From 786baecfe78f8e25547c628b48a60fc8e5636056 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 14 Jun 2012 16:35:56 -0300 Subject: [media] dvb-usb: move it to drivers/media/usb/dvb-usb As media/dvb will be removed, move it to a proper place. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/Kconfig | 18 + drivers/media/usb/Makefile | 6 + drivers/media/usb/dvb-usb-v2/Kconfig | 146 + drivers/media/usb/dvb-usb-v2/Makefile | 48 + drivers/media/usb/dvb-usb-v2/af9015.c | 1434 ++++++ drivers/media/usb/dvb-usb-v2/af9015.h | 170 + drivers/media/usb/dvb-usb-v2/af9035.c | 1086 +++++ drivers/media/usb/dvb-usb-v2/af9035.h | 111 + drivers/media/usb/dvb-usb-v2/anysee.c | 1324 ++++++ drivers/media/usb/dvb-usb-v2/anysee.h | 356 ++ drivers/media/usb/dvb-usb-v2/au6610.c | 206 + drivers/media/usb/dvb-usb-v2/au6610.h | 32 + drivers/media/usb/dvb-usb-v2/az6007.c | 919 ++++ drivers/media/usb/dvb-usb-v2/ce6230.c | 287 ++ drivers/media/usb/dvb-usb-v2/ce6230.h | 61 + drivers/media/usb/dvb-usb-v2/cypress_firmware.c | 125 + drivers/media/usb/dvb-usb-v2/cypress_firmware.h | 31 + drivers/media/usb/dvb-usb-v2/dvb_usb.h | 389 ++ drivers/media/usb/dvb-usb-v2/dvb_usb_common.h | 35 + drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 997 +++++ drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c | 77 + drivers/media/usb/dvb-usb-v2/ec168.c | 376 ++ drivers/media/usb/dvb-usb-v2/ec168.h | 63 + drivers/media/usb/dvb-usb-v2/gl861.c | 175 + drivers/media/usb/dvb-usb-v2/gl861.h | 12 + drivers/media/usb/dvb-usb-v2/it913x.c | 799 ++++ drivers/media/usb/dvb-usb-v2/lmedm04.c | 1369 ++++++ drivers/media/usb/dvb-usb-v2/lmedm04.h | 175 + drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c | 612 +++ drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h | 55 + drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c | 763 ++++ drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h | 56 + drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c | 850 ++++ drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h | 35 + drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c | 343 ++ drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h | 53 + drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h | 179 + drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c | 527 +++ drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h | 89 + drivers/media/usb/dvb-usb-v2/mxl111sf.c | 1431 ++++++ drivers/media/usb/dvb-usb-v2/mxl111sf.h | 160 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 1259 ++++++ drivers/media/usb/dvb-usb-v2/rtl28xxu.h | 254 ++ drivers/media/usb/dvb-usb-v2/usb_urb.c | 357 ++ drivers/media/usb/dvb-usb/Kconfig | 313 ++ drivers/media/usb/dvb-usb/Makefile | 82 + drivers/media/usb/dvb-usb/a800.c | 191 + drivers/media/usb/dvb-usb/af9005-fe.c | 1487 +++++++ drivers/media/usb/dvb-usb/af9005-remote.c | 157 + drivers/media/usb/dvb-usb/af9005-script.h | 203 + drivers/media/usb/dvb-usb/af9005.c | 1117 +++++ drivers/media/usb/dvb-usb/af9005.h | 3496 +++++++++++++++ drivers/media/usb/dvb-usb/az6027.c | 1182 +++++ drivers/media/usb/dvb-usb/az6027.h | 14 + drivers/media/usb/dvb-usb/cinergyT2-core.c | 254 ++ drivers/media/usb/dvb-usb/cinergyT2-fe.c | 356 ++ drivers/media/usb/dvb-usb/cinergyT2.h | 95 + drivers/media/usb/dvb-usb/cxusb.c | 2043 +++++++++ drivers/media/usb/dvb-usb/cxusb.h | 35 + drivers/media/usb/dvb-usb/dib0700.h | 75 + drivers/media/usb/dvb-usb/dib0700_core.c | 845 ++++ drivers/media/usb/dvb-usb/dib0700_devices.c | 4813 +++++++++++++++++++++ drivers/media/usb/dvb-usb/dib07x0.h | 21 + drivers/media/usb/dvb-usb/dibusb-common.c | 479 ++ drivers/media/usb/dvb-usb/dibusb-mb.c | 471 ++ drivers/media/usb/dvb-usb/dibusb-mc.c | 149 + drivers/media/usb/dvb-usb/dibusb.h | 131 + drivers/media/usb/dvb-usb/digitv.c | 354 ++ drivers/media/usb/dvb-usb/digitv.h | 66 + drivers/media/usb/dvb-usb/dtt200u-fe.c | 210 + drivers/media/usb/dvb-usb/dtt200u.c | 368 ++ drivers/media/usb/dvb-usb/dtt200u.h | 57 + drivers/media/usb/dvb-usb/dtv5100.c | 224 + drivers/media/usb/dvb-usb/dtv5100.h | 51 + drivers/media/usb/dvb-usb/dvb-usb-common.h | 52 + drivers/media/usb/dvb-usb/dvb-usb-dvb.c | 288 ++ drivers/media/usb/dvb-usb/dvb-usb-firmware.c | 146 + drivers/media/usb/dvb-usb/dvb-usb-i2c.c | 43 + drivers/media/usb/dvb-usb/dvb-usb-init.c | 304 ++ drivers/media/usb/dvb-usb/dvb-usb-remote.c | 391 ++ drivers/media/usb/dvb-usb/dvb-usb-urb.c | 121 + drivers/media/usb/dvb-usb/dvb-usb.h | 483 +++ drivers/media/usb/dvb-usb/dvb_usb_dvb.c | 403 ++ drivers/media/usb/dvb-usb/dvb_usb_remote.c | 117 + drivers/media/usb/dvb-usb/dw2102.c | 1951 +++++++++ drivers/media/usb/dvb-usb/dw2102.h | 9 + drivers/media/usb/dvb-usb/friio-fe.c | 473 ++ drivers/media/usb/dvb-usb/friio.c | 522 +++ drivers/media/usb/dvb-usb/friio.h | 99 + drivers/media/usb/dvb-usb/gp8psk-fe.c | 369 ++ drivers/media/usb/dvb-usb/gp8psk.c | 328 ++ drivers/media/usb/dvb-usb/gp8psk.h | 100 + drivers/media/usb/dvb-usb/m920x.c | 1099 +++++ drivers/media/usb/dvb-usb/m920x.h | 77 + drivers/media/usb/dvb-usb/nova-t-usb2.c | 233 + drivers/media/usb/dvb-usb/opera1.c | 583 +++ drivers/media/usb/dvb-usb/pctv452e.c | 1063 +++++ drivers/media/usb/dvb-usb/technisat-usb2.c | 789 ++++ drivers/media/usb/dvb-usb/ttusb2.c | 820 ++++ drivers/media/usb/dvb-usb/ttusb2.h | 70 + drivers/media/usb/dvb-usb/umt-010.c | 151 + drivers/media/usb/dvb-usb/usb-urb.c | 254 ++ drivers/media/usb/dvb-usb/vp702x-fe.c | 379 ++ drivers/media/usb/dvb-usb/vp702x.c | 444 ++ drivers/media/usb/dvb-usb/vp702x.h | 113 + drivers/media/usb/dvb-usb/vp7045-fe.c | 190 + drivers/media/usb/dvb-usb/vp7045.c | 302 ++ drivers/media/usb/dvb-usb/vp7045.h | 70 + drivers/media/usb/siano/Kconfig | 34 + drivers/media/usb/siano/Makefile | 11 + 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 ++ drivers/media/usb/siano/smsusb.c | 568 +++ drivers/media/usb/ttusb-budget/Kconfig | 18 + drivers/media/usb/ttusb-budget/Makefile | 3 + drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c | 1816 ++++++++ drivers/media/usb/ttusb-dec/Kconfig | 21 + drivers/media/usb/ttusb-dec/Makefile | 3 + drivers/media/usb/ttusb-dec/ttusb_dec.c | 1764 ++++++++ drivers/media/usb/ttusb-dec/ttusbdecfe.c | 298 ++ drivers/media/usb/ttusb-dec/ttusbdecfe.h | 38 + 129 files changed, 59192 insertions(+) create mode 100644 drivers/media/usb/Kconfig create mode 100644 drivers/media/usb/Makefile create mode 100644 drivers/media/usb/dvb-usb-v2/Kconfig create mode 100644 drivers/media/usb/dvb-usb-v2/Makefile create mode 100644 drivers/media/usb/dvb-usb-v2/af9015.c create mode 100644 drivers/media/usb/dvb-usb-v2/af9015.h create mode 100644 drivers/media/usb/dvb-usb-v2/af9035.c create mode 100644 drivers/media/usb/dvb-usb-v2/af9035.h create mode 100644 drivers/media/usb/dvb-usb-v2/anysee.c create mode 100644 drivers/media/usb/dvb-usb-v2/anysee.h create mode 100644 drivers/media/usb/dvb-usb-v2/au6610.c create mode 100644 drivers/media/usb/dvb-usb-v2/au6610.h create mode 100644 drivers/media/usb/dvb-usb-v2/az6007.c create mode 100644 drivers/media/usb/dvb-usb-v2/ce6230.c create mode 100644 drivers/media/usb/dvb-usb-v2/ce6230.h create mode 100644 drivers/media/usb/dvb-usb-v2/cypress_firmware.c create mode 100644 drivers/media/usb/dvb-usb-v2/cypress_firmware.h create mode 100644 drivers/media/usb/dvb-usb-v2/dvb_usb.h create mode 100644 drivers/media/usb/dvb-usb-v2/dvb_usb_common.h create mode 100644 drivers/media/usb/dvb-usb-v2/dvb_usb_core.c create mode 100644 drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c create mode 100644 drivers/media/usb/dvb-usb-v2/ec168.c create mode 100644 drivers/media/usb/dvb-usb-v2/ec168.h create mode 100644 drivers/media/usb/dvb-usb-v2/gl861.c create mode 100644 drivers/media/usb/dvb-usb-v2/gl861.h create mode 100644 drivers/media/usb/dvb-usb-v2/it913x.c create mode 100644 drivers/media/usb/dvb-usb-v2/lmedm04.c create mode 100644 drivers/media/usb/dvb-usb-v2/lmedm04.h create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf.c create mode 100644 drivers/media/usb/dvb-usb-v2/mxl111sf.h create mode 100644 drivers/media/usb/dvb-usb-v2/rtl28xxu.c create mode 100644 drivers/media/usb/dvb-usb-v2/rtl28xxu.h create mode 100644 drivers/media/usb/dvb-usb-v2/usb_urb.c create mode 100644 drivers/media/usb/dvb-usb/Kconfig create mode 100644 drivers/media/usb/dvb-usb/Makefile create mode 100644 drivers/media/usb/dvb-usb/a800.c create mode 100644 drivers/media/usb/dvb-usb/af9005-fe.c create mode 100644 drivers/media/usb/dvb-usb/af9005-remote.c create mode 100644 drivers/media/usb/dvb-usb/af9005-script.h create mode 100644 drivers/media/usb/dvb-usb/af9005.c create mode 100644 drivers/media/usb/dvb-usb/af9005.h create mode 100644 drivers/media/usb/dvb-usb/az6027.c create mode 100644 drivers/media/usb/dvb-usb/az6027.h create mode 100644 drivers/media/usb/dvb-usb/cinergyT2-core.c create mode 100644 drivers/media/usb/dvb-usb/cinergyT2-fe.c create mode 100644 drivers/media/usb/dvb-usb/cinergyT2.h create mode 100644 drivers/media/usb/dvb-usb/cxusb.c create mode 100644 drivers/media/usb/dvb-usb/cxusb.h create mode 100644 drivers/media/usb/dvb-usb/dib0700.h create mode 100644 drivers/media/usb/dvb-usb/dib0700_core.c create mode 100644 drivers/media/usb/dvb-usb/dib0700_devices.c create mode 100644 drivers/media/usb/dvb-usb/dib07x0.h create mode 100644 drivers/media/usb/dvb-usb/dibusb-common.c create mode 100644 drivers/media/usb/dvb-usb/dibusb-mb.c create mode 100644 drivers/media/usb/dvb-usb/dibusb-mc.c create mode 100644 drivers/media/usb/dvb-usb/dibusb.h create mode 100644 drivers/media/usb/dvb-usb/digitv.c create mode 100644 drivers/media/usb/dvb-usb/digitv.h create mode 100644 drivers/media/usb/dvb-usb/dtt200u-fe.c create mode 100644 drivers/media/usb/dvb-usb/dtt200u.c create mode 100644 drivers/media/usb/dvb-usb/dtt200u.h create mode 100644 drivers/media/usb/dvb-usb/dtv5100.c create mode 100644 drivers/media/usb/dvb-usb/dtv5100.h create mode 100644 drivers/media/usb/dvb-usb/dvb-usb-common.h create mode 100644 drivers/media/usb/dvb-usb/dvb-usb-dvb.c create mode 100644 drivers/media/usb/dvb-usb/dvb-usb-firmware.c create mode 100644 drivers/media/usb/dvb-usb/dvb-usb-i2c.c create mode 100644 drivers/media/usb/dvb-usb/dvb-usb-init.c create mode 100644 drivers/media/usb/dvb-usb/dvb-usb-remote.c create mode 100644 drivers/media/usb/dvb-usb/dvb-usb-urb.c create mode 100644 drivers/media/usb/dvb-usb/dvb-usb.h create mode 100644 drivers/media/usb/dvb-usb/dvb_usb_dvb.c create mode 100644 drivers/media/usb/dvb-usb/dvb_usb_remote.c create mode 100644 drivers/media/usb/dvb-usb/dw2102.c create mode 100644 drivers/media/usb/dvb-usb/dw2102.h create mode 100644 drivers/media/usb/dvb-usb/friio-fe.c create mode 100644 drivers/media/usb/dvb-usb/friio.c create mode 100644 drivers/media/usb/dvb-usb/friio.h create mode 100644 drivers/media/usb/dvb-usb/gp8psk-fe.c create mode 100644 drivers/media/usb/dvb-usb/gp8psk.c create mode 100644 drivers/media/usb/dvb-usb/gp8psk.h create mode 100644 drivers/media/usb/dvb-usb/m920x.c create mode 100644 drivers/media/usb/dvb-usb/m920x.h create mode 100644 drivers/media/usb/dvb-usb/nova-t-usb2.c create mode 100644 drivers/media/usb/dvb-usb/opera1.c create mode 100644 drivers/media/usb/dvb-usb/pctv452e.c create mode 100644 drivers/media/usb/dvb-usb/technisat-usb2.c create mode 100644 drivers/media/usb/dvb-usb/ttusb2.c create mode 100644 drivers/media/usb/dvb-usb/ttusb2.h create mode 100644 drivers/media/usb/dvb-usb/umt-010.c create mode 100644 drivers/media/usb/dvb-usb/usb-urb.c create mode 100644 drivers/media/usb/dvb-usb/vp702x-fe.c create mode 100644 drivers/media/usb/dvb-usb/vp702x.c create mode 100644 drivers/media/usb/dvb-usb/vp702x.h create mode 100644 drivers/media/usb/dvb-usb/vp7045-fe.c create mode 100644 drivers/media/usb/dvb-usb/vp7045.c create mode 100644 drivers/media/usb/dvb-usb/vp7045.h create mode 100644 drivers/media/usb/siano/Kconfig create mode 100644 drivers/media/usb/siano/Makefile create mode 100644 drivers/media/usb/siano/sms-cards.c create mode 100644 drivers/media/usb/siano/sms-cards.h create mode 100644 drivers/media/usb/siano/smscoreapi.c create mode 100644 drivers/media/usb/siano/smscoreapi.h create mode 100644 drivers/media/usb/siano/smsdvb.c create mode 100644 drivers/media/usb/siano/smsendian.c create mode 100644 drivers/media/usb/siano/smsendian.h create mode 100644 drivers/media/usb/siano/smsir.c create mode 100644 drivers/media/usb/siano/smsir.h create mode 100644 drivers/media/usb/siano/smssdio.c create mode 100644 drivers/media/usb/siano/smsusb.c create mode 100644 drivers/media/usb/ttusb-budget/Kconfig create mode 100644 drivers/media/usb/ttusb-budget/Makefile create mode 100644 drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c create mode 100644 drivers/media/usb/ttusb-dec/Kconfig create mode 100644 drivers/media/usb/ttusb-dec/Makefile create mode 100644 drivers/media/usb/ttusb-dec/ttusb_dec.c create mode 100644 drivers/media/usb/ttusb-dec/ttusbdecfe.c create mode 100644 drivers/media/usb/ttusb-dec/ttusbdecfe.h (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig new file mode 100644 index 000000000000..70b1708db05a --- /dev/null +++ b/drivers/media/usb/Kconfig @@ -0,0 +1,18 @@ +# +# USB media device configuration +# + +menuconfig MEDIA_USB_DRIVERS + bool "Supported DVB USB Adapters" + depends on USB + default y + +if MEDIA_USB_DRIVERS && DVB_CORE && I2C + +source "drivers/media/usb/dvb-usb/Kconfig" +source "drivers/media/usb/dvb-usb-v2/Kconfig" +source "drivers/media/usb/ttusb-budget/Kconfig" +source "drivers/media/usb/ttusb-dec/Kconfig" +source "drivers/media/usb/siano/Kconfig" + +endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile new file mode 100644 index 000000000000..44e29f340ebd --- /dev/null +++ b/drivers/media/usb/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the USB media device drivers +# + +# DVB USB-only drivers +obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig new file mode 100644 index 000000000000..276374fbaf4f --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -0,0 +1,146 @@ +config DVB_USB_V2 + tristate "Support for various USB DVB devices v2" + depends on DVB_CORE && USB && I2C && RC_CORE + help + By enabling this you will be able to choose the various supported + USB1.1 and USB2.0 DVB devices. + + Almost every USB device needs a firmware, please look into + . + + For a complete list of supported USB devices see the LinuxTV DVB Wiki: + + + Say Y if you own a USB DVB device. + +config DVB_USB_CYPRESS_FIRMWARE + tristate "Cypress firmware helper routines" + depends on DVB_USB_V2 + +config DVB_USB_AF9015 + tristate "Afatech AF9015 DVB-T USB2.0 support" + depends on DVB_USB_V2 + select DVB_AF9013 + select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver + +config DVB_USB_AF9035 + tristate "Afatech AF9035 DVB-T USB2.0 support" + depends on DVB_USB_V2 + select DVB_AF9033 + select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Afatech AF9035 based DVB USB receiver. + +config DVB_USB_ANYSEE + tristate "Anysee DVB-T/C USB2.0 support" + depends on DVB_USB_V2 + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18212 if !MEDIA_TUNER_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE + select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_STV6110 if !DVB_FE_CUSTOMISE + select DVB_ISL6423 if !DVB_FE_CUSTOMISE + select DVB_CXD2820R if !DVB_FE_CUSTOMISE + help + Say Y here to support the Anysee E30, Anysee E30 Plus or + Anysee E30 C Plus DVB USB2.0 receiver. + +config DVB_USB_AU6610 + tristate "Alcor Micro AU6610 USB2.0 support" + depends on DVB_USB_V2 + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. + +config DVB_USB_AZ6007 + tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support" + depends on DVB_USB_V2 + select DVB_USB_CYPRESS_FIRMWARE + select DVB_DRXK if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE + help + Say Y here to support the AZ6007 receivers like Terratec H7. + +config DVB_USB_CE6230 + tristate "Intel CE6230 DVB-T USB2.0 support" + depends on DVB_USB_V2 + select DVB_ZL10353 + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver + +config DVB_USB_EC168 + tristate "E3C EC168 DVB-T USB2.0 support" + depends on DVB_USB_V2 + select DVB_EC100 + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. + +config DVB_USB_GL861 + tristate "Genesys Logic GL861 USB2.0 support" + depends on DVB_USB_V2 + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 + receiver with USB ID 0db0:5581. + +config DVB_USB_IT913X + tristate "ITE IT913X DVB-T USB2.0 support" + depends on DVB_USB_V2 + select DVB_IT913X_FE + help + Say Y here to support the ITE IT913X DVB-T USB2.0 + +config DVB_USB_LME2510 + tristate "LME DM04/QQBOX DVB-S USB2.0 support" + depends on DVB_USB_V2 + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_STV0288 if !DVB_FE_CUSTOMISE + select DVB_IX2505V if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_M88RS2000 if !DVB_FE_CUSTOMISE + help + Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 + +config DVB_USB_MXL111SF + tristate "MxL111SF DTV USB2.0 support" + depends on DVB_USB_V2 + select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select DVB_LG2160 if !DVB_FE_CUSTOMISE + select VIDEO_TVEEPROM + help + Say Y here to support the MxL111SF USB2.0 DTV receiver. + +config DVB_USB_RTL28XXU + tristate "Realtek RTL28xxU DVB USB support" + depends on DVB_USB_V2 && EXPERIMENTAL + select DVB_RTL2830 + select DVB_RTL2832 + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0012 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0013 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Realtek RTL28xxU DVB USB receiver. + diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile new file mode 100644 index 000000000000..24248b3acd4f --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -0,0 +1,48 @@ +dvb_usbv2-objs = dvb_usb_core.o dvb_usb_urb.o usb_urb.o +obj-$(CONFIG_DVB_USB_V2) += dvb_usbv2.o + +dvb_usb_cypress_firmware-objs = cypress_firmware.o +obj-$(CONFIG_DVB_USB_CYPRESS_FIRMWARE) += dvb_usb_cypress_firmware.o + +dvb-usb-af9015-objs = af9015.o +obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o + +dvb-usb-af9035-objs = af9035.o +obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o + +dvb-usb-anysee-objs = anysee.o +obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o + +dvb-usb-au6610-objs = au6610.o +obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o + +dvb-usb-az6007-objs = az6007.o +obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o + +dvb-usb-ce6230-objs = ce6230.o +obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o + +dvb-usb-ec168-objs = ec168.o +obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o + +dvb-usb-it913x-objs = it913x.o +obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o + +dvb-usb-lmedm04-objs = lmedm04.o +obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o + +dvb-usb-gl861-objs = gl861.o +obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o + +dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o +obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o +obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o +obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o + +dvb-usb-rtl28xxu-objs = rtl28xxu.o +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 + diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c new file mode 100644 index 000000000000..e77429b37a7d --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -0,0 +1,1434 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; 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 "af9015.h" + +static int dvb_usb_af9015_debug; +module_param_named(debug, dvb_usb_af9015_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +static int dvb_usb_af9015_remote; +module_param_named(remote, dvb_usb_af9015_remote, int, 0644); +MODULE_PARM_DESC(remote, "select remote"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) +{ +#define BUF_LEN 63 +#define REQ_HDR_LEN 8 /* send header size */ +#define ACK_HDR_LEN 2 /* rece header size */ + struct af9015_state *state = d_to_priv(d); + int ret, wlen, rlen; + u8 buf[BUF_LEN]; + u8 write = 1; + + buf[0] = req->cmd; + buf[1] = state->seq++; + buf[2] = req->i2c_addr; + buf[3] = req->addr >> 8; + buf[4] = req->addr & 0xff; + buf[5] = req->mbox; + buf[6] = req->addr_len; + buf[7] = req->data_len; + + switch (req->cmd) { + case GET_CONFIG: + case READ_MEMORY: + case RECONNECT_USB: + write = 0; + break; + case READ_I2C: + write = 0; + buf[2] |= 0x01; /* set I2C direction */ + case WRITE_I2C: + buf[0] = READ_WRITE_I2C; + break; + case WRITE_MEMORY: + if (((req->addr & 0xff00) == 0xff00) || + ((req->addr & 0xff00) == 0xae00)) + buf[0] = WRITE_VIRTUAL_MEMORY; + case WRITE_VIRTUAL_MEMORY: + case COPY_FIRMWARE: + case DOWNLOAD_FIRMWARE: + case BOOT: + break; + default: + err("unknown command:%d", req->cmd); + ret = -1; + goto error; + } + + /* buffer overflow check */ + if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) || + (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { + err("too much data; cmd:%d len:%d", req->cmd, req->data_len); + ret = -EINVAL; + goto error; + } + + /* write receives seq + status = 2 bytes + read receives seq + status + data = 2 + N bytes */ + wlen = REQ_HDR_LEN; + rlen = ACK_HDR_LEN; + if (write) { + wlen += req->data_len; + memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len); + } else { + rlen += req->data_len; + } + + /* no ack for these packets */ + if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) + rlen = 0; + + ret = dvb_usbv2_generic_rw(d, buf, wlen, buf, rlen); + if (ret) + goto error; + + /* check status */ + if (rlen && buf[1]) { + err("command failed:%d", buf[1]); + ret = -1; + goto error; + } + + /* read request, copy returned data to return buf */ + if (!write) + memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len); +error: + return ret; +} + +static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val, + u8 len) +{ + struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, + val}; + return af9015_ctrl_msg(d, &req); +} + +static int af9015_read_regs(struct dvb_usb_device *d, u16 addr, u8 *val, u8 len) +{ + struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, + val}; + return af9015_ctrl_msg(d, &req); +} + +static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val) +{ + return af9015_write_regs(d, addr, &val, 1); +} + +static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val) +{ + return af9015_read_regs(d, addr, val, 1); +} + +static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, + u8 val) +{ + struct af9015_state *state = d_to_priv(d); + struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val}; + + if (addr == state->af9013_config[0].i2c_addr || + addr == state->af9013_config[1].i2c_addr) + req.addr_len = 3; + + return af9015_ctrl_msg(d, &req); +} + +static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, + u8 *val) +{ + struct af9015_state *state = d_to_priv(d); + struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val}; + + if (addr == state->af9013_config[0].i2c_addr || + addr == state->af9013_config[1].i2c_addr) + req.addr_len = 3; + + return af9015_ctrl_msg(d, &req); +} + +static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op) +{ + int ret; + u8 val, mask = 0x01; + + ret = af9015_read_reg(d, addr, &val); + if (ret) + return ret; + + mask <<= bit; + if (op) { + /* set bit */ + val |= mask; + } else { + /* clear bit */ + mask ^= 0xff; + val &= mask; + } + + return af9015_write_reg(d, addr, val); +} + +static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +{ + return af9015_do_reg_bit(d, addr, bit, 1); +} + +static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +{ + return af9015_do_reg_bit(d, addr, bit, 0); +} + +static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct af9015_state *state = d_to_priv(d); + int ret = 0, i = 0; + u16 addr; + u8 uninitialized_var(mbox), addr_len; + struct req_t req; + +/* +The bus lock is needed because there is two tuners both using same I2C-address. +Due to that the only way to select correct tuner is use demodulator I2C-gate. + +................................................ +. AF9015 includes integrated AF9013 demodulator. +. ____________ ____________ . ____________ +.| uC | | demod | . | tuner | +.|------------| |------------| . |------------| +.| AF9015 | | AF9013/5 | . | MXL5003 | +.| |--+----I2C-------|-----/ -----|-.-----I2C-------| | +.| | | | addr 0x38 | . | addr 0xc6 | +.|____________| | |____________| . |____________| +.................|.............................. + | ____________ ____________ + | | demod | | tuner | + | |------------| |------------| + | | AF9013 | | MXL5003 | + +----I2C-------|-----/ -----|-------I2C-------| | + | addr 0x3a | | addr 0xc6 | + |____________| |____________| +*/ + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (msg[i].addr == state->af9013_config[0].i2c_addr || + msg[i].addr == state->af9013_config[1].i2c_addr) { + addr = msg[i].buf[0] << 8; + addr += msg[i].buf[1]; + mbox = msg[i].buf[2]; + addr_len = 3; + } else { + addr = msg[i].buf[0]; + addr_len = 1; + /* mbox is don't care in that case */ + } + + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].len > 3 || msg[i+1].len > 61) { + ret = -EOPNOTSUPP; + goto error; + } + if (msg[i].addr == state->af9013_config[0].i2c_addr) + req.cmd = READ_MEMORY; + else + req.cmd = READ_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i+1].len; + req.data = &msg[i+1].buf[0]; + ret = af9015_ctrl_msg(d, &req); + i += 2; + } else if (msg[i].flags & I2C_M_RD) { + if (msg[i].len > 61) { + ret = -EOPNOTSUPP; + goto error; + } + if (msg[i].addr == state->af9013_config[0].i2c_addr) { + ret = -EINVAL; + goto error; + } + req.cmd = READ_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i].len; + req.data = &msg[i].buf[0]; + ret = af9015_ctrl_msg(d, &req); + i += 1; + } else { + if (msg[i].len > 21) { + ret = -EOPNOTSUPP; + goto error; + } + if (msg[i].addr == state->af9013_config[0].i2c_addr) + req.cmd = WRITE_MEMORY; + else + req.cmd = WRITE_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i].len-addr_len; + req.data = &msg[i].buf[addr_len]; + ret = af9015_ctrl_msg(d, &req); + i += 1; + } + if (ret) + goto error; + + } + ret = i; + +error: + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 af9015_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9015_i2c_algo = { + .master_xfer = af9015_i2c_xfer, + .functionality = af9015_i2c_func, +}; + +static int af9015_identify_state(struct dvb_usb_device *d, const char **name) +{ + int ret; + u8 reply; + struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply}; + + ret = af9015_ctrl_msg(d, &req); + if (ret) + return ret; + + deb_info("%s: reply:%02x\n", __func__, reply); + if (reply == 0x02) + ret = WARM; + else + ret = COLD; + + return ret; +} + +static int af9015_download_firmware(struct dvb_usb_device *d, + const struct firmware *fw) +{ + struct af9015_state *state = d_to_priv(d); + int i, len, remaining, ret; + struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL}; + u16 checksum = 0; + + deb_info("%s:\n", __func__); + + /* calc checksum */ + for (i = 0; i < fw->size; i++) + checksum += fw->data[i]; + + state->firmware_size = fw->size; + state->firmware_checksum = checksum; + + #define FW_ADDR 0x5100 /* firmware start address */ + #define LEN_MAX 55 /* max packet size */ + for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { + len = remaining; + if (len > LEN_MAX) + len = LEN_MAX; + + req.data_len = len; + req.data = (u8 *) &fw->data[fw->size - remaining]; + req.addr = FW_ADDR + fw->size - remaining; + + ret = af9015_ctrl_msg(d, &req); + if (ret) { + err("firmware download failed:%d", ret); + goto error; + } + } + + /* firmware loaded, request boot */ + req.cmd = BOOT; + req.data_len = 0; + ret = af9015_ctrl_msg(d, &req); + if (ret) { + err("firmware boot failed:%d", ret); + goto error; + } + +error: + return ret; +} + +/* hash (and dump) eeprom */ +static int af9015_eeprom_hash(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + int ret; + static const unsigned int eeprom_size = 256; + unsigned int reg; + u8 val, *eeprom; + struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; + + eeprom = kmalloc(eeprom_size, GFP_KERNEL); + if (eeprom == NULL) + return -ENOMEM; + + for (reg = 0; reg < eeprom_size; reg++) { + req.addr = reg; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto free; + + eeprom[reg] = val; + } + + if (dvb_usb_af9015_debug & 0x01) + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, eeprom, + eeprom_size); + + BUG_ON(eeprom_size % 4); + + state->eeprom_sum = 0; + for (reg = 0; reg < eeprom_size / sizeof(u32); reg++) { + state->eeprom_sum *= GOLDEN_RATIO_PRIME_32; + state->eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]); + } + + deb_info("%s: eeprom sum=%.8x\n", __func__, state->eeprom_sum); + + ret = 0; +free: + kfree(eeprom); + return ret; +} + +static int af9015_read_config(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + int ret; + u8 val, i, offset = 0; + struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; + + deb_info("%s:\n", __func__); + + /* IR remote controller */ + req.addr = AF9015_EEPROM_IR_MODE; + /* first message will timeout often due to possible hw bug */ + for (i = 0; i < 4; i++) { + ret = af9015_ctrl_msg(d, &req); + if (!ret) + break; + } + if (ret) + goto error; + + ret = af9015_eeprom_hash(d); + if (ret) + goto error; + + deb_info("%s: IR mode=%d\n", __func__, val); + state->ir_mode = val; + + /* TS mode - one or two receivers */ + req.addr = AF9015_EEPROM_TS_MODE; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + + state->dual_mode = val; + deb_info("%s: TS mode=%d\n", __func__, state->dual_mode); + + /* disable 2nd adapter because we don't have PID-filters */ + if (d->udev->speed == USB_SPEED_FULL) + state->dual_mode = 0; + + if (state->dual_mode) { + /* read 2nd demodulator I2C address */ + req.addr = AF9015_EEPROM_DEMOD2_I2C; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + + state->af9013_config[1].i2c_addr = val; + } + + for (i = 0; i < state->dual_mode + 1; i++) { + if (i == 1) + offset = AF9015_EEPROM_OFFSET; + /* xtal */ + req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + switch (val) { + case 0: + state->af9013_config[i].clock = 28800000; + break; + case 1: + state->af9013_config[i].clock = 20480000; + break; + case 2: + state->af9013_config[i].clock = 28000000; + break; + case 3: + state->af9013_config[i].clock = 25000000; + break; + }; + deb_info("%s: [%d] xtal=%d set clock=%d\n", __func__, i, + val, state->af9013_config[i].clock); + + /* IF frequency */ + req.addr = AF9015_EEPROM_IF1H + offset; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + + state->af9013_config[i].if_frequency = val << 8; + + req.addr = AF9015_EEPROM_IF1L + offset; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + + state->af9013_config[i].if_frequency += val; + state->af9013_config[i].if_frequency *= 1000; + deb_info("%s: [%d] IF frequency=%d\n", __func__, i, + state->af9013_config[i].if_frequency); + + /* MT2060 IF1 */ + req.addr = AF9015_EEPROM_MT2060_IF1H + offset; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + state->mt2060_if1[i] = val << 8; + req.addr = AF9015_EEPROM_MT2060_IF1L + offset; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + state->mt2060_if1[i] += val; + deb_info("%s: [%d] MT2060 IF1=%d\n", __func__, i, + state->mt2060_if1[i]); + + /* tuner */ + req.addr = AF9015_EEPROM_TUNER_ID1 + offset; + ret = af9015_ctrl_msg(d, &req); + if (ret) + goto error; + switch (val) { + case AF9013_TUNER_ENV77H11D5: + case AF9013_TUNER_MT2060: + case AF9013_TUNER_QT1010: + case AF9013_TUNER_UNKNOWN: + case AF9013_TUNER_MT2060_2: + case AF9013_TUNER_TDA18271: + case AF9013_TUNER_QT1010A: + case AF9013_TUNER_TDA18218: + state->af9013_config[i].spec_inv = 1; + break; + case AF9013_TUNER_MXL5003D: + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + case AF9013_TUNER_MXL5007T: + state->af9013_config[i].spec_inv = 0; + break; + case AF9013_TUNER_MC44S803: + state->af9013_config[i].gpio[1] = AF9013_GPIO_LO; + state->af9013_config[i].spec_inv = 1; + break; + default: + warn("tuner id=%d not supported, please report!", val); + return -ENODEV; + }; + + state->af9013_config[i].tuner = val; + deb_info("%s: [%d] tuner id=%d\n", __func__, i, val); + } + +error: + if (ret) + err("eeprom read failed=%d", ret); + + /* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM + content :-( Override some wrong values here. Ditto for the + AVerTV Red HD+ (A850T) device. */ + if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA && + ((le16_to_cpu(d->udev->descriptor.idProduct) == + USB_PID_AVERMEDIA_A850) || + (le16_to_cpu(d->udev->descriptor.idProduct) == + USB_PID_AVERMEDIA_A850T))) { + deb_info("%s: AverMedia A850: overriding config\n", __func__); + /* disable dual mode */ + state->dual_mode = 0; + + /* set correct IF */ + state->af9013_config[0].if_frequency = 4570000; + } + + return ret; +} + +static int af9015_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, + struct usb_data_stream_properties *stream) +{ + deb_info("%s: adap=%d\n", __func__, fe_to_adap(fe)->id); + + if (fe_to_d(fe)->udev->speed == USB_SPEED_FULL) + stream->u.bulk.buffersize = TS_USB11_FRAME_SIZE; + + return 0; +} + +static int af9015_get_adapter_count(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + return state->dual_mode + 1; +} + +/* override demod callbacks for resource locking */ +static int af9015_af9013_set_frontend(struct dvb_frontend *fe) +{ + int ret; + struct af9015_state *state = fe_to_priv(fe); + + if (mutex_lock_interruptible(&state->fe_mutex)) + return -EAGAIN; + + ret = state->set_frontend[fe_to_adap(fe)->id](fe); + + mutex_unlock(&state->fe_mutex); + + return ret; +} + +/* override demod callbacks for resource locking */ +static int af9015_af9013_read_status(struct dvb_frontend *fe, + fe_status_t *status) +{ + int ret; + struct af9015_state *state = fe_to_priv(fe); + + if (mutex_lock_interruptible(&state->fe_mutex)) + return -EAGAIN; + + ret = state->read_status[fe_to_adap(fe)->id](fe, status); + + mutex_unlock(&state->fe_mutex); + + return ret; +} + +/* override demod callbacks for resource locking */ +static int af9015_af9013_init(struct dvb_frontend *fe) +{ + int ret; + struct af9015_state *state = fe_to_priv(fe); + + if (mutex_lock_interruptible(&state->fe_mutex)) + return -EAGAIN; + + ret = state->init[fe_to_adap(fe)->id](fe); + + mutex_unlock(&state->fe_mutex); + + return ret; +} + +/* override demod callbacks for resource locking */ +static int af9015_af9013_sleep(struct dvb_frontend *fe) +{ + int ret; + struct af9015_state *state = fe_to_priv(fe); + + if (mutex_lock_interruptible(&state->fe_mutex)) + return -EAGAIN; + + ret = state->sleep[fe_to_adap(fe)->id](fe); + + mutex_unlock(&state->fe_mutex); + + return ret; +} + +/* override tuner callbacks for resource locking */ +static int af9015_tuner_init(struct dvb_frontend *fe) +{ + int ret; + struct af9015_state *state = fe_to_priv(fe); + + if (mutex_lock_interruptible(&state->fe_mutex)) + return -EAGAIN; + + ret = state->tuner_init[fe_to_adap(fe)->id](fe); + + mutex_unlock(&state->fe_mutex); + + return ret; +} + +/* override tuner callbacks for resource locking */ +static int af9015_tuner_sleep(struct dvb_frontend *fe) +{ + int ret; + struct af9015_state *state = fe_to_priv(fe); + + if (mutex_lock_interruptible(&state->fe_mutex)) + return -EAGAIN; + + ret = state->tuner_sleep[fe_to_adap(fe)->id](fe); + + mutex_unlock(&state->fe_mutex); + + return ret; +} + +static int af9015_copy_firmware(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + int ret; + u8 fw_params[4]; + u8 val, i; + struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params), + fw_params }; + deb_info("%s:\n", __func__); + + fw_params[0] = state->firmware_size >> 8; + fw_params[1] = state->firmware_size & 0xff; + fw_params[2] = state->firmware_checksum >> 8; + fw_params[3] = state->firmware_checksum & 0xff; + + /* wait 2nd demodulator ready */ + msleep(100); + + ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr, + 0x98be, &val); + if (ret) + goto error; + else + deb_info("%s: firmware status:%02x\n", __func__, val); + + if (val == 0x0c) /* fw is running, no need for download */ + goto exit; + + /* set I2C master clock to fast (to speed up firmware copy) */ + ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */ + if (ret) + goto error; + + msleep(50); + + /* copy firmware */ + ret = af9015_ctrl_msg(d, &req); + if (ret) + err("firmware copy cmd failed:%d", ret); + deb_info("%s: firmware copy done\n", __func__); + + /* set I2C master clock back to normal */ + ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */ + if (ret) + goto error; + + /* request boot firmware */ + ret = af9015_write_reg_i2c(d, state->af9013_config[1].i2c_addr, + 0xe205, 1); + deb_info("%s: firmware boot cmd status:%d\n", __func__, ret); + if (ret) + goto error; + + for (i = 0; i < 15; i++) { + msleep(100); + + /* check firmware status */ + ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr, + 0x98be, &val); + deb_info("%s: firmware status cmd status:%d fw status:%02x\n", + __func__, ret, val); + if (ret) + goto error; + + if (val == 0x0c || val == 0x04) /* success or fail */ + break; + } + + if (val == 0x04) { + err("firmware did not run"); + ret = -1; + } else if (val != 0x0c) { + err("firmware boot timeout"); + ret = -1; + } + +error: +exit: + return ret; +} + +static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct af9015_state *state = adap_to_priv(adap); + + if (adap->id == 0) { + state->af9013_config[0].ts_mode = AF9013_TS_USB; + memcpy(state->af9013_config[0].api_version, "\x0\x1\x9\x0", 4); + state->af9013_config[0].gpio[0] = AF9013_GPIO_HI; + state->af9013_config[0].gpio[3] = AF9013_GPIO_TUNER_ON; + } else if (adap->id == 1) { + state->af9013_config[1].ts_mode = AF9013_TS_SERIAL; + memcpy(state->af9013_config[1].api_version, "\x0\x1\x9\x0", 4); + state->af9013_config[1].gpio[0] = AF9013_GPIO_TUNER_ON; + state->af9013_config[1].gpio[1] = AF9013_GPIO_LO; + + /* copy firmware to 2nd demodulator */ + if (state->dual_mode) { + ret = af9015_copy_firmware(adap_to_d(adap)); + if (ret) { + err("firmware copy to 2nd frontend " \ + "failed, will disable it"); + state->dual_mode = 0; + return -ENODEV; + } + } else { + return -ENODEV; + } + } + + /* attach demodulator */ + adap->fe[0] = dvb_attach(af9013_attach, + &state->af9013_config[adap->id], &adap_to_d(adap)->i2c_adap); + + /* + * AF9015 firmware does not like if it gets interrupted by I2C adapter + * request on some critical phases. During normal operation I2C adapter + * is used only 2nd demodulator and tuner on dual tuner devices. + * Override demodulator callbacks and use mutex for limit access to + * those "critical" paths to keep AF9015 happy. + */ + if (adap->fe[0]) { + state->set_frontend[adap->id] = + adap->fe[0]->ops.set_frontend; + adap->fe[0]->ops.set_frontend = + af9015_af9013_set_frontend; + + state->read_status[adap->id] = + adap->fe[0]->ops.read_status; + adap->fe[0]->ops.read_status = + af9015_af9013_read_status; + + state->init[adap->id] = adap->fe[0]->ops.init; + adap->fe[0]->ops.init = af9015_af9013_init; + + state->sleep[adap->id] = adap->fe[0]->ops.sleep; + adap->fe[0]->ops.sleep = af9015_af9013_sleep; + } + + return adap->fe[0] == NULL ? -ENODEV : 0; +} + +static struct mt2060_config af9015_mt2060_config = { + .i2c_address = 0xc0, + .clock_out = 0, +}; + +static struct qt1010_config af9015_qt1010_config = { + .i2c_address = 0xc4, +}; + +static struct tda18271_config af9015_tda18271_config = { + .gate = TDA18271_GATE_DIGITAL, + .small_i2c = TDA18271_16_BYTE_CHUNK_INIT, +}; + +static struct mxl5005s_config af9015_mxl5003_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_DEFAULT, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static struct mxl5005s_config af9015_mxl5005_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_OFF, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static struct mc44s803_config af9015_mc44s803_config = { + .i2c_address = 0xc0, + .dig_out = 1, +}; + +static struct tda18218_config af9015_tda18218_config = { + .i2c_address = 0xc0, + .i2c_wr_max = 21, /* max wr bytes AF9015 I2C adap can handle at once */ +}; + +static struct mxl5007t_config af9015_mxl5007t_config = { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, +}; + +static int af9015_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct af9015_state *state = adap_to_priv(adap); + int ret; + deb_info("%s:\n", __func__); + + switch (state->af9013_config[adap->id].tuner) { + case AF9013_TUNER_MT2060: + case AF9013_TUNER_MT2060_2: + ret = dvb_attach(mt2060_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, &af9015_mt2060_config, + state->mt2060_if1[adap->id]) + == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_QT1010: + case AF9013_TUNER_QT1010A: + ret = dvb_attach(qt1010_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &af9015_qt1010_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_TDA18271: + ret = dvb_attach(tda18271_attach, adap->fe[0], 0xc0, + &adap_to_d(adap)->i2c_adap, + &af9015_tda18271_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_TDA18218: + ret = dvb_attach(tda18218_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &af9015_tda18218_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MXL5003D: + ret = dvb_attach(mxl5005s_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &af9015_mxl5003_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + ret = dvb_attach(mxl5005s_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &af9015_mxl5005_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_ENV77H11D5: + ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0xc0, + &adap_to_d(adap)->i2c_adap, + DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MC44S803: + ret = dvb_attach(mc44s803_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &af9015_mc44s803_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MXL5007T: + ret = dvb_attach(mxl5007t_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + 0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_UNKNOWN: + default: + ret = -ENODEV; + err("Unknown tuner id:%d", + state->af9013_config[adap->id].tuner); + } + + if (adap->fe[0]->ops.tuner_ops.init) { + state->tuner_init[adap->id] = + adap->fe[0]->ops.tuner_ops.init; + adap->fe[0]->ops.tuner_ops.init = af9015_tuner_init; + } + + if (adap->fe[0]->ops.tuner_ops.sleep) { + state->tuner_sleep[adap->id] = + adap->fe[0]->ops.tuner_ops.sleep; + adap->fe[0]->ops.tuner_ops.sleep = af9015_tuner_sleep; + } + + return ret; +} + +static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + deb_info("%s: onoff:%d\n", __func__, onoff); + + if (onoff) + ret = af9015_set_reg_bit(adap_to_d(adap), 0xd503, 0); + else + ret = af9015_clear_reg_bit(adap_to_d(adap), 0xd503, 0); + + return ret; +} + +static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + int ret; + u8 idx; + + deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n", + __func__, index, pid, onoff); + + ret = af9015_write_reg(adap_to_d(adap), 0xd505, (pid & 0xff)); + if (ret) + goto error; + + ret = af9015_write_reg(adap_to_d(adap), 0xd506, (pid >> 8)); + if (ret) + goto error; + + idx = ((index & 0x1f) | (1 << 5)); + ret = af9015_write_reg(adap_to_d(adap), 0xd504, idx); + +error: + return ret; +} + +static int af9015_init_endpoint(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + int ret; + u16 frame_size; + u8 packet_size; + deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); + + if (d->udev->speed == USB_SPEED_FULL) { + frame_size = TS_USB11_FRAME_SIZE/4; + packet_size = TS_USB11_MAX_PACKET_SIZE/4; + } else { + frame_size = TS_USB20_FRAME_SIZE/4; + packet_size = TS_USB20_MAX_PACKET_SIZE/4; + } + + ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */ + if (ret) + goto error; + ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */ + if (ret) + goto error; + ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */ + if (ret) + goto error; + if (state->dual_mode) { + ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */ + if (ret) + goto error; + } + ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */ + if (ret) + goto error; + if (state->dual_mode) { + ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */ + if (ret) + goto error; + } + /* EP4 xfer length */ + ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd89, frame_size >> 8); + if (ret) + goto error; + /* EP5 xfer length */ + ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */ + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */ + if (ret) + goto error; + if (state->dual_mode) { + ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */ + if (ret) + goto error; + } + + /* enable / disable mp2if2 */ + if (state->dual_mode) + ret = af9015_set_reg_bit(d, 0xd50b, 0); + else + ret = af9015_clear_reg_bit(d, 0xd50b, 0); + +error: + if (ret) + err("endpoint init failed:%d", ret); + return ret; +} + +static int af9015_init(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + int ret; + deb_info("%s:\n", __func__); + + mutex_init(&state->fe_mutex); + + /* init RC canary */ + ret = af9015_write_reg(d, 0x98e9, 0xff); + if (ret) + goto error; + + ret = af9015_init_endpoint(d); + if (ret) + goto error; + +error: + return ret; +} + +struct af9015_rc_setup { + unsigned int id; + char *rc_codes; +}; + +static char *af9015_rc_setup_match(unsigned int id, + const struct af9015_rc_setup *table) +{ + for (; table->rc_codes; table++) + if (table->id == id) + return table->rc_codes; + return NULL; +} + +static const struct af9015_rc_setup af9015_rc_setup_modparam[] = { + { AF9015_REMOTE_A_LINK_DTU_M, RC_MAP_ALINK_DTU_M }, + { AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, RC_MAP_MSI_DIGIVOX_II }, + { AF9015_REMOTE_MYGICTV_U718, RC_MAP_TOTAL_MEDIA_IN_HAND }, + { AF9015_REMOTE_DIGITTRADE_DVB_T, RC_MAP_DIGITTRADE }, + { AF9015_REMOTE_AVERMEDIA_KS, RC_MAP_AVERMEDIA_RM_KS }, + { } +}; + +static const struct af9015_rc_setup af9015_rc_setup_hashes[] = { + { 0xb8feb708, RC_MAP_MSI_DIGIVOX_II }, + { 0xa3703d00, RC_MAP_ALINK_DTU_M }, + { 0x9b7dc64e, RC_MAP_TOTAL_MEDIA_IN_HAND }, /* MYGICTV U718 */ + { 0x5d49e3db, RC_MAP_DIGITTRADE }, /* LC-Power LC-USB-DVBT */ + { } +}; + +static int af9015_rc_query(struct dvb_usb_device *d) +{ + struct af9015_state *state = d_to_priv(d); + int ret; + u8 buf[17]; + + deb_info("%s:\n", __func__); + + /* read registers needed to detect remote controller code */ + ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf)); + if (ret) + goto error; + + /* If any of these are non-zero, assume invalid data */ + if (buf[1] || buf[2] || buf[3]) + return ret; + + /* Check for repeat of previous code */ + if ((state->rc_repeat != buf[6] || buf[0]) && + !memcmp(&buf[12], state->rc_last, 4)) { + deb_rc("%s: key repeated\n", __func__); + rc_keydown(d->rc_dev, state->rc_keycode, 0); + state->rc_repeat = buf[6]; + return ret; + } + + /* Only process key if canary killed */ + if (buf[16] != 0xff && buf[0] != 0x01) { + deb_rc("%s: key pressed %*ph\n", __func__, 4, buf + 12); + + /* Reset the canary */ + ret = af9015_write_reg(d, 0x98e9, 0xff); + if (ret) + goto error; + + /* Remember this key */ + memcpy(state->rc_last, &buf[12], 4); + if (buf[14] == (u8) ~buf[15]) { + if (buf[12] == (u8) ~buf[13]) { + /* NEC */ + state->rc_keycode = buf[12] << 8 | buf[14]; + } else { + /* NEC extended*/ + state->rc_keycode = buf[12] << 16 | + buf[13] << 8 | buf[14]; + } + } else { + /* 32 bit NEC */ + state->rc_keycode = buf[12] << 24 | buf[13] << 16 | + buf[14] << 8 | buf[15]; + } + rc_keydown(d->rc_dev, state->rc_keycode, 0); + } else { + deb_rc("%s: no key press\n", __func__); + /* Invalidate last keypress */ + /* Not really needed, but helps with debug */ + state->rc_last[2] = state->rc_last[3]; + } + + state->rc_repeat = buf[6]; + state->rc_failed = false; + +error: + if (ret) { + err("%s: failed:%d", __func__, ret); + + /* allow random errors as dvb-usb will stop polling on error */ + if (!state->rc_failed) + ret = 0; + + state->rc_failed = true; + } + + return ret; +} + +static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + struct af9015_state *state = d_to_priv(d); + u16 vid = le16_to_cpu(d->udev->descriptor.idVendor); + + if (state->ir_mode == AF9015_IR_MODE_DISABLED) + return 0; + + /* try to load remote based module param */ + if (!rc->map_name) + rc->map_name = af9015_rc_setup_match(dvb_usb_af9015_remote, + af9015_rc_setup_modparam); + + /* try to load remote based eeprom hash */ + if (!rc->map_name) + rc->map_name = af9015_rc_setup_match(state->eeprom_sum, + af9015_rc_setup_hashes); + + /* try to load remote based USB iManufacturer string */ + if (!rc->map_name && vid == USB_VID_AFATECH) { + /* Check USB manufacturer and product strings and try + to determine correct remote in case of chip vendor + reference IDs are used. + DO NOT ADD ANYTHING NEW HERE. Use hashes instead. */ + char manufacturer[10]; + memset(manufacturer, 0, sizeof(manufacturer)); + usb_string(d->udev, d->udev->descriptor.iManufacturer, + manufacturer, sizeof(manufacturer)); + if (!strcmp("MSI", manufacturer)) { + /* iManufacturer 1 MSI + iProduct 2 MSI K-VOX */ + rc->map_name = af9015_rc_setup_match( + AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, + af9015_rc_setup_modparam); + } + } + + /* load empty to enable rc */ + if (!rc->map_name) + rc->map_name = RC_MAP_EMPTY; + + rc->allowed_protos = RC_TYPE_NEC; + rc->query = af9015_rc_query; + rc->interval = 500; + + return 0; +} + +/* interface 0 is used by DVB-T receiver and + interface 1 is for remote controller (HID) */ +static struct dvb_usb_device_properties af9015_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct af9015_state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .identify_state = af9015_identify_state, + .firmware = "dvb-usb-af9015.fw", + .download_firmware = af9015_download_firmware, + + .i2c_algo = &af9015_i2c_algo, + .read_config = af9015_read_config, + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .init = af9015_init, + .get_rc_config = af9015_get_rc_config, + .get_stream_config = af9015_get_stream_config, + + .get_adapter_count = af9015_get_adapter_count, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .stream = DVB_USB_STREAM_BULK(0x84, 8, TS_USB20_FRAME_SIZE), + }, { + .stream = DVB_USB_STREAM_BULK(0x85, 8, TS_USB20_FRAME_SIZE), + }, + }, +}; + +static const struct usb_device_id af9015_id_table[] = { + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015, + &af9015_props, "Afatech AF9015 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016, + &af9015_props, "Afatech AF9015 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD, + &af9015_props, "Leadtek WinFast DTV Dongle Gold", RC_MAP_LEADTEK_Y04G0051) }, + { DVB_USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E, + &af9015_props, "Pinnacle PCTV 71e", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U, + &af9015_props, "KWorld PlusTV Dual DVB-T Stick (DVB-T 399U)", NULL) }, + { DVB_USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN, + &af9015_props, "DigitalNow TinyTwin", RC_MAP_AZUREWAVE_AD_TU700) }, + { DVB_USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700, + &af9015_props, "TwinHan AzureWave AD-TU700(704J)", RC_MAP_AZUREWAVE_AD_TU700) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2, + &af9015_props, "TerraTec Cinergy T USB XE", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T, + &af9015_props, "KWorld PlusTV Dual DVB-T PCI (DVB-T PC160-2T)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X, + &af9015_props, "AVerMedia AVerTV DVB-T Volar X", RC_MAP_AVERMEDIA_M135A) }, + { DVB_USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380, + &af9015_props, "Xtensions XD-380", NULL) }, + { DVB_USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO, + &af9015_props, "MSI DIGIVOX Duo", RC_MAP_MSI_DIGIVOX_III) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2, + &af9015_props, "Fujitsu-Siemens Slim Mobile USB DVB-T", NULL) }, + { DVB_USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2, + &af9015_props, "Telestar Starstick 2", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309, + &af9015_props, "AVerMedia A309", NULL) }, + { DVB_USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III, + &af9015_props, "MSI Digi VOX mini III", RC_MAP_MSI_DIGIVOX_III) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U, + &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2, + &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3, + &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT, + &af9015_props, "TrekStor DVB-T USB Stick", RC_MAP_TREKSTOR) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850, + &af9015_props, "AverMedia AVerTV Volar Black HD (A850)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805, + &af9015_props, "AverMedia AVerTV Volar GPS 805 (A805)", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU, + &af9015_props, "Conceptronic USB2.0 DVB-T CTVDIGRCU V3.0", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810, + &af9015_props, "KWorld Digial MC-810", NULL) }, + { DVB_USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03, + &af9015_props, "Genius TVGo DVB-T03", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2, + &af9015_props, "KWorld PlusTV Dual DVB-T Stick (DVB-T 399U)", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T, + &af9015_props, "KWorld PlusTV DVB-T PCI Pro Card (DVB-T PC160-T)", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20, + &af9015_props, "Sveon STV20 Tuner USB DVB-T HDTV", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2, + &af9015_props, "DigitalNow TinyTwin v2", RC_MAP_DIGITALNOW_TINYTWIN) }, + { DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS, + &af9015_props, "Leadtek WinFast DTV2000DS", RC_MAP_LEADTEK_Y04G0051) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T, + &af9015_props, "KWorld USB DVB-T Stick Mobile (UB383-T)", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4, + &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M, + &af9015_props, "AverMedia AVerTV Volar M (A815Mac)", NULL) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_RC, + &af9015_props, "TerraTec Cinergy T Stick RC", RC_MAP_TERRATEC_SLIM_2) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC, + &af9015_props, "TerraTec Cinergy T Stick Dual RC", RC_MAP_TERRATEC_SLIM) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T, + &af9015_props, "AverMedia AVerTV Red HD+ (A850T)", NULL) }, + { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3, + &af9015_props, "DigitalNow TinyTwin v3", RC_MAP_DIGITALNOW_TINYTWIN) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22, + &af9015_props, "Sveon STV22 Dual USB DVB-T Tuner HDTV", RC_MAP_MSI_DIGIVOX_III) }, + { } +}; +MODULE_DEVICE_TABLE(usb, af9015_id_table); + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9015_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = af9015_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(af9015_usb_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Afatech AF9015 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h new file mode 100644 index 000000000000..c6b304d962ad --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/af9015.h @@ -0,0 +1,170 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; 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 AF9015_H +#define AF9015_H + +#include +#include "dvb_usb.h" +#include "af9013.h" +#include "dvb-pll.h" +#include "mt2060.h" +#include "qt1010.h" +#include "tda18271.h" +#include "mxl5005s.h" +#include "mc44s803.h" +#include "tda18218.h" +#include "mxl5007t.h" + +#define DVB_USB_LOG_PREFIX "af9015" + +#ifdef CONFIG_DVB_USB_DEBUG +#define dprintk(var, level, args...) \ + do { if ((var & level)) printk(args); } while (0) +#define DVB_USB_DEBUG_STATUS +#else +#define dprintk(args...) +#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" +#endif + +#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args) +#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args) + +#undef err +#define err(format, arg...) \ + printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) \ + printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) + +/* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. + We use smaller - about 1/4 from the original, 5 and 87. */ +#define TS_PACKET_SIZE 188 + +#define TS_USB20_PACKET_COUNT 87 +#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) + +#define TS_USB11_PACKET_COUNT 5 +#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) + +#define TS_USB20_MAX_PACKET_SIZE 512 +#define TS_USB11_MAX_PACKET_SIZE 64 + +#define AF9015_I2C_EEPROM 0xa0 +#define AF9015_I2C_DEMOD 0x38 +#define AF9015_USB_TIMEOUT 2000 + +/* EEPROM locations */ +#define AF9015_EEPROM_IR_MODE 0x18 +#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34 +#define AF9015_EEPROM_TS_MODE 0x31 +#define AF9015_EEPROM_DEMOD2_I2C 0x32 + +#define AF9015_EEPROM_SAW_BW1 0x35 +#define AF9015_EEPROM_XTAL_TYPE1 0x36 +#define AF9015_EEPROM_SPEC_INV1 0x37 +#define AF9015_EEPROM_IF1L 0x38 +#define AF9015_EEPROM_IF1H 0x39 +#define AF9015_EEPROM_MT2060_IF1L 0x3a +#define AF9015_EEPROM_MT2060_IF1H 0x3b +#define AF9015_EEPROM_TUNER_ID1 0x3c + +#define AF9015_EEPROM_SAW_BW2 0x45 +#define AF9015_EEPROM_XTAL_TYPE2 0x46 +#define AF9015_EEPROM_SPEC_INV2 0x47 +#define AF9015_EEPROM_IF2L 0x48 +#define AF9015_EEPROM_IF2H 0x49 +#define AF9015_EEPROM_MT2060_IF2L 0x4a +#define AF9015_EEPROM_MT2060_IF2H 0x4b +#define AF9015_EEPROM_TUNER_ID2 0x4c + +#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1) + +struct req_t { + u8 cmd; /* [0] */ + /* seq */ /* [1] */ + u8 i2c_addr; /* [2] */ + u16 addr; /* [3|4] */ + u8 mbox; /* [5] */ + u8 addr_len; /* [6] */ + u8 data_len; /* [7] */ + u8 *data; +}; + +enum af9015_cmd { + GET_CONFIG = 0x10, + DOWNLOAD_FIRMWARE = 0x11, + BOOT = 0x13, + READ_MEMORY = 0x20, + WRITE_MEMORY = 0x21, + READ_WRITE_I2C = 0x22, + COPY_FIRMWARE = 0x23, + RECONNECT_USB = 0x5a, + WRITE_VIRTUAL_MEMORY = 0x26, + GET_IR_CODE = 0x27, + READ_I2C, + WRITE_I2C, +}; + +enum af9015_ir_mode { + AF9015_IR_MODE_DISABLED = 0, + AF9015_IR_MODE_HID, + AF9015_IR_MODE_RLC, + AF9015_IR_MODE_RC6, + AF9015_IR_MODE_POLLING, /* just guess */ +}; + +struct af9015_state { + u8 ir_mode; + u8 rc_repeat; + u32 rc_keycode; + u8 rc_last[4]; + bool rc_failed; + u8 dual_mode; + u8 seq; /* packet sequence number */ + u16 mt2060_if1[2]; + u16 firmware_size; + u16 firmware_checksum; + u32 eeprom_sum; + struct af9013_config af9013_config[2]; + + /* for demod callback override */ + int (*set_frontend[2]) (struct dvb_frontend *fe); + int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status); + int (*init[2]) (struct dvb_frontend *fe); + int (*sleep[2]) (struct dvb_frontend *fe); + int (*tuner_init[2]) (struct dvb_frontend *fe); + int (*tuner_sleep[2]) (struct dvb_frontend *fe); + struct mutex fe_mutex; +}; + +enum af9015_remote { + AF9015_REMOTE_NONE = 0, +/* 1 */ AF9015_REMOTE_A_LINK_DTU_M, + AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, + AF9015_REMOTE_MYGICTV_U718, + AF9015_REMOTE_DIGITTRADE_DVB_T, +/* 5 */ AF9015_REMOTE_AVERMEDIA_KS, +}; + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c new file mode 100644 index 000000000000..bb90b877d07b --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -0,0 +1,1086 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 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 "af9035.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static u16 af9035_checksum(const u8 *buf, size_t len) +{ + size_t i; + u16 checksum = 0; + + for (i = 1; i < len; i++) { + if (i % 2) + checksum += buf[i] << 8; + else + checksum += buf[i]; + } + checksum = ~checksum; + + return checksum; +} + +static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) +{ +#define BUF_LEN 64 +#define REQ_HDR_LEN 4 /* send header size */ +#define ACK_HDR_LEN 3 /* rece header size */ +#define CHECKSUM_LEN 2 +#define USB_TIMEOUT 2000 + struct state *state = d_to_priv(d); + int ret, wlen, rlen; + u8 buf[BUF_LEN]; + u16 checksum, tmp_checksum; + + /* buffer overflow check */ + if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || + req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { + pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__, + req->wlen, req->rlen); + return -EINVAL; + } + + buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1; + buf[1] = req->mbox; + buf[2] = req->cmd; + buf[3] = state->seq++; + memcpy(&buf[REQ_HDR_LEN], req->wbuf, req->wlen); + + wlen = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN; + rlen = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN; + + /* calc and add checksum */ + checksum = af9035_checksum(buf, buf[0] - 1); + buf[buf[0] - 1] = (checksum >> 8); + buf[buf[0] - 0] = (checksum & 0xff); + + /* no ack for these packets */ + if (req->cmd == CMD_FW_DL) + rlen = 0; + + ret = dvb_usbv2_generic_rw(d, buf, wlen, buf, rlen); + if (ret) + goto err; + + /* no ack for those packets */ + if (req->cmd == CMD_FW_DL) + goto exit; + + /* verify checksum */ + checksum = af9035_checksum(buf, rlen - 2); + tmp_checksum = (buf[rlen - 2] << 8) | buf[rlen - 1]; + if (tmp_checksum != checksum) { + pr_err("%s: command=%02x checksum mismatch (%04x != %04x)\n", + KBUILD_MODNAME, req->cmd, tmp_checksum, + checksum); + ret = -EIO; + goto err; + } + + /* check status */ + if (buf[2]) { + pr_debug("%s: command=%02x failed fw error=%d\n", __func__, + req->cmd, buf[2]); + ret = -EIO; + goto err; + } + + /* read request, copy returned data to return buf */ + if (req->rlen) + memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen); + +exit: + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* write multiple registers */ +static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[6 + len]; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL }; + + wbuf[0] = len; + wbuf[1] = 2; + wbuf[2] = 0; + wbuf[3] = 0; + wbuf[4] = (reg >> 8) & 0xff; + wbuf[5] = (reg >> 0) & 0xff; + memcpy(&wbuf[6], val, len); + + return af9035_ctrl_msg(d, &req); +} + +/* read multiple registers */ +static int af9035_rd_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[] = { len, 2, 0, 0, (reg >> 8) & 0xff, reg & 0xff }; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_RD, mbox, sizeof(wbuf), wbuf, len, val }; + + return af9035_ctrl_msg(d, &req); +} + +/* write single register */ +static int af9035_wr_reg(struct dvb_usb_device *d, u32 reg, u8 val) +{ + return af9035_wr_regs(d, reg, &val, 1); +} + +/* read single register */ +static int af9035_rd_reg(struct dvb_usb_device *d, u32 reg, u8 *val) +{ + return af9035_rd_regs(d, reg, val, 1); +} + +/* write single register with mask */ +static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = af9035_rd_regs(d, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return af9035_wr_regs(d, reg, &val, 1); +} + +static int af9035_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct state *state = d_to_priv(d); + int ret; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + /* + * I2C sub header is 5 bytes long. Meaning of those bytes are: + * 0: data len + * 1: I2C addr << 1 + * 2: reg addr len + * byte 3 and 4 can be used as reg addr + * 3: reg addr MSB + * used when reg addr len is set to 2 + * 4: reg addr LSB + * used when reg addr len is set to 1 or 2 + * + * For the simplify we do not use register addr at all. + * NOTE: As a firmware knows tuner type there is very small possibility + * there could be some tuner I2C hacks done by firmware and this may + * lead problems if firmware expects those bytes are used. + */ + if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + if (msg[0].len > 40 || msg[1].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_rd_regs(d, reg, &msg[1].buf[0], + msg[1].len); + } else { + /* I2C */ + u8 buf[5 + msg[0].len]; + struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), + buf, msg[1].len, msg[1].buf }; + buf[0] = msg[1].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d, &req); + } + } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + if (msg[0].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], + msg[0].len - 3); + } else { + /* I2C */ + u8 buf[5 + msg[0].len]; + struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, + 0, NULL }; + buf[0] = msg[0].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d, &req); + } + } else { + /* + * We support only two kind of I2C transactions: + * 1) 1 x read + 1 x write + * 2) 1 x write + */ + ret = -EOPNOTSUPP; + } + + mutex_unlock(&d->i2c_mutex); + + if (ret < 0) + return ret; + else + return num; +} + +static u32 af9035_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9035_i2c_algo = { + .master_xfer = af9035_i2c_master_xfer, + .functionality = af9035_i2c_functionality, +}; + +static int af9035_identify_state(struct dvb_usb_device *d, const char **name) +{ + int ret; + u8 wbuf[1] = { 1 }; + u8 rbuf[4]; + struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, + sizeof(rbuf), rbuf }; + + ret = af9035_ctrl_msg(d, &req); + if (ret < 0) + goto err; + + pr_debug("%s: reply=%*ph\n", __func__, 4, rbuf); + if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3]) + ret = WARM; + else + ret = COLD; + + return ret; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware(struct dvb_usb_device *d, + const struct firmware *fw) +{ + int ret, i, j, len; + u8 wbuf[1]; + u8 rbuf[4]; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + u8 hdr_core; + u16 hdr_addr, hdr_data_len, hdr_checksum; + #define MAX_DATA 58 + #define HDR_SIZE 7 + + /* + * Thanks to Daniel Glöckner about that info! + * + * byte 0: MCS 51 core + * There are two inside the AF9035 (1=Link and 2=OFDM) with separate + * address spaces + * byte 1-2: Big endian destination address + * byte 3-4: Big endian number of data bytes following the header + * byte 5-6: Big endian header checksum, apparently ignored by the chip + * Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256) + */ + + for (i = fw->size; i > HDR_SIZE;) { + hdr_core = fw->data[fw->size - i + 0]; + hdr_addr = fw->data[fw->size - i + 1] << 8; + hdr_addr |= fw->data[fw->size - i + 2] << 0; + hdr_data_len = fw->data[fw->size - i + 3] << 8; + hdr_data_len |= fw->data[fw->size - i + 4] << 0; + hdr_checksum = fw->data[fw->size - i + 5] << 8; + hdr_checksum |= fw->data[fw->size - i + 6] << 0; + + pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n", + __func__, hdr_core, hdr_addr, hdr_data_len, + hdr_checksum); + + if (((hdr_core != 1) && (hdr_core != 2)) || + (hdr_data_len > i)) { + pr_debug("%s: bad firmware\n", __func__); + break; + } + + /* download begin packet */ + req.cmd = CMD_FW_DL_BEGIN; + ret = af9035_ctrl_msg(d, &req); + if (ret < 0) + goto err; + + /* download firmware packet(s) */ + for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) { + len = j; + if (len > MAX_DATA) + len = MAX_DATA; + req_fw_dl.wlen = len; + req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i + + HDR_SIZE + hdr_data_len - j]; + ret = af9035_ctrl_msg(d, &req_fw_dl); + if (ret < 0) + goto err; + } + + /* download end packet */ + req.cmd = CMD_FW_DL_END; + ret = af9035_ctrl_msg(d, &req); + if (ret < 0) + goto err; + + i -= hdr_data_len + HDR_SIZE; + + pr_debug("%s: data uploaded=%zu\n", __func__, fw->size - i); + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(d, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(d, &req_fw_ver); + if (ret < 0) + goto err; + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + pr_err("%s: firmware did not run\n", KBUILD_MODNAME); + ret = -ENODEV; + goto err; + } + + pr_info("%s: firmware version=%d.%d.%d.%d", KBUILD_MODNAME, + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware_it9135(struct dvb_usb_device *d, + const struct firmware *fw) +{ + int ret, i, i_prev; + u8 wbuf[1]; + u8 rbuf[4]; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + #define HDR_SIZE 7 + + /* + * There seems to be following firmware header. Meaning of bytes 0-3 + * is unknown. + * + * 0: 3 + * 1: 0, 1 + * 2: 0 + * 3: 1, 2, 3 + * 4: addr MSB + * 5: addr LSB + * 6: count of data bytes ? + */ + + for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) { + if (i == fw->size || + (fw->data[i + 0] == 0x03 && + (fw->data[i + 1] == 0x00 || + fw->data[i + 1] == 0x01) && + fw->data[i + 2] == 0x00)) { + req_fw_dl.wlen = i - i_prev; + req_fw_dl.wbuf = (u8 *) &fw->data[i_prev]; + i_prev = i; + ret = af9035_ctrl_msg(d, &req_fw_dl); + if (ret < 0) + goto err; + + pr_debug("%s: data uploaded=%d\n", __func__, i); + } + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(d, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(d, &req_fw_ver); + if (ret < 0) + goto err; + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + pr_err("%s: firmware did not run\n", KBUILD_MODNAME); + ret = -ENODEV; + goto err; + } + + pr_info("%s: firmware version=%d.%d.%d.%d", KBUILD_MODNAME, + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_read_config(struct dvb_usb_device *d) +{ + struct state *state = d_to_priv(d); + int ret, i, eeprom_shift = 0; + u8 tmp; + u16 tmp16; + + /* check if there is dual tuners */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + state->dual_mode = tmp; + pr_debug("%s: dual mode=%d\n", __func__, state->dual_mode); + + for (i = 0; i < state->dual_mode + 1; i++) { + /* tuner */ + ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + state->af9033_config[i].tuner = tmp; + pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp); + + switch (tmp) { + case AF9033_TUNER_TUA9001: + case AF9033_TUNER_FC0011: + case AF9033_TUNER_MXL5007T: + case AF9033_TUNER_TDA18218: + state->af9033_config[i].spec_inv = 1; + break; + default: + pr_info("%s: tuner ID=%02x not supported, please " \ + "report!", KBUILD_MODNAME, tmp); + }; + + /* tuner IF frequency */ + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 = tmp; + + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 |= tmp << 8; + + pr_debug("%s: [%d]IF=%d\n", __func__, i, tmp16); + + eeprom_shift = 0x10; /* shift for the 2nd tuner params */ + } + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < ARRAY_SIZE(state->af9033_config); i++) + state->af9033_config[i].clock = clock_lut[tmp]; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_read_config_it9135(struct dvb_usb_device *d) +{ + struct state *state = d_to_priv(d); + int ret, i; + u8 tmp; + + state->dual_mode = false; + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < ARRAY_SIZE(state->af9033_config); i++) + state->af9033_config[i].clock = clock_lut_it9135[tmp]; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int ret; + + switch (cmd) { + case FC0011_FE_CALLBACK_POWER: + /* Tuner enable */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 1, 1); + if (ret < 0) + goto err; + + /* LED */ + ret = af9035_wr_reg_mask(d, 0xd8d0, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8d1, 1, 1); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + break; + case FC0011_FE_CALLBACK_RESET: + ret = af9035_wr_reg(d, 0xd8e9, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e8, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e7, 1); + if (ret < 0) + goto err; + + usleep_range(10000, 20000); + + ret = af9035_wr_reg(d, 0xd8e7, 0); + if (ret < 0) + goto err; + + usleep_range(10000, 20000); + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) +{ + struct state *state = d_to_priv(d); + + switch (state->af9033_config[0].tuner) { + case AF9033_TUNER_FC0011: + return af9035_fc0011_tuner_callback(d, cmd, arg); + default: + break; + } + + return -ENODEV; +} + +static int af9035_frontend_callback(void *adapter_priv, int component, + int cmd, int arg) +{ + struct i2c_adapter *adap = adapter_priv; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + switch (component) { + case DVB_FRONTEND_COMPONENT_TUNER: + return af9035_tuner_callback(d, cmd, arg); + default: + break; + } + + return -EINVAL; +} + +static int af9035_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + int ret; + + if (!state->af9033_config[adap->id].tuner) { + /* unsupported tuner */ + ret = -ENODEV; + goto err; + } + + if (adap->id == 0) { + state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB; + state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL; + + ret = af9035_wr_reg(d, 0x00417f, + state->af9033_config[1].i2c_addr); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d81a, + state->dual_mode); + if (ret < 0) + goto err; + } + + /* attach demodulator */ + adap->fe[0] = dvb_attach(af9033_attach, + &state->af9033_config[adap->id], &d->i2c_adap); + if (adap->fe[0] == NULL) { + ret = -ENODEV; + goto err; + } + + /* disable I2C-gate */ + adap->fe[0]->ops.i2c_gate_ctrl = NULL; + adap->fe[0]->callback = af9035_frontend_callback; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static struct tua9001_config af9035_tua9001_config = { + .i2c_addr = 0x60, +}; + +static const struct fc0011_config af9035_fc0011_config = { + .i2c_address = 0x60, +}; + +static struct mxl5007t_config af9035_mxl5007t_config = { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 0, + .clk_out_enable = 0, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, +}; + +static struct tda18218_config af9035_tda18218_config = { + .i2c_address = 0x60, + .i2c_wr_max = 21, +}; + +static int af9035_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + int ret; + struct dvb_frontend *fe; + + switch (state->af9033_config[adap->id].tuner) { + case AF9033_TUNER_TUA9001: + /* AF9035 gpiot3 = TUA9001 RESETN + AF9035 gpiot2 = TUA9001 RXEN */ + + /* configure gpiot2 and gpiot2 as output */ + ret = af9035_wr_reg_mask(d, 0x00d8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8ed, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8e8, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8e9, 0x01, 0x01); + if (ret < 0) + goto err; + + /* reset tuner */ + ret = af9035_wr_reg_mask(d, 0x00d8e7, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(2000, 20000); + + ret = af9035_wr_reg_mask(d, 0x00d8e7, 0x01, 0x01); + if (ret < 0) + goto err; + + /* activate tuner RX */ + /* TODO: use callback for TUA9001 RXEN */ + ret = af9035_wr_reg_mask(d, 0x00d8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + /* attach tuner */ + fe = dvb_attach(tua9001_attach, adap->fe[0], + &d->i2c_adap, &af9035_tua9001_config); + break; + case AF9033_TUNER_FC0011: + fe = dvb_attach(fc0011_attach, adap->fe[0], + &d->i2c_adap, &af9035_fc0011_config); + break; + case AF9033_TUNER_MXL5007T: + ret = af9035_wr_reg(d, 0x00d8e0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8e1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8df, 0); + if (ret < 0) + goto err; + + msleep(30); + + ret = af9035_wr_reg(d, 0x00d8df, 1); + if (ret < 0) + goto err; + + msleep(300); + + ret = af9035_wr_reg(d, 0x00d8c0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8c1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8bf, 0); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b4, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b5, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b3, 1); + if (ret < 0) + goto err; + + /* attach tuner */ + fe = dvb_attach(mxl5007t_attach, adap->fe[0], + &d->i2c_adap, 0x60, &af9035_mxl5007t_config); + break; + case AF9033_TUNER_TDA18218: + /* attach tuner */ + fe = dvb_attach(tda18218_attach, adap->fe[0], + &d->i2c_adap, &af9035_tda18218_config); + break; + default: + fe = NULL; + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_init(struct dvb_usb_device *d) +{ + struct state *state = d_to_priv(d); + int ret, i; + u16 frame_size = 87 * 188 / 4; + u8 packet_size = 512 / 4; + struct reg_val_mask tab[] = { + { 0x80f99d, 0x01, 0x01 }, + { 0x80f9a4, 0x01, 0x01 }, + { 0x00dd11, 0x00, 0x20 }, + { 0x00dd11, 0x00, 0x40 }, + { 0x00dd13, 0x00, 0x20 }, + { 0x00dd13, 0x00, 0x40 }, + { 0x00dd11, 0x20, 0x20 }, + { 0x00dd88, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd89, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0c, packet_size, 0xff}, + { 0x00dd11, state->dual_mode << 6, 0x40 }, + { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0d, packet_size, 0xff }, + { 0x80f9a3, 0x00, 0x01 }, + { 0x80f9cd, 0x00, 0x01 }, + { 0x80f99d, 0x00, 0x01 }, + { 0x80f9a4, 0x00, 0x01 }, + }; + + pr_debug("%s: USB speed=%d frame_size=%04x packet_size=%02x\n", + __func__, d->udev->speed, frame_size, packet_size); + + /* init endpoints */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9035_wr_reg_mask(d, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_rc_query(struct dvb_usb_device *d) +{ + unsigned int key; + unsigned char b[4]; + int ret; + struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, b }; + + ret = af9035_ctrl_msg(d, &req); + if (ret < 0) + goto err; + + if ((b[2] + b[3]) == 0xff) { + if ((b[0] + b[1]) == 0xff) { + /* NEC */ + key = b[0] << 8 | b[2]; + } else { + /* ext. NEC */ + key = b[0] << 16 | b[1] << 8 | b[2]; + } + } else { + key = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; + } + + rc_keydown(d->rc_dev, key, 0); + +err: + /* ignore errors */ + return 0; +} + +static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + int ret; + u8 tmp; + + ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp); + if (ret < 0) + goto err; + + pr_debug("%s: ir_mode=%02x\n", __func__, tmp); + + /* don't activate rc if in HID mode or if not available */ + if (tmp == 5) { + ret = af9035_rd_reg(d, EEPROM_IR_TYPE, &tmp); + if (ret < 0) + goto err; + + pr_debug("%s: ir_type=%02x\n", __func__, tmp); + + switch (tmp) { + case 0: /* NEC */ + default: + rc->allowed_protos = RC_TYPE_NEC; + break; + case 1: /* RC6 */ + rc->allowed_protos = RC_TYPE_RC6; + break; + } + + rc->query = af9035_rc_query; + rc->interval = 500; + + /* load empty to enable rc */ + if (!rc->map_name) + rc->map_name = RC_MAP_EMPTY; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* interface 0 is used by DVB-T receiver and + interface 1 is for remote controller (HID) */ +static const struct dvb_usb_device_properties af9035_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .identify_state = af9035_identify_state, + .firmware = "dvb-usb-af9035-02.fw", + .download_firmware = af9035_download_firmware, + + .i2c_algo = &af9035_i2c_algo, + .read_config = af9035_read_config, + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .init = af9035_init, + .get_rc_config = af9035_get_rc_config, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), + }, { + .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188), + }, + }, +}; + +static const struct dvb_usb_device_properties it9135_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .identify_state = af9035_identify_state, + .firmware = "dvb-usb-it9135-01.fw", + .download_firmware = af9035_download_firmware_it9135, + + .i2c_algo = &af9035_i2c_algo, + .read_config = af9035_read_config_it9135, + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .init = af9035_init, + .get_rc_config = af9035_get_rc_config, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), + }, { + .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188), + }, + }, +}; + +static const struct usb_device_id af9035_id_table[] = { + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035, + &af9035_props, "Afatech AF9035 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1000, + &af9035_props, "Afatech AF9035 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1001, + &af9035_props, "Afatech AF9035 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1002, + &af9035_props, "Afatech AF9035 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1003, + &af9035_props, "Afatech AF9035 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK, + &af9035_props, "TerraTec Cinergy T Stick", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835, + &af9035_props, "AVerMedia AVerTV Volar HD/PRO (A835)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835, + &af9035_props, "AVerMedia AVerTV Volar HD/PRO (A835)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867, + &af9035_props, "AVerMedia HD Volar (A867)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867, + &af9035_props, "AVerMedia HD Volar (A867)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR, + &af9035_props, "AVerMedia Twinstar (A825)", NULL) }, + { } +}; +MODULE_DEVICE_TABLE(usb, af9035_id_table); + +static struct usb_driver af9035_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = af9035_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(af9035_usb_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Afatech AF9035 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h new file mode 100644 index 000000000000..59ff69ede0f0 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -0,0 +1,111 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 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 AF9035_H +#define AF9035_H + +#include "dvb_usb.h" +#include "af9033.h" +#include "tua9001.h" +#include "fc0011.h" +#include "mxl5007t.h" +#include "tda18218.h" + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct usb_req { + u8 cmd; + u8 mbox; + u8 wlen; + u8 *wbuf; + u8 rlen; + u8 *rbuf; +}; + +struct state { + u8 seq; /* packet sequence number */ + bool dual_mode; + + struct af9033_config af9033_config[2]; +}; + +u32 clock_lut[] = { + 20480000, /* FPGA */ + 16384000, /* 16.38 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ + 12000000, /* 12.00 MHz */ +}; + +u32 clock_lut_it9135[] = { + 12000000, /* 12.00 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ +}; + +/* EEPROM locations */ +#define EEPROM_IR_MODE 0x430d +#define EEPROM_DUAL_MODE 0x4326 +#define EEPROM_IR_TYPE 0x4329 +#define EEPROM_1_IFFREQ_L 0x432d +#define EEPROM_1_IFFREQ_H 0x432e +#define EEPROM_1_TUNER_ID 0x4331 +#define EEPROM_2_IFFREQ_L 0x433d +#define EEPROM_2_IFFREQ_H 0x433e +#define EEPROM_2_TUNER_ID 0x4341 + +/* USB commands */ +#define CMD_MEM_RD 0x00 +#define CMD_MEM_WR 0x01 +#define CMD_I2C_RD 0x02 +#define CMD_I2C_WR 0x03 +#define CMD_IR_GET 0x18 +#define CMD_FW_DL 0x21 +#define CMD_FW_QUERYINFO 0x22 +#define CMD_FW_BOOT 0x23 +#define CMD_FW_DL_BEGIN 0x24 +#define CMD_FW_DL_END 0x25 +#define CMD_FW_SCATTER_WR 0x29 + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c new file mode 100644 index 000000000000..fb3829a73d2d --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -0,0 +1,1324 @@ +/* + * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver + * + * Copyright (C) 2007 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. + * + * TODO: + * - add smart card reader support for Conditional Access (CA) + * + * Card reader in Anysee is nothing more than ISO 7816 card reader. + * There is no hardware CAM in any Anysee device sold. + * In my understanding it should be implemented by making own module + * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This + * module registers serial interface that can be used to communicate + * with any ISO 7816 smart card. + * + * Any help according to implement serial smart card reader support + * is highly welcome! + */ + +#include "anysee.h" +#include "dvb-pll.h" +#include "tda1002x.h" +#include "mt352.h" +#include "mt352_priv.h" +#include "zl10353.h" +#include "tda18212.h" +#include "cx24116.h" +#include "stv0900.h" +#include "stv6110.h" +#include "isl6423.h" +#include "cxd2820r.h" + +/* debug */ +static int dvb_usb_anysee_debug; +module_param_named(debug, dvb_usb_anysee_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static DEFINE_MUTEX(anysee_usb_mutex); + +static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, + u8 *rbuf, u8 rlen) +{ + struct anysee_state *state = d_to_priv(d); + int act_len, ret, i; + u8 buf[64]; + + memcpy(&buf[0], sbuf, slen); + buf[60] = state->seq++; + + mutex_lock(&anysee_usb_mutex); + + deb_xfer(">>> "); + debug_dump(buf, slen, deb_xfer); + + /* We need receive one message more after dvb_usb_generic_rw due + to weird transaction flow, which is 1 x send + 2 x receive. */ + ret = dvb_usbv2_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf)); + if (ret) + goto error_unlock; + + /* TODO FIXME: dvb_usb_generic_rw() fails rarely with error code -32 + * (EPIPE, Broken pipe). Function supports currently msleep() as a + * parameter but I would not like to use it, since according to + * Documentation/timers/timers-howto.txt it should not be used such + * short, under < 20ms, sleeps. Repeating failed message would be + * better choice as not to add unwanted delays... + * Fixing that correctly is one of those or both; + * 1) use repeat if possible + * 2) add suitable delay + */ + + /* get answer, retry few times if error returned */ + for (i = 0; i < 3; i++) { + /* receive 2nd answer */ + ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, + d->props->generic_bulk_ctrl_endpoint), buf, sizeof(buf), + &act_len, 2000); + + if (ret) { + deb_info("%s: recv bulk message failed: %d", + __func__, ret); + } else { + deb_xfer("<<< "); + debug_dump(buf, rlen, deb_xfer); + + if (buf[63] != 0x4f) + deb_info("%s: cmd failed\n", __func__); + + break; + } + } + + if (ret) { + /* all retries failed, it is fatal */ + err("%s: recv bulk message failed: %d", __func__, ret); + goto error_unlock; + } + + /* read request, copy returned data to return buf */ + if (rbuf && rlen) + memcpy(rbuf, buf, rlen); + +error_unlock: + mutex_unlock(&anysee_usb_mutex); + + return ret; +} + +static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +{ + u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01}; + int ret; + ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1); + deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val); + return ret; +} + +static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val) +{ + u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val}; + deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +/* write single register with mask */ +static int anysee_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = anysee_read_reg(d, reg, &tmp); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return anysee_write_reg(d, reg, val); +} + +/* read single register with mask */ +static int anysee_rd_reg_mask(struct dvb_usb_device *d, u16 reg, u8 *val, + u8 mask) +{ + int ret, i; + u8 tmp; + + ret = anysee_read_reg(d, reg, &tmp); + if (ret) + return ret; + + tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = tmp >> i; + + return 0; +} + +static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id) +{ + u8 buf[] = {CMD_GET_HW_INFO}; + return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3); +} + +static int anysee_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00}; + deb_info("%s: onoff:%02x\n", __func__, onoff); + return anysee_ctrl_msg(fe_to_d(fe), buf, sizeof(buf), NULL, 0); +} + +static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval) +{ + u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval}; + deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff) +{ + u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff}; + deb_info("%s: onoff:%02x\n", __func__, onoff); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +/* I2C */ +static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0, inc, i = 0; + u8 buf[52]; /* 4 + 48 (I2C WR USB command header + I2C WR max) */ + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].len > 2 || msg[i+1].len > 60) { + ret = -EOPNOTSUPP; + break; + } + buf[0] = CMD_I2C_READ; + buf[1] = (msg[i].addr << 1) | 0x01; + buf[2] = msg[i].buf[0]; + buf[3] = msg[i].buf[1]; + buf[4] = msg[i].len-1; + buf[5] = msg[i+1].len; + ret = anysee_ctrl_msg(d, buf, 6, msg[i+1].buf, + msg[i+1].len); + inc = 2; + } else { + if (msg[i].len > 48) { + ret = -EOPNOTSUPP; + break; + } + buf[0] = CMD_I2C_WRITE; + buf[1] = (msg[i].addr << 1); + buf[2] = msg[i].len; + buf[3] = 0x01; + memcpy(&buf[4], msg[i].buf, msg[i].len); + ret = anysee_ctrl_msg(d, buf, 4 + msg[i].len, NULL, 0); + inc = 1; + } + if (ret) + break; + + i += inc; + } + + mutex_unlock(&d->i2c_mutex); + + return ret ? ret : i; +} + +static u32 anysee_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm anysee_i2c_algo = { + .master_xfer = anysee_master_xfer, + .functionality = anysee_i2c_func, +}; + +static int anysee_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + +/* Callbacks for DVB USB */ +static struct tda10023_config anysee_tda10023_config = { + .demod_address = (0x1a >> 1), + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_C, + .deltaf = 0xfeeb, +}; + +static struct mt352_config anysee_mt352_config = { + .demod_address = (0x1e >> 1), + .demod_init = anysee_mt352_demod_init, +}; + +static struct zl10353_config anysee_zl10353_config = { + .demod_address = (0x1e >> 1), + .parallel_ts = 1, +}; + +static struct zl10353_config anysee_zl10353_tda18212_config2 = { + .demod_address = (0x1e >> 1), + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, + .no_tuner = 1, + .if2 = 41500, +}; + +static struct zl10353_config anysee_zl10353_tda18212_config = { + .demod_address = (0x18 >> 1), + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, + .no_tuner = 1, + .if2 = 41500, +}; + +static struct tda10023_config anysee_tda10023_tda18212_config = { + .demod_address = (0x1a >> 1), + .xtal = 16000000, + .pll_m = 12, + .pll_p = 3, + .pll_n = 1, + .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_B, + .deltaf = 0xba02, +}; + +static struct tda18212_config anysee_tda18212_config = { + .i2c_address = (0xc0 >> 1), + .if_dvbt_6 = 4150, + .if_dvbt_7 = 4150, + .if_dvbt_8 = 4150, + .if_dvbc = 5000, +}; + +static struct tda18212_config anysee_tda18212_config2 = { + .i2c_address = 0x60 /* (0xc0 >> 1) */, + .if_dvbt_6 = 3550, + .if_dvbt_7 = 3700, + .if_dvbt_8 = 4150, + .if_dvbt2_6 = 3250, + .if_dvbt2_7 = 4000, + .if_dvbt2_8 = 4000, + .if_dvbc = 5000, +}; + +static struct cx24116_config anysee_cx24116_config = { + .demod_address = (0xaa >> 1), + .mpg_clk_pos_pol = 0x00, + .i2c_wr_max = 48, +}; + +static struct stv0900_config anysee_stv0900_config = { + .demod_address = (0xd0 >> 1), + .demod_mode = 0, + .xtal = 8000000, + .clkmode = 3, + .diseqc_mode = 2, + .tun1_maddress = 0, + .tun1_adc = 1, /* 1 Vpp */ + .path1_mode = 3, +}; + +static struct stv6110_config anysee_stv6110_config = { + .i2c_address = (0xc0 >> 1), + .mclk = 16000000, + .clk_div = 1, +}; + +static struct isl6423_config anysee_isl6423_config = { + .current_max = SEC_CURRENT_800m, + .curlim = SEC_CURRENT_LIM_OFF, + .mod_extern = 1, + .addr = (0x10 >> 1), +}; + +static struct cxd2820r_config anysee_cxd2820r_config = { + .i2c_address = 0x6d, /* (0xda >> 1) */ + .ts_mode = 0x38, +}; + +/* + * New USB device strings: Mfr=1, Product=2, SerialNumber=0 + * Manufacturer: AMT.CO.KR + * + * E30 VID=04b4 PID=861f HW=2 FW=2.1 Product=???????? + * PCB: ? + * parts: DNOS404ZH102A(MT352, DTT7579(?)) + * + * E30 VID=04b4 PID=861f HW=2 FW=2.1 "anysee-T(LP)" + * PCB: PCB 507T (rev1.61) + * parts: DNOS404ZH103A(ZL10353, DTT7579(?)) + * OEA=0a OEB=00 OEC=00 OED=ff OEE=00 + * IOA=45 IOB=ff IOC=00 IOD=ff IOE=00 + * + * E30 Plus VID=04b4 PID=861f HW=6 FW=1.0 "anysee" + * PCB: 507CD (rev1.1) + * parts: DNOS404ZH103A(ZL10353, DTT7579(?)), CST56I01 + * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe + * IOA=4f IOB=ff IOC=00 IOD=06 IOE=01 + * IOD[0] ZL10353 1=enabled + * IOA[7] TS 0=enabled + * tuner is not behind ZL10353 I2C-gate (no care if gate disabled or not) + * + * E30 C Plus VID=04b4 PID=861f HW=10 FW=1.0 "anysee-DC(LP)" + * PCB: 507DC (rev0.2) + * parts: TDA10023, DTOS403IH102B TM, CST56I01 + * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe + * IOA=4f IOB=ff IOC=00 IOD=26 IOE=01 + * IOD[0] TDA10023 1=enabled + * + * E30 S2 Plus VID=04b4 PID=861f HW=11 FW=0.1 "anysee-S2(LP)" + * PCB: 507SI (rev2.1) + * parts: BS2N10WCC01(CX24116, CX24118), ISL6423, TDA8024 + * OEA=80 OEB=00 OEC=ff OED=ff OEE=fe + * IOA=4d IOB=ff IOC=00 IOD=26 IOE=01 + * IOD[0] CX24116 1=enabled + * + * E30 C Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)" + * PCB: 507FA (rev0.4) + * parts: TDA10023, DTOS403IH102B TM, TDA8024 + * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff + * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0 + * IOD[5] TDA10023 1=enabled + * IOE[0] tuner 1=enabled + * + * E30 Combo Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)" + * PCB: 507FA (rev1.1) + * parts: ZL10353, TDA10023, DTOS403IH102B TM, TDA8024 + * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff + * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0 + * DVB-C: + * IOD[5] TDA10023 1=enabled + * IOE[0] tuner 1=enabled + * DVB-T: + * IOD[0] ZL10353 1=enabled + * IOE[0] tuner 0=enabled + * tuner is behind ZL10353 I2C-gate + * + * E7 TC VID=1c73 PID=861f HW=18 FW=0.7 AMTCI=0.5 "anysee-E7TC(LP)" + * PCB: 508TC (rev0.6) + * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212) + * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff + * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4 + * IOA[7] TS 1=enabled + * IOE[4] TDA18212 1=enabled + * DVB-C: + * IOD[6] ZL10353 0=disabled + * IOD[5] TDA10023 1=enabled + * IOE[0] IF 1=enabled + * DVB-T: + * IOD[5] TDA10023 0=disabled + * IOD[6] ZL10353 1=enabled + * IOE[0] IF 0=enabled + * + * E7 S2 VID=1c73 PID=861f HW=19 FW=0.4 AMTCI=0.5 "anysee-E7S2(LP)" + * PCB: 508S2 (rev0.7) + * parts: DNBU10512IST(STV0903, STV6110), ISL6423 + * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff + * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4 + * IOA[7] TS 1=enabled + * IOE[5] STV0903 1=enabled + * + * E7 T2C VID=1c73 PID=861f HW=20 FW=0.1 AMTCI=0.5 "anysee-E7T2C(LP)" + * PCB: 508T2C (rev0.3) + * parts: DNOQ44QCH106A(CXD2820R, TDA18212), TDA8024 + * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff + * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4 + * IOA[7] TS 1=enabled + * IOE[5] CXD2820R 1=enabled + * + * E7 PTC VID=1c73 PID=861f HW=21 FW=0.1 AMTCI=?? "anysee-E7PTC(LP)" + * PCB: 508PTC (rev0.5) + * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212) + * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff + * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4 + * IOA[7] TS 1=enabled + * IOE[4] TDA18212 1=enabled + * DVB-C: + * IOD[6] ZL10353 0=disabled + * IOD[5] TDA10023 1=enabled + * IOE[0] IF 1=enabled + * DVB-T: + * IOD[5] TDA10023 0=disabled + * IOD[6] ZL10353 1=enabled + * IOE[0] IF 0=enabled + * + * E7 PS2 VID=1c73 PID=861f HW=22 FW=0.1 AMTCI=?? "anysee-E7PS2(LP)" + * PCB: 508PS2 (rev0.4) + * parts: DNBU10512IST(STV0903, STV6110), ISL6423 + * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff + * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4 + * IOA[7] TS 1=enabled + * IOE[5] STV0903 1=enabled + */ + +static int anysee_read_config(struct dvb_usb_device *d) +{ + struct anysee_state *state = d_to_priv(d); + int ret; + u8 hw_info[3]; + + /* + * Check which hardware we have. + * We must do this call two times to get reliable values (hw/fw bug). + */ + ret = anysee_get_hw_info(d, hw_info); + if (ret) + goto error; + + ret = anysee_get_hw_info(d, hw_info); + if (ret) + goto error; + + /* Meaning of these info bytes are guessed. */ + info("firmware version:%d.%d hardware id:%d", + hw_info[1], hw_info[2], hw_info[0]); + + state->hw = hw_info[0]; +error: + return ret; +} + +/* external I2C gate used for DNOD44CDH086A(TDA18212) tuner module */ +static int anysee_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + /* enable / disable tuner access on IOE[4] */ + return anysee_wr_reg_mask(fe_to_d(fe), REG_IOE, (enable << 4), 0x10); +} + +static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct anysee_state *state = fe_to_priv(fe); + struct dvb_usb_device *d = fe_to_d(fe); + int ret; + + deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); + + /* no frontend sleep control */ + if (onoff == 0) + return 0; + + switch (state->hw) { + case ANYSEE_HW_507FA: /* 15 */ + /* E30 Combo Plus */ + /* E30 C Plus */ + + if (fe->id == 0) { + /* disable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 0), 0x01); + if (ret) + goto error; + + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20); + if (ret) + goto error; + + /* enable DVB-C tuner on IOE[0] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 0), 0x01); + if (ret) + goto error; + } else { + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20); + if (ret) + goto error; + + /* enable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01); + if (ret) + goto error; + + /* enable DVB-T tuner on IOE[0] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (0 << 0), 0x01); + if (ret) + goto error; + } + + break; + case ANYSEE_HW_508TC: /* 18 */ + case ANYSEE_HW_508PTC: /* 21 */ + /* E7 TC */ + /* E7 PTC */ + + if (fe->id == 0) { + /* disable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 6), 0x40); + if (ret) + goto error; + + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20); + if (ret) + goto error; + + /* enable IF route on IOE[0] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 0), 0x01); + if (ret) + goto error; + } else { + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20); + if (ret) + goto error; + + /* enable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 6), 0x40); + if (ret) + goto error; + + /* enable IF route on IOE[0] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (0 << 0), 0x01); + if (ret) + goto error; + } + + break; + default: + ret = 0; + } + +error: + return ret; +} + +static int anysee_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct anysee_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + int ret; + u8 tmp; + struct i2c_msg msg[2] = { + { + .addr = anysee_tda18212_config.i2c_address, + .flags = 0, + .len = 1, + .buf = "\x00", + }, { + .addr = anysee_tda18212_config.i2c_address, + .flags = I2C_M_RD, + .len = 1, + .buf = &tmp, + } + }; + + switch (state->hw) { + case ANYSEE_HW_507T: /* 2 */ + /* E30 */ + + /* attach demod */ + adap->fe[0] = dvb_attach(mt352_attach, &anysee_mt352_config, + &d->i2c_adap); + if (adap->fe[0]) + break; + + /* attach demod */ + adap->fe[0] = dvb_attach(zl10353_attach, &anysee_zl10353_config, + &d->i2c_adap); + + break; + case ANYSEE_HW_507CD: /* 6 */ + /* E30 Plus */ + + /* enable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01); + if (ret) + goto error; + + /* enable transport stream on IOA[7] */ + ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80); + if (ret) + goto error; + + /* attach demod */ + adap->fe[0] = dvb_attach(zl10353_attach, &anysee_zl10353_config, + &d->i2c_adap); + + break; + case ANYSEE_HW_507DC: /* 10 */ + /* E30 C Plus */ + + /* enable DVB-C demod on IOD[0] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01); + if (ret) + goto error; + + /* attach demod */ + adap->fe[0] = dvb_attach(tda10023_attach, + &anysee_tda10023_config, &d->i2c_adap, 0x48); + + break; + case ANYSEE_HW_507SI: /* 11 */ + /* E30 S2 Plus */ + + /* enable DVB-S/S2 demod on IOD[0] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01); + if (ret) + goto error; + + /* attach demod */ + adap->fe[0] = dvb_attach(cx24116_attach, &anysee_cx24116_config, + &d->i2c_adap); + + break; + case ANYSEE_HW_507FA: /* 15 */ + /* E30 Combo Plus */ + /* E30 C Plus */ + + /* enable tuner on IOE[4] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 4), 0x10); + if (ret) + goto error; + + /* probe TDA18212 */ + tmp = 0; + ret = i2c_transfer(&d->i2c_adap, msg, 2); + if (ret == 2 && tmp == 0xc7) + deb_info("%s: TDA18212 found\n", __func__); + else + tmp = 0; + + /* disable tuner on IOE[4] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (0 << 4), 0x10); + if (ret) + goto error; + + /* disable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 0), 0x01); + if (ret) + goto error; + + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20); + if (ret) + goto error; + + /* attach demod */ + if (tmp == 0xc7) { + /* TDA18212 config */ + adap->fe[0] = dvb_attach(tda10023_attach, + &anysee_tda10023_tda18212_config, + &d->i2c_adap, 0x48); + + /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */ + if (adap->fe[0]) + adap->fe[0]->ops.i2c_gate_ctrl = + anysee_i2c_gate_ctrl; + } else { + /* PLL config */ + adap->fe[0] = dvb_attach(tda10023_attach, + &anysee_tda10023_config, + &d->i2c_adap, 0x48); + } + + /* break out if first frontend attaching fails */ + if (!adap->fe[0]) + break; + + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20); + if (ret) + goto error; + + /* enable DVB-T demod on IOD[0] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01); + if (ret) + goto error; + + /* attach demod */ + if (tmp == 0xc7) { + /* TDA18212 config */ + adap->fe[1] = dvb_attach(zl10353_attach, + &anysee_zl10353_tda18212_config2, + &d->i2c_adap); + + /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */ + if (adap->fe[1]) + adap->fe[1]->ops.i2c_gate_ctrl = + anysee_i2c_gate_ctrl; + } else { + /* PLL config */ + adap->fe[1] = dvb_attach(zl10353_attach, + &anysee_zl10353_config, + &d->i2c_adap); + } + + break; + case ANYSEE_HW_508TC: /* 18 */ + case ANYSEE_HW_508PTC: /* 21 */ + /* E7 TC */ + /* E7 PTC */ + + /* disable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 6), 0x40); + if (ret) + goto error; + + /* enable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20); + if (ret) + goto error; + + /* attach demod */ + adap->fe[0] = dvb_attach(tda10023_attach, + &anysee_tda10023_tda18212_config, + &d->i2c_adap, 0x48); + + /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */ + if (adap->fe[0]) + adap->fe[0]->ops.i2c_gate_ctrl = anysee_i2c_gate_ctrl; + + /* break out if first frontend attaching fails */ + if (!adap->fe[0]) + break; + + /* disable DVB-C demod on IOD[5] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20); + if (ret) + goto error; + + /* enable DVB-T demod on IOD[6] */ + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 6), 0x40); + if (ret) + goto error; + + /* attach demod */ + adap->fe[1] = dvb_attach(zl10353_attach, + &anysee_zl10353_tda18212_config, + &d->i2c_adap); + + /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */ + if (adap->fe[1]) + adap->fe[1]->ops.i2c_gate_ctrl = anysee_i2c_gate_ctrl; + + state->has_ci = true; + + break; + case ANYSEE_HW_508S2: /* 19 */ + case ANYSEE_HW_508PS2: /* 22 */ + /* E7 S2 */ + /* E7 PS2 */ + + /* enable DVB-S/S2 demod on IOE[5] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 5), 0x20); + if (ret) + goto error; + + /* attach demod */ + adap->fe[0] = dvb_attach(stv0900_attach, + &anysee_stv0900_config, &d->i2c_adap, 0); + + state->has_ci = true; + + break; + case ANYSEE_HW_508T2C: /* 20 */ + /* E7 T2C */ + + /* enable DVB-T/T2/C demod on IOE[5] */ + ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 5), 0x20); + if (ret) + goto error; + + /* attach demod */ + adap->fe[0] = dvb_attach(cxd2820r_attach, + &anysee_cxd2820r_config, &d->i2c_adap); + + state->has_ci = true; + + break; + } + + if (!adap->fe[0]) { + /* we have no frontend :-( */ + ret = -ENODEV; + err("Unsupported Anysee version. " \ + "Please report the ."); + } +error: + return ret; +} + +static int anysee_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct anysee_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + struct dvb_frontend *fe; + int ret; + deb_info("%s: adap=%d\n", __func__, adap->id); + + switch (state->hw) { + case ANYSEE_HW_507T: /* 2 */ + /* E30 */ + + /* attach tuner */ + fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc2 >> 1), NULL, + DVB_PLL_THOMSON_DTT7579); + + break; + case ANYSEE_HW_507CD: /* 6 */ + /* E30 Plus */ + + /* attach tuner */ + fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc2 >> 1), + &d->i2c_adap, DVB_PLL_THOMSON_DTT7579); + + break; + case ANYSEE_HW_507DC: /* 10 */ + /* E30 C Plus */ + + /* attach tuner */ + fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1), + &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); + + break; + case ANYSEE_HW_507SI: /* 11 */ + /* E30 S2 Plus */ + + /* attach LNB controller */ + fe = dvb_attach(isl6423_attach, adap->fe[0], &d->i2c_adap, + &anysee_isl6423_config); + + break; + case ANYSEE_HW_507FA: /* 15 */ + /* E30 Combo Plus */ + /* E30 C Plus */ + + /* Try first attach TDA18212 silicon tuner on IOE[4], if that + * fails attach old simple PLL. */ + + /* attach tuner */ + fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap, + &anysee_tda18212_config); + + if (fe && adap->fe[1]) { + /* attach tuner for 2nd FE */ + fe = dvb_attach(tda18212_attach, adap->fe[1], + &d->i2c_adap, &anysee_tda18212_config); + break; + } else if (fe) { + break; + } + + /* attach tuner */ + fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1), + &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); + + if (fe && adap->fe[1]) { + /* attach tuner for 2nd FE */ + fe = dvb_attach(dvb_pll_attach, adap->fe[0], + (0xc0 >> 1), &d->i2c_adap, + DVB_PLL_SAMSUNG_DTOS403IH102A); + } + + break; + case ANYSEE_HW_508TC: /* 18 */ + case ANYSEE_HW_508PTC: /* 21 */ + /* E7 TC */ + /* E7 PTC */ + + /* attach tuner */ + fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap, + &anysee_tda18212_config); + + if (fe) { + /* attach tuner for 2nd FE */ + fe = dvb_attach(tda18212_attach, adap->fe[1], + &d->i2c_adap, &anysee_tda18212_config); + } + + break; + case ANYSEE_HW_508S2: /* 19 */ + case ANYSEE_HW_508PS2: /* 22 */ + /* E7 S2 */ + /* E7 PS2 */ + + /* attach tuner */ + fe = dvb_attach(stv6110_attach, adap->fe[0], + &anysee_stv6110_config, &d->i2c_adap); + + if (fe) { + /* attach LNB controller */ + fe = dvb_attach(isl6423_attach, adap->fe[0], + &d->i2c_adap, &anysee_isl6423_config); + } + + break; + + case ANYSEE_HW_508T2C: /* 20 */ + /* E7 T2C */ + + /* attach tuner */ + fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap, + &anysee_tda18212_config2); + + break; + default: + fe = NULL; + } + + if (fe) + ret = 0; + else + ret = -ENODEV; + + return ret; +} + +static int anysee_rc_query(struct dvb_usb_device *d) +{ + u8 buf[] = {CMD_GET_IR_CODE}; + u8 ircode[2]; + int ret; + + /* Remote controller is basic NEC using address byte 0x08. + Anysee device RC query returns only two bytes, status and code, + address byte is dropped. Also it does not return any value for + NEC RCs having address byte other than 0x08. Due to that, we + cannot use that device as standard NEC receiver. + It could be possible make hack which reads whole code directly + from device memory... */ + + ret = anysee_ctrl_msg(d, buf, sizeof(buf), ircode, sizeof(ircode)); + if (ret) + return ret; + + if (ircode[0]) { + deb_rc("%s: key pressed %02x\n", __func__, ircode[1]); + rc_keydown(d->rc_dev, 0x08 << 8 | ircode[1], 0); + } + + return 0; +} + +static int anysee_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + rc->allowed_protos = RC_TYPE_NEC; + rc->query = anysee_rc_query; + rc->interval = 250; /* windows driver uses 500ms */ + + return 0; +} + +static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot, + int addr) +{ + struct dvb_usb_device *d = ci->data; + int ret; + u8 buf[] = {CMD_CI, 0x02, 0x40 | addr >> 8, addr & 0xff, 0x00, 1}; + u8 val; + + ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1); + if (ret) + return ret; + + return val; +} + +static int anysee_ci_write_attribute_mem(struct dvb_ca_en50221 *ci, int slot, + int addr, u8 val) +{ + struct dvb_usb_device *d = ci->data; + int ret; + u8 buf[] = {CMD_CI, 0x03, 0x40 | addr >> 8, addr & 0xff, 0x00, 1, val}; + + ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); + if (ret) + return ret; + + return 0; +} + +static int anysee_ci_read_cam_control(struct dvb_ca_en50221 *ci, int slot, + u8 addr) +{ + struct dvb_usb_device *d = ci->data; + int ret; + u8 buf[] = {CMD_CI, 0x04, 0x40, addr, 0x00, 1}; + u8 val; + + ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1); + if (ret) + return ret; + + return val; +} + +static int anysee_ci_write_cam_control(struct dvb_ca_en50221 *ci, int slot, + u8 addr, u8 val) +{ + struct dvb_usb_device *d = ci->data; + int ret; + u8 buf[] = {CMD_CI, 0x05, 0x40, addr, 0x00, 1, val}; + + ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); + if (ret) + return ret; + + return 0; +} + +static int anysee_ci_slot_reset(struct dvb_ca_en50221 *ci, int slot) +{ + struct dvb_usb_device *d = ci->data; + int ret; + struct anysee_state *state = d_to_priv(d); + + state->ci_cam_ready = jiffies + msecs_to_jiffies(1000); + + ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80); + if (ret) + return ret; + + msleep(300); + + ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80); + if (ret) + return ret; + + return 0; +} + +static int anysee_ci_slot_shutdown(struct dvb_ca_en50221 *ci, int slot) +{ + struct dvb_usb_device *d = ci->data; + int ret; + + ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80); + if (ret) + return ret; + + msleep(30); + + ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80); + if (ret) + return ret; + + return 0; +} + +static int anysee_ci_slot_ts_enable(struct dvb_ca_en50221 *ci, int slot) +{ + struct dvb_usb_device *d = ci->data; + int ret; + + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 1), 0x02); + if (ret) + return ret; + + return 0; +} + +static int anysee_ci_poll_slot_status(struct dvb_ca_en50221 *ci, int slot, + int open) +{ + struct dvb_usb_device *d = ci->data; + struct anysee_state *state = d_to_priv(d); + int ret; + u8 tmp; + + ret = anysee_rd_reg_mask(d, REG_IOC, &tmp, 0x40); + if (ret) + return ret; + + if (tmp == 0) { + ret = DVB_CA_EN50221_POLL_CAM_PRESENT; + if (time_after(jiffies, state->ci_cam_ready)) + ret |= DVB_CA_EN50221_POLL_CAM_READY; + } + + return ret; +} + +static int anysee_ci_init(struct dvb_usb_device *d) +{ + struct anysee_state *state = d_to_priv(d); + int ret; + + state->ci.owner = THIS_MODULE; + state->ci.read_attribute_mem = anysee_ci_read_attribute_mem; + state->ci.write_attribute_mem = anysee_ci_write_attribute_mem; + state->ci.read_cam_control = anysee_ci_read_cam_control; + state->ci.write_cam_control = anysee_ci_write_cam_control; + state->ci.slot_reset = anysee_ci_slot_reset; + state->ci.slot_shutdown = anysee_ci_slot_shutdown; + state->ci.slot_ts_enable = anysee_ci_slot_ts_enable; + state->ci.poll_slot_status = anysee_ci_poll_slot_status; + state->ci.data = d; + + ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80); + if (ret) + return ret; + + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 2)|(0 << 1)|(0 << 0), 0x07); + if (ret) + return ret; + + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 2)|(1 << 1)|(1 << 0), 0x07); + if (ret) + return ret; + + ret = dvb_ca_en50221_init(&d->adapter[0].dvb_adap, &state->ci, 0, 1); + if (ret) + return ret; + + return 0; +} + +static void anysee_ci_release(struct dvb_usb_device *d) +{ + struct anysee_state *state = d_to_priv(d); + + /* detach CI */ + if (state->has_ci) + dvb_ca_en50221_release(&state->ci); + + return; +} + +static int anysee_init(struct dvb_usb_device *d) +{ + struct anysee_state *state = d_to_priv(d); + int ret; + + /* There is one interface with two alternate settings. + Alternate setting 0 is for bulk transfer. + Alternate setting 1 is for isochronous transfer. + We use bulk transfer (alternate setting 0). */ + ret = usb_set_interface(d->udev, 0, 0); + if (ret) + return ret; + + /* LED light */ + ret = anysee_led_ctrl(d, 0x01, 0x03); + if (ret) + return ret; + + /* enable IR */ + ret = anysee_ir_ctrl(d, 1); + if (ret) + return ret; + + /* attach CI */ + if (state->has_ci) { + ret = anysee_ci_init(d); + if (ret) { + state->has_ci = false; + return ret; + } + } + + return 0; +} + +static void anysee_exit(struct dvb_usb_device *d) +{ + return anysee_ci_release(d); +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties anysee_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct anysee_state), + + .generic_bulk_ctrl_endpoint = 0x01, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .i2c_algo = &anysee_i2c_algo, + .read_config = anysee_read_config, + .frontend_attach = anysee_frontend_attach, + .tuner_attach = anysee_tuner_attach, + .init = anysee_init, + .get_rc_config = anysee_get_rc_config, + .frontend_ctrl = anysee_frontend_ctrl, + .streaming_ctrl = anysee_streaming_ctrl, + .exit = anysee_exit, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x82, 8, 16 * 512), + } + } +}; + +static const struct usb_device_id anysee_id_table[] = { + { DVB_USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE, + &anysee_props, "Anysee", RC_MAP_ANYSEE) }, + { DVB_USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE, + &anysee_props, "Anysee", RC_MAP_ANYSEE) }, + { } +}; +MODULE_DEVICE_TABLE(usb, anysee_id_table); + +static struct usb_driver anysee_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = anysee_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(anysee_usb_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h new file mode 100644 index 000000000000..dc40dcf7c328 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/anysee.h @@ -0,0 +1,356 @@ +/* + * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver + * + * Copyright (C) 2007 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. + * + * TODO: + * - add smart card reader support for Conditional Access (CA) + * + * Card reader in Anysee is nothing more than ISO 7816 card reader. + * There is no hardware CAM in any Anysee device sold. + * In my understanding it should be implemented by making own module + * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This + * module registers serial interface that can be used to communicate + * with any ISO 7816 smart card. + * + * Any help according to implement serial smart card reader support + * is highly welcome! + */ + +#ifndef _DVB_USB_ANYSEE_H_ +#define _DVB_USB_ANYSEE_H_ + +#define DVB_USB_LOG_PREFIX "anysee" +#include "dvb_usb.h" +#include "dvb_ca_en50221.h" + +#ifdef CONFIG_DVB_USB_DEBUG +#define dprintk(var, level, args...) \ + do { if ((var & level)) printk(args); } while (0) +#define DVB_USB_DEBUG_STATUS +#else +#define dprintk(args...) +#define debug_dump(b, l, func) +#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" +#endif + +#define debug_dump(b, l, func) {\ + int loop_; \ + for (loop_ = 0; loop_ < l; loop_++) \ + func("%02x ", b[loop_]); \ + func("\n");\ +} + +#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_anysee_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_anysee_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_anysee_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_anysee_debug, 0x20, args) + +#undef err +#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef info +#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) + +enum cmd { + CMD_I2C_READ = 0x33, + CMD_I2C_WRITE = 0x31, + CMD_REG_READ = 0xb0, + CMD_REG_WRITE = 0xb1, + CMD_STREAMING_CTRL = 0x12, + CMD_LED_AND_IR_CTRL = 0x16, + CMD_GET_IR_CODE = 0x41, + CMD_GET_HW_INFO = 0x19, + CMD_SMARTCARD = 0x34, + CMD_CI = 0x37, +}; + +struct anysee_state { + u8 hw; /* PCB ID */ + u8 seq; + u8 fe_id:1; /* frondend ID */ + u8 has_ci:1; + struct dvb_ca_en50221 ci; + unsigned long ci_cam_ready; /* jiffies */ +}; + +#define ANYSEE_HW_507T 2 /* E30 */ +#define ANYSEE_HW_507CD 6 /* E30 Plus */ +#define ANYSEE_HW_507DC 10 /* E30 C Plus */ +#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */ +#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */ +#define ANYSEE_HW_508TC 18 /* E7 TC */ +#define ANYSEE_HW_508S2 19 /* E7 S2 */ +#define ANYSEE_HW_508T2C 20 /* E7 T2C */ +#define ANYSEE_HW_508PTC 21 /* E7 PTC Plus */ +#define ANYSEE_HW_508PS2 22 /* E7 PS2 Plus */ + +#define REG_IOA 0x80 /* Port A (bit addressable) */ +#define REG_IOB 0x90 /* Port B (bit addressable) */ +#define REG_IOC 0xa0 /* Port C (bit addressable) */ +#define REG_IOD 0xb0 /* Port D (bit addressable) */ +#define REG_IOE 0xb1 /* Port E (NOT bit addressable) */ +#define REG_OEA 0xb2 /* Port A Output Enable */ +#define REG_OEB 0xb3 /* Port B Output Enable */ +#define REG_OEC 0xb4 /* Port C Output Enable */ +#define REG_OED 0xb5 /* Port D Output Enable */ +#define REG_OEE 0xb6 /* Port E Output Enable */ + +#endif + +/*************************************************************************** + * USB API description (reverse engineered) + *************************************************************************** + +Transaction flow: +================= +BULK[00001] >>> REQUEST PACKET 64 bytes +BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY) +BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY) + +General reply packet(s) are always used if not own reply defined. + +============================================================================ +| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY) +============================================================================ +| 00 | reply data (if any) from previous transaction +| | Just same reply packet as returned during previous transaction. +| | Needed only if reply is missed in previous transaction. +| | Just skip normally. +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY) +============================================================================ +| 00 | reply data (if any) +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | I2C WRITE REQUEST PACKET +============================================================================ +| 00 | 0x31 I2C write command +---------------------------------------------------------------------------- +| 01 | i2c address +---------------------------------------------------------------------------- +| 02 | data length +| | 0x02 (for typical I2C reg / val pair) +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04- | data +---------------------------------------------------------------------------- +| -59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | I2C READ REQUEST PACKET +============================================================================ +| 00 | 0x33 I2C read command +---------------------------------------------------------------------------- +| 01 | i2c address + 1 +---------------------------------------------------------------------------- +| 02 | register +---------------------------------------------------------------------------- +| 03 | 0x00 +---------------------------------------------------------------------------- +| 04 | 0x00 +---------------------------------------------------------------------------- +| 05 | data length +---------------------------------------------------------------------------- +| 06-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET +============================================================================ +| 00 | 0xb1 register write command +---------------------------------------------------------------------------- +| 01-02 | register +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04 | value +---------------------------------------------------------------------------- +| 05-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET +============================================================================ +| 00 | 0xb0 register read command +---------------------------------------------------------------------------- +| 01-02 | register +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | LED CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x16 LED and IR control command +---------------------------------------------------------------------------- +| 01 | 0x01 (LED) +---------------------------------------------------------------------------- +| 03 | 0x00 blink +| | 0x01 lights continuously +---------------------------------------------------------------------------- +| 04 | blink interval +| | 0x00 fastest (looks like LED lights continuously) +| | 0xff slowest +---------------------------------------------------------------------------- +| 05-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | IR CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x16 LED and IR control command +---------------------------------------------------------------------------- +| 01 | 0x02 (IR) +---------------------------------------------------------------------------- +| 03 | 0x00 IR disabled +| | 0x01 IR enabled +---------------------------------------------------------------------------- +| 04-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | STREAMING CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x12 streaming control command +---------------------------------------------------------------------------- +| 01 | 0x00 streaming disabled +| | 0x01 streaming enabled +---------------------------------------------------------------------------- +| 02 | 0x00 +---------------------------------------------------------------------------- +| 03-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | REMOTE CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x41 remote control command +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | REMOTE CONTROL REPLY PACKET +============================================================================ +| 00 | 0x00 code not received +| | 0x01 code received +---------------------------------------------------------------------------- +| 01 | remote control code +---------------------------------------------------------------------------- +| 02-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GET HARDWARE INFO REQUEST PACKET +============================================================================ +| 00 | 0x19 get hardware info command +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GET HARDWARE INFO REPLY PACKET +============================================================================ +| 00 | hardware id +---------------------------------------------------------------------------- +| 01-02 | firmware version +---------------------------------------------------------------------------- +| 03-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | SMART CARD READER PACKET +============================================================================ +| 00 | 0x34 smart card reader command +---------------------------------------------------------------------------- +| xx | +---------------------------------------------------------------------------- +| xx-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +*/ diff --git a/drivers/media/usb/dvb-usb-v2/au6610.c b/drivers/media/usb/dvb-usb-v2/au6610.c new file mode 100644 index 000000000000..05f2a8628142 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/au6610.c @@ -0,0 +1,206 @@ +/* + * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0. + * + * Copyright (C) 2006 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 "au6610.h" +#include "zl10353.h" +#include "qt1010.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + int ret; + u16 index; + u8 *usb_buf; + + /* + * allocate enough for all known requests, + * read returns 5 and write 6 bytes + */ + usb_buf = kmalloc(6, GFP_KERNEL); + if (!usb_buf) + return -ENOMEM; + + switch (wlen) { + case 1: + index = wbuf[0] << 8; + break; + case 2: + index = wbuf[0] << 8; + index += wbuf[1]; + break; + default: + pr_err("%s: wlen = %d, aborting\n", KBUILD_MODNAME, wlen); + ret = -EINVAL; + goto error; + } + + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, + USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, + usb_buf, 6, AU6610_USB_TIMEOUT); + if (ret < 0) + goto error; + + switch (operation) { + case AU6610_REQ_I2C_READ: + case AU6610_REQ_USB_READ: + /* requested value is always 5th byte in buffer */ + rbuf[0] = usb_buf[4]; + } +error: + kfree(usb_buf); + return ret; +} + +static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 request; + u8 wo = (rbuf == NULL || rlen == 0); /* write-only */ + + if (wo) { + request = AU6610_REQ_I2C_WRITE; + } else { /* rw */ + request = AU6610_REQ_I2C_READ; + } + + return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen); +} + + +/* I2C */ +static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, msg[i+1].buf, + msg[i+1].len) < 0) + break; + i++; + } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + + +static u32 au6610_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm au6610_i2c_algo = { + .master_xfer = au6610_i2c_xfer, + .functionality = au6610_i2c_func, +}; + +/* Callbacks for DVB USB */ +static struct zl10353_config au6610_zl10353_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .parallel_ts = 1, +}; + +static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap) +{ + adap->fe[0] = dvb_attach(zl10353_attach, &au6610_zl10353_config, + &adap_to_d(adap)->i2c_adap); + if (adap->fe[0] == NULL) + return -ENODEV; + + return 0; +} + +static struct qt1010_config au6610_qt1010_config = { + .i2c_address = 0x62 +}; + +static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap) +{ + return dvb_attach(qt1010_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &au6610_qt1010_config) == NULL ? -ENODEV : 0; +} + +static int au6610_init(struct dvb_usb_device *d) +{ + /* TODO: this functionality belongs likely to the streaming control */ + /* bInterfaceNumber 0, bAlternateSetting 5 */ + return usb_set_interface(d->udev, 0, 5); +} + +static struct dvb_usb_device_properties au6610_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + + .i2c_algo = &au6610_i2c_algo, + .frontend_attach = au6610_zl10353_frontend_attach, + .tuner_attach = au6610_qt1010_tuner_attach, + .init = au6610_init, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_ISOC(0x82, 5, 40, 942, 1), + }, + }, +}; + +static const struct usb_device_id au6610_id_table[] = { + { DVB_USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110, + &au6610_props, "Sigmatek DVB-110", NULL) }, + { } +}; +MODULE_DEVICE_TABLE(usb, au6610_id_table); + +static struct usb_driver au6610_driver = { + .name = KBUILD_MODNAME, + .id_table = au6610_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(au6610_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/au6610.h b/drivers/media/usb/dvb-usb-v2/au6610.h new file mode 100644 index 000000000000..ea337bfc00b1 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/au6610.h @@ -0,0 +1,32 @@ +/* + * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0. + * + * Copyright (C) 2006 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 AU6610_H +#define AU6610_H +#include "dvb_usb.h" + +#define AU6610_REQ_I2C_WRITE 0x14 +#define AU6610_REQ_I2C_READ 0x13 +#define AU6610_REQ_USB_WRITE 0x16 +#define AU6610_REQ_USB_READ 0x15 + +#define AU6610_USB_TIMEOUT 1000 + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c new file mode 100644 index 000000000000..54f1221d930d --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -0,0 +1,919 @@ +/* + * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones + * + * Copyright (c) Henry Wang + * + * This driver was 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() + * + * Copyright (c) 2010-2012 Mauro Carvalho Chehab + * Driver modified by in order to work with upstream drxk driver, and + * tons of bugs got fixed, and converted to use dvb-usb-v2. + * + * This program is free software; 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 "drxk.h" +#include "mt2063.h" +#include "dvb_ca_en50221.h" +#include "dvb_usb.h" +#include "cypress_firmware.h" + +#define AZ6007_FIRMWARE "dvb-usb-terratec-h7-az6007.fw" + +static int az6007_xfer_debug; +module_param_named(xfer_debug, az6007_xfer_debug, int, 0644); +MODULE_PARM_DESC(xfer_debug, "Enable xfer debug"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/ + +#define FX2_OED 0xb5 +#define AZ6007_READ_DATA 0xb7 +#define AZ6007_I2C_RD 0xb9 +#define AZ6007_POWER 0xbc +#define AZ6007_I2C_WR 0xbd +#define FX2_SCON1 0xc0 +#define AZ6007_TS_THROUGH 0xc7 +#define AZ6007_READ_IR 0xb4 + +struct az6007_device_state { + struct mutex mutex; + struct mutex ca_mutex; + struct dvb_ca_en50221 ca; + unsigned warm:1; + int (*gate_ctrl) (struct dvb_frontend *, int); + unsigned char data[4096]; +}; + +static struct drxk_config terratec_h7_drxk = { + .adr = 0x29, + .parallel_ts = true, + .dynamic_clk = true, + .single_master = true, + .enable_merr_cfg = true, + .no_i2c_bridge = false, + .chunk_size = 64, + .mpeg_out_clk_strength = 0x02, + .qam_demod_parameter_count = 2, + .microcode_name = "dvb-usb-terratec-h7-drxk.fw", +}; + +static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct az6007_device_state *st = fe_to_priv(fe); + struct dvb_usb_adapter *adap = fe->sec_priv; + int status = 0; + + pr_debug("%s: %s\n", __func__, enable ? "enable" : "disable"); + + if (!adap || !st) + return -EINVAL; + + if (enable) + status = st->gate_ctrl(fe, 1); + else + status = st->gate_ctrl(fe, 0); + + return status; +} + +static struct mt2063_config az6007_mt2063_config = { + .tuner_address = 0x60, + .refclock = 36125000, +}; + +static int __az6007_read(struct usb_device *udev, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_IN, + value, index, b, blen, 5000); + if (ret < 0) { + pr_warn("usb read operation failed. (%d)\n", ret); + return -EIO; + } + + if (az6007_xfer_debug) { + printk(KERN_DEBUG "az6007: IN req: %02x, value: %04x, index: %04x\n", + req, value, index); + print_hex_dump_bytes("az6007: payload: ", + DUMP_PREFIX_NONE, b, blen); + } + + return ret; +} + +static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct az6007_device_state *st = d->priv; + int ret; + + if (mutex_lock_interruptible(&st->mutex) < 0) + return -EAGAIN; + + ret = __az6007_read(d->udev, req, value, index, b, blen); + + mutex_unlock(&st->mutex); + + return ret; +} + +static int __az6007_write(struct usb_device *udev, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + + if (az6007_xfer_debug) { + printk(KERN_DEBUG "az6007: OUT req: %02x, value: %04x, index: %04x\n", + req, value, index); + print_hex_dump_bytes("az6007: payload: ", + DUMP_PREFIX_NONE, b, blen); + } + + if (blen > 64) { + pr_err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n", + blen); + return -EOPNOTSUPP; + } + + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_OUT, + value, index, b, blen, 5000); + if (ret != blen) { + pr_err("usb write operation failed. (%d)\n", ret); + return -EIO; + } + + return 0; +} + +static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct az6007_device_state *st = d->priv; + int ret; + + if (mutex_lock_interruptible(&st->mutex) < 0) + return -EAGAIN; + + ret = __az6007_write(d->udev, req, value, index, b, blen); + + mutex_unlock(&st->mutex); + + return ret; +} + +static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_device *d = fe_to_d(fe); + + pr_debug("%s: %s\n", __func__, onoff ? "enable" : "disable"); + + return az6007_write(d, 0xbc, onoff, 0, NULL, 0); +} + +/* remote control stuff (does not work with my box) */ +static int az6007_rc_query(struct dvb_usb_device *d) +{ + struct az6007_device_state *st = d_to_priv(d); + unsigned code = 0; + + az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10); + + if (st->data[1] == 0x44) + return 0; + + if ((st->data[1] ^ st->data[2]) == 0xff) + code = st->data[1]; + else + code = st->data[1] << 8 | st->data[2]; + + if ((st->data[3] ^ st->data[4]) == 0xff) + code = code << 8 | st->data[3]; + else + code = code << 16 | st->data[3] << 8 | st->data[4]; + + rc_keydown(d->rc_dev, code, st->data[5]); + + return 0; +} + +static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = d_to_priv(d); + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC1; + value = address; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + pr_warn("usb in operation failed. (%d)\n", ret); + ret = -EINVAL; + } else { + ret = b[0]; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = d_to_priv(d); + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + pr_debug("%s(), slot %d\n", __func__, slot); + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC2; + value1 = address; + index = value; + blen = 0; + + ret = az6007_write(d, req, value1, index, NULL, blen); + if (ret != 0) + pr_warn("usb out operation failed. (%d)\n", ret); + + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = d_to_priv(d); + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC3; + value = address; + index = 0; + blen = 2; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + pr_warn("usb in operation failed. (%d)\n", ret); + ret = -EINVAL; + } else { + if (b[0] == 0) + pr_warn("Read CI IO error\n"); + + ret = b[1]; + pr_debug("read cam data = %x from 0x%x\n", b[1], value); + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = d_to_priv(d); + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC4; + value1 = address; + index = value; + blen = 0; + + ret = az6007_write(d, req, value1, index, NULL, blen); + if (ret != 0) { + pr_warn("usb out operation failed. (%d)\n", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + req = 0xC8; + value = 0; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + pr_warn("usb in operation failed. (%d)\n", ret); + ret = -EIO; + } else{ + ret = b[0]; + } + kfree(b); + return ret; +} + +static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = d_to_priv(d); + + int ret, i; + u8 req; + u16 value; + u16 index; + int blen; + + mutex_lock(&state->ca_mutex); + + req = 0xC6; + value = 1; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + pr_warn("usb out operation failed. (%d)\n", ret); + goto failed; + } + + msleep(500); + req = 0xC6; + value = 0; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + pr_warn("usb out operation failed. (%d)\n", ret); + goto failed; + } + + for (i = 0; i < 15; i++) { + msleep(100); + + if (CI_CamReady(ca, slot)) { + pr_debug("CAM Ready\n"); + break; + } + } + msleep(5000); + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return 0; +} + +static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = d_to_priv(d); + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + pr_debug("%s()\n", __func__); + mutex_lock(&state->ca_mutex); + req = 0xC7; + value = 1; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + pr_warn("usb out operation failed. (%d)\n", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = d_to_priv(d); + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + mutex_lock(&state->ca_mutex); + + req = 0xC5; + value = 0; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + pr_warn("usb in operation failed. (%d)\n", ret); + ret = -EIO; + } else + ret = 0; + + if (!ret && b[0] == 1) { + ret = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + + +static void az6007_ci_uninit(struct dvb_usb_device *d) +{ + struct az6007_device_state *state; + + pr_debug("%s()\n", __func__); + + if (NULL == d) + return; + + state = d_to_priv(d); + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + + +static int az6007_ci_init(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct az6007_device_state *state = adap_to_priv(adap); + int ret; + + pr_debug("%s()\n", __func__); + + mutex_init(&state->ca_mutex); + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = az6007_ci_read_attribute_mem; + state->ca.write_attribute_mem = az6007_ci_write_attribute_mem; + state->ca.read_cam_control = az6007_ci_read_cam_control; + state->ca.write_cam_control = az6007_ci_write_cam_control; + state->ca.slot_reset = az6007_ci_slot_reset; + state->ca.slot_shutdown = az6007_ci_slot_shutdown; + state->ca.slot_ts_enable = az6007_ci_slot_ts_enable; + state->ca.poll_slot_status = az6007_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&adap->dvb_adap, + &state->ca, + 0, /* flags */ + 1);/* n_slots */ + if (ret != 0) { + pr_err("Cannot initialize CI: Error %d.\n", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + pr_debug("CI initialized.\n"); + + return 0; +} + +static int az6007_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6]) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct az6007_device_state *st = adap_to_priv(adap); + int ret; + + ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6); + memcpy(mac, st->data, 6); + + if (ret > 0) + pr_debug("%s: mac is %pM\n", __func__, mac); + + return ret; +} + +static int az6007_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct az6007_device_state *st = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + + pr_debug("attaching demod drxk\n"); + + adap->fe[0] = dvb_attach(drxk_attach, &terratec_h7_drxk, + &d->i2c_adap); + if (!adap->fe[0]) + return -EINVAL; + + adap->fe[0]->sec_priv = adap; + st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl; + adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; + + az6007_ci_init(adap); + + return 0; +} + +static int az6007_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + + pr_debug("attaching tuner mt2063\n"); + + /* Attach mt2063 to DVB-C frontend */ + if (adap->fe[0]->ops.i2c_gate_ctrl) + adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 1); + if (!dvb_attach(mt2063_attach, adap->fe[0], + &az6007_mt2063_config, + &d->i2c_adap)) + return -EINVAL; + + if (adap->fe[0]->ops.i2c_gate_ctrl) + adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 0); + + return 0; +} + +static int az6007_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + struct az6007_device_state *state = d_to_priv(d); + int ret; + + pr_debug("%s()\n", __func__); + + if (!state->warm) { + mutex_init(&state->mutex); + + ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0); + if (ret < 0) + return ret; + msleep(60); + ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0); + if (ret < 0) + return ret; + msleep(100); + ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0); + if (ret < 0) + return ret; + msleep(20); + ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0); + if (ret < 0) + return ret; + + msleep(400); + ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0); + if (ret < 0) + return ret; + msleep(150); + ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0); + if (ret < 0) + return ret; + msleep(430); + ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0); + if (ret < 0) + return ret; + + state->warm = true; + + return 0; + } + + if (!onoff) + return 0; + + az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0); + az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0); + + return 0; +} + +/* I2C */ +static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct az6007_device_state *st = d_to_priv(d); + int i, j, len; + int ret = 0; + u16 index; + u16 value; + int length; + u8 req, addr; + + if (mutex_lock_interruptible(&st->mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + addr = msgs[i].addr << 1; + if (((i + 1) < num) + && (msgs[i].len == 1) + && ((msgs[i].flags & I2C_M_RD) != I2C_M_RD) + && (msgs[i + 1].flags & I2C_M_RD) + && (msgs[i].addr == msgs[i + 1].addr)) { + /* + * A write + read xfer for the same address, where + * the first xfer has just 1 byte length. + * Need to join both into one operation + */ + if (az6007_xfer_debug) + printk(KERN_DEBUG "az6007: I2C W/R addr=0x%x len=%d/%d\n", + addr, msgs[i].len, msgs[i + 1].len); + req = AZ6007_I2C_RD; + index = msgs[i].buf[0]; + value = addr | (1 << 8); + length = 6 + msgs[i + 1].len; + len = msgs[i + 1].len; + ret = __az6007_read(d->udev, req, value, index, + st->data, length); + if (ret >= len) { + for (j = 0; j < len; j++) + msgs[i + 1].buf[j] = st->data[j + 5]; + } else + ret = -EIO; + i++; + } else if (!(msgs[i].flags & I2C_M_RD)) { + /* write bytes */ + if (az6007_xfer_debug) + printk(KERN_DEBUG "az6007: I2C W addr=0x%x len=%d\n", + addr, msgs[i].len); + req = AZ6007_I2C_WR; + index = msgs[i].buf[0]; + value = addr | (1 << 8); + length = msgs[i].len - 1; + len = msgs[i].len - 1; + for (j = 0; j < len; j++) + st->data[j] = msgs[i].buf[j + 1]; + ret = __az6007_write(d->udev, req, value, index, + st->data, length); + } else { + /* read bytes */ + if (az6007_xfer_debug) + printk(KERN_DEBUG "az6007: I2C R addr=0x%x len=%d\n", + addr, msgs[i].len); + req = AZ6007_I2C_RD; + index = msgs[i].buf[0]; + value = addr; + length = msgs[i].len + 6; + len = msgs[i].len; + ret = __az6007_read(d->udev, req, value, index, + st->data, length); + for (j = 0; j < len; j++) + msgs[i].buf[j] = st->data[j + 5]; + } + if (ret < 0) + goto err; + } +err: + mutex_unlock(&st->mutex); + + if (ret < 0) { + pr_info("%s ERROR: %i\n", __func__, ret); + return ret; + } + return num; +} + +static u32 az6007_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm az6007_i2c_algo = { + .master_xfer = az6007_i2c_xfer, + .functionality = az6007_i2c_func, +}; + +static int az6007_identify_state(struct dvb_usb_device *d, const char **name) +{ + int ret; + u8 *mac; + + pr_debug("Identifying az6007 state\n"); + + mac = kmalloc(6, GFP_ATOMIC); + if (!mac) + return -ENOMEM; + + /* Try to read the mac address */ + ret = __az6007_read(d->udev, AZ6007_READ_DATA, 6, 0, mac, 6); + if (ret == 6) + ret = WARM; + else + ret = COLD; + + kfree(mac); + + if (ret == COLD) { + __az6007_write(d->udev, 0x09, 1, 0, NULL, 0); + __az6007_write(d->udev, 0x00, 0, 0, NULL, 0); + __az6007_write(d->udev, 0x00, 0, 0, NULL, 0); + } + + pr_debug("Device is on %s state\n", + ret == WARM ? "warm" : "cold"); + return ret; +} + +static void az6007_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + az6007_ci_uninit(d); + dvb_usbv2_disconnect(intf); +} + +static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + pr_debug("Getting az6007 Remote Control properties\n"); + + rc->allowed_protos = RC_TYPE_NEC; + rc->query = az6007_rc_query; + rc->interval = 400; + + return 0; +} + +static int az6007_download_firmware(struct dvb_usb_device *d, + const struct firmware *fw) +{ + pr_debug("Loading az6007 firmware\n"); + + return usbv2_cypress_load_firmware(d->udev, fw, CYPRESS_FX2); +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties az6007_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .firmware = AZ6007_FIRMWARE, + + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct az6007_device_state), + .i2c_algo = &az6007_i2c_algo, + .tuner_attach = az6007_tuner_attach, + .frontend_attach = az6007_frontend_attach, + .streaming_ctrl = az6007_streaming_ctrl, + .get_rc_config = az6007_get_rc_config, + .read_mac_address = az6007_read_mac_addr, + .download_firmware = az6007_download_firmware, + .identify_state = az6007_identify_state, + .power_ctrl = az6007_power_ctrl, + .num_adapters = 1, + .adapter = { + { .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), } + } +}; + +static struct usb_device_id az6007_usb_table[] = { + {DVB_USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007, + &az6007_props, "Azurewave 6007", RC_MAP_EMPTY)}, + {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7, + &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)}, + {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2, + &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)}, + {0}, +}; + +MODULE_DEVICE_TABLE(usb, az6007_usb_table); + +static int az6007_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + + az6007_ci_uninit(d); + return dvb_usbv2_suspend(intf, msg); +} + +static int az6007_resume(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + struct dvb_usb_adapter *adap = &d->adapter[0]; + + az6007_ci_init(adap); + return dvb_usbv2_resume(intf); +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver az6007_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = az6007_usb_table, + .probe = dvb_usbv2_probe, + .disconnect = az6007_usb_disconnect, + .no_dynamic_id = 1, + .soft_unbind = 1, + /* + * FIXME: need to implement reset_resume, likely with + * dvb-usb-v2 core support + */ + .suspend = az6007_suspend, + .resume = az6007_resume, +}; + +module_usb_driver(az6007_usb_driver); + +MODULE_AUTHOR("Henry Wang "); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(AZ6007_FIRMWARE); diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.c b/drivers/media/usb/dvb-usb-v2/ce6230.c new file mode 100644 index 000000000000..84ff4a96ca4e --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/ce6230.c @@ -0,0 +1,287 @@ +/* + * Intel CE6230 DVB USB 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ce6230.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) +{ + int ret; + unsigned int pipe; + u8 request; + u8 requesttype; + u16 value; + u16 index; + u8 *buf; + + request = req->cmd; + value = req->value; + index = req->index; + + switch (req->cmd) { + case I2C_READ: + case DEMOD_READ: + case REG_READ: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + break; + case I2C_WRITE: + case DEMOD_WRITE: + case REG_WRITE: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + break; + default: + pr_debug("%s: unknown command=%02x\n", __func__, req->cmd); + ret = -EINVAL; + goto error; + } + + buf = kmalloc(req->data_len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto error; + } + + if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) { + /* write */ + memcpy(buf, req->data, req->data_len); + pipe = usb_sndctrlpipe(d->udev, 0); + } else { + /* read */ + pipe = usb_rcvctrlpipe(d->udev, 0); + } + + msleep(1); /* avoid I2C errors */ + + ret = usb_control_msg(d->udev, pipe, request, requesttype, value, index, + buf, req->data_len, CE6230_USB_TIMEOUT); + + ce6230_debug_dump(request, requesttype, value, index, buf, + req->data_len); + + if (ret < 0) + pr_err("%s: usb_control_msg() failed=%d\n", KBUILD_MODNAME, + ret); + else + ret = 0; + + /* read request, copy returned data to return buf */ + if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, buf, req->data_len); + + kfree(buf); +error: + return ret; +} + +/* I2C */ +static struct zl10353_config ce6230_zl10353_config; + +static int ce6230_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0, i = 0; + struct usb_req req; + + if (num > 2) + return -EOPNOTSUPP; + + memset(&req, 0, sizeof(req)); + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].addr == + ce6230_zl10353_config.demod_address) { + req.cmd = DEMOD_READ; + req.value = msg[i].addr >> 1; + req.index = msg[i].buf[0]; + req.data_len = msg[i+1].len; + req.data = &msg[i+1].buf[0]; + ret = ce6230_ctrl_msg(d, &req); + } else { + pr_err("%s: I2C read not implemented\n", + KBUILD_MODNAME); + ret = -EOPNOTSUPP; + } + i += 2; + } else { + if (msg[i].addr == + ce6230_zl10353_config.demod_address) { + req.cmd = DEMOD_WRITE; + req.value = msg[i].addr >> 1; + req.index = msg[i].buf[0]; + req.data_len = msg[i].len-1; + req.data = &msg[i].buf[1]; + ret = ce6230_ctrl_msg(d, &req); + } else { + req.cmd = I2C_WRITE; + req.value = 0x2000 + (msg[i].addr >> 1); + req.index = 0x0000; + req.data_len = msg[i].len; + req.data = &msg[i].buf[0]; + ret = ce6230_ctrl_msg(d, &req); + } + i += 1; + } + if (ret) + break; + } + + mutex_unlock(&d->i2c_mutex); + return ret ? ret : i; +} + +static u32 ce6230_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm ce6230_i2c_algorithm = { + .master_xfer = ce6230_i2c_master_xfer, + .functionality = ce6230_i2c_functionality, +}; + +/* Callbacks for DVB USB */ +static struct zl10353_config ce6230_zl10353_config = { + .demod_address = 0x1e, + .adc_clock = 450000, + .if2 = 45700, + .no_tuner = 1, + .parallel_ts = 1, + .clock_ctl_1 = 0x34, + .pll_0 = 0x0e, +}; + +static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap) +{ + pr_debug("%s:\n", __func__); + + adap->fe[0] = dvb_attach(zl10353_attach, &ce6230_zl10353_config, + &adap_to_d(adap)->i2c_adap); + if (adap->fe[0] == NULL) + return -ENODEV; + + return 0; +} + +static struct mxl5005s_config ce6230_mxl5003s_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_DEFAULT, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +{ + int ret; + + pr_debug("%s:\n", __func__); + + ret = dvb_attach(mxl5005s_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0; + return ret; +} + +static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + + pr_debug("%s: onoff=%d\n", __func__, onoff); + + /* InterfaceNumber 1 / AlternateSetting 0 idle + InterfaceNumber 1 / AlternateSetting 1 streaming */ + ret = usb_set_interface(d->udev, 1, onoff); + if (ret) + pr_err("%s: usb_set_interface() failed=%d\n", KBUILD_MODNAME, + ret); + + return ret; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties ce6230_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .bInterfaceNumber = 1, + + .i2c_algo = &ce6230_i2c_algorithm, + .power_ctrl = ce6230_power_ctrl, + .frontend_attach = ce6230_zl10353_frontend_attach, + .tuner_attach = ce6230_mxl5003s_tuner_attach, + + .num_adapters = 1, + .adapter = { + { + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = (16 * 512), + } + } + }, + } + }, +}; + +static const struct usb_device_id ce6230_id_table[] = { + { DVB_USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500, + &ce6230_props, "Intel CE9500 reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310, + &ce6230_props, "AVerMedia A310 USB 2.0 DVB-T tuner", NULL) }, + { } +}; +MODULE_DEVICE_TABLE(usb, ce6230_id_table); + +static struct usb_driver ce6230_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = ce6230_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(ce6230_usb_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Intel CE6230 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.h b/drivers/media/usb/dvb-usb-v2/ce6230.h new file mode 100644 index 000000000000..42d754494a3a --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/ce6230.h @@ -0,0 +1,61 @@ +/* + * Intel CE6230 DVB USB 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef CE6230_H +#define CE6230_H + +#include "dvb_usb.h" +#include "zl10353.h" +#include "mxl5005s.h" + +#define ce6230_debug_dump(r, t, v, i, b, l) { \ + char *direction; \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + direction = ">>>"; \ + else \ + direction = "<<<"; \ + pr_debug("%s: %02x %02x %02x %02x %02x %02x %02x %02x %s [%d bytes]\n", \ + __func__, t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, \ + l & 0xff, l >> 8, direction, l); \ +} + +#define CE6230_USB_TIMEOUT 1000 + +struct usb_req { + u8 cmd; /* [1] */ + u16 value; /* [2|3] */ + u16 index; /* [4|5] */ + u16 data_len; /* [6|7] */ + u8 *data; +}; + +enum ce6230_cmd { + CONFIG_READ = 0xd0, /* rd 0 (unclear) */ + UNKNOWN_WRITE = 0xc7, /* wr 7 (unclear) */ + I2C_READ = 0xd9, /* rd 9 (unclear) */ + I2C_WRITE = 0xca, /* wr a */ + DEMOD_READ = 0xdb, /* rd b */ + DEMOD_WRITE = 0xcc, /* wr c */ + REG_READ = 0xde, /* rd e */ + REG_WRITE = 0xcf, /* wr f */ +}; + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c new file mode 100644 index 000000000000..9f7c970c6424 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c @@ -0,0 +1,125 @@ +/* cypress_firmware.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for downloading the firmware to Cypress FX 1 + * and 2 based devices. + * + */ + +#include "dvb_usb.h" +#include "cypress_firmware.h" + +struct usb_cypress_controller { + u8 id; + const char *name; /* name of the usb controller */ + u16 cs_reg; /* needs to be restarted, + * when the firmware has been downloaded */ +}; + +static const struct usb_cypress_controller cypress[] = { + { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 }, + { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 }, + { .id = CYPRESS_FX2, .name = "Cypress FX2", .cs_reg = 0xe600 }, +}; + +/* + * load a firmware packet to the device + */ +static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data, + u8 len) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000); +} + +int usbv2_cypress_load_firmware(struct usb_device *udev, + const struct firmware *fw, int type) +{ + struct hexline hx; + u8 reset; + int ret, pos = 0; + + /* stop the CPU */ + reset = 1; + ret = usb_cypress_writemem(udev, cypress[type].cs_reg, &reset, 1); + if (ret != 1) + pr_err("%s: could not stop the USB controller CPU", + KBUILD_MODNAME); + + while ((ret = dvb_usbv2_get_hexline(fw, &hx, &pos)) > 0) { + pr_debug("%s: writing to address %04x (buffer: %02x %02x)\n", + __func__, hx.addr, hx.len, hx.chk); + + ret = usb_cypress_writemem(udev, hx.addr, hx.data, hx.len); + if (ret != hx.len) { + pr_err("%s: error while transferring firmware " \ + "(transferred size=%d, block size=%d)", + KBUILD_MODNAME, ret, hx.len); + ret = -EINVAL; + break; + } + } + if (ret < 0) { + pr_err("%s: firmware download failed at %d with %d", + KBUILD_MODNAME, pos, ret); + return ret; + } + + if (ret == 0) { + /* restart the CPU */ + reset = 0; + if (ret || usb_cypress_writemem( + udev, cypress[type].cs_reg, &reset, 1) != 1) { + pr_err("%s: could not restart the USB controller CPU", + KBUILD_MODNAME); + ret = -EINVAL; + } + } else + ret = -EIO; + + return ret; +} +EXPORT_SYMBOL(usbv2_cypress_load_firmware); + +int dvb_usbv2_get_hexline(const struct firmware *fw, struct hexline *hx, + int *pos) +{ + u8 *b = (u8 *) &fw->data[*pos]; + int data_offs = 4; + + if (*pos >= fw->size) + return 0; + + memset(hx, 0, sizeof(struct hexline)); + + hx->len = b[0]; + + if ((*pos + hx->len + 4) >= fw->size) + return -EINVAL; + + hx->addr = b[1] | (b[2] << 8); + hx->type = b[3]; + + if (hx->type == 0x04) { + /* b[4] and b[5] are the Extended linear address record data + * field */ + hx->addr |= (b[4] << 24) | (b[5] << 16); + /* + hx->len -= 2; + data_offs += 2; + */ + } + memcpy(hx->data, &b[data_offs], hx->len); + hx->chk = b[hx->len + data_offs]; + + *pos += hx->len + 5; + + return *pos; +} +EXPORT_SYMBOL(dvb_usbv2_get_hexline); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Cypress firmware download"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.h b/drivers/media/usb/dvb-usb-v2/cypress_firmware.h new file mode 100644 index 000000000000..80085fd4132c --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/cypress_firmware.h @@ -0,0 +1,31 @@ +/* cypress_firmware.h is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for downloading the firmware to Cypress FX 1 + * and 2 based devices. + * + */ + +#ifndef CYPRESS_FIRMWARE_H +#define CYPRESS_FIRMWARE_H + +#define CYPRESS_AN2135 0 +#define CYPRESS_AN2235 1 +#define CYPRESS_FX2 2 + +/* commonly used firmware download types and function */ +struct hexline { + u8 len; + u32 addr; + u8 type; + u8 data[255]; + u8 chk; +}; +extern int usbv2_cypress_load_firmware(struct usb_device *, + const struct firmware *, int); +extern int dvb_usbv2_get_hexline(const struct firmware *, + struct hexline *, int *); + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h new file mode 100644 index 000000000000..79b3b8b6750d --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -0,0 +1,389 @@ +/* + * DVB USB framework + * + * Copyright (C) 2004-6 Patrick Boettcher + * Copyright (C) 2012 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 DVB_USB_H +#define DVB_USB_H + +#include +#include +#include + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" +#include "dvb-usb-ids.h" + +/* + * device file: /dev/dvb/adapter[0-1]/frontend[0-2] + * + * |-- device + * | |-- adapter0 + * | | |-- frontend0 + * | | |-- frontend1 + * | | `-- frontend2 + * | `-- adapter1 + * | |-- frontend0 + * | |-- frontend1 + * | `-- frontend2 + * + * + * Commonly used variable names: + * d = pointer to device (struct dvb_usb_device *) + * adap = pointer to adapter (struct dvb_usb_adapter *) + * fe = pointer to frontend (struct dvb_frontend *) + * + * Use macros defined in that file to resolve needed pointers. + */ + +/* helper macros for every DVB USB driver use */ +#define adap_to_d(adap) (container_of(adap, struct dvb_usb_device, \ + adapter[adap->id])) +#define adap_to_priv(adap) (adap_to_d(adap)->priv) +#define fe_to_adap(fe) ((struct dvb_usb_adapter *) ((fe)->dvb->priv)) +#define fe_to_d(fe) (adap_to_d(fe_to_adap(fe))) +#define fe_to_priv(fe) (fe_to_d(fe)->priv) +#define d_to_priv(d) (d->priv) + +#define DVB_USB_STREAM_BULK(endpoint_, count_, size_) { \ + .type = USB_BULK, \ + .count = count_, \ + .endpoint = endpoint_, \ + .u = { \ + .bulk = { \ + .buffersize = size_, \ + } \ + } \ +} + +#define DVB_USB_STREAM_ISOC(endpoint_, count_, frames_, size_, interval_) { \ + .type = USB_ISOC, \ + .count = count_, \ + .endpoint = endpoint_, \ + .u = { \ + .isoc = { \ + .framesperurb = frames_, \ + .framesize = size_,\ + .interval = interval_, \ + } \ + } \ +} + +#define DVB_USB_DEVICE(vend, prod, props_, name_, rc) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .driver_info = (kernel_ulong_t) &((const struct dvb_usb_driver_info) { \ + .props = (props_), \ + .name = (name_), \ + .rc_map = (rc), \ + }) + +struct dvb_usb_device; +struct dvb_usb_adapter; + +/** + * structure for carrying all needed data from the device driver to the general + * dvb usb routines + * @name: device name + * @rc_map: name of rc codes table + * @props: structure containing all device properties + */ +struct dvb_usb_driver_info { + const char *name; + const char *rc_map; + const struct dvb_usb_device_properties *props; +}; + +/** + * structure for remote controller configuration + * @map_name: name of rc codes table + * @allowed_protos: protocol(s) supported by the driver + * @change_protocol: callback to change protocol + * @query: called to query an event from the device + * @interval: time in ms between two queries + * @driver_type: used to point if a device supports raw mode + * @bulk_mode: device supports bulk mode for rc (disable polling mode) + */ +struct dvb_usb_rc { + const char *map_name; + u64 allowed_protos; + int (*change_protocol)(struct rc_dev *dev, u64 rc_type); + int (*query) (struct dvb_usb_device *d); + unsigned int interval; + const enum rc_driver_type driver_type; + bool bulk_mode; +}; + +/** + * usb streaming configration for adapter + * @type: urb type + * @count: count of used urbs + * @endpoint: stream usb endpoint number + */ +struct usb_data_stream_properties { +#define USB_BULK 1 +#define USB_ISOC 2 + u8 type; + u8 count; + u8 endpoint; + + union { + struct { + unsigned int buffersize; /* per URB */ + } bulk; + struct { + int framesperurb; + int framesize; + int interval; + } isoc; + } u; +}; + +/** + * properties of dvb usb device adapter + * @caps: adapter capabilities + * @pid_filter_count: pid count of adapter pid-filter + * @pid_filter_ctrl: called to enable/disable pid-filter + * @pid_filter: called to set/unset pid for filtering + * @stream: adapter usb stream configuration + */ +#define MAX_NO_OF_FE_PER_ADAP 3 +struct dvb_usb_adapter_properties { +#define DVB_USB_ADAP_HAS_PID_FILTER 0x01 +#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02 +#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04 + u8 caps; + + u8 pid_filter_count; + int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int); + int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int); + + struct usb_data_stream_properties stream; +}; + +/** + * struct dvb_usb_device_properties - properties of a dvb-usb-device + * @driver_name: name of the owning driver module + * @owner: owner of the dvb_adapter + * @adapter_nr: values from the DVB_DEFINE_MOD_OPT_ADAPTER_NR() macro + * @bInterfaceNumber: usb interface number driver binds + * @size_of_priv: bytes allocated for the driver private data + * @generic_bulk_ctrl_endpoint: bulk control endpoint number for sent + * @generic_bulk_ctrl_endpoint_response: bulk control endpoint number for + * receive + * @generic_bulk_ctrl_delay: delay between bulk control sent and receive message + * @identify_state: called to determine the firmware state (cold or warm) and + * return possible firmware file name to be loaded + * @firmware: name of the firmware file to be loaded + * @download_firmware: called to download the firmware + * @i2c_algo: i2c_algorithm if the device has i2c-adapter + * @num_adapters: dvb usb device adapter count + * @get_adapter_count: called to resolve adapter count + * @adapter: array of all adapter properties of device + * @power_ctrl: called to enable/disable power of the device + * @read_config: called to resolve device configuration + * @read_mac_address: called to resolve adapter mac-address + * @frontend_attach: called to attach the possible frontends + * @tuner_attach: called to attach the possible tuners + * @frontend_ctrl: called to power on/off active frontend + * @streaming_ctrl: called to start/stop the usb streaming of adapter + * @init: called after adapters are created in order to finalize device + * configuration + * @exit: called when driver is unloaded + * @get_rc_config: called to resolve used remote controller configuration + * @get_stream_config: called to resolve input and output stream configuration + * of the adapter just before streaming is started. input stream is transport + * stream from the demodulator and output stream is usb stream to host. + */ +#define MAX_NO_OF_ADAPTER_PER_DEVICE 2 +struct dvb_usb_device_properties { + const char *driver_name; + struct module *owner; + short *adapter_nr; + + u8 bInterfaceNumber; + unsigned int size_of_priv; + u8 generic_bulk_ctrl_endpoint; + u8 generic_bulk_ctrl_endpoint_response; + unsigned int generic_bulk_ctrl_delay; + +#define WARM 0 +#define COLD 1 + int (*identify_state) (struct dvb_usb_device *, const char **); + const char *firmware; +#define RECONNECTS_USB 1 + int (*download_firmware) (struct dvb_usb_device *, + const struct firmware *); + + struct i2c_algorithm *i2c_algo; + + unsigned int num_adapters; + int (*get_adapter_count) (struct dvb_usb_device *); + struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; + int (*power_ctrl) (struct dvb_usb_device *, int); + int (*read_config) (struct dvb_usb_device *d); + int (*read_mac_address) (struct dvb_usb_adapter *, u8 []); + int (*frontend_attach) (struct dvb_usb_adapter *); + int (*tuner_attach) (struct dvb_usb_adapter *); + int (*frontend_ctrl) (struct dvb_frontend *, int); + int (*streaming_ctrl) (struct dvb_frontend *, int); + int (*init) (struct dvb_usb_device *); + void (*exit) (struct dvb_usb_device *); + int (*get_rc_config) (struct dvb_usb_device *, struct dvb_usb_rc *); +#define DVB_USB_FE_TS_TYPE_188 0 +#define DVB_USB_FE_TS_TYPE_204 1 +#define DVB_USB_FE_TS_TYPE_RAW 2 + int (*get_stream_config) (struct dvb_frontend *, u8 *, + struct usb_data_stream_properties *); +}; + +/** + * generic object of an usb stream + * @buf_num: number of buffer allocated + * @buf_size: size of each buffer in buf_list + * @buf_list: array containing all allocate buffers for streaming + * @dma_addr: list of dma_addr_t for each buffer in buf_list + * + * @urbs_initialized: number of URBs initialized + * @urbs_submitted: number of URBs submitted + */ +#define MAX_NO_URBS_FOR_DATA_STREAM 10 +struct usb_data_stream { + struct usb_device *udev; + struct usb_data_stream_properties props; + +#define USB_STATE_INIT 0x00 +#define USB_STATE_URB_BUF 0x01 + u8 state; + + void (*complete) (struct usb_data_stream *, u8 *, size_t); + + struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM]; + int buf_num; + unsigned long buf_size; + u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM]; + dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM]; + + int urbs_initialized; + int urbs_submitted; + + void *user_priv; +}; + +/** + * dvb adapter object on dvb usb device + * @props: pointer to adapter properties + * @stream: adapter the usb data stream + * @id: index of this adapter (starting with 0) + * @ts_type: transport stream, input stream, type + * @pid_filtering: is hardware pid_filtering used or not + * @feed_count: current feed count + * @max_feed_count: maimum feed count device can handle + * @dvb_adap: adapter dvb_adapter + * @dmxdev: adapter dmxdev + * @demux: adapter software demuxer + * @dvb_net: adapter dvb_net interfaces + * @sync_mutex: mutex used to sync control and streaming of the adapter + * @fe: adapter frontends + * @fe_init: rerouted frontend-init function + * @fe_sleep: rerouted frontend-sleep function + */ +struct dvb_usb_adapter { + const struct dvb_usb_adapter_properties *props; + struct usb_data_stream stream; + u8 id; + u8 ts_type; + bool pid_filtering; + u8 feed_count; + u8 max_feed_count; + s8 active_fe; + + /* dvb */ + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct mutex sync_mutex; + + struct dvb_frontend *fe[MAX_NO_OF_FE_PER_ADAP]; + int (*fe_init[MAX_NO_OF_FE_PER_ADAP]) (struct dvb_frontend *); + int (*fe_sleep[MAX_NO_OF_FE_PER_ADAP]) (struct dvb_frontend *); +}; + +/** + * dvb usb device object + * @props: device properties + * @name: device name + * @rc_map: name of rc codes table + * @udev: pointer to the device's struct usb_device + * @intf: pointer to the device's usb interface + * @rc: remote controller configuration + * @probe_work: work to defer .probe() + * @powered: indicated whether the device is power or not + * @usb_mutex: mutex for usb control messages + * @i2c_mutex: mutex for i2c-transfers + * @i2c_adap: device's i2c-adapter + * @rc_dev: rc device for the remote control + * @rc_query_work: work for polling remote + * @priv: private data of the actual driver (allocate by dvb usb, size defined + * in size_of_priv of dvb_usb_properties). + */ +struct dvb_usb_device { + const struct dvb_usb_device_properties *props; + const char *name; + const char *rc_map; + + struct usb_device *udev; + struct usb_interface *intf; + struct dvb_usb_rc rc; + struct work_struct probe_work; + pid_t work_pid; + int powered; + + /* locking */ + struct mutex usb_mutex; + + /* i2c */ + struct mutex i2c_mutex; + struct i2c_adapter i2c_adap; + + struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; + + /* remote control */ + struct rc_dev *rc_dev; + char rc_phys[64]; + struct delayed_work rc_query_work; + + void *priv; +}; + +extern int dvb_usbv2_probe(struct usb_interface *, + const struct usb_device_id *); +extern void dvb_usbv2_disconnect(struct usb_interface *); +extern int dvb_usbv2_suspend(struct usb_interface *, pm_message_t); +extern int dvb_usbv2_resume(struct usb_interface *); + +/* the generic read/write method for device control */ +extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); +extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16); + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_common.h b/drivers/media/usb/dvb-usb-v2/dvb_usb_common.h new file mode 100644 index 000000000000..45f07090d431 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_common.h @@ -0,0 +1,35 @@ +/* + * DVB USB framework + * + * Copyright (C) 2004-6 Patrick Boettcher + * Copyright (C) 2012 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 DVB_USB_COMMON_H +#define DVB_USB_COMMON_H + +#include "dvb_usb.h" + +/* commonly used methods */ +extern int usb_urb_initv2(struct usb_data_stream *stream, + const struct usb_data_stream_properties *props); +extern int usb_urb_exitv2(struct usb_data_stream *stream); +extern int usb_urb_submitv2(struct usb_data_stream *stream, + struct usb_data_stream_properties *props); +extern int usb_urb_killv2(struct usb_data_stream *stream); + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c new file mode 100644 index 000000000000..a72f9c7de682 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -0,0 +1,997 @@ +/* + * DVB USB framework + * + * Copyright (C) 2004-6 Patrick Boettcher + * Copyright (C) 2012 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 "dvb_usb_common.h" + +int dvb_usbv2_disable_rc_polling; +module_param_named(disable_rc_polling, dvb_usbv2_disable_rc_polling, int, 0644); +MODULE_PARM_DESC(disable_rc_polling, + "disable remote control polling (default: 0)"); +static int dvb_usb_force_pid_filter_usage; +module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage, + int, 0444); +MODULE_PARM_DESC(force_pid_filter_usage, "force all DVB USB devices to use a " \ + "PID filter, if any (default: 0)"); + +static int dvb_usbv2_download_firmware(struct dvb_usb_device *d, const char *name) +{ + int ret; + const struct firmware *fw; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + if (!d->props->download_firmware) { + ret = -EINVAL; + goto err; + } + + ret = request_firmware(&fw, name, &d->udev->dev); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: Did not find the firmware file "\ + "'%s'. Please see linux/Documentation/dvb/ " \ + "for more details on firmware-problems. " \ + "Status %d\n", KBUILD_MODNAME, name, ret); + goto err; + } + + dev_info(&d->udev->dev, "%s: downloading firmware from file '%s'\n", + KBUILD_MODNAME, name); + + ret = d->props->download_firmware(d, fw); + release_firmware(fw); + if (ret < 0) + goto err; + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usbv2_i2c_init(struct dvb_usb_device *d) +{ + int ret; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + if (!d->props->i2c_algo) + return 0; + + strlcpy(d->i2c_adap.name, d->name, sizeof(d->i2c_adap.name)); + d->i2c_adap.algo = d->props->i2c_algo; + d->i2c_adap.dev.parent = &d->udev->dev; + i2c_set_adapdata(&d->i2c_adap, d); + + ret = i2c_add_adapter(&d->i2c_adap); + if (ret < 0) { + d->i2c_adap.algo = NULL; + dev_err(&d->udev->dev, "%s: i2c_add_adapter() failed=%d\n", + KBUILD_MODNAME, ret); + goto err; + } + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d) +{ + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + if (d->i2c_adap.algo) + i2c_del_adapter(&d->i2c_adap); + + return 0; +} + +static void dvb_usb_read_remote_control(struct work_struct *work) +{ + struct dvb_usb_device *d = container_of(work, + struct dvb_usb_device, rc_query_work.work); + int ret; + + /* + * When the parameter has been set to 1 via sysfs while the + * driver was running, or when bulk mode is enabled after IR init. + */ + if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) + return; + + ret = d->rc.query(d); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: rc.query() failed=%d\n", + KBUILD_MODNAME, ret); + return; /* stop polling */ + } + + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(d->rc.interval)); +} + +static int dvb_usbv2_remote_init(struct dvb_usb_device *d) +{ + int ret; + struct rc_dev *dev; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + if (dvb_usbv2_disable_rc_polling || !d->props->get_rc_config) + return 0; + + d->rc.map_name = d->rc_map; + ret = d->props->get_rc_config(d, &d->rc); + if (ret < 0) + goto err; + + /* disable rc when there is no keymap defined */ + if (!d->rc.map_name) + return 0; + + dev = rc_allocate_device(); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->dev.parent = &d->udev->dev; + dev->input_name = d->name; + usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); + strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); + dev->input_phys = d->rc_phys; + usb_to_input_id(d->udev, &dev->input_id); + /* TODO: likely RC-core should took const char * */ + dev->driver_name = (char *) d->props->driver_name; + dev->map_name = d->rc.map_name; + dev->driver_type = d->rc.driver_type; + dev->allowed_protos = d->rc.allowed_protos; + dev->change_protocol = d->rc.change_protocol; + dev->priv = d; + + ret = rc_register_device(dev); + if (ret < 0) { + rc_free_device(dev); + goto err; + } + + d->rc_dev = dev; + + /* start polling if needed */ + if (d->rc.query && !d->rc.bulk_mode) { + /* initialize a work queue for handling polling */ + INIT_DELAYED_WORK(&d->rc_query_work, + dvb_usb_read_remote_control); + dev_info(&d->udev->dev, "%s: schedule remote query interval " \ + "to %d msecs\n", KBUILD_MODNAME, + d->rc.interval); + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(d->rc.interval)); + } + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) +{ + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + if (d->rc_dev) { + cancel_delayed_work_sync(&d->rc_query_work); + rc_unregister_device(d->rc_dev); + d->rc_dev = NULL; + } + + return 0; +} + +static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, + size_t len) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + dvb_dmx_swfilter(&adap->demux, buf, len); +} + +static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buf, + size_t len) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + dvb_dmx_swfilter_204(&adap->demux, buf, len); +} + +static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, u8 *buf, + size_t len) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + dvb_dmx_swfilter_raw(&adap->demux, buf, len); +} + +int dvb_usbv2_adapter_stream_init(struct dvb_usb_adapter *adap) +{ + dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__, + adap->id); + + adap->stream.udev = adap_to_d(adap)->udev; + adap->stream.user_priv = adap; + adap->stream.complete = dvb_usb_data_complete; + + return usb_urb_initv2(&adap->stream, &adap->props->stream); +} + +int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap) +{ + dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__, + adap->id); + + return usb_urb_exitv2(&adap->stream); +} + +static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, + int count) +{ + struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; + struct dvb_usb_device *d = adap_to_d(adap); + int ret; + dev_dbg(&d->udev->dev, "%s: adap=%d active_fe=%d feed_type=%d " \ + "setting pid [%s]: %04x (%04d) at index %d '%s'\n", + __func__, adap->id, adap->active_fe, dvbdmxfeed->type, + adap->pid_filtering ? "yes" : "no", dvbdmxfeed->pid, + dvbdmxfeed->pid, dvbdmxfeed->index, + (count == 1) ? "on" : "off"); + + if (adap->active_fe == -1) + return -EINVAL; + + adap->feed_count += count; + + /* stop feeding if it is last pid */ + if (adap->feed_count == 0) { + dev_dbg(&d->udev->dev, "%s: stop feeding\n", __func__); + usb_urb_killv2(&adap->stream); + + if (d->props->streaming_ctrl) { + ret = d->props->streaming_ctrl( + adap->fe[adap->active_fe], 0); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: streaming_ctrl() " \ + "failed=%d\n", KBUILD_MODNAME, + ret); + goto err_mutex_unlock; + } + } + mutex_unlock(&adap->sync_mutex); + } + + /* activate the pid on the device pid filter */ + if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->pid_filtering && + adap->props->pid_filter) + ret = adap->props->pid_filter(adap, dvbdmxfeed->index, + dvbdmxfeed->pid, (count == 1) ? 1 : 0); + if (ret < 0) + dev_err(&d->udev->dev, "%s: pid_filter() " \ + "failed=%d\n", KBUILD_MODNAME, + ret); + + /* start feeding if it is first pid */ + if (adap->feed_count == 1 && count == 1) { + struct usb_data_stream_properties stream_props; + mutex_lock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: start feeding\n", __func__); + + /* resolve input and output streaming paramters */ + if (d->props->get_stream_config) { + memcpy(&stream_props, &adap->props->stream, + sizeof(struct usb_data_stream_properties)); + ret = d->props->get_stream_config( + adap->fe[adap->active_fe], + &adap->ts_type, &stream_props); + if (ret < 0) + goto err_mutex_unlock; + } else { + stream_props = adap->props->stream; + } + + switch (adap->ts_type) { + case DVB_USB_FE_TS_TYPE_204: + adap->stream.complete = dvb_usb_data_complete_204; + break; + case DVB_USB_FE_TS_TYPE_RAW: + adap->stream.complete = dvb_usb_data_complete_raw; + break; + case DVB_USB_FE_TS_TYPE_188: + default: + adap->stream.complete = dvb_usb_data_complete; + break; + } + + usb_urb_submitv2(&adap->stream, &stream_props); + + if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->props->caps & + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF && + adap->props->pid_filter_ctrl) { + ret = adap->props->pid_filter_ctrl(adap, + adap->pid_filtering); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: " \ + "pid_filter_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + goto err_mutex_unlock; + } + } + + if (d->props->streaming_ctrl) { + ret = d->props->streaming_ctrl( + adap->fe[adap->active_fe], 1); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: streaming_ctrl() " \ + "failed=%d\n", KBUILD_MODNAME, + ret); + goto err_mutex_unlock; + } + } + } + + return 0; +err_mutex_unlock: + mutex_unlock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + return dvb_usb_ctrl_feed(dvbdmxfeed, 1); +} + +static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + return dvb_usb_ctrl_feed(dvbdmxfeed, -1); +} + +int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_usb_device *d = adap_to_d(adap); + dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id); + + ret = dvb_register_adapter(&adap->dvb_adap, d->name, d->props->owner, + &d->udev->dev, d->props->adapter_nr); + if (ret < 0) { + dev_dbg(&d->udev->dev, "%s: dvb_register_adapter() failed=%d\n", + __func__, ret); + goto err_dvb_register_adapter; + } + + adap->dvb_adap.priv = adap; + + if (d->props->read_mac_address) { + ret = d->props->read_mac_address(adap, + adap->dvb_adap.proposed_mac); + if (ret < 0) + goto err_dvb_dmx_init; + + dev_info(&d->udev->dev, "%s: MAC address: %pM\n", + KBUILD_MODNAME, adap->dvb_adap.proposed_mac); + } + + adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + adap->demux.priv = adap; + adap->demux.filternum = 0; + adap->demux.filternum = adap->max_feed_count; + adap->demux.feednum = adap->demux.filternum; + adap->demux.start_feed = dvb_usb_start_feed; + adap->demux.stop_feed = dvb_usb_stop_feed; + adap->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: dvb_dmx_init() failed=%d\n", + KBUILD_MODNAME, ret); + goto err_dvb_dmx_init; + } + + adap->dmxdev.filternum = adap->demux.filternum; + adap->dmxdev.demux = &adap->demux.dmx; + adap->dmxdev.capabilities = 0; + ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: dvb_dmxdev_init() failed=%d\n", + KBUILD_MODNAME, ret); + goto err_dvb_dmxdev_init; + } + + ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: dvb_net_init() failed=%d\n", + KBUILD_MODNAME, ret); + goto err_dvb_net_init; + } + + mutex_init(&adap->sync_mutex); + + return 0; +err_dvb_net_init: + dvb_dmxdev_release(&adap->dmxdev); +err_dvb_dmxdev_init: + dvb_dmx_release(&adap->demux); +err_dvb_dmx_init: + dvb_unregister_adapter(&adap->dvb_adap); +err_dvb_register_adapter: + adap->dvb_adap.priv = NULL; + return ret; +} + +int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap) +{ + dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__, + adap->id); + + if (adap->dvb_adap.priv) { + dvb_net_release(&adap->dvb_net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->dvb_adap); + } + + return 0; +} + +int dvb_usbv2_device_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + + if (onoff) + d->powered++; + else + d->powered--; + + if (d->powered == 0 || (onoff && d->powered == 1)) { + /* when switching from 1 to 0 or from 0 to 1 */ + dev_dbg(&d->udev->dev, "%s: power=%d\n", __func__, onoff); + if (d->props->power_ctrl) { + ret = d->props->power_ctrl(d, onoff); + if (ret < 0) + goto err; + } + } + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usb_fe_init(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap_to_d(adap); + mutex_lock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id, + fe->id); + + ret = dvb_usbv2_device_power_ctrl(d, 1); + if (ret < 0) + goto err; + + if (d->props->frontend_ctrl) { + ret = d->props->frontend_ctrl(fe, 1); + if (ret < 0) + goto err; + } + + if (adap->fe_init[fe->id]) { + ret = adap->fe_init[fe->id](fe); + if (ret < 0) + goto err; + } + + adap->active_fe = fe->id; + mutex_unlock(&adap->sync_mutex); + + return 0; +err: + mutex_unlock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usb_fe_sleep(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap_to_d(adap); + mutex_lock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id, + fe->id); + + if (adap->fe_sleep[fe->id]) { + ret = adap->fe_sleep[fe->id](fe); + if (ret < 0) + goto err; + } + + if (d->props->frontend_ctrl) { + ret = d->props->frontend_ctrl(fe, 0); + if (ret < 0) + goto err; + } + + ret = dvb_usbv2_device_power_ctrl(d, 0); + if (ret < 0) + goto err; + + adap->active_fe = -1; + mutex_unlock(&adap->sync_mutex); + + return 0; +err: + mutex_unlock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) +{ + int ret, i, count_registered = 0; + struct dvb_usb_device *d = adap_to_d(adap); + dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id); + + memset(adap->fe, 0, sizeof(adap->fe)); + adap->active_fe = -1; + + if (d->props->frontend_attach) { + ret = d->props->frontend_attach(adap); + if (ret < 0) { + dev_dbg(&d->udev->dev, "%s: frontend_attach() " \ + "failed=%d\n", __func__, ret); + goto err_dvb_frontend_detach; + } + } else { + dev_dbg(&d->udev->dev, "%s: frontend_attach() do not exists\n", + __func__); + ret = 0; + goto err; + } + + for (i = 0; i < MAX_NO_OF_FE_PER_ADAP && adap->fe[i]; i++) { + adap->fe[i]->id = i; + /* re-assign sleep and wakeup functions */ + adap->fe_init[i] = adap->fe[i]->ops.init; + adap->fe[i]->ops.init = dvb_usb_fe_init; + adap->fe_sleep[i] = adap->fe[i]->ops.sleep; + adap->fe[i]->ops.sleep = dvb_usb_fe_sleep; + + ret = dvb_register_frontend(&adap->dvb_adap, adap->fe[i]); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: frontend%d registration " \ + "failed\n", KBUILD_MODNAME, i); + goto err_dvb_unregister_frontend; + } + + count_registered++; + } + + if (d->props->tuner_attach) { + ret = d->props->tuner_attach(adap); + if (ret < 0) { + dev_dbg(&d->udev->dev, "%s: tuner_attach() failed=%d\n", + __func__, ret); + goto err_dvb_unregister_frontend; + } + } + + return 0; + +err_dvb_unregister_frontend: + for (i = count_registered - 1; i >= 0; i--) + dvb_unregister_frontend(adap->fe[i]); + +err_dvb_frontend_detach: + for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { + if (adap->fe[i]) + dvb_frontend_detach(adap->fe[i]); + } + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap) +{ + int i; + dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__, + adap->id); + + for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { + if (adap->fe[i]) { + dvb_unregister_frontend(adap->fe[i]); + dvb_frontend_detach(adap->fe[i]); + } + } + + return 0; +} + +static int dvb_usbv2_adapter_init(struct dvb_usb_device *d) +{ + struct dvb_usb_adapter *adap; + int ret, i, adapter_count; + + /* resolve adapter count */ + adapter_count = d->props->num_adapters; + if (d->props->get_adapter_count) { + ret = d->props->get_adapter_count(d); + if (ret < 0) + goto err; + + adapter_count = ret; + } + + for (i = 0; i < adapter_count; i++) { + adap = &d->adapter[i]; + adap->id = i; + adap->props = &d->props->adapter[i]; + + /* speed - when running at FULL speed we need a HW PID filter */ + if (d->udev->speed == USB_SPEED_FULL && + !(adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { + dev_err(&d->udev->dev, "%s: this USB2.0 device " \ + "cannot be run on a USB1.1 port (it " \ + "lacks a hardware PID filter)\n", + KBUILD_MODNAME); + ret = -ENODEV; + goto err; + } else if ((d->udev->speed == USB_SPEED_FULL && + adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || + (adap->props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { + dev_info(&d->udev->dev, "%s: will use the device's " \ + "hardware PID filter " \ + "(table count: %d)\n", KBUILD_MODNAME, + adap->props->pid_filter_count); + adap->pid_filtering = 1; + adap->max_feed_count = adap->props->pid_filter_count; + } else { + dev_info(&d->udev->dev, "%s: will pass the complete " \ + "MPEG2 transport stream to the " \ + "software demuxer\n", KBUILD_MODNAME); + adap->pid_filtering = 0; + adap->max_feed_count = 255; + } + + if (!adap->pid_filtering && dvb_usb_force_pid_filter_usage && + adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { + dev_info(&d->udev->dev, "%s: PID filter enabled by " \ + "module option\n", KBUILD_MODNAME); + adap->pid_filtering = 1; + adap->max_feed_count = adap->props->pid_filter_count; + } + + ret = dvb_usbv2_adapter_stream_init(adap); + if (ret) + goto err; + + ret = dvb_usbv2_adapter_dvb_init(adap); + if (ret) + goto err; + + ret = dvb_usbv2_adapter_frontend_init(adap); + if (ret) + goto err; + + /* use exclusive FE lock if there is multiple shared FEs */ + if (adap->fe[1]) + adap->dvb_adap.mfe_shared = 1; + } + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d) +{ + int i; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) { + if (d->adapter[i].props) { + dvb_usbv2_adapter_frontend_exit(&d->adapter[i]); + dvb_usbv2_adapter_dvb_exit(&d->adapter[i]); + dvb_usbv2_adapter_stream_exit(&d->adapter[i]); + } + } + + return 0; +} + +/* general initialization functions */ +static int dvb_usbv2_exit(struct dvb_usb_device *d) +{ + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + dvb_usbv2_remote_exit(d); + dvb_usbv2_adapter_exit(d); + dvb_usbv2_i2c_exit(d); + kfree(d->priv); + kfree(d); + + return 0; +} + +static int dvb_usbv2_init(struct dvb_usb_device *d) +{ + int ret; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + dvb_usbv2_device_power_ctrl(d, 1); + + if (d->props->read_config) { + ret = d->props->read_config(d); + if (ret < 0) + goto err; + } + + ret = dvb_usbv2_i2c_init(d); + if (ret < 0) + goto err; + + ret = dvb_usbv2_adapter_init(d); + if (ret < 0) + goto err; + + if (d->props->init) { + ret = d->props->init(d); + if (ret < 0) + goto err; + } + + ret = dvb_usbv2_remote_init(d); + if (ret < 0) + goto err; + + dvb_usbv2_device_power_ctrl(d, 0); + + return 0; +err: + dvb_usbv2_device_power_ctrl(d, 0); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +/* + * udev, which is used for the firmware downloading, requires we cannot + * block during module_init(). module_init() calls USB probe() which + * is this routine. Due to that we delay actual operation using workqueue + * and return always success here. + */ +static void dvb_usbv2_init_work(struct work_struct *work) +{ + int ret; + struct dvb_usb_device *d = + container_of(work, struct dvb_usb_device, probe_work); + + d->work_pid = current->pid; + dev_dbg(&d->udev->dev, "%s: work_pid=%d\n", __func__, d->work_pid); + + if (d->props->size_of_priv) { + d->priv = kzalloc(d->props->size_of_priv, GFP_KERNEL); + if (!d->priv) { + dev_err(&d->udev->dev, "%s: kzalloc() failed\n", + KBUILD_MODNAME); + ret = -ENOMEM; + goto err_usb_driver_release_interface; + } + } + + if (d->props->identify_state) { + const char *name = NULL; + ret = d->props->identify_state(d, &name); + if (ret == 0) { + ; + } else if (ret == COLD) { + dev_info(&d->udev->dev, "%s: found a '%s' in cold " \ + "state\n", KBUILD_MODNAME, d->name); + + if (!name) + name = d->props->firmware; + + ret = dvb_usbv2_download_firmware(d, name); + if (ret == 0) { + /* device is warm, continue initialization */ + ; + } else if (ret == RECONNECTS_USB) { + /* + * USB core will call disconnect() and then + * probe() as device reconnects itself from the + * USB bus. disconnect() will release all driver + * resources and probe() is called for 'new' + * device. As 'new' device is warm we should + * never go here again. + */ + return; + } else { + /* + * Unexpected error. We must unregister driver + * manually from the device, because device is + * already register by returning from probe() + * with success. usb_driver_release_interface() + * finally calls disconnect() in order to free + * resources. + */ + goto err_usb_driver_release_interface; + } + } else { + goto err_usb_driver_release_interface; + } + } + + dev_info(&d->udev->dev, "%s: found a '%s' in warm state\n", + KBUILD_MODNAME, d->name); + + ret = dvb_usbv2_init(d); + if (ret < 0) + goto err_usb_driver_release_interface; + + dev_info(&d->udev->dev, "%s: '%s' successfully initialized and " \ + "connected\n", KBUILD_MODNAME, d->name); + + return; +err_usb_driver_release_interface: + dev_info(&d->udev->dev, "%s: '%s' error while loading driver (%d)\n", + KBUILD_MODNAME, d->name, ret); + usb_driver_release_interface(to_usb_driver(d->intf->dev.driver), + d->intf); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return; +} + +int dvb_usbv2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + struct dvb_usb_device *d; + struct usb_device *udev = interface_to_usbdev(intf); + struct dvb_usb_driver_info *driver_info = + (struct dvb_usb_driver_info *) id->driver_info; + + dev_dbg(&udev->dev, "%s: bInterfaceNumber=%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + if (!id->driver_info) { + dev_err(&udev->dev, "%s: driver_info failed\n", KBUILD_MODNAME); + ret = -ENODEV; + goto err; + } + + d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); + if (!d) { + dev_err(&udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + ret = -ENOMEM; + goto err; + } + + d->name = driver_info->name; + d->rc_map = driver_info->rc_map; + d->udev = udev; + d->intf = intf; + d->props = driver_info->props; + + if (d->intf->cur_altsetting->desc.bInterfaceNumber != + d->props->bInterfaceNumber) { + ret = -ENODEV; + goto err_kfree; + } + + mutex_init(&d->usb_mutex); + mutex_init(&d->i2c_mutex); + INIT_WORK(&d->probe_work, dvb_usbv2_init_work); + usb_set_intfdata(intf, d); + ret = schedule_work(&d->probe_work); + if (ret < 0) { + dev_err(&d->udev->dev, "%s: schedule_work() failed\n", + KBUILD_MODNAME); + goto err_kfree; + } + + return 0; +err_kfree: + kfree(d); +err: + dev_dbg(&udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(dvb_usbv2_probe); + +void dvb_usbv2_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + const char *name = d->name; + struct device dev = d->udev->dev; + dev_dbg(&d->udev->dev, "%s: pid=%d work_pid=%d\n", __func__, + current->pid, d->work_pid); + + /* ensure initialization work is finished until release resources */ + if (d->work_pid != current->pid) + cancel_work_sync(&d->probe_work); + + if (d->props->exit) + d->props->exit(d); + + dvb_usbv2_exit(d); + + dev_info(&dev, "%s: '%s' successfully deinitialized and disconnected\n", + KBUILD_MODNAME, name); +} +EXPORT_SYMBOL(dvb_usbv2_disconnect); + +int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + int i; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + /* stop remote controller poll */ + if (d->rc.query && !d->rc.bulk_mode) + cancel_delayed_work_sync(&d->rc_query_work); + + /* stop streaming */ + for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) { + if (d->adapter[i].dvb_adap.priv && + d->adapter[i].active_fe != -1) + usb_urb_killv2(&d->adapter[i].stream); + } + + return 0; +} +EXPORT_SYMBOL(dvb_usbv2_suspend); + +int dvb_usbv2_resume(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + int i; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + /* start streaming */ + for (i = 0; i < MAX_NO_OF_ADAPTER_PER_DEVICE; i++) { + if (d->adapter[i].dvb_adap.priv && + d->adapter[i].active_fe != -1) + usb_urb_submitv2(&d->adapter[i].stream, NULL); + } + + /* start remote controller poll */ + if (d->rc.query && !d->rc.bulk_mode) + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(d->rc.interval)); + + return 0; +} +EXPORT_SYMBOL(dvb_usbv2_resume); + +MODULE_VERSION("2.0"); +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("DVB USB common"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c new file mode 100644 index 000000000000..0431beed0ef4 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c @@ -0,0 +1,77 @@ +/* + * DVB USB framework + * + * Copyright (C) 2004-6 Patrick Boettcher + * Copyright (C) 2012 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 "dvb_usb_common.h" + +int dvb_usbv2_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, + u16 rlen) +{ + int ret, actual_length; + + if (!d || !wbuf || !wlen || !d->props->generic_bulk_ctrl_endpoint || + !d->props->generic_bulk_ctrl_endpoint_response) { + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, -EINVAL); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&d->usb_mutex); + if (ret < 0) + return ret; + + dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, wlen, wbuf); + + ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, + d->props->generic_bulk_ctrl_endpoint), wbuf, wlen, + &actual_length, 2000); + if (ret < 0) + dev_err(&d->udev->dev, "%s: usb_bulk_msg() failed=%d\n", + KBUILD_MODNAME, ret); + else + ret = actual_length != wlen ? -EIO : 0; + + /* an answer is expected, and no error before */ + if (!ret && rbuf && rlen) { + if (d->props->generic_bulk_ctrl_delay) + usleep_range(d->props->generic_bulk_ctrl_delay, + d->props->generic_bulk_ctrl_delay + + 20000); + + ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, + d->props->generic_bulk_ctrl_endpoint_response), + rbuf, rlen, &actual_length, 2000); + if (ret) + dev_err(&d->udev->dev, "%s: 2nd usb_bulk_msg() " \ + "failed=%d\n", KBUILD_MODNAME, ret); + + dev_dbg(&d->udev->dev, "%s: <<< %*ph\n", __func__, + actual_length, rbuf); + } + + mutex_unlock(&d->usb_mutex); + return ret; +} +EXPORT_SYMBOL(dvb_usbv2_generic_rw); + +int dvb_usbv2_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len) +{ + return dvb_usbv2_generic_rw(d, buf, len, NULL, 0); +} +EXPORT_SYMBOL(dvb_usbv2_generic_write); diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c new file mode 100644 index 000000000000..ab77622c383d --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/ec168.c @@ -0,0 +1,376 @@ +/* + * E3C EC168 DVB USB 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ec168.h" +#include "ec100.h" +#include "mxl5005s.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req) +{ + int ret; + unsigned int pipe; + u8 request, requesttype; + u8 *buf; + + switch (req->cmd) { + case DOWNLOAD_FIRMWARE: + case GPIO: + case WRITE_I2C: + case STREAMING_CTRL: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = req->cmd; + break; + case READ_I2C: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = req->cmd; + break; + case GET_CONFIG: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = CONFIG; + break; + case SET_CONFIG: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = CONFIG; + break; + case WRITE_DEMOD: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = DEMOD_RW; + break; + case READ_DEMOD: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = DEMOD_RW; + break; + default: + pr_err("%s: unknown command=%02x\n", KBUILD_MODNAME, req->cmd); + ret = -EINVAL; + goto error; + } + + buf = kmalloc(req->size, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto error; + } + + if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) { + /* write */ + memcpy(buf, req->data, req->size); + pipe = usb_sndctrlpipe(d->udev, 0); + } else { + /* read */ + pipe = usb_rcvctrlpipe(d->udev, 0); + } + + msleep(1); /* avoid I2C errors */ + + ret = usb_control_msg(d->udev, pipe, request, requesttype, req->value, + req->index, buf, req->size, EC168_USB_TIMEOUT); + + ec168_debug_dump(request, requesttype, req->value, req->index, buf, + req->size); + + if (ret < 0) + goto err_dealloc; + else + ret = 0; + + /* read request, copy returned data to return buf */ + if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, buf, req->size); + + kfree(buf); + return ret; + +err_dealloc: + kfree(buf); +error: + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +/* I2C */ +static struct ec100_config ec168_ec100_config; + +static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct ec168_req req; + int i = 0; + int ret; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].addr == ec168_ec100_config.demod_address) { + req.cmd = READ_DEMOD; + req.value = 0; + req.index = 0xff00 + msg[i].buf[0]; /* reg */ + req.size = msg[i+1].len; /* bytes to read */ + req.data = &msg[i+1].buf[0]; + ret = ec168_ctrl_msg(d, &req); + i += 2; + } else { + pr_err("%s: I2C read not implemented\n", + KBUILD_MODNAME); + ret = -EOPNOTSUPP; + i += 2; + } + } else { + if (msg[i].addr == ec168_ec100_config.demod_address) { + req.cmd = WRITE_DEMOD; + req.value = msg[i].buf[1]; /* val */ + req.index = 0xff00 + msg[i].buf[0]; /* reg */ + req.size = 0; + req.data = NULL; + ret = ec168_ctrl_msg(d, &req); + i += 1; + } else { + req.cmd = WRITE_I2C; + req.value = msg[i].buf[0]; /* val */ + req.index = 0x0100 + msg[i].addr; /* I2C addr */ + req.size = msg[i].len-1; + req.data = &msg[i].buf[1]; + ret = ec168_ctrl_msg(d, &req); + i += 1; + } + } + if (ret) + goto error; + + } + ret = i; + +error: + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 ec168_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm ec168_i2c_algo = { + .master_xfer = ec168_i2c_xfer, + .functionality = ec168_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int ec168_identify_state(struct dvb_usb_device *d, const char **name) +{ + int ret; + u8 reply; + struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply}; + pr_debug("%s:\n", __func__); + + ret = ec168_ctrl_msg(d, &req); + if (ret) + goto error; + + pr_debug("%s: reply=%02x\n", __func__, reply); + + if (reply == 0x01) + ret = WARM; + else + ret = COLD; + + return ret; +error: + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int ec168_download_firmware(struct dvb_usb_device *d, + const struct firmware *fw) +{ + int ret, len, remaining; + struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL}; + pr_debug("%s:\n", __func__); + + #define LEN_MAX 2048 /* max packet size */ + for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { + len = remaining; + if (len > LEN_MAX) + len = LEN_MAX; + + req.size = len; + req.data = (u8 *) &fw->data[fw->size - remaining]; + req.index = fw->size - remaining; + + ret = ec168_ctrl_msg(d, &req); + if (ret) { + pr_err("%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); + goto error; + } + } + + req.size = 0; + + /* set "warm"? */ + req.cmd = SET_CONFIG; + req.value = 0; + req.index = 0x0001; + ret = ec168_ctrl_msg(d, &req); + if (ret) + goto error; + + /* really needed - no idea what does */ + req.cmd = GPIO; + req.value = 0; + req.index = 0x0206; + ret = ec168_ctrl_msg(d, &req); + if (ret) + goto error; + + /* activate tuner I2C? */ + req.cmd = WRITE_I2C; + req.value = 0; + req.index = 0x00c6; + ret = ec168_ctrl_msg(d, &req); + if (ret) + goto error; + + return ret; +error: + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +static struct ec100_config ec168_ec100_config = { + .demod_address = 0xff, /* not real address, demod is integrated */ +}; + +static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap) +{ + pr_debug("%s:\n", __func__); + adap->fe[0] = dvb_attach(ec100_attach, &ec168_ec100_config, + &adap_to_d(adap)->i2c_adap); + if (adap->fe[0] == NULL) + return -ENODEV; + + return 0; +} + +static struct mxl5005s_config ec168_mxl5003s_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_OFF, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +{ + pr_debug("%s:\n", __func__); + return dvb_attach(mxl5005s_attach, adap->fe[0], + &adap_to_d(adap)->i2c_adap, + &ec168_mxl5003s_config) == NULL ? -ENODEV : 0; +} + +static int ec168_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL}; + pr_debug("%s: onoff=%d\n", __func__, onoff); + if (onoff) + req.index = 0x0102; + return ec168_ctrl_msg(fe_to_d(fe), &req); +} + +/* DVB USB Driver stuff */ +/* bInterfaceNumber 0 is HID + * bInterfaceNumber 1 is DVB-T */ +static struct dvb_usb_device_properties ec168_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .bInterfaceNumber = 1, + + .identify_state = ec168_identify_state, + .firmware = "dvb-usb-ec168.fw", + .download_firmware = ec168_download_firmware, + + .i2c_algo = &ec168_i2c_algo, + .frontend_attach = ec168_ec100_frontend_attach, + .tuner_attach = ec168_mxl5003s_tuner_attach, + .streaming_ctrl = ec168_streaming_ctrl, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x82, 6, 32 * 512), + } + }, +}; + +static const struct dvb_usb_driver_info ec168_driver_info = { + .name = "E3C EC168 reference design", + .props = &ec168_props, +}; + +static const struct usb_device_id ec168_id[] = { + { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168), + .driver_info = (kernel_ulong_t) &ec168_driver_info }, + { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2), + .driver_info = (kernel_ulong_t) &ec168_driver_info }, + { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3), + .driver_info = (kernel_ulong_t) &ec168_driver_info }, + { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4), + .driver_info = (kernel_ulong_t) &ec168_driver_info }, + { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5), + .driver_info = (kernel_ulong_t) &ec168_driver_info }, + {} +}; +MODULE_DEVICE_TABLE(usb, ec168_id); + +static struct usb_driver ec168_driver = { + .name = KBUILD_MODNAME, + .id_table = ec168_id, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(ec168_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("E3C EC168 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/ec168.h b/drivers/media/usb/dvb-usb-v2/ec168.h new file mode 100644 index 000000000000..9181236f6ebc --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/ec168.h @@ -0,0 +1,63 @@ +/* + * E3C EC168 DVB USB 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef EC168_H +#define EC168_H + +#include "dvb_usb.h" + +#define ec168_debug_dump(r, t, v, i, b, l) { \ + char *direction; \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + direction = ">>>"; \ + else \ + direction = "<<<"; \ + pr_debug("%s: %02x %02x %02x %02x %02x %02x %02x %02x %s\n", \ + __func__, t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, \ + l & 0xff, l >> 8, direction); \ +} + +#define EC168_USB_TIMEOUT 1000 + +struct ec168_req { + u8 cmd; /* [1] */ + u16 value; /* [2|3] */ + u16 index; /* [4|5] */ + u16 size; /* [6|7] */ + u8 *data; +}; + +enum ec168_cmd { + DOWNLOAD_FIRMWARE = 0x00, + CONFIG = 0x01, + DEMOD_RW = 0x03, + GPIO = 0x04, + STREAMING_CTRL = 0x10, + READ_I2C = 0x20, + WRITE_I2C = 0x21, + HID_DOWNLOAD = 0x30, + GET_CONFIG, + SET_CONFIG, + READ_DEMOD, + WRITE_DEMOD, +}; + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c new file mode 100644 index 000000000000..cf29f43e3598 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -0,0 +1,175 @@ +/* DVB USB compliant linux driver for GL861 USB2.0 devices. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "gl861.h" + +#include "zl10353.h" +#include "qt1010.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index; + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 req, type; + + if (wo) { + req = GL861_REQ_I2C_WRITE; + type = GL861_WRITE; + } else { /* rw */ + req = GL861_REQ_I2C_READ; + type = GL861_READ; + } + + switch (wlen) { + case 1: + index = wbuf[0]; + break; + case 2: + index = wbuf[0]; + value = value + wbuf[1]; + break; + default: + pr_err("%s: wlen=%d, aborting\n", KBUILD_MODNAME, wlen); + return -EINVAL; + } + + msleep(1); /* avoid I2C errors */ + + return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, + value, index, rbuf, rlen, 2000); +} + +/* I2C */ +static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) + break; + i++; + } else + if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 gl861_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm gl861_i2c_algo = { + .master_xfer = gl861_i2c_xfer, + .functionality = gl861_i2c_func, +}; + +/* Callbacks for DVB USB */ +static struct zl10353_config gl861_zl10353_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .parallel_ts = 1, +}; + +static int gl861_frontend_attach(struct dvb_usb_adapter *adap) +{ + + adap->fe[0] = dvb_attach(zl10353_attach, &gl861_zl10353_config, + &adap_to_d(adap)->i2c_adap); + if (adap->fe[0] == NULL) + return -EIO; + + return 0; +} + +static struct qt1010_config gl861_qt1010_config = { + .i2c_address = 0x62 +}; + +static int gl861_tuner_attach(struct dvb_usb_adapter *adap) +{ + return dvb_attach(qt1010_attach, + adap->fe[0], &adap_to_d(adap)->i2c_adap, + &gl861_qt1010_config) == NULL ? -ENODEV : 0; +} + +static int gl861_init(struct dvb_usb_device *d) +{ + /* + * There is 2 interfaces. Interface 0 is for TV and interface 1 is + * for HID remote controller. Interface 0 has 2 alternate settings. + * For some reason we need to set interface explicitly, defaulted + * as alternate setting 1? + */ + return usb_set_interface(d->udev, 0, 0); +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties gl861_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + + .i2c_algo = &gl861_i2c_algo, + .frontend_attach = gl861_frontend_attach, + .tuner_attach = gl861_tuner_attach, + .init = gl861_init, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x81, 7, 512), + } + } +}; + +static const struct usb_device_id gl861_id_table[] = { + { DVB_USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801, + &gl861_props, "MSI Mega Sky 55801 DVB-T USB2.0", NULL) }, + { DVB_USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU, + &gl861_props, "A-LINK DTU DVB-T USB2.0", NULL) }, + { } +}; +MODULE_DEVICE_TABLE(usb, gl861_id_table); + +static struct usb_driver gl861_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = gl861_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(gl861_usb_driver); + +MODULE_AUTHOR("Carl Lundqvist "); +MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/gl861.h b/drivers/media/usb/dvb-usb-v2/gl861.h new file mode 100644 index 000000000000..b0b80d87bb7e --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/gl861.h @@ -0,0 +1,12 @@ +#ifndef _DVB_USB_GL861_H_ +#define _DVB_USB_GL861_H_ + +#include "dvb_usb.h" + +#define GL861_WRITE 0x40 +#define GL861_READ 0xc0 + +#define GL861_REQ_I2C_WRITE 0x01 +#define GL861_REQ_I2C_READ 0x02 + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c new file mode 100644 index 000000000000..695f9106bc54 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -0,0 +1,799 @@ +/* + * DVB USB compliant linux driver for ITE IT9135 and IT9137 + * + * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) + * IT9135 (C) ITE Tech Inc. + * IT9137 (C) ITE Tech 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. + * + * 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. + * + * + * see Documentation/dvb/README.dvb-usb for more information + * see Documentation/dvb/it9137.txt for firmware information + * + */ +#define DVB_USB_LOG_PREFIX "it913x" + +#include +#include +#include + +#include "dvb_usb.h" +#include "it913x-fe.h" + +/* debug */ +static int dvb_usb_it913x_debug; +#define it_debug(var, level, args...) \ + do { if ((var & level)) pr_debug(DVB_USB_LOG_PREFIX": " args); \ +} while (0) +#define deb_info(level, args...) it_debug(dvb_usb_it913x_debug, level, args) +#define info(args...) pr_info(DVB_USB_LOG_PREFIX": " args) + +module_param_named(debug, dvb_usb_it913x_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +static int dvb_usb_it913x_firmware; +module_param_named(firmware, dvb_usb_it913x_firmware, int, 0644); +MODULE_PARM_DESC(firmware, "set firmware 0=auto"\ + "1=IT9137 2=IT9135 V1 3=IT9135 V2"); +#define FW_IT9137 "dvb-usb-it9137-01.fw" +#define FW_IT9135_V1 "dvb-usb-it9135-01.fw" +#define FW_IT9135_V2 "dvb-usb-it9135-02.fw" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct it913x_state { + struct ite_config it913x_config; + u8 pid_filter_onoff; + bool proprietary_ir; + int cmd_counter; +}; + +static u16 check_sum(u8 *p, u8 len) +{ + u16 sum = 0; + u8 i = 1; + while (i < len) + sum += (i++ & 1) ? (*p++) << 8 : *p++; + return ~sum; +} + +static int it913x_io(struct dvb_usb_device *d, u8 mode, u8 pro, + u8 cmd, u32 reg, u8 addr, u8 *data, u8 len) +{ + struct it913x_state *st = d->priv; + int ret = 0, i, buf_size = 1; + u8 *buff; + u8 rlen; + u16 chk_sum; + + buff = kzalloc(256, GFP_KERNEL); + if (!buff) { + info("USB Buffer Failed"); + return -ENOMEM; + } + + buff[buf_size++] = pro; + buff[buf_size++] = cmd; + buff[buf_size++] = st->cmd_counter; + + switch (mode) { + case READ_LONG: + case WRITE_LONG: + buff[buf_size++] = len; + buff[buf_size++] = 2; + buff[buf_size++] = (reg >> 24); + buff[buf_size++] = (reg >> 16) & 0xff; + buff[buf_size++] = (reg >> 8) & 0xff; + buff[buf_size++] = reg & 0xff; + break; + case READ_SHORT: + buff[buf_size++] = addr; + break; + case WRITE_SHORT: + buff[buf_size++] = len; + buff[buf_size++] = addr; + buff[buf_size++] = (reg >> 8) & 0xff; + buff[buf_size++] = reg & 0xff; + break; + case READ_DATA: + case WRITE_DATA: + break; + case WRITE_CMD: + mode = 7; + break; + default: + kfree(buff); + return -EINVAL; + } + + if (mode & 1) { + for (i = 0; i < len ; i++) + buff[buf_size++] = data[i]; + } + chk_sum = check_sum(&buff[1], buf_size); + + buff[buf_size++] = chk_sum >> 8; + buff[0] = buf_size; + buff[buf_size++] = (chk_sum & 0xff); + + ret = dvb_usbv2_generic_rw(d, buff, buf_size, buff, (mode & 1) ? + 5 : len + 5); + if (ret < 0) + goto error; + + rlen = (mode & 0x1) ? 0x1 : len; + + if (mode & 1) + ret = buff[2]; + else + memcpy(data, &buff[3], rlen); + + st->cmd_counter++; + +error: kfree(buff); + + return ret; +} + +static int it913x_wr_reg(struct dvb_usb_device *d, u8 pro, u32 reg , u8 data) +{ + int ret; + u8 b[1]; + b[0] = data; + ret = it913x_io(d, WRITE_LONG, pro, + CMD_DEMOD_WRITE, reg, 0, b, sizeof(b)); + + return ret; +} + +static int it913x_read_reg(struct dvb_usb_device *d, u32 reg) +{ + int ret; + u8 data[1]; + + ret = it913x_io(d, READ_LONG, DEV_0, + CMD_DEMOD_READ, reg, 0, &data[0], sizeof(data)); + + return (ret < 0) ? ret : data[0]; +} + +static int it913x_query(struct dvb_usb_device *d, u8 pro) +{ + struct it913x_state *st = d->priv; + int ret, i; + u8 data[4]; + u8 ver; + + for (i = 0; i < 5; i++) { + ret = it913x_io(d, READ_LONG, pro, CMD_DEMOD_READ, + 0x1222, 0, &data[0], 3); + ver = data[0]; + if (ver > 0 && ver < 3) + break; + msleep(100); + } + + if (ver < 1 || ver > 2) { + info("Failed to identify chip version applying 1"); + st->it913x_config.chip_ver = 0x1; + st->it913x_config.chip_type = 0x9135; + return 0; + } + + st->it913x_config.chip_ver = ver; + st->it913x_config.chip_type = (u16)(data[2] << 8) + data[1]; + + info("Chip Version=%02x Chip Type=%04x", st->it913x_config.chip_ver, + st->it913x_config.chip_type); + + ret = it913x_io(d, READ_SHORT, pro, + CMD_QUERYINFO, 0, 0x1, &data[0], 4); + + st->it913x_config.firmware = (data[0] << 24) | (data[1] << 16) | + (data[2] << 8) | data[3]; + + return ret; +} + +static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct it913x_state *st = adap_to_priv(adap); + int ret; + u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; + + mutex_lock(&d->i2c_mutex); + + deb_info(1, "PID_C (%02x)", onoff); + + ret = it913x_wr_reg(d, pro, PID_EN, st->pid_filter_onoff); + + mutex_unlock(&d->i2c_mutex); + return ret; +} + +static int it913x_pid_filter(struct dvb_usb_adapter *adap, + int index, u16 pid, int onoff) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct it913x_state *st = adap_to_priv(adap); + int ret; + u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; + + mutex_lock(&d->i2c_mutex); + + deb_info(1, "PID_F (%02x)", onoff); + + ret = it913x_wr_reg(d, pro, PID_LSB, (u8)(pid & 0xff)); + + ret |= it913x_wr_reg(d, pro, PID_MSB, (u8)(pid >> 8)); + + ret |= it913x_wr_reg(d, pro, PID_INX_EN, (u8)onoff); + + ret |= it913x_wr_reg(d, pro, PID_INX, (u8)(index & 0x1f)); + + if (d->udev->speed == USB_SPEED_HIGH && pid == 0x2000) { + ret |= it913x_wr_reg(d , pro, PID_EN, !onoff); + st->pid_filter_onoff = !onoff; + } else + st->pid_filter_onoff = + adap->pid_filtering; + + mutex_unlock(&d->i2c_mutex); + return 0; +} + + +static int it913x_return_status(struct dvb_usb_device *d) +{ + struct it913x_state *st = d->priv; + int ret = it913x_query(d, DEV_0); + if (st->it913x_config.firmware > 0) + info("Firmware Version %d", st->it913x_config.firmware); + + return ret; +} + +static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + static u8 data[256]; + int ret; + u32 reg; + u8 pro; + + mutex_lock(&d->i2c_mutex); + + deb_info(2, "num of messages %d address %02x", num, msg[0].addr); + + pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0; + pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0; + memcpy(data, msg[0].buf, msg[0].len); + reg = (data[0] << 24) + (data[1] << 16) + + (data[2] << 8) + data[3]; + if (num == 2) { + ret = it913x_io(d, READ_LONG, pro, + CMD_DEMOD_READ, reg, 0, data, msg[1].len); + memcpy(msg[1].buf, data, msg[1].len); + } else + ret = it913x_io(d, WRITE_LONG, pro, CMD_DEMOD_WRITE, + reg, 0, &data[4], msg[0].len - 4); + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 it913x_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm it913x_i2c_algo = { + .master_xfer = it913x_i2c_xfer, + .functionality = it913x_i2c_func, +}; + +/* Callbacks for DVB USB */ +#define IT913X_POLL 250 +static int it913x_rc_query(struct dvb_usb_device *d) +{ + u8 ibuf[4]; + int ret; + u32 key; + /* Avoid conflict with frontends*/ + mutex_lock(&d->i2c_mutex); + + ret = it913x_io(d, READ_LONG, PRO_LINK, CMD_IR_GET, + 0, 0, &ibuf[0], sizeof(ibuf)); + + if ((ibuf[2] + ibuf[3]) == 0xff) { + key = ibuf[2]; + key += ibuf[0] << 16; + key += ibuf[1] << 8; + deb_info(1, "NEC Extended Key =%08x", key); + if (d->rc_dev != NULL) + rc_keydown(d->rc_dev, key, 0); + } + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +/* Firmware sets raw */ +static const char fw_it9135_v1[] = FW_IT9135_V1; +static const char fw_it9135_v2[] = FW_IT9135_V2; +static const char fw_it9137[] = FW_IT9137; + +static void ite_get_firmware_name(struct dvb_usb_device *d, + const char **name) +{ + struct it913x_state *st = d->priv; + int sw; + /* auto switch */ + if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_KWORLD_2) + sw = IT9137_FW; + else if (st->it913x_config.chip_ver == 1) + sw = IT9135_V1_FW; + else + sw = IT9135_V2_FW; + + /* force switch */ + if (dvb_usb_it913x_firmware != IT9135_AUTO) + sw = dvb_usb_it913x_firmware; + + switch (sw) { + case IT9135_V1_FW: + st->it913x_config.firmware_ver = 1; + st->it913x_config.adc_x2 = 1; + st->it913x_config.read_slevel = false; + *name = fw_it9135_v1; + break; + case IT9135_V2_FW: + st->it913x_config.firmware_ver = 1; + st->it913x_config.adc_x2 = 1; + st->it913x_config.read_slevel = false; + *name = fw_it9135_v2; + switch (st->it913x_config.tuner_id_0) { + case IT9135_61: + case IT9135_62: + break; + default: + info("Unknown tuner ID applying default 0x60"); + case IT9135_60: + st->it913x_config.tuner_id_0 = IT9135_60; + } + break; + case IT9137_FW: + default: + st->it913x_config.firmware_ver = 0; + st->it913x_config.adc_x2 = 0; + st->it913x_config.read_slevel = true; + *name = fw_it9137; + } + + return; +} + +#define TS_MPEG_PKT_SIZE 188 +#define EP_LOW 21 +#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE) +#define EP_HIGH 348 +#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE) + +static int it913x_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, + struct usb_data_stream_properties *stream) +{ + struct dvb_usb_adapter *adap = fe_to_adap(fe); + if (adap->pid_filtering) + stream->u.bulk.buffersize = TS_BUFFER_SIZE_PID; + else + stream->u.bulk.buffersize = TS_BUFFER_SIZE_MAX; + + return 0; +} + +static int it913x_select_config(struct dvb_usb_device *d) +{ + struct it913x_state *st = d->priv; + int ret, reg; + + ret = it913x_return_status(d); + if (ret < 0) + return ret; + + if (st->it913x_config.chip_ver == 0x02 + && st->it913x_config.chip_type == 0x9135) + reg = it913x_read_reg(d, 0x461d); + else + reg = it913x_read_reg(d, 0x461b); + + if (reg < 0) + return reg; + + if (reg == 0) { + st->it913x_config.dual_mode = 0; + st->it913x_config.tuner_id_0 = IT9135_38; + st->proprietary_ir = true; + } else { + /* TS mode */ + reg = it913x_read_reg(d, 0x49c5); + if (reg < 0) + return reg; + st->it913x_config.dual_mode = reg; + + /* IR mode type */ + reg = it913x_read_reg(d, 0x49ac); + if (reg < 0) + return reg; + if (reg == 5) { + info("Remote propriety (raw) mode"); + st->proprietary_ir = true; + } else if (reg == 1) { + info("Remote HID mode NOT SUPPORTED"); + st->proprietary_ir = false; + } + + /* Tuner_id */ + reg = it913x_read_reg(d, 0x49d0); + if (reg < 0) + return reg; + st->it913x_config.tuner_id_0 = reg; + } + + info("Dual mode=%x Tuner Type=%x", st->it913x_config.dual_mode, + st->it913x_config.tuner_id_0); + + return ret; +} + +static int it913x_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_adapter *adap = fe_to_adap(fe); + struct dvb_usb_device *d = adap_to_d(adap); + struct it913x_state *st = fe_to_priv(fe); + int ret = 0; + u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; + + deb_info(1, "STM (%02x)", onoff); + + if (!onoff) { + mutex_lock(&d->i2c_mutex); + + ret = it913x_wr_reg(d, pro, PID_RST, 0x1); + + mutex_unlock(&d->i2c_mutex); + st->pid_filter_onoff = + adap->pid_filtering; + + } + + return ret; +} + +static int it913x_identify_state(struct dvb_usb_device *d, const char **name) +{ + struct it913x_state *st = d->priv; + int ret; + u8 reg; + + /* Read and select config */ + ret = it913x_select_config(d); + if (ret < 0) + return ret; + + ite_get_firmware_name(d, name); + + if (st->it913x_config.firmware > 0) + return WARM; + + if (st->it913x_config.dual_mode) { + st->it913x_config.tuner_id_1 = it913x_read_reg(d, 0x49e0); + ret = it913x_wr_reg(d, DEV_0, GPIOH1_EN, 0x1); + ret |= it913x_wr_reg(d, DEV_0, GPIOH1_ON, 0x1); + ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x1); + msleep(50); + ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x0); + msleep(50); + reg = it913x_read_reg(d, GPIOH1_O); + if (reg == 0) { + ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x1); + ret |= it913x_return_status(d); + if (ret != 0) + ret = it913x_wr_reg(d, DEV_0, + GPIOH1_O, 0x0); + } + } + + reg = it913x_read_reg(d, IO_MUX_POWER_CLK); + + if (st->it913x_config.dual_mode) { + ret |= it913x_wr_reg(d, DEV_0, 0x4bfb, CHIP2_I2C_ADDR); + if (st->it913x_config.firmware_ver == 1) + ret |= it913x_wr_reg(d, DEV_0, 0xcfff, 0x1); + else + ret |= it913x_wr_reg(d, DEV_0, CLK_O_EN, 0x1); + } else { + ret |= it913x_wr_reg(d, DEV_0, 0x4bfb, 0x0); + if (st->it913x_config.firmware_ver == 1) + ret |= it913x_wr_reg(d, DEV_0, 0xcfff, 0x0); + else + ret |= it913x_wr_reg(d, DEV_0, CLK_O_EN, 0x0); + } + + ret |= it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_100); + + return (ret < 0) ? ret : COLD; +} + +static int it913x_download_firmware(struct dvb_usb_device *d, + const struct firmware *fw) +{ + struct it913x_state *st = d->priv; + int ret = 0, i = 0, pos = 0; + u8 packet_size, min_pkt; + u8 *fw_data; + + ret = it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_100); + + info("FRM Starting Firmware Download"); + + /* Multi firmware loader */ + /* This uses scatter write firmware headers */ + /* The firmware must start with 03 XX 00 */ + /* and be the extact firmware length */ + + if (st->it913x_config.chip_ver == 2) + min_pkt = 0x11; + else + min_pkt = 0x19; + + while (i <= fw->size) { + if (((fw->data[i] == 0x3) && (fw->data[i + 2] == 0x0)) + || (i == fw->size)) { + packet_size = i - pos; + if ((packet_size > min_pkt) || (i == fw->size)) { + fw_data = (u8 *)(fw->data + pos); + pos += packet_size; + if (packet_size > 0) { + ret = it913x_io(d, WRITE_DATA, + DEV_0, CMD_SCATTER_WRITE, 0, + 0, fw_data, packet_size); + if (ret < 0) + break; + } + udelay(1000); + } + } + i++; + } + + if (ret < 0) + info("FRM Firmware Download Failed (%d)" , ret); + else + info("FRM Firmware Download Completed - Resetting Device"); + + msleep(30); + + ret = it913x_io(d, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0); + if (ret < 0) + info("FRM Device not responding to reboot"); + + ret = it913x_return_status(d); + if (st->it913x_config.firmware == 0) { + info("FRM Failed to reboot device"); + return -ENODEV; + } + + msleep(30); + + ret = it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_400); + + msleep(30); + + /* Tuner function */ + if (st->it913x_config.dual_mode) + ret |= it913x_wr_reg(d, DEV_0_DMOD , 0xec4c, 0xa0); + else + ret |= it913x_wr_reg(d, DEV_0_DMOD , 0xec4c, 0x68); + + if ((st->it913x_config.chip_ver == 1) && + (st->it913x_config.chip_type == 0x9135)) { + ret |= it913x_wr_reg(d, DEV_0, PADODPU, 0x0); + ret |= it913x_wr_reg(d, DEV_0, AGC_O_D, 0x0); + if (st->it913x_config.dual_mode) { + ret |= it913x_wr_reg(d, DEV_1, PADODPU, 0x0); + ret |= it913x_wr_reg(d, DEV_1, AGC_O_D, 0x0); + } + } + + return (ret < 0) ? -ENODEV : 0; +} + +static int it913x_name(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + const char *desc = d->name; + char *fe_name[] = {"_1", "_2", "_3", "_4"}; + char *name = adap->fe[0]->ops.info.name; + + strlcpy(name, desc, 128); + strlcat(name, fe_name[adap->id], 128); + + return 0; +} + +static int it913x_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct it913x_state *st = d->priv; + int ret = 0; + u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); + u16 ep_size = adap->stream.buf_size / 4; + u8 pkt_size = 0x80; + + if (d->udev->speed != USB_SPEED_HIGH) + pkt_size = 0x10; + + st->it913x_config.adf = it913x_read_reg(d, IO_MUX_POWER_CLK); + + adap->fe[0] = dvb_attach(it913x_fe_attach, + &d->i2c_adap, adap_addr, &st->it913x_config); + + if (adap->id == 0 && adap->fe[0]) { + it913x_wr_reg(d, DEV_0_DMOD, MP2_SW_RST, 0x1); + it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_SW_RST, 0x1); + it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x0f); + it913x_wr_reg(d, DEV_0, EP0_TX_NAK, 0x1b); + it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x2f); + it913x_wr_reg(d, DEV_0, EP4_TX_LEN_LSB, + ep_size & 0xff); + it913x_wr_reg(d, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8); + ret = it913x_wr_reg(d, DEV_0, EP4_MAX_PKT, pkt_size); + } else if (adap->id == 1 && adap->fe[0]) { + it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x6f); + it913x_wr_reg(d, DEV_0, EP5_TX_LEN_LSB, + ep_size & 0xff); + it913x_wr_reg(d, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8); + it913x_wr_reg(d, DEV_0, EP5_MAX_PKT, pkt_size); + it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_EN, 0x1); + it913x_wr_reg(d, DEV_1_DMOD, MP2IF_SERIAL, 0x1); + it913x_wr_reg(d, DEV_1, TOP_HOSTB_SER_MODE, 0x1); + it913x_wr_reg(d, DEV_0_DMOD, TSIS_ENABLE, 0x1); + it913x_wr_reg(d, DEV_0_DMOD, MP2_SW_RST, 0x0); + it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_SW_RST, 0x0); + it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0); + it913x_wr_reg(d, DEV_0_DMOD, MP2IF_STOP_EN, 0x1); + it913x_wr_reg(d, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0); + ret = it913x_wr_reg(d, DEV_1_DMOD, MP2IF_STOP_EN, 0x0); + } else + return -ENODEV; + + ret |= it913x_name(adap); + + return ret; +} + +/* DVB USB Driver */ +static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + struct it913x_state *st = d->priv; + + if (st->proprietary_ir == false) { + rc->map_name = NULL; + return 0; + } + + rc->allowed_protos = RC_TYPE_NEC; + rc->query = it913x_rc_query; + rc->interval = 250; + + return 0; +} + +static int it913x_get_adapter_count(struct dvb_usb_device *d) +{ + struct it913x_state *st = d->priv; + if (st->it913x_config.dual_mode) + return 2; + return 1; +} + +static struct dvb_usb_device_properties it913x_properties = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .bInterfaceNumber = 0, + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct it913x_state), + + .identify_state = it913x_identify_state, + .i2c_algo = &it913x_i2c_algo, + + .download_firmware = it913x_download_firmware, + + .frontend_attach = it913x_frontend_attach, + .get_rc_config = it913x_get_rc_config, + .get_stream_config = it913x_get_stream_config, + .get_adapter_count = it913x_get_adapter_count, + .streaming_ctrl = it913x_streaming_ctrl, + + + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER| + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = it913x_pid_filter, + .pid_filter_ctrl = it913x_pid_filter_ctrl, + .stream = + DVB_USB_STREAM_BULK(0x84, 10, TS_BUFFER_SIZE_MAX), + }, + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER| + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = it913x_pid_filter, + .pid_filter_ctrl = it913x_pid_filter_ctrl, + .stream = + DVB_USB_STREAM_BULK(0x85, 10, TS_BUFFER_SIZE_MAX), + } + } +}; + +static const struct usb_device_id it913x_id_table[] = { + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09, + &it913x_properties, "Kworld UB499-2T T09(IT9137)", + RC_MAP_IT913X_V1) }, + { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135, + &it913x_properties, "ITE 9135 Generic", + RC_MAP_IT913X_V1) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22_IT9137, + &it913x_properties, "Sveon STV22 Dual DVB-T HDTV(IT9137)", + RC_MAP_IT913X_V1) }, + { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9005, + &it913x_properties, "ITE 9135(9005) Generic", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006, + &it913x_properties, "ITE 9135(9006) Generic", + RC_MAP_IT913X_V1) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, it913x_id_table); + +static struct usb_driver it913x_driver = { + .name = KBUILD_MODNAME, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .id_table = it913x_id_table, +}; + +module_usb_driver(it913x_driver); + +MODULE_AUTHOR("Malcolm Priestley "); +MODULE_DESCRIPTION("it913x USB 2 Driver"); +MODULE_VERSION("1.32"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FW_IT9135_V1); +MODULE_FIRMWARE(FW_IT9135_V2); +MODULE_FIRMWARE(FW_IT9137); + diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c new file mode 100644 index 000000000000..c41d9d9ec7b5 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -0,0 +1,1369 @@ +/* DVB USB compliant linux driver for + * + * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 + * LME2510C + LG TDQY-P001F + * LME2510C + BS2F7HZ0194 + * LME2510 + LG TDQY-P001F + * LME2510 + BS2F7HZ0194 + * + * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) + * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) + * + * MV001F (LME2510+LGTDQY-P001F) + * LG TDQY - P001F =(TDA8263 + TDA10086H) + * + * MVB0001F (LME2510C+LGTDQT-P001F) + * + * MV0194 (LME2510+SHARP:BS2F7HZ0194) + * SHARP:BS2F7HZ0194 = (STV0299+IX2410) + * + * MVB0194 (LME2510C+SHARP0194) + * + * LME2510C + M88RS2000 + * + * For firmware see Documentation/dvb/lmedm04.txt + * + * I2C addresses: + * 0xd0 - STV0288 - Demodulator + * 0xc0 - Sharp IX2505V - Tuner + * -- + * 0x1c - TDA10086 - Demodulator + * 0xc0 - TDA8263 - Tuner + * -- + * 0xd0 - STV0299 - Demodulator + * 0xc0 - IX2410 - Tuner + * + * + * VID = 3344 PID LME2510=1122 LME2510C=1120 + * + * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com) + * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * see Documentation/dvb/README.dvb-usb for more information + * + * Known Issues : + * LME2510: Non Intel USB chipsets fail to maintain High Speed on + * Boot or Hot Plug. + * + * QQbox suffers from noise on LNB voltage. + * + * LME2510: SHARP:BS2F7HZ0194(MV0194) cannot cold reset and share system + * with other tuners. After a cold reset streaming will not start. + * + * M88RS2000 suffers from loss of lock. + */ +#define DVB_USB_LOG_PREFIX "LME2510(C)" +#include +#include +#include + +#include "dvb_usb.h" +#include "lmedm04.h" +#include "tda826x.h" +#include "tda10086.h" +#include "stv0288.h" +#include "ix2505v.h" +#include "stv0299.h" +#include "dvb-pll.h" +#include "z0194a.h" +#include "m88rs2000.h" + + +#define LME2510_C_S7395 "dvb-usb-lme2510c-s7395.fw"; +#define LME2510_C_LG "dvb-usb-lme2510c-lg.fw"; +#define LME2510_C_S0194 "dvb-usb-lme2510c-s0194.fw"; +#define LME2510_C_RS2000 "dvb-usb-lme2510c-rs2000.fw"; +#define LME2510_LG "dvb-usb-lme2510-lg.fw"; +#define LME2510_S0194 "dvb-usb-lme2510-s0194.fw"; + +/* debug */ +static int dvb_usb_lme2510_debug; +#define lme_debug(var, level, args...) do { \ + if ((var >= level)) \ + pr_debug(DVB_USB_LOG_PREFIX": " args); \ +} while (0) +#define deb_info(level, args...) lme_debug(dvb_usb_lme2510_debug, level, args) +#define debug_data_snipet(level, name, p) \ + deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ + *p, *(p+1), *(p+2), *(p+3), *(p+4), \ + *(p+5), *(p+6), *(p+7)); +#define info(args...) pr_info(DVB_USB_LOG_PREFIX": "args) + +module_param_named(debug, dvb_usb_lme2510_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +static int dvb_usb_lme2510_firmware; +module_param_named(firmware, dvb_usb_lme2510_firmware, int, 0644); +MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG"); + +static int pid_filter; +module_param_named(pid, pid_filter, int, 0644); +MODULE_PARM_DESC(pid, "set default 0=default 1=off 2=on"); + + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define TUNER_DEFAULT 0x0 +#define TUNER_LG 0x1 +#define TUNER_S7395 0x2 +#define TUNER_S0194 0x3 +#define TUNER_RS2000 0x4 + +struct lme2510_state { + u8 id; + u8 tuner_config; + u8 signal_lock; + u8 signal_level; + u8 signal_sn; + u8 time_key; + u8 last_key; + u8 key_timeout; + u8 i2c_talk_onoff; + u8 i2c_gate; + u8 i2c_tuner_gate_w; + u8 i2c_tuner_gate_r; + u8 i2c_tuner_addr; + u8 stream_on; + u8 pid_size; + u8 pid_off; + void *buffer; + struct urb *lme_urb; + void *usb_buffer; + int (*fe_set_voltage)(struct dvb_frontend *, fe_sec_voltage_t); + u8 dvb_usb_lme2510_firmware; +}; + +static int lme2510_bulk_write(struct usb_device *dev, + u8 *snd, int len, u8 pipe) +{ + int ret, actual_l; + + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), + snd, len , &actual_l, 100); + return ret; +} + +static int lme2510_bulk_read(struct usb_device *dev, + u8 *rev, int len, u8 pipe) +{ + int ret, actual_l; + + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), + rev, len , &actual_l, 200); + return ret; +} + +static int lme2510_usb_talk(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct lme2510_state *st = d->priv; + u8 *buff; + int ret = 0; + + if (st->usb_buffer == NULL) { + st->usb_buffer = kmalloc(64, GFP_KERNEL); + if (st->usb_buffer == NULL) { + info("MEM Error no memory"); + return -ENOMEM; + } + } + buff = st->usb_buffer; + + ret = mutex_lock_interruptible(&d->usb_mutex); + + if (ret < 0) + return -EAGAIN; + + /* the read/write capped at 64 */ + memcpy(buff, wbuf, (wlen < 64) ? wlen : 64); + + ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01); + + ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ? + rlen : 64 , 0x01); + + if (rlen > 0) + memcpy(rbuf, buff, rlen); + + mutex_unlock(&d->usb_mutex); + + return (ret < 0) ? -ENODEV : 0; +} + +static int lme2510_stream_restart(struct dvb_usb_device *d) +{ + struct lme2510_state *st = d->priv; + u8 all_pids[] = LME_ALL_PIDS; + u8 stream_on[] = LME_ST_ON_W; + int ret; + u8 rbuff[1]; + if (st->pid_off) + ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids), + rbuff, sizeof(rbuff)); + /*Restart Stream Command*/ + ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on), + rbuff, sizeof(rbuff)); + return ret; +} + +static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out) +{ + struct lme2510_state *st = d->priv; + static u8 pid_buff[] = LME_ZERO_PID; + static u8 rbuf[1]; + u8 pid_no = index * 2; + u8 pid_len = pid_no + 2; + int ret = 0; + deb_info(1, "PID Setting Pid %04x", pid_out); + + if (st->pid_size == 0) + ret |= lme2510_stream_restart(d); + + pid_buff[2] = pid_no; + pid_buff[3] = (u8)pid_out & 0xff; + pid_buff[4] = pid_no + 1; + pid_buff[5] = (u8)(pid_out >> 8); + + if (pid_len > st->pid_size) + st->pid_size = pid_len; + pid_buff[7] = 0x80 + st->pid_size; + + ret |= lme2510_usb_talk(d, pid_buff , + sizeof(pid_buff) , rbuf, sizeof(rbuf)); + + if (st->stream_on) + ret |= lme2510_stream_restart(d); + + return ret; +} + +static void lme2510_int_response(struct urb *lme_urb) +{ + struct dvb_usb_adapter *adap = lme_urb->context; + struct lme2510_state *st = adap_to_priv(adap); + static u8 *ibuf, *rbuf; + int i = 0, offset; + u32 key; + + switch (lme_urb->status) { + case 0: + case -ETIMEDOUT: + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: + info("Error %x", lme_urb->status); + break; + } + + rbuf = (u8 *) lme_urb->transfer_buffer; + + offset = ((lme_urb->actual_length/8) > 4) + ? 4 : (lme_urb->actual_length/8) ; + + for (i = 0; i < offset; ++i) { + ibuf = (u8 *)&rbuf[i*8]; + deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x", + offset, i, ibuf[0], ibuf[1]); + + switch (ibuf[0]) { + case 0xaa: + debug_data_snipet(1, "INT Remote data snipet", ibuf); + if ((ibuf[4] + ibuf[5]) == 0xff) { + key = ibuf[5]; + key += (ibuf[3] > 0) + ? (ibuf[3] ^ 0xff) << 8 : 0; + key += (ibuf[2] ^ 0xff) << 16; + deb_info(1, "INT Key =%08x", key); + if (adap_to_d(adap)->rc_dev != NULL) + rc_keydown(adap_to_d(adap)->rc_dev, + key, 0); + } + break; + case 0xbb: + switch (st->tuner_config) { + case TUNER_LG: + if (ibuf[2] > 0) + st->signal_lock = ibuf[2]; + st->signal_level = ibuf[4]; + st->signal_sn = ibuf[3]; + st->time_key = ibuf[7]; + break; + case TUNER_S7395: + case TUNER_S0194: + /* Tweak for earlier firmware*/ + if (ibuf[1] == 0x03) { + if (ibuf[2] > 1) + st->signal_lock = ibuf[2]; + st->signal_level = ibuf[3]; + st->signal_sn = ibuf[4]; + } else { + st->signal_level = ibuf[4]; + st->signal_sn = ibuf[5]; + st->signal_lock = + (st->signal_lock & 0xf7) + + ((ibuf[2] & 0x01) << 0x03); + } + break; + case TUNER_RS2000: + if (ibuf[1] == 0x3 && ibuf[6] == 0xff) + st->signal_lock = 0xff; + else + st->signal_lock = 0x00; + st->signal_level = ibuf[5]; + st->signal_sn = ibuf[4]; + st->time_key = ibuf[7]; + default: + break; + } + debug_data_snipet(5, "INT Remote data snipet in", ibuf); + break; + case 0xcc: + debug_data_snipet(1, "INT Control data snipet", ibuf); + break; + default: + debug_data_snipet(1, "INT Unknown data snipet", ibuf); + break; + } + } + usb_submit_urb(lme_urb, GFP_ATOMIC); +} + +static int lme2510_int_read(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct lme2510_state *lme_int = adap_to_priv(adap); + + lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (lme_int->lme_urb == NULL) + return -ENOMEM; + + lme_int->buffer = usb_alloc_coherent(d->udev, 128, GFP_ATOMIC, + &lme_int->lme_urb->transfer_dma); + + if (lme_int->buffer == NULL) + return -ENOMEM; + + usb_fill_int_urb(lme_int->lme_urb, + d->udev, + usb_rcvintpipe(d->udev, 0xa), + lme_int->buffer, + 128, + lme2510_int_response, + adap, + 8); + + lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); + info("INT Interrupt Service Started"); + + return 0; +} + +static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct lme2510_state *st = adap_to_priv(adap); + static u8 clear_pid_reg[] = LME_ALL_PIDS; + static u8 rbuf[1]; + int ret = 0; + + deb_info(1, "PID Clearing Filter"); + + mutex_lock(&d->i2c_mutex); + + if (!onoff) { + ret |= lme2510_usb_talk(d, clear_pid_reg, + sizeof(clear_pid_reg), rbuf, sizeof(rbuf)); + st->pid_off = true; + } else + st->pid_off = false; + + st->pid_size = 0; + + mutex_unlock(&d->i2c_mutex); + + return 0; +} + +static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + struct dvb_usb_device *d = adap_to_d(adap); + int ret = 0; + + deb_info(3, "%s PID=%04x Index=%04x onoff=%02x", __func__, + pid, index, onoff); + + if (onoff) { + mutex_lock(&d->i2c_mutex); + ret |= lme2510_enable_pid(d, index, pid); + mutex_unlock(&d->i2c_mutex); + } + + + return ret; +} + + +static int lme2510_return_status(struct dvb_usb_device *d) +{ + int ret = 0; + u8 *data; + + data = kzalloc(10, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret |= usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200); + info("Firmware Status: %x (%x)", ret , data[2]); + + ret = (ret < 0) ? -ENODEV : data[2]; + kfree(data); + return ret; +} + +static int lme2510_msg(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int ret = 0; + struct lme2510_state *st = d->priv; + + if (st->i2c_talk_onoff == 1) { + + ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + + switch (st->tuner_config) { + case TUNER_LG: + if (wbuf[2] == 0x1c) { + if (wbuf[3] == 0x0e) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x10)) { + lme2510_stream_restart(d); + st->i2c_talk_onoff = 0; + } + msleep(80); + } + } + break; + case TUNER_S7395: + if (wbuf[2] == 0xd0) { + if (wbuf[3] == 0x24) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x8)) { + lme2510_stream_restart(d); + st->i2c_talk_onoff = 0; + } + } + } + break; + case TUNER_S0194: + if (wbuf[2] == 0xd0) { + if (wbuf[3] == 0x1b) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x8)) { + lme2510_stream_restart(d); + st->i2c_talk_onoff = 0; + } + } + } + break; + case TUNER_RS2000: + default: + break; + } + } else { + /* TODO rewrite this section */ + switch (st->tuner_config) { + case TUNER_LG: + switch (wbuf[3]) { + case 0x0e: + rbuf[0] = 0x55; + rbuf[1] = st->signal_lock; + break; + case 0x43: + rbuf[0] = 0x55; + rbuf[1] = st->signal_level; + break; + case 0x1c: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + case 0x15: + case 0x16: + case 0x17: + case 0x18: + rbuf[0] = 0x55; + rbuf[1] = 0x00; + break; + default: + lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 1; + break; + } + break; + case TUNER_S7395: + switch (wbuf[3]) { + case 0x10: + rbuf[0] = 0x55; + rbuf[1] = (st->signal_level & 0x80) + ? 0 : (st->signal_level * 2); + break; + case 0x2d: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + case 0x24: + rbuf[0] = 0x55; + rbuf[1] = st->signal_lock; + break; + case 0x2e: + case 0x26: + case 0x27: + rbuf[0] = 0x55; + rbuf[1] = 0x00; + break; + default: + lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 1; + break; + } + break; + case TUNER_S0194: + switch (wbuf[3]) { + case 0x18: + rbuf[0] = 0x55; + rbuf[1] = (st->signal_level & 0x80) + ? 0 : (st->signal_level * 2); + break; + case 0x24: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + case 0x1b: + rbuf[0] = 0x55; + rbuf[1] = st->signal_lock; + break; + case 0x19: + case 0x25: + case 0x1e: + case 0x1d: + rbuf[0] = 0x55; + rbuf[1] = 0x00; + break; + default: + lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 1; + break; + } + break; + case TUNER_RS2000: + switch (wbuf[3]) { + case 0x8c: + rbuf[0] = 0x55; + rbuf[1] = 0xff; + if (st->last_key == st->time_key) { + st->key_timeout++; + if (st->key_timeout > 5) + rbuf[1] = 0; + } else + st->key_timeout = 0; + st->last_key = st->time_key; + break; + default: + lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 1; + break; + } + default: + break; + } + + deb_info(4, "I2C From Interrupt Message out(%02x) in(%02x)", + wbuf[3], rbuf[1]); + + } + + return ret; +} + + +static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct lme2510_state *st = d->priv; + static u8 obuf[64], ibuf[64]; + int i, read, read_o; + u16 len; + u8 gate = st->i2c_gate; + + mutex_lock(&d->i2c_mutex); + + if (gate == 0) + gate = 5; + + for (i = 0; i < num; i++) { + read_o = 1 & (msg[i].flags & I2C_M_RD); + read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read |= read_o; + gate = (msg[i].addr == st->i2c_tuner_addr) + ? (read) ? st->i2c_tuner_gate_r + : st->i2c_tuner_gate_w + : st->i2c_gate; + obuf[0] = gate | (read << 7); + + if (gate == 5) + obuf[1] = (read) ? 2 : msg[i].len + 1; + else + obuf[1] = msg[i].len + read + 1; + + obuf[2] = msg[i].addr; + if (read) { + if (read_o) + len = 3; + else { + memcpy(&obuf[3], msg[i].buf, msg[i].len); + obuf[msg[i].len+3] = msg[i+1].len; + len = msg[i].len+4; + } + } else { + memcpy(&obuf[3], msg[i].buf, msg[i].len); + len = msg[i].len+3; + } + + if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) { + deb_info(1, "i2c transfer failed."); + mutex_unlock(&d->i2c_mutex); + return -EAGAIN; + } + + if (read) { + if (read_o) + memcpy(msg[i].buf, &ibuf[1], msg[i].len); + else { + memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); + i++; + } + } + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 lme2510_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm lme2510_i2c_algo = { + .master_xfer = lme2510_i2c_xfer, + .functionality = lme2510_i2c_func, +}; + +static int lme2510_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_adapter *adap = fe_to_adap(fe); + struct dvb_usb_device *d = adap_to_d(adap); + struct lme2510_state *st = adap_to_priv(adap); + static u8 clear_reg_3[] = LME_ALL_PIDS; + static u8 rbuf[1]; + int ret = 0, rlen = sizeof(rbuf); + + deb_info(1, "STM (%02x)", onoff); + + /* Streaming is started by FE_HAS_LOCK */ + if (onoff == 1) + st->stream_on = 1; + else { + deb_info(1, "STM Steam Off"); + /* mutex is here only to avoid collision with I2C */ + mutex_lock(&d->i2c_mutex); + + ret = lme2510_usb_talk(d, clear_reg_3, + sizeof(clear_reg_3), rbuf, rlen); + st->stream_on = 0; + st->i2c_talk_onoff = 1; + + mutex_unlock(&d->i2c_mutex); + } + + return (ret < 0) ? -ENODEV : 0; +} + +static u8 check_sum(u8 *p, u8 len) +{ + u8 sum = 0; + while (len--) + sum += *p++; + return sum; +} + +static int lme2510_download_firmware(struct dvb_usb_device *d, + const struct firmware *fw) +{ + int ret = 0; + u8 *data; + u16 j, wlen, len_in, start, end; + u8 packet_size, dlen, i; + u8 *fw_data; + + packet_size = 0x31; + len_in = 1; + + data = kzalloc(128, GFP_KERNEL); + if (!data) { + info("FRM Could not start Firmware Download"\ + "(Buffer allocation failed)"); + return -ENOMEM; + } + + info("FRM Starting Firmware Download"); + + for (i = 1; i < 3; i++) { + start = (i == 1) ? 0 : 512; + end = (i == 1) ? 512 : fw->size; + for (j = start; j < end; j += (packet_size+1)) { + fw_data = (u8 *)(fw->data + j); + if ((end - j) > packet_size) { + data[0] = i; + dlen = packet_size; + } else { + data[0] = i | 0x80; + dlen = (u8)(end - j)-1; + } + data[1] = dlen; + memcpy(&data[2], fw_data, dlen+1); + wlen = (u8) dlen + 4; + data[wlen-1] = check_sum(fw_data, dlen+1); + deb_info(1, "Data S=%02x:E=%02x CS= %02x", data[3], + data[dlen+2], data[dlen+3]); + lme2510_usb_talk(d, data, wlen, data, len_in); + ret |= (data[0] == 0x88) ? 0 : -1; + } + } + + data[0] = 0x8a; + len_in = 1; + msleep(2000); + lme2510_usb_talk(d, data, len_in, data, len_in); + msleep(400); + + if (ret < 0) + info("FRM Firmware Download Failed (%04x)" , ret); + else + info("FRM Firmware Download Completed - Resetting Device"); + + kfree(data); + return RECONNECTS_USB; +} + +static void lme_coldreset(struct dvb_usb_device *d) +{ + u8 data[1] = {0}; + data[0] = 0x0a; + info("FRM Firmware Cold Reset"); + + lme2510_usb_talk(d, data, sizeof(data), data, sizeof(data)); + + return; +} + +static const char fw_c_s7395[] = LME2510_C_S7395; +static const char fw_c_lg[] = LME2510_C_LG; +static const char fw_c_s0194[] = LME2510_C_S0194; +static const char fw_c_rs2000[] = LME2510_C_RS2000; +static const char fw_lg[] = LME2510_LG; +static const char fw_s0194[] = LME2510_S0194; + +const char *lme_firmware_switch(struct dvb_usb_device *d, int cold) +{ + struct lme2510_state *st = d->priv; + struct usb_device *udev = d->udev; + const struct firmware *fw = NULL; + const char *fw_lme; + int ret = 0; + + cold = (cold > 0) ? (cold & 1) : 0; + + switch (le16_to_cpu(udev->descriptor.idProduct)) { + case 0x1122: + switch (st->dvb_usb_lme2510_firmware) { + default: + st->dvb_usb_lme2510_firmware = TUNER_S0194; + case TUNER_S0194: + fw_lme = fw_s0194; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) { + cold = 0; + break; + } + st->dvb_usb_lme2510_firmware = TUNER_LG; + case TUNER_LG: + fw_lme = fw_lg; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) + break; + st->dvb_usb_lme2510_firmware = TUNER_DEFAULT; + break; + } + break; + case 0x1120: + switch (st->dvb_usb_lme2510_firmware) { + default: + st->dvb_usb_lme2510_firmware = TUNER_S7395; + case TUNER_S7395: + fw_lme = fw_c_s7395; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) { + cold = 0; + break; + } + st->dvb_usb_lme2510_firmware = TUNER_LG; + case TUNER_LG: + fw_lme = fw_c_lg; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) + break; + st->dvb_usb_lme2510_firmware = TUNER_S0194; + case TUNER_S0194: + fw_lme = fw_c_s0194; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) + break; + st->dvb_usb_lme2510_firmware = TUNER_DEFAULT; + cold = 0; + break; + } + break; + case 0x22f0: + fw_lme = fw_c_rs2000; + st->dvb_usb_lme2510_firmware = TUNER_RS2000; + break; + default: + fw_lme = fw_c_s7395; + } + + release_firmware(fw); + + if (cold) { + dvb_usb_lme2510_firmware = st->dvb_usb_lme2510_firmware; + info("FRM Changing to %s firmware", fw_lme); + lme_coldreset(d); + return NULL; + } + + return fw_lme; +} + +static int lme2510_kill_urb(struct usb_data_stream *stream) +{ + int i; + + for (i = 0; i < stream->urbs_submitted; i++) { + deb_info(3, "killing URB no. %d.", i); + /* stop the URB */ + usb_kill_urb(stream->urb_list[i]); + } + stream->urbs_submitted = 0; + + return 0; +} + +static struct tda10086_config tda10086_config = { + .demod_address = 0x1c, + .invert = 0, + .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static struct stv0288_config lme_config = { + .demod_address = 0xd0, + .min_delay_ms = 15, + .inittab = s7395_inittab, +}; + +static struct ix2505v_config lme_tuner = { + .tuner_address = 0xc0, + .min_delay_ms = 100, + .tuner_gain = 0x0, + .tuner_chargepump = 0x3, +}; + +static struct stv0299_config sharp_z0194_config = { + .demod_address = 0xd0, + .inittab = sharp_z0194a_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = sharp_z0194a_set_symbol_rate, +}; + +static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, + int caller) +{ + struct dvb_usb_adapter *adap = fe_to_adap(fe); + struct dvb_usb_device *d = adap_to_d(adap); + struct lme2510_state *st = d->priv; + + mutex_lock(&d->i2c_mutex); + if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) { + st->i2c_talk_onoff = 0; + lme2510_stream_restart(d); + } + mutex_unlock(&d->i2c_mutex); + + return 0; +} + +static struct m88rs2000_config m88rs2000_config = { + .demod_addr = 0xd0, + .tuner_addr = 0xc0, + .set_ts_params = dm04_rs2000_set_ts_param, +}; + +static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct dvb_usb_device *d = fe_to_d(fe); + struct lme2510_state *st = fe_to_priv(fe); + static u8 voltage_low[] = LME_VOLTAGE_L; + static u8 voltage_high[] = LME_VOLTAGE_H; + static u8 rbuf[1]; + int ret = 0, len = 3, rlen = 1; + + mutex_lock(&d->i2c_mutex); + + switch (voltage) { + case SEC_VOLTAGE_18: + ret |= lme2510_usb_talk(d, + voltage_high, len, rbuf, rlen); + break; + + case SEC_VOLTAGE_OFF: + case SEC_VOLTAGE_13: + default: + ret |= lme2510_usb_talk(d, + voltage_low, len, rbuf, rlen); + break; + } + + mutex_unlock(&d->i2c_mutex); + + if (st->tuner_config == TUNER_RS2000) + if (st->fe_set_voltage) + st->fe_set_voltage(fe, voltage); + + + return (ret < 0) ? -ENODEV : 0; +} + +static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct lme2510_state *st = fe_to_priv(fe); + + *strength = (u16)((u32)st->signal_level * 0xffff / 0xff); + + return 0; +} + +static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lme2510_state *st = fe_to_priv(fe); + + *snr = (u16)((u32)st->signal_sn * 0xffff / 0x7f); + + return 0; +} + +static int dm04_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + + return 0; +} + +static int dm04_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + + return 0; +} + +static int lme_name(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct lme2510_state *st = adap_to_priv(adap); + const char *desc = d->name; + char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395", + " SHARP:BS2F7HZ0194", " RS2000"}; + char *name = adap->fe[0]->ops.info.name; + + strlcpy(name, desc, 128); + strlcat(name, fe_name[st->tuner_config], 128); + + return 0; +} + +static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct lme2510_state *st = d->priv; + int ret = 0; + + st->i2c_talk_onoff = 1; + switch (le16_to_cpu(d->udev->descriptor.idProduct)) { + case 0x1122: + case 0x1120: + st->i2c_gate = 4; + adap->fe[0] = dvb_attach(tda10086_attach, + &tda10086_config, &d->i2c_adap); + if (adap->fe[0]) { + info("TUN Found Frontend TDA10086"); + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 4; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_LG; + if (st->dvb_usb_lme2510_firmware != TUNER_LG) { + st->dvb_usb_lme2510_firmware = TUNER_LG; + ret = lme_firmware_switch(d, 1) ? 0 : -ENODEV; + } + break; + } + + st->i2c_gate = 4; + adap->fe[0] = dvb_attach(stv0299_attach, + &sharp_z0194_config, &d->i2c_adap); + if (adap->fe[0]) { + info("FE Found Stv0299"); + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_S0194; + if (st->dvb_usb_lme2510_firmware != TUNER_S0194) { + st->dvb_usb_lme2510_firmware = TUNER_S0194; + ret = lme_firmware_switch(d, 1) ? 0 : -ENODEV; + } + break; + } + + st->i2c_gate = 5; + adap->fe[0] = dvb_attach(stv0288_attach, &lme_config, + &d->i2c_adap); + + if (adap->fe[0]) { + info("FE Found Stv0288"); + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_S7395; + if (st->dvb_usb_lme2510_firmware != TUNER_S7395) { + st->dvb_usb_lme2510_firmware = TUNER_S7395; + ret = lme_firmware_switch(d, 1) ? 0 : -ENODEV; + } + break; + } + case 0x22f0: + st->i2c_gate = 5; + adap->fe[0] = dvb_attach(m88rs2000_attach, + &m88rs2000_config, &d->i2c_adap); + + if (adap->fe[0]) { + info("FE Found M88RS2000"); + st->i2c_tuner_gate_w = 5; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + st->tuner_config = TUNER_RS2000; + st->fe_set_voltage = + adap->fe[0]->ops.set_voltage; + + adap->fe[0]->ops.read_signal_strength = + dm04_rs2000_read_signal_strength; + adap->fe[0]->ops.read_snr = + dm04_rs2000_read_snr; + adap->fe[0]->ops.read_ber = + dm04_read_ber; + adap->fe[0]->ops.read_ucblocks = + dm04_read_ucblocks; + } + break; + } + + if (adap->fe[0] == NULL) { + info("DM04/QQBOX Not Powered up or not Supported"); + return -ENODEV; + } + + if (ret) { + if (adap->fe[0]) { + dvb_frontend_detach(adap->fe[0]); + adap->fe[0] = NULL; + } + d->rc_map = NULL; + return -ENODEV; + } + + adap->fe[0]->ops.set_voltage = dm04_lme2510_set_voltage; + ret = lme_name(adap); + return ret; +} + +static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct lme2510_state *st = adap_to_priv(adap); + char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA", "RS2000"}; + int ret = 0; + + switch (st->tuner_config) { + case TUNER_LG: + if (dvb_attach(tda826x_attach, adap->fe[0], 0xc0, + &d->i2c_adap, 1)) + ret = st->tuner_config; + break; + case TUNER_S7395: + if (dvb_attach(ix2505v_attach , adap->fe[0], &lme_tuner, + &d->i2c_adap)) + ret = st->tuner_config; + break; + case TUNER_S0194: + if (dvb_attach(dvb_pll_attach , adap->fe[0], 0xc0, + &d->i2c_adap, DVB_PLL_OPERA1)) + ret = st->tuner_config; + break; + case TUNER_RS2000: + ret = st->tuner_config; + break; + default: + break; + } + + if (ret) + info("TUN Found %s tuner", tun_msg[ret]); + else { + info("TUN No tuner found --- resetting device"); + lme_coldreset(d); + return -ENODEV; + } + + /* Start the Interrupt*/ + ret = lme2510_int_read(adap); + if (ret < 0) { + info("INT Unable to start Interrupt Service"); + return -ENODEV; + } + + return ret; +} + +static int lme2510_powerup(struct dvb_usb_device *d, int onoff) +{ + struct lme2510_state *st = d->priv; + static u8 lnb_on[] = LNB_ON; + static u8 lnb_off[] = LNB_OFF; + static u8 rbuf[1]; + int ret = 0, len = 3, rlen = 1; + + mutex_lock(&d->i2c_mutex); + + if (onoff) + ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen); + else + ret = lme2510_usb_talk(d, lnb_off, len, rbuf, rlen); + + st->i2c_talk_onoff = 1; + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static int lme2510_get_adapter_count(struct dvb_usb_device *d) +{ + return 1; +} + +static int lme2510_identify_state(struct dvb_usb_device *d, const char **name) +{ + struct lme2510_state *st = d->priv; + + usb_reset_configuration(d->udev); + + usb_set_interface(d->udev, + d->intf->cur_altsetting->desc.bInterfaceNumber, 1); + + st->dvb_usb_lme2510_firmware = dvb_usb_lme2510_firmware; + + if (lme2510_return_status(d) == 0x44) { + *name = lme_firmware_switch(d, 0); + return COLD; + } + + return 0; +} + +static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, + struct usb_data_stream_properties *stream) +{ + struct dvb_usb_adapter *adap = fe_to_adap(fe); + struct dvb_usb_device *d = adap_to_d(adap); + + if (adap == NULL) + return 0; + /* Turn PID filter on the fly by module option */ + if (pid_filter == 2) { + adap->pid_filtering = 1; + adap->max_feed_count = 15; + } + + if (!(le16_to_cpu(d->udev->descriptor.idProduct) + == 0x1122)) + stream->endpoint = 0x8; + + return 0; +} + +static int lme2510_get_rc_config(struct dvb_usb_device *d, + struct dvb_usb_rc *rc) +{ + rc->allowed_protos = RC_TYPE_NEC; + return 0; +} + +static void *lme2510_exit_int(struct dvb_usb_device *d) +{ + struct lme2510_state *st = d->priv; + struct dvb_usb_adapter *adap = &d->adapter[0]; + void *buffer = NULL; + + if (adap != NULL) { + lme2510_kill_urb(&adap->stream); + } + + if (st->usb_buffer != NULL) { + st->i2c_talk_onoff = 1; + st->signal_lock = 0; + st->signal_level = 0; + st->signal_sn = 0; + buffer = st->usb_buffer; + } + + if (st->lme_urb != NULL) { + usb_kill_urb(st->lme_urb); + usb_free_coherent(d->udev, 128, st->buffer, + st->lme_urb->transfer_dma); + info("Interrupt Service Stopped"); + } + + return buffer; +} + +static void lme2510_exit(struct dvb_usb_device *d) +{ + void *usb_buffer; + + if (d != NULL) { + usb_buffer = lme2510_exit_int(d); + if (usb_buffer != NULL) + kfree(usb_buffer); + } +} + +static struct dvb_usb_device_properties lme2510_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .bInterfaceNumber = 0, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct lme2510_state), + + .download_firmware = lme2510_download_firmware, + + .power_ctrl = lme2510_powerup, + .identify_state = lme2510_identify_state, + .i2c_algo = &lme2510_i2c_algo, + + .frontend_attach = dm04_lme2510_frontend_attach, + .tuner_attach = dm04_lme2510_tuner, + .get_stream_config = lme2510_get_stream_config, + .get_adapter_count = lme2510_get_adapter_count, + .streaming_ctrl = lme2510_streaming_ctrl, + + .get_rc_config = lme2510_get_rc_config, + + .exit = lme2510_exit, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER| + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 15, + .pid_filter = lme2510_pid_filter, + .pid_filter_ctrl = lme2510_pid_filter_ctrl, + .stream = + DVB_USB_STREAM_BULK(0x86, 10, 4096), + }, + { + } + }, +}; + +static const struct usb_device_id lme2510_id_table[] = { + { DVB_USB_DEVICE(0x3344, 0x1122, &lme2510_props, + "DM04_LME2510_DVB-S", RC_MAP_LME2510) }, + { DVB_USB_DEVICE(0x3344, 0x1120, &lme2510_props, + "DM04_LME2510C_DVB-S", RC_MAP_LME2510) }, + { DVB_USB_DEVICE(0x3344, 0x22f0, &lme2510_props, + "DM04_LME2510C_DVB-S RS2000", RC_MAP_LME2510) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, lme2510_id_table); + +static struct usb_driver lme2510_driver = { + .name = KBUILD_MODNAME, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .id_table = lme2510_id_table, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(lme2510_driver); + +MODULE_AUTHOR("Malcolm Priestley "); +MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); +MODULE_VERSION("2.06"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(LME2510_C_S7395); +MODULE_FIRMWARE(LME2510_C_LG); +MODULE_FIRMWARE(LME2510_C_S0194); +MODULE_FIRMWARE(LME2510_C_RS2000); +MODULE_FIRMWARE(LME2510_LG); +MODULE_FIRMWARE(LME2510_S0194); + diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.h b/drivers/media/usb/dvb-usb-v2/lmedm04.h new file mode 100644 index 000000000000..e9c207205c2f --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.h @@ -0,0 +1,175 @@ +/* DVB USB compliant linux driver for + * + * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 + * LME2510C + LG TDQY-P001F + * LME2510 + LG TDQY-P001F + * + * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) + * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) + * + * MVB001F (LME2510+LGTDQT-P001F) + * LG TDQY - P001F =(TDA8263 + TDA10086H) + * + * MVB0001F (LME2510C+LGTDQT-P001F) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_LME2510_H_ +#define _DVB_USB_LME2510_H_ + +/* Streamer & PID + * + * Note: These commands do not actually stop the streaming + * but form some kind of packet filtering/stream count + * or tuning related functions. + * 06 XX + * offset 1 = 00 Enable Streaming + * + * + * PID + * 03 XX XX ----> reg number ---> setting....20 XX + * offset 1 = length + * offset 2 = start of data + * end byte -1 = 20 + * end byte = clear pid always a0, other wise 9c, 9a ?? + * +*/ +#define LME_ST_ON_W {0x06, 0x00} +#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0} +#define LME_ZERO_PID {0x03, 0x06, 0x00, 0x00, 0x01, 0x00, 0x20, 0x9c} +#define LME_ALL_PIDS {0x03, 0x06, 0x00, 0xff, 0x01, 0x1f, 0x20, 0x81} + +/* LNB Voltage + * 07 XX XX + * offset 1 = 01 + * offset 2 = 00=Voltage low 01=Voltage high + * + * LNB Power + * 03 01 XX + * offset 2 = 00=ON 01=OFF + */ + +#define LME_VOLTAGE_L {0x07, 0x01, 0x00} +#define LME_VOLTAGE_H {0x07, 0x01, 0x01} +#define LNB_ON {0x3a, 0x01, 0x00} +#define LNB_OFF {0x3a, 0x01, 0x01} + +/* Initial stv0288 settings for 7395 Frontend */ +static u8 s7395_inittab[] = { + 0x01, 0x15, + 0x02, 0x20, + 0x03, 0xa0, + 0x04, 0xa0, + 0x05, 0x12, + 0x06, 0x00, + 0x09, 0x00, + 0x0a, 0x04, + 0x0b, 0x00, + 0x0c, 0x00, + 0x0d, 0x00, + 0x0e, 0xc1, + 0x0f, 0x54, + 0x11, 0x7a, + 0x12, 0x03, + 0x13, 0x48, + 0x14, 0x84, + 0x15, 0xc5, + 0x16, 0xb8, + 0x17, 0x9c, + 0x18, 0x00, + 0x19, 0xa6, + 0x1a, 0x88, + 0x1b, 0x8f, + 0x1c, 0xf0, + 0x20, 0x0b, + 0x21, 0x54, + 0x22, 0xff, + 0x23, 0x01, + 0x28, 0x46, + 0x29, 0x66, + 0x2a, 0x90, + 0x2b, 0xfa, + 0x2c, 0xd9, + 0x30, 0x0, + 0x31, 0x1e, + 0x32, 0x14, + 0x33, 0x0f, + 0x34, 0x09, + 0x35, 0x0c, + 0x36, 0x05, + 0x37, 0x2f, + 0x38, 0x16, + 0x39, 0xbd, + 0x3a, 0x0, + 0x3b, 0x13, + 0x3c, 0x11, + 0x3d, 0x30, + 0x40, 0x63, + 0x41, 0x04, + 0x42, 0x20, + 0x43, 0x00, + 0x44, 0x00, + 0x45, 0x00, + 0x46, 0x00, + 0x47, 0x00, + 0x4a, 0x00, + 0x50, 0x10, + 0x51, 0x36, + 0x52, 0x21, + 0x53, 0x94, + 0x54, 0xb2, + 0x55, 0x29, + 0x56, 0x64, + 0x57, 0x2b, + 0x58, 0x54, + 0x59, 0x86, + 0x5a, 0x00, + 0x5b, 0x9b, + 0x5c, 0x08, + 0x5d, 0x7f, + 0x5e, 0xff, + 0x5f, 0x8d, + 0x70, 0x0, + 0x71, 0x0, + 0x72, 0x0, + 0x74, 0x0, + 0x75, 0x0, + 0x76, 0x0, + 0x81, 0x0, + 0x82, 0x3f, + 0x83, 0x3f, + 0x84, 0x0, + 0x85, 0x0, + 0x88, 0x0, + 0x89, 0x0, + 0x8a, 0x0, + 0x8b, 0x0, + 0x8c, 0x0, + 0x90, 0x0, + 0x91, 0x0, + 0x92, 0x0, + 0x93, 0x0, + 0x94, 0x1c, + 0x97, 0x0, + 0xa0, 0x48, + 0xa1, 0x0, + 0xb0, 0xb8, + 0xb1, 0x3a, + 0xb2, 0x10, + 0xb3, 0x82, + 0xb4, 0x80, + 0xb5, 0x82, + 0xb6, 0x82, + 0xb7, 0x82, + 0xb8, 0x20, + 0xb9, 0x0, + 0xf0, 0x0, + 0xf1, 0x0, + 0xf2, 0xc0, + 0xff, 0xff, +}; +#endif diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c new file mode 100644 index 000000000000..d83df4bb72d3 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c @@ -0,0 +1,612 @@ +/* + * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator + * + * Copyright (C) 2010 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 "mxl111sf-demod.h" +#include "mxl111sf-reg.h" + +/* debug */ +static int mxl111sf_demod_debug; +module_param_named(debug, mxl111sf_demod_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define mxl_dbg(fmt, arg...) \ + if (mxl111sf_demod_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +/* ------------------------------------------------------------------------ */ + +struct mxl111sf_demod_state { + struct mxl111sf_state *mxl_state; + + struct mxl111sf_demod_config *cfg; + + struct dvb_frontend fe; +}; + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state, + u8 addr, u8 *data) +{ + return (state->cfg->read_reg) ? + state->cfg->read_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state, + u8 addr, u8 data) +{ + return (state->cfg->write_reg) ? + state->cfg->write_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static +int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +{ + return (state->cfg->program_regs) ? + state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : + -EINVAL; +} + +/* ------------------------------------------------------------------------ */ +/* TPS */ + +static +int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state, + fe_code_rate_t *code_rate) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val); + /* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */ + if (mxl_fail(ret)) + goto fail; + + switch (val & V6_CODE_RATE_TPS_MASK) { + case 0: + *code_rate = FEC_1_2; + break; + case 1: + *code_rate = FEC_2_3; + break; + case 2: + *code_rate = FEC_3_4; + break; + case 3: + *code_rate = FEC_5_6; + break; + case 4: + *code_rate = FEC_7_8; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_modulation(struct mxl111sf_demod_state *state, + fe_modulation_t *modulation) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val); + /* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) { + case 0: + *modulation = QPSK; + break; + case 1: + *modulation = QAM_16; + break; + case 2: + *modulation = QAM_64; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state, + fe_transmit_mode_t *fft_mode) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val); + /* FFT Mode, 00:2K, 01:8K, 10:4K */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) { + case 0: + *fft_mode = TRANSMISSION_MODE_2K; + break; + case 1: + *fft_mode = TRANSMISSION_MODE_8K; + break; + case 2: + *fft_mode = TRANSMISSION_MODE_4K; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state, + fe_guard_interval_t *guard) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val); + /* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_GI_MASK) >> 4) { + case 0: + *guard = GUARD_INTERVAL_1_32; + break; + case 1: + *guard = GUARD_INTERVAL_1_16; + break; + case 2: + *guard = GUARD_INTERVAL_1_8; + break; + case 3: + *guard = GUARD_INTERVAL_1_4; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state, + fe_hierarchy_t *hierarchy) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val); + /* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) { + case 0: + *hierarchy = HIERARCHY_NONE; + break; + case 1: + *hierarchy = HIERARCHY_1; + break; + case 2: + *hierarchy = HIERARCHY_2; + break; + case 3: + *hierarchy = HIERARCHY_4; + break; + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ +/* LOCKS */ + +static +int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state, + int *sync_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val); + if (mxl_fail(ret)) + goto fail; + *sync_lock = (val & SYNC_LOCK_MASK) >> 4; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state, + int *rs_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val); + if (mxl_fail(ret)) + goto fail; + *rs_lock = (val & RS_LOCK_DET_MASK) >> 3; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state, + int *tps_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val); + if (mxl_fail(ret)) + goto fail; + *tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state, + int *fec_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val); + if (mxl_fail(ret)) + goto fail; + *fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4; +fail: + return ret; +} + +#if 0 +static +int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state, + int *cp_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val); + if (mxl_fail(ret)) + goto fail; + *cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2; +fail: + return ret; +} +#endif + +static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state) +{ + return mxl111sf_demod_write_reg(state, 0x0e, 0xff); +} + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + int ret = 0; + + struct mxl111sf_reg_ctrl_info phy_pll_patch[] = { + {0x00, 0xff, 0x01}, /* change page to 1 */ + {0x40, 0xff, 0x05}, + {0x40, 0xff, 0x01}, + {0x41, 0xff, 0xca}, + {0x41, 0xff, 0xc0}, + {0x00, 0xff, 0x00}, /* change page to 0 */ + {0, 0, 0} + }; + + mxl_dbg("()"); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (mxl_fail(ret)) + goto fail; + msleep(50); + } + ret = mxl111sf_demod_program_regs(state, phy_pll_patch); + mxl_fail(ret); + msleep(50); + ret = mxl1x1sf_demod_reset_irq_status(state); + mxl_fail(ret); + msleep(100); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#if 0 +/* resets TS Packet error count */ +/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */ +static +int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state) +{ + struct mxl111sf_reg_ctrl_info reset_per_count[] = { + {0x20, 0x01, 0x01}, + {0x20, 0x01, 0x00}, + {0, 0, 0} + }; + return mxl111sf_demod_program_regs(state, reset_per_count); +} +#endif + +/* returns TS Packet error count */ +/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */ +static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + u32 fec_per_count, fec_per_scale; + u8 val; + int ret; + + *ucblocks = 0; + + /* FEC_PER_COUNT Register */ + ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val); + if (mxl_fail(ret)) + goto fail; + + fec_per_count = val; + + /* FEC_PER_SCALE Register */ + ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val); + if (mxl_fail(ret)) + goto fail; + + val &= V6_FEC_PER_SCALE_MASK; + val *= 4; + + fec_per_scale = 1 << val; + + fec_per_count *= fec_per_scale; + + *ucblocks = fec_per_count; +fail: + return ret; +} + +#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS +/* FIXME: leaving this enabled breaks the build on some architectures, + * and we shouldn't have any floating point math in the kernel, anyway. + * + * These macros need to be re-written, but it's harmless to simply + * return zero for now. */ +#define CALCULATE_BER(avg_errors, count) \ + ((u32)(avg_errors * 4)/(count*64*188*8)) +#define CALCULATE_SNR(data) \ + ((u32)((10 * (u32)data / 64) - 2.5)) +#else +#define CALCULATE_BER(avg_errors, count) 0 +#define CALCULATE_SNR(data) 0 +#endif + +static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + u8 val1, val2, val3; + int ret; + + *ber = 0; + + ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3); + if (mxl_fail(ret)) + goto fail; + + *ber = CALCULATE_BER((val1 | (val2 << 8)), val3); +fail: + return ret; +} + +static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state, + u16 *snr) +{ + u8 val1, val2; + int ret; + + *snr = 0; + + ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + + *snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8)); +fail: + return ret; +} + +static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + + int ret = mxl111sf_demod_calc_snr(state, snr); + if (mxl_fail(ret)) + goto fail; + + *snr /= 10; /* 0.1 dB */ +fail: + return ret; +} + +static int mxl111sf_demod_read_status(struct dvb_frontend *fe, + fe_status_t *status) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + int ret, locked, cr_lock, sync_lock, fec_lock; + + *status = 0; + + ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock); + if (mxl_fail(ret)) + goto fail; + + if (locked) + *status |= FE_HAS_SIGNAL; + if (cr_lock) + *status |= FE_HAS_CARRIER; + if (sync_lock) + *status |= FE_HAS_SYNC; + if (fec_lock) /* false positives? */ + *status |= FE_HAS_VITERBI; + + if ((locked) && (cr_lock) && (sync_lock)) + *status |= FE_HAS_LOCK; +fail: + return ret; +} + +static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + fe_modulation_t modulation; + u16 snr; + + mxl111sf_demod_calc_snr(state, &snr); + mxl1x1sf_demod_get_tps_modulation(state, &modulation); + + switch (modulation) { + case QPSK: + *signal_strength = (snr >= 1300) ? + min(65535, snr * 44) : snr * 38; + break; + case QAM_16: + *signal_strength = (snr >= 1500) ? + min(65535, snr * 38) : snr * 33; + break; + case QAM_64: + *signal_strength = (snr >= 2000) ? + min(65535, snr * 29) : snr * 25; + break; + default: + *signal_strength = 0; + return -EINVAL; + } + + return 0; +} + +static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mxl111sf_demod_state *state = fe->demodulator_priv; + + mxl_dbg("()"); +#if 0 + p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF; +#endif + if (fe->ops.tuner_ops.get_bandwidth) + fe->ops.tuner_ops.get_bandwidth(fe, &p->bandwidth_hz); + if (fe->ops.tuner_ops.get_frequency) + fe->ops.tuner_ops.get_frequency(fe, &p->frequency); + mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_HP); + mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_LP); + mxl1x1sf_demod_get_tps_modulation(state, &p->modulation); + mxl1x1sf_demod_get_tps_guard_fft_mode(state, + &p->transmission_mode); + mxl1x1sf_demod_get_tps_guard_interval(state, + &p->guard_interval); + mxl1x1sf_demod_get_tps_hierarchy(state, + &p->hierarchy); + + return 0; +} + +static +int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void mxl111sf_demod_release(struct dvb_frontend *fe) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + mxl_dbg("()"); + kfree(state); + fe->demodulator_priv = NULL; +} + +static struct dvb_frontend_ops mxl111sf_demod_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "MaxLinear MxL111SF DVB-T demodulator", + .frequency_min = 177000000, + .frequency_max = 858000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + .release = mxl111sf_demod_release, +#if 0 + .init = mxl111sf_init, + .i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl, +#endif + .set_frontend = mxl111sf_demod_set_frontend, + .get_frontend = mxl111sf_demod_get_frontend, + .get_tune_settings = mxl111sf_demod_get_tune_settings, + .read_status = mxl111sf_demod_read_status, + .read_signal_strength = mxl111sf_demod_read_signal_strength, + .read_ber = mxl111sf_demod_read_ber, + .read_snr = mxl111sf_demod_read_snr, + .read_ucblocks = mxl111sf_demod_read_ucblocks, +}; + +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg) +{ + struct mxl111sf_demod_state *state = NULL; + + mxl_dbg("()"); + + state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->mxl_state = mxl_state; + state->cfg = cfg; + + memcpy(&state->fe.ops, &mxl111sf_demod_ops, + sizeof(struct dvb_frontend_ops)); + + state->fe.demodulator_priv = state; + return &state->fe; +} +EXPORT_SYMBOL_GPL(mxl111sf_demod_attach); + +MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h new file mode 100644 index 000000000000..432706ae5274 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h @@ -0,0 +1,55 @@ +/* + * mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator + * + * Copyright (C) 2010 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 __MXL111SF_DEMOD_H__ +#define __MXL111SF_DEMOD_H__ + +#include "dvb_frontend.h" +#include "mxl111sf.h" + +struct mxl111sf_demod_config { + int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data); + int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data); + int (*program_regs)(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info); +}; + +#if defined(CONFIG_DVB_USB_MXL111SF) || \ + (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +extern +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg); +#else +static inline +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_USB_MXL111SF */ + +#endif /* __MXL111SF_DEMOD_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c new file mode 100644 index 000000000000..e4121cb8f5ef --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c @@ -0,0 +1,763 @@ +/* + * mxl111sf-gpio.c - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 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 "mxl111sf-gpio.h" +#include "mxl111sf-i2c.h" +#include "mxl111sf.h" + +/* ------------------------------------------------------------------------- */ + +#define MXL_GPIO_MUX_REG_0 0x84 +#define MXL_GPIO_MUX_REG_1 0x89 +#define MXL_GPIO_MUX_REG_2 0x82 + +#define MXL_GPIO_DIR_INPUT 0 +#define MXL_GPIO_DIR_OUTPUT 1 + + +static int mxl111sf_set_gpo_state(struct mxl111sf_state *state, u8 pin, u8 val) +{ + int ret; + u8 tmp; + + mxl_debug_adv("(%d, %d)", pin, val); + + if ((pin > 0) && (pin < 8)) { + ret = mxl111sf_read_reg(state, 0x19, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (pin - 1)); + tmp |= (val << (pin - 1)); + ret = mxl111sf_write_reg(state, 0x19, tmp); + if (mxl_fail(ret)) + goto fail; + } else if (pin <= 10) { + if (pin == 0) + pin += 7; + ret = mxl111sf_read_reg(state, 0x30, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (pin - 3)); + tmp |= (val << (pin - 3)); + ret = mxl111sf_write_reg(state, 0x30, tmp); + if (mxl_fail(ret)) + goto fail; + } else + ret = -EINVAL; +fail: + return ret; +} + +static int mxl111sf_get_gpi_state(struct mxl111sf_state *state, u8 pin, u8 *val) +{ + int ret; + u8 tmp; + + mxl_debug("(0x%02x)", pin); + + *val = 0; + + switch (pin) { + case 0: + case 1: + case 2: + case 3: + ret = mxl111sf_read_reg(state, 0x23, &tmp); + if (mxl_fail(ret)) + goto fail; + *val = (tmp >> (pin + 4)) & 0x01; + break; + case 4: + case 5: + case 6: + case 7: + ret = mxl111sf_read_reg(state, 0x2f, &tmp); + if (mxl_fail(ret)) + goto fail; + *val = (tmp >> pin) & 0x01; + break; + case 8: + case 9: + case 10: + ret = mxl111sf_read_reg(state, 0x22, &tmp); + if (mxl_fail(ret)) + goto fail; + *val = (tmp >> (pin - 3)) & 0x01; + break; + default: + return -EINVAL; /* invalid pin */ + } +fail: + return ret; +} + +struct mxl_gpio_cfg { + u8 pin; + u8 dir; + u8 val; +}; + +static int mxl111sf_config_gpio_pins(struct mxl111sf_state *state, + struct mxl_gpio_cfg *gpio_cfg) +{ + int ret; + u8 tmp; + + mxl_debug_adv("(%d, %d)", gpio_cfg->pin, gpio_cfg->dir); + + switch (gpio_cfg->pin) { + case 0: + case 1: + case 2: + case 3: + ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_0, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (gpio_cfg->pin + 4)); + tmp |= (gpio_cfg->dir << (gpio_cfg->pin + 4)); + ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_0, tmp); + if (mxl_fail(ret)) + goto fail; + break; + case 4: + case 5: + case 6: + case 7: + ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_1, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << gpio_cfg->pin); + tmp |= (gpio_cfg->dir << gpio_cfg->pin); + ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_1, tmp); + if (mxl_fail(ret)) + goto fail; + break; + case 8: + case 9: + case 10: + ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_2, &tmp); + if (mxl_fail(ret)) + goto fail; + tmp &= ~(1 << (gpio_cfg->pin - 3)); + tmp |= (gpio_cfg->dir << (gpio_cfg->pin - 3)); + ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_2, tmp); + if (mxl_fail(ret)) + goto fail; + break; + default: + return -EINVAL; /* invalid pin */ + } + + ret = (MXL_GPIO_DIR_OUTPUT == gpio_cfg->dir) ? + mxl111sf_set_gpo_state(state, + gpio_cfg->pin, gpio_cfg->val) : + mxl111sf_get_gpi_state(state, + gpio_cfg->pin, &gpio_cfg->val); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_hw_do_set_gpio(struct mxl111sf_state *state, + int gpio, int direction, int val) +{ + struct mxl_gpio_cfg gpio_config = { + .pin = gpio, + .dir = direction, + .val = val, + }; + + mxl_debug("(%d, %d, %d)", gpio, direction, val); + + return mxl111sf_config_gpio_pins(state, &gpio_config); +} + +/* ------------------------------------------------------------------------- */ + +#define PIN_MUX_MPEG_MODE_MASK 0x40 /* 0x17 <6> */ +#define PIN_MUX_MPEG_PAR_EN_MASK 0x01 /* 0x18 <0> */ +#define PIN_MUX_MPEG_SER_EN_MASK 0x02 /* 0x18 <1> */ +#define PIN_MUX_MPG_IN_MUX_MASK 0x80 /* 0x3D <7> */ +#define PIN_MUX_BT656_ENABLE_MASK 0x04 /* 0x12 <2> */ +#define PIN_MUX_I2S_ENABLE_MASK 0x40 /* 0x15 <6> */ +#define PIN_MUX_SPI_MODE_MASK 0x10 /* 0x3D <4> */ +#define PIN_MUX_MCLK_EN_CTRL_MASK 0x10 /* 0x82 <4> */ +#define PIN_MUX_MPSYN_EN_CTRL_MASK 0x20 /* 0x82 <5> */ +#define PIN_MUX_MDVAL_EN_CTRL_MASK 0x40 /* 0x82 <6> */ +#define PIN_MUX_MPERR_EN_CTRL_MASK 0x80 /* 0x82 <7> */ +#define PIN_MUX_MDAT_EN_0_MASK 0x10 /* 0x84 <4> */ +#define PIN_MUX_MDAT_EN_1_MASK 0x20 /* 0x84 <5> */ +#define PIN_MUX_MDAT_EN_2_MASK 0x40 /* 0x84 <6> */ +#define PIN_MUX_MDAT_EN_3_MASK 0x80 /* 0x84 <7> */ +#define PIN_MUX_MDAT_EN_4_MASK 0x10 /* 0x89 <4> */ +#define PIN_MUX_MDAT_EN_5_MASK 0x20 /* 0x89 <5> */ +#define PIN_MUX_MDAT_EN_6_MASK 0x40 /* 0x89 <6> */ +#define PIN_MUX_MDAT_EN_7_MASK 0x80 /* 0x89 <7> */ + +int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, + enum mxl111sf_mux_config pin_mux_config) +{ + u8 r12, r15, r17, r18, r3D, r82, r84, r89; + int ret; + + mxl_debug("(%d)", pin_mux_config); + + ret = mxl111sf_read_reg(state, 0x17, &r17); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x18, &r18); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x12, &r12); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x15, &r15); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x82, &r82); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x84, &r84); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x89, &r89); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_read_reg(state, 0x3D, &r3D); + if (mxl_fail(ret)) + goto fail; + + switch (pin_mux_config) { + case PIN_MUX_TS_OUT_PARALLEL: + /* mpeg_mode = 1 */ + r17 |= PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 1 */ + r18 |= PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 1 */ + r82 |= PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 1 */ + r82 |= PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 1 */ + r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 1 */ + r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0xF */ + r84 |= 0xF0; + /* mdat_en_ctrl[7:4] = 0xF */ + r89 |= 0xF0; + break; + case PIN_MUX_TS_OUT_SERIAL: + /* mpeg_mode = 1 */ + r17 |= PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 1 */ + r82 |= PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 1 */ + r82 |= PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 1 */ + r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 1 */ + r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0xF */ + r84 |= 0xF0; + /* mdat_en_ctrl[7:4] = 0xF */ + r89 |= 0xF0; + break; + case PIN_MUX_GPIO_MODE: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SERIAL_IN_MODE_0: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SERIAL_IN_MODE_1: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 1 */ + r3D |= PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SPI_IN_MODE_1: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 1 */ + r3D |= PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 1 */ + r15 |= PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 1 */ + r3D |= PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_SPI_IN_MODE_0: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 1 */ + r18 |= PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 1 */ + r15 |= PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 1 */ + r3D |= PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_TS_PARALLEL_IN: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 1 */ + r18 |= PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_BT656_I2S_MODE: + /* mpeg_mode = 0 */ + r17 &= ~PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 1 */ + r12 |= PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 1 */ + r15 |= PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + case PIN_MUX_DEFAULT: + default: + /* mpeg_mode = 1 */ + r17 |= PIN_MUX_MPEG_MODE_MASK; + /* mpeg_par_en = 0 */ + r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; + /* mpeg_ser_en = 0 */ + r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; + /* mpg_in_mux = 0 */ + r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; + /* bt656_enable = 0 */ + r12 &= ~PIN_MUX_BT656_ENABLE_MASK; + /* i2s_enable = 0 */ + r15 &= ~PIN_MUX_I2S_ENABLE_MASK; + /* spi_mode = 0 */ + r3D &= ~PIN_MUX_SPI_MODE_MASK; + /* mclk_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; + /* mperr_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; + /* mdval_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; + /* mpsyn_en_ctrl = 0 */ + r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; + /* mdat_en_ctrl[3:0] = 0x0 */ + r84 &= 0x0F; + /* mdat_en_ctrl[7:4] = 0x0 */ + r89 &= 0x0F; + break; + } + + ret = mxl111sf_write_reg(state, 0x17, r17); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x18, r18); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x12, r12); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x15, r15); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x82, r82); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x84, r84); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x89, r89); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x3D, r3D); + if (mxl_fail(ret)) + goto fail; +fail: + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl111sf_hw_set_gpio(struct mxl111sf_state *state, int gpio, int val) +{ + return mxl111sf_hw_do_set_gpio(state, gpio, MXL_GPIO_DIR_OUTPUT, val); +} + +static int mxl111sf_hw_gpio_initialize(struct mxl111sf_state *state) +{ + u8 gpioval = 0x07; /* write protect enabled, signal LEDs off */ + int i, ret; + + mxl_debug("()"); + + for (i = 3; i < 8; i++) { + ret = mxl111sf_hw_set_gpio(state, i, (gpioval >> i) & 0x01); + if (mxl_fail(ret)) + break; + } + + return ret; +} + +#define PCA9534_I2C_ADDR (0x40 >> 1) +static int pca9534_set_gpio(struct mxl111sf_state *state, int gpio, int val) +{ + u8 w[2] = { 1, 0 }; + u8 r = 0; + struct i2c_msg msg[] = { + { .addr = PCA9534_I2C_ADDR, + .flags = 0, .buf = w, .len = 1 }, + { .addr = PCA9534_I2C_ADDR, + .flags = I2C_M_RD, .buf = &r, .len = 1 }, + }; + + mxl_debug("(%d, %d)", gpio, val); + + /* read current GPIO levels from flip-flop */ + i2c_transfer(&state->d->i2c_adap, msg, 2); + + /* prepare write buffer with current GPIO levels */ + msg[0].len = 2; +#if 0 + w[0] = 1; +#endif + w[1] = r; + + /* clear the desired GPIO */ + w[1] &= ~(1 << gpio); + + /* set the desired GPIO value */ + w[1] |= ((val ? 1 : 0) << gpio); + + /* write new GPIO levels to flip-flop */ + i2c_transfer(&state->d->i2c_adap, &msg[0], 1); + + return 0; +} + +static int pca9534_init_port_expander(struct mxl111sf_state *state) +{ + u8 w[2] = { 1, 0x07 }; /* write protect enabled, signal LEDs off */ + + struct i2c_msg msg = { + .addr = PCA9534_I2C_ADDR, + .flags = 0, .buf = w, .len = 2 + }; + + mxl_debug("()"); + + i2c_transfer(&state->d->i2c_adap, &msg, 1); + + /* configure all pins as outputs */ + w[0] = 3; + w[1] = 0; + + i2c_transfer(&state->d->i2c_adap, &msg, 1); + + return 0; +} + +int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val) +{ + mxl_debug("(%d, %d)", gpio, val); + + switch (state->gpio_port_expander) { + default: + mxl_printk(KERN_ERR, + "gpio_port_expander undefined, assuming PCA9534"); + /* fall-thru */ + case mxl111sf_PCA9534: + return pca9534_set_gpio(state, gpio, val); + case mxl111sf_gpio_hw: + return mxl111sf_hw_set_gpio(state, gpio, val); + } +} + +static int mxl111sf_probe_port_expander(struct mxl111sf_state *state) +{ + int ret; + u8 w = 1; + u8 r = 0; + struct i2c_msg msg[] = { + { .flags = 0, .buf = &w, .len = 1 }, + { .flags = I2C_M_RD, .buf = &r, .len = 1 }, + }; + + mxl_debug("()"); + + msg[0].addr = 0x70 >> 1; + msg[1].addr = 0x70 >> 1; + + /* read current GPIO levels from flip-flop */ + ret = i2c_transfer(&state->d->i2c_adap, msg, 2); + if (ret == 2) { + state->port_expander_addr = msg[0].addr; + state->gpio_port_expander = mxl111sf_PCA9534; + mxl_debug("found port expander at 0x%02x", + state->port_expander_addr); + return 0; + } + + msg[0].addr = 0x40 >> 1; + msg[1].addr = 0x40 >> 1; + + ret = i2c_transfer(&state->d->i2c_adap, msg, 2); + if (ret == 2) { + state->port_expander_addr = msg[0].addr; + state->gpio_port_expander = mxl111sf_PCA9534; + mxl_debug("found port expander at 0x%02x", + state->port_expander_addr); + return 0; + } + state->port_expander_addr = 0xff; + state->gpio_port_expander = mxl111sf_gpio_hw; + mxl_debug("using hardware gpio"); + return 0; +} + +int mxl111sf_init_port_expander(struct mxl111sf_state *state) +{ + mxl_debug("()"); + + if (0x00 == state->port_expander_addr) + mxl111sf_probe_port_expander(state); + + switch (state->gpio_port_expander) { + default: + mxl_printk(KERN_ERR, + "gpio_port_expander undefined, assuming PCA9534"); + /* fall-thru */ + case mxl111sf_PCA9534: + return pca9534_init_port_expander(state); + case mxl111sf_gpio_hw: + return mxl111sf_hw_gpio_initialize(state); + } +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode) +{ +/* GPO: + * 3 - ATSC/MH# | 1 = ATSC transport, 0 = MH transport | default 0 + * 4 - ATSC_RST## | 1 = ATSC enable, 0 = ATSC Reset | default 0 + * 5 - ATSC_EN | 1 = ATSC power enable, 0 = ATSC power off | default 0 + * 6 - MH_RESET# | 1 = MH enable, 0 = MH Reset | default 0 + * 7 - MH_EN | 1 = MH power enable, 0 = MH power off | default 0 + */ + mxl_debug("(%d)", mode); + + switch (mode) { + case MXL111SF_GPIO_MOD_MH: + mxl111sf_set_gpio(state, 4, 0); + mxl111sf_set_gpio(state, 5, 0); + msleep(50); + mxl111sf_set_gpio(state, 7, 1); + msleep(50); + mxl111sf_set_gpio(state, 6, 1); + msleep(50); + + mxl111sf_set_gpio(state, 3, 0); + break; + case MXL111SF_GPIO_MOD_ATSC: + mxl111sf_set_gpio(state, 6, 0); + mxl111sf_set_gpio(state, 7, 0); + msleep(50); + mxl111sf_set_gpio(state, 5, 1); + msleep(50); + mxl111sf_set_gpio(state, 4, 1); + msleep(50); + mxl111sf_set_gpio(state, 3, 1); + break; + default: /* DVBT / STANDBY */ + mxl111sf_init_port_expander(state); + break; + } + return 0; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h new file mode 100644 index 000000000000..0220f54299a5 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h @@ -0,0 +1,56 @@ +/* + * mxl111sf-gpio.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 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_USB_MXL111SF_GPIO_H_ +#define _DVB_USB_MXL111SF_GPIO_H_ + +#include "mxl111sf.h" + +int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val); +int mxl111sf_init_port_expander(struct mxl111sf_state *state); + +#define MXL111SF_GPIO_MOD_DVBT 0 +#define MXL111SF_GPIO_MOD_MH 1 +#define MXL111SF_GPIO_MOD_ATSC 2 +int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode); + +enum mxl111sf_mux_config { + PIN_MUX_DEFAULT = 0, + PIN_MUX_TS_OUT_PARALLEL, + PIN_MUX_TS_OUT_SERIAL, + PIN_MUX_GPIO_MODE, + PIN_MUX_TS_SERIAL_IN_MODE_0, + PIN_MUX_TS_SERIAL_IN_MODE_1, + PIN_MUX_TS_SPI_IN_MODE_0, + PIN_MUX_TS_SPI_IN_MODE_1, + PIN_MUX_TS_PARALLEL_IN, + PIN_MUX_BT656_I2S_MODE, +}; + +int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, + enum mxl111sf_mux_config pin_mux_config); + +#endif /* _DVB_USB_MXL111SF_GPIO_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c new file mode 100644 index 000000000000..34434557ef65 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c @@ -0,0 +1,850 @@ +/* + * mxl111sf-i2c.c - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 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 "mxl111sf-i2c.h" +#include "mxl111sf.h" + +/* SW-I2C ----------------------------------------------------------------- */ + +#define SW_I2C_ADDR 0x1a +#define SW_I2C_EN 0x02 +#define SW_SCL_OUT 0x04 +#define SW_SDA_OUT 0x08 +#define SW_SDA_IN 0x04 + +#define SW_I2C_BUSY_ADDR 0x2f +#define SW_I2C_BUSY 0x02 + +static int mxl111sf_i2c_bitbang_sendbyte(struct mxl111sf_state *state, + u8 byte) +{ + int i, ret; + u8 data = 0; + + mxl_i2c("(0x%02x)", byte); + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); + if (mxl_fail(ret)) + goto fail; + + for (i = 0; i < 8; i++) { + + data = (byte & (0x80 >> i)) ? SW_SDA_OUT : 0; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | data); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | data | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | data); + if (mxl_fail(ret)) + goto fail; + } + + /* last bit was 0 so we need to release SDA */ + if (!(byte & 1)) { + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + } + + /* CLK high for ACK readback */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); + if (mxl_fail(ret)) + goto fail; + + /* drop the CLK after getting ACK, SDA will go high right away */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + if (data & SW_SDA_IN) + ret = -EIO; +fail: + return ret; +} + +static int mxl111sf_i2c_bitbang_recvbyte(struct mxl111sf_state *state, + u8 *pbyte) +{ + int i, ret; + u8 byte = 0; + u8 data = 0; + + mxl_i2c("()"); + + *pbyte = 0; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + for (i = 0; i < 8; i++) { + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | + SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); + if (mxl_fail(ret)) + goto fail; + + if (data & SW_SDA_IN) + byte |= (0x80 >> i); + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + } + *pbyte = byte; +fail: + return ret; +} + +static int mxl111sf_i2c_start(struct mxl111sf_state *state) +{ + int ret; + + mxl_i2c("()"); + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN); /* start */ + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_i2c_stop(struct mxl111sf_state *state) +{ + int ret; + + mxl_i2c("()"); + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN); /* stop */ + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_SCL_OUT | SW_SDA_OUT); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_i2c_ack(struct mxl111sf_state *state) +{ + int ret; + u8 b = 0; + + mxl_i2c("()"); + + ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &b); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN); + if (mxl_fail(ret)) + goto fail; + + /* pull SDA low */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl111sf_i2c_nack(struct mxl111sf_state *state) +{ + int ret; + + mxl_i2c("()"); + + /* SDA high to signal last byte read from slave */ + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, SW_I2C_ADDR, + 0x10 | SW_I2C_EN | SW_SDA_OUT); + mxl_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_i2c_sw_xfer_msg(struct mxl111sf_state *state, + struct i2c_msg *msg) +{ + int i, ret; + + mxl_i2c("()"); + + if (msg->flags & I2C_M_RD) { + + ret = mxl111sf_i2c_start(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_i2c_bitbang_sendbyte(state, + (msg->addr << 1) | 0x01); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + + for (i = 0; i < msg->len; i++) { + ret = mxl111sf_i2c_bitbang_recvbyte(state, + &msg->buf[i]); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + + if (i < msg->len - 1) + mxl111sf_i2c_ack(state); + } + + mxl111sf_i2c_nack(state); + + ret = mxl111sf_i2c_stop(state); + if (mxl_fail(ret)) + goto fail; + + } else { + + ret = mxl111sf_i2c_start(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_i2c_bitbang_sendbyte(state, + (msg->addr << 1) & 0xfe); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + + for (i = 0; i < msg->len; i++) { + ret = mxl111sf_i2c_bitbang_sendbyte(state, + msg->buf[i]); + if (mxl_fail(ret)) { + mxl111sf_i2c_stop(state); + goto fail; + } + } + + /* FIXME: we only want to do this on the last transaction */ + mxl111sf_i2c_stop(state); + } +fail: + return ret; +} + +/* HW-I2C ----------------------------------------------------------------- */ + +#define USB_WRITE_I2C_CMD 0x99 +#define USB_READ_I2C_CMD 0xdd +#define USB_END_I2C_CMD 0xfe + +#define USB_WRITE_I2C_CMD_LEN 26 +#define USB_READ_I2C_CMD_LEN 24 + +#define I2C_MUX_REG 0x30 +#define I2C_CONTROL_REG 0x00 +#define I2C_SLAVE_ADDR_REG 0x08 +#define I2C_DATA_REG 0x0c +#define I2C_INT_STATUS_REG 0x10 + +static int mxl111sf_i2c_send_data(struct mxl111sf_state *state, + u8 index, u8 *wdata) +{ + int ret = mxl111sf_ctrl_msg(state->d, wdata[0], + &wdata[1], 25, NULL, 0); + mxl_fail(ret); + + return ret; +} + +static int mxl111sf_i2c_get_data(struct mxl111sf_state *state, + u8 index, u8 *wdata, u8 *rdata) +{ + int ret = mxl111sf_ctrl_msg(state->d, wdata[0], + &wdata[1], 25, rdata, 24); + mxl_fail(ret); + + return ret; +} + +static u8 mxl111sf_i2c_check_status(struct mxl111sf_state *state) +{ + u8 status = 0; + u8 buf[26]; + + mxl_i2c_adv("()"); + + buf[0] = USB_READ_I2C_CMD; + buf[1] = 0x00; + + buf[2] = I2C_INT_STATUS_REG; + buf[3] = 0x00; + buf[4] = 0x00; + + buf[5] = USB_END_I2C_CMD; + + mxl111sf_i2c_get_data(state, 0, buf, buf); + + if (buf[1] & 0x04) + status = 1; + + return status; +} + +static u8 mxl111sf_i2c_check_fifo(struct mxl111sf_state *state) +{ + u8 status = 0; + u8 buf[26]; + + mxl_i2c("()"); + + buf[0] = USB_READ_I2C_CMD; + buf[1] = 0x00; + + buf[2] = I2C_MUX_REG; + buf[3] = 0x00; + buf[4] = 0x00; + + buf[5] = I2C_INT_STATUS_REG; + buf[6] = 0x00; + buf[7] = 0x00; + buf[8] = USB_END_I2C_CMD; + + mxl111sf_i2c_get_data(state, 0, buf, buf); + + if (0x08 == (buf[1] & 0x08)) + status = 1; + + if ((buf[5] & 0x02) == 0x02) + mxl_i2c("(buf[5] & 0x02) == 0x02"); /* FIXME */ + + return status; +} + +static int mxl111sf_i2c_readagain(struct mxl111sf_state *state, + u8 count, u8 *rbuf) +{ + u8 i2c_w_data[26]; + u8 i2c_r_data[24]; + u8 i = 0; + u8 fifo_status = 0; + int status = 0; + + mxl_i2c("read %d bytes", count); + + while ((fifo_status == 0) && (i++ < 5)) + fifo_status = mxl111sf_i2c_check_fifo(state); + + i2c_w_data[0] = 0xDD; + i2c_w_data[1] = 0x00; + + for (i = 2; i < 26; i++) + i2c_w_data[i] = 0xFE; + + for (i = 0; i < count; i++) { + i2c_w_data[2+(i*3)] = 0x0C; + i2c_w_data[3+(i*3)] = 0x00; + i2c_w_data[4+(i*3)] = 0x00; + } + + mxl111sf_i2c_get_data(state, 0, i2c_w_data, i2c_r_data); + + /* Check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("error!"); + } else { + for (i = 0; i < count; i++) { + rbuf[i] = i2c_r_data[(i*3)+1]; + mxl_i2c("%02x\t %02x", + i2c_r_data[(i*3)+1], + i2c_r_data[(i*3)+2]); + } + + status = 1; + } + + return status; +} + +#define HWI2C400 1 +static int mxl111sf_i2c_hw_xfer_msg(struct mxl111sf_state *state, + struct i2c_msg *msg) +{ + int i, k, ret = 0; + u16 index = 0; + u8 buf[26]; + u8 i2c_r_data[24]; + u16 block_len; + u16 left_over_len; + u8 rd_status[8]; + u8 ret_status; + u8 readbuff[26]; + + mxl_i2c("addr: 0x%02x, read buff len: %d, write buff len: %d", + msg->addr, (msg->flags & I2C_M_RD) ? msg->len : 0, + (!(msg->flags & I2C_M_RD)) ? msg->len : 0); + + for (index = 0; index < 26; index++) + buf[index] = USB_END_I2C_CMD; + + /* command to indicate data payload is destined for I2C interface */ + buf[0] = USB_WRITE_I2C_CMD; + buf[1] = 0x00; + + /* enable I2C interface */ + buf[2] = I2C_MUX_REG; + buf[3] = 0x80; + buf[4] = 0x00; + + /* enable I2C interface */ + buf[5] = I2C_MUX_REG; + buf[6] = 0x81; + buf[7] = 0x00; + + /* set Timeout register on I2C interface */ + buf[8] = 0x14; + buf[9] = 0xff; + buf[10] = 0x00; +#if 0 + /* enable Interrupts on I2C interface */ + buf[8] = 0x24; + buf[9] = 0xF7; + buf[10] = 0x00; +#endif + buf[11] = 0x24; + buf[12] = 0xF7; + buf[13] = 0x00; + + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* write data on I2C bus */ + if (!(msg->flags & I2C_M_RD) && (msg->len > 0)) { + mxl_i2c("%d\t%02x", msg->len, msg->buf[0]); + + /* control register on I2C interface to initialize I2C bus */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x5E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + /* I2C Slave device Address */ + buf[5] = I2C_SLAVE_ADDR_REG; + buf[6] = (msg->addr); + buf[7] = 0x00; + buf[8] = USB_END_I2C_CMD; + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for slave device status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK writing slave address %02x", + msg->addr); + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + /* I2C interface can do I2C operations in block of 8 bytes of + I2C data. calculation to figure out number of blocks of i2c + data required to program */ + block_len = (msg->len / 8); + left_over_len = (msg->len % 8); + index = 0; + + mxl_i2c("block_len %d, left_over_len %d", + block_len, left_over_len); + + for (index = 0; index < block_len; index++) { + for (i = 0; i < 8; i++) { + /* write data on I2C interface */ + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = msg->buf[(index*8)+i]; + buf[4+(i*3)] = 0x00; + } + + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK writing slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + } + + if (left_over_len) { + for (k = 0; k < 26; k++) + buf[k] = USB_END_I2C_CMD; + + buf[0] = 0x99; + buf[1] = 0x00; + + for (i = 0; i < left_over_len; i++) { + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = msg->buf[(index*8)+i]; + mxl_i2c("index = %d %d data %d", + index, i, msg->buf[(index*8)+i]); + buf[4+(i*3)] = 0x00; + } + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK writing slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + } + + /* issue I2C STOP after write */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x4E; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + } + + /* read data from I2C bus */ + if ((msg->flags & I2C_M_RD) && (msg->len > 0)) { + mxl_i2c("read buf len %d", msg->len); + + /* command to indicate data payload is + destined for I2C interface */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xDF; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + /* I2C xfer length */ + buf[5] = 0x14; + buf[6] = (msg->len & 0xFF); + buf[7] = 0; + + /* I2C slave device Address */ + buf[8] = I2C_SLAVE_ADDR_REG; + buf[9] = msg->addr; + buf[10] = 0x00; + buf[11] = USB_END_I2C_CMD; + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK reading slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + /* I2C interface can do I2C operations in block of 8 bytes of + I2C data. calculation to figure out number of blocks of + i2c data required to program */ + block_len = ((msg->len) / 8); + left_over_len = ((msg->len) % 8); + index = 0; + + mxl_i2c("block_len %d, left_over_len %d", + block_len, left_over_len); + + /* command to read data from I2C interface */ + buf[0] = USB_READ_I2C_CMD; + buf[1] = 0x00; + + for (index = 0; index < block_len; index++) { + /* setup I2C read request packet on I2C interface */ + for (i = 0; i < 8; i++) { + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = 0x00; + buf[4+(i*3)] = 0x00; + } + + ret = mxl111sf_i2c_get_data(state, 0, buf, i2c_r_data); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK reading slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + /* copy data from i2c data payload to read buffer */ + for (i = 0; i < 8; i++) { + rd_status[i] = i2c_r_data[(i*3)+2]; + + if (rd_status[i] == 0x04) { + if (i < 7) { + mxl_i2c("i2c fifo empty!" + " @ %d", i); + msg->buf[(index*8)+i] = + i2c_r_data[(i*3)+1]; + /* read again */ + ret_status = + mxl111sf_i2c_readagain( + state, 8-(i+1), + readbuff); + if (ret_status == 1) { + for (k = 0; + k < 8-(i+1); + k++) { + + msg->buf[(index*8)+(k+i+1)] = + readbuff[k]; + mxl_i2c("read data: %02x\t %02x", + msg->buf[(index*8)+(k+i)], + (index*8)+(k+i)); + mxl_i2c("read data: %02x\t %02x", + msg->buf[(index*8)+(k+i+1)], + readbuff[k]); + + } + goto stop_copy; + } else { + mxl_i2c("readagain " + "ERROR!"); + } + } else { + msg->buf[(index*8)+i] = + i2c_r_data[(i*3)+1]; + } + } else { + msg->buf[(index*8)+i] = + i2c_r_data[(i*3)+1]; + } + } +stop_copy: + ; + + } + + if (left_over_len) { + for (k = 0; k < 26; k++) + buf[k] = USB_END_I2C_CMD; + + buf[0] = 0xDD; + buf[1] = 0x00; + + for (i = 0; i < left_over_len; i++) { + buf[2+(i*3)] = I2C_DATA_REG; + buf[3+(i*3)] = 0x00; + buf[4+(i*3)] = 0x00; + } + ret = mxl111sf_i2c_get_data(state, 0, buf, + i2c_r_data); + + /* check for I2C NACK status */ + if (mxl111sf_i2c_check_status(state) == 1) { + mxl_i2c("NACK reading slave address %02x", + msg->addr); + + /* if NACK, stop I2C bus and exit */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + ret = -EIO; + goto exit; + } + + for (i = 0; i < left_over_len; i++) { + msg->buf[(block_len*8)+i] = + i2c_r_data[(i*3)+1]; + mxl_i2c("read data: %02x\t %02x", + i2c_r_data[(i*3)+1], + i2c_r_data[(i*3)+2]); + } + } + + /* indicate I2C interface to issue NACK + after next I2C read op */ + buf[0] = USB_WRITE_I2C_CMD; + buf[1] = 0x00; + + /* control register */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0x17; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + buf[5] = USB_END_I2C_CMD; + ret = mxl111sf_i2c_send_data(state, 0, buf); + + /* control register */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xC7; + buf[4] = (HWI2C400) ? 0x03 : 0x0D; + + } +exit: + /* STOP and disable I2C MUX */ + buf[0] = USB_WRITE_I2C_CMD; + buf[1] = 0x00; + + /* de-initilize I2C BUS */ + buf[5] = USB_END_I2C_CMD; + mxl111sf_i2c_send_data(state, 0, buf); + + /* Control Register */ + buf[2] = I2C_CONTROL_REG; + buf[3] = 0xDF; + buf[4] = 0x03; + + /* disable I2C interface */ + buf[5] = I2C_MUX_REG; + buf[6] = 0x00; + buf[7] = 0x00; + + /* de-initilize I2C BUS */ + buf[8] = USB_END_I2C_CMD; + mxl111sf_i2c_send_data(state, 0, buf); + + /* disable I2C interface */ + buf[2] = I2C_MUX_REG; + buf[3] = 0x81; + buf[4] = 0x00; + + /* disable I2C interface */ + buf[5] = I2C_MUX_REG; + buf[6] = 0x00; + buf[7] = 0x00; + + /* disable I2C interface */ + buf[8] = I2C_MUX_REG; + buf[9] = 0x00; + buf[10] = 0x00; + + buf[11] = USB_END_I2C_CMD; + mxl111sf_i2c_send_data(state, 0, buf); + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct mxl111sf_state *state = d->priv; + int hwi2c = (state->chip_rev > MXL111SF_V6); + int i, ret; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + ret = (hwi2c) ? + mxl111sf_i2c_hw_xfer_msg(state, &msg[i]) : + mxl111sf_i2c_sw_xfer_msg(state, &msg[i]); + if (mxl_fail(ret)) { + mxl_debug_adv("failed with error %d on i2c " + "transaction %d of %d, %sing %d bytes " + "to/from 0x%02x", ret, i+1, num, + (msg[i].flags & I2C_M_RD) ? + "read" : "writ", + msg[i].len, msg[i].addr); + + break; + } + } + + mutex_unlock(&d->i2c_mutex); + + return i == num ? num : -EREMOTEIO; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h new file mode 100644 index 000000000000..a57a45ffb9e4 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h @@ -0,0 +1,35 @@ +/* + * mxl111sf-i2c.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 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_USB_MXL111SF_I2C_H_ +#define _DVB_USB_MXL111SF_I2C_H_ + +#include + +int mxl111sf_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num); + +#endif /* _DVB_USB_MXL111SF_I2C_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c new file mode 100644 index 000000000000..b741b3a7a325 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c @@ -0,0 +1,343 @@ +/* + * mxl111sf-phy.c - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 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 "mxl111sf-phy.h" +#include "mxl111sf-reg.h" + +int mxl111sf_init_tuner_demod(struct mxl111sf_state *state) +{ + struct mxl111sf_reg_ctrl_info mxl_111_overwrite_default[] = { + {0x07, 0xff, 0x0c}, + {0x58, 0xff, 0x9d}, + {0x09, 0xff, 0x00}, + {0x06, 0xff, 0x06}, + {0xc8, 0xff, 0x40}, /* ED_LE_WIN_OLD = 0 */ + {0x8d, 0x01, 0x01}, /* NEGATE_Q */ + {0x32, 0xff, 0xac}, /* DIG_RFREFSELECT = 12 */ + {0x42, 0xff, 0x43}, /* DIG_REG_AMP = 4 */ + {0x74, 0xff, 0xc4}, /* SSPUR_FS_PRIO = 4 */ + {0x71, 0xff, 0xe6}, /* SPUR_ROT_PRIO_VAL = 1 */ + {0x83, 0xff, 0x64}, /* INF_FILT1_THD_SC = 100 */ + {0x85, 0xff, 0x64}, /* INF_FILT2_THD_SC = 100 */ + {0x88, 0xff, 0xf0}, /* INF_THD = 240 */ + {0x6f, 0xf0, 0xb0}, /* DFE_DLY = 11 */ + {0x00, 0xff, 0x01}, /* Change to page 1 */ + {0x81, 0xff, 0x11}, /* DSM_FERR_BYPASS = 1 */ + {0xf4, 0xff, 0x07}, /* DIG_FREQ_CORR = 1 */ + {0xd4, 0x1f, 0x0f}, /* SPUR_TEST_NOISE_TH = 15 */ + {0xd6, 0xff, 0x0c}, /* SPUR_TEST_NOISE_PAPR = 12 */ + {0x00, 0xff, 0x00}, /* Change to page 0 */ + {0, 0, 0} + }; + + mxl_debug("()"); + + return mxl111sf_ctrl_program_regs(state, mxl_111_overwrite_default); +} + +int mxl1x1sf_soft_reset(struct mxl111sf_state *state) +{ + int ret; + mxl_debug("()"); + + ret = mxl111sf_write_reg(state, 0xff, 0x00); /* AIC */ + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_write_reg(state, 0x02, 0x01); /* get out of reset */ + mxl_fail(ret); +fail: + return ret; +} + +int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode) +{ + int ret; + + mxl_debug("(%s)", MXL_SOC_MODE == mode ? + "MXL_SOC_MODE" : "MXL_TUNER_MODE"); + + /* set device mode */ + ret = mxl111sf_write_reg(state, 0x03, + MXL_SOC_MODE == mode ? 0x01 : 0x00); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg_mask(state, + 0x7d, 0x40, MXL_SOC_MODE == mode ? + 0x00 : /* enable impulse noise filter, + INF_BYP = 0 */ + 0x40); /* disable impulse noise filter, + INF_BYP = 1 */ + if (mxl_fail(ret)) + goto fail; + + state->device_mode = mode; +fail: + return ret; +} + +/* power up tuner */ +int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff) +{ + mxl_debug("(%d)", onoff); + + return mxl111sf_write_reg(state, 0x01, onoff ? 0x01 : 0x00); +} + +int mxl111sf_disable_656_port(struct mxl111sf_state *state) +{ + mxl_debug("()"); + + return mxl111sf_write_reg_mask(state, 0x12, 0x04, 0x00); +} + +int mxl111sf_enable_usb_output(struct mxl111sf_state *state) +{ + mxl_debug("()"); + + return mxl111sf_write_reg_mask(state, 0x17, 0x40, 0x00); +} + +/* initialize TSIF as input port of MxL1X1SF for MPEG2 data transfer */ +int mxl111sf_config_mpeg_in(struct mxl111sf_state *state, + unsigned int parallel_serial, + unsigned int msb_lsb_1st, + unsigned int clock_phase, + unsigned int mpeg_valid_pol, + unsigned int mpeg_sync_pol) +{ + int ret; + u8 mode, tmp; + + mxl_debug("(%u,%u,%u,%u,%u)", parallel_serial, msb_lsb_1st, + clock_phase, mpeg_valid_pol, mpeg_sync_pol); + + /* Enable PIN MUX */ + ret = mxl111sf_write_reg(state, V6_PIN_MUX_MODE_REG, V6_ENABLE_PIN_MUX); + mxl_fail(ret); + + /* Configure MPEG Clock phase */ + mxl111sf_read_reg(state, V6_MPEG_IN_CLK_INV_REG, &mode); + + if (clock_phase == TSIF_NORMAL) + mode &= ~V6_INVERTED_CLK_PHASE; + else + mode |= V6_INVERTED_CLK_PHASE; + + ret = mxl111sf_write_reg(state, V6_MPEG_IN_CLK_INV_REG, mode); + mxl_fail(ret); + + /* Configure data input mode, MPEG Valid polarity, MPEG Sync polarity + * Get current configuration */ + ret = mxl111sf_read_reg(state, V6_MPEG_IN_CTRL_REG, &mode); + mxl_fail(ret); + + /* Data Input mode */ + if (parallel_serial == TSIF_INPUT_PARALLEL) { + /* Disable serial mode */ + mode &= ~V6_MPEG_IN_DATA_SERIAL; + + /* Enable Parallel mode */ + mode |= V6_MPEG_IN_DATA_PARALLEL; + } else { + /* Disable Parallel mode */ + mode &= ~V6_MPEG_IN_DATA_PARALLEL; + + /* Enable Serial Mode */ + mode |= V6_MPEG_IN_DATA_SERIAL; + + /* If serial interface is chosen, configure + MSB or LSB order in transmission */ + ret = mxl111sf_read_reg(state, + V6_MPEG_INOUT_BIT_ORDER_CTRL_REG, + &tmp); + mxl_fail(ret); + + if (msb_lsb_1st == MPEG_SER_MSB_FIRST_ENABLED) + tmp |= V6_MPEG_SER_MSB_FIRST; + else + tmp &= ~V6_MPEG_SER_MSB_FIRST; + + ret = mxl111sf_write_reg(state, + V6_MPEG_INOUT_BIT_ORDER_CTRL_REG, + tmp); + mxl_fail(ret); + } + + /* MPEG Sync polarity */ + if (mpeg_sync_pol == TSIF_NORMAL) + mode &= ~V6_INVERTED_MPEG_SYNC; + else + mode |= V6_INVERTED_MPEG_SYNC; + + /* MPEG Valid polarity */ + if (mpeg_valid_pol == 0) + mode &= ~V6_INVERTED_MPEG_VALID; + else + mode |= V6_INVERTED_MPEG_VALID; + + ret = mxl111sf_write_reg(state, V6_MPEG_IN_CTRL_REG, mode); + mxl_fail(ret); + + return ret; +} + +int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size) +{ + static struct mxl111sf_reg_ctrl_info init_i2s[] = { + {0x1b, 0xff, 0x1e}, /* pin mux mode, Choose 656/I2S input */ + {0x15, 0x60, 0x60}, /* Enable I2S */ + {0x17, 0xe0, 0x20}, /* Input, MPEG MODE USB, + Inverted 656 Clock, I2S_SOFT_RESET, + 0 : Normal operation, 1 : Reset State */ +#if 0 + {0x12, 0x01, 0x00}, /* AUDIO_IRQ_CLR (Overflow Indicator) */ +#endif + {0x00, 0xff, 0x02}, /* Change to Control Page */ + {0x26, 0x0d, 0x0d}, /* I2S_MODE & BT656_SRC_SEL for FPGA only */ + {0x00, 0xff, 0x00}, + {0, 0, 0} + }; + int ret; + + mxl_debug("(0x%02x)", sample_size); + + ret = mxl111sf_ctrl_program_regs(state, init_i2s); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, V6_I2S_NUM_SAMPLES_REG, sample_size); + mxl_fail(ret); +fail: + return ret; +} + +int mxl111sf_disable_i2s_port(struct mxl111sf_state *state) +{ + static struct mxl111sf_reg_ctrl_info disable_i2s[] = { + {0x15, 0x40, 0x00}, + {0, 0, 0} + }; + + mxl_debug("()"); + + return mxl111sf_ctrl_program_regs(state, disable_i2s); +} + +int mxl111sf_config_i2s(struct mxl111sf_state *state, + u8 msb_start_pos, u8 data_width) +{ + int ret; + u8 tmp; + + mxl_debug("(0x%02x, 0x%02x)", msb_start_pos, data_width); + + ret = mxl111sf_read_reg(state, V6_I2S_STREAM_START_BIT_REG, &tmp); + if (mxl_fail(ret)) + goto fail; + + tmp &= 0xe0; + tmp |= msb_start_pos; + ret = mxl111sf_write_reg(state, V6_I2S_STREAM_START_BIT_REG, tmp); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, V6_I2S_STREAM_END_BIT_REG, &tmp); + if (mxl_fail(ret)) + goto fail; + + tmp &= 0xe0; + tmp |= data_width; + ret = mxl111sf_write_reg(state, V6_I2S_STREAM_END_BIT_REG, tmp); + mxl_fail(ret); +fail: + return ret; +} + +int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff) +{ + u8 val; + int ret; + + mxl_debug("(%d)", onoff); + + ret = mxl111sf_write_reg(state, 0x00, 0x02); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_read_reg(state, V8_SPI_MODE_REG, &val); + if (mxl_fail(ret)) + goto fail; + + if (onoff) + val |= 0x04; + else + val &= ~0x04; + + ret = mxl111sf_write_reg(state, V8_SPI_MODE_REG, val); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_write_reg(state, 0x00, 0x00); + mxl_fail(ret); +fail: + return ret; +} + +int mxl111sf_idac_config(struct mxl111sf_state *state, + u8 control_mode, u8 current_setting, + u8 current_value, u8 hysteresis_value) +{ + int ret; + u8 val; + /* current value will be set for both automatic & manual IDAC control */ + val = current_value; + + if (control_mode == IDAC_MANUAL_CONTROL) { + /* enable manual control of IDAC */ + val |= IDAC_MANUAL_CONTROL_BIT_MASK; + + if (current_setting == IDAC_CURRENT_SINKING_ENABLE) + /* enable current sinking in manual mode */ + val |= IDAC_CURRENT_SINKING_BIT_MASK; + else + /* disable current sinking in manual mode */ + val &= ~IDAC_CURRENT_SINKING_BIT_MASK; + } else { + /* disable manual control of IDAC */ + val &= ~IDAC_MANUAL_CONTROL_BIT_MASK; + + /* set hysteresis value reg: 0x0B<5:0> */ + ret = mxl111sf_write_reg(state, V6_IDAC_HYSTERESIS_REG, + (hysteresis_value & 0x3F)); + mxl_fail(ret); + } + + ret = mxl111sf_write_reg(state, V6_IDAC_SETTINGS_REG, val); + mxl_fail(ret); + + return ret; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h new file mode 100644 index 000000000000..f0756071d347 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h @@ -0,0 +1,53 @@ +/* + * mxl111sf-phy.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 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_USB_MXL111SF_PHY_H_ +#define _DVB_USB_MXL111SF_PHY_H_ + +#include "mxl111sf.h" + +int mxl1x1sf_soft_reset(struct mxl111sf_state *state); +int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode); +int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff); +int mxl111sf_disable_656_port(struct mxl111sf_state *state); +int mxl111sf_init_tuner_demod(struct mxl111sf_state *state); +int mxl111sf_enable_usb_output(struct mxl111sf_state *state); +int mxl111sf_config_mpeg_in(struct mxl111sf_state *state, + unsigned int parallel_serial, + unsigned int msb_lsb_1st, + unsigned int clock_phase, + unsigned int mpeg_valid_pol, + unsigned int mpeg_sync_pol); +int mxl111sf_config_i2s(struct mxl111sf_state *state, + u8 msb_start_pos, u8 data_width); +int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size); +int mxl111sf_disable_i2s_port(struct mxl111sf_state *state); +int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff); +int mxl111sf_idac_config(struct mxl111sf_state *state, + u8 control_mode, u8 current_setting, + u8 current_value, u8 hysteresis_value); + +#endif /* _DVB_USB_MXL111SF_PHY_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h new file mode 100644 index 000000000000..17831b0fb9db --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h @@ -0,0 +1,179 @@ +/* + * mxl111sf-reg.h - driver for the MaxLinear MXL111SF + * + * Copyright (C) 2010 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_USB_MXL111SF_REG_H_ +#define _DVB_USB_MXL111SF_REG_H_ + +#define CHIP_ID_REG 0xFC +#define TOP_CHIP_REV_ID_REG 0xFA + +#define V6_SNR_RB_LSB_REG 0x27 +#define V6_SNR_RB_MSB_REG 0x28 + +#define V6_N_ACCUMULATE_REG 0x11 +#define V6_RS_AVG_ERRORS_LSB_REG 0x2C +#define V6_RS_AVG_ERRORS_MSB_REG 0x2D + +#define V6_IRQ_STATUS_REG 0x24 +#define IRQ_MASK_FEC_LOCK 0x10 + +#define V6_SYNC_LOCK_REG 0x28 +#define SYNC_LOCK_MASK 0x10 + +#define V6_RS_LOCK_DET_REG 0x28 +#define RS_LOCK_DET_MASK 0x08 + +#define V6_INITACQ_NODETECT_REG 0x20 +#define V6_FORCE_NFFT_CPSIZE_REG 0x20 + +#define V6_CODE_RATE_TPS_REG 0x29 +#define V6_CODE_RATE_TPS_MASK 0x07 + + +#define V6_CP_LOCK_DET_REG 0x28 +#define V6_CP_LOCK_DET_MASK 0x04 + +#define V6_TPS_HIERACHY_REG 0x29 +#define V6_TPS_HIERARCHY_INFO_MASK 0x40 + +#define V6_MODORDER_TPS_REG 0x2A +#define V6_PARAM_CONSTELLATION_MASK 0x30 + +#define V6_MODE_TPS_REG 0x2A +#define V6_PARAM_FFT_MODE_MASK 0x0C + + +#define V6_CP_TPS_REG 0x29 +#define V6_PARAM_GI_MASK 0x30 + +#define V6_TPS_LOCK_REG 0x2A +#define V6_PARAM_TPS_LOCK_MASK 0x40 + +#define V6_FEC_PER_COUNT_REG 0x2E +#define V6_FEC_PER_SCALE_REG 0x2B +#define V6_FEC_PER_SCALE_MASK 0x03 +#define V6_FEC_PER_CLR_REG 0x20 +#define V6_FEC_PER_CLR_MASK 0x01 + +#define V6_PIN_MUX_MODE_REG 0x1B +#define V6_ENABLE_PIN_MUX 0x1E + +#define V6_I2S_NUM_SAMPLES_REG 0x16 + +#define V6_MPEG_IN_CLK_INV_REG 0x17 +#define V6_MPEG_IN_CTRL_REG 0x18 + +#define V6_INVERTED_CLK_PHASE 0x20 +#define V6_MPEG_IN_DATA_PARALLEL 0x01 +#define V6_MPEG_IN_DATA_SERIAL 0x02 + +#define V6_INVERTED_MPEG_SYNC 0x04 +#define V6_INVERTED_MPEG_VALID 0x08 + +#define TSIF_INPUT_PARALLEL 0 +#define TSIF_INPUT_SERIAL 1 +#define TSIF_NORMAL 0 + +#define V6_MPEG_INOUT_BIT_ORDER_CTRL_REG 0x19 +#define V6_MPEG_SER_MSB_FIRST 0x80 +#define MPEG_SER_MSB_FIRST_ENABLED 0x01 + +#define V6_656_I2S_BUFF_STATUS_REG 0x2F +#define V6_656_OVERFLOW_MASK_BIT 0x08 +#define V6_I2S_OVERFLOW_MASK_BIT 0x01 + +#define V6_I2S_STREAM_START_BIT_REG 0x14 +#define V6_I2S_STREAM_END_BIT_REG 0x15 +#define I2S_RIGHT_JUSTIFIED 0 +#define I2S_LEFT_JUSTIFIED 1 +#define I2S_DATA_FORMAT 2 + +#define V6_TUNER_LOOP_THRU_CONTROL_REG 0x09 +#define V6_ENABLE_LOOP_THRU 0x01 + +#define TOTAL_NUM_IF_OUTPUT_FREQ 16 + +#define TUNER_NORMAL_IF_SPECTRUM 0x0 +#define TUNER_INVERT_IF_SPECTRUM 0x10 + +#define V6_TUNER_IF_SEL_REG 0x06 +#define V6_TUNER_IF_FCW_REG 0x3C +#define V6_TUNER_IF_FCW_BYP_REG 0x3D +#define V6_RF_LOCK_STATUS_REG 0x23 + +#define NUM_DIG_TV_CHANNEL 1000 + +#define V6_DIG_CLK_FREQ_SEL_REG 0x07 +#define V6_REF_SYNTH_INT_REG 0x5C +#define V6_REF_SYNTH_REMAIN_REG 0x58 +#define V6_DIG_RFREFSELECT_REG 0x32 +#define V6_XTAL_CLK_OUT_GAIN_REG 0x31 +#define V6_TUNER_LOOP_THRU_CTRL_REG 0x09 +#define V6_DIG_XTAL_ENABLE_REG 0x06 +#define V6_DIG_XTAL_BIAS_REG 0x66 +#define V6_XTAL_CAP_REG 0x08 + +#define V6_GPO_CTRL_REG 0x18 +#define MXL_GPO_0 0x00 +#define MXL_GPO_1 0x01 +#define V6_GPO_0_MASK 0x10 +#define V6_GPO_1_MASK 0x20 + +#define V6_111SF_GPO_CTRL_REG 0x19 +#define MXL_111SF_GPO_1 0x00 +#define MXL_111SF_GPO_2 0x01 +#define MXL_111SF_GPO_3 0x02 +#define MXL_111SF_GPO_4 0x03 +#define MXL_111SF_GPO_5 0x04 +#define MXL_111SF_GPO_6 0x05 +#define MXL_111SF_GPO_7 0x06 + +#define MXL_111SF_GPO_0_MASK 0x01 +#define MXL_111SF_GPO_1_MASK 0x02 +#define MXL_111SF_GPO_2_MASK 0x04 +#define MXL_111SF_GPO_3_MASK 0x08 +#define MXL_111SF_GPO_4_MASK 0x10 +#define MXL_111SF_GPO_5_MASK 0x20 +#define MXL_111SF_GPO_6_MASK 0x40 + +#define V6_ATSC_CONFIG_REG 0x0A + +#define MXL_MODE_REG 0x03 +#define START_TUNE_REG 0x1C + +#define V6_IDAC_HYSTERESIS_REG 0x0B +#define V6_IDAC_SETTINGS_REG 0x0C +#define IDAC_MANUAL_CONTROL 1 +#define IDAC_CURRENT_SINKING_ENABLE 1 +#define IDAC_MANUAL_CONTROL_BIT_MASK 0x80 +#define IDAC_CURRENT_SINKING_BIT_MASK 0x40 + +#define V8_SPI_MODE_REG 0xE9 + +#define V6_DIG_RF_PWR_LSB_REG 0x46 +#define V6_DIG_RF_PWR_MSB_REG 0x47 + +#endif /* _DVB_USB_MXL111SF_REG_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c new file mode 100644 index 000000000000..ef4c65fcbb73 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c @@ -0,0 +1,527 @@ +/* + * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner + * + * Copyright (C) 2010 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 "mxl111sf-tuner.h" +#include "mxl111sf-phy.h" +#include "mxl111sf-reg.h" + +/* debug */ +static int mxl111sf_tuner_debug; +module_param_named(debug, mxl111sf_tuner_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define mxl_dbg(fmt, arg...) \ + if (mxl111sf_tuner_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +#define err pr_err + +/* ------------------------------------------------------------------------ */ + +struct mxl111sf_tuner_state { + struct mxl111sf_state *mxl_state; + + struct mxl111sf_tuner_config *cfg; + + enum mxl_if_freq if_freq; + + u32 frequency; + u32 bandwidth; +}; + +static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state, + u8 addr, u8 *data) +{ + return (state->cfg->read_reg) ? + state->cfg->read_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state, + u8 addr, u8 data) +{ + return (state->cfg->write_reg) ? + state->cfg->write_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +{ + return (state->cfg->program_regs) ? + state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : + -EINVAL; +} + +static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state, + int onoff) +{ + return (state->cfg->top_master_ctrl) ? + state->cfg->top_master_ctrl(state->mxl_state, onoff) : + -EINVAL; +} + +/* ------------------------------------------------------------------------ */ + +static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = { + {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3, + DIG_MODEINDEX, _A, _CSF, */ + {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */ + {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */ + {0, 0, 0} +}; + +/* ------------------------------------------------------------------------ */ + +static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq, + u8 bw) +{ + u8 filt_bw; + + /* set channel bandwidth */ + switch (bw) { + case 0: /* ATSC */ + filt_bw = 25; + break; + case 1: /* QAM */ + filt_bw = 69; + break; + case 6: + filt_bw = 21; + break; + case 7: + filt_bw = 42; + break; + case 8: + filt_bw = 63; + break; + default: + err("%s: invalid bandwidth setting!", __func__); + return NULL; + } + + /* calculate RF channel */ + freq /= 1000000; + + freq *= 64; +#if 0 + /* do round */ + freq += 0.5; +#endif + /* set bandwidth */ + mxl_phy_tune_rf[0].data = filt_bw; + + /* set RF */ + mxl_phy_tune_rf[1].data = (freq & 0xff); + mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff; + + /* start tune */ + return mxl_phy_tune_rf; +} + +static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state) +{ + int ret; + u8 ctrl; +#if 0 + u16 iffcw; + u32 if_freq; +#endif + mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)", + state->cfg->invert_spectrum, state->cfg->if_freq); + + /* set IF polarity */ + ctrl = state->cfg->invert_spectrum; + + ctrl |= state->cfg->if_freq; + + ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl); + if (mxl_fail(ret)) + goto fail; + +#if 0 + if_freq /= 1000000; + + /* do round */ + if_freq += 0.5; + + if (MXL_IF_LO == state->cfg->if_freq) { + ctrl = 0x08; + iffcw = (u16)(if_freq / (108 * 4096)); + } else if (MXL_IF_HI == state->cfg->if_freq) { + ctrl = 0x08; + iffcw = (u16)(if_freq / (216 * 4096)); + } else { + ctrl = 0; + iffcw = 0; + } + + ctrl |= (iffcw >> 8); +#endif + ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl); + if (mxl_fail(ret)) + goto fail; + + ctrl &= 0xf0; + ctrl |= 0x90; + + ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl); + if (mxl_fail(ret)) + goto fail; + +#if 0 + ctrl = iffcw & 0x00ff; +#endif + ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl); + if (mxl_fail(ret)) + goto fail; + + state->if_freq = state->cfg->if_freq; +fail: + return ret; +} + +static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + static struct mxl111sf_reg_ctrl_info *reg_ctrl_array; + int ret; + u8 mxl_mode; + + mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw); + + /* stop tune */ + ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0); + if (mxl_fail(ret)) + goto fail; + + /* check device mode */ + ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode); + if (mxl_fail(ret)) + goto fail; + + /* Fill out registers for channel tune */ + reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw); + if (!reg_ctrl_array) + return -EINVAL; + + ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array); + if (mxl_fail(ret)) + goto fail; + + if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) { + /* IF tuner mode only */ + mxl1x1sf_tuner_top_master_ctrl(state, 0); + mxl1x1sf_tuner_top_master_ctrl(state, 1); + mxl1x1sf_tuner_set_if_output_freq(state); + } + + ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1); + if (mxl_fail(ret)) + goto fail; + + if (state->cfg->ant_hunt) + state->cfg->ant_hunt(fe); +fail: + return ret; +} + +static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state, + int *rf_synth_lock, + int *ref_synth_lock) +{ + int ret; + u8 data; + + *rf_synth_lock = 0; + *ref_synth_lock = 0; + + ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data); + if (mxl_fail(ret)) + goto fail; + + *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0; + *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0; +fail: + return ret; +} + +#if 0 +static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state, + int onoff) +{ + return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG, + onoff ? 1 : 0); +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 delsys = c->delivery_system; + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int ret; + u8 bw; + + mxl_dbg("()"); + + switch (delsys) { + case SYS_ATSC: + case SYS_ATSCMH: + bw = 0; /* ATSC */ + break; + case SYS_DVBC_ANNEX_B: + bw = 1; /* US CABLE */ + break; + case SYS_DVBT: + switch (c->bandwidth_hz) { + case 6000000: + bw = 6; + break; + case 7000000: + bw = 7; + break; + case 8000000: + bw = 8; + break; + default: + err("%s: bandwidth not set!", __func__); + return -EINVAL; + } + break; + default: + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + ret = mxl1x1sf_tune_rf(fe, c->frequency, bw); + if (mxl_fail(ret)) + goto fail; + + state->frequency = c->frequency; + state->bandwidth = c->bandwidth_hz; +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#if 0 +static int mxl111sf_tuner_init(struct dvb_frontend *fe) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int ret; + + /* wake from standby handled by usb driver */ + + return ret; +} + +static int mxl111sf_tuner_sleep(struct dvb_frontend *fe) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int ret; + + /* enter standby mode handled by usb driver */ + + return ret; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + int rf_locked, ref_locked, ret; + + *status = 0; + + ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked); + if (mxl_fail(ret)) + goto fail; + mxl_info("%s%s", rf_locked ? "rf locked " : "", + ref_locked ? "ref locked" : ""); + + if ((rf_locked) || (ref_locked)) + *status |= TUNER_STATUS_LOCKED; +fail: + return ret; +} + +static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + u8 val1, val2; + int ret; + + *strength = 0; + + ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + + *strength = val1 | ((val2 & 0x07) << 8); +fail: + ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00); + mxl_fail(ret); + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + *bandwidth = state->bandwidth; + return 0; +} + +static int mxl111sf_tuner_get_if_frequency(struct dvb_frontend *fe, + u32 *frequency) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + + *frequency = 0; + + switch (state->if_freq) { + case MXL_IF_4_0: /* 4.0 MHz */ + *frequency = 4000000; + break; + case MXL_IF_4_5: /* 4.5 MHz */ + *frequency = 4500000; + break; + case MXL_IF_4_57: /* 4.57 MHz */ + *frequency = 4570000; + break; + case MXL_IF_5_0: /* 5.0 MHz */ + *frequency = 5000000; + break; + case MXL_IF_5_38: /* 5.38 MHz */ + *frequency = 5380000; + break; + case MXL_IF_6_0: /* 6.0 MHz */ + *frequency = 6000000; + break; + case MXL_IF_6_28: /* 6.28 MHz */ + *frequency = 6280000; + break; + case MXL_IF_7_2: /* 7.2 MHz */ + *frequency = 7200000; + break; + case MXL_IF_35_25: /* 35.25 MHz */ + *frequency = 35250000; + break; + case MXL_IF_36: /* 36 MHz */ + *frequency = 36000000; + break; + case MXL_IF_36_15: /* 36.15 MHz */ + *frequency = 36150000; + break; + case MXL_IF_44: /* 44 MHz */ + *frequency = 44000000; + break; + } + return 0; +} + +static int mxl111sf_tuner_release(struct dvb_frontend *fe) +{ + struct mxl111sf_tuner_state *state = fe->tuner_priv; + mxl_dbg("()"); + kfree(state); + fe->tuner_priv = NULL; + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { + .info = { + .name = "MaxLinear MxL111SF", +#if 0 + .frequency_min = , + .frequency_max = , + .frequency_step = , +#endif + }, +#if 0 + .init = mxl111sf_tuner_init, + .sleep = mxl111sf_tuner_sleep, +#endif + .set_params = mxl111sf_tuner_set_params, + .get_status = mxl111sf_tuner_get_status, + .get_rf_strength = mxl111sf_get_rf_strength, + .get_frequency = mxl111sf_tuner_get_frequency, + .get_bandwidth = mxl111sf_tuner_get_bandwidth, + .get_if_frequency = mxl111sf_tuner_get_if_frequency, + .release = mxl111sf_tuner_release, +}; + +struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, + struct mxl111sf_state *mxl_state, + struct mxl111sf_tuner_config *cfg) +{ + struct mxl111sf_tuner_state *state = NULL; + + mxl_dbg("()"); + + state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->mxl_state = mxl_state; + state->cfg = cfg; + + memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = state; + return fe; +} +EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach); + +MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver"); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h new file mode 100644 index 000000000000..ff333960b184 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h @@ -0,0 +1,89 @@ +/* + * mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner + * + * Copyright (C) 2010 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 __MXL111SF_TUNER_H__ +#define __MXL111SF_TUNER_H__ + +#include "dvb_frontend.h" + +#include "mxl111sf.h" + +enum mxl_if_freq { +#if 0 + MXL_IF_LO = 0x00, /* other IF < 9MHz */ +#endif + MXL_IF_4_0 = 0x01, /* 4.0 MHz */ + MXL_IF_4_5 = 0x02, /* 4.5 MHz */ + MXL_IF_4_57 = 0x03, /* 4.57 MHz */ + MXL_IF_5_0 = 0x04, /* 5.0 MHz */ + MXL_IF_5_38 = 0x05, /* 5.38 MHz */ + MXL_IF_6_0 = 0x06, /* 6.0 MHz */ + MXL_IF_6_28 = 0x07, /* 6.28 MHz */ + MXL_IF_7_2 = 0x08, /* 7.2 MHz */ + MXL_IF_35_25 = 0x09, /* 35.25 MHz */ + MXL_IF_36 = 0x0a, /* 36 MHz */ + MXL_IF_36_15 = 0x0b, /* 36.15 MHz */ + MXL_IF_44 = 0x0c, /* 44 MHz */ +#if 0 + MXL_IF_HI = 0x0f, /* other IF > 35 MHz and < 45 MHz */ +#endif +}; + +struct mxl111sf_tuner_config { + enum mxl_if_freq if_freq; + unsigned int invert_spectrum:1; + + int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data); + int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data); + int (*program_regs)(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info); + int (*top_master_ctrl)(struct mxl111sf_state *state, int onoff); + int (*ant_hunt)(struct dvb_frontend *fe); +}; + +/* ------------------------------------------------------------------------ */ + +#if defined(CONFIG_DVB_USB_MXL111SF) || \ + (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +extern +struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, + struct mxl111sf_state *mxl_state, + struct mxl111sf_tuner_config *cfg); +#else +static inline +struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, + struct mxl111sf_state *mxl_state + struct mxl111sf_tuner_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* __MXL111SF_TUNER_H__ */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c new file mode 100644 index 000000000000..efdcb15358f1 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -0,0 +1,1431 @@ +/* + * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#include +#include + +#include "mxl111sf.h" +#include "mxl111sf-reg.h" +#include "mxl111sf-phy.h" +#include "mxl111sf-i2c.h" +#include "mxl111sf-gpio.h" + +#include "mxl111sf-demod.h" +#include "mxl111sf-tuner.h" + +#include "lgdt3305.h" +#include "lg2160.h" + +int dvb_usb_mxl111sf_debug; +module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level " + "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able))."); + +int dvb_usb_mxl111sf_isoc; +module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644); +MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc)."); + +int dvb_usb_mxl111sf_spi; +module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644); +MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi)."); + +#define ANT_PATH_AUTO 0 +#define ANT_PATH_EXTERNAL 1 +#define ANT_PATH_INTERNAL 2 + +int dvb_usb_mxl111sf_rfswitch = +#if 0 + ANT_PATH_AUTO; +#else + ANT_PATH_EXTERNAL; +#endif + +module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644); +MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int)."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define deb_info pr_debug +#define deb_reg pr_debug +#define deb_adv pr_debug +#define err pr_err +#define info pr_info + +int mxl111sf_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + int ret; + u8 sndbuf[1+wlen]; + + deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen); + + memset(sndbuf, 0, 1+wlen); + + sndbuf[0] = cmd; + memcpy(&sndbuf[1], wbuf, wlen); + + ret = (wo) ? dvb_usbv2_generic_write(d, sndbuf, 1+wlen) : + dvb_usbv2_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen); + mxl_fail(ret); + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#define MXL_CMD_REG_READ 0xaa +#define MXL_CMD_REG_WRITE 0x55 + +int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data) +{ + u8 buf[2]; + int ret; + + ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2); + if (mxl_fail(ret)) { + mxl_debug("error reading reg: 0x%02x", addr); + goto fail; + } + + if (buf[0] == addr) + *data = buf[1]; + else { + err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x", + addr, buf[0], buf[1]); + ret = -EINVAL; + } + + deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data); +fail: + return ret; +} + +int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data) +{ + u8 buf[] = { addr, data }; + int ret; + + deb_reg("W: (0x%02x, 0x%02x)\n", addr, data); + + ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0); + if (mxl_fail(ret)) + err("error writing reg: 0x%02x, val: 0x%02x", addr, data); + return ret; +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_write_reg_mask(struct mxl111sf_state *state, + u8 addr, u8 mask, u8 data) +{ + int ret; + u8 val; + + if (mask != 0xff) { + ret = mxl111sf_read_reg(state, addr, &val); +#if 1 + /* dont know why this usually errors out on the first try */ + if (mxl_fail(ret)) + err("error writing addr: 0x%02x, mask: 0x%02x, " + "data: 0x%02x, retrying...", addr, mask, data); + + ret = mxl111sf_read_reg(state, addr, &val); +#endif + if (mxl_fail(ret)) + goto fail; + } + val &= ~mask; + val |= data; + + ret = mxl111sf_write_reg(state, addr, val); + mxl_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +{ + int i, ret = 0; + + for (i = 0; ctrl_reg_info[i].addr | + ctrl_reg_info[i].mask | + ctrl_reg_info[i].data; i++) { + + ret = mxl111sf_write_reg_mask(state, + ctrl_reg_info[i].addr, + ctrl_reg_info[i].mask, + ctrl_reg_info[i].data); + if (mxl_fail(ret)) { + err("failed on reg #%d (0x%02x)", i, + ctrl_reg_info[i].addr); + break; + } + } + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state) +{ + int ret; + u8 id, ver; + char *mxl_chip, *mxl_rev; + + if ((state->chip_id) && (state->chip_ver)) + return 0; + + ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id); + if (mxl_fail(ret)) + goto fail; + state->chip_id = id; + + ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver); + if (mxl_fail(ret)) + goto fail; + state->chip_ver = ver; + + switch (id) { + case 0x61: + mxl_chip = "MxL101SF"; + break; + case 0x63: + mxl_chip = "MxL111SF"; + break; + default: + mxl_chip = "UNKNOWN MxL1X1"; + break; + } + switch (ver) { + case 0x36: + state->chip_rev = MXL111SF_V6; + mxl_rev = "v6"; + break; + case 0x08: + state->chip_rev = MXL111SF_V8_100; + mxl_rev = "v8_100"; + break; + case 0x18: + state->chip_rev = MXL111SF_V8_200; + mxl_rev = "v8_200"; + break; + default: + state->chip_rev = 0; + mxl_rev = "UNKNOWN REVISION"; + break; + } + info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver); +fail: + return ret; +} + +#define get_chip_info(state) \ +({ \ + int ___ret; \ + ___ret = mxl1x1sf_get_chip_info(state); \ + if (mxl_fail(___ret)) { \ + mxl_debug("failed to get chip info" \ + " on first probe attempt"); \ + ___ret = mxl1x1sf_get_chip_info(state); \ + if (mxl_fail(___ret)) \ + err("failed to get chip info during probe"); \ + else \ + mxl_debug("probe needed a retry " \ + "in order to succeed."); \ + } \ + ___ret; \ +}) + +/* ------------------------------------------------------------------------ */ +#if 0 +static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + /* power control depends on which adapter is being woken: + * save this for init, instead, via mxl111sf_adap_fe_init */ + return 0; +} +#endif + +static int mxl111sf_adap_fe_init(struct dvb_frontend *fe) +{ + struct dvb_usb_device *d = fe_to_d(fe); + struct mxl111sf_state *state = fe_to_priv(fe); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; + int err; + + /* exit if we didnt initialize the driver yet */ + if (!state->chip_id) { + mxl_debug("driver not yet initialized, exit."); + goto fail; + } + + deb_info("%s()\n", __func__); + + mutex_lock(&state->fe_lock); + + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(d->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + err = mxl1x1sf_soft_reset(state); + mxl_fail(err); + err = mxl111sf_init_tuner_demod(state); + mxl_fail(err); + err = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + + mxl_fail(err); + mxl111sf_enable_usb_output(state); + mxl_fail(err); + mxl1x1sf_top_master_ctrl(state, 1); + mxl_fail(err); + + if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) && + (state->chip_rev > MXL111SF_V6)) { + mxl111sf_config_pin_mux_modes(state, + PIN_MUX_TS_SPI_IN_MODE_1); + mxl_fail(err); + } + err = mxl111sf_init_port_expander(state); + if (!mxl_fail(err)) { + state->gpio_mode = adap_state->gpio_mode; + err = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + mxl_fail(err); +#if 0 + err = fe->ops.init(fe); +#endif + msleep(100); /* add short delay after enabling + * the demod before touching it */ + } + + return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0; +fail: + return -ENODEV; +} + +static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe) +{ + struct mxl111sf_state *state = fe_to_priv(fe); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; + int err; + + /* exit if we didnt initialize the driver yet */ + if (!state->chip_id) { + mxl_debug("driver not yet initialized, exit."); + goto fail; + } + + deb_info("%s()\n", __func__); + + err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0; + + mutex_unlock(&state->fe_lock); + + return err; +fail: + return -ENODEV; +} + + +static int mxl111sf_ep6_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct mxl111sf_state *state = fe_to_priv(fe); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id]; + int ret = 0; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + ret = mxl111sf_config_mpeg_in(state, 1, 1, + adap_state->ep6_clockphase, + 0, 0); + mxl_fail(ret); +#if 0 + } else { + ret = mxl111sf_disable_656_port(state); + mxl_fail(ret); +#endif + } + + return ret; +} + +static int mxl111sf_ep5_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct mxl111sf_state *state = fe_to_priv(fe); + int ret = 0; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + + ret = mxl111sf_init_i2s_port(state, 200); + mxl_fail(ret); + ret = mxl111sf_config_i2s(state, 0, 15); + mxl_fail(ret); + } else { + ret = mxl111sf_disable_i2s_port(state); + mxl_fail(ret); + } + if (state->chip_rev > MXL111SF_V6) + ret = mxl111sf_config_spi(state, onoff); + mxl_fail(ret); + + return ret; +} + +static int mxl111sf_ep4_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct mxl111sf_state *state = fe_to_priv(fe); + int ret = 0; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + } + + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static struct lgdt3305_config hauppauge_lgdt3305_config = { + .i2c_addr = 0xb2 >> 1, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .qam_if_khz = 6000, + .vsb_if_khz = 6000, +}; + +static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct mxl111sf_state *state = d_to_priv(d); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(d->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_ATSC; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + adap->fe[fe_id] = dvb_attach(lgdt3305_attach, + &hauppauge_lgdt3305_config, + &d->i2c_adap); + if (adap->fe[fe_id]) { + state->num_frontends++; + adap_state->fe_init = adap->fe[fe_id]->ops.init; + adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; + adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct lg2160_config hauppauge_lg2160_config = { + .lg_chip = LG2160, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, +}; + +static int mxl111sf_lg2160_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct mxl111sf_state *state = d_to_priv(d); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(d->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe[fe_id] = dvb_attach(lg2160_attach, + &hauppauge_lg2160_config, + &d->i2c_adap); + if (adap->fe[fe_id]) { + state->num_frontends++; + adap_state->fe_init = adap->fe[fe_id]->ops.init; + adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; + adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct lg2160_config hauppauge_lg2161_1019_config = { + .lg_chip = LG2161_1019, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 2, /* LG2161_OIF_SPI_MAS */ +}; + +static struct lg2160_config hauppauge_lg2161_1040_config = { + .lg_chip = LG2161_1040, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 4, /* LG2161_OIF_SPI_MAS */ +}; + +static int mxl111sf_lg2161_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct mxl111sf_state *state = d_to_priv(d); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(d->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe[fe_id] = dvb_attach(lg2160_attach, + (MXL111SF_V8_200 == state->chip_rev) ? + &hauppauge_lg2161_1040_config : + &hauppauge_lg2161_1019_config, + &d->i2c_adap); + if (adap->fe[fe_id]) { + state->num_frontends++; + adap_state->fe_init = adap->fe[fe_id]->ops.init; + adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; + adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct lg2160_config hauppauge_lg2161_1019_ep6_config = { + .lg_chip = LG2161_1019, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 1, /* LG2161_OIF_SERIAL_TS */ +}; + +static struct lg2160_config hauppauge_lg2161_1040_ep6_config = { + .lg_chip = LG2161_1040, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 7, /* LG2161_OIF_SERIAL_TS */ +}; + +static int mxl111sf_lg2161_ep6_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct mxl111sf_state *state = d_to_priv(d); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(d->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 0; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe[fe_id] = dvb_attach(lg2160_attach, + (MXL111SF_V8_200 == state->chip_rev) ? + &hauppauge_lg2161_1040_ep6_config : + &hauppauge_lg2161_1019_ep6_config, + &d->i2c_adap); + if (adap->fe[fe_id]) { + state->num_frontends++; + adap_state->fe_init = adap->fe[fe_id]->ops.init; + adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; + adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct mxl111sf_demod_config mxl_demod_config = { + .read_reg = mxl111sf_read_reg, + .write_reg = mxl111sf_write_reg, + .program_regs = mxl111sf_ctrl_program_regs, +}; + +static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap, u8 fe_id) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct mxl111sf_state *state = d_to_priv(d); + struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id]; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(d->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_DVBT; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_SOC_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + /* dont care if this fails */ + mxl111sf_init_port_expander(state); + + adap->fe[fe_id] = dvb_attach(mxl111sf_demod_attach, state, + &mxl_demod_config); + if (adap->fe[fe_id]) { + state->num_frontends++; + adap_state->fe_init = adap->fe[fe_id]->ops.init; + adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep; + adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state, + int antpath) +{ + return mxl111sf_idac_config(state, 1, 1, + (antpath == ANT_PATH_INTERNAL) ? + 0x3f : 0x00, 0); +} + +#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \ + err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \ + __func__, __LINE__, \ + (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \ + pwr0, pwr1, pwr2, pwr3) + +#define ANT_HUNT_SLEEP 90 +#define ANT_EXT_TWEAK 0 + +static int mxl111sf_ant_hunt(struct dvb_frontend *fe) +{ + struct mxl111sf_state *state = fe_to_priv(fe); + int antctrl = dvb_usb_mxl111sf_rfswitch; + + u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2; + + /* FIXME: must force EXTERNAL for QAM - done elsewhere */ + mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ? + ANT_PATH_EXTERNAL : antctrl); + + if (antctrl == ANT_PATH_AUTO) { +#if 0 + msleep(ANT_HUNT_SLEEP); +#endif + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA); + + mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); + msleep(ANT_HUNT_SLEEP); + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0); + + mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); + msleep(ANT_HUNT_SLEEP); + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1); + + mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL); + msleep(ANT_HUNT_SLEEP); + fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2); + + if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) { + /* return with EXTERNAL enabled */ + mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); + DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA, + rxPwr0, rxPwr1, rxPwr2); + } else { + /* return with INTERNAL enabled */ + DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA, + rxPwr0, rxPwr1, rxPwr2); + } + } + return 0; +} + +static struct mxl111sf_tuner_config mxl_tuner_config = { + .if_freq = MXL_IF_6_0, /* applies to external IF output, only */ + .invert_spectrum = 0, + .read_reg = mxl111sf_read_reg, + .write_reg = mxl111sf_write_reg, + .program_regs = mxl111sf_ctrl_program_regs, + .top_master_ctrl = mxl1x1sf_top_master_ctrl, + .ant_hunt = mxl111sf_ant_hunt, +}; + +static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) +{ + struct mxl111sf_state *state = adap_to_priv(adap); + int i; + + deb_adv("%s()\n", __func__); + + for (i = 0; i < state->num_frontends; i++) { + if (dvb_attach(mxl111sf_tuner_attach, adap->fe[i], state, + &mxl_tuner_config) == NULL) + return -EIO; + adap->fe[i]->ops.read_signal_strength = adap->fe[i]->ops.tuner_ops.get_rf_strength; + } + + return 0; +} + +static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +struct i2c_algorithm mxl111sf_i2c_algo = { + .master_xfer = mxl111sf_i2c_xfer, + .functionality = mxl111sf_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif +}; + +static int mxl111sf_init(struct dvb_usb_device *d) +{ + struct mxl111sf_state *state = d_to_priv(d); + int ret; + static u8 eeprom[256]; + struct i2c_client c; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + err("failed to get chip info during probe"); + + mutex_init(&state->fe_lock); + + if (state->chip_rev > MXL111SF_V6) + mxl111sf_config_pin_mux_modes(state, PIN_MUX_TS_SPI_IN_MODE_1); + + c.adapter = &d->i2c_adap; + c.addr = 0xa0 >> 1; + + ret = tveeprom_read(&c, eeprom, sizeof(eeprom)); + if (mxl_fail(ret)) + return 0; + tveeprom_hauppauge_analog(&c, &state->tv, (0x84 == eeprom[0xa0]) ? + eeprom + 0xa0 : eeprom + 0x80); +#if 0 + switch (state->tv.model) { + case 117001: + case 126001: + case 138001: + break; + default: + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", + __func__, state->tv.model); + } +#endif + return 0; +} + +static int mxl111sf_frontend_attach_dvbt(struct dvb_usb_adapter *adap) +{ + return mxl111sf_attach_demod(adap, 0); +} + +static int mxl111sf_frontend_attach_atsc(struct dvb_usb_adapter *adap) +{ + return mxl111sf_lgdt3305_frontend_attach(adap, 0); +} + +static int mxl111sf_frontend_attach_mh(struct dvb_usb_adapter *adap) +{ + return mxl111sf_lg2160_frontend_attach(adap, 0); +} + +static int mxl111sf_frontend_attach_atsc_mh(struct dvb_usb_adapter *adap) +{ + int ret; + deb_info("%s\n", __func__); + + ret = mxl111sf_lgdt3305_frontend_attach(adap, 0); + if (ret < 0) + return ret; + + ret = mxl111sf_attach_demod(adap, 1); + if (ret < 0) + return ret; + + ret = mxl111sf_lg2160_frontend_attach(adap, 2); + if (ret < 0) + return ret; + + return ret; +} + +static int mxl111sf_frontend_attach_mercury(struct dvb_usb_adapter *adap) +{ + int ret; + deb_info("%s\n", __func__); + + ret = mxl111sf_lgdt3305_frontend_attach(adap, 0); + if (ret < 0) + return ret; + + ret = mxl111sf_attach_demod(adap, 1); + if (ret < 0) + return ret; + + ret = mxl111sf_lg2161_ep6_frontend_attach(adap, 2); + if (ret < 0) + return ret; + + return ret; +} + +static int mxl111sf_frontend_attach_mercury_mh(struct dvb_usb_adapter *adap) +{ + int ret; + deb_info("%s\n", __func__); + + ret = mxl111sf_attach_demod(adap, 0); + if (ret < 0) + return ret; + + if (dvb_usb_mxl111sf_spi) + ret = mxl111sf_lg2161_frontend_attach(adap, 1); + else + ret = mxl111sf_lg2161_ep6_frontend_attach(adap, 1); + + return ret; +} + +static void mxl111sf_stream_config_bulk(struct usb_data_stream_properties *stream, u8 endpoint) +{ + deb_info("%s: endpoint=%d size=8192\n", __func__, endpoint); + stream->type = USB_BULK; + stream->count = 5; + stream->endpoint = endpoint; + stream->u.bulk.buffersize = 8192; +} + +static void mxl111sf_stream_config_isoc(struct usb_data_stream_properties *stream, + u8 endpoint, int framesperurb, int framesize) +{ + deb_info("%s: endpoint=%d size=%d\n", __func__, endpoint, + framesperurb * framesize); + stream->type = USB_ISOC; + stream->count = 5; + stream->endpoint = endpoint; + stream->u.isoc.framesperurb = framesperurb; + stream->u.isoc.framesize = framesize; + stream->u.isoc.interval = 1; +} + +/* DVB USB Driver stuff */ + +/* dvbt mxl111sf + * bulk EP4/BULK/5/8192 + * isoc EP4/ISOC/5/96/564 + */ +static int mxl111sf_get_stream_config_dvbt(struct dvb_frontend *fe, + u8 *ts_type, struct usb_data_stream_properties *stream) +{ + deb_info("%s: fe=%d\n", __func__, fe->id); + + *ts_type = DVB_USB_FE_TS_TYPE_188; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 4, 96, 564); + else + mxl111sf_stream_config_bulk(stream, 4); + return 0; +} + +static struct dvb_usb_device_properties mxl111sf_props_dvbt = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct mxl111sf_state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .i2c_algo = &mxl111sf_i2c_algo, + .frontend_attach = mxl111sf_frontend_attach_dvbt, + .tuner_attach = mxl111sf_attach_tuner, + .init = mxl111sf_init, + .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, + .get_stream_config = mxl111sf_get_stream_config_dvbt, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), + } + } +}; + +/* atsc lgdt3305 + * bulk EP6/BULK/5/8192 + * isoc EP6/ISOC/5/24/3072 + */ +static int mxl111sf_get_stream_config_atsc(struct dvb_frontend *fe, + u8 *ts_type, struct usb_data_stream_properties *stream) +{ + deb_info("%s: fe=%d\n", __func__, fe->id); + + *ts_type = DVB_USB_FE_TS_TYPE_188; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 6, 24, 3072); + else + mxl111sf_stream_config_bulk(stream, 6); + return 0; +} + +static struct dvb_usb_device_properties mxl111sf_props_atsc = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct mxl111sf_state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .i2c_algo = &mxl111sf_i2c_algo, + .frontend_attach = mxl111sf_frontend_attach_atsc, + .tuner_attach = mxl111sf_attach_tuner, + .init = mxl111sf_init, + .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, + .get_stream_config = mxl111sf_get_stream_config_atsc, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), + } + } +}; + +/* mh lg2160 + * bulk EP5/BULK/5/8192/RAW + * isoc EP5/ISOC/5/96/200/RAW + */ +static int mxl111sf_get_stream_config_mh(struct dvb_frontend *fe, + u8 *ts_type, struct usb_data_stream_properties *stream) +{ + deb_info("%s: fe=%d\n", __func__, fe->id); + + *ts_type = DVB_USB_FE_TS_TYPE_RAW; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 5, 96, 200); + else + mxl111sf_stream_config_bulk(stream, 5); + return 0; +} + +static struct dvb_usb_device_properties mxl111sf_props_mh = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct mxl111sf_state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .i2c_algo = &mxl111sf_i2c_algo, + .frontend_attach = mxl111sf_frontend_attach_mh, + .tuner_attach = mxl111sf_attach_tuner, + .init = mxl111sf_init, + .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, + .get_stream_config = mxl111sf_get_stream_config_mh, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), + } + } +}; + +/* atsc mh lgdt3305 mxl111sf lg2160 + * bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP5/BULK/5/8192/RAW + * isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW + */ +static int mxl111sf_get_stream_config_atsc_mh(struct dvb_frontend *fe, + u8 *ts_type, struct usb_data_stream_properties *stream) +{ + deb_info("%s: fe=%d\n", __func__, fe->id); + + if (fe->id == 0) { + *ts_type = DVB_USB_FE_TS_TYPE_188; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 6, 24, 3072); + else + mxl111sf_stream_config_bulk(stream, 6); + } else if (fe->id == 1) { + *ts_type = DVB_USB_FE_TS_TYPE_188; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 4, 96, 564); + else + mxl111sf_stream_config_bulk(stream, 4); + } else if (fe->id == 2) { + *ts_type = DVB_USB_FE_TS_TYPE_RAW; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 5, 96, 200); + else + mxl111sf_stream_config_bulk(stream, 5); + } + return 0; +} + +static int mxl111sf_streaming_ctrl_atsc_mh(struct dvb_frontend *fe, int onoff) +{ + deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); + + if (fe->id == 0) + return mxl111sf_ep6_streaming_ctrl(fe, onoff); + else if (fe->id == 1) + return mxl111sf_ep4_streaming_ctrl(fe, onoff); + else if (fe->id == 2) + return mxl111sf_ep5_streaming_ctrl(fe, onoff); + return 0; +} + +static struct dvb_usb_device_properties mxl111sf_props_atsc_mh = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct mxl111sf_state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .i2c_algo = &mxl111sf_i2c_algo, + .frontend_attach = mxl111sf_frontend_attach_atsc_mh, + .tuner_attach = mxl111sf_attach_tuner, + .init = mxl111sf_init, + .streaming_ctrl = mxl111sf_streaming_ctrl_atsc_mh, + .get_stream_config = mxl111sf_get_stream_config_atsc_mh, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), + } + } +}; + +/* mercury lgdt3305 mxl111sf lg2161 + * tp bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP6/BULK/5/8192/RAW + * tp isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP6/ISOC/5/24/3072/RAW + * spi bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP5/BULK/5/8192/RAW + * spi isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW + */ +static int mxl111sf_get_stream_config_mercury(struct dvb_frontend *fe, + u8 *ts_type, struct usb_data_stream_properties *stream) +{ + deb_info("%s: fe=%d\n", __func__, fe->id); + + if (fe->id == 0) { + *ts_type = DVB_USB_FE_TS_TYPE_188; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 6, 24, 3072); + else + mxl111sf_stream_config_bulk(stream, 6); + } else if (fe->id == 1) { + *ts_type = DVB_USB_FE_TS_TYPE_188; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 4, 96, 564); + else + mxl111sf_stream_config_bulk(stream, 4); + } else if (fe->id == 2 && dvb_usb_mxl111sf_spi) { + *ts_type = DVB_USB_FE_TS_TYPE_RAW; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 5, 96, 200); + else + mxl111sf_stream_config_bulk(stream, 5); + } else if (fe->id == 2 && !dvb_usb_mxl111sf_spi) { + *ts_type = DVB_USB_FE_TS_TYPE_RAW; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 6, 24, 3072); + else + mxl111sf_stream_config_bulk(stream, 6); + } + return 0; +} + +static int mxl111sf_streaming_ctrl_mercury(struct dvb_frontend *fe, int onoff) +{ + deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); + + if (fe->id == 0) + return mxl111sf_ep6_streaming_ctrl(fe, onoff); + else if (fe->id == 1) + return mxl111sf_ep4_streaming_ctrl(fe, onoff); + else if (fe->id == 2 && dvb_usb_mxl111sf_spi) + return mxl111sf_ep5_streaming_ctrl(fe, onoff); + else if (fe->id == 2 && !dvb_usb_mxl111sf_spi) + return mxl111sf_ep6_streaming_ctrl(fe, onoff); + return 0; +} + +static struct dvb_usb_device_properties mxl111sf_props_mercury = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct mxl111sf_state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .i2c_algo = &mxl111sf_i2c_algo, + .frontend_attach = mxl111sf_frontend_attach_mercury, + .tuner_attach = mxl111sf_attach_tuner, + .init = mxl111sf_init, + .streaming_ctrl = mxl111sf_streaming_ctrl_mercury, + .get_stream_config = mxl111sf_get_stream_config_mercury, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), + } + } +}; + +/* mercury mh mxl111sf lg2161 + * tp bulk EP4/BULK/5/8192 EP6/BULK/5/8192/RAW + * tp isoc EP4/ISOC/5/96/564 EP6/ISOC/5/24/3072/RAW + * spi bulk EP4/BULK/5/8192 EP5/BULK/5/8192/RAW + * spi isoc EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW + */ +static int mxl111sf_get_stream_config_mercury_mh(struct dvb_frontend *fe, + u8 *ts_type, struct usb_data_stream_properties *stream) +{ + deb_info("%s: fe=%d\n", __func__, fe->id); + + if (fe->id == 0) { + *ts_type = DVB_USB_FE_TS_TYPE_188; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 4, 96, 564); + else + mxl111sf_stream_config_bulk(stream, 4); + } else if (fe->id == 1 && dvb_usb_mxl111sf_spi) { + *ts_type = DVB_USB_FE_TS_TYPE_RAW; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 5, 96, 200); + else + mxl111sf_stream_config_bulk(stream, 5); + } else if (fe->id == 1 && !dvb_usb_mxl111sf_spi) { + *ts_type = DVB_USB_FE_TS_TYPE_RAW; + if (dvb_usb_mxl111sf_isoc) + mxl111sf_stream_config_isoc(stream, 6, 24, 3072); + else + mxl111sf_stream_config_bulk(stream, 6); + } + return 0; +} + +static int mxl111sf_streaming_ctrl_mercury_mh(struct dvb_frontend *fe, int onoff) +{ + deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); + + if (fe->id == 0) + return mxl111sf_ep4_streaming_ctrl(fe, onoff); + else if (fe->id == 1 && dvb_usb_mxl111sf_spi) + return mxl111sf_ep5_streaming_ctrl(fe, onoff); + else if (fe->id == 1 && !dvb_usb_mxl111sf_spi) + return mxl111sf_ep6_streaming_ctrl(fe, onoff); + return 0; +} + +static struct dvb_usb_device_properties mxl111sf_props_mercury_mh = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct mxl111sf_state), + + .generic_bulk_ctrl_endpoint = 0x02, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .i2c_algo = &mxl111sf_i2c_algo, + .frontend_attach = mxl111sf_frontend_attach_mercury_mh, + .tuner_attach = mxl111sf_attach_tuner, + .init = mxl111sf_init, + .streaming_ctrl = mxl111sf_streaming_ctrl_mercury_mh, + .get_stream_config = mxl111sf_get_stream_config_mercury_mh, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1), + } + } +}; + +static const struct usb_device_id mxl111sf_id_table[] = { + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602, &mxl111sf_props_mh, "HCW 126xxx", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604, &mxl111sf_props_dvbt, "Hauppauge 126xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a, &mxl111sf_props_mh, "HCW 126xxx", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c, &mxl111sf_props_dvbt, "Hauppauge 126xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702, &mxl111sf_props_mh, "HCW 117xxx", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704, &mxl111sf_props_dvbt, "Hauppauge 117xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764, &mxl111sf_props_dvbt, "Hauppauge 117xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612, &mxl111sf_props_mercury_mh, "Hauppauge 126xxx", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613, &mxl111sf_props_mercury, "Hauppauge WinTV-Aero-M", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a, &mxl111sf_props_mercury_mh, "Hauppauge 126xxx", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b, &mxl111sf_props_mercury, "Hauppauge WinTV-Aero-M", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) }, + { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) }, + { } +}; +MODULE_DEVICE_TABLE(usb, mxl111sf_id_table); + +static struct usb_driver mxl111sf_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = mxl111sf_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(mxl111sf_usb_driver); + +MODULE_AUTHOR("Michael Krufky "); +MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h new file mode 100644 index 000000000000..9816de86e48c --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#ifndef _DVB_USB_MXL111SF_H_ +#define _DVB_USB_MXL111SF_H_ + +#ifdef DVB_USB_LOG_PREFIX +#undef DVB_USB_LOG_PREFIX +#endif +#define DVB_USB_LOG_PREFIX "mxl111sf" +#include "dvb_usb.h" +#include + +#define MXL_EP1_REG_READ 1 +#define MXL_EP2_REG_WRITE 2 +#define MXL_EP3_INTERRUPT 3 +#define MXL_EP4_MPEG2 4 +#define MXL_EP5_I2S 5 +#define MXL_EP6_656 6 +#define MXL_EP6_MPEG2 6 + +#ifdef USING_ENUM_mxl111sf_current_mode +enum mxl111sf_current_mode { + mxl_mode_dvbt = MXL_EP4_MPEG2, + mxl_mode_mh = MXL_EP5_I2S, + mxl_mode_atsc = MXL_EP6_MPEG2, +}; +#endif + +enum mxl111sf_gpio_port_expander { + mxl111sf_gpio_hw, + mxl111sf_PCA9534, +}; + +struct mxl111sf_adap_state { + int alt_mode; + int gpio_mode; + int device_mode; + int ep6_clockphase; + int (*fe_init)(struct dvb_frontend *); + int (*fe_sleep)(struct dvb_frontend *); +}; + +struct mxl111sf_state { + struct dvb_usb_device *d; + + enum mxl111sf_gpio_port_expander gpio_port_expander; + u8 port_expander_addr; + + u8 chip_id; + u8 chip_ver; +#define MXL111SF_V6 1 +#define MXL111SF_V8_100 2 +#define MXL111SF_V8_200 3 + u8 chip_rev; + +#ifdef USING_ENUM_mxl111sf_current_mode + enum mxl111sf_current_mode current_mode; +#endif + +#define MXL_TUNER_MODE 0 +#define MXL_SOC_MODE 1 +#define MXL_DEV_MODE_MASK 0x01 +#if 1 + int device_mode; +#endif + /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), + EP5 BULK transfer (atsc-mh), + EP6 BULK transfer (atsc/qam), + use usb alt setting 2 for EP4 BULK transfer (dvb-t), + EP5 ISOC transfer (atsc-mh), + EP6 ISOC transfer (atsc/qam), + */ + int alt_mode; + int gpio_mode; + struct tveeprom tv; + + struct mutex fe_lock; + u8 num_frontends; + struct mxl111sf_adap_state adap_state[3]; +}; + +int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data); +int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data); + +struct mxl111sf_reg_ctrl_info { + u8 addr; + u8 mask; + u8 data; +}; + +int mxl111sf_write_reg_mask(struct mxl111sf_state *state, + u8 addr, u8 mask, u8 data); +int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info); + +/* needed for hardware i2c functions in mxl111sf-i2c.c: + * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */ +int mxl111sf_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen); + +#define mxl_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt "\n", __func__, ##arg) + +#define mxl_info(fmt, arg...) \ + mxl_printk(KERN_INFO, fmt, ##arg) + +extern int dvb_usb_mxl111sf_debug; +#define mxl_debug(fmt, arg...) \ + if (dvb_usb_mxl111sf_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +#define MXL_I2C_DBG 0x04 +#define MXL_ADV_DBG 0x10 +#define mxl_debug_adv(fmt, arg...) \ + if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +#define mxl_i2c(fmt, arg...) \ + if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +#define mxl_i2c_adv(fmt, arg...) \ + if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \ + (MXL_I2C_DBG | MXL_ADV_DBG)) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +/* The following allows the mxl_fail() macro defined below to work + * in externel modules, such as mxl111sf-tuner.ko, even though + * dvb_usb_mxl111sf_debug is not defined within those modules */ +#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__)) +#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG +#else +#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug +#endif + +#define mxl_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \ + mxl_printk(KERN_ERR, "error %d on line %d", \ + ret, __LINE__); \ + __ret; \ +}) + +#endif /* _DVB_USB_MXL111SF_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c new file mode 100644 index 000000000000..a2d1e5b9d9d4 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -0,0 +1,1259 @@ +/* + * Realtek RTL28xxU DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2011 Antti Palosaari + * Copyright (C) 2012 Thomas Mair + * + * This program is free software; 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 "rtl28xxu.h" + +#include "rtl2830.h" +#include "rtl2832.h" + +#include "qt1010.h" +#include "mt2060.h" +#include "mxl5005s.h" +#include "fc0012.h" +#include "fc0013.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) +{ + int ret; + unsigned int pipe; + u8 requesttype; + u8 *buf; + + buf = kmalloc(req->size, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err; + } + + if (req->index & CMD_WR_FLAG) { + /* write */ + memcpy(buf, req->data, req->size); + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + pipe = usb_sndctrlpipe(d->udev, 0); + } else { + /* read */ + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + pipe = usb_rcvctrlpipe(d->udev, 0); + } + + ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value, + req->index, buf, req->size, 1000); + if (ret > 0) + ret = 0; + + deb_dump(0, requesttype, req->value, req->index, buf, req->size); + + /* read request, copy returned data to return buf */ + if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, buf, req->size); + + kfree(buf); + + if (ret) + goto err; + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) +{ + struct rtl28xxu_req req; + + if (reg < 0x3000) + req.index = CMD_USB_WR; + else if (reg < 0x4000) + req.index = CMD_SYS_WR; + else + req.index = CMD_IR_WR; + + req.value = reg; + req.size = len; + req.data = val; + + return rtl28xxu_ctrl_msg(d, &req); +} + +static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) +{ + struct rtl28xxu_req req; + + if (reg < 0x3000) + req.index = CMD_USB_RD; + else if (reg < 0x4000) + req.index = CMD_SYS_RD; + else + req.index = CMD_IR_RD; + + req.value = reg; + req.size = len; + req.data = val; + + return rtl28xxu_ctrl_msg(d, &req); +} + +static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val) +{ + return rtl28xx_wr_regs(d, reg, &val, 1); +} + +static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +{ + return rtl2831_rd_regs(d, reg, val, 1); +} + +/* I2C */ +static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + int ret; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct rtl28xxu_priv *priv = d->priv; + struct rtl28xxu_req req; + + /* + * It is not known which are real I2C bus xfer limits, but testing + * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes. + * TODO: find out RTL2832U lens + */ + + /* + * I2C adapter logic looks rather complicated due to fact it handles + * three different access methods. Those methods are; + * 1) integrated demod access + * 2) old I2C access + * 3) new I2C access + * + * Used method is selected in order 1, 2, 3. Method 3 can handle all + * requests but there is two reasons why not use it always; + * 1) It is most expensive, usually two USB messages are needed + * 2) At least RTL2831U does not support it + * + * Method 3 is needed in case of I2C write+read (typical register read) + * where write is more than one byte. + */ + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + if (msg[0].len > 24 || msg[1].len > 24) { + /* TODO: check msg[0].len max */ + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } else if (msg[0].addr == 0x10) { + /* method 1 - integrated demod */ + req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); + req.index = CMD_DEMOD_RD | priv->page; + req.size = msg[1].len; + req.data = &msg[1].buf[0]; + ret = rtl28xxu_ctrl_msg(d, &req); + } else if (msg[0].len < 2) { + /* method 2 - old I2C */ + req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); + req.index = CMD_I2C_RD; + req.size = msg[1].len; + req.data = &msg[1].buf[0]; + ret = rtl28xxu_ctrl_msg(d, &req); + } else { + /* method 3 - new I2C */ + req.value = (msg[0].addr << 1); + req.index = CMD_I2C_DA_WR; + req.size = msg[0].len; + req.data = msg[0].buf; + ret = rtl28xxu_ctrl_msg(d, &req); + if (ret) + goto err_mutex_unlock; + + req.value = (msg[0].addr << 1); + req.index = CMD_I2C_DA_RD; + req.size = msg[1].len; + req.data = msg[1].buf; + ret = rtl28xxu_ctrl_msg(d, &req); + } + } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + if (msg[0].len > 22) { + /* TODO: check msg[0].len max */ + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } else if (msg[0].addr == 0x10) { + /* method 1 - integrated demod */ + if (msg[0].buf[0] == 0x00) { + /* save demod page for later demod access */ + priv->page = msg[0].buf[1]; + ret = 0; + } else { + req.value = (msg[0].buf[0] << 8) | + (msg[0].addr << 1); + req.index = CMD_DEMOD_WR | priv->page; + req.size = msg[0].len-1; + req.data = &msg[0].buf[1]; + ret = rtl28xxu_ctrl_msg(d, &req); + } + } else if (msg[0].len < 23) { + /* method 2 - old I2C */ + req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); + req.index = CMD_I2C_WR; + req.size = msg[0].len-1; + req.data = &msg[0].buf[1]; + ret = rtl28xxu_ctrl_msg(d, &req); + } else { + /* method 3 - new I2C */ + req.value = (msg[0].addr << 1); + req.index = CMD_I2C_DA_WR; + req.size = msg[0].len; + req.data = msg[0].buf; + ret = rtl28xxu_ctrl_msg(d, &req); + } + } else { + ret = -EINVAL; + } + +err_mutex_unlock: + mutex_unlock(&d->i2c_mutex); + + return ret ? ret : num; +} + +static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm rtl28xxu_i2c_algo = { + .master_xfer = rtl28xxu_i2c_xfer, + .functionality = rtl28xxu_i2c_func, +}; + +static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .ts_mode = 0, + .spec_inv = 1, + .if_dvbt = 36150000, + .vtop = 0x20, + .krf = 0x04, + .agc_targ_val = 0x2d, + +}; + +static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .ts_mode = 0, + .spec_inv = 1, + .if_dvbt = 36125000, + .vtop = 0x20, + .krf = 0x04, + .agc_targ_val = 0x2d, +}; + +static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .ts_mode = 0, + .spec_inv = 0, + .if_dvbt = 4570000, + .vtop = 0x3f, + .krf = 0x04, + .agc_targ_val = 0x3e, +}; + +static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_usb_device *d = adap_to_d(adap); + struct rtl28xxu_priv *priv = d_to_priv(d); + u8 buf[1]; + struct rtl2830_config *rtl2830_config; + /* open RTL2831U/RTL2830 I2C gate */ + struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" }; + /* for MT2060 tuner probe */ + struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf }; + /* for QT1010 tuner probe */ + struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf }; + + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + /* + * RTL2831U GPIOs + * ========================================================= + * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?) + * GPIO2 | LED | 0 off | 1 on | + * GPIO4 | tuner#1 | 0 on | 1 off | MT2060 + */ + + /* GPIO direction */ + ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, 0x0a); + if (ret) + goto err; + + /* enable as output GPIO0, GPIO2, GPIO4 */ + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, 0x15); + if (ret) + goto err; + + /* + * Probe used tuner. We need to know used tuner before demod attach + * since there is some demod params needed to set according to tuner. + */ + + /* demod needs some time to wake up */ + msleep(20); + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate); + if (ret) + goto err; + + /* check QT1010 ID(?) register; reg=0f val=2c */ + ret = rtl28xxu_ctrl_msg(d, &req_qt1010); + if (ret == 0 && buf[0] == 0x2c) { + priv->tuner = TUNER_RTL2830_QT1010; + rtl2830_config = &rtl28xxu_rtl2830_qt1010_config; + dev_dbg(&d->udev->dev, "%s: QT1010\n", __func__); + goto found; + } else { + dev_dbg(&d->udev->dev, "%s: QT1010 probe failed=%d - %02x\n", + __func__, ret, buf[0]); + } + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate); + if (ret) + goto err; + + /* check MT2060 ID register; reg=00 val=63 */ + ret = rtl28xxu_ctrl_msg(d, &req_mt2060); + if (ret == 0 && buf[0] == 0x63) { + priv->tuner = TUNER_RTL2830_MT2060; + rtl2830_config = &rtl28xxu_rtl2830_mt2060_config; + dev_dbg(&d->udev->dev, "%s: MT2060\n", __func__); + goto found; + } else { + dev_dbg(&d->udev->dev, "%s: MT2060 probe failed=%d - %02x\n", + __func__, ret, buf[0]); + } + + /* assume MXL5005S */ + ret = 0; + priv->tuner = TUNER_RTL2830_MXL5005S; + rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config; + dev_dbg(&d->udev->dev, "%s: MXL5005S\n", __func__); + goto found; + +found: + /* attach demodulator */ + adap->fe[0] = dvb_attach(rtl2830_attach, rtl2830_config, + &d->i2c_adap); + if (adap->fe[0] == NULL) { + ret = -ENODEV; + goto err; + } + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .if_dvbt = 0, + .tuner = TUNER_RTL2832_FC0012 +}; + +static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .if_dvbt = 0, + .tuner = TUNER_RTL2832_FC0013 +}; + +static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int ret; + u8 val; + + dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg); + + switch (cmd) { + case FC_FE_CALLBACK_VHF_ENABLE: + /* set output values */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + if (ret) + goto err; + + if (arg) + val &= 0xbf; /* set GPIO6 low */ + else + val |= 0x40; /* set GPIO6 high */ + + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret) + goto err; + break; + default: + ret = -EINVAL; + goto err; + } + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + + +static int rtl2832u_fc0013_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + /* TODO implement*/ + return 0; +} + +static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) +{ + struct rtl28xxu_priv *priv = d->priv; + + switch (priv->tuner) { + case TUNER_RTL2832_FC0012: + return rtl2832u_fc0012_tuner_callback(d, cmd, arg); + + case TUNER_RTL2832_FC0013: + return rtl2832u_fc0013_tuner_callback(d, cmd, arg); + default: + break; + } + + return -ENODEV; +} + +static int rtl2832u_frontend_callback(void *adapter_priv, int component, + int cmd, int arg) +{ + struct i2c_adapter *adap = adapter_priv; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + switch (component) { + case DVB_FRONTEND_COMPONENT_TUNER: + return rtl2832u_tuner_callback(d, cmd, arg); + default: + break; + } + + return -EINVAL; +} + +static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_usb_device *d = adap_to_d(adap); + struct rtl28xxu_priv *priv = d_to_priv(d); + struct rtl2832_config *rtl2832_config; + u8 buf[2], val; + /* open RTL2832U/RTL2832 I2C gate */ + struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"}; + /* close RTL2832U/RTL2832 I2C gate */ + struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"}; + /* for FC0012 tuner probe */ + struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf}; + /* for FC0013 tuner probe */ + struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf}; + /* for MT2266 tuner probe */ + struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf}; + /* for FC2580 tuner probe */ + struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf}; + /* for MT2063 tuner probe */ + struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf}; + /* for MAX3543 tuner probe */ + struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf}; + /* for TUA9001 tuner probe */ + struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf}; + /* for MXL5007T tuner probe */ + struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf}; + /* for E4000 tuner probe */ + struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf}; + /* for TDA18272 tuner probe */ + struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf}; + + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + ret = rtl28xx_rd_reg(d, SYS_GPIO_DIR, &val); + if (ret) + goto err; + + val &= 0xbf; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, val); + if (ret) + goto err; + + /* enable as output GPIO3 and GPIO6*/ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_EN, &val); + if (ret) + goto err; + + val |= 0x48; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, val); + if (ret) + goto err; + + /* + * Probe used tuner. We need to know used tuner before demod attach + * since there is some demod params needed to set according to tuner. + */ + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate_open); + if (ret) + goto err; + + priv->tuner = TUNER_NONE; + + /* check FC0012 ID register; reg=00 val=a1 */ + ret = rtl28xxu_ctrl_msg(d, &req_fc0012); + if (ret == 0 && buf[0] == 0xa1) { + priv->tuner = TUNER_RTL2832_FC0012; + rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; + dev_info(&d->udev->dev, "%s: FC0012 tuner found", + KBUILD_MODNAME); + goto found; + } + + /* check FC0013 ID register; reg=00 val=a3 */ + ret = rtl28xxu_ctrl_msg(d, &req_fc0013); + if (ret == 0 && buf[0] == 0xa3) { + priv->tuner = TUNER_RTL2832_FC0013; + rtl2832_config = &rtl28xxu_rtl2832_fc0013_config; + dev_info(&d->udev->dev, "%s: FC0013 tuner found", + KBUILD_MODNAME); + goto found; + } + + /* check MT2266 ID register; reg=00 val=85 */ + ret = rtl28xxu_ctrl_msg(d, &req_mt2266); + if (ret == 0 && buf[0] == 0x85) { + priv->tuner = TUNER_RTL2832_MT2266; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: MT2266 tuner found", + KBUILD_MODNAME); + goto unsupported; + } + + /* check FC2580 ID register; reg=01 val=56 */ + ret = rtl28xxu_ctrl_msg(d, &req_fc2580); + if (ret == 0 && buf[0] == 0x56) { + priv->tuner = TUNER_RTL2832_FC2580; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: FC2580 tuner found", + KBUILD_MODNAME); + goto unsupported; + } + + /* check MT2063 ID register; reg=00 val=9e || 9c */ + ret = rtl28xxu_ctrl_msg(d, &req_mt2063); + if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) { + priv->tuner = TUNER_RTL2832_MT2063; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: MT2063 tuner found", + KBUILD_MODNAME); + goto unsupported; + } + + /* check MAX3543 ID register; reg=00 val=38 */ + ret = rtl28xxu_ctrl_msg(d, &req_max3543); + if (ret == 0 && buf[0] == 0x38) { + priv->tuner = TUNER_RTL2832_MAX3543; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: MAX3534 tuner found", + KBUILD_MODNAME); + goto unsupported; + } + + /* check TUA9001 ID register; reg=7e val=2328 */ + ret = rtl28xxu_ctrl_msg(d, &req_tua9001); + if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) { + priv->tuner = TUNER_RTL2832_TUA9001; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: TUA9001 tuner found", + KBUILD_MODNAME); + goto unsupported; + } + + /* check MXL5007R ID register; reg=d9 val=14 */ + ret = rtl28xxu_ctrl_msg(d, &req_mxl5007t); + if (ret == 0 && buf[0] == 0x14) { + priv->tuner = TUNER_RTL2832_MXL5007T; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: MXL5007T tuner found", + KBUILD_MODNAME); + goto unsupported; + } + + /* check E4000 ID register; reg=02 val=40 */ + ret = rtl28xxu_ctrl_msg(d, &req_e4000); + if (ret == 0 && buf[0] == 0x40) { + priv->tuner = TUNER_RTL2832_E4000; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: E4000 tuner found", + KBUILD_MODNAME); + goto unsupported; + } + + /* check TDA18272 ID register; reg=00 val=c760 */ + ret = rtl28xxu_ctrl_msg(d, &req_tda18272); + if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) { + priv->tuner = TUNER_RTL2832_TDA18272; + /* TODO implement tuner */ + dev_info(&d->udev->dev, "%s: TDA18272 tuner found", + KBUILD_MODNAME); + goto unsupported; + } + +unsupported: + /* close demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate_close); + if (ret) + goto err; + + /* tuner not found */ + dev_dbg(&d->udev->dev, "%s: No compatible tuner found\n", __func__); + ret = -ENODEV; + return ret; + +found: + /* close demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate_close); + if (ret) + goto err; + + /* attach demodulator */ + adap->fe[0] = dvb_attach(rtl2832_attach, rtl2832_config, + &d->i2c_adap); + if (adap->fe[0] == NULL) { + ret = -ENODEV; + goto err; + } + + /* set fe callbacks */ + adap->fe[0]->callback = rtl2832u_frontend_callback; + + return ret; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static struct qt1010_config rtl28xxu_qt1010_config = { + .i2c_address = 0x62, /* 0xc4 */ +}; + +static struct mt2060_config rtl28xxu_mt2060_config = { + .i2c_address = 0x60, /* 0xc0 */ + .clock_out = 0, +}; + +static struct mxl5005s_config rtl28xxu_mxl5005s_config = { + .i2c_address = 0x63, /* 0xc6 */ + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_C_H, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_usb_device *d = adap_to_d(adap); + struct rtl28xxu_priv *priv = d_to_priv(d); + struct i2c_adapter *rtl2830_tuner_i2c; + struct dvb_frontend *fe; + + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */ + rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe[0]); + + switch (priv->tuner) { + case TUNER_RTL2830_QT1010: + fe = dvb_attach(qt1010_attach, adap->fe[0], + rtl2830_tuner_i2c, &rtl28xxu_qt1010_config); + break; + case TUNER_RTL2830_MT2060: + fe = dvb_attach(mt2060_attach, adap->fe[0], + rtl2830_tuner_i2c, &rtl28xxu_mt2060_config, + 1220); + break; + case TUNER_RTL2830_MXL5005S: + fe = dvb_attach(mxl5005s_attach, adap->fe[0], + rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config); + break; + default: + fe = NULL; + dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, + priv->tuner); + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_usb_device *d = adap_to_d(adap); + struct rtl28xxu_priv *priv = d_to_priv(d); + struct dvb_frontend *fe; + + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + switch (priv->tuner) { + case TUNER_RTL2832_FC0012: + fe = dvb_attach(fc0012_attach, adap->fe[0], + &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + + /* since fc0012 includs reading the signal strength delegate + * that to the tuner driver */ + adap->fe[0]->ops.read_signal_strength = + adap->fe[0]->ops.tuner_ops.get_rf_strength; + return 0; + break; + case TUNER_RTL2832_FC0013: + fe = dvb_attach(fc0013_attach, adap->fe[0], + &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + + /* fc0013 also supports signal strength reading */ + adap->fe[0]->ops.read_signal_strength = + adap->fe[0]->ops.tuner_ops.get_rf_strength; + return 0; + default: + fe = NULL; + dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, + priv->tuner); + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl28xxu_init(struct dvb_usb_device *d) +{ + int ret; + u8 val; + + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + /* init USB endpoints */ + ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val); + if (ret) + goto err; + + /* enable DMA and Full Packet Mode*/ + val |= 0x09; + ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val); + if (ret) + goto err; + + /* set EPA maximum packet size to 0x0200 */ + ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4); + if (ret) + goto err; + + /* change EPA FIFO length */ + ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4); + if (ret) + goto err; + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl28xxu_streaming_ctrl(struct dvb_frontend *fe , int onoff) +{ + int ret; + u8 buf[2]; + struct dvb_usb_device *d = fe_to_d(fe); + + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); + + if (onoff) { + buf[0] = 0x00; + buf[1] = 0x00; + } else { + buf[0] = 0x10; /* stall EPA */ + buf[1] = 0x02; /* reset EPA */ + } + + ret = rtl28xx_wr_regs(d, USB_EPA_CTL, buf, 2); + if (ret) + goto err; + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + u8 gpio, sys0; + + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); + + /* demod adc */ + ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0); + if (ret) + goto err; + + /* tuner power, read GPIOs */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio); + if (ret) + goto err; + + dev_dbg(&d->udev->dev, "%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, + sys0, gpio); + + if (onoff) { + gpio |= 0x01; /* GPIO0 = 1 */ + gpio &= (~0x10); /* GPIO4 = 0 */ + gpio |= 0x04; /* GPIO2 = 1, LED on */ + sys0 = sys0 & 0x0f; + sys0 |= 0xe0; + } else { + gpio &= (~0x01); /* GPIO0 = 0 */ + gpio |= 0x10; /* GPIO4 = 1 */ + gpio &= (~0x04); /* GPIO2 = 1, LED off */ + sys0 = sys0 & (~0xc0); + } + + dev_dbg(&d->udev->dev, "%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, + sys0, gpio); + + /* demod adc */ + ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0); + if (ret) + goto err; + + /* tuner power, write GPIOs */ + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio); + if (ret) + goto err; + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + u8 val; + + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); + + if (onoff) { + /* set output values */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + if (ret) + goto err; + + val |= 0x08; + val &= 0xef; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret) + goto err; + + /* demod_ctl_1 */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val); + if (ret) + goto err; + + val &= 0xef; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val); + if (ret) + goto err; + + /* demod control */ + /* PLL enable */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + /* bit 7 to 1 */ + val |= 0x80; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + /* demod HW reset */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + /* bit 5 to 0 */ + val &= 0xdf; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + val |= 0x20; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + mdelay(5); + + /*enable ADC_Q and ADC_I */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + val |= 0x48; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + + } else { + /* demod_ctl_1 */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val); + if (ret) + goto err; + + val |= 0x0c; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val); + if (ret) + goto err; + + /* set output values */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + if (ret) + goto err; + + val |= 0x10; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret) + goto err; + + /* demod control */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + val &= 0x37; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + } + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + + +static int rtl2831u_rc_query(struct dvb_usb_device *d) +{ + int ret, i; + struct rtl28xxu_priv *priv = d->priv; + u8 buf[5]; + u32 rc_code; + struct rtl28xxu_reg_val rc_nec_tab[] = { + { 0x3033, 0x80 }, + { 0x3020, 0x43 }, + { 0x3021, 0x16 }, + { 0x3022, 0x16 }, + { 0x3023, 0x5a }, + { 0x3024, 0x2d }, + { 0x3025, 0x16 }, + { 0x3026, 0x01 }, + { 0x3028, 0xb0 }, + { 0x3029, 0x04 }, + { 0x302c, 0x88 }, + { 0x302e, 0x13 }, + { 0x3030, 0xdf }, + { 0x3031, 0x05 }, + }; + + /* init remote controller */ + if (!priv->rc_active) { + for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) { + ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg, + rc_nec_tab[i].val); + if (ret) + goto err; + } + priv->rc_active = true; + } + + ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5); + if (ret) + goto err; + + if (buf[4] & 0x01) { + if (buf[2] == (u8) ~buf[3]) { + if (buf[0] == (u8) ~buf[1]) { + /* NEC standard (16 bit) */ + rc_code = buf[0] << 8 | buf[2]; + } else { + /* NEC extended (24 bit) */ + rc_code = buf[0] << 16 | + buf[1] << 8 | buf[2]; + } + } else { + /* NEC full (32 bit) */ + rc_code = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]; + } + + rc_keydown(d->rc_dev, rc_code, 0); + + ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); + if (ret) + goto err; + + /* repeated intentionally to avoid extra keypress */ + ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); + if (ret) + goto err; + } + + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2831u_get_rc_config(struct dvb_usb_device *d, + struct dvb_usb_rc *rc) +{ + rc->map_name = RC_MAP_EMPTY; + rc->allowed_protos = RC_TYPE_NEC; + rc->query = rtl2831u_rc_query; + rc->interval = 400; + + return 0; +} + +static int rtl2832u_rc_query(struct dvb_usb_device *d) +{ + int ret, i; + struct rtl28xxu_priv *priv = d->priv; + u8 buf[128]; + int len; + struct rtl28xxu_reg_val rc_nec_tab[] = { + { IR_RX_CTRL, 0x20 }, + { IR_RX_BUF_CTRL, 0x80 }, + { IR_RX_IF, 0xff }, + { IR_RX_IE, 0xff }, + { IR_MAX_DURATION0, 0xd0 }, + { IR_MAX_DURATION1, 0x07 }, + { IR_IDLE_LEN0, 0xc0 }, + { IR_IDLE_LEN1, 0x00 }, + { IR_GLITCH_LEN, 0x03 }, + { IR_RX_CLK, 0x09 }, + { IR_RX_CFG, 0x1c }, + { IR_MAX_H_TOL_LEN, 0x1e }, + { IR_MAX_L_TOL_LEN, 0x1e }, + { IR_RX_CTRL, 0x80 }, + }; + + /* init remote controller */ + if (!priv->rc_active) { + for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) { + ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg, + rc_nec_tab[i].val); + if (ret) + goto err; + } + priv->rc_active = true; + } + + ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]); + if (ret) + goto err; + + if (buf[0] != 0x83) + goto exit; + + ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]); + if (ret) + goto err; + + len = buf[0]; + ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len); + + /* TODO: pass raw IR to Kernel IR decoder */ + + ret = rtl28xx_wr_reg(d, IR_RX_IF, 0x03); + ret = rtl28xx_wr_reg(d, IR_RX_BUF_CTRL, 0x80); + ret = rtl28xx_wr_reg(d, IR_RX_CTRL, 0x80); + +exit: + return ret; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2832u_get_rc_config(struct dvb_usb_device *d, + struct dvb_usb_rc *rc) +{ + rc->map_name = RC_MAP_EMPTY; + rc->allowed_protos = RC_TYPE_NEC; + rc->query = rtl2832u_rc_query; + rc->interval = 400; + + return 0; +} + +static const struct dvb_usb_device_properties rtl2831u_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct rtl28xxu_priv), + + .power_ctrl = rtl2831u_power_ctrl, + .i2c_algo = &rtl28xxu_i2c_algo, + .frontend_attach = rtl2831u_frontend_attach, + .tuner_attach = rtl2831u_tuner_attach, + .init = rtl28xxu_init, + .get_rc_config = rtl2831u_get_rc_config, + .streaming_ctrl = rtl28xxu_streaming_ctrl, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512), + }, + }, +}; + +static const struct dvb_usb_device_properties rtl2832u_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct rtl28xxu_priv), + + .power_ctrl = rtl2832u_power_ctrl, + .i2c_algo = &rtl28xxu_i2c_algo, + .frontend_attach = rtl2832u_frontend_attach, + .tuner_attach = rtl2832u_tuner_attach, + .init = rtl28xxu_init, + .get_rc_config = rtl2832u_get_rc_config, + .streaming_ctrl = rtl28xxu_streaming_ctrl, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512), + }, + }, +}; + +static const struct usb_device_id rtl28xxu_id_table[] = { + { DVB_USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U, + &rtl2831u_props, "Realtek RTL2831U reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT, + &rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) }, + { DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2, + &rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) }, + + { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1, + &rtl2832u_props, "Terratec Cinergy T Stick Black", NULL) }, + { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT, + &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK, + &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) }, + { } +}; +MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); + +static struct usb_driver rtl28xxu_usb_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl28xxu_id_table, + .probe = dvb_usbv2_probe, + .disconnect = dvb_usbv2_disconnect, + .suspend = dvb_usbv2_suspend, + .resume = dvb_usbv2_resume, + .no_dynamic_id = 1, + .soft_unbind = 1, +}; + +module_usb_driver(rtl28xxu_usb_driver); + +MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver"); +MODULE_AUTHOR("Antti Palosaari "); +MODULE_AUTHOR("Thomas Mair "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h new file mode 100644 index 000000000000..575edbf13a92 --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -0,0 +1,254 @@ +/* + * Realtek RTL28xxU DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * 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 RTL28XXU_H +#define RTL28XXU_H + +#include "dvb_usb.h" + +#define deb_dump(r, t, v, i, b, l) { \ + char *direction; \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + direction = ">>>"; \ + else \ + direction = "<<<"; \ + dev_dbg(&d->udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ + "%s [%d bytes]\n", __func__, t, r, v & 0xff, v >> 8, \ + i & 0xff, i >> 8, l & 0xff, l >> 8, direction, l); \ +} + +/* + * USB commands + * (usb_control_msg() index parameter) + */ + +#define DEMOD 0x0000 +#define USB 0x0100 +#define SYS 0x0200 +#define I2C 0x0300 +#define I2C_DA 0x0600 + +#define CMD_WR_FLAG 0x0010 +#define CMD_DEMOD_RD 0x0000 +#define CMD_DEMOD_WR 0x0010 +#define CMD_USB_RD 0x0100 +#define CMD_USB_WR 0x0110 +#define CMD_SYS_RD 0x0200 +#define CMD_IR_RD 0x0201 +#define CMD_IR_WR 0x0211 +#define CMD_SYS_WR 0x0210 +#define CMD_I2C_RD 0x0300 +#define CMD_I2C_WR 0x0310 +#define CMD_I2C_DA_RD 0x0600 +#define CMD_I2C_DA_WR 0x0610 + + +struct rtl28xxu_priv { + u8 chip_id; + u8 tuner; + u8 page; /* integrated demod active register page */ + bool rc_active; +}; + +enum rtl28xxu_chip_id { + CHIP_ID_NONE, + CHIP_ID_RTL2831U, + CHIP_ID_RTL2832U, +}; + +enum rtl28xxu_tuner { + TUNER_NONE, + + TUNER_RTL2830_QT1010, + TUNER_RTL2830_MT2060, + TUNER_RTL2830_MXL5005S, + + TUNER_RTL2832_MT2266, + TUNER_RTL2832_FC2580, + TUNER_RTL2832_MT2063, + TUNER_RTL2832_MAX3543, + TUNER_RTL2832_TUA9001, + TUNER_RTL2832_MXL5007T, + TUNER_RTL2832_FC0012, + TUNER_RTL2832_E4000, + TUNER_RTL2832_TDA18272, + TUNER_RTL2832_FC0013, +}; + +struct rtl28xxu_req { + u16 value; + u16 index; + u16 size; + u8 *data; +}; + +struct rtl28xxu_reg_val { + u16 reg; + u8 val; +}; + +/* + * memory map + * + * 0x0000 DEMOD : demodulator + * 0x2000 USB : SIE, USB endpoint, debug, DMA + * 0x3000 SYS : system + * 0xfc00 RC : remote controller (not RTL2831U) + */ + +/* + * USB registers + */ +/* SIE Control Registers */ +#define USB_SYSCTL 0x2000 /* USB system control */ +#define USB_SYSCTL_0 0x2000 /* USB system control */ +#define USB_SYSCTL_1 0x2001 /* USB system control */ +#define USB_SYSCTL_2 0x2002 /* USB system control */ +#define USB_SYSCTL_3 0x2003 /* USB system control */ +#define USB_IRQSTAT 0x2008 /* SIE interrupt status */ +#define USB_IRQEN 0x200C /* SIE interrupt enable */ +#define USB_CTRL 0x2010 /* USB control */ +#define USB_STAT 0x2014 /* USB status */ +#define USB_DEVADDR 0x2018 /* USB device address */ +#define USB_TEST 0x201C /* USB test mode */ +#define USB_FRAME_NUMBER 0x2020 /* frame number */ +#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */ +#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */ +#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */ +/* Endpoint Registers */ +#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */ +#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */ +#define USB_EP0_CFG 0x2104 /* EP 0 configure */ +#define USB_EP0_CTL 0x2108 /* EP 0 control */ +#define USB_EP0_STAT 0x210C /* EP 0 status */ +#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */ +#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */ +#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */ +#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */ +#define USB_EPA_CFG 0x2144 /* EP A configure */ +#define USB_EPA_CFG_0 0x2144 /* EP A configure */ +#define USB_EPA_CFG_1 0x2145 /* EP A configure */ +#define USB_EPA_CFG_2 0x2146 /* EP A configure */ +#define USB_EPA_CFG_3 0x2147 /* EP A configure */ +#define USB_EPA_CTL 0x2148 /* EP A control */ +#define USB_EPA_CTL_0 0x2148 /* EP A control */ +#define USB_EPA_CTL_1 0x2149 /* EP A control */ +#define USB_EPA_CTL_2 0x214A /* EP A control */ +#define USB_EPA_CTL_3 0x214B /* EP A control */ +#define USB_EPA_STAT 0x214C /* EP A status */ +#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */ +#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */ +#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */ +#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */ +#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */ +#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */ +#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */ +#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */ +#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */ +/* Debug Registers */ +#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */ +#define USB_TOUT_VAL 0x2F08 /* USB time-out time */ +#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */ +#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */ +#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */ +#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */ +#define USB_UTMI_TST 0x2F80 /* UTMI test */ +#define USB_UTMI_STATUS 0x2F84 /* UTMI status */ +#define USB_TSTCTL 0x2F88 /* test control */ +#define USB_TSTCTL2 0x2F8C /* test control 2 */ +#define USB_PID_FORCE 0x2F90 /* force PID */ +#define USB_PKTERR_CNT 0x2F94 /* packet error counter */ +#define USB_RXERR_CNT 0x2F98 /* RX error counter */ +#define USB_MEM_BIST 0x2F9C /* MEM BIST test */ +#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */ +#define USB_CNTTEST 0x2FA4 /* counter test */ +#define USB_PHYTST 0x2FC0 /* USB PHY test */ +#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */ +#define USB_DBGMUX 0x2FF4 /* debug signal module mux */ + +/* + * SYS registers + */ +/* demod control registers */ +#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */ +#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */ +/* GPIO registers */ +#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */ +#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */ +#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */ +#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */ +#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */ +#define SYS_SYSINTE 0x3005 /* system interrupt enable */ +#define SYS_SYSINTS 0x3006 /* system interrupt status */ +#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */ +#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */ +#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */ +#define SYS_DEMOD_CTL1 0x300B + +/* IrDA registers */ +#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */ +#define SYS_IRRC_PER 0x3024 /* IR protocol extension */ +#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */ +#define SYS_IRRC_DPIR 0x302C /* IR data package interval */ +#define SYS_IRRC_CR 0x3030 /* IR control */ +#define SYS_IRRC_RP 0x3034 /* IR read port */ +#define SYS_IRRC_SR 0x3038 /* IR status */ +/* I2C master registers */ +#define SYS_I2CCR 0x3040 /* I2C clock */ +#define SYS_I2CMCR 0x3044 /* I2C master control */ +#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */ +#define SYS_I2CMSR 0x304C /* I2C master status */ +#define SYS_I2CMFR 0x3050 /* I2C master FIFO */ + +/* + * IR registers + */ +#define IR_RX_BUF 0xFC00 +#define IR_RX_IE 0xFD00 +#define IR_RX_IF 0xFD01 +#define IR_RX_CTRL 0xFD02 +#define IR_RX_CFG 0xFD03 +#define IR_MAX_DURATION0 0xFD04 +#define IR_MAX_DURATION1 0xFD05 +#define IR_IDLE_LEN0 0xFD06 +#define IR_IDLE_LEN1 0xFD07 +#define IR_GLITCH_LEN 0xFD08 +#define IR_RX_BUF_CTRL 0xFD09 +#define IR_RX_BUF_DATA 0xFD0A +#define IR_RX_BC 0xFD0B +#define IR_RX_CLK 0xFD0C +#define IR_RX_C_COUNT_L 0xFD0D +#define IR_RX_C_COUNT_H 0xFD0E +#define IR_SUSPEND_CTRL 0xFD10 +#define IR_ERR_TOL_CTRL 0xFD11 +#define IR_UNIT_LEN 0xFD12 +#define IR_ERR_TOL_LEN 0xFD13 +#define IR_MAX_H_TOL_LEN 0xFD14 +#define IR_MAX_L_TOL_LEN 0xFD15 +#define IR_MASK_CTRL 0xFD16 +#define IR_MASK_DATA 0xFD17 +#define IR_RES_MASK_ADDR 0xFD18 +#define IR_RES_MASK_T_LEN 0xFD19 + +#endif diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c new file mode 100644 index 000000000000..eaf673a3978d --- /dev/null +++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c @@ -0,0 +1,357 @@ +/* usb-urb.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file keeps functions for initializing and handling the + * BULK and ISOC USB data transfers in a generic way. + * Can be used for DVB-only and also, that's the plan, for + * Hybrid USB devices (analog and DVB). + */ +#include "dvb_usb_common.h" + +/* URB stuff for streaming */ + +int usb_urb_reconfig(struct usb_data_stream *stream, + struct usb_data_stream_properties *props); + +static void usb_urb_complete(struct urb *urb) +{ + struct usb_data_stream *stream = urb->context; + int ptype = usb_pipetype(urb->pipe); + int i; + u8 *b; + + dev_dbg(&stream->udev->dev, "%s: %s urb completed status=%d " \ + "length=%d/%d pack_num=%d errors=%d\n", __func__, + ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk", + urb->status, urb->actual_length, + urb->transfer_buffer_length, + urb->number_of_packets, urb->error_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dev_dbg(&stream->udev->dev, "%s: urb completition failed=%d\n", + __func__, urb->status); + break; + } + + b = (u8 *) urb->transfer_buffer; + switch (ptype) { + case PIPE_ISOCHRONOUS: + for (i = 0; i < urb->number_of_packets; i++) { + if (urb->iso_frame_desc[i].status != 0) + dev_dbg(&stream->udev->dev, "%s: iso frame " \ + "descriptor has an error=%d\n", + __func__, + urb->iso_frame_desc[i].status); + else if (urb->iso_frame_desc[i].actual_length > 0) + stream->complete(stream, + b + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + break; + case PIPE_BULK: + if (urb->actual_length > 0) + stream->complete(stream, b, urb->actual_length); + break; + default: + dev_err(&stream->udev->dev, "%s: unknown endpoint type in " \ + "completition handler\n", KBUILD_MODNAME); + return; + } + usb_submit_urb(urb, GFP_ATOMIC); +} + +int usb_urb_killv2(struct usb_data_stream *stream) +{ + int i; + for (i = 0; i < stream->urbs_submitted; i++) { + dev_dbg(&stream->udev->dev, "%s: kill urb=%d\n", __func__, i); + /* stop the URB */ + usb_kill_urb(stream->urb_list[i]); + } + stream->urbs_submitted = 0; + return 0; +} + +int usb_urb_submitv2(struct usb_data_stream *stream, + struct usb_data_stream_properties *props) +{ + int i, ret; + + if (props) { + ret = usb_urb_reconfig(stream, props); + if (ret < 0) + return ret; + } + + for (i = 0; i < stream->urbs_initialized; i++) { + dev_dbg(&stream->udev->dev, "%s: submit urb=%d\n", __func__, i); + ret = usb_submit_urb(stream->urb_list[i], GFP_ATOMIC); + if (ret) { + dev_err(&stream->udev->dev, "%s: could not submit " \ + "urb no. %d - get them all back\n", + KBUILD_MODNAME, i); + usb_urb_killv2(stream); + return ret; + } + stream->urbs_submitted++; + } + return 0; +} + +int usb_urb_free_urbs(struct usb_data_stream *stream) +{ + int i; + + usb_urb_killv2(stream); + + for (i = stream->urbs_initialized - 1; i >= 0; i--) { + if (stream->urb_list[i]) { + dev_dbg(&stream->udev->dev, "%s: free urb=%d\n", + __func__, i); + /* free the URBs */ + usb_free_urb(stream->urb_list[i]); + } + } + stream->urbs_initialized = 0; + + return 0; +} + +static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream) +{ + int i, j; + + /* allocate the URBs */ + for (i = 0; i < stream->props.count; i++) { + dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i); + stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!stream->urb_list[i]) { + dev_dbg(&stream->udev->dev, "%s: failed\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(stream->urb_list[j]); + return -ENOMEM; + } + usb_fill_bulk_urb(stream->urb_list[i], + stream->udev, + usb_rcvbulkpipe(stream->udev, + stream->props.endpoint), + stream->buf_list[i], + stream->props.u.bulk.buffersize, + usb_urb_complete, stream); + + stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + stream->urb_list[i]->transfer_dma = stream->dma_addr[i]; + stream->urbs_initialized++; + } + return 0; +} + +static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream) +{ + int i, j; + + /* allocate the URBs */ + for (i = 0; i < stream->props.count; i++) { + struct urb *urb; + int frame_offset = 0; + dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i); + stream->urb_list[i] = usb_alloc_urb( + stream->props.u.isoc.framesperurb, GFP_ATOMIC); + if (!stream->urb_list[i]) { + dev_dbg(&stream->udev->dev, "%s: failed\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(stream->urb_list[j]); + return -ENOMEM; + } + + urb = stream->urb_list[i]; + + urb->dev = stream->udev; + urb->context = stream; + urb->complete = usb_urb_complete; + urb->pipe = usb_rcvisocpipe(stream->udev, + stream->props.endpoint); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->interval = stream->props.u.isoc.interval; + urb->number_of_packets = stream->props.u.isoc.framesperurb; + urb->transfer_buffer_length = stream->props.u.isoc.framesize * + stream->props.u.isoc.framesperurb; + urb->transfer_buffer = stream->buf_list[i]; + urb->transfer_dma = stream->dma_addr[i]; + + for (j = 0; j < stream->props.u.isoc.framesperurb; j++) { + urb->iso_frame_desc[j].offset = frame_offset; + urb->iso_frame_desc[j].length = + stream->props.u.isoc.framesize; + frame_offset += stream->props.u.isoc.framesize; + } + + stream->urbs_initialized++; + } + return 0; +} + +int usb_free_stream_buffers(struct usb_data_stream *stream) +{ + if (stream->state & USB_STATE_URB_BUF) { + while (stream->buf_num) { + stream->buf_num--; + dev_dbg(&stream->udev->dev, "%s: free buf=%d\n", + __func__, stream->buf_num); + usb_free_coherent(stream->udev, stream->buf_size, + stream->buf_list[stream->buf_num], + stream->dma_addr[stream->buf_num]); + } + } + + stream->state &= ~USB_STATE_URB_BUF; + + return 0; +} + +int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num, + unsigned long size) +{ + stream->buf_num = 0; + stream->buf_size = size; + + dev_dbg(&stream->udev->dev, "%s: all in all I will use %lu bytes for " \ + "streaming\n", __func__, num * size); + + for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) { + stream->buf_list[stream->buf_num] = usb_alloc_coherent( + stream->udev, size, GFP_ATOMIC, + &stream->dma_addr[stream->buf_num]); + if (!stream->buf_list[stream->buf_num]) { + dev_dbg(&stream->udev->dev, "%s: alloc buf=%d failed\n", + __func__, stream->buf_num); + usb_free_stream_buffers(stream); + return -ENOMEM; + } + + dev_dbg(&stream->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", + __func__, stream->buf_num, + stream->buf_list[stream->buf_num], + (long long)stream->dma_addr[stream->buf_num]); + memset(stream->buf_list[stream->buf_num], 0, size); + stream->state |= USB_STATE_URB_BUF; + } + + return 0; +} + +int usb_urb_reconfig(struct usb_data_stream *stream, + struct usb_data_stream_properties *props) +{ + int buf_size; + + if (!props) + return 0; + + /* check allocated buffers are large enough for the request */ + if (props->type == USB_BULK) { + buf_size = stream->props.u.bulk.buffersize; + } else if (props->type == USB_ISOC) { + buf_size = props->u.isoc.framesize * props->u.isoc.framesperurb; + } else { + dev_err(&stream->udev->dev, "%s: invalid endpoint type=%d\n", + KBUILD_MODNAME, props->type); + return -EINVAL; + } + + if (stream->buf_num < props->count || stream->buf_size < buf_size) { + dev_err(&stream->udev->dev, "%s: cannot reconfigure as " \ + "allocated buffers are too small\n", + KBUILD_MODNAME); + return -EINVAL; + } + + /* check if all fields are same */ + if (stream->props.type == props->type && + stream->props.count == props->count && + stream->props.endpoint == props->endpoint) { + if (props->type == USB_BULK && + props->u.bulk.buffersize == + stream->props.u.bulk.buffersize) + return 0; + else if (props->type == USB_ISOC && + props->u.isoc.framesperurb == + stream->props.u.isoc.framesperurb && + props->u.isoc.framesize == + stream->props.u.isoc.framesize && + props->u.isoc.interval == + stream->props.u.isoc.interval) + return 0; + } + + dev_dbg(&stream->udev->dev, "%s: re-alloc urbs\n", __func__); + + usb_urb_free_urbs(stream); + memcpy(&stream->props, props, sizeof(*props)); + if (props->type == USB_BULK) + return usb_urb_alloc_bulk_urbs(stream); + else if (props->type == USB_ISOC) + return usb_urb_alloc_isoc_urbs(stream); + + return 0; +} + +int usb_urb_initv2(struct usb_data_stream *stream, + const struct usb_data_stream_properties *props) +{ + int ret; + + if (!stream || !props) + return -EINVAL; + + memcpy(&stream->props, props, sizeof(*props)); + + if (!stream->complete) { + dev_err(&stream->udev->dev, "%s: there is no data callback - " \ + "this doesn't make sense\n", KBUILD_MODNAME); + return -EINVAL; + } + + switch (stream->props.type) { + case USB_BULK: + ret = usb_alloc_stream_buffers(stream, stream->props.count, + stream->props.u.bulk.buffersize); + if (ret < 0) + return ret; + + return usb_urb_alloc_bulk_urbs(stream); + case USB_ISOC: + ret = usb_alloc_stream_buffers(stream, stream->props.count, + stream->props.u.isoc.framesize * + stream->props.u.isoc.framesperurb); + if (ret < 0) + return ret; + + return usb_urb_alloc_isoc_urbs(stream); + default: + dev_err(&stream->udev->dev, "%s: unknown urb-type for data " \ + "transfer\n", KBUILD_MODNAME); + return -EINVAL; + } +} + +int usb_urb_exitv2(struct usb_data_stream *stream) +{ + usb_urb_free_urbs(stream); + usb_free_stream_buffers(stream); + + return 0; +} diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig new file mode 100644 index 000000000000..00173ee15d4a --- /dev/null +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -0,0 +1,313 @@ +config DVB_USB + tristate "Support for various USB DVB devices" + depends on DVB_CORE && USB && I2C && RC_CORE + help + By enabling this you will be able to choose the various supported + USB1.1 and USB2.0 DVB devices. + + Almost every USB device needs a firmware, please look into + . + + For a complete list of supported USB devices see the LinuxTV DVB Wiki: + + + Say Y if you own a USB DVB device. + +config DVB_USB_DEBUG + bool "Enable extended debug support for all DVB-USB devices" + depends on DVB_USB + help + Say Y if you want to enable debugging. See modinfo dvb-usb (and the + appropriate drivers) for debug levels. + +config DVB_USB_A800 + tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" + depends on DVB_USB + select DVB_DIB3000MC + select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. + +config DVB_USB_DIBUSB_MB + tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" + depends on DVB_USB + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_DIB3000MB + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + help + Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by + DiBcom () equipped with a DiB3000M-B demodulator. + + For an up-to-date list of devices supported by this driver, have a look + on the Linux-DVB Wiki at www.linuxtv.org. + + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_DIBUSB_MB_FAULTY + bool "Support faulty USB IDs" + depends on DVB_USB_DIBUSB_MB + help + Support for faulty USB IDs due to an invalid EEPROM on some Artec devices. + +config DVB_USB_DIBUSB_MC + tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" + depends on DVB_USB + select DVB_DIB3000MC + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + help + Support for USB2.0 DVB-T receivers based on reference designs made by + DiBcom () equipped with a DiB3000M-C/P demodulator. + + For an up-to-date list of devices supported by this driver, have a look + on the Linux-DVB Wiki at www.linuxtv.org. + + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_DIB0700 + tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" + depends on DVB_USB + select DVB_DIB7000P if !DVB_FE_CUSTOMISE + select DVB_DIB7000M if !DVB_FE_CUSTOMISE + select DVB_DIB8000 if !DVB_FE_CUSTOMISE + select DVB_DIB3000MC if !DVB_FE_CUSTOMISE + select DVB_S5H1411 if !DVB_FE_CUSTOMISE + select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE + select DVB_TUNER_DIB0090 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE + 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_MXL5007T if !MEDIA_TUNER_CUSTOMISE + help + Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The + USB bridge is also present in devices having the DiB7700 DVB-T-USB + silicon. This chip can be found in devices offered by Hauppauge, + Avermedia and other big and small companies. + + For an up-to-date list of devices supported by this driver, have a look + on the LinuxTV Wiki at www.linuxtv.org. + + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_UMT_010 + tristate "HanfTek UMT-010 DVB-T USB2.0 support" + depends on DVB_USB + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_DIB3000MC + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + help + Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. + +config DVB_USB_CXUSB + tristate "Conexant USB2.0 hybrid reference design support" + depends on DVB_USB + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_CX22702 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_DIB7000P if !DVB_FE_CUSTOMISE + select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE + select DVB_ATBM8830 if !DVB_FE_CUSTOMISE + select DVB_LGS8GXX if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Conexant USB2.0 hybrid reference design. + Currently, only DVB and ATSC modes are supported, analog mode + shall be added in the future. Devices that require this module: + + Medion MD95700 hybrid USB2.0 device. + DViCO FusionHDTV (Bluebird) USB2.0 devices + +config DVB_USB_M920X + tristate "Uli m920x DVB-T USB2.0 support" + depends on DVB_USB + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. + Currently, only devices with a product id of + "DTV USB MINI" (in cold state) are supported. + Firmware required. + +config DVB_USB_DIGITV + tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" + depends on DVB_USB + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_NXT6000 if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. + +config DVB_USB_VP7045 + tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support" + depends on DVB_USB + help + Say Y here to support the + + TwinhanDTV Alpha (stick) (VP-7045), + TwinhanDTV MagicBox II (VP-7046), + DigitalNow TinyUSB 2 DVB-t, + DigitalRise USB 2.0 Ter (Beetle) and + TYPHOON DVB-T USB DRIVE + + DVB-T USB2.0 receivers. + +config DVB_USB_VP702X + tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support" + depends on DVB_USB + help + Say Y here to support the + + TwinhanDTV StarBox, + DigitalRise USB Starbox and + TYPHOON DVB-S USB 2.0 BOX + + DVB-S USB2.0 receivers. + +config DVB_USB_GP8PSK + tristate "GENPIX 8PSK->USB module support" + depends on DVB_USB + help + Say Y here to support the + GENPIX 8psk module + + DVB-S USB2.0 receivers. + +config DVB_USB_NOVA_T_USB2 + tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" + depends on DVB_USB + select DVB_DIB3000MC + select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. + +config DVB_USB_TTUSB2 + tristate "Pinnacle 400e DVB-S USB2.0 support" + depends on DVB_USB + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + help + Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The + firmware protocol used by this module is similar to the one used by the + old ttusb-driver - that's why the module is called dvb-usb-ttusb2. + +config DVB_USB_DTT200U + tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)" + depends on DVB_USB + help + Say Y here to support the WideView/Yakumo/Hama/Typhoon/Yuan DVB-T USB2.0 receiver. + + The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan). + + The WT-220U and its clones are pen-sized. + +config DVB_USB_OPERA1 + tristate "Opera1 DVB-S USB2.0 receiver" + depends on DVB_USB + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_PLL if !DVB_FE_CUSTOMISE + help + Say Y here to support the Opera DVB-S USB2.0 receiver. + +config DVB_USB_AF9005 + tristate "Afatech AF9005 DVB-T USB1.1 support" + depends on DVB_USB && EXPERIMENTAL + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver + and the TerraTec Cinergy T USB XE (Rev.1) + +config DVB_USB_AF9005_REMOTE + tristate "Afatech AF9005 default remote control support" + depends on DVB_USB_AF9005 + help + Say Y here to support the default remote control decoding for the + Afatech AF9005 based receiver. + +config DVB_USB_PCTV452E + tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" + depends on DVB_USB + select TTPCI_EEPROM + select DVB_LNBP22 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + help + Support for external USB adapter designed by Pinnacle, + shipped under the brand name 'PCTV HDTV Pro USB'. + Also supports TT Connect S2-3600/3650 cards. + Say Y if you own such a device and want to use it. + +config DVB_USB_DW2102 + tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support" + depends on DVB_USB + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_STV0288 if !DVB_FE_CUSTOMISE + select DVB_STB6000 if !DVB_FE_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE + select DVB_SI21XX if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_MT312 if !DVB_FE_CUSTOMISE + select DVB_ZL10039 if !DVB_FE_CUSTOMISE + select DVB_DS3000 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_STV6110 if !DVB_FE_CUSTOMISE + select DVB_STV0900 if !DVB_FE_CUSTOMISE + help + Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 + receivers. + +config DVB_USB_CINERGY_T2 + tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" + depends on DVB_USB + help + Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers + + Say Y if you own such a device and want to use it. + +config DVB_USB_DTV5100 + tristate "AME DTV-5100 USB2.0 DVB-T support" + depends on DVB_USB + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. + +config DVB_USB_FRIIO + tristate "Friio ISDB-T USB2.0 Receiver support" + depends on DVB_USB + help + Say Y here to support the Japanese DTV receiver Friio. + +config DVB_USB_AZ6027 + tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" + depends on DVB_USB + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + help + Say Y here to support the AZ6027 device + +config DVB_USB_TECHNISAT_USB2 + tristate "Technisat DVB-S/S2 USB2.0 support" + depends on DVB_USB + select DVB_STV090x if !DVB_FE_CUSTOMISE + select DVB_STV6110x if !DVB_FE_CUSTOMISE + help + Say Y here to support the Technisat USB2 DVB-S/S2 device diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile new file mode 100644 index 000000000000..a5630e30eadc --- /dev/null +++ b/drivers/media/usb/dvb-usb/Makefile @@ -0,0 +1,82 @@ +dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o +obj-$(CONFIG_DVB_USB) += dvb-usb.o + +dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o +obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o + +dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o +obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o + +dvb-usb-gp8psk-objs = gp8psk.o gp8psk-fe.o +obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o + +dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o +obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o + +dvb-usb-dibusb-common-objs = dibusb-common.o + +dvb-usb-a800-objs = a800.o +obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o + +dvb-usb-dibusb-mb-objs = dibusb-mb.o +obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o + +dvb-usb-dibusb-mc-objs = dibusb-mc.o +obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o + +dvb-usb-nova-t-usb2-objs = nova-t-usb2.o +obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o + +dvb-usb-umt-010-objs = umt-010.o +obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o + +dvb-usb-m920x-objs = m920x.o +obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o + +dvb-usb-digitv-objs = digitv.o +obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o + +dvb-usb-cxusb-objs = cxusb.o +obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o + +dvb-usb-ttusb2-objs = ttusb2.o +obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o + +dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o +obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o + +dvb-usb-opera-objs = opera1.o +obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o + +dvb-usb-af9005-objs = af9005.o af9005-fe.o +obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o + +dvb-usb-af9005-remote-objs = af9005-remote.o +obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o + +dvb-usb-pctv452e-objs = pctv452e.o +obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o + +dvb-usb-dw2102-objs = dw2102.o +obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o + +dvb-usb-dtv5100-objs = dtv5100.o +obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o + +dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o +obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o + +dvb-usb-friio-objs = friio.o friio-fe.o +obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o + +dvb-usb-az6027-objs = az6027.o +obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o + +dvb-usb-technisat-usb2-objs = technisat-usb2.o +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/dvb/ttpci diff --git a/drivers/media/usb/dvb-usb/a800.c b/drivers/media/usb/dvb-usb/a800.c new file mode 100644 index 000000000000..8d7fef84afd8 --- /dev/null +++ b/drivers/media/usb/dvb-usb/a800.c @@ -0,0 +1,191 @@ +/* DVB USB framework compliant Linux driver for the AVerMedia AverTV DVB-T + * USB2.0 (A800) DVB-T receiver. + * + * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to + * - AVerMedia who kindly provided information and + * - Glen Harris who suffered from my mistakes during development. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (rc=1 (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define deb_rc(args...) dprintk(debug,0x01,args) + +static int a800_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + /* do nothing for the AVerMedia */ + return 0; +} + +/* assure to put cold to 0 for iManufacturer == 1 */ +static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold) +{ + *cold = udev->descriptor.iManufacturer != 1; + return 0; +} + +static struct rc_map_table rc_map_a800_table[] = { + { 0x0201, KEY_MODE }, /* SOURCE */ + { 0x0200, KEY_POWER2 }, /* POWER */ + { 0x0205, KEY_1 }, /* 1 */ + { 0x0206, KEY_2 }, /* 2 */ + { 0x0207, KEY_3 }, /* 3 */ + { 0x0209, KEY_4 }, /* 4 */ + { 0x020a, KEY_5 }, /* 5 */ + { 0x020b, KEY_6 }, /* 6 */ + { 0x020d, KEY_7 }, /* 7 */ + { 0x020e, KEY_8 }, /* 8 */ + { 0x020f, KEY_9 }, /* 9 */ + { 0x0212, KEY_LEFT }, /* L / DISPLAY */ + { 0x0211, KEY_0 }, /* 0 */ + { 0x0213, KEY_RIGHT }, /* R / CH RTN */ + { 0x0217, KEY_CAMERA }, /* SNAP SHOT */ + { 0x0210, KEY_LAST }, /* 16-CH PREV */ + { 0x021e, KEY_VOLUMEDOWN }, /* VOL DOWN */ + { 0x020c, KEY_ZOOM }, /* FULL SCREEN */ + { 0x021f, KEY_VOLUMEUP }, /* VOL UP */ + { 0x0214, KEY_MUTE }, /* MUTE */ + { 0x0208, KEY_AUDIO }, /* AUDIO */ + { 0x0219, KEY_RECORD }, /* RECORD */ + { 0x0218, KEY_PLAY }, /* PLAY */ + { 0x021b, KEY_STOP }, /* STOP */ + { 0x021a, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ + { 0x021d, KEY_BACK }, /* << / RED */ + { 0x021c, KEY_FORWARD }, /* >> / YELLOW */ + { 0x0203, KEY_TEXT }, /* TELETEXT */ + { 0x0204, KEY_EPG }, /* EPG */ + { 0x0215, KEY_MENU }, /* MENU */ + + { 0x0303, KEY_CHANNELUP }, /* CH UP */ + { 0x0302, KEY_CHANNELDOWN }, /* CH DOWN */ + { 0x0301, KEY_FIRST }, /* |<< / GREEN */ + { 0x0300, KEY_LAST }, /* >>| / BLUE */ + +}; + +static int a800_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + int ret; + u8 *key = kmalloc(5, GFP_KERNEL); + if (!key) + return -ENOMEM; + + if (usb_control_msg(d->udev,usb_rcvctrlpipe(d->udev,0), + 0x04, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, key, 5, + 2000) != 5) { + ret = -ENODEV; + goto out; + } + + /* call the universal NEC remote processor, to find out the key's state and event */ + dvb_usb_nec_rc_key_to_event(d,key,event,state); + if (key[0] != 0) + deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + ret = 0; +out: + kfree(key); + return ret; +} + +/* USB Driver stuff */ +static struct dvb_usb_device_properties a800_properties; + +static int a800_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &a800_properties, + THIS_MODULE, NULL, adapter_nr); +} + +/* do not change the order of the ID table */ +static struct usb_device_id a800_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, a800_table); + +static struct dvb_usb_device_properties a800_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-avertv-a800-02.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + }, + }, + + .power_ctrl = a800_power_ctrl, + .identify_state = a800_identify_state, + + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_map_table = rc_map_a800_table, + .rc_map_size = ARRAY_SIZE(rc_map_a800_table), + .rc_query = a800_rc_query, + }, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + .num_device_descs = 1, + .devices = { + { "AVerMedia AverTV DVB-T USB 2.0 (A800)", + { &a800_table[0], NULL }, + { &a800_table[1], NULL }, + }, + } +}; + +static struct usb_driver a800_driver = { + .name = "dvb_usb_a800", + .probe = a800_probe, + .disconnect = dvb_usb_device_exit, + .id_table = a800_table, +}; + +module_usb_driver(a800_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("AVerMedia AverTV DVB-T USB 2.0 (A800)"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/af9005-fe.c b/drivers/media/usb/dvb-usb/af9005-fe.c new file mode 100644 index 000000000000..740f3f496f12 --- /dev/null +++ b/drivers/media/usb/dvb-usb/af9005-fe.c @@ -0,0 +1,1487 @@ +/* Frontend part of the Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "af9005.h" +#include "af9005-script.h" +#include "mt2060.h" +#include "qt1010.h" +#include + +struct af9005_fe_state { + struct dvb_usb_device *d; + fe_status_t stat; + + /* retraining parameters */ + u32 original_fcw; + u16 original_rf_top; + u16 original_if_top; + u16 original_if_min; + u16 original_aci0_if_top; + u16 original_aci1_if_top; + u16 original_aci0_if_min; + u8 original_if_unplug_th; + u8 original_rf_unplug_th; + u8 original_dtop_if_unplug_th; + u8 original_dtop_rf_unplug_th; + + /* statistics */ + u32 pre_vit_error_count; + u32 pre_vit_bit_count; + u32 ber; + u32 post_vit_error_count; + u32 post_vit_bit_count; + u32 unc; + u16 abort_count; + + int opened; + int strong; + unsigned long next_status_check; + struct dvb_frontend frontend; +}; + +static int af9005_write_word_agc(struct dvb_usb_device *d, u16 reghi, + u16 reglo, u8 pos, u8 len, u16 value) +{ + int ret; + + if ((ret = af9005_write_ofdm_register(d, reglo, (u8) (value & 0xff)))) + return ret; + return af9005_write_register_bits(d, reghi, pos, len, + (u8) ((value & 0x300) >> 8)); +} + +static int af9005_read_word_agc(struct dvb_usb_device *d, u16 reghi, + u16 reglo, u8 pos, u8 len, u16 * value) +{ + int ret; + u8 temp0, temp1; + + if ((ret = af9005_read_ofdm_register(d, reglo, &temp0))) + return ret; + if ((ret = af9005_read_ofdm_register(d, reghi, &temp1))) + return ret; + switch (pos) { + case 0: + *value = ((u16) (temp1 & 0x03) << 8) + (u16) temp0; + break; + case 2: + *value = ((u16) (temp1 & 0x0C) << 6) + (u16) temp0; + break; + case 4: + *value = ((u16) (temp1 & 0x30) << 4) + (u16) temp0; + break; + case 6: + *value = ((u16) (temp1 & 0xC0) << 2) + (u16) temp0; + break; + default: + err("invalid pos in read word agc"); + return -EINVAL; + } + return 0; + +} + +static int af9005_is_fecmon_available(struct dvb_frontend *fe, int *available) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 temp; + + *available = false; + + ret = af9005_read_register_bits(state->d, xd_p_fec_vtb_rsd_mon_en, + fec_vtb_rsd_mon_en_pos, + fec_vtb_rsd_mon_en_len, &temp); + if (ret) + return ret; + if (temp & 1) { + ret = + af9005_read_register_bits(state->d, + xd_p_reg_ofsm_read_rbc_en, + reg_ofsm_read_rbc_en_pos, + reg_ofsm_read_rbc_en_len, &temp); + if (ret) + return ret; + if ((temp & 1) == 0) + *available = true; + + } + return 0; +} + +static int af9005_get_post_vit_err_cw_count(struct dvb_frontend *fe, + u32 * post_err_count, + u32 * post_cw_count, + u16 * abort_count) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u32 err_count; + u32 cw_count; + u8 temp, temp0, temp1, temp2; + u16 loc_abort_count; + + *post_err_count = 0; + *post_cw_count = 0; + + /* check if error bit count is ready */ + ret = + af9005_read_register_bits(state->d, xd_r_fec_rsd_ber_rdy, + fec_rsd_ber_rdy_pos, fec_rsd_ber_rdy_len, + &temp); + if (ret) + return ret; + if (!temp) { + deb_info("rsd counter not ready\n"); + return 100; + } + /* get abort count */ + ret = + af9005_read_ofdm_register(state->d, + xd_r_fec_rsd_abort_packet_cnt_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, + xd_r_fec_rsd_abort_packet_cnt_15_8, + &temp1); + if (ret) + return ret; + loc_abort_count = ((u16) temp1 << 8) + temp0; + + /* get error count */ + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_15_8, + &temp1); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_23_16, + &temp2); + if (ret) + return ret; + err_count = ((u32) temp2 << 16) + ((u32) temp1 << 8) + temp0; + *post_err_count = err_count - (u32) loc_abort_count *8 * 8; + + /* get RSD packet number */ + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_15_8, + &temp1); + if (ret) + return ret; + cw_count = ((u32) temp1 << 8) + temp0; + if (cw_count == 0) { + err("wrong RSD packet count"); + return -EIO; + } + deb_info("POST abort count %d err count %d rsd packets %d\n", + loc_abort_count, err_count, cw_count); + *post_cw_count = cw_count - (u32) loc_abort_count; + *abort_count = loc_abort_count; + return 0; + +} + +static int af9005_get_post_vit_ber(struct dvb_frontend *fe, + u32 * post_err_count, u32 * post_cw_count, + u16 * abort_count) +{ + u32 loc_cw_count = 0, loc_err_count; + u16 loc_abort_count = 0; + int ret; + + ret = + af9005_get_post_vit_err_cw_count(fe, &loc_err_count, &loc_cw_count, + &loc_abort_count); + if (ret) + return ret; + *post_err_count = loc_err_count; + *post_cw_count = loc_cw_count * 204 * 8; + *abort_count = loc_abort_count; + + return 0; +} + +static int af9005_get_pre_vit_err_bit_count(struct dvb_frontend *fe, + u32 * pre_err_count, + u32 * pre_bit_count) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + u8 temp, temp0, temp1, temp2; + u32 super_frame_count, x, bits; + int ret; + + ret = + af9005_read_register_bits(state->d, xd_r_fec_vtb_ber_rdy, + fec_vtb_ber_rdy_pos, fec_vtb_ber_rdy_len, + &temp); + if (ret) + return ret; + if (!temp) { + deb_info("viterbi counter not ready\n"); + return 101; /* ERR_APO_VTB_COUNTER_NOT_READY; */ + } + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_15_8, + &temp1); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_23_16, + &temp2); + if (ret) + return ret; + *pre_err_count = ((u32) temp2 << 16) + ((u32) temp1 << 8) + temp0; + + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_super_frm_unit_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_super_frm_unit_15_8, + &temp1); + if (ret) + return ret; + super_frame_count = ((u32) temp1 << 8) + temp0; + if (super_frame_count == 0) { + deb_info("super frame count 0\n"); + return 102; + } + + /* read fft mode */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_txmod, + reg_tpsd_txmod_pos, reg_tpsd_txmod_len, + &temp); + if (ret) + return ret; + if (temp == 0) { + /* 2K */ + x = 1512; + } else if (temp == 1) { + /* 8k */ + x = 6048; + } else { + err("Invalid fft mode"); + return -EINVAL; + } + + /* read modulation mode */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_const, + reg_tpsd_const_pos, reg_tpsd_const_len, + &temp); + if (ret) + return ret; + switch (temp) { + case 0: /* QPSK */ + bits = 2; + break; + case 1: /* QAM_16 */ + bits = 4; + break; + case 2: /* QAM_64 */ + bits = 6; + break; + default: + err("invalid modulation mode"); + return -EINVAL; + } + *pre_bit_count = super_frame_count * 68 * 4 * x * bits; + deb_info("PRE err count %d frame count %d bit count %d\n", + *pre_err_count, super_frame_count, *pre_bit_count); + return 0; +} + +static int af9005_reset_pre_viterbi(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + + /* set super frame count to 1 */ + ret = + af9005_write_ofdm_register(state->d, xd_p_fec_super_frm_unit_7_0, + 1 & 0xff); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, xd_p_fec_super_frm_unit_15_8, + 1 >> 8); + if (ret) + return ret; + /* reset pre viterbi error count */ + ret = + af9005_write_register_bits(state->d, xd_p_fec_vtb_ber_rst, + fec_vtb_ber_rst_pos, fec_vtb_ber_rst_len, + 1); + + return ret; +} + +static int af9005_reset_post_viterbi(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + + /* set packet unit */ + ret = + af9005_write_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_7_0, + 10000 & 0xff); + if (ret) + return ret; + ret = + af9005_write_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_15_8, + 10000 >> 8); + if (ret) + return ret; + /* reset post viterbi error count */ + ret = + af9005_write_register_bits(state->d, xd_p_fec_rsd_ber_rst, + fec_rsd_ber_rst_pos, fec_rsd_ber_rst_len, + 1); + + return ret; +} + +static int af9005_get_statistic(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret, fecavailable; + u64 numerator, denominator; + + deb_info("GET STATISTIC\n"); + ret = af9005_is_fecmon_available(fe, &fecavailable); + if (ret) + return ret; + if (!fecavailable) { + deb_info("fecmon not available\n"); + return 0; + } + + ret = af9005_get_pre_vit_err_bit_count(fe, &state->pre_vit_error_count, + &state->pre_vit_bit_count); + if (ret == 0) { + af9005_reset_pre_viterbi(fe); + if (state->pre_vit_bit_count > 0) { + /* according to v 0.0.4 of the dvb api ber should be a multiple + of 10E-9 so we have to multiply the error count by + 10E9=1000000000 */ + numerator = + (u64) state->pre_vit_error_count * (u64) 1000000000; + denominator = (u64) state->pre_vit_bit_count; + state->ber = do_div(numerator, denominator); + } else { + state->ber = 0xffffffff; + } + } + + ret = af9005_get_post_vit_ber(fe, &state->post_vit_error_count, + &state->post_vit_bit_count, + &state->abort_count); + if (ret == 0) { + ret = af9005_reset_post_viterbi(fe); + state->unc += state->abort_count; + if (ret) + return ret; + } + return 0; +} + +static int af9005_fe_refresh_state(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + if (time_after(jiffies, state->next_status_check)) { + deb_info("REFRESH STATE\n"); + + /* statistics */ + if (af9005_get_statistic(fe)) + err("get_statistic_failed"); + state->next_status_check = jiffies + 250 * HZ / 1000; + } + return 0; +} + +static int af9005_fe_read_status(struct dvb_frontend *fe, fe_status_t * stat) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + u8 temp; + int ret; + + if (fe->ops.tuner_ops.release == NULL) + return -ENODEV; + + *stat = 0; + ret = af9005_read_register_bits(state->d, xd_p_agc_lock, + agc_lock_pos, agc_lock_len, &temp); + if (ret) + return ret; + if (temp) + *stat |= FE_HAS_SIGNAL; + + ret = af9005_read_register_bits(state->d, xd_p_fd_tpsd_lock, + fd_tpsd_lock_pos, fd_tpsd_lock_len, + &temp); + if (ret) + return ret; + if (temp) + *stat |= FE_HAS_CARRIER; + + ret = af9005_read_register_bits(state->d, + xd_r_mp2if_sync_byte_locked, + mp2if_sync_byte_locked_pos, + mp2if_sync_byte_locked_pos, &temp); + if (ret) + return ret; + if (temp) + *stat |= FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_LOCK; + if (state->opened) + af9005_led_control(state->d, *stat & FE_HAS_LOCK); + + ret = + af9005_read_register_bits(state->d, xd_p_reg_strong_sginal_detected, + reg_strong_sginal_detected_pos, + reg_strong_sginal_detected_len, &temp); + if (ret) + return ret; + if (temp != state->strong) { + deb_info("adjust for strong signal %d\n", temp); + state->strong = temp; + } + return 0; +} + +static int af9005_fe_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + if (fe->ops.tuner_ops.release == NULL) + return -ENODEV; + af9005_fe_refresh_state(fe); + *ber = state->ber; + return 0; +} + +static int af9005_fe_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + if (fe->ops.tuner_ops.release == NULL) + return -ENODEV; + af9005_fe_refresh_state(fe); + *unc = state->unc; + return 0; +} + +static int af9005_fe_read_signal_strength(struct dvb_frontend *fe, + u16 * strength) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 if_gain, rf_gain; + + if (fe->ops.tuner_ops.release == NULL) + return -ENODEV; + ret = + af9005_read_ofdm_register(state->d, xd_r_reg_aagc_rf_gain, + &rf_gain); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_reg_aagc_if_gain, + &if_gain); + if (ret) + return ret; + /* this value has no real meaning, but i don't have the tables that relate + the rf and if gain with the dbm, so I just scale the value */ + *strength = (512 - rf_gain - if_gain) << 7; + return 0; +} + +static int af9005_fe_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + /* the snr can be derived from the ber and the modulation + but I don't think this kind of complex calculations belong + in the driver. I may be wrong.... */ + return -ENOSYS; +} + +static int af9005_fe_program_cfoe(struct dvb_usb_device *d, u32 bw) +{ + u8 temp0, temp1, temp2, temp3, buf[4]; + int ret; + u32 NS_coeff1_2048Nu; + u32 NS_coeff1_8191Nu; + u32 NS_coeff1_8192Nu; + u32 NS_coeff1_8193Nu; + u32 NS_coeff2_2k; + u32 NS_coeff2_8k; + + switch (bw) { + case 6000000: + NS_coeff1_2048Nu = 0x2ADB6DC; + NS_coeff1_8191Nu = 0xAB7313; + NS_coeff1_8192Nu = 0xAB6DB7; + NS_coeff1_8193Nu = 0xAB685C; + NS_coeff2_2k = 0x156DB6E; + NS_coeff2_8k = 0x55B6DC; + break; + + case 7000000: + NS_coeff1_2048Nu = 0x3200001; + NS_coeff1_8191Nu = 0xC80640; + NS_coeff1_8192Nu = 0xC80000; + NS_coeff1_8193Nu = 0xC7F9C0; + NS_coeff2_2k = 0x1900000; + NS_coeff2_8k = 0x640000; + break; + + case 8000000: + NS_coeff1_2048Nu = 0x3924926; + NS_coeff1_8191Nu = 0xE4996E; + NS_coeff1_8192Nu = 0xE49249; + NS_coeff1_8193Nu = 0xE48B25; + NS_coeff2_2k = 0x1C92493; + NS_coeff2_8k = 0x724925; + break; + default: + err("Invalid bandwidth %d.", bw); + return -EINVAL; + } + + /* + * write NS_coeff1_2048Nu + */ + + temp0 = (u8) (NS_coeff1_2048Nu & 0x000000FF); + temp1 = (u8) ((NS_coeff1_2048Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_2048Nu & 0x00FF0000) >> 16); + temp3 = (u8) ((NS_coeff1_2048Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + /* cfoe_NS_2k_coeff1_25_24 */ + ret = af9005_write_ofdm_register(d, 0xAE00, buf[0]); + if (ret) + return ret; + + /* cfoe_NS_2k_coeff1_23_16 */ + ret = af9005_write_ofdm_register(d, 0xAE01, buf[1]); + if (ret) + return ret; + + /* cfoe_NS_2k_coeff1_15_8 */ + ret = af9005_write_ofdm_register(d, 0xAE02, buf[2]); + if (ret) + return ret; + + /* cfoe_NS_2k_coeff1_7_0 */ + ret = af9005_write_ofdm_register(d, 0xAE03, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff2_2k + */ + + temp0 = (u8) ((NS_coeff2_2k & 0x0000003F)); + temp1 = (u8) ((NS_coeff2_2k & 0x00003FC0) >> 6); + temp2 = (u8) ((NS_coeff2_2k & 0x003FC000) >> 14); + temp3 = (u8) ((NS_coeff2_2k & 0x01C00000) >> 22); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE04, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE05, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE06, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE07, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff1_8191Nu + */ + + temp0 = (u8) ((NS_coeff1_8191Nu & 0x000000FF)); + temp1 = (u8) ((NS_coeff1_8191Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_8191Nu & 0x00FFC000) >> 16); + temp3 = (u8) ((NS_coeff1_8191Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE08, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE09, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0A, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0B, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff1_8192Nu + */ + + temp0 = (u8) (NS_coeff1_8192Nu & 0x000000FF); + temp1 = (u8) ((NS_coeff1_8192Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_8192Nu & 0x00FFC000) >> 16); + temp3 = (u8) ((NS_coeff1_8192Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE0C, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0D, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0E, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0F, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff1_8193Nu + */ + + temp0 = (u8) ((NS_coeff1_8193Nu & 0x000000FF)); + temp1 = (u8) ((NS_coeff1_8193Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_8193Nu & 0x00FFC000) >> 16); + temp3 = (u8) ((NS_coeff1_8193Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE10, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE11, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE12, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE13, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff2_8k + */ + + temp0 = (u8) ((NS_coeff2_8k & 0x0000003F)); + temp1 = (u8) ((NS_coeff2_8k & 0x00003FC0) >> 6); + temp2 = (u8) ((NS_coeff2_8k & 0x003FC000) >> 14); + temp3 = (u8) ((NS_coeff2_8k & 0x01C00000) >> 22); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE14, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE15, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE16, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE17, buf[3]); + return ret; + +} + +static int af9005_fe_select_bw(struct dvb_usb_device *d, u32 bw) +{ + u8 temp; + switch (bw) { + case 6000000: + temp = 0; + break; + case 7000000: + temp = 1; + break; + case 8000000: + temp = 2; + break; + default: + err("Invalid bandwidth %d.", bw); + return -EINVAL; + } + return af9005_write_register_bits(d, xd_g_reg_bw, reg_bw_pos, + reg_bw_len, temp); +} + +static int af9005_fe_power(struct dvb_frontend *fe, int on) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + u8 temp = on; + int ret; + deb_info("power %s tuner\n", on ? "on" : "off"); + ret = af9005_send_command(state->d, 0x03, &temp, 1, NULL, 0); + return ret; +} + +static struct mt2060_config af9005_mt2060_config = { + 0xC0 +}; + +static struct qt1010_config af9005_qt1010_config = { + 0xC4 +}; + +static int af9005_fe_init(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + struct dvb_usb_adapter *adap = fe->dvb->priv; + int ret, i, scriptlen; + u8 temp, temp0 = 0, temp1 = 0, temp2 = 0; + u8 buf[2]; + u16 if1; + + deb_info("in af9005_fe_init\n"); + + /* reset */ + deb_info("reset\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst_en, + 4, 1, 0x01))) + return ret; + if ((ret = af9005_write_ofdm_register(state->d, APO_REG_RESET, 0))) + return ret; + /* clear ofdm reset */ + deb_info("clear ofdm reset\n"); + for (i = 0; i < 150; i++) { + if ((ret = + af9005_read_ofdm_register(state->d, + xd_I2C_reg_ofdm_rst, &temp))) + return ret; + if (temp & (regmask[reg_ofdm_rst_len - 1] << reg_ofdm_rst_pos)) + break; + msleep(10); + } + if (i == 150) + return -ETIMEDOUT; + + /*FIXME in the dump + write B200 A9 + write xd_g_reg_ofsm_clk 7 + read eepr c6 (2) + read eepr c7 (2) + misc ctrl 3 -> 1 + read eepr ca (6) + write xd_g_reg_ofsm_clk 0 + write B200 a1 + */ + ret = af9005_write_ofdm_register(state->d, 0xb200, 0xa9); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, xd_g_reg_ofsm_clk, 0x07); + if (ret) + return ret; + temp = 0x01; + ret = af9005_send_command(state->d, 0x03, &temp, 1, NULL, 0); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, xd_g_reg_ofsm_clk, 0x00); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xb200, 0xa1); + if (ret) + return ret; + + temp = regmask[reg_ofdm_rst_len - 1] << reg_ofdm_rst_pos; + if ((ret = + af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst, + reg_ofdm_rst_pos, reg_ofdm_rst_len, 1))) + return ret; + ret = af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst, + reg_ofdm_rst_pos, reg_ofdm_rst_len, 0); + + if (ret) + return ret; + /* don't know what register aefc is, but this is what the windows driver does */ + ret = af9005_write_ofdm_register(state->d, 0xaefc, 0); + if (ret) + return ret; + + /* set stand alone chip */ + deb_info("set stand alone chip\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_stand_alone, + reg_dca_stand_alone_pos, + reg_dca_stand_alone_len, 1))) + return ret; + + /* set dca upper & lower chip */ + deb_info("set dca upper & lower chip\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_upper_chip, + reg_dca_upper_chip_pos, + reg_dca_upper_chip_len, 0))) + return ret; + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_lower_chip, + reg_dca_lower_chip_pos, + reg_dca_lower_chip_len, 0))) + return ret; + + /* set 2wire master clock to 0x14 (for 60KHz) */ + deb_info("set 2wire master clock to 0x14 (for 60KHz)\n"); + if ((ret = + af9005_write_ofdm_register(state->d, xd_I2C_i2c_m_period, 0x14))) + return ret; + + /* clear dca enable chip */ + deb_info("clear dca enable chip\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_en, + reg_dca_en_pos, reg_dca_en_len, 0))) + return ret; + /* FIXME these are register bits, but I don't know which ones */ + ret = af9005_write_ofdm_register(state->d, 0xa16c, 1); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xa3c1, 0); + if (ret) + return ret; + + /* init other parameters: program cfoe and select bandwidth */ + deb_info("program cfoe\n"); + ret = af9005_fe_program_cfoe(state->d, 6000000); + if (ret) + return ret; + /* set read-update bit for modulation */ + deb_info("set read-update bit for modulation\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_feq_read_update, + reg_feq_read_update_pos, + reg_feq_read_update_len, 1))) + return ret; + + /* sample code has a set MPEG TS code here + but sniffing reveals that it doesn't do it */ + + /* set read-update bit to 1 for DCA modulation */ + deb_info("set read-update bit 1 for DCA modulation\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_read_update, + reg_dca_read_update_pos, + reg_dca_read_update_len, 1))) + return ret; + + /* enable fec monitor */ + deb_info("enable fec monitor\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_fec_vtb_rsd_mon_en, + fec_vtb_rsd_mon_en_pos, + fec_vtb_rsd_mon_en_len, 1))) + return ret; + + /* FIXME should be register bits, I don't know which ones */ + ret = af9005_write_ofdm_register(state->d, 0xa601, 0); + + /* set api_retrain_never_freeze */ + deb_info("set api_retrain_never_freeze\n"); + if ((ret = af9005_write_ofdm_register(state->d, 0xaefb, 0x01))) + return ret; + + /* load init script */ + deb_info("load init script\n"); + scriptlen = sizeof(script) / sizeof(RegDesc); + for (i = 0; i < scriptlen; i++) { + if ((ret = + af9005_write_register_bits(state->d, script[i].reg, + script[i].pos, + script[i].len, script[i].val))) + return ret; + /* save 3 bytes of original fcw */ + if (script[i].reg == 0xae18) + temp2 = script[i].val; + if (script[i].reg == 0xae19) + temp1 = script[i].val; + if (script[i].reg == 0xae1a) + temp0 = script[i].val; + + /* save original unplug threshold */ + if (script[i].reg == xd_p_reg_unplug_th) + state->original_if_unplug_th = script[i].val; + if (script[i].reg == xd_p_reg_unplug_rf_gain_th) + state->original_rf_unplug_th = script[i].val; + if (script[i].reg == xd_p_reg_unplug_dtop_if_gain_th) + state->original_dtop_if_unplug_th = script[i].val; + if (script[i].reg == xd_p_reg_unplug_dtop_rf_gain_th) + state->original_dtop_rf_unplug_th = script[i].val; + + } + state->original_fcw = + ((u32) temp2 << 16) + ((u32) temp1 << 8) + (u32) temp0; + + + /* save original TOPs */ + deb_info("save original TOPs\n"); + + /* RF TOP */ + ret = + af9005_read_word_agc(state->d, + xd_p_reg_aagc_rf_top_numerator_9_8, + xd_p_reg_aagc_rf_top_numerator_7_0, 0, 2, + &state->original_rf_top); + if (ret) + return ret; + + /* IF TOP */ + ret = + af9005_read_word_agc(state->d, + xd_p_reg_aagc_if_top_numerator_9_8, + xd_p_reg_aagc_if_top_numerator_7_0, 0, 2, + &state->original_if_top); + if (ret) + return ret; + + /* ACI 0 IF TOP */ + ret = + af9005_read_word_agc(state->d, 0xA60E, 0xA60A, 4, 2, + &state->original_aci0_if_top); + if (ret) + return ret; + + /* ACI 1 IF TOP */ + ret = + af9005_read_word_agc(state->d, 0xA60E, 0xA60B, 6, 2, + &state->original_aci1_if_top); + if (ret) + return ret; + + /* attach tuner and init */ + if (fe->ops.tuner_ops.release == NULL) { + /* read tuner and board id from eeprom */ + ret = af9005_read_eeprom(adap->dev, 0xc6, buf, 2); + if (ret) { + err("Impossible to read EEPROM\n"); + return ret; + } + deb_info("Tuner id %d, board id %d\n", buf[0], buf[1]); + switch (buf[0]) { + case 2: /* MT2060 */ + /* read if1 from eeprom */ + ret = af9005_read_eeprom(adap->dev, 0xc8, buf, 2); + if (ret) { + err("Impossible to read EEPROM\n"); + return ret; + } + if1 = (u16) (buf[0] << 8) + buf[1]; + if (dvb_attach(mt2060_attach, fe, &adap->dev->i2c_adap, + &af9005_mt2060_config, if1) == NULL) { + deb_info("MT2060 attach failed\n"); + return -ENODEV; + } + break; + case 3: /* QT1010 */ + case 9: /* QT1010B */ + if (dvb_attach(qt1010_attach, fe, &adap->dev->i2c_adap, + &af9005_qt1010_config) ==NULL) { + deb_info("QT1010 attach failed\n"); + return -ENODEV; + } + break; + default: + err("Unsupported tuner type %d", buf[0]); + return -ENODEV; + } + ret = fe->ops.tuner_ops.init(fe); + if (ret) + return ret; + } + + deb_info("profit!\n"); + return 0; +} + +static int af9005_fe_sleep(struct dvb_frontend *fe) +{ + return af9005_fe_power(fe, 0); +} + +static int af9005_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + + if (acquire) { + state->opened++; + } else { + + state->opened--; + if (!state->opened) + af9005_led_control(state->d, 0); + } + return 0; +} + +static int af9005_fe_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 temp, temp0, temp1, temp2; + + deb_info("af9005_fe_set_frontend freq %d bw %d\n", fep->frequency, + fep->bandwidth_hz); + if (fe->ops.tuner_ops.release == NULL) { + err("Tuner not attached"); + return -ENODEV; + } + + deb_info("turn off led\n"); + /* not in the log */ + ret = af9005_led_control(state->d, 0); + if (ret) + return ret; + /* not sure about the bits */ + ret = af9005_write_register_bits(state->d, XD_MP2IF_MISC, 2, 1, 0); + if (ret) + return ret; + + /* set FCW to default value */ + deb_info("set FCW to default value\n"); + temp0 = (u8) (state->original_fcw & 0x000000ff); + temp1 = (u8) ((state->original_fcw & 0x0000ff00) >> 8); + temp2 = (u8) ((state->original_fcw & 0x00ff0000) >> 16); + ret = af9005_write_ofdm_register(state->d, 0xae1a, temp0); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xae19, temp1); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xae18, temp2); + if (ret) + return ret; + + /* restore original TOPs */ + deb_info("restore original TOPs\n"); + ret = + af9005_write_word_agc(state->d, + xd_p_reg_aagc_rf_top_numerator_9_8, + xd_p_reg_aagc_rf_top_numerator_7_0, 0, 2, + state->original_rf_top); + if (ret) + return ret; + ret = + af9005_write_word_agc(state->d, + xd_p_reg_aagc_if_top_numerator_9_8, + xd_p_reg_aagc_if_top_numerator_7_0, 0, 2, + state->original_if_top); + if (ret) + return ret; + ret = + af9005_write_word_agc(state->d, 0xA60E, 0xA60A, 4, 2, + state->original_aci0_if_top); + if (ret) + return ret; + ret = + af9005_write_word_agc(state->d, 0xA60E, 0xA60B, 6, 2, + state->original_aci1_if_top); + if (ret) + return ret; + + /* select bandwidth */ + deb_info("select bandwidth"); + ret = af9005_fe_select_bw(state->d, fep->bandwidth_hz); + if (ret) + return ret; + ret = af9005_fe_program_cfoe(state->d, fep->bandwidth_hz); + if (ret) + return ret; + + /* clear easy mode flag */ + deb_info("clear easy mode flag\n"); + ret = af9005_write_ofdm_register(state->d, 0xaefd, 0); + if (ret) + return ret; + + /* set unplug threshold to original value */ + deb_info("set unplug threshold to original value\n"); + ret = + af9005_write_ofdm_register(state->d, xd_p_reg_unplug_th, + state->original_if_unplug_th); + if (ret) + return ret; + /* set tuner */ + deb_info("set tuner\n"); + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + return ret; + + /* trigger ofsm */ + deb_info("trigger ofsm\n"); + temp = 0; + ret = af9005_write_tuner_registers(state->d, 0xffff, &temp, 1); + if (ret) + return ret; + + /* clear retrain and freeze flag */ + deb_info("clear retrain and freeze flag\n"); + ret = + af9005_write_register_bits(state->d, + xd_p_reg_api_retrain_request, + reg_api_retrain_request_pos, 2, 0); + if (ret) + return ret; + + /* reset pre viterbi and post viterbi registers and statistics */ + af9005_reset_pre_viterbi(fe); + af9005_reset_post_viterbi(fe); + state->pre_vit_error_count = 0; + state->pre_vit_bit_count = 0; + state->ber = 0; + state->post_vit_error_count = 0; + /* state->unc = 0; commented out since it should be ever increasing */ + state->abort_count = 0; + + state->next_status_check = jiffies; + state->strong = -1; + + return 0; +} + +static int af9005_fe_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 temp; + + /* mode */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_const, + reg_tpsd_const_pos, reg_tpsd_const_len, + &temp); + if (ret) + return ret; + deb_info("===== fe_get_frontend_legacy = =============\n"); + deb_info("CONSTELLATION "); + switch (temp) { + case 0: + fep->modulation = QPSK; + deb_info("QPSK\n"); + break; + case 1: + fep->modulation = QAM_16; + deb_info("QAM_16\n"); + break; + case 2: + fep->modulation = QAM_64; + deb_info("QAM_64\n"); + break; + } + + /* tps hierarchy and alpha value */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_hier, + reg_tpsd_hier_pos, reg_tpsd_hier_len, + &temp); + if (ret) + return ret; + deb_info("HIERARCHY "); + switch (temp) { + case 0: + fep->hierarchy = HIERARCHY_NONE; + deb_info("NONE\n"); + break; + case 1: + fep->hierarchy = HIERARCHY_1; + deb_info("1\n"); + break; + case 2: + fep->hierarchy = HIERARCHY_2; + deb_info("2\n"); + break; + case 3: + fep->hierarchy = HIERARCHY_4; + deb_info("4\n"); + break; + } + + /* high/low priority */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_dec_pri, + reg_dec_pri_pos, reg_dec_pri_len, &temp); + if (ret) + return ret; + /* if temp is set = high priority */ + deb_info("PRIORITY %s\n", temp ? "high" : "low"); + + /* high coderate */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_hpcr, + reg_tpsd_hpcr_pos, reg_tpsd_hpcr_len, + &temp); + if (ret) + return ret; + deb_info("CODERATE HP "); + switch (temp) { + case 0: + fep->code_rate_HP = FEC_1_2; + deb_info("FEC_1_2\n"); + break; + case 1: + fep->code_rate_HP = FEC_2_3; + deb_info("FEC_2_3\n"); + break; + case 2: + fep->code_rate_HP = FEC_3_4; + deb_info("FEC_3_4\n"); + break; + case 3: + fep->code_rate_HP = FEC_5_6; + deb_info("FEC_5_6\n"); + break; + case 4: + fep->code_rate_HP = FEC_7_8; + deb_info("FEC_7_8\n"); + break; + } + + /* low coderate */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_lpcr, + reg_tpsd_lpcr_pos, reg_tpsd_lpcr_len, + &temp); + if (ret) + return ret; + deb_info("CODERATE LP "); + switch (temp) { + case 0: + fep->code_rate_LP = FEC_1_2; + deb_info("FEC_1_2\n"); + break; + case 1: + fep->code_rate_LP = FEC_2_3; + deb_info("FEC_2_3\n"); + break; + case 2: + fep->code_rate_LP = FEC_3_4; + deb_info("FEC_3_4\n"); + break; + case 3: + fep->code_rate_LP = FEC_5_6; + deb_info("FEC_5_6\n"); + break; + case 4: + fep->code_rate_LP = FEC_7_8; + deb_info("FEC_7_8\n"); + break; + } + + /* guard interval */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_gi, + reg_tpsd_gi_pos, reg_tpsd_gi_len, &temp); + if (ret) + return ret; + deb_info("GUARD INTERVAL "); + switch (temp) { + case 0: + fep->guard_interval = GUARD_INTERVAL_1_32; + deb_info("1_32\n"); + break; + case 1: + fep->guard_interval = GUARD_INTERVAL_1_16; + deb_info("1_16\n"); + break; + case 2: + fep->guard_interval = GUARD_INTERVAL_1_8; + deb_info("1_8\n"); + break; + case 3: + fep->guard_interval = GUARD_INTERVAL_1_4; + deb_info("1_4\n"); + break; + } + + /* fft */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_txmod, + reg_tpsd_txmod_pos, reg_tpsd_txmod_len, + &temp); + if (ret) + return ret; + deb_info("TRANSMISSION MODE "); + switch (temp) { + case 0: + fep->transmission_mode = TRANSMISSION_MODE_2K; + deb_info("2K\n"); + break; + case 1: + fep->transmission_mode = TRANSMISSION_MODE_8K; + deb_info("8K\n"); + break; + } + + /* bandwidth */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_bw, reg_bw_pos, + reg_bw_len, &temp); + deb_info("BANDWIDTH "); + switch (temp) { + case 0: + fep->bandwidth_hz = 6000000; + deb_info("6\n"); + break; + case 1: + fep->bandwidth_hz = 7000000; + deb_info("7\n"); + break; + case 2: + fep->bandwidth_hz = 8000000; + deb_info("8\n"); + break; + } + return 0; +} + +static void af9005_fe_release(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = + (struct af9005_fe_state *)fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops af9005_fe_ops; + +struct dvb_frontend *af9005_fe_attach(struct dvb_usb_device *d) +{ + struct af9005_fe_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct af9005_fe_state), GFP_KERNEL); + if (state == NULL) + goto error; + + deb_info("attaching frontend af9005\n"); + + state->d = d; + state->opened = 0; + + memcpy(&state->frontend.ops, &af9005_fe_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; + error: + return NULL; +} + +static struct dvb_frontend_ops af9005_fe_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "AF9005 USB DVB-T", + .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 = af9005_fe_release, + + .init = af9005_fe_init, + .sleep = af9005_fe_sleep, + .ts_bus_ctrl = af9005_ts_bus_ctrl, + + .set_frontend = af9005_fe_set_frontend, + .get_frontend = af9005_fe_get_frontend, + + .read_status = af9005_fe_read_status, + .read_ber = af9005_fe_read_ber, + .read_signal_strength = af9005_fe_read_signal_strength, + .read_snr = af9005_fe_read_snr, + .read_ucblocks = af9005_fe_read_unc_blocks, +}; diff --git a/drivers/media/usb/dvb-usb/af9005-remote.c b/drivers/media/usb/dvb-usb/af9005-remote.c new file mode 100644 index 000000000000..7e3961d0db6b --- /dev/null +++ b/drivers/media/usb/dvb-usb/af9005-remote.c @@ -0,0 +1,157 @@ +/* DVB USB compliant Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Standard remote decode function + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "af9005.h" +/* debug */ +static int dvb_usb_af9005_remote_debug; +module_param_named(debug, dvb_usb_af9005_remote_debug, int, 0644); +MODULE_PARM_DESC(debug, + "enable (1) or disable (0) debug messages." + DVB_USB_DEBUG_STATUS); + +#define deb_decode(args...) dprintk(dvb_usb_af9005_remote_debug,0x01,args) + +struct rc_map_table rc_map_af9005_table[] = { + + {0x01b7, KEY_POWER}, + {0x01a7, KEY_VOLUMEUP}, + {0x0187, KEY_CHANNELUP}, + {0x017f, KEY_MUTE}, + {0x01bf, KEY_VOLUMEDOWN}, + {0x013f, KEY_CHANNELDOWN}, + {0x01df, KEY_1}, + {0x015f, KEY_2}, + {0x019f, KEY_3}, + {0x011f, KEY_4}, + {0x01ef, KEY_5}, + {0x016f, KEY_6}, + {0x01af, KEY_7}, + {0x0127, KEY_8}, + {0x0107, KEY_9}, + {0x01cf, KEY_ZOOM}, + {0x014f, KEY_0}, + {0x018f, KEY_GOTO}, /* marked jump on the remote */ + + {0x00bd, KEY_POWER}, + {0x007d, KEY_VOLUMEUP}, + {0x00fd, KEY_CHANNELUP}, + {0x009d, KEY_MUTE}, + {0x005d, KEY_VOLUMEDOWN}, + {0x00dd, KEY_CHANNELDOWN}, + {0x00ad, KEY_1}, + {0x006d, KEY_2}, + {0x00ed, KEY_3}, + {0x008d, KEY_4}, + {0x004d, KEY_5}, + {0x00cd, KEY_6}, + {0x00b5, KEY_7}, + {0x0075, KEY_8}, + {0x00f5, KEY_9}, + {0x0095, KEY_ZOOM}, + {0x0055, KEY_0}, + {0x00d5, KEY_GOTO}, /* marked jump on the remote */ +}; + +int rc_map_af9005_table_size = ARRAY_SIZE(rc_map_af9005_table); + +static int repeatable_keys[] = { + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_CHANNELUP, + KEY_CHANNELDOWN +}; + +int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, + int *state) +{ + u16 mark, space; + u32 result; + u8 cust, dat, invdat; + int i; + + if (len >= 6) { + mark = (u16) (data[0] << 8) + data[1]; + space = (u16) (data[2] << 8) + data[3]; + if (space * 3 < mark) { + for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) { + if (d->last_event == repeatable_keys[i]) { + *state = REMOTE_KEY_REPEAT; + *event = d->last_event; + deb_decode("repeat key, event %x\n", + *event); + return 0; + } + } + deb_decode("repeated key ignored (non repeatable)\n"); + return 0; + } else if (len >= 33 * 4) { /*32 bits + start code */ + result = 0; + for (i = 4; i < 4 + 32 * 4; i += 4) { + result <<= 1; + mark = (u16) (data[i] << 8) + data[i + 1]; + mark >>= 1; + space = (u16) (data[i + 2] << 8) + data[i + 3]; + space >>= 1; + if (mark * 2 > space) + result += 1; + } + deb_decode("key pressed, raw value %x\n", result); + if ((result & 0xff000000) != 0xfe000000) { + deb_decode + ("doesn't start with 0xfe, ignored\n"); + return 0; + } + cust = (result >> 16) & 0xff; + dat = (result >> 8) & 0xff; + invdat = (~result) & 0xff; + if (dat != invdat) { + deb_decode("code != inverted code\n"); + return 0; + } + for (i = 0; i < rc_map_af9005_table_size; i++) { + if (rc5_custom(&rc_map_af9005_table[i]) == cust + && rc5_data(&rc_map_af9005_table[i]) == dat) { + *event = rc_map_af9005_table[i].keycode; + *state = REMOTE_KEY_PRESSED; + deb_decode + ("key pressed, event %x\n", *event); + return 0; + } + } + deb_decode("not found in table\n"); + } + } + return 0; +} + +EXPORT_SYMBOL(rc_map_af9005_table); +EXPORT_SYMBOL(rc_map_af9005_table_size); +EXPORT_SYMBOL(af9005_rc_decode); + +MODULE_AUTHOR("Luca Olivetti "); +MODULE_DESCRIPTION + ("Standard remote control decoder for Afatech 9005 DVB-T USB1.1 stick"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/af9005-script.h b/drivers/media/usb/dvb-usb/af9005-script.h new file mode 100644 index 000000000000..4d69045426dd --- /dev/null +++ b/drivers/media/usb/dvb-usb/af9005-script.h @@ -0,0 +1,203 @@ +/* +File automatically generated by createinit.py using data +extracted from AF05BDA.sys (windows driver): + +dd if=AF05BDA.sys of=initsequence bs=1 skip=88316 count=1110 +python createinit.py > af9005-script.h + +*/ + +typedef struct { + u16 reg; + u8 pos; + u8 len; + u8 val; +} RegDesc; + +static RegDesc script[] = { + {0xa180, 0x0, 0x8, 0xa}, + {0xa181, 0x0, 0x8, 0xd7}, + {0xa182, 0x0, 0x8, 0xa3}, + {0xa0a0, 0x0, 0x8, 0x0}, + {0xa0a1, 0x0, 0x5, 0x0}, + {0xa0a1, 0x5, 0x1, 0x1}, + {0xa0c0, 0x0, 0x4, 0x1}, + {0xa20e, 0x4, 0x4, 0xa}, + {0xa20f, 0x0, 0x8, 0x40}, + {0xa210, 0x0, 0x8, 0x8}, + {0xa32a, 0x0, 0x4, 0xa}, + {0xa32c, 0x0, 0x8, 0x20}, + {0xa32b, 0x0, 0x8, 0x15}, + {0xa1a0, 0x1, 0x1, 0x1}, + {0xa000, 0x0, 0x1, 0x1}, + {0xa000, 0x1, 0x1, 0x0}, + {0xa001, 0x1, 0x1, 0x1}, + {0xa001, 0x0, 0x1, 0x0}, + {0xa001, 0x5, 0x1, 0x0}, + {0xa00e, 0x0, 0x5, 0x10}, + {0xa00f, 0x0, 0x3, 0x4}, + {0xa00f, 0x3, 0x3, 0x5}, + {0xa010, 0x0, 0x3, 0x4}, + {0xa010, 0x3, 0x3, 0x5}, + {0xa016, 0x4, 0x4, 0x3}, + {0xa01f, 0x0, 0x6, 0xa}, + {0xa020, 0x0, 0x6, 0xa}, + {0xa2bc, 0x0, 0x1, 0x1}, + {0xa2bc, 0x5, 0x1, 0x1}, + {0xa015, 0x0, 0x8, 0x50}, + {0xa016, 0x0, 0x1, 0x0}, + {0xa02a, 0x0, 0x8, 0x50}, + {0xa029, 0x0, 0x8, 0x4b}, + {0xa614, 0x0, 0x8, 0x46}, + {0xa002, 0x0, 0x5, 0x19}, + {0xa003, 0x0, 0x5, 0x1a}, + {0xa004, 0x0, 0x5, 0x19}, + {0xa005, 0x0, 0x5, 0x1a}, + {0xa008, 0x0, 0x8, 0x69}, + {0xa009, 0x0, 0x2, 0x2}, + {0xae1b, 0x0, 0x8, 0x69}, + {0xae1c, 0x0, 0x8, 0x2}, + {0xae1d, 0x0, 0x8, 0x2a}, + {0xa022, 0x0, 0x8, 0xaa}, + {0xa006, 0x0, 0x8, 0xc8}, + {0xa007, 0x0, 0x2, 0x0}, + {0xa00c, 0x0, 0x8, 0xba}, + {0xa00d, 0x0, 0x2, 0x2}, + {0xa608, 0x0, 0x8, 0xba}, + {0xa60e, 0x0, 0x2, 0x2}, + {0xa609, 0x0, 0x8, 0x80}, + {0xa60e, 0x2, 0x2, 0x3}, + {0xa00a, 0x0, 0x8, 0xb6}, + {0xa00b, 0x0, 0x2, 0x0}, + {0xa011, 0x0, 0x8, 0xb9}, + {0xa012, 0x0, 0x2, 0x0}, + {0xa013, 0x0, 0x8, 0xbd}, + {0xa014, 0x0, 0x2, 0x2}, + {0xa366, 0x0, 0x1, 0x1}, + {0xa2bc, 0x3, 0x1, 0x0}, + {0xa2bd, 0x0, 0x8, 0xa}, + {0xa2be, 0x0, 0x8, 0x14}, + {0xa2bf, 0x0, 0x8, 0x8}, + {0xa60a, 0x0, 0x8, 0xbd}, + {0xa60e, 0x4, 0x2, 0x2}, + {0xa60b, 0x0, 0x8, 0x86}, + {0xa60e, 0x6, 0x2, 0x3}, + {0xa001, 0x2, 0x2, 0x1}, + {0xa1c7, 0x0, 0x8, 0xf5}, + {0xa03d, 0x0, 0x8, 0xb1}, + {0xa616, 0x0, 0x8, 0xff}, + {0xa617, 0x0, 0x8, 0xad}, + {0xa618, 0x0, 0x8, 0xad}, + {0xa61e, 0x3, 0x1, 0x1}, + {0xae1a, 0x0, 0x8, 0x0}, + {0xae19, 0x0, 0x8, 0xc8}, + {0xae18, 0x0, 0x8, 0x61}, + {0xa140, 0x0, 0x8, 0x0}, + {0xa141, 0x0, 0x8, 0xc8}, + {0xa142, 0x0, 0x7, 0x61}, + {0xa023, 0x0, 0x8, 0xff}, + {0xa021, 0x0, 0x8, 0xad}, + {0xa026, 0x0, 0x1, 0x0}, + {0xa024, 0x0, 0x8, 0xff}, + {0xa025, 0x0, 0x8, 0xff}, + {0xa1c8, 0x0, 0x8, 0xf}, + {0xa2bc, 0x1, 0x1, 0x0}, + {0xa60c, 0x0, 0x4, 0x5}, + {0xa60c, 0x4, 0x4, 0x6}, + {0xa60d, 0x0, 0x8, 0xa}, + {0xa371, 0x0, 0x1, 0x1}, + {0xa366, 0x1, 0x3, 0x7}, + {0xa338, 0x0, 0x8, 0x10}, + {0xa339, 0x0, 0x6, 0x7}, + {0xa33a, 0x0, 0x6, 0x1f}, + {0xa33b, 0x0, 0x8, 0xf6}, + {0xa33c, 0x3, 0x5, 0x4}, + {0xa33d, 0x4, 0x4, 0x0}, + {0xa33d, 0x1, 0x1, 0x1}, + {0xa33d, 0x2, 0x1, 0x1}, + {0xa33d, 0x3, 0x1, 0x1}, + {0xa16d, 0x0, 0x4, 0xf}, + {0xa161, 0x0, 0x5, 0x5}, + {0xa162, 0x0, 0x4, 0x5}, + {0xa165, 0x0, 0x8, 0xff}, + {0xa166, 0x0, 0x8, 0x9c}, + {0xa2c3, 0x0, 0x4, 0x5}, + {0xa61a, 0x0, 0x6, 0xf}, + {0xb200, 0x0, 0x8, 0xa1}, + {0xb201, 0x0, 0x8, 0x7}, + {0xa093, 0x0, 0x1, 0x0}, + {0xa093, 0x1, 0x5, 0xf}, + {0xa094, 0x0, 0x8, 0xff}, + {0xa095, 0x0, 0x8, 0xf}, + {0xa080, 0x2, 0x5, 0x3}, + {0xa081, 0x0, 0x4, 0x0}, + {0xa081, 0x4, 0x4, 0x9}, + {0xa082, 0x0, 0x5, 0x1f}, + {0xa08d, 0x0, 0x8, 0x1}, + {0xa083, 0x0, 0x8, 0x32}, + {0xa084, 0x0, 0x1, 0x0}, + {0xa08e, 0x0, 0x8, 0x3}, + {0xa085, 0x0, 0x8, 0x32}, + {0xa086, 0x0, 0x3, 0x0}, + {0xa087, 0x0, 0x8, 0x6e}, + {0xa088, 0x0, 0x5, 0x15}, + {0xa089, 0x0, 0x8, 0x0}, + {0xa08a, 0x0, 0x5, 0x19}, + {0xa08b, 0x0, 0x8, 0x92}, + {0xa08c, 0x0, 0x5, 0x1c}, + {0xa120, 0x0, 0x8, 0x0}, + {0xa121, 0x0, 0x5, 0x10}, + {0xa122, 0x0, 0x8, 0x0}, + {0xa123, 0x0, 0x7, 0x40}, + {0xa123, 0x7, 0x1, 0x0}, + {0xa124, 0x0, 0x8, 0x13}, + {0xa125, 0x0, 0x7, 0x10}, + {0xa1c0, 0x0, 0x8, 0x0}, + {0xa1c1, 0x0, 0x5, 0x4}, + {0xa1c2, 0x0, 0x8, 0x0}, + {0xa1c3, 0x0, 0x5, 0x10}, + {0xa1c3, 0x5, 0x3, 0x0}, + {0xa1c4, 0x0, 0x6, 0x0}, + {0xa1c5, 0x0, 0x7, 0x10}, + {0xa100, 0x0, 0x8, 0x0}, + {0xa101, 0x0, 0x5, 0x10}, + {0xa102, 0x0, 0x8, 0x0}, + {0xa103, 0x0, 0x7, 0x40}, + {0xa103, 0x7, 0x1, 0x0}, + {0xa104, 0x0, 0x8, 0x18}, + {0xa105, 0x0, 0x7, 0xa}, + {0xa106, 0x0, 0x8, 0x20}, + {0xa107, 0x0, 0x8, 0x40}, + {0xa108, 0x0, 0x4, 0x0}, + {0xa38c, 0x0, 0x8, 0xfc}, + {0xa38d, 0x0, 0x8, 0x0}, + {0xa38e, 0x0, 0x8, 0x7e}, + {0xa38f, 0x0, 0x8, 0x0}, + {0xa390, 0x0, 0x8, 0x2f}, + {0xa60f, 0x5, 0x1, 0x1}, + {0xa170, 0x0, 0x8, 0xdc}, + {0xa171, 0x0, 0x2, 0x0}, + {0xa2ae, 0x0, 0x1, 0x1}, + {0xa2ae, 0x1, 0x1, 0x1}, + {0xa392, 0x0, 0x1, 0x1}, + {0xa391, 0x2, 0x1, 0x0}, + {0xabc1, 0x0, 0x8, 0xff}, + {0xabc2, 0x0, 0x8, 0x0}, + {0xabc8, 0x0, 0x8, 0x8}, + {0xabca, 0x0, 0x8, 0x10}, + {0xabcb, 0x0, 0x1, 0x0}, + {0xabc3, 0x5, 0x3, 0x7}, + {0xabc0, 0x6, 0x1, 0x0}, + {0xabc0, 0x4, 0x2, 0x0}, + {0xa344, 0x4, 0x4, 0x1}, + {0xabc0, 0x7, 0x1, 0x1}, + {0xabc0, 0x2, 0x1, 0x1}, + {0xa345, 0x0, 0x8, 0x66}, + {0xa346, 0x0, 0x8, 0x66}, + {0xa347, 0x0, 0x4, 0x0}, + {0xa343, 0x0, 0x4, 0xa}, + {0xa347, 0x4, 0x4, 0x2}, + {0xa348, 0x0, 0x4, 0xc}, + {0xa348, 0x4, 0x4, 0x7}, + {0xa349, 0x0, 0x6, 0x2}, +}; diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c new file mode 100644 index 000000000000..af176b6ce738 --- /dev/null +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -0,0 +1,1117 @@ +/* DVB USB compliant Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "af9005.h" + +/* debug */ +int dvb_usb_af9005_debug; +module_param_named(debug, dvb_usb_af9005_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))." + DVB_USB_DEBUG_STATUS); +/* enable obnoxious led */ +bool dvb_usb_af9005_led = 1; +module_param_named(led, dvb_usb_af9005_led, bool, 0644); +MODULE_PARM_DESC(led, "enable led (default: 1)."); + +/* eeprom dump */ +static int dvb_usb_af9005_dump_eeprom; +module_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0); +MODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* remote control decoder */ +static int (*rc_decode) (struct dvb_usb_device *d, u8 *data, int len, + u32 *event, int *state); +static void *rc_keys; +static int *rc_keys_size; + +u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + +struct af9005_device_state { + u8 sequence; + int led_state; +}; + +static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, + int readwrite, int type, u8 * values, int len) +{ + struct af9005_device_state *st = d->priv; + u8 obuf[16] = { 0 }; + u8 ibuf[17] = { 0 }; + u8 command; + int i; + int ret; + + if (len < 1) { + err("generic read/write, less than 1 byte. Makes no sense."); + return -EINVAL; + } + if (len > 8) { + err("generic read/write, more than 8 bytes. Not supported."); + return -EINVAL; + } + + obuf[0] = 14; /* rest of buffer length low */ + obuf[1] = 0; /* rest of buffer length high */ + + obuf[2] = AF9005_REGISTER_RW; /* register operation */ + obuf[3] = 12; /* rest of buffer length */ + + obuf[4] = st->sequence++; /* sequence number */ + + obuf[5] = (u8) (reg >> 8); /* register address */ + obuf[6] = (u8) (reg & 0xff); + + if (type == AF9005_OFDM_REG) { + command = AF9005_CMD_OFDM_REG; + } else { + command = AF9005_CMD_TUNER; + } + + if (len > 1) + command |= + AF9005_CMD_BURST | AF9005_CMD_AUTOINC | (len - 1) << 3; + command |= readwrite; + if (readwrite == AF9005_CMD_WRITE) + for (i = 0; i < len; i++) + obuf[8 + i] = values[i]; + else if (type == AF9005_TUNER_REG) + /* read command for tuner, the first byte contains the i2c address */ + obuf[8] = values[0]; + obuf[7] = command; + + ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 17, 0); + if (ret) + return ret; + + /* sanity check */ + if (ibuf[2] != AF9005_REGISTER_RW_ACK) { + err("generic read/write, wrong reply code."); + return -EIO; + } + if (ibuf[3] != 0x0d) { + err("generic read/write, wrong length in reply."); + return -EIO; + } + if (ibuf[4] != obuf[4]) { + err("generic read/write, wrong sequence in reply."); + return -EIO; + } + /* + Windows driver doesn't check these fields, in fact sometimes + the register in the reply is different that what has been sent + + if (ibuf[5] != obuf[5] || ibuf[6] != obuf[6]) { + err("generic read/write, wrong register in reply."); + return -EIO; + } + if (ibuf[7] != command) { + err("generic read/write wrong command in reply."); + return -EIO; + } + */ + if (ibuf[16] != 0x01) { + err("generic read/write wrong status code in reply."); + return -EIO; + } + if (readwrite == AF9005_CMD_READ) + for (i = 0; i < len; i++) + values[i] = ibuf[8 + i]; + + return 0; + +} + +int af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 * value) +{ + int ret; + deb_reg("read register %x ", reg); + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_READ, AF9005_OFDM_REG, + value, 1); + if (ret) + deb_reg("failed\n"); + else + deb_reg("value %x\n", *value); + return ret; +} + +int af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len) +{ + int ret; + deb_reg("read %d registers %x ", len, reg); + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_READ, AF9005_OFDM_REG, + values, len); + if (ret) + deb_reg("failed\n"); + else + debug_dump(values, len, deb_reg); + return ret; +} + +int af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 value) +{ + int ret; + u8 temp = value; + deb_reg("write register %x value %x ", reg, value); + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_WRITE, AF9005_OFDM_REG, + &temp, 1); + if (ret) + deb_reg("failed\n"); + else + deb_reg("ok\n"); + return ret; +} + +int af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len) +{ + int ret; + deb_reg("write %d registers %x values ", len, reg); + debug_dump(values, len, deb_reg); + + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_WRITE, AF9005_OFDM_REG, + values, len); + if (ret) + deb_reg("failed\n"); + else + deb_reg("ok\n"); + return ret; +} + +int af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, + u8 len, u8 * value) +{ + u8 temp; + int ret; + deb_reg("read bits %x %x %x", reg, pos, len); + ret = af9005_read_ofdm_register(d, reg, &temp); + if (ret) { + deb_reg(" failed\n"); + return ret; + } + *value = (temp >> pos) & regmask[len - 1]; + deb_reg(" value %x\n", *value); + return 0; + +} + +int af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, + u8 len, u8 value) +{ + u8 temp, mask; + int ret; + deb_reg("write bits %x %x %x value %x\n", reg, pos, len, value); + if (pos == 0 && len == 8) + return af9005_write_ofdm_register(d, reg, value); + ret = af9005_read_ofdm_register(d, reg, &temp); + if (ret) + return ret; + mask = regmask[len - 1] << pos; + temp = (temp & ~mask) | ((value << pos) & mask); + return af9005_write_ofdm_register(d, reg, temp); + +} + +static int af9005_usb_read_tuner_registers(struct dvb_usb_device *d, + u16 reg, u8 * values, int len) +{ + return af9005_generic_read_write(d, reg, + AF9005_CMD_READ, AF9005_TUNER_REG, + values, len); +} + +static int af9005_usb_write_tuner_registers(struct dvb_usb_device *d, + u16 reg, u8 * values, int len) +{ + return af9005_generic_read_write(d, reg, + AF9005_CMD_WRITE, + AF9005_TUNER_REG, values, len); +} + +int af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len) +{ + /* don't let the name of this function mislead you: it's just used + as an interface from the firmware to the i2c bus. The actual + i2c addresses are contained in the data */ + int ret, i, done = 0, fail = 0; + u8 temp; + ret = af9005_usb_write_tuner_registers(d, reg, values, len); + if (ret) + return ret; + if (reg != 0xffff) { + /* check if write done (0xa40d bit 1) or fail (0xa40d bit 2) */ + for (i = 0; i < 200; i++) { + ret = + af9005_read_ofdm_register(d, + xd_I2C_i2c_m_status_wdat_done, + &temp); + if (ret) + return ret; + done = temp & (regmask[i2c_m_status_wdat_done_len - 1] + << i2c_m_status_wdat_done_pos); + if (done) + break; + fail = temp & (regmask[i2c_m_status_wdat_fail_len - 1] + << i2c_m_status_wdat_fail_pos); + if (fail) + break; + msleep(50); + } + if (i == 200) + return -ETIMEDOUT; + if (fail) { + /* clear write fail bit */ + af9005_write_register_bits(d, + xd_I2C_i2c_m_status_wdat_fail, + i2c_m_status_wdat_fail_pos, + i2c_m_status_wdat_fail_len, + 1); + return -EIO; + } + /* clear write done bit */ + ret = + af9005_write_register_bits(d, + xd_I2C_i2c_m_status_wdat_fail, + i2c_m_status_wdat_done_pos, + i2c_m_status_wdat_done_len, 1); + if (ret) + return ret; + } + return 0; +} + +int af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 addr, + u8 * values, int len) +{ + /* don't let the name of this function mislead you: it's just used + as an interface from the firmware to the i2c bus. The actual + i2c addresses are contained in the data */ + int ret, i; + u8 temp, buf[2]; + + buf[0] = addr; /* tuner i2c address */ + buf[1] = values[0]; /* tuner register */ + + values[0] = addr + 0x01; /* i2c read address */ + + if (reg == APO_REG_I2C_RW_SILICON_TUNER) { + /* write tuner i2c address to tuner, 0c00c0 undocumented, found by sniffing */ + ret = af9005_write_tuner_registers(d, 0x00c0, buf, 2); + if (ret) + return ret; + } + + /* send read command to ofsm */ + ret = af9005_usb_read_tuner_registers(d, reg, values, 1); + if (ret) + return ret; + + /* check if read done */ + for (i = 0; i < 200; i++) { + ret = af9005_read_ofdm_register(d, 0xa408, &temp); + if (ret) + return ret; + if (temp & 0x01) + break; + msleep(50); + } + if (i == 200) + return -ETIMEDOUT; + + /* clear read done bit (by writing 1) */ + ret = af9005_write_ofdm_register(d, xd_I2C_i2c_m_data8, 1); + if (ret) + return ret; + + /* get read data (available from 0xa400) */ + for (i = 0; i < len; i++) { + ret = af9005_read_ofdm_register(d, 0xa400 + i, &temp); + if (ret) + return ret; + values[i] = temp; + } + return 0; +} + +static int af9005_i2c_write(struct dvb_usb_device *d, u8 i2caddr, u8 reg, + u8 * data, int len) +{ + int ret, i; + u8 buf[3]; + deb_i2c("i2c_write i2caddr %x, reg %x, len %d data ", i2caddr, + reg, len); + debug_dump(data, len, deb_i2c); + + for (i = 0; i < len; i++) { + buf[0] = i2caddr; + buf[1] = reg + (u8) i; + buf[2] = data[i]; + ret = + af9005_write_tuner_registers(d, + APO_REG_I2C_RW_SILICON_TUNER, + buf, 3); + if (ret) { + deb_i2c("i2c_write failed\n"); + return ret; + } + } + deb_i2c("i2c_write ok\n"); + return 0; +} + +static int af9005_i2c_read(struct dvb_usb_device *d, u8 i2caddr, u8 reg, + u8 * data, int len) +{ + int ret, i; + u8 temp; + deb_i2c("i2c_read i2caddr %x, reg %x, len %d\n ", i2caddr, reg, len); + for (i = 0; i < len; i++) { + temp = reg + i; + ret = + af9005_read_tuner_registers(d, + APO_REG_I2C_RW_SILICON_TUNER, + i2caddr, &temp, 1); + if (ret) { + deb_i2c("i2c_read failed\n"); + return ret; + } + data[i] = temp; + } + deb_i2c("i2c data read: "); + debug_dump(data, len, deb_i2c); + return 0; +} + +static int af9005_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + /* only implements what the mt2060 module does, don't know how + to make it really generic */ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret; + u8 reg, addr; + u8 *value; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + if (num == 2) { + /* reads a single register */ + reg = *msg[0].buf; + addr = msg[0].addr; + value = msg[1].buf; + ret = af9005_i2c_read(d, addr, reg, value, 1); + if (ret == 0) + ret = 2; + } else { + /* write one or more registers */ + reg = msg[0].buf[0]; + addr = msg[0].addr; + value = &msg[0].buf[1]; + ret = af9005_i2c_write(d, addr, reg, value, msg[0].len - 1); + if (ret == 0) + ret = 1; + } + + mutex_unlock(&d->i2c_mutex); + return ret; +} + +static u32 af9005_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9005_i2c_algo = { + .master_xfer = af9005_i2c_xfer, + .functionality = af9005_i2c_func, +}; + +int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, + int wlen, u8 * rbuf, int rlen) +{ + struct af9005_device_state *st = d->priv; + + int ret, i, packet_len; + u8 buf[64]; + u8 ibuf[64]; + + if (wlen < 0) { + err("send command, wlen less than 0 bytes. Makes no sense."); + return -EINVAL; + } + if (wlen > 54) { + err("send command, wlen more than 54 bytes. Not supported."); + return -EINVAL; + } + if (rlen > 54) { + err("send command, rlen more than 54 bytes. Not supported."); + return -EINVAL; + } + packet_len = wlen + 5; + buf[0] = (u8) (packet_len & 0xff); + buf[1] = (u8) ((packet_len & 0xff00) >> 8); + + buf[2] = 0x26; /* packet type */ + buf[3] = wlen + 3; + buf[4] = st->sequence++; + buf[5] = command; + buf[6] = wlen; + for (i = 0; i < wlen; i++) + buf[7 + i] = wbuf[i]; + ret = dvb_usb_generic_rw(d, buf, wlen + 7, ibuf, rlen + 7, 0); + if (ret) + return ret; + if (ibuf[2] != 0x27) { + err("send command, wrong reply code."); + return -EIO; + } + if (ibuf[4] != buf[4]) { + err("send command, wrong sequence in reply."); + return -EIO; + } + if (ibuf[5] != 0x01) { + err("send command, wrong status code in reply."); + return -EIO; + } + if (ibuf[6] != rlen) { + err("send command, invalid data length in reply."); + return -EIO; + } + for (i = 0; i < rlen; i++) + rbuf[i] = ibuf[i + 7]; + return 0; +} + +int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, + int len) +{ + struct af9005_device_state *st = d->priv; + u8 obuf[16], ibuf[14]; + int ret, i; + + memset(obuf, 0, sizeof(obuf)); + memset(ibuf, 0, sizeof(ibuf)); + + obuf[0] = 14; /* length of rest of packet low */ + obuf[1] = 0; /* length of rest of packer high */ + + obuf[2] = 0x2a; /* read/write eeprom */ + + obuf[3] = 12; /* size */ + + obuf[4] = st->sequence++; + + obuf[5] = 0; /* read */ + + obuf[6] = len; + obuf[7] = address; + ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 14, 0); + if (ret) + return ret; + if (ibuf[2] != 0x2b) { + err("Read eeprom, invalid reply code"); + return -EIO; + } + if (ibuf[3] != 10) { + err("Read eeprom, invalid reply length"); + return -EIO; + } + if (ibuf[4] != obuf[4]) { + err("Read eeprom, wrong sequence in reply "); + return -EIO; + } + if (ibuf[5] != 1) { + err("Read eeprom, wrong status in reply "); + return -EIO; + } + for (i = 0; i < len; i++) { + values[i] = ibuf[6 + i]; + } + return 0; +} + +static int af9005_boot_packet(struct usb_device *udev, int type, u8 * reply) +{ + u8 buf[FW_BULKOUT_SIZE + 2]; + u16 checksum; + int act_len, i, ret; + memset(buf, 0, sizeof(buf)); + buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); + buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); + switch (type) { + case FW_CONFIG: + buf[2] = 0x11; + buf[3] = 0x04; + buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ + buf[5] = 0x03; + checksum = buf[4] + buf[5]; + buf[6] = (u8) ((checksum >> 8) & 0xff); + buf[7] = (u8) (checksum & 0xff); + break; + case FW_CONFIRM: + buf[2] = 0x11; + buf[3] = 0x04; + buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ + buf[5] = 0x01; + checksum = buf[4] + buf[5]; + buf[6] = (u8) ((checksum >> 8) & 0xff); + buf[7] = (u8) (checksum & 0xff); + break; + case FW_BOOT: + buf[2] = 0x10; + buf[3] = 0x08; + buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ + buf[5] = 0x97; + buf[6] = 0xaa; + buf[7] = 0x55; + buf[8] = 0xa5; + buf[9] = 0x5a; + checksum = 0; + for (i = 4; i <= 9; i++) + checksum += buf[i]; + buf[10] = (u8) ((checksum >> 8) & 0xff); + buf[11] = (u8) (checksum & 0xff); + break; + default: + err("boot packet invalid boot packet type"); + return -EINVAL; + } + deb_fw(">>> "); + debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); + + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x02), + buf, FW_BULKOUT_SIZE + 2, &act_len, 2000); + if (ret) + err("boot packet bulk message failed: %d (%d/%d)", ret, + FW_BULKOUT_SIZE + 2, act_len); + else + ret = act_len != FW_BULKOUT_SIZE + 2 ? -1 : 0; + if (ret) + return ret; + memset(buf, 0, 9); + ret = usb_bulk_msg(udev, + usb_rcvbulkpipe(udev, 0x01), buf, 9, &act_len, 2000); + if (ret) { + err("boot packet recv bulk message failed: %d", ret); + return ret; + } + deb_fw("<<< "); + debug_dump(buf, act_len, deb_fw); + checksum = 0; + switch (type) { + case FW_CONFIG: + if (buf[2] != 0x11) { + err("boot bad config header."); + return -EIO; + } + if (buf[3] != 0x05) { + err("boot bad config size."); + return -EIO; + } + if (buf[4] != 0x00) { + err("boot bad config sequence."); + return -EIO; + } + if (buf[5] != 0x04) { + err("boot bad config subtype."); + return -EIO; + } + for (i = 4; i <= 6; i++) + checksum += buf[i]; + if (buf[7] * 256 + buf[8] != checksum) { + err("boot bad config checksum."); + return -EIO; + } + *reply = buf[6]; + break; + case FW_CONFIRM: + if (buf[2] != 0x11) { + err("boot bad confirm header."); + return -EIO; + } + if (buf[3] != 0x05) { + err("boot bad confirm size."); + return -EIO; + } + if (buf[4] != 0x00) { + err("boot bad confirm sequence."); + return -EIO; + } + if (buf[5] != 0x02) { + err("boot bad confirm subtype."); + return -EIO; + } + for (i = 4; i <= 6; i++) + checksum += buf[i]; + if (buf[7] * 256 + buf[8] != checksum) { + err("boot bad confirm checksum."); + return -EIO; + } + *reply = buf[6]; + break; + case FW_BOOT: + if (buf[2] != 0x10) { + err("boot bad boot header."); + return -EIO; + } + if (buf[3] != 0x05) { + err("boot bad boot size."); + return -EIO; + } + if (buf[4] != 0x00) { + err("boot bad boot sequence."); + return -EIO; + } + if (buf[5] != 0x01) { + err("boot bad boot pattern 01."); + return -EIO; + } + if (buf[6] != 0x10) { + err("boot bad boot pattern 10."); + return -EIO; + } + for (i = 4; i <= 6; i++) + checksum += buf[i]; + if (buf[7] * 256 + buf[8] != checksum) { + err("boot bad boot checksum."); + return -EIO; + } + break; + + } + + return 0; +} + +static int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw) +{ + int i, packets, ret, act_len; + + u8 buf[FW_BULKOUT_SIZE + 2]; + u8 reply; + + ret = af9005_boot_packet(udev, FW_CONFIG, &reply); + if (ret) + return ret; + if (reply != 0x01) { + err("before downloading firmware, FW_CONFIG expected 0x01, received 0x%x", reply); + return -EIO; + } + packets = fw->size / FW_BULKOUT_SIZE; + buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); + buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); + for (i = 0; i < packets; i++) { + memcpy(&buf[2], fw->data + i * FW_BULKOUT_SIZE, + FW_BULKOUT_SIZE); + deb_fw(">>> "); + debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x02), + buf, FW_BULKOUT_SIZE + 2, &act_len, 1000); + if (ret) { + err("firmware download failed at packet %d with code %d", i, ret); + return ret; + } + } + ret = af9005_boot_packet(udev, FW_CONFIRM, &reply); + if (ret) + return ret; + if (reply != (u8) (packets & 0xff)) { + err("after downloading firmware, FW_CONFIRM expected 0x%x, received 0x%x", packets & 0xff, reply); + return -EIO; + } + ret = af9005_boot_packet(udev, FW_BOOT, &reply); + if (ret) + return ret; + ret = af9005_boot_packet(udev, FW_CONFIG, &reply); + if (ret) + return ret; + if (reply != 0x02) { + err("after downloading firmware, FW_CONFIG expected 0x02, received 0x%x", reply); + return -EIO; + } + + return 0; + +} + +int af9005_led_control(struct dvb_usb_device *d, int onoff) +{ + struct af9005_device_state *st = d->priv; + int temp, ret; + + if (onoff && dvb_usb_af9005_led) + temp = 1; + else + temp = 0; + if (st->led_state != temp) { + ret = + af9005_write_register_bits(d, xd_p_reg_top_locken1, + reg_top_locken1_pos, + reg_top_locken1_len, temp); + if (ret) + return ret; + ret = + af9005_write_register_bits(d, xd_p_reg_top_lock1, + reg_top_lock1_pos, + reg_top_lock1_len, temp); + if (ret) + return ret; + st->led_state = temp; + } + return 0; +} + +static int af9005_frontend_attach(struct dvb_usb_adapter *adap) +{ + u8 buf[8]; + int i; + + /* without these calls the first commands after downloading + the firmware fail. I put these calls here to simulate + what it is done in dvb-usb-init.c. + */ + struct usb_device *udev = adap->dev->udev; + usb_clear_halt(udev, usb_sndbulkpipe(udev, 2)); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 1)); + if (dvb_usb_af9005_dump_eeprom) { + printk("EEPROM DUMP\n"); + for (i = 0; i < 255; i += 8) { + af9005_read_eeprom(adap->dev, i, buf, 8); + printk("ADDR %x ", i); + debug_dump(buf, 8, printk); + } + } + adap->fe_adap[0].fe = af9005_fe_attach(adap->dev); + return 0; +} + +static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) +{ + struct af9005_device_state *st = d->priv; + int ret, len; + + u8 obuf[5]; + u8 ibuf[256]; + + *state = REMOTE_NO_KEY_PRESSED; + if (rc_decode == NULL) { + /* it shouldn't never come here */ + return 0; + } + /* deb_info("rc_query\n"); */ + obuf[0] = 3; /* rest of packet length low */ + obuf[1] = 0; /* rest of packet lentgh high */ + obuf[2] = 0x40; /* read remote */ + obuf[3] = 1; /* rest of packet length */ + obuf[4] = st->sequence++; /* sequence number */ + ret = dvb_usb_generic_rw(d, obuf, 5, ibuf, 256, 0); + if (ret) { + err("rc query failed"); + return ret; + } + if (ibuf[2] != 0x41) { + err("rc query bad header."); + return -EIO; + } + if (ibuf[4] != obuf[4]) { + err("rc query bad sequence."); + return -EIO; + } + len = ibuf[5]; + if (len > 246) { + err("rc query invalid length"); + return -EIO; + } + if (len > 0) { + deb_rc("rc data (%d) ", len); + debug_dump((ibuf + 6), len, deb_rc); + ret = rc_decode(d, &ibuf[6], len, event, state); + if (ret) { + err("rc_decode failed"); + return ret; + } else { + deb_rc("rc_decode state %x event %x\n", *state, *event); + if (*state == REMOTE_KEY_REPEAT) + *event = d->last_event; + } + } + return 0; +} + +static int af9005_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + + return 0; +} + +static int af9005_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + deb_info("pid filter control onoff %d\n", onoff); + if (onoff) { + ret = + af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); + if (ret) + return ret; + ret = + af9005_write_register_bits(adap->dev, + XD_MP2IF_DMX_CTRL, 1, 1, 1); + if (ret) + return ret; + ret = + af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); + } else + ret = + af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 0); + if (ret) + return ret; + deb_info("pid filter control ok\n"); + return 0; +} + +static int af9005_pid_filter(struct dvb_usb_adapter *adap, int index, + u16 pid, int onoff) +{ + u8 cmd = index & 0x1f; + int ret; + deb_info("set pid filter, index %d, pid %x, onoff %d\n", index, + pid, onoff); + if (onoff) { + /* cannot use it as pid_filter_ctrl since it has to be done + before setting the first pid */ + if (adap->feedcount == 1) { + deb_info("first pid set, enable pid table\n"); + ret = af9005_pid_filter_control(adap, onoff); + if (ret) + return ret; + } + ret = + af9005_write_ofdm_register(adap->dev, + XD_MP2IF_PID_DATA_L, + (u8) (pid & 0xff)); + if (ret) + return ret; + ret = + af9005_write_ofdm_register(adap->dev, + XD_MP2IF_PID_DATA_H, + (u8) (pid >> 8)); + if (ret) + return ret; + cmd |= 0x20 | 0x40; + } else { + if (adap->feedcount == 0) { + deb_info("last pid unset, disable pid table\n"); + ret = af9005_pid_filter_control(adap, onoff); + if (ret) + return ret; + } + } + ret = af9005_write_ofdm_register(adap->dev, XD_MP2IF_PID_IDX, cmd); + if (ret) + return ret; + deb_info("set pid ok\n"); + return 0; +} + +static int af9005_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret; + u8 reply; + ret = af9005_boot_packet(udev, FW_CONFIG, &reply); + if (ret) + return ret; + deb_info("result of FW_CONFIG in identify state %d\n", reply); + if (reply == 0x01) + *cold = 1; + else if (reply == 0x02) + *cold = 0; + else + return -EIO; + deb_info("Identify state cold = %d\n", *cold); + return 0; +} + +static struct dvb_usb_device_properties af9005_properties; + +static int af9005_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &af9005_properties, + THIS_MODULE, NULL, adapter_nr); +} + +enum af9005_usb_table_entry { + AFATECH_AF9005, + TERRATEC_AF9005, + ANSONIC_AF9005, +}; + +static struct usb_device_id af9005_usb_table[] = { + [AFATECH_AF9005] = {USB_DEVICE(USB_VID_AFATECH, + USB_PID_AFATECH_AF9005)}, + [TERRATEC_AF9005] = {USB_DEVICE(USB_VID_TERRATEC, + USB_PID_TERRATEC_CINERGY_T_USB_XE)}, + [ANSONIC_AF9005] = {USB_DEVICE(USB_VID_ANSONIC, + USB_PID_ANSONIC_DVBT_USB)}, + { } +}; + +MODULE_DEVICE_TABLE(usb, af9005_usb_table); + +static struct dvb_usb_device_properties af9005_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "af9005.fw", + .download_firmware = af9005_download_firmware, + .no_reconnect = 1, + + .size_of_priv = sizeof(struct af9005_device_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = + DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = af9005_pid_filter, + /* .pid_filter_ctrl = af9005_pid_filter_control, */ + .frontend_attach = af9005_frontend_attach, + /* .tuner_attach = af9005_tuner_attach, */ + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 4096, /* actual size seen is 3948 */ + } + } + }, + }}, + } + }, + .power_ctrl = af9005_power_ctrl, + .identify_state = af9005_identify_state, + + .i2c_algo = &af9005_i2c_algo, + + .rc.legacy = { + .rc_interval = 200, + .rc_map_table = NULL, + .rc_map_size = 0, + .rc_query = af9005_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 2, + .generic_bulk_ctrl_endpoint_response = 1, + + .num_device_descs = 3, + .devices = { + {.name = "Afatech DVB-T USB1.1 stick", + .cold_ids = {&af9005_usb_table[AFATECH_AF9005], NULL}, + .warm_ids = {NULL}, + }, + {.name = "TerraTec Cinergy T USB XE", + .cold_ids = {&af9005_usb_table[TERRATEC_AF9005], NULL}, + .warm_ids = {NULL}, + }, + {.name = "Ansonic DVB-T USB1.1 stick", + .cold_ids = {&af9005_usb_table[ANSONIC_AF9005], NULL}, + .warm_ids = {NULL}, + }, + {NULL}, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9005_usb_driver = { + .name = "dvb_usb_af9005", + .probe = af9005_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = af9005_usb_table, +}; + +/* module stuff */ +static int __init af9005_usb_module_init(void) +{ + int result; + if ((result = usb_register(&af9005_usb_driver))) { + err("usb_register failed. (%d)", result); + return result; + } + rc_decode = symbol_request(af9005_rc_decode); + rc_keys = symbol_request(rc_map_af9005_table); + rc_keys_size = symbol_request(rc_map_af9005_table_size); + if (rc_decode == NULL || rc_keys == NULL || rc_keys_size == NULL) { + err("af9005_rc_decode function not found, disabling remote"); + af9005_properties.rc.legacy.rc_query = NULL; + } else { + af9005_properties.rc.legacy.rc_map_table = rc_keys; + af9005_properties.rc.legacy.rc_map_size = *rc_keys_size; + } + + return 0; +} + +static void __exit af9005_usb_module_exit(void) +{ + /* release rc decode symbols */ + if (rc_decode != NULL) + symbol_put(af9005_rc_decode); + if (rc_keys != NULL) + symbol_put(rc_map_af9005_table); + if (rc_keys_size != NULL) + symbol_put(rc_map_af9005_table_size); + /* deregister this driver from the USB subsystem */ + usb_deregister(&af9005_usb_driver); +} + +module_init(af9005_usb_module_init); +module_exit(af9005_usb_module_exit); + +MODULE_AUTHOR("Luca Olivetti "); +MODULE_DESCRIPTION("Driver for Afatech 9005 DVB-T USB1.1 stick"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/af9005.h b/drivers/media/usb/dvb-usb/af9005.h new file mode 100644 index 000000000000..6a2bf3de8456 --- /dev/null +++ b/drivers/media/usb/dvb-usb/af9005.h @@ -0,0 +1,3496 @@ +/* Common header-file of the Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_AF9005_H_ +#define _DVB_USB_AF9005_H_ + +#define DVB_USB_LOG_PREFIX "af9005" +#include "dvb-usb.h" + +extern int dvb_usb_af9005_debug; +#define deb_info(args...) dprintk(dvb_usb_af9005_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_af9005_debug,0x02,args) +#define deb_rc(args...) dprintk(dvb_usb_af9005_debug,0x04,args) +#define deb_reg(args...) dprintk(dvb_usb_af9005_debug,0x08,args) +#define deb_i2c(args...) dprintk(dvb_usb_af9005_debug,0x10,args) +#define deb_fw(args...) dprintk(dvb_usb_af9005_debug,0x20,args) + +extern bool dvb_usb_af9005_led; + +/* firmware */ +#define FW_BULKOUT_SIZE 250 +enum { + FW_CONFIG, + FW_CONFIRM, + FW_BOOT +}; + +/* af9005 commands */ +#define AF9005_OFDM_REG 0 +#define AF9005_TUNER_REG 1 + +#define AF9005_REGISTER_RW 0x20 +#define AF9005_REGISTER_RW_ACK 0x21 + +#define AF9005_CMD_OFDM_REG 0x00 +#define AF9005_CMD_TUNER 0x80 +#define AF9005_CMD_BURST 0x02 +#define AF9005_CMD_AUTOINC 0x04 +#define AF9005_CMD_READ 0x00 +#define AF9005_CMD_WRITE 0x01 + +/* af9005 registers */ +#define APO_REG_RESET 0xAEFF + +#define APO_REG_I2C_RW_CAN_TUNER 0xF000 +#define APO_REG_I2C_RW_SILICON_TUNER 0xF001 +#define APO_REG_GPIO_RW_SILICON_TUNER 0xFFFE /* also for OFSM */ +#define APO_REG_TRIGGER_OFSM 0xFFFF /* also for OFSM */ + +/*********************************************************************** + * Apollo Registers from VLSI * + ***********************************************************************/ +#define xd_p_reg_aagc_inverted_agc 0xA000 +#define reg_aagc_inverted_agc_pos 0 +#define reg_aagc_inverted_agc_len 1 +#define reg_aagc_inverted_agc_lsb 0 +#define xd_p_reg_aagc_sign_only 0xA000 +#define reg_aagc_sign_only_pos 1 +#define reg_aagc_sign_only_len 1 +#define reg_aagc_sign_only_lsb 0 +#define xd_p_reg_aagc_slow_adc_en 0xA000 +#define reg_aagc_slow_adc_en_pos 2 +#define reg_aagc_slow_adc_en_len 1 +#define reg_aagc_slow_adc_en_lsb 0 +#define xd_p_reg_aagc_slow_adc_scale 0xA000 +#define reg_aagc_slow_adc_scale_pos 3 +#define reg_aagc_slow_adc_scale_len 5 +#define reg_aagc_slow_adc_scale_lsb 0 +#define xd_p_reg_aagc_check_slow_adc_lock 0xA001 +#define reg_aagc_check_slow_adc_lock_pos 0 +#define reg_aagc_check_slow_adc_lock_len 1 +#define reg_aagc_check_slow_adc_lock_lsb 0 +#define xd_p_reg_aagc_init_control 0xA001 +#define reg_aagc_init_control_pos 1 +#define reg_aagc_init_control_len 1 +#define reg_aagc_init_control_lsb 0 +#define xd_p_reg_aagc_total_gain_sel 0xA001 +#define reg_aagc_total_gain_sel_pos 2 +#define reg_aagc_total_gain_sel_len 2 +#define reg_aagc_total_gain_sel_lsb 0 +#define xd_p_reg_aagc_out_inv 0xA001 +#define reg_aagc_out_inv_pos 5 +#define reg_aagc_out_inv_len 1 +#define reg_aagc_out_inv_lsb 0 +#define xd_p_reg_aagc_int_en 0xA001 +#define reg_aagc_int_en_pos 6 +#define reg_aagc_int_en_len 1 +#define reg_aagc_int_en_lsb 0 +#define xd_p_reg_aagc_lock_change_flag 0xA001 +#define reg_aagc_lock_change_flag_pos 7 +#define reg_aagc_lock_change_flag_len 1 +#define reg_aagc_lock_change_flag_lsb 0 +#define xd_p_reg_aagc_rf_loop_bw_scale_acquire 0xA002 +#define reg_aagc_rf_loop_bw_scale_acquire_pos 0 +#define reg_aagc_rf_loop_bw_scale_acquire_len 5 +#define reg_aagc_rf_loop_bw_scale_acquire_lsb 0 +#define xd_p_reg_aagc_rf_loop_bw_scale_track 0xA003 +#define reg_aagc_rf_loop_bw_scale_track_pos 0 +#define reg_aagc_rf_loop_bw_scale_track_len 5 +#define reg_aagc_rf_loop_bw_scale_track_lsb 0 +#define xd_p_reg_aagc_if_loop_bw_scale_acquire 0xA004 +#define reg_aagc_if_loop_bw_scale_acquire_pos 0 +#define reg_aagc_if_loop_bw_scale_acquire_len 5 +#define reg_aagc_if_loop_bw_scale_acquire_lsb 0 +#define xd_p_reg_aagc_if_loop_bw_scale_track 0xA005 +#define reg_aagc_if_loop_bw_scale_track_pos 0 +#define reg_aagc_if_loop_bw_scale_track_len 5 +#define reg_aagc_if_loop_bw_scale_track_lsb 0 +#define xd_p_reg_aagc_max_rf_agc_7_0 0xA006 +#define reg_aagc_max_rf_agc_7_0_pos 0 +#define reg_aagc_max_rf_agc_7_0_len 8 +#define reg_aagc_max_rf_agc_7_0_lsb 0 +#define xd_p_reg_aagc_max_rf_agc_9_8 0xA007 +#define reg_aagc_max_rf_agc_9_8_pos 0 +#define reg_aagc_max_rf_agc_9_8_len 2 +#define reg_aagc_max_rf_agc_9_8_lsb 8 +#define xd_p_reg_aagc_min_rf_agc_7_0 0xA008 +#define reg_aagc_min_rf_agc_7_0_pos 0 +#define reg_aagc_min_rf_agc_7_0_len 8 +#define reg_aagc_min_rf_agc_7_0_lsb 0 +#define xd_p_reg_aagc_min_rf_agc_9_8 0xA009 +#define reg_aagc_min_rf_agc_9_8_pos 0 +#define reg_aagc_min_rf_agc_9_8_len 2 +#define reg_aagc_min_rf_agc_9_8_lsb 8 +#define xd_p_reg_aagc_max_if_agc_7_0 0xA00A +#define reg_aagc_max_if_agc_7_0_pos 0 +#define reg_aagc_max_if_agc_7_0_len 8 +#define reg_aagc_max_if_agc_7_0_lsb 0 +#define xd_p_reg_aagc_max_if_agc_9_8 0xA00B +#define reg_aagc_max_if_agc_9_8_pos 0 +#define reg_aagc_max_if_agc_9_8_len 2 +#define reg_aagc_max_if_agc_9_8_lsb 8 +#define xd_p_reg_aagc_min_if_agc_7_0 0xA00C +#define reg_aagc_min_if_agc_7_0_pos 0 +#define reg_aagc_min_if_agc_7_0_len 8 +#define reg_aagc_min_if_agc_7_0_lsb 0 +#define xd_p_reg_aagc_min_if_agc_9_8 0xA00D +#define reg_aagc_min_if_agc_9_8_pos 0 +#define reg_aagc_min_if_agc_9_8_len 2 +#define reg_aagc_min_if_agc_9_8_lsb 8 +#define xd_p_reg_aagc_lock_sample_scale 0xA00E +#define reg_aagc_lock_sample_scale_pos 0 +#define reg_aagc_lock_sample_scale_len 5 +#define reg_aagc_lock_sample_scale_lsb 0 +#define xd_p_reg_aagc_rf_agc_lock_scale_acquire 0xA00F +#define reg_aagc_rf_agc_lock_scale_acquire_pos 0 +#define reg_aagc_rf_agc_lock_scale_acquire_len 3 +#define reg_aagc_rf_agc_lock_scale_acquire_lsb 0 +#define xd_p_reg_aagc_rf_agc_lock_scale_track 0xA00F +#define reg_aagc_rf_agc_lock_scale_track_pos 3 +#define reg_aagc_rf_agc_lock_scale_track_len 3 +#define reg_aagc_rf_agc_lock_scale_track_lsb 0 +#define xd_p_reg_aagc_if_agc_lock_scale_acquire 0xA010 +#define reg_aagc_if_agc_lock_scale_acquire_pos 0 +#define reg_aagc_if_agc_lock_scale_acquire_len 3 +#define reg_aagc_if_agc_lock_scale_acquire_lsb 0 +#define xd_p_reg_aagc_if_agc_lock_scale_track 0xA010 +#define reg_aagc_if_agc_lock_scale_track_pos 3 +#define reg_aagc_if_agc_lock_scale_track_len 3 +#define reg_aagc_if_agc_lock_scale_track_lsb 0 +#define xd_p_reg_aagc_rf_top_numerator_7_0 0xA011 +#define reg_aagc_rf_top_numerator_7_0_pos 0 +#define reg_aagc_rf_top_numerator_7_0_len 8 +#define reg_aagc_rf_top_numerator_7_0_lsb 0 +#define xd_p_reg_aagc_rf_top_numerator_9_8 0xA012 +#define reg_aagc_rf_top_numerator_9_8_pos 0 +#define reg_aagc_rf_top_numerator_9_8_len 2 +#define reg_aagc_rf_top_numerator_9_8_lsb 8 +#define xd_p_reg_aagc_if_top_numerator_7_0 0xA013 +#define reg_aagc_if_top_numerator_7_0_pos 0 +#define reg_aagc_if_top_numerator_7_0_len 8 +#define reg_aagc_if_top_numerator_7_0_lsb 0 +#define xd_p_reg_aagc_if_top_numerator_9_8 0xA014 +#define reg_aagc_if_top_numerator_9_8_pos 0 +#define reg_aagc_if_top_numerator_9_8_len 2 +#define reg_aagc_if_top_numerator_9_8_lsb 8 +#define xd_p_reg_aagc_adc_out_desired_7_0 0xA015 +#define reg_aagc_adc_out_desired_7_0_pos 0 +#define reg_aagc_adc_out_desired_7_0_len 8 +#define reg_aagc_adc_out_desired_7_0_lsb 0 +#define xd_p_reg_aagc_adc_out_desired_8 0xA016 +#define reg_aagc_adc_out_desired_8_pos 0 +#define reg_aagc_adc_out_desired_8_len 1 +#define reg_aagc_adc_out_desired_8_lsb 0 +#define xd_p_reg_aagc_fixed_gain 0xA016 +#define reg_aagc_fixed_gain_pos 3 +#define reg_aagc_fixed_gain_len 1 +#define reg_aagc_fixed_gain_lsb 0 +#define xd_p_reg_aagc_lock_count_th 0xA016 +#define reg_aagc_lock_count_th_pos 4 +#define reg_aagc_lock_count_th_len 4 +#define reg_aagc_lock_count_th_lsb 0 +#define xd_p_reg_aagc_fixed_rf_agc_control_7_0 0xA017 +#define reg_aagc_fixed_rf_agc_control_7_0_pos 0 +#define reg_aagc_fixed_rf_agc_control_7_0_len 8 +#define reg_aagc_fixed_rf_agc_control_7_0_lsb 0 +#define xd_p_reg_aagc_fixed_rf_agc_control_15_8 0xA018 +#define reg_aagc_fixed_rf_agc_control_15_8_pos 0 +#define reg_aagc_fixed_rf_agc_control_15_8_len 8 +#define reg_aagc_fixed_rf_agc_control_15_8_lsb 8 +#define xd_p_reg_aagc_fixed_rf_agc_control_23_16 0xA019 +#define reg_aagc_fixed_rf_agc_control_23_16_pos 0 +#define reg_aagc_fixed_rf_agc_control_23_16_len 8 +#define reg_aagc_fixed_rf_agc_control_23_16_lsb 16 +#define xd_p_reg_aagc_fixed_rf_agc_control_30_24 0xA01A +#define reg_aagc_fixed_rf_agc_control_30_24_pos 0 +#define reg_aagc_fixed_rf_agc_control_30_24_len 7 +#define reg_aagc_fixed_rf_agc_control_30_24_lsb 24 +#define xd_p_reg_aagc_fixed_if_agc_control_7_0 0xA01B +#define reg_aagc_fixed_if_agc_control_7_0_pos 0 +#define reg_aagc_fixed_if_agc_control_7_0_len 8 +#define reg_aagc_fixed_if_agc_control_7_0_lsb 0 +#define xd_p_reg_aagc_fixed_if_agc_control_15_8 0xA01C +#define reg_aagc_fixed_if_agc_control_15_8_pos 0 +#define reg_aagc_fixed_if_agc_control_15_8_len 8 +#define reg_aagc_fixed_if_agc_control_15_8_lsb 8 +#define xd_p_reg_aagc_fixed_if_agc_control_23_16 0xA01D +#define reg_aagc_fixed_if_agc_control_23_16_pos 0 +#define reg_aagc_fixed_if_agc_control_23_16_len 8 +#define reg_aagc_fixed_if_agc_control_23_16_lsb 16 +#define xd_p_reg_aagc_fixed_if_agc_control_30_24 0xA01E +#define reg_aagc_fixed_if_agc_control_30_24_pos 0 +#define reg_aagc_fixed_if_agc_control_30_24_len 7 +#define reg_aagc_fixed_if_agc_control_30_24_lsb 24 +#define xd_p_reg_aagc_rf_agc_unlock_numerator 0xA01F +#define reg_aagc_rf_agc_unlock_numerator_pos 0 +#define reg_aagc_rf_agc_unlock_numerator_len 6 +#define reg_aagc_rf_agc_unlock_numerator_lsb 0 +#define xd_p_reg_aagc_if_agc_unlock_numerator 0xA020 +#define reg_aagc_if_agc_unlock_numerator_pos 0 +#define reg_aagc_if_agc_unlock_numerator_len 6 +#define reg_aagc_if_agc_unlock_numerator_lsb 0 +#define xd_p_reg_unplug_th 0xA021 +#define reg_unplug_th_pos 0 +#define reg_unplug_th_len 8 +#define reg_aagc_rf_x0_lsb 0 +#define xd_p_reg_weak_signal_rfagc_thr 0xA022 +#define reg_weak_signal_rfagc_thr_pos 0 +#define reg_weak_signal_rfagc_thr_len 8 +#define reg_weak_signal_rfagc_thr_lsb 0 +#define xd_p_reg_unplug_rf_gain_th 0xA023 +#define reg_unplug_rf_gain_th_pos 0 +#define reg_unplug_rf_gain_th_len 8 +#define reg_unplug_rf_gain_th_lsb 0 +#define xd_p_reg_unplug_dtop_rf_gain_th 0xA024 +#define reg_unplug_dtop_rf_gain_th_pos 0 +#define reg_unplug_dtop_rf_gain_th_len 8 +#define reg_unplug_dtop_rf_gain_th_lsb 0 +#define xd_p_reg_unplug_dtop_if_gain_th 0xA025 +#define reg_unplug_dtop_if_gain_th_pos 0 +#define reg_unplug_dtop_if_gain_th_len 8 +#define reg_unplug_dtop_if_gain_th_lsb 0 +#define xd_p_reg_top_recover_at_unplug_en 0xA026 +#define reg_top_recover_at_unplug_en_pos 0 +#define reg_top_recover_at_unplug_en_len 1 +#define reg_top_recover_at_unplug_en_lsb 0 +#define xd_p_reg_aagc_rf_x6 0xA027 +#define reg_aagc_rf_x6_pos 0 +#define reg_aagc_rf_x6_len 8 +#define reg_aagc_rf_x6_lsb 0 +#define xd_p_reg_aagc_rf_x7 0xA028 +#define reg_aagc_rf_x7_pos 0 +#define reg_aagc_rf_x7_len 8 +#define reg_aagc_rf_x7_lsb 0 +#define xd_p_reg_aagc_rf_x8 0xA029 +#define reg_aagc_rf_x8_pos 0 +#define reg_aagc_rf_x8_len 8 +#define reg_aagc_rf_x8_lsb 0 +#define xd_p_reg_aagc_rf_x9 0xA02A +#define reg_aagc_rf_x9_pos 0 +#define reg_aagc_rf_x9_len 8 +#define reg_aagc_rf_x9_lsb 0 +#define xd_p_reg_aagc_rf_x10 0xA02B +#define reg_aagc_rf_x10_pos 0 +#define reg_aagc_rf_x10_len 8 +#define reg_aagc_rf_x10_lsb 0 +#define xd_p_reg_aagc_rf_x11 0xA02C +#define reg_aagc_rf_x11_pos 0 +#define reg_aagc_rf_x11_len 8 +#define reg_aagc_rf_x11_lsb 0 +#define xd_p_reg_aagc_rf_x12 0xA02D +#define reg_aagc_rf_x12_pos 0 +#define reg_aagc_rf_x12_len 8 +#define reg_aagc_rf_x12_lsb 0 +#define xd_p_reg_aagc_rf_x13 0xA02E +#define reg_aagc_rf_x13_pos 0 +#define reg_aagc_rf_x13_len 8 +#define reg_aagc_rf_x13_lsb 0 +#define xd_p_reg_aagc_if_x0 0xA02F +#define reg_aagc_if_x0_pos 0 +#define reg_aagc_if_x0_len 8 +#define reg_aagc_if_x0_lsb 0 +#define xd_p_reg_aagc_if_x1 0xA030 +#define reg_aagc_if_x1_pos 0 +#define reg_aagc_if_x1_len 8 +#define reg_aagc_if_x1_lsb 0 +#define xd_p_reg_aagc_if_x2 0xA031 +#define reg_aagc_if_x2_pos 0 +#define reg_aagc_if_x2_len 8 +#define reg_aagc_if_x2_lsb 0 +#define xd_p_reg_aagc_if_x3 0xA032 +#define reg_aagc_if_x3_pos 0 +#define reg_aagc_if_x3_len 8 +#define reg_aagc_if_x3_lsb 0 +#define xd_p_reg_aagc_if_x4 0xA033 +#define reg_aagc_if_x4_pos 0 +#define reg_aagc_if_x4_len 8 +#define reg_aagc_if_x4_lsb 0 +#define xd_p_reg_aagc_if_x5 0xA034 +#define reg_aagc_if_x5_pos 0 +#define reg_aagc_if_x5_len 8 +#define reg_aagc_if_x5_lsb 0 +#define xd_p_reg_aagc_if_x6 0xA035 +#define reg_aagc_if_x6_pos 0 +#define reg_aagc_if_x6_len 8 +#define reg_aagc_if_x6_lsb 0 +#define xd_p_reg_aagc_if_x7 0xA036 +#define reg_aagc_if_x7_pos 0 +#define reg_aagc_if_x7_len 8 +#define reg_aagc_if_x7_lsb 0 +#define xd_p_reg_aagc_if_x8 0xA037 +#define reg_aagc_if_x8_pos 0 +#define reg_aagc_if_x8_len 8 +#define reg_aagc_if_x8_lsb 0 +#define xd_p_reg_aagc_if_x9 0xA038 +#define reg_aagc_if_x9_pos 0 +#define reg_aagc_if_x9_len 8 +#define reg_aagc_if_x9_lsb 0 +#define xd_p_reg_aagc_if_x10 0xA039 +#define reg_aagc_if_x10_pos 0 +#define reg_aagc_if_x10_len 8 +#define reg_aagc_if_x10_lsb 0 +#define xd_p_reg_aagc_if_x11 0xA03A +#define reg_aagc_if_x11_pos 0 +#define reg_aagc_if_x11_len 8 +#define reg_aagc_if_x11_lsb 0 +#define xd_p_reg_aagc_if_x12 0xA03B +#define reg_aagc_if_x12_pos 0 +#define reg_aagc_if_x12_len 8 +#define reg_aagc_if_x12_lsb 0 +#define xd_p_reg_aagc_if_x13 0xA03C +#define reg_aagc_if_x13_pos 0 +#define reg_aagc_if_x13_len 8 +#define reg_aagc_if_x13_lsb 0 +#define xd_p_reg_aagc_min_rf_ctl_8bit_for_dca 0xA03D +#define reg_aagc_min_rf_ctl_8bit_for_dca_pos 0 +#define reg_aagc_min_rf_ctl_8bit_for_dca_len 8 +#define reg_aagc_min_rf_ctl_8bit_for_dca_lsb 0 +#define xd_p_reg_aagc_min_if_ctl_8bit_for_dca 0xA03E +#define reg_aagc_min_if_ctl_8bit_for_dca_pos 0 +#define reg_aagc_min_if_ctl_8bit_for_dca_len 8 +#define reg_aagc_min_if_ctl_8bit_for_dca_lsb 0 +#define xd_r_reg_aagc_total_gain_7_0 0xA070 +#define reg_aagc_total_gain_7_0_pos 0 +#define reg_aagc_total_gain_7_0_len 8 +#define reg_aagc_total_gain_7_0_lsb 0 +#define xd_r_reg_aagc_total_gain_15_8 0xA071 +#define reg_aagc_total_gain_15_8_pos 0 +#define reg_aagc_total_gain_15_8_len 8 +#define reg_aagc_total_gain_15_8_lsb 8 +#define xd_p_reg_aagc_in_sat_cnt_7_0 0xA074 +#define reg_aagc_in_sat_cnt_7_0_pos 0 +#define reg_aagc_in_sat_cnt_7_0_len 8 +#define reg_aagc_in_sat_cnt_7_0_lsb 0 +#define xd_p_reg_aagc_in_sat_cnt_15_8 0xA075 +#define reg_aagc_in_sat_cnt_15_8_pos 0 +#define reg_aagc_in_sat_cnt_15_8_len 8 +#define reg_aagc_in_sat_cnt_15_8_lsb 8 +#define xd_p_reg_aagc_in_sat_cnt_23_16 0xA076 +#define reg_aagc_in_sat_cnt_23_16_pos 0 +#define reg_aagc_in_sat_cnt_23_16_len 8 +#define reg_aagc_in_sat_cnt_23_16_lsb 16 +#define xd_p_reg_aagc_in_sat_cnt_31_24 0xA077 +#define reg_aagc_in_sat_cnt_31_24_pos 0 +#define reg_aagc_in_sat_cnt_31_24_len 8 +#define reg_aagc_in_sat_cnt_31_24_lsb 24 +#define xd_r_reg_aagc_digital_rf_volt_7_0 0xA078 +#define reg_aagc_digital_rf_volt_7_0_pos 0 +#define reg_aagc_digital_rf_volt_7_0_len 8 +#define reg_aagc_digital_rf_volt_7_0_lsb 0 +#define xd_r_reg_aagc_digital_rf_volt_9_8 0xA079 +#define reg_aagc_digital_rf_volt_9_8_pos 0 +#define reg_aagc_digital_rf_volt_9_8_len 2 +#define reg_aagc_digital_rf_volt_9_8_lsb 8 +#define xd_r_reg_aagc_digital_if_volt_7_0 0xA07A +#define reg_aagc_digital_if_volt_7_0_pos 0 +#define reg_aagc_digital_if_volt_7_0_len 8 +#define reg_aagc_digital_if_volt_7_0_lsb 0 +#define xd_r_reg_aagc_digital_if_volt_9_8 0xA07B +#define reg_aagc_digital_if_volt_9_8_pos 0 +#define reg_aagc_digital_if_volt_9_8_len 2 +#define reg_aagc_digital_if_volt_9_8_lsb 8 +#define xd_r_reg_aagc_rf_gain 0xA07C +#define reg_aagc_rf_gain_pos 0 +#define reg_aagc_rf_gain_len 8 +#define reg_aagc_rf_gain_lsb 0 +#define xd_r_reg_aagc_if_gain 0xA07D +#define reg_aagc_if_gain_pos 0 +#define reg_aagc_if_gain_len 8 +#define reg_aagc_if_gain_lsb 0 +#define xd_p_tinr_imp_indicator 0xA080 +#define tinr_imp_indicator_pos 0 +#define tinr_imp_indicator_len 2 +#define tinr_imp_indicator_lsb 0 +#define xd_p_reg_tinr_fifo_size 0xA080 +#define reg_tinr_fifo_size_pos 2 +#define reg_tinr_fifo_size_len 5 +#define reg_tinr_fifo_size_lsb 0 +#define xd_p_reg_tinr_saturation_cnt_th 0xA081 +#define reg_tinr_saturation_cnt_th_pos 0 +#define reg_tinr_saturation_cnt_th_len 4 +#define reg_tinr_saturation_cnt_th_lsb 0 +#define xd_p_reg_tinr_saturation_th_3_0 0xA081 +#define reg_tinr_saturation_th_3_0_pos 4 +#define reg_tinr_saturation_th_3_0_len 4 +#define reg_tinr_saturation_th_3_0_lsb 0 +#define xd_p_reg_tinr_saturation_th_8_4 0xA082 +#define reg_tinr_saturation_th_8_4_pos 0 +#define reg_tinr_saturation_th_8_4_len 5 +#define reg_tinr_saturation_th_8_4_lsb 4 +#define xd_p_reg_tinr_imp_duration_th_2k_7_0 0xA083 +#define reg_tinr_imp_duration_th_2k_7_0_pos 0 +#define reg_tinr_imp_duration_th_2k_7_0_len 8 +#define reg_tinr_imp_duration_th_2k_7_0_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_2k_8 0xA084 +#define reg_tinr_imp_duration_th_2k_8_pos 0 +#define reg_tinr_imp_duration_th_2k_8_len 1 +#define reg_tinr_imp_duration_th_2k_8_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_8k_7_0 0xA085 +#define reg_tinr_imp_duration_th_8k_7_0_pos 0 +#define reg_tinr_imp_duration_th_8k_7_0_len 8 +#define reg_tinr_imp_duration_th_8k_7_0_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_8k_10_8 0xA086 +#define reg_tinr_imp_duration_th_8k_10_8_pos 0 +#define reg_tinr_imp_duration_th_8k_10_8_len 3 +#define reg_tinr_imp_duration_th_8k_10_8_lsb 8 +#define xd_p_reg_tinr_freq_ratio_6m_7_0 0xA087 +#define reg_tinr_freq_ratio_6m_7_0_pos 0 +#define reg_tinr_freq_ratio_6m_7_0_len 8 +#define reg_tinr_freq_ratio_6m_7_0_lsb 0 +#define xd_p_reg_tinr_freq_ratio_6m_12_8 0xA088 +#define reg_tinr_freq_ratio_6m_12_8_pos 0 +#define reg_tinr_freq_ratio_6m_12_8_len 5 +#define reg_tinr_freq_ratio_6m_12_8_lsb 8 +#define xd_p_reg_tinr_freq_ratio_7m_7_0 0xA089 +#define reg_tinr_freq_ratio_7m_7_0_pos 0 +#define reg_tinr_freq_ratio_7m_7_0_len 8 +#define reg_tinr_freq_ratio_7m_7_0_lsb 0 +#define xd_p_reg_tinr_freq_ratio_7m_12_8 0xA08A +#define reg_tinr_freq_ratio_7m_12_8_pos 0 +#define reg_tinr_freq_ratio_7m_12_8_len 5 +#define reg_tinr_freq_ratio_7m_12_8_lsb 8 +#define xd_p_reg_tinr_freq_ratio_8m_7_0 0xA08B +#define reg_tinr_freq_ratio_8m_7_0_pos 0 +#define reg_tinr_freq_ratio_8m_7_0_len 8 +#define reg_tinr_freq_ratio_8m_7_0_lsb 0 +#define xd_p_reg_tinr_freq_ratio_8m_12_8 0xA08C +#define reg_tinr_freq_ratio_8m_12_8_pos 0 +#define reg_tinr_freq_ratio_8m_12_8_len 5 +#define reg_tinr_freq_ratio_8m_12_8_lsb 8 +#define xd_p_reg_tinr_imp_duration_th_low_2k 0xA08D +#define reg_tinr_imp_duration_th_low_2k_pos 0 +#define reg_tinr_imp_duration_th_low_2k_len 8 +#define reg_tinr_imp_duration_th_low_2k_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_low_8k 0xA08E +#define reg_tinr_imp_duration_th_low_8k_pos 0 +#define reg_tinr_imp_duration_th_low_8k_len 8 +#define reg_tinr_imp_duration_th_low_8k_lsb 0 +#define xd_r_reg_tinr_counter_7_0 0xA090 +#define reg_tinr_counter_7_0_pos 0 +#define reg_tinr_counter_7_0_len 8 +#define reg_tinr_counter_7_0_lsb 0 +#define xd_r_reg_tinr_counter_15_8 0xA091 +#define reg_tinr_counter_15_8_pos 0 +#define reg_tinr_counter_15_8_len 8 +#define reg_tinr_counter_15_8_lsb 8 +#define xd_p_reg_tinr_adative_tinr_en 0xA093 +#define reg_tinr_adative_tinr_en_pos 0 +#define reg_tinr_adative_tinr_en_len 1 +#define reg_tinr_adative_tinr_en_lsb 0 +#define xd_p_reg_tinr_peak_fifo_size 0xA093 +#define reg_tinr_peak_fifo_size_pos 1 +#define reg_tinr_peak_fifo_size_len 5 +#define reg_tinr_peak_fifo_size_lsb 0 +#define xd_p_reg_tinr_counter_rst 0xA093 +#define reg_tinr_counter_rst_pos 6 +#define reg_tinr_counter_rst_len 1 +#define reg_tinr_counter_rst_lsb 0 +#define xd_p_reg_tinr_search_period_7_0 0xA094 +#define reg_tinr_search_period_7_0_pos 0 +#define reg_tinr_search_period_7_0_len 8 +#define reg_tinr_search_period_7_0_lsb 0 +#define xd_p_reg_tinr_search_period_15_8 0xA095 +#define reg_tinr_search_period_15_8_pos 0 +#define reg_tinr_search_period_15_8_len 8 +#define reg_tinr_search_period_15_8_lsb 8 +#define xd_p_reg_ccifs_fcw_7_0 0xA0A0 +#define reg_ccifs_fcw_7_0_pos 0 +#define reg_ccifs_fcw_7_0_len 8 +#define reg_ccifs_fcw_7_0_lsb 0 +#define xd_p_reg_ccifs_fcw_12_8 0xA0A1 +#define reg_ccifs_fcw_12_8_pos 0 +#define reg_ccifs_fcw_12_8_len 5 +#define reg_ccifs_fcw_12_8_lsb 8 +#define xd_p_reg_ccifs_spec_inv 0xA0A1 +#define reg_ccifs_spec_inv_pos 5 +#define reg_ccifs_spec_inv_len 1 +#define reg_ccifs_spec_inv_lsb 0 +#define xd_p_reg_gp_trigger 0xA0A2 +#define reg_gp_trigger_pos 0 +#define reg_gp_trigger_len 1 +#define reg_gp_trigger_lsb 0 +#define xd_p_reg_trigger_sel 0xA0A2 +#define reg_trigger_sel_pos 1 +#define reg_trigger_sel_len 2 +#define reg_trigger_sel_lsb 0 +#define xd_p_reg_debug_ofdm 0xA0A2 +#define reg_debug_ofdm_pos 3 +#define reg_debug_ofdm_len 2 +#define reg_debug_ofdm_lsb 0 +#define xd_p_reg_trigger_module_sel 0xA0A3 +#define reg_trigger_module_sel_pos 0 +#define reg_trigger_module_sel_len 6 +#define reg_trigger_module_sel_lsb 0 +#define xd_p_reg_trigger_set_sel 0xA0A4 +#define reg_trigger_set_sel_pos 0 +#define reg_trigger_set_sel_len 6 +#define reg_trigger_set_sel_lsb 0 +#define xd_p_reg_fw_int_mask_n 0xA0A4 +#define reg_fw_int_mask_n_pos 6 +#define reg_fw_int_mask_n_len 1 +#define reg_fw_int_mask_n_lsb 0 +#define xd_p_reg_debug_group 0xA0A5 +#define reg_debug_group_pos 0 +#define reg_debug_group_len 4 +#define reg_debug_group_lsb 0 +#define xd_p_reg_odbg_clk_sel 0xA0A5 +#define reg_odbg_clk_sel_pos 4 +#define reg_odbg_clk_sel_len 2 +#define reg_odbg_clk_sel_lsb 0 +#define xd_p_reg_ccif_sc 0xA0C0 +#define reg_ccif_sc_pos 0 +#define reg_ccif_sc_len 4 +#define reg_ccif_sc_lsb 0 +#define xd_r_reg_ccif_saturate 0xA0C1 +#define reg_ccif_saturate_pos 0 +#define reg_ccif_saturate_len 2 +#define reg_ccif_saturate_lsb 0 +#define xd_r_reg_antif_saturate 0xA0C1 +#define reg_antif_saturate_pos 2 +#define reg_antif_saturate_len 4 +#define reg_antif_saturate_lsb 0 +#define xd_r_reg_acif_saturate 0xA0C2 +#define reg_acif_saturate_pos 0 +#define reg_acif_saturate_len 8 +#define reg_acif_saturate_lsb 0 +#define xd_p_reg_tmr_timer0_threshold_7_0 0xA0C8 +#define reg_tmr_timer0_threshold_7_0_pos 0 +#define reg_tmr_timer0_threshold_7_0_len 8 +#define reg_tmr_timer0_threshold_7_0_lsb 0 +#define xd_p_reg_tmr_timer0_threshold_15_8 0xA0C9 +#define reg_tmr_timer0_threshold_15_8_pos 0 +#define reg_tmr_timer0_threshold_15_8_len 8 +#define reg_tmr_timer0_threshold_15_8_lsb 8 +#define xd_p_reg_tmr_timer0_enable 0xA0CA +#define reg_tmr_timer0_enable_pos 0 +#define reg_tmr_timer0_enable_len 1 +#define reg_tmr_timer0_enable_lsb 0 +#define xd_p_reg_tmr_timer0_clk_sel 0xA0CA +#define reg_tmr_timer0_clk_sel_pos 1 +#define reg_tmr_timer0_clk_sel_len 1 +#define reg_tmr_timer0_clk_sel_lsb 0 +#define xd_p_reg_tmr_timer0_int 0xA0CA +#define reg_tmr_timer0_int_pos 2 +#define reg_tmr_timer0_int_len 1 +#define reg_tmr_timer0_int_lsb 0 +#define xd_p_reg_tmr_timer0_rst 0xA0CA +#define reg_tmr_timer0_rst_pos 3 +#define reg_tmr_timer0_rst_len 1 +#define reg_tmr_timer0_rst_lsb 0 +#define xd_r_reg_tmr_timer0_count_7_0 0xA0CB +#define reg_tmr_timer0_count_7_0_pos 0 +#define reg_tmr_timer0_count_7_0_len 8 +#define reg_tmr_timer0_count_7_0_lsb 0 +#define xd_r_reg_tmr_timer0_count_15_8 0xA0CC +#define reg_tmr_timer0_count_15_8_pos 0 +#define reg_tmr_timer0_count_15_8_len 8 +#define reg_tmr_timer0_count_15_8_lsb 8 +#define xd_p_reg_suspend 0xA0CD +#define reg_suspend_pos 0 +#define reg_suspend_len 1 +#define reg_suspend_lsb 0 +#define xd_p_reg_suspend_rdy 0xA0CD +#define reg_suspend_rdy_pos 1 +#define reg_suspend_rdy_len 1 +#define reg_suspend_rdy_lsb 0 +#define xd_p_reg_resume 0xA0CD +#define reg_resume_pos 2 +#define reg_resume_len 1 +#define reg_resume_lsb 0 +#define xd_p_reg_resume_rdy 0xA0CD +#define reg_resume_rdy_pos 3 +#define reg_resume_rdy_len 1 +#define reg_resume_rdy_lsb 0 +#define xd_p_reg_fmf 0xA0CE +#define reg_fmf_pos 0 +#define reg_fmf_len 8 +#define reg_fmf_lsb 0 +#define xd_p_ccid_accumulate_num_2k_7_0 0xA100 +#define ccid_accumulate_num_2k_7_0_pos 0 +#define ccid_accumulate_num_2k_7_0_len 8 +#define ccid_accumulate_num_2k_7_0_lsb 0 +#define xd_p_ccid_accumulate_num_2k_12_8 0xA101 +#define ccid_accumulate_num_2k_12_8_pos 0 +#define ccid_accumulate_num_2k_12_8_len 5 +#define ccid_accumulate_num_2k_12_8_lsb 8 +#define xd_p_ccid_accumulate_num_8k_7_0 0xA102 +#define ccid_accumulate_num_8k_7_0_pos 0 +#define ccid_accumulate_num_8k_7_0_len 8 +#define ccid_accumulate_num_8k_7_0_lsb 0 +#define xd_p_ccid_accumulate_num_8k_14_8 0xA103 +#define ccid_accumulate_num_8k_14_8_pos 0 +#define ccid_accumulate_num_8k_14_8_len 7 +#define ccid_accumulate_num_8k_14_8_lsb 8 +#define xd_p_ccid_desired_level_0 0xA103 +#define ccid_desired_level_0_pos 7 +#define ccid_desired_level_0_len 1 +#define ccid_desired_level_0_lsb 0 +#define xd_p_ccid_desired_level_8_1 0xA104 +#define ccid_desired_level_8_1_pos 0 +#define ccid_desired_level_8_1_len 8 +#define ccid_desired_level_8_1_lsb 1 +#define xd_p_ccid_apply_delay 0xA105 +#define ccid_apply_delay_pos 0 +#define ccid_apply_delay_len 7 +#define ccid_apply_delay_lsb 0 +#define xd_p_ccid_CCID_Threshold1 0xA106 +#define ccid_CCID_Threshold1_pos 0 +#define ccid_CCID_Threshold1_len 8 +#define ccid_CCID_Threshold1_lsb 0 +#define xd_p_ccid_CCID_Threshold2 0xA107 +#define ccid_CCID_Threshold2_pos 0 +#define ccid_CCID_Threshold2_len 8 +#define ccid_CCID_Threshold2_lsb 0 +#define xd_p_reg_ccid_gain_scale 0xA108 +#define reg_ccid_gain_scale_pos 0 +#define reg_ccid_gain_scale_len 4 +#define reg_ccid_gain_scale_lsb 0 +#define xd_p_reg_ccid2_passband_gain_set 0xA108 +#define reg_ccid2_passband_gain_set_pos 4 +#define reg_ccid2_passband_gain_set_len 4 +#define reg_ccid2_passband_gain_set_lsb 0 +#define xd_r_ccid_multiplier_7_0 0xA109 +#define ccid_multiplier_7_0_pos 0 +#define ccid_multiplier_7_0_len 8 +#define ccid_multiplier_7_0_lsb 0 +#define xd_r_ccid_multiplier_15_8 0xA10A +#define ccid_multiplier_15_8_pos 0 +#define ccid_multiplier_15_8_len 8 +#define ccid_multiplier_15_8_lsb 8 +#define xd_r_ccid_right_shift_bits 0xA10B +#define ccid_right_shift_bits_pos 0 +#define ccid_right_shift_bits_len 4 +#define ccid_right_shift_bits_lsb 0 +#define xd_r_reg_ccid_sx_7_0 0xA10C +#define reg_ccid_sx_7_0_pos 0 +#define reg_ccid_sx_7_0_len 8 +#define reg_ccid_sx_7_0_lsb 0 +#define xd_r_reg_ccid_sx_15_8 0xA10D +#define reg_ccid_sx_15_8_pos 0 +#define reg_ccid_sx_15_8_len 8 +#define reg_ccid_sx_15_8_lsb 8 +#define xd_r_reg_ccid_sx_21_16 0xA10E +#define reg_ccid_sx_21_16_pos 0 +#define reg_ccid_sx_21_16_len 6 +#define reg_ccid_sx_21_16_lsb 16 +#define xd_r_reg_ccid_sy_7_0 0xA110 +#define reg_ccid_sy_7_0_pos 0 +#define reg_ccid_sy_7_0_len 8 +#define reg_ccid_sy_7_0_lsb 0 +#define xd_r_reg_ccid_sy_15_8 0xA111 +#define reg_ccid_sy_15_8_pos 0 +#define reg_ccid_sy_15_8_len 8 +#define reg_ccid_sy_15_8_lsb 8 +#define xd_r_reg_ccid_sy_23_16 0xA112 +#define reg_ccid_sy_23_16_pos 0 +#define reg_ccid_sy_23_16_len 8 +#define reg_ccid_sy_23_16_lsb 16 +#define xd_r_reg_ccid2_sz_7_0 0xA114 +#define reg_ccid2_sz_7_0_pos 0 +#define reg_ccid2_sz_7_0_len 8 +#define reg_ccid2_sz_7_0_lsb 0 +#define xd_r_reg_ccid2_sz_15_8 0xA115 +#define reg_ccid2_sz_15_8_pos 0 +#define reg_ccid2_sz_15_8_len 8 +#define reg_ccid2_sz_15_8_lsb 8 +#define xd_r_reg_ccid2_sz_23_16 0xA116 +#define reg_ccid2_sz_23_16_pos 0 +#define reg_ccid2_sz_23_16_len 8 +#define reg_ccid2_sz_23_16_lsb 16 +#define xd_r_reg_ccid2_sz_25_24 0xA117 +#define reg_ccid2_sz_25_24_pos 0 +#define reg_ccid2_sz_25_24_len 2 +#define reg_ccid2_sz_25_24_lsb 24 +#define xd_r_reg_ccid2_sy_7_0 0xA118 +#define reg_ccid2_sy_7_0_pos 0 +#define reg_ccid2_sy_7_0_len 8 +#define reg_ccid2_sy_7_0_lsb 0 +#define xd_r_reg_ccid2_sy_15_8 0xA119 +#define reg_ccid2_sy_15_8_pos 0 +#define reg_ccid2_sy_15_8_len 8 +#define reg_ccid2_sy_15_8_lsb 8 +#define xd_r_reg_ccid2_sy_23_16 0xA11A +#define reg_ccid2_sy_23_16_pos 0 +#define reg_ccid2_sy_23_16_len 8 +#define reg_ccid2_sy_23_16_lsb 16 +#define xd_r_reg_ccid2_sy_25_24 0xA11B +#define reg_ccid2_sy_25_24_pos 0 +#define reg_ccid2_sy_25_24_len 2 +#define reg_ccid2_sy_25_24_lsb 24 +#define xd_p_dagc1_accumulate_num_2k_7_0 0xA120 +#define dagc1_accumulate_num_2k_7_0_pos 0 +#define dagc1_accumulate_num_2k_7_0_len 8 +#define dagc1_accumulate_num_2k_7_0_lsb 0 +#define xd_p_dagc1_accumulate_num_2k_12_8 0xA121 +#define dagc1_accumulate_num_2k_12_8_pos 0 +#define dagc1_accumulate_num_2k_12_8_len 5 +#define dagc1_accumulate_num_2k_12_8_lsb 8 +#define xd_p_dagc1_accumulate_num_8k_7_0 0xA122 +#define dagc1_accumulate_num_8k_7_0_pos 0 +#define dagc1_accumulate_num_8k_7_0_len 8 +#define dagc1_accumulate_num_8k_7_0_lsb 0 +#define xd_p_dagc1_accumulate_num_8k_14_8 0xA123 +#define dagc1_accumulate_num_8k_14_8_pos 0 +#define dagc1_accumulate_num_8k_14_8_len 7 +#define dagc1_accumulate_num_8k_14_8_lsb 8 +#define xd_p_dagc1_desired_level_0 0xA123 +#define dagc1_desired_level_0_pos 7 +#define dagc1_desired_level_0_len 1 +#define dagc1_desired_level_0_lsb 0 +#define xd_p_dagc1_desired_level_8_1 0xA124 +#define dagc1_desired_level_8_1_pos 0 +#define dagc1_desired_level_8_1_len 8 +#define dagc1_desired_level_8_1_lsb 1 +#define xd_p_dagc1_apply_delay 0xA125 +#define dagc1_apply_delay_pos 0 +#define dagc1_apply_delay_len 7 +#define dagc1_apply_delay_lsb 0 +#define xd_p_dagc1_bypass_scale_ctl 0xA126 +#define dagc1_bypass_scale_ctl_pos 0 +#define dagc1_bypass_scale_ctl_len 2 +#define dagc1_bypass_scale_ctl_lsb 0 +#define xd_p_reg_dagc1_in_sat_cnt_7_0 0xA127 +#define reg_dagc1_in_sat_cnt_7_0_pos 0 +#define reg_dagc1_in_sat_cnt_7_0_len 8 +#define reg_dagc1_in_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc1_in_sat_cnt_15_8 0xA128 +#define reg_dagc1_in_sat_cnt_15_8_pos 0 +#define reg_dagc1_in_sat_cnt_15_8_len 8 +#define reg_dagc1_in_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc1_in_sat_cnt_23_16 0xA129 +#define reg_dagc1_in_sat_cnt_23_16_pos 0 +#define reg_dagc1_in_sat_cnt_23_16_len 8 +#define reg_dagc1_in_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc1_in_sat_cnt_31_24 0xA12A +#define reg_dagc1_in_sat_cnt_31_24_pos 0 +#define reg_dagc1_in_sat_cnt_31_24_len 8 +#define reg_dagc1_in_sat_cnt_31_24_lsb 24 +#define xd_p_reg_dagc1_out_sat_cnt_7_0 0xA12B +#define reg_dagc1_out_sat_cnt_7_0_pos 0 +#define reg_dagc1_out_sat_cnt_7_0_len 8 +#define reg_dagc1_out_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc1_out_sat_cnt_15_8 0xA12C +#define reg_dagc1_out_sat_cnt_15_8_pos 0 +#define reg_dagc1_out_sat_cnt_15_8_len 8 +#define reg_dagc1_out_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc1_out_sat_cnt_23_16 0xA12D +#define reg_dagc1_out_sat_cnt_23_16_pos 0 +#define reg_dagc1_out_sat_cnt_23_16_len 8 +#define reg_dagc1_out_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc1_out_sat_cnt_31_24 0xA12E +#define reg_dagc1_out_sat_cnt_31_24_pos 0 +#define reg_dagc1_out_sat_cnt_31_24_len 8 +#define reg_dagc1_out_sat_cnt_31_24_lsb 24 +#define xd_r_dagc1_multiplier_7_0 0xA136 +#define dagc1_multiplier_7_0_pos 0 +#define dagc1_multiplier_7_0_len 8 +#define dagc1_multiplier_7_0_lsb 0 +#define xd_r_dagc1_multiplier_15_8 0xA137 +#define dagc1_multiplier_15_8_pos 0 +#define dagc1_multiplier_15_8_len 8 +#define dagc1_multiplier_15_8_lsb 8 +#define xd_r_dagc1_right_shift_bits 0xA138 +#define dagc1_right_shift_bits_pos 0 +#define dagc1_right_shift_bits_len 4 +#define dagc1_right_shift_bits_lsb 0 +#define xd_p_reg_bfs_fcw_7_0 0xA140 +#define reg_bfs_fcw_7_0_pos 0 +#define reg_bfs_fcw_7_0_len 8 +#define reg_bfs_fcw_7_0_lsb 0 +#define xd_p_reg_bfs_fcw_15_8 0xA141 +#define reg_bfs_fcw_15_8_pos 0 +#define reg_bfs_fcw_15_8_len 8 +#define reg_bfs_fcw_15_8_lsb 8 +#define xd_p_reg_bfs_fcw_22_16 0xA142 +#define reg_bfs_fcw_22_16_pos 0 +#define reg_bfs_fcw_22_16_len 7 +#define reg_bfs_fcw_22_16_lsb 16 +#define xd_p_reg_antif_sf_7_0 0xA144 +#define reg_antif_sf_7_0_pos 0 +#define reg_antif_sf_7_0_len 8 +#define reg_antif_sf_7_0_lsb 0 +#define xd_p_reg_antif_sf_11_8 0xA145 +#define reg_antif_sf_11_8_pos 0 +#define reg_antif_sf_11_8_len 4 +#define reg_antif_sf_11_8_lsb 8 +#define xd_r_bfs_fcw_q_7_0 0xA150 +#define bfs_fcw_q_7_0_pos 0 +#define bfs_fcw_q_7_0_len 8 +#define bfs_fcw_q_7_0_lsb 0 +#define xd_r_bfs_fcw_q_15_8 0xA151 +#define bfs_fcw_q_15_8_pos 0 +#define bfs_fcw_q_15_8_len 8 +#define bfs_fcw_q_15_8_lsb 8 +#define xd_r_bfs_fcw_q_22_16 0xA152 +#define bfs_fcw_q_22_16_pos 0 +#define bfs_fcw_q_22_16_len 7 +#define bfs_fcw_q_22_16_lsb 16 +#define xd_p_reg_dca_enu 0xA160 +#define reg_dca_enu_pos 0 +#define reg_dca_enu_len 1 +#define reg_dca_enu_lsb 0 +#define xd_p_reg_dca_enl 0xA160 +#define reg_dca_enl_pos 1 +#define reg_dca_enl_len 1 +#define reg_dca_enl_lsb 0 +#define xd_p_reg_dca_lower_chip 0xA160 +#define reg_dca_lower_chip_pos 2 +#define reg_dca_lower_chip_len 1 +#define reg_dca_lower_chip_lsb 0 +#define xd_p_reg_dca_upper_chip 0xA160 +#define reg_dca_upper_chip_pos 3 +#define reg_dca_upper_chip_len 1 +#define reg_dca_upper_chip_lsb 0 +#define xd_p_reg_dca_platch 0xA160 +#define reg_dca_platch_pos 4 +#define reg_dca_platch_len 1 +#define reg_dca_platch_lsb 0 +#define xd_p_reg_dca_th 0xA161 +#define reg_dca_th_pos 0 +#define reg_dca_th_len 5 +#define reg_dca_th_lsb 0 +#define xd_p_reg_dca_scale 0xA162 +#define reg_dca_scale_pos 0 +#define reg_dca_scale_len 4 +#define reg_dca_scale_lsb 0 +#define xd_p_reg_dca_tone_7_0 0xA163 +#define reg_dca_tone_7_0_pos 0 +#define reg_dca_tone_7_0_len 8 +#define reg_dca_tone_7_0_lsb 0 +#define xd_p_reg_dca_tone_12_8 0xA164 +#define reg_dca_tone_12_8_pos 0 +#define reg_dca_tone_12_8_len 5 +#define reg_dca_tone_12_8_lsb 8 +#define xd_p_reg_dca_time_7_0 0xA165 +#define reg_dca_time_7_0_pos 0 +#define reg_dca_time_7_0_len 8 +#define reg_dca_time_7_0_lsb 0 +#define xd_p_reg_dca_time_15_8 0xA166 +#define reg_dca_time_15_8_pos 0 +#define reg_dca_time_15_8_len 8 +#define reg_dca_time_15_8_lsb 8 +#define xd_r_dcasm 0xA167 +#define dcasm_pos 0 +#define dcasm_len 3 +#define dcasm_lsb 0 +#define xd_p_reg_qnt_valuew_7_0 0xA168 +#define reg_qnt_valuew_7_0_pos 0 +#define reg_qnt_valuew_7_0_len 8 +#define reg_qnt_valuew_7_0_lsb 0 +#define xd_p_reg_qnt_valuew_10_8 0xA169 +#define reg_qnt_valuew_10_8_pos 0 +#define reg_qnt_valuew_10_8_len 3 +#define reg_qnt_valuew_10_8_lsb 8 +#define xd_p_dca_sbx_gain_diff_7_0 0xA16A +#define dca_sbx_gain_diff_7_0_pos 0 +#define dca_sbx_gain_diff_7_0_len 8 +#define dca_sbx_gain_diff_7_0_lsb 0 +#define xd_p_dca_sbx_gain_diff_9_8 0xA16B +#define dca_sbx_gain_diff_9_8_pos 0 +#define dca_sbx_gain_diff_9_8_len 2 +#define dca_sbx_gain_diff_9_8_lsb 8 +#define xd_p_reg_dca_stand_alone 0xA16C +#define reg_dca_stand_alone_pos 0 +#define reg_dca_stand_alone_len 1 +#define reg_dca_stand_alone_lsb 0 +#define xd_p_reg_dca_upper_out_en 0xA16C +#define reg_dca_upper_out_en_pos 1 +#define reg_dca_upper_out_en_len 1 +#define reg_dca_upper_out_en_lsb 0 +#define xd_p_reg_dca_rc_en 0xA16C +#define reg_dca_rc_en_pos 2 +#define reg_dca_rc_en_len 1 +#define reg_dca_rc_en_lsb 0 +#define xd_p_reg_dca_retrain_send 0xA16C +#define reg_dca_retrain_send_pos 3 +#define reg_dca_retrain_send_len 1 +#define reg_dca_retrain_send_lsb 0 +#define xd_p_reg_dca_retrain_rec 0xA16C +#define reg_dca_retrain_rec_pos 4 +#define reg_dca_retrain_rec_len 1 +#define reg_dca_retrain_rec_lsb 0 +#define xd_p_reg_dca_api_tpsrdy 0xA16C +#define reg_dca_api_tpsrdy_pos 5 +#define reg_dca_api_tpsrdy_len 1 +#define reg_dca_api_tpsrdy_lsb 0 +#define xd_p_reg_dca_symbol_gap 0xA16D +#define reg_dca_symbol_gap_pos 0 +#define reg_dca_symbol_gap_len 4 +#define reg_dca_symbol_gap_lsb 0 +#define xd_p_reg_qnt_nfvaluew_7_0 0xA16E +#define reg_qnt_nfvaluew_7_0_pos 0 +#define reg_qnt_nfvaluew_7_0_len 8 +#define reg_qnt_nfvaluew_7_0_lsb 0 +#define xd_p_reg_qnt_nfvaluew_10_8 0xA16F +#define reg_qnt_nfvaluew_10_8_pos 0 +#define reg_qnt_nfvaluew_10_8_len 3 +#define reg_qnt_nfvaluew_10_8_lsb 8 +#define xd_p_reg_qnt_flatness_thr_7_0 0xA170 +#define reg_qnt_flatness_thr_7_0_pos 0 +#define reg_qnt_flatness_thr_7_0_len 8 +#define reg_qnt_flatness_thr_7_0_lsb 0 +#define xd_p_reg_qnt_flatness_thr_9_8 0xA171 +#define reg_qnt_flatness_thr_9_8_pos 0 +#define reg_qnt_flatness_thr_9_8_len 2 +#define reg_qnt_flatness_thr_9_8_lsb 8 +#define xd_p_reg_dca_tone_idx_5_0 0xA171 +#define reg_dca_tone_idx_5_0_pos 2 +#define reg_dca_tone_idx_5_0_len 6 +#define reg_dca_tone_idx_5_0_lsb 0 +#define xd_p_reg_dca_tone_idx_12_6 0xA172 +#define reg_dca_tone_idx_12_6_pos 0 +#define reg_dca_tone_idx_12_6_len 7 +#define reg_dca_tone_idx_12_6_lsb 6 +#define xd_p_reg_dca_data_vld 0xA173 +#define reg_dca_data_vld_pos 0 +#define reg_dca_data_vld_len 1 +#define reg_dca_data_vld_lsb 0 +#define xd_p_reg_dca_read_update 0xA173 +#define reg_dca_read_update_pos 1 +#define reg_dca_read_update_len 1 +#define reg_dca_read_update_lsb 0 +#define xd_r_reg_dca_data_re_5_0 0xA173 +#define reg_dca_data_re_5_0_pos 2 +#define reg_dca_data_re_5_0_len 6 +#define reg_dca_data_re_5_0_lsb 0 +#define xd_r_reg_dca_data_re_10_6 0xA174 +#define reg_dca_data_re_10_6_pos 0 +#define reg_dca_data_re_10_6_len 5 +#define reg_dca_data_re_10_6_lsb 6 +#define xd_r_reg_dca_data_im_7_0 0xA175 +#define reg_dca_data_im_7_0_pos 0 +#define reg_dca_data_im_7_0_len 8 +#define reg_dca_data_im_7_0_lsb 0 +#define xd_r_reg_dca_data_im_10_8 0xA176 +#define reg_dca_data_im_10_8_pos 0 +#define reg_dca_data_im_10_8_len 3 +#define reg_dca_data_im_10_8_lsb 8 +#define xd_r_reg_dca_data_h2_7_0 0xA178 +#define reg_dca_data_h2_7_0_pos 0 +#define reg_dca_data_h2_7_0_len 8 +#define reg_dca_data_h2_7_0_lsb 0 +#define xd_r_reg_dca_data_h2_9_8 0xA179 +#define reg_dca_data_h2_9_8_pos 0 +#define reg_dca_data_h2_9_8_len 2 +#define reg_dca_data_h2_9_8_lsb 8 +#define xd_p_reg_f_adc_7_0 0xA180 +#define reg_f_adc_7_0_pos 0 +#define reg_f_adc_7_0_len 8 +#define reg_f_adc_7_0_lsb 0 +#define xd_p_reg_f_adc_15_8 0xA181 +#define reg_f_adc_15_8_pos 0 +#define reg_f_adc_15_8_len 8 +#define reg_f_adc_15_8_lsb 8 +#define xd_p_reg_f_adc_23_16 0xA182 +#define reg_f_adc_23_16_pos 0 +#define reg_f_adc_23_16_len 8 +#define reg_f_adc_23_16_lsb 16 +#define xd_r_intp_mu_7_0 0xA190 +#define intp_mu_7_0_pos 0 +#define intp_mu_7_0_len 8 +#define intp_mu_7_0_lsb 0 +#define xd_r_intp_mu_15_8 0xA191 +#define intp_mu_15_8_pos 0 +#define intp_mu_15_8_len 8 +#define intp_mu_15_8_lsb 8 +#define xd_r_intp_mu_19_16 0xA192 +#define intp_mu_19_16_pos 0 +#define intp_mu_19_16_len 4 +#define intp_mu_19_16_lsb 16 +#define xd_p_reg_agc_rst 0xA1A0 +#define reg_agc_rst_pos 0 +#define reg_agc_rst_len 1 +#define reg_agc_rst_lsb 0 +#define xd_p_rf_agc_en 0xA1A0 +#define rf_agc_en_pos 1 +#define rf_agc_en_len 1 +#define rf_agc_en_lsb 0 +#define xd_p_rf_agc_dis 0xA1A0 +#define rf_agc_dis_pos 2 +#define rf_agc_dis_len 1 +#define rf_agc_dis_lsb 0 +#define xd_p_if_agc_rst 0xA1A0 +#define if_agc_rst_pos 3 +#define if_agc_rst_len 1 +#define if_agc_rst_lsb 0 +#define xd_p_if_agc_en 0xA1A0 +#define if_agc_en_pos 4 +#define if_agc_en_len 1 +#define if_agc_en_lsb 0 +#define xd_p_if_agc_dis 0xA1A0 +#define if_agc_dis_pos 5 +#define if_agc_dis_len 1 +#define if_agc_dis_lsb 0 +#define xd_p_agc_lock 0xA1A0 +#define agc_lock_pos 6 +#define agc_lock_len 1 +#define agc_lock_lsb 0 +#define xd_p_reg_tinr_rst 0xA1A1 +#define reg_tinr_rst_pos 0 +#define reg_tinr_rst_len 1 +#define reg_tinr_rst_lsb 0 +#define xd_p_reg_tinr_en 0xA1A1 +#define reg_tinr_en_pos 1 +#define reg_tinr_en_len 1 +#define reg_tinr_en_lsb 0 +#define xd_p_reg_ccifs_en 0xA1A2 +#define reg_ccifs_en_pos 0 +#define reg_ccifs_en_len 1 +#define reg_ccifs_en_lsb 0 +#define xd_p_reg_ccifs_dis 0xA1A2 +#define reg_ccifs_dis_pos 1 +#define reg_ccifs_dis_len 1 +#define reg_ccifs_dis_lsb 0 +#define xd_p_reg_ccifs_rst 0xA1A2 +#define reg_ccifs_rst_pos 2 +#define reg_ccifs_rst_len 1 +#define reg_ccifs_rst_lsb 0 +#define xd_p_reg_ccifs_byp 0xA1A2 +#define reg_ccifs_byp_pos 3 +#define reg_ccifs_byp_len 1 +#define reg_ccifs_byp_lsb 0 +#define xd_p_reg_ccif_en 0xA1A3 +#define reg_ccif_en_pos 0 +#define reg_ccif_en_len 1 +#define reg_ccif_en_lsb 0 +#define xd_p_reg_ccif_dis 0xA1A3 +#define reg_ccif_dis_pos 1 +#define reg_ccif_dis_len 1 +#define reg_ccif_dis_lsb 0 +#define xd_p_reg_ccif_rst 0xA1A3 +#define reg_ccif_rst_pos 2 +#define reg_ccif_rst_len 1 +#define reg_ccif_rst_lsb 0 +#define xd_p_reg_ccif_byp 0xA1A3 +#define reg_ccif_byp_pos 3 +#define reg_ccif_byp_len 1 +#define reg_ccif_byp_lsb 0 +#define xd_p_dagc1_rst 0xA1A4 +#define dagc1_rst_pos 0 +#define dagc1_rst_len 1 +#define dagc1_rst_lsb 0 +#define xd_p_dagc1_en 0xA1A4 +#define dagc1_en_pos 1 +#define dagc1_en_len 1 +#define dagc1_en_lsb 0 +#define xd_p_dagc1_mode 0xA1A4 +#define dagc1_mode_pos 2 +#define dagc1_mode_len 2 +#define dagc1_mode_lsb 0 +#define xd_p_dagc1_done 0xA1A4 +#define dagc1_done_pos 4 +#define dagc1_done_len 1 +#define dagc1_done_lsb 0 +#define xd_p_ccid_rst 0xA1A5 +#define ccid_rst_pos 0 +#define ccid_rst_len 1 +#define ccid_rst_lsb 0 +#define xd_p_ccid_en 0xA1A5 +#define ccid_en_pos 1 +#define ccid_en_len 1 +#define ccid_en_lsb 0 +#define xd_p_ccid_mode 0xA1A5 +#define ccid_mode_pos 2 +#define ccid_mode_len 2 +#define ccid_mode_lsb 0 +#define xd_p_ccid_done 0xA1A5 +#define ccid_done_pos 4 +#define ccid_done_len 1 +#define ccid_done_lsb 0 +#define xd_r_ccid_deted 0xA1A5 +#define ccid_deted_pos 5 +#define ccid_deted_len 1 +#define ccid_deted_lsb 0 +#define xd_p_ccid2_en 0xA1A5 +#define ccid2_en_pos 6 +#define ccid2_en_len 1 +#define ccid2_en_lsb 0 +#define xd_p_ccid2_done 0xA1A5 +#define ccid2_done_pos 7 +#define ccid2_done_len 1 +#define ccid2_done_lsb 0 +#define xd_p_reg_bfs_en 0xA1A6 +#define reg_bfs_en_pos 0 +#define reg_bfs_en_len 1 +#define reg_bfs_en_lsb 0 +#define xd_p_reg_bfs_dis 0xA1A6 +#define reg_bfs_dis_pos 1 +#define reg_bfs_dis_len 1 +#define reg_bfs_dis_lsb 0 +#define xd_p_reg_bfs_rst 0xA1A6 +#define reg_bfs_rst_pos 2 +#define reg_bfs_rst_len 1 +#define reg_bfs_rst_lsb 0 +#define xd_p_reg_bfs_byp 0xA1A6 +#define reg_bfs_byp_pos 3 +#define reg_bfs_byp_len 1 +#define reg_bfs_byp_lsb 0 +#define xd_p_reg_antif_en 0xA1A7 +#define reg_antif_en_pos 0 +#define reg_antif_en_len 1 +#define reg_antif_en_lsb 0 +#define xd_p_reg_antif_dis 0xA1A7 +#define reg_antif_dis_pos 1 +#define reg_antif_dis_len 1 +#define reg_antif_dis_lsb 0 +#define xd_p_reg_antif_rst 0xA1A7 +#define reg_antif_rst_pos 2 +#define reg_antif_rst_len 1 +#define reg_antif_rst_lsb 0 +#define xd_p_reg_antif_byp 0xA1A7 +#define reg_antif_byp_pos 3 +#define reg_antif_byp_len 1 +#define reg_antif_byp_lsb 0 +#define xd_p_intp_en 0xA1A8 +#define intp_en_pos 0 +#define intp_en_len 1 +#define intp_en_lsb 0 +#define xd_p_intp_dis 0xA1A8 +#define intp_dis_pos 1 +#define intp_dis_len 1 +#define intp_dis_lsb 0 +#define xd_p_intp_rst 0xA1A8 +#define intp_rst_pos 2 +#define intp_rst_len 1 +#define intp_rst_lsb 0 +#define xd_p_intp_byp 0xA1A8 +#define intp_byp_pos 3 +#define intp_byp_len 1 +#define intp_byp_lsb 0 +#define xd_p_reg_acif_en 0xA1A9 +#define reg_acif_en_pos 0 +#define reg_acif_en_len 1 +#define reg_acif_en_lsb 0 +#define xd_p_reg_acif_dis 0xA1A9 +#define reg_acif_dis_pos 1 +#define reg_acif_dis_len 1 +#define reg_acif_dis_lsb 0 +#define xd_p_reg_acif_rst 0xA1A9 +#define reg_acif_rst_pos 2 +#define reg_acif_rst_len 1 +#define reg_acif_rst_lsb 0 +#define xd_p_reg_acif_byp 0xA1A9 +#define reg_acif_byp_pos 3 +#define reg_acif_byp_len 1 +#define reg_acif_byp_lsb 0 +#define xd_p_reg_acif_sync_mode 0xA1A9 +#define reg_acif_sync_mode_pos 4 +#define reg_acif_sync_mode_len 1 +#define reg_acif_sync_mode_lsb 0 +#define xd_p_dagc2_rst 0xA1AA +#define dagc2_rst_pos 0 +#define dagc2_rst_len 1 +#define dagc2_rst_lsb 0 +#define xd_p_dagc2_en 0xA1AA +#define dagc2_en_pos 1 +#define dagc2_en_len 1 +#define dagc2_en_lsb 0 +#define xd_p_dagc2_mode 0xA1AA +#define dagc2_mode_pos 2 +#define dagc2_mode_len 2 +#define dagc2_mode_lsb 0 +#define xd_p_dagc2_done 0xA1AA +#define dagc2_done_pos 4 +#define dagc2_done_len 1 +#define dagc2_done_lsb 0 +#define xd_p_reg_dca_en 0xA1AB +#define reg_dca_en_pos 0 +#define reg_dca_en_len 1 +#define reg_dca_en_lsb 0 +#define xd_p_dagc2_accumulate_num_2k_7_0 0xA1C0 +#define dagc2_accumulate_num_2k_7_0_pos 0 +#define dagc2_accumulate_num_2k_7_0_len 8 +#define dagc2_accumulate_num_2k_7_0_lsb 0 +#define xd_p_dagc2_accumulate_num_2k_12_8 0xA1C1 +#define dagc2_accumulate_num_2k_12_8_pos 0 +#define dagc2_accumulate_num_2k_12_8_len 5 +#define dagc2_accumulate_num_2k_12_8_lsb 8 +#define xd_p_dagc2_accumulate_num_8k_7_0 0xA1C2 +#define dagc2_accumulate_num_8k_7_0_pos 0 +#define dagc2_accumulate_num_8k_7_0_len 8 +#define dagc2_accumulate_num_8k_7_0_lsb 0 +#define xd_p_dagc2_accumulate_num_8k_12_8 0xA1C3 +#define dagc2_accumulate_num_8k_12_8_pos 0 +#define dagc2_accumulate_num_8k_12_8_len 5 +#define dagc2_accumulate_num_8k_12_8_lsb 8 +#define xd_p_dagc2_desired_level_2_0 0xA1C3 +#define dagc2_desired_level_2_0_pos 5 +#define dagc2_desired_level_2_0_len 3 +#define dagc2_desired_level_2_0_lsb 0 +#define xd_p_dagc2_desired_level_8_3 0xA1C4 +#define dagc2_desired_level_8_3_pos 0 +#define dagc2_desired_level_8_3_len 6 +#define dagc2_desired_level_8_3_lsb 3 +#define xd_p_dagc2_apply_delay 0xA1C5 +#define dagc2_apply_delay_pos 0 +#define dagc2_apply_delay_len 7 +#define dagc2_apply_delay_lsb 0 +#define xd_p_dagc2_bypass_scale_ctl 0xA1C6 +#define dagc2_bypass_scale_ctl_pos 0 +#define dagc2_bypass_scale_ctl_len 3 +#define dagc2_bypass_scale_ctl_lsb 0 +#define xd_p_dagc2_programmable_shift1 0xA1C7 +#define dagc2_programmable_shift1_pos 0 +#define dagc2_programmable_shift1_len 8 +#define dagc2_programmable_shift1_lsb 0 +#define xd_p_dagc2_programmable_shift2 0xA1C8 +#define dagc2_programmable_shift2_pos 0 +#define dagc2_programmable_shift2_len 8 +#define dagc2_programmable_shift2_lsb 0 +#define xd_p_reg_dagc2_in_sat_cnt_7_0 0xA1C9 +#define reg_dagc2_in_sat_cnt_7_0_pos 0 +#define reg_dagc2_in_sat_cnt_7_0_len 8 +#define reg_dagc2_in_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc2_in_sat_cnt_15_8 0xA1CA +#define reg_dagc2_in_sat_cnt_15_8_pos 0 +#define reg_dagc2_in_sat_cnt_15_8_len 8 +#define reg_dagc2_in_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc2_in_sat_cnt_23_16 0xA1CB +#define reg_dagc2_in_sat_cnt_23_16_pos 0 +#define reg_dagc2_in_sat_cnt_23_16_len 8 +#define reg_dagc2_in_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc2_in_sat_cnt_31_24 0xA1CC +#define reg_dagc2_in_sat_cnt_31_24_pos 0 +#define reg_dagc2_in_sat_cnt_31_24_len 8 +#define reg_dagc2_in_sat_cnt_31_24_lsb 24 +#define xd_p_reg_dagc2_out_sat_cnt_7_0 0xA1CD +#define reg_dagc2_out_sat_cnt_7_0_pos 0 +#define reg_dagc2_out_sat_cnt_7_0_len 8 +#define reg_dagc2_out_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc2_out_sat_cnt_15_8 0xA1CE +#define reg_dagc2_out_sat_cnt_15_8_pos 0 +#define reg_dagc2_out_sat_cnt_15_8_len 8 +#define reg_dagc2_out_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc2_out_sat_cnt_23_16 0xA1CF +#define reg_dagc2_out_sat_cnt_23_16_pos 0 +#define reg_dagc2_out_sat_cnt_23_16_len 8 +#define reg_dagc2_out_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc2_out_sat_cnt_31_24 0xA1D0 +#define reg_dagc2_out_sat_cnt_31_24_pos 0 +#define reg_dagc2_out_sat_cnt_31_24_len 8 +#define reg_dagc2_out_sat_cnt_31_24_lsb 24 +#define xd_r_dagc2_multiplier_7_0 0xA1D6 +#define dagc2_multiplier_7_0_pos 0 +#define dagc2_multiplier_7_0_len 8 +#define dagc2_multiplier_7_0_lsb 0 +#define xd_r_dagc2_multiplier_15_8 0xA1D7 +#define dagc2_multiplier_15_8_pos 0 +#define dagc2_multiplier_15_8_len 8 +#define dagc2_multiplier_15_8_lsb 8 +#define xd_r_dagc2_right_shift_bits 0xA1D8 +#define dagc2_right_shift_bits_pos 0 +#define dagc2_right_shift_bits_len 4 +#define dagc2_right_shift_bits_lsb 0 +#define xd_p_cfoe_NS_coeff1_7_0 0xA200 +#define cfoe_NS_coeff1_7_0_pos 0 +#define cfoe_NS_coeff1_7_0_len 8 +#define cfoe_NS_coeff1_7_0_lsb 0 +#define xd_p_cfoe_NS_coeff1_15_8 0xA201 +#define cfoe_NS_coeff1_15_8_pos 0 +#define cfoe_NS_coeff1_15_8_len 8 +#define cfoe_NS_coeff1_15_8_lsb 8 +#define xd_p_cfoe_NS_coeff1_23_16 0xA202 +#define cfoe_NS_coeff1_23_16_pos 0 +#define cfoe_NS_coeff1_23_16_len 8 +#define cfoe_NS_coeff1_23_16_lsb 16 +#define xd_p_cfoe_NS_coeff1_25_24 0xA203 +#define cfoe_NS_coeff1_25_24_pos 0 +#define cfoe_NS_coeff1_25_24_len 2 +#define cfoe_NS_coeff1_25_24_lsb 24 +#define xd_p_cfoe_NS_coeff2_5_0 0xA203 +#define cfoe_NS_coeff2_5_0_pos 2 +#define cfoe_NS_coeff2_5_0_len 6 +#define cfoe_NS_coeff2_5_0_lsb 0 +#define xd_p_cfoe_NS_coeff2_13_6 0xA204 +#define cfoe_NS_coeff2_13_6_pos 0 +#define cfoe_NS_coeff2_13_6_len 8 +#define cfoe_NS_coeff2_13_6_lsb 6 +#define xd_p_cfoe_NS_coeff2_21_14 0xA205 +#define cfoe_NS_coeff2_21_14_pos 0 +#define cfoe_NS_coeff2_21_14_len 8 +#define cfoe_NS_coeff2_21_14_lsb 14 +#define xd_p_cfoe_NS_coeff2_24_22 0xA206 +#define cfoe_NS_coeff2_24_22_pos 0 +#define cfoe_NS_coeff2_24_22_len 3 +#define cfoe_NS_coeff2_24_22_lsb 22 +#define xd_p_cfoe_lf_c1_4_0 0xA206 +#define cfoe_lf_c1_4_0_pos 3 +#define cfoe_lf_c1_4_0_len 5 +#define cfoe_lf_c1_4_0_lsb 0 +#define xd_p_cfoe_lf_c1_12_5 0xA207 +#define cfoe_lf_c1_12_5_pos 0 +#define cfoe_lf_c1_12_5_len 8 +#define cfoe_lf_c1_12_5_lsb 5 +#define xd_p_cfoe_lf_c1_20_13 0xA208 +#define cfoe_lf_c1_20_13_pos 0 +#define cfoe_lf_c1_20_13_len 8 +#define cfoe_lf_c1_20_13_lsb 13 +#define xd_p_cfoe_lf_c1_25_21 0xA209 +#define cfoe_lf_c1_25_21_pos 0 +#define cfoe_lf_c1_25_21_len 5 +#define cfoe_lf_c1_25_21_lsb 21 +#define xd_p_cfoe_lf_c2_2_0 0xA209 +#define cfoe_lf_c2_2_0_pos 5 +#define cfoe_lf_c2_2_0_len 3 +#define cfoe_lf_c2_2_0_lsb 0 +#define xd_p_cfoe_lf_c2_10_3 0xA20A +#define cfoe_lf_c2_10_3_pos 0 +#define cfoe_lf_c2_10_3_len 8 +#define cfoe_lf_c2_10_3_lsb 3 +#define xd_p_cfoe_lf_c2_18_11 0xA20B +#define cfoe_lf_c2_18_11_pos 0 +#define cfoe_lf_c2_18_11_len 8 +#define cfoe_lf_c2_18_11_lsb 11 +#define xd_p_cfoe_lf_c2_25_19 0xA20C +#define cfoe_lf_c2_25_19_pos 0 +#define cfoe_lf_c2_25_19_len 7 +#define cfoe_lf_c2_25_19_lsb 19 +#define xd_p_cfoe_ifod_7_0 0xA20D +#define cfoe_ifod_7_0_pos 0 +#define cfoe_ifod_7_0_len 8 +#define cfoe_ifod_7_0_lsb 0 +#define xd_p_cfoe_ifod_10_8 0xA20E +#define cfoe_ifod_10_8_pos 0 +#define cfoe_ifod_10_8_len 3 +#define cfoe_ifod_10_8_lsb 8 +#define xd_p_cfoe_Divg_ctr_th 0xA20E +#define cfoe_Divg_ctr_th_pos 4 +#define cfoe_Divg_ctr_th_len 4 +#define cfoe_Divg_ctr_th_lsb 0 +#define xd_p_cfoe_FOT_divg_th 0xA20F +#define cfoe_FOT_divg_th_pos 0 +#define cfoe_FOT_divg_th_len 8 +#define cfoe_FOT_divg_th_lsb 0 +#define xd_p_cfoe_FOT_cnvg_th 0xA210 +#define cfoe_FOT_cnvg_th_pos 0 +#define cfoe_FOT_cnvg_th_len 8 +#define cfoe_FOT_cnvg_th_lsb 0 +#define xd_p_reg_cfoe_offset_7_0 0xA211 +#define reg_cfoe_offset_7_0_pos 0 +#define reg_cfoe_offset_7_0_len 8 +#define reg_cfoe_offset_7_0_lsb 0 +#define xd_p_reg_cfoe_offset_9_8 0xA212 +#define reg_cfoe_offset_9_8_pos 0 +#define reg_cfoe_offset_9_8_len 2 +#define reg_cfoe_offset_9_8_lsb 8 +#define xd_p_reg_cfoe_ifoe_sign_corr 0xA212 +#define reg_cfoe_ifoe_sign_corr_pos 2 +#define reg_cfoe_ifoe_sign_corr_len 1 +#define reg_cfoe_ifoe_sign_corr_lsb 0 +#define xd_r_cfoe_fot_LF_output_7_0 0xA218 +#define cfoe_fot_LF_output_7_0_pos 0 +#define cfoe_fot_LF_output_7_0_len 8 +#define cfoe_fot_LF_output_7_0_lsb 0 +#define xd_r_cfoe_fot_LF_output_15_8 0xA219 +#define cfoe_fot_LF_output_15_8_pos 0 +#define cfoe_fot_LF_output_15_8_len 8 +#define cfoe_fot_LF_output_15_8_lsb 8 +#define xd_r_cfoe_ifo_metric_7_0 0xA21A +#define cfoe_ifo_metric_7_0_pos 0 +#define cfoe_ifo_metric_7_0_len 8 +#define cfoe_ifo_metric_7_0_lsb 0 +#define xd_r_cfoe_ifo_metric_15_8 0xA21B +#define cfoe_ifo_metric_15_8_pos 0 +#define cfoe_ifo_metric_15_8_len 8 +#define cfoe_ifo_metric_15_8_lsb 8 +#define xd_r_cfoe_ifo_metric_23_16 0xA21C +#define cfoe_ifo_metric_23_16_pos 0 +#define cfoe_ifo_metric_23_16_len 8 +#define cfoe_ifo_metric_23_16_lsb 16 +#define xd_p_ste_Nu 0xA220 +#define ste_Nu_pos 0 +#define ste_Nu_len 2 +#define ste_Nu_lsb 0 +#define xd_p_ste_GI 0xA220 +#define ste_GI_pos 2 +#define ste_GI_len 3 +#define ste_GI_lsb 0 +#define xd_p_ste_symbol_num 0xA221 +#define ste_symbol_num_pos 0 +#define ste_symbol_num_len 2 +#define ste_symbol_num_lsb 0 +#define xd_p_ste_sample_num 0xA221 +#define ste_sample_num_pos 2 +#define ste_sample_num_len 2 +#define ste_sample_num_lsb 0 +#define xd_p_reg_ste_buf_en 0xA221 +#define reg_ste_buf_en_pos 7 +#define reg_ste_buf_en_len 1 +#define reg_ste_buf_en_lsb 0 +#define xd_p_ste_FFT_offset_7_0 0xA222 +#define ste_FFT_offset_7_0_pos 0 +#define ste_FFT_offset_7_0_len 8 +#define ste_FFT_offset_7_0_lsb 0 +#define xd_p_ste_FFT_offset_11_8 0xA223 +#define ste_FFT_offset_11_8_pos 0 +#define ste_FFT_offset_11_8_len 4 +#define ste_FFT_offset_11_8_lsb 8 +#define xd_p_reg_ste_tstmod 0xA223 +#define reg_ste_tstmod_pos 5 +#define reg_ste_tstmod_len 1 +#define reg_ste_tstmod_lsb 0 +#define xd_p_ste_adv_start_7_0 0xA224 +#define ste_adv_start_7_0_pos 0 +#define ste_adv_start_7_0_len 8 +#define ste_adv_start_7_0_lsb 0 +#define xd_p_ste_adv_start_10_8 0xA225 +#define ste_adv_start_10_8_pos 0 +#define ste_adv_start_10_8_len 3 +#define ste_adv_start_10_8_lsb 8 +#define xd_p_ste_adv_stop 0xA226 +#define ste_adv_stop_pos 0 +#define ste_adv_stop_len 8 +#define ste_adv_stop_lsb 0 +#define xd_r_ste_P_value_7_0 0xA228 +#define ste_P_value_7_0_pos 0 +#define ste_P_value_7_0_len 8 +#define ste_P_value_7_0_lsb 0 +#define xd_r_ste_P_value_10_8 0xA229 +#define ste_P_value_10_8_pos 0 +#define ste_P_value_10_8_len 3 +#define ste_P_value_10_8_lsb 8 +#define xd_r_ste_M_value_7_0 0xA22A +#define ste_M_value_7_0_pos 0 +#define ste_M_value_7_0_len 8 +#define ste_M_value_7_0_lsb 0 +#define xd_r_ste_M_value_10_8 0xA22B +#define ste_M_value_10_8_pos 0 +#define ste_M_value_10_8_len 3 +#define ste_M_value_10_8_lsb 8 +#define xd_r_ste_H1 0xA22C +#define ste_H1_pos 0 +#define ste_H1_len 7 +#define ste_H1_lsb 0 +#define xd_r_ste_H2 0xA22D +#define ste_H2_pos 0 +#define ste_H2_len 7 +#define ste_H2_lsb 0 +#define xd_r_ste_H3 0xA22E +#define ste_H3_pos 0 +#define ste_H3_len 7 +#define ste_H3_lsb 0 +#define xd_r_ste_H4 0xA22F +#define ste_H4_pos 0 +#define ste_H4_len 7 +#define ste_H4_lsb 0 +#define xd_r_ste_Corr_value_I_7_0 0xA230 +#define ste_Corr_value_I_7_0_pos 0 +#define ste_Corr_value_I_7_0_len 8 +#define ste_Corr_value_I_7_0_lsb 0 +#define xd_r_ste_Corr_value_I_15_8 0xA231 +#define ste_Corr_value_I_15_8_pos 0 +#define ste_Corr_value_I_15_8_len 8 +#define ste_Corr_value_I_15_8_lsb 8 +#define xd_r_ste_Corr_value_I_23_16 0xA232 +#define ste_Corr_value_I_23_16_pos 0 +#define ste_Corr_value_I_23_16_len 8 +#define ste_Corr_value_I_23_16_lsb 16 +#define xd_r_ste_Corr_value_I_27_24 0xA233 +#define ste_Corr_value_I_27_24_pos 0 +#define ste_Corr_value_I_27_24_len 4 +#define ste_Corr_value_I_27_24_lsb 24 +#define xd_r_ste_Corr_value_Q_7_0 0xA234 +#define ste_Corr_value_Q_7_0_pos 0 +#define ste_Corr_value_Q_7_0_len 8 +#define ste_Corr_value_Q_7_0_lsb 0 +#define xd_r_ste_Corr_value_Q_15_8 0xA235 +#define ste_Corr_value_Q_15_8_pos 0 +#define ste_Corr_value_Q_15_8_len 8 +#define ste_Corr_value_Q_15_8_lsb 8 +#define xd_r_ste_Corr_value_Q_23_16 0xA236 +#define ste_Corr_value_Q_23_16_pos 0 +#define ste_Corr_value_Q_23_16_len 8 +#define ste_Corr_value_Q_23_16_lsb 16 +#define xd_r_ste_Corr_value_Q_27_24 0xA237 +#define ste_Corr_value_Q_27_24_pos 0 +#define ste_Corr_value_Q_27_24_len 4 +#define ste_Corr_value_Q_27_24_lsb 24 +#define xd_r_ste_J_num_7_0 0xA238 +#define ste_J_num_7_0_pos 0 +#define ste_J_num_7_0_len 8 +#define ste_J_num_7_0_lsb 0 +#define xd_r_ste_J_num_15_8 0xA239 +#define ste_J_num_15_8_pos 0 +#define ste_J_num_15_8_len 8 +#define ste_J_num_15_8_lsb 8 +#define xd_r_ste_J_num_23_16 0xA23A +#define ste_J_num_23_16_pos 0 +#define ste_J_num_23_16_len 8 +#define ste_J_num_23_16_lsb 16 +#define xd_r_ste_J_num_31_24 0xA23B +#define ste_J_num_31_24_pos 0 +#define ste_J_num_31_24_len 8 +#define ste_J_num_31_24_lsb 24 +#define xd_r_ste_J_den_7_0 0xA23C +#define ste_J_den_7_0_pos 0 +#define ste_J_den_7_0_len 8 +#define ste_J_den_7_0_lsb 0 +#define xd_r_ste_J_den_15_8 0xA23D +#define ste_J_den_15_8_pos 0 +#define ste_J_den_15_8_len 8 +#define ste_J_den_15_8_lsb 8 +#define xd_r_ste_J_den_18_16 0xA23E +#define ste_J_den_18_16_pos 0 +#define ste_J_den_18_16_len 3 +#define ste_J_den_18_16_lsb 16 +#define xd_r_ste_Beacon_Indicator 0xA23E +#define ste_Beacon_Indicator_pos 4 +#define ste_Beacon_Indicator_len 1 +#define ste_Beacon_Indicator_lsb 0 +#define xd_r_tpsd_Frame_Num 0xA250 +#define tpsd_Frame_Num_pos 0 +#define tpsd_Frame_Num_len 2 +#define tpsd_Frame_Num_lsb 0 +#define xd_r_tpsd_Constel 0xA250 +#define tpsd_Constel_pos 2 +#define tpsd_Constel_len 2 +#define tpsd_Constel_lsb 0 +#define xd_r_tpsd_GI 0xA250 +#define tpsd_GI_pos 4 +#define tpsd_GI_len 2 +#define tpsd_GI_lsb 0 +#define xd_r_tpsd_Mode 0xA250 +#define tpsd_Mode_pos 6 +#define tpsd_Mode_len 2 +#define tpsd_Mode_lsb 0 +#define xd_r_tpsd_CR_HP 0xA251 +#define tpsd_CR_HP_pos 0 +#define tpsd_CR_HP_len 3 +#define tpsd_CR_HP_lsb 0 +#define xd_r_tpsd_CR_LP 0xA251 +#define tpsd_CR_LP_pos 3 +#define tpsd_CR_LP_len 3 +#define tpsd_CR_LP_lsb 0 +#define xd_r_tpsd_Hie 0xA252 +#define tpsd_Hie_pos 0 +#define tpsd_Hie_len 3 +#define tpsd_Hie_lsb 0 +#define xd_r_tpsd_Res_Bits 0xA252 +#define tpsd_Res_Bits_pos 3 +#define tpsd_Res_Bits_len 5 +#define tpsd_Res_Bits_lsb 0 +#define xd_r_tpsd_Res_Bits_0 0xA253 +#define tpsd_Res_Bits_0_pos 0 +#define tpsd_Res_Bits_0_len 1 +#define tpsd_Res_Bits_0_lsb 0 +#define xd_r_tpsd_LengthInd 0xA253 +#define tpsd_LengthInd_pos 1 +#define tpsd_LengthInd_len 6 +#define tpsd_LengthInd_lsb 0 +#define xd_r_tpsd_Cell_Id_7_0 0xA254 +#define tpsd_Cell_Id_7_0_pos 0 +#define tpsd_Cell_Id_7_0_len 8 +#define tpsd_Cell_Id_7_0_lsb 0 +#define xd_r_tpsd_Cell_Id_15_8 0xA255 +#define tpsd_Cell_Id_15_8_pos 0 +#define tpsd_Cell_Id_15_8_len 8 +#define tpsd_Cell_Id_15_8_lsb 0 +#define xd_p_reg_fft_mask_tone0_7_0 0xA260 +#define reg_fft_mask_tone0_7_0_pos 0 +#define reg_fft_mask_tone0_7_0_len 8 +#define reg_fft_mask_tone0_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone0_12_8 0xA261 +#define reg_fft_mask_tone0_12_8_pos 0 +#define reg_fft_mask_tone0_12_8_len 5 +#define reg_fft_mask_tone0_12_8_lsb 8 +#define xd_p_reg_fft_mask_tone1_7_0 0xA262 +#define reg_fft_mask_tone1_7_0_pos 0 +#define reg_fft_mask_tone1_7_0_len 8 +#define reg_fft_mask_tone1_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone1_12_8 0xA263 +#define reg_fft_mask_tone1_12_8_pos 0 +#define reg_fft_mask_tone1_12_8_len 5 +#define reg_fft_mask_tone1_12_8_lsb 8 +#define xd_p_reg_fft_mask_tone2_7_0 0xA264 +#define reg_fft_mask_tone2_7_0_pos 0 +#define reg_fft_mask_tone2_7_0_len 8 +#define reg_fft_mask_tone2_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone2_12_8 0xA265 +#define reg_fft_mask_tone2_12_8_pos 0 +#define reg_fft_mask_tone2_12_8_len 5 +#define reg_fft_mask_tone2_12_8_lsb 8 +#define xd_p_reg_fft_mask_tone3_7_0 0xA266 +#define reg_fft_mask_tone3_7_0_pos 0 +#define reg_fft_mask_tone3_7_0_len 8 +#define reg_fft_mask_tone3_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone3_12_8 0xA267 +#define reg_fft_mask_tone3_12_8_pos 0 +#define reg_fft_mask_tone3_12_8_len 5 +#define reg_fft_mask_tone3_12_8_lsb 8 +#define xd_p_reg_fft_mask_from0_7_0 0xA268 +#define reg_fft_mask_from0_7_0_pos 0 +#define reg_fft_mask_from0_7_0_len 8 +#define reg_fft_mask_from0_7_0_lsb 0 +#define xd_p_reg_fft_mask_from0_12_8 0xA269 +#define reg_fft_mask_from0_12_8_pos 0 +#define reg_fft_mask_from0_12_8_len 5 +#define reg_fft_mask_from0_12_8_lsb 8 +#define xd_p_reg_fft_mask_to0_7_0 0xA26A +#define reg_fft_mask_to0_7_0_pos 0 +#define reg_fft_mask_to0_7_0_len 8 +#define reg_fft_mask_to0_7_0_lsb 0 +#define xd_p_reg_fft_mask_to0_12_8 0xA26B +#define reg_fft_mask_to0_12_8_pos 0 +#define reg_fft_mask_to0_12_8_len 5 +#define reg_fft_mask_to0_12_8_lsb 8 +#define xd_p_reg_fft_mask_from1_7_0 0xA26C +#define reg_fft_mask_from1_7_0_pos 0 +#define reg_fft_mask_from1_7_0_len 8 +#define reg_fft_mask_from1_7_0_lsb 0 +#define xd_p_reg_fft_mask_from1_12_8 0xA26D +#define reg_fft_mask_from1_12_8_pos 0 +#define reg_fft_mask_from1_12_8_len 5 +#define reg_fft_mask_from1_12_8_lsb 8 +#define xd_p_reg_fft_mask_to1_7_0 0xA26E +#define reg_fft_mask_to1_7_0_pos 0 +#define reg_fft_mask_to1_7_0_len 8 +#define reg_fft_mask_to1_7_0_lsb 0 +#define xd_p_reg_fft_mask_to1_12_8 0xA26F +#define reg_fft_mask_to1_12_8_pos 0 +#define reg_fft_mask_to1_12_8_len 5 +#define reg_fft_mask_to1_12_8_lsb 8 +#define xd_p_reg_cge_idx0_7_0 0xA280 +#define reg_cge_idx0_7_0_pos 0 +#define reg_cge_idx0_7_0_len 8 +#define reg_cge_idx0_7_0_lsb 0 +#define xd_p_reg_cge_idx0_12_8 0xA281 +#define reg_cge_idx0_12_8_pos 0 +#define reg_cge_idx0_12_8_len 5 +#define reg_cge_idx0_12_8_lsb 8 +#define xd_p_reg_cge_idx1_7_0 0xA282 +#define reg_cge_idx1_7_0_pos 0 +#define reg_cge_idx1_7_0_len 8 +#define reg_cge_idx1_7_0_lsb 0 +#define xd_p_reg_cge_idx1_12_8 0xA283 +#define reg_cge_idx1_12_8_pos 0 +#define reg_cge_idx1_12_8_len 5 +#define reg_cge_idx1_12_8_lsb 8 +#define xd_p_reg_cge_idx2_7_0 0xA284 +#define reg_cge_idx2_7_0_pos 0 +#define reg_cge_idx2_7_0_len 8 +#define reg_cge_idx2_7_0_lsb 0 +#define xd_p_reg_cge_idx2_12_8 0xA285 +#define reg_cge_idx2_12_8_pos 0 +#define reg_cge_idx2_12_8_len 5 +#define reg_cge_idx2_12_8_lsb 8 +#define xd_p_reg_cge_idx3_7_0 0xA286 +#define reg_cge_idx3_7_0_pos 0 +#define reg_cge_idx3_7_0_len 8 +#define reg_cge_idx3_7_0_lsb 0 +#define xd_p_reg_cge_idx3_12_8 0xA287 +#define reg_cge_idx3_12_8_pos 0 +#define reg_cge_idx3_12_8_len 5 +#define reg_cge_idx3_12_8_lsb 8 +#define xd_p_reg_cge_idx4_7_0 0xA288 +#define reg_cge_idx4_7_0_pos 0 +#define reg_cge_idx4_7_0_len 8 +#define reg_cge_idx4_7_0_lsb 0 +#define xd_p_reg_cge_idx4_12_8 0xA289 +#define reg_cge_idx4_12_8_pos 0 +#define reg_cge_idx4_12_8_len 5 +#define reg_cge_idx4_12_8_lsb 8 +#define xd_p_reg_cge_idx5_7_0 0xA28A +#define reg_cge_idx5_7_0_pos 0 +#define reg_cge_idx5_7_0_len 8 +#define reg_cge_idx5_7_0_lsb 0 +#define xd_p_reg_cge_idx5_12_8 0xA28B +#define reg_cge_idx5_12_8_pos 0 +#define reg_cge_idx5_12_8_len 5 +#define reg_cge_idx5_12_8_lsb 8 +#define xd_p_reg_cge_idx6_7_0 0xA28C +#define reg_cge_idx6_7_0_pos 0 +#define reg_cge_idx6_7_0_len 8 +#define reg_cge_idx6_7_0_lsb 0 +#define xd_p_reg_cge_idx6_12_8 0xA28D +#define reg_cge_idx6_12_8_pos 0 +#define reg_cge_idx6_12_8_len 5 +#define reg_cge_idx6_12_8_lsb 8 +#define xd_p_reg_cge_idx7_7_0 0xA28E +#define reg_cge_idx7_7_0_pos 0 +#define reg_cge_idx7_7_0_len 8 +#define reg_cge_idx7_7_0_lsb 0 +#define xd_p_reg_cge_idx7_12_8 0xA28F +#define reg_cge_idx7_12_8_pos 0 +#define reg_cge_idx7_12_8_len 5 +#define reg_cge_idx7_12_8_lsb 8 +#define xd_p_reg_cge_idx8_7_0 0xA290 +#define reg_cge_idx8_7_0_pos 0 +#define reg_cge_idx8_7_0_len 8 +#define reg_cge_idx8_7_0_lsb 0 +#define xd_p_reg_cge_idx8_12_8 0xA291 +#define reg_cge_idx8_12_8_pos 0 +#define reg_cge_idx8_12_8_len 5 +#define reg_cge_idx8_12_8_lsb 8 +#define xd_p_reg_cge_idx9_7_0 0xA292 +#define reg_cge_idx9_7_0_pos 0 +#define reg_cge_idx9_7_0_len 8 +#define reg_cge_idx9_7_0_lsb 0 +#define xd_p_reg_cge_idx9_12_8 0xA293 +#define reg_cge_idx9_12_8_pos 0 +#define reg_cge_idx9_12_8_len 5 +#define reg_cge_idx9_12_8_lsb 8 +#define xd_p_reg_cge_idx10_7_0 0xA294 +#define reg_cge_idx10_7_0_pos 0 +#define reg_cge_idx10_7_0_len 8 +#define reg_cge_idx10_7_0_lsb 0 +#define xd_p_reg_cge_idx10_12_8 0xA295 +#define reg_cge_idx10_12_8_pos 0 +#define reg_cge_idx10_12_8_len 5 +#define reg_cge_idx10_12_8_lsb 8 +#define xd_p_reg_cge_idx11_7_0 0xA296 +#define reg_cge_idx11_7_0_pos 0 +#define reg_cge_idx11_7_0_len 8 +#define reg_cge_idx11_7_0_lsb 0 +#define xd_p_reg_cge_idx11_12_8 0xA297 +#define reg_cge_idx11_12_8_pos 0 +#define reg_cge_idx11_12_8_len 5 +#define reg_cge_idx11_12_8_lsb 8 +#define xd_p_reg_cge_idx12_7_0 0xA298 +#define reg_cge_idx12_7_0_pos 0 +#define reg_cge_idx12_7_0_len 8 +#define reg_cge_idx12_7_0_lsb 0 +#define xd_p_reg_cge_idx12_12_8 0xA299 +#define reg_cge_idx12_12_8_pos 0 +#define reg_cge_idx12_12_8_len 5 +#define reg_cge_idx12_12_8_lsb 8 +#define xd_p_reg_cge_idx13_7_0 0xA29A +#define reg_cge_idx13_7_0_pos 0 +#define reg_cge_idx13_7_0_len 8 +#define reg_cge_idx13_7_0_lsb 0 +#define xd_p_reg_cge_idx13_12_8 0xA29B +#define reg_cge_idx13_12_8_pos 0 +#define reg_cge_idx13_12_8_len 5 +#define reg_cge_idx13_12_8_lsb 8 +#define xd_p_reg_cge_idx14_7_0 0xA29C +#define reg_cge_idx14_7_0_pos 0 +#define reg_cge_idx14_7_0_len 8 +#define reg_cge_idx14_7_0_lsb 0 +#define xd_p_reg_cge_idx14_12_8 0xA29D +#define reg_cge_idx14_12_8_pos 0 +#define reg_cge_idx14_12_8_len 5 +#define reg_cge_idx14_12_8_lsb 8 +#define xd_p_reg_cge_idx15_7_0 0xA29E +#define reg_cge_idx15_7_0_pos 0 +#define reg_cge_idx15_7_0_len 8 +#define reg_cge_idx15_7_0_lsb 0 +#define xd_p_reg_cge_idx15_12_8 0xA29F +#define reg_cge_idx15_12_8_pos 0 +#define reg_cge_idx15_12_8_len 5 +#define reg_cge_idx15_12_8_lsb 8 +#define xd_r_reg_fft_crc 0xA2A8 +#define reg_fft_crc_pos 0 +#define reg_fft_crc_len 8 +#define reg_fft_crc_lsb 0 +#define xd_p_fd_fft_shift_max 0xA2A9 +#define fd_fft_shift_max_pos 0 +#define fd_fft_shift_max_len 4 +#define fd_fft_shift_max_lsb 0 +#define xd_r_fd_fft_shift 0xA2A9 +#define fd_fft_shift_pos 4 +#define fd_fft_shift_len 4 +#define fd_fft_shift_lsb 0 +#define xd_r_fd_fft_frame_num 0xA2AA +#define fd_fft_frame_num_pos 0 +#define fd_fft_frame_num_len 2 +#define fd_fft_frame_num_lsb 0 +#define xd_r_fd_fft_symbol_count 0xA2AB +#define fd_fft_symbol_count_pos 0 +#define fd_fft_symbol_count_len 7 +#define fd_fft_symbol_count_lsb 0 +#define xd_r_reg_fft_idx_max_7_0 0xA2AC +#define reg_fft_idx_max_7_0_pos 0 +#define reg_fft_idx_max_7_0_len 8 +#define reg_fft_idx_max_7_0_lsb 0 +#define xd_r_reg_fft_idx_max_12_8 0xA2AD +#define reg_fft_idx_max_12_8_pos 0 +#define reg_fft_idx_max_12_8_len 5 +#define reg_fft_idx_max_12_8_lsb 8 +#define xd_p_reg_cge_program 0xA2AE +#define reg_cge_program_pos 0 +#define reg_cge_program_len 1 +#define reg_cge_program_lsb 0 +#define xd_p_reg_cge_fixed 0xA2AE +#define reg_cge_fixed_pos 1 +#define reg_cge_fixed_len 1 +#define reg_cge_fixed_lsb 0 +#define xd_p_reg_fft_rotate_en 0xA2AE +#define reg_fft_rotate_en_pos 2 +#define reg_fft_rotate_en_len 1 +#define reg_fft_rotate_en_lsb 0 +#define xd_p_reg_fft_rotate_base_4_0 0xA2AE +#define reg_fft_rotate_base_4_0_pos 3 +#define reg_fft_rotate_base_4_0_len 5 +#define reg_fft_rotate_base_4_0_lsb 0 +#define xd_p_reg_fft_rotate_base_12_5 0xA2AF +#define reg_fft_rotate_base_12_5_pos 0 +#define reg_fft_rotate_base_12_5_len 8 +#define reg_fft_rotate_base_12_5_lsb 5 +#define xd_p_reg_gp_trigger_fd 0xA2B8 +#define reg_gp_trigger_fd_pos 0 +#define reg_gp_trigger_fd_len 1 +#define reg_gp_trigger_fd_lsb 0 +#define xd_p_reg_trigger_sel_fd 0xA2B8 +#define reg_trigger_sel_fd_pos 1 +#define reg_trigger_sel_fd_len 2 +#define reg_trigger_sel_fd_lsb 0 +#define xd_p_reg_trigger_module_sel_fd 0xA2B9 +#define reg_trigger_module_sel_fd_pos 0 +#define reg_trigger_module_sel_fd_len 6 +#define reg_trigger_module_sel_fd_lsb 0 +#define xd_p_reg_trigger_set_sel_fd 0xA2BA +#define reg_trigger_set_sel_fd_pos 0 +#define reg_trigger_set_sel_fd_len 6 +#define reg_trigger_set_sel_fd_lsb 0 +#define xd_p_reg_fd_noname_7_0 0xA2BC +#define reg_fd_noname_7_0_pos 0 +#define reg_fd_noname_7_0_len 8 +#define reg_fd_noname_7_0_lsb 0 +#define xd_p_reg_fd_noname_15_8 0xA2BD +#define reg_fd_noname_15_8_pos 0 +#define reg_fd_noname_15_8_len 8 +#define reg_fd_noname_15_8_lsb 8 +#define xd_p_reg_fd_noname_23_16 0xA2BE +#define reg_fd_noname_23_16_pos 0 +#define reg_fd_noname_23_16_len 8 +#define reg_fd_noname_23_16_lsb 16 +#define xd_p_reg_fd_noname_31_24 0xA2BF +#define reg_fd_noname_31_24_pos 0 +#define reg_fd_noname_31_24_len 8 +#define reg_fd_noname_31_24_lsb 24 +#define xd_r_fd_fpcc_cp_corr_signn 0xA2C0 +#define fd_fpcc_cp_corr_signn_pos 0 +#define fd_fpcc_cp_corr_signn_len 8 +#define fd_fpcc_cp_corr_signn_lsb 0 +#define xd_p_reg_feq_s1 0xA2C1 +#define reg_feq_s1_pos 0 +#define reg_feq_s1_len 5 +#define reg_feq_s1_lsb 0 +#define xd_p_fd_fpcc_cp_corr_tone_th 0xA2C2 +#define fd_fpcc_cp_corr_tone_th_pos 0 +#define fd_fpcc_cp_corr_tone_th_len 6 +#define fd_fpcc_cp_corr_tone_th_lsb 0 +#define xd_p_fd_fpcc_cp_corr_symbol_log_th 0xA2C3 +#define fd_fpcc_cp_corr_symbol_log_th_pos 0 +#define fd_fpcc_cp_corr_symbol_log_th_len 4 +#define fd_fpcc_cp_corr_symbol_log_th_lsb 0 +#define xd_p_fd_fpcc_cp_corr_int 0xA2C4 +#define fd_fpcc_cp_corr_int_pos 0 +#define fd_fpcc_cp_corr_int_len 1 +#define fd_fpcc_cp_corr_int_lsb 0 +#define xd_p_reg_sfoe_ns_7_0 0xA320 +#define reg_sfoe_ns_7_0_pos 0 +#define reg_sfoe_ns_7_0_len 8 +#define reg_sfoe_ns_7_0_lsb 0 +#define xd_p_reg_sfoe_ns_14_8 0xA321 +#define reg_sfoe_ns_14_8_pos 0 +#define reg_sfoe_ns_14_8_len 7 +#define reg_sfoe_ns_14_8_lsb 8 +#define xd_p_reg_sfoe_c1_7_0 0xA322 +#define reg_sfoe_c1_7_0_pos 0 +#define reg_sfoe_c1_7_0_len 8 +#define reg_sfoe_c1_7_0_lsb 0 +#define xd_p_reg_sfoe_c1_15_8 0xA323 +#define reg_sfoe_c1_15_8_pos 0 +#define reg_sfoe_c1_15_8_len 8 +#define reg_sfoe_c1_15_8_lsb 8 +#define xd_p_reg_sfoe_c1_17_16 0xA324 +#define reg_sfoe_c1_17_16_pos 0 +#define reg_sfoe_c1_17_16_len 2 +#define reg_sfoe_c1_17_16_lsb 16 +#define xd_p_reg_sfoe_c2_7_0 0xA325 +#define reg_sfoe_c2_7_0_pos 0 +#define reg_sfoe_c2_7_0_len 8 +#define reg_sfoe_c2_7_0_lsb 0 +#define xd_p_reg_sfoe_c2_15_8 0xA326 +#define reg_sfoe_c2_15_8_pos 0 +#define reg_sfoe_c2_15_8_len 8 +#define reg_sfoe_c2_15_8_lsb 8 +#define xd_p_reg_sfoe_c2_17_16 0xA327 +#define reg_sfoe_c2_17_16_pos 0 +#define reg_sfoe_c2_17_16_len 2 +#define reg_sfoe_c2_17_16_lsb 16 +#define xd_r_reg_sfoe_out_9_2 0xA328 +#define reg_sfoe_out_9_2_pos 0 +#define reg_sfoe_out_9_2_len 8 +#define reg_sfoe_out_9_2_lsb 0 +#define xd_r_reg_sfoe_out_1_0 0xA329 +#define reg_sfoe_out_1_0_pos 0 +#define reg_sfoe_out_1_0_len 2 +#define reg_sfoe_out_1_0_lsb 0 +#define xd_p_reg_sfoe_lm_counter_th 0xA32A +#define reg_sfoe_lm_counter_th_pos 0 +#define reg_sfoe_lm_counter_th_len 4 +#define reg_sfoe_lm_counter_th_lsb 0 +#define xd_p_reg_sfoe_convg_th 0xA32B +#define reg_sfoe_convg_th_pos 0 +#define reg_sfoe_convg_th_len 8 +#define reg_sfoe_convg_th_lsb 0 +#define xd_p_reg_sfoe_divg_th 0xA32C +#define reg_sfoe_divg_th_pos 0 +#define reg_sfoe_divg_th_len 8 +#define reg_sfoe_divg_th_lsb 0 +#define xd_p_fd_tpsd_en 0xA330 +#define fd_tpsd_en_pos 0 +#define fd_tpsd_en_len 1 +#define fd_tpsd_en_lsb 0 +#define xd_p_fd_tpsd_dis 0xA330 +#define fd_tpsd_dis_pos 1 +#define fd_tpsd_dis_len 1 +#define fd_tpsd_dis_lsb 0 +#define xd_p_fd_tpsd_rst 0xA330 +#define fd_tpsd_rst_pos 2 +#define fd_tpsd_rst_len 1 +#define fd_tpsd_rst_lsb 0 +#define xd_p_fd_tpsd_lock 0xA330 +#define fd_tpsd_lock_pos 3 +#define fd_tpsd_lock_len 1 +#define fd_tpsd_lock_lsb 0 +#define xd_r_fd_tpsd_s19 0xA330 +#define fd_tpsd_s19_pos 4 +#define fd_tpsd_s19_len 1 +#define fd_tpsd_s19_lsb 0 +#define xd_r_fd_tpsd_s17 0xA330 +#define fd_tpsd_s17_pos 5 +#define fd_tpsd_s17_len 1 +#define fd_tpsd_s17_lsb 0 +#define xd_p_fd_sfr_ste_en 0xA331 +#define fd_sfr_ste_en_pos 0 +#define fd_sfr_ste_en_len 1 +#define fd_sfr_ste_en_lsb 0 +#define xd_p_fd_sfr_ste_dis 0xA331 +#define fd_sfr_ste_dis_pos 1 +#define fd_sfr_ste_dis_len 1 +#define fd_sfr_ste_dis_lsb 0 +#define xd_p_fd_sfr_ste_rst 0xA331 +#define fd_sfr_ste_rst_pos 2 +#define fd_sfr_ste_rst_len 1 +#define fd_sfr_ste_rst_lsb 0 +#define xd_p_fd_sfr_ste_mode 0xA331 +#define fd_sfr_ste_mode_pos 3 +#define fd_sfr_ste_mode_len 1 +#define fd_sfr_ste_mode_lsb 0 +#define xd_p_fd_sfr_ste_done 0xA331 +#define fd_sfr_ste_done_pos 4 +#define fd_sfr_ste_done_len 1 +#define fd_sfr_ste_done_lsb 0 +#define xd_p_reg_cfoe_ffoe_en 0xA332 +#define reg_cfoe_ffoe_en_pos 0 +#define reg_cfoe_ffoe_en_len 1 +#define reg_cfoe_ffoe_en_lsb 0 +#define xd_p_reg_cfoe_ffoe_dis 0xA332 +#define reg_cfoe_ffoe_dis_pos 1 +#define reg_cfoe_ffoe_dis_len 1 +#define reg_cfoe_ffoe_dis_lsb 0 +#define xd_p_reg_cfoe_ffoe_rst 0xA332 +#define reg_cfoe_ffoe_rst_pos 2 +#define reg_cfoe_ffoe_rst_len 1 +#define reg_cfoe_ffoe_rst_lsb 0 +#define xd_p_reg_cfoe_ifoe_en 0xA332 +#define reg_cfoe_ifoe_en_pos 3 +#define reg_cfoe_ifoe_en_len 1 +#define reg_cfoe_ifoe_en_lsb 0 +#define xd_p_reg_cfoe_ifoe_dis 0xA332 +#define reg_cfoe_ifoe_dis_pos 4 +#define reg_cfoe_ifoe_dis_len 1 +#define reg_cfoe_ifoe_dis_lsb 0 +#define xd_p_reg_cfoe_ifoe_rst 0xA332 +#define reg_cfoe_ifoe_rst_pos 5 +#define reg_cfoe_ifoe_rst_len 1 +#define reg_cfoe_ifoe_rst_lsb 0 +#define xd_p_reg_cfoe_fot_en 0xA332 +#define reg_cfoe_fot_en_pos 6 +#define reg_cfoe_fot_en_len 1 +#define reg_cfoe_fot_en_lsb 0 +#define xd_p_reg_cfoe_fot_lm_en 0xA332 +#define reg_cfoe_fot_lm_en_pos 7 +#define reg_cfoe_fot_lm_en_len 1 +#define reg_cfoe_fot_lm_en_lsb 0 +#define xd_p_reg_cfoe_fot_rst 0xA333 +#define reg_cfoe_fot_rst_pos 0 +#define reg_cfoe_fot_rst_len 1 +#define reg_cfoe_fot_rst_lsb 0 +#define xd_r_fd_cfoe_ffoe_done 0xA333 +#define fd_cfoe_ffoe_done_pos 1 +#define fd_cfoe_ffoe_done_len 1 +#define fd_cfoe_ffoe_done_lsb 0 +#define xd_p_fd_cfoe_metric_vld 0xA333 +#define fd_cfoe_metric_vld_pos 2 +#define fd_cfoe_metric_vld_len 1 +#define fd_cfoe_metric_vld_lsb 0 +#define xd_p_reg_cfoe_ifod_vld 0xA333 +#define reg_cfoe_ifod_vld_pos 3 +#define reg_cfoe_ifod_vld_len 1 +#define reg_cfoe_ifod_vld_lsb 0 +#define xd_r_fd_cfoe_ifoe_done 0xA333 +#define fd_cfoe_ifoe_done_pos 4 +#define fd_cfoe_ifoe_done_len 1 +#define fd_cfoe_ifoe_done_lsb 0 +#define xd_r_fd_cfoe_fot_valid 0xA333 +#define fd_cfoe_fot_valid_pos 5 +#define fd_cfoe_fot_valid_len 1 +#define fd_cfoe_fot_valid_lsb 0 +#define xd_p_reg_cfoe_divg_int 0xA333 +#define reg_cfoe_divg_int_pos 6 +#define reg_cfoe_divg_int_len 1 +#define reg_cfoe_divg_int_lsb 0 +#define xd_r_reg_cfoe_divg_flag 0xA333 +#define reg_cfoe_divg_flag_pos 7 +#define reg_cfoe_divg_flag_len 1 +#define reg_cfoe_divg_flag_lsb 0 +#define xd_p_reg_sfoe_en 0xA334 +#define reg_sfoe_en_pos 0 +#define reg_sfoe_en_len 1 +#define reg_sfoe_en_lsb 0 +#define xd_p_reg_sfoe_dis 0xA334 +#define reg_sfoe_dis_pos 1 +#define reg_sfoe_dis_len 1 +#define reg_sfoe_dis_lsb 0 +#define xd_p_reg_sfoe_rst 0xA334 +#define reg_sfoe_rst_pos 2 +#define reg_sfoe_rst_len 1 +#define reg_sfoe_rst_lsb 0 +#define xd_p_reg_sfoe_vld_int 0xA334 +#define reg_sfoe_vld_int_pos 3 +#define reg_sfoe_vld_int_len 1 +#define reg_sfoe_vld_int_lsb 0 +#define xd_p_reg_sfoe_lm_en 0xA334 +#define reg_sfoe_lm_en_pos 4 +#define reg_sfoe_lm_en_len 1 +#define reg_sfoe_lm_en_lsb 0 +#define xd_p_reg_sfoe_divg_int 0xA334 +#define reg_sfoe_divg_int_pos 5 +#define reg_sfoe_divg_int_len 1 +#define reg_sfoe_divg_int_lsb 0 +#define xd_r_reg_sfoe_divg_flag 0xA334 +#define reg_sfoe_divg_flag_pos 6 +#define reg_sfoe_divg_flag_len 1 +#define reg_sfoe_divg_flag_lsb 0 +#define xd_p_reg_fft_rst 0xA335 +#define reg_fft_rst_pos 0 +#define reg_fft_rst_len 1 +#define reg_fft_rst_lsb 0 +#define xd_p_reg_fft_fast_beacon 0xA335 +#define reg_fft_fast_beacon_pos 1 +#define reg_fft_fast_beacon_len 1 +#define reg_fft_fast_beacon_lsb 0 +#define xd_p_reg_fft_fast_valid 0xA335 +#define reg_fft_fast_valid_pos 2 +#define reg_fft_fast_valid_len 1 +#define reg_fft_fast_valid_lsb 0 +#define xd_p_reg_fft_mask_en 0xA335 +#define reg_fft_mask_en_pos 3 +#define reg_fft_mask_en_len 1 +#define reg_fft_mask_en_lsb 0 +#define xd_p_reg_fft_crc_en 0xA335 +#define reg_fft_crc_en_pos 4 +#define reg_fft_crc_en_len 1 +#define reg_fft_crc_en_lsb 0 +#define xd_p_reg_finr_en 0xA336 +#define reg_finr_en_pos 0 +#define reg_finr_en_len 1 +#define reg_finr_en_lsb 0 +#define xd_p_fd_fste_en 0xA337 +#define fd_fste_en_pos 1 +#define fd_fste_en_len 1 +#define fd_fste_en_lsb 0 +#define xd_p_fd_sqi_tps_level_shift 0xA338 +#define fd_sqi_tps_level_shift_pos 0 +#define fd_sqi_tps_level_shift_len 8 +#define fd_sqi_tps_level_shift_lsb 0 +#define xd_p_fd_pilot_ma_len 0xA339 +#define fd_pilot_ma_len_pos 0 +#define fd_pilot_ma_len_len 6 +#define fd_pilot_ma_len_lsb 0 +#define xd_p_fd_tps_ma_len 0xA33A +#define fd_tps_ma_len_pos 0 +#define fd_tps_ma_len_len 6 +#define fd_tps_ma_len_lsb 0 +#define xd_p_fd_sqi_s3 0xA33B +#define fd_sqi_s3_pos 0 +#define fd_sqi_s3_len 8 +#define fd_sqi_s3_lsb 0 +#define xd_p_fd_sqi_dummy_reg_0 0xA33C +#define fd_sqi_dummy_reg_0_pos 0 +#define fd_sqi_dummy_reg_0_len 1 +#define fd_sqi_dummy_reg_0_lsb 0 +#define xd_p_fd_sqi_debug_sel 0xA33C +#define fd_sqi_debug_sel_pos 1 +#define fd_sqi_debug_sel_len 2 +#define fd_sqi_debug_sel_lsb 0 +#define xd_p_fd_sqi_s2 0xA33C +#define fd_sqi_s2_pos 3 +#define fd_sqi_s2_len 5 +#define fd_sqi_s2_lsb 0 +#define xd_p_fd_sqi_dummy_reg_1 0xA33D +#define fd_sqi_dummy_reg_1_pos 0 +#define fd_sqi_dummy_reg_1_len 1 +#define fd_sqi_dummy_reg_1_lsb 0 +#define xd_p_fd_inr_ignore 0xA33D +#define fd_inr_ignore_pos 1 +#define fd_inr_ignore_len 1 +#define fd_inr_ignore_lsb 0 +#define xd_p_fd_pilot_ignore 0xA33D +#define fd_pilot_ignore_pos 2 +#define fd_pilot_ignore_len 1 +#define fd_pilot_ignore_lsb 0 +#define xd_p_fd_etps_ignore 0xA33D +#define fd_etps_ignore_pos 3 +#define fd_etps_ignore_len 1 +#define fd_etps_ignore_lsb 0 +#define xd_p_fd_sqi_s1 0xA33D +#define fd_sqi_s1_pos 4 +#define fd_sqi_s1_len 4 +#define fd_sqi_s1_lsb 0 +#define xd_p_reg_fste_ehw_7_0 0xA33E +#define reg_fste_ehw_7_0_pos 0 +#define reg_fste_ehw_7_0_len 8 +#define reg_fste_ehw_7_0_lsb 0 +#define xd_p_reg_fste_ehw_9_8 0xA33F +#define reg_fste_ehw_9_8_pos 0 +#define reg_fste_ehw_9_8_len 2 +#define reg_fste_ehw_9_8_lsb 8 +#define xd_p_reg_fste_i_adj_vld 0xA33F +#define reg_fste_i_adj_vld_pos 2 +#define reg_fste_i_adj_vld_len 1 +#define reg_fste_i_adj_vld_lsb 0 +#define xd_p_reg_fste_phase_ini_7_0 0xA340 +#define reg_fste_phase_ini_7_0_pos 0 +#define reg_fste_phase_ini_7_0_len 8 +#define reg_fste_phase_ini_7_0_lsb 0 +#define xd_p_reg_fste_phase_ini_11_8 0xA341 +#define reg_fste_phase_ini_11_8_pos 0 +#define reg_fste_phase_ini_11_8_len 4 +#define reg_fste_phase_ini_11_8_lsb 8 +#define xd_p_reg_fste_phase_inc_3_0 0xA341 +#define reg_fste_phase_inc_3_0_pos 4 +#define reg_fste_phase_inc_3_0_len 4 +#define reg_fste_phase_inc_3_0_lsb 0 +#define xd_p_reg_fste_phase_inc_11_4 0xA342 +#define reg_fste_phase_inc_11_4_pos 0 +#define reg_fste_phase_inc_11_4_len 8 +#define reg_fste_phase_inc_11_4_lsb 4 +#define xd_p_reg_fste_acum_cost_cnt_max 0xA343 +#define reg_fste_acum_cost_cnt_max_pos 0 +#define reg_fste_acum_cost_cnt_max_len 4 +#define reg_fste_acum_cost_cnt_max_lsb 0 +#define xd_p_reg_fste_step_size_std 0xA343 +#define reg_fste_step_size_std_pos 4 +#define reg_fste_step_size_std_len 4 +#define reg_fste_step_size_std_lsb 0 +#define xd_p_reg_fste_step_size_max 0xA344 +#define reg_fste_step_size_max_pos 0 +#define reg_fste_step_size_max_len 4 +#define reg_fste_step_size_max_lsb 0 +#define xd_p_reg_fste_step_size_min 0xA344 +#define reg_fste_step_size_min_pos 4 +#define reg_fste_step_size_min_len 4 +#define reg_fste_step_size_min_lsb 0 +#define xd_p_reg_fste_frac_step_size_7_0 0xA345 +#define reg_fste_frac_step_size_7_0_pos 0 +#define reg_fste_frac_step_size_7_0_len 8 +#define reg_fste_frac_step_size_7_0_lsb 0 +#define xd_p_reg_fste_frac_step_size_15_8 0xA346 +#define reg_fste_frac_step_size_15_8_pos 0 +#define reg_fste_frac_step_size_15_8_len 8 +#define reg_fste_frac_step_size_15_8_lsb 8 +#define xd_p_reg_fste_frac_step_size_19_16 0xA347 +#define reg_fste_frac_step_size_19_16_pos 0 +#define reg_fste_frac_step_size_19_16_len 4 +#define reg_fste_frac_step_size_19_16_lsb 16 +#define xd_p_reg_fste_rpd_dir_cnt_max 0xA347 +#define reg_fste_rpd_dir_cnt_max_pos 4 +#define reg_fste_rpd_dir_cnt_max_len 4 +#define reg_fste_rpd_dir_cnt_max_lsb 0 +#define xd_p_reg_fste_ehs 0xA348 +#define reg_fste_ehs_pos 0 +#define reg_fste_ehs_len 4 +#define reg_fste_ehs_lsb 0 +#define xd_p_reg_fste_frac_cost_cnt_max_3_0 0xA348 +#define reg_fste_frac_cost_cnt_max_3_0_pos 4 +#define reg_fste_frac_cost_cnt_max_3_0_len 4 +#define reg_fste_frac_cost_cnt_max_3_0_lsb 0 +#define xd_p_reg_fste_frac_cost_cnt_max_9_4 0xA349 +#define reg_fste_frac_cost_cnt_max_9_4_pos 0 +#define reg_fste_frac_cost_cnt_max_9_4_len 6 +#define reg_fste_frac_cost_cnt_max_9_4_lsb 4 +#define xd_p_reg_fste_w0_7_0 0xA34A +#define reg_fste_w0_7_0_pos 0 +#define reg_fste_w0_7_0_len 8 +#define reg_fste_w0_7_0_lsb 0 +#define xd_p_reg_fste_w0_11_8 0xA34B +#define reg_fste_w0_11_8_pos 0 +#define reg_fste_w0_11_8_len 4 +#define reg_fste_w0_11_8_lsb 8 +#define xd_p_reg_fste_w1_3_0 0xA34B +#define reg_fste_w1_3_0_pos 4 +#define reg_fste_w1_3_0_len 4 +#define reg_fste_w1_3_0_lsb 0 +#define xd_p_reg_fste_w1_11_4 0xA34C +#define reg_fste_w1_11_4_pos 0 +#define reg_fste_w1_11_4_len 8 +#define reg_fste_w1_11_4_lsb 4 +#define xd_p_reg_fste_w2_7_0 0xA34D +#define reg_fste_w2_7_0_pos 0 +#define reg_fste_w2_7_0_len 8 +#define reg_fste_w2_7_0_lsb 0 +#define xd_p_reg_fste_w2_11_8 0xA34E +#define reg_fste_w2_11_8_pos 0 +#define reg_fste_w2_11_8_len 4 +#define reg_fste_w2_11_8_lsb 8 +#define xd_p_reg_fste_w3_3_0 0xA34E +#define reg_fste_w3_3_0_pos 4 +#define reg_fste_w3_3_0_len 4 +#define reg_fste_w3_3_0_lsb 0 +#define xd_p_reg_fste_w3_11_4 0xA34F +#define reg_fste_w3_11_4_pos 0 +#define reg_fste_w3_11_4_len 8 +#define reg_fste_w3_11_4_lsb 4 +#define xd_p_reg_fste_w4_7_0 0xA350 +#define reg_fste_w4_7_0_pos 0 +#define reg_fste_w4_7_0_len 8 +#define reg_fste_w4_7_0_lsb 0 +#define xd_p_reg_fste_w4_11_8 0xA351 +#define reg_fste_w4_11_8_pos 0 +#define reg_fste_w4_11_8_len 4 +#define reg_fste_w4_11_8_lsb 8 +#define xd_p_reg_fste_w5_3_0 0xA351 +#define reg_fste_w5_3_0_pos 4 +#define reg_fste_w5_3_0_len 4 +#define reg_fste_w5_3_0_lsb 0 +#define xd_p_reg_fste_w5_11_4 0xA352 +#define reg_fste_w5_11_4_pos 0 +#define reg_fste_w5_11_4_len 8 +#define reg_fste_w5_11_4_lsb 4 +#define xd_p_reg_fste_w6_7_0 0xA353 +#define reg_fste_w6_7_0_pos 0 +#define reg_fste_w6_7_0_len 8 +#define reg_fste_w6_7_0_lsb 0 +#define xd_p_reg_fste_w6_11_8 0xA354 +#define reg_fste_w6_11_8_pos 0 +#define reg_fste_w6_11_8_len 4 +#define reg_fste_w6_11_8_lsb 8 +#define xd_p_reg_fste_w7_3_0 0xA354 +#define reg_fste_w7_3_0_pos 4 +#define reg_fste_w7_3_0_len 4 +#define reg_fste_w7_3_0_lsb 0 +#define xd_p_reg_fste_w7_11_4 0xA355 +#define reg_fste_w7_11_4_pos 0 +#define reg_fste_w7_11_4_len 8 +#define reg_fste_w7_11_4_lsb 4 +#define xd_p_reg_fste_w8_7_0 0xA356 +#define reg_fste_w8_7_0_pos 0 +#define reg_fste_w8_7_0_len 8 +#define reg_fste_w8_7_0_lsb 0 +#define xd_p_reg_fste_w8_11_8 0xA357 +#define reg_fste_w8_11_8_pos 0 +#define reg_fste_w8_11_8_len 4 +#define reg_fste_w8_11_8_lsb 8 +#define xd_p_reg_fste_w9_3_0 0xA357 +#define reg_fste_w9_3_0_pos 4 +#define reg_fste_w9_3_0_len 4 +#define reg_fste_w9_3_0_lsb 0 +#define xd_p_reg_fste_w9_11_4 0xA358 +#define reg_fste_w9_11_4_pos 0 +#define reg_fste_w9_11_4_len 8 +#define reg_fste_w9_11_4_lsb 4 +#define xd_p_reg_fste_wa_7_0 0xA359 +#define reg_fste_wa_7_0_pos 0 +#define reg_fste_wa_7_0_len 8 +#define reg_fste_wa_7_0_lsb 0 +#define xd_p_reg_fste_wa_11_8 0xA35A +#define reg_fste_wa_11_8_pos 0 +#define reg_fste_wa_11_8_len 4 +#define reg_fste_wa_11_8_lsb 8 +#define xd_p_reg_fste_wb_3_0 0xA35A +#define reg_fste_wb_3_0_pos 4 +#define reg_fste_wb_3_0_len 4 +#define reg_fste_wb_3_0_lsb 0 +#define xd_p_reg_fste_wb_11_4 0xA35B +#define reg_fste_wb_11_4_pos 0 +#define reg_fste_wb_11_4_len 8 +#define reg_fste_wb_11_4_lsb 4 +#define xd_r_fd_fste_i_adj 0xA35C +#define fd_fste_i_adj_pos 0 +#define fd_fste_i_adj_len 5 +#define fd_fste_i_adj_lsb 0 +#define xd_r_fd_fste_f_adj_7_0 0xA35D +#define fd_fste_f_adj_7_0_pos 0 +#define fd_fste_f_adj_7_0_len 8 +#define fd_fste_f_adj_7_0_lsb 0 +#define xd_r_fd_fste_f_adj_15_8 0xA35E +#define fd_fste_f_adj_15_8_pos 0 +#define fd_fste_f_adj_15_8_len 8 +#define fd_fste_f_adj_15_8_lsb 8 +#define xd_r_fd_fste_f_adj_19_16 0xA35F +#define fd_fste_f_adj_19_16_pos 0 +#define fd_fste_f_adj_19_16_len 4 +#define fd_fste_f_adj_19_16_lsb 16 +#define xd_p_reg_feq_Leak_Bypass 0xA366 +#define reg_feq_Leak_Bypass_pos 0 +#define reg_feq_Leak_Bypass_len 1 +#define reg_feq_Leak_Bypass_lsb 0 +#define xd_p_reg_feq_Leak_Mneg1 0xA366 +#define reg_feq_Leak_Mneg1_pos 1 +#define reg_feq_Leak_Mneg1_len 3 +#define reg_feq_Leak_Mneg1_lsb 0 +#define xd_p_reg_feq_Leak_B_ShiftQ 0xA366 +#define reg_feq_Leak_B_ShiftQ_pos 4 +#define reg_feq_Leak_B_ShiftQ_len 4 +#define reg_feq_Leak_B_ShiftQ_lsb 0 +#define xd_p_reg_feq_Leak_B_Float0 0xA367 +#define reg_feq_Leak_B_Float0_pos 0 +#define reg_feq_Leak_B_Float0_len 8 +#define reg_feq_Leak_B_Float0_lsb 0 +#define xd_p_reg_feq_Leak_B_Float1 0xA368 +#define reg_feq_Leak_B_Float1_pos 0 +#define reg_feq_Leak_B_Float1_len 8 +#define reg_feq_Leak_B_Float1_lsb 0 +#define xd_p_reg_feq_Leak_B_Float2 0xA369 +#define reg_feq_Leak_B_Float2_pos 0 +#define reg_feq_Leak_B_Float2_len 8 +#define reg_feq_Leak_B_Float2_lsb 0 +#define xd_p_reg_feq_Leak_B_Float3 0xA36A +#define reg_feq_Leak_B_Float3_pos 0 +#define reg_feq_Leak_B_Float3_len 8 +#define reg_feq_Leak_B_Float3_lsb 0 +#define xd_p_reg_feq_Leak_B_Float4 0xA36B +#define reg_feq_Leak_B_Float4_pos 0 +#define reg_feq_Leak_B_Float4_len 8 +#define reg_feq_Leak_B_Float4_lsb 0 +#define xd_p_reg_feq_Leak_B_Float5 0xA36C +#define reg_feq_Leak_B_Float5_pos 0 +#define reg_feq_Leak_B_Float5_len 8 +#define reg_feq_Leak_B_Float5_lsb 0 +#define xd_p_reg_feq_Leak_B_Float6 0xA36D +#define reg_feq_Leak_B_Float6_pos 0 +#define reg_feq_Leak_B_Float6_len 8 +#define reg_feq_Leak_B_Float6_lsb 0 +#define xd_p_reg_feq_Leak_B_Float7 0xA36E +#define reg_feq_Leak_B_Float7_pos 0 +#define reg_feq_Leak_B_Float7_len 8 +#define reg_feq_Leak_B_Float7_lsb 0 +#define xd_r_reg_feq_data_h2_7_0 0xA36F +#define reg_feq_data_h2_7_0_pos 0 +#define reg_feq_data_h2_7_0_len 8 +#define reg_feq_data_h2_7_0_lsb 0 +#define xd_r_reg_feq_data_h2_9_8 0xA370 +#define reg_feq_data_h2_9_8_pos 0 +#define reg_feq_data_h2_9_8_len 2 +#define reg_feq_data_h2_9_8_lsb 8 +#define xd_p_reg_feq_leak_use_slice_tps 0xA371 +#define reg_feq_leak_use_slice_tps_pos 0 +#define reg_feq_leak_use_slice_tps_len 1 +#define reg_feq_leak_use_slice_tps_lsb 0 +#define xd_p_reg_feq_read_update 0xA371 +#define reg_feq_read_update_pos 1 +#define reg_feq_read_update_len 1 +#define reg_feq_read_update_lsb 0 +#define xd_p_reg_feq_data_vld 0xA371 +#define reg_feq_data_vld_pos 2 +#define reg_feq_data_vld_len 1 +#define reg_feq_data_vld_lsb 0 +#define xd_p_reg_feq_tone_idx_4_0 0xA371 +#define reg_feq_tone_idx_4_0_pos 3 +#define reg_feq_tone_idx_4_0_len 5 +#define reg_feq_tone_idx_4_0_lsb 0 +#define xd_p_reg_feq_tone_idx_12_5 0xA372 +#define reg_feq_tone_idx_12_5_pos 0 +#define reg_feq_tone_idx_12_5_len 8 +#define reg_feq_tone_idx_12_5_lsb 5 +#define xd_r_reg_feq_data_re_7_0 0xA373 +#define reg_feq_data_re_7_0_pos 0 +#define reg_feq_data_re_7_0_len 8 +#define reg_feq_data_re_7_0_lsb 0 +#define xd_r_reg_feq_data_re_10_8 0xA374 +#define reg_feq_data_re_10_8_pos 0 +#define reg_feq_data_re_10_8_len 3 +#define reg_feq_data_re_10_8_lsb 8 +#define xd_r_reg_feq_data_im_7_0 0xA375 +#define reg_feq_data_im_7_0_pos 0 +#define reg_feq_data_im_7_0_len 8 +#define reg_feq_data_im_7_0_lsb 0 +#define xd_r_reg_feq_data_im_10_8 0xA376 +#define reg_feq_data_im_10_8_pos 0 +#define reg_feq_data_im_10_8_len 3 +#define reg_feq_data_im_10_8_lsb 8 +#define xd_r_reg_feq_y_re 0xA377 +#define reg_feq_y_re_pos 0 +#define reg_feq_y_re_len 8 +#define reg_feq_y_re_lsb 0 +#define xd_r_reg_feq_y_im 0xA378 +#define reg_feq_y_im_pos 0 +#define reg_feq_y_im_len 8 +#define reg_feq_y_im_lsb 0 +#define xd_r_reg_feq_h_re_7_0 0xA379 +#define reg_feq_h_re_7_0_pos 0 +#define reg_feq_h_re_7_0_len 8 +#define reg_feq_h_re_7_0_lsb 0 +#define xd_r_reg_feq_h_re_8 0xA37A +#define reg_feq_h_re_8_pos 0 +#define reg_feq_h_re_8_len 1 +#define reg_feq_h_re_8_lsb 0 +#define xd_r_reg_feq_h_im_7_0 0xA37B +#define reg_feq_h_im_7_0_pos 0 +#define reg_feq_h_im_7_0_len 8 +#define reg_feq_h_im_7_0_lsb 0 +#define xd_r_reg_feq_h_im_8 0xA37C +#define reg_feq_h_im_8_pos 0 +#define reg_feq_h_im_8_len 1 +#define reg_feq_h_im_8_lsb 0 +#define xd_p_fec_super_frm_unit_7_0 0xA380 +#define fec_super_frm_unit_7_0_pos 0 +#define fec_super_frm_unit_7_0_len 8 +#define fec_super_frm_unit_7_0_lsb 0 +#define xd_p_fec_super_frm_unit_15_8 0xA381 +#define fec_super_frm_unit_15_8_pos 0 +#define fec_super_frm_unit_15_8_len 8 +#define fec_super_frm_unit_15_8_lsb 8 +#define xd_r_fec_vtb_err_bit_cnt_7_0 0xA382 +#define fec_vtb_err_bit_cnt_7_0_pos 0 +#define fec_vtb_err_bit_cnt_7_0_len 8 +#define fec_vtb_err_bit_cnt_7_0_lsb 0 +#define xd_r_fec_vtb_err_bit_cnt_15_8 0xA383 +#define fec_vtb_err_bit_cnt_15_8_pos 0 +#define fec_vtb_err_bit_cnt_15_8_len 8 +#define fec_vtb_err_bit_cnt_15_8_lsb 8 +#define xd_r_fec_vtb_err_bit_cnt_23_16 0xA384 +#define fec_vtb_err_bit_cnt_23_16_pos 0 +#define fec_vtb_err_bit_cnt_23_16_len 8 +#define fec_vtb_err_bit_cnt_23_16_lsb 16 +#define xd_p_fec_rsd_packet_unit_7_0 0xA385 +#define fec_rsd_packet_unit_7_0_pos 0 +#define fec_rsd_packet_unit_7_0_len 8 +#define fec_rsd_packet_unit_7_0_lsb 0 +#define xd_p_fec_rsd_packet_unit_15_8 0xA386 +#define fec_rsd_packet_unit_15_8_pos 0 +#define fec_rsd_packet_unit_15_8_len 8 +#define fec_rsd_packet_unit_15_8_lsb 8 +#define xd_r_fec_rsd_bit_err_cnt_7_0 0xA387 +#define fec_rsd_bit_err_cnt_7_0_pos 0 +#define fec_rsd_bit_err_cnt_7_0_len 8 +#define fec_rsd_bit_err_cnt_7_0_lsb 0 +#define xd_r_fec_rsd_bit_err_cnt_15_8 0xA388 +#define fec_rsd_bit_err_cnt_15_8_pos 0 +#define fec_rsd_bit_err_cnt_15_8_len 8 +#define fec_rsd_bit_err_cnt_15_8_lsb 8 +#define xd_r_fec_rsd_bit_err_cnt_23_16 0xA389 +#define fec_rsd_bit_err_cnt_23_16_pos 0 +#define fec_rsd_bit_err_cnt_23_16_len 8 +#define fec_rsd_bit_err_cnt_23_16_lsb 16 +#define xd_r_fec_rsd_abort_packet_cnt_7_0 0xA38A +#define fec_rsd_abort_packet_cnt_7_0_pos 0 +#define fec_rsd_abort_packet_cnt_7_0_len 8 +#define fec_rsd_abort_packet_cnt_7_0_lsb 0 +#define xd_r_fec_rsd_abort_packet_cnt_15_8 0xA38B +#define fec_rsd_abort_packet_cnt_15_8_pos 0 +#define fec_rsd_abort_packet_cnt_15_8_len 8 +#define fec_rsd_abort_packet_cnt_15_8_lsb 8 +#define xd_p_fec_RSD_PKT_NUM_PER_UNIT_7_0 0xA38C +#define fec_RSD_PKT_NUM_PER_UNIT_7_0_pos 0 +#define fec_RSD_PKT_NUM_PER_UNIT_7_0_len 8 +#define fec_RSD_PKT_NUM_PER_UNIT_7_0_lsb 0 +#define xd_p_fec_RSD_PKT_NUM_PER_UNIT_15_8 0xA38D +#define fec_RSD_PKT_NUM_PER_UNIT_15_8_pos 0 +#define fec_RSD_PKT_NUM_PER_UNIT_15_8_len 8 +#define fec_RSD_PKT_NUM_PER_UNIT_15_8_lsb 8 +#define xd_p_fec_RS_TH_1_7_0 0xA38E +#define fec_RS_TH_1_7_0_pos 0 +#define fec_RS_TH_1_7_0_len 8 +#define fec_RS_TH_1_7_0_lsb 0 +#define xd_p_fec_RS_TH_1_15_8 0xA38F +#define fec_RS_TH_1_15_8_pos 0 +#define fec_RS_TH_1_15_8_len 8 +#define fec_RS_TH_1_15_8_lsb 8 +#define xd_p_fec_RS_TH_2 0xA390 +#define fec_RS_TH_2_pos 0 +#define fec_RS_TH_2_len 8 +#define fec_RS_TH_2_lsb 0 +#define xd_p_fec_mon_en 0xA391 +#define fec_mon_en_pos 0 +#define fec_mon_en_len 1 +#define fec_mon_en_lsb 0 +#define xd_p_reg_b8to47 0xA391 +#define reg_b8to47_pos 1 +#define reg_b8to47_len 1 +#define reg_b8to47_lsb 0 +#define xd_p_reg_rsd_sync_rep 0xA391 +#define reg_rsd_sync_rep_pos 2 +#define reg_rsd_sync_rep_len 1 +#define reg_rsd_sync_rep_lsb 0 +#define xd_p_fec_rsd_retrain_rst 0xA391 +#define fec_rsd_retrain_rst_pos 3 +#define fec_rsd_retrain_rst_len 1 +#define fec_rsd_retrain_rst_lsb 0 +#define xd_r_fec_rsd_ber_rdy 0xA391 +#define fec_rsd_ber_rdy_pos 4 +#define fec_rsd_ber_rdy_len 1 +#define fec_rsd_ber_rdy_lsb 0 +#define xd_p_fec_rsd_ber_rst 0xA391 +#define fec_rsd_ber_rst_pos 5 +#define fec_rsd_ber_rst_len 1 +#define fec_rsd_ber_rst_lsb 0 +#define xd_r_fec_vtb_ber_rdy 0xA391 +#define fec_vtb_ber_rdy_pos 6 +#define fec_vtb_ber_rdy_len 1 +#define fec_vtb_ber_rdy_lsb 0 +#define xd_p_fec_vtb_ber_rst 0xA391 +#define fec_vtb_ber_rst_pos 7 +#define fec_vtb_ber_rst_len 1 +#define fec_vtb_ber_rst_lsb 0 +#define xd_p_reg_vtb_clk40en 0xA392 +#define reg_vtb_clk40en_pos 0 +#define reg_vtb_clk40en_len 1 +#define reg_vtb_clk40en_lsb 0 +#define xd_p_fec_vtb_rsd_mon_en 0xA392 +#define fec_vtb_rsd_mon_en_pos 1 +#define fec_vtb_rsd_mon_en_len 1 +#define fec_vtb_rsd_mon_en_lsb 0 +#define xd_p_reg_fec_data_en 0xA392 +#define reg_fec_data_en_pos 2 +#define reg_fec_data_en_len 1 +#define reg_fec_data_en_lsb 0 +#define xd_p_fec_dummy_reg_2 0xA392 +#define fec_dummy_reg_2_pos 3 +#define fec_dummy_reg_2_len 3 +#define fec_dummy_reg_2_lsb 0 +#define xd_p_reg_sync_chk 0xA392 +#define reg_sync_chk_pos 6 +#define reg_sync_chk_len 1 +#define reg_sync_chk_lsb 0 +#define xd_p_fec_rsd_bypass 0xA392 +#define fec_rsd_bypass_pos 7 +#define fec_rsd_bypass_len 1 +#define fec_rsd_bypass_lsb 0 +#define xd_p_fec_sw_rst 0xA393 +#define fec_sw_rst_pos 0 +#define fec_sw_rst_len 1 +#define fec_sw_rst_lsb 0 +#define xd_r_fec_vtb_pm_crc 0xA394 +#define fec_vtb_pm_crc_pos 0 +#define fec_vtb_pm_crc_len 8 +#define fec_vtb_pm_crc_lsb 0 +#define xd_r_fec_vtb_tb_7_crc 0xA395 +#define fec_vtb_tb_7_crc_pos 0 +#define fec_vtb_tb_7_crc_len 8 +#define fec_vtb_tb_7_crc_lsb 0 +#define xd_r_fec_vtb_tb_6_crc 0xA396 +#define fec_vtb_tb_6_crc_pos 0 +#define fec_vtb_tb_6_crc_len 8 +#define fec_vtb_tb_6_crc_lsb 0 +#define xd_r_fec_vtb_tb_5_crc 0xA397 +#define fec_vtb_tb_5_crc_pos 0 +#define fec_vtb_tb_5_crc_len 8 +#define fec_vtb_tb_5_crc_lsb 0 +#define xd_r_fec_vtb_tb_4_crc 0xA398 +#define fec_vtb_tb_4_crc_pos 0 +#define fec_vtb_tb_4_crc_len 8 +#define fec_vtb_tb_4_crc_lsb 0 +#define xd_r_fec_vtb_tb_3_crc 0xA399 +#define fec_vtb_tb_3_crc_pos 0 +#define fec_vtb_tb_3_crc_len 8 +#define fec_vtb_tb_3_crc_lsb 0 +#define xd_r_fec_vtb_tb_2_crc 0xA39A +#define fec_vtb_tb_2_crc_pos 0 +#define fec_vtb_tb_2_crc_len 8 +#define fec_vtb_tb_2_crc_lsb 0 +#define xd_r_fec_vtb_tb_1_crc 0xA39B +#define fec_vtb_tb_1_crc_pos 0 +#define fec_vtb_tb_1_crc_len 8 +#define fec_vtb_tb_1_crc_lsb 0 +#define xd_r_fec_vtb_tb_0_crc 0xA39C +#define fec_vtb_tb_0_crc_pos 0 +#define fec_vtb_tb_0_crc_len 8 +#define fec_vtb_tb_0_crc_lsb 0 +#define xd_r_fec_rsd_bank0_crc 0xA39D +#define fec_rsd_bank0_crc_pos 0 +#define fec_rsd_bank0_crc_len 8 +#define fec_rsd_bank0_crc_lsb 0 +#define xd_r_fec_rsd_bank1_crc 0xA39E +#define fec_rsd_bank1_crc_pos 0 +#define fec_rsd_bank1_crc_len 8 +#define fec_rsd_bank1_crc_lsb 0 +#define xd_r_fec_idi_vtb_crc 0xA39F +#define fec_idi_vtb_crc_pos 0 +#define fec_idi_vtb_crc_len 8 +#define fec_idi_vtb_crc_lsb 0 +#define xd_g_reg_tpsd_txmod 0xA3C0 +#define reg_tpsd_txmod_pos 0 +#define reg_tpsd_txmod_len 2 +#define reg_tpsd_txmod_lsb 0 +#define xd_g_reg_tpsd_gi 0xA3C0 +#define reg_tpsd_gi_pos 2 +#define reg_tpsd_gi_len 2 +#define reg_tpsd_gi_lsb 0 +#define xd_g_reg_tpsd_hier 0xA3C0 +#define reg_tpsd_hier_pos 4 +#define reg_tpsd_hier_len 3 +#define reg_tpsd_hier_lsb 0 +#define xd_g_reg_bw 0xA3C1 +#define reg_bw_pos 2 +#define reg_bw_len 2 +#define reg_bw_lsb 0 +#define xd_g_reg_dec_pri 0xA3C1 +#define reg_dec_pri_pos 4 +#define reg_dec_pri_len 1 +#define reg_dec_pri_lsb 0 +#define xd_g_reg_tpsd_const 0xA3C1 +#define reg_tpsd_const_pos 6 +#define reg_tpsd_const_len 2 +#define reg_tpsd_const_lsb 0 +#define xd_g_reg_tpsd_hpcr 0xA3C2 +#define reg_tpsd_hpcr_pos 0 +#define reg_tpsd_hpcr_len 3 +#define reg_tpsd_hpcr_lsb 0 +#define xd_g_reg_tpsd_lpcr 0xA3C2 +#define reg_tpsd_lpcr_pos 3 +#define reg_tpsd_lpcr_len 3 +#define reg_tpsd_lpcr_lsb 0 +#define xd_g_reg_ofsm_clk 0xA3D0 +#define reg_ofsm_clk_pos 0 +#define reg_ofsm_clk_len 3 +#define reg_ofsm_clk_lsb 0 +#define xd_g_reg_fclk_cfg 0xA3D1 +#define reg_fclk_cfg_pos 0 +#define reg_fclk_cfg_len 1 +#define reg_fclk_cfg_lsb 0 +#define xd_g_reg_fclk_idi 0xA3D1 +#define reg_fclk_idi_pos 1 +#define reg_fclk_idi_len 1 +#define reg_fclk_idi_lsb 0 +#define xd_g_reg_fclk_odi 0xA3D1 +#define reg_fclk_odi_pos 2 +#define reg_fclk_odi_len 1 +#define reg_fclk_odi_lsb 0 +#define xd_g_reg_fclk_rsd 0xA3D1 +#define reg_fclk_rsd_pos 3 +#define reg_fclk_rsd_len 1 +#define reg_fclk_rsd_lsb 0 +#define xd_g_reg_fclk_vtb 0xA3D1 +#define reg_fclk_vtb_pos 4 +#define reg_fclk_vtb_len 1 +#define reg_fclk_vtb_lsb 0 +#define xd_g_reg_fclk_cste 0xA3D1 +#define reg_fclk_cste_pos 5 +#define reg_fclk_cste_len 1 +#define reg_fclk_cste_lsb 0 +#define xd_g_reg_fclk_mp2if 0xA3D1 +#define reg_fclk_mp2if_pos 6 +#define reg_fclk_mp2if_len 1 +#define reg_fclk_mp2if_lsb 0 +#define xd_I2C_i2c_m_slave_addr 0xA400 +#define i2c_m_slave_addr_pos 0 +#define i2c_m_slave_addr_len 8 +#define i2c_m_slave_addr_lsb 0 +#define xd_I2C_i2c_m_data1 0xA401 +#define i2c_m_data1_pos 0 +#define i2c_m_data1_len 8 +#define i2c_m_data1_lsb 0 +#define xd_I2C_i2c_m_data2 0xA402 +#define i2c_m_data2_pos 0 +#define i2c_m_data2_len 8 +#define i2c_m_data2_lsb 0 +#define xd_I2C_i2c_m_data3 0xA403 +#define i2c_m_data3_pos 0 +#define i2c_m_data3_len 8 +#define i2c_m_data3_lsb 0 +#define xd_I2C_i2c_m_data4 0xA404 +#define i2c_m_data4_pos 0 +#define i2c_m_data4_len 8 +#define i2c_m_data4_lsb 0 +#define xd_I2C_i2c_m_data5 0xA405 +#define i2c_m_data5_pos 0 +#define i2c_m_data5_len 8 +#define i2c_m_data5_lsb 0 +#define xd_I2C_i2c_m_data6 0xA406 +#define i2c_m_data6_pos 0 +#define i2c_m_data6_len 8 +#define i2c_m_data6_lsb 0 +#define xd_I2C_i2c_m_data7 0xA407 +#define i2c_m_data7_pos 0 +#define i2c_m_data7_len 8 +#define i2c_m_data7_lsb 0 +#define xd_I2C_i2c_m_data8 0xA408 +#define i2c_m_data8_pos 0 +#define i2c_m_data8_len 8 +#define i2c_m_data8_lsb 0 +#define xd_I2C_i2c_m_data9 0xA409 +#define i2c_m_data9_pos 0 +#define i2c_m_data9_len 8 +#define i2c_m_data9_lsb 0 +#define xd_I2C_i2c_m_data10 0xA40A +#define i2c_m_data10_pos 0 +#define i2c_m_data10_len 8 +#define i2c_m_data10_lsb 0 +#define xd_I2C_i2c_m_data11 0xA40B +#define i2c_m_data11_pos 0 +#define i2c_m_data11_len 8 +#define i2c_m_data11_lsb 0 +#define xd_I2C_i2c_m_cmd_rw 0xA40C +#define i2c_m_cmd_rw_pos 0 +#define i2c_m_cmd_rw_len 1 +#define i2c_m_cmd_rw_lsb 0 +#define xd_I2C_i2c_m_cmd_rwlen 0xA40C +#define i2c_m_cmd_rwlen_pos 3 +#define i2c_m_cmd_rwlen_len 4 +#define i2c_m_cmd_rwlen_lsb 0 +#define xd_I2C_i2c_m_status_cmd_exe 0xA40D +#define i2c_m_status_cmd_exe_pos 0 +#define i2c_m_status_cmd_exe_len 1 +#define i2c_m_status_cmd_exe_lsb 0 +#define xd_I2C_i2c_m_status_wdat_done 0xA40D +#define i2c_m_status_wdat_done_pos 1 +#define i2c_m_status_wdat_done_len 1 +#define i2c_m_status_wdat_done_lsb 0 +#define xd_I2C_i2c_m_status_wdat_fail 0xA40D +#define i2c_m_status_wdat_fail_pos 2 +#define i2c_m_status_wdat_fail_len 1 +#define i2c_m_status_wdat_fail_lsb 0 +#define xd_I2C_i2c_m_period 0xA40E +#define i2c_m_period_pos 0 +#define i2c_m_period_len 8 +#define i2c_m_period_lsb 0 +#define xd_I2C_i2c_m_reg_msb_lsb 0xA40F +#define i2c_m_reg_msb_lsb_pos 0 +#define i2c_m_reg_msb_lsb_len 1 +#define i2c_m_reg_msb_lsb_lsb 0 +#define xd_I2C_reg_ofdm_rst 0xA40F +#define reg_ofdm_rst_pos 1 +#define reg_ofdm_rst_len 1 +#define reg_ofdm_rst_lsb 0 +#define xd_I2C_reg_sample_period_on_tuner 0xA40F +#define reg_sample_period_on_tuner_pos 2 +#define reg_sample_period_on_tuner_len 1 +#define reg_sample_period_on_tuner_lsb 0 +#define xd_I2C_reg_rst_i2c 0xA40F +#define reg_rst_i2c_pos 3 +#define reg_rst_i2c_len 1 +#define reg_rst_i2c_lsb 0 +#define xd_I2C_reg_ofdm_rst_en 0xA40F +#define reg_ofdm_rst_en_pos 4 +#define reg_ofdm_rst_en_len 1 +#define reg_ofdm_rst_en_lsb 0 +#define xd_I2C_reg_tuner_sda_sync_on 0xA40F +#define reg_tuner_sda_sync_on_pos 5 +#define reg_tuner_sda_sync_on_len 1 +#define reg_tuner_sda_sync_on_lsb 0 +#define xd_p_mp2if_data_access_disable_ofsm 0xA500 +#define mp2if_data_access_disable_ofsm_pos 0 +#define mp2if_data_access_disable_ofsm_len 1 +#define mp2if_data_access_disable_ofsm_lsb 0 +#define xd_p_reg_mp2_sw_rst_ofsm 0xA500 +#define reg_mp2_sw_rst_ofsm_pos 1 +#define reg_mp2_sw_rst_ofsm_len 1 +#define reg_mp2_sw_rst_ofsm_lsb 0 +#define xd_p_reg_mp2if_clk_en_ofsm 0xA500 +#define reg_mp2if_clk_en_ofsm_pos 2 +#define reg_mp2if_clk_en_ofsm_len 1 +#define reg_mp2if_clk_en_ofsm_lsb 0 +#define xd_r_mp2if_sync_byte_locked 0xA500 +#define mp2if_sync_byte_locked_pos 3 +#define mp2if_sync_byte_locked_len 1 +#define mp2if_sync_byte_locked_lsb 0 +#define xd_r_mp2if_ts_not_188 0xA500 +#define mp2if_ts_not_188_pos 4 +#define mp2if_ts_not_188_len 1 +#define mp2if_ts_not_188_lsb 0 +#define xd_r_mp2if_psb_empty 0xA500 +#define mp2if_psb_empty_pos 5 +#define mp2if_psb_empty_len 1 +#define mp2if_psb_empty_lsb 0 +#define xd_r_mp2if_psb_overflow 0xA500 +#define mp2if_psb_overflow_pos 6 +#define mp2if_psb_overflow_len 1 +#define mp2if_psb_overflow_lsb 0 +#define xd_p_mp2if_keep_sf_sync_byte_ofsm 0xA500 +#define mp2if_keep_sf_sync_byte_ofsm_pos 7 +#define mp2if_keep_sf_sync_byte_ofsm_len 1 +#define mp2if_keep_sf_sync_byte_ofsm_lsb 0 +#define xd_r_mp2if_psb_mp2if_num_pkt 0xA501 +#define mp2if_psb_mp2if_num_pkt_pos 0 +#define mp2if_psb_mp2if_num_pkt_len 6 +#define mp2if_psb_mp2if_num_pkt_lsb 0 +#define xd_p_reg_mpeg_full_speed_ofsm 0xA501 +#define reg_mpeg_full_speed_ofsm_pos 6 +#define reg_mpeg_full_speed_ofsm_len 1 +#define reg_mpeg_full_speed_ofsm_lsb 0 +#define xd_p_mp2if_mpeg_ser_mode_ofsm 0xA501 +#define mp2if_mpeg_ser_mode_ofsm_pos 7 +#define mp2if_mpeg_ser_mode_ofsm_len 1 +#define mp2if_mpeg_ser_mode_ofsm_lsb 0 +#define xd_p_reg_sw_mon51 0xA600 +#define reg_sw_mon51_pos 0 +#define reg_sw_mon51_len 8 +#define reg_sw_mon51_lsb 0 +#define xd_p_reg_top_pcsel 0xA601 +#define reg_top_pcsel_pos 0 +#define reg_top_pcsel_len 1 +#define reg_top_pcsel_lsb 0 +#define xd_p_reg_top_rs232 0xA601 +#define reg_top_rs232_pos 1 +#define reg_top_rs232_len 1 +#define reg_top_rs232_lsb 0 +#define xd_p_reg_top_pcout 0xA601 +#define reg_top_pcout_pos 2 +#define reg_top_pcout_len 1 +#define reg_top_pcout_lsb 0 +#define xd_p_reg_top_debug 0xA601 +#define reg_top_debug_pos 3 +#define reg_top_debug_len 1 +#define reg_top_debug_lsb 0 +#define xd_p_reg_top_adcdly 0xA601 +#define reg_top_adcdly_pos 4 +#define reg_top_adcdly_len 2 +#define reg_top_adcdly_lsb 0 +#define xd_p_reg_top_pwrdw 0xA601 +#define reg_top_pwrdw_pos 6 +#define reg_top_pwrdw_len 1 +#define reg_top_pwrdw_lsb 0 +#define xd_p_reg_top_pwrdw_inv 0xA601 +#define reg_top_pwrdw_inv_pos 7 +#define reg_top_pwrdw_inv_len 1 +#define reg_top_pwrdw_inv_lsb 0 +#define xd_p_reg_top_int_inv 0xA602 +#define reg_top_int_inv_pos 0 +#define reg_top_int_inv_len 1 +#define reg_top_int_inv_lsb 0 +#define xd_p_reg_top_dio_sel 0xA602 +#define reg_top_dio_sel_pos 1 +#define reg_top_dio_sel_len 1 +#define reg_top_dio_sel_lsb 0 +#define xd_p_reg_top_gpioon0 0xA603 +#define reg_top_gpioon0_pos 0 +#define reg_top_gpioon0_len 1 +#define reg_top_gpioon0_lsb 0 +#define xd_p_reg_top_gpioon1 0xA603 +#define reg_top_gpioon1_pos 1 +#define reg_top_gpioon1_len 1 +#define reg_top_gpioon1_lsb 0 +#define xd_p_reg_top_gpioon2 0xA603 +#define reg_top_gpioon2_pos 2 +#define reg_top_gpioon2_len 1 +#define reg_top_gpioon2_lsb 0 +#define xd_p_reg_top_gpioon3 0xA603 +#define reg_top_gpioon3_pos 3 +#define reg_top_gpioon3_len 1 +#define reg_top_gpioon3_lsb 0 +#define xd_p_reg_top_lockon1 0xA603 +#define reg_top_lockon1_pos 4 +#define reg_top_lockon1_len 1 +#define reg_top_lockon1_lsb 0 +#define xd_p_reg_top_lockon2 0xA603 +#define reg_top_lockon2_pos 5 +#define reg_top_lockon2_len 1 +#define reg_top_lockon2_lsb 0 +#define xd_p_reg_top_gpioo0 0xA604 +#define reg_top_gpioo0_pos 0 +#define reg_top_gpioo0_len 1 +#define reg_top_gpioo0_lsb 0 +#define xd_p_reg_top_gpioo1 0xA604 +#define reg_top_gpioo1_pos 1 +#define reg_top_gpioo1_len 1 +#define reg_top_gpioo1_lsb 0 +#define xd_p_reg_top_gpioo2 0xA604 +#define reg_top_gpioo2_pos 2 +#define reg_top_gpioo2_len 1 +#define reg_top_gpioo2_lsb 0 +#define xd_p_reg_top_gpioo3 0xA604 +#define reg_top_gpioo3_pos 3 +#define reg_top_gpioo3_len 1 +#define reg_top_gpioo3_lsb 0 +#define xd_p_reg_top_lock1 0xA604 +#define reg_top_lock1_pos 4 +#define reg_top_lock1_len 1 +#define reg_top_lock1_lsb 0 +#define xd_p_reg_top_lock2 0xA604 +#define reg_top_lock2_pos 5 +#define reg_top_lock2_len 1 +#define reg_top_lock2_lsb 0 +#define xd_p_reg_top_gpioen0 0xA605 +#define reg_top_gpioen0_pos 0 +#define reg_top_gpioen0_len 1 +#define reg_top_gpioen0_lsb 0 +#define xd_p_reg_top_gpioen1 0xA605 +#define reg_top_gpioen1_pos 1 +#define reg_top_gpioen1_len 1 +#define reg_top_gpioen1_lsb 0 +#define xd_p_reg_top_gpioen2 0xA605 +#define reg_top_gpioen2_pos 2 +#define reg_top_gpioen2_len 1 +#define reg_top_gpioen2_lsb 0 +#define xd_p_reg_top_gpioen3 0xA605 +#define reg_top_gpioen3_pos 3 +#define reg_top_gpioen3_len 1 +#define reg_top_gpioen3_lsb 0 +#define xd_p_reg_top_locken1 0xA605 +#define reg_top_locken1_pos 4 +#define reg_top_locken1_len 1 +#define reg_top_locken1_lsb 0 +#define xd_p_reg_top_locken2 0xA605 +#define reg_top_locken2_pos 5 +#define reg_top_locken2_len 1 +#define reg_top_locken2_lsb 0 +#define xd_r_reg_top_gpioi0 0xA606 +#define reg_top_gpioi0_pos 0 +#define reg_top_gpioi0_len 1 +#define reg_top_gpioi0_lsb 0 +#define xd_r_reg_top_gpioi1 0xA606 +#define reg_top_gpioi1_pos 1 +#define reg_top_gpioi1_len 1 +#define reg_top_gpioi1_lsb 0 +#define xd_r_reg_top_gpioi2 0xA606 +#define reg_top_gpioi2_pos 2 +#define reg_top_gpioi2_len 1 +#define reg_top_gpioi2_lsb 0 +#define xd_r_reg_top_gpioi3 0xA606 +#define reg_top_gpioi3_pos 3 +#define reg_top_gpioi3_len 1 +#define reg_top_gpioi3_lsb 0 +#define xd_r_reg_top_locki1 0xA606 +#define reg_top_locki1_pos 4 +#define reg_top_locki1_len 1 +#define reg_top_locki1_lsb 0 +#define xd_r_reg_top_locki2 0xA606 +#define reg_top_locki2_pos 5 +#define reg_top_locki2_len 1 +#define reg_top_locki2_lsb 0 +#define xd_p_reg_dummy_7_0 0xA608 +#define reg_dummy_7_0_pos 0 +#define reg_dummy_7_0_len 8 +#define reg_dummy_7_0_lsb 0 +#define xd_p_reg_dummy_15_8 0xA609 +#define reg_dummy_15_8_pos 0 +#define reg_dummy_15_8_len 8 +#define reg_dummy_15_8_lsb 8 +#define xd_p_reg_dummy_23_16 0xA60A +#define reg_dummy_23_16_pos 0 +#define reg_dummy_23_16_len 8 +#define reg_dummy_23_16_lsb 16 +#define xd_p_reg_dummy_31_24 0xA60B +#define reg_dummy_31_24_pos 0 +#define reg_dummy_31_24_len 8 +#define reg_dummy_31_24_lsb 24 +#define xd_p_reg_dummy_39_32 0xA60C +#define reg_dummy_39_32_pos 0 +#define reg_dummy_39_32_len 8 +#define reg_dummy_39_32_lsb 32 +#define xd_p_reg_dummy_47_40 0xA60D +#define reg_dummy_47_40_pos 0 +#define reg_dummy_47_40_len 8 +#define reg_dummy_47_40_lsb 40 +#define xd_p_reg_dummy_55_48 0xA60E +#define reg_dummy_55_48_pos 0 +#define reg_dummy_55_48_len 8 +#define reg_dummy_55_48_lsb 48 +#define xd_p_reg_dummy_63_56 0xA60F +#define reg_dummy_63_56_pos 0 +#define reg_dummy_63_56_len 8 +#define reg_dummy_63_56_lsb 56 +#define xd_p_reg_dummy_71_64 0xA610 +#define reg_dummy_71_64_pos 0 +#define reg_dummy_71_64_len 8 +#define reg_dummy_71_64_lsb 64 +#define xd_p_reg_dummy_79_72 0xA611 +#define reg_dummy_79_72_pos 0 +#define reg_dummy_79_72_len 8 +#define reg_dummy_79_72_lsb 72 +#define xd_p_reg_dummy_87_80 0xA612 +#define reg_dummy_87_80_pos 0 +#define reg_dummy_87_80_len 8 +#define reg_dummy_87_80_lsb 80 +#define xd_p_reg_dummy_95_88 0xA613 +#define reg_dummy_95_88_pos 0 +#define reg_dummy_95_88_len 8 +#define reg_dummy_95_88_lsb 88 +#define xd_p_reg_dummy_103_96 0xA614 +#define reg_dummy_103_96_pos 0 +#define reg_dummy_103_96_len 8 +#define reg_dummy_103_96_lsb 96 + +#define xd_p_reg_unplug_flag 0xA615 +#define reg_unplug_flag_pos 0 +#define reg_unplug_flag_len 1 +#define reg_unplug_flag_lsb 104 + +#define xd_p_reg_api_dca_stes_request 0xA615 +#define reg_api_dca_stes_request_pos 1 +#define reg_api_dca_stes_request_len 1 +#define reg_api_dca_stes_request_lsb 0 + +#define xd_p_reg_back_to_dca_flag 0xA615 +#define reg_back_to_dca_flag_pos 2 +#define reg_back_to_dca_flag_len 1 +#define reg_back_to_dca_flag_lsb 106 + +#define xd_p_reg_api_retrain_request 0xA615 +#define reg_api_retrain_request_pos 3 +#define reg_api_retrain_request_len 1 +#define reg_api_retrain_request_lsb 0 + +#define xd_p_reg_Dyn_Top_Try_flag 0xA615 +#define reg_Dyn_Top_Try_flag_pos 3 +#define reg_Dyn_Top_Try_flag_len 1 +#define reg_Dyn_Top_Try_flag_lsb 107 + +#define xd_p_reg_API_retrain_freeze_flag 0xA615 +#define reg_API_retrain_freeze_flag_pos 4 +#define reg_API_retrain_freeze_flag_len 1 +#define reg_API_retrain_freeze_flag_lsb 108 + +#define xd_p_reg_dummy_111_104 0xA615 +#define reg_dummy_111_104_pos 0 +#define reg_dummy_111_104_len 8 +#define reg_dummy_111_104_lsb 104 +#define xd_p_reg_dummy_119_112 0xA616 +#define reg_dummy_119_112_pos 0 +#define reg_dummy_119_112_len 8 +#define reg_dummy_119_112_lsb 112 +#define xd_p_reg_dummy_127_120 0xA617 +#define reg_dummy_127_120_pos 0 +#define reg_dummy_127_120_len 8 +#define reg_dummy_127_120_lsb 120 +#define xd_p_reg_dummy_135_128 0xA618 +#define reg_dummy_135_128_pos 0 +#define reg_dummy_135_128_len 8 +#define reg_dummy_135_128_lsb 128 + +#define xd_p_reg_dummy_143_136 0xA619 +#define reg_dummy_143_136_pos 0 +#define reg_dummy_143_136_len 8 +#define reg_dummy_143_136_lsb 136 + +#define xd_p_reg_CCIR_dis 0xA619 +#define reg_CCIR_dis_pos 0 +#define reg_CCIR_dis_len 1 +#define reg_CCIR_dis_lsb 0 + +#define xd_p_reg_dummy_151_144 0xA61A +#define reg_dummy_151_144_pos 0 +#define reg_dummy_151_144_len 8 +#define reg_dummy_151_144_lsb 144 + +#define xd_p_reg_dummy_159_152 0xA61B +#define reg_dummy_159_152_pos 0 +#define reg_dummy_159_152_len 8 +#define reg_dummy_159_152_lsb 152 + +#define xd_p_reg_dummy_167_160 0xA61C +#define reg_dummy_167_160_pos 0 +#define reg_dummy_167_160_len 8 +#define reg_dummy_167_160_lsb 160 + +#define xd_p_reg_dummy_175_168 0xA61D +#define reg_dummy_175_168_pos 0 +#define reg_dummy_175_168_len 8 +#define reg_dummy_175_168_lsb 168 + +#define xd_p_reg_dummy_183_176 0xA61E +#define reg_dummy_183_176_pos 0 +#define reg_dummy_183_176_len 8 +#define reg_dummy_183_176_lsb 176 + +#define xd_p_reg_ofsm_read_rbc_en 0xA61E +#define reg_ofsm_read_rbc_en_pos 2 +#define reg_ofsm_read_rbc_en_len 1 +#define reg_ofsm_read_rbc_en_lsb 0 + +#define xd_p_reg_ce_filter_selection_dis 0xA61E +#define reg_ce_filter_selection_dis_pos 1 +#define reg_ce_filter_selection_dis_len 1 +#define reg_ce_filter_selection_dis_lsb 0 + +#define xd_p_reg_OFSM_version_control_7_0 0xA611 +#define reg_OFSM_version_control_7_0_pos 0 +#define reg_OFSM_version_control_7_0_len 8 +#define reg_OFSM_version_control_7_0_lsb 0 + +#define xd_p_reg_OFSM_version_control_15_8 0xA61F +#define reg_OFSM_version_control_15_8_pos 0 +#define reg_OFSM_version_control_15_8_len 8 +#define reg_OFSM_version_control_15_8_lsb 0 + +#define xd_p_reg_OFSM_version_control_23_16 0xA620 +#define reg_OFSM_version_control_23_16_pos 0 +#define reg_OFSM_version_control_23_16_len 8 +#define reg_OFSM_version_control_23_16_lsb 0 + +#define xd_p_reg_dummy_191_184 0xA61F +#define reg_dummy_191_184_pos 0 +#define reg_dummy_191_184_len 8 +#define reg_dummy_191_184_lsb 184 + +#define xd_p_reg_dummy_199_192 0xA620 +#define reg_dummy_199_192_pos 0 +#define reg_dummy_199_192_len 8 +#define reg_dummy_199_192_lsb 192 + +#define xd_p_reg_ce_en 0xABC0 +#define reg_ce_en_pos 0 +#define reg_ce_en_len 1 +#define reg_ce_en_lsb 0 +#define xd_p_reg_ce_fctrl_en 0xABC0 +#define reg_ce_fctrl_en_pos 1 +#define reg_ce_fctrl_en_len 1 +#define reg_ce_fctrl_en_lsb 0 +#define xd_p_reg_ce_fste_tdi 0xABC0 +#define reg_ce_fste_tdi_pos 2 +#define reg_ce_fste_tdi_len 1 +#define reg_ce_fste_tdi_lsb 0 +#define xd_p_reg_ce_dynamic 0xABC0 +#define reg_ce_dynamic_pos 3 +#define reg_ce_dynamic_len 1 +#define reg_ce_dynamic_lsb 0 +#define xd_p_reg_ce_conf 0xABC0 +#define reg_ce_conf_pos 4 +#define reg_ce_conf_len 2 +#define reg_ce_conf_lsb 0 +#define xd_p_reg_ce_dyn12 0xABC0 +#define reg_ce_dyn12_pos 6 +#define reg_ce_dyn12_len 1 +#define reg_ce_dyn12_lsb 0 +#define xd_p_reg_ce_derot_en 0xABC0 +#define reg_ce_derot_en_pos 7 +#define reg_ce_derot_en_len 1 +#define reg_ce_derot_en_lsb 0 +#define xd_p_reg_ce_dynamic_th_7_0 0xABC1 +#define reg_ce_dynamic_th_7_0_pos 0 +#define reg_ce_dynamic_th_7_0_len 8 +#define reg_ce_dynamic_th_7_0_lsb 0 +#define xd_p_reg_ce_dynamic_th_15_8 0xABC2 +#define reg_ce_dynamic_th_15_8_pos 0 +#define reg_ce_dynamic_th_15_8_len 8 +#define reg_ce_dynamic_th_15_8_lsb 8 +#define xd_p_reg_ce_s1 0xABC3 +#define reg_ce_s1_pos 0 +#define reg_ce_s1_len 5 +#define reg_ce_s1_lsb 0 +#define xd_p_reg_ce_var_forced_value 0xABC3 +#define reg_ce_var_forced_value_pos 5 +#define reg_ce_var_forced_value_len 3 +#define reg_ce_var_forced_value_lsb 0 +#define xd_p_reg_ce_data_im_7_0 0xABC4 +#define reg_ce_data_im_7_0_pos 0 +#define reg_ce_data_im_7_0_len 8 +#define reg_ce_data_im_7_0_lsb 0 +#define xd_p_reg_ce_data_im_8 0xABC5 +#define reg_ce_data_im_8_pos 0 +#define reg_ce_data_im_8_len 1 +#define reg_ce_data_im_8_lsb 0 +#define xd_p_reg_ce_data_re_6_0 0xABC5 +#define reg_ce_data_re_6_0_pos 1 +#define reg_ce_data_re_6_0_len 7 +#define reg_ce_data_re_6_0_lsb 0 +#define xd_p_reg_ce_data_re_8_7 0xABC6 +#define reg_ce_data_re_8_7_pos 0 +#define reg_ce_data_re_8_7_len 2 +#define reg_ce_data_re_8_7_lsb 7 +#define xd_p_reg_ce_tone_5_0 0xABC6 +#define reg_ce_tone_5_0_pos 2 +#define reg_ce_tone_5_0_len 6 +#define reg_ce_tone_5_0_lsb 0 +#define xd_p_reg_ce_tone_12_6 0xABC7 +#define reg_ce_tone_12_6_pos 0 +#define reg_ce_tone_12_6_len 7 +#define reg_ce_tone_12_6_lsb 6 +#define xd_p_reg_ce_centroid_drift_th 0xABC8 +#define reg_ce_centroid_drift_th_pos 0 +#define reg_ce_centroid_drift_th_len 8 +#define reg_ce_centroid_drift_th_lsb 0 +#define xd_p_reg_ce_centroid_count_max 0xABC9 +#define reg_ce_centroid_count_max_pos 0 +#define reg_ce_centroid_count_max_len 4 +#define reg_ce_centroid_count_max_lsb 0 +#define xd_p_reg_ce_centroid_bias_inc_7_0 0xABCA +#define reg_ce_centroid_bias_inc_7_0_pos 0 +#define reg_ce_centroid_bias_inc_7_0_len 8 +#define reg_ce_centroid_bias_inc_7_0_lsb 0 +#define xd_p_reg_ce_centroid_bias_inc_8 0xABCB +#define reg_ce_centroid_bias_inc_8_pos 0 +#define reg_ce_centroid_bias_inc_8_len 1 +#define reg_ce_centroid_bias_inc_8_lsb 0 +#define xd_p_reg_ce_var_th0_7_0 0xABCC +#define reg_ce_var_th0_7_0_pos 0 +#define reg_ce_var_th0_7_0_len 8 +#define reg_ce_var_th0_7_0_lsb 0 +#define xd_p_reg_ce_var_th0_15_8 0xABCD +#define reg_ce_var_th0_15_8_pos 0 +#define reg_ce_var_th0_15_8_len 8 +#define reg_ce_var_th0_15_8_lsb 8 +#define xd_p_reg_ce_var_th1_7_0 0xABCE +#define reg_ce_var_th1_7_0_pos 0 +#define reg_ce_var_th1_7_0_len 8 +#define reg_ce_var_th1_7_0_lsb 0 +#define xd_p_reg_ce_var_th1_15_8 0xABCF +#define reg_ce_var_th1_15_8_pos 0 +#define reg_ce_var_th1_15_8_len 8 +#define reg_ce_var_th1_15_8_lsb 8 +#define xd_p_reg_ce_var_th2_7_0 0xABD0 +#define reg_ce_var_th2_7_0_pos 0 +#define reg_ce_var_th2_7_0_len 8 +#define reg_ce_var_th2_7_0_lsb 0 +#define xd_p_reg_ce_var_th2_15_8 0xABD1 +#define reg_ce_var_th2_15_8_pos 0 +#define reg_ce_var_th2_15_8_len 8 +#define reg_ce_var_th2_15_8_lsb 8 +#define xd_p_reg_ce_var_th3_7_0 0xABD2 +#define reg_ce_var_th3_7_0_pos 0 +#define reg_ce_var_th3_7_0_len 8 +#define reg_ce_var_th3_7_0_lsb 0 +#define xd_p_reg_ce_var_th3_15_8 0xABD3 +#define reg_ce_var_th3_15_8_pos 0 +#define reg_ce_var_th3_15_8_len 8 +#define reg_ce_var_th3_15_8_lsb 8 +#define xd_p_reg_ce_var_th4_7_0 0xABD4 +#define reg_ce_var_th4_7_0_pos 0 +#define reg_ce_var_th4_7_0_len 8 +#define reg_ce_var_th4_7_0_lsb 0 +#define xd_p_reg_ce_var_th4_15_8 0xABD5 +#define reg_ce_var_th4_15_8_pos 0 +#define reg_ce_var_th4_15_8_len 8 +#define reg_ce_var_th4_15_8_lsb 8 +#define xd_p_reg_ce_var_th5_7_0 0xABD6 +#define reg_ce_var_th5_7_0_pos 0 +#define reg_ce_var_th5_7_0_len 8 +#define reg_ce_var_th5_7_0_lsb 0 +#define xd_p_reg_ce_var_th5_15_8 0xABD7 +#define reg_ce_var_th5_15_8_pos 0 +#define reg_ce_var_th5_15_8_len 8 +#define reg_ce_var_th5_15_8_lsb 8 +#define xd_p_reg_ce_var_th6_7_0 0xABD8 +#define reg_ce_var_th6_7_0_pos 0 +#define reg_ce_var_th6_7_0_len 8 +#define reg_ce_var_th6_7_0_lsb 0 +#define xd_p_reg_ce_var_th6_15_8 0xABD9 +#define reg_ce_var_th6_15_8_pos 0 +#define reg_ce_var_th6_15_8_len 8 +#define reg_ce_var_th6_15_8_lsb 8 +#define xd_p_reg_ce_fctrl_reset 0xABDA +#define reg_ce_fctrl_reset_pos 0 +#define reg_ce_fctrl_reset_len 1 +#define reg_ce_fctrl_reset_lsb 0 +#define xd_p_reg_ce_cent_auto_clr_en 0xABDA +#define reg_ce_cent_auto_clr_en_pos 1 +#define reg_ce_cent_auto_clr_en_len 1 +#define reg_ce_cent_auto_clr_en_lsb 0 +#define xd_p_reg_ce_fctrl_auto_reset_en 0xABDA +#define reg_ce_fctrl_auto_reset_en_pos 2 +#define reg_ce_fctrl_auto_reset_en_len 1 +#define reg_ce_fctrl_auto_reset_en_lsb 0 +#define xd_p_reg_ce_var_forced_en 0xABDA +#define reg_ce_var_forced_en_pos 3 +#define reg_ce_var_forced_en_len 1 +#define reg_ce_var_forced_en_lsb 0 +#define xd_p_reg_ce_cent_forced_en 0xABDA +#define reg_ce_cent_forced_en_pos 4 +#define reg_ce_cent_forced_en_len 1 +#define reg_ce_cent_forced_en_lsb 0 +#define xd_p_reg_ce_var_max 0xABDA +#define reg_ce_var_max_pos 5 +#define reg_ce_var_max_len 3 +#define reg_ce_var_max_lsb 0 +#define xd_p_reg_ce_cent_forced_value_7_0 0xABDB +#define reg_ce_cent_forced_value_7_0_pos 0 +#define reg_ce_cent_forced_value_7_0_len 8 +#define reg_ce_cent_forced_value_7_0_lsb 0 +#define xd_p_reg_ce_cent_forced_value_11_8 0xABDC +#define reg_ce_cent_forced_value_11_8_pos 0 +#define reg_ce_cent_forced_value_11_8_len 4 +#define reg_ce_cent_forced_value_11_8_lsb 8 +#define xd_p_reg_ce_fctrl_rd 0xABDD +#define reg_ce_fctrl_rd_pos 0 +#define reg_ce_fctrl_rd_len 1 +#define reg_ce_fctrl_rd_lsb 0 +#define xd_p_reg_ce_centroid_max_6_0 0xABDD +#define reg_ce_centroid_max_6_0_pos 1 +#define reg_ce_centroid_max_6_0_len 7 +#define reg_ce_centroid_max_6_0_lsb 0 +#define xd_p_reg_ce_centroid_max_11_7 0xABDE +#define reg_ce_centroid_max_11_7_pos 0 +#define reg_ce_centroid_max_11_7_len 5 +#define reg_ce_centroid_max_11_7_lsb 7 +#define xd_p_reg_ce_var 0xABDF +#define reg_ce_var_pos 0 +#define reg_ce_var_len 3 +#define reg_ce_var_lsb 0 +#define xd_p_reg_ce_fctrl_rdy 0xABDF +#define reg_ce_fctrl_rdy_pos 3 +#define reg_ce_fctrl_rdy_len 1 +#define reg_ce_fctrl_rdy_lsb 0 +#define xd_p_reg_ce_centroid_out_3_0 0xABDF +#define reg_ce_centroid_out_3_0_pos 4 +#define reg_ce_centroid_out_3_0_len 4 +#define reg_ce_centroid_out_3_0_lsb 0 +#define xd_p_reg_ce_centroid_out_11_4 0xABE0 +#define reg_ce_centroid_out_11_4_pos 0 +#define reg_ce_centroid_out_11_4_len 8 +#define reg_ce_centroid_out_11_4_lsb 4 +#define xd_p_reg_ce_bias_7_0 0xABE1 +#define reg_ce_bias_7_0_pos 0 +#define reg_ce_bias_7_0_len 8 +#define reg_ce_bias_7_0_lsb 0 +#define xd_p_reg_ce_bias_11_8 0xABE2 +#define reg_ce_bias_11_8_pos 0 +#define reg_ce_bias_11_8_len 4 +#define reg_ce_bias_11_8_lsb 8 +#define xd_p_reg_ce_m1_3_0 0xABE2 +#define reg_ce_m1_3_0_pos 4 +#define reg_ce_m1_3_0_len 4 +#define reg_ce_m1_3_0_lsb 0 +#define xd_p_reg_ce_m1_11_4 0xABE3 +#define reg_ce_m1_11_4_pos 0 +#define reg_ce_m1_11_4_len 8 +#define reg_ce_m1_11_4_lsb 4 +#define xd_p_reg_ce_rh0_7_0 0xABE4 +#define reg_ce_rh0_7_0_pos 0 +#define reg_ce_rh0_7_0_len 8 +#define reg_ce_rh0_7_0_lsb 0 +#define xd_p_reg_ce_rh0_15_8 0xABE5 +#define reg_ce_rh0_15_8_pos 0 +#define reg_ce_rh0_15_8_len 8 +#define reg_ce_rh0_15_8_lsb 8 +#define xd_p_reg_ce_rh0_23_16 0xABE6 +#define reg_ce_rh0_23_16_pos 0 +#define reg_ce_rh0_23_16_len 8 +#define reg_ce_rh0_23_16_lsb 16 +#define xd_p_reg_ce_rh0_31_24 0xABE7 +#define reg_ce_rh0_31_24_pos 0 +#define reg_ce_rh0_31_24_len 8 +#define reg_ce_rh0_31_24_lsb 24 +#define xd_p_reg_ce_rh3_real_7_0 0xABE8 +#define reg_ce_rh3_real_7_0_pos 0 +#define reg_ce_rh3_real_7_0_len 8 +#define reg_ce_rh3_real_7_0_lsb 0 +#define xd_p_reg_ce_rh3_real_15_8 0xABE9 +#define reg_ce_rh3_real_15_8_pos 0 +#define reg_ce_rh3_real_15_8_len 8 +#define reg_ce_rh3_real_15_8_lsb 8 +#define xd_p_reg_ce_rh3_real_23_16 0xABEA +#define reg_ce_rh3_real_23_16_pos 0 +#define reg_ce_rh3_real_23_16_len 8 +#define reg_ce_rh3_real_23_16_lsb 16 +#define xd_p_reg_ce_rh3_real_31_24 0xABEB +#define reg_ce_rh3_real_31_24_pos 0 +#define reg_ce_rh3_real_31_24_len 8 +#define reg_ce_rh3_real_31_24_lsb 24 +#define xd_p_reg_ce_rh3_imag_7_0 0xABEC +#define reg_ce_rh3_imag_7_0_pos 0 +#define reg_ce_rh3_imag_7_0_len 8 +#define reg_ce_rh3_imag_7_0_lsb 0 +#define xd_p_reg_ce_rh3_imag_15_8 0xABED +#define reg_ce_rh3_imag_15_8_pos 0 +#define reg_ce_rh3_imag_15_8_len 8 +#define reg_ce_rh3_imag_15_8_lsb 8 +#define xd_p_reg_ce_rh3_imag_23_16 0xABEE +#define reg_ce_rh3_imag_23_16_pos 0 +#define reg_ce_rh3_imag_23_16_len 8 +#define reg_ce_rh3_imag_23_16_lsb 16 +#define xd_p_reg_ce_rh3_imag_31_24 0xABEF +#define reg_ce_rh3_imag_31_24_pos 0 +#define reg_ce_rh3_imag_31_24_len 8 +#define reg_ce_rh3_imag_31_24_lsb 24 +#define xd_p_reg_feq_fix_eh2_7_0 0xABF0 +#define reg_feq_fix_eh2_7_0_pos 0 +#define reg_feq_fix_eh2_7_0_len 8 +#define reg_feq_fix_eh2_7_0_lsb 0 +#define xd_p_reg_feq_fix_eh2_15_8 0xABF1 +#define reg_feq_fix_eh2_15_8_pos 0 +#define reg_feq_fix_eh2_15_8_len 8 +#define reg_feq_fix_eh2_15_8_lsb 8 +#define xd_p_reg_feq_fix_eh2_23_16 0xABF2 +#define reg_feq_fix_eh2_23_16_pos 0 +#define reg_feq_fix_eh2_23_16_len 8 +#define reg_feq_fix_eh2_23_16_lsb 16 +#define xd_p_reg_feq_fix_eh2_31_24 0xABF3 +#define reg_feq_fix_eh2_31_24_pos 0 +#define reg_feq_fix_eh2_31_24_len 8 +#define reg_feq_fix_eh2_31_24_lsb 24 +#define xd_p_reg_ce_m2_central_7_0 0xABF4 +#define reg_ce_m2_central_7_0_pos 0 +#define reg_ce_m2_central_7_0_len 8 +#define reg_ce_m2_central_7_0_lsb 0 +#define xd_p_reg_ce_m2_central_15_8 0xABF5 +#define reg_ce_m2_central_15_8_pos 0 +#define reg_ce_m2_central_15_8_len 8 +#define reg_ce_m2_central_15_8_lsb 8 +#define xd_p_reg_ce_fftshift 0xABF6 +#define reg_ce_fftshift_pos 0 +#define reg_ce_fftshift_len 4 +#define reg_ce_fftshift_lsb 0 +#define xd_p_reg_ce_fftshift1 0xABF6 +#define reg_ce_fftshift1_pos 4 +#define reg_ce_fftshift1_len 4 +#define reg_ce_fftshift1_lsb 0 +#define xd_p_reg_ce_fftshift2 0xABF7 +#define reg_ce_fftshift2_pos 0 +#define reg_ce_fftshift2_len 4 +#define reg_ce_fftshift2_lsb 0 +#define xd_p_reg_ce_top_mobile 0xABF7 +#define reg_ce_top_mobile_pos 4 +#define reg_ce_top_mobile_len 1 +#define reg_ce_top_mobile_lsb 0 +#define xd_p_reg_strong_sginal_detected 0xA2BC +#define reg_strong_sginal_detected_pos 2 +#define reg_strong_sginal_detected_len 1 +#define reg_strong_sginal_detected_lsb 0 + +#define XD_MP2IF_BASE 0xB000 +#define XD_MP2IF_CSR (0x00 + XD_MP2IF_BASE) +#define XD_MP2IF_DMX_CTRL (0x03 + XD_MP2IF_BASE) +#define XD_MP2IF_PID_IDX (0x04 + XD_MP2IF_BASE) +#define XD_MP2IF_PID_DATA_L (0x05 + XD_MP2IF_BASE) +#define XD_MP2IF_PID_DATA_H (0x06 + XD_MP2IF_BASE) +#define XD_MP2IF_MISC (0x07 + XD_MP2IF_BASE) + +extern struct dvb_frontend *af9005_fe_attach(struct dvb_usb_device *d); +extern int af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, + u8 * value); +extern int af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len); +extern int af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, + u8 value); +extern int af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len); +extern int af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, + u8 addr, u8 * values, int len); +extern int af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len); +extern int af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, + u8 pos, u8 len, u8 * value); +extern int af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, + u8 pos, u8 len, u8 value); +extern int af9005_send_command(struct dvb_usb_device *d, u8 command, + u8 * wbuf, int wlen, u8 * rbuf, int rlen); +extern int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, + u8 * values, int len); +extern int af9005_tuner_attach(struct dvb_usb_adapter *adap); +extern int af9005_led_control(struct dvb_usb_device *d, int onoff); + +extern u8 regmask[8]; + +/* remote control decoder */ +extern int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, + u32 * event, int *state); +extern struct rc_map_table rc_map_af9005_table[]; +extern int rc_map_af9005_table_size; + +#endif diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c new file mode 100644 index 000000000000..5e45ae605427 --- /dev/null +++ b/drivers/media/usb/dvb-usb/az6027.c @@ -0,0 +1,1182 @@ +/* DVB USB compliant Linux driver for the AZUREWAVE DVB-S/S2 USB2.0 (AZ6027) + * receiver. + * + * Copyright (C) 2009 Adams.Xu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "az6027.h" + +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" + +#include "stb6100.h" +#include "stb6100_cfg.h" +#include "dvb_ca_en50221.h" + +int dvb_usb_az6027_debug; +module_param_named(debug, dvb_usb_az6027_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct az6027_device_state { + struct dvb_ca_en50221 ca; + struct mutex ca_mutex; + u8 power_state; +}; + +static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = { + + /* 0x0000000b, SYSREG */ + { STB0899_DEV_ID , 0x30 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x99 }, + { STB0899_DISF22RX , 0xa8 }, + /* SYSREG ? */ + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0xfe }, + { STB0899_IRQSTATUS_2 , 0x03 }, + { STB0899_IRQSTATUS_1 , 0x7c }, + { STB0899_IRQSTATUS_0 , 0xf4 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x58 }, + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x33 }, + { STB0899_IOPVALUE3 , 0x6d }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x60 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x01 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x01 }, + { STB0899_AGC1REF , 0x10 }, + { STB0899_RTC , 0x23 }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x34 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xf7 }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xf1 }, + { STB0899_LDT2 , 0xe3 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfd }, + { STB0899_QDCCOMP , 0xff }, + { STB0899_POWERI , 0x0c }, + { STB0899_POWERQ , 0x0f }, + { STB0899_RCOMP , 0x6c }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x06 }, + { STB0899_AGC2I2 , 0x00 }, + { STB0899_TLIR , 0x30 }, + { STB0899_RTF , 0x7f }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xbc }, + { STB0899_CFRM , 0xea }, + { STB0899_CFRL , 0x31 }, + { STB0899_NIRM , 0x2b }, + { STB0899_NIRL , 0x80 }, + { STB0899_ISYMB , 0x1d }, + { STB0899_QSYMB , 0xa6 }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0x02 }, + { STB0899_EQUAQ1 , 0xff }, + { STB0899_EQUAI2 , 0x04 }, + { STB0899_EQUAQ2 , 0x05 }, + { STB0899_EQUAI3 , 0x02 }, + { STB0899_EQUAQ3 , 0xfd }, + { STB0899_EQUAI4 , 0x03 }, + { STB0899_EQUAQ4 , 0x07 }, + { STB0899_EQUAI5 , 0x08 }, + { STB0899_EQUAQ5 , 0xf5 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0x86 }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x0a }, + { STB0899_ECNT3L , 0xad }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xb0 }, + { STB0899_VTH23 , 0x7a }, + { STB0899_VTH34 , 0x58 }, + { STB0899_VTH56 , 0x38 }, + { STB0899_VTH67 , 0x34 }, + { STB0899_VTH78 , 0x24 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x41 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x00 }, + { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x1b }, + { STB0899_TSLLSTKL , 0xb3 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0x00 }, + { STB0899_PCKLENUL , 0xbc }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xbd }, + { STB0899_TSSTATUS , 0x90 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x95 }, + { STB0899_ERRCTRL3 , 0x8d }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x19 }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0xf0 }, + { STB0899_MATSTRL , 0x02 }, + { STB0899_UPLSTRM , 0x45 }, + { STB0899_UPLSTRL , 0x60 }, + { STB0899_DFLSTRM , 0xe3 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x47 }, + { STB0899_SYNCDSTRM , 0x05 }, + { STB0899_SYNCDSTRL , 0x18 }, + { STB0899_CFGPDELSTATUS1 , 0x19 }, + { STB0899_CFGPDELSTATUS2 , 0x2b }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x01 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + + + +struct stb0899_config az6027_stb0899_config = { + .init_dev = az6027_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = az6027_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .demod_address = 0xd0, /* 0x68, 0xd0 >> 1 */ + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_ON, /* 1 */ + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL, +}; + +struct stb6100_config az6027_stb6100_config = { + .tuner_address = 0xc0, + .refclock = 27000000, +}; + + +/* check for mutex FIXME */ +int az6027_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) +{ + int ret = -1; + if (mutex_lock_interruptible(&d->usb_mutex)) + return -EAGAIN; + + ret = usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_IN, + value, + index, + b, + blen, + 2000); + + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else + ret = 0; + + deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index); + debug_dump(b, blen, deb_xfer); + + mutex_unlock(&d->usb_mutex); + return ret; +} + +static int az6027_usb_out_op(struct dvb_usb_device *d, + u8 req, + u16 value, + u16 index, + u8 *b, + int blen) +{ + int ret; + + deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index); + debug_dump(b, blen, deb_xfer); + + if (mutex_lock_interruptible(&d->usb_mutex)) + return -EAGAIN; + + ret = usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_OUT, + value, + index, + b, + blen, + 2000); + + if (ret != blen) { + warn("usb out operation failed. (%d)", ret); + mutex_unlock(&d->usb_mutex); + return -EIO; + } else{ + mutex_unlock(&d->usb_mutex); + return 0; + } +} + +static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + deb_info("%s %d", __func__, onoff); + + req = 0xBC; + value = onoff; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + warn("usb out operation failed. (%d)", ret); + + return ret; +} + +/* keys for the enclosed remote control */ +static struct rc_map_table rc_map_az6027_table[] = { + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, +}; + +/* remote control stuff (does not work with my box) */ +static int az6027_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + return 0; +} + +/* +int az6027_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 v = onoff; + return az6027_usb_out_op(d,0xBC,v,3,NULL,1); +} +*/ + +static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC1; + value = address; + index = 0; + blen = 1; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + ret = b[0]; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6027_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + deb_info("%s %d", __func__, slot); + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC2; + value1 = address; + index = value; + blen = 0; + + ret = az6027_usb_out_op(d, req, value1, index, NULL, blen); + if (ret != 0) + warn("usb out operation failed. (%d)", ret); + + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_read_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC3; + value = address; + index = 0; + blen = 2; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + if (b[0] == 0) + warn("Read CI IO error"); + + ret = b[1]; + deb_info("read cam data = %x from 0x%x", b[1], value); + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6027_ci_write_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC4; + value1 = address; + index = value; + blen = 0; + + ret = az6027_usb_out_op(d, req, value1, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + req = 0xC8; + value = 0; + index = 0; + blen = 1; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else{ + ret = b[0]; + } + kfree(b); + return ret; +} + +static int az6027_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret, i; + u8 req; + u16 value; + u16 index; + int blen; + + mutex_lock(&state->ca_mutex); + + req = 0xC6; + value = 1; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + msleep(500); + req = 0xC6; + value = 0; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + for (i = 0; i < 15; i++) { + msleep(100); + + if (CI_CamReady(ca, slot)) { + deb_info("CAM Ready"); + break; + } + } + msleep(5000); + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return 0; +} + +static int az6027_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + deb_info("%s", __func__); + mutex_lock(&state->ca_mutex); + req = 0xC7; + value = 1; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + mutex_lock(&state->ca_mutex); + + req = 0xC5; + value = 0; + index = 0; + blen = 1; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else + ret = 0; + + if (!ret && b[0] == 1) { + ret = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + + +static void az6027_ci_uninit(struct dvb_usb_device *d) +{ + struct az6027_device_state *state; + + deb_info("%s", __func__); + + if (NULL == d) + return; + + state = (struct az6027_device_state *)d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + + +static int az6027_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + int ret; + + deb_info("%s", __func__); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = az6027_ci_read_attribute_mem; + state->ca.write_attribute_mem = az6027_ci_write_attribute_mem; + state->ca.read_cam_control = az6027_ci_read_cam_control; + state->ca.write_cam_control = az6027_ci_write_cam_control; + state->ca.slot_reset = az6027_ci_slot_reset; + state->ca.slot_shutdown = az6027_ci_slot_shutdown; + state->ca.slot_ts_enable = az6027_ci_slot_ts_enable; + state->ca.poll_slot_status = az6027_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + 0, /* flags */ + 1);/* n_slots */ + if (ret != 0) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + deb_info("CI initialized."); + + return 0; +} + +/* +static int az6027_read_mac_addr(struct dvb_usb_device *d, u8 mac[6]) +{ + az6027_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6); + return 0; +} +*/ + +static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + + u8 buf; + struct dvb_usb_adapter *adap = fe->dvb->priv; + + struct i2c_msg i2c_msg = { + .addr = 0x99, + .flags = 0, + .buf = &buf, + .len = 1 + }; + + /* + * 2 --18v + * 1 --13v + * 0 --off + */ + switch (voltage) { + case SEC_VOLTAGE_13: + buf = 1; + i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + + case SEC_VOLTAGE_18: + buf = 2; + i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + + case SEC_VOLTAGE_OFF: + buf = 0; + i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + + default: + return -EINVAL; + } + return 0; +} + + +static int az6027_frontend_poweron(struct dvb_usb_adapter *adap) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + req = 0xBC; + value = 1; /* power on */ + index = 3; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + return 0; +} +static int az6027_frontend_reset(struct dvb_usb_adapter *adap) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + /* reset demodulator */ + req = 0xC0; + value = 1; /* high */ + index = 3; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + req = 0xC0; + value = 0; /* low */ + index = 3; + blen = 0; + msleep_interruptible(200); + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + msleep_interruptible(200); + + req = 0xC0; + value = 1; /*high */ + index = 3; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + msleep_interruptible(200); + return 0; +} + +static int az6027_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + /* TS passthrough */ + req = 0xC7; + value = onoff; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + return 0; +} + +static int az6027_frontend_attach(struct dvb_usb_adapter *adap) +{ + + az6027_frontend_poweron(adap); + az6027_frontend_reset(adap); + + deb_info("adap = %p, dev = %p\n", adap, adap->dev); + adap->fe_adap[0].fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap); + + if (adap->fe_adap[0].fe) { + deb_info("found STB0899 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb0899_config.demod_address); + if (stb6100_attach(adap->fe_adap[0].fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) { + deb_info("found STB6100 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb6100_config.tuner_address); + adap->fe_adap[0].fe->ops.set_voltage = az6027_set_voltage; + az6027_ci_init(adap); + } else { + adap->fe_adap[0].fe = NULL; + } + } else + warn("no front-end attached\n"); + + az6027_frontend_tsbypass(adap, 0); + + return 0; +} + +static struct dvb_usb_device_properties az6027_properties; + +static void az6027_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + az6027_ci_uninit(d); + dvb_usb_device_exit(intf); +} + + +static int az6027_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, + &az6027_properties, + THIS_MODULE, + NULL, + adapter_nr); +} + +/* I2C */ +static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0, j = 0, len = 0; + u16 index; + u16 value; + int length; + u8 req; + u8 *data; + + data = kmalloc(256, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) { + kfree(data); + return -EAGAIN; + } + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + + if (msg[i].addr == 0x99) { + req = 0xBE; + index = 0; + value = msg[i].buf[0] & 0x00ff; + length = 1; + az6027_usb_out_op(d, req, value, index, data, length); + } + + if (msg[i].addr == 0xd0) { + /* write/read request */ + if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { + req = 0xB9; + index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); + value = msg[i].addr + (msg[i].len << 8); + length = msg[i + 1].len + 6; + az6027_usb_in_op(d, req, value, index, data, length); + len = msg[i + 1].len; + for (j = 0; j < len; j++) + msg[i + 1].buf[j] = data[j + 5]; + + i++; + } else { + + /* demod 16bit addr */ + req = 0xBD; + index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); + value = msg[i].addr + (2 << 8); + length = msg[i].len - 2; + len = msg[i].len - 2; + for (j = 0; j < len; j++) + data[j] = msg[i].buf[j + 2]; + az6027_usb_out_op(d, req, value, index, data, length); + } + } + + if (msg[i].addr == 0xc0) { + if (msg[i].flags & I2C_M_RD) { + + req = 0xB9; + index = 0x0; + value = msg[i].addr; + length = msg[i].len + 6; + az6027_usb_in_op(d, req, value, index, data, length); + len = msg[i].len; + for (j = 0; j < len; j++) + msg[i].buf[j] = data[j + 5]; + + } else { + + req = 0xBD; + index = msg[i].buf[0] & 0x00FF; + value = msg[i].addr + (1 << 8); + length = msg[i].len - 1; + len = msg[i].len - 1; + + for (j = 0; j < len; j++) + data[j] = msg[i].buf[j + 1]; + + az6027_usb_out_op(d, req, value, index, data, length); + } + } + } + mutex_unlock(&d->i2c_mutex); + kfree(data); + + return i; +} + + +static u32 az6027_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm az6027_i2c_algo = { + .master_xfer = az6027_i2c_xfer, + .functionality = az6027_i2c_func, +}; + +int az6027_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + u8 *b; + s16 ret; + + b = kmalloc(16, GFP_KERNEL); + if (!b) + return -ENOMEM; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0xb7, + USB_TYPE_VENDOR | USB_DIR_IN, + 6, + 0, + b, + 6, + USB_CTRL_GET_TIMEOUT); + + *cold = ret <= 0; + kfree(b); + deb_info("cold: %d\n", *cold); + return 0; +} + + +static struct usb_device_id az6027_usb_table[] = { + { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_AZ6027) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI_V1) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI_V2) }, + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V1) }, + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V2) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT) }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, az6027_usb_table); + +static struct dvb_usb_device_properties az6027_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-az6027-03.fw", + .no_reconnect = 1, + + .size_of_priv = sizeof(struct az6027_device_state), + .identify_state = az6027_identify_state, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = az6027_streaming_ctrl, + .frontend_attach = az6027_frontend_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, +/* + .power_ctrl = az6027_power_ctrl, + .read_mac_address = az6027_read_mac_addr, + */ + .rc.legacy = { + .rc_map_table = rc_map_az6027_table, + .rc_map_size = ARRAY_SIZE(rc_map_az6027_table), + .rc_interval = 400, + .rc_query = az6027_rc_query, + }, + + .i2c_algo = &az6027_i2c_algo, + + .num_device_descs = 6, + .devices = { + { + .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", + .cold_ids = { &az6027_usb_table[0], NULL }, + .warm_ids = { NULL }, + }, { + .name = "TERRATEC S7", + .cold_ids = { &az6027_usb_table[1], NULL }, + .warm_ids = { NULL }, + }, { + .name = "TERRATEC S7 MKII", + .cold_ids = { &az6027_usb_table[2], NULL }, + .warm_ids = { NULL }, + }, { + .name = "Technisat SkyStar USB 2 HD CI", + .cold_ids = { &az6027_usb_table[3], NULL }, + .warm_ids = { NULL }, + }, { + .name = "Technisat SkyStar USB 2 HD CI", + .cold_ids = { &az6027_usb_table[4], NULL }, + .warm_ids = { NULL }, + }, { + .name = "Elgato EyeTV Sat", + .cold_ids = { &az6027_usb_table[5], NULL }, + .warm_ids = { NULL }, + }, + { NULL }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver az6027_usb_driver = { + .name = "dvb_usb_az6027", + .probe = az6027_usb_probe, + .disconnect = az6027_usb_disconnect, + .id_table = az6027_usb_table, +}; + +module_usb_driver(az6027_usb_driver); + +MODULE_AUTHOR("Adams Xu "); +MODULE_DESCRIPTION("Driver for AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/az6027.h b/drivers/media/usb/dvb-usb/az6027.h new file mode 100644 index 000000000000..f3afe17f3f3d --- /dev/null +++ b/drivers/media/usb/dvb-usb/az6027.h @@ -0,0 +1,14 @@ +#ifndef _DVB_USB_VP6027_H_ +#define _DVB_USB_VP6027_H_ + +#define DVB_USB_LOG_PREFIX "az6027" +#include "dvb-usb.h" + + +extern int dvb_usb_az6027_debug; +#define deb_info(args...) dprintk(dvb_usb_az6027_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_az6027_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_az6027_debug, 0x04, args) +#define deb_fe(args...) dprintk(dvb_usb_az6027_debug, 0x08, args) + +#endif diff --git a/drivers/media/usb/dvb-usb/cinergyT2-core.c b/drivers/media/usb/dvb-usb/cinergyT2-core.c new file mode 100644 index 000000000000..0a98548ecd17 --- /dev/null +++ b/drivers/media/usb/dvb-usb/cinergyT2-core.c @@ -0,0 +1,254 @@ +/* + * TerraTec Cinergy T2/qanu USB2 DVB-T adapter. + * + * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi) + * + * Based on the dvb-usb-framework code and the + * original Terratec Cinergy T2 driver by: + * + * Copyright (C) 2004 Daniel Mack and + * Holger Waechtler + * + * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf + * + * This program is free software; 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 "cinergyT2.h" + + +/* debug */ +int dvb_usb_cinergyt2_debug; + +module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 " + "(or-able))."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct cinergyt2_state { + u8 rc_counter; +}; + +/* We are missing a release hook with usb_device data */ +static struct dvb_usb_device *cinergyt2_usb_device; + +static struct dvb_usb_device_properties cinergyt2_properties; + +static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable) +{ + char buf[] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 }; + char result[64]; + return dvb_usb_generic_rw(adap->dev, buf, sizeof(buf), result, + sizeof(result), 0); +} + +static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable) +{ + char buf[] = { CINERGYT2_EP1_SLEEP_MODE, enable ? 0 : 1 }; + char state[3]; + return dvb_usb_generic_rw(d, buf, sizeof(buf), state, sizeof(state), 0); +} + +static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) +{ + char query[] = { CINERGYT2_EP1_GET_FIRMWARE_VERSION }; + char state[3]; + int ret; + + adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev); + + ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state, + sizeof(state), 0); + if (ret < 0) { + deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep " + "state info\n"); + } + + /* Copy this pointer as we are gonna need it in the release phase */ + cinergyt2_usb_device = adap->dev; + + return 0; +} + +static struct rc_map_table rc_map_cinergyt2_table[] = { + { 0x0401, KEY_POWER }, + { 0x0402, KEY_1 }, + { 0x0403, KEY_2 }, + { 0x0404, KEY_3 }, + { 0x0405, KEY_4 }, + { 0x0406, KEY_5 }, + { 0x0407, KEY_6 }, + { 0x0408, KEY_7 }, + { 0x0409, KEY_8 }, + { 0x040a, KEY_9 }, + { 0x040c, KEY_0 }, + { 0x040b, KEY_VIDEO }, + { 0x040d, KEY_REFRESH }, + { 0x040e, KEY_SELECT }, + { 0x040f, KEY_EPG }, + { 0x0410, KEY_UP }, + { 0x0414, KEY_DOWN }, + { 0x0411, KEY_LEFT }, + { 0x0413, KEY_RIGHT }, + { 0x0412, KEY_OK }, + { 0x0415, KEY_TEXT }, + { 0x0416, KEY_INFO }, + { 0x0417, KEY_RED }, + { 0x0418, KEY_GREEN }, + { 0x0419, KEY_YELLOW }, + { 0x041a, KEY_BLUE }, + { 0x041c, KEY_VOLUMEUP }, + { 0x041e, KEY_VOLUMEDOWN }, + { 0x041d, KEY_MUTE }, + { 0x041b, KEY_CHANNELUP }, + { 0x041f, KEY_CHANNELDOWN }, + { 0x0440, KEY_PAUSE }, + { 0x044c, KEY_PLAY }, + { 0x0458, KEY_RECORD }, + { 0x0454, KEY_PREVIOUS }, + { 0x0448, KEY_STOP }, + { 0x045c, KEY_NEXT } +}; + +/* Number of keypresses to ignore before detect repeating */ +#define RC_REPEAT_DELAY 3 + +static int repeatable_keys[] = { + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_CHANNELUP, + KEY_CHANNELDOWN +}; + +static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct cinergyt2_state *st = d->priv; + u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS; + int i; + + *state = REMOTE_NO_KEY_PRESSED; + + dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0); + if (key[4] == 0xff) { + /* key repeat */ + st->rc_counter++; + if (st->rc_counter > RC_REPEAT_DELAY) { + for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) { + if (d->last_event == repeatable_keys[i]) { + *state = REMOTE_KEY_REPEAT; + *event = d->last_event; + deb_rc("repeat key, event %x\n", + *event); + return 0; + } + } + deb_rc("repeated key (non repeatable)\n"); + } + return 0; + } + + /* hack to pass checksum on the custom field */ + key[2] = ~key[1]; + dvb_usb_nec_rc_key_to_event(d, key, event, state); + if (key[0] != 0) { + if (*event != d->last_event) + st->rc_counter = 0; + + deb_rc("key: %x %x %x %x %x\n", + key[0], key[1], key[2], key[3], key[4]); + } + return 0; +} + +static int cinergyt2_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &cinergyt2_properties, + THIS_MODULE, NULL, adapter_nr); +} + + +static struct usb_device_id cinergyt2_usb_table[] = { + { USB_DEVICE(USB_VID_TERRATEC, 0x0038) }, + { 0 } +}; + +MODULE_DEVICE_TABLE(usb, cinergyt2_usb_table); + +static struct dvb_usb_device_properties cinergyt2_properties = { + .size_of_priv = sizeof(struct cinergyt2_state), + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cinergyt2_streaming_ctrl, + .frontend_attach = cinergyt2_frontend_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + }}, + } + }, + + .power_ctrl = cinergyt2_power_ctrl, + + .rc.legacy = { + .rc_interval = 50, + .rc_map_table = rc_map_cinergyt2_table, + .rc_map_size = ARRAY_SIZE(rc_map_cinergyt2_table), + .rc_query = cinergyt2_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 1, + + .num_device_descs = 1, + .devices = { + { .name = "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver", + .cold_ids = {NULL}, + .warm_ids = { &cinergyt2_usb_table[0], NULL }, + }, + { NULL }, + } +}; + + +static struct usb_driver cinergyt2_driver = { + .name = "cinergyT2", + .probe = cinergyt2_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = cinergyt2_usb_table +}; + +module_usb_driver(cinergyt2_driver); + +MODULE_DESCRIPTION("Terratec Cinergy T2 DVB-T driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomi Orava"); diff --git a/drivers/media/usb/dvb-usb/cinergyT2-fe.c b/drivers/media/usb/dvb-usb/cinergyT2-fe.c new file mode 100644 index 000000000000..1efc028a76c9 --- /dev/null +++ b/drivers/media/usb/dvb-usb/cinergyT2-fe.c @@ -0,0 +1,356 @@ +/* + * TerraTec Cinergy T2/qanu USB2 DVB-T adapter. + * + * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi) + * + * Based on the dvb-usb-framework code and the + * original Terratec Cinergy T2 driver by: + * + * Copyright (C) 2004 Daniel Mack and + * Holger Waechtler + * + * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf + * + * This program is free software; 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 "cinergyT2.h" + + +/** + * convert linux-dvb frontend parameter set into TPS. + * See ETSI ETS-300744, section 4.6.2, table 9 for details. + * + * This function is probably reusable and may better get placed in a support + * library. + * + * We replace errornous fields by default TPS fields (the ones with value 0). + */ + +static uint16_t compute_tps(struct dtv_frontend_properties *op) +{ + uint16_t tps = 0; + + switch (op->code_rate_HP) { + case FEC_2_3: + tps |= (1 << 7); + break; + case FEC_3_4: + tps |= (2 << 7); + break; + case FEC_5_6: + tps |= (3 << 7); + break; + case FEC_7_8: + tps |= (4 << 7); + break; + case FEC_1_2: + case FEC_AUTO: + default: + /* tps |= (0 << 7) */; + } + + switch (op->code_rate_LP) { + case FEC_2_3: + tps |= (1 << 4); + break; + case FEC_3_4: + tps |= (2 << 4); + break; + case FEC_5_6: + tps |= (3 << 4); + break; + case FEC_7_8: + tps |= (4 << 4); + break; + case FEC_1_2: + case FEC_AUTO: + default: + /* tps |= (0 << 4) */; + } + + switch (op->modulation) { + case QAM_16: + tps |= (1 << 13); + break; + case QAM_64: + tps |= (2 << 13); + break; + case QPSK: + default: + /* tps |= (0 << 13) */; + } + + switch (op->transmission_mode) { + case TRANSMISSION_MODE_8K: + tps |= (1 << 0); + break; + case TRANSMISSION_MODE_2K: + default: + /* tps |= (0 << 0) */; + } + + switch (op->guard_interval) { + case GUARD_INTERVAL_1_16: + tps |= (1 << 2); + break; + case GUARD_INTERVAL_1_8: + tps |= (2 << 2); + break; + case GUARD_INTERVAL_1_4: + tps |= (3 << 2); + break; + case GUARD_INTERVAL_1_32: + default: + /* tps |= (0 << 2) */; + } + + switch (op->hierarchy) { + case HIERARCHY_1: + tps |= (1 << 10); + break; + case HIERARCHY_2: + tps |= (2 << 10); + break; + case HIERARCHY_4: + tps |= (3 << 10); + break; + case HIERARCHY_NONE: + default: + /* tps |= (0 << 10) */; + } + + return tps; +} + +struct cinergyt2_fe_state { + struct dvb_frontend fe; + struct dvb_usb_device *d; +}; + +static int cinergyt2_fe_read_status(struct dvb_frontend *fe, + fe_status_t *status) +{ + struct cinergyt2_fe_state *state = fe->demodulator_priv; + struct dvbt_get_status_msg result; + u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; + int ret; + + ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result, + sizeof(result), 0); + if (ret < 0) + return ret; + + *status = 0; + + if (0xffff - le16_to_cpu(result.gain) > 30) + *status |= FE_HAS_SIGNAL; + if (result.lock_bits & (1 << 6)) + *status |= FE_HAS_LOCK; + if (result.lock_bits & (1 << 5)) + *status |= FE_HAS_SYNC; + if (result.lock_bits & (1 << 4)) + *status |= FE_HAS_CARRIER; + if (result.lock_bits & (1 << 1)) + *status |= FE_HAS_VITERBI; + + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; + + return 0; +} + +static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cinergyt2_fe_state *state = fe->demodulator_priv; + struct dvbt_get_status_msg status; + char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; + int ret; + + ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, + sizeof(status), 0); + if (ret < 0) + return ret; + + *ber = le32_to_cpu(status.viterbi_error_rate); + return 0; +} + +static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + struct cinergyt2_fe_state *state = fe->demodulator_priv; + struct dvbt_get_status_msg status; + u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; + int ret; + + ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status, + sizeof(status), 0); + if (ret < 0) { + err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n", + ret); + return ret; + } + *unc = le32_to_cpu(status.uncorrected_block_count); + return 0; +} + +static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct cinergyt2_fe_state *state = fe->demodulator_priv; + struct dvbt_get_status_msg status; + char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; + int ret; + + ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, + sizeof(status), 0); + if (ret < 0) { + err("cinergyt2_fe_read_signal_strength() Failed!" + " (Error=%d)\n", ret); + return ret; + } + *strength = (0xffff - le16_to_cpu(status.gain)); + return 0; +} + +static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct cinergyt2_fe_state *state = fe->demodulator_priv; + struct dvbt_get_status_msg status; + char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; + int ret; + + ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, + sizeof(status), 0); + if (ret < 0) { + err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret); + return ret; + } + *snr = (status.snr << 8) | status.snr; + return 0; +} + +static int cinergyt2_fe_init(struct dvb_frontend *fe) +{ + return 0; +} + +static int cinergyt2_fe_sleep(struct dvb_frontend *fe) +{ + deb_info("cinergyt2_fe_sleep() Called\n"); + return 0; +} + +static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct cinergyt2_fe_state *state = fe->demodulator_priv; + struct dvbt_set_parameters_msg param; + char result[2]; + int err; + + param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; + param.tps = cpu_to_le16(compute_tps(fep)); + param.freq = cpu_to_le32(fep->frequency / 1000); + param.flags = 0; + + switch (fep->bandwidth_hz) { + default: + case 8000000: + param.bandwidth = 8; + break; + case 7000000: + param.bandwidth = 7; + break; + case 6000000: + param.bandwidth = 6; + break; + } + + err = dvb_usb_generic_rw(state->d, + (char *)¶m, sizeof(param), + result, sizeof(result), 0); + if (err < 0) + err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err); + + return (err < 0) ? err : 0; +} + +static void cinergyt2_fe_release(struct dvb_frontend *fe) +{ + struct cinergyt2_fe_state *state = fe->demodulator_priv; + if (state != NULL) + kfree(state); +} + +static struct dvb_frontend_ops cinergyt2_fe_ops; + +struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d) +{ + struct cinergyt2_fe_state *s = kzalloc(sizeof( + struct cinergyt2_fe_state), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->d = d; + memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops)); + s->fe.demodulator_priv = s; + return &s->fe; +} + + +static struct dvb_frontend_ops cinergyt2_fe_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = DRIVER_NAME, + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 166667, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 + | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 + | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 + | FE_CAN_FEC_AUTO | FE_CAN_QPSK + | FE_CAN_QAM_16 | FE_CAN_QAM_64 + | FE_CAN_QAM_AUTO + | FE_CAN_TRANSMISSION_MODE_AUTO + | FE_CAN_GUARD_INTERVAL_AUTO + | FE_CAN_HIERARCHY_AUTO + | FE_CAN_RECOVER + | FE_CAN_MUTE_TS + }, + + .release = cinergyt2_fe_release, + + .init = cinergyt2_fe_init, + .sleep = cinergyt2_fe_sleep, + + .set_frontend = cinergyt2_fe_set_frontend, + .get_tune_settings = cinergyt2_fe_get_tune_settings, + + .read_status = cinergyt2_fe_read_status, + .read_ber = cinergyt2_fe_read_ber, + .read_signal_strength = cinergyt2_fe_read_signal_strength, + .read_snr = cinergyt2_fe_read_snr, + .read_ucblocks = cinergyt2_fe_read_unc_blocks, +}; diff --git a/drivers/media/usb/dvb-usb/cinergyT2.h b/drivers/media/usb/dvb-usb/cinergyT2.h new file mode 100644 index 000000000000..84efe03771eb --- /dev/null +++ b/drivers/media/usb/dvb-usb/cinergyT2.h @@ -0,0 +1,95 @@ +/* + * TerraTec Cinergy T2/qanu USB2 DVB-T adapter. + * + * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi) + * + * Based on the dvb-usb-framework code and the + * original Terratec Cinergy T2 driver by: + * + * Copyright (C) 2004 Daniel Mack and + * Holger Waechtler + * + * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf + * + * This program is free software; 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_USB_CINERGYT2_H_ +#define _DVB_USB_CINERGYT2_H_ + +#include + +#define DVB_USB_LOG_PREFIX "cinergyT2" +#include "dvb-usb.h" + +#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver" + +extern int dvb_usb_cinergyt2_debug; + +#define deb_info(args...) dprintk(dvb_usb_cinergyt2_debug, 0x001, args) +#define deb_xfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x002, args) +#define deb_pll(args...) dprintk(dvb_usb_cinergyt2_debug, 0x004, args) +#define deb_ts(args...) dprintk(dvb_usb_cinergyt2_debug, 0x008, args) +#define deb_err(args...) dprintk(dvb_usb_cinergyt2_debug, 0x010, args) +#define deb_rc(args...) dprintk(dvb_usb_cinergyt2_debug, 0x020, args) +#define deb_fw(args...) dprintk(dvb_usb_cinergyt2_debug, 0x040, args) +#define deb_mem(args...) dprintk(dvb_usb_cinergyt2_debug, 0x080, args) +#define deb_uxfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x100, args) + + + +enum cinergyt2_ep1_cmd { + CINERGYT2_EP1_PID_TABLE_RESET = 0x01, + CINERGYT2_EP1_PID_SETUP = 0x02, + CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03, + CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04, + CINERGYT2_EP1_GET_TUNER_STATUS = 0x05, + CINERGYT2_EP1_START_SCAN = 0x06, + CINERGYT2_EP1_CONTINUE_SCAN = 0x07, + CINERGYT2_EP1_GET_RC_EVENTS = 0x08, + CINERGYT2_EP1_SLEEP_MODE = 0x09, + CINERGYT2_EP1_GET_FIRMWARE_VERSION = 0x0A +}; + + +struct dvbt_get_status_msg { + uint32_t freq; + uint8_t bandwidth; + uint16_t tps; + uint8_t flags; + __le16 gain; + uint8_t snr; + __le32 viterbi_error_rate; + uint32_t rs_error_rate; + __le32 uncorrected_block_count; + uint8_t lock_bits; + uint8_t prev_lock_bits; +} __attribute__((packed)); + + +struct dvbt_set_parameters_msg { + uint8_t cmd; + __le32 freq; + uint8_t bandwidth; + __le16 tps; + uint8_t flags; +} __attribute__((packed)); + + +extern struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d); + +#endif /* _DVB_USB_CINERGYT2_H_ */ + diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c new file mode 100644 index 000000000000..3940bb0f9ef6 --- /dev/null +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -0,0 +1,2043 @@ +/* DVB USB compliant linux driver for Conexant USB reference design. + * + * The Conexant reference design I saw on their website was only for analogue + * capturing (using the cx25842). The box I took to write this driver (reverse + * engineered) is the one labeled Medion MD95700. In addition to the cx25842 + * for analogue capturing it also has a cx22702 DVB-T demodulator on the main + * board. Besides it has a atiremote (X10) and a USB2.0 hub onboard. + * + * Maybe it is a little bit premature to call this driver cxusb, but I assume + * the USB protocol is identical or at least inherited from the reference + * design, so it can be reused for the "analogue-only" device (if it will + * appear at all). + * + * TODO: Use the cx25840-driver for the analogue part + * + * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) + * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org) + * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include +#include +#include + +#include "cxusb.h" + +#include "cx22702.h" +#include "lgdt330x.h" +#include "mt352.h" +#include "mt352_priv.h" +#include "zl10353.h" +#include "tuner-xc2028.h" +#include "tuner-simple.h" +#include "mxl5005s.h" +#include "max2165.h" +#include "dib7000p.h" +#include "dib0070.h" +#include "lgs8gxx.h" +#include "atbm8830.h" + +/* debug */ +static int dvb_usb_cxusb_debug; +module_param_named(debug, dvb_usb_cxusb_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define deb_info(args...) dprintk(dvb_usb_cxusb_debug, 0x03, args) +#define deb_i2c(args...) dprintk(dvb_usb_cxusb_debug, 0x02, args) + +static int cxusb_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 sndbuf[1+wlen]; + memset(sndbuf, 0, 1+wlen); + + sndbuf[0] = cmd; + memcpy(&sndbuf[1], wbuf, wlen); + if (wo) + return dvb_usb_generic_write(d, sndbuf, 1+wlen); + else + return dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0); +} + +/* GPIO */ +static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) +{ + struct cxusb_state *st = d->priv; + u8 o[2], i; + + if (st->gpio_write_state[GPIO_TUNER] == onoff) + return; + + o[0] = GPIO_TUNER; + o[1] = onoff; + cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); + + if (i != 0x01) + deb_info("gpio_write failed.\n"); + + st->gpio_write_state[GPIO_TUNER] = onoff; +} + +static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask, + u8 newval) +{ + u8 o[2], gpio_state; + int rc; + + o[0] = 0xff & ~changemask; /* mask of bits to keep */ + o[1] = newval & changemask; /* new values for bits */ + + rc = cxusb_ctrl_msg(d, CMD_BLUEBIRD_GPIO_RW, o, 2, &gpio_state, 1); + if (rc < 0 || (gpio_state & changemask) != (newval & changemask)) + deb_info("bluebird_gpio_write failed.\n"); + + return rc < 0 ? rc : gpio_state; +} + +static void cxusb_bluebird_gpio_pulse(struct dvb_usb_device *d, u8 pin, int low) +{ + cxusb_bluebird_gpio_rw(d, pin, low ? 0 : pin); + msleep(5); + cxusb_bluebird_gpio_rw(d, pin, low ? pin : 0); +} + +static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff) +{ + cxusb_bluebird_gpio_rw(d, 0x40, onoff ? 0 : 0x40); +} + +static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, + u8 addr, int onoff) +{ + u8 o[2] = {addr, onoff}; + u8 i; + int rc; + + rc = cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); + + if (rc < 0) + return rc; + if (i == 0x01) + return 0; + else { + deb_info("gpio_write failed.\n"); + return -EIO; + } +} + +/* I2C */ +static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + + if (d->udev->descriptor.idVendor == USB_VID_MEDION) + switch (msg[i].addr) { + case 0x63: + cxusb_gpio_tuner(d, 0); + break; + default: + cxusb_gpio_tuner(d, 1); + break; + } + + if (msg[i].flags & I2C_M_RD) { + /* read only */ + u8 obuf[3], ibuf[1+msg[i].len]; + obuf[0] = 0; + obuf[1] = msg[i].len; + obuf[2] = msg[i].addr; + if (cxusb_ctrl_msg(d, CMD_I2C_READ, + obuf, 3, + ibuf, 1+msg[i].len) < 0) { + warn("i2c read failed"); + break; + } + memcpy(msg[i].buf, &ibuf[1], msg[i].len); + } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) && + msg[i].addr == msg[i+1].addr) { + /* write to then read from same address */ + u8 obuf[3+msg[i].len], ibuf[1+msg[i+1].len]; + obuf[0] = msg[i].len; + obuf[1] = msg[i+1].len; + obuf[2] = msg[i].addr; + memcpy(&obuf[3], msg[i].buf, msg[i].len); + + if (cxusb_ctrl_msg(d, CMD_I2C_READ, + obuf, 3+msg[i].len, + ibuf, 1+msg[i+1].len) < 0) + break; + + if (ibuf[0] != 0x08) + deb_i2c("i2c read may have failed\n"); + + memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); + + i++; + } else { + /* write only */ + u8 obuf[2+msg[i].len], ibuf; + obuf[0] = msg[i].addr; + obuf[1] = msg[i].len; + memcpy(&obuf[2], msg[i].buf, msg[i].len); + + if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf, + 2+msg[i].len, &ibuf,1) < 0) + break; + if (ibuf != 0x08) + deb_i2c("i2c write may have failed\n"); + } + } + + mutex_unlock(&d->i2c_mutex); + return i == num ? num : -EREMOTEIO; +} + +static u32 cxusb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm cxusb_i2c_algo = { + .master_xfer = cxusb_i2c_xfer, + .functionality = cxusb_i2c_func, +}; + +static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = 0; + if (onoff) + return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); + else + return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0); +} + +static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + if (!onoff) + return cxusb_ctrl_msg(d, CMD_POWER_OFF, NULL, 0, NULL, 0); + if (d->state == DVB_USB_STATE_INIT && + usb_set_interface(d->udev, 0, 0) < 0) + err("set interface failed"); + do {} while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); + if (!ret) { + /* FIXME: We don't know why, but we need to configure the + * lgdt3303 with the register settings below on resume */ + int i; + u8 buf, bufs[] = { + 0x0e, 0x2, 0x00, 0x7f, + 0x0e, 0x2, 0x02, 0xfe, + 0x0e, 0x2, 0x02, 0x01, + 0x0e, 0x2, 0x00, 0x03, + 0x0e, 0x2, 0x0d, 0x40, + 0x0e, 0x2, 0x0e, 0x87, + 0x0e, 0x2, 0x0f, 0x8e, + 0x0e, 0x2, 0x10, 0x01, + 0x0e, 0x2, 0x14, 0xd7, + 0x0e, 0x2, 0x47, 0x88, + }; + msleep(20); + for (i = 0; i < sizeof(bufs)/sizeof(u8); i += 4/sizeof(u8)) { + ret = cxusb_ctrl_msg(d, CMD_I2C_WRITE, + bufs+i, 4, &buf, 1); + if (ret) + break; + if (buf != 0x8) + return -EREMOTEIO; + } + } + return ret; +} + +static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = 0; + if (onoff) + return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); + else + return 0; +} + +static int cxusb_nano2_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int rc = 0; + + rc = cxusb_power_ctrl(d, onoff); + if (!onoff) + cxusb_nano2_led(d, 0); + + return rc; +} + +static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + u8 b; + ret = cxusb_power_ctrl(d, onoff); + if (!onoff) + return ret; + + msleep(128); + cxusb_ctrl_msg(d, CMD_DIGITAL, NULL, 0, &b, 1); + msleep(100); + return ret; +} + +static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + u8 buf[2] = { 0x03, 0x00 }; + if (onoff) + cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, buf, 2, NULL, 0); + else + cxusb_ctrl_msg(adap->dev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + + return 0; +} + +static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + if (onoff) + cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_ON, NULL, 0, NULL, 0); + else + cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_OFF, + NULL, 0, NULL, 0); + return 0; +} + +static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d) +{ + int ep = d->props.generic_bulk_ctrl_endpoint; + const int timeout = 100; + const int junk_len = 32; + u8 *junk; + int rd_count; + + /* Discard remaining data in video pipe */ + junk = kmalloc(junk_len, GFP_KERNEL); + if (!junk) + return; + while (1) { + if (usb_bulk_msg(d->udev, + usb_rcvbulkpipe(d->udev, ep), + junk, junk_len, &rd_count, timeout) < 0) + break; + if (!rd_count) + break; + } + kfree(junk); +} + +static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) +{ + struct usb_data_stream_properties *p = &d->props.adapter[0].fe[0].stream; + const int timeout = 100; + const int junk_len = p->u.bulk.buffersize; + u8 *junk; + int rd_count; + + /* Discard remaining data in video pipe */ + junk = kmalloc(junk_len, GFP_KERNEL); + if (!junk) + return; + while (1) { + if (usb_bulk_msg(d->udev, + usb_rcvbulkpipe(d->udev, p->endpoint), + junk, junk_len, &rd_count, timeout) < 0) + break; + if (!rd_count) + break; + } + kfree(junk); +} + +static int cxusb_d680_dmb_streaming_ctrl( + struct dvb_usb_adapter *adap, int onoff) +{ + if (onoff) { + u8 buf[2] = { 0x03, 0x00 }; + cxusb_d680_dmb_drain_video(adap->dev); + return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, + buf, sizeof(buf), NULL, 0); + } else { + int ret = cxusb_ctrl_msg(adap->dev, + CMD_STREAMING_OFF, NULL, 0, NULL, 0); + return ret; + } +} + +static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; + u8 ircode[4]; + int i; + + cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4); + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { + if (rc5_custom(&keymap[i]) == ircode[2] && + rc5_data(&keymap[i]) == ircode[3]) { + *event = keymap[i].keycode; + *state = REMOTE_KEY_PRESSED; + + return 0; + } + } + + return 0; +} + +static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event, + int *state) +{ + struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; + u8 ircode[4]; + int i; + struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, + .buf = ircode, .len = 4 }; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1) + return 0; + + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { + if (rc5_custom(&keymap[i]) == ircode[1] && + rc5_data(&keymap[i]) == ircode[2]) { + *event = keymap[i].keycode; + *state = REMOTE_KEY_PRESSED; + + return 0; + } + } + + return 0; +} + +static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, + int *state) +{ + struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; + u8 ircode[2]; + int i; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + if (cxusb_ctrl_msg(d, 0x10, NULL, 0, ircode, 2) < 0) + return 0; + + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { + if (rc5_custom(&keymap[i]) == ircode[0] && + rc5_data(&keymap[i]) == ircode[1]) { + *event = keymap[i].keycode; + *state = REMOTE_KEY_PRESSED; + + return 0; + } + } + + return 0; +} + +static struct rc_map_table rc_map_dvico_mce_table[] = { + { 0xfe02, KEY_TV }, + { 0xfe0e, KEY_MP3 }, + { 0xfe1a, KEY_DVD }, + { 0xfe1e, KEY_FAVORITES }, + { 0xfe16, KEY_SETUP }, + { 0xfe46, KEY_POWER2 }, + { 0xfe0a, KEY_EPG }, + { 0xfe49, KEY_BACK }, + { 0xfe4d, KEY_MENU }, + { 0xfe51, KEY_UP }, + { 0xfe5b, KEY_LEFT }, + { 0xfe5f, KEY_RIGHT }, + { 0xfe53, KEY_DOWN }, + { 0xfe5e, KEY_OK }, + { 0xfe59, KEY_INFO }, + { 0xfe55, KEY_TAB }, + { 0xfe0f, KEY_PREVIOUSSONG },/* Replay */ + { 0xfe12, KEY_NEXTSONG }, /* Skip */ + { 0xfe42, KEY_ENTER }, /* Windows/Start */ + { 0xfe15, KEY_VOLUMEUP }, + { 0xfe05, KEY_VOLUMEDOWN }, + { 0xfe11, KEY_CHANNELUP }, + { 0xfe09, KEY_CHANNELDOWN }, + { 0xfe52, KEY_CAMERA }, + { 0xfe5a, KEY_TUNER }, /* Live */ + { 0xfe19, KEY_OPEN }, + { 0xfe0b, KEY_1 }, + { 0xfe17, KEY_2 }, + { 0xfe1b, KEY_3 }, + { 0xfe07, KEY_4 }, + { 0xfe50, KEY_5 }, + { 0xfe54, KEY_6 }, + { 0xfe48, KEY_7 }, + { 0xfe4c, KEY_8 }, + { 0xfe58, KEY_9 }, + { 0xfe13, KEY_ANGLE }, /* Aspect */ + { 0xfe03, KEY_0 }, + { 0xfe1f, KEY_ZOOM }, + { 0xfe43, KEY_REWIND }, + { 0xfe47, KEY_PLAYPAUSE }, + { 0xfe4f, KEY_FASTFORWARD }, + { 0xfe57, KEY_MUTE }, + { 0xfe0d, KEY_STOP }, + { 0xfe01, KEY_RECORD }, + { 0xfe4e, KEY_POWER }, +}; + +static struct rc_map_table rc_map_dvico_portable_table[] = { + { 0xfc02, KEY_SETUP }, /* Profile */ + { 0xfc43, KEY_POWER2 }, + { 0xfc06, KEY_EPG }, + { 0xfc5a, KEY_BACK }, + { 0xfc05, KEY_MENU }, + { 0xfc47, KEY_INFO }, + { 0xfc01, KEY_TAB }, + { 0xfc42, KEY_PREVIOUSSONG },/* Replay */ + { 0xfc49, KEY_VOLUMEUP }, + { 0xfc09, KEY_VOLUMEDOWN }, + { 0xfc54, KEY_CHANNELUP }, + { 0xfc0b, KEY_CHANNELDOWN }, + { 0xfc16, KEY_CAMERA }, + { 0xfc40, KEY_TUNER }, /* ATV/DTV */ + { 0xfc45, KEY_OPEN }, + { 0xfc19, KEY_1 }, + { 0xfc18, KEY_2 }, + { 0xfc1b, KEY_3 }, + { 0xfc1a, KEY_4 }, + { 0xfc58, KEY_5 }, + { 0xfc59, KEY_6 }, + { 0xfc15, KEY_7 }, + { 0xfc14, KEY_8 }, + { 0xfc17, KEY_9 }, + { 0xfc44, KEY_ANGLE }, /* Aspect */ + { 0xfc55, KEY_0 }, + { 0xfc07, KEY_ZOOM }, + { 0xfc0a, KEY_REWIND }, + { 0xfc08, KEY_PLAYPAUSE }, + { 0xfc4b, KEY_FASTFORWARD }, + { 0xfc5b, KEY_MUTE }, + { 0xfc04, KEY_STOP }, + { 0xfc56, KEY_RECORD }, + { 0xfc57, KEY_POWER }, + { 0xfc41, KEY_UNKNOWN }, /* INPUT */ + { 0xfc00, KEY_UNKNOWN }, /* HD */ +}; + +static struct rc_map_table rc_map_d680_dmb_table[] = { + { 0x0038, KEY_UNKNOWN }, /* TV/AV */ + { 0x080c, KEY_ZOOM }, + { 0x0800, KEY_0 }, + { 0x0001, KEY_1 }, + { 0x0802, KEY_2 }, + { 0x0003, KEY_3 }, + { 0x0804, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0806, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0808, KEY_8 }, + { 0x0009, KEY_9 }, + { 0x000a, KEY_MUTE }, + { 0x0829, KEY_BACK }, + { 0x0012, KEY_CHANNELUP }, + { 0x0813, KEY_CHANNELDOWN }, + { 0x002b, KEY_VOLUMEUP }, + { 0x082c, KEY_VOLUMEDOWN }, + { 0x0020, KEY_UP }, + { 0x0821, KEY_DOWN }, + { 0x0011, KEY_LEFT }, + { 0x0810, KEY_RIGHT }, + { 0x000d, KEY_OK }, + { 0x081f, KEY_RECORD }, + { 0x0017, KEY_PLAYPAUSE }, + { 0x0816, KEY_PLAYPAUSE }, + { 0x000b, KEY_STOP }, + { 0x0827, KEY_FASTFORWARD }, + { 0x0026, KEY_REWIND }, + { 0x081e, KEY_UNKNOWN }, /* Time Shift */ + { 0x000e, KEY_UNKNOWN }, /* Snapshot */ + { 0x082d, KEY_UNKNOWN }, /* Mouse Cursor */ + { 0x000f, KEY_UNKNOWN }, /* Minimize/Maximize */ + { 0x0814, KEY_UNKNOWN }, /* Shuffle */ + { 0x0025, KEY_POWER }, +}; + +static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + +static int cxusb_mt352_demod_init(struct dvb_frontend* fe) +{ /* used in both lgz201 and th7579 */ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x29 }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; + static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + return 0; +} + +static struct cx22702_config cxusb_cx22702_config = { + .demod_address = 0x63, + .output_mode = CX22702_PARALLEL_OUTPUT, +}; + +static struct lgdt330x_config cxusb_lgdt3303_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, +}; + +static struct lgdt330x_config cxusb_aver_lgdt3303_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .clock_polarity_flip = 2, +}; + +static struct mt352_config cxusb_dee1601_config = { + .demod_address = 0x0f, + .demod_init = cxusb_dee1601_demod_init, +}; + +static struct zl10353_config cxusb_zl10353_dee1601_config = { + .demod_address = 0x0f, + .parallel_ts = 1, +}; + +static struct mt352_config cxusb_mt352_config = { + /* used in both lgz201 and th7579 */ + .demod_address = 0x0f, + .demod_init = cxusb_mt352_demod_init, +}; + +static struct zl10353_config cxusb_zl10353_xc3028_config = { + .demod_address = 0x0f, + .if2 = 45600, + .no_tuner = 1, + .parallel_ts = 1, +}; + +static struct zl10353_config cxusb_zl10353_xc3028_config_no_i2c_gate = { + .demod_address = 0x0f, + .if2 = 45600, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + +static struct mt352_config cxusb_mt352_xc3028_config = { + .demod_address = 0x0f, + .if2 = 4560, + .no_tuner = 1, + .demod_init = cxusb_mt352_demod_init, +}; + +/* FIXME: needs tweaking */ +static struct mxl5005s_config aver_a868r_tuner = { + .i2c_address = 0x63, + .if_freq = 6000000UL, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_C, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +/* FIXME: needs tweaking */ +static struct mxl5005s_config d680_dmb_tuner = { + .i2c_address = 0x63, + .if_freq = 36125000UL, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_C, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static struct max2165_config mygica_d689_max2165_cfg = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + +/* Callbacks for DVB USB */ +static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216ME_MK3); + return 0; +} + +static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, + NULL, DVB_PLL_THOMSON_DTT7579); + return 0; +} + +static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201); + return 0; +} + +static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, + NULL, DVB_PLL_THOMSON_DTT7579); + return 0; +} + +static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0x61, TUNER_LG_TDVS_H06XF); + return 0; +} + +static int dvico_bluebird_xc2028_callback(void *ptr, int component, + int command, int arg) +{ + struct dvb_usb_adapter *adap = ptr; + struct dvb_usb_device *d = adap->dev; + + switch (command) { + case XC2028_TUNER_RESET: + deb_info("%s: XC2028_TUNER_RESET %d\n", __func__, arg); + cxusb_bluebird_gpio_pulse(d, 0x01, 1); + break; + case XC2028_RESET_CLK: + deb_info("%s: XC2028_RESET_CLK %d\n", __func__, arg); + break; + default: + deb_info("%s: unknown command %d, arg %d\n", __func__, + command, arg); + return -EINVAL; + } + + return 0; +} + +static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &adap->dev->i2c_adap, + .i2c_addr = 0x61, + }; + static struct xc2028_ctrl ctl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = XC3028_FE_ZARLINK456, + }; + + /* FIXME: generalize & move to common area */ + adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback; + + fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg); + if (fe == NULL || fe->ops.tuner_ops.set_config == NULL) + return -EIO; + + fe->ops.tuner_ops.set_config(fe, &ctl); + + return 0; +} + +static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &aver_a868r_tuner); + return 0; +} + +static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe; + fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &d680_dmb_tuner); + return (fe == NULL) ? -EIO : 0; +} + +static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe; + fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &mygica_d689_max2165_cfg); + return (fe == NULL) ? -EIO : 0; +} + +static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) +{ + u8 b; + if (usb_set_interface(adap->dev->udev, 0, 6) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1); + + adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + return -EIO; +} + +static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev, 0, 7) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); + + adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, + &cxusb_lgdt3303_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + return -EIO; +} + +static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) +{ + adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe != NULL) + return 0; + + return -EIO; +} + +static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap) +{ + /* used in both lgz201 and th7579 */ + if (usb_set_interface(adap->dev->udev, 0, 0) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); + + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + return -EIO; +} + +static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev, 0, 0) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); + + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, + &cxusb_zl10353_dee1601_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + return -EIO; +} + +static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) +{ + u8 ircode[4]; + int i; + struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, + .buf = ircode, .len = 4 }; + + if (usb_set_interface(adap->dev->udev, 0, 1) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); + + /* reset the tuner and demodulator */ + cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0); + cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1); + cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); + + adap->fe_adap[0].fe = + dvb_attach(zl10353_attach, + &cxusb_zl10353_xc3028_config_no_i2c_gate, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) + return -EIO; + + /* try to determine if there is no IR decoder on the I2C bus */ + for (i = 0; adap->dev->props.rc.legacy.rc_map_table != NULL && i < 5; i++) { + msleep(20); + if (cxusb_i2c_xfer(&adap->dev->i2c_adap, &msg, 1) != 1) + goto no_IR; + if (ircode[0] == 0 && ircode[1] == 0) + continue; + if (ircode[2] + ircode[3] != 0xff) { +no_IR: + adap->dev->props.rc.legacy.rc_map_table = NULL; + info("No IR receiver detected on this device."); + break; + } + } + + return 0; +} + +static struct dibx000_agc_config dib7070_agc_config = { + .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, + + /* + * P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 + */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + .inv_gain = 600, + .time_stabiliz = 10, + .alpha_level = 0, + .thlock = 118, + .wbd_inv = 0, + .wbd_ref = 3530, + .wbd_sel = 1, + .wbd_alpha = 5, + .agc1_max = 65535, + .agc1_min = 0, + .agc2_max = 65535, + .agc2_min = 0, + .agc1_pt1 = 0, + .agc1_pt2 = 40, + .agc1_pt3 = 183, + .agc1_slope1 = 206, + .agc1_slope2 = 255, + .agc2_pt1 = 72, + .agc2_pt2 = 152, + .agc2_slope1 = 88, + .agc2_slope2 = 90, + .alpha_mant = 17, + .alpha_exp = 27, + .beta_mant = 23, + .beta_exp = 51, + .perform_agc_softsplit = 0, +}; + +static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { + .internal = 60000, + .sampling = 15000, + .pll_prediv = 1, + .pll_ratio = 20, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + /* refsel, sel, freq_15k */ + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = (0 << 25) | 0, + .timf = 20452225, + .xtal_hz = 12000000, +}; + +static struct dib7000p_config cxusb_dualdig4_rev2_config = { + .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &dib7070_agc_config, + .bw = &dib7070_bw_config_12_mhz, + .tuner_is_baseband = 1, + .spur_protect = 1, + + .gpio_dir = 0xfcef, + .gpio_val = 0x0110, + + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, +}; + +static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev, 0, 1) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); + + cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + &cxusb_dualdig4_rev2_config) < 0) { + printk(KERN_WARNING "Unable to enumerate dib7000p\n"); + return -ENODEV; + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + &cxusb_dualdig4_rev2_config); + if (adap->fe_adap[0].fe == NULL) + return -EIO; + + return 0; +} + +static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + return dib7000p_set_gpio(fe, 8, 0, !onoff); +} + +static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return 0; +} + +static struct dib0070_config dib7070p_dib0070_config = { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, +}; + +struct dib0700_adapter_state { + int (*set_param_save) (struct dvb_frontend *); +}; + +static int dib7070_set_param_override(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset; + u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + switch (band) { + case BAND_VHF: offset = 950; break; + default: + case BAND_UHF: offset = 550; break; + } + + dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + + return state->set_param_save(fe); +} + +static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = + dib7000p_get_i2c_master(adap->fe_adap[0].fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, + &dib7070p_dib0070_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override; + return 0; +} + +static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev, 0, 1) < 0) + err("set interface failed"); + + cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); + + /* reset the tuner and demodulator */ + cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0); + cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1); + cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); + + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, + &cxusb_zl10353_xc3028_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + adap->fe_adap[0].fe = dvb_attach(mt352_attach, + &cxusb_mt352_xc3028_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) + return 0; + + return -EIO; +} + +static struct lgs8gxx_config d680_lgs8gl5_cfg = { + .prod = LGS8GXX_PROD_LGS8GL5, + .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 0, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 5725, /* 5.725 MHz */ + .if_neg_center = 0, + .ext_adc = 0, + .adc_signed = 0, + .if_neg_edge = 0, +}; + +static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + int n; + + /* Select required USB configuration */ + if (usb_set_interface(d->udev, 0, 0) < 0) + err("set interface failed"); + + /* Unblock all USB pipes */ + usb_clear_halt(d->udev, + usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); + + /* Drain USB pipes to avoid hang after reboot */ + for (n = 0; n < 5; n++) { + cxusb_d680_dmb_drain_message(d); + cxusb_d680_dmb_drain_video(d); + msleep(200); + } + + /* Reset the tuner */ + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { + err("clear tuner gpio failed"); + return -EIO; + } + msleep(100); + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) { + err("set tuner gpio failed"); + return -EIO; + } + msleep(100); + + /* Attach frontend */ + adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); + if (adap->fe_adap[0].fe == NULL) + return -EIO; + + return 0; +} + +static struct atbm8830_config mygica_d689_atbm8830_cfg = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x40, + .serial_ts = 0, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, + .agc_min = 0x2E, + .agc_max = 0x90, + .agc_hold_loop = 0, +}; + +static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + + /* Select required USB configuration */ + if (usb_set_interface(d->udev, 0, 0) < 0) + err("set interface failed"); + + /* Unblock all USB pipes */ + usb_clear_halt(d->udev, + usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); + + + /* Reset the tuner */ + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { + err("clear tuner gpio failed"); + return -EIO; + } + msleep(100); + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) { + err("set tuner gpio failed"); + return -EIO; + } + msleep(100); + + /* Attach frontend */ + adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, + &d->i2c_adap); + if (adap->fe_adap[0].fe == NULL) + return -EIO; + + return 0; +} + +/* + * DViCO has shipped two devices with the same USB ID, but only one of them + * needs a firmware download. Check the device class details to see if they + * have non-default values to decide whether the device is actually cold or + * not, and forget a match if it turns out we selected the wrong device. + */ +static int bluebird_fx2_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int wascold = *cold; + + *cold = udev->descriptor.bDeviceClass == 0xff && + udev->descriptor.bDeviceSubClass == 0xff && + udev->descriptor.bDeviceProtocol == 0xff; + + if (*cold && !wascold) + *desc = NULL; + + return 0; +} + +/* + * DViCO bluebird firmware needs the "warm" product ID to be patched into the + * firmware file before download. + */ + +static const int dvico_firmware_id_offsets[] = { 6638, 3204 }; +static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, + const struct firmware *fw) +{ + int pos; + + for (pos = 0; pos < ARRAY_SIZE(dvico_firmware_id_offsets); pos++) { + int idoff = dvico_firmware_id_offsets[pos]; + + if (fw->size < idoff + 4) + continue; + + if (fw->data[idoff] == (USB_VID_DVICO & 0xff) && + fw->data[idoff + 1] == USB_VID_DVICO >> 8) { + struct firmware new_fw; + u8 *new_fw_data = vmalloc(fw->size); + int ret; + + if (!new_fw_data) + return -ENOMEM; + + memcpy(new_fw_data, fw->data, fw->size); + new_fw.size = fw->size; + new_fw.data = new_fw_data; + + new_fw_data[idoff + 2] = + le16_to_cpu(udev->descriptor.idProduct) + 1; + new_fw_data[idoff + 3] = + le16_to_cpu(udev->descriptor.idProduct) >> 8; + + ret = usb_cypress_load_firmware(udev, &new_fw, + CYPRESS_FX2); + vfree(new_fw_data); + return ret; + } + } + + return -EINVAL; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties cxusb_medion_properties; +static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties; +static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties; +static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties; +static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties; +static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties; +static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties; +static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties; +static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties; +static struct dvb_usb_device_properties cxusb_aver_a868r_properties; +static struct dvb_usb_device_properties cxusb_d680_dmb_properties; +static struct dvb_usb_device_properties cxusb_mygica_d689_properties; + +static int cxusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (0 == dvb_usb_device_init(intf, &cxusb_medion_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &cxusb_bluebird_nano2_needsfirmware_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &cxusb_bluebird_dualdig4_rev2_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, + THIS_MODULE, NULL, adapter_nr) || + 0) + return 0; + + return -EINVAL; +} + +static struct usb_device_id cxusb_table [] = { + { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) }, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) }, + { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) }, + { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) }, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, cxusb_table); + +static struct dvb_usb_device_properties cxusb_medion_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_cx22702_frontend_attach, + .tuner_attach = cxusb_fmd1216me_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + .power_ctrl = cxusb_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Medion MD95700 (MDUSBTV-HYBRID)", + { NULL }, + { &cxusb_table[0], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-bluebird-01.fw", + .download_firmware = bluebird_patch_dvico_firmware_download, + /* use usb alt setting 0 for EP4 transfer (dvb-t), + use usb alt setting 7 for EP2 transfer (atsc) */ + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_lgdt3303_frontend_attach, + .tuner_attach = cxusb_lgh064f_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_bluebird_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_dvico_portable_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV5 USB Gold", + { &cxusb_table[1], NULL }, + { &cxusb_table[2], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-bluebird-01.fw", + .download_firmware = bluebird_patch_dvico_firmware_download, + /* use usb alt setting 0 for EP4 transfer (dvb-t), + use usb alt setting 7 for EP2 transfer (atsc) */ + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_dee1601_frontend_attach, + .tuner_attach = cxusb_dee1601_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_bluebird_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .rc.legacy = { + .rc_interval = 150, + .rc_map_table = rc_map_dvico_mce_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_mce_table), + .rc_query = cxusb_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 3, + .devices = { + { "DViCO FusionHDTV DVB-T Dual USB", + { &cxusb_table[3], NULL }, + { &cxusb_table[4], NULL }, + }, + { "DigitalNow DVB-T Dual USB", + { &cxusb_table[9], NULL }, + { &cxusb_table[10], NULL }, + }, + { "DViCO FusionHDTV DVB-T Dual Digital 2", + { &cxusb_table[11], NULL }, + { &cxusb_table[12], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-bluebird-01.fw", + .download_firmware = bluebird_patch_dvico_firmware_download, + /* use usb alt setting 0 for EP4 transfer (dvb-t), + use usb alt setting 7 for EP2 transfer (atsc) */ + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_mt352_frontend_attach, + .tuner_attach = cxusb_lgz201_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + .power_ctrl = cxusb_bluebird_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_dvico_portable_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV DVB-T USB (LGZ201)", + { &cxusb_table[5], NULL }, + { &cxusb_table[6], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-bluebird-01.fw", + .download_firmware = bluebird_patch_dvico_firmware_download, + /* use usb alt setting 0 for EP4 transfer (dvb-t), + use usb alt setting 7 for EP2 transfer (atsc) */ + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_mt352_frontend_attach, + .tuner_attach = cxusb_dtt7579_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + .power_ctrl = cxusb_bluebird_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_dvico_portable_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV DVB-T USB (TH7579)", + { &cxusb_table[7], NULL }, + { &cxusb_table[8], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_dualdig4_frontend_attach, + .tuner_attach = cxusb_dvico_xc3028_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_dvico_mce_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_mce_table), + .rc_query = cxusb_bluebird2_rc_query, + }, + + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV DVB-T Dual Digital 4", + { NULL }, + { &cxusb_table[13], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .identify_state = bluebird_fx2_identify_state, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_nano2_frontend_attach, + .tuner_attach = cxusb_dvico_xc3028_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_nano2_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_dvico_portable_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), + .rc_query = cxusb_bluebird2_rc_query, + }, + + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV DVB-T NANO2", + { NULL }, + { &cxusb_table[14], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-bluebird-02.fw", + .download_firmware = bluebird_patch_dvico_firmware_download, + .identify_state = bluebird_fx2_identify_state, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_nano2_frontend_attach, + .tuner_attach = cxusb_dvico_xc3028_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_nano2_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_dvico_portable_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, + + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV DVB-T NANO2 w/o firmware", + { &cxusb_table[14], NULL }, + { &cxusb_table[15], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_aver_streaming_ctrl, + .frontend_attach = cxusb_aver_lgdt3303_frontend_attach, + .tuner_attach = cxusb_mxl5003s_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + .power_ctrl = cxusb_aver_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "AVerMedia AVerTVHD Volar (A868R)", + { NULL }, + { &cxusb_table[16], NULL }, + }, + } +}; + +static +struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .size_of_priv = sizeof(struct dib0700_adapter_state), + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_dualdig4_rev2_frontend_attach, + .tuner_attach = cxusb_dualdig4_rev2_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_bluebird_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_dvico_mce_table, + .rc_map_size = ARRAY_SIZE(rc_map_dvico_mce_table), + .rc_query = cxusb_rc_query, + }, + + .num_device_descs = 1, + .devices = { + { "DViCO FusionHDTV DVB-T Dual Digital 4 (rev 2)", + { NULL }, + { &cxusb_table[17], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, + .frontend_attach = cxusb_d680_dmb_frontend_attach, + .tuner_attach = cxusb_d680_dmb_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_d680_dmb_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_d680_dmb_table, + .rc_map_size = ARRAY_SIZE(rc_map_d680_dmb_table), + .rc_query = cxusb_d680_dmb_rc_query, + }, + + .num_device_descs = 1, + .devices = { + { + "Conexant DMB-TH Stick", + { NULL }, + { &cxusb_table[18], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, + .frontend_attach = cxusb_mygica_d689_frontend_attach, + .tuner_attach = cxusb_mygica_d689_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + }, + }, + + .power_ctrl = cxusb_d680_dmb_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_d680_dmb_table, + .rc_map_size = ARRAY_SIZE(rc_map_d680_dmb_table), + .rc_query = cxusb_d680_dmb_rc_query, + }, + + .num_device_descs = 1, + .devices = { + { + "Mygica D689 DMB-TH", + { NULL }, + { &cxusb_table[19], NULL }, + }, + } +}; + +static struct usb_driver cxusb_driver = { + .name = "dvb_usb_cxusb", + .probe = cxusb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = cxusb_table, +}; + +module_usb_driver(cxusb_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_AUTHOR("Michael Krufky "); +MODULE_AUTHOR("Chris Pascoe "); +MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design"); +MODULE_VERSION("1.0-alpha"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h new file mode 100644 index 000000000000..1a51eafd31b9 --- /dev/null +++ b/drivers/media/usb/dvb-usb/cxusb.h @@ -0,0 +1,35 @@ +#ifndef _DVB_USB_CXUSB_H_ +#define _DVB_USB_CXUSB_H_ + +#define DVB_USB_LOG_PREFIX "cxusb" +#include "dvb-usb.h" + +/* usb commands - some of it are guesses, don't have a reference yet */ +#define CMD_BLUEBIRD_GPIO_RW 0x05 + +#define CMD_I2C_WRITE 0x08 +#define CMD_I2C_READ 0x09 + +#define CMD_GPIO_READ 0x0d +#define CMD_GPIO_WRITE 0x0e +#define GPIO_TUNER 0x02 + +#define CMD_POWER_OFF 0xdc +#define CMD_POWER_ON 0xde + +#define CMD_STREAMING_ON 0x36 +#define CMD_STREAMING_OFF 0x37 + +#define CMD_AVER_STREAM_ON 0x18 +#define CMD_AVER_STREAM_OFF 0x19 + +#define CMD_GET_IR_CODE 0x47 + +#define CMD_ANALOG 0x50 +#define CMD_DIGITAL 0x51 + +struct cxusb_state { + u8 gpio_write_state[3]; +}; + +#endif diff --git a/drivers/media/usb/dvb-usb/dib0700.h b/drivers/media/usb/dvb-usb/dib0700.h new file mode 100644 index 000000000000..7de125c0b36f --- /dev/null +++ b/drivers/media/usb/dvb-usb/dib0700.h @@ -0,0 +1,75 @@ +/* Linux driver for devices based on the DiBcom DiB0700 USB bridge + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * Copyright (C) 2005-6 DiBcom, SA + */ +#ifndef _DIB0700_H_ +#define _DIB0700_H_ + +#define DVB_USB_LOG_PREFIX "dib0700" +#include "dvb-usb.h" + +#include "dib07x0.h" + +extern int dvb_usb_dib0700_debug; +#define deb_info(args...) dprintk(dvb_usb_dib0700_debug,0x01,args) +#define deb_fw(args...) dprintk(dvb_usb_dib0700_debug,0x02,args) +#define deb_fwdata(args...) dprintk(dvb_usb_dib0700_debug,0x04,args) +#define deb_data(args...) dprintk(dvb_usb_dib0700_debug,0x08,args) + +#define REQUEST_SET_USB_XFER_LEN 0x0 /* valid only for firmware version */ + /* higher than 1.21 */ +#define REQUEST_I2C_READ 0x2 +#define REQUEST_I2C_WRITE 0x3 +#define REQUEST_POLL_RC 0x4 /* deprecated in firmware v1.20 */ +#define REQUEST_JUMPRAM 0x8 +#define REQUEST_SET_CLOCK 0xB +#define REQUEST_SET_GPIO 0xC +#define REQUEST_ENABLE_VIDEO 0xF + // 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog) + // 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1) + // 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " ) +#define REQUEST_SET_I2C_PARAM 0x10 +#define REQUEST_SET_RC 0x11 +#define REQUEST_NEW_I2C_READ 0x12 +#define REQUEST_NEW_I2C_WRITE 0x13 +#define REQUEST_GET_VERSION 0x15 + +struct dib0700_state { + u8 channel_state; + u16 mt2060_if1[2]; + u8 rc_toggle; + u8 rc_counter; + u8 is_dib7000pc; + u8 fw_use_new_i2c_api; + u8 disable_streaming_master_mode; + u32 fw_version; + u32 nb_packet_buffer_size; + int (*read_status)(struct dvb_frontend *, fe_status_t *); + int (*sleep)(struct dvb_frontend* fe); + u8 buf[255]; +}; + +extern int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, + u32 *romversion, u32 *ramversion, u32 *fwtype); +extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val); +extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); +extern int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen); +extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw); +extern int dib0700_rc_setup(struct dvb_usb_device *d); +extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); +extern struct i2c_algorithm dib0700_i2c_algo; +extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold); +extern int dib0700_change_protocol(struct rc_dev *dev, u64 rc_type); +extern int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz); + +extern int dib0700_device_count; +extern int dvb_usb_dib0700_ir_proto; +extern struct dvb_usb_device_properties dib0700_devices[]; +extern struct usb_device_id dib0700_usb_id_table[]; + +#endif diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c new file mode 100644 index 000000000000..ef87229de6af --- /dev/null +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -0,0 +1,845 @@ +/* Linux driver for devices based on the DiBcom DiB0700 USB bridge + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * Copyright (C) 2005-6 DiBcom, SA + */ +#include "dib0700.h" + +/* debug */ +int dvb_usb_dib0700_debug; +module_param_named(debug,dvb_usb_dib0700_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS); + +static int nb_packet_buffer_size = 21; +module_param(nb_packet_buffer_size, int, 0644); +MODULE_PARM_DESC(nb_packet_buffer_size, + "Set the dib0700 driver data buffer size. This parameter " + "corresponds to the number of TS packets. The actual size of " + "the data buffer corresponds to this parameter " + "multiplied by 188 (default: 21)"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + + +int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, + u32 *romversion, u32 *ramversion, u32 *fwtype) +{ + struct dib0700_state *st = d->priv; + int ret; + + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return -EINTR; + } + + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + REQUEST_GET_VERSION, + USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, + st->buf, 16, USB_CTRL_GET_TIMEOUT); + if (hwversion != NULL) + *hwversion = (st->buf[0] << 24) | (st->buf[1] << 16) | + (st->buf[2] << 8) | st->buf[3]; + if (romversion != NULL) + *romversion = (st->buf[4] << 24) | (st->buf[5] << 16) | + (st->buf[6] << 8) | st->buf[7]; + if (ramversion != NULL) + *ramversion = (st->buf[8] << 24) | (st->buf[9] << 16) | + (st->buf[10] << 8) | st->buf[11]; + if (fwtype != NULL) + *fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) | + (st->buf[14] << 8) | st->buf[15]; + mutex_unlock(&d->usb_mutex); + return ret; +} + +/* expecting rx buffer: request data[0] data[1] ... data[2] */ +static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen) +{ + int status; + + deb_data(">>> "); + debug_dump(tx, txlen, deb_data); + + status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0), + tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen, + USB_CTRL_GET_TIMEOUT); + + if (status != txlen) + deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen); + + return status < 0 ? status : 0; +} + +/* expecting tx buffer: request data[0] ... data[n] (n <= 4) */ +int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) +{ + u16 index, value; + int status; + + if (txlen < 2) { + err("tx buffer length is smaller than 2. Makes no sense."); + return -EINVAL; + } + if (txlen > 4) { + err("tx buffer length is larger than 4. Not supported."); + return -EINVAL; + } + + deb_data(">>> "); + debug_dump(tx,txlen,deb_data); + + value = ((txlen - 2) << 8) | tx[1]; + index = 0; + if (txlen > 2) + index |= (tx[2] << 8); + if (txlen > 3) + index |= tx[3]; + + status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0], + USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen, + USB_CTRL_GET_TIMEOUT); + + if (status < 0) + deb_info("ep 0 read error (status = %d)\n",status); + + deb_data("<<< "); + debug_dump(rx, rxlen, deb_data); + + return status; /* length in case of success */ +} + +int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val) +{ + struct dib0700_state *st = d->priv; + int ret; + + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return -EINTR; + } + + st->buf[0] = REQUEST_SET_GPIO; + st->buf[1] = gpio; + st->buf[2] = ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6); + + ret = dib0700_ctrl_wr(d, st->buf, 3); + + mutex_unlock(&d->usb_mutex); + return ret; +} + +static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets) +{ + struct dib0700_state *st = d->priv; + int ret; + + if (st->fw_version >= 0x10201) { + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return -EINTR; + } + + st->buf[0] = REQUEST_SET_USB_XFER_LEN; + st->buf[1] = (nb_ts_packets >> 8) & 0xff; + st->buf[2] = nb_ts_packets & 0xff; + + deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets); + + ret = dib0700_ctrl_wr(d, st->buf, 3); + mutex_unlock(&d->usb_mutex); + } else { + deb_info("this firmware does not allow to change the USB xfer len\n"); + ret = -EIO; + } + + return ret; +} + +/* + * I2C master xfer function (supported in 1.20 firmware) + */ +static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + /* The new i2c firmware messages are more reliable and in particular + properly support i2c read calls not preceded by a write */ + + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct dib0700_state *st = d->priv; + uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */ + uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */ + uint8_t en_start = 0; + uint8_t en_stop = 0; + int result, i; + + /* Ensure nobody else hits the i2c bus while we're sending our + sequence of messages, (such as the remote control thread) */ + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EINTR; + + for (i = 0; i < num; i++) { + if (i == 0) { + /* First message in the transaction */ + en_start = 1; + } else if (!(msg[i].flags & I2C_M_NOSTART)) { + /* Device supports repeated-start */ + en_start = 1; + } else { + /* Not the first packet and device doesn't support + repeated start */ + en_start = 0; + } + if (i == (num - 1)) { + /* Last message in the transaction */ + en_stop = 1; + } + + if (msg[i].flags & I2C_M_RD) { + /* Read request */ + u16 index, value; + uint8_t i2c_dest; + + i2c_dest = (msg[i].addr << 1); + value = ((en_start << 7) | (en_stop << 6) | + (msg[i].len & 0x3F)) << 8 | i2c_dest; + /* I2C ctrl + FE bus; */ + index = ((gen_mode << 6) & 0xC0) | + ((bus_mode << 4) & 0x30); + + result = usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev, 0), + REQUEST_NEW_I2C_READ, + USB_TYPE_VENDOR | USB_DIR_IN, + value, index, msg[i].buf, + msg[i].len, + USB_CTRL_GET_TIMEOUT); + if (result < 0) { + deb_info("i2c read error (status = %d)\n", result); + break; + } + + deb_data("<<< "); + debug_dump(msg[i].buf, msg[i].len, deb_data); + + } else { + /* Write request */ + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + mutex_unlock(&d->i2c_mutex); + return -EINTR; + } + st->buf[0] = REQUEST_NEW_I2C_WRITE; + st->buf[1] = msg[i].addr << 1; + st->buf[2] = (en_start << 7) | (en_stop << 6) | + (msg[i].len & 0x3F); + /* I2C ctrl + FE bus; */ + st->buf[3] = ((gen_mode << 6) & 0xC0) | + ((bus_mode << 4) & 0x30); + /* The Actual i2c payload */ + memcpy(&st->buf[4], msg[i].buf, msg[i].len); + + deb_data(">>> "); + debug_dump(st->buf, msg[i].len + 4, deb_data); + + result = usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev, 0), + REQUEST_NEW_I2C_WRITE, + USB_TYPE_VENDOR | USB_DIR_OUT, + 0, 0, st->buf, msg[i].len + 4, + USB_CTRL_GET_TIMEOUT); + mutex_unlock(&d->usb_mutex); + if (result < 0) { + deb_info("i2c write error (status = %d)\n", result); + break; + } + } + } + mutex_unlock(&d->i2c_mutex); + return i; +} + +/* + * I2C master xfer function (pre-1.20 firmware) + */ +static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, + struct i2c_msg *msg, int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct dib0700_state *st = d->priv; + int i,len; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EINTR; + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + mutex_unlock(&d->i2c_mutex); + return -EINTR; + } + + for (i = 0; i < num; i++) { + /* fill in the address */ + st->buf[1] = msg[i].addr << 1; + /* fill the buffer */ + memcpy(&st->buf[2], msg[i].buf, msg[i].len); + + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + st->buf[0] = REQUEST_I2C_READ; + st->buf[1] |= 1; + + /* special thing in the current firmware: when length is zero the read-failed */ + len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2, + msg[i+1].buf, msg[i+1].len); + if (len <= 0) { + deb_info("I2C read failed on address 0x%02x\n", + msg[i].addr); + break; + } + + msg[i+1].len = len; + + i++; + } else { + st->buf[0] = REQUEST_I2C_WRITE; + if (dib0700_ctrl_wr(d, st->buf, msg[i].len + 2) < 0) + break; + } + } + mutex_unlock(&d->usb_mutex); + mutex_unlock(&d->i2c_mutex); + + return i; +} + +static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct dib0700_state *st = d->priv; + + if (st->fw_use_new_i2c_api == 1) { + /* User running at least fw 1.20 */ + return dib0700_i2c_xfer_new(adap, msg, num); + } else { + /* Use legacy calls */ + return dib0700_i2c_xfer_legacy(adap, msg, num); + } +} + +static u32 dib0700_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +struct i2c_algorithm dib0700_i2c_algo = { + .master_xfer = dib0700_i2c_xfer, + .functionality = dib0700_i2c_func, +}; + +int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold) +{ + s16 ret; + u8 *b; + + b = kmalloc(16, GFP_KERNEL); + if (!b) + return -ENOMEM; + + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT); + + deb_info("FW GET_VERSION length: %d\n",ret); + + *cold = ret <= 0; + deb_info("cold: %d\n", *cold); + + kfree(b); + return 0; +} + +static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, + u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv, + u16 pll_loopdiv, u16 free_div, u16 dsuScaler) +{ + struct dib0700_state *st = d->priv; + int ret; + + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return -EINTR; + } + + st->buf[0] = REQUEST_SET_CLOCK; + st->buf[1] = (en_pll << 7) | (pll_src << 6) | + (pll_range << 5) | (clock_gpio3 << 4); + st->buf[2] = (pll_prediv >> 8) & 0xff; /* MSB */ + st->buf[3] = pll_prediv & 0xff; /* LSB */ + st->buf[4] = (pll_loopdiv >> 8) & 0xff; /* MSB */ + st->buf[5] = pll_loopdiv & 0xff; /* LSB */ + st->buf[6] = (free_div >> 8) & 0xff; /* MSB */ + st->buf[7] = free_div & 0xff; /* LSB */ + st->buf[8] = (dsuScaler >> 8) & 0xff; /* MSB */ + st->buf[9] = dsuScaler & 0xff; /* LSB */ + + ret = dib0700_ctrl_wr(d, st->buf, 10); + mutex_unlock(&d->usb_mutex); + + return ret; +} + +int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) +{ + struct dib0700_state *st = d->priv; + u16 divider; + int ret; + + if (scl_kHz == 0) + return -EINVAL; + + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return -EINTR; + } + + st->buf[0] = REQUEST_SET_I2C_PARAM; + divider = (u16) (30000 / scl_kHz); + st->buf[1] = 0; + st->buf[2] = (u8) (divider >> 8); + st->buf[3] = (u8) (divider & 0xff); + divider = (u16) (72000 / scl_kHz); + st->buf[4] = (u8) (divider >> 8); + st->buf[5] = (u8) (divider & 0xff); + divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */ + st->buf[6] = (u8) (divider >> 8); + st->buf[7] = (u8) (divider & 0xff); + + deb_info("setting I2C speed: %04x %04x %04x (%d kHz).", + (st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) | + st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz); + + ret = dib0700_ctrl_wr(d, st->buf, 8); + mutex_unlock(&d->usb_mutex); + + return ret; +} + + +int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3) +{ + switch (clk_MHz) { + case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break; + default: return -EINVAL; + } + return 0; +} + +static int dib0700_jumpram(struct usb_device *udev, u32 address) +{ + int ret = 0, actlen; + u8 *buf; + + buf = kmalloc(8, GFP_KERNEL); + if (!buf) + return -ENOMEM; + buf[0] = REQUEST_JUMPRAM; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf[4] = (address >> 24) & 0xff; + buf[5] = (address >> 16) & 0xff; + buf[6] = (address >> 8) & 0xff; + buf[7] = address & 0xff; + + if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) { + deb_fw("jumpram to 0x%x failed\n",address); + goto out; + } + if (actlen != 8) { + deb_fw("jumpram to 0x%x failed\n",address); + ret = -EIO; + goto out; + } +out: + kfree(buf); + return ret; +} + +int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw) +{ + struct hexline hx; + int pos = 0, ret, act_len, i, adap_num; + u8 *buf; + u32 fw_version; + + buf = kmalloc(260, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) { + deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n", + hx.addr, hx.len, hx.chk); + + buf[0] = hx.len; + buf[1] = (hx.addr >> 8) & 0xff; + buf[2] = hx.addr & 0xff; + buf[3] = hx.type; + memcpy(&buf[4],hx.data,hx.len); + buf[4+hx.len] = hx.chk; + + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x01), + buf, + hx.len + 5, + &act_len, + 1000); + + if (ret < 0) { + err("firmware download failed at %d with %d",pos,ret); + goto out; + } + } + + if (ret == 0) { + /* start the firmware */ + if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) { + info("firmware started successfully."); + msleep(500); + } + } else + ret = -EIO; + + /* the number of ts packet has to be at least 1 */ + if (nb_packet_buffer_size < 1) + nb_packet_buffer_size = 1; + + /* get the fimware version */ + usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + REQUEST_GET_VERSION, + USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, + buf, 16, USB_CTRL_GET_TIMEOUT); + fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11]; + + /* set the buffer size - DVB-USB is allocating URB buffers + * only after the firwmare download was successful */ + for (i = 0; i < dib0700_device_count; i++) { + for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters; + adap_num++) { + if (fw_version >= 0x10201) { + dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size; + } else { + /* for fw version older than 1.20.1, + * the buffersize has to be n times 512 */ + dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512; + if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512) + dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512; + } + } + } +out: + kfree(buf); + return ret; +} + +int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dib0700_state *st = adap->dev->priv; + int ret; + + if ((onoff != 0) && (st->fw_version >= 0x10201)) { + /* for firmware later than 1.20.1, + * the USB xfer length can be set */ + ret = dib0700_set_usb_xfer_len(adap->dev, + st->nb_packet_buffer_size); + if (ret < 0) { + deb_info("can not set the USB xfer len\n"); + return ret; + } + } + + if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { + err("could not acquire lock"); + return -EINTR; + } + + st->buf[0] = REQUEST_ENABLE_VIDEO; + /* this bit gives a kind of command, + * rather than enabling something or not */ + st->buf[1] = (onoff << 4) | 0x00; + + if (st->disable_streaming_master_mode == 1) + st->buf[2] = 0x00; + else + st->buf[2] = 0x01 << 4; /* Master mode */ + + st->buf[3] = 0x00; + + deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id); + + st->channel_state &= ~0x3; + if ((adap->fe_adap[0].stream.props.endpoint != 2) + && (adap->fe_adap[0].stream.props.endpoint != 3)) { + deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].stream.props.endpoint); + if (onoff) + st->channel_state |= 1 << (adap->id); + else + st->channel_state |= 1 << ~(adap->id); + } else { + if (onoff) + st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2); + else + st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint); + } + + st->buf[2] |= st->channel_state; + + deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]); + + ret = dib0700_ctrl_wr(adap->dev, st->buf, 4); + mutex_unlock(&adap->dev->usb_mutex); + + return ret; +} + +int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type) +{ + struct dvb_usb_device *d = rc->priv; + struct dib0700_state *st = d->priv; + int new_proto, ret; + + if (mutex_lock_interruptible(&d->usb_mutex) < 0) { + err("could not acquire lock"); + return -EINTR; + } + + st->buf[0] = REQUEST_SET_RC; + st->buf[1] = 0; + st->buf[2] = 0; + + /* Set the IR mode */ + if (rc_type == RC_TYPE_RC5) + new_proto = 1; + else if (rc_type == RC_TYPE_NEC) + new_proto = 0; + else if (rc_type == RC_TYPE_RC6) { + if (st->fw_version < 0x10200) { + ret = -EINVAL; + goto out; + } + + new_proto = 2; + } else { + ret = -EINVAL; + goto out; + } + + st->buf[1] = new_proto; + + ret = dib0700_ctrl_wr(d, st->buf, 3); + if (ret < 0) { + err("ir protocol setup failed"); + goto out; + } + + d->props.rc.core.protocol = rc_type; + +out: + mutex_unlock(&d->usb_mutex); + return ret; +} + +/* Number of keypresses to ignore before start repeating */ +#define RC_REPEAT_DELAY_V1_20 10 + +/* This is the structure of the RC response packet starting in firmware 1.20 */ +struct dib0700_rc_response { + u8 report_id; + u8 data_state; + union { + u16 system16; + struct { + u8 not_system; + u8 system; + }; + }; + u8 data; + u8 not_data; +}; +#define RC_MSG_SIZE_V1_20 6 + +static void dib0700_rc_urb_completion(struct urb *purb) +{ + struct dvb_usb_device *d = purb->context; + struct dib0700_rc_response *poll_reply; + u32 uninitialized_var(keycode); + u8 toggle; + + deb_info("%s()\n", __func__); + if (d->rc_dev == NULL) { + /* This will occur if disable_rc_polling=1 */ + kfree(purb->transfer_buffer); + usb_free_urb(purb); + return; + } + + poll_reply = purb->transfer_buffer; + + if (purb->status < 0) { + deb_info("discontinuing polling\n"); + kfree(purb->transfer_buffer); + usb_free_urb(purb); + return; + } + + if (purb->actual_length != RC_MSG_SIZE_V1_20) { + deb_info("malformed rc msg size=%d\n", purb->actual_length); + goto resubmit; + } + + deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n", + poll_reply->report_id, poll_reply->data_state, + poll_reply->system, poll_reply->not_system, + poll_reply->data, poll_reply->not_data, + purb->actual_length); + + switch (d->props.rc.core.protocol) { + case RC_TYPE_NEC: + toggle = 0; + + /* NEC protocol sends repeat code as 0 0 0 FF */ + if ((poll_reply->system == 0x00) && (poll_reply->data == 0x00) + && (poll_reply->not_data == 0xff)) { + poll_reply->data_state = 2; + break; + } + + if ((poll_reply->system ^ poll_reply->not_system) != 0xff) { + deb_data("NEC extended protocol\n"); + /* NEC extended code - 24 bits */ + keycode = be16_to_cpu(poll_reply->system16) << 8 | poll_reply->data; + } else { + deb_data("NEC normal protocol\n"); + /* normal NEC code - 16 bits */ + keycode = poll_reply->system << 8 | poll_reply->data; + } + + break; + default: + deb_data("RC5 protocol\n"); + /* RC5 Protocol */ + toggle = poll_reply->report_id; + keycode = poll_reply->system << 8 | poll_reply->data; + + break; + } + + if ((poll_reply->data + poll_reply->not_data) != 0xff) { + /* Key failed integrity check */ + err("key failed integrity check: %04x %02x %02x", + poll_reply->system, + poll_reply->data, poll_reply->not_data); + goto resubmit; + } + + rc_keydown(d->rc_dev, keycode, toggle); + +resubmit: + /* Clean the buffer before we requeue */ + memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20); + + /* Requeue URB */ + usb_submit_urb(purb, GFP_ATOMIC); +} + +int dib0700_rc_setup(struct dvb_usb_device *d) +{ + struct dib0700_state *st = d->priv; + struct urb *purb; + int ret; + + /* Poll-based. Don't initialize bulk mode */ + if (st->fw_version < 0x10200) + return 0; + + /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ + purb = usb_alloc_urb(0, GFP_KERNEL); + if (purb == NULL) { + err("rc usb alloc urb failed"); + return -ENOMEM; + } + + purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL); + if (purb->transfer_buffer == NULL) { + err("rc kzalloc failed"); + usb_free_urb(purb); + return -ENOMEM; + } + + purb->status = -EINPROGRESS; + usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1), + purb->transfer_buffer, RC_MSG_SIZE_V1_20, + dib0700_rc_urb_completion, d); + + ret = usb_submit_urb(purb, GFP_ATOMIC); + if (ret) { + err("rc submit urb failed"); + kfree(purb->transfer_buffer); + usb_free_urb(purb); + } + + return ret; +} + +static int dib0700_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int i; + struct dvb_usb_device *dev; + + for (i = 0; i < dib0700_device_count; i++) + if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE, + &dev, adapter_nr) == 0) { + struct dib0700_state *st = dev->priv; + u32 hwversion, romversion, fw_version, fwtype; + + dib0700_get_version(dev, &hwversion, &romversion, + &fw_version, &fwtype); + + deb_info("Firmware version: %x, %d, 0x%x, %d\n", + hwversion, romversion, fw_version, fwtype); + + st->fw_version = fw_version; + st->nb_packet_buffer_size = (u32)nb_packet_buffer_size; + + /* Disable polling mode on newer firmwares */ + if (st->fw_version >= 0x10200) + dev->props.rc.core.bulk_mode = true; + else + dev->props.rc.core.bulk_mode = false; + + dib0700_rc_setup(dev); + + return 0; + } + + return -ENODEV; +} + +static struct usb_driver dib0700_driver = { + .name = "dvb_usb_dib0700", + .probe = dib0700_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dib0700_usb_id_table, +}; + +module_usb_driver(dib0700_driver); + +MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw"); +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c new file mode 100644 index 000000000000..510001da6e83 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -0,0 +1,4813 @@ +/* Linux driver for devices based on the DiBcom DiB0700 USB bridge + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * Copyright (C) 2005-9 DiBcom, SA et al + */ +#include "dib0700.h" + +#include "dib3000mc.h" +#include "dib7000m.h" +#include "dib7000p.h" +#include "dib8000.h" +#include "dib9000.h" +#include "mt2060.h" +#include "mt2266.h" +#include "tuner-xc2028.h" +#include "xc5000.h" +#include "xc4000.h" +#include "s5h1411.h" +#include "dib0070.h" +#include "dib0090.h" +#include "lgdt3305.h" +#include "mxl5007t.h" + +static int force_lna_activation; +module_param(force_lna_activation, int, 0644); +MODULE_PARM_DESC(force_lna_activation, "force the activation of Low-Noise-Amplifyer(s) (LNA), " + "if applicable for the device (default: 0=automatic/off)."); + +struct dib0700_adapter_state { + int (*set_param_save) (struct dvb_frontend *); + const struct firmware *frontend_firmware; +}; + +/* Hauppauge Nova-T 500 (aka Bristol) + * has a LNA on GPIO0 which is enabled by setting 1 */ +static struct mt2060_config bristol_mt2060_config[2] = { + { + .i2c_address = 0x60, + .clock_out = 3, + }, { + .i2c_address = 0x61, + } +}; + + +static struct dibx000_agc_config bristol_dib3000p_mt2060_agc_config = { + .band_caps = BAND_VHF | BAND_UHF, + .setup = (1 << 8) | (5 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (2 << 0), + + .agc1_max = 42598, + .agc1_min = 17694, + .agc2_max = 45875, + .agc2_min = 0, + + .agc1_pt1 = 0, + .agc1_pt2 = 59, + + .agc1_slope1 = 0, + .agc1_slope2 = 69, + + .agc2_pt1 = 0, + .agc2_pt2 = 59, + + .agc2_slope1 = 111, + .agc2_slope2 = 28, +}; + +static struct dib3000mc_config bristol_dib3000mc_config[2] = { + { .agc = &bristol_dib3000p_mt2060_agc_config, + .max_time = 0x196, + .ln_adc_level = 0x1cc7, + .output_mpeg2_in_188_bytes = 1, + }, + { .agc = &bristol_dib3000p_mt2060_agc_config, + .max_time = 0x196, + .ln_adc_level = 0x1cc7, + .output_mpeg2_in_188_bytes = 1, + } +}; + +static int bristol_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_state *st = adap->dev->priv; + if (adap->id == 0) { + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); + + if (force_lna_activation) + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + else + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0); + + if (dib3000mc_i2c_enumeration(&adap->dev->i2c_adap, 2, DEFAULT_DIB3000P_I2C_ADDRESS, bristol_dib3000mc_config) != 0) { + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); + return -ENODEV; + } + } + st->mt2060_if1[adap->id] = 1220; + return (adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, + (10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0; +} + +static int eeprom_read(struct i2c_adapter *adap,u8 adrs,u8 *pval) +{ + struct i2c_msg msg[2] = { + { .addr = 0x50, .flags = 0, .buf = &adrs, .len = 1 }, + { .addr = 0x50, .flags = I2C_M_RD, .buf = pval, .len = 1 }, + }; + if (i2c_transfer(adap, msg, 2) != 2) return -EREMOTEIO; + return 0; +} + +static int bristol_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap; + struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); + s8 a; + int if1=1220; + if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && + adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_500_2)) { + if (!eeprom_read(prim_i2c,0x59 + adap->id,&a)) if1=1220+a; + } + return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, + &bristol_mt2060_config[adap->id], if1) == NULL ? + -ENODEV : 0; +} + +/* STK7700D: Pinnacle/Terratec/Hauppauge Dual DVB-T Diversity */ + +/* MT226x */ +static struct dibx000_agc_config stk7700d_7000p_mt2266_agc_config[2] = { + { + BAND_UHF, + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=1, P_agc_inv_pwm2=1, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8) + | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + + 1130, + 21, + + 0, + 118, + + 0, + 3530, + 1, + 0, + + 65535, + 33770, + 65535, + 23592, + + 0, + 62, + 255, + 64, + 64, + 132, + 192, + 80, + 80, + + 17, + 27, + 23, + 51, + + 1, + }, { + BAND_VHF | BAND_LBAND, + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=1, P_agc_inv_pwm2=1, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8) + | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), + + 2372, + 21, + + 0, + 118, + + 0, + 3530, + 1, + 0, + + 65535, + 0, + 65535, + 23592, + + 0, + 128, + 128, + 128, + 0, + 128, + 253, + 81, + 0, + + 17, + 27, + 23, + 51, + + 1, + } +}; + +static struct dibx000_bandwidth_config stk7700d_mt2266_pll_config = { + 60000, 30000, + 1, 8, 3, 1, 0, + 0, 0, 1, 1, 2, + (3 << 14) | (1 << 12) | (524 << 0), + 0, + 20452225, +}; + +static struct dib7000p_config stk7700d_dib7000p_mt2266_config[] = { + { .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + + .agc_config_count = 2, + .agc = stk7700d_7000p_mt2266_agc_config, + .bw = &stk7700d_mt2266_pll_config, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + }, + { .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + + .agc_config_count = 2, + .agc = stk7700d_7000p_mt2266_agc_config, + .bw = &stk7700d_mt2266_pll_config, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + } +}; + +static struct mt2266_config stk7700d_mt2266_config[2] = { + { .i2c_address = 0x60 + }, + { .i2c_address = 0x60 + } +}; + +static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (adap->id == 0) { + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + stk7700d_dib7000p_mt2266_config) + != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } + } + + adap->fe_adap[0].fe = + dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + 0x80 + (adap->id << 1), + &stk7700d_dib7000p_mt2266_config[adap->id]); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (adap->id == 0) { + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, + stk7700d_dib7000p_mt2266_config) + != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } + } + + adap->fe_adap[0].fe = + dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + 0x80 + (adap->id << 1), + &stk7700d_dib7000p_mt2266_config[adap->id]); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *tun_i2c; + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + return dvb_attach(mt2266_attach, adap->fe_adap[0].fe, tun_i2c, + &stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0; +} + +/* STK7700-PH: Digital/Analog Hybrid Tuner, e.h. Cinergy HT USB HE */ +static struct dibx000_agc_config xc3028_agc_config = { + BAND_VHF | BAND_UHF, /* band_caps */ + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | + (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */ + + 712, /* inv_gain */ + 21, /* time_stabiliz */ + + 0, /* alpha_level */ + 118, /* thlock */ + + 0, /* wbd_inv */ + 2867, /* wbd_ref */ + 0, /* wbd_sel */ + 2, /* wbd_alpha */ + + 0, /* agc1_max */ + 0, /* agc1_min */ + 39718, /* agc2_max */ + 9930, /* agc2_min */ + 0, /* agc1_pt1 */ + 0, /* agc1_pt2 */ + 0, /* agc1_pt3 */ + 0, /* agc1_slope1 */ + 0, /* agc1_slope2 */ + 0, /* agc2_pt1 */ + 128, /* agc2_pt2 */ + 29, /* agc2_slope1 */ + 29, /* agc2_slope2 */ + + 17, /* alpha_mant */ + 27, /* alpha_exp */ + 23, /* beta_mant */ + 51, /* beta_exp */ + + 1, /* perform_agc_softsplit */ +}; + +/* PLL Configuration for COFDM BW_MHz = 8.00 with external clock = 30.00 */ +static struct dibx000_bandwidth_config xc3028_bw_config = { + 60000, 30000, /* internal, sampling */ + 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */ + 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, + modulo */ + (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ + (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ + 20452225, /* timf */ + 30000000, /* xtal_hz */ +}; + +static struct dib7000p_config stk7700ph_dib7700_xc3028_config = { + .output_mpeg2_in_188_bytes = 1, + .tuner_is_baseband = 1, + + .agc_config_count = 1, + .agc = &xc3028_agc_config, + .bw = &xc3028_bw_config, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +}; + +static int stk7700ph_xc3028_callback(void *ptr, int component, + int command, int arg) +{ + struct dvb_usb_adapter *adap = ptr; + + switch (command) { + case XC2028_TUNER_RESET: + /* Send the tuner in then out of reset */ + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + break; + case XC2028_RESET_CLK: + break; + default: + err("%s: unknown command %d, arg %d\n", __func__, + command, arg); + return -EINVAL; + } + return 0; +} + +static struct xc2028_ctrl stk7700ph_xc3028_ctrl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = XC3028_FE_DIBCOM52, +}; + +static struct xc2028_config stk7700ph_xc3028_config = { + .i2c_addr = 0x61, + .ctrl = &stk7700ph_xc3028_ctrl, +}; + +static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct usb_device_descriptor *desc = &adap->dev->udev->descriptor; + + if (desc->idVendor == cpu_to_le16(USB_VID_PINNACLE) && + desc->idProduct == cpu_to_le16(USB_PID_PINNACLE_EXPRESSCARD_320CX)) + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + else + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + msleep(10); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + &stk7700ph_dib7700_xc3028_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + &stk7700ph_dib7700_xc3028_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *tun_i2c; + + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + stk7700ph_xc3028_config.i2c_adap = tun_i2c; + + /* FIXME: generalize & move to common area */ + adap->fe_adap[0].fe->callback = stk7700ph_xc3028_callback; + + return dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &stk7700ph_xc3028_config) + == NULL ? -ENODEV : 0; +} + +#define DEFAULT_RC_INTERVAL 50 + +static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; + +/* Number of keypresses to ignore before start repeating */ +#define RC_REPEAT_DELAY 6 + +/* + * This function is used only when firmware is < 1.20 version. Newer + * firmwares use bulk mode, with functions implemented at dib0700_core, + * at dib0700_rc_urb_completion() + */ +static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) +{ + u8 key[4]; + u32 keycode; + u8 toggle; + int i; + struct dib0700_state *st = d->priv; + + if (st->fw_version >= 0x10200) { + /* For 1.20 firmware , We need to keep the RC polling + callback so we can reuse the input device setup in + dvb-usb-remote.c. However, the actual work is being done + in the bulk URB completion handler. */ + return 0; + } + + i = dib0700_ctrl_rd(d, rc_request, 2, key, 4); + if (i <= 0) { + err("RC Query Failed"); + return -1; + } + + /* losing half of KEY_0 events from Philipps rc5 remotes.. */ + if (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 0) + return 0; + + /* info("%d: %2X %2X %2X %2X",dvb_usb_dib0700_ir_proto,(int)key[3-2],(int)key[3-3],(int)key[3-1],(int)key[3]); */ + + dib0700_rc_setup(d); /* reset ir sensor data to prevent false events */ + + d->last_event = 0; + switch (d->props.rc.core.protocol) { + case RC_TYPE_NEC: + /* NEC protocol sends repeat code as 0 0 0 FF */ + if ((key[3-2] == 0x00) && (key[3-3] == 0x00) && + (key[3] == 0xff)) + keycode = d->last_event; + else { + keycode = key[3-2] << 8 | key[3-3]; + d->last_event = keycode; + } + + rc_keydown(d->rc_dev, keycode, 0); + break; + default: + /* RC-5 protocol changes toggle bit on new keypress */ + keycode = key[3-2] << 8 | key[3-3]; + toggle = key[3-1]; + rc_keydown(d->rc_dev, keycode, toggle); + + break; + } + return 0; +} + +/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ +static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = { + BAND_UHF | BAND_VHF, + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) + | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), + + 712, + 41, + + 0, + 118, + + 0, + 4095, + 0, + 0, + + 42598, + 17694, + 45875, + 2621, + 0, + 76, + 139, + 52, + 59, + 107, + 172, + 57, + 70, + + 21, + 25, + 28, + 48, + + 1, + { 0, + 107, + 51800, + 24700 + }, +}; + +static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = { + BAND_UHF | BAND_VHF, + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) + | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), + + 712, + 41, + + 0, + 118, + + 0, + 4095, + 0, + 0, + + 42598, + 16384, + 42598, + 0, + + 0, + 137, + 255, + + 0, + 255, + + 0, + 0, + + 0, + 41, + + 15, + 25, + + 28, + 48, + + 0, +}; + +static struct dibx000_bandwidth_config stk7700p_pll_config = { + 60000, 30000, + 1, 8, 3, 1, 0, + 0, 0, 1, 1, 0, + (3 << 14) | (1 << 12) | (524 << 0), + 60258167, + 20452225, + 30000000, +}; + +static struct dib7000m_config stk7700p_dib7000m_config = { + .dvbt_mode = 1, + .output_mpeg2_in_188_bytes = 1, + .quartz_direct = 1, + + .agc_config_count = 1, + .agc = &stk7700p_7000m_mt2060_agc_config, + .bw = &stk7700p_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +}; + +static struct dib7000p_config stk7700p_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &stk7700p_7000p_mt2060_agc_config, + .bw = &stk7700p_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +}; + +static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_state *st = adap->dev->priv; + /* unless there is no real power management in DVB - we leave the device on GPIO6 */ + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(50); + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10); + dib0700_ctrl_clock(adap->dev, 72, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(100); + + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + st->mt2060_if1[0] = 1220; + + if (dib7000pc_detection(&adap->dev->i2c_adap)) { + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); + st->is_dib7000pc = 1; + } else + adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static struct mt2060_config stk7700p_mt2060_config = { + 0x60 +}; + +static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap; + struct dib0700_state *st = adap->dev->priv; + struct i2c_adapter *tun_i2c; + s8 a; + int if1=1220; + if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && + adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_STICK)) { + if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a; + } + if (st->is_dib7000pc) + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + else + tun_i2c = dib7000m_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + + return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk7700p_mt2060_config, + if1) == NULL ? -ENODEV : 0; +} + +/* DIB7070 generic */ +static struct dibx000_agc_config dib7070_agc_config = { + BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) + | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + + 600, + 10, + + 0, + 118, + + 0, + 3530, + 1, + 5, + + 65535, + 0, + + 65535, + 0, + + 0, + 40, + 183, + 206, + 255, + 72, + 152, + 88, + 90, + + 17, + 27, + 23, + 51, + + 0, +}; + +static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + deb_info("reset: %d", onoff); + return dib7000p_set_gpio(fe, 8, 0, !onoff); +} + +static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + deb_info("sleep: %d", onoff); + return dib7000p_set_gpio(fe, 9, 0, onoff); +} + +static struct dib0070_config dib7070p_dib0070_config[2] = { + { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 4, + .charge_pump = 2, + }, { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, + .charge_pump = 2, + } +}; + +static struct dib0070_config dib7770p_dib0070_config = { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 0, + .flip_chip = 1, + .charge_pump = 2, +}; + +static int dib7070_set_param_override(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset; + u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + switch (band) { + case BAND_VHF: offset = 950; break; + case BAND_UHF: + default: offset = 550; break; + } + deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); + dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + return state->set_param_save(fe); +} + +static int dib7770_set_param_override(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset; + u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + switch (band) { + case BAND_VHF: + dib7000p_set_gpio(fe, 0, 0, 1); + offset = 850; + break; + case BAND_UHF: + default: + dib7000p_set_gpio(fe, 0, 0, 0); + offset = 250; + break; + } + deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); + dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + return state->set_param_save(fe); +} + +static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, + &dib7770p_dib0070_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7770_set_param_override; + return 0; +} + +static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + + if (adap->id == 0) { + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL) + return -ENODEV; + } else { + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL) + return -ENODEV; + } + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override; + return 0; +} + +static int stk7700p_pid_filter(struct dvb_usb_adapter *adapter, int index, + u16 pid, int onoff) +{ + struct dib0700_state *st = adapter->dev->priv; + if (st->is_dib7000pc) + return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); + return dib7000m_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +} + +static int stk7700p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + struct dib0700_state *st = adapter->dev->priv; + if (st->is_dib7000pc) + return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); + return dib7000m_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +} + +static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +{ + return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +} + +static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +} + +static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { + 60000, 15000, + 1, 20, 3, 1, 0, + 0, 0, 1, 1, 2, + (3 << 14) | (1 << 12) | (524 << 0), + (0 << 25) | 0, + 20452225, + 12000000, +}; + +static struct dib7000p_config dib7070p_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &dib7070_agc_config, + .bw = &dib7070_bw_config_12_mhz, + .tuner_is_baseband = 1, + .spur_protect = 1, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, +}; + +/* STK7070P */ +static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct usb_device_descriptor *p = &adap->dev->udev->descriptor; + if (p->idVendor == cpu_to_le16(USB_VID_PINNACLE) && + p->idProduct == cpu_to_le16(USB_PID_PINNACLE_PCTV72E)) + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + else + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + &dib7070p_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + &dib7070p_dib7000p_config); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +/* STK7770P */ +static struct dib7000p_config dib7770p_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &dib7070_agc_config, + .bw = &dib7070_bw_config_12_mhz, + .tuner_is_baseband = 1, + .spur_protect = 1, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .enable_current_mirror = 1, + .disable_sample_and_hold = 0, +}; + +static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct usb_device_descriptor *p = &adap->dev->udev->descriptor; + if (p->idVendor == cpu_to_le16(USB_VID_PINNACLE) && + p->idProduct == cpu_to_le16(USB_PID_PINNACLE_PCTV72E)) + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + else + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + &dib7770p_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + &dib7770p_dib7000p_config); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +/* DIB807x generic */ +static struct dibx000_agc_config dib807x_agc_config[2] = { + { + BAND_VHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + * P_agc_inv_pwm2=0,P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + * P_agc_write=0 */ + (0 << 15) | (0 << 14) | (7 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | + (0 << 0), /* setup*/ + + 600, /* inv_gain*/ + 10, /* time_stabiliz*/ + + 0, /* alpha_level*/ + 118, /* thlock*/ + + 0, /* wbd_inv*/ + 3530, /* wbd_ref*/ + 1, /* wbd_sel*/ + 5, /* wbd_alpha*/ + + 65535, /* agc1_max*/ + 0, /* agc1_min*/ + + 65535, /* agc2_max*/ + 0, /* agc2_min*/ + + 0, /* agc1_pt1*/ + 40, /* agc1_pt2*/ + 183, /* agc1_pt3*/ + 206, /* agc1_slope1*/ + 255, /* agc1_slope2*/ + 72, /* agc2_pt1*/ + 152, /* agc2_pt2*/ + 88, /* agc2_slope1*/ + 90, /* agc2_slope2*/ + + 17, /* alpha_mant*/ + 27, /* alpha_exp*/ + 23, /* beta_mant*/ + 51, /* beta_exp*/ + + 0, /* perform_agc_softsplit*/ + }, { + BAND_UHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + * P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + * P_agc_write=0 */ + (0 << 15) | (0 << 14) | (1 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | + (0 << 0), /* setup */ + + 600, /* inv_gain*/ + 10, /* time_stabiliz*/ + + 0, /* alpha_level*/ + 118, /* thlock*/ + + 0, /* wbd_inv*/ + 3530, /* wbd_ref*/ + 1, /* wbd_sel*/ + 5, /* wbd_alpha*/ + + 65535, /* agc1_max*/ + 0, /* agc1_min*/ + + 65535, /* agc2_max*/ + 0, /* agc2_min*/ + + 0, /* agc1_pt1*/ + 40, /* agc1_pt2*/ + 183, /* agc1_pt3*/ + 206, /* agc1_slope1*/ + 255, /* agc1_slope2*/ + 72, /* agc2_pt1*/ + 152, /* agc2_pt2*/ + 88, /* agc2_slope1*/ + 90, /* agc2_slope2*/ + + 17, /* alpha_mant*/ + 27, /* alpha_exp*/ + 23, /* beta_mant*/ + 51, /* beta_exp*/ + + 0, /* perform_agc_softsplit*/ + } +}; + +static struct dibx000_bandwidth_config dib807x_bw_config_12_mhz = { + 60000, 15000, /* internal, sampling*/ + 1, 20, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass*/ + 0, 0, 1, 1, 2, /* misc: refdiv, bypclk_div, IO_CLK_en_core, + ADClkSrc, modulo */ + (3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/ + (0 << 25) | 0, /* ifreq = 0.000000 MHz*/ + 18179755, /* timf*/ + 12000000, /* xtal_hz*/ +}; + +static struct dib8000_config dib807x_dib8000_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib807x_agc_config, + .pll = &dib807x_bw_config_12_mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .div_cfg = 1, + .agc_control = &dib0070_ctrl_agc_filter, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + }, { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib807x_agc_config, + .pll = &dib807x_bw_config_12_mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .agc_control = &dib0070_ctrl_agc_filter, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + } +}; + +static int dib80xx_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + return dib8000_set_gpio(fe, 5, 0, !onoff); +} + +static int dib80xx_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return dib8000_set_gpio(fe, 0, 0, onoff); +} + +static const struct dib0070_wbd_gain_cfg dib8070_wbd_gain_cfg[] = { + { 240, 7}, + { 0xffff, 6}, +}; + +static struct dib0070_config dib807x_dib0070_config[2] = { + { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib80xx_tuner_reset, + .sleep = dib80xx_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 4, + .vga_filter = 1, + .force_crystal_mode = 1, + .enable_third_order_filter = 1, + .charge_pump = 0, + .wbd_gain = dib8070_wbd_gain_cfg, + .osc_buffer_state = 0, + .freq_offset_khz_uhf = -100, + .freq_offset_khz_vhf = -100, + }, { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib80xx_tuner_reset, + .sleep = dib80xx_tuner_sleep, + .clock_khz = 12000, + .clock_pad_drive = 2, + .vga_filter = 1, + .force_crystal_mode = 1, + .enable_third_order_filter = 1, + .charge_pump = 0, + .wbd_gain = dib8070_wbd_gain_cfg, + .osc_buffer_state = 0, + .freq_offset_khz_uhf = -25, + .freq_offset_khz_vhf = -25, + } +}; + +static int dib807x_set_param_override(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + u16 offset = dib0070_wbd_offset(fe); + u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + switch (band) { + case BAND_VHF: + offset += 750; + break; + case BAND_UHF: /* fall-thru wanted */ + default: + offset += 250; break; + } + deb_info("WBD for DiB8000: %d\n", offset); + dib8000_set_wbd_ref(fe, offset); + + return state->set_param_save(fe); +} + +static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + + if (adap->id == 0) { + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, + &dib807x_dib0070_config[0]) == NULL) + return -ENODEV; + } else { + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, + &dib807x_dib0070_config[1]) == NULL) + return -ENODEV; + } + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib807x_set_param_override; + return 0; +} + +static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index, + u16 pid, int onoff) +{ + return dib8000_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +} + +static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter, + int onoff) +{ + return dib8000_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +} + +/* STK807x */ +static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + 0x80, 0); + + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + &dib807x_dib8000_config[0]); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +/* STK807xPVR */ +static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + msleep(30); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(500); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + /* initialize IC 0 */ + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80, 0); + + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + &dib807x_dib8000_config[0]); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) +{ + /* initialize IC 1 */ + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82, 0); + + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, + &dib807x_dib8000_config[1]); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +/* STK8096GP */ +static struct dibx000_agc_config dib8090_agc_config[2] = { + { + BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) + | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + + 787, + 10, + + 0, + 118, + + 0, + 3530, + 1, + 5, + + 65535, + 0, + + 65535, + 0, + + 0, + 32, + 114, + 143, + 144, + 114, + 227, + 116, + 117, + + 28, + 26, + 31, + 51, + + 0, + }, + { + BAND_CBAND, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) + | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + + 787, + 10, + + 0, + 118, + + 0, + 3530, + 1, + 5, + + 0, + 0, + + 65535, + 0, + + 0, + 32, + 114, + 143, + 144, + 114, + 227, + 116, + 117, + + 28, + 26, + 31, + 51, + + 0, + } +}; + +static struct dibx000_bandwidth_config dib8090_pll_config_12mhz = { + 54000, 13500, + 1, 18, 3, 1, 0, + 0, 0, 1, 1, 2, + (3 << 14) | (1 << 12) | (599 << 0), + (0 << 25) | 0, + 20199727, + 12000000, +}; + +static int dib8090_get_adc_power(struct dvb_frontend *fe) +{ + return dib8000_get_adc_power(fe, 1); +} + +static struct dib8000_config dib809x_dib8000_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib8090_agc_config, + .agc_control = dib0090_dcc_freq, + .pll = &dib8090_pll_config_12mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .div_cfg = 0x31, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + .diversity_delay = 48, + .refclksel = 3, + }, { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib8090_agc_config, + .agc_control = dib0090_dcc_freq, + .pll = &dib8090_pll_config_12mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .div_cfg = 0x31, + .output_mode = OUTMODE_DIVERSITY, + .drives = 0x2d08, + .diversity_delay = 1, + .refclksel = 3, + } +}; + +static struct dib0090_wbd_slope dib8090_wbd_table[] = { + /* max freq ; cold slope ; cold offset ; warm slope ; warm offset ; wbd gain */ + { 120, 0, 500, 0, 500, 4 }, /* CBAND */ + { 170, 0, 450, 0, 450, 4 }, /* CBAND */ + { 380, 48, 373, 28, 259, 6 }, /* VHF */ + { 860, 34, 700, 36, 616, 6 }, /* high UHF */ + { 0xFFFF, 34, 700, 36, 616, 6 }, /* default */ +}; + +static struct dib0090_config dib809x_dib0090_config = { + .io.pll_bypass = 1, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 20, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 12000, + .reset = dib80xx_tuner_reset, + .sleep = dib80xx_tuner_sleep, + .clkouttobamse = 1, + .analog_output = 1, + .i2c_address = DEFAULT_DIB0090_I2C_ADDRESS, + .use_pwm_agc = 1, + .clkoutdrive = 1, + .get_adc_power = dib8090_get_adc_power, + .freq_offset_khz_uhf = -63, + .freq_offset_khz_vhf = -143, + .wbd = dib8090_wbd_table, + .fref_clock_ratio = 6, +}; + +static int dib8096_set_param_override(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + u16 target; + int ret = 0; + enum frontend_tune_state tune_state = CT_SHUTDOWN; + u16 ltgain, rf_gain_limit; + + ret = state->set_param_save(fe); + if (ret < 0) + return ret; + + target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2; + dib8000_set_wbd_ref(fe, target); + + + if (band == BAND_CBAND) { + deb_info("tuning in CBAND - soft-AGC startup\n"); + dib0090_set_tune_state(fe, CT_AGC_START); + do { + ret = dib0090_gain_control(fe); + msleep(ret); + tune_state = dib0090_get_tune_state(fe); + if (tune_state == CT_AGC_STEP_0) + dib8000_set_gpio(fe, 6, 0, 1); + else if (tune_state == CT_AGC_STEP_1) { + dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); + if (rf_gain_limit == 0) + dib8000_set_gpio(fe, 6, 0, 0); + } + } while (tune_state < CT_AGC_STOP); + dib0090_pwm_gain_reset(fe); + dib8000_pwm_agc_reset(fe); + dib8000_set_tune_state(fe, CT_DEMOD_START); + } else { + deb_info("not tuning in CBAND - standard AGC startup\n"); + dib0090_pwm_gain_reset(fe); + } + + return 0; +} + +static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override; + return 0; +} + +static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80, 0); + + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c; + struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe_adap[0].fe, 1); + + if (fe_slave) { + tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1); + if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL) + return -ENODEV; + fe_slave->dvb = adap->fe_adap[0].fe->dvb; + fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override; + } + tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override; + + return 0; +} + +static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe_slave; + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(1000); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80, 0); + + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + if (adap->fe_adap[0].fe == NULL) + return -ENODEV; + + fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); + dib8000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); + + return fe_slave == NULL ? -ENODEV : 0; +} + +/* TFE8096P */ +static struct dibx000_agc_config dib8096p_agc_config[2] = { + { + .band_caps = BAND_UHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + P_agc_write=0 */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) + | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) + | (0 << 4) | (5 << 1) | (0 << 0), + + .inv_gain = 684, + .time_stabiliz = 10, + + .alpha_level = 0, + .thlock = 118, + + .wbd_inv = 0, + .wbd_ref = 1200, + .wbd_sel = 3, + .wbd_alpha = 5, + + .agc1_max = 65535, + .agc1_min = 0, + + .agc2_max = 32767, + .agc2_min = 0, + + .agc1_pt1 = 0, + .agc1_pt2 = 0, + .agc1_pt3 = 105, + .agc1_slope1 = 0, + .agc1_slope2 = 156, + .agc2_pt1 = 105, + .agc2_pt2 = 255, + .agc2_slope1 = 54, + .agc2_slope2 = 0, + + .alpha_mant = 28, + .alpha_exp = 26, + .beta_mant = 31, + .beta_exp = 51, + + .perform_agc_softsplit = 0, + } , { + .band_caps = BAND_FM | BAND_VHF | BAND_CBAND, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, + P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, + P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, + P_agc_write=0 */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) + | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) + | (0 << 4) | (5 << 1) | (0 << 0), + + .inv_gain = 732, + .time_stabiliz = 10, + + .alpha_level = 0, + .thlock = 118, + + .wbd_inv = 0, + .wbd_ref = 1200, + .wbd_sel = 3, + .wbd_alpha = 5, + + .agc1_max = 65535, + .agc1_min = 0, + + .agc2_max = 32767, + .agc2_min = 0, + + .agc1_pt1 = 0, + .agc1_pt2 = 0, + .agc1_pt3 = 98, + .agc1_slope1 = 0, + .agc1_slope2 = 167, + .agc2_pt1 = 98, + .agc2_pt2 = 255, + .agc2_slope1 = 52, + .agc2_slope2 = 0, + + .alpha_mant = 28, + .alpha_exp = 26, + .beta_mant = 31, + .beta_exp = 51, + + .perform_agc_softsplit = 0, + } +}; + +static struct dibx000_bandwidth_config dib8096p_clock_config_12_mhz = { + 108000, 13500, + 1, 9, 1, 0, 0, + 0, 0, 0, 0, 2, + (3 << 14) | (1 << 12) | (524 << 0), + (0 << 25) | 0, + 20199729, + 12000000, +}; + +static struct dib8000_config tfe8096p_dib8000_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .update_lna = NULL, + + .agc_config_count = 2, + .agc = dib8096p_agc_config, + .pll = &dib8096p_clock_config_12_mhz, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .agc_control = NULL, + .diversity_delay = 48, + .output_mode = OUTMODE_MPEG2_FIFO, + .enMpegOutput = 1, +}; + +static struct dib0090_wbd_slope dib8096p_wbd_table[] = { + { 380, 81, 850, 64, 540, 4}, + { 860, 51, 866, 21, 375, 4}, + {1700, 0, 250, 0, 100, 6}, + {2600, 0, 250, 0, 100, 6}, + { 0xFFFF, 0, 0, 0, 0, 0}, +}; + +static const struct dib0090_config tfe8096p_dib0090_config = { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib8096p_tuner_sleep, + .sleep = dib8096p_tuner_sleep, + + .freq_offset_khz_uhf = -143, + .freq_offset_khz_vhf = -143, + + .get_adc_power = dib8090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 1, + + .wbd = dib8096p_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + .force_cband_input = 0, +}; + +struct dibx090p_adc { + u32 freq; /* RF freq MHz */ + u32 timf; /* New Timf */ + u32 pll_loopdiv; /* New prediv */ + u32 pll_prediv; /* New loopdiv */ +}; + +struct dibx090p_adc dib8090p_adc_tab[] = { + { 50000, 17043521, 16, 3}, /* 64 MHz */ + {878000, 20199729, 9, 1}, /* 60 MHz */ + {0xffffffff, 0, 0, 0}, /* 60 MHz */ +}; + +static int dib8096p_agc_startup(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + struct dibx000_bandwidth_config pll; + u16 target; + int better_sampling_freq = 0, ret; + struct dibx090p_adc *adc_table = &dib8090p_adc_tab[0]; + + ret = state->set_param_save(fe); + if (ret < 0) + return ret; + memset(&pll, 0, sizeof(struct dibx000_bandwidth_config)); + + dib0090_pwm_gain_reset(fe); + /* dib0090_get_wbd_target is returning any possible + temperature compensated wbd-target */ + target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; + dib8000_set_wbd_ref(fe, target); + + + while (p->frequency / 1000 > adc_table->freq) { + better_sampling_freq = 1; + adc_table++; + } + + if ((adc_table->freq != 0xffffffff) && better_sampling_freq) { + pll.pll_ratio = adc_table->pll_loopdiv; + pll.pll_prediv = adc_table->pll_prediv; + dib8000_update_pll(fe, &pll); + dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc_table->timf); + } + return 0; +} + +static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80, 1); + + adap->fe_adap[0].fe = dvb_attach(dib8000_attach, + &adap->dev->i2c_adap, 0x80, &tfe8096p_dib8000_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int tfe8096p_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib8096p_get_i2c_tuner(adap->fe_adap[0].fe); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, + &tfe8096p_dib0090_config) == NULL) + return -ENODEV; + + dib8000_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096p_agc_startup; + return 0; +} + +/* STK9090M */ +static int dib90x0_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +{ + return dib9000_fw_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +} + +static int dib90x0_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + return dib9000_fw_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +} + +static int dib90x0_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + return dib9000_set_gpio(fe, 5, 0, !onoff); +} + +static int dib90x0_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return dib9000_set_gpio(fe, 0, 0, onoff); +} + +static int dib01x0_pmu_update(struct i2c_adapter *i2c, u16 *data, u8 len) +{ + u8 wb[4] = { 0xc >> 8, 0xc & 0xff, 0, 0 }; + u8 rb[2]; + struct i2c_msg msg[2] = { + {.addr = 0x1e >> 1, .flags = 0, .buf = wb, .len = 2}, + {.addr = 0x1e >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2}, + }; + u8 index_data; + + dibx000_i2c_set_speed(i2c, 250); + + if (i2c_transfer(i2c, msg, 2) != 2) + return -EIO; + + switch (rb[0] << 8 | rb[1]) { + case 0: + deb_info("Found DiB0170 rev1: This version of DiB0170 is not supported any longer.\n"); + return -EIO; + case 1: + deb_info("Found DiB0170 rev2"); + break; + case 2: + deb_info("Found DiB0190 rev2"); + break; + default: + deb_info("DiB01x0 not found"); + return -EIO; + } + + for (index_data = 0; index_data < len; index_data += 2) { + wb[2] = (data[index_data + 1] >> 8) & 0xff; + wb[3] = (data[index_data + 1]) & 0xff; + + if (data[index_data] == 0) { + wb[0] = (data[index_data] >> 8) & 0xff; + wb[1] = (data[index_data]) & 0xff; + msg[0].len = 2; + if (i2c_transfer(i2c, msg, 2) != 2) + return -EIO; + wb[2] |= rb[0]; + wb[3] |= rb[1] & ~(3 << 4); + } + + wb[0] = (data[index_data] >> 8)&0xff; + wb[1] = (data[index_data])&0xff; + msg[0].len = 4; + if (i2c_transfer(i2c, &msg[0], 1) != 1) + return -EIO; + } + return 0; +} + +static struct dib9000_config stk9090m_config = { + .output_mpeg2_in_188_bytes = 1, + .output_mode = OUTMODE_MPEG2_FIFO, + .vcxo_timer = 279620, + .timing_frequency = 20452225, + .demod_clock_khz = 60000, + .xtal_clock_khz = 30000, + .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), + .subband = { + 2, + { + { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0008 } }, /* GPIO 3 to 1 for VHF */ + { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0000 } }, /* GPIO 3 to 0 for UHF */ + { 0 }, + }, + }, + .gpio_function = { + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 }, + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 }, + }, +}; + +static struct dib9000_config nim9090md_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + .output_mode = OUTMODE_MPEG2_FIFO, + .vcxo_timer = 279620, + .timing_frequency = 20452225, + .demod_clock_khz = 60000, + .xtal_clock_khz = 30000, + .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), + }, { + .output_mpeg2_in_188_bytes = 1, + .output_mode = OUTMODE_DIVERSITY, + .vcxo_timer = 279620, + .timing_frequency = 20452225, + .demod_clock_khz = 60000, + .xtal_clock_khz = 30000, + .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), + .subband = { + 2, + { + { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0006 } }, /* GPIO 1 and 2 to 1 for VHF */ + { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0000 } }, /* GPIO 1 and 2 to 0 for UHF */ + { 0 }, + }, + }, + .gpio_function = { + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 }, + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 }, + }, + } +}; + +static struct dib0090_config dib9090_dib0090_config = { + .io.pll_bypass = 0, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 8, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 30000, + .reset = dib90x0_tuner_reset, + .sleep = dib90x0_tuner_sleep, + .clkouttobamse = 0, + .analog_output = 0, + .use_pwm_agc = 0, + .clkoutdrive = 0, + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, +}; + +static struct dib0090_config nim9090md_dib0090_config[2] = { + { + .io.pll_bypass = 0, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 8, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 30000, + .reset = dib90x0_tuner_reset, + .sleep = dib90x0_tuner_sleep, + .clkouttobamse = 1, + .analog_output = 0, + .use_pwm_agc = 0, + .clkoutdrive = 0, + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + }, { + .io.pll_bypass = 0, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 8, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 30000, + .reset = dib90x0_tuner_reset, + .sleep = dib90x0_tuner_sleep, + .clkouttobamse = 0, + .analog_output = 0, + .use_pwm_agc = 0, + .clkoutdrive = 0, + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + } +}; + + +static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct dib0700_state *st = adap->dev->priv; + u32 fw_version; + + /* Make use of the new i2c functions from FW 1.20 */ + dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); + if (fw_version >= 0x10200) + st->fw_use_new_i2c_api = 1; + dib0700_set_i2c_speed(adap->dev, 340); + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80); + + if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) { + deb_info("%s: Upload failed. (file not found?)\n", __func__); + return -ENODEV; + } else { + deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); + } + stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size; + stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data; + + adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int dib9090_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe); + u16 data_dib190[10] = { + 1, 0x1374, + 2, 0x01a2, + 7, 0x0020, + 0, 0x00ef, + 8, 0x0486, + }; + + if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &dib9090_dib0090_config) == NULL) + return -ENODEV; + i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); + if (dib01x0_pmu_update(i2c, data_dib190, 10) != 0) + return -ENODEV; + dib0700_set_i2c_speed(adap->dev, 1500); + if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0) + return -ENODEV; + release_firmware(state->frontend_firmware); + return 0; +} + +static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct dib0700_state *st = adap->dev->priv; + struct i2c_adapter *i2c; + struct dvb_frontend *fe_slave; + u32 fw_version; + + /* Make use of the new i2c functions from FW 1.20 */ + dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); + if (fw_version >= 0x10200) + st->fw_use_new_i2c_api = 1; + dib0700_set_i2c_speed(adap->dev, 340); + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) { + deb_info("%s: Upload failed. (file not found?)\n", __func__); + return -EIO; + } else { + deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); + } + nim9090md_config[0].microcode_B_fe_size = state->frontend_firmware->size; + nim9090md_config[0].microcode_B_fe_buffer = state->frontend_firmware->data; + nim9090md_config[1].microcode_B_fe_size = state->frontend_firmware->size; + nim9090md_config[1].microcode_B_fe_buffer = state->frontend_firmware->data; + + dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80); + adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]); + + if (adap->fe_adap[0].fe == NULL) + return -ENODEV; + + i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0); + dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82); + + fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]); + dib9000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); + + return fe_slave == NULL ? -ENODEV : 0; +} + +static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct i2c_adapter *i2c; + struct dvb_frontend *fe_slave; + u16 data_dib190[10] = { + 1, 0x5374, + 2, 0x01ae, + 7, 0x0020, + 0, 0x00ef, + 8, 0x0406, + }; + i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe); + if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &nim9090md_dib0090_config[0]) == NULL) + return -ENODEV; + i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); + if (dib01x0_pmu_update(i2c, data_dib190, 10) < 0) + return -ENODEV; + + dib0700_set_i2c_speed(adap->dev, 1500); + if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0) + return -ENODEV; + + fe_slave = dib9000_get_slave_frontend(adap->fe_adap[0].fe, 1); + if (fe_slave != NULL) { + i2c = dib9000_get_component_bus_interface(adap->fe_adap[0].fe); + dib9000_set_i2c_adapter(fe_slave, i2c); + + i2c = dib9000_get_tuner_interface(fe_slave); + if (dvb_attach(dib0090_fw_register, fe_slave, i2c, &nim9090md_dib0090_config[1]) == NULL) + return -ENODEV; + fe_slave->dvb = adap->fe_adap[0].fe->dvb; + dib9000_fw_set_component_bus_speed(adap->fe_adap[0].fe, 1500); + if (dib9000_firmware_post_pll_init(fe_slave) < 0) + return -ENODEV; + } + release_firmware(state->frontend_firmware); + + return 0; +} + +/* NIM7090 */ +struct dib7090p_best_adc { + u32 timf; + u32 pll_loopdiv; + u32 pll_prediv; +}; + +static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_best_adc *adc) +{ + u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1; + + u16 xtal = 12000; + u32 fcp_min = 1900; /* PLL Minimum Frequency comparator KHz */ + u32 fcp_max = 20000; /* PLL Maximum Frequency comparator KHz */ + u32 fdem_max = 76000; + u32 fdem_min = 69500; + u32 fcp = 0, fs = 0, fdem = 0; + u32 harmonic_id = 0; + + adc->pll_loopdiv = loopdiv; + adc->pll_prediv = prediv; + adc->timf = 0; + + deb_info("bandwidth = %d fdem_min =%d", fe->dtv_property_cache.bandwidth_hz, fdem_min); + + /* Find Min and Max prediv */ + while ((xtal/max_prediv) >= fcp_min) + max_prediv++; + + max_prediv--; + min_prediv = max_prediv; + while ((xtal/min_prediv) <= fcp_max) { + min_prediv--; + if (min_prediv == 1) + break; + } + deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv); + + min_prediv = 2; + + for (prediv = min_prediv ; prediv < max_prediv; prediv++) { + fcp = xtal / prediv; + if (fcp > fcp_min && fcp < fcp_max) { + for (loopdiv = 1 ; loopdiv < 64 ; loopdiv++) { + fdem = ((xtal/prediv) * loopdiv); + fs = fdem / 4; + /* test min/max system restrictions */ + + if ((fdem >= fdem_min) && (fdem <= fdem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz/1000)) { + spur = 0; + /* test fs harmonics positions */ + for (harmonic_id = (fe->dtv_property_cache.frequency / (1000*fs)) ; harmonic_id <= ((fe->dtv_property_cache.frequency / (1000*fs))+1) ; harmonic_id++) { + if (((fs*harmonic_id) >= ((fe->dtv_property_cache.frequency/1000) - (fe->dtv_property_cache.bandwidth_hz/2000))) && ((fs*harmonic_id) <= ((fe->dtv_property_cache.frequency/1000) + (fe->dtv_property_cache.bandwidth_hz/2000)))) { + spur = 1; + break; + } + } + + if (!spur) { + adc->pll_loopdiv = loopdiv; + adc->pll_prediv = prediv; + adc->timf = 2396745143UL/fdem*(1 << 9); + adc->timf += ((2396745143UL%fdem) << 9)/fdem; + deb_info("loopdiv=%i prediv=%i timf=%i", loopdiv, prediv, adc->timf); + break; + } + } + } + } + if (!spur) + break; + } + + + if (adc->pll_loopdiv == 0 && adc->pll_prediv == 0) + return -EINVAL; + else + return 0; +} + +static int dib7090_agc_startup(struct dvb_frontend *fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + struct dibx000_bandwidth_config pll; + u16 target; + struct dib7090p_best_adc adc; + int ret; + + ret = state->set_param_save(fe); + if (ret < 0) + return ret; + + memset(&pll, 0, sizeof(struct dibx000_bandwidth_config)); + dib0090_pwm_gain_reset(fe); + target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; + dib7000p_set_wbd_ref(fe, target); + + if (dib7090p_get_best_sampling(fe, &adc) == 0) { + pll.pll_ratio = adc.pll_loopdiv; + pll.pll_prediv = adc.pll_prediv; + + dib7000p_update_pll(fe, &pll); + dib7000p_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); + } + return 0; +} + +static int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart) +{ + deb_info("AGC restart callback: %d", restart); + if (restart == 0) /* before AGC startup */ + dib0090_set_dc_servo(fe, 1); + return 0; +} + +static int dib7090e_update_lna(struct dvb_frontend *fe, u16 agc_global) +{ + u16 agc1 = 0, agc2, wbd = 0, wbd_target, wbd_offset, threshold_agc1; + s16 wbd_delta; + + if ((fe->dtv_property_cache.frequency) < 400000000) + threshold_agc1 = 25000; + else + threshold_agc1 = 30000; + + wbd_target = (dib0090_get_wbd_target(fe)*8+1)/2; + wbd_offset = dib0090_get_wbd_offset(fe); + dib7000p_get_agc_values(fe, NULL, &agc1, &agc2, &wbd); + wbd_delta = (s16)wbd - (((s16)wbd_offset+10)*4) ; + + deb_info("update lna, agc_global=%d agc1=%d agc2=%d", + agc_global, agc1, agc2); + deb_info("update lna, wbd=%d wbd target=%d wbd offset=%d wbd delta=%d", + wbd, wbd_target, wbd_offset, wbd_delta); + + if ((agc1 < threshold_agc1) && (wbd_delta > 0)) { + dib0090_set_switch(fe, 1, 1, 1); + dib0090_set_vga(fe, 0); + dib0090_update_rframp_7090(fe, 0); + dib0090_update_tuning_table_7090(fe, 0); + } else { + dib0090_set_vga(fe, 1); + dib0090_update_rframp_7090(fe, 1); + dib0090_update_tuning_table_7090(fe, 1); + dib0090_set_switch(fe, 0, 0, 0); + } + + return 0; +} + +static struct dib0090_wbd_slope dib7090_wbd_table[] = { + { 380, 81, 850, 64, 540, 4}, + { 860, 51, 866, 21, 375, 4}, + {1700, 0, 250, 0, 100, 6}, + {2600, 0, 250, 0, 100, 6}, + { 0xFFFF, 0, 0, 0, 0, 0}, +}; + +static struct dib0090_wbd_slope dib7090e_wbd_table[] = { + { 380, 81, 850, 64, 540, 4}, + { 700, 51, 866, 21, 320, 4}, + { 860, 48, 666, 18, 330, 6}, + {1700, 0, 250, 0, 100, 6}, + {2600, 0, 250, 0, 100, 6}, + { 0xFFFF, 0, 0, 0, 0, 0}, +}; + +static struct dibx000_agc_config dib7090_agc_config[2] = { + { + .band_caps = BAND_UHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + + .inv_gain = 687, + .time_stabiliz = 10, + + .alpha_level = 0, + .thlock = 118, + + .wbd_inv = 0, + .wbd_ref = 1200, + .wbd_sel = 3, + .wbd_alpha = 5, + + .agc1_max = 65535, + .agc1_min = 0, + + .agc2_max = 65535, + .agc2_min = 0, + + .agc1_pt1 = 0, + .agc1_pt2 = 32, + .agc1_pt3 = 114, + .agc1_slope1 = 143, + .agc1_slope2 = 144, + .agc2_pt1 = 114, + .agc2_pt2 = 227, + .agc2_slope1 = 116, + .agc2_slope2 = 117, + + .alpha_mant = 18, + .alpha_exp = 0, + .beta_mant = 20, + .beta_exp = 59, + + .perform_agc_softsplit = 0, + } , { + .band_caps = BAND_FM | BAND_VHF | BAND_CBAND, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + + .inv_gain = 732, + .time_stabiliz = 10, + + .alpha_level = 0, + .thlock = 118, + + .wbd_inv = 0, + .wbd_ref = 1200, + .wbd_sel = 3, + .wbd_alpha = 5, + + .agc1_max = 65535, + .agc1_min = 0, + + .agc2_max = 65535, + .agc2_min = 0, + + .agc1_pt1 = 0, + .agc1_pt2 = 0, + .agc1_pt3 = 98, + .agc1_slope1 = 0, + .agc1_slope2 = 167, + .agc2_pt1 = 98, + .agc2_pt2 = 255, + .agc2_slope1 = 104, + .agc2_slope2 = 0, + + .alpha_mant = 18, + .alpha_exp = 0, + .beta_mant = 20, + .beta_exp = 59, + + .perform_agc_softsplit = 0, + } +}; + +static struct dibx000_bandwidth_config dib7090_clock_config_12_mhz = { + 60000, 15000, + 1, 5, 0, 0, 0, + 0, 0, 1, 1, 2, + (3 << 14) | (1 << 12) | (524 << 0), + (0 << 25) | 0, + 20452225, + 15000000, +}; + +static struct dib7000p_config nim7090_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = NULL, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_FIFO, + .enMpegOutput = 1, +}; + +static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = NULL, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, + .default_i2c_addr = 0x90, + .enMpegOutput = 1, + }, { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = NULL, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, + .default_i2c_addr = 0x92, + .enMpegOutput = 0, + } +}; + +static struct dib7000p_config tfe7090e_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = dib7090e_update_lna, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_FIFO, + .enMpegOutput = 1, +}; + +static const struct dib0090_config nim7090_dib0090_config = { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, +}; + +static const struct dib0090_config tfe7090e_dib0090_config = { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090e_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + .force_cband_input = 1, + .is_dib7090e = 1, +}; + +static struct dib7000p_config tfe7790e_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = dib7090e_update_lna, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, + .enMpegOutput = 1, +}; + +static const struct dib0090_config tfe7790e_dib0090_config = { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090e_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + .force_cband_input = 1, + .is_dib7090e = 1, + .force_crystal_mode = 1, +}; + +static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { + { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = 50, + .freq_offset_khz_vhf = 70, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + }, { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = -50, + .freq_offset_khz_vhf = -70, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + } +}; + +static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int nim7090_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &nim7090_dib0090_config) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; +} + +static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_state *st = adap->dev->priv; + + /* The TFE7090 requires the dib0700 to not be in master mode */ + st->disable_streaming_master_mode = 1; + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + /* initialize IC 0 */ + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, &tfe7090pvr_dib7000p_config[0]) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } + + dib0700_set_i2c_speed(adap->dev, 340); + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); + if (adap->fe_adap[0].fe == NULL) + return -ENODEV; + + dib7090_slave_reset(adap->fe_adap[0].fe); + + return 0; +} + +static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *i2c; + + if (adap->dev->adapter[0].fe_adap[0].fe == NULL) { + err("the master dib7090 has to be initialized first"); + return -ENODEV; /* the master device has not been initialized */ + } + + i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); + if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); + dib0700_set_i2c_speed(adap->dev, 200); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; +} + +static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; +} + +static int tfe7090e_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, + 1, 0x10, &tfe7090e_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + 0x80, &tfe7090e_dib7000p_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int tfe7790e_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_state *st = adap->dev->priv; + + /* The TFE7790E requires the dib0700 to not be in master mode */ + st->disable_streaming_master_mode = 1; + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(20); + dib0700_ctrl_clock(adap->dev, 72, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, + 1, 0x10, &tfe7790e_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + 0x80, &tfe7790e_dib7000p_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int tfe7790e_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = + dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, + &tfe7790e_dib0090_config) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; +} + +static int tfe7090e_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = + dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, + &tfe7090e_dib0090_config) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; +} + +/* STK7070PD */ +static struct dib7000p_config stk7070pd_dib7000p_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &dib7070_agc_config, + .bw = &dib7070_bw_config_12_mhz, + .tuner_is_baseband = 1, + .spur_protect = 1, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + }, { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &dib7070_agc_config, + .bw = &dib7070_bw_config_12_mhz, + .tuner_is_baseband = 1, + .spur_protect = 1, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + } +}; + +static void stk7070pd_init(struct dvb_usb_device *dev) +{ + dib0700_set_gpio(dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(dev, 72, 1); + + msleep(10); + dib0700_set_gpio(dev, GPIO10, GPIO_OUT, 1); +} + +static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) +{ + stk7070pd_init(adap->dev); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, + stk7070pd_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) +{ + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int novatd_read_status_override(struct dvb_frontend *fe, + fe_status_t *stat) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *dev = adap->dev; + struct dib0700_state *state = dev->priv; + int ret; + + ret = state->read_status(fe, stat); + + if (!ret) + dib0700_set_gpio(dev, adap->id == 0 ? GPIO1 : GPIO0, GPIO_OUT, + !!(*stat & FE_HAS_LOCK)); + + return ret; +} + +static int novatd_sleep_override(struct dvb_frontend* fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *dev = adap->dev; + struct dib0700_state *state = dev->priv; + + /* turn off LED */ + dib0700_set_gpio(dev, adap->id == 0 ? GPIO1 : GPIO0, GPIO_OUT, 0); + + return state->sleep(fe); +} + +/** + * novatd_frontend_attach - Nova-TD specific attach + * + * Nova-TD has GPIO0, 1 and 2 for LEDs. So do not fiddle with them except for + * information purposes. + */ +static int novatd_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *dev = adap->dev; + struct dib0700_state *st = dev->priv; + + if (adap->id == 0) { + stk7070pd_init(dev); + + /* turn the power LED on, the other two off (just in case) */ + dib0700_set_gpio(dev, GPIO0, GPIO_OUT, 0); + dib0700_set_gpio(dev, GPIO1, GPIO_OUT, 0); + dib0700_set_gpio(dev, GPIO2, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&dev->i2c_adap, 2, 18, + stk7070pd_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &dev->i2c_adap, + adap->id == 0 ? 0x80 : 0x82, + &stk7070pd_dib7000p_config[adap->id]); + + if (adap->fe_adap[0].fe == NULL) + return -ENODEV; + + st->read_status = adap->fe_adap[0].fe->ops.read_status; + adap->fe_adap[0].fe->ops.read_status = novatd_read_status_override; + st->sleep = adap->fe_adap[0].fe->ops.sleep; + adap->fe_adap[0].fe->ops.sleep = novatd_sleep_override; + + return 0; +} + +/* S5H1411 */ +static struct s5h1411_config pinnacle_801e_config = { + .output_mode = S5H1411_PARALLEL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK, + .qam_if = S5H1411_IF_44000, + .vsb_if = S5H1411_IF_44000, + .inversion = S5H1411_INVERSION_OFF, + .status_mode = S5H1411_DEMODLOCKING +}; + +/* Pinnacle PCTV HD Pro 801e GPIOs map: + GPIO0 - currently unknown + GPIO1 - xc5000 tuner reset + GPIO2 - CX25843 sleep + GPIO3 - currently unknown + GPIO4 - currently unknown + GPIO6 - currently unknown + GPIO7 - currently unknown + GPIO9 - currently unknown + GPIO10 - CX25843 reset + */ +static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_state *st = adap->dev->priv; + + /* Make use of the new i2c functions from FW 1.20 */ + st->fw_use_new_i2c_api = 1; + + /* The s5h1411 requires the dib0700 to not be in master mode */ + st->disable_streaming_master_mode = 1; + + /* All msleep values taken from Windows USB trace */ + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0); + dib0700_set_gpio(adap->dev, GPIO3, GPIO_OUT, 0); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(400); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(60); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(30); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 0); + msleep(30); + + /* Put the CX25843 to sleep for now since we're in digital mode */ + dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1); + + /* GPIOs are initialized, do the attach */ + adap->fe_adap[0].fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config, + &adap->dev->i2c_adap); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int dib0700_xc5000_tuner_callback(void *priv, int component, + int command, int arg) +{ + struct dvb_usb_adapter *adap = priv; + + if (command == XC5000_TUNER_RESET) { + /* Reset the tuner */ + dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 0); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 1); + msleep(10); + } else { + err("xc5000: unknown tuner callback command: %d\n", command); + return -EINVAL; + } + + return 0; +} + +static struct xc5000_config s5h1411_xc5000_tunerconfig = { + .i2c_address = 0x64, + .if_khz = 5380, +}; + +static int xc5000_tuner_attach(struct dvb_usb_adapter *adap) +{ + /* FIXME: generalize & move to common area */ + adap->fe_adap[0].fe->callback = dib0700_xc5000_tuner_callback; + + return dvb_attach(xc5000_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, + &s5h1411_xc5000_tunerconfig) + == NULL ? -ENODEV : 0; +} + +static int dib0700_xc4000_tuner_callback(void *priv, int component, + int command, int arg) +{ + struct dvb_usb_adapter *adap = priv; + + if (command == XC4000_TUNER_RESET) { + /* Reset the tuner */ + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); + msleep(10); + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + } else { + err("xc4000: unknown tuner callback command: %d\n", command); + return -EINVAL; + } + + return 0; +} + +static struct dibx000_agc_config stk7700p_7000p_xc4000_agc_config = { + .band_caps = BAND_UHF | BAND_VHF, + .setup = 0x64, + .inv_gain = 0x02c8, + .time_stabiliz = 0x15, + .alpha_level = 0x00, + .thlock = 0x76, + .wbd_inv = 0x01, + .wbd_ref = 0x0b33, + .wbd_sel = 0x00, + .wbd_alpha = 0x02, + .agc1_max = 0x00, + .agc1_min = 0x00, + .agc2_max = 0x9b26, + .agc2_min = 0x26ca, + .agc1_pt1 = 0x00, + .agc1_pt2 = 0x00, + .agc1_pt3 = 0x00, + .agc1_slope1 = 0x00, + .agc1_slope2 = 0x00, + .agc2_pt1 = 0x00, + .agc2_pt2 = 0x80, + .agc2_slope1 = 0x1d, + .agc2_slope2 = 0x1d, + .alpha_mant = 0x11, + .alpha_exp = 0x1b, + .beta_mant = 0x17, + .beta_exp = 0x33, + .perform_agc_softsplit = 0x00, +}; + +static struct dibx000_bandwidth_config stk7700p_xc4000_pll_config = { + 60000, 30000, /* internal, sampling */ + 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */ + 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, */ + /* ADClkSrc, modulo */ + (3 << 14) | (1 << 12) | 524, /* sad_cfg: refsel, sel, freq_15k */ + 39370534, /* ifreq */ + 20452225, /* timf */ + 30000000 /* xtal */ +}; + +/* FIXME: none of these inputs are validated yet */ +static struct dib7000p_config pctv_340e_config = { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &stk7700p_7000p_xc4000_agc_config, + .bw = &stk7700p_xc4000_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +}; + +/* PCTV 340e GPIOs map: + dib0700: + GPIO2 - CX25843 sleep + GPIO3 - CS5340 reset + GPIO5 - IRD + GPIO6 - Power Supply + GPIO8 - LNA (1=off 0=on) + GPIO10 - CX25843 reset + dib7000: + GPIO8 - xc4000 reset + */ +static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_state *st = adap->dev->priv; + + /* Power Supply on */ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + msleep(50); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(100); /* Allow power supply to settle before probing */ + + /* cx25843 reset */ + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(1); /* cx25843 datasheet say 350us required */ + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + + /* LNA off for now */ + dib0700_set_gpio(adap->dev, GPIO8, GPIO_OUT, 1); + + /* Put the CX25843 to sleep for now since we're in digital mode */ + dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1); + + /* FIXME: not verified yet */ + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(500); + + if (dib7000pc_detection(&adap->dev->i2c_adap) == 0) { + /* Demodulator not found for some reason? */ + return -ENODEV; + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12, + &pctv_340e_config); + st->is_dib7000pc = 1; + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static struct xc4000_config dib7000p_xc4000_tunerconfig = { + .i2c_address = 0x61, + .default_pm = 1, + .dvb_amplitude = 0, + .set_smoothedcvbs = 0, + .if_khz = 5400 +}; + +static int xc4000_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *tun_i2c; + + /* The xc4000 is not on the main i2c bus */ + tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, + DIBX000_I2C_INTERFACE_TUNER, 1); + if (tun_i2c == NULL) { + printk(KERN_ERR "Could not reach tuner i2c bus\n"); + return 0; + } + + /* Setup the reset callback */ + adap->fe_adap[0].fe->callback = dib0700_xc4000_tuner_callback; + + return dvb_attach(xc4000_attach, adap->fe_adap[0].fe, tun_i2c, + &dib7000p_xc4000_tunerconfig) + == NULL ? -ENODEV : 0; +} + +static struct lgdt3305_config hcw_lgdt3305_config = { + .i2c_addr = 0x0e, + .mpeg_mode = LGDT3305_MPEG_PARALLEL, + .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_LOW, + .deny_i2c_rptr = 0, + .spectral_inversion = 1, + .qam_if_khz = 6000, + .vsb_if_khz = 6000, + .usref_8vsb = 0x0500, +}; + +static struct mxl5007t_config hcw_mxl5007t_config = { + .xtal_freq_hz = MxL_XTAL_25_MHZ, + .if_freq_hz = MxL_IF_6_MHZ, + .invert_if = 1, +}; + +/* TIGER-ATSC map: + GPIO0 - LNA_CTR (H: LNA power enabled, L: LNA power disabled) + GPIO1 - ANT_SEL (H: VPA, L: MCX) + GPIO4 - SCL2 + GPIO6 - EN_TUNER + GPIO7 - SDA2 + GPIO10 - DEM_RST + + MXL is behind LG's i2c repeater. LG is on SCL2/SDA2 gpios on the DIB + */ +static int lgdt3305_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_state *st = adap->dev->priv; + + /* Make use of the new i2c functions from FW 1.20 */ + st->fw_use_new_i2c_api = 1; + + st->disable_streaming_master_mode = 1; + + /* fe power enable */ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + msleep(30); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(30); + + /* demod reset */ + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(30); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(30); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(30); + + adap->fe_adap[0].fe = dvb_attach(lgdt3305_attach, + &hcw_lgdt3305_config, + &adap->dev->i2c_adap); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int mxl5007t_tuner_attach(struct dvb_usb_adapter *adap) +{ + return dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0x60, + &hcw_mxl5007t_config) == NULL ? -ENODEV : 0; +} + + +/* DVB-USB and USB stuff follows */ +struct usb_device_id dib0700_usb_id_table[] = { +/* 0 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) }, +/* 5 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) }, + { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500) }, + { USB_DEVICE(USB_VID_UNIWILL, USB_PID_UNIWILL_STK7700P) }, + { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) }, +/* 10 */{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV2000E) }, + { USB_DEVICE(USB_VID_TERRATEC, + USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700D) }, +/* 15 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7070P) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV_DVB_T_FLASH) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7070PD) }, + { USB_DEVICE(USB_VID_PINNACLE, + USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T) }, + { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500_PC) }, +/* 20 */{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_EXPRESS) }, + { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U7000) }, + { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14BR) }, + { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000) }, + { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100) }, +/* 25 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_3) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_MYTV_T) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_HT_USB_XE) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_EXPRESSCARD_320CX) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV72E) }, +/* 30 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73E) }, + { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_EC372S) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_HT_EXPRESS) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS) }, + { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) }, +/* 35 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_3) }, + { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U8000) }, + { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700PH) }, + { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000H) }, +/* 40 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E_SE) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_EXPRESS) }, + { USB_DEVICE(USB_VID_TERRATEC, + USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2) }, + { USB_DEVICE(USB_VID_SONY, USB_PID_SONY_PLAYTV) }, +/* 45 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_PD378S) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_TIGER_ATSC) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_TIGER_ATSC_B210) }, + { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_MC770) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT) }, +/* 50 */{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_Dlx) }, + { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_H) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T3) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T5) }, + { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) }, +/* 55 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73A) }, + { USB_DEVICE(USB_VID_PCTV, USB_PID_PINNACLE_PCTV73ESE) }, + { USB_DEVICE(USB_VID_PCTV, USB_PID_PINNACLE_PCTV282E) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) }, +/* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) }, + { USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x000, 0x3f00) }, + { USB_DEVICE(USB_VID_EVOLUTEPC, USB_PID_TVWAY_PLUS) }, +/* 65 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096GP) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DIVERSITY) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090M) }, +/* 70 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM8096MD) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090MD) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM7090) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090PVR) }, + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2) }, +/* 75 */{ USB_DEVICE(USB_VID_MEDION, USB_PID_CREATIX_CTX1921) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E_SE) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090E) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790E) }, +/* 80 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, + { 0 } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); + +#define DIB0700_DEFAULT_DEVICE_PROPERTIES \ + .caps = DVB_USB_IS_AN_I2C_ADAPTER, \ + .usb_ctrl = DEVICE_SPECIFIC, \ + .firmware = "dvb-usb-dib0700-1.20.fw", \ + .download_firmware = dib0700_download_firmware, \ + .no_reconnect = 1, \ + .size_of_priv = sizeof(struct dib0700_state), \ + .i2c_algo = &dib0700_i2c_algo, \ + .identify_state = dib0700_identify_state + +#define DIB0700_DEFAULT_STREAMING_CONFIG(ep) \ + .streaming_ctrl = dib0700_streaming_ctrl, \ + .stream = { \ + .type = USB_BULK, \ + .count = 4, \ + .endpoint = ep, \ + .u = { \ + .bulk = { \ + .buffersize = 39480, \ + } \ + } \ + } + +struct dvb_usb_device_properties dib0700_devices[] = { + { + DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk7700p_pid_filter, + .pid_filter_ctrl = stk7700p_pid_filter_ctrl, + .frontend_attach = stk7700p_frontend_attach, + .tuner_attach = stk7700p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + }, + }, + + .num_device_descs = 8, + .devices = { + { "DiBcom STK7700P reference design", + { &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] }, + { NULL }, + }, + { "Hauppauge Nova-T Stick", + { &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL }, + { NULL }, + }, + { "AVerMedia AVerTV DVB-T Volar", + { &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] }, + { NULL }, + }, + { "Compro Videomate U500", + { &dib0700_usb_id_table[6], &dib0700_usb_id_table[19] }, + { NULL }, + }, + { "Uniwill STK7700P based (Hama and others)", + { &dib0700_usb_id_table[7], NULL }, + { NULL }, + }, + { "Leadtek Winfast DTV Dongle (STK7700P based)", + { &dib0700_usb_id_table[8], &dib0700_usb_id_table[34] }, + { NULL }, + }, + { "AVerMedia AVerTV DVB-T Express", + { &dib0700_usb_id_table[20] }, + { NULL }, + }, + { "Gigabyte U7000", + { &dib0700_usb_id_table[21], NULL }, + { NULL }, + } + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = bristol_frontend_attach, + .tuner_attach = bristol_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + }, { + .num_frontends = 1, + .fe = {{ + .frontend_attach = bristol_frontend_attach, + .tuner_attach = bristol_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + } + }, + + .num_device_descs = 1, + .devices = { + { "Hauppauge Nova-T 500 Dual DVB-T", + { &dib0700_usb_id_table[2], &dib0700_usb_id_table[3], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7700d_frontend_attach, + .tuner_attach = stk7700d_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + }, { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7700d_frontend_attach, + .tuner_attach = stk7700d_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + } + }, + + .num_device_descs = 5, + .devices = { + { "Pinnacle PCTV 2000e", + { &dib0700_usb_id_table[11], NULL }, + { NULL }, + }, + { "Terratec Cinergy DT XS Diversity", + { &dib0700_usb_id_table[12], NULL }, + { NULL }, + }, + { "Hauppauge Nova-TD Stick/Elgato Eye-TV Diversity", + { &dib0700_usb_id_table[13], NULL }, + { NULL }, + }, + { "DiBcom STK7700D reference design", + { &dib0700_usb_id_table[14], NULL }, + { NULL }, + }, + { "YUAN High-Tech DiBcom STK7700D", + { &dib0700_usb_id_table[55], NULL }, + { NULL }, + }, + + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7700P2_frontend_attach, + .tuner_attach = stk7700d_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + }, + }, + + .num_device_descs = 3, + .devices = { + { "ASUS My Cinema U3000 Mini DVBT Tuner", + { &dib0700_usb_id_table[23], NULL }, + { NULL }, + }, + { "Yuan EC372S", + { &dib0700_usb_id_table[31], NULL }, + { NULL }, + }, + { "Terratec Cinergy T Express", + { &dib0700_usb_id_table[42], NULL }, + { NULL }, + } + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070p_frontend_attach, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 12, + .devices = { + { "DiBcom STK7070P reference design", + { &dib0700_usb_id_table[15], NULL }, + { NULL }, + }, + { "Pinnacle PCTV DVB-T Flash Stick", + { &dib0700_usb_id_table[16], NULL }, + { NULL }, + }, + { "Artec T14BR DVB-T", + { &dib0700_usb_id_table[22], NULL }, + { NULL }, + }, + { "ASUS My Cinema U3100 Mini DVBT Tuner", + { &dib0700_usb_id_table[24], NULL }, + { NULL }, + }, + { "Hauppauge Nova-T Stick", + { &dib0700_usb_id_table[25], NULL }, + { NULL }, + }, + { "Hauppauge Nova-T MyTV.t", + { &dib0700_usb_id_table[26], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 72e", + { &dib0700_usb_id_table[29], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 73e", + { &dib0700_usb_id_table[30], NULL }, + { NULL }, + }, + { "Elgato EyeTV DTT", + { &dib0700_usb_id_table[49], NULL }, + { NULL }, + }, + { "Yuan PD378S", + { &dib0700_usb_id_table[45], NULL }, + { NULL }, + }, + { "Elgato EyeTV Dtt Dlx PD378S", + { &dib0700_usb_id_table[50], NULL }, + { NULL }, + }, + { "Elgato EyeTV DTT rev. 2", + { &dib0700_usb_id_table[81], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070p_frontend_attach, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 3, + .devices = { + { "Pinnacle PCTV 73A", + { &dib0700_usb_id_table[56], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 73e SE", + { &dib0700_usb_id_table[57], &dib0700_usb_id_table[65], NULL }, + { NULL }, + }, + { "Pinnacle PCTV 282e", + { &dib0700_usb_id_table[58], &dib0700_usb_id_table[66], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = novatd_frontend_attach, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = novatd_frontend_attach, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + } + }, + + .num_device_descs = 1, + .devices = { + { "Hauppauge Nova-TD Stick (52009)", + { &dib0700_usb_id_table[35], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach0, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach1, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + } + }, + + .num_device_descs = 5, + .devices = { + { "DiBcom STK7070PD reference design", + { &dib0700_usb_id_table[17], NULL }, + { NULL }, + }, + { "Pinnacle PCTV Dual DVB-T Diversity Stick", + { &dib0700_usb_id_table[18], NULL }, + { NULL }, + }, + { "Hauppauge Nova-TD-500 (84xxx)", + { &dib0700_usb_id_table[36], NULL }, + { NULL }, + }, + { "Terratec Cinergy DT USB XS Diversity/ T5", + { &dib0700_usb_id_table[43], + &dib0700_usb_id_table[53], NULL}, + { NULL }, + }, + { "Sony PlayTV", + { &dib0700_usb_id_table[44], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach0, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach1, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + } + }, + + .num_device_descs = 1, + .devices = { + { "Elgato EyeTV Diversity", + { &dib0700_usb_id_table[68], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_NEC_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7700ph_frontend_attach, + .tuner_attach = stk7700ph_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct + dib0700_adapter_state), + }, + }, + + .num_device_descs = 9, + .devices = { + { "Terratec Cinergy HT USB XE", + { &dib0700_usb_id_table[27], NULL }, + { NULL }, + }, + { "Pinnacle Expresscard 320cx", + { &dib0700_usb_id_table[28], NULL }, + { NULL }, + }, + { "Terratec Cinergy HT Express", + { &dib0700_usb_id_table[32], NULL }, + { NULL }, + }, + { "Gigabyte U8000-RH", + { &dib0700_usb_id_table[37], NULL }, + { NULL }, + }, + { "YUAN High-Tech STK7700PH", + { &dib0700_usb_id_table[38], NULL }, + { NULL }, + }, + { "Asus My Cinema-U3000Hybrid", + { &dib0700_usb_id_table[39], NULL }, + { NULL }, + }, + { "YUAN High-Tech MC770", + { &dib0700_usb_id_table[48], NULL }, + { NULL }, + }, + { "Leadtek WinFast DTV Dongle H", + { &dib0700_usb_id_table[51], NULL }, + { NULL }, + }, + { "YUAN High-Tech STK7700D", + { &dib0700_usb_id_table[54], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = s5h1411_frontend_attach, + .tuner_attach = xc5000_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct + dib0700_adapter_state), + }, + }, + + .num_device_descs = 2, + .devices = { + { "Pinnacle PCTV HD Pro USB Stick", + { &dib0700_usb_id_table[40], NULL }, + { NULL }, + }, + { "Pinnacle PCTV HD USB Stick", + { &dib0700_usb_id_table[41], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = lgdt3305_frontend_attach, + .tuner_attach = mxl5007t_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct + dib0700_adapter_state), + }, + }, + + .num_device_descs = 2, + .devices = { + { "Hauppauge ATSC MiniCard (B200)", + { &dib0700_usb_id_table[46], NULL }, + { NULL }, + }, + { "Hauppauge ATSC MiniCard (B210)", + { &dib0700_usb_id_table[47], NULL }, + { NULL }, + }, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7770p_frontend_attach, + .tuner_attach = dib7770p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 4, + .devices = { + { "DiBcom STK7770P reference design", + { &dib0700_usb_id_table[59], NULL }, + { NULL }, + }, + { "Terratec Cinergy T USB XXS (HD)/ T3", + { &dib0700_usb_id_table[33], + &dib0700_usb_id_table[52], + &dib0700_usb_id_table[60], NULL}, + { NULL }, + }, + { "TechniSat AirStar TeleStick 2", + { &dib0700_usb_id_table[74], NULL }, + { NULL }, + }, + { "Medion CTX1921 DVB-T USB", + { &dib0700_usb_id_table[75], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk80xx_pid_filter, + .pid_filter_ctrl = stk80xx_pid_filter_ctrl, + .frontend_attach = stk807x_frontend_attach, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 3, + .devices = { + { "DiBcom STK807xP reference design", + { &dib0700_usb_id_table[62], NULL }, + { NULL }, + }, + { "Prolink Pixelview SBTVD", + { &dib0700_usb_id_table[63], NULL }, + { NULL }, + }, + { "EvolutePC TVWay+", + { &dib0700_usb_id_table[64], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_NEC_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk80xx_pid_filter, + .pid_filter_ctrl = stk80xx_pid_filter_ctrl, + .frontend_attach = stk807xpvr_frontend_attach0, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk80xx_pid_filter, + .pid_filter_ctrl = stk80xx_pid_filter_ctrl, + .frontend_attach = stk807xpvr_frontend_attach1, + .tuner_attach = dib807x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK807xPVR reference design", + { &dib0700_usb_id_table[61], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk80xx_pid_filter, + .pid_filter_ctrl = stk80xx_pid_filter_ctrl, + .frontend_attach = stk809x_frontend_attach, + .tuner_attach = dib809x_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK8096GP reference design", + { &dib0700_usb_id_table[67], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = dib90x0_pid_filter, + .pid_filter_ctrl = dib90x0_pid_filter_ctrl, + .frontend_attach = stk9090m_frontend_attach, + .tuner_attach = dib9090_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK9090M reference design", + { &dib0700_usb_id_table[69], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk80xx_pid_filter, + .pid_filter_ctrl = stk80xx_pid_filter_ctrl, + .frontend_attach = nim8096md_frontend_attach, + .tuner_attach = nim8096md_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom NIM8096MD reference design", + { &dib0700_usb_id_table[70], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = dib90x0_pid_filter, + .pid_filter_ctrl = dib90x0_pid_filter_ctrl, + .frontend_attach = nim9090md_frontend_attach, + .tuner_attach = nim9090md_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom NIM9090MD reference design", + { &dib0700_usb_id_table[71], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = nim7090_frontend_attach, + .tuner_attach = nim7090_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom NIM7090 reference design", + { &dib0700_usb_id_table[72], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = tfe7090pvr_frontend0_attach, + .tuner_attach = tfe7090pvr_tuner0_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = tfe7090pvr_frontend1_attach, + .tuner_attach = tfe7090pvr_tuner1_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom TFE7090PVR reference design", + { &dib0700_usb_id_table[73], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = pctv340e_frontend_attach, + .tuner_attach = xc4000_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct + dib0700_adapter_state), + }, + }, + + .num_device_descs = 2, + .devices = { + { "Pinnacle PCTV 340e HD Pro USB Stick", + { &dib0700_usb_id_table[76], NULL }, + { NULL }, + }, + { "Pinnacle PCTV Hybrid Stick Solo", + { &dib0700_usb_id_table[77], NULL }, + { NULL }, + }, + }, + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = tfe7090e_frontend_attach, + .tuner_attach = tfe7090e_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + } }, + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom TFE7090E reference design", + { &dib0700_usb_id_table[78], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = tfe7790e_frontend_attach, + .tuner_attach = tfe7790e_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + } }, + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom TFE7790E reference design", + { &dib0700_usb_id_table[79], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk80xx_pid_filter, + .pid_filter_ctrl = stk80xx_pid_filter_ctrl, + .frontend_attach = tfe8096p_frontend_attach, + .tuner_attach = tfe8096p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + } }, + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom TFE8096P reference design", + { &dib0700_usb_id_table[80], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, +}; + +int dib0700_device_count = ARRAY_SIZE(dib0700_devices); diff --git a/drivers/media/usb/dvb-usb/dib07x0.h b/drivers/media/usb/dvb-usb/dib07x0.h new file mode 100644 index 000000000000..7e62c1018520 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dib07x0.h @@ -0,0 +1,21 @@ +#ifndef _DIB07X0_H_ +#define _DIB07X0_H_ + +enum dib07x0_gpios { + GPIO0 = 0, + GPIO1 = 2, + GPIO2 = 3, + GPIO3 = 4, + GPIO4 = 5, + GPIO5 = 6, + GPIO6 = 8, + GPIO7 = 10, + GPIO8 = 11, + GPIO9 = 14, + GPIO10 = 15, +}; + +#define GPIO_IN 0 +#define GPIO_OUT 1 + +#endif diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c new file mode 100644 index 000000000000..a76bbb29ca36 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -0,0 +1,479 @@ +/* Common methods for dibusb-based-receivers. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (|-able))." DVB_USB_DEBUG_STATUS); +MODULE_LICENSE("GPL"); + +#define deb_info(args...) dprintk(debug,0x01,args) + +/* common stuff used by the different dibusb modules */ +int dibusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + if (adap->priv != NULL) { + struct dibusb_state *st = adap->priv; + if (st->ops.fifo_ctrl != NULL) + if (st->ops.fifo_ctrl(adap->fe_adap[0].fe, onoff)) { + err("error while controlling the fifo of the demod."); + return -ENODEV; + } + } + return 0; +} +EXPORT_SYMBOL(dibusb_streaming_ctrl); + +int dibusb_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) +{ + if (adap->priv != NULL) { + struct dibusb_state *st = adap->priv; + if (st->ops.pid_ctrl != NULL) + st->ops.pid_ctrl(adap->fe_adap[0].fe, + index, pid, onoff); + } + return 0; +} +EXPORT_SYMBOL(dibusb_pid_filter); + +int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + if (adap->priv != NULL) { + struct dibusb_state *st = adap->priv; + if (st->ops.pid_parse != NULL) + if (st->ops.pid_parse(adap->fe_adap[0].fe, onoff) < 0) + err("could not handle pid_parser"); + } + return 0; +} +EXPORT_SYMBOL(dibusb_pid_filter_ctrl); + +int dibusb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b[3]; + int ret; + b[0] = DIBUSB_REQ_SET_IOCTL; + b[1] = DIBUSB_IOCTL_CMD_POWER_MODE; + b[2] = onoff ? DIBUSB_IOCTL_POWER_WAKEUP : DIBUSB_IOCTL_POWER_SLEEP; + ret = dvb_usb_generic_write(d,b,3); + msleep(10); + return ret; +} +EXPORT_SYMBOL(dibusb_power_ctrl); + +int dibusb2_0_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + u8 b[3] = { 0 }; + int ret; + + if ((ret = dibusb_streaming_ctrl(adap,onoff)) < 0) + return ret; + + if (onoff) { + b[0] = DIBUSB_REQ_SET_STREAMING_MODE; + b[1] = 0x00; + if ((ret = dvb_usb_generic_write(adap->dev,b,2)) < 0) + return ret; + } + + b[0] = DIBUSB_REQ_SET_IOCTL; + b[1] = onoff ? DIBUSB_IOCTL_CMD_ENABLE_STREAM : DIBUSB_IOCTL_CMD_DISABLE_STREAM; + return dvb_usb_generic_write(adap->dev,b,3); +} +EXPORT_SYMBOL(dibusb2_0_streaming_ctrl); + +int dibusb2_0_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + if (onoff) { + u8 b[3] = { DIBUSB_REQ_SET_IOCTL, DIBUSB_IOCTL_CMD_POWER_MODE, DIBUSB_IOCTL_POWER_WAKEUP }; + return dvb_usb_generic_write(d,b,3); + } else + return 0; +} +EXPORT_SYMBOL(dibusb2_0_power_ctrl); + +static int dibusb_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */ + /* write only ? */ + int wo = (rbuf == NULL || rlen == 0), + len = 2 + wlen + (wo ? 0 : 2); + + sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ; + sndbuf[1] = (addr << 1) | (wo ? 0 : 1); + + memcpy(&sndbuf[2],wbuf,wlen); + + if (!wo) { + sndbuf[wlen+2] = (rlen >> 8) & 0xff; + sndbuf[wlen+3] = rlen & 0xff; + } + + return dvb_usb_generic_rw(d,sndbuf,len,rbuf,rlen,0); +} + +/* + * I2C master xfer function + */ +static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i].flags & I2C_M_RD) == 0 + && (msg[i+1].flags & I2C_M_RD)) { + if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len, + msg[i+1].buf,msg[i+1].len) < 0) + break; + i++; + } else if ((msg[i].flags & I2C_M_RD) == 0) { + if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0) + break; + } else if (msg[i].addr != 0x50) { + /* 0x50 is the address of the eeprom - we need to protect it + * from dibusb's bad i2c implementation: reads without + * writing the offset before are forbidden */ + if (dibusb_i2c_msg(d, msg[i].addr, NULL, 0, msg[i].buf, msg[i].len) < 0) + break; + } + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 dibusb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +struct i2c_algorithm dibusb_i2c_algo = { + .master_xfer = dibusb_i2c_xfer, + .functionality = dibusb_i2c_func, +}; +EXPORT_SYMBOL(dibusb_i2c_algo); + +int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val) +{ + u8 wbuf[1] = { offs }; + return dibusb_i2c_msg(d, 0x50, wbuf, 1, val, 1); +} +EXPORT_SYMBOL(dibusb_read_eeprom_byte); + +/* 3000MC/P stuff */ +// Config Adjacent channels Perf -cal22 +static struct dibx000_agc_config dib3000p_mt2060_agc_config = { + .band_caps = BAND_VHF | BAND_UHF, + .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), + + .agc1_max = 48497, + .agc1_min = 23593, + .agc2_max = 46531, + .agc2_min = 24904, + + .agc1_pt1 = 0x65, + .agc1_pt2 = 0x69, + + .agc1_slope1 = 0x51, + .agc1_slope2 = 0x27, + + .agc2_pt1 = 0, + .agc2_pt2 = 0x33, + + .agc2_slope1 = 0x35, + .agc2_slope2 = 0x37, +}; + +static struct dib3000mc_config stk3000p_dib3000p_config = { + &dib3000p_mt2060_agc_config, + + .max_time = 0x196, + .ln_adc_level = 0x1cc7, + + .output_mpeg2_in_188_bytes = 1, + + .agc_command1 = 1, + .agc_command2 = 1, +}; + +static struct dibx000_agc_config dib3000p_panasonic_agc_config = { + .band_caps = BAND_VHF | BAND_UHF, + .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), + + .agc1_max = 56361, + .agc1_min = 22282, + .agc2_max = 47841, + .agc2_min = 36045, + + .agc1_pt1 = 0x3b, + .agc1_pt2 = 0x6b, + + .agc1_slope1 = 0x55, + .agc1_slope2 = 0x1d, + + .agc2_pt1 = 0, + .agc2_pt2 = 0x0a, + + .agc2_slope1 = 0x95, + .agc2_slope2 = 0x1e, +}; + +#if defined(CONFIG_DVB_DIB3000MC) || \ + (defined(CONFIG_DVB_DIB3000MC_MODULE) && defined(MODULE)) + +static struct dib3000mc_config mod3000p_dib3000p_config = { + &dib3000p_panasonic_agc_config, + + .max_time = 0x51, + .ln_adc_level = 0x1cc7, + + .output_mpeg2_in_188_bytes = 1, + + .agc_command1 = 1, + .agc_command2 = 1, +}; + +int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON && + adap->dev->udev->descriptor.idProduct == + USB_PID_LITEON_DVB_T_WARM) { + msleep(1000); + } + + adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, + &adap->dev->i2c_adap, + DEFAULT_DIB3000P_I2C_ADDRESS, + &mod3000p_dib3000p_config); + if ((adap->fe_adap[0].fe) == NULL) + adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, + &adap->dev->i2c_adap, + DEFAULT_DIB3000MC_I2C_ADDRESS, + &mod3000p_dib3000p_config); + if ((adap->fe_adap[0].fe) != NULL) { + if (adap->priv != NULL) { + struct dibusb_state *st = adap->priv; + st->ops.pid_parse = dib3000mc_pid_parse; + st->ops.pid_ctrl = dib3000mc_pid_control; + } + return 0; + } + return -ENODEV; +} +EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach); + +static struct mt2060_config stk3000p_mt2060_config = { + 0x60 +}; + +int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dibusb_state *st = adap->priv; + u8 a,b; + u16 if1 = 1220; + struct i2c_adapter *tun_i2c; + + // First IF calibration for Liteon Sticks + if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON && + adap->dev->udev->descriptor.idProduct == USB_PID_LITEON_DVB_T_WARM) { + + dibusb_read_eeprom_byte(adap->dev,0x7E,&a); + dibusb_read_eeprom_byte(adap->dev,0x7F,&b); + + if (a == 0x00) + if1 += b; + else if (a == 0x80) + if1 -= b; + else + warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b); + + } else if (adap->dev->udev->descriptor.idVendor == USB_VID_DIBCOM && + adap->dev->udev->descriptor.idProduct == USB_PID_DIBCOM_MOD3001_WARM) { + u8 desc; + dibusb_read_eeprom_byte(adap->dev, 7, &desc); + if (desc == 2) { + a = 127; + do { + dibusb_read_eeprom_byte(adap->dev, a, &desc); + a--; + } while (a > 7 && (desc == 0xff || desc == 0x00)); + if (desc & 0x80) + if1 -= (0xff - desc); + else + if1 += desc; + } + } + + tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); + if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) { + /* not found - use panasonic pll parameters */ + if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) + return -ENOMEM; + } else { + st->mt2060_present = 1; + /* set the correct parameters for the dib3000p */ + dib3000mc_set_config(adap->fe_adap[0].fe, &stk3000p_dib3000p_config); + } + return 0; +} +EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); +#endif + +/* + * common remote control stuff + */ +struct rc_map_table rc_map_dibusb_table[] = { + /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ + { 0x0016, KEY_POWER }, + { 0x0010, KEY_MUTE }, + { 0x0003, KEY_1 }, + { 0x0001, KEY_2 }, + { 0x0006, KEY_3 }, + { 0x0009, KEY_4 }, + { 0x001d, KEY_5 }, + { 0x001f, KEY_6 }, + { 0x000d, KEY_7 }, + { 0x0019, KEY_8 }, + { 0x001b, KEY_9 }, + { 0x0015, KEY_0 }, + { 0x0005, KEY_CHANNELUP }, + { 0x0002, KEY_CHANNELDOWN }, + { 0x001e, KEY_VOLUMEUP }, + { 0x000a, KEY_VOLUMEDOWN }, + { 0x0011, KEY_RECORD }, + { 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x0014, KEY_PLAY }, + { 0x001a, KEY_STOP }, + { 0x0040, KEY_REWIND }, + { 0x0012, KEY_FASTFORWARD }, + { 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x004c, KEY_PAUSE }, + { 0x004d, KEY_SCREEN }, /* Full screen mode. */ + { 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + /* additional keys TwinHan VisionPlus, the Artec seemingly not have */ + { 0x000c, KEY_CANCEL }, /* Cancel */ + { 0x001c, KEY_EPG }, /* EPG */ + { 0x0000, KEY_TAB }, /* Tab */ + { 0x0048, KEY_INFO }, /* Preview */ + { 0x0004, KEY_LIST }, /* RecordList */ + { 0x000f, KEY_TEXT }, /* Teletext */ + /* Key codes for the KWorld/ADSTech/JetWay remote. */ + { 0x8612, KEY_POWER }, + { 0x860f, KEY_SELECT }, /* source */ + { 0x860c, KEY_UNKNOWN }, /* scan */ + { 0x860b, KEY_EPG }, + { 0x8610, KEY_MUTE }, + { 0x8601, KEY_1 }, + { 0x8602, KEY_2 }, + { 0x8603, KEY_3 }, + { 0x8604, KEY_4 }, + { 0x8605, KEY_5 }, + { 0x8606, KEY_6 }, + { 0x8607, KEY_7 }, + { 0x8608, KEY_8 }, + { 0x8609, KEY_9 }, + { 0x860a, KEY_0 }, + { 0x8618, KEY_ZOOM }, + { 0x861c, KEY_UNKNOWN }, /* preview */ + { 0x8613, KEY_UNKNOWN }, /* snap */ + { 0x8600, KEY_UNDO }, + { 0x861d, KEY_RECORD }, + { 0x860d, KEY_STOP }, + { 0x860e, KEY_PAUSE }, + { 0x8616, KEY_PLAY }, + { 0x8611, KEY_BACK }, + { 0x8619, KEY_FORWARD }, + { 0x8614, KEY_UNKNOWN }, /* pip */ + { 0x8615, KEY_ESC }, + { 0x861a, KEY_UP }, + { 0x861e, KEY_DOWN }, + { 0x861f, KEY_LEFT }, + { 0x861b, KEY_RIGHT }, + + /* Key codes for the DiBcom MOD3000 remote. */ + { 0x8000, KEY_MUTE }, + { 0x8001, KEY_TEXT }, + { 0x8002, KEY_HOME }, + { 0x8003, KEY_POWER }, + + { 0x8004, KEY_RED }, + { 0x8005, KEY_GREEN }, + { 0x8006, KEY_YELLOW }, + { 0x8007, KEY_BLUE }, + + { 0x8008, KEY_DVD }, + { 0x8009, KEY_AUDIO }, + { 0x800a, KEY_IMAGES }, /* Pictures */ + { 0x800b, KEY_VIDEO }, + + { 0x800c, KEY_BACK }, + { 0x800d, KEY_UP }, + { 0x800e, KEY_RADIO }, + { 0x800f, KEY_EPG }, + + { 0x8010, KEY_LEFT }, + { 0x8011, KEY_OK }, + { 0x8012, KEY_RIGHT }, + { 0x8013, KEY_UNKNOWN }, /* SAP */ + + { 0x8014, KEY_TV }, + { 0x8015, KEY_DOWN }, + { 0x8016, KEY_MENU }, /* DVD Menu */ + { 0x8017, KEY_LAST }, + + { 0x8018, KEY_RECORD }, + { 0x8019, KEY_STOP }, + { 0x801a, KEY_PAUSE }, + { 0x801b, KEY_PLAY }, + + { 0x801c, KEY_PREVIOUS }, + { 0x801d, KEY_REWIND }, + { 0x801e, KEY_FASTFORWARD }, + { 0x801f, KEY_NEXT}, + + { 0x8040, KEY_1 }, + { 0x8041, KEY_2 }, + { 0x8042, KEY_3 }, + { 0x8043, KEY_CHANNELUP }, + + { 0x8044, KEY_4 }, + { 0x8045, KEY_5 }, + { 0x8046, KEY_6 }, + { 0x8047, KEY_CHANNELDOWN }, + + { 0x8048, KEY_7 }, + { 0x8049, KEY_8 }, + { 0x804a, KEY_9 }, + { 0x804b, KEY_VOLUMEUP }, + + { 0x804c, KEY_CLEAR }, + { 0x804d, KEY_0 }, + { 0x804e, KEY_ENTER }, + { 0x804f, KEY_VOLUMEDOWN }, +}; +EXPORT_SYMBOL(rc_map_dibusb_table); + +int dibusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5],cmd = DIBUSB_REQ_POLL_REMOTE; + dvb_usb_generic_rw(d,&cmd,1,key,5,0); + dvb_usb_nec_rc_key_to_event(d,key,event,state); + if (key[0] != 0) + deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + return 0; +} +EXPORT_SYMBOL(dibusb_rc_query); diff --git a/drivers/media/usb/dvb-usb/dibusb-mb.c b/drivers/media/usb/dvb-usb/dibusb-mb.c new file mode 100644 index 000000000000..a4ac37e0e98b --- /dev/null +++ b/drivers/media/usb/dvb-usb/dibusb-mb.c @@ -0,0 +1,471 @@ +/* DVB USB compliant linux driver for mobile DVB-T USB devices based on + * reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-B) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DiBcom, which has + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int dib3000mb_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dibusb_state *st = adap->priv; + + return st->ops.tuner_pass_ctrl(fe, enable, st->tuner_addr); +} + +static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib3000_config demod_cfg; + struct dibusb_state *st = adap->priv; + + demod_cfg.demod_address = 0x8; + + adap->fe_adap[0].fe = dvb_attach(dib3000mb_attach, &demod_cfg, + &adap->dev->i2c_adap, &st->ops); + if ((adap->fe_adap[0].fe) == NULL) + return -ENODEV; + + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl; + + return 0; +} + +static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dibusb_state *st = adap->priv; + + st->tuner_addr = 0x61; + + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, + DVB_PLL_TUA6010XS); + return 0; +} + +static int dibusb_panasonic_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dibusb_state *st = adap->priv; + + st->tuner_addr = 0x60; + + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, + DVB_PLL_TDA665X); + return 0; +} + +/* Some of the Artec 1.1 device aren't equipped with the default tuner + * (Thomson Cable), but with a Panasonic ENV77H11D5. This function figures + * this out. */ +static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap) +{ + u8 b[2] = { 0,0 }, b2[1]; + int ret = 0; + struct i2c_msg msg[2] = { + { .flags = 0, .buf = b, .len = 2 }, + { .flags = I2C_M_RD, .buf = b2, .len = 1 }, + }; + struct dibusb_state *st = adap->priv; + + /* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */ + msg[0].addr = msg[1].addr = st->tuner_addr = 0x60; + + if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) + adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1); + + if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) { + err("tuner i2c write failed."); + ret = -EREMOTEIO; + } + + if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) + adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0); + + if (b2[0] == 0xfe) { + info("This device has the Thomson Cable onboard. Which is default."); + ret = dibusb_thomson_tuner_attach(adap); + } else { + info("This device has the Panasonic ENV77H11D5 onboard."); + ret = dibusb_panasonic_tuner_attach(adap); + } + + return ret; +} + +/* USB Driver stuff */ +static struct dvb_usb_device_properties dibusb1_1_properties; +static struct dvb_usb_device_properties dibusb1_1_an2235_properties; +static struct dvb_usb_device_properties dibusb2_0b_properties; +static struct dvb_usb_device_properties artec_t1_usb2_properties; + +static int dibusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (0 == dvb_usb_device_init(intf, &dibusb1_1_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &dibusb1_1_an2235_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &dibusb2_0b_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &artec_t1_usb2_properties, + THIS_MODULE, NULL, adapter_nr)) + return 0; + + return -EINVAL; +} + +/* do not change the order of the ID table */ +static struct usb_device_id dibusb_dib3000mb_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_AVERMEDIA_DVBT_USB_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_AVERMEDIA_DVBT_USB_WARM) }, +/* 02 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) }, +/* 03 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) }, +/* 04 */ { USB_DEVICE(USB_VID_COMPRO_UNK, USB_PID_COMPRO_DVBU2000_UNK_COLD) }, +/* 05 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) }, +/* 06 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) }, +/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) }, +/* 08 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) }, +/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) }, +/* 10 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) }, +/* 11 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) }, +/* 12 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) }, +/* 13 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) }, +/* 14 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) }, +/* 15 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_COLD) }, +/* 16 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_WARM) }, +/* 17 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) }, +/* 18 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) }, +/* 19 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) }, +/* 20 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) }, +/* 21 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) }, +/* 22 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) }, +/* 23 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_COLD) }, + +/* device ID with default DIBUSB2_0-firmware and with the hacked firmware */ +/* 24 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_WARM) }, +/* 25 */ { USB_DEVICE(USB_VID_KYE, USB_PID_KYE_DVB_T_COLD) }, +/* 26 */ { USB_DEVICE(USB_VID_KYE, USB_PID_KYE_DVB_T_WARM) }, + +/* 27 */ { USB_DEVICE(USB_VID_KWORLD, USB_PID_KWORLD_VSTREAM_COLD) }, + +/* 28 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, +/* 29 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) }, + +/* + * XXX: As Artec just 'forgot' to program the EEPROM on some Artec T1 devices + * we don't catch these faulty IDs (namely 'Cypress FX1 USB controller') that + * have been left on the device. If you don't have such a device but an Artec + * device that's supposed to work with this driver but is not detected by it, + * free to enable CONFIG_DVB_USB_DIBUSB_MB_FAULTY via your kernel config. + */ + +#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY +/* 30 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) }, +#endif + + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, dibusb_dib3000mb_table); + +static struct dvb_usb_device_properties dibusb1_1_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_AN2135, + + .firmware = "dvb-usb-dibusb-5.0.0.11.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 16, + + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + } + }, + + .power_ctrl = dibusb_power_ctrl, + + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_map_table = rc_map_dibusb_table, + .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 9, + .devices = { + { "AVerMedia AverTV DVBT USB1.1", + { &dibusb_dib3000mb_table[0], NULL }, + { &dibusb_dib3000mb_table[1], NULL }, + }, + { "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)", + { &dibusb_dib3000mb_table[2], &dibusb_dib3000mb_table[4], NULL}, + { &dibusb_dib3000mb_table[3], NULL }, + }, + { "DiBcom USB1.1 DVB-T reference design (MOD3000)", + { &dibusb_dib3000mb_table[5], NULL }, + { &dibusb_dib3000mb_table[6], NULL }, + }, + { "KWorld V-Stream XPERT DTV - DVB-T USB1.1", + { &dibusb_dib3000mb_table[7], NULL }, + { &dibusb_dib3000mb_table[8], NULL }, + }, + { "Grandtec USB1.1 DVB-T", + { &dibusb_dib3000mb_table[9], &dibusb_dib3000mb_table[11], NULL }, + { &dibusb_dib3000mb_table[10], &dibusb_dib3000mb_table[12], NULL }, + }, + { "Unknown USB1.1 DVB-T device ???? please report the name to the author", + { &dibusb_dib3000mb_table[13], NULL }, + { &dibusb_dib3000mb_table[14], NULL }, + }, + { "TwinhanDTV USB-Ter USB1.1 / Magic Box I / HAMA USB1.1 DVB-T device", + { &dibusb_dib3000mb_table[15], &dibusb_dib3000mb_table[17], NULL}, + { &dibusb_dib3000mb_table[16], &dibusb_dib3000mb_table[18], NULL}, + }, + { "Artec T1 USB1.1 TVBOX with AN2135", + { &dibusb_dib3000mb_table[19], NULL }, + { &dibusb_dib3000mb_table[20], NULL }, + }, + { "VideoWalker DVB-T USB", + { &dibusb_dib3000mb_table[25], NULL }, + { &dibusb_dib3000mb_table[26], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties dibusb1_1_an2235_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_AN2235, + + .firmware = "dvb-usb-dibusb-an2235-01.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER, + .pid_filter_count = 16, + + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + }, + }, + .power_ctrl = dibusb_power_ctrl, + + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_map_table = rc_map_dibusb_table, + .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + +#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY + .num_device_descs = 2, +#else + .num_device_descs = 1, +#endif + .devices = { + { "Artec T1 USB1.1 TVBOX with AN2235", + { &dibusb_dib3000mb_table[21], NULL }, + { &dibusb_dib3000mb_table[22], NULL }, + }, +#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY + { "Artec T1 USB1.1 TVBOX with AN2235 (faulty USB IDs)", + { &dibusb_dib3000mb_table[30], NULL }, + { NULL }, + }, + { NULL }, +#endif + } +}; + +static struct dvb_usb_device_properties dibusb2_0b_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .firmware = "dvb-usb-adstech-usb2-02.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 16, + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_thomson_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + } + }, + .power_ctrl = dibusb2_0_power_ctrl, + + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_map_table = rc_map_dibusb_table, + .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 2, + .devices = { + { "KWorld/ADSTech Instant DVB-T USB2.0", + { &dibusb_dib3000mb_table[23], NULL }, + { &dibusb_dib3000mb_table[24], NULL }, + }, + { "KWorld Xpert DVB-T USB2.0", + { &dibusb_dib3000mb_table[27], NULL }, + { NULL } + }, + { NULL }, + } +}; + +static struct dvb_usb_device_properties artec_t1_usb2_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .firmware = "dvb-usb-dibusb-6.0.0.8.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 16, + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + } + }, + .power_ctrl = dibusb2_0_power_ctrl, + + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_map_table = rc_map_dibusb_table, + .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Artec T1 USB2.0", + { &dibusb_dib3000mb_table[28], NULL }, + { &dibusb_dib3000mb_table[29], NULL }, + }, + { NULL }, + } +}; + +static struct usb_driver dibusb_driver = { + .name = "dvb_usb_dibusb_mb", + .probe = dibusb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dibusb_dib3000mb_table, +}; + +module_usb_driver(dibusb_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for DiBcom USB DVB-T devices (DiB3000M-B based)"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/dibusb-mc.c b/drivers/media/usb/dvb-usb/dibusb-mc.c new file mode 100644 index 000000000000..9d1a59d09c52 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dibusb-mc.c @@ -0,0 +1,149 @@ +/* DVB USB compliant linux driver for mobile DVB-T USB devices based on + * reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-C/P) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DiBcom, which has + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* USB Driver stuff */ +static struct dvb_usb_device_properties dibusb_mc_properties; + +static int dibusb_mc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &dibusb_mc_properties, THIS_MODULE, + NULL, adapter_nr); +} + +/* do not change the order of the ID table */ +static struct usb_device_id dibusb_dib3000mc_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) }, +/* 02 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, +/* 03 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) }, // ( ? ) +/* 04 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_COLD) }, +/* 05 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_WARM) }, +/* 06 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_COLD) }, +/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_WARM) }, +/* 08 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_COLD) }, +/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_WARM) }, +/* 10 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_COLD) }, +/* 11 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_WARM) }, +/* 12 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_COLD) }, +/* 13 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_WARM) }, +/* 14 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD) }, +/* 15 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table); + +static struct dvb_usb_device_properties dibusb_mc_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-dibusb-6.0.0.8.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + } + }, + .power_ctrl = dibusb2_0_power_ctrl, + + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_map_table = rc_map_dibusb_table, + .rc_map_size = 111, /* FIXME */ + .rc_query = dibusb_rc_query, + }, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 8, + .devices = { + { "DiBcom USB2.0 DVB-T reference design (MOD3000P)", + { &dibusb_dib3000mc_table[0], NULL }, + { &dibusb_dib3000mc_table[1], NULL }, + }, + { "Artec T1 USB2.0 TVBOX (please check the warm ID)", + { &dibusb_dib3000mc_table[2], NULL }, + { &dibusb_dib3000mc_table[3], NULL }, + }, + { "LITE-ON USB2.0 DVB-T Tuner", + /* Also rebranded as Intuix S800, Toshiba */ + { &dibusb_dib3000mc_table[4], NULL }, + { &dibusb_dib3000mc_table[5], NULL }, + }, + { "MSI Digivox Mini SL", + { &dibusb_dib3000mc_table[6], NULL }, + { &dibusb_dib3000mc_table[7], NULL }, + }, + { "GRAND - USB2.0 DVB-T adapter", + { &dibusb_dib3000mc_table[8], NULL }, + { &dibusb_dib3000mc_table[9], NULL }, + }, + { "Artec T14 - USB2.0 DVB-T", + { &dibusb_dib3000mc_table[10], NULL }, + { &dibusb_dib3000mc_table[11], NULL }, + }, + { "Leadtek - USB2.0 Winfast DTV dongle", + { &dibusb_dib3000mc_table[12], NULL }, + { &dibusb_dib3000mc_table[13], NULL }, + }, + { "Humax/Coex DVB-T USB Stick 2.0 High Speed", + { &dibusb_dib3000mc_table[14], NULL }, + { &dibusb_dib3000mc_table[15], NULL }, + }, + { NULL }, + } +}; + +static struct usb_driver dibusb_mc_driver = { + .name = "dvb_usb_dibusb_mc", + .probe = dibusb_mc_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dibusb_dib3000mc_table, +}; + +module_usb_driver(dibusb_mc_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for DiBcom USB2.0 DVB-T (DiB3000M-C/P based) devices"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/dibusb.h b/drivers/media/usb/dvb-usb/dibusb.h new file mode 100644 index 000000000000..e47c321b3ffc --- /dev/null +++ b/drivers/media/usb/dvb-usb/dibusb.h @@ -0,0 +1,131 @@ +/* Header file for all dibusb-based-receivers. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_DIBUSB_H_ +#define _DVB_USB_DIBUSB_H_ + +#ifndef DVB_USB_LOG_PREFIX + #define DVB_USB_LOG_PREFIX "dibusb" +#endif +#include "dvb-usb.h" + +#include "dib3000.h" +#include "dib3000mc.h" +#include "mt2060.h" + +/* + * protocol of all dibusb related devices + */ + +/* + * bulk msg to/from endpoint 0x01 + * + * general structure: + * request_byte parameter_bytes + */ + +#define DIBUSB_REQ_START_READ 0x00 +#define DIBUSB_REQ_START_DEMOD 0x01 + +/* + * i2c read + * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word + * bulk read: byte_buffer (length_word bytes) + */ +#define DIBUSB_REQ_I2C_READ 0x02 + +/* + * i2c write + * bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes + */ +#define DIBUSB_REQ_I2C_WRITE 0x03 + +/* + * polling the value of the remote control + * bulk write: 0x04 + * bulk read: byte_buffer (5 bytes) + */ +#define DIBUSB_REQ_POLL_REMOTE 0x04 + +/* additional status values for Hauppauge Remote Control Protocol */ +#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED 0x01 +#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY 0x03 + +/* streaming mode: + * bulk write: 0x05 mode_byte + * + * mode_byte is mostly 0x00 + */ +#define DIBUSB_REQ_SET_STREAMING_MODE 0x05 + +/* interrupt the internal read loop, when blocking */ +#define DIBUSB_REQ_INTR_READ 0x06 + +/* io control + * 0x07 cmd_byte param_bytes + * + * param_bytes can be up to 32 bytes + * + * cmd_byte function parameter name + * 0x00 power mode + * 0x00 sleep + * 0x01 wakeup + * + * 0x01 enable streaming + * 0x02 disable streaming + * + * + */ +#define DIBUSB_REQ_SET_IOCTL 0x07 + +/* IOCTL commands */ + +/* change the power mode in firmware */ +#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00 +#define DIBUSB_IOCTL_POWER_SLEEP 0x00 +#define DIBUSB_IOCTL_POWER_WAKEUP 0x01 + +/* modify streaming of the FX2 */ +#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01 +#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02 + +struct dibusb_state { + struct dib_fe_xfer_ops ops; + int mt2060_present; + u8 tuner_addr; +}; + +struct dibusb_device_state { + /* for RC5 remote control */ + int old_toggle; + int last_repeat_count; +}; + +extern struct i2c_algorithm dibusb_i2c_algo; + +extern int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *); +extern int dibusb_dib3000mc_tuner_attach (struct dvb_usb_adapter *); + +extern int dibusb_streaming_ctrl(struct dvb_usb_adapter *, int); +extern int dibusb_pid_filter(struct dvb_usb_adapter *, int, u16, int); +extern int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *, int); +extern int dibusb2_0_streaming_ctrl(struct dvb_usb_adapter *, int); + +extern int dibusb_power_ctrl(struct dvb_usb_device *, int); +extern int dibusb2_0_power_ctrl(struct dvb_usb_device *, int); + +#define DEFAULT_RC_INTERVAL 150 +//#define DEFAULT_RC_INTERVAL 100000 + +extern struct rc_map_table rc_map_dibusb_table[]; +extern int dibusb_rc_query(struct dvb_usb_device *, u32 *, int *); +extern int dibusb_read_eeprom_byte(struct dvb_usb_device *, u8, u8 *); + +#endif diff --git a/drivers/media/usb/dvb-usb/digitv.c b/drivers/media/usb/dvb-usb/digitv.c new file mode 100644 index 000000000000..ff34419a4c88 --- /dev/null +++ b/drivers/media/usb/dvb-usb/digitv.c @@ -0,0 +1,354 @@ +/* DVB USB compliant linux driver for Nebula Electronics uDigiTV DVB-T USB2.0 + * receiver + * + * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) + * + * partly based on the SDK published by Nebula Electronics + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "digitv.h" + +#include "mt352.h" +#include "nxt6000.h" + +/* debug */ +static int dvb_usb_digitv_debug; +module_param_named(debug,dvb_usb_digitv_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args) + +static int digitv_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, u8 vv, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 sndbuf[7],rcvbuf[7]; + memset(sndbuf,0,7); memset(rcvbuf,0,7); + + sndbuf[0] = cmd; + sndbuf[1] = vv; + sndbuf[2] = wo ? wlen : rlen; + + if (wo) { + memcpy(&sndbuf[3],wbuf,wlen); + dvb_usb_generic_write(d,sndbuf,7); + } else { + dvb_usb_generic_rw(d,sndbuf,7,rcvbuf,7,10); + memcpy(rbuf,&rcvbuf[3],rlen); + } + return 0; +} + +/* I2C */ +static int digitv_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (digitv_ctrl_msg(d, USB_READ_COFDM, msg[i].buf[0], NULL, 0, + msg[i+1].buf,msg[i+1].len) < 0) + break; + i++; + } else + if (digitv_ctrl_msg(d,USB_WRITE_COFDM, msg[i].buf[0], + &msg[i].buf[1],msg[i].len-1,NULL,0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 digitv_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm digitv_i2c_algo = { + .master_xfer = digitv_i2c_xfer, + .functionality = digitv_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int digitv_identify_state (struct usb_device *udev, struct + dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, + int *cold) +{ + *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; + return 0; +} + +static int digitv_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 reset_buf[] = { 0x89, 0x38, 0x8a, 0x2d, 0x50, 0x80 }; + static u8 init_buf[] = { 0x68, 0xa0, 0x8e, 0x40, 0x53, 0x50, + 0x67, 0x20, 0x7d, 0x01, 0x7c, 0x00, 0x7a, 0x00, + 0x79, 0x20, 0x57, 0x05, 0x56, 0x31, 0x88, 0x0f, + 0x75, 0x32 }; + int i; + + for (i = 0; i < ARRAY_SIZE(reset_buf); i += 2) + mt352_write(fe, &reset_buf[i], 2); + + msleep(1); + + for (i = 0; i < ARRAY_SIZE(init_buf); i += 2) + mt352_write(fe, &init_buf[i], 2); + + return 0; +} + +static struct mt352_config digitv_mt352_config = { + .demod_init = digitv_mt352_demod_init, +}; + +static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + u8 b[5]; + + fe->ops.tuner_ops.calc_regs(fe, b, sizeof(b)); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + return digitv_ctrl_msg(adap->dev, USB_WRITE_TUNER, 0, &b[1], 4, NULL, 0); +} + +static struct nxt6000_config digitv_nxt6000_config = { + .clock_inversion = 1, +}; + +static int digitv_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct digitv_state *st = adap->dev->priv; + + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &digitv_mt352_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) { + st->is_nxt6000 = 0; + return 0; + } + adap->fe_adap[0].fe = dvb_attach(nxt6000_attach, + &digitv_nxt6000_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) != NULL) { + st->is_nxt6000 = 1; + return 0; + } + return -EIO; +} + +static int digitv_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct digitv_state *st = adap->dev->priv; + + if (!dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, NULL, DVB_PLL_TDED4)) + return -ENODEV; + + if (st->is_nxt6000) + adap->fe_adap[0].fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params; + + return 0; +} + +static struct rc_map_table rc_map_digitv_table[] = { + { 0x5f55, KEY_0 }, + { 0x6f55, KEY_1 }, + { 0x9f55, KEY_2 }, + { 0xaf55, KEY_3 }, + { 0x5f56, KEY_4 }, + { 0x6f56, KEY_5 }, + { 0x9f56, KEY_6 }, + { 0xaf56, KEY_7 }, + { 0x5f59, KEY_8 }, + { 0x6f59, KEY_9 }, + { 0x9f59, KEY_TV }, + { 0xaf59, KEY_AUX }, + { 0x5f5a, KEY_DVD }, + { 0x6f5a, KEY_POWER }, + { 0x9f5a, KEY_CAMERA }, /* labelled 'Picture' */ + { 0xaf5a, KEY_AUDIO }, + { 0x5f65, KEY_INFO }, + { 0x6f65, KEY_F13 }, /* 16:9 */ + { 0x9f65, KEY_F14 }, /* 14:9 */ + { 0xaf65, KEY_EPG }, + { 0x5f66, KEY_EXIT }, + { 0x6f66, KEY_MENU }, + { 0x9f66, KEY_UP }, + { 0xaf66, KEY_DOWN }, + { 0x5f69, KEY_LEFT }, + { 0x6f69, KEY_RIGHT }, + { 0x9f69, KEY_ENTER }, + { 0xaf69, KEY_CHANNELUP }, + { 0x5f6a, KEY_CHANNELDOWN }, + { 0x6f6a, KEY_VOLUMEUP }, + { 0x9f6a, KEY_VOLUMEDOWN }, + { 0xaf6a, KEY_RED }, + { 0x5f95, KEY_GREEN }, + { 0x6f95, KEY_YELLOW }, + { 0x9f95, KEY_BLUE }, + { 0xaf95, KEY_SUBTITLE }, + { 0x5f96, KEY_F15 }, /* AD */ + { 0x6f96, KEY_TEXT }, + { 0x9f96, KEY_MUTE }, + { 0xaf96, KEY_REWIND }, + { 0x5f99, KEY_STOP }, + { 0x6f99, KEY_PLAY }, + { 0x9f99, KEY_FASTFORWARD }, + { 0xaf99, KEY_F16 }, /* chapter */ + { 0x5f9a, KEY_PAUSE }, + { 0x6f9a, KEY_PLAY }, + { 0x9f9a, KEY_RECORD }, + { 0xaf9a, KEY_F17 }, /* picture in picture */ + { 0x5fa5, KEY_KPPLUS }, /* zoom in */ + { 0x6fa5, KEY_KPMINUS }, /* zoom out */ + { 0x9fa5, KEY_F18 }, /* capture */ + { 0xafa5, KEY_F19 }, /* web */ + { 0x5fa6, KEY_EMAIL }, + { 0x6fa6, KEY_PHONE }, + { 0x9fa6, KEY_PC }, +}; + +static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + int i; + u8 key[5]; + u8 b[4] = { 0 }; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + digitv_ctrl_msg(d,USB_READ_REMOTE,0,NULL,0,&key[1],4); + + /* Tell the device we've read the remote. Not sure how necessary + this is, but the Nebula SDK does it. */ + digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0); + + /* if something is inside the buffer, simulate key press */ + if (key[1] != 0) + { + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { + if (rc5_custom(&d->props.rc.legacy.rc_map_table[i]) == key[1] && + rc5_data(&d->props.rc.legacy.rc_map_table[i]) == key[2]) { + *event = d->props.rc.legacy.rc_map_table[i].keycode; + *state = REMOTE_KEY_PRESSED; + return 0; + } + } + } + + if (key[0] != 0) + deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties digitv_properties; + +static int digitv_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + int ret = dvb_usb_device_init(intf, &digitv_properties, THIS_MODULE, &d, + adapter_nr); + if (ret == 0) { + u8 b[4] = { 0 }; + + if (d != NULL) { /* do that only when the firmware is loaded */ + b[0] = 1; + digitv_ctrl_msg(d,USB_WRITE_REMOTE_TYPE,0,b,4,NULL,0); + + b[0] = 0; + digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0); + } + } + return ret; +} + +static struct usb_device_id digitv_table [] = { + { USB_DEVICE(USB_VID_ANCHOR, USB_PID_NEBULA_DIGITV) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, digitv_table); + +static struct dvb_usb_device_properties digitv_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-digitv-02.fw", + + .size_of_priv = sizeof(struct digitv_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = digitv_frontend_attach, + .tuner_attach = digitv_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .identify_state = digitv_identify_state, + + .rc.legacy = { + .rc_interval = 1000, + .rc_map_table = rc_map_digitv_table, + .rc_map_size = ARRAY_SIZE(rc_map_digitv_table), + .rc_query = digitv_rc_query, + }, + + .i2c_algo = &digitv_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Nebula Electronics uDigiTV DVB-T USB2.0)", + { &digitv_table[0], NULL }, + { NULL }, + }, + { NULL }, + } +}; + +static struct usb_driver digitv_driver = { + .name = "dvb_usb_digitv", + .probe = digitv_probe, + .disconnect = dvb_usb_device_exit, + .id_table = digitv_table, +}; + +module_usb_driver(digitv_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Nebula Electronics uDigiTV DVB-T USB2.0"); +MODULE_VERSION("1.0-alpha"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/digitv.h b/drivers/media/usb/dvb-usb/digitv.h new file mode 100644 index 000000000000..908c09f4966b --- /dev/null +++ b/drivers/media/usb/dvb-usb/digitv.h @@ -0,0 +1,66 @@ +#ifndef _DVB_USB_DIGITV_H_ +#define _DVB_USB_DIGITV_H_ + +#define DVB_USB_LOG_PREFIX "digitv" +#include "dvb-usb.h" + +struct digitv_state { + int is_nxt6000; +}; + +/* protocol (from usblogging and the SDK: + * + * Always 7 bytes bulk message(s) for controlling + * + * First byte describes the command. Reads are 2 consecutive transfer (as always). + * + * General structure: + * + * write or first message of a read: + * VV B0 B1 B2 B3 + * + * second message of a read + * VV R0 R1 R2 R3 + * + * whereas 0 < len <= 4 + * + * I2C address is stored somewhere inside the device. + * + * 0x01 read from EEPROM + * VV = offset; B* = 0; R* = value(s) + * + * 0x02 read register of the COFDM + * VV = register; B* = 0; R* = value(s) + * + * 0x05 write register of the COFDM + * VV = register; B* = value(s); + * + * 0x06 write to the tuner (only for NXT6000) + * VV = 0; B* = PLL data; len = 4; + * + * 0x03 read remote control + * VV = 0; B* = 0; len = 4; R* = key + * + * 0x07 write to the remote (don't know why one should this, resetting ?) + * VV = 0; B* = key; len = 4; + * + * 0x08 write remote type + * VV = 0; B[0] = 0x01, len = 4 + * + * 0x09 write device init + * TODO + */ +#define USB_READ_EEPROM 1 + +#define USB_READ_COFDM 2 +#define USB_WRITE_COFDM 5 + +#define USB_WRITE_TUNER 6 + +#define USB_READ_REMOTE 3 +#define USB_WRITE_REMOTE 7 +#define USB_WRITE_REMOTE_TYPE 8 + +#define USB_DEV_INIT 9 + +#endif diff --git a/drivers/media/usb/dvb-usb/dtt200u-fe.c b/drivers/media/usb/dvb-usb/dtt200u-fe.c new file mode 100644 index 000000000000..3d81daa49172 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dtt200u-fe.c @@ -0,0 +1,210 @@ +/* Frontend part of the Linux driver for the WideView/ Yakumo/ Hama/ + * Typhoon/ Yuan DVB-T USB2.0 receiver. + * + * Copyright (C) 2005 Patrick Boettcher + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dtt200u.h" + +struct dtt200u_fe_state { + struct dvb_usb_device *d; + + fe_status_t stat; + + struct dtv_frontend_properties fep; + struct dvb_frontend frontend; +}; + +static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 st = GET_TUNE_STATUS, b[3]; + + dvb_usb_generic_rw(state->d,&st,1,b,3,0); + + switch (b[0]) { + case 0x01: + *stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + break; + case 0x00: /* pending */ + *stat = FE_TIMEDOUT; /* during set_frontend */ + break; + default: + case 0x02: /* failed */ + *stat = 0; + break; + } + return 0; +} + +static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw = GET_VIT_ERR_CNT,b[3]; + dvb_usb_generic_rw(state->d,&bw,1,b,3,0); + *ber = (b[0] << 16) | (b[1] << 8) | b[2]; + return 0; +} + +static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw = GET_RS_UNCOR_BLK_CNT,b[2]; + + dvb_usb_generic_rw(state->d,&bw,1,b,2,0); + *unc = (b[0] << 8) | b[1]; + return 0; +} + +static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw = GET_AGC, b; + dvb_usb_generic_rw(state->d,&bw,1,&b,1,0); + *strength = (b << 8) | b; + return 0; +} + +static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 bw = GET_SNR,br; + dvb_usb_generic_rw(state->d,&bw,1,&br,1,0); + *snr = ~((br << 8) | br); + return 0; +} + +static int dtt200u_fe_init(struct dvb_frontend* fe) +{ + struct dtt200u_fe_state *state = fe->demodulator_priv; + u8 b = SET_INIT; + return dvb_usb_generic_write(state->d,&b,1); +} + +static int dtt200u_fe_sleep(struct dvb_frontend* fe) +{ + return dtt200u_fe_init(fe); +} + +static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1500; + tune->step_size = 0; + tune->max_drift = 0; + return 0; +} + +static int dtt200u_fe_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct dtt200u_fe_state *state = fe->demodulator_priv; + int i; + fe_status_t st; + u16 freq = fep->frequency / 250000; + u8 bwbuf[2] = { SET_BANDWIDTH, 0 },freqbuf[3] = { SET_RF_FREQ, 0, 0 }; + + switch (fep->bandwidth_hz) { + case 8000000: + bwbuf[1] = 8; + break; + case 7000000: + bwbuf[1] = 7; + break; + case 6000000: + bwbuf[1] = 6; + break; + default: + return -EINVAL; + } + + dvb_usb_generic_write(state->d,bwbuf,2); + + freqbuf[1] = freq & 0xff; + freqbuf[2] = (freq >> 8) & 0xff; + dvb_usb_generic_write(state->d,freqbuf,3); + + for (i = 0; i < 30; i++) { + msleep(20); + dtt200u_fe_read_status(fe, &st); + if (st & FE_TIMEDOUT) + continue; + } + + return 0; +} + +static int dtt200u_fe_get_frontend(struct dvb_frontend* fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct dtt200u_fe_state *state = fe->demodulator_priv; + memcpy(fep, &state->fep, sizeof(struct dtv_frontend_properties)); + return 0; +} + +static void dtt200u_fe_release(struct dvb_frontend* fe) +{ + struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops dtt200u_fe_ops; + +struct dvb_frontend* dtt200u_fe_attach(struct dvb_usb_device *d) +{ + struct dtt200u_fe_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL); + if (state == NULL) + goto error; + + deb_info("attaching frontend dtt200u\n"); + + state->d = d; + + memcpy(&state->frontend.ops,&dtt200u_fe_ops,sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + return NULL; +} + +static struct dvb_frontend_ops dtt200u_fe_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "WideView USB DVB-T", + .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 = dtt200u_fe_release, + + .init = dtt200u_fe_init, + .sleep = dtt200u_fe_sleep, + + .set_frontend = dtt200u_fe_set_frontend, + .get_frontend = dtt200u_fe_get_frontend, + .get_tune_settings = dtt200u_fe_get_tune_settings, + + .read_status = dtt200u_fe_read_status, + .read_ber = dtt200u_fe_read_ber, + .read_signal_strength = dtt200u_fe_read_signal_strength, + .read_snr = dtt200u_fe_read_snr, + .read_ucblocks = dtt200u_fe_read_unc_blocks, +}; diff --git a/drivers/media/usb/dvb-usb/dtt200u.c b/drivers/media/usb/dvb-usb/dtt200u.c new file mode 100644 index 000000000000..66f205c112b2 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dtt200u.c @@ -0,0 +1,368 @@ +/* DVB USB library compliant Linux driver for the WideView/ Yakumo/ Hama/ + * Typhoon/ Yuan/ Miglia DVB-T USB2.0 receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to Steve Chang from WideView for providing support for the WT-220U. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dtt200u.h" + +/* debug */ +int dvb_usb_dtt200u_debug; +module_param_named(debug,dvb_usb_dtt200u_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2 (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = SET_INIT; + + if (onoff) + dvb_usb_generic_write(d,&b,2); + + return 0; +} + +static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + u8 b_streaming[2] = { SET_STREAMING, onoff }; + u8 b_rst_pid = RESET_PID_FILTER; + + dvb_usb_generic_write(adap->dev, b_streaming, 2); + + if (onoff == 0) + dvb_usb_generic_write(adap->dev, &b_rst_pid, 1); + return 0; +} + +static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) +{ + u8 b_pid[4]; + pid = onoff ? pid : 0; + + b_pid[0] = SET_PID_FILTER; + b_pid[1] = index; + b_pid[2] = pid & 0xff; + b_pid[3] = (pid >> 8) & 0x1f; + + return dvb_usb_generic_write(adap->dev, b_pid, 4); +} + +/* remote control */ +/* key list for the tiny remote control (Yakumo, don't know about the others) */ +static struct rc_map_table rc_map_dtt200u_table[] = { + { 0x8001, KEY_MUTE }, + { 0x8002, KEY_CHANNELDOWN }, + { 0x8003, KEY_VOLUMEDOWN }, + { 0x8004, KEY_1 }, + { 0x8005, KEY_2 }, + { 0x8006, KEY_3 }, + { 0x8007, KEY_4 }, + { 0x8008, KEY_5 }, + { 0x8009, KEY_6 }, + { 0x800a, KEY_7 }, + { 0x800c, KEY_ZOOM }, + { 0x800d, KEY_0 }, + { 0x800e, KEY_SELECT }, + { 0x8012, KEY_POWER }, + { 0x801a, KEY_CHANNELUP }, + { 0x801b, KEY_8 }, + { 0x801e, KEY_VOLUMEUP }, + { 0x801f, KEY_9 }, +}; + +static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5],cmd = GET_RC_CODE; + dvb_usb_generic_rw(d,&cmd,1,key,5,0); + dvb_usb_nec_rc_key_to_event(d,key,event,state); + if (key[0] != 0) + deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + return 0; +} + +static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap) +{ + adap->fe_adap[0].fe = dtt200u_fe_attach(adap->dev); + return 0; +} + +static struct dvb_usb_device_properties dtt200u_properties; +static struct dvb_usb_device_properties wt220u_fc_properties; +static struct dvb_usb_device_properties wt220u_properties; +static struct dvb_usb_device_properties wt220u_zl0353_properties; +static struct dvb_usb_device_properties wt220u_miglia_properties; + +static int dtt200u_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (0 == dvb_usb_device_init(intf, &dtt200u_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &wt220u_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &wt220u_fc_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &wt220u_miglia_properties, + THIS_MODULE, NULL, adapter_nr)) + return 0; + + return -ENODEV; +} + +static struct usb_device_id dtt200u_usb_table [] = { + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_COLD) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_COLD) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_COLD) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZAP250_COLD) }, + { USB_DEVICE(USB_VID_MIGLIA, USB_PID_WT220U_ZAP250_COLD) }, + { 0 }, +}; +MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); + +static struct dvb_usb_device_properties dtt200u_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-dtt200u-01.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, + .pid_filter_count = 15, + + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .power_ctrl = dtt200u_power_ctrl, + + .rc.legacy = { + .rc_interval = 300, + .rc_map_table = rc_map_dtt200u_table, + .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { .name = "WideView/Yuan/Yakumo/Hama/Typhoon DVB-T USB2.0 (WT-200U)", + .cold_ids = { &dtt200u_usb_table[0], NULL }, + .warm_ids = { &dtt200u_usb_table[1], NULL }, + }, + { NULL }, + } +}; + +static struct dvb_usb_device_properties wt220u_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-02.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, + .pid_filter_count = 15, + + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .power_ctrl = dtt200u_power_ctrl, + + .rc.legacy = { + .rc_interval = 300, + .rc_map_table = rc_map_dtt200u_table, + .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)", + .cold_ids = { &dtt200u_usb_table[2], &dtt200u_usb_table[8], NULL }, + .warm_ids = { &dtt200u_usb_table[3], NULL }, + }, + { NULL }, + } +}; + +static struct dvb_usb_device_properties wt220u_fc_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-fc03.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, + .pid_filter_count = 15, + + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .power_ctrl = dtt200u_power_ctrl, + + .rc.legacy = { + .rc_interval = 300, + .rc_map_table = rc_map_dtt200u_table, + .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)", + .cold_ids = { &dtt200u_usb_table[6], NULL }, + .warm_ids = { &dtt200u_usb_table[7], NULL }, + }, + { NULL }, + } +}; + +static struct dvb_usb_device_properties wt220u_zl0353_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-zl0353-01.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, + .pid_filter_count = 15, + + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .power_ctrl = dtt200u_power_ctrl, + + .rc.legacy = { + .rc_interval = 300, + .rc_map_table = rc_map_dtt200u_table, + .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (based on ZL353)", + .cold_ids = { &dtt200u_usb_table[4], NULL }, + .warm_ids = { &dtt200u_usb_table[5], NULL }, + }, + { NULL }, + } +}; + +static struct dvb_usb_device_properties wt220u_miglia_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-miglia-01.fw", + + .num_adapters = 1, + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (Miglia)", + .cold_ids = { &dtt200u_usb_table[9], NULL }, + /* This device turns into WT220U_ZL0353_WARM when fw + has been uploaded */ + .warm_ids = { NULL }, + }, + { NULL }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver dtt200u_usb_driver = { + .name = "dvb_usb_dtt200u", + .probe = dtt200u_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dtt200u_usb_table, +}; + +module_usb_driver(dtt200u_usb_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/dtt200u.h b/drivers/media/usb/dvb-usb/dtt200u.h new file mode 100644 index 000000000000..005b0a7df358 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dtt200u.h @@ -0,0 +1,57 @@ +/* Common header file of Linux driver for the WideView/ Yakumo/ Hama/ + * Typhoon/ Yuan DVB-T USB2.0 receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_DTT200U_H_ +#define _DVB_USB_DTT200U_H_ + +#define DVB_USB_LOG_PREFIX "dtt200u" + +#include "dvb-usb.h" + +extern int dvb_usb_dtt200u_debug; +#define deb_info(args...) dprintk(dvb_usb_dtt200u_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_dtt200u_debug,0x02,args) + +/* guessed protocol description (reverse engineered): + * read + * 00 - USB type 0x02 for usb2.0, 0x01 for usb1.1 + * 88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal) + */ + +#define GET_SPEED 0x00 +#define GET_TUNE_STATUS 0x81 +#define GET_RC_CODE 0x84 +#define GET_CONFIGURATION 0x88 +#define GET_AGC 0x89 +#define GET_SNR 0x8a +#define GET_VIT_ERR_CNT 0x8c +#define GET_RS_ERR_CNT 0x8d +#define GET_RS_UNCOR_BLK_CNT 0x8e + +/* write + * 01 - init + * 02 - frequency (divided by 250000) + * 03 - bandwidth + * 04 - pid table (index pid(7:0) pid(12:8)) + * 05 - reset the pid table + * 08 - transfer switch + */ + +#define SET_INIT 0x01 +#define SET_RF_FREQ 0x02 +#define SET_BANDWIDTH 0x03 +#define SET_PID_FILTER 0x04 +#define RESET_PID_FILTER 0x05 +#define SET_STREAMING 0x08 + +extern struct dvb_frontend * dtt200u_fe_attach(struct dvb_usb_device *d); + +#endif diff --git a/drivers/media/usb/dvb-usb/dtv5100.c b/drivers/media/usb/dvb-usb/dtv5100.c new file mode 100644 index 000000000000..3d11df41cac0 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dtv5100.c @@ -0,0 +1,224 @@ +/* + * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T + * + * Copyright (C) 2008 Antoine Jacquet + * http://royale.zerezo.com/dtv5100/ + * + * Inspired by gl861.c and au6610.c drivers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "dtv5100.h" +#include "zl10353.h" +#include "qt1010.h" + +/* debug */ +static int dvb_usb_dtv5100_debug; +module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 request; + u8 type; + u16 value; + u16 index; + + switch (wlen) { + case 1: + /* write { reg }, read { value } */ + request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ : + DTV5100_TUNER_READ); + type = USB_TYPE_VENDOR | USB_DIR_IN; + value = 0; + break; + case 2: + /* write { reg, value } */ + request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE : + DTV5100_TUNER_WRITE); + type = USB_TYPE_VENDOR | USB_DIR_OUT; + value = wbuf[1]; + break; + default: + warn("wlen = %x, aborting.", wlen); + return -EINVAL; + } + index = (addr << 8) + wbuf[0]; + + msleep(1); /* avoid I2C errors */ + return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request, + type, value, index, rbuf, rlen, + DTV5100_USB_TIMEOUT); +} + +/* I2C */ +static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, msg[i+1].buf, + msg[i+1].len) < 0) + break; + i++; + } else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 dtv5100_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dtv5100_i2c_algo = { + .master_xfer = dtv5100_i2c_xfer, + .functionality = dtv5100_i2c_func, +}; + +/* Callbacks for DVB USB */ +static struct zl10353_config dtv5100_zl10353_config = { + .demod_address = DTV5100_DEMOD_ADDR, + .no_tuner = 1, + .parallel_ts = 1, +}; + +static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap) +{ + adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe == NULL) + return -EIO; + + /* disable i2c gate, or it won't work... is this safe? */ + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; + + return 0; +} + +static struct qt1010_config dtv5100_qt1010_config = { + .i2c_address = DTV5100_TUNER_ADDR +}; + +static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap) +{ + return dvb_attach(qt1010_attach, + adap->fe_adap[0].fe, &adap->dev->i2c_adap, + &dtv5100_qt1010_config) == NULL ? -ENODEV : 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties dtv5100_properties; + +static int dtv5100_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int i, ret; + struct usb_device *udev = interface_to_usbdev(intf); + + /* initialize non qt1010/zl10353 part? */ + for (i = 0; dtv5100_init[i].request; i++) { + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + dtv5100_init[i].request, + USB_TYPE_VENDOR | USB_DIR_OUT, + dtv5100_init[i].value, + dtv5100_init[i].index, NULL, 0, + DTV5100_USB_TIMEOUT); + if (ret) + return ret; + } + + ret = dvb_usb_device_init(intf, &dtv5100_properties, + THIS_MODULE, NULL, adapter_nr); + if (ret) + return ret; + + return 0; +} + +static struct usb_device_id dtv5100_table[] = { + { USB_DEVICE(0x06be, 0xa232) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, dtv5100_table); + +static struct dvb_usb_device_properties dtv5100_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = 0, + + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + .frontend_attach = dtv5100_frontend_attach, + .tuner_attach = dtv5100_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } }, + + .i2c_algo = &dtv5100_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "AME DTV-5100 USB2.0 DVB-T", + .cold_ids = { NULL }, + .warm_ids = { &dtv5100_table[0], NULL }, + }, + } +}; + +static struct usb_driver dtv5100_driver = { + .name = "dvb_usb_dtv5100", + .probe = dtv5100_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dtv5100_table, +}; + +module_usb_driver(dtv5100_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/dtv5100.h b/drivers/media/usb/dvb-usb/dtv5100.h new file mode 100644 index 000000000000..93e96e04a82a --- /dev/null +++ b/drivers/media/usb/dvb-usb/dtv5100.h @@ -0,0 +1,51 @@ +/* + * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T + * + * Copyright (C) 2008 Antoine Jacquet + * http://royale.zerezo.com/dtv5100/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DVB_USB_DTV5100_H_ +#define _DVB_USB_DTV5100_H_ + +#define DVB_USB_LOG_PREFIX "dtv5100" +#include "dvb-usb.h" + +#define DTV5100_USB_TIMEOUT 500 + +#define DTV5100_DEMOD_ADDR 0x00 +#define DTV5100_DEMOD_WRITE 0xc0 +#define DTV5100_DEMOD_READ 0xc1 + +#define DTV5100_TUNER_ADDR 0xc4 +#define DTV5100_TUNER_WRITE 0xc7 +#define DTV5100_TUNER_READ 0xc8 + +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "AME DTV-5100 USB2.0 DVB-T" + +static struct { + u8 request; + u8 value; + u16 index; +} dtv5100_init[] = { + { 0x000000c5, 0x00000000, 0x00000001 }, + { 0x000000c5, 0x00000001, 0x00000001 }, + { } /* Terminating entry */ +}; + +#endif diff --git a/drivers/media/usb/dvb-usb/dvb-usb-common.h b/drivers/media/usb/dvb-usb/dvb-usb-common.h new file mode 100644 index 000000000000..6b7b2a89242e --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb-common.h @@ -0,0 +1,52 @@ +/* dvb-usb-common.h is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * a header file containing prototypes and types for internal use of the dvb-usb-lib + */ +#ifndef _DVB_USB_COMMON_H_ +#define _DVB_USB_COMMON_H_ + +#define DVB_USB_LOG_PREFIX "dvb-usb" +#include "dvb-usb.h" + +extern int dvb_usb_debug; +extern int dvb_usb_disable_rc_polling; + +#define deb_info(args...) dprintk(dvb_usb_debug,0x001,args) +#define deb_xfer(args...) dprintk(dvb_usb_debug,0x002,args) +#define deb_pll(args...) dprintk(dvb_usb_debug,0x004,args) +#define deb_ts(args...) dprintk(dvb_usb_debug,0x008,args) +#define deb_err(args...) dprintk(dvb_usb_debug,0x010,args) +#define deb_rc(args...) dprintk(dvb_usb_debug,0x020,args) +#define deb_fw(args...) dprintk(dvb_usb_debug,0x040,args) +#define deb_mem(args...) dprintk(dvb_usb_debug,0x080,args) +#define deb_uxfer(args...) dprintk(dvb_usb_debug,0x100,args) + +/* commonly used methods */ +extern int dvb_usb_download_firmware(struct usb_device *, struct dvb_usb_device_properties *); + +extern int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff); + +extern int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props); +extern int usb_urb_exit(struct usb_data_stream *stream); +extern int usb_urb_submit(struct usb_data_stream *stream); +extern int usb_urb_kill(struct usb_data_stream *stream); + +extern int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap); +extern int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap); + +extern int dvb_usb_i2c_init(struct dvb_usb_device *); +extern int dvb_usb_i2c_exit(struct dvb_usb_device *); + +extern int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, + short *adapter_nums); +extern int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap); +extern int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap); +extern int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap); + +extern int dvb_usb_remote_init(struct dvb_usb_device *); +extern int dvb_usb_remote_exit(struct dvb_usb_device *); + +#endif diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c new file mode 100644 index 000000000000..719413b15f20 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c @@ -0,0 +1,288 @@ +/* dvb-usb-dvb.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for initializing and handling the + * linux-dvb API. + */ +#include "dvb-usb-common.h" + +/* does the complete input transfer handling */ +static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; + int newfeedcount, ret; + + if (adap == NULL) + return -ENODEV; + + if ((adap->active_fe < 0) || + (adap->active_fe >= adap->num_frontends_initialized)) { + return -EINVAL; + } + + newfeedcount = adap->feedcount + (onoff ? 1 : -1); + + /* stop feed before setting a new pid if there will be no pid anymore */ + if (newfeedcount == 0) { + deb_ts("stop feeding\n"); + usb_urb_kill(&adap->fe_adap[adap->active_fe].stream); + + if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) { + ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0); + if (ret < 0) { + err("error while stopping stream."); + return ret; + } + } + } + + adap->feedcount = newfeedcount; + + /* activate the pid on the device specific pid_filter */ + deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n", + adap->fe_adap[adap->active_fe].pid_filtering ? + "yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid, + dvbdmxfeed->index, onoff ? "on" : "off"); + if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->fe_adap[adap->active_fe].pid_filtering && + adap->props.fe[adap->active_fe].pid_filter != NULL) + adap->props.fe[adap->active_fe].pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, onoff); + + /* start the feed if this was the first feed and there is still a feed + * for reception. + */ + if (adap->feedcount == onoff && adap->feedcount > 0) { + deb_ts("submitting all URBs\n"); + usb_urb_submit(&adap->fe_adap[adap->active_fe].stream); + + deb_ts("controlling pid parser\n"); + if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->props.fe[adap->active_fe].caps & + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF && + adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) { + ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap, + adap->fe_adap[adap->active_fe].pid_filtering); + if (ret < 0) { + err("could not handle pid_parser"); + return ret; + } + } + deb_ts("start feeding\n"); + if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) { + ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1); + if (ret < 0) { + err("error while enabling fifo."); + return ret; + } + } + + } + return 0; +} + +static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); + return dvb_usb_ctrl_feed(dvbdmxfeed,1); +} + +static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type); + return dvb_usb_ctrl_feed(dvbdmxfeed,0); +} + +int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) +{ + int i; + int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name, + adap->dev->owner, &adap->dev->udev->dev, + adapter_nums); + + if (ret < 0) { + deb_info("dvb_register_adapter failed: error %d", ret); + goto err; + } + adap->dvb_adap.priv = adap; + + if (adap->dev->props.read_mac_address) { + if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0) + info("MAC address: %pM",adap->dvb_adap.proposed_mac); + else + err("MAC address reading failed."); + } + + + adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + adap->demux.priv = adap; + + adap->demux.filternum = 0; + for (i = 0; i < adap->props.num_frontends; i++) { + if (adap->demux.filternum < adap->fe_adap[i].max_feed_count) + adap->demux.filternum = adap->fe_adap[i].max_feed_count; + } + adap->demux.feednum = adap->demux.filternum; + adap->demux.start_feed = dvb_usb_start_feed; + adap->demux.stop_feed = dvb_usb_stop_feed; + adap->demux.write_to_decoder = NULL; + if ((ret = dvb_dmx_init(&adap->demux)) < 0) { + err("dvb_dmx_init failed: error %d",ret); + goto err_dmx; + } + + adap->dmxdev.filternum = adap->demux.filternum; + adap->dmxdev.demux = &adap->demux.dmx; + adap->dmxdev.capabilities = 0; + if ((ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap)) < 0) { + err("dvb_dmxdev_init failed: error %d",ret); + goto err_dmx_dev; + } + + if ((ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net, + &adap->demux.dmx)) < 0) { + err("dvb_net_init failed: error %d",ret); + goto err_net_init; + } + + adap->state |= DVB_USB_ADAP_STATE_DVB; + return 0; + +err_net_init: + dvb_dmxdev_release(&adap->dmxdev); +err_dmx_dev: + dvb_dmx_release(&adap->demux); +err_dmx: + dvb_unregister_adapter(&adap->dvb_adap); +err: + return ret; +} + +int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap) +{ + if (adap->state & DVB_USB_ADAP_STATE_DVB) { + deb_info("unregistering DVB part\n"); + dvb_net_release(&adap->dvb_net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->dvb_adap); + adap->state &= ~DVB_USB_ADAP_STATE_DVB; + } + return 0; +} + +static int dvb_usb_set_active_fe(struct dvb_frontend *fe, int onoff) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + + int ret = (adap->props.frontend_ctrl) ? + adap->props.frontend_ctrl(fe, onoff) : 0; + + if (ret < 0) { + err("frontend_ctrl request failed"); + return ret; + } + if (onoff) + adap->active_fe = fe->id; + + return 0; +} + +static int dvb_usb_fe_wakeup(struct dvb_frontend *fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + + dvb_usb_device_power_ctrl(adap->dev, 1); + + dvb_usb_set_active_fe(fe, 1); + + if (adap->fe_adap[fe->id].fe_init) + adap->fe_adap[fe->id].fe_init(fe); + + return 0; +} + +static int dvb_usb_fe_sleep(struct dvb_frontend *fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + + if (adap->fe_adap[fe->id].fe_sleep) + adap->fe_adap[fe->id].fe_sleep(fe); + + dvb_usb_set_active_fe(fe, 0); + + return dvb_usb_device_power_ctrl(adap->dev, 0); +} + +int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap) +{ + int ret, i; + + /* register all given adapter frontends */ + for (i = 0; i < adap->props.num_frontends; i++) { + + if (adap->props.fe[i].frontend_attach == NULL) { + err("strange: '%s' #%d,%d " + "doesn't want to attach a frontend.", + adap->dev->desc->name, adap->id, i); + + return 0; + } + + ret = adap->props.fe[i].frontend_attach(adap); + if (ret || adap->fe_adap[i].fe == NULL) { + /* only print error when there is no FE at all */ + if (i == 0) + err("no frontend was attached by '%s'", + adap->dev->desc->name); + + return 0; + } + + adap->fe_adap[i].fe->id = i; + + /* re-assign sleep and wakeup functions */ + adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init; + adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup; + adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep; + adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep; + + if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) { + err("Frontend %d registration failed.", i); + dvb_frontend_detach(adap->fe_adap[i].fe); + adap->fe_adap[i].fe = NULL; + /* In error case, do not try register more FEs, + * still leaving already registered FEs alive. */ + if (i == 0) + return -ENODEV; + else + return 0; + } + + /* only attach the tuner if the demod is there */ + if (adap->props.fe[i].tuner_attach != NULL) + adap->props.fe[i].tuner_attach(adap); + + adap->num_frontends_initialized++; + } + + return 0; +} + +int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap) +{ + int i = adap->num_frontends_initialized - 1; + + /* unregister all given adapter frontends */ + for (; i >= 0; i--) { + if (adap->fe_adap[i].fe != NULL) { + dvb_unregister_frontend(adap->fe_adap[i].fe); + dvb_frontend_detach(adap->fe_adap[i].fe); + } + } + adap->num_frontends_initialized = 0; + + return 0; +} diff --git a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c new file mode 100644 index 000000000000..733a7ff7b207 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c @@ -0,0 +1,146 @@ +/* dvb-usb-firmware.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for downloading the firmware to Cypress FX 1 and 2 based devices. + * + * FIXME: This part does actually not belong to dvb-usb, but to the usb-subsystem. + */ +#include "dvb-usb-common.h" + +#include + +struct usb_cypress_controller { + int id; + const char *name; /* name of the usb controller */ + u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */ +}; + +static struct usb_cypress_controller cypress[] = { + { .id = DEVICE_SPECIFIC, .name = "Device specific", .cpu_cs_register = 0 }, + { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 }, + { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 }, + { .id = CYPRESS_FX2, .name = "Cypress FX2", .cpu_cs_register = 0xe600 }, +}; + +/* + * load a firmware packet to the device + */ +static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev,0), + 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000); +} + +int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) +{ + struct hexline hx; + u8 reset; + int ret,pos=0; + + /* stop the CPU */ + reset = 1; + if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1) + err("could not stop the USB controller CPU."); + + while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) { + deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk); + ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len); + + if (ret != hx.len) { + err("error while transferring firmware " + "(transferred size: %d, block size: %d)", + ret,hx.len); + ret = -EINVAL; + break; + } + } + if (ret < 0) { + err("firmware download failed at %d with %d",pos,ret); + return ret; + } + + if (ret == 0) { + /* restart the CPU */ + reset = 0; + if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + } else + ret = -EIO; + + return ret; +} +EXPORT_SYMBOL(usb_cypress_load_firmware); + +int dvb_usb_download_firmware(struct usb_device *udev, struct dvb_usb_device_properties *props) +{ + int ret; + const struct firmware *fw = NULL; + + if ((ret = request_firmware(&fw, props->firmware, &udev->dev)) != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)", + props->firmware,ret); + return ret; + } + + info("downloading firmware from file '%s'",props->firmware); + + switch (props->usb_ctrl) { + case CYPRESS_AN2135: + case CYPRESS_AN2235: + case CYPRESS_FX2: + ret = usb_cypress_load_firmware(udev, fw, props->usb_ctrl); + break; + case DEVICE_SPECIFIC: + if (props->download_firmware) + ret = props->download_firmware(udev,fw); + else { + err("BUG: driver didn't specified a download_firmware-callback, although it claims to have a DEVICE_SPECIFIC one."); + ret = -EINVAL; + } + break; + default: + ret = -EINVAL; + break; + } + + release_firmware(fw); + return ret; +} + +int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx, + int *pos) +{ + u8 *b = (u8 *) &fw->data[*pos]; + int data_offs = 4; + if (*pos >= fw->size) + return 0; + + memset(hx,0,sizeof(struct hexline)); + + hx->len = b[0]; + + if ((*pos + hx->len + 4) >= fw->size) + return -EINVAL; + + hx->addr = b[1] | (b[2] << 8); + hx->type = b[3]; + + if (hx->type == 0x04) { + /* b[4] and b[5] are the Extended linear address record data field */ + hx->addr |= (b[4] << 24) | (b[5] << 16); +/* hx->len -= 2; + data_offs += 2; */ + } + memcpy(hx->data,&b[data_offs],hx->len); + hx->chk = b[hx->len + data_offs]; + + *pos += hx->len + 5; + + return *pos; +} +EXPORT_SYMBOL(dvb_usb_get_hexline); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-i2c.c b/drivers/media/usb/dvb-usb/dvb-usb-i2c.c new file mode 100644 index 000000000000..88e4a62abc44 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb-i2c.c @@ -0,0 +1,43 @@ +/* dvb-usb-i2c.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for (de-)initializing an I2C adapter. + */ +#include "dvb-usb-common.h" + +int dvb_usb_i2c_init(struct dvb_usb_device *d) +{ + int ret = 0; + + if (!(d->props.caps & DVB_USB_IS_AN_I2C_ADAPTER)) + return 0; + + if (d->props.i2c_algo == NULL) { + err("no i2c algorithm specified"); + return -EINVAL; + } + + strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); + d->i2c_adap.algo = d->props.i2c_algo; + d->i2c_adap.algo_data = NULL; + d->i2c_adap.dev.parent = &d->udev->dev; + + i2c_set_adapdata(&d->i2c_adap, d); + + if ((ret = i2c_add_adapter(&d->i2c_adap)) < 0) + err("could not add i2c adapter"); + + d->state |= DVB_USB_STATE_I2C; + + return ret; +} + +int dvb_usb_i2c_exit(struct dvb_usb_device *d) +{ + if (d->state & DVB_USB_STATE_I2C) + i2c_del_adapter(&d->i2c_adap); + d->state &= ~DVB_USB_STATE_I2C; + return 0; +} diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c new file mode 100644 index 000000000000..169196ec2d4e --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -0,0 +1,304 @@ +/* + * DVB USB library - provides a generic interface for a DVB USB device driver. + * + * dvb-usb-init.c + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dvb-usb-common.h" + +/* debug */ +int dvb_usb_debug; +module_param_named(debug, dvb_usb_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64,mem=128,uxfer=256 (or-able))." DVB_USB_DEBUG_STATUS); + +int dvb_usb_disable_rc_polling; +module_param_named(disable_rc_polling, dvb_usb_disable_rc_polling, int, 0644); +MODULE_PARM_DESC(disable_rc_polling, "disable remote control polling (default: 0)."); + +static int dvb_usb_force_pid_filter_usage; +module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage, int, 0444); +MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID filter, if any (default: 0)."); + +static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) +{ + struct dvb_usb_adapter *adap; + int ret, n, o; + + for (n = 0; n < d->props.num_adapters; n++) { + adap = &d->adapter[n]; + adap->dev = d; + adap->id = n; + + memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties)); + + for (o = 0; o < adap->props.num_frontends; o++) { + struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; + /* speed - when running at FULL speed we need a HW PID filter */ + if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { + err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); + return -ENODEV; + } + + if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || + (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { + info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } else { + info("will pass the complete MPEG2 transport stream to the software demuxer."); + adap->fe_adap[o].pid_filtering = 0; + adap->fe_adap[o].max_feed_count = 255; + } + + if (!adap->fe_adap[o].pid_filtering && + dvb_usb_force_pid_filter_usage && + props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { + info("pid filter enabled by module option."); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } + + if (props->size_of_priv > 0) { + adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); + if (adap->fe_adap[o].priv == NULL) { + err("no memory for priv for adapter %d fe %d.", n, o); + return -ENOMEM; + } + } + } + + if (adap->props.size_of_priv > 0) { + adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL); + if (adap->priv == NULL) { + err("no memory for priv for adapter %d.", n); + return -ENOMEM; + } + } + + if ((ret = dvb_usb_adapter_stream_init(adap)) || + (ret = dvb_usb_adapter_dvb_init(adap, adapter_nrs)) || + (ret = dvb_usb_adapter_frontend_init(adap))) { + return ret; + } + + /* use exclusive FE lock if there is multiple shared FEs */ + if (adap->fe_adap[1].fe) + adap->dvb_adap.mfe_shared = 1; + + d->num_adapters_initialized++; + d->state |= DVB_USB_STATE_DVB; + } + + /* + * when reloading the driver w/o replugging the device + * sometimes a timeout occures, this helps + */ + if (d->props.generic_bulk_ctrl_endpoint != 0) { + usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + } + + return 0; +} + +static int dvb_usb_adapter_exit(struct dvb_usb_device *d) +{ + int n; + + for (n = 0; n < d->num_adapters_initialized; n++) { + dvb_usb_adapter_frontend_exit(&d->adapter[n]); + dvb_usb_adapter_dvb_exit(&d->adapter[n]); + dvb_usb_adapter_stream_exit(&d->adapter[n]); + kfree(d->adapter[n].priv); + } + d->num_adapters_initialized = 0; + d->state &= ~DVB_USB_STATE_DVB; + return 0; +} + + +/* general initialization functions */ +static int dvb_usb_exit(struct dvb_usb_device *d) +{ + deb_info("state before exiting everything: %x\n", d->state); + dvb_usb_remote_exit(d); + dvb_usb_adapter_exit(d); + dvb_usb_i2c_exit(d); + deb_info("state should be zero now: %x\n", d->state); + d->state = DVB_USB_STATE_INIT; + kfree(d->priv); + kfree(d); + return 0; +} + +static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) +{ + int ret = 0; + + mutex_init(&d->usb_mutex); + mutex_init(&d->i2c_mutex); + + d->state = DVB_USB_STATE_INIT; + + if (d->props.size_of_priv > 0) { + d->priv = kzalloc(d->props.size_of_priv, GFP_KERNEL); + if (d->priv == NULL) { + err("no memory for priv in 'struct dvb_usb_device'"); + return -ENOMEM; + } + } + + /* check the capabilities and set appropriate variables */ + dvb_usb_device_power_ctrl(d, 1); + + if ((ret = dvb_usb_i2c_init(d)) || + (ret = dvb_usb_adapter_init(d, adapter_nums))) { + dvb_usb_exit(d); + return ret; + } + + if ((ret = dvb_usb_remote_init(d))) + err("could not initialize remote control."); + + dvb_usb_device_power_ctrl(d, 0); + + return 0; +} + +/* determine the name and the state of the just found USB device */ +static struct dvb_usb_device_description *dvb_usb_find_device(struct usb_device *udev, struct dvb_usb_device_properties *props, int *cold) +{ + int i, j; + struct dvb_usb_device_description *desc = NULL; + + *cold = -1; + + for (i = 0; i < props->num_device_descs; i++) { + + for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].cold_ids[j] != NULL; j++) { + deb_info("check for cold %x %x\n", props->devices[i].cold_ids[j]->idVendor, props->devices[i].cold_ids[j]->idProduct); + if (props->devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + props->devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 1; + desc = &props->devices[i]; + break; + } + } + + if (desc != NULL) + break; + + for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].warm_ids[j] != NULL; j++) { + deb_info("check for warm %x %x\n", props->devices[i].warm_ids[j]->idVendor, props->devices[i].warm_ids[j]->idProduct); + if (props->devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + props->devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 0; + desc = &props->devices[i]; + break; + } + } + } + + if (desc != NULL && props->identify_state != NULL) + props->identify_state(udev, props, &desc, cold); + + return desc; +} + +int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + if (onoff) + d->powered++; + else + d->powered--; + + if (d->powered == 0 || (onoff && d->powered == 1)) { /* when switching from 1 to 0 or from 0 to 1 */ + deb_info("power control: %d\n", onoff); + if (d->props.power_ctrl) + return d->props.power_ctrl(d, onoff); + } + return 0; +} + +/* + * USB + */ +int dvb_usb_device_init(struct usb_interface *intf, + struct dvb_usb_device_properties *props, + struct module *owner, struct dvb_usb_device **du, + short *adapter_nums) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct dvb_usb_device *d = NULL; + struct dvb_usb_device_description *desc = NULL; + + int ret = -ENOMEM, cold = 0; + + if (du != NULL) + *du = NULL; + + if ((desc = dvb_usb_find_device(udev, props, &cold)) == NULL) { + deb_err("something went very wrong, device was not found in current device list - let's see what comes next.\n"); + return -ENODEV; + } + + if (cold) { + info("found a '%s' in cold state, will try to load a firmware", desc->name); + ret = dvb_usb_download_firmware(udev, props); + if (!props->no_reconnect || ret != 0) + return ret; + } + + info("found a '%s' in warm state.", desc->name); + d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); + if (d == NULL) { + err("no memory for 'struct dvb_usb_device'"); + return -ENOMEM; + } + + d->udev = udev; + memcpy(&d->props, props, sizeof(struct dvb_usb_device_properties)); + d->desc = desc; + d->owner = owner; + + usb_set_intfdata(intf, d); + + if (du != NULL) + *du = d; + + ret = dvb_usb_init(d, adapter_nums); + + if (ret == 0) + info("%s successfully initialized and connected.", desc->name); + else + info("%s error while loading driver (%d)", desc->name, ret); + return ret; +} +EXPORT_SYMBOL(dvb_usb_device_init); + +void dvb_usb_device_exit(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + const char *name = "generic DVB-USB module"; + + usb_set_intfdata(intf, NULL); + if (d != NULL && d->desc != NULL) { + name = d->desc->name; + dvb_usb_exit(d); + } + info("%s successfully deinitialized and disconnected.", name); + +} +EXPORT_SYMBOL(dvb_usb_device_exit); + +MODULE_VERSION("1.0"); +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("A library module containing commonly used USB and DVB function USB DVB devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c new file mode 100644 index 000000000000..41bacff24960 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c @@ -0,0 +1,391 @@ +/* dvb-usb-remote.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for initializing the input-device and for handling remote-control-queries. + */ +#include "dvb-usb-common.h" +#include + +static unsigned int +legacy_dvb_usb_get_keymap_index(const struct input_keymap_entry *ke, + struct rc_map_table *keymap, + unsigned int keymap_size) +{ + unsigned int index; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + } else { + if (input_scancode_to_scalar(ke, &scancode)) + return keymap_size; + + /* See if we can match the raw key code. */ + for (index = 0; index < keymap_size; index++) + if (keymap[index].scancode == scancode) + break; + + /* See if there is an unused hole in the map */ + if (index >= keymap_size) { + for (index = 0; index < keymap_size; index++) { + if (keymap[index].keycode == KEY_RESERVED || + keymap[index].keycode == KEY_UNKNOWN) { + break; + } + } + } + } + + return index; +} + +static int legacy_dvb_usb_getkeycode(struct input_dev *dev, + struct input_keymap_entry *ke) +{ + struct dvb_usb_device *d = input_get_drvdata(dev); + struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; + unsigned int keymap_size = d->props.rc.legacy.rc_map_size; + unsigned int index; + + index = legacy_dvb_usb_get_keymap_index(ke, keymap, keymap_size); + if (index >= keymap_size) + return -EINVAL; + + ke->keycode = keymap[index].keycode; + if (ke->keycode == KEY_UNKNOWN) + ke->keycode = KEY_RESERVED; + ke->len = sizeof(keymap[index].scancode); + memcpy(&ke->scancode, &keymap[index].scancode, ke->len); + ke->index = index; + + return 0; +} + +static int legacy_dvb_usb_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) +{ + struct dvb_usb_device *d = input_get_drvdata(dev); + struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; + unsigned int keymap_size = d->props.rc.legacy.rc_map_size; + unsigned int index; + + index = legacy_dvb_usb_get_keymap_index(ke, keymap, keymap_size); + /* + * FIXME: Currently, it is not possible to increase the size of + * scancode table. For it to happen, one possibility + * would be to allocate a table with key_map_size + 1, + * copying data, appending the new key on it, and freeing + * the old one - or maybe just allocating some spare space + */ + if (index >= keymap_size) + return -EINVAL; + + *old_keycode = keymap[index].keycode; + keymap->keycode = ke->keycode; + __set_bit(ke->keycode, dev->keybit); + + if (*old_keycode != KEY_RESERVED) { + __clear_bit(*old_keycode, dev->keybit); + for (index = 0; index < keymap_size; index++) { + if (keymap[index].keycode == *old_keycode) { + __set_bit(*old_keycode, dev->keybit); + break; + } + } + } + + return 0; +} + +/* Remote-control poll function - called every dib->rc_query_interval ms to see + * whether the remote control has received anything. + * + * TODO: Fix the repeat rate of the input device. + */ +static void legacy_dvb_usb_read_remote_control(struct work_struct *work) +{ + struct dvb_usb_device *d = + container_of(work, struct dvb_usb_device, rc_query_work.work); + u32 event; + int state; + + /* TODO: need a lock here. We can simply skip checking for the remote control + if we're busy. */ + + /* when the parameter has been set to 1 via sysfs while the driver was running */ + if (dvb_usb_disable_rc_polling) + return; + + if (d->props.rc.legacy.rc_query(d,&event,&state)) { + err("error while querying for an remote control event."); + goto schedule; + } + + + switch (state) { + case REMOTE_NO_KEY_PRESSED: + break; + case REMOTE_KEY_PRESSED: + deb_rc("key pressed\n"); + d->last_event = event; + case REMOTE_KEY_REPEAT: + deb_rc("key repeated\n"); + input_event(d->input_dev, EV_KEY, event, 1); + input_sync(d->input_dev); + input_event(d->input_dev, EV_KEY, d->last_event, 0); + input_sync(d->input_dev); + break; + default: + break; + } + +/* improved repeat handling ??? + switch (state) { + case REMOTE_NO_KEY_PRESSED: + deb_rc("NO KEY PRESSED\n"); + if (d->last_state != REMOTE_NO_KEY_PRESSED) { + deb_rc("releasing event %d\n",d->last_event); + input_event(d->rc_input_dev, EV_KEY, d->last_event, 0); + input_sync(d->rc_input_dev); + } + d->last_state = REMOTE_NO_KEY_PRESSED; + d->last_event = 0; + break; + case REMOTE_KEY_PRESSED: + deb_rc("KEY PRESSED\n"); + deb_rc("pressing event %d\n",event); + + input_event(d->rc_input_dev, EV_KEY, event, 1); + input_sync(d->rc_input_dev); + + d->last_event = event; + d->last_state = REMOTE_KEY_PRESSED; + break; + case REMOTE_KEY_REPEAT: + deb_rc("KEY_REPEAT\n"); + if (d->last_state != REMOTE_NO_KEY_PRESSED) { + deb_rc("repeating event %d\n",d->last_event); + input_event(d->rc_input_dev, EV_KEY, d->last_event, 2); + input_sync(d->rc_input_dev); + d->last_state = REMOTE_KEY_REPEAT; + } + default: + break; + } +*/ + +schedule: + schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc.legacy.rc_interval)); +} + +static int legacy_dvb_usb_remote_init(struct dvb_usb_device *d) +{ + int i, err, rc_interval; + struct input_dev *input_dev; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + input_dev->name = "IR-receiver inside an USB DVB receiver"; + input_dev->phys = d->rc_phys; + usb_to_input_id(d->udev, &input_dev->id); + input_dev->dev.parent = &d->udev->dev; + d->input_dev = input_dev; + d->rc_dev = NULL; + + input_dev->getkeycode = legacy_dvb_usb_getkeycode; + input_dev->setkeycode = legacy_dvb_usb_setkeycode; + + /* set the bits for the keys */ + deb_rc("key map size: %d\n", d->props.rc.legacy.rc_map_size); + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { + deb_rc("setting bit for event %d item %d\n", + d->props.rc.legacy.rc_map_table[i].keycode, i); + set_bit(d->props.rc.legacy.rc_map_table[i].keycode, input_dev->keybit); + } + + /* setting these two values to non-zero, we have to manage key repeats */ + input_dev->rep[REP_PERIOD] = d->props.rc.legacy.rc_interval; + input_dev->rep[REP_DELAY] = d->props.rc.legacy.rc_interval + 150; + + input_set_drvdata(input_dev, d); + + err = input_register_device(input_dev); + if (err) + input_free_device(input_dev); + + rc_interval = d->props.rc.legacy.rc_interval; + + INIT_DELAYED_WORK(&d->rc_query_work, legacy_dvb_usb_read_remote_control); + + info("schedule remote query interval to %d msecs.", rc_interval); + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(rc_interval)); + + d->state |= DVB_USB_STATE_REMOTE; + + return err; +} + +/* Remote-control poll function - called every dib->rc_query_interval ms to see + * whether the remote control has received anything. + * + * TODO: Fix the repeat rate of the input device. + */ +static void dvb_usb_read_remote_control(struct work_struct *work) +{ + struct dvb_usb_device *d = + container_of(work, struct dvb_usb_device, rc_query_work.work); + int err; + + /* TODO: need a lock here. We can simply skip checking for the remote control + if we're busy. */ + + /* when the parameter has been set to 1 via sysfs while the + * driver was running, or when bulk mode is enabled after IR init + */ + if (dvb_usb_disable_rc_polling || d->props.rc.core.bulk_mode) + return; + + err = d->props.rc.core.rc_query(d); + if (err) + err("error %d while querying for an remote control event.", err); + + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(d->props.rc.core.rc_interval)); +} + +static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d) +{ + int err, rc_interval; + struct rc_dev *dev; + + dev = rc_allocate_device(); + if (!dev) + return -ENOMEM; + + dev->driver_name = d->props.rc.core.module_name; + dev->map_name = d->props.rc.core.rc_codes; + dev->change_protocol = d->props.rc.core.change_protocol; + dev->allowed_protos = d->props.rc.core.allowed_protos; + dev->driver_type = d->props.rc.core.driver_type; + usb_to_input_id(d->udev, &dev->input_id); + dev->input_name = "IR-receiver inside an USB DVB receiver"; + dev->input_phys = d->rc_phys; + dev->dev.parent = &d->udev->dev; + dev->priv = d; + + err = rc_register_device(dev); + if (err < 0) { + rc_free_device(dev); + return err; + } + + d->input_dev = NULL; + d->rc_dev = dev; + + if (!d->props.rc.core.rc_query || d->props.rc.core.bulk_mode) + return 0; + + /* Polling mode - initialize a work queue for handling it */ + INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control); + + rc_interval = d->props.rc.core.rc_interval; + + info("schedule remote query interval to %d msecs.", rc_interval); + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(rc_interval)); + + return 0; +} + +int dvb_usb_remote_init(struct dvb_usb_device *d) +{ + int err; + + if (dvb_usb_disable_rc_polling) + return 0; + + if (d->props.rc.legacy.rc_map_table && d->props.rc.legacy.rc_query) + d->props.rc.mode = DVB_RC_LEGACY; + else if (d->props.rc.core.rc_codes) + d->props.rc.mode = DVB_RC_CORE; + else + return 0; + + usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); + strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); + + /* Start the remote-control polling. */ + if (d->props.rc.legacy.rc_interval < 40) + d->props.rc.legacy.rc_interval = 100; /* default */ + + if (d->props.rc.mode == DVB_RC_LEGACY) + err = legacy_dvb_usb_remote_init(d); + else + err = rc_core_dvb_usb_remote_init(d); + if (err) + return err; + + d->state |= DVB_USB_STATE_REMOTE; + + return 0; +} + +int dvb_usb_remote_exit(struct dvb_usb_device *d) +{ + if (d->state & DVB_USB_STATE_REMOTE) { + cancel_delayed_work_sync(&d->rc_query_work); + if (d->props.rc.mode == DVB_RC_LEGACY) + input_unregister_device(d->input_dev); + else + rc_unregister_device(d->rc_dev); + } + d->state &= ~DVB_USB_STATE_REMOTE; + return 0; +} + +#define DVB_USB_RC_NEC_EMPTY 0x00 +#define DVB_USB_RC_NEC_KEY_PRESSED 0x01 +#define DVB_USB_RC_NEC_KEY_REPEATED 0x02 +int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, + u8 keybuf[5], u32 *event, int *state) +{ + int i; + struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + switch (keybuf[0]) { + case DVB_USB_RC_NEC_EMPTY: + break; + case DVB_USB_RC_NEC_KEY_PRESSED: + if ((u8) ~keybuf[1] != keybuf[2] || + (u8) ~keybuf[3] != keybuf[4]) { + deb_err("remote control checksum failed.\n"); + break; + } + /* See if we can match the raw key code. */ + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) + if (rc5_custom(&keymap[i]) == keybuf[1] && + rc5_data(&keymap[i]) == keybuf[3]) { + *event = keymap[i].keycode; + *state = REMOTE_KEY_PRESSED; + return 0; + } + deb_err("key mapping failed - no appropriate key found in keymapping\n"); + break; + case DVB_USB_RC_NEC_KEY_REPEATED: + *state = REMOTE_KEY_REPEAT; + break; + default: + deb_err("unknown type of remote status: %d\n",keybuf[0]); + break; + } + return 0; +} +EXPORT_SYMBOL(dvb_usb_nec_rc_key_to_event); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-urb.c b/drivers/media/usb/dvb-usb/dvb-usb-urb.c new file mode 100644 index 000000000000..5c8f651344fc --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb-urb.c @@ -0,0 +1,121 @@ +/* dvb-usb-urb.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file keeps functions for initializing and handling the + * USB and URB stuff. + */ +#include "dvb-usb-common.h" + +int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, + u16 rlen, int delay_ms) +{ + int actlen,ret = -ENOMEM; + + if (!d || wbuf == NULL || wlen == 0) + return -EINVAL; + + if (d->props.generic_bulk_ctrl_endpoint == 0) { + err("endpoint for generic control not specified."); + return -EINVAL; + } + + if ((ret = mutex_lock_interruptible(&d->usb_mutex))) + return ret; + + deb_xfer(">>> "); + debug_dump(wbuf,wlen,deb_xfer); + + ret = usb_bulk_msg(d->udev,usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint), wbuf,wlen,&actlen, + 2000); + + if (ret) + err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); + else + ret = actlen != wlen ? -1 : 0; + + /* an answer is expected, and no error before */ + if (!ret && rbuf && rlen) { + if (delay_ms) + msleep(delay_ms); + + ret = usb_bulk_msg(d->udev,usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint_response ? + d->props.generic_bulk_ctrl_endpoint_response : + d->props.generic_bulk_ctrl_endpoint),rbuf,rlen,&actlen, + 2000); + + if (ret) + err("recv bulk message failed: %d",ret); + else { + deb_xfer("<<< "); + debug_dump(rbuf,actlen,deb_xfer); + } + } + + mutex_unlock(&d->usb_mutex); + return ret; +} +EXPORT_SYMBOL(dvb_usb_generic_rw); + +int dvb_usb_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len) +{ + return dvb_usb_generic_rw(d,buf,len,NULL,0,0); +} +EXPORT_SYMBOL(dvb_usb_generic_write); + +static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buffer, size_t length) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB) + dvb_dmx_swfilter(&adap->demux, buffer, length); +} + +static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer, size_t length) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB) + dvb_dmx_swfilter_204(&adap->demux, buffer, length); +} + +static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, + u8 *buffer, size_t length) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB) + dvb_dmx_swfilter_raw(&adap->demux, buffer, length); +} + +int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap) +{ + int i, ret = 0; + for (i = 0; i < adap->props.num_frontends; i++) { + + adap->fe_adap[i].stream.udev = adap->dev->udev; + if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS) + adap->fe_adap[i].stream.complete = + dvb_usb_data_complete_204; + else + if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD) + adap->fe_adap[i].stream.complete = + dvb_usb_data_complete_raw; + else + adap->fe_adap[i].stream.complete = dvb_usb_data_complete; + adap->fe_adap[i].stream.user_priv = adap; + ret = usb_urb_init(&adap->fe_adap[i].stream, + &adap->props.fe[i].stream); + if (ret < 0) + break; + } + return ret; +} + +int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap) +{ + int i; + for (i = 0; i < adap->props.num_frontends; i++) + usb_urb_exit(&adap->fe_adap[i].stream); + return 0; +} diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h new file mode 100644 index 000000000000..aab0f99bc892 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb-usb.h @@ -0,0 +1,483 @@ +/* dvb-usb.h is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * the headerfile, all dvb-usb-drivers have to include. + * + * TODO: clean-up the structures for unused fields and update the comments + */ +#ifndef __DVB_USB_H__ +#define __DVB_USB_H__ + +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" + +#include "dvb-pll.h" + +#include "dvb-usb-ids.h" + +/* debug */ +#ifdef CONFIG_DVB_USB_DEBUG +#define dprintk(var,level,args...) \ + do { if ((var & level)) { printk(args); } } while (0) + +#define debug_dump(b,l,func) {\ + int loop_; \ + for (loop_ = 0; loop_ < l; loop_++) func("%02x ", b[loop_]); \ + func("\n");\ +} +#define DVB_USB_DEBUG_STATUS +#else +#define dprintk(args...) +#define debug_dump(b,l,func) + +#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" + +#endif + +/* generic log methods - taken from usb.h */ +#ifndef DVB_USB_LOG_PREFIX + #define DVB_USB_LOG_PREFIX "dvb-usb (please define a log prefix)" +#endif + +#undef err +#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef info +#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) + +/** + * struct dvb_usb_device_description - name and its according USB IDs + * @name: real name of the box, regardless which DVB USB device class is in use + * @cold_ids: array of struct usb_device_id which describe the device in + * pre-firmware state + * @warm_ids: array of struct usb_device_id which describe the device in + * post-firmware state + * + * Each DVB USB device class can have one or more actual devices, this struct + * assigns a name to it. + */ +struct dvb_usb_device_description { + const char *name; + +#define DVB_USB_ID_MAX_NUM 15 + struct usb_device_id *cold_ids[DVB_USB_ID_MAX_NUM]; + struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM]; +}; + +static inline u8 rc5_custom(struct rc_map_table *key) +{ + return (key->scancode >> 8) & 0xff; +} + +static inline u8 rc5_data(struct rc_map_table *key) +{ + return key->scancode & 0xff; +} + +static inline u16 rc5_scan(struct rc_map_table *key) +{ + return key->scancode & 0xffff; +} + +struct dvb_usb_device; +struct dvb_usb_adapter; +struct usb_data_stream; + +/** + * Properties of USB streaming - TODO this structure should be somewhere else + * describes the kind of USB transfer used for data-streaming. + * (BULK or ISOC) + */ +struct usb_data_stream_properties { +#define USB_BULK 1 +#define USB_ISOC 2 + int type; + int count; + int endpoint; + + union { + struct { + int buffersize; /* per URB */ + } bulk; + struct { + int framesperurb; + int framesize; + int interval; + } isoc; + } u; +}; + +/** + * struct dvb_usb_adapter_properties - properties of a dvb-usb-adapter. + * A DVB-USB-Adapter is basically a dvb_adapter which is present on a USB-device. + * @caps: capabilities of the DVB USB device. + * @pid_filter_count: number of PID filter position in the optional hardware + * PID-filter. + * @num_frontends: number of frontends of the DVB USB adapter. + * @frontend_ctrl: called to power on/off active frontend. + * @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the + * device (not URB submitting/killing). + * @pid_filter_ctrl: called to en/disable the PID filter, if any. + * @pid_filter: called to set/unset a PID for filtering. + * @frontend_attach: called to attach the possible frontends (fill fe-field + * of struct dvb_usb_device). + * @tuner_attach: called to attach the correct tuner and to fill pll_addr, + * pll_desc and pll_init_buf of struct dvb_usb_device). + * @stream: configuration of the USB streaming + */ +struct dvb_usb_adapter_fe_properties { +#define DVB_USB_ADAP_HAS_PID_FILTER 0x01 +#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02 +#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04 +#define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08 +#define DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD 0x10 + int caps; + int pid_filter_count; + + int (*streaming_ctrl) (struct dvb_usb_adapter *, int); + int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int); + int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int); + + int (*frontend_attach) (struct dvb_usb_adapter *); + int (*tuner_attach) (struct dvb_usb_adapter *); + + struct usb_data_stream_properties stream; + + int size_of_priv; +}; + +#define MAX_NO_OF_FE_PER_ADAP 3 +struct dvb_usb_adapter_properties { + int size_of_priv; + + int (*frontend_ctrl) (struct dvb_frontend *, int); + + int num_frontends; + struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP]; +}; + +/** + * struct dvb_rc_legacy - old properties of remote controller + * @rc_map_table: a hard-wired array of struct rc_map_table (NULL to disable + * remote control handling). + * @rc_map_size: number of items in @rc_map_table. + * @rc_query: called to query an event event. + * @rc_interval: time in ms between two queries. + */ +struct dvb_rc_legacy { +/* remote control properties */ +#define REMOTE_NO_KEY_PRESSED 0x00 +#define REMOTE_KEY_PRESSED 0x01 +#define REMOTE_KEY_REPEAT 0x02 + struct rc_map_table *rc_map_table; + int rc_map_size; + int (*rc_query) (struct dvb_usb_device *, u32 *, int *); + int rc_interval; +}; + +/** + * struct dvb_rc properties of remote controller, using rc-core + * @rc_codes: name of rc codes table + * @protocol: type of protocol(s) currently used by the driver + * @allowed_protos: protocol(s) supported by the driver + * @driver_type: Used to point if a device supports raw mode + * @change_protocol: callback to change protocol + * @rc_query: called to query an event event. + * @rc_interval: time in ms between two queries. + * @bulk_mode: device supports bulk mode for RC (disable polling mode) + */ +struct dvb_rc { + char *rc_codes; + u64 protocol; + u64 allowed_protos; + enum rc_driver_type driver_type; + int (*change_protocol)(struct rc_dev *dev, u64 rc_type); + char *module_name; + int (*rc_query) (struct dvb_usb_device *d); + int rc_interval; + bool bulk_mode; /* uses bulk mode */ +}; + +/** + * enum dvb_usb_mode - Specifies if it is using a legacy driver or a new one + * based on rc-core + * This is initialized/used only inside dvb-usb-remote.c. + * It shouldn't be set by the drivers. + */ +enum dvb_usb_mode { + DVB_RC_LEGACY, + DVB_RC_CORE, +}; + +/** + * struct dvb_usb_device_properties - properties of a dvb-usb-device + * @usb_ctrl: which USB device-side controller is in use. Needed for firmware + * download. + * @firmware: name of the firmware file. + * @download_firmware: called to download the firmware when the usb_ctrl is + * DEVICE_SPECIFIC. + * @no_reconnect: device doesn't do a reconnect after downloading the firmware, + * so do the warm initialization right after it + * + * @size_of_priv: how many bytes shall be allocated for the private field + * of struct dvb_usb_device. + * + * @power_ctrl: called to enable/disable power of the device. + * @read_mac_address: called to read the MAC address of the device. + * @identify_state: called to determine the state (cold or warm), when it + * is not distinguishable by the USB IDs. + * + * @rc: remote controller properties + * + * @i2c_algo: i2c_algorithm if the device has I2CoverUSB. + * + * @generic_bulk_ctrl_endpoint: most of the DVB USB devices have a generic + * endpoint which received control messages with bulk transfers. When this + * is non-zero, one can use dvb_usb_generic_rw and dvb_usb_generic_write- + * helper functions. + * + * @generic_bulk_ctrl_endpoint_response: some DVB USB devices use a separate + * endpoint for responses to control messages sent with bulk transfers via + * the generic_bulk_ctrl_endpoint. When this is non-zero, this will be used + * instead of the generic_bulk_ctrl_endpoint when reading usb responses in + * the dvb_usb_generic_rw helper function. + * + * @num_device_descs: number of struct dvb_usb_device_description in @devices + * @devices: array of struct dvb_usb_device_description compatibles with these + * properties. + */ +#define MAX_NO_OF_ADAPTER_PER_DEVICE 2 +struct dvb_usb_device_properties { + +#define DVB_USB_IS_AN_I2C_ADAPTER 0x01 + int caps; + +#define DEVICE_SPECIFIC 0 +#define CYPRESS_AN2135 1 +#define CYPRESS_AN2235 2 +#define CYPRESS_FX2 3 + int usb_ctrl; + int (*download_firmware) (struct usb_device *, const struct firmware *); + const char *firmware; + int no_reconnect; + + int size_of_priv; + + int num_adapters; + struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; + + int (*power_ctrl) (struct dvb_usb_device *, int); + int (*read_mac_address) (struct dvb_usb_device *, u8 []); + int (*identify_state) (struct usb_device *, struct dvb_usb_device_properties *, + struct dvb_usb_device_description **, int *); + + struct { + enum dvb_usb_mode mode; /* Drivers shouldn't touch on it */ + struct dvb_rc_legacy legacy; + struct dvb_rc core; + } rc; + + struct i2c_algorithm *i2c_algo; + + int generic_bulk_ctrl_endpoint; + int generic_bulk_ctrl_endpoint_response; + + int num_device_descs; + struct dvb_usb_device_description devices[12]; +}; + +/** + * struct usb_data_stream - generic object of an USB stream + * @buf_num: number of buffer allocated. + * @buf_size: size of each buffer in buf_list. + * @buf_list: array containing all allocate buffers for streaming. + * @dma_addr: list of dma_addr_t for each buffer in buf_list. + * + * @urbs_initialized: number of URBs initialized. + * @urbs_submitted: number of URBs submitted. + */ +#define MAX_NO_URBS_FOR_DATA_STREAM 10 +struct usb_data_stream { + struct usb_device *udev; + struct usb_data_stream_properties props; + +#define USB_STATE_INIT 0x00 +#define USB_STATE_URB_BUF 0x01 + int state; + + void (*complete) (struct usb_data_stream *, u8 *, size_t); + + struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM]; + int buf_num; + unsigned long buf_size; + u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM]; + dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM]; + + int urbs_initialized; + int urbs_submitted; + + void *user_priv; +}; + +/** + * struct dvb_usb_adapter - a DVB adapter on a USB device + * @id: index of this adapter (starting with 0). + * + * @feedcount: number of reqested feeds (used for streaming-activation) + * @pid_filtering: is hardware pid_filtering used or not. + * + * @pll_addr: I2C address of the tuner for programming + * @pll_init: array containing the initialization buffer + * @pll_desc: pointer to the appropriate struct dvb_pll_desc + * @tuner_pass_ctrl: called to (de)activate tuner passthru of the demod or the board + * + * @dvb_adap: device's dvb_adapter. + * @dmxdev: device's dmxdev. + * @demux: device's software demuxer. + * @dvb_net: device's dvb_net interfaces. + * @dvb_frontend: device's frontend. + * @max_feed_count: how many feeds can be handled simultaneously by this + * device + * + * @fe_init: rerouted frontend-init (wakeup) function. + * @fe_sleep: rerouted frontend-sleep function. + * + * @stream: the usb data stream. + */ +struct dvb_usb_fe_adapter { + struct dvb_frontend *fe; + + int (*fe_init) (struct dvb_frontend *); + int (*fe_sleep) (struct dvb_frontend *); + + struct usb_data_stream stream; + + int pid_filtering; + int max_feed_count; + + void *priv; +}; + +struct dvb_usb_adapter { + struct dvb_usb_device *dev; + struct dvb_usb_adapter_properties props; + +#define DVB_USB_ADAP_STATE_INIT 0x000 +#define DVB_USB_ADAP_STATE_DVB 0x001 + int state; + + u8 id; + + int feedcount; + + /* dvb */ + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + + struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP]; + int active_fe; + int num_frontends_initialized; + + void *priv; +}; + +/** + * struct dvb_usb_device - object of a DVB USB device + * @props: copy of the struct dvb_usb_properties this device belongs to. + * @desc: pointer to the device's struct dvb_usb_device_description. + * @state: initialization and runtime state of the device. + * + * @powered: indicated whether the device is power or not. + * Powered is in/decremented for each call to modify the state. + * @udev: pointer to the device's struct usb_device. + * + * @usb_mutex: semaphore of USB control messages (reading needs two messages) + * @i2c_mutex: semaphore for i2c-transfers + * + * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB + * + * @rc_dev: rc device for the remote control (rc-core mode) + * @input_dev: input device for the remote control (legacy mode) + * @rc_query_work: struct work_struct frequent rc queries + * @last_event: last triggered event + * @last_state: last state (no, pressed, repeat) + * @owner: owner of the dvb_adapter + * @priv: private data of the actual driver (allocate by dvb-usb, size defined + * in size_of_priv of dvb_usb_properties). + */ +struct dvb_usb_device { + struct dvb_usb_device_properties props; + struct dvb_usb_device_description *desc; + + struct usb_device *udev; + +#define DVB_USB_STATE_INIT 0x000 +#define DVB_USB_STATE_I2C 0x001 +#define DVB_USB_STATE_DVB 0x002 +#define DVB_USB_STATE_REMOTE 0x004 + int state; + + int powered; + + /* locking */ + struct mutex usb_mutex; + + /* i2c */ + struct mutex i2c_mutex; + struct i2c_adapter i2c_adap; + + int num_adapters_initialized; + struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; + + /* remote control */ + struct rc_dev *rc_dev; + struct input_dev *input_dev; + char rc_phys[64]; + struct delayed_work rc_query_work; + u32 last_event; + int last_state; + + struct module *owner; + + void *priv; +}; + +extern int dvb_usb_device_init(struct usb_interface *, + struct dvb_usb_device_properties *, + struct module *, struct dvb_usb_device **, + short *adapter_nums); +extern void dvb_usb_device_exit(struct usb_interface *); + +/* the generic read/write method for device control */ +extern int dvb_usb_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16,int); +extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16); + +/* commonly used remote control parsing */ +extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *); + +/* commonly used firmware download types and function */ +struct hexline { + u8 len; + u32 addr; + u8 type; + u8 data[255]; + u8 chk; +}; +extern int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type); +extern int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx, int *pos); + + +#endif diff --git a/drivers/media/usb/dvb-usb/dvb_usb_dvb.c b/drivers/media/usb/dvb-usb/dvb_usb_dvb.c new file mode 100644 index 000000000000..384fe8eec21f --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb_usb_dvb.c @@ -0,0 +1,403 @@ +/* dvb-usb-dvb.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for initializing and handling the + * linux-dvb API. + */ +#include "dvb_usb_common.h" + +static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, + size_t len) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + dvb_dmx_swfilter(&adap->demux, buf, len); +} + +static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buf, + size_t len) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + dvb_dmx_swfilter_204(&adap->demux, buf, len); +} + +static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, u8 *buf, + size_t len) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + dvb_dmx_swfilter_raw(&adap->demux, buf, len); +} + +int dvb_usbv2_adapter_stream_init(struct dvb_usb_adapter *adap) +{ + pr_debug("%s: adap=%d\n", __func__, adap->id); + + adap->stream.udev = adap_to_d(adap)->udev; + adap->stream.user_priv = adap; + adap->stream.complete = dvb_usb_data_complete; + + return usb_urb_initv2(&adap->stream, &adap->props->stream); +} + +int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap) +{ + pr_debug("%s: adap=%d\n", __func__, adap->id); + usb_urb_exitv2(&adap->stream); + + return 0; +} + +/* does the complete input transfer handling */ +static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int count) +{ + struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; + struct dvb_usb_device *d = adap_to_d(adap); + int ret; + pr_debug("%s: adap=%d active_fe=%d feed_type=%d setting pid [%s]: " \ + "%04x (%04d) at index %d '%s'\n", __func__, adap->id, + adap->active_fe, dvbdmxfeed->type, + adap->pid_filtering ? "yes" : "no", dvbdmxfeed->pid, + dvbdmxfeed->pid, dvbdmxfeed->index, + (count == 1) ? "on" : "off"); + + if (adap->active_fe == -1) + return -EINVAL; + + adap->feed_count += count; + + /* stop feeding if it is last pid */ + if (adap->feed_count == 0) { + pr_debug("%s: stop feeding\n", __func__); + usb_urb_killv2(&adap->stream); + + if (d->props->streaming_ctrl) { + ret = d->props->streaming_ctrl(adap, 0); + if (ret < 0) { + pr_err("%s: streaming_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + goto err_mutex_unlock; + } + } + mutex_unlock(&adap->sync_mutex); + } + + /* activate the pid on the device pid filter */ + if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->pid_filtering && + adap->props->pid_filter) + ret = adap->props->pid_filter(adap, dvbdmxfeed->index, + dvbdmxfeed->pid, (count == 1) ? 1 : 0); + if (ret < 0) + pr_err("%s: pid_filter() failed=%d\n", + KBUILD_MODNAME, ret); + + /* start feeding if it is first pid */ + if (adap->feed_count == 1 && count == 1) { + struct usb_data_stream_properties stream_props; + mutex_lock(&adap->sync_mutex); + pr_debug("%s: start feeding\n", __func__); + + /* resolve input and output streaming paramters */ + if (d->props->get_stream_config) { + memcpy(&stream_props, &adap->props->stream, + sizeof(struct usb_data_stream_properties)); + ret = d->props->get_stream_config( + adap->fe[adap->active_fe], + &adap->ts_type, &stream_props); + if (ret < 0) + goto err_mutex_unlock; + } else { + stream_props = adap->props->stream; + } + + switch (adap->ts_type) { + case DVB_USB_FE_TS_TYPE_204: + adap->stream.complete = dvb_usb_data_complete_204; + break; + case DVB_USB_FE_TS_TYPE_RAW: + adap->stream.complete = dvb_usb_data_complete_raw; + break; + case DVB_USB_FE_TS_TYPE_188: + default: + adap->stream.complete = dvb_usb_data_complete; + break; + } + + usb_urb_submitv2(&adap->stream, &stream_props); + + if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && + adap->props->caps & + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF && + adap->props->pid_filter_ctrl) { + ret = adap->props->pid_filter_ctrl(adap, + adap->pid_filtering); + if (ret < 0) { + pr_err("%s: pid_filter_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + goto err_mutex_unlock; + } + } + + if (d->props->streaming_ctrl) { + ret = d->props->streaming_ctrl(adap, 1); + if (ret < 0) { + pr_err("%s: streaming_ctrl() failed=%d\n", + KBUILD_MODNAME, ret); + goto err_mutex_unlock; + } + } + } + + return 0; +err_mutex_unlock: + mutex_unlock(&adap->sync_mutex); + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + return dvb_usb_ctrl_feed(dvbdmxfeed, 1); +} + +static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + return dvb_usb_ctrl_feed(dvbdmxfeed, -1); +} + +int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_usb_device *d = adap_to_d(adap); + pr_debug("%s: adap=%d\n", __func__, adap->id); + + ret = dvb_register_adapter(&adap->dvb_adap, d->name, d->props->owner, + &d->udev->dev, d->props->adapter_nr); + if (ret < 0) { + pr_debug("%s: dvb_register_adapter() failed=%d\n", __func__, + ret); + goto err; + } + + adap->dvb_adap.priv = adap; + + if (d->props->read_mac_address) { + ret = d->props->read_mac_address(adap, + adap->dvb_adap.proposed_mac); + if (ret < 0) + goto err_dmx; + + pr_info("%s: MAC address: %pM\n", KBUILD_MODNAME, + adap->dvb_adap.proposed_mac); + } + + adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + adap->demux.priv = adap; + adap->demux.filternum = 0; + if (adap->demux.filternum < adap->max_feed_count) + adap->demux.filternum = adap->max_feed_count; + adap->demux.feednum = adap->demux.filternum; + adap->demux.start_feed = dvb_usb_start_feed; + adap->demux.stop_feed = dvb_usb_stop_feed; + adap->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + pr_err("%s: dvb_dmx_init() failed=%d\n", KBUILD_MODNAME, ret); + goto err_dmx; + } + + adap->dmxdev.filternum = adap->demux.filternum; + adap->dmxdev.demux = &adap->demux.dmx; + adap->dmxdev.capabilities = 0; + ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); + if (ret < 0) { + pr_err("%s: dvb_dmxdev_init() failed=%d\n", KBUILD_MODNAME, + ret); + goto err_dmx_dev; + } + + ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); + if (ret < 0) { + pr_err("%s: dvb_net_init() failed=%d\n", KBUILD_MODNAME, ret); + goto err_net_init; + } + + mutex_init(&adap->sync_mutex); + + return 0; +err_net_init: + dvb_dmxdev_release(&adap->dmxdev); +err_dmx_dev: + dvb_dmx_release(&adap->demux); +err_dmx: + dvb_unregister_adapter(&adap->dvb_adap); +err: + adap->dvb_adap.priv = NULL; + return ret; +} + +int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap) +{ + pr_debug("%s: adap=%d\n", __func__, adap->id); + + if (adap->dvb_adap.priv) { + dvb_net_release(&adap->dvb_net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->dvb_adap); + } + + return 0; +} + +static int dvb_usb_fe_wakeup(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap_to_d(adap); + mutex_lock(&adap->sync_mutex); + pr_debug("%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); + + ret = dvb_usbv2_device_power_ctrl(d, 1); + if (ret < 0) + goto err; + + if (d->props->frontend_ctrl) { + ret = d->props->frontend_ctrl(fe, 1); + if (ret < 0) + goto err; + } + + if (adap->fe_init[fe->id]) { + ret = adap->fe_init[fe->id](fe); + if (ret < 0) + goto err; + } + + adap->active_fe = fe->id; + mutex_unlock(&adap->sync_mutex); + + return 0; +err: + mutex_unlock(&adap->sync_mutex); + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int dvb_usb_fe_sleep(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *d = adap_to_d(adap); + mutex_lock(&adap->sync_mutex); + pr_debug("%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); + + if (adap->fe_sleep[fe->id]) { + ret = adap->fe_sleep[fe->id](fe); + if (ret < 0) + goto err; + } + + if (d->props->frontend_ctrl) { + ret = d->props->frontend_ctrl(fe, 0); + if (ret < 0) + goto err; + } + + ret = dvb_usbv2_device_power_ctrl(d, 0); + if (ret < 0) + goto err; + + adap->active_fe = -1; + mutex_unlock(&adap->sync_mutex); + + return 0; +err: + mutex_unlock(&adap->sync_mutex); + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) +{ + int ret, i, count_registered = 0; + struct dvb_usb_device *d = adap_to_d(adap); + pr_debug("%s: adap=%d\n", __func__, adap->id); + + memset(adap->fe, 0, sizeof(adap->fe)); + adap->active_fe = -1; + + if (d->props->frontend_attach) { + ret = d->props->frontend_attach(adap); + if (ret < 0) { + pr_debug("%s: frontend_attach() failed=%d\n", __func__, + ret); + goto err_dvb_frontend_detach; + } + } else { + pr_debug("%s: frontend_attach() do not exists\n", __func__); + ret = 0; + goto err; + } + + for (i = 0; i < MAX_NO_OF_FE_PER_ADAP && adap->fe[i]; i++) { + adap->fe[i]->id = i; + + /* re-assign sleep and wakeup functions */ + adap->fe_init[i] = adap->fe[i]->ops.init; + adap->fe[i]->ops.init = dvb_usb_fe_wakeup; + adap->fe_sleep[i] = adap->fe[i]->ops.sleep; + adap->fe[i]->ops.sleep = dvb_usb_fe_sleep; + + ret = dvb_register_frontend(&adap->dvb_adap, adap->fe[i]); + if (ret < 0) { + pr_err("%s: frontend%d registration failed\n", + KBUILD_MODNAME, i); + goto err_dvb_unregister_frontend; + } + + count_registered++; + } + + if (d->props->tuner_attach) { + ret = d->props->tuner_attach(adap); + if (ret < 0) { + pr_debug("%s: tuner_attach() failed=%d\n", __func__, + ret); + goto err_dvb_unregister_frontend; + } + } + + return 0; + +err_dvb_unregister_frontend: + for (i = count_registered - 1; i >= 0; i--) + dvb_unregister_frontend(adap->fe[i]); + +err_dvb_frontend_detach: + for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { + if (adap->fe[i]) + dvb_frontend_detach(adap->fe[i]); + } + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap) +{ + int i; + pr_debug("%s: adap=%d\n", __func__, adap->id); + + for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { + if (adap->fe[i]) { + dvb_unregister_frontend(adap->fe[i]); + dvb_frontend_detach(adap->fe[i]); + } + } + + return 0; +} diff --git a/drivers/media/usb/dvb-usb/dvb_usb_remote.c b/drivers/media/usb/dvb-usb/dvb_usb_remote.c new file mode 100644 index 000000000000..f856ab6648c7 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dvb_usb_remote.c @@ -0,0 +1,117 @@ +/* dvb-usb-remote.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for initializing the input-device and for + * handling remote-control-queries. + */ +#include "dvb_usb_common.h" +#include + +/* Remote-control poll function - called every dib->rc_query_interval ms to see + * whether the remote control has received anything. + * + * TODO: Fix the repeat rate of the input device. + */ +static void dvb_usb_read_remote_control(struct work_struct *work) +{ + struct dvb_usb_device *d = container_of(work, + struct dvb_usb_device, rc_query_work.work); + int ret; + + /* TODO: need a lock here. We can simply skip checking for the remote + control if we're busy. */ + + /* when the parameter has been set to 1 via sysfs while the + * driver was running, or when bulk mode is enabled after IR init + */ + if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) + return; + + ret = d->rc.query(d); + if (ret < 0) + pr_err("%s: error %d while querying for an remote control " \ + "event\n", KBUILD_MODNAME, ret); + + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(d->rc.interval)); +} + +int dvb_usbv2_remote_init(struct dvb_usb_device *d) +{ + int ret; + struct rc_dev *dev; + + if (dvb_usbv2_disable_rc_polling || !d->props->get_rc_config) + return 0; + + ret = d->props->get_rc_config(d, &d->rc); + if (ret < 0) + goto err; + + dev = rc_allocate_device(); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->dev.parent = &d->udev->dev; + dev->input_name = "IR-receiver inside an USB DVB receiver"; + usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); + strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); + dev->input_phys = d->rc_phys; + usb_to_input_id(d->udev, &dev->input_id); + /* TODO: likely RC-core should took const char * */ + dev->driver_name = (char *) d->props->driver_name; + dev->driver_type = d->rc.driver_type; + dev->allowed_protos = d->rc.allowed_protos; + dev->change_protocol = d->rc.change_protocol; + dev->priv = d; + /* select used keymap */ + if (d->rc.map_name) + dev->map_name = d->rc.map_name; + else if (d->rc_map) + dev->map_name = d->rc_map; + else + dev->map_name = RC_MAP_EMPTY; /* keep rc enabled */ + + ret = rc_register_device(dev); + if (ret < 0) { + rc_free_device(dev); + goto err; + } + + d->input_dev = NULL; + d->rc_dev = dev; + + /* start polling if needed */ + if (d->rc.query && !d->rc.bulk_mode) { + /* initialize a work queue for handling polling */ + INIT_DELAYED_WORK(&d->rc_query_work, + dvb_usb_read_remote_control); + pr_info("%s: schedule remote query interval to %d msecs\n", + KBUILD_MODNAME, d->rc.interval); + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(d->rc.interval)); + } + + d->state |= DVB_USB_STATE_REMOTE; + + return 0; +err: + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +int dvb_usbv2_remote_exit(struct dvb_usb_device *d) +{ + if (d->state & DVB_USB_STATE_REMOTE) { + cancel_delayed_work_sync(&d->rc_query_work); + rc_unregister_device(d->rc_dev); + } + + d->state &= ~DVB_USB_STATE_REMOTE; + + return 0; +} diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c new file mode 100644 index 000000000000..9382895b1b88 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -0,0 +1,1951 @@ +/* DVB USB framework compliant Linux driver for the + * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, + * TeVii S600, S630, S650, S660, S480, + * Prof 1100, 7500, + * Geniatech SU3000 Cards + * Copyright (C) 2008-2011 Igor M. Liplianin (liplianin@me.by) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dw2102.h" +#include "si21xx.h" +#include "stv0299.h" +#include "z0194a.h" +#include "stv0288.h" +#include "stb6000.h" +#include "eds1547.h" +#include "cx24116.h" +#include "tda1002x.h" +#include "mt312.h" +#include "zl10039.h" +#include "ds3000.h" +#include "stv0900.h" +#include "stv6110.h" +#include "stb6100.h" +#include "stb6100_proc.h" + +#ifndef USB_PID_DW2102 +#define USB_PID_DW2102 0x2102 +#endif + +#ifndef USB_PID_DW2104 +#define USB_PID_DW2104 0x2104 +#endif + +#ifndef USB_PID_DW3101 +#define USB_PID_DW3101 0x3101 +#endif + +#ifndef USB_PID_CINERGY_S +#define USB_PID_CINERGY_S 0x0064 +#endif + +#ifndef USB_PID_TEVII_S630 +#define USB_PID_TEVII_S630 0xd630 +#endif + +#ifndef USB_PID_TEVII_S650 +#define USB_PID_TEVII_S650 0xd650 +#endif + +#ifndef USB_PID_TEVII_S660 +#define USB_PID_TEVII_S660 0xd660 +#endif + +#ifndef USB_PID_TEVII_S480_1 +#define USB_PID_TEVII_S480_1 0xd481 +#endif + +#ifndef USB_PID_TEVII_S480_2 +#define USB_PID_TEVII_S480_2 0xd482 +#endif + +#ifndef USB_PID_PROF_1100 +#define USB_PID_PROF_1100 0xb012 +#endif + +#define DW210X_READ_MSG 0 +#define DW210X_WRITE_MSG 1 + +#define REG_1F_SYMBOLRATE_BYTE0 0x1f +#define REG_20_SYMBOLRATE_BYTE1 0x20 +#define REG_21_SYMBOLRATE_BYTE2 0x21 +/* on my own*/ +#define DW2102_VOLTAGE_CTRL (0x1800) +#define SU3000_STREAM_CTRL (0x1900) +#define DW2102_RC_QUERY (0x1a00) +#define DW2102_LED_CTRL (0x1b00) + +#define err_str "did not find the firmware file. (%s) " \ + "Please see linux/Documentation/dvb/ for more details " \ + "on firmware-problems." + +struct rc_map_dvb_usb_table_table { + struct rc_map_table *rc_keys; + int rc_keys_size; +}; + +struct su3000_state { + u8 initialized; +}; + +struct s6x0_state { + int (*old_set_voltage)(struct dvb_frontend *f, fe_sec_voltage_t v); +}; + +/* debug */ +static int dvb_usb_dw2102_debug; +module_param_named(debug, dvb_usb_dw2102_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer 4=rc(or-able))." + DVB_USB_DEBUG_STATUS); + +/* keymaps */ +static int ir_keymap; +module_param_named(keymap, ir_keymap, int, 0644); +MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..." + " 256=none"); + +/* demod probe */ +static int demod_probe = 1; +module_param_named(demod, demod_probe, int, 0644); +MODULE_PARM_DESC(demod, "demod to probe (1=cx24116 2=stv0903+stv6110 " + "4=stv0903+stb6100(or-able))."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value, + u16 index, u8 * data, u16 len, int flags) +{ + int ret; + u8 *u8buf; + unsigned int pipe = (flags == DW210X_READ_MSG) ? + usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); + u8 request_type = (flags == DW210X_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; + + u8buf = kmalloc(len, GFP_KERNEL); + if (!u8buf) + return -ENOMEM; + + + if (flags == DW210X_WRITE_MSG) + memcpy(u8buf, data, len); + ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR, + value, index , u8buf, len, 2000); + + if (flags == DW210X_READ_MSG) + memcpy(data, u8buf, len); + + kfree(u8buf); + return ret; +} + +/* I2C */ +static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0; + u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0}; + u16 value; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: + /* read stv0299 register */ + value = msg[0].buf[0];/* register */ + for (i = 0; i < msg[1].len; i++) { + dw210x_op_rw(d->udev, 0xb5, value + i, 0, + buf6, 2, DW210X_READ_MSG); + msg[1].buf[i] = buf6[0]; + } + break; + case 1: + switch (msg[0].addr) { + case 0x68: + /* write to stv0299 register */ + buf6[0] = 0x2a; + buf6[1] = msg[0].buf[0]; + buf6[2] = msg[0].buf[1]; + dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 3, DW210X_WRITE_MSG); + break; + case 0x60: + if (msg[0].flags == 0) { + /* write to tuner pll */ + buf6[0] = 0x2c; + buf6[1] = 5; + buf6[2] = 0xc0; + buf6[3] = msg[0].buf[0]; + buf6[4] = msg[0].buf[1]; + buf6[5] = msg[0].buf[2]; + buf6[6] = msg[0].buf[3]; + dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 7, DW210X_WRITE_MSG); + } else { + /* read from tuner */ + dw210x_op_rw(d->udev, 0xb5, 0, 0, + buf6, 1, DW210X_READ_MSG); + msg[0].buf[0] = buf6[0]; + } + break; + case (DW2102_RC_QUERY): + dw210x_op_rw(d->udev, 0xb8, 0, 0, + buf6, 2, DW210X_READ_MSG); + msg[0].buf[0] = buf6[0]; + msg[0].buf[1] = buf6[1]; + break; + case (DW2102_VOLTAGE_CTRL): + buf6[0] = 0x30; + buf6[1] = msg[0].buf[0]; + dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 2, DW210X_WRITE_MSG); + break; + } + + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + u8 buf6[] = {0, 0, 0, 0, 0, 0, 0}; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: + /* read si2109 register by number */ + buf6[0] = msg[0].addr << 1; + buf6[1] = msg[0].len; + buf6[2] = msg[0].buf[0]; + dw210x_op_rw(d->udev, 0xc2, 0, 0, + buf6, msg[0].len + 2, DW210X_WRITE_MSG); + /* read si2109 register */ + dw210x_op_rw(d->udev, 0xc3, 0xd0, 0, + buf6, msg[1].len + 2, DW210X_READ_MSG); + memcpy(msg[1].buf, buf6 + 2, msg[1].len); + + break; + case 1: + switch (msg[0].addr) { + case 0x68: + /* write to si2109 register */ + buf6[0] = msg[0].addr << 1; + buf6[1] = msg[0].len; + memcpy(buf6 + 2, msg[0].buf, msg[0].len); + dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, + msg[0].len + 2, DW210X_WRITE_MSG); + break; + case(DW2102_RC_QUERY): + dw210x_op_rw(d->udev, 0xb8, 0, 0, + buf6, 2, DW210X_READ_MSG); + msg[0].buf[0] = buf6[0]; + msg[0].buf[1] = buf6[1]; + break; + case(DW2102_VOLTAGE_CTRL): + buf6[0] = 0x30; + buf6[1] = msg[0].buf[0]; + dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 2, DW210X_WRITE_MSG); + break; + } + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: { + /* read */ + /* first write first register number */ + u8 ibuf[msg[1].len + 2], obuf[3]; + obuf[0] = msg[0].addr << 1; + obuf[1] = msg[0].len; + obuf[2] = msg[0].buf[0]; + dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); + /* second read registers */ + dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0, + ibuf, msg[1].len + 2, DW210X_READ_MSG); + memcpy(msg[1].buf, ibuf + 2, msg[1].len); + + break; + } + case 1: + switch (msg[0].addr) { + case 0x68: { + /* write to register */ + u8 obuf[msg[0].len + 2]; + obuf[0] = msg[0].addr << 1; + obuf[1] = msg[0].len; + memcpy(obuf + 2, msg[0].buf, msg[0].len); + dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); + break; + } + case 0x61: { + /* write to tuner */ + u8 obuf[msg[0].len + 2]; + obuf[0] = msg[0].addr << 1; + obuf[1] = msg[0].len; + memcpy(obuf + 2, msg[0].buf, msg[0].len); + dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); + break; + } + case(DW2102_RC_QUERY): { + u8 ibuf[2]; + dw210x_op_rw(d->udev, 0xb8, 0, 0, + ibuf, 2, DW210X_READ_MSG); + memcpy(msg[0].buf, ibuf , 2); + break; + } + case(DW2102_VOLTAGE_CTRL): { + u8 obuf[2]; + obuf[0] = 0x30; + obuf[1] = msg[0].buf[0]; + dw210x_op_rw(d->udev, 0xb2, 0, 0, + obuf, 2, DW210X_WRITE_MSG); + break; + } + } + + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int len, i, j; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (j = 0; j < num; j++) { + switch (msg[j].addr) { + case(DW2102_RC_QUERY): { + u8 ibuf[2]; + dw210x_op_rw(d->udev, 0xb8, 0, 0, + ibuf, 2, DW210X_READ_MSG); + memcpy(msg[j].buf, ibuf , 2); + break; + } + case(DW2102_VOLTAGE_CTRL): { + u8 obuf[2]; + obuf[0] = 0x30; + obuf[1] = msg[j].buf[0]; + dw210x_op_rw(d->udev, 0xb2, 0, 0, + obuf, 2, DW210X_WRITE_MSG); + break; + } + /*case 0x55: cx24116 + case 0x6a: stv0903 + case 0x68: ds3000, stv0903 + case 0x60: ts2020, stv6110, stb6100 */ + default: { + if (msg[j].flags == I2C_M_RD) { + /* read registers */ + u8 ibuf[msg[j].len + 2]; + dw210x_op_rw(d->udev, 0xc3, + (msg[j].addr << 1) + 1, 0, + ibuf, msg[j].len + 2, + DW210X_READ_MSG); + memcpy(msg[j].buf, ibuf + 2, msg[j].len); + mdelay(10); + } else if (((msg[j].buf[0] == 0xb0) && + (msg[j].addr == 0x68)) || + ((msg[j].buf[0] == 0xf7) && + (msg[j].addr == 0x55))) { + /* write firmware */ + u8 obuf[19]; + obuf[0] = msg[j].addr << 1; + obuf[1] = (msg[j].len > 15 ? 17 : msg[j].len); + obuf[2] = msg[j].buf[0]; + len = msg[j].len - 1; + i = 1; + do { + memcpy(obuf + 3, msg[j].buf + i, + (len > 16 ? 16 : len)); + dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, (len > 16 ? 16 : len) + 3, + DW210X_WRITE_MSG); + i += 16; + len -= 16; + } while (len > 0); + } else { + /* write registers */ + u8 obuf[msg[j].len + 2]; + obuf[0] = msg[j].addr << 1; + obuf[1] = msg[j].len; + memcpy(obuf + 2, msg[j].buf, msg[j].len); + dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[j].len + 2, + DW210X_WRITE_MSG); + } + break; + } + } + + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: { + /* read */ + /* first write first register number */ + u8 ibuf[msg[1].len + 2], obuf[3]; + obuf[0] = msg[0].addr << 1; + obuf[1] = msg[0].len; + obuf[2] = msg[0].buf[0]; + dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); + /* second read registers */ + dw210x_op_rw(d->udev, 0xc3, 0x19 , 0, + ibuf, msg[1].len + 2, DW210X_READ_MSG); + memcpy(msg[1].buf, ibuf + 2, msg[1].len); + + break; + } + case 1: + switch (msg[0].addr) { + case 0x60: + case 0x0c: { + /* write to register */ + u8 obuf[msg[0].len + 2]; + obuf[0] = msg[0].addr << 1; + obuf[1] = msg[0].len; + memcpy(obuf + 2, msg[0].buf, msg[0].len); + dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); + break; + } + case(DW2102_RC_QUERY): { + u8 ibuf[2]; + dw210x_op_rw(d->udev, 0xb8, 0, 0, + ibuf, 2, DW210X_READ_MSG); + memcpy(msg[0].buf, ibuf , 2); + break; + } + } + + break; + } + + for (i = 0; i < num; i++) { + deb_xfer("%02x:%02x: %s ", i, msg[i].addr, + msg[i].flags == 0 ? ">>>" : "<<<"); + debug_dump(msg[i].buf, msg[i].len, deb_xfer); + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct usb_device *udev; + int len, i, j; + + if (!d) + return -ENODEV; + udev = d->udev; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (j = 0; j < num; j++) { + switch (msg[j].addr) { + case (DW2102_RC_QUERY): { + u8 ibuf[5]; + dw210x_op_rw(d->udev, 0xb8, 0, 0, + ibuf, 5, DW210X_READ_MSG); + memcpy(msg[j].buf, ibuf + 3, 2); + break; + } + case (DW2102_VOLTAGE_CTRL): { + u8 obuf[2]; + + obuf[0] = 1; + obuf[1] = msg[j].buf[1];/* off-on */ + dw210x_op_rw(d->udev, 0x8a, 0, 0, + obuf, 2, DW210X_WRITE_MSG); + obuf[0] = 3; + obuf[1] = msg[j].buf[0];/* 13v-18v */ + dw210x_op_rw(d->udev, 0x8a, 0, 0, + obuf, 2, DW210X_WRITE_MSG); + break; + } + case (DW2102_LED_CTRL): { + u8 obuf[2]; + + obuf[0] = 5; + obuf[1] = msg[j].buf[0]; + dw210x_op_rw(d->udev, 0x8a, 0, 0, + obuf, 2, DW210X_WRITE_MSG); + break; + } + /*case 0x55: cx24116 + case 0x6a: stv0903 + case 0x68: ds3000, stv0903 + case 0x60: ts2020, stv6110, stb6100 + case 0xa0: eeprom */ + default: { + if (msg[j].flags == I2C_M_RD) { + /* read registers */ + u8 ibuf[msg[j].len]; + dw210x_op_rw(d->udev, 0x91, 0, 0, + ibuf, msg[j].len, + DW210X_READ_MSG); + memcpy(msg[j].buf, ibuf, msg[j].len); + break; + } else if ((msg[j].buf[0] == 0xb0) && + (msg[j].addr == 0x68)) { + /* write firmware */ + u8 obuf[19]; + obuf[0] = (msg[j].len > 16 ? + 18 : msg[j].len + 1); + obuf[1] = msg[j].addr << 1; + obuf[2] = msg[j].buf[0]; + len = msg[j].len - 1; + i = 1; + do { + memcpy(obuf + 3, msg[j].buf + i, + (len > 16 ? 16 : len)); + dw210x_op_rw(d->udev, 0x80, 0, 0, + obuf, (len > 16 ? 16 : len) + 3, + DW210X_WRITE_MSG); + i += 16; + len -= 16; + } while (len > 0); + } else if (j < (num - 1)) { + /* write register addr before read */ + u8 obuf[msg[j].len + 2]; + obuf[0] = msg[j + 1].len; + obuf[1] = (msg[j].addr << 1); + memcpy(obuf + 2, msg[j].buf, msg[j].len); + dw210x_op_rw(d->udev, + udev->descriptor.idProduct == + 0x7500 ? 0x92 : 0x90, 0, 0, + obuf, msg[j].len + 2, + DW210X_WRITE_MSG); + break; + } else { + /* write registers */ + u8 obuf[msg[j].len + 2]; + obuf[0] = msg[j].len + 1; + obuf[1] = (msg[j].addr << 1); + memcpy(obuf + 2, msg[j].buf, msg[j].len); + dw210x_op_rw(d->udev, 0x80, 0, 0, + obuf, msg[j].len + 2, + DW210X_WRITE_MSG); + break; + } + break; + } + } + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + u8 obuf[0x40], ibuf[0x40]; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 1: + switch (msg[0].addr) { + case SU3000_STREAM_CTRL: + obuf[0] = msg[0].buf[0] + 0x36; + obuf[1] = 3; + obuf[2] = 0; + if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 0, 0) < 0) + err("i2c transfer failed."); + break; + case DW2102_RC_QUERY: + obuf[0] = 0x10; + if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 2, 0) < 0) + err("i2c transfer failed."); + msg[0].buf[1] = ibuf[0]; + msg[0].buf[0] = ibuf[1]; + break; + default: + /* always i2c write*/ + obuf[0] = 0x08; + obuf[1] = msg[0].addr; + obuf[2] = msg[0].len; + + memcpy(&obuf[3], msg[0].buf, msg[0].len); + + if (dvb_usb_generic_rw(d, obuf, msg[0].len + 3, + ibuf, 1, 0) < 0) + err("i2c transfer failed."); + + } + break; + case 2: + /* always i2c read */ + obuf[0] = 0x09; + obuf[1] = msg[0].len; + obuf[2] = msg[1].len; + obuf[3] = msg[0].addr; + memcpy(&obuf[4], msg[0].buf, msg[0].len); + + if (dvb_usb_generic_rw(d, obuf, msg[0].len + 4, + ibuf, msg[1].len + 1, 0) < 0) + err("i2c transfer failed."); + + memcpy(msg[1].buf, &ibuf[1], msg[1].len); + break; + default: + warn("more than 2 i2c messages at a time is not handled yet."); + break; + } + mutex_unlock(&d->i2c_mutex); + return num; +} + +static u32 dw210x_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dw2102_i2c_algo = { + .master_xfer = dw2102_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + +static struct i2c_algorithm dw2102_serit_i2c_algo = { + .master_xfer = dw2102_serit_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + +static struct i2c_algorithm dw2102_earda_i2c_algo = { + .master_xfer = dw2102_earda_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + +static struct i2c_algorithm dw2104_i2c_algo = { + .master_xfer = dw2104_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + +static struct i2c_algorithm dw3101_i2c_algo = { + .master_xfer = dw3101_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + +static struct i2c_algorithm s6x0_i2c_algo = { + .master_xfer = s6x0_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + +static struct i2c_algorithm su3000_i2c_algo = { + .master_xfer = su3000_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + +static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int i; + u8 ibuf[] = {0, 0}; + u8 eeprom[256], eepromline[16]; + + for (i = 0; i < 256; i++) { + if (dw210x_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW210X_READ_MSG) < 0) { + err("read eeprom failed."); + return -1; + } else { + eepromline[i%16] = ibuf[0]; + eeprom[i] = ibuf[0]; + } + if ((i % 16) == 15) { + deb_xfer("%02x: ", i - 15); + debug_dump(eepromline, 16, deb_xfer); + } + } + + memcpy(mac, eeprom + 8, 6); + return 0; +}; + +static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int i, ret; + u8 ibuf[] = { 0 }, obuf[] = { 0 }; + u8 eeprom[256], eepromline[16]; + struct i2c_msg msg[] = { + { + .addr = 0xa0 >> 1, + .flags = 0, + .buf = obuf, + .len = 1, + }, { + .addr = 0xa0 >> 1, + .flags = I2C_M_RD, + .buf = ibuf, + .len = 1, + } + }; + + for (i = 0; i < 256; i++) { + obuf[0] = i; + ret = s6x0_i2c_transfer(&d->i2c_adap, msg, 2); + if (ret != 2) { + err("read eeprom failed."); + return -1; + } else { + eepromline[i % 16] = ibuf[0]; + eeprom[i] = ibuf[0]; + } + + if ((i % 16) == 15) { + deb_xfer("%02x: ", i - 15); + debug_dump(eepromline, 16, deb_xfer); + } + } + + memcpy(mac, eeprom + 16, 6); + return 0; +}; + +static int su3000_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + static u8 command_start[] = {0x00}; + static u8 command_stop[] = {0x01}; + struct i2c_msg msg = { + .addr = SU3000_STREAM_CTRL, + .flags = 0, + .buf = onoff ? command_start : command_stop, + .len = 1 + }; + + i2c_transfer(&adap->dev->i2c_adap, &msg, 1); + + return 0; +} + +static int su3000_power_ctrl(struct dvb_usb_device *d, int i) +{ + struct su3000_state *state = (struct su3000_state *)d->priv; + u8 obuf[] = {0xde, 0}; + + info("%s: %d, initialized %d\n", __func__, i, state->initialized); + + if (i && !state->initialized) { + state->initialized = 1; + /* reset board */ + dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0); + } + + return 0; +} + +static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int i; + u8 obuf[] = { 0x1f, 0xf0 }; + u8 ibuf[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = 0x51, + .flags = 0, + .buf = obuf, + .len = 2, + }, { + .addr = 0x51, + .flags = I2C_M_RD, + .buf = ibuf, + .len = 1, + + } + }; + + for (i = 0; i < 6; i++) { + obuf[1] = 0xf0 + i; + if (i2c_transfer(&d->i2c_adap, msg, 2) != 2) + break; + else + mac[i] = ibuf[0]; + + debug_dump(mac, 6, printk); + } + + return 0; +} + +static int su3000_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + info("%s\n", __func__); + + *cold = 0; + return 0; +} + +static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + static u8 command_13v[] = {0x00, 0x01}; + static u8 command_18v[] = {0x01, 0x01}; + static u8 command_off[] = {0x00, 0x00}; + struct i2c_msg msg = { + .addr = DW2102_VOLTAGE_CTRL, + .flags = 0, + .buf = command_off, + .len = 2, + }; + + struct dvb_usb_adapter *udev_adap = + (struct dvb_usb_adapter *)(fe->dvb->priv); + if (voltage == SEC_VOLTAGE_18) + msg.buf = command_18v; + else if (voltage == SEC_VOLTAGE_13) + msg.buf = command_13v; + + i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1); + + return 0; +} + +static int s660_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct dvb_usb_adapter *d = + (struct dvb_usb_adapter *)(fe->dvb->priv); + struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; + + dw210x_set_voltage(fe, voltage); + if (st->old_set_voltage) + st->old_set_voltage(fe, voltage); + + return 0; +} + +static void dw210x_led_ctrl(struct dvb_frontend *fe, int offon) +{ + static u8 led_off[] = { 0 }; + static u8 led_on[] = { 1 }; + struct i2c_msg msg = { + .addr = DW2102_LED_CTRL, + .flags = 0, + .buf = led_off, + .len = 1 + }; + struct dvb_usb_adapter *udev_adap = + (struct dvb_usb_adapter *)(fe->dvb->priv); + + if (offon) + msg.buf = led_on; + i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1); +} + +static struct stv0299_config sharp_z0194a_config = { + .demod_address = 0x68, + .inittab = sharp_z0194a_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = sharp_z0194a_set_symbol_rate, +}; + +static struct cx24116_config dw2104_config = { + .demod_address = 0x55, + .mpg_clk_pos_pol = 0x01, +}; + +static struct si21xx_config serit_sp1511lhb_config = { + .demod_address = 0x68, + .min_delay_ms = 100, + +}; + +static struct tda10023_config dw3101_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static struct mt312_config zl313_config = { + .demod_address = 0x0e, +}; + +static struct ds3000_config dw2104_ds3000_config = { + .demod_address = 0x68, +}; + +static struct stv0900_config dw2104a_stv0900_config = { + .demod_address = 0x6a, + .demod_mode = 0, + .xtal = 27000000, + .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ + .diseqc_mode = 2,/* 2/3 PWM */ + .tun1_maddress = 0,/* 0x60 */ + .tun1_adc = 0,/* 2 Vpp */ + .path1_mode = 3, +}; + +static struct stb6100_config dw2104a_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + +static struct stv0900_config dw2104_stv0900_config = { + .demod_address = 0x68, + .demod_mode = 0, + .xtal = 8000000, + .clkmode = 3, + .diseqc_mode = 2, + .tun1_maddress = 0, + .tun1_adc = 1,/* 1 Vpp */ + .path1_mode = 3, +}; + +static struct stv6110_config dw2104_stv6110_config = { + .i2c_address = 0x60, + .mclk = 16000000, + .clk_div = 1, +}; + +static struct stv0900_config prof_7500_stv0900_config = { + .demod_address = 0x6a, + .demod_mode = 0, + .xtal = 27000000, + .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ + .diseqc_mode = 2,/* 2/3 PWM */ + .tun1_maddress = 0,/* 0x60 */ + .tun1_adc = 0,/* 2 Vpp */ + .path1_mode = 3, + .tun1_type = 3, + .set_lock_led = dw210x_led_ctrl, +}; + +static struct ds3000_config su3000_ds3000_config = { + .demod_address = 0x68, + .ci_mode = 1, +}; + +static int dw2104_frontend_attach(struct dvb_usb_adapter *d) +{ + struct dvb_tuner_ops *tuner_ops = NULL; + + if (demod_probe & 4) { + d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config, + &d->dev->i2c_adap, 0); + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(stb6100_attach, d->fe_adap[0].fe, + &dw2104a_stb6100_config, + &d->dev->i2c_adap)) { + tuner_ops = &d->fe_adap[0].fe->ops.tuner_ops; + tuner_ops->set_frequency = stb6100_set_freq; + tuner_ops->get_frequency = stb6100_get_freq; + tuner_ops->set_bandwidth = stb6100_set_bandw; + tuner_ops->get_bandwidth = stb6100_get_bandw; + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached STV0900+STB6100!\n"); + return 0; + } + } + } + + if (demod_probe & 2) { + d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config, + &d->dev->i2c_adap, 0); + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(stv6110_attach, d->fe_adap[0].fe, + &dw2104_stv6110_config, + &d->dev->i2c_adap)) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached STV0900+STV6110A!\n"); + return 0; + } + } + } + + if (demod_probe & 1) { + d->fe_adap[0].fe = dvb_attach(cx24116_attach, &dw2104_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached cx24116!\n"); + return 0; + } + } + + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached DS3000!\n"); + return 0; + } + + return -EIO; +} + +static struct dvb_usb_device_properties dw2102_properties; +static struct dvb_usb_device_properties dw2104_properties; +static struct dvb_usb_device_properties s6x0_properties; + +static int dw2102_frontend_attach(struct dvb_usb_adapter *d) +{ + if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) { + /*dw2102_properties.adapter->tuner_attach = NULL;*/ + d->fe_adap[0].fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached si21xx!\n"); + return 0; + } + } + + if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) { + d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, + &d->dev->i2c_adap)) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached stv0288!\n"); + return 0; + } + } + } + + if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) { + /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/ + d->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe != NULL) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached stv0299!\n"); + return 0; + } + } + return -EIO; +} + +static int dw3101_frontend_attach(struct dvb_usb_adapter *d) +{ + d->fe_adap[0].fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config, + &d->dev->i2c_adap, 0x48); + if (d->fe_adap[0].fe != NULL) { + info("Attached tda10023!\n"); + return 0; + } + return -EIO; +} + +static int zl100313_frontend_attach(struct dvb_usb_adapter *d) +{ + d->fe_adap[0].fe = dvb_attach(mt312_attach, &zl313_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe != NULL) { + if (dvb_attach(zl10039_attach, d->fe_adap[0].fe, 0x60, + &d->dev->i2c_adap)) { + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + info("Attached zl100313+zl10039!\n"); + return 0; + } + } + + return -EIO; +} + +static int stv0288_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[] = {7, 1}; + + d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config, + &d->dev->i2c_adap); + + if (d->fe_adap[0].fe == NULL) + return -EIO; + + if (NULL == dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap)) + return -EIO; + + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + + dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); + + info("Attached stv0288+stb6000!\n"); + + return 0; + +} + +static int ds3000_frontend_attach(struct dvb_usb_adapter *d) +{ + struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; + u8 obuf[] = {7, 1}; + + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, + &d->dev->i2c_adap); + + if (d->fe_adap[0].fe == NULL) + return -EIO; + + st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage; + d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage; + + dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); + + info("Attached ds3000+ds2020!\n"); + + return 0; +} + +static int prof_7500_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[] = {7, 1}; + + d->fe_adap[0].fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config, + &d->dev->i2c_adap, 0); + if (d->fe_adap[0].fe == NULL) + return -EIO; + + d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; + + dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); + + info("Attached STV0900+STB6100A!\n"); + + return 0; +} + +static int su3000_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[3] = { 0xe, 0x80, 0 }; + u8 ibuf[] = { 0 }; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0xe; + obuf[1] = 0x83; + obuf[2] = 0; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0xe; + obuf[1] = 0x83; + obuf[2] = 1; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0x51; + + if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) + err("command 0x51 transfer failed."); + + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe == NULL) + return -EIO; + + info("Attached DS3000!\n"); + + return 0; +} + +static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, + &adap->dev->i2c_adap, DVB_PLL_OPERA1); + return 0; +} + +static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, + &adap->dev->i2c_adap, DVB_PLL_TUA6034); + + return 0; +} + +static struct rc_map_table rc_map_dw210x_table[] = { + { 0xf80a, KEY_POWER2 }, /*power*/ + { 0xf80c, KEY_MUTE }, /*mute*/ + { 0xf811, KEY_1 }, + { 0xf812, KEY_2 }, + { 0xf813, KEY_3 }, + { 0xf814, KEY_4 }, + { 0xf815, KEY_5 }, + { 0xf816, KEY_6 }, + { 0xf817, KEY_7 }, + { 0xf818, KEY_8 }, + { 0xf819, KEY_9 }, + { 0xf810, KEY_0 }, + { 0xf81c, KEY_CHANNELUP }, /*ch+*/ + { 0xf80f, KEY_CHANNELDOWN }, /*ch-*/ + { 0xf81a, KEY_VOLUMEUP }, /*vol+*/ + { 0xf80e, KEY_VOLUMEDOWN }, /*vol-*/ + { 0xf804, KEY_RECORD }, /*rec*/ + { 0xf809, KEY_FAVORITES }, /*fav*/ + { 0xf808, KEY_REWIND }, /*rewind*/ + { 0xf807, KEY_FASTFORWARD }, /*fast*/ + { 0xf80b, KEY_PAUSE }, /*pause*/ + { 0xf802, KEY_ESC }, /*cancel*/ + { 0xf803, KEY_TAB }, /*tab*/ + { 0xf800, KEY_UP }, /*up*/ + { 0xf81f, KEY_OK }, /*ok*/ + { 0xf801, KEY_DOWN }, /*down*/ + { 0xf805, KEY_CAMERA }, /*cap*/ + { 0xf806, KEY_STOP }, /*stop*/ + { 0xf840, KEY_ZOOM }, /*full*/ + { 0xf81e, KEY_TV }, /*tvmode*/ + { 0xf81b, KEY_LAST }, /*recall*/ +}; + +static struct rc_map_table rc_map_tevii_table[] = { + { 0xf80a, KEY_POWER }, + { 0xf80c, KEY_MUTE }, + { 0xf811, KEY_1 }, + { 0xf812, KEY_2 }, + { 0xf813, KEY_3 }, + { 0xf814, KEY_4 }, + { 0xf815, KEY_5 }, + { 0xf816, KEY_6 }, + { 0xf817, KEY_7 }, + { 0xf818, KEY_8 }, + { 0xf819, KEY_9 }, + { 0xf810, KEY_0 }, + { 0xf81c, KEY_MENU }, + { 0xf80f, KEY_VOLUMEDOWN }, + { 0xf81a, KEY_LAST }, + { 0xf80e, KEY_OPEN }, + { 0xf804, KEY_RECORD }, + { 0xf809, KEY_VOLUMEUP }, + { 0xf808, KEY_CHANNELUP }, + { 0xf807, KEY_PVR }, + { 0xf80b, KEY_TIME }, + { 0xf802, KEY_RIGHT }, + { 0xf803, KEY_LEFT }, + { 0xf800, KEY_UP }, + { 0xf81f, KEY_OK }, + { 0xf801, KEY_DOWN }, + { 0xf805, KEY_TUNER }, + { 0xf806, KEY_CHANNELDOWN }, + { 0xf840, KEY_PLAYPAUSE }, + { 0xf81e, KEY_REWIND }, + { 0xf81b, KEY_FAVORITES }, + { 0xf81d, KEY_BACK }, + { 0xf84d, KEY_FASTFORWARD }, + { 0xf844, KEY_EPG }, + { 0xf84c, KEY_INFO }, + { 0xf841, KEY_AB }, + { 0xf843, KEY_AUDIO }, + { 0xf845, KEY_SUBTITLE }, + { 0xf84a, KEY_LIST }, + { 0xf846, KEY_F1 }, + { 0xf847, KEY_F2 }, + { 0xf85e, KEY_F3 }, + { 0xf85c, KEY_F4 }, + { 0xf852, KEY_F5 }, + { 0xf85a, KEY_F6 }, + { 0xf856, KEY_MODE }, + { 0xf858, KEY_SWITCHVIDEOMODE }, +}; + +static struct rc_map_table rc_map_tbs_table[] = { + { 0xf884, KEY_POWER }, + { 0xf894, KEY_MUTE }, + { 0xf887, KEY_1 }, + { 0xf886, KEY_2 }, + { 0xf885, KEY_3 }, + { 0xf88b, KEY_4 }, + { 0xf88a, KEY_5 }, + { 0xf889, KEY_6 }, + { 0xf88f, KEY_7 }, + { 0xf88e, KEY_8 }, + { 0xf88d, KEY_9 }, + { 0xf892, KEY_0 }, + { 0xf896, KEY_CHANNELUP }, + { 0xf891, KEY_CHANNELDOWN }, + { 0xf893, KEY_VOLUMEUP }, + { 0xf88c, KEY_VOLUMEDOWN }, + { 0xf883, KEY_RECORD }, + { 0xf898, KEY_PAUSE }, + { 0xf899, KEY_OK }, + { 0xf89a, KEY_SHUFFLE }, + { 0xf881, KEY_UP }, + { 0xf890, KEY_LEFT }, + { 0xf882, KEY_RIGHT }, + { 0xf888, KEY_DOWN }, + { 0xf895, KEY_FAVORITES }, + { 0xf897, KEY_SUBTITLE }, + { 0xf89d, KEY_ZOOM }, + { 0xf89f, KEY_EXIT }, + { 0xf89e, KEY_MENU }, + { 0xf89c, KEY_EPG }, + { 0xf880, KEY_PREVIOUS }, + { 0xf89b, KEY_MODE } +}; + +static struct rc_map_table rc_map_su3000_table[] = { + { 0x25, KEY_POWER }, /* right-bottom Red */ + { 0x0a, KEY_MUTE }, /* -/-- */ + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x00, KEY_0 }, + { 0x20, KEY_UP }, /* CH+ */ + { 0x21, KEY_DOWN }, /* CH+ */ + { 0x12, KEY_VOLUMEUP }, /* Brightness Up */ + { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */ + { 0x1f, KEY_RECORD }, + { 0x17, KEY_PLAY }, + { 0x16, KEY_PAUSE }, + { 0x0b, KEY_STOP }, + { 0x27, KEY_FASTFORWARD },/* >> */ + { 0x26, KEY_REWIND }, /* << */ + { 0x0d, KEY_OK }, /* Mute */ + { 0x11, KEY_LEFT }, /* VOL- */ + { 0x10, KEY_RIGHT }, /* VOL+ */ + { 0x29, KEY_BACK }, /* button under 9 */ + { 0x2c, KEY_MENU }, /* TTX */ + { 0x2b, KEY_EPG }, /* EPG */ + { 0x1e, KEY_RED }, /* OSD */ + { 0x0e, KEY_GREEN }, /* Window */ + { 0x2d, KEY_YELLOW }, /* button under << */ + { 0x0f, KEY_BLUE }, /* bottom yellow button */ + { 0x14, KEY_AUDIO }, /* Snapshot */ + { 0x38, KEY_TV }, /* TV/Radio */ + { 0x0c, KEY_ESC } /* upper Red button */ +}; + +static struct rc_map_dvb_usb_table_table keys_tables[] = { + { rc_map_dw210x_table, ARRAY_SIZE(rc_map_dw210x_table) }, + { rc_map_tevii_table, ARRAY_SIZE(rc_map_tevii_table) }, + { rc_map_tbs_table, ARRAY_SIZE(rc_map_tbs_table) }, + { rc_map_su3000_table, ARRAY_SIZE(rc_map_su3000_table) }, +}; + +static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; + int keymap_size = d->props.rc.legacy.rc_map_size; + u8 key[2]; + struct i2c_msg msg = { + .addr = DW2102_RC_QUERY, + .flags = I2C_M_RD, + .buf = key, + .len = 2 + }; + int i; + /* override keymap */ + if ((ir_keymap > 0) && (ir_keymap <= ARRAY_SIZE(keys_tables))) { + keymap = keys_tables[ir_keymap - 1].rc_keys ; + keymap_size = keys_tables[ir_keymap - 1].rc_keys_size; + } else if (ir_keymap > ARRAY_SIZE(keys_tables)) + return 0; /* none */ + + *state = REMOTE_NO_KEY_PRESSED; + if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { + for (i = 0; i < keymap_size ; i++) { + if (rc5_data(&keymap[i]) == msg.buf[0]) { + *state = REMOTE_KEY_PRESSED; + *event = keymap[i].keycode; + break; + } + + } + + if ((*state) == REMOTE_KEY_PRESSED) + deb_rc("%s: found rc key: %x, %x, event: %x\n", + __func__, key[0], key[1], (*event)); + else if (key[0] != 0xff) + deb_rc("%s: unknown rc key: %x, %x\n", + __func__, key[0], key[1]); + + } + + return 0; +} + +enum dw2102_table_entry { + CYPRESS_DW2102, + CYPRESS_DW2101, + CYPRESS_DW2104, + TEVII_S650, + TERRATEC_CINERGY_S, + CYPRESS_DW3101, + TEVII_S630, + PROF_1100, + TEVII_S660, + PROF_7500, + GENIATECH_SU3000, + TERRATEC_CINERGY_S2, + TEVII_S480_1, + TEVII_S480_2, + X3M_SPC1400HD, +}; + +static struct usb_device_id dw2102_table[] = { + [CYPRESS_DW2102] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)}, + [CYPRESS_DW2101] = {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, + [CYPRESS_DW2104] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2104)}, + [TEVII_S650] = {USB_DEVICE(0x9022, USB_PID_TEVII_S650)}, + [TERRATEC_CINERGY_S] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, + [CYPRESS_DW3101] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)}, + [TEVII_S630] = {USB_DEVICE(0x9022, USB_PID_TEVII_S630)}, + [PROF_1100] = {USB_DEVICE(0x3011, USB_PID_PROF_1100)}, + [TEVII_S660] = {USB_DEVICE(0x9022, USB_PID_TEVII_S660)}, + [PROF_7500] = {USB_DEVICE(0x3034, 0x7500)}, + [GENIATECH_SU3000] = {USB_DEVICE(0x1f4d, 0x3000)}, + [TERRATEC_CINERGY_S2] = {USB_DEVICE(USB_VID_TERRATEC, 0x00a8)}, + [TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, + [TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, + [X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)}, + { } +}; + +MODULE_DEVICE_TABLE(usb, dw2102_table); + +static int dw2102_load_firmware(struct usb_device *dev, + const struct firmware *frmwr) +{ + u8 *b, *p; + int ret = 0, i; + u8 reset; + u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; + const struct firmware *fw; + const char *fw_2101 = "dvb-usb-dw2101.fw"; + + switch (dev->descriptor.idProduct) { + case 0x2101: + ret = request_firmware(&fw, fw_2101, &dev->dev); + if (ret != 0) { + err(err_str, fw_2101); + return ret; + } + break; + default: + fw = frmwr; + break; + } + info("start downloading DW210X firmware"); + p = kmalloc(fw->size, GFP_KERNEL); + reset = 1; + /*stop the CPU*/ + dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW210X_WRITE_MSG); + dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW210X_WRITE_MSG); + + if (p != NULL) { + memcpy(p, fw->data, fw->size); + for (i = 0; i < fw->size; i += 0x40) { + b = (u8 *) p + i; + if (dw210x_op_rw(dev, 0xa0, i, 0, b , 0x40, + DW210X_WRITE_MSG) != 0x40) { + err("error while transferring firmware"); + ret = -EINVAL; + break; + } + } + /* restart the CPU */ + reset = 0; + if (ret || dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, + DW210X_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + if (ret || dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, + DW210X_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + /* init registers */ + switch (dev->descriptor.idProduct) { + case USB_PID_TEVII_S650: + dw2104_properties.rc.legacy.rc_map_table = rc_map_tevii_table; + dw2104_properties.rc.legacy.rc_map_size = + ARRAY_SIZE(rc_map_tevii_table); + case USB_PID_DW2104: + reset = 1; + dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, + DW210X_WRITE_MSG); + /* break omitted intentionally */ + case USB_PID_DW3101: + reset = 0; + dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, + DW210X_WRITE_MSG); + break; + case USB_PID_CINERGY_S: + case USB_PID_DW2102: + dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, + DW210X_WRITE_MSG); + dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, + DW210X_READ_MSG); + /* check STV0299 frontend */ + dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2, + DW210X_READ_MSG); + if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) { + dw2102_properties.i2c_algo = &dw2102_i2c_algo; + dw2102_properties.adapter->fe[0].tuner_attach = &dw2102_tuner_attach; + break; + } else { + /* check STV0288 frontend */ + reset16[0] = 0xd0; + reset16[1] = 1; + reset16[2] = 0; + dw210x_op_rw(dev, 0xc2, 0, 0, &reset16[0], 3, + DW210X_WRITE_MSG); + dw210x_op_rw(dev, 0xc3, 0xd1, 0, &reset16[0], 3, + DW210X_READ_MSG); + if (reset16[2] == 0x11) { + dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo; + break; + } + } + case 0x2101: + dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2, + DW210X_READ_MSG); + dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, + DW210X_READ_MSG); + dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, + DW210X_READ_MSG); + dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, + DW210X_READ_MSG); + break; + } + + msleep(100); + kfree(p); + } + return ret; +} + +static struct dvb_usb_device_properties dw2102_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-dw2102.fw", + .no_reconnect = 1, + + .i2c_algo = &dw2102_serit_i2c_algo, + + .rc.legacy = { + .rc_map_table = rc_map_dw210x_table, + .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x81, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .read_mac_address = dw210x_read_mac_address, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = dw2102_frontend_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .num_device_descs = 3, + .devices = { + {"DVBWorld DVB-S 2102 USB2.0", + {&dw2102_table[CYPRESS_DW2102], NULL}, + {NULL}, + }, + {"DVBWorld DVB-S 2101 USB2.0", + {&dw2102_table[CYPRESS_DW2101], NULL}, + {NULL}, + }, + {"TerraTec Cinergy S USB", + {&dw2102_table[TERRATEC_CINERGY_S], NULL}, + {NULL}, + }, + } +}; + +static struct dvb_usb_device_properties dw2104_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-dw2104.fw", + .no_reconnect = 1, + + .i2c_algo = &dw2104_i2c_algo, + .rc.legacy = { + .rc_map_table = rc_map_dw210x_table, + .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x81, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .read_mac_address = dw210x_read_mac_address, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = dw2104_frontend_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .num_device_descs = 2, + .devices = { + { "DVBWorld DW2104 USB2.0", + {&dw2102_table[CYPRESS_DW2104], NULL}, + {NULL}, + }, + { "TeVii S650 USB2.0", + {&dw2102_table[TEVII_S650], NULL}, + {NULL}, + }, + } +}; + +static struct dvb_usb_device_properties dw3101_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-dw3101.fw", + .no_reconnect = 1, + + .i2c_algo = &dw3101_i2c_algo, + .rc.legacy = { + .rc_map_table = rc_map_dw210x_table, + .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x81, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .read_mac_address = dw210x_read_mac_address, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = dw3101_frontend_attach, + .tuner_attach = dw3101_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .num_device_descs = 1, + .devices = { + { "DVBWorld DVB-C 3101 USB2.0", + {&dw2102_table[CYPRESS_DW3101], NULL}, + {NULL}, + }, + } +}; + +static struct dvb_usb_device_properties s6x0_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .size_of_priv = sizeof(struct s6x0_state), + .firmware = "dvb-usb-s630.fw", + .no_reconnect = 1, + + .i2c_algo = &s6x0_i2c_algo, + .rc.legacy = { + .rc_map_table = rc_map_tevii_table, + .rc_map_size = ARRAY_SIZE(rc_map_tevii_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 0x81, + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .read_mac_address = s6x0_read_mac_address, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = zl100313_frontend_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .num_device_descs = 1, + .devices = { + {"TeVii S630 USB", + {&dw2102_table[TEVII_S630], NULL}, + {NULL}, + }, + } +}; + +struct dvb_usb_device_properties *p1100; +static struct dvb_usb_device_description d1100 = { + "Prof 1100 USB ", + {&dw2102_table[PROF_1100], NULL}, + {NULL}, +}; + +struct dvb_usb_device_properties *s660; +static struct dvb_usb_device_description d660 = { + "TeVii S660 USB", + {&dw2102_table[TEVII_S660], NULL}, + {NULL}, +}; + +static struct dvb_usb_device_description d480_1 = { + "TeVii S480.1 USB", + {&dw2102_table[TEVII_S480_1], NULL}, + {NULL}, +}; + +static struct dvb_usb_device_description d480_2 = { + "TeVii S480.2 USB", + {&dw2102_table[TEVII_S480_2], NULL}, + {NULL}, +}; + +struct dvb_usb_device_properties *p7500; +static struct dvb_usb_device_description d7500 = { + "Prof 7500 USB DVB-S2", + {&dw2102_table[PROF_7500], NULL}, + {NULL}, +}; + +static struct dvb_usb_device_properties su3000_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .size_of_priv = sizeof(struct su3000_state), + .power_ctrl = su3000_power_ctrl, + .num_adapters = 1, + .identify_state = su3000_identify_state, + .i2c_algo = &su3000_i2c_algo, + + .rc.legacy = { + .rc_map_table = rc_map_su3000_table, + .rc_map_size = ARRAY_SIZE(rc_map_su3000_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, + + .read_mac_address = su3000_read_mac_address, + + .generic_bulk_ctrl_endpoint = 0x01, + + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = su3000_streaming_ctrl, + .frontend_attach = su3000_frontend_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + } + }}, + } + }, + .num_device_descs = 3, + .devices = { + { "SU3000HD DVB-S USB2.0", + { &dw2102_table[GENIATECH_SU3000], NULL }, + { NULL }, + }, + { "Terratec Cinergy S2 USB HD", + { &dw2102_table[TERRATEC_CINERGY_S2], NULL }, + { NULL }, + }, + { "X3M TV SPC1400HD PCI", + { &dw2102_table[X3M_SPC1400HD], NULL }, + { NULL }, + }, + } +}; + +static int dw2102_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + p1100 = kmemdup(&s6x0_properties, + sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!p1100) + return -ENOMEM; + /* copy default structure */ + /* fill only different fields */ + p1100->firmware = "dvb-usb-p1100.fw"; + p1100->devices[0] = d1100; + p1100->rc.legacy.rc_map_table = rc_map_tbs_table; + p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); + p1100->adapter->fe[0].frontend_attach = stv0288_frontend_attach; + + s660 = kmemdup(&s6x0_properties, + sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!s660) { + kfree(p1100); + return -ENOMEM; + } + s660->firmware = "dvb-usb-s660.fw"; + s660->num_device_descs = 3; + s660->devices[0] = d660; + s660->devices[1] = d480_1; + s660->devices[2] = d480_2; + s660->adapter->fe[0].frontend_attach = ds3000_frontend_attach; + + p7500 = kmemdup(&s6x0_properties, + sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!p7500) { + kfree(p1100); + kfree(s660); + return -ENOMEM; + } + p7500->firmware = "dvb-usb-p7500.fw"; + p7500->devices[0] = d7500; + p7500->rc.legacy.rc_map_table = rc_map_tbs_table; + p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); + p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach; + + if (0 == dvb_usb_device_init(intf, &dw2102_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &dw2104_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &dw3101_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &s6x0_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, p1100, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, s660, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, p7500, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &su3000_properties, + THIS_MODULE, NULL, adapter_nr)) + return 0; + + return -ENODEV; +} + +static struct usb_driver dw2102_driver = { + .name = "dw2102", + .probe = dw2102_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dw2102_table, +}; + +module_usb_driver(dw2102_driver); + +MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); +MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," + " DVB-C 3101 USB2.0," + " TeVii S600, S630, S650, S660, S480," + " Prof 1100, 7500 USB2.0," + " Geniatech SU3000 devices"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/dw2102.h b/drivers/media/usb/dvb-usb/dw2102.h new file mode 100644 index 000000000000..5cd0b0eb6ce1 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dw2102.h @@ -0,0 +1,9 @@ +#ifndef _DW2102_H_ +#define _DW2102_H_ + +#define DVB_USB_LOG_PREFIX "dw2102" +#include "dvb-usb.h" + +#define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_dw2102_debug, 0x04, args) +#endif diff --git a/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c new file mode 100644 index 000000000000..90a70c66a96e --- /dev/null +++ b/drivers/media/usb/dvb-usb/friio-fe.c @@ -0,0 +1,473 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada + * + * This module is based off the the gl861 and vp702x modules. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include +#include +#include + +#include "friio.h" + +struct jdvbt90502_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct jdvbt90502_config config; +}; + +/* NOTE: TC90502 has 16bit register-address? */ +/* register 0x0100 is used for reading PLL status, so reg is u16 here */ +static int jdvbt90502_reg_read(struct jdvbt90502_state *state, + const u16 reg, u8 *buf, const size_t count) +{ + int ret; + u8 wbuf[3]; + struct i2c_msg msg[2]; + + wbuf[0] = reg & 0xFF; + wbuf[1] = 0; + wbuf[2] = reg >> 8; + + msg[0].addr = state->config.demod_address; + msg[0].flags = 0; + msg[0].buf = wbuf; + msg[0].len = sizeof(wbuf); + + msg[1].addr = msg[0].addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + deb_fe(" reg read failed.\n"); + return -EREMOTEIO; + } + return 0; +} + +/* currently 16bit register-address is not used, so reg is u8 here */ +static int jdvbt90502_single_reg_write(struct jdvbt90502_state *state, + const u8 reg, const u8 val) +{ + struct i2c_msg msg; + u8 wbuf[2]; + + wbuf[0] = reg; + wbuf[1] = val; + + msg.addr = state->config.demod_address; + msg.flags = 0; + msg.buf = wbuf; + msg.len = sizeof(wbuf); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + deb_fe(" reg write failed."); + return -EREMOTEIO; + } + return 0; +} + +static int _jdvbt90502_write(struct dvb_frontend *fe, const u8 buf[], int len) +{ + struct jdvbt90502_state *state = fe->demodulator_priv; + int err, i; + for (i = 0; i < len - 1; i++) { + err = jdvbt90502_single_reg_write(state, + buf[0] + i, buf[i + 1]); + if (err) + return err; + } + + return 0; +} + +/* read pll status byte via the demodulator's I2C register */ +/* note: Win box reads it by 8B block at the I2C addr 0x30 from reg:0x80 */ +static int jdvbt90502_pll_read(struct jdvbt90502_state *state, u8 *result) +{ + int ret; + + /* +1 for reading */ + u8 pll_addr_byte = (state->config.pll_address << 1) + 1; + + *result = 0; + + ret = jdvbt90502_single_reg_write(state, JDVBT90502_2ND_I2C_REG, + pll_addr_byte); + if (ret) + goto error; + + ret = jdvbt90502_reg_read(state, 0x0100, result, 1); + if (ret) + goto error; + + deb_fe("PLL read val:%02x\n", *result); + return 0; + +error: + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; +} + + +/* set pll frequency via the demodulator's I2C register */ +static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq) +{ + int ret; + int retry; + u8 res1; + u8 res2[9]; + + u8 pll_freq_cmd[PLL_CMD_LEN]; + u8 pll_agc_cmd[PLL_CMD_LEN]; + struct i2c_msg msg[2]; + u32 f; + + deb_fe("%s: freq=%d, step=%d\n", __func__, freq, + state->frontend.ops.info.frequency_stepsize); + /* freq -> oscilator frequency conversion. */ + /* freq: 473,000,000 + n*6,000,000 [+ 142857 (center freq. shift)] */ + f = freq / state->frontend.ops.info.frequency_stepsize; + /* add 399[1/7 MHZ] = 57MHz for the IF */ + f += 399; + /* add center frequency shift if necessary */ + if (f % 7 == 0) + f++; + pll_freq_cmd[DEMOD_REDIRECT_REG] = JDVBT90502_2ND_I2C_REG; /* 0xFE */ + pll_freq_cmd[ADDRESS_BYTE] = state->config.pll_address << 1; + pll_freq_cmd[DIVIDER_BYTE1] = (f >> 8) & 0x7F; + pll_freq_cmd[DIVIDER_BYTE2] = f & 0xFF; + pll_freq_cmd[CONTROL_BYTE] = 0xB2; /* ref.divider:28, 4MHz/28=1/7MHz */ + pll_freq_cmd[BANDSWITCH_BYTE] = 0x08; /* UHF band */ + + msg[0].addr = state->config.demod_address; + msg[0].flags = 0; + msg[0].buf = pll_freq_cmd; + msg[0].len = sizeof(pll_freq_cmd); + + ret = i2c_transfer(state->i2c, &msg[0], 1); + if (ret != 1) + goto error; + + udelay(50); + + pll_agc_cmd[DEMOD_REDIRECT_REG] = pll_freq_cmd[DEMOD_REDIRECT_REG]; + pll_agc_cmd[ADDRESS_BYTE] = pll_freq_cmd[ADDRESS_BYTE]; + pll_agc_cmd[DIVIDER_BYTE1] = pll_freq_cmd[DIVIDER_BYTE1]; + pll_agc_cmd[DIVIDER_BYTE2] = pll_freq_cmd[DIVIDER_BYTE2]; + pll_agc_cmd[CONTROL_BYTE] = 0x9A; /* AGC_CTRL instead of BANDSWITCH */ + pll_agc_cmd[AGC_CTRL_BYTE] = 0x50; + /* AGC Time Constant 2s, AGC take-over point:103dBuV(lowest) */ + + msg[1].addr = msg[0].addr; + msg[1].flags = 0; + msg[1].buf = pll_agc_cmd; + msg[1].len = sizeof(pll_agc_cmd); + + ret = i2c_transfer(state->i2c, &msg[1], 1); + if (ret != 1) + goto error; + + /* I don't know what these cmds are for, */ + /* but the USB log on a windows box contains them */ + ret = jdvbt90502_single_reg_write(state, 0x01, 0x40); + ret |= jdvbt90502_single_reg_write(state, 0x01, 0x00); + if (ret) + goto error; + udelay(100); + + /* wait for the demod to be ready? */ +#define RETRY_COUNT 5 + for (retry = 0; retry < RETRY_COUNT; retry++) { + ret = jdvbt90502_reg_read(state, 0x0096, &res1, 1); + if (ret) + goto error; + /* if (res1 != 0x00) goto error; */ + ret = jdvbt90502_reg_read(state, 0x00B0, res2, sizeof(res2)); + if (ret) + goto error; + if (res2[0] >= 0xA7) + break; + msleep(100); + } + if (retry >= RETRY_COUNT) { + deb_fe("%s: FE does not get ready after freq setting.\n", + __func__); + return -EREMOTEIO; + } + + return 0; +error: + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; +} + +static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state) +{ + u8 result; + int ret; + + *state = FE_HAS_SIGNAL; + + ret = jdvbt90502_pll_read(fe->demodulator_priv, &result); + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + *state = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC; + + if (result & PLL_STATUS_LOCKED) + *state |= FE_HAS_LOCK; + + return 0; +} + +static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + int ret; + u8 rbuf[37]; + + *strength = 0; + + /* status register (incl. signal strength) : 0x89 */ + /* TODO: read just the necessary registers [0x8B..0x8D]? */ + ret = jdvbt90502_reg_read(fe->demodulator_priv, 0x0089, + rbuf, sizeof(rbuf)); + + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + /* signal_strength: rbuf[2-4] (24bit BE), use lower 16bit for now. */ + *strength = (rbuf[3] << 8) + rbuf[4]; + if (rbuf[2]) + *strength = 0xffff; + + return 0; +} + + +/* filter out un-supported properties to notify users */ +static int jdvbt90502_set_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + int r = 0; + + switch (tvp->cmd) { + case DTV_DELIVERY_SYSTEM: + if (tvp->u.data != SYS_ISDBT) + r = -EINVAL; + break; + case DTV_CLEAR: + case DTV_TUNE: + case DTV_FREQUENCY: + break; + default: + r = -EINVAL; + } + return r; +} + +static int jdvbt90502_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + p->inversion = INVERSION_AUTO; + p->bandwidth_hz = 6000000; + p->code_rate_HP = FEC_AUTO; + p->code_rate_LP = FEC_AUTO; + p->modulation = QAM_64; + p->transmission_mode = TRANSMISSION_MODE_AUTO; + p->guard_interval = GUARD_INTERVAL_AUTO; + p->hierarchy = HIERARCHY_AUTO; + return 0; +} + +static int jdvbt90502_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + /** + * NOTE: ignore all the parameters except frequency. + * others should be fixed to the proper value for ISDB-T, + * but don't check here. + */ + + struct jdvbt90502_state *state = fe->demodulator_priv; + int ret; + + deb_fe("%s: Freq:%d\n", __func__, p->frequency); + + /* for recovery from DTV_CLEAN */ + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + + ret = jdvbt90502_pll_set_freq(state, p->frequency); + if (ret) { + deb_fe("%s:ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + return 0; +} + + +/** + * (reg, val) commad list to initialize this module. + * captured on a Windows box. + */ +static u8 init_code[][2] = { + {0x01, 0x40}, + {0x04, 0x38}, + {0x05, 0x40}, + {0x07, 0x40}, + {0x0F, 0x4F}, + {0x11, 0x21}, + {0x12, 0x0B}, + {0x13, 0x2F}, + {0x14, 0x31}, + {0x16, 0x02}, + {0x21, 0xC4}, + {0x22, 0x20}, + {0x2C, 0x79}, + {0x2D, 0x34}, + {0x2F, 0x00}, + {0x30, 0x28}, + {0x31, 0x31}, + {0x32, 0xDF}, + {0x38, 0x01}, + {0x39, 0x78}, + {0x3B, 0x33}, + {0x3C, 0x33}, + {0x48, 0x90}, + {0x51, 0x68}, + {0x5E, 0x38}, + {0x71, 0x00}, + {0x72, 0x08}, + {0x77, 0x00}, + {0xC0, 0x21}, + {0xC1, 0x10}, + {0xE4, 0x1A}, + {0xEA, 0x1F}, + {0x77, 0x00}, + {0x71, 0x00}, + {0x71, 0x00}, + {0x76, 0x0C}, +}; + +static const int init_code_len = sizeof(init_code) / sizeof(u8[2]); + +static int jdvbt90502_init(struct dvb_frontend *fe) +{ + int i = -1; + int ret; + struct i2c_msg msg; + + struct jdvbt90502_state *state = fe->demodulator_priv; + + deb_fe("%s called.\n", __func__); + + msg.addr = state->config.demod_address; + msg.flags = 0; + msg.len = 2; + for (i = 0; i < init_code_len; i++) { + msg.buf = init_code[i]; + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) + goto error; + } + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + msleep(100); + + return 0; + +error: + deb_fe("%s: init_code[%d] failed. ret==%d\n", __func__, i, ret); + return -EREMOTEIO; +} + + +static void jdvbt90502_release(struct dvb_frontend *fe) +{ + struct jdvbt90502_state *state = fe->demodulator_priv; + kfree(state); +} + + +static struct dvb_frontend_ops jdvbt90502_ops; + +struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d) +{ + struct jdvbt90502_state *state = NULL; + + deb_info("%s called.\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct jdvbt90502_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = &d->i2c_adap; + memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config)); + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &jdvbt90502_ops, + sizeof(jdvbt90502_ops)); + state->frontend.demodulator_priv = state; + + if (jdvbt90502_init(&state->frontend) < 0) + goto error; + + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops jdvbt90502_ops = { + .delsys = { SYS_ISDBT }, + .info = { + .name = "Comtech JDVBT90502 ISDB-T", + .frequency_min = 473000000, /* UHF 13ch, center */ + .frequency_max = 767142857, /* UHF 62ch, center */ + .frequency_stepsize = JDVBT90502_PLL_CLK / JDVBT90502_PLL_DIVIDER, + .frequency_tolerance = 0, + + /* NOTE: this driver ignores all parameters but frequency. */ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = jdvbt90502_release, + + .init = jdvbt90502_init, + .write = _jdvbt90502_write, + + .set_property = jdvbt90502_set_property, + + .set_frontend = jdvbt90502_set_frontend, + .get_frontend = jdvbt90502_get_frontend, + + .read_status = jdvbt90502_read_status, + .read_signal_strength = jdvbt90502_read_signal_strength, +}; diff --git a/drivers/media/usb/dvb-usb/friio.c b/drivers/media/usb/dvb-usb/friio.c new file mode 100644 index 000000000000..474a17e4db0c --- /dev/null +++ b/drivers/media/usb/dvb-usb/friio.c @@ -0,0 +1,522 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada + * + * This module is based off the the gl861 and vp702x modules. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "friio.h" + +/* debug */ +int dvb_usb_friio_debug; +module_param_named(debug, dvb_usb_friio_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,2=xfer,4=rc,8=fe (or-able))." + DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/** + * Indirect I2C access to the PLL via FE. + * whole I2C protocol data to the PLL is sent via the FE's I2C register. + * This is done by a control msg to the FE with the I2C data accompanied, and + * a specific USB request number is assigned for that purpose. + * + * this func sends wbuf[1..] to the I2C register wbuf[0] at addr (= at FE). + * TODO: refoctored, smarter i2c functions. + */ +static int gl861_i2c_ctrlmsg_data(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index = wbuf[0]; /* must be JDVBT90502_2ND_I2C_REG(=0xFE) */ + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write only */ + u8 req, type; + + deb_xfer("write to PLL:0x%02x via FE reg:0x%02x, len:%d\n", + wbuf[1], wbuf[0], wlen - 1); + + if (wo && wlen >= 2) { + req = GL861_REQ_I2C_DATA_CTRL_WRITE; + type = GL861_WRITE; + udelay(20); + return usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + req, type, value, index, + &wbuf[1], wlen - 1, 2000); + } + + deb_xfer("not supported ctrl-msg, aborting."); + return -EINVAL; +} + +/* normal I2C access (without extra data arguments). + * write to the register wbuf[0] at I2C address addr with the value wbuf[1], + * or read from the register wbuf[0]. + * register address can be 16bit (wbuf[2]<<8 | wbuf[0]) if wlen==3 + */ +static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index; + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 req, type; + unsigned int pipe; + + /* special case for the indirect I2C access to the PLL via FE, */ + if (addr == friio_fe_config.demod_address && + wbuf[0] == JDVBT90502_2ND_I2C_REG) + return gl861_i2c_ctrlmsg_data(d, addr, wbuf, wlen, rbuf, rlen); + + if (wo) { + req = GL861_REQ_I2C_WRITE; + type = GL861_WRITE; + pipe = usb_sndctrlpipe(d->udev, 0); + } else { /* rw */ + req = GL861_REQ_I2C_READ; + type = GL861_READ; + pipe = usb_rcvctrlpipe(d->udev, 0); + } + + switch (wlen) { + case 1: + index = wbuf[0]; + break; + case 2: + index = wbuf[0]; + value = value + wbuf[1]; + break; + case 3: + /* special case for 16bit register-address */ + index = (wbuf[2] << 8) | wbuf[0]; + value = value + wbuf[1]; + break; + default: + deb_xfer("wlen = %x, aborting.", wlen); + return -EINVAL; + } + msleep(1); + return usb_control_msg(d->udev, pipe, req, type, + value, index, rbuf, rlen, 2000); +} + +/* I2C */ +static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { + if (gl861_i2c_msg(d, msg[i].addr, + msg[i].buf, msg[i].len, + msg[i + 1].buf, msg[i + 1].len) < 0) + break; + i++; + } else + if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 gl861_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int friio_ext_ctl(struct dvb_usb_adapter *adap, + u32 sat_color, int lnb_on) +{ + int i; + int ret; + struct i2c_msg msg; + u8 *buf; + u32 mask; + u8 lnb = (lnb_on) ? FRIIO_CTL_LNB : 0; + + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msg.addr = 0x00; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + + buf[0] = 0x00; + + /* send 2bit header (&B10) */ + buf[1] = lnb | FRIIO_CTL_LED | FRIIO_CTL_STROBE; + ret = gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + buf[1] = lnb | FRIIO_CTL_STROBE; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + /* send 32bit(satur, R, G, B) data in serial */ + mask = 1 << 31; + for (i = 0; i < 32; i++) { + buf[1] = lnb | FRIIO_CTL_STROBE; + if (sat_color & mask) + buf[1] |= FRIIO_CTL_LED; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + mask >>= 1; + } + + /* set the strobe off */ + buf[1] = lnb; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); + + kfree(buf); + return (ret == 70); +} + + +static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); + +/* TODO: move these init cmds to the FE's init routine? */ +static u8 streaming_init_cmds[][2] = { + {0x33, 0x08}, + {0x37, 0x40}, + {0x3A, 0x1F}, + {0x3B, 0xFF}, + {0x3C, 0x1F}, + {0x3D, 0xFF}, + {0x38, 0x00}, + {0x35, 0x00}, + {0x39, 0x00}, + {0x36, 0x00}, +}; +static int cmdlen = sizeof(streaming_init_cmds) / 2; + +/* + * Command sequence in this init function is a replay + * of the captured USB commands from the Windows proprietary driver. + */ +static int friio_initialize(struct dvb_usb_device *d) +{ + int ret; + int i; + int retry = 0; + u8 *rbuf, *wbuf; + + deb_info("%s called.\n", __func__); + + wbuf = kmalloc(3, GFP_KERNEL); + if (!wbuf) + return -ENOMEM; + + rbuf = kmalloc(2, GFP_KERNEL); + if (!rbuf) { + kfree(wbuf); + return -ENOMEM; + } + + /* use gl861_i2c_msg instead of gl861_i2c_xfer(), */ + /* because the i2c device is not set up yet. */ + wbuf[0] = 0x11; + wbuf[1] = 0x02; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + msleep(2); + + wbuf[0] = 0x11; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + msleep(1); + + /* following msgs should be in the FE's init code? */ + /* cmd sequence to identify the device type? (friio black/white) */ + wbuf[0] = 0x03; + wbuf[1] = 0x80; + /* can't use gl861_i2c_cmd, as the register-addr is 16bit(0x0100) */ + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, + 0x1200, 0x0100, wbuf, 2, 2000); + if (ret < 0) + goto error; + + msleep(2); + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg.0x0100 */ + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x12 >> 1, wbuf, 3, rbuf, 2); + /* my Friio White returns 0xffff. */ + if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) + goto error; + + msleep(2); + wbuf[0] = 0x03; + wbuf[1] = 0x80; + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, + 0x9000, 0x0100, wbuf, 2, 2000); + if (ret < 0) + goto error; + + msleep(2); + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg.0x0100 */ + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x90 >> 1, wbuf, 3, rbuf, 2); + /* my Friio White returns 0xffff again. */ + if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) + goto error; + + msleep(1); + +restart: + /* ============ start DEMOD init cmds ================== */ + /* read PLL status to clear the POR bit */ + wbuf[0] = JDVBT90502_2ND_I2C_REG; + wbuf[1] = (FRIIO_PLL_ADDR << 1) + 1; /* +1 for reading */ + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + msleep(5); + /* note: DEMODULATOR has 16bit register-address. */ + wbuf[0] = 0x00; + wbuf[2] = 0x01; /* reg addr: 0x0100 */ + wbuf[1] = 0x00; /* val: not used */ + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 3, rbuf, 1); + if (ret < 0) + goto error; +/* + msleep(1); + wbuf[0] = 0x80; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, rbuf, 1); + if (ret < 0) + goto error; + */ + if (rbuf[0] & 0x80) { /* still in PowerOnReset state? */ + if (++retry > 3) { + deb_info("failed to get the correct" + " FE demod status:0x%02x\n", rbuf[0]); + goto error; + } + msleep(100); + goto restart; + } + + /* TODO: check return value in rbuf */ + /* =========== end DEMOD init cmds ===================== */ + msleep(1); + + wbuf[0] = 0x30; + wbuf[1] = 0x04; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + msleep(2); + /* following 2 cmds unnecessary? */ + wbuf[0] = 0x00; + wbuf[1] = 0x01; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + wbuf[0] = 0x06; + wbuf[1] = 0x0F; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + goto error; + + /* some streaming ctl cmds (maybe) */ + msleep(10); + for (i = 0; i < cmdlen; i++) { + ret = gl861_i2c_msg(d, 0x00, streaming_init_cmds[i], 2, + NULL, 0); + if (ret < 0) + goto error; + msleep(1); + } + msleep(20); + + /* change the LED color etc. */ + ret = friio_streaming_ctrl(&d->adapter[0], 0); + if (ret < 0) + goto error; + + return 0; + +error: + kfree(wbuf); + kfree(rbuf); + deb_info("%s:ret == %d\n", __func__, ret); + return -EIO; +} + +/* Callbacks for DVB USB */ + +static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + + deb_info("%s called.(%d)\n", __func__, onoff); + + /* set the LED color and saturation (and LNB on) */ + if (onoff) + ret = friio_ext_ctl(adap, 0x6400ff64, 1); + else + ret = friio_ext_ctl(adap, 0x96ff00ff, 1); + + if (ret != 1) { + deb_info("%s failed to send cmdx. ret==%d\n", __func__, ret); + return -EREMOTEIO; + } + return 0; +} + +static int friio_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (friio_initialize(adap->dev) < 0) + return -EIO; + + adap->fe_adap[0].fe = jdvbt90502_attach(adap->dev); + if (adap->fe_adap[0].fe == NULL) + return -EIO; + + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties friio_properties; + +static int friio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + if (intf->num_altsetting < GL861_ALTSETTING_COUNT) + return -ENODEV; + + alt = usb_altnum_to_altsetting(intf, FRIIO_BULK_ALTSETTING); + if (alt == NULL) { + deb_rc("not alt found!\n"); + return -ENODEV; + } + ret = usb_set_interface(interface_to_usbdev(intf), + alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + if (ret != 0) { + deb_rc("failed to set alt-setting!\n"); + return ret; + } + + ret = dvb_usb_device_init(intf, &friio_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) + friio_streaming_ctrl(&d->adapter[0], 1); + + return ret; +} + + +struct jdvbt90502_config friio_fe_config = { + .demod_address = FRIIO_DEMOD_ADDR, + .pll_address = FRIIO_PLL_ADDR, +}; + +static struct i2c_algorithm gl861_i2c_algo = { + .master_xfer = gl861_i2c_xfer, + .functionality = gl861_i2c_func, +}; + +static struct usb_device_id friio_table[] = { + { USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, friio_table); + + +static struct dvb_usb_device_properties friio_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = 0, + + .num_adapters = 1, + .adapter = { + /* caps:0 => no pid filter, 188B TS packet */ + /* GL861 has a HW pid filter, but no info available. */ + { + .num_frontends = 1, + .fe = {{ + .caps = 0, + + .frontend_attach = friio_frontend_attach, + .streaming_ctrl = friio_streaming_ctrl, + + .stream = { + .type = USB_BULK, + /* count <= MAX_NO_URBS_FOR_DATA_STREAM(10) */ + .count = 8, + .endpoint = 0x01, + .u = { + /* GL861 has 6KB buf inside */ + .bulk = { + .buffersize = 16384, + } + } + }, + }}, + } + }, + .i2c_algo = &gl861_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "774 Friio ISDB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &friio_table[0], NULL }, + }, + } +}; + +static struct usb_driver friio_driver = { + .name = "dvb_usb_friio", + .probe = friio_probe, + .disconnect = dvb_usb_device_exit, + .id_table = friio_table, +}; + +module_usb_driver(friio_driver); + +MODULE_AUTHOR("Akihiro Tsukada "); +MODULE_DESCRIPTION("Driver for Friio ISDB-T USB2.0 Receiver"); +MODULE_VERSION("0.2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/friio.h b/drivers/media/usb/dvb-usb/friio.h new file mode 100644 index 000000000000..0f461ca10cb9 --- /dev/null +++ b/drivers/media/usb/dvb-usb/friio.h @@ -0,0 +1,99 @@ +/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. + * + * Copyright (C) 2009 Akihiro Tsukada + * + * This module is based off the the gl861 and vp702x modules. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_FRIIO_H_ +#define _DVB_USB_FRIIO_H_ + +/** + * Friio Components + * USB hub: AU4254 + * USB controller(+ TS dmx & streaming): GL861 + * Frontend: comtech JDVBT-90502 + * (tuner PLL: tua6034, I2C addr:(0xC0 >> 1)) + * (OFDM demodulator: TC90502, I2C addr:(0x30 >> 1)) + * LED x3 (+LNB) control: PIC 16F676 + * EEPROM: 24C08 + * + * (USB smart card reader: AU9522) + * + */ + +#define DVB_USB_LOG_PREFIX "friio" +#include "dvb-usb.h" + +extern int dvb_usb_friio_debug; +#define deb_info(args...) dprintk(dvb_usb_friio_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_friio_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_friio_debug, 0x04, args) +#define deb_fe(args...) dprintk(dvb_usb_friio_debug, 0x08, args) + +/* Vendor requests */ +#define GL861_WRITE 0x40 +#define GL861_READ 0xc0 + +/* command bytes */ +#define GL861_REQ_I2C_WRITE 0x01 +#define GL861_REQ_I2C_READ 0x02 +/* For control msg with data argument */ +/* Used for accessing the PLL on the secondary I2C bus of FE via GL861 */ +#define GL861_REQ_I2C_DATA_CTRL_WRITE 0x03 + +#define GL861_ALTSETTING_COUNT 2 +#define FRIIO_BULK_ALTSETTING 0 +#define FRIIO_ISOC_ALTSETTING 1 + +/* LED & LNB control via PIC. */ +/* basically, it's serial control with clock and strobe. */ +/* write the below 4bit control data to the reg 0x00 at the I2C addr 0x00 */ +/* when controlling the LEDs, 32bit(saturation, R, G, B) is sent on the bit3*/ +#define FRIIO_CTL_LNB (1 << 0) +#define FRIIO_CTL_STROBE (1 << 1) +#define FRIIO_CTL_CLK (1 << 2) +#define FRIIO_CTL_LED (1 << 3) + +/* Front End related */ + +#define FRIIO_DEMOD_ADDR (0x30 >> 1) +#define FRIIO_PLL_ADDR (0xC0 >> 1) + +#define JDVBT90502_PLL_CLK 4000000 +#define JDVBT90502_PLL_DIVIDER 28 + +#define JDVBT90502_2ND_I2C_REG 0xFE + +/* byte index for pll i2c command data structure*/ +/* see datasheet for tua6034 */ +#define DEMOD_REDIRECT_REG 0 +#define ADDRESS_BYTE 1 +#define DIVIDER_BYTE1 2 +#define DIVIDER_BYTE2 3 +#define CONTROL_BYTE 4 +#define BANDSWITCH_BYTE 5 +#define AGC_CTRL_BYTE 5 +#define PLL_CMD_LEN 6 + +/* bit masks for PLL STATUS response */ +#define PLL_STATUS_POR_MODE 0x80 /* 1: Power on Reset (test) Mode */ +#define PLL_STATUS_LOCKED 0x40 /* 1: locked */ +#define PLL_STATUS_AGC_ACTIVE 0x08 /* 1:active */ +#define PLL_STATUS_TESTMODE 0x07 /* digital output level (5 level) */ + /* 0.15Vcc step 0x00: < 0.15Vcc, ..., 0x04: >= 0.6Vcc (<= 1Vcc) */ + + +struct jdvbt90502_config { + u8 demod_address; /* i2c addr for demodulator IC */ + u8 pll_address; /* PLL addr on the secondary i2c*/ +}; +extern struct jdvbt90502_config friio_fe_config; + +extern struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d); +#endif diff --git a/drivers/media/usb/dvb-usb/gp8psk-fe.c b/drivers/media/usb/dvb-usb/gp8psk-fe.c new file mode 100644 index 000000000000..67957dd99ede --- /dev/null +++ b/drivers/media/usb/dvb-usb/gp8psk-fe.c @@ -0,0 +1,369 @@ +/* DVB USB compliant Linux driver for the + * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module + * + * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) + * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) + * + * Thanks to GENPIX for the sample code used to implement this module. + * + * This module is based off the vp7045 and vp702x modules + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "gp8psk.h" + +struct gp8psk_fe_state { + struct dvb_frontend fe; + struct dvb_usb_device *d; + u8 lock; + u16 snr; + unsigned long next_status_check; + unsigned long status_check_interval; +}; + +static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 status; + gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); + return status & bmDCtuned; +} + +static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) +{ + struct gp8psk_fe_state *state = fe->demodulator_priv; + return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); +} + +static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) +{ + u8 buf[6]; + if (time_after(jiffies,st->next_status_check)) { + gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0,0,&st->lock,1); + gp8psk_usb_in_op(st->d, GET_SIGNAL_STRENGTH, 0,0,buf,6); + st->snr = (buf[1]) << 8 | buf[0]; + st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; + } + return 0; +} + +static int gp8psk_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + + if (st->lock) + *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + + if (*status & FE_HAS_LOCK) + st->status_check_interval = 1000; + else + st->status_check_interval = 100; + return 0; +} + +/* not supported by this Frontend */ +static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + (void) fe; + *ber = 0; + return 0; +} + +/* not supported by this Frontend */ +static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + (void) fe; + *unc = 0; + return 0; +} + +static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + /* snr is reported in dBu*256 */ + *snr = st->snr; + return 0; +} + +static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + /* snr is reported in dBu*256 */ + /* snr / 38.4 ~= 100% strength */ + /* snr * 17 returns 100% strength as 65535 */ + if (st->snr > 0xf00) + *strength = 0xffff; + else + *strength = (st->snr << 4) + st->snr; /* snr*17 */ + return 0; +} + +static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 cmd[10]; + u32 freq = c->frequency * 1000; + int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); + + deb_fe("%s()\n", __func__); + + cmd[4] = freq & 0xff; + cmd[5] = (freq >> 8) & 0xff; + cmd[6] = (freq >> 16) & 0xff; + cmd[7] = (freq >> 24) & 0xff; + + /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ + if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) + c->delivery_system = SYS_TURBO; + + switch (c->delivery_system) { + case SYS_DVBS: + if (c->modulation != QPSK) { + deb_fe("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + c->fec_inner = FEC_AUTO; + break; + case SYS_DVBS2: /* kept for backwards compatibility */ + deb_fe("%s: DVB-S2 delivery system selected\n", __func__); + break; + case SYS_TURBO: + deb_fe("%s: Turbo-FEC delivery system selected\n", __func__); + break; + + default: + deb_fe("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + cmd[0] = c->symbol_rate & 0xff; + cmd[1] = (c->symbol_rate >> 8) & 0xff; + cmd[2] = (c->symbol_rate >> 16) & 0xff; + cmd[3] = (c->symbol_rate >> 24) & 0xff; + switch (c->modulation) { + case QPSK: + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (gp8psk_tuned_to_DCII(fe)) + gp8psk_bcm4500_reload(state->d); + switch (c->fec_inner) { + case FEC_1_2: + cmd[9] = 0; break; + case FEC_2_3: + cmd[9] = 1; break; + case FEC_3_4: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_7_8: + cmd[9] = 4; break; + case FEC_AUTO: + cmd[9] = 5; break; + default: + cmd[9] = 5; break; + } + if (c->delivery_system == SYS_TURBO) + cmd[8] = ADV_MOD_TURBO_QPSK; + else + cmd[8] = ADV_MOD_DVB_QPSK; + break; + case PSK_8: /* PSK_8 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_8PSK; + switch (c->fec_inner) { + case FEC_2_3: + cmd[9] = 0; break; + case FEC_3_4: + cmd[9] = 1; break; + case FEC_3_5: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_8_9: + cmd[9] = 4; break; + default: + cmd[9] = 0; break; + } + break; + case QAM_16: /* QAM_16 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_16QAM; + cmd[9] = 0; + break; + default: /* Unknown modulation */ + deb_fe("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + gp8psk_set_tuner_mode(fe, 0); + gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); + + state->lock = 0; + state->next_status_check = jiffies; + state->status_check_interval = 200; + + return 0; +} + +static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *m) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + deb_fe("%s\n",__func__); + + if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0, + m->msg, m->msg_len)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_send_diseqc_burst (struct dvb_frontend* fe, + fe_sec_mini_cmd_t burst) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 cmd; + + deb_fe("%s\n",__func__); + + /* These commands are certainly wrong */ + cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; + + if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0, + &cmd, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct gp8psk_fe_state* state = fe->demodulator_priv; + + if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE, + (tone == SEC_TONE_ON), 0, NULL, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct gp8psk_fe_state* state = fe->demodulator_priv; + + if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, + voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) +{ + struct gp8psk_fe_state* state = fe->demodulator_priv; + return gp8psk_usb_out_op(state->d, USE_EXTRA_VOLT, onoff, 0,NULL,0); +} + +static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) +{ + struct gp8psk_fe_state* state = fe->demodulator_priv; + u8 cmd = sw_cmd & 0x7f; + + if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0, + NULL, 0)) { + return -EINVAL; + } + if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), + 0, NULL, 0)) { + return -EINVAL; + } + + return 0; +} + +static void gp8psk_fe_release(struct dvb_frontend* fe) +{ + struct gp8psk_fe_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops gp8psk_fe_ops; + +struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d) +{ + struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); + if (s == NULL) + goto error; + + s->d = d; + memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); + s->fe.demodulator_priv = s; + + goto success; +error: + return NULL; +success: + return &s->fe; +} + + +static struct dvb_frontend_ops gp8psk_fe_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Genpix DVB-S", + .frequency_min = 800000, + .frequency_max = 2250000, + .frequency_stepsize = 100, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + /* + * FE_CAN_QAM_16 is for compatibility + * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) + */ + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC + }, + + .release = gp8psk_fe_release, + + .init = NULL, + .sleep = NULL, + + .set_frontend = gp8psk_fe_set_frontend, + + .get_tune_settings = gp8psk_fe_get_tune_settings, + + .read_status = gp8psk_fe_read_status, + .read_ber = gp8psk_fe_read_ber, + .read_signal_strength = gp8psk_fe_read_signal_strength, + .read_snr = gp8psk_fe_read_snr, + .read_ucblocks = gp8psk_fe_read_unc_blocks, + + .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg, + .diseqc_send_burst = gp8psk_fe_send_diseqc_burst, + .set_tone = gp8psk_fe_set_tone, + .set_voltage = gp8psk_fe_set_voltage, + .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd, + .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage +}; diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c new file mode 100644 index 000000000000..5d0384dd45b5 --- /dev/null +++ b/drivers/media/usb/dvb-usb/gp8psk.c @@ -0,0 +1,328 @@ +/* DVB USB compliant Linux driver for the + * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module + * + * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) + * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) + * + * Thanks to GENPIX for the sample code used to implement this module. + * + * This module is based off the vp7045 and vp702x modules + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "gp8psk.h" + +/* debug */ +static char bcm4500_firmware[] = "dvb-usb-gp8psk-02.fw"; +int dvb_usb_gp8psk_debug; +module_param_named(debug,dvb_usb_gp8psk_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int gp8psk_get_fw_version(struct dvb_usb_device *d, u8 *fw_vers) +{ + return (gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6)); +} + +static int gp8psk_get_fpga_version(struct dvb_usb_device *d, u8 *fpga_vers) +{ + return (gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, fpga_vers, 1)); +} + +static void gp8psk_info(struct dvb_usb_device *d) +{ + u8 fpga_vers, fw_vers[6]; + + if (!gp8psk_get_fw_version(d, fw_vers)) + info("FW Version = %i.%02i.%i (0x%x) Build %4i/%02i/%02i", + fw_vers[2], fw_vers[1], fw_vers[0], GP8PSK_FW_VERS(fw_vers), + 2000 + fw_vers[5], fw_vers[4], fw_vers[3]); + else + info("failed to get FW version"); + + if (!gp8psk_get_fpga_version(d, &fpga_vers)) + info("FPGA Version = %i", fpga_vers); + else + info("failed to get FPGA version"); +} + +int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) +{ + int ret = 0,try = 0; + + if ((ret = mutex_lock_interruptible(&d->usb_mutex))) + return ret; + + while (ret >= 0 && ret != blen && try < 3) { + ret = usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev,0), + req, + USB_TYPE_VENDOR | USB_DIR_IN, + value,index,b,blen, + 2000); + deb_info("reading number %d (ret: %d)\n",try,ret); + try++; + } + + if (ret < 0 || ret != blen) { + warn("usb in %d operation failed.", req); + ret = -EIO; + } else + ret = 0; + + deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index); + debug_dump(b,blen,deb_xfer); + + mutex_unlock(&d->usb_mutex); + + return ret; +} + +int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + + deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index); + debug_dump(b,blen,deb_xfer); + + if ((ret = mutex_lock_interruptible(&d->usb_mutex))) + return ret; + + if (usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev,0), + req, + USB_TYPE_VENDOR | USB_DIR_OUT, + value,index,b,blen, + 2000) != blen) { + warn("usb out operation failed."); + ret = -EIO; + } else + ret = 0; + mutex_unlock(&d->usb_mutex); + + return ret; +} + +static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d) +{ + int ret; + const struct firmware *fw = NULL; + const u8 *ptr; + u8 *buf; + if ((ret = request_firmware(&fw, bcm4500_firmware, + &d->udev->dev)) != 0) { + err("did not find the bcm4500 firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)", + bcm4500_firmware,ret); + return ret; + } + + ret = -EINVAL; + + if (gp8psk_usb_out_op(d, LOAD_BCM4500,1,0,NULL, 0)) + goto out_rel_fw; + + info("downloading bcm4500 firmware from file '%s'",bcm4500_firmware); + + ptr = fw->data; + buf = kmalloc(64, GFP_KERNEL | GFP_DMA); + if (!buf) { + ret = -ENOMEM; + goto out_rel_fw; + } + + while (ptr[0] != 0xff) { + u16 buflen = ptr[0] + 4; + if (ptr + buflen >= fw->data + fw->size) { + err("failed to load bcm4500 firmware."); + goto out_free; + } + memcpy(buf, ptr, buflen); + if (dvb_usb_generic_write(d, buf, buflen)) { + err("failed to load bcm4500 firmware."); + goto out_free; + } + ptr += buflen; + } + + ret = 0; + +out_free: + kfree(buf); +out_rel_fw: + release_firmware(fw); + + return ret; +} + +static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 status, buf; + int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); + + if (onoff) { + gp8psk_usb_in_op(d, GET_8PSK_CONFIG,0,0,&status,1); + if (! (status & bm8pskStarted)) { /* started */ + if(gp_product_id == USB_PID_GENPIX_SKYWALKER_CW3K) + gp8psk_usb_out_op(d, CW3K_INIT, 1, 0, NULL, 0); + if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1)) + return -EINVAL; + gp8psk_info(d); + } + + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (! (status & bm8pskFW_Loaded)) /* BCM4500 firmware loaded */ + if(gp8psk_load_bcm4500fw(d)) + return -EINVAL; + + if (! (status & bmIntersilOn)) /* LNB Power */ + if (gp8psk_usb_in_op(d, START_INTERSIL, 1, 0, + &buf, 1)) + return -EINVAL; + + /* Set DVB mode to 1 */ + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (gp8psk_usb_out_op(d, SET_DVB_MODE, 1, 0, NULL, 0)) + return -EINVAL; + /* Abort possible TS (if previous tune crashed) */ + if (gp8psk_usb_out_op(d, ARM_TRANSFER, 0, 0, NULL, 0)) + return -EINVAL; + } else { + /* Turn off LNB power */ + if (gp8psk_usb_in_op(d, START_INTERSIL, 0, 0, &buf, 1)) + return -EINVAL; + /* Turn off 8psk power */ + if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) + return -EINVAL; + if(gp_product_id == USB_PID_GENPIX_SKYWALKER_CW3K) + gp8psk_usb_out_op(d, CW3K_INIT, 0, 0, NULL, 0); + } + return 0; +} + +int gp8psk_bcm4500_reload(struct dvb_usb_device *d) +{ + u8 buf; + int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); + /* Turn off 8psk power */ + if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) + return -EINVAL; + /* Turn On 8psk power */ + if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1)) + return -EINVAL; + /* load BCM4500 firmware */ + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (gp8psk_load_bcm4500fw(d)) + return -EINVAL; + return 0; +} + +static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + return gp8psk_usb_out_op(adap->dev, ARM_TRANSFER, onoff, 0 , NULL, 0); +} + +static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap) +{ + adap->fe_adap[0].fe = gp8psk_fe_attach(adap->dev); + return 0; +} + +static struct dvb_usb_device_properties gp8psk_properties; + +static int gp8psk_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + struct usb_device *udev = interface_to_usbdev(intf); + ret = dvb_usb_device_init(intf, &gp8psk_properties, + THIS_MODULE, NULL, adapter_nr); + if (ret == 0) { + info("found Genpix USB device pID = %x (hex)", + le16_to_cpu(udev->descriptor.idProduct)); + } + return ret; +} + +static struct usb_device_id gp8psk_usb_table [] = { + { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_1_COLD) }, + { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_1_WARM) }, + { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_2) }, + { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_1) }, + { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_2) }, +/* { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_CW3K) }, */ + { 0 }, +}; +MODULE_DEVICE_TABLE(usb, gp8psk_usb_table); + +static struct dvb_usb_device_properties gp8psk_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-gp8psk-01.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = gp8psk_streaming_ctrl, + .frontend_attach = gp8psk_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }}, + } + }, + .power_ctrl = gp8psk_power_ctrl, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 4, + .devices = { + { .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver", + .cold_ids = { &gp8psk_usb_table[0], NULL }, + .warm_ids = { &gp8psk_usb_table[1], NULL }, + }, + { .name = "Genpix 8PSK-to-USB2 Rev.2 DVB-S receiver", + .cold_ids = { NULL }, + .warm_ids = { &gp8psk_usb_table[2], NULL }, + }, + { .name = "Genpix SkyWalker-1 DVB-S receiver", + .cold_ids = { NULL }, + .warm_ids = { &gp8psk_usb_table[3], NULL }, + }, + { .name = "Genpix SkyWalker-2 DVB-S receiver", + .cold_ids = { NULL }, + .warm_ids = { &gp8psk_usb_table[4], NULL }, + }, + { NULL }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver gp8psk_usb_driver = { + .name = "dvb_usb_gp8psk", + .probe = gp8psk_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = gp8psk_usb_table, +}; + +module_usb_driver(gp8psk_usb_driver); + +MODULE_AUTHOR("Alan Nisota "); +MODULE_DESCRIPTION("Driver for Genpix DVB-S"); +MODULE_VERSION("1.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/gp8psk.h b/drivers/media/usb/dvb-usb/gp8psk.h new file mode 100644 index 000000000000..ed32b9da4843 --- /dev/null +++ b/drivers/media/usb/dvb-usb/gp8psk.h @@ -0,0 +1,100 @@ +/* DVB USB compliant Linux driver for the + * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module + * + * Copyright (C) 2006 Alan Nisota (alannisota@gmail.com) + * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) + * + * Thanks to GENPIX for the sample code used to implement this module. + * + * This module is based off the vp7045 and vp702x modules + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_GP8PSK_H_ +#define _DVB_USB_GP8PSK_H_ + +#define DVB_USB_LOG_PREFIX "gp8psk" +#include "dvb-usb.h" + +extern int dvb_usb_gp8psk_debug; +#define deb_info(args...) dprintk(dvb_usb_gp8psk_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_gp8psk_debug,0x02,args) +#define deb_rc(args...) dprintk(dvb_usb_gp8psk_debug,0x04,args) +#define deb_fe(args...) dprintk(dvb_usb_gp8psk_debug,0x08,args) + +/* Twinhan Vendor requests */ +#define TH_COMMAND_IN 0xC0 +#define TH_COMMAND_OUT 0xC1 + +/* gp8psk commands */ + +#define GET_8PSK_CONFIG 0x80 /* in */ +#define SET_8PSK_CONFIG 0x81 +#define I2C_WRITE 0x83 +#define I2C_READ 0x84 +#define ARM_TRANSFER 0x85 +#define TUNE_8PSK 0x86 +#define GET_SIGNAL_STRENGTH 0x87 /* in */ +#define LOAD_BCM4500 0x88 +#define BOOT_8PSK 0x89 /* in */ +#define START_INTERSIL 0x8A /* in */ +#define SET_LNB_VOLTAGE 0x8B +#define SET_22KHZ_TONE 0x8C +#define SEND_DISEQC_COMMAND 0x8D +#define SET_DVB_MODE 0x8E +#define SET_DN_SWITCH 0x8F +#define GET_SIGNAL_LOCK 0x90 /* in */ +#define GET_FW_VERS 0x92 +#define GET_SERIAL_NUMBER 0x93 /* in */ +#define USE_EXTRA_VOLT 0x94 +#define GET_FPGA_VERS 0x95 +#define CW3K_INIT 0x9d + +/* PSK_configuration bits */ +#define bm8pskStarted 0x01 +#define bm8pskFW_Loaded 0x02 +#define bmIntersilOn 0x04 +#define bmDVBmode 0x08 +#define bm22kHz 0x10 +#define bmSEL18V 0x20 +#define bmDCtuned 0x40 +#define bmArmed 0x80 + +/* Satellite modulation modes */ +#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */ +#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */ +#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */ +#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */ + +#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */ +#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */ +#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */ +#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */ +#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */ +#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */ + +#define GET_USB_SPEED 0x07 + +#define RESET_FX2 0x13 + +#define FW_VERSION_READ 0x0B +#define VENDOR_STRING_READ 0x0C +#define PRODUCT_STRING_READ 0x0D +#define FW_BCD_VERSION_READ 0x14 + +/* firmware revision id's */ +#define GP8PSK_FW_REV1 0x020604 +#define GP8PSK_FW_REV2 0x020704 +#define GP8PSK_FW_VERS(_fw_vers) ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0]) + +extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d); +extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); +extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen); +extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d); + +#endif diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c new file mode 100644 index 000000000000..288af29a8bb7 --- /dev/null +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -0,0 +1,1099 @@ +/* DVB USB compliant linux driver for MSI Mega Sky 580 DVB-T USB2.0 receiver + * + * Copyright (C) 2006 Aapo Tahkola (aet@rasterburn.org) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#include "m920x.h" + +#include "mt352.h" +#include "mt352_priv.h" +#include "qt1010.h" +#include "tda1004x.h" +#include "tda827x.h" + +#include +#include "tuner-simple.h" +#include + +/* debug */ +static int dvb_usb_m920x_debug; +module_param_named(debug,dvb_usb_m920x_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int m920x_set_filter(struct dvb_usb_device *d, int type, int idx, int pid); + +static inline int m920x_read(struct usb_device *udev, u8 request, u16 value, + u16 index, void *data, int size) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + request, USB_TYPE_VENDOR | USB_DIR_IN, + value, index, data, size, 2000); + if (ret < 0) { + printk(KERN_INFO "m920x_read = error: %d\n", ret); + return ret; + } + + if (ret != size) { + deb("m920x_read = no data\n"); + return -EIO; + } + + return 0; +} + +static inline int m920x_write(struct usb_device *udev, u8 request, + u16 value, u16 index) +{ + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + request, USB_TYPE_VENDOR | USB_DIR_OUT, + value, index, NULL, 0, 2000); + + return ret; +} + +static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) +{ + int ret = 0, i, epi, flags = 0; + int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; + + /* Remote controller init. */ + if (d->props.rc.legacy.rc_query) { + deb("Initialising remote control\n"); + while (rc_seq->address) { + if ((ret = m920x_write(d->udev, M9206_CORE, + rc_seq->data, + rc_seq->address)) != 0) { + deb("Initialising remote control failed\n"); + return ret; + } + + rc_seq++; + } + + deb("Initialising remote control success\n"); + } + + for (i = 0; i < d->props.num_adapters; i++) + flags |= d->adapter[i].props.fe[0].caps; + + /* Some devices(Dposh) might crash if we attempt touch at all. */ + if (flags & DVB_USB_ADAP_HAS_PID_FILTER) { + for (i = 0; i < d->props.num_adapters; i++) { + epi = d->adapter[i].props.fe[0].stream.endpoint - 0x81; + + if (epi < 0 || epi >= M9206_MAX_ADAPTERS) { + printk(KERN_INFO "m920x: Unexpected adapter endpoint!\n"); + return -EINVAL; + } + + adap_enabled[epi] = 1; + } + + for (i = 0; i < M9206_MAX_ADAPTERS; i++) { + if (adap_enabled[i]) + continue; + + if ((ret = m920x_set_filter(d, 0x81 + i, 0, 0x0)) != 0) + return ret; + + if ((ret = m920x_set_filter(d, 0x81 + i, 0, 0x02f5)) != 0) + return ret; + } + } + + return ret; +} + +static int m920x_init_ep(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *alt; + + if ((alt = usb_altnum_to_altsetting(intf, 1)) == NULL) { + deb("No alt found!\n"); + return -ENODEV; + } + + return usb_set_interface(udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); +} + +static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct m920x_state *m = d->priv; + int i, ret = 0; + u8 *rc_state; + + rc_state = kmalloc(2, GFP_KERNEL); + if (!rc_state) + return -ENOMEM; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0) + goto out; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) + goto out; + + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) + if (rc5_data(&d->props.rc.legacy.rc_map_table[i]) == rc_state[1]) { + *event = d->props.rc.legacy.rc_map_table[i].keycode; + + switch(rc_state[0]) { + case 0x80: + *state = REMOTE_NO_KEY_PRESSED; + goto out; + + case 0x88: /* framing error or "invalid code" */ + case 0x99: + case 0xc0: + case 0xd8: + *state = REMOTE_NO_KEY_PRESSED; + m->rep_count = 0; + goto out; + + case 0x93: + case 0x92: + case 0x83: /* pinnacle PCTV310e */ + case 0x82: + m->rep_count = 0; + *state = REMOTE_KEY_PRESSED; + goto out; + + case 0x91: + case 0x81: /* pinnacle PCTV310e */ + /* prevent immediate auto-repeat */ + if (++m->rep_count > 2) + *state = REMOTE_KEY_REPEAT; + else + *state = REMOTE_NO_KEY_PRESSED; + goto out; + + default: + deb("Unexpected rc state %02x\n", rc_state[0]); + *state = REMOTE_NO_KEY_PRESSED; + goto out; + } + } + + if (rc_state[1] != 0) + deb("Unknown rc key %02x\n", rc_state[1]); + + *state = REMOTE_NO_KEY_PRESSED; + + out: + kfree(rc_state); + return ret; +} + +/* I2C */ +static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i, j; + int ret = 0; + + if (!num) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + if (msg[i].flags & (I2C_M_NO_RD_ACK | I2C_M_IGNORE_NAK | I2C_M_TEN) || msg[i].len == 0) { + /* For a 0 byte message, I think sending the address + * to index 0x80|0x40 would be the correct thing to + * do. However, zero byte messages are only used for + * probing, and since we don't know how to get the + * slave's ack, we can't probe. */ + ret = -ENOTSUPP; + goto unlock; + } + /* Send START & address/RW bit */ + if (!(msg[i].flags & I2C_M_NOSTART)) { + if ((ret = m920x_write(d->udev, M9206_I2C, + (msg[i].addr << 1) | + (msg[i].flags & I2C_M_RD ? 0x01 : 0), 0x80)) != 0) + goto unlock; + /* Should check for ack here, if we knew how. */ + } + if (msg[i].flags & I2C_M_RD) { + for (j = 0; j < msg[i].len; j++) { + /* Last byte of transaction? + * Send STOP, otherwise send ACK. */ + int stop = (i+1 == num && j+1 == msg[i].len) ? 0x40 : 0x01; + + if ((ret = m920x_read(d->udev, M9206_I2C, 0x0, + 0x20 | stop, + &msg[i].buf[j], 1)) != 0) + goto unlock; + } + } else { + for (j = 0; j < msg[i].len; j++) { + /* Last byte of transaction? Then send STOP. */ + int stop = (i+1 == num && j+1 == msg[i].len) ? 0x40 : 0x00; + + if ((ret = m920x_write(d->udev, M9206_I2C, msg[i].buf[j], stop)) != 0) + goto unlock; + /* Should check for ack here too. */ + } + } + } + ret = num; + + unlock: + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 m920x_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm m920x_i2c_algo = { + .master_xfer = m920x_i2c_xfer, + .functionality = m920x_i2c_func, +}; + +/* pid filter */ +static int m920x_set_filter(struct dvb_usb_device *d, int type, int idx, int pid) +{ + int ret = 0; + + if (pid >= 0x8000) + return -EINVAL; + + pid |= 0x8000; + + if ((ret = m920x_write(d->udev, M9206_FILTER, pid, (type << 8) | (idx * 4) )) != 0) + return ret; + + if ((ret = m920x_write(d->udev, M9206_FILTER, 0, (type << 8) | (idx * 4) )) != 0) + return ret; + + return ret; +} + +static int m920x_update_filters(struct dvb_usb_adapter *adap) +{ + struct m920x_state *m = adap->dev->priv; + int enabled = m->filtering_enabled[adap->id]; + int i, ret = 0, filter = 0; + int ep = adap->props.fe[0].stream.endpoint; + + for (i = 0; i < M9206_MAX_FILTERS; i++) + if (m->filters[adap->id][i] == 8192) + enabled = 0; + + /* Disable all filters */ + if ((ret = m920x_set_filter(adap->dev, ep, 1, enabled)) != 0) + return ret; + + for (i = 0; i < M9206_MAX_FILTERS; i++) + if ((ret = m920x_set_filter(adap->dev, ep, i + 2, 0)) != 0) + return ret; + + /* Set */ + if (enabled) { + for (i = 0; i < M9206_MAX_FILTERS; i++) { + if (m->filters[adap->id][i] == 0) + continue; + + if ((ret = m920x_set_filter(adap->dev, ep, filter + 2, m->filters[adap->id][i])) != 0) + return ret; + + filter++; + } + } + + return ret; +} + +static int m920x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct m920x_state *m = adap->dev->priv; + + m->filtering_enabled[adap->id] = onoff ? 1 : 0; + + return m920x_update_filters(adap); +} + +static int m920x_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) +{ + struct m920x_state *m = adap->dev->priv; + + m->filters[adap->id][index] = onoff ? pid : 0; + + return m920x_update_filters(adap); +} + +static int m920x_firmware_download(struct usb_device *udev, const struct firmware *fw) +{ + u16 value, index, size; + u8 *read, *buff; + int i, pass, ret = 0; + + buff = kmalloc(65536, GFP_KERNEL); + if (buff == NULL) + return -ENOMEM; + + read = kmalloc(4, GFP_KERNEL); + if (!read) { + kfree(buff); + return -ENOMEM; + } + + if ((ret = m920x_read(udev, M9206_FILTER, 0x0, 0x8000, read, 4)) != 0) + goto done; + deb("%x %x %x %x\n", read[0], read[1], read[2], read[3]); + + if ((ret = m920x_read(udev, M9206_FW, 0x0, 0x0, read, 1)) != 0) + goto done; + deb("%x\n", read[0]); + + for (pass = 0; pass < 2; pass++) { + for (i = 0; i + (sizeof(u16) * 3) < fw->size;) { + value = get_unaligned_le16(fw->data + i); + i += sizeof(u16); + + index = get_unaligned_le16(fw->data + i); + i += sizeof(u16); + + size = get_unaligned_le16(fw->data + i); + i += sizeof(u16); + + if (pass == 1) { + /* Will stall if using fw->data ... */ + memcpy(buff, fw->data + i, size); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0), + M9206_FW, + USB_TYPE_VENDOR | USB_DIR_OUT, + value, index, buff, size, 20); + if (ret != size) { + deb("error while uploading fw!\n"); + ret = -EIO; + goto done; + } + msleep(3); + } + i += size; + } + if (i != fw->size) { + deb("bad firmware file!\n"); + ret = -EINVAL; + goto done; + } + } + + msleep(36); + + /* m920x will disconnect itself from the bus after this. */ + (void) m920x_write(udev, M9206_CORE, 0x01, M9206_FW_GO); + deb("firmware uploaded!\n"); + + done: + kfree(read); + kfree(buff); + + return ret; +} + +/* Callbacks for DVB USB */ +static int m920x_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(usb_ifnum_to_if(udev, 0), 1); + *cold = (alt == NULL) ? 1 : 0; + + return 0; +} + +/* demod configurations */ +static int m920x_mt352_demod_init(struct dvb_frontend *fe) +{ + int ret; + u8 config[] = { CONFIG, 0x3d }; + u8 clock[] = { CLOCK_CTL, 0x30 }; + u8 reset[] = { RESET, 0x80 }; + u8 adc_ctl[] = { ADC_CTL_1, 0x40 }; + u8 agc[] = { AGC_TARGET, 0x1c, 0x20 }; + u8 sec_agc[] = { 0x69, 0x00, 0xff, 0xff, 0x40, 0xff, 0x00, 0x40, 0x40 }; + u8 unk1[] = { 0x93, 0x1a }; + u8 unk2[] = { 0xb5, 0x7a }; + + deb("Demod init!\n"); + + if ((ret = mt352_write(fe, config, ARRAY_SIZE(config))) != 0) + return ret; + if ((ret = mt352_write(fe, clock, ARRAY_SIZE(clock))) != 0) + return ret; + if ((ret = mt352_write(fe, reset, ARRAY_SIZE(reset))) != 0) + return ret; + if ((ret = mt352_write(fe, adc_ctl, ARRAY_SIZE(adc_ctl))) != 0) + return ret; + if ((ret = mt352_write(fe, agc, ARRAY_SIZE(agc))) != 0) + return ret; + if ((ret = mt352_write(fe, sec_agc, ARRAY_SIZE(sec_agc))) != 0) + return ret; + if ((ret = mt352_write(fe, unk1, ARRAY_SIZE(unk1))) != 0) + return ret; + if ((ret = mt352_write(fe, unk2, ARRAY_SIZE(unk2))) != 0) + return ret; + + return 0; +} + +static struct mt352_config m920x_mt352_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .demod_init = m920x_mt352_demod_init, +}; + +static struct tda1004x_config m920x_tda10046_08_config = { + .demod_address = 0x08, + .invert = 0, + .invert_oclk = 0, + .ts_mode = TDA10046_TS_SERIAL, + .xtal_freq = TDA10046_XTAL_16M, + .if_freq = TDA10046_FREQ_045, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GPTRI, + .request_firmware = NULL, +}; + +static struct tda1004x_config m920x_tda10046_0b_config = { + .demod_address = 0x0b, + .invert = 0, + .invert_oclk = 0, + .ts_mode = TDA10046_TS_SERIAL, + .xtal_freq = TDA10046_XTAL_16M, + .if_freq = TDA10046_FREQ_045, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GPTRI, + .request_firmware = NULL, /* uses firmware EEPROM */ +}; + +/* tuner configurations */ +static struct qt1010_config m920x_qt1010_config = { + .i2c_address = 0x62 +}; + +/* Callbacks for DVB USB */ +static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n",__func__); + + adap->fe_adap[0].fe = dvb_attach(mt352_attach, + &m920x_mt352_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) + return -EIO; + + return 0; +} + +static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n",__func__); + + adap->fe_adap[0].fe = dvb_attach(tda10046_attach, + &m920x_tda10046_08_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) + return -EIO; + + return 0; +} + +static int m920x_tda10046_0b_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n",__func__); + + adap->fe_adap[0].fe = dvb_attach(tda10046_attach, + &m920x_tda10046_0b_config, + &adap->dev->i2c_adap); + if ((adap->fe_adap[0].fe) == NULL) + return -EIO; + + return 0; +} + +static int m920x_qt1010_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n",__func__); + + if (dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL) + return -ENODEV; + + return 0; +} + +static int m920x_tda8275_60_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n",__func__); + + if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL) + return -ENODEV; + + return 0; +} + +static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n",__func__); + + if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL) + return -ENODEV; + + return 0; +} + +static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216ME_MK3); + return 0; +} + +/* device-specific initialization */ +static struct m920x_inits megasky_rc_init [] = { + { M9206_RC_INIT2, 0xa8 }, + { M9206_RC_INIT1, 0x51 }, + { } /* terminating entry */ +}; + +static struct m920x_inits tvwalkertwin_rc_init [] = { + { M9206_RC_INIT2, 0x00 }, + { M9206_RC_INIT1, 0xef }, + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff21, 0x30 }, + { } /* terminating entry */ +}; + +static struct m920x_inits pinnacle310e_init[] = { + /* without these the tuner don't work */ + { 0xff20, 0x9b }, + { 0xff22, 0x70 }, + + /* rc settings */ + { 0xff50, 0x80 }, + { M9206_RC_INIT1, 0x00 }, + { M9206_RC_INIT2, 0xff }, + { } /* terminating entry */ +}; + +/* ir keymaps */ +static struct rc_map_table rc_map_megasky_table[] = { + { 0x0012, KEY_POWER }, + { 0x001e, KEY_CYCLEWINDOWS }, /* min/max */ + { 0x0002, KEY_CHANNELUP }, + { 0x0005, KEY_CHANNELDOWN }, + { 0x0003, KEY_VOLUMEUP }, + { 0x0006, KEY_VOLUMEDOWN }, + { 0x0004, KEY_MUTE }, + { 0x0007, KEY_OK }, /* TS */ + { 0x0008, KEY_STOP }, + { 0x0009, KEY_MENU }, /* swap */ + { 0x000a, KEY_REWIND }, + { 0x001b, KEY_PAUSE }, + { 0x001f, KEY_FASTFORWARD }, + { 0x000c, KEY_RECORD }, + { 0x000d, KEY_CAMERA }, /* screenshot */ + { 0x000e, KEY_COFFEE }, /* "MTS" */ +}; + +static struct rc_map_table rc_map_tvwalkertwin_table[] = { + { 0x0001, KEY_ZOOM }, /* Full Screen */ + { 0x0002, KEY_CAMERA }, /* snapshot */ + { 0x0003, KEY_MUTE }, + { 0x0004, KEY_REWIND }, + { 0x0005, KEY_PLAYPAUSE }, /* Play/Pause */ + { 0x0006, KEY_FASTFORWARD }, + { 0x0007, KEY_RECORD }, + { 0x0008, KEY_STOP }, + { 0x0009, KEY_TIME }, /* Timeshift */ + { 0x000c, KEY_COFFEE }, /* Recall */ + { 0x000e, KEY_CHANNELUP }, + { 0x0012, KEY_POWER }, + { 0x0015, KEY_MENU }, /* source */ + { 0x0018, KEY_CYCLEWINDOWS }, /* TWIN PIP */ + { 0x001a, KEY_CHANNELDOWN }, + { 0x001b, KEY_VOLUMEDOWN }, + { 0x001e, KEY_VOLUMEUP }, +}; + +static struct rc_map_table rc_map_pinnacle310e_table[] = { + { 0x16, KEY_POWER }, + { 0x17, KEY_FAVORITES }, + { 0x0f, KEY_TEXT }, + { 0x48, KEY_PROGRAM }, /* preview */ + { 0x1c, KEY_EPG }, + { 0x04, KEY_LIST }, /* record list */ + { 0x03, KEY_1 }, + { 0x01, KEY_2 }, + { 0x06, KEY_3 }, + { 0x09, KEY_4 }, + { 0x1d, KEY_5 }, + { 0x1f, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x19, KEY_8 }, + { 0x1b, KEY_9 }, + { 0x15, KEY_0 }, + { 0x0c, KEY_CANCEL }, + { 0x4a, KEY_CLEAR }, + { 0x13, KEY_BACK }, + { 0x00, KEY_TAB }, + { 0x4b, KEY_UP }, + { 0x4e, KEY_LEFT }, + { 0x52, KEY_RIGHT }, + { 0x51, KEY_DOWN }, + { 0x4f, KEY_ENTER }, /* could also be KEY_OK */ + { 0x1e, KEY_VOLUMEUP }, + { 0x0a, KEY_VOLUMEDOWN }, + { 0x05, KEY_CHANNELUP }, + { 0x02, KEY_CHANNELDOWN }, + { 0x11, KEY_RECORD }, + { 0x14, KEY_PLAY }, + { 0x4c, KEY_PAUSE }, + { 0x1a, KEY_STOP }, + { 0x40, KEY_REWIND }, + { 0x12, KEY_FASTFORWARD }, + { 0x41, KEY_PREVIOUSSONG }, /* Replay */ + { 0x42, KEY_NEXTSONG }, /* Skip */ + { 0x54, KEY_CAMERA }, /* Capture */ +/* { 0x50, KEY_SAP }, */ /* Sap */ + { 0x47, KEY_CYCLEWINDOWS }, /* Pip */ + { 0x4d, KEY_SCREEN }, /* FullScreen */ + { 0x08, KEY_SUBTITLE }, + { 0x0e, KEY_MUTE }, +/* { 0x49, KEY_LR }, */ /* L/R */ + { 0x07, KEY_SLEEP }, /* Hibernate */ + { 0x08, KEY_VIDEO }, /* A/V */ + { 0x0e, KEY_MENU }, /* Recall */ + { 0x45, KEY_ZOOMIN }, + { 0x46, KEY_ZOOMOUT }, + { 0x18, KEY_RED }, /* Red */ + { 0x53, KEY_GREEN }, /* Green */ + { 0x5e, KEY_YELLOW }, /* Yellow */ + { 0x5f, KEY_BLUE }, /* Blue */ +}; + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties megasky_properties; +static struct dvb_usb_device_properties digivox_mini_ii_properties; +static struct dvb_usb_device_properties tvwalkertwin_properties; +static struct dvb_usb_device_properties dposh_properties; +static struct dvb_usb_device_properties pinnacle_pctv310e_properties; + +static int m920x_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d = NULL; + int ret; + struct m920x_inits *rc_init_seq = NULL; + int bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber; + + deb("Probing for m920x device at interface %d\n", bInterfaceNumber); + + if (bInterfaceNumber == 0) { + /* Single-tuner device, or first interface on + * multi-tuner device + */ + + ret = dvb_usb_device_init(intf, &megasky_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + rc_init_seq = megasky_rc_init; + goto found; + } + + ret = dvb_usb_device_init(intf, &digivox_mini_ii_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + /* No remote control, so no rc_init_seq */ + goto found; + } + + /* This configures both tuners on the TV Walker Twin */ + ret = dvb_usb_device_init(intf, &tvwalkertwin_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + rc_init_seq = tvwalkertwin_rc_init; + goto found; + } + + ret = dvb_usb_device_init(intf, &dposh_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + /* Remote controller not supported yet. */ + goto found; + } + + ret = dvb_usb_device_init(intf, &pinnacle_pctv310e_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + rc_init_seq = pinnacle310e_init; + goto found; + } + + return ret; + } else { + /* Another interface on a multi-tuner device */ + + /* The LifeView TV Walker Twin gets here, but struct + * tvwalkertwin_properties already configured both + * tuners, so there is nothing for us to do here + */ + } + + found: + if ((ret = m920x_init_ep(intf)) < 0) + return ret; + + if (d && (ret = m920x_init(d, rc_init_seq)) != 0) + return ret; + + return ret; +} + +static struct usb_device_id m920x_table [] = { + { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) }, + { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, + USB_PID_MSI_DIGI_VOX_MINI_II) }, + { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, + USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD) }, + { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, + USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM) }, + { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_COLD) }, + { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_WARM) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_PINNACLE_PCTV310E) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, m920x_table); + +static struct dvb_usb_device_properties megasky_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-megasky-02.fw", + .download_firmware = m920x_firmware_download, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_megasky_table, + .rc_map_size = ARRAY_SIZE(rc_map_megasky_table), + .rc_query = m920x_rc_query, + }, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_mt352_frontend_attach, + .tuner_attach = m920x_qt1010_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + }}, + }}, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "MSI Mega Sky 580 DVB-T USB2.0", + { &m920x_table[0], NULL }, + { NULL }, + } + } +}; + +static struct dvb_usb_device_properties digivox_mini_ii_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-digivox-02.fw", + .download_firmware = m920x_firmware_download, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_tda10046_08_frontend_attach, + .tuner_attach = m920x_tda8275_60_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 0x4000, + } + } + }, + }}, + }}, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "MSI DIGI VOX mini II DVB-T USB2.0", + { &m920x_table[1], NULL }, + { NULL }, + }, + } +}; + +/* LifeView TV Walker Twin support by Nick Andrew + * + * LifeView TV Walker Twin has 1 x M9206, 2 x TDA10046, 2 x TDA8275A + * TDA10046 #0 is located at i2c address 0x08 + * TDA10046 #1 is located at i2c address 0x0b + * TDA8275A #0 is located at i2c address 0x60 + * TDA8275A #1 is located at i2c address 0x61 + */ +static struct dvb_usb_device_properties tvwalkertwin_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-tvwalkert.fw", + .download_firmware = m920x_firmware_download, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_tvwalkertwin_table, + .rc_map_size = ARRAY_SIZE(rc_map_tvwalkertwin_table), + .rc_query = m920x_rc_query, + }, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 2, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_tda10046_08_frontend_attach, + .tuner_attach = m920x_tda8275_60_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }}, + }},{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_tda10046_0b_frontend_attach, + .tuner_attach = m920x_tda8275_61_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 512, + } + } + }}, + }, + }}, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { .name = "LifeView TV Walker Twin DVB-T USB2.0", + .cold_ids = { &m920x_table[2], NULL }, + .warm_ids = { &m920x_table[3], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties dposh_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-dposh-01.fw", + .download_firmware = m920x_firmware_download, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + /* Hardware pid filters don't work with this device/firmware */ + + .frontend_attach = m920x_mt352_frontend_attach, + .tuner_attach = m920x_qt1010_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + }}, + }}, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { .name = "Dposh DVB-T USB2.0", + .cold_ids = { &m920x_table[4], NULL }, + .warm_ids = { &m920x_table[5], NULL }, + }, + } +}; + +static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = NULL, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_pinnacle310e_table, + .rc_map_size = ARRAY_SIZE(rc_map_pinnacle310e_table), + .rc_query = m920x_rc_query, + }, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_mt352_frontend_attach, + .tuner_attach = m920x_fmd1216me_tuner_attach, + + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x84, + .u = { + .isoc = { + .framesperurb = 128, + .framesize = 564, + .interval = 1, + } + } + }, + }}, + } }, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "Pinnacle PCTV 310e", + { &m920x_table[6], NULL }, + { NULL }, + } + } +}; + +static struct usb_driver m920x_driver = { + .name = "dvb_usb_m920x", + .probe = m920x_probe, + .disconnect = dvb_usb_device_exit, + .id_table = m920x_table, +}; + +module_usb_driver(m920x_driver); + +MODULE_AUTHOR("Aapo Tahkola "); +MODULE_DESCRIPTION("DVB Driver for ULI M920x"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + */ diff --git a/drivers/media/usb/dvb-usb/m920x.h b/drivers/media/usb/dvb-usb/m920x.h new file mode 100644 index 000000000000..3c061518ffc1 --- /dev/null +++ b/drivers/media/usb/dvb-usb/m920x.h @@ -0,0 +1,77 @@ +#ifndef _DVB_USB_M920X_H_ +#define _DVB_USB_M920X_H_ + +#define DVB_USB_LOG_PREFIX "m920x" +#include "dvb-usb.h" + +#define deb(args...) dprintk(dvb_usb_m920x_debug,0x01,args) + +#define M9206_CORE 0x22 +#define M9206_RC_STATE 0xff51 +#define M9206_RC_KEY 0xff52 +#define M9206_RC_INIT1 0xff54 +#define M9206_RC_INIT2 0xff55 +#define M9206_FW_GO 0xff69 + +#define M9206_I2C 0x23 +#define M9206_FILTER 0x25 +#define M9206_FW 0x30 + +#define M9206_MAX_FILTERS 8 +#define M9206_MAX_ADAPTERS 4 + +/* +sequences found in logs: +[index value] +0x80 write addr +(0x00 out byte)* +0x40 out byte + +0x80 write addr +(0x00 out byte)* +0x80 read addr +(0x21 in byte)* +0x60 in byte + +this sequence works: +0x80 read addr +(0x21 in byte)* +0x60 in byte + +Guess at API of the I2C function: +I2C operation is done one byte at a time with USB control messages. The +index the messages is sent to is made up of a set of flags that control +the I2C bus state: +0x80: Send START condition. After a START condition, one would normally + always send the 7-bit slave I2C address as the 7 MSB, followed by + the read/write bit as the LSB. +0x40: Send STOP condition. This should be set on the last byte of an + I2C transaction. +0x20: Read a byte from the slave. As opposed to writing a byte to the + slave. The slave will normally not produce any data unless you + set the R/W bit to 1 when sending the slave's address after the + START condition. +0x01: Respond with ACK, as opposed to a NACK. For a multi-byte read, + the master should send an ACK, that is pull SDA low during the 9th + clock cycle, after every byte but the last. This flags only makes + sense when bit 0x20 is set, indicating a read. + +What any other bits might mean, or how to get the slave's ACK/NACK +response to a write, is unknown. +*/ + +struct m920x_state { + u16 filters[M9206_MAX_ADAPTERS][M9206_MAX_FILTERS]; + int filtering_enabled[M9206_MAX_ADAPTERS]; + int rep_count; +}; + +/* Initialisation data for the m920x + */ + +struct m920x_inits { + u16 address; + u8 data; +}; + +#endif diff --git a/drivers/media/usb/dvb-usb/nova-t-usb2.c b/drivers/media/usb/dvb-usb/nova-t-usb2.c new file mode 100644 index 000000000000..6c55384e2fca --- /dev/null +++ b/drivers/media/usb/dvb-usb/nova-t-usb2.c @@ -0,0 +1,233 @@ +/* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2 + * DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc,2=eeprom (|-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define deb_rc(args...) dprintk(debug,0x01,args) +#define deb_ee(args...) dprintk(debug,0x02,args) + +/* Hauppauge NOVA-T USB2 keys */ +static struct rc_map_table rc_map_haupp_table[] = { + { 0x1e00, KEY_0 }, + { 0x1e01, KEY_1 }, + { 0x1e02, KEY_2 }, + { 0x1e03, KEY_3 }, + { 0x1e04, KEY_4 }, + { 0x1e05, KEY_5 }, + { 0x1e06, KEY_6 }, + { 0x1e07, KEY_7 }, + { 0x1e08, KEY_8 }, + { 0x1e09, KEY_9 }, + { 0x1e0a, KEY_KPASTERISK }, + { 0x1e0b, KEY_RED }, + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_GRAVE }, /* # */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_CHANNEL }, + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, + { 0x1e19, KEY_AUDIO }, + { 0x1e1a, KEY_IMAGES }, + { 0x1e1b, KEY_EPG }, + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXT }, + { 0x1e1f, KEY_BACK }, + { 0x1e20, KEY_CHANNELUP }, + { 0x1e21, KEY_CHANNELDOWN }, + { 0x1e24, KEY_LAST }, /* Skip backwards */ + { 0x1e25, KEY_OK }, + { 0x1e29, KEY_BLUE}, + { 0x1e2e, KEY_GREEN }, + { 0x1e30, KEY_PAUSE }, + { 0x1e32, KEY_REWIND }, + { 0x1e34, KEY_FASTFORWARD }, + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, + { 0x1e38, KEY_YELLOW }, + { 0x1e3b, KEY_GOTO }, + { 0x1e3d, KEY_POWER }, +}; + +/* Firmware bug? sometimes, when a new key is pressed, the previous pressed key + * is delivered. No workaround yet, maybe a new firmware. + */ +static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5],cmd[2] = { DIBUSB_REQ_POLL_REMOTE, 0x35 }, data,toggle,custom; + u16 raw; + int i; + struct dibusb_device_state *st = d->priv; + + dvb_usb_generic_rw(d,cmd,2,key,5,0); + + *state = REMOTE_NO_KEY_PRESSED; + switch (key[0]) { + case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED: + raw = ((key[1] << 8) | key[2]) >> 3; + toggle = !!(raw & 0x800); + data = raw & 0x3f; + custom = (raw >> 6) & 0x1f; + + deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle); + + for (i = 0; i < ARRAY_SIZE(rc_map_haupp_table); i++) { + if (rc5_data(&rc_map_haupp_table[i]) == data && + rc5_custom(&rc_map_haupp_table[i]) == custom) { + + deb_rc("c: %x, d: %x\n", rc5_data(&rc_map_haupp_table[i]), + rc5_custom(&rc_map_haupp_table[i])); + + *event = rc_map_haupp_table[i].keycode; + *state = REMOTE_KEY_PRESSED; + if (st->old_toggle == toggle) { + if (st->last_repeat_count++ < 2) + *state = REMOTE_NO_KEY_PRESSED; + } else { + st->last_repeat_count = 0; + st->old_toggle = toggle; + } + break; + } + } + + break; + case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY: + default: + break; + } + + return 0; +} + +static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6]) +{ + int i; + u8 b; + + mac[0] = 0x00; + mac[1] = 0x0d; + mac[2] = 0xfe; + + /* this is a complete guess, but works for my box */ + for (i = 136; i < 139; i++) { + dibusb_read_eeprom_byte(d,i, &b); + + mac[5 - (i - 136)] = b; + } + + return 0; +} + +/* USB Driver stuff */ +static struct dvb_usb_device_properties nova_t_properties; + +static int nova_t_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &nova_t_properties, + THIS_MODULE, NULL, adapter_nr); +} + +/* do not change the order of the ID table */ +static struct usb_device_id nova_t_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, nova_t_table); + +static struct dvb_usb_device_properties nova_t_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-nova-t-usb2-02.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + } + }, + .size_of_priv = sizeof(struct dibusb_device_state), + + .power_ctrl = dibusb2_0_power_ctrl, + .read_mac_address = nova_t_read_mac_address, + + .rc.legacy = { + .rc_interval = 100, + .rc_map_table = rc_map_haupp_table, + .rc_map_size = ARRAY_SIZE(rc_map_haupp_table), + .rc_query = nova_t_rc_query, + }, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Hauppauge WinTV-NOVA-T usb2", + { &nova_t_table[0], NULL }, + { &nova_t_table[1], NULL }, + }, + { NULL }, + } +}; + +static struct usb_driver nova_t_driver = { + .name = "dvb_usb_nova_t_usb2", + .probe = nova_t_probe, + .disconnect = dvb_usb_device_exit, + .id_table = nova_t_table, +}; + +module_usb_driver(nova_t_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c new file mode 100644 index 000000000000..c8a95042dfbc --- /dev/null +++ b/drivers/media/usb/dvb-usb/opera1.c @@ -0,0 +1,583 @@ +/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card +* +* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org) +* Copyright (C) 2006 Marco Gittler (g.marco@freenet.de) +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the Free +* Software Foundation, version 2. +* +* see Documentation/dvb/README.dvb-usb for more information +*/ + +#define DVB_USB_LOG_PREFIX "opera" + +#include "dvb-usb.h" +#include "stv0299.h" + +#define OPERA_READ_MSG 0 +#define OPERA_WRITE_MSG 1 +#define OPERA_I2C_TUNER 0xd1 + +#define READ_FX2_REG_REQ 0xba +#define READ_MAC_ADDR 0x08 +#define OPERA_WRITE_FX2 0xbb +#define OPERA_TUNER_REQ 0xb1 +#define REG_1F_SYMBOLRATE_BYTE0 0x1f +#define REG_20_SYMBOLRATE_BYTE1 0x20 +#define REG_21_SYMBOLRATE_BYTE2 0x21 + +#define ADDR_B600_VOLTAGE_13V (0x02) +#define ADDR_B601_VOLTAGE_18V (0x03) +#define ADDR_B1A6_STREAM_CTRL (0x04) +#define ADDR_B880_READ_REMOTE (0x05) + +struct opera1_state { + u32 last_key_pressed; +}; +struct rc_map_opera_table { + u32 keycode; + u32 event; +}; + +static int dvb_usb_opera1_debug; +module_param_named(debug, dvb_usb_opera1_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))." + DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + + +static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value, + u8 * data, u16 len, int flags) +{ + int ret; + u8 tmp; + u8 *buf; + unsigned int pipe = (flags == OPERA_READ_MSG) ? + usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0); + u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (flags == OPERA_WRITE_MSG) + memcpy(buf, data, len); + ret = usb_control_msg(dev, pipe, request, + request_type | USB_TYPE_VENDOR, value, 0x0, + buf, len, 2000); + + if (request == OPERA_TUNER_REQ) { + tmp = buf[0]; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR, + 0x01, 0x0, buf, 1, 2000) < 1 || buf[0] != 0x08) { + ret = 0; + goto out; + } + buf[0] = tmp; + } + if (flags == OPERA_READ_MSG) + memcpy(data, buf, len); +out: + kfree(buf); + return ret; +} + +/* I2C */ + +static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr, + u8 * buf, u16 len) +{ + int ret = 0; + u8 request; + u16 value; + + if (!dev) { + info("no usb_device"); + return -EINVAL; + } + if (mutex_lock_interruptible(&dev->usb_mutex) < 0) + return -EAGAIN; + + switch (addr>>1){ + case ADDR_B600_VOLTAGE_13V: + request=0xb6; + value=0x00; + break; + case ADDR_B601_VOLTAGE_18V: + request=0xb6; + value=0x01; + break; + case ADDR_B1A6_STREAM_CTRL: + request=0xb1; + value=0xa6; + break; + case ADDR_B880_READ_REMOTE: + request=0xb8; + value=0x80; + break; + default: + request=0xb1; + value=addr; + } + ret = opera1_xilinx_rw(dev->udev, request, + value, buf, len, + addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG); + + mutex_unlock(&dev->usb_mutex); + return ret; +} + +static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0, tmp = 0; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + if ((tmp = opera1_usb_i2c_msgxfer(d, + (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), + msg[i].buf, + msg[i].len + )) != msg[i].len) { + break; + } + if (dvb_usb_opera1_debug & 0x10) + info("sending i2c mesage %d %d", tmp, msg[i].len); + } + mutex_unlock(&d->i2c_mutex); + return num; +} + +static u32 opera1_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm opera1_i2c_algo = { + .master_xfer = opera1_i2c_xfer, + .functionality = opera1_i2c_func, +}; + +static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + static u8 command_13v[1]={0x00}; + static u8 command_18v[1]={0x01}; + struct i2c_msg msg[] = { + {.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1}, + }; + struct dvb_usb_adapter *udev_adap = + (struct dvb_usb_adapter *)(fe->dvb->priv); + if (voltage == SEC_VOLTAGE_18) { + msg[0].addr = ADDR_B601_VOLTAGE_18V; + msg[0].buf = command_18v; + } + i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, + u32 ratio) +{ + stv0299_writereg(fe, 0x13, 0x98); + stv0299_writereg(fe, 0x14, 0x95); + stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff); + stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff); + stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0); + return 0; + +} +static u8 opera1_inittab[] = { + 0x00, 0xa1, + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, + 0x05, 0x05, + 0x06, 0x02, + 0x07, 0x00, + 0x0b, 0x00, + 0x0c, 0x01, + 0x0d, 0x81, + 0x0e, 0x44, + 0x0f, 0x19, + 0x10, 0x3f, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x98, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0xeb, + 0x17, 0x00, + 0x18, 0x19, + 0x19, 0x8b, + 0x1a, 0x00, + 0x1b, 0x82, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + REG_1F_SYMBOLRATE_BYTE0, 0x06, + REG_20_SYMBOLRATE_BYTE1, 0x50, + REG_21_SYMBOLRATE_BYTE2, 0x10, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x37, + 0x25, 0xbc, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x00, + 0x29, 0x1e, + 0x2a, 0x14, + 0x2b, 0x1f, + 0x2c, 0x09, + 0x2d, 0x0a, + 0x2e, 0x00, + 0x2f, 0x00, + 0x30, 0x00, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x13, + 0xff, 0xff, +}; + +static struct stv0299_config opera1_stv0299_config = { + .demod_address = 0xd0>>1, + .min_delay_ms = 100, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .inittab = opera1_inittab, + .set_symbol_rate = opera1_stv0299_set_symbol_rate, +}; + +static int opera1_frontend_attach(struct dvb_usb_adapter *d) +{ + d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config, + &d->dev->i2c_adap); + if ((d->fe_adap[0].fe) != NULL) { + d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage; + return 0; + } + info("not attached stv0299"); + return -EIO; +} + +static int opera1_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach( + dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1, + &adap->dev->i2c_adap, DVB_PLL_OPERA1 + ); + return 0; +} + +static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 val = onoff ? 0x01 : 0x00; + + if (dvb_usb_opera1_debug) + info("power %s", onoff ? "on" : "off"); + return opera1_xilinx_rw(d->udev, 0xb7, val, + &val, 1, OPERA_WRITE_MSG); +} + +static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + static u8 buf_start[2] = { 0xff, 0x03 }; + static u8 buf_stop[2] = { 0xff, 0x00 }; + struct i2c_msg start_tuner[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2}, + }; + if (dvb_usb_opera1_debug) + info("streaming %s", onoff ? "on" : "off"); + i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1); + return 0; +} + +static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + u8 b_pid[3]; + struct i2c_msg msg[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, + }; + if (dvb_usb_opera1_debug) + info("pidfilter index: %d pid: %d %s", index, pid, + onoff ? "on" : "off"); + b_pid[0] = (2 * index) + 4; + b_pid[1] = onoff ? (pid & 0xff) : (0x00); + b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00); + i2c_transfer(&adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) +{ + int u = 0x04; + u8 b_pid[3]; + struct i2c_msg msg[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, + }; + if (dvb_usb_opera1_debug) + info("%s hw-pidfilter", onoff ? "enable" : "disable"); + for (; u < 0x7e; u += 2) { + b_pid[0] = u; + b_pid[1] = 0; + b_pid[2] = 0x80; + i2c_transfer(&adap->dev->i2c_adap, msg, 1); + } + return 0; +} + +static struct rc_map_table rc_map_opera1_table[] = { + {0x5fa0, KEY_1}, + {0x51af, KEY_2}, + {0x5da2, KEY_3}, + {0x41be, KEY_4}, + {0x0bf5, KEY_5}, + {0x43bd, KEY_6}, + {0x47b8, KEY_7}, + {0x49b6, KEY_8}, + {0x05fa, KEY_9}, + {0x45ba, KEY_0}, + {0x09f6, KEY_CHANNELUP}, /*chanup */ + {0x1be5, KEY_CHANNELDOWN}, /*chandown */ + {0x5da3, KEY_VOLUMEDOWN}, /*voldown */ + {0x5fa1, KEY_VOLUMEUP}, /*volup */ + {0x07f8, KEY_SPACE}, /*tab */ + {0x1fe1, KEY_OK}, /*play ok */ + {0x1be4, KEY_ZOOM}, /*zoom */ + {0x59a6, KEY_MUTE}, /*mute */ + {0x5ba5, KEY_RADIO}, /*tv/f */ + {0x19e7, KEY_RECORD}, /*rec */ + {0x01fe, KEY_STOP}, /*Stop */ + {0x03fd, KEY_PAUSE}, /*pause */ + {0x03fc, KEY_SCREEN}, /*<- -> */ + {0x07f9, KEY_CAMERA}, /*capture */ + {0x47b9, KEY_ESC}, /*exit */ + {0x43bc, KEY_POWER2}, /*power */ +}; + +static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) +{ + struct opera1_state *opst = dev->priv; + u8 rcbuffer[32]; + const u16 startmarker1 = 0x10ed; + const u16 startmarker2 = 0x11ec; + struct i2c_msg read_remote[] = { + {.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32}, + }; + int i = 0; + u32 send_key = 0; + + if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) { + for (i = 0; i < 32; i++) { + if (rcbuffer[i]) + send_key |= 1; + if (i < 31) + send_key = send_key << 1; + } + if (send_key & 0x8000) + send_key = (send_key << 1) | (send_key >> 15 & 0x01); + + if (send_key == 0xffff && opst->last_key_pressed != 0) { + *state = REMOTE_KEY_REPEAT; + *event = opst->last_key_pressed; + return 0; + } + for (; send_key != 0;) { + if (send_key >> 16 == startmarker2) { + break; + } else if (send_key >> 16 == startmarker1) { + send_key = + (send_key & 0xfffeffff) | (startmarker1 << 16); + break; + } else + send_key >>= 1; + } + + if (send_key == 0) + return 0; + + send_key = (send_key & 0xffff) | 0x0100; + + for (i = 0; i < ARRAY_SIZE(rc_map_opera1_table); i++) { + if (rc5_scan(&rc_map_opera1_table[i]) == (send_key & 0xffff)) { + *state = REMOTE_KEY_PRESSED; + *event = rc_map_opera1_table[i].keycode; + opst->last_key_pressed = + rc_map_opera1_table[i].keycode; + break; + } + opst->last_key_pressed = 0; + } + } else + *state = REMOTE_NO_KEY_PRESSED; + return 0; +} + +static struct usb_device_id opera1_table[] = { + {USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)}, + {USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, opera1_table); + +static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + u8 command[] = { READ_MAC_ADDR }; + opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG); + opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG); + return 0; +} +static int opera1_xilinx_load_firmware(struct usb_device *dev, + const char *filename) +{ + const struct firmware *fw = NULL; + u8 *b, *p; + int ret = 0, i,fpgasize=40; + u8 testval; + info("start downloading fpga firmware %s",filename); + + if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems.", + filename); + return ret; + } else { + p = kmalloc(fw->size, GFP_KERNEL); + opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG); + if (p != NULL && testval != 0x67) { + + u8 reset = 0, fpga_command = 0; + memcpy(p, fw->data, fw->size); + /* clear fpga ? */ + opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1, + OPERA_WRITE_MSG); + for (i = 0; i < fw->size;) { + if ( (fw->size - i) size-i; + } + b = (u8 *) p + i; + if (opera1_xilinx_rw + (dev, OPERA_WRITE_FX2, 0x0, b , fpgasize, + OPERA_WRITE_MSG) != fpgasize + ) { + err("error while transferring firmware"); + ret = -EINVAL; + break; + } + i = i + fpgasize; + } + /* restart the CPU */ + if (ret || opera1_xilinx_rw + (dev, 0xa0, 0xe600, &reset, 1, + OPERA_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + } + } + kfree(p); + release_firmware(fw); + return ret; +} + +static struct dvb_usb_device_properties opera1_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-opera-01.fw", + .size_of_priv = sizeof(struct opera1_state), + + .power_ctrl = opera1_power_ctrl, + .i2c_algo = &opera1_i2c_algo, + + .rc.legacy = { + .rc_map_table = rc_map_opera1_table, + .rc_map_size = ARRAY_SIZE(rc_map_opera1_table), + .rc_interval = 200, + .rc_query = opera1_rc_query, + }, + .read_mac_address = opera1_read_mac_address, + .generic_bulk_ctrl_endpoint = 0x00, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = opera1_frontend_attach, + .streaming_ctrl = opera1_streaming_ctrl, + .tuner_attach = opera1_tuner_attach, + .caps = + DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter = opera1_pid_filter, + .pid_filter_ctrl = opera1_pid_filter_control, + .pid_filter_count = 252, + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .num_device_descs = 1, + .devices = { + {"Opera1 DVB-S USB2.0", + {&opera1_table[0], NULL}, + {&opera1_table[1], NULL}, + }, + } +}; + +static int opera1_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + + if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM && + udev->descriptor.idVendor == USB_VID_OPERA1 && + opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0 + ) { + return -EINVAL; + } + + if (0 != dvb_usb_device_init(intf, &opera1_properties, + THIS_MODULE, NULL, adapter_nr)) + return -EINVAL; + return 0; +} + +static struct usb_driver opera1_driver = { + .name = "opera1", + .probe = opera1_probe, + .disconnect = dvb_usb_device_exit, + .id_table = opera1_table, +}; + +module_usb_driver(opera1_driver); + +MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org"); +MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de"); +MODULE_DESCRIPTION("Driver for Opera1 DVB-S device"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c new file mode 100644 index 000000000000..02e878577c3d --- /dev/null +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -0,0 +1,1063 @@ +/* + * PCTV 452e DVB driver + * + * Copyright (c) 2006-2008 Dominik Kuhlen + * + * TT connect S2-3650-CI Common Interface support, MAC readout + * Copyright (C) 2008 Michael H. Schimek + * + * This program is free software; 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. + */ + +/* dvb usb framework */ +#define DVB_USB_LOG_PREFIX "pctv452e" +#include "dvb-usb.h" + +/* Demodulator */ +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +/* Tuner */ +#include "stb6100.h" +#include "stb6100_cfg.h" +/* FE Power */ +#include "lnbp22.h" + +#include "dvb_ca_en50221.h" +#include "ttpci-eeprom.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define ISOC_INTERFACE_ALTERNATIVE 3 + +#define SYNC_BYTE_OUT 0xaa +#define SYNC_BYTE_IN 0x55 + +/* guessed: (copied from ttusb-budget) */ +#define PCTV_CMD_RESET 0x15 +/* command to poll IR receiver */ +#define PCTV_CMD_IR 0x1b +/* command to send I2C */ +#define PCTV_CMD_I2C 0x31 + +#define I2C_ADDR_STB0899 (0xd0 >> 1) +#define I2C_ADDR_STB6100 (0xc0 >> 1) +#define I2C_ADDR_LNBP22 (0x10 >> 1) +#define I2C_ADDR_24C16 (0xa0 >> 1) +#define I2C_ADDR_24C64 (0xa2 >> 1) + + +/* pctv452e sends us this amount of data for each issued usb-command */ +#define PCTV_ANSWER_LEN 64 +/* Wait up to 1000ms for device */ +#define PCTV_TIMEOUT 1000 + + +#define PCTV_LED_GPIO STB0899_GPIO01 +#define PCTV_LED_GREEN 0x82 +#define PCTV_LED_ORANGE 0x02 + +#define ci_dbg(format, arg...) \ +do { \ + if (0) \ + printk(KERN_DEBUG DVB_USB_LOG_PREFIX \ + ": " format "\n" , ## arg); \ +} while (0) + +enum { + TT3650_CMD_CI_TEST = 0x40, + TT3650_CMD_CI_RD_CTRL, + TT3650_CMD_CI_WR_CTRL, + TT3650_CMD_CI_RD_ATTR, + TT3650_CMD_CI_WR_ATTR, + TT3650_CMD_CI_RESET, + TT3650_CMD_CI_SET_VIDEO_PORT +}; + + +static struct stb0899_postproc pctv45e_postproc[] = { + { PCTV_LED_GPIO, STB0899_GPIOPULLUP }, + { 0, 0 } +}; + +/* + * stores all private variables for communication with the PCTV452e DVB-S2 + */ +struct pctv452e_state { + struct dvb_ca_en50221 ca; + struct mutex ca_mutex; + + u8 c; /* transaction counter, wraps around... */ + u8 initialized; /* set to 1 if 0x15 has been sent */ + u16 last_rc_key; +}; + +static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, + unsigned int write_len, unsigned int read_len) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 buf[64]; + u8 id; + unsigned int rlen; + int ret; + + BUG_ON(NULL == data && 0 != (write_len | read_len)); + BUG_ON(write_len > 64 - 4); + BUG_ON(read_len > 64 - 4); + + id = state->c++; + + buf[0] = SYNC_BYTE_OUT; + buf[1] = id; + buf[2] = cmd; + buf[3] = write_len; + + memcpy(buf + 4, data, write_len); + + rlen = (read_len > 0) ? 64 : 0; + ret = dvb_usb_generic_rw(d, buf, 4 + write_len, + buf, rlen, /* delay_ms */ 0); + if (0 != ret) + goto failed; + + ret = -EIO; + if (SYNC_BYTE_IN != buf[0] || id != buf[1]) + goto failed; + + memcpy(data, buf + 4, read_len); + + return 0; + +failed: + err("CI error %d; %02X %02X %02X -> %*ph.", + ret, SYNC_BYTE_OUT, id, cmd, 3, buf); + + return ret; +} + +static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, + u8 cmd, u8 *data, unsigned int write_len, + unsigned int read_len) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + int ret; + + mutex_lock(&state->ca_mutex); + ret = tt3650_ci_msg(d, cmd, data, write_len, read_len); + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + u8 buf[3]; + int ret; + + if (0 != slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3); + + ci_dbg("%s %04x -> %d 0x%02x", + __func__, address, ret, buf[2]); + + if (ret < 0) + return ret; + + return buf[2]; +} + +static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address, u8 value) +{ + u8 buf[3]; + + ci_dbg("%s %d 0x%04x 0x%02x", + __func__, slot, address, value); + + if (0 != slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + buf[2] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3); +} + +static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address) +{ + u8 buf[2]; + int ret; + + if (0 != slot) + return -EINVAL; + + buf[0] = address & 3; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2); + + ci_dbg("%s 0x%02x -> %d 0x%02x", + __func__, address, ret, buf[1]); + + if (ret < 0) + return ret; + + return buf[1]; +} + +static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address, + u8 value) +{ + u8 buf[2]; + + ci_dbg("%s %d 0x%02x 0x%02x", + __func__, slot, address, value); + + if (0 != slot) + return -EINVAL; + + buf[0] = address; + buf[1] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2); +} + +static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, + int slot, + int enable) +{ + u8 buf[1]; + int ret; + + ci_dbg("%s %d %d", __func__, slot, enable); + + if (0 != slot) + return -EINVAL; + + enable = !!enable; + buf[0] = enable; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + if (ret < 0) + return ret; + + if (enable != buf[0]) { + err("CI not %sabled.", enable ? "en" : "dis"); + return -EIO; + } + + return 0; +} + +static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, /* enable */ 0); +} + +static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, /* enable */ 1); +} + +static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 buf[1]; + int ret; + + ci_dbg("%s %d", __func__, slot); + + if (0 != slot) + return -EINVAL; + + buf[0] = 0; + + mutex_lock(&state->ca_mutex); + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (0 != ret) + goto failed; + + msleep(500); + + buf[0] = 1; + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (0 != ret) + goto failed; + + msleep(500); + + buf[0] = 0; /* FTA */ + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + + failed: + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, + int slot, + int open) +{ + u8 buf[1]; + int ret; + + if (0 != slot) + return -EINVAL; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1); + if (0 != ret) + return ret; + + if (1 == buf[0]) + return DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + + return 0; + +} + +static void tt3650_ci_uninit(struct dvb_usb_device *d) +{ + struct pctv452e_state *state; + + ci_dbg("%s", __func__); + + if (NULL == d) + return; + + state = (struct pctv452e_state *)d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + /* Error ignored. */ + tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0); + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + +static int tt3650_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + int ret; + + ci_dbg("%s", __func__); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem; + state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem; + state->ca.read_cam_control = tt3650_ci_read_cam_control; + state->ca.write_cam_control = tt3650_ci_write_cam_control; + state->ca.slot_reset = tt3650_ci_slot_reset; + state->ca.slot_shutdown = tt3650_ci_slot_shutdown; + state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable; + state->ca.poll_slot_status = tt3650_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + /* flags */ 0, + /* n_slots */ 1); + if (0 != ret) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + info("CI initialized."); + + return 0; +} + +#define CMD_BUFFER_SIZE 0x28 +static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr, + const u8 *snd_buf, u8 snd_len, + u8 *rcv_buf, u8 rcv_len) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 buf[64]; + u8 id; + int ret; + + id = state->c++; + + ret = -EINVAL; + if (snd_len > 64 - 7 || rcv_len > 64 - 7) + goto failed; + + buf[0] = SYNC_BYTE_OUT; + buf[1] = id; + buf[2] = PCTV_CMD_I2C; + buf[3] = snd_len + 3; + buf[4] = addr << 1; + buf[5] = snd_len; + buf[6] = rcv_len; + + memcpy(buf + 7, snd_buf, snd_len); + + ret = dvb_usb_generic_rw(d, buf, 7 + snd_len, + buf, /* rcv_len */ 64, + /* delay_ms */ 0); + if (ret < 0) + goto failed; + + /* TT USB protocol error. */ + ret = -EIO; + if (SYNC_BYTE_IN != buf[0] || id != buf[1]) + goto failed; + + /* I2C device didn't respond as expected. */ + ret = -EREMOTEIO; + if (buf[5] < snd_len || buf[6] < rcv_len) + goto failed; + + memcpy(rcv_buf, buf + 7, rcv_len); + + return rcv_len; + +failed: + err("I2C error %d; %02X %02X %02X %02X %02X -> " + "%02X %02X %02X %02X %02X.", + ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len, + buf[0], buf[1], buf[4], buf[5], buf[6]); + + return ret; +} + +static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg, + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adapter); + int i; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf; + int ret; + + if (msg[i].flags & I2C_M_RD) { + addr = msg[i].addr; + snd_buf = NULL; + snd_len = 0; + rcv_buf = msg[i].buf; + rcv_len = msg[i].len; + } else { + addr = msg[i].addr; + snd_buf = msg[i].buf; + snd_len = msg[i].len; + rcv_buf = NULL; + rcv_len = 0; + } + + ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf, + rcv_len); + if (ret < rcv_len) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 pctv452e_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 }; + u8 rx[PCTV_ANSWER_LEN]; + int ret; + + info("%s: %d\n", __func__, i); + + if (!i) + return 0; + + if (state->initialized) + return 0; + + /* hmm where shoud this should go? */ + ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE); + if (ret != 0) + info("%s: Warning set interface returned: %d\n", + __func__, ret); + + /* this is a one-time initialization, dont know where to put */ + b0[1] = state->c++; + /* reset board */ + ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0); + if (ret) + return ret; + + b0[1] = state->c++; + b0[4] = 1; + /* reset board (again?) */ + ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0); + if (ret) + return ret; + + state->initialized = 1; + + return 0; +} + +static int pctv452e_rc_query(struct dvb_usb_device *d) +{ + struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 b[CMD_BUFFER_SIZE]; + u8 rx[PCTV_ANSWER_LEN]; + int ret, i; + u8 id = state->c++; + + /* prepare command header */ + b[0] = SYNC_BYTE_OUT; + b[1] = id; + b[2] = PCTV_CMD_IR; + b[3] = 0; + + /* send ir request */ + ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0); + if (ret != 0) + return ret; + + if (debug > 3) { + info("%s: read: %2d: %*ph: ", __func__, ret, 3, rx); + for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++) + info(" %02x", rx[i+3]); + + info("\n"); + } + + if ((rx[3] == 9) && (rx[12] & 0x01)) { + /* got a "press" event */ + state->last_rc_key = (rx[7] << 8) | rx[6]; + if (debug > 2) + info("%s: cmd=0x%02x sys=0x%02x\n", + __func__, rx[6], rx[7]); + + rc_keydown(d->rc_dev, state->last_rc_key, 0); + } else if (state->last_rc_key) { + rc_keyup(d->rc_dev); + state->last_rc_key = 0; + } + + return 0; +} + +static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + const u8 mem_addr[] = { 0x1f, 0xcc }; + u8 encoded_mac[20]; + int ret; + + ret = -EAGAIN; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + goto failed; + + ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16, + mem_addr + 1, /* snd_len */ 1, + encoded_mac, /* rcv_len */ 20); + if (-EREMOTEIO == ret) + /* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a + byte write if /WC is low. */ + ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64, + mem_addr, 2, + encoded_mac, 20); + + mutex_unlock(&d->i2c_mutex); + + if (20 != ret) + goto failed; + + ret = ttpci_eeprom_decode_mac(mac, encoded_mac); + if (0 != ret) + goto failed; + + return 0; + +failed: + memset(mac, 0, 6); + + return ret; +} + +static const struct stb0899_s1_reg pctv452e_init_dev[] = { + { STB0899_DISCNTRL1, 0x26 }, + { STB0899_DISCNTRL2, 0x80 }, + { STB0899_DISRX_ST0, 0x04 }, + { STB0899_DISRX_ST1, 0x20 }, + { STB0899_DISPARITY, 0x00 }, + { STB0899_DISFIFO, 0x00 }, + { STB0899_DISF22, 0x99 }, + { STB0899_DISF22RX, 0x85 }, /* 0xa8 */ + { STB0899_ACRPRESC, 0x11 }, + { STB0899_ACRDIV1, 0x0a }, + { STB0899_ACRDIV2, 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG, 0x00 }, + { STB0899_MODECFG, 0x00 }, /* Inversion */ + { STB0899_IRQMSK_3, 0xf3 }, + { STB0899_IRQMSK_2, 0xfc }, + { STB0899_IRQMSK_1, 0xff }, + { STB0899_IRQMSK_0, 0xff }, + { STB0899_I2CCFG, 0x88 }, + { STB0899_I2CRPT, 0x58 }, + { STB0899_GPIO00CFG, 0x82 }, + { STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */ + { STB0899_GPIO02CFG, 0x82 }, + { STB0899_GPIO03CFG, 0x82 }, + { STB0899_GPIO04CFG, 0x82 }, + { STB0899_GPIO05CFG, 0x82 }, + { STB0899_GPIO06CFG, 0x82 }, + { STB0899_GPIO07CFG, 0x82 }, + { STB0899_GPIO08CFG, 0x82 }, + { STB0899_GPIO09CFG, 0x82 }, + { STB0899_GPIO10CFG, 0x82 }, + { STB0899_GPIO11CFG, 0x82 }, + { STB0899_GPIO12CFG, 0x82 }, + { STB0899_GPIO13CFG, 0x82 }, + { STB0899_GPIO14CFG, 0x82 }, + { STB0899_GPIO15CFG, 0x82 }, + { STB0899_GPIO16CFG, 0x82 }, + { STB0899_GPIO17CFG, 0x82 }, + { STB0899_GPIO18CFG, 0x82 }, + { STB0899_GPIO19CFG, 0x82 }, + { STB0899_GPIO20CFG, 0x82 }, + { STB0899_SDATCFG, 0xb8 }, + { STB0899_SCLTCFG, 0xba }, + { STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */ + { STB0899_GPIO22, 0x82 }, + { STB0899_GPIO21, 0x91 }, + { STB0899_DIRCLKCFG, 0x82 }, + { STB0899_CLKOUT27CFG, 0x7e }, + { STB0899_STDBYCFG, 0x82 }, + { STB0899_CS0CFG, 0x82 }, + { STB0899_CS1CFG, 0x82 }, + { STB0899_DISEQCOCFG, 0x20 }, + { STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */ + { STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */ + { STB0899_FILTCTRL, 0x00 }, + { STB0899_SYSCTRL, 0x00 }, + { STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */ + { STB0899_STOPCLK2, 0x00 }, + { STB0899_INTBUFCTRL, 0x0a }, + { STB0899_AGC2I1, 0x00 }, + { STB0899_AGC2I2, 0x00 }, + { STB0899_AGCIQIN, 0x00 }, + { STB0899_TSTRES, 0x40 }, /* rjkm */ + { 0xffff, 0xff }, +}; + +static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = { + { STB0899_DEMOD, 0x00 }, + { STB0899_RCOMPC, 0xc9 }, + { STB0899_AGC1CN, 0x01 }, + { STB0899_AGC1REF, 0x10 }, + { STB0899_RTC, 0x23 }, + { STB0899_TMGCFG, 0x4e }, + { STB0899_AGC2REF, 0x34 }, + { STB0899_TLSR, 0x84 }, + { STB0899_CFD, 0xf7 }, + { STB0899_ACLC, 0x87 }, + { STB0899_BCLC, 0x94 }, + { STB0899_EQON, 0x41 }, + { STB0899_LDT, 0xf1 }, + { STB0899_LDT2, 0xe3 }, + { STB0899_EQUALREF, 0xb4 }, + { STB0899_TMGRAMP, 0x10 }, + { STB0899_TMGTHD, 0x30 }, + { STB0899_IDCCOMP, 0xfd }, + { STB0899_QDCCOMP, 0xff }, + { STB0899_POWERI, 0x0c }, + { STB0899_POWERQ, 0x0f }, + { STB0899_RCOMP, 0x6c }, + { STB0899_AGCIQIN, 0x80 }, + { STB0899_AGC2I1, 0x06 }, + { STB0899_AGC2I2, 0x00 }, + { STB0899_TLIR, 0x30 }, + { STB0899_RTF, 0x7f }, + { STB0899_DSTATUS, 0x00 }, + { STB0899_LDI, 0xbc }, + { STB0899_CFRM, 0xea }, + { STB0899_CFRL, 0x31 }, + { STB0899_NIRM, 0x2b }, + { STB0899_NIRL, 0x80 }, + { STB0899_ISYMB, 0x1d }, + { STB0899_QSYMB, 0xa6 }, + { STB0899_SFRH, 0x2f }, + { STB0899_SFRM, 0x68 }, + { STB0899_SFRL, 0x40 }, + { STB0899_SFRUPH, 0x2f }, + { STB0899_SFRUPM, 0x68 }, + { STB0899_SFRUPL, 0x40 }, + { STB0899_EQUAI1, 0x02 }, + { STB0899_EQUAQ1, 0xff }, + { STB0899_EQUAI2, 0x04 }, + { STB0899_EQUAQ2, 0x05 }, + { STB0899_EQUAI3, 0x02 }, + { STB0899_EQUAQ3, 0xfd }, + { STB0899_EQUAI4, 0x03 }, + { STB0899_EQUAQ4, 0x07 }, + { STB0899_EQUAI5, 0x08 }, + { STB0899_EQUAQ5, 0xf5 }, + { STB0899_DSTATUS2, 0x00 }, + { STB0899_VSTATUS, 0x00 }, + { STB0899_VERROR, 0x86 }, + { STB0899_IQSWAP, 0x2a }, + { STB0899_ECNT1M, 0x00 }, + { STB0899_ECNT1L, 0x00 }, + { STB0899_ECNT2M, 0x00 }, + { STB0899_ECNT2L, 0x00 }, + { STB0899_ECNT3M, 0x0a }, + { STB0899_ECNT3L, 0xad }, + { STB0899_FECAUTO1, 0x06 }, + { STB0899_FECM, 0x01 }, + { STB0899_VTH12, 0xb0 }, + { STB0899_VTH23, 0x7a }, + { STB0899_VTH34, 0x58 }, + { STB0899_VTH56, 0x38 }, + { STB0899_VTH67, 0x34 }, + { STB0899_VTH78, 0x24 }, + { STB0899_PRVIT, 0xff }, + { STB0899_VITSYNC, 0x19 }, + { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC, 0x42 }, + { STB0899_RSLLC, 0x41 }, + { STB0899_TSLPL, 0x12 }, + { STB0899_TSCFGH, 0x0c }, + { STB0899_TSCFGM, 0x00 }, + { STB0899_TSCFGL, 0x00 }, + { STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL, 0x00 }, + { STB0899_TSINHDELH, 0x02 }, + { STB0899_TSINHDELM, 0x00 }, + { STB0899_TSINHDELL, 0x00 }, + { STB0899_TSLLSTKM, 0x1b }, + { STB0899_TSLLSTKL, 0xb3 }, + { STB0899_TSULSTKM, 0x00 }, + { STB0899_TSULSTKL, 0x00 }, + { STB0899_PCKLENUL, 0xbc }, + { STB0899_PCKLENLL, 0xcc }, + { STB0899_RSPCKLEN, 0xbd }, + { STB0899_TSSTATUS, 0x90 }, + { STB0899_ERRCTRL1, 0xb6 }, + { STB0899_ERRCTRL2, 0x95 }, + { STB0899_ERRCTRL3, 0x8d }, + { STB0899_DMONMSK1, 0x27 }, + { STB0899_DMONMSK0, 0x03 }, + { STB0899_DEMAPVIT, 0x5c }, + { STB0899_PLPARM, 0x19 }, + { STB0899_PDELCTRL, 0x48 }, + { STB0899_PDELCTRL2, 0x00 }, + { STB0899_BBHCTRL1, 0x00 }, + { STB0899_BBHCTRL2, 0x00 }, + { STB0899_HYSTTHRESH, 0x77 }, + { STB0899_MATCSTM, 0x00 }, + { STB0899_MATCSTL, 0x00 }, + { STB0899_UPLCSTM, 0x00 }, + { STB0899_UPLCSTL, 0x00 }, + { STB0899_DFLCSTM, 0x00 }, + { STB0899_DFLCSTL, 0x00 }, + { STB0899_SYNCCST, 0x00 }, + { STB0899_SYNCDCSTM, 0x00 }, + { STB0899_SYNCDCSTL, 0x00 }, + { STB0899_ISI_ENTRY, 0x00 }, + { STB0899_ISI_BIT_EN, 0x00 }, + { STB0899_MATSTRM, 0xf0 }, + { STB0899_MATSTRL, 0x02 }, + { STB0899_UPLSTRM, 0x45 }, + { STB0899_UPLSTRL, 0x60 }, + { STB0899_DFLSTRM, 0xe3 }, + { STB0899_DFLSTRL, 0x00 }, + { STB0899_SYNCSTR, 0x47 }, + { STB0899_SYNCDSTRM, 0x05 }, + { STB0899_SYNCDSTRL, 0x18 }, + { STB0899_CFGPDELSTATUS1, 0x19 }, + { STB0899_CFGPDELSTATUS2, 0x2b }, + { STB0899_BBFERRORM, 0x00 }, + { STB0899_BBFERRORL, 0x01 }, + { STB0899_UPKTERRORM, 0x00 }, + { STB0899_UPKTERRORL, 0x00 }, + { 0xffff, 0xff }, +}; + +static struct stb0899_config stb0899_config = { + .init_dev = pctv452e_init_dev, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = pctv452e_init_s1_demod, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .demod_address = I2C_ADDR_STB0899, /* I2C Address */ + .block_sync_mode = STB0899_SYNC_FORCED, /* ? */ + + .xtal_freq = 27000000, /* Assume Hz ? */ + .inversion = IQ_SWAP_ON, /* ? */ + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .ts_output_mode = 0, /* Use parallel mode */ + .clock_polarity = 0, + .data_clk_parity = 0, + .fec_mode = 0, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL, + + /* helper for switching LED green/orange */ + .postproc = pctv45e_postproc +}; + +static struct stb6100_config stb6100_config = { + .tuner_address = I2C_ADDR_STB6100, + .refclock = 27000000 +}; + + +static struct i2c_algorithm pctv452e_i2c_algo = { + .master_xfer = pctv452e_i2c_xfer, + .functionality = pctv452e_i2c_func +}; + +static int pctv452e_frontend_attach(struct dvb_usb_adapter *a) +{ + struct usb_device_id *id; + + a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config, + &a->dev->i2c_adap); + if (!a->fe_adap[0].fe) + return -ENODEV; + if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe, + &a->dev->i2c_adap)) == 0) + err("Cannot attach lnbp22\n"); + + id = a->dev->desc->warm_ids[0]; + if (USB_VID_TECHNOTREND == id->idVendor + && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct) + /* Error ignored. */ + tt3650_ci_init(a); + + return 0; +} + +static int pctv452e_tuner_attach(struct dvb_usb_adapter *a) +{ + if (!a->fe_adap[0].fe) + return -ENODEV; + if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config, + &a->dev->i2c_adap) == 0) { + err("%s failed\n", __func__); + return -ENODEV; + } + + return 0; +} + +static struct usb_device_id pctv452e_usb_table[] = { + {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)}, + {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)}, + {USB_DEVICE(USB_VID_TECHNOTREND, + USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)}, + {} +}; +MODULE_DEVICE_TABLE(usb, pctv452e_usb_table); + +static struct dvb_usb_device_properties pctv452e_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */ + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = sizeof(struct pctv452e_state), + + .power_ctrl = pctv452e_power_ctrl, + + .rc.core = { + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .allowed_protos = RC_TYPE_UNKNOWN, + .rc_query = pctv452e_rc_query, + .rc_interval = 100, + }, + + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + .frontend_attach = pctv452e_frontend_attach, + .tuner_attach = pctv452e_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 4, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1 + } + } + }, + } }, + } }, + + .i2c_algo = &pctv452e_i2c_algo, + + .generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */ + + .num_device_descs = 1, + .devices = { + { .name = "PCTV HDTV USB", + .cold_ids = { NULL, NULL }, /* this is a warm only device */ + .warm_ids = { &pctv452e_usb_table[0], NULL } + }, + { 0 }, + } +}; + +static struct dvb_usb_device_properties tt_connect_s2_3600_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */ + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = sizeof(struct pctv452e_state), + + .power_ctrl = pctv452e_power_ctrl, + .read_mac_address = pctv452e_read_mac_address, + + .rc.core = { + .rc_codes = RC_MAP_TT_1500, + .allowed_protos = RC_TYPE_UNKNOWN, + .rc_query = pctv452e_rc_query, + .rc_interval = 100, + }, + + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + .frontend_attach = pctv452e_frontend_attach, + .tuner_attach = pctv452e_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 7, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1 + } + } + }, + + } }, + } }, + + .i2c_algo = &pctv452e_i2c_algo, + + .generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/ + + .num_device_descs = 2, + .devices = { + { .name = "Technotrend TT Connect S2-3600", + .cold_ids = { NULL, NULL }, /* this is a warm only device */ + .warm_ids = { &pctv452e_usb_table[1], NULL } + }, + { .name = "Technotrend TT Connect S2-3650-CI", + .cold_ids = { NULL, NULL }, + .warm_ids = { &pctv452e_usb_table[2], NULL } + }, + { 0 }, + } +}; + +static void pctv452e_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + + tt3650_ci_uninit(d); + dvb_usb_device_exit(intf); +} + +static int pctv452e_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (0 == dvb_usb_device_init(intf, &pctv452e_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties, + THIS_MODULE, NULL, adapter_nr)) + return 0; + + return -ENODEV; +} + +static struct usb_driver pctv452e_usb_driver = { + .name = "pctv452e", + .probe = pctv452e_usb_probe, + .disconnect = pctv452e_usb_disconnect, + .id_table = pctv452e_usb_table, +}; + +module_usb_driver(pctv452e_usb_driver); + +MODULE_AUTHOR("Dominik Kuhlen "); +MODULE_AUTHOR("Andre Weidemann "); +MODULE_AUTHOR("Michael H. Schimek "); +MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c new file mode 100644 index 000000000000..acefaa89cc53 --- /dev/null +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -0,0 +1,789 @@ +/* + * Linux driver for Technisat DVB-S/S2 USB 2.0 device + * + * Copyright (C) 2010 Patrick Boettcher, + * Kernel Labs Inc. PO Box 745, St James, NY 11780 + * + * Development was sponsored by Technisat Digital UK Limited, whose + * registered office is Witan Gate House 500 - 600 Witan Gate West, + * Milton Keynes, MK9 1SH + * + * This program is free software; 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. + * + * + * 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 PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND + * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER + * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the + * GNU General Public License for more details. + */ + +#define DVB_USB_LOG_PREFIX "technisat-usb2" +#include "dvb-usb.h" + +#include "stv6110x.h" +#include "stv090x.h" + +/* module parameters */ +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (bit-mask: 1=info,2=eeprom,4=i2c,8=rc)." \ + DVB_USB_DEBUG_STATUS); + +/* disables all LED control command and + * also does not start the signal polling thread */ +static int disable_led_control; +module_param(disable_led_control, int, 0444); +MODULE_PARM_DESC(disable_led_control, + "disable LED control of the device " + "(default: 0 - LED control is active)."); + +/* device private data */ +struct technisat_usb2_state { + struct dvb_usb_device *dev; + struct delayed_work green_led_work; + u8 power_state; + + u16 last_scan_code; +}; + +/* debug print helpers */ +#define deb_info(args...) dprintk(debug, 0x01, args) +#define deb_eeprom(args...) dprintk(debug, 0x02, args) +#define deb_i2c(args...) dprintk(debug, 0x04, args) +#define deb_rc(args...) dprintk(debug, 0x08, args) + +/* vendor requests */ +#define SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST 0xB3 +#define SET_FRONT_END_RESET_VENDOR_REQUEST 0xB4 +#define GET_VERSION_INFO_VENDOR_REQUEST 0xB5 +#define SET_GREEN_LED_VENDOR_REQUEST 0xB6 +#define SET_RED_LED_VENDOR_REQUEST 0xB7 +#define GET_IR_DATA_VENDOR_REQUEST 0xB8 +#define SET_LED_TIMER_DIVIDER_VENDOR_REQUEST 0xB9 +#define SET_USB_REENUMERATION 0xBA + +/* i2c-access methods */ +#define I2C_SPEED_100KHZ_BIT 0x40 + +#define I2C_STATUS_NAK 7 +#define I2C_STATUS_OK 8 + +static int technisat_usb2_i2c_access(struct usb_device *udev, + u8 device_addr, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) +{ + u8 b[64]; + int ret, actual_length; + + deb_i2c("i2c-access: %02x, tx: ", device_addr); + debug_dump(tx, txlen, deb_i2c); + deb_i2c(" "); + + if (txlen > 62) { + err("i2c TX buffer can't exceed 62 bytes (dev 0x%02x)", + device_addr); + txlen = 62; + } + if (rxlen > 62) { + err("i2c RX buffer can't exceed 62 bytes (dev 0x%02x)", + device_addr); + txlen = 62; + } + + b[0] = I2C_SPEED_100KHZ_BIT; + b[1] = device_addr << 1; + + if (rx != NULL) { + b[0] |= rxlen; + b[1] |= 1; + } + + memcpy(&b[2], tx, txlen); + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x01), + b, 2 + txlen, + NULL, 1000); + + if (ret < 0) { + err("i2c-error: out failed %02x = %d", device_addr, ret); + return -ENODEV; + } + + ret = usb_bulk_msg(udev, + usb_rcvbulkpipe(udev, 0x01), + b, 64, &actual_length, 1000); + if (ret < 0) { + err("i2c-error: in failed %02x = %d", device_addr, ret); + return -ENODEV; + } + + if (b[0] != I2C_STATUS_OK) { + err("i2c-error: %02x = %d", device_addr, b[0]); + /* handle tuner-i2c-nak */ + if (!(b[0] == I2C_STATUS_NAK && + device_addr == 0x60 + /* && device_is_technisat_usb2 */)) + return -ENODEV; + } + + deb_i2c("status: %d, ", b[0]); + + if (rx != NULL) { + memcpy(rx, &b[2], rxlen); + + deb_i2c("rx (%d): ", rxlen); + debug_dump(rx, rxlen, deb_i2c); + } + + deb_i2c("\n"); + + return 0; +} + +static int technisat_usb2_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + int ret = 0, i; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + /* Ensure nobody else hits the i2c bus while we're sending our + sequence of messages, (such as the remote control thread) */ + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + if (i+1 < num && msg[i+1].flags & I2C_M_RD) { + ret = technisat_usb2_i2c_access(d->udev, msg[i+1].addr, + msg[i].buf, msg[i].len, + msg[i+1].buf, msg[i+1].len); + if (ret != 0) + break; + i++; + } else { + ret = technisat_usb2_i2c_access(d->udev, msg[i].addr, + msg[i].buf, msg[i].len, + NULL, 0); + if (ret != 0) + break; + } + } + + if (ret == 0) + ret = i; + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 technisat_usb2_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm technisat_usb2_i2c_algo = { + .master_xfer = technisat_usb2_i2c_xfer, + .functionality = technisat_usb2_i2c_func, +}; + +#if 0 +static void technisat_usb2_frontend_reset(struct usb_device *udev) +{ + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SET_FRONT_END_RESET_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 10, 0, + NULL, 0, 500); +} +#endif + +/* LED control */ +enum technisat_usb2_led_state { + LED_OFF, + LED_BLINK, + LED_ON, + LED_UNDEFINED +}; + +static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state) +{ + int ret; + + u8 led[8] = { + red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, + 0 + }; + + if (disable_led_control && state != LED_OFF) + return 0; + + switch (state) { + case LED_ON: + led[1] = 0x82; + break; + case LED_BLINK: + led[1] = 0x82; + if (red) { + led[2] = 0x02; + led[3] = 10; + led[4] = 10; + } else { + led[2] = 0xff; + led[3] = 50; + led[4] = 50; + } + led[5] = 1; + break; + + default: + case LED_OFF: + led[1] = 0x80; + break; + } + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 0, 0, + led, sizeof(led), 500); + + mutex_unlock(&d->i2c_mutex); + return ret; +} + +static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green) +{ + int ret; + u8 b = 0; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + SET_LED_TIMER_DIVIDER_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + (red << 8) | green, 0, + &b, 1, 500); + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static void technisat_usb2_green_led_control(struct work_struct *work) +{ + struct technisat_usb2_state *state = + container_of(work, struct technisat_usb2_state, green_led_work.work); + struct dvb_frontend *fe = state->dev->adapter[0].fe_adap[0].fe; + + if (state->power_state == 0) + goto schedule; + + if (fe != NULL) { + enum fe_status status; + + if (fe->ops.read_status(fe, &status) != 0) + goto schedule; + + if (status & FE_HAS_LOCK) { + u32 ber; + + if (fe->ops.read_ber(fe, &ber) != 0) + goto schedule; + + if (ber > 1000) + technisat_usb2_set_led(state->dev, 0, LED_BLINK); + else + technisat_usb2_set_led(state->dev, 0, LED_ON); + } else + technisat_usb2_set_led(state->dev, 0, LED_OFF); + } + +schedule: + schedule_delayed_work(&state->green_led_work, + msecs_to_jiffies(500)); +} + +/* method to find out whether the firmware has to be downloaded or not */ +static int technisat_usb2_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold) +{ + int ret; + u8 version[3]; + + /* first select the interface */ + if (usb_set_interface(udev, 0, 1) != 0) + err("could not set alternate setting to 0"); + else + info("set alternate setting"); + + *cold = 0; /* by default do not download a firmware - just in case something is wrong */ + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + GET_VERSION_INFO_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_IN, + 0, 0, + version, sizeof(version), 500); + + if (ret < 0) + *cold = 1; + else { + info("firmware version: %d.%d", version[1], version[2]); + *cold = 0; + } + + return 0; +} + +/* power control */ +static int technisat_usb2_power_ctrl(struct dvb_usb_device *d, int level) +{ + struct technisat_usb2_state *state = d->priv; + + state->power_state = level; + + if (disable_led_control) + return 0; + + /* green led is turned off in any case - will be turned on when tuning */ + technisat_usb2_set_led(d, 0, LED_OFF); + /* red led is turned on all the time */ + technisat_usb2_set_led(d, 1, LED_ON); + return 0; +} + +/* mac address reading - from the eeprom */ +#if 0 +static void technisat_usb2_eeprom_dump(struct dvb_usb_device *d) +{ + u8 reg; + u8 b[16]; + int i, j; + + /* full EEPROM dump */ + for (j = 0; j < 256 * 4; j += 16) { + reg = j; + if (technisat_usb2_i2c_access(d->udev, 0x50 + j / 256, ®, 1, b, 16) != 0) + break; + + deb_eeprom("EEPROM: %01x%02x: ", j / 256, reg); + for (i = 0; i < 16; i++) + deb_eeprom("%02x ", b[i]); + deb_eeprom("\n"); + } +} +#endif + +static u8 technisat_usb2_calc_lrc(const u8 *b, u16 length) +{ + u8 lrc = 0; + while (--length) + lrc ^= *b++; + return lrc; +} + +static int technisat_usb2_eeprom_lrc_read(struct dvb_usb_device *d, + u16 offset, u8 *b, u16 length, u8 tries) +{ + u8 bo = offset & 0xff; + struct i2c_msg msg[] = { + { + .addr = 0x50 | ((offset >> 8) & 0x3), + .buf = &bo, + .len = 1 + }, { + .addr = 0x50 | ((offset >> 8) & 0x3), + .flags = I2C_M_RD, + .buf = b, + .len = length + } + }; + + while (tries--) { + int status; + + if (i2c_transfer(&d->i2c_adap, msg, 2) != 2) + break; + + status = + technisat_usb2_calc_lrc(b, length - 1) == b[length - 1]; + + if (status) + return 0; + } + + return -EREMOTEIO; +} + +#define EEPROM_MAC_START 0x3f8 +#define EEPROM_MAC_TOTAL 8 +static int technisat_usb2_read_mac_address(struct dvb_usb_device *d, + u8 mac[]) +{ + u8 buf[EEPROM_MAC_TOTAL]; + + if (technisat_usb2_eeprom_lrc_read(d, EEPROM_MAC_START, + buf, EEPROM_MAC_TOTAL, 4) != 0) + return -ENODEV; + + memcpy(mac, buf, 6); + return 0; +} + +/* frontend attach */ +static int technisat_usb2_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + int i; + u8 gpio[3] = { 0 }; /* 0 = 2, 1 = 3, 2 = 4 */ + + gpio[2] = 1; /* high - voltage ? */ + + switch (voltage) { + case SEC_VOLTAGE_13: + gpio[0] = 1; + break; + case SEC_VOLTAGE_18: + gpio[0] = 1; + gpio[1] = 1; + break; + default: + case SEC_VOLTAGE_OFF: + break; + } + + for (i = 0; i < 3; i++) + if (stv090x_set_gpio(fe, i+2, 0, gpio[i], 0) != 0) + return -EREMOTEIO; + return 0; +} + +static struct stv090x_config technisat_usb2_stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 8000000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_DVBCI, + .ts1_clk = 13400000, + .ts1_tei = 1, + + .repeater_level = STV090x_RPTLEVEL_64, + + .tuner_bbgain = 6, +}; + +static struct stv6110x_config technisat_usb2_stv6110x_config = { + .addr = 0x60, + .refclk = 16000000, + .clk_div = 2, +}; + +static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) +{ + struct usb_device *udev = a->dev->udev; + int ret; + + a->fe_adap[0].fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config, + &a->dev->i2c_adap, STV090x_DEMODULATOR_0); + + if (a->fe_adap[0].fe) { + struct stv6110x_devctl *ctl; + + ctl = dvb_attach(stv6110x_attach, + a->fe_adap[0].fe, + &technisat_usb2_stv6110x_config, + &a->dev->i2c_adap); + + if (ctl) { + technisat_usb2_stv090x_config.tuner_init = ctl->tuner_init; + technisat_usb2_stv090x_config.tuner_sleep = ctl->tuner_sleep; + technisat_usb2_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + technisat_usb2_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + technisat_usb2_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + technisat_usb2_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + technisat_usb2_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + technisat_usb2_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + technisat_usb2_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + technisat_usb2_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + technisat_usb2_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (a->fe_adap[0].fe->ops.init) + a->fe_adap[0].fe->ops.init(a->fe_adap[0].fe); + + if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0) + return -EAGAIN; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 0, 0, + NULL, 0, 500); + mutex_unlock(&a->dev->i2c_mutex); + + if (ret != 0) + err("could not set IF_CLK to external"); + + a->fe_adap[0].fe->ops.set_voltage = technisat_usb2_set_voltage; + + /* if everything was successful assign a nice name to the frontend */ + strlcpy(a->fe_adap[0].fe->ops.info.name, a->dev->desc->name, + sizeof(a->fe_adap[0].fe->ops.info.name)); + } else { + dvb_frontend_detach(a->fe_adap[0].fe); + a->fe_adap[0].fe = NULL; + } + } + + technisat_usb2_set_led_timer(a->dev, 1, 1); + + return a->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +/* Remote control */ + +/* the device is giving providing raw IR-signals to the host mapping + * it only to one remote control is just the default implementation + */ +#define NOMINAL_IR_BIT_TRANSITION_TIME_US 889 +#define NOMINAL_IR_BIT_TIME_US (2 * NOMINAL_IR_BIT_TRANSITION_TIME_US) + +#define FIRMWARE_CLOCK_TICK 83333 +#define FIRMWARE_CLOCK_DIVISOR 256 + +#define IR_PERCENT_TOLERANCE 15 + +#define NOMINAL_IR_BIT_TRANSITION_TICKS ((NOMINAL_IR_BIT_TRANSITION_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK) +#define NOMINAL_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICKS / FIRMWARE_CLOCK_DIVISOR) + +#define NOMINAL_IR_BIT_TIME_TICKS ((NOMINAL_IR_BIT_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK) +#define NOMINAL_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICKS / FIRMWARE_CLOCK_DIVISOR) + +#define MINIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT - ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +#define MAXIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT + ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) + +#define MINIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT - ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +#define MAXIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT + ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) + +static int technisat_usb2_get_ir(struct dvb_usb_device *d) +{ + u8 buf[62], *b; + int ret; + struct ir_raw_event ev; + + buf[0] = GET_IR_DATA_VENDOR_REQUEST; + buf[1] = 0x08; + buf[2] = 0x8f; + buf[3] = MINIMUM_IR_BIT_TRANSITION_TICK_COUNT; + buf[4] = MAXIMUM_IR_BIT_TIME_TICK_COUNT; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GET_IR_DATA_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 0, 0, + buf, 5, 500); + if (ret < 0) + goto unlock; + + buf[1] = 0; + buf[2] = 0; + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + GET_IR_DATA_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_IN, + 0x8080, 0, + buf, sizeof(buf), 500); + +unlock: + mutex_unlock(&d->i2c_mutex); + + if (ret < 0) + return ret; + + if (ret == 1) + return 0; /* no key pressed */ + + /* decoding */ + b = buf+1; + +#if 0 + deb_rc("RC: %d ", ret); + debug_dump(b, ret, deb_rc); +#endif + + ev.pulse = 0; + while (1) { + ev.pulse = !ev.pulse; + ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000; + ir_raw_event_store(d->rc_dev, &ev); + + b++; + if (*b == 0xff) { + ev.pulse = 0; + ev.duration = 888888*2; + ir_raw_event_store(d->rc_dev, &ev); + break; + } + } + + ir_raw_event_handle(d->rc_dev); + + return 1; +} + +static int technisat_usb2_rc_query(struct dvb_usb_device *d) +{ + int ret = technisat_usb2_get_ir(d); + + if (ret < 0) + return ret; + + if (ret == 0) + return 0; + + if (!disable_led_control) + technisat_usb2_set_led(d, 1, LED_BLINK); + + return 0; +} + +/* DVB-USB and USB stuff follows */ +static struct usb_device_id technisat_usb2_id_table[] = { + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_DVB_S2) }, + { 0 } /* Terminating entry */ +}; + +/* device description */ +static struct dvb_usb_device_properties technisat_usb2_devices = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .identify_state = technisat_usb2_identify_state, + .firmware = "dvb-usb-SkyStar_USB_HD_FW_v17_63.HEX.fw", + + .size_of_priv = sizeof(struct technisat_usb2_state), + + .i2c_algo = &technisat_usb2_i2c_algo, + + .power_ctrl = technisat_usb2_power_ctrl, + .read_mac_address = technisat_usb2_read_mac_address, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = technisat_usb2_frontend_attach, + + .stream = { + .type = USB_ISOC, + .count = 8, + .endpoint = 0x2, + .u = { + .isoc = { + .framesperurb = 32, + .framesize = 2048, + .interval = 3, + } + } + }, + }}, + .size_of_priv = 0, + }, + }, + + .num_device_descs = 1, + .devices = { + { "Technisat SkyStar USB HD (DVB-S/S2)", + { &technisat_usb2_id_table[0], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = 100, + .rc_codes = RC_MAP_TECHNISAT_USB2, + .module_name = "technisat-usb2", + .rc_query = technisat_usb2_rc_query, + .allowed_protos = RC_TYPE_ALL, + .driver_type = RC_DRIVER_IR_RAW, + } +}; + +static int technisat_usb2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *dev; + + if (dvb_usb_device_init(intf, &technisat_usb2_devices, THIS_MODULE, + &dev, adapter_nr) != 0) + return -ENODEV; + + if (dev) { + struct technisat_usb2_state *state = dev->priv; + state->dev = dev; + + if (!disable_led_control) { + INIT_DELAYED_WORK(&state->green_led_work, + technisat_usb2_green_led_control); + schedule_delayed_work(&state->green_led_work, + msecs_to_jiffies(500)); + } + } + + return 0; +} + +static void technisat_usb2_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *dev = usb_get_intfdata(intf); + + /* work and stuff was only created when the device is is hot-state */ + if (dev != NULL) { + struct technisat_usb2_state *state = dev->priv; + if (state != NULL) + cancel_delayed_work_sync(&state->green_led_work); + } + + dvb_usb_device_exit(intf); +} + +static struct usb_driver technisat_usb2_driver = { + .name = "dvb_usb_technisat_usb2", + .probe = technisat_usb2_probe, + .disconnect = technisat_usb2_disconnect, + .id_table = technisat_usb2_id_table, +}; + +module_usb_driver(technisat_usb2_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Technisat DVB-S/S2 USB 2.0 device"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c new file mode 100644 index 000000000000..e53a1061cb8e --- /dev/null +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -0,0 +1,820 @@ +/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones + * (e.g. Pinnacle 400e DVB-S USB2.0). + * + * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes. + * + * TDA8263 + TDA10086 + * + * I2C addresses: + * 0x08 - LNBP21PD - LNB power supply + * 0x0e - TDA10086 - Demodulator + * 0x50 - FX2 eeprom + * 0x60 - TDA8263 - Tuner + * 0x78 ??? + * + * Copyright (c) 2002 Holger Waechtler + * Copyright (c) 2003 Felix Domke + * Copyright (C) 2005-6 Patrick Boettcher + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#define DVB_USB_LOG_PREFIX "ttusb2" +#include "dvb-usb.h" + +#include "ttusb2.h" + +#include "tda826x.h" +#include "tda10086.h" +#include "tda1002x.h" +#include "tda10048.h" +#include "tda827x.h" +#include "lnbp21.h" +/* CA */ +#include "dvb_ca_en50221.h" + +/* debug */ +static int dvb_usb_ttusb2_debug; +#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args) +module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS); +static int dvb_usb_ttusb2_debug_ci; +module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644); +MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define ci_dbg(format, arg...) \ +do { \ + if (dvb_usb_ttusb2_debug_ci) \ + printk(KERN_DEBUG DVB_USB_LOG_PREFIX \ + ": %s " format "\n" , __func__, ## arg); \ +} while (0) + +enum { + TT3650_CMD_CI_TEST = 0x40, + TT3650_CMD_CI_RD_CTRL, + TT3650_CMD_CI_WR_CTRL, + TT3650_CMD_CI_RD_ATTR, + TT3650_CMD_CI_WR_ATTR, + TT3650_CMD_CI_RESET, + TT3650_CMD_CI_SET_VIDEO_PORT +}; + +struct ttusb2_state { + struct dvb_ca_en50221 ca; + struct mutex ca_mutex; + u8 id; + u16 last_rc_key; +}; + +static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct ttusb2_state *st = d->priv; + u8 *s, *r = NULL; + int ret = 0; + + s = kzalloc(wlen+4, GFP_KERNEL); + if (!s) + return -ENOMEM; + + r = kzalloc(64, GFP_KERNEL); + if (!r) { + kfree(s); + return -ENOMEM; + } + + s[0] = 0xaa; + s[1] = ++st->id; + s[2] = cmd; + s[3] = wlen; + memcpy(&s[4],wbuf,wlen); + + ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0); + + if (ret != 0 || + r[0] != 0x55 || + r[1] != s[1] || + r[2] != cmd || + (rlen > 0 && r[3] != rlen)) { + warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]); + kfree(s); + kfree(r); + return -EIO; + } + + if (rlen > 0) + memcpy(rbuf, &r[4], rlen); + + kfree(s); + kfree(r); + + return 0; +} + +/* ci */ +static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len) +{ + int ret; + u8 rx[60];/* (64 -4) */ + ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len); + if (!ret) + memcpy(data, rx, read_len); + return ret; +} + +static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len) +{ + struct dvb_usb_device *d = ca->data; + struct ttusb2_state *state = d->priv; + int ret; + + mutex_lock(&state->ca_mutex); + ret = tt3650_ci_msg(d, cmd, data, write_len, read_len); + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + u8 buf[3]; + int ret = 0; + + if (slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3); + + ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]); + + if (ret < 0) + return ret; + + return buf[2]; +} + +static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + u8 buf[3]; + + ci_dbg("%d 0x%04x 0x%02x", slot, address, value); + + if (slot) + return -EINVAL; + + buf[0] = (address >> 8) & 0x0F; + buf[1] = address; + buf[2] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3); +} + +static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + u8 buf[2]; + int ret; + + if (slot) + return -EINVAL; + + buf[0] = address & 3; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2); + + ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]); + + if (ret < 0) + return ret; + + return buf[1]; +} + +static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + u8 buf[2]; + + ci_dbg("%d 0x%02x 0x%02x", slot, address, value); + + if (slot) + return -EINVAL; + + buf[0] = address; + buf[1] = value; + + return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2); +} + +static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable) +{ + u8 buf[1]; + int ret; + + ci_dbg("%d %d", slot, enable); + + if (slot) + return -EINVAL; + + buf[0] = enable; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + if (ret < 0) + return ret; + + if (enable != buf[0]) { + err("CI not %sabled.", enable ? "en" : "dis"); + return -EIO; + } + + return 0; +} + +static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, 0); +} + +static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + return tt3650_ci_set_video_port(ca, slot, 1); +} + +static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = ca->data; + struct ttusb2_state *state = d->priv; + u8 buf[1]; + int ret; + + ci_dbg("%d", slot); + + if (slot) + return -EINVAL; + + buf[0] = 0; + + mutex_lock(&state->ca_mutex); + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (ret) + goto failed; + + msleep(500); + + buf[0] = 1; + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); + if (ret) + goto failed; + + msleep(500); + + buf[0] = 0; /* FTA */ + + ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); + + msleep(1100); + + failed: + mutex_unlock(&state->ca_mutex); + + return ret; +} + +static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + u8 buf[1]; + int ret; + + if (slot) + return -EINVAL; + + ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1); + if (ret) + return ret; + + if (1 == buf[0]) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } + return 0; +} + +static void tt3650_ci_uninit(struct dvb_usb_device *d) +{ + struct ttusb2_state *state; + + ci_dbg(""); + + if (NULL == d) + return; + + state = d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + +static int tt3650_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct ttusb2_state *state = d->priv; + int ret; + + ci_dbg(""); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem; + state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem; + state->ca.read_cam_control = tt3650_ci_read_cam_control; + state->ca.write_cam_control = tt3650_ci_write_cam_control; + state->ca.slot_reset = tt3650_ci_slot_reset; + state->ca.slot_shutdown = tt3650_ci_slot_shutdown; + state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable; + state->ca.poll_slot_status = tt3650_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + /* flags */ 0, + /* n_slots */ 1); + if (ret) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + info("CI initialized."); + + return 0; +} + +static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + static u8 obuf[60], ibuf[60]; + int i, write_read, read; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read = msg[i].flags & I2C_M_RD; + + obuf[0] = (msg[i].addr << 1) | (write_read | read); + if (read) + obuf[1] = 0; + else + obuf[1] = msg[i].len; + + /* read request */ + if (write_read) + obuf[2] = msg[i+1].len; + else if (read) + obuf[2] = msg[i].len; + else + obuf[2] = 0; + + memcpy(&obuf[3], msg[i].buf, msg[i].len); + + if (ttusb2_msg(d, CMD_I2C_XFER, obuf, obuf[1]+3, ibuf, obuf[2] + 3) < 0) { + err("i2c transfer failed."); + break; + } + + if (write_read) { + memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len); + i++; + } else if (read) + memcpy(msg[i].buf, &ibuf[3], msg[i].len); + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 ttusb2_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm ttusb2_i2c_algo = { + .master_xfer = ttusb2_i2c_xfer, + .functionality = ttusb2_i2c_func, +}; + +/* command to poll IR receiver (copied from pctv452e.c) */ +#define CMD_GET_IR_CODE 0x1b + +/* IR */ +static int tt3650_rc_query(struct dvb_usb_device *d) +{ + int ret; + u8 rx[9]; /* A CMD_GET_IR_CODE reply is 9 bytes long */ + struct ttusb2_state *st = d->priv; + ret = ttusb2_msg(d, CMD_GET_IR_CODE, NULL, 0, rx, sizeof(rx)); + if (ret != 0) + return ret; + + if (rx[8] & 0x01) { + /* got a "press" event */ + st->last_rc_key = (rx[3] << 8) | rx[2]; + deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]); + rc_keydown(d->rc_dev, st->last_rc_key, 0); + } else if (st->last_rc_key) { + rc_keyup(d->rc_dev); + st->last_rc_key = 0; + } + + return 0; +} + + +/* Callbacks for DVB USB */ +static int ttusb2_identify_state (struct usb_device *udev, struct + dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, + int *cold) +{ + *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; + return 0; +} + +static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = onoff; + ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0); + return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0); +} + + +static struct tda10086_config tda10086_config = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static struct tda10023_config tda10023_config = { + .demod_address = 0x0c, + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .deltaf = 0xa511, +}; + +static struct tda10048_config tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_4000, + .dtv7_if_freq_khz = TDA10048_IF_4500, + .dtv8_if_freq_khz = TDA10048_IF_5000, + .clk_freq_khz = TDA10048_CLK_16000, + .no_firmware = 1, + .set_pll = true , + .pll_m = 5, + .pll_n = 3, + .pll_p = 0, +}; + +static struct tda827x_config tda827x_config = { + .config = 0, +}; + +static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev,0,3) < 0) + err("set interface to alts=3 failed"); + + if ((adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) { + deb_info("TDA10086 attach failed\n"); + return -ENODEV; + } + + return 0; +} + +static int ttusb2_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + + return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable); +} + +static int ttusb2_frontend_tda10023_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev, 0, 3) < 0) + err("set interface to alts=3 failed"); + + if (adap->fe_adap[0].fe == NULL) { + /* FE 0 DVB-C */ + adap->fe_adap[0].fe = dvb_attach(tda10023_attach, + &tda10023_config, &adap->dev->i2c_adap, 0x48); + + if (adap->fe_adap[0].fe == NULL) { + deb_info("TDA10023 attach failed\n"); + return -ENODEV; + } + tt3650_ci_init(adap); + } else { + adap->fe_adap[1].fe = dvb_attach(tda10048_attach, + &tda10048_config, &adap->dev->i2c_adap); + + if (adap->fe_adap[1].fe == NULL) { + deb_info("TDA10048 attach failed\n"); + return -ENODEV; + } + + /* tuner is behind TDA10023 I2C-gate */ + adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl; + + } + + return 0; +} + +static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe; + + /* MFE: select correct FE to attach tuner since that's called twice */ + if (adap->fe_adap[1].fe == NULL) + fe = adap->fe_adap[0].fe; + else + fe = adap->fe_adap[1].fe; + + /* attach tuner */ + if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) { + printk(KERN_ERR "%s: No tda827x found!\n", __func__); + return -ENODEV; + } + return 0; +} + +static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap) +{ + if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) { + deb_info("TDA8263 attach failed\n"); + return -ENODEV; + } + + if (dvb_attach(lnbp21_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0, 0) == NULL) { + deb_info("LNBP21 attach failed\n"); + return -ENODEV; + } + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties ttusb2_properties; +static struct dvb_usb_device_properties ttusb2_properties_s2400; +static struct dvb_usb_device_properties ttusb2_properties_ct3650; + +static void ttusb2_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + + tt3650_ci_uninit(d); + dvb_usb_device_exit(intf); +} + +static int ttusb2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (0 == dvb_usb_device_init(intf, &ttusb2_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &ttusb2_properties_s2400, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &ttusb2_properties_ct3650, + THIS_MODULE, NULL, adapter_nr)) + return 0; + return -ENODEV; +} + +static struct usb_device_id ttusb2_table [] = { + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) }, + { USB_DEVICE(USB_VID_TECHNOTREND, + USB_PID_TECHNOTREND_CONNECT_S2400) }, + { USB_DEVICE(USB_VID_TECHNOTREND, + USB_PID_TECHNOTREND_CONNECT_CT3650) }, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, ttusb2_table); + +static struct dvb_usb_device_properties ttusb2_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-pctv-400e-01.fw", + + .size_of_priv = sizeof(struct ttusb2_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = NULL, // ttusb2_streaming_ctrl, + + .frontend_attach = ttusb2_frontend_tda10086_attach, + .tuner_attach = ttusb2_tuner_tda826x_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1, + } + } + } + }}, + } + }, + + .power_ctrl = ttusb2_power_ctrl, + .identify_state = ttusb2_identify_state, + + .i2c_algo = &ttusb2_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 2, + .devices = { + { "Pinnacle 400e DVB-S USB2.0", + { &ttusb2_table[0], NULL }, + { NULL }, + }, + { "Pinnacle 450e DVB-S USB2.0", + { &ttusb2_table[1], NULL }, + { NULL }, + }, + } +}; + +static struct dvb_usb_device_properties ttusb2_properties_s2400 = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-tt-s2400-01.fw", + + .size_of_priv = sizeof(struct ttusb2_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = NULL, + + .frontend_attach = ttusb2_frontend_tda10086_attach, + .tuner_attach = ttusb2_tuner_tda826x_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1, + } + } + } + }}, + } + }, + + .power_ctrl = ttusb2_power_ctrl, + .identify_state = ttusb2_identify_state, + + .i2c_algo = &ttusb2_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Technotrend TT-connect S-2400", + { &ttusb2_table[2], NULL }, + { NULL }, + }, + } +}; + +static struct dvb_usb_device_properties ttusb2_properties_ct3650 = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct ttusb2_state), + + .rc.core = { + .rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */ + .rc_codes = RC_MAP_TT_1500, + .rc_query = tt3650_rc_query, + .allowed_protos = RC_TYPE_UNKNOWN, + }, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 2, + .fe = {{ + .streaming_ctrl = NULL, + + .frontend_attach = ttusb2_frontend_tda10023_attach, + .tuner_attach = ttusb2_tuner_tda827x_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1, + } + } + } + }, { + .streaming_ctrl = NULL, + + .frontend_attach = ttusb2_frontend_tda10023_attach, + .tuner_attach = ttusb2_tuner_tda827x_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1, + } + } + } + }}, + }, + }, + + .power_ctrl = ttusb2_power_ctrl, + .identify_state = ttusb2_identify_state, + + .i2c_algo = &ttusb2_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Technotrend TT-connect CT-3650", + .warm_ids = { &ttusb2_table[3], NULL }, + }, + } +}; + +static struct usb_driver ttusb2_driver = { + .name = "dvb_usb_ttusb2", + .probe = ttusb2_probe, + .disconnect = ttusb2_usb_disconnect, + .id_table = ttusb2_table, +}; + +module_usb_driver(ttusb2_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/ttusb2.h b/drivers/media/usb/dvb-usb/ttusb2.h new file mode 100644 index 000000000000..52a63af40896 --- /dev/null +++ b/drivers/media/usb/dvb-usb/ttusb2.h @@ -0,0 +1,70 @@ +/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones + * (e.g. Pinnacle 400e DVB-S USB2.0). + * + * Copyright (c) 2002 Holger Waechtler + * Copyright (c) 2003 Felix Domke + * Copyright (C) 2005-6 Patrick Boettcher + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_TTUSB2_H_ +#define _DVB_USB_TTUSB2_H_ + +/* TTUSB protocol + * + * always to messages (out/in) + * out message: + * 0xaa + * + * in message (complete block is always 0x40 bytes long) + * 0x55 + * + * id is incremented for each transaction + */ + +#define CMD_DSP_DOWNLOAD 0x13 +/* out data: [28] + * last block must be empty */ + +#define CMD_DSP_BOOT 0x14 +/* out data: nothing */ + +#define CMD_POWER 0x15 +/* out data: */ + +#define CMD_LNB 0x16 +/* out data: <18V=0,13V=1> */ + +#define CMD_GET_VERSION 0x17 +/* in data: [5] */ + +#define CMD_DISEQC 0x18 +/* out data: [cmdlen] */ + +#define CMD_PID_ENABLE 0x22 +/* out data: */ + +#define CMD_PID_DISABLE 0x23 +/* out data: */ + +#define CMD_FILTER_ENABLE 0x24 +/* out data: [12] [12] */ + +#define CMD_FILTER_DISABLE 0x25 +/* out data: */ + +#define CMD_GET_DSP_VERSION 0x26 +/* in data: [28] */ + +#define CMD_I2C_XFER 0x31 +/* out data: [sndlen] + * in data: [rcvlen] */ + +#define CMD_I2C_BITRATE 0x32 +/* out data: */ + +#endif diff --git a/drivers/media/usb/dvb-usb/umt-010.c b/drivers/media/usb/dvb-usb/umt-010.c new file mode 100644 index 000000000000..9b042292e788 --- /dev/null +++ b/drivers/media/usb/dvb-usb/umt-010.c @@ -0,0 +1,151 @@ +/* DVB USB framework compliant Linux driver for the HanfTek UMT-010 USB2.0 + * DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +#include "mt352.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int umt_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d }; + static u8 mt352_reset[] = { 0x50, 0x80 }; + static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 }; + static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 }; + + static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff }; + static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff }; + static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 }; + static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 }; + static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f }; + + static u8 mt352_acq_ctl[] = { 0x53, 0x50 }; + static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio)); + + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + + mt352_write(fe, mt352_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1)); + mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2)); + mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3)); + mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4)); + mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5)); + + mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl)); + mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1)); + + return 0; +} + +static int umt_mt352_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct mt352_config umt_config; + + memset(&umt_config,0,sizeof(struct mt352_config)); + umt_config.demod_init = umt_mt352_demod_init; + umt_config.demod_address = 0xf; + + adap->fe_adap[0].fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap); + + return 0; +} + +static int umt_tuner_attach (struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_TUA6034); + return 0; +} + +/* USB Driver stuff */ +static struct dvb_usb_device_properties umt_properties; + +static int umt_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (0 == dvb_usb_device_init(intf, &umt_properties, + THIS_MODULE, NULL, adapter_nr)) + return 0; + return -EINVAL; +} + +/* do not change the order of the ID table */ +static struct usb_device_id umt_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, umt_table); + +static struct dvb_usb_device_properties umt_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-umt-010-02.fw", + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .frontend_attach = umt_mt352_frontend_attach, + .tuner_attach = umt_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = MAX_NO_URBS_FOR_DATA_STREAM, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + }}, + .size_of_priv = sizeof(struct dibusb_state), + } + }, + .power_ctrl = dibusb_power_ctrl, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Hanftek UMT-010 DVB-T USB2.0", + { &umt_table[0], NULL }, + { &umt_table[1], NULL }, + }, + } +}; + +static struct usb_driver umt_driver = { + .name = "dvb_usb_umt_010", + .probe = umt_probe, + .disconnect = dvb_usb_device_exit, + .id_table = umt_table, +}; + +module_usb_driver(umt_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for HanfTek UMT 010 USB2.0 DVB-T device"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/usb-urb.c b/drivers/media/usb/dvb-usb/usb-urb.c new file mode 100644 index 000000000000..d62ee0f5a165 --- /dev/null +++ b/drivers/media/usb/dvb-usb/usb-urb.c @@ -0,0 +1,254 @@ +/* usb-urb.c is part of the DVB USB library. + * + * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file keeps functions for initializing and handling the + * BULK and ISOC USB data transfers in a generic way. + * Can be used for DVB-only and also, that's the plan, for + * Hybrid USB devices (analog and DVB). + */ +#include "dvb-usb-common.h" + +/* URB stuff for streaming */ +static void usb_urb_complete(struct urb *urb) +{ + struct usb_data_stream *stream = urb->context; + int ptype = usb_pipetype(urb->pipe); + int i; + u8 *b; + + deb_uxfer("'%s' urb completed. status: %d, length: %d/%d, pack_num: %d, errors: %d\n", + ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk", + urb->status,urb->actual_length,urb->transfer_buffer_length, + urb->number_of_packets,urb->error_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + deb_ts("urb completition error %d.\n", urb->status); + break; + } + + b = (u8 *) urb->transfer_buffer; + switch (ptype) { + case PIPE_ISOCHRONOUS: + for (i = 0; i < urb->number_of_packets; i++) { + + if (urb->iso_frame_desc[i].status != 0) + deb_ts("iso frame descriptor has an error: %d\n",urb->iso_frame_desc[i].status); + else if (urb->iso_frame_desc[i].actual_length > 0) + stream->complete(stream, b + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length); + + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + debug_dump(b,20,deb_uxfer); + break; + case PIPE_BULK: + if (urb->actual_length > 0) + stream->complete(stream, b, urb->actual_length); + break; + default: + err("unknown endpoint type in completition handler."); + return; + } + usb_submit_urb(urb,GFP_ATOMIC); +} + +int usb_urb_kill(struct usb_data_stream *stream) +{ + int i; + for (i = 0; i < stream->urbs_submitted; i++) { + deb_ts("killing URB no. %d.\n",i); + + /* stop the URB */ + usb_kill_urb(stream->urb_list[i]); + } + stream->urbs_submitted = 0; + return 0; +} + +int usb_urb_submit(struct usb_data_stream *stream) +{ + int i,ret; + for (i = 0; i < stream->urbs_initialized; i++) { + deb_ts("submitting URB no. %d\n",i); + if ((ret = usb_submit_urb(stream->urb_list[i],GFP_ATOMIC))) { + err("could not submit URB no. %d - get them all back",i); + usb_urb_kill(stream); + return ret; + } + stream->urbs_submitted++; + } + return 0; +} + +static int usb_free_stream_buffers(struct usb_data_stream *stream) +{ + if (stream->state & USB_STATE_URB_BUF) { + while (stream->buf_num) { + stream->buf_num--; + deb_mem("freeing buffer %d\n",stream->buf_num); + usb_free_coherent(stream->udev, stream->buf_size, + stream->buf_list[stream->buf_num], + stream->dma_addr[stream->buf_num]); + } + } + + stream->state &= ~USB_STATE_URB_BUF; + + return 0; +} + +static int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num, unsigned long size) +{ + stream->buf_num = 0; + stream->buf_size = size; + + deb_mem("all in all I will use %lu bytes for streaming\n",num*size); + + for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) { + deb_mem("allocating buffer %d\n",stream->buf_num); + if (( stream->buf_list[stream->buf_num] = + usb_alloc_coherent(stream->udev, size, GFP_ATOMIC, + &stream->dma_addr[stream->buf_num]) ) == NULL) { + deb_mem("not enough memory for urb-buffer allocation.\n"); + usb_free_stream_buffers(stream); + return -ENOMEM; + } + deb_mem("buffer %d: %p (dma: %Lu)\n", + stream->buf_num, +stream->buf_list[stream->buf_num], (long long)stream->dma_addr[stream->buf_num]); + memset(stream->buf_list[stream->buf_num],0,size); + stream->state |= USB_STATE_URB_BUF; + } + deb_mem("allocation successful\n"); + + return 0; +} + +static int usb_bulk_urb_init(struct usb_data_stream *stream) +{ + int i, j; + + if ((i = usb_allocate_stream_buffers(stream,stream->props.count, + stream->props.u.bulk.buffersize)) < 0) + return i; + + /* allocate the URBs */ + for (i = 0; i < stream->props.count; i++) { + stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!stream->urb_list[i]) { + deb_mem("not enough memory for urb_alloc_urb!.\n"); + for (j = 0; j < i; j++) + usb_free_urb(stream->urb_list[j]); + return -ENOMEM; + } + usb_fill_bulk_urb( stream->urb_list[i], stream->udev, + usb_rcvbulkpipe(stream->udev,stream->props.endpoint), + stream->buf_list[i], + stream->props.u.bulk.buffersize, + usb_urb_complete, stream); + + stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + stream->urb_list[i]->transfer_dma = stream->dma_addr[i]; + stream->urbs_initialized++; + } + return 0; +} + +static int usb_isoc_urb_init(struct usb_data_stream *stream) +{ + int i,j; + + if ((i = usb_allocate_stream_buffers(stream,stream->props.count, + stream->props.u.isoc.framesize*stream->props.u.isoc.framesperurb)) < 0) + return i; + + /* allocate the URBs */ + for (i = 0; i < stream->props.count; i++) { + struct urb *urb; + int frame_offset = 0; + + stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_ATOMIC); + if (!stream->urb_list[i]) { + deb_mem("not enough memory for urb_alloc_urb!\n"); + for (j = 0; j < i; j++) + usb_free_urb(stream->urb_list[j]); + return -ENOMEM; + } + + urb = stream->urb_list[i]; + + urb->dev = stream->udev; + urb->context = stream; + urb->complete = usb_urb_complete; + urb->pipe = usb_rcvisocpipe(stream->udev,stream->props.endpoint); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->interval = stream->props.u.isoc.interval; + urb->number_of_packets = stream->props.u.isoc.framesperurb; + urb->transfer_buffer_length = stream->buf_size; + urb->transfer_buffer = stream->buf_list[i]; + urb->transfer_dma = stream->dma_addr[i]; + + for (j = 0; j < stream->props.u.isoc.framesperurb; j++) { + urb->iso_frame_desc[j].offset = frame_offset; + urb->iso_frame_desc[j].length = stream->props.u.isoc.framesize; + frame_offset += stream->props.u.isoc.framesize; + } + + stream->urbs_initialized++; + } + return 0; +} + +int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props) +{ + if (stream == NULL || props == NULL) + return -EINVAL; + + memcpy(&stream->props, props, sizeof(*props)); + + usb_clear_halt(stream->udev,usb_rcvbulkpipe(stream->udev,stream->props.endpoint)); + + if (stream->complete == NULL) { + err("there is no data callback - this doesn't make sense."); + return -EINVAL; + } + + switch (stream->props.type) { + case USB_BULK: + return usb_bulk_urb_init(stream); + case USB_ISOC: + return usb_isoc_urb_init(stream); + default: + err("unknown URB-type for data transfer."); + return -EINVAL; + } +} + +int usb_urb_exit(struct usb_data_stream *stream) +{ + int i; + + usb_urb_kill(stream); + + for (i = 0; i < stream->urbs_initialized; i++) { + if (stream->urb_list[i] != NULL) { + deb_mem("freeing URB no. %d.\n",i); + /* free the URBs */ + usb_free_urb(stream->urb_list[i]); + } + } + stream->urbs_initialized = 0; + + usb_free_stream_buffers(stream); + return 0; +} diff --git a/drivers/media/usb/dvb-usb/vp702x-fe.c b/drivers/media/usb/dvb-usb/vp702x-fe.c new file mode 100644 index 000000000000..5eab468dd904 --- /dev/null +++ b/drivers/media/usb/dvb-usb/vp702x-fe.c @@ -0,0 +1,379 @@ +/* DVB frontend part of the Linux driver for the TwinhanDTV StarBox USB2.0 + * DVB-S receiver. + * + * Copyright (C) 2005 Ralph Metzler + * Metzler Brothers Systementwicklung GbR + * + * Copyright (C) 2005 Patrick Boettcher + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * This file can be removed soon, after the DST-driver is rewritten to provice + * the frontend-controlling separately. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + * + */ +#include "vp702x.h" + +struct vp702x_fe_state { + struct dvb_frontend fe; + struct dvb_usb_device *d; + + struct dvb_frontend_ops ops; + + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t tone_mode; + + u8 lnb_buf[8]; + + u8 lock; + u8 sig; + u8 snr; + + unsigned long next_status_check; + unsigned long status_check_interval; +}; + +static int vp702x_fe_refresh_state(struct vp702x_fe_state *st) +{ + struct vp702x_device_state *dst = st->d->priv; + u8 *buf; + + if (time_after(jiffies, st->next_status_check)) { + mutex_lock(&dst->buf_mutex); + buf = dst->buf; + + vp702x_usb_in_op(st->d, READ_STATUS, 0, 0, buf, 10); + st->lock = buf[4]; + + vp702x_usb_in_op(st->d, READ_TUNER_REG_REQ, 0x11, 0, buf, 1); + st->snr = buf[0]; + + vp702x_usb_in_op(st->d, READ_TUNER_REG_REQ, 0x15, 0, buf, 1); + st->sig = buf[0]; + + mutex_unlock(&dst->buf_mutex); + st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; + } + return 0; +} + +static u8 vp702x_chksum(u8 *buf,int f, int count) +{ + u8 s = 0; + int i; + for (i = f; i < f+count; i++) + s += buf[i]; + return ~s+1; +} + +static int vp702x_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + vp702x_fe_refresh_state(st); + deb_fe("%s\n",__func__); + + if (st->lock == 0) + *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + + if (*status & FE_HAS_LOCK) + st->status_check_interval = 1000; + else + st->status_check_interval = 250; + return 0; +} + +/* not supported by this Frontend */ +static int vp702x_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + vp702x_fe_refresh_state(st); + *ber = 0; + return 0; +} + +/* not supported by this Frontend */ +static int vp702x_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + vp702x_fe_refresh_state(st); + *unc = 0; + return 0; +} + +static int vp702x_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + vp702x_fe_refresh_state(st); + + *strength = (st->sig << 8) | st->sig; + return 0; +} + +static int vp702x_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + u8 _snr; + struct vp702x_fe_state *st = fe->demodulator_priv; + vp702x_fe_refresh_state(st); + + _snr = (st->snr & 0x1f) * 0xff / 0x1f; + *snr = (_snr << 8) | _snr; + return 0; +} + +static int vp702x_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + deb_fe("%s\n",__func__); + tune->min_delay_ms = 2000; + return 0; +} + +static int vp702x_fe_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct vp702x_fe_state *st = fe->demodulator_priv; + struct vp702x_device_state *dst = st->d->priv; + u32 freq = fep->frequency/1000; + /*CalFrequency*/ +/* u16 frequencyRef[16] = { 2, 4, 8, 16, 32, 64, 128, 256, 24, 5, 10, 20, 40, 80, 160, 320 }; */ + u64 sr; + u8 *cmd; + + mutex_lock(&dst->buf_mutex); + + cmd = dst->buf; + memset(cmd, 0, 10); + + cmd[0] = (freq >> 8) & 0x7f; + cmd[1] = freq & 0xff; + cmd[2] = 1; /* divrate == 4 -> frequencyRef[1] -> 1 here */ + + sr = (u64) (fep->symbol_rate/1000) << 20; + do_div(sr,88000); + cmd[3] = (sr >> 12) & 0xff; + cmd[4] = (sr >> 4) & 0xff; + cmd[5] = (sr << 4) & 0xf0; + + deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %lu (%lx)\n", + fep->frequency, freq, freq, fep->symbol_rate, + (unsigned long) sr, (unsigned long) sr); + +/* if (fep->inversion == INVERSION_ON) + cmd[6] |= 0x80; */ + + if (st->voltage == SEC_VOLTAGE_18) + cmd[6] |= 0x40; + +/* if (fep->symbol_rate > 8000000) + cmd[6] |= 0x20; + + if (fep->frequency < 1531000) + cmd[6] |= 0x04; + + if (st->tone_mode == SEC_TONE_ON) + cmd[6] |= 0x01;*/ + + cmd[7] = vp702x_chksum(cmd,0,7); + + st->status_check_interval = 250; + st->next_status_check = jiffies; + + vp702x_usb_inout_op(st->d, cmd, 8, cmd, 10, 100); + + if (cmd[2] == 0 && cmd[3] == 0) + deb_fe("tuning failed.\n"); + else + deb_fe("tuning succeeded.\n"); + + mutex_unlock(&dst->buf_mutex); + + return 0; +} + +static int vp702x_fe_init(struct dvb_frontend *fe) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + deb_fe("%s\n",__func__); + vp702x_usb_in_op(st->d, RESET_TUNER, 0, 0, NULL, 0); + return 0; +} + +static int vp702x_fe_sleep(struct dvb_frontend *fe) +{ + deb_fe("%s\n",__func__); + return 0; +} + +static int vp702x_fe_send_diseqc_msg (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *m) +{ + u8 *cmd; + struct vp702x_fe_state *st = fe->demodulator_priv; + struct vp702x_device_state *dst = st->d->priv; + + deb_fe("%s\n",__func__); + + if (m->msg_len > 4) + return -EINVAL; + + mutex_lock(&dst->buf_mutex); + + cmd = dst->buf; + cmd[1] = SET_DISEQC_CMD; + cmd[2] = m->msg_len; + memcpy(&cmd[3], m->msg, m->msg_len); + cmd[7] = vp702x_chksum(cmd, 0, 7); + + vp702x_usb_inout_op(st->d, cmd, 8, cmd, 10, 100); + + if (cmd[2] == 0 && cmd[3] == 0) + deb_fe("diseqc cmd failed.\n"); + else + deb_fe("diseqc cmd succeeded.\n"); + + mutex_unlock(&dst->buf_mutex); + + return 0; +} + +static int vp702x_fe_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +{ + deb_fe("%s\n",__func__); + return 0; +} + +static int vp702x_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + struct vp702x_device_state *dst = st->d->priv; + u8 *buf; + + deb_fe("%s\n",__func__); + + st->tone_mode = tone; + + if (tone == SEC_TONE_ON) + st->lnb_buf[2] = 0x02; + else + st->lnb_buf[2] = 0x00; + + st->lnb_buf[7] = vp702x_chksum(st->lnb_buf, 0, 7); + + mutex_lock(&dst->buf_mutex); + + buf = dst->buf; + memcpy(buf, st->lnb_buf, 8); + + vp702x_usb_inout_op(st->d, buf, 8, buf, 10, 100); + if (buf[2] == 0 && buf[3] == 0) + deb_fe("set_tone cmd failed.\n"); + else + deb_fe("set_tone cmd succeeded.\n"); + + mutex_unlock(&dst->buf_mutex); + + return 0; +} + +static int vp702x_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t + voltage) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + struct vp702x_device_state *dst = st->d->priv; + u8 *buf; + deb_fe("%s\n",__func__); + + st->voltage = voltage; + + if (voltage != SEC_VOLTAGE_OFF) + st->lnb_buf[4] = 0x01; + else + st->lnb_buf[4] = 0x00; + + st->lnb_buf[7] = vp702x_chksum(st->lnb_buf, 0, 7); + + mutex_lock(&dst->buf_mutex); + + buf = dst->buf; + memcpy(buf, st->lnb_buf, 8); + + vp702x_usb_inout_op(st->d, buf, 8, buf, 10, 100); + if (buf[2] == 0 && buf[3] == 0) + deb_fe("set_voltage cmd failed.\n"); + else + deb_fe("set_voltage cmd succeeded.\n"); + + mutex_unlock(&dst->buf_mutex); + return 0; +} + +static void vp702x_fe_release(struct dvb_frontend* fe) +{ + struct vp702x_fe_state *st = fe->demodulator_priv; + kfree(st); +} + +static struct dvb_frontend_ops vp702x_fe_ops; + +struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d) +{ + struct vp702x_fe_state *s = kzalloc(sizeof(struct vp702x_fe_state), GFP_KERNEL); + if (s == NULL) + goto error; + + s->d = d; + + memcpy(&s->fe.ops,&vp702x_fe_ops,sizeof(struct dvb_frontend_ops)); + s->fe.demodulator_priv = s; + + s->lnb_buf[1] = SET_LNB_POWER; + s->lnb_buf[3] = 0xff; /* 0=tone burst, 2=data burst, ff=off */ + + return &s->fe; +error: + return NULL; +} + + +static struct dvb_frontend_ops vp702x_fe_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, /* kHz for QPSK frontends */ + .frequency_tolerance = 0, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + .release = vp702x_fe_release, + + .init = vp702x_fe_init, + .sleep = vp702x_fe_sleep, + + .set_frontend = vp702x_fe_set_frontend, + .get_tune_settings = vp702x_fe_get_tune_settings, + + .read_status = vp702x_fe_read_status, + .read_ber = vp702x_fe_read_ber, + .read_signal_strength = vp702x_fe_read_signal_strength, + .read_snr = vp702x_fe_read_snr, + .read_ucblocks = vp702x_fe_read_unc_blocks, + + .diseqc_send_master_cmd = vp702x_fe_send_diseqc_msg, + .diseqc_send_burst = vp702x_fe_send_diseqc_burst, + .set_tone = vp702x_fe_set_tone, + .set_voltage = vp702x_fe_set_voltage, +}; diff --git a/drivers/media/usb/dvb-usb/vp702x.c b/drivers/media/usb/dvb-usb/vp702x.c new file mode 100644 index 000000000000..07c673a6e764 --- /dev/null +++ b/drivers/media/usb/dvb-usb/vp702x.c @@ -0,0 +1,444 @@ +/* DVB USB compliant Linux driver for the TwinhanDTV StarBox USB2.0 DVB-S + * receiver. + * + * Copyright (C) 2005 Ralph Metzler + * Metzler Brothers Systementwicklung GbR + * + * Copyright (C) 2005 Patrick Boettcher + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "vp702x.h" +#include + +/* debug */ +int dvb_usb_vp702x_debug; +module_param_named(debug,dvb_usb_vp702x_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct vp702x_adapter_state { + int pid_filter_count; + int pid_filter_can_bypass; + u8 pid_filter_state; +}; + +static int vp702x_usb_in_op_unlocked(struct dvb_usb_device *d, u8 req, + u16 value, u16 index, u8 *b, int blen) +{ + int ret; + + ret = usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_IN, + value, index, b, blen, + 2000); + + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else + ret = 0; + + + deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index); + debug_dump(b,blen,deb_xfer); + + return ret; +} + +int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + + mutex_lock(&d->usb_mutex); + ret = vp702x_usb_in_op_unlocked(d, req, value, index, b, blen); + mutex_unlock(&d->usb_mutex); + + return ret; +} + +int vp702x_usb_out_op_unlocked(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index); + debug_dump(b,blen,deb_xfer); + + if ((ret = usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev,0), + req, + USB_TYPE_VENDOR | USB_DIR_OUT, + value,index,b,blen, + 2000)) != blen) { + warn("usb out operation failed. (%d)",ret); + return -EIO; + } else + return 0; +} + +int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + int ret; + + mutex_lock(&d->usb_mutex); + ret = vp702x_usb_out_op_unlocked(d, req, value, index, b, blen); + mutex_unlock(&d->usb_mutex); + + return ret; +} + +int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec) +{ + int ret; + + if ((ret = mutex_lock_interruptible(&d->usb_mutex))) + return ret; + + ret = vp702x_usb_out_op_unlocked(d, REQUEST_OUT, 0, 0, o, olen); + msleep(msec); + ret = vp702x_usb_in_op_unlocked(d, REQUEST_IN, 0, 0, i, ilen); + + mutex_unlock(&d->usb_mutex); + return ret; +} + +static int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o, + int olen, u8 *i, int ilen, int msec) +{ + struct vp702x_device_state *st = d->priv; + int ret = 0; + u8 *buf; + int buflen = max(olen + 2, ilen + 1); + + ret = mutex_lock_interruptible(&st->buf_mutex); + if (ret < 0) + return ret; + + if (buflen > st->buf_len) { + buf = kmalloc(buflen, GFP_KERNEL); + if (!buf) { + mutex_unlock(&st->buf_mutex); + return -ENOMEM; + } + info("successfully reallocated a bigger buffer"); + kfree(st->buf); + st->buf = buf; + st->buf_len = buflen; + } else { + buf = st->buf; + } + + buf[0] = 0x00; + buf[1] = cmd; + memcpy(&buf[2], o, olen); + + ret = vp702x_usb_inout_op(d, buf, olen+2, buf, ilen+1, msec); + + if (ret == 0) + memcpy(i, &buf[1], ilen); + mutex_unlock(&st->buf_mutex); + + return ret; +} + +static int vp702x_set_pld_mode(struct dvb_usb_adapter *adap, u8 bypass) +{ + int ret; + struct vp702x_device_state *st = adap->dev->priv; + u8 *buf; + + mutex_lock(&st->buf_mutex); + + buf = st->buf; + memset(buf, 0, 16); + + ret = vp702x_usb_in_op(adap->dev, 0xe0, (bypass << 8) | 0x0e, + 0, buf, 16); + mutex_unlock(&st->buf_mutex); + return ret; +} + +static int vp702x_set_pld_state(struct dvb_usb_adapter *adap, u8 state) +{ + int ret; + struct vp702x_device_state *st = adap->dev->priv; + u8 *buf; + + mutex_lock(&st->buf_mutex); + + buf = st->buf; + memset(buf, 0, 16); + ret = vp702x_usb_in_op(adap->dev, 0xe0, (state << 8) | 0x0f, + 0, buf, 16); + + mutex_unlock(&st->buf_mutex); + + return ret; +} + +static int vp702x_set_pid(struct dvb_usb_adapter *adap, u16 pid, u8 id, int onoff) +{ + struct vp702x_adapter_state *st = adap->priv; + struct vp702x_device_state *dst = adap->dev->priv; + u8 *buf; + + if (onoff) + st->pid_filter_state |= (1 << id); + else { + st->pid_filter_state &= ~(1 << id); + pid = 0xffff; + } + + id = 0x10 + id*2; + + vp702x_set_pld_state(adap, st->pid_filter_state); + + mutex_lock(&dst->buf_mutex); + + buf = dst->buf; + memset(buf, 0, 16); + vp702x_usb_in_op(adap->dev, 0xe0, (((pid >> 8) & 0xff) << 8) | (id), 0, buf, 16); + vp702x_usb_in_op(adap->dev, 0xe0, (((pid ) & 0xff) << 8) | (id+1), 0, buf, 16); + + mutex_unlock(&dst->buf_mutex); + + return 0; +} + + +static int vp702x_init_pid_filter(struct dvb_usb_adapter *adap) +{ + struct vp702x_adapter_state *st = adap->priv; + struct vp702x_device_state *dst = adap->dev->priv; + int i; + u8 *b; + + st->pid_filter_count = 8; + st->pid_filter_can_bypass = 1; + st->pid_filter_state = 0x00; + + vp702x_set_pld_mode(adap, 1); /* bypass */ + + for (i = 0; i < st->pid_filter_count; i++) + vp702x_set_pid(adap, 0xffff, i, 1); + + mutex_lock(&dst->buf_mutex); + b = dst->buf; + memset(b, 0, 10); + vp702x_usb_in_op(adap->dev, 0xb5, 3, 0, b, 10); + vp702x_usb_in_op(adap->dev, 0xb5, 0, 0, b, 10); + vp702x_usb_in_op(adap->dev, 0xb5, 1, 0, b, 10); + mutex_unlock(&dst->buf_mutex); + /*vp702x_set_pld_mode(d, 0); // filter */ + + return 0; +} + +static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + return 0; +} + +/* keys for the enclosed remote control */ +static struct rc_map_table rc_map_vp702x_table[] = { + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, +}; + +/* remote control stuff (does not work with my box) */ +static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 *key; + int i; + +/* remove the following return to enabled remote querying */ + return 0; + + key = kmalloc(10, GFP_KERNEL); + if (!key) + return -ENOMEM; + + vp702x_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10); + + deb_rc("remote query key: %x %d\n",key[1],key[1]); + + if (key[1] == 0x44) { + *state = REMOTE_NO_KEY_PRESSED; + kfree(key); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(rc_map_vp702x_table); i++) + if (rc5_custom(&rc_map_vp702x_table[i]) == key[1]) { + *state = REMOTE_KEY_PRESSED; + *event = rc_map_vp702x_table[i].keycode; + break; + } + kfree(key); + return 0; +} + + +static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) +{ + u8 i, *buf; + struct vp702x_device_state *st = d->priv; + + mutex_lock(&st->buf_mutex); + buf = st->buf; + for (i = 6; i < 12; i++) + vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1, &buf[i - 6], 1); + + memcpy(mac, buf, 6); + mutex_unlock(&st->buf_mutex); + return 0; +} + +static int vp702x_frontend_attach(struct dvb_usb_adapter *adap) +{ + u8 buf[10] = { 0 }; + + vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 0, 7, NULL, 0); + + if (vp702x_usb_inout_cmd(adap->dev, GET_SYSTEM_STRING, NULL, 0, + buf, 10, 10)) + return -EIO; + + buf[9] = '\0'; + info("system string: %s",&buf[1]); + + vp702x_init_pid_filter(adap); + + adap->fe_adap[0].fe = vp702x_fe_attach(adap->dev); + vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 1, 7, NULL, 0); + + return 0; +} + +static struct dvb_usb_device_properties vp702x_properties; + +static int vp702x_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct vp702x_device_state *st; + int ret; + + ret = dvb_usb_device_init(intf, &vp702x_properties, + THIS_MODULE, &d, adapter_nr); + if (ret) + goto out; + + st = d->priv; + st->buf_len = 16; + st->buf = kmalloc(st->buf_len, GFP_KERNEL); + if (!st->buf) { + ret = -ENOMEM; + dvb_usb_device_exit(intf); + goto out; + } + mutex_init(&st->buf_mutex); + +out: + return ret; + +} + +static void vp702x_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + struct vp702x_device_state *st = d->priv; + mutex_lock(&st->buf_mutex); + kfree(st->buf); + mutex_unlock(&st->buf_mutex); + dvb_usb_device_exit(intf); +} + +static struct usb_device_id vp702x_usb_table [] = { + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_COLD) }, +// { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_COLD) }, +// { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_WARM) }, + { 0 }, +}; +MODULE_DEVICE_TABLE(usb, vp702x_usb_table); + +static struct dvb_usb_device_properties vp702x_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-vp702x-02.fw", + .no_reconnect = 1, + + .size_of_priv = sizeof(struct vp702x_device_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, + + .streaming_ctrl = vp702x_streaming_ctrl, + .frontend_attach = vp702x_frontend_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + .size_of_priv = sizeof(struct vp702x_adapter_state), + } + }, + .read_mac_address = vp702x_read_mac_addr, + + .rc.legacy = { + .rc_map_table = rc_map_vp702x_table, + .rc_map_size = ARRAY_SIZE(rc_map_vp702x_table), + .rc_interval = 400, + .rc_query = vp702x_rc_query, + }, + + .num_device_descs = 1, + .devices = { + { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7021)", + .cold_ids = { &vp702x_usb_table[0], NULL }, + .warm_ids = { NULL }, + }, +/* { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7020)", + .cold_ids = { &vp702x_usb_table[2], NULL }, + .warm_ids = { &vp702x_usb_table[3], NULL }, + }, +*/ { NULL }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver vp702x_usb_driver = { + .name = "dvb_usb_vp702x", + .probe = vp702x_usb_probe, + .disconnect = vp702x_usb_disconnect, + .id_table = vp702x_usb_table, +}; + +module_usb_driver(vp702x_usb_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/vp702x.h b/drivers/media/usb/dvb-usb/vp702x.h new file mode 100644 index 000000000000..20b90055e7ac --- /dev/null +++ b/drivers/media/usb/dvb-usb/vp702x.h @@ -0,0 +1,113 @@ +#ifndef _DVB_USB_VP7021_H_ +#define _DVB_USB_VP7021_H_ + +#define DVB_USB_LOG_PREFIX "vp702x" +#include "dvb-usb.h" + +extern int dvb_usb_vp702x_debug; +#define deb_info(args...) dprintk(dvb_usb_vp702x_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_vp702x_debug,0x02,args) +#define deb_rc(args...) dprintk(dvb_usb_vp702x_debug,0x04,args) +#define deb_fe(args...) dprintk(dvb_usb_vp702x_debug,0x08,args) + +/* commands are read and written with USB control messages */ + +/* consecutive read/write operation */ +#define REQUEST_OUT 0xB2 +#define REQUEST_IN 0xB3 + +/* the out-buffer of these consecutive operations contain sub-commands when b[0] = 0 + * request: 0xB2; i: 0; v: 0; b[0] = 0, b[1] = subcmd, additional buffer + * the returning buffer looks as follows + * request: 0xB3; i: 0; v: 0; b[0] = 0xB3, additional buffer */ + +#define GET_TUNER_STATUS 0x05 +/* additional in buffer: + * 0 1 2 3 4 5 6 7 8 + * N/A N/A 0x05 signal-quality N/A N/A signal-strength lock==0 N/A */ + +#define GET_SYSTEM_STRING 0x06 +/* additional in buffer: + * 0 1 2 3 4 5 6 7 8 + * N/A 'U' 'S' 'B' '7' '0' '2' 'X' N/A */ + +#define SET_DISEQC_CMD 0x08 +/* additional out buffer: + * 0 1 2 3 4 + * len X1 X2 X3 X4 + * additional in buffer: + * 0 1 2 + * N/A 0 0 b[1] == b[2] == 0 -> success, failure otherwise */ + +#define SET_LNB_POWER 0x09 +/* additional out buffer: + * 0 1 2 + * 0x00 0xff 1 = on, 0 = off + * additional in buffer: + * 0 1 2 + * N/A 0 0 b[1] == b[2] == 0 -> success failure otherwise */ + +#define GET_MAC_ADDRESS 0x0A +/* #define GET_MAC_ADDRESS 0x0B */ +/* additional in buffer: + * 0 1 2 3 4 5 6 7 8 + * N/A N/A 0x0A or 0x0B MAC0 MAC1 MAC2 MAC3 MAC4 MAC5 */ + +#define SET_PID_FILTER 0x11 +/* additional in buffer: + * 0 1 ... 14 15 16 + * PID0_MSB PID0_LSB ... PID7_MSB PID7_LSB PID_active (bits) */ + +/* request: 0xB2; i: 0; v: 0; + * b[0] != 0 -> tune and lock a channel + * 0 1 2 3 4 5 6 7 + * freq0 freq1 divstep srate0 srate1 srate2 flag chksum + */ + +/* one direction requests */ +#define READ_REMOTE_REQ 0xB4 +/* IN i: 0; v: 0; b[0] == request, b[1] == key */ + +#define READ_PID_NUMBER_REQ 0xB5 +/* IN i: 0; v: 0; b[0] == request, b[1] == 0, b[2] = pid number */ + +#define WRITE_EEPROM_REQ 0xB6 +/* OUT i: offset; v: value to write; no extra buffer */ + +#define READ_EEPROM_REQ 0xB7 +/* IN i: bufferlen; v: offset; buffer with bufferlen bytes */ + +#define READ_STATUS 0xB8 +/* IN i: 0; v: 0; bufferlen 10 */ + +#define READ_TUNER_REG_REQ 0xB9 +/* IN i: 0; v: register; b[0] = value */ + +#define READ_FX2_REG_REQ 0xBA +/* IN i: offset; v: 0; b[0] = value */ + +#define WRITE_FX2_REG_REQ 0xBB +/* OUT i: offset; v: value to write; 1 byte extra buffer */ + +#define SET_TUNER_POWER_REQ 0xBC +/* IN i: 0 = power off, 1 = power on */ + +#define WRITE_TUNER_REG_REQ 0xBD +/* IN i: register, v: value to write, no extra buffer */ + +#define RESET_TUNER 0xBE +/* IN i: 0, v: 0, no extra buffer */ + +struct vp702x_device_state { + struct mutex buf_mutex; + int buf_len; + u8 *buf; +}; + + +extern struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d); + +extern int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec); +extern int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); + +#endif diff --git a/drivers/media/usb/dvb-usb/vp7045-fe.c b/drivers/media/usb/dvb-usb/vp7045-fe.c new file mode 100644 index 000000000000..b8825b18c003 --- /dev/null +++ b/drivers/media/usb/dvb-usb/vp7045-fe.c @@ -0,0 +1,190 @@ +/* DVB frontend part of the Linux driver for TwinhanDTV Alpha/MagicBoxII USB2.0 + * DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + * + */ +#include "vp7045.h" + +/* It is a Zarlink MT352 within a Samsung Tuner (DNOS404ZH102A) - 040929 - AAT + * + * Programming is hidden inside the firmware, so set_frontend is very easy. + * Even though there is a Firmware command that one can use to access the demod + * via its registers. This is used for status information. + */ + +struct vp7045_fe_state { + struct dvb_frontend fe; + struct dvb_usb_device *d; +}; + +static int vp7045_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 s0 = vp7045_read_reg(state->d,0x00), + s1 = vp7045_read_reg(state->d,0x01), + s3 = vp7045_read_reg(state->d,0x03); + + *status = 0; + if (s0 & (1 << 4)) + *status |= FE_HAS_CARRIER; + if (s0 & (1 << 1)) + *status |= FE_HAS_VITERBI; + if (s0 & (1 << 5)) + *status |= FE_HAS_LOCK; + if (s1 & (1 << 1)) + *status |= FE_HAS_SYNC; + if (s3 & (1 << 6)) + *status |= FE_HAS_SIGNAL; + + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; + + return 0; +} + +static int vp7045_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + *ber = (vp7045_read_reg(state->d, 0x0D) << 16) | + (vp7045_read_reg(state->d, 0x0E) << 8) | + vp7045_read_reg(state->d, 0x0F); + return 0; +} + +static int vp7045_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + *unc = (vp7045_read_reg(state->d, 0x10) << 8) | + vp7045_read_reg(state->d, 0x11); + return 0; +} + +static int vp7045_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u16 signal = (vp7045_read_reg(state->d, 0x14) << 8) | + vp7045_read_reg(state->d, 0x15); + + *strength = ~signal; + return 0; +} + +static int vp7045_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 _snr = vp7045_read_reg(state->d, 0x09); + *snr = (_snr << 8) | _snr; + return 0; +} + +static int vp7045_fe_init(struct dvb_frontend* fe) +{ + return 0; +} + +static int vp7045_fe_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int vp7045_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int vp7045_fe_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 buf[5]; + u32 freq = fep->frequency / 1000; + + buf[0] = (freq >> 16) & 0xff; + buf[1] = (freq >> 8) & 0xff; + buf[2] = freq & 0xff; + buf[3] = 0; + + switch (fep->bandwidth_hz) { + case 8000000: + buf[4] = 8; + break; + case 7000000: + buf[4] = 7; + break; + case 6000000: + buf[4] = 6; + break; + default: + return -EINVAL; + } + + vp7045_usb_op(state->d,LOCK_TUNER_COMMAND,buf,5,NULL,0,200); + return 0; +} + +static void vp7045_fe_release(struct dvb_frontend* fe) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops vp7045_fe_ops; + +struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d) +{ + struct vp7045_fe_state *s = kzalloc(sizeof(struct vp7045_fe_state), GFP_KERNEL); + if (s == NULL) + goto error; + + s->d = d; + memcpy(&s->fe.ops, &vp7045_fe_ops, sizeof(struct dvb_frontend_ops)); + s->fe.demodulator_priv = s; + + return &s->fe; +error: + return NULL; +} + + +static struct dvb_frontend_ops vp7045_fe_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Twinhan VP7045/46 USB DVB-T", + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 1000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_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 = vp7045_fe_release, + + .init = vp7045_fe_init, + .sleep = vp7045_fe_sleep, + + .set_frontend = vp7045_fe_set_frontend, + .get_tune_settings = vp7045_fe_get_tune_settings, + + .read_status = vp7045_fe_read_status, + .read_ber = vp7045_fe_read_ber, + .read_signal_strength = vp7045_fe_read_signal_strength, + .read_snr = vp7045_fe_read_snr, + .read_ucblocks = vp7045_fe_read_unc_blocks, +}; diff --git a/drivers/media/usb/dvb-usb/vp7045.c b/drivers/media/usb/dvb-usb/vp7045.c new file mode 100644 index 000000000000..d750724132ee --- /dev/null +++ b/drivers/media/usb/dvb-usb/vp7045.c @@ -0,0 +1,302 @@ +/* DVB USB compliant Linux driver for the + * - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver + * - DigitalNow TinyUSB2 DVB-t receiver + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "vp7045.h" + +/* debug */ +static int dvb_usb_vp7045_debug; +module_param_named(debug,dvb_usb_vp7045_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args) +#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args) + +int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec) +{ + int ret = 0; + u8 *buf = d->priv; + + buf[0] = cmd; + + if (outlen > 19) + outlen = 19; + + if (inlen > 11) + inlen = 11; + + ret = mutex_lock_interruptible(&d->usb_mutex); + if (ret) + return ret; + + if (out != NULL && outlen > 0) + memcpy(&buf[1], out, outlen); + + deb_xfer("out buffer: "); + debug_dump(buf, outlen+1, deb_xfer); + + + if (usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev,0), + TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, + buf, 20, 2000) != 20) { + err("USB control message 'out' went wrong."); + ret = -EIO; + goto unlock; + } + + msleep(msec); + + if (usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev,0), + TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, + buf, 12, 2000) != 12) { + err("USB control message 'in' went wrong."); + ret = -EIO; + goto unlock; + } + + deb_xfer("in buffer: "); + debug_dump(buf, 12, deb_xfer); + + if (in != NULL && inlen > 0) + memcpy(in, &buf[1], inlen); + +unlock: + mutex_unlock(&d->usb_mutex); + + return ret; +} + +u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg) +{ + u8 obuf[2] = { 0 },v; + obuf[1] = reg; + + vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30); + + return v; +} + +static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 v = onoff; + return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150); +} + +/* remote control stuff */ + +/* The keymapping struct. Somehow this should be loaded to the driver, but + * currently it is hardcoded. */ +static struct rc_map_table rc_map_vp7045_table[] = { + { 0x0016, KEY_POWER }, + { 0x0010, KEY_MUTE }, + { 0x0003, KEY_1 }, + { 0x0001, KEY_2 }, + { 0x0006, KEY_3 }, + { 0x0009, KEY_4 }, + { 0x001d, KEY_5 }, + { 0x001f, KEY_6 }, + { 0x000d, KEY_7 }, + { 0x0019, KEY_8 }, + { 0x001b, KEY_9 }, + { 0x0015, KEY_0 }, + { 0x0005, KEY_CHANNELUP }, + { 0x0002, KEY_CHANNELDOWN }, + { 0x001e, KEY_VOLUMEUP }, + { 0x000a, KEY_VOLUMEDOWN }, + { 0x0011, KEY_RECORD }, + { 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x0014, KEY_PLAY }, + { 0x001a, KEY_STOP }, + { 0x0040, KEY_REWIND }, + { 0x0012, KEY_FASTFORWARD }, + { 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x004c, KEY_PAUSE }, + { 0x004d, KEY_SCREEN }, /* Full screen mode. */ + { 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + { 0x000c, KEY_CANCEL }, /* Cancel */ + { 0x001c, KEY_EPG }, /* EPG */ + { 0x0000, KEY_TAB }, /* Tab */ + { 0x0048, KEY_INFO }, /* Preview */ + { 0x0004, KEY_LIST }, /* RecordList */ + { 0x000f, KEY_TEXT }, /* Teletext */ + { 0x0041, KEY_PREVIOUSSONG }, + { 0x0042, KEY_NEXTSONG }, + { 0x004b, KEY_UP }, + { 0x0051, KEY_DOWN }, + { 0x004e, KEY_LEFT }, + { 0x0052, KEY_RIGHT }, + { 0x004f, KEY_ENTER }, + { 0x0013, KEY_CANCEL }, + { 0x004a, KEY_CLEAR }, + { 0x0054, KEY_PRINT }, /* Capture */ + { 0x0043, KEY_SUBTITLE }, /* Subtitle/CC */ + { 0x0008, KEY_VIDEO }, /* A/V */ + { 0x0007, KEY_SLEEP }, /* Hibernate */ + { 0x0045, KEY_ZOOM }, /* Zoom+ */ + { 0x0018, KEY_RED}, + { 0x0053, KEY_GREEN}, + { 0x005e, KEY_YELLOW}, + { 0x005f, KEY_BLUE} +}; + +static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key; + int i; + vp7045_usb_op(d,RC_VAL_READ,NULL,0,&key,1,20); + + deb_rc("remote query key: %x %d\n",key,key); + + if (key == 0x44) { + *state = REMOTE_NO_KEY_PRESSED; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(rc_map_vp7045_table); i++) + if (rc5_data(&rc_map_vp7045_table[i]) == key) { + *state = REMOTE_KEY_PRESSED; + *event = rc_map_vp7045_table[i].keycode; + break; + } + return 0; +} + +static int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset) +{ + int i = 0; + u8 v,br[2]; + for (i=0; i < len; i++) { + v = offset + i; + vp7045_usb_op(d,GET_EE_VALUE,&v,1,br,2,5); + buf[i] = br[1]; + } + deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ",offset, i); + debug_dump(buf,i,deb_info); + return 0; +} + +static int vp7045_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) +{ + return vp7045_read_eeprom(d,mac, 6, MAC_0_ADDR); +} + +static int vp7045_frontend_attach(struct dvb_usb_adapter *adap) +{ + u8 buf[255] = { 0 }; + + vp7045_usb_op(adap->dev,VENDOR_STRING_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("firmware says: %s ",buf); + + vp7045_usb_op(adap->dev,PRODUCT_STRING_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("%s ",buf); + + vp7045_usb_op(adap->dev,FW_VERSION_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("v%s\n",buf); + +/* Dump the EEPROM */ +/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */ + + adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev); + + return 0; +} + +static struct dvb_usb_device_properties vp7045_properties; + +static int vp7045_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &vp7045_properties, + THIS_MODULE, NULL, adapter_nr); +} + +static struct usb_device_id vp7045_usb_table [] = { + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_COLD) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_WARM) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_COLD) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_WARM) }, + { 0 }, +}; +MODULE_DEVICE_TABLE(usb, vp7045_usb_table); + +static struct dvb_usb_device_properties vp7045_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-vp7045-01.fw", + .size_of_priv = 20, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .frontend_attach = vp7045_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + }}, + } + }, + .power_ctrl = vp7045_power_ctrl, + .read_mac_address = vp7045_read_mac_addr, + + .rc.legacy = { + .rc_interval = 400, + .rc_map_table = rc_map_vp7045_table, + .rc_map_size = ARRAY_SIZE(rc_map_vp7045_table), + .rc_query = vp7045_rc_query, + }, + + .num_device_descs = 2, + .devices = { + { .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)", + .cold_ids = { &vp7045_usb_table[0], NULL }, + .warm_ids = { &vp7045_usb_table[1], NULL }, + }, + { .name = "DigitalNow TinyUSB 2 DVB-t Receiver", + .cold_ids = { &vp7045_usb_table[2], NULL }, + .warm_ids = { &vp7045_usb_table[3], NULL }, + }, + { NULL }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver vp7045_usb_driver = { + .name = "dvb_usb_vp7045", + .probe = vp7045_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = vp7045_usb_table, +}; + +module_usb_driver(vp7045_usb_driver); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/vp7045.h b/drivers/media/usb/dvb-usb/vp7045.h new file mode 100644 index 000000000000..cf5ec46f8bb1 --- /dev/null +++ b/drivers/media/usb/dvb-usb/vp7045.h @@ -0,0 +1,70 @@ +/* Common header-file of the Linux driver for the TwinhanDTV Alpha/MagicBoxII + * USB2.0 DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_VP7045_H_ +#define _DVB_USB_VP7045_H_ + +#define DVB_USB_LOG_PREFIX "vp7045" +#include "dvb-usb.h" + +/* vp7045 commands */ + +/* Twinhan Vendor requests */ +#define TH_COMMAND_IN 0xC0 +#define TH_COMMAND_OUT 0xC1 + +/* command bytes */ +#define TUNER_REG_READ 0x03 +#define TUNER_REG_WRITE 0x04 + +#define RC_VAL_READ 0x05 + #define RC_NO_KEY 0x44 + +#define SET_TUNER_POWER 0x06 +#define CHECK_TUNER_POWER 0x12 + #define Tuner_Power_ON 1 + #define Tuner_Power_OFF 0 + +#define GET_USB_SPEED 0x07 + +#define LOCK_TUNER_COMMAND 0x09 + +#define TUNER_SIGNAL_READ 0x0A + +/* FX2 eeprom */ +#define SET_EE_VALUE 0x10 +#define GET_EE_VALUE 0x11 + #define FX2_ID_ADDR 0x00 + #define VID_MSB_ADDR 0x02 + #define VID_LSB_ADDR 0x01 + #define PID_MSB_ADDR 0x04 + #define PID_LSB_ADDR 0x03 + #define MAC_0_ADDR 0x07 + #define MAC_1_ADDR 0x08 + #define MAC_2_ADDR 0x09 + #define MAC_3_ADDR 0x0a + #define MAC_4_ADDR 0x0b + #define MAC_5_ADDR 0x0c + +#define RESET_FX2 0x13 + +#define FW_VERSION_READ 0x0B +#define VENDOR_STRING_READ 0x0C +#define PRODUCT_STRING_READ 0x0D +#define FW_BCD_VERSION_READ 0x14 + +extern struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d); +extern int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen,int msec); +extern u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg); + +#endif diff --git a/drivers/media/usb/siano/Kconfig b/drivers/media/usb/siano/Kconfig new file mode 100644 index 000000000000..bc6456eb2c4f --- /dev/null +++ b/drivers/media/usb/siano/Kconfig @@ -0,0 +1,34 @@ +# +# Siano Mobile Silicon Digital TV device configuration +# + +config SMS_SIANO_MDTV + 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 new file mode 100644 index 000000000000..14756bdb6eaa --- /dev/null +++ b/drivers/media/usb/siano/Makefile @@ -0,0 +1,11 @@ + +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 += $(extra-cflags-y) $(extra-cflags-m) + diff --git a/drivers/media/usb/siano/sms-cards.c b/drivers/media/usb/siano/sms-cards.c new file mode 100644 index 000000000000..680c781c8dd6 --- /dev/null +++ b/drivers/media/usb/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/usb/siano/sms-cards.h b/drivers/media/usb/siano/sms-cards.h new file mode 100644 index 000000000000..d8cdf756f7cf --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smscoreapi.c b/drivers/media/usb/siano/smscoreapi.c new file mode 100644 index 000000000000..9cc55546cc30 --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smscoreapi.h b/drivers/media/usb/siano/smscoreapi.h new file mode 100644 index 000000000000..c592ae090397 --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smsdvb.c b/drivers/media/usb/siano/smsdvb.c new file mode 100644 index 000000000000..aa77e54a8fae --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smsendian.c b/drivers/media/usb/siano/smsendian.c new file mode 100644 index 000000000000..e2657c2f0109 --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smsendian.h b/drivers/media/usb/siano/smsendian.h new file mode 100644 index 000000000000..1624d6fd367b --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smsir.c b/drivers/media/usb/siano/smsir.c new file mode 100644 index 000000000000..37bc5c4b8ad8 --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smsir.h b/drivers/media/usb/siano/smsir.h new file mode 100644 index 000000000000..ae92b3a8587e --- /dev/null +++ b/drivers/media/usb/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/usb/siano/smssdio.c b/drivers/media/usb/siano/smssdio.c new file mode 100644 index 000000000000..d6f3f100699a --- /dev/null +++ b/drivers/media/usb/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/smsusb.c b/drivers/media/usb/siano/smsusb.c new file mode 100644 index 000000000000..664e460f247b --- /dev/null +++ b/drivers/media/usb/siano/smsusb.c @@ -0,0 +1,568 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2005-2009, 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 . + +****************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "smscoreapi.h" +#include "sms-cards.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))"); + +#define USB1_BUFFER_SIZE 0x1000 +#define USB2_BUFFER_SIZE 0x4000 + +#define MAX_BUFFERS 50 +#define MAX_URBS 10 + +struct smsusb_device_t; + +struct smsusb_urb_t { + struct smscore_buffer_t *cb; + struct smsusb_device_t *dev; + + struct urb urb; +}; + +struct smsusb_device_t { + struct usb_device *udev; + struct smscore_device_t *coredev; + + struct smsusb_urb_t surbs[MAX_URBS]; + + int response_alignment; + int buffer_size; +}; + +static int smsusb_submit_urb(struct smsusb_device_t *dev, + struct smsusb_urb_t *surb); + +static void smsusb_onresponse(struct urb *urb) +{ + struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context; + struct smsusb_device_t *dev = surb->dev; + + if (urb->status == -ESHUTDOWN) { + sms_err("error, urb status %d (-ESHUTDOWN), %d bytes", + urb->status, urb->actual_length); + return; + } + + if ((urb->actual_length > 0) && (urb->status == 0)) { + struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p; + + smsendian_handle_message_header(phdr); + if (urb->actual_length >= phdr->msgLength) { + surb->cb->size = phdr->msgLength; + + if (dev->response_alignment && + (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) { + + surb->cb->offset = + dev->response_alignment + + ((phdr->msgFlags >> 8) & 3); + + /* sanity check */ + if (((int) phdr->msgLength + + surb->cb->offset) > urb->actual_length) { + sms_err("invalid response " + "msglen %d offset %d " + "size %d", + phdr->msgLength, + surb->cb->offset, + urb->actual_length); + goto exit_and_resubmit; + } + + /* move buffer pointer and + * copy header to its new location */ + memcpy((char *) phdr + surb->cb->offset, + phdr, sizeof(struct SmsMsgHdr_ST)); + } else + surb->cb->offset = 0; + + smscore_onresponse(dev->coredev, surb->cb); + surb->cb = NULL; + } else { + sms_err("invalid response " + "msglen %d actual %d", + phdr->msgLength, urb->actual_length); + } + } else + sms_err("error, urb status %d, %d bytes", + urb->status, urb->actual_length); + + +exit_and_resubmit: + smsusb_submit_urb(dev, surb); +} + +static int smsusb_submit_urb(struct smsusb_device_t *dev, + struct smsusb_urb_t *surb) +{ + if (!surb->cb) { + surb->cb = smscore_getbuffer(dev->coredev); + if (!surb->cb) { + sms_err("smscore_getbuffer(...) returned NULL"); + return -ENOMEM; + } + } + + usb_fill_bulk_urb( + &surb->urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, 0x81), + surb->cb->p, + dev->buffer_size, + smsusb_onresponse, + surb + ); + surb->urb.transfer_dma = surb->cb->phys; + surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return usb_submit_urb(&surb->urb, GFP_ATOMIC); +} + +static void smsusb_stop_streaming(struct smsusb_device_t *dev) +{ + int i; + + for (i = 0; i < MAX_URBS; i++) { + usb_kill_urb(&dev->surbs[i].urb); + + if (dev->surbs[i].cb) { + smscore_putbuffer(dev->coredev, dev->surbs[i].cb); + dev->surbs[i].cb = NULL; + } + } +} + +static int smsusb_start_streaming(struct smsusb_device_t *dev) +{ + int i, rc; + + for (i = 0; i < MAX_URBS; i++) { + rc = smsusb_submit_urb(dev, &dev->surbs[i]); + if (rc < 0) { + sms_err("smsusb_submit_urb(...) failed"); + smsusb_stop_streaming(dev); + break; + } + } + + return rc; +} + +static int smsusb_sendrequest(void *context, void *buffer, size_t size) +{ + struct smsusb_device_t *dev = (struct smsusb_device_t *) context; + int dummy; + + smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer); + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + buffer, size, &dummy, 1000); +} + +static char *smsusb1_fw_lkup[] = { + "dvbt_stellar_usb.inp", + "dvbh_stellar_usb.inp", + "tdmb_stellar_usb.inp", + "none", + "dvbt_bda_stellar_usb.inp", +}; + +static inline char *sms_get_fw_name(int mode, int board_id) +{ + char **fw = sms_get_board(board_id)->fw; + return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode]; +} + +static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id) +{ + const struct firmware *fw; + u8 *fw_buffer; + int rc, dummy; + char *fw_filename; + + if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) { + sms_err("invalid firmware id specified %d", id); + return -EINVAL; + } + + fw_filename = sms_get_fw_name(id, board_id); + + rc = request_firmware(&fw, fw_filename, &udev->dev); + if (rc < 0) { + sms_warn("failed to open \"%s\" mode %d, " + "trying again with default firmware", fw_filename, id); + + fw_filename = smsusb1_fw_lkup[id]; + rc = request_firmware(&fw, fw_filename, &udev->dev); + if (rc < 0) { + sms_warn("failed to open \"%s\" mode %d", + fw_filename, id); + + return rc; + } + } + + fw_buffer = kmalloc(fw->size, GFP_KERNEL); + if (fw_buffer) { + memcpy(fw_buffer, fw->data, fw->size); + + rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2), + fw_buffer, fw->size, &dummy, 1000); + + sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc); + + kfree(fw_buffer); + } else { + sms_err("failed to allocate firmware buffer"); + rc = -ENOMEM; + } + sms_info("read FW %s, size=%zd", fw_filename, fw->size); + + release_firmware(fw); + + return rc; +} + +static void smsusb1_detectmode(void *context, int *mode) +{ + char *product_string = + ((struct smsusb_device_t *) context)->udev->product; + + *mode = DEVICE_MODE_NONE; + + if (!product_string) { + product_string = "none"; + sms_err("product string not found"); + } else if (strstr(product_string, "DVBH")) + *mode = 1; + else if (strstr(product_string, "BDA")) + *mode = 4; + else if (strstr(product_string, "DVBT")) + *mode = 0; + else if (strstr(product_string, "TDMB")) + *mode = 2; + + sms_info("%d \"%s\"", *mode, product_string); +} + +static int smsusb1_setmode(void *context, int mode) +{ + struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, + sizeof(struct SmsMsgHdr_ST), 0 }; + + if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { + sms_err("invalid firmware id specified %d", mode); + return -EINVAL; + } + + return smsusb_sendrequest(context, &Msg, sizeof(Msg)); +} + +static void smsusb_term_device(struct usb_interface *intf) +{ + struct smsusb_device_t *dev = usb_get_intfdata(intf); + + if (dev) { + smsusb_stop_streaming(dev); + + /* unregister from smscore */ + if (dev->coredev) + smscore_unregister_device(dev->coredev); + + sms_info("device %p destroyed", dev); + kfree(dev); + } + + usb_set_intfdata(intf, NULL); +} + +static int smsusb_init_device(struct usb_interface *intf, int board_id) +{ + struct smsdevice_params_t params; + struct smsusb_device_t *dev; + int i, rc; + + /* create device object */ + dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL); + if (!dev) { + sms_err("kzalloc(sizeof(struct smsusb_device_t) failed"); + return -ENOMEM; + } + + memset(¶ms, 0, sizeof(params)); + usb_set_intfdata(intf, dev); + dev->udev = interface_to_usbdev(intf); + + params.device_type = sms_get_board(board_id)->type; + + switch (params.device_type) { + case SMS_STELLAR: + dev->buffer_size = USB1_BUFFER_SIZE; + + params.setmode_handler = smsusb1_setmode; + params.detectmode_handler = smsusb1_detectmode; + break; + default: + sms_err("Unspecified sms device type!"); + /* fall-thru */ + case SMS_NOVA_A0: + case SMS_NOVA_B0: + case SMS_VEGA: + dev->buffer_size = USB2_BUFFER_SIZE; + dev->response_alignment = + le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) - + sizeof(struct SmsMsgHdr_ST); + + params.flags |= SMS_DEVICE_FAMILY2; + break; + } + + params.device = &dev->udev->dev; + params.buffer_size = dev->buffer_size; + params.num_buffers = MAX_BUFFERS; + params.sendrequest_handler = smsusb_sendrequest; + params.context = dev; + usb_make_path(dev->udev, params.devpath, sizeof(params.devpath)); + + /* register in smscore */ + rc = smscore_register_device(¶ms, &dev->coredev); + if (rc < 0) { + sms_err("smscore_register_device(...) failed, rc %d", rc); + smsusb_term_device(intf); + return rc; + } + + smscore_set_board_id(dev->coredev, board_id); + + /* initialize urbs */ + for (i = 0; i < MAX_URBS; i++) { + dev->surbs[i].dev = dev; + usb_init_urb(&dev->surbs[i].urb); + } + + sms_info("smsusb_start_streaming(...)."); + rc = smsusb_start_streaming(dev); + if (rc < 0) { + sms_err("smsusb_start_streaming(...) failed"); + smsusb_term_device(intf); + return rc; + } + + rc = smscore_start_device(dev->coredev); + if (rc < 0) { + sms_err("smscore_start_device(...) failed"); + smsusb_term_device(intf); + return rc; + } + + sms_info("device %p created", dev); + + return rc; +} + +static int __devinit smsusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + char devpath[32]; + int i, rc; + + rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); + rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); + + if (intf->num_altsetting > 0) { + rc = usb_set_interface( + udev, intf->cur_altsetting->desc.bInterfaceNumber, 0); + if (rc < 0) { + sms_err("usb_set_interface failed, rc %d", rc); + return rc; + } + } + + sms_info("smsusb_probe %d", + intf->cur_altsetting->desc.bInterfaceNumber); + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) + sms_info("endpoint %d %02x %02x %d", i, + intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, + intf->cur_altsetting->endpoint[i].desc.bmAttributes, + intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); + + if ((udev->actconfig->desc.bNumInterfaces == 2) && + (intf->cur_altsetting->desc.bInterfaceNumber == 0)) { + sms_err("rom interface 0 is not used"); + return -ENODEV; + } + + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + snprintf(devpath, sizeof(devpath), "usb\\%d-%s", + udev->bus->busnum, udev->devpath); + sms_info("stellar device was found."); + return smsusb1_load_firmware( + udev, smscore_registry_getmode(devpath), + id->driver_info); + } + + rc = smsusb_init_device(intf, id->driver_info); + sms_info("rc %d", rc); + sms_board_load_modules(id->driver_info); + return rc; +} + +static void smsusb_disconnect(struct usb_interface *intf) +{ + smsusb_term_device(intf); +} + +static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct smsusb_device_t *dev = usb_get_intfdata(intf); + printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event); + smsusb_stop_streaming(dev); + return 0; +} + +static int smsusb_resume(struct usb_interface *intf) +{ + int rc, i; + struct smsusb_device_t *dev = usb_get_intfdata(intf); + struct usb_device *udev = interface_to_usbdev(intf); + + printk(KERN_INFO "%s: Entering.\n", __func__); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) + printk(KERN_INFO "endpoint %d %02x %02x %d\n", i, + intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, + intf->cur_altsetting->endpoint[i].desc.bmAttributes, + intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); + + if (intf->num_altsetting > 0) { + rc = usb_set_interface(udev, + intf->cur_altsetting->desc. + bInterfaceNumber, 0); + if (rc < 0) { + printk(KERN_INFO "%s usb_set_interface failed, " + "rc %d\n", __func__, rc); + return rc; + } + } + + smsusb_start_streaming(dev); + return 0; +} + +static const struct usb_device_id smsusb_id_table[] __devinitconst = { + { USB_DEVICE(0x187f, 0x0010), + .driver_info = SMS1XXX_BOARD_SIANO_STELLAR }, + { USB_DEVICE(0x187f, 0x0100), + .driver_info = SMS1XXX_BOARD_SIANO_STELLAR }, + { USB_DEVICE(0x187f, 0x0200), + .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A }, + { USB_DEVICE(0x187f, 0x0201), + .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B }, + { USB_DEVICE(0x187f, 0x0300), + .driver_info = SMS1XXX_BOARD_SIANO_VEGA }, + { USB_DEVICE(0x2040, 0x1700), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT }, + { USB_DEVICE(0x2040, 0x1800), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A }, + { USB_DEVICE(0x2040, 0x1801), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, + { USB_DEVICE(0x2040, 0x2000), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2009), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 }, + { USB_DEVICE(0x2040, 0x200a), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2010), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2011), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2019), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x5500), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5510), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5520), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5530), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5580), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5590), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x187f, 0x0202), + .driver_info = SMS1XXX_BOARD_SIANO_NICE }, + { USB_DEVICE(0x187f, 0x0301), + .driver_info = SMS1XXX_BOARD_SIANO_VENICE }, + { USB_DEVICE(0x2040, 0xb900), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xb910), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xb980), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xb990), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc000), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc010), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc080), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc090), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc0a0), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xf5a0), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { } /* Terminating entry */ + }; + +MODULE_DEVICE_TABLE(usb, smsusb_id_table); + +static struct usb_driver smsusb_driver = { + .name = "smsusb", + .probe = smsusb_probe, + .disconnect = smsusb_disconnect, + .id_table = smsusb_id_table, + + .suspend = smsusb_suspend, + .resume = smsusb_resume, +}; + +module_usb_driver(smsusb_driver); + +MODULE_DESCRIPTION("Driver for the Siano SMS1xxx USB dongle"); +MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/ttusb-budget/Kconfig b/drivers/media/usb/ttusb-budget/Kconfig new file mode 100644 index 000000000000..2663ae39b886 --- /dev/null +++ b/drivers/media/usb/ttusb-budget/Kconfig @@ -0,0 +1,18 @@ +config DVB_TTUSB_BUDGET + tristate "Technotrend/Hauppauge Nova-USB devices" + depends on DVB_CORE && USB && I2C && PCI + select DVB_CX22700 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_VES1820 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + help + Support for external USB adapters designed by Technotrend and + produced by Hauppauge, shipped under the brand name 'Nova-USB'. + + These devices don't have a MPEG decoder built in, so you need + an external software decoder to watch TV. + + Say Y if you own such a device and want to use it. diff --git a/drivers/media/usb/ttusb-budget/Makefile b/drivers/media/usb/ttusb-budget/Makefile new file mode 100644 index 000000000000..f47bbf62dcde --- /dev/null +++ b/drivers/media/usb/ttusb-budget/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o + +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c new file mode 100644 index 000000000000..5b682cc4c814 --- /dev/null +++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c @@ -0,0 +1,1816 @@ +/* + * TTUSB DVB driver + * + * Copyright (c) 2002 Holger Waechtler + * Copyright (c) 2003 Felix Domke + * + * This program is free software; 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "ves1820.h" +#include "cx22700.h" +#include "tda1004x.h" +#include "stv0299.h" +#include "tda8083.h" +#include "stv0297.h" +#include "lnbp21.h" + +#include +#include +#include + +/* + TTUSB_HWSECTIONS: + the DSP supports filtering in hardware, however, since the "muxstream" + is a bit braindead (no matching channel masks or no matching filter mask), + we won't support this - yet. it doesn't event support negative filters, + so the best way is maybe to keep TTUSB_HWSECTIONS undef'd and just + parse TS data. USB bandwidth will be a problem when having large + datastreams, especially for dvb-net, but hey, that's not my problem. + + TTUSB_DISEQC, TTUSB_TONE: + let the STC do the diseqc/tone stuff. this isn't supported at least with + my TTUSB, so let it undef'd unless you want to implement another + frontend. never tested. + + debug: + define it to > 3 for really hardcore debugging. you probably don't want + this unless the device doesn't load at all. > 2 for bandwidth statistics. +*/ + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(x...) do { if (debug) printk(KERN_DEBUG x); } while (0) + +#define ISO_BUF_COUNT 4 +#define FRAMES_PER_ISO_BUF 4 +#define ISO_FRAME_SIZE 912 +#define TTUSB_MAXCHANNEL 32 +#ifdef TTUSB_HWSECTIONS +#define TTUSB_MAXFILTER 16 /* ??? */ +#endif + +#define TTUSB_REV_2_2 0x22 +#define TTUSB_BUDGET_NAME "ttusb_stc_fw" + +/** + * since we're casting (struct ttusb*) <-> (struct dvb_demux*) around + * the dvb_demux field must be the first in struct!! + */ +struct ttusb { + struct dvb_demux dvb_demux; + struct dmxdev dmxdev; + struct dvb_net dvbnet; + + /* and one for USB access. */ + struct mutex semi2c; + struct mutex semusb; + + struct dvb_adapter adapter; + struct usb_device *dev; + + struct i2c_adapter i2c_adap; + + int disconnecting; + int iso_streaming; + + unsigned int bulk_out_pipe; + unsigned int bulk_in_pipe; + unsigned int isoc_in_pipe; + + void *iso_buffer; + dma_addr_t iso_dma_handle; + + struct urb *iso_urb[ISO_BUF_COUNT]; + + int running_feed_count; + int last_channel; + int last_filter; + + u8 c; /* transaction counter, wraps around... */ + fe_sec_tone_mode_t tone; + fe_sec_voltage_t voltage; + + int mux_state; // 0..2 - MuxSyncWord, 3 - nMuxPacks, 4 - muxpack + u8 mux_npacks; + u8 muxpack[256 + 8]; + int muxpack_ptr, muxpack_len; + + int insync; + + int cc; /* MuxCounter - will increment on EVERY MUX PACKET */ + /* (including stuffing. yes. really.) */ + + u8 last_result[32]; + + int revision; + + struct dvb_frontend* fe; +}; + +/* ugly workaround ... don't know why it's necessary to read */ +/* all result codes. */ + +static int ttusb_cmd(struct ttusb *ttusb, + const u8 * data, int len, int needresult) +{ + int actual_len; + int err; + int i; + + if (debug >= 3) { + printk(KERN_DEBUG ">"); + for (i = 0; i < len; ++i) + printk(KERN_CONT " %02x", data[i]); + printk(KERN_CONT "\n"); + } + + if (mutex_lock_interruptible(&ttusb->semusb) < 0) + return -EAGAIN; + + err = usb_bulk_msg(ttusb->dev, ttusb->bulk_out_pipe, + (u8 *) data, len, &actual_len, 1000); + if (err != 0) { + dprintk("%s: usb_bulk_msg(send) failed, err == %i!\n", + __func__, err); + mutex_unlock(&ttusb->semusb); + return err; + } + if (actual_len != len) { + dprintk("%s: only wrote %d of %d bytes\n", __func__, + actual_len, len); + mutex_unlock(&ttusb->semusb); + return -1; + } + + err = usb_bulk_msg(ttusb->dev, ttusb->bulk_in_pipe, + ttusb->last_result, 32, &actual_len, 1000); + + if (err != 0) { + printk("%s: failed, receive error %d\n", __func__, + err); + mutex_unlock(&ttusb->semusb); + return err; + } + + if (debug >= 3) { + actual_len = ttusb->last_result[3] + 4; + printk(KERN_DEBUG "<"); + for (i = 0; i < actual_len; ++i) + printk(KERN_CONT " %02x", ttusb->last_result[i]); + printk(KERN_CONT "\n"); + } + + if (!needresult) + mutex_unlock(&ttusb->semusb); + return 0; +} + +static int ttusb_result(struct ttusb *ttusb, u8 * data, int len) +{ + memcpy(data, ttusb->last_result, len); + mutex_unlock(&ttusb->semusb); + return 0; +} + +static int ttusb_i2c_msg(struct ttusb *ttusb, + u8 addr, u8 * snd_buf, u8 snd_len, u8 * rcv_buf, + u8 rcv_len) +{ + u8 b[0x28]; + u8 id = ++ttusb->c; + int i, err; + + if (snd_len > 0x28 - 7 || rcv_len > 0x20 - 7) + return -EINVAL; + + b[0] = 0xaa; + b[1] = id; + b[2] = 0x31; + b[3] = snd_len + 3; + b[4] = addr << 1; + b[5] = snd_len; + b[6] = rcv_len; + + for (i = 0; i < snd_len; i++) + b[7 + i] = snd_buf[i]; + + err = ttusb_cmd(ttusb, b, snd_len + 7, 1); + + if (err) + return -EREMOTEIO; + + err = ttusb_result(ttusb, b, 0x20); + + /* check if the i2c transaction was successful */ + if ((snd_len != b[5]) || (rcv_len != b[6])) return -EREMOTEIO; + + if (rcv_len > 0) { + + if (err || b[0] != 0x55 || b[1] != id) { + dprintk + ("%s: usb_bulk_msg(recv) failed, err == %i, id == %02x, b == ", + __func__, err, id); + return -EREMOTEIO; + } + + for (i = 0; i < rcv_len; i++) + rcv_buf[i] = b[7 + i]; + } + + return rcv_len; +} + +static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) +{ + struct ttusb *ttusb = i2c_get_adapdata(adapter); + int i = 0; + int inc; + + if (mutex_lock_interruptible(&ttusb->semi2c) < 0) + return -EAGAIN; + + while (i < num) { + u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf; + int err; + + if (num > i + 1 && (msg[i + 1].flags & I2C_M_RD)) { + addr = msg[i].addr; + snd_buf = msg[i].buf; + snd_len = msg[i].len; + rcv_buf = msg[i + 1].buf; + rcv_len = msg[i + 1].len; + inc = 2; + } else { + addr = msg[i].addr; + snd_buf = msg[i].buf; + snd_len = msg[i].len; + rcv_buf = NULL; + rcv_len = 0; + inc = 1; + } + + err = ttusb_i2c_msg(ttusb, addr, + snd_buf, snd_len, rcv_buf, rcv_len); + + if (err < rcv_len) { + dprintk("%s: i == %i\n", __func__, i); + break; + } + + i += inc; + } + + mutex_unlock(&ttusb->semi2c); + return i; +} + +static int ttusb_boot_dsp(struct ttusb *ttusb) +{ + const struct firmware *fw; + int i, err; + u8 b[40]; + + err = request_firmware(&fw, "ttusb-budget/dspbootcode.bin", + &ttusb->dev->dev); + if (err) { + printk(KERN_ERR "ttusb-budget: failed to request firmware\n"); + return err; + } + + /* BootBlock */ + b[0] = 0xaa; + b[2] = 0x13; + b[3] = 28; + + /* upload dsp code in 32 byte steps (36 didn't work for me ...) */ + /* 32 is max packet size, no messages should be splitted. */ + for (i = 0; i < fw->size; i += 28) { + memcpy(&b[4], &fw->data[i], 28); + + b[1] = ++ttusb->c; + + err = ttusb_cmd(ttusb, b, 32, 0); + if (err) + goto done; + } + + /* last block ... */ + b[1] = ++ttusb->c; + b[2] = 0x13; + b[3] = 0; + + err = ttusb_cmd(ttusb, b, 4, 0); + if (err) + goto done; + + /* BootEnd */ + b[1] = ++ttusb->c; + b[2] = 0x14; + b[3] = 0; + + err = ttusb_cmd(ttusb, b, 4, 0); + + done: + release_firmware(fw); + if (err) { + dprintk("%s: usb_bulk_msg() failed, return value %i!\n", + __func__, err); + } + + return err; +} + +static int ttusb_set_channel(struct ttusb *ttusb, int chan_id, int filter_type, + int pid) +{ + int err; + /* SetChannel */ + u8 b[] = { 0xaa, ++ttusb->c, 0x22, 4, chan_id, filter_type, + (pid >> 8) & 0xff, pid & 0xff + }; + + err = ttusb_cmd(ttusb, b, sizeof(b), 0); + return err; +} + +static int ttusb_del_channel(struct ttusb *ttusb, int channel_id) +{ + int err; + /* DelChannel */ + u8 b[] = { 0xaa, ++ttusb->c, 0x23, 1, channel_id }; + + err = ttusb_cmd(ttusb, b, sizeof(b), 0); + return err; +} + +#ifdef TTUSB_HWSECTIONS +static int ttusb_set_filter(struct ttusb *ttusb, int filter_id, + int associated_chan, u8 filter[8], u8 mask[8]) +{ + int err; + /* SetFilter */ + u8 b[] = { 0xaa, 0, 0x24, 0x1a, filter_id, associated_chan, + filter[0], filter[1], filter[2], filter[3], + filter[4], filter[5], filter[6], filter[7], + filter[8], filter[9], filter[10], filter[11], + mask[0], mask[1], mask[2], mask[3], + mask[4], mask[5], mask[6], mask[7], + mask[8], mask[9], mask[10], mask[11] + }; + + err = ttusb_cmd(ttusb, b, sizeof(b), 0); + return err; +} + +static int ttusb_del_filter(struct ttusb *ttusb, int filter_id) +{ + int err; + /* DelFilter */ + u8 b[] = { 0xaa, ++ttusb->c, 0x25, 1, filter_id }; + + err = ttusb_cmd(ttusb, b, sizeof(b), 0); + return err; +} +#endif + +static int ttusb_init_controller(struct ttusb *ttusb) +{ + u8 b0[] = { 0xaa, ++ttusb->c, 0x15, 1, 0 }; + u8 b1[] = { 0xaa, ++ttusb->c, 0x15, 1, 1 }; + u8 b2[] = { 0xaa, ++ttusb->c, 0x32, 1, 0 }; + /* i2c write read: 5 bytes, addr 0x10, 0x02 bytes write, 1 bytes read. */ + u8 b3[] = + { 0xaa, ++ttusb->c, 0x31, 5, 0x10, 0x02, 0x01, 0x00, 0x1e }; + u8 b4[] = + { 0x55, ttusb->c, 0x31, 4, 0x10, 0x02, 0x01, 0x00, 0x1e }; + + u8 get_version[] = { 0xaa, ++ttusb->c, 0x17, 5, 0, 0, 0, 0, 0 }; + u8 get_dsp_version[0x20] = + { 0xaa, ++ttusb->c, 0x26, 28, 0, 0, 0, 0, 0 }; + int err; + + /* reset board */ + if ((err = ttusb_cmd(ttusb, b0, sizeof(b0), 0))) + return err; + + /* reset board (again?) */ + if ((err = ttusb_cmd(ttusb, b1, sizeof(b1), 0))) + return err; + + ttusb_boot_dsp(ttusb); + + /* set i2c bit rate */ + if ((err = ttusb_cmd(ttusb, b2, sizeof(b2), 0))) + return err; + + if ((err = ttusb_cmd(ttusb, b3, sizeof(b3), 1))) + return err; + + err = ttusb_result(ttusb, b4, sizeof(b4)); + + if ((err = ttusb_cmd(ttusb, get_version, sizeof(get_version), 1))) + return err; + + if ((err = ttusb_result(ttusb, get_version, sizeof(get_version)))) + return err; + + dprintk("%s: stc-version: %c%c%c%c%c\n", __func__, + get_version[4], get_version[5], get_version[6], + get_version[7], get_version[8]); + + if (memcmp(get_version + 4, "V 0.0", 5) && + memcmp(get_version + 4, "V 1.1", 5) && + memcmp(get_version + 4, "V 2.1", 5) && + memcmp(get_version + 4, "V 2.2", 5)) { + printk + ("%s: unknown STC version %c%c%c%c%c, please report!\n", + __func__, get_version[4], get_version[5], + get_version[6], get_version[7], get_version[8]); + } + + ttusb->revision = ((get_version[6] - '0') << 4) | + (get_version[8] - '0'); + + err = + ttusb_cmd(ttusb, get_dsp_version, sizeof(get_dsp_version), 1); + if (err) + return err; + + err = + ttusb_result(ttusb, get_dsp_version, sizeof(get_dsp_version)); + if (err) + return err; + printk("%s: dsp-version: %c%c%c\n", __func__, + get_dsp_version[4], get_dsp_version[5], get_dsp_version[6]); + return 0; +} + +#ifdef TTUSB_DISEQC +static int ttusb_send_diseqc(struct dvb_frontend* fe, + const struct dvb_diseqc_master_cmd *cmd) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 b[12] = { 0xaa, ++ttusb->c, 0x18 }; + + int err; + + b[3] = 4 + 2 + cmd->msg_len; + b[4] = 0xFF; /* send diseqc master, not burst */ + b[5] = cmd->msg_len; + + memcpy(b + 5, cmd->msg, cmd->msg_len); + + /* Diseqc */ + if ((err = ttusb_cmd(ttusb, b, 4 + b[3], 0))) { + dprintk("%s: usb_bulk_msg() failed, return value %i!\n", + __func__, err); + } + + return err; +} +#endif + +static int ttusb_update_lnb(struct ttusb *ttusb) +{ + u8 b[] = { 0xaa, ++ttusb->c, 0x16, 5, /*power: */ 1, + ttusb->voltage == SEC_VOLTAGE_18 ? 0 : 1, + ttusb->tone == SEC_TONE_ON ? 1 : 0, 1, 1 + }; + int err; + + /* SetLNB */ + if ((err = ttusb_cmd(ttusb, b, sizeof(b), 0))) { + dprintk("%s: usb_bulk_msg() failed, return value %i!\n", + __func__, err); + } + + return err; +} + +static int ttusb_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + + ttusb->voltage = voltage; + return ttusb_update_lnb(ttusb); +} + +#ifdef TTUSB_TONE +static int ttusb_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + + ttusb->tone = tone; + return ttusb_update_lnb(ttusb); +} +#endif + + +#if 0 +static void ttusb_set_led_freq(struct ttusb *ttusb, u8 freq) +{ + u8 b[] = { 0xaa, ++ttusb->c, 0x19, 1, freq }; + int err, actual_len; + + err = ttusb_cmd(ttusb, b, sizeof(b), 0); + if (err) { + dprintk("%s: usb_bulk_msg() failed, return value %i!\n", + __func__, err); + } +} +#endif + +/*****************************************************************************/ + +#ifdef TTUSB_HWSECTIONS +static void ttusb_handle_ts_data(struct ttusb_channel *channel, + const u8 * data, int len); +static void ttusb_handle_sec_data(struct ttusb_channel *channel, + const u8 * data, int len); +#endif + +static int numpkt, numts, numstuff, numsec, numinvalid; +static unsigned long lastj; + +static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, + int len) +{ + u16 csum = 0, cc; + int i; + for (i = 0; i < len; i += 2) + csum ^= le16_to_cpup((__le16 *) (muxpack + i)); + if (csum) { + printk("%s: muxpack with incorrect checksum, ignoring\n", + __func__); + numinvalid++; + return; + } + + cc = (muxpack[len - 4] << 8) | muxpack[len - 3]; + cc &= 0x7FFF; + if ((cc != ttusb->cc) && (ttusb->cc != -1)) + printk("%s: cc discontinuity (%d frames missing)\n", + __func__, (cc - ttusb->cc) & 0x7FFF); + ttusb->cc = (cc + 1) & 0x7FFF; + if (muxpack[0] & 0x80) { +#ifdef TTUSB_HWSECTIONS + /* section data */ + int pusi = muxpack[0] & 0x40; + int channel = muxpack[0] & 0x1F; + int payload = muxpack[1]; + const u8 *data = muxpack + 2; + /* check offset flag */ + if (muxpack[0] & 0x20) + data++; + + ttusb_handle_sec_data(ttusb->channel + channel, data, + payload); + data += payload; + + if ((!!(ttusb->muxpack[0] & 0x20)) ^ + !!(ttusb->muxpack[1] & 1)) + data++; +#warning TODO: pusi + printk("cc: %04x\n", (data[0] << 8) | data[1]); +#endif + numsec++; + } else if (muxpack[0] == 0x47) { +#ifdef TTUSB_HWSECTIONS + /* we have TS data here! */ + int pid = ((muxpack[1] & 0x0F) << 8) | muxpack[2]; + int channel; + for (channel = 0; channel < TTUSB_MAXCHANNEL; ++channel) + if (ttusb->channel[channel].active + && (pid == ttusb->channel[channel].pid)) + ttusb_handle_ts_data(ttusb->channel + + channel, muxpack, + 188); +#endif + numts++; + dvb_dmx_swfilter_packets(&ttusb->dvb_demux, muxpack, 1); + } else if (muxpack[0] != 0) { + numinvalid++; + printk("illegal muxpack type %02x\n", muxpack[0]); + } else + numstuff++; +} + +static void ttusb_process_frame(struct ttusb *ttusb, u8 * data, int len) +{ + int maxwork = 1024; + while (len) { + if (!(maxwork--)) { + printk("%s: too much work\n", __func__); + break; + } + + switch (ttusb->mux_state) { + case 0: + case 1: + case 2: + len--; + if (*data++ == 0xAA) + ++ttusb->mux_state; + else { + ttusb->mux_state = 0; + if (ttusb->insync) { + dprintk("%s: %02x\n", + __func__, data[-1]); + printk(KERN_INFO "%s: lost sync.\n", + __func__); + ttusb->insync = 0; + } + } + break; + case 3: + ttusb->insync = 1; + len--; + ttusb->mux_npacks = *data++; + ++ttusb->mux_state; + ttusb->muxpack_ptr = 0; + /* maximum bytes, until we know the length */ + ttusb->muxpack_len = 2; + break; + case 4: + { + int avail; + avail = len; + if (avail > + (ttusb->muxpack_len - + ttusb->muxpack_ptr)) + avail = + ttusb->muxpack_len - + ttusb->muxpack_ptr; + memcpy(ttusb->muxpack + ttusb->muxpack_ptr, + data, avail); + ttusb->muxpack_ptr += avail; + BUG_ON(ttusb->muxpack_ptr > 264); + data += avail; + len -= avail; + /* determine length */ + if (ttusb->muxpack_ptr == 2) { + if (ttusb->muxpack[0] & 0x80) { + ttusb->muxpack_len = + ttusb->muxpack[1] + 2; + if (ttusb-> + muxpack[0] & 0x20) + ttusb-> + muxpack_len++; + if ((!! + (ttusb-> + muxpack[0] & 0x20)) ^ + !!(ttusb-> + muxpack[1] & 1)) + ttusb-> + muxpack_len++; + ttusb->muxpack_len += 4; + } else if (ttusb->muxpack[0] == + 0x47) + ttusb->muxpack_len = + 188 + 4; + else if (ttusb->muxpack[0] == 0x00) + ttusb->muxpack_len = + ttusb->muxpack[1] + 2 + + 4; + else { + dprintk + ("%s: invalid state: first byte is %x\n", + __func__, + ttusb->muxpack[0]); + ttusb->mux_state = 0; + } + } + + /** + * if length is valid and we reached the end: + * goto next muxpack + */ + if ((ttusb->muxpack_ptr >= 2) && + (ttusb->muxpack_ptr == + ttusb->muxpack_len)) { + ttusb_process_muxpack(ttusb, + ttusb-> + muxpack, + ttusb-> + muxpack_ptr); + ttusb->muxpack_ptr = 0; + /* maximum bytes, until we know the length */ + ttusb->muxpack_len = 2; + + /** + * no muxpacks left? + * return to search-sync state + */ + if (!ttusb->mux_npacks--) { + ttusb->mux_state = 0; + break; + } + } + break; + } + default: + BUG(); + break; + } + } +} + +static void ttusb_iso_irq(struct urb *urb) +{ + struct ttusb *ttusb = urb->context; + struct usb_iso_packet_descriptor *d; + u8 *data; + int len, i; + + if (!ttusb->iso_streaming) + return; + +#if 0 + printk("%s: status %d, errcount == %d, length == %i\n", + __func__, + urb->status, urb->error_count, urb->actual_length); +#endif + + if (!urb->status) { + for (i = 0; i < urb->number_of_packets; ++i) { + numpkt++; + if (time_after_eq(jiffies, lastj + HZ)) { + dprintk("frames/s: %lu (ts: %d, stuff %d, " + "sec: %d, invalid: %d, all: %d)\n", + numpkt * HZ / (jiffies - lastj), + numts, numstuff, numsec, numinvalid, + numts + numstuff + numsec + numinvalid); + numts = numstuff = numsec = numinvalid = 0; + lastj = jiffies; + numpkt = 0; + } + d = &urb->iso_frame_desc[i]; + data = urb->transfer_buffer + d->offset; + len = d->actual_length; + d->actual_length = 0; + d->status = 0; + ttusb_process_frame(ttusb, data, len); + } + } + usb_submit_urb(urb, GFP_ATOMIC); +} + +static void ttusb_free_iso_urbs(struct ttusb *ttusb) +{ + int i; + + for (i = 0; i < ISO_BUF_COUNT; i++) + if (ttusb->iso_urb[i]) + usb_free_urb(ttusb->iso_urb[i]); + + pci_free_consistent(NULL, + ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * + ISO_BUF_COUNT, ttusb->iso_buffer, + ttusb->iso_dma_handle); +} + +static int ttusb_alloc_iso_urbs(struct ttusb *ttusb) +{ + int i; + + ttusb->iso_buffer = pci_alloc_consistent(NULL, + ISO_FRAME_SIZE * + FRAMES_PER_ISO_BUF * + ISO_BUF_COUNT, + &ttusb->iso_dma_handle); + + if (!ttusb->iso_buffer) { + dprintk("%s: pci_alloc_consistent - not enough memory\n", + __func__); + return -ENOMEM; + } + + memset(ttusb->iso_buffer, 0, + ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * ISO_BUF_COUNT); + + for (i = 0; i < ISO_BUF_COUNT; i++) { + struct urb *urb; + + if (! + (urb = + usb_alloc_urb(FRAMES_PER_ISO_BUF, GFP_ATOMIC))) { + ttusb_free_iso_urbs(ttusb); + return -ENOMEM; + } + + ttusb->iso_urb[i] = urb; + } + + return 0; +} + +static void ttusb_stop_iso_xfer(struct ttusb *ttusb) +{ + int i; + + for (i = 0; i < ISO_BUF_COUNT; i++) + usb_kill_urb(ttusb->iso_urb[i]); + + ttusb->iso_streaming = 0; +} + +static int ttusb_start_iso_xfer(struct ttusb *ttusb) +{ + int i, j, err, buffer_offset = 0; + + if (ttusb->iso_streaming) { + printk("%s: iso xfer already running!\n", __func__); + return 0; + } + + ttusb->cc = -1; + ttusb->insync = 0; + ttusb->mux_state = 0; + + for (i = 0; i < ISO_BUF_COUNT; i++) { + int frame_offset = 0; + struct urb *urb = ttusb->iso_urb[i]; + + urb->dev = ttusb->dev; + urb->context = ttusb; + urb->complete = ttusb_iso_irq; + urb->pipe = ttusb->isoc_in_pipe; + urb->transfer_flags = URB_ISO_ASAP; + urb->interval = 1; + urb->number_of_packets = FRAMES_PER_ISO_BUF; + urb->transfer_buffer_length = + ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF; + urb->transfer_buffer = ttusb->iso_buffer + buffer_offset; + buffer_offset += ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF; + + for (j = 0; j < FRAMES_PER_ISO_BUF; j++) { + urb->iso_frame_desc[j].offset = frame_offset; + urb->iso_frame_desc[j].length = ISO_FRAME_SIZE; + frame_offset += ISO_FRAME_SIZE; + } + } + + for (i = 0; i < ISO_BUF_COUNT; i++) { + if ((err = usb_submit_urb(ttusb->iso_urb[i], GFP_ATOMIC))) { + ttusb_stop_iso_xfer(ttusb); + printk + ("%s: failed urb submission (%i: err = %i)!\n", + __func__, i, err); + return err; + } + } + + ttusb->iso_streaming = 1; + + return 0; +} + +#ifdef TTUSB_HWSECTIONS +static void ttusb_handle_ts_data(struct dvb_demux_feed *dvbdmxfeed, const u8 * data, + int len) +{ + dvbdmxfeed->cb.ts(data, len, 0, 0, &dvbdmxfeed->feed.ts, 0); +} + +static void ttusb_handle_sec_data(struct dvb_demux_feed *dvbdmxfeed, const u8 * data, + int len) +{ +// struct dvb_demux_feed *dvbdmxfeed = channel->dvbdmxfeed; +#error TODO: handle ugly stuff +// dvbdmxfeed->cb.sec(data, len, 0, 0, &dvbdmxfeed->feed.sec, 0); +} +#endif + +static int ttusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct ttusb *ttusb = (struct ttusb *) dvbdmxfeed->demux; + int feed_type = 1; + + dprintk("ttusb_start_feed\n"); + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + break; + case DMX_TYPE_SEC: + break; + default: + return -EINVAL; + } + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + switch (dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + case DMX_TS_PES_AUDIO: + case DMX_TS_PES_TELETEXT: + case DMX_TS_PES_PCR: + case DMX_TS_PES_OTHER: + break; + default: + return -EINVAL; + } + } + +#ifdef TTUSB_HWSECTIONS +#error TODO: allocate filters + if (dvbdmxfeed->type == DMX_TYPE_TS) { + feed_type = 1; + } else if (dvbdmxfeed->type == DMX_TYPE_SEC) { + feed_type = 2; + } +#endif + + ttusb_set_channel(ttusb, dvbdmxfeed->index, feed_type, dvbdmxfeed->pid); + + if (0 == ttusb->running_feed_count++) + ttusb_start_iso_xfer(ttusb); + + return 0; +} + +static int ttusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct ttusb *ttusb = (struct ttusb *) dvbdmxfeed->demux; + + ttusb_del_channel(ttusb, dvbdmxfeed->index); + + if (--ttusb->running_feed_count == 0) + ttusb_stop_iso_xfer(ttusb); + + return 0; +} + +static int ttusb_setup_interfaces(struct ttusb *ttusb) +{ + usb_set_interface(ttusb->dev, 1, 1); + + ttusb->bulk_out_pipe = usb_sndbulkpipe(ttusb->dev, 1); + ttusb->bulk_in_pipe = usb_rcvbulkpipe(ttusb->dev, 1); + ttusb->isoc_in_pipe = usb_rcvisocpipe(ttusb->dev, 2); + + return 0; +} + +#if 0 +static u8 stc_firmware[8192]; + +static int stc_open(struct inode *inode, struct file *file) +{ + struct ttusb *ttusb = file->private_data; + int addr; + + for (addr = 0; addr < 8192; addr += 16) { + u8 snd_buf[2] = { addr >> 8, addr & 0xFF }; + ttusb_i2c_msg(ttusb, 0x50, snd_buf, 2, stc_firmware + addr, + 16); + } + + return 0; +} + +static ssize_t stc_read(struct file *file, char *buf, size_t count, + loff_t *offset) +{ + return simple_read_from_buffer(buf, count, offset, stc_firmware, 8192); +} + +static int stc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations stc_fops = { + .owner = THIS_MODULE, + .read = stc_read, + .open = stc_open, + .release = stc_release, +}; +#endif + +static u32 functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + + + +static int alps_tdmb7_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 data[4]; + struct i2c_msg msg = {.addr=0x61, .flags=0, .buf=data, .len=sizeof(data) }; + u32 div; + + div = (p->frequency + 36166667) / 166667; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | 0x85; + data[3] = p->frequency < 592000000 ? 0x40 : 0x80; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct cx22700_config alps_tdmb7_config = { + .demod_address = 0x43, +}; + + + + + +static int philips_tdm1316l_tuner_init(struct dvb_frontend* fe) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + struct i2c_msg tuner_msg = { .addr=0x60, .flags=0, .buf=td1316_init, .len=sizeof(td1316_init) }; + + // setup PLL configuration + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) return -EIO; + msleep(1); + + // disable the mc44BC374c (do not check for errors) + tuner_msg.addr = 0x65; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { + i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1); + } + + return 0; +} + +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr=0x60, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency + 36130000; + if (tuner_frequency < 87000000) return -EINVAL; + else if (tuner_frequency < 130000000) cp = 3; + else if (tuner_frequency < 160000000) cp = 5; + else if (tuner_frequency < 200000000) cp = 6; + else if (tuner_frequency < 290000000) cp = 3; + else if (tuner_frequency < 420000000) cp = 5; + else if (tuner_frequency < 480000000) cp = 6; + else if (tuner_frequency < 620000000) cp = 3; + else if (tuner_frequency < 830000000) cp = 5; + else if (tuner_frequency < 895000000) cp = 7; + else return -EINVAL; + + // determine band + if (p->frequency < 49000000) + return -EINVAL; + else if (p->frequency < 159000000) + band = 1; + else if (p->frequency < 444000000) + band = 2; + else if (p->frequency < 861000000) + band = 4; + else return -EINVAL; + + // setup PLL filter + switch (p->bandwidth_hz) { + case 6000000: + tda1004x_writereg(fe, 0x0C, 0); + filter = 0; + break; + + case 7000000: + tda1004x_writereg(fe, 0x0C, 0); + filter = 0; + break; + + case 8000000: + tda1004x_writereg(fe, 0x0C, 0xFF); + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tdm1316l_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + + return request_firmware(fw, name, &ttusb->dev->dev); +} + +static struct tda1004x_config philips_tdm1316l_config = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .request_firmware = philips_tdm1316l_request_firmware, +}; + +static u8 alps_bsbe1_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x92, + 0xff, 0xff +}; + +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff +}; + +static int alps_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; + bclk = 0x47; + } else if (srate < 3000000) { + aclk = 0xb7; + bclk = 0x4b; + } else if (srate < 7000000) { + aclk = 0xb7; + bclk = 0x4f; + } else if (srate < 14000000) { + aclk = 0xb7; + bclk = 0x53; + } else if (srate < 30000000) { + aclk = 0xb6; + bclk = 0x53; + } else if (srate < 45000000) { + aclk = 0xb4; + bclk = 0x51; + } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static int philips_tsa5059_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((p->frequency < 950000) || (p->frequency > 2150000)) + return -EINVAL; + + div = (p->frequency + (125 - 1)) / 125; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0xC4; + + if (p->frequency > 1530000) + buf[3] = 0xC0; + + /* BSBE1 wants XCE bit set */ + if (ttusb->revision == TTUSB_REV_2_2) + buf[3] |= 0x20; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct stv0299_config alps_stv0299_config = { + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_stv0299_set_symbol_rate, +}; + +static int ttusb_novas_grundig_29504_491_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + div = p->frequency / 125; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x8e; + buf[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct tda8083_config ttusb_novas_grundig_29504_491_config = { + + .demod_address = 0x68, +}; + +static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusb* ttusb = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (p->frequency + 35937500 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&ttusb->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + + +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + +static u8 read_pwm(struct ttusb* ttusb) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&ttusb->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + + +static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusb *ttusb = (struct ttusb *) fe->dvb->priv; + u8 tuner_buf[5]; + struct i2c_msg tuner_msg = {.addr = 0x60, + .flags = 0, + .buf = tuner_buf, + .len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency; + if (tuner_frequency < 87000000) {return -EINVAL;} + else if (tuner_frequency < 130000000) {cp = 3; band = 1;} + else if (tuner_frequency < 160000000) {cp = 5; band = 1;} + else if (tuner_frequency < 200000000) {cp = 6; band = 1;} + else if (tuner_frequency < 290000000) {cp = 3; band = 2;} + else if (tuner_frequency < 420000000) {cp = 5; band = 2;} + else if (tuner_frequency < 480000000) {cp = 6; band = 2;} + else if (tuner_frequency < 620000000) {cp = 3; band = 4;} + else if (tuner_frequency < 830000000) {cp = 5; band = 4;} + else if (tuner_frequency < 895000000) {cp = 7; band = 4;} + else {return -EINVAL;} + + // assume PLL filter should always be 8MHz for the moment. + filter = 1; + + // calculate divisor + // (Finput + Fif)/Fref; Fif = 36125000 Hz, Fref = 62500 Hz + tuner_frequency = ((p->frequency + 36125000) / 62500); + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xc8; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + tuner_buf[4] = 0x80; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { + printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 1\n"); + return -EIO; + } + + msleep(50); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { + printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 2\n"); + return -EIO; + } + + msleep(1); + + return 0; +} + +static u8 dvbc_philips_tdm1316l_inittab[] = { + 0x80, 0x21, + 0x80, 0x20, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x20, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0xff, + 0x4b, 0x7f, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x00, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x00, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x4b, + 0xc1, 0x00, + 0xc2, 0x00, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x38, + 0x62, 0x0a, + 0x53, 0x13, + 0x59, 0x08, + 0x55, 0x00, + 0x56, 0x40, + 0x57, 0x08, + 0x58, 0x3d, + 0x88, 0x10, + 0xa0, 0x00, + 0xa0, 0x00, + 0xa0, 0x00, + 0xa0, 0x04, + 0xff, 0xff, +}; + +static struct stv0297_config dvbc_philips_tdm1316l_config = { + .demod_address = 0x1c, + .inittab = dvbc_philips_tdm1316l_inittab, + .invert = 0, +}; + +static void frontend_init(struct ttusb* ttusb) +{ + switch(le16_to_cpu(ttusb->dev->descriptor.idProduct)) { + case 0x1003: // Hauppauge/TT Nova-USB-S budget (stv0299/ALPS BSRU6|BSBE1(tsa5059)) + // try the stv0299 based first + ttusb->fe = dvb_attach(stv0299_attach, &alps_stv0299_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops.tuner_ops.set_params = philips_tsa5059_tuner_set_params; + + if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1 + alps_stv0299_config.inittab = alps_bsbe1_inittab; + dvb_attach(lnbp21_attach, ttusb->fe, &ttusb->i2c_adap, 0, 0); + } else { // ALPS BSRU6 + ttusb->fe->ops.set_voltage = ttusb_set_voltage; + } + break; + } + + // Grundig 29504-491 + ttusb->fe = dvb_attach(tda8083_attach, &ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops.tuner_ops.set_params = ttusb_novas_grundig_29504_491_tuner_set_params; + ttusb->fe->ops.set_voltage = ttusb_set_voltage; + break; + } + break; + + case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) + ttusb->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb)); + if (ttusb->fe != NULL) { + ttusb->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + break; + } + + ttusb->fe = dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) + // try the ALPS TDMB7 first + ttusb->fe = dvb_attach(cx22700_attach, &alps_tdmb7_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops.tuner_ops.set_params = alps_tdmb7_tuner_set_params; + break; + } + + // Philips td1316 + ttusb->fe = dvb_attach(tda10046_attach, &philips_tdm1316l_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops.tuner_ops.init = philips_tdm1316l_tuner_init; + ttusb->fe->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; + break; + } + break; + } + + if (ttusb->fe == NULL) { + printk("dvb-ttusb-budget: A frontend driver was not found for device [%04x:%04x]\n", + le16_to_cpu(ttusb->dev->descriptor.idVendor), + le16_to_cpu(ttusb->dev->descriptor.idProduct)); + } else { + if (dvb_register_frontend(&ttusb->adapter, ttusb->fe)) { + printk("dvb-ttusb-budget: Frontend registration failed!\n"); + dvb_frontend_detach(ttusb->fe); + ttusb->fe = NULL; + } + } +} + + + +static struct i2c_algorithm ttusb_dec_algo = { + .master_xfer = master_xfer, + .functionality = functionality, +}; + +static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev; + struct ttusb *ttusb; + int result; + + dprintk("%s: TTUSB DVB connected\n", __func__); + + udev = interface_to_usbdev(intf); + + if (intf->altsetting->desc.bInterfaceNumber != 1) return -ENODEV; + + if (!(ttusb = kzalloc(sizeof(struct ttusb), GFP_KERNEL))) + return -ENOMEM; + + ttusb->dev = udev; + ttusb->c = 0; + ttusb->mux_state = 0; + mutex_init(&ttusb->semi2c); + + mutex_lock(&ttusb->semi2c); + + mutex_init(&ttusb->semusb); + + ttusb_setup_interfaces(ttusb); + + result = ttusb_alloc_iso_urbs(ttusb); + if (result < 0) { + dprintk("%s: ttusb_alloc_iso_urbs - failed\n", __func__); + mutex_unlock(&ttusb->semi2c); + kfree(ttusb); + return result; + } + + if (ttusb_init_controller(ttusb)) + printk("ttusb_init_controller: error\n"); + + mutex_unlock(&ttusb->semi2c); + + result = dvb_register_adapter(&ttusb->adapter, + "Technotrend/Hauppauge Nova-USB", + THIS_MODULE, &udev->dev, adapter_nr); + if (result < 0) { + ttusb_free_iso_urbs(ttusb); + kfree(ttusb); + return result; + } + ttusb->adapter.priv = ttusb; + + /* i2c */ + memset(&ttusb->i2c_adap, 0, sizeof(struct i2c_adapter)); + strcpy(ttusb->i2c_adap.name, "TTUSB DEC"); + + i2c_set_adapdata(&ttusb->i2c_adap, ttusb); + + ttusb->i2c_adap.algo = &ttusb_dec_algo; + ttusb->i2c_adap.algo_data = NULL; + ttusb->i2c_adap.dev.parent = &udev->dev; + + result = i2c_add_adapter(&ttusb->i2c_adap); + if (result) + goto err_unregister_adapter; + + memset(&ttusb->dvb_demux, 0, sizeof(ttusb->dvb_demux)); + + ttusb->dvb_demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING; + ttusb->dvb_demux.priv = NULL; +#ifdef TTUSB_HWSECTIONS + ttusb->dvb_demux.filternum = TTUSB_MAXFILTER; +#else + ttusb->dvb_demux.filternum = 32; +#endif + ttusb->dvb_demux.feednum = TTUSB_MAXCHANNEL; + ttusb->dvb_demux.start_feed = ttusb_start_feed; + ttusb->dvb_demux.stop_feed = ttusb_stop_feed; + ttusb->dvb_demux.write_to_decoder = NULL; + + result = dvb_dmx_init(&ttusb->dvb_demux); + if (result < 0) { + printk("ttusb_dvb: dvb_dmx_init failed (errno = %d)\n", result); + result = -ENODEV; + goto err_i2c_del_adapter; + } +//FIXME dmxdev (nur WAS?) + ttusb->dmxdev.filternum = ttusb->dvb_demux.filternum; + ttusb->dmxdev.demux = &ttusb->dvb_demux.dmx; + ttusb->dmxdev.capabilities = 0; + + result = dvb_dmxdev_init(&ttusb->dmxdev, &ttusb->adapter); + if (result < 0) { + printk("ttusb_dvb: dvb_dmxdev_init failed (errno = %d)\n", + result); + result = -ENODEV; + goto err_release_dmx; + } + + if (dvb_net_init(&ttusb->adapter, &ttusb->dvbnet, &ttusb->dvb_demux.dmx)) { + printk("ttusb_dvb: dvb_net_init failed!\n"); + result = -ENODEV; + goto err_release_dmxdev; + } + + usb_set_intfdata(intf, (void *) ttusb); + + frontend_init(ttusb); + + return 0; + +err_release_dmxdev: + dvb_dmxdev_release(&ttusb->dmxdev); +err_release_dmx: + dvb_dmx_release(&ttusb->dvb_demux); +err_i2c_del_adapter: + i2c_del_adapter(&ttusb->i2c_adap); +err_unregister_adapter: + dvb_unregister_adapter (&ttusb->adapter); + return result; +} + +static void ttusb_disconnect(struct usb_interface *intf) +{ + struct ttusb *ttusb = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + ttusb->disconnecting = 1; + + ttusb_stop_iso_xfer(ttusb); + + ttusb->dvb_demux.dmx.close(&ttusb->dvb_demux.dmx); + dvb_net_release(&ttusb->dvbnet); + dvb_dmxdev_release(&ttusb->dmxdev); + dvb_dmx_release(&ttusb->dvb_demux); + if (ttusb->fe != NULL) { + dvb_unregister_frontend(ttusb->fe); + dvb_frontend_detach(ttusb->fe); + } + i2c_del_adapter(&ttusb->i2c_adap); + dvb_unregister_adapter(&ttusb->adapter); + + ttusb_free_iso_urbs(ttusb); + + kfree(ttusb); + + dprintk("%s: TTUSB DVB disconnected\n", __func__); +} + +static struct usb_device_id ttusb_table[] = { + {USB_DEVICE(0xb48, 0x1003)}, + {USB_DEVICE(0xb48, 0x1004)}, + {USB_DEVICE(0xb48, 0x1005)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, ttusb_table); + +static struct usb_driver ttusb_driver = { + .name = "ttusb", + .probe = ttusb_probe, + .disconnect = ttusb_disconnect, + .id_table = ttusb_table, +}; + +module_usb_driver(ttusb_driver); + +MODULE_AUTHOR("Holger Waechtler "); +MODULE_DESCRIPTION("TTUSB DVB Driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("ttusb-budget/dspbootcode.bin"); diff --git a/drivers/media/usb/ttusb-dec/Kconfig b/drivers/media/usb/ttusb-dec/Kconfig new file mode 100644 index 000000000000..290254ab06db --- /dev/null +++ b/drivers/media/usb/ttusb-dec/Kconfig @@ -0,0 +1,21 @@ +config DVB_TTUSB_DEC + tristate "Technotrend/Hauppauge USB DEC devices" + depends on DVB_CORE && USB && INPUT && PCI + select CRC32 + help + Support for external USB adapters designed by Technotrend and + produced by Hauppauge, shipped under the brand name 'DEC2000-t' + and 'DEC3000-s'. + + Even if these devices have a MPEG decoder built in, they transmit + only compressed MPEG data over the USB bus, so you need + an external software decoder to watch TV on your computer. + + This driver needs external firmware. Please use the commands + "/Documentation/dvb/get_dvb_firmware dec2000t", + "/Documentation/dvb/get_dvb_firmware dec2540t", + "/Documentation/dvb/get_dvb_firmware dec3000s", + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + + Say Y if you own such a device and want to use it. diff --git a/drivers/media/usb/ttusb-dec/Makefile b/drivers/media/usb/ttusb-dec/Makefile new file mode 100644 index 000000000000..5352740d2353 --- /dev/null +++ b/drivers/media/usb/ttusb-dec/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o + +ccflags-y += -Idrivers/media/dvb-core/ diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c new file mode 100644 index 000000000000..504c81230339 --- /dev/null +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -0,0 +1,1764 @@ +/* + * TTUSB DEC Driver + * + * Copyright (C) 2003-2004 Alex Woods + * IR support by Peter Beutner + * + * This program is free software; 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 +#include +#include + +#include + +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_filter.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "ttusbdecfe.h" + +static int debug; +static int output_pva; +static int enable_rc; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +module_param(output_pva, int, 0444); +MODULE_PARM_DESC(output_pva, "Output PVA from dvr device (default:off)"); +module_param(enable_rc, int, 0644); +MODULE_PARM_DESC(enable_rc, "Turn on/off IR remote control(default: off)"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk if (debug) printk + +#define DRIVER_NAME "TechnoTrend/Hauppauge DEC USB" + +#define COMMAND_PIPE 0x03 +#define RESULT_PIPE 0x04 +#define IN_PIPE 0x08 +#define OUT_PIPE 0x07 +#define IRQ_PIPE 0x0A + +#define COMMAND_PACKET_SIZE 0x3c +#define ARM_PACKET_SIZE 0x1000 +#define IRQ_PACKET_SIZE 0x8 + +#define ISO_BUF_COUNT 0x04 +#define FRAMES_PER_ISO_BUF 0x04 +#define ISO_FRAME_SIZE 0x0380 + +#define MAX_PVA_LENGTH 6144 + +enum ttusb_dec_model { + TTUSB_DEC2000T, + TTUSB_DEC2540T, + TTUSB_DEC3000S +}; + +enum ttusb_dec_packet_type { + TTUSB_DEC_PACKET_PVA, + TTUSB_DEC_PACKET_SECTION, + TTUSB_DEC_PACKET_EMPTY +}; + +enum ttusb_dec_interface { + TTUSB_DEC_INTERFACE_INITIAL, + TTUSB_DEC_INTERFACE_IN, + TTUSB_DEC_INTERFACE_OUT +}; + +struct ttusb_dec { + enum ttusb_dec_model model; + char *model_name; + char *firmware_name; + int can_playback; + + /* DVB bits */ + struct dvb_adapter adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dmx_frontend frontend; + struct dvb_net dvb_net; + struct dvb_frontend* fe; + + u16 pid[DMX_PES_OTHER]; + + /* USB bits */ + struct usb_device *udev; + u8 trans_count; + unsigned int command_pipe; + unsigned int result_pipe; + unsigned int in_pipe; + unsigned int out_pipe; + unsigned int irq_pipe; + enum ttusb_dec_interface interface; + struct mutex usb_mutex; + + void *irq_buffer; + struct urb *irq_urb; + dma_addr_t irq_dma_handle; + void *iso_buffer; + dma_addr_t iso_dma_handle; + struct urb *iso_urb[ISO_BUF_COUNT]; + int iso_stream_count; + struct mutex iso_mutex; + + u8 packet[MAX_PVA_LENGTH + 4]; + enum ttusb_dec_packet_type packet_type; + int packet_state; + int packet_length; + int packet_payload_length; + u16 next_packet_id; + + int pva_stream_count; + int filter_stream_count; + + struct dvb_filter_pes2ts a_pes2ts; + struct dvb_filter_pes2ts v_pes2ts; + + u8 v_pes[16 + MAX_PVA_LENGTH]; + int v_pes_length; + int v_pes_postbytes; + + struct list_head urb_frame_list; + struct tasklet_struct urb_tasklet; + spinlock_t urb_frame_list_lock; + + struct dvb_demux_filter *audio_filter; + struct dvb_demux_filter *video_filter; + struct list_head filter_info_list; + spinlock_t filter_info_list_lock; + + struct input_dev *rc_input_dev; + char rc_phys[64]; + + int active; /* Loaded successfully */ +}; + +struct urb_frame { + u8 data[ISO_FRAME_SIZE]; + int length; + struct list_head urb_frame_list; +}; + +struct filter_info { + u8 stream_id; + struct dvb_demux_filter *filter; + struct list_head filter_info_list; +}; + +static u16 rc_keys[] = { + KEY_POWER, + KEY_MUTE, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_0, + KEY_CHANNELUP, + KEY_VOLUMEDOWN, + KEY_OK, + KEY_VOLUMEUP, + KEY_CHANNELDOWN, + KEY_PREVIOUS, + KEY_ESC, + KEY_RED, + KEY_GREEN, + KEY_YELLOW, + KEY_BLUE, + KEY_OPTION, + KEY_M, + KEY_RADIO +}; + +static void ttusb_dec_set_model(struct ttusb_dec *dec, + enum ttusb_dec_model model); + +static void ttusb_dec_handle_irq( struct urb *urb) +{ + struct ttusb_dec * dec = urb->context; + char *buffer = dec->irq_buffer; + int retval; + + switch(urb->status) { + case 0: /*success*/ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -ETIME: + /* this urb is dead, cleanup */ + dprintk("%s:urb shutting down with status: %d\n", + __func__, urb->status); + return; + default: + dprintk("%s:nonzero status received: %d\n", + __func__,urb->status); + goto exit; + } + + if( (buffer[0] == 0x1) && (buffer[2] == 0x15) ) { + /* IR - Event */ + /* this is an fact a bit too simple implementation; + * the box also reports a keyrepeat signal + * (with buffer[3] == 0x40) in an intervall of ~100ms. + * But to handle this correctly we had to imlemenent some + * kind of timer which signals a 'key up' event if no + * keyrepeat signal is received for lets say 200ms. + * this should/could be added later ... + * for now lets report each signal as a key down and up*/ + dprintk("%s:rc signal:%d\n", __func__, buffer[4]); + input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1); + input_sync(dec->rc_input_dev); + input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0); + input_sync(dec->rc_input_dev); + } + +exit: retval = usb_submit_urb(urb, GFP_ATOMIC); + if(retval) + printk("%s - usb_commit_urb failed with result: %d\n", + __func__, retval); +} + +static u16 crc16(u16 crc, const u8 *buf, size_t len) +{ + u16 tmp; + + while (len--) { + crc ^= *buf++; + crc ^= (u8)crc >> 4; + tmp = (u8)crc; + crc ^= (tmp ^ (tmp << 1)) << 4; + } + return crc; +} + +static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command, + int param_length, const u8 params[], + int *result_length, u8 cmd_result[]) +{ + int result, actual_len, i; + u8 *b; + + dprintk("%s\n", __func__); + + b = kmalloc(COMMAND_PACKET_SIZE + 4, GFP_KERNEL); + if (!b) + return -ENOMEM; + + if ((result = mutex_lock_interruptible(&dec->usb_mutex))) { + kfree(b); + printk("%s: Failed to lock usb mutex.\n", __func__); + return result; + } + + b[0] = 0xaa; + b[1] = ++dec->trans_count; + b[2] = command; + b[3] = param_length; + + if (params) + memcpy(&b[4], params, param_length); + + if (debug) { + printk("%s: command: ", __func__); + for (i = 0; i < param_length + 4; i++) + printk("0x%02X ", b[i]); + printk("\n"); + } + + result = usb_bulk_msg(dec->udev, dec->command_pipe, b, + COMMAND_PACKET_SIZE + 4, &actual_len, 1000); + + if (result) { + printk("%s: command bulk message failed: error %d\n", + __func__, result); + mutex_unlock(&dec->usb_mutex); + kfree(b); + return result; + } + + result = usb_bulk_msg(dec->udev, dec->result_pipe, b, + COMMAND_PACKET_SIZE + 4, &actual_len, 1000); + + if (result) { + printk("%s: result bulk message failed: error %d\n", + __func__, result); + mutex_unlock(&dec->usb_mutex); + kfree(b); + return result; + } else { + if (debug) { + printk("%s: result: ", __func__); + for (i = 0; i < actual_len; i++) + printk("0x%02X ", b[i]); + printk("\n"); + } + + if (result_length) + *result_length = b[3]; + if (cmd_result && b[3] > 0) + memcpy(cmd_result, &b[4], b[3]); + + mutex_unlock(&dec->usb_mutex); + + kfree(b); + return 0; + } +} + +static int ttusb_dec_get_stb_state (struct ttusb_dec *dec, unsigned int *mode, + unsigned int *model, unsigned int *version) +{ + u8 c[COMMAND_PACKET_SIZE]; + int c_length; + int result; + __be32 tmp; + + dprintk("%s\n", __func__); + + result = ttusb_dec_send_command(dec, 0x08, 0, NULL, &c_length, c); + if (result) + return result; + + if (c_length >= 0x0c) { + if (mode != NULL) { + memcpy(&tmp, c, 4); + *mode = ntohl(tmp); + } + if (model != NULL) { + memcpy(&tmp, &c[4], 4); + *model = ntohl(tmp); + } + if (version != NULL) { + memcpy(&tmp, &c[8], 4); + *version = ntohl(tmp); + } + return 0; + } else { + return -1; + } +} + +static int ttusb_dec_audio_pes2ts_cb(void *priv, unsigned char *data) +{ + struct ttusb_dec *dec = priv; + + dec->audio_filter->feed->cb.ts(data, 188, NULL, 0, + &dec->audio_filter->feed->feed.ts, + DMX_OK); + + return 0; +} + +static int ttusb_dec_video_pes2ts_cb(void *priv, unsigned char *data) +{ + struct ttusb_dec *dec = priv; + + dec->video_filter->feed->cb.ts(data, 188, NULL, 0, + &dec->video_filter->feed->feed.ts, + DMX_OK); + + return 0; +} + +static void ttusb_dec_set_pids(struct ttusb_dec *dec) +{ + u8 b[] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff }; + + __be16 pcr = htons(dec->pid[DMX_PES_PCR]); + __be16 audio = htons(dec->pid[DMX_PES_AUDIO]); + __be16 video = htons(dec->pid[DMX_PES_VIDEO]); + + dprintk("%s\n", __func__); + + memcpy(&b[0], &pcr, 2); + memcpy(&b[2], &audio, 2); + memcpy(&b[4], &video, 2); + + ttusb_dec_send_command(dec, 0x50, sizeof(b), b, NULL, NULL); + + dvb_filter_pes2ts_init(&dec->a_pes2ts, dec->pid[DMX_PES_AUDIO], + ttusb_dec_audio_pes2ts_cb, dec); + dvb_filter_pes2ts_init(&dec->v_pes2ts, dec->pid[DMX_PES_VIDEO], + ttusb_dec_video_pes2ts_cb, dec); + dec->v_pes_length = 0; + dec->v_pes_postbytes = 0; +} + +static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length) +{ + if (length < 8) { + printk("%s: packet too short - discarding\n", __func__); + return; + } + + if (length > 8 + MAX_PVA_LENGTH) { + printk("%s: packet too long - discarding\n", __func__); + return; + } + + switch (pva[2]) { + + case 0x01: { /* VideoStream */ + int prebytes = pva[5] & 0x03; + int postbytes = (pva[5] & 0x0c) >> 2; + __be16 v_pes_payload_length; + + if (output_pva) { + dec->video_filter->feed->cb.ts(pva, length, NULL, 0, + &dec->video_filter->feed->feed.ts, DMX_OK); + return; + } + + if (dec->v_pes_postbytes > 0 && + dec->v_pes_postbytes == prebytes) { + memcpy(&dec->v_pes[dec->v_pes_length], + &pva[12], prebytes); + + dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes, + dec->v_pes_length + prebytes, 1); + } + + if (pva[5] & 0x10) { + dec->v_pes[7] = 0x80; + dec->v_pes[8] = 0x05; + + dec->v_pes[9] = 0x21 | ((pva[8] & 0xc0) >> 5); + dec->v_pes[10] = ((pva[8] & 0x3f) << 2) | + ((pva[9] & 0xc0) >> 6); + dec->v_pes[11] = 0x01 | + ((pva[9] & 0x3f) << 2) | + ((pva[10] & 0x80) >> 6); + dec->v_pes[12] = ((pva[10] & 0x7f) << 1) | + ((pva[11] & 0xc0) >> 7); + dec->v_pes[13] = 0x01 | ((pva[11] & 0x7f) << 1); + + memcpy(&dec->v_pes[14], &pva[12 + prebytes], + length - 12 - prebytes); + dec->v_pes_length = 14 + length - 12 - prebytes; + } else { + dec->v_pes[7] = 0x00; + dec->v_pes[8] = 0x00; + + memcpy(&dec->v_pes[9], &pva[8], length - 8); + dec->v_pes_length = 9 + length - 8; + } + + dec->v_pes_postbytes = postbytes; + + if (dec->v_pes[9 + dec->v_pes[8]] == 0x00 && + dec->v_pes[10 + dec->v_pes[8]] == 0x00 && + dec->v_pes[11 + dec->v_pes[8]] == 0x01) + dec->v_pes[6] = 0x84; + else + dec->v_pes[6] = 0x80; + + v_pes_payload_length = htons(dec->v_pes_length - 6 + + postbytes); + memcpy(&dec->v_pes[4], &v_pes_payload_length, 2); + + if (postbytes == 0) + dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes, + dec->v_pes_length, 1); + + break; + } + + case 0x02: /* MainAudioStream */ + if (output_pva) { + dec->audio_filter->feed->cb.ts(pva, length, NULL, 0, + &dec->audio_filter->feed->feed.ts, DMX_OK); + return; + } + + dvb_filter_pes2ts(&dec->a_pes2ts, &pva[8], length - 8, + pva[5] & 0x10); + break; + + default: + printk("%s: unknown PVA type: %02x.\n", __func__, + pva[2]); + break; + } +} + +static void ttusb_dec_process_filter(struct ttusb_dec *dec, u8 *packet, + int length) +{ + struct list_head *item; + struct filter_info *finfo; + struct dvb_demux_filter *filter = NULL; + unsigned long flags; + u8 sid; + + sid = packet[1]; + spin_lock_irqsave(&dec->filter_info_list_lock, flags); + for (item = dec->filter_info_list.next; item != &dec->filter_info_list; + item = item->next) { + finfo = list_entry(item, struct filter_info, filter_info_list); + if (finfo->stream_id == sid) { + filter = finfo->filter; + break; + } + } + spin_unlock_irqrestore(&dec->filter_info_list_lock, flags); + + if (filter) + filter->feed->cb.sec(&packet[2], length - 2, NULL, 0, + &filter->filter, DMX_OK); +} + +static void ttusb_dec_process_packet(struct ttusb_dec *dec) +{ + int i; + u16 csum = 0; + u16 packet_id; + + if (dec->packet_length % 2) { + printk("%s: odd sized packet - discarding\n", __func__); + return; + } + + for (i = 0; i < dec->packet_length; i += 2) + csum ^= ((dec->packet[i] << 8) + dec->packet[i + 1]); + + if (csum) { + printk("%s: checksum failed - discarding\n", __func__); + return; + } + + packet_id = dec->packet[dec->packet_length - 4] << 8; + packet_id += dec->packet[dec->packet_length - 3]; + + if ((packet_id != dec->next_packet_id) && dec->next_packet_id) { + printk("%s: warning: lost packets between %u and %u\n", + __func__, dec->next_packet_id - 1, packet_id); + } + + if (packet_id == 0xffff) + dec->next_packet_id = 0x8000; + else + dec->next_packet_id = packet_id + 1; + + switch (dec->packet_type) { + case TTUSB_DEC_PACKET_PVA: + if (dec->pva_stream_count) + ttusb_dec_process_pva(dec, dec->packet, + dec->packet_payload_length); + break; + + case TTUSB_DEC_PACKET_SECTION: + if (dec->filter_stream_count) + ttusb_dec_process_filter(dec, dec->packet, + dec->packet_payload_length); + break; + + case TTUSB_DEC_PACKET_EMPTY: + break; + } +} + +static void swap_bytes(u8 *b, int length) +{ + u8 c; + + length -= length % 2; + for (; length; b += 2, length -= 2) { + c = *b; + *b = *(b + 1); + *(b + 1) = c; + } +} + +static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b, + int length) +{ + swap_bytes(b, length); + + while (length) { + switch (dec->packet_state) { + + case 0: + case 1: + case 2: + if (*b++ == 0xaa) + dec->packet_state++; + else + dec->packet_state = 0; + + length--; + break; + + case 3: + if (*b == 0x00) { + dec->packet_state++; + dec->packet_length = 0; + } else if (*b != 0xaa) { + dec->packet_state = 0; + } + + b++; + length--; + break; + + case 4: + dec->packet[dec->packet_length++] = *b++; + + if (dec->packet_length == 2) { + if (dec->packet[0] == 'A' && + dec->packet[1] == 'V') { + dec->packet_type = + TTUSB_DEC_PACKET_PVA; + dec->packet_state++; + } else if (dec->packet[0] == 'S') { + dec->packet_type = + TTUSB_DEC_PACKET_SECTION; + dec->packet_state++; + } else if (dec->packet[0] == 0x00) { + dec->packet_type = + TTUSB_DEC_PACKET_EMPTY; + dec->packet_payload_length = 2; + dec->packet_state = 7; + } else { + printk("%s: unknown packet type: " + "%02x%02x\n", __func__, + dec->packet[0], dec->packet[1]); + dec->packet_state = 0; + } + } + + length--; + break; + + case 5: + dec->packet[dec->packet_length++] = *b++; + + if (dec->packet_type == TTUSB_DEC_PACKET_PVA && + dec->packet_length == 8) { + dec->packet_state++; + dec->packet_payload_length = 8 + + (dec->packet[6] << 8) + + dec->packet[7]; + } else if (dec->packet_type == + TTUSB_DEC_PACKET_SECTION && + dec->packet_length == 5) { + dec->packet_state++; + dec->packet_payload_length = 5 + + ((dec->packet[3] & 0x0f) << 8) + + dec->packet[4]; + } + + length--; + break; + + case 6: { + int remainder = dec->packet_payload_length - + dec->packet_length; + + if (length >= remainder) { + memcpy(dec->packet + dec->packet_length, + b, remainder); + dec->packet_length += remainder; + b += remainder; + length -= remainder; + dec->packet_state++; + } else { + memcpy(&dec->packet[dec->packet_length], + b, length); + dec->packet_length += length; + length = 0; + } + + break; + } + + case 7: { + int tail = 4; + + dec->packet[dec->packet_length++] = *b++; + + if (dec->packet_type == TTUSB_DEC_PACKET_SECTION && + dec->packet_payload_length % 2) + tail++; + + if (dec->packet_length == + dec->packet_payload_length + tail) { + ttusb_dec_process_packet(dec); + dec->packet_state = 0; + } + + length--; + break; + } + + default: + printk("%s: illegal packet state encountered.\n", + __func__); + dec->packet_state = 0; + } + } +} + +static void ttusb_dec_process_urb_frame_list(unsigned long data) +{ + struct ttusb_dec *dec = (struct ttusb_dec *)data; + struct list_head *item; + struct urb_frame *frame; + unsigned long flags; + + while (1) { + spin_lock_irqsave(&dec->urb_frame_list_lock, flags); + if ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) { + frame = list_entry(item, struct urb_frame, + urb_frame_list); + list_del(&frame->urb_frame_list); + } else { + spin_unlock_irqrestore(&dec->urb_frame_list_lock, + flags); + return; + } + spin_unlock_irqrestore(&dec->urb_frame_list_lock, flags); + + ttusb_dec_process_urb_frame(dec, frame->data, frame->length); + kfree(frame); + } +} + +static void ttusb_dec_process_urb(struct urb *urb) +{ + struct ttusb_dec *dec = urb->context; + + if (!urb->status) { + int i; + + for (i = 0; i < FRAMES_PER_ISO_BUF; i++) { + struct usb_iso_packet_descriptor *d; + u8 *b; + int length; + struct urb_frame *frame; + + d = &urb->iso_frame_desc[i]; + b = urb->transfer_buffer + d->offset; + length = d->actual_length; + + if ((frame = kmalloc(sizeof(struct urb_frame), + GFP_ATOMIC))) { + unsigned long flags; + + memcpy(frame->data, b, length); + frame->length = length; + + spin_lock_irqsave(&dec->urb_frame_list_lock, + flags); + list_add_tail(&frame->urb_frame_list, + &dec->urb_frame_list); + spin_unlock_irqrestore(&dec->urb_frame_list_lock, + flags); + + tasklet_schedule(&dec->urb_tasklet); + } + } + } else { + /* -ENOENT is expected when unlinking urbs */ + if (urb->status != -ENOENT) + dprintk("%s: urb error: %d\n", __func__, + urb->status); + } + + if (dec->iso_stream_count) + usb_submit_urb(urb, GFP_ATOMIC); +} + +static void ttusb_dec_setup_urbs(struct ttusb_dec *dec) +{ + int i, j, buffer_offset = 0; + + dprintk("%s\n", __func__); + + for (i = 0; i < ISO_BUF_COUNT; i++) { + int frame_offset = 0; + struct urb *urb = dec->iso_urb[i]; + + urb->dev = dec->udev; + urb->context = dec; + urb->complete = ttusb_dec_process_urb; + urb->pipe = dec->in_pipe; + urb->transfer_flags = URB_ISO_ASAP; + urb->interval = 1; + urb->number_of_packets = FRAMES_PER_ISO_BUF; + urb->transfer_buffer_length = ISO_FRAME_SIZE * + FRAMES_PER_ISO_BUF; + urb->transfer_buffer = dec->iso_buffer + buffer_offset; + buffer_offset += ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF; + + for (j = 0; j < FRAMES_PER_ISO_BUF; j++) { + urb->iso_frame_desc[j].offset = frame_offset; + urb->iso_frame_desc[j].length = ISO_FRAME_SIZE; + frame_offset += ISO_FRAME_SIZE; + } + } +} + +static void ttusb_dec_stop_iso_xfer(struct ttusb_dec *dec) +{ + int i; + + dprintk("%s\n", __func__); + + if (mutex_lock_interruptible(&dec->iso_mutex)) + return; + + dec->iso_stream_count--; + + if (!dec->iso_stream_count) { + for (i = 0; i < ISO_BUF_COUNT; i++) + usb_kill_urb(dec->iso_urb[i]); + } + + mutex_unlock(&dec->iso_mutex); +} + +/* Setting the interface of the DEC tends to take down the USB communications + * for a short period, so it's important not to call this function just before + * trying to talk to it. + */ +static int ttusb_dec_set_interface(struct ttusb_dec *dec, + enum ttusb_dec_interface interface) +{ + int result = 0; + u8 b[] = { 0x05 }; + + if (interface != dec->interface) { + switch (interface) { + case TTUSB_DEC_INTERFACE_INITIAL: + result = usb_set_interface(dec->udev, 0, 0); + break; + case TTUSB_DEC_INTERFACE_IN: + result = ttusb_dec_send_command(dec, 0x80, sizeof(b), + b, NULL, NULL); + if (result) + return result; + result = usb_set_interface(dec->udev, 0, 8); + break; + case TTUSB_DEC_INTERFACE_OUT: + result = usb_set_interface(dec->udev, 0, 1); + break; + } + + if (result) + return result; + + dec->interface = interface; + } + + return 0; +} + +static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec) +{ + int i, result; + + dprintk("%s\n", __func__); + + if (mutex_lock_interruptible(&dec->iso_mutex)) + return -EAGAIN; + + if (!dec->iso_stream_count) { + ttusb_dec_setup_urbs(dec); + + dec->packet_state = 0; + dec->v_pes_postbytes = 0; + dec->next_packet_id = 0; + + for (i = 0; i < ISO_BUF_COUNT; i++) { + if ((result = usb_submit_urb(dec->iso_urb[i], + GFP_ATOMIC))) { + printk("%s: failed urb submission %d: " + "error %d\n", __func__, i, result); + + while (i) { + usb_kill_urb(dec->iso_urb[i - 1]); + i--; + } + + mutex_unlock(&dec->iso_mutex); + return result; + } + } + } + + dec->iso_stream_count++; + + mutex_unlock(&dec->iso_mutex); + + return 0; +} + +static int ttusb_dec_start_ts_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ttusb_dec *dec = dvbdmx->priv; + u8 b0[] = { 0x05 }; + int result = 0; + + dprintk("%s\n", __func__); + + dprintk(" ts_type:"); + + if (dvbdmxfeed->ts_type & TS_DECODER) + dprintk(" TS_DECODER"); + + if (dvbdmxfeed->ts_type & TS_PACKET) + dprintk(" TS_PACKET"); + + if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) + dprintk(" TS_PAYLOAD_ONLY"); + + dprintk("\n"); + + switch (dvbdmxfeed->pes_type) { + + case DMX_TS_PES_VIDEO: + dprintk(" pes_type: DMX_TS_PES_VIDEO\n"); + dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid; + dec->pid[DMX_PES_VIDEO] = dvbdmxfeed->pid; + dec->video_filter = dvbdmxfeed->filter; + ttusb_dec_set_pids(dec); + break; + + case DMX_TS_PES_AUDIO: + dprintk(" pes_type: DMX_TS_PES_AUDIO\n"); + dec->pid[DMX_PES_AUDIO] = dvbdmxfeed->pid; + dec->audio_filter = dvbdmxfeed->filter; + ttusb_dec_set_pids(dec); + break; + + case DMX_TS_PES_TELETEXT: + dec->pid[DMX_PES_TELETEXT] = dvbdmxfeed->pid; + dprintk(" pes_type: DMX_TS_PES_TELETEXT(not supported)\n"); + return -ENOSYS; + + case DMX_TS_PES_PCR: + dprintk(" pes_type: DMX_TS_PES_PCR\n"); + dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid; + ttusb_dec_set_pids(dec); + break; + + case DMX_TS_PES_OTHER: + dprintk(" pes_type: DMX_TS_PES_OTHER(not supported)\n"); + return -ENOSYS; + + default: + dprintk(" pes_type: unknown (%d)\n", dvbdmxfeed->pes_type); + return -EINVAL; + + } + + result = ttusb_dec_send_command(dec, 0x80, sizeof(b0), b0, NULL, NULL); + if (result) + return result; + + dec->pva_stream_count++; + return ttusb_dec_start_iso_xfer(dec); +} + +static int ttusb_dec_start_sec_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct ttusb_dec *dec = dvbdmxfeed->demux->priv; + u8 b0[] = { 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00 }; + __be16 pid; + u8 c[COMMAND_PACKET_SIZE]; + int c_length; + int result; + struct filter_info *finfo; + unsigned long flags; + u8 x = 1; + + dprintk("%s\n", __func__); + + pid = htons(dvbdmxfeed->pid); + memcpy(&b0[0], &pid, 2); + memcpy(&b0[4], &x, 1); + memcpy(&b0[5], &dvbdmxfeed->filter->filter.filter_value[0], 1); + + result = ttusb_dec_send_command(dec, 0x60, sizeof(b0), b0, + &c_length, c); + + if (!result) { + if (c_length == 2) { + if (!(finfo = kmalloc(sizeof(struct filter_info), + GFP_ATOMIC))) + return -ENOMEM; + + finfo->stream_id = c[1]; + finfo->filter = dvbdmxfeed->filter; + + spin_lock_irqsave(&dec->filter_info_list_lock, flags); + list_add_tail(&finfo->filter_info_list, + &dec->filter_info_list); + spin_unlock_irqrestore(&dec->filter_info_list_lock, + flags); + + dvbdmxfeed->priv = finfo; + + dec->filter_stream_count++; + return ttusb_dec_start_iso_xfer(dec); + } + + return -EAGAIN; + } else + return result; +} + +static int ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + + dprintk("%s\n", __func__); + + if (!dvbdmx->dmx.frontend) + return -EINVAL; + + dprintk(" pid: 0x%04X\n", dvbdmxfeed->pid); + + switch (dvbdmxfeed->type) { + + case DMX_TYPE_TS: + return ttusb_dec_start_ts_feed(dvbdmxfeed); + break; + + case DMX_TYPE_SEC: + return ttusb_dec_start_sec_feed(dvbdmxfeed); + break; + + default: + dprintk(" type: unknown (%d)\n", dvbdmxfeed->type); + return -EINVAL; + + } +} + +static int ttusb_dec_stop_ts_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct ttusb_dec *dec = dvbdmxfeed->demux->priv; + u8 b0[] = { 0x00 }; + + ttusb_dec_send_command(dec, 0x81, sizeof(b0), b0, NULL, NULL); + + dec->pva_stream_count--; + + ttusb_dec_stop_iso_xfer(dec); + + return 0; +} + +static int ttusb_dec_stop_sec_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct ttusb_dec *dec = dvbdmxfeed->demux->priv; + u8 b0[] = { 0x00, 0x00 }; + struct filter_info *finfo = (struct filter_info *)dvbdmxfeed->priv; + unsigned long flags; + + b0[1] = finfo->stream_id; + spin_lock_irqsave(&dec->filter_info_list_lock, flags); + list_del(&finfo->filter_info_list); + spin_unlock_irqrestore(&dec->filter_info_list_lock, flags); + kfree(finfo); + ttusb_dec_send_command(dec, 0x62, sizeof(b0), b0, NULL, NULL); + + dec->filter_stream_count--; + + ttusb_dec_stop_iso_xfer(dec); + + return 0; +} + +static int ttusb_dec_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + dprintk("%s\n", __func__); + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + return ttusb_dec_stop_ts_feed(dvbdmxfeed); + break; + + case DMX_TYPE_SEC: + return ttusb_dec_stop_sec_feed(dvbdmxfeed); + break; + } + + return 0; +} + +static void ttusb_dec_free_iso_urbs(struct ttusb_dec *dec) +{ + int i; + + dprintk("%s\n", __func__); + + for (i = 0; i < ISO_BUF_COUNT; i++) + usb_free_urb(dec->iso_urb[i]); + + pci_free_consistent(NULL, + ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * + ISO_BUF_COUNT), + dec->iso_buffer, dec->iso_dma_handle); +} + +static int ttusb_dec_alloc_iso_urbs(struct ttusb_dec *dec) +{ + int i; + + dprintk("%s\n", __func__); + + dec->iso_buffer = pci_alloc_consistent(NULL, + ISO_FRAME_SIZE * + (FRAMES_PER_ISO_BUF * + ISO_BUF_COUNT), + &dec->iso_dma_handle); + + if (!dec->iso_buffer) { + dprintk("%s: pci_alloc_consistent - not enough memory\n", + __func__); + return -ENOMEM; + } + + memset(dec->iso_buffer, 0, + ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * ISO_BUF_COUNT)); + + for (i = 0; i < ISO_BUF_COUNT; i++) { + struct urb *urb; + + if (!(urb = usb_alloc_urb(FRAMES_PER_ISO_BUF, GFP_ATOMIC))) { + ttusb_dec_free_iso_urbs(dec); + return -ENOMEM; + } + + dec->iso_urb[i] = urb; + } + + ttusb_dec_setup_urbs(dec); + + return 0; +} + +static void ttusb_dec_init_tasklet(struct ttusb_dec *dec) +{ + spin_lock_init(&dec->urb_frame_list_lock); + INIT_LIST_HEAD(&dec->urb_frame_list); + tasklet_init(&dec->urb_tasklet, ttusb_dec_process_urb_frame_list, + (unsigned long)dec); +} + +static int ttusb_init_rc( struct ttusb_dec *dec) +{ + struct input_dev *input_dev; + u8 b[] = { 0x00, 0x01 }; + int i; + int err; + + usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys)); + strlcat(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "ttusb_dec remote control"; + input_dev->phys = dec->rc_phys; + input_dev->evbit[0] = BIT_MASK(EV_KEY); + input_dev->keycodesize = sizeof(u16); + input_dev->keycodemax = 0x1a; + input_dev->keycode = rc_keys; + + for (i = 0; i < ARRAY_SIZE(rc_keys); i++) + set_bit(rc_keys[i], input_dev->keybit); + + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + dec->rc_input_dev = input_dev; + if (usb_submit_urb(dec->irq_urb, GFP_KERNEL)) + printk("%s: usb_submit_urb failed\n",__func__); + /* enable irq pipe */ + ttusb_dec_send_command(dec,0xb0,sizeof(b),b,NULL,NULL); + + return 0; +} + +static void ttusb_dec_init_v_pes(struct ttusb_dec *dec) +{ + dprintk("%s\n", __func__); + + dec->v_pes[0] = 0x00; + dec->v_pes[1] = 0x00; + dec->v_pes[2] = 0x01; + dec->v_pes[3] = 0xe0; +} + +static int ttusb_dec_init_usb(struct ttusb_dec *dec) +{ + dprintk("%s\n", __func__); + + mutex_init(&dec->usb_mutex); + mutex_init(&dec->iso_mutex); + + dec->command_pipe = usb_sndbulkpipe(dec->udev, COMMAND_PIPE); + dec->result_pipe = usb_rcvbulkpipe(dec->udev, RESULT_PIPE); + dec->in_pipe = usb_rcvisocpipe(dec->udev, IN_PIPE); + dec->out_pipe = usb_sndisocpipe(dec->udev, OUT_PIPE); + dec->irq_pipe = usb_rcvintpipe(dec->udev, IRQ_PIPE); + + if(enable_rc) { + dec->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if(!dec->irq_urb) { + return -ENOMEM; + } + dec->irq_buffer = usb_alloc_coherent(dec->udev,IRQ_PACKET_SIZE, + GFP_ATOMIC, &dec->irq_dma_handle); + if(!dec->irq_buffer) { + usb_free_urb(dec->irq_urb); + return -ENOMEM; + } + usb_fill_int_urb(dec->irq_urb, dec->udev,dec->irq_pipe, + dec->irq_buffer, IRQ_PACKET_SIZE, + ttusb_dec_handle_irq, dec, 1); + dec->irq_urb->transfer_dma = dec->irq_dma_handle; + dec->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + + return ttusb_dec_alloc_iso_urbs(dec); +} + +static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) +{ + int i, j, actual_len, result, size, trans_count; + u8 b0[] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x61, 0x00 }; + u8 b1[] = { 0x61 }; + u8 *b; + char idstring[21]; + const u8 *firmware = NULL; + size_t firmware_size = 0; + u16 firmware_csum = 0; + __be16 firmware_csum_ns; + __be32 firmware_size_nl; + u32 crc32_csum, crc32_check; + __be32 tmp; + const struct firmware *fw_entry = NULL; + + dprintk("%s\n", __func__); + + if (request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev)) { + printk(KERN_ERR "%s: Firmware (%s) unavailable.\n", + __func__, dec->firmware_name); + return 1; + } + + firmware = fw_entry->data; + firmware_size = fw_entry->size; + + if (firmware_size < 60) { + printk("%s: firmware size too small for DSP code (%zu < 60).\n", + __func__, firmware_size); + release_firmware(fw_entry); + return -1; + } + + /* a 32 bit checksum over the first 56 bytes of the DSP Code is stored + at offset 56 of file, so use it to check if the firmware file is + valid. */ + crc32_csum = crc32(~0L, firmware, 56) ^ ~0L; + memcpy(&tmp, &firmware[56], 4); + crc32_check = ntohl(tmp); + if (crc32_csum != crc32_check) { + printk("%s: crc32 check of DSP code failed (calculated " + "0x%08x != 0x%08x in file), file invalid.\n", + __func__, crc32_csum, crc32_check); + release_firmware(fw_entry); + return -1; + } + memcpy(idstring, &firmware[36], 20); + idstring[20] = '\0'; + printk(KERN_INFO "ttusb_dec: found DSP code \"%s\".\n", idstring); + + firmware_size_nl = htonl(firmware_size); + memcpy(b0, &firmware_size_nl, 4); + firmware_csum = crc16(~0, firmware, firmware_size) ^ ~0; + firmware_csum_ns = htons(firmware_csum); + memcpy(&b0[6], &firmware_csum_ns, 2); + + result = ttusb_dec_send_command(dec, 0x41, sizeof(b0), b0, NULL, NULL); + + if (result) { + release_firmware(fw_entry); + return result; + } + + trans_count = 0; + j = 0; + + b = kmalloc(ARM_PACKET_SIZE, GFP_KERNEL); + if (b == NULL) { + release_firmware(fw_entry); + return -ENOMEM; + } + + for (i = 0; i < firmware_size; i += COMMAND_PACKET_SIZE) { + size = firmware_size - i; + if (size > COMMAND_PACKET_SIZE) + size = COMMAND_PACKET_SIZE; + + b[j + 0] = 0xaa; + b[j + 1] = trans_count++; + b[j + 2] = 0xf0; + b[j + 3] = size; + memcpy(&b[j + 4], &firmware[i], size); + + j += COMMAND_PACKET_SIZE + 4; + + if (j >= ARM_PACKET_SIZE) { + result = usb_bulk_msg(dec->udev, dec->command_pipe, b, + ARM_PACKET_SIZE, &actual_len, + 100); + j = 0; + } else if (size < COMMAND_PACKET_SIZE) { + result = usb_bulk_msg(dec->udev, dec->command_pipe, b, + j - COMMAND_PACKET_SIZE + size, + &actual_len, 100); + } + } + + result = ttusb_dec_send_command(dec, 0x43, sizeof(b1), b1, NULL, NULL); + + release_firmware(fw_entry); + kfree(b); + + return result; +} + +static int ttusb_dec_init_stb(struct ttusb_dec *dec) +{ + int result; + unsigned int mode = 0, model = 0, version = 0; + + dprintk("%s\n", __func__); + + result = ttusb_dec_get_stb_state(dec, &mode, &model, &version); + + if (!result) { + if (!mode) { + if (version == 0xABCDEFAB) + printk(KERN_INFO "ttusb_dec: no version " + "info in Firmware\n"); + else + printk(KERN_INFO "ttusb_dec: Firmware " + "%x.%02x%c%c\n", + version >> 24, (version >> 16) & 0xff, + (version >> 8) & 0xff, version & 0xff); + + result = ttusb_dec_boot_dsp(dec); + if (result) + return result; + else + return 1; + } else { + /* We can't trust the USB IDs that some firmwares + give the box */ + switch (model) { + case 0x00070001: + case 0x00070008: + case 0x0007000c: + ttusb_dec_set_model(dec, TTUSB_DEC3000S); + break; + case 0x00070009: + case 0x00070013: + ttusb_dec_set_model(dec, TTUSB_DEC2000T); + break; + case 0x00070011: + ttusb_dec_set_model(dec, TTUSB_DEC2540T); + break; + default: + printk(KERN_ERR "%s: unknown model returned " + "by firmware (%08x) - please report\n", + __func__, model); + return -1; + break; + } + + if (version >= 0x01770000) + dec->can_playback = 1; + + return 0; + } + } + else + return result; +} + +static int ttusb_dec_init_dvb(struct ttusb_dec *dec) +{ + int result; + + dprintk("%s\n", __func__); + + if ((result = dvb_register_adapter(&dec->adapter, + dec->model_name, THIS_MODULE, + &dec->udev->dev, + adapter_nr)) < 0) { + printk("%s: dvb_register_adapter failed: error %d\n", + __func__, result); + + return result; + } + + dec->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + + dec->demux.priv = (void *)dec; + dec->demux.filternum = 31; + dec->demux.feednum = 31; + dec->demux.start_feed = ttusb_dec_start_feed; + dec->demux.stop_feed = ttusb_dec_stop_feed; + dec->demux.write_to_decoder = NULL; + + if ((result = dvb_dmx_init(&dec->demux)) < 0) { + printk("%s: dvb_dmx_init failed: error %d\n", __func__, + result); + + dvb_unregister_adapter(&dec->adapter); + + return result; + } + + dec->dmxdev.filternum = 32; + dec->dmxdev.demux = &dec->demux.dmx; + dec->dmxdev.capabilities = 0; + + if ((result = dvb_dmxdev_init(&dec->dmxdev, &dec->adapter)) < 0) { + printk("%s: dvb_dmxdev_init failed: error %d\n", + __func__, result); + + dvb_dmx_release(&dec->demux); + dvb_unregister_adapter(&dec->adapter); + + return result; + } + + dec->frontend.source = DMX_FRONTEND_0; + + if ((result = dec->demux.dmx.add_frontend(&dec->demux.dmx, + &dec->frontend)) < 0) { + printk("%s: dvb_dmx_init failed: error %d\n", __func__, + result); + + dvb_dmxdev_release(&dec->dmxdev); + dvb_dmx_release(&dec->demux); + dvb_unregister_adapter(&dec->adapter); + + return result; + } + + if ((result = dec->demux.dmx.connect_frontend(&dec->demux.dmx, + &dec->frontend)) < 0) { + printk("%s: dvb_dmx_init failed: error %d\n", __func__, + result); + + dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend); + dvb_dmxdev_release(&dec->dmxdev); + dvb_dmx_release(&dec->demux); + dvb_unregister_adapter(&dec->adapter); + + return result; + } + + dvb_net_init(&dec->adapter, &dec->dvb_net, &dec->demux.dmx); + + return 0; +} + +static void ttusb_dec_exit_dvb(struct ttusb_dec *dec) +{ + dprintk("%s\n", __func__); + + dvb_net_release(&dec->dvb_net); + dec->demux.dmx.close(&dec->demux.dmx); + dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend); + dvb_dmxdev_release(&dec->dmxdev); + dvb_dmx_release(&dec->demux); + if (dec->fe) { + dvb_unregister_frontend(dec->fe); + if (dec->fe->ops.release) + dec->fe->ops.release(dec->fe); + } + dvb_unregister_adapter(&dec->adapter); +} + +static void ttusb_dec_exit_rc(struct ttusb_dec *dec) +{ + + dprintk("%s\n", __func__); + /* we have to check whether the irq URB is already submitted. + * As the irq is submitted after the interface is changed, + * this is the best method i figured out. + * Any others?*/ + if (dec->interface == TTUSB_DEC_INTERFACE_IN) + usb_kill_urb(dec->irq_urb); + + usb_free_urb(dec->irq_urb); + + usb_free_coherent(dec->udev,IRQ_PACKET_SIZE, + dec->irq_buffer, dec->irq_dma_handle); + + if (dec->rc_input_dev) { + input_unregister_device(dec->rc_input_dev); + dec->rc_input_dev = NULL; + } +} + + +static void ttusb_dec_exit_usb(struct ttusb_dec *dec) +{ + int i; + + dprintk("%s\n", __func__); + + dec->iso_stream_count = 0; + + for (i = 0; i < ISO_BUF_COUNT; i++) + usb_kill_urb(dec->iso_urb[i]); + + ttusb_dec_free_iso_urbs(dec); +} + +static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec) +{ + struct list_head *item; + struct urb_frame *frame; + + tasklet_kill(&dec->urb_tasklet); + + while ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) { + frame = list_entry(item, struct urb_frame, urb_frame_list); + list_del(&frame->urb_frame_list); + kfree(frame); + } +} + +static void ttusb_dec_init_filters(struct ttusb_dec *dec) +{ + INIT_LIST_HEAD(&dec->filter_info_list); + spin_lock_init(&dec->filter_info_list_lock); +} + +static void ttusb_dec_exit_filters(struct ttusb_dec *dec) +{ + struct list_head *item; + struct filter_info *finfo; + + while ((item = dec->filter_info_list.next) != &dec->filter_info_list) { + finfo = list_entry(item, struct filter_info, filter_info_list); + list_del(&finfo->filter_info_list); + kfree(finfo); + } +} + +static int fe_send_command(struct dvb_frontend* fe, const u8 command, + int param_length, const u8 params[], + int *result_length, u8 cmd_result[]) +{ + struct ttusb_dec* dec = fe->dvb->priv; + return ttusb_dec_send_command(dec, command, param_length, params, result_length, cmd_result); +} + +static struct ttusbdecfe_config fe_config = { + .send_command = fe_send_command +}; + +static int ttusb_dec_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct ttusb_dec *dec; + + dprintk("%s\n", __func__); + + udev = interface_to_usbdev(intf); + + if (!(dec = kzalloc(sizeof(struct ttusb_dec), GFP_KERNEL))) { + printk("%s: couldn't allocate memory.\n", __func__); + return -ENOMEM; + } + + usb_set_intfdata(intf, (void *)dec); + + switch (id->idProduct) { + case 0x1006: + ttusb_dec_set_model(dec, TTUSB_DEC3000S); + break; + + case 0x1008: + ttusb_dec_set_model(dec, TTUSB_DEC2000T); + break; + + case 0x1009: + ttusb_dec_set_model(dec, TTUSB_DEC2540T); + break; + } + + dec->udev = udev; + + if (ttusb_dec_init_usb(dec)) + return 0; + if (ttusb_dec_init_stb(dec)) { + ttusb_dec_exit_usb(dec); + return 0; + } + ttusb_dec_init_dvb(dec); + + dec->adapter.priv = dec; + switch (id->idProduct) { + case 0x1006: + dec->fe = ttusbdecfe_dvbs_attach(&fe_config); + break; + + case 0x1008: + case 0x1009: + dec->fe = ttusbdecfe_dvbt_attach(&fe_config); + break; + } + + if (dec->fe == NULL) { + printk("dvb-ttusb-dec: A frontend driver was not found for device [%04x:%04x]\n", + le16_to_cpu(dec->udev->descriptor.idVendor), + le16_to_cpu(dec->udev->descriptor.idProduct)); + } else { + if (dvb_register_frontend(&dec->adapter, dec->fe)) { + printk("budget-ci: Frontend registration failed!\n"); + if (dec->fe->ops.release) + dec->fe->ops.release(dec->fe); + dec->fe = NULL; + } + } + + ttusb_dec_init_v_pes(dec); + ttusb_dec_init_filters(dec); + ttusb_dec_init_tasklet(dec); + + dec->active = 1; + + ttusb_dec_set_interface(dec, TTUSB_DEC_INTERFACE_IN); + + if (enable_rc) + ttusb_init_rc(dec); + + return 0; +} + +static void ttusb_dec_disconnect(struct usb_interface *intf) +{ + struct ttusb_dec *dec = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + dprintk("%s\n", __func__); + + if (dec->active) { + ttusb_dec_exit_tasklet(dec); + ttusb_dec_exit_filters(dec); + if(enable_rc) + ttusb_dec_exit_rc(dec); + ttusb_dec_exit_usb(dec); + ttusb_dec_exit_dvb(dec); + } + + kfree(dec); +} + +static void ttusb_dec_set_model(struct ttusb_dec *dec, + enum ttusb_dec_model model) +{ + dec->model = model; + + switch (model) { + case TTUSB_DEC2000T: + dec->model_name = "DEC2000-t"; + dec->firmware_name = "dvb-ttusb-dec-2000t.fw"; + break; + + case TTUSB_DEC2540T: + dec->model_name = "DEC2540-t"; + dec->firmware_name = "dvb-ttusb-dec-2540t.fw"; + break; + + case TTUSB_DEC3000S: + dec->model_name = "DEC3000-s"; + dec->firmware_name = "dvb-ttusb-dec-3000s.fw"; + break; + } +} + +static struct usb_device_id ttusb_dec_table[] = { + {USB_DEVICE(0x0b48, 0x1006)}, /* DEC3000-s */ + /*{USB_DEVICE(0x0b48, 0x1007)}, Unconfirmed */ + {USB_DEVICE(0x0b48, 0x1008)}, /* DEC2000-t */ + {USB_DEVICE(0x0b48, 0x1009)}, /* DEC2540-t */ + {} +}; + +static struct usb_driver ttusb_dec_driver = { + .name = "ttusb-dec", + .probe = ttusb_dec_probe, + .disconnect = ttusb_dec_disconnect, + .id_table = ttusb_dec_table, +}; + +module_usb_driver(ttusb_dec_driver); + +MODULE_AUTHOR("Alex Woods "); +MODULE_DESCRIPTION(DRIVER_NAME); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, ttusb_dec_table); diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c new file mode 100644 index 000000000000..5c45c9d0712d --- /dev/null +++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c @@ -0,0 +1,298 @@ +/* + * TTUSB DEC Frontend Driver + * + * Copyright (C) 2003-2004 Alex Woods + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "dvb_frontend.h" +#include "ttusbdecfe.h" + + +#define LOF_HI 10600000 +#define LOF_LO 9750000 + +struct ttusbdecfe_state { + + /* configuration settings */ + const struct ttusbdecfe_config* config; + + struct dvb_frontend frontend; + + u8 hi_band; + u8 voltage; +}; + + +static int ttusbdecfe_dvbs_read_status(struct dvb_frontend *fe, + fe_status_t *status) +{ + *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; +} + + +static int ttusbdecfe_dvbt_read_status(struct dvb_frontend *fe, + fe_status_t *status) +{ + struct ttusbdecfe_state* state = fe->demodulator_priv; + u8 b[] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + u8 result[4]; + int len, ret; + + *status=0; + + ret=state->config->send_command(fe, 0x73, sizeof(b), b, &len, result); + if(ret) + return ret; + + if(len != 4) { + printk(KERN_ERR "%s: unexpected reply\n", __func__); + return -EIO; + } + + switch(result[3]) { + case 1: /* not tuned yet */ + case 2: /* no signal/no lock*/ + break; + case 3: /* signal found and locked*/ + *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; + break; + case 4: + *status = FE_TIMEDOUT; + break; + default: + pr_info("%s: returned unknown value: %d\n", + __func__, result[3]); + return -EIO; + } + + return 0; +} + +static int ttusbdecfe_dvbt_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + u8 b[] = { 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff }; + + __be32 freq = htonl(p->frequency / 1000); + memcpy(&b[4], &freq, sizeof (u32)); + state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL); + + return 0; +} + +static int ttusbdecfe_dvbt_get_tune_settings(struct dvb_frontend* fe, + struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 1500; + /* Drift compensation makes no sense for DVB-T */ + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + + u8 b[] = { 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + __be32 freq; + __be32 sym_rate; + __be32 band; + __be32 lnb_voltage; + + freq = htonl(p->frequency + + (state->hi_band ? LOF_HI : LOF_LO)); + memcpy(&b[4], &freq, sizeof(u32)); + sym_rate = htonl(p->symbol_rate); + memcpy(&b[12], &sym_rate, sizeof(u32)); + band = htonl(state->hi_band ? LOF_HI : LOF_LO); + memcpy(&b[24], &band, sizeof(u32)); + lnb_voltage = htonl(state->voltage); + memcpy(&b[28], &lnb_voltage, sizeof(u32)); + + state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL); + + return 0; +} + +static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + u8 b[] = { 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; + + memcpy(&b[4], cmd->msg, cmd->msg_len); + + state->config->send_command(fe, 0x72, + sizeof(b) - (6 - cmd->msg_len), b, + NULL, NULL); + + return 0; +} + + +static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + + state->hi_band = (SEC_TONE_ON == tone); + + return 0; +} + + +static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + state->voltage = 13; + break; + case SEC_VOLTAGE_18: + state->voltage = 18; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void ttusbdecfe_release(struct dvb_frontend* fe) +{ + struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops ttusbdecfe_dvbt_ops; + +struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config) +{ + struct ttusbdecfe_state* state = NULL; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + /* setup the state */ + state->config = config; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &ttusbdecfe_dvbt_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +static struct dvb_frontend_ops ttusbdecfe_dvbs_ops; + +struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config) +{ + struct ttusbdecfe_state* state = NULL; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + /* setup the state */ + state->config = config; + state->voltage = 0; + state->hi_band = 0; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &ttusbdecfe_dvbs_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +static struct dvb_frontend_ops ttusbdecfe_dvbt_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "TechnoTrend/Hauppauge DEC2000-t Frontend", + .frequency_min = 51000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = ttusbdecfe_release, + + .set_frontend = ttusbdecfe_dvbt_set_frontend, + + .get_tune_settings = ttusbdecfe_dvbt_get_tune_settings, + + .read_status = ttusbdecfe_dvbt_read_status, +}; + +static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "TechnoTrend/Hauppauge DEC3000-s Frontend", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, + .symbol_rate_min = 1000000, /* guessed */ + .symbol_rate_max = 45000000, /* guessed */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = ttusbdecfe_release, + + .set_frontend = ttusbdecfe_dvbs_set_frontend, + + .read_status = ttusbdecfe_dvbs_read_status, + + .diseqc_send_master_cmd = ttusbdecfe_dvbs_diseqc_send_master_cmd, + .set_voltage = ttusbdecfe_dvbs_set_voltage, + .set_tone = ttusbdecfe_dvbs_set_tone, +}; + +MODULE_DESCRIPTION("TTUSB DEC DVB-T/S Demodulator driver"); +MODULE_AUTHOR("Alex Woods/Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(ttusbdecfe_dvbt_attach); +EXPORT_SYMBOL(ttusbdecfe_dvbs_attach); diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.h b/drivers/media/usb/ttusb-dec/ttusbdecfe.h new file mode 100644 index 000000000000..15ccc3d1a20e --- /dev/null +++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.h @@ -0,0 +1,38 @@ +/* + * TTUSB DEC Driver + * + * Copyright (C) 2003-2004 Alex Woods + * + * This program is free software; 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 TTUSBDECFE_H +#define TTUSBDECFE_H + +#include + +struct ttusbdecfe_config +{ + int (*send_command)(struct dvb_frontend* fe, const u8 command, + int param_length, const u8 params[], + int *result_length, u8 cmd_result[]); +}; + +extern struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config); + +extern struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config); + +#endif // TTUSBDECFE_H -- cgit v1.2.3 From 25aee3debe0464f6c680173041fa3de30ec9ff54 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 14 Jun 2012 16:35:57 -0300 Subject: [media] Rename media/dvb as media/pci The remaining dvb drivers are pci, so rename them to match the bus. Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/kdapi.xml | 2 +- drivers/media/Kconfig | 2 +- drivers/media/Makefile | 2 +- drivers/media/dvb/Kconfig | 50 - drivers/media/dvb/Makefile | 13 - drivers/media/dvb/b2c2/Kconfig | 45 - drivers/media/dvb/b2c2/Makefile | 16 - drivers/media/dvb/b2c2/flexcop-common.h | 185 -- drivers/media/dvb/b2c2/flexcop-dma.c | 172 -- drivers/media/dvb/b2c2/flexcop-eeprom.c | 147 -- drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 678 ------ drivers/media/dvb/b2c2/flexcop-hw-filter.c | 232 -- drivers/media/dvb/b2c2/flexcop-i2c.c | 288 --- drivers/media/dvb/b2c2/flexcop-misc.c | 86 - drivers/media/dvb/b2c2/flexcop-pci.c | 450 ---- drivers/media/dvb/b2c2/flexcop-reg.h | 166 -- drivers/media/dvb/b2c2/flexcop-sram.c | 363 --- drivers/media/dvb/b2c2/flexcop-usb.c | 587 ----- drivers/media/dvb/b2c2/flexcop-usb.h | 111 - drivers/media/dvb/b2c2/flexcop.c | 324 --- drivers/media/dvb/b2c2/flexcop.h | 29 - drivers/media/dvb/b2c2/flexcop_ibi_value_be.h | 455 ---- drivers/media/dvb/b2c2/flexcop_ibi_value_le.h | 455 ---- drivers/media/dvb/bt8xx/Kconfig | 22 - drivers/media/dvb/bt8xx/Makefile | 6 - drivers/media/dvb/bt8xx/bt878.c | 609 ----- drivers/media/dvb/bt8xx/bt878.h | 159 -- drivers/media/dvb/bt8xx/dst.c | 1873 ---------------- drivers/media/dvb/bt8xx/dst_ca.c | 726 ------ drivers/media/dvb/bt8xx/dst_ca.h | 58 - drivers/media/dvb/bt8xx/dst_common.h | 182 -- drivers/media/dvb/bt8xx/dst_priv.h | 35 - drivers/media/dvb/bt8xx/dvb-bt8xx.c | 975 -------- drivers/media/dvb/bt8xx/dvb-bt8xx.h | 63 - drivers/media/dvb/ddbridge/Kconfig | 18 - drivers/media/dvb/ddbridge/Makefile | 14 - drivers/media/dvb/ddbridge/ddbridge-core.c | 1723 --------------- drivers/media/dvb/ddbridge/ddbridge-regs.h | 151 -- drivers/media/dvb/ddbridge/ddbridge.h | 185 -- drivers/media/dvb/dm1105/Kconfig | 20 - drivers/media/dvb/dm1105/Makefile | 3 - drivers/media/dvb/dm1105/dm1105.c | 1248 ----------- drivers/media/dvb/mantis/Kconfig | 38 - drivers/media/dvb/mantis/Makefile | 28 - drivers/media/dvb/mantis/hopper_cards.c | 277 --- drivers/media/dvb/mantis/hopper_vp3028.c | 88 - drivers/media/dvb/mantis/hopper_vp3028.h | 30 - drivers/media/dvb/mantis/mantis_ca.c | 209 -- drivers/media/dvb/mantis/mantis_ca.h | 27 - drivers/media/dvb/mantis/mantis_cards.c | 307 --- drivers/media/dvb/mantis/mantis_common.h | 179 -- drivers/media/dvb/mantis/mantis_core.c | 235 -- drivers/media/dvb/mantis/mantis_core.h | 57 - drivers/media/dvb/mantis/mantis_dma.c | 230 -- drivers/media/dvb/mantis/mantis_dma.h | 30 - drivers/media/dvb/mantis/mantis_dvb.c | 301 --- drivers/media/dvb/mantis/mantis_dvb.h | 35 - drivers/media/dvb/mantis/mantis_evm.c | 117 - drivers/media/dvb/mantis/mantis_hif.c | 239 -- drivers/media/dvb/mantis/mantis_hif.h | 29 - drivers/media/dvb/mantis/mantis_i2c.c | 266 --- drivers/media/dvb/mantis/mantis_i2c.h | 30 - drivers/media/dvb/mantis/mantis_input.c | 159 -- drivers/media/dvb/mantis/mantis_ioc.c | 124 -- drivers/media/dvb/mantis/mantis_ioc.h | 51 - drivers/media/dvb/mantis/mantis_link.h | 83 - drivers/media/dvb/mantis/mantis_pci.c | 170 -- drivers/media/dvb/mantis/mantis_pci.h | 27 - drivers/media/dvb/mantis/mantis_pcmcia.c | 121 - drivers/media/dvb/mantis/mantis_reg.h | 197 -- drivers/media/dvb/mantis/mantis_uart.c | 188 -- drivers/media/dvb/mantis/mantis_uart.h | 58 - drivers/media/dvb/mantis/mantis_vp1033.c | 212 -- drivers/media/dvb/mantis/mantis_vp1033.h | 30 - drivers/media/dvb/mantis/mantis_vp1034.c | 120 - drivers/media/dvb/mantis/mantis_vp1034.h | 33 - drivers/media/dvb/mantis/mantis_vp1041.c | 357 --- drivers/media/dvb/mantis/mantis_vp1041.h | 33 - drivers/media/dvb/mantis/mantis_vp2033.c | 188 -- drivers/media/dvb/mantis/mantis_vp2033.h | 30 - drivers/media/dvb/mantis/mantis_vp2040.c | 187 -- drivers/media/dvb/mantis/mantis_vp2040.h | 32 - drivers/media/dvb/mantis/mantis_vp3028.c | 38 - drivers/media/dvb/mantis/mantis_vp3028.h | 33 - drivers/media/dvb/mantis/mantis_vp3030.c | 105 - drivers/media/dvb/mantis/mantis_vp3030.h | 30 - drivers/media/dvb/ngene/Kconfig | 13 - drivers/media/dvb/ngene/Makefile | 14 - drivers/media/dvb/ngene/ngene-cards.c | 823 ------- drivers/media/dvb/ngene/ngene-core.c | 1707 -------------- drivers/media/dvb/ngene/ngene-dvb.c | 261 --- drivers/media/dvb/ngene/ngene-i2c.c | 176 -- drivers/media/dvb/ngene/ngene.h | 921 -------- drivers/media/dvb/pluto2/Kconfig | 15 - drivers/media/dvb/pluto2/Makefile | 3 - drivers/media/dvb/pluto2/pluto2.c | 815 ------- drivers/media/dvb/pt1/Kconfig | 12 - drivers/media/dvb/pt1/Makefile | 5 - drivers/media/dvb/pt1/pt1.c | 1246 ----------- drivers/media/dvb/pt1/va1j5jf8007s.c | 735 ------- drivers/media/dvb/pt1/va1j5jf8007s.h | 46 - drivers/media/dvb/pt1/va1j5jf8007t.c | 536 ----- drivers/media/dvb/pt1/va1j5jf8007t.h | 46 - drivers/media/dvb/ttpci/Kconfig | 159 -- drivers/media/dvb/ttpci/Makefile | 21 - drivers/media/dvb/ttpci/av7110.c | 2939 ------------------------- drivers/media/dvb/ttpci/av7110.h | 314 --- drivers/media/dvb/ttpci/av7110_av.c | 1626 -------------- drivers/media/dvb/ttpci/av7110_av.h | 30 - drivers/media/dvb/ttpci/av7110_ca.c | 387 ---- drivers/media/dvb/ttpci/av7110_ca.h | 14 - drivers/media/dvb/ttpci/av7110_hw.c | 1208 ---------- drivers/media/dvb/ttpci/av7110_hw.h | 495 ----- drivers/media/dvb/ttpci/av7110_ipack.c | 403 ---- drivers/media/dvb/ttpci/av7110_ipack.h | 12 - drivers/media/dvb/ttpci/av7110_ir.c | 415 ---- drivers/media/dvb/ttpci/av7110_v4l.c | 966 -------- drivers/media/dvb/ttpci/budget-av.c | 1640 -------------- drivers/media/dvb/ttpci/budget-ci.c | 1591 ------------- drivers/media/dvb/ttpci/budget-core.c | 602 ----- drivers/media/dvb/ttpci/budget-patch.c | 680 ------ drivers/media/dvb/ttpci/budget.c | 871 -------- drivers/media/dvb/ttpci/budget.h | 124 -- drivers/media/dvb/ttpci/ttpci-eeprom.c | 176 -- drivers/media/dvb/ttpci/ttpci-eeprom.h | 34 - drivers/media/pci/Kconfig | 50 + drivers/media/pci/Makefile | 13 + drivers/media/pci/b2c2/Kconfig | 45 + drivers/media/pci/b2c2/Makefile | 16 + drivers/media/pci/b2c2/flexcop-common.h | 185 ++ drivers/media/pci/b2c2/flexcop-dma.c | 172 ++ drivers/media/pci/b2c2/flexcop-eeprom.c | 147 ++ drivers/media/pci/b2c2/flexcop-fe-tuner.c | 678 ++++++ drivers/media/pci/b2c2/flexcop-hw-filter.c | 232 ++ drivers/media/pci/b2c2/flexcop-i2c.c | 288 +++ drivers/media/pci/b2c2/flexcop-misc.c | 86 + drivers/media/pci/b2c2/flexcop-pci.c | 450 ++++ drivers/media/pci/b2c2/flexcop-reg.h | 166 ++ drivers/media/pci/b2c2/flexcop-sram.c | 363 +++ drivers/media/pci/b2c2/flexcop-usb.c | 587 +++++ drivers/media/pci/b2c2/flexcop-usb.h | 111 + drivers/media/pci/b2c2/flexcop.c | 324 +++ drivers/media/pci/b2c2/flexcop.h | 29 + drivers/media/pci/b2c2/flexcop_ibi_value_be.h | 455 ++++ drivers/media/pci/b2c2/flexcop_ibi_value_le.h | 455 ++++ drivers/media/pci/bt8xx/Kconfig | 22 + drivers/media/pci/bt8xx/Makefile | 6 + drivers/media/pci/bt8xx/bt878.c | 609 +++++ drivers/media/pci/bt8xx/bt878.h | 159 ++ drivers/media/pci/bt8xx/dst.c | 1873 ++++++++++++++++ drivers/media/pci/bt8xx/dst_ca.c | 726 ++++++ drivers/media/pci/bt8xx/dst_ca.h | 58 + drivers/media/pci/bt8xx/dst_common.h | 182 ++ drivers/media/pci/bt8xx/dst_priv.h | 35 + drivers/media/pci/bt8xx/dvb-bt8xx.c | 975 ++++++++ drivers/media/pci/bt8xx/dvb-bt8xx.h | 63 + drivers/media/pci/ddbridge/Kconfig | 18 + drivers/media/pci/ddbridge/Makefile | 14 + drivers/media/pci/ddbridge/ddbridge-core.c | 1723 +++++++++++++++ drivers/media/pci/ddbridge/ddbridge-regs.h | 151 ++ drivers/media/pci/ddbridge/ddbridge.h | 185 ++ drivers/media/pci/dm1105/Kconfig | 20 + drivers/media/pci/dm1105/Makefile | 3 + drivers/media/pci/dm1105/dm1105.c | 1248 +++++++++++ drivers/media/pci/mantis/Kconfig | 38 + drivers/media/pci/mantis/Makefile | 28 + drivers/media/pci/mantis/hopper_cards.c | 277 +++ drivers/media/pci/mantis/hopper_vp3028.c | 88 + drivers/media/pci/mantis/hopper_vp3028.h | 30 + drivers/media/pci/mantis/mantis_ca.c | 209 ++ drivers/media/pci/mantis/mantis_ca.h | 27 + drivers/media/pci/mantis/mantis_cards.c | 307 +++ drivers/media/pci/mantis/mantis_common.h | 179 ++ drivers/media/pci/mantis/mantis_core.c | 235 ++ drivers/media/pci/mantis/mantis_core.h | 57 + drivers/media/pci/mantis/mantis_dma.c | 230 ++ drivers/media/pci/mantis/mantis_dma.h | 30 + drivers/media/pci/mantis/mantis_dvb.c | 301 +++ drivers/media/pci/mantis/mantis_dvb.h | 35 + drivers/media/pci/mantis/mantis_evm.c | 117 + drivers/media/pci/mantis/mantis_hif.c | 239 ++ drivers/media/pci/mantis/mantis_hif.h | 29 + drivers/media/pci/mantis/mantis_i2c.c | 266 +++ drivers/media/pci/mantis/mantis_i2c.h | 30 + drivers/media/pci/mantis/mantis_input.c | 159 ++ drivers/media/pci/mantis/mantis_ioc.c | 124 ++ drivers/media/pci/mantis/mantis_ioc.h | 51 + drivers/media/pci/mantis/mantis_link.h | 83 + drivers/media/pci/mantis/mantis_pci.c | 170 ++ drivers/media/pci/mantis/mantis_pci.h | 27 + drivers/media/pci/mantis/mantis_pcmcia.c | 121 + drivers/media/pci/mantis/mantis_reg.h | 197 ++ drivers/media/pci/mantis/mantis_uart.c | 188 ++ drivers/media/pci/mantis/mantis_uart.h | 58 + drivers/media/pci/mantis/mantis_vp1033.c | 212 ++ drivers/media/pci/mantis/mantis_vp1033.h | 30 + drivers/media/pci/mantis/mantis_vp1034.c | 120 + drivers/media/pci/mantis/mantis_vp1034.h | 33 + drivers/media/pci/mantis/mantis_vp1041.c | 357 +++ drivers/media/pci/mantis/mantis_vp1041.h | 33 + drivers/media/pci/mantis/mantis_vp2033.c | 188 ++ drivers/media/pci/mantis/mantis_vp2033.h | 30 + drivers/media/pci/mantis/mantis_vp2040.c | 187 ++ drivers/media/pci/mantis/mantis_vp2040.h | 32 + drivers/media/pci/mantis/mantis_vp3028.c | 38 + drivers/media/pci/mantis/mantis_vp3028.h | 33 + drivers/media/pci/mantis/mantis_vp3030.c | 105 + drivers/media/pci/mantis/mantis_vp3030.h | 30 + drivers/media/pci/ngene/Kconfig | 13 + drivers/media/pci/ngene/Makefile | 14 + drivers/media/pci/ngene/ngene-cards.c | 823 +++++++ drivers/media/pci/ngene/ngene-core.c | 1707 ++++++++++++++ drivers/media/pci/ngene/ngene-dvb.c | 261 +++ drivers/media/pci/ngene/ngene-i2c.c | 176 ++ drivers/media/pci/ngene/ngene.h | 921 ++++++++ drivers/media/pci/pluto2/Kconfig | 15 + drivers/media/pci/pluto2/Makefile | 3 + drivers/media/pci/pluto2/pluto2.c | 815 +++++++ drivers/media/pci/pt1/Kconfig | 12 + drivers/media/pci/pt1/Makefile | 5 + drivers/media/pci/pt1/pt1.c | 1246 +++++++++++ drivers/media/pci/pt1/va1j5jf8007s.c | 735 +++++++ drivers/media/pci/pt1/va1j5jf8007s.h | 46 + drivers/media/pci/pt1/va1j5jf8007t.c | 536 +++++ drivers/media/pci/pt1/va1j5jf8007t.h | 46 + drivers/media/pci/ttpci/Kconfig | 159 ++ drivers/media/pci/ttpci/Makefile | 21 + drivers/media/pci/ttpci/av7110.c | 2939 +++++++++++++++++++++++++ drivers/media/pci/ttpci/av7110.h | 314 +++ drivers/media/pci/ttpci/av7110_av.c | 1626 ++++++++++++++ drivers/media/pci/ttpci/av7110_av.h | 30 + drivers/media/pci/ttpci/av7110_ca.c | 387 ++++ drivers/media/pci/ttpci/av7110_ca.h | 14 + drivers/media/pci/ttpci/av7110_hw.c | 1208 ++++++++++ drivers/media/pci/ttpci/av7110_hw.h | 495 +++++ drivers/media/pci/ttpci/av7110_ipack.c | 403 ++++ drivers/media/pci/ttpci/av7110_ipack.h | 12 + drivers/media/pci/ttpci/av7110_ir.c | 415 ++++ drivers/media/pci/ttpci/av7110_v4l.c | 966 ++++++++ drivers/media/pci/ttpci/budget-av.c | 1640 ++++++++++++++ drivers/media/pci/ttpci/budget-ci.c | 1591 +++++++++++++ drivers/media/pci/ttpci/budget-core.c | 602 +++++ drivers/media/pci/ttpci/budget-patch.c | 680 ++++++ drivers/media/pci/ttpci/budget.c | 871 ++++++++ drivers/media/pci/ttpci/budget.h | 124 ++ drivers/media/pci/ttpci/ttpci-eeprom.c | 176 ++ drivers/media/pci/ttpci/ttpci-eeprom.h | 34 + drivers/media/usb/dvb-usb/Makefile | 2 +- 248 files changed, 40365 insertions(+), 40365 deletions(-) delete mode 100644 drivers/media/dvb/Kconfig delete mode 100644 drivers/media/dvb/Makefile delete mode 100644 drivers/media/dvb/b2c2/Kconfig delete mode 100644 drivers/media/dvb/b2c2/Makefile delete mode 100644 drivers/media/dvb/b2c2/flexcop-common.h delete mode 100644 drivers/media/dvb/b2c2/flexcop-dma.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-eeprom.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-fe-tuner.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-hw-filter.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-i2c.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-misc.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-pci.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-reg.h delete mode 100644 drivers/media/dvb/b2c2/flexcop-sram.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-usb.c delete mode 100644 drivers/media/dvb/b2c2/flexcop-usb.h delete mode 100644 drivers/media/dvb/b2c2/flexcop.c delete mode 100644 drivers/media/dvb/b2c2/flexcop.h delete mode 100644 drivers/media/dvb/b2c2/flexcop_ibi_value_be.h delete mode 100644 drivers/media/dvb/b2c2/flexcop_ibi_value_le.h delete mode 100644 drivers/media/dvb/bt8xx/Kconfig delete mode 100644 drivers/media/dvb/bt8xx/Makefile delete mode 100644 drivers/media/dvb/bt8xx/bt878.c delete mode 100644 drivers/media/dvb/bt8xx/bt878.h delete mode 100644 drivers/media/dvb/bt8xx/dst.c delete mode 100644 drivers/media/dvb/bt8xx/dst_ca.c delete mode 100644 drivers/media/dvb/bt8xx/dst_ca.h delete mode 100644 drivers/media/dvb/bt8xx/dst_common.h delete mode 100644 drivers/media/dvb/bt8xx/dst_priv.h delete mode 100644 drivers/media/dvb/bt8xx/dvb-bt8xx.c delete mode 100644 drivers/media/dvb/bt8xx/dvb-bt8xx.h delete mode 100644 drivers/media/dvb/ddbridge/Kconfig delete mode 100644 drivers/media/dvb/ddbridge/Makefile delete mode 100644 drivers/media/dvb/ddbridge/ddbridge-core.c delete mode 100644 drivers/media/dvb/ddbridge/ddbridge-regs.h delete mode 100644 drivers/media/dvb/ddbridge/ddbridge.h delete mode 100644 drivers/media/dvb/dm1105/Kconfig delete mode 100644 drivers/media/dvb/dm1105/Makefile delete mode 100644 drivers/media/dvb/dm1105/dm1105.c delete mode 100644 drivers/media/dvb/mantis/Kconfig delete mode 100644 drivers/media/dvb/mantis/Makefile delete mode 100644 drivers/media/dvb/mantis/hopper_cards.c delete mode 100644 drivers/media/dvb/mantis/hopper_vp3028.c delete mode 100644 drivers/media/dvb/mantis/hopper_vp3028.h delete mode 100644 drivers/media/dvb/mantis/mantis_ca.c delete mode 100644 drivers/media/dvb/mantis/mantis_ca.h delete mode 100644 drivers/media/dvb/mantis/mantis_cards.c delete mode 100644 drivers/media/dvb/mantis/mantis_common.h delete mode 100644 drivers/media/dvb/mantis/mantis_core.c delete mode 100644 drivers/media/dvb/mantis/mantis_core.h delete mode 100644 drivers/media/dvb/mantis/mantis_dma.c delete mode 100644 drivers/media/dvb/mantis/mantis_dma.h delete mode 100644 drivers/media/dvb/mantis/mantis_dvb.c delete mode 100644 drivers/media/dvb/mantis/mantis_dvb.h delete mode 100644 drivers/media/dvb/mantis/mantis_evm.c delete mode 100644 drivers/media/dvb/mantis/mantis_hif.c delete mode 100644 drivers/media/dvb/mantis/mantis_hif.h delete mode 100644 drivers/media/dvb/mantis/mantis_i2c.c delete mode 100644 drivers/media/dvb/mantis/mantis_i2c.h delete mode 100644 drivers/media/dvb/mantis/mantis_input.c delete mode 100644 drivers/media/dvb/mantis/mantis_ioc.c delete mode 100644 drivers/media/dvb/mantis/mantis_ioc.h delete mode 100644 drivers/media/dvb/mantis/mantis_link.h delete mode 100644 drivers/media/dvb/mantis/mantis_pci.c delete mode 100644 drivers/media/dvb/mantis/mantis_pci.h delete mode 100644 drivers/media/dvb/mantis/mantis_pcmcia.c delete mode 100644 drivers/media/dvb/mantis/mantis_reg.h delete mode 100644 drivers/media/dvb/mantis/mantis_uart.c delete mode 100644 drivers/media/dvb/mantis/mantis_uart.h delete mode 100644 drivers/media/dvb/mantis/mantis_vp1033.c delete mode 100644 drivers/media/dvb/mantis/mantis_vp1033.h delete mode 100644 drivers/media/dvb/mantis/mantis_vp1034.c delete mode 100644 drivers/media/dvb/mantis/mantis_vp1034.h delete mode 100644 drivers/media/dvb/mantis/mantis_vp1041.c delete mode 100644 drivers/media/dvb/mantis/mantis_vp1041.h delete mode 100644 drivers/media/dvb/mantis/mantis_vp2033.c delete mode 100644 drivers/media/dvb/mantis/mantis_vp2033.h delete mode 100644 drivers/media/dvb/mantis/mantis_vp2040.c delete mode 100644 drivers/media/dvb/mantis/mantis_vp2040.h delete mode 100644 drivers/media/dvb/mantis/mantis_vp3028.c delete mode 100644 drivers/media/dvb/mantis/mantis_vp3028.h delete mode 100644 drivers/media/dvb/mantis/mantis_vp3030.c delete mode 100644 drivers/media/dvb/mantis/mantis_vp3030.h delete mode 100644 drivers/media/dvb/ngene/Kconfig delete mode 100644 drivers/media/dvb/ngene/Makefile delete mode 100644 drivers/media/dvb/ngene/ngene-cards.c delete mode 100644 drivers/media/dvb/ngene/ngene-core.c delete mode 100644 drivers/media/dvb/ngene/ngene-dvb.c delete mode 100644 drivers/media/dvb/ngene/ngene-i2c.c delete mode 100644 drivers/media/dvb/ngene/ngene.h delete mode 100644 drivers/media/dvb/pluto2/Kconfig delete mode 100644 drivers/media/dvb/pluto2/Makefile delete mode 100644 drivers/media/dvb/pluto2/pluto2.c delete mode 100644 drivers/media/dvb/pt1/Kconfig delete mode 100644 drivers/media/dvb/pt1/Makefile delete mode 100644 drivers/media/dvb/pt1/pt1.c delete mode 100644 drivers/media/dvb/pt1/va1j5jf8007s.c delete mode 100644 drivers/media/dvb/pt1/va1j5jf8007s.h delete mode 100644 drivers/media/dvb/pt1/va1j5jf8007t.c delete mode 100644 drivers/media/dvb/pt1/va1j5jf8007t.h delete mode 100644 drivers/media/dvb/ttpci/Kconfig delete mode 100644 drivers/media/dvb/ttpci/Makefile delete mode 100644 drivers/media/dvb/ttpci/av7110.c delete mode 100644 drivers/media/dvb/ttpci/av7110.h delete mode 100644 drivers/media/dvb/ttpci/av7110_av.c delete mode 100644 drivers/media/dvb/ttpci/av7110_av.h delete mode 100644 drivers/media/dvb/ttpci/av7110_ca.c delete mode 100644 drivers/media/dvb/ttpci/av7110_ca.h delete mode 100644 drivers/media/dvb/ttpci/av7110_hw.c delete mode 100644 drivers/media/dvb/ttpci/av7110_hw.h delete mode 100644 drivers/media/dvb/ttpci/av7110_ipack.c delete mode 100644 drivers/media/dvb/ttpci/av7110_ipack.h delete mode 100644 drivers/media/dvb/ttpci/av7110_ir.c delete mode 100644 drivers/media/dvb/ttpci/av7110_v4l.c delete mode 100644 drivers/media/dvb/ttpci/budget-av.c delete mode 100644 drivers/media/dvb/ttpci/budget-ci.c delete mode 100644 drivers/media/dvb/ttpci/budget-core.c delete mode 100644 drivers/media/dvb/ttpci/budget-patch.c delete mode 100644 drivers/media/dvb/ttpci/budget.c delete mode 100644 drivers/media/dvb/ttpci/budget.h delete mode 100644 drivers/media/dvb/ttpci/ttpci-eeprom.c delete mode 100644 drivers/media/dvb/ttpci/ttpci-eeprom.h create mode 100644 drivers/media/pci/Kconfig create mode 100644 drivers/media/pci/Makefile create mode 100644 drivers/media/pci/b2c2/Kconfig create mode 100644 drivers/media/pci/b2c2/Makefile create mode 100644 drivers/media/pci/b2c2/flexcop-common.h create mode 100644 drivers/media/pci/b2c2/flexcop-dma.c create mode 100644 drivers/media/pci/b2c2/flexcop-eeprom.c create mode 100644 drivers/media/pci/b2c2/flexcop-fe-tuner.c create mode 100644 drivers/media/pci/b2c2/flexcop-hw-filter.c create mode 100644 drivers/media/pci/b2c2/flexcop-i2c.c create mode 100644 drivers/media/pci/b2c2/flexcop-misc.c create mode 100644 drivers/media/pci/b2c2/flexcop-pci.c create mode 100644 drivers/media/pci/b2c2/flexcop-reg.h create mode 100644 drivers/media/pci/b2c2/flexcop-sram.c create mode 100644 drivers/media/pci/b2c2/flexcop-usb.c create mode 100644 drivers/media/pci/b2c2/flexcop-usb.h create mode 100644 drivers/media/pci/b2c2/flexcop.c create mode 100644 drivers/media/pci/b2c2/flexcop.h create mode 100644 drivers/media/pci/b2c2/flexcop_ibi_value_be.h create mode 100644 drivers/media/pci/b2c2/flexcop_ibi_value_le.h create mode 100644 drivers/media/pci/bt8xx/Kconfig create mode 100644 drivers/media/pci/bt8xx/Makefile create mode 100644 drivers/media/pci/bt8xx/bt878.c create mode 100644 drivers/media/pci/bt8xx/bt878.h create mode 100644 drivers/media/pci/bt8xx/dst.c create mode 100644 drivers/media/pci/bt8xx/dst_ca.c create mode 100644 drivers/media/pci/bt8xx/dst_ca.h create mode 100644 drivers/media/pci/bt8xx/dst_common.h create mode 100644 drivers/media/pci/bt8xx/dst_priv.h create mode 100644 drivers/media/pci/bt8xx/dvb-bt8xx.c create mode 100644 drivers/media/pci/bt8xx/dvb-bt8xx.h create mode 100644 drivers/media/pci/ddbridge/Kconfig create mode 100644 drivers/media/pci/ddbridge/Makefile create mode 100644 drivers/media/pci/ddbridge/ddbridge-core.c create mode 100644 drivers/media/pci/ddbridge/ddbridge-regs.h create mode 100644 drivers/media/pci/ddbridge/ddbridge.h create mode 100644 drivers/media/pci/dm1105/Kconfig create mode 100644 drivers/media/pci/dm1105/Makefile create mode 100644 drivers/media/pci/dm1105/dm1105.c create mode 100644 drivers/media/pci/mantis/Kconfig create mode 100644 drivers/media/pci/mantis/Makefile create mode 100644 drivers/media/pci/mantis/hopper_cards.c create mode 100644 drivers/media/pci/mantis/hopper_vp3028.c create mode 100644 drivers/media/pci/mantis/hopper_vp3028.h create mode 100644 drivers/media/pci/mantis/mantis_ca.c create mode 100644 drivers/media/pci/mantis/mantis_ca.h create mode 100644 drivers/media/pci/mantis/mantis_cards.c create mode 100644 drivers/media/pci/mantis/mantis_common.h create mode 100644 drivers/media/pci/mantis/mantis_core.c create mode 100644 drivers/media/pci/mantis/mantis_core.h create mode 100644 drivers/media/pci/mantis/mantis_dma.c create mode 100644 drivers/media/pci/mantis/mantis_dma.h create mode 100644 drivers/media/pci/mantis/mantis_dvb.c create mode 100644 drivers/media/pci/mantis/mantis_dvb.h create mode 100644 drivers/media/pci/mantis/mantis_evm.c create mode 100644 drivers/media/pci/mantis/mantis_hif.c create mode 100644 drivers/media/pci/mantis/mantis_hif.h create mode 100644 drivers/media/pci/mantis/mantis_i2c.c create mode 100644 drivers/media/pci/mantis/mantis_i2c.h create mode 100644 drivers/media/pci/mantis/mantis_input.c create mode 100644 drivers/media/pci/mantis/mantis_ioc.c create mode 100644 drivers/media/pci/mantis/mantis_ioc.h create mode 100644 drivers/media/pci/mantis/mantis_link.h create mode 100644 drivers/media/pci/mantis/mantis_pci.c create mode 100644 drivers/media/pci/mantis/mantis_pci.h create mode 100644 drivers/media/pci/mantis/mantis_pcmcia.c create mode 100644 drivers/media/pci/mantis/mantis_reg.h create mode 100644 drivers/media/pci/mantis/mantis_uart.c create mode 100644 drivers/media/pci/mantis/mantis_uart.h create mode 100644 drivers/media/pci/mantis/mantis_vp1033.c create mode 100644 drivers/media/pci/mantis/mantis_vp1033.h create mode 100644 drivers/media/pci/mantis/mantis_vp1034.c create mode 100644 drivers/media/pci/mantis/mantis_vp1034.h create mode 100644 drivers/media/pci/mantis/mantis_vp1041.c create mode 100644 drivers/media/pci/mantis/mantis_vp1041.h create mode 100644 drivers/media/pci/mantis/mantis_vp2033.c create mode 100644 drivers/media/pci/mantis/mantis_vp2033.h create mode 100644 drivers/media/pci/mantis/mantis_vp2040.c create mode 100644 drivers/media/pci/mantis/mantis_vp2040.h create mode 100644 drivers/media/pci/mantis/mantis_vp3028.c create mode 100644 drivers/media/pci/mantis/mantis_vp3028.h create mode 100644 drivers/media/pci/mantis/mantis_vp3030.c create mode 100644 drivers/media/pci/mantis/mantis_vp3030.h create mode 100644 drivers/media/pci/ngene/Kconfig create mode 100644 drivers/media/pci/ngene/Makefile create mode 100644 drivers/media/pci/ngene/ngene-cards.c create mode 100644 drivers/media/pci/ngene/ngene-core.c create mode 100644 drivers/media/pci/ngene/ngene-dvb.c create mode 100644 drivers/media/pci/ngene/ngene-i2c.c create mode 100644 drivers/media/pci/ngene/ngene.h create mode 100644 drivers/media/pci/pluto2/Kconfig create mode 100644 drivers/media/pci/pluto2/Makefile create mode 100644 drivers/media/pci/pluto2/pluto2.c create mode 100644 drivers/media/pci/pt1/Kconfig create mode 100644 drivers/media/pci/pt1/Makefile create mode 100644 drivers/media/pci/pt1/pt1.c create mode 100644 drivers/media/pci/pt1/va1j5jf8007s.c create mode 100644 drivers/media/pci/pt1/va1j5jf8007s.h create mode 100644 drivers/media/pci/pt1/va1j5jf8007t.c create mode 100644 drivers/media/pci/pt1/va1j5jf8007t.h create mode 100644 drivers/media/pci/ttpci/Kconfig create mode 100644 drivers/media/pci/ttpci/Makefile create mode 100644 drivers/media/pci/ttpci/av7110.c create mode 100644 drivers/media/pci/ttpci/av7110.h create mode 100644 drivers/media/pci/ttpci/av7110_av.c create mode 100644 drivers/media/pci/ttpci/av7110_av.h create mode 100644 drivers/media/pci/ttpci/av7110_ca.c create mode 100644 drivers/media/pci/ttpci/av7110_ca.h create mode 100644 drivers/media/pci/ttpci/av7110_hw.c create mode 100644 drivers/media/pci/ttpci/av7110_hw.h create mode 100644 drivers/media/pci/ttpci/av7110_ipack.c create mode 100644 drivers/media/pci/ttpci/av7110_ipack.h create mode 100644 drivers/media/pci/ttpci/av7110_ir.c create mode 100644 drivers/media/pci/ttpci/av7110_v4l.c create mode 100644 drivers/media/pci/ttpci/budget-av.c create mode 100644 drivers/media/pci/ttpci/budget-ci.c create mode 100644 drivers/media/pci/ttpci/budget-core.c create mode 100644 drivers/media/pci/ttpci/budget-patch.c create mode 100644 drivers/media/pci/ttpci/budget.c create mode 100644 drivers/media/pci/ttpci/budget.h create mode 100644 drivers/media/pci/ttpci/ttpci-eeprom.c create mode 100644 drivers/media/pci/ttpci/ttpci-eeprom.h (limited to 'drivers/media/usb') diff --git a/Documentation/DocBook/media/dvb/kdapi.xml b/Documentation/DocBook/media/dvb/kdapi.xml index 6c67481eaa4b..6c11ec52cbee 100644 --- a/Documentation/DocBook/media/dvb/kdapi.xml +++ b/Documentation/DocBook/media/dvb/kdapi.xml @@ -2,7 +2,7 @@ The kernel demux API defines a driver-internal interface for registering low-level, hardware specific driver to a hardware independent demux layer. It is only of interest for DVB device driver writers. The header file for this API is named demux.h and located in -drivers/media/dvb/dvb-core. +drivers/media/dvb-core. Maintainer note: This section must be reviewed. It is probably out of date. diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 26da8b8721d5..81c8662a1a7c 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -162,7 +162,7 @@ source "drivers/media/radio/Kconfig" # source "drivers/media/dvb-core/Kconfig" -source "drivers/media/dvb/Kconfig" +source "drivers/media/pci/Kconfig" source "drivers/media/usb/Kconfig" comment "Supported FireWire (IEEE 1394) Adapters" diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 46a8dc3337b3..90ec998a8e6a 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -11,5 +11,5 @@ endif obj-y += v4l2-core/ common/ rc/ video/ obj-$(CONFIG_VIDEO_DEV) += radio/ -obj-$(CONFIG_DVB_CORE) += dvb-core/ dvb/ dvb-frontends/ usb/ +obj-$(CONFIG_DVB_CORE) += dvb-core/ pci/ dvb-frontends/ usb/ obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig deleted file mode 100644 index e2565a45de9b..000000000000 --- a/drivers/media/dvb/Kconfig +++ /dev/null @@ -1,50 +0,0 @@ -# -# DVB device configuration -# - -menuconfig DVB_CAPTURE_DRIVERS - bool "DVB/ATSC adapters" - depends on DVB_CORE - default y - ---help--- - Say Y to select Digital TV adapters - -if DVB_CAPTURE_DRIVERS && DVB_CORE - -comment "Supported SAA7146 based PCI Adapters" - depends on DVB_CORE && PCI && I2C -source "drivers/media/dvb/ttpci/Kconfig" - -comment "Supported FlexCopII (B2C2) Adapters" - depends on DVB_CORE && (PCI || USB) && I2C -source "drivers/media/dvb/b2c2/Kconfig" - -comment "Supported BT878 Adapters" - depends on DVB_CORE && PCI && I2C -source "drivers/media/dvb/bt8xx/Kconfig" - -comment "Supported Pluto2 Adapters" - depends on DVB_CORE && PCI && I2C -source "drivers/media/dvb/pluto2/Kconfig" - -comment "Supported SDMC DM1105 Adapters" - depends on DVB_CORE && PCI && I2C -source "drivers/media/dvb/dm1105/Kconfig" - -comment "Supported Earthsoft PT1 Adapters" - depends on DVB_CORE && PCI && I2C -source "drivers/media/dvb/pt1/Kconfig" - -comment "Supported Mantis Adapters" - depends on DVB_CORE && PCI && I2C - source "drivers/media/dvb/mantis/Kconfig" - -comment "Supported nGene Adapters" - depends on DVB_CORE && PCI && I2C - source "drivers/media/dvb/ngene/Kconfig" - -comment "Supported ddbridge ('Octopus') Adapters" - depends on DVB_CORE && PCI && I2C - source "drivers/media/dvb/ddbridge/Kconfig" - -endif # DVB_CAPTURE_DRIVERS diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile deleted file mode 100644 index c5fa43a275ae..000000000000 --- a/drivers/media/dvb/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# -# Makefile for the kernel multimedia device drivers. -# - -obj-y := ttpci/ \ - b2c2/ \ - bt8xx/ \ - pluto2/ \ - dm1105/ \ - pt1/ \ - mantis/ \ - ngene/ \ - ddbridge/ diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig deleted file mode 100644 index 9e5781400744..000000000000 --- a/drivers/media/dvb/b2c2/Kconfig +++ /dev/null @@ -1,45 +0,0 @@ -config DVB_B2C2_FLEXCOP - tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters" - depends on DVB_CORE && I2C - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_BCM3510 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_S5H1420 if !DVB_FE_CUSTOMISE - select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_CX24123 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE - help - Support for the digital TV receiver chip made by B2C2 Inc. included in - Technisats PCI cards and USB boxes. - - Say Y if you own such a device and want to use it. - -config DVB_B2C2_FLEXCOP_PCI - tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI" - depends on DVB_B2C2_FLEXCOP && PCI && I2C - help - Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2. - - Say Y if you own such a device and want to use it. - -config DVB_B2C2_FLEXCOP_USB - tristate "Technisat/B2C2 Air/Sky/Cable2PC USB" - depends on DVB_B2C2_FLEXCOP && USB && I2C - help - Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2, - - Say Y if you own such a device and want to use it. - -config DVB_B2C2_FLEXCOP_DEBUG - bool "Enable debug for the B2C2 FlexCop drivers" - depends on DVB_B2C2_FLEXCOP - help - Say Y if you want to enable the module option to control debug messages - of all B2C2 FlexCop drivers. diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile deleted file mode 100644 index 7a1f5ce6d322..000000000000 --- a/drivers/media/dvb/b2c2/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ - flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o -obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o - -ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),) -b2c2-flexcop-objs += flexcop-dma.o -endif - -b2c2-flexcop-pci-objs = flexcop-pci.o -obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o - -b2c2-flexcop-usb-objs = flexcop-usb.o -obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o - -ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/dvb/b2c2/flexcop-common.h deleted file mode 100644 index 437912e49824..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-common.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-common.h - common header file for device-specific source files - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_COMMON_H__ -#define __FLEXCOP_COMMON_H__ - -#include -#include -#include - -#include "flexcop-reg.h" - -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_filter.h" -#include "dvb_net.h" -#include "dvb_frontend.h" - -#define FC_MAX_FEED 256 - -#ifndef FC_LOG_PREFIX -#warning please define a log prefix for your file, using a default one -#define FC_LOG_PREFIX "b2c2-undef" -#endif - -/* Steal from usb.h */ -#undef err -#define err(format, arg...) \ - printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg) -#undef info -#define info(format, arg...) \ - printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg) -#undef warn -#define warn(format, arg...) \ - printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg) - -struct flexcop_dma { - struct pci_dev *pdev; - - u8 *cpu_addr0; - dma_addr_t dma_addr0; - u8 *cpu_addr1; - dma_addr_t dma_addr1; - u32 size; /* size of each address in bytes */ -}; - -struct flexcop_i2c_adapter { - struct flexcop_device *fc; - struct i2c_adapter i2c_adap; - - u8 no_base_addr; - flexcop_i2c_port_t port; -}; - -/* Control structure for data definitions that are common to - * the B2C2-based PCI and USB devices. - */ -struct flexcop_device { - /* general */ - struct device *dev; /* for firmware_class */ - -#define FC_STATE_DVB_INIT 0x01 -#define FC_STATE_I2C_INIT 0x02 -#define FC_STATE_FE_INIT 0x04 - int init_state; - - /* device information */ - int has_32_hw_pid_filter; - flexcop_revision_t rev; - flexcop_device_type_t dev_type; - flexcop_bus_t bus_type; - - /* dvb stuff */ - struct dvb_adapter dvb_adapter; - struct dvb_frontend *fe; - struct dvb_net dvbnet; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - int (*fe_sleep) (struct dvb_frontend *); - - struct flexcop_i2c_adapter fc_i2c_adap[3]; - struct mutex i2c_mutex; - struct module *owner; - - /* options and status */ - int extra_feedcount; - int feedcount; - int pid_filtering; - int fullts_streaming_state; - - /* bus specific callbacks */ - flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *, - flexcop_ibi_register); - int (*write_ibi_reg) (struct flexcop_device *, - flexcop_ibi_register, flexcop_ibi_value); - int (*i2c_request) (struct flexcop_i2c_adapter *, - flexcop_access_op_t, u8 chipaddr, u8 addr, u8 *buf, u16 len); - int (*stream_control) (struct flexcop_device *, int); - int (*get_mac_addr) (struct flexcop_device *fc, int extended); - void *bus_specific; -}; - -/* exported prototypes */ - -/* from flexcop.c */ -void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len); -void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no); - -struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len); -void flexcop_device_kfree(struct flexcop_device *); - -int flexcop_device_initialize(struct flexcop_device *); -void flexcop_device_exit(struct flexcop_device *fc); -void flexcop_reset_block_300(struct flexcop_device *fc); - -/* from flexcop-dma.c */ -int flexcop_dma_allocate(struct pci_dev *pdev, - struct flexcop_dma *dma, u32 size); -void flexcop_dma_free(struct flexcop_dma *dma); - -int flexcop_dma_control_timer_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, int onoff); -int flexcop_dma_control_size_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, int onoff); -int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, - flexcop_dma_index_t dma_idx); -int flexcop_dma_xfer_control(struct flexcop_device *fc, - flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index, - int onoff); -int flexcop_dma_config_timer(struct flexcop_device *fc, - flexcop_dma_index_t dma_idx, u8 cycles); - -/* from flexcop-eeprom.c */ -/* the PCI part uses this call to get the MAC address, the USB part has its own */ -int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended); - -/* from flexcop-i2c.c */ -/* the PCI part uses this a i2c_request callback, whereas the usb part has its own - * one. We have it in flexcop-i2c.c, because it is going via the actual - * I2C-channel of the flexcop. - */ -int flexcop_i2c_request(struct flexcop_i2c_adapter*, flexcop_access_op_t, - u8 chipaddr, u8 addr, u8 *buf, u16 len); - -/* from flexcop-sram.c */ -int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, - flexcop_sram_dest_target_t target); -void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s); -void flexcop_sram_ctrl(struct flexcop_device *fc, - int usb_wan, int sramdma, int maximumfill); - -/* global prototypes for the flexcop-chip */ -/* from flexcop-fe-tuner.c */ -int flexcop_frontend_init(struct flexcop_device *fc); -void flexcop_frontend_exit(struct flexcop_device *fc); - -/* from flexcop-i2c.c */ -int flexcop_i2c_init(struct flexcop_device *fc); -void flexcop_i2c_exit(struct flexcop_device *fc); - -/* from flexcop-sram.c */ -int flexcop_sram_init(struct flexcop_device *fc); - -/* from flexcop-misc.c */ -void flexcop_determine_revision(struct flexcop_device *fc); -void flexcop_device_name(struct flexcop_device *fc, - const char *prefix, const char *suffix); -void flexcop_dump_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, int num); - -/* from flexcop-hw-filter.c */ -int flexcop_pid_feed_control(struct flexcop_device *fc, - struct dvb_demux_feed *dvbdmxfeed, int onoff); -void flexcop_hw_filter_init(struct flexcop_device *fc); - -void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff); - -void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]); -void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff); - -#endif diff --git a/drivers/media/dvb/b2c2/flexcop-dma.c b/drivers/media/dvb/b2c2/flexcop-dma.c deleted file mode 100644 index 2881e0d956ad..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-dma.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-dma.c - configuring and controlling the DMA of the FlexCop - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -int flexcop_dma_allocate(struct pci_dev *pdev, - struct flexcop_dma *dma, u32 size) -{ - u8 *tcpu; - dma_addr_t tdma = 0; - - if (size % 2) { - err("dma buffersize has to be even."); - return -EINVAL; - } - - if ((tcpu = pci_alloc_consistent(pdev, size, &tdma)) != NULL) { - dma->pdev = pdev; - dma->cpu_addr0 = tcpu; - dma->dma_addr0 = tdma; - dma->cpu_addr1 = tcpu + size/2; - dma->dma_addr1 = tdma + size/2; - dma->size = size/2; - return 0; - } - return -ENOMEM; -} -EXPORT_SYMBOL(flexcop_dma_allocate); - -void flexcop_dma_free(struct flexcop_dma *dma) -{ - pci_free_consistent(dma->pdev, dma->size*2, - dma->cpu_addr0, dma->dma_addr0); - memset(dma,0,sizeof(struct flexcop_dma)); -} -EXPORT_SYMBOL(flexcop_dma_free); - -int flexcop_dma_config(struct flexcop_device *fc, - struct flexcop_dma *dma, - flexcop_dma_index_t dma_idx) -{ - flexcop_ibi_value v0x0,v0x4,v0xc; - v0x0.raw = v0x4.raw = v0xc.raw = 0; - - v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2; - v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2; - v0x4.dma_0x4_write.dma_addr_size = dma->size / 4; - - if ((dma_idx & FC_DMA_1) == dma_idx) { - fc->write_ibi_reg(fc,dma1_000,v0x0); - fc->write_ibi_reg(fc,dma1_004,v0x4); - fc->write_ibi_reg(fc,dma1_00c,v0xc); - } else if ((dma_idx & FC_DMA_2) == dma_idx) { - fc->write_ibi_reg(fc,dma2_010,v0x0); - fc->write_ibi_reg(fc,dma2_014,v0x4); - fc->write_ibi_reg(fc,dma2_01c,v0xc); - } else { - err("either DMA1 or DMA2 can be configured within one " - "flexcop_dma_config call."); - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL(flexcop_dma_config); - -/* start the DMA transfers, but not the DMA IRQs */ -int flexcop_dma_xfer_control(struct flexcop_device *fc, - flexcop_dma_index_t dma_idx, - flexcop_dma_addr_index_t index, - int onoff) -{ - flexcop_ibi_value v0x0,v0xc; - flexcop_ibi_register r0x0,r0xc; - - if ((dma_idx & FC_DMA_1) == dma_idx) { - r0x0 = dma1_000; - r0xc = dma1_00c; - } else if ((dma_idx & FC_DMA_2) == dma_idx) { - r0x0 = dma2_010; - r0xc = dma2_01c; - } else { - err("either transfer DMA1 or DMA2 can be started within one " - "flexcop_dma_xfer_control call."); - return -EINVAL; - } - - v0x0 = fc->read_ibi_reg(fc,r0x0); - v0xc = fc->read_ibi_reg(fc,r0xc); - - deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); - deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); - - if (index & FC_DMA_SUBADDR_0) - v0x0.dma_0x0.dma_0start = onoff; - - if (index & FC_DMA_SUBADDR_1) - v0xc.dma_0xc.dma_1start = onoff; - - fc->write_ibi_reg(fc,r0x0,v0x0); - fc->write_ibi_reg(fc,r0xc,v0xc); - - deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); - deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); - return 0; -} -EXPORT_SYMBOL(flexcop_dma_xfer_control); - -static int flexcop_dma_remap(struct flexcop_device *fc, - flexcop_dma_index_t dma_idx, - int onoff) -{ - flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c; - flexcop_ibi_value v = fc->read_ibi_reg(fc,r); - deb_info("%s\n",__func__); - v.dma_0xc.remap_enable = onoff; - fc->write_ibi_reg(fc,r,v); - return 0; -} - -int flexcop_dma_control_size_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, - int onoff) -{ - flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); - - if (no & FC_DMA_1) - v.ctrl_208.DMA1_IRQ_Enable_sig = onoff; - - if (no & FC_DMA_2) - v.ctrl_208.DMA2_IRQ_Enable_sig = onoff; - - fc->write_ibi_reg(fc,ctrl_208,v); - return 0; -} -EXPORT_SYMBOL(flexcop_dma_control_size_irq); - -int flexcop_dma_control_timer_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, - int onoff) -{ - flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); - - if (no & FC_DMA_1) - v.ctrl_208.DMA1_Timer_Enable_sig = onoff; - - if (no & FC_DMA_2) - v.ctrl_208.DMA2_Timer_Enable_sig = onoff; - - fc->write_ibi_reg(fc,ctrl_208,v); - return 0; -} -EXPORT_SYMBOL(flexcop_dma_control_timer_irq); - -/* 1 cycles = 1.97 msec */ -int flexcop_dma_config_timer(struct flexcop_device *fc, - flexcop_dma_index_t dma_idx, u8 cycles) -{ - flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014; - flexcop_ibi_value v = fc->read_ibi_reg(fc,r); - - flexcop_dma_remap(fc,dma_idx,0); - - deb_info("%s\n",__func__); - v.dma_0x4_write.dmatimer = cycles; - fc->write_ibi_reg(fc,r,v); - return 0; -} -EXPORT_SYMBOL(flexcop_dma_config_timer); - diff --git a/drivers/media/dvb/b2c2/flexcop-eeprom.c b/drivers/media/dvb/b2c2/flexcop-eeprom.c deleted file mode 100644 index a25373a9bd84..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-eeprom.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-eeprom.c - eeprom access methods (currently only MAC address reading) - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -#if 0 -/*EEPROM (Skystar2 has one "24LC08B" chip on board) */ -static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len) -{ - return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len); -} - -static int eeprom_lrc_write(struct adapter *adapter, u32 addr, - u32 len, u8 *wbuf, u8 *rbuf, int retries) -{ -int i; - -for (i = 0; i < retries; i++) { - if (eeprom_write(adapter, addr, wbuf, len) == len) { - if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1) - return 1; - } - } - return 0; -} - -/* These functions could be used to unlock SkyStar2 cards. */ - -static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len) -{ - u8 rbuf[20]; - u8 wbuf[20]; - - if (len != 16) - return 0; - - memcpy(wbuf, key, len); - wbuf[16] = 0; - wbuf[17] = 0; - wbuf[18] = 0; - wbuf[19] = calc_lrc(wbuf, 19); - return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4); -} - -static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len) -{ - u8 buf[20]; - - if (len != 16) - return 0; - - if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0) - return 0; - - memcpy(key, buf, len); - return 1; -} - -static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac) -{ - u8 tmp[8]; - - if (type != 0) { - tmp[0] = mac[0]; - tmp[1] = mac[1]; - tmp[2] = mac[2]; - tmp[3] = mac[5]; - tmp[4] = mac[6]; - tmp[5] = mac[7]; - } else { - tmp[0] = mac[0]; - tmp[1] = mac[1]; - tmp[2] = mac[2]; - tmp[3] = mac[3]; - tmp[4] = mac[4]; - tmp[5] = mac[5]; - } - - tmp[6] = 0; - tmp[7] = calc_lrc(tmp, 7); - - if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8) - return 1; - return 0; -} - -static int flexcop_eeprom_read(struct flexcop_device *fc, - u16 addr, u8 *buf, u16 len) -{ - return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len); -} - -#endif - -static u8 calc_lrc(u8 *buf, int len) -{ - int i; - u8 sum = 0; - for (i = 0; i < len; i++) - sum = sum ^ buf[i]; - return sum; -} - -static int flexcop_eeprom_request(struct flexcop_device *fc, - flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries) -{ - int i,ret = 0; - u8 chipaddr = 0x50 | ((addr >> 8) & 3); - for (i = 0; i < retries; i++) { - ret = fc->i2c_request(&fc->fc_i2c_adap[1], op, chipaddr, - addr & 0xff, buf, len); - if (ret == 0) - break; - } - return ret; -} - -static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, - u8 *buf, u16 len, int retries) -{ - int ret = flexcop_eeprom_request(fc, FC_READ, addr, buf, len, retries); - if (ret == 0) - if (calc_lrc(buf, len - 1) != buf[len - 1]) - ret = -EINVAL; - return ret; -} - -/* JJ's comment about extended == 1: it is not presently used anywhere but was - * added to the low-level functions for possible support of EUI64 */ -int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended) -{ - u8 buf[8]; - int ret = 0; - - if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) { - if (extended != 0) { - err("TODO: extended (EUI64) MAC addresses aren't " - "completely supported yet"); - ret = -EINVAL; - } else - memcpy(fc->dvb_adapter.proposed_mac,buf,6); - } - return ret; -} -EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr); diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c deleted file mode 100644 index 850a6c606750..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ /dev/null @@ -1,678 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling - * see flexcop.c for copyright information - */ -#include -#include "flexcop.h" -#include "mt312.h" -#include "stv0299.h" -#include "s5h1420.h" -#include "itd1000.h" -#include "cx24113.h" -#include "cx24123.h" -#include "isl6421.h" -#include "mt352.h" -#include "bcm3510.h" -#include "nxt200x.h" -#include "dvb-pll.h" -#include "lgdt330x.h" -#include "tuner-simple.h" -#include "stv0297.h" - - -/* Can we use the specified front-end? Remember that if we are compiled - * into the kernel we can't call code that's in modules. */ -#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ - (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) - -/* lnb control */ -#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) -static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct flexcop_device *fc = fe->dvb->priv; - flexcop_ibi_value v; - deb_tuner("polarity/voltage = %u\n", voltage); - - v = fc->read_ibi_reg(fc, misc_204); - switch (voltage) { - case SEC_VOLTAGE_OFF: - v.misc_204.ACPI1_sig = 1; - break; - case SEC_VOLTAGE_13: - v.misc_204.ACPI1_sig = 0; - v.misc_204.LNB_L_H_sig = 0; - break; - case SEC_VOLTAGE_18: - v.misc_204.ACPI1_sig = 0; - v.misc_204.LNB_L_H_sig = 1; - break; - default: - err("unknown SEC_VOLTAGE value"); - return -EINVAL; - } - return fc->write_ibi_reg(fc, misc_204, v); -} -#endif - -#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312) -static int flexcop_sleep(struct dvb_frontend* fe) -{ - struct flexcop_device *fc = fe->dvb->priv; - if (fc->fe_sleep) - return fc->fe_sleep(fe); - return 0; -} -#endif - -/* SkyStar2 DVB-S rev 2.3 */ -#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL) -static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) -{ -/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ - struct flexcop_device *fc = fe->dvb->priv; - flexcop_ibi_value v; - u16 ax; - v.raw = 0; - deb_tuner("tone = %u\n",tone); - - switch (tone) { - case SEC_TONE_ON: - ax = 0x01ff; - break; - case SEC_TONE_OFF: - ax = 0; - break; - default: - err("unknown SEC_TONE value"); - return -EINVAL; - } - - v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */ - v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax; - v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax; - return fc->write_ibi_reg(fc,lnb_switch_freq_200,v); -} - -static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data) -{ - flexcop_set_tone(fe, SEC_TONE_ON); - udelay(data ? 500 : 1000); - flexcop_set_tone(fe, SEC_TONE_OFF); - udelay(data ? 1000 : 500); -} - -static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data) -{ - int i, par = 1, d; - for (i = 7; i >= 0; i--) { - d = (data >> i) & 1; - par ^= d; - flexcop_diseqc_send_bit(fe, d); - } - flexcop_diseqc_send_bit(fe, par); -} - -static int flexcop_send_diseqc_msg(struct dvb_frontend *fe, - int len, u8 *msg, unsigned long burst) -{ - int i; - - flexcop_set_tone(fe, SEC_TONE_OFF); - mdelay(16); - - for (i = 0; i < len; i++) - flexcop_diseqc_send_byte(fe,msg[i]); - mdelay(16); - - if (burst != -1) { - if (burst) - flexcop_diseqc_send_byte(fe, 0xff); - else { - flexcop_set_tone(fe, SEC_TONE_ON); - mdelay(12); - udelay(500); - flexcop_set_tone(fe, SEC_TONE_OFF); - } - msleep(20); - } - return 0; -} - -static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe, - struct dvb_diseqc_master_cmd *cmd) -{ - return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0); -} - -static int flexcop_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t minicmd) -{ - return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd); -} - -static struct mt312_config skystar23_samsung_tbdu18132_config = { - .demod_address = 0x0e, -}; - -static int skystar2_rev23_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - struct dvb_frontend_ops *ops; - - fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c); - if (!fc->fe) - return 0; - - if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, - DVB_PLL_SAMSUNG_TBDU18132)) - return 0; - - ops = &fc->fe->ops; - ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; - ops->diseqc_send_burst = flexcop_diseqc_send_burst; - ops->set_tone = flexcop_set_tone; - ops->set_voltage = flexcop_set_voltage; - fc->fe_sleep = ops->sleep; - ops->sleep = flexcop_sleep; - return 1; -} -#else -#define skystar2_rev23_attach NULL -#endif - -/* SkyStar2 DVB-S rev 2.6 */ -#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL) -static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, - u32 srate, u32 ratio) -{ - u8 aclk = 0; - u8 bclk = 0; - - if (srate < 1500000) { - aclk = 0xb7; bclk = 0x47; - } else if (srate < 3000000) { - aclk = 0xb7; bclk = 0x4b; - } else if (srate < 7000000) { - aclk = 0xb7; bclk = 0x4f; - } else if (srate < 14000000) { - aclk = 0xb7; bclk = 0x53; - } else if (srate < 30000000) { - aclk = 0xb6; bclk = 0x53; - } else if (srate < 45000000) { - aclk = 0xb4; bclk = 0x51; - } - - stv0299_writereg(fe, 0x13, aclk); - stv0299_writereg(fe, 0x14, bclk); - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, ratio & 0xf0); - return 0; -} - -static u8 samsung_tbmu24112_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x7D, - 0x05, 0x35, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0xC3, - 0x0C, 0x00, - 0x0D, 0x81, - 0x0E, 0x23, - 0x0F, 0x12, - 0x10, 0x7E, - 0x11, 0x84, - 0x12, 0xB9, - 0x13, 0x88, - 0x14, 0x89, - 0x15, 0xC9, - 0x16, 0x00, - 0x17, 0x5C, - 0x18, 0x00, - 0x19, 0x00, - 0x1A, 0x00, - 0x1C, 0x00, - 0x1D, 0x00, - 0x1E, 0x00, - 0x1F, 0x3A, - 0x20, 0x2E, - 0x21, 0x80, - 0x22, 0xFF, - 0x23, 0xC1, - 0x28, 0x00, - 0x29, 0x1E, - 0x2A, 0x14, - 0x2B, 0x0F, - 0x2C, 0x09, - 0x2D, 0x05, - 0x31, 0x1F, - 0x32, 0x19, - 0x33, 0xFE, - 0x34, 0x93, - 0xff, 0xff, -}; - -static struct stv0299_config samsung_tbmu24112_config = { - .demod_address = 0x68, - .inittab = samsung_tbmu24112_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_LK, - .volt13_op0_op1 = STV0299_VOLT13_OP1, - .min_delay_ms = 100, - .set_symbol_rate = samsung_tbmu24112_set_symbol_rate, -}; - -static int skystar2_rev26_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c); - if (!fc->fe) - return 0; - - if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, - DVB_PLL_SAMSUNG_TBMU24112)) - return 0; - - fc->fe->ops.set_voltage = flexcop_set_voltage; - fc->fe_sleep = fc->fe->ops.sleep; - fc->fe->ops.sleep = flexcop_sleep; - return 1; - -} -#else -#define skystar2_rev26_attach NULL -#endif - -/* SkyStar2 DVB-S rev 2.7 */ -#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000) -static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { - .demod_address = 0x53, - .invert = 1, - .repeated_start_workaround = 1, - .serial_mpeg = 1, -}; - -static struct itd1000_config skystar2_rev2_7_itd1000_config = { - .i2c_address = 0x61, -}; - -static int skystar2_rev27_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - flexcop_ibi_value r108; - struct i2c_adapter *i2c_tuner; - - /* enable no_base_addr - no repeated start when reading */ - fc->fc_i2c_adap[0].no_base_addr = 1; - fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config, - i2c); - if (!fc->fe) - goto fail; - - i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe); - if (!i2c_tuner) - goto fail; - - fc->fe_sleep = fc->fe->ops.sleep; - fc->fe->ops.sleep = flexcop_sleep; - - /* enable no_base_addr - no repeated start when reading */ - fc->fc_i2c_adap[2].no_base_addr = 1; - if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, - 0x08, 1, 1)) { - err("ISL6421 could NOT be attached"); - goto fail_isl; - } - info("ISL6421 successfully attached"); - - /* the ITD1000 requires a lower i2c clock - is it a problem ? */ - r108.raw = 0x00000506; - fc->write_ibi_reg(fc, tw_sm_c_108, r108); - if (!dvb_attach(itd1000_attach, fc->fe, i2c_tuner, - &skystar2_rev2_7_itd1000_config)) { - err("ITD1000 could NOT be attached"); - /* Should i2c clock be restored? */ - goto fail_isl; - } - info("ITD1000 successfully attached"); - - return 1; - -fail_isl: - fc->fc_i2c_adap[2].no_base_addr = 0; -fail: - /* for the next devices we need it again */ - fc->fc_i2c_adap[0].no_base_addr = 0; - return 0; -} -#else -#define skystar2_rev27_attach NULL -#endif - -/* SkyStar2 rev 2.8 */ -#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113) -static struct cx24123_config skystar2_rev2_8_cx24123_config = { - .demod_address = 0x55, - .dont_use_pll = 1, - .agc_callback = cx24113_agc_callback, -}; - -static const struct cx24113_config skystar2_rev2_8_cx24113_config = { - .i2c_addr = 0x54, - .xtal_khz = 10111, -}; - -static int skystar2_rev28_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - struct i2c_adapter *i2c_tuner; - - fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config, - i2c); - if (!fc->fe) - return 0; - - i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe); - if (!i2c_tuner) - return 0; - - if (!dvb_attach(cx24113_attach, fc->fe, &skystar2_rev2_8_cx24113_config, - i2c_tuner)) { - err("CX24113 could NOT be attached"); - return 0; - } - info("CX24113 successfully attached"); - - fc->fc_i2c_adap[2].no_base_addr = 1; - if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, - 0x08, 0, 0)) { - err("ISL6421 could NOT be attached"); - fc->fc_i2c_adap[2].no_base_addr = 0; - return 0; - } - info("ISL6421 successfully attached"); - /* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an - * IR-receiver (PIC16F818) - but the card has no input for that ??? */ - return 1; -} -#else -#define skystar2_rev28_attach NULL -#endif - -/* AirStar DVB-T */ -#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL) -static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) -{ - static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; - static u8 mt352_reset[] = { 0x50, 0x80 }; - static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; - static u8 mt352_agc_cfg[] = { 0x67, 0x28, 0xa1 }; - static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; - - mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(fe, mt352_reset, sizeof(mt352_reset)); - mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - return 0; -} - -static struct mt352_config samsung_tdtc9251dh0_config = { - .demod_address = 0x0f, - .demod_init = samsung_tdtc9251dh0_demod_init, -}; - -static int airstar_dvbt_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c); - if (!fc->fe) - return 0; - - return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, - DVB_PLL_SAMSUNG_TDTC9251DH0); -} -#else -#define airstar_dvbt_attach NULL -#endif - -/* AirStar ATSC 1st generation */ -#if FE_SUPPORTED(BCM3510) -static int flexcop_fe_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char* name) -{ - struct flexcop_device *fc = fe->dvb->priv; - return request_firmware(fw, name, fc->dev); -} - -static struct bcm3510_config air2pc_atsc_first_gen_config = { - .demod_address = 0x0f, - .request_firmware = flexcop_fe_request_firmware, -}; - -static int airstar_atsc1_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c); - return fc->fe != NULL; -} -#else -#define airstar_atsc1_attach NULL -#endif - -/* AirStar ATSC 2nd generation */ -#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL) -static struct nxt200x_config samsung_tbmv_config = { - .demod_address = 0x0a, -}; - -static int airstar_atsc2_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c); - if (!fc->fe) - return 0; - - return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, - DVB_PLL_SAMSUNG_TBMV); -} -#else -#define airstar_atsc2_attach NULL -#endif - -/* AirStar ATSC 3rd generation */ -#if FE_SUPPORTED(LGDT330X) -static struct lgdt330x_config air2pc_atsc_hd5000_config = { - .demod_address = 0x59, - .demod_chip = LGDT3303, - .serial_mpeg = 0x04, - .clock_polarity_flip = 1, -}; - -static int airstar_atsc3_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c); - if (!fc->fe) - return 0; - - return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61, - TUNER_LG_TDVS_H06XF); -} -#else -#define airstar_atsc3_attach NULL -#endif - -/* CableStar2 DVB-C */ -#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL) -static u8 alps_tdee4_stv0297_inittab[] = { - 0x80, 0x01, - 0x80, 0x00, - 0x81, 0x01, - 0x81, 0x00, - 0x00, 0x48, - 0x01, 0x58, - 0x03, 0x00, - 0x04, 0x00, - 0x07, 0x00, - 0x08, 0x00, - 0x30, 0xff, - 0x31, 0x9d, - 0x32, 0xff, - 0x33, 0x00, - 0x34, 0x29, - 0x35, 0x55, - 0x36, 0x80, - 0x37, 0x6e, - 0x38, 0x9c, - 0x40, 0x1a, - 0x41, 0xfe, - 0x42, 0x33, - 0x43, 0x00, - 0x44, 0xff, - 0x45, 0x00, - 0x46, 0x00, - 0x49, 0x04, - 0x4a, 0x51, - 0x4b, 0xf8, - 0x52, 0x30, - 0x53, 0x06, - 0x59, 0x06, - 0x5a, 0x5e, - 0x5b, 0x04, - 0x61, 0x49, - 0x62, 0x0a, - 0x70, 0xff, - 0x71, 0x04, - 0x72, 0x00, - 0x73, 0x00, - 0x74, 0x0c, - 0x80, 0x20, - 0x81, 0x00, - 0x82, 0x30, - 0x83, 0x00, - 0x84, 0x04, - 0x85, 0x22, - 0x86, 0x08, - 0x87, 0x1b, - 0x88, 0x00, - 0x89, 0x00, - 0x90, 0x00, - 0x91, 0x04, - 0xa0, 0x86, - 0xa1, 0x00, - 0xa2, 0x00, - 0xb0, 0x91, - 0xb1, 0x0b, - 0xc0, 0x5b, - 0xc1, 0x10, - 0xc2, 0x12, - 0xd0, 0x02, - 0xd1, 0x00, - 0xd2, 0x00, - 0xd3, 0x00, - 0xd4, 0x02, - 0xd5, 0x00, - 0xde, 0x00, - 0xdf, 0x01, - 0xff, 0xff, -}; - -static struct stv0297_config alps_tdee4_stv0297_config = { - .demod_address = 0x1c, - .inittab = alps_tdee4_stv0297_inittab, -}; - -static int cablestar2_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fc_i2c_adap[0].no_base_addr = 1; - fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); - if (!fc->fe) - goto fail; - - /* This tuner doesn't use the stv0297's I2C gate, but instead the - * tuner is connected to a different flexcop I2C adapter. */ - if (fc->fe->ops.i2c_gate_ctrl) - fc->fe->ops.i2c_gate_ctrl(fc->fe, 0); - fc->fe->ops.i2c_gate_ctrl = NULL; - - if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, - &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) - goto fail; - - return 1; - -fail: - /* Reset for next frontend to try */ - fc->fc_i2c_adap[0].no_base_addr = 0; - return 0; -} -#else -#define cablestar2_attach NULL -#endif - -static struct { - flexcop_device_type_t type; - int (*attach)(struct flexcop_device *, struct i2c_adapter *); -} flexcop_frontends[] = { - { FC_SKY_REV27, skystar2_rev27_attach }, - { FC_SKY_REV28, skystar2_rev28_attach }, - { FC_SKY_REV26, skystar2_rev26_attach }, - { FC_AIR_DVBT, airstar_dvbt_attach }, - { FC_AIR_ATSC2, airstar_atsc2_attach }, - { FC_AIR_ATSC3, airstar_atsc3_attach }, - { FC_AIR_ATSC1, airstar_atsc1_attach }, - { FC_CABLE, cablestar2_attach }, - { FC_SKY_REV23, skystar2_rev23_attach }, -}; - -/* try to figure out the frontend */ -int flexcop_frontend_init(struct flexcop_device *fc) -{ - int i; - for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) { - if (!flexcop_frontends[i].attach) - continue; - /* type needs to be set before, because of some workarounds - * done based on the probed card type */ - fc->dev_type = flexcop_frontends[i].type; - if (flexcop_frontends[i].attach(fc, &fc->fc_i2c_adap[0].i2c_adap)) - goto fe_found; - /* Clean up partially attached frontend */ - if (fc->fe) { - dvb_frontend_detach(fc->fe); - fc->fe = NULL; - } - } - fc->dev_type = FC_UNK; - err("no frontend driver found for this B2C2/FlexCop adapter"); - return -ENODEV; - -fe_found: - info("found '%s' .", fc->fe->ops.info.name); - if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) { - err("frontend registration failed!"); - dvb_frontend_detach(fc->fe); - fc->fe = NULL; - return -EINVAL; - } - fc->init_state |= FC_STATE_FE_INIT; - return 0; -} - -void flexcop_frontend_exit(struct flexcop_device *fc) -{ - if (fc->init_state & FC_STATE_FE_INIT) { - dvb_unregister_frontend(fc->fe); - dvb_frontend_detach(fc->fe); - } - fc->init_state &= ~FC_STATE_FE_INIT; -} diff --git a/drivers/media/dvb/b2c2/flexcop-hw-filter.c b/drivers/media/dvb/b2c2/flexcop-hw-filter.c deleted file mode 100644 index 77e45475f4c7..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-hw-filter.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-hw-filter.c - pid and mac address filtering and control functions - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff); - deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off"); -} - -void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff); -} - -static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff); -} - -void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]) -{ - flexcop_ibi_value v418, v41c; - v41c = fc->read_ibi_reg(fc, mac_address_41c); - - v418.mac_address_418.MAC1 = mac[0]; - v418.mac_address_418.MAC2 = mac[1]; - v418.mac_address_418.MAC3 = mac[2]; - v418.mac_address_418.MAC6 = mac[3]; - v41c.mac_address_41c.MAC7 = mac[4]; - v41c.mac_address_41c.MAC8 = mac[5]; - - fc->write_ibi_reg(fc, mac_address_418, v418); - fc->write_ibi_reg(fc, mac_address_41c, v41c); -} - -void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff); -} - -static void flexcop_pid_group_filter(struct flexcop_device *fc, - u16 pid, u16 mask) -{ - /* index_reg_310.extra_index_reg need to 0 or 7 to work */ - flexcop_ibi_value v30c; - v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid; - v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask; - fc->write_ibi_reg(fc, pid_filter_30c, v30c); -} - -static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff); -} - -/* this fancy define reduces the code size of the quite similar PID controlling of - * the first 6 PIDs - */ - -#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \ - flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \ -v208 = fc->read_ibi_reg(fc, ctrl_208); \ -vpid.vregname.field = onoff ? pid : 0x1fff; \ -vpid.vregname.trans_field = transval; \ -v208.ctrl_208.enablefield = onoff; \ -fc->write_ibi_reg(fc, vregname, vpid); \ -fc->write_ibi_reg(fc, ctrl_208, v208); - -static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig, - Stream1_trans, 0); -} - -static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig, - Stream2_trans, 0); -} - -static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0); -} - -static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0); -} - -static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0); -} - -static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0); -} - -static void flexcop_pid_control(struct flexcop_device *fc, - int index, u16 pid, int onoff) -{ - if (pid == 0x2000) - return; - - deb_ts("setting pid: %5d %04x at index %d '%s'\n", - pid, pid, index, onoff ? "on" : "off"); - - /* We could use bit magic here to reduce source code size. - * I decided against it, but to use the real register names */ - switch (index) { - case 0: - flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff); - break; - case 1: - flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff); - break; - case 2: - flexcop_pid_PCR_PID_ctrl(fc, pid, onoff); - break; - case 3: - flexcop_pid_PMT_PID_ctrl(fc, pid, onoff); - break; - case 4: - flexcop_pid_EMM_PID_ctrl(fc, pid, onoff); - break; - case 5: - flexcop_pid_ECM_PID_ctrl(fc, pid, onoff); - break; - default: - if (fc->has_32_hw_pid_filter && index < 38) { - flexcop_ibi_value vpid, vid; - - /* set the index */ - vid = fc->read_ibi_reg(fc, index_reg_310); - vid.index_reg_310.index_reg = index - 6; - fc->write_ibi_reg(fc, index_reg_310, vid); - - vpid = fc->read_ibi_reg(fc, pid_n_reg_314); - vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff; - vpid.pid_n_reg_314.PID_enable_bit = onoff; - fc->write_ibi_reg(fc, pid_n_reg_314, vpid); - } - break; - } -} - -static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff) -{ - if (fc->fullts_streaming_state != onoff) { - deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling"); - flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff)); - flexcop_pid_group_filter_ctrl(fc, onoff); - fc->fullts_streaming_state = onoff; - } - return 0; -} - -int flexcop_pid_feed_control(struct flexcop_device *fc, - struct dvb_demux_feed *dvbdmxfeed, int onoff) -{ - int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32; - - fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */ - if (dvbdmxfeed->index >= max_pid_filter) - fc->extra_feedcount += onoff ? 1 : -1; - - /* toggle complete-TS-streaming when: - * - pid_filtering is not enabled and it is the first or last feed requested - * - pid_filtering is enabled, - * - but the number of requested feeds is exceeded - * - or the requested pid is 0x2000 */ - - if (!fc->pid_filtering && fc->feedcount == onoff) - flexcop_toggle_fullts_streaming(fc, onoff); - - if (fc->pid_filtering) { - flexcop_pid_control \ - (fc, dvbdmxfeed->index, dvbdmxfeed->pid, onoff); - - if (fc->extra_feedcount > 0) - flexcop_toggle_fullts_streaming(fc, 1); - else if (dvbdmxfeed->pid == 0x2000) - flexcop_toggle_fullts_streaming(fc, onoff); - else - flexcop_toggle_fullts_streaming(fc, 0); - } - - /* if it was the first or last feed request change the stream-status */ - if (fc->feedcount == onoff) { - flexcop_rcv_data_ctrl(fc, onoff); - if (fc->stream_control) /* device specific stream control */ - fc->stream_control(fc, onoff); - - /* feeding stopped -> reset the flexcop filter*/ - if (onoff == 0) { - flexcop_reset_block_300(fc); - flexcop_hw_filter_init(fc); - } - } - return 0; -} -EXPORT_SYMBOL(flexcop_pid_feed_control); - -void flexcop_hw_filter_init(struct flexcop_device *fc) -{ - int i; - flexcop_ibi_value v; - for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++) - flexcop_pid_control(fc, i, 0x1fff, 0); - - flexcop_pid_group_filter(fc, 0, 0x1fe0); - flexcop_pid_group_filter_ctrl(fc, 0); - - v = fc->read_ibi_reg(fc, pid_filter_308); - v.pid_filter_308.EMM_filter_4 = 1; - v.pid_filter_308.EMM_filter_6 = 0; - fc->write_ibi_reg(fc, pid_filter_308, v); - - flexcop_null_filter_ctrl(fc, 1); -} diff --git a/drivers/media/dvb/b2c2/flexcop-i2c.c b/drivers/media/dvb/b2c2/flexcop-i2c.c deleted file mode 100644 index 965d5eb33752..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-i2c.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -#define FC_MAX_I2C_RETRIES 100000 - -static int flexcop_i2c_operation(struct flexcop_device *fc, - flexcop_ibi_value *r100) -{ - int i; - flexcop_ibi_value r; - - r100->tw_sm_c_100.working_start = 1; - deb_i2c("r100 before: %08x\n",r100->raw); - - fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero); - fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */ - - for (i = 0; i < FC_MAX_I2C_RETRIES; i++) { - r = fc->read_ibi_reg(fc, tw_sm_c_100); - - if (!r.tw_sm_c_100.no_base_addr_ack_error) { - if (r.tw_sm_c_100.st_done) { - *r100 = r; - deb_i2c("i2c success\n"); - return 0; - } - } else { - deb_i2c("suffering from an i2c ack_error\n"); - return -EREMOTEIO; - } - } - deb_i2c("tried %d times i2c operation, " - "never finished or too many ack errors.\n", i); - return -EREMOTEIO; -} - -static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c, - flexcop_ibi_value r100, u8 *buf) -{ - flexcop_ibi_value r104; - int len = r100.tw_sm_c_100.total_bytes, - /* remember total_bytes is buflen-1 */ - ret; - - /* work-around to have CableStar2 and SkyStar2 rev 2.7 work - * correctly: - * - * the ITD1000 is behind an i2c-gate which closes automatically - * after an i2c-transaction the STV0297 needs 2 consecutive reads - * one with no_base_addr = 0 and one with 1 - * - * those two work-arounds are conflictin: we check for the card - * type, it is set when probing the ITD1000 */ - if (i2c->fc->dev_type == FC_SKY_REV27) - r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; - - ret = flexcop_i2c_operation(i2c->fc, &r100); - if (ret != 0) { - deb_i2c("Retrying operation\n"); - r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; - ret = flexcop_i2c_operation(i2c->fc, &r100); - } - if (ret != 0) { - deb_i2c("read failed. %d\n", ret); - return ret; - } - - buf[0] = r100.tw_sm_c_100.data1_reg; - - if (len > 0) { - r104 = i2c->fc->read_ibi_reg(i2c->fc, tw_sm_c_104); - deb_i2c("read: r100: %08x, r104: %08x\n", r100.raw, r104.raw); - - /* there is at least one more byte, otherwise we wouldn't be here */ - buf[1] = r104.tw_sm_c_104.data2_reg; - if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg; - if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg; - } - return 0; -} - -static int flexcop_i2c_write4(struct flexcop_device *fc, - flexcop_ibi_value r100, u8 *buf) -{ - flexcop_ibi_value r104; - int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */ - r104.raw = 0; - - /* there is at least one byte, otherwise we wouldn't be here */ - r100.tw_sm_c_100.data1_reg = buf[0]; - r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0; - r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0; - r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0; - - deb_i2c("write: r100: %08x, r104: %08x\n", r100.raw, r104.raw); - - /* write the additional i2c data before doing the actual i2c operation */ - fc->write_ibi_reg(fc, tw_sm_c_104, r104); - return flexcop_i2c_operation(fc, &r100); -} - -int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c, - flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) -{ - int ret; - -#ifdef DUMP_I2C_MESSAGES - int i; -#endif - - u16 bytes_to_transfer; - flexcop_ibi_value r100; - - deb_i2c("op = %d\n",op); - r100.raw = 0; - r100.tw_sm_c_100.chipaddr = chipaddr; - r100.tw_sm_c_100.twoWS_rw = op; - r100.tw_sm_c_100.twoWS_port_reg = i2c->port; - -#ifdef DUMP_I2C_MESSAGES - printk(KERN_DEBUG "%d ", i2c->port); - if (op == FC_READ) - printk("rd("); - else - printk("wr("); - printk("%02x): %02x ", chipaddr, addr); -#endif - - /* in that case addr is the only value -> - * we write it twice as baseaddr and val0 - * BBTI is doing it like that for ISL6421 at least */ - if (i2c->no_base_addr && len == 0 && op == FC_WRITE) { - buf = &addr; - len = 1; - } - - while (len != 0) { - bytes_to_transfer = len > 4 ? 4 : len; - - r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1; - r100.tw_sm_c_100.baseaddr = addr; - - if (op == FC_READ) - ret = flexcop_i2c_read4(i2c, r100, buf); - else - ret = flexcop_i2c_write4(i2c->fc, r100, buf); - -#ifdef DUMP_I2C_MESSAGES - for (i = 0; i < bytes_to_transfer; i++) - printk("%02x ", buf[i]); -#endif - - if (ret < 0) - return ret; - - buf += bytes_to_transfer; - addr += bytes_to_transfer; - len -= bytes_to_transfer; - } - -#ifdef DUMP_I2C_MESSAGES - printk("\n"); -#endif - - return 0; -} -/* exported for PCI i2c */ -EXPORT_SYMBOL(flexcop_i2c_request); - -/* master xfer callback for demodulator */ -static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) -{ - struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); - int i, ret = 0; - - /* Some drivers use 1 byte or 0 byte reads as probes, which this - * driver doesn't support. These probes will always fail, so this - * hack makes them always succeed. If one knew how, it would of - * course be better to actually do the read. */ - if (num == 1 && msgs[0].flags == I2C_M_RD && msgs[0].len <= 1) - return 1; - - if (mutex_lock_interruptible(&i2c->fc->i2c_mutex)) - return -ERESTARTSYS; - - for (i = 0; i < num; i++) { - /* reading */ - if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) { - ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr, - msgs[i].buf[0], msgs[i+1].buf, - msgs[i+1].len); - i++; /* skip the following message */ - } else /* writing */ - ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr, - msgs[i].buf[0], &msgs[i].buf[1], - msgs[i].len - 1); - if (ret < 0) { - deb_i2c("i2c master_xfer failed"); - break; - } - } - - mutex_unlock(&i2c->fc->i2c_mutex); - - if (ret == 0) - ret = num; - return ret; -} - -static u32 flexcop_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm flexcop_algo = { - .master_xfer = flexcop_master_xfer, - .functionality = flexcop_i2c_func, -}; - -int flexcop_i2c_init(struct flexcop_device *fc) -{ - int ret; - mutex_init(&fc->i2c_mutex); - - fc->fc_i2c_adap[0].fc = fc; - fc->fc_i2c_adap[1].fc = fc; - fc->fc_i2c_adap[2].fc = fc; - fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD; - fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM; - fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER; - - strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod", - sizeof(fc->fc_i2c_adap[0].i2c_adap.name)); - strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom", - sizeof(fc->fc_i2c_adap[1].i2c_adap.name)); - strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner", - sizeof(fc->fc_i2c_adap[2].i2c_adap.name)); - - i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]); - i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]); - i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]); - - fc->fc_i2c_adap[0].i2c_adap.algo = - fc->fc_i2c_adap[1].i2c_adap.algo = - fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo; - fc->fc_i2c_adap[0].i2c_adap.algo_data = - fc->fc_i2c_adap[1].i2c_adap.algo_data = - fc->fc_i2c_adap[2].i2c_adap.algo_data = NULL; - fc->fc_i2c_adap[0].i2c_adap.dev.parent = - fc->fc_i2c_adap[1].i2c_adap.dev.parent = - fc->fc_i2c_adap[2].i2c_adap.dev.parent = fc->dev; - - ret = i2c_add_adapter(&fc->fc_i2c_adap[0].i2c_adap); - if (ret < 0) - return ret; - - ret = i2c_add_adapter(&fc->fc_i2c_adap[1].i2c_adap); - if (ret < 0) - goto adap_1_failed; - - ret = i2c_add_adapter(&fc->fc_i2c_adap[2].i2c_adap); - if (ret < 0) - goto adap_2_failed; - - fc->init_state |= FC_STATE_I2C_INIT; - return 0; - -adap_2_failed: - i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); -adap_1_failed: - i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); - return ret; -} - -void flexcop_i2c_exit(struct flexcop_device *fc) -{ - if (fc->init_state & FC_STATE_I2C_INIT) { - i2c_del_adapter(&fc->fc_i2c_adap[2].i2c_adap); - i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); - i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); - } - fc->init_state &= ~FC_STATE_I2C_INIT; -} diff --git a/drivers/media/dvb/b2c2/flexcop-misc.c b/drivers/media/dvb/b2c2/flexcop-misc.c deleted file mode 100644 index f06f3a9070f5..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-misc.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-misc.c - miscellaneous functions - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -void flexcop_determine_revision(struct flexcop_device *fc) -{ - flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); - - switch (v.misc_204.Rev_N_sig_revision_hi) { - case 0x2: - deb_info("found a FlexCopII.\n"); - fc->rev = FLEXCOP_II; - break; - case 0x3: - deb_info("found a FlexCopIIb.\n"); - fc->rev = FLEXCOP_IIB; - break; - case 0x0: - deb_info("found a FlexCopIII.\n"); - fc->rev = FLEXCOP_III; - break; - default: - err("unknown FlexCop Revision: %x. Please report this to " - "linux-dvb@linuxtv.org.", - v.misc_204.Rev_N_sig_revision_hi); - break; - } - - if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps)) - deb_info("this FlexCop has " - "the additional 32 hardware pid filter.\n"); - else - deb_info("this FlexCop has " - "the 6 basic main hardware pid filter.\n"); - /* bus parts have to decide if hw pid filtering is used or not. */ -} - -static const char *flexcop_revision_names[] = { - "Unknown chip", - "FlexCopII", - "FlexCopIIb", - "FlexCopIII", -}; - -static const char *flexcop_device_names[] = { - [FC_UNK] = "Unknown device", - [FC_CABLE] = "Cable2PC/CableStar 2 DVB-C", - [FC_AIR_DVBT] = "Air2PC/AirStar 2 DVB-T", - [FC_AIR_ATSC1] = "Air2PC/AirStar 2 ATSC 1st generation", - [FC_AIR_ATSC2] = "Air2PC/AirStar 2 ATSC 2nd generation", - [FC_AIR_ATSC3] = "Air2PC/AirStar 2 ATSC 3rd generation (HD5000)", - [FC_SKY_REV23] = "Sky2PC/SkyStar 2 DVB-S rev 2.3 (old version)", - [FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6", - [FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u", - [FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8", -}; - -static const char *flexcop_bus_names[] = { - "USB", - "PCI", -}; - -void flexcop_device_name(struct flexcop_device *fc, - const char *prefix, const char *suffix) -{ - info("%s '%s' at the '%s' bus controlled by a '%s' %s", - prefix, flexcop_device_names[fc->dev_type], - flexcop_bus_names[fc->bus_type], - flexcop_revision_names[fc->rev], suffix); -} - -void flexcop_dump_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, int num) -{ - flexcop_ibi_value v; - int i; - for (i = 0; i < num; i++) { - v = fc->read_ibi_reg(fc, reg+4*i); - deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw); - } - deb_rdump("\n"); -} -EXPORT_SYMBOL(flexcop_dump_reg); diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c deleted file mode 100644 index 44f8fb5f17ff..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-pci.c +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-pci.c - covers the PCI part including DMA transfers - * see flexcop.c for copyright information - */ - -#define FC_LOG_PREFIX "flexcop-pci" -#include "flexcop-common.h" - -static int enable_pid_filtering = 1; -module_param(enable_pid_filtering, int, 0444); -MODULE_PARM_DESC(enable_pid_filtering, - "enable hardware pid filtering: supported values: 0 (fullts), 1"); - -static int irq_chk_intv = 100; -module_param(irq_chk_intv, int, 0644); -MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog."); - -#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG -#define dprintk(level,args...) \ - do { if ((debug & level)) printk(args); } while (0) -#define DEBSTATUS "" -#else -#define dprintk(level,args...) -#define DEBSTATUS " (debugging is not enabled)" -#endif - -#define deb_info(args...) dprintk(0x01, args) -#define deb_reg(args...) dprintk(0x02, args) -#define deb_ts(args...) dprintk(0x04, args) -#define deb_irq(args...) dprintk(0x08, args) -#define deb_chk(args...) dprintk(0x10, args) - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, - "set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))." - DEBSTATUS); - -#define DRIVER_VERSION "0.1" -#define DRIVER_NAME "flexcop-pci" -#define DRIVER_AUTHOR "Patrick Boettcher " - -struct flexcop_pci { - struct pci_dev *pdev; - -#define FC_PCI_INIT 0x01 -#define FC_PCI_DMA_INIT 0x02 - int init_state; - - void __iomem *io_mem; - u32 irq; - /* buffersize (at least for DMA1, need to be % 188 == 0, - * this logic is required */ -#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188) -#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188) - struct flexcop_dma dma[2]; - - int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */ - u32 last_dma1_cur_pos; - /* position of the pointer last time the timer/packet irq occurred */ - int count; - int count_prev; - int stream_problem; - - spinlock_t irq_lock; - unsigned long last_irq; - - struct delayed_work irq_check_work; - struct flexcop_device *fc_dev; -}; - -static int lastwreg, lastwval, lastrreg, lastrval; - -static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc, - flexcop_ibi_register r) -{ - struct flexcop_pci *fc_pci = fc->bus_specific; - flexcop_ibi_value v; - v.raw = readl(fc_pci->io_mem + r); - - if (lastrreg != r || lastrval != v.raw) { - lastrreg = r; lastrval = v.raw; - deb_reg("new rd: %3x: %08x\n", r, v.raw); - } - - return v; -} - -static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, - flexcop_ibi_register r, flexcop_ibi_value v) -{ - struct flexcop_pci *fc_pci = fc->bus_specific; - - if (lastwreg != r || lastwval != v.raw) { - lastwreg = r; lastwval = v.raw; - deb_reg("new wr: %3x: %08x\n", r, v.raw); - } - - writel(v.raw, fc_pci->io_mem + r); - return 0; -} - -static void flexcop_pci_irq_check_work(struct work_struct *work) -{ - struct flexcop_pci *fc_pci = - container_of(work, struct flexcop_pci, irq_check_work.work); - struct flexcop_device *fc = fc_pci->fc_dev; - - if (fc->feedcount) { - - if (fc_pci->count == fc_pci->count_prev) { - deb_chk("no IRQ since the last check\n"); - if (fc_pci->stream_problem++ == 3) { - struct dvb_demux_feed *feed; - deb_info("flexcop-pci: stream problem, resetting pid filter\n"); - - spin_lock_irq(&fc->demux.lock); - list_for_each_entry(feed, &fc->demux.feed_list, - list_head) { - flexcop_pid_feed_control(fc, feed, 0); - } - - list_for_each_entry(feed, &fc->demux.feed_list, - list_head) { - flexcop_pid_feed_control(fc, feed, 1); - } - spin_unlock_irq(&fc->demux.lock); - - fc_pci->stream_problem = 0; - } - } else { - fc_pci->stream_problem = 0; - fc_pci->count_prev = fc_pci->count; - } - } - - schedule_delayed_work(&fc_pci->irq_check_work, - msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv)); -} - -/* When PID filtering is turned on, we use the timer IRQ, because small amounts - * of data need to be passed to the user space instantly as well. When PID - * filtering is turned off, we use the page-change-IRQ */ -static irqreturn_t flexcop_pci_isr(int irq, void *dev_id) -{ - struct flexcop_pci *fc_pci = dev_id; - struct flexcop_device *fc = fc_pci->fc_dev; - unsigned long flags; - flexcop_ibi_value v; - irqreturn_t ret = IRQ_HANDLED; - - spin_lock_irqsave(&fc_pci->irq_lock, flags); - v = fc->read_ibi_reg(fc, irq_20c); - - /* errors */ - if (v.irq_20c.Data_receiver_error) - deb_chk("data receiver error\n"); - if (v.irq_20c.Continuity_error_flag) - deb_chk("Contunuity error flag is set\n"); - if (v.irq_20c.LLC_SNAP_FLAG_set) - deb_chk("LLC_SNAP_FLAG_set is set\n"); - if (v.irq_20c.Transport_Error) - deb_chk("Transport error\n"); - - if ((fc_pci->count % 1000) == 0) - deb_chk("%d valid irq took place so far\n", fc_pci->count); - - if (v.irq_20c.DMA1_IRQ_Status == 1) { - if (fc_pci->active_dma1_addr == 0) - flexcop_pass_dmx_packets(fc_pci->fc_dev, - fc_pci->dma[0].cpu_addr0, - fc_pci->dma[0].size / 188); - else - flexcop_pass_dmx_packets(fc_pci->fc_dev, - fc_pci->dma[0].cpu_addr1, - fc_pci->dma[0].size / 188); - - deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr); - fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr; - /* for the timer IRQ we only can use buffer dmx feeding, because we don't have - * complete TS packets when reading from the DMA memory */ - } else if (v.irq_20c.DMA1_Timer_Status == 1) { - dma_addr_t cur_addr = - fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2; - u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0; - - deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, " - "last_cur_pos: %08x ", - jiffies_to_usecs(jiffies - fc_pci->last_irq), - v.raw, (unsigned long long)cur_addr, cur_pos, - fc_pci->last_dma1_cur_pos); - fc_pci->last_irq = jiffies; - - /* buffer end was reached, restarted from the beginning - * pass the data from last_cur_pos to the buffer end to the demux - */ - if (cur_pos < fc_pci->last_dma1_cur_pos) { - deb_irq(" end was reached: passing %d bytes ", - (fc_pci->dma[0].size*2 - 1) - - fc_pci->last_dma1_cur_pos); - flexcop_pass_dmx_data(fc_pci->fc_dev, - fc_pci->dma[0].cpu_addr0 + - fc_pci->last_dma1_cur_pos, - (fc_pci->dma[0].size*2) - - fc_pci->last_dma1_cur_pos); - fc_pci->last_dma1_cur_pos = 0; - } - - if (cur_pos > fc_pci->last_dma1_cur_pos) { - deb_irq(" passing %d bytes ", - cur_pos - fc_pci->last_dma1_cur_pos); - flexcop_pass_dmx_data(fc_pci->fc_dev, - fc_pci->dma[0].cpu_addr0 + - fc_pci->last_dma1_cur_pos, - cur_pos - fc_pci->last_dma1_cur_pos); - } - deb_irq("\n"); - - fc_pci->last_dma1_cur_pos = cur_pos; - fc_pci->count++; - } else { - deb_irq("isr for flexcop called, " - "apparently without reason (%08x)\n", v.raw); - ret = IRQ_NONE; - } - - spin_unlock_irqrestore(&fc_pci->irq_lock, flags); - return ret; -} - -static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff) -{ - struct flexcop_pci *fc_pci = fc->bus_specific; - if (onoff) { - flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1); - flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2); - flexcop_dma_config_timer(fc, FC_DMA_1, 0); - flexcop_dma_xfer_control(fc, FC_DMA_1, - FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1); - deb_irq("DMA xfer enabled\n"); - - fc_pci->last_dma1_cur_pos = 0; - flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1); - deb_irq("IRQ enabled\n"); - fc_pci->count_prev = fc_pci->count; - } else { - flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0); - deb_irq("IRQ disabled\n"); - - flexcop_dma_xfer_control(fc, FC_DMA_1, - FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0); - deb_irq("DMA xfer disabled\n"); - } - return 0; -} - -static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci) -{ - int ret; - ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0], - FC_DEFAULT_DMA1_BUFSIZE); - if (ret != 0) - return ret; - - ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1], - FC_DEFAULT_DMA2_BUFSIZE); - if (ret != 0) { - flexcop_dma_free(&fc_pci->dma[0]); - return ret; - } - - flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA | - FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1); - flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO | - FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2); - fc_pci->init_state |= FC_PCI_DMA_INIT; - return ret; -} - -static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci) -{ - if (fc_pci->init_state & FC_PCI_DMA_INIT) { - flexcop_dma_free(&fc_pci->dma[0]); - flexcop_dma_free(&fc_pci->dma[1]); - } - fc_pci->init_state &= ~FC_PCI_DMA_INIT; -} - -static int flexcop_pci_init(struct flexcop_pci *fc_pci) -{ - int ret; - - info("card revision %x", fc_pci->pdev->revision); - - if ((ret = pci_enable_device(fc_pci->pdev)) != 0) - return ret; - pci_set_master(fc_pci->pdev); - - if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0) - goto err_pci_disable_device; - - fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800); - - if (!fc_pci->io_mem) { - err("cannot map io memory\n"); - ret = -EIO; - goto err_pci_release_regions; - } - - pci_set_drvdata(fc_pci->pdev, fc_pci); - spin_lock_init(&fc_pci->irq_lock); - if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr, - IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0) - goto err_pci_iounmap; - - fc_pci->init_state |= FC_PCI_INIT; - return ret; - -err_pci_iounmap: - pci_iounmap(fc_pci->pdev, fc_pci->io_mem); - pci_set_drvdata(fc_pci->pdev, NULL); -err_pci_release_regions: - pci_release_regions(fc_pci->pdev); -err_pci_disable_device: - pci_disable_device(fc_pci->pdev); - return ret; -} - -static void flexcop_pci_exit(struct flexcop_pci *fc_pci) -{ - if (fc_pci->init_state & FC_PCI_INIT) { - free_irq(fc_pci->pdev->irq, fc_pci); - pci_iounmap(fc_pci->pdev, fc_pci->io_mem); - pci_set_drvdata(fc_pci->pdev, NULL); - pci_release_regions(fc_pci->pdev); - pci_disable_device(fc_pci->pdev); - } - fc_pci->init_state &= ~FC_PCI_INIT; -} - -static int flexcop_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct flexcop_device *fc; - struct flexcop_pci *fc_pci; - int ret = -ENOMEM; - - if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) { - err("out of memory\n"); - return -ENOMEM; - } - - /* general flexcop init */ - fc_pci = fc->bus_specific; - fc_pci->fc_dev = fc; - - fc->read_ibi_reg = flexcop_pci_read_ibi_reg; - fc->write_ibi_reg = flexcop_pci_write_ibi_reg; - fc->i2c_request = flexcop_i2c_request; - fc->get_mac_addr = flexcop_eeprom_check_mac_addr; - fc->stream_control = flexcop_pci_stream_control; - - if (enable_pid_filtering) - info("will use the HW PID filter."); - else - info("will pass the complete TS to the demuxer."); - - fc->pid_filtering = enable_pid_filtering; - fc->bus_type = FC_PCI; - fc->dev = &pdev->dev; - fc->owner = THIS_MODULE; - - /* bus specific part */ - fc_pci->pdev = pdev; - if ((ret = flexcop_pci_init(fc_pci)) != 0) - goto err_kfree; - - /* init flexcop */ - if ((ret = flexcop_device_initialize(fc)) != 0) - goto err_pci_exit; - - /* init dma */ - if ((ret = flexcop_pci_dma_init(fc_pci)) != 0) - goto err_fc_exit; - - INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work); - - if (irq_chk_intv > 0) - schedule_delayed_work(&fc_pci->irq_check_work, - msecs_to_jiffies(irq_chk_intv < 100 ? - 100 : - irq_chk_intv)); - return ret; - -err_fc_exit: - flexcop_device_exit(fc); -err_pci_exit: - flexcop_pci_exit(fc_pci); -err_kfree: - flexcop_device_kfree(fc); - return ret; -} - -/* in theory every _exit function should be called exactly two times, - * here and in the bail-out-part of the _init-function - */ -static void flexcop_pci_remove(struct pci_dev *pdev) -{ - struct flexcop_pci *fc_pci = pci_get_drvdata(pdev); - - if (irq_chk_intv > 0) - cancel_delayed_work(&fc_pci->irq_check_work); - - flexcop_pci_dma_exit(fc_pci); - flexcop_device_exit(fc_pci->fc_dev); - flexcop_pci_exit(fc_pci); - flexcop_device_kfree(fc_pci->fc_dev); -} - -static struct pci_device_id flexcop_pci_tbl[] = { - { PCI_DEVICE(0x13d0, 0x2103) }, - { }, -}; - -MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl); - -static struct pci_driver flexcop_pci_driver = { - .name = "b2c2_flexcop_pci", - .id_table = flexcop_pci_tbl, - .probe = flexcop_pci_probe, - .remove = flexcop_pci_remove, -}; - -static int __init flexcop_pci_module_init(void) -{ - return pci_register_driver(&flexcop_pci_driver); -} - -static void __exit flexcop_pci_module_exit(void) -{ - pci_unregister_driver(&flexcop_pci_driver); -} - -module_init(flexcop_pci_module_init); -module_exit(flexcop_pci_module_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_NAME); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/b2c2/flexcop-reg.h b/drivers/media/dvb/b2c2/flexcop-reg.h deleted file mode 100644 index dc4528dcbb98..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-reg.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_REG_H__ -#define __FLEXCOP_REG_H__ - -typedef enum { - FLEXCOP_UNK = 0, - FLEXCOP_II, - FLEXCOP_IIB, - FLEXCOP_III, -} flexcop_revision_t; - -typedef enum { - FC_UNK = 0, - FC_CABLE, - FC_AIR_DVBT, - FC_AIR_ATSC1, - FC_AIR_ATSC2, - FC_AIR_ATSC3, - FC_SKY_REV23, - FC_SKY_REV26, - FC_SKY_REV27, - FC_SKY_REV28, -} flexcop_device_type_t; - -typedef enum { - FC_USB = 0, - FC_PCI, -} flexcop_bus_t; - -/* FlexCop IBI Registers */ -#if defined(__LITTLE_ENDIAN) -#include "flexcop_ibi_value_le.h" -#else -#if defined(__BIG_ENDIAN) -#include "flexcop_ibi_value_be.h" -#else -#error no endian defined -#endif -#endif - -#define fc_data_Tag_ID_DVB 0x3e -#define fc_data_Tag_ID_ATSC 0x3f -#define fc_data_Tag_ID_IDSB 0x8b - -#define fc_key_code_default 0x1 -#define fc_key_code_even 0x2 -#define fc_key_code_odd 0x3 - -extern flexcop_ibi_value ibi_zero; - -typedef enum { - FC_I2C_PORT_DEMOD = 1, - FC_I2C_PORT_EEPROM = 2, - FC_I2C_PORT_TUNER = 3, -} flexcop_i2c_port_t; - -typedef enum { - FC_WRITE = 0, - FC_READ = 1, -} flexcop_access_op_t; - -typedef enum { - FC_SRAM_DEST_NET = 1, - FC_SRAM_DEST_CAI = 2, - FC_SRAM_DEST_CAO = 4, - FC_SRAM_DEST_MEDIA = 8 -} flexcop_sram_dest_t; - -typedef enum { - FC_SRAM_DEST_TARGET_WAN_USB = 0, - FC_SRAM_DEST_TARGET_DMA1 = 1, - FC_SRAM_DEST_TARGET_DMA2 = 2, - FC_SRAM_DEST_TARGET_FC3_CA = 3 -} flexcop_sram_dest_target_t; - -typedef enum { - FC_SRAM_2_32KB = 0, /* 64KB */ - FC_SRAM_1_32KB = 1, /* 32KB - default fow FCII */ - FC_SRAM_1_128KB = 2, /* 128KB */ - FC_SRAM_1_48KB = 3, /* 48KB - default for FCIII */ -} flexcop_sram_type_t; - -typedef enum { - FC_WAN_SPEED_4MBITS = 0, - FC_WAN_SPEED_8MBITS = 1, - FC_WAN_SPEED_12MBITS = 2, - FC_WAN_SPEED_16MBITS = 3, -} flexcop_wan_speed_t; - -typedef enum { - FC_DMA_1 = 1, - FC_DMA_2 = 2, -} flexcop_dma_index_t; - -typedef enum { - FC_DMA_SUBADDR_0 = 1, - FC_DMA_SUBADDR_1 = 2, -} flexcop_dma_addr_index_t; - -/* names of the particular registers */ -typedef enum { - dma1_000 = 0x000, - dma1_004 = 0x004, - dma1_008 = 0x008, - dma1_00c = 0x00c, - dma2_010 = 0x010, - dma2_014 = 0x014, - dma2_018 = 0x018, - dma2_01c = 0x01c, - - tw_sm_c_100 = 0x100, - tw_sm_c_104 = 0x104, - tw_sm_c_108 = 0x108, - tw_sm_c_10c = 0x10c, - tw_sm_c_110 = 0x110, - - lnb_switch_freq_200 = 0x200, - misc_204 = 0x204, - ctrl_208 = 0x208, - irq_20c = 0x20c, - sw_reset_210 = 0x210, - misc_214 = 0x214, - mbox_v8_to_host_218 = 0x218, - mbox_host_to_v8_21c = 0x21c, - - pid_filter_300 = 0x300, - pid_filter_304 = 0x304, - pid_filter_308 = 0x308, - pid_filter_30c = 0x30c, - index_reg_310 = 0x310, - pid_n_reg_314 = 0x314, - mac_low_reg_318 = 0x318, - mac_high_reg_31c = 0x31c, - - data_tag_400 = 0x400, - card_id_408 = 0x408, - card_id_40c = 0x40c, - mac_address_418 = 0x418, - mac_address_41c = 0x41c, - - ci_600 = 0x600, - pi_604 = 0x604, - pi_608 = 0x608, - dvb_reg_60c = 0x60c, - - sram_ctrl_reg_700 = 0x700, - net_buf_reg_704 = 0x704, - cai_buf_reg_708 = 0x708, - cao_buf_reg_70c = 0x70c, - media_buf_reg_710 = 0x710, - sram_dest_reg_714 = 0x714, - net_buf_reg_718 = 0x718, - wan_ctrl_reg_71c = 0x71c, -} flexcop_ibi_register; - -#define flexcop_set_ibi_value(reg,attr,val) { \ - flexcop_ibi_value v = fc->read_ibi_reg(fc,reg); \ - v.reg.attr = val; \ - fc->write_ibi_reg(fc,reg,v); \ -} - -#endif diff --git a/drivers/media/dvb/b2c2/flexcop-sram.c b/drivers/media/dvb/b2c2/flexcop-sram.c deleted file mode 100644 index f2199e43e803..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-sram.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-sram.c - functions for controlling the SRAM - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -static void flexcop_sram_set_chip(struct flexcop_device *fc, - flexcop_sram_type_t type) -{ - flexcop_set_ibi_value(wan_ctrl_reg_71c, sram_chip, type); -} - -int flexcop_sram_init(struct flexcop_device *fc) -{ - switch (fc->rev) { - case FLEXCOP_II: - case FLEXCOP_IIB: - flexcop_sram_set_chip(fc, FC_SRAM_1_32KB); - break; - case FLEXCOP_III: - flexcop_sram_set_chip(fc, FC_SRAM_1_48KB); - break; - default: - return -EINVAL; - } - return 0; -} - -int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, - flexcop_sram_dest_target_t target) -{ - flexcop_ibi_value v; - v = fc->read_ibi_reg(fc, sram_dest_reg_714); - - if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) { - err("SRAM destination target to available on FlexCopII(b)\n"); - return -EINVAL; - } - deb_sram("sram dest: %x target: %x\n", dest, target); - - if (dest & FC_SRAM_DEST_NET) - v.sram_dest_reg_714.NET_Dest = target; - if (dest & FC_SRAM_DEST_CAI) - v.sram_dest_reg_714.CAI_Dest = target; - if (dest & FC_SRAM_DEST_CAO) - v.sram_dest_reg_714.CAO_Dest = target; - if (dest & FC_SRAM_DEST_MEDIA) - v.sram_dest_reg_714.MEDIA_Dest = target; - - fc->write_ibi_reg(fc,sram_dest_reg_714,v); - udelay(1000); /* TODO delay really necessary */ - - return 0; -} -EXPORT_SYMBOL(flexcop_sram_set_dest); - -void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s) -{ - flexcop_set_ibi_value(wan_ctrl_reg_71c,wan_speed_sig,s); -} -EXPORT_SYMBOL(flexcop_wan_set_speed); - -void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill) -{ - flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714); - v.sram_dest_reg_714.ctrl_usb_wan = usb_wan; - v.sram_dest_reg_714.ctrl_sramdma = sramdma; - v.sram_dest_reg_714.ctrl_maximumfill = maximumfill; - fc->write_ibi_reg(fc,sram_dest_reg_714,v); -} -EXPORT_SYMBOL(flexcop_sram_ctrl); - -#if 0 -static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) -{ - int i, retries; - u32 command; - - for (i = 0; i < len; i++) { - command = bank | addr | 0x04000000 | (*buf << 0x10); - - retries = 2; - - while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { - mdelay(1); - retries--; - }; - - if (retries == 0) - printk("%s: SRAM timeout\n", __func__); - - write_reg_dw(adapter, 0x700, command); - - buf++; - addr++; - } -} - -static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) -{ - int i, retries; - u32 command, value; - - for (i = 0; i < len; i++) { - command = bank | addr | 0x04008000; - - retries = 10000; - - while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { - mdelay(1); - retries--; - }; - - if (retries == 0) - printk("%s: SRAM timeout\n", __func__); - - write_reg_dw(adapter, 0x700, command); - - retries = 10000; - - while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { - mdelay(1); - retries--; - }; - - if (retries == 0) - printk("%s: SRAM timeout\n", __func__); - - value = read_reg_dw(adapter, 0x700) >> 0x10; - - *buf = (value & 0xff); - - addr++; - buf++; - } -} - -static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) -{ - u32 bank; - - bank = 0; - - if (adapter->dw_sram_type == 0x20000) { - bank = (addr & 0x18000) << 0x0d; - } - - if (adapter->dw_sram_type == 0x00000) { - if ((addr >> 0x0f) == 0) - bank = 0x20000000; - else - bank = 0x10000000; - } - flex_sram_write(adapter, bank, addr & 0x7fff, buf, len); -} - -static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) -{ - u32 bank; - bank = 0; - - if (adapter->dw_sram_type == 0x20000) { - bank = (addr & 0x18000) << 0x0d; - } - - if (adapter->dw_sram_type == 0x00000) { - if ((addr >> 0x0f) == 0) - bank = 0x20000000; - else - bank = 0x10000000; - } - flex_sram_read(adapter, bank, addr & 0x7fff, buf, len); -} - -static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len) -{ - u32 length; - while (len != 0) { - length = len; - /* check if the address range belongs to the same - * 32K memory chip. If not, the data is read - * from one chip at a time */ - if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { - length = (((addr >> 0x0f) + 1) << 0x0f) - addr; - } - - sram_read_chunk(adapter, addr, buf, length); - addr = addr + length; - buf = buf + length; - len = len - length; - } -} - -static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len) -{ - u32 length; - while (len != 0) { - length = len; - - /* check if the address range belongs to the same - * 32K memory chip. If not, the data is - * written to one chip at a time */ - if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { - length = (((addr >> 0x0f) + 1) << 0x0f) - addr; - } - - sram_write_chunk(adapter, addr, buf, length); - addr = addr + length; - buf = buf + length; - len = len - length; - } -} - -static void sram_set_size(struct adapter *adapter, u32 mask) -{ - write_reg_dw(adapter, 0x71c, - (mask | (~0x30000 & read_reg_dw(adapter, 0x71c)))); -} - -static void sram_init(struct adapter *adapter) -{ - u32 tmp; - tmp = read_reg_dw(adapter, 0x71c); - write_reg_dw(adapter, 0x71c, 1); - - if (read_reg_dw(adapter, 0x71c) != 0) { - write_reg_dw(adapter, 0x71c, tmp); - adapter->dw_sram_type = tmp & 0x30000; - ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); - } else { - adapter->dw_sram_type = 0x10000; - ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); - } -} - -static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr) -{ - u8 tmp1, tmp2; - dprintk("%s: mask = %x, addr = %x\n", __func__, mask, addr); - - sram_set_size(adapter, mask); - sram_init(adapter); - - tmp2 = 0xa5; - tmp1 = 0x4f; - - sram_write(adapter, addr, &tmp2, 1); - sram_write(adapter, addr + 4, &tmp1, 1); - - tmp2 = 0; - mdelay(20); - - sram_read(adapter, addr, &tmp2, 1); - sram_read(adapter, addr, &tmp2, 1); - - dprintk("%s: wrote 0xa5, read 0x%2x\n", __func__, tmp2); - - if (tmp2 != 0xa5) - return 0; - - tmp2 = 0x5a; - tmp1 = 0xf4; - - sram_write(adapter, addr, &tmp2, 1); - sram_write(adapter, addr + 4, &tmp1, 1); - - tmp2 = 0; - mdelay(20); - - sram_read(adapter, addr, &tmp2, 1); - sram_read(adapter, addr, &tmp2, 1); - - dprintk("%s: wrote 0x5a, read 0x%2x\n", __func__, tmp2); - - if (tmp2 != 0x5a) - return 0; - return 1; -} - -static u32 sram_length(struct adapter *adapter) -{ - if (adapter->dw_sram_type == 0x10000) - return 32768; /* 32K */ - if (adapter->dw_sram_type == 0x00000) - return 65536; /* 64K */ - if (adapter->dw_sram_type == 0x20000) - return 131072; /* 128K */ - return 32768; /* 32K */ -} - -/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory. - - for 128K there are 4x32K chips at bank 0,1,2,3. - - for 64K there are 2x32K chips at bank 1,2. - - for 32K there is one 32K chip at bank 0. - - FlexCop works only with one bank at a time. The bank is selected - by bits 28-29 of the 0x700 register. - - bank 0 covers addresses 0x00000-0x07fff - bank 1 covers addresses 0x08000-0x0ffff - bank 2 covers addresses 0x10000-0x17fff - bank 3 covers addresses 0x18000-0x1ffff */ - -static int flexcop_sram_detect(struct flexcop_device *fc) -{ - flexcop_ibi_value r208, r71c_0, vr71c_1; - r208 = fc->read_ibi_reg(fc, ctrl_208); - fc->write_ibi_reg(fc, ctrl_208, ibi_zero); - - r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c); - write_reg_dw(adapter, 0x71c, 1); - tmp3 = read_reg_dw(adapter, 0x71c); - dprintk("%s: tmp3 = %x\n", __func__, tmp3); - write_reg_dw(adapter, 0x71c, tmp2); - - // check for internal SRAM ??? - tmp3--; - if (tmp3 != 0) { - sram_set_size(adapter, 0x10000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 32K\n", __func__); - return 32; - } - - if (sram_test_location(adapter, 0x20000, 0x18000) != 0) { - sram_set_size(adapter, 0x20000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 128K\n", __func__); - return 128; - } - - if (sram_test_location(adapter, 0x00000, 0x10000) != 0) { - sram_set_size(adapter, 0x00000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 64K\n", __func__); - return 64; - } - - if (sram_test_location(adapter, 0x10000, 0x00000) != 0) { - sram_set_size(adapter, 0x10000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 32K\n", __func__); - return 32; - } - - sram_set_size(adapter, 0x10000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: SRAM detection failed. Set to 32K \n", __func__); - return 0; -} - -static void sll_detect_sram_size(struct adapter *adapter) -{ - sram_detect_for_flex2(adapter); -} - -#endif diff --git a/drivers/media/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c deleted file mode 100644 index 8b6275f85908..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-usb.c +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-usb.c - covers the USB part - * see flexcop.c for copyright information - */ -#define FC_LOG_PREFIX "flexcop_usb" -#include "flexcop-usb.h" -#include "flexcop-common.h" - -/* Version information */ -#define DRIVER_VERSION "0.1" -#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver" -#define DRIVER_AUTHOR "Patrick Boettcher " - -/* debug */ -#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG -#define dprintk(level,args...) \ - do { if ((debug & level)) printk(args); } while (0) - -#define debug_dump(b, l, method) do {\ - int i; \ - for (i = 0; i < l; i++) \ - method("%02x ", b[i]); \ - method("\n"); \ -} while (0) - -#define DEBSTATUS "" -#else -#define dprintk(level, args...) -#define debug_dump(b, l, method) -#define DEBSTATUS " (debugging is not enabled)" -#endif - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2," - "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS); -#undef DEBSTATUS - -#define deb_info(args...) dprintk(0x01, args) -#define deb_ts(args...) dprintk(0x02, args) -#define deb_ctrl(args...) dprintk(0x04, args) -#define deb_i2c(args...) dprintk(0x08, args) -#define deb_v8(args...) dprintk(0x10, args) - -/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits - * in the IBI address, to make the V8 code simpler. - * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used) - * in general: 0000 0HHH 000L LL00 - * IBI ADDRESS FORMAT: RHHH BLLL - * - * where R is the read(1)/write(0) bit, B is the busy bit - * and HHH and LLL are the two sets of three bits from the PCI address. - */ -#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \ - (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70)) -#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \ - (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4)) - -/* - * DKT 020228 - * - forget about this VENDOR_BUFFER_SIZE, read and write register - * deal with DWORD or 4 bytes, that should be should from now on - * - from now on, we don't support anything older than firm 1.00 - * I eliminated the write register as a 2 trip of writing hi word and lo word - * and force this to write only 4 bytes at a time. - * NOTE: this should work with all the firmware from 1.00 and newer - */ -static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read) -{ - struct flexcop_usb *fc_usb = fc->bus_specific; - u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG; - u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR; - u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | - (read ? 0x80 : 0); - - int len = usb_control_msg(fc_usb->udev, - read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT, - request, - request_type, /* 0xc0 read or 0x40 write */ - wAddress, - 0, - val, - sizeof(u32), - B2C2_WAIT_FOR_OPERATION_RDW * HZ); - - if (len != sizeof(u32)) { - err("error while %s dword from %d (%d).", read ? "reading" : - "writing", wAddress, wRegOffsPCI); - return -EIO; - } - return 0; -} -/* - * DKT 010817 - add support for V8 memory read/write and flash update - */ -static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb, - flexcop_usb_request_t req, u8 page, u16 wAddress, - u8 *pbBuffer, u32 buflen) -{ - u8 request_type = USB_TYPE_VENDOR; - u16 wIndex; - int nWaitTime, pipe, len; - wIndex = page << 8; - - switch (req) { - case B2C2_USB_READ_V8_MEM: - nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ; - request_type |= USB_DIR_IN; - pipe = B2C2_USB_CTRL_PIPE_IN; - break; - case B2C2_USB_WRITE_V8_MEM: - wIndex |= pbBuffer[0]; - request_type |= USB_DIR_OUT; - nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE; - pipe = B2C2_USB_CTRL_PIPE_OUT; - break; - case B2C2_USB_FLASH_BLOCK: - request_type |= USB_DIR_OUT; - nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH; - pipe = B2C2_USB_CTRL_PIPE_OUT; - break; - default: - deb_info("unsupported request for v8_mem_req %x.\n", req); - return -EINVAL; - } - deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req, - wAddress, wIndex, buflen); - - len = usb_control_msg(fc_usb->udev, pipe, - req, - request_type, - wAddress, - wIndex, - pbBuffer, - buflen, - nWaitTime * HZ); - - debug_dump(pbBuffer, len, deb_v8); - return len == buflen ? 0 : -EIO; -} - -#define bytes_left_to_read_on_page(paddr,buflen) \ - ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \ - ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK))) - -static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, - flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, - u32 addr, int extended, u8 *buf, u32 len) -{ - int i,ret = 0; - u16 wMax; - u32 pagechunk = 0; - - switch(req) { - case B2C2_USB_READ_V8_MEM: - wMax = USB_MEM_READ_MAX; - break; - case B2C2_USB_WRITE_V8_MEM: - wMax = USB_MEM_WRITE_MAX; - break; - case B2C2_USB_FLASH_BLOCK: - wMax = USB_FLASH_MAX; - break; - default: - return -EINVAL; - break; - } - for (i = 0; i < len;) { - pagechunk = - wMax < bytes_left_to_read_on_page(addr, len) ? - wMax : - bytes_left_to_read_on_page(addr, len); - deb_info("%x\n", - (addr & V8_MEMORY_PAGE_MASK) | - (V8_MEMORY_EXTENDED*extended)); - - ret = flexcop_usb_v8_memory_req(fc_usb, req, - page_start + (addr / V8_MEMORY_PAGE_SIZE), - (addr & V8_MEMORY_PAGE_MASK) | - (V8_MEMORY_EXTENDED*extended), - &buf[i], pagechunk); - - if (ret < 0) - return ret; - addr += pagechunk; - len -= pagechunk; - } - return 0; -} - -static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended) -{ - return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM, - V8_MEMORY_PAGE_FLASH, 0x1f010, 1, - fc->dvb_adapter.proposed_mac, 6); -} - -#if 0 -static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set, - flexcop_usb_utility_function_t func, u8 extra, u16 wIndex, - u16 buflen, u8 *pvBuffer) -{ - u16 wValue; - u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR; - int nWaitTime = 2, - pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len; - wValue = (func << 8) | extra; - - len = usb_control_msg(fc_usb->udev,pipe, - B2C2_USB_UTILITY, - request_type, - wValue, - wIndex, - pvBuffer, - buflen, - nWaitTime * HZ); - return len == buflen ? 0 : -EIO; -} -#endif - -/* usb i2c stuff */ -static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c, - flexcop_usb_request_t req, flexcop_usb_i2c_function_t func, - u8 chipaddr, u8 addr, u8 *buf, u8 buflen) -{ - struct flexcop_usb *fc_usb = i2c->fc->bus_specific; - u16 wValue, wIndex; - int nWaitTime,pipe,len; - u8 request_type = USB_TYPE_VENDOR; - - switch (func) { - case USB_FUNC_I2C_WRITE: - case USB_FUNC_I2C_MULTIWRITE: - case USB_FUNC_I2C_REPEATWRITE: - /* DKT 020208 - add this to support special case of DiSEqC */ - case USB_FUNC_I2C_CHECKWRITE: - pipe = B2C2_USB_CTRL_PIPE_OUT; - nWaitTime = 2; - request_type |= USB_DIR_OUT; - break; - case USB_FUNC_I2C_READ: - case USB_FUNC_I2C_REPEATREAD: - pipe = B2C2_USB_CTRL_PIPE_IN; - nWaitTime = 2; - request_type |= USB_DIR_IN; - break; - default: - deb_info("unsupported function for i2c_req %x\n", func); - return -EINVAL; - } - wValue = (func << 8) | (i2c->port << 4); - wIndex = (chipaddr << 8 ) | addr; - - deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n", - func, request_type, req, - wValue & 0xff, wValue >> 8, - wIndex & 0xff, wIndex >> 8); - - len = usb_control_msg(fc_usb->udev,pipe, - req, - request_type, - wValue, - wIndex, - buf, - buflen, - nWaitTime * HZ); - return len == buflen ? 0 : -EREMOTEIO; -} - -/* actual bus specific access functions, - make sure prototype are/will be equal to pci */ -static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, - flexcop_ibi_register reg) -{ - flexcop_ibi_value val; - val.raw = 0; - flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1); - return val; -} - -static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, flexcop_ibi_value val) -{ - return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0); -} - -static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c, - flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) -{ - if (op == FC_READ) - return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, - USB_FUNC_I2C_READ, chipaddr, addr, buf, len); - else - return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, - USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len); -} - -static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, - u8 *buffer, int buffer_length) -{ - u8 *b; - int l; - - deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", - fc_usb->tmp_buffer_length, buffer_length); - - if (fc_usb->tmp_buffer_length > 0) { - memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, - buffer_length); - fc_usb->tmp_buffer_length += buffer_length; - b = fc_usb->tmp_buffer; - l = fc_usb->tmp_buffer_length; - } else { - b=buffer; - l=buffer_length; - } - - while (l >= 190) { - if (*b == 0xff) { - switch (*(b+1) & 0x03) { - case 0x01: /* media packet */ - if (*(b+2) == 0x47) - flexcop_pass_dmx_packets( - fc_usb->fc_dev, b+2, 1); - else - deb_ts("not ts packet %*ph\n", 4, b+2); - b += 190; - l -= 190; - break; - default: - deb_ts("wrong packet type\n"); - l = 0; - break; - } - } else { - deb_ts("wrong header\n"); - l = 0; - } - } - - if (l>0) - memcpy(fc_usb->tmp_buffer, b, l); - fc_usb->tmp_buffer_length = l; -} - -static void flexcop_usb_urb_complete(struct urb *urb) -{ - struct flexcop_usb *fc_usb = urb->context; - int i; - - if (urb->actual_length > 0) - deb_ts("urb completed, bufsize: %d actlen; %d\n", - urb->transfer_buffer_length, urb->actual_length); - - for (i = 0; i < urb->number_of_packets; i++) { - if (urb->iso_frame_desc[i].status < 0) { - err("iso frame descriptor %d has an error: %d\n", i, - urb->iso_frame_desc[i].status); - } else - if (urb->iso_frame_desc[i].actual_length > 0) { - deb_ts("passed %d bytes to the demux\n", - urb->iso_frame_desc[i].actual_length); - - flexcop_usb_process_frame(fc_usb, - urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); - } - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - usb_submit_urb(urb,GFP_ATOMIC); -} - -static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff) -{ - /* submit/kill iso packets */ - return 0; -} - -static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) -{ - int i; - for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) - if (fc_usb->iso_urb[i] != NULL) { - deb_ts("unlinking/killing urb no. %d\n",i); - usb_kill_urb(fc_usb->iso_urb[i]); - usb_free_urb(fc_usb->iso_urb[i]); - } - - if (fc_usb->iso_buffer != NULL) - pci_free_consistent(NULL, - fc_usb->buffer_size, fc_usb->iso_buffer, - fc_usb->dma_addr); -} - -static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) -{ - u16 frame_size = le16_to_cpu( - fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize); - int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * - frame_size, i, j, ret; - int buffer_offset = 0; - - deb_ts("creating %d iso-urbs with %d frames " - "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB, - B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize); - - fc_usb->iso_buffer = pci_alloc_consistent(NULL, - bufsize, &fc_usb->dma_addr); - if (fc_usb->iso_buffer == NULL) - return -ENOMEM; - - memset(fc_usb->iso_buffer, 0, bufsize); - fc_usb->buffer_size = bufsize; - - /* creating iso urbs */ - for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { - fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO, - GFP_ATOMIC); - if (fc_usb->iso_urb[i] == NULL) { - ret = -ENOMEM; - goto urb_error; - } - } - - /* initialising and submitting iso urbs */ - for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { - int frame_offset = 0; - struct urb *urb = fc_usb->iso_urb[i]; - deb_ts("initializing and submitting urb no. %d " - "(buf_offset: %d).\n", i, buffer_offset); - - urb->dev = fc_usb->udev; - urb->context = fc_usb; - urb->complete = flexcop_usb_urb_complete; - urb->pipe = B2C2_USB_DATA_PIPE; - urb->transfer_flags = URB_ISO_ASAP; - urb->interval = 1; - urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO; - urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO; - urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset; - - buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO; - for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) { - deb_ts("urb no: %d, frame: %d, frame_offset: %d\n", - i, j, frame_offset); - urb->iso_frame_desc[j].offset = frame_offset; - urb->iso_frame_desc[j].length = frame_size; - frame_offset += frame_size; - } - - if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) { - err("submitting urb %d failed with %d.", i, ret); - goto urb_error; - } - deb_ts("submitted urb no. %d.\n",i); - } - - /* SRAM */ - flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA | - FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, - FC_SRAM_DEST_TARGET_WAN_USB); - flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS); - flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1); - return 0; - -urb_error: - flexcop_usb_transfer_exit(fc_usb); - return ret; -} - -static int flexcop_usb_init(struct flexcop_usb *fc_usb) -{ - /* use the alternate setting with the larges buffer */ - usb_set_interface(fc_usb->udev,0,1); - switch (fc_usb->udev->speed) { - case USB_SPEED_LOW: - err("cannot handle USB speed because it is too slow."); - return -ENODEV; - break; - case USB_SPEED_FULL: - info("running at FULL speed."); - break; - case USB_SPEED_HIGH: - info("running at HIGH speed."); - break; - case USB_SPEED_UNKNOWN: /* fall through */ - default: - err("cannot handle USB speed because it is unknown."); - return -ENODEV; - } - usb_set_intfdata(fc_usb->uintf, fc_usb); - return 0; -} - -static void flexcop_usb_exit(struct flexcop_usb *fc_usb) -{ - usb_set_intfdata(fc_usb->uintf, NULL); -} - -static int flexcop_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct flexcop_usb *fc_usb = NULL; - struct flexcop_device *fc = NULL; - int ret; - - if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { - err("out of memory\n"); - return -ENOMEM; - } - - /* general flexcop init */ - fc_usb = fc->bus_specific; - fc_usb->fc_dev = fc; - - fc->read_ibi_reg = flexcop_usb_read_ibi_reg; - fc->write_ibi_reg = flexcop_usb_write_ibi_reg; - fc->i2c_request = flexcop_usb_i2c_request; - fc->get_mac_addr = flexcop_usb_get_mac_addr; - - fc->stream_control = flexcop_usb_stream_control; - - fc->pid_filtering = 1; - fc->bus_type = FC_USB; - - fc->dev = &udev->dev; - fc->owner = THIS_MODULE; - - /* bus specific part */ - fc_usb->udev = udev; - fc_usb->uintf = intf; - if ((ret = flexcop_usb_init(fc_usb)) != 0) - goto err_kfree; - - /* init flexcop */ - if ((ret = flexcop_device_initialize(fc)) != 0) - goto err_usb_exit; - - /* xfer init */ - if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0) - goto err_fc_exit; - - info("%s successfully initialized and connected.", DRIVER_NAME); - return 0; - -err_fc_exit: - flexcop_device_exit(fc); -err_usb_exit: - flexcop_usb_exit(fc_usb); -err_kfree: - flexcop_device_kfree(fc); - return ret; -} - -static void flexcop_usb_disconnect(struct usb_interface *intf) -{ - struct flexcop_usb *fc_usb = usb_get_intfdata(intf); - flexcop_usb_transfer_exit(fc_usb); - flexcop_device_exit(fc_usb->fc_dev); - flexcop_usb_exit(fc_usb); - flexcop_device_kfree(fc_usb->fc_dev); - info("%s successfully deinitialized and disconnected.", DRIVER_NAME); -} - -static struct usb_device_id flexcop_usb_table [] = { - { USB_DEVICE(0x0af7, 0x0101) }, - { } -}; -MODULE_DEVICE_TABLE (usb, flexcop_usb_table); - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver flexcop_usb_driver = { - .name = "b2c2_flexcop_usb", - .probe = flexcop_usb_probe, - .disconnect = flexcop_usb_disconnect, - .id_table = flexcop_usb_table, -}; - -module_usb_driver(flexcop_usb_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_NAME); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/b2c2/flexcop-usb.h b/drivers/media/dvb/b2c2/flexcop-usb.h deleted file mode 100644 index 92529a9c4475..000000000000 --- a/drivers/media/dvb/b2c2/flexcop-usb.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-usb.h - header file for the USB part - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_USB_H_INCLUDED__ -#define __FLEXCOP_USB_H_INCLUDED__ - -#include - -/* transfer parameters */ -#define B2C2_USB_FRAMES_PER_ISO 4 -#define B2C2_USB_NUM_ISO_URB 4 - -#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0) -#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0) -#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81) - -struct flexcop_usb { - struct usb_device *udev; - struct usb_interface *uintf; - - u8 *iso_buffer; - int buffer_size; - dma_addr_t dma_addr; - - struct urb *iso_urb[B2C2_USB_NUM_ISO_URB]; - struct flexcop_device *fc_dev; - - u8 tmp_buffer[1023+190]; - int tmp_buffer_length; -}; - -#if 0 -/* request types TODO What is its use?*/ -typedef enum { - -} flexcop_usb_request_type_t; -#endif - -/* request */ -typedef enum { - B2C2_USB_WRITE_V8_MEM = 0x04, - B2C2_USB_READ_V8_MEM = 0x05, - B2C2_USB_READ_REG = 0x08, - B2C2_USB_WRITE_REG = 0x0A, - B2C2_USB_WRITEREGHI = 0x0B, - B2C2_USB_FLASH_BLOCK = 0x10, - B2C2_USB_I2C_REQUEST = 0x11, - B2C2_USB_UTILITY = 0x12, -} flexcop_usb_request_t; - -/* function definition for I2C_REQUEST */ -typedef enum { - USB_FUNC_I2C_WRITE = 0x01, - USB_FUNC_I2C_MULTIWRITE = 0x02, - USB_FUNC_I2C_READ = 0x03, - USB_FUNC_I2C_REPEATWRITE = 0x04, - USB_FUNC_GET_DESCRIPTOR = 0x05, - USB_FUNC_I2C_REPEATREAD = 0x06, - /* DKT 020208 - add this to support special case of DiSEqC */ - USB_FUNC_I2C_CHECKWRITE = 0x07, - USB_FUNC_I2C_CHECKRESULT = 0x08, -} flexcop_usb_i2c_function_t; - -/* function definition for UTILITY request 0x12 - * DKT 020304 - new utility function */ -typedef enum { - UTILITY_SET_FILTER = 0x01, - UTILITY_DATA_ENABLE = 0x02, - UTILITY_FLEX_MULTIWRITE = 0x03, - UTILITY_SET_BUFFER_SIZE = 0x04, - UTILITY_FLEX_OPERATOR = 0x05, - UTILITY_FLEX_RESET300_START = 0x06, - UTILITY_FLEX_RESET300_STOP = 0x07, - UTILITY_FLEX_RESET300 = 0x08, - UTILITY_SET_ISO_SIZE = 0x09, - UTILITY_DATA_RESET = 0x0A, - UTILITY_GET_DATA_STATUS = 0x10, - UTILITY_GET_V8_REG = 0x11, - /* DKT 020326 - add function for v1.14 */ - UTILITY_SRAM_WRITE = 0x12, - UTILITY_SRAM_READ = 0x13, - UTILITY_SRAM_TESTFILL = 0x14, - UTILITY_SRAM_TESTSET = 0x15, - UTILITY_SRAM_TESTVERIFY = 0x16, -} flexcop_usb_utility_function_t; - -#define B2C2_WAIT_FOR_OPERATION_RW (1*HZ) -#define B2C2_WAIT_FOR_OPERATION_RDW (3*HZ) -#define B2C2_WAIT_FOR_OPERATION_WDW (1*HZ) - -#define B2C2_WAIT_FOR_OPERATION_V8READ (3*HZ) -#define B2C2_WAIT_FOR_OPERATION_V8WRITE (3*HZ) -#define B2C2_WAIT_FOR_OPERATION_V8FLASH (3*HZ) - -typedef enum { - V8_MEMORY_PAGE_DVB_CI = 0x20, - V8_MEMORY_PAGE_DVB_DS = 0x40, - V8_MEMORY_PAGE_MULTI2 = 0x60, - V8_MEMORY_PAGE_FLASH = 0x80 -} flexcop_usb_mem_page_t; - -#define V8_MEMORY_EXTENDED (1 << 15) -#define USB_MEM_READ_MAX 32 -#define USB_MEM_WRITE_MAX 1 -#define USB_FLASH_MAX 8 -#define V8_MEMORY_PAGE_SIZE 0x8000 /* 32K */ -#define V8_MEMORY_PAGE_MASK 0x7FFF - -#endif diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/dvb/b2c2/flexcop.c deleted file mode 100644 index b1e8c99f469b..000000000000 --- a/drivers/media/dvb/b2c2/flexcop.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop.c - main module part - * Copyright (C) 2004-9 Patrick Boettcher - * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc - * - * Acknowledgements: - * John Jurrius from BBTI, Inc. for extensive support - * with code examples and data books - * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting) - * - * Contributions to the skystar2-driver have been done by - * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes) - * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code) - * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu) - * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac - * filtering) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "flexcop.h" - -#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip" -#define DRIVER_AUTHOR "Patrick Boettcher demux->priv; - return flexcop_pid_feed_control(fc, dvbdmxfeed, 1); -} - -static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct flexcop_device *fc = dvbdmxfeed->demux->priv; - return flexcop_pid_feed_control(fc, dvbdmxfeed, 0); -} - -static int flexcop_dvb_init(struct flexcop_device *fc) -{ - int ret = dvb_register_adapter(&fc->dvb_adapter, - "FlexCop Digital TV device", fc->owner, - fc->dev, adapter_nr); - if (ret < 0) { - err("error registering DVB adapter"); - return ret; - } - fc->dvb_adapter.priv = fc; - - fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING - | DMX_MEMORY_BASED_FILTERING); - fc->demux.priv = fc; - fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED; - fc->demux.start_feed = flexcop_dvb_start_feed; - fc->demux.stop_feed = flexcop_dvb_stop_feed; - fc->demux.write_to_decoder = NULL; - - ret = dvb_dmx_init(&fc->demux); - if (ret < 0) { - err("dvb_dmx failed: error %d", ret); - goto err_dmx; - } - - fc->hw_frontend.source = DMX_FRONTEND_0; - - fc->dmxdev.filternum = fc->demux.feednum; - fc->dmxdev.demux = &fc->demux.dmx; - fc->dmxdev.capabilities = 0; - ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter); - if (ret < 0) { - err("dvb_dmxdev_init failed: error %d", ret); - goto err_dmx_dev; - } - - ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend); - if (ret < 0) { - err("adding hw_frontend to dmx failed: error %d", ret); - goto err_dmx_add_hw_frontend; - } - - fc->mem_frontend.source = DMX_MEMORY_FE; - ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend); - if (ret < 0) { - err("adding mem_frontend to dmx failed: error %d", ret); - goto err_dmx_add_mem_frontend; - } - - ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend); - if (ret < 0) { - err("connect frontend failed: error %d", ret); - goto err_connect_frontend; - } - - ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx); - if (ret < 0) { - err("dvb_net_init failed: error %d", ret); - goto err_net; - } - - fc->init_state |= FC_STATE_DVB_INIT; - return 0; - -err_net: - fc->demux.dmx.disconnect_frontend(&fc->demux.dmx); -err_connect_frontend: - fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend); -err_dmx_add_mem_frontend: - fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend); -err_dmx_add_hw_frontend: - dvb_dmxdev_release(&fc->dmxdev); -err_dmx_dev: - dvb_dmx_release(&fc->demux); -err_dmx: - dvb_unregister_adapter(&fc->dvb_adapter); - return ret; -} - -static void flexcop_dvb_exit(struct flexcop_device *fc) -{ - if (fc->init_state & FC_STATE_DVB_INIT) { - dvb_net_release(&fc->dvbnet); - - fc->demux.dmx.close(&fc->demux.dmx); - fc->demux.dmx.remove_frontend(&fc->demux.dmx, - &fc->mem_frontend); - fc->demux.dmx.remove_frontend(&fc->demux.dmx, - &fc->hw_frontend); - dvb_dmxdev_release(&fc->dmxdev); - dvb_dmx_release(&fc->demux); - dvb_unregister_adapter(&fc->dvb_adapter); - deb_info("deinitialized dvb stuff\n"); - } - fc->init_state &= ~FC_STATE_DVB_INIT; -} - -/* these methods are necessary to achieve the long-term-goal of hiding the - * struct flexcop_device from the bus-parts */ -void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len) -{ - dvb_dmx_swfilter(&fc->demux, buf, len); -} -EXPORT_SYMBOL(flexcop_pass_dmx_data); - -void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no) -{ - dvb_dmx_swfilter_packets(&fc->demux, buf, no); -} -EXPORT_SYMBOL(flexcop_pass_dmx_packets); - -static void flexcop_reset(struct flexcop_device *fc) -{ - flexcop_ibi_value v210, v204; - - /* reset the flexcop itself */ - fc->write_ibi_reg(fc,ctrl_208,ibi_zero); - - v210.raw = 0; - v210.sw_reset_210.reset_block_000 = 1; - v210.sw_reset_210.reset_block_100 = 1; - v210.sw_reset_210.reset_block_200 = 1; - v210.sw_reset_210.reset_block_300 = 1; - v210.sw_reset_210.reset_block_400 = 1; - v210.sw_reset_210.reset_block_500 = 1; - v210.sw_reset_210.reset_block_600 = 1; - v210.sw_reset_210.reset_block_700 = 1; - v210.sw_reset_210.Block_reset_enable = 0xb2; - v210.sw_reset_210.Special_controls = 0xc259; - fc->write_ibi_reg(fc,sw_reset_210,v210); - msleep(1); - - /* reset the periphical devices */ - - v204 = fc->read_ibi_reg(fc,misc_204); - v204.misc_204.Per_reset_sig = 0; - fc->write_ibi_reg(fc,misc_204,v204); - msleep(1); - v204.misc_204.Per_reset_sig = 1; - fc->write_ibi_reg(fc,misc_204,v204); -} - -void flexcop_reset_block_300(struct flexcop_device *fc) -{ - flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208), - v210 = fc->read_ibi_reg(fc, sw_reset_210); - - deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw); - fc->write_ibi_reg(fc,ctrl_208,ibi_zero); - - v210.sw_reset_210.reset_block_300 = 1; - v210.sw_reset_210.Block_reset_enable = 0xb2; - - fc->write_ibi_reg(fc,sw_reset_210,v210); - fc->write_ibi_reg(fc,ctrl_208,v208_save); -} - -struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len) -{ - void *bus; - struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device), - GFP_KERNEL); - if (!fc) { - err("no memory"); - return NULL; - } - - bus = kzalloc(bus_specific_len, GFP_KERNEL); - if (!bus) { - err("no memory"); - kfree(fc); - return NULL; - } - - fc->bus_specific = bus; - - return fc; -} -EXPORT_SYMBOL(flexcop_device_kmalloc); - -void flexcop_device_kfree(struct flexcop_device *fc) -{ - kfree(fc->bus_specific); - kfree(fc); -} -EXPORT_SYMBOL(flexcop_device_kfree); - -int flexcop_device_initialize(struct flexcop_device *fc) -{ - int ret; - ibi_zero.raw = 0; - - flexcop_reset(fc); - flexcop_determine_revision(fc); - flexcop_sram_init(fc); - flexcop_hw_filter_init(fc); - flexcop_smc_ctrl(fc, 0); - - ret = flexcop_dvb_init(fc); - if (ret) - goto error; - - /* i2c has to be done before doing EEProm stuff - - * because the EEProm is accessed via i2c */ - ret = flexcop_i2c_init(fc); - if (ret) - goto error; - - /* do the MAC address reading after initializing the dvb_adapter */ - if (fc->get_mac_addr(fc, 0) == 0) { - u8 *b = fc->dvb_adapter.proposed_mac; - info("MAC address = %pM", b); - flexcop_set_mac_filter(fc,b); - flexcop_mac_filter_ctrl(fc,1); - } else - warn("reading of MAC address failed.\n"); - - ret = flexcop_frontend_init(fc); - if (ret) - goto error; - - flexcop_device_name(fc,"initialization of","complete"); - return 0; - -error: - flexcop_device_exit(fc); - return ret; -} -EXPORT_SYMBOL(flexcop_device_initialize); - -void flexcop_device_exit(struct flexcop_device *fc) -{ - flexcop_frontend_exit(fc); - flexcop_i2c_exit(fc); - flexcop_dvb_exit(fc); -} -EXPORT_SYMBOL(flexcop_device_exit); - -static int flexcop_module_init(void) -{ - info(DRIVER_NAME " loaded successfully"); - return 0; -} - -static void flexcop_module_cleanup(void) -{ - info(DRIVER_NAME " unloaded successfully"); -} - -module_init(flexcop_module_init); -module_exit(flexcop_module_cleanup); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_NAME); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/b2c2/flexcop.h b/drivers/media/dvb/b2c2/flexcop.h deleted file mode 100644 index 897b10c85ad9..000000000000 --- a/drivers/media/dvb/b2c2/flexcop.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop.h - private header file for all flexcop-chip-source files - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_H__ -#define __FLEXCOP_H___ - -#define FC_LOG_PREFIX "b2c2-flexcop" -#include "flexcop-common.h" - -extern int b2c2_flexcop_debug; - -/* debug */ -#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG -#define dprintk(level,args...) \ - do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0) -#else -#define dprintk(level,args...) -#endif - -#define deb_info(args...) dprintk(0x01, args) -#define deb_tuner(args...) dprintk(0x02, args) -#define deb_i2c(args...) dprintk(0x04, args) -#define deb_ts(args...) dprintk(0x08, args) -#define deb_sram(args...) dprintk(0x10, args) -#define deb_rdump(args...) dprintk(0x20, args) - -#endif diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h b/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h deleted file mode 100644 index 8f64bdbd72bb..000000000000 --- a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h +++ /dev/null @@ -1,455 +0,0 @@ -/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * register descriptions - * see flexcop.c for copyright information - */ -/* This file is automatically generated, do not edit things here. */ -#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ -#define __FLEXCOP_IBI_VALUE_INCLUDED__ - -typedef union { - u32 raw; - - struct { - u32 dma_address0 :30; - u32 dma_0No_update : 1; - u32 dma_0start : 1; - } dma_0x0; - - struct { - u32 dma_addr_size :24; - u32 DMA_maxpackets : 8; - } dma_0x4_remap; - - struct { - u32 dma_addr_size :24; - u32 unused : 1; - u32 dma1timer : 7; - } dma_0x4_read; - - struct { - u32 dma_addr_size :24; - u32 dmatimer : 7; - u32 unused : 1; - } dma_0x4_write; - - struct { - u32 dma_cur_addr :30; - u32 unused : 2; - } dma_0x8; - - struct { - u32 dma_address1 :30; - u32 remap_enable : 1; - u32 dma_1start : 1; - } dma_0xc; - - struct { - u32 st_done : 1; - u32 no_base_addr_ack_error : 1; - u32 twoWS_port_reg : 2; - u32 total_bytes : 2; - u32 twoWS_rw : 1; - u32 working_start : 1; - u32 data1_reg : 8; - u32 baseaddr : 8; - u32 reserved1 : 1; - u32 chipaddr : 7; - } tw_sm_c_100; - - struct { - u32 unused : 6; - u32 force_stop : 1; - u32 exlicit_stops : 1; - u32 data4_reg : 8; - u32 data3_reg : 8; - u32 data2_reg : 8; - } tw_sm_c_104; - - struct { - u32 reserved2 :19; - u32 tlo1 : 5; - u32 reserved1 : 2; - u32 thi1 : 6; - } tw_sm_c_108; - - struct { - u32 reserved2 :19; - u32 tlo1 : 5; - u32 reserved1 : 2; - u32 thi1 : 6; - } tw_sm_c_10c; - - struct { - u32 reserved2 :19; - u32 tlo1 : 5; - u32 reserved1 : 2; - u32 thi1 : 6; - } tw_sm_c_110; - - struct { - u32 LNB_CTLPrescaler_sig : 2; - u32 LNB_CTLLowCount_sig :15; - u32 LNB_CTLHighCount_sig :15; - } lnb_switch_freq_200; - - struct { - u32 Rev_N_sig_reserved2 : 1; - u32 Rev_N_sig_caps : 1; - u32 Rev_N_sig_reserved1 : 2; - u32 Rev_N_sig_revision_hi : 4; - u32 reserved :20; - u32 Per_reset_sig : 1; - u32 LNB_L_H_sig : 1; - u32 ACPI3_sig : 1; - u32 ACPI1_sig : 1; - } misc_204; - - struct { - u32 unused : 9; - u32 Mailbox_from_V8_Enable_sig : 1; - u32 DMA2_Size_IRQ_Enable_sig : 1; - u32 DMA1_Size_IRQ_Enable_sig : 1; - u32 DMA2_Timer_Enable_sig : 1; - u32 DMA2_IRQ_Enable_sig : 1; - u32 DMA1_Timer_Enable_sig : 1; - u32 DMA1_IRQ_Enable_sig : 1; - u32 Rcv_Data_sig : 1; - u32 MAC_filter_Mode_sig : 1; - u32 Multi2_Enable_sig : 1; - u32 Per_CA_Enable_sig : 1; - u32 SMC_Enable_sig : 1; - u32 CA_Enable_sig : 1; - u32 WAN_CA_Enable_sig : 1; - u32 WAN_Enable_sig : 1; - u32 Mask_filter_sig : 1; - u32 Null_filter_sig : 1; - u32 ECM_filter_sig : 1; - u32 EMM_filter_sig : 1; - u32 PMT_filter_sig : 1; - u32 PCR_filter_sig : 1; - u32 Stream2_filter_sig : 1; - u32 Stream1_filter_sig : 1; - } ctrl_208; - - struct { - u32 reserved :21; - u32 Transport_Error : 1; - u32 LLC_SNAP_FLAG_set : 1; - u32 Continuity_error_flag : 1; - u32 Data_receiver_error : 1; - u32 Mailbox_from_V8_Status_sig : 1; - u32 DMA2_Size_IRQ_Status : 1; - u32 DMA1_Size_IRQ_Status : 1; - u32 DMA2_Timer_Status : 1; - u32 DMA2_IRQ_Status : 1; - u32 DMA1_Timer_Status : 1; - u32 DMA1_IRQ_Status : 1; - } irq_20c; - - struct { - u32 Special_controls :16; - u32 Block_reset_enable : 8; - u32 reset_block_700 : 1; - u32 reset_block_600 : 1; - u32 reset_block_500 : 1; - u32 reset_block_400 : 1; - u32 reset_block_300 : 1; - u32 reset_block_200 : 1; - u32 reset_block_100 : 1; - u32 reset_block_000 : 1; - } sw_reset_210; - - struct { - u32 unused2 :20; - u32 polarity_PS_ERR_sig : 1; - u32 polarity_PS_SYNC_sig : 1; - u32 polarity_PS_VALID_sig : 1; - u32 polarity_PS_CLK_sig : 1; - u32 unused1 : 3; - u32 s2p_sel_sig : 1; - u32 section_pkg_enable_sig : 1; - u32 halt_V8_sig : 1; - u32 v2WS_oe_sig : 1; - u32 vuart_oe_sig : 1; - } misc_214; - - struct { - u32 Mailbox_from_V8 :32; - } mbox_v8_to_host_218; - - struct { - u32 sysramaccess_busmuster : 1; - u32 sysramaccess_write : 1; - u32 unused : 7; - u32 sysramaccess_addr :15; - u32 sysramaccess_data : 8; - } mbox_host_to_v8_21c; - - struct { - u32 debug_fifo_problem : 1; - u32 debug_flag_write_status00 : 1; - u32 Stream2_trans : 1; - u32 Stream2_PID :13; - u32 debug_flag_pid_saved : 1; - u32 MAC_Multicast_filter : 1; - u32 Stream1_trans : 1; - u32 Stream1_PID :13; - } pid_filter_300; - - struct { - u32 reserved : 2; - u32 PMT_trans : 1; - u32 PMT_PID :13; - u32 debug_overrun2 : 1; - u32 debug_overrun3 : 1; - u32 PCR_trans : 1; - u32 PCR_PID :13; - } pid_filter_304; - - struct { - u32 reserved : 2; - u32 ECM_trans : 1; - u32 ECM_PID :13; - u32 EMM_filter_6 : 1; - u32 EMM_filter_4 : 1; - u32 EMM_trans : 1; - u32 EMM_PID :13; - } pid_filter_308; - - struct { - u32 unused2 : 3; - u32 Group_mask :13; - u32 unused1 : 2; - u32 Group_trans : 1; - u32 Group_PID :13; - } pid_filter_30c_ext_ind_0_7; - - struct { - u32 unused :15; - u32 net_master_read :17; - } pid_filter_30c_ext_ind_1; - - struct { - u32 unused :15; - u32 net_master_write :17; - } pid_filter_30c_ext_ind_2; - - struct { - u32 unused :15; - u32 next_net_master_write :17; - } pid_filter_30c_ext_ind_3; - - struct { - u32 reserved2 : 5; - u32 stack_read :10; - u32 reserved1 : 6; - u32 state_write :10; - u32 unused1 : 1; - } pid_filter_30c_ext_ind_4; - - struct { - u32 unused :22; - u32 stack_cnt :10; - } pid_filter_30c_ext_ind_5; - - struct { - u32 unused : 4; - u32 data_size_reg :12; - u32 write_status4 : 2; - u32 write_status1 : 2; - u32 pid_fsm_save_reg300 : 2; - u32 pid_fsm_save_reg4 : 2; - u32 pid_fsm_save_reg3 : 2; - u32 pid_fsm_save_reg2 : 2; - u32 pid_fsm_save_reg1 : 2; - u32 pid_fsm_save_reg0 : 2; - } pid_filter_30c_ext_ind_6; - - struct { - u32 unused :22; - u32 pass_alltables : 1; - u32 AB_select : 1; - u32 extra_index_reg : 3; - u32 index_reg : 5; - } index_reg_310; - - struct { - u32 reserved :17; - u32 PID_enable_bit : 1; - u32 PID_trans : 1; - u32 PID :13; - } pid_n_reg_314; - - struct { - u32 reserved : 6; - u32 HighAB_bit : 1; - u32 Enable_bit : 1; - u32 A6_byte : 8; - u32 A5_byte : 8; - u32 A4_byte : 8; - } mac_low_reg_318; - - struct { - u32 reserved : 8; - u32 A3_byte : 8; - u32 A2_byte : 8; - u32 A1_byte : 8; - } mac_high_reg_31c; - - struct { - u32 data_Tag_ID :16; - u32 reserved :16; - } data_tag_400; - - struct { - u32 Card_IDbyte3 : 8; - u32 Card_IDbyte4 : 8; - u32 Card_IDbyte5 : 8; - u32 Card_IDbyte6 : 8; - } card_id_408; - - struct { - u32 Card_IDbyte1 : 8; - u32 Card_IDbyte2 : 8; - } card_id_40c; - - struct { - u32 MAC6 : 8; - u32 MAC3 : 8; - u32 MAC2 : 8; - u32 MAC1 : 8; - } mac_address_418; - - struct { - u32 reserved :16; - u32 MAC8 : 8; - u32 MAC7 : 8; - } mac_address_41c; - - struct { - u32 reserved :21; - u32 txbuffempty : 1; - u32 ReceiveByteFrameError : 1; - u32 ReceiveDataReady : 1; - u32 transmitter_data_byte : 8; - } ci_600; - - struct { - u32 pi_component_reg : 3; - u32 pi_rw : 1; - u32 pi_ha :20; - u32 pi_d : 8; - } pi_604; - - struct { - u32 pi_busy_n : 1; - u32 pi_wait_n : 1; - u32 pi_timeout_status : 1; - u32 pi_CiMax_IRQ_n : 1; - u32 config_cclk : 1; - u32 config_cs_n : 1; - u32 config_wr_n : 1; - u32 config_Prog_n : 1; - u32 config_Init_stat : 1; - u32 config_Done_stat : 1; - u32 pcmcia_b_mod_pwr_n : 1; - u32 pcmcia_a_mod_pwr_n : 1; - u32 reserved : 3; - u32 Timer_addr : 5; - u32 unused : 1; - u32 timer_data : 7; - u32 Timer_Load_req : 1; - u32 Timer_Read_req : 1; - u32 oncecycle_read : 1; - u32 serialReset : 1; - } pi_608; - - struct { - u32 reserved : 6; - u32 rw_flag : 1; - u32 dvb_en : 1; - u32 key_array_row : 5; - u32 key_array_col : 3; - u32 key_code : 2; - u32 key_enable : 1; - u32 PID :13; - } dvb_reg_60c; - - struct { - u32 start_sram_ibi : 1; - u32 reserved2 : 1; - u32 ce_pin_reg : 1; - u32 oe_pin_reg : 1; - u32 reserved1 : 3; - u32 sc_xfer_bit : 1; - u32 sram_data : 8; - u32 sram_rw : 1; - u32 sram_addr :15; - } sram_ctrl_reg_700; - - struct { - u32 net_addr_write :16; - u32 net_addr_read :16; - } net_buf_reg_704; - - struct { - u32 cai_cnt : 4; - u32 reserved2 : 6; - u32 cai_write :11; - u32 reserved1 : 5; - u32 cai_read :11; - } cai_buf_reg_708; - - struct { - u32 cao_cnt : 4; - u32 reserved2 : 6; - u32 cap_write :11; - u32 reserved1 : 5; - u32 cao_read :11; - } cao_buf_reg_70c; - - struct { - u32 media_cnt : 4; - u32 reserved2 : 6; - u32 media_write :11; - u32 reserved1 : 5; - u32 media_read :11; - } media_buf_reg_710; - - struct { - u32 reserved :17; - u32 ctrl_maximumfill : 1; - u32 ctrl_sramdma : 1; - u32 ctrl_usb_wan : 1; - u32 cao_ovflow_error : 1; - u32 cai_ovflow_error : 1; - u32 media_ovflow_error : 1; - u32 net_ovflow_error : 1; - u32 MEDIA_Dest : 2; - u32 CAO_Dest : 2; - u32 CAI_Dest : 2; - u32 NET_Dest : 2; - } sram_dest_reg_714; - - struct { - u32 reserved3 :11; - u32 net_addr_write : 1; - u32 reserved2 : 3; - u32 net_addr_read : 1; - u32 reserved1 : 4; - u32 net_cnt :12; - } net_buf_reg_718; - - struct { - u32 reserved3 : 4; - u32 wan_pkt_frame : 4; - u32 reserved2 : 4; - u32 sram_memmap : 2; - u32 sram_chip : 2; - u32 wan_wait_state : 8; - u32 reserved1 : 6; - u32 wan_speed_sig : 2; - } wan_ctrl_reg_71c; -} flexcop_ibi_value; - -#endif diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h b/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h deleted file mode 100644 index c75830d7d942..000000000000 --- a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h +++ /dev/null @@ -1,455 +0,0 @@ -/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * register descriptions - * see flexcop.c for copyright information - */ -/* This file is automatically generated, do not edit things here. */ -#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ -#define __FLEXCOP_IBI_VALUE_INCLUDED__ - -typedef union { - u32 raw; - - struct { - u32 dma_0start : 1; - u32 dma_0No_update : 1; - u32 dma_address0 :30; - } dma_0x0; - - struct { - u32 DMA_maxpackets : 8; - u32 dma_addr_size :24; - } dma_0x4_remap; - - struct { - u32 dma1timer : 7; - u32 unused : 1; - u32 dma_addr_size :24; - } dma_0x4_read; - - struct { - u32 unused : 1; - u32 dmatimer : 7; - u32 dma_addr_size :24; - } dma_0x4_write; - - struct { - u32 unused : 2; - u32 dma_cur_addr :30; - } dma_0x8; - - struct { - u32 dma_1start : 1; - u32 remap_enable : 1; - u32 dma_address1 :30; - } dma_0xc; - - struct { - u32 chipaddr : 7; - u32 reserved1 : 1; - u32 baseaddr : 8; - u32 data1_reg : 8; - u32 working_start : 1; - u32 twoWS_rw : 1; - u32 total_bytes : 2; - u32 twoWS_port_reg : 2; - u32 no_base_addr_ack_error : 1; - u32 st_done : 1; - } tw_sm_c_100; - - struct { - u32 data2_reg : 8; - u32 data3_reg : 8; - u32 data4_reg : 8; - u32 exlicit_stops : 1; - u32 force_stop : 1; - u32 unused : 6; - } tw_sm_c_104; - - struct { - u32 thi1 : 6; - u32 reserved1 : 2; - u32 tlo1 : 5; - u32 reserved2 :19; - } tw_sm_c_108; - - struct { - u32 thi1 : 6; - u32 reserved1 : 2; - u32 tlo1 : 5; - u32 reserved2 :19; - } tw_sm_c_10c; - - struct { - u32 thi1 : 6; - u32 reserved1 : 2; - u32 tlo1 : 5; - u32 reserved2 :19; - } tw_sm_c_110; - - struct { - u32 LNB_CTLHighCount_sig :15; - u32 LNB_CTLLowCount_sig :15; - u32 LNB_CTLPrescaler_sig : 2; - } lnb_switch_freq_200; - - struct { - u32 ACPI1_sig : 1; - u32 ACPI3_sig : 1; - u32 LNB_L_H_sig : 1; - u32 Per_reset_sig : 1; - u32 reserved :20; - u32 Rev_N_sig_revision_hi : 4; - u32 Rev_N_sig_reserved1 : 2; - u32 Rev_N_sig_caps : 1; - u32 Rev_N_sig_reserved2 : 1; - } misc_204; - - struct { - u32 Stream1_filter_sig : 1; - u32 Stream2_filter_sig : 1; - u32 PCR_filter_sig : 1; - u32 PMT_filter_sig : 1; - u32 EMM_filter_sig : 1; - u32 ECM_filter_sig : 1; - u32 Null_filter_sig : 1; - u32 Mask_filter_sig : 1; - u32 WAN_Enable_sig : 1; - u32 WAN_CA_Enable_sig : 1; - u32 CA_Enable_sig : 1; - u32 SMC_Enable_sig : 1; - u32 Per_CA_Enable_sig : 1; - u32 Multi2_Enable_sig : 1; - u32 MAC_filter_Mode_sig : 1; - u32 Rcv_Data_sig : 1; - u32 DMA1_IRQ_Enable_sig : 1; - u32 DMA1_Timer_Enable_sig : 1; - u32 DMA2_IRQ_Enable_sig : 1; - u32 DMA2_Timer_Enable_sig : 1; - u32 DMA1_Size_IRQ_Enable_sig : 1; - u32 DMA2_Size_IRQ_Enable_sig : 1; - u32 Mailbox_from_V8_Enable_sig : 1; - u32 unused : 9; - } ctrl_208; - - struct { - u32 DMA1_IRQ_Status : 1; - u32 DMA1_Timer_Status : 1; - u32 DMA2_IRQ_Status : 1; - u32 DMA2_Timer_Status : 1; - u32 DMA1_Size_IRQ_Status : 1; - u32 DMA2_Size_IRQ_Status : 1; - u32 Mailbox_from_V8_Status_sig : 1; - u32 Data_receiver_error : 1; - u32 Continuity_error_flag : 1; - u32 LLC_SNAP_FLAG_set : 1; - u32 Transport_Error : 1; - u32 reserved :21; - } irq_20c; - - struct { - u32 reset_block_000 : 1; - u32 reset_block_100 : 1; - u32 reset_block_200 : 1; - u32 reset_block_300 : 1; - u32 reset_block_400 : 1; - u32 reset_block_500 : 1; - u32 reset_block_600 : 1; - u32 reset_block_700 : 1; - u32 Block_reset_enable : 8; - u32 Special_controls :16; - } sw_reset_210; - - struct { - u32 vuart_oe_sig : 1; - u32 v2WS_oe_sig : 1; - u32 halt_V8_sig : 1; - u32 section_pkg_enable_sig : 1; - u32 s2p_sel_sig : 1; - u32 unused1 : 3; - u32 polarity_PS_CLK_sig : 1; - u32 polarity_PS_VALID_sig : 1; - u32 polarity_PS_SYNC_sig : 1; - u32 polarity_PS_ERR_sig : 1; - u32 unused2 :20; - } misc_214; - - struct { - u32 Mailbox_from_V8 :32; - } mbox_v8_to_host_218; - - struct { - u32 sysramaccess_data : 8; - u32 sysramaccess_addr :15; - u32 unused : 7; - u32 sysramaccess_write : 1; - u32 sysramaccess_busmuster : 1; - } mbox_host_to_v8_21c; - - struct { - u32 Stream1_PID :13; - u32 Stream1_trans : 1; - u32 MAC_Multicast_filter : 1; - u32 debug_flag_pid_saved : 1; - u32 Stream2_PID :13; - u32 Stream2_trans : 1; - u32 debug_flag_write_status00 : 1; - u32 debug_fifo_problem : 1; - } pid_filter_300; - - struct { - u32 PCR_PID :13; - u32 PCR_trans : 1; - u32 debug_overrun3 : 1; - u32 debug_overrun2 : 1; - u32 PMT_PID :13; - u32 PMT_trans : 1; - u32 reserved : 2; - } pid_filter_304; - - struct { - u32 EMM_PID :13; - u32 EMM_trans : 1; - u32 EMM_filter_4 : 1; - u32 EMM_filter_6 : 1; - u32 ECM_PID :13; - u32 ECM_trans : 1; - u32 reserved : 2; - } pid_filter_308; - - struct { - u32 Group_PID :13; - u32 Group_trans : 1; - u32 unused1 : 2; - u32 Group_mask :13; - u32 unused2 : 3; - } pid_filter_30c_ext_ind_0_7; - - struct { - u32 net_master_read :17; - u32 unused :15; - } pid_filter_30c_ext_ind_1; - - struct { - u32 net_master_write :17; - u32 unused :15; - } pid_filter_30c_ext_ind_2; - - struct { - u32 next_net_master_write :17; - u32 unused :15; - } pid_filter_30c_ext_ind_3; - - struct { - u32 unused1 : 1; - u32 state_write :10; - u32 reserved1 : 6; - u32 stack_read :10; - u32 reserved2 : 5; - } pid_filter_30c_ext_ind_4; - - struct { - u32 stack_cnt :10; - u32 unused :22; - } pid_filter_30c_ext_ind_5; - - struct { - u32 pid_fsm_save_reg0 : 2; - u32 pid_fsm_save_reg1 : 2; - u32 pid_fsm_save_reg2 : 2; - u32 pid_fsm_save_reg3 : 2; - u32 pid_fsm_save_reg4 : 2; - u32 pid_fsm_save_reg300 : 2; - u32 write_status1 : 2; - u32 write_status4 : 2; - u32 data_size_reg :12; - u32 unused : 4; - } pid_filter_30c_ext_ind_6; - - struct { - u32 index_reg : 5; - u32 extra_index_reg : 3; - u32 AB_select : 1; - u32 pass_alltables : 1; - u32 unused :22; - } index_reg_310; - - struct { - u32 PID :13; - u32 PID_trans : 1; - u32 PID_enable_bit : 1; - u32 reserved :17; - } pid_n_reg_314; - - struct { - u32 A4_byte : 8; - u32 A5_byte : 8; - u32 A6_byte : 8; - u32 Enable_bit : 1; - u32 HighAB_bit : 1; - u32 reserved : 6; - } mac_low_reg_318; - - struct { - u32 A1_byte : 8; - u32 A2_byte : 8; - u32 A3_byte : 8; - u32 reserved : 8; - } mac_high_reg_31c; - - struct { - u32 reserved :16; - u32 data_Tag_ID :16; - } data_tag_400; - - struct { - u32 Card_IDbyte6 : 8; - u32 Card_IDbyte5 : 8; - u32 Card_IDbyte4 : 8; - u32 Card_IDbyte3 : 8; - } card_id_408; - - struct { - u32 Card_IDbyte2 : 8; - u32 Card_IDbyte1 : 8; - } card_id_40c; - - struct { - u32 MAC1 : 8; - u32 MAC2 : 8; - u32 MAC3 : 8; - u32 MAC6 : 8; - } mac_address_418; - - struct { - u32 MAC7 : 8; - u32 MAC8 : 8; - u32 reserved :16; - } mac_address_41c; - - struct { - u32 transmitter_data_byte : 8; - u32 ReceiveDataReady : 1; - u32 ReceiveByteFrameError : 1; - u32 txbuffempty : 1; - u32 reserved :21; - } ci_600; - - struct { - u32 pi_d : 8; - u32 pi_ha :20; - u32 pi_rw : 1; - u32 pi_component_reg : 3; - } pi_604; - - struct { - u32 serialReset : 1; - u32 oncecycle_read : 1; - u32 Timer_Read_req : 1; - u32 Timer_Load_req : 1; - u32 timer_data : 7; - u32 unused : 1; - u32 Timer_addr : 5; - u32 reserved : 3; - u32 pcmcia_a_mod_pwr_n : 1; - u32 pcmcia_b_mod_pwr_n : 1; - u32 config_Done_stat : 1; - u32 config_Init_stat : 1; - u32 config_Prog_n : 1; - u32 config_wr_n : 1; - u32 config_cs_n : 1; - u32 config_cclk : 1; - u32 pi_CiMax_IRQ_n : 1; - u32 pi_timeout_status : 1; - u32 pi_wait_n : 1; - u32 pi_busy_n : 1; - } pi_608; - - struct { - u32 PID :13; - u32 key_enable : 1; - u32 key_code : 2; - u32 key_array_col : 3; - u32 key_array_row : 5; - u32 dvb_en : 1; - u32 rw_flag : 1; - u32 reserved : 6; - } dvb_reg_60c; - - struct { - u32 sram_addr :15; - u32 sram_rw : 1; - u32 sram_data : 8; - u32 sc_xfer_bit : 1; - u32 reserved1 : 3; - u32 oe_pin_reg : 1; - u32 ce_pin_reg : 1; - u32 reserved2 : 1; - u32 start_sram_ibi : 1; - } sram_ctrl_reg_700; - - struct { - u32 net_addr_read :16; - u32 net_addr_write :16; - } net_buf_reg_704; - - struct { - u32 cai_read :11; - u32 reserved1 : 5; - u32 cai_write :11; - u32 reserved2 : 6; - u32 cai_cnt : 4; - } cai_buf_reg_708; - - struct { - u32 cao_read :11; - u32 reserved1 : 5; - u32 cap_write :11; - u32 reserved2 : 6; - u32 cao_cnt : 4; - } cao_buf_reg_70c; - - struct { - u32 media_read :11; - u32 reserved1 : 5; - u32 media_write :11; - u32 reserved2 : 6; - u32 media_cnt : 4; - } media_buf_reg_710; - - struct { - u32 NET_Dest : 2; - u32 CAI_Dest : 2; - u32 CAO_Dest : 2; - u32 MEDIA_Dest : 2; - u32 net_ovflow_error : 1; - u32 media_ovflow_error : 1; - u32 cai_ovflow_error : 1; - u32 cao_ovflow_error : 1; - u32 ctrl_usb_wan : 1; - u32 ctrl_sramdma : 1; - u32 ctrl_maximumfill : 1; - u32 reserved :17; - } sram_dest_reg_714; - - struct { - u32 net_cnt :12; - u32 reserved1 : 4; - u32 net_addr_read : 1; - u32 reserved2 : 3; - u32 net_addr_write : 1; - u32 reserved3 :11; - } net_buf_reg_718; - - struct { - u32 wan_speed_sig : 2; - u32 reserved1 : 6; - u32 wan_wait_state : 8; - u32 sram_chip : 2; - u32 sram_memmap : 2; - u32 reserved2 : 4; - u32 wan_pkt_frame : 4; - u32 reserved3 : 4; - } wan_ctrl_reg_71c; -} flexcop_ibi_value; - -#endif diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig deleted file mode 100644 index 8668e634c7ec..000000000000 --- a/drivers/media/dvb/bt8xx/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -config DVB_BT8XX - tristate "BT8xx based PCI cards" - depends on DVB_CORE && PCI && I2C && VIDEO_BT848 - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_SP887X if !DVB_FE_CUSTOMISE - select DVB_NXT6000 if !DVB_FE_CUSTOMISE - select DVB_CX24110 if !DVB_FE_CUSTOMISE - select DVB_OR51211 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - help - Support for PCI cards based on the Bt8xx PCI bridge. Examples are - the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, - the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and - some AVerMedia cards. - - Since these cards have no MPEG decoder onboard, they transmit - only compressed MPEG data over the PCI bus, so you need - an external software decoder to watch TV on your computer. - - Say Y if you own such a device and want to use it. diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile deleted file mode 100644 index 36591ae505f4..000000000000 --- a/drivers/media/dvb/bt8xx/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -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 diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c deleted file mode 100644 index b34fa95185e4..000000000000 --- a/drivers/media/dvb/bt8xx/bt878.c +++ /dev/null @@ -1,609 +0,0 @@ -/* - * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card - * - * Copyright (C) 2002 Peter Hettkamp - * - * large parts based on the bttv driver - * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de) - * & Marcus Metzler (mocm@metzlerbros.de) - * (c) 1999,2000 Gerd Knorr - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "bt878.h" -#include "dst_priv.h" - - -/**************************************/ -/* Miscellaneous utility definitions */ -/**************************************/ - -static unsigned int bt878_verbose = 1; -static unsigned int bt878_debug; - -module_param_named(verbose, bt878_verbose, int, 0444); -MODULE_PARM_DESC(verbose, - "verbose startup messages, default is 1 (yes)"); -module_param_named(debug, bt878_debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off)."); - -int bt878_num; -struct bt878 bt878[BT878_MAX]; - -EXPORT_SYMBOL(bt878_num); -EXPORT_SYMBOL(bt878); - -#define btwrite(dat,adr) bmtwrite((dat), (bt->bt878_mem+(adr))) -#define btread(adr) bmtread(bt->bt878_mem+(adr)) - -#define btand(dat,adr) btwrite((dat) & btread(adr), adr) -#define btor(dat,adr) btwrite((dat) | btread(adr), adr) -#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) - -#if defined(dprintk) -#undef dprintk -#endif -#define dprintk(fmt, arg...) \ - do { \ - if (bt878_debug) \ - printk(KERN_DEBUG fmt, ##arg); \ - } while (0) - -static void bt878_mem_free(struct bt878 *bt) -{ - if (bt->buf_cpu) { - pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu, - bt->buf_dma); - bt->buf_cpu = NULL; - } - - if (bt->risc_cpu) { - pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu, - bt->risc_dma); - bt->risc_cpu = NULL; - } -} - -static int bt878_mem_alloc(struct bt878 *bt) -{ - if (!bt->buf_cpu) { - bt->buf_size = 128 * 1024; - - bt->buf_cpu = - pci_alloc_consistent(bt->dev, bt->buf_size, - &bt->buf_dma); - - if (!bt->buf_cpu) - return -ENOMEM; - - memset(bt->buf_cpu, 0, bt->buf_size); - } - - if (!bt->risc_cpu) { - bt->risc_size = PAGE_SIZE; - bt->risc_cpu = - pci_alloc_consistent(bt->dev, bt->risc_size, - &bt->risc_dma); - - if (!bt->risc_cpu) { - bt878_mem_free(bt); - return -ENOMEM; - } - - memset(bt->risc_cpu, 0, bt->risc_size); - } - - return 0; -} - -/* RISC instructions */ -#define RISC_WRITE (0x01 << 28) -#define RISC_JUMP (0x07 << 28) -#define RISC_SYNC (0x08 << 28) - -/* RISC bits */ -#define RISC_WR_SOL (1 << 27) -#define RISC_WR_EOL (1 << 26) -#define RISC_IRQ (1 << 24) -#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16)) -#define RISC_SYNC_RESYNC (1 << 15) -#define RISC_SYNC_FM1 0x06 -#define RISC_SYNC_VRO 0x0C - -#define RISC_FLUSH() bt->risc_pos = 0 -#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr) - -static int bt878_make_risc(struct bt878 *bt) -{ - bt->block_bytes = bt->buf_size >> 4; - bt->block_count = 1 << 4; - bt->line_bytes = bt->block_bytes; - bt->line_count = bt->block_count; - - while (bt->line_bytes > 4095) { - bt->line_bytes >>= 1; - bt->line_count <<= 1; - } - - if (bt->line_count > 255) { - printk(KERN_ERR "bt878: buffer size error!\n"); - return -EINVAL; - } - return 0; -} - - -static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin) -{ - u32 buf_pos = 0; - u32 line; - - RISC_FLUSH(); - RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin); - RISC_INSTR(0); - - dprintk("bt878: risc len lines %u, bytes per line %u\n", - bt->line_count, bt->line_bytes); - for (line = 0; line < bt->line_count; line++) { - // At the beginning of every block we issue an IRQ with previous (finished) block number set - if (!(buf_pos % bt->block_bytes)) - RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | - RISC_IRQ | - RISC_STATUS(((buf_pos / - bt->block_bytes) + - (bt->block_count - - 1)) % - bt->block_count) | bt-> - line_bytes); - else - RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | - bt->line_bytes); - RISC_INSTR(bt->buf_dma + buf_pos); - buf_pos += bt->line_bytes; - } - - RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO); - RISC_INSTR(0); - - RISC_INSTR(RISC_JUMP); - RISC_INSTR(bt->risc_dma); - - btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN); -} - -/*****************************/ -/* Start/Stop grabbing funcs */ -/*****************************/ - -void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, - u32 irq_err_ignore) -{ - u32 int_mask; - - dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg); - /* complete the writing of the risc dma program now we have - * the card specifics - */ - bt878_risc_program(bt, op_sync_orin); - controlreg &= ~0x1f; - controlreg |= 0x1b; - - btwrite(bt->risc_dma, BT878_ARISC_START); - - /* original int mask had : - * 6 2 8 4 0 - * 1111 1111 1000 0000 0000 - * SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI - * Hacked for DST to: - * SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI - */ - int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT | - BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT | - BT878_AFBUS | BT878_ARISCI; - - - /* ignore pesky bits */ - int_mask &= ~irq_err_ignore; - - btwrite(int_mask, BT878_AINT_MASK); - btwrite(controlreg, BT878_AGPIO_DMA_CTL); -} - -void bt878_stop(struct bt878 *bt) -{ - u32 stat; - int i = 0; - - dprintk("bt878 debug: bt878_stop\n"); - - btwrite(0, BT878_AINT_MASK); - btand(~0x13, BT878_AGPIO_DMA_CTL); - - do { - stat = btread(BT878_AINT_STAT); - if (!(stat & BT878_ARISC_EN)) - break; - i++; - } while (i < 500); - - dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n", - bt->nr, i, stat); -} - -EXPORT_SYMBOL(bt878_start); -EXPORT_SYMBOL(bt878_stop); - -/*****************************/ -/* Interrupt service routine */ -/*****************************/ - -static irqreturn_t bt878_irq(int irq, void *dev_id) -{ - u32 stat, astat, mask; - int count; - struct bt878 *bt; - - bt = (struct bt878 *) dev_id; - - count = 0; - while (1) { - stat = btread(BT878_AINT_STAT); - mask = btread(BT878_AINT_MASK); - if (!(astat = (stat & mask))) - return IRQ_NONE; /* this interrupt is not for me */ -/* dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */ - btwrite(astat, BT878_AINT_STAT); /* try to clear interrupt condition */ - - - if (astat & (BT878_ASCERR | BT878_AOCERR)) { - if (bt878_verbose) { - printk(KERN_INFO - "bt878(%d): irq%s%s risc_pc=%08x\n", - bt->nr, - (astat & BT878_ASCERR) ? " SCERR" : - "", - (astat & BT878_AOCERR) ? " OCERR" : - "", btread(BT878_ARISC_PC)); - } - } - if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) { - if (bt878_verbose) { - printk(KERN_INFO - "bt878(%d): irq%s%s%s risc_pc=%08x\n", - bt->nr, - (astat & BT878_APABORT) ? " PABORT" : - "", - (astat & BT878_ARIPERR) ? " RIPERR" : - "", - (astat & BT878_APPERR) ? " PPERR" : - "", btread(BT878_ARISC_PC)); - } - } - if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) { - if (bt878_verbose) { - printk(KERN_INFO - "bt878(%d): irq%s%s%s risc_pc=%08x\n", - bt->nr, - (astat & BT878_AFDSR) ? " FDSR" : "", - (astat & BT878_AFTRGT) ? " FTRGT" : - "", - (astat & BT878_AFBUS) ? " FBUS" : "", - btread(BT878_ARISC_PC)); - } - } - if (astat & BT878_ARISCI) { - bt->finished_block = (stat & BT878_ARISCS) >> 28; - tasklet_schedule(&bt->tasklet); - break; - } - count++; - if (count > 20) { - btwrite(0, BT878_AINT_MASK); - printk(KERN_ERR - "bt878(%d): IRQ lockup, cleared int mask\n", - bt->nr); - break; - } - } - return IRQ_HANDLED; -} - -int -bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp) -{ - int retval; - - retval = 0; - if (mutex_lock_interruptible(&bt->gpio_lock)) - return -ERESTARTSYS; - /* special gpio signal */ - switch (cmd) { - case DST_IG_ENABLE: - // dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable); - retval = bttv_gpio_enable(bt->bttv_nr, - mp->enb.mask, - mp->enb.enable); - break; - case DST_IG_WRITE: - // dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals); - retval = bttv_write_gpio(bt->bttv_nr, - mp->outp.mask, - mp->outp.highvals); - - break; - case DST_IG_READ: - /* read */ - retval = bttv_read_gpio(bt->bttv_nr, &mp->rd.value); - // dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value); - break; - case DST_IG_TS: - /* Set packet size */ - bt->TS_Size = mp->psize; - break; - - default: - retval = -EINVAL; - break; - } - mutex_unlock(&bt->gpio_lock); - return retval; -} - -EXPORT_SYMBOL(bt878_device_control); - -#define BROOKTREE_878_DEVICE(vend, dev, name) \ - { \ - .vendor = PCI_VENDOR_ID_BROOKTREE, \ - .device = PCI_DEVICE_ID_BROOKTREE_878, \ - .subvendor = (vend), .subdevice = (dev), \ - .driver_data = (unsigned long) name \ - } - -static struct pci_device_id bt878_pci_tbl[] __devinitdata = { - BROOKTREE_878_DEVICE(0x0071, 0x0101, "Nebula Electronics DigiTV"), - BROOKTREE_878_DEVICE(0x1461, 0x0761, "AverMedia AverTV DVB-T 761"), - BROOKTREE_878_DEVICE(0x11bd, 0x001c, "Pinnacle PCTV Sat"), - BROOKTREE_878_DEVICE(0x11bd, 0x0026, "Pinnacle PCTV SAT CI"), - BROOKTREE_878_DEVICE(0x1822, 0x0001, "Twinhan VisionPlus DVB"), - BROOKTREE_878_DEVICE(0x270f, 0xfc00, - "ChainTech digitop DST-1000 DVB-S"), - BROOKTREE_878_DEVICE(0x1461, 0x0771, "AVermedia AverTV DVB-T 771"), - BROOKTREE_878_DEVICE(0x18ac, 0xdb10, "DViCO FusionHDTV DVB-T Lite"), - BROOKTREE_878_DEVICE(0x18ac, 0xdb11, "Ultraview DVB-T Lite"), - BROOKTREE_878_DEVICE(0x18ac, 0xd500, "DViCO FusionHDTV 5 Lite"), - BROOKTREE_878_DEVICE(0x7063, 0x2000, "pcHDTV HD-2000 TV"), - BROOKTREE_878_DEVICE(0x1822, 0x0026, "DNTV Live! Mini"), - { } -}; - -MODULE_DEVICE_TABLE(pci, bt878_pci_tbl); - -static const char * __devinit card_name(const struct pci_device_id *id) -{ - return id->driver_data ? (const char *)id->driver_data : "Unknown"; -} - -/***********************/ -/* PCI device handling */ -/***********************/ - -static int __devinit bt878_probe(struct pci_dev *dev, - const struct pci_device_id *pci_id) -{ - int result = 0; - unsigned char lat; - struct bt878 *bt; -#if defined(__powerpc__) - unsigned int cmd; -#endif - unsigned int cardid; - - printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n", - bt878_num); - if (bt878_num >= BT878_MAX) { - printk(KERN_ERR "bt878: Too many devices inserted\n"); - result = -ENOMEM; - goto fail0; - } - if (pci_enable_device(dev)) - return -EIO; - - cardid = dev->subsystem_device << 16; - cardid |= dev->subsystem_vendor; - - printk(KERN_INFO "%s: card id=[0x%x],[ %s ] has DVB functions.\n", - __func__, cardid, card_name(pci_id)); - - bt = &bt878[bt878_num]; - bt->dev = dev; - bt->nr = bt878_num; - bt->shutdown = 0; - - bt->id = dev->device; - bt->irq = dev->irq; - bt->bt878_adr = pci_resource_start(dev, 0); - if (!request_mem_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0), "bt878")) { - result = -EBUSY; - goto fail0; - } - - bt->revision = dev->revision; - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); - - - printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ", - bt878_num, bt->id, bt->revision, dev->bus->number, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - printk("irq: %d, latency: %d, memory: 0x%lx\n", - bt->irq, lat, bt->bt878_adr); - - -#if defined(__powerpc__) - /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ - /* response on cards with no firmware is not enabled by OF */ - pci_read_config_dword(dev, PCI_COMMAND, &cmd); - cmd = (cmd | PCI_COMMAND_MEMORY); - pci_write_config_dword(dev, PCI_COMMAND, cmd); -#endif - -#ifdef __sparc__ - bt->bt878_mem = (unsigned char *) bt->bt878_adr; -#else - bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000); -#endif - - /* clear interrupt mask */ - btwrite(0, BT848_INT_MASK); - - result = request_irq(bt->irq, bt878_irq, - IRQF_SHARED | IRQF_DISABLED, "bt878", - (void *) bt); - if (result == -EINVAL) { - printk(KERN_ERR "bt878(%d): Bad irq number or handler\n", - bt878_num); - goto fail1; - } - if (result == -EBUSY) { - printk(KERN_ERR - "bt878(%d): IRQ %d busy, change your PnP config in BIOS\n", - bt878_num, bt->irq); - goto fail1; - } - if (result < 0) - goto fail1; - - pci_set_master(dev); - pci_set_drvdata(dev, bt); - - if ((result = bt878_mem_alloc(bt))) { - printk(KERN_ERR "bt878: failed to allocate memory!\n"); - goto fail2; - } - - bt878_make_risc(bt); - btwrite(0, BT878_AINT_MASK); - bt878_num++; - - return 0; - - fail2: - free_irq(bt->irq, bt); - fail1: - release_mem_region(pci_resource_start(bt->dev, 0), - pci_resource_len(bt->dev, 0)); - fail0: - pci_disable_device(dev); - return result; -} - -static void __devexit bt878_remove(struct pci_dev *pci_dev) -{ - u8 command; - struct bt878 *bt = pci_get_drvdata(pci_dev); - - if (bt878_verbose) - printk(KERN_INFO "bt878(%d): unloading\n", bt->nr); - - /* turn off all capturing, DMA and IRQs */ - btand(~0x13, BT878_AGPIO_DMA_CTL); - - /* first disable interrupts before unmapping the memory! */ - btwrite(0, BT878_AINT_MASK); - btwrite(~0U, BT878_AINT_STAT); - - /* disable PCI bus-mastering */ - pci_read_config_byte(bt->dev, PCI_COMMAND, &command); - /* Should this be &=~ ?? */ - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte(bt->dev, PCI_COMMAND, command); - - free_irq(bt->irq, bt); - printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem); - if (bt->bt878_mem) - iounmap(bt->bt878_mem); - - release_mem_region(pci_resource_start(bt->dev, 0), - pci_resource_len(bt->dev, 0)); - /* wake up any waiting processes - because shutdown flag is set, no new processes (in this queue) - are expected - */ - bt->shutdown = 1; - bt878_mem_free(bt); - - pci_set_drvdata(pci_dev, NULL); - pci_disable_device(pci_dev); - return; -} - -static struct pci_driver bt878_pci_driver = { - .name = "bt878", - .id_table = bt878_pci_tbl, - .probe = bt878_probe, - .remove = __devexit_p(bt878_remove), -}; - -/*******************************/ -/* Module management functions */ -/*******************************/ - -static int __init bt878_init_module(void) -{ - bt878_num = 0; - - printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n", - (BT878_VERSION_CODE >> 16) & 0xff, - (BT878_VERSION_CODE >> 8) & 0xff, - BT878_VERSION_CODE & 0xff); - - return pci_register_driver(&bt878_pci_driver); -} - -static void __exit bt878_cleanup_module(void) -{ - pci_unregister_driver(&bt878_pci_driver); -} - -module_init(bt878_init_module); -module_exit(bt878_cleanup_module); - -MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb/bt8xx/bt878.h b/drivers/media/dvb/bt8xx/bt878.h deleted file mode 100644 index d19b59299d78..000000000000 --- a/drivers/media/dvb/bt8xx/bt878.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - bt878.h - Bt878 audio module (register offsets) - - Copyright (C) 2002 Peter Hettkamp - - This program is free software; 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 _BT878_H_ -#define _BT878_H_ - -#include -#include -#include -#include -#include - -#include "bt848.h" -#include "bttv.h" - -#define BT878_VERSION_CODE 0x000000 - -#define BT878_AINT_STAT 0x100 -#define BT878_ARISCS (0xf<<28) -#define BT878_ARISC_EN (1<<27) -#define BT878_ASCERR (1<<19) -#define BT878_AOCERR (1<<18) -#define BT878_APABORT (1<<17) -#define BT878_ARIPERR (1<<16) -#define BT878_APPERR (1<<15) -#define BT878_AFDSR (1<<14) -#define BT878_AFTRGT (1<<13) -#define BT878_AFBUS (1<<12) -#define BT878_ARISCI (1<<11) -#define BT878_AOFLOW (1<<3) - -#define BT878_AINT_MASK 0x104 - -#define BT878_AGPIO_DMA_CTL 0x10c -#define BT878_A_GAIN (0xf<<28) -#define BT878_A_G2X (1<<27) -#define BT878_A_PWRDN (1<<26) -#define BT878_A_SEL (3<<24) -#define BT878_DA_SCE (1<<23) -#define BT878_DA_LRI (1<<22) -#define BT878_DA_MLB (1<<21) -#define BT878_DA_LRD (0x1f<<16) -#define BT878_DA_DPM (1<<15) -#define BT878_DA_SBR (1<<14) -#define BT878_DA_ES2 (1<<13) -#define BT878_DA_LMT (1<<12) -#define BT878_DA_SDR (0xf<<8) -#define BT878_DA_IOM (3<<6) -#define BT878_DA_APP (1<<5) -#define BT878_ACAP_EN (1<<4) -#define BT878_PKTP (3<<2) -#define BT878_RISC_EN (1<<1) -#define BT878_FIFO_EN 1 - -#define BT878_APACK_LEN 0x110 -#define BT878_AFP_LEN (0xff<<16) -#define BT878_ALP_LEN 0xfff - -#define BT878_ARISC_START 0x114 - -#define BT878_ARISC_PC 0x120 - -/* BT878 FUNCTION 0 REGISTERS */ -#define BT878_GPIO_DMA_CTL 0x10c - -/* Interrupt register */ -#define BT878_INT_STAT 0x100 -#define BT878_INT_MASK 0x104 -#define BT878_I2CRACK (1<<25) -#define BT878_I2CDONE (1<<8) - -#define BT878_MAX 4 - -#define BT878_RISC_SYNC_MASK (1 << 15) - - -#define BTTV_BOARD_UNKNOWN 0x00 -#define BTTV_BOARD_PINNACLESAT 0x5e -#define BTTV_BOARD_NEBULA_DIGITV 0x68 -#define BTTV_BOARD_PC_HDTV 0x70 -#define BTTV_BOARD_TWINHAN_DST 0x71 -#define BTTV_BOARD_AVDVBT_771 0x7b -#define BTTV_BOARD_AVDVBT_761 0x7c -#define BTTV_BOARD_DVICO_DVBT_LITE 0x80 -#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87 - -extern int bt878_num; - -struct bt878 { - struct mutex gpio_lock; - unsigned int nr; - unsigned int bttv_nr; - struct i2c_adapter *adapter; - struct pci_dev *dev; - unsigned int id; - unsigned int TS_Size; - unsigned char revision; - unsigned int irq; - unsigned long bt878_adr; - volatile void __iomem *bt878_mem; /* function 1 */ - - volatile u32 finished_block; - volatile u32 last_block; - u32 block_count; - u32 block_bytes; - u32 line_bytes; - u32 line_count; - - u32 buf_size; - u8 *buf_cpu; - dma_addr_t buf_dma; - - u32 risc_size; - __le32 *risc_cpu; - dma_addr_t risc_dma; - u32 risc_pos; - - struct tasklet_struct tasklet; - int shutdown; -}; - -extern struct bt878 bt878[BT878_MAX]; - -void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, - u32 irq_err_ignore); -void bt878_stop(struct bt878 *bt); - -#if defined(__powerpc__) /* big-endian */ -static inline void io_st_le32(volatile unsigned __iomem *addr, unsigned val) -{ - st_le32(addr, val); - eieio(); -} - -#define bmtwrite(dat,adr) io_st_le32((adr),(dat)) -#define bmtread(adr) ld_le32((adr)) -#else -#define bmtwrite(dat,adr) writel((dat), (adr)) -#define bmtread(adr) readl(adr) -#endif - -#endif diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c deleted file mode 100644 index 430b3eb11815..000000000000 --- a/drivers/media/dvb/bt8xx/dst.c +++ /dev/null @@ -1,1873 +0,0 @@ -/* - Frontend/Card driver for TwinHan DST Frontend - Copyright (C) 2003 Jamie Honan - Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "dvb_frontend.h" -#include "dst_priv.h" -#include "dst_common.h" - -static unsigned int verbose = 1; -module_param(verbose, int, 0644); -MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); - -static unsigned int dst_addons; -module_param(dst_addons, int, 0644); -MODULE_PARM_DESC(dst_addons, "CA daughterboard, default is 0 (No addons)"); - -static unsigned int dst_algo; -module_param(dst_algo, int, 0644); -MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)"); - -#define HAS_LOCK 1 -#define ATTEMPT_TUNE 2 -#define HAS_POWER 4 - -#define DST_ERROR 0 -#define DST_NOTICE 1 -#define DST_INFO 2 -#define DST_DEBUG 3 - -#define dprintk(x, y, z, format, arg...) do { \ - if (z) { \ - if ((x > DST_ERROR) && (x > y)) \ - printk(KERN_ERR "dst(%d) %s: " format "\n", \ - state->bt->nr, __func__ , ##arg); \ - else if ((x > DST_NOTICE) && (x > y)) \ - printk(KERN_NOTICE "dst(%d) %s: " format "\n", \ - state->bt->nr, __func__ , ##arg); \ - else if ((x > DST_INFO) && (x > y)) \ - printk(KERN_INFO "dst(%d) %s: " format "\n", \ - state->bt->nr, __func__ , ##arg); \ - else if ((x > DST_DEBUG) && (x > y)) \ - printk(KERN_DEBUG "dst(%d) %s: " format "\n", \ - state->bt->nr, __func__ , ##arg); \ - } else { \ - if (x > y) \ - printk(format, ##arg); \ - } \ -} while(0) - -static int dst_command(struct dst_state *state, u8 *data, u8 len); - -static void dst_packsize(struct dst_state *state, int psize) -{ - union dst_gpio_packet bits; - - bits.psize = psize; - bt878_device_control(state->bt, DST_IG_TS, &bits); -} - -static int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, - u32 outhigh, int delay) -{ - union dst_gpio_packet enb; - union dst_gpio_packet bits; - int err; - - enb.enb.mask = mask; - enb.enb.enable = enbb; - - dprintk(verbose, DST_INFO, 1, "mask=[%04x], enbb=[%04x], outhigh=[%04x]", mask, enbb, outhigh); - if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) { - dprintk(verbose, DST_INFO, 1, "dst_gpio_enb error (err == %i, mask == %02x, enb == %02x)", err, mask, enbb); - return -EREMOTEIO; - } - udelay(1000); - /* because complete disabling means no output, no need to do output packet */ - if (enbb == 0) - return 0; - if (delay) - msleep(10); - bits.outp.mask = enbb; - bits.outp.highvals = outhigh; - if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) { - dprintk(verbose, DST_INFO, 1, "dst_gpio_outb error (err == %i, enbb == %02x, outhigh == %02x)", err, enbb, outhigh); - return -EREMOTEIO; - } - - return 0; -} - -static int dst_gpio_inb(struct dst_state *state, u8 *result) -{ - union dst_gpio_packet rd_packet; - int err; - - *result = 0; - if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb error (err == %i)", err); - return -EREMOTEIO; - } - *result = (u8) rd_packet.rd.value; - - return 0; -} - -int rdc_reset_state(struct dst_state *state) -{ - dprintk(verbose, DST_INFO, 1, "Resetting state machine"); - if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, 0, NO_DELAY) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); - return -1; - } - msleep(10); - if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, RDC_8820_INT, NO_DELAY) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); - msleep(10); - return -1; - } - - return 0; -} -EXPORT_SYMBOL(rdc_reset_state); - -static int rdc_8820_reset(struct dst_state *state) -{ - dprintk(verbose, DST_DEBUG, 1, "Resetting DST"); - if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, 0, NO_DELAY) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); - return -1; - } - udelay(1000); - if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, RDC_8820_RESET, DELAY) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); - return -1; - } - - return 0; -} - -static int dst_pio_enable(struct dst_state *state) -{ - if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_ENABLE, 0, NO_DELAY) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); - return -1; - } - udelay(1000); - - return 0; -} - -int dst_pio_disable(struct dst_state *state) -{ - if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_DISABLE, RDC_8820_PIO_0_DISABLE, NO_DELAY) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); - return -1; - } - if (state->type_flags & DST_TYPE_HAS_FW_1) - udelay(1000); - - return 0; -} -EXPORT_SYMBOL(dst_pio_disable); - -int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode) -{ - u8 reply; - int i; - - for (i = 0; i < 200; i++) { - if (dst_gpio_inb(state, &reply) < 0) { - dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb ERROR !"); - return -1; - } - if ((reply & RDC_8820_PIO_0_ENABLE) == 0) { - dprintk(verbose, DST_INFO, 1, "dst wait ready after %d", i); - return 1; - } - msleep(10); - } - dprintk(verbose, DST_NOTICE, 1, "dst wait NOT ready after %d", i); - - return 0; -} -EXPORT_SYMBOL(dst_wait_dst_ready); - -int dst_error_recovery(struct dst_state *state) -{ - dprintk(verbose, DST_NOTICE, 1, "Trying to return from previous errors."); - dst_pio_disable(state); - msleep(10); - dst_pio_enable(state); - msleep(10); - - return 0; -} -EXPORT_SYMBOL(dst_error_recovery); - -int dst_error_bailout(struct dst_state *state) -{ - dprintk(verbose, DST_INFO, 1, "Trying to bailout from previous error."); - rdc_8820_reset(state); - dst_pio_disable(state); - msleep(10); - - return 0; -} -EXPORT_SYMBOL(dst_error_bailout); - -int dst_comm_init(struct dst_state *state) -{ - dprintk(verbose, DST_INFO, 1, "Initializing DST."); - if ((dst_pio_enable(state)) < 0) { - dprintk(verbose, DST_ERROR, 1, "PIO Enable Failed"); - return -1; - } - if ((rdc_reset_state(state)) < 0) { - dprintk(verbose, DST_ERROR, 1, "RDC 8820 State RESET Failed."); - return -1; - } - if (state->type_flags & DST_TYPE_HAS_FW_1) - msleep(100); - else - msleep(5); - - return 0; -} -EXPORT_SYMBOL(dst_comm_init); - -int write_dst(struct dst_state *state, u8 *data, u8 len) -{ - struct i2c_msg msg = { - .addr = state->config->demod_address, - .flags = 0, - .buf = data, - .len = len - }; - - int err; - u8 cnt, i; - - dprintk(verbose, DST_NOTICE, 0, "writing [ "); - for (i = 0; i < len; i++) - dprintk(verbose, DST_NOTICE, 0, "%02x ", data[i]); - dprintk(verbose, DST_NOTICE, 0, "]\n"); - - for (cnt = 0; cnt < 2; cnt++) { - if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { - dprintk(verbose, DST_INFO, 1, "_write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, data[0]); - dst_error_recovery(state); - continue; - } else - break; - } - if (cnt >= 2) { - dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); - dst_error_bailout(state); - - return -1; - } - - return 0; -} -EXPORT_SYMBOL(write_dst); - -int read_dst(struct dst_state *state, u8 *ret, u8 len) -{ - struct i2c_msg msg = { - .addr = state->config->demod_address, - .flags = I2C_M_RD, - .buf = ret, - .len = len - }; - - int err; - int cnt; - - for (cnt = 0; cnt < 2; cnt++) { - if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { - dprintk(verbose, DST_INFO, 1, "read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, ret[0]); - dst_error_recovery(state); - continue; - } else - break; - } - if (cnt >= 2) { - dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); - dst_error_bailout(state); - - return -1; - } - dprintk(verbose, DST_DEBUG, 1, "reply is 0x%x", ret[0]); - for (err = 1; err < len; err++) - dprintk(verbose, DST_DEBUG, 0, " 0x%x", ret[err]); - if (err > 1) - dprintk(verbose, DST_DEBUG, 0, "\n"); - - return 0; -} -EXPORT_SYMBOL(read_dst); - -static int dst_set_polarization(struct dst_state *state) -{ - switch (state->voltage) { - case SEC_VOLTAGE_13: /* Vertical */ - dprintk(verbose, DST_INFO, 1, "Polarization=[Vertical]"); - state->tx_tuna[8] &= ~0x40; - break; - case SEC_VOLTAGE_18: /* Horizontal */ - dprintk(verbose, DST_INFO, 1, "Polarization=[Horizontal]"); - state->tx_tuna[8] |= 0x40; - break; - case SEC_VOLTAGE_OFF: - break; - } - - return 0; -} - -static int dst_set_freq(struct dst_state *state, u32 freq) -{ - state->frequency = freq; - dprintk(verbose, DST_INFO, 1, "set Frequency %u", freq); - - if (state->dst_type == DST_TYPE_IS_SAT) { - freq = freq / 1000; - if (freq < 950 || freq > 2150) - return -EINVAL; - state->tx_tuna[2] = (freq >> 8); - state->tx_tuna[3] = (u8) freq; - state->tx_tuna[4] = 0x01; - state->tx_tuna[8] &= ~0x04; - if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { - if (freq < 1531) - state->tx_tuna[8] |= 0x04; - } - } else if (state->dst_type == DST_TYPE_IS_TERR) { - freq = freq / 1000; - if (freq < 137000 || freq > 858000) - return -EINVAL; - state->tx_tuna[2] = (freq >> 16) & 0xff; - state->tx_tuna[3] = (freq >> 8) & 0xff; - state->tx_tuna[4] = (u8) freq; - } else if (state->dst_type == DST_TYPE_IS_CABLE) { - freq = freq / 1000; - state->tx_tuna[2] = (freq >> 16) & 0xff; - state->tx_tuna[3] = (freq >> 8) & 0xff; - state->tx_tuna[4] = (u8) freq; - } else if (state->dst_type == DST_TYPE_IS_ATSC) { - freq = freq / 1000; - if (freq < 51000 || freq > 858000) - return -EINVAL; - state->tx_tuna[2] = (freq >> 16) & 0xff; - state->tx_tuna[3] = (freq >> 8) & 0xff; - state->tx_tuna[4] = (u8) freq; - state->tx_tuna[5] = 0x00; /* ATSC */ - state->tx_tuna[6] = 0x00; - if (state->dst_hw_cap & DST_TYPE_HAS_ANALOG) - state->tx_tuna[7] = 0x00; /* Digital */ - } else - return -EINVAL; - - return 0; -} - -static int dst_set_bandwidth(struct dst_state *state, u32 bandwidth) -{ - state->bandwidth = bandwidth; - - if (state->dst_type != DST_TYPE_IS_TERR) - return -EOPNOTSUPP; - - switch (bandwidth) { - case 6000000: - if (state->dst_hw_cap & DST_TYPE_HAS_CA) - state->tx_tuna[7] = 0x06; - else { - state->tx_tuna[6] = 0x06; - state->tx_tuna[7] = 0x00; - } - break; - case 7000000: - if (state->dst_hw_cap & DST_TYPE_HAS_CA) - state->tx_tuna[7] = 0x07; - else { - state->tx_tuna[6] = 0x07; - state->tx_tuna[7] = 0x00; - } - break; - case 8000000: - if (state->dst_hw_cap & DST_TYPE_HAS_CA) - state->tx_tuna[7] = 0x08; - else { - state->tx_tuna[6] = 0x08; - state->tx_tuna[7] = 0x00; - } - break; - default: - return -EINVAL; - } - - return 0; -} - -static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t inversion) -{ - state->inversion = inversion; - switch (inversion) { - case INVERSION_OFF: /* Inversion = Normal */ - state->tx_tuna[8] &= ~0x80; - break; - case INVERSION_ON: - state->tx_tuna[8] |= 0x80; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int dst_set_fec(struct dst_state *state, fe_code_rate_t fec) -{ - state->fec = fec; - return 0; -} - -static fe_code_rate_t dst_get_fec(struct dst_state *state) -{ - return state->fec; -} - -static int dst_set_symbolrate(struct dst_state *state, u32 srate) -{ - u32 symcalc; - u64 sval; - - state->symbol_rate = srate; - if (state->dst_type == DST_TYPE_IS_TERR) { - return -EOPNOTSUPP; - } - dprintk(verbose, DST_INFO, 1, "set symrate %u", srate); - srate /= 1000; - if (state->dst_type == DST_TYPE_IS_SAT) { - if (state->type_flags & DST_TYPE_HAS_SYMDIV) { - sval = srate; - sval <<= 20; - do_div(sval, 88000); - symcalc = (u32) sval; - dprintk(verbose, DST_INFO, 1, "set symcalc %u", symcalc); - state->tx_tuna[5] = (u8) (symcalc >> 12); - state->tx_tuna[6] = (u8) (symcalc >> 4); - state->tx_tuna[7] = (u8) (symcalc << 4); - } else { - state->tx_tuna[5] = (u8) (srate >> 16) & 0x7f; - state->tx_tuna[6] = (u8) (srate >> 8); - state->tx_tuna[7] = (u8) srate; - } - state->tx_tuna[8] &= ~0x20; - if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { - if (srate > 8000) - state->tx_tuna[8] |= 0x20; - } - } else if (state->dst_type == DST_TYPE_IS_CABLE) { - dprintk(verbose, DST_DEBUG, 1, "%s", state->fw_name); - if (!strncmp(state->fw_name, "DCTNEW", 6)) { - state->tx_tuna[5] = (u8) (srate >> 8); - state->tx_tuna[6] = (u8) srate; - state->tx_tuna[7] = 0x00; - } else if (!strncmp(state->fw_name, "DCT-CI", 6)) { - state->tx_tuna[5] = 0x00; - state->tx_tuna[6] = (u8) (srate >> 8); - state->tx_tuna[7] = (u8) srate; - } - } - return 0; -} - -static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulation) -{ - if (state->dst_type != DST_TYPE_IS_CABLE) - return -EOPNOTSUPP; - - state->modulation = modulation; - switch (modulation) { - case QAM_16: - state->tx_tuna[8] = 0x10; - break; - case QAM_32: - state->tx_tuna[8] = 0x20; - break; - case QAM_64: - state->tx_tuna[8] = 0x40; - break; - case QAM_128: - state->tx_tuna[8] = 0x80; - break; - case QAM_256: - if (!strncmp(state->fw_name, "DCTNEW", 6)) - state->tx_tuna[8] = 0xff; - else if (!strncmp(state->fw_name, "DCT-CI", 6)) - state->tx_tuna[8] = 0x00; - break; - case QPSK: - case QAM_AUTO: - case VSB_8: - case VSB_16: - default: - return -EINVAL; - - } - - return 0; -} - -static fe_modulation_t dst_get_modulation(struct dst_state *state) -{ - return state->modulation; -} - - -u8 dst_check_sum(u8 *buf, u32 len) -{ - u32 i; - u8 val = 0; - if (!len) - return 0; - for (i = 0; i < len; i++) { - val += buf[i]; - } - return ((~val) + 1); -} -EXPORT_SYMBOL(dst_check_sum); - -static void dst_type_flags_print(struct dst_state *state) -{ - u32 type_flags = state->type_flags; - - dprintk(verbose, DST_ERROR, 0, "DST type flags :"); - if (type_flags & DST_TYPE_HAS_TS188) - dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner", DST_TYPE_HAS_TS188); - if (type_flags & DST_TYPE_HAS_NEWTUNE_2) - dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner 2", DST_TYPE_HAS_NEWTUNE_2); - if (type_flags & DST_TYPE_HAS_TS204) - dprintk(verbose, DST_ERROR, 0, " 0x%x ts204", DST_TYPE_HAS_TS204); - if (type_flags & DST_TYPE_HAS_VLF) - dprintk(verbose, DST_ERROR, 0, " 0x%x VLF", DST_TYPE_HAS_VLF); - if (type_flags & DST_TYPE_HAS_SYMDIV) - dprintk(verbose, DST_ERROR, 0, " 0x%x symdiv", DST_TYPE_HAS_SYMDIV); - if (type_flags & DST_TYPE_HAS_FW_1) - dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 1", DST_TYPE_HAS_FW_1); - if (type_flags & DST_TYPE_HAS_FW_2) - dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 2", DST_TYPE_HAS_FW_2); - if (type_flags & DST_TYPE_HAS_FW_3) - dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 3", DST_TYPE_HAS_FW_3); - dprintk(verbose, DST_ERROR, 0, "\n"); -} - - -static int dst_type_print(struct dst_state *state, u8 type) -{ - char *otype; - switch (type) { - case DST_TYPE_IS_SAT: - otype = "satellite"; - break; - - case DST_TYPE_IS_TERR: - otype = "terrestrial"; - break; - - case DST_TYPE_IS_CABLE: - otype = "cable"; - break; - - case DST_TYPE_IS_ATSC: - otype = "atsc"; - break; - - default: - dprintk(verbose, DST_INFO, 1, "invalid dst type %d", type); - return -EINVAL; - } - dprintk(verbose, DST_INFO, 1, "DST type: %s", otype); - - return 0; -} - -static struct tuner_types tuner_list[] = { - { - .tuner_type = TUNER_TYPE_L64724, - .tuner_name = "L 64724", - .board_name = "UNKNOWN", - .fw_name = "UNKNOWN" - }, - - { - .tuner_type = TUNER_TYPE_STV0299, - .tuner_name = "STV 0299", - .board_name = "VP1020", - .fw_name = "DST-MOT" - }, - - { - .tuner_type = TUNER_TYPE_STV0299, - .tuner_name = "STV 0299", - .board_name = "VP1020", - .fw_name = "DST-03T" - }, - - { - .tuner_type = TUNER_TYPE_MB86A15, - .tuner_name = "MB 86A15", - .board_name = "VP1022", - .fw_name = "DST-03T" - }, - - { - .tuner_type = TUNER_TYPE_MB86A15, - .tuner_name = "MB 86A15", - .board_name = "VP1025", - .fw_name = "DST-03T" - }, - - { - .tuner_type = TUNER_TYPE_STV0299, - .tuner_name = "STV 0299", - .board_name = "VP1030", - .fw_name = "DST-CI" - }, - - { - .tuner_type = TUNER_TYPE_STV0299, - .tuner_name = "STV 0299", - .board_name = "VP1030", - .fw_name = "DSTMCI" - }, - - { - .tuner_type = TUNER_TYPE_UNKNOWN, - .tuner_name = "UNKNOWN", - .board_name = "VP2021", - .fw_name = "DCTNEW" - }, - - { - .tuner_type = TUNER_TYPE_UNKNOWN, - .tuner_name = "UNKNOWN", - .board_name = "VP2030", - .fw_name = "DCT-CI" - }, - - { - .tuner_type = TUNER_TYPE_UNKNOWN, - .tuner_name = "UNKNOWN", - .board_name = "VP2031", - .fw_name = "DCT-CI" - }, - - { - .tuner_type = TUNER_TYPE_UNKNOWN, - .tuner_name = "UNKNOWN", - .board_name = "VP2040", - .fw_name = "DCT-CI" - }, - - { - .tuner_type = TUNER_TYPE_UNKNOWN, - .tuner_name = "UNKNOWN", - .board_name = "VP3020", - .fw_name = "DTTFTA" - }, - - { - .tuner_type = TUNER_TYPE_UNKNOWN, - .tuner_name = "UNKNOWN", - .board_name = "VP3021", - .fw_name = "DTTFTA" - }, - - { - .tuner_type = TUNER_TYPE_TDA10046, - .tuner_name = "TDA10046", - .board_name = "VP3040", - .fw_name = "DTT-CI" - }, - - { - .tuner_type = TUNER_TYPE_UNKNOWN, - .tuner_name = "UNKNOWN", - .board_name = "VP3051", - .fw_name = "DTTNXT" - }, - - { - .tuner_type = TUNER_TYPE_NXT200x, - .tuner_name = "NXT200x", - .board_name = "VP3220", - .fw_name = "ATSCDI" - }, - - { - .tuner_type = TUNER_TYPE_NXT200x, - .tuner_name = "NXT200x", - .board_name = "VP3250", - .fw_name = "ATSCAD" - }, -}; - -/* - Known cards list - Satellite - ------------------- - 200103A - VP-1020 DST-MOT LG(old), TS=188 - - VP-1020 DST-03T LG(new), TS=204 - VP-1022 DST-03T LG(new), TS=204 - VP-1025 DST-03T LG(new), TS=204 - - VP-1030 DSTMCI, LG(new), TS=188 - VP-1032 DSTMCI, LG(new), TS=188 - - Cable - ------------------- - VP-2030 DCT-CI, Samsung, TS=204 - VP-2021 DCT-CI, Unknown, TS=204 - VP-2031 DCT-CI, Philips, TS=188 - VP-2040 DCT-CI, Philips, TS=188, with CA daughter board - VP-2040 DCT-CI, Philips, TS=204, without CA daughter board - - Terrestrial - ------------------- - VP-3050 DTTNXT TS=188 - VP-3040 DTT-CI, Philips, TS=188 - VP-3040 DTT-CI, Philips, TS=204 - - ATSC - ------------------- - VP-3220 ATSCDI, TS=188 - VP-3250 ATSCAD, TS=188 - -*/ - -static struct dst_types dst_tlist[] = { - { - .device_id = "200103A", - .offset = 0, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_OBS_REGS, - .dst_feature = 0, - .tuner_type = 0 - }, /* obsolete */ - - { - .device_id = "DST-020", - .offset = 0, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, - .dst_feature = 0, - .tuner_type = 0 - }, /* obsolete */ - - { - .device_id = "DST-030", - .offset = 0, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, - .dst_feature = 0, - .tuner_type = 0 - }, /* obsolete */ - - { - .device_id = "DST-03T", - .offset = 0, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_2, - .dst_feature = DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 | DST_TYPE_HAS_DISEQC5 - | DST_TYPE_HAS_MAC | DST_TYPE_HAS_MOTO, - .tuner_type = TUNER_TYPE_MULTI - }, - - { - .device_id = "DST-MOT", - .offset = 0, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, - .dst_feature = 0, - .tuner_type = 0 - }, /* obsolete */ - - { - .device_id = "DST-CI", - .offset = 1, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_1, - .dst_feature = DST_TYPE_HAS_CA, - .tuner_type = 0 - }, /* An OEM board */ - - { - .device_id = "DSTMCI", - .offset = 1, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_INC_COUNT | DST_TYPE_HAS_VLF, - .dst_feature = DST_TYPE_HAS_CA | DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 - | DST_TYPE_HAS_MOTO | DST_TYPE_HAS_MAC, - .tuner_type = TUNER_TYPE_MULTI - }, - - { - .device_id = "DSTFCI", - .offset = 1, - .dst_type = DST_TYPE_IS_SAT, - .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, - .dst_feature = 0, - .tuner_type = 0 - }, /* unknown to vendor */ - - { - .device_id = "DCT-CI", - .offset = 1, - .dst_type = DST_TYPE_IS_CABLE, - .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_VLF, - .dst_feature = DST_TYPE_HAS_CA, - .tuner_type = 0 - }, - - { - .device_id = "DCTNEW", - .offset = 1, - .dst_type = DST_TYPE_IS_CABLE, - .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_3 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_MULTI_FE, - .dst_feature = 0, - .tuner_type = 0 - }, - - { - .device_id = "DTT-CI", - .offset = 1, - .dst_type = DST_TYPE_IS_TERR, - .type_flags = DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_VLF, - .dst_feature = DST_TYPE_HAS_CA, - .tuner_type = 0 - }, - - { - .device_id = "DTTDIG", - .offset = 1, - .dst_type = DST_TYPE_IS_TERR, - .type_flags = DST_TYPE_HAS_FW_2, - .dst_feature = 0, - .tuner_type = 0 - }, - - { - .device_id = "DTTNXT", - .offset = 1, - .dst_type = DST_TYPE_IS_TERR, - .type_flags = DST_TYPE_HAS_FW_2, - .dst_feature = DST_TYPE_HAS_ANALOG, - .tuner_type = 0 - }, - - { - .device_id = "ATSCDI", - .offset = 1, - .dst_type = DST_TYPE_IS_ATSC, - .type_flags = DST_TYPE_HAS_FW_2, - .dst_feature = 0, - .tuner_type = 0 - }, - - { - .device_id = "ATSCAD", - .offset = 1, - .dst_type = DST_TYPE_IS_ATSC, - .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD, - .dst_feature = DST_TYPE_HAS_MAC | DST_TYPE_HAS_ANALOG, - .tuner_type = 0 - }, - - { } - -}; - -static int dst_get_mac(struct dst_state *state) -{ - u8 get_mac[] = { 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - get_mac[7] = dst_check_sum(get_mac, 7); - if (dst_command(state, get_mac, 8) < 0) { - dprintk(verbose, DST_INFO, 1, "Unsupported Command"); - return -1; - } - memset(&state->mac_address, '\0', 8); - memcpy(&state->mac_address, &state->rxbuffer, 6); - dprintk(verbose, DST_ERROR, 1, "MAC Address=[%pM]", state->mac_address); - - return 0; -} - -static int dst_fw_ver(struct dst_state *state) -{ - u8 get_ver[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - get_ver[7] = dst_check_sum(get_ver, 7); - if (dst_command(state, get_ver, 8) < 0) { - dprintk(verbose, DST_INFO, 1, "Unsupported Command"); - return -1; - } - memcpy(&state->fw_version, &state->rxbuffer, 8); - dprintk(verbose, DST_ERROR, 1, "Firmware Ver = %x.%x Build = %02x, on %x:%x, %x-%x-20%02x", - state->fw_version[0] >> 4, state->fw_version[0] & 0x0f, - state->fw_version[1], - state->fw_version[5], state->fw_version[6], - state->fw_version[4], state->fw_version[3], state->fw_version[2]); - - return 0; -} - -static int dst_card_type(struct dst_state *state) -{ - int j; - struct tuner_types *p_tuner_list = NULL; - - u8 get_type[] = { 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - get_type[7] = dst_check_sum(get_type, 7); - if (dst_command(state, get_type, 8) < 0) { - dprintk(verbose, DST_INFO, 1, "Unsupported Command"); - return -1; - } - memset(&state->card_info, '\0', 8); - memcpy(&state->card_info, &state->rxbuffer, 7); - dprintk(verbose, DST_ERROR, 1, "Device Model=[%s]", &state->card_info[0]); - - for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { - if (!strcmp(&state->card_info[0], p_tuner_list->board_name)) { - state->tuner_type = p_tuner_list->tuner_type; - dprintk(verbose, DST_ERROR, 1, "DST has [%s] tuner, tuner type=[%d]", - p_tuner_list->tuner_name, p_tuner_list->tuner_type); - } - } - - return 0; -} - -static int dst_get_vendor(struct dst_state *state) -{ - u8 get_vendor[] = { 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - get_vendor[7] = dst_check_sum(get_vendor, 7); - if (dst_command(state, get_vendor, 8) < 0) { - dprintk(verbose, DST_INFO, 1, "Unsupported Command"); - return -1; - } - memset(&state->vendor, '\0', 8); - memcpy(&state->vendor, &state->rxbuffer, 7); - dprintk(verbose, DST_ERROR, 1, "Vendor=[%s]", &state->vendor[0]); - - return 0; -} - -static void debug_dst_buffer(struct dst_state *state) -{ - int i; - - if (verbose > 2) { - printk("%s: [", __func__); - for (i = 0; i < 8; i++) - printk(" %02x", state->rxbuffer[i]); - printk("]\n"); - } -} - -static int dst_check_stv0299(struct dst_state *state) -{ - u8 check_stv0299[] = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - check_stv0299[7] = dst_check_sum(check_stv0299, 7); - if (dst_command(state, check_stv0299, 8) < 0) { - dprintk(verbose, DST_ERROR, 1, "Cmd=[0x04] failed"); - return -1; - } - debug_dst_buffer(state); - - if (memcmp(&check_stv0299, &state->rxbuffer, 8)) { - dprintk(verbose, DST_ERROR, 1, "Found a STV0299 NIM"); - state->tuner_type = TUNER_TYPE_STV0299; - return 0; - } - - return -1; -} - -static int dst_check_mb86a15(struct dst_state *state) -{ - u8 check_mb86a15[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - check_mb86a15[7] = dst_check_sum(check_mb86a15, 7); - if (dst_command(state, check_mb86a15, 8) < 0) { - dprintk(verbose, DST_ERROR, 1, "Cmd=[0x10], failed"); - return -1; - } - debug_dst_buffer(state); - - if (memcmp(&check_mb86a15, &state->rxbuffer, 8) < 0) { - dprintk(verbose, DST_ERROR, 1, "Found a MB86A15 NIM"); - state->tuner_type = TUNER_TYPE_MB86A15; - return 0; - } - - return -1; -} - -static int dst_get_tuner_info(struct dst_state *state) -{ - u8 get_tuner_1[] = { 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - u8 get_tuner_2[] = { 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - get_tuner_1[7] = dst_check_sum(get_tuner_1, 7); - get_tuner_2[7] = dst_check_sum(get_tuner_2, 7); - dprintk(verbose, DST_ERROR, 1, "DST TYpe = MULTI FE"); - if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { - if (dst_command(state, get_tuner_1, 8) < 0) { - dprintk(verbose, DST_INFO, 1, "Cmd=[0x13], Unsupported"); - goto force; - } - } else { - if (dst_command(state, get_tuner_2, 8) < 0) { - dprintk(verbose, DST_INFO, 1, "Cmd=[0xb], Unsupported"); - goto force; - } - } - memcpy(&state->board_info, &state->rxbuffer, 8); - if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { - dprintk(verbose, DST_ERROR, 1, "DST type has TS=188"); - } - if (state->board_info[0] == 0xbc) { - if (state->dst_type != DST_TYPE_IS_ATSC) - state->type_flags |= DST_TYPE_HAS_TS188; - else - state->type_flags |= DST_TYPE_HAS_NEWTUNE_2; - - if (state->board_info[1] == 0x01) { - state->dst_hw_cap |= DST_TYPE_HAS_DBOARD; - dprintk(verbose, DST_ERROR, 1, "DST has Daughterboard"); - } - } - - return 0; -force: - if (!strncmp(state->fw_name, "DCT-CI", 6)) { - state->type_flags |= DST_TYPE_HAS_TS204; - dprintk(verbose, DST_ERROR, 1, "Forcing [%s] to TS188", state->fw_name); - } - - return -1; -} - -static int dst_get_device_id(struct dst_state *state) -{ - u8 reply; - - int i, j; - struct dst_types *p_dst_type = NULL; - struct tuner_types *p_tuner_list = NULL; - - u8 use_dst_type = 0; - u32 use_type_flags = 0; - - static u8 device_type[8] = {0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; - - state->tuner_type = 0; - device_type[7] = dst_check_sum(device_type, 7); - - if (write_dst(state, device_type, FIXED_COMM)) - return -1; /* Write failed */ - if ((dst_pio_disable(state)) < 0) - return -1; - if (read_dst(state, &reply, GET_ACK)) - return -1; /* Read failure */ - if (reply != ACK) { - dprintk(verbose, DST_INFO, 1, "Write not Acknowledged! [Reply=0x%02x]", reply); - return -1; /* Unack'd write */ - } - if (!dst_wait_dst_ready(state, DEVICE_INIT)) - return -1; /* DST not ready yet */ - if (read_dst(state, state->rxbuffer, FIXED_COMM)) - return -1; - - dst_pio_disable(state); - if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { - dprintk(verbose, DST_INFO, 1, "Checksum failure!"); - return -1; /* Checksum failure */ - } - state->rxbuffer[7] = '\0'; - - for (i = 0, p_dst_type = dst_tlist; i < ARRAY_SIZE(dst_tlist); i++, p_dst_type++) { - if (!strncmp (&state->rxbuffer[p_dst_type->offset], p_dst_type->device_id, strlen (p_dst_type->device_id))) { - use_type_flags = p_dst_type->type_flags; - use_dst_type = p_dst_type->dst_type; - - /* Card capabilities */ - state->dst_hw_cap = p_dst_type->dst_feature; - dprintk(verbose, DST_ERROR, 1, "Recognise [%s]", p_dst_type->device_id); - strncpy(&state->fw_name[0], p_dst_type->device_id, 6); - /* Multiple tuners */ - if (p_dst_type->tuner_type & TUNER_TYPE_MULTI) { - switch (use_dst_type) { - case DST_TYPE_IS_SAT: - /* STV0299 check */ - if (dst_check_stv0299(state) < 0) { - dprintk(verbose, DST_ERROR, 1, "Unsupported"); - state->tuner_type = TUNER_TYPE_MB86A15; - } - break; - default: - break; - } - if (dst_check_mb86a15(state) < 0) - dprintk(verbose, DST_ERROR, 1, "Unsupported"); - /* Single tuner */ - } else { - state->tuner_type = p_dst_type->tuner_type; - } - for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { - if (!(strncmp(p_dst_type->device_id, p_tuner_list->fw_name, 7)) && - p_tuner_list->tuner_type == state->tuner_type) { - dprintk(verbose, DST_ERROR, 1, "[%s] has a [%s]", - p_dst_type->device_id, p_tuner_list->tuner_name); - } - } - break; - } - } - - if (i >= ARRAY_SIZE(dst_tlist)) { - dprintk(verbose, DST_ERROR, 1, "Unable to recognize %s or %s", &state->rxbuffer[0], &state->rxbuffer[1]); - dprintk(verbose, DST_ERROR, 1, "please email linux-dvb@linuxtv.org with this type in"); - use_dst_type = DST_TYPE_IS_SAT; - use_type_flags = DST_TYPE_HAS_SYMDIV; - } - dst_type_print(state, use_dst_type); - state->type_flags = use_type_flags; - state->dst_type = use_dst_type; - dst_type_flags_print(state); - - return 0; -} - -static int dst_probe(struct dst_state *state) -{ - mutex_init(&state->dst_mutex); - if (dst_addons & DST_TYPE_HAS_CA) { - if ((rdc_8820_reset(state)) < 0) { - dprintk(verbose, DST_ERROR, 1, "RDC 8820 RESET Failed."); - return -1; - } - msleep(4000); - } else { - msleep(100); - } - if ((dst_comm_init(state)) < 0) { - dprintk(verbose, DST_ERROR, 1, "DST Initialization Failed."); - return -1; - } - msleep(100); - if (dst_get_device_id(state) < 0) { - dprintk(verbose, DST_ERROR, 1, "unknown device."); - return -1; - } - if (dst_get_mac(state) < 0) { - dprintk(verbose, DST_INFO, 1, "MAC: Unsupported command"); - } - if ((state->type_flags & DST_TYPE_HAS_MULTI_FE) || (state->type_flags & DST_TYPE_HAS_FW_BUILD)) { - if (dst_get_tuner_info(state) < 0) - dprintk(verbose, DST_INFO, 1, "Tuner: Unsupported command"); - } - if (state->type_flags & DST_TYPE_HAS_TS204) { - dst_packsize(state, 204); - } - if (state->type_flags & DST_TYPE_HAS_FW_BUILD) { - if (dst_fw_ver(state) < 0) { - dprintk(verbose, DST_INFO, 1, "FW: Unsupported command"); - return 0; - } - if (dst_card_type(state) < 0) { - dprintk(verbose, DST_INFO, 1, "Card: Unsupported command"); - return 0; - } - if (dst_get_vendor(state) < 0) { - dprintk(verbose, DST_INFO, 1, "Vendor: Unsupported command"); - return 0; - } - } - - return 0; -} - -static int dst_command(struct dst_state *state, u8 *data, u8 len) -{ - u8 reply; - - mutex_lock(&state->dst_mutex); - if ((dst_comm_init(state)) < 0) { - dprintk(verbose, DST_NOTICE, 1, "DST Communication Initialization Failed."); - goto error; - } - if (write_dst(state, data, len)) { - dprintk(verbose, DST_INFO, 1, "Trying to recover.. "); - if ((dst_error_recovery(state)) < 0) { - dprintk(verbose, DST_ERROR, 1, "Recovery Failed."); - goto error; - } - goto error; - } - if ((dst_pio_disable(state)) < 0) { - dprintk(verbose, DST_ERROR, 1, "PIO Disable Failed."); - goto error; - } - if (state->type_flags & DST_TYPE_HAS_FW_1) - mdelay(3); - if (read_dst(state, &reply, GET_ACK)) { - dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); - if ((dst_error_recovery(state)) < 0) { - dprintk(verbose, DST_INFO, 1, "Recovery Failed."); - goto error; - } - goto error; - } - if (reply != ACK) { - dprintk(verbose, DST_INFO, 1, "write not acknowledged 0x%02x ", reply); - goto error; - } - if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3)) - goto error; - if (state->type_flags & DST_TYPE_HAS_FW_1) - mdelay(3); - else - udelay(2000); - if (!dst_wait_dst_ready(state, NO_DELAY)) - goto error; - if (read_dst(state, state->rxbuffer, FIXED_COMM)) { - dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); - if ((dst_error_recovery(state)) < 0) { - dprintk(verbose, DST_INFO, 1, "Recovery failed."); - goto error; - } - goto error; - } - if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { - dprintk(verbose, DST_INFO, 1, "checksum failure"); - goto error; - } - mutex_unlock(&state->dst_mutex); - return 0; - -error: - mutex_unlock(&state->dst_mutex); - return -EIO; - -} - -static int dst_get_signal(struct dst_state *state) -{ - int retval; - u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb }; - //dprintk("%s: Getting Signal strength and other parameters\n", __func__); - if ((state->diseq_flags & ATTEMPT_TUNE) == 0) { - state->decode_lock = state->decode_strength = state->decode_snr = 0; - return 0; - } - if (0 == (state->diseq_flags & HAS_LOCK)) { - state->decode_lock = state->decode_strength = state->decode_snr = 0; - return 0; - } - if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) { - retval = dst_command(state, get_signal, 8); - if (retval < 0) - return retval; - if (state->dst_type == DST_TYPE_IS_SAT) { - state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0; - state->decode_strength = state->rxbuffer[5] << 8; - state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; - } else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) { - state->decode_lock = (state->rxbuffer[1]) ? 1 : 0; - state->decode_strength = state->rxbuffer[4] << 8; - state->decode_snr = state->rxbuffer[3] << 8; - } else if (state->dst_type == DST_TYPE_IS_ATSC) { - state->decode_lock = (state->rxbuffer[6] == 0x00) ? 1 : 0; - state->decode_strength = state->rxbuffer[4] << 8; - state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; - } - state->cur_jiff = jiffies; - } - return 0; -} - -static int dst_tone_power_cmd(struct dst_state *state) -{ - u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 }; - - if (state->dst_type != DST_TYPE_IS_SAT) - return -EOPNOTSUPP; - paket[4] = state->tx_tuna[4]; - paket[2] = state->tx_tuna[2]; - paket[3] = state->tx_tuna[3]; - paket[7] = dst_check_sum (paket, 7); - return dst_command(state, paket, 8); -} - -static int dst_get_tuna(struct dst_state *state) -{ - int retval; - - if ((state->diseq_flags & ATTEMPT_TUNE) == 0) - return 0; - state->diseq_flags &= ~(HAS_LOCK); - if (!dst_wait_dst_ready(state, NO_DELAY)) - return -EIO; - if ((state->type_flags & DST_TYPE_HAS_VLF) && - !(state->dst_type == DST_TYPE_IS_ATSC)) - - retval = read_dst(state, state->rx_tuna, 10); - else - retval = read_dst(state, &state->rx_tuna[2], FIXED_COMM); - if (retval < 0) { - dprintk(verbose, DST_DEBUG, 1, "read not successful"); - return retval; - } - if ((state->type_flags & DST_TYPE_HAS_VLF) && - !(state->dst_type == DST_TYPE_IS_ATSC)) { - - if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) { - dprintk(verbose, DST_INFO, 1, "checksum failure ? "); - return -EIO; - } - } else { - if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) { - dprintk(verbose, DST_INFO, 1, "checksum failure? "); - return -EIO; - } - } - if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0) - return 0; - if (state->dst_type == DST_TYPE_IS_SAT) { - state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3]; - } else { - state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 16) + (state->rx_tuna[3] << 8) + state->rx_tuna[4]; - } - state->decode_freq = state->decode_freq * 1000; - state->decode_lock = 1; - state->diseq_flags |= HAS_LOCK; - - return 1; -} - -static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); - -static int dst_write_tuna(struct dvb_frontend *fe) -{ - struct dst_state *state = fe->demodulator_priv; - int retval; - u8 reply; - - dprintk(verbose, DST_INFO, 1, "type_flags 0x%x ", state->type_flags); - state->decode_freq = 0; - state->decode_lock = state->decode_strength = state->decode_snr = 0; - if (state->dst_type == DST_TYPE_IS_SAT) { - if (!(state->diseq_flags & HAS_POWER)) - dst_set_voltage(fe, SEC_VOLTAGE_13); - } - state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE); - mutex_lock(&state->dst_mutex); - if ((dst_comm_init(state)) < 0) { - dprintk(verbose, DST_DEBUG, 1, "DST Communication initialization failed."); - goto error; - } -// if (state->type_flags & DST_TYPE_HAS_NEWTUNE) { - if ((state->type_flags & DST_TYPE_HAS_VLF) && - (!(state->dst_type == DST_TYPE_IS_ATSC))) { - - state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9); - retval = write_dst(state, &state->tx_tuna[0], 10); - } else { - state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7); - retval = write_dst(state, &state->tx_tuna[2], FIXED_COMM); - } - if (retval < 0) { - dst_pio_disable(state); - dprintk(verbose, DST_DEBUG, 1, "write not successful"); - goto werr; - } - if ((dst_pio_disable(state)) < 0) { - dprintk(verbose, DST_DEBUG, 1, "DST PIO disable failed !"); - goto error; - } - if ((read_dst(state, &reply, GET_ACK) < 0)) { - dprintk(verbose, DST_DEBUG, 1, "read verify not successful."); - goto error; - } - if (reply != ACK) { - dprintk(verbose, DST_DEBUG, 1, "write not acknowledged 0x%02x ", reply); - goto error; - } - state->diseq_flags |= ATTEMPT_TUNE; - retval = dst_get_tuna(state); -werr: - mutex_unlock(&state->dst_mutex); - return retval; - -error: - mutex_unlock(&state->dst_mutex); - return -EIO; -} - -/* - * line22k0 0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00 - * line22k1 0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00 - * line22k2 0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00 - * tone 0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00 - * data 0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00 - * power_off 0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 - * power_on 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 - * Diseqc 1 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec - * Diseqc 2 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8 - * Diseqc 3 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4 - * Diseqc 4 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0 - */ - -static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) -{ - struct dst_state *state = fe->demodulator_priv; - u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec }; - - if (state->dst_type != DST_TYPE_IS_SAT) - return -EOPNOTSUPP; - if (cmd->msg_len > 0 && cmd->msg_len < 5) - memcpy(&paket[3], cmd->msg, cmd->msg_len); - else if (cmd->msg_len == 5 && state->dst_hw_cap & DST_TYPE_HAS_DISEQC5) - memcpy(&paket[2], cmd->msg, cmd->msg_len); - else - return -EINVAL; - paket[7] = dst_check_sum(&paket[0], 7); - return dst_command(state, paket, 8); -} - -static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - int need_cmd, retval = 0; - struct dst_state *state = fe->demodulator_priv; - - state->voltage = voltage; - if (state->dst_type != DST_TYPE_IS_SAT) - return -EOPNOTSUPP; - - need_cmd = 0; - - switch (voltage) { - case SEC_VOLTAGE_13: - case SEC_VOLTAGE_18: - if ((state->diseq_flags & HAS_POWER) == 0) - need_cmd = 1; - state->diseq_flags |= HAS_POWER; - state->tx_tuna[4] = 0x01; - break; - case SEC_VOLTAGE_OFF: - need_cmd = 1; - state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE); - state->tx_tuna[4] = 0x00; - break; - default: - return -EINVAL; - } - - if (need_cmd) - retval = dst_tone_power_cmd(state); - - return retval; -} - -static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) -{ - struct dst_state *state = fe->demodulator_priv; - - state->tone = tone; - if (state->dst_type != DST_TYPE_IS_SAT) - return -EOPNOTSUPP; - - switch (tone) { - case SEC_TONE_OFF: - if (state->type_flags & DST_TYPE_HAS_OBS_REGS) - state->tx_tuna[2] = 0x00; - else - state->tx_tuna[2] = 0xff; - break; - - case SEC_TONE_ON: - state->tx_tuna[2] = 0x02; - break; - default: - return -EINVAL; - } - return dst_tone_power_cmd(state); -} - -static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd) -{ - struct dst_state *state = fe->demodulator_priv; - - if (state->dst_type != DST_TYPE_IS_SAT) - return -EOPNOTSUPP; - state->minicmd = minicmd; - switch (minicmd) { - case SEC_MINI_A: - state->tx_tuna[3] = 0x02; - break; - case SEC_MINI_B: - state->tx_tuna[3] = 0xff; - break; - } - return dst_tone_power_cmd(state); -} - - -static int dst_init(struct dvb_frontend *fe) -{ - struct dst_state *state = fe->demodulator_priv; - - static u8 sat_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x00, 0x73, 0x21, 0x00, 0x00 }; - static u8 sat_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x55, 0xbd, 0x50, 0x00, 0x00 }; - static u8 ter_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; - static u8 ter_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; - static u8 cab_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; - static u8 cab_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; - static u8 atsc_tuner[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; - - state->inversion = INVERSION_OFF; - state->voltage = SEC_VOLTAGE_13; - state->tone = SEC_TONE_OFF; - state->diseq_flags = 0; - state->k22 = 0x02; - state->bandwidth = 7000000; - state->cur_jiff = jiffies; - if (state->dst_type == DST_TYPE_IS_SAT) - memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? sat_tuna_188 : sat_tuna_204), sizeof (sat_tuna_204)); - else if (state->dst_type == DST_TYPE_IS_TERR) - memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? ter_tuna_188 : ter_tuna_204), sizeof (ter_tuna_204)); - else if (state->dst_type == DST_TYPE_IS_CABLE) - memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? cab_tuna_188 : cab_tuna_204), sizeof (cab_tuna_204)); - else if (state->dst_type == DST_TYPE_IS_ATSC) - memcpy(state->tx_tuna, atsc_tuner, sizeof (atsc_tuner)); - - return 0; -} - -static int dst_read_status(struct dvb_frontend *fe, fe_status_t *status) -{ - struct dst_state *state = fe->demodulator_priv; - - *status = 0; - if (state->diseq_flags & HAS_LOCK) { -// dst_get_signal(state); // don't require(?) to ask MCU - if (state->decode_lock) - *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI; - } - - return 0; -} - -static int dst_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct dst_state *state = fe->demodulator_priv; - - int retval = dst_get_signal(state); - *strength = state->decode_strength; - - return retval; -} - -static int dst_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct dst_state *state = fe->demodulator_priv; - - int retval = dst_get_signal(state); - *snr = state->decode_snr; - - return retval; -} - -static int dst_set_frontend(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - int retval = -EINVAL; - struct dst_state *state = fe->demodulator_priv; - - if (p != NULL) { - retval = dst_set_freq(state, p->frequency); - if(retval != 0) - return retval; - dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); - - if (state->dst_type == DST_TYPE_IS_SAT) { - if (state->type_flags & DST_TYPE_HAS_OBS_REGS) - dst_set_inversion(state, p->inversion); - dst_set_fec(state, p->fec_inner); - dst_set_symbolrate(state, p->symbol_rate); - dst_set_polarization(state); - dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); - - } else if (state->dst_type == DST_TYPE_IS_TERR) - dst_set_bandwidth(state, p->bandwidth_hz); - else if (state->dst_type == DST_TYPE_IS_CABLE) { - dst_set_fec(state, p->fec_inner); - dst_set_symbolrate(state, p->symbol_rate); - dst_set_modulation(state, p->modulation); - } - retval = dst_write_tuna(fe); - } - - return retval; -} - -static int dst_tune_frontend(struct dvb_frontend* fe, - bool re_tune, - unsigned int mode_flags, - unsigned int *delay, - fe_status_t *status) -{ - struct dst_state *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - - if (re_tune) { - dst_set_freq(state, p->frequency); - dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); - - if (state->dst_type == DST_TYPE_IS_SAT) { - if (state->type_flags & DST_TYPE_HAS_OBS_REGS) - dst_set_inversion(state, p->inversion); - dst_set_fec(state, p->fec_inner); - dst_set_symbolrate(state, p->symbol_rate); - dst_set_polarization(state); - dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); - - } else if (state->dst_type == DST_TYPE_IS_TERR) - dst_set_bandwidth(state, p->bandwidth_hz); - else if (state->dst_type == DST_TYPE_IS_CABLE) { - dst_set_fec(state, p->fec_inner); - dst_set_symbolrate(state, p->symbol_rate); - dst_set_modulation(state, p->modulation); - } - dst_write_tuna(fe); - } - - if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) - dst_read_status(fe, status); - - *delay = HZ/10; - return 0; -} - -static int dst_get_tuning_algo(struct dvb_frontend *fe) -{ - return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW; -} - -static int dst_get_frontend(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct dst_state *state = fe->demodulator_priv; - - p->frequency = state->decode_freq; - if (state->dst_type == DST_TYPE_IS_SAT) { - if (state->type_flags & DST_TYPE_HAS_OBS_REGS) - p->inversion = state->inversion; - p->symbol_rate = state->symbol_rate; - p->fec_inner = dst_get_fec(state); - } else if (state->dst_type == DST_TYPE_IS_TERR) { - p->bandwidth_hz = state->bandwidth; - } else if (state->dst_type == DST_TYPE_IS_CABLE) { - p->symbol_rate = state->symbol_rate; - p->fec_inner = dst_get_fec(state); - p->modulation = dst_get_modulation(state); - } - - return 0; -} - -static void dst_release(struct dvb_frontend *fe) -{ - struct dst_state *state = fe->demodulator_priv; - if (state->dst_ca) { - dvb_unregister_device(state->dst_ca); -#ifdef CONFIG_MEDIA_ATTACH - symbol_put(dst_ca_attach); -#endif - } - kfree(state); -} - -static struct dvb_frontend_ops dst_dvbt_ops; -static struct dvb_frontend_ops dst_dvbs_ops; -static struct dvb_frontend_ops dst_dvbc_ops; -static struct dvb_frontend_ops dst_atsc_ops; - -struct dst_state *dst_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter) -{ - /* check if the ASIC is there */ - if (dst_probe(state) < 0) { - kfree(state); - return NULL; - } - /* determine settings based on type */ - /* create dvb_frontend */ - switch (state->dst_type) { - case DST_TYPE_IS_TERR: - memcpy(&state->frontend.ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops)); - break; - case DST_TYPE_IS_CABLE: - memcpy(&state->frontend.ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops)); - break; - case DST_TYPE_IS_SAT: - memcpy(&state->frontend.ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops)); - break; - case DST_TYPE_IS_ATSC: - memcpy(&state->frontend.ops, &dst_atsc_ops, sizeof(struct dvb_frontend_ops)); - break; - default: - dprintk(verbose, DST_ERROR, 1, "unknown DST type. please report to the LinuxTV.org DVB mailinglist."); - kfree(state); - return NULL; - } - state->frontend.demodulator_priv = state; - - return state; /* Manu (DST is a card not a frontend) */ -} - -EXPORT_SYMBOL(dst_attach); - -static struct dvb_frontend_ops dst_dvbt_ops = { - .delsys = { SYS_DVBT }, - .info = { - .name = "DST DVB-T", - .frequency_min = 137000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, - .caps = FE_CAN_FEC_AUTO | - FE_CAN_QAM_AUTO | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO - }, - - .release = dst_release, - .init = dst_init, - .tune = dst_tune_frontend, - .set_frontend = dst_set_frontend, - .get_frontend = dst_get_frontend, - .get_frontend_algo = dst_get_tuning_algo, - .read_status = dst_read_status, - .read_signal_strength = dst_read_signal_strength, - .read_snr = dst_read_snr, -}; - -static struct dvb_frontend_ops dst_dvbs_ops = { - .delsys = { SYS_DVBS }, - .info = { - .name = "DST DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - /* . symbol_rate_tolerance = ???,*/ - .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK - }, - - .release = dst_release, - .init = dst_init, - .tune = dst_tune_frontend, - .set_frontend = dst_set_frontend, - .get_frontend = dst_get_frontend, - .get_frontend_algo = dst_get_tuning_algo, - .read_status = dst_read_status, - .read_signal_strength = dst_read_signal_strength, - .read_snr = dst_read_snr, - .diseqc_send_burst = dst_send_burst, - .diseqc_send_master_cmd = dst_set_diseqc, - .set_voltage = dst_set_voltage, - .set_tone = dst_set_tone, -}; - -static struct dvb_frontend_ops dst_dvbc_ops = { - .delsys = { SYS_DVBC_ANNEX_A }, - .info = { - .name = "DST DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - .caps = FE_CAN_FEC_AUTO | - FE_CAN_QAM_AUTO | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 - }, - - .release = dst_release, - .init = dst_init, - .tune = dst_tune_frontend, - .set_frontend = dst_set_frontend, - .get_frontend = dst_get_frontend, - .get_frontend_algo = dst_get_tuning_algo, - .read_status = dst_read_status, - .read_signal_strength = dst_read_signal_strength, - .read_snr = dst_read_snr, -}; - -static struct dvb_frontend_ops dst_atsc_ops = { - .delsys = { SYS_ATSC }, - .info = { - .name = "DST ATSC", - .frequency_stepsize = 62500, - .frequency_min = 510000000, - .frequency_max = 858000000, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB - }, - - .release = dst_release, - .init = dst_init, - .tune = dst_tune_frontend, - .set_frontend = dst_set_frontend, - .get_frontend = dst_get_frontend, - .get_frontend_algo = dst_get_tuning_algo, - .read_status = dst_read_status, - .read_signal_strength = dst_read_signal_strength, - .read_snr = dst_read_snr, -}; - -MODULE_DESCRIPTION("DST DVB-S/T/C/ATSC Combo Frontend driver"); -MODULE_AUTHOR("Jamie Honan, Manu Abraham"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c deleted file mode 100644 index ee3884fbc9ce..000000000000 --- a/drivers/media/dvb/bt8xx/dst_ca.c +++ /dev/null @@ -1,726 +0,0 @@ -/* - CA-driver for TwinHan DST Frontend/Card - - Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "dvbdev.h" -#include "dvb_frontend.h" -#include "dst_ca.h" -#include "dst_common.h" - -#define DST_CA_ERROR 0 -#define DST_CA_NOTICE 1 -#define DST_CA_INFO 2 -#define DST_CA_DEBUG 3 - -#define dprintk(x, y, z, format, arg...) do { \ - if (z) { \ - if ((x > DST_CA_ERROR) && (x > y)) \ - printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ - else if ((x > DST_CA_NOTICE) && (x > y)) \ - printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ - else if ((x > DST_CA_INFO) && (x > y)) \ - printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ - else if ((x > DST_CA_DEBUG) && (x > y)) \ - printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ - } else { \ - if (x > y) \ - printk(format, ## arg); \ - } \ -} while(0) - - -static DEFINE_MUTEX(dst_ca_mutex); -static unsigned int verbose = 5; -module_param(verbose, int, 0644); -MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); - -/* Need some more work */ -static int ca_set_slot_descr(void) -{ - /* We could make this more graceful ? */ - return -EOPNOTSUPP; -} - -/* Need some more work */ -static int ca_set_pid(void) -{ - /* We could make this more graceful ? */ - return -EOPNOTSUPP; -} - -static void put_command_and_length(u8 *data, int command, int length) -{ - data[0] = (command >> 16) & 0xff; - data[1] = (command >> 8) & 0xff; - data[2] = command & 0xff; - data[3] = length; -} - -static void put_checksum(u8 *check_string, int length) -{ - dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum."); - dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length); - check_string[length] = dst_check_sum (check_string, length); - dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]); -} - -static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read) -{ - u8 reply; - - mutex_lock(&state->dst_mutex); - dst_comm_init(state); - msleep(65); - - if (write_dst(state, data, len)) { - dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover"); - dst_error_recovery(state); - goto error; - } - if ((dst_pio_disable(state)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed."); - goto error; - } - if (read_dst(state, &reply, GET_ACK) < 0) { - dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); - dst_error_recovery(state); - goto error; - } - if (read) { - if (! dst_wait_dst_ready(state, LONG_DELAY)) { - dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready"); - goto error; - } - if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */ - dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); - dst_error_recovery(state); - goto error; - } - } - mutex_unlock(&state->dst_mutex); - return 0; - -error: - mutex_unlock(&state->dst_mutex); - return -EIO; -} - - -static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read) -{ - u8 dst_ca_comm_err = 0; - - while (dst_ca_comm_err < RETRIES) { - dprintk(verbose, DST_CA_NOTICE, 1, " Put Command"); - if (dst_ci_command(state, data, ca_string, len, read)) { // If error - dst_error_recovery(state); - dst_ca_comm_err++; // work required here. - } else { - break; - } - } - - if(dst_ca_comm_err == RETRIES) - return -1; - - return 0; -} - - - -static int ca_get_app_info(struct dst_state *state) -{ - int length, str_length; - static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff}; - - put_checksum(&command[0], command[0]); - if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); - return -1; - } - dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); - dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================"); - dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]", - state->messages[7], (state->messages[8] << 8) | state->messages[9], - (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12])); - dprintk(verbose, DST_CA_INFO, 1, " =================================================================================================="); - - // Transform dst message to correct application_info message - length = state->messages[5]; - str_length = length - 6; - if (str_length < 0) { - str_length = 0; - dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering."); - } - - // First, the command and length fields - put_command_and_length(&state->messages[0], CA_APP_INFO, length); - - // Copy application_type, application_manufacturer and manufacturer_code - memcpy(&state->messages[4], &state->messages[7], 5); - - // Set string length and copy string - state->messages[9] = str_length; - memcpy(&state->messages[10], &state->messages[12], str_length); - - return 0; -} - -static int ca_get_ca_info(struct dst_state *state) -{ - int srcPtr, dstPtr, i, num_ids; - static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff}; - const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7; - - put_checksum(&slot_command[0], slot_command[0]); - if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); - return -1; - } - dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); - - // Print raw data - dprintk(verbose, DST_CA_INFO, 0, " DST data = ["); - for (i = 0; i < state->messages[0] + 1; i++) { - dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]); - } - dprintk(verbose, DST_CA_INFO, 0, "]\n"); - - // Set the command and length of the output - num_ids = state->messages[in_num_ids_pos]; - if (num_ids >= 100) { - num_ids = 100; - dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering."); - } - put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2); - - dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = ["); - srcPtr = in_system_id_pos; - dstPtr = out_system_id_pos; - for(i = 0; i < num_ids; i++) { - dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]); - // Append to output - state->messages[dstPtr + 0] = state->messages[srcPtr + 0]; - state->messages[dstPtr + 1] = state->messages[srcPtr + 1]; - srcPtr += 2; - dstPtr += 2; - } - dprintk(verbose, DST_CA_INFO, 0, "]\n"); - - return 0; -} - -static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg) -{ - int i; - u8 slot_cap[256]; - static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff}; - - put_checksum(&slot_command[0], slot_command[0]); - if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); - return -1; - } - dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !"); - - /* Will implement the rest soon */ - - dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]); - dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); - for (i = 0; i < slot_cap[0] + 1; i++) - dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]); - dprintk(verbose, DST_CA_INFO, 0, "\n"); - - p_ca_caps->slot_num = 1; - p_ca_caps->slot_type = 1; - p_ca_caps->descr_num = slot_cap[7]; - p_ca_caps->descr_type = 1; - - if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps))) - return -EFAULT; - - return 0; -} - -/* Need some more work */ -static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) -{ - return -EOPNOTSUPP; -} - - -static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg) -{ - int i; - static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; - - u8 *slot_info = state->messages; - - put_checksum(&slot_command[0], 7); - if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); - return -1; - } - dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); - - /* Will implement the rest soon */ - - dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]); - dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); - for (i = 0; i < 8; i++) - dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]); - dprintk(verbose, DST_CA_INFO, 0, "\n"); - - if (slot_info[4] & 0x80) { - p_ca_slot_info->flags = CA_CI_MODULE_PRESENT; - p_ca_slot_info->num = 1; - p_ca_slot_info->type = CA_CI; - } else if (slot_info[4] & 0x40) { - p_ca_slot_info->flags = CA_CI_MODULE_READY; - p_ca_slot_info->num = 1; - p_ca_slot_info->type = CA_CI; - } else - p_ca_slot_info->flags = 0; - - if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info))) - return -EFAULT; - - return 0; -} - - -static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) -{ - u8 i = 0; - u32 command = 0; - - if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) - return -EFAULT; - - if (p_ca_message->msg) { - dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]", - 3, p_ca_message->msg); - - for (i = 0; i < 3; i++) { - command = command | p_ca_message->msg[i]; - if (i < 2) - command = command << 8; - } - dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command); - - switch (command) { - case CA_APP_INFO: - memcpy(p_ca_message->msg, state->messages, 128); - if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) - return -EFAULT; - break; - case CA_INFO: - memcpy(p_ca_message->msg, state->messages, 128); - if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) - return -EFAULT; - break; - } - } - - return 0; -} - -static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length) -{ - if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) { - hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */ - hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */ - } else { - if (length > 247) { - dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !"); - return -1; - } - hw_buffer->msg[0] = (length & 0xff) + 7; - hw_buffer->msg[1] = 0x40; - hw_buffer->msg[2] = 0x03; - hw_buffer->msg[3] = 0x00; - hw_buffer->msg[4] = 0x03; - hw_buffer->msg[5] = length & 0xff; - hw_buffer->msg[6] = 0x00; - - /* - * Need to compute length for EN50221 section 8.3.2, for the time being - * assuming 8.3.2 is not applicable - */ - memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length); - } - - return 0; -} - -static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply) -{ - if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed."); - dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST."); - rdc_reset_state(state); - return -1; - } - dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success."); - - return 0; -} - -static u32 asn_1_decode(u8 *asn_1_array) -{ - u8 length_field = 0, word_count = 0, count = 0; - u32 length = 0; - - length_field = asn_1_array[0]; - dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field); - if (length_field < 0x80) { - length = length_field & 0x7f; - dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length); - } else { - word_count = length_field & 0x7f; - for (count = 0; count < word_count; count++) { - length = length << 8; - length += asn_1_array[count + 1]; - dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length); - } - } - return length; -} - -static int debug_string(u8 *msg, u32 length, u32 offset) -{ - u32 i; - - dprintk(verbose, DST_CA_DEBUG, 0, " String=[ "); - for (i = offset; i < length; i++) - dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]); - dprintk(verbose, DST_CA_DEBUG, 0, "]\n"); - - return 0; -} - - -static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query) -{ - u32 length = 0; - u8 tag_length = 8; - - length = asn_1_decode(&p_ca_message->msg[3]); - dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length); - debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */ - - memset(hw_buffer->msg, '\0', length); - handle_dst_tag(state, p_ca_message, hw_buffer, length); - put_checksum(hw_buffer->msg, hw_buffer->msg[0]); - - debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */ - write_to_8820(state, hw_buffer, (length + tag_length), reply); - - return 0; -} - - -/* Board supports CA PMT reply ? */ -static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer) -{ - int ca_pmt_reply_test = 0; - - /* Do test board */ - /* Not there yet but soon */ - - /* CA PMT Reply capable */ - if (ca_pmt_reply_test) { - if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); - return -1; - } - - /* Process CA PMT Reply */ - /* will implement soon */ - dprintk(verbose, DST_CA_ERROR, 1, " Not there yet"); - } - /* CA PMT Reply not capable */ - if (!ca_pmt_reply_test) { - if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); - return -1; - } - dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !"); - /* put a dummy message */ - - } - return 0; -} - -static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) -{ - int i = 0; - - u32 command = 0; - struct ca_msg *hw_buffer; - int result = 0; - - if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) { - dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); - return -ENOMEM; - } - dprintk(verbose, DST_CA_DEBUG, 1, " "); - - if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) { - result = -EFAULT; - goto free_mem_and_exit; - } - - - if (p_ca_message->msg) { - /* EN50221 tag */ - command = 0; - - for (i = 0; i < 3; i++) { - command = command | p_ca_message->msg[i]; - if (i < 2) - command = command << 8; - } - dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command); - - switch (command) { - case CA_PMT: - dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT"); - if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !"); - break; - case CA_PMT_REPLY: - dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY"); - /* Have to handle the 2 basic types of cards here */ - if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !"); - break; - case CA_APP_INFO_ENQUIRY: // only for debugging - dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information"); - - if ((ca_get_app_info(state)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !"); - break; - case CA_INFO_ENQUIRY: - dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information"); - - if ((ca_get_ca_info(state)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !"); - break; - } - } -free_mem_and_exit: - kfree (hw_buffer); - - return result; -} - -static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg) -{ - struct dvb_device *dvbdev; - struct dst_state *state; - struct ca_slot_info *p_ca_slot_info; - struct ca_caps *p_ca_caps; - struct ca_msg *p_ca_message; - void __user *arg = (void __user *)ioctl_arg; - int result = 0; - - mutex_lock(&dst_ca_mutex); - dvbdev = file->private_data; - state = (struct dst_state *)dvbdev->priv; - p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL); - p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL); - p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL); - if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) { - dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); - result = -ENOMEM; - goto free_mem_and_exit; - } - - /* We have now only the standard ioctl's, the driver is upposed to handle internals. */ - switch (cmd) { - case CA_SEND_MSG: - dprintk(verbose, DST_CA_INFO, 1, " Sending message"); - if ((ca_send_message(state, p_ca_message, arg)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !"); - result = -1; - goto free_mem_and_exit; - } - break; - case CA_GET_MSG: - dprintk(verbose, DST_CA_INFO, 1, " Getting message"); - if ((ca_get_message(state, p_ca_message, arg)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !"); - break; - case CA_RESET: - dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST"); - dst_error_bailout(state); - msleep(4000); - break; - case CA_GET_SLOT_INFO: - dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info"); - if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !"); - break; - case CA_GET_CAP: - dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities"); - if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !"); - break; - case CA_GET_DESCR_INFO: - dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description"); - if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !"); - break; - case CA_SET_DESCR: - dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler"); - if ((ca_set_slot_descr()) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !"); - break; - case CA_SET_PID: - dprintk(verbose, DST_CA_INFO, 1, " Setting PID"); - if ((ca_set_pid()) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !"); - default: - result = -EOPNOTSUPP; - }; - free_mem_and_exit: - kfree (p_ca_message); - kfree (p_ca_slot_info); - kfree (p_ca_caps); - - mutex_unlock(&dst_ca_mutex); - return result; -} - -static int dst_ca_open(struct inode *inode, struct file *file) -{ - dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file); - try_module_get(THIS_MODULE); - - return 0; -} - -static int dst_ca_release(struct inode *inode, struct file *file) -{ - dprintk(verbose, DST_CA_DEBUG, 1, " Device closed."); - module_put(THIS_MODULE); - - return 0; -} - -static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) -{ - ssize_t bytes_read = 0; - - dprintk(verbose, DST_CA_DEBUG, 1, " Device read."); - - return bytes_read; -} - -static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) -{ - dprintk(verbose, DST_CA_DEBUG, 1, " Device write."); - - return 0; -} - -static const struct file_operations dst_ca_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = dst_ca_ioctl, - .open = dst_ca_open, - .release = dst_ca_release, - .read = dst_ca_read, - .write = dst_ca_write, - .llseek = noop_llseek, -}; - -static struct dvb_device dvbdev_ca = { - .priv = NULL, - .users = 1, - .readers = 1, - .writers = 1, - .fops = &dst_ca_fops -}; - -struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter) -{ - struct dvb_device *dvbdev; - - dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); - if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { - dst->dst_ca = dvbdev; - return dst->dst_ca; - } - - return NULL; -} - -EXPORT_SYMBOL(dst_ca_attach); - -MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver"); -MODULE_AUTHOR("Manu Abraham"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/bt8xx/dst_ca.h b/drivers/media/dvb/bt8xx/dst_ca.h deleted file mode 100644 index 59cd0ddd6d8e..000000000000 --- a/drivers/media/dvb/bt8xx/dst_ca.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - CA-driver for TwinHan DST Frontend/Card - - Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef _DST_CA_H_ -#define _DST_CA_H_ - -#define RETRIES 5 - - -#define CA_APP_INFO_ENQUIRY 0x9f8020 -#define CA_APP_INFO 0x9f8021 -#define CA_ENTER_MENU 0x9f8022 -#define CA_INFO_ENQUIRY 0x9f8030 -#define CA_INFO 0x9f8031 -#define CA_PMT 0x9f8032 -#define CA_PMT_REPLY 0x9f8033 - -#define CA_CLOSE_MMI 0x9f8800 -#define CA_DISPLAY_CONTROL 0x9f8801 -#define CA_DISPLAY_REPLY 0x9f8802 -#define CA_TEXT_LAST 0x9f8803 -#define CA_TEXT_MORE 0x9f8804 -#define CA_KEYPAD_CONTROL 0x9f8805 -#define CA_KEYPRESS 0x9f8806 - -#define CA_ENQUIRY 0x9f8807 -#define CA_ANSWER 0x9f8808 -#define CA_MENU_LAST 0x9f8809 -#define CA_MENU_MORE 0x9f880a -#define CA_MENU_ANSWER 0x9f880b -#define CA_LIST_LAST 0x9f880c -#define CA_LIST_MORE 0x9f880d - - -struct dst_ca_private { - struct dst_state *dst; - struct dvb_device *dvbdev; -}; - - -#endif diff --git a/drivers/media/dvb/bt8xx/dst_common.h b/drivers/media/dvb/bt8xx/dst_common.h deleted file mode 100644 index d70d98f1a571..000000000000 --- a/drivers/media/dvb/bt8xx/dst_common.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - Frontend-driver for TwinHan DST Frontend - - Copyright (C) 2003 Jamie Honan - Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef DST_COMMON_H -#define DST_COMMON_H - -#include -#include -#include -#include "bt878.h" - -#include "dst_ca.h" - - -#define NO_DELAY 0 -#define LONG_DELAY 1 -#define DEVICE_INIT 2 - -#define DELAY 1 - -#define DST_TYPE_IS_SAT 0 -#define DST_TYPE_IS_TERR 1 -#define DST_TYPE_IS_CABLE 2 -#define DST_TYPE_IS_ATSC 3 - -#define DST_TYPE_HAS_TS188 1 -#define DST_TYPE_HAS_TS204 2 -#define DST_TYPE_HAS_SYMDIV 4 -#define DST_TYPE_HAS_FW_1 8 -#define DST_TYPE_HAS_FW_2 16 -#define DST_TYPE_HAS_FW_3 32 -#define DST_TYPE_HAS_FW_BUILD 64 -#define DST_TYPE_HAS_OBS_REGS 128 -#define DST_TYPE_HAS_INC_COUNT 256 -#define DST_TYPE_HAS_MULTI_FE 512 -#define DST_TYPE_HAS_NEWTUNE_2 1024 -#define DST_TYPE_HAS_DBOARD 2048 -#define DST_TYPE_HAS_VLF 4096 - -/* Card capability list */ - -#define DST_TYPE_HAS_MAC 1 -#define DST_TYPE_HAS_DISEQC3 2 -#define DST_TYPE_HAS_DISEQC4 4 -#define DST_TYPE_HAS_DISEQC5 8 -#define DST_TYPE_HAS_MOTO 16 -#define DST_TYPE_HAS_CA 32 -#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */ -#define DST_TYPE_HAS_SESSION 128 - -#define TUNER_TYPE_MULTI 1 -#define TUNER_TYPE_UNKNOWN 2 -/* DVB-S */ -#define TUNER_TYPE_L64724 4 -#define TUNER_TYPE_STV0299 8 -#define TUNER_TYPE_MB86A15 16 - -/* DVB-T */ -#define TUNER_TYPE_TDA10046 32 - -/* ATSC */ -#define TUNER_TYPE_NXT200x 64 - - -#define RDC_8820_PIO_0_DISABLE 0 -#define RDC_8820_PIO_0_ENABLE 1 -#define RDC_8820_INT 2 -#define RDC_8820_RESET 4 - -/* DST Communication */ -#define GET_REPLY 1 -#define NO_REPLY 0 - -#define GET_ACK 1 -#define FIXED_COMM 8 - -#define ACK 0xff - -struct dst_state { - - struct i2c_adapter* i2c; - - struct bt878* bt; - - /* configuration settings */ - const struct dst_config* config; - - struct dvb_frontend frontend; - - /* private ASIC data */ - u8 tx_tuna[10]; - u8 rx_tuna[10]; - u8 rxbuffer[10]; - u8 diseq_flags; - u8 dst_type; - u32 type_flags; - u32 frequency; /* intermediate frequency in kHz for QPSK */ - fe_spectral_inversion_t inversion; - u32 symbol_rate; /* symbol rate in Symbols per second */ - fe_code_rate_t fec; - fe_sec_voltage_t voltage; - fe_sec_tone_mode_t tone; - u32 decode_freq; - u8 decode_lock; - u16 decode_strength; - u16 decode_snr; - unsigned long cur_jiff; - u8 k22; - u32 bandwidth; - u32 dst_hw_cap; - u8 dst_fw_version; - fe_sec_mini_cmd_t minicmd; - fe_modulation_t modulation; - u8 messages[256]; - u8 mac_address[8]; - u8 fw_version[8]; - u8 card_info[8]; - u8 vendor[8]; - u8 board_info[8]; - u32 tuner_type; - char *tuner_name; - struct mutex dst_mutex; - u8 fw_name[8]; - struct dvb_device *dst_ca; -}; - -struct tuner_types { - u32 tuner_type; - char *tuner_name; - char *board_name; - char *fw_name; -}; - -struct dst_types { - char *device_id; - int offset; - u8 dst_type; - u32 type_flags; - u32 dst_feature; - u32 tuner_type; -}; - -struct dst_config -{ - /* the ASIC i2c address */ - u8 demod_address; -}; - -int rdc_reset_state(struct dst_state *state); - -int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode); -int dst_pio_disable(struct dst_state *state); -int dst_error_recovery(struct dst_state* state); -int dst_error_bailout(struct dst_state *state); -int dst_comm_init(struct dst_state* state); - -int write_dst(struct dst_state *state, u8 * data, u8 len); -int read_dst(struct dst_state *state, u8 * ret, u8 len); -u8 dst_check_sum(u8 * buf, u32 len); -struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter); -struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter); - - -#endif // DST_COMMON_H diff --git a/drivers/media/dvb/bt8xx/dst_priv.h b/drivers/media/dvb/bt8xx/dst_priv.h deleted file mode 100644 index 3974a4c6ebe7..000000000000 --- a/drivers/media/dvb/bt8xx/dst_priv.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend - * - * Copyright (C) 2003 Jamie Honan - */ - -struct dst_gpio_enable { - u32 mask; - u32 enable; -}; - -struct dst_gpio_output { - u32 mask; - u32 highvals; -}; - -struct dst_gpio_read { - unsigned long value; -}; - -union dst_gpio_packet { - struct dst_gpio_enable enb; - struct dst_gpio_output outp; - struct dst_gpio_read rd; - int psize; -}; - -#define DST_IG_ENABLE 0 -#define DST_IG_WRITE 1 -#define DST_IG_READ 2 -#define DST_IG_TS 3 - -struct bt878; - -int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp); diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c deleted file mode 100644 index 81fab9adc1ca..000000000000 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ /dev/null @@ -1,975 +0,0 @@ -/* - * Bt8xx based DVB adapter driver - * - * Copyright (C) 2002,2003 Florian Schirmer - * - * This program is free software; 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) "dvb_bt8xx: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb-bt8xx.h" -#include "bt878.h" - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define dprintk( args... ) \ - do { \ - if (debug) printk(KERN_DEBUG args); \ - } while (0) - -#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ - -static void dvb_bt8xx_task(unsigned long data) -{ - struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data; - - //printk("%d ", card->bt->finished_block); - - while (card->bt->last_block != card->bt->finished_block) { - (card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) - (&card->demux, - &card->bt->buf_cpu[card->bt->last_block * - card->bt->block_bytes], - card->bt->block_bytes); - card->bt->last_block = (card->bt->last_block + 1) % - card->bt->block_count; - } -} - -static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux*dvbdmx = dvbdmxfeed->demux; - struct dvb_bt8xx_card *card = dvbdmx->priv; - int rc; - - dprintk("dvb_bt8xx: start_feed\n"); - - if (!dvbdmx->dmx.frontend) - return -EINVAL; - - mutex_lock(&card->lock); - card->nfeeds++; - rc = card->nfeeds; - if (card->nfeeds == 1) - bt878_start(card->bt, card->gpio_mode, - card->op_sync_orin, card->irq_err_ignore); - mutex_unlock(&card->lock); - return rc; -} - -static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct dvb_bt8xx_card *card = dvbdmx->priv; - - dprintk("dvb_bt8xx: stop_feed\n"); - - if (!dvbdmx->dmx.frontend) - return -EINVAL; - - mutex_lock(&card->lock); - card->nfeeds--; - if (card->nfeeds == 0) - bt878_stop(card->bt); - mutex_unlock(&card->lock); - - return 0; -} - -static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev) -{ - if ((adev->subsystem_vendor == bdev->subsystem_vendor) && - (adev->subsystem_device == bdev->subsystem_device) && - (adev->bus->number == bdev->bus->number) && - (PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn))) - return 1; - return 0; -} - -static struct bt878 __devinit *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev) -{ - unsigned int card_nr; - - /* Hmm, n squared. Hope n is small */ - for (card_nr = 0; card_nr < bt878_num; card_nr++) - if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev)) - return &bt878[card_nr]; - return NULL; -} - -static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) -{ - static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 }; - static u8 mt352_reset [] = { 0x50, 0x80 }; - static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; - static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 }; - static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 }; - static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; - - mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(fe, mt352_reset, sizeof(mt352_reset)); - mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); - mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend *fe, u8* pllbuf, int buf_len) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 div; - unsigned char bs = 0; - unsigned char cp = 0; - - if (buf_len < 5) - return -EINVAL; - - div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - - if (c->frequency < 542000000) - cp = 0xb4; - else if (c->frequency < 771000000) - cp = 0xbc; - else - cp = 0xf4; - - if (c->frequency == 0) - bs = 0x03; - else if (c->frequency < 443250000) - bs = 0x02; - else - bs = 0x08; - - pllbuf[0] = 0x60; - pllbuf[1] = div >> 8; - pllbuf[2] = div & 0xff; - pllbuf[3] = cp; - pllbuf[4] = bs; - - return 5; -} - -static struct mt352_config thomson_dtt7579_config = { - .demod_address = 0x0f, - .demod_init = thomson_dtt7579_demod_init, -}; - -static struct zl10353_config thomson_dtt7579_zl10353_config = { - .demod_address = 0x0f, -}; - -static int cx24108_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 freq = c->frequency; - int i, a, n, pump; - u32 band, pll; - u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000, - 1576000,1718000,1856000,2036000,2150000}; - u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000, - 0x00102000,0x00104000,0x00108000,0x00110000, - 0x00120000,0x00140000}; - - #define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */ - dprintk("cx24108 debug: entering SetTunerFreq, freq=%d\n", freq); - - /* This is really the bit driving the tuner chip cx24108 */ - - if (freq<950000) - freq = 950000; /* kHz */ - else if (freq>2150000) - freq = 2150000; /* satellite IF is 950..2150MHz */ - - /* decide which VCO to use for the input frequency */ - for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++); - dprintk("cx24108 debug: select vco #%d (f=%d)\n", i, freq); - band=bandsel[i]; - /* the gain values must be set by SetSymbolrate */ - /* compute the pll divider needed, from Conexant data sheet, - resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4, - depending on the divider bit. It is set to /4 on the 2 lowest - bands */ - n=((i<=2?2:1)*freq*10L)/(XTAL/100); - a=n%32; n/=32; if(a==0) n--; - pump=(freq<(osci[i-1]+osci[i])/2); - pll=0xf8000000| - ((pump?1:2)<<(14+11))| - ((n&0x1ff)<<(5+11))| - ((a&0x1f)<<11); - /* everything is shifted left 11 bits to left-align the bits in the - 32bit word. Output to the tuner goes MSB-aligned, after all */ - dprintk("cx24108 debug: pump=%d, n=%d, a=%d\n", pump, n, a); - cx24110_pll_write(fe,band); - /* set vga and vca to their widest-band settings, as a precaution. - SetSymbolrate might not be called to set this up */ - cx24110_pll_write(fe,0x500c0000); - cx24110_pll_write(fe,0x83f1f800); - cx24110_pll_write(fe,pll); - //writereg(client,0x56,0x7f); - - return 0; -} - -static int pinnsat_tuner_init(struct dvb_frontend* fe) -{ - struct dvb_bt8xx_card *card = fe->dvb->priv; - - bttv_gpio_enable(card->bttv_nr, 1, 1); /* output */ - bttv_write_gpio(card->bttv_nr, 1, 1); /* relay on */ - - return 0; -} - -static int pinnsat_tuner_sleep(struct dvb_frontend* fe) -{ - struct dvb_bt8xx_card *card = fe->dvb->priv; - - bttv_write_gpio(card->bttv_nr, 1, 0); /* relay off */ - - return 0; -} - -static struct cx24110_config pctvsat_config = { - .demod_address = 0x55, -}; - -static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; - u8 cfg, cpump, band_select; - u8 data[4]; - u32 div; - struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (36000000 + c->frequency + 83333) / 166666; - cfg = 0x88; - - if (c->frequency < 175000000) - cpump = 2; - else if (c->frequency < 390000000) - cpump = 1; - else if (c->frequency < 470000000) - cpump = 2; - else if (c->frequency < 750000000) - cpump = 2; - else - cpump = 3; - - if (c->frequency < 175000000) - band_select = 0x0e; - else if (c->frequency < 470000000) - band_select = 0x05; - else - band_select = 0x03; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = ((div >> 10) & 0x60) | cfg; - data[3] = (cpump << 6) | band_select; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(card->i2c_adapter, &msg, 1); - return (div * 166666 - 36000000); -} - -static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) -{ - struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; - - return request_firmware(fw, name, &bt->bt->dev->dev); -} - -static struct sp887x_config microtune_mt7202dtf_config = { - .demod_address = 0x70, - .request_firmware = microtune_mt7202dtf_request_firmware, -}; - -static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) -{ - static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; - static u8 mt352_reset [] = { 0x50, 0x80 }; - static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; - static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF, - 0x00, 0xFF, 0x00, 0x40, 0x40 }; - static u8 mt352_av771_extra[] = { 0xB5, 0x7A }; - static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; - - mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(fe, mt352_reset, sizeof(mt352_reset)); - mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg)); - udelay(2000); - mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra)); - mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 div; - unsigned char bs = 0; - unsigned char cp = 0; - - if (buf_len < 5) return -EINVAL; - - div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - - if (c->frequency < 150000000) - cp = 0xB4; - else if (c->frequency < 173000000) - cp = 0xBC; - else if (c->frequency < 250000000) - cp = 0xB4; - else if (c->frequency < 400000000) - cp = 0xBC; - else if (c->frequency < 420000000) - cp = 0xF4; - else if (c->frequency < 470000000) - cp = 0xFC; - else if (c->frequency < 600000000) - cp = 0xBC; - else if (c->frequency < 730000000) - cp = 0xF4; - else - cp = 0xFC; - - if (c->frequency < 150000000) - bs = 0x01; - else if (c->frequency < 173000000) - bs = 0x01; - else if (c->frequency < 250000000) - bs = 0x02; - else if (c->frequency < 400000000) - bs = 0x02; - else if (c->frequency < 420000000) - bs = 0x02; - else if (c->frequency < 470000000) - bs = 0x02; - else if (c->frequency < 600000000) - bs = 0x08; - else if (c->frequency < 730000000) - bs = 0x08; - else - bs = 0x08; - - pllbuf[0] = 0x61; - pllbuf[1] = div >> 8; - pllbuf[2] = div & 0xff; - pllbuf[3] = cp; - pllbuf[4] = bs; - - return 5; -} - -static struct mt352_config advbt771_samsung_tdtc9251dh0_config = { - .demod_address = 0x0f, - .demod_init = advbt771_samsung_tdtc9251dh0_demod_init, -}; - -static struct dst_config dst_config = { - .demod_address = 0x55, -}; - -static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) -{ - struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; - - return request_firmware(fw, name, &bt->bt->dev->dev); -} - -static void or51211_setmode(struct dvb_frontend * fe, int mode) -{ - struct dvb_bt8xx_card *bt = fe->dvb->priv; - bttv_write_gpio(bt->bttv_nr, 0x0002, mode); /* Reset */ - msleep(20); -} - -static void or51211_reset(struct dvb_frontend * fe) -{ - struct dvb_bt8xx_card *bt = fe->dvb->priv; - - /* RESET DEVICE - * reset is controlled by GPIO-0 - * when set to 0 causes reset and when to 1 for normal op - * must remain reset for 128 clock cycles on a 50Mhz clock - * also PRM1 PRM2 & PRM4 are controlled by GPIO-1,GPIO-2 & GPIO-4 - * We assume that the reset has be held low long enough or we - * have been reset by a power on. When the driver is unloaded - * reset set to 0 so if reloaded we have been reset. - */ - /* reset & PRM1,2&4 are outputs */ - int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F); - if (ret != 0) - printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR (%i)\n", ret); - bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000); /* Reset */ - msleep(20); - /* Now set for normal operation */ - bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001); - /* wait for operation to begin */ - msleep(500); -} - -static void or51211_sleep(struct dvb_frontend * fe) -{ - struct dvb_bt8xx_card *bt = fe->dvb->priv; - bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000); -} - -static struct or51211_config or51211_config = { - .demod_address = 0x15, - .request_firmware = or51211_request_firmware, - .setmode = or51211_setmode, - .reset = or51211_reset, - .sleep = or51211_sleep, -}; - -static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; - u8 buf[4]; - u32 div; - struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) }; - - div = (c->frequency + 36166667) / 166667; - - buf[0] = (div >> 8) & 0x7F; - buf[1] = div & 0xFF; - buf[2] = 0x85; - if ((c->frequency >= 47000000) && (c->frequency < 153000000)) - buf[3] = 0x01; - else if ((c->frequency >= 153000000) && (c->frequency < 430000000)) - buf[3] = 0x02; - else if ((c->frequency >= 430000000) && (c->frequency < 824000000)) - buf[3] = 0x0C; - else if ((c->frequency >= 824000000) && (c->frequency < 863000000)) - buf[3] = 0x8C; - else - return -EINVAL; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(card->i2c_adapter, &msg, 1); - return 0; -} - -static struct nxt6000_config vp3021_alps_tded4_config = { - .demod_address = 0x0a, - .clock_inversion = 1, -}; - -static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) -{ - static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; - static u8 mt352_reset [] = { 0x50, 0x80 }; - static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; - static u8 mt352_agc_cfg [] = { 0x67, 0x20, 0xa0 }; - static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; - - mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(fe, mt352_reset, sizeof(mt352_reset)); - mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg)); - mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) -{ - u32 div; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - - if (buf_len < 5) - return -EINVAL; - - div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - - pllbuf[0] = 0x61; - pllbuf[1] = (div >> 8) & 0x7F; - pllbuf[2] = div & 0xFF; - pllbuf[3] = 0x85; - - dprintk("frequency %u, div %u\n", c->frequency, div); - - if (c->frequency < 470000000) - pllbuf[4] = 0x02; - else if (c->frequency > 823000000) - pllbuf[4] = 0x88; - else - pllbuf[4] = 0x08; - - if (c->bandwidth_hz == 8000000) - pllbuf[4] |= 0x04; - - return 5; -} - -static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) -{ - /* - * Reset the frontend, must be called before trying - * to initialise the MT352 or mt352_attach - * will fail. Same goes for the nxt6000 frontend. - * - */ - - int ret = bttv_gpio_enable(bt->bttv_nr, 0x08, 0x08); - if (ret != 0) - printk(KERN_WARNING "digitv_alps_tded4: Init Error - Can't Reset DVR (%i)\n", ret); - - /* Pulse the reset line */ - bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ - bttv_write_gpio(bt->bttv_nr, 0x08, 0x00); /* Low */ - msleep(100); - - bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ -} - -static struct mt352_config digitv_alps_tded4_config = { - .demod_address = 0x0a, - .demod_init = digitv_alps_tded4_demod_init, -}; - -static struct lgdt330x_config tdvs_tua6034_config = { - .demod_address = 0x0e, - .demod_chip = LGDT3303, - .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ -}; - -static void lgdt330x_reset(struct dvb_bt8xx_card *bt) -{ - /* Set pin 27 of the lgdt3303 chip high to reset the frontend */ - - /* Pulse the reset line */ - bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ - bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000000); /* Low */ - msleep(100); - - bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ - msleep(100); -} - -static void frontend_init(struct dvb_bt8xx_card *card, u32 type) -{ - struct dst_state* state = NULL; - - switch(type) { - case BTTV_BOARD_DVICO_DVBT_LITE: - card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter); - - if (card->fe == NULL) - card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config, - card->i2c_adapter); - - if (card->fe != NULL) { - card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; - card->fe->ops.info.frequency_min = 174000000; - card->fe->ops.info.frequency_max = 862000000; - } - break; - - case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: - lgdt330x_reset(card); - card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); - if (card->fe != NULL) { - dvb_attach(simple_tuner_attach, card->fe, - card->i2c_adapter, 0x61, - TUNER_LG_TDVS_H06XF); - dprintk ("dvb_bt8xx: lgdt330x detected\n"); - } - break; - - case BTTV_BOARD_NEBULA_DIGITV: - /* - * It is possible to determine the correct frontend using the I2C bus (see the Nebula SDK); - * this would be a cleaner solution than trying each frontend in turn. - */ - - /* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */ - digitv_alps_tded4_reset(card); - card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter); - if (card->fe != NULL) { - card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params; - dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n"); - break; - } - - /* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */ - digitv_alps_tded4_reset(card); - card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter); - - if (card->fe != NULL) { - card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs; - dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n"); - } - break; - - case BTTV_BOARD_AVDVBT_761: - card->fe = dvb_attach(sp887x_attach, µtune_mt7202dtf_config, card->i2c_adapter); - if (card->fe) { - card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params; - } - break; - - case BTTV_BOARD_AVDVBT_771: - card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); - if (card->fe != NULL) { - card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; - card->fe->ops.info.frequency_min = 174000000; - card->fe->ops.info.frequency_max = 862000000; - } - break; - - case BTTV_BOARD_TWINHAN_DST: - /* DST is not a frontend driver !!! */ - state = kmalloc(sizeof (struct dst_state), GFP_KERNEL); - if (!state) { - pr_err("No memory\n"); - break; - } - /* Setup the Card */ - state->config = &dst_config; - state->i2c = card->i2c_adapter; - state->bt = card->bt; - state->dst_ca = NULL; - /* DST is not a frontend, attaching the ASIC */ - if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) { - pr_err("%s: Could not find a Twinhan DST\n", __func__); - break; - } - /* Attach other DST peripherals if any */ - /* Conditional Access device */ - card->fe = &state->frontend; - if (state->dst_hw_cap & DST_TYPE_HAS_CA) - dvb_attach(dst_ca_attach, state, &card->dvb_adapter); - break; - - case BTTV_BOARD_PINNACLESAT: - card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter); - if (card->fe) { - card->fe->ops.tuner_ops.init = pinnsat_tuner_init; - card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep; - card->fe->ops.tuner_ops.set_params = cx24108_tuner_set_params; - } - break; - - case BTTV_BOARD_PC_HDTV: - card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter); - if (card->fe != NULL) - dvb_attach(simple_tuner_attach, card->fe, - card->i2c_adapter, 0x61, - TUNER_PHILIPS_FCV1236D); - break; - } - - if (card->fe == NULL) - pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - card->bt->dev->vendor, - card->bt->dev->device, - card->bt->dev->subsystem_vendor, - card->bt->dev->subsystem_device); - else - if (dvb_register_frontend(&card->dvb_adapter, card->fe)) { - pr_err("Frontend registration failed!\n"); - dvb_frontend_detach(card->fe); - card->fe = NULL; - } -} - -static int __devinit dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) -{ - int result; - - result = dvb_register_adapter(&card->dvb_adapter, card->card_name, - THIS_MODULE, &card->bt->dev->dev, - adapter_nr); - if (result < 0) { - pr_err("dvb_register_adapter failed (errno = %d)\n", result); - return result; - } - card->dvb_adapter.priv = card; - - card->bt->adapter = card->i2c_adapter; - - memset(&card->demux, 0, sizeof(struct dvb_demux)); - - card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING; - - card->demux.priv = card; - card->demux.filternum = 256; - card->demux.feednum = 256; - card->demux.start_feed = dvb_bt8xx_start_feed; - card->demux.stop_feed = dvb_bt8xx_stop_feed; - card->demux.write_to_decoder = NULL; - - result = dvb_dmx_init(&card->demux); - if (result < 0) { - pr_err("dvb_dmx_init failed (errno = %d)\n", result); - goto err_unregister_adaptor; - } - - card->dmxdev.filternum = 256; - card->dmxdev.demux = &card->demux.dmx; - card->dmxdev.capabilities = 0; - - result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter); - if (result < 0) { - pr_err("dvb_dmxdev_init failed (errno = %d)\n", result); - goto err_dmx_release; - } - - card->fe_hw.source = DMX_FRONTEND_0; - - result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw); - if (result < 0) { - pr_err("dvb_dmx_init failed (errno = %d)\n", result); - goto err_dmxdev_release; - } - - card->fe_mem.source = DMX_MEMORY_FE; - - result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem); - if (result < 0) { - pr_err("dvb_dmx_init failed (errno = %d)\n", result); - goto err_remove_hw_frontend; - } - - result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw); - if (result < 0) { - pr_err("dvb_dmx_init failed (errno = %d)\n", result); - goto err_remove_mem_frontend; - } - - result = dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx); - if (result < 0) { - pr_err("dvb_net_init failed (errno = %d)\n", result); - goto err_disconnect_frontend; - } - - tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card); - - frontend_init(card, type); - - return 0; - -err_disconnect_frontend: - card->demux.dmx.disconnect_frontend(&card->demux.dmx); -err_remove_mem_frontend: - card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); -err_remove_hw_frontend: - card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); -err_dmxdev_release: - dvb_dmxdev_release(&card->dmxdev); -err_dmx_release: - dvb_dmx_release(&card->demux); -err_unregister_adaptor: - dvb_unregister_adapter(&card->dvb_adapter); - return result; -} - -static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub) -{ - struct dvb_bt8xx_card *card; - struct pci_dev* bttv_pci_dev; - int ret; - - if (!(card = kzalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL))) - return -ENOMEM; - - mutex_init(&card->lock); - card->bttv_nr = sub->core->nr; - strlcpy(card->card_name, sub->core->v4l2_dev.name, sizeof(card->card_name)); - card->i2c_adapter = &sub->core->i2c_adap; - - switch(sub->core->type) { - case BTTV_BOARD_PINNACLESAT: - card->gpio_mode = 0x0400c060; - /* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR, - BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */ - card->op_sync_orin = BT878_RISC_SYNC_MASK; - card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; - break; - - case BTTV_BOARD_DVICO_DVBT_LITE: - card->gpio_mode = 0x0400C060; - card->op_sync_orin = BT878_RISC_SYNC_MASK; - card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; - /* 26, 15, 14, 6, 5 - * A_PWRDN DA_DPM DA_SBR DA_IOM_DA - * DA_APP(parallel) */ - break; - - case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: - card->gpio_mode = 0x0400c060; - card->op_sync_orin = BT878_RISC_SYNC_MASK; - card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; - break; - - case BTTV_BOARD_NEBULA_DIGITV: - case BTTV_BOARD_AVDVBT_761: - card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5); - card->op_sync_orin = BT878_RISC_SYNC_MASK; - card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; - /* A_PWRDN DA_SBR DA_APP (high speed serial) */ - break; - - case BTTV_BOARD_AVDVBT_771: //case 0x07711461: - card->gpio_mode = 0x0400402B; - card->op_sync_orin = BT878_RISC_SYNC_MASK; - card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; - /* A_PWRDN DA_SBR DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/ - break; - - case BTTV_BOARD_TWINHAN_DST: - card->gpio_mode = 0x2204f2c; - card->op_sync_orin = BT878_RISC_SYNC_MASK; - card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR | - BT878_APPERR | BT878_AFBUS; - /* 25,21,14,11,10,9,8,3,2 then - * 0x33 = 5,4,1,0 - * A_SEL=SML, DA_MLB, DA_SBR, - * DA_SDR=f, fifo trigger = 32 DWORDS - * IOM = 0 == audio A/D - * DPM = 0 == digital audio mode - * == async data parallel port - * then 0x33 (13 is set by start_capture) - * DA_APP = async data parallel port, - * ACAP_EN = 1, - * RISC+FIFO ENABLE */ - break; - - case BTTV_BOARD_PC_HDTV: - card->gpio_mode = 0x0100EC7B; - card->op_sync_orin = BT878_RISC_SYNC_MASK; - card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; - break; - - default: - pr_err("Unknown bttv card type: %d\n", sub->core->type); - kfree(card); - return -ENODEV; - } - - dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name); - - if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) { - pr_err("no pci device for card %d\n", card->bttv_nr); - kfree(card); - return -ENODEV; - } - - if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) { - pr_err("unable to determine DMA core of card %d,\n", card->bttv_nr); - pr_err("if you have the ALSA bt87x audio driver installed, try removing it.\n"); - - kfree(card); - return -ENODEV; - } - - mutex_init(&card->bt->gpio_lock); - card->bt->bttv_nr = sub->core->nr; - - if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) { - kfree(card); - return ret; - } - - dev_set_drvdata(&sub->dev, card); - return 0; -} - -static void dvb_bt8xx_remove(struct bttv_sub_device *sub) -{ - struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev); - - dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr); - - bt878_stop(card->bt); - tasklet_kill(&card->bt->tasklet); - dvb_net_release(&card->dvbnet); - card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); - card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); - dvb_dmxdev_release(&card->dmxdev); - dvb_dmx_release(&card->demux); - if (card->fe) { - dvb_unregister_frontend(card->fe); - dvb_frontend_detach(card->fe); - } - dvb_unregister_adapter(&card->dvb_adapter); - - kfree(card); -} - -static struct bttv_sub_driver driver = { - .drv = { - .name = "dvb-bt8xx", - }, - .probe = dvb_bt8xx_probe, - .remove = dvb_bt8xx_remove, - /* FIXME: - * .shutdown = dvb_bt8xx_shutdown, - * .suspend = dvb_bt8xx_suspend, - * .resume = dvb_bt8xx_resume, - */ -}; - -static int __init dvb_bt8xx_init(void) -{ - return bttv_sub_register(&driver, "dvb"); -} - -static void __exit dvb_bt8xx_exit(void) -{ - bttv_sub_unregister(&driver); -} - -module_init(dvb_bt8xx_init); -module_exit(dvb_bt8xx_exit); - -MODULE_DESCRIPTION("Bt8xx based DVB adapter driver"); -MODULE_AUTHOR("Florian Schirmer "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h deleted file mode 100644 index 4499ed2ac0ed..000000000000 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Bt8xx based DVB adapter driver - * - * Copyright (C) 2002,2003 Florian Schirmer - * Copyright (C) 2002 Peter Hettkamp - * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH - * Copyright (C) 1998,1999 Christian Theiss - * - * This program is free software; 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_BT8XX_H -#define DVB_BT8XX_H - -#include -#include -#include "dvbdev.h" -#include "dvb_net.h" -#include "bttv.h" -#include "mt352.h" -#include "sp887x.h" -#include "dst_common.h" -#include "nxt6000.h" -#include "cx24110.h" -#include "or51211.h" -#include "lgdt330x.h" -#include "zl10353.h" -#include "tuner-simple.h" - -struct dvb_bt8xx_card { - struct mutex lock; - int nfeeds; - char card_name[32]; - struct dvb_adapter dvb_adapter; - struct bt878 *bt; - unsigned int bttv_nr; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dmx_frontend fe_hw; - struct dmx_frontend fe_mem; - u32 gpio_mode; - u32 op_sync_orin; - u32 irq_err_ignore; - struct i2c_adapter *i2c_adapter; - struct dvb_net dvbnet; - - struct dvb_frontend* fe; -}; - -#endif /* DVB_BT8XX_H */ diff --git a/drivers/media/dvb/ddbridge/Kconfig b/drivers/media/dvb/ddbridge/Kconfig deleted file mode 100644 index d099e1a12c85..000000000000 --- a/drivers/media/dvb/ddbridge/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -config DVB_DDBRIDGE - tristate "Digital Devices bridge support" - depends on DVB_CORE && PCI && I2C - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE - ---help--- - Support for cards with the Digital Devices PCI express bridge: - - Octopus PCIe Bridge - - Octopus mini PCIe Bridge - - Octopus LE - - DuoFlex S2 Octopus - - DuoFlex CT Octopus - - cineS2(v6) - - Say Y if you own such a card and want to use it. diff --git a/drivers/media/dvb/ddbridge/Makefile b/drivers/media/dvb/ddbridge/Makefile deleted file mode 100644 index 9d083c98ce58..000000000000 --- a/drivers/media/dvb/ddbridge/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# Makefile for the ddbridge device driver -# - -ddbridge-objs := ddbridge-core.o - -obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o - -ccflags-y += -Idrivers/media/dvb-core/ -ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ - -# For the staging CI driver cxd2099 -ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c deleted file mode 100644 index ebf3f05839d2..000000000000 --- a/drivers/media/dvb/ddbridge/ddbridge-core.c +++ /dev/null @@ -1,1723 +0,0 @@ -/* - * ddbridge.c: Digital Devices PCIe bridge driver - * - * Copyright (C) 2010-2011 Digital Devices GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ddbridge.h" - -#include "ddbridge-regs.h" - -#include "tda18271c2dd.h" -#include "stv6110x.h" -#include "stv090x.h" -#include "lnbh24.h" -#include "drxk.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -/* MSI had problems with lost interrupts, fixed but needs testing */ -#undef CONFIG_PCI_MSI - -/******************************************************************************/ - -static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) -{ - struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1 } }; - return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; -} - -static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val) -{ - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = ®, .len = 1 }, - {.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1 } }; - return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; -} - -static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, - u16 reg, u8 *val) -{ - u8 msg[2] = {reg>>8, reg&0xff}; - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = msg, .len = 2}, - {.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1} }; - return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; -} - -static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) -{ - struct ddb *dev = i2c->dev; - int stat; - u32 val; - - i2c->done = 0; - ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND); - stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ); - if (stat <= 0) { - printk(KERN_ERR "I2C timeout\n"); - { /* MSI debugging*/ - u32 istat = ddbreadl(INTERRUPT_STATUS); - printk(KERN_ERR "IRS %08x\n", istat); - ddbwritel(istat, INTERRUPT_ACK); - } - return -EIO; - } - val = ddbreadl(i2c->regs+I2C_COMMAND); - if (val & 0x70000) - return -EIO; - return 0; -} - -static int ddb_i2c_master_xfer(struct i2c_adapter *adapter, - struct i2c_msg msg[], int num) -{ - struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter); - struct ddb *dev = i2c->dev; - u8 addr = 0; - - if (num) - addr = msg[0].addr; - - if (num == 2 && msg[1].flags & I2C_M_RD && - !(msg[0].flags & I2C_M_RD)) { - memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf, - msg[0].buf, msg[0].len); - ddbwritel(msg[0].len|(msg[1].len << 16), - i2c->regs+I2C_TASKLENGTH); - if (!ddb_i2c_cmd(i2c, addr, 1)) { - memcpy_fromio(msg[1].buf, - dev->regs + I2C_TASKMEM_BASE + i2c->rbuf, - msg[1].len); - return num; - } - } - - if (num == 1 && !(msg[0].flags & I2C_M_RD)) { - ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len); - ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH); - if (!ddb_i2c_cmd(i2c, addr, 2)) - return num; - } - if (num == 1 && (msg[0].flags & I2C_M_RD)) { - ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH); - if (!ddb_i2c_cmd(i2c, addr, 3)) { - ddbcpyfrom(msg[0].buf, - I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len); - return num; - } - } - return -EIO; -} - - -static u32 ddb_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -struct i2c_algorithm ddb_i2c_algo = { - .master_xfer = ddb_i2c_master_xfer, - .functionality = ddb_i2c_functionality, -}; - -static void ddb_i2c_release(struct ddb *dev) -{ - int i; - struct ddb_i2c *i2c; - struct i2c_adapter *adap; - - for (i = 0; i < dev->info->port_num; i++) { - i2c = &dev->i2c[i]; - adap = &i2c->adap; - i2c_del_adapter(adap); - } -} - -static int ddb_i2c_init(struct ddb *dev) -{ - int i, j, stat = 0; - struct ddb_i2c *i2c; - struct i2c_adapter *adap; - - for (i = 0; i < dev->info->port_num; i++) { - i2c = &dev->i2c[i]; - i2c->dev = dev; - i2c->nr = i; - i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4); - i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8); - i2c->regs = 0x80 + i * 0x20; - ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING); - ddbwritel((i2c->rbuf << 16) | i2c->wbuf, - i2c->regs + I2C_TASKADDRESS); - init_waitqueue_head(&i2c->wq); - - adap = &i2c->adap; - i2c_set_adapdata(adap, i2c); -#ifdef I2C_ADAP_CLASS_TV_DIGITAL - adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG; -#else -#ifdef I2C_CLASS_TV_ANALOG - adap->class = I2C_CLASS_TV_ANALOG; -#endif -#endif - strcpy(adap->name, "ddbridge"); - adap->algo = &ddb_i2c_algo; - adap->algo_data = (void *)i2c; - adap->dev.parent = &dev->pdev->dev; - stat = i2c_add_adapter(adap); - if (stat) - break; - } - if (stat) - for (j = 0; j < i; j++) { - i2c = &dev->i2c[j]; - adap = &i2c->adap; - i2c_del_adapter(adap); - } - return stat; -} - - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -#if 0 -static void set_table(struct ddb *dev, u32 off, - dma_addr_t *pbuf, u32 num) -{ - u32 i, base; - u64 mem; - - base = DMA_BASE_ADDRESS_TABLE + off; - for (i = 0; i < num; i++) { - mem = pbuf[i]; - ddbwritel(mem & 0xffffffff, base + i * 8); - ddbwritel(mem >> 32, base + i * 8 + 4); - } -} -#endif - -static void ddb_address_table(struct ddb *dev) -{ - u32 i, j, base; - u64 mem; - dma_addr_t *pbuf; - - for (i = 0; i < dev->info->port_num * 2; i++) { - base = DMA_BASE_ADDRESS_TABLE + i * 0x100; - pbuf = dev->input[i].pbuf; - for (j = 0; j < dev->input[i].dma_buf_num; j++) { - mem = pbuf[j]; - ddbwritel(mem & 0xffffffff, base + j * 8); - ddbwritel(mem >> 32, base + j * 8 + 4); - } - } - for (i = 0; i < dev->info->port_num; i++) { - base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100; - pbuf = dev->output[i].pbuf; - for (j = 0; j < dev->output[i].dma_buf_num; j++) { - mem = pbuf[j]; - ddbwritel(mem & 0xffffffff, base + j * 8); - ddbwritel(mem >> 32, base + j * 8 + 4); - } - } -} - -static void io_free(struct pci_dev *pdev, u8 **vbuf, - dma_addr_t *pbuf, u32 size, int num) -{ - int i; - - for (i = 0; i < num; i++) { - if (vbuf[i]) { - pci_free_consistent(pdev, size, vbuf[i], pbuf[i]); - vbuf[i] = 0; - } - } -} - -static int io_alloc(struct pci_dev *pdev, u8 **vbuf, - dma_addr_t *pbuf, u32 size, int num) -{ - int i; - - for (i = 0; i < num; i++) { - vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]); - if (!vbuf[i]) - return -ENOMEM; - } - return 0; -} - -static int ddb_buffers_alloc(struct ddb *dev) -{ - int i; - struct ddb_port *port; - - for (i = 0; i < dev->info->port_num; i++) { - port = &dev->port[i]; - switch (port->class) { - case DDB_PORT_TUNER: - if (io_alloc(dev->pdev, port->input[0]->vbuf, - port->input[0]->pbuf, - port->input[0]->dma_buf_size, - port->input[0]->dma_buf_num) < 0) - return -1; - if (io_alloc(dev->pdev, port->input[1]->vbuf, - port->input[1]->pbuf, - port->input[1]->dma_buf_size, - port->input[1]->dma_buf_num) < 0) - return -1; - break; - case DDB_PORT_CI: - if (io_alloc(dev->pdev, port->input[0]->vbuf, - port->input[0]->pbuf, - port->input[0]->dma_buf_size, - port->input[0]->dma_buf_num) < 0) - return -1; - if (io_alloc(dev->pdev, port->output->vbuf, - port->output->pbuf, - port->output->dma_buf_size, - port->output->dma_buf_num) < 0) - return -1; - break; - default: - break; - } - } - ddb_address_table(dev); - return 0; -} - -static void ddb_buffers_free(struct ddb *dev) -{ - int i; - struct ddb_port *port; - - for (i = 0; i < dev->info->port_num; i++) { - port = &dev->port[i]; - io_free(dev->pdev, port->input[0]->vbuf, - port->input[0]->pbuf, - port->input[0]->dma_buf_size, - port->input[0]->dma_buf_num); - io_free(dev->pdev, port->input[1]->vbuf, - port->input[1]->pbuf, - port->input[1]->dma_buf_size, - port->input[1]->dma_buf_num); - io_free(dev->pdev, port->output->vbuf, - port->output->pbuf, - port->output->dma_buf_size, - port->output->dma_buf_num); - } -} - -static void ddb_input_start(struct ddb_input *input) -{ - struct ddb *dev = input->port->dev; - - spin_lock_irq(&input->lock); - input->cbuf = 0; - input->coff = 0; - - /* reset */ - ddbwritel(0, TS_INPUT_CONTROL(input->nr)); - ddbwritel(2, TS_INPUT_CONTROL(input->nr)); - ddbwritel(0, TS_INPUT_CONTROL(input->nr)); - - ddbwritel((1 << 16) | - (input->dma_buf_num << 11) | - (input->dma_buf_size >> 7), - DMA_BUFFER_SIZE(input->nr)); - ddbwritel(0, DMA_BUFFER_ACK(input->nr)); - - ddbwritel(1, DMA_BASE_WRITE); - ddbwritel(3, DMA_BUFFER_CONTROL(input->nr)); - ddbwritel(9, TS_INPUT_CONTROL(input->nr)); - input->running = 1; - spin_unlock_irq(&input->lock); -} - -static void ddb_input_stop(struct ddb_input *input) -{ - struct ddb *dev = input->port->dev; - - spin_lock_irq(&input->lock); - ddbwritel(0, TS_INPUT_CONTROL(input->nr)); - ddbwritel(0, DMA_BUFFER_CONTROL(input->nr)); - input->running = 0; - spin_unlock_irq(&input->lock); -} - -static void ddb_output_start(struct ddb_output *output) -{ - struct ddb *dev = output->port->dev; - - spin_lock_irq(&output->lock); - output->cbuf = 0; - output->coff = 0; - ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); - ddbwritel(2, TS_OUTPUT_CONTROL(output->nr)); - ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); - ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr)); - ddbwritel((1 << 16) | - (output->dma_buf_num << 11) | - (output->dma_buf_size >> 7), - DMA_BUFFER_SIZE(output->nr + 8)); - ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8)); - - ddbwritel(1, DMA_BASE_READ); - ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8)); - /* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */ - ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr)); - output->running = 1; - spin_unlock_irq(&output->lock); -} - -static void ddb_output_stop(struct ddb_output *output) -{ - struct ddb *dev = output->port->dev; - - spin_lock_irq(&output->lock); - ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); - ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8)); - output->running = 0; - spin_unlock_irq(&output->lock); -} - -static u32 ddb_output_free(struct ddb_output *output) -{ - u32 idx, off, stat = output->stat; - s32 diff; - - idx = (stat >> 11) & 0x1f; - off = (stat & 0x7ff) << 7; - - if (output->cbuf != idx) { - if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && - (output->dma_buf_size - output->coff <= 188)) - return 0; - return 188; - } - diff = off - output->coff; - if (diff <= 0 || diff > 188) - return 188; - return 0; -} - -static ssize_t ddb_output_write(struct ddb_output *output, - const u8 *buf, size_t count) -{ - struct ddb *dev = output->port->dev; - u32 idx, off, stat = output->stat; - u32 left = count, len; - - idx = (stat >> 11) & 0x1f; - off = (stat & 0x7ff) << 7; - - while (left) { - len = output->dma_buf_size - output->coff; - if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && - (off == 0)) { - if (len <= 188) - break; - len -= 188; - } - if (output->cbuf == idx) { - if (off > output->coff) { -#if 1 - len = off - output->coff; - len -= (len % 188); - if (len <= 188) - -#endif - break; - len -= 188; - } - } - if (len > left) - len = left; - if (copy_from_user(output->vbuf[output->cbuf] + output->coff, - buf, len)) - return -EIO; - left -= len; - buf += len; - output->coff += len; - if (output->coff == output->dma_buf_size) { - output->coff = 0; - output->cbuf = ((output->cbuf + 1) % output->dma_buf_num); - } - ddbwritel((output->cbuf << 11) | (output->coff >> 7), - DMA_BUFFER_ACK(output->nr + 8)); - } - return count - left; -} - -static u32 ddb_input_avail(struct ddb_input *input) -{ - struct ddb *dev = input->port->dev; - u32 idx, off, stat = input->stat; - u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr)); - - idx = (stat >> 11) & 0x1f; - off = (stat & 0x7ff) << 7; - - if (ctrl & 4) { - printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl); - ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr)); - return 0; - } - if (input->cbuf != idx) - return 188; - return 0; -} - -static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count) -{ - struct ddb *dev = input->port->dev; - u32 left = count; - u32 idx, free, stat = input->stat; - int ret; - - idx = (stat >> 11) & 0x1f; - - while (left) { - if (input->cbuf == idx) - return count - left; - free = input->dma_buf_size - input->coff; - if (free > left) - free = left; - ret = copy_to_user(buf, input->vbuf[input->cbuf] + - input->coff, free); - if (ret) - return -EFAULT; - input->coff += free; - if (input->coff == input->dma_buf_size) { - input->coff = 0; - input->cbuf = (input->cbuf+1) % input->dma_buf_num; - } - left -= free; - ddbwritel((input->cbuf << 11) | (input->coff >> 7), - DMA_BUFFER_ACK(input->nr)); - } - return count; -} - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -#if 0 -static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe) -{ - int i; - - for (i = 0; i < dev->info->port_num * 2; i++) { - if (dev->input[i].fe == fe) - return &dev->input[i]; - } - return NULL; -} -#endif - -static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct ddb_input *input = fe->sec_priv; - struct ddb_port *port = input->port; - int status; - - if (enable) { - mutex_lock(&port->i2c_gate_lock); - status = input->gate_ctrl(fe, 1); - } else { - status = input->gate_ctrl(fe, 0); - mutex_unlock(&port->i2c_gate_lock); - } - return status; -} - -static int demod_attach_drxk(struct ddb_input *input) -{ - struct i2c_adapter *i2c = &input->port->i2c->adap; - struct dvb_frontend *fe; - struct drxk_config config; - - memset(&config, 0, sizeof(config)); - config.microcode_name = "drxk_a3.mc"; - config.qam_demod_parameter_count = 4; - config.adr = 0x29 + (input->nr & 1); - - fe = input->fe = dvb_attach(drxk_attach, &config, i2c); - if (!input->fe) { - printk(KERN_ERR "No DRXK found!\n"); - return -ENODEV; - } - fe->sec_priv = input; - input->gate_ctrl = fe->ops.i2c_gate_ctrl; - fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; - return 0; -} - -static int tuner_attach_tda18271(struct ddb_input *input) -{ - struct i2c_adapter *i2c = &input->port->i2c->adap; - struct dvb_frontend *fe; - - if (input->fe->ops.i2c_gate_ctrl) - input->fe->ops.i2c_gate_ctrl(input->fe, 1); - fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60); - if (!fe) { - printk(KERN_ERR "No TDA18271 found!\n"); - return -ENODEV; - } - if (input->fe->ops.i2c_gate_ctrl) - input->fe->ops.i2c_gate_ctrl(input->fe, 0); - return 0; -} - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -static struct stv090x_config stv0900 = { - .device = STV0900, - .demod_mode = STV090x_DUAL, - .clk_mode = STV090x_CLK_EXT, - - .xtal = 27000000, - .address = 0x69, - - .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - - .repeater_level = STV090x_RPTLEVEL_16, - - .adc1_range = STV090x_ADC_1Vpp, - .adc2_range = STV090x_ADC_1Vpp, - - .diseqc_envelope_mode = true, -}; - -static struct stv090x_config stv0900_aa = { - .device = STV0900, - .demod_mode = STV090x_DUAL, - .clk_mode = STV090x_CLK_EXT, - - .xtal = 27000000, - .address = 0x68, - - .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - - .repeater_level = STV090x_RPTLEVEL_16, - - .adc1_range = STV090x_ADC_1Vpp, - .adc2_range = STV090x_ADC_1Vpp, - - .diseqc_envelope_mode = true, -}; - -static struct stv6110x_config stv6110a = { - .addr = 0x60, - .refclk = 27000000, - .clk_div = 1, -}; - -static struct stv6110x_config stv6110b = { - .addr = 0x63, - .refclk = 27000000, - .clk_div = 1, -}; - -static int demod_attach_stv0900(struct ddb_input *input, int type) -{ - struct i2c_adapter *i2c = &input->port->i2c->adap; - struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; - - input->fe = dvb_attach(stv090x_attach, feconf, i2c, - (input->nr & 1) ? STV090x_DEMODULATOR_1 - : STV090x_DEMODULATOR_0); - if (!input->fe) { - printk(KERN_ERR "No STV0900 found!\n"); - return -ENODEV; - } - if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0, - 0, (input->nr & 1) ? - (0x09 - type) : (0x0b - type))) { - printk(KERN_ERR "No LNBH24 found!\n"); - return -ENODEV; - } - return 0; -} - -static int tuner_attach_stv6110(struct ddb_input *input, int type) -{ - struct i2c_adapter *i2c = &input->port->i2c->adap; - struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; - struct stv6110x_config *tunerconf = (input->nr & 1) ? - &stv6110b : &stv6110a; - struct stv6110x_devctl *ctl; - - ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c); - if (!ctl) { - printk(KERN_ERR "No STV6110X found!\n"); - return -ENODEV; - } - printk(KERN_INFO "attach tuner input %d adr %02x\n", - input->nr, tunerconf->addr); - - feconf->tuner_init = ctl->tuner_init; - feconf->tuner_sleep = ctl->tuner_sleep; - feconf->tuner_set_mode = ctl->tuner_set_mode; - feconf->tuner_set_frequency = ctl->tuner_set_frequency; - feconf->tuner_get_frequency = ctl->tuner_get_frequency; - feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; - feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; - feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; - feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; - feconf->tuner_set_refclk = ctl->tuner_set_refclk; - feconf->tuner_get_status = ctl->tuner_get_status; - - return 0; -} - -static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, - int (*start_feed)(struct dvb_demux_feed *), - int (*stop_feed)(struct dvb_demux_feed *), - void *priv) -{ - dvbdemux->priv = priv; - - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = start_feed; - dvbdemux->stop_feed = stop_feed; - dvbdemux->write_to_decoder = NULL; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - return dvb_dmx_init(dvbdemux); -} - -static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, - struct dvb_demux *dvbdemux, - struct dmx_frontend *hw_frontend, - struct dmx_frontend *mem_frontend, - struct dvb_adapter *dvb_adapter) -{ - int ret; - - dmxdev->filternum = 256; - dmxdev->demux = &dvbdemux->dmx; - dmxdev->capabilities = 0; - ret = dvb_dmxdev_init(dmxdev, dvb_adapter); - if (ret < 0) - return ret; - - hw_frontend->source = DMX_FRONTEND_0; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); - mem_frontend->source = DMX_MEMORY_FE; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); - return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); -} - -static int start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct ddb_input *input = dvbdmx->priv; - - if (!input->users) - ddb_input_start(input); - - return ++input->users; -} - -static int stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct ddb_input *input = dvbdmx->priv; - - if (--input->users) - return input->users; - - ddb_input_stop(input); - return 0; -} - - -static void dvb_input_detach(struct ddb_input *input) -{ - struct dvb_adapter *adap = &input->adap; - struct dvb_demux *dvbdemux = &input->demux; - - switch (input->attached) { - case 5: - if (input->fe2) - dvb_unregister_frontend(input->fe2); - if (input->fe) { - dvb_unregister_frontend(input->fe); - dvb_frontend_detach(input->fe); - input->fe = NULL; - } - case 4: - dvb_net_release(&input->dvbnet); - - case 3: - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, - &input->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, - &input->mem_frontend); - dvb_dmxdev_release(&input->dmxdev); - - case 2: - dvb_dmx_release(&input->demux); - - case 1: - dvb_unregister_adapter(adap); - } - input->attached = 0; -} - -static int dvb_input_attach(struct ddb_input *input) -{ - int ret; - struct ddb_port *port = input->port; - struct dvb_adapter *adap = &input->adap; - struct dvb_demux *dvbdemux = &input->demux; - - ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE, - &input->port->dev->pdev->dev, - adapter_nr); - if (ret < 0) { - printk(KERN_ERR "ddbridge: Could not register adapter." - "Check if you enabled enough adapters in dvb-core!\n"); - return ret; - } - input->attached = 1; - - ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", - start_feed, - stop_feed, input); - if (ret < 0) - return ret; - input->attached = 2; - - ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux, - &input->hw_frontend, - &input->mem_frontend, adap); - if (ret < 0) - return ret; - input->attached = 3; - - ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux); - if (ret < 0) - return ret; - input->attached = 4; - - input->fe = 0; - switch (port->type) { - case DDB_TUNER_DVBS_ST: - if (demod_attach_stv0900(input, 0) < 0) - return -ENODEV; - if (tuner_attach_stv6110(input, 0) < 0) - return -ENODEV; - if (input->fe) { - if (dvb_register_frontend(adap, input->fe) < 0) - return -ENODEV; - } - break; - case DDB_TUNER_DVBS_ST_AA: - if (demod_attach_stv0900(input, 1) < 0) - return -ENODEV; - if (tuner_attach_stv6110(input, 1) < 0) - return -ENODEV; - if (input->fe) { - if (dvb_register_frontend(adap, input->fe) < 0) - return -ENODEV; - } - break; - case DDB_TUNER_DVBCT_TR: - if (demod_attach_drxk(input) < 0) - return -ENODEV; - if (tuner_attach_tda18271(input) < 0) - return -ENODEV; - if (input->fe) { - if (dvb_register_frontend(adap, input->fe) < 0) - return -ENODEV; - } - if (input->fe2) { - if (dvb_register_frontend(adap, input->fe2) < 0) - return -ENODEV; - input->fe2->tuner_priv = input->fe->tuner_priv; - memcpy(&input->fe2->ops.tuner_ops, - &input->fe->ops.tuner_ops, - sizeof(struct dvb_tuner_ops)); - } - break; - } - input->attached = 5; - return 0; -} - -/****************************************************************************/ -/****************************************************************************/ - -static ssize_t ts_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ddb_output *output = dvbdev->priv; - size_t left = count; - int stat; - - while (left) { - if (ddb_output_free(output) < 188) { - if (file->f_flags & O_NONBLOCK) - break; - if (wait_event_interruptible( - output->wq, ddb_output_free(output) >= 188) < 0) - break; - } - stat = ddb_output_write(output, buf, left); - if (stat < 0) - break; - buf += stat; - left -= stat; - } - return (left == count) ? -EAGAIN : (count - left); -} - -static ssize_t ts_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ddb_output *output = dvbdev->priv; - struct ddb_input *input = output->port->input[0]; - int left, read; - - count -= count % 188; - left = count; - while (left) { - if (ddb_input_avail(input) < 188) { - if (file->f_flags & O_NONBLOCK) - break; - if (wait_event_interruptible( - input->wq, ddb_input_avail(input) >= 188) < 0) - break; - } - read = ddb_input_read(input, buf, left); - if (read < 0) - return read; - left -= read; - buf += read; - } - return (left == count) ? -EAGAIN : (count - left); -} - -static unsigned int ts_poll(struct file *file, poll_table *wait) -{ - /* - struct dvb_device *dvbdev = file->private_data; - struct ddb_output *output = dvbdev->priv; - struct ddb_input *input = output->port->input[0]; - */ - unsigned int mask = 0; - -#if 0 - if (data_avail_to_read) - mask |= POLLIN | POLLRDNORM; - if (data_avail_to_write) - mask |= POLLOUT | POLLWRNORM; - - poll_wait(file, &read_queue, wait); - poll_wait(file, &write_queue, wait); -#endif - return mask; -} - -static const struct file_operations ci_fops = { - .owner = THIS_MODULE, - .read = ts_read, - .write = ts_write, - .open = dvb_generic_open, - .release = dvb_generic_release, - .poll = ts_poll, - .mmap = 0, -}; - -static struct dvb_device dvbdev_ci = { - .priv = 0, - .readers = -1, - .writers = -1, - .users = -1, - .fops = &ci_fops, -}; - -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ - -static void input_tasklet(unsigned long data) -{ - struct ddb_input *input = (struct ddb_input *) data; - struct ddb *dev = input->port->dev; - - spin_lock(&input->lock); - if (!input->running) { - spin_unlock(&input->lock); - return; - } - input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); - - if (input->port->class == DDB_PORT_TUNER) { - if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr))) - printk(KERN_ERR "Overflow input %d\n", input->nr); - while (input->cbuf != ((input->stat >> 11) & 0x1f) - || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) { - dvb_dmx_swfilter_packets(&input->demux, - input->vbuf[input->cbuf], - input->dma_buf_size / 188); - - input->cbuf = (input->cbuf + 1) % input->dma_buf_num; - ddbwritel((input->cbuf << 11), - DMA_BUFFER_ACK(input->nr)); - input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); - } - } - if (input->port->class == DDB_PORT_CI) - wake_up(&input->wq); - spin_unlock(&input->lock); -} - -static void output_tasklet(unsigned long data) -{ - struct ddb_output *output = (struct ddb_output *) data; - struct ddb *dev = output->port->dev; - - spin_lock(&output->lock); - if (!output->running) { - spin_unlock(&output->lock); - return; - } - output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8)); - wake_up(&output->wq); - spin_unlock(&output->lock); -} - - -struct cxd2099_cfg cxd_cfg = { - .bitrate = 62000, - .adr = 0x40, - .polarity = 1, - .clock_mode = 1, -}; - -static int ddb_ci_attach(struct ddb_port *port) -{ - int ret; - - ret = dvb_register_adapter(&port->output->adap, - "DDBridge", - THIS_MODULE, - &port->dev->pdev->dev, - adapter_nr); - if (ret < 0) - return ret; - port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap); - if (!port->en) { - dvb_unregister_adapter(&port->output->adap); - return -ENODEV; - } - ddb_input_start(port->input[0]); - ddb_output_start(port->output); - dvb_ca_en50221_init(&port->output->adap, - port->en, 0, 1); - ret = dvb_register_device(&port->output->adap, &port->output->dev, - &dvbdev_ci, (void *) port->output, - DVB_DEVICE_SEC); - return ret; -} - -static int ddb_port_attach(struct ddb_port *port) -{ - int ret = 0; - - switch (port->class) { - case DDB_PORT_TUNER: - ret = dvb_input_attach(port->input[0]); - if (ret < 0) - break; - ret = dvb_input_attach(port->input[1]); - break; - case DDB_PORT_CI: - ret = ddb_ci_attach(port); - break; - default: - break; - } - if (ret < 0) - printk(KERN_ERR "port_attach on port %d failed\n", port->nr); - return ret; -} - -static int ddb_ports_attach(struct ddb *dev) -{ - int i, ret = 0; - struct ddb_port *port; - - for (i = 0; i < dev->info->port_num; i++) { - port = &dev->port[i]; - ret = ddb_port_attach(port); - if (ret < 0) - break; - } - return ret; -} - -static void ddb_ports_detach(struct ddb *dev) -{ - int i; - struct ddb_port *port; - - for (i = 0; i < dev->info->port_num; i++) { - port = &dev->port[i]; - switch (port->class) { - case DDB_PORT_TUNER: - dvb_input_detach(port->input[0]); - dvb_input_detach(port->input[1]); - break; - case DDB_PORT_CI: - if (port->output->dev) - dvb_unregister_device(port->output->dev); - if (port->en) { - ddb_input_stop(port->input[0]); - ddb_output_stop(port->output); - dvb_ca_en50221_release(port->en); - kfree(port->en); - port->en = 0; - dvb_unregister_adapter(&port->output->adap); - } - break; - } - } -} - -/****************************************************************************/ -/****************************************************************************/ - -static int port_has_ci(struct ddb_port *port) -{ - u8 val; - return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1; -} - -static int port_has_stv0900(struct ddb_port *port) -{ - u8 val; - if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0) - return 0; - return 1; -} - -static int port_has_stv0900_aa(struct ddb_port *port) -{ - u8 val; - if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0) - return 0; - return 1; -} - -static int port_has_drxks(struct ddb_port *port) -{ - u8 val; - if (i2c_read(&port->i2c->adap, 0x29, &val) < 0) - return 0; - if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0) - return 0; - return 1; -} - -static void ddb_port_probe(struct ddb_port *port) -{ - struct ddb *dev = port->dev; - char *modname = "NO MODULE"; - - port->class = DDB_PORT_NONE; - - if (port_has_ci(port)) { - modname = "CI"; - port->class = DDB_PORT_CI; - ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); - } else if (port_has_stv0900(port)) { - modname = "DUAL DVB-S2"; - port->class = DDB_PORT_TUNER; - port->type = DDB_TUNER_DVBS_ST; - ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); - } else if (port_has_stv0900_aa(port)) { - modname = "DUAL DVB-S2"; - port->class = DDB_PORT_TUNER; - port->type = DDB_TUNER_DVBS_ST_AA; - ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); - } else if (port_has_drxks(port)) { - modname = "DUAL DVB-C/T"; - port->class = DDB_PORT_TUNER; - port->type = DDB_TUNER_DVBCT_TR; - ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); - } - printk(KERN_INFO "Port %d (TAB %d): %s\n", - port->nr, port->nr+1, modname); -} - -static void ddb_input_init(struct ddb_port *port, int nr) -{ - struct ddb *dev = port->dev; - struct ddb_input *input = &dev->input[nr]; - - input->nr = nr; - input->port = port; - input->dma_buf_num = INPUT_DMA_BUFS; - input->dma_buf_size = INPUT_DMA_SIZE; - ddbwritel(0, TS_INPUT_CONTROL(nr)); - ddbwritel(2, TS_INPUT_CONTROL(nr)); - ddbwritel(0, TS_INPUT_CONTROL(nr)); - ddbwritel(0, DMA_BUFFER_ACK(nr)); - tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input); - spin_lock_init(&input->lock); - init_waitqueue_head(&input->wq); -} - -static void ddb_output_init(struct ddb_port *port, int nr) -{ - struct ddb *dev = port->dev; - struct ddb_output *output = &dev->output[nr]; - output->nr = nr; - output->port = port; - output->dma_buf_num = OUTPUT_DMA_BUFS; - output->dma_buf_size = OUTPUT_DMA_SIZE; - - ddbwritel(0, TS_OUTPUT_CONTROL(nr)); - ddbwritel(2, TS_OUTPUT_CONTROL(nr)); - ddbwritel(0, TS_OUTPUT_CONTROL(nr)); - tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output); - init_waitqueue_head(&output->wq); -} - -static void ddb_ports_init(struct ddb *dev) -{ - int i; - struct ddb_port *port; - - for (i = 0; i < dev->info->port_num; i++) { - port = &dev->port[i]; - port->dev = dev; - port->nr = i; - port->i2c = &dev->i2c[i]; - port->input[0] = &dev->input[2 * i]; - port->input[1] = &dev->input[2 * i + 1]; - port->output = &dev->output[i]; - - mutex_init(&port->i2c_gate_lock); - ddb_port_probe(port); - ddb_input_init(port, 2 * i); - ddb_input_init(port, 2 * i + 1); - ddb_output_init(port, i); - } -} - -static void ddb_ports_release(struct ddb *dev) -{ - int i; - struct ddb_port *port; - - for (i = 0; i < dev->info->port_num; i++) { - port = &dev->port[i]; - port->dev = dev; - tasklet_kill(&port->input[0]->tasklet); - tasklet_kill(&port->input[1]->tasklet); - tasklet_kill(&port->output->tasklet); - } -} - -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ - -static void irq_handle_i2c(struct ddb *dev, int n) -{ - struct ddb_i2c *i2c = &dev->i2c[n]; - - i2c->done = 1; - wake_up(&i2c->wq); -} - -static irqreturn_t irq_handler(int irq, void *dev_id) -{ - struct ddb *dev = (struct ddb *) dev_id; - u32 s = ddbreadl(INTERRUPT_STATUS); - - if (!s) - return IRQ_NONE; - - do { - ddbwritel(s, INTERRUPT_ACK); - - if (s & 0x00000001) - irq_handle_i2c(dev, 0); - if (s & 0x00000002) - irq_handle_i2c(dev, 1); - if (s & 0x00000004) - irq_handle_i2c(dev, 2); - if (s & 0x00000008) - irq_handle_i2c(dev, 3); - - if (s & 0x00000100) - tasklet_schedule(&dev->input[0].tasklet); - if (s & 0x00000200) - tasklet_schedule(&dev->input[1].tasklet); - if (s & 0x00000400) - tasklet_schedule(&dev->input[2].tasklet); - if (s & 0x00000800) - tasklet_schedule(&dev->input[3].tasklet); - if (s & 0x00001000) - tasklet_schedule(&dev->input[4].tasklet); - if (s & 0x00002000) - tasklet_schedule(&dev->input[5].tasklet); - if (s & 0x00004000) - tasklet_schedule(&dev->input[6].tasklet); - if (s & 0x00008000) - tasklet_schedule(&dev->input[7].tasklet); - - if (s & 0x00010000) - tasklet_schedule(&dev->output[0].tasklet); - if (s & 0x00020000) - tasklet_schedule(&dev->output[1].tasklet); - if (s & 0x00040000) - tasklet_schedule(&dev->output[2].tasklet); - if (s & 0x00080000) - tasklet_schedule(&dev->output[3].tasklet); - - /* if (s & 0x000f0000) printk(KERN_DEBUG "%08x\n", istat); */ - } while ((s = ddbreadl(INTERRUPT_STATUS))); - - return IRQ_HANDLED; -} - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) -{ - u32 data, shift; - - if (wlen > 4) - ddbwritel(1, SPI_CONTROL); - while (wlen > 4) { - /* FIXME: check for big-endian */ - data = swab32(*(u32 *)wbuf); - wbuf += 4; - wlen -= 4; - ddbwritel(data, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) - ; - } - - if (rlen) - ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); - else - ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); - - data = 0; - shift = ((4 - wlen) * 8); - while (wlen) { - data <<= 8; - data |= *wbuf; - wlen--; - wbuf++; - } - if (shift) - data <<= shift; - ddbwritel(data, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) - ; - - if (!rlen) { - ddbwritel(0, SPI_CONTROL); - return 0; - } - if (rlen > 4) - ddbwritel(1, SPI_CONTROL); - - while (rlen > 4) { - ddbwritel(0xffffffff, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) - ; - data = ddbreadl(SPI_DATA); - *(u32 *) rbuf = swab32(data); - rbuf += 4; - rlen -= 4; - } - ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL); - ddbwritel(0xffffffff, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) - ; - - data = ddbreadl(SPI_DATA); - ddbwritel(0, SPI_CONTROL); - - if (rlen < 4) - data <<= ((4 - rlen) * 8); - - while (rlen > 0) { - *rbuf = ((data >> 24) & 0xff); - data <<= 8; - rbuf++; - rlen--; - } - return 0; -} - -#define DDB_MAGIC 'd' - -struct ddb_flashio { - __u8 *write_buf; - __u32 write_len; - __u8 *read_buf; - __u32 read_len; -}; - -#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio) - -#define DDB_NAME "ddbridge" - -static u32 ddb_num; -static struct ddb *ddbs[32]; -static struct class *ddb_class; -static int ddb_major; - -static int ddb_open(struct inode *inode, struct file *file) -{ - struct ddb *dev = ddbs[iminor(inode)]; - - file->private_data = dev; - return 0; -} - -static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct ddb *dev = file->private_data; - void *parg = (void *)arg; - int res; - - switch (cmd) { - case IOCTL_DDB_FLASHIO: - { - struct ddb_flashio fio; - u8 *rbuf, *wbuf; - - if (copy_from_user(&fio, parg, sizeof(fio))) - return -EFAULT; - - if (fio.write_len > 1028 || fio.read_len > 1028) - return -EINVAL; - if (fio.write_len + fio.read_len > 1028) - return -EINVAL; - - wbuf = &dev->iobuf[0]; - rbuf = wbuf + fio.write_len; - - if (copy_from_user(wbuf, fio.write_buf, fio.write_len)) - return -EFAULT; - res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len); - if (res) - return res; - if (copy_to_user(fio.read_buf, rbuf, fio.read_len)) - return -EFAULT; - break; - } - default: - return -ENOTTY; - } - return 0; -} - -static const struct file_operations ddb_fops = { - .unlocked_ioctl = ddb_ioctl, - .open = ddb_open, -}; - -static char *ddb_devnode(struct device *device, umode_t *mode) -{ - struct ddb *dev = dev_get_drvdata(device); - - return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr); -} - -static int ddb_class_create(void) -{ - ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops); - if (ddb_major < 0) - return ddb_major; - - ddb_class = class_create(THIS_MODULE, DDB_NAME); - if (IS_ERR(ddb_class)) { - unregister_chrdev(ddb_major, DDB_NAME); - return -1; - } - ddb_class->devnode = ddb_devnode; - return 0; -} - -static void ddb_class_destroy(void) -{ - class_destroy(ddb_class); - unregister_chrdev(ddb_major, DDB_NAME); -} - -static int ddb_device_create(struct ddb *dev) -{ - dev->nr = ddb_num++; - dev->ddb_dev = device_create(ddb_class, NULL, - MKDEV(ddb_major, dev->nr), - dev, "ddbridge%d", dev->nr); - ddbs[dev->nr] = dev; - if (IS_ERR(dev->ddb_dev)) - return -1; - return 0; -} - -static void ddb_device_destroy(struct ddb *dev) -{ - ddb_num--; - if (IS_ERR(dev->ddb_dev)) - return; - device_destroy(ddb_class, MKDEV(ddb_major, 0)); -} - - -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ - -static void ddb_unmap(struct ddb *dev) -{ - if (dev->regs) - iounmap(dev->regs); - vfree(dev); -} - - -static void __devexit ddb_remove(struct pci_dev *pdev) -{ - struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev); - - ddb_ports_detach(dev); - ddb_i2c_release(dev); - - ddbwritel(0, INTERRUPT_ENABLE); - free_irq(dev->pdev->irq, dev); -#ifdef CONFIG_PCI_MSI - if (dev->msi) - pci_disable_msi(dev->pdev); -#endif - ddb_ports_release(dev); - ddb_buffers_free(dev); - ddb_device_destroy(dev); - - ddb_unmap(dev); - pci_set_drvdata(pdev, 0); - pci_disable_device(pdev); -} - - -static int __devinit ddb_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct ddb *dev; - int stat = 0; - int irq_flag = IRQF_SHARED; - - if (pci_enable_device(pdev) < 0) - return -ENODEV; - - dev = vmalloc(sizeof(struct ddb)); - if (dev == NULL) - return -ENOMEM; - memset(dev, 0, sizeof(struct ddb)); - - dev->pdev = pdev; - pci_set_drvdata(pdev, dev); - dev->info = (struct ddb_info *) id->driver_data; - printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name); - - dev->regs = ioremap(pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0)); - if (!dev->regs) { - stat = -ENOMEM; - goto fail; - } - printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4)); - -#ifdef CONFIG_PCI_MSI - if (pci_msi_enabled()) - stat = pci_enable_msi(dev->pdev); - if (stat) { - printk(KERN_INFO ": MSI not available.\n"); - } else { - irq_flag = 0; - dev->msi = 1; - } -#endif - stat = request_irq(dev->pdev->irq, irq_handler, - irq_flag, "DDBridge", (void *) dev); - if (stat < 0) - goto fail1; - ddbwritel(0, DMA_BASE_WRITE); - ddbwritel(0, DMA_BASE_READ); - ddbwritel(0xffffffff, INTERRUPT_ACK); - ddbwritel(0xfff0f, INTERRUPT_ENABLE); - ddbwritel(0, MSI1_ENABLE); - - if (ddb_i2c_init(dev) < 0) - goto fail1; - ddb_ports_init(dev); - if (ddb_buffers_alloc(dev) < 0) { - printk(KERN_INFO ": Could not allocate buffer memory\n"); - goto fail2; - } - if (ddb_ports_attach(dev) < 0) - goto fail3; - ddb_device_create(dev); - return 0; - -fail3: - ddb_ports_detach(dev); - printk(KERN_ERR "fail3\n"); - ddb_ports_release(dev); -fail2: - printk(KERN_ERR "fail2\n"); - ddb_buffers_free(dev); -fail1: - printk(KERN_ERR "fail1\n"); - if (dev->msi) - pci_disable_msi(dev->pdev); - free_irq(dev->pdev->irq, dev); -fail: - printk(KERN_ERR "fail\n"); - ddb_unmap(dev); - pci_set_drvdata(pdev, 0); - pci_disable_device(pdev); - return -1; -} - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -static struct ddb_info ddb_none = { - .type = DDB_NONE, - .name = "Digital Devices PCIe bridge", -}; - -static struct ddb_info ddb_octopus = { - .type = DDB_OCTOPUS, - .name = "Digital Devices Octopus DVB adapter", - .port_num = 4, -}; - -static struct ddb_info ddb_octopus_le = { - .type = DDB_OCTOPUS, - .name = "Digital Devices Octopus LE DVB adapter", - .port_num = 2, -}; - -static struct ddb_info ddb_v6 = { - .type = DDB_OCTOPUS, - .name = "Digital Devices Cine S2 V6 DVB adapter", - .port_num = 3, -}; - -#define DDVID 0xdd01 /* Digital Devices Vendor ID */ - -#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \ - .vendor = _vend, .device = _dev, \ - .subvendor = _subvend, .subdevice = _subdev, \ - .driver_data = (unsigned long)&_driverdata } - -static const struct pci_device_id ddb_id_tbl[] __devinitdata = { - DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus), - DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus), - DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le), - DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus), - DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6), - /* in case sub-ids got deleted in flash */ - DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none), - {0} -}; -MODULE_DEVICE_TABLE(pci, ddb_id_tbl); - - -static struct pci_driver ddb_pci_driver = { - .name = "DDBridge", - .id_table = ddb_id_tbl, - .probe = ddb_probe, - .remove = __devexit_p(ddb_remove), -}; - -static __init int module_init_ddbridge(void) -{ - printk(KERN_INFO "Digital Devices PCIE bridge driver, " - "Copyright (C) 2010-11 Digital Devices GmbH\n"); - if (ddb_class_create()) - return -1; - return pci_register_driver(&ddb_pci_driver); -} - -static __exit void module_exit_ddbridge(void) -{ - pci_unregister_driver(&ddb_pci_driver); - ddb_class_destroy(); -} - -module_init(module_init_ddbridge); -module_exit(module_exit_ddbridge); - -MODULE_DESCRIPTION("Digital Devices PCIe Bridge"); -MODULE_AUTHOR("Ralph Metzler"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.5"); diff --git a/drivers/media/dvb/ddbridge/ddbridge-regs.h b/drivers/media/dvb/ddbridge/ddbridge-regs.h deleted file mode 100644 index a3ccb318b500..000000000000 --- a/drivers/media/dvb/ddbridge/ddbridge-regs.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * ddbridge-regs.h: Digital Devices PCIe bridge driver - * - * Copyright (C) 2010-2011 Digital Devices GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -/* DD-DVBBridgeV1.h 273 2010-09-17 05:03:16Z manfred */ - -/* Register Definitions */ - -#define CUR_REGISTERMAP_VERSION 0x10000 - -#define HARDWARE_VERSION 0x00 -#define REGISTERMAP_VERSION 0x04 - -/* ------------------------------------------------------------------------- */ -/* SPI Controller */ - -#define SPI_CONTROL 0x10 -#define SPI_DATA 0x14 - -/* ------------------------------------------------------------------------- */ - -/* Interrupt controller */ -/* How many MSI's are available depends on HW (Min 2 max 8) */ -/* How many are usable also depends on Host platform */ - -#define INTERRUPT_BASE (0x40) - -#define INTERRUPT_ENABLE (INTERRUPT_BASE + 0x00) -#define MSI0_ENABLE (INTERRUPT_BASE + 0x00) -#define MSI1_ENABLE (INTERRUPT_BASE + 0x04) -#define MSI2_ENABLE (INTERRUPT_BASE + 0x08) -#define MSI3_ENABLE (INTERRUPT_BASE + 0x0C) -#define MSI4_ENABLE (INTERRUPT_BASE + 0x10) -#define MSI5_ENABLE (INTERRUPT_BASE + 0x14) -#define MSI6_ENABLE (INTERRUPT_BASE + 0x18) -#define MSI7_ENABLE (INTERRUPT_BASE + 0x1C) - -#define INTERRUPT_STATUS (INTERRUPT_BASE + 0x20) -#define INTERRUPT_ACK (INTERRUPT_BASE + 0x20) - -#define INTMASK_I2C1 (0x00000001) -#define INTMASK_I2C2 (0x00000002) -#define INTMASK_I2C3 (0x00000004) -#define INTMASK_I2C4 (0x00000008) - -#define INTMASK_CIRQ1 (0x00000010) -#define INTMASK_CIRQ2 (0x00000020) -#define INTMASK_CIRQ3 (0x00000040) -#define INTMASK_CIRQ4 (0x00000080) - -#define INTMASK_TSINPUT1 (0x00000100) -#define INTMASK_TSINPUT2 (0x00000200) -#define INTMASK_TSINPUT3 (0x00000400) -#define INTMASK_TSINPUT4 (0x00000800) -#define INTMASK_TSINPUT5 (0x00001000) -#define INTMASK_TSINPUT6 (0x00002000) -#define INTMASK_TSINPUT7 (0x00004000) -#define INTMASK_TSINPUT8 (0x00008000) - -#define INTMASK_TSOUTPUT1 (0x00010000) -#define INTMASK_TSOUTPUT2 (0x00020000) -#define INTMASK_TSOUTPUT3 (0x00040000) -#define INTMASK_TSOUTPUT4 (0x00080000) - -/* ------------------------------------------------------------------------- */ -/* I2C Master Controller */ - -#define I2C_BASE (0x80) /* Byte offset */ - -#define I2C_COMMAND (0x00) -#define I2C_TIMING (0x04) -#define I2C_TASKLENGTH (0x08) /* High read, low write */ -#define I2C_TASKADDRESS (0x0C) /* High read, low write */ - -#define I2C_MONITOR (0x1C) - -#define I2C_BASE_1 (I2C_BASE + 0x00) -#define I2C_BASE_2 (I2C_BASE + 0x20) -#define I2C_BASE_3 (I2C_BASE + 0x40) -#define I2C_BASE_4 (I2C_BASE + 0x60) - -#define I2C_BASE_N(i) (I2C_BASE + (i) * 0x20) - -#define I2C_TASKMEM_BASE (0x1000) /* Byte offset */ -#define I2C_TASKMEM_SIZE (0x1000) - -#define I2C_SPEED_400 (0x04030404) -#define I2C_SPEED_200 (0x09080909) -#define I2C_SPEED_154 (0x0C0B0C0C) -#define I2C_SPEED_100 (0x13121313) -#define I2C_SPEED_77 (0x19181919) -#define I2C_SPEED_50 (0x27262727) - - -/* ------------------------------------------------------------------------- */ -/* DMA Controller */ - -#define DMA_BASE_WRITE (0x100) -#define DMA_BASE_READ (0x140) - -#define DMA_CONTROL (0x00) /* 64 */ -#define DMA_ERROR (0x04) /* 65 ( only read instance ) */ - -#define DMA_DIAG_CONTROL (0x1C) /* 71 */ -#define DMA_DIAG_PACKETCOUNTER_LOW (0x20) /* 72 */ -#define DMA_DIAG_PACKETCOUNTER_HIGH (0x24) /* 73 */ -#define DMA_DIAG_TIMECOUNTER_LOW (0x28) /* 74 */ -#define DMA_DIAG_TIMECOUNTER_HIGH (0x2C) /* 75 */ -#define DMA_DIAG_RECHECKCOUNTER (0x30) /* 76 ( Split completions on read ) */ -#define DMA_DIAG_WAITTIMEOUTINIT (0x34) /* 77 */ -#define DMA_DIAG_WAITOVERFLOWCOUNTER (0x38) /* 78 */ -#define DMA_DIAG_WAITCOUNTER (0x3C) /* 79 */ - -/* ------------------------------------------------------------------------- */ -/* DMA Buffer */ - -#define TS_INPUT_BASE (0x200) -#define TS_INPUT_CONTROL(i) (TS_INPUT_BASE + (i) * 16 + 0x00) - -#define TS_OUTPUT_BASE (0x280) -#define TS_OUTPUT_CONTROL(i) (TS_OUTPUT_BASE + (i) * 16 + 0x00) - -#define DMA_BUFFER_BASE (0x300) - -#define DMA_BUFFER_CONTROL(i) (DMA_BUFFER_BASE + (i) * 16 + 0x00) -#define DMA_BUFFER_ACK(i) (DMA_BUFFER_BASE + (i) * 16 + 0x04) -#define DMA_BUFFER_CURRENT(i) (DMA_BUFFER_BASE + (i) * 16 + 0x08) -#define DMA_BUFFER_SIZE(i) (DMA_BUFFER_BASE + (i) * 16 + 0x0c) - -#define DMA_BASE_ADDRESS_TABLE (0x2000) -#define DMA_BASE_ADDRESS_TABLE_ENTRIES (512) - diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/dvb/ddbridge/ddbridge.h deleted file mode 100644 index 8b1b41d2a52d..000000000000 --- a/drivers/media/dvb/ddbridge/ddbridge.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * ddbridge.h: Digital Devices PCIe bridge driver - * - * Copyright (C) 2010-2011 Digital Devices GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#ifndef _DDBRIDGE_H_ -#define _DDBRIDGE_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_ringbuffer.h" -#include "dvb_ca_en50221.h" -#include "dvb_net.h" -#include "cxd2099.h" - -#define DDB_MAX_I2C 4 -#define DDB_MAX_PORT 4 -#define DDB_MAX_INPUT 8 -#define DDB_MAX_OUTPUT 4 - -struct ddb_info { - int type; -#define DDB_NONE 0 -#define DDB_OCTOPUS 1 - char *name; - int port_num; - u32 port_type[DDB_MAX_PORT]; -}; - -/* DMA_SIZE MUST be divisible by 188 and 128 !!! */ - -#define INPUT_DMA_MAX_BUFS 32 /* hardware table limit */ -#define INPUT_DMA_BUFS 8 -#define INPUT_DMA_SIZE (128*47*21) - -#define OUTPUT_DMA_MAX_BUFS 32 -#define OUTPUT_DMA_BUFS 8 -#define OUTPUT_DMA_SIZE (128*47*21) - -struct ddb; -struct ddb_port; - -struct ddb_input { - struct ddb_port *port; - u32 nr; - int attached; - - dma_addr_t pbuf[INPUT_DMA_MAX_BUFS]; - u8 *vbuf[INPUT_DMA_MAX_BUFS]; - u32 dma_buf_num; - u32 dma_buf_size; - - struct tasklet_struct tasklet; - spinlock_t lock; - wait_queue_head_t wq; - int running; - u32 stat; - u32 cbuf; - u32 coff; - - struct dvb_adapter adap; - struct dvb_device *dev; - struct dvb_frontend *fe; - struct dvb_frontend *fe2; - struct dmxdev dmxdev; - struct dvb_demux demux; - struct dvb_net dvbnet; - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - int users; - int (*gate_ctrl)(struct dvb_frontend *, int); -}; - -struct ddb_output { - struct ddb_port *port; - u32 nr; - dma_addr_t pbuf[OUTPUT_DMA_MAX_BUFS]; - u8 *vbuf[OUTPUT_DMA_MAX_BUFS]; - u32 dma_buf_num; - u32 dma_buf_size; - struct tasklet_struct tasklet; - spinlock_t lock; - wait_queue_head_t wq; - int running; - u32 stat; - u32 cbuf; - u32 coff; - - struct dvb_adapter adap; - struct dvb_device *dev; -}; - -struct ddb_i2c { - struct ddb *dev; - u32 nr; - struct i2c_adapter adap; - struct i2c_adapter adap2; - u32 regs; - u32 rbuf; - u32 wbuf; - int done; - wait_queue_head_t wq; -}; - -struct ddb_port { - struct ddb *dev; - u32 nr; - struct ddb_i2c *i2c; - struct mutex i2c_gate_lock; - u32 class; -#define DDB_PORT_NONE 0 -#define DDB_PORT_CI 1 -#define DDB_PORT_TUNER 2 - u32 type; -#define DDB_TUNER_NONE 0 -#define DDB_TUNER_DVBS_ST 1 -#define DDB_TUNER_DVBS_ST_AA 2 -#define DDB_TUNER_DVBCT_TR 16 -#define DDB_TUNER_DVBCT_ST 17 - u32 adr; - - struct ddb_input *input[2]; - struct ddb_output *output; - struct dvb_ca_en50221 *en; -}; - -struct ddb { - struct pci_dev *pdev; - unsigned char *regs; - struct ddb_port port[DDB_MAX_PORT]; - struct ddb_i2c i2c[DDB_MAX_I2C]; - struct ddb_input input[DDB_MAX_INPUT]; - struct ddb_output output[DDB_MAX_OUTPUT]; - - struct device *ddb_dev; - int nr; - u8 iobuf[1028]; - - struct ddb_info *info; - int msi; -}; - -/****************************************************************************/ - -#define ddbwritel(_val, _adr) writel((_val), \ - (char *) (dev->regs+(_adr))) -#define ddbreadl(_adr) readl((char *) (dev->regs+(_adr))) -#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *) \ - (dev->regs+(_adr)), (_src), (_count)) -#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \ - (dev->regs+(_adr)), (_count)) - -/****************************************************************************/ - -#endif diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig deleted file mode 100644 index f3de0a4d63f2..000000000000 --- a/drivers/media/dvb/dm1105/Kconfig +++ /dev/null @@ -1,20 +0,0 @@ -config DVB_DM1105 - tristate "SDMC DM1105 based PCI cards" - depends on DVB_CORE && PCI && I2C - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_SI21XX if !DVB_FE_CUSTOMISE - select DVB_DS3000 if !DVB_FE_CUSTOMISE - depends on RC_CORE - help - Support for cards based on the SDMC DM1105 PCI chip like - DvbWorld 2002 - - Since these cards have no MPEG decoder onboard, they transmit - only compressed MPEG data over the PCI bus, so you need - an external software decoder to watch TV on your computer. - - Say Y or M if you own such a device and want to use it. diff --git a/drivers/media/dvb/dm1105/Makefile b/drivers/media/dvb/dm1105/Makefile deleted file mode 100644 index 327585143c83..000000000000 --- a/drivers/media/dvb/dm1105/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_DVB_DM1105) += dm1105.o - -ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c deleted file mode 100644 index a609b3a9b146..000000000000 --- a/drivers/media/dvb/dm1105/dm1105.c +++ /dev/null @@ -1,1248 +0,0 @@ -/* - * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip - * - * Copyright (C) 2008 Igor M. Liplianin - * - * This program is free software; 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 -#include -#include - -#include "demux.h" -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" -#include "dvbdev.h" -#include "dvb-pll.h" - -#include "stv0299.h" -#include "stv0288.h" -#include "stb6000.h" -#include "si21xx.h" -#include "cx24116.h" -#include "z0194a.h" -#include "ds3000.h" - -#define MODULE_NAME "dm1105" - -#define UNSET (-1U) - -#define DM1105_BOARD_NOAUTO UNSET -#define DM1105_BOARD_UNKNOWN 0 -#define DM1105_BOARD_DVBWORLD_2002 1 -#define DM1105_BOARD_DVBWORLD_2004 2 -#define DM1105_BOARD_AXESS_DM05 3 -#define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO 4 - -/* ----------------------------------------------- */ -/* - * PCI ID's - */ -#ifndef PCI_VENDOR_ID_TRIGEM -#define PCI_VENDOR_ID_TRIGEM 0x109f -#endif -#ifndef PCI_VENDOR_ID_AXESS -#define PCI_VENDOR_ID_AXESS 0x195d -#endif -#ifndef PCI_DEVICE_ID_DM1105 -#define PCI_DEVICE_ID_DM1105 0x036f -#endif -#ifndef PCI_DEVICE_ID_DW2002 -#define PCI_DEVICE_ID_DW2002 0x2002 -#endif -#ifndef PCI_DEVICE_ID_DW2004 -#define PCI_DEVICE_ID_DW2004 0x2004 -#endif -#ifndef PCI_DEVICE_ID_DM05 -#define PCI_DEVICE_ID_DM05 0x1105 -#endif -/* ----------------------------------------------- */ -/* sdmc dm1105 registers */ - -/* TS Control */ -#define DM1105_TSCTR 0x00 -#define DM1105_DTALENTH 0x04 - -/* GPIO Interface */ -#define DM1105_GPIOVAL 0x08 -#define DM1105_GPIOCTR 0x0c - -/* PID serial number */ -#define DM1105_PIDN 0x10 - -/* Odd-even secret key select */ -#define DM1105_CWSEL 0x14 - -/* Host Command Interface */ -#define DM1105_HOST_CTR 0x18 -#define DM1105_HOST_AD 0x1c - -/* PCI Interface */ -#define DM1105_CR 0x30 -#define DM1105_RST 0x34 -#define DM1105_STADR 0x38 -#define DM1105_RLEN 0x3c -#define DM1105_WRP 0x40 -#define DM1105_INTCNT 0x44 -#define DM1105_INTMAK 0x48 -#define DM1105_INTSTS 0x4c - -/* CW Value */ -#define DM1105_ODD 0x50 -#define DM1105_EVEN 0x58 - -/* PID Value */ -#define DM1105_PID 0x60 - -/* IR Control */ -#define DM1105_IRCTR 0x64 -#define DM1105_IRMODE 0x68 -#define DM1105_SYSTEMCODE 0x6c -#define DM1105_IRCODE 0x70 - -/* Unknown Values */ -#define DM1105_ENCRYPT 0x74 -#define DM1105_VER 0x7c - -/* I2C Interface */ -#define DM1105_I2CCTR 0x80 -#define DM1105_I2CSTS 0x81 -#define DM1105_I2CDAT 0x82 -#define DM1105_I2C_RA 0x83 -/* ----------------------------------------------- */ -/* Interrupt Mask Bits */ - -#define INTMAK_TSIRQM 0x01 -#define INTMAK_HIRQM 0x04 -#define INTMAK_IRM 0x08 -#define INTMAK_ALLMASK (INTMAK_TSIRQM | \ - INTMAK_HIRQM | \ - INTMAK_IRM) -#define INTMAK_NONEMASK 0x00 - -/* Interrupt Status Bits */ -#define INTSTS_TSIRQ 0x01 -#define INTSTS_HIRQ 0x04 -#define INTSTS_IR 0x08 - -/* IR Control Bits */ -#define DM1105_IR_EN 0x01 -#define DM1105_SYS_CHK 0x02 -#define DM1105_REP_FLG 0x08 - -/* EEPROM addr */ -#define IIC_24C01_addr 0xa0 -/* Max board count */ -#define DM1105_MAX 0x04 - -#define DRIVER_NAME "dm1105" -#define DM1105_I2C_GPIO_NAME "dm1105-gpio" - -#define DM1105_DMA_PACKETS 47 -#define DM1105_DMA_PACKET_LENGTH (128*4) -#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) - -/* */ -#define GPIO08 (1 << 8) -#define GPIO13 (1 << 13) -#define GPIO14 (1 << 14) -#define GPIO15 (1 << 15) -#define GPIO16 (1 << 16) -#define GPIO17 (1 << 17) -#define GPIO_ALL 0x03ffff - -/* GPIO's for LNB power control */ -#define DM1105_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) -#define DM1105_LNB_OFF GPIO17 -#define DM1105_LNB_13V (GPIO16 | GPIO08) -#define DM1105_LNB_18V GPIO08 - -/* GPIO's for LNB power control for Axess DM05 */ -#define DM05_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) -#define DM05_LNB_OFF GPIO17/* actually 13v */ -#define DM05_LNB_13V GPIO17 -#define DM05_LNB_18V (GPIO17 | GPIO16) - -/* GPIO's for LNB power control for unbranded with I2C on GPIO */ -#define UNBR_LNB_MASK (GPIO17 | GPIO16) -#define UNBR_LNB_OFF 0 -#define UNBR_LNB_13V GPIO17 -#define UNBR_LNB_18V (GPIO17 | GPIO16) - -static unsigned int card[] = {[0 ... 3] = UNSET }; -module_param_array(card, int, NULL, 0444); -MODULE_PARM_DESC(card, "card type"); - -static int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); - -static unsigned int dm1105_devcount; - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -struct dm1105_board { - char *name; - struct { - u32 mask, off, v13, v18; - } lnb; - u32 gpio_scl, gpio_sda; -}; - -struct dm1105_subid { - u16 subvendor; - u16 subdevice; - u32 card; -}; - -static const struct dm1105_board dm1105_boards[] = { - [DM1105_BOARD_UNKNOWN] = { - .name = "UNKNOWN/GENERIC", - .lnb = { - .mask = DM1105_LNB_MASK, - .off = DM1105_LNB_OFF, - .v13 = DM1105_LNB_13V, - .v18 = DM1105_LNB_18V, - }, - }, - [DM1105_BOARD_DVBWORLD_2002] = { - .name = "DVBWorld PCI 2002", - .lnb = { - .mask = DM1105_LNB_MASK, - .off = DM1105_LNB_OFF, - .v13 = DM1105_LNB_13V, - .v18 = DM1105_LNB_18V, - }, - }, - [DM1105_BOARD_DVBWORLD_2004] = { - .name = "DVBWorld PCI 2004", - .lnb = { - .mask = DM1105_LNB_MASK, - .off = DM1105_LNB_OFF, - .v13 = DM1105_LNB_13V, - .v18 = DM1105_LNB_18V, - }, - }, - [DM1105_BOARD_AXESS_DM05] = { - .name = "Axess/EasyTv DM05", - .lnb = { - .mask = DM05_LNB_MASK, - .off = DM05_LNB_OFF, - .v13 = DM05_LNB_13V, - .v18 = DM05_LNB_18V, - }, - }, - [DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = { - .name = "Unbranded DM1105 with i2c on GPIOs", - .lnb = { - .mask = UNBR_LNB_MASK, - .off = UNBR_LNB_OFF, - .v13 = UNBR_LNB_13V, - .v18 = UNBR_LNB_18V, - }, - .gpio_scl = GPIO14, - .gpio_sda = GPIO13, - }, -}; - -static const struct dm1105_subid dm1105_subids[] = { - { - .subvendor = 0x0000, - .subdevice = 0x2002, - .card = DM1105_BOARD_DVBWORLD_2002, - }, { - .subvendor = 0x0001, - .subdevice = 0x2002, - .card = DM1105_BOARD_DVBWORLD_2002, - }, { - .subvendor = 0x0000, - .subdevice = 0x2004, - .card = DM1105_BOARD_DVBWORLD_2004, - }, { - .subvendor = 0x0001, - .subdevice = 0x2004, - .card = DM1105_BOARD_DVBWORLD_2004, - }, { - .subvendor = 0x195d, - .subdevice = 0x1105, - .card = DM1105_BOARD_AXESS_DM05, - }, -}; - -static void dm1105_card_list(struct pci_dev *pci) -{ - int i; - - if (0 == pci->subsystem_vendor && - 0 == pci->subsystem_device) { - printk(KERN_ERR - "dm1105: Your board has no valid PCI Subsystem ID\n" - "dm1105: and thus can't be autodetected\n" - "dm1105: Please pass card= insmod option to\n" - "dm1105: workaround that. Redirect complaints to\n" - "dm1105: the vendor of the TV card. Best regards,\n" - "dm1105: -- tux\n"); - } else { - printk(KERN_ERR - "dm1105: Your board isn't known (yet) to the driver.\n" - "dm1105: You can try to pick one of the existing\n" - "dm1105: card configs via card= insmod option.\n" - "dm1105: Updating to the latest version might help\n" - "dm1105: as well.\n"); - } - printk(KERN_ERR "Here is a list of valid choices for the card= " - "insmod option:\n"); - for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++) - printk(KERN_ERR "dm1105: card=%d -> %s\n", - i, dm1105_boards[i].name); -} - -/* infrared remote control */ -struct infrared { - struct rc_dev *dev; - char input_phys[32]; - struct work_struct work; - u32 ir_command; -}; - -struct dm1105_dev { - /* pci */ - struct pci_dev *pdev; - u8 __iomem *io_mem; - - /* ir */ - struct infrared ir; - - /* dvb */ - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - struct dmxdev dmxdev; - struct dvb_adapter dvb_adapter; - struct dvb_demux demux; - struct dvb_frontend *fe; - struct dvb_net dvbnet; - unsigned int full_ts_users; - unsigned int boardnr; - int nr; - - /* i2c */ - struct i2c_adapter i2c_adap; - struct i2c_adapter i2c_bb_adap; - struct i2c_algo_bit_data i2c_bit; - - /* irq */ - struct work_struct work; - struct workqueue_struct *wq; - char wqn[16]; - - /* dma */ - dma_addr_t dma_addr; - unsigned char *ts_buf; - u32 wrp; - u32 nextwrp; - u32 buffer_size; - unsigned int PacketErrorCount; - unsigned int dmarst; - spinlock_t lock; -}; - -#define dm_io_mem(reg) ((unsigned long)(&dev->io_mem[reg])) - -#define dm_readb(reg) inb(dm_io_mem(reg)) -#define dm_writeb(reg, value) outb((value), (dm_io_mem(reg))) - -#define dm_readw(reg) inw(dm_io_mem(reg)) -#define dm_writew(reg, value) outw((value), (dm_io_mem(reg))) - -#define dm_readl(reg) inl(dm_io_mem(reg)) -#define dm_writel(reg, value) outl((value), (dm_io_mem(reg))) - -#define dm_andorl(reg, mask, value) \ - outl((inl(dm_io_mem(reg)) & ~(mask)) |\ - ((value) & (mask)), (dm_io_mem(reg))) - -#define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) -#define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) - -/* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines, - so we can use only 3 GPIO's from GPIO15 to GPIO17. - Here I don't check whether HOST is enebled as it is not implemented yet. - */ -static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask) -{ - if (mask & 0xfffc0000) - printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); - - if (mask & 0x0003ffff) - dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff); - -} - -static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask) -{ - if (mask & 0xfffc0000) - printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); - - if (mask & 0x0003ffff) - dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff); - -} - -static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val) -{ - if (mask & 0xfffc0000) - printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); - - if (mask & 0x0003ffff) - dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val); - -} - -static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask) -{ - if (mask & 0xfffc0000) - printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); - - if (mask & 0x0003ffff) - return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff; - - return 0; -} - -static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput) -{ - if (mask & 0xfffc0000) - printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); - - if ((mask & 0x0003ffff) && asoutput) - dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff); - else if ((mask & 0x0003ffff) && !asoutput) - dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff); - -} - -static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state) -{ - if (state) - dm1105_gpio_enable(dev, line, 0); - else { - dm1105_gpio_enable(dev, line, 1); - dm1105_gpio_clear(dev, line); - } -} - -static void dm1105_setsda(void *data, int state) -{ - struct dm1105_dev *dev = data; - - dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state); -} - -static void dm1105_setscl(void *data, int state) -{ - struct dm1105_dev *dev = data; - - dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state); -} - -static int dm1105_getsda(void *data) -{ - struct dm1105_dev *dev = data; - - return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda) - ? 1 : 0; -} - -static int dm1105_getscl(void *data) -{ - struct dm1105_dev *dev = data; - - return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl) - ? 1 : 0; -} - -static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg *msgs, int num) -{ - struct dm1105_dev *dev ; - - int addr, rc, i, j, k, len, byte, data; - u8 status; - - dev = i2c_adap->algo_data; - for (i = 0; i < num; i++) { - dm_writeb(DM1105_I2CCTR, 0x00); - if (msgs[i].flags & I2C_M_RD) { - /* read bytes */ - addr = msgs[i].addr << 1; - addr |= 1; - dm_writeb(DM1105_I2CDAT, addr); - for (byte = 0; byte < msgs[i].len; byte++) - dm_writeb(DM1105_I2CDAT + byte + 1, 0); - - dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); - for (j = 0; j < 55; j++) { - mdelay(10); - status = dm_readb(DM1105_I2CSTS); - if ((status & 0xc0) == 0x40) - break; - } - if (j >= 55) - return -1; - - for (byte = 0; byte < msgs[i].len; byte++) { - rc = dm_readb(DM1105_I2CDAT + byte + 1); - if (rc < 0) - goto err; - msgs[i].buf[byte] = rc; - } - } else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { - /* prepaired for cx24116 firmware */ - /* Write in small blocks */ - len = msgs[i].len - 1; - k = 1; - do { - dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); - dm_writeb(DM1105_I2CDAT + 1, 0xf7); - for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { - data = msgs[i].buf[k + byte]; - dm_writeb(DM1105_I2CDAT + byte + 2, data); - } - dm_writeb(DM1105_I2CCTR, 0x82 + (len > 48 ? 48 : len)); - for (j = 0; j < 25; j++) { - mdelay(10); - status = dm_readb(DM1105_I2CSTS); - if ((status & 0xc0) == 0x40) - break; - } - - if (j >= 25) - return -1; - - k += 48; - len -= 48; - } while (len > 0); - } else { - /* write bytes */ - dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); - for (byte = 0; byte < msgs[i].len; byte++) { - data = msgs[i].buf[byte]; - dm_writeb(DM1105_I2CDAT + byte + 1, data); - } - dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); - for (j = 0; j < 25; j++) { - mdelay(10); - status = dm_readb(DM1105_I2CSTS); - if ((status & 0xc0) == 0x40) - break; - } - - if (j >= 25) - return -1; - } - } - return num; - err: - return rc; -} - -static u32 functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm dm1105_algo = { - .master_xfer = dm1105_i2c_xfer, - .functionality = functionality, -}; - -static inline struct dm1105_dev *feed_to_dm1105_dev(struct dvb_demux_feed *feed) -{ - return container_of(feed->demux, struct dm1105_dev, demux); -} - -static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) -{ - return container_of(fe->dvb, struct dm1105_dev, dvb_adapter); -} - -static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); - - dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1); - if (voltage == SEC_VOLTAGE_18) - dm1105_gpio_andor(dev, - dm1105_boards[dev->boardnr].lnb.mask, - dm1105_boards[dev->boardnr].lnb.v18); - else if (voltage == SEC_VOLTAGE_13) - dm1105_gpio_andor(dev, - dm1105_boards[dev->boardnr].lnb.mask, - dm1105_boards[dev->boardnr].lnb.v13); - else - dm1105_gpio_andor(dev, - dm1105_boards[dev->boardnr].lnb.mask, - dm1105_boards[dev->boardnr].lnb.off); - - return 0; -} - -static void dm1105_set_dma_addr(struct dm1105_dev *dev) -{ - dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); -} - -static int __devinit dm1105_dma_map(struct dm1105_dev *dev) -{ - dev->ts_buf = pci_alloc_consistent(dev->pdev, - 6 * DM1105_DMA_BYTES, - &dev->dma_addr); - - return !dev->ts_buf; -} - -static void dm1105_dma_unmap(struct dm1105_dev *dev) -{ - pci_free_consistent(dev->pdev, - 6 * DM1105_DMA_BYTES, - dev->ts_buf, - dev->dma_addr); -} - -static void dm1105_enable_irqs(struct dm1105_dev *dev) -{ - dm_writeb(DM1105_INTMAK, INTMAK_ALLMASK); - dm_writeb(DM1105_CR, 1); -} - -static void dm1105_disable_irqs(struct dm1105_dev *dev) -{ - dm_writeb(DM1105_INTMAK, INTMAK_IRM); - dm_writeb(DM1105_CR, 0); -} - -static int dm1105_start_feed(struct dvb_demux_feed *f) -{ - struct dm1105_dev *dev = feed_to_dm1105_dev(f); - - if (dev->full_ts_users++ == 0) - dm1105_enable_irqs(dev); - - return 0; -} - -static int dm1105_stop_feed(struct dvb_demux_feed *f) -{ - struct dm1105_dev *dev = feed_to_dm1105_dev(f); - - if (--dev->full_ts_users == 0) - dm1105_disable_irqs(dev); - - return 0; -} - -/* ir work handler */ -static void dm1105_emit_key(struct work_struct *work) -{ - struct infrared *ir = container_of(work, struct infrared, work); - u32 ircom = ir->ir_command; - u8 data; - - if (ir_debug) - printk(KERN_INFO "%s: received byte 0x%04x\n", __func__, ircom); - - data = (ircom >> 8) & 0x7f; - - rc_keydown(ir->dev, data, 0); -} - -/* work handler */ -static void dm1105_dmx_buffer(struct work_struct *work) -{ - struct dm1105_dev *dev = container_of(work, struct dm1105_dev, work); - unsigned int nbpackets; - u32 oldwrp = dev->wrp; - u32 nextwrp = dev->nextwrp; - - if (!((dev->ts_buf[oldwrp] == 0x47) && - (dev->ts_buf[oldwrp + 188] == 0x47) && - (dev->ts_buf[oldwrp + 188 * 2] == 0x47))) { - dev->PacketErrorCount++; - /* bad packet found */ - if ((dev->PacketErrorCount >= 2) && - (dev->dmarst == 0)) { - dm_writeb(DM1105_RST, 1); - dev->wrp = 0; - dev->PacketErrorCount = 0; - dev->dmarst = 0; - return; - } - } - - if (nextwrp < oldwrp) { - memcpy(dev->ts_buf + dev->buffer_size, dev->ts_buf, nextwrp); - nbpackets = ((dev->buffer_size - oldwrp) + nextwrp) / 188; - } else - nbpackets = (nextwrp - oldwrp) / 188; - - dev->wrp = nextwrp; - dvb_dmx_swfilter_packets(&dev->demux, &dev->ts_buf[oldwrp], nbpackets); -} - -static irqreturn_t dm1105_irq(int irq, void *dev_id) -{ - struct dm1105_dev *dev = dev_id; - - /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */ - unsigned int intsts = dm_readb(DM1105_INTSTS); - dm_writeb(DM1105_INTSTS, intsts); - - switch (intsts) { - case INTSTS_TSIRQ: - case (INTSTS_TSIRQ | INTSTS_IR): - dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR); - queue_work(dev->wq, &dev->work); - break; - case INTSTS_IR: - dev->ir.ir_command = dm_readl(DM1105_IRCODE); - schedule_work(&dev->ir.work); - break; - } - - return IRQ_HANDLED; -} - -int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) -{ - struct rc_dev *dev; - int err = -ENOMEM; - - dev = rc_allocate_device(); - if (!dev) - return -ENOMEM; - - snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), - "pci-%s/ir0", pci_name(dm1105->pdev)); - - dev->driver_name = MODULE_NAME; - dev->map_name = RC_MAP_DM1105_NEC; - dev->driver_type = RC_DRIVER_SCANCODE; - dev->input_name = "DVB on-card IR receiver"; - dev->input_phys = dm1105->ir.input_phys; - dev->input_id.bustype = BUS_PCI; - dev->input_id.version = 1; - if (dm1105->pdev->subsystem_vendor) { - dev->input_id.vendor = dm1105->pdev->subsystem_vendor; - dev->input_id.product = dm1105->pdev->subsystem_device; - } else { - dev->input_id.vendor = dm1105->pdev->vendor; - dev->input_id.product = dm1105->pdev->device; - } - dev->dev.parent = &dm1105->pdev->dev; - - INIT_WORK(&dm1105->ir.work, dm1105_emit_key); - - err = rc_register_device(dev); - if (err < 0) { - rc_free_device(dev); - return err; - } - - dm1105->ir.dev = dev; - return 0; -} - -void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105) -{ - rc_unregister_device(dm1105->ir.dev); -} - -static int __devinit dm1105_hw_init(struct dm1105_dev *dev) -{ - dm1105_disable_irqs(dev); - - dm_writeb(DM1105_HOST_CTR, 0); - - /*DATALEN 188,*/ - dm_writeb(DM1105_DTALENTH, 188); - /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/ - dm_writew(DM1105_TSCTR, 0xc10a); - - /* map DMA and set address */ - dm1105_dma_map(dev); - dm1105_set_dma_addr(dev); - /* big buffer */ - dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); - dm_writeb(DM1105_INTCNT, 47); - - /* IR NEC mode enable */ - dm_writeb(DM1105_IRCTR, (DM1105_IR_EN | DM1105_SYS_CHK)); - dm_writeb(DM1105_IRMODE, 0); - dm_writew(DM1105_SYSTEMCODE, 0); - - return 0; -} - -static void dm1105_hw_exit(struct dm1105_dev *dev) -{ - dm1105_disable_irqs(dev); - - /* IR disable */ - dm_writeb(DM1105_IRCTR, 0); - dm_writeb(DM1105_INTMAK, INTMAK_NONEMASK); - - dm1105_dma_unmap(dev); -} - -static struct stv0299_config sharp_z0194a_config = { - .demod_address = 0x68, - .inittab = sharp_z0194a_inittab, - .mclk = 88000000UL, - .invert = 1, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP1, - .min_delay_ms = 100, - .set_symbol_rate = sharp_z0194a_set_symbol_rate, -}; - -static struct stv0288_config earda_config = { - .demod_address = 0x68, - .min_delay_ms = 100, -}; - -static struct si21xx_config serit_config = { - .demod_address = 0x68, - .min_delay_ms = 100, - -}; - -static struct cx24116_config serit_sp2633_config = { - .demod_address = 0x55, -}; - -static struct ds3000_config dvbworld_ds3000_config = { - .demod_address = 0x68, -}; - -static int __devinit frontend_init(struct dm1105_dev *dev) -{ - int ret; - - switch (dev->boardnr) { - case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO: - dm1105_gpio_enable(dev, GPIO15, 1); - dm1105_gpio_clear(dev, GPIO15); - msleep(100); - dm1105_gpio_set(dev, GPIO15); - msleep(200); - dev->fe = dvb_attach( - stv0299_attach, &sharp_z0194a_config, - &dev->i2c_bb_adap); - if (dev->fe) { - dev->fe->ops.set_voltage = dm1105_set_voltage; - dvb_attach(dvb_pll_attach, dev->fe, 0x60, - &dev->i2c_bb_adap, DVB_PLL_OPERA1); - break; - } - - dev->fe = dvb_attach( - stv0288_attach, &earda_config, - &dev->i2c_bb_adap); - if (dev->fe) { - dev->fe->ops.set_voltage = dm1105_set_voltage; - dvb_attach(stb6000_attach, dev->fe, 0x61, - &dev->i2c_bb_adap); - break; - } - - dev->fe = dvb_attach( - si21xx_attach, &serit_config, - &dev->i2c_bb_adap); - if (dev->fe) - dev->fe->ops.set_voltage = dm1105_set_voltage; - break; - case DM1105_BOARD_DVBWORLD_2004: - dev->fe = dvb_attach( - cx24116_attach, &serit_sp2633_config, - &dev->i2c_adap); - if (dev->fe) { - dev->fe->ops.set_voltage = dm1105_set_voltage; - break; - } - - dev->fe = dvb_attach( - ds3000_attach, &dvbworld_ds3000_config, - &dev->i2c_adap); - if (dev->fe) - dev->fe->ops.set_voltage = dm1105_set_voltage; - - break; - case DM1105_BOARD_DVBWORLD_2002: - case DM1105_BOARD_AXESS_DM05: - default: - dev->fe = dvb_attach( - stv0299_attach, &sharp_z0194a_config, - &dev->i2c_adap); - if (dev->fe) { - dev->fe->ops.set_voltage = dm1105_set_voltage; - dvb_attach(dvb_pll_attach, dev->fe, 0x60, - &dev->i2c_adap, DVB_PLL_OPERA1); - break; - } - - dev->fe = dvb_attach( - stv0288_attach, &earda_config, - &dev->i2c_adap); - if (dev->fe) { - dev->fe->ops.set_voltage = dm1105_set_voltage; - dvb_attach(stb6000_attach, dev->fe, 0x61, - &dev->i2c_adap); - break; - } - - dev->fe = dvb_attach( - si21xx_attach, &serit_config, - &dev->i2c_adap); - if (dev->fe) - dev->fe->ops.set_voltage = dm1105_set_voltage; - - } - - if (!dev->fe) { - dev_err(&dev->pdev->dev, "could not attach frontend\n"); - return -ENODEV; - } - - ret = dvb_register_frontend(&dev->dvb_adapter, dev->fe); - if (ret < 0) { - if (dev->fe->ops.release) - dev->fe->ops.release(dev->fe); - dev->fe = NULL; - return ret; - } - - return 0; -} - -static void __devinit dm1105_read_mac(struct dm1105_dev *dev, u8 *mac) -{ - static u8 command[1] = { 0x28 }; - - struct i2c_msg msg[] = { - { - .addr = IIC_24C01_addr >> 1, - .flags = 0, - .buf = command, - .len = 1 - }, { - .addr = IIC_24C01_addr >> 1, - .flags = I2C_M_RD, - .buf = mac, - .len = 6 - }, - }; - - dm1105_i2c_xfer(&dev->i2c_adap, msg , 2); - dev_info(&dev->pdev->dev, "MAC %pM\n", mac); -} - -static int __devinit dm1105_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct dm1105_dev *dev; - struct dvb_adapter *dvb_adapter; - struct dvb_demux *dvbdemux; - struct dmx_demux *dmx; - int ret = -ENOMEM; - int i; - - dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - /* board config */ - dev->nr = dm1105_devcount; - dev->boardnr = UNSET; - if (card[dev->nr] < ARRAY_SIZE(dm1105_boards)) - dev->boardnr = card[dev->nr]; - for (i = 0; UNSET == dev->boardnr && - i < ARRAY_SIZE(dm1105_subids); i++) - if (pdev->subsystem_vendor == - dm1105_subids[i].subvendor && - pdev->subsystem_device == - dm1105_subids[i].subdevice) - dev->boardnr = dm1105_subids[i].card; - - if (UNSET == dev->boardnr) { - dev->boardnr = DM1105_BOARD_UNKNOWN; - dm1105_card_list(pdev); - } - - dm1105_devcount++; - dev->pdev = pdev; - dev->buffer_size = 5 * DM1105_DMA_BYTES; - dev->PacketErrorCount = 0; - dev->dmarst = 0; - - ret = pci_enable_device(pdev); - if (ret < 0) - goto err_kfree; - - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret < 0) - goto err_pci_disable_device; - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, DRIVER_NAME); - if (ret < 0) - goto err_pci_disable_device; - - dev->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); - if (!dev->io_mem) { - ret = -EIO; - goto err_pci_release_regions; - } - - spin_lock_init(&dev->lock); - pci_set_drvdata(pdev, dev); - - ret = dm1105_hw_init(dev); - if (ret < 0) - goto err_pci_iounmap; - - /* i2c */ - i2c_set_adapdata(&dev->i2c_adap, dev); - strcpy(dev->i2c_adap.name, DRIVER_NAME); - dev->i2c_adap.owner = THIS_MODULE; - dev->i2c_adap.dev.parent = &pdev->dev; - dev->i2c_adap.algo = &dm1105_algo; - dev->i2c_adap.algo_data = dev; - ret = i2c_add_adapter(&dev->i2c_adap); - - if (ret < 0) - goto err_dm1105_hw_exit; - - i2c_set_adapdata(&dev->i2c_bb_adap, dev); - strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME); - dev->i2c_bb_adap.owner = THIS_MODULE; - dev->i2c_bb_adap.dev.parent = &pdev->dev; - dev->i2c_bb_adap.algo_data = &dev->i2c_bit; - dev->i2c_bit.data = dev; - dev->i2c_bit.setsda = dm1105_setsda; - dev->i2c_bit.setscl = dm1105_setscl; - dev->i2c_bit.getsda = dm1105_getsda; - dev->i2c_bit.getscl = dm1105_getscl; - dev->i2c_bit.udelay = 10; - dev->i2c_bit.timeout = 10; - - /* Raise SCL and SDA */ - dm1105_setsda(dev, 1); - dm1105_setscl(dev, 1); - - ret = i2c_bit_add_bus(&dev->i2c_bb_adap); - if (ret < 0) - goto err_i2c_del_adapter; - - /* dvb */ - ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, - THIS_MODULE, &pdev->dev, adapter_nr); - if (ret < 0) - goto err_i2c_del_adapters; - - dvb_adapter = &dev->dvb_adapter; - - dm1105_read_mac(dev, dvb_adapter->proposed_mac); - - dvbdemux = &dev->demux; - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = dm1105_start_feed; - dvbdemux->stop_feed = dm1105_stop_feed; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | - DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); - ret = dvb_dmx_init(dvbdemux); - if (ret < 0) - goto err_dvb_unregister_adapter; - - dmx = &dvbdemux->dmx; - dev->dmxdev.filternum = 256; - dev->dmxdev.demux = dmx; - dev->dmxdev.capabilities = 0; - - ret = dvb_dmxdev_init(&dev->dmxdev, dvb_adapter); - if (ret < 0) - goto err_dvb_dmx_release; - - dev->hw_frontend.source = DMX_FRONTEND_0; - - ret = dmx->add_frontend(dmx, &dev->hw_frontend); - if (ret < 0) - goto err_dvb_dmxdev_release; - - dev->mem_frontend.source = DMX_MEMORY_FE; - - ret = dmx->add_frontend(dmx, &dev->mem_frontend); - if (ret < 0) - goto err_remove_hw_frontend; - - ret = dmx->connect_frontend(dmx, &dev->hw_frontend); - if (ret < 0) - goto err_remove_mem_frontend; - - ret = dvb_net_init(dvb_adapter, &dev->dvbnet, dmx); - if (ret < 0) - goto err_disconnect_frontend; - - ret = frontend_init(dev); - if (ret < 0) - goto err_dvb_net; - - dm1105_ir_init(dev); - - INIT_WORK(&dev->work, dm1105_dmx_buffer); - sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num); - dev->wq = create_singlethread_workqueue(dev->wqn); - if (!dev->wq) - goto err_dvb_net; - - ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED, - DRIVER_NAME, dev); - if (ret < 0) - goto err_workqueue; - - return 0; - -err_workqueue: - destroy_workqueue(dev->wq); -err_dvb_net: - dvb_net_release(&dev->dvbnet); -err_disconnect_frontend: - dmx->disconnect_frontend(dmx); -err_remove_mem_frontend: - dmx->remove_frontend(dmx, &dev->mem_frontend); -err_remove_hw_frontend: - dmx->remove_frontend(dmx, &dev->hw_frontend); -err_dvb_dmxdev_release: - dvb_dmxdev_release(&dev->dmxdev); -err_dvb_dmx_release: - dvb_dmx_release(dvbdemux); -err_dvb_unregister_adapter: - dvb_unregister_adapter(dvb_adapter); -err_i2c_del_adapters: - i2c_del_adapter(&dev->i2c_bb_adap); -err_i2c_del_adapter: - i2c_del_adapter(&dev->i2c_adap); -err_dm1105_hw_exit: - dm1105_hw_exit(dev); -err_pci_iounmap: - pci_iounmap(pdev, dev->io_mem); -err_pci_release_regions: - pci_release_regions(pdev); -err_pci_disable_device: - pci_disable_device(pdev); -err_kfree: - pci_set_drvdata(pdev, NULL); - kfree(dev); - return ret; -} - -static void __devexit dm1105_remove(struct pci_dev *pdev) -{ - struct dm1105_dev *dev = pci_get_drvdata(pdev); - struct dvb_adapter *dvb_adapter = &dev->dvb_adapter; - struct dvb_demux *dvbdemux = &dev->demux; - struct dmx_demux *dmx = &dvbdemux->dmx; - - dm1105_ir_exit(dev); - dmx->close(dmx); - dvb_net_release(&dev->dvbnet); - if (dev->fe) - dvb_unregister_frontend(dev->fe); - - dmx->disconnect_frontend(dmx); - dmx->remove_frontend(dmx, &dev->mem_frontend); - dmx->remove_frontend(dmx, &dev->hw_frontend); - dvb_dmxdev_release(&dev->dmxdev); - dvb_dmx_release(dvbdemux); - dvb_unregister_adapter(dvb_adapter); - if (&dev->i2c_adap) - i2c_del_adapter(&dev->i2c_adap); - - dm1105_hw_exit(dev); - synchronize_irq(pdev->irq); - free_irq(pdev->irq, dev); - pci_iounmap(pdev, dev->io_mem); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - dm1105_devcount--; - kfree(dev); -} - -static struct pci_device_id dm1105_id_table[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_TRIGEM, - .device = PCI_DEVICE_ID_DM1105, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, { - .vendor = PCI_VENDOR_ID_AXESS, - .device = PCI_DEVICE_ID_DM05, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, { - /* empty */ - }, -}; - -MODULE_DEVICE_TABLE(pci, dm1105_id_table); - -static struct pci_driver dm1105_driver = { - .name = DRIVER_NAME, - .id_table = dm1105_id_table, - .probe = dm1105_probe, - .remove = __devexit_p(dm1105_remove), -}; - -static int __init dm1105_init(void) -{ - return pci_register_driver(&dm1105_driver); -} - -static void __exit dm1105_exit(void) -{ - pci_unregister_driver(&dm1105_driver); -} - -module_init(dm1105_init); -module_exit(dm1105_exit); - -MODULE_AUTHOR("Igor M. Liplianin "); -MODULE_DESCRIPTION("SDMC DM1105 DVB driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/mantis/Kconfig b/drivers/media/dvb/mantis/Kconfig deleted file mode 100644 index a13a50503134..000000000000 --- a/drivers/media/dvb/mantis/Kconfig +++ /dev/null @@ -1,38 +0,0 @@ -config MANTIS_CORE - tristate "Mantis/Hopper PCI bridge based devices" - depends on PCI && I2C && INPUT && RC_CORE - - help - Support for PCI cards based on the Mantis and Hopper PCi bridge. - - Say Y if you own such a device and want to use it. - -config DVB_MANTIS - tristate "MANTIS based cards" - depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_MB86A16 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_TDA665x if !DVB_FE_CUSTOMISE - select DVB_TDA10021 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_PLL - help - Support for PCI cards based on the Mantis PCI bridge. - Say Y when you have a Mantis based DVB card and want to use it. - - If unsure say N. - -config DVB_HOPPER - tristate "HOPPER based cards" - depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_PLL - help - Support for PCI cards based on the Hopper PCI bridge. - Say Y when you have a Hopper based DVB card and want to use it. - - If unsure say N diff --git a/drivers/media/dvb/mantis/Makefile b/drivers/media/dvb/mantis/Makefile deleted file mode 100644 index f715051e4453..000000000000 --- a/drivers/media/dvb/mantis/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -mantis_core-objs := mantis_ioc.o \ - mantis_uart.o \ - mantis_dma.o \ - mantis_pci.o \ - mantis_i2c.o \ - mantis_dvb.o \ - mantis_evm.o \ - mantis_hif.o \ - mantis_ca.o \ - mantis_pcmcia.o \ - mantis_input.o - -mantis-objs := mantis_cards.o \ - mantis_vp1033.o \ - mantis_vp1034.o \ - mantis_vp1041.o \ - mantis_vp2033.o \ - mantis_vp2040.o \ - mantis_vp3030.o - -hopper-objs := hopper_cards.o \ - hopper_vp3028.o - -obj-$(CONFIG_MANTIS_CORE) += mantis_core.o -obj-$(CONFIG_DVB_MANTIS) += mantis.o -obj-$(CONFIG_DVB_HOPPER) += hopper.o - -ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/dvb/mantis/hopper_cards.c deleted file mode 100644 index cc0251e01077..000000000000 --- a/drivers/media/dvb/mantis/hopper_cards.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - Hopper PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "hopper_vp3028.h" -#include "mantis_dma.h" -#include "mantis_dvb.h" -#include "mantis_uart.h" -#include "mantis_ioc.h" -#include "mantis_pci.h" -#include "mantis_i2c.h" -#include "mantis_reg.h" - -static unsigned int verbose; -module_param(verbose, int, 0644); -MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); - -#define DRIVER_NAME "Hopper" - -static char *label[10] = { - "DMA", - "IRQ-0", - "IRQ-1", - "OCERR", - "PABRT", - "RIPRR", - "PPERR", - "FTRGT", - "RISCI", - "RACK" -}; - -static int devs; - -static irqreturn_t hopper_irq_handler(int irq, void *dev_id) -{ - u32 stat = 0, mask = 0; - u32 rst_stat = 0, rst_mask = 0; - - struct mantis_pci *mantis; - struct mantis_ca *ca; - - mantis = (struct mantis_pci *) dev_id; - if (unlikely(mantis == NULL)) { - dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); - return IRQ_NONE; - } - ca = mantis->mantis_ca; - - stat = mmread(MANTIS_INT_STAT); - mask = mmread(MANTIS_INT_MASK); - if (!(stat & mask)) - return IRQ_NONE; - - rst_mask = MANTIS_GPIF_WRACK | - MANTIS_GPIF_OTHERR | - MANTIS_SBUF_WSTO | - MANTIS_GPIF_EXTIRQ; - - rst_stat = mmread(MANTIS_GPIF_STATUS); - rst_stat &= rst_mask; - mmwrite(rst_stat, MANTIS_GPIF_STATUS); - - mantis->mantis_int_stat = stat; - mantis->mantis_int_mask = mask; - dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); - if (stat & MANTIS_INT_RISCEN) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); - } - if (stat & MANTIS_INT_IRQ0) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); - mantis->gpif_status = rst_stat; - wake_up(&ca->hif_write_wq); - schedule_work(&ca->hif_evm_work); - } - if (stat & MANTIS_INT_IRQ1) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); - schedule_work(&mantis->uart_work); - } - if (stat & MANTIS_INT_OCERR) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); - } - if (stat & MANTIS_INT_PABORT) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); - } - if (stat & MANTIS_INT_RIPERR) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); - } - if (stat & MANTIS_INT_PPERR) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); - } - if (stat & MANTIS_INT_FTRGT) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); - } - if (stat & MANTIS_INT_RISCI) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); - mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; - tasklet_schedule(&mantis->tasklet); - } - if (stat & MANTIS_INT_I2CDONE) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); - wake_up(&mantis->i2c_wq); - } - mmwrite(stat, MANTIS_INT_STAT); - stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | - MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | - MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | - MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | - MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | - MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | - MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | - MANTIS_INT_PABORT | MANTIS_INT_RIPERR | - MANTIS_INT_PPERR | MANTIS_INT_FTRGT | - MANTIS_INT_RISCI); - - if (stat) - dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); - - dprintk(MANTIS_DEBUG, 0, "\n"); - return IRQ_HANDLED; -} - -static int __devinit hopper_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) -{ - struct mantis_pci *mantis; - struct mantis_hwconfig *config; - int err = 0; - - mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); - if (mantis == NULL) { - printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); - err = -ENOMEM; - goto fail0; - } - - mantis->num = devs; - mantis->verbose = verbose; - mantis->pdev = pdev; - config = (struct mantis_hwconfig *) pci_id->driver_data; - config->irq_handler = &hopper_irq_handler; - mantis->hwconfig = config; - - err = mantis_pci_init(mantis); - if (err) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); - goto fail1; - } - - err = mantis_stream_control(mantis, STREAM_TO_HIF); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); - goto fail1; - } - - err = mantis_i2c_init(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); - goto fail2; - } - - err = mantis_get_mac(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); - goto fail2; - } - - err = mantis_dma_init(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); - goto fail3; - } - - err = mantis_dvb_init(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); - goto fail4; - } - devs++; - - return err; - -fail4: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); - mantis_dma_exit(mantis); - -fail3: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); - mantis_i2c_exit(mantis); - -fail2: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); - mantis_pci_exit(mantis); - -fail1: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); - kfree(mantis); - -fail0: - return err; -} - -static void __devexit hopper_pci_remove(struct pci_dev *pdev) -{ - struct mantis_pci *mantis = pci_get_drvdata(pdev); - - if (mantis) { - mantis_dvb_exit(mantis); - mantis_dma_exit(mantis); - mantis_i2c_exit(mantis); - mantis_pci_exit(mantis); - kfree(mantis); - } - return; - -} - -static struct pci_device_id hopper_pci_table[] = { - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3028_DVB_T, &vp3028_config), - { } -}; - -MODULE_DEVICE_TABLE(pci, hopper_pci_table); - -static struct pci_driver hopper_pci_driver = { - .name = DRIVER_NAME, - .id_table = hopper_pci_table, - .probe = hopper_pci_probe, - .remove = hopper_pci_remove, -}; - -static int __devinit hopper_init(void) -{ - return pci_register_driver(&hopper_pci_driver); -} - -static void __devexit hopper_exit(void) -{ - return pci_unregister_driver(&hopper_pci_driver); -} - -module_init(hopper_init); -module_exit(hopper_exit); - -MODULE_DESCRIPTION("HOPPER driver"); -MODULE_AUTHOR("Manu Abraham"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/mantis/hopper_vp3028.c b/drivers/media/dvb/mantis/hopper_vp3028.c deleted file mode 100644 index 68a29f8bdf73..000000000000 --- a/drivers/media/dvb/mantis/hopper_vp3028.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - Hopper VP-3028 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "zl10353.h" -#include "mantis_common.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" -#include "hopper_vp3028.h" - -struct zl10353_config hopper_vp3028_config = { - .demod_address = 0x0f, -}; - -#define MANTIS_MODEL_NAME "VP-3028" -#define MANTIS_DEV_TYPE "DVB-T" - -static int vp3028_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) -{ - struct i2c_adapter *adapter = &mantis->adapter; - struct mantis_hwconfig *config = mantis->hwconfig; - int err = 0; - - mantis_gpio_set_bits(mantis, config->reset, 0); - msleep(100); - err = mantis_frontend_power(mantis, POWER_ON); - msleep(100); - mantis_gpio_set_bits(mantis, config->reset, 1); - - err = mantis_frontend_power(mantis, POWER_ON); - if (err == 0) { - msleep(250); - dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); - fe = dvb_attach(zl10353_attach, &hopper_vp3028_config, adapter); - - if (!fe) - return -1; - } else { - dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", - adapter->name, - err); - - return -EIO; - } - dprintk(MANTIS_ERROR, 1, "Done!"); - - return 0; -} - -struct mantis_hwconfig vp3028_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_188, - - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, - - .frontend_init = vp3028_frontend_init, - .power = GPIF_A00, - .reset = GPIF_A03, -}; diff --git a/drivers/media/dvb/mantis/hopper_vp3028.h b/drivers/media/dvb/mantis/hopper_vp3028.h deleted file mode 100644 index 57239498bc87..000000000000 --- a/drivers/media/dvb/mantis/hopper_vp3028.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Hopper VP-3028 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP3028_H -#define __MANTIS_VP3028_H - -#include "mantis_common.h" - -#define MANTIS_VP_3028_DVB_T 0x0028 - -extern struct mantis_hwconfig vp3028_config; - -#endif /* __MANTIS_VP3028_H */ diff --git a/drivers/media/dvb/mantis/mantis_ca.c b/drivers/media/dvb/mantis/mantis_ca.c deleted file mode 100644 index 3d7046909009..000000000000 --- a/drivers/media/dvb/mantis/mantis_ca.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_link.h" -#include "mantis_hif.h" -#include "mantis_reg.h" - -#include "mantis_ca.h" - -static int mantis_ca_read_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Read", slot); - - if (slot != 0) - return -EINVAL; - - return mantis_hif_read_mem(ca, addr); -} - -static int mantis_ca_write_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr, u8 data) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Write", slot); - - if (slot != 0) - return -EINVAL; - - return mantis_hif_write_mem(ca, addr, data); -} - -static int mantis_ca_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Read", slot); - - if (slot != 0) - return -EINVAL; - - return mantis_hif_read_iom(ca, addr); -} - -static int mantis_ca_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr, u8 data) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Write", slot); - - if (slot != 0) - return -EINVAL; - - return mantis_hif_write_iom(ca, addr, data); -} - -static int mantis_ca_slot_reset(struct dvb_ca_en50221 *en50221, int slot) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot RESET", slot); - udelay(500); /* Wait.. */ - mmwrite(0xda, MANTIS_PCMCIA_RESET); /* Leading edge assert */ - udelay(500); - mmwrite(0x00, MANTIS_PCMCIA_RESET); /* Trailing edge deassert */ - msleep(1000); - dvb_ca_en50221_camready_irq(&ca->en50221, 0); - - return 0; -} - -static int mantis_ca_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot shutdown", slot); - - return 0; -} - -static int mantis_ts_control(struct dvb_ca_en50221 *en50221, int slot) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): TS control", slot); -/* mantis_set_direction(mantis, 1); */ /* Enable TS through CAM */ - - return 0; -} - -static int mantis_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open) -{ - struct mantis_ca *ca = en50221->data; - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Slot(%d): Poll Slot status", slot); - - if (ca->slot_state == MODULE_INSERTED) { - dprintk(MANTIS_DEBUG, 1, "CA Module present and ready"); - return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; - } else { - dprintk(MANTIS_DEBUG, 1, "CA Module not present or not ready"); - } - - return 0; -} - -int mantis_ca_init(struct mantis_pci *mantis) -{ - struct dvb_adapter *dvb_adapter = &mantis->dvb_adapter; - struct mantis_ca *ca; - int ca_flags = 0, result; - - dprintk(MANTIS_DEBUG, 1, "Initializing Mantis CA"); - ca = kzalloc(sizeof(struct mantis_ca), GFP_KERNEL); - if (!ca) { - dprintk(MANTIS_ERROR, 1, "Out of memory!, exiting .."); - result = -ENOMEM; - goto err; - } - - ca->ca_priv = mantis; - mantis->mantis_ca = ca; - ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE; - /* register CA interface */ - ca->en50221.owner = THIS_MODULE; - ca->en50221.read_attribute_mem = mantis_ca_read_attr_mem; - ca->en50221.write_attribute_mem = mantis_ca_write_attr_mem; - ca->en50221.read_cam_control = mantis_ca_read_cam_ctl; - ca->en50221.write_cam_control = mantis_ca_write_cam_ctl; - ca->en50221.slot_reset = mantis_ca_slot_reset; - ca->en50221.slot_shutdown = mantis_ca_slot_shutdown; - ca->en50221.slot_ts_enable = mantis_ts_control; - ca->en50221.poll_slot_status = mantis_slot_status; - ca->en50221.data = ca; - - mutex_init(&ca->ca_lock); - - init_waitqueue_head(&ca->hif_data_wq); - init_waitqueue_head(&ca->hif_opdone_wq); - init_waitqueue_head(&ca->hif_write_wq); - - dprintk(MANTIS_ERROR, 1, "Registering EN50221 device"); - result = dvb_ca_en50221_init(dvb_adapter, &ca->en50221, ca_flags, 1); - if (result != 0) { - dprintk(MANTIS_ERROR, 1, "EN50221: Initialization failed <%d>", result); - goto err; - } - dprintk(MANTIS_ERROR, 1, "Registered EN50221 device"); - mantis_evmgr_init(ca); - return 0; -err: - kfree(ca); - return result; -} -EXPORT_SYMBOL_GPL(mantis_ca_init); - -void mantis_ca_exit(struct mantis_pci *mantis) -{ - struct mantis_ca *ca = mantis->mantis_ca; - - dprintk(MANTIS_DEBUG, 1, "Mantis CA exit"); - - mantis_evmgr_exit(ca); - dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device"); - if (ca) - dvb_ca_en50221_release(&ca->en50221); - - kfree(ca); -} -EXPORT_SYMBOL_GPL(mantis_ca_exit); diff --git a/drivers/media/dvb/mantis/mantis_ca.h b/drivers/media/dvb/mantis/mantis_ca.h deleted file mode 100644 index dc63e55f7eca..000000000000 --- a/drivers/media/dvb/mantis/mantis_ca.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_CA_H -#define __MANTIS_CA_H - -extern int mantis_ca_init(struct mantis_pci *mantis); -extern void mantis_ca_exit(struct mantis_pci *mantis); - -#endif /* __MANTIS_CA_H */ diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c deleted file mode 100644 index 0207d1f064e0..000000000000 --- a/drivers/media/dvb/mantis/mantis_cards.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" - -#include "mantis_vp1033.h" -#include "mantis_vp1034.h" -#include "mantis_vp1041.h" -#include "mantis_vp2033.h" -#include "mantis_vp2040.h" -#include "mantis_vp3030.h" - -#include "mantis_dma.h" -#include "mantis_ca.h" -#include "mantis_dvb.h" -#include "mantis_uart.h" -#include "mantis_ioc.h" -#include "mantis_pci.h" -#include "mantis_i2c.h" -#include "mantis_reg.h" - -static unsigned int verbose; -module_param(verbose, int, 0644); -MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); - -static int devs; - -#define DRIVER_NAME "Mantis" - -static char *label[10] = { - "DMA", - "IRQ-0", - "IRQ-1", - "OCERR", - "PABRT", - "RIPRR", - "PPERR", - "FTRGT", - "RISCI", - "RACK" -}; - -static irqreturn_t mantis_irq_handler(int irq, void *dev_id) -{ - u32 stat = 0, mask = 0; - u32 rst_stat = 0, rst_mask = 0; - - struct mantis_pci *mantis; - struct mantis_ca *ca; - - mantis = (struct mantis_pci *) dev_id; - if (unlikely(mantis == NULL)) { - dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); - return IRQ_NONE; - } - ca = mantis->mantis_ca; - - stat = mmread(MANTIS_INT_STAT); - mask = mmread(MANTIS_INT_MASK); - if (!(stat & mask)) - return IRQ_NONE; - - rst_mask = MANTIS_GPIF_WRACK | - MANTIS_GPIF_OTHERR | - MANTIS_SBUF_WSTO | - MANTIS_GPIF_EXTIRQ; - - rst_stat = mmread(MANTIS_GPIF_STATUS); - rst_stat &= rst_mask; - mmwrite(rst_stat, MANTIS_GPIF_STATUS); - - mantis->mantis_int_stat = stat; - mantis->mantis_int_mask = mask; - dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); - if (stat & MANTIS_INT_RISCEN) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); - } - if (stat & MANTIS_INT_IRQ0) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); - mantis->gpif_status = rst_stat; - wake_up(&ca->hif_write_wq); - schedule_work(&ca->hif_evm_work); - } - if (stat & MANTIS_INT_IRQ1) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); - schedule_work(&mantis->uart_work); - } - if (stat & MANTIS_INT_OCERR) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); - } - if (stat & MANTIS_INT_PABORT) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); - } - if (stat & MANTIS_INT_RIPERR) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); - } - if (stat & MANTIS_INT_PPERR) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); - } - if (stat & MANTIS_INT_FTRGT) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); - } - if (stat & MANTIS_INT_RISCI) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); - mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; - tasklet_schedule(&mantis->tasklet); - } - if (stat & MANTIS_INT_I2CDONE) { - dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); - wake_up(&mantis->i2c_wq); - } - mmwrite(stat, MANTIS_INT_STAT); - stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | - MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | - MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | - MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | - MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | - MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | - MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | - MANTIS_INT_PABORT | MANTIS_INT_RIPERR | - MANTIS_INT_PPERR | MANTIS_INT_FTRGT | - MANTIS_INT_RISCI); - - if (stat) - dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); - - dprintk(MANTIS_DEBUG, 0, "\n"); - return IRQ_HANDLED; -} - -static int __devinit mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) -{ - struct mantis_pci *mantis; - struct mantis_hwconfig *config; - int err = 0; - - mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); - if (mantis == NULL) { - printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); - err = -ENOMEM; - goto fail0; - } - - mantis->num = devs; - mantis->verbose = verbose; - mantis->pdev = pdev; - config = (struct mantis_hwconfig *) pci_id->driver_data; - config->irq_handler = &mantis_irq_handler; - mantis->hwconfig = config; - - err = mantis_pci_init(mantis); - if (err) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); - goto fail1; - } - - err = mantis_stream_control(mantis, STREAM_TO_HIF); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); - goto fail1; - } - - err = mantis_i2c_init(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); - goto fail2; - } - - err = mantis_get_mac(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); - goto fail2; - } - - err = mantis_dma_init(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); - goto fail3; - } - - err = mantis_dvb_init(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); - goto fail4; - } - err = mantis_uart_init(mantis); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err); - goto fail6; - } - - devs++; - - return err; - - - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err); - mantis_uart_exit(mantis); - -fail6: -fail4: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); - mantis_dma_exit(mantis); - -fail3: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); - mantis_i2c_exit(mantis); - -fail2: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); - mantis_pci_exit(mantis); - -fail1: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); - kfree(mantis); - -fail0: - return err; -} - -static void __devexit mantis_pci_remove(struct pci_dev *pdev) -{ - struct mantis_pci *mantis = pci_get_drvdata(pdev); - - if (mantis) { - - mantis_uart_exit(mantis); - mantis_dvb_exit(mantis); - mantis_dma_exit(mantis); - mantis_i2c_exit(mantis); - mantis_pci_exit(mantis); - kfree(mantis); - } - return; -} - -static struct pci_device_id mantis_pci_table[] = { - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config), - MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config), - MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config), - MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config), - MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config), - MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2040_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config), - { } -}; - -MODULE_DEVICE_TABLE(pci, mantis_pci_table); - -static struct pci_driver mantis_pci_driver = { - .name = DRIVER_NAME, - .id_table = mantis_pci_table, - .probe = mantis_pci_probe, - .remove = mantis_pci_remove, -}; - -static int __devinit mantis_init(void) -{ - return pci_register_driver(&mantis_pci_driver); -} - -static void __devexit mantis_exit(void) -{ - return pci_unregister_driver(&mantis_pci_driver); -} - -module_init(mantis_init); -module_exit(mantis_exit); - -MODULE_DESCRIPTION("MANTIS driver"); -MODULE_AUTHOR("Manu Abraham"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/mantis/mantis_common.h b/drivers/media/dvb/mantis/mantis_common.h deleted file mode 100644 index f2410cf0a6bf..000000000000 --- a/drivers/media/dvb/mantis/mantis_common.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_COMMON_H -#define __MANTIS_COMMON_H - -#include -#include -#include - -#include "mantis_uart.h" - -#include "mantis_link.h" - -#define MANTIS_ERROR 0 -#define MANTIS_NOTICE 1 -#define MANTIS_INFO 2 -#define MANTIS_DEBUG 3 -#define MANTIS_TMG 9 - -#define dprintk(y, z, format, arg...) do { \ - if (z) { \ - if ((mantis->verbose > MANTIS_ERROR) && (mantis->verbose > y)) \ - printk(KERN_ERR "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ - else if ((mantis->verbose > MANTIS_NOTICE) && (mantis->verbose > y)) \ - printk(KERN_NOTICE "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ - else if ((mantis->verbose > MANTIS_INFO) && (mantis->verbose > y)) \ - printk(KERN_INFO "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ - else if ((mantis->verbose > MANTIS_DEBUG) && (mantis->verbose > y)) \ - printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ - else if ((mantis->verbose > MANTIS_TMG) && (mantis->verbose > y)) \ - printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ - } else { \ - if (mantis->verbose > y) \ - printk(format , ##arg); \ - } \ -} while(0) - -#define mwrite(dat, addr) writel((dat), addr) -#define mread(addr) readl(addr) - -#define mmwrite(dat, addr) mwrite((dat), (mantis->mmio + (addr))) -#define mmread(addr) mread(mantis->mmio + (addr)) - -#define MANTIS_TS_188 0 -#define MANTIS_TS_204 1 - -#define TWINHAN_TECHNOLOGIES 0x1822 -#define MANTIS 0x4e35 - -#define TECHNISAT 0x1ae4 -#define TERRATEC 0x153b - -#define MAKE_ENTRY(__subven, __subdev, __configptr) { \ - .vendor = TWINHAN_TECHNOLOGIES, \ - .device = MANTIS, \ - .subvendor = (__subven), \ - .subdevice = (__subdev), \ - .driver_data = (unsigned long) (__configptr) \ -} - -enum mantis_i2c_mode { - MANTIS_PAGE_MODE = 0, - MANTIS_BYTE_MODE, -}; - -struct mantis_pci; - -struct mantis_hwconfig { - char *model_name; - char *dev_type; - u32 ts_size; - - enum mantis_baud baud_rate; - enum mantis_parity parity; - u32 bytes; - - irqreturn_t (*irq_handler)(int irq, void *dev_id); - int (*frontend_init)(struct mantis_pci *mantis, struct dvb_frontend *fe); - - u8 power; - u8 reset; - - enum mantis_i2c_mode i2c_mode; -}; - -struct mantis_pci { - unsigned int verbose; - - /* PCI stuff */ - u16 vendor_id; - u16 device_id; - u16 subsystem_vendor; - u16 subsystem_device; - - u8 latency; - - struct pci_dev *pdev; - - unsigned long mantis_addr; - void __iomem *mmio; - - u8 irq; - u8 revision; - - unsigned int num; - - /* RISC Core */ - u32 busy_block; - u32 last_block; - u8 *buf_cpu; - dma_addr_t buf_dma; - u32 *risc_cpu; - dma_addr_t risc_dma; - - struct tasklet_struct tasklet; - - struct i2c_adapter adapter; - int i2c_rc; - wait_queue_head_t i2c_wq; - struct mutex i2c_lock; - - /* DVB stuff */ - struct dvb_adapter dvb_adapter; - struct dvb_frontend *fe; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dmx_frontend fe_hw; - struct dmx_frontend fe_mem; - struct dvb_net dvbnet; - - u8 feeds; - - struct mantis_hwconfig *hwconfig; - - u32 mantis_int_stat; - u32 mantis_int_mask; - - /* board specific */ - u8 mac_address[8]; - u32 sub_vendor_id; - u32 sub_device_id; - - /* A12 A13 A14 */ - u32 gpio_status; - - u32 gpif_status; - - struct mantis_ca *mantis_ca; - - wait_queue_head_t uart_wq; - struct work_struct uart_work; - spinlock_t uart_lock; - - struct rc_dev *rc; - char input_name[80]; - char input_phys[80]; -}; - -#define MANTIS_HIF_STATUS (mantis->gpio_status) - -#endif /* __MANTIS_COMMON_H */ diff --git a/drivers/media/dvb/mantis/mantis_core.c b/drivers/media/dvb/mantis/mantis_core.c deleted file mode 100644 index 684d9061fe2a..000000000000 --- a/drivers/media/dvb/mantis/mantis_core.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "mantis_common.h" -#include "mantis_core.h" -#include "mantis_vp1033.h" -#include "mantis_vp1034.h" -#include "mantis_vp1041.h" -#include "mantis_vp2033.h" -#include "mantis_vp2040.h" -#include "mantis_vp3030.h" - -static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) -{ - int err; - struct i2c_msg msg[] = { - { - .addr = 0x50, - .flags = 0, - .buf = data, - .len = 1 - }, { - .addr = 0x50, - .flags = I2C_M_RD, - .buf = data, - .len = length - }, - }; - - err = i2c_transfer(&mantis->adapter, msg, 2); - if (err < 0) { - dprintk(verbose, MANTIS_ERROR, 1, - "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", - err, data[0], data[1]); - - return err; - } - - return 0; -} - -static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) -{ - int err; - - struct i2c_msg msg = { - .addr = 0x50, - .flags = 0, - .buf = data, - .len = length - }; - - err = i2c_transfer(&mantis->adapter, &msg, 1); - if (err < 0) { - dprintk(verbose, MANTIS_ERROR, 1, - "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >", - err, length, data[0], data[1]); - - return err; - } - - return 0; -} - -static int get_mac_address(struct mantis_pci *mantis) -{ - int err; - - mantis->mac_address[0] = 0x08; - err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6); - if (err < 0) { - dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error"); - - return err; - } - dprintk(verbose, MANTIS_ERROR, 0, - " MAC Address=[%pM]\n", mantis->mac_address); - - return 0; -} - -#define MANTIS_MODEL_UNKNOWN "UNKNOWN" -#define MANTIS_DEV_UNKNOWN "UNKNOWN" - -struct mantis_hwconfig unknown_device = { - .model_name = MANTIS_MODEL_UNKNOWN, - .dev_type = MANTIS_DEV_UNKNOWN, -}; - -static void mantis_load_config(struct mantis_pci *mantis) -{ - switch (mantis->subsystem_device) { - case MANTIS_VP_1033_DVB_S: /* VP-1033 */ - mantis->hwconfig = &vp1033_mantis_config; - break; - case MANTIS_VP_1034_DVB_S: /* VP-1034 */ - mantis->hwconfig = &vp1034_mantis_config; - break; - case MANTIS_VP_1041_DVB_S2: /* VP-1041 */ - case TECHNISAT_SKYSTAR_HD2: - mantis->hwconfig = &vp1041_mantis_config; - break; - case MANTIS_VP_2033_DVB_C: /* VP-2033 */ - mantis->hwconfig = &vp2033_mantis_config; - break; - case MANTIS_VP_2040_DVB_C: /* VP-2040 */ - case CINERGY_C: /* VP-2040 clone */ - case TECHNISAT_CABLESTAR_HD2: - mantis->hwconfig = &vp2040_mantis_config; - break; - case MANTIS_VP_3030_DVB_T: /* VP-3030 */ - mantis->hwconfig = &vp3030_mantis_config; - break; - default: - mantis->hwconfig = &unknown_device; - break; - } -} - -int mantis_core_init(struct mantis_pci *mantis) -{ - int err = 0; - - mantis_load_config(mantis); - dprintk(verbose, MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", - mantis->hwconfig->model_name, mantis->hwconfig->dev_type, - mantis->pdev->bus->number, PCI_SLOT(mantis->pdev->devfn), PCI_FUNC(mantis->pdev->devfn)); - dprintk(verbose, MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", - mantis->revision, - mantis->subsystem_vendor, mantis->subsystem_device); - dprintk(verbose, MANTIS_ERROR, 0, - "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", - mantis->pdev->irq, mantis->latency, - mantis->mantis_addr, mantis->mantis_mmio); - - err = mantis_i2c_init(mantis); - if (err < 0) { - dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed"); - return err; - } - err = get_mac_address(mantis); - if (err < 0) { - dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed"); - return err; - } - err = mantis_dma_init(mantis); - if (err < 0) { - dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed"); - return err; - } - err = mantis_dvb_init(mantis); - if (err < 0) { - dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed"); - return err; - } - err = mantis_uart_init(mantis); - if (err < 0) { - dprintk(verbose, MANTIS_DEBUG, 1, "Mantis UART init failed"); - return err; - } - - return 0; -} - -int mantis_core_exit(struct mantis_pci *mantis) -{ - mantis_dma_stop(mantis); - dprintk(verbose, MANTIS_ERROR, 1, "DMA engine stopping"); - - mantis_uart_exit(mantis); - dprintk(verbose, MANTIS_ERROR, 1, "UART exit failed"); - - if (mantis_dma_exit(mantis) < 0) - dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed"); - if (mantis_dvb_exit(mantis) < 0) - dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed"); - if (mantis_i2c_exit(mantis) < 0) - dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed"); - - return 0; -} - -/* Turn the given bit on or off. */ -void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) -{ - u32 cur; - - cur = mmread(MANTIS_GPIF_ADDR); - if (value) - mantis->gpio_status = cur | (1 << bitpos); - else - mantis->gpio_status = cur & (~(1 << bitpos)); - - mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); - mmwrite(0x00, MANTIS_GPIF_DOUT); - udelay(100); -} - -/* direction = 0 , no CI passthrough ; 1 , CI passthrough */ -void mantis_set_direction(struct mantis_pci *mantis, int direction) -{ - u32 reg; - - reg = mmread(0x28); - dprintk(verbose, MANTIS_DEBUG, 1, "TS direction setup"); - if (direction == 0x01) { - /* to CI */ - reg |= 0x04; - mmwrite(reg, 0x28); - reg &= 0xff - 0x04; - mmwrite(reg, 0x28); - } else { - reg &= 0xff - 0x04; - mmwrite(reg, 0x28); - reg |= 0x04; - mmwrite(reg, 0x28); - } -} diff --git a/drivers/media/dvb/mantis/mantis_core.h b/drivers/media/dvb/mantis/mantis_core.h deleted file mode 100644 index 833ee42e694e..000000000000 --- a/drivers/media/dvb/mantis/mantis_core.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_CORE_H -#define __MANTIS_CORE_H - -#include "mantis_common.h" - - -#define FE_TYPE_SAT 0 -#define FE_TYPE_CAB 1 -#define FE_TYPE_TER 2 - -#define FE_TYPE_TS204 0 -#define FE_TYPE_TS188 1 - - -struct vendorname { - u8 *sub_vendor_name; - u32 sub_vendor_id; -}; - -struct devicetype { - u8 *sub_device_name; - u32 sub_device_id; - u8 device_type; - u32 type_flags; -}; - - -extern int mantis_dma_init(struct mantis_pci *mantis); -extern int mantis_dma_exit(struct mantis_pci *mantis); -extern void mantis_dma_start(struct mantis_pci *mantis); -extern void mantis_dma_stop(struct mantis_pci *mantis); -extern int mantis_i2c_init(struct mantis_pci *mantis); -extern int mantis_i2c_exit(struct mantis_pci *mantis); -extern int mantis_core_init(struct mantis_pci *mantis); -extern int mantis_core_exit(struct mantis_pci *mantis); - -#endif /* __MANTIS_CORE_H */ diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/dvb/mantis/mantis_dma.c deleted file mode 100644 index 566c407175a4..000000000000 --- a/drivers/media/dvb/mantis/mantis_dma.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_reg.h" -#include "mantis_dma.h" - -#define RISC_WRITE (0x01 << 28) -#define RISC_JUMP (0x07 << 28) -#define RISC_IRQ (0x01 << 24) - -#define RISC_STATUS(status) ((((~status) & 0x0f) << 20) | ((status & 0x0f) << 16)) -#define RISC_FLUSH(risc_pos) (risc_pos = 0) -#define RISC_INSTR(risc_pos, opcode) (mantis->risc_cpu[risc_pos++] = cpu_to_le32(opcode)) - -#define MANTIS_BUF_SIZE (64 * 1024) -#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE / 4) -#define MANTIS_DMA_TR_BYTES (2 * 1024) /* upper limit: 4095 bytes. */ -#define MANTIS_BLOCK_COUNT (MANTIS_BUF_SIZE / MANTIS_BLOCK_BYTES) - -#define MANTIS_DMA_TR_UNITS (MANTIS_BLOCK_BYTES / MANTIS_DMA_TR_BYTES) -/* MANTIS_BUF_SIZE / MANTIS_DMA_TR_UNITS must not exceed MANTIS_RISC_SIZE (4k RISC cmd buffer) */ -#define MANTIS_RISC_SIZE PAGE_SIZE /* RISC program must fit here. */ - -int mantis_dma_exit(struct mantis_pci *mantis) -{ - if (mantis->buf_cpu) { - dprintk(MANTIS_ERROR, 1, - "DMA=0x%lx cpu=0x%p size=%d", - (unsigned long) mantis->buf_dma, - mantis->buf_cpu, - MANTIS_BUF_SIZE); - - pci_free_consistent(mantis->pdev, MANTIS_BUF_SIZE, - mantis->buf_cpu, mantis->buf_dma); - - mantis->buf_cpu = NULL; - } - if (mantis->risc_cpu) { - dprintk(MANTIS_ERROR, 1, - "RISC=0x%lx cpu=0x%p size=%lx", - (unsigned long) mantis->risc_dma, - mantis->risc_cpu, - MANTIS_RISC_SIZE); - - pci_free_consistent(mantis->pdev, MANTIS_RISC_SIZE, - mantis->risc_cpu, mantis->risc_dma); - - mantis->risc_cpu = NULL; - } - - return 0; -} -EXPORT_SYMBOL_GPL(mantis_dma_exit); - -static inline int mantis_alloc_buffers(struct mantis_pci *mantis) -{ - if (!mantis->buf_cpu) { - mantis->buf_cpu = pci_alloc_consistent(mantis->pdev, - MANTIS_BUF_SIZE, - &mantis->buf_dma); - if (!mantis->buf_cpu) { - dprintk(MANTIS_ERROR, 1, - "DMA buffer allocation failed"); - - goto err; - } - dprintk(MANTIS_ERROR, 1, - "DMA=0x%lx cpu=0x%p size=%d", - (unsigned long) mantis->buf_dma, - mantis->buf_cpu, MANTIS_BUF_SIZE); - } - if (!mantis->risc_cpu) { - mantis->risc_cpu = pci_alloc_consistent(mantis->pdev, - MANTIS_RISC_SIZE, - &mantis->risc_dma); - - if (!mantis->risc_cpu) { - dprintk(MANTIS_ERROR, 1, - "RISC program allocation failed"); - - mantis_dma_exit(mantis); - - goto err; - } - dprintk(MANTIS_ERROR, 1, - "RISC=0x%lx cpu=0x%p size=%lx", - (unsigned long) mantis->risc_dma, - mantis->risc_cpu, MANTIS_RISC_SIZE); - } - - return 0; -err: - dprintk(MANTIS_ERROR, 1, "Out of memory (?) ....."); - return -ENOMEM; -} - -int mantis_dma_init(struct mantis_pci *mantis) -{ - int err = 0; - - dprintk(MANTIS_DEBUG, 1, "Mantis DMA init"); - if (mantis_alloc_buffers(mantis) < 0) { - dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer"); - - /* Stop RISC Engine */ - mmwrite(0, MANTIS_DMA_CTL); - - goto err; - } - - return 0; -err: - return err; -} -EXPORT_SYMBOL_GPL(mantis_dma_init); - -static inline void mantis_risc_program(struct mantis_pci *mantis) -{ - u32 buf_pos = 0; - u32 line, step; - u32 risc_pos; - - dprintk(MANTIS_DEBUG, 1, "Mantis create RISC program"); - RISC_FLUSH(risc_pos); - - dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u, bytes per DMA tr %u", - MANTIS_BLOCK_COUNT, MANTIS_BLOCK_BYTES, MANTIS_DMA_TR_BYTES); - - for (line = 0; line < MANTIS_BLOCK_COUNT; line++) { - for (step = 0; step < MANTIS_DMA_TR_UNITS; step++) { - dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d], step=[%d]", line, step); - if (step == 0) { - RISC_INSTR(risc_pos, RISC_WRITE | - RISC_IRQ | - RISC_STATUS(line) | - MANTIS_DMA_TR_BYTES); - } else { - RISC_INSTR(risc_pos, RISC_WRITE | MANTIS_DMA_TR_BYTES); - } - RISC_INSTR(risc_pos, mantis->buf_dma + buf_pos); - buf_pos += MANTIS_DMA_TR_BYTES; - } - } - RISC_INSTR(risc_pos, RISC_JUMP); - RISC_INSTR(risc_pos, mantis->risc_dma); -} - -void mantis_dma_start(struct mantis_pci *mantis) -{ - dprintk(MANTIS_DEBUG, 1, "Mantis Start DMA engine"); - - mantis_risc_program(mantis); - mmwrite(mantis->risc_dma, MANTIS_RISC_START); - mmwrite(mmread(MANTIS_GPIF_ADDR) | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); - - mmwrite(0, MANTIS_DMA_CTL); - mantis->last_block = mantis->busy_block = 0; - - mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK); - - mmwrite(MANTIS_FIFO_EN | MANTIS_DCAP_EN - | MANTIS_RISC_EN, MANTIS_DMA_CTL); - -} - -void mantis_dma_stop(struct mantis_pci *mantis) -{ - dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine"); - - mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR); - - mmwrite((mmread(MANTIS_DMA_CTL) & ~(MANTIS_FIFO_EN | - MANTIS_DCAP_EN | - MANTIS_RISC_EN)), MANTIS_DMA_CTL); - - mmwrite(mmread(MANTIS_INT_STAT), MANTIS_INT_STAT); - - mmwrite(mmread(MANTIS_INT_MASK) & ~(MANTIS_INT_RISCI | - MANTIS_INT_RISCEN), MANTIS_INT_MASK); -} - - -void mantis_dma_xfer(unsigned long data) -{ - struct mantis_pci *mantis = (struct mantis_pci *) data; - struct mantis_hwconfig *config = mantis->hwconfig; - - while (mantis->last_block != mantis->busy_block) { - dprintk(MANTIS_DEBUG, 1, "last block=[%d] finished block=[%d]", - mantis->last_block, mantis->busy_block); - - (config->ts_size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) - (&mantis->demux, &mantis->buf_cpu[mantis->last_block * MANTIS_BLOCK_BYTES], MANTIS_BLOCK_BYTES); - mantis->last_block = (mantis->last_block + 1) % MANTIS_BLOCK_COUNT; - } -} diff --git a/drivers/media/dvb/mantis/mantis_dma.h b/drivers/media/dvb/mantis/mantis_dma.h deleted file mode 100644 index 6be00fa82094..000000000000 --- a/drivers/media/dvb/mantis/mantis_dma.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_DMA_H -#define __MANTIS_DMA_H - -extern int mantis_dma_init(struct mantis_pci *mantis); -extern int mantis_dma_exit(struct mantis_pci *mantis); -extern void mantis_dma_start(struct mantis_pci *mantis); -extern void mantis_dma_stop(struct mantis_pci *mantis); -extern void mantis_dma_xfer(unsigned long data); - -#endif /* __MANTIS_DMA_H */ diff --git a/drivers/media/dvb/mantis/mantis_dvb.c b/drivers/media/dvb/mantis/mantis_dvb.c deleted file mode 100644 index 5d15c6b74d9b..000000000000 --- a/drivers/media/dvb/mantis/mantis_dvb.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - Mantis PCI bridge driver - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include - -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_dma.h" -#include "mantis_ca.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power) -{ - struct mantis_hwconfig *config = mantis->hwconfig; - - switch (power) { - case POWER_ON: - dprintk(MANTIS_DEBUG, 1, "Power ON"); - mantis_gpio_set_bits(mantis, config->power, POWER_ON); - msleep(100); - mantis_gpio_set_bits(mantis, config->power, POWER_ON); - msleep(100); - break; - - case POWER_OFF: - dprintk(MANTIS_DEBUG, 1, "Power OFF"); - mantis_gpio_set_bits(mantis, config->power, POWER_OFF); - msleep(100); - break; - - default: - dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power); - return -1; - } - - return 0; -} -EXPORT_SYMBOL_GPL(mantis_frontend_power); - -void mantis_frontend_soft_reset(struct mantis_pci *mantis) -{ - struct mantis_hwconfig *config = mantis->hwconfig; - - dprintk(MANTIS_DEBUG, 1, "Frontend RESET"); - mantis_gpio_set_bits(mantis, config->reset, 0); - msleep(100); - mantis_gpio_set_bits(mantis, config->reset, 0); - msleep(100); - mantis_gpio_set_bits(mantis, config->reset, 1); - msleep(100); - mantis_gpio_set_bits(mantis, config->reset, 1); - msleep(100); - - return; -} -EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset); - -static int mantis_frontend_shutdown(struct mantis_pci *mantis) -{ - int err; - - mantis_frontend_soft_reset(mantis); - err = mantis_frontend_power(mantis, POWER_OFF); - if (err != 0) { - dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err); - return 1; - } - - return 0; -} - -static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct mantis_pci *mantis = dvbdmx->priv; - - dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed"); - if (!dvbdmx->dmx.frontend) { - dprintk(MANTIS_DEBUG, 1, "no frontend ?"); - return -EINVAL; - } - - mantis->feeds++; - dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds); - - if (mantis->feeds == 1) { - dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma"); - mantis_dma_start(mantis); - tasklet_enable(&mantis->tasklet); - } - - return mantis->feeds; -} - -static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct mantis_pci *mantis = dvbdmx->priv; - - dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed"); - if (!dvbdmx->dmx.frontend) { - dprintk(MANTIS_DEBUG, 1, "no frontend ?"); - return -EINVAL; - } - - mantis->feeds--; - if (mantis->feeds == 0) { - dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma"); - tasklet_disable(&mantis->tasklet); - mantis_dma_stop(mantis); - } - - return 0; -} - -int __devinit mantis_dvb_init(struct mantis_pci *mantis) -{ - struct mantis_hwconfig *config = mantis->hwconfig; - int result = -1; - - dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter"); - - result = dvb_register_adapter(&mantis->dvb_adapter, - "Mantis DVB adapter", - THIS_MODULE, - &mantis->pdev->dev, - adapter_nr); - - if (result < 0) { - - dprintk(MANTIS_ERROR, 1, "Error registering adapter"); - return -ENODEV; - } - - mantis->dvb_adapter.priv = mantis; - mantis->demux.dmx.capabilities = DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - - mantis->demux.priv = mantis; - mantis->demux.filternum = 256; - mantis->demux.feednum = 256; - mantis->demux.start_feed = mantis_dvb_start_feed; - mantis->demux.stop_feed = mantis_dvb_stop_feed; - mantis->demux.write_to_decoder = NULL; - - dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init"); - result = dvb_dmx_init(&mantis->demux); - if (result < 0) { - dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); - - goto err0; - } - - mantis->dmxdev.filternum = 256; - mantis->dmxdev.demux = &mantis->demux.dmx; - mantis->dmxdev.capabilities = 0; - dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init"); - - result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter); - if (result < 0) { - - dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result); - goto err1; - } - - mantis->fe_hw.source = DMX_FRONTEND_0; - result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw); - if (result < 0) { - - dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); - goto err2; - } - - mantis->fe_mem.source = DMX_MEMORY_FE; - result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem); - if (result < 0) { - dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); - goto err3; - } - - result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw); - if (result < 0) { - dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); - goto err4; - } - - dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx); - tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis); - tasklet_disable(&mantis->tasklet); - if (mantis->hwconfig) { - result = config->frontend_init(mantis, mantis->fe); - if (result < 0) { - dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!"); - goto err5; - } else { - if (mantis->fe == NULL) { - dprintk(MANTIS_ERROR, 1, "FE "); - goto err5; - } - - if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) { - dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed"); - - if (mantis->fe->ops.release) - mantis->fe->ops.release(mantis->fe); - - mantis->fe = NULL; - goto err5; - } - } - } - - return 0; - - /* Error conditions .. */ -err5: - tasklet_kill(&mantis->tasklet); - dvb_net_release(&mantis->dvbnet); - if (mantis->fe) { - dvb_unregister_frontend(mantis->fe); - dvb_frontend_detach(mantis->fe); - } -err4: - mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); - -err3: - mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); - -err2: - dvb_dmxdev_release(&mantis->dmxdev); - -err1: - dvb_dmx_release(&mantis->demux); - -err0: - dvb_unregister_adapter(&mantis->dvb_adapter); - - return result; -} -EXPORT_SYMBOL_GPL(mantis_dvb_init); - -int __devexit mantis_dvb_exit(struct mantis_pci *mantis) -{ - int err; - - if (mantis->fe) { - /* mantis_ca_exit(mantis); */ - err = mantis_frontend_shutdown(mantis); - if (err != 0) - dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err); - dvb_unregister_frontend(mantis->fe); - dvb_frontend_detach(mantis->fe); - } - - tasklet_kill(&mantis->tasklet); - dvb_net_release(&mantis->dvbnet); - - mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); - mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); - - dvb_dmxdev_release(&mantis->dmxdev); - dvb_dmx_release(&mantis->demux); - - dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter"); - dvb_unregister_adapter(&mantis->dvb_adapter); - - return 0; -} -EXPORT_SYMBOL_GPL(mantis_dvb_exit); diff --git a/drivers/media/dvb/mantis/mantis_dvb.h b/drivers/media/dvb/mantis/mantis_dvb.h deleted file mode 100644 index 464199db304e..000000000000 --- a/drivers/media/dvb/mantis/mantis_dvb.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_DVB_H -#define __MANTIS_DVB_H - -enum mantis_power { - POWER_OFF = 0, - POWER_ON = 1 -}; - -extern int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power); -extern void mantis_frontend_soft_reset(struct mantis_pci *mantis); - -extern int mantis_dvb_init(struct mantis_pci *mantis); -extern int mantis_dvb_exit(struct mantis_pci *mantis); - -#endif /* __MANTIS_DVB_H */ diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/dvb/mantis/mantis_evm.c deleted file mode 100644 index 71ce52875c38..000000000000 --- a/drivers/media/dvb/mantis/mantis_evm.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_link.h" -#include "mantis_hif.h" -#include "mantis_reg.h" - -static void mantis_hifevm_work(struct work_struct *work) -{ - struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work); - struct mantis_pci *mantis = ca->ca_priv; - - u32 gpif_stat; - - gpif_stat = mmread(MANTIS_GPIF_STATUS); - - if (gpif_stat & MANTIS_GPIF_DETSTAT) { - if (gpif_stat & MANTIS_CARD_PLUGIN) { - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Plugin", mantis->num); - mmwrite(0xdada0000, MANTIS_CARD_RESET); - mantis_event_cam_plugin(ca); - dvb_ca_en50221_camchange_irq(&ca->en50221, - 0, - DVB_CA_EN50221_CAMCHANGE_INSERTED); - } - } else { - if (gpif_stat & MANTIS_CARD_PLUGOUT) { - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Unplug", mantis->num); - mmwrite(0xdada0000, MANTIS_CARD_RESET); - mantis_event_cam_unplug(ca); - dvb_ca_en50221_camchange_irq(&ca->en50221, - 0, - DVB_CA_EN50221_CAMCHANGE_REMOVED); - } - } - - if (mantis->gpif_status & MANTIS_GPIF_EXTIRQ) - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Ext IRQ", mantis->num); - - if (mantis->gpif_status & MANTIS_SBUF_WSTO) - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Timeout", mantis->num); - - if (mantis->gpif_status & MANTIS_GPIF_OTHERR) - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Alignment Error", mantis->num); - - if (gpif_stat & MANTIS_SBUF_OVFLW) - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Overflow", mantis->num); - - if (gpif_stat & MANTIS_GPIF_BRRDY) - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Read Ready", mantis->num); - - if (gpif_stat & MANTIS_GPIF_INTSTAT) - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): GPIF IRQ", mantis->num); - - if (gpif_stat & MANTIS_SBUF_EMPTY) - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Empty", mantis->num); - - if (gpif_stat & MANTIS_SBUF_OPDONE) { - dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer operation complete", mantis->num); - ca->sbuf_status = MANTIS_SBUF_DATA_AVAIL; - ca->hif_event = MANTIS_SBUF_OPDONE; - wake_up(&ca->hif_opdone_wq); - } -} - -int mantis_evmgr_init(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Initializing Mantis Host I/F Event manager"); - INIT_WORK(&ca->hif_evm_work, mantis_hifevm_work); - mantis_pcmcia_init(ca); - schedule_work(&ca->hif_evm_work); - mantis_hif_init(ca); - return 0; -} - -void mantis_evmgr_exit(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - - dprintk(MANTIS_DEBUG, 1, "Mantis Host I/F Event manager exiting"); - flush_work_sync(&ca->hif_evm_work); - mantis_hif_exit(ca); - mantis_pcmcia_exit(ca); -} diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/dvb/mantis/mantis_hif.c deleted file mode 100644 index 10c68df7e16f..000000000000 --- a/drivers/media/dvb/mantis/mantis_hif.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" - -#include "mantis_hif.h" -#include "mantis_link.h" /* temporary due to physical layer stuff */ - -#include "mantis_reg.h" - - -static int mantis_hif_sbuf_opdone_wait(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - int rc = 0; - - if (wait_event_timeout(ca->hif_opdone_wq, - ca->hif_event & MANTIS_SBUF_OPDONE, - msecs_to_jiffies(500)) == -ERESTARTSYS) { - - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Smart buffer operation timeout !", mantis->num); - rc = -EREMOTEIO; - } - dprintk(MANTIS_DEBUG, 1, "Smart Buffer Operation complete"); - ca->hif_event &= ~MANTIS_SBUF_OPDONE; - return rc; -} - -static int mantis_hif_write_wait(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - u32 opdone = 0, timeout = 0; - int rc = 0; - - if (wait_event_timeout(ca->hif_write_wq, - mantis->gpif_status & MANTIS_GPIF_WRACK, - msecs_to_jiffies(500)) == -ERESTARTSYS) { - - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write ACK timed out !", mantis->num); - rc = -EREMOTEIO; - } - dprintk(MANTIS_DEBUG, 1, "Write Acknowledged"); - mantis->gpif_status &= ~MANTIS_GPIF_WRACK; - while (!opdone) { - opdone = (mmread(MANTIS_GPIF_STATUS) & MANTIS_SBUF_OPDONE); - udelay(500); - timeout++; - if (timeout > 100) { - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write operation timed out!", mantis->num); - rc = -ETIMEDOUT; - break; - } - } - dprintk(MANTIS_DEBUG, 1, "HIF Write success"); - return rc; -} - - -int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr) -{ - struct mantis_pci *mantis = ca->ca_priv; - u32 hif_addr = 0, data, count = 4; - - dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Read", mantis->num); - mutex_lock(&ca->ca_lock); - hif_addr &= ~MANTIS_GPIF_PCMCIAREG; - hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; - hif_addr |= MANTIS_HIF_STATUS; - hif_addr |= addr; - - mmwrite(hif_addr, MANTIS_GPIF_BRADDR); - mmwrite(count, MANTIS_GPIF_BRBYTES); - udelay(20); - mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); - - if (mantis_hif_sbuf_opdone_wait(ca) != 0) { - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): GPIF Smart Buffer operation failed", mantis->num); - mutex_unlock(&ca->ca_lock); - return -EREMOTEIO; - } - data = mmread(MANTIS_GPIF_DIN); - mutex_unlock(&ca->ca_lock); - dprintk(MANTIS_DEBUG, 1, "Mem Read: 0x%02x", data); - return (data >> 24) & 0xff; -} - -int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data) -{ - struct mantis_slot *slot = ca->slot; - struct mantis_pci *mantis = ca->ca_priv; - u32 hif_addr = 0; - - dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Write", mantis->num); - mutex_lock(&ca->ca_lock); - hif_addr &= ~MANTIS_GPIF_HIFRDWRN; - hif_addr &= ~MANTIS_GPIF_PCMCIAREG; - hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; - hif_addr |= MANTIS_HIF_STATUS; - hif_addr |= addr; - - mmwrite(slot->slave_cfg, MANTIS_GPIF_CFGSLA); /* Slot0 alone for now */ - mmwrite(hif_addr, MANTIS_GPIF_ADDR); - mmwrite(data, MANTIS_GPIF_DOUT); - - if (mantis_hif_write_wait(ca) != 0) { - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); - mutex_unlock(&ca->ca_lock); - return -EREMOTEIO; - } - dprintk(MANTIS_DEBUG, 1, "Mem Write: (0x%02x to 0x%02x)", data, addr); - mutex_unlock(&ca->ca_lock); - - return 0; -} - -int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr) -{ - struct mantis_pci *mantis = ca->ca_priv; - u32 data, hif_addr = 0; - - dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Read", mantis->num); - mutex_lock(&ca->ca_lock); - hif_addr &= ~MANTIS_GPIF_PCMCIAREG; - hif_addr |= MANTIS_GPIF_PCMCIAIOM; - hif_addr |= MANTIS_HIF_STATUS; - hif_addr |= addr; - - mmwrite(hif_addr, MANTIS_GPIF_BRADDR); - mmwrite(1, MANTIS_GPIF_BRBYTES); - udelay(20); - mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); - - if (mantis_hif_sbuf_opdone_wait(ca) != 0) { - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); - mutex_unlock(&ca->ca_lock); - return -EREMOTEIO; - } - data = mmread(MANTIS_GPIF_DIN); - dprintk(MANTIS_DEBUG, 1, "I/O Read: 0x%02x", data); - udelay(50); - mutex_unlock(&ca->ca_lock); - - return (u8) data; -} - -int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data) -{ - struct mantis_pci *mantis = ca->ca_priv; - u32 hif_addr = 0; - - dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Write", mantis->num); - mutex_lock(&ca->ca_lock); - hif_addr &= ~MANTIS_GPIF_PCMCIAREG; - hif_addr &= ~MANTIS_GPIF_HIFRDWRN; - hif_addr |= MANTIS_GPIF_PCMCIAIOM; - hif_addr |= MANTIS_HIF_STATUS; - hif_addr |= addr; - - mmwrite(hif_addr, MANTIS_GPIF_ADDR); - mmwrite(data, MANTIS_GPIF_DOUT); - - if (mantis_hif_write_wait(ca) != 0) { - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); - mutex_unlock(&ca->ca_lock); - return -EREMOTEIO; - } - dprintk(MANTIS_DEBUG, 1, "I/O Write: (0x%02x to 0x%02x)", data, addr); - mutex_unlock(&ca->ca_lock); - udelay(50); - - return 0; -} - -int mantis_hif_init(struct mantis_ca *ca) -{ - struct mantis_slot *slot = ca->slot; - struct mantis_pci *mantis = ca->ca_priv; - u32 irqcfg; - - slot[0].slave_cfg = 0x70773028; - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Initializing Mantis Host Interface", mantis->num); - - mutex_lock(&ca->ca_lock); - irqcfg = mmread(MANTIS_GPIF_IRQCFG); - irqcfg = MANTIS_MASK_BRRDY | - MANTIS_MASK_WRACK | - MANTIS_MASK_EXTIRQ | - MANTIS_MASK_WSTO | - MANTIS_MASK_OTHERR | - MANTIS_MASK_OVFLW; - - mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); - mutex_unlock(&ca->ca_lock); - - return 0; -} - -void mantis_hif_exit(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - u32 irqcfg; - - dprintk(MANTIS_ERROR, 1, "Adapter(%d) Exiting Mantis Host Interface", mantis->num); - mutex_lock(&ca->ca_lock); - irqcfg = mmread(MANTIS_GPIF_IRQCFG); - irqcfg &= ~MANTIS_MASK_BRRDY; - mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); - mutex_unlock(&ca->ca_lock); -} diff --git a/drivers/media/dvb/mantis/mantis_hif.h b/drivers/media/dvb/mantis/mantis_hif.h deleted file mode 100644 index 9094f9ed2362..000000000000 --- a/drivers/media/dvb/mantis/mantis_hif.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_HIF_H -#define __MANTIS_HIF_H - -#define MANTIS_HIF_MEMRD 1 -#define MANTIS_HIF_MEMWR 2 -#define MANTIS_HIF_IOMRD 3 -#define MANTIS_HIF_IOMWR 4 - -#endif /* __MANTIS_HIF_H */ diff --git a/drivers/media/dvb/mantis/mantis_i2c.c b/drivers/media/dvb/mantis/mantis_i2c.c deleted file mode 100644 index e7794517fe26..000000000000 --- a/drivers/media/dvb/mantis/mantis_i2c.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_reg.h" -#include "mantis_i2c.h" - -#define TRIALS 10000 - -static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg) -{ - u32 rxd, i, stat, trials; - - dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", - __func__, msg->addr); - - for (i = 0; i < msg->len; i++) { - rxd = (msg->addr << 25) | (1 << 24) - | MANTIS_I2C_RATE_3 - | MANTIS_I2C_STOP - | MANTIS_I2C_PGMODE; - - if (i == (msg->len - 1)) - rxd &= ~MANTIS_I2C_STOP; - - mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); - mmwrite(rxd, MANTIS_I2CDATA_CTL); - - /* wait for xfer completion */ - for (trials = 0; trials < TRIALS; trials++) { - stat = mmread(MANTIS_INT_STAT); - if (stat & MANTIS_INT_I2CDONE) - break; - } - - dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); - - /* wait for xfer completion */ - for (trials = 0; trials < TRIALS; trials++) { - stat = mmread(MANTIS_INT_STAT); - if (stat & MANTIS_INT_I2CRACK) - break; - } - - dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); - - rxd = mmread(MANTIS_I2CDATA_CTL); - msg->buf[i] = (u8)((rxd >> 8) & 0xFF); - dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); - } - dprintk(MANTIS_INFO, 0, "]\n"); - - return 0; -} - -static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg) -{ - int i; - u32 txd = 0, stat, trials; - - dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", - __func__, msg->addr); - - for (i = 0; i < msg->len; i++) { - dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); - txd = (msg->addr << 25) | (msg->buf[i] << 8) - | MANTIS_I2C_RATE_3 - | MANTIS_I2C_STOP - | MANTIS_I2C_PGMODE; - - if (i == (msg->len - 1)) - txd &= ~MANTIS_I2C_STOP; - - mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); - mmwrite(txd, MANTIS_I2CDATA_CTL); - - /* wait for xfer completion */ - for (trials = 0; trials < TRIALS; trials++) { - stat = mmread(MANTIS_INT_STAT); - if (stat & MANTIS_INT_I2CDONE) - break; - } - - dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); - - /* wait for xfer completion */ - for (trials = 0; trials < TRIALS; trials++) { - stat = mmread(MANTIS_INT_STAT); - if (stat & MANTIS_INT_I2CRACK) - break; - } - - dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); - } - dprintk(MANTIS_INFO, 0, "]\n"); - - return 0; -} - -static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) -{ - int ret = 0, i = 0, trials; - u32 stat, data, txd; - struct mantis_pci *mantis; - struct mantis_hwconfig *config; - - mantis = i2c_get_adapdata(adapter); - BUG_ON(!mantis); - config = mantis->hwconfig; - BUG_ON(!config); - - dprintk(MANTIS_DEBUG, 1, "Messages:%d", num); - mutex_lock(&mantis->i2c_lock); - - while (i < num) { - /* Byte MODE */ - if ((config->i2c_mode & MANTIS_BYTE_MODE) && - ((i + 1) < num) && - (msgs[i].len < 2) && - (msgs[i + 1].len < 2) && - (msgs[i + 1].flags & I2C_M_RD)) { - - dprintk(MANTIS_DEBUG, 0, " Byte MODE:\n"); - - /* Read operation */ - txd = msgs[i].addr << 25 | (0x1 << 24) - | (msgs[i].buf[0] << 16) - | MANTIS_I2C_RATE_3; - - mmwrite(txd, MANTIS_I2CDATA_CTL); - /* wait for xfer completion */ - for (trials = 0; trials < TRIALS; trials++) { - stat = mmread(MANTIS_INT_STAT); - if (stat & MANTIS_INT_I2CDONE) - break; - } - - /* check for xfer completion */ - if (stat & MANTIS_INT_I2CDONE) { - /* check xfer was acknowledged */ - if (stat & MANTIS_INT_I2CRACK) { - data = mmread(MANTIS_I2CDATA_CTL); - msgs[i + 1].buf[0] = (data >> 8) & 0xff; - dprintk(MANTIS_DEBUG, 0, " Byte <%d> RXD=0x%02x [%02x]\n", 0x0, data, msgs[i + 1].buf[0]); - } else { - /* I/O error */ - dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); - ret = -EIO; - break; - } - } else { - /* I/O error */ - dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); - ret = -EIO; - break; - } - i += 2; /* Write/Read operation in one go */ - } - - if (i < num) { - if (msgs[i].flags & I2C_M_RD) - ret = mantis_i2c_read(mantis, &msgs[i]); - else - ret = mantis_i2c_write(mantis, &msgs[i]); - - i++; - if (ret < 0) - goto bail_out; - } - - } - - mutex_unlock(&mantis->i2c_lock); - - return num; - -bail_out: - mutex_unlock(&mantis->i2c_lock); - return ret; -} - -static u32 mantis_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static struct i2c_algorithm mantis_algo = { - .master_xfer = mantis_i2c_xfer, - .functionality = mantis_i2c_func, -}; - -int __devinit mantis_i2c_init(struct mantis_pci *mantis) -{ - u32 intstat, intmask; - struct i2c_adapter *i2c_adapter = &mantis->adapter; - struct pci_dev *pdev = mantis->pdev; - - init_waitqueue_head(&mantis->i2c_wq); - mutex_init(&mantis->i2c_lock); - strncpy(i2c_adapter->name, "Mantis I2C", sizeof(i2c_adapter->name)); - i2c_set_adapdata(i2c_adapter, mantis); - - i2c_adapter->owner = THIS_MODULE; - i2c_adapter->algo = &mantis_algo; - i2c_adapter->algo_data = NULL; - i2c_adapter->timeout = 500; - i2c_adapter->retries = 3; - i2c_adapter->dev.parent = &pdev->dev; - - mantis->i2c_rc = i2c_add_adapter(i2c_adapter); - if (mantis->i2c_rc < 0) - return mantis->i2c_rc; - - dprintk(MANTIS_DEBUG, 1, "Initializing I2C .."); - - intstat = mmread(MANTIS_INT_STAT); - intmask = mmread(MANTIS_INT_MASK); - mmwrite(intstat, MANTIS_INT_STAT); - dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); - intmask = mmread(MANTIS_INT_MASK); - mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); - - return 0; -} -EXPORT_SYMBOL_GPL(mantis_i2c_init); - -int mantis_i2c_exit(struct mantis_pci *mantis) -{ - u32 intmask; - - dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); - intmask = mmread(MANTIS_INT_MASK); - mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); - - dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter"); - return i2c_del_adapter(&mantis->adapter); -} -EXPORT_SYMBOL_GPL(mantis_i2c_exit); diff --git a/drivers/media/dvb/mantis/mantis_i2c.h b/drivers/media/dvb/mantis/mantis_i2c.h deleted file mode 100644 index 1342df2faed8..000000000000 --- a/drivers/media/dvb/mantis/mantis_i2c.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_I2C_H -#define __MANTIS_I2C_H - -#define I2C_STOP (1 << 0) -#define I2C_READ (1 << 1) - -extern int mantis_i2c_init(struct mantis_pci *mantis); -extern int mantis_i2c_exit(struct mantis_pci *mantis); - -#endif /* __MANTIS_I2C_H */ diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c deleted file mode 100644 index db6d54d3fec0..000000000000 --- a/drivers/media/dvb/mantis/mantis_input.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_reg.h" -#include "mantis_uart.h" - -#define MODULE_NAME "mantis_core" -#define RC_MAP_MANTIS "rc-mantis" - -static struct rc_map_table mantis_ir_table[] = { - { 0x29, KEY_POWER }, - { 0x28, KEY_FAVORITES }, - { 0x30, KEY_TEXT }, - { 0x17, KEY_INFO }, /* Preview */ - { 0x23, KEY_EPG }, - { 0x3b, KEY_F22 }, /* Record List */ - { 0x3c, KEY_1 }, - { 0x3e, KEY_2 }, - { 0x39, KEY_3 }, - { 0x36, KEY_4 }, - { 0x22, KEY_5 }, - { 0x20, KEY_6 }, - { 0x32, KEY_7 }, - { 0x26, KEY_8 }, - { 0x24, KEY_9 }, - { 0x2a, KEY_0 }, - - { 0x33, KEY_CANCEL }, - { 0x2c, KEY_BACK }, - { 0x15, KEY_CLEAR }, - { 0x3f, KEY_TAB }, - { 0x10, KEY_ENTER }, - { 0x14, KEY_UP }, - { 0x0d, KEY_RIGHT }, - { 0x0e, KEY_DOWN }, - { 0x11, KEY_LEFT }, - - { 0x21, KEY_VOLUMEUP }, - { 0x35, KEY_VOLUMEDOWN }, - { 0x3d, KEY_CHANNELDOWN }, - { 0x3a, KEY_CHANNELUP }, - { 0x2e, KEY_RECORD }, - { 0x2b, KEY_PLAY }, - { 0x13, KEY_PAUSE }, - { 0x25, KEY_STOP }, - - { 0x1f, KEY_REWIND }, - { 0x2d, KEY_FASTFORWARD }, - { 0x1e, KEY_PREVIOUS }, /* Replay |< */ - { 0x1d, KEY_NEXT }, /* Skip >| */ - - { 0x0b, KEY_CAMERA }, /* Capture */ - { 0x0f, KEY_LANGUAGE }, /* SAP */ - { 0x18, KEY_MODE }, /* PIP */ - { 0x12, KEY_ZOOM }, /* Full screen */ - { 0x1c, KEY_SUBTITLE }, - { 0x2f, KEY_MUTE }, - { 0x16, KEY_F20 }, /* L/R */ - { 0x38, KEY_F21 }, /* Hibernate */ - - { 0x37, KEY_SWITCHVIDEOMODE }, /* A/V */ - { 0x31, KEY_AGAIN }, /* Recall */ - { 0x1a, KEY_KPPLUS }, /* Zoom+ */ - { 0x19, KEY_KPMINUS }, /* Zoom- */ - { 0x27, KEY_RED }, - { 0x0C, KEY_GREEN }, - { 0x01, KEY_YELLOW }, - { 0x00, KEY_BLUE }, -}; - -static struct rc_map_list ir_mantis_map = { - .map = { - .scan = mantis_ir_table, - .size = ARRAY_SIZE(mantis_ir_table), - .rc_type = RC_TYPE_UNKNOWN, - .name = RC_MAP_MANTIS, - } -}; - -int mantis_input_init(struct mantis_pci *mantis) -{ - struct rc_dev *dev; - int err; - - err = rc_map_register(&ir_mantis_map); - if (err) - goto out; - - dev = rc_allocate_device(); - if (!dev) { - dprintk(MANTIS_ERROR, 1, "Remote device allocation failed"); - err = -ENOMEM; - goto out_map; - } - - sprintf(mantis->input_name, "Mantis %s IR receiver", mantis->hwconfig->model_name); - sprintf(mantis->input_phys, "pci-%s/ir0", pci_name(mantis->pdev)); - - dev->input_name = mantis->input_name; - dev->input_phys = mantis->input_phys; - dev->input_id.bustype = BUS_PCI; - dev->input_id.vendor = mantis->vendor_id; - dev->input_id.product = mantis->device_id; - dev->input_id.version = 1; - dev->driver_name = MODULE_NAME; - dev->map_name = RC_MAP_MANTIS; - dev->dev.parent = &mantis->pdev->dev; - - err = rc_register_device(dev); - if (err) { - dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err); - goto out_dev; - } - - mantis->rc = dev; - return 0; - -out_dev: - rc_free_device(dev); -out_map: - rc_map_unregister(&ir_mantis_map); -out: - return err; -} - -int mantis_exit(struct mantis_pci *mantis) -{ - rc_unregister_device(mantis->rc); - rc_map_unregister(&ir_mantis_map); - return 0; -} - diff --git a/drivers/media/dvb/mantis/mantis_ioc.c b/drivers/media/dvb/mantis/mantis_ioc.c deleted file mode 100644 index 24fcdc63d6d5..000000000000 --- a/drivers/media/dvb/mantis/mantis_ioc.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_reg.h" -#include "mantis_ioc.h" - -static int read_eeprom_bytes(struct mantis_pci *mantis, u8 reg, u8 *data, u8 length) -{ - struct i2c_adapter *adapter = &mantis->adapter; - int err; - u8 buf = reg; - - struct i2c_msg msg[] = { - { .addr = 0x50, .flags = 0, .buf = &buf, .len = 1 }, - { .addr = 0x50, .flags = I2C_M_RD, .buf = data, .len = length }, - }; - - err = i2c_transfer(adapter, msg, 2); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", - err, data[0], data[1]); - - return err; - } - - return 0; -} -int mantis_get_mac(struct mantis_pci *mantis) -{ - int err; - u8 mac_addr[6] = {0}; - - err = read_eeprom_bytes(mantis, 0x08, mac_addr, 6); - if (err < 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis EEPROM read error <%d>", err); - - return err; - } - - dprintk(MANTIS_ERROR, 0, " MAC Address=[%pM]\n", mac_addr); - - return 0; -} -EXPORT_SYMBOL_GPL(mantis_get_mac); - -/* Turn the given bit on or off. */ -void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) -{ - u32 cur; - - dprintk(MANTIS_DEBUG, 1, "Set Bit <%d> to <%d>", bitpos, value); - cur = mmread(MANTIS_GPIF_ADDR); - if (value) - mantis->gpio_status = cur | (1 << bitpos); - else - mantis->gpio_status = cur & (~(1 << bitpos)); - - dprintk(MANTIS_DEBUG, 1, "GPIO Value <%02x>", mantis->gpio_status); - mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); - mmwrite(0x00, MANTIS_GPIF_DOUT); -} -EXPORT_SYMBOL_GPL(mantis_gpio_set_bits); - -int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl) -{ - u32 reg; - - reg = mmread(MANTIS_CONTROL); - switch (stream_ctl) { - case STREAM_TO_HIF: - dprintk(MANTIS_DEBUG, 1, "Set stream to HIF"); - reg &= 0xff - MANTIS_BYPASS; - mmwrite(reg, MANTIS_CONTROL); - reg |= MANTIS_BYPASS; - mmwrite(reg, MANTIS_CONTROL); - break; - - case STREAM_TO_CAM: - dprintk(MANTIS_DEBUG, 1, "Set stream to CAM"); - reg |= MANTIS_BYPASS; - mmwrite(reg, MANTIS_CONTROL); - reg &= 0xff - MANTIS_BYPASS; - mmwrite(reg, MANTIS_CONTROL); - break; - default: - dprintk(MANTIS_ERROR, 1, "Unknown MODE <%02x>", stream_ctl); - return -1; - } - - return 0; -} -EXPORT_SYMBOL_GPL(mantis_stream_control); diff --git a/drivers/media/dvb/mantis/mantis_ioc.h b/drivers/media/dvb/mantis/mantis_ioc.h deleted file mode 100644 index d56e002b2955..000000000000 --- a/drivers/media/dvb/mantis/mantis_ioc.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_IOC_H -#define __MANTIS_IOC_H - -#define GPIF_A00 0x00 -#define GPIF_A01 0x01 -#define GPIF_A02 0x02 -#define GPIF_A03 0x03 -#define GPIF_A04 0x04 -#define GPIF_A05 0x05 -#define GPIF_A06 0x06 -#define GPIF_A07 0x07 -#define GPIF_A08 0x08 -#define GPIF_A09 0x09 -#define GPIF_A10 0x0a -#define GPIF_A11 0x0b - -#define GPIF_A12 0x0c -#define GPIF_A13 0x0d -#define GPIF_A14 0x0e - -enum mantis_stream_control { - STREAM_TO_HIF = 0, - STREAM_TO_CAM -}; - -extern int mantis_get_mac(struct mantis_pci *mantis); -extern void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value); - -extern int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl); - -#endif /* __MANTIS_IOC_H */ diff --git a/drivers/media/dvb/mantis/mantis_link.h b/drivers/media/dvb/mantis/mantis_link.h deleted file mode 100644 index 2a814774a001..000000000000 --- a/drivers/media/dvb/mantis/mantis_link.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_LINK_H -#define __MANTIS_LINK_H - -#include -#include -#include "dvb_ca_en50221.h" - -enum mantis_sbuf_status { - MANTIS_SBUF_DATA_AVAIL = 1, - MANTIS_SBUF_DATA_EMPTY = 2, - MANTIS_SBUF_DATA_OVFLW = 3 -}; - -struct mantis_slot { - u32 timeout; - u32 slave_cfg; - u32 bar; -}; - -/* Physical layer */ -enum mantis_slot_state { - MODULE_INSERTED = 3, - MODULE_XTRACTED = 4 -}; - -struct mantis_ca { - struct mantis_slot slot[4]; - - struct work_struct hif_evm_work; - - u32 hif_event; - wait_queue_head_t hif_opdone_wq; - wait_queue_head_t hif_brrdyw_wq; - wait_queue_head_t hif_data_wq; - wait_queue_head_t hif_write_wq; /* HIF Write op */ - - enum mantis_sbuf_status sbuf_status; - - enum mantis_slot_state slot_state; - - void *ca_priv; - - struct dvb_ca_en50221 en50221; - struct mutex ca_lock; -}; - -/* CA */ -extern void mantis_event_cam_plugin(struct mantis_ca *ca); -extern void mantis_event_cam_unplug(struct mantis_ca *ca); -extern int mantis_pcmcia_init(struct mantis_ca *ca); -extern void mantis_pcmcia_exit(struct mantis_ca *ca); -extern int mantis_evmgr_init(struct mantis_ca *ca); -extern void mantis_evmgr_exit(struct mantis_ca *ca); - -/* HIF */ -extern int mantis_hif_init(struct mantis_ca *ca); -extern void mantis_hif_exit(struct mantis_ca *ca); -extern int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr); -extern int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data); -extern int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr); -extern int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data); - -#endif /* __MANTIS_LINK_H */ diff --git a/drivers/media/dvb/mantis/mantis_pci.c b/drivers/media/dvb/mantis/mantis_pci.c deleted file mode 100644 index 371558af2d96..000000000000 --- a/drivers/media/dvb/mantis/mantis_pci.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_reg.h" -#include "mantis_pci.h" - -#define DRIVER_NAME "Mantis Core" - -int __devinit mantis_pci_init(struct mantis_pci *mantis) -{ - u8 latency; - struct mantis_hwconfig *config = mantis->hwconfig; - struct pci_dev *pdev = mantis->pdev; - int err, ret = 0; - - dprintk(MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", - config->model_name, - config->dev_type, - mantis->pdev->bus->number, - PCI_SLOT(mantis->pdev->devfn), - PCI_FUNC(mantis->pdev->devfn)); - - err = pci_enable_device(pdev); - if (err != 0) { - ret = -ENODEV; - dprintk(MANTIS_ERROR, 1, "ERROR: PCI enable failed <%i>", err); - goto fail0; - } - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err != 0) { - dprintk(MANTIS_ERROR, 1, "ERROR: Unable to obtain 32 bit DMA <%i>", err); - ret = -ENOMEM; - goto fail1; - } - - pci_set_master(pdev); - - if (!request_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0), - DRIVER_NAME)) { - - dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 Request failed !"); - ret = -ENODEV; - goto fail1; - } - - mantis->mmio = ioremap(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - - if (!mantis->mmio) { - dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 remap failed !"); - ret = -ENODEV; - goto fail2; - } - - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency); - mantis->latency = latency; - mantis->revision = pdev->revision; - - dprintk(MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", - mantis->revision, - mantis->pdev->subsystem_vendor, - mantis->pdev->subsystem_device); - - dprintk(MANTIS_ERROR, 0, - "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", - mantis->pdev->irq, - mantis->latency, - mantis->mantis_addr, - mantis->mmio); - - err = request_irq(pdev->irq, - config->irq_handler, - IRQF_SHARED, - DRIVER_NAME, - mantis); - - if (err != 0) { - - dprintk(MANTIS_ERROR, 1, "ERROR: IRQ registration failed ! <%d>", err); - ret = -ENODEV; - goto fail3; - } - - pci_set_drvdata(pdev, mantis); - return ret; - - /* Error conditions */ -fail3: - dprintk(MANTIS_ERROR, 1, "ERROR: <%d> I/O unmap", ret); - if (mantis->mmio) - iounmap(mantis->mmio); - -fail2: - dprintk(MANTIS_ERROR, 1, "ERROR: <%d> releasing regions", ret); - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - -fail1: - dprintk(MANTIS_ERROR, 1, "ERROR: <%d> disabling device", ret); - pci_disable_device(pdev); - -fail0: - dprintk(MANTIS_ERROR, 1, "ERROR: <%d> exiting", ret); - pci_set_drvdata(pdev, NULL); - return ret; -} -EXPORT_SYMBOL_GPL(mantis_pci_init); - -void mantis_pci_exit(struct mantis_pci *mantis) -{ - struct pci_dev *pdev = mantis->pdev; - - dprintk(MANTIS_NOTICE, 1, " mem: 0x%p", mantis->mmio); - free_irq(pdev->irq, mantis); - if (mantis->mmio) { - iounmap(mantis->mmio); - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - } - - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); -} -EXPORT_SYMBOL_GPL(mantis_pci_exit); - -MODULE_DESCRIPTION("Mantis PCI DTV bridge driver"); -MODULE_AUTHOR("Manu Abraham"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/mantis/mantis_pci.h b/drivers/media/dvb/mantis/mantis_pci.h deleted file mode 100644 index 65f004519086..000000000000 --- a/drivers/media/dvb/mantis/mantis_pci.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_PCI_H -#define __MANTIS_PCI_H - -extern int mantis_pci_init(struct mantis_pci *mantis); -extern void mantis_pci_exit(struct mantis_pci *mantis); - -#endif /* __MANTIS_PCI_H */ diff --git a/drivers/media/dvb/mantis/mantis_pcmcia.c b/drivers/media/dvb/mantis/mantis_pcmcia.c deleted file mode 100644 index 2f188c089666..000000000000 --- a/drivers/media/dvb/mantis/mantis_pcmcia.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_link.h" /* temporary due to physical layer stuff */ -#include "mantis_reg.h" - -/* - * If Slot state is already PLUG_IN event and we are called - * again, definitely it is jitter alone - */ -void mantis_event_cam_plugin(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - - u32 gpif_irqcfg; - - if (ca->slot_state == MODULE_XTRACTED) { - dprintk(MANTIS_DEBUG, 1, "Event: CAM Plugged IN: Adapter(%d) Slot(0)", mantis->num); - udelay(50); - mmwrite(0xda000000, MANTIS_CARD_RESET); - gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); - gpif_irqcfg |= MANTIS_MASK_PLUGOUT; - gpif_irqcfg &= ~MANTIS_MASK_PLUGIN; - mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); - udelay(500); - ca->slot_state = MODULE_INSERTED; - } - udelay(100); -} - -/* - * If Slot state is already UN_PLUG event and we are called - * again, definitely it is jitter alone - */ -void mantis_event_cam_unplug(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - - u32 gpif_irqcfg; - - if (ca->slot_state == MODULE_INSERTED) { - dprintk(MANTIS_DEBUG, 1, "Event: CAM Unplugged: Adapter(%d) Slot(0)", mantis->num); - udelay(50); - mmwrite(0x00da0000, MANTIS_CARD_RESET); - gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); - gpif_irqcfg |= MANTIS_MASK_PLUGIN; - gpif_irqcfg &= ~MANTIS_MASK_PLUGOUT; - mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); - udelay(500); - ca->slot_state = MODULE_XTRACTED; - } - udelay(100); -} - -int mantis_pcmcia_init(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - - u32 gpif_stat, card_stat; - - mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_IRQ0, MANTIS_INT_MASK); - gpif_stat = mmread(MANTIS_GPIF_STATUS); - card_stat = mmread(MANTIS_GPIF_IRQCFG); - - if (gpif_stat & MANTIS_GPIF_DETSTAT) { - dprintk(MANTIS_DEBUG, 1, "CAM found on Adapter(%d) Slot(0)", mantis->num); - mmwrite(card_stat | MANTIS_MASK_PLUGOUT, MANTIS_GPIF_IRQCFG); - ca->slot_state = MODULE_INSERTED; - dvb_ca_en50221_camchange_irq(&ca->en50221, - 0, - DVB_CA_EN50221_CAMCHANGE_INSERTED); - } else { - dprintk(MANTIS_DEBUG, 1, "Empty Slot on Adapter(%d) Slot(0)", mantis->num); - mmwrite(card_stat | MANTIS_MASK_PLUGIN, MANTIS_GPIF_IRQCFG); - ca->slot_state = MODULE_XTRACTED; - dvb_ca_en50221_camchange_irq(&ca->en50221, - 0, - DVB_CA_EN50221_CAMCHANGE_REMOVED); - } - - return 0; -} - -void mantis_pcmcia_exit(struct mantis_ca *ca) -{ - struct mantis_pci *mantis = ca->ca_priv; - - mmwrite(mmread(MANTIS_GPIF_STATUS) & (~MANTIS_CARD_PLUGOUT | ~MANTIS_CARD_PLUGIN), MANTIS_GPIF_STATUS); - mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ0, MANTIS_INT_MASK); -} diff --git a/drivers/media/dvb/mantis/mantis_reg.h b/drivers/media/dvb/mantis/mantis_reg.h deleted file mode 100644 index 7761f9dc7fe0..000000000000 --- a/drivers/media/dvb/mantis/mantis_reg.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_REG_H -#define __MANTIS_REG_H - -/* Interrupts */ -#define MANTIS_INT_STAT 0x00 -#define MANTIS_INT_MASK 0x04 - -#define MANTIS_INT_RISCSTAT (0x0f << 28) -#define MANTIS_INT_RISCEN (0x01 << 27) -#define MANTIS_INT_I2CRACK (0x01 << 26) - -/* #define MANTIS_INT_GPIF (0xff << 12) */ - -#define MANTIS_INT_PCMCIA7 (0x01 << 19) -#define MANTIS_INT_PCMCIA6 (0x01 << 18) -#define MANTIS_INT_PCMCIA5 (0x01 << 17) -#define MANTIS_INT_PCMCIA4 (0x01 << 16) -#define MANTIS_INT_PCMCIA3 (0x01 << 15) -#define MANTIS_INT_PCMCIA2 (0x01 << 14) -#define MANTIS_INT_PCMCIA1 (0x01 << 13) -#define MANTIS_INT_PCMCIA0 (0x01 << 12) -#define MANTIS_INT_IRQ1 (0x01 << 11) -#define MANTIS_INT_IRQ0 (0x01 << 10) -#define MANTIS_INT_OCERR (0x01 << 8) -#define MANTIS_INT_PABORT (0x01 << 7) -#define MANTIS_INT_RIPERR (0x01 << 6) -#define MANTIS_INT_PPERR (0x01 << 5) -#define MANTIS_INT_FTRGT (0x01 << 3) -#define MANTIS_INT_RISCI (0x01 << 1) -#define MANTIS_INT_I2CDONE (0x01 << 0) - -/* DMA */ -#define MANTIS_DMA_CTL 0x08 -#define MANTIS_GPIF_RD (0xff << 24) -#define MANTIS_GPIF_WR (0xff << 16) -#define MANTIS_CPU_DO (0x01 << 10) -#define MANTIS_DRV_DO (0x01 << 9) -#define MANTIS_I2C_RD (0x01 << 7) -#define MANTIS_I2C_WR (0x01 << 6) -#define MANTIS_DCAP_MODE (0x01 << 5) -#define MANTIS_FIFO_TP_4 (0x00 << 3) -#define MANTIS_FIFO_TP_8 (0x01 << 3) -#define MANTIS_FIFO_TP_16 (0x02 << 3) -#define MANTIS_FIFO_EN (0x01 << 2) -#define MANTIS_DCAP_EN (0x01 << 1) -#define MANTIS_RISC_EN (0x01 << 0) - -/* DEBUG */ -#define MANTIS_DEBUGREG 0x0c -#define MANTIS_DATINV (0x0e << 7) -#define MANTIS_TOP_DEBUGSEL (0x07 << 4) -#define MANTIS_PCMCIA_DEBUGSEL (0x0f << 0) - -#define MANTIS_RISC_START 0x10 -#define MANTIS_RISC_PC 0x14 - -/* I2C */ -#define MANTIS_I2CDATA_CTL 0x18 -#define MANTIS_I2C_RATE_1 (0x00 << 6) -#define MANTIS_I2C_RATE_2 (0x01 << 6) -#define MANTIS_I2C_RATE_3 (0x02 << 6) -#define MANTIS_I2C_RATE_4 (0x03 << 6) -#define MANTIS_I2C_STOP (0x01 << 5) -#define MANTIS_I2C_PGMODE (0x01 << 3) - -/* DATA */ -#define MANTIS_CMD_DATA_R1 0x20 -#define MANTIS_CMD_DATA_3 (0xff << 24) -#define MANTIS_CMD_DATA_2 (0xff << 16) -#define MANTIS_CMD_DATA_1 (0xff << 8) -#define MANTIS_CMD_DATA_0 (0xff << 0) - -#define MANTIS_CMD_DATA_R2 0x24 -#define MANTIS_CMD_DATA_7 (0xff << 24) -#define MANTIS_CMD_DATA_6 (0xff << 16) -#define MANTIS_CMD_DATA_5 (0xff << 8) -#define MANTIS_CMD_DATA_4 (0xff << 0) - -#define MANTIS_CONTROL 0x28 -#define MANTIS_DET (0x01 << 7) -#define MANTIS_DAT_CF_EN (0x01 << 6) -#define MANTIS_ACS (0x03 << 4) -#define MANTIS_VCCEN (0x01 << 3) -#define MANTIS_BYPASS (0x01 << 2) -#define MANTIS_MRST (0x01 << 1) -#define MANTIS_CRST_INT (0x01 << 0) - -#define MANTIS_GPIF_CFGSLA 0x84 -#define MANTIS_GPIF_WAITSMPL (0x07 << 28) -#define MANTIS_GPIF_BYTEADDRSUB (0x01 << 25) -#define MANTIS_GPIF_WAITPOL (0x01 << 24) -#define MANTIS_GPIF_NCDELAY (0x07 << 20) -#define MANTIS_GPIF_RW2CSDELAY (0x07 << 16) -#define MANTIS_GPIF_SLFTIMEDMODE (0x01 << 15) -#define MANTIS_GPIF_SLFTIMEDDELY (0x7f << 8) -#define MANTIS_GPIF_DEVTYPE (0x07 << 4) -#define MANTIS_GPIF_BIGENDIAN (0x01 << 3) -#define MANTIS_GPIF_FETCHCMD (0x03 << 1) -#define MANTIS_GPIF_HWORDDEV (0x01 << 0) - -#define MANTIS_GPIF_WSTOPER 0x90 -#define MANTIS_GPIF_WSTOPERWREN3 (0x01 << 31) -#define MANTIS_GPIF_PARBOOTN (0x01 << 29) -#define MANTIS_GPIF_WSTOPERSLID3 (0x1f << 24) -#define MANTIS_GPIF_WSTOPERWREN2 (0x01 << 23) -#define MANTIS_GPIF_WSTOPERSLID2 (0x1f << 16) -#define MANTIS_GPIF_WSTOPERWREN1 (0x01 << 15) -#define MANTIS_GPIF_WSTOPERSLID1 (0x1f << 8) -#define MANTIS_GPIF_WSTOPERWREN0 (0x01 << 7) -#define MANTIS_GPIF_WSTOPERSLID0 (0x1f << 0) - -#define MANTIS_GPIF_CS2RW 0x94 -#define MANTIS_GPIF_CS2RWWREN3 (0x01 << 31) -#define MANTIS_GPIF_CS2RWDELY3 (0x3f << 24) -#define MANTIS_GPIF_CS2RWWREN2 (0x01 << 23) -#define MANTIS_GPIF_CS2RWDELY2 (0x3f << 16) -#define MANTIS_GPIF_CS2RWWREN1 (0x01 << 15) -#define MANTIS_GPIF_CS2RWDELY1 (0x3f << 8) -#define MANTIS_GPIF_CS2RWWREN0 (0x01 << 7) -#define MANTIS_GPIF_CS2RWDELY0 (0x3f << 0) - -#define MANTIS_GPIF_IRQCFG 0x98 -#define MANTIS_GPIF_IRQPOL (0x01 << 8) -#define MANTIS_MASK_WRACK (0x01 << 7) -#define MANTIS_MASK_BRRDY (0x01 << 6) -#define MANTIS_MASK_OVFLW (0x01 << 5) -#define MANTIS_MASK_OTHERR (0x01 << 4) -#define MANTIS_MASK_WSTO (0x01 << 3) -#define MANTIS_MASK_EXTIRQ (0x01 << 2) -#define MANTIS_MASK_PLUGIN (0x01 << 1) -#define MANTIS_MASK_PLUGOUT (0x01 << 0) - -#define MANTIS_GPIF_STATUS 0x9c -#define MANTIS_SBUF_KILLOP (0x01 << 15) -#define MANTIS_SBUF_OPDONE (0x01 << 14) -#define MANTIS_SBUF_EMPTY (0x01 << 13) -#define MANTIS_GPIF_DETSTAT (0x01 << 9) -#define MANTIS_GPIF_INTSTAT (0x01 << 8) -#define MANTIS_GPIF_WRACK (0x01 << 7) -#define MANTIS_GPIF_BRRDY (0x01 << 6) -#define MANTIS_SBUF_OVFLW (0x01 << 5) -#define MANTIS_GPIF_OTHERR (0x01 << 4) -#define MANTIS_SBUF_WSTO (0x01 << 3) -#define MANTIS_GPIF_EXTIRQ (0x01 << 2) -#define MANTIS_CARD_PLUGIN (0x01 << 1) -#define MANTIS_CARD_PLUGOUT (0x01 << 0) - -#define MANTIS_GPIF_BRADDR 0xa0 -#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) -#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) -#define MANTIS_GPIF_BR_ADDR (0xfffffff << 0) - -#define MANTIS_GPIF_BRBYTES 0xa4 -#define MANTIS_GPIF_BRCNT (0xfff << 0) - -#define MANTIS_PCMCIA_RESET 0xa8 -#define MANTIS_PCMCIA_RSTVAL (0xff << 0) - -#define MANTIS_CARD_RESET 0xac - -#define MANTIS_GPIF_ADDR 0xb0 -#define MANTIS_GPIF_HIFRDWRN (0x01 << 31) -#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) -#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) -#define MANTIS_GPIF_HIFADDR (0xfffffff << 0) - -#define MANTIS_GPIF_DOUT 0xb4 -#define MANTIS_GPIF_HIFDOUT (0xfffffff << 0) - -#define MANTIS_GPIF_DIN 0xb8 -#define MANTIS_GPIF_HIFDIN (0xfffffff << 0) - -#define MANTIS_GPIF_SPARE 0xbc -#define MANTIS_GPIF_LOGICRD (0xffff << 16) -#define MANTIS_GPIF_LOGICRW (0xffff << 0) - -#endif /* __MANTIS_REG_H */ diff --git a/drivers/media/dvb/mantis/mantis_uart.c b/drivers/media/dvb/mantis/mantis_uart.c deleted file mode 100644 index 18340dafa426..000000000000 --- a/drivers/media/dvb/mantis/mantis_uart.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_reg.h" -#include "mantis_uart.h" - -struct mantis_uart_params { - enum mantis_baud baud_rate; - enum mantis_parity parity; -}; - -static struct { - char string[7]; -} rates[5] = { - { "9600" }, - { "19200" }, - { "38400" }, - { "57600" }, - { "115200" } -}; - -static struct { - char string[5]; -} parity[3] = { - { "NONE" }, - { "ODD" }, - { "EVEN" } -}; - -#define UART_MAX_BUF 16 - -int mantis_uart_read(struct mantis_pci *mantis, u8 *data) -{ - struct mantis_hwconfig *config = mantis->hwconfig; - u32 stat = 0, i; - - /* get data */ - for (i = 0; i < (config->bytes + 1); i++) { - - stat = mmread(MANTIS_UART_STAT); - - if (stat & MANTIS_UART_RXFIFO_FULL) { - dprintk(MANTIS_ERROR, 1, "RX Fifo FULL"); - } - data[i] = mmread(MANTIS_UART_RXD) & 0x3f; - - dprintk(MANTIS_DEBUG, 1, "Reading ... <%02x>", data[i] & 0x3f); - - if (data[i] & (1 << 7)) { - dprintk(MANTIS_ERROR, 1, "UART framing error"); - return -EINVAL; - } - if (data[i] & (1 << 6)) { - dprintk(MANTIS_ERROR, 1, "UART parity error"); - return -EINVAL; - } - } - - return 0; -} - -static void mantis_uart_work(struct work_struct *work) -{ - struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work); - struct mantis_hwconfig *config = mantis->hwconfig; - u8 buf[16]; - int i; - - mantis_uart_read(mantis, buf); - - for (i = 0; i < (config->bytes + 1); i++) - dprintk(MANTIS_INFO, 1, "UART BUF:%d <%02x> ", i, buf[i]); - - dprintk(MANTIS_DEBUG, 0, "\n"); -} - -static int mantis_uart_setup(struct mantis_pci *mantis, - struct mantis_uart_params *params) -{ - u32 reg; - - mmwrite((mmread(MANTIS_UART_CTL) | (params->parity & 0x3)), MANTIS_UART_CTL); - - reg = mmread(MANTIS_UART_BAUD); - - switch (params->baud_rate) { - case MANTIS_BAUD_9600: - reg |= 0xd8; - break; - case MANTIS_BAUD_19200: - reg |= 0x6c; - break; - case MANTIS_BAUD_38400: - reg |= 0x36; - break; - case MANTIS_BAUD_57600: - reg |= 0x23; - break; - case MANTIS_BAUD_115200: - reg |= 0x11; - break; - default: - return -EINVAL; - } - - mmwrite(reg, MANTIS_UART_BAUD); - - return 0; -} - -int mantis_uart_init(struct mantis_pci *mantis) -{ - struct mantis_hwconfig *config = mantis->hwconfig; - struct mantis_uart_params params; - - /* default parity: */ - params.baud_rate = config->baud_rate; - params.parity = config->parity; - dprintk(MANTIS_INFO, 1, "Initializing UART @ %sbps parity:%s", - rates[params.baud_rate].string, - parity[params.parity].string); - - init_waitqueue_head(&mantis->uart_wq); - spin_lock_init(&mantis->uart_lock); - - INIT_WORK(&mantis->uart_work, mantis_uart_work); - - /* disable interrupt */ - mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); - - mantis_uart_setup(mantis, ¶ms); - - /* default 1 byte */ - mmwrite((mmread(MANTIS_UART_BAUD) | (config->bytes << 8)), MANTIS_UART_BAUD); - - /* flush buffer */ - mmwrite((mmread(MANTIS_UART_CTL) | MANTIS_UART_RXFLUSH), MANTIS_UART_CTL); - - /* enable interrupt */ - mmwrite(mmread(MANTIS_INT_MASK) | 0x800, MANTIS_INT_MASK); - mmwrite(mmread(MANTIS_UART_CTL) | MANTIS_UART_RXINT, MANTIS_UART_CTL); - - schedule_work(&mantis->uart_work); - dprintk(MANTIS_DEBUG, 1, "UART successfully initialized"); - - return 0; -} -EXPORT_SYMBOL_GPL(mantis_uart_init); - -void mantis_uart_exit(struct mantis_pci *mantis) -{ - /* disable interrupt */ - mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); - flush_work_sync(&mantis->uart_work); -} -EXPORT_SYMBOL_GPL(mantis_uart_exit); diff --git a/drivers/media/dvb/mantis/mantis_uart.h b/drivers/media/dvb/mantis/mantis_uart.h deleted file mode 100644 index ffb62a0a5a13..000000000000 --- a/drivers/media/dvb/mantis/mantis_uart.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - Mantis PCI bridge driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_UART_H -#define __MANTIS_UART_H - -#define MANTIS_UART_CTL 0xe0 -#define MANTIS_UART_RXINT (1 << 4) -#define MANTIS_UART_RXFLUSH (1 << 2) - -#define MANTIS_UART_RXD 0xe8 -#define MANTIS_UART_BAUD 0xec - -#define MANTIS_UART_STAT 0xf0 -#define MANTIS_UART_RXFIFO_DATA (1 << 7) -#define MANTIS_UART_RXFIFO_EMPTY (1 << 6) -#define MANTIS_UART_RXFIFO_FULL (1 << 3) -#define MANTIS_UART_FRAME_ERR (1 << 2) -#define MANTIS_UART_PARITY_ERR (1 << 1) -#define MANTIS_UART_RXTHRESH_INT (1 << 0) - -enum mantis_baud { - MANTIS_BAUD_9600 = 0, - MANTIS_BAUD_19200, - MANTIS_BAUD_38400, - MANTIS_BAUD_57600, - MANTIS_BAUD_115200 -}; - -enum mantis_parity { - MANTIS_PARITY_NONE = 0, - MANTIS_PARITY_EVEN, - MANTIS_PARITY_ODD, -}; - -struct mantis_pci; - -extern int mantis_uart_init(struct mantis_pci *mantis); -extern void mantis_uart_exit(struct mantis_pci *mantis); - -#endif /* __MANTIS_UART_H */ diff --git a/drivers/media/dvb/mantis/mantis_vp1033.c b/drivers/media/dvb/mantis/mantis_vp1033.c deleted file mode 100644 index ad013e93ed11..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp1033.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - Mantis VP-1033 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "stv0299.h" -#include "mantis_common.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" -#include "mantis_vp1033.h" -#include "mantis_reg.h" - -u8 lgtdqcs001f_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x2a, - 0x05, 0x85, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0x00, - 0x0c, 0x01, - 0x0d, 0x81, - 0x0e, 0x44, - 0x0f, 0x94, - 0x10, 0x3c, - 0x11, 0x84, - 0x12, 0xb9, - 0x13, 0xb5, - 0x14, 0x4f, - 0x15, 0xc9, - 0x16, 0x80, - 0x17, 0x36, - 0x18, 0xfb, - 0x19, 0xcf, - 0x1a, 0xbc, - 0x1c, 0x2b, - 0x1d, 0x27, - 0x1e, 0x00, - 0x1f, 0x0b, - 0x20, 0xa1, - 0x21, 0x60, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, - 0x29, 0x28, - 0x2a, 0x14, - 0x2b, 0x0f, - 0x2c, 0x09, - 0x2d, 0x05, - 0x31, 0x1f, - 0x32, 0x19, - 0x33, 0xfc, - 0x34, 0x13, - 0xff, 0xff, -}; - -#define MANTIS_MODEL_NAME "VP-1033" -#define MANTIS_DEV_TYPE "DVB-S/DSS" - -int lgtdqcs001f_tuner_set(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct mantis_pci *mantis = fe->dvb->priv; - struct i2c_adapter *adapter = &mantis->adapter; - - u8 buf[4]; - u32 div; - - - struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf)}; - - div = p->frequency / 250; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x83; - buf[3] = 0xc0; - - if (p->frequency < 1531000) - buf[3] |= 0x04; - else - buf[3] &= ~0x04; - if (i2c_transfer(adapter, &msg, 1) < 0) { - dprintk(MANTIS_ERROR, 1, "Write: I2C Transfer failed"); - return -EIO; - } - msleep_interruptible(100); - - return 0; -} - -int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe, - u32 srate, u32 ratio) -{ - u8 aclk = 0; - u8 bclk = 0; - - if (srate < 1500000) { - aclk = 0xb7; - bclk = 0x47; - } else if (srate < 3000000) { - aclk = 0xb7; - bclk = 0x4b; - } else if (srate < 7000000) { - aclk = 0xb7; - bclk = 0x4f; - } else if (srate < 14000000) { - aclk = 0xb7; - bclk = 0x53; - } else if (srate < 30000000) { - aclk = 0xb6; - bclk = 0x53; - } else if (srate < 45000000) { - aclk = 0xb4; - bclk = 0x51; - } - stv0299_writereg(fe, 0x13, aclk); - stv0299_writereg(fe, 0x14, bclk); - - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, ratio & 0xf0); - - return 0; -} - -struct stv0299_config lgtdqcs001f_config = { - .demod_address = 0x68, - .inittab = lgtdqcs001f_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = lgtdqcs001f_set_symbol_rate, -}; - -static int vp1033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) -{ - struct i2c_adapter *adapter = &mantis->adapter; - - int err = 0; - - err = mantis_frontend_power(mantis, POWER_ON); - if (err == 0) { - mantis_frontend_soft_reset(mantis); - msleep(250); - - dprintk(MANTIS_ERROR, 1, "Probing for STV0299 (DVB-S)"); - fe = dvb_attach(stv0299_attach, &lgtdqcs001f_config, adapter); - - if (fe) { - fe->ops.tuner_ops.set_params = lgtdqcs001f_tuner_set; - dprintk(MANTIS_ERROR, 1, "found STV0299 DVB-S frontend @ 0x%02x", - lgtdqcs001f_config.demod_address); - - dprintk(MANTIS_ERROR, 1, "Mantis DVB-S STV0299 frontend attach success"); - } else { - return -1; - } - } else { - dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", - adapter->name, - err); - - return -EIO; - } - mantis->fe = fe; - dprintk(MANTIS_ERROR, 1, "Done!"); - - return 0; -} - -struct mantis_hwconfig vp1033_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_204, - - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, - - .frontend_init = vp1033_frontend_init, - .power = GPIF_A12, - .reset = GPIF_A13, -}; diff --git a/drivers/media/dvb/mantis/mantis_vp1033.h b/drivers/media/dvb/mantis/mantis_vp1033.h deleted file mode 100644 index 7daaa1bf127d..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp1033.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Mantis VP-1033 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP1033_H -#define __MANTIS_VP1033_H - -#include "mantis_common.h" - -#define MANTIS_VP_1033_DVB_S 0x0016 - -extern struct mantis_hwconfig vp1033_config; - -#endif /* __MANTIS_VP1033_H */ diff --git a/drivers/media/dvb/mantis/mantis_vp1034.c b/drivers/media/dvb/mantis/mantis_vp1034.c deleted file mode 100644 index 430ae84ce528..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp1034.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - Mantis VP-1034 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mb86a16.h" -#include "mantis_common.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" -#include "mantis_vp1034.h" -#include "mantis_reg.h" - -struct mb86a16_config vp1034_mb86a16_config = { - .demod_address = 0x08, - .set_voltage = vp1034_set_voltage, -}; - -#define MANTIS_MODEL_NAME "VP-1034" -#define MANTIS_DEV_TYPE "DVB-S/DSS" - -int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct mantis_pci *mantis = fe->dvb->priv; - - switch (voltage) { - case SEC_VOLTAGE_13: - dprintk(MANTIS_ERROR, 1, "Polarization=[13V]"); - mantis_gpio_set_bits(mantis, 13, 1); - mantis_gpio_set_bits(mantis, 14, 0); - break; - case SEC_VOLTAGE_18: - dprintk(MANTIS_ERROR, 1, "Polarization=[18V]"); - mantis_gpio_set_bits(mantis, 13, 1); - mantis_gpio_set_bits(mantis, 14, 1); - break; - case SEC_VOLTAGE_OFF: - dprintk(MANTIS_ERROR, 1, "Frontend (dummy) POWERDOWN"); - break; - default: - dprintk(MANTIS_ERROR, 1, "Invalid = (%d)", (u32) voltage); - return -EINVAL; - } - mmwrite(0x00, MANTIS_GPIF_DOUT); - - return 0; -} - -static int vp1034_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) -{ - struct i2c_adapter *adapter = &mantis->adapter; - - int err = 0; - - err = mantis_frontend_power(mantis, POWER_ON); - if (err == 0) { - mantis_frontend_soft_reset(mantis); - msleep(250); - - dprintk(MANTIS_ERROR, 1, "Probing for MB86A16 (DVB-S/DSS)"); - fe = dvb_attach(mb86a16_attach, &vp1034_mb86a16_config, adapter); - if (fe) { - dprintk(MANTIS_ERROR, 1, - "found MB86A16 DVB-S/DSS frontend @0x%02x", - vp1034_mb86a16_config.demod_address); - - } else { - return -1; - } - } else { - dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", - adapter->name, - err); - - return -EIO; - } - mantis->fe = fe; - dprintk(MANTIS_ERROR, 1, "Done!"); - - return 0; -} - -struct mantis_hwconfig vp1034_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_204, - - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, - - .frontend_init = vp1034_frontend_init, - .power = GPIF_A12, - .reset = GPIF_A13, -}; diff --git a/drivers/media/dvb/mantis/mantis_vp1034.h b/drivers/media/dvb/mantis/mantis_vp1034.h deleted file mode 100644 index 323f38ef8e3d..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp1034.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Mantis VP-1034 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP1034_H -#define __MANTIS_VP1034_H - -#include "dvb_frontend.h" -#include "mantis_common.h" - - -#define MANTIS_VP_1034_DVB_S 0x0014 - -extern struct mantis_hwconfig vp1034_config; -extern int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); - -#endif /* __MANTIS_VP1034_H */ diff --git a/drivers/media/dvb/mantis/mantis_vp1041.c b/drivers/media/dvb/mantis/mantis_vp1041.c deleted file mode 100644 index 07aa887a4b4a..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp1041.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - Mantis VP-1041 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "mantis_common.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" -#include "mantis_vp1041.h" -#include "stb0899_reg.h" -#include "stb0899_drv.h" -#include "stb0899_cfg.h" -#include "stb6100_cfg.h" -#include "stb6100.h" -#include "lnbp21.h" - -#define MANTIS_MODEL_NAME "VP-1041" -#define MANTIS_DEV_TYPE "DSS/DVB-S/DVB-S2" - -static const struct stb0899_s1_reg vp1041_stb0899_s1_init_1[] = { - - /* 0x0000000b, *//* SYSREG */ - { STB0899_DEV_ID , 0x30 }, - { STB0899_DISCNTRL1 , 0x32 }, - { STB0899_DISCNTRL2 , 0x80 }, - { STB0899_DISRX_ST0 , 0x04 }, - { STB0899_DISRX_ST1 , 0x00 }, - { STB0899_DISPARITY , 0x00 }, - { STB0899_DISSTATUS , 0x20 }, - { STB0899_DISF22 , 0x99 }, - { STB0899_DISF22RX , 0xa8 }, - /* SYSREG ? */ - { STB0899_ACRPRESC , 0x11 }, - { STB0899_ACRDIV1 , 0x0a }, - { STB0899_ACRDIV2 , 0x05 }, - { STB0899_DACR1 , 0x00 }, - { STB0899_DACR2 , 0x00 }, - { STB0899_OUTCFG , 0x00 }, - { STB0899_MODECFG , 0x00 }, - { STB0899_IRQSTATUS_3 , 0xfe }, - { STB0899_IRQSTATUS_2 , 0x03 }, - { STB0899_IRQSTATUS_1 , 0x7c }, - { STB0899_IRQSTATUS_0 , 0xf4 }, - { STB0899_IRQMSK_3 , 0xf3 }, - { STB0899_IRQMSK_2 , 0xfc }, - { STB0899_IRQMSK_1 , 0xff }, - { STB0899_IRQMSK_0 , 0xff }, - { STB0899_IRQCFG , 0x00 }, - { STB0899_I2CCFG , 0x88 }, - { STB0899_I2CRPT , 0x58 }, - { STB0899_IOPVALUE5 , 0x00 }, - { STB0899_IOPVALUE4 , 0x33 }, - { STB0899_IOPVALUE3 , 0x6d }, - { STB0899_IOPVALUE2 , 0x90 }, - { STB0899_IOPVALUE1 , 0x60 }, - { STB0899_IOPVALUE0 , 0x00 }, - { STB0899_GPIO00CFG , 0x82 }, - { STB0899_GPIO01CFG , 0x82 }, - { STB0899_GPIO02CFG , 0x82 }, - { STB0899_GPIO03CFG , 0x82 }, - { STB0899_GPIO04CFG , 0x82 }, - { STB0899_GPIO05CFG , 0x82 }, - { STB0899_GPIO06CFG , 0x82 }, - { STB0899_GPIO07CFG , 0x82 }, - { STB0899_GPIO08CFG , 0x82 }, - { STB0899_GPIO09CFG , 0x82 }, - { STB0899_GPIO10CFG , 0x82 }, - { STB0899_GPIO11CFG , 0x82 }, - { STB0899_GPIO12CFG , 0x82 }, - { STB0899_GPIO13CFG , 0x82 }, - { STB0899_GPIO14CFG , 0x82 }, - { STB0899_GPIO15CFG , 0x82 }, - { STB0899_GPIO16CFG , 0x82 }, - { STB0899_GPIO17CFG , 0x82 }, - { STB0899_GPIO18CFG , 0x82 }, - { STB0899_GPIO19CFG , 0x82 }, - { STB0899_GPIO20CFG , 0x82 }, - { STB0899_SDATCFG , 0xb8 }, - { STB0899_SCLTCFG , 0xba }, - { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ - { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ - { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ - { STB0899_DIRCLKCFG , 0x82 }, - { STB0899_CLKOUT27CFG , 0x7e }, - { STB0899_STDBYCFG , 0x82 }, - { STB0899_CS0CFG , 0x82 }, - { STB0899_CS1CFG , 0x82 }, - { STB0899_DISEQCOCFG , 0x20 }, - { STB0899_GPIO32CFG , 0x82 }, - { STB0899_GPIO33CFG , 0x82 }, - { STB0899_GPIO34CFG , 0x82 }, - { STB0899_GPIO35CFG , 0x82 }, - { STB0899_GPIO36CFG , 0x82 }, - { STB0899_GPIO37CFG , 0x82 }, - { STB0899_GPIO38CFG , 0x82 }, - { STB0899_GPIO39CFG , 0x82 }, - { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ - { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ - { STB0899_FILTCTRL , 0x00 }, - { STB0899_SYSCTRL , 0x01 }, - { STB0899_STOPCLK1 , 0x20 }, - { STB0899_STOPCLK2 , 0x00 }, - { STB0899_INTBUFSTATUS , 0x00 }, - { STB0899_INTBUFCTRL , 0x0a }, - { 0xffff , 0xff }, -}; - -static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { - { STB0899_DEMOD , 0x00 }, - { STB0899_RCOMPC , 0xc9 }, - { STB0899_AGC1CN , 0x01 }, - { STB0899_AGC1REF , 0x10 }, - { STB0899_RTC , 0x23 }, - { STB0899_TMGCFG , 0x4e }, - { STB0899_AGC2REF , 0x34 }, - { STB0899_TLSR , 0x84 }, - { STB0899_CFD , 0xf7 }, - { STB0899_ACLC , 0x87 }, - { STB0899_BCLC , 0x94 }, - { STB0899_EQON , 0x41 }, - { STB0899_LDT , 0xf1 }, - { STB0899_LDT2 , 0xe3 }, - { STB0899_EQUALREF , 0xb4 }, - { STB0899_TMGRAMP , 0x10 }, - { STB0899_TMGTHD , 0x30 }, - { STB0899_IDCCOMP , 0xfd }, - { STB0899_QDCCOMP , 0xff }, - { STB0899_POWERI , 0x0c }, - { STB0899_POWERQ , 0x0f }, - { STB0899_RCOMP , 0x6c }, - { STB0899_AGCIQIN , 0x80 }, - { STB0899_AGC2I1 , 0x06 }, - { STB0899_AGC2I2 , 0x00 }, - { STB0899_TLIR , 0x30 }, - { STB0899_RTF , 0x7f }, - { STB0899_DSTATUS , 0x00 }, - { STB0899_LDI , 0xbc }, - { STB0899_CFRM , 0xea }, - { STB0899_CFRL , 0x31 }, - { STB0899_NIRM , 0x2b }, - { STB0899_NIRL , 0x80 }, - { STB0899_ISYMB , 0x1d }, - { STB0899_QSYMB , 0xa6 }, - { STB0899_SFRH , 0x2f }, - { STB0899_SFRM , 0x68 }, - { STB0899_SFRL , 0x40 }, - { STB0899_SFRUPH , 0x2f }, - { STB0899_SFRUPM , 0x68 }, - { STB0899_SFRUPL , 0x40 }, - { STB0899_EQUAI1 , 0x02 }, - { STB0899_EQUAQ1 , 0xff }, - { STB0899_EQUAI2 , 0x04 }, - { STB0899_EQUAQ2 , 0x05 }, - { STB0899_EQUAI3 , 0x02 }, - { STB0899_EQUAQ3 , 0xfd }, - { STB0899_EQUAI4 , 0x03 }, - { STB0899_EQUAQ4 , 0x07 }, - { STB0899_EQUAI5 , 0x08 }, - { STB0899_EQUAQ5 , 0xf5 }, - { STB0899_DSTATUS2 , 0x00 }, - { STB0899_VSTATUS , 0x00 }, - { STB0899_VERROR , 0x86 }, - { STB0899_IQSWAP , 0x2a }, - { STB0899_ECNT1M , 0x00 }, - { STB0899_ECNT1L , 0x00 }, - { STB0899_ECNT2M , 0x00 }, - { STB0899_ECNT2L , 0x00 }, - { STB0899_ECNT3M , 0x0a }, - { STB0899_ECNT3L , 0xad }, - { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, - { STB0899_VTH12 , 0xb0 }, - { STB0899_VTH23 , 0x7a }, - { STB0899_VTH34 , 0x58 }, - { STB0899_VTH56 , 0x38 }, - { STB0899_VTH67 , 0x34 }, - { STB0899_VTH78 , 0x24 }, - { STB0899_PRVIT , 0xff }, - { STB0899_VITSYNC , 0x19 }, - { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ - { STB0899_TSULC , 0x42 }, - { STB0899_RSLLC , 0x41 }, - { STB0899_TSLPL , 0x12 }, - { STB0899_TSCFGH , 0x0c }, - { STB0899_TSCFGM , 0x00 }, - { STB0899_TSCFGL , 0x00 }, - { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */ - { STB0899_RSSYNCDEL , 0x00 }, - { STB0899_TSINHDELH , 0x02 }, - { STB0899_TSINHDELM , 0x00 }, - { STB0899_TSINHDELL , 0x00 }, - { STB0899_TSLLSTKM , 0x1b }, - { STB0899_TSLLSTKL , 0xb3 }, - { STB0899_TSULSTKM , 0x00 }, - { STB0899_TSULSTKL , 0x00 }, - { STB0899_PCKLENUL , 0xbc }, - { STB0899_PCKLENLL , 0xcc }, - { STB0899_RSPCKLEN , 0xbd }, - { STB0899_TSSTATUS , 0x90 }, - { STB0899_ERRCTRL1 , 0xb6 }, - { STB0899_ERRCTRL2 , 0x95 }, - { STB0899_ERRCTRL3 , 0x8d }, - { STB0899_DMONMSK1 , 0x27 }, - { STB0899_DMONMSK0 , 0x03 }, - { STB0899_DEMAPVIT , 0x5c }, - { STB0899_PLPARM , 0x19 }, - { STB0899_PDELCTRL , 0x48 }, - { STB0899_PDELCTRL2 , 0x00 }, - { STB0899_BBHCTRL1 , 0x00 }, - { STB0899_BBHCTRL2 , 0x00 }, - { STB0899_HYSTTHRESH , 0x77 }, - { STB0899_MATCSTM , 0x00 }, - { STB0899_MATCSTL , 0x00 }, - { STB0899_UPLCSTM , 0x00 }, - { STB0899_UPLCSTL , 0x00 }, - { STB0899_DFLCSTM , 0x00 }, - { STB0899_DFLCSTL , 0x00 }, - { STB0899_SYNCCST , 0x00 }, - { STB0899_SYNCDCSTM , 0x00 }, - { STB0899_SYNCDCSTL , 0x00 }, - { STB0899_ISI_ENTRY , 0x00 }, - { STB0899_ISI_BIT_EN , 0x00 }, - { STB0899_MATSTRM , 0xf0 }, - { STB0899_MATSTRL , 0x02 }, - { STB0899_UPLSTRM , 0x45 }, - { STB0899_UPLSTRL , 0x60 }, - { STB0899_DFLSTRM , 0xe3 }, - { STB0899_DFLSTRL , 0x00 }, - { STB0899_SYNCSTR , 0x47 }, - { STB0899_SYNCDSTRM , 0x05 }, - { STB0899_SYNCDSTRL , 0x18 }, - { STB0899_CFGPDELSTATUS1 , 0x19 }, - { STB0899_CFGPDELSTATUS2 , 0x2b }, - { STB0899_BBFERRORM , 0x00 }, - { STB0899_BBFERRORL , 0x01 }, - { STB0899_UPKTERRORM , 0x00 }, - { STB0899_UPKTERRORL , 0x00 }, - { 0xffff , 0xff }, -}; - -struct stb0899_config vp1041_stb0899_config = { - .init_dev = vp1041_stb0899_s1_init_1, - .init_s2_demod = stb0899_s2_init_2, - .init_s1_demod = vp1041_stb0899_s1_init_3, - .init_s2_fec = stb0899_s2_init_4, - .init_tst = stb0899_s1_init_5, - - .demod_address = 0x68, /* 0xd0 >> 1 */ - - .xtal_freq = 27000000, - .inversion = IQ_SWAP_ON, /* 1 */ - - .lo_clk = 76500000, - .hi_clk = 99000000, - - .esno_ave = STB0899_DVBS2_ESNO_AVE, - .esno_quant = STB0899_DVBS2_ESNO_QUANT, - .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, - .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, - .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, - .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, - .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, - .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, - .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, - - .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, - .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, - .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, - .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, - - .tuner_get_frequency = stb6100_get_frequency, - .tuner_set_frequency = stb6100_set_frequency, - .tuner_set_bandwidth = stb6100_set_bandwidth, - .tuner_get_bandwidth = stb6100_get_bandwidth, - .tuner_set_rfsiggain = NULL, -}; - -struct stb6100_config vp1041_stb6100_config = { - .tuner_address = 0x60, - .refclock = 27000000, -}; - -static int vp1041_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) -{ - struct i2c_adapter *adapter = &mantis->adapter; - - int err = 0; - - err = mantis_frontend_power(mantis, POWER_ON); - if (err == 0) { - mantis_frontend_soft_reset(mantis); - msleep(250); - mantis->fe = dvb_attach(stb0899_attach, &vp1041_stb0899_config, adapter); - if (mantis->fe) { - dprintk(MANTIS_ERROR, 1, - "found STB0899 DVB-S/DVB-S2 frontend @0x%02x", - vp1041_stb0899_config.demod_address); - - if (dvb_attach(stb6100_attach, mantis->fe, &vp1041_stb6100_config, adapter)) { - if (!dvb_attach(lnbp21_attach, mantis->fe, adapter, 0, 0)) - dprintk(MANTIS_ERROR, 1, "No LNBP21 found!"); - } - } else { - return -EREMOTEIO; - } - } else { - dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", - adapter->name, - err); - - return -EIO; - } - - - dprintk(MANTIS_ERROR, 1, "Done!"); - - return 0; -} - -struct mantis_hwconfig vp1041_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_188, - - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, - - .frontend_init = vp1041_frontend_init, - .power = GPIF_A12, - .reset = GPIF_A13, -}; diff --git a/drivers/media/dvb/mantis/mantis_vp1041.h b/drivers/media/dvb/mantis/mantis_vp1041.h deleted file mode 100644 index 1ae5b3de8081..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp1041.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Mantis VP-1041 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP1041_H -#define __MANTIS_VP1041_H - -#include "mantis_common.h" - -#define MANTIS_VP_1041_DVB_S2 0x0031 -#define SKYSTAR_HD2_10 0x0001 -#define SKYSTAR_HD2_20 0x0003 -#define CINERGY_S2_PCI_HD 0x1179 - -extern struct mantis_hwconfig vp1041_config; - -#endif /* __MANTIS_VP1041_H */ diff --git a/drivers/media/dvb/mantis/mantis_vp2033.c b/drivers/media/dvb/mantis/mantis_vp2033.c deleted file mode 100644 index 1ca6837fbe46..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp2033.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - Mantis VP-2033 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "tda1002x.h" -#include "mantis_common.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" -#include "mantis_vp2033.h" - -#define MANTIS_MODEL_NAME "VP-2033" -#define MANTIS_DEV_TYPE "DVB-C" - -struct tda1002x_config vp2033_tda1002x_cu1216_config = { - .demod_address = 0x18 >> 1, - .invert = 1, -}; - -struct tda10023_config vp2033_tda10023_cu1216_config = { - .demod_address = 0x18 >> 1, - .invert = 1, -}; - -static u8 read_pwm(struct mantis_pci *mantis) -{ - struct i2c_adapter *adapter = &mantis->adapter; - - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { - {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, - {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} - }; - - if ((i2c_transfer(adapter, msg, 2) != 2) - || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct mantis_pci *mantis = fe->dvb->priv; - struct i2c_adapter *adapter = &mantis->adapter; - - u8 buf[6]; - struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; - int i; - -#define CU1216_IF 36125000 -#define TUNER_MUL 62500 - - u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0xce; - buf[3] = (p->frequency < 150000000 ? 0x01 : - p->frequency < 445000000 ? 0x02 : 0x04); - buf[4] = 0xde; - buf[5] = 0x20; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(adapter, &msg, 1) != 1) - return -EIO; - - /* wait for the pll lock */ - msg.flags = I2C_M_RD; - msg.len = 1; - for (i = 0; i < 20; i++) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) - break; - - msleep(10); - } - - /* switch the charge pump to the lower current */ - msg.flags = 0; - msg.len = 2; - msg.buf = &buf[2]; - buf[2] &= ~0x40; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(adapter, &msg, 1) != 1) - return -EIO; - - return 0; -} - -static int vp2033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) -{ - struct i2c_adapter *adapter = &mantis->adapter; - - int err = 0; - - err = mantis_frontend_power(mantis, POWER_ON); - if (err == 0) { - mantis_frontend_soft_reset(mantis); - msleep(250); - - dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); - fe = dvb_attach(tda10021_attach, &vp2033_tda1002x_cu1216_config, - adapter, - read_pwm(mantis)); - - if (fe) { - dprintk(MANTIS_ERROR, 1, - "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", - vp2033_tda1002x_cu1216_config.demod_address); - } else { - fe = dvb_attach(tda10023_attach, &vp2033_tda10023_cu1216_config, - adapter, - read_pwm(mantis)); - - if (fe) { - dprintk(MANTIS_ERROR, 1, - "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", - vp2033_tda1002x_cu1216_config.demod_address); - } - } - - if (fe) { - fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; - dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); - } else { - return -1; - } - } else { - dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", - adapter->name, - err); - - return -EIO; - } - - mantis->fe = fe; - dprintk(MANTIS_DEBUG, 1, "Done!"); - - return 0; -} - -struct mantis_hwconfig vp2033_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_204, - - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, - - .frontend_init = vp2033_frontend_init, - .power = GPIF_A12, - .reset = GPIF_A13, -}; diff --git a/drivers/media/dvb/mantis/mantis_vp2033.h b/drivers/media/dvb/mantis/mantis_vp2033.h deleted file mode 100644 index c55242b79d54..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp2033.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Mantis VP-2033 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP2033_H -#define __MANTIS_VP2033_H - -#include "mantis_common.h" - -#define MANTIS_VP_2033_DVB_C 0x0008 - -extern struct mantis_hwconfig vp2033_config; - -#endif /* __MANTIS_VP2033_H */ diff --git a/drivers/media/dvb/mantis/mantis_vp2040.c b/drivers/media/dvb/mantis/mantis_vp2040.c deleted file mode 100644 index d480741afd78..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp2040.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - Mantis VP-2040 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "tda1002x.h" -#include "mantis_common.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" -#include "mantis_vp2040.h" - -#define MANTIS_MODEL_NAME "VP-2040" -#define MANTIS_DEV_TYPE "DVB-C" - -struct tda1002x_config vp2040_tda1002x_cu1216_config = { - .demod_address = 0x18 >> 1, - .invert = 1, -}; - -struct tda10023_config vp2040_tda10023_cu1216_config = { - .demod_address = 0x18 >> 1, - .invert = 1, -}; - -static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct mantis_pci *mantis = fe->dvb->priv; - struct i2c_adapter *adapter = &mantis->adapter; - - u8 buf[6]; - struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; - int i; - -#define CU1216_IF 36125000 -#define TUNER_MUL 62500 - - u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0xce; - buf[3] = (p->frequency < 150000000 ? 0x01 : - p->frequency < 445000000 ? 0x02 : 0x04); - buf[4] = 0xde; - buf[5] = 0x20; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(adapter, &msg, 1) != 1) - return -EIO; - - /* wait for the pll lock */ - msg.flags = I2C_M_RD; - msg.len = 1; - for (i = 0; i < 20; i++) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) - break; - - msleep(10); - } - - /* switch the charge pump to the lower current */ - msg.flags = 0; - msg.len = 2; - msg.buf = &buf[2]; - buf[2] &= ~0x40; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(adapter, &msg, 1) != 1) - return -EIO; - - return 0; -} - -static u8 read_pwm(struct mantis_pci *mantis) -{ - struct i2c_adapter *adapter = &mantis->adapter; - - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { - {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, - {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} - }; - - if ((i2c_transfer(adapter, msg, 2) != 2) - || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -static int vp2040_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) -{ - struct i2c_adapter *adapter = &mantis->adapter; - - int err = 0; - - err = mantis_frontend_power(mantis, POWER_ON); - if (err == 0) { - mantis_frontend_soft_reset(mantis); - msleep(250); - - dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); - fe = dvb_attach(tda10021_attach, &vp2040_tda1002x_cu1216_config, - adapter, - read_pwm(mantis)); - - if (fe) { - dprintk(MANTIS_ERROR, 1, - "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", - vp2040_tda1002x_cu1216_config.demod_address); - } else { - fe = dvb_attach(tda10023_attach, &vp2040_tda10023_cu1216_config, - adapter, - read_pwm(mantis)); - - if (fe) { - dprintk(MANTIS_ERROR, 1, - "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", - vp2040_tda1002x_cu1216_config.demod_address); - } - } - - if (fe) { - fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; - dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); - } else { - return -1; - } - } else { - dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", - adapter->name, - err); - - return -EIO; - } - mantis->fe = fe; - dprintk(MANTIS_DEBUG, 1, "Done!"); - - return 0; -} - -struct mantis_hwconfig vp2040_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_204, - - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, - - .frontend_init = vp2040_frontend_init, - .power = GPIF_A12, - .reset = GPIF_A13, -}; diff --git a/drivers/media/dvb/mantis/mantis_vp2040.h b/drivers/media/dvb/mantis/mantis_vp2040.h deleted file mode 100644 index d125e219b685..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp2040.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - Mantis VP-2040 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP2040_H -#define __MANTIS_VP2040_H - -#include "mantis_common.h" - -#define MANTIS_VP_2040_DVB_C 0x0043 -#define CINERGY_C 0x1178 -#define CABLESTAR_HD2 0x0002 - -extern struct mantis_hwconfig vp2040_config; - -#endif /* __MANTIS_VP2040_H */ diff --git a/drivers/media/dvb/mantis/mantis_vp3028.c b/drivers/media/dvb/mantis/mantis_vp3028.c deleted file mode 100644 index 4155c838a18a..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp3028.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - Mantis VP-3028 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "mantis_common.h" -#include "mantis_vp3028.h" - -struct zl10353_config mantis_vp3028_config = { - .demod_address = 0x0f, -}; - -#define MANTIS_MODEL_NAME "VP-3028" -#define MANTIS_DEV_TYPE "DVB-T" - -struct mantis_hwconfig vp3028_mantis_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_188, - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, -}; diff --git a/drivers/media/dvb/mantis/mantis_vp3028.h b/drivers/media/dvb/mantis/mantis_vp3028.h deleted file mode 100644 index b07be6adc522..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp3028.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Mantis VP-3028 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP3028_H -#define __MANTIS_VP3028_H - -#include "dvb_frontend.h" -#include "mantis_common.h" -#include "zl10353.h" - -#define MANTIS_VP_3028_DVB_T 0x0028 - -extern struct zl10353_config mantis_vp3028_config; -extern struct mantis_hwconfig vp3028_mantis_config; - -#endif /* __MANTIS_VP3028_H */ diff --git a/drivers/media/dvb/mantis/mantis_vp3030.c b/drivers/media/dvb/mantis/mantis_vp3030.c deleted file mode 100644 index c09308cd3ac6..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp3030.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - Mantis VP-3030 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" - -#include "zl10353.h" -#include "tda665x.h" -#include "mantis_common.h" -#include "mantis_ioc.h" -#include "mantis_dvb.h" -#include "mantis_vp3030.h" - -struct zl10353_config mantis_vp3030_config = { - .demod_address = 0x0f, -}; - -struct tda665x_config env57h12d5_config = { - .name = "ENV57H12D5 (ET-50DT)", - .addr = 0x60, - .frequency_min = 47000000, - .frequency_max = 862000000, - .frequency_offst = 3616667, - .ref_multiplier = 6, /* 1/6 MHz */ - .ref_divider = 100000, /* 1/6 MHz */ -}; - -#define MANTIS_MODEL_NAME "VP-3030" -#define MANTIS_DEV_TYPE "DVB-T" - - -static int vp3030_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) -{ - struct i2c_adapter *adapter = &mantis->adapter; - struct mantis_hwconfig *config = mantis->hwconfig; - int err = 0; - - mantis_gpio_set_bits(mantis, config->reset, 0); - msleep(100); - err = mantis_frontend_power(mantis, POWER_ON); - msleep(100); - mantis_gpio_set_bits(mantis, config->reset, 1); - - if (err == 0) { - msleep(250); - dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); - fe = dvb_attach(zl10353_attach, &mantis_vp3030_config, adapter); - - if (!fe) - return -1; - - dvb_attach(tda665x_attach, fe, &env57h12d5_config, adapter); - } else { - dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", - adapter->name, - err); - - return -EIO; - - } - mantis->fe = fe; - dprintk(MANTIS_ERROR, 1, "Done!"); - - return 0; -} - -struct mantis_hwconfig vp3030_config = { - .model_name = MANTIS_MODEL_NAME, - .dev_type = MANTIS_DEV_TYPE, - .ts_size = MANTIS_TS_188, - - .baud_rate = MANTIS_BAUD_9600, - .parity = MANTIS_PARITY_NONE, - .bytes = 0, - - .frontend_init = vp3030_frontend_init, - .power = GPIF_A12, - .reset = GPIF_A13, - - .i2c_mode = MANTIS_BYTE_MODE -}; diff --git a/drivers/media/dvb/mantis/mantis_vp3030.h b/drivers/media/dvb/mantis/mantis_vp3030.h deleted file mode 100644 index 5f12c4266277..000000000000 --- a/drivers/media/dvb/mantis/mantis_vp3030.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Mantis VP-3030 driver - - Copyright (C) Manu Abraham (abraham.manu@gmail.com) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __MANTIS_VP3030_H -#define __MANTIS_VP3030_H - -#include "mantis_common.h" - -#define MANTIS_VP_3030_DVB_T 0x0024 - -extern struct mantis_hwconfig vp3030_config; - -#endif /* __MANTIS_VP3030_H */ diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig deleted file mode 100644 index 64c84702ba5c..000000000000 --- a/drivers/media/dvb/ngene/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config DVB_NGENE - tristate "Micronas nGene support" - depends on DVB_CORE && PCI && I2C - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE - ---help--- - Support for Micronas PCI express cards with nGene bridge. - diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile deleted file mode 100644 index 63997089f9d1..000000000000 --- a/drivers/media/dvb/ngene/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# Makefile for the nGene device driver -# - -ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o - -obj-$(CONFIG_DVB_NGENE) += ngene.o - -ccflags-y += -Idrivers/media/dvb-core/ -ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ - -# For the staging CI driver cxd2099 -ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c deleted file mode 100644 index a6cd6959ad19..000000000000 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ /dev/null @@ -1,823 +0,0 @@ -/* - * ngene-cards.c: nGene PCIe bridge driver - card specific info - * - * Copyright (C) 2005-2007 Micronas - * - * Copyright (C) 2008-2009 Ralph Metzler - * Modifications for new nGene firmware, - * support for EEPROM-copying, - * support for new dual DVB-S2 card prototype - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include - -#include "ngene.h" - -/* demods/tuners */ -#include "stv6110x.h" -#include "stv090x.h" -#include "lnbh24.h" -#include "lgdt330x.h" -#include "mt2131.h" -#include "tda18271c2dd.h" -#include "drxk.h" -#include "drxd.h" -#include "dvb-pll.h" - - -/****************************************************************************/ -/* Demod/tuner attachment ***************************************************/ -/****************************************************************************/ - -static int tuner_attach_stv6110(struct ngene_channel *chan) -{ - struct i2c_adapter *i2c; - struct stv090x_config *feconf = (struct stv090x_config *) - chan->dev->card_info->fe_config[chan->number]; - struct stv6110x_config *tunerconf = (struct stv6110x_config *) - chan->dev->card_info->tuner_config[chan->number]; - struct stv6110x_devctl *ctl; - - /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ - if (chan->number < 2) - i2c = &chan->dev->channel[0].i2c_adapter; - else - i2c = &chan->dev->channel[1].i2c_adapter; - - ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c); - if (ctl == NULL) { - printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); - return -ENODEV; - } - - feconf->tuner_init = ctl->tuner_init; - feconf->tuner_sleep = ctl->tuner_sleep; - feconf->tuner_set_mode = ctl->tuner_set_mode; - feconf->tuner_set_frequency = ctl->tuner_set_frequency; - feconf->tuner_get_frequency = ctl->tuner_get_frequency; - feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; - feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; - feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; - feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; - feconf->tuner_set_refclk = ctl->tuner_set_refclk; - feconf->tuner_get_status = ctl->tuner_get_status; - - return 0; -} - - -static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct ngene_channel *chan = fe->sec_priv; - int status; - - if (enable) { - down(&chan->dev->pll_mutex); - status = chan->gate_ctrl(fe, 1); - } else { - status = chan->gate_ctrl(fe, 0); - up(&chan->dev->pll_mutex); - } - return status; -} - -static int tuner_attach_tda18271(struct ngene_channel *chan) -{ - struct i2c_adapter *i2c; - struct dvb_frontend *fe; - - i2c = &chan->dev->channel[0].i2c_adapter; - if (chan->fe->ops.i2c_gate_ctrl) - chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); - fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60); - if (chan->fe->ops.i2c_gate_ctrl) - chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); - if (!fe) { - printk(KERN_ERR "No TDA18271 found!\n"); - return -ENODEV; - } - - return 0; -} - -static int tuner_attach_probe(struct ngene_channel *chan) -{ - if (chan->demod_type == 0) - return tuner_attach_stv6110(chan); - if (chan->demod_type == 1) - return tuner_attach_tda18271(chan); - return -EINVAL; -} - -static int demod_attach_stv0900(struct ngene_channel *chan) -{ - struct i2c_adapter *i2c; - struct stv090x_config *feconf = (struct stv090x_config *) - chan->dev->card_info->fe_config[chan->number]; - - /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ - /* Note: Both adapters share the same i2c bus, but the demod */ - /* driver requires that each demod has its own i2c adapter */ - if (chan->number < 2) - i2c = &chan->dev->channel[0].i2c_adapter; - else - i2c = &chan->dev->channel[1].i2c_adapter; - - chan->fe = dvb_attach(stv090x_attach, feconf, i2c, - (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0 - : STV090x_DEMODULATOR_1); - if (chan->fe == NULL) { - printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); - return -ENODEV; - } - - /* store channel info */ - if (feconf->tuner_i2c_lock) - chan->fe->analog_demod_priv = chan; - - if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0, - 0, chan->dev->card_info->lnb[chan->number])) { - printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); - dvb_frontend_detach(chan->fe); - chan->fe = NULL; - return -ENODEV; - } - - return 0; -} - -static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock) -{ - struct ngene_channel *chan = fe->analog_demod_priv; - - if (lock) - down(&chan->dev->pll_mutex); - else - up(&chan->dev->pll_mutex); -} - -static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) -{ - struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1 } }; - return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; -} - -static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, - u16 reg, u8 *val) -{ - u8 msg[2] = {reg>>8, reg&0xff}; - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = msg, .len = 2}, - {.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1} }; - return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; -} - -static int port_has_stv0900(struct i2c_adapter *i2c, int port) -{ - u8 val; - if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0) - return 0; - return 1; -} - -static int port_has_drxk(struct i2c_adapter *i2c, int port) -{ - u8 val; - - if (i2c_read(i2c, 0x29+port, &val) < 0) - return 0; - return 1; -} - -static int demod_attach_drxk(struct ngene_channel *chan, - struct i2c_adapter *i2c) -{ - struct drxk_config config; - - memset(&config, 0, sizeof(config)); - config.microcode_name = "drxk_a3.mc"; - config.qam_demod_parameter_count = 4; - config.adr = 0x29 + (chan->number ^ 2); - - chan->fe = dvb_attach(drxk_attach, &config, i2c); - if (!chan->fe) { - printk(KERN_ERR "No DRXK found!\n"); - return -ENODEV; - } - chan->fe->sec_priv = chan; - chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl; - chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; - return 0; -} - -static int cineS2_probe(struct ngene_channel *chan) -{ - struct i2c_adapter *i2c; - struct stv090x_config *fe_conf; - u8 buf[3]; - struct i2c_msg i2c_msg = { .flags = 0, .buf = buf }; - int rc; - - /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ - if (chan->number < 2) - i2c = &chan->dev->channel[0].i2c_adapter; - else - i2c = &chan->dev->channel[1].i2c_adapter; - - if (port_has_stv0900(i2c, chan->number)) { - chan->demod_type = 0; - fe_conf = chan->dev->card_info->fe_config[chan->number]; - /* demod found, attach it */ - rc = demod_attach_stv0900(chan); - if (rc < 0 || chan->number < 2) - return rc; - - /* demod #2: reprogram outputs DPN1 & DPN2 */ - i2c_msg.addr = fe_conf->address; - i2c_msg.len = 3; - buf[0] = 0xf1; - switch (chan->number) { - case 2: - buf[1] = 0x5c; - buf[2] = 0xc2; - break; - case 3: - buf[1] = 0x61; - buf[2] = 0xcc; - break; - default: - return -ENODEV; - } - rc = i2c_transfer(i2c, &i2c_msg, 1); - if (rc != 1) { - printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n"); - return -EIO; - } - } else if (port_has_drxk(i2c, chan->number^2)) { - chan->demod_type = 1; - demod_attach_drxk(chan, i2c); - } else { - printk(KERN_ERR "No demod found on chan %d\n", chan->number); - return -ENODEV; - } - return 0; -} - - -static struct lgdt330x_config aver_m780 = { - .demod_address = 0xb2 >> 1, - .demod_chip = LGDT3303, - .serial_mpeg = 0x00, /* PARALLEL */ - .clock_polarity_flip = 1, -}; - -static struct mt2131_config m780_tunerconfig = { - 0xc0 >> 1 -}; - -/* A single func to attach the demo and tuner, rather than - * use two sep funcs like the current design mandates. - */ -static int demod_attach_lg330x(struct ngene_channel *chan) -{ - chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter); - if (chan->fe == NULL) { - printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n"); - return -ENODEV; - } - - dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter, - &m780_tunerconfig, 0); - - return (chan->fe) ? 0 : -ENODEV; -} - -static int demod_attach_drxd(struct ngene_channel *chan) -{ - struct drxd_config *feconf; - - feconf = chan->dev->card_info->fe_config[chan->number]; - - chan->fe = dvb_attach(drxd_attach, feconf, chan, - &chan->i2c_adapter, &chan->dev->pci_dev->dev); - if (!chan->fe) { - pr_err("No DRXD found!\n"); - return -ENODEV; - } - - if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, - &chan->i2c_adapter, - feconf->pll_type)) { - pr_err("No pll(%d) found!\n", feconf->pll_type); - return -ENODEV; - } - return 0; -} - -/****************************************************************************/ -/* EEPROM TAGS **************************************************************/ -/****************************************************************************/ - -#define MICNG_EE_START 0x0100 -#define MICNG_EE_END 0x0FF0 - -#define MICNG_EETAG_END0 0x0000 -#define MICNG_EETAG_END1 0xFFFF - -/* 0x0001 - 0x000F reserved for housekeeping */ -/* 0xFFFF - 0xFFFE reserved for housekeeping */ - -/* Micronas assigned tags - EEProm tags for hardware support */ - -#define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */ -#define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */ - -#define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */ -#define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */ - -/* Tag range for OEMs */ - -#define MICNG_EETAG_OEM_FIRST 0xC000 -#define MICNG_EETAG_OEM_LAST 0xFFEF - -static int i2c_write_eeprom(struct i2c_adapter *adapter, - u8 adr, u16 reg, u8 data) -{ - u8 m[3] = {(reg >> 8), (reg & 0xff), data}; - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, - .len = sizeof(m)}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - pr_err(DEVICE_NAME ": Error writing EEPROM!\n"); - return -EIO; - } - return 0; -} - -static int i2c_read_eeprom(struct i2c_adapter *adapter, - u8 adr, u16 reg, u8 *data, int len) -{ - u8 msg[2] = {(reg >> 8), (reg & 0xff)}; - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = msg, .len = 2 }, - {.addr = adr, .flags = I2C_M_RD, - .buf = data, .len = len} }; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - pr_err(DEVICE_NAME ": Error reading EEPROM\n"); - return -EIO; - } - return 0; -} - -static int ReadEEProm(struct i2c_adapter *adapter, - u16 Tag, u32 MaxLen, u8 *data, u32 *pLength) -{ - int status = 0; - u16 Addr = MICNG_EE_START, Length, tag = 0; - u8 EETag[3]; - - while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { - if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) - return -1; - tag = (EETag[0] << 8) | EETag[1]; - if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) - return -1; - if (tag == Tag) - break; - Addr += sizeof(u16) + 1 + EETag[2]; - } - if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { - pr_err(DEVICE_NAME - ": Reached EOEE @ Tag = %04x Length = %3d\n", - tag, EETag[2]); - return -1; - } - Length = EETag[2]; - if (Length > MaxLen) - Length = (u16) MaxLen; - if (Length > 0) { - Addr += sizeof(u16) + 1; - status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length); - if (!status) { - *pLength = EETag[2]; - if (Length < EETag[2]) - ; /*status=STATUS_BUFFER_OVERFLOW; */ - } - } - return status; -} - -static int WriteEEProm(struct i2c_adapter *adapter, - u16 Tag, u32 Length, u8 *data) -{ - int status = 0; - u16 Addr = MICNG_EE_START; - u8 EETag[3]; - u16 tag = 0; - int retry, i; - - while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { - if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) - return -1; - tag = (EETag[0] << 8) | EETag[1]; - if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) - return -1; - if (tag == Tag) - break; - Addr += sizeof(u16) + 1 + EETag[2]; - } - if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { - pr_err(DEVICE_NAME - ": Reached EOEE @ Tag = %04x Length = %3d\n", - tag, EETag[2]); - return -1; - } - - if (Length > EETag[2]) - return -EINVAL; - /* Note: We write the data one byte at a time to avoid - issues with page sizes. (which are different for - each manufacture and eeprom size) - */ - Addr += sizeof(u16) + 1; - for (i = 0; i < Length; i++, Addr++) { - status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]); - - if (status) - break; - - /* Poll for finishing write cycle */ - retry = 10; - while (retry) { - u8 Tmp; - - msleep(50); - status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1); - if (status) - break; - if (Tmp != data[i]) - pr_err(DEVICE_NAME - "eeprom write error\n"); - retry -= 1; - } - if (status) { - pr_err(DEVICE_NAME - ": Timeout polling eeprom\n"); - break; - } - } - return status; -} - -static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data) -{ - int stat; - u8 buf[2]; - u32 len = 0; - - stat = ReadEEProm(adapter, tag, 2, buf, &len); - if (stat) - return stat; - if (len != 2) - return -EINVAL; - - *data = (buf[0] << 8) | buf[1]; - return 0; -} - -static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data) -{ - int stat; - u8 buf[2]; - - buf[0] = data >> 8; - buf[1] = data & 0xff; - stat = WriteEEProm(adapter, tag, 2, buf); - if (stat) - return stat; - return 0; -} - -static s16 osc_deviation(void *priv, s16 deviation, int flag) -{ - struct ngene_channel *chan = priv; - struct i2c_adapter *adap = &chan->i2c_adapter; - u16 data = 0; - - if (flag) { - data = (u16) deviation; - pr_info(DEVICE_NAME ": write deviation %d\n", - deviation); - eeprom_write_ushort(adap, 0x1000 + chan->number, data); - } else { - if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data)) - data = 0; - pr_info(DEVICE_NAME ": read deviation %d\n", - (s16) data); - } - - return (s16) data; -} - -/****************************************************************************/ -/* Switch control (I2C gates, etc.) *****************************************/ -/****************************************************************************/ - - -static struct stv090x_config fe_cineS2 = { - .device = STV0900, - .demod_mode = STV090x_DUAL, - .clk_mode = STV090x_CLK_EXT, - - .xtal = 27000000, - .address = 0x68, - - .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - - .repeater_level = STV090x_RPTLEVEL_16, - - .adc1_range = STV090x_ADC_1Vpp, - .adc2_range = STV090x_ADC_1Vpp, - - .diseqc_envelope_mode = true, - - .tuner_i2c_lock = cineS2_tuner_i2c_lock, -}; - -static struct stv090x_config fe_cineS2_2 = { - .device = STV0900, - .demod_mode = STV090x_DUAL, - .clk_mode = STV090x_CLK_EXT, - - .xtal = 27000000, - .address = 0x69, - - .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - - .repeater_level = STV090x_RPTLEVEL_16, - - .adc1_range = STV090x_ADC_1Vpp, - .adc2_range = STV090x_ADC_1Vpp, - - .diseqc_envelope_mode = true, - - .tuner_i2c_lock = cineS2_tuner_i2c_lock, -}; - -static struct stv6110x_config tuner_cineS2_0 = { - .addr = 0x60, - .refclk = 27000000, - .clk_div = 1, -}; - -static struct stv6110x_config tuner_cineS2_1 = { - .addr = 0x63, - .refclk = 27000000, - .clk_div = 1, -}; - -static struct ngene_info ngene_info_cineS2 = { - .type = NGENE_SIDEWINDER, - .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, - .fe_config = {&fe_cineS2, &fe_cineS2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0b, 0x08}, - .tsf = {3, 3}, - .fw_version = 18, - .msi_supported = true, -}; - -static struct ngene_info ngene_info_satixS2 = { - .type = NGENE_SIDEWINDER, - .name = "Mystique SaTiX-S2 Dual", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, - .fe_config = {&fe_cineS2, &fe_cineS2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0b, 0x08}, - .tsf = {3, 3}, - .fw_version = 18, - .msi_supported = true, -}; - -static struct ngene_info ngene_info_satixS2v2 = { - .type = NGENE_SIDEWINDER, - .name = "Mystique SaTiX-S2 Dual (v2)", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, - NGENE_IO_TSOUT}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, - .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0a, 0x08, 0x0b, 0x09}, - .tsf = {3, 3}, - .fw_version = 18, - .msi_supported = true, -}; - -static struct ngene_info ngene_info_cineS2v5 = { - .type = NGENE_SIDEWINDER, - .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, - NGENE_IO_TSOUT}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, - .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0a, 0x08, 0x0b, 0x09}, - .tsf = {3, 3}, - .fw_version = 18, - .msi_supported = true, -}; - - -static struct ngene_info ngene_info_duoFlex = { - .type = NGENE_SIDEWINDER, - .name = "Digital Devices DuoFlex PCIe or miniPCIe", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, - NGENE_IO_TSOUT}, - .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe}, - .tuner_attach = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe}, - .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0a, 0x08, 0x0b, 0x09}, - .tsf = {3, 3}, - .fw_version = 18, - .msi_supported = true, -}; - -static struct ngene_info ngene_info_m780 = { - .type = NGENE_APP, - .name = "Aver M780 ATSC/QAM-B", - - /* Channel 0 is analog, which is currently unsupported */ - .io_type = { NGENE_IO_NONE, NGENE_IO_TSIN }, - .demod_attach = { NULL, demod_attach_lg330x }, - - /* Ensure these are NULL else the frame will call them (as funcs) */ - .tuner_attach = { 0, 0, 0, 0 }, - .fe_config = { NULL, &aver_m780 }, - .avf = { 0 }, - - /* A custom electrical interface config for the demod to bridge */ - .tsf = { 4, 4 }, - .fw_version = 15, -}; - -static struct drxd_config fe_terratec_dvbt_0 = { - .index = 0, - .demod_address = 0x70, - .demod_revision = 0xa2, - .demoda_address = 0x00, - .pll_address = 0x60, - .pll_type = DVB_PLL_THOMSON_DTT7520X, - .clock = 20000, - .osc_deviation = osc_deviation, -}; - -static struct drxd_config fe_terratec_dvbt_1 = { - .index = 1, - .demod_address = 0x71, - .demod_revision = 0xa2, - .demoda_address = 0x00, - .pll_address = 0x60, - .pll_type = DVB_PLL_THOMSON_DTT7520X, - .clock = 20000, - .osc_deviation = osc_deviation, -}; - -static struct ngene_info ngene_info_terratec = { - .type = NGENE_TERRATEC, - .name = "Terratec Integra/Cinergy2400i Dual DVB-T", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_drxd, demod_attach_drxd}, - .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, - .i2c_access = 1, -}; - -/****************************************************************************/ - - - -/****************************************************************************/ -/* PCI Subsystem ID *********************************************************/ -/****************************************************************************/ - -#define NGENE_ID(_subvend, _subdev, _driverdata) { \ - .vendor = NGENE_VID, .device = NGENE_PID, \ - .subvendor = _subvend, .subdevice = _subdev, \ - .driver_data = (unsigned long) &_driverdata } - -/****************************************************************************/ - -static const struct pci_device_id ngene_id_tbl[] __devinitdata = { - NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), - NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), - NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2), - NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2), - NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5), - NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex), - NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex), - NGENE_ID(0x1461, 0x062e, ngene_info_m780), - NGENE_ID(0x153b, 0x1167, ngene_info_terratec), - {0} -}; -MODULE_DEVICE_TABLE(pci, ngene_id_tbl); - -/****************************************************************************/ -/* Init/Exit ****************************************************************/ -/****************************************************************************/ - -static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, - enum pci_channel_state state) -{ - printk(KERN_ERR DEVICE_NAME ": PCI error\n"); - if (state == pci_channel_io_perm_failure) - return PCI_ERS_RESULT_DISCONNECT; - if (state == pci_channel_io_frozen) - return PCI_ERS_RESULT_NEED_RESET; - return PCI_ERS_RESULT_CAN_RECOVER; -} - -static pci_ers_result_t ngene_link_reset(struct pci_dev *dev) -{ - printk(KERN_INFO DEVICE_NAME ": link reset\n"); - return 0; -} - -static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) -{ - printk(KERN_INFO DEVICE_NAME ": slot reset\n"); - return 0; -} - -static void ngene_resume(struct pci_dev *dev) -{ - printk(KERN_INFO DEVICE_NAME ": resume\n"); -} - -static struct pci_error_handlers ngene_errors = { - .error_detected = ngene_error_detected, - .link_reset = ngene_link_reset, - .slot_reset = ngene_slot_reset, - .resume = ngene_resume, -}; - -static struct pci_driver ngene_pci_driver = { - .name = "ngene", - .id_table = ngene_id_tbl, - .probe = ngene_probe, - .remove = __devexit_p(ngene_remove), - .err_handler = &ngene_errors, - .shutdown = ngene_shutdown, -}; - -static __init int module_init_ngene(void) -{ - printk(KERN_INFO - "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); - return pci_register_driver(&ngene_pci_driver); -} - -static __exit void module_exit_ngene(void) -{ - pci_unregister_driver(&ngene_pci_driver); -} - -module_init(module_init_ngene); -module_exit(module_exit_ngene); - -MODULE_DESCRIPTION("nGene"); -MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c deleted file mode 100644 index c8e0d5b99d4c..000000000000 --- a/drivers/media/dvb/ngene/ngene-core.c +++ /dev/null @@ -1,1707 +0,0 @@ -/* - * ngene.c: nGene PCIe bridge driver - * - * Copyright (C) 2005-2007 Micronas - * - * Copyright (C) 2008-2009 Ralph Metzler - * Modifications for new nGene firmware, - * support for EEPROM-copying, - * support for new dual DVB-S2 card prototype - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ngene.h" - -static int one_adapter; -module_param(one_adapter, int, 0444); -MODULE_PARM_DESC(one_adapter, "Use only one adapter."); - -static int shutdown_workaround; -module_param(shutdown_workaround, int, 0644); -MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets."); - -static int debug; -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "Print debugging information."); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define dprintk if (debug) printk - -#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) -#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) -#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) -#define ngreadl(adr) readl(dev->iomem + (adr)) -#define ngreadb(adr) readb(dev->iomem + (adr)) -#define ngcpyto(adr, src, count) memcpy_toio((char *) \ - (dev->iomem + (adr)), (src), (count)) -#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ - (dev->iomem + (adr)), (count)) - -/****************************************************************************/ -/* nGene interrupt handler **************************************************/ -/****************************************************************************/ - -static void event_tasklet(unsigned long data) -{ - struct ngene *dev = (struct ngene *)data; - - while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { - struct EVENT_BUFFER Event = - dev->EventQueue[dev->EventQueueReadIndex]; - dev->EventQueueReadIndex = - (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1); - - if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify)) - dev->TxEventNotify(dev, Event.TimeStamp); - if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify)) - dev->RxEventNotify(dev, Event.TimeStamp, - Event.RXCharacter); - } -} - -static void demux_tasklet(unsigned long data) -{ - struct ngene_channel *chan = (struct ngene_channel *)data; - struct SBufferHeader *Cur = chan->nextBuffer; - - spin_lock_irq(&chan->state_lock); - - while (Cur->ngeneBuffer.SR.Flags & 0x80) { - if (chan->mode & NGENE_IO_TSOUT) { - u32 Flags = chan->DataFormatFlags; - if (Cur->ngeneBuffer.SR.Flags & 0x20) - Flags |= BEF_OVERFLOW; - if (chan->pBufferExchange) { - if (!chan->pBufferExchange(chan, - Cur->Buffer1, - chan->Capture1Length, - Cur->ngeneBuffer.SR. - Clock, Flags)) { - /* - We didn't get data - Clear in service flag to make sure we - get called on next interrupt again. - leave fill/empty (0x80) flag alone - to avoid hardware running out of - buffers during startup, we hold only - in run state ( the source may be late - delivering data ) - */ - - if (chan->HWState == HWSTATE_RUN) { - Cur->ngeneBuffer.SR.Flags &= - ~0x40; - break; - /* Stop processing stream */ - } - } else { - /* We got a valid buffer, - so switch to run state */ - chan->HWState = HWSTATE_RUN; - } - } else { - printk(KERN_ERR DEVICE_NAME ": OOPS\n"); - if (chan->HWState == HWSTATE_RUN) { - Cur->ngeneBuffer.SR.Flags &= ~0x40; - break; /* Stop processing stream */ - } - } - if (chan->AudioDTOUpdated) { - printk(KERN_INFO DEVICE_NAME - ": Update AudioDTO = %d\n", - chan->AudioDTOValue); - Cur->ngeneBuffer.SR.DTOUpdate = - chan->AudioDTOValue; - chan->AudioDTOUpdated = 0; - } - } else { - if (chan->HWState == HWSTATE_RUN) { - u32 Flags = chan->DataFormatFlags; - IBufferExchange *exch1 = chan->pBufferExchange; - IBufferExchange *exch2 = chan->pBufferExchange2; - if (Cur->ngeneBuffer.SR.Flags & 0x01) - Flags |= BEF_EVEN_FIELD; - if (Cur->ngeneBuffer.SR.Flags & 0x20) - Flags |= BEF_OVERFLOW; - spin_unlock_irq(&chan->state_lock); - if (exch1) - exch1(chan, Cur->Buffer1, - chan->Capture1Length, - Cur->ngeneBuffer.SR.Clock, - Flags); - if (exch2) - exch2(chan, Cur->Buffer2, - chan->Capture2Length, - Cur->ngeneBuffer.SR.Clock, - Flags); - spin_lock_irq(&chan->state_lock); - } else if (chan->HWState != HWSTATE_STOP) - chan->HWState = HWSTATE_RUN; - } - Cur->ngeneBuffer.SR.Flags = 0x00; - Cur = Cur->Next; - } - chan->nextBuffer = Cur; - - spin_unlock_irq(&chan->state_lock); -} - -static irqreturn_t irq_handler(int irq, void *dev_id) -{ - struct ngene *dev = (struct ngene *)dev_id; - u32 icounts = 0; - irqreturn_t rc = IRQ_NONE; - u32 i = MAX_STREAM; - u8 *tmpCmdDoneByte; - - if (dev->BootFirmware) { - icounts = ngreadl(NGENE_INT_COUNTS); - if (icounts != dev->icounts) { - ngwritel(0, FORCE_NMI); - dev->cmd_done = 1; - wake_up(&dev->cmd_wq); - dev->icounts = icounts; - rc = IRQ_HANDLED; - } - return rc; - } - - ngwritel(0, FORCE_NMI); - - spin_lock(&dev->cmd_lock); - tmpCmdDoneByte = dev->CmdDoneByte; - if (tmpCmdDoneByte && - (*tmpCmdDoneByte || - (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) { - dev->CmdDoneByte = NULL; - dev->cmd_done = 1; - wake_up(&dev->cmd_wq); - rc = IRQ_HANDLED; - } - spin_unlock(&dev->cmd_lock); - - if (dev->EventBuffer->EventStatus & 0x80) { - u8 nextWriteIndex = - (dev->EventQueueWriteIndex + 1) & - (EVENT_QUEUE_SIZE - 1); - if (nextWriteIndex != dev->EventQueueReadIndex) { - dev->EventQueue[dev->EventQueueWriteIndex] = - *(dev->EventBuffer); - dev->EventQueueWriteIndex = nextWriteIndex; - } else { - printk(KERN_ERR DEVICE_NAME ": event overflow\n"); - dev->EventQueueOverflowCount += 1; - dev->EventQueueOverflowFlag = 1; - } - dev->EventBuffer->EventStatus &= ~0x80; - tasklet_schedule(&dev->event_tasklet); - rc = IRQ_HANDLED; - } - - while (i > 0) { - i--; - spin_lock(&dev->channel[i].state_lock); - /* if (dev->channel[i].State>=KSSTATE_RUN) { */ - if (dev->channel[i].nextBuffer) { - if ((dev->channel[i].nextBuffer-> - ngeneBuffer.SR.Flags & 0xC0) == 0x80) { - dev->channel[i].nextBuffer-> - ngeneBuffer.SR.Flags |= 0x40; - tasklet_schedule( - &dev->channel[i].demux_tasklet); - rc = IRQ_HANDLED; - } - } - spin_unlock(&dev->channel[i].state_lock); - } - - /* Request might have been processed by a previous call. */ - return IRQ_HANDLED; -} - -/****************************************************************************/ -/* nGene command interface **************************************************/ -/****************************************************************************/ - -static void dump_command_io(struct ngene *dev) -{ - u8 buf[8], *b; - - ngcpyfrom(buf, HOST_TO_NGENE, 8); - printk(KERN_ERR "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf); - - ngcpyfrom(buf, NGENE_TO_HOST, 8); - printk(KERN_ERR "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf); - - b = dev->hosttongene; - printk(KERN_ERR "dev->hosttongene (%p): %*ph\n", b, 8, b); - - b = dev->ngenetohost; - printk(KERN_ERR "dev->ngenetohost (%p): %*ph\n", b, 8, b); -} - -static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) -{ - int ret; - u8 *tmpCmdDoneByte; - - dev->cmd_done = 0; - - if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) { - dev->BootFirmware = 1; - dev->icounts = ngreadl(NGENE_INT_COUNTS); - ngwritel(0, NGENE_COMMAND); - ngwritel(0, NGENE_COMMAND_HI); - ngwritel(0, NGENE_STATUS); - ngwritel(0, NGENE_STATUS_HI); - ngwritel(0, NGENE_EVENT); - ngwritel(0, NGENE_EVENT_HI); - } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) { - u64 fwio = dev->PAFWInterfaceBuffer; - - ngwritel(fwio & 0xffffffff, NGENE_COMMAND); - ngwritel(fwio >> 32, NGENE_COMMAND_HI); - ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS); - ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI); - ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT); - ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI); - } - - memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2); - - if (dev->BootFirmware) - ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2); - - spin_lock_irq(&dev->cmd_lock); - tmpCmdDoneByte = dev->ngenetohost + com->out_len; - if (!com->out_len) - tmpCmdDoneByte++; - *tmpCmdDoneByte = 0; - dev->ngenetohost[0] = 0; - dev->ngenetohost[1] = 0; - dev->CmdDoneByte = tmpCmdDoneByte; - spin_unlock_irq(&dev->cmd_lock); - - /* Notify 8051. */ - ngwritel(1, FORCE_INT); - - ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ); - if (!ret) { - /*ngwritel(0, FORCE_NMI);*/ - - printk(KERN_ERR DEVICE_NAME - ": Command timeout cmd=%02x prev=%02x\n", - com->cmd.hdr.Opcode, dev->prev_cmd); - dump_command_io(dev); - return -1; - } - if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) - dev->BootFirmware = 0; - - dev->prev_cmd = com->cmd.hdr.Opcode; - - if (!com->out_len) - return 0; - - memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len); - - return 0; -} - -int ngene_command(struct ngene *dev, struct ngene_command *com) -{ - int result; - - down(&dev->cmd_mutex); - result = ngene_command_mutex(dev, com); - up(&dev->cmd_mutex); - return result; -} - - -static int ngene_command_load_firmware(struct ngene *dev, - u8 *ngene_fw, u32 size) -{ -#define FIRSTCHUNK (1024) - u32 cleft; - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE; - com.cmd.hdr.Length = 0; - com.in_len = 0; - com.out_len = 0; - - ngene_command(dev, &com); - - cleft = (size + 3) & ~3; - if (cleft > FIRSTCHUNK) { - ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK, - cleft - FIRSTCHUNK); - cleft = FIRSTCHUNK; - } - ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft); - - memset(&com, 0, sizeof(struct ngene_command)); - com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH; - com.cmd.hdr.Length = 4; - com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA; - com.cmd.FWLoadFinish.Length = (unsigned short)cleft; - com.in_len = 4; - com.out_len = 0; - - return ngene_command(dev, &com); -} - - -static int ngene_command_config_buf(struct ngene *dev, u8 config) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER; - com.cmd.hdr.Length = 1; - com.cmd.ConfigureBuffers.config = config; - com.in_len = 1; - com.out_len = 0; - - if (ngene_command(dev, &com) < 0) - return -EIO; - return 0; -} - -static int ngene_command_config_free_buf(struct ngene *dev, u8 *config) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER; - com.cmd.hdr.Length = 6; - memcpy(&com.cmd.ConfigureBuffers.config, config, 6); - com.in_len = 6; - com.out_len = 0; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - return 0; -} - -int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN; - com.cmd.hdr.Length = 1; - com.cmd.SetGpioPin.select = select | (level << 7); - com.in_len = 1; - com.out_len = 0; - - return ngene_command(dev, &com); -} - - -/* - 02000640 is sample on rising edge. - 02000740 is sample on falling edge. - 02000040 is ignore "valid" signal - - 0: FD_CTL1 Bit 7,6 must be 0,1 - 7 disable(fw controlled) - 6 0-AUX,1-TS - 5 0-par,1-ser - 4 0-lsb/1-msb - 3,2 reserved - 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both - 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge - 2: FD_STA is read-only. 0-sync - 3: FD_INSYNC is number of 47s to trigger "in sync". - 4: FD_OUTSYNC is number of 47s to trigger "out of sync". - 5: FD_MAXBYTE1 is low-order of bytes per packet. - 6: FD_MAXBYTE2 is high-order of bytes per packet. - 7: Top byte is unused. -*/ - -/****************************************************************************/ - -static u8 TSFeatureDecoderSetup[8 * 5] = { - 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, - 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */ - 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */ - 0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */ - 0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */ -}; - -/* Set NGENE I2S Config to 16 bit packed */ -static u8 I2SConfiguration[] = { - 0x00, 0x10, 0x00, 0x00, - 0x80, 0x10, 0x00, 0x00, -}; - -static u8 SPDIFConfiguration[10] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* Set NGENE I2S Config to transport stream compatible mode */ - -static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 }; - -static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 }; - -static u8 ITUDecoderSetup[4][16] = { - {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */ - 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00}, - {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, - {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */ - 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, - {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */ - 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, -}; - -/* - * 50 48 60 gleich - * 27p50 9f 00 22 80 42 69 18 ... - * 27p60 93 00 22 80 82 69 1c ... - */ - -/* Maxbyte to 1144 (for raw data) */ -static u8 ITUFeatureDecoderSetup[8] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00 -}; - -void FillTSBuffer(void *Buffer, int Length, u32 Flags) -{ - u32 *ptr = Buffer; - - memset(Buffer, TS_FILLER, Length); - while (Length > 0) { - if (Flags & DF_SWAP32) - *ptr = 0x471FFF10; - else - *ptr = 0x10FF1F47; - ptr += (188 / 4); - Length -= 188; - } -} - - -static void flush_buffers(struct ngene_channel *chan) -{ - u8 val; - - do { - msleep(1); - spin_lock_irq(&chan->state_lock); - val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80; - spin_unlock_irq(&chan->state_lock); - } while (val); -} - -static void clear_buffers(struct ngene_channel *chan) -{ - struct SBufferHeader *Cur = chan->nextBuffer; - - do { - memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR)); - if (chan->mode & NGENE_IO_TSOUT) - FillTSBuffer(Cur->Buffer1, - chan->Capture1Length, - chan->DataFormatFlags); - Cur = Cur->Next; - } while (Cur != chan->nextBuffer); - - if (chan->mode & NGENE_IO_TSOUT) { - chan->nextBuffer->ngeneBuffer.SR.DTOUpdate = - chan->AudioDTOValue; - chan->AudioDTOUpdated = 0; - - Cur = chan->TSIdleBuffer.Head; - - do { - memset(&Cur->ngeneBuffer.SR, 0, - sizeof(Cur->ngeneBuffer.SR)); - FillTSBuffer(Cur->Buffer1, - chan->Capture1Length, - chan->DataFormatFlags); - Cur = Cur->Next; - } while (Cur != chan->TSIdleBuffer.Head); - } -} - -static int ngene_command_stream_control(struct ngene *dev, u8 stream, - u8 control, u8 mode, u8 flags) -{ - struct ngene_channel *chan = &dev->channel[stream]; - struct ngene_command com; - u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300); - u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500); - u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700); - u16 BsSDO = 0x9B00; - - down(&dev->stream_mutex); - memset(&com, 0, sizeof(com)); - com.cmd.hdr.Opcode = CMD_CONTROL; - com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2; - com.cmd.StreamControl.Stream = stream | (control ? 8 : 0); - if (chan->mode & NGENE_IO_TSOUT) - com.cmd.StreamControl.Stream |= 0x07; - com.cmd.StreamControl.Control = control | - (flags & SFLAG_ORDER_LUMA_CHROMA); - com.cmd.StreamControl.Mode = mode; - com.in_len = sizeof(struct FW_STREAM_CONTROL); - com.out_len = 0; - - dprintk(KERN_INFO DEVICE_NAME - ": Stream=%02x, Control=%02x, Mode=%02x\n", - com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control, - com.cmd.StreamControl.Mode); - - chan->Mode = mode; - - if (!(control & 0x80)) { - spin_lock_irq(&chan->state_lock); - if (chan->State == KSSTATE_RUN) { - chan->State = KSSTATE_ACQUIRE; - chan->HWState = HWSTATE_STOP; - spin_unlock_irq(&chan->state_lock); - if (ngene_command(dev, &com) < 0) { - up(&dev->stream_mutex); - return -1; - } - /* clear_buffers(chan); */ - flush_buffers(chan); - up(&dev->stream_mutex); - return 0; - } - spin_unlock_irq(&chan->state_lock); - up(&dev->stream_mutex); - return 0; - } - - if (mode & SMODE_AUDIO_CAPTURE) { - com.cmd.StreamControl.CaptureBlockCount = - chan->Capture1Length / AUDIO_BLOCK_SIZE; - com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; - } else if (mode & SMODE_TRANSPORT_STREAM) { - com.cmd.StreamControl.CaptureBlockCount = - chan->Capture1Length / TS_BLOCK_SIZE; - com.cmd.StreamControl.MaxLinesPerField = - chan->Capture1Length / TS_BLOCK_SIZE; - com.cmd.StreamControl.Buffer_Address = - chan->TSRingBuffer.PAHead; - if (chan->mode & NGENE_IO_TSOUT) { - com.cmd.StreamControl.BytesPerVBILine = - chan->Capture1Length / TS_BLOCK_SIZE; - com.cmd.StreamControl.Stream |= 0x07; - } - } else { - com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine; - com.cmd.StreamControl.MaxLinesPerField = chan->nLines; - com.cmd.StreamControl.MinLinesPerField = 100; - com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; - - if (mode & SMODE_VBI_CAPTURE) { - com.cmd.StreamControl.MaxVBILinesPerField = - chan->nVBILines; - com.cmd.StreamControl.MinVBILinesPerField = 0; - com.cmd.StreamControl.BytesPerVBILine = - chan->nBytesPerVBILine; - } - if (flags & SFLAG_COLORBAR) - com.cmd.StreamControl.Stream |= 0x04; - } - - spin_lock_irq(&chan->state_lock); - if (mode & SMODE_AUDIO_CAPTURE) { - chan->nextBuffer = chan->RingBuffer.Head; - if (mode & SMODE_AUDIO_SPDIF) { - com.cmd.StreamControl.SetupDataLen = - sizeof(SPDIFConfiguration); - com.cmd.StreamControl.SetupDataAddr = BsSPI; - memcpy(com.cmd.StreamControl.SetupData, - SPDIFConfiguration, sizeof(SPDIFConfiguration)); - } else { - com.cmd.StreamControl.SetupDataLen = 4; - com.cmd.StreamControl.SetupDataAddr = BsSDI; - memcpy(com.cmd.StreamControl.SetupData, - I2SConfiguration + - 4 * dev->card_info->i2s[stream], 4); - } - } else if (mode & SMODE_TRANSPORT_STREAM) { - chan->nextBuffer = chan->TSRingBuffer.Head; - if (stream >= STREAM_AUDIOIN1) { - if (chan->mode & NGENE_IO_TSOUT) { - com.cmd.StreamControl.SetupDataLen = - sizeof(TS_I2SOutConfiguration); - com.cmd.StreamControl.SetupDataAddr = BsSDO; - memcpy(com.cmd.StreamControl.SetupData, - TS_I2SOutConfiguration, - sizeof(TS_I2SOutConfiguration)); - } else { - com.cmd.StreamControl.SetupDataLen = - sizeof(TS_I2SConfiguration); - com.cmd.StreamControl.SetupDataAddr = BsSDI; - memcpy(com.cmd.StreamControl.SetupData, - TS_I2SConfiguration, - sizeof(TS_I2SConfiguration)); - } - } else { - com.cmd.StreamControl.SetupDataLen = 8; - com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10; - memcpy(com.cmd.StreamControl.SetupData, - TSFeatureDecoderSetup + - 8 * dev->card_info->tsf[stream], 8); - } - } else { - chan->nextBuffer = chan->RingBuffer.Head; - com.cmd.StreamControl.SetupDataLen = - 16 + sizeof(ITUFeatureDecoderSetup); - com.cmd.StreamControl.SetupDataAddr = BsUVI; - memcpy(com.cmd.StreamControl.SetupData, - ITUDecoderSetup[chan->itumode], 16); - memcpy(com.cmd.StreamControl.SetupData + 16, - ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup)); - } - clear_buffers(chan); - chan->State = KSSTATE_RUN; - if (mode & SMODE_TRANSPORT_STREAM) - chan->HWState = HWSTATE_RUN; - else - chan->HWState = HWSTATE_STARTUP; - spin_unlock_irq(&chan->state_lock); - - if (ngene_command(dev, &com) < 0) { - up(&dev->stream_mutex); - return -1; - } - up(&dev->stream_mutex); - return 0; -} - -void set_transfer(struct ngene_channel *chan, int state) -{ - u8 control = 0, mode = 0, flags = 0; - struct ngene *dev = chan->dev; - int ret; - - /* - printk(KERN_INFO DEVICE_NAME ": st %d\n", state); - msleep(100); - */ - - if (state) { - if (chan->running) { - printk(KERN_INFO DEVICE_NAME ": already running\n"); - return; - } - } else { - if (!chan->running) { - printk(KERN_INFO DEVICE_NAME ": already stopped\n"); - return; - } - } - - if (dev->card_info->switch_ctrl) - dev->card_info->switch_ctrl(chan, 1, state ^ 1); - - if (state) { - spin_lock_irq(&chan->state_lock); - - /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", - ngreadl(0x9310)); */ - dvb_ringbuffer_flush(&dev->tsout_rbuf); - control = 0x80; - if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { - chan->Capture1Length = 512 * 188; - mode = SMODE_TRANSPORT_STREAM; - } - if (chan->mode & NGENE_IO_TSOUT) { - chan->pBufferExchange = tsout_exchange; - /* 0x66666666 = 50MHz *2^33 /250MHz */ - chan->AudioDTOValue = 0x80000000; - chan->AudioDTOUpdated = 1; - } - if (chan->mode & NGENE_IO_TSIN) - chan->pBufferExchange = tsin_exchange; - spin_unlock_irq(&chan->state_lock); - } else - ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", - ngreadl(0x9310)); */ - - ret = ngene_command_stream_control(dev, chan->number, - control, mode, flags); - if (!ret) - chan->running = state; - else - printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n", - state); - if (!state) { - spin_lock_irq(&chan->state_lock); - chan->pBufferExchange = NULL; - dvb_ringbuffer_flush(&dev->tsout_rbuf); - spin_unlock_irq(&chan->state_lock); - } -} - - -/****************************************************************************/ -/* nGene hardware init and release functions ********************************/ -/****************************************************************************/ - -static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) -{ - struct SBufferHeader *Cur = rb->Head; - u32 j; - - if (!Cur) - return; - - for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) { - if (Cur->Buffer1) - pci_free_consistent(dev->pci_dev, - rb->Buffer1Length, - Cur->Buffer1, - Cur->scList1->Address); - - if (Cur->Buffer2) - pci_free_consistent(dev->pci_dev, - rb->Buffer2Length, - Cur->Buffer2, - Cur->scList2->Address); - } - - if (rb->SCListMem) - pci_free_consistent(dev->pci_dev, rb->SCListMemSize, - rb->SCListMem, rb->PASCListMem); - - pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead); -} - -static void free_idlebuffer(struct ngene *dev, - struct SRingBufferDescriptor *rb, - struct SRingBufferDescriptor *tb) -{ - int j; - struct SBufferHeader *Cur = tb->Head; - - if (!rb->Head) - return; - free_ringbuffer(dev, rb); - for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) { - Cur->Buffer2 = NULL; - Cur->scList2 = NULL; - Cur->ngeneBuffer.Address_of_first_entry_2 = 0; - Cur->ngeneBuffer.Number_of_entries_2 = 0; - } -} - -static void free_common_buffers(struct ngene *dev) -{ - u32 i; - struct ngene_channel *chan; - - for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { - chan = &dev->channel[i]; - free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer); - free_ringbuffer(dev, &chan->RingBuffer); - free_ringbuffer(dev, &chan->TSRingBuffer); - } - - if (dev->OverflowBuffer) - pci_free_consistent(dev->pci_dev, - OVERFLOW_BUFFER_SIZE, - dev->OverflowBuffer, dev->PAOverflowBuffer); - - if (dev->FWInterfaceBuffer) - pci_free_consistent(dev->pci_dev, - 4096, - dev->FWInterfaceBuffer, - dev->PAFWInterfaceBuffer); -} - -/****************************************************************************/ -/* Ring buffer handling *****************************************************/ -/****************************************************************************/ - -static int create_ring_buffer(struct pci_dev *pci_dev, - struct SRingBufferDescriptor *descr, u32 NumBuffers) -{ - dma_addr_t tmp; - struct SBufferHeader *Head; - u32 i; - u32 MemSize = SIZEOF_SBufferHeader * NumBuffers; - u64 PARingBufferHead; - u64 PARingBufferCur; - u64 PARingBufferNext; - struct SBufferHeader *Cur, *Next; - - descr->Head = NULL; - descr->MemSize = 0; - descr->PAHead = 0; - descr->NumBuffers = 0; - - if (MemSize < 4096) - MemSize = 4096; - - Head = pci_alloc_consistent(pci_dev, MemSize, &tmp); - PARingBufferHead = tmp; - - if (!Head) - return -ENOMEM; - - memset(Head, 0, MemSize); - - PARingBufferCur = PARingBufferHead; - Cur = Head; - - for (i = 0; i < NumBuffers - 1; i++) { - Next = (struct SBufferHeader *) - (((u8 *) Cur) + SIZEOF_SBufferHeader); - PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader; - Cur->Next = Next; - Cur->ngeneBuffer.Next = PARingBufferNext; - Cur = Next; - PARingBufferCur = PARingBufferNext; - } - /* Last Buffer points back to first one */ - Cur->Next = Head; - Cur->ngeneBuffer.Next = PARingBufferHead; - - descr->Head = Head; - descr->MemSize = MemSize; - descr->PAHead = PARingBufferHead; - descr->NumBuffers = NumBuffers; - - return 0; -} - -static int AllocateRingBuffers(struct pci_dev *pci_dev, - dma_addr_t of, - struct SRingBufferDescriptor *pRingBuffer, - u32 Buffer1Length, u32 Buffer2Length) -{ - dma_addr_t tmp; - u32 i, j; - int status = 0; - u32 SCListMemSize = pRingBuffer->NumBuffers - * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) : - NUM_SCATTER_GATHER_ENTRIES) - * sizeof(struct HW_SCATTER_GATHER_ELEMENT); - - u64 PASCListMem; - struct HW_SCATTER_GATHER_ELEMENT *SCListEntry; - u64 PASCListEntry; - struct SBufferHeader *Cur; - void *SCListMem; - - if (SCListMemSize < 4096) - SCListMemSize = 4096; - - SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp); - - PASCListMem = tmp; - if (SCListMem == NULL) - return -ENOMEM; - - memset(SCListMem, 0, SCListMemSize); - - pRingBuffer->SCListMem = SCListMem; - pRingBuffer->PASCListMem = PASCListMem; - pRingBuffer->SCListMemSize = SCListMemSize; - pRingBuffer->Buffer1Length = Buffer1Length; - pRingBuffer->Buffer2Length = Buffer2Length; - - SCListEntry = SCListMem; - PASCListEntry = PASCListMem; - Cur = pRingBuffer->Head; - - for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) { - u64 PABuffer; - - void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length, - &tmp); - PABuffer = tmp; - - if (Buffer == NULL) - return -ENOMEM; - - Cur->Buffer1 = Buffer; - - SCListEntry->Address = PABuffer; - SCListEntry->Length = Buffer1Length; - - Cur->scList1 = SCListEntry; - Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry; - Cur->ngeneBuffer.Number_of_entries_1 = - NUM_SCATTER_GATHER_ENTRIES; - - SCListEntry += 1; - PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); - -#if NUM_SCATTER_GATHER_ENTRIES > 1 - for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) { - SCListEntry->Address = of; - SCListEntry->Length = OVERFLOW_BUFFER_SIZE; - SCListEntry += 1; - PASCListEntry += - sizeof(struct HW_SCATTER_GATHER_ELEMENT); - } -#endif - - if (!Buffer2Length) - continue; - - Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp); - PABuffer = tmp; - - if (Buffer == NULL) - return -ENOMEM; - - Cur->Buffer2 = Buffer; - - SCListEntry->Address = PABuffer; - SCListEntry->Length = Buffer2Length; - - Cur->scList2 = SCListEntry; - Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry; - Cur->ngeneBuffer.Number_of_entries_2 = - NUM_SCATTER_GATHER_ENTRIES; - - SCListEntry += 1; - PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); - -#if NUM_SCATTER_GATHER_ENTRIES > 1 - for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) { - SCListEntry->Address = of; - SCListEntry->Length = OVERFLOW_BUFFER_SIZE; - SCListEntry += 1; - PASCListEntry += - sizeof(struct HW_SCATTER_GATHER_ELEMENT); - } -#endif - - } - - return status; -} - -static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer, - struct SRingBufferDescriptor *pRingBuffer) -{ - int status = 0; - - /* Copy pointer to scatter gather list in TSRingbuffer - structure for buffer 2 - Load number of buffer - */ - u32 n = pRingBuffer->NumBuffers; - - /* Point to first buffer entry */ - struct SBufferHeader *Cur = pRingBuffer->Head; - int i; - /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */ - for (i = 0; i < n; i++) { - Cur->Buffer2 = pIdleBuffer->Head->Buffer1; - Cur->scList2 = pIdleBuffer->Head->scList1; - Cur->ngeneBuffer.Address_of_first_entry_2 = - pIdleBuffer->Head->ngeneBuffer. - Address_of_first_entry_1; - Cur->ngeneBuffer.Number_of_entries_2 = - pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1; - Cur = Cur->Next; - } - return status; -} - -static u32 RingBufferSizes[MAX_STREAM] = { - RING_SIZE_VIDEO, - RING_SIZE_VIDEO, - RING_SIZE_AUDIO, - RING_SIZE_AUDIO, - RING_SIZE_AUDIO, -}; - -static u32 Buffer1Sizes[MAX_STREAM] = { - MAX_VIDEO_BUFFER_SIZE, - MAX_VIDEO_BUFFER_SIZE, - MAX_AUDIO_BUFFER_SIZE, - MAX_AUDIO_BUFFER_SIZE, - MAX_AUDIO_BUFFER_SIZE -}; - -static u32 Buffer2Sizes[MAX_STREAM] = { - MAX_VBI_BUFFER_SIZE, - MAX_VBI_BUFFER_SIZE, - 0, - 0, - 0 -}; - - -static int AllocCommonBuffers(struct ngene *dev) -{ - int status = 0, i; - - dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096, - &dev->PAFWInterfaceBuffer); - if (!dev->FWInterfaceBuffer) - return -ENOMEM; - dev->hosttongene = dev->FWInterfaceBuffer; - dev->ngenetohost = dev->FWInterfaceBuffer + 256; - dev->EventBuffer = dev->FWInterfaceBuffer + 512; - - dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev, - OVERFLOW_BUFFER_SIZE, - &dev->PAOverflowBuffer); - if (!dev->OverflowBuffer) - return -ENOMEM; - memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE); - - for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { - int type = dev->card_info->io_type[i]; - - dev->channel[i].State = KSSTATE_STOP; - - if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) { - status = create_ring_buffer(dev->pci_dev, - &dev->channel[i].RingBuffer, - RingBufferSizes[i]); - if (status < 0) - break; - - if (type & (NGENE_IO_TV | NGENE_IO_AIN)) { - status = AllocateRingBuffers(dev->pci_dev, - dev-> - PAOverflowBuffer, - &dev->channel[i]. - RingBuffer, - Buffer1Sizes[i], - Buffer2Sizes[i]); - if (status < 0) - break; - } else if (type & NGENE_IO_HDTV) { - status = AllocateRingBuffers(dev->pci_dev, - dev-> - PAOverflowBuffer, - &dev->channel[i]. - RingBuffer, - MAX_HDTV_BUFFER_SIZE, - 0); - if (status < 0) - break; - } - } - - if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { - - status = create_ring_buffer(dev->pci_dev, - &dev->channel[i]. - TSRingBuffer, RING_SIZE_TS); - if (status < 0) - break; - - status = AllocateRingBuffers(dev->pci_dev, - dev->PAOverflowBuffer, - &dev->channel[i]. - TSRingBuffer, - MAX_TS_BUFFER_SIZE, 0); - if (status) - break; - } - - if (type & NGENE_IO_TSOUT) { - status = create_ring_buffer(dev->pci_dev, - &dev->channel[i]. - TSIdleBuffer, 1); - if (status < 0) - break; - status = AllocateRingBuffers(dev->pci_dev, - dev->PAOverflowBuffer, - &dev->channel[i]. - TSIdleBuffer, - MAX_TS_BUFFER_SIZE, 0); - if (status) - break; - FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer, - &dev->channel[i].TSRingBuffer); - } - } - return status; -} - -static void ngene_release_buffers(struct ngene *dev) -{ - if (dev->iomem) - iounmap(dev->iomem); - free_common_buffers(dev); - vfree(dev->tsout_buf); - vfree(dev->tsin_buf); - vfree(dev->ain_buf); - vfree(dev->vin_buf); - vfree(dev); -} - -static int ngene_get_buffers(struct ngene *dev) -{ - if (AllocCommonBuffers(dev)) - return -ENOMEM; - if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) { - dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE); - if (!dev->tsout_buf) - return -ENOMEM; - dvb_ringbuffer_init(&dev->tsout_rbuf, - dev->tsout_buf, TSOUT_BUF_SIZE); - } - if (dev->card_info->io_type[2]&NGENE_IO_TSIN) { - dev->tsin_buf = vmalloc(TSIN_BUF_SIZE); - if (!dev->tsin_buf) - return -ENOMEM; - dvb_ringbuffer_init(&dev->tsin_rbuf, - dev->tsin_buf, TSIN_BUF_SIZE); - } - if (dev->card_info->io_type[2] & NGENE_IO_AIN) { - dev->ain_buf = vmalloc(AIN_BUF_SIZE); - if (!dev->ain_buf) - return -ENOMEM; - dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE); - } - if (dev->card_info->io_type[0] & NGENE_IO_HDTV) { - dev->vin_buf = vmalloc(VIN_BUF_SIZE); - if (!dev->vin_buf) - return -ENOMEM; - dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE); - } - dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0), - pci_resource_len(dev->pci_dev, 0)); - if (!dev->iomem) - return -ENOMEM; - - return 0; -} - -static void ngene_init(struct ngene *dev) -{ - int i; - - tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev); - - memset_io(dev->iomem + 0xc000, 0x00, 0x220); - memset_io(dev->iomem + 0xc400, 0x00, 0x100); - - for (i = 0; i < MAX_STREAM; i++) { - dev->channel[i].dev = dev; - dev->channel[i].number = i; - } - - dev->fw_interface_version = 0; - - ngwritel(0, NGENE_INT_ENABLE); - - dev->icounts = ngreadl(NGENE_INT_COUNTS); - - dev->device_version = ngreadl(DEV_VER) & 0x0f; - printk(KERN_INFO DEVICE_NAME ": Device version %d\n", - dev->device_version); -} - -static int ngene_load_firm(struct ngene *dev) -{ - u32 size; - const struct firmware *fw = NULL; - u8 *ngene_fw; - char *fw_name; - int err, version; - - version = dev->card_info->fw_version; - - switch (version) { - default: - case 15: - version = 15; - size = 23466; - fw_name = "ngene_15.fw"; - dev->cmd_timeout_workaround = true; - break; - case 16: - size = 23498; - fw_name = "ngene_16.fw"; - dev->cmd_timeout_workaround = true; - break; - case 17: - size = 24446; - fw_name = "ngene_17.fw"; - dev->cmd_timeout_workaround = true; - break; - case 18: - size = 0; - fw_name = "ngene_18.fw"; - break; - } - - if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) { - printk(KERN_ERR DEVICE_NAME - ": Could not load firmware file %s.\n", fw_name); - printk(KERN_INFO DEVICE_NAME - ": Copy %s to your hotplug directory!\n", fw_name); - return -1; - } - if (size == 0) - size = fw->size; - if (size != fw->size) { - printk(KERN_ERR DEVICE_NAME - ": Firmware %s has invalid size!", fw_name); - err = -1; - } else { - printk(KERN_INFO DEVICE_NAME - ": Loading firmware file %s.\n", fw_name); - ngene_fw = (u8 *) fw->data; - err = ngene_command_load_firmware(dev, ngene_fw, size); - } - - release_firmware(fw); - - return err; -} - -static void ngene_stop(struct ngene *dev) -{ - down(&dev->cmd_mutex); - i2c_del_adapter(&(dev->channel[0].i2c_adapter)); - i2c_del_adapter(&(dev->channel[1].i2c_adapter)); - ngwritel(0, NGENE_INT_ENABLE); - ngwritel(0, NGENE_COMMAND); - ngwritel(0, NGENE_COMMAND_HI); - ngwritel(0, NGENE_STATUS); - ngwritel(0, NGENE_STATUS_HI); - ngwritel(0, NGENE_EVENT); - ngwritel(0, NGENE_EVENT_HI); - free_irq(dev->pci_dev->irq, dev); -#ifdef CONFIG_PCI_MSI - if (dev->msi_enabled) - pci_disable_msi(dev->pci_dev); -#endif -} - -static int ngene_buffer_config(struct ngene *dev) -{ - int stat; - - if (dev->card_info->fw_version >= 17) { - u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }; - u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 }; - u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 }; - u8 *bconf = tsin12_config; - - if (dev->card_info->io_type[2]&NGENE_IO_TSIN && - dev->card_info->io_type[3]&NGENE_IO_TSIN) { - bconf = tsin1234_config; - if (dev->card_info->io_type[4]&NGENE_IO_TSOUT && - dev->ci.en) - bconf = tsio1235_config; - } - stat = ngene_command_config_free_buf(dev, bconf); - } else { - int bconf = BUFFER_CONFIG_4422; - - if (dev->card_info->io_type[3] == NGENE_IO_TSIN) - bconf = BUFFER_CONFIG_3333; - stat = ngene_command_config_buf(dev, bconf); - } - return stat; -} - - -static int ngene_start(struct ngene *dev) -{ - int stat; - int i; - - pci_set_master(dev->pci_dev); - ngene_init(dev); - - stat = request_irq(dev->pci_dev->irq, irq_handler, - IRQF_SHARED, "nGene", - (void *)dev); - if (stat < 0) - return stat; - - init_waitqueue_head(&dev->cmd_wq); - init_waitqueue_head(&dev->tx_wq); - init_waitqueue_head(&dev->rx_wq); - sema_init(&dev->cmd_mutex, 1); - sema_init(&dev->stream_mutex, 1); - sema_init(&dev->pll_mutex, 1); - sema_init(&dev->i2c_switch_mutex, 1); - spin_lock_init(&dev->cmd_lock); - for (i = 0; i < MAX_STREAM; i++) - spin_lock_init(&dev->channel[i].state_lock); - ngwritel(1, TIMESTAMPS); - - ngwritel(1, NGENE_INT_ENABLE); - - stat = ngene_load_firm(dev); - if (stat < 0) - goto fail; - -#ifdef CONFIG_PCI_MSI - /* enable MSI if kernel and card support it */ - if (pci_msi_enabled() && dev->card_info->msi_supported) { - unsigned long flags; - - ngwritel(0, NGENE_INT_ENABLE); - free_irq(dev->pci_dev->irq, dev); - stat = pci_enable_msi(dev->pci_dev); - if (stat) { - printk(KERN_INFO DEVICE_NAME - ": MSI not available\n"); - flags = IRQF_SHARED; - } else { - flags = 0; - dev->msi_enabled = true; - } - stat = request_irq(dev->pci_dev->irq, irq_handler, - flags, "nGene", dev); - if (stat < 0) - goto fail2; - ngwritel(1, NGENE_INT_ENABLE); - } -#endif - - stat = ngene_i2c_init(dev, 0); - if (stat < 0) - goto fail; - - stat = ngene_i2c_init(dev, 1); - if (stat < 0) - goto fail; - - return 0; - -fail: - ngwritel(0, NGENE_INT_ENABLE); - free_irq(dev->pci_dev->irq, dev); -#ifdef CONFIG_PCI_MSI -fail2: - if (dev->msi_enabled) - pci_disable_msi(dev->pci_dev); -#endif - return stat; -} - -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ - -static void release_channel(struct ngene_channel *chan) -{ - struct dvb_demux *dvbdemux = &chan->demux; - struct ngene *dev = chan->dev; - - if (chan->running) - set_transfer(chan, 0); - - tasklet_kill(&chan->demux_tasklet); - - if (chan->ci_dev) { - dvb_unregister_device(chan->ci_dev); - chan->ci_dev = NULL; - } - - if (chan->fe2) - dvb_unregister_frontend(chan->fe2); - - if (chan->fe) { - dvb_unregister_frontend(chan->fe); - dvb_frontend_detach(chan->fe); - chan->fe = NULL; - } - - if (chan->has_demux) { - dvb_net_release(&chan->dvbnet); - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, - &chan->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, - &chan->mem_frontend); - dvb_dmxdev_release(&chan->dmxdev); - dvb_dmx_release(&chan->demux); - chan->has_demux = false; - } - - if (chan->has_adapter) { - dvb_unregister_adapter(&dev->adapter[chan->number]); - chan->has_adapter = false; - } -} - -static int init_channel(struct ngene_channel *chan) -{ - int ret = 0, nr = chan->number; - struct dvb_adapter *adapter = NULL; - struct dvb_demux *dvbdemux = &chan->demux; - struct ngene *dev = chan->dev; - struct ngene_info *ni = dev->card_info; - int io = ni->io_type[nr]; - - tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan); - chan->users = 0; - chan->type = io; - chan->mode = chan->type; /* for now only one mode */ - - if (io & NGENE_IO_TSIN) { - chan->fe = NULL; - if (ni->demod_attach[nr]) { - ret = ni->demod_attach[nr](chan); - if (ret < 0) - goto err; - } - if (chan->fe && ni->tuner_attach[nr]) { - ret = ni->tuner_attach[nr](chan); - if (ret < 0) - goto err; - } - } - - if (!dev->ci.en && (io & NGENE_IO_TSOUT)) - return 0; - - if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { - if (nr >= STREAM_AUDIOIN1) - chan->DataFormatFlags = DF_SWAP32; - - if (nr == 0 || !one_adapter || dev->first_adapter == NULL) { - adapter = &dev->adapter[nr]; - ret = dvb_register_adapter(adapter, "nGene", - THIS_MODULE, - &chan->dev->pci_dev->dev, - adapter_nr); - if (ret < 0) - goto err; - if (dev->first_adapter == NULL) - dev->first_adapter = adapter; - chan->has_adapter = true; - } else - adapter = dev->first_adapter; - } - - if (dev->ci.en && (io & NGENE_IO_TSOUT)) { - dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); - set_transfer(chan, 1); - chan->dev->channel[2].DataFormatFlags = DF_SWAP32; - set_transfer(&chan->dev->channel[2], 1); - dvb_register_device(adapter, &chan->ci_dev, - &ngene_dvbdev_ci, (void *) chan, - DVB_DEVICE_SEC); - if (!chan->ci_dev) - goto err; - } - - if (chan->fe) { - if (dvb_register_frontend(adapter, chan->fe) < 0) - goto err; - chan->has_demux = true; - } - if (chan->fe2) { - if (dvb_register_frontend(adapter, chan->fe2) < 0) - goto err; - chan->fe2->tuner_priv = chan->fe->tuner_priv; - memcpy(&chan->fe2->ops.tuner_ops, - &chan->fe->ops.tuner_ops, - sizeof(struct dvb_tuner_ops)); - } - - if (chan->has_demux) { - ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", - ngene_start_feed, - ngene_stop_feed, chan); - ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, - &chan->hw_frontend, - &chan->mem_frontend, adapter); - ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx); - } - - return ret; - -err: - if (chan->fe) { - dvb_frontend_detach(chan->fe); - chan->fe = NULL; - } - release_channel(chan); - return 0; -} - -static int init_channels(struct ngene *dev) -{ - int i, j; - - for (i = 0; i < MAX_STREAM; i++) { - dev->channel[i].number = i; - if (init_channel(&dev->channel[i]) < 0) { - for (j = i - 1; j >= 0; j--) - release_channel(&dev->channel[j]); - return -1; - } - } - return 0; -} - -static struct cxd2099_cfg cxd_cfg = { - .bitrate = 62000, - .adr = 0x40, - .polarity = 0, - .clock_mode = 0, -}; - -static void cxd_attach(struct ngene *dev) -{ - struct ngene_ci *ci = &dev->ci; - - ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter); - ci->dev = dev; - return; -} - -static void cxd_detach(struct ngene *dev) -{ - struct ngene_ci *ci = &dev->ci; - - dvb_ca_en50221_release(ci->en); - kfree(ci->en); - ci->en = 0; -} - -/***********************************/ -/* workaround for shutdown failure */ -/***********************************/ - -static void ngene_unlink(struct ngene *dev) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_MEM_WRITE; - com.cmd.hdr.Length = 3; - com.cmd.MemoryWrite.address = 0x910c; - com.cmd.MemoryWrite.data = 0xff; - com.in_len = 3; - com.out_len = 1; - - down(&dev->cmd_mutex); - ngwritel(0, NGENE_INT_ENABLE); - ngene_command_mutex(dev, &com); - up(&dev->cmd_mutex); -} - -void ngene_shutdown(struct pci_dev *pdev) -{ - struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev); - - if (!dev || !shutdown_workaround) - return; - - printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n"); - ngene_unlink(dev); - pci_disable_device(pdev); -} - -/****************************************************************************/ -/* device probe/remove calls ************************************************/ -/****************************************************************************/ - -void __devexit ngene_remove(struct pci_dev *pdev) -{ - struct ngene *dev = pci_get_drvdata(pdev); - int i; - - tasklet_kill(&dev->event_tasklet); - for (i = MAX_STREAM - 1; i >= 0; i--) - release_channel(&dev->channel[i]); - if (dev->ci.en) - cxd_detach(dev); - ngene_stop(dev); - ngene_release_buffers(dev); - pci_set_drvdata(pdev, NULL); - pci_disable_device(pdev); -} - -int __devinit ngene_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) -{ - struct ngene *dev; - int stat = 0; - - if (pci_enable_device(pci_dev) < 0) - return -ENODEV; - - dev = vzalloc(sizeof(struct ngene)); - if (dev == NULL) { - stat = -ENOMEM; - goto fail0; - } - - dev->pci_dev = pci_dev; - dev->card_info = (struct ngene_info *)id->driver_data; - printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name); - - pci_set_drvdata(pci_dev, dev); - - /* Alloc buffers and start nGene */ - stat = ngene_get_buffers(dev); - if (stat < 0) - goto fail1; - stat = ngene_start(dev); - if (stat < 0) - goto fail1; - - cxd_attach(dev); - - stat = ngene_buffer_config(dev); - if (stat < 0) - goto fail1; - - - dev->i2c_current_bus = -1; - - /* Register DVB adapters and devices for both channels */ - if (init_channels(dev) < 0) - goto fail2; - - return 0; - -fail2: - ngene_stop(dev); -fail1: - ngene_release_buffers(dev); -fail0: - pci_disable_device(pci_dev); - pci_set_drvdata(pci_dev, NULL); - return stat; -} diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c deleted file mode 100644 index fcb16a615aab..000000000000 --- a/drivers/media/dvb/ngene/ngene-dvb.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * ngene-dvb.c: nGene PCIe bridge driver - DVB functions - * - * Copyright (C) 2005-2007 Micronas - * - * Copyright (C) 2008-2009 Ralph Metzler - * Modifications for new nGene firmware, - * support for EEPROM-copying, - * support for new dual DVB-S2 card prototype - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ngene.h" - - -/****************************************************************************/ -/* COMMAND API interface ****************************************************/ -/****************************************************************************/ - -static ssize_t ts_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - - if (wait_event_interruptible(dev->tsout_rbuf.queue, - dvb_ringbuffer_free - (&dev->tsout_rbuf) >= count) < 0) - return 0; - - dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); - - return count; -} - -static ssize_t ts_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - int left, avail; - - left = count; - while (left) { - if (wait_event_interruptible( - dev->tsin_rbuf.queue, - dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0) - return -EAGAIN; - avail = dvb_ringbuffer_avail(&dev->tsin_rbuf); - if (avail > left) - avail = left; - dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail); - left -= avail; - buf += avail; - } - return count; -} - -static const struct file_operations ci_fops = { - .owner = THIS_MODULE, - .read = ts_read, - .write = ts_write, - .open = dvb_generic_open, - .release = dvb_generic_release, -}; - -struct dvb_device ngene_dvbdev_ci = { - .priv = 0, - .readers = -1, - .writers = -1, - .users = -1, - .fops = &ci_fops, -}; - - -/****************************************************************************/ -/* DVB functions and API interface ******************************************/ -/****************************************************************************/ - -static void swap_buffer(u32 *p, u32 len) -{ - while (len) { - *p = swab32(*p); - p++; - len -= 4; - } -} - -/* start of filler packet */ -static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER }; - -/* #define DEBUG_CI_XFER */ -#ifdef DEBUG_CI_XFER -static u32 ok; -static u32 overflow; -static u32 stripped; -#endif - -void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) -{ - struct ngene_channel *chan = priv; - struct ngene *dev = chan->dev; - - - if (flags & DF_SWAP32) - swap_buffer(buf, len); - - if (dev->ci.en && chan->number == 2) { - while (len >= 188) { - if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) { - if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) { - dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188); - wake_up(&dev->tsin_rbuf.queue); -#ifdef DEBUG_CI_XFER - ok++; -#endif - } -#ifdef DEBUG_CI_XFER - else - overflow++; -#endif - } -#ifdef DEBUG_CI_XFER - else - stripped++; - - if (ok % 100 == 0 && overflow) - printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped); -#endif - buf += 188; - len -= 188; - } - return NULL; - } - - if (chan->users > 0) - dvb_dmx_swfilter(&chan->demux, buf, len); - - return NULL; -} - -void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) -{ - struct ngene_channel *chan = priv; - struct ngene *dev = chan->dev; - u32 alen; - - alen = dvb_ringbuffer_avail(&dev->tsout_rbuf); - alen -= alen % 188; - - if (alen < len) - FillTSBuffer(buf + alen, len - alen, flags); - else - alen = len; - dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen); - if (flags & DF_SWAP32) - swap_buffer((u32 *)buf, alen); - wake_up_interruptible(&dev->tsout_rbuf.queue); - return buf; -} - - - -int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct ngene_channel *chan = dvbdmx->priv; - - if (chan->users == 0) { - if (!chan->dev->cmd_timeout_workaround || !chan->running) - set_transfer(chan, 1); - } - - return ++chan->users; -} - -int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct ngene_channel *chan = dvbdmx->priv; - - if (--chan->users) - return chan->users; - - if (!chan->dev->cmd_timeout_workaround) - set_transfer(chan, 0); - - return 0; -} - -int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, - int (*start_feed)(struct dvb_demux_feed *), - int (*stop_feed)(struct dvb_demux_feed *), - void *priv) -{ - dvbdemux->priv = priv; - - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = start_feed; - dvbdemux->stop_feed = stop_feed; - dvbdemux->write_to_decoder = NULL; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - return dvb_dmx_init(dvbdemux); -} - -int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, - struct dvb_demux *dvbdemux, - struct dmx_frontend *hw_frontend, - struct dmx_frontend *mem_frontend, - struct dvb_adapter *dvb_adapter) -{ - int ret; - - dmxdev->filternum = 256; - dmxdev->demux = &dvbdemux->dmx; - dmxdev->capabilities = 0; - ret = dvb_dmxdev_init(dmxdev, dvb_adapter); - if (ret < 0) - return ret; - - hw_frontend->source = DMX_FRONTEND_0; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); - mem_frontend->source = DMX_MEMORY_FE; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); - return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); -} diff --git a/drivers/media/dvb/ngene/ngene-i2c.c b/drivers/media/dvb/ngene/ngene-i2c.c deleted file mode 100644 index d28554f8ce99..000000000000 --- a/drivers/media/dvb/ngene/ngene-i2c.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * ngene-i2c.c: nGene PCIe bridge driver i2c functions - * - * Copyright (C) 2005-2007 Micronas - * - * Copyright (C) 2008-2009 Ralph Metzler - * Modifications for new nGene firmware, - * support for EEPROM-copying, - * support for new dual DVB-S2 card prototype - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -/* FIXME - some of these can probably be removed */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ngene.h" - -/* Firmware command for i2c operations */ -static int ngene_command_i2c_read(struct ngene *dev, u8 adr, - u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_I2C_READ; - com.cmd.hdr.Length = outlen + 3; - com.cmd.I2CRead.Device = adr << 1; - memcpy(com.cmd.I2CRead.Data, out, outlen); - com.cmd.I2CRead.Data[outlen] = inlen; - com.cmd.I2CRead.Data[outlen + 1] = 0; - com.in_len = outlen + 3; - com.out_len = inlen + 1; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - if ((com.cmd.raw8[0] >> 1) != adr) - return -EIO; - - if (flag) - memcpy(in, com.cmd.raw8, inlen + 1); - else - memcpy(in, com.cmd.raw8 + 1, inlen); - return 0; -} - -static int ngene_command_i2c_write(struct ngene *dev, u8 adr, - u8 *out, u8 outlen) -{ - struct ngene_command com; - - - com.cmd.hdr.Opcode = CMD_I2C_WRITE; - com.cmd.hdr.Length = outlen + 1; - com.cmd.I2CRead.Device = adr << 1; - memcpy(com.cmd.I2CRead.Data, out, outlen); - com.in_len = outlen + 1; - com.out_len = 1; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - if (com.cmd.raw8[0] == 1) - return -EIO; - - return 0; -} - -static void ngene_i2c_set_bus(struct ngene *dev, int bus) -{ - if (!(dev->card_info->i2c_access & 2)) - return; - if (dev->i2c_current_bus == bus) - return; - - switch (bus) { - case 0: - ngene_command_gpio_set(dev, 3, 0); - ngene_command_gpio_set(dev, 2, 1); - break; - - case 1: - ngene_command_gpio_set(dev, 2, 0); - ngene_command_gpio_set(dev, 3, 1); - break; - } - dev->i2c_current_bus = bus; -} - -static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, - struct i2c_msg msg[], int num) -{ - struct ngene_channel *chan = - (struct ngene_channel *)i2c_get_adapdata(adapter); - struct ngene *dev = chan->dev; - - down(&dev->i2c_switch_mutex); - ngene_i2c_set_bus(dev, chan->number); - - if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) - if (!ngene_command_i2c_read(dev, msg[0].addr, - msg[0].buf, msg[0].len, - msg[1].buf, msg[1].len, 0)) - goto done; - - if (num == 1 && !(msg[0].flags & I2C_M_RD)) - if (!ngene_command_i2c_write(dev, msg[0].addr, - msg[0].buf, msg[0].len)) - goto done; - if (num == 1 && (msg[0].flags & I2C_M_RD)) - if (!ngene_command_i2c_read(dev, msg[0].addr, NULL, 0, - msg[0].buf, msg[0].len, 0)) - goto done; - - up(&dev->i2c_switch_mutex); - return -EIO; - -done: - up(&dev->i2c_switch_mutex); - return num; -} - - -static u32 ngene_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static struct i2c_algorithm ngene_i2c_algo = { - .master_xfer = ngene_i2c_master_xfer, - .functionality = ngene_i2c_functionality, -}; - -int ngene_i2c_init(struct ngene *dev, int dev_nr) -{ - struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); - - i2c_set_adapdata(adap, &(dev->channel[dev_nr])); - - strcpy(adap->name, "nGene"); - - adap->algo = &ngene_i2c_algo; - adap->algo_data = (void *)&(dev->channel[dev_nr]); - adap->dev.parent = &dev->pci_dev->dev; - - return i2c_add_adapter(adap); -} - diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h deleted file mode 100644 index 5443dc0caea5..000000000000 --- a/drivers/media/dvb/ngene/ngene.h +++ /dev/null @@ -1,921 +0,0 @@ -/* - * ngene.h: nGene PCIe bridge driver - * - * Copyright (C) 2005-2007 Micronas - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#ifndef _NGENE_H_ -#define _NGENE_H_ - -#include -#include -#include -#include -#include -#include - -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_ca_en50221.h" -#include "dvb_frontend.h" -#include "dvb_ringbuffer.h" -#include "dvb_net.h" -#include "cxd2099.h" - -#define DEVICE_NAME "ngene" - -#define NGENE_VID 0x18c3 -#define NGENE_PID 0x0720 - -#ifndef VIDEO_CAP_VC1 -#define VIDEO_CAP_AVC 128 -#define VIDEO_CAP_H264 128 -#define VIDEO_CAP_VC1 256 -#define VIDEO_CAP_WMV9 256 -#define VIDEO_CAP_MPEG4 512 -#endif - -enum STREAM { - STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */ - STREAM_VIDEOIN2, - STREAM_AUDIOIN1, /* I2S or SPI Input */ - STREAM_AUDIOIN2, - STREAM_AUDIOOUT, - MAX_STREAM -}; - -enum SMODE_BITS { - SMODE_AUDIO_SPDIF = 0x20, - SMODE_AVSYNC = 0x10, - SMODE_TRANSPORT_STREAM = 0x08, - SMODE_AUDIO_CAPTURE = 0x04, - SMODE_VBI_CAPTURE = 0x02, - SMODE_VIDEO_CAPTURE = 0x01 -}; - -enum STREAM_FLAG_BITS { - SFLAG_CHROMA_FORMAT_2COMP = 0x01, /* Chroma Format : 2's complement */ - SFLAG_CHROMA_FORMAT_OFFSET = 0x00, /* Chroma Format : Binary offset */ - SFLAG_ORDER_LUMA_CHROMA = 0x02, /* Byte order: Y,Cb,Y,Cr */ - SFLAG_ORDER_CHROMA_LUMA = 0x00, /* Byte order: Cb,Y,Cr,Y */ - SFLAG_COLORBAR = 0x04, /* Select colorbar */ -}; - -#define PROGRAM_ROM 0x0000 -#define PROGRAM_SRAM 0x1000 -#define PERIPHERALS0 0x8000 -#define PERIPHERALS1 0x9000 -#define SHARED_BUFFER 0xC000 - -#define HOST_TO_NGENE (SHARED_BUFFER+0x0000) -#define NGENE_TO_HOST (SHARED_BUFFER+0x0100) -#define NGENE_COMMAND (SHARED_BUFFER+0x0200) -#define NGENE_COMMAND_HI (SHARED_BUFFER+0x0204) -#define NGENE_STATUS (SHARED_BUFFER+0x0208) -#define NGENE_STATUS_HI (SHARED_BUFFER+0x020C) -#define NGENE_EVENT (SHARED_BUFFER+0x0210) -#define NGENE_EVENT_HI (SHARED_BUFFER+0x0214) -#define VARIABLES (SHARED_BUFFER+0x0210) - -#define NGENE_INT_COUNTS (SHARED_BUFFER+0x0260) -#define NGENE_INT_ENABLE (SHARED_BUFFER+0x0264) -#define NGENE_VBI_LINE_COUNT (SHARED_BUFFER+0x0268) - -#define BUFFER_GP_XMIT (SHARED_BUFFER+0x0800) -#define BUFFER_GP_RECV (SHARED_BUFFER+0x0900) -#define EEPROM_AREA (SHARED_BUFFER+0x0A00) - -#define SG_V_IN_1 (SHARED_BUFFER+0x0A80) -#define SG_VBI_1 (SHARED_BUFFER+0x0B00) -#define SG_A_IN_1 (SHARED_BUFFER+0x0B80) -#define SG_V_IN_2 (SHARED_BUFFER+0x0C00) -#define SG_VBI_2 (SHARED_BUFFER+0x0C80) -#define SG_A_IN_2 (SHARED_BUFFER+0x0D00) -#define SG_V_OUT (SHARED_BUFFER+0x0D80) -#define SG_A_OUT2 (SHARED_BUFFER+0x0E00) - -#define DATA_A_IN_1 (SHARED_BUFFER+0x0E80) -#define DATA_A_IN_2 (SHARED_BUFFER+0x0F00) -#define DATA_A_OUT (SHARED_BUFFER+0x0F80) -#define DATA_V_IN_1 (SHARED_BUFFER+0x1000) -#define DATA_V_IN_2 (SHARED_BUFFER+0x2000) -#define DATA_V_OUT (SHARED_BUFFER+0x3000) - -#define DATA_FIFO_AREA (SHARED_BUFFER+0x1000) - -#define TIMESTAMPS 0xA000 -#define SCRATCHPAD 0xA080 -#define FORCE_INT 0xA088 -#define FORCE_NMI 0xA090 -#define INT_STATUS 0xA0A0 - -#define DEV_VER 0x9004 - -#define FW_DEBUG_DEFAULT (PROGRAM_SRAM+0x00FF) - -struct SG_ADDR { - u64 start; - u64 curr; - u16 curr_ptr; - u16 elements; - u32 pad[3]; -} __attribute__ ((__packed__)); - -struct SHARED_MEMORY { - /* C000 */ - u32 HostToNgene[64]; - - /* C100 */ - u32 NgeneToHost[64]; - - /* C200 */ - u64 NgeneCommand; - u64 NgeneStatus; - u64 NgeneEvent; - - /* C210 */ - u8 pad1[0xc260 - 0xc218]; - - /* C260 */ - u32 IntCounts; - u32 IntEnable; - - /* C268 */ - u8 pad2[0xd000 - 0xc268]; - -} __attribute__ ((__packed__)); - -struct BUFFER_STREAM_RESULTS { - u32 Clock; /* Stream time in 100ns units */ - u16 RemainingLines; /* Remaining lines in this field. - 0 for complete field */ - u8 FieldCount; /* Video field number */ - u8 Flags; /* Bit 7 = Done, Bit 6 = seen, Bit 5 = overflow, - Bit 0 = FieldID */ - u16 BlockCount; /* Audio block count (unused) */ - u8 Reserved[2]; - u32 DTOUpdate; -} __attribute__ ((__packed__)); - -struct HW_SCATTER_GATHER_ELEMENT { - u64 Address; - u32 Length; - u32 Reserved; -} __attribute__ ((__packed__)); - -struct BUFFER_HEADER { - u64 Next; - struct BUFFER_STREAM_RESULTS SR; - - u32 Number_of_entries_1; - u32 Reserved5; - u64 Address_of_first_entry_1; - - u32 Number_of_entries_2; - u32 Reserved7; - u64 Address_of_first_entry_2; -} __attribute__ ((__packed__)); - -struct EVENT_BUFFER { - u32 TimeStamp; - u8 GPIOStatus; - u8 UARTStatus; - u8 RXCharacter; - u8 EventStatus; - u32 Reserved[2]; -} __attribute__ ((__packed__)); - -/* Firmware commands. */ - -enum OPCODES { - CMD_NOP = 0, - CMD_FWLOAD_PREPARE = 0x01, - CMD_FWLOAD_FINISH = 0x02, - CMD_I2C_READ = 0x03, - CMD_I2C_WRITE = 0x04, - - CMD_I2C_WRITE_NOSTOP = 0x05, - CMD_I2C_CONTINUE_WRITE = 0x06, - CMD_I2C_CONTINUE_WRITE_NOSTOP = 0x07, - - CMD_DEBUG_OUTPUT = 0x09, - - CMD_CONTROL = 0x10, - CMD_CONFIGURE_BUFFER = 0x11, - CMD_CONFIGURE_FREE_BUFFER = 0x12, - - CMD_SPI_READ = 0x13, - CMD_SPI_WRITE = 0x14, - - CMD_MEM_READ = 0x20, - CMD_MEM_WRITE = 0x21, - CMD_SFR_READ = 0x22, - CMD_SFR_WRITE = 0x23, - CMD_IRAM_READ = 0x24, - CMD_IRAM_WRITE = 0x25, - CMD_SET_GPIO_PIN = 0x26, - CMD_SET_GPIO_INT = 0x27, - CMD_CONFIGURE_UART = 0x28, - CMD_WRITE_UART = 0x29, - MAX_CMD -}; - -enum RESPONSES { - OK = 0, - ERROR = 1 -}; - -struct FW_HEADER { - u8 Opcode; - u8 Length; -} __attribute__ ((__packed__)); - -struct FW_I2C_WRITE { - struct FW_HEADER hdr; - u8 Device; - u8 Data[250]; -} __attribute__ ((__packed__)); - -struct FW_I2C_CONTINUE_WRITE { - struct FW_HEADER hdr; - u8 Data[250]; -} __attribute__ ((__packed__)); - -struct FW_I2C_READ { - struct FW_HEADER hdr; - u8 Device; - u8 Data[252]; /* followed by two bytes of read data count */ -} __attribute__ ((__packed__)); - -struct FW_SPI_WRITE { - struct FW_HEADER hdr; - u8 ModeSelect; - u8 Data[250]; -} __attribute__ ((__packed__)); - -struct FW_SPI_READ { - struct FW_HEADER hdr; - u8 ModeSelect; - u8 Data[252]; /* followed by two bytes of read data count */ -} __attribute__ ((__packed__)); - -struct FW_FWLOAD_PREPARE { - struct FW_HEADER hdr; -} __attribute__ ((__packed__)); - -struct FW_FWLOAD_FINISH { - struct FW_HEADER hdr; - u16 Address; /* address of final block */ - u16 Length; -} __attribute__ ((__packed__)); - -/* - * Meaning of FW_STREAM_CONTROL::Mode bits: - * Bit 7: Loopback PEXin to PEXout using TVOut channel - * Bit 6: AVLOOP - * Bit 5: Audio select; 0=I2S, 1=SPDIF - * Bit 4: AVSYNC - * Bit 3: Enable transport stream - * Bit 2: Enable audio capture - * Bit 1: Enable ITU-Video VBI capture - * Bit 0: Enable ITU-Video capture - * - * Meaning of FW_STREAM_CONTROL::Control bits (see UVI1_CTL) - * Bit 7: continuous capture - * Bit 6: capture one field - * Bit 5: capture one frame - * Bit 4: unused - * Bit 3: starting field; 0=odd, 1=even - * Bit 2: sample size; 0=8-bit, 1=10-bit - * Bit 1: data format; 0=UYVY, 1=YUY2 - * Bit 0: resets buffer pointers -*/ - -enum FSC_MODE_BITS { - SMODE_LOOPBACK = 0x80, - SMODE_AVLOOP = 0x40, - _SMODE_AUDIO_SPDIF = 0x20, - _SMODE_AVSYNC = 0x10, - _SMODE_TRANSPORT_STREAM = 0x08, - _SMODE_AUDIO_CAPTURE = 0x04, - _SMODE_VBI_CAPTURE = 0x02, - _SMODE_VIDEO_CAPTURE = 0x01 -}; - - -/* Meaning of FW_STREAM_CONTROL::Stream bits: - * Bit 3: Audio sample count: 0 = relative, 1 = absolute - * Bit 2: color bar select; 1=color bars, 0=CV3 decoder - * Bits 1-0: stream select, UVI1, UVI2, TVOUT - */ - -struct FW_STREAM_CONTROL { - struct FW_HEADER hdr; - u8 Stream; /* Stream number (UVI1, UVI2, TVOUT) */ - u8 Control; /* Value written to UVI1_CTL */ - u8 Mode; /* Controls clock source */ - u8 SetupDataLen; /* Length of setup data, MSB=1 write - backwards */ - u16 CaptureBlockCount; /* Blocks (a 256 Bytes) to capture per buffer - for TS and Audio */ - u64 Buffer_Address; /* Address of first buffer header */ - u16 BytesPerVideoLine; - u16 MaxLinesPerField; - u16 MinLinesPerField; - u16 Reserved_1; - u16 BytesPerVBILine; - u16 MaxVBILinesPerField; - u16 MinVBILinesPerField; - u16 SetupDataAddr; /* ngene relative address of setup data */ - u8 SetupData[32]; /* setup data */ -} __attribute__((__packed__)); - -#define AUDIO_BLOCK_SIZE 256 -#define TS_BLOCK_SIZE 256 - -struct FW_MEM_READ { - struct FW_HEADER hdr; - u16 address; -} __attribute__ ((__packed__)); - -struct FW_MEM_WRITE { - struct FW_HEADER hdr; - u16 address; - u8 data; -} __attribute__ ((__packed__)); - -struct FW_SFR_IRAM_READ { - struct FW_HEADER hdr; - u8 address; -} __attribute__ ((__packed__)); - -struct FW_SFR_IRAM_WRITE { - struct FW_HEADER hdr; - u8 address; - u8 data; -} __attribute__ ((__packed__)); - -struct FW_SET_GPIO_PIN { - struct FW_HEADER hdr; - u8 select; -} __attribute__ ((__packed__)); - -struct FW_SET_GPIO_INT { - struct FW_HEADER hdr; - u8 select; -} __attribute__ ((__packed__)); - -struct FW_SET_DEBUGMODE { - struct FW_HEADER hdr; - u8 debug_flags; -} __attribute__ ((__packed__)); - -struct FW_CONFIGURE_BUFFERS { - struct FW_HEADER hdr; - u8 config; -} __attribute__ ((__packed__)); - -enum _BUFFER_CONFIGS { - /* 4k UVI1, 4k UVI2, 2k AUD1, 2k AUD2 (standard usage) */ - BUFFER_CONFIG_4422 = 0, - /* 3k UVI1, 3k UVI2, 3k AUD1, 3k AUD2 (4x TS input usage) */ - BUFFER_CONFIG_3333 = 1, - /* 8k UVI1, 0k UVI2, 2k AUD1, 2k I2SOut (HDTV decoder usage) */ - BUFFER_CONFIG_8022 = 2, - BUFFER_CONFIG_FW17 = 255, /* Use new FW 17 command */ -}; - -struct FW_CONFIGURE_FREE_BUFFERS { - struct FW_HEADER hdr; - u8 UVI1_BufferLength; - u8 UVI2_BufferLength; - u8 TVO_BufferLength; - u8 AUD1_BufferLength; - u8 AUD2_BufferLength; - u8 TVA_BufferLength; -} __attribute__ ((__packed__)); - -struct FW_CONFIGURE_UART { - struct FW_HEADER hdr; - u8 UartControl; -} __attribute__ ((__packed__)); - -enum _UART_CONFIG { - _UART_BAUDRATE_19200 = 0, - _UART_BAUDRATE_9600 = 1, - _UART_BAUDRATE_4800 = 2, - _UART_BAUDRATE_2400 = 3, - _UART_RX_ENABLE = 0x40, - _UART_TX_ENABLE = 0x80, -}; - -struct FW_WRITE_UART { - struct FW_HEADER hdr; - u8 Data[252]; -} __attribute__ ((__packed__)); - - -struct ngene_command { - u32 in_len; - u32 out_len; - union { - u32 raw[64]; - u8 raw8[256]; - struct FW_HEADER hdr; - struct FW_I2C_WRITE I2CWrite; - struct FW_I2C_CONTINUE_WRITE I2CContinueWrite; - struct FW_I2C_READ I2CRead; - struct FW_STREAM_CONTROL StreamControl; - struct FW_FWLOAD_PREPARE FWLoadPrepare; - struct FW_FWLOAD_FINISH FWLoadFinish; - struct FW_MEM_READ MemoryRead; - struct FW_MEM_WRITE MemoryWrite; - struct FW_SFR_IRAM_READ SfrIramRead; - struct FW_SFR_IRAM_WRITE SfrIramWrite; - struct FW_SPI_WRITE SPIWrite; - struct FW_SPI_READ SPIRead; - struct FW_SET_GPIO_PIN SetGpioPin; - struct FW_SET_GPIO_INT SetGpioInt; - struct FW_SET_DEBUGMODE SetDebugMode; - struct FW_CONFIGURE_BUFFERS ConfigureBuffers; - struct FW_CONFIGURE_FREE_BUFFERS ConfigureFreeBuffers; - struct FW_CONFIGURE_UART ConfigureUart; - struct FW_WRITE_UART WriteUart; - } cmd; -} __attribute__ ((__packed__)); - -#define NGENE_INTERFACE_VERSION 0x103 -#define MAX_VIDEO_BUFFER_SIZE (417792) /* 288*1440 rounded up to next page */ -#define MAX_AUDIO_BUFFER_SIZE (8192) /* Gives room for about 23msec@48KHz */ -#define MAX_VBI_BUFFER_SIZE (28672) /* 1144*18 rounded up to next page */ -#define MAX_TS_BUFFER_SIZE (98304) /* 512*188 rounded up to next page */ -#define MAX_HDTV_BUFFER_SIZE (2080768) /* 541*1920*2 rounded up to next page - Max: (1920x1080i60) */ - -#define OVERFLOW_BUFFER_SIZE (8192) - -#define RING_SIZE_VIDEO 4 -#define RING_SIZE_AUDIO 8 -#define RING_SIZE_TS 8 - -#define NUM_SCATTER_GATHER_ENTRIES 8 - -#define MAX_DMA_LENGTH (((MAX_VIDEO_BUFFER_SIZE + MAX_VBI_BUFFER_SIZE) * \ - RING_SIZE_VIDEO * 2) + \ - (MAX_AUDIO_BUFFER_SIZE * RING_SIZE_AUDIO * 2) + \ - (MAX_TS_BUFFER_SIZE * RING_SIZE_TS * 4) + \ - (RING_SIZE_VIDEO * PAGE_SIZE * 2) + \ - (RING_SIZE_AUDIO * PAGE_SIZE * 2) + \ - (RING_SIZE_TS * PAGE_SIZE * 4) + \ - 8 * PAGE_SIZE + OVERFLOW_BUFFER_SIZE + PAGE_SIZE) - -#define EVENT_QUEUE_SIZE 16 - -/* Gathers the current state of a single channel. */ - -struct SBufferHeader { - struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */ - struct SBufferHeader *Next; - void *Buffer1; - struct HW_SCATTER_GATHER_ELEMENT *scList1; - void *Buffer2; - struct HW_SCATTER_GATHER_ELEMENT *scList2; -}; - -/* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */ -#define SIZEOF_SBufferHeader ((sizeof(struct SBufferHeader) + 63) & ~63) - -enum HWSTATE { - HWSTATE_STOP, - HWSTATE_STARTUP, - HWSTATE_RUN, - HWSTATE_PAUSE, -}; - -enum KSSTATE { - KSSTATE_STOP, - KSSTATE_ACQUIRE, - KSSTATE_PAUSE, - KSSTATE_RUN, -}; - -struct SRingBufferDescriptor { - struct SBufferHeader *Head; /* Points to first buffer in ring buffer - structure*/ - u64 PAHead; /* Physical address of first buffer */ - u32 MemSize; /* Memory size of allocated ring buffers - (needed for freeing) */ - u32 NumBuffers; /* Number of buffers in the ring */ - u32 Buffer1Length; /* Allocated length of Buffer 1 */ - u32 Buffer2Length; /* Allocated length of Buffer 2 */ - void *SCListMem; /* Memory to hold scatter gather lists for this - ring */ - u64 PASCListMem; /* Physical address .. */ - u32 SCListMemSize; /* Size of this memory */ -}; - -enum STREAMMODEFLAGS { - StreamMode_NONE = 0, /* Stream not used */ - StreamMode_ANALOG = 1, /* Analog: Stream 0,1 = Video, 2,3 = Audio */ - StreamMode_TSIN = 2, /* Transport stream input (all) */ - StreamMode_HDTV = 4, /* HDTV: Maximum 1920x1080p30,1920x1080i60 - (only stream 0) */ - StreamMode_TSOUT = 8, /* Transport stream output (only stream 3) */ -}; - - -enum BufferExchangeFlags { - BEF_EVEN_FIELD = 0x00000001, - BEF_CONTINUATION = 0x00000002, - BEF_MORE_DATA = 0x00000004, - BEF_OVERFLOW = 0x00000008, - DF_SWAP32 = 0x00010000, -}; - -typedef void *(IBufferExchange)(void *, void *, u32, u32, u32); - -struct MICI_STREAMINFO { - IBufferExchange *pExchange; - IBufferExchange *pExchangeVBI; /* Secondary (VBI, ancillary) */ - u8 Stream; - u8 Flags; - u8 Mode; - u8 Reserved; - u16 nLinesVideo; - u16 nBytesPerLineVideo; - u16 nLinesVBI; - u16 nBytesPerLineVBI; - u32 CaptureLength; /* Used for audio and transport stream */ -}; - -/****************************************************************************/ -/* STRUCTS ******************************************************************/ -/****************************************************************************/ - -/* sound hardware definition */ -#define MIXER_ADDR_TVTUNER 0 -#define MIXER_ADDR_LAST 0 - -struct ngene_channel; - -/*struct sound chip*/ - -struct mychip { - struct ngene_channel *chan; - struct snd_card *card; - struct pci_dev *pci; - struct snd_pcm_substream *substream; - struct snd_pcm *pcm; - unsigned long port; - int irq; - spinlock_t mixer_lock; - spinlock_t lock; - int mixer_volume[MIXER_ADDR_LAST + 1][2]; - int capture_source[MIXER_ADDR_LAST + 1][2]; -}; - -#ifdef NGENE_V4L -struct ngene_overlay { - int tvnorm; - struct v4l2_rect w; - enum v4l2_field field; - struct v4l2_clip *clips; - int nclips; - int setup_ok; -}; - -struct ngene_tvnorm { - int v4l2_id; - char *name; - u16 swidth, sheight; /* scaled standard width, height */ - int tuner_norm; - int soundstd; -}; - -struct ngene_vopen { - struct ngene_channel *ch; - enum v4l2_priority prio; - int width; - int height; - int depth; - struct videobuf_queue vbuf_q; - struct videobuf_queue vbi; - int fourcc; - int picxcount; - int resources; - enum v4l2_buf_type type; - const struct ngene_format *fmt; - - const struct ngene_format *ovfmt; - struct ngene_overlay ov; -}; -#endif - -struct ngene_channel { - struct device device; - struct i2c_adapter i2c_adapter; - - struct ngene *dev; - int number; - int type; - int mode; - bool has_adapter; - bool has_demux; - int demod_type; - int (*gate_ctrl)(struct dvb_frontend *, int); - - struct dvb_frontend *fe; - struct dvb_frontend *fe2; - struct dmxdev dmxdev; - struct dvb_demux demux; - struct dvb_net dvbnet; - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - int users; - struct video_device *v4l_dev; - struct dvb_device *ci_dev; - struct tasklet_struct demux_tasklet; - - struct SBufferHeader *nextBuffer; - enum KSSTATE State; - enum HWSTATE HWState; - u8 Stream; - u8 Flags; - u8 Mode; - IBufferExchange *pBufferExchange; - IBufferExchange *pBufferExchange2; - - spinlock_t state_lock; - u16 nLines; - u16 nBytesPerLine; - u16 nVBILines; - u16 nBytesPerVBILine; - u16 itumode; - u32 Capture1Length; - u32 Capture2Length; - struct SRingBufferDescriptor RingBuffer; - struct SRingBufferDescriptor TSRingBuffer; - struct SRingBufferDescriptor TSIdleBuffer; - - u32 DataFormatFlags; - - int AudioDTOUpdated; - u32 AudioDTOValue; - - int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t); - u8 lnbh; - - /* stuff from analog driver */ - - int minor; - struct mychip *mychip; - struct snd_card *soundcard; - u8 *evenbuffer; - u8 dma_on; - int soundstreamon; - int audiomute; - int soundbuffisallocated; - int sndbuffflag; - int tun_rdy; - int dec_rdy; - int tun_dec_rdy; - int lastbufferflag; - - struct ngene_tvnorm *tvnorms; - int tvnorm_num; - int tvnorm; - -#ifdef NGENE_V4L - int videousers; - struct v4l2_prio_state prio; - struct ngene_vopen init; - int resources; - struct v4l2_framebuffer fbuf; - struct ngene_buffer *screen; /* overlay */ - struct list_head capture; /* video capture queue */ - spinlock_t s_lock; - struct semaphore reslock; -#endif - - int running; -}; - - -struct ngene_ci { - struct device device; - struct i2c_adapter i2c_adapter; - - struct ngene *dev; - struct dvb_ca_en50221 *en; -}; - -struct ngene; - -typedef void (rx_cb_t)(struct ngene *, u32, u8); -typedef void (tx_cb_t)(struct ngene *, u32); - -struct ngene { - int nr; - struct pci_dev *pci_dev; - unsigned char *iomem; - - /*struct i2c_adapter i2c_adapter;*/ - - u32 device_version; - u32 fw_interface_version; - u32 icounts; - bool msi_enabled; - bool cmd_timeout_workaround; - - u8 *CmdDoneByte; - int BootFirmware; - void *OverflowBuffer; - dma_addr_t PAOverflowBuffer; - void *FWInterfaceBuffer; - dma_addr_t PAFWInterfaceBuffer; - u8 *ngenetohost; - u8 *hosttongene; - - struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE]; - int EventQueueOverflowCount; - int EventQueueOverflowFlag; - struct tasklet_struct event_tasklet; - struct EVENT_BUFFER *EventBuffer; - int EventQueueWriteIndex; - int EventQueueReadIndex; - - wait_queue_head_t cmd_wq; - int cmd_done; - struct semaphore cmd_mutex; - struct semaphore stream_mutex; - struct semaphore pll_mutex; - struct semaphore i2c_switch_mutex; - int i2c_current_channel; - int i2c_current_bus; - spinlock_t cmd_lock; - - struct dvb_adapter adapter[MAX_STREAM]; - struct dvb_adapter *first_adapter; /* "one_adapter" modprobe opt */ - struct ngene_channel channel[MAX_STREAM]; - - struct ngene_info *card_info; - - tx_cb_t *TxEventNotify; - rx_cb_t *RxEventNotify; - int tx_busy; - wait_queue_head_t tx_wq; - wait_queue_head_t rx_wq; -#define UART_RBUF_LEN 4096 - u8 uart_rbuf[UART_RBUF_LEN]; - int uart_rp, uart_wp; - -#define TS_FILLER 0x6f - - u8 *tsout_buf; -#define TSOUT_BUF_SIZE (512*188*8) - struct dvb_ringbuffer tsout_rbuf; - - u8 *tsin_buf; -#define TSIN_BUF_SIZE (512*188*8) - struct dvb_ringbuffer tsin_rbuf; - - u8 *ain_buf; -#define AIN_BUF_SIZE (128*1024) - struct dvb_ringbuffer ain_rbuf; - - - u8 *vin_buf; -#define VIN_BUF_SIZE (4*1920*1080) - struct dvb_ringbuffer vin_rbuf; - - unsigned long exp_val; - int prev_cmd; - - struct ngene_ci ci; -}; - -struct ngene_info { - int type; -#define NGENE_APP 0 -#define NGENE_TERRATEC 1 -#define NGENE_SIDEWINDER 2 -#define NGENE_RACER 3 -#define NGENE_VIPER 4 -#define NGENE_PYTHON 5 -#define NGENE_VBOX_V1 6 -#define NGENE_VBOX_V2 7 - - int fw_version; - bool msi_supported; - char *name; - - int io_type[MAX_STREAM]; -#define NGENE_IO_NONE 0 -#define NGENE_IO_TV 1 -#define NGENE_IO_HDTV 2 -#define NGENE_IO_TSIN 4 -#define NGENE_IO_TSOUT 8 -#define NGENE_IO_AIN 16 - - void *fe_config[4]; - void *tuner_config[4]; - - int (*demod_attach[4])(struct ngene_channel *); - int (*tuner_attach[4])(struct ngene_channel *); - - u8 avf[4]; - u8 msp[4]; - u8 demoda[4]; - u8 lnb[4]; - int i2c_access; - u8 ntsc; - u8 tsf[4]; - u8 i2s[4]; - - int (*gate_ctrl)(struct dvb_frontend *, int); - int (*switch_ctrl)(struct ngene_channel *, int, int); -}; - -#ifdef NGENE_V4L -struct ngene_format { - char *name; - int fourcc; /* video4linux 2 */ - int btformat; /* BT848_COLOR_FMT_* */ - int format; - int btswap; /* BT848_COLOR_CTL_* */ - int depth; /* bit/pixel */ - int flags; - int hshift, vshift; /* for planar modes */ - int palette; -}; - -#define RESOURCE_OVERLAY 1 -#define RESOURCE_VIDEO 2 -#define RESOURCE_VBI 4 - -struct ngene_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - /* ngene specific */ - const struct ngene_format *fmt; - int tvnorm; - int btformat; - int btswap; -}; -#endif - - -/* Provided by ngene-core.c */ -int __devinit ngene_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id); -void __devexit ngene_remove(struct pci_dev *pdev); -void ngene_shutdown(struct pci_dev *pdev); -int ngene_command(struct ngene *dev, struct ngene_command *com); -int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level); -void set_transfer(struct ngene_channel *chan, int state); -void FillTSBuffer(void *Buffer, int Length, u32 Flags); - -/* Provided by ngene-i2c.c */ -int ngene_i2c_init(struct ngene *dev, int dev_nr); - -/* Provided by ngene-dvb.c */ -extern struct dvb_device ngene_dvbdev_ci; -void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); -void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); -int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed); -int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed); -int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, - int (*start_feed)(struct dvb_demux_feed *), - int (*stop_feed)(struct dvb_demux_feed *), - void *priv); -int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, - struct dvb_demux *dvbdemux, - struct dmx_frontend *hw_frontend, - struct dmx_frontend *mem_frontend, - struct dvb_adapter *dvb_adapter); - -#endif - -/* LocalWords: Endif - */ diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/dvb/pluto2/Kconfig deleted file mode 100644 index 7d8e6e87bdbb..000000000000 --- a/drivers/media/dvb/pluto2/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config DVB_PLUTO2 - tristate "Pluto2 cards" - depends on DVB_CORE && PCI && I2C - select I2C_ALGOBIT - select DVB_TDA1004X - help - Support for PCI cards based on the Pluto2 FPGA like the Satelco - Easywatch Mobile Terrestrial DVB-T Receiver. - - Since these cards have no MPEG decoder onboard, they transmit - only compressed MPEG data over the PCI bus, so you need - an external software decoder to watch TV on your computer. - - Say Y or M if you own such a device and want to use it. - diff --git a/drivers/media/dvb/pluto2/Makefile b/drivers/media/dvb/pluto2/Makefile deleted file mode 100644 index 524bf841f42b..000000000000 --- a/drivers/media/dvb/pluto2/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_DVB_PLUTO2) += pluto2.o - -ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c deleted file mode 100644 index f148b19a206a..000000000000 --- a/drivers/media/dvb/pluto2/pluto2.c +++ /dev/null @@ -1,815 +0,0 @@ -/* - * pluto2.c - Satelco Easywatch Mobile Terrestrial Receiver [DVB-T] - * - * Copyright (C) 2005 Andreas Oberritter - * - * based on pluto2.c 1.10 - http://instinct-wp8.no-ip.org/pluto/ - * by Dany Salman - * Copyright (c) 2004 TDF - * - * This program is free software; 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 - -#include "demux.h" -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" -#include "dvbdev.h" -#include "tda1004x.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define DRIVER_NAME "pluto2" - -#define REG_PIDn(n) ((n) << 2) /* PID n pattern registers */ -#define REG_PCAR 0x0020 /* PC address register */ -#define REG_TSCR 0x0024 /* TS ctrl & status */ -#define REG_MISC 0x0028 /* miscellaneous */ -#define REG_MMAC 0x002c /* MSB MAC address */ -#define REG_IMAC 0x0030 /* ISB MAC address */ -#define REG_LMAC 0x0034 /* LSB MAC address */ -#define REG_SPID 0x0038 /* SPI data */ -#define REG_SLCS 0x003c /* serial links ctrl/status */ - -#define PID0_NOFIL (0x0001 << 16) -#define PIDn_ENP (0x0001 << 15) -#define PID0_END (0x0001 << 14) -#define PID0_AFIL (0x0001 << 13) -#define PIDn_PID (0x1fff << 0) - -#define TSCR_NBPACKETS (0x00ff << 24) -#define TSCR_DEM (0x0001 << 17) -#define TSCR_DE (0x0001 << 16) -#define TSCR_RSTN (0x0001 << 15) -#define TSCR_MSKO (0x0001 << 14) -#define TSCR_MSKA (0x0001 << 13) -#define TSCR_MSKL (0x0001 << 12) -#define TSCR_OVR (0x0001 << 11) -#define TSCR_AFUL (0x0001 << 10) -#define TSCR_LOCK (0x0001 << 9) -#define TSCR_IACK (0x0001 << 8) -#define TSCR_ADEF (0x007f << 0) - -#define MISC_DVR (0x0fff << 4) -#define MISC_ALED (0x0001 << 3) -#define MISC_FRST (0x0001 << 2) -#define MISC_LED1 (0x0001 << 1) -#define MISC_LED0 (0x0001 << 0) - -#define SPID_SPIDR (0x00ff << 0) - -#define SLCS_SCL (0x0001 << 7) -#define SLCS_SDA (0x0001 << 6) -#define SLCS_CSN (0x0001 << 2) -#define SLCS_OVR (0x0001 << 1) -#define SLCS_SWC (0x0001 << 0) - -#define TS_DMA_PACKETS (8) -#define TS_DMA_BYTES (188 * TS_DMA_PACKETS) - -#define I2C_ADDR_TDA10046 0x10 -#define I2C_ADDR_TUA6034 0xc2 -#define NHWFILTERS 8 - -struct pluto { - /* pci */ - struct pci_dev *pdev; - u8 __iomem *io_mem; - - /* dvb */ - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - struct dmxdev dmxdev; - struct dvb_adapter dvb_adapter; - struct dvb_demux demux; - struct dvb_frontend *fe; - struct dvb_net dvbnet; - unsigned int full_ts_users; - unsigned int users; - - /* i2c */ - struct i2c_algo_bit_data i2c_bit; - struct i2c_adapter i2c_adap; - unsigned int i2cbug; - - /* irq */ - unsigned int overflow; - unsigned int dead; - - /* dma */ - dma_addr_t dma_addr; - u8 dma_buf[TS_DMA_BYTES]; - u8 dummy[4096]; -}; - -static inline struct pluto *feed_to_pluto(struct dvb_demux_feed *feed) -{ - return container_of(feed->demux, struct pluto, demux); -} - -static inline struct pluto *frontend_to_pluto(struct dvb_frontend *fe) -{ - return container_of(fe->dvb, struct pluto, dvb_adapter); -} - -static inline u32 pluto_readreg(struct pluto *pluto, u32 reg) -{ - return readl(&pluto->io_mem[reg]); -} - -static inline void pluto_writereg(struct pluto *pluto, u32 reg, u32 val) -{ - writel(val, &pluto->io_mem[reg]); -} - -static inline void pluto_rw(struct pluto *pluto, u32 reg, u32 mask, u32 bits) -{ - u32 val = readl(&pluto->io_mem[reg]); - val &= ~mask; - val |= bits; - writel(val, &pluto->io_mem[reg]); -} - -static void pluto_write_tscr(struct pluto *pluto, u32 val) -{ - /* set the number of packets */ - val &= ~TSCR_ADEF; - val |= TS_DMA_PACKETS / 2; - - pluto_writereg(pluto, REG_TSCR, val); -} - -static void pluto_setsda(void *data, int state) -{ - struct pluto *pluto = data; - - if (state) - pluto_rw(pluto, REG_SLCS, SLCS_SDA, SLCS_SDA); - else - pluto_rw(pluto, REG_SLCS, SLCS_SDA, 0); -} - -static void pluto_setscl(void *data, int state) -{ - struct pluto *pluto = data; - - if (state) - pluto_rw(pluto, REG_SLCS, SLCS_SCL, SLCS_SCL); - else - pluto_rw(pluto, REG_SLCS, SLCS_SCL, 0); - - /* try to detect i2c_inb() to workaround hardware bug: - * reset SDA to high after SCL has been set to low */ - if ((state) && (pluto->i2cbug == 0)) { - pluto->i2cbug = 1; - } else { - if ((!state) && (pluto->i2cbug == 1)) - pluto_setsda(pluto, 1); - pluto->i2cbug = 0; - } -} - -static int pluto_getsda(void *data) -{ - struct pluto *pluto = data; - - return pluto_readreg(pluto, REG_SLCS) & SLCS_SDA; -} - -static int pluto_getscl(void *data) -{ - struct pluto *pluto = data; - - return pluto_readreg(pluto, REG_SLCS) & SLCS_SCL; -} - -static void pluto_reset_frontend(struct pluto *pluto, int reenable) -{ - u32 val = pluto_readreg(pluto, REG_MISC); - - if (val & MISC_FRST) { - val &= ~MISC_FRST; - pluto_writereg(pluto, REG_MISC, val); - } - if (reenable) { - val |= MISC_FRST; - pluto_writereg(pluto, REG_MISC, val); - } -} - -static void pluto_reset_ts(struct pluto *pluto, int reenable) -{ - u32 val = pluto_readreg(pluto, REG_TSCR); - - if (val & TSCR_RSTN) { - val &= ~TSCR_RSTN; - pluto_write_tscr(pluto, val); - } - if (reenable) { - val |= TSCR_RSTN; - pluto_write_tscr(pluto, val); - } -} - -static void pluto_set_dma_addr(struct pluto *pluto) -{ - pluto_writereg(pluto, REG_PCAR, pluto->dma_addr); -} - -static int __devinit pluto_dma_map(struct pluto *pluto) -{ - pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, - TS_DMA_BYTES, PCI_DMA_FROMDEVICE); - - return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr); -} - -static void pluto_dma_unmap(struct pluto *pluto) -{ - pci_unmap_single(pluto->pdev, pluto->dma_addr, - TS_DMA_BYTES, PCI_DMA_FROMDEVICE); -} - -static int pluto_start_feed(struct dvb_demux_feed *f) -{ - struct pluto *pluto = feed_to_pluto(f); - - /* enable PID filtering */ - if (pluto->users++ == 0) - pluto_rw(pluto, REG_PIDn(0), PID0_AFIL | PID0_NOFIL, 0); - - if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) - pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, PIDn_ENP | f->pid); - else if (pluto->full_ts_users++ == 0) - pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, PID0_NOFIL); - - return 0; -} - -static int pluto_stop_feed(struct dvb_demux_feed *f) -{ - struct pluto *pluto = feed_to_pluto(f); - - /* disable PID filtering */ - if (--pluto->users == 0) - pluto_rw(pluto, REG_PIDn(0), PID0_AFIL, PID0_AFIL); - - if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) - pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, 0x1fff); - else if (--pluto->full_ts_users == 0) - pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, 0); - - return 0; -} - -static void pluto_dma_end(struct pluto *pluto, unsigned int nbpackets) -{ - /* synchronize the DMA transfer with the CPU - * first so that we see updated contents. */ - pci_dma_sync_single_for_cpu(pluto->pdev, pluto->dma_addr, - TS_DMA_BYTES, PCI_DMA_FROMDEVICE); - - /* Workaround for broken hardware: - * [1] On startup NBPACKETS seems to contain an uninitialized value, - * but no packets have been transferred. - * [2] Sometimes (actually very often) NBPACKETS stays at zero - * although one packet has been transferred. - * [3] Sometimes (actually rarely), the card gets into an erroneous - * mode where it continuously generates interrupts, claiming it - * has received nbpackets>TS_DMA_PACKETS packets, but no packet - * has been transferred. Only a reset seems to solve this - */ - if ((nbpackets == 0) || (nbpackets > TS_DMA_PACKETS)) { - unsigned int i = 0; - while (pluto->dma_buf[i] == 0x47) - i += 188; - nbpackets = i / 188; - if (i == 0) { - pluto_reset_ts(pluto, 1); - dev_printk(KERN_DEBUG, &pluto->pdev->dev, "resetting TS because of invalid packet counter\n"); - } - } - - dvb_dmx_swfilter_packets(&pluto->demux, pluto->dma_buf, nbpackets); - - /* clear the dma buffer. this is needed to be able to identify - * new valid ts packets above */ - memset(pluto->dma_buf, 0, nbpackets * 188); - - /* reset the dma address */ - pluto_set_dma_addr(pluto); - - /* sync the buffer and give it back to the card */ - pci_dma_sync_single_for_device(pluto->pdev, pluto->dma_addr, - TS_DMA_BYTES, PCI_DMA_FROMDEVICE); -} - -static irqreturn_t pluto_irq(int irq, void *dev_id) -{ - struct pluto *pluto = dev_id; - u32 tscr; - - /* check whether an interrupt occurred on this device */ - tscr = pluto_readreg(pluto, REG_TSCR); - if (!(tscr & (TSCR_DE | TSCR_OVR))) - return IRQ_NONE; - - if (tscr == 0xffffffff) { - if (pluto->dead == 0) - dev_err(&pluto->pdev->dev, "card has hung or been ejected.\n"); - /* It's dead Jim */ - pluto->dead = 1; - return IRQ_HANDLED; - } - - /* dma end interrupt */ - if (tscr & TSCR_DE) { - pluto_dma_end(pluto, (tscr & TSCR_NBPACKETS) >> 24); - /* overflow interrupt */ - if (tscr & TSCR_OVR) - pluto->overflow++; - if (pluto->overflow) { - dev_err(&pluto->pdev->dev, "overflow irq (%d)\n", - pluto->overflow); - pluto_reset_ts(pluto, 1); - pluto->overflow = 0; - } - } else if (tscr & TSCR_OVR) { - pluto->overflow++; - } - - /* ACK the interrupt */ - pluto_write_tscr(pluto, tscr | TSCR_IACK); - - return IRQ_HANDLED; -} - -static void __devinit pluto_enable_irqs(struct pluto *pluto) -{ - u32 val = pluto_readreg(pluto, REG_TSCR); - - /* disable AFUL and LOCK interrupts */ - val |= (TSCR_MSKA | TSCR_MSKL); - /* enable DMA and OVERFLOW interrupts */ - val &= ~(TSCR_DEM | TSCR_MSKO); - /* clear pending interrupts */ - val |= TSCR_IACK; - - pluto_write_tscr(pluto, val); -} - -static void pluto_disable_irqs(struct pluto *pluto) -{ - u32 val = pluto_readreg(pluto, REG_TSCR); - - /* disable all interrupts */ - val |= (TSCR_DEM | TSCR_MSKO | TSCR_MSKA | TSCR_MSKL); - /* clear pending interrupts */ - val |= TSCR_IACK; - - pluto_write_tscr(pluto, val); -} - -static int __devinit pluto_hw_init(struct pluto *pluto) -{ - pluto_reset_frontend(pluto, 1); - - /* set automatic LED control by FPGA */ - pluto_rw(pluto, REG_MISC, MISC_ALED, MISC_ALED); - - /* set data endianess */ -#ifdef __LITTLE_ENDIAN - pluto_rw(pluto, REG_PIDn(0), PID0_END, PID0_END); -#else - pluto_rw(pluto, REG_PIDn(0), PID0_END, 0); -#endif - /* map DMA and set address */ - pluto_dma_map(pluto); - pluto_set_dma_addr(pluto); - - /* enable interrupts */ - pluto_enable_irqs(pluto); - - /* reset TS logic */ - pluto_reset_ts(pluto, 1); - - return 0; -} - -static void pluto_hw_exit(struct pluto *pluto) -{ - /* disable interrupts */ - pluto_disable_irqs(pluto); - - pluto_reset_ts(pluto, 0); - - /* LED: disable automatic control, enable yellow, disable green */ - pluto_rw(pluto, REG_MISC, MISC_ALED | MISC_LED1 | MISC_LED0, MISC_LED1); - - /* unmap DMA */ - pluto_dma_unmap(pluto); - - pluto_reset_frontend(pluto, 0); -} - -static inline u32 divide(u32 numerator, u32 denominator) -{ - if (denominator == 0) - return ~0; - - return DIV_ROUND_CLOSEST(numerator, denominator); -} - -/* LG Innotek TDTE-E001P (Infineon TUA6034) */ -static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct pluto *pluto = frontend_to_pluto(fe); - struct i2c_msg msg; - int ret; - u8 buf[4]; - u32 div; - - // Fref = 166.667 Hz - // Fref * 3 = 500.000 Hz - // IF = 36166667 - // IF / Fref = 217 - //div = divide(p->frequency + 36166667, 166667); - div = divide(p->frequency * 3, 500000) + 217; - buf[0] = (div >> 8) & 0x7f; - buf[1] = (div >> 0) & 0xff; - - if (p->frequency < 611000000) - buf[2] = 0xb4; - else if (p->frequency < 811000000) - buf[2] = 0xbc; - else - buf[2] = 0xf4; - - // VHF: 174-230 MHz - // center: 350 MHz - // UHF: 470-862 MHz - if (p->frequency < 350000000) - buf[3] = 0x02; - else - buf[3] = 0x04; - - if (p->bandwidth_hz == 8000000) - buf[3] |= 0x08; - - msg.addr = I2C_ADDR_TUA6034 >> 1; - msg.flags = 0; - msg.buf = buf; - msg.len = sizeof(buf); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - ret = i2c_transfer(&pluto->i2c_adap, &msg, 1); - if (ret < 0) - return ret; - else if (ret == 0) - return -EREMOTEIO; - - return 0; -} - -static int pluto2_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct pluto *pluto = frontend_to_pluto(fe); - - return request_firmware(fw, name, &pluto->pdev->dev); -} - -static struct tda1004x_config pluto2_fe_config __devinitdata = { - .demod_address = I2C_ADDR_TDA10046 >> 1, - .invert = 1, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_DEFAULT, - .if_freq = TDA10046_FREQ_3617, - .request_firmware = pluto2_request_firmware, -}; - -static int __devinit frontend_init(struct pluto *pluto) -{ - int ret; - - pluto->fe = tda10046_attach(&pluto2_fe_config, &pluto->i2c_adap); - if (!pluto->fe) { - dev_err(&pluto->pdev->dev, "could not attach frontend\n"); - return -ENODEV; - } - pluto->fe->ops.tuner_ops.set_params = lg_tdtpe001p_tuner_set_params; - - ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe); - if (ret < 0) { - if (pluto->fe->ops.release) - pluto->fe->ops.release(pluto->fe); - return ret; - } - - return 0; -} - -static void __devinit pluto_read_rev(struct pluto *pluto) -{ - u32 val = pluto_readreg(pluto, REG_MISC) & MISC_DVR; - dev_info(&pluto->pdev->dev, "board revision %d.%d\n", - (val >> 12) & 0x0f, (val >> 4) & 0xff); -} - -static void __devinit pluto_read_mac(struct pluto *pluto, u8 *mac) -{ - u32 val = pluto_readreg(pluto, REG_MMAC); - mac[0] = (val >> 8) & 0xff; - mac[1] = (val >> 0) & 0xff; - - val = pluto_readreg(pluto, REG_IMAC); - mac[2] = (val >> 8) & 0xff; - mac[3] = (val >> 0) & 0xff; - - val = pluto_readreg(pluto, REG_LMAC); - mac[4] = (val >> 8) & 0xff; - mac[5] = (val >> 0) & 0xff; - - dev_info(&pluto->pdev->dev, "MAC %pM\n", mac); -} - -static int __devinit pluto_read_serial(struct pluto *pluto) -{ - struct pci_dev *pdev = pluto->pdev; - unsigned int i, j; - u8 __iomem *cis; - - cis = pci_iomap(pdev, 1, 0); - if (!cis) - return -EIO; - - dev_info(&pdev->dev, "S/N "); - - for (i = 0xe0; i < 0x100; i += 4) { - u32 val = readl(&cis[i]); - for (j = 0; j < 32; j += 8) { - if ((val & 0xff) == 0xff) - goto out; - printk("%c", val & 0xff); - val >>= 8; - } - } -out: - printk("\n"); - pci_iounmap(pdev, cis); - - return 0; -} - -static int __devinit pluto2_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct pluto *pluto; - struct dvb_adapter *dvb_adapter; - struct dvb_demux *dvbdemux; - struct dmx_demux *dmx; - int ret = -ENOMEM; - - pluto = kzalloc(sizeof(struct pluto), GFP_KERNEL); - if (!pluto) - goto out; - - pluto->pdev = pdev; - - ret = pci_enable_device(pdev); - if (ret < 0) - goto err_kfree; - - /* enable interrupts */ - pci_write_config_dword(pdev, 0x6c, 0x8000); - - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret < 0) - goto err_pci_disable_device; - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, DRIVER_NAME); - if (ret < 0) - goto err_pci_disable_device; - - pluto->io_mem = pci_iomap(pdev, 0, 0x40); - if (!pluto->io_mem) { - ret = -EIO; - goto err_pci_release_regions; - } - - pci_set_drvdata(pdev, pluto); - - ret = request_irq(pdev->irq, pluto_irq, IRQF_SHARED, DRIVER_NAME, pluto); - if (ret < 0) - goto err_pci_iounmap; - - ret = pluto_hw_init(pluto); - if (ret < 0) - goto err_free_irq; - - /* i2c */ - i2c_set_adapdata(&pluto->i2c_adap, pluto); - strcpy(pluto->i2c_adap.name, DRIVER_NAME); - pluto->i2c_adap.owner = THIS_MODULE; - pluto->i2c_adap.dev.parent = &pdev->dev; - pluto->i2c_adap.algo_data = &pluto->i2c_bit; - pluto->i2c_bit.data = pluto; - pluto->i2c_bit.setsda = pluto_setsda; - pluto->i2c_bit.setscl = pluto_setscl; - pluto->i2c_bit.getsda = pluto_getsda; - pluto->i2c_bit.getscl = pluto_getscl; - pluto->i2c_bit.udelay = 10; - pluto->i2c_bit.timeout = 10; - - /* Raise SCL and SDA */ - pluto_setsda(pluto, 1); - pluto_setscl(pluto, 1); - - ret = i2c_bit_add_bus(&pluto->i2c_adap); - if (ret < 0) - goto err_pluto_hw_exit; - - /* dvb */ - ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, - THIS_MODULE, &pdev->dev, adapter_nr); - if (ret < 0) - goto err_i2c_del_adapter; - - dvb_adapter = &pluto->dvb_adapter; - - pluto_read_rev(pluto); - pluto_read_serial(pluto); - pluto_read_mac(pluto, dvb_adapter->proposed_mac); - - dvbdemux = &pluto->demux; - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = pluto_start_feed; - dvbdemux->stop_feed = pluto_stop_feed; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | - DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); - ret = dvb_dmx_init(dvbdemux); - if (ret < 0) - goto err_dvb_unregister_adapter; - - dmx = &dvbdemux->dmx; - - pluto->hw_frontend.source = DMX_FRONTEND_0; - pluto->mem_frontend.source = DMX_MEMORY_FE; - pluto->dmxdev.filternum = NHWFILTERS; - pluto->dmxdev.demux = dmx; - - ret = dvb_dmxdev_init(&pluto->dmxdev, dvb_adapter); - if (ret < 0) - goto err_dvb_dmx_release; - - ret = dmx->add_frontend(dmx, &pluto->hw_frontend); - if (ret < 0) - goto err_dvb_dmxdev_release; - - ret = dmx->add_frontend(dmx, &pluto->mem_frontend); - if (ret < 0) - goto err_remove_hw_frontend; - - ret = dmx->connect_frontend(dmx, &pluto->hw_frontend); - if (ret < 0) - goto err_remove_mem_frontend; - - ret = frontend_init(pluto); - if (ret < 0) - goto err_disconnect_frontend; - - dvb_net_init(dvb_adapter, &pluto->dvbnet, dmx); -out: - return ret; - -err_disconnect_frontend: - dmx->disconnect_frontend(dmx); -err_remove_mem_frontend: - dmx->remove_frontend(dmx, &pluto->mem_frontend); -err_remove_hw_frontend: - dmx->remove_frontend(dmx, &pluto->hw_frontend); -err_dvb_dmxdev_release: - dvb_dmxdev_release(&pluto->dmxdev); -err_dvb_dmx_release: - dvb_dmx_release(dvbdemux); -err_dvb_unregister_adapter: - dvb_unregister_adapter(dvb_adapter); -err_i2c_del_adapter: - i2c_del_adapter(&pluto->i2c_adap); -err_pluto_hw_exit: - pluto_hw_exit(pluto); -err_free_irq: - free_irq(pdev->irq, pluto); -err_pci_iounmap: - pci_iounmap(pdev, pluto->io_mem); -err_pci_release_regions: - pci_release_regions(pdev); -err_pci_disable_device: - pci_disable_device(pdev); -err_kfree: - pci_set_drvdata(pdev, NULL); - kfree(pluto); - goto out; -} - -static void __devexit pluto2_remove(struct pci_dev *pdev) -{ - struct pluto *pluto = pci_get_drvdata(pdev); - struct dvb_adapter *dvb_adapter = &pluto->dvb_adapter; - struct dvb_demux *dvbdemux = &pluto->demux; - struct dmx_demux *dmx = &dvbdemux->dmx; - - dmx->close(dmx); - dvb_net_release(&pluto->dvbnet); - if (pluto->fe) - dvb_unregister_frontend(pluto->fe); - - dmx->disconnect_frontend(dmx); - dmx->remove_frontend(dmx, &pluto->mem_frontend); - dmx->remove_frontend(dmx, &pluto->hw_frontend); - dvb_dmxdev_release(&pluto->dmxdev); - dvb_dmx_release(dvbdemux); - dvb_unregister_adapter(dvb_adapter); - i2c_del_adapter(&pluto->i2c_adap); - pluto_hw_exit(pluto); - free_irq(pdev->irq, pluto); - pci_iounmap(pdev, pluto->io_mem); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - kfree(pluto); -} - -#ifndef PCI_VENDOR_ID_SCM -#define PCI_VENDOR_ID_SCM 0x0432 -#endif -#ifndef PCI_DEVICE_ID_PLUTO2 -#define PCI_DEVICE_ID_PLUTO2 0x0001 -#endif - -static struct pci_device_id pluto2_id_table[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_SCM, - .device = PCI_DEVICE_ID_PLUTO2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, { - /* empty */ - }, -}; - -MODULE_DEVICE_TABLE(pci, pluto2_id_table); - -static struct pci_driver pluto2_driver = { - .name = DRIVER_NAME, - .id_table = pluto2_id_table, - .probe = pluto2_probe, - .remove = __devexit_p(pluto2_remove), -}; - -static int __init pluto2_init(void) -{ - return pci_register_driver(&pluto2_driver); -} - -static void __exit pluto2_exit(void) -{ - pci_unregister_driver(&pluto2_driver); -} - -module_init(pluto2_init); -module_exit(pluto2_exit); - -MODULE_AUTHOR("Andreas Oberritter "); -MODULE_DESCRIPTION("Pluto2 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/pt1/Kconfig b/drivers/media/dvb/pt1/Kconfig deleted file mode 100644 index 24501d5bf70d..000000000000 --- a/drivers/media/dvb/pt1/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config DVB_PT1 - tristate "PT1 cards" - depends on DVB_CORE && PCI && I2C - help - Support for Earthsoft PT1 PCI cards. - - Since these cards have no MPEG decoder onboard, they transmit - only compressed MPEG data over the PCI bus, so you need - an external software decoder to watch TV on your computer. - - Say Y or M if you own such a device and want to use it. - diff --git a/drivers/media/dvb/pt1/Makefile b/drivers/media/dvb/pt1/Makefile deleted file mode 100644 index 98e391295afe..000000000000 --- a/drivers/media/dvb/pt1/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o - -obj-$(CONFIG_DVB_PT1) += earth-pt1.o - -ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c deleted file mode 100644 index 15b35c4725f1..000000000000 --- a/drivers/media/dvb/pt1/pt1.c +++ /dev/null @@ -1,1246 +0,0 @@ -/* - * driver for Earthsoft PT1/PT2 - * - * Copyright (C) 2009 HIRANO Takahito - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa - * - * This program is free software; 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 "dvbdev.h" -#include "dvb_demux.h" -#include "dmxdev.h" -#include "dvb_net.h" -#include "dvb_frontend.h" - -#include "va1j5jf8007t.h" -#include "va1j5jf8007s.h" - -#define DRIVER_NAME "earth-pt1" - -#define PT1_PAGE_SHIFT 12 -#define PT1_PAGE_SIZE (1 << PT1_PAGE_SHIFT) -#define PT1_NR_UPACKETS 1024 -#define PT1_NR_BUFS 511 - -struct pt1_buffer_page { - __le32 upackets[PT1_NR_UPACKETS]; -}; - -struct pt1_table_page { - __le32 next_pfn; - __le32 buf_pfns[PT1_NR_BUFS]; -}; - -struct pt1_buffer { - struct pt1_buffer_page *page; - dma_addr_t addr; -}; - -struct pt1_table { - struct pt1_table_page *page; - dma_addr_t addr; - struct pt1_buffer bufs[PT1_NR_BUFS]; -}; - -#define PT1_NR_ADAPS 4 - -struct pt1_adapter; - -struct pt1 { - struct pci_dev *pdev; - void __iomem *regs; - struct i2c_adapter i2c_adap; - int i2c_running; - struct pt1_adapter *adaps[PT1_NR_ADAPS]; - struct pt1_table *tables; - struct task_struct *kthread; - int table_index; - int buf_index; - - struct mutex lock; - int power; - int reset; -}; - -struct pt1_adapter { - struct pt1 *pt1; - int index; - - u8 *buf; - int upacket_count; - int packet_count; - int st_count; - - struct dvb_adapter adap; - struct dvb_demux demux; - int users; - struct dmxdev dmxdev; - struct dvb_frontend *fe; - int (*orig_set_voltage)(struct dvb_frontend *fe, - fe_sec_voltage_t voltage); - int (*orig_sleep)(struct dvb_frontend *fe); - int (*orig_init)(struct dvb_frontend *fe); - - fe_sec_voltage_t voltage; - int sleep; -}; - -#define pt1_printk(level, pt1, format, arg...) \ - dev_printk(level, &(pt1)->pdev->dev, format, ##arg) - -static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) -{ - writel(data, pt1->regs + reg * 4); -} - -static u32 pt1_read_reg(struct pt1 *pt1, int reg) -{ - return readl(pt1->regs + reg * 4); -} - -static int pt1_nr_tables = 8; -module_param_named(nr_tables, pt1_nr_tables, int, 0); - -static void pt1_increment_table_count(struct pt1 *pt1) -{ - pt1_write_reg(pt1, 0, 0x00000020); -} - -static void pt1_init_table_count(struct pt1 *pt1) -{ - pt1_write_reg(pt1, 0, 0x00000010); -} - -static void pt1_register_tables(struct pt1 *pt1, u32 first_pfn) -{ - pt1_write_reg(pt1, 5, first_pfn); - pt1_write_reg(pt1, 0, 0x0c000040); -} - -static void pt1_unregister_tables(struct pt1 *pt1) -{ - pt1_write_reg(pt1, 0, 0x08080000); -} - -static int pt1_sync(struct pt1 *pt1) -{ - int i; - for (i = 0; i < 57; i++) { - if (pt1_read_reg(pt1, 0) & 0x20000000) - return 0; - pt1_write_reg(pt1, 0, 0x00000008); - } - pt1_printk(KERN_ERR, pt1, "could not sync\n"); - return -EIO; -} - -static u64 pt1_identify(struct pt1 *pt1) -{ - int i; - u64 id; - id = 0; - for (i = 0; i < 57; i++) { - id |= (u64)(pt1_read_reg(pt1, 0) >> 30 & 1) << i; - pt1_write_reg(pt1, 0, 0x00000008); - } - return id; -} - -static int pt1_unlock(struct pt1 *pt1) -{ - int i; - pt1_write_reg(pt1, 0, 0x00000008); - for (i = 0; i < 3; i++) { - if (pt1_read_reg(pt1, 0) & 0x80000000) - return 0; - schedule_timeout_uninterruptible((HZ + 999) / 1000); - } - pt1_printk(KERN_ERR, pt1, "could not unlock\n"); - return -EIO; -} - -static int pt1_reset_pci(struct pt1 *pt1) -{ - int i; - pt1_write_reg(pt1, 0, 0x01010000); - pt1_write_reg(pt1, 0, 0x01000000); - for (i = 0; i < 10; i++) { - if (pt1_read_reg(pt1, 0) & 0x00000001) - return 0; - schedule_timeout_uninterruptible((HZ + 999) / 1000); - } - pt1_printk(KERN_ERR, pt1, "could not reset PCI\n"); - return -EIO; -} - -static int pt1_reset_ram(struct pt1 *pt1) -{ - int i; - pt1_write_reg(pt1, 0, 0x02020000); - pt1_write_reg(pt1, 0, 0x02000000); - for (i = 0; i < 10; i++) { - if (pt1_read_reg(pt1, 0) & 0x00000002) - return 0; - schedule_timeout_uninterruptible((HZ + 999) / 1000); - } - pt1_printk(KERN_ERR, pt1, "could not reset RAM\n"); - return -EIO; -} - -static int pt1_do_enable_ram(struct pt1 *pt1) -{ - int i, j; - u32 status; - status = pt1_read_reg(pt1, 0) & 0x00000004; - pt1_write_reg(pt1, 0, 0x00000002); - for (i = 0; i < 10; i++) { - for (j = 0; j < 1024; j++) { - if ((pt1_read_reg(pt1, 0) & 0x00000004) != status) - return 0; - } - schedule_timeout_uninterruptible((HZ + 999) / 1000); - } - pt1_printk(KERN_ERR, pt1, "could not enable RAM\n"); - return -EIO; -} - -static int pt1_enable_ram(struct pt1 *pt1) -{ - int i, ret; - int phase; - schedule_timeout_uninterruptible((HZ + 999) / 1000); - phase = pt1->pdev->device == 0x211a ? 128 : 166; - for (i = 0; i < phase; i++) { - ret = pt1_do_enable_ram(pt1); - if (ret < 0) - return ret; - } - return 0; -} - -static void pt1_disable_ram(struct pt1 *pt1) -{ - pt1_write_reg(pt1, 0, 0x0b0b0000); -} - -static void pt1_set_stream(struct pt1 *pt1, int index, int enabled) -{ - pt1_write_reg(pt1, 2, 1 << (index + 8) | enabled << index); -} - -static void pt1_init_streams(struct pt1 *pt1) -{ - int i; - for (i = 0; i < PT1_NR_ADAPS; i++) - pt1_set_stream(pt1, i, 0); -} - -static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) -{ - u32 upacket; - int i; - int index; - struct pt1_adapter *adap; - int offset; - u8 *buf; - int sc; - - if (!page->upackets[PT1_NR_UPACKETS - 1]) - return 0; - - for (i = 0; i < PT1_NR_UPACKETS; i++) { - upacket = le32_to_cpu(page->upackets[i]); - index = (upacket >> 29) - 1; - if (index < 0 || index >= PT1_NR_ADAPS) - continue; - - adap = pt1->adaps[index]; - if (upacket >> 25 & 1) - adap->upacket_count = 0; - else if (!adap->upacket_count) - continue; - - if (upacket >> 24 & 1) - printk_ratelimited(KERN_INFO "earth-pt1: device " - "buffer overflowing. table[%d] buf[%d]\n", - pt1->table_index, pt1->buf_index); - sc = upacket >> 26 & 0x7; - if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7)) - printk_ratelimited(KERN_INFO "earth-pt1: data loss" - " in streamID(adapter)[%d]\n", index); - adap->st_count = sc; - - buf = adap->buf; - offset = adap->packet_count * 188 + adap->upacket_count * 3; - buf[offset] = upacket >> 16; - buf[offset + 1] = upacket >> 8; - if (adap->upacket_count != 62) - buf[offset + 2] = upacket; - - if (++adap->upacket_count >= 63) { - adap->upacket_count = 0; - if (++adap->packet_count >= 21) { - dvb_dmx_swfilter_packets(&adap->demux, buf, 21); - adap->packet_count = 0; - } - } - } - - page->upackets[PT1_NR_UPACKETS - 1] = 0; - return 1; -} - -static int pt1_thread(void *data) -{ - struct pt1 *pt1; - struct pt1_buffer_page *page; - - pt1 = data; - set_freezable(); - - while (!kthread_should_stop()) { - try_to_freeze(); - - page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page; - if (!pt1_filter(pt1, page)) { - schedule_timeout_interruptible((HZ + 999) / 1000); - continue; - } - - if (++pt1->buf_index >= PT1_NR_BUFS) { - pt1_increment_table_count(pt1); - pt1->buf_index = 0; - if (++pt1->table_index >= pt1_nr_tables) - pt1->table_index = 0; - } - } - - return 0; -} - -static void pt1_free_page(struct pt1 *pt1, void *page, dma_addr_t addr) -{ - dma_free_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, page, addr); -} - -static void *pt1_alloc_page(struct pt1 *pt1, dma_addr_t *addrp, u32 *pfnp) -{ - void *page; - dma_addr_t addr; - - page = dma_alloc_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, &addr, - GFP_KERNEL); - if (page == NULL) - return NULL; - - BUG_ON(addr & (PT1_PAGE_SIZE - 1)); - BUG_ON(addr >> PT1_PAGE_SHIFT >> 31 >> 1); - - *addrp = addr; - *pfnp = addr >> PT1_PAGE_SHIFT; - return page; -} - -static void pt1_cleanup_buffer(struct pt1 *pt1, struct pt1_buffer *buf) -{ - pt1_free_page(pt1, buf->page, buf->addr); -} - -static int -pt1_init_buffer(struct pt1 *pt1, struct pt1_buffer *buf, u32 *pfnp) -{ - struct pt1_buffer_page *page; - dma_addr_t addr; - - page = pt1_alloc_page(pt1, &addr, pfnp); - if (page == NULL) - return -ENOMEM; - - page->upackets[PT1_NR_UPACKETS - 1] = 0; - - buf->page = page; - buf->addr = addr; - return 0; -} - -static void pt1_cleanup_table(struct pt1 *pt1, struct pt1_table *table) -{ - int i; - - for (i = 0; i < PT1_NR_BUFS; i++) - pt1_cleanup_buffer(pt1, &table->bufs[i]); - - pt1_free_page(pt1, table->page, table->addr); -} - -static int -pt1_init_table(struct pt1 *pt1, struct pt1_table *table, u32 *pfnp) -{ - struct pt1_table_page *page; - dma_addr_t addr; - int i, ret; - u32 buf_pfn; - - page = pt1_alloc_page(pt1, &addr, pfnp); - if (page == NULL) - return -ENOMEM; - - for (i = 0; i < PT1_NR_BUFS; i++) { - ret = pt1_init_buffer(pt1, &table->bufs[i], &buf_pfn); - if (ret < 0) - goto err; - - page->buf_pfns[i] = cpu_to_le32(buf_pfn); - } - - pt1_increment_table_count(pt1); - table->page = page; - table->addr = addr; - return 0; - -err: - while (i--) - pt1_cleanup_buffer(pt1, &table->bufs[i]); - - pt1_free_page(pt1, page, addr); - return ret; -} - -static void pt1_cleanup_tables(struct pt1 *pt1) -{ - struct pt1_table *tables; - int i; - - tables = pt1->tables; - pt1_unregister_tables(pt1); - - for (i = 0; i < pt1_nr_tables; i++) - pt1_cleanup_table(pt1, &tables[i]); - - vfree(tables); -} - -static int pt1_init_tables(struct pt1 *pt1) -{ - struct pt1_table *tables; - int i, ret; - u32 first_pfn, pfn; - - tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables); - if (tables == NULL) - return -ENOMEM; - - pt1_init_table_count(pt1); - - i = 0; - if (pt1_nr_tables) { - ret = pt1_init_table(pt1, &tables[0], &first_pfn); - if (ret) - goto err; - i++; - } - - while (i < pt1_nr_tables) { - ret = pt1_init_table(pt1, &tables[i], &pfn); - if (ret) - goto err; - tables[i - 1].page->next_pfn = cpu_to_le32(pfn); - i++; - } - - tables[pt1_nr_tables - 1].page->next_pfn = cpu_to_le32(first_pfn); - - pt1_register_tables(pt1, first_pfn); - pt1->tables = tables; - return 0; - -err: - while (i--) - pt1_cleanup_table(pt1, &tables[i]); - - vfree(tables); - return ret; -} - -static int pt1_start_polling(struct pt1 *pt1) -{ - int ret = 0; - - mutex_lock(&pt1->lock); - if (!pt1->kthread) { - pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1"); - if (IS_ERR(pt1->kthread)) { - ret = PTR_ERR(pt1->kthread); - pt1->kthread = NULL; - } - } - mutex_unlock(&pt1->lock); - return ret; -} - -static int pt1_start_feed(struct dvb_demux_feed *feed) -{ - struct pt1_adapter *adap; - adap = container_of(feed->demux, struct pt1_adapter, demux); - if (!adap->users++) { - int ret; - - ret = pt1_start_polling(adap->pt1); - if (ret) - return ret; - pt1_set_stream(adap->pt1, adap->index, 1); - } - return 0; -} - -static void pt1_stop_polling(struct pt1 *pt1) -{ - int i, count; - - mutex_lock(&pt1->lock); - for (i = 0, count = 0; i < PT1_NR_ADAPS; i++) - count += pt1->adaps[i]->users; - - if (count == 0 && pt1->kthread) { - kthread_stop(pt1->kthread); - pt1->kthread = NULL; - } - mutex_unlock(&pt1->lock); -} - -static int pt1_stop_feed(struct dvb_demux_feed *feed) -{ - struct pt1_adapter *adap; - adap = container_of(feed->demux, struct pt1_adapter, demux); - if (!--adap->users) { - pt1_set_stream(adap->pt1, adap->index, 0); - pt1_stop_polling(adap->pt1); - } - return 0; -} - -static void -pt1_update_power(struct pt1 *pt1) -{ - int bits; - int i; - struct pt1_adapter *adap; - static const int sleep_bits[] = { - 1 << 4, - 1 << 6 | 1 << 7, - 1 << 5, - 1 << 6 | 1 << 8, - }; - - bits = pt1->power | !pt1->reset << 3; - mutex_lock(&pt1->lock); - for (i = 0; i < PT1_NR_ADAPS; i++) { - adap = pt1->adaps[i]; - switch (adap->voltage) { - case SEC_VOLTAGE_13: /* actually 11V */ - bits |= 1 << 1; - break; - case SEC_VOLTAGE_18: /* actually 15V */ - bits |= 1 << 1 | 1 << 2; - break; - default: - break; - } - - /* XXX: The bits should be changed depending on adap->sleep. */ - bits |= sleep_bits[i]; - } - pt1_write_reg(pt1, 1, bits); - mutex_unlock(&pt1->lock); -} - -static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct pt1_adapter *adap; - - adap = container_of(fe->dvb, struct pt1_adapter, adap); - adap->voltage = voltage; - pt1_update_power(adap->pt1); - - if (adap->orig_set_voltage) - return adap->orig_set_voltage(fe, voltage); - else - return 0; -} - -static int pt1_sleep(struct dvb_frontend *fe) -{ - struct pt1_adapter *adap; - - adap = container_of(fe->dvb, struct pt1_adapter, adap); - adap->sleep = 1; - pt1_update_power(adap->pt1); - - if (adap->orig_sleep) - return adap->orig_sleep(fe); - else - return 0; -} - -static int pt1_wakeup(struct dvb_frontend *fe) -{ - struct pt1_adapter *adap; - - adap = container_of(fe->dvb, struct pt1_adapter, adap); - adap->sleep = 0; - pt1_update_power(adap->pt1); - schedule_timeout_uninterruptible((HZ + 999) / 1000); - - if (adap->orig_init) - return adap->orig_init(fe); - else - return 0; -} - -static void pt1_free_adapter(struct pt1_adapter *adap) -{ - adap->demux.dmx.close(&adap->demux.dmx); - dvb_dmxdev_release(&adap->dmxdev); - dvb_dmx_release(&adap->demux); - dvb_unregister_adapter(&adap->adap); - free_page((unsigned long)adap->buf); - kfree(adap); -} - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static struct pt1_adapter * -pt1_alloc_adapter(struct pt1 *pt1) -{ - struct pt1_adapter *adap; - void *buf; - struct dvb_adapter *dvb_adap; - struct dvb_demux *demux; - struct dmxdev *dmxdev; - int ret; - - adap = kzalloc(sizeof(struct pt1_adapter), GFP_KERNEL); - if (!adap) { - ret = -ENOMEM; - goto err; - } - - adap->pt1 = pt1; - - adap->voltage = SEC_VOLTAGE_OFF; - adap->sleep = 1; - - buf = (u8 *)__get_free_page(GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto err_kfree; - } - - adap->buf = buf; - adap->upacket_count = 0; - adap->packet_count = 0; - adap->st_count = -1; - - dvb_adap = &adap->adap; - dvb_adap->priv = adap; - ret = dvb_register_adapter(dvb_adap, DRIVER_NAME, THIS_MODULE, - &pt1->pdev->dev, adapter_nr); - if (ret < 0) - goto err_free_page; - - demux = &adap->demux; - demux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; - demux->priv = adap; - demux->feednum = 256; - demux->filternum = 256; - demux->start_feed = pt1_start_feed; - demux->stop_feed = pt1_stop_feed; - demux->write_to_decoder = NULL; - ret = dvb_dmx_init(demux); - if (ret < 0) - goto err_unregister_adapter; - - dmxdev = &adap->dmxdev; - dmxdev->filternum = 256; - dmxdev->demux = &demux->dmx; - dmxdev->capabilities = 0; - ret = dvb_dmxdev_init(dmxdev, dvb_adap); - if (ret < 0) - goto err_dmx_release; - - return adap; - -err_dmx_release: - dvb_dmx_release(demux); -err_unregister_adapter: - dvb_unregister_adapter(dvb_adap); -err_free_page: - free_page((unsigned long)buf); -err_kfree: - kfree(adap); -err: - return ERR_PTR(ret); -} - -static void pt1_cleanup_adapters(struct pt1 *pt1) -{ - int i; - for (i = 0; i < PT1_NR_ADAPS; i++) - pt1_free_adapter(pt1->adaps[i]); -} - -static int pt1_init_adapters(struct pt1 *pt1) -{ - int i; - struct pt1_adapter *adap; - int ret; - - for (i = 0; i < PT1_NR_ADAPS; i++) { - adap = pt1_alloc_adapter(pt1); - if (IS_ERR(adap)) { - ret = PTR_ERR(adap); - goto err; - } - - adap->index = i; - pt1->adaps[i] = adap; - } - return 0; - -err: - while (i--) - pt1_free_adapter(pt1->adaps[i]); - - return ret; -} - -static void pt1_cleanup_frontend(struct pt1_adapter *adap) -{ - dvb_unregister_frontend(adap->fe); -} - -static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe) -{ - int ret; - - adap->orig_set_voltage = fe->ops.set_voltage; - adap->orig_sleep = fe->ops.sleep; - adap->orig_init = fe->ops.init; - fe->ops.set_voltage = pt1_set_voltage; - fe->ops.sleep = pt1_sleep; - fe->ops.init = pt1_wakeup; - - ret = dvb_register_frontend(&adap->adap, fe); - if (ret < 0) - return ret; - - adap->fe = fe; - return 0; -} - -static void pt1_cleanup_frontends(struct pt1 *pt1) -{ - int i; - for (i = 0; i < PT1_NR_ADAPS; i++) - pt1_cleanup_frontend(pt1->adaps[i]); -} - -struct pt1_config { - struct va1j5jf8007s_config va1j5jf8007s_config; - struct va1j5jf8007t_config va1j5jf8007t_config; -}; - -static const struct pt1_config pt1_configs[2] = { - { - { - .demod_address = 0x1b, - .frequency = VA1J5JF8007S_20MHZ, - }, - { - .demod_address = 0x1a, - .frequency = VA1J5JF8007T_20MHZ, - }, - }, { - { - .demod_address = 0x19, - .frequency = VA1J5JF8007S_20MHZ, - }, - { - .demod_address = 0x18, - .frequency = VA1J5JF8007T_20MHZ, - }, - }, -}; - -static const struct pt1_config pt2_configs[2] = { - { - { - .demod_address = 0x1b, - .frequency = VA1J5JF8007S_25MHZ, - }, - { - .demod_address = 0x1a, - .frequency = VA1J5JF8007T_25MHZ, - }, - }, { - { - .demod_address = 0x19, - .frequency = VA1J5JF8007S_25MHZ, - }, - { - .demod_address = 0x18, - .frequency = VA1J5JF8007T_25MHZ, - }, - }, -}; - -static int pt1_init_frontends(struct pt1 *pt1) -{ - int i, j; - struct i2c_adapter *i2c_adap; - const struct pt1_config *configs, *config; - struct dvb_frontend *fe[4]; - int ret; - - i = 0; - j = 0; - - i2c_adap = &pt1->i2c_adap; - configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs; - do { - config = &configs[i / 2]; - - fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, - i2c_adap); - if (!fe[i]) { - ret = -ENODEV; /* This does not sound nice... */ - goto err; - } - i++; - - fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config, - i2c_adap); - if (!fe[i]) { - ret = -ENODEV; - goto err; - } - i++; - - ret = va1j5jf8007s_prepare(fe[i - 2]); - if (ret < 0) - goto err; - - ret = va1j5jf8007t_prepare(fe[i - 1]); - if (ret < 0) - goto err; - - } while (i < 4); - - do { - ret = pt1_init_frontend(pt1->adaps[j], fe[j]); - if (ret < 0) - goto err; - } while (++j < 4); - - return 0; - -err: - while (i-- > j) - fe[i]->ops.release(fe[i]); - - while (j--) - dvb_unregister_frontend(fe[j]); - - return ret; -} - -static void pt1_i2c_emit(struct pt1 *pt1, int addr, int busy, int read_enable, - int clock, int data, int next_addr) -{ - pt1_write_reg(pt1, 4, addr << 18 | busy << 13 | read_enable << 12 | - !clock << 11 | !data << 10 | next_addr); -} - -static void pt1_i2c_write_bit(struct pt1 *pt1, int addr, int *addrp, int data) -{ - pt1_i2c_emit(pt1, addr, 1, 0, 0, data, addr + 1); - pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, data, addr + 2); - pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, data, addr + 3); - *addrp = addr + 3; -} - -static void pt1_i2c_read_bit(struct pt1 *pt1, int addr, int *addrp) -{ - pt1_i2c_emit(pt1, addr, 1, 0, 0, 1, addr + 1); - pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 1, addr + 2); - pt1_i2c_emit(pt1, addr + 2, 1, 1, 1, 1, addr + 3); - pt1_i2c_emit(pt1, addr + 3, 1, 0, 0, 1, addr + 4); - *addrp = addr + 4; -} - -static void pt1_i2c_write_byte(struct pt1 *pt1, int addr, int *addrp, int data) -{ - int i; - for (i = 0; i < 8; i++) - pt1_i2c_write_bit(pt1, addr, &addr, data >> (7 - i) & 1); - pt1_i2c_write_bit(pt1, addr, &addr, 1); - *addrp = addr; -} - -static void pt1_i2c_read_byte(struct pt1 *pt1, int addr, int *addrp, int last) -{ - int i; - for (i = 0; i < 8; i++) - pt1_i2c_read_bit(pt1, addr, &addr); - pt1_i2c_write_bit(pt1, addr, &addr, last); - *addrp = addr; -} - -static void pt1_i2c_prepare(struct pt1 *pt1, int addr, int *addrp) -{ - pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); - pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); - pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, 0, addr + 3); - *addrp = addr + 3; -} - -static void -pt1_i2c_write_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) -{ - int i; - pt1_i2c_prepare(pt1, addr, &addr); - pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1); - for (i = 0; i < msg->len; i++) - pt1_i2c_write_byte(pt1, addr, &addr, msg->buf[i]); - *addrp = addr; -} - -static void -pt1_i2c_read_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) -{ - int i; - pt1_i2c_prepare(pt1, addr, &addr); - pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1 | 1); - for (i = 0; i < msg->len; i++) - pt1_i2c_read_byte(pt1, addr, &addr, i == msg->len - 1); - *addrp = addr; -} - -static int pt1_i2c_end(struct pt1 *pt1, int addr) -{ - pt1_i2c_emit(pt1, addr, 1, 0, 0, 0, addr + 1); - pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); - pt1_i2c_emit(pt1, addr + 2, 1, 0, 1, 1, 0); - - pt1_write_reg(pt1, 0, 0x00000004); - do { - if (signal_pending(current)) - return -EINTR; - schedule_timeout_interruptible((HZ + 999) / 1000); - } while (pt1_read_reg(pt1, 0) & 0x00000080); - return 0; -} - -static void pt1_i2c_begin(struct pt1 *pt1, int *addrp) -{ - int addr; - addr = 0; - - pt1_i2c_emit(pt1, addr, 0, 0, 1, 1, addr /* itself */); - addr = addr + 1; - - if (!pt1->i2c_running) { - pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); - pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); - addr = addr + 2; - pt1->i2c_running = 1; - } - *addrp = addr; -} - -static int pt1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) -{ - struct pt1 *pt1; - int i; - struct i2c_msg *msg, *next_msg; - int addr, ret; - u16 len; - u32 word; - - pt1 = i2c_get_adapdata(adap); - - for (i = 0; i < num; i++) { - msg = &msgs[i]; - if (msg->flags & I2C_M_RD) - return -ENOTSUPP; - - if (i + 1 < num) - next_msg = &msgs[i + 1]; - else - next_msg = NULL; - - if (next_msg && next_msg->flags & I2C_M_RD) { - i++; - - len = next_msg->len; - if (len > 4) - return -ENOTSUPP; - - pt1_i2c_begin(pt1, &addr); - pt1_i2c_write_msg(pt1, addr, &addr, msg); - pt1_i2c_read_msg(pt1, addr, &addr, next_msg); - ret = pt1_i2c_end(pt1, addr); - if (ret < 0) - return ret; - - word = pt1_read_reg(pt1, 2); - while (len--) { - next_msg->buf[len] = word; - word >>= 8; - } - } else { - pt1_i2c_begin(pt1, &addr); - pt1_i2c_write_msg(pt1, addr, &addr, msg); - ret = pt1_i2c_end(pt1, addr); - if (ret < 0) - return ret; - } - } - - return num; -} - -static u32 pt1_i2c_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C; -} - -static const struct i2c_algorithm pt1_i2c_algo = { - .master_xfer = pt1_i2c_xfer, - .functionality = pt1_i2c_func, -}; - -static void pt1_i2c_wait(struct pt1 *pt1) -{ - int i; - for (i = 0; i < 128; i++) - pt1_i2c_emit(pt1, 0, 0, 0, 1, 1, 0); -} - -static void pt1_i2c_init(struct pt1 *pt1) -{ - int i; - for (i = 0; i < 1024; i++) - pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0); -} - -static void __devexit pt1_remove(struct pci_dev *pdev) -{ - struct pt1 *pt1; - void __iomem *regs; - - pt1 = pci_get_drvdata(pdev); - regs = pt1->regs; - - if (pt1->kthread) - kthread_stop(pt1->kthread); - pt1_cleanup_tables(pt1); - pt1_cleanup_frontends(pt1); - pt1_disable_ram(pt1); - pt1->power = 0; - pt1->reset = 1; - pt1_update_power(pt1); - pt1_cleanup_adapters(pt1); - i2c_del_adapter(&pt1->i2c_adap); - pci_set_drvdata(pdev, NULL); - kfree(pt1); - pci_iounmap(pdev, regs); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static int __devinit -pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int ret; - void __iomem *regs; - struct pt1 *pt1; - struct i2c_adapter *i2c_adap; - - ret = pci_enable_device(pdev); - if (ret < 0) - goto err; - - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret < 0) - goto err_pci_disable_device; - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, DRIVER_NAME); - if (ret < 0) - goto err_pci_disable_device; - - regs = pci_iomap(pdev, 0, 0); - if (!regs) { - ret = -EIO; - goto err_pci_release_regions; - } - - pt1 = kzalloc(sizeof(struct pt1), GFP_KERNEL); - if (!pt1) { - ret = -ENOMEM; - goto err_pci_iounmap; - } - - mutex_init(&pt1->lock); - pt1->pdev = pdev; - pt1->regs = regs; - pci_set_drvdata(pdev, pt1); - - ret = pt1_init_adapters(pt1); - if (ret < 0) - goto err_kfree; - - mutex_init(&pt1->lock); - - pt1->power = 0; - pt1->reset = 1; - pt1_update_power(pt1); - - i2c_adap = &pt1->i2c_adap; - i2c_adap->algo = &pt1_i2c_algo; - i2c_adap->algo_data = NULL; - i2c_adap->dev.parent = &pdev->dev; - strcpy(i2c_adap->name, DRIVER_NAME); - i2c_set_adapdata(i2c_adap, pt1); - ret = i2c_add_adapter(i2c_adap); - if (ret < 0) - goto err_pt1_cleanup_adapters; - - pt1_i2c_init(pt1); - pt1_i2c_wait(pt1); - - ret = pt1_sync(pt1); - if (ret < 0) - goto err_i2c_del_adapter; - - pt1_identify(pt1); - - ret = pt1_unlock(pt1); - if (ret < 0) - goto err_i2c_del_adapter; - - ret = pt1_reset_pci(pt1); - if (ret < 0) - goto err_i2c_del_adapter; - - ret = pt1_reset_ram(pt1); - if (ret < 0) - goto err_i2c_del_adapter; - - ret = pt1_enable_ram(pt1); - if (ret < 0) - goto err_i2c_del_adapter; - - pt1_init_streams(pt1); - - pt1->power = 1; - pt1_update_power(pt1); - schedule_timeout_uninterruptible((HZ + 49) / 50); - - pt1->reset = 0; - pt1_update_power(pt1); - schedule_timeout_uninterruptible((HZ + 999) / 1000); - - ret = pt1_init_frontends(pt1); - if (ret < 0) - goto err_pt1_disable_ram; - - ret = pt1_init_tables(pt1); - if (ret < 0) - goto err_pt1_cleanup_frontends; - - return 0; - -err_pt1_cleanup_frontends: - pt1_cleanup_frontends(pt1); -err_pt1_disable_ram: - pt1_disable_ram(pt1); - pt1->power = 0; - pt1->reset = 1; - pt1_update_power(pt1); -err_i2c_del_adapter: - i2c_del_adapter(i2c_adap); -err_pt1_cleanup_adapters: - pt1_cleanup_adapters(pt1); -err_kfree: - pci_set_drvdata(pdev, NULL); - kfree(pt1); -err_pci_iounmap: - pci_iounmap(pdev, regs); -err_pci_release_regions: - pci_release_regions(pdev); -err_pci_disable_device: - pci_disable_device(pdev); -err: - return ret; - -} - -static struct pci_device_id pt1_id_table[] = { - { PCI_DEVICE(0x10ee, 0x211a) }, - { PCI_DEVICE(0x10ee, 0x222a) }, - { }, -}; -MODULE_DEVICE_TABLE(pci, pt1_id_table); - -static struct pci_driver pt1_driver = { - .name = DRIVER_NAME, - .probe = pt1_probe, - .remove = __devexit_p(pt1_remove), - .id_table = pt1_id_table, -}; - - -static int __init pt1_init(void) -{ - return pci_register_driver(&pt1_driver); -} - - -static void __exit pt1_cleanup(void) -{ - pci_unregister_driver(&pt1_driver); -} - -module_init(pt1_init); -module_exit(pt1_cleanup); - -MODULE_AUTHOR("Takahito HIRANO "); -MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c deleted file mode 100644 index d980dfb21e5e..000000000000 --- a/drivers/media/dvb/pt1/va1j5jf8007s.c +++ /dev/null @@ -1,735 +0,0 @@ -/* - * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa - * - * This program is free software; 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 "dvb_frontend.h" -#include "va1j5jf8007s.h" - -enum va1j5jf8007s_tune_state { - VA1J5JF8007S_IDLE, - VA1J5JF8007S_SET_FREQUENCY_1, - VA1J5JF8007S_SET_FREQUENCY_2, - VA1J5JF8007S_SET_FREQUENCY_3, - VA1J5JF8007S_CHECK_FREQUENCY, - VA1J5JF8007S_SET_MODULATION, - VA1J5JF8007S_CHECK_MODULATION, - VA1J5JF8007S_SET_TS_ID, - VA1J5JF8007S_CHECK_TS_ID, - VA1J5JF8007S_TRACK, -}; - -struct va1j5jf8007s_state { - const struct va1j5jf8007s_config *config; - struct i2c_adapter *adap; - struct dvb_frontend fe; - enum va1j5jf8007s_tune_state tune_state; -}; - -static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct va1j5jf8007s_state *state; - u8 addr; - int i; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - s32 word, x1, x2, x3, x4, x5, y; - - state = fe->demodulator_priv; - addr = state->config->demod_address; - - word = 0; - for (i = 0; i < 2; i++) { - write_buf[0] = 0xbc + i; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - word <<= 8; - word |= read_buf[0]; - } - - word -= 3000; - if (word < 0) - word = 0; - - x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000); - x2 = (s64)x1 * x1 >> 31; - x3 = (s64)x2 * x1 >> 31; - x4 = (s64)x2 * x2 >> 31; - x5 = (s64)x4 * x1 >> 31; - - y = (58857ll << 23) / 1000; - y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30; - y += (s64)x2 * ((88977ll << 24) / 1000) >> 28; - y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27; - y += (s64)x4 * ((14341ll << 27) / 1000) >> 27; - y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28; - - *snr = y < 0 ? 0 : y >> 15; - return 0; -} - -static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) -{ - return DVBFE_ALGO_HW; -} - -static int -va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status) -{ - struct va1j5jf8007s_state *state; - - state = fe->demodulator_priv; - - switch (state->tune_state) { - case VA1J5JF8007S_IDLE: - case VA1J5JF8007S_SET_FREQUENCY_1: - case VA1J5JF8007S_SET_FREQUENCY_2: - case VA1J5JF8007S_SET_FREQUENCY_3: - case VA1J5JF8007S_CHECK_FREQUENCY: - *status = 0; - return 0; - - - case VA1J5JF8007S_SET_MODULATION: - case VA1J5JF8007S_CHECK_MODULATION: - *status |= FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007S_SET_TS_ID: - case VA1J5JF8007S_CHECK_TS_ID: - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; - return 0; - - case VA1J5JF8007S_TRACK: - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - } - - BUG(); -} - -struct va1j5jf8007s_cb_map { - u32 frequency; - u8 cb; -}; - -static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = { - { 986000, 0xb2 }, - { 1072000, 0xd2 }, - { 1154000, 0xe2 }, - { 1291000, 0x20 }, - { 1447000, 0x40 }, - { 1615000, 0x60 }, - { 1791000, 0x80 }, - { 1972000, 0xa0 }, -}; - -static u8 va1j5jf8007s_lookup_cb(u32 frequency) -{ - int i; - const struct va1j5jf8007s_cb_map *map; - - for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) { - map = &va1j5jf8007s_cb_maps[i]; - if (frequency < map->frequency) - return map->cb; - } - return 0xc0; -} - -static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state) -{ - u32 frequency; - u16 word; - u8 buf[6]; - struct i2c_msg msg; - - frequency = state->fe.dtv_property_cache.frequency; - - word = (frequency + 500) / 1000; - if (frequency < 1072000) - word = (word << 1 & ~0x1f) | (word & 0x0f); - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0x40 | word >> 8; - buf[3] = word; - buf[4] = 0xe0; - buf[5] = va1j5jf8007s_lookup_cb(frequency); - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state) -{ - u8 buf[3]; - struct i2c_msg msg; - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0xe4; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state) -{ - u32 frequency; - u8 buf[4]; - struct i2c_msg msg; - - frequency = state->fe.dtv_property_cache.frequency; - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0xf4; - buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock) -{ - u8 addr; - u8 write_buf[2], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0xfe; - write_buf[1] = 0xc1; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = read_buf[0] & 0x40; - return 0; -} - -static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x03; - buf[1] = 0x01; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock) -{ - u8 addr; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0xc3; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = !(read_buf[0] & 0x10); - return 0; -} - -static int -va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state) -{ - u32 ts_id; - u8 buf[3]; - struct i2c_msg msg; - - ts_id = state->fe.dtv_property_cache.isdbs_ts_id; - if (!ts_id) - return 0; - - buf[0] = 0x8f; - buf[1] = ts_id >> 8; - buf[2] = ts_id; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock) -{ - u8 addr; - u8 write_buf[1], read_buf[2]; - struct i2c_msg msgs[2]; - u32 ts_id; - - ts_id = state->fe.dtv_property_cache.isdbs_ts_id; - if (!ts_id) { - *lock = 1; - return 0; - } - - addr = state->config->demod_address; - - write_buf[0] = 0xe6; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id; - return 0; -} - -static int -va1j5jf8007s_tune(struct dvb_frontend *fe, - bool re_tune, - unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) -{ - struct va1j5jf8007s_state *state; - int ret; - int lock = 0; - - state = fe->demodulator_priv; - - if (re_tune) - state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1; - - switch (state->tune_state) { - case VA1J5JF8007S_IDLE: - *delay = 3 * HZ; - *status = 0; - return 0; - - case VA1J5JF8007S_SET_FREQUENCY_1: - ret = va1j5jf8007s_set_frequency_1(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2; - *delay = 0; - *status = 0; - return 0; - - case VA1J5JF8007S_SET_FREQUENCY_2: - ret = va1j5jf8007s_set_frequency_2(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3; - *delay = (HZ + 99) / 100; - *status = 0; - return 0; - - case VA1J5JF8007S_SET_FREQUENCY_3: - ret = va1j5jf8007s_set_frequency_3(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY; - *delay = 0; - *status = 0; - return 0; - - case VA1J5JF8007S_CHECK_FREQUENCY: - ret = va1j5jf8007s_check_frequency(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 999) / 1000; - *status = 0; - return 0; - } - - state->tune_state = VA1J5JF8007S_SET_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007S_SET_MODULATION: - ret = va1j5jf8007s_set_modulation(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_CHECK_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007S_CHECK_MODULATION: - ret = va1j5jf8007s_check_modulation(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 49) / 50; - *status = FE_HAS_SIGNAL; - return 0; - } - - state->tune_state = VA1J5JF8007S_SET_TS_ID; - *delay = 0; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; - return 0; - - case VA1J5JF8007S_SET_TS_ID: - ret = va1j5jf8007s_set_ts_id(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_CHECK_TS_ID; - return 0; - - case VA1J5JF8007S_CHECK_TS_ID: - ret = va1j5jf8007s_check_ts_id(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 99) / 100; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; - return 0; - } - - state->tune_state = VA1J5JF8007S_TRACK; - /* fall through */ - - case VA1J5JF8007S_TRACK: - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - } - - BUG(); -} - -static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state) -{ - u8 buf[4]; - struct i2c_msg msg; - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0xf0; - buf[3] = 0x04; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x17; - buf[1] = sleep ? 0x01 : 0x00; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_sleep(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - int ret; - - state = fe->demodulator_priv; - - ret = va1j5jf8007s_init_frequency(state); - if (ret < 0) - return ret; - - return va1j5jf8007s_set_sleep(state, 1); -} - -static int va1j5jf8007s_init(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - - state = fe->demodulator_priv; - state->tune_state = VA1J5JF8007S_IDLE; - - return va1j5jf8007s_set_sleep(state, 0); -} - -static void va1j5jf8007s_release(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - state = fe->demodulator_priv; - kfree(state); -} - -static struct dvb_frontend_ops va1j5jf8007s_ops = { - .delsys = { SYS_ISDBS }, - .info = { - .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, - .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | - FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, - }, - - .read_snr = va1j5jf8007s_read_snr, - .get_frontend_algo = va1j5jf8007s_get_frontend_algo, - .read_status = va1j5jf8007s_read_status, - .tune = va1j5jf8007s_tune, - .sleep = va1j5jf8007s_sleep, - .init = va1j5jf8007s_init, - .release = va1j5jf8007s_release, -}; - -static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) -{ - u8 addr; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0x07; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - if (read_buf[0] != 0x41) - return -EIO; - - return 0; -} - -static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = { - {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, - {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, - {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, - {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, -}; - -static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = { - {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, - {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, - {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, - {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0}, -}; - -static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) -{ - const u8 (*bufs)[2]; - int size; - u8 addr; - u8 buf[2]; - struct i2c_msg msg; - int i; - - switch (state->config->frequency) { - case VA1J5JF8007S_20MHZ: - bufs = va1j5jf8007s_20mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs); - break; - case VA1J5JF8007S_25MHZ: - bufs = va1j5jf8007s_25mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs); - break; - default: - return -EINVAL; - } - - addr = state->config->demod_address; - - msg.addr = addr; - msg.flags = 0; - msg.len = 2; - msg.buf = buf; - for (i = 0; i < size; i++) { - memcpy(buf, bufs[i], sizeof(buf)); - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - } - - return 0; -} - -/* must be called after va1j5jf8007t_attach */ -int va1j5jf8007s_prepare(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - int ret; - - state = fe->demodulator_priv; - - ret = va1j5jf8007s_prepare_1(state); - if (ret < 0) - return ret; - - ret = va1j5jf8007s_prepare_2(state); - if (ret < 0) - return ret; - - return va1j5jf8007s_init_frequency(state); -} - -struct dvb_frontend * -va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, - struct i2c_adapter *adap) -{ - struct va1j5jf8007s_state *state; - struct dvb_frontend *fe; - u8 buf[2]; - struct i2c_msg msg; - - state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL); - if (!state) - return NULL; - - state->config = config; - state->adap = adap; - - fe = &state->fe; - memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops)); - fe->demodulator_priv = state; - - buf[0] = 0x01; - buf[1] = 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) { - kfree(state); - return NULL; - } - - return fe; -} diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.h b/drivers/media/dvb/pt1/va1j5jf8007s.h deleted file mode 100644 index b7d6f05a0e02..000000000000 --- a/drivers/media/dvb/pt1/va1j5jf8007s.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa - * - * This program is free software; 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 VA1J5JF8007S_H -#define VA1J5JF8007S_H - -enum va1j5jf8007s_frequency { - VA1J5JF8007S_20MHZ, - VA1J5JF8007S_25MHZ, -}; - -struct va1j5jf8007s_config { - u8 demod_address; - enum va1j5jf8007s_frequency frequency; -}; - -struct i2c_adapter; - -struct dvb_frontend * -va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, - struct i2c_adapter *adap); - -/* must be called after va1j5jf8007t_attach */ -int va1j5jf8007s_prepare(struct dvb_frontend *fe); - -#endif diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c deleted file mode 100644 index 2db15159d514..000000000000 --- a/drivers/media/dvb/pt1/va1j5jf8007t.c +++ /dev/null @@ -1,536 +0,0 @@ -/* - * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa - * - * This program is free software; 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 "dvb_frontend.h" -#include "dvb_math.h" -#include "va1j5jf8007t.h" - -enum va1j5jf8007t_tune_state { - VA1J5JF8007T_IDLE, - VA1J5JF8007T_SET_FREQUENCY, - VA1J5JF8007T_CHECK_FREQUENCY, - VA1J5JF8007T_SET_MODULATION, - VA1J5JF8007T_CHECK_MODULATION, - VA1J5JF8007T_TRACK, - VA1J5JF8007T_ABORT, -}; - -struct va1j5jf8007t_state { - const struct va1j5jf8007t_config *config; - struct i2c_adapter *adap; - struct dvb_frontend fe; - enum va1j5jf8007t_tune_state tune_state; -}; - -static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct va1j5jf8007t_state *state; - u8 addr; - int i; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - s32 word, x, y; - - state = fe->demodulator_priv; - addr = state->config->demod_address; - - word = 0; - for (i = 0; i < 3; i++) { - write_buf[0] = 0x8b + i; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - word <<= 8; - word |= read_buf[0]; - } - - if (!word) - return -EIO; - - x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24)); - y = (24ll << 46) / 1000000; - y = ((s64)y * x >> 30) - (16ll << 40) / 10000; - y = ((s64)y * x >> 29) + (398ll << 35) / 10000; - y = ((s64)y * x >> 30) + (5491ll << 29) / 10000; - y = ((s64)y * x >> 30) + (30965ll << 23) / 10000; - *snr = y >> 15; - return 0; -} - -static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) -{ - return DVBFE_ALGO_HW; -} - -static int -va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status) -{ - struct va1j5jf8007t_state *state; - - state = fe->demodulator_priv; - - switch (state->tune_state) { - case VA1J5JF8007T_IDLE: - case VA1J5JF8007T_SET_FREQUENCY: - case VA1J5JF8007T_CHECK_FREQUENCY: - *status = 0; - return 0; - - - case VA1J5JF8007T_SET_MODULATION: - case VA1J5JF8007T_CHECK_MODULATION: - case VA1J5JF8007T_ABORT: - *status |= FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007T_TRACK: - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - } - - BUG(); -} - -struct va1j5jf8007t_cb_map { - u32 frequency; - u8 cb; -}; - -static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = { - { 90000000, 0x80 }, - { 140000000, 0x81 }, - { 170000000, 0xa1 }, - { 220000000, 0x62 }, - { 330000000, 0xa2 }, - { 402000000, 0xe2 }, - { 450000000, 0x64 }, - { 550000000, 0x84 }, - { 600000000, 0xa4 }, - { 700000000, 0xc4 }, -}; - -static u8 va1j5jf8007t_lookup_cb(u32 frequency) -{ - int i; - const struct va1j5jf8007t_cb_map *map; - - for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) { - map = &va1j5jf8007t_cb_maps[i]; - if (frequency < map->frequency) - return map->cb; - } - return 0xe4; -} - -static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state) -{ - u32 frequency; - u16 word; - u8 buf[6]; - struct i2c_msg msg; - - frequency = state->fe.dtv_property_cache.frequency; - - word = (frequency + 71428) / 142857 + 399; - buf[0] = 0xfe; - buf[1] = 0xc2; - buf[2] = word >> 8; - buf[3] = word; - buf[4] = 0x80; - buf[5] = va1j5jf8007t_lookup_cb(frequency); - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock) -{ - u8 addr; - u8 write_buf[2], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0xfe; - write_buf[1] = 0xc3; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = read_buf[0] & 0x40; - return 0; -} - -static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x01; - buf[1] = 0x40; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state, - int *lock, int *retry) -{ - u8 addr; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0x80; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = !(read_buf[0] & 0x10); - *retry = read_buf[0] & 0x80; - return 0; -} - -static int -va1j5jf8007t_tune(struct dvb_frontend *fe, - bool re_tune, - unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) -{ - struct va1j5jf8007t_state *state; - int ret; - int lock = 0, retry = 0; - - state = fe->demodulator_priv; - - if (re_tune) - state->tune_state = VA1J5JF8007T_SET_FREQUENCY; - - switch (state->tune_state) { - case VA1J5JF8007T_IDLE: - *delay = 3 * HZ; - *status = 0; - return 0; - - case VA1J5JF8007T_SET_FREQUENCY: - ret = va1j5jf8007t_set_frequency(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY; - *delay = 0; - *status = 0; - return 0; - - case VA1J5JF8007T_CHECK_FREQUENCY: - ret = va1j5jf8007t_check_frequency(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 999) / 1000; - *status = 0; - return 0; - } - - state->tune_state = VA1J5JF8007T_SET_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007T_SET_MODULATION: - ret = va1j5jf8007t_set_modulation(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007T_CHECK_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007T_CHECK_MODULATION: - ret = va1j5jf8007t_check_modulation(state, &lock, &retry); - if (ret < 0) - return ret; - - if (!lock) { - if (!retry) { - state->tune_state = VA1J5JF8007T_ABORT; - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL; - return 0; - } - *delay = (HZ + 999) / 1000; - *status = FE_HAS_SIGNAL; - return 0; - } - - state->tune_state = VA1J5JF8007T_TRACK; - /* fall through */ - - case VA1J5JF8007T_TRACK: - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - - case VA1J5JF8007T_ABORT: - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL; - return 0; - } - - BUG(); -} - -static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state) -{ - u8 buf[7]; - struct i2c_msg msg; - - buf[0] = 0xfe; - buf[1] = 0xc2; - buf[2] = 0x01; - buf[3] = 0x8f; - buf[4] = 0xc1; - buf[5] = 0x80; - buf[6] = 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x03; - buf[1] = sleep ? 0x90 : 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007t_sleep(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - int ret; - - state = fe->demodulator_priv; - - ret = va1j5jf8007t_init_frequency(state); - if (ret < 0) - return ret; - - return va1j5jf8007t_set_sleep(state, 1); -} - -static int va1j5jf8007t_init(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - - state = fe->demodulator_priv; - state->tune_state = VA1J5JF8007T_IDLE; - - return va1j5jf8007t_set_sleep(state, 0); -} - -static void va1j5jf8007t_release(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - state = fe->demodulator_priv; - kfree(state); -} - -static struct dvb_frontend_ops va1j5jf8007t_ops = { - .delsys = { SYS_ISDBT }, - .info = { - .name = "VA1J5JF8007/VA1J5JF8011 ISDB-T", - .frequency_min = 90000000, - .frequency_max = 770000000, - .frequency_stepsize = 142857, - .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | - FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, - }, - - .read_snr = va1j5jf8007t_read_snr, - .get_frontend_algo = va1j5jf8007t_get_frontend_algo, - .read_status = va1j5jf8007t_read_status, - .tune = va1j5jf8007t_tune, - .sleep = va1j5jf8007t_sleep, - .init = va1j5jf8007t_init, - .release = va1j5jf8007t_release, -}; - -static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = { - {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, - {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, - {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, - {0xef, 0x01} -}; - -static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = { - {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, - {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c}, - {0x77, 0x03}, {0xef, 0x01} -}; - -int va1j5jf8007t_prepare(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - const u8 (*bufs)[2]; - int size; - u8 buf[2]; - struct i2c_msg msg; - int i; - - state = fe->demodulator_priv; - - switch (state->config->frequency) { - case VA1J5JF8007T_20MHZ: - bufs = va1j5jf8007t_20mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs); - break; - case VA1J5JF8007T_25MHZ: - bufs = va1j5jf8007t_25mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs); - break; - default: - return -EINVAL; - } - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - for (i = 0; i < size; i++) { - memcpy(buf, bufs[i], sizeof(buf)); - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - } - - return va1j5jf8007t_init_frequency(state); -} - -struct dvb_frontend * -va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, - struct i2c_adapter *adap) -{ - struct va1j5jf8007t_state *state; - struct dvb_frontend *fe; - u8 buf[2]; - struct i2c_msg msg; - - state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL); - if (!state) - return NULL; - - state->config = config; - state->adap = adap; - - fe = &state->fe; - memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops)); - fe->demodulator_priv = state; - - buf[0] = 0x01; - buf[1] = 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) { - kfree(state); - return NULL; - } - - return fe; -} diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.h b/drivers/media/dvb/pt1/va1j5jf8007t.h deleted file mode 100644 index 2903be519ef5..000000000000 --- a/drivers/media/dvb/pt1/va1j5jf8007t.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa - * - * This program is free software; 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 VA1J5JF8007T_H -#define VA1J5JF8007T_H - -enum va1j5jf8007t_frequency { - VA1J5JF8007T_20MHZ, - VA1J5JF8007T_25MHZ, -}; - -struct va1j5jf8007t_config { - u8 demod_address; - enum va1j5jf8007t_frequency frequency; -}; - -struct i2c_adapter; - -struct dvb_frontend * -va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, - struct i2c_adapter *adap); - -/* must be called after va1j5jf8007s_attach */ -int va1j5jf8007t_prepare(struct dvb_frontend *fe); - -#endif diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig deleted file mode 100644 index 9d83ced69dd6..000000000000 --- a/drivers/media/dvb/ttpci/Kconfig +++ /dev/null @@ -1,159 +0,0 @@ -config TTPCI_EEPROM - tristate - depends on I2C - default n - -config DVB_AV7110 - tristate "AV7110 cards" - depends on DVB_CORE && PCI && I2C - select TTPCI_EEPROM - select VIDEO_SAA7146_VV - depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_SP8870 if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_L64781 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - help - Support for SAA7146 and AV7110 based DVB cards as produced - by Fujitsu-Siemens, Technotrend, Hauppauge and others. - - This driver only supports the fullfeatured cards with - onboard MPEG2 decoder. - - This driver needs an external firmware. Please use the script - "/Documentation/dvb/get_dvb_firmware av7110" to - download/extract it, and then copy it to /usr/lib/hotplug/firmware - or /lib/firmware (depending on configuration of firmware hotplug). - - Alternatively, you can download the file and use the kernel's - EXTRA_FIRMWARE configuration option to build it into your - kernel image by adding the filename to the EXTRA_FIRMWARE - configuration option string. - - Say Y if you own such a card and want to use it. - -config DVB_AV7110_OSD - bool "AV7110 OSD support" - depends on DVB_AV7110 - default y if DVB_AV7110=y || DVB_AV7110=m - help - The AV7110 firmware provides some code to generate an OnScreenDisplay - on the video output. This is kind of nonstandard and not guaranteed to - be maintained. - - Anyway, some popular DVB software like VDR uses this OSD to render - its menus, so say Y if you want to use this software. - - All other people say N. - -config DVB_BUDGET_CORE - tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)" - depends on DVB_CORE && PCI && I2C - select VIDEO_SAA7146 - select TTPCI_EEPROM - help - Support for simple SAA7146 based DVB cards - (so called Budget- or Nova-PCI cards) without onboard - MPEG2 decoder. - -config DVB_BUDGET - tristate "Budget cards" - depends on DVB_BUDGET_CORE && I2C - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_L64781 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_S5H1420 if !DVB_FE_CUSTOMISE - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_ISL6423 if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE - help - Support for simple SAA7146 based DVB cards (so called Budget- - or Nova-PCI cards) without onboard MPEG2 decoder, and without - analog inputs or an onboard Common Interface connector. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget. - -config DVB_BUDGET_CI - tristate "Budget cards with onboard CI connector" - depends on DVB_BUDGET_CORE && I2C - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE - depends on RC_CORE - help - Support for simple SAA7146 based DVB cards - (so called Budget- or Nova-PCI cards) without onboard - MPEG2 decoder, but with onboard Common Interface connector. - - Note: The Common Interface is not yet supported by this driver - due to lack of information from the vendor. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget-ci. - -config DVB_BUDGET_AV - tristate "Budget cards with analog video inputs" - depends on DVB_BUDGET_CORE && I2C - select VIDEO_SAA7146_VV - depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_TDA10021 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_TDA8261 if !DVB_FE_CUSTOMISE - select DVB_TUA6100 if !DVB_FE_CUSTOMISE - help - Support for simple SAA7146 based DVB cards - (so called Budget- or Nova-PCI cards) without onboard - MPEG2 decoder, but with one or more analog video inputs. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget-av. - -config DVB_BUDGET_PATCH - tristate "AV7110 cards with Budget Patch" - depends on DVB_BUDGET_CORE && I2C - depends on DVB_AV7110 - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - help - Support for Budget Patch (full TS) modification on - SAA7146+AV7110 based cards (DVB-S cards). This - driver doesn't use onboard MPEG2 decoder. The - card is driven in Budget-only mode. Card is - required to have loaded firmware to tune properly. - Firmware can be loaded by insertion and removal of - standard AV7110 driver prior to loading this - driver. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget-patch. diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile deleted file mode 100644 index 22a235f3cc48..000000000000 --- a/drivers/media/dvb/ttpci/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# -# Makefile for the kernel SAA7146 FULL TS DVB device driver -# and the AV7110 DVB device driver -# - -dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o - -ifdef CONFIG_INPUT_EVDEV -dvb-ttpci-objs += av7110_ir.o -endif - -obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o -obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o -obj-$(CONFIG_DVB_BUDGET) += budget.o -obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o -obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o -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 diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c deleted file mode 100644 index 4bd8bd56befc..000000000000 --- a/drivers/media/dvb/ttpci/av7110.c +++ /dev/null @@ -1,2939 +0,0 @@ -/* - * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) - * av7110.c: initialization and demux stuff - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include - -#include "dvb_frontend.h" - -#include "ttpci-eeprom.h" -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_av.h" -#include "av7110_ca.h" -#include "av7110_ipack.h" - -#include "bsbe1.h" -#include "lnbp21.h" -#include "bsru6.h" - -#define TS_WIDTH 376 -#define TS_HEIGHT 512 -#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) -#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) - - -int av7110_debug; - -static int vidmode = CVBS_RGB_OUT; -static int pids_off; -static int adac = DVB_ADAC_TI; -static int hw_sections; -static int rgb_on; -static int volume = 255; -static int budgetpatch; -static int wss_cfg_4_3 = 0x4008; -static int wss_cfg_16_9 = 0x0007; -static int tv_standard; -static int full_ts; - -module_param_named(debug, av7110_debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); -module_param(vidmode, int, 0444); -MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); -module_param(pids_off, int, 0444); -MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed"); -module_param(adac, int, 0444); -MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); -module_param(hw_sections, int, 0444); -MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware"); -module_param(rgb_on, int, 0444); -MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control" - " signal on SCART pin 16 to switch SCART video mode from CVBS to RGB"); -module_param(volume, int, 0444); -MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); -module_param(budgetpatch, int, 0444); -MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"); -module_param(full_ts, int, 0444); -MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable"); -module_param(wss_cfg_4_3, int, 0444); -MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); -module_param(wss_cfg_16_9, int, 0444); -MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); -module_param(tv_standard, int, 0444); -MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static void restart_feeds(struct av7110 *av7110); -static int budget_start_feed(struct dvb_demux_feed *feed); -static int budget_stop_feed(struct dvb_demux_feed *feed); - -static int av7110_num; - -#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ -{\ - if (fe_func != NULL) { \ - av7110_copy = fe_func; \ - fe_func = av7110_func; \ - } \ -} - - -static void init_av7110_av(struct av7110 *av7110) -{ - int ret; - struct saa7146_dev *dev = av7110->dev; - - /* set internal volume control to maximum */ - av7110->adac_type = DVB_ADAC_TI; - ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); - if (ret < 0) - printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); - - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16) av7110->display_ar); - if (ret < 0) - printk("dvb-ttpci: unable to set aspect ratio\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); - if (ret < 0) - printk("dvb-ttpci: unable to set pan scan\n"); - - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); - if (ret < 0) - printk("dvb-ttpci: unable to configure 4:3 wss\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); - if (ret < 0) - printk("dvb-ttpci: unable to configure 16:9 wss\n"); - - ret = av7710_set_video_mode(av7110, vidmode); - if (ret < 0) - printk("dvb-ttpci:cannot set video mode:%d\n",ret); - - /* handle different card types */ - /* remaining inits according to card and frontend type */ - av7110->analog_tuner_flags = 0; - av7110->current_input = 0; - if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) - av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on - if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { - printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_CRYSTAL; - i2c_writereg(av7110, 0x20, 0x01, 0xd2); - i2c_writereg(av7110, 0x20, 0x02, 0x49); - i2c_writereg(av7110, 0x20, 0x03, 0x00); - i2c_writereg(av7110, 0x20, 0x04, 0x00); - - /** - * some special handling for the Siemens DVB-C cards... - */ - } else if (0 == av7110_init_analog_module(av7110)) { - /* done. */ - } - else if (dev->pci->subsystem_vendor == 0x110a) { - printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_NONE; - } - else { - av7110->adac_type = adac; - printk("dvb-ttpci: adac type set to %d @ card %d\n", - av7110->adac_type, av7110->dvb_adapter.num); - } - - if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) { - // switch DVB SCART on - ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); - if (ret < 0) - printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret); - ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); - if (ret < 0) - printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret); - if (rgb_on && - ((av7110->dev->pci->subsystem_vendor == 0x110a) || - (av7110->dev->pci->subsystem_vendor == 0x13c2)) && - (av7110->dev->pci->subsystem_device == 0x0000)) { - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 - //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 - } - } - - if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e) - av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on - - ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); - if (ret < 0) - printk("dvb-ttpci:cannot set volume :%d\n",ret); -} - -static void recover_arm(struct av7110 *av7110) -{ - dprintk(4, "%p\n",av7110); - - av7110_bootarm(av7110); - msleep(100); - - init_av7110_av(av7110); - - /* card-specific recovery */ - if (av7110->recover) - av7110->recover(av7110); - - restart_feeds(av7110); - -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) - av7110_check_ir_config(av7110, true); -#endif -} - -static void av7110_arm_sync(struct av7110 *av7110) -{ - if (av7110->arm_thread) - kthread_stop(av7110->arm_thread); - - av7110->arm_thread = NULL; -} - -static int arm_thread(void *data) -{ - struct av7110 *av7110 = data; - u16 newloops = 0; - int timeout; - - dprintk(4, "%p\n",av7110); - - for (;;) { - timeout = wait_event_interruptible_timeout(av7110->arm_wait, - kthread_should_stop(), 5 * HZ); - - if (-ERESTARTSYS == timeout || kthread_should_stop()) { - /* got signal or told to quit*/ - break; - } - - if (!av7110->arm_ready) - continue; - -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) - av7110_check_ir_config(av7110, false); -#endif - - if (mutex_lock_interruptible(&av7110->dcomlock)) - break; - newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); - mutex_unlock(&av7110->dcomlock); - - if (newloops == av7110->arm_loops || av7110->arm_errors > 3) { - printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", - av7110->dvb_adapter.num); - - recover_arm(av7110); - - if (mutex_lock_interruptible(&av7110->dcomlock)) - break; - newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1; - mutex_unlock(&av7110->dcomlock); - } - av7110->arm_loops = newloops; - av7110->arm_errors = 0; - } - - return 0; -} - - -/**************************************************************************** - * IRQ handling - ****************************************************************************/ - -static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, - u8 *buffer2, size_t buffer2_len, - struct dvb_demux_filter *dvbdmxfilter, - enum dmx_success success, - struct av7110 *av7110) -{ - if (!dvbdmxfilter->feed->demux->dmx.frontend) - return 0; - if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE) - return 0; - - switch (dvbdmxfilter->type) { - case DMX_TYPE_SEC: - if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len) - return 0; - if (dvbdmxfilter->doneq) { - struct dmx_section_filter *filter = &dvbdmxfilter->filter; - int i; - u8 xor, neq = 0; - - for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { - xor = filter->filter_value[i] ^ buffer1[i]; - neq |= dvbdmxfilter->maskandnotmode[i] & xor; - } - if (!neq) - return 0; - } - return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, - buffer2, buffer2_len, - &dvbdmxfilter->filter, - DMX_OK); - case DMX_TYPE_TS: - if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) - return 0; - if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) - return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, - buffer2, buffer2_len, - &dvbdmxfilter->feed->feed.ts, - DMX_OK); - else - av7110_p2t_write(buffer1, buffer1_len, - dvbdmxfilter->feed->pid, - &av7110->p2t_filter[dvbdmxfilter->index]); - default: - return 0; - } -} - - -//#define DEBUG_TIMING -static inline void print_time(char *s) -{ -#ifdef DEBUG_TIMING - struct timeval tv; - do_gettimeofday(&tv); - printk("%s: %d.%d\n", s, (int)tv.tv_sec, (int)tv.tv_usec); -#endif -} - -#define DEBI_READ 0 -#define DEBI_WRITE 1 -static inline void start_debi_dma(struct av7110 *av7110, int dir, - unsigned long addr, unsigned int len) -{ - dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); - if (saa7146_wait_for_debi_done(av7110->dev, 0)) { - printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); - return; - } - - SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */ - SAA7146_IER_ENABLE(av7110->dev, MASK_19); - if (len < 5) - len = 5; /* we want a real DEBI DMA */ - if (dir == DEBI_WRITE) - iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3); - else - irdebi(av7110, DEBISWAB, addr, 0, len); -} - -static void debiirq(unsigned long cookie) -{ - struct av7110 *av7110 = (struct av7110 *)cookie; - int type = av7110->debitype; - int handle = (type >> 8) & 0x1f; - unsigned int xfer = 0; - - print_time("debi"); - dprintk(4, "type 0x%04x\n", type); - - if (type == -1) { - printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", - jiffies, saa7146_read(av7110->dev, PSR), - saa7146_read(av7110->dev, SSR)); - goto debi_done; - } - av7110->debitype = -1; - - switch (type & 0xff) { - - case DATA_TS_RECORD: - dvb_dmx_swfilter_packets(&av7110->demux, - (const u8 *) av7110->debi_virt, - av7110->debilen / 188); - xfer = RX_BUFF; - break; - - case DATA_PES_RECORD: - if (av7110->demux.recording) - av7110_record_cb(&av7110->p2t[handle], - (u8 *) av7110->debi_virt, - av7110->debilen); - xfer = RX_BUFF; - break; - - case DATA_IPMPE: - case DATA_FSECTION: - case DATA_PIPING: - if (av7110->handle2filter[handle]) - DvbDmxFilterCallback((u8 *)av7110->debi_virt, - av7110->debilen, NULL, 0, - av7110->handle2filter[handle], - DMX_OK, av7110); - xfer = RX_BUFF; - break; - - case DATA_CI_GET: - { - u8 *data = av7110->debi_virt; - - if ((data[0] < 2) && data[2] == 0xff) { - int flags = 0; - if (data[5] > 0) - flags |= CA_CI_MODULE_PRESENT; - if (data[5] > 5) - flags |= CA_CI_MODULE_READY; - av7110->ci_slot[data[0]].flags = flags; - } else - ci_get_data(&av7110->ci_rbuffer, - av7110->debi_virt, - av7110->debilen); - xfer = RX_BUFF; - break; - } - - case DATA_COMMON_INTERFACE: - CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen); -#if 0 - { - int i; - - printk("av7110%d: ", av7110->num); - printk("%02x ", *(u8 *)av7110->debi_virt); - printk("%02x ", *(1+(u8 *)av7110->debi_virt)); - for (i = 2; i < av7110->debilen; i++) - printk("%02x ", (*(i+(unsigned char *)av7110->debi_virt))); - for (i = 2; i < av7110->debilen; i++) - printk("%c", chtrans(*(i+(unsigned char *)av7110->debi_virt))); - - printk("\n"); - } -#endif - xfer = RX_BUFF; - break; - - case DATA_DEBUG_MESSAGE: - ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; - printk("%s\n", (s8 *) av7110->debi_virt); - xfer = RX_BUFF; - break; - - case DATA_CI_PUT: - dprintk(4, "debi DATA_CI_PUT\n"); - case DATA_MPEG_PLAY: - dprintk(4, "debi DATA_MPEG_PLAY\n"); - case DATA_BMP_LOAD: - dprintk(4, "debi DATA_BMP_LOAD\n"); - xfer = TX_BUFF; - break; - default: - break; - } -debi_done: - spin_lock(&av7110->debilock); - if (xfer) - iwdebi(av7110, DEBINOSWAP, xfer, 0, 2); - ARM_ClearMailBox(av7110); - spin_unlock(&av7110->debilock); -} - -/* irq from av7110 firmware writing the mailbox register in the DPRAM */ -static void gpioirq(unsigned long cookie) -{ - struct av7110 *av7110 = (struct av7110 *)cookie; - u32 rxbuf, txbuf; - int len; - - if (av7110->debitype != -1) - /* we shouldn't get any irq while a debi xfer is running */ - printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", - jiffies, saa7146_read(av7110->dev, PSR), - saa7146_read(av7110->dev, SSR)); - - if (saa7146_wait_for_debi_done(av7110->dev, 0)) { - printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); - BUG(); /* maybe we should try resetting the debi? */ - } - - spin_lock(&av7110->debilock); - ARM_ClearIrq(av7110); - - /* see what the av7110 wants */ - av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); - av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - len = (av7110->debilen + 3) & ~3; - - print_time("gpio"); - dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); - - switch (av7110->debitype & 0xff) { - - case DATA_TS_PLAY: - case DATA_PES_PLAY: - break; - - case DATA_MPEG_VIDEO_EVENT: - { - u32 h_ar; - struct video_event event; - - av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2); - h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2); - - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - - av7110->video_size.h = h_ar & 0xfff; - - event.type = VIDEO_EVENT_SIZE_CHANGED; - event.u.size.w = av7110->video_size.w; - event.u.size.h = av7110->video_size.h; - switch ((h_ar >> 12) & 0xf) - { - case 3: - av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; - event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; - av7110->videostate.video_format = VIDEO_FORMAT_16_9; - break; - case 4: - av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1; - event.u.size.aspect_ratio = VIDEO_FORMAT_221_1; - av7110->videostate.video_format = VIDEO_FORMAT_221_1; - break; - default: - av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3; - event.u.size.aspect_ratio = VIDEO_FORMAT_4_3; - av7110->videostate.video_format = VIDEO_FORMAT_4_3; - } - - dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n", - av7110->video_size.w, av7110->video_size.h, - av7110->video_size.aspect_ratio); - - dvb_video_add_event(av7110, &event); - break; - } - - case DATA_CI_PUT: - { - int avail; - struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer; - - avail = dvb_ringbuffer_avail(cibuf); - if (avail <= 2) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; - len |= DVB_RINGBUFFER_PEEK(cibuf, 1); - if (avail < len + 2) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - DVB_RINGBUFFER_SKIP(cibuf, 2); - - dvb_ringbuffer_read(cibuf, av7110->debi_virt, len); - - iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - dprintk(8, "DMA: CI\n"); - start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); - spin_unlock(&av7110->debilock); - wake_up(&cibuf->queue); - return; - } - - case DATA_MPEG_PLAY: - if (!av7110->playing) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - len = 0; - if (av7110->debitype & 0x100) { - spin_lock(&av7110->aout.lock); - len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); - spin_unlock(&av7110->aout.lock); - } - if (len <= 0 && (av7110->debitype & 0x200) - &&av7110->videostate.play_state != VIDEO_FREEZED) { - spin_lock(&av7110->avout.lock); - len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); - spin_unlock(&av7110->avout.lock); - } - if (len <= 0) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len); - iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - dprintk(8, "DMA: MPEG_PLAY\n"); - start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_BMP_LOAD: - len = av7110->debilen; - dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len); - if (!len) { - av7110->bmp_state = BMP_LOADED; - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - wake_up(&av7110->bmpq); - dprintk(8, "gpio DATA_BMP_LOAD done\n"); - break; - } - if (len > av7110->bmplen) - len = av7110->bmplen; - if (len > 2 * 1024) - len = 2 * 1024; - iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); - av7110->bmpp += len; - av7110->bmplen -= len; - dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len); - start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_CI_GET: - case DATA_COMMON_INTERFACE: - case DATA_FSECTION: - case DATA_IPMPE: - case DATA_PIPING: - if (!len || len > 4 * 1024) { - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - break; - } - /* fall through */ - - case DATA_TS_RECORD: - case DATA_PES_RECORD: - dprintk(8, "DMA: TS_REC etc.\n"); - start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_DEBUG_MESSAGE: - if (!len || len > 0xff) { - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - break; - } - start_debi_dma(av7110, DEBI_READ, Reserved, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_IRCOMMAND: - if (av7110->ir.ir_handler) - av7110->ir.ir_handler(av7110, - swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4))); - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - break; - - default: - printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n", - av7110->debitype, av7110->debilen); - break; - } - av7110->debitype = -1; - ARM_ClearMailBox(av7110); - spin_unlock(&av7110->debilock); -} - - -#ifdef CONFIG_DVB_AV7110_OSD -static int dvb_osd_ioctl(struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(4, "%p\n", av7110); - - if (cmd == OSD_SEND_CMD) - return av7110_osd_cmd(av7110, (osd_cmd_t *) parg); - if (cmd == OSD_GET_CAPABILITY) - return av7110_osd_capability(av7110, (osd_cap_t *) parg); - - return -EINVAL; -} - - -static const struct file_operations dvb_osd_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = dvb_generic_ioctl, - .open = dvb_generic_open, - .release = dvb_generic_release, - .llseek = noop_llseek, -}; - -static struct dvb_device dvbdev_osd = { - .priv = NULL, - .users = 1, - .writers = 1, - .fops = &dvb_osd_fops, - .kernel_ioctl = dvb_osd_ioctl, -}; -#endif /* CONFIG_DVB_AV7110_OSD */ - - -static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid) -{ - u16 aflags = 0; - - dprintk(4, "%p\n", av7110); - - if (vpid == 0x1fff || apid == 0x1fff || - ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) { - vpid = apid = ttpid = subpid = pcrpid = 0; - av7110->pids[DMX_PES_VIDEO] = 0; - av7110->pids[DMX_PES_AUDIO] = 0; - av7110->pids[DMX_PES_TELETEXT] = 0; - av7110->pids[DMX_PES_PCR] = 0; - } - - if (av7110->audiostate.bypass_mode) - aflags |= 0x8000; - - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, - pcrpid, vpid, apid, ttpid, subpid, aflags); -} - -int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid) -{ - int ret = 0; - dprintk(4, "%p\n", av7110); - - if (mutex_lock_interruptible(&av7110->pid_mutex)) - return -ERESTARTSYS; - - if (!(vpid & 0x8000)) - av7110->pids[DMX_PES_VIDEO] = vpid; - if (!(apid & 0x8000)) - av7110->pids[DMX_PES_AUDIO] = apid; - if (!(ttpid & 0x8000)) - av7110->pids[DMX_PES_TELETEXT] = ttpid; - if (!(pcrpid & 0x8000)) - av7110->pids[DMX_PES_PCR] = pcrpid; - - av7110->pids[DMX_PES_SUBTITLE] = 0; - - if (av7110->fe_synced) { - pcrpid = av7110->pids[DMX_PES_PCR]; - ret = SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid); - } - - mutex_unlock(&av7110->pid_mutex); - return ret; -} - - -/****************************************************************************** - * hardware filter functions - ******************************************************************************/ - -static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) -{ - struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed; - struct av7110 *av7110 = dvbdmxfeed->demux->priv; - u16 buf[20]; - int ret, i; - u16 handle; -// u16 mode = 0x0320; - u16 mode = 0xb96a; - - dprintk(4, "%p\n", av7110); - - if (av7110->full_ts) - return 0; - - if (dvbdmxfilter->type == DMX_TYPE_SEC) { - if (hw_sections) { - buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) | - dvbdmxfilter->maskandmode[0]; - for (i = 3; i < 18; i++) - buf[i + 4 - 2] = - (dvbdmxfilter->filter.filter_value[i] << 8) | - dvbdmxfilter->maskandmode[i]; - mode = 4; - } - } else if ((dvbdmxfeed->ts_type & TS_PACKET) && - !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) { - av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); - } - - buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; - buf[1] = 16; - buf[2] = dvbdmxfeed->pid; - buf[3] = mode; - - ret = av7110_fw_request(av7110, buf, 20, &handle, 1); - if (ret != 0 || handle >= 32) { - printk("dvb-ttpci: %s error buf %04x %04x %04x %04x " - "ret %d handle %04x\n", - __func__, buf[0], buf[1], buf[2], buf[3], - ret, handle); - dvbdmxfilter->hw_handle = 0xffff; - if (!ret) - ret = -1; - return ret; - } - - av7110->handle2filter[handle] = dvbdmxfilter; - dvbdmxfilter->hw_handle = handle; - - return ret; -} - -static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) -{ - struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv; - u16 buf[3]; - u16 answ[2]; - int ret; - u16 handle; - - dprintk(4, "%p\n", av7110); - - if (av7110->full_ts) - return 0; - - handle = dvbdmxfilter->hw_handle; - if (handle >= 32) { - printk("%s tried to stop invalid filter %04x, filter type = %x\n", - __func__, handle, dvbdmxfilter->type); - return -EINVAL; - } - - av7110->handle2filter[handle] = NULL; - - buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; - buf[1] = 1; - buf[2] = handle; - ret = av7110_fw_request(av7110, buf, 3, answ, 2); - if (ret != 0 || answ[1] != handle) { - printk("dvb-ttpci: %s error cmd %04x %04x %04x ret %x " - "resp %04x %04x pid %d\n", - __func__, buf[0], buf[1], buf[2], ret, - answ[0], answ[1], dvbdmxfilter->feed->pid); - if (!ret) - ret = -1; - } - return ret; -} - - -static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct av7110 *av7110 = dvbdmx->priv; - u16 *pid = dvbdmx->pids, npids[5]; - int i; - int ret = 0; - - dprintk(4, "%p\n", av7110); - - npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; - i = dvbdmxfeed->pes_type; - npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; - if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { - npids[i] = 0; - ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); - if (!ret) - ret = StartHWFilter(dvbdmxfeed->filter); - return ret; - } - if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4) { - ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); - if (ret) - return ret; - } - - if (dvbdmxfeed->pes_type < 2 && npids[0]) - if (av7110->fe_synced) - { - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); - if (ret) - return ret; - } - - if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) { - if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000)) - ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed); - if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000)) - ret = av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed); - } - return ret; -} - -static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct av7110 *av7110 = dvbdmx->priv; - u16 *pid = dvbdmx->pids, npids[5]; - int i; - - int ret = 0; - - dprintk(4, "%p\n", av7110); - - if (dvbdmxfeed->pes_type <= 1) { - ret = av7110_av_stop(av7110, dvbdmxfeed->pes_type ? RP_VIDEO : RP_AUDIO); - if (ret) - return ret; - if (!av7110->rec_mode) - dvbdmx->recording = 0; - if (!av7110->playing) - dvbdmx->playing = 0; - } - npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; - i = dvbdmxfeed->pes_type; - switch (i) { - case 2: //teletext - if (dvbdmxfeed->ts_type & TS_PACKET) - ret = StopHWFilter(dvbdmxfeed->filter); - npids[2] = 0; - break; - case 0: - case 1: - case 4: - if (!pids_off) - return 0; - npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; - break; - } - if (!ret) - ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); - return ret; -} - -static int av7110_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = demux->priv; - int ret = 0; - - dprintk(4, "%p\n", av7110); - - if (!demux->dmx.frontend) - return -EINVAL; - - if (!av7110->full_ts && feed->pid > 0x1fff) - return -EINVAL; - - if (feed->type == DMX_TYPE_TS) { - if ((feed->ts_type & TS_DECODER) && - (feed->pes_type <= DMX_TS_PES_PCR)) { - switch (demux->dmx.frontend->source) { - case DMX_MEMORY_FE: - if (feed->ts_type & TS_DECODER) - if (feed->pes_type < 2 && - !(demux->pids[0] & 0x8000) && - !(demux->pids[1] & 0x8000)) { - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - ret = av7110_av_start_play(av7110,RP_AV); - if (!ret) - demux->playing = 1; - } - break; - default: - ret = dvb_feed_start_pid(feed); - break; - } - } else if ((feed->ts_type & TS_PACKET) && - (demux->dmx.frontend->source != DMX_MEMORY_FE)) { - ret = StartHWFilter(feed->filter); - } - } - - if (av7110->full_ts) { - budget_start_feed(feed); - return ret; - } - - if (feed->type == DMX_TYPE_SEC) { - int i; - - for (i = 0; i < demux->filternum; i++) { - if (demux->filter[i].state != DMX_STATE_READY) - continue; - if (demux->filter[i].type != DMX_TYPE_SEC) - continue; - if (demux->filter[i].filter.parent != &feed->feed.sec) - continue; - demux->filter[i].state = DMX_STATE_GO; - if (demux->dmx.frontend->source != DMX_MEMORY_FE) { - ret = StartHWFilter(&demux->filter[i]); - if (ret) - break; - } - } - } - - return ret; -} - - -static int av7110_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = demux->priv; - int i, rc, ret = 0; - dprintk(4, "%p\n", av7110); - - if (feed->type == DMX_TYPE_TS) { - if (feed->ts_type & TS_DECODER) { - if (feed->pes_type >= DMX_TS_PES_OTHER || - !demux->pesfilter[feed->pes_type]) - return -EINVAL; - demux->pids[feed->pes_type] |= 0x8000; - demux->pesfilter[feed->pes_type] = NULL; - } - if (feed->ts_type & TS_DECODER && - feed->pes_type < DMX_TS_PES_OTHER) { - ret = dvb_feed_stop_pid(feed); - } else - if ((feed->ts_type & TS_PACKET) && - (demux->dmx.frontend->source != DMX_MEMORY_FE)) - ret = StopHWFilter(feed->filter); - } - - if (av7110->full_ts) { - budget_stop_feed(feed); - return ret; - } - - if (feed->type == DMX_TYPE_SEC) { - for (i = 0; ifilternum; i++) { - if (demux->filter[i].state == DMX_STATE_GO && - demux->filter[i].filter.parent == &feed->feed.sec) { - demux->filter[i].state = DMX_STATE_READY; - if (demux->dmx.frontend->source != DMX_MEMORY_FE) { - rc = StopHWFilter(&demux->filter[i]); - if (!ret) - ret = rc; - /* keep going, stop as many filters as possible */ - } - } - } - } - - return ret; -} - - -static void restart_feeds(struct av7110 *av7110) -{ - struct dvb_demux *dvbdmx = &av7110->demux; - struct dvb_demux_feed *feed; - int mode; - int feeding; - int i, j; - - dprintk(4, "%p\n", av7110); - - mode = av7110->playing; - av7110->playing = 0; - av7110->rec_mode = 0; - - feeding = av7110->feeding1; /* full_ts mod */ - - for (i = 0; i < dvbdmx->feednum; i++) { - feed = &dvbdmx->feed[i]; - if (feed->state == DMX_STATE_GO) { - if (feed->type == DMX_TYPE_SEC) { - for (j = 0; j < dvbdmx->filternum; j++) { - if (dvbdmx->filter[j].type != DMX_TYPE_SEC) - continue; - if (dvbdmx->filter[j].filter.parent != &feed->feed.sec) - continue; - if (dvbdmx->filter[j].state == DMX_STATE_GO) - dvbdmx->filter[j].state = DMX_STATE_READY; - } - } - av7110_start_feed(feed); - } - } - - av7110->feeding1 = feeding; /* full_ts mod */ - - if (mode) - av7110_av_start_play(av7110, mode); -} - -static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, - uint64_t *stc, unsigned int *base) -{ - int ret; - u16 fwstc[4]; - u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC); - struct dvb_demux *dvbdemux; - struct av7110 *av7110; - - /* pointer casting paranoia... */ - BUG_ON(!demux); - dvbdemux = demux->priv; - BUG_ON(!dvbdemux); - av7110 = dvbdemux->priv; - - dprintk(4, "%p\n", av7110); - - if (num != 0) - return -EINVAL; - - ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4); - if (ret) { - printk(KERN_ERR "%s: av7110_fw_request error\n", __func__); - return ret; - } - dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", - fwstc[0], fwstc[1], fwstc[2], fwstc[3]); - - *stc = (((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) | - (((uint64_t) fwstc[1]) << 16) | ((uint64_t) fwstc[0]); - *base = 1; - - dprintk(4, "stc = %lu\n", (unsigned long)*stc); - - return 0; -} - - -/****************************************************************************** - * SEC device file operations - ******************************************************************************/ - - -static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) -{ - struct av7110* av7110 = fe->dvb->priv; - - switch (tone) { - case SEC_TONE_ON: - return Set22K(av7110, 1); - - case SEC_TONE_OFF: - return Set22K(av7110, 0); - - default: - return -EINVAL; - } -} - -static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd* cmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); -} - -static int av7110_diseqc_send_burst(struct dvb_frontend* fe, - fe_sec_mini_cmd_t minicmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - return av7110_diseqc_send(av7110, 0, NULL, minicmd); -} - -/* simplified code from budget-core.c */ -static int stop_ts_capture(struct av7110 *budget) -{ - dprintk(2, "budget: %p\n", budget); - - if (--budget->feeding1) - return budget->feeding1; - saa7146_write(budget->dev, MC1, MASK_20); /* DMA3 off */ - SAA7146_IER_DISABLE(budget->dev, MASK_10); - SAA7146_ISR_CLEAR(budget->dev, MASK_10); - return 0; -} - -static int start_ts_capture(struct av7110 *budget) -{ - dprintk(2, "budget: %p\n", budget); - - if (budget->feeding1) - return ++budget->feeding1; - memset(budget->grabbing, 0x00, TS_BUFLEN); - budget->ttbp = 0; - SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ - SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ - saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ - return ++budget->feeding1; -} - -static int budget_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *budget = demux->priv; - int status; - - dprintk(2, "av7110: %p\n", budget); - - spin_lock(&budget->feedlock1); - feed->pusi_seen = 0; /* have a clean section start */ - status = start_ts_capture(budget); - spin_unlock(&budget->feedlock1); - return status; -} - -static int budget_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *budget = demux->priv; - int status; - - dprintk(2, "budget: %p\n", budget); - - spin_lock(&budget->feedlock1); - status = stop_ts_capture(budget); - spin_unlock(&budget->feedlock1); - return status; -} - -static void vpeirq(unsigned long cookie) -{ - struct av7110 *budget = (struct av7110 *)cookie; - u8 *mem = (u8 *) (budget->grabbing); - u32 olddma = budget->ttbp; - u32 newdma = saa7146_read(budget->dev, PCI_VDP3); - struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; - - /* nearest lower position divisible by 188 */ - newdma -= newdma % 188; - - if (newdma >= TS_BUFLEN) - return; - - budget->ttbp = newdma; - - if (!budget->feeding1 || (newdma == olddma)) - return; - - /* Ensure streamed PCI data is synced to CPU */ - pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); - -#if 0 - /* track rps1 activity */ - printk("vpeirq: %02x Event Counter 1 0x%04x\n", - mem[olddma], - saa7146_read(budget->dev, EC1R) & 0x3fff); -#endif - - if (newdma > olddma) - /* no wraparound, dump olddma..newdma */ - dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); - else { - /* wraparound, dump olddma..buflen and 0..newdma */ - dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); - dvb_dmx_swfilter_packets(demux, mem, newdma / 188); - } -} - -static int av7110_register(struct av7110 *av7110) -{ - int ret, i; - struct dvb_demux *dvbdemux = &av7110->demux; - struct dvb_demux *dvbdemux1 = &av7110->demux1; - - dprintk(4, "%p\n", av7110); - - if (av7110->registered) - return -1; - - av7110->registered = 1; - - dvbdemux->priv = (void *) av7110; - - for (i = 0; i < 32; i++) - av7110->handle2filter[i] = NULL; - - dvbdemux->filternum = (av7110->full_ts) ? 256 : 32; - dvbdemux->feednum = (av7110->full_ts) ? 256 : 32; - dvbdemux->start_feed = av7110_start_feed; - dvbdemux->stop_feed = av7110_stop_feed; - dvbdemux->write_to_decoder = av7110_write_to_decoder; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - - dvb_dmx_init(&av7110->demux); - av7110->demux.dmx.get_stc = dvb_get_stc; - - av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32; - av7110->dmxdev.demux = &dvbdemux->dmx; - av7110->dmxdev.capabilities = 0; - - dvb_dmxdev_init(&av7110->dmxdev, &av7110->dvb_adapter); - - av7110->hw_frontend.source = DMX_FRONTEND_0; - - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend); - - if (ret < 0) - return ret; - - av7110->mem_frontend.source = DMX_MEMORY_FE; - - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend); - - if (ret < 0) - return ret; - - ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, - &av7110->hw_frontend); - if (ret < 0) - return ret; - - av7110_av_register(av7110); - av7110_ca_register(av7110); - -#ifdef CONFIG_DVB_AV7110_OSD - dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, - &dvbdev_osd, av7110, DVB_DEVICE_OSD); -#endif - - dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); - - if (budgetpatch) { - /* initialize software demux1 without its own frontend - * demux1 hardware is connected to frontend0 of demux0 - */ - dvbdemux1->priv = (void *) av7110; - - dvbdemux1->filternum = 256; - dvbdemux1->feednum = 256; - dvbdemux1->start_feed = budget_start_feed; - dvbdemux1->stop_feed = budget_stop_feed; - dvbdemux1->write_to_decoder = NULL; - - dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - - dvb_dmx_init(&av7110->demux1); - - av7110->dmxdev1.filternum = 256; - av7110->dmxdev1.demux = &dvbdemux1->dmx; - av7110->dmxdev1.capabilities = 0; - - dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter); - - dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx); - printk("dvb-ttpci: additional demux1 for budget-patch registered\n"); - } - return 0; -} - - -static void dvb_unregister(struct av7110 *av7110) -{ - struct dvb_demux *dvbdemux = &av7110->demux; - struct dvb_demux *dvbdemux1 = &av7110->demux1; - - dprintk(4, "%p\n", av7110); - - if (!av7110->registered) - return; - - if (budgetpatch) { - dvb_net_release(&av7110->dvb_net1); - dvbdemux->dmx.close(&dvbdemux1->dmx); - dvb_dmxdev_release(&av7110->dmxdev1); - dvb_dmx_release(&av7110->demux1); - } - - dvb_net_release(&av7110->dvb_net); - - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend); - - dvb_dmxdev_release(&av7110->dmxdev); - dvb_dmx_release(&av7110->demux); - - if (av7110->fe != NULL) { - dvb_unregister_frontend(av7110->fe); - dvb_frontend_detach(av7110->fe); - } - dvb_unregister_device(av7110->osd_dev); - av7110_av_unregister(av7110); - av7110_ca_unregister(av7110); -} - - -/**************************************************************************** - * I2C client commands - ****************************************************************************/ - -int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) -{ - u8 msg[2] = { reg, val }; - struct i2c_msg msgs; - - msgs.flags = 0; - msgs.addr = id / 2; - msgs.len = 2; - msgs.buf = msg; - return i2c_transfer(&av7110->i2c_adap, &msgs, 1); -} - -u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) -{ - u8 mm1[] = {0x00}; - u8 mm2[] = {0x00}; - struct i2c_msg msgs[2]; - - msgs[0].flags = 0; - msgs[1].flags = I2C_M_RD; - msgs[0].addr = msgs[1].addr = id / 2; - mm1[0] = reg; - msgs[0].len = 1; msgs[1].len = 1; - msgs[0].buf = mm1; msgs[1].buf = mm2; - i2c_transfer(&av7110->i2c_adap, msgs, 2); - - return mm2[0]; -} - -/**************************************************************************** - * INITIALIZATION - ****************************************************************************/ - - -static int check_firmware(struct av7110* av7110) -{ - u32 crc = 0, len = 0; - unsigned char *ptr; - - /* check for firmware magic */ - ptr = av7110->bin_fw; - if (ptr[0] != 'A' || ptr[1] != 'V' || - ptr[2] != 'F' || ptr[3] != 'W') { - printk("dvb-ttpci: this is not an av7110 firmware\n"); - return -EINVAL; - } - ptr += 4; - - /* check dpram file */ - crc = get_unaligned_be32(ptr); - ptr += 4; - len = get_unaligned_be32(ptr); - ptr += 4; - if (len >= 512) { - printk("dvb-ttpci: dpram file is way too big.\n"); - return -EINVAL; - } - if (crc != crc32_le(0, ptr, len)) { - printk("dvb-ttpci: crc32 of dpram file does not match.\n"); - return -EINVAL; - } - av7110->bin_dpram = ptr; - av7110->size_dpram = len; - ptr += len; - - /* check root file */ - crc = get_unaligned_be32(ptr); - ptr += 4; - len = get_unaligned_be32(ptr); - ptr += 4; - - if (len <= 200000 || len >= 300000 || - len > ((av7110->bin_fw + av7110->size_fw) - ptr)) { - printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); - return -EINVAL; - } - if( crc != crc32_le(0, ptr, len)) { - printk("dvb-ttpci: crc32 of root file does not match.\n"); - return -EINVAL; - } - av7110->bin_root = ptr; - av7110->size_root = len; - return 0; -} - -static void put_firmware(struct av7110* av7110) -{ - vfree(av7110->bin_fw); -} - -static int get_firmware(struct av7110* av7110) -{ - int ret; - const struct firmware *fw; - - /* request the av7110 firmware, this will block until someone uploads it */ - ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev); - if (ret) { - if (ret == -ENOENT) { - printk(KERN_ERR "dvb-ttpci: could not load firmware," - " file not found: dvb-ttpci-01.fw\n"); - printk(KERN_ERR "dvb-ttpci: usually this should be in " - "/usr/lib/hotplug/firmware or /lib/firmware\n"); - printk(KERN_ERR "dvb-ttpci: and can be downloaded from" - " http://www.linuxtv.org/download/dvb/firmware/\n"); - } else - printk(KERN_ERR "dvb-ttpci: cannot request firmware" - " (error %i)\n", ret); - return -EINVAL; - } - - if (fw->size <= 200000) { - printk("dvb-ttpci: this firmware is way too small.\n"); - release_firmware(fw); - return -EINVAL; - } - - /* check if the firmware is available */ - av7110->bin_fw = vmalloc(fw->size); - if (NULL == av7110->bin_fw) { - dprintk(1, "out of memory\n"); - release_firmware(fw); - return -ENOMEM; - } - - memcpy(av7110->bin_fw, fw->data, fw->size); - av7110->size_fw = fw->size; - if ((ret = check_firmware(av7110))) - vfree(av7110->bin_fw); - - release_firmware(fw); - return ret; -} - -static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u8 pwr = 0; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div = (p->frequency + 479500) / 125; - - if (p->frequency > 2000000) - pwr = 3; - else if (p->frequency > 1800000) - pwr = 2; - else if (p->frequency > 1600000) - pwr = 1; - else if (p->frequency > 1200000) - pwr = 0; - else if (p->frequency >= 1100000) - pwr = 1; - else - pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1x93_config alps_bsrv2_config = { - .demod_address = 0x08, - .xin = 90100000UL, - .invert_pwm = 0, -}; - -static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (p->frequency + 35937500 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x85 | ((div >> 10) & 0x60); - data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1820_config alps_tdbe2_config = { - .demod_address = 0x09, - .xin = 57840000UL, - .invert = 1, - .selagc = VES1820_SELAGC_SIGNAMPERR, -}; - - - - -static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = p->frequency / 125; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = 0x00; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct tda8083_config grundig_29504_451_config = { - .demod_address = 0x68, -}; - - - -static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u32 f = p->frequency; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (f + 36125000 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1820_config philips_cd1516_config = { - .demod_address = 0x09, - .xin = 57840000UL, - .invert = 1, - .selagc = VES1820_SELAGC_SIGNAMPERR, -}; - - - -static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div, pwr; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (p->frequency + 36200000) / 166666; - - if (p->frequency <= 782000000) - pwr = 1; - else - pwr = 2; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x85; - data[3] = pwr << 6; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) -{ -#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) - struct av7110* av7110 = fe->dvb->priv; - - return request_firmware(fw, name, &av7110->dev->pci->dev); -#else - return -EINVAL; -#endif -} - -static struct sp8870_config alps_tdlb7_config = { - - .demod_address = 0x71, - .request_firmware = alps_tdlb7_request_firmware, -}; - - -static u8 nexusca_stv0297_inittab[] = { - 0x80, 0x01, - 0x80, 0x00, - 0x81, 0x01, - 0x81, 0x00, - 0x00, 0x09, - 0x01, 0x69, - 0x03, 0x00, - 0x04, 0x00, - 0x07, 0x00, - 0x08, 0x00, - 0x20, 0x00, - 0x21, 0x40, - 0x22, 0x00, - 0x23, 0x00, - 0x24, 0x40, - 0x25, 0x88, - 0x30, 0xff, - 0x31, 0x00, - 0x32, 0xff, - 0x33, 0x00, - 0x34, 0x50, - 0x35, 0x7f, - 0x36, 0x00, - 0x37, 0x20, - 0x38, 0x00, - 0x40, 0x1c, - 0x41, 0xff, - 0x42, 0x29, - 0x43, 0x00, - 0x44, 0xff, - 0x45, 0x00, - 0x46, 0x00, - 0x49, 0x04, - 0x4a, 0x00, - 0x4b, 0x7b, - 0x52, 0x30, - 0x55, 0xae, - 0x56, 0x47, - 0x57, 0xe1, - 0x58, 0x3a, - 0x5a, 0x1e, - 0x5b, 0x34, - 0x60, 0x00, - 0x63, 0x00, - 0x64, 0x00, - 0x65, 0x00, - 0x66, 0x00, - 0x67, 0x00, - 0x68, 0x00, - 0x69, 0x00, - 0x6a, 0x02, - 0x6b, 0x00, - 0x70, 0xff, - 0x71, 0x00, - 0x72, 0x00, - 0x73, 0x00, - 0x74, 0x0c, - 0x80, 0x00, - 0x81, 0x00, - 0x82, 0x00, - 0x83, 0x00, - 0x84, 0x04, - 0x85, 0x80, - 0x86, 0x24, - 0x87, 0x78, - 0x88, 0x10, - 0x89, 0x00, - 0x90, 0x01, - 0x91, 0x01, - 0xa0, 0x04, - 0xa1, 0x00, - 0xa2, 0x00, - 0xb0, 0x91, - 0xb1, 0x0b, - 0xc0, 0x53, - 0xc1, 0x70, - 0xc2, 0x12, - 0xd0, 0x00, - 0xd1, 0x00, - 0xd2, 0x00, - 0xd3, 0x00, - 0xd4, 0x00, - 0xd5, 0x00, - 0xde, 0x00, - 0xdf, 0x00, - 0x61, 0x49, - 0x62, 0x0b, - 0x53, 0x08, - 0x59, 0x08, - 0xff, 0xff, -}; - -static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; - struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; - int i; - - div = (p->frequency + 36150000 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0xce; - - if (p->frequency < 45000000) - return -EINVAL; - else if (p->frequency < 137000000) - data[3] = 0x01; - else if (p->frequency < 403000000) - data[3] = 0x02; - else if (p->frequency < 860000000) - data[3] = 0x04; - else - return -EINVAL; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { - printk("nexusca: pll transfer failed!\n"); - return -EIO; - } - - // wait for PLL lock - for(i = 0; i < 20; i++) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) - if (data[0] & 0x40) break; - msleep(10); - } - - return 0; -} - -static struct stv0297_config nexusca_stv0297_config = { - - .demod_address = 0x1C, - .inittab = nexusca_stv0297_inittab, - .invert = 1, - .stop_during_read = 1, -}; - - - -static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 cfg, cpump, band_select; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (36125000 + p->frequency) / 166666; - - cfg = 0x88; - - if (p->frequency < 175000000) - cpump = 2; - else if (p->frequency < 390000000) - cpump = 1; - else if (p->frequency < 470000000) - cpump = 2; - else if (p->frequency < 750000000) - cpump = 1; - else - cpump = 3; - - if (p->frequency < 175000000) - band_select = 0x0e; - else if (p->frequency < 470000000) - band_select = 0x05; - else - band_select = 0x03; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = ((div >> 10) & 0x60) | cfg; - data[3] = (cpump << 6) | band_select; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct l64781_config grundig_29504_401_config = { - .demod_address = 0x55, -}; - - - -static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) -{ - int ret = 0; - int synced = (status & FE_HAS_LOCK) ? 1 : 0; - - av7110->fe_status = status; - - if (av7110->fe_synced == synced) - return 0; - - if (av7110->playing) { - av7110->fe_synced = synced; - return 0; - } - - if (mutex_lock_interruptible(&av7110->pid_mutex)) - return -ERESTARTSYS; - - if (synced) { - ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], - av7110->pids[DMX_PES_AUDIO], - av7110->pids[DMX_PES_TELETEXT], 0, - av7110->pids[DMX_PES_PCR]); - if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); - } else { - ret = SetPIDs(av7110, 0, 0, 0, 0, 0); - if (!ret) { - ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); - if (!ret) - ret = av7110_wait_msgstate(av7110, GPMQBusy); - } - } - - if (!ret) - av7110->fe_synced = synced; - - mutex_unlock(&av7110->pid_mutex); - return ret; -} - -static int av7110_fe_set_frontend(struct dvb_frontend *fe) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_set_frontend(fe); - - return ret; -} - -static int av7110_fe_init(struct dvb_frontend* fe) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_init(fe); - return ret; -} - -static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) -{ - struct av7110* av7110 = fe->dvb->priv; - - /* call the real implementation */ - int ret = av7110->fe_read_status(fe, status); - if (!ret) - if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) - ret = av7110_fe_lock_fix(av7110, *status); - return ret; -} - -static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_diseqc_reset_overload(fe); - return ret; -} - -static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd* cmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_master_cmd = *cmd; - ret = av7110->fe_diseqc_send_master_cmd(fe, cmd); - } - return ret; -} - -static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_minicmd = minicmd; - ret = av7110->fe_diseqc_send_burst(fe, minicmd); - } - return ret; -} - -static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_tone = tone; - ret = av7110->fe_set_tone(fe, tone); - } - return ret; -} - -static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_voltage = voltage; - ret = av7110->fe_set_voltage(fe, voltage); - } - return ret; -} - -static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd); - return ret; -} - -static void dvb_s_recover(struct av7110* av7110) -{ - av7110_fe_init(av7110->fe); - - av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage); - if (av7110->saved_master_cmd.msg_len) { - msleep(20); - av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd); - } - msleep(20); - av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd); - msleep(20); - av7110_fe_set_tone(av7110->fe, av7110->saved_tone); - - av7110_fe_set_frontend(av7110->fe); -} - -static u8 read_pwm(struct av7110* av7110) -{ - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, - { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; - - if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -static int frontend_init(struct av7110 *av7110) -{ - int ret; - - if (av7110->dev->pci->subsystem_vendor == 0x110a) { - switch(av7110->dev->pci->subsystem_device) { - case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) - av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, - &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; - } - break; - } - - } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { - switch(av7110->dev->pci->subsystem_device) { - case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X - case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X - case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE - - // try the ALPS BSRV2 first of all - av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - break; - } - - // try the ALPS BSRU6 now - av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - av7110->fe->tuner_priv = &av7110->i2c_adap; - - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - break; - } - - // Try the grundig 29504-451 - av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - break; - } - - /* Try DVB-C cards */ - switch(av7110->dev->pci->subsystem_device) { - case 0x0000: - /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ - av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, - read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; - } - break; - case 0x0003: - /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ - av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, - read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - } - break; - } - break; - - case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X - // try ALPS TDLB7 first, then Grundig 29504-401 - av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; - break; - } - /* fall-thru */ - - case 0x0008: // Hauppauge/TT DVB-T - // Grundig 29504-401 - av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap); - if (av7110->fe) - av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; - break; - - case 0x0002: // Hauppauge/TT DVB-C premium rev2.X - - av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - } - break; - - case 0x0004: // Galaxis DVB-S rev1.3 - /* ALPS BSRV2 */ - av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - } - break; - - case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */ - /* Grundig 29504-451 */ - av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - } - break; - - case 0x000A: // Hauppauge/TT Nexus-CA rev1.X - - av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params; - - /* set TDA9819 into DVB mode */ - saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) - saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) - - /* tuner on this needs a slower i2c bus speed */ - av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - break; - } - break; - - case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */ - /* ALPS BSBE1 */ - av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; - av7110->fe->tuner_priv = &av7110->i2c_adap; - - if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { - printk("dvb-ttpci: LNBP21 not found!\n"); - if (av7110->fe->ops.release) - av7110->fe->ops.release(av7110->fe); - av7110->fe = NULL; - } else { - av7110->fe->ops.dishnetwork_send_legacy_command = NULL; - av7110->recover = dvb_s_recover; - } - } - break; - } - } - - if (!av7110->fe) { - /* FIXME: propagate the failure code from the lower layers */ - ret = -ENOMEM; - printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - av7110->dev->pci->vendor, - av7110->dev->pci->device, - av7110->dev->pci->subsystem_vendor, - av7110->dev->pci->subsystem_device); - } else { - FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init); - FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status); - FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload); - FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd); - FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst); - FE_FUNC_OVERRIDE(av7110->fe->ops.set_tone, av7110->fe_set_tone, av7110_fe_set_tone); - FE_FUNC_OVERRIDE(av7110->fe->ops.set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage); - FE_FUNC_OVERRIDE(av7110->fe->ops.dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); - FE_FUNC_OVERRIDE(av7110->fe->ops.set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); - - ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); - if (ret < 0) { - printk("av7110: Frontend registration failed!\n"); - dvb_frontend_detach(av7110->fe); - av7110->fe = NULL; - } - } - return ret; -} - -/* Budgetpatch note: - * Original hardware design by Roberto Deza: - * There is a DVB_Wiki at - * http://www.linuxtv.org/ - * - * New software triggering design by Emard that works on - * original Roberto Deza's hardware: - * - * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. - * GPIO3 is in budget-patch hardware connectd to port B VSYNC - * HS is an internal event of 7146, accessible with RPS - * and temporarily raised high every n lines - * (n in defined in the RPS_THRESH1 counter threshold) - * I think HS is raised high on the beginning of the n-th line - * and remains high until this n-th line that triggered - * it is completely received. When the receiption of n-th line - * ends, HS is lowered. - * - * To transmit data over DMA, 7146 needs changing state at - * port B VSYNC pin. Any changing of port B VSYNC will - * cause some DMA data transfer, with more or less packets loss. - * It depends on the phase and frequency of VSYNC and - * the way of 7146 is instructed to trigger on port B (defined - * in DD1_INIT register, 3rd nibble from the right valid - * numbers are 0-7, see datasheet) - * - * The correct triggering can minimize packet loss, - * dvbtraffic should give this stable bandwidths: - * 22k transponder = 33814 kbit/s - * 27.5k transponder = 38045 kbit/s - * by experiment it is found that the best results - * (stable bandwidths and almost no packet loss) - * are obtained using DD1_INIT triggering number 2 - * (Va at rising edge of VS Fa = HS x VS-failing forced toggle) - * and a VSYNC phase that occurs in the middle of DMA transfer - * (about byte 188*512=96256 in the DMA window). - * - * Phase of HS is still not clear to me how to control, - * It just happens to be so. It can be seen if one enables - * RPS_IRQ and print Event Counter 1 in vpeirq(). Every - * time RPS_INTERRUPT is called, the Event Counter 1 will - * increment. That's how the 7146 is programmed to do event - * counting in this budget-patch.c - * I *think* HPS setting has something to do with the phase - * of HS but I can't be 100% sure in that. - * - * hardware debug note: a working budget card (including budget patch) - * with vpeirq() interrupt setup in mode "0x90" (every 64K) will - * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes - * and that means 3*25=75 Hz of interrupt freqency, as seen by - * watch cat /proc/interrupts - * - * If this frequency is 3x lower (and data received in the DMA - * buffer don't start with 0x47, but in the middle of packets, - * whose lengths appear to be like 188 292 188 104 etc. - * this means VSYNC line is not connected in the hardware. - * (check soldering pcb and pins) - * The same behaviour of missing VSYNC can be duplicated on budget - * cards, by seting DD1_INIT trigger mode 7 in 3rd nibble. - */ -static int __devinit av7110_attach(struct saa7146_dev* dev, - struct saa7146_pci_extension_data *pci_ext) -{ - const int length = TS_WIDTH * TS_HEIGHT; - struct pci_dev *pdev = dev->pci; - struct av7110 *av7110; - struct task_struct *thread; - int ret, count = 0; - - dprintk(4, "dev: %p\n", dev); - - /* Set RPS_IRQ to 1 to track rps1 activity. - * Enabling this won't send any interrupt to PC CPU. - */ -#define RPS_IRQ 0 - - if (budgetpatch == 1) { - budgetpatch = 0; - /* autodetect the presence of budget patch - * this only works if saa7146 has been recently - * reset with with MASK_31 to MC1 - * - * will wait for VBI_B event (vertical blank at port B) - * and will reset GPIO3 after VBI_B is detected. - * (GPIO3 should be raised high by CPU to - * test if GPIO3 will generate vertical blank signal - * in budget patch GPIO3 is connected to VSYNC_B - */ - - /* RESET SAA7146 */ - saa7146_write(dev, MC1, MASK_31); - /* autodetection success seems to be time-dependend after reset */ - - /* Fix VSYNC level */ - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - /* set vsync_b triggering */ - saa7146_write(dev, DD1_STREAM_B, 0); - /* port B VSYNC at rising edge */ - saa7146_write(dev, DD1_INIT, 0x00000200); - saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI - saa7146_write(dev, MC2, - 1 * (MASK_08 | MASK_24) | // BRS control - 0 * (MASK_09 | MASK_25) | // a - 1 * (MASK_10 | MASK_26) | // b - 0 * (MASK_06 | MASK_22) | // HPS_CTRL1 - 0 * (MASK_05 | MASK_21) | // HPS_CTRL2 - 0 * (MASK_01 | MASK_15) // DEBI - ); - - /* start writing RPS1 code from beginning */ - count = 0; - /* Disable RPS1 */ - saa7146_write(dev, MC1, MASK_29); - /* RPS1 timeout disable */ - saa7146_write(dev, RPS_TOV1, 0); - WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - /* issue RPS1 interrupt to increment counter */ - WRITE_RPS1(CMD_INTERRUPT); -#endif - WRITE_RPS1(CMD_STOP); - /* Jump to begin of RPS program as safety measure (p37) */ - WRITE_RPS1(CMD_JUMP); - WRITE_RPS1(dev->d_rps1.dma_handle); - -#if RPS_IRQ - /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) - * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled - * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called - */ - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - /* set event counter 1 threshold to maximum allowed value (rEC p55) */ - saa7146_write(dev, ECT1R, 0x3fff ); -#endif - /* Set RPS1 Address register to point to RPS code (r108 p42) */ - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - /* Enable RPS1, (rFC p33) */ - saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); - - mdelay(10); - /* now send VSYNC_B to rps1 by rising GPIO3 */ - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - mdelay(10); - /* if rps1 responded by lowering the GPIO3, - * then we have budgetpatch hardware - */ - if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) { - budgetpatch = 1; - printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); - } - /* Disable RPS1 */ - saa7146_write(dev, MC1, ( MASK_29 )); -#if RPS_IRQ - printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); -#endif - } - - /* prepare the av7110 device struct */ - av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL); - if (!av7110) { - dprintk(1, "out of memory\n"); - return -ENOMEM; - } - - av7110->card_name = (char*) pci_ext->ext_priv; - av7110->dev = dev; - dev->ext_priv = av7110; - - ret = get_firmware(av7110); - if (ret < 0) - goto err_kfree_0; - - ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, - THIS_MODULE, &dev->pci->dev, adapter_nr); - if (ret < 0) - goto err_put_firmware_1; - - /* the Siemens DVB needs this if you want to have the i2c chips - get recognized before the main driver is fully loaded */ - saa7146_write(dev, GPIO_CTRL, 0x500000); - - strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name)); - - saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ - - ret = i2c_add_adapter(&av7110->i2c_adap); - if (ret < 0) - goto err_dvb_unregister_adapter_2; - - ttpci_eeprom_parse_mac(&av7110->i2c_adap, - av7110->dvb_adapter.proposed_mac); - ret = -ENOMEM; - - /* full-ts mod? */ - if (full_ts) - av7110->full_ts = true; - - /* check for full-ts flag in eeprom */ - if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { - u8 flags = i2c_readreg(av7110, 0xaa, 2); - if (flags != 0xff && (flags & 0x01)) - av7110->full_ts = true; - } - - if (av7110->full_ts) { - printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); - spin_lock_init(&av7110->feedlock1); - av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, - &av7110->pt); - if (!av7110->grabbing) - goto err_i2c_del_3; - - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, MC2, (MASK_10 | MASK_26)); - - saa7146_write(dev, DD1_INIT, 0x00000600); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - saa7146_write(dev, BRS_CTRL, 0x60000000); - saa7146_write(dev, MC2, MASK_08 | MASK_24); - - /* dma3 */ - saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); - saa7146_write(dev, BASE_ODD3, 0); - saa7146_write(dev, BASE_EVEN3, 0); - saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); - saa7146_write(dev, PITCH3, TS_WIDTH); - saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); - saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); - saa7146_write(dev, MC2, MASK_04 | MASK_20); - - tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); - - } else if (budgetpatch) { - spin_lock_init(&av7110->feedlock1); - av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, - &av7110->pt); - if (!av7110->grabbing) - goto err_i2c_del_3; - - saa7146_write(dev, PCI_BT_V1, 0x1c1f101f); - saa7146_write(dev, BCS_CTRL, 0x80400040); - /* set dd1 stream a & b */ - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, DD1_INIT, 0x03000200); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - saa7146_write(dev, BASE_ODD3, 0); - saa7146_write(dev, BASE_EVEN3, 0); - saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); - saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); - - saa7146_write(dev, PITCH3, TS_WIDTH); - saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); - - /* upload all */ - saa7146_write(dev, MC2, 0x077c077c); - saa7146_write(dev, GPIO_CTRL, 0x000000); -#if RPS_IRQ - /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) - * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled - * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called - */ - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - /* set event counter 1 threshold to maximum allowed value (rEC p55) */ - saa7146_write(dev, ECT1R, 0x3fff ); -#endif - /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ - count = 0; - - /* Wait Source Line Counter Threshold (p36) */ - WRITE_RPS1(CMD_PAUSE | EVT_HS); - /* Set GPIO3=1 (p42) */ - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); -#if RPS_IRQ - /* issue RPS1 interrupt */ - WRITE_RPS1(CMD_INTERRUPT); -#endif - /* Wait reset Source Line Counter Threshold (p36) */ - WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); - /* Set GPIO3=0 (p42) */ - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - /* issue RPS1 interrupt */ - WRITE_RPS1(CMD_INTERRUPT); -#endif - /* Jump to begin of RPS program (p37) */ - WRITE_RPS1(CMD_JUMP); - WRITE_RPS1(dev->d_rps1.dma_handle); - - /* Fix VSYNC level */ - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - /* Set RPS1 Address register to point to RPS code (r108 p42) */ - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - /* Set Source Line Counter Threshold, using BRS (rCC p43) - * It generates HS event every TS_HEIGHT lines - * this is related to TS_WIDTH set in register - * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits - * are set to TS_WIDTH bytes (TS_WIDTH=2*188), - * then RPS_THRESH1 should be set to trigger - * every TS_HEIGHT (512) lines. - */ - saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); - - /* Enable RPS1 (rFC p33) */ - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - - /* end of budgetpatch register initialization */ - tasklet_init (&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); - } else { - saa7146_write(dev, PCI_BT_V1, 0x1c00101f); - saa7146_write(dev, BCS_CTRL, 0x80400040); - - /* set dd1 stream a & b */ - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, DD1_INIT, 0x03000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - /* upload all */ - saa7146_write(dev, MC2, 0x077c077c); - saa7146_write(dev, GPIO_CTRL, 0x000000); - } - - tasklet_init (&av7110->debi_tasklet, debiirq, (unsigned long) av7110); - tasklet_init (&av7110->gpio_tasklet, gpioirq, (unsigned long) av7110); - - mutex_init(&av7110->pid_mutex); - - /* locks for data transfers from/to AV7110 */ - spin_lock_init(&av7110->debilock); - mutex_init(&av7110->dcomlock); - av7110->debitype = -1; - - /* default OSD window */ - av7110->osdwin = 1; - mutex_init(&av7110->osd_mutex); - - /* TV standard */ - av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC - : AV7110_VIDEO_MODE_PAL; - - /* ARM "watchdog" */ - init_waitqueue_head(&av7110->arm_wait); - av7110->arm_thread = NULL; - - /* allocate and init buffers */ - av7110->debi_virt = pci_alloc_consistent(pdev, 8192, &av7110->debi_bus); - if (!av7110->debi_virt) - goto err_saa71466_vfree_4; - - - av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); - if (!av7110->iobuf) - goto err_pci_free_5; - - ret = av7110_av_init(av7110); - if (ret < 0) - goto err_iobuf_vfree_6; - - /* init BMP buffer */ - av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN; - init_waitqueue_head(&av7110->bmpq); - - ret = av7110_ca_init(av7110); - if (ret < 0) - goto err_av7110_av_exit_7; - - /* load firmware into AV7110 cards */ - ret = av7110_bootarm(av7110); - if (ret < 0) - goto err_av7110_ca_exit_8; - - ret = av7110_firmversion(av7110); - if (ret < 0) - goto err_stop_arm_9; - - if (FW_VERSION(av7110->arm_app)<0x2501) - printk ("dvb-ttpci: Warning, firmware version 0x%04x is too old. " - "System might be unstable!\n", FW_VERSION(av7110->arm_app)); - - thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); - if (IS_ERR(thread)) { - ret = PTR_ERR(thread); - goto err_stop_arm_9; - } - av7110->arm_thread = thread; - - /* set initial volume in mixer struct */ - av7110->mixer.volume_left = volume; - av7110->mixer.volume_right = volume; - - ret = av7110_register(av7110); - if (ret < 0) - goto err_arm_thread_stop_10; - - init_av7110_av(av7110); - - /* special case DVB-C: these cards have an analog tuner - plus need some special handling, so we have separate - saa7146_ext_vv data for these... */ - ret = av7110_init_v4l(av7110); - if (ret < 0) - goto err_av7110_unregister_11; - - av7110->dvb_adapter.priv = av7110; - ret = frontend_init(av7110); - if (ret < 0) - goto err_av7110_exit_v4l_12; - -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) - av7110_ir_init(av7110); -#endif - printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); - av7110_num++; -out: - return ret; - -err_av7110_exit_v4l_12: - av7110_exit_v4l(av7110); -err_av7110_unregister_11: - dvb_unregister(av7110); -err_arm_thread_stop_10: - av7110_arm_sync(av7110); -err_stop_arm_9: - /* Nothing to do. Rejoice. */ -err_av7110_ca_exit_8: - av7110_ca_exit(av7110); -err_av7110_av_exit_7: - av7110_av_exit(av7110); -err_iobuf_vfree_6: - vfree(av7110->iobuf); -err_pci_free_5: - pci_free_consistent(pdev, 8192, av7110->debi_virt, av7110->debi_bus); -err_saa71466_vfree_4: - if (av7110->grabbing) - saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt); -err_i2c_del_3: - i2c_del_adapter(&av7110->i2c_adap); -err_dvb_unregister_adapter_2: - dvb_unregister_adapter(&av7110->dvb_adapter); -err_put_firmware_1: - put_firmware(av7110); -err_kfree_0: - kfree(av7110); - goto out; -} - -static int __devexit av7110_detach(struct saa7146_dev* saa) -{ - struct av7110 *av7110 = saa->ext_priv; - dprintk(4, "%p\n", av7110); - -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) - av7110_ir_exit(av7110); -#endif - if (budgetpatch || av7110->full_ts) { - if (budgetpatch) { - /* Disable RPS1 */ - saa7146_write(saa, MC1, MASK_29); - /* VSYNC LOW (inactive) */ - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); - } - saa7146_write(saa, MC1, MASK_20); /* DMA3 off */ - SAA7146_IER_DISABLE(saa, MASK_10); - SAA7146_ISR_CLEAR(saa, MASK_10); - msleep(50); - tasklet_kill(&av7110->vpe_tasklet); - saa7146_vfree_destroy_pgtable(saa->pci, av7110->grabbing, &av7110->pt); - } - av7110_exit_v4l(av7110); - - av7110_arm_sync(av7110); - - tasklet_kill(&av7110->debi_tasklet); - tasklet_kill(&av7110->gpio_tasklet); - - dvb_unregister(av7110); - - SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); - SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); - - av7110_ca_exit(av7110); - av7110_av_exit(av7110); - - vfree(av7110->iobuf); - pci_free_consistent(saa->pci, 8192, av7110->debi_virt, - av7110->debi_bus); - - i2c_del_adapter(&av7110->i2c_adap); - - dvb_unregister_adapter (&av7110->dvb_adapter); - - av7110_num--; - - put_firmware(av7110); - - kfree(av7110); - - saa->ext_priv = NULL; - - return 0; -} - - -static void av7110_irq(struct saa7146_dev* dev, u32 *isr) -{ - struct av7110 *av7110 = dev->ext_priv; - - //print_time("av7110_irq"); - - /* Note: Don't try to handle the DEBI error irq (MASK_18), in - * intel mode the timeout is asserted all the time... - */ - - if (*isr & MASK_19) { - //printk("av7110_irq: DEBI\n"); - /* Note 1: The DEBI irq is level triggered: We must enable it - * only after we started a DMA xfer, and disable it here - * immediately, or it will be signalled all the time while - * DEBI is idle. - * Note 2: You would think that an irq which is masked is - * not signalled by the hardware. Not so for the SAA7146: - * An irq is signalled as long as the corresponding bit - * in the ISR is set, and disabling irqs just prevents the - * hardware from setting the ISR bit. This means a) that we - * must clear the ISR *after* disabling the irq (which is why - * we must do it here even though saa7146_core did it already), - * and b) that if we were to disable an edge triggered irq - * (like the gpio irqs sadly are) temporarily we would likely - * loose some. This sucks :-( - */ - SAA7146_IER_DISABLE(av7110->dev, MASK_19); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19); - tasklet_schedule(&av7110->debi_tasklet); - } - - if (*isr & MASK_03) { - //printk("av7110_irq: GPIO\n"); - tasklet_schedule(&av7110->gpio_tasklet); - } - - if (*isr & MASK_10) - tasklet_schedule(&av7110->vpe_tasklet); -} - - -static struct saa7146_extension av7110_extension_driver; - -#define MAKE_AV7110_INFO(x_var,x_name) \ -static struct saa7146_pci_extension_data x_var = { \ - .ext_priv = x_name, \ - .ext = &av7110_extension_driver } - -MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); -MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); -MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); -MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); -MAKE_AV7110_INFO(tts_2_X, "Technotrend/Hauppauge WinTV Nexus-S rev2.X"); -MAKE_AV7110_INFO(tts_2_3, "Technotrend/Hauppauge WinTV Nexus-S rev2.3"); -MAKE_AV7110_INFO(tts_1_3se, "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE"); -MAKE_AV7110_INFO(ttt, "Technotrend/Hauppauge DVB-T"); -MAKE_AV7110_INFO(fsc, "Fujitsu Siemens DVB-C"); -MAKE_AV7110_INFO(fss, "Fujitsu Siemens DVB-S rev1.6"); -MAKE_AV7110_INFO(gxs_1_3, "Galaxis DVB-S rev1.3"); - -static struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(fsc, 0x110a, 0x0000), - MAKE_EXTENSION_PCI(tts_1_X_fsc, 0x13c2, 0x0000), - MAKE_EXTENSION_PCI(ttt_1_X, 0x13c2, 0x0001), - MAKE_EXTENSION_PCI(ttc_2_X, 0x13c2, 0x0002), - MAKE_EXTENSION_PCI(tts_2_X, 0x13c2, 0x0003), - MAKE_EXTENSION_PCI(gxs_1_3, 0x13c2, 0x0004), - MAKE_EXTENSION_PCI(fss, 0x13c2, 0x0006), - MAKE_EXTENSION_PCI(ttt, 0x13c2, 0x0008), - MAKE_EXTENSION_PCI(ttc_1_X, 0x13c2, 0x000a), - MAKE_EXTENSION_PCI(tts_2_3, 0x13c2, 0x000e), - MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), - -/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 -/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? - - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - - -static struct saa7146_extension av7110_extension_driver = { - .name = "av7110", - .flags = SAA7146_USE_I2C_IRQ, - - .module = THIS_MODULE, - .pci_tbl = &pci_tbl[0], - .attach = av7110_attach, - .detach = __devexit_p(av7110_detach), - - .irq_mask = MASK_19 | MASK_03 | MASK_10, - .irq_func = av7110_irq, -}; - - -static int __init av7110_init(void) -{ - int retval; - retval = saa7146_register_extension(&av7110_extension_driver); - return retval; -} - - -static void __exit av7110_exit(void) -{ - saa7146_unregister_extension(&av7110_extension_driver); -} - -module_init(av7110_init); -module_exit(av7110_exit); - -MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by " - "Siemens, Technotrend, Hauppauge"); -MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h deleted file mode 100644 index 88b3b2d6cc0e..000000000000 --- a/drivers/media/dvb/ttpci/av7110.h +++ /dev/null @@ -1,314 +0,0 @@ -#ifndef _AV7110_H_ -#define _AV7110_H_ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "dvbdev.h" -#include "demux.h" -#include "dvb_demux.h" -#include "dmxdev.h" -#include "dvb_filter.h" -#include "dvb_net.h" -#include "dvb_ringbuffer.h" -#include "dvb_frontend.h" -#include "ves1820.h" -#include "ves1x93.h" -#include "stv0299.h" -#include "tda8083.h" -#include "sp8870.h" -#include "stv0297.h" -#include "l64781.h" - -#include - - -#define ANALOG_TUNER_VES1820 1 -#define ANALOG_TUNER_STV0297 2 - -extern int av7110_debug; - -#define dprintk(level,args...) \ - do { if ((av7110_debug & level)) { printk("dvb-ttpci: %s(): ", __func__); printk(args); } } while (0) - -#define MAXFILT 32 - -enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM}; - -enum av7110_video_mode { - AV7110_VIDEO_MODE_PAL = 0, - AV7110_VIDEO_MODE_NTSC = 1 -}; - -struct av7110_p2t { - u8 pes[TS_SIZE]; - u8 counter; - long int pos; - int frags; - struct dvb_demux_feed *feed; -}; - -/* video MPEG decoder events: */ -/* (code copied from dvb_frontend.c, should maybe be factored out...) */ -#define MAX_VIDEO_EVENT 8 -struct dvb_video_events { - struct video_event events[MAX_VIDEO_EVENT]; - int eventw; - int eventr; - int overflow; - wait_queue_head_t wait_queue; - spinlock_t lock; -}; - - -struct av7110; - -/* infrared remote control */ -struct infrared { - u16 key_map[256]; - struct input_dev *input_dev; - char input_phys[32]; - struct timer_list keyup_timer; - struct tasklet_struct ir_tasklet; - void (*ir_handler)(struct av7110 *av7110, u32 ircom); - u32 ir_command; - u32 ir_config; - u32 device_mask; - u8 protocol; - u8 inversion; - u16 last_key; - u16 last_toggle; - u8 delay_timer_finished; -}; - - -/* place to store all the necessary device information */ -struct av7110 { - - /* devices */ - - struct dvb_device dvb_dev; - struct dvb_net dvb_net; - - struct video_device *v4l_dev; - struct video_device *vbi_dev; - - struct saa7146_dev *dev; - - struct i2c_adapter i2c_adap; - - char *card_name; - - /* support for analog module of dvb-c */ - int analog_tuner_flags; - int current_input; - u32 current_freq; - - struct tasklet_struct debi_tasklet; - struct tasklet_struct gpio_tasklet; - - int adac_type; /* audio DAC type */ -#define DVB_ADAC_TI 0 -#define DVB_ADAC_CRYSTAL 1 -#define DVB_ADAC_MSP34x0 2 -#define DVB_ADAC_MSP34x5 3 -#define DVB_ADAC_NONE -1 - - - /* buffers */ - - void *iobuf; /* memory for all buffers */ - struct dvb_ringbuffer avout; /* buffer for video or A/V mux */ -#define AVOUTLEN (128*1024) - struct dvb_ringbuffer aout; /* buffer for audio */ -#define AOUTLEN (64*1024) - void *bmpbuf; -#define BMPLEN (8*32768+1024) - - /* bitmap buffers and states */ - - int bmpp; - int bmplen; - volatile int bmp_state; -#define BMP_NONE 0 -#define BMP_LOADING 1 -#define BMP_LOADED 2 - wait_queue_head_t bmpq; - - - /* DEBI and polled command interface */ - - spinlock_t debilock; - struct mutex dcomlock; - volatile int debitype; - volatile int debilen; - - - /* Recording and playback flags */ - - int rec_mode; - int playing; -#define RP_NONE 0 -#define RP_VIDEO 1 -#define RP_AUDIO 2 -#define RP_AV 3 - - - /* OSD */ - - int osdwin; /* currently active window */ - u16 osdbpp[8]; - struct mutex osd_mutex; - - /* CA */ - - ca_slot_info_t ci_slot[2]; - - enum av7110_video_mode vidmode; - struct dmxdev dmxdev; - struct dvb_demux demux; - - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - - /* for budget mode demux1 */ - struct dmxdev dmxdev1; - struct dvb_demux demux1; - struct dvb_net dvb_net1; - spinlock_t feedlock1; - int feeding1; - u32 ttbp; - unsigned char *grabbing; - struct saa7146_pgtable pt; - struct tasklet_struct vpe_tasklet; - bool full_ts; - - int fe_synced; - struct mutex pid_mutex; - - int video_blank; - struct video_status videostate; - u16 display_panscan; - int display_ar; - int trickmode; -#define TRICK_NONE 0 -#define TRICK_FAST 1 -#define TRICK_SLOW 2 -#define TRICK_FREEZE 3 - struct audio_status audiostate; - - struct dvb_demux_filter *handle2filter[32]; - struct av7110_p2t p2t_filter[MAXFILT]; - struct dvb_filter_pes2ts p2t[2]; - struct ipack ipack[2]; - u8 *kbuf[2]; - - int sinfo; - int feeding; - - int arm_errors; - int registered; - - - /* AV711X */ - - u32 arm_fw; - u32 arm_rtsl; - u32 arm_vid; - u32 arm_app; - u32 avtype; - int arm_ready; - struct task_struct *arm_thread; - wait_queue_head_t arm_wait; - u16 arm_loops; - - void *debi_virt; - dma_addr_t debi_bus; - - u16 pids[DMX_PES_OTHER]; - - struct dvb_ringbuffer ci_rbuffer; - struct dvb_ringbuffer ci_wbuffer; - - struct audio_mixer mixer; - - struct dvb_adapter dvb_adapter; - struct dvb_device *video_dev; - struct dvb_device *audio_dev; - struct dvb_device *ca_dev; - struct dvb_device *osd_dev; - - struct dvb_video_events video_events; - video_size_t video_size; - - u16 wssMode; - u16 wssData; - - struct infrared ir; - - /* firmware stuff */ - unsigned char *bin_fw; - unsigned long size_fw; - - unsigned char *bin_dpram; - unsigned long size_dpram; - - unsigned char *bin_root; - unsigned long size_root; - - struct dvb_frontend* fe; - fe_status_t fe_status; - - /* crash recovery */ - void (*recover)(struct av7110* av7110); - fe_sec_voltage_t saved_voltage; - fe_sec_tone_mode_t saved_tone; - struct dvb_diseqc_master_cmd saved_master_cmd; - fe_sec_mini_cmd_t saved_minicmd; - - int (*fe_init)(struct dvb_frontend* fe); - int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status); - int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe); - int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); - int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); - int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); - int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); - int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); - int (*fe_set_frontend)(struct dvb_frontend *fe); -}; - - -extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid); - -extern int av7110_check_ir_config(struct av7110 *av7110, int force); -extern int av7110_ir_init(struct av7110 *av7110); -extern void av7110_ir_exit(struct av7110 *av7110); - -/* msp3400 i2c subaddresses */ -#define MSP_WR_DEM 0x10 -#define MSP_RD_DEM 0x11 -#define MSP_WR_DSP 0x12 -#define MSP_RD_DSP 0x13 - -extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); -extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); -extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); - - -extern int av7110_init_analog_module(struct av7110 *av7110); -extern int av7110_init_v4l(struct av7110 *av7110); -extern int av7110_exit_v4l(struct av7110 *av7110); - -#endif /* _AV7110_H_ */ diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c deleted file mode 100644 index 952b33dbac4f..000000000000 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ /dev/null @@ -1,1626 +0,0 @@ -/* - * av7110_av.c: audio and video MPEG decoder stuff - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_av.h" -#include "av7110_ipack.h" - -/* MPEG-2 (ISO 13818 / H.222.0) stream types */ -#define PROG_STREAM_MAP 0xBC -#define PRIVATE_STREAM1 0xBD -#define PADDING_STREAM 0xBE -#define PRIVATE_STREAM2 0xBF -#define AUDIO_STREAM_S 0xC0 -#define AUDIO_STREAM_E 0xDF -#define VIDEO_STREAM_S 0xE0 -#define VIDEO_STREAM_E 0xEF -#define ECM_STREAM 0xF0 -#define EMM_STREAM 0xF1 -#define DSM_CC_STREAM 0xF2 -#define ISO13522_STREAM 0xF3 -#define PROG_STREAM_DIR 0xFF - -#define PTS_DTS_FLAGS 0xC0 - -//pts_dts flags -#define PTS_ONLY 0x80 -#define PTS_DTS 0xC0 -#define TS_SIZE 188 -#define TRANS_ERROR 0x80 -#define PAY_START 0x40 -#define TRANS_PRIO 0x20 -#define PID_MASK_HI 0x1F -//flags -#define TRANS_SCRMBL1 0x80 -#define TRANS_SCRMBL2 0x40 -#define ADAPT_FIELD 0x20 -#define PAYLOAD 0x10 -#define COUNT_MASK 0x0F - -// adaptation flags -#define DISCON_IND 0x80 -#define RAND_ACC_IND 0x40 -#define ES_PRI_IND 0x20 -#define PCR_FLAG 0x10 -#define OPCR_FLAG 0x08 -#define SPLICE_FLAG 0x04 -#define TRANS_PRIV 0x02 -#define ADAP_EXT_FLAG 0x01 - -// adaptation extension flags -#define LTW_FLAG 0x80 -#define PIECE_RATE 0x40 -#define SEAM_SPLICE 0x20 - - -static void p_to_t(u8 const *buf, long int length, u16 pid, - u8 *counter, struct dvb_demux_feed *feed); -static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); - - -int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv; - - if (!(dvbdmxfeed->ts_type & TS_PACKET)) - return 0; - if (buf[3] == 0xe0) // video PES do not have a length in TS - buf[4] = buf[5] = 0; - if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) - return dvbdmxfeed->cb.ts(buf, len, NULL, 0, - &dvbdmxfeed->feed.ts, DMX_OK); - else - return dvb_filter_pes2ts(p2t, buf, len, 1); -} - -static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; - - dvbdmxfeed->cb.ts(data, 188, NULL, 0, - &dvbdmxfeed->feed.ts, DMX_OK); - return 0; -} - -int av7110_av_start_record(struct av7110 *av7110, int av, - struct dvb_demux_feed *dvbdmxfeed) -{ - int ret = 0; - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - - dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed); - - if (av7110->playing || (av7110->rec_mode & av)) - return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - dvbdmx->recording = 1; - av7110->rec_mode |= av; - - switch (av7110->rec_mode) { - case RP_AUDIO: - dvb_filter_pes2ts_init(&av7110->p2t[0], - dvbdmx->pesfilter[0]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[0]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); - break; - - case RP_VIDEO: - dvb_filter_pes2ts_init(&av7110->p2t[1], - dvbdmx->pesfilter[1]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); - break; - - case RP_AV: - dvb_filter_pes2ts_init(&av7110->p2t[0], - dvbdmx->pesfilter[0]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[0]); - dvb_filter_pes2ts_init(&av7110->p2t[1], - dvbdmx->pesfilter[1]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); - break; - } - return ret; -} - -int av7110_av_start_play(struct av7110 *av7110, int av) -{ - int ret = 0; - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->rec_mode) - return -EBUSY; - if (av7110->playing & av) - return -EBUSY; - - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - - if (av7110->playing == RP_NONE) { - av7110_ipack_reset(&av7110->ipack[0]); - av7110_ipack_reset(&av7110->ipack[1]); - } - - av7110->playing |= av; - switch (av7110->playing) { - case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); - break; - case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); - av7110->sinfo = 0; - break; - case RP_AV: - av7110->sinfo = 0; - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); - break; - } - return ret; -} - -int av7110_av_stop(struct av7110 *av7110, int av) -{ - int ret = 0; - dprintk(2, "av7110:%p, \n", av7110); - - if (!(av7110->playing & av) && !(av7110->rec_mode & av)) - return 0; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - if (av7110->playing) { - av7110->playing &= ~av; - switch (av7110->playing) { - case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); - break; - case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); - break; - case RP_NONE: - ret = av7110_set_vidmode(av7110, av7110->vidmode); - break; - } - } else { - av7110->rec_mode &= ~av; - switch (av7110->rec_mode) { - case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); - break; - case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); - break; - case RP_NONE: - break; - } - } - return ret; -} - - -int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) -{ - int len; - u32 sync; - u16 blen; - - if (!dlen) { - wake_up(&buf->queue); - return -1; - } - while (1) { - len = dvb_ringbuffer_avail(buf); - if (len < 6) { - wake_up(&buf->queue); - return -1; - } - sync = DVB_RINGBUFFER_PEEK(buf, 0) << 24; - sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16; - sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8; - sync |= DVB_RINGBUFFER_PEEK(buf, 3); - - if (((sync &~ 0x0f) == 0x000001e0) || - ((sync &~ 0x1f) == 0x000001c0) || - (sync == 0x000001bd)) - break; - printk("resync\n"); - DVB_RINGBUFFER_SKIP(buf, 1); - } - blen = DVB_RINGBUFFER_PEEK(buf, 4) << 8; - blen |= DVB_RINGBUFFER_PEEK(buf, 5); - blen += 6; - if (len < blen || blen > dlen) { - //printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); - wake_up(&buf->queue); - return -1; - } - - dvb_ringbuffer_read(buf, dest, (size_t) blen); - - dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", - (unsigned long) buf->pread, (unsigned long) buf->pwrite); - wake_up(&buf->queue); - return blen; -} - - -int av7110_set_volume(struct av7110 *av7110, int volleft, int volright) -{ - int err, vol, val, balance = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - av7110->mixer.volume_left = volleft; - av7110->mixer.volume_right = volright; - - switch (av7110->adac_type) { - case DVB_ADAC_TI: - volleft = (volleft * 256) / 1036; - volright = (volright * 256) / 1036; - if (volleft > 0x3f) - volleft = 0x3f; - if (volright > 0x3f) - volright = 0x3f; - if ((err = SendDAC(av7110, 3, 0x80 + volleft))) - return err; - return SendDAC(av7110, 4, volright); - - case DVB_ADAC_CRYSTAL: - volleft = 127 - volleft / 2; - volright = 127 - volright / 2; - i2c_writereg(av7110, 0x20, 0x03, volleft); - i2c_writereg(av7110, 0x20, 0x04, volright); - return 0; - - case DVB_ADAC_MSP34x0: - vol = (volleft > volright) ? volleft : volright; - val = (vol * 0x73 / 255) << 8; - if (vol > 0) - balance = ((volright - volleft) * 127) / vol; - msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ - msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ - return 0; - - case DVB_ADAC_MSP34x5: - vol = (volleft > volright) ? volleft : volright; - val = (vol * 0x73 / 255) << 8; - if (vol > 0) - balance = ((volright - volleft) * 127) / vol; - msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ - return 0; - } - - return 0; -} - -int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) -{ - int ret; - dprintk(2, "av7110:%p, \n", av7110); - - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); - - if (!ret && !av7110->playing) { - ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], - av7110->pids[DMX_PES_AUDIO], - av7110->pids[DMX_PES_TELETEXT], - 0, av7110->pids[DMX_PES_PCR]); - if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); - } - return ret; -} - - -static enum av7110_video_mode sw2mode[16] = { - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, - AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, -}; - -static int get_video_format(struct av7110 *av7110, u8 *buf, int count) -{ - int i; - int hsize, vsize; - int sw; - u8 *p; - int ret = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->sinfo) - return 0; - for (i = 7; i < count - 10; i++) { - p = buf + i; - if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3) - continue; - p += 4; - hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); - vsize = ((p[1] &0x0F) << 8) | (p[2]); - sw = (p[3] & 0x0F); - ret = av7110_set_vidmode(av7110, sw2mode[sw]); - if (!ret) { - dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw); - av7110->sinfo = 1; - } - break; - } - return ret; -} - - -/**************************************************************************** - * I/O buffer management and control - ****************************************************************************/ - -static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, - const u8 *buf, unsigned long count) -{ - unsigned long todo = count; - int free; - - while (todo > 0) { - if (dvb_ringbuffer_free(rbuf) < 2048) { - if (wait_event_interruptible(rbuf->queue, - (dvb_ringbuffer_free(rbuf) >= 2048))) - return count - todo; - } - free = dvb_ringbuffer_free(rbuf); - if (free > todo) - free = todo; - dvb_ringbuffer_write(rbuf, buf, free); - todo -= free; - buf += free; - } - - return count - todo; -} - -static void play_video_cb(u8 *buf, int count, void *priv) -{ - struct av7110 *av7110 = (struct av7110 *) priv; - dprintk(2, "av7110:%p, \n", av7110); - - if ((buf[3] & 0xe0) == 0xe0) { - get_video_format(av7110, buf, count); - aux_ring_buffer_write(&av7110->avout, buf, count); - } else - aux_ring_buffer_write(&av7110->aout, buf, count); -} - -static void play_audio_cb(u8 *buf, int count, void *priv) -{ - struct av7110 *av7110 = (struct av7110 *) priv; - dprintk(2, "av7110:%p, \n", av7110); - - aux_ring_buffer_write(&av7110->aout, buf, count); -} - - -#define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096) - -static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, - unsigned long count, int nonblock, int type) -{ - struct dvb_ringbuffer *rb; - u8 *kb; - unsigned long todo = count; - - dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count); - - rb = (type) ? &av7110->avout : &av7110->aout; - kb = av7110->kbuf[type]; - - if (!kb) - return -ENOBUFS; - - if (nonblock && !FREE_COND_TS) - return -EWOULDBLOCK; - - while (todo >= TS_SIZE) { - if (!FREE_COND_TS) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(rb->queue, FREE_COND_TS)) - return count - todo; - } - if (copy_from_user(kb, buf, TS_SIZE)) - return -EFAULT; - write_ts_to_decoder(av7110, type, kb, TS_SIZE); - todo -= TS_SIZE; - buf += TS_SIZE; - } - - return count - todo; -} - - -#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ - dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) - -static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, - unsigned long count, int nonblock, int type) -{ - unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); - - if (!av7110->kbuf[type]) - return -ENOBUFS; - - if (nonblock && !FREE_COND) - return -EWOULDBLOCK; - - while (todo > 0) { - if (!FREE_COND) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(av7110->avout.queue, - FREE_COND)) - return count - todo; - } - n = todo; - if (n > IPACKS * 2) - n = IPACKS * 2; - if (copy_from_user(av7110->kbuf[type], buf, n)) - return -EFAULT; - av7110_ipack_instant_repack(av7110->kbuf[type], n, - &av7110->ipack[type]); - todo -= n; - buf += n; - } - return count - todo; -} - -static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, - unsigned long count, int nonblock, int type) -{ - unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); - - if (!av7110->kbuf[type]) - return -ENOBUFS; - - if (nonblock && !FREE_COND) - return -EWOULDBLOCK; - - while (todo > 0) { - if (!FREE_COND) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(av7110->avout.queue, - FREE_COND)) - return count - todo; - } - n = todo; - if (n > IPACKS * 2) - n = IPACKS * 2; - av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]); - todo -= n; - buf += n; - } - return count - todo; -} - -static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, - unsigned long count, int nonblock, int type) -{ - unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); - - if (!av7110->kbuf[type]) - return -ENOBUFS; - if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) - return -EWOULDBLOCK; - - while (todo > 0) { - if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(av7110->aout.queue, - (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) - return count-todo; - } - n = todo; - if (n > IPACKS * 2) - n = IPACKS * 2; - if (copy_from_user(av7110->kbuf[type], buf, n)) - return -EFAULT; - av7110_ipack_instant_repack(av7110->kbuf[type], n, - &av7110->ipack[type]); - todo -= n; - buf += n; - } - return count - todo; -} - -void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed) -{ - memset(p->pes, 0, TS_SIZE); - p->counter = 0; - p->pos = 0; - p->frags = 0; - if (feed) - p->feed = feed; -} - -static void clear_p2t(struct av7110_p2t *p) -{ - memset(p->pes, 0, TS_SIZE); -// p->counter = 0; - p->pos = 0; - p->frags = 0; -} - - -static int find_pes_header(u8 const *buf, long int length, int *frags) -{ - int c = 0; - int found = 0; - - *frags = 0; - - while (c < length - 3 && !found) { - if (buf[c] == 0x00 && buf[c + 1] == 0x00 && - buf[c + 2] == 0x01) { - switch ( buf[c + 3] ) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM : - case EMM_STREAM : - case PADDING_STREAM : - case DSM_CC_STREAM : - case ISO13522_STREAM: - case PRIVATE_STREAM1: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - found = 1; - break; - - default: - c++; - break; - } - } else - c++; - } - if (c == length - 3 && !found) { - if (buf[length - 1] == 0x00) - *frags = 1; - if (buf[length - 2] == 0x00 && - buf[length - 1] == 0x00) - *frags = 2; - if (buf[length - 3] == 0x00 && - buf[length - 2] == 0x00 && - buf[length - 1] == 0x01) - *frags = 3; - return -1; - } - - return c; -} - -void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) -{ - int c, c2, l, add; - int check, rest; - - c = 0; - c2 = 0; - if (p->frags){ - check = 0; - switch(p->frags) { - case 1: - if (buf[c] == 0x00 && buf[c + 1] == 0x01) { - check = 1; - c += 2; - } - break; - case 2: - if (buf[c] == 0x01) { - check = 1; - c++; - } - break; - case 3: - check = 1; - } - if (check) { - switch (buf[c]) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM : - case EMM_STREAM : - case PADDING_STREAM : - case DSM_CC_STREAM : - case ISO13522_STREAM: - case PRIVATE_STREAM1: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - p->pes[0] = 0x00; - p->pes[1] = 0x00; - p->pes[2] = 0x01; - p->pes[3] = buf[c]; - p->pos = 4; - memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos); - c += (TS_SIZE - 4) - p->pos; - p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed); - clear_p2t(p); - break; - - default: - c = 0; - break; - } - } - p->frags = 0; - } - - if (p->pos) { - c2 = find_pes_header(buf + c, length - c, &p->frags); - if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos) - l = c2+c; - else - l = (TS_SIZE - 4) - p->pos; - memcpy(p->pes + p->pos, buf, l); - c += l; - p->pos += l; - p_to_t(p->pes, p->pos, pid, &p->counter, p->feed); - clear_p2t(p); - } - - add = 0; - while (c < length) { - c2 = find_pes_header(buf + c + add, length - c - add, &p->frags); - if (c2 >= 0) { - c2 += c + add; - if (c2 > c){ - p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed); - c = c2; - clear_p2t(p); - add = 0; - } else - add = 1; - } else { - l = length - c; - rest = l % (TS_SIZE - 4); - l -= rest; - p_to_t(buf + c, l, pid, &p->counter, p->feed); - memcpy(p->pes, buf + c + l, rest); - p->pos = rest; - c = length; - } - } -} - - -static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) -{ - int i; - int c = 0; - int fill; - u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 }; - - fill = (TS_SIZE - 4) - length; - if (pes_start) - tshead[1] = 0x40; - if (fill) - tshead[3] = 0x30; - tshead[1] |= (u8)((pid & 0x1F00) >> 8); - tshead[2] |= (u8)(pid & 0x00FF); - tshead[3] |= ((*counter)++ & 0x0F); - memcpy(buf, tshead, 4); - c += 4; - - if (fill) { - buf[4] = fill - 1; - c++; - if (fill > 1) { - buf[5] = 0x00; - c++; - } - for (i = 6; i < fill + 4; i++) { - buf[i] = 0xFF; - c++; - } - } - - return c; -} - - -static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, - struct dvb_demux_feed *feed) -{ - int l, pes_start; - u8 obuf[TS_SIZE]; - long c = 0; - - pes_start = 0; - if (length > 3 && - buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) - switch (buf[3]) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM : - case EMM_STREAM : - case PADDING_STREAM : - case DSM_CC_STREAM : - case ISO13522_STREAM: - case PRIVATE_STREAM1: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - pes_start = 1; - break; - - default: - break; - } - - while (c < length) { - memset(obuf, 0, TS_SIZE); - if (length - c >= (TS_SIZE - 4)){ - l = write_ts_header2(pid, counter, pes_start, - obuf, (TS_SIZE - 4)); - memcpy(obuf + l, buf + c, TS_SIZE - l); - c += TS_SIZE - l; - } else { - l = write_ts_header2(pid, counter, pes_start, - obuf, length - c); - memcpy(obuf + l, buf + c, TS_SIZE - l); - c = length; - } - feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, DMX_OK); - pes_start = 0; - } -} - - -static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len) -{ - struct ipack *ipack = &av7110->ipack[type]; - - if (buf[1] & TRANS_ERROR) { - av7110_ipack_reset(ipack); - return -1; - } - - if (!(buf[3] & PAYLOAD)) - return -1; - - if (buf[1] & PAY_START) - av7110_ipack_flush(ipack); - - if (buf[3] & ADAPT_FIELD) { - len -= buf[4] + 1; - buf += buf[4] + 1; - if (!len) - return 0; - } - - av7110_ipack_instant_repack(buf + 4, len - 4, ipack); - return 0; -} - - -int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = (struct av7110 *) demux->priv; - - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) - return 0; - - switch (feed->pes_type) { - case 0: - if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) - return -EINVAL; - break; - case 1: - if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) - return -EINVAL; - break; - default: - return -1; - } - - return write_ts_to_decoder(av7110, feed->pes_type, buf, len); -} - - - -/****************************************************************************** - * Video MPEG decoder events - ******************************************************************************/ -void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) -{ - struct dvb_video_events *events = &av7110->video_events; - int wp; - - spin_lock_bh(&events->lock); - - wp = (events->eventw + 1) % MAX_VIDEO_EVENT; - if (wp == events->eventr) { - events->overflow = 1; - events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; - } - - //FIXME: timestamp? - memcpy(&events->events[events->eventw], event, sizeof(struct video_event)); - events->eventw = wp; - - spin_unlock_bh(&events->lock); - - wake_up_interruptible(&events->wait_queue); -} - - -static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) -{ - struct dvb_video_events *events = &av7110->video_events; - - if (events->overflow) { - events->overflow = 0; - return -EOVERFLOW; - } - if (events->eventw == events->eventr) { - int ret; - - if (flags & O_NONBLOCK) - return -EWOULDBLOCK; - - ret = wait_event_interruptible(events->wait_queue, - events->eventw != events->eventr); - if (ret < 0) - return ret; - } - - spin_lock_bh(&events->lock); - - memcpy(event, &events->events[events->eventr], - sizeof(struct video_event)); - events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; - - spin_unlock_bh(&events->lock); - - return 0; -} - - -/****************************************************************************** - * DVB device file operations - ******************************************************************************/ - -static unsigned int dvb_video_poll(struct file *file, poll_table *wait) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned int mask = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) - poll_wait(file, &av7110->avout.queue, wait); - - poll_wait(file, &av7110->video_events.wait_queue, wait); - - if (av7110->video_events.eventw != av7110->video_events.eventr) - mask = POLLPRI; - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - if (av7110->playing) { - if (FREE_COND) - mask |= (POLLOUT | POLLWRNORM); - } else /* if not playing: may play if asked for */ - mask |= (POLLOUT | POLLWRNORM); - } - - return mask; -} - -static ssize_t dvb_video_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned char c; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - return -EPERM; - - if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY) - return -EPERM; - - if (get_user(c, buf)) - return -EFAULT; - if (c == 0x47 && count % TS_SIZE == 0) - return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); - else - return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); -} - -static unsigned int dvb_audio_poll(struct file *file, poll_table *wait) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned int mask = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - poll_wait(file, &av7110->aout.queue, wait); - - if (av7110->playing) { - if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) - mask |= (POLLOUT | POLLWRNORM); - } else /* if not playing: may play if asked for */ - mask = (POLLOUT | POLLWRNORM); - - return mask; -} - -static ssize_t dvb_audio_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned char c; - - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) { - printk(KERN_ERR "not audio source memory\n"); - return -EPERM; - } - - if (get_user(c, buf)) - return -EFAULT; - if (c == 0x47 && count % TS_SIZE == 0) - return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); - else - return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); -} - -static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; - -#define MIN_IFRAME 400000 - -static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) -{ - unsigned i, n; - int progressive = 0; - int match = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - if (!(av7110->playing & RP_VIDEO)) { - if (av7110_av_start_play(av7110, RP_VIDEO) < 0) - return -EBUSY; - } - - /* search in buf for instances of 00 00 01 b5 1? */ - for (i = 0; i < len; i++) { - unsigned char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (match == 5) { - progressive = c & 0x08; - match = 0; - } - if (c == 0x00) { - match = (match == 1 || match == 2) ? 2 : 1; - continue; - } - switch (match++) { - case 2: if (c == 0x01) - continue; - break; - case 3: if (c == 0xb5) - continue; - break; - case 4: if ((c & 0xf0) == 0x10) - continue; - break; - } - match = 0; - } - - /* setting n always > 1, fixes problems when playing stillframes - consisting of I- and P-Frames */ - n = MIN_IFRAME / len + 1; - - /* FIXME: nonblock? */ - dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1); - - for (i = 0; i < n; i++) - dvb_play(av7110, buf, len, 0, 1); - - av7110_ipack_flush(&av7110->ipack[1]); - - if (progressive) - return vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); - else - return 0; -} - - -static int dvb_video_ioctl(struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; - int ret = 0; - - dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); - - if ((file->f_flags & O_ACCMODE) == O_RDONLY) { - if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && - cmd != VIDEO_GET_SIZE ) { - return -EPERM; - } - } - - switch (cmd) { - case VIDEO_STOP: - av7110->videostate.play_state = VIDEO_STOPPED; - if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) - ret = av7110_av_stop(av7110, RP_VIDEO); - else - ret = vidcom(av7110, AV_VIDEO_CMD_STOP, - av7110->videostate.video_blank ? 0 : 1); - if (!ret) - av7110->trickmode = TRICK_NONE; - break; - - case VIDEO_PLAY: - av7110->trickmode = TRICK_NONE; - if (av7110->videostate.play_state == VIDEO_FREEZED) { - av7110->videostate.play_state = VIDEO_PLAYING; - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (ret) - break; - } - if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { - if (av7110->playing == RP_AV) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - if (ret) - break; - av7110->playing &= ~RP_VIDEO; - } - ret = av7110_av_start_play(av7110, RP_VIDEO); - } - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (!ret) - av7110->videostate.play_state = VIDEO_PLAYING; - break; - - case VIDEO_FREEZE: - av7110->videostate.play_state = VIDEO_FREEZED; - if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); - else - ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); - if (!ret) - av7110->trickmode = TRICK_FREEZE; - break; - - case VIDEO_CONTINUE: - if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (!ret) { - av7110->videostate.play_state = VIDEO_PLAYING; - av7110->trickmode = TRICK_NONE; - } - break; - - case VIDEO_SELECT_SOURCE: - av7110->videostate.stream_source = (video_stream_source_t) arg; - break; - - case VIDEO_SET_BLANK: - av7110->videostate.video_blank = (int) arg; - break; - - case VIDEO_GET_STATUS: - memcpy(parg, &av7110->videostate, sizeof(struct video_status)); - break; - - case VIDEO_GET_EVENT: - ret = dvb_video_get_event(av7110, parg, file->f_flags); - break; - - case VIDEO_GET_SIZE: - memcpy(parg, &av7110->video_size, sizeof(video_size_t)); - break; - - case VIDEO_SET_DISPLAY_FORMAT: - { - video_displayformat_t format = (video_displayformat_t) arg; - switch (format) { - case VIDEO_PAN_SCAN: - av7110->display_panscan = VID_PAN_SCAN_PREF; - break; - case VIDEO_LETTER_BOX: - av7110->display_panscan = VID_VC_AND_PS_PREF; - break; - case VIDEO_CENTER_CUT_OUT: - av7110->display_panscan = VID_CENTRE_CUT_PREF; - break; - default: - ret = -EINVAL; - } - if (ret < 0) - break; - av7110->videostate.display_format = format; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); - break; - } - - case VIDEO_SET_FORMAT: - if (arg > 1) { - ret = -EINVAL; - break; - } - av7110->display_ar = arg; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16) arg); - break; - - case VIDEO_STILLPICTURE: - { - struct video_still_picture *pic = - (struct video_still_picture *) parg; - av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - ret = play_iframe(av7110, pic->iFrame, pic->size, - file->f_flags & O_NONBLOCK); - break; - } - - case VIDEO_FAST_FORWARD: - //note: arg is ignored by firmware - if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); - else - ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); - if (!ret) { - av7110->trickmode = TRICK_FAST; - av7110->videostate.play_state = VIDEO_PLAYING; - } - break; - - case VIDEO_SLOWMOTION: - if (av7110->playing&RP_VIDEO) { - if (av7110->trickmode != TRICK_SLOW) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); - } else { - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); - } - if (!ret) { - av7110->trickmode = TRICK_SLOW; - av7110->videostate.play_state = VIDEO_PLAYING; - } - break; - - case VIDEO_GET_CAPABILITIES: - *(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 | - VIDEO_CAP_SYS | VIDEO_CAP_PROG; - break; - - case VIDEO_CLEAR_BUFFER: - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - av7110_ipack_reset(&av7110->ipack[1]); - if (av7110->playing == RP_AV) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); - if (ret) - break; - if (av7110->trickmode == TRICK_FAST) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); - if (av7110->trickmode == TRICK_SLOW) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Slow, 2, 0, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); - } - if (av7110->trickmode == TRICK_FREEZE) - ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); - } - break; - - case VIDEO_SET_STREAMTYPE: - break; - - default: - ret = -ENOIOCTLCMD; - break; - } - - return ret; -} - -static int dvb_audio_ioctl(struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; - int ret = 0; - - dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); - - if (((file->f_flags & O_ACCMODE) == O_RDONLY) && - (cmd != AUDIO_GET_STATUS)) - return -EPERM; - - switch (cmd) { - case AUDIO_STOP: - if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) - ret = av7110_av_stop(av7110, RP_AUDIO); - else - ret = audcom(av7110, AUDIO_CMD_MUTE); - if (!ret) - av7110->audiostate.play_state = AUDIO_STOPPED; - break; - - case AUDIO_PLAY: - if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) - ret = av7110_av_start_play(av7110, RP_AUDIO); - if (!ret) - ret = audcom(av7110, AUDIO_CMD_UNMUTE); - if (!ret) - av7110->audiostate.play_state = AUDIO_PLAYING; - break; - - case AUDIO_PAUSE: - ret = audcom(av7110, AUDIO_CMD_MUTE); - if (!ret) - av7110->audiostate.play_state = AUDIO_PAUSED; - break; - - case AUDIO_CONTINUE: - if (av7110->audiostate.play_state == AUDIO_PAUSED) { - av7110->audiostate.play_state = AUDIO_PLAYING; - ret = audcom(av7110, AUDIO_CMD_UNMUTE | AUDIO_CMD_PCM16); - } - break; - - case AUDIO_SELECT_SOURCE: - av7110->audiostate.stream_source = (audio_stream_source_t) arg; - break; - - case AUDIO_SET_MUTE: - { - ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE); - if (!ret) - av7110->audiostate.mute_state = (int) arg; - break; - } - - case AUDIO_SET_AV_SYNC: - av7110->audiostate.AV_sync_state = (int) arg; - ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF); - break; - - case AUDIO_SET_BYPASS_MODE: - if (FW_VERSION(av7110->arm_app) < 0x2621) - ret = -EINVAL; - av7110->audiostate.bypass_mode = (int)arg; - break; - - case AUDIO_CHANNEL_SELECT: - av7110->audiostate.channel_select = (audio_channel_select_t) arg; - switch(av7110->audiostate.channel_select) { - case AUDIO_STEREO: - ret = audcom(av7110, AUDIO_CMD_STEREO); - if (!ret) { - if (av7110->adac_type == DVB_ADAC_CRYSTAL) - i2c_writereg(av7110, 0x20, 0x02, 0x49); - else if (av7110->adac_type == DVB_ADAC_MSP34x5) - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); - } - break; - case AUDIO_MONO_LEFT: - ret = audcom(av7110, AUDIO_CMD_MONO_L); - if (!ret) { - if (av7110->adac_type == DVB_ADAC_CRYSTAL) - i2c_writereg(av7110, 0x20, 0x02, 0x4a); - else if (av7110->adac_type == DVB_ADAC_MSP34x5) - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200); - } - break; - case AUDIO_MONO_RIGHT: - ret = audcom(av7110, AUDIO_CMD_MONO_R); - if (!ret) { - if (av7110->adac_type == DVB_ADAC_CRYSTAL) - i2c_writereg(av7110, 0x20, 0x02, 0x45); - else if (av7110->adac_type == DVB_ADAC_MSP34x5) - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210); - } - break; - default: - ret = -EINVAL; - break; - } - break; - - case AUDIO_GET_STATUS: - memcpy(parg, &av7110->audiostate, sizeof(struct audio_status)); - break; - - case AUDIO_GET_CAPABILITIES: - if (FW_VERSION(av7110->arm_app) < 0x2621) - *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2; - else - *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_DTS | AUDIO_CAP_AC3 | - AUDIO_CAP_MP1 | AUDIO_CAP_MP2; - break; - - case AUDIO_CLEAR_BUFFER: - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - av7110_ipack_reset(&av7110->ipack[0]); - if (av7110->playing == RP_AV) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); - break; - - case AUDIO_SET_ID: - break; - - case AUDIO_SET_MIXER: - { - struct audio_mixer *amix = (struct audio_mixer *)parg; - ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right); - break; - } - - case AUDIO_SET_STREAMTYPE: - break; - - default: - ret = -ENOIOCTLCMD; - } - - return ret; -} - - -static int dvb_video_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - int err; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((err = dvb_generic_open(inode, file)) < 0) - return err; - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - av7110->video_blank = 1; - av7110->audiostate.AV_sync_state = 1; - av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; - - /* empty event queue */ - av7110->video_events.eventr = av7110->video_events.eventw = 0; - } - - return 0; -} - -static int dvb_video_release(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - av7110_av_stop(av7110, RP_VIDEO); - } - - return dvb_generic_release(inode, file); -} - -static int dvb_audio_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - int err = dvb_generic_open(inode, file); - - dprintk(2, "av7110:%p, \n", av7110); - - if (err < 0) - return err; - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; - return 0; -} - -static int dvb_audio_release(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(2, "av7110:%p, \n", av7110); - - av7110_av_stop(av7110, RP_AUDIO); - return dvb_generic_release(inode, file); -} - - - -/****************************************************************************** - * driver registration - ******************************************************************************/ - -static const struct file_operations dvb_video_fops = { - .owner = THIS_MODULE, - .write = dvb_video_write, - .unlocked_ioctl = dvb_generic_ioctl, - .open = dvb_video_open, - .release = dvb_video_release, - .poll = dvb_video_poll, - .llseek = noop_llseek, -}; - -static struct dvb_device dvbdev_video = { - .priv = NULL, - .users = 6, - .readers = 5, /* arbitrary */ - .writers = 1, - .fops = &dvb_video_fops, - .kernel_ioctl = dvb_video_ioctl, -}; - -static const struct file_operations dvb_audio_fops = { - .owner = THIS_MODULE, - .write = dvb_audio_write, - .unlocked_ioctl = dvb_generic_ioctl, - .open = dvb_audio_open, - .release = dvb_audio_release, - .poll = dvb_audio_poll, - .llseek = noop_llseek, -}; - -static struct dvb_device dvbdev_audio = { - .priv = NULL, - .users = 1, - .writers = 1, - .fops = &dvb_audio_fops, - .kernel_ioctl = dvb_audio_ioctl, -}; - - -int av7110_av_register(struct av7110 *av7110) -{ - av7110->audiostate.AV_sync_state = 0; - av7110->audiostate.mute_state = 0; - av7110->audiostate.play_state = AUDIO_STOPPED; - av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; - av7110->audiostate.channel_select = AUDIO_STEREO; - av7110->audiostate.bypass_mode = 0; - - av7110->videostate.video_blank = 0; - av7110->videostate.play_state = VIDEO_STOPPED; - av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; - av7110->videostate.video_format = VIDEO_FORMAT_4_3; - av7110->videostate.display_format = VIDEO_LETTER_BOX; - av7110->display_ar = VIDEO_FORMAT_4_3; - av7110->display_panscan = VID_VC_AND_PS_PREF; - - init_waitqueue_head(&av7110->video_events.wait_queue); - spin_lock_init(&av7110->video_events.lock); - av7110->video_events.eventw = av7110->video_events.eventr = 0; - av7110->video_events.overflow = 0; - memset(&av7110->video_size, 0, sizeof (video_size_t)); - - dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, - &dvbdev_video, av7110, DVB_DEVICE_VIDEO); - - dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, - &dvbdev_audio, av7110, DVB_DEVICE_AUDIO); - - return 0; -} - -void av7110_av_unregister(struct av7110 *av7110) -{ - dvb_unregister_device(av7110->audio_dev); - dvb_unregister_device(av7110->video_dev); -} - -int av7110_av_init(struct av7110 *av7110) -{ - void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; - int i, ret; - - for (i = 0; i < 2; i++) { - struct ipack *ipack = av7110->ipack + i; - - ret = av7110_ipack_init(ipack, IPACKS, play[i]); - if (ret < 0) { - if (i) - av7110_ipack_free(--ipack); - goto out; - } - ipack->data = av7110; - } - - dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN); - dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN); - - av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN); - av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS; -out: - return ret; -} - -void av7110_av_exit(struct av7110 *av7110) -{ - av7110_ipack_free(&av7110->ipack[0]); - av7110_ipack_free(&av7110->ipack[1]); -} diff --git a/drivers/media/dvb/ttpci/av7110_av.h b/drivers/media/dvb/ttpci/av7110_av.h deleted file mode 100644 index 5f02ef85e47d..000000000000 --- a/drivers/media/dvb/ttpci/av7110_av.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _AV7110_AV_H_ -#define _AV7110_AV_H_ - -struct av7110; - -extern int av7110_set_vidmode(struct av7110 *av7110, - enum av7110_video_mode mode); - -extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); -extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); -extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); - -extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright); -extern int av7110_av_stop(struct av7110 *av7110, int av); -extern int av7110_av_start_record(struct av7110 *av7110, int av, - struct dvb_demux_feed *dvbdmxfeed); -extern int av7110_av_start_play(struct av7110 *av7110, int av); - -extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); - -extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); -extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p); - -extern int av7110_av_register(struct av7110 *av7110); -extern void av7110_av_unregister(struct av7110 *av7110); -extern int av7110_av_init(struct av7110 *av7110); -extern void av7110_av_exit(struct av7110 *av7110); - - -#endif /* _AV7110_AV_H_ */ diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c deleted file mode 100644 index 9fc1dd0ba4c3..000000000000 --- a/drivers/media/dvb/ttpci/av7110_ca.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - * av7110_ca.c: CA and CI stuff - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_ca.h" - - -void CI_handle(struct av7110 *av7110, u8 *data, u16 len) -{ - dprintk(8, "av7110:%p\n",av7110); - - if (len < 3) - return; - switch (data[0]) { - case CI_MSG_CI_INFO: - if (data[2] != 1 && data[2] != 2) - break; - switch (data[1]) { - case 0: - av7110->ci_slot[data[2] - 1].flags = 0; - break; - case 1: - av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT; - break; - case 2: - av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY; - break; - } - break; - case CI_SWITCH_PRG_REPLY: - //av7110->ci_stat=data[1]; - break; - default: - break; - } -} - - -void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) -{ - if (dvb_ringbuffer_free(cibuf) < len + 2) - return; - - DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8); - DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff); - dvb_ringbuffer_write(cibuf, data, len); - wake_up_interruptible(&cibuf->queue); -} - - -/****************************************************************************** - * CI link layer file ops - ******************************************************************************/ - -static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) -{ - struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p; - void *data; - - for (p = tab; *p; p++) { - data = vmalloc(size); - if (!data) { - while (p-- != tab) { - vfree(p[0]->data); - p[0]->data = NULL; - } - return -ENOMEM; - } - dvb_ringbuffer_init(*p, data, size); - } - return 0; -} - -static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) -{ - dvb_ringbuffer_flush_spinlock_wakeup(cirbuf); - dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf); -} - -static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) -{ - vfree(cirbuf->data); - cirbuf->data = NULL; - vfree(ciwbuf->data); - ciwbuf->data = NULL; -} - -static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, - int slots, ca_slot_info_t *slot) -{ - int i; - int len = 0; - u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 }; - - for (i = 0; i < 2; i++) { - if (slots & (1 << i)) - len += 8; - } - - if (dvb_ringbuffer_free(cibuf) < len) - return -EBUSY; - - for (i = 0; i < 2; i++) { - if (slots & (1 << i)) { - msg[2] = i; - dvb_ringbuffer_write(cibuf, msg, 8); - slot[i].flags = 0; - } - } - - return 0; -} - -static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - int free; - int non_blocking = file->f_flags & O_NONBLOCK; - u8 *page = (u8 *)__get_free_page(GFP_USER); - int res; - - if (!page) - return -ENOMEM; - - res = -EINVAL; - if (count > 2048) - goto out; - - res = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out; - - free = dvb_ringbuffer_free(cibuf); - if (count + 2 > free) { - res = -EWOULDBLOCK; - if (non_blocking) - goto out; - res = -ERESTARTSYS; - if (wait_event_interruptible(cibuf->queue, - (dvb_ringbuffer_free(cibuf) >= count + 2))) - goto out; - } - - DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8); - DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff); - - res = dvb_ringbuffer_write(cibuf, page, count); -out: - free_page((unsigned long)page); - return res; -} - -static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, - char __user *buf, size_t count, loff_t *ppos) -{ - int avail; - int non_blocking = file->f_flags & O_NONBLOCK; - ssize_t len; - - if (!cibuf->data || !count) - return 0; - if (non_blocking && (dvb_ringbuffer_empty(cibuf))) - return -EWOULDBLOCK; - if (wait_event_interruptible(cibuf->queue, - !dvb_ringbuffer_empty(cibuf))) - return -ERESTARTSYS; - avail = dvb_ringbuffer_avail(cibuf); - if (avail < 4) - return 0; - len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; - len |= DVB_RINGBUFFER_PEEK(cibuf, 1); - if (avail < len + 2 || count < len) - return -EINVAL; - DVB_RINGBUFFER_SKIP(cibuf, 2); - - return dvb_ringbuffer_read_user(cibuf, buf, len); -} - -static int dvb_ca_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - int err = dvb_generic_open(inode, file); - - dprintk(8, "av7110:%p\n",av7110); - - if (err < 0) - return err; - ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer); - return 0; -} - -static unsigned int dvb_ca_poll (struct file *file, poll_table *wait) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; - struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; - unsigned int mask = 0; - - dprintk(8, "av7110:%p\n",av7110); - - poll_wait(file, &rbuf->queue, wait); - poll_wait(file, &wbuf->queue, wait); - - if (!dvb_ringbuffer_empty(rbuf)) - mask |= (POLLIN | POLLRDNORM); - - if (dvb_ringbuffer_free(wbuf) > 1024) - mask |= (POLLOUT | POLLWRNORM); - - return mask; -} - -static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; - - dprintk(8, "av7110:%p\n",av7110); - - switch (cmd) { - case CA_RESET: - return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]); - break; - case CA_GET_CAP: - { - ca_caps_t cap; - - cap.slot_num = 2; - cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ? - CA_CI_LINK : CA_CI) | CA_DESCR; - cap.descr_num = 16; - cap.descr_type = CA_ECD; - memcpy(parg, &cap, sizeof(cap)); - break; - } - - case CA_GET_SLOT_INFO: - { - ca_slot_info_t *info=(ca_slot_info_t *)parg; - - if (info->num < 0 || info->num > 1) - return -EINVAL; - av7110->ci_slot[info->num].num = info->num; - av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? - CA_CI_LINK : CA_CI; - memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); - break; - } - - case CA_GET_MSG: - break; - - case CA_SEND_MSG: - break; - - case CA_GET_DESCR_INFO: - { - ca_descr_info_t info; - - info.num = 16; - info.type = CA_ECD; - memcpy(parg, &info, sizeof (info)); - break; - } - - case CA_SET_DESCR: - { - ca_descr_t *descr = (ca_descr_t*) parg; - - if (descr->index >= 16) - return -EINVAL; - if (descr->parity > 1) - return -EINVAL; - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, - (descr->index<<8)|descr->parity, - (descr->cw[0]<<8)|descr->cw[1], - (descr->cw[2]<<8)|descr->cw[3], - (descr->cw[4]<<8)|descr->cw[5], - (descr->cw[6]<<8)|descr->cw[7]); - break; - } - - default: - return -EINVAL; - } - return 0; -} - -static ssize_t dvb_ca_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(8, "av7110:%p\n",av7110); - return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); -} - -static ssize_t dvb_ca_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(8, "av7110:%p\n",av7110); - return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); -} - -static const struct file_operations dvb_ca_fops = { - .owner = THIS_MODULE, - .read = dvb_ca_read, - .write = dvb_ca_write, - .unlocked_ioctl = dvb_generic_ioctl, - .open = dvb_ca_open, - .release = dvb_generic_release, - .poll = dvb_ca_poll, - .llseek = default_llseek, -}; - -static struct dvb_device dvbdev_ca = { - .priv = NULL, - .users = 1, - .writers = 1, - .fops = &dvb_ca_fops, - .kernel_ioctl = dvb_ca_ioctl, -}; - - -int av7110_ca_register(struct av7110 *av7110) -{ - return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, - &dvbdev_ca, av7110, DVB_DEVICE_CA); -} - -void av7110_ca_unregister(struct av7110 *av7110) -{ - dvb_unregister_device(av7110->ca_dev); -} - -int av7110_ca_init(struct av7110* av7110) -{ - return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192); -} - -void av7110_ca_exit(struct av7110* av7110) -{ - ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer); -} diff --git a/drivers/media/dvb/ttpci/av7110_ca.h b/drivers/media/dvb/ttpci/av7110_ca.h deleted file mode 100644 index 70ee855ece1b..000000000000 --- a/drivers/media/dvb/ttpci/av7110_ca.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _AV7110_CA_H_ -#define _AV7110_CA_H_ - -struct av7110; - -extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len); -extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); - -extern int av7110_ca_register(struct av7110 *av7110); -extern void av7110_ca_unregister(struct av7110 *av7110); -extern int av7110_ca_init(struct av7110* av7110); -extern void av7110_ca_exit(struct av7110* av7110); - -#endif /* _AV7110_CA_H_ */ diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c deleted file mode 100644 index f1cbfe526989..000000000000 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ /dev/null @@ -1,1208 +0,0 @@ -/* - * av7110_hw.c: av7110 low level hardware access and firmware interface - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * the project's page is at http://www.linuxtv.org/ - */ - -/* for debugging ARM communication: */ -//#define COM_DEBUG - -#include -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" - -#define _NOHANDSHAKE - -/**************************************************************************** - * DEBI functions - ****************************************************************************/ - -/* This DEBI code is based on the Stradis driver - by Nathan Laredo */ - -int av7110_debiwrite(struct av7110 *av7110, u32 config, - int addr, u32 val, int count) -{ - struct saa7146_dev *dev = av7110->dev; - - if (count <= 0 || count > 32764) { - printk("%s: invalid count %d\n", __func__, count); - return -1; - } - if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done failed\n", __func__); - return -1; - } - saa7146_write(dev, DEBI_CONFIG, config); - if (count <= 4) /* immediate transfer */ - saa7146_write(dev, DEBI_AD, val); - else /* block transfer */ - saa7146_write(dev, DEBI_AD, av7110->debi_bus); - saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff)); - saa7146_write(dev, MC2, (2 << 16) | 2); - return 0; -} - -u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count) -{ - struct saa7146_dev *dev = av7110->dev; - u32 result = 0; - - if (count > 32764 || count <= 0) { - printk("%s: invalid count %d\n", __func__, count); - return 0; - } - if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done #1 failed\n", __func__); - return 0; - } - saa7146_write(dev, DEBI_AD, av7110->debi_bus); - saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); - - saa7146_write(dev, DEBI_CONFIG, config); - saa7146_write(dev, MC2, (2 << 16) | 2); - if (count > 4) - return count; - if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done #2 failed\n", __func__); - return 0; - } - - result = saa7146_read(dev, DEBI_AD); - result &= (0xffffffffUL >> ((4 - count) * 8)); - return result; -} - - - -/* av7110 ARM core boot stuff */ -#if 0 -void av7110_reset_arm(struct av7110 *av7110) -{ - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); - - /* Disable DEBI and GPIO irq */ - SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); - msleep(30); /* the firmware needs some time to initialize */ - - ARM_ResetMailBox(av7110); - - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - SAA7146_IER_ENABLE(av7110->dev, MASK_03); - - av7110->arm_ready = 1; - dprintk(1, "reset ARM\n"); -} -#endif /* 0 */ - -static int waitdebi(struct av7110 *av7110, int adr, int state) -{ - int k; - - dprintk(4, "%p\n", av7110); - - for (k = 0; k < 100; k++) { - if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state) - return 0; - udelay(5); - } - return -ETIMEDOUT; -} - -static int load_dram(struct av7110 *av7110, u32 *data, int len) -{ - int i; - int blocks, rest; - u32 base, bootblock = AV7110_BOOT_BLOCK; - - dprintk(4, "%p\n", av7110); - - blocks = len / AV7110_BOOT_MAX_SIZE; - rest = len % AV7110_BOOT_MAX_SIZE; - base = DRAM_START_CODE; - - for (i = 0; i < blocks; i++) { - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i); - return -ETIMEDOUT; - } - dprintk(4, "writing DRAM block %d\n", i); - mwdebi(av7110, DEBISWAB, bootblock, - ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); - bootblock ^= 0x1400; - iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - base += AV7110_BOOT_MAX_SIZE; - } - - if (rest > 0) { - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n"); - return -ETIMEDOUT; - } - if (rest > 4) - mwdebi(av7110, DEBISWAB, bootblock, - ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest); - else - mwdebi(av7110, DEBISWAB, bootblock, - ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); - - iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - } - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n"); - return -ETIMEDOUT; - } - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n"); - return -ETIMEDOUT; - } - return 0; -} - - -/* we cannot write av7110 DRAM directly, so load a bootloader into - * the DPRAM which implements a simple boot protocol */ -int av7110_bootarm(struct av7110 *av7110) -{ - const struct firmware *fw; - const char *fw_name = "av7110/bootcode.bin"; - struct saa7146_dev *dev = av7110->dev; - u32 ret; - int i; - - dprintk(4, "%p\n", av7110); - - av7110->arm_ready = 0; - - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); - - /* Disable DEBI and GPIO irq */ - SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - - /* enable DEBI */ - saa7146_write(av7110->dev, MC1, 0x08800880); - saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); - saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - /* test DEBI */ - iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); - /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */ - iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); - - if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) { - printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: " - "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", - ret, 0x10325476); - return -1; - } - for (i = 0; i < 8192; i += 4) - iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4); - dprintk(2, "debi test OK\n"); - - /* boot */ - dprintk(1, "load boot code\n"); - saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO); - //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT); - //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT); - - ret = request_firmware(&fw, fw_name, &dev->pci->dev); - if (ret) { - printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", - fw_name); - return ret; - } - - mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size); - release_firmware(fw); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - - if (saa7146_wait_for_debi_done(av7110->dev, 1)) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " - "saa7146_wait_for_debi_done() timed out\n"); - return -ETIMEDOUT; - } - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); - mdelay(1); - - dprintk(1, "load dram code\n"); - if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " - "load_dram() failed\n"); - return -1; - } - - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); - mdelay(1); - - dprintk(1, "load dpram code\n"); - mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); - - if (saa7146_wait_for_debi_done(av7110->dev, 1)) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " - "saa7146_wait_for_debi_done() timed out after loading DRAM\n"); - return -ETIMEDOUT; - } - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); - msleep(30); /* the firmware needs some time to initialize */ - - //ARM_ClearIrq(av7110); - ARM_ResetMailBox(av7110); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - SAA7146_IER_ENABLE(av7110->dev, MASK_03); - - av7110->arm_errors = 0; - av7110->arm_ready = 1; - return 0; -} -MODULE_FIRMWARE("av7110/bootcode.bin"); - -/**************************************************************************** - * DEBI command polling - ****************************************************************************/ - -int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) -{ - unsigned long start; - u32 stat; - int err; - - if (FW_VERSION(av7110->arm_app) <= 0x261c) { - /* not supported by old firmware */ - msleep(50); - return 0; - } - - /* new firmware */ - start = jiffies; - for (;;) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - mutex_unlock(&av7110->dcomlock); - if ((stat & flags) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", - __func__, stat & flags); - return -ETIMEDOUT; - } - msleep(1); - } - return 0; -} - -static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) -{ - int i; - unsigned long start; - char *type = NULL; - u16 flags[2] = {0, 0}; - u32 stat; - int err; - -// dprintk(4, "%p\n", av7110); - - if (!av7110->arm_ready) { - dprintk(1, "arm not ready.\n"); - return -ENXIO; - } - - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__); - av7110->arm_errors++; - return -ETIMEDOUT; - } - msleep(1); - } - - if (FW_VERSION(av7110->arm_app) <= 0x261f) - wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2); - -#ifndef _NOHANDSHAKE - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_SHAKE); - if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__); - return -ETIMEDOUT; - } - msleep(1); - } -#endif - - switch ((buf[0] >> 8) & 0xff) { - case COMTYPE_PIDFILTER: - case COMTYPE_ENCODER: - case COMTYPE_REC_PLAY: - case COMTYPE_MPEGDECODER: - type = "MSG"; - flags[0] = GPMQOver; - flags[1] = GPMQFull; - break; - case COMTYPE_OSD: - type = "OSD"; - flags[0] = OSDQOver; - flags[1] = OSDQFull; - break; - case COMTYPE_MISC: - if (FW_VERSION(av7110->arm_app) >= 0x261d) { - type = "MSG"; - flags[0] = GPMQOver; - flags[1] = GPMQBusy; - } - break; - default: - break; - } - - if (type != NULL) { - /* non-immediate COMMAND type */ - start = jiffies; - for (;;) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - if (stat & flags[0]) { - printk(KERN_ERR "%s: %s QUEUE overflow\n", - __func__, type); - return -1; - } - if ((stat & flags[1]) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", - __func__, type); - av7110->arm_errors++; - return -ETIMEDOUT; - } - msleep(1); - } - } - - for (i = 2; i < length; i++) - wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); - - if (length) - wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2); - else - wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2); - - wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); - - if (FW_VERSION(av7110->arm_app) <= 0x261f) - wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); - -#ifdef COM_DEBUG - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n", - __func__, (buf[0] >> 8) & 0xff); - return -ETIMEDOUT; - } - msleep(1); - } - - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - if (stat & GPMQOver) { - printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); - return -ENOSPC; - } - else if (stat & OSDQOver) { - printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); - return -ENOSPC; - } -#endif - - return 0; -} - -static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) -{ - int ret; - -// dprintk(4, "%p\n", av7110); - - if (!av7110->arm_ready) { - dprintk(1, "arm not ready.\n"); - return -1; - } - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - - ret = __av7110_send_fw_cmd(av7110, buf, length); - mutex_unlock(&av7110->dcomlock); - if (ret && ret!=-ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", - __func__, ret); - return ret; -} - -int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) -{ - va_list args; - u16 buf[num + 2]; - int i, ret; - -// dprintk(4, "%p\n", av7110); - - buf[0] = ((type << 8) | com); - buf[1] = num; - - if (num) { - va_start(args, num); - for (i = 0; i < num; i++) - buf[i + 2] = va_arg(args, u32); - va_end(args); - } - - ret = av7110_send_fw_cmd(av7110, buf, num + 2); - if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret); - return ret; -} - -#if 0 -int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) -{ - int i, ret; - u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(4, "%p\n", av7110); - - for(i = 0; i < len && i < 32; i++) - { - if(i % 2 == 0) - cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; - else - cmd[(i / 2) + 2] |= buf[i]; - } - - ret = av7110_send_fw_cmd(av7110, cmd, 18); - if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret); - return ret; -} -#endif /* 0 */ - -int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, - int request_buf_len, u16 *reply_buf, int reply_buf_len) -{ - int err; - s16 i; - unsigned long start; -#ifdef COM_DEBUG - u32 stat; -#endif - - dprintk(4, "%p\n", av7110); - - if (!av7110->arm_ready) { - dprintk(1, "arm not ready.\n"); - return -1; - } - - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - - if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) { - mutex_unlock(&av7110->dcomlock); - printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); - return err; - } - - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } -#ifdef _NOHANDSHAKE - msleep(1); -#endif - } - -#ifndef _NOHANDSHAKE - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_SHAKE); - if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } -#endif - -#ifdef COM_DEBUG - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - if (stat & GPMQOver) { - printk(KERN_ERR "%s: GPMQOver\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -1; - } - else if (stat & OSDQOver) { - printk(KERN_ERR "%s: OSDQOver\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -1; - } -#endif - - for (i = 0; i < reply_buf_len; i++) - reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2); - - mutex_unlock(&av7110->dcomlock); - return 0; -} - -static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) -{ - int ret; - ret = av7110_fw_request(av7110, &tag, 0, buf, length); - if (ret) - printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); - return ret; -} - - -/**************************************************************************** - * Firmware commands - ****************************************************************************/ - -/* get version of the firmware ROM, RTSL, video ucode and ARM application */ -int av7110_firmversion(struct av7110 *av7110) -{ - u16 buf[20]; - u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion); - - dprintk(4, "%p\n", av7110); - - if (av7110_fw_query(av7110, tag, buf, 16)) { - printk("dvb-ttpci: failed to boot firmware @ card %d\n", - av7110->dvb_adapter.num); - return -EIO; - } - - av7110->arm_fw = (buf[0] << 16) + buf[1]; - av7110->arm_rtsl = (buf[2] << 16) + buf[3]; - av7110->arm_vid = (buf[4] << 16) + buf[5]; - av7110->arm_app = (buf[6] << 16) + buf[7]; - av7110->avtype = (buf[8] << 16) + buf[9]; - - printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", - av7110->dvb_adapter.num, av7110->arm_fw, - av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); - - /* print firmware capabilities */ - if (FW_CI_LL_SUPPORT(av7110->arm_app)) - printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n", - av7110->dvb_adapter.num); - else - printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n", - av7110->dvb_adapter.num); - - return 0; -} - - -int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst) -{ - int i, ret; - u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(4, "%p\n", av7110); - - if (len > 10) - len = 10; - - buf[1] = len + 2; - buf[2] = len; - - if (burst != -1) - buf[3] = burst ? 0x01 : 0x00; - else - buf[3] = 0xffff; - - for (i = 0; i < len; i++) - buf[i + 4] = msg[i]; - - ret = av7110_send_fw_cmd(av7110, buf, 18); - if (ret && ret!=-ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); - return ret; -} - - -#ifdef CONFIG_DVB_AV7110_OSD - -static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr); -} - -static inline int SetBlend_(struct av7110 *av7110, u8 windownr, - enum av7110_osd_palette_type colordepth, u16 index, u8 blending) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4, - windownr, colordepth, index, blending); -} - -static inline int SetColor_(struct av7110 *av7110, u8 windownr, - enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5, - windownr, colordepth, index, colorhi, colorlo); -} - -static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize, - u16 colorfg, u16 colorbg) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4, - windownr, fontsize, colorfg, colorbg); -} - -static int FlushText(struct av7110 *av7110) -{ - unsigned long start; - int err; - - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_OSD); - if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n", - __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } - mutex_unlock(&av7110->dcomlock); - return 0; -} - -static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) -{ - int i, ret; - unsigned long start; - int length = strlen(buf) + 1; - u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y }; - - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - - start = jiffies; - while (1) { - ret = time_after(jiffies, start + ARM_WAIT_OSD); - if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) - break; - if (ret) { - printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n", - __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } -#ifndef _NOHANDSHAKE - start = jiffies; - while (1) { - ret = time_after(jiffies, start + ARM_WAIT_SHAKE); - if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) - break; - if (ret) { - printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n", - __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } -#endif - for (i = 0; i < length / 2; i++) - wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, - swab16(*(u16 *)(buf + 2 * i)), 2); - if (length & 1) - wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2); - ret = __av7110_send_fw_cmd(av7110, cbuf, 5); - mutex_unlock(&av7110->dcomlock); - if (ret && ret!=-ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); - return ret; -} - -static inline int DrawLine(struct av7110 *av7110, u8 windownr, - u16 x, u16 y, u16 dx, u16 dy, u16 color) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6, - windownr, x, y, dx, dy, color); -} - -static inline int DrawBlock(struct av7110 *av7110, u8 windownr, - u16 x, u16 y, u16 dx, u16 dy, u16 color) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6, - windownr, x, y, dx, dy, color); -} - -static inline int HideWindow(struct av7110 *av7110, u8 windownr) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr); -} - -static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y); -} - -static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y); -} - -static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr); -} - -static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr, - osd_raw_window_t disptype, - u16 width, u16 height) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4, - windownr, disptype, width, height); -} - - -static enum av7110_osd_palette_type bpp2pal[8] = { - Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit -}; -static osd_raw_window_t bpp2bit[8] = { - OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 -}; - -static inline int WaitUntilBmpLoaded(struct av7110 *av7110) -{ - int ret = wait_event_timeout(av7110->bmpq, - av7110->bmp_state != BMP_LOADING, 10*HZ); - if (ret == 0) { - printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", - ret, av7110->bmp_state); - av7110->bmp_state = BMP_NONE; - return -ETIMEDOUT; - } - return 0; -} - -static inline int LoadBitmap(struct av7110 *av7110, - u16 dx, u16 dy, int inc, u8 __user * data) -{ - u16 format; - int bpp; - int i; - int d, delta; - u8 c; - int ret; - - dprintk(4, "%p\n", av7110); - - format = bpp2bit[av7110->osdbpp[av7110->osdwin]]; - - av7110->bmp_state = BMP_LOADING; - if (format == OSD_BITMAP8) { - bpp=8; delta = 1; - } else if (format == OSD_BITMAP4) { - bpp=4; delta = 2; - } else if (format == OSD_BITMAP2) { - bpp=2; delta = 4; - } else if (format == OSD_BITMAP1) { - bpp=1; delta = 8; - } else { - av7110->bmp_state = BMP_NONE; - return -EINVAL; - } - av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8; - av7110->bmpp = 0; - if (av7110->bmplen > 32768) { - av7110->bmp_state = BMP_NONE; - return -EINVAL; - } - for (i = 0; i < dy; i++) { - if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) { - av7110->bmp_state = BMP_NONE; - return -EINVAL; - } - } - if (format != OSD_BITMAP8) { - for (i = 0; i < dx * dy / delta; i++) { - c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1]; - for (d = delta - 2; d >= 0; d--) { - c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d] - << ((delta - d - 1) * bpp)); - ((u8 *)av7110->bmpbuf)[1024 + i] = c; - } - } - } - av7110->bmplen += 1024; - dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen); - ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy); - if (!ret) - ret = WaitUntilBmpLoaded(av7110); - return ret; -} - -static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y) -{ - dprintk(4, "%p\n", av7110); - - return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0); -} - -static inline int ReleaseBitmap(struct av7110 *av7110) -{ - dprintk(4, "%p\n", av7110); - - if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e) - return -1; - if (av7110->bmp_state == BMP_LOADING) - dprintk(1,"ReleaseBitmap called while BMP_LOADING\n"); - av7110->bmp_state = BMP_NONE; - return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0); -} - -static u32 RGB2YUV(u16 R, u16 G, u16 B) -{ - u16 y, u, v; - u16 Y, Cr, Cb; - - y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */ - u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */ - v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */ - - Y = y / 256; - Cb = u / 16; - Cr = v / 16; - - return Cr | (Cb << 16) | (Y << 8); -} - -static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend) -{ - int ret; - - u16 ch, cl; - u32 yuv; - - yuv = blend ? RGB2YUV(r,g,b) : 0; - cl = (yuv & 0xffff); - ch = ((yuv >> 16) & 0xffff); - ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], - color, ch, cl); - if (!ret) - ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], - color, ((blend >> 4) & 0x0f)); - return ret; -} - -static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last) -{ - int i; - int length = last - first + 1; - - if (length * 4 > DATA_BUFF3_SIZE) - return -EINVAL; - - for (i = 0; i < length; i++) { - u32 color, blend, yuv; - - if (get_user(color, colors + i)) - return -EFAULT; - blend = (color & 0xF0000000) >> 4; - yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF, - (color >> 16) & 0xFF) | blend : 0; - yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16); - wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4); - } - return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4, - av7110->osdwin, - bpp2pal[av7110->osdbpp[av7110->osdwin]], - first, last); -} - -static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, - int x1, int y1, int inc, u8 __user * data) -{ - uint w, h, bpp, bpl, size, lpb, bnum, brest; - int i; - int rc,release_rc; - - w = x1 - x0 + 1; - h = y1 - y0 + 1; - if (inc <= 0) - inc = w; - if (w <= 0 || w > 720 || h <= 0 || h > 576) - return -EINVAL; - bpp = av7110->osdbpp[av7110->osdwin] + 1; - bpl = ((w * bpp + 7) & ~7) / 8; - size = h * bpl; - lpb = (32 * 1024) / bpl; - bnum = size / (lpb * bpl); - brest = size - bnum * lpb * bpl; - - if (av7110->bmp_state == BMP_LOADING) { - /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */ - BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e); - rc = WaitUntilBmpLoaded(av7110); - if (rc) - return rc; - /* just continue. This should work for all fw versions - * if bnum==1 && !brest && LoadBitmap was successful - */ - } - - rc = 0; - for (i = 0; i < bnum; i++) { - rc = LoadBitmap(av7110, w, lpb, inc, data); - if (rc) - break; - rc = BlitBitmap(av7110, x0, y0 + i * lpb); - if (rc) - break; - data += lpb * inc; - } - if (!rc && brest) { - rc = LoadBitmap(av7110, w, brest / bpl, inc, data); - if (!rc) - rc = BlitBitmap(av7110, x0, y0 + bnum * lpb); - } - release_rc = ReleaseBitmap(av7110); - if (!rc) - rc = release_rc; - if (rc) - dprintk(1,"returns %d\n",rc); - return rc; -} - -int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) -{ - int ret; - - if (mutex_lock_interruptible(&av7110->osd_mutex)) - return -ERESTARTSYS; - - switch (dc->cmd) { - case OSD_Close: - ret = DestroyOSDWindow(av7110, av7110->osdwin); - break; - case OSD_Open: - av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7; - ret = CreateOSDWindow(av7110, av7110->osdwin, - bpp2bit[av7110->osdbpp[av7110->osdwin]], - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); - if (ret) - break; - if (!dc->data) { - ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); - if (ret) - break; - ret = SetColorBlend(av7110, av7110->osdwin); - } - break; - case OSD_Show: - ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0); - break; - case OSD_Hide: - ret = HideWindow(av7110, av7110->osdwin); - break; - case OSD_Clear: - ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0); - break; - case OSD_Fill: - ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color); - break; - case OSD_SetColor: - ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1); - break; - case OSD_SetPalette: - if (FW_VERSION(av7110->arm_app) >= 0x2618) - ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); - else { - int i, len = dc->x0-dc->color+1; - u8 __user *colors = (u8 __user *)dc->data; - u8 r, g = 0, b = 0, blend = 0; - ret = 0; - for (i = 0; icolor + i, r, g, b, blend); - if (ret) - break; - } - } - break; - case OSD_SetPixel: - ret = DrawLine(av7110, av7110->osdwin, - dc->x0, dc->y0, 0, 0, dc->color); - break; - case OSD_SetRow: - dc->y1 = dc->y0; - /* fall through */ - case OSD_SetBlock: - ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data); - break; - case OSD_FillRow: - ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, - dc->x1-dc->x0+1, dc->y1, dc->color); - break; - case OSD_FillBlock: - ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); - break; - case OSD_Line: - ret = DrawLine(av7110, av7110->osdwin, - dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); - break; - case OSD_Text: - { - char textbuf[240]; - - if (strncpy_from_user(textbuf, dc->data, 240) < 0) { - ret = -EFAULT; - break; - } - textbuf[239] = 0; - if (dc->x1 > 3) - dc->x1 = 3; - ret = SetFont(av7110, av7110->osdwin, dc->x1, - (u16) (dc->color & 0xffff), (u16) (dc->color >> 16)); - if (!ret) - ret = FlushText(av7110); - if (!ret) - ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf); - break; - } - case OSD_SetWindow: - if (dc->x0 < 1 || dc->x0 > 7) - ret = -EINVAL; - else { - av7110->osdwin = dc->x0; - ret = 0; - } - break; - case OSD_MoveWindow: - ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); - if (!ret) - ret = SetColorBlend(av7110, av7110->osdwin); - break; - case OSD_OpenRaw: - if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) { - ret = -EINVAL; - break; - } - if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR) - av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1; - else - av7110->osdbpp[av7110->osdwin] = 0; - ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color, - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); - if (ret) - break; - if (!dc->data) { - ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); - if (!ret) - ret = SetColorBlend(av7110, av7110->osdwin); - } - break; - default: - ret = -EINVAL; - break; - } - - mutex_unlock(&av7110->osd_mutex); - if (ret==-ERESTARTSYS) - dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); - else if (ret) - dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); - - return ret; -} - -int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap) -{ - switch (cap->cmd) { - case OSD_CAP_MEMSIZE: - if (FW_4M_SDRAM(av7110->arm_app)) - cap->val = 1000000; - else - cap->val = 92000; - return 0; - default: - return -EINVAL; - } -} -#endif /* CONFIG_DVB_AV7110_OSD */ diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h deleted file mode 100644 index 1634aba5cb84..000000000000 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ /dev/null @@ -1,495 +0,0 @@ -#ifndef _AV7110_HW_H_ -#define _AV7110_HW_H_ - -#include "av7110.h" - -/* DEBI transfer mode defs */ - -#define DEBINOSWAP 0x000e0000 -#define DEBISWAB 0x001e0000 -#define DEBISWAP 0x002e0000 - -#define ARM_WAIT_FREE (HZ) -#define ARM_WAIT_SHAKE (HZ/5) -#define ARM_WAIT_OSD (HZ) - - -enum av7110_bootstate -{ - BOOTSTATE_BUFFER_EMPTY = 0, - BOOTSTATE_BUFFER_FULL = 1, - BOOTSTATE_AV7110_BOOT_COMPLETE = 2 -}; - -enum av7110_type_rec_play_format -{ RP_None, - AudioPES, - AudioMp2, - AudioPCM, - VideoPES, - AV_PES -}; - -enum av7110_osd_palette_type -{ - NoPalet = 0, /* No palette */ - Pal1Bit = 2, /* 2 colors for 1 Bit Palette */ - Pal2Bit = 4, /* 4 colors for 2 bit palette */ - Pal4Bit = 16, /* 16 colors for 4 bit palette */ - Pal8Bit = 256 /* 256 colors for 16 bit palette */ -}; - -/* switch defines */ -#define SB_GPIO 3 -#define SB_OFF SAA7146_GPIO_OUTLO /* SlowBlank off (TV-Mode) */ -#define SB_ON SAA7146_GPIO_INPUT /* SlowBlank on (AV-Mode) */ -#define SB_WIDE SAA7146_GPIO_OUTHI /* SlowBlank 6V (16/9-Mode) (not implemented) */ - -#define FB_GPIO 1 -#define FB_OFF SAA7146_GPIO_LO /* FastBlank off (CVBS-Mode) */ -#define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */ -#define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */ - -enum av7110_video_output_mode -{ - NO_OUT = 0, /* disable analog output */ - CVBS_RGB_OUT = 1, - CVBS_YC_OUT = 2, - YC_OUT = 3 -}; - -/* firmware internal msg q status: */ -#define GPMQFull 0x0001 /* Main Message Queue Full */ -#define GPMQOver 0x0002 /* Main Message Queue Overflow */ -#define HPQFull 0x0004 /* High Priority Msg Queue Full */ -#define HPQOver 0x0008 -#define OSDQFull 0x0010 /* OSD Queue Full */ -#define OSDQOver 0x0020 -#define GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */ -#define HPQBusy 0x0080 -#define OSDQBusy 0x0100 - -/* hw section filter flags */ -#define SECTION_EIT 0x01 -#define SECTION_SINGLE 0x00 -#define SECTION_CYCLE 0x02 -#define SECTION_CONTINUOS 0x04 -#define SECTION_MODE 0x06 -#define SECTION_IPMPE 0x0C /* size up to 4k */ -#define SECTION_HIGH_SPEED 0x1C /* larger buffer */ -#define DATA_PIPING_FLAG 0x20 /* for Data Piping Filter */ - -#define PBUFSIZE_NONE 0x0000 -#define PBUFSIZE_1P 0x0100 -#define PBUFSIZE_2P 0x0200 -#define PBUFSIZE_1K 0x0300 -#define PBUFSIZE_2K 0x0400 -#define PBUFSIZE_4K 0x0500 -#define PBUFSIZE_8K 0x0600 -#define PBUFSIZE_16K 0x0700 -#define PBUFSIZE_32K 0x0800 - - -/* firmware command codes */ -enum av7110_osd_command { - WCreate, - WDestroy, - WMoveD, - WMoveA, - WHide, - WTop, - DBox, - DLine, - DText, - Set_Font, - SetColor, - SetBlend, - SetWBlend, - SetCBlend, - SetNonBlend, - LoadBmp, - BlitBmp, - ReleaseBmp, - SetWTrans, - SetWNoTrans, - Set_Palette -}; - -enum av7110_pid_command { - MultiPID, - VideoPID, - AudioPID, - InitFilt, - FiltError, - NewVersion, - CacheError, - AddPIDFilter, - DelPIDFilter, - Scan, - SetDescr, - SetIR, - FlushTSQueue -}; - -enum av7110_mpeg_command { - SelAudChannels -}; - -enum av7110_audio_command { - AudioDAC, - CabADAC, - ON22K, - OFF22K, - MainSwitch, - ADSwitch, - SendDiSEqC, - SetRegister, - SpdifSwitch -}; - -enum av7110_request_command { - AudioState, - AudioBuffState, - VideoState1, - VideoState2, - VideoState3, - CrashCounter, - ReqVersion, - ReqVCXO, - ReqRegister, - ReqSecFilterError, - ReqSTC -}; - -enum av7110_encoder_command { - SetVidMode, - SetTestMode, - LoadVidCode, - SetMonitorType, - SetPanScanType, - SetFreezeMode, - SetWSSConfig -}; - -enum av7110_rec_play_state { - __Record, - __Stop, - __Play, - __Pause, - __Slow, - __FF_IP, - __Scan_I, - __Continue -}; - -enum av7110_fw_cmd_misc { - AV7110_FW_VIDEO_ZOOM = 1, - AV7110_FW_VIDEO_COMMAND, - AV7110_FW_AUDIO_COMMAND -}; - -enum av7110_command_type { - COMTYPE_NOCOM, - COMTYPE_PIDFILTER, - COMTYPE_MPEGDECODER, - COMTYPE_OSD, - COMTYPE_BMP, - COMTYPE_ENCODER, - COMTYPE_AUDIODAC, - COMTYPE_REQUEST, - COMTYPE_SYSTEM, - COMTYPE_REC_PLAY, - COMTYPE_COMMON_IF, - COMTYPE_PID_FILTER, - COMTYPE_PES, - COMTYPE_TS, - COMTYPE_VIDEO, - COMTYPE_AUDIO, - COMTYPE_CI_LL, - COMTYPE_MISC = 0x80 -}; - -#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */ -#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */ -#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */ -#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */ -#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ - -/* MPEG video decoder commands */ -#define AV_VIDEO_CMD_STOP 0x000e -#define AV_VIDEO_CMD_PLAY 0x000d -#define AV_VIDEO_CMD_FREEZE 0x0102 -#define AV_VIDEO_CMD_FFWD 0x0016 -#define AV_VIDEO_CMD_SLOW 0x0022 - -/* MPEG audio decoder commands */ -#define AUDIO_CMD_MUTE 0x0001 -#define AUDIO_CMD_UNMUTE 0x0002 -#define AUDIO_CMD_PCM16 0x0010 -#define AUDIO_CMD_STEREO 0x0080 -#define AUDIO_CMD_MONO_L 0x0100 -#define AUDIO_CMD_MONO_R 0x0200 -#define AUDIO_CMD_SYNC_OFF 0x000e -#define AUDIO_CMD_SYNC_ON 0x000f - -/* firmware data interface codes */ -#define DATA_NONE 0x00 -#define DATA_FSECTION 0x01 -#define DATA_IPMPE 0x02 -#define DATA_MPEG_RECORD 0x03 -#define DATA_DEBUG_MESSAGE 0x04 -#define DATA_COMMON_INTERFACE 0x05 -#define DATA_MPEG_PLAY 0x06 -#define DATA_BMP_LOAD 0x07 -#define DATA_IRCOMMAND 0x08 -#define DATA_PIPING 0x09 -#define DATA_STREAMING 0x0a -#define DATA_CI_GET 0x0b -#define DATA_CI_PUT 0x0c -#define DATA_MPEG_VIDEO_EVENT 0x0d - -#define DATA_PES_RECORD 0x10 -#define DATA_PES_PLAY 0x11 -#define DATA_TS_RECORD 0x12 -#define DATA_TS_PLAY 0x13 - -/* ancient CI command codes, only two are actually still used - * by the link level CI firmware */ -#define CI_CMD_ERROR 0x00 -#define CI_CMD_ACK 0x01 -#define CI_CMD_SYSTEM_READY 0x02 -#define CI_CMD_KEYPRESS 0x03 -#define CI_CMD_ON_TUNED 0x04 -#define CI_CMD_ON_SWITCH_PROGRAM 0x05 -#define CI_CMD_SECTION_ARRIVED 0x06 -#define CI_CMD_SECTION_TIMEOUT 0x07 -#define CI_CMD_TIME 0x08 -#define CI_CMD_ENTER_MENU 0x09 -#define CI_CMD_FAST_PSI 0x0a -#define CI_CMD_GET_SLOT_INFO 0x0b - -#define CI_MSG_NONE 0x00 -#define CI_MSG_CI_INFO 0x01 -#define CI_MSG_MENU 0x02 -#define CI_MSG_LIST 0x03 -#define CI_MSG_TEXT 0x04 -#define CI_MSG_REQUEST_INPUT 0x05 -#define CI_MSG_INPUT_COMPLETE 0x06 -#define CI_MSG_LIST_MORE 0x07 -#define CI_MSG_MENU_MORE 0x08 -#define CI_MSG_CLOSE_MMI_IMM 0x09 -#define CI_MSG_SECTION_REQUEST 0x0a -#define CI_MSG_CLOSE_FILTER 0x0b -#define CI_PSI_COMPLETE 0x0c -#define CI_MODULE_READY 0x0d -#define CI_SWITCH_PRG_REPLY 0x0e -#define CI_MSG_TEXT_MORE 0x0f - -#define CI_MSG_CA_PMT 0xe0 -#define CI_MSG_ERROR 0xf0 - - -/* base address of the dual ported RAM which serves as communication - * area between PCI bus and av7110, - * as seen by the DEBI bus of the saa7146 */ -#define DPRAM_BASE 0x4000 - -/* boot protocol area */ -#define AV7110_BOOT_STATE (DPRAM_BASE + 0x3F8) -#define AV7110_BOOT_SIZE (DPRAM_BASE + 0x3FA) -#define AV7110_BOOT_BASE (DPRAM_BASE + 0x3FC) -#define AV7110_BOOT_BLOCK (DPRAM_BASE + 0x400) -#define AV7110_BOOT_MAX_SIZE 0xc00 - -/* firmware command protocol area */ -#define IRQ_STATE (DPRAM_BASE + 0x0F4) -#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6) -#define MSGSTATE (DPRAM_BASE + 0x0F8) -#define COMMAND (DPRAM_BASE + 0x0FC) -#define COM_BUFF (DPRAM_BASE + 0x100) -#define COM_BUFF_SIZE 0x20 - -/* various data buffers */ -#define BUFF1_BASE (DPRAM_BASE + 0x120) -#define BUFF1_SIZE 0xE0 - -#define DATA_BUFF0_BASE (DPRAM_BASE + 0x200) -#define DATA_BUFF0_SIZE 0x0800 - -#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE) -#define DATA_BUFF1_SIZE 0x0800 - -#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE) -#define DATA_BUFF2_SIZE 0x0800 - -#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE) -#define DATA_BUFF3_SIZE 0x0400 - -#define Reserved (DPRAM_BASE + 0x1E00) -#define Reserved_SIZE 0x1C0 - - -/* firmware status area */ -#define STATUS_BASE (DPRAM_BASE + 0x1FC0) -#define STATUS_LOOPS (STATUS_BASE + 0x08) - -#define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C) -/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */ -#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E) - -/* firmware data protocol area */ -#define RX_TYPE (DPRAM_BASE + 0x1FE8) -#define RX_LEN (DPRAM_BASE + 0x1FEA) -#define TX_TYPE (DPRAM_BASE + 0x1FEC) -#define TX_LEN (DPRAM_BASE + 0x1FEE) - -#define RX_BUFF (DPRAM_BASE + 0x1FF4) -#define TX_BUFF (DPRAM_BASE + 0x1FF6) - -#define HANDSHAKE_REG (DPRAM_BASE + 0x1FF8) -#define COM_IF_LOCK (DPRAM_BASE + 0x1FFA) - -#define IRQ_RX (DPRAM_BASE + 0x1FFC) -#define IRQ_TX (DPRAM_BASE + 0x1FFE) - -/* used by boot protocol to load firmware into av7110 DRAM */ -#define DRAM_START_CODE 0x2e000404 -#define DRAM_MAX_CODE_SIZE 0x00100000 - -/* saa7146 gpio lines */ -#define RESET_LINE 2 -#define DEBI_DONE_LINE 1 -#define ARM_IRQ_LINE 0 - - - -extern int av7110_bootarm(struct av7110 *av7110); -extern int av7110_firmversion(struct av7110 *av7110); -#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) -#define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) -#define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) - -extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); -extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); -extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, - int request_buf_len, u16 *reply_buf, int reply_buf_len); - - -/* DEBI (saa7146 data extension bus interface) access */ -extern int av7110_debiwrite(struct av7110 *av7110, u32 config, - int addr, u32 val, int count); -extern u32 av7110_debiread(struct av7110 *av7110, u32 config, - int addr, int count); - - -/* DEBI during interrupt */ -/* single word writes */ -static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) -{ - av7110_debiwrite(av7110, config, addr, val, count); -} - -/* buffer writes */ -static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, - const u8 *val, int count) -{ - memcpy(av7110->debi_virt, val, count); - av7110_debiwrite(av7110, config, addr, 0, count); -} - -static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) -{ - u32 res; - - res=av7110_debiread(av7110, config, addr, count); - if (count<=4) - memcpy(av7110->debi_virt, (char *) &res, count); - return res; -} - -/* DEBI outside interrupts, only for count <= 4! */ -static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) -{ - unsigned long flags; - - spin_lock_irqsave(&av7110->debilock, flags); - av7110_debiwrite(av7110, config, addr, val, count); - spin_unlock_irqrestore(&av7110->debilock, flags); -} - -static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) -{ - unsigned long flags; - u32 res; - - spin_lock_irqsave(&av7110->debilock, flags); - res=av7110_debiread(av7110, config, addr, count); - spin_unlock_irqrestore(&av7110->debilock, flags); - return res; -} - -/* handle mailbox registers of the dual ported RAM */ -static inline void ARM_ResetMailBox(struct av7110 *av7110) -{ - unsigned long flags; - - spin_lock_irqsave(&av7110->debilock, flags); - av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2); - av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2); - spin_unlock_irqrestore(&av7110->debilock, flags); -} - -static inline void ARM_ClearMailBox(struct av7110 *av7110) -{ - iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); -} - -static inline void ARM_ClearIrq(struct av7110 *av7110) -{ - irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); -} - -/**************************************************************************** - * Firmware commands - ****************************************************************************/ - -static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) -{ - return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data); -} - -static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) -{ - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); -} - -static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) -{ - return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4, - (com>>16), (com&0xffff), - (arg>>16), (arg&0xffff)); -} - -static inline int audcom(struct av7110 *av7110, u32 com) -{ - return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2, - (com>>16), (com&0xffff)); -} - -static inline int Set22K(struct av7110 *av7110, int state) -{ - return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0); -} - - -extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); - - -#ifdef CONFIG_DVB_AV7110_OSD -extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); -extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); -#endif /* CONFIG_DVB_AV7110_OSD */ - - - -#endif /* _AV7110_HW_H_ */ diff --git a/drivers/media/dvb/ttpci/av7110_ipack.c b/drivers/media/dvb/ttpci/av7110_ipack.c deleted file mode 100644 index 699ef8b5b99a..000000000000 --- a/drivers/media/dvb/ttpci/av7110_ipack.c +++ /dev/null @@ -1,403 +0,0 @@ -#include "dvb_filter.h" -#include "av7110_ipack.h" -#include /* for memcpy() */ -#include - - -void av7110_ipack_reset(struct ipack *p) -{ - p->found = 0; - p->cid = 0; - p->plength = 0; - p->flag1 = 0; - p->flag2 = 0; - p->hlength = 0; - p->mpeg = 0; - p->check = 0; - p->which = 0; - p->done = 0; - p->count = 0; -} - - -int av7110_ipack_init(struct ipack *p, int size, - void (*func)(u8 *buf, int size, void *priv)) -{ - if (!(p->buf = vmalloc(size*sizeof(u8)))) { - printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); - return -ENOMEM; - } - p->size = size; - p->func = func; - p->repack_subids = 0; - av7110_ipack_reset(p); - return 0; -} - - -void av7110_ipack_free(struct ipack *p) -{ - vfree(p->buf); -} - - -static void send_ipack(struct ipack *p) -{ - int off; - struct dvb_audio_info ai; - int ac3_off = 0; - int streamid = 0; - int nframes = 0; - int f = 0; - - switch (p->mpeg) { - case 2: - if (p->count < 10) - return; - p->buf[3] = p->cid; - p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); - p->buf[5] = (u8)((p->count - 6) & 0x00ff); - if (p->repack_subids && p->cid == PRIVATE_STREAM1) { - off = 9 + p->buf[8]; - streamid = p->buf[off]; - if ((streamid & 0xf8) == 0x80) { - ai.off = 0; - ac3_off = ((p->buf[off + 2] << 8)| - p->buf[off + 3]); - if (ac3_off < p->count) - f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, - p->count - ac3_off, &ai, 0); - if (!f) { - nframes = (p->count - off - 3 - ac3_off) / - ai.framesize + 1; - p->buf[off + 2] = (ac3_off >> 8) & 0xff; - p->buf[off + 3] = (ac3_off) & 0xff; - p->buf[off + 1] = nframes; - ac3_off += nframes * ai.framesize - p->count; - } - } - } - p->func(p->buf, p->count, p->data); - - p->buf[6] = 0x80; - p->buf[7] = 0x00; - p->buf[8] = 0x00; - p->count = 9; - if (p->repack_subids && p->cid == PRIVATE_STREAM1 - && (streamid & 0xf8) == 0x80) { - p->count += 4; - p->buf[9] = streamid; - p->buf[10] = (ac3_off >> 8) & 0xff; - p->buf[11] = (ac3_off) & 0xff; - p->buf[12] = 0; - } - break; - - case 1: - if (p->count < 8) - return; - p->buf[3] = p->cid; - p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); - p->buf[5] = (u8)((p->count - 6) & 0x00ff); - p->func(p->buf, p->count, p->data); - - p->buf[6] = 0x0f; - p->count = 7; - break; - } -} - - -void av7110_ipack_flush(struct ipack *p) -{ - if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) - return; - p->plength = p->found - 6; - p->found = 0; - send_ipack(p); - av7110_ipack_reset(p); -} - - -static void write_ipack(struct ipack *p, const u8 *data, int count) -{ - u8 headr[3] = { 0x00, 0x00, 0x01 }; - - if (p->count < 6) { - memcpy(p->buf, headr, 3); - p->count = 6; - } - - if (p->count + count < p->size){ - memcpy(p->buf+p->count, data, count); - p->count += count; - } else { - int rest = p->size - p->count; - memcpy(p->buf+p->count, data, rest); - p->count += rest; - send_ipack(p); - if (count - rest > 0) - write_ipack(p, data + rest, count - rest); - } -} - - -int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) -{ - int l; - int c = 0; - - while (c < count && (p->mpeg == 0 || - (p->mpeg == 1 && p->found < 7) || - (p->mpeg == 2 && p->found < 9)) - && (p->found < 5 || !p->done)) { - switch (p->found) { - case 0: - case 1: - if (buf[c] == 0x00) - p->found++; - else - p->found = 0; - c++; - break; - case 2: - if (buf[c] == 0x01) - p->found++; - else if (buf[c] == 0) - p->found = 2; - else - p->found = 0; - c++; - break; - case 3: - p->cid = 0; - switch (buf[c]) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM : - case EMM_STREAM : - case PADDING_STREAM : - case DSM_CC_STREAM : - case ISO13522_STREAM: - p->done = 1; - /* fall through */ - case PRIVATE_STREAM1: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - p->found++; - p->cid = buf[c]; - c++; - break; - default: - p->found = 0; - break; - } - break; - - case 4: - if (count-c > 1) { - p->plen[0] = buf[c]; - c++; - p->plen[1] = buf[c]; - c++; - p->found += 2; - p->plength = (p->plen[0] << 8) | p->plen[1]; - } else { - p->plen[0] = buf[c]; - p->found++; - return count; - } - break; - case 5: - p->plen[1] = buf[c]; - c++; - p->found++; - p->plength = (p->plen[0] << 8) | p->plen[1]; - break; - case 6: - if (!p->done) { - p->flag1 = buf[c]; - c++; - p->found++; - if ((p->flag1 & 0xc0) == 0x80) - p->mpeg = 2; - else { - p->hlength = 0; - p->which = 0; - p->mpeg = 1; - p->flag2 = 0; - } - } - break; - - case 7: - if (!p->done && p->mpeg == 2) { - p->flag2 = buf[c]; - c++; - p->found++; - } - break; - - case 8: - if (!p->done && p->mpeg == 2) { - p->hlength = buf[c]; - c++; - p->found++; - } - break; - } - } - - if (c == count) - return count; - - if (!p->plength) - p->plength = MMAX_PLENGTH - 6; - - if (p->done || ((p->mpeg == 2 && p->found >= 9) || - (p->mpeg == 1 && p->found >= 7))) { - switch (p->cid) { - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - case PRIVATE_STREAM1: - if (p->mpeg == 2 && p->found == 9) { - write_ipack(p, &p->flag1, 1); - write_ipack(p, &p->flag2, 1); - write_ipack(p, &p->hlength, 1); - } - - if (p->mpeg == 1 && p->found == 7) - write_ipack(p, &p->flag1, 1); - - if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && - p->found < 14) { - while (c < count && p->found < 14) { - p->pts[p->found - 9] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - } - if (c == count) - return count; - } - - if (p->mpeg == 1 && p->which < 2000) { - - if (p->found == 7) { - p->check = p->flag1; - p->hlength = 1; - } - - while (!p->which && c < count && - p->check == 0xff){ - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - } - - if (c == count) - return count; - - if ((p->check & 0xc0) == 0x40 && !p->which) { - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - - p->which = 1; - if (c == count) - return count; - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - p->which = 2; - if (c == count) - return count; - } - - if (p->which == 1) { - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - p->which = 2; - if (c == count) - return count; - } - - if ((p->check & 0x30) && p->check != 0xff) { - p->flag2 = (p->check & 0xf0) << 2; - p->pts[0] = p->check; - p->which = 3; - } - - if (c == count) - return count; - if (p->which > 2){ - if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { - while (c < count && p->which < 7) { - p->pts[p->which - 2] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->which++; - p->hlength++; - } - if (c == count) - return count; - } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { - while (c < count && p->which < 12) { - if (p->which < 7) - p->pts[p->which - 2] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->which++; - p->hlength++; - } - if (c == count) - return count; - } - p->which = 2000; - } - - } - - while (c < count && p->found < p->plength + 6) { - l = count - c; - if (l + p->found > p->plength + 6) - l = p->plength + 6 - p->found; - write_ipack(p, buf + c, l); - p->found += l; - c += l; - } - break; - } - - - if (p->done) { - if (p->found + count - c < p->plength + 6) { - p->found += count - c; - c = count; - } else { - c += p->plength + 6 - p->found; - p->found = p->plength + 6; - } - } - - if (p->plength && p->found == p->plength + 6) { - send_ipack(p); - av7110_ipack_reset(p); - if (c < count) - av7110_ipack_instant_repack(buf + c, count - c, p); - } - } - return count; -} diff --git a/drivers/media/dvb/ttpci/av7110_ipack.h b/drivers/media/dvb/ttpci/av7110_ipack.h deleted file mode 100644 index becf94d3fdfa..000000000000 --- a/drivers/media/dvb/ttpci/av7110_ipack.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _AV7110_IPACK_H_ -#define _AV7110_IPACK_H_ - -extern int av7110_ipack_init(struct ipack *p, int size, - void (*func)(u8 *buf, int size, void *priv)); - -extern void av7110_ipack_reset(struct ipack *p); -extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); -extern void av7110_ipack_free(struct ipack * p); -extern void av7110_ipack_flush(struct ipack *p); - -#endif diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c deleted file mode 100644 index 908f272fe26c..000000000000 --- a/drivers/media/dvb/ttpci/av7110_ir.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Driver for the remote control of SAA7146 based AV7110 cards - * - * Copyright (C) 1999-2003 Holger Waechtler - * Copyright (C) 2003-2007 Oliver Endriss - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - */ - - -#include -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" - - -#define AV_CNT 4 - -#define IR_RC5 0 -#define IR_RCMM 1 -#define IR_RC5_EXT 2 /* internal only */ - -#define IR_ALL 0xffffffff - -#define UP_TIMEOUT (HZ*7/25) - - -/* Note: enable ir debugging by or'ing debug with 16 */ - -static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; -module_param_array(ir_protocol, int, NULL, 0644); -MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); - -static int ir_inversion[AV_CNT]; -module_param_array(ir_inversion, int, NULL, 0644); -MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); - -static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; -module_param_array(ir_device_mask, uint, NULL, 0644); -MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); - - -static int av_cnt; -static struct av7110 *av_list[AV_CNT]; - -static u16 default_key_map [256] = { - KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, - KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, - KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0, - 0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0, - KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0, - KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW, - KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN, - 0, 0, 0, 0, KEY_EPG, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR -}; - - -/* key-up timer */ -static void av7110_emit_keyup(unsigned long parm) -{ - struct infrared *ir = (struct infrared *) parm; - - if (!ir || !test_bit(ir->last_key, ir->input_dev->key)) - return; - - input_report_key(ir->input_dev, ir->last_key, 0); - input_sync(ir->input_dev); -} - - -/* tasklet */ -static void av7110_emit_key(unsigned long parm) -{ - struct infrared *ir = (struct infrared *) parm; - u32 ircom = ir->ir_command; - u8 data; - u8 addr; - u16 toggle; - u16 keycode; - - /* extract device address and data */ - switch (ir->protocol) { - case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ - data = ircom & 0x3f; - addr = (ircom >> 6) & 0x1f; - toggle = ircom & 0x0800; - break; - - case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ - data = ircom & 0xff; - addr = (ircom >> 8) & 0x1f; - toggle = ircom & 0x8000; - break; - - case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ - data = ircom & 0x3f; - addr = (ircom >> 6) & 0x1f; - /* invert 7th data bit for backward compatibility with RC5 keymaps */ - if (!(ircom & 0x1000)) - data |= 0x40; - toggle = ircom & 0x0800; - break; - - default: - printk("%s invalid protocol %x\n", __func__, ir->protocol); - return; - } - - input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); - input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); - - keycode = ir->key_map[data]; - - dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", - __func__, ircom, addr, data, keycode); - - /* check device address */ - if (!(ir->device_mask & (1 << addr))) - return; - - if (!keycode) { - printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", - __func__, ircom, addr, data); - return; - } - - if (timer_pending(&ir->keyup_timer)) { - del_timer(&ir->keyup_timer); - if (ir->last_key != keycode || toggle != ir->last_toggle) { - ir->delay_timer_finished = 0; - input_event(ir->input_dev, EV_KEY, ir->last_key, 0); - input_event(ir->input_dev, EV_KEY, keycode, 1); - input_sync(ir->input_dev); - } else if (ir->delay_timer_finished) { - input_event(ir->input_dev, EV_KEY, keycode, 2); - input_sync(ir->input_dev); - } - } else { - ir->delay_timer_finished = 0; - input_event(ir->input_dev, EV_KEY, keycode, 1); - input_sync(ir->input_dev); - } - - ir->last_key = keycode; - ir->last_toggle = toggle; - - ir->keyup_timer.expires = jiffies + UP_TIMEOUT; - add_timer(&ir->keyup_timer); - -} - - -/* register with input layer */ -static void input_register_keys(struct infrared *ir) -{ - int i; - - set_bit(EV_KEY, ir->input_dev->evbit); - set_bit(EV_REP, ir->input_dev->evbit); - set_bit(EV_MSC, ir->input_dev->evbit); - - set_bit(MSC_RAW, ir->input_dev->mscbit); - set_bit(MSC_SCAN, ir->input_dev->mscbit); - - memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); - - for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { - if (ir->key_map[i] > KEY_MAX) - ir->key_map[i] = 0; - else if (ir->key_map[i] > KEY_RESERVED) - set_bit(ir->key_map[i], ir->input_dev->keybit); - } - - ir->input_dev->keycode = ir->key_map; - ir->input_dev->keycodesize = sizeof(ir->key_map[0]); - ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); -} - - -/* called by the input driver after rep[REP_DELAY] ms */ -static void input_repeat_key(unsigned long parm) -{ - struct infrared *ir = (struct infrared *) parm; - - ir->delay_timer_finished = 1; -} - - -/* check for configuration changes */ -int av7110_check_ir_config(struct av7110 *av7110, int force) -{ - int i; - int modified = force; - int ret = -ENODEV; - - for (i = 0; i < av_cnt; i++) - if (av7110 == av_list[i]) - break; - - if (i < av_cnt && av7110) { - if ((av7110->ir.protocol & 1) != ir_protocol[i] || - av7110->ir.inversion != ir_inversion[i]) - modified = true; - - if (modified) { - /* protocol */ - if (ir_protocol[i]) { - ir_protocol[i] = 1; - av7110->ir.protocol = IR_RCMM; - av7110->ir.ir_config = 0x0001; - } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { - av7110->ir.protocol = IR_RC5_EXT; - av7110->ir.ir_config = 0x0002; - } else { - av7110->ir.protocol = IR_RC5; - av7110->ir.ir_config = 0x0000; - } - /* inversion */ - if (ir_inversion[i]) { - ir_inversion[i] = 1; - av7110->ir.ir_config |= 0x8000; - } - av7110->ir.inversion = ir_inversion[i]; - /* update ARM */ - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, - av7110->ir.ir_config); - } else - ret = 0; - - /* address */ - if (av7110->ir.device_mask != ir_device_mask[i]) - av7110->ir.device_mask = ir_device_mask[i]; - } - - return ret; -} - - -/* /proc/av7110_ir interface */ -static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - char *page; - u32 ir_config; - int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; - int i; - - if (count < size) - return -EINVAL; - - page = vmalloc(size); - if (!page) - return -ENOMEM; - - if (copy_from_user(page, buffer, size)) { - vfree(page); - return -EFAULT; - } - - memcpy(&ir_config, page, sizeof ir_config); - - for (i = 0; i < av_cnt; i++) { - /* keymap */ - memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, - sizeof(av_list[i]->ir.key_map)); - /* protocol, inversion, address */ - ir_protocol[i] = ir_config & 0x0001; - ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; - if (ir_config & 0x4000) - ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); - else - ir_device_mask[i] = IR_ALL; - /* update configuration */ - av7110_check_ir_config(av_list[i], false); - input_register_keys(&av_list[i]->ir); - } - vfree(page); - return count; -} - -static const struct file_operations av7110_ir_proc_fops = { - .owner = THIS_MODULE, - .write = av7110_ir_proc_write, - .llseek = noop_llseek, -}; - -/* interrupt handler */ -static void ir_handler(struct av7110 *av7110, u32 ircom) -{ - dprintk(4, "ir command = %08x\n", ircom); - av7110->ir.ir_command = ircom; - tasklet_schedule(&av7110->ir.ir_tasklet); -} - - -int __devinit av7110_ir_init(struct av7110 *av7110) -{ - struct input_dev *input_dev; - static struct proc_dir_entry *e; - int err; - - if (av_cnt >= ARRAY_SIZE(av_list)) - return -ENOSPC; - - av_list[av_cnt++] = av7110; - av7110_check_ir_config(av7110, true); - - init_timer(&av7110->ir.keyup_timer); - av7110->ir.keyup_timer.function = av7110_emit_keyup; - av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir; - - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; - - av7110->ir.input_dev = input_dev; - snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), - "pci-%s/ir0", pci_name(av7110->dev->pci)); - - input_dev->name = "DVB on-card IR receiver"; - - input_dev->phys = av7110->ir.input_phys; - input_dev->id.bustype = BUS_PCI; - input_dev->id.version = 2; - if (av7110->dev->pci->subsystem_vendor) { - input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; - input_dev->id.product = av7110->dev->pci->subsystem_device; - } else { - input_dev->id.vendor = av7110->dev->pci->vendor; - input_dev->id.product = av7110->dev->pci->device; - } - input_dev->dev.parent = &av7110->dev->pci->dev; - /* initial keymap */ - memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); - input_register_keys(&av7110->ir); - err = input_register_device(input_dev); - if (err) { - input_free_device(input_dev); - return err; - } - input_dev->timer.function = input_repeat_key; - input_dev->timer.data = (unsigned long) &av7110->ir; - - if (av_cnt == 1) { - e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops); - if (e) - e->size = 4 + 256 * sizeof(u16); - } - - tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); - av7110->ir.ir_handler = ir_handler; - - return 0; -} - - -void __devexit av7110_ir_exit(struct av7110 *av7110) -{ - int i; - - if (av_cnt == 0) - return; - - del_timer_sync(&av7110->ir.keyup_timer); - av7110->ir.ir_handler = NULL; - tasklet_kill(&av7110->ir.ir_tasklet); - - for (i = 0; i < av_cnt; i++) - if (av_list[i] == av7110) { - av_list[i] = av_list[av_cnt-1]; - av_list[av_cnt-1] = NULL; - break; - } - - if (av_cnt == 1) - remove_proc_entry("av7110_ir", NULL); - - input_unregister_device(av7110->ir.input_dev); - - av_cnt--; -} - -//MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); -//MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c deleted file mode 100644 index 1b2d15140a1d..000000000000 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ /dev/null @@ -1,966 +0,0 @@ -/* - * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * the project's page is at http://www.linuxtv.org/ - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_av.h" - -int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) -{ - u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; - struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; - - switch (av7110->adac_type) { - case DVB_ADAC_MSP34x0: - msgs.addr = 0x40; - break; - case DVB_ADAC_MSP34x5: - msgs.addr = 0x42; - break; - default: - return 0; - } - - if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) { - dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n", - av7110->dvb_adapter.num, reg, val); - return -EIO; - } - return 0; -} - -static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) -{ - u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; - u8 msg2[2]; - struct i2c_msg msgs[2] = { - { .flags = 0 , .len = 3, .buf = msg1 }, - { .flags = I2C_M_RD, .len = 2, .buf = msg2 } - }; - - switch (av7110->adac_type) { - case DVB_ADAC_MSP34x0: - msgs[0].addr = 0x40; - msgs[1].addr = 0x40; - break; - case DVB_ADAC_MSP34x5: - msgs[0].addr = 0x42; - msgs[1].addr = 0x42; - break; - default: - return 0; - } - - if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) { - dprintk(1, "dvb-ttpci: failed @ card %d, %u\n", - av7110->dvb_adapter.num, reg); - return -EIO; - } - *val = (msg2[0] << 8) | msg2[1]; - return 0; -} - -static struct v4l2_input inputs[4] = { - { - .index = 0, - .name = "DVB", - .type = V4L2_INPUT_TYPE_CAMERA, - .audioset = 1, - .tuner = 0, /* ignored */ - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - }, { - .index = 1, - .name = "Television", - .type = V4L2_INPUT_TYPE_TUNER, - .audioset = 1, - .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - }, { - .index = 2, - .name = "Video", - .type = V4L2_INPUT_TYPE_CAMERA, - .audioset = 0, - .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - }, { - .index = 3, - .name = "Y/C", - .type = V4L2_INPUT_TYPE_CAMERA, - .audioset = 0, - .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - } -}; - -static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) -{ - struct av7110 *av7110 = dev->ext_priv; - u8 buf[] = { 0x00, reg, data }; - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; - - dprintk(4, "dev: %p\n", dev); - - if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) - return -1; - return 0; -} - -static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) -{ - struct av7110 *av7110 = dev->ext_priv; - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; - - dprintk(4, "dev: %p\n", dev); - - if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) - return -1; - return 0; -} - -static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) -{ - u32 div; - u8 config; - u8 buf[4]; - - dprintk(4, "freq: 0x%08x\n", freq); - - /* magic number: 614. tuning with the frequency given by v4l2 - is always off by 614*62.5 = 38375 kHz...*/ - div = freq + 614; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x8e; - - if (freq < (u32) (16 * 168.25)) - config = 0xa0; - else if (freq < (u32) (16 * 447.25)) - config = 0x90; - else - config = 0x30; - config &= ~0x02; - - buf[3] = config; - - return tuner_write(dev, 0x61, buf); -} - -static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) -{ - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; - u32 div; - u8 data[4]; - - div = (freq + 38900000 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0xce; - - if (freq < 45000000) - return -EINVAL; - else if (freq < 137000000) - data[3] = 0x01; - else if (freq < 403000000) - data[3] = 0x02; - else if (freq < 860000000) - data[3] = 0x04; - else - return -EINVAL; - - if (av7110->fe->ops.i2c_gate_ctrl) - av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); - return tuner_write(dev, 0x63, data); -} - - - -static struct saa7146_standard analog_standard[]; -static struct saa7146_standard dvb_standard[]; -static struct saa7146_standard standard[]; - -static struct v4l2_audio msp3400_v4l2_audio = { - .index = 0, - .name = "Television", - .capability = V4L2_AUDCAP_STEREO -}; - -static int av7110_dvb_c_switch(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; - u16 adswitch; - int source, sync, err; - - dprintk(4, "%p\n", av7110); - - 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 (err != 0) { - dprintk(2, "suspending video failed\n"); - vv->ov_suspend = NULL; - } - } - - if (0 != av7110->current_input) { - dprintk(1, "switching to analog TV:\n"); - adswitch = 1; - source = SAA7146_HPS_SOURCE_PORT_B; - sync = SAA7146_HPS_SYNC_PORT_B; - memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); - - switch (av7110->current_input) { - case 1: - dprintk(1, "switching SAA7113 to Analog Tuner Input\n"); - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source - msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source - msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source - msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume - - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { - if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) - dprintk(1, "setting band in demodulator failed\n"); - } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD) - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF) - } - if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1) - dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); - break; - case 2: - dprintk(1, "switching SAA7113 to Video AV CVBS Input\n"); - if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1) - dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); - break; - case 3: - dprintk(1, "switching SAA7113 to Video AV Y/C Input\n"); - if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1) - dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); - break; - default: - dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n"); - } - } else { - adswitch = 0; - source = SAA7146_HPS_SOURCE_PORT_A; - sync = SAA7146_HPS_SYNC_PORT_A; - memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); - dprintk(1, "switching DVB mode\n"); - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source - msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source - msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source - msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume - - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { - if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) - dprintk(1, "setting band in demodulator failed\n"); - } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) - } - } - - /* hmm, this does not do anything!? */ - if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) - dprintk(1, "ADSwitch error\n"); - - saa7146_set_hps_source_and_sync(dev, source, sync); - - if (vv->ov_suspend != NULL) { - saa7146_start_preview(vv->ov_suspend); - vv->ov_suspend = NULL; - } - - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - u16 stereo_det; - s8 stereo; - - dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); - - if (!av7110->analog_tuner_flags || t->index != 0) - return -EINVAL; - - memset(t, 0, sizeof(*t)); - strcpy((char *)t->name, "Television"); - - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ - t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ - /* FIXME: add the real signal strength here */ - t->signal = 0xffff; - t->afc = 0; - - /* FIXME: standard / stereo detection is still broken */ - msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det); - dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det); - msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det); - dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det); - stereo = (s8)(stereo_det >> 8); - if (stereo > 0x10) { - /* stereo */ - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - t->audmode = V4L2_TUNER_MODE_STEREO; - } else if (stereo < -0x10) { - /* bilingual */ - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - t->audmode = V4L2_TUNER_MODE_LANG1; - } else /* mono */ - t->rxsubchans = V4L2_TUNER_SUB_MONO; - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - u16 fm_matrix, src; - dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); - - if (!av7110->analog_tuner_flags || av7110->current_input != 1) - return -EINVAL; - - switch (t->audmode) { - case V4L2_TUNER_MODE_STEREO: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"); - fm_matrix = 0x3001; /* stereo */ - src = 0x0020; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"); - fm_matrix = 0x3000; /* bilingual */ - src = 0x0020; - break; - case V4L2_TUNER_MODE_LANG1: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"); - fm_matrix = 0x3000; /* mono */ - src = 0x0000; - break; - case V4L2_TUNER_MODE_LANG2: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"); - fm_matrix = 0x3000; /* mono */ - src = 0x0010; - break; - default: /* case V4L2_TUNER_MODE_MONO: */ - dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n"); - fm_matrix = 0x3000; /* mono */ - src = 0x0030; - break; - } - msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix); - msp_writereg(av7110, MSP_WR_DSP, 0x0008, src); - msp_writereg(av7110, MSP_WR_DSP, 0x0009, src); - msp_writereg(av7110, MSP_WR_DSP, 0x000a, src); - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency); - - if (!av7110->analog_tuner_flags || av7110->current_input != 1) - return -EINVAL; - - memset(f, 0, sizeof(*f)); - f->type = V4L2_TUNER_ANALOG_TV; - f->frequency = av7110->current_freq; - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency); - - if (!av7110->analog_tuner_flags || av7110->current_input != 1) - return -EINVAL; - - if (V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; - - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */ - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); - - /* tune in desired frequency */ - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) - ves1820_set_tv_freq(dev, f->frequency); - else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) - stv0297_set_tv_freq(dev, f->frequency); - av7110->current_freq = f->frequency; - - msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */ - msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */ - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */ - return 0; -} - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); - - if (av7110->analog_tuner_flags) { - if (i->index >= 4) - return -EINVAL; - } else { - if (i->index != 0) - return -EINVAL; - } - - memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); - - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - *input = av7110->current_input; - dprintk(2, "VIDIOC_G_INPUT: %d\n", *input); - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_INPUT: %d\n", input); - - if (!av7110->analog_tuner_flags) - return input ? -EINVAL : 0; - - if (input >= 4) - return -EINVAL; - - av7110->current_input = input; - return av7110_dvb_c_switch(fh); -} - -static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) -{ - dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); - if (a->index != 0) - return -EINVAL; - *a = msp3400_v4l2_audio; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); - if (a->index != 0) - return -EINVAL; - if (av7110->current_input >= 2) - return -EINVAL; - *a = msp3400_v4l2_audio; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); - if (av7110->current_input >= 2) - return -EINVAL; - return a->index ? -EINVAL : 0; -} - -static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, - struct v4l2_sliced_vbi_cap *cap) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n"); - if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) - return -EINVAL; - if (FW_VERSION(av7110->arm_app) >= 0x2623) { - cap->service_set = V4L2_SLICED_WSS_625; - cap->service_lines[0][23] = V4L2_SLICED_WSS_625; - } - return 0; -} - -static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_FMT:\n"); - if (FW_VERSION(av7110->arm_app) < 0x2623) - return -EINVAL; - memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); - if (av7110->wssMode) { - f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; - f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; - f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); - } - return 0; -} - -static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_FMT\n"); - if (FW_VERSION(av7110->arm_app) < 0x2623) - return -EINVAL; - if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 && - f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) { - memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); - /* WSS controlled by firmware */ - av7110->wssMode = 0; - av7110->wssData = 0; - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, - SetWSSConfig, 1, 0); - } else { - memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); - f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; - f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; - f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); - /* WSS controlled by userspace */ - av7110->wssMode = 1; - av7110->wssData = 0; - } - return 0; -} - -static int av7110_vbi_reset(struct file *file) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; - - dprintk(2, "%s\n", __func__); - av7110->wssMode = 0; - av7110->wssData = 0; - if (FW_VERSION(av7110->arm_app) < 0x2623) - return 0; - else - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0); -} - -static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; - struct v4l2_sliced_vbi_data d; - int rc; - - dprintk(2, "%s\n", __func__); - if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) - return -EINVAL; - if (copy_from_user(&d, data, count)) - return -EFAULT; - if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) - return -EINVAL; - if (d.id) - av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; - else - av7110->wssData = 0x8000; - rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); - return (rc < 0) ? rc : count; -} - -/**************************************************************************** - * INITIALIZATION - ****************************************************************************/ - -static u8 saa7113_init_regs[] = { - 0x02, 0xd0, - 0x03, 0x23, - 0x04, 0x00, - 0x05, 0x00, - 0x06, 0xe9, - 0x07, 0x0d, - 0x08, 0x98, - 0x09, 0x02, - 0x0a, 0x80, - 0x0b, 0x40, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x01, - 0x0f, 0x7c, - 0x10, 0x48, - 0x11, 0x0c, - 0x12, 0x8b, - 0x13, 0x1a, - 0x14, 0x00, - 0x15, 0x00, - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - 0x1b, 0x00, - 0x1c, 0x00, - 0x1d, 0x00, - 0x1e, 0x00, - - 0x41, 0x77, - 0x42, 0x77, - 0x43, 0x77, - 0x44, 0x77, - 0x45, 0x77, - 0x46, 0x77, - 0x47, 0x77, - 0x48, 0x77, - 0x49, 0x77, - 0x4a, 0x77, - 0x4b, 0x77, - 0x4c, 0x77, - 0x4d, 0x77, - 0x4e, 0x77, - 0x4f, 0x77, - 0x50, 0x77, - 0x51, 0x77, - 0x52, 0x77, - 0x53, 0x77, - 0x54, 0x77, - 0x55, 0x77, - 0x56, 0x77, - 0x57, 0xff, - - 0xff -}; - - -static struct saa7146_ext_vv av7110_vv_data_st; -static struct saa7146_ext_vv av7110_vv_data_c; - -int av7110_init_analog_module(struct av7110 *av7110) -{ - u16 version1, version2; - - if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 && - i2c_writereg(av7110, 0x80, 0x0, 0) == 1) { - pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_MSP34x0; - } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 && - i2c_writereg(av7110, 0x84, 0x0, 0) == 1) { - pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_MSP34x5; - } else - return -ENODEV; - - msleep(100); // the probing above resets the msp... - msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); - msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); - dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", - av7110->dvb_adapter.num, version1, version2); - msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source - msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source - msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume - msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume - msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART - - if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { - pr_info("saa7113 not accessible\n"); - } else { - u8 *i = saa7113_init_regs; - - if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { - /* Fujitsu/Siemens DVB-Cable */ - av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; - } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { - /* Hauppauge/TT DVB-C premium */ - av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; - } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { - /* Hauppauge/TT DVB-C premium */ - av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; - } - - /* setup for DVB by default */ - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { - if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) - dprintk(1, "setting band in demodulator failed\n"); - } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { - saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) - saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) - } - - /* init the saa7113 */ - while (*i != 0xff) { - if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { - dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num); - break; - } - i += 2; - } - /* setup msp for analog sound: B/G Dual-FM */ - msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG - msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz - msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI - msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz - msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI - msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2 - } - - memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); - /* set dd1 stream a & b */ - saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); - saa7146_write(av7110->dev, DD1_INIT, 0x03000700); - saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - return 0; -} - -int av7110_init_v4l(struct av7110 *av7110) -{ - struct saa7146_dev* dev = av7110->dev; - struct saa7146_ext_vv *vv_data; - int ret; - - /* special case DVB-C: these cards have an analog tuner - plus need some special handling, so we have separate - saa7146_ext_vv data for these... */ - if (av7110->analog_tuner_flags) - vv_data = &av7110_vv_data_c; - else - vv_data = &av7110_vv_data_st; - ret = saa7146_vv_init(dev, vv_data); - - if (ret) { - ERR("cannot init capture device. skipping\n"); - return -ENODEV; - } - vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; - vv_data->vid_ops.vidioc_g_input = vidioc_g_input; - vv_data->vid_ops.vidioc_s_input = vidioc_s_input; - vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; - vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; - vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; - vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; - - vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; - vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; - vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; - vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; - - if (FW_VERSION(av7110->arm_app) < 0x2623) - vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; - - if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { - ERR("cannot register capture device. skipping\n"); - saa7146_vv_release(dev); - return -ENODEV; - } - if (FW_VERSION(av7110->arm_app) >= 0x2623) { - if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) - ERR("cannot register vbi v4l2 device. skipping\n"); - } - return 0; -} - -int av7110_exit_v4l(struct av7110 *av7110) -{ - struct saa7146_dev* dev = av7110->dev; - - saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); - saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); - - saa7146_vv_release(dev); - - return 0; -} - - - -/* FIXME: these values are experimental values that look better than the - values from the latest "official" driver -- at least for me... (MiHu) */ -static struct saa7146_standard standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL_BG, - .v_offset = 0x15, .v_field = 288, - .h_offset = 0x48, .h_pixels = 708, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x10, .v_field = 244, - .h_offset = 0x40, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - } -}; - -static struct saa7146_standard analog_standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL_BG, - .v_offset = 0x1b, .v_field = 288, - .h_offset = 0x08, .h_pixels = 708, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x10, .v_field = 244, - .h_offset = 0x40, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - } -}; - -static struct saa7146_standard dvb_standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL_BG, - .v_offset = 0x14, .v_field = 288, - .h_offset = 0x48, .h_pixels = 708, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x10, .v_field = 244, - .h_offset = 0x40, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - } -}; - -static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) -{ - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; - - if (std->id & V4L2_STD_PAL) { - av7110->vidmode = AV7110_VIDEO_MODE_PAL; - av7110_set_vidmode(av7110, av7110->vidmode); - } - else if (std->id & V4L2_STD_NTSC) { - av7110->vidmode = AV7110_VIDEO_MODE_NTSC; - av7110_set_vidmode(av7110, av7110->vidmode); - } - else - return -1; - - return 0; -} - - -static struct saa7146_ext_vv av7110_vv_data_st = { - .inputs = 1, - .audios = 1, - .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, - .flags = 0, - - .stds = &standard[0], - .num_stds = ARRAY_SIZE(standard), - .std_callback = &std_callback, - - .vbi_fops.open = av7110_vbi_reset, - .vbi_fops.release = av7110_vbi_reset, - .vbi_fops.write = av7110_vbi_write, -}; - -static struct saa7146_ext_vv av7110_vv_data_c = { - .inputs = 1, - .audios = 1, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, - .flags = SAA7146_USE_PORT_B_FOR_VBI, - - .stds = &standard[0], - .num_stds = ARRAY_SIZE(standard), - .std_callback = &std_callback, - - .vbi_fops.open = av7110_vbi_reset, - .vbi_fops.release = av7110_vbi_reset, - .vbi_fops.write = av7110_vbi_write, -}; - diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c deleted file mode 100644 index 12ddb53c58dc..000000000000 --- a/drivers/media/dvb/ttpci/budget-av.c +++ /dev/null @@ -1,1640 +0,0 @@ -/* - * budget-av.c: driver for the SAA7146 based Budget DVB cards - * with analog video in - * - * Compiled from various sources by Michael Hunold - * - * CI interface support (c) 2004 Olivier Gournet & - * Andrew de Quincey - * - * Copyright (C) 2002 Ralph Metzler - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "budget.h" -#include "stv0299.h" -#include "stb0899_drv.h" -#include "stb0899_reg.h" -#include "stb0899_cfg.h" -#include "tda8261.h" -#include "tda8261_cfg.h" -#include "tda1002x.h" -#include "tda1004x.h" -#include "tua6100.h" -#include "dvb-pll.h" -#include -#include -#include -#include -#include -#include -#include - -#include "dvb_ca_en50221.h" - -#define DEBICICAM 0x02420000 - -#define SLOTSTATUS_NONE 1 -#define SLOTSTATUS_PRESENT 2 -#define SLOTSTATUS_RESET 4 -#define SLOTSTATUS_READY 8 -#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -struct budget_av { - struct budget budget; - struct video_device *vd; - int cur_input; - int has_saa7113; - struct tasklet_struct ciintf_irq_tasklet; - int slot_status; - struct dvb_ca_en50221 ca; - u8 reinitialise_demod:1; -}; - -static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); - - -/* GPIO Connections: - * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! - * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory - * 2 - CI Card Enable (Active Low) - * 3 - CI Card Detect - */ - -/**************************************************************************** - * INITIALIZATION - ****************************************************************************/ - -static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg) -{ - u8 mm1[] = { 0x00 }; - u8 mm2[] = { 0x00 }; - struct i2c_msg msgs[2]; - - msgs[0].flags = 0; - msgs[1].flags = I2C_M_RD; - msgs[0].addr = msgs[1].addr = id / 2; - mm1[0] = reg; - msgs[0].len = 1; - msgs[1].len = 1; - msgs[0].buf = mm1; - msgs[1].buf = mm2; - - i2c_transfer(i2c, msgs, 2); - - return mm2[0]; -} - -static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len) -{ - u8 mm1[] = { reg }; - struct i2c_msg msgs[2] = { - {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, - {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} - }; - - if (i2c_transfer(i2c, msgs, 2) != 2) - return -EIO; - - return 0; -} - -static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) -{ - u8 msg[2] = { reg, val }; - struct i2c_msg msgs; - - msgs.flags = 0; - msgs.addr = id / 2; - msgs.len = 2; - msgs.buf = msg; - return i2c_transfer(i2c, &msgs, 1); -} - -static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); - udelay(1); - - result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 1\n"); - } - return result; -} - -static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); - udelay(1); - - result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 2\n"); - } - return result; -} - -static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - udelay(1); - - result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 3\n"); - return -ETIMEDOUT; - } - return result; -} - -static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - udelay(1); - - result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 5\n"); - } - return result; -} - -static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - - if (slot != 0) - return -EINVAL; - - dprintk(1, "ciintf_slot_reset\n"); - budget_av->slot_status = SLOTSTATUS_RESET; - - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ - - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ - msleep(2); - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ - msleep(20); /* 20 ms Vcc settling time */ - - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - msleep(20); - - /* reinitialise the frontend if necessary */ - if (budget_av->reinitialise_demod) - dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); - - return 0; -} - -static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - - if (slot != 0) - return -EINVAL; - - dprintk(1, "ciintf_slot_shutdown\n"); - - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - budget_av->slot_status = SLOTSTATUS_NONE; - - return 0; -} - -static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - - if (slot != 0) - return -EINVAL; - - dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); - - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); - - return 0; -} - -static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - int result; - - if (slot != 0) - return -EINVAL; - - /* test the card detect line - needs to be done carefully - * since it never goes high for some CAMs on this interface (e.g. topuptv) */ - if (budget_av->slot_status == SLOTSTATUS_NONE) { - saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - udelay(1); - if (saa7146_read(saa, PSR) & MASK_06) { - if (budget_av->slot_status == SLOTSTATUS_NONE) { - budget_av->slot_status = SLOTSTATUS_PRESENT; - pr_info("cam inserted A\n"); - } - } - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); - } - - /* We also try and read from IO memory to work round the above detection bug. If - * there is no CAM, we will get a timeout. Only done if there is no cam - * present, since this test actually breaks some cams :( - * - * if the CI interface is not open, we also do the above test since we - * don't care if the cam has problems - we'll be resetting it on open() anyway */ - if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); - if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { - budget_av->slot_status = SLOTSTATUS_PRESENT; - pr_info("cam inserted B\n"); - } else if (result < 0) { - if (budget_av->slot_status != SLOTSTATUS_NONE) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 5\n"); - return 0; - } - } - } - - /* read from attribute memory in reset/ready state to know when the CAM is ready */ - if (budget_av->slot_status == SLOTSTATUS_RESET) { - result = ciintf_read_attribute_mem(ca, slot, 0); - if (result == 0x1d) { - budget_av->slot_status = SLOTSTATUS_READY; - } - } - - /* work out correct return code */ - if (budget_av->slot_status != SLOTSTATUS_NONE) { - if (budget_av->slot_status & SLOTSTATUS_READY) { - return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; - } - return DVB_CA_EN50221_POLL_CAM_PRESENT; - } - return 0; -} - -static int ciintf_init(struct budget_av *budget_av) -{ - struct saa7146_dev *saa = budget_av->budget.dev; - int result; - - memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); - - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); - - /* Enable DEBI pins */ - saa7146_write(saa, MC1, MASK_27 | MASK_11); - - /* register CI interface */ - budget_av->ca.owner = THIS_MODULE; - budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; - budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; - budget_av->ca.read_cam_control = ciintf_read_cam_control; - budget_av->ca.write_cam_control = ciintf_write_cam_control; - budget_av->ca.slot_reset = ciintf_slot_reset; - budget_av->ca.slot_shutdown = ciintf_slot_shutdown; - budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; - budget_av->ca.poll_slot_status = ciintf_poll_slot_status; - budget_av->ca.data = budget_av; - budget_av->budget.ci_present = 1; - budget_av->slot_status = SLOTSTATUS_NONE; - - if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, - &budget_av->ca, 0, 1)) != 0) { - pr_err("ci initialisation failed\n"); - goto error; - } - - pr_info("ci interface initialised\n"); - return 0; - -error: - saa7146_write(saa, MC1, MASK_27); - return result; -} - -static void ciintf_deinit(struct budget_av *budget_av) -{ - struct saa7146_dev *saa = budget_av->budget.dev; - - saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); - saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); - saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); - saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - - /* release the CA device */ - dvb_ca_en50221_release(&budget_av->ca); - - /* disable DEBI pins */ - saa7146_write(saa, MC1, MASK_27); -} - - -static const u8 saa7113_tab[] = { - 0x01, 0x08, - 0x02, 0xc0, - 0x03, 0x33, - 0x04, 0x00, - 0x05, 0x00, - 0x06, 0xeb, - 0x07, 0xe0, - 0x08, 0x28, - 0x09, 0x00, - 0x0a, 0x80, - 0x0b, 0x47, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x01, - 0x0f, 0x44, - - 0x10, 0x08, - 0x11, 0x0c, - 0x12, 0x7b, - 0x13, 0x00, - 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, - - 0x57, 0xff, - 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, - 0x5b, 0x83, 0x5e, 0x00, - 0xff -}; - -static int saa7113_init(struct budget_av *budget_av) -{ - struct budget *budget = &budget_av->budget; - struct saa7146_dev *saa = budget->dev; - const u8 *data = saa7113_tab; - - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); - msleep(200); - - if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { - dprintk(1, "saa7113 not found on KNC card\n"); - return -ENODEV; - } - - dprintk(1, "saa7113 detected and initializing\n"); - - while (*data != 0xff) { - i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); - data += 2; - } - - dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); - - return 0; -} - -static int saa7113_setinput(struct budget_av *budget_av, int input) -{ - struct budget *budget = &budget_av->budget; - - if (1 != budget_av->has_saa7113) - return -ENODEV; - - if (input == 1) { - i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7); - i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80); - } else if (input == 0) { - i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0); - i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00); - } else - return -EINVAL; - - budget_av->cur_input = input; - return 0; -} - - -static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) -{ - u8 aclk = 0; - u8 bclk = 0; - u8 m1; - - aclk = 0xb5; - if (srate < 2000000) - bclk = 0x86; - else if (srate < 5000000) - bclk = 0x89; - else if (srate < 15000000) - bclk = 0x8f; - else if (srate < 45000000) - bclk = 0x95; - - m1 = 0x14; - if (srate < 4000000) - m1 = 0x10; - - stv0299_writereg(fe, 0x13, aclk); - stv0299_writereg(fe, 0x14, bclk); - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, (ratio) & 0xf0); - stv0299_writereg(fe, 0x0f, 0x80 | m1); - - return 0; -} - -static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 div; - u8 buf[4]; - struct budget *budget = (struct budget *) fe->dvb->priv; - struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; - - if ((c->frequency < 950000) || (c->frequency > 2150000)) - return -EINVAL; - - div = (c->frequency + (125 - 1)) / 125; /* round correctly */ - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; - buf[3] = 0x20; - - if (c->symbol_rate < 4000000) - buf[3] |= 1; - - if (c->frequency < 1250000) - buf[3] |= 0; - else if (c->frequency < 1550000) - buf[3] |= 0x40; - else if (c->frequency < 2050000) - buf[3] |= 0x80; - else if (c->frequency < 2150000) - buf[3] |= 0xC0; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static u8 typhoon_cinergy1200s_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ - 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ - 0x06, 0x40, /* DAC not used, set to high impendance mode */ - 0x07, 0x00, /* DAC LSB */ - 0x08, 0x40, /* DiSEqC off */ - 0x09, 0x00, /* FIFO */ - 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ - 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ - 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ - 0x10, 0x3f, // AGC2 0x3d - 0x11, 0x84, - 0x12, 0xb9, - 0x15, 0xc9, // lock detector threshold - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 - 0x29, 0x1e, // 1/2 threshold - 0x2a, 0x14, // 2/3 threshold - 0x2b, 0x0f, // 3/4 threshold - 0x2c, 0x09, // 5/6 threshold - 0x2d, 0x05, // 7/8 threshold - 0x2e, 0x01, - 0x31, 0x1f, // test all FECs - 0x32, 0x19, // viterbi and synchro search - 0x33, 0xfc, // rs control - 0x34, 0x93, // error control - 0x0f, 0x92, - 0xff, 0xff -}; - -static struct stv0299_config typhoon_config = { - .demod_address = 0x68, - .inittab = typhoon_cinergy1200s_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, -}; - - -static struct stv0299_config cinergy_1200s_config = { - .demod_address = 0x68, - .inittab = typhoon_cinergy1200s_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_0, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, -}; - -static struct stv0299_config cinergy_1200s_1894_0010_config = { - .demod_address = 0x68, - .inittab = typhoon_cinergy1200s_inittab, - .mclk = 88000000UL, - .invert = 1, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, -}; - -static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = (struct budget *) fe->dvb->priv; - u8 buf[6]; - struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; - int i; - -#define CU1216_IF 36125000 -#define TUNER_MUL 62500 - - u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0xce; - buf[3] = (c->frequency < 150000000 ? 0x01 : - c->frequency < 445000000 ? 0x02 : 0x04); - buf[4] = 0xde; - buf[5] = 0x20; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - - /* wait for the pll lock */ - msg.flags = I2C_M_RD; - msg.len = 1; - for (i = 0; i < 20; i++) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) - break; - msleep(10); - } - - /* switch the charge pump to the lower current */ - msg.flags = 0; - msg.len = 2; - msg.buf = &buf[2]; - buf[2] &= ~0x40; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - - return 0; -} - -static struct tda1002x_config philips_cu1216_config = { - .demod_address = 0x0c, - .invert = 1, -}; - -static struct tda1002x_config philips_cu1216_config_altaddress = { - .demod_address = 0x0d, - .invert = 0, -}; - -static struct tda10023_config philips_cu1216_tda10023_config = { - .demod_address = 0x0c, - .invert = 1, -}; - -static int philips_tu1216_tuner_init(struct dvb_frontend *fe) -{ - struct budget *budget = (struct budget *) fe->dvb->priv; - static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; - - // setup PLL configuration - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - msleep(1); - - return 0; -} - -static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = (struct budget *) fe->dvb->priv; - u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = - sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - - // determine charge pump - tuner_frequency = c->frequency + 36166000; - if (tuner_frequency < 87000000) - return -EINVAL; - else if (tuner_frequency < 130000000) - cp = 3; - else if (tuner_frequency < 160000000) - cp = 5; - else if (tuner_frequency < 200000000) - cp = 6; - else if (tuner_frequency < 290000000) - cp = 3; - else if (tuner_frequency < 420000000) - cp = 5; - else if (tuner_frequency < 480000000) - cp = 6; - else if (tuner_frequency < 620000000) - cp = 3; - else if (tuner_frequency < 830000000) - cp = 5; - else if (tuner_frequency < 895000000) - cp = 7; - else - return -EINVAL; - - // determine band - if (c->frequency < 49000000) - return -EINVAL; - else if (c->frequency < 161000000) - band = 1; - else if (c->frequency < 444000000) - band = 2; - else if (c->frequency < 861000000) - band = 4; - else - return -EINVAL; - - // setup PLL filter - switch (c->bandwidth_hz) { - case 6000000: - filter = 0; - break; - - case 7000000: - filter = 0; - break; - - case 8000000: - filter = 1; - break; - - default: - return -EINVAL; - } - - // calculate divisor - // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; - - // setup tuner buffer - tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xca; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(1); - return 0; -} - -static int philips_tu1216_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct budget *budget = (struct budget *) fe->dvb->priv; - - return request_firmware(fw, name, &budget->dev->pci->dev); -} - -static struct tda1004x_config philips_tu1216_config = { - - .demod_address = 0x8, - .invert = 1, - .invert_oclk = 1, - .xtal_freq = TDA10046_XTAL_4M, - .agc_config = TDA10046_AGC_DEFAULT, - .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tu1216_request_firmware, -}; - -static u8 philips_sd1878_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x7d, - 0x05, 0x35, - 0x06, 0x40, - 0x07, 0x00, - 0x08, 0x43, - 0x09, 0x02, - 0x0C, 0x51, - 0x0D, 0x82, - 0x0E, 0x23, - 0x10, 0x3f, - 0x11, 0x84, - 0x12, 0xb9, - 0x15, 0xc9, - 0x16, 0x19, - 0x17, 0x8c, - 0x18, 0x59, - 0x19, 0xf8, - 0x1a, 0xfe, - 0x1c, 0x7f, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, - 0x29, 0x28, - 0x2a, 0x14, - 0x2b, 0x0f, - 0x2c, 0x09, - 0x2d, 0x09, - 0x31, 0x1f, - 0x32, 0x19, - 0x33, 0xfc, - 0x34, 0x93, - 0xff, 0xff -}; - -static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, - u32 srate, u32 ratio) -{ - u8 aclk = 0; - u8 bclk = 0; - u8 m1; - - aclk = 0xb5; - if (srate < 2000000) - bclk = 0x86; - else if (srate < 5000000) - bclk = 0x89; - else if (srate < 15000000) - bclk = 0x8f; - else if (srate < 45000000) - bclk = 0x95; - - m1 = 0x14; - if (srate < 4000000) - m1 = 0x10; - - stv0299_writereg(fe, 0x0e, 0x23); - stv0299_writereg(fe, 0x0f, 0x94); - stv0299_writereg(fe, 0x10, 0x39); - stv0299_writereg(fe, 0x13, aclk); - stv0299_writereg(fe, 0x14, bclk); - stv0299_writereg(fe, 0x15, 0xc9); - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, (ratio) & 0xf0); - stv0299_writereg(fe, 0x0f, 0x80 | m1); - - return 0; -} - -static struct stv0299_config philips_sd1878_config = { - .demod_address = 0x68, - .inittab = philips_sd1878_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, -}; - -/* KNC1 DVB-S (STB0899) Inittab */ -static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { - - { STB0899_DEV_ID , 0x81 }, - { STB0899_DISCNTRL1 , 0x32 }, - { STB0899_DISCNTRL2 , 0x80 }, - { STB0899_DISRX_ST0 , 0x04 }, - { STB0899_DISRX_ST1 , 0x00 }, - { STB0899_DISPARITY , 0x00 }, - { STB0899_DISSTATUS , 0x20 }, - { STB0899_DISF22 , 0x8c }, - { STB0899_DISF22RX , 0x9a }, - { STB0899_SYSREG , 0x0b }, - { STB0899_ACRPRESC , 0x11 }, - { STB0899_ACRDIV1 , 0x0a }, - { STB0899_ACRDIV2 , 0x05 }, - { STB0899_DACR1 , 0x00 }, - { STB0899_DACR2 , 0x00 }, - { STB0899_OUTCFG , 0x00 }, - { STB0899_MODECFG , 0x00 }, - { STB0899_IRQSTATUS_3 , 0x30 }, - { STB0899_IRQSTATUS_2 , 0x00 }, - { STB0899_IRQSTATUS_1 , 0x00 }, - { STB0899_IRQSTATUS_0 , 0x00 }, - { STB0899_IRQMSK_3 , 0xf3 }, - { STB0899_IRQMSK_2 , 0xfc }, - { STB0899_IRQMSK_1 , 0xff }, - { STB0899_IRQMSK_0 , 0xff }, - { STB0899_IRQCFG , 0x00 }, - { STB0899_I2CCFG , 0x88 }, - { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ - { STB0899_IOPVALUE5 , 0x00 }, - { STB0899_IOPVALUE4 , 0x20 }, - { STB0899_IOPVALUE3 , 0xc9 }, - { STB0899_IOPVALUE2 , 0x90 }, - { STB0899_IOPVALUE1 , 0x40 }, - { STB0899_IOPVALUE0 , 0x00 }, - { STB0899_GPIO00CFG , 0x82 }, - { STB0899_GPIO01CFG , 0x82 }, - { STB0899_GPIO02CFG , 0x82 }, - { STB0899_GPIO03CFG , 0x82 }, - { STB0899_GPIO04CFG , 0x82 }, - { STB0899_GPIO05CFG , 0x82 }, - { STB0899_GPIO06CFG , 0x82 }, - { STB0899_GPIO07CFG , 0x82 }, - { STB0899_GPIO08CFG , 0x82 }, - { STB0899_GPIO09CFG , 0x82 }, - { STB0899_GPIO10CFG , 0x82 }, - { STB0899_GPIO11CFG , 0x82 }, - { STB0899_GPIO12CFG , 0x82 }, - { STB0899_GPIO13CFG , 0x82 }, - { STB0899_GPIO14CFG , 0x82 }, - { STB0899_GPIO15CFG , 0x82 }, - { STB0899_GPIO16CFG , 0x82 }, - { STB0899_GPIO17CFG , 0x82 }, - { STB0899_GPIO18CFG , 0x82 }, - { STB0899_GPIO19CFG , 0x82 }, - { STB0899_GPIO20CFG , 0x82 }, - { STB0899_SDATCFG , 0xb8 }, - { STB0899_SCLTCFG , 0xba }, - { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ - { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ - { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ - { STB0899_DIRCLKCFG , 0x82 }, - { STB0899_CLKOUT27CFG , 0x7e }, - { STB0899_STDBYCFG , 0x82 }, - { STB0899_CS0CFG , 0x82 }, - { STB0899_CS1CFG , 0x82 }, - { STB0899_DISEQCOCFG , 0x20 }, - { STB0899_GPIO32CFG , 0x82 }, - { STB0899_GPIO33CFG , 0x82 }, - { STB0899_GPIO34CFG , 0x82 }, - { STB0899_GPIO35CFG , 0x82 }, - { STB0899_GPIO36CFG , 0x82 }, - { STB0899_GPIO37CFG , 0x82 }, - { STB0899_GPIO38CFG , 0x82 }, - { STB0899_GPIO39CFG , 0x82 }, - { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ - { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ - { STB0899_FILTCTRL , 0x00 }, - { STB0899_SYSCTRL , 0x00 }, - { STB0899_STOPCLK1 , 0x20 }, - { STB0899_STOPCLK2 , 0x00 }, - { STB0899_INTBUFSTATUS , 0x00 }, - { STB0899_INTBUFCTRL , 0x0a }, - { 0xffff , 0xff }, -}; - -static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { - { STB0899_DEMOD , 0x00 }, - { STB0899_RCOMPC , 0xc9 }, - { STB0899_AGC1CN , 0x41 }, - { STB0899_AGC1REF , 0x08 }, - { STB0899_RTC , 0x7a }, - { STB0899_TMGCFG , 0x4e }, - { STB0899_AGC2REF , 0x33 }, - { STB0899_TLSR , 0x84 }, - { STB0899_CFD , 0xee }, - { STB0899_ACLC , 0x87 }, - { STB0899_BCLC , 0x94 }, - { STB0899_EQON , 0x41 }, - { STB0899_LDT , 0xdd }, - { STB0899_LDT2 , 0xc9 }, - { STB0899_EQUALREF , 0xb4 }, - { STB0899_TMGRAMP , 0x10 }, - { STB0899_TMGTHD , 0x30 }, - { STB0899_IDCCOMP , 0xfb }, - { STB0899_QDCCOMP , 0x03 }, - { STB0899_POWERI , 0x3b }, - { STB0899_POWERQ , 0x3d }, - { STB0899_RCOMP , 0x81 }, - { STB0899_AGCIQIN , 0x80 }, - { STB0899_AGC2I1 , 0x04 }, - { STB0899_AGC2I2 , 0xf5 }, - { STB0899_TLIR , 0x25 }, - { STB0899_RTF , 0x80 }, - { STB0899_DSTATUS , 0x00 }, - { STB0899_LDI , 0xca }, - { STB0899_CFRM , 0xf1 }, - { STB0899_CFRL , 0xf3 }, - { STB0899_NIRM , 0x2a }, - { STB0899_NIRL , 0x05 }, - { STB0899_ISYMB , 0x17 }, - { STB0899_QSYMB , 0xfa }, - { STB0899_SFRH , 0x2f }, - { STB0899_SFRM , 0x68 }, - { STB0899_SFRL , 0x40 }, - { STB0899_SFRUPH , 0x2f }, - { STB0899_SFRUPM , 0x68 }, - { STB0899_SFRUPL , 0x40 }, - { STB0899_EQUAI1 , 0xfd }, - { STB0899_EQUAQ1 , 0x04 }, - { STB0899_EQUAI2 , 0x0f }, - { STB0899_EQUAQ2 , 0xff }, - { STB0899_EQUAI3 , 0xdf }, - { STB0899_EQUAQ3 , 0xfa }, - { STB0899_EQUAI4 , 0x37 }, - { STB0899_EQUAQ4 , 0x0d }, - { STB0899_EQUAI5 , 0xbd }, - { STB0899_EQUAQ5 , 0xf7 }, - { STB0899_DSTATUS2 , 0x00 }, - { STB0899_VSTATUS , 0x00 }, - { STB0899_VERROR , 0xff }, - { STB0899_IQSWAP , 0x2a }, - { STB0899_ECNT1M , 0x00 }, - { STB0899_ECNT1L , 0x00 }, - { STB0899_ECNT2M , 0x00 }, - { STB0899_ECNT2L , 0x00 }, - { STB0899_ECNT3M , 0x00 }, - { STB0899_ECNT3L , 0x00 }, - { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, - { STB0899_VTH12 , 0xf0 }, - { STB0899_VTH23 , 0xa0 }, - { STB0899_VTH34 , 0x78 }, - { STB0899_VTH56 , 0x4e }, - { STB0899_VTH67 , 0x48 }, - { STB0899_VTH78 , 0x38 }, - { STB0899_PRVIT , 0xff }, - { STB0899_VITSYNC , 0x19 }, - { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ - { STB0899_TSULC , 0x42 }, - { STB0899_RSLLC , 0x40 }, - { STB0899_TSLPL , 0x12 }, - { STB0899_TSCFGH , 0x0c }, - { STB0899_TSCFGM , 0x00 }, - { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ - { STB0899_RSSYNCDEL , 0x00 }, - { STB0899_TSINHDELH , 0x02 }, - { STB0899_TSINHDELM , 0x00 }, - { STB0899_TSINHDELL , 0x00 }, - { STB0899_TSLLSTKM , 0x00 }, - { STB0899_TSLLSTKL , 0x00 }, - { STB0899_TSULSTKM , 0x00 }, - { STB0899_TSULSTKL , 0xab }, - { STB0899_PCKLENUL , 0x00 }, - { STB0899_PCKLENLL , 0xcc }, - { STB0899_RSPCKLEN , 0xcc }, - { STB0899_TSSTATUS , 0x80 }, - { STB0899_ERRCTRL1 , 0xb6 }, - { STB0899_ERRCTRL2 , 0x96 }, - { STB0899_ERRCTRL3 , 0x89 }, - { STB0899_DMONMSK1 , 0x27 }, - { STB0899_DMONMSK0 , 0x03 }, - { STB0899_DEMAPVIT , 0x5c }, - { STB0899_PLPARM , 0x1f }, - { STB0899_PDELCTRL , 0x48 }, - { STB0899_PDELCTRL2 , 0x00 }, - { STB0899_BBHCTRL1 , 0x00 }, - { STB0899_BBHCTRL2 , 0x00 }, - { STB0899_HYSTTHRESH , 0x77 }, - { STB0899_MATCSTM , 0x00 }, - { STB0899_MATCSTL , 0x00 }, - { STB0899_UPLCSTM , 0x00 }, - { STB0899_UPLCSTL , 0x00 }, - { STB0899_DFLCSTM , 0x00 }, - { STB0899_DFLCSTL , 0x00 }, - { STB0899_SYNCCST , 0x00 }, - { STB0899_SYNCDCSTM , 0x00 }, - { STB0899_SYNCDCSTL , 0x00 }, - { STB0899_ISI_ENTRY , 0x00 }, - { STB0899_ISI_BIT_EN , 0x00 }, - { STB0899_MATSTRM , 0x00 }, - { STB0899_MATSTRL , 0x00 }, - { STB0899_UPLSTRM , 0x00 }, - { STB0899_UPLSTRL , 0x00 }, - { STB0899_DFLSTRM , 0x00 }, - { STB0899_DFLSTRL , 0x00 }, - { STB0899_SYNCSTR , 0x00 }, - { STB0899_SYNCDSTRM , 0x00 }, - { STB0899_SYNCDSTRL , 0x00 }, - { STB0899_CFGPDELSTATUS1 , 0x10 }, - { STB0899_CFGPDELSTATUS2 , 0x00 }, - { STB0899_BBFERRORM , 0x00 }, - { STB0899_BBFERRORL , 0x00 }, - { STB0899_UPKTERRORM , 0x00 }, - { STB0899_UPKTERRORL , 0x00 }, - { 0xffff , 0xff }, -}; - -/* STB0899 demodulator config for the KNC1 and clones */ -static struct stb0899_config knc1_dvbs2_config = { - .init_dev = knc1_stb0899_s1_init_1, - .init_s2_demod = stb0899_s2_init_2, - .init_s1_demod = knc1_stb0899_s1_init_3, - .init_s2_fec = stb0899_s2_init_4, - .init_tst = stb0899_s1_init_5, - - .postproc = NULL, - - .demod_address = 0x68, -// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ - .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ -// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ - - .xtal_freq = 27000000, - .inversion = IQ_SWAP_OFF, /* 1 */ - - .lo_clk = 76500000, - .hi_clk = 90000000, - - .esno_ave = STB0899_DVBS2_ESNO_AVE, - .esno_quant = STB0899_DVBS2_ESNO_QUANT, - .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, - .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, - .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, - .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, - .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, - .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, - .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, - - .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, - .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, - .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, - .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, - - .tuner_get_frequency = tda8261_get_frequency, - .tuner_set_frequency = tda8261_set_frequency, - .tuner_set_bandwidth = NULL, - .tuner_get_bandwidth = tda8261_get_bandwidth, - .tuner_set_rfsiggain = NULL -}; - -/* - * SD1878/SHA tuner config - * 1F, Single I/P, Horizontal mount, High Sensitivity - */ -static const struct tda8261_config sd1878c_config = { -// .name = "SD1878/SHA", - .addr = 0x60, - .step_size = TDA8261_STEP_1000 /* kHz */ -}; - -static u8 read_pwm(struct budget_av *budget_av) -{ - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, - {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} - }; - - if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) - || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -#define SUBID_DVBS_KNC1 0x0010 -#define SUBID_DVBS_KNC1_PLUS 0x0011 -#define SUBID_DVBS_TYPHOON 0x4f56 -#define SUBID_DVBS_CINERGY1200 0x1154 -#define SUBID_DVBS_CYNERGY1200N 0x1155 -#define SUBID_DVBS_TV_STAR 0x0014 -#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 -#define SUBID_DVBS_TV_STAR_CI 0x0016 -#define SUBID_DVBS2_KNC1 0x0018 -#define SUBID_DVBS2_KNC1_OEM 0x0019 -#define SUBID_DVBS_EASYWATCH_1 0x001a -#define SUBID_DVBS_EASYWATCH_2 0x001b -#define SUBID_DVBS2_EASYWATCH 0x001d -#define SUBID_DVBS_EASYWATCH 0x001e - -#define SUBID_DVBC_EASYWATCH 0x002a -#define SUBID_DVBC_EASYWATCH_MK3 0x002c -#define SUBID_DVBC_KNC1 0x0020 -#define SUBID_DVBC_KNC1_PLUS 0x0021 -#define SUBID_DVBC_KNC1_MK3 0x0022 -#define SUBID_DVBC_KNC1_TDA10024 0x0028 -#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 -#define SUBID_DVBC_CINERGY1200 0x1156 -#define SUBID_DVBC_CINERGY1200_MK3 0x1176 - -#define SUBID_DVBT_EASYWATCH 0x003a -#define SUBID_DVBT_KNC1_PLUS 0x0031 -#define SUBID_DVBT_KNC1 0x0030 -#define SUBID_DVBT_CINERGY1200 0x1157 - -static void frontend_init(struct budget_av *budget_av) -{ - struct saa7146_dev * saa = budget_av->budget.dev; - struct dvb_frontend * fe = NULL; - - /* Enable / PowerON Frontend */ - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); - - /* Wait for PowerON */ - msleep(100); - - /* additional setup necessary for the PLUS cards */ - switch (saa->pci->subsystem_device) { - case SUBID_DVBS_KNC1_PLUS: - case SUBID_DVBC_KNC1_PLUS: - case SUBID_DVBT_KNC1_PLUS: - case SUBID_DVBC_EASYWATCH: - case SUBID_DVBC_KNC1_PLUS_MK3: - case SUBID_DVBS2_KNC1: - case SUBID_DVBS2_KNC1_OEM: - case SUBID_DVBS2_EASYWATCH: - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); - break; - } - - switch (saa->pci->subsystem_device) { - - case SUBID_DVBS_KNC1: - /* - * maybe that setting is needed for other dvb-s cards as well, - * but so far it has been only confirmed for this type - */ - budget_av->reinitialise_demod = 1; - /* fall through */ - case SUBID_DVBS_KNC1_PLUS: - case SUBID_DVBS_EASYWATCH_1: - if (saa->pci->subsystem_vendor == 0x1894) { - fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, - &budget_av->budget.i2c_adap); - if (fe) { - dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); - } - } else { - fe = dvb_attach(stv0299_attach, &typhoon_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; - } - } - break; - - case SUBID_DVBS_TV_STAR: - case SUBID_DVBS_TV_STAR_PLUS_X4: - case SUBID_DVBS_TV_STAR_CI: - case SUBID_DVBS_CYNERGY1200N: - case SUBID_DVBS_EASYWATCH: - case SUBID_DVBS_EASYWATCH_2: - fe = dvb_attach(stv0299_attach, &philips_sd1878_config, - &budget_av->budget.i2c_adap); - if (fe) { - dvb_attach(dvb_pll_attach, fe, 0x60, - &budget_av->budget.i2c_adap, - DVB_PLL_PHILIPS_SD1878_TDA8261); - } - break; - - case SUBID_DVBS_TYPHOON: - fe = dvb_attach(stv0299_attach, &typhoon_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; - } - break; - case SUBID_DVBS2_KNC1: - case SUBID_DVBS2_KNC1_OEM: - case SUBID_DVBS2_EASYWATCH: - budget_av->reinitialise_demod = 1; - if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) - dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); - - break; - case SUBID_DVBS_CINERGY1200: - fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; - } - break; - - case SUBID_DVBC_KNC1: - case SUBID_DVBC_KNC1_PLUS: - case SUBID_DVBC_CINERGY1200: - case SUBID_DVBC_EASYWATCH: - budget_av->reinitialise_demod = 1; - budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - fe = dvb_attach(tda10021_attach, &philips_cu1216_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe == NULL) - fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe) { - fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; - } - break; - - case SUBID_DVBC_EASYWATCH_MK3: - case SUBID_DVBC_CINERGY1200_MK3: - case SUBID_DVBC_KNC1_MK3: - case SUBID_DVBC_KNC1_TDA10024: - case SUBID_DVBC_KNC1_PLUS_MK3: - budget_av->reinitialise_demod = 1; - budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - fe = dvb_attach(tda10023_attach, - &philips_cu1216_tda10023_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe) { - fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; - } - break; - - case SUBID_DVBT_EASYWATCH: - case SUBID_DVBT_KNC1: - case SUBID_DVBT_KNC1_PLUS: - case SUBID_DVBT_CINERGY1200: - budget_av->reinitialise_demod = 1; - fe = dvb_attach(tda10046_attach, &philips_tu1216_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.init = philips_tu1216_tuner_init; - fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params; - } - break; - } - - if (fe == NULL) { - pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - saa->pci->vendor, - saa->pci->device, - saa->pci->subsystem_vendor, - saa->pci->subsystem_device); - return; - } - - budget_av->budget.dvb_frontend = fe; - - if (dvb_register_frontend(&budget_av->budget.dvb_adapter, - budget_av->budget.dvb_frontend)) { - pr_err("Frontend registration failed!\n"); - dvb_frontend_detach(budget_av->budget.dvb_frontend); - budget_av->budget.dvb_frontend = NULL; - } -} - - -static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) -{ - struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; - - dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); - - if (*isr & MASK_10) - ttpci_budget_irq10_handler(dev, isr); -} - -static int budget_av_detach(struct saa7146_dev *dev) -{ - struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; - int err; - - dprintk(2, "dev: %p\n", dev); - - if (1 == budget_av->has_saa7113) { - saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); - - msleep(200); - - saa7146_unregister_device(&budget_av->vd, dev); - - saa7146_vv_release(dev); - } - - if (budget_av->budget.ci_present) - ciintf_deinit(budget_av); - - if (budget_av->budget.dvb_frontend != NULL) { - dvb_unregister_frontend(budget_av->budget.dvb_frontend); - dvb_frontend_detach(budget_av->budget.dvb_frontend); - } - err = ttpci_budget_deinit(&budget_av->budget); - - kfree(budget_av); - - return err; -} - -#define KNC1_INPUTS 2 -static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { - { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, - V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, - V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, -}; - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index); - if (i->index >= KNC1_INPUTS) - return -EINVAL; - memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; - - *i = budget_av->cur_input; - - dprintk(1, "VIDIOC_G_INPUT %d\n", *i); - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; - - dprintk(1, "VIDIOC_S_INPUT %d\n", input); - return saa7113_setinput(budget_av, input); -} - -static struct saa7146_ext_vv vv_data; - -static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) -{ - struct budget_av *budget_av; - u8 *mac; - int err; - - dprintk(2, "dev: %p\n", dev); - - if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL))) - return -ENOMEM; - - budget_av->has_saa7113 = 0; - budget_av->budget.ci_present = 0; - - dev->ext_priv = budget_av; - - err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, - adapter_nr); - if (err) { - kfree(budget_av); - return err; - } - - /* knc1 initialization */ - saa7146_write(dev, DD1_STREAM_B, 0x04000000); - saa7146_write(dev, DD1_INIT, 0x07000600); - saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); - - if (saa7113_init(budget_av) == 0) { - budget_av->has_saa7113 = 1; - - if (0 != saa7146_vv_init(dev, &vv_data)) { - /* fixme: proper cleanup here */ - ERR("cannot init vv subsystem\n"); - return err; - } - vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; - vv_data.vid_ops.vidioc_g_input = vidioc_g_input; - vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - - if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { - /* fixme: proper cleanup here */ - ERR("cannot register capture v4l2 device\n"); - saa7146_vv_release(dev); - return err; - } - - /* beware: this modifies dev->vv ... */ - saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, - SAA7146_HPS_SYNC_PORT_A); - - saa7113_setinput(budget_av, 0); - } - - /* fixme: find some sane values here... */ - saa7146_write(dev, PCI_BT_V1, 0x1c00101f); - - mac = budget_av->budget.dvb_adapter.proposed_mac; - if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { - pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", - budget_av->budget.dvb_adapter.num); - memset(mac, 0, 6); - } else { - pr_info("KNC1-%d: MAC addr = %pM\n", - budget_av->budget.dvb_adapter.num, mac); - } - - budget_av->budget.dvb_adapter.priv = budget_av; - frontend_init(budget_av); - ciintf_init(budget_av); - - ttpci_budget_init_hooks(&budget_av->budget); - - return 0; -} - -static struct saa7146_standard standard[] = { - {.name = "PAL",.id = V4L2_STD_PAL, - .v_offset = 0x17,.v_field = 288, - .h_offset = 0x14,.h_pixels = 680, - .v_max_out = 576,.h_max_out = 768 }, - - {.name = "NTSC",.id = V4L2_STD_NTSC, - .v_offset = 0x16,.v_field = 240, - .h_offset = 0x06,.h_pixels = 708, - .v_max_out = 480,.h_max_out = 640, }, -}; - -static struct saa7146_ext_vv vv_data = { - .inputs = 2, - .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 - .flags = 0, - .stds = &standard[0], - .num_stds = ARRAY_SIZE(standard), -}; - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); -MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); -MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); -MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); -MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); -MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); -MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); -MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); -MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); -MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); -MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); -MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T); -MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); -MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP); -MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); -MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); -MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024); -MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); -MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); -MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); -MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); -MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); -MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); -MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); - -static struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), - MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), - MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), - MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), - MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), - MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), - MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), - MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), - MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), - MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), - MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), - MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), - MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), - MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), - MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), - MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), - MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a), - MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), - MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), - MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), - MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028), - MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), - MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), - MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), - MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), - MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), - MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), - MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), - MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_extension budget_extension = { - .name = "budget_av", - .flags = SAA7146_USE_I2C_IRQ, - - .pci_tbl = pci_tbl, - - .module = THIS_MODULE, - .attach = budget_av_attach, - .detach = budget_av_detach, - - .irq_mask = MASK_10, - .irq_func = budget_av_irq, -}; - -static int __init budget_av_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_av_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -module_init(budget_av_init); -module_exit(budget_av_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); -MODULE_DESCRIPTION("driver for the SAA7146 based so-called " - "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c deleted file mode 100644 index 98e524178765..000000000000 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * budget-ci.c: driver for the SAA7146 based Budget DVB cards - * - * Compiled from various sources by Michael Hunold - * - * msp430 IR support contributed by Jack Thomasson - * partially based on the Siemens DVB driver by Ralph+Marcus Metzler - * - * CI interface support (c) 2004 Andrew de Quincey - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - -#include -#include -#include -#include -#include -#include - -#include "budget.h" - -#include "dvb_ca_en50221.h" -#include "stv0299.h" -#include "stv0297.h" -#include "tda1004x.h" -#include "stb0899_drv.h" -#include "stb0899_reg.h" -#include "stb0899_cfg.h" -#include "stb6100.h" -#include "stb6100_cfg.h" -#include "lnbp21.h" -#include "bsbe1.h" -#include "bsru6.h" -#include "tda1002x.h" -#include "tda827x.h" -#include "bsbe1-d01a.h" - -#define MODULE_NAME "budget_ci" - -/* - * Regarding DEBIADDR_IR: - * Some CI modules hang if random addresses are read. - * Using address 0x4000 for the IR read means that we - * use the same address as for CI version, which should - * be a safe default. - */ -#define DEBIADDR_IR 0x4000 -#define DEBIADDR_CICONTROL 0x0000 -#define DEBIADDR_CIVERSION 0x4000 -#define DEBIADDR_IO 0x1000 -#define DEBIADDR_ATTR 0x3000 - -#define CICONTROL_RESET 0x01 -#define CICONTROL_ENABLETS 0x02 -#define CICONTROL_CAMDETECT 0x08 - -#define DEBICICTL 0x00420000 -#define DEBICICAM 0x02420000 - -#define SLOTSTATUS_NONE 1 -#define SLOTSTATUS_PRESENT 2 -#define SLOTSTATUS_RESET 4 -#define SLOTSTATUS_READY 8 -#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) - -/* RC5 device wildcard */ -#define IR_DEVICE_ANY 255 - -static int rc5_device = -1; -module_param(rc5_device, int, 0644); -MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); - -static int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -struct budget_ci_ir { - struct rc_dev *dev; - struct tasklet_struct msp430_irq_tasklet; - char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ - char phys[32]; - int rc5_device; - u32 ir_key; - bool have_command; - bool full_rc5; /* Outputs a full RC5 code */ -}; - -struct budget_ci { - struct budget budget; - struct tasklet_struct ciintf_irq_tasklet; - int slot_status; - int ci_irq; - struct dvb_ca_en50221 ca; - struct budget_ci_ir ir; - u8 tuner_pll_address; /* used for philips_tdm1316l configs */ -}; - -static void msp430_ir_interrupt(unsigned long data) -{ - struct budget_ci *budget_ci = (struct budget_ci *) data; - struct rc_dev *dev = budget_ci->ir.dev; - u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; - - /* - * The msp430 chip can generate two different bytes, command and device - * - * type1: X1CCCCCC, C = command bits (0 - 63) - * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit - * - * Each signal from the remote control can generate one or more command - * bytes and one or more device bytes. For the repeated bytes, the - * highest bit (X) is set. The first command byte is always generated - * before the first device byte. Other than that, no specific order - * seems to apply. To make life interesting, bytes can also be lost. - * - * Only when we have a command and device byte, a keypress is - * generated. - */ - - if (ir_debug) - printk("budget_ci: received byte 0x%02x\n", command); - - /* Remove repeat bit, we use every command */ - command = command & 0x7f; - - /* Is this a RC5 command byte? */ - if (command & 0x40) { - budget_ci->ir.have_command = true; - budget_ci->ir.ir_key = command & 0x3f; - return; - } - - /* It's a RC5 device byte */ - if (!budget_ci->ir.have_command) - return; - budget_ci->ir.have_command = false; - - if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && - budget_ci->ir.rc5_device != (command & 0x1f)) - return; - - if (budget_ci->ir.full_rc5) { - rc_keydown(dev, - budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key, - (command & 0x20) ? 1 : 0); - return; - } - - /* FIXME: We should generate complete scancodes for all devices */ - rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); -} - -static int msp430_ir_init(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - struct rc_dev *dev; - int error; - - dev = rc_allocate_device(); - if (!dev) { - printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); - return -ENOMEM; - } - - snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), - "Budget-CI dvb ir receiver %s", saa->name); - snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), - "pci-%s/ir0", pci_name(saa->pci)); - - dev->driver_name = MODULE_NAME; - dev->input_name = budget_ci->ir.name; - dev->input_phys = budget_ci->ir.phys; - dev->input_id.bustype = BUS_PCI; - dev->input_id.version = 1; - if (saa->pci->subsystem_vendor) { - dev->input_id.vendor = saa->pci->subsystem_vendor; - dev->input_id.product = saa->pci->subsystem_device; - } else { - dev->input_id.vendor = saa->pci->vendor; - dev->input_id.product = saa->pci->device; - } - dev->dev.parent = &saa->pci->dev; - - if (rc5_device < 0) - budget_ci->ir.rc5_device = IR_DEVICE_ANY; - else - budget_ci->ir.rc5_device = rc5_device; - - /* Select keymap and address */ - switch (budget_ci->budget.dev->pci->subsystem_device) { - case 0x100c: - case 0x100f: - case 0x1011: - case 0x1012: - /* The hauppauge keymap is a superset of these remotes */ - dev->map_name = RC_MAP_HAUPPAUGE; - budget_ci->ir.full_rc5 = true; - - if (rc5_device < 0) - budget_ci->ir.rc5_device = 0x1f; - break; - case 0x1010: - case 0x1017: - case 0x1019: - case 0x101a: - case 0x101b: - /* for the Technotrend 1500 bundled remote */ - dev->map_name = RC_MAP_TT_1500; - break; - default: - /* unknown remote */ - dev->map_name = RC_MAP_BUDGET_CI_OLD; - break; - } - if (!budget_ci->ir.full_rc5) - dev->scanmask = 0xff; - - error = rc_register_device(dev); - if (error) { - printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); - rc_free_device(dev); - return error; - } - - budget_ci->ir.dev = dev; - - tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, - (unsigned long) budget_ci); - - SAA7146_IER_ENABLE(saa, MASK_06); - saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); - - return 0; -} - -static void msp430_ir_deinit(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - - SAA7146_IER_DISABLE(saa, MASK_06); - saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); - - rc_unregister_device(budget_ci->ir.dev); -} - -static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, - DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); -} - -static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, - DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); -} - -static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, - DEBIADDR_IO | (address & 3), 1, 1, 0); -} - -static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, - DEBIADDR_IO | (address & 3), 1, value, 1, 0); -} - -static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - struct saa7146_dev *saa = budget_ci->budget.dev; - - if (slot != 0) - return -EINVAL; - - if (budget_ci->ci_irq) { - // trigger on RISING edge during reset so we know when READY is re-asserted - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); - } - budget_ci->slot_status = SLOTSTATUS_RESET; - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); - msleep(1); - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - CICONTROL_RESET, 1, 0); - - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - return 0; -} - -static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - struct saa7146_dev *saa = budget_ci->budget.dev; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - return 0; -} - -static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - struct saa7146_dev *saa = budget_ci->budget.dev; - int tmp; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); - - tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - tmp | CICONTROL_ENABLETS, 1, 0); - - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); - return 0; -} - -static void ciintf_interrupt(unsigned long data) -{ - struct budget_ci *budget_ci = (struct budget_ci *) data; - struct saa7146_dev *saa = budget_ci->budget.dev; - unsigned int flags; - - // ensure we don't get spurious IRQs during initialisation - if (!budget_ci->budget.ci_present) - return; - - // read the CAM status - flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - if (flags & CICONTROL_CAMDETECT) { - - // GPIO should be set to trigger on falling edge if a CAM is present - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); - - if (budget_ci->slot_status & SLOTSTATUS_NONE) { - // CAM insertion IRQ - budget_ci->slot_status = SLOTSTATUS_PRESENT; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, - DVB_CA_EN50221_CAMCHANGE_INSERTED); - - } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { - // CAM ready (reset completed) - budget_ci->slot_status = SLOTSTATUS_READY; - dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); - - } else if (budget_ci->slot_status & SLOTSTATUS_READY) { - // FR/DA IRQ - dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); - } - } else { - - // trigger on rising edge if a CAM is not present - when a CAM is inserted, we - // only want to get the IRQ when it sets READY. If we trigger on the falling edge, - // the CAM might not actually be ready yet. - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); - - // generate a CAM removal IRQ if we haven't already - if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { - // CAM removal IRQ - budget_ci->slot_status = SLOTSTATUS_NONE; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, - DVB_CA_EN50221_CAMCHANGE_REMOVED); - } - } -} - -static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - unsigned int flags; - - // ensure we don't get spurious IRQs during initialisation - if (!budget_ci->budget.ci_present) - return -EINVAL; - - // read the CAM status - flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - if (flags & CICONTROL_CAMDETECT) { - // mark it as present if it wasn't before - if (budget_ci->slot_status & SLOTSTATUS_NONE) { - budget_ci->slot_status = SLOTSTATUS_PRESENT; - } - - // during a RESET, we check if we can read from IO memory to see when CAM is ready - if (budget_ci->slot_status & SLOTSTATUS_RESET) { - if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { - budget_ci->slot_status = SLOTSTATUS_READY; - } - } - } else { - budget_ci->slot_status = SLOTSTATUS_NONE; - } - - if (budget_ci->slot_status != SLOTSTATUS_NONE) { - if (budget_ci->slot_status & SLOTSTATUS_READY) { - return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; - } - return DVB_CA_EN50221_POLL_CAM_PRESENT; - } - - return 0; -} - -static int ciintf_init(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - int flags; - int result; - int ci_version; - int ca_flags; - - memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); - - // enable DEBI pins - saa7146_write(saa, MC1, MASK_27 | MASK_11); - - // test if it is there - ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); - if ((ci_version & 0xa0) != 0xa0) { - result = -ENODEV; - goto error; - } - - // determine whether a CAM is present or not - flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - budget_ci->slot_status = SLOTSTATUS_NONE; - if (flags & CICONTROL_CAMDETECT) - budget_ci->slot_status = SLOTSTATUS_PRESENT; - - // version 0xa2 of the CI firmware doesn't generate interrupts - if (ci_version == 0xa2) { - ca_flags = 0; - budget_ci->ci_irq = 0; - } else { - ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | - DVB_CA_EN50221_FLAG_IRQ_FR | - DVB_CA_EN50221_FLAG_IRQ_DA; - budget_ci->ci_irq = 1; - } - - // register CI interface - budget_ci->ca.owner = THIS_MODULE; - budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; - budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; - budget_ci->ca.read_cam_control = ciintf_read_cam_control; - budget_ci->ca.write_cam_control = ciintf_write_cam_control; - budget_ci->ca.slot_reset = ciintf_slot_reset; - budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; - budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; - budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; - budget_ci->ca.data = budget_ci; - if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, - &budget_ci->ca, - ca_flags, 1)) != 0) { - printk("budget_ci: CI interface detected, but initialisation failed.\n"); - goto error; - } - - // Setup CI slot IRQ - if (budget_ci->ci_irq) { - tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); - if (budget_ci->slot_status != SLOTSTATUS_NONE) { - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); - } else { - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); - } - SAA7146_IER_ENABLE(saa, MASK_03); - } - - // enable interface - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - CICONTROL_RESET, 1, 0); - - // success! - printk("budget_ci: CI interface initialised\n"); - budget_ci->budget.ci_present = 1; - - // forge a fake CI IRQ so the CAM state is setup correctly - if (budget_ci->ci_irq) { - flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; - if (budget_ci->slot_status != SLOTSTATUS_NONE) - flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); - } - - return 0; - -error: - saa7146_write(saa, MC1, MASK_27); - return result; -} - -static void ciintf_deinit(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - - // disable CI interrupts - if (budget_ci->ci_irq) { - SAA7146_IER_DISABLE(saa, MASK_03); - saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ciintf_irq_tasklet); - } - - // reset interface - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); - msleep(1); - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - CICONTROL_RESET, 1, 0); - - // disable TS data stream to CI interface - saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); - - // release the CA device - dvb_ca_en50221_release(&budget_ci->ca); - - // disable DEBI pins - saa7146_write(saa, MC1, MASK_27); -} - -static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) -{ - struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; - - dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); - - if (*isr & MASK_06) - tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); - - if (*isr & MASK_10) - ttpci_budget_irq10_handler(dev, isr); - - if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) - tasklet_schedule(&budget_ci->ciintf_irq_tasklet); -} - -static u8 philips_su1278_tt_inittab[] = { - 0x01, 0x0f, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x5b, - 0x05, 0x85, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0x02, - 0x09, 0x00, - 0x0C, 0x01, - 0x0D, 0x81, - 0x0E, 0x44, - 0x0f, 0x14, - 0x10, 0x3c, - 0x11, 0x84, - 0x12, 0xda, - 0x13, 0x97, - 0x14, 0x95, - 0x15, 0xc9, - 0x16, 0x19, - 0x17, 0x8c, - 0x18, 0x59, - 0x19, 0xf8, - 0x1a, 0xfe, - 0x1c, 0x7f, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, - 0x29, 0x28, - 0x2a, 0x14, - 0x2b, 0x0f, - 0x2c, 0x09, - 0x2d, 0x09, - 0x31, 0x1f, - 0x32, 0x19, - 0x33, 0xfc, - 0x34, 0x93, - 0xff, 0xff -}; - -static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) -{ - stv0299_writereg(fe, 0x0e, 0x44); - if (srate >= 10000000) { - stv0299_writereg(fe, 0x13, 0x97); - stv0299_writereg(fe, 0x14, 0x95); - stv0299_writereg(fe, 0x15, 0xc9); - stv0299_writereg(fe, 0x17, 0x8c); - stv0299_writereg(fe, 0x1a, 0xfe); - stv0299_writereg(fe, 0x1c, 0x7f); - stv0299_writereg(fe, 0x2d, 0x09); - } else { - stv0299_writereg(fe, 0x13, 0x99); - stv0299_writereg(fe, 0x14, 0x8d); - stv0299_writereg(fe, 0x15, 0xce); - stv0299_writereg(fe, 0x17, 0x43); - stv0299_writereg(fe, 0x1a, 0x1d); - stv0299_writereg(fe, 0x1c, 0x12); - stv0299_writereg(fe, 0x2d, 0x05); - } - stv0299_writereg(fe, 0x0e, 0x23); - stv0299_writereg(fe, 0x0f, 0x94); - stv0299_writereg(fe, 0x10, 0x39); - stv0299_writereg(fe, 0x15, 0xc9); - - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, (ratio) & 0xf0); - - return 0; -} - -static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - u32 div; - u8 buf[4]; - struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; - - if ((p->frequency < 950000) || (p->frequency > 2150000)) - return -EINVAL; - - div = (p->frequency + (500 - 1)) / 500; /* round correctly */ - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; - buf[3] = 0x20; - - if (p->symbol_rate < 4000000) - buf[3] |= 1; - - if (p->frequency < 1250000) - buf[3] |= 0; - else if (p->frequency < 1550000) - buf[3] |= 0x40; - else if (p->frequency < 2050000) - buf[3] |= 0x80; - else if (p->frequency < 2150000) - buf[3] |= 0xC0; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct stv0299_config philips_su1278_tt_config = { - - .demod_address = 0x68, - .inittab = philips_su1278_tt_inittab, - .mclk = 64000000UL, - .invert = 0, - .skip_reinit = 1, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP1, - .min_delay_ms = 50, - .set_symbol_rate = philips_su1278_tt_set_symbol_rate, -}; - - - -static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) -{ - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; - struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = - sizeof(td1316_init) }; - - // setup PLL configuration - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - msleep(1); - - // disable the mc44BC374c (do not check for errors) - tuner_msg.addr = 0x65; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); - } - - return 0; -} - -static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - - // determine charge pump - tuner_frequency = p->frequency + 36130000; - if (tuner_frequency < 87000000) - return -EINVAL; - else if (tuner_frequency < 130000000) - cp = 3; - else if (tuner_frequency < 160000000) - cp = 5; - else if (tuner_frequency < 200000000) - cp = 6; - else if (tuner_frequency < 290000000) - cp = 3; - else if (tuner_frequency < 420000000) - cp = 5; - else if (tuner_frequency < 480000000) - cp = 6; - else if (tuner_frequency < 620000000) - cp = 3; - else if (tuner_frequency < 830000000) - cp = 5; - else if (tuner_frequency < 895000000) - cp = 7; - else - return -EINVAL; - - // determine band - if (p->frequency < 49000000) - return -EINVAL; - else if (p->frequency < 159000000) - band = 1; - else if (p->frequency < 444000000) - band = 2; - else if (p->frequency < 861000000) - band = 4; - else - return -EINVAL; - - // setup PLL filter and TDA9889 - switch (p->bandwidth_hz) { - case 6000000: - tda1004x_writereg(fe, 0x0C, 0x14); - filter = 0; - break; - - case 7000000: - tda1004x_writereg(fe, 0x0C, 0x80); - filter = 0; - break; - - case 8000000: - tda1004x_writereg(fe, 0x0C, 0x14); - filter = 1; - break; - - default: - return -EINVAL; - } - - // calculate divisor - // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; - - // setup tuner buffer - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xca; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(1); - return 0; -} - -static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - - return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); -} - -static struct tda1004x_config philips_tdm1316l_config = { - - .demod_address = 0x8, - .invert = 0, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_4M, - .agc_config = TDA10046_AGC_DEFAULT, - .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tdm1316l_request_firmware, -}; - -static struct tda1004x_config philips_tdm1316l_config_invert = { - - .demod_address = 0x8, - .invert = 1, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_4M, - .agc_config = TDA10046_AGC_DEFAULT, - .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tdm1316l_request_firmware, -}; - -static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - u8 tuner_buf[5]; - struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, - .flags = 0, - .buf = tuner_buf, - .len = sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - - // determine charge pump - tuner_frequency = p->frequency + 36125000; - if (tuner_frequency < 87000000) - return -EINVAL; - else if (tuner_frequency < 130000000) { - cp = 3; - band = 1; - } else if (tuner_frequency < 160000000) { - cp = 5; - band = 1; - } else if (tuner_frequency < 200000000) { - cp = 6; - band = 1; - } else if (tuner_frequency < 290000000) { - cp = 3; - band = 2; - } else if (tuner_frequency < 420000000) { - cp = 5; - band = 2; - } else if (tuner_frequency < 480000000) { - cp = 6; - band = 2; - } else if (tuner_frequency < 620000000) { - cp = 3; - band = 4; - } else if (tuner_frequency < 830000000) { - cp = 5; - band = 4; - } else if (tuner_frequency < 895000000) { - cp = 7; - band = 4; - } else - return -EINVAL; - - // assume PLL filter should always be 8MHz for the moment. - filter = 1; - - // calculate divisor - tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500; - - // setup tuner buffer - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xc8; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - tuner_buf[4] = 0x80; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(50); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(1); - - return 0; -} - -static u8 dvbc_philips_tdm1316l_inittab[] = { - 0x80, 0x01, - 0x80, 0x00, - 0x81, 0x01, - 0x81, 0x00, - 0x00, 0x09, - 0x01, 0x69, - 0x03, 0x00, - 0x04, 0x00, - 0x07, 0x00, - 0x08, 0x00, - 0x20, 0x00, - 0x21, 0x40, - 0x22, 0x00, - 0x23, 0x00, - 0x24, 0x40, - 0x25, 0x88, - 0x30, 0xff, - 0x31, 0x00, - 0x32, 0xff, - 0x33, 0x00, - 0x34, 0x50, - 0x35, 0x7f, - 0x36, 0x00, - 0x37, 0x20, - 0x38, 0x00, - 0x40, 0x1c, - 0x41, 0xff, - 0x42, 0x29, - 0x43, 0x20, - 0x44, 0xff, - 0x45, 0x00, - 0x46, 0x00, - 0x49, 0x04, - 0x4a, 0x00, - 0x4b, 0x7b, - 0x52, 0x30, - 0x55, 0xae, - 0x56, 0x47, - 0x57, 0xe1, - 0x58, 0x3a, - 0x5a, 0x1e, - 0x5b, 0x34, - 0x60, 0x00, - 0x63, 0x00, - 0x64, 0x00, - 0x65, 0x00, - 0x66, 0x00, - 0x67, 0x00, - 0x68, 0x00, - 0x69, 0x00, - 0x6a, 0x02, - 0x6b, 0x00, - 0x70, 0xff, - 0x71, 0x00, - 0x72, 0x00, - 0x73, 0x00, - 0x74, 0x0c, - 0x80, 0x00, - 0x81, 0x00, - 0x82, 0x00, - 0x83, 0x00, - 0x84, 0x04, - 0x85, 0x80, - 0x86, 0x24, - 0x87, 0x78, - 0x88, 0x10, - 0x89, 0x00, - 0x90, 0x01, - 0x91, 0x01, - 0xa0, 0x04, - 0xa1, 0x00, - 0xa2, 0x00, - 0xb0, 0x91, - 0xb1, 0x0b, - 0xc0, 0x53, - 0xc1, 0x70, - 0xc2, 0x12, - 0xd0, 0x00, - 0xd1, 0x00, - 0xd2, 0x00, - 0xd3, 0x00, - 0xd4, 0x00, - 0xd5, 0x00, - 0xde, 0x00, - 0xdf, 0x00, - 0x61, 0x38, - 0x62, 0x0a, - 0x53, 0x13, - 0x59, 0x08, - 0xff, 0xff, -}; - -static struct stv0297_config dvbc_philips_tdm1316l_config = { - .demod_address = 0x1c, - .inittab = dvbc_philips_tdm1316l_inittab, - .invert = 0, - .stop_during_read = 1, -}; - -static struct tda10023_config tda10023_config = { - .demod_address = 0xc, - .invert = 0, - .xtal = 16000000, - .pll_m = 11, - .pll_p = 3, - .pll_n = 1, - .deltaf = 0xa511, -}; - -static struct tda827x_config tda827x_config = { - .config = 0, -}; - -/* TT S2-3200 DVB-S (STB0899) Inittab */ -static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { - - { STB0899_DEV_ID , 0x81 }, - { STB0899_DISCNTRL1 , 0x32 }, - { STB0899_DISCNTRL2 , 0x80 }, - { STB0899_DISRX_ST0 , 0x04 }, - { STB0899_DISRX_ST1 , 0x00 }, - { STB0899_DISPARITY , 0x00 }, - { STB0899_DISSTATUS , 0x20 }, - { STB0899_DISF22 , 0x8c }, - { STB0899_DISF22RX , 0x9a }, - { STB0899_SYSREG , 0x0b }, - { STB0899_ACRPRESC , 0x11 }, - { STB0899_ACRDIV1 , 0x0a }, - { STB0899_ACRDIV2 , 0x05 }, - { STB0899_DACR1 , 0x00 }, - { STB0899_DACR2 , 0x00 }, - { STB0899_OUTCFG , 0x00 }, - { STB0899_MODECFG , 0x00 }, - { STB0899_IRQSTATUS_3 , 0x30 }, - { STB0899_IRQSTATUS_2 , 0x00 }, - { STB0899_IRQSTATUS_1 , 0x00 }, - { STB0899_IRQSTATUS_0 , 0x00 }, - { STB0899_IRQMSK_3 , 0xf3 }, - { STB0899_IRQMSK_2 , 0xfc }, - { STB0899_IRQMSK_1 , 0xff }, - { STB0899_IRQMSK_0 , 0xff }, - { STB0899_IRQCFG , 0x00 }, - { STB0899_I2CCFG , 0x88 }, - { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ - { STB0899_IOPVALUE5 , 0x00 }, - { STB0899_IOPVALUE4 , 0x20 }, - { STB0899_IOPVALUE3 , 0xc9 }, - { STB0899_IOPVALUE2 , 0x90 }, - { STB0899_IOPVALUE1 , 0x40 }, - { STB0899_IOPVALUE0 , 0x00 }, - { STB0899_GPIO00CFG , 0x82 }, - { STB0899_GPIO01CFG , 0x82 }, - { STB0899_GPIO02CFG , 0x82 }, - { STB0899_GPIO03CFG , 0x82 }, - { STB0899_GPIO04CFG , 0x82 }, - { STB0899_GPIO05CFG , 0x82 }, - { STB0899_GPIO06CFG , 0x82 }, - { STB0899_GPIO07CFG , 0x82 }, - { STB0899_GPIO08CFG , 0x82 }, - { STB0899_GPIO09CFG , 0x82 }, - { STB0899_GPIO10CFG , 0x82 }, - { STB0899_GPIO11CFG , 0x82 }, - { STB0899_GPIO12CFG , 0x82 }, - { STB0899_GPIO13CFG , 0x82 }, - { STB0899_GPIO14CFG , 0x82 }, - { STB0899_GPIO15CFG , 0x82 }, - { STB0899_GPIO16CFG , 0x82 }, - { STB0899_GPIO17CFG , 0x82 }, - { STB0899_GPIO18CFG , 0x82 }, - { STB0899_GPIO19CFG , 0x82 }, - { STB0899_GPIO20CFG , 0x82 }, - { STB0899_SDATCFG , 0xb8 }, - { STB0899_SCLTCFG , 0xba }, - { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ - { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ - { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ - { STB0899_DIRCLKCFG , 0x82 }, - { STB0899_CLKOUT27CFG , 0x7e }, - { STB0899_STDBYCFG , 0x82 }, - { STB0899_CS0CFG , 0x82 }, - { STB0899_CS1CFG , 0x82 }, - { STB0899_DISEQCOCFG , 0x20 }, - { STB0899_GPIO32CFG , 0x82 }, - { STB0899_GPIO33CFG , 0x82 }, - { STB0899_GPIO34CFG , 0x82 }, - { STB0899_GPIO35CFG , 0x82 }, - { STB0899_GPIO36CFG , 0x82 }, - { STB0899_GPIO37CFG , 0x82 }, - { STB0899_GPIO38CFG , 0x82 }, - { STB0899_GPIO39CFG , 0x82 }, - { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ - { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ - { STB0899_FILTCTRL , 0x00 }, - { STB0899_SYSCTRL , 0x00 }, - { STB0899_STOPCLK1 , 0x20 }, - { STB0899_STOPCLK2 , 0x00 }, - { STB0899_INTBUFSTATUS , 0x00 }, - { STB0899_INTBUFCTRL , 0x0a }, - { 0xffff , 0xff }, -}; - -static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { - { STB0899_DEMOD , 0x00 }, - { STB0899_RCOMPC , 0xc9 }, - { STB0899_AGC1CN , 0x41 }, - { STB0899_AGC1REF , 0x10 }, - { STB0899_RTC , 0x7a }, - { STB0899_TMGCFG , 0x4e }, - { STB0899_AGC2REF , 0x34 }, - { STB0899_TLSR , 0x84 }, - { STB0899_CFD , 0xc7 }, - { STB0899_ACLC , 0x87 }, - { STB0899_BCLC , 0x94 }, - { STB0899_EQON , 0x41 }, - { STB0899_LDT , 0xdd }, - { STB0899_LDT2 , 0xc9 }, - { STB0899_EQUALREF , 0xb4 }, - { STB0899_TMGRAMP , 0x10 }, - { STB0899_TMGTHD , 0x30 }, - { STB0899_IDCCOMP , 0xfb }, - { STB0899_QDCCOMP , 0x03 }, - { STB0899_POWERI , 0x3b }, - { STB0899_POWERQ , 0x3d }, - { STB0899_RCOMP , 0x81 }, - { STB0899_AGCIQIN , 0x80 }, - { STB0899_AGC2I1 , 0x04 }, - { STB0899_AGC2I2 , 0xf5 }, - { STB0899_TLIR , 0x25 }, - { STB0899_RTF , 0x80 }, - { STB0899_DSTATUS , 0x00 }, - { STB0899_LDI , 0xca }, - { STB0899_CFRM , 0xf1 }, - { STB0899_CFRL , 0xf3 }, - { STB0899_NIRM , 0x2a }, - { STB0899_NIRL , 0x05 }, - { STB0899_ISYMB , 0x17 }, - { STB0899_QSYMB , 0xfa }, - { STB0899_SFRH , 0x2f }, - { STB0899_SFRM , 0x68 }, - { STB0899_SFRL , 0x40 }, - { STB0899_SFRUPH , 0x2f }, - { STB0899_SFRUPM , 0x68 }, - { STB0899_SFRUPL , 0x40 }, - { STB0899_EQUAI1 , 0xfd }, - { STB0899_EQUAQ1 , 0x04 }, - { STB0899_EQUAI2 , 0x0f }, - { STB0899_EQUAQ2 , 0xff }, - { STB0899_EQUAI3 , 0xdf }, - { STB0899_EQUAQ3 , 0xfa }, - { STB0899_EQUAI4 , 0x37 }, - { STB0899_EQUAQ4 , 0x0d }, - { STB0899_EQUAI5 , 0xbd }, - { STB0899_EQUAQ5 , 0xf7 }, - { STB0899_DSTATUS2 , 0x00 }, - { STB0899_VSTATUS , 0x00 }, - { STB0899_VERROR , 0xff }, - { STB0899_IQSWAP , 0x2a }, - { STB0899_ECNT1M , 0x00 }, - { STB0899_ECNT1L , 0x00 }, - { STB0899_ECNT2M , 0x00 }, - { STB0899_ECNT2L , 0x00 }, - { STB0899_ECNT3M , 0x00 }, - { STB0899_ECNT3L , 0x00 }, - { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, - { STB0899_VTH12 , 0xf0 }, - { STB0899_VTH23 , 0xa0 }, - { STB0899_VTH34 , 0x78 }, - { STB0899_VTH56 , 0x4e }, - { STB0899_VTH67 , 0x48 }, - { STB0899_VTH78 , 0x38 }, - { STB0899_PRVIT , 0xff }, - { STB0899_VITSYNC , 0x19 }, - { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ - { STB0899_TSULC , 0x42 }, - { STB0899_RSLLC , 0x40 }, - { STB0899_TSLPL , 0x12 }, - { STB0899_TSCFGH , 0x0c }, - { STB0899_TSCFGM , 0x00 }, - { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ - { STB0899_RSSYNCDEL , 0x00 }, - { STB0899_TSINHDELH , 0x02 }, - { STB0899_TSINHDELM , 0x00 }, - { STB0899_TSINHDELL , 0x00 }, - { STB0899_TSLLSTKM , 0x00 }, - { STB0899_TSLLSTKL , 0x00 }, - { STB0899_TSULSTKM , 0x00 }, - { STB0899_TSULSTKL , 0xab }, - { STB0899_PCKLENUL , 0x00 }, - { STB0899_PCKLENLL , 0xcc }, - { STB0899_RSPCKLEN , 0xcc }, - { STB0899_TSSTATUS , 0x80 }, - { STB0899_ERRCTRL1 , 0xb6 }, - { STB0899_ERRCTRL2 , 0x96 }, - { STB0899_ERRCTRL3 , 0x89 }, - { STB0899_DMONMSK1 , 0x27 }, - { STB0899_DMONMSK0 , 0x03 }, - { STB0899_DEMAPVIT , 0x5c }, - { STB0899_PLPARM , 0x1f }, - { STB0899_PDELCTRL , 0x48 }, - { STB0899_PDELCTRL2 , 0x00 }, - { STB0899_BBHCTRL1 , 0x00 }, - { STB0899_BBHCTRL2 , 0x00 }, - { STB0899_HYSTTHRESH , 0x77 }, - { STB0899_MATCSTM , 0x00 }, - { STB0899_MATCSTL , 0x00 }, - { STB0899_UPLCSTM , 0x00 }, - { STB0899_UPLCSTL , 0x00 }, - { STB0899_DFLCSTM , 0x00 }, - { STB0899_DFLCSTL , 0x00 }, - { STB0899_SYNCCST , 0x00 }, - { STB0899_SYNCDCSTM , 0x00 }, - { STB0899_SYNCDCSTL , 0x00 }, - { STB0899_ISI_ENTRY , 0x00 }, - { STB0899_ISI_BIT_EN , 0x00 }, - { STB0899_MATSTRM , 0x00 }, - { STB0899_MATSTRL , 0x00 }, - { STB0899_UPLSTRM , 0x00 }, - { STB0899_UPLSTRL , 0x00 }, - { STB0899_DFLSTRM , 0x00 }, - { STB0899_DFLSTRL , 0x00 }, - { STB0899_SYNCSTR , 0x00 }, - { STB0899_SYNCDSTRM , 0x00 }, - { STB0899_SYNCDSTRL , 0x00 }, - { STB0899_CFGPDELSTATUS1 , 0x10 }, - { STB0899_CFGPDELSTATUS2 , 0x00 }, - { STB0899_BBFERRORM , 0x00 }, - { STB0899_BBFERRORL , 0x00 }, - { STB0899_UPKTERRORM , 0x00 }, - { STB0899_UPKTERRORL , 0x00 }, - { 0xffff , 0xff }, -}; - -static struct stb0899_config tt3200_config = { - .init_dev = tt3200_stb0899_s1_init_1, - .init_s2_demod = stb0899_s2_init_2, - .init_s1_demod = tt3200_stb0899_s1_init_3, - .init_s2_fec = stb0899_s2_init_4, - .init_tst = stb0899_s1_init_5, - - .postproc = NULL, - - .demod_address = 0x68, - - .xtal_freq = 27000000, - .inversion = IQ_SWAP_ON, /* 1 */ - - .lo_clk = 76500000, - .hi_clk = 99000000, - - .esno_ave = STB0899_DVBS2_ESNO_AVE, - .esno_quant = STB0899_DVBS2_ESNO_QUANT, - .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, - .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, - .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, - .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, - .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, - .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, - .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, - - .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, - .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, - .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, - .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, - - .tuner_get_frequency = stb6100_get_frequency, - .tuner_set_frequency = stb6100_set_frequency, - .tuner_set_bandwidth = stb6100_set_bandwidth, - .tuner_get_bandwidth = stb6100_get_bandwidth, - .tuner_set_rfsiggain = NULL -}; - -static struct stb6100_config tt3200_stb6100_config = { - .tuner_address = 0x60, - .refclock = 27000000, -}; - -static void frontend_init(struct budget_ci *budget_ci) -{ - switch (budget_ci->budget.dev->pci->subsystem_device) { - case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) - budget_ci->budget.dvb_frontend = - dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; - break; - } - break; - - case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) - budget_ci->budget.dvb_frontend = - dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; - break; - } - break; - - case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) - budget_ci->tuner_pll_address = 0x61; - budget_ci->budget.dvb_frontend = - dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; - break; - } - break; - - case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) - budget_ci->tuner_pll_address = 0x63; - budget_ci->budget.dvb_frontend = - dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; - break; - } - break; - - case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) - budget_ci->tuner_pll_address = 0x60; - budget_ci->budget.dvb_frontend = - dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; - break; - } - break; - - case 0x1017: // TT S-1500 PCI - budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; - budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; - - budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { - printk("%s: No LNBP21 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ - budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); - if (budget_ci->budget.dvb_frontend) { - if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) { - printk(KERN_ERR "%s: No tda827x found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */ - budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) { - if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { - printk(KERN_ERR "%s: No LNBP21 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } else { - printk(KERN_ERR "%s: No STB6000 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - case 0x1019: // TT S2-3200 PCI - /* - * NOTE! on some STB0899 versions, the internal PLL takes a longer time - * to settle, aka LOCK. On the older revisions of the chip, we don't see - * this, as a result on the newer chips the entire clock tree, will not - * be stable after a freshly POWER 'ed up situation. - * In this case, we should RESET the STB0899 (Active LOW) and wait for - * PLL stabilization. - * - * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is - * connected to the SAA7146 GPIO, GPIO2, Pin 142 - */ - /* Reset Demodulator */ - saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); - /* Wait for everything to die */ - msleep(50); - /* Pull it up out of Reset state */ - saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); - /* Wait for PLL to stabilize */ - msleep(250); - /* - * PLL state should be stable now. Ideally, we should check - * for PLL LOCK status. But well, never mind! - */ - budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { - if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { - printk("%s: No LNBP21 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } else { - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - } - - if (budget_ci->budget.dvb_frontend == NULL) { - printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - budget_ci->budget.dev->pci->vendor, - budget_ci->budget.dev->pci->device, - budget_ci->budget.dev->pci->subsystem_vendor, - budget_ci->budget.dev->pci->subsystem_device); - } else { - if (dvb_register_frontend - (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { - printk("budget-ci: Frontend registration failed!\n"); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } -} - -static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) -{ - struct budget_ci *budget_ci; - int err; - - budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); - if (!budget_ci) { - err = -ENOMEM; - goto out1; - } - - dprintk(2, "budget_ci: %p\n", budget_ci); - - dev->ext_priv = budget_ci; - - err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, - adapter_nr); - if (err) - goto out2; - - err = msp430_ir_init(budget_ci); - if (err) - goto out3; - - ciintf_init(budget_ci); - - budget_ci->budget.dvb_adapter.priv = budget_ci; - frontend_init(budget_ci); - - ttpci_budget_init_hooks(&budget_ci->budget); - - return 0; - -out3: - ttpci_budget_deinit(&budget_ci->budget); -out2: - kfree(budget_ci); -out1: - return err; -} - -static int budget_ci_detach(struct saa7146_dev *dev) -{ - struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; - struct saa7146_dev *saa = budget_ci->budget.dev; - int err; - - if (budget_ci->budget.ci_present) - ciintf_deinit(budget_ci); - msp430_ir_deinit(budget_ci); - if (budget_ci->budget.dvb_frontend) { - dvb_unregister_frontend(budget_ci->budget.dvb_frontend); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - } - err = ttpci_budget_deinit(&budget_ci->budget); - - // disable frontend and CI interface - saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); - - kfree(budget_ci); - - return err; -} - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); -MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT); - -static struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), - MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), - MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010), - MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), - MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), - MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), - MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), - MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), - MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_extension budget_extension = { - .name = "budget_ci dvb", - .flags = SAA7146_USE_I2C_IRQ, - - .module = THIS_MODULE, - .pci_tbl = &pci_tbl[0], - .attach = budget_ci_attach, - .detach = budget_ci_detach, - - .irq_mask = MASK_03 | MASK_06 | MASK_10, - .irq_func = budget_ci_irq, -}; - -static int __init budget_ci_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_ci_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -module_init(budget_ci_init); -module_exit(budget_ci_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); -MODULE_DESCRIPTION("driver for the SAA7146 based so-called " - "budget PCI DVB cards w/ CI-module produced by " - "Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c deleted file mode 100644 index 37d02fe09137..000000000000 --- a/drivers/media/dvb/ttpci/budget-core.c +++ /dev/null @@ -1,602 +0,0 @@ -/* - * budget-core.c: driver for the SAA7146 based Budget DVB cards - * - * Compiled from various sources by Michael Hunold - * - * Copyright (C) 2002 Ralph Metzler - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * 26feb2004 Support for FS Activy Card (Grundig tuner) by - * Michael Dreher , - * Oliver Endriss , - * Andreas 'randy' Weinberger - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - - -#include "budget.h" -#include "ttpci-eeprom.h" - -#define TS_WIDTH (2 * TS_SIZE) -#define TS_WIDTH_ACTIVY TS_SIZE -#define TS_WIDTH_DVBC TS_SIZE -#define TS_HEIGHT_MASK 0xf00 -#define TS_HEIGHT_MASK_ACTIVY 0xc00 -#define TS_HEIGHT_MASK_DVBC 0xe00 -#define TS_MIN_BUFSIZE_K 188 -#define TS_MAX_BUFSIZE_K 1410 -#define TS_MAX_BUFSIZE_K_ACTIVY 564 -#define TS_MAX_BUFSIZE_K_DVBC 1316 -#define BUFFER_WARNING_WAIT (30*HZ) - -int budget_debug; -static int dma_buffer_size = TS_MIN_BUFSIZE_K; -module_param_named(debug, budget_debug, int, 0644); -module_param_named(bufsize, dma_buffer_size, int, 0444); -MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); -MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); - -/**************************************************************************** - * TT budget / WinTV Nova - ****************************************************************************/ - -static int stop_ts_capture(struct budget *budget) -{ - dprintk(2, "budget: %p\n", budget); - - saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off - SAA7146_IER_DISABLE(budget->dev, MASK_10); - return 0; -} - -static int start_ts_capture(struct budget *budget) -{ - struct saa7146_dev *dev = budget->dev; - - dprintk(2, "budget: %p\n", budget); - - if (!budget->feeding || !budget->fe_synced) - return 0; - - saa7146_write(dev, MC1, MASK_20); // DMA3 off - - memset(budget->grabbing, 0x00, budget->buffer_size); - - saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); - - budget->ttbp = 0; - - /* - * Signal path on the Activy: - * - * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory - * - * Since the tuner feeds 204 bytes packets into the SAA7146, - * DMA3 is configured to strip the trailing 16 FEC bytes: - * Pitch: 188, NumBytes3: 188, NumLines3: 1024 - */ - - switch(budget->card->type) { - case BUDGET_FS_ACTIVY: - saa7146_write(dev, DD1_INIT, 0x04000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25)); - saa7146_write(dev, BRS_CTRL, 0x00000000); - break; - case BUDGET_PATCH: - saa7146_write(dev, DD1_INIT, 0x00000200); - saa7146_write(dev, MC2, (MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - break; - case BUDGET_CIN1200C_MK3: - case BUDGET_KNC1C_MK3: - case BUDGET_KNC1C_TDA10024: - case BUDGET_KNC1CP_MK3: - if (budget->video_port == BUDGET_VIDEO_PORTA) { - saa7146_write(dev, DD1_INIT, 0x06000200); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x00000000); - } else { - saa7146_write(dev, DD1_INIT, 0x00000600); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - } - break; - default: - if (budget->video_port == BUDGET_VIDEO_PORTA) { - saa7146_write(dev, DD1_INIT, 0x06000200); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x00000000); - } else { - saa7146_write(dev, DD1_INIT, 0x02000600); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - } - } - - saa7146_write(dev, MC2, (MASK_08 | MASK_24)); - mdelay(10); - - saa7146_write(dev, BASE_ODD3, 0); - if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { - // using odd/even buffers - saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); - } else { - // using a single buffer - saa7146_write(dev, BASE_EVEN3, 0); - } - saa7146_write(dev, PROT_ADDR3, budget->buffer_size); - saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); - - saa7146_write(dev, PITCH3, budget->buffer_width); - saa7146_write(dev, NUM_LINE_BYTE3, - (budget->buffer_height << 16) | budget->buffer_width); - - saa7146_write(dev, MC2, (MASK_04 | MASK_20)); - - SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ - SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ - saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ - - return 0; -} - -static int budget_read_fe_status(struct dvb_frontend *fe, fe_status_t *status) -{ - struct budget *budget = (struct budget *) fe->dvb->priv; - int synced; - int ret; - - if (budget->read_fe_status) - ret = budget->read_fe_status(fe, status); - else - ret = -EINVAL; - - if (!ret) { - synced = (*status & FE_HAS_LOCK); - if (synced != budget->fe_synced) { - budget->fe_synced = synced; - spin_lock(&budget->feedlock); - if (synced) - start_ts_capture(budget); - else - stop_ts_capture(budget); - spin_unlock(&budget->feedlock); - } - } - return ret; -} - -static void vpeirq(unsigned long data) -{ - struct budget *budget = (struct budget *) data; - u8 *mem = (u8 *) (budget->grabbing); - u32 olddma = budget->ttbp; - u32 newdma = saa7146_read(budget->dev, PCI_VDP3); - u32 count; - - /* Ensure streamed PCI data is synced to CPU */ - pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); - - /* nearest lower position divisible by 188 */ - newdma -= newdma % 188; - - if (newdma >= budget->buffer_size) - return; - - budget->ttbp = newdma; - - if (budget->feeding == 0 || newdma == olddma) - return; - - if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ - count = newdma - olddma; - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); - } else { /* wraparound, dump olddma..buflen and 0..newdma */ - count = budget->buffer_size - olddma; - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); - count += newdma; - dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); - } - - if (count > budget->buffer_warning_threshold) - budget->buffer_warnings++; - - if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { - printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", - budget->dev->name, __func__, budget->buffer_warnings, count); - budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; - budget->buffer_warnings = 0; - } -} - - -int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, - int uselocks, int nobusyloop) -{ - struct saa7146_dev *saa = budget->dev; - int result = 0; - unsigned long flags = 0; - - if (count > 4 || count <= 0) - return 0; - - if (uselocks) - spin_lock_irqsave(&budget->debilock, flags); - - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } - - saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); - saa7146_write(saa, DEBI_CONFIG, config); - saa7146_write(saa, DEBI_PAGE, 0); - saa7146_write(saa, MC2, (2 << 16) | 2); - - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } - - result = saa7146_read(saa, DEBI_AD); - result &= (0xffffffffUL >> ((4 - count) * 8)); - - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - - return result; -} - -int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, - int count, u32 value, int uselocks, int nobusyloop) -{ - struct saa7146_dev *saa = budget->dev; - unsigned long flags = 0; - int result; - - if (count > 4 || count <= 0) - return 0; - - if (uselocks) - spin_lock_irqsave(&budget->debilock, flags); - - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } - - saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); - saa7146_write(saa, DEBI_CONFIG, config); - saa7146_write(saa, DEBI_PAGE, 0); - saa7146_write(saa, DEBI_AD, value); - saa7146_write(saa, MC2, (2 << 16) | 2); - - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } - - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return 0; -} - - -/**************************************************************************** - * DVB API SECTION - ****************************************************************************/ - -static int budget_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct budget *budget = (struct budget *) demux->priv; - int status = 0; - - dprintk(2, "budget: %p\n", budget); - - if (!demux->dmx.frontend) - return -EINVAL; - - spin_lock(&budget->feedlock); - feed->pusi_seen = 0; /* have a clean section start */ - if (budget->feeding++ == 0) - status = start_ts_capture(budget); - spin_unlock(&budget->feedlock); - return status; -} - -static int budget_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct budget *budget = (struct budget *) demux->priv; - int status = 0; - - dprintk(2, "budget: %p\n", budget); - - spin_lock(&budget->feedlock); - if (--budget->feeding == 0) - status = stop_ts_capture(budget); - spin_unlock(&budget->feedlock); - return status; -} - -static int budget_register(struct budget *budget) -{ - struct dvb_demux *dvbdemux = &budget->demux; - int ret; - - dprintk(2, "budget: %p\n", budget); - - dvbdemux->priv = (void *) budget; - - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = budget_start_feed; - dvbdemux->stop_feed = budget_stop_feed; - dvbdemux->write_to_decoder = NULL; - - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - - dvb_dmx_init(&budget->demux); - - budget->dmxdev.filternum = 256; - budget->dmxdev.demux = &dvbdemux->dmx; - budget->dmxdev.capabilities = 0; - - dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter); - - budget->hw_frontend.source = DMX_FRONTEND_0; - - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); - - if (ret < 0) - return ret; - - budget->mem_frontend.source = DMX_MEMORY_FE; - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); - if (ret < 0) - return ret; - - ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); - if (ret < 0) - return ret; - - dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); - - return 0; -} - -static void budget_unregister(struct budget *budget) -{ - struct dvb_demux *dvbdemux = &budget->demux; - - dprintk(2, "budget: %p\n", budget); - - dvb_net_release(&budget->dvb_net); - - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend); - - dvb_dmxdev_release(&budget->dmxdev); - dvb_dmx_release(&budget->demux); -} - -int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, - struct saa7146_pci_extension_data *info, - struct module *owner, short *adapter_nums) -{ - int ret = 0; - struct budget_info *bi = info->ext_priv; - int max_bufsize; - int height_mask; - - memset(budget, 0, sizeof(struct budget)); - - dprintk(2, "dev: %p, budget: %p\n", dev, budget); - - budget->card = bi; - budget->dev = (struct saa7146_dev *) dev; - - switch(budget->card->type) { - case BUDGET_FS_ACTIVY: - budget->buffer_width = TS_WIDTH_ACTIVY; - max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; - height_mask = TS_HEIGHT_MASK_ACTIVY; - break; - - case BUDGET_KNC1C: - case BUDGET_KNC1CP: - case BUDGET_CIN1200C: - case BUDGET_KNC1C_MK3: - case BUDGET_KNC1C_TDA10024: - case BUDGET_KNC1CP_MK3: - case BUDGET_CIN1200C_MK3: - budget->buffer_width = TS_WIDTH_DVBC; - max_bufsize = TS_MAX_BUFSIZE_K_DVBC; - height_mask = TS_HEIGHT_MASK_DVBC; - break; - - default: - budget->buffer_width = TS_WIDTH; - max_bufsize = TS_MAX_BUFSIZE_K; - height_mask = TS_HEIGHT_MASK; - } - - if (dma_buffer_size < TS_MIN_BUFSIZE_K) - dma_buffer_size = TS_MIN_BUFSIZE_K; - else if (dma_buffer_size > max_bufsize) - dma_buffer_size = max_bufsize; - - budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; - if (budget->buffer_height > 0xfff) { - budget->buffer_height /= 2; - budget->buffer_height &= height_mask; - budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; - } else { - budget->buffer_height &= height_mask; - budget->buffer_size = budget->buffer_height * budget->buffer_width; - } - budget->buffer_warning_threshold = budget->buffer_size * 80/100; - budget->buffer_warnings = 0; - budget->buffer_warning_time = jiffies; - - dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", - budget->dev->name, - budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", - budget->buffer_width, budget->buffer_height); - printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); - - ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, - owner, &budget->dev->pci->dev, adapter_nums); - if (ret < 0) - return ret; - - /* set dd1 stream a & b */ - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25)); - saa7146_write(dev, MC2, (MASK_10 | MASK_26)); - saa7146_write(dev, DD1_INIT, 0x02000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - if (bi->type != BUDGET_FS_ACTIVY) - budget->video_port = BUDGET_VIDEO_PORTB; - else - budget->video_port = BUDGET_VIDEO_PORTA; - spin_lock_init(&budget->feedlock); - spin_lock_init(&budget->debilock); - - /* the Siemens DVB needs this if you want to have the i2c chips - get recognized before the main driver is loaded */ - if (bi->type != BUDGET_FS_ACTIVY) - saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ - - strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name)); - - saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); - strcpy(budget->i2c_adap.name, budget->card->name); - - if (i2c_add_adapter(&budget->i2c_adap) < 0) { - ret = -ENOMEM; - goto err_dvb_unregister; - } - - ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); - - budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt); - if (NULL == budget->grabbing) { - ret = -ENOMEM; - goto err_del_i2c; - } - - saa7146_write(dev, PCI_BT_V1, 0x001c0000); - /* upload all */ - saa7146_write(dev, GPIO_CTRL, 0x000000); - - tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget); - - /* frontend power on */ - if (bi->type != BUDGET_FS_ACTIVY) - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); - - if ((ret = budget_register(budget)) == 0) - return 0; /* Everything OK */ - - /* An error occurred, cleanup resources */ - saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); - -err_del_i2c: - i2c_del_adapter(&budget->i2c_adap); - -err_dvb_unregister: - dvb_unregister_adapter(&budget->dvb_adapter); - - return ret; -} - -void ttpci_budget_init_hooks(struct budget *budget) -{ - if (budget->dvb_frontend && !budget->read_fe_status) { - budget->read_fe_status = budget->dvb_frontend->ops.read_status; - budget->dvb_frontend->ops.read_status = budget_read_fe_status; - } -} - -int ttpci_budget_deinit(struct budget *budget) -{ - struct saa7146_dev *dev = budget->dev; - - dprintk(2, "budget: %p\n", budget); - - budget_unregister(budget); - - tasklet_kill(&budget->vpe_tasklet); - - saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); - - i2c_del_adapter(&budget->i2c_adap); - - dvb_unregister_adapter(&budget->dvb_adapter); - - return 0; -} - -void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) -{ - struct budget *budget = (struct budget *) dev->ext_priv; - - dprintk(8, "dev: %p, budget: %p\n", dev, budget); - - if (*isr & MASK_10) - tasklet_schedule(&budget->vpe_tasklet); -} - -void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) -{ - struct budget *budget = (struct budget *) dev->ext_priv; - - spin_lock(&budget->feedlock); - budget->video_port = video_port; - if (budget->feeding) { - stop_ts_capture(budget); - start_ts_capture(budget); - } - spin_unlock(&budget->feedlock); -} - -EXPORT_SYMBOL_GPL(ttpci_budget_debiread); -EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); -EXPORT_SYMBOL_GPL(ttpci_budget_init); -EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks); -EXPORT_SYMBOL_GPL(ttpci_budget_deinit); -EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); -EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); -EXPORT_SYMBOL_GPL(budget_debug); - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c deleted file mode 100644 index 2cb35c23d2ac..000000000000 --- a/drivers/media/dvb/ttpci/budget-patch.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - * budget-patch.c: driver for Budget Patch, - * hardware modification of DVB-S cards enabling full TS - * - * Written by Emard - * - * Original idea by Roberto Deza - * - * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic - * and Metzlerbros - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - -#include "av7110.h" -#include "av7110_hw.h" -#include "budget.h" -#include "stv0299.h" -#include "ves1x93.h" -#include "tda8083.h" - -#include "bsru6.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define budget_patch budget - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH); -//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC); - -static struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000), -// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), - { - .vendor = 0, - } -}; - -/* those lines are for budget-patch to be tried -** on a true budget card and observe the -** behaviour of VSYNC generated by rps1. -** this code was shamelessly copy/pasted from budget.c -*/ -static void gpio_Set22K (struct budget *budget, int state) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); -} - -/* Diseqc functions only for TT Budget card */ -/* taken from the Skyvision DVB driver by - Ralph Metzler */ - -static void DiseqcSendBit (struct budget *budget, int data) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - udelay(data ? 500 : 1000); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - udelay(data ? 1000 : 500); -} - -static void DiseqcSendByte (struct budget *budget, int data) -{ - int i, par=1, d; - - dprintk(2, "budget: %p\n", budget); - - for (i=7; i>=0; i--) { - d = (data>>i)&1; - par ^= d; - DiseqcSendBit(budget, d); - } - - DiseqcSendBit(budget, par); -} - -static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) -{ - struct saa7146_dev *dev=budget->dev; - int i; - - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - mdelay(16); - - for (i=0; idvb->priv; - - switch (tone) { - case SEC_TONE_ON: - gpio_Set22K (budget, 1); - break; - - case SEC_TONE_OFF: - gpio_Set22K (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, 0, NULL, minicmd); - - return 0; -} - -static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length) -{ - int i; - - dprintk(2, "budget: %p\n", budget); - - for (i = 2; i < length; i++) - { - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0); - msleep(5); - } - if (length) - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0); - else - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0); - msleep(5); - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0); - msleep(5); - return 0; -} - -static void av7110_set22k(struct budget_patch *budget, int state) -{ - u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0}; - - dprintk(2, "budget: %p\n", budget); - budget_av7110_send_fw_cmd(budget, buf, 2); -} - -static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst) -{ - int i; - u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(2, "budget: %p\n", budget); - - if (len>10) - len=10; - - buf[1] = len+2; - buf[2] = len; - - if (burst != -1) - buf[3]=burst ? 0x01 : 0x00; - else - buf[3]=0xffff; - - for (i=0; idvb->priv; - - switch (tone) { - case SEC_TONE_ON: - av7110_set22k (budget, 1); - break; - - case SEC_TONE_OFF: - av7110_set22k (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - - av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) -{ - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - - av7110_send_diseqc_msg (budget, 0, NULL, minicmd); - - return 0; -} - -static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - u8 pwr = 0; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div = (p->frequency + 479500) / 125; - - if (p->frequency > 2000000) - pwr = 3; - else if (p->frequency > 1800000) - pwr = 2; - else if (p->frequency > 1600000) - pwr = 1; - else if (p->frequency > 1200000) - pwr = 0; - else if (p->frequency >= 1100000) - pwr = 1; - else pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1x93_config alps_bsrv2_config = { - .demod_address = 0x08, - .xin = 90100000UL, - .invert_pwm = 0, -}; - -static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = p->frequency / 125; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = 0x00; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct tda8083_config grundig_29504_451_config = { - .demod_address = 0x68, -}; - -static void frontend_init(struct budget_patch* budget) -{ - switch(budget->dev->pci->subsystem_device) { - case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X - case 0x1013: // SATELCO Multimedia PCI - - // try the ALPS BSRV2 first of all - budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_patch_set_tone; - break; - } - - // try the ALPS BSRU6 now - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - - // Try the grundig 29504-451 - budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - break; - } - - if (budget->dvb_frontend == NULL) { - printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - budget->dev->pci->vendor, - budget->dev->pci->device, - budget->dev->pci->subsystem_vendor, - budget->dev->pci->subsystem_device); - } else { - if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { - printk("budget-av: Frontend registration failed!\n"); - dvb_frontend_detach(budget->dvb_frontend); - budget->dvb_frontend = NULL; - } - } -} - -/* written by Emard */ -static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) -{ - struct budget_patch *budget; - int err; - int count = 0; - int detected = 0; - -#define PATCH_RESET 0 -#define RPS_IRQ 0 -#define HPS_SETUP 0 -#if PATCH_RESET - saa7146_write(dev, MC1, MASK_31); - msleep(40); -#endif -#if HPS_SETUP - // initialize registers. Better to have it like this - // than leaving something unconfigured - saa7146_write(dev, DD1_STREAM_B, 0); - // port B VSYNC at rising edge - saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too! - saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI - - // debi config - // saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18); - - // zero all HPS registers - saa7146_write(dev, HPS_H_PRESCALE, 0); // r68 - saa7146_write(dev, HPS_H_SCALE, 0); // r6c - saa7146_write(dev, BCS_CTRL, 0); // r70 - saa7146_write(dev, HPS_V_SCALE, 0); // r60 - saa7146_write(dev, HPS_V_GAIN, 0); // r64 - saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74 - saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78 - // Set HPS prescaler for port B input - saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) ); - saa7146_write(dev, MC2, - 0 * (MASK_08 | MASK_24) | // BRS control - 0 * (MASK_09 | MASK_25) | // a - 0 * (MASK_10 | MASK_26) | // b - 1 * (MASK_06 | MASK_22) | // HPS_CTRL1 - 1 * (MASK_05 | MASK_21) | // HPS_CTRL2 - 0 * (MASK_01 | MASK_15) // DEBI - ); -#endif - // Disable RPS1 and RPS0 - saa7146_write(dev, MC1, ( MASK_29 | MASK_28)); - // RPS1 timeout disable - saa7146_write(dev, RPS_TOV1, 0); - - // code for autodetection - // will wait for VBI_B event (vertical blank at port B) - // and will reset GPIO3 after VBI_B is detected. - // (GPIO3 should be raised high by CPU to - // test if GPIO3 will generate vertical blank signal - // in budget patch GPIO3 is connected to VSYNC_B - count = 0; -#if 0 - WRITE_RPS1(CMD_UPLOAD | - MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); -#endif - WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - // issue RPS1 interrupt to increment counter - WRITE_RPS1(CMD_INTERRUPT); - // at least a NOP is neede between two interrupts - WRITE_RPS1(CMD_NOP); - // interrupt again - WRITE_RPS1(CMD_INTERRUPT); -#endif - WRITE_RPS1(CMD_STOP); - -#if RPS_IRQ - // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) - // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled - // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - // set event counter 1 threshold to maximum allowed value (rEC p55) - saa7146_write(dev, ECT1R, 0x3fff ); -#endif - // Fix VSYNC level - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - // Set RPS1 Address register to point to RPS code (r108 p42) - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - // Enable RPS1, (rFC p33) - saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); - - - mdelay(50); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - mdelay(150); - - - if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) - detected = 1; - -#if RPS_IRQ - printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); -#endif - // Disable RPS1 - saa7146_write(dev, MC1, ( MASK_29 )); - - if(detected == 0) - printk("budget-patch not detected or saa7146 in non-default state.\n" - "try enabling ressetting of 7146 with MASK_31 in MC1 register\n"); - - else - printk("BUDGET-PATCH DETECTED.\n"); - - -/* OLD (Original design by Roberto Deza): -** This code will setup the SAA7146_RPS1 to generate a square -** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of -** TS_WIDTH packets) has been acquired on SAA7146_D1B video port; -** then, this GPIO3 output which is connected to the D1B_VSYNC -** input, will trigger the acquisition of the alternate field -** and so on. -** Currently, the TT_budget / WinTV_Nova cards have two ICs -** (74HCT4040, LVC74) for the generation of this VSYNC signal, -** which seems that can be done perfectly without this :-)). -*/ - -/* New design (By Emard) -** this rps1 code will copy internal HS event to GPIO3 pin. -** GPIO3 is in budget-patch hardware connected to port B VSYNC - -** HS is an internal event of 7146, accessible with RPS -** and temporarily raised high every n lines -** (n in defined in the RPS_THRESH1 counter threshold) -** I think HS is raised high on the beginning of the n-th line -** and remains high until this n-th line that triggered -** it is completely received. When the reception of n-th line -** ends, HS is lowered. - -** To transmit data over DMA, 7146 needs changing state at -** port B VSYNC pin. Any changing of port B VSYNC will -** cause some DMA data transfer, with more or less packets loss. -** It depends on the phase and frequency of VSYNC and -** the way of 7146 is instructed to trigger on port B (defined -** in DD1_INIT register, 3rd nibble from the right valid -** numbers are 0-7, see datasheet) -** -** The correct triggering can minimize packet loss, -** dvbtraffic should give this stable bandwidths: -** 22k transponder = 33814 kbit/s -** 27.5k transponder = 38045 kbit/s -** by experiment it is found that the best results -** (stable bandwidths and almost no packet loss) -** are obtained using DD1_INIT triggering number 2 -** (Va at rising edge of VS Fa = HS x VS-failing forced toggle) -** and a VSYNC phase that occurs in the middle of DMA transfer -** (about byte 188*512=96256 in the DMA window). -** -** Phase of HS is still not clear to me how to control, -** It just happens to be so. It can be seen if one enables -** RPS_IRQ and print Event Counter 1 in vpeirq(). Every -** time RPS_INTERRUPT is called, the Event Counter 1 will -** increment. That's how the 7146 is programmed to do event -** counting in this budget-patch.c -** I *think* HPS setting has something to do with the phase -** of HS but I can't be 100% sure in that. - -** hardware debug note: a working budget card (including budget patch) -** with vpeirq() interrupt setup in mode "0x90" (every 64K) will -** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes -** and that means 3*25=75 Hz of interrupt frequency, as seen by -** watch cat /proc/interrupts -** -** If this frequency is 3x lower (and data received in the DMA -** buffer don't start with 0x47, but in the middle of packets, -** whose lengths appear to be like 188 292 188 104 etc. -** this means VSYNC line is not connected in the hardware. -** (check soldering pcb and pins) -** The same behaviour of missing VSYNC can be duplicated on budget -** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. -*/ - - // Setup RPS1 "program" (p35) - count = 0; - - - // Wait Source Line Counter Threshold (p36) - WRITE_RPS1(CMD_PAUSE | EVT_HS); - // Set GPIO3=1 (p42) - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); -#if RPS_IRQ - // issue RPS1 interrupt - WRITE_RPS1(CMD_INTERRUPT); -#endif - // Wait reset Source Line Counter Threshold (p36) - WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); - // Set GPIO3=0 (p42) - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - // issue RPS1 interrupt - WRITE_RPS1(CMD_INTERRUPT); -#endif - // Jump to begin of RPS program (p37) - WRITE_RPS1(CMD_JUMP); - WRITE_RPS1(dev->d_rps1.dma_handle); - - // Fix VSYNC level - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - // Set RPS1 Address register to point to RPS code (r108 p42) - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - - if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) - return -ENOMEM; - - dprintk(2, "budget: %p\n", budget); - - err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); - if (err) { - kfree(budget); - return err; - } - - // Set Source Line Counter Threshold, using BRS (rCC p43) - // It generates HS event every TS_HEIGHT lines - // this is related to TS_WIDTH set in register - // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE - // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 - //,then RPS_THRESH1 - // should be set to trigger every TS_HEIGHT (512) lines. - // - saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); - - // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); - // Enable RPS1 (rFC p33) - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - - - dev->ext_priv = budget; - - budget->dvb_adapter.priv = budget; - frontend_init(budget); - - ttpci_budget_init_hooks(budget); - - return 0; -} - -static int budget_patch_detach (struct saa7146_dev* dev) -{ - struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; - int err; - - if (budget->dvb_frontend) { - dvb_unregister_frontend(budget->dvb_frontend); - dvb_frontend_detach(budget->dvb_frontend); - } - err = ttpci_budget_deinit (budget); - - kfree (budget); - - return err; -} - -static int __init budget_patch_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_patch_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -static struct saa7146_extension budget_extension = { - .name = "budget_patch dvb", - .flags = 0, - - .module = THIS_MODULE, - .pci_tbl = pci_tbl, - .attach = budget_patch_attach, - .detach = budget_patch_detach, - - .irq_mask = MASK_10, - .irq_func = ttpci_budget_irq10_handler, -}; - -module_init(budget_patch_init); -module_exit(budget_patch_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others"); -MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 " - "based so-called Budget Patch cards"); diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c deleted file mode 100644 index 7e6e43ae5c51..000000000000 --- a/drivers/media/dvb/ttpci/budget.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * budget.c: driver for the SAA7146 based Budget DVB cards - * - * Compiled from various sources by Michael Hunold - * - * Copyright (C) 2002 Ralph Metzler - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * 26feb2004 Support for FS Activy Card (Grundig tuner) by - * Michael Dreher , - * Oliver Endriss and - * Andreas 'randy' Weinberger - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - * - * - * the project's page is at http://www.linuxtv.org/ - */ - -#include "budget.h" -#include "stv0299.h" -#include "ves1x93.h" -#include "ves1820.h" -#include "l64781.h" -#include "tda8083.h" -#include "s5h1420.h" -#include "tda10086.h" -#include "tda826x.h" -#include "lnbp21.h" -#include "bsru6.h" -#include "bsbe1.h" -#include "tdhd1.h" -#include "stv6110x.h" -#include "stv090x.h" -#include "isl6423.h" -#include "lnbh24.h" - - -static int diseqc_method; -module_param(diseqc_method, int, 0444); -MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static void Set22K (struct budget *budget, int state) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); -} - -/* Diseqc functions only for TT Budget card */ -/* taken from the Skyvision DVB driver by - Ralph Metzler */ - -static void DiseqcSendBit (struct budget *budget, int data) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - udelay(data ? 500 : 1000); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - udelay(data ? 1000 : 500); -} - -static void DiseqcSendByte (struct budget *budget, int data) -{ - int i, par=1, d; - - dprintk(2, "budget: %p\n", budget); - - for (i=7; i>=0; i--) { - d = (data>>i)&1; - par ^= d; - DiseqcSendBit(budget, d); - } - - DiseqcSendBit(budget, par); -} - -static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) -{ - struct saa7146_dev *dev=budget->dev; - int i; - - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - mdelay(16); - - for (i=0; idev; - - dprintk(2, "budget: %p\n", budget); - - switch (voltage) { - case SEC_VOLTAGE_13: - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); - break; - case SEC_VOLTAGE_18: - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); - break; - case SEC_VOLTAGE_OFF: - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - return SetVoltage_Activy (budget, voltage); -} - -static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - switch (tone) { - case SEC_TONE_ON: - Set22K (budget, 1); - break; - - case SEC_TONE_OFF: - Set22K (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, 0, NULL, minicmd); - - return 0; -} - -static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u8 pwr = 0; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div = (c->frequency + 479500) / 125; - - if (c->frequency > 2000000) - pwr = 3; - else if (c->frequency > 1800000) - pwr = 2; - else if (c->frequency > 1600000) - pwr = 1; - else if (c->frequency > 1200000) - pwr = 0; - else if (c->frequency >= 1100000) - pwr = 1; - else pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct ves1x93_config alps_bsrv2_config = -{ - .demod_address = 0x08, - .xin = 90100000UL, - .invert_pwm = 0, -}; - -static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (c->frequency + 35937500 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x85 | ((div >> 10) & 0x60); - data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct ves1820_config alps_tdbe2_config = { - .demod_address = 0x09, - .xin = 57840000UL, - .invert = 1, - .selagc = VES1820_SELAGC_SIGNAMPERR, -}; - -static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = fe->dvb->priv; - u8 *tuner_addr = fe->tuner_priv; - u32 div; - u8 cfg, cpump, band_select; - u8 data[4]; - struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) }; - - if (tuner_addr) - msg.addr = *tuner_addr; - else - msg.addr = 0x61; - - div = (36125000 + c->frequency) / 166666; - - cfg = 0x88; - - if (c->frequency < 175000000) - cpump = 2; - else if (c->frequency < 390000000) - cpump = 1; - else if (c->frequency < 470000000) - cpump = 2; - else if (c->frequency < 750000000) - cpump = 1; - else - cpump = 3; - - if (c->frequency < 175000000) - band_select = 0x0e; - else if (c->frequency < 470000000) - band_select = 0x05; - else - band_select = 0x03; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = ((div >> 10) & 0x60) | cfg; - data[3] = (cpump << 6) | band_select; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct l64781_config grundig_29504_401_config = { - .demod_address = 0x55, -}; - -static struct l64781_config grundig_29504_401_config_activy = { - .demod_address = 0x54, -}; - -static u8 tuner_address_grundig_29504_401_activy = 0x60; - -static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = c->frequency / 125; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = 0x00; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct tda8083_config grundig_29504_451_config = { - .demod_address = 0x68, -}; - -static int s5h1420_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = c->frequency / 1000; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0xc2; - - if (div < 1450) - data[3] = 0x00; - else if (div < 1850) - data[3] = 0x40; - else if (div < 2000) - data[3] = 0x80; - else - data[3] = 0xc0; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - - return 0; -} - -static struct s5h1420_config s5h1420_config = { - .demod_address = 0x53, - .invert = 1, - .cdclk_polarity = 1, -}; - -static struct tda10086_config tda10086_config = { - .demod_address = 0x0e, - .invert = 0, - .diseqc_tone = 1, - .xtal_freq = TDA10086_XTAL_16M, -}; - -static struct stv0299_config alps_bsru6_config_activy = { - .demod_address = 0x68, - .inittab = alps_bsru6_inittab, - .mclk = 88000000UL, - .invert = 1, - .op0_off = 1, - .min_delay_ms = 100, - .set_symbol_rate = alps_bsru6_set_symbol_rate, -}; - -static struct stv0299_config alps_bsbe1_config_activy = { - .demod_address = 0x68, - .inittab = alps_bsbe1_inittab, - .mclk = 88000000UL, - .invert = 1, - .op0_off = 1, - .min_delay_ms = 100, - .set_symbol_rate = alps_bsbe1_set_symbol_rate, -}; - -static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) -{ - struct budget *budget = (struct budget *)fe->dvb->priv; - - return request_firmware(fw, name, &budget->dev->pci->dev); -} - - -static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) -{ - u8 val; - struct i2c_msg msg[] = { - { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, - { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } - }; - - return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; -} - -static u8 read_pwm(struct budget* budget) -{ - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, - { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; - - if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -static struct stv090x_config tt1600_stv090x_config = { - .device = STV0903, - .demod_mode = STV090x_SINGLE, - .clk_mode = STV090x_CLK_EXT, - - .xtal = 13500000, - .address = 0x68, - - .ts1_mode = STV090x_TSMODE_DVBCI, - .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, - - .repeater_level = STV090x_RPTLEVEL_16, - - .tuner_init = NULL, - .tuner_sleep = NULL, - .tuner_set_mode = NULL, - .tuner_set_frequency = NULL, - .tuner_get_frequency = NULL, - .tuner_set_bandwidth = NULL, - .tuner_get_bandwidth = NULL, - .tuner_set_bbgain = NULL, - .tuner_get_bbgain = NULL, - .tuner_set_refclk = NULL, - .tuner_get_status = NULL, -}; - -static struct stv6110x_config tt1600_stv6110x_config = { - .addr = 0x60, - .refclk = 27000000, - .clk_div = 2, -}; - -static struct isl6423_config tt1600_isl6423_config = { - .current_max = SEC_CURRENT_515m, - .curlim = SEC_CURRENT_LIM_ON, - .mod_extern = 1, - .addr = 0x08, -}; - -static void frontend_init(struct budget *budget) -{ - (void)alps_bsbe1_config; /* avoid warning */ - - switch(budget->dev->pci->subsystem_device) { - case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) - case 0x1013: - // try the ALPS BSRV2 first of all - budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - - // try the ALPS BSRU6 now - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - } - break; - } - break; - - case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) - - budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - break; - } - break; - - case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) - - budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; - budget->dvb_frontend->tuner_priv = NULL; - break; - } - break; - - case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ - { - int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); - - if (subtype < 0) - break; - /* fixme: find a better way to identify the card */ - if (subtype < 0x36) { - /* assume ALPS BSRU6 */ - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); - if (budget->dvb_frontend) { - printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; - budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - break; - } - } else { - /* assume ALPS BSBE1 */ - /* reset tuner */ - saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); - msleep(50); - saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); - msleep(250); - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); - if (budget->dvb_frontend) { - printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; - budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - break; - } - } - break; - } - - case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) - budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; - budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - } - break; - - case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ - budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - } - break; - - case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ - budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy; - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; - } - break; - - case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) - budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params; - if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { - printk("%s: No LNBP21 found!\n", __func__); - goto error_out; - } - break; - } - - case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) - // gpio2 is connected to CLB - reset it + leave it high - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); - msleep(1); - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); - msleep(1); - - budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL) - printk("%s: No tda826x found!\n", __func__); - if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { - printk("%s: No LNBP21 found!\n", __func__); - goto error_out; - } - break; - } - - case 0x101c: { /* TT S2-1600 */ - struct stv6110x_devctl *ctl; - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); - msleep(50); - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); - msleep(250); - - budget->dvb_frontend = dvb_attach(stv090x_attach, - &tt1600_stv090x_config, - &budget->i2c_adap, - STV090x_DEMODULATOR_0); - - if (budget->dvb_frontend) { - - ctl = dvb_attach(stv6110x_attach, - budget->dvb_frontend, - &tt1600_stv6110x_config, - &budget->i2c_adap); - - if (ctl) { - tt1600_stv090x_config.tuner_init = ctl->tuner_init; - tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; - tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; - tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; - tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; - tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; - tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; - tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; - tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; - tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; - tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; - - /* call the init function once to initialize - tuner's clock output divider and demod's - master clock */ - if (budget->dvb_frontend->ops.init) - budget->dvb_frontend->ops.init(budget->dvb_frontend); - - if (dvb_attach(isl6423_attach, - budget->dvb_frontend, - &budget->i2c_adap, - &tt1600_isl6423_config) == NULL) { - printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); - goto error_out; - } - } else { - printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); - goto error_out; - } - } - } - break; - - case 0x1020: { /* Omicom S2 */ - struct stv6110x_devctl *ctl; - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); - msleep(50); - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); - msleep(250); - - budget->dvb_frontend = dvb_attach(stv090x_attach, - &tt1600_stv090x_config, - &budget->i2c_adap, - STV090x_DEMODULATOR_0); - - if (budget->dvb_frontend) { - printk(KERN_INFO "budget: Omicom S2 detected\n"); - - ctl = dvb_attach(stv6110x_attach, - budget->dvb_frontend, - &tt1600_stv6110x_config, - &budget->i2c_adap); - - if (ctl) { - tt1600_stv090x_config.tuner_init = ctl->tuner_init; - tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; - tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; - tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; - tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; - tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; - tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; - tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; - tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; - tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; - tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; - - /* call the init function once to initialize - tuner's clock output divider and demod's - master clock */ - if (budget->dvb_frontend->ops.init) - budget->dvb_frontend->ops.init(budget->dvb_frontend); - - if (dvb_attach(lnbh24_attach, - budget->dvb_frontend, - &budget->i2c_adap, - LNBH24_PCL | LNBH24_TTX, - LNBH24_TEN, 0x14>>1) == NULL) { - printk(KERN_ERR - "No LNBH24 found!\n"); - goto error_out; - } - } else { - printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); - goto error_out; - } - } - } - break; - } - - if (budget->dvb_frontend == NULL) { - printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - budget->dev->pci->vendor, - budget->dev->pci->device, - budget->dev->pci->subsystem_vendor, - budget->dev->pci->subsystem_device); - } else { - if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) - goto error_out; - } - return; - -error_out: - printk("budget: Frontend registration failed!\n"); - dvb_frontend_detach(budget->dvb_frontend); - budget->dvb_frontend = NULL; - return; -} - -static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) -{ - struct budget *budget = NULL; - int err; - - budget = kmalloc(sizeof(struct budget), GFP_KERNEL); - if( NULL == budget ) { - return -ENOMEM; - } - - dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget); - - dev->ext_priv = budget; - - err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); - if (err) { - printk("==> failed\n"); - kfree (budget); - return err; - } - - budget->dvb_adapter.priv = budget; - frontend_init(budget); - - ttpci_budget_init_hooks(budget); - - return 0; -} - -static int budget_detach (struct saa7146_dev* dev) -{ - struct budget *budget = (struct budget*) dev->ext_priv; - int err; - - if (budget->dvb_frontend) { - dvb_unregister_frontend(budget->dvb_frontend); - dvb_frontend_detach(budget->dvb_frontend); - } - - err = ttpci_budget_deinit (budget); - - kfree (budget); - dev->ext_priv = NULL; - - return err; -} - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); -MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); -MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT); - -static struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), - MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), - MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), - MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), - MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), - MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), - MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c), - MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), - MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), - MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), - MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), - MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_extension budget_extension = { - .name = "budget dvb", - .flags = SAA7146_USE_I2C_IRQ, - - .module = THIS_MODULE, - .pci_tbl = pci_tbl, - .attach = budget_attach, - .detach = budget_detach, - - .irq_mask = MASK_10, - .irq_func = ttpci_budget_irq10_handler, -}; - -static int __init budget_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -module_init(budget_init); -module_exit(budget_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); -MODULE_DESCRIPTION("driver for the SAA7146 based so-called " - "budget PCI DVB cards by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h deleted file mode 100644 index 3d8a806c20bb..000000000000 --- a/drivers/media/dvb/ttpci/budget.h +++ /dev/null @@ -1,124 +0,0 @@ - -#ifndef __BUDGET_DVB__ -#define __BUDGET_DVB__ - -#include "dvb_frontend.h" -#include "dvbdev.h" -#include "demux.h" -#include "dvb_demux.h" -#include "dmxdev.h" -#include "dvb_filter.h" -#include "dvb_net.h" - -#include -#include - -#include - -extern int budget_debug; - -#ifdef dprintk -#undef dprintk -#endif - -#define dprintk(level,args...) \ - do { if ((budget_debug & level)) { printk("%s: %s(): ", KBUILD_MODNAME, __func__); printk(args); } } while (0) - -struct budget_info { - char *name; - int type; -}; - -/* place to store all the necessary device information */ -struct budget { - - /* devices */ - struct dvb_device dvb_dev; - struct dvb_net dvb_net; - - struct saa7146_dev *dev; - - struct i2c_adapter i2c_adap; - struct budget_info *card; - - unsigned char *grabbing; - struct saa7146_pgtable pt; - - struct tasklet_struct fidb_tasklet; - struct tasklet_struct vpe_tasklet; - - struct dmxdev dmxdev; - struct dvb_demux demux; - - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - - int ci_present; - int video_port; - - u32 buffer_width; - u32 buffer_height; - u32 buffer_size; - u32 buffer_warning_threshold; - u32 buffer_warnings; - unsigned long buffer_warning_time; - - u32 ttbp; - int feeding; - - spinlock_t feedlock; - - spinlock_t debilock; - - struct dvb_adapter dvb_adapter; - struct dvb_frontend *dvb_frontend; - int (*read_fe_status)(struct dvb_frontend *fe, fe_status_t *status); - int fe_synced; - - void *priv; -}; - -#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \ -static struct budget_info x_var ## _info = { \ - .name=x_name, \ - .type=x_type }; \ -static struct saa7146_pci_extension_data x_var = { \ - .ext_priv = &x_var ## _info, \ - .ext = &budget_extension }; - -#define BUDGET_TT 0 -#define BUDGET_TT_HW_DISEQC 1 -#define BUDGET_PATCH 3 -#define BUDGET_FS_ACTIVY 4 -#define BUDGET_CIN1200S 5 -#define BUDGET_CIN1200C 6 -#define BUDGET_CIN1200T 7 -#define BUDGET_KNC1S 8 -#define BUDGET_KNC1C 9 -#define BUDGET_KNC1T 10 -#define BUDGET_KNC1SP 11 -#define BUDGET_KNC1CP 12 -#define BUDGET_KNC1TP 13 -#define BUDGET_TVSTAR 14 -#define BUDGET_CIN1200C_MK3 15 -#define BUDGET_KNC1C_MK3 16 -#define BUDGET_KNC1CP_MK3 17 -#define BUDGET_KNC1S2 18 -#define BUDGET_KNC1C_TDA10024 19 - -#define BUDGET_VIDEO_PORTA 0 -#define BUDGET_VIDEO_PORTB 1 - -extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, - struct saa7146_pci_extension_data *info, - struct module *owner, short *adapter_nums); -extern void ttpci_budget_init_hooks(struct budget *budget); -extern int ttpci_budget_deinit(struct budget *budget); -extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); -extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port); -extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, - int uselocks, int nobusyloop); -extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, - int uselocks, int nobusyloop); - -#endif diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.c b/drivers/media/dvb/ttpci/ttpci-eeprom.c deleted file mode 100644 index 32d43156c548..000000000000 --- a/drivers/media/dvb/ttpci/ttpci-eeprom.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM, - decode it and store it in the associated adapter struct for - use by dvb_net.c - - This card appear to have the 24C16 write protect held to ground, - thus permitting normal read/write operation. Theoretically it - would be possible to write routines to burn a different (encoded) - MAC address into the EEPROM. - - Robert Schlabbach GMX - Michael Glaum KVH Industries - Holger Waechtler Convergence - - Copyright (C) 2002-2003 Ralph Metzler - Metzler Brothers Systementwicklung GbR - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include - -#include "ttpci-eeprom.h" - -#if 1 -#define dprintk(x...) do { printk(x); } while (0) -#else -#define dprintk(x...) do { } while (0) -#endif - - -static int check_mac_tt(u8 *buf) -{ - int i; - u16 tmp = 0xffff; - - for (i = 0; i < 8; i++) { - tmp = (tmp << 8) | ((tmp >> 8) ^ buf[i]); - tmp ^= (tmp >> 4) & 0x0f; - tmp ^= (tmp << 12) ^ ((tmp & 0xff) << 5); - } - tmp ^= 0xffff; - return (((tmp >> 8) ^ buf[8]) | ((tmp & 0xff) ^ buf[9])); -} - -static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC) -{ - u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, - 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, - 0x1d, 0x36, 0x64, 0x78}; - u8 data[20]; - int i; - - /* In case there is a sig check failure have the orig contents available */ - memcpy(data, encodedMAC, 20); - - for (i = 0; i < 20; i++) - data[i] ^= xor[i]; - for (i = 0; i < 10; i++) - data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) - >> ((data[2 * i + 1] >> 6) & 3); - - if (check_mac_tt(data)) - return -ENODEV; - - decodedMAC[0] = data[2]; decodedMAC[1] = data[1]; decodedMAC[2] = data[0]; - decodedMAC[3] = data[6]; decodedMAC[4] = data[5]; decodedMAC[5] = data[4]; - return 0; -} - -int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC) -{ - u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, - 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, - 0x1d, 0x36, 0x64, 0x78}; - u8 data[20]; - int i; - - memcpy(data, encodedMAC, 20); - - for (i = 0; i < 20; i++) - data[i] ^= xor[i]; - for (i = 0; i < 10; i++) - data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) - >> ((data[2 * i + 1] >> 6) & 3); - - if (check_mac_tt(data)) - return -ENODEV; - - decodedMAC[0] = data[2]; - decodedMAC[1] = data[1]; - decodedMAC[2] = data[0]; - decodedMAC[3] = data[6]; - decodedMAC[4] = data[5]; - decodedMAC[5] = data[4]; - return 0; -} -EXPORT_SYMBOL(ttpci_eeprom_decode_mac); - -static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC) -{ - int ret; - u8 b0[] = { 0xcc }; - - struct i2c_msg msg[] = { - { .addr = 0x50, .flags = 0, .buf = b0, .len = 1 }, - { .addr = 0x50, .flags = I2C_M_RD, .buf = encodedMAC, .len = 20 } - }; - - /* dprintk("%s\n", __func__); */ - - ret = i2c_transfer(adapter, msg, 2); - - if (ret != 2) /* Assume EEPROM isn't there */ - return (-ENODEV); - - return 0; -} - - -int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) -{ - int ret, i; - u8 encodedMAC[20]; - u8 decodedMAC[6]; - - ret = ttpci_eeprom_read_encodedMAC(adapter, encodedMAC); - - if (ret != 0) { /* Will only be -ENODEV */ - dprintk("Couldn't read from EEPROM: not there?\n"); - memset(proposed_mac, 0, 6); - return ret; - } - - ret = getmac_tt(decodedMAC, encodedMAC); - if( ret != 0 ) { - dprintk("adapter failed MAC signature check\n"); - dprintk("encoded MAC from EEPROM was " ); - for(i=0; i<19; i++) { - dprintk( "%.2x:", encodedMAC[i]); - } - dprintk("%.2x\n", encodedMAC[19]); - memset(proposed_mac, 0, 6); - return ret; - } - - memcpy(proposed_mac, decodedMAC, 6); - dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", - decodedMAC[0], decodedMAC[1], decodedMAC[2], - decodedMAC[3], decodedMAC[4], decodedMAC[5]); - return 0; -} - -EXPORT_SYMBOL(ttpci_eeprom_parse_mac); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); -MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards " - "made by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.h b/drivers/media/dvb/ttpci/ttpci-eeprom.h deleted file mode 100644 index dcc33d5a5cb1..000000000000 --- a/drivers/media/dvb/ttpci/ttpci-eeprom.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM, - decode it and store it in associated adapter net device - - Robert Schlabbach GMX - Michael Glaum KVH Industries - Holger Waechtler Convergence - - This program is free software; 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 __TTPCI_EEPROM_H__ -#define __TTPCI_EEPROM_H__ - -#include -#include - -extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC); -extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac); - -#endif diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig new file mode 100644 index 000000000000..3b9164af6ec4 --- /dev/null +++ b/drivers/media/pci/Kconfig @@ -0,0 +1,50 @@ +# +# DVB device configuration +# + +menuconfig DVB_CAPTURE_DRIVERS + bool "DVB/ATSC adapters" + depends on DVB_CORE + default y + ---help--- + Say Y to select Digital TV adapters + +if DVB_CAPTURE_DRIVERS && DVB_CORE + +comment "Supported SAA7146 based PCI Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/pci/ttpci/Kconfig" + +comment "Supported FlexCopII (B2C2) Adapters" + depends on DVB_CORE && (PCI || USB) && I2C +source "drivers/media/pci/b2c2/Kconfig" + +comment "Supported BT878 Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/pci/bt8xx/Kconfig" + +comment "Supported Pluto2 Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/pci/pluto2/Kconfig" + +comment "Supported SDMC DM1105 Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/pci/dm1105/Kconfig" + +comment "Supported Earthsoft PT1 Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/pci/pt1/Kconfig" + +comment "Supported Mantis Adapters" + depends on DVB_CORE && PCI && I2C + source "drivers/media/pci/mantis/Kconfig" + +comment "Supported nGene Adapters" + depends on DVB_CORE && PCI && I2C + source "drivers/media/pci/ngene/Kconfig" + +comment "Supported ddbridge ('Octopus') Adapters" + depends on DVB_CORE && PCI && I2C + source "drivers/media/pci/ddbridge/Kconfig" + +endif # DVB_CAPTURE_DRIVERS diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile new file mode 100644 index 000000000000..c5fa43a275ae --- /dev/null +++ b/drivers/media/pci/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the kernel multimedia device drivers. +# + +obj-y := ttpci/ \ + b2c2/ \ + bt8xx/ \ + pluto2/ \ + dm1105/ \ + pt1/ \ + mantis/ \ + ngene/ \ + ddbridge/ diff --git a/drivers/media/pci/b2c2/Kconfig b/drivers/media/pci/b2c2/Kconfig new file mode 100644 index 000000000000..9e5781400744 --- /dev/null +++ b/drivers/media/pci/b2c2/Kconfig @@ -0,0 +1,45 @@ +config DVB_B2C2_FLEXCOP + tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters" + depends on DVB_CORE && I2C + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_MT312 if !DVB_FE_CUSTOMISE + select DVB_NXT200X if !DVB_FE_CUSTOMISE + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_BCM3510 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_S5H1420 if !DVB_FE_CUSTOMISE + select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE + select DVB_ISL6421 if !DVB_FE_CUSTOMISE + select DVB_CX24123 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE + help + Support for the digital TV receiver chip made by B2C2 Inc. included in + Technisats PCI cards and USB boxes. + + Say Y if you own such a device and want to use it. + +config DVB_B2C2_FLEXCOP_PCI + tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI" + depends on DVB_B2C2_FLEXCOP && PCI && I2C + help + Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2. + + Say Y if you own such a device and want to use it. + +config DVB_B2C2_FLEXCOP_USB + tristate "Technisat/B2C2 Air/Sky/Cable2PC USB" + depends on DVB_B2C2_FLEXCOP && USB && I2C + help + Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2, + + Say Y if you own such a device and want to use it. + +config DVB_B2C2_FLEXCOP_DEBUG + bool "Enable debug for the B2C2 FlexCop drivers" + depends on DVB_B2C2_FLEXCOP + help + Say Y if you want to enable the module option to control debug messages + of all B2C2 FlexCop drivers. diff --git a/drivers/media/pci/b2c2/Makefile b/drivers/media/pci/b2c2/Makefile new file mode 100644 index 000000000000..7a1f5ce6d322 --- /dev/null +++ b/drivers/media/pci/b2c2/Makefile @@ -0,0 +1,16 @@ +b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ + flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o +obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o + +ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),) +b2c2-flexcop-objs += flexcop-dma.o +endif + +b2c2-flexcop-pci-objs = flexcop-pci.o +obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o + +b2c2-flexcop-usb-objs = flexcop-usb.o +obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o + +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ +ccflags-y += -Idrivers/media/common/tuners/ diff --git a/drivers/media/pci/b2c2/flexcop-common.h b/drivers/media/pci/b2c2/flexcop-common.h new file mode 100644 index 000000000000..437912e49824 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-common.h @@ -0,0 +1,185 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-common.h - common header file for device-specific source files + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_COMMON_H__ +#define __FLEXCOP_COMMON_H__ + +#include +#include +#include + +#include "flexcop-reg.h" + +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_filter.h" +#include "dvb_net.h" +#include "dvb_frontend.h" + +#define FC_MAX_FEED 256 + +#ifndef FC_LOG_PREFIX +#warning please define a log prefix for your file, using a default one +#define FC_LOG_PREFIX "b2c2-undef" +#endif + +/* Steal from usb.h */ +#undef err +#define err(format, arg...) \ + printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg) +#undef info +#define info(format, arg...) \ + printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) \ + printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg) + +struct flexcop_dma { + struct pci_dev *pdev; + + u8 *cpu_addr0; + dma_addr_t dma_addr0; + u8 *cpu_addr1; + dma_addr_t dma_addr1; + u32 size; /* size of each address in bytes */ +}; + +struct flexcop_i2c_adapter { + struct flexcop_device *fc; + struct i2c_adapter i2c_adap; + + u8 no_base_addr; + flexcop_i2c_port_t port; +}; + +/* Control structure for data definitions that are common to + * the B2C2-based PCI and USB devices. + */ +struct flexcop_device { + /* general */ + struct device *dev; /* for firmware_class */ + +#define FC_STATE_DVB_INIT 0x01 +#define FC_STATE_I2C_INIT 0x02 +#define FC_STATE_FE_INIT 0x04 + int init_state; + + /* device information */ + int has_32_hw_pid_filter; + flexcop_revision_t rev; + flexcop_device_type_t dev_type; + flexcop_bus_t bus_type; + + /* dvb stuff */ + struct dvb_adapter dvb_adapter; + struct dvb_frontend *fe; + struct dvb_net dvbnet; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + int (*fe_sleep) (struct dvb_frontend *); + + struct flexcop_i2c_adapter fc_i2c_adap[3]; + struct mutex i2c_mutex; + struct module *owner; + + /* options and status */ + int extra_feedcount; + int feedcount; + int pid_filtering; + int fullts_streaming_state; + + /* bus specific callbacks */ + flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *, + flexcop_ibi_register); + int (*write_ibi_reg) (struct flexcop_device *, + flexcop_ibi_register, flexcop_ibi_value); + int (*i2c_request) (struct flexcop_i2c_adapter *, + flexcop_access_op_t, u8 chipaddr, u8 addr, u8 *buf, u16 len); + int (*stream_control) (struct flexcop_device *, int); + int (*get_mac_addr) (struct flexcop_device *fc, int extended); + void *bus_specific; +}; + +/* exported prototypes */ + +/* from flexcop.c */ +void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len); +void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no); + +struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len); +void flexcop_device_kfree(struct flexcop_device *); + +int flexcop_device_initialize(struct flexcop_device *); +void flexcop_device_exit(struct flexcop_device *fc); +void flexcop_reset_block_300(struct flexcop_device *fc); + +/* from flexcop-dma.c */ +int flexcop_dma_allocate(struct pci_dev *pdev, + struct flexcop_dma *dma, u32 size); +void flexcop_dma_free(struct flexcop_dma *dma); + +int flexcop_dma_control_timer_irq(struct flexcop_device *fc, + flexcop_dma_index_t no, int onoff); +int flexcop_dma_control_size_irq(struct flexcop_device *fc, + flexcop_dma_index_t no, int onoff); +int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, + flexcop_dma_index_t dma_idx); +int flexcop_dma_xfer_control(struct flexcop_device *fc, + flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index, + int onoff); +int flexcop_dma_config_timer(struct flexcop_device *fc, + flexcop_dma_index_t dma_idx, u8 cycles); + +/* from flexcop-eeprom.c */ +/* the PCI part uses this call to get the MAC address, the USB part has its own */ +int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended); + +/* from flexcop-i2c.c */ +/* the PCI part uses this a i2c_request callback, whereas the usb part has its own + * one. We have it in flexcop-i2c.c, because it is going via the actual + * I2C-channel of the flexcop. + */ +int flexcop_i2c_request(struct flexcop_i2c_adapter*, flexcop_access_op_t, + u8 chipaddr, u8 addr, u8 *buf, u16 len); + +/* from flexcop-sram.c */ +int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, + flexcop_sram_dest_target_t target); +void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s); +void flexcop_sram_ctrl(struct flexcop_device *fc, + int usb_wan, int sramdma, int maximumfill); + +/* global prototypes for the flexcop-chip */ +/* from flexcop-fe-tuner.c */ +int flexcop_frontend_init(struct flexcop_device *fc); +void flexcop_frontend_exit(struct flexcop_device *fc); + +/* from flexcop-i2c.c */ +int flexcop_i2c_init(struct flexcop_device *fc); +void flexcop_i2c_exit(struct flexcop_device *fc); + +/* from flexcop-sram.c */ +int flexcop_sram_init(struct flexcop_device *fc); + +/* from flexcop-misc.c */ +void flexcop_determine_revision(struct flexcop_device *fc); +void flexcop_device_name(struct flexcop_device *fc, + const char *prefix, const char *suffix); +void flexcop_dump_reg(struct flexcop_device *fc, + flexcop_ibi_register reg, int num); + +/* from flexcop-hw-filter.c */ +int flexcop_pid_feed_control(struct flexcop_device *fc, + struct dvb_demux_feed *dvbdmxfeed, int onoff); +void flexcop_hw_filter_init(struct flexcop_device *fc); + +void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff); + +void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]); +void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff); + +#endif diff --git a/drivers/media/pci/b2c2/flexcop-dma.c b/drivers/media/pci/b2c2/flexcop-dma.c new file mode 100644 index 000000000000..2881e0d956ad --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-dma.c @@ -0,0 +1,172 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-dma.c - configuring and controlling the DMA of the FlexCop + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +int flexcop_dma_allocate(struct pci_dev *pdev, + struct flexcop_dma *dma, u32 size) +{ + u8 *tcpu; + dma_addr_t tdma = 0; + + if (size % 2) { + err("dma buffersize has to be even."); + return -EINVAL; + } + + if ((tcpu = pci_alloc_consistent(pdev, size, &tdma)) != NULL) { + dma->pdev = pdev; + dma->cpu_addr0 = tcpu; + dma->dma_addr0 = tdma; + dma->cpu_addr1 = tcpu + size/2; + dma->dma_addr1 = tdma + size/2; + dma->size = size/2; + return 0; + } + return -ENOMEM; +} +EXPORT_SYMBOL(flexcop_dma_allocate); + +void flexcop_dma_free(struct flexcop_dma *dma) +{ + pci_free_consistent(dma->pdev, dma->size*2, + dma->cpu_addr0, dma->dma_addr0); + memset(dma,0,sizeof(struct flexcop_dma)); +} +EXPORT_SYMBOL(flexcop_dma_free); + +int flexcop_dma_config(struct flexcop_device *fc, + struct flexcop_dma *dma, + flexcop_dma_index_t dma_idx) +{ + flexcop_ibi_value v0x0,v0x4,v0xc; + v0x0.raw = v0x4.raw = v0xc.raw = 0; + + v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2; + v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2; + v0x4.dma_0x4_write.dma_addr_size = dma->size / 4; + + if ((dma_idx & FC_DMA_1) == dma_idx) { + fc->write_ibi_reg(fc,dma1_000,v0x0); + fc->write_ibi_reg(fc,dma1_004,v0x4); + fc->write_ibi_reg(fc,dma1_00c,v0xc); + } else if ((dma_idx & FC_DMA_2) == dma_idx) { + fc->write_ibi_reg(fc,dma2_010,v0x0); + fc->write_ibi_reg(fc,dma2_014,v0x4); + fc->write_ibi_reg(fc,dma2_01c,v0xc); + } else { + err("either DMA1 or DMA2 can be configured within one " + "flexcop_dma_config call."); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(flexcop_dma_config); + +/* start the DMA transfers, but not the DMA IRQs */ +int flexcop_dma_xfer_control(struct flexcop_device *fc, + flexcop_dma_index_t dma_idx, + flexcop_dma_addr_index_t index, + int onoff) +{ + flexcop_ibi_value v0x0,v0xc; + flexcop_ibi_register r0x0,r0xc; + + if ((dma_idx & FC_DMA_1) == dma_idx) { + r0x0 = dma1_000; + r0xc = dma1_00c; + } else if ((dma_idx & FC_DMA_2) == dma_idx) { + r0x0 = dma2_010; + r0xc = dma2_01c; + } else { + err("either transfer DMA1 or DMA2 can be started within one " + "flexcop_dma_xfer_control call."); + return -EINVAL; + } + + v0x0 = fc->read_ibi_reg(fc,r0x0); + v0xc = fc->read_ibi_reg(fc,r0xc); + + deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); + deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); + + if (index & FC_DMA_SUBADDR_0) + v0x0.dma_0x0.dma_0start = onoff; + + if (index & FC_DMA_SUBADDR_1) + v0xc.dma_0xc.dma_1start = onoff; + + fc->write_ibi_reg(fc,r0x0,v0x0); + fc->write_ibi_reg(fc,r0xc,v0xc); + + deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); + deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); + return 0; +} +EXPORT_SYMBOL(flexcop_dma_xfer_control); + +static int flexcop_dma_remap(struct flexcop_device *fc, + flexcop_dma_index_t dma_idx, + int onoff) +{ + flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c; + flexcop_ibi_value v = fc->read_ibi_reg(fc,r); + deb_info("%s\n",__func__); + v.dma_0xc.remap_enable = onoff; + fc->write_ibi_reg(fc,r,v); + return 0; +} + +int flexcop_dma_control_size_irq(struct flexcop_device *fc, + flexcop_dma_index_t no, + int onoff) +{ + flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); + + if (no & FC_DMA_1) + v.ctrl_208.DMA1_IRQ_Enable_sig = onoff; + + if (no & FC_DMA_2) + v.ctrl_208.DMA2_IRQ_Enable_sig = onoff; + + fc->write_ibi_reg(fc,ctrl_208,v); + return 0; +} +EXPORT_SYMBOL(flexcop_dma_control_size_irq); + +int flexcop_dma_control_timer_irq(struct flexcop_device *fc, + flexcop_dma_index_t no, + int onoff) +{ + flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); + + if (no & FC_DMA_1) + v.ctrl_208.DMA1_Timer_Enable_sig = onoff; + + if (no & FC_DMA_2) + v.ctrl_208.DMA2_Timer_Enable_sig = onoff; + + fc->write_ibi_reg(fc,ctrl_208,v); + return 0; +} +EXPORT_SYMBOL(flexcop_dma_control_timer_irq); + +/* 1 cycles = 1.97 msec */ +int flexcop_dma_config_timer(struct flexcop_device *fc, + flexcop_dma_index_t dma_idx, u8 cycles) +{ + flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014; + flexcop_ibi_value v = fc->read_ibi_reg(fc,r); + + flexcop_dma_remap(fc,dma_idx,0); + + deb_info("%s\n",__func__); + v.dma_0x4_write.dmatimer = cycles; + fc->write_ibi_reg(fc,r,v); + return 0; +} +EXPORT_SYMBOL(flexcop_dma_config_timer); + diff --git a/drivers/media/pci/b2c2/flexcop-eeprom.c b/drivers/media/pci/b2c2/flexcop-eeprom.c new file mode 100644 index 000000000000..a25373a9bd84 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-eeprom.c @@ -0,0 +1,147 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-eeprom.c - eeprom access methods (currently only MAC address reading) + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +#if 0 +/*EEPROM (Skystar2 has one "24LC08B" chip on board) */ +static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len) +{ + return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len); +} + +static int eeprom_lrc_write(struct adapter *adapter, u32 addr, + u32 len, u8 *wbuf, u8 *rbuf, int retries) +{ +int i; + +for (i = 0; i < retries; i++) { + if (eeprom_write(adapter, addr, wbuf, len) == len) { + if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1) + return 1; + } + } + return 0; +} + +/* These functions could be used to unlock SkyStar2 cards. */ + +static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len) +{ + u8 rbuf[20]; + u8 wbuf[20]; + + if (len != 16) + return 0; + + memcpy(wbuf, key, len); + wbuf[16] = 0; + wbuf[17] = 0; + wbuf[18] = 0; + wbuf[19] = calc_lrc(wbuf, 19); + return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4); +} + +static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len) +{ + u8 buf[20]; + + if (len != 16) + return 0; + + if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0) + return 0; + + memcpy(key, buf, len); + return 1; +} + +static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac) +{ + u8 tmp[8]; + + if (type != 0) { + tmp[0] = mac[0]; + tmp[1] = mac[1]; + tmp[2] = mac[2]; + tmp[3] = mac[5]; + tmp[4] = mac[6]; + tmp[5] = mac[7]; + } else { + tmp[0] = mac[0]; + tmp[1] = mac[1]; + tmp[2] = mac[2]; + tmp[3] = mac[3]; + tmp[4] = mac[4]; + tmp[5] = mac[5]; + } + + tmp[6] = 0; + tmp[7] = calc_lrc(tmp, 7); + + if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8) + return 1; + return 0; +} + +static int flexcop_eeprom_read(struct flexcop_device *fc, + u16 addr, u8 *buf, u16 len) +{ + return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len); +} + +#endif + +static u8 calc_lrc(u8 *buf, int len) +{ + int i; + u8 sum = 0; + for (i = 0; i < len; i++) + sum = sum ^ buf[i]; + return sum; +} + +static int flexcop_eeprom_request(struct flexcop_device *fc, + flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries) +{ + int i,ret = 0; + u8 chipaddr = 0x50 | ((addr >> 8) & 3); + for (i = 0; i < retries; i++) { + ret = fc->i2c_request(&fc->fc_i2c_adap[1], op, chipaddr, + addr & 0xff, buf, len); + if (ret == 0) + break; + } + return ret; +} + +static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, + u8 *buf, u16 len, int retries) +{ + int ret = flexcop_eeprom_request(fc, FC_READ, addr, buf, len, retries); + if (ret == 0) + if (calc_lrc(buf, len - 1) != buf[len - 1]) + ret = -EINVAL; + return ret; +} + +/* JJ's comment about extended == 1: it is not presently used anywhere but was + * added to the low-level functions for possible support of EUI64 */ +int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended) +{ + u8 buf[8]; + int ret = 0; + + if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) { + if (extended != 0) { + err("TODO: extended (EUI64) MAC addresses aren't " + "completely supported yet"); + ret = -EINVAL; + } else + memcpy(fc->dvb_adapter.proposed_mac,buf,6); + } + return ret; +} +EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr); diff --git a/drivers/media/pci/b2c2/flexcop-fe-tuner.c b/drivers/media/pci/b2c2/flexcop-fe-tuner.c new file mode 100644 index 000000000000..850a6c606750 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-fe-tuner.c @@ -0,0 +1,678 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling + * see flexcop.c for copyright information + */ +#include +#include "flexcop.h" +#include "mt312.h" +#include "stv0299.h" +#include "s5h1420.h" +#include "itd1000.h" +#include "cx24113.h" +#include "cx24123.h" +#include "isl6421.h" +#include "mt352.h" +#include "bcm3510.h" +#include "nxt200x.h" +#include "dvb-pll.h" +#include "lgdt330x.h" +#include "tuner-simple.h" +#include "stv0297.h" + + +/* Can we use the specified front-end? Remember that if we are compiled + * into the kernel we can't call code that's in modules. */ +#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ + (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) + +/* lnb control */ +#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) +static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct flexcop_device *fc = fe->dvb->priv; + flexcop_ibi_value v; + deb_tuner("polarity/voltage = %u\n", voltage); + + v = fc->read_ibi_reg(fc, misc_204); + switch (voltage) { + case SEC_VOLTAGE_OFF: + v.misc_204.ACPI1_sig = 1; + break; + case SEC_VOLTAGE_13: + v.misc_204.ACPI1_sig = 0; + v.misc_204.LNB_L_H_sig = 0; + break; + case SEC_VOLTAGE_18: + v.misc_204.ACPI1_sig = 0; + v.misc_204.LNB_L_H_sig = 1; + break; + default: + err("unknown SEC_VOLTAGE value"); + return -EINVAL; + } + return fc->write_ibi_reg(fc, misc_204, v); +} +#endif + +#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312) +static int flexcop_sleep(struct dvb_frontend* fe) +{ + struct flexcop_device *fc = fe->dvb->priv; + if (fc->fe_sleep) + return fc->fe_sleep(fe); + return 0; +} +#endif + +/* SkyStar2 DVB-S rev 2.3 */ +#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL) +static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ +/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ + struct flexcop_device *fc = fe->dvb->priv; + flexcop_ibi_value v; + u16 ax; + v.raw = 0; + deb_tuner("tone = %u\n",tone); + + switch (tone) { + case SEC_TONE_ON: + ax = 0x01ff; + break; + case SEC_TONE_OFF: + ax = 0; + break; + default: + err("unknown SEC_TONE value"); + return -EINVAL; + } + + v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */ + v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax; + v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax; + return fc->write_ibi_reg(fc,lnb_switch_freq_200,v); +} + +static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data) +{ + flexcop_set_tone(fe, SEC_TONE_ON); + udelay(data ? 500 : 1000); + flexcop_set_tone(fe, SEC_TONE_OFF); + udelay(data ? 1000 : 500); +} + +static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data) +{ + int i, par = 1, d; + for (i = 7; i >= 0; i--) { + d = (data >> i) & 1; + par ^= d; + flexcop_diseqc_send_bit(fe, d); + } + flexcop_diseqc_send_bit(fe, par); +} + +static int flexcop_send_diseqc_msg(struct dvb_frontend *fe, + int len, u8 *msg, unsigned long burst) +{ + int i; + + flexcop_set_tone(fe, SEC_TONE_OFF); + mdelay(16); + + for (i = 0; i < len; i++) + flexcop_diseqc_send_byte(fe,msg[i]); + mdelay(16); + + if (burst != -1) { + if (burst) + flexcop_diseqc_send_byte(fe, 0xff); + else { + flexcop_set_tone(fe, SEC_TONE_ON); + mdelay(12); + udelay(500); + flexcop_set_tone(fe, SEC_TONE_OFF); + } + msleep(20); + } + return 0; +} + +static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0); +} + +static int flexcop_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t minicmd) +{ + return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd); +} + +static struct mt312_config skystar23_samsung_tbdu18132_config = { + .demod_address = 0x0e, +}; + +static int skystar2_rev23_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + struct dvb_frontend_ops *ops; + + fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c); + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBDU18132)) + return 0; + + ops = &fc->fe->ops; + ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; + ops->diseqc_send_burst = flexcop_diseqc_send_burst; + ops->set_tone = flexcop_set_tone; + ops->set_voltage = flexcop_set_voltage; + fc->fe_sleep = ops->sleep; + ops->sleep = flexcop_sleep; + return 1; +} +#else +#define skystar2_rev23_attach NULL +#endif + +/* SkyStar2 DVB-S rev 2.6 */ +#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL) +static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; bclk = 0x47; + } else if (srate < 3000000) { + aclk = 0xb7; bclk = 0x4b; + } else if (srate < 7000000) { + aclk = 0xb7; bclk = 0x4f; + } else if (srate < 14000000) { + aclk = 0xb7; bclk = 0x53; + } else if (srate < 30000000) { + aclk = 0xb6; bclk = 0x53; + } else if (srate < 45000000) { + aclk = 0xb4; bclk = 0x51; + } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, ratio & 0xf0); + return 0; +} + +static u8 samsung_tbmu24112_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7D, + 0x05, 0x35, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0xC3, + 0x0C, 0x00, + 0x0D, 0x81, + 0x0E, 0x23, + 0x0F, 0x12, + 0x10, 0x7E, + 0x11, 0x84, + 0x12, 0xB9, + 0x13, 0x88, + 0x14, 0x89, + 0x15, 0xC9, + 0x16, 0x00, + 0x17, 0x5C, + 0x18, 0x00, + 0x19, 0x00, + 0x1A, 0x00, + 0x1C, 0x00, + 0x1D, 0x00, + 0x1E, 0x00, + 0x1F, 0x3A, + 0x20, 0x2E, + 0x21, 0x80, + 0x22, 0xFF, + 0x23, 0xC1, + 0x28, 0x00, + 0x29, 0x1E, + 0x2A, 0x14, + 0x2B, 0x0F, + 0x2C, 0x09, + 0x2D, 0x05, + 0x31, 0x1F, + 0x32, 0x19, + 0x33, 0xFE, + 0x34, 0x93, + 0xff, 0xff, +}; + +static struct stv0299_config samsung_tbmu24112_config = { + .demod_address = 0x68, + .inittab = samsung_tbmu24112_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_LK, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = samsung_tbmu24112_set_symbol_rate, +}; + +static int skystar2_rev26_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c); + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBMU24112)) + return 0; + + fc->fe->ops.set_voltage = flexcop_set_voltage; + fc->fe_sleep = fc->fe->ops.sleep; + fc->fe->ops.sleep = flexcop_sleep; + return 1; + +} +#else +#define skystar2_rev26_attach NULL +#endif + +/* SkyStar2 DVB-S rev 2.7 */ +#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000) +static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { + .demod_address = 0x53, + .invert = 1, + .repeated_start_workaround = 1, + .serial_mpeg = 1, +}; + +static struct itd1000_config skystar2_rev2_7_itd1000_config = { + .i2c_address = 0x61, +}; + +static int skystar2_rev27_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + flexcop_ibi_value r108; + struct i2c_adapter *i2c_tuner; + + /* enable no_base_addr - no repeated start when reading */ + fc->fc_i2c_adap[0].no_base_addr = 1; + fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config, + i2c); + if (!fc->fe) + goto fail; + + i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe); + if (!i2c_tuner) + goto fail; + + fc->fe_sleep = fc->fe->ops.sleep; + fc->fe->ops.sleep = flexcop_sleep; + + /* enable no_base_addr - no repeated start when reading */ + fc->fc_i2c_adap[2].no_base_addr = 1; + if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, + 0x08, 1, 1)) { + err("ISL6421 could NOT be attached"); + goto fail_isl; + } + info("ISL6421 successfully attached"); + + /* the ITD1000 requires a lower i2c clock - is it a problem ? */ + r108.raw = 0x00000506; + fc->write_ibi_reg(fc, tw_sm_c_108, r108); + if (!dvb_attach(itd1000_attach, fc->fe, i2c_tuner, + &skystar2_rev2_7_itd1000_config)) { + err("ITD1000 could NOT be attached"); + /* Should i2c clock be restored? */ + goto fail_isl; + } + info("ITD1000 successfully attached"); + + return 1; + +fail_isl: + fc->fc_i2c_adap[2].no_base_addr = 0; +fail: + /* for the next devices we need it again */ + fc->fc_i2c_adap[0].no_base_addr = 0; + return 0; +} +#else +#define skystar2_rev27_attach NULL +#endif + +/* SkyStar2 rev 2.8 */ +#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113) +static struct cx24123_config skystar2_rev2_8_cx24123_config = { + .demod_address = 0x55, + .dont_use_pll = 1, + .agc_callback = cx24113_agc_callback, +}; + +static const struct cx24113_config skystar2_rev2_8_cx24113_config = { + .i2c_addr = 0x54, + .xtal_khz = 10111, +}; + +static int skystar2_rev28_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + struct i2c_adapter *i2c_tuner; + + fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config, + i2c); + if (!fc->fe) + return 0; + + i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe); + if (!i2c_tuner) + return 0; + + if (!dvb_attach(cx24113_attach, fc->fe, &skystar2_rev2_8_cx24113_config, + i2c_tuner)) { + err("CX24113 could NOT be attached"); + return 0; + } + info("CX24113 successfully attached"); + + fc->fc_i2c_adap[2].no_base_addr = 1; + if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, + 0x08, 0, 0)) { + err("ISL6421 could NOT be attached"); + fc->fc_i2c_adap[2].no_base_addr = 0; + return 0; + } + info("ISL6421 successfully attached"); + /* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an + * IR-receiver (PIC16F818) - but the card has no input for that ??? */ + return 1; +} +#else +#define skystar2_rev28_attach NULL +#endif + +/* AirStar DVB-T */ +#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL) +static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) +{ + static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; + static u8 mt352_reset[] = { 0x50, 0x80 }; + static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg[] = { 0x67, 0x28, 0xa1 }; + static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); + return 0; +} + +static struct mt352_config samsung_tdtc9251dh0_config = { + .demod_address = 0x0f, + .demod_init = samsung_tdtc9251dh0_demod_init, +}; + +static int airstar_dvbt_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c); + if (!fc->fe) + return 0; + + return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, + DVB_PLL_SAMSUNG_TDTC9251DH0); +} +#else +#define airstar_dvbt_attach NULL +#endif + +/* AirStar ATSC 1st generation */ +#if FE_SUPPORTED(BCM3510) +static int flexcop_fe_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char* name) +{ + struct flexcop_device *fc = fe->dvb->priv; + return request_firmware(fw, name, fc->dev); +} + +static struct bcm3510_config air2pc_atsc_first_gen_config = { + .demod_address = 0x0f, + .request_firmware = flexcop_fe_request_firmware, +}; + +static int airstar_atsc1_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c); + return fc->fe != NULL; +} +#else +#define airstar_atsc1_attach NULL +#endif + +/* AirStar ATSC 2nd generation */ +#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL) +static struct nxt200x_config samsung_tbmv_config = { + .demod_address = 0x0a, +}; + +static int airstar_atsc2_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c); + if (!fc->fe) + return 0; + + return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, + DVB_PLL_SAMSUNG_TBMV); +} +#else +#define airstar_atsc2_attach NULL +#endif + +/* AirStar ATSC 3rd generation */ +#if FE_SUPPORTED(LGDT330X) +static struct lgdt330x_config air2pc_atsc_hd5000_config = { + .demod_address = 0x59, + .demod_chip = LGDT3303, + .serial_mpeg = 0x04, + .clock_polarity_flip = 1, +}; + +static int airstar_atsc3_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c); + if (!fc->fe) + return 0; + + return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61, + TUNER_LG_TDVS_H06XF); +} +#else +#define airstar_atsc3_attach NULL +#endif + +/* CableStar2 DVB-C */ +#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL) +static u8 alps_tdee4_stv0297_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x48, + 0x01, 0x58, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x30, 0xff, + 0x31, 0x9d, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x29, + 0x35, 0x55, + 0x36, 0x80, + 0x37, 0x6e, + 0x38, 0x9c, + 0x40, 0x1a, + 0x41, 0xfe, + 0x42, 0x33, + 0x43, 0x00, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x51, + 0x4b, 0xf8, + 0x52, 0x30, + 0x53, 0x06, + 0x59, 0x06, + 0x5a, 0x5e, + 0x5b, 0x04, + 0x61, 0x49, + 0x62, 0x0a, + 0x70, 0xff, + 0x71, 0x04, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x20, + 0x81, 0x00, + 0x82, 0x30, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x22, + 0x86, 0x08, + 0x87, 0x1b, + 0x88, 0x00, + 0x89, 0x00, + 0x90, 0x00, + 0x91, 0x04, + 0xa0, 0x86, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x5b, + 0xc1, 0x10, + 0xc2, 0x12, + 0xd0, 0x02, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x02, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x01, + 0xff, 0xff, +}; + +static struct stv0297_config alps_tdee4_stv0297_config = { + .demod_address = 0x1c, + .inittab = alps_tdee4_stv0297_inittab, +}; + +static int cablestar2_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fc_i2c_adap[0].no_base_addr = 1; + fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); + if (!fc->fe) + goto fail; + + /* This tuner doesn't use the stv0297's I2C gate, but instead the + * tuner is connected to a different flexcop I2C adapter. */ + if (fc->fe->ops.i2c_gate_ctrl) + fc->fe->ops.i2c_gate_ctrl(fc->fe, 0); + fc->fe->ops.i2c_gate_ctrl = NULL; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, + &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) + goto fail; + + return 1; + +fail: + /* Reset for next frontend to try */ + fc->fc_i2c_adap[0].no_base_addr = 0; + return 0; +} +#else +#define cablestar2_attach NULL +#endif + +static struct { + flexcop_device_type_t type; + int (*attach)(struct flexcop_device *, struct i2c_adapter *); +} flexcop_frontends[] = { + { FC_SKY_REV27, skystar2_rev27_attach }, + { FC_SKY_REV28, skystar2_rev28_attach }, + { FC_SKY_REV26, skystar2_rev26_attach }, + { FC_AIR_DVBT, airstar_dvbt_attach }, + { FC_AIR_ATSC2, airstar_atsc2_attach }, + { FC_AIR_ATSC3, airstar_atsc3_attach }, + { FC_AIR_ATSC1, airstar_atsc1_attach }, + { FC_CABLE, cablestar2_attach }, + { FC_SKY_REV23, skystar2_rev23_attach }, +}; + +/* try to figure out the frontend */ +int flexcop_frontend_init(struct flexcop_device *fc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) { + if (!flexcop_frontends[i].attach) + continue; + /* type needs to be set before, because of some workarounds + * done based on the probed card type */ + fc->dev_type = flexcop_frontends[i].type; + if (flexcop_frontends[i].attach(fc, &fc->fc_i2c_adap[0].i2c_adap)) + goto fe_found; + /* Clean up partially attached frontend */ + if (fc->fe) { + dvb_frontend_detach(fc->fe); + fc->fe = NULL; + } + } + fc->dev_type = FC_UNK; + err("no frontend driver found for this B2C2/FlexCop adapter"); + return -ENODEV; + +fe_found: + info("found '%s' .", fc->fe->ops.info.name); + if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) { + err("frontend registration failed!"); + dvb_frontend_detach(fc->fe); + fc->fe = NULL; + return -EINVAL; + } + fc->init_state |= FC_STATE_FE_INIT; + return 0; +} + +void flexcop_frontend_exit(struct flexcop_device *fc) +{ + if (fc->init_state & FC_STATE_FE_INIT) { + dvb_unregister_frontend(fc->fe); + dvb_frontend_detach(fc->fe); + } + fc->init_state &= ~FC_STATE_FE_INIT; +} diff --git a/drivers/media/pci/b2c2/flexcop-hw-filter.c b/drivers/media/pci/b2c2/flexcop-hw-filter.c new file mode 100644 index 000000000000..77e45475f4c7 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-hw-filter.c @@ -0,0 +1,232 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-hw-filter.c - pid and mac address filtering and control functions + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff); + deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off"); +} + +void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff); +} + +static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff); +} + +void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]) +{ + flexcop_ibi_value v418, v41c; + v41c = fc->read_ibi_reg(fc, mac_address_41c); + + v418.mac_address_418.MAC1 = mac[0]; + v418.mac_address_418.MAC2 = mac[1]; + v418.mac_address_418.MAC3 = mac[2]; + v418.mac_address_418.MAC6 = mac[3]; + v41c.mac_address_41c.MAC7 = mac[4]; + v41c.mac_address_41c.MAC8 = mac[5]; + + fc->write_ibi_reg(fc, mac_address_418, v418); + fc->write_ibi_reg(fc, mac_address_41c, v41c); +} + +void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff); +} + +static void flexcop_pid_group_filter(struct flexcop_device *fc, + u16 pid, u16 mask) +{ + /* index_reg_310.extra_index_reg need to 0 or 7 to work */ + flexcop_ibi_value v30c; + v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid; + v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask; + fc->write_ibi_reg(fc, pid_filter_30c, v30c); +} + +static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff); +} + +/* this fancy define reduces the code size of the quite similar PID controlling of + * the first 6 PIDs + */ + +#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \ + flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \ +v208 = fc->read_ibi_reg(fc, ctrl_208); \ +vpid.vregname.field = onoff ? pid : 0x1fff; \ +vpid.vregname.trans_field = transval; \ +v208.ctrl_208.enablefield = onoff; \ +fc->write_ibi_reg(fc, vregname, vpid); \ +fc->write_ibi_reg(fc, ctrl_208, v208); + +static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig, + Stream1_trans, 0); +} + +static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig, + Stream2_trans, 0); +} + +static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0); +} + +static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0); +} + +static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0); +} + +static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0); +} + +static void flexcop_pid_control(struct flexcop_device *fc, + int index, u16 pid, int onoff) +{ + if (pid == 0x2000) + return; + + deb_ts("setting pid: %5d %04x at index %d '%s'\n", + pid, pid, index, onoff ? "on" : "off"); + + /* We could use bit magic here to reduce source code size. + * I decided against it, but to use the real register names */ + switch (index) { + case 0: + flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff); + break; + case 1: + flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff); + break; + case 2: + flexcop_pid_PCR_PID_ctrl(fc, pid, onoff); + break; + case 3: + flexcop_pid_PMT_PID_ctrl(fc, pid, onoff); + break; + case 4: + flexcop_pid_EMM_PID_ctrl(fc, pid, onoff); + break; + case 5: + flexcop_pid_ECM_PID_ctrl(fc, pid, onoff); + break; + default: + if (fc->has_32_hw_pid_filter && index < 38) { + flexcop_ibi_value vpid, vid; + + /* set the index */ + vid = fc->read_ibi_reg(fc, index_reg_310); + vid.index_reg_310.index_reg = index - 6; + fc->write_ibi_reg(fc, index_reg_310, vid); + + vpid = fc->read_ibi_reg(fc, pid_n_reg_314); + vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff; + vpid.pid_n_reg_314.PID_enable_bit = onoff; + fc->write_ibi_reg(fc, pid_n_reg_314, vpid); + } + break; + } +} + +static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff) +{ + if (fc->fullts_streaming_state != onoff) { + deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling"); + flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff)); + flexcop_pid_group_filter_ctrl(fc, onoff); + fc->fullts_streaming_state = onoff; + } + return 0; +} + +int flexcop_pid_feed_control(struct flexcop_device *fc, + struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32; + + fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */ + if (dvbdmxfeed->index >= max_pid_filter) + fc->extra_feedcount += onoff ? 1 : -1; + + /* toggle complete-TS-streaming when: + * - pid_filtering is not enabled and it is the first or last feed requested + * - pid_filtering is enabled, + * - but the number of requested feeds is exceeded + * - or the requested pid is 0x2000 */ + + if (!fc->pid_filtering && fc->feedcount == onoff) + flexcop_toggle_fullts_streaming(fc, onoff); + + if (fc->pid_filtering) { + flexcop_pid_control \ + (fc, dvbdmxfeed->index, dvbdmxfeed->pid, onoff); + + if (fc->extra_feedcount > 0) + flexcop_toggle_fullts_streaming(fc, 1); + else if (dvbdmxfeed->pid == 0x2000) + flexcop_toggle_fullts_streaming(fc, onoff); + else + flexcop_toggle_fullts_streaming(fc, 0); + } + + /* if it was the first or last feed request change the stream-status */ + if (fc->feedcount == onoff) { + flexcop_rcv_data_ctrl(fc, onoff); + if (fc->stream_control) /* device specific stream control */ + fc->stream_control(fc, onoff); + + /* feeding stopped -> reset the flexcop filter*/ + if (onoff == 0) { + flexcop_reset_block_300(fc); + flexcop_hw_filter_init(fc); + } + } + return 0; +} +EXPORT_SYMBOL(flexcop_pid_feed_control); + +void flexcop_hw_filter_init(struct flexcop_device *fc) +{ + int i; + flexcop_ibi_value v; + for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++) + flexcop_pid_control(fc, i, 0x1fff, 0); + + flexcop_pid_group_filter(fc, 0, 0x1fe0); + flexcop_pid_group_filter_ctrl(fc, 0); + + v = fc->read_ibi_reg(fc, pid_filter_308); + v.pid_filter_308.EMM_filter_4 = 1; + v.pid_filter_308.EMM_filter_6 = 0; + fc->write_ibi_reg(fc, pid_filter_308, v); + + flexcop_null_filter_ctrl(fc, 1); +} diff --git a/drivers/media/pci/b2c2/flexcop-i2c.c b/drivers/media/pci/b2c2/flexcop-i2c.c new file mode 100644 index 000000000000..965d5eb33752 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-i2c.c @@ -0,0 +1,288 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +#define FC_MAX_I2C_RETRIES 100000 + +static int flexcop_i2c_operation(struct flexcop_device *fc, + flexcop_ibi_value *r100) +{ + int i; + flexcop_ibi_value r; + + r100->tw_sm_c_100.working_start = 1; + deb_i2c("r100 before: %08x\n",r100->raw); + + fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero); + fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */ + + for (i = 0; i < FC_MAX_I2C_RETRIES; i++) { + r = fc->read_ibi_reg(fc, tw_sm_c_100); + + if (!r.tw_sm_c_100.no_base_addr_ack_error) { + if (r.tw_sm_c_100.st_done) { + *r100 = r; + deb_i2c("i2c success\n"); + return 0; + } + } else { + deb_i2c("suffering from an i2c ack_error\n"); + return -EREMOTEIO; + } + } + deb_i2c("tried %d times i2c operation, " + "never finished or too many ack errors.\n", i); + return -EREMOTEIO; +} + +static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c, + flexcop_ibi_value r100, u8 *buf) +{ + flexcop_ibi_value r104; + int len = r100.tw_sm_c_100.total_bytes, + /* remember total_bytes is buflen-1 */ + ret; + + /* work-around to have CableStar2 and SkyStar2 rev 2.7 work + * correctly: + * + * the ITD1000 is behind an i2c-gate which closes automatically + * after an i2c-transaction the STV0297 needs 2 consecutive reads + * one with no_base_addr = 0 and one with 1 + * + * those two work-arounds are conflictin: we check for the card + * type, it is set when probing the ITD1000 */ + if (i2c->fc->dev_type == FC_SKY_REV27) + r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; + + ret = flexcop_i2c_operation(i2c->fc, &r100); + if (ret != 0) { + deb_i2c("Retrying operation\n"); + r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; + ret = flexcop_i2c_operation(i2c->fc, &r100); + } + if (ret != 0) { + deb_i2c("read failed. %d\n", ret); + return ret; + } + + buf[0] = r100.tw_sm_c_100.data1_reg; + + if (len > 0) { + r104 = i2c->fc->read_ibi_reg(i2c->fc, tw_sm_c_104); + deb_i2c("read: r100: %08x, r104: %08x\n", r100.raw, r104.raw); + + /* there is at least one more byte, otherwise we wouldn't be here */ + buf[1] = r104.tw_sm_c_104.data2_reg; + if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg; + if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg; + } + return 0; +} + +static int flexcop_i2c_write4(struct flexcop_device *fc, + flexcop_ibi_value r100, u8 *buf) +{ + flexcop_ibi_value r104; + int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */ + r104.raw = 0; + + /* there is at least one byte, otherwise we wouldn't be here */ + r100.tw_sm_c_100.data1_reg = buf[0]; + r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0; + r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0; + r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0; + + deb_i2c("write: r100: %08x, r104: %08x\n", r100.raw, r104.raw); + + /* write the additional i2c data before doing the actual i2c operation */ + fc->write_ibi_reg(fc, tw_sm_c_104, r104); + return flexcop_i2c_operation(fc, &r100); +} + +int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c, + flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) +{ + int ret; + +#ifdef DUMP_I2C_MESSAGES + int i; +#endif + + u16 bytes_to_transfer; + flexcop_ibi_value r100; + + deb_i2c("op = %d\n",op); + r100.raw = 0; + r100.tw_sm_c_100.chipaddr = chipaddr; + r100.tw_sm_c_100.twoWS_rw = op; + r100.tw_sm_c_100.twoWS_port_reg = i2c->port; + +#ifdef DUMP_I2C_MESSAGES + printk(KERN_DEBUG "%d ", i2c->port); + if (op == FC_READ) + printk("rd("); + else + printk("wr("); + printk("%02x): %02x ", chipaddr, addr); +#endif + + /* in that case addr is the only value -> + * we write it twice as baseaddr and val0 + * BBTI is doing it like that for ISL6421 at least */ + if (i2c->no_base_addr && len == 0 && op == FC_WRITE) { + buf = &addr; + len = 1; + } + + while (len != 0) { + bytes_to_transfer = len > 4 ? 4 : len; + + r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1; + r100.tw_sm_c_100.baseaddr = addr; + + if (op == FC_READ) + ret = flexcop_i2c_read4(i2c, r100, buf); + else + ret = flexcop_i2c_write4(i2c->fc, r100, buf); + +#ifdef DUMP_I2C_MESSAGES + for (i = 0; i < bytes_to_transfer; i++) + printk("%02x ", buf[i]); +#endif + + if (ret < 0) + return ret; + + buf += bytes_to_transfer; + addr += bytes_to_transfer; + len -= bytes_to_transfer; + } + +#ifdef DUMP_I2C_MESSAGES + printk("\n"); +#endif + + return 0; +} +/* exported for PCI i2c */ +EXPORT_SYMBOL(flexcop_i2c_request); + +/* master xfer callback for demodulator */ +static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); + int i, ret = 0; + + /* Some drivers use 1 byte or 0 byte reads as probes, which this + * driver doesn't support. These probes will always fail, so this + * hack makes them always succeed. If one knew how, it would of + * course be better to actually do the read. */ + if (num == 1 && msgs[0].flags == I2C_M_RD && msgs[0].len <= 1) + return 1; + + if (mutex_lock_interruptible(&i2c->fc->i2c_mutex)) + return -ERESTARTSYS; + + for (i = 0; i < num; i++) { + /* reading */ + if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) { + ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr, + msgs[i].buf[0], msgs[i+1].buf, + msgs[i+1].len); + i++; /* skip the following message */ + } else /* writing */ + ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr, + msgs[i].buf[0], &msgs[i].buf[1], + msgs[i].len - 1); + if (ret < 0) { + deb_i2c("i2c master_xfer failed"); + break; + } + } + + mutex_unlock(&i2c->fc->i2c_mutex); + + if (ret == 0) + ret = num; + return ret; +} + +static u32 flexcop_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm flexcop_algo = { + .master_xfer = flexcop_master_xfer, + .functionality = flexcop_i2c_func, +}; + +int flexcop_i2c_init(struct flexcop_device *fc) +{ + int ret; + mutex_init(&fc->i2c_mutex); + + fc->fc_i2c_adap[0].fc = fc; + fc->fc_i2c_adap[1].fc = fc; + fc->fc_i2c_adap[2].fc = fc; + fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD; + fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM; + fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER; + + strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod", + sizeof(fc->fc_i2c_adap[0].i2c_adap.name)); + strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom", + sizeof(fc->fc_i2c_adap[1].i2c_adap.name)); + strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner", + sizeof(fc->fc_i2c_adap[2].i2c_adap.name)); + + i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]); + i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]); + i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]); + + fc->fc_i2c_adap[0].i2c_adap.algo = + fc->fc_i2c_adap[1].i2c_adap.algo = + fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo; + fc->fc_i2c_adap[0].i2c_adap.algo_data = + fc->fc_i2c_adap[1].i2c_adap.algo_data = + fc->fc_i2c_adap[2].i2c_adap.algo_data = NULL; + fc->fc_i2c_adap[0].i2c_adap.dev.parent = + fc->fc_i2c_adap[1].i2c_adap.dev.parent = + fc->fc_i2c_adap[2].i2c_adap.dev.parent = fc->dev; + + ret = i2c_add_adapter(&fc->fc_i2c_adap[0].i2c_adap); + if (ret < 0) + return ret; + + ret = i2c_add_adapter(&fc->fc_i2c_adap[1].i2c_adap); + if (ret < 0) + goto adap_1_failed; + + ret = i2c_add_adapter(&fc->fc_i2c_adap[2].i2c_adap); + if (ret < 0) + goto adap_2_failed; + + fc->init_state |= FC_STATE_I2C_INIT; + return 0; + +adap_2_failed: + i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); +adap_1_failed: + i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); + return ret; +} + +void flexcop_i2c_exit(struct flexcop_device *fc) +{ + if (fc->init_state & FC_STATE_I2C_INIT) { + i2c_del_adapter(&fc->fc_i2c_adap[2].i2c_adap); + i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); + i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); + } + fc->init_state &= ~FC_STATE_I2C_INIT; +} diff --git a/drivers/media/pci/b2c2/flexcop-misc.c b/drivers/media/pci/b2c2/flexcop-misc.c new file mode 100644 index 000000000000..f06f3a9070f5 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-misc.c @@ -0,0 +1,86 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-misc.c - miscellaneous functions + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +void flexcop_determine_revision(struct flexcop_device *fc) +{ + flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); + + switch (v.misc_204.Rev_N_sig_revision_hi) { + case 0x2: + deb_info("found a FlexCopII.\n"); + fc->rev = FLEXCOP_II; + break; + case 0x3: + deb_info("found a FlexCopIIb.\n"); + fc->rev = FLEXCOP_IIB; + break; + case 0x0: + deb_info("found a FlexCopIII.\n"); + fc->rev = FLEXCOP_III; + break; + default: + err("unknown FlexCop Revision: %x. Please report this to " + "linux-dvb@linuxtv.org.", + v.misc_204.Rev_N_sig_revision_hi); + break; + } + + if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps)) + deb_info("this FlexCop has " + "the additional 32 hardware pid filter.\n"); + else + deb_info("this FlexCop has " + "the 6 basic main hardware pid filter.\n"); + /* bus parts have to decide if hw pid filtering is used or not. */ +} + +static const char *flexcop_revision_names[] = { + "Unknown chip", + "FlexCopII", + "FlexCopIIb", + "FlexCopIII", +}; + +static const char *flexcop_device_names[] = { + [FC_UNK] = "Unknown device", + [FC_CABLE] = "Cable2PC/CableStar 2 DVB-C", + [FC_AIR_DVBT] = "Air2PC/AirStar 2 DVB-T", + [FC_AIR_ATSC1] = "Air2PC/AirStar 2 ATSC 1st generation", + [FC_AIR_ATSC2] = "Air2PC/AirStar 2 ATSC 2nd generation", + [FC_AIR_ATSC3] = "Air2PC/AirStar 2 ATSC 3rd generation (HD5000)", + [FC_SKY_REV23] = "Sky2PC/SkyStar 2 DVB-S rev 2.3 (old version)", + [FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6", + [FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u", + [FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8", +}; + +static const char *flexcop_bus_names[] = { + "USB", + "PCI", +}; + +void flexcop_device_name(struct flexcop_device *fc, + const char *prefix, const char *suffix) +{ + info("%s '%s' at the '%s' bus controlled by a '%s' %s", + prefix, flexcop_device_names[fc->dev_type], + flexcop_bus_names[fc->bus_type], + flexcop_revision_names[fc->rev], suffix); +} + +void flexcop_dump_reg(struct flexcop_device *fc, + flexcop_ibi_register reg, int num) +{ + flexcop_ibi_value v; + int i; + for (i = 0; i < num; i++) { + v = fc->read_ibi_reg(fc, reg+4*i); + deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw); + } + deb_rdump("\n"); +} +EXPORT_SYMBOL(flexcop_dump_reg); diff --git a/drivers/media/pci/b2c2/flexcop-pci.c b/drivers/media/pci/b2c2/flexcop-pci.c new file mode 100644 index 000000000000..44f8fb5f17ff --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-pci.c @@ -0,0 +1,450 @@ +/* + * Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-pci.c - covers the PCI part including DMA transfers + * see flexcop.c for copyright information + */ + +#define FC_LOG_PREFIX "flexcop-pci" +#include "flexcop-common.h" + +static int enable_pid_filtering = 1; +module_param(enable_pid_filtering, int, 0444); +MODULE_PARM_DESC(enable_pid_filtering, + "enable hardware pid filtering: supported values: 0 (fullts), 1"); + +static int irq_chk_intv = 100; +module_param(irq_chk_intv, int, 0644); +MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog."); + +#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG +#define dprintk(level,args...) \ + do { if ((debug & level)) printk(args); } while (0) +#define DEBSTATUS "" +#else +#define dprintk(level,args...) +#define DEBSTATUS " (debugging is not enabled)" +#endif + +#define deb_info(args...) dprintk(0x01, args) +#define deb_reg(args...) dprintk(0x02, args) +#define deb_ts(args...) dprintk(0x04, args) +#define deb_irq(args...) dprintk(0x08, args) +#define deb_chk(args...) dprintk(0x10, args) + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))." + DEBSTATUS); + +#define DRIVER_VERSION "0.1" +#define DRIVER_NAME "flexcop-pci" +#define DRIVER_AUTHOR "Patrick Boettcher " + +struct flexcop_pci { + struct pci_dev *pdev; + +#define FC_PCI_INIT 0x01 +#define FC_PCI_DMA_INIT 0x02 + int init_state; + + void __iomem *io_mem; + u32 irq; + /* buffersize (at least for DMA1, need to be % 188 == 0, + * this logic is required */ +#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188) +#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188) + struct flexcop_dma dma[2]; + + int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */ + u32 last_dma1_cur_pos; + /* position of the pointer last time the timer/packet irq occurred */ + int count; + int count_prev; + int stream_problem; + + spinlock_t irq_lock; + unsigned long last_irq; + + struct delayed_work irq_check_work; + struct flexcop_device *fc_dev; +}; + +static int lastwreg, lastwval, lastrreg, lastrval; + +static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc, + flexcop_ibi_register r) +{ + struct flexcop_pci *fc_pci = fc->bus_specific; + flexcop_ibi_value v; + v.raw = readl(fc_pci->io_mem + r); + + if (lastrreg != r || lastrval != v.raw) { + lastrreg = r; lastrval = v.raw; + deb_reg("new rd: %3x: %08x\n", r, v.raw); + } + + return v; +} + +static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, + flexcop_ibi_register r, flexcop_ibi_value v) +{ + struct flexcop_pci *fc_pci = fc->bus_specific; + + if (lastwreg != r || lastwval != v.raw) { + lastwreg = r; lastwval = v.raw; + deb_reg("new wr: %3x: %08x\n", r, v.raw); + } + + writel(v.raw, fc_pci->io_mem + r); + return 0; +} + +static void flexcop_pci_irq_check_work(struct work_struct *work) +{ + struct flexcop_pci *fc_pci = + container_of(work, struct flexcop_pci, irq_check_work.work); + struct flexcop_device *fc = fc_pci->fc_dev; + + if (fc->feedcount) { + + if (fc_pci->count == fc_pci->count_prev) { + deb_chk("no IRQ since the last check\n"); + if (fc_pci->stream_problem++ == 3) { + struct dvb_demux_feed *feed; + deb_info("flexcop-pci: stream problem, resetting pid filter\n"); + + spin_lock_irq(&fc->demux.lock); + list_for_each_entry(feed, &fc->demux.feed_list, + list_head) { + flexcop_pid_feed_control(fc, feed, 0); + } + + list_for_each_entry(feed, &fc->demux.feed_list, + list_head) { + flexcop_pid_feed_control(fc, feed, 1); + } + spin_unlock_irq(&fc->demux.lock); + + fc_pci->stream_problem = 0; + } + } else { + fc_pci->stream_problem = 0; + fc_pci->count_prev = fc_pci->count; + } + } + + schedule_delayed_work(&fc_pci->irq_check_work, + msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv)); +} + +/* When PID filtering is turned on, we use the timer IRQ, because small amounts + * of data need to be passed to the user space instantly as well. When PID + * filtering is turned off, we use the page-change-IRQ */ +static irqreturn_t flexcop_pci_isr(int irq, void *dev_id) +{ + struct flexcop_pci *fc_pci = dev_id; + struct flexcop_device *fc = fc_pci->fc_dev; + unsigned long flags; + flexcop_ibi_value v; + irqreturn_t ret = IRQ_HANDLED; + + spin_lock_irqsave(&fc_pci->irq_lock, flags); + v = fc->read_ibi_reg(fc, irq_20c); + + /* errors */ + if (v.irq_20c.Data_receiver_error) + deb_chk("data receiver error\n"); + if (v.irq_20c.Continuity_error_flag) + deb_chk("Contunuity error flag is set\n"); + if (v.irq_20c.LLC_SNAP_FLAG_set) + deb_chk("LLC_SNAP_FLAG_set is set\n"); + if (v.irq_20c.Transport_Error) + deb_chk("Transport error\n"); + + if ((fc_pci->count % 1000) == 0) + deb_chk("%d valid irq took place so far\n", fc_pci->count); + + if (v.irq_20c.DMA1_IRQ_Status == 1) { + if (fc_pci->active_dma1_addr == 0) + flexcop_pass_dmx_packets(fc_pci->fc_dev, + fc_pci->dma[0].cpu_addr0, + fc_pci->dma[0].size / 188); + else + flexcop_pass_dmx_packets(fc_pci->fc_dev, + fc_pci->dma[0].cpu_addr1, + fc_pci->dma[0].size / 188); + + deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr); + fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr; + /* for the timer IRQ we only can use buffer dmx feeding, because we don't have + * complete TS packets when reading from the DMA memory */ + } else if (v.irq_20c.DMA1_Timer_Status == 1) { + dma_addr_t cur_addr = + fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2; + u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0; + + deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, " + "last_cur_pos: %08x ", + jiffies_to_usecs(jiffies - fc_pci->last_irq), + v.raw, (unsigned long long)cur_addr, cur_pos, + fc_pci->last_dma1_cur_pos); + fc_pci->last_irq = jiffies; + + /* buffer end was reached, restarted from the beginning + * pass the data from last_cur_pos to the buffer end to the demux + */ + if (cur_pos < fc_pci->last_dma1_cur_pos) { + deb_irq(" end was reached: passing %d bytes ", + (fc_pci->dma[0].size*2 - 1) - + fc_pci->last_dma1_cur_pos); + flexcop_pass_dmx_data(fc_pci->fc_dev, + fc_pci->dma[0].cpu_addr0 + + fc_pci->last_dma1_cur_pos, + (fc_pci->dma[0].size*2) - + fc_pci->last_dma1_cur_pos); + fc_pci->last_dma1_cur_pos = 0; + } + + if (cur_pos > fc_pci->last_dma1_cur_pos) { + deb_irq(" passing %d bytes ", + cur_pos - fc_pci->last_dma1_cur_pos); + flexcop_pass_dmx_data(fc_pci->fc_dev, + fc_pci->dma[0].cpu_addr0 + + fc_pci->last_dma1_cur_pos, + cur_pos - fc_pci->last_dma1_cur_pos); + } + deb_irq("\n"); + + fc_pci->last_dma1_cur_pos = cur_pos; + fc_pci->count++; + } else { + deb_irq("isr for flexcop called, " + "apparently without reason (%08x)\n", v.raw); + ret = IRQ_NONE; + } + + spin_unlock_irqrestore(&fc_pci->irq_lock, flags); + return ret; +} + +static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff) +{ + struct flexcop_pci *fc_pci = fc->bus_specific; + if (onoff) { + flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1); + flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2); + flexcop_dma_config_timer(fc, FC_DMA_1, 0); + flexcop_dma_xfer_control(fc, FC_DMA_1, + FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1); + deb_irq("DMA xfer enabled\n"); + + fc_pci->last_dma1_cur_pos = 0; + flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1); + deb_irq("IRQ enabled\n"); + fc_pci->count_prev = fc_pci->count; + } else { + flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0); + deb_irq("IRQ disabled\n"); + + flexcop_dma_xfer_control(fc, FC_DMA_1, + FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0); + deb_irq("DMA xfer disabled\n"); + } + return 0; +} + +static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci) +{ + int ret; + ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0], + FC_DEFAULT_DMA1_BUFSIZE); + if (ret != 0) + return ret; + + ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1], + FC_DEFAULT_DMA2_BUFSIZE); + if (ret != 0) { + flexcop_dma_free(&fc_pci->dma[0]); + return ret; + } + + flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA | + FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1); + flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO | + FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2); + fc_pci->init_state |= FC_PCI_DMA_INIT; + return ret; +} + +static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci) +{ + if (fc_pci->init_state & FC_PCI_DMA_INIT) { + flexcop_dma_free(&fc_pci->dma[0]); + flexcop_dma_free(&fc_pci->dma[1]); + } + fc_pci->init_state &= ~FC_PCI_DMA_INIT; +} + +static int flexcop_pci_init(struct flexcop_pci *fc_pci) +{ + int ret; + + info("card revision %x", fc_pci->pdev->revision); + + if ((ret = pci_enable_device(fc_pci->pdev)) != 0) + return ret; + pci_set_master(fc_pci->pdev); + + if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0) + goto err_pci_disable_device; + + fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800); + + if (!fc_pci->io_mem) { + err("cannot map io memory\n"); + ret = -EIO; + goto err_pci_release_regions; + } + + pci_set_drvdata(fc_pci->pdev, fc_pci); + spin_lock_init(&fc_pci->irq_lock); + if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr, + IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0) + goto err_pci_iounmap; + + fc_pci->init_state |= FC_PCI_INIT; + return ret; + +err_pci_iounmap: + pci_iounmap(fc_pci->pdev, fc_pci->io_mem); + pci_set_drvdata(fc_pci->pdev, NULL); +err_pci_release_regions: + pci_release_regions(fc_pci->pdev); +err_pci_disable_device: + pci_disable_device(fc_pci->pdev); + return ret; +} + +static void flexcop_pci_exit(struct flexcop_pci *fc_pci) +{ + if (fc_pci->init_state & FC_PCI_INIT) { + free_irq(fc_pci->pdev->irq, fc_pci); + pci_iounmap(fc_pci->pdev, fc_pci->io_mem); + pci_set_drvdata(fc_pci->pdev, NULL); + pci_release_regions(fc_pci->pdev); + pci_disable_device(fc_pci->pdev); + } + fc_pci->init_state &= ~FC_PCI_INIT; +} + +static int flexcop_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct flexcop_device *fc; + struct flexcop_pci *fc_pci; + int ret = -ENOMEM; + + if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) { + err("out of memory\n"); + return -ENOMEM; + } + + /* general flexcop init */ + fc_pci = fc->bus_specific; + fc_pci->fc_dev = fc; + + fc->read_ibi_reg = flexcop_pci_read_ibi_reg; + fc->write_ibi_reg = flexcop_pci_write_ibi_reg; + fc->i2c_request = flexcop_i2c_request; + fc->get_mac_addr = flexcop_eeprom_check_mac_addr; + fc->stream_control = flexcop_pci_stream_control; + + if (enable_pid_filtering) + info("will use the HW PID filter."); + else + info("will pass the complete TS to the demuxer."); + + fc->pid_filtering = enable_pid_filtering; + fc->bus_type = FC_PCI; + fc->dev = &pdev->dev; + fc->owner = THIS_MODULE; + + /* bus specific part */ + fc_pci->pdev = pdev; + if ((ret = flexcop_pci_init(fc_pci)) != 0) + goto err_kfree; + + /* init flexcop */ + if ((ret = flexcop_device_initialize(fc)) != 0) + goto err_pci_exit; + + /* init dma */ + if ((ret = flexcop_pci_dma_init(fc_pci)) != 0) + goto err_fc_exit; + + INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work); + + if (irq_chk_intv > 0) + schedule_delayed_work(&fc_pci->irq_check_work, + msecs_to_jiffies(irq_chk_intv < 100 ? + 100 : + irq_chk_intv)); + return ret; + +err_fc_exit: + flexcop_device_exit(fc); +err_pci_exit: + flexcop_pci_exit(fc_pci); +err_kfree: + flexcop_device_kfree(fc); + return ret; +} + +/* in theory every _exit function should be called exactly two times, + * here and in the bail-out-part of the _init-function + */ +static void flexcop_pci_remove(struct pci_dev *pdev) +{ + struct flexcop_pci *fc_pci = pci_get_drvdata(pdev); + + if (irq_chk_intv > 0) + cancel_delayed_work(&fc_pci->irq_check_work); + + flexcop_pci_dma_exit(fc_pci); + flexcop_device_exit(fc_pci->fc_dev); + flexcop_pci_exit(fc_pci); + flexcop_device_kfree(fc_pci->fc_dev); +} + +static struct pci_device_id flexcop_pci_tbl[] = { + { PCI_DEVICE(0x13d0, 0x2103) }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl); + +static struct pci_driver flexcop_pci_driver = { + .name = "b2c2_flexcop_pci", + .id_table = flexcop_pci_tbl, + .probe = flexcop_pci_probe, + .remove = flexcop_pci_remove, +}; + +static int __init flexcop_pci_module_init(void) +{ + return pci_register_driver(&flexcop_pci_driver); +} + +static void __exit flexcop_pci_module_exit(void) +{ + pci_unregister_driver(&flexcop_pci_driver); +} + +module_init(flexcop_pci_module_init); +module_exit(flexcop_pci_module_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/b2c2/flexcop-reg.h b/drivers/media/pci/b2c2/flexcop-reg.h new file mode 100644 index 000000000000..dc4528dcbb98 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-reg.h @@ -0,0 +1,166 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_REG_H__ +#define __FLEXCOP_REG_H__ + +typedef enum { + FLEXCOP_UNK = 0, + FLEXCOP_II, + FLEXCOP_IIB, + FLEXCOP_III, +} flexcop_revision_t; + +typedef enum { + FC_UNK = 0, + FC_CABLE, + FC_AIR_DVBT, + FC_AIR_ATSC1, + FC_AIR_ATSC2, + FC_AIR_ATSC3, + FC_SKY_REV23, + FC_SKY_REV26, + FC_SKY_REV27, + FC_SKY_REV28, +} flexcop_device_type_t; + +typedef enum { + FC_USB = 0, + FC_PCI, +} flexcop_bus_t; + +/* FlexCop IBI Registers */ +#if defined(__LITTLE_ENDIAN) +#include "flexcop_ibi_value_le.h" +#else +#if defined(__BIG_ENDIAN) +#include "flexcop_ibi_value_be.h" +#else +#error no endian defined +#endif +#endif + +#define fc_data_Tag_ID_DVB 0x3e +#define fc_data_Tag_ID_ATSC 0x3f +#define fc_data_Tag_ID_IDSB 0x8b + +#define fc_key_code_default 0x1 +#define fc_key_code_even 0x2 +#define fc_key_code_odd 0x3 + +extern flexcop_ibi_value ibi_zero; + +typedef enum { + FC_I2C_PORT_DEMOD = 1, + FC_I2C_PORT_EEPROM = 2, + FC_I2C_PORT_TUNER = 3, +} flexcop_i2c_port_t; + +typedef enum { + FC_WRITE = 0, + FC_READ = 1, +} flexcop_access_op_t; + +typedef enum { + FC_SRAM_DEST_NET = 1, + FC_SRAM_DEST_CAI = 2, + FC_SRAM_DEST_CAO = 4, + FC_SRAM_DEST_MEDIA = 8 +} flexcop_sram_dest_t; + +typedef enum { + FC_SRAM_DEST_TARGET_WAN_USB = 0, + FC_SRAM_DEST_TARGET_DMA1 = 1, + FC_SRAM_DEST_TARGET_DMA2 = 2, + FC_SRAM_DEST_TARGET_FC3_CA = 3 +} flexcop_sram_dest_target_t; + +typedef enum { + FC_SRAM_2_32KB = 0, /* 64KB */ + FC_SRAM_1_32KB = 1, /* 32KB - default fow FCII */ + FC_SRAM_1_128KB = 2, /* 128KB */ + FC_SRAM_1_48KB = 3, /* 48KB - default for FCIII */ +} flexcop_sram_type_t; + +typedef enum { + FC_WAN_SPEED_4MBITS = 0, + FC_WAN_SPEED_8MBITS = 1, + FC_WAN_SPEED_12MBITS = 2, + FC_WAN_SPEED_16MBITS = 3, +} flexcop_wan_speed_t; + +typedef enum { + FC_DMA_1 = 1, + FC_DMA_2 = 2, +} flexcop_dma_index_t; + +typedef enum { + FC_DMA_SUBADDR_0 = 1, + FC_DMA_SUBADDR_1 = 2, +} flexcop_dma_addr_index_t; + +/* names of the particular registers */ +typedef enum { + dma1_000 = 0x000, + dma1_004 = 0x004, + dma1_008 = 0x008, + dma1_00c = 0x00c, + dma2_010 = 0x010, + dma2_014 = 0x014, + dma2_018 = 0x018, + dma2_01c = 0x01c, + + tw_sm_c_100 = 0x100, + tw_sm_c_104 = 0x104, + tw_sm_c_108 = 0x108, + tw_sm_c_10c = 0x10c, + tw_sm_c_110 = 0x110, + + lnb_switch_freq_200 = 0x200, + misc_204 = 0x204, + ctrl_208 = 0x208, + irq_20c = 0x20c, + sw_reset_210 = 0x210, + misc_214 = 0x214, + mbox_v8_to_host_218 = 0x218, + mbox_host_to_v8_21c = 0x21c, + + pid_filter_300 = 0x300, + pid_filter_304 = 0x304, + pid_filter_308 = 0x308, + pid_filter_30c = 0x30c, + index_reg_310 = 0x310, + pid_n_reg_314 = 0x314, + mac_low_reg_318 = 0x318, + mac_high_reg_31c = 0x31c, + + data_tag_400 = 0x400, + card_id_408 = 0x408, + card_id_40c = 0x40c, + mac_address_418 = 0x418, + mac_address_41c = 0x41c, + + ci_600 = 0x600, + pi_604 = 0x604, + pi_608 = 0x608, + dvb_reg_60c = 0x60c, + + sram_ctrl_reg_700 = 0x700, + net_buf_reg_704 = 0x704, + cai_buf_reg_708 = 0x708, + cao_buf_reg_70c = 0x70c, + media_buf_reg_710 = 0x710, + sram_dest_reg_714 = 0x714, + net_buf_reg_718 = 0x718, + wan_ctrl_reg_71c = 0x71c, +} flexcop_ibi_register; + +#define flexcop_set_ibi_value(reg,attr,val) { \ + flexcop_ibi_value v = fc->read_ibi_reg(fc,reg); \ + v.reg.attr = val; \ + fc->write_ibi_reg(fc,reg,v); \ +} + +#endif diff --git a/drivers/media/pci/b2c2/flexcop-sram.c b/drivers/media/pci/b2c2/flexcop-sram.c new file mode 100644 index 000000000000..f2199e43e803 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-sram.c @@ -0,0 +1,363 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-sram.c - functions for controlling the SRAM + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +static void flexcop_sram_set_chip(struct flexcop_device *fc, + flexcop_sram_type_t type) +{ + flexcop_set_ibi_value(wan_ctrl_reg_71c, sram_chip, type); +} + +int flexcop_sram_init(struct flexcop_device *fc) +{ + switch (fc->rev) { + case FLEXCOP_II: + case FLEXCOP_IIB: + flexcop_sram_set_chip(fc, FC_SRAM_1_32KB); + break; + case FLEXCOP_III: + flexcop_sram_set_chip(fc, FC_SRAM_1_48KB); + break; + default: + return -EINVAL; + } + return 0; +} + +int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, + flexcop_sram_dest_target_t target) +{ + flexcop_ibi_value v; + v = fc->read_ibi_reg(fc, sram_dest_reg_714); + + if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) { + err("SRAM destination target to available on FlexCopII(b)\n"); + return -EINVAL; + } + deb_sram("sram dest: %x target: %x\n", dest, target); + + if (dest & FC_SRAM_DEST_NET) + v.sram_dest_reg_714.NET_Dest = target; + if (dest & FC_SRAM_DEST_CAI) + v.sram_dest_reg_714.CAI_Dest = target; + if (dest & FC_SRAM_DEST_CAO) + v.sram_dest_reg_714.CAO_Dest = target; + if (dest & FC_SRAM_DEST_MEDIA) + v.sram_dest_reg_714.MEDIA_Dest = target; + + fc->write_ibi_reg(fc,sram_dest_reg_714,v); + udelay(1000); /* TODO delay really necessary */ + + return 0; +} +EXPORT_SYMBOL(flexcop_sram_set_dest); + +void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s) +{ + flexcop_set_ibi_value(wan_ctrl_reg_71c,wan_speed_sig,s); +} +EXPORT_SYMBOL(flexcop_wan_set_speed); + +void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill) +{ + flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714); + v.sram_dest_reg_714.ctrl_usb_wan = usb_wan; + v.sram_dest_reg_714.ctrl_sramdma = sramdma; + v.sram_dest_reg_714.ctrl_maximumfill = maximumfill; + fc->write_ibi_reg(fc,sram_dest_reg_714,v); +} +EXPORT_SYMBOL(flexcop_sram_ctrl); + +#if 0 +static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) +{ + int i, retries; + u32 command; + + for (i = 0; i < len; i++) { + command = bank | addr | 0x04000000 | (*buf << 0x10); + + retries = 2; + + while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { + mdelay(1); + retries--; + }; + + if (retries == 0) + printk("%s: SRAM timeout\n", __func__); + + write_reg_dw(adapter, 0x700, command); + + buf++; + addr++; + } +} + +static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) +{ + int i, retries; + u32 command, value; + + for (i = 0; i < len; i++) { + command = bank | addr | 0x04008000; + + retries = 10000; + + while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { + mdelay(1); + retries--; + }; + + if (retries == 0) + printk("%s: SRAM timeout\n", __func__); + + write_reg_dw(adapter, 0x700, command); + + retries = 10000; + + while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { + mdelay(1); + retries--; + }; + + if (retries == 0) + printk("%s: SRAM timeout\n", __func__); + + value = read_reg_dw(adapter, 0x700) >> 0x10; + + *buf = (value & 0xff); + + addr++; + buf++; + } +} + +static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) +{ + u32 bank; + + bank = 0; + + if (adapter->dw_sram_type == 0x20000) { + bank = (addr & 0x18000) << 0x0d; + } + + if (adapter->dw_sram_type == 0x00000) { + if ((addr >> 0x0f) == 0) + bank = 0x20000000; + else + bank = 0x10000000; + } + flex_sram_write(adapter, bank, addr & 0x7fff, buf, len); +} + +static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) +{ + u32 bank; + bank = 0; + + if (adapter->dw_sram_type == 0x20000) { + bank = (addr & 0x18000) << 0x0d; + } + + if (adapter->dw_sram_type == 0x00000) { + if ((addr >> 0x0f) == 0) + bank = 0x20000000; + else + bank = 0x10000000; + } + flex_sram_read(adapter, bank, addr & 0x7fff, buf, len); +} + +static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len) +{ + u32 length; + while (len != 0) { + length = len; + /* check if the address range belongs to the same + * 32K memory chip. If not, the data is read + * from one chip at a time */ + if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { + length = (((addr >> 0x0f) + 1) << 0x0f) - addr; + } + + sram_read_chunk(adapter, addr, buf, length); + addr = addr + length; + buf = buf + length; + len = len - length; + } +} + +static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len) +{ + u32 length; + while (len != 0) { + length = len; + + /* check if the address range belongs to the same + * 32K memory chip. If not, the data is + * written to one chip at a time */ + if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { + length = (((addr >> 0x0f) + 1) << 0x0f) - addr; + } + + sram_write_chunk(adapter, addr, buf, length); + addr = addr + length; + buf = buf + length; + len = len - length; + } +} + +static void sram_set_size(struct adapter *adapter, u32 mask) +{ + write_reg_dw(adapter, 0x71c, + (mask | (~0x30000 & read_reg_dw(adapter, 0x71c)))); +} + +static void sram_init(struct adapter *adapter) +{ + u32 tmp; + tmp = read_reg_dw(adapter, 0x71c); + write_reg_dw(adapter, 0x71c, 1); + + if (read_reg_dw(adapter, 0x71c) != 0) { + write_reg_dw(adapter, 0x71c, tmp); + adapter->dw_sram_type = tmp & 0x30000; + ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); + } else { + adapter->dw_sram_type = 0x10000; + ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); + } +} + +static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr) +{ + u8 tmp1, tmp2; + dprintk("%s: mask = %x, addr = %x\n", __func__, mask, addr); + + sram_set_size(adapter, mask); + sram_init(adapter); + + tmp2 = 0xa5; + tmp1 = 0x4f; + + sram_write(adapter, addr, &tmp2, 1); + sram_write(adapter, addr + 4, &tmp1, 1); + + tmp2 = 0; + mdelay(20); + + sram_read(adapter, addr, &tmp2, 1); + sram_read(adapter, addr, &tmp2, 1); + + dprintk("%s: wrote 0xa5, read 0x%2x\n", __func__, tmp2); + + if (tmp2 != 0xa5) + return 0; + + tmp2 = 0x5a; + tmp1 = 0xf4; + + sram_write(adapter, addr, &tmp2, 1); + sram_write(adapter, addr + 4, &tmp1, 1); + + tmp2 = 0; + mdelay(20); + + sram_read(adapter, addr, &tmp2, 1); + sram_read(adapter, addr, &tmp2, 1); + + dprintk("%s: wrote 0x5a, read 0x%2x\n", __func__, tmp2); + + if (tmp2 != 0x5a) + return 0; + return 1; +} + +static u32 sram_length(struct adapter *adapter) +{ + if (adapter->dw_sram_type == 0x10000) + return 32768; /* 32K */ + if (adapter->dw_sram_type == 0x00000) + return 65536; /* 64K */ + if (adapter->dw_sram_type == 0x20000) + return 131072; /* 128K */ + return 32768; /* 32K */ +} + +/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory. + - for 128K there are 4x32K chips at bank 0,1,2,3. + - for 64K there are 2x32K chips at bank 1,2. + - for 32K there is one 32K chip at bank 0. + + FlexCop works only with one bank at a time. The bank is selected + by bits 28-29 of the 0x700 register. + + bank 0 covers addresses 0x00000-0x07fff + bank 1 covers addresses 0x08000-0x0ffff + bank 2 covers addresses 0x10000-0x17fff + bank 3 covers addresses 0x18000-0x1ffff */ + +static int flexcop_sram_detect(struct flexcop_device *fc) +{ + flexcop_ibi_value r208, r71c_0, vr71c_1; + r208 = fc->read_ibi_reg(fc, ctrl_208); + fc->write_ibi_reg(fc, ctrl_208, ibi_zero); + + r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c); + write_reg_dw(adapter, 0x71c, 1); + tmp3 = read_reg_dw(adapter, 0x71c); + dprintk("%s: tmp3 = %x\n", __func__, tmp3); + write_reg_dw(adapter, 0x71c, tmp2); + + // check for internal SRAM ??? + tmp3--; + if (tmp3 != 0) { + sram_set_size(adapter, 0x10000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 32K\n", __func__); + return 32; + } + + if (sram_test_location(adapter, 0x20000, 0x18000) != 0) { + sram_set_size(adapter, 0x20000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 128K\n", __func__); + return 128; + } + + if (sram_test_location(adapter, 0x00000, 0x10000) != 0) { + sram_set_size(adapter, 0x00000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 64K\n", __func__); + return 64; + } + + if (sram_test_location(adapter, 0x10000, 0x00000) != 0) { + sram_set_size(adapter, 0x10000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 32K\n", __func__); + return 32; + } + + sram_set_size(adapter, 0x10000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: SRAM detection failed. Set to 32K \n", __func__); + return 0; +} + +static void sll_detect_sram_size(struct adapter *adapter) +{ + sram_detect_for_flex2(adapter); +} + +#endif diff --git a/drivers/media/pci/b2c2/flexcop-usb.c b/drivers/media/pci/b2c2/flexcop-usb.c new file mode 100644 index 000000000000..8b6275f85908 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-usb.c @@ -0,0 +1,587 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-usb.c - covers the USB part + * see flexcop.c for copyright information + */ +#define FC_LOG_PREFIX "flexcop_usb" +#include "flexcop-usb.h" +#include "flexcop-common.h" + +/* Version information */ +#define DRIVER_VERSION "0.1" +#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver" +#define DRIVER_AUTHOR "Patrick Boettcher " + +/* debug */ +#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG +#define dprintk(level,args...) \ + do { if ((debug & level)) printk(args); } while (0) + +#define debug_dump(b, l, method) do {\ + int i; \ + for (i = 0; i < l; i++) \ + method("%02x ", b[i]); \ + method("\n"); \ +} while (0) + +#define DEBSTATUS "" +#else +#define dprintk(level, args...) +#define debug_dump(b, l, method) +#define DEBSTATUS " (debugging is not enabled)" +#endif + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2," + "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS); +#undef DEBSTATUS + +#define deb_info(args...) dprintk(0x01, args) +#define deb_ts(args...) dprintk(0x02, args) +#define deb_ctrl(args...) dprintk(0x04, args) +#define deb_i2c(args...) dprintk(0x08, args) +#define deb_v8(args...) dprintk(0x10, args) + +/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits + * in the IBI address, to make the V8 code simpler. + * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used) + * in general: 0000 0HHH 000L LL00 + * IBI ADDRESS FORMAT: RHHH BLLL + * + * where R is the read(1)/write(0) bit, B is the busy bit + * and HHH and LLL are the two sets of three bits from the PCI address. + */ +#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \ + (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70)) +#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \ + (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4)) + +/* + * DKT 020228 + * - forget about this VENDOR_BUFFER_SIZE, read and write register + * deal with DWORD or 4 bytes, that should be should from now on + * - from now on, we don't support anything older than firm 1.00 + * I eliminated the write register as a 2 trip of writing hi word and lo word + * and force this to write only 4 bytes at a time. + * NOTE: this should work with all the firmware from 1.00 and newer + */ +static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read) +{ + struct flexcop_usb *fc_usb = fc->bus_specific; + u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG; + u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR; + u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | + (read ? 0x80 : 0); + + int len = usb_control_msg(fc_usb->udev, + read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT, + request, + request_type, /* 0xc0 read or 0x40 write */ + wAddress, + 0, + val, + sizeof(u32), + B2C2_WAIT_FOR_OPERATION_RDW * HZ); + + if (len != sizeof(u32)) { + err("error while %s dword from %d (%d).", read ? "reading" : + "writing", wAddress, wRegOffsPCI); + return -EIO; + } + return 0; +} +/* + * DKT 010817 - add support for V8 memory read/write and flash update + */ +static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb, + flexcop_usb_request_t req, u8 page, u16 wAddress, + u8 *pbBuffer, u32 buflen) +{ + u8 request_type = USB_TYPE_VENDOR; + u16 wIndex; + int nWaitTime, pipe, len; + wIndex = page << 8; + + switch (req) { + case B2C2_USB_READ_V8_MEM: + nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ; + request_type |= USB_DIR_IN; + pipe = B2C2_USB_CTRL_PIPE_IN; + break; + case B2C2_USB_WRITE_V8_MEM: + wIndex |= pbBuffer[0]; + request_type |= USB_DIR_OUT; + nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE; + pipe = B2C2_USB_CTRL_PIPE_OUT; + break; + case B2C2_USB_FLASH_BLOCK: + request_type |= USB_DIR_OUT; + nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH; + pipe = B2C2_USB_CTRL_PIPE_OUT; + break; + default: + deb_info("unsupported request for v8_mem_req %x.\n", req); + return -EINVAL; + } + deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req, + wAddress, wIndex, buflen); + + len = usb_control_msg(fc_usb->udev, pipe, + req, + request_type, + wAddress, + wIndex, + pbBuffer, + buflen, + nWaitTime * HZ); + + debug_dump(pbBuffer, len, deb_v8); + return len == buflen ? 0 : -EIO; +} + +#define bytes_left_to_read_on_page(paddr,buflen) \ + ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \ + ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK))) + +static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, + flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, + u32 addr, int extended, u8 *buf, u32 len) +{ + int i,ret = 0; + u16 wMax; + u32 pagechunk = 0; + + switch(req) { + case B2C2_USB_READ_V8_MEM: + wMax = USB_MEM_READ_MAX; + break; + case B2C2_USB_WRITE_V8_MEM: + wMax = USB_MEM_WRITE_MAX; + break; + case B2C2_USB_FLASH_BLOCK: + wMax = USB_FLASH_MAX; + break; + default: + return -EINVAL; + break; + } + for (i = 0; i < len;) { + pagechunk = + wMax < bytes_left_to_read_on_page(addr, len) ? + wMax : + bytes_left_to_read_on_page(addr, len); + deb_info("%x\n", + (addr & V8_MEMORY_PAGE_MASK) | + (V8_MEMORY_EXTENDED*extended)); + + ret = flexcop_usb_v8_memory_req(fc_usb, req, + page_start + (addr / V8_MEMORY_PAGE_SIZE), + (addr & V8_MEMORY_PAGE_MASK) | + (V8_MEMORY_EXTENDED*extended), + &buf[i], pagechunk); + + if (ret < 0) + return ret; + addr += pagechunk; + len -= pagechunk; + } + return 0; +} + +static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended) +{ + return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM, + V8_MEMORY_PAGE_FLASH, 0x1f010, 1, + fc->dvb_adapter.proposed_mac, 6); +} + +#if 0 +static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set, + flexcop_usb_utility_function_t func, u8 extra, u16 wIndex, + u16 buflen, u8 *pvBuffer) +{ + u16 wValue; + u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR; + int nWaitTime = 2, + pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len; + wValue = (func << 8) | extra; + + len = usb_control_msg(fc_usb->udev,pipe, + B2C2_USB_UTILITY, + request_type, + wValue, + wIndex, + pvBuffer, + buflen, + nWaitTime * HZ); + return len == buflen ? 0 : -EIO; +} +#endif + +/* usb i2c stuff */ +static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c, + flexcop_usb_request_t req, flexcop_usb_i2c_function_t func, + u8 chipaddr, u8 addr, u8 *buf, u8 buflen) +{ + struct flexcop_usb *fc_usb = i2c->fc->bus_specific; + u16 wValue, wIndex; + int nWaitTime,pipe,len; + u8 request_type = USB_TYPE_VENDOR; + + switch (func) { + case USB_FUNC_I2C_WRITE: + case USB_FUNC_I2C_MULTIWRITE: + case USB_FUNC_I2C_REPEATWRITE: + /* DKT 020208 - add this to support special case of DiSEqC */ + case USB_FUNC_I2C_CHECKWRITE: + pipe = B2C2_USB_CTRL_PIPE_OUT; + nWaitTime = 2; + request_type |= USB_DIR_OUT; + break; + case USB_FUNC_I2C_READ: + case USB_FUNC_I2C_REPEATREAD: + pipe = B2C2_USB_CTRL_PIPE_IN; + nWaitTime = 2; + request_type |= USB_DIR_IN; + break; + default: + deb_info("unsupported function for i2c_req %x\n", func); + return -EINVAL; + } + wValue = (func << 8) | (i2c->port << 4); + wIndex = (chipaddr << 8 ) | addr; + + deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n", + func, request_type, req, + wValue & 0xff, wValue >> 8, + wIndex & 0xff, wIndex >> 8); + + len = usb_control_msg(fc_usb->udev,pipe, + req, + request_type, + wValue, + wIndex, + buf, + buflen, + nWaitTime * HZ); + return len == buflen ? 0 : -EREMOTEIO; +} + +/* actual bus specific access functions, + make sure prototype are/will be equal to pci */ +static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, + flexcop_ibi_register reg) +{ + flexcop_ibi_value val; + val.raw = 0; + flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1); + return val; +} + +static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, + flexcop_ibi_register reg, flexcop_ibi_value val) +{ + return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0); +} + +static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c, + flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) +{ + if (op == FC_READ) + return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, + USB_FUNC_I2C_READ, chipaddr, addr, buf, len); + else + return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, + USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len); +} + +static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, + u8 *buffer, int buffer_length) +{ + u8 *b; + int l; + + deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", + fc_usb->tmp_buffer_length, buffer_length); + + if (fc_usb->tmp_buffer_length > 0) { + memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, + buffer_length); + fc_usb->tmp_buffer_length += buffer_length; + b = fc_usb->tmp_buffer; + l = fc_usb->tmp_buffer_length; + } else { + b=buffer; + l=buffer_length; + } + + while (l >= 190) { + if (*b == 0xff) { + switch (*(b+1) & 0x03) { + case 0x01: /* media packet */ + if (*(b+2) == 0x47) + flexcop_pass_dmx_packets( + fc_usb->fc_dev, b+2, 1); + else + deb_ts("not ts packet %*ph\n", 4, b+2); + b += 190; + l -= 190; + break; + default: + deb_ts("wrong packet type\n"); + l = 0; + break; + } + } else { + deb_ts("wrong header\n"); + l = 0; + } + } + + if (l>0) + memcpy(fc_usb->tmp_buffer, b, l); + fc_usb->tmp_buffer_length = l; +} + +static void flexcop_usb_urb_complete(struct urb *urb) +{ + struct flexcop_usb *fc_usb = urb->context; + int i; + + if (urb->actual_length > 0) + deb_ts("urb completed, bufsize: %d actlen; %d\n", + urb->transfer_buffer_length, urb->actual_length); + + for (i = 0; i < urb->number_of_packets; i++) { + if (urb->iso_frame_desc[i].status < 0) { + err("iso frame descriptor %d has an error: %d\n", i, + urb->iso_frame_desc[i].status); + } else + if (urb->iso_frame_desc[i].actual_length > 0) { + deb_ts("passed %d bytes to the demux\n", + urb->iso_frame_desc[i].actual_length); + + flexcop_usb_process_frame(fc_usb, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + usb_submit_urb(urb,GFP_ATOMIC); +} + +static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff) +{ + /* submit/kill iso packets */ + return 0; +} + +static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) +{ + int i; + for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) + if (fc_usb->iso_urb[i] != NULL) { + deb_ts("unlinking/killing urb no. %d\n",i); + usb_kill_urb(fc_usb->iso_urb[i]); + usb_free_urb(fc_usb->iso_urb[i]); + } + + if (fc_usb->iso_buffer != NULL) + pci_free_consistent(NULL, + fc_usb->buffer_size, fc_usb->iso_buffer, + fc_usb->dma_addr); +} + +static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) +{ + u16 frame_size = le16_to_cpu( + fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize); + int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * + frame_size, i, j, ret; + int buffer_offset = 0; + + deb_ts("creating %d iso-urbs with %d frames " + "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB, + B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize); + + fc_usb->iso_buffer = pci_alloc_consistent(NULL, + bufsize, &fc_usb->dma_addr); + if (fc_usb->iso_buffer == NULL) + return -ENOMEM; + + memset(fc_usb->iso_buffer, 0, bufsize); + fc_usb->buffer_size = bufsize; + + /* creating iso urbs */ + for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { + fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO, + GFP_ATOMIC); + if (fc_usb->iso_urb[i] == NULL) { + ret = -ENOMEM; + goto urb_error; + } + } + + /* initialising and submitting iso urbs */ + for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { + int frame_offset = 0; + struct urb *urb = fc_usb->iso_urb[i]; + deb_ts("initializing and submitting urb no. %d " + "(buf_offset: %d).\n", i, buffer_offset); + + urb->dev = fc_usb->udev; + urb->context = fc_usb; + urb->complete = flexcop_usb_urb_complete; + urb->pipe = B2C2_USB_DATA_PIPE; + urb->transfer_flags = URB_ISO_ASAP; + urb->interval = 1; + urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO; + urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO; + urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset; + + buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO; + for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) { + deb_ts("urb no: %d, frame: %d, frame_offset: %d\n", + i, j, frame_offset); + urb->iso_frame_desc[j].offset = frame_offset; + urb->iso_frame_desc[j].length = frame_size; + frame_offset += frame_size; + } + + if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) { + err("submitting urb %d failed with %d.", i, ret); + goto urb_error; + } + deb_ts("submitted urb no. %d.\n",i); + } + + /* SRAM */ + flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA | + FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, + FC_SRAM_DEST_TARGET_WAN_USB); + flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS); + flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1); + return 0; + +urb_error: + flexcop_usb_transfer_exit(fc_usb); + return ret; +} + +static int flexcop_usb_init(struct flexcop_usb *fc_usb) +{ + /* use the alternate setting with the larges buffer */ + usb_set_interface(fc_usb->udev,0,1); + switch (fc_usb->udev->speed) { + case USB_SPEED_LOW: + err("cannot handle USB speed because it is too slow."); + return -ENODEV; + break; + case USB_SPEED_FULL: + info("running at FULL speed."); + break; + case USB_SPEED_HIGH: + info("running at HIGH speed."); + break; + case USB_SPEED_UNKNOWN: /* fall through */ + default: + err("cannot handle USB speed because it is unknown."); + return -ENODEV; + } + usb_set_intfdata(fc_usb->uintf, fc_usb); + return 0; +} + +static void flexcop_usb_exit(struct flexcop_usb *fc_usb) +{ + usb_set_intfdata(fc_usb->uintf, NULL); +} + +static int flexcop_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct flexcop_usb *fc_usb = NULL; + struct flexcop_device *fc = NULL; + int ret; + + if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { + err("out of memory\n"); + return -ENOMEM; + } + + /* general flexcop init */ + fc_usb = fc->bus_specific; + fc_usb->fc_dev = fc; + + fc->read_ibi_reg = flexcop_usb_read_ibi_reg; + fc->write_ibi_reg = flexcop_usb_write_ibi_reg; + fc->i2c_request = flexcop_usb_i2c_request; + fc->get_mac_addr = flexcop_usb_get_mac_addr; + + fc->stream_control = flexcop_usb_stream_control; + + fc->pid_filtering = 1; + fc->bus_type = FC_USB; + + fc->dev = &udev->dev; + fc->owner = THIS_MODULE; + + /* bus specific part */ + fc_usb->udev = udev; + fc_usb->uintf = intf; + if ((ret = flexcop_usb_init(fc_usb)) != 0) + goto err_kfree; + + /* init flexcop */ + if ((ret = flexcop_device_initialize(fc)) != 0) + goto err_usb_exit; + + /* xfer init */ + if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0) + goto err_fc_exit; + + info("%s successfully initialized and connected.", DRIVER_NAME); + return 0; + +err_fc_exit: + flexcop_device_exit(fc); +err_usb_exit: + flexcop_usb_exit(fc_usb); +err_kfree: + flexcop_device_kfree(fc); + return ret; +} + +static void flexcop_usb_disconnect(struct usb_interface *intf) +{ + struct flexcop_usb *fc_usb = usb_get_intfdata(intf); + flexcop_usb_transfer_exit(fc_usb); + flexcop_device_exit(fc_usb->fc_dev); + flexcop_usb_exit(fc_usb); + flexcop_device_kfree(fc_usb->fc_dev); + info("%s successfully deinitialized and disconnected.", DRIVER_NAME); +} + +static struct usb_device_id flexcop_usb_table [] = { + { USB_DEVICE(0x0af7, 0x0101) }, + { } +}; +MODULE_DEVICE_TABLE (usb, flexcop_usb_table); + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver flexcop_usb_driver = { + .name = "b2c2_flexcop_usb", + .probe = flexcop_usb_probe, + .disconnect = flexcop_usb_disconnect, + .id_table = flexcop_usb_table, +}; + +module_usb_driver(flexcop_usb_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/b2c2/flexcop-usb.h b/drivers/media/pci/b2c2/flexcop-usb.h new file mode 100644 index 000000000000..92529a9c4475 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop-usb.h @@ -0,0 +1,111 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-usb.h - header file for the USB part + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_USB_H_INCLUDED__ +#define __FLEXCOP_USB_H_INCLUDED__ + +#include + +/* transfer parameters */ +#define B2C2_USB_FRAMES_PER_ISO 4 +#define B2C2_USB_NUM_ISO_URB 4 + +#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0) +#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0) +#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81) + +struct flexcop_usb { + struct usb_device *udev; + struct usb_interface *uintf; + + u8 *iso_buffer; + int buffer_size; + dma_addr_t dma_addr; + + struct urb *iso_urb[B2C2_USB_NUM_ISO_URB]; + struct flexcop_device *fc_dev; + + u8 tmp_buffer[1023+190]; + int tmp_buffer_length; +}; + +#if 0 +/* request types TODO What is its use?*/ +typedef enum { + +} flexcop_usb_request_type_t; +#endif + +/* request */ +typedef enum { + B2C2_USB_WRITE_V8_MEM = 0x04, + B2C2_USB_READ_V8_MEM = 0x05, + B2C2_USB_READ_REG = 0x08, + B2C2_USB_WRITE_REG = 0x0A, + B2C2_USB_WRITEREGHI = 0x0B, + B2C2_USB_FLASH_BLOCK = 0x10, + B2C2_USB_I2C_REQUEST = 0x11, + B2C2_USB_UTILITY = 0x12, +} flexcop_usb_request_t; + +/* function definition for I2C_REQUEST */ +typedef enum { + USB_FUNC_I2C_WRITE = 0x01, + USB_FUNC_I2C_MULTIWRITE = 0x02, + USB_FUNC_I2C_READ = 0x03, + USB_FUNC_I2C_REPEATWRITE = 0x04, + USB_FUNC_GET_DESCRIPTOR = 0x05, + USB_FUNC_I2C_REPEATREAD = 0x06, + /* DKT 020208 - add this to support special case of DiSEqC */ + USB_FUNC_I2C_CHECKWRITE = 0x07, + USB_FUNC_I2C_CHECKRESULT = 0x08, +} flexcop_usb_i2c_function_t; + +/* function definition for UTILITY request 0x12 + * DKT 020304 - new utility function */ +typedef enum { + UTILITY_SET_FILTER = 0x01, + UTILITY_DATA_ENABLE = 0x02, + UTILITY_FLEX_MULTIWRITE = 0x03, + UTILITY_SET_BUFFER_SIZE = 0x04, + UTILITY_FLEX_OPERATOR = 0x05, + UTILITY_FLEX_RESET300_START = 0x06, + UTILITY_FLEX_RESET300_STOP = 0x07, + UTILITY_FLEX_RESET300 = 0x08, + UTILITY_SET_ISO_SIZE = 0x09, + UTILITY_DATA_RESET = 0x0A, + UTILITY_GET_DATA_STATUS = 0x10, + UTILITY_GET_V8_REG = 0x11, + /* DKT 020326 - add function for v1.14 */ + UTILITY_SRAM_WRITE = 0x12, + UTILITY_SRAM_READ = 0x13, + UTILITY_SRAM_TESTFILL = 0x14, + UTILITY_SRAM_TESTSET = 0x15, + UTILITY_SRAM_TESTVERIFY = 0x16, +} flexcop_usb_utility_function_t; + +#define B2C2_WAIT_FOR_OPERATION_RW (1*HZ) +#define B2C2_WAIT_FOR_OPERATION_RDW (3*HZ) +#define B2C2_WAIT_FOR_OPERATION_WDW (1*HZ) + +#define B2C2_WAIT_FOR_OPERATION_V8READ (3*HZ) +#define B2C2_WAIT_FOR_OPERATION_V8WRITE (3*HZ) +#define B2C2_WAIT_FOR_OPERATION_V8FLASH (3*HZ) + +typedef enum { + V8_MEMORY_PAGE_DVB_CI = 0x20, + V8_MEMORY_PAGE_DVB_DS = 0x40, + V8_MEMORY_PAGE_MULTI2 = 0x60, + V8_MEMORY_PAGE_FLASH = 0x80 +} flexcop_usb_mem_page_t; + +#define V8_MEMORY_EXTENDED (1 << 15) +#define USB_MEM_READ_MAX 32 +#define USB_MEM_WRITE_MAX 1 +#define USB_FLASH_MAX 8 +#define V8_MEMORY_PAGE_SIZE 0x8000 /* 32K */ +#define V8_MEMORY_PAGE_MASK 0x7FFF + +#endif diff --git a/drivers/media/pci/b2c2/flexcop.c b/drivers/media/pci/b2c2/flexcop.c new file mode 100644 index 000000000000..b1e8c99f469b --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop.c @@ -0,0 +1,324 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop.c - main module part + * Copyright (C) 2004-9 Patrick Boettcher + * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc + * + * Acknowledgements: + * John Jurrius from BBTI, Inc. for extensive support + * with code examples and data books + * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting) + * + * Contributions to the skystar2-driver have been done by + * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes) + * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code) + * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu) + * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac + * filtering) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "flexcop.h" + +#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip" +#define DRIVER_AUTHOR "Patrick Boettcher demux->priv; + return flexcop_pid_feed_control(fc, dvbdmxfeed, 1); +} + +static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct flexcop_device *fc = dvbdmxfeed->demux->priv; + return flexcop_pid_feed_control(fc, dvbdmxfeed, 0); +} + +static int flexcop_dvb_init(struct flexcop_device *fc) +{ + int ret = dvb_register_adapter(&fc->dvb_adapter, + "FlexCop Digital TV device", fc->owner, + fc->dev, adapter_nr); + if (ret < 0) { + err("error registering DVB adapter"); + return ret; + } + fc->dvb_adapter.priv = fc; + + fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING); + fc->demux.priv = fc; + fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED; + fc->demux.start_feed = flexcop_dvb_start_feed; + fc->demux.stop_feed = flexcop_dvb_stop_feed; + fc->demux.write_to_decoder = NULL; + + ret = dvb_dmx_init(&fc->demux); + if (ret < 0) { + err("dvb_dmx failed: error %d", ret); + goto err_dmx; + } + + fc->hw_frontend.source = DMX_FRONTEND_0; + + fc->dmxdev.filternum = fc->demux.feednum; + fc->dmxdev.demux = &fc->demux.dmx; + fc->dmxdev.capabilities = 0; + ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter); + if (ret < 0) { + err("dvb_dmxdev_init failed: error %d", ret); + goto err_dmx_dev; + } + + ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend); + if (ret < 0) { + err("adding hw_frontend to dmx failed: error %d", ret); + goto err_dmx_add_hw_frontend; + } + + fc->mem_frontend.source = DMX_MEMORY_FE; + ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend); + if (ret < 0) { + err("adding mem_frontend to dmx failed: error %d", ret); + goto err_dmx_add_mem_frontend; + } + + ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend); + if (ret < 0) { + err("connect frontend failed: error %d", ret); + goto err_connect_frontend; + } + + ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx); + if (ret < 0) { + err("dvb_net_init failed: error %d", ret); + goto err_net; + } + + fc->init_state |= FC_STATE_DVB_INIT; + return 0; + +err_net: + fc->demux.dmx.disconnect_frontend(&fc->demux.dmx); +err_connect_frontend: + fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend); +err_dmx_add_mem_frontend: + fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend); +err_dmx_add_hw_frontend: + dvb_dmxdev_release(&fc->dmxdev); +err_dmx_dev: + dvb_dmx_release(&fc->demux); +err_dmx: + dvb_unregister_adapter(&fc->dvb_adapter); + return ret; +} + +static void flexcop_dvb_exit(struct flexcop_device *fc) +{ + if (fc->init_state & FC_STATE_DVB_INIT) { + dvb_net_release(&fc->dvbnet); + + fc->demux.dmx.close(&fc->demux.dmx); + fc->demux.dmx.remove_frontend(&fc->demux.dmx, + &fc->mem_frontend); + fc->demux.dmx.remove_frontend(&fc->demux.dmx, + &fc->hw_frontend); + dvb_dmxdev_release(&fc->dmxdev); + dvb_dmx_release(&fc->demux); + dvb_unregister_adapter(&fc->dvb_adapter); + deb_info("deinitialized dvb stuff\n"); + } + fc->init_state &= ~FC_STATE_DVB_INIT; +} + +/* these methods are necessary to achieve the long-term-goal of hiding the + * struct flexcop_device from the bus-parts */ +void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len) +{ + dvb_dmx_swfilter(&fc->demux, buf, len); +} +EXPORT_SYMBOL(flexcop_pass_dmx_data); + +void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no) +{ + dvb_dmx_swfilter_packets(&fc->demux, buf, no); +} +EXPORT_SYMBOL(flexcop_pass_dmx_packets); + +static void flexcop_reset(struct flexcop_device *fc) +{ + flexcop_ibi_value v210, v204; + + /* reset the flexcop itself */ + fc->write_ibi_reg(fc,ctrl_208,ibi_zero); + + v210.raw = 0; + v210.sw_reset_210.reset_block_000 = 1; + v210.sw_reset_210.reset_block_100 = 1; + v210.sw_reset_210.reset_block_200 = 1; + v210.sw_reset_210.reset_block_300 = 1; + v210.sw_reset_210.reset_block_400 = 1; + v210.sw_reset_210.reset_block_500 = 1; + v210.sw_reset_210.reset_block_600 = 1; + v210.sw_reset_210.reset_block_700 = 1; + v210.sw_reset_210.Block_reset_enable = 0xb2; + v210.sw_reset_210.Special_controls = 0xc259; + fc->write_ibi_reg(fc,sw_reset_210,v210); + msleep(1); + + /* reset the periphical devices */ + + v204 = fc->read_ibi_reg(fc,misc_204); + v204.misc_204.Per_reset_sig = 0; + fc->write_ibi_reg(fc,misc_204,v204); + msleep(1); + v204.misc_204.Per_reset_sig = 1; + fc->write_ibi_reg(fc,misc_204,v204); +} + +void flexcop_reset_block_300(struct flexcop_device *fc) +{ + flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208), + v210 = fc->read_ibi_reg(fc, sw_reset_210); + + deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw); + fc->write_ibi_reg(fc,ctrl_208,ibi_zero); + + v210.sw_reset_210.reset_block_300 = 1; + v210.sw_reset_210.Block_reset_enable = 0xb2; + + fc->write_ibi_reg(fc,sw_reset_210,v210); + fc->write_ibi_reg(fc,ctrl_208,v208_save); +} + +struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len) +{ + void *bus; + struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device), + GFP_KERNEL); + if (!fc) { + err("no memory"); + return NULL; + } + + bus = kzalloc(bus_specific_len, GFP_KERNEL); + if (!bus) { + err("no memory"); + kfree(fc); + return NULL; + } + + fc->bus_specific = bus; + + return fc; +} +EXPORT_SYMBOL(flexcop_device_kmalloc); + +void flexcop_device_kfree(struct flexcop_device *fc) +{ + kfree(fc->bus_specific); + kfree(fc); +} +EXPORT_SYMBOL(flexcop_device_kfree); + +int flexcop_device_initialize(struct flexcop_device *fc) +{ + int ret; + ibi_zero.raw = 0; + + flexcop_reset(fc); + flexcop_determine_revision(fc); + flexcop_sram_init(fc); + flexcop_hw_filter_init(fc); + flexcop_smc_ctrl(fc, 0); + + ret = flexcop_dvb_init(fc); + if (ret) + goto error; + + /* i2c has to be done before doing EEProm stuff - + * because the EEProm is accessed via i2c */ + ret = flexcop_i2c_init(fc); + if (ret) + goto error; + + /* do the MAC address reading after initializing the dvb_adapter */ + if (fc->get_mac_addr(fc, 0) == 0) { + u8 *b = fc->dvb_adapter.proposed_mac; + info("MAC address = %pM", b); + flexcop_set_mac_filter(fc,b); + flexcop_mac_filter_ctrl(fc,1); + } else + warn("reading of MAC address failed.\n"); + + ret = flexcop_frontend_init(fc); + if (ret) + goto error; + + flexcop_device_name(fc,"initialization of","complete"); + return 0; + +error: + flexcop_device_exit(fc); + return ret; +} +EXPORT_SYMBOL(flexcop_device_initialize); + +void flexcop_device_exit(struct flexcop_device *fc) +{ + flexcop_frontend_exit(fc); + flexcop_i2c_exit(fc); + flexcop_dvb_exit(fc); +} +EXPORT_SYMBOL(flexcop_device_exit); + +static int flexcop_module_init(void) +{ + info(DRIVER_NAME " loaded successfully"); + return 0; +} + +static void flexcop_module_cleanup(void) +{ + info(DRIVER_NAME " unloaded successfully"); +} + +module_init(flexcop_module_init); +module_exit(flexcop_module_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/b2c2/flexcop.h b/drivers/media/pci/b2c2/flexcop.h new file mode 100644 index 000000000000..897b10c85ad9 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop.h @@ -0,0 +1,29 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop.h - private header file for all flexcop-chip-source files + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_H__ +#define __FLEXCOP_H___ + +#define FC_LOG_PREFIX "b2c2-flexcop" +#include "flexcop-common.h" + +extern int b2c2_flexcop_debug; + +/* debug */ +#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG +#define dprintk(level,args...) \ + do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0) +#else +#define dprintk(level,args...) +#endif + +#define deb_info(args...) dprintk(0x01, args) +#define deb_tuner(args...) dprintk(0x02, args) +#define deb_i2c(args...) dprintk(0x04, args) +#define deb_ts(args...) dprintk(0x08, args) +#define deb_sram(args...) dprintk(0x10, args) +#define deb_rdump(args...) dprintk(0x20, args) + +#endif diff --git a/drivers/media/pci/b2c2/flexcop_ibi_value_be.h b/drivers/media/pci/b2c2/flexcop_ibi_value_be.h new file mode 100644 index 000000000000..8f64bdbd72bb --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop_ibi_value_be.h @@ -0,0 +1,455 @@ +/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * register descriptions + * see flexcop.c for copyright information + */ +/* This file is automatically generated, do not edit things here. */ +#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ +#define __FLEXCOP_IBI_VALUE_INCLUDED__ + +typedef union { + u32 raw; + + struct { + u32 dma_address0 :30; + u32 dma_0No_update : 1; + u32 dma_0start : 1; + } dma_0x0; + + struct { + u32 dma_addr_size :24; + u32 DMA_maxpackets : 8; + } dma_0x4_remap; + + struct { + u32 dma_addr_size :24; + u32 unused : 1; + u32 dma1timer : 7; + } dma_0x4_read; + + struct { + u32 dma_addr_size :24; + u32 dmatimer : 7; + u32 unused : 1; + } dma_0x4_write; + + struct { + u32 dma_cur_addr :30; + u32 unused : 2; + } dma_0x8; + + struct { + u32 dma_address1 :30; + u32 remap_enable : 1; + u32 dma_1start : 1; + } dma_0xc; + + struct { + u32 st_done : 1; + u32 no_base_addr_ack_error : 1; + u32 twoWS_port_reg : 2; + u32 total_bytes : 2; + u32 twoWS_rw : 1; + u32 working_start : 1; + u32 data1_reg : 8; + u32 baseaddr : 8; + u32 reserved1 : 1; + u32 chipaddr : 7; + } tw_sm_c_100; + + struct { + u32 unused : 6; + u32 force_stop : 1; + u32 exlicit_stops : 1; + u32 data4_reg : 8; + u32 data3_reg : 8; + u32 data2_reg : 8; + } tw_sm_c_104; + + struct { + u32 reserved2 :19; + u32 tlo1 : 5; + u32 reserved1 : 2; + u32 thi1 : 6; + } tw_sm_c_108; + + struct { + u32 reserved2 :19; + u32 tlo1 : 5; + u32 reserved1 : 2; + u32 thi1 : 6; + } tw_sm_c_10c; + + struct { + u32 reserved2 :19; + u32 tlo1 : 5; + u32 reserved1 : 2; + u32 thi1 : 6; + } tw_sm_c_110; + + struct { + u32 LNB_CTLPrescaler_sig : 2; + u32 LNB_CTLLowCount_sig :15; + u32 LNB_CTLHighCount_sig :15; + } lnb_switch_freq_200; + + struct { + u32 Rev_N_sig_reserved2 : 1; + u32 Rev_N_sig_caps : 1; + u32 Rev_N_sig_reserved1 : 2; + u32 Rev_N_sig_revision_hi : 4; + u32 reserved :20; + u32 Per_reset_sig : 1; + u32 LNB_L_H_sig : 1; + u32 ACPI3_sig : 1; + u32 ACPI1_sig : 1; + } misc_204; + + struct { + u32 unused : 9; + u32 Mailbox_from_V8_Enable_sig : 1; + u32 DMA2_Size_IRQ_Enable_sig : 1; + u32 DMA1_Size_IRQ_Enable_sig : 1; + u32 DMA2_Timer_Enable_sig : 1; + u32 DMA2_IRQ_Enable_sig : 1; + u32 DMA1_Timer_Enable_sig : 1; + u32 DMA1_IRQ_Enable_sig : 1; + u32 Rcv_Data_sig : 1; + u32 MAC_filter_Mode_sig : 1; + u32 Multi2_Enable_sig : 1; + u32 Per_CA_Enable_sig : 1; + u32 SMC_Enable_sig : 1; + u32 CA_Enable_sig : 1; + u32 WAN_CA_Enable_sig : 1; + u32 WAN_Enable_sig : 1; + u32 Mask_filter_sig : 1; + u32 Null_filter_sig : 1; + u32 ECM_filter_sig : 1; + u32 EMM_filter_sig : 1; + u32 PMT_filter_sig : 1; + u32 PCR_filter_sig : 1; + u32 Stream2_filter_sig : 1; + u32 Stream1_filter_sig : 1; + } ctrl_208; + + struct { + u32 reserved :21; + u32 Transport_Error : 1; + u32 LLC_SNAP_FLAG_set : 1; + u32 Continuity_error_flag : 1; + u32 Data_receiver_error : 1; + u32 Mailbox_from_V8_Status_sig : 1; + u32 DMA2_Size_IRQ_Status : 1; + u32 DMA1_Size_IRQ_Status : 1; + u32 DMA2_Timer_Status : 1; + u32 DMA2_IRQ_Status : 1; + u32 DMA1_Timer_Status : 1; + u32 DMA1_IRQ_Status : 1; + } irq_20c; + + struct { + u32 Special_controls :16; + u32 Block_reset_enable : 8; + u32 reset_block_700 : 1; + u32 reset_block_600 : 1; + u32 reset_block_500 : 1; + u32 reset_block_400 : 1; + u32 reset_block_300 : 1; + u32 reset_block_200 : 1; + u32 reset_block_100 : 1; + u32 reset_block_000 : 1; + } sw_reset_210; + + struct { + u32 unused2 :20; + u32 polarity_PS_ERR_sig : 1; + u32 polarity_PS_SYNC_sig : 1; + u32 polarity_PS_VALID_sig : 1; + u32 polarity_PS_CLK_sig : 1; + u32 unused1 : 3; + u32 s2p_sel_sig : 1; + u32 section_pkg_enable_sig : 1; + u32 halt_V8_sig : 1; + u32 v2WS_oe_sig : 1; + u32 vuart_oe_sig : 1; + } misc_214; + + struct { + u32 Mailbox_from_V8 :32; + } mbox_v8_to_host_218; + + struct { + u32 sysramaccess_busmuster : 1; + u32 sysramaccess_write : 1; + u32 unused : 7; + u32 sysramaccess_addr :15; + u32 sysramaccess_data : 8; + } mbox_host_to_v8_21c; + + struct { + u32 debug_fifo_problem : 1; + u32 debug_flag_write_status00 : 1; + u32 Stream2_trans : 1; + u32 Stream2_PID :13; + u32 debug_flag_pid_saved : 1; + u32 MAC_Multicast_filter : 1; + u32 Stream1_trans : 1; + u32 Stream1_PID :13; + } pid_filter_300; + + struct { + u32 reserved : 2; + u32 PMT_trans : 1; + u32 PMT_PID :13; + u32 debug_overrun2 : 1; + u32 debug_overrun3 : 1; + u32 PCR_trans : 1; + u32 PCR_PID :13; + } pid_filter_304; + + struct { + u32 reserved : 2; + u32 ECM_trans : 1; + u32 ECM_PID :13; + u32 EMM_filter_6 : 1; + u32 EMM_filter_4 : 1; + u32 EMM_trans : 1; + u32 EMM_PID :13; + } pid_filter_308; + + struct { + u32 unused2 : 3; + u32 Group_mask :13; + u32 unused1 : 2; + u32 Group_trans : 1; + u32 Group_PID :13; + } pid_filter_30c_ext_ind_0_7; + + struct { + u32 unused :15; + u32 net_master_read :17; + } pid_filter_30c_ext_ind_1; + + struct { + u32 unused :15; + u32 net_master_write :17; + } pid_filter_30c_ext_ind_2; + + struct { + u32 unused :15; + u32 next_net_master_write :17; + } pid_filter_30c_ext_ind_3; + + struct { + u32 reserved2 : 5; + u32 stack_read :10; + u32 reserved1 : 6; + u32 state_write :10; + u32 unused1 : 1; + } pid_filter_30c_ext_ind_4; + + struct { + u32 unused :22; + u32 stack_cnt :10; + } pid_filter_30c_ext_ind_5; + + struct { + u32 unused : 4; + u32 data_size_reg :12; + u32 write_status4 : 2; + u32 write_status1 : 2; + u32 pid_fsm_save_reg300 : 2; + u32 pid_fsm_save_reg4 : 2; + u32 pid_fsm_save_reg3 : 2; + u32 pid_fsm_save_reg2 : 2; + u32 pid_fsm_save_reg1 : 2; + u32 pid_fsm_save_reg0 : 2; + } pid_filter_30c_ext_ind_6; + + struct { + u32 unused :22; + u32 pass_alltables : 1; + u32 AB_select : 1; + u32 extra_index_reg : 3; + u32 index_reg : 5; + } index_reg_310; + + struct { + u32 reserved :17; + u32 PID_enable_bit : 1; + u32 PID_trans : 1; + u32 PID :13; + } pid_n_reg_314; + + struct { + u32 reserved : 6; + u32 HighAB_bit : 1; + u32 Enable_bit : 1; + u32 A6_byte : 8; + u32 A5_byte : 8; + u32 A4_byte : 8; + } mac_low_reg_318; + + struct { + u32 reserved : 8; + u32 A3_byte : 8; + u32 A2_byte : 8; + u32 A1_byte : 8; + } mac_high_reg_31c; + + struct { + u32 data_Tag_ID :16; + u32 reserved :16; + } data_tag_400; + + struct { + u32 Card_IDbyte3 : 8; + u32 Card_IDbyte4 : 8; + u32 Card_IDbyte5 : 8; + u32 Card_IDbyte6 : 8; + } card_id_408; + + struct { + u32 Card_IDbyte1 : 8; + u32 Card_IDbyte2 : 8; + } card_id_40c; + + struct { + u32 MAC6 : 8; + u32 MAC3 : 8; + u32 MAC2 : 8; + u32 MAC1 : 8; + } mac_address_418; + + struct { + u32 reserved :16; + u32 MAC8 : 8; + u32 MAC7 : 8; + } mac_address_41c; + + struct { + u32 reserved :21; + u32 txbuffempty : 1; + u32 ReceiveByteFrameError : 1; + u32 ReceiveDataReady : 1; + u32 transmitter_data_byte : 8; + } ci_600; + + struct { + u32 pi_component_reg : 3; + u32 pi_rw : 1; + u32 pi_ha :20; + u32 pi_d : 8; + } pi_604; + + struct { + u32 pi_busy_n : 1; + u32 pi_wait_n : 1; + u32 pi_timeout_status : 1; + u32 pi_CiMax_IRQ_n : 1; + u32 config_cclk : 1; + u32 config_cs_n : 1; + u32 config_wr_n : 1; + u32 config_Prog_n : 1; + u32 config_Init_stat : 1; + u32 config_Done_stat : 1; + u32 pcmcia_b_mod_pwr_n : 1; + u32 pcmcia_a_mod_pwr_n : 1; + u32 reserved : 3; + u32 Timer_addr : 5; + u32 unused : 1; + u32 timer_data : 7; + u32 Timer_Load_req : 1; + u32 Timer_Read_req : 1; + u32 oncecycle_read : 1; + u32 serialReset : 1; + } pi_608; + + struct { + u32 reserved : 6; + u32 rw_flag : 1; + u32 dvb_en : 1; + u32 key_array_row : 5; + u32 key_array_col : 3; + u32 key_code : 2; + u32 key_enable : 1; + u32 PID :13; + } dvb_reg_60c; + + struct { + u32 start_sram_ibi : 1; + u32 reserved2 : 1; + u32 ce_pin_reg : 1; + u32 oe_pin_reg : 1; + u32 reserved1 : 3; + u32 sc_xfer_bit : 1; + u32 sram_data : 8; + u32 sram_rw : 1; + u32 sram_addr :15; + } sram_ctrl_reg_700; + + struct { + u32 net_addr_write :16; + u32 net_addr_read :16; + } net_buf_reg_704; + + struct { + u32 cai_cnt : 4; + u32 reserved2 : 6; + u32 cai_write :11; + u32 reserved1 : 5; + u32 cai_read :11; + } cai_buf_reg_708; + + struct { + u32 cao_cnt : 4; + u32 reserved2 : 6; + u32 cap_write :11; + u32 reserved1 : 5; + u32 cao_read :11; + } cao_buf_reg_70c; + + struct { + u32 media_cnt : 4; + u32 reserved2 : 6; + u32 media_write :11; + u32 reserved1 : 5; + u32 media_read :11; + } media_buf_reg_710; + + struct { + u32 reserved :17; + u32 ctrl_maximumfill : 1; + u32 ctrl_sramdma : 1; + u32 ctrl_usb_wan : 1; + u32 cao_ovflow_error : 1; + u32 cai_ovflow_error : 1; + u32 media_ovflow_error : 1; + u32 net_ovflow_error : 1; + u32 MEDIA_Dest : 2; + u32 CAO_Dest : 2; + u32 CAI_Dest : 2; + u32 NET_Dest : 2; + } sram_dest_reg_714; + + struct { + u32 reserved3 :11; + u32 net_addr_write : 1; + u32 reserved2 : 3; + u32 net_addr_read : 1; + u32 reserved1 : 4; + u32 net_cnt :12; + } net_buf_reg_718; + + struct { + u32 reserved3 : 4; + u32 wan_pkt_frame : 4; + u32 reserved2 : 4; + u32 sram_memmap : 2; + u32 sram_chip : 2; + u32 wan_wait_state : 8; + u32 reserved1 : 6; + u32 wan_speed_sig : 2; + } wan_ctrl_reg_71c; +} flexcop_ibi_value; + +#endif diff --git a/drivers/media/pci/b2c2/flexcop_ibi_value_le.h b/drivers/media/pci/b2c2/flexcop_ibi_value_le.h new file mode 100644 index 000000000000..c75830d7d942 --- /dev/null +++ b/drivers/media/pci/b2c2/flexcop_ibi_value_le.h @@ -0,0 +1,455 @@ +/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * register descriptions + * see flexcop.c for copyright information + */ +/* This file is automatically generated, do not edit things here. */ +#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ +#define __FLEXCOP_IBI_VALUE_INCLUDED__ + +typedef union { + u32 raw; + + struct { + u32 dma_0start : 1; + u32 dma_0No_update : 1; + u32 dma_address0 :30; + } dma_0x0; + + struct { + u32 DMA_maxpackets : 8; + u32 dma_addr_size :24; + } dma_0x4_remap; + + struct { + u32 dma1timer : 7; + u32 unused : 1; + u32 dma_addr_size :24; + } dma_0x4_read; + + struct { + u32 unused : 1; + u32 dmatimer : 7; + u32 dma_addr_size :24; + } dma_0x4_write; + + struct { + u32 unused : 2; + u32 dma_cur_addr :30; + } dma_0x8; + + struct { + u32 dma_1start : 1; + u32 remap_enable : 1; + u32 dma_address1 :30; + } dma_0xc; + + struct { + u32 chipaddr : 7; + u32 reserved1 : 1; + u32 baseaddr : 8; + u32 data1_reg : 8; + u32 working_start : 1; + u32 twoWS_rw : 1; + u32 total_bytes : 2; + u32 twoWS_port_reg : 2; + u32 no_base_addr_ack_error : 1; + u32 st_done : 1; + } tw_sm_c_100; + + struct { + u32 data2_reg : 8; + u32 data3_reg : 8; + u32 data4_reg : 8; + u32 exlicit_stops : 1; + u32 force_stop : 1; + u32 unused : 6; + } tw_sm_c_104; + + struct { + u32 thi1 : 6; + u32 reserved1 : 2; + u32 tlo1 : 5; + u32 reserved2 :19; + } tw_sm_c_108; + + struct { + u32 thi1 : 6; + u32 reserved1 : 2; + u32 tlo1 : 5; + u32 reserved2 :19; + } tw_sm_c_10c; + + struct { + u32 thi1 : 6; + u32 reserved1 : 2; + u32 tlo1 : 5; + u32 reserved2 :19; + } tw_sm_c_110; + + struct { + u32 LNB_CTLHighCount_sig :15; + u32 LNB_CTLLowCount_sig :15; + u32 LNB_CTLPrescaler_sig : 2; + } lnb_switch_freq_200; + + struct { + u32 ACPI1_sig : 1; + u32 ACPI3_sig : 1; + u32 LNB_L_H_sig : 1; + u32 Per_reset_sig : 1; + u32 reserved :20; + u32 Rev_N_sig_revision_hi : 4; + u32 Rev_N_sig_reserved1 : 2; + u32 Rev_N_sig_caps : 1; + u32 Rev_N_sig_reserved2 : 1; + } misc_204; + + struct { + u32 Stream1_filter_sig : 1; + u32 Stream2_filter_sig : 1; + u32 PCR_filter_sig : 1; + u32 PMT_filter_sig : 1; + u32 EMM_filter_sig : 1; + u32 ECM_filter_sig : 1; + u32 Null_filter_sig : 1; + u32 Mask_filter_sig : 1; + u32 WAN_Enable_sig : 1; + u32 WAN_CA_Enable_sig : 1; + u32 CA_Enable_sig : 1; + u32 SMC_Enable_sig : 1; + u32 Per_CA_Enable_sig : 1; + u32 Multi2_Enable_sig : 1; + u32 MAC_filter_Mode_sig : 1; + u32 Rcv_Data_sig : 1; + u32 DMA1_IRQ_Enable_sig : 1; + u32 DMA1_Timer_Enable_sig : 1; + u32 DMA2_IRQ_Enable_sig : 1; + u32 DMA2_Timer_Enable_sig : 1; + u32 DMA1_Size_IRQ_Enable_sig : 1; + u32 DMA2_Size_IRQ_Enable_sig : 1; + u32 Mailbox_from_V8_Enable_sig : 1; + u32 unused : 9; + } ctrl_208; + + struct { + u32 DMA1_IRQ_Status : 1; + u32 DMA1_Timer_Status : 1; + u32 DMA2_IRQ_Status : 1; + u32 DMA2_Timer_Status : 1; + u32 DMA1_Size_IRQ_Status : 1; + u32 DMA2_Size_IRQ_Status : 1; + u32 Mailbox_from_V8_Status_sig : 1; + u32 Data_receiver_error : 1; + u32 Continuity_error_flag : 1; + u32 LLC_SNAP_FLAG_set : 1; + u32 Transport_Error : 1; + u32 reserved :21; + } irq_20c; + + struct { + u32 reset_block_000 : 1; + u32 reset_block_100 : 1; + u32 reset_block_200 : 1; + u32 reset_block_300 : 1; + u32 reset_block_400 : 1; + u32 reset_block_500 : 1; + u32 reset_block_600 : 1; + u32 reset_block_700 : 1; + u32 Block_reset_enable : 8; + u32 Special_controls :16; + } sw_reset_210; + + struct { + u32 vuart_oe_sig : 1; + u32 v2WS_oe_sig : 1; + u32 halt_V8_sig : 1; + u32 section_pkg_enable_sig : 1; + u32 s2p_sel_sig : 1; + u32 unused1 : 3; + u32 polarity_PS_CLK_sig : 1; + u32 polarity_PS_VALID_sig : 1; + u32 polarity_PS_SYNC_sig : 1; + u32 polarity_PS_ERR_sig : 1; + u32 unused2 :20; + } misc_214; + + struct { + u32 Mailbox_from_V8 :32; + } mbox_v8_to_host_218; + + struct { + u32 sysramaccess_data : 8; + u32 sysramaccess_addr :15; + u32 unused : 7; + u32 sysramaccess_write : 1; + u32 sysramaccess_busmuster : 1; + } mbox_host_to_v8_21c; + + struct { + u32 Stream1_PID :13; + u32 Stream1_trans : 1; + u32 MAC_Multicast_filter : 1; + u32 debug_flag_pid_saved : 1; + u32 Stream2_PID :13; + u32 Stream2_trans : 1; + u32 debug_flag_write_status00 : 1; + u32 debug_fifo_problem : 1; + } pid_filter_300; + + struct { + u32 PCR_PID :13; + u32 PCR_trans : 1; + u32 debug_overrun3 : 1; + u32 debug_overrun2 : 1; + u32 PMT_PID :13; + u32 PMT_trans : 1; + u32 reserved : 2; + } pid_filter_304; + + struct { + u32 EMM_PID :13; + u32 EMM_trans : 1; + u32 EMM_filter_4 : 1; + u32 EMM_filter_6 : 1; + u32 ECM_PID :13; + u32 ECM_trans : 1; + u32 reserved : 2; + } pid_filter_308; + + struct { + u32 Group_PID :13; + u32 Group_trans : 1; + u32 unused1 : 2; + u32 Group_mask :13; + u32 unused2 : 3; + } pid_filter_30c_ext_ind_0_7; + + struct { + u32 net_master_read :17; + u32 unused :15; + } pid_filter_30c_ext_ind_1; + + struct { + u32 net_master_write :17; + u32 unused :15; + } pid_filter_30c_ext_ind_2; + + struct { + u32 next_net_master_write :17; + u32 unused :15; + } pid_filter_30c_ext_ind_3; + + struct { + u32 unused1 : 1; + u32 state_write :10; + u32 reserved1 : 6; + u32 stack_read :10; + u32 reserved2 : 5; + } pid_filter_30c_ext_ind_4; + + struct { + u32 stack_cnt :10; + u32 unused :22; + } pid_filter_30c_ext_ind_5; + + struct { + u32 pid_fsm_save_reg0 : 2; + u32 pid_fsm_save_reg1 : 2; + u32 pid_fsm_save_reg2 : 2; + u32 pid_fsm_save_reg3 : 2; + u32 pid_fsm_save_reg4 : 2; + u32 pid_fsm_save_reg300 : 2; + u32 write_status1 : 2; + u32 write_status4 : 2; + u32 data_size_reg :12; + u32 unused : 4; + } pid_filter_30c_ext_ind_6; + + struct { + u32 index_reg : 5; + u32 extra_index_reg : 3; + u32 AB_select : 1; + u32 pass_alltables : 1; + u32 unused :22; + } index_reg_310; + + struct { + u32 PID :13; + u32 PID_trans : 1; + u32 PID_enable_bit : 1; + u32 reserved :17; + } pid_n_reg_314; + + struct { + u32 A4_byte : 8; + u32 A5_byte : 8; + u32 A6_byte : 8; + u32 Enable_bit : 1; + u32 HighAB_bit : 1; + u32 reserved : 6; + } mac_low_reg_318; + + struct { + u32 A1_byte : 8; + u32 A2_byte : 8; + u32 A3_byte : 8; + u32 reserved : 8; + } mac_high_reg_31c; + + struct { + u32 reserved :16; + u32 data_Tag_ID :16; + } data_tag_400; + + struct { + u32 Card_IDbyte6 : 8; + u32 Card_IDbyte5 : 8; + u32 Card_IDbyte4 : 8; + u32 Card_IDbyte3 : 8; + } card_id_408; + + struct { + u32 Card_IDbyte2 : 8; + u32 Card_IDbyte1 : 8; + } card_id_40c; + + struct { + u32 MAC1 : 8; + u32 MAC2 : 8; + u32 MAC3 : 8; + u32 MAC6 : 8; + } mac_address_418; + + struct { + u32 MAC7 : 8; + u32 MAC8 : 8; + u32 reserved :16; + } mac_address_41c; + + struct { + u32 transmitter_data_byte : 8; + u32 ReceiveDataReady : 1; + u32 ReceiveByteFrameError : 1; + u32 txbuffempty : 1; + u32 reserved :21; + } ci_600; + + struct { + u32 pi_d : 8; + u32 pi_ha :20; + u32 pi_rw : 1; + u32 pi_component_reg : 3; + } pi_604; + + struct { + u32 serialReset : 1; + u32 oncecycle_read : 1; + u32 Timer_Read_req : 1; + u32 Timer_Load_req : 1; + u32 timer_data : 7; + u32 unused : 1; + u32 Timer_addr : 5; + u32 reserved : 3; + u32 pcmcia_a_mod_pwr_n : 1; + u32 pcmcia_b_mod_pwr_n : 1; + u32 config_Done_stat : 1; + u32 config_Init_stat : 1; + u32 config_Prog_n : 1; + u32 config_wr_n : 1; + u32 config_cs_n : 1; + u32 config_cclk : 1; + u32 pi_CiMax_IRQ_n : 1; + u32 pi_timeout_status : 1; + u32 pi_wait_n : 1; + u32 pi_busy_n : 1; + } pi_608; + + struct { + u32 PID :13; + u32 key_enable : 1; + u32 key_code : 2; + u32 key_array_col : 3; + u32 key_array_row : 5; + u32 dvb_en : 1; + u32 rw_flag : 1; + u32 reserved : 6; + } dvb_reg_60c; + + struct { + u32 sram_addr :15; + u32 sram_rw : 1; + u32 sram_data : 8; + u32 sc_xfer_bit : 1; + u32 reserved1 : 3; + u32 oe_pin_reg : 1; + u32 ce_pin_reg : 1; + u32 reserved2 : 1; + u32 start_sram_ibi : 1; + } sram_ctrl_reg_700; + + struct { + u32 net_addr_read :16; + u32 net_addr_write :16; + } net_buf_reg_704; + + struct { + u32 cai_read :11; + u32 reserved1 : 5; + u32 cai_write :11; + u32 reserved2 : 6; + u32 cai_cnt : 4; + } cai_buf_reg_708; + + struct { + u32 cao_read :11; + u32 reserved1 : 5; + u32 cap_write :11; + u32 reserved2 : 6; + u32 cao_cnt : 4; + } cao_buf_reg_70c; + + struct { + u32 media_read :11; + u32 reserved1 : 5; + u32 media_write :11; + u32 reserved2 : 6; + u32 media_cnt : 4; + } media_buf_reg_710; + + struct { + u32 NET_Dest : 2; + u32 CAI_Dest : 2; + u32 CAO_Dest : 2; + u32 MEDIA_Dest : 2; + u32 net_ovflow_error : 1; + u32 media_ovflow_error : 1; + u32 cai_ovflow_error : 1; + u32 cao_ovflow_error : 1; + u32 ctrl_usb_wan : 1; + u32 ctrl_sramdma : 1; + u32 ctrl_maximumfill : 1; + u32 reserved :17; + } sram_dest_reg_714; + + struct { + u32 net_cnt :12; + u32 reserved1 : 4; + u32 net_addr_read : 1; + u32 reserved2 : 3; + u32 net_addr_write : 1; + u32 reserved3 :11; + } net_buf_reg_718; + + struct { + u32 wan_speed_sig : 2; + u32 reserved1 : 6; + u32 wan_wait_state : 8; + u32 sram_chip : 2; + u32 sram_memmap : 2; + u32 reserved2 : 4; + u32 wan_pkt_frame : 4; + u32 reserved3 : 4; + } wan_ctrl_reg_71c; +} flexcop_ibi_value; + +#endif diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig new file mode 100644 index 000000000000..8668e634c7ec --- /dev/null +++ b/drivers/media/pci/bt8xx/Kconfig @@ -0,0 +1,22 @@ +config DVB_BT8XX + tristate "BT8xx based PCI cards" + depends on DVB_CORE && PCI && I2C && VIDEO_BT848 + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_SP887X if !DVB_FE_CUSTOMISE + select DVB_NXT6000 if !DVB_FE_CUSTOMISE + select DVB_CX24110 if !DVB_FE_CUSTOMISE + select DVB_OR51211 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + help + Support for PCI cards based on the Bt8xx PCI bridge. Examples are + the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, + the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and + some AVerMedia cards. + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the PCI bus, so you need + an external software decoder to watch TV on your computer. + + Say Y if you own such a device and want to use it. diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile new file mode 100644 index 000000000000..36591ae505f4 --- /dev/null +++ b/drivers/media/pci/bt8xx/Makefile @@ -0,0 +1,6 @@ +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 diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c new file mode 100644 index 000000000000..b34fa95185e4 --- /dev/null +++ b/drivers/media/pci/bt8xx/bt878.c @@ -0,0 +1,609 @@ +/* + * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card + * + * Copyright (C) 2002 Peter Hettkamp + * + * large parts based on the bttv driver + * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de) + * & Marcus Metzler (mocm@metzlerbros.de) + * (c) 1999,2000 Gerd Knorr + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "bt878.h" +#include "dst_priv.h" + + +/**************************************/ +/* Miscellaneous utility definitions */ +/**************************************/ + +static unsigned int bt878_verbose = 1; +static unsigned int bt878_debug; + +module_param_named(verbose, bt878_verbose, int, 0444); +MODULE_PARM_DESC(verbose, + "verbose startup messages, default is 1 (yes)"); +module_param_named(debug, bt878_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off)."); + +int bt878_num; +struct bt878 bt878[BT878_MAX]; + +EXPORT_SYMBOL(bt878_num); +EXPORT_SYMBOL(bt878); + +#define btwrite(dat,adr) bmtwrite((dat), (bt->bt878_mem+(adr))) +#define btread(adr) bmtread(bt->bt878_mem+(adr)) + +#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) + +#if defined(dprintk) +#undef dprintk +#endif +#define dprintk(fmt, arg...) \ + do { \ + if (bt878_debug) \ + printk(KERN_DEBUG fmt, ##arg); \ + } while (0) + +static void bt878_mem_free(struct bt878 *bt) +{ + if (bt->buf_cpu) { + pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu, + bt->buf_dma); + bt->buf_cpu = NULL; + } + + if (bt->risc_cpu) { + pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu, + bt->risc_dma); + bt->risc_cpu = NULL; + } +} + +static int bt878_mem_alloc(struct bt878 *bt) +{ + if (!bt->buf_cpu) { + bt->buf_size = 128 * 1024; + + bt->buf_cpu = + pci_alloc_consistent(bt->dev, bt->buf_size, + &bt->buf_dma); + + if (!bt->buf_cpu) + return -ENOMEM; + + memset(bt->buf_cpu, 0, bt->buf_size); + } + + if (!bt->risc_cpu) { + bt->risc_size = PAGE_SIZE; + bt->risc_cpu = + pci_alloc_consistent(bt->dev, bt->risc_size, + &bt->risc_dma); + + if (!bt->risc_cpu) { + bt878_mem_free(bt); + return -ENOMEM; + } + + memset(bt->risc_cpu, 0, bt->risc_size); + } + + return 0; +} + +/* RISC instructions */ +#define RISC_WRITE (0x01 << 28) +#define RISC_JUMP (0x07 << 28) +#define RISC_SYNC (0x08 << 28) + +/* RISC bits */ +#define RISC_WR_SOL (1 << 27) +#define RISC_WR_EOL (1 << 26) +#define RISC_IRQ (1 << 24) +#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16)) +#define RISC_SYNC_RESYNC (1 << 15) +#define RISC_SYNC_FM1 0x06 +#define RISC_SYNC_VRO 0x0C + +#define RISC_FLUSH() bt->risc_pos = 0 +#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr) + +static int bt878_make_risc(struct bt878 *bt) +{ + bt->block_bytes = bt->buf_size >> 4; + bt->block_count = 1 << 4; + bt->line_bytes = bt->block_bytes; + bt->line_count = bt->block_count; + + while (bt->line_bytes > 4095) { + bt->line_bytes >>= 1; + bt->line_count <<= 1; + } + + if (bt->line_count > 255) { + printk(KERN_ERR "bt878: buffer size error!\n"); + return -EINVAL; + } + return 0; +} + + +static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin) +{ + u32 buf_pos = 0; + u32 line; + + RISC_FLUSH(); + RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin); + RISC_INSTR(0); + + dprintk("bt878: risc len lines %u, bytes per line %u\n", + bt->line_count, bt->line_bytes); + for (line = 0; line < bt->line_count; line++) { + // At the beginning of every block we issue an IRQ with previous (finished) block number set + if (!(buf_pos % bt->block_bytes)) + RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | + RISC_IRQ | + RISC_STATUS(((buf_pos / + bt->block_bytes) + + (bt->block_count - + 1)) % + bt->block_count) | bt-> + line_bytes); + else + RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | + bt->line_bytes); + RISC_INSTR(bt->buf_dma + buf_pos); + buf_pos += bt->line_bytes; + } + + RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO); + RISC_INSTR(0); + + RISC_INSTR(RISC_JUMP); + RISC_INSTR(bt->risc_dma); + + btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN); +} + +/*****************************/ +/* Start/Stop grabbing funcs */ +/*****************************/ + +void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, + u32 irq_err_ignore) +{ + u32 int_mask; + + dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg); + /* complete the writing of the risc dma program now we have + * the card specifics + */ + bt878_risc_program(bt, op_sync_orin); + controlreg &= ~0x1f; + controlreg |= 0x1b; + + btwrite(bt->risc_dma, BT878_ARISC_START); + + /* original int mask had : + * 6 2 8 4 0 + * 1111 1111 1000 0000 0000 + * SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI + * Hacked for DST to: + * SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI + */ + int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT | + BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT | + BT878_AFBUS | BT878_ARISCI; + + + /* ignore pesky bits */ + int_mask &= ~irq_err_ignore; + + btwrite(int_mask, BT878_AINT_MASK); + btwrite(controlreg, BT878_AGPIO_DMA_CTL); +} + +void bt878_stop(struct bt878 *bt) +{ + u32 stat; + int i = 0; + + dprintk("bt878 debug: bt878_stop\n"); + + btwrite(0, BT878_AINT_MASK); + btand(~0x13, BT878_AGPIO_DMA_CTL); + + do { + stat = btread(BT878_AINT_STAT); + if (!(stat & BT878_ARISC_EN)) + break; + i++; + } while (i < 500); + + dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n", + bt->nr, i, stat); +} + +EXPORT_SYMBOL(bt878_start); +EXPORT_SYMBOL(bt878_stop); + +/*****************************/ +/* Interrupt service routine */ +/*****************************/ + +static irqreturn_t bt878_irq(int irq, void *dev_id) +{ + u32 stat, astat, mask; + int count; + struct bt878 *bt; + + bt = (struct bt878 *) dev_id; + + count = 0; + while (1) { + stat = btread(BT878_AINT_STAT); + mask = btread(BT878_AINT_MASK); + if (!(astat = (stat & mask))) + return IRQ_NONE; /* this interrupt is not for me */ +/* dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */ + btwrite(astat, BT878_AINT_STAT); /* try to clear interrupt condition */ + + + if (astat & (BT878_ASCERR | BT878_AOCERR)) { + if (bt878_verbose) { + printk(KERN_INFO + "bt878(%d): irq%s%s risc_pc=%08x\n", + bt->nr, + (astat & BT878_ASCERR) ? " SCERR" : + "", + (astat & BT878_AOCERR) ? " OCERR" : + "", btread(BT878_ARISC_PC)); + } + } + if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) { + if (bt878_verbose) { + printk(KERN_INFO + "bt878(%d): irq%s%s%s risc_pc=%08x\n", + bt->nr, + (astat & BT878_APABORT) ? " PABORT" : + "", + (astat & BT878_ARIPERR) ? " RIPERR" : + "", + (astat & BT878_APPERR) ? " PPERR" : + "", btread(BT878_ARISC_PC)); + } + } + if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) { + if (bt878_verbose) { + printk(KERN_INFO + "bt878(%d): irq%s%s%s risc_pc=%08x\n", + bt->nr, + (astat & BT878_AFDSR) ? " FDSR" : "", + (astat & BT878_AFTRGT) ? " FTRGT" : + "", + (astat & BT878_AFBUS) ? " FBUS" : "", + btread(BT878_ARISC_PC)); + } + } + if (astat & BT878_ARISCI) { + bt->finished_block = (stat & BT878_ARISCS) >> 28; + tasklet_schedule(&bt->tasklet); + break; + } + count++; + if (count > 20) { + btwrite(0, BT878_AINT_MASK); + printk(KERN_ERR + "bt878(%d): IRQ lockup, cleared int mask\n", + bt->nr); + break; + } + } + return IRQ_HANDLED; +} + +int +bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp) +{ + int retval; + + retval = 0; + if (mutex_lock_interruptible(&bt->gpio_lock)) + return -ERESTARTSYS; + /* special gpio signal */ + switch (cmd) { + case DST_IG_ENABLE: + // dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable); + retval = bttv_gpio_enable(bt->bttv_nr, + mp->enb.mask, + mp->enb.enable); + break; + case DST_IG_WRITE: + // dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals); + retval = bttv_write_gpio(bt->bttv_nr, + mp->outp.mask, + mp->outp.highvals); + + break; + case DST_IG_READ: + /* read */ + retval = bttv_read_gpio(bt->bttv_nr, &mp->rd.value); + // dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value); + break; + case DST_IG_TS: + /* Set packet size */ + bt->TS_Size = mp->psize; + break; + + default: + retval = -EINVAL; + break; + } + mutex_unlock(&bt->gpio_lock); + return retval; +} + +EXPORT_SYMBOL(bt878_device_control); + +#define BROOKTREE_878_DEVICE(vend, dev, name) \ + { \ + .vendor = PCI_VENDOR_ID_BROOKTREE, \ + .device = PCI_DEVICE_ID_BROOKTREE_878, \ + .subvendor = (vend), .subdevice = (dev), \ + .driver_data = (unsigned long) name \ + } + +static struct pci_device_id bt878_pci_tbl[] __devinitdata = { + BROOKTREE_878_DEVICE(0x0071, 0x0101, "Nebula Electronics DigiTV"), + BROOKTREE_878_DEVICE(0x1461, 0x0761, "AverMedia AverTV DVB-T 761"), + BROOKTREE_878_DEVICE(0x11bd, 0x001c, "Pinnacle PCTV Sat"), + BROOKTREE_878_DEVICE(0x11bd, 0x0026, "Pinnacle PCTV SAT CI"), + BROOKTREE_878_DEVICE(0x1822, 0x0001, "Twinhan VisionPlus DVB"), + BROOKTREE_878_DEVICE(0x270f, 0xfc00, + "ChainTech digitop DST-1000 DVB-S"), + BROOKTREE_878_DEVICE(0x1461, 0x0771, "AVermedia AverTV DVB-T 771"), + BROOKTREE_878_DEVICE(0x18ac, 0xdb10, "DViCO FusionHDTV DVB-T Lite"), + BROOKTREE_878_DEVICE(0x18ac, 0xdb11, "Ultraview DVB-T Lite"), + BROOKTREE_878_DEVICE(0x18ac, 0xd500, "DViCO FusionHDTV 5 Lite"), + BROOKTREE_878_DEVICE(0x7063, 0x2000, "pcHDTV HD-2000 TV"), + BROOKTREE_878_DEVICE(0x1822, 0x0026, "DNTV Live! Mini"), + { } +}; + +MODULE_DEVICE_TABLE(pci, bt878_pci_tbl); + +static const char * __devinit card_name(const struct pci_device_id *id) +{ + return id->driver_data ? (const char *)id->driver_data : "Unknown"; +} + +/***********************/ +/* PCI device handling */ +/***********************/ + +static int __devinit bt878_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + int result = 0; + unsigned char lat; + struct bt878 *bt; +#if defined(__powerpc__) + unsigned int cmd; +#endif + unsigned int cardid; + + printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n", + bt878_num); + if (bt878_num >= BT878_MAX) { + printk(KERN_ERR "bt878: Too many devices inserted\n"); + result = -ENOMEM; + goto fail0; + } + if (pci_enable_device(dev)) + return -EIO; + + cardid = dev->subsystem_device << 16; + cardid |= dev->subsystem_vendor; + + printk(KERN_INFO "%s: card id=[0x%x],[ %s ] has DVB functions.\n", + __func__, cardid, card_name(pci_id)); + + bt = &bt878[bt878_num]; + bt->dev = dev; + bt->nr = bt878_num; + bt->shutdown = 0; + + bt->id = dev->device; + bt->irq = dev->irq; + bt->bt878_adr = pci_resource_start(dev, 0); + if (!request_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0), "bt878")) { + result = -EBUSY; + goto fail0; + } + + bt->revision = dev->revision; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + + + printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ", + bt878_num, bt->id, bt->revision, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + printk("irq: %d, latency: %d, memory: 0x%lx\n", + bt->irq, lat, bt->bt878_adr); + + +#if defined(__powerpc__) + /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ + /* response on cards with no firmware is not enabled by OF */ + pci_read_config_dword(dev, PCI_COMMAND, &cmd); + cmd = (cmd | PCI_COMMAND_MEMORY); + pci_write_config_dword(dev, PCI_COMMAND, cmd); +#endif + +#ifdef __sparc__ + bt->bt878_mem = (unsigned char *) bt->bt878_adr; +#else + bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000); +#endif + + /* clear interrupt mask */ + btwrite(0, BT848_INT_MASK); + + result = request_irq(bt->irq, bt878_irq, + IRQF_SHARED | IRQF_DISABLED, "bt878", + (void *) bt); + if (result == -EINVAL) { + printk(KERN_ERR "bt878(%d): Bad irq number or handler\n", + bt878_num); + goto fail1; + } + if (result == -EBUSY) { + printk(KERN_ERR + "bt878(%d): IRQ %d busy, change your PnP config in BIOS\n", + bt878_num, bt->irq); + goto fail1; + } + if (result < 0) + goto fail1; + + pci_set_master(dev); + pci_set_drvdata(dev, bt); + + if ((result = bt878_mem_alloc(bt))) { + printk(KERN_ERR "bt878: failed to allocate memory!\n"); + goto fail2; + } + + bt878_make_risc(bt); + btwrite(0, BT878_AINT_MASK); + bt878_num++; + + return 0; + + fail2: + free_irq(bt->irq, bt); + fail1: + release_mem_region(pci_resource_start(bt->dev, 0), + pci_resource_len(bt->dev, 0)); + fail0: + pci_disable_device(dev); + return result; +} + +static void __devexit bt878_remove(struct pci_dev *pci_dev) +{ + u8 command; + struct bt878 *bt = pci_get_drvdata(pci_dev); + + if (bt878_verbose) + printk(KERN_INFO "bt878(%d): unloading\n", bt->nr); + + /* turn off all capturing, DMA and IRQs */ + btand(~0x13, BT878_AGPIO_DMA_CTL); + + /* first disable interrupts before unmapping the memory! */ + btwrite(0, BT878_AINT_MASK); + btwrite(~0U, BT878_AINT_STAT); + + /* disable PCI bus-mastering */ + pci_read_config_byte(bt->dev, PCI_COMMAND, &command); + /* Should this be &=~ ?? */ + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(bt->dev, PCI_COMMAND, command); + + free_irq(bt->irq, bt); + printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem); + if (bt->bt878_mem) + iounmap(bt->bt878_mem); + + release_mem_region(pci_resource_start(bt->dev, 0), + pci_resource_len(bt->dev, 0)); + /* wake up any waiting processes + because shutdown flag is set, no new processes (in this queue) + are expected + */ + bt->shutdown = 1; + bt878_mem_free(bt); + + pci_set_drvdata(pci_dev, NULL); + pci_disable_device(pci_dev); + return; +} + +static struct pci_driver bt878_pci_driver = { + .name = "bt878", + .id_table = bt878_pci_tbl, + .probe = bt878_probe, + .remove = __devexit_p(bt878_remove), +}; + +/*******************************/ +/* Module management functions */ +/*******************************/ + +static int __init bt878_init_module(void) +{ + bt878_num = 0; + + printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n", + (BT878_VERSION_CODE >> 16) & 0xff, + (BT878_VERSION_CODE >> 8) & 0xff, + BT878_VERSION_CODE & 0xff); + + return pci_register_driver(&bt878_pci_driver); +} + +static void __exit bt878_cleanup_module(void) +{ + pci_unregister_driver(&bt878_pci_driver); +} + +module_init(bt878_init_module); +module_exit(bt878_cleanup_module); + +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h new file mode 100644 index 000000000000..d19b59299d78 --- /dev/null +++ b/drivers/media/pci/bt8xx/bt878.h @@ -0,0 +1,159 @@ +/* + bt878.h - Bt878 audio module (register offsets) + + Copyright (C) 2002 Peter Hettkamp + + This program is free software; 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 _BT878_H_ +#define _BT878_H_ + +#include +#include +#include +#include +#include + +#include "bt848.h" +#include "bttv.h" + +#define BT878_VERSION_CODE 0x000000 + +#define BT878_AINT_STAT 0x100 +#define BT878_ARISCS (0xf<<28) +#define BT878_ARISC_EN (1<<27) +#define BT878_ASCERR (1<<19) +#define BT878_AOCERR (1<<18) +#define BT878_APABORT (1<<17) +#define BT878_ARIPERR (1<<16) +#define BT878_APPERR (1<<15) +#define BT878_AFDSR (1<<14) +#define BT878_AFTRGT (1<<13) +#define BT878_AFBUS (1<<12) +#define BT878_ARISCI (1<<11) +#define BT878_AOFLOW (1<<3) + +#define BT878_AINT_MASK 0x104 + +#define BT878_AGPIO_DMA_CTL 0x10c +#define BT878_A_GAIN (0xf<<28) +#define BT878_A_G2X (1<<27) +#define BT878_A_PWRDN (1<<26) +#define BT878_A_SEL (3<<24) +#define BT878_DA_SCE (1<<23) +#define BT878_DA_LRI (1<<22) +#define BT878_DA_MLB (1<<21) +#define BT878_DA_LRD (0x1f<<16) +#define BT878_DA_DPM (1<<15) +#define BT878_DA_SBR (1<<14) +#define BT878_DA_ES2 (1<<13) +#define BT878_DA_LMT (1<<12) +#define BT878_DA_SDR (0xf<<8) +#define BT878_DA_IOM (3<<6) +#define BT878_DA_APP (1<<5) +#define BT878_ACAP_EN (1<<4) +#define BT878_PKTP (3<<2) +#define BT878_RISC_EN (1<<1) +#define BT878_FIFO_EN 1 + +#define BT878_APACK_LEN 0x110 +#define BT878_AFP_LEN (0xff<<16) +#define BT878_ALP_LEN 0xfff + +#define BT878_ARISC_START 0x114 + +#define BT878_ARISC_PC 0x120 + +/* BT878 FUNCTION 0 REGISTERS */ +#define BT878_GPIO_DMA_CTL 0x10c + +/* Interrupt register */ +#define BT878_INT_STAT 0x100 +#define BT878_INT_MASK 0x104 +#define BT878_I2CRACK (1<<25) +#define BT878_I2CDONE (1<<8) + +#define BT878_MAX 4 + +#define BT878_RISC_SYNC_MASK (1 << 15) + + +#define BTTV_BOARD_UNKNOWN 0x00 +#define BTTV_BOARD_PINNACLESAT 0x5e +#define BTTV_BOARD_NEBULA_DIGITV 0x68 +#define BTTV_BOARD_PC_HDTV 0x70 +#define BTTV_BOARD_TWINHAN_DST 0x71 +#define BTTV_BOARD_AVDVBT_771 0x7b +#define BTTV_BOARD_AVDVBT_761 0x7c +#define BTTV_BOARD_DVICO_DVBT_LITE 0x80 +#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87 + +extern int bt878_num; + +struct bt878 { + struct mutex gpio_lock; + unsigned int nr; + unsigned int bttv_nr; + struct i2c_adapter *adapter; + struct pci_dev *dev; + unsigned int id; + unsigned int TS_Size; + unsigned char revision; + unsigned int irq; + unsigned long bt878_adr; + volatile void __iomem *bt878_mem; /* function 1 */ + + volatile u32 finished_block; + volatile u32 last_block; + u32 block_count; + u32 block_bytes; + u32 line_bytes; + u32 line_count; + + u32 buf_size; + u8 *buf_cpu; + dma_addr_t buf_dma; + + u32 risc_size; + __le32 *risc_cpu; + dma_addr_t risc_dma; + u32 risc_pos; + + struct tasklet_struct tasklet; + int shutdown; +}; + +extern struct bt878 bt878[BT878_MAX]; + +void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, + u32 irq_err_ignore); +void bt878_stop(struct bt878 *bt); + +#if defined(__powerpc__) /* big-endian */ +static inline void io_st_le32(volatile unsigned __iomem *addr, unsigned val) +{ + st_le32(addr, val); + eieio(); +} + +#define bmtwrite(dat,adr) io_st_le32((adr),(dat)) +#define bmtread(adr) ld_le32((adr)) +#else +#define bmtwrite(dat,adr) writel((dat), (adr)) +#define bmtread(adr) readl(adr) +#endif + +#endif diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c new file mode 100644 index 000000000000..430b3eb11815 --- /dev/null +++ b/drivers/media/pci/bt8xx/dst.c @@ -0,0 +1,1873 @@ +/* + Frontend/Card driver for TwinHan DST Frontend + Copyright (C) 2003 Jamie Honan + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "dvb_frontend.h" +#include "dst_priv.h" +#include "dst_common.h" + +static unsigned int verbose = 1; +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); + +static unsigned int dst_addons; +module_param(dst_addons, int, 0644); +MODULE_PARM_DESC(dst_addons, "CA daughterboard, default is 0 (No addons)"); + +static unsigned int dst_algo; +module_param(dst_algo, int, 0644); +MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)"); + +#define HAS_LOCK 1 +#define ATTEMPT_TUNE 2 +#define HAS_POWER 4 + +#define DST_ERROR 0 +#define DST_NOTICE 1 +#define DST_INFO 2 +#define DST_DEBUG 3 + +#define dprintk(x, y, z, format, arg...) do { \ + if (z) { \ + if ((x > DST_ERROR) && (x > y)) \ + printk(KERN_ERR "dst(%d) %s: " format "\n", \ + state->bt->nr, __func__ , ##arg); \ + else if ((x > DST_NOTICE) && (x > y)) \ + printk(KERN_NOTICE "dst(%d) %s: " format "\n", \ + state->bt->nr, __func__ , ##arg); \ + else if ((x > DST_INFO) && (x > y)) \ + printk(KERN_INFO "dst(%d) %s: " format "\n", \ + state->bt->nr, __func__ , ##arg); \ + else if ((x > DST_DEBUG) && (x > y)) \ + printk(KERN_DEBUG "dst(%d) %s: " format "\n", \ + state->bt->nr, __func__ , ##arg); \ + } else { \ + if (x > y) \ + printk(format, ##arg); \ + } \ +} while(0) + +static int dst_command(struct dst_state *state, u8 *data, u8 len); + +static void dst_packsize(struct dst_state *state, int psize) +{ + union dst_gpio_packet bits; + + bits.psize = psize; + bt878_device_control(state->bt, DST_IG_TS, &bits); +} + +static int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, + u32 outhigh, int delay) +{ + union dst_gpio_packet enb; + union dst_gpio_packet bits; + int err; + + enb.enb.mask = mask; + enb.enb.enable = enbb; + + dprintk(verbose, DST_INFO, 1, "mask=[%04x], enbb=[%04x], outhigh=[%04x]", mask, enbb, outhigh); + if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) { + dprintk(verbose, DST_INFO, 1, "dst_gpio_enb error (err == %i, mask == %02x, enb == %02x)", err, mask, enbb); + return -EREMOTEIO; + } + udelay(1000); + /* because complete disabling means no output, no need to do output packet */ + if (enbb == 0) + return 0; + if (delay) + msleep(10); + bits.outp.mask = enbb; + bits.outp.highvals = outhigh; + if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) { + dprintk(verbose, DST_INFO, 1, "dst_gpio_outb error (err == %i, enbb == %02x, outhigh == %02x)", err, enbb, outhigh); + return -EREMOTEIO; + } + + return 0; +} + +static int dst_gpio_inb(struct dst_state *state, u8 *result) +{ + union dst_gpio_packet rd_packet; + int err; + + *result = 0; + if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb error (err == %i)", err); + return -EREMOTEIO; + } + *result = (u8) rd_packet.rd.value; + + return 0; +} + +int rdc_reset_state(struct dst_state *state) +{ + dprintk(verbose, DST_INFO, 1, "Resetting state machine"); + if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, 0, NO_DELAY) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); + return -1; + } + msleep(10); + if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, RDC_8820_INT, NO_DELAY) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); + msleep(10); + return -1; + } + + return 0; +} +EXPORT_SYMBOL(rdc_reset_state); + +static int rdc_8820_reset(struct dst_state *state) +{ + dprintk(verbose, DST_DEBUG, 1, "Resetting DST"); + if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, 0, NO_DELAY) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); + return -1; + } + udelay(1000); + if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, RDC_8820_RESET, DELAY) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); + return -1; + } + + return 0; +} + +static int dst_pio_enable(struct dst_state *state) +{ + if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_ENABLE, 0, NO_DELAY) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); + return -1; + } + udelay(1000); + + return 0; +} + +int dst_pio_disable(struct dst_state *state) +{ + if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_DISABLE, RDC_8820_PIO_0_DISABLE, NO_DELAY) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); + return -1; + } + if (state->type_flags & DST_TYPE_HAS_FW_1) + udelay(1000); + + return 0; +} +EXPORT_SYMBOL(dst_pio_disable); + +int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode) +{ + u8 reply; + int i; + + for (i = 0; i < 200; i++) { + if (dst_gpio_inb(state, &reply) < 0) { + dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb ERROR !"); + return -1; + } + if ((reply & RDC_8820_PIO_0_ENABLE) == 0) { + dprintk(verbose, DST_INFO, 1, "dst wait ready after %d", i); + return 1; + } + msleep(10); + } + dprintk(verbose, DST_NOTICE, 1, "dst wait NOT ready after %d", i); + + return 0; +} +EXPORT_SYMBOL(dst_wait_dst_ready); + +int dst_error_recovery(struct dst_state *state) +{ + dprintk(verbose, DST_NOTICE, 1, "Trying to return from previous errors."); + dst_pio_disable(state); + msleep(10); + dst_pio_enable(state); + msleep(10); + + return 0; +} +EXPORT_SYMBOL(dst_error_recovery); + +int dst_error_bailout(struct dst_state *state) +{ + dprintk(verbose, DST_INFO, 1, "Trying to bailout from previous error."); + rdc_8820_reset(state); + dst_pio_disable(state); + msleep(10); + + return 0; +} +EXPORT_SYMBOL(dst_error_bailout); + +int dst_comm_init(struct dst_state *state) +{ + dprintk(verbose, DST_INFO, 1, "Initializing DST."); + if ((dst_pio_enable(state)) < 0) { + dprintk(verbose, DST_ERROR, 1, "PIO Enable Failed"); + return -1; + } + if ((rdc_reset_state(state)) < 0) { + dprintk(verbose, DST_ERROR, 1, "RDC 8820 State RESET Failed."); + return -1; + } + if (state->type_flags & DST_TYPE_HAS_FW_1) + msleep(100); + else + msleep(5); + + return 0; +} +EXPORT_SYMBOL(dst_comm_init); + +int write_dst(struct dst_state *state, u8 *data, u8 len) +{ + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = data, + .len = len + }; + + int err; + u8 cnt, i; + + dprintk(verbose, DST_NOTICE, 0, "writing [ "); + for (i = 0; i < len; i++) + dprintk(verbose, DST_NOTICE, 0, "%02x ", data[i]); + dprintk(verbose, DST_NOTICE, 0, "]\n"); + + for (cnt = 0; cnt < 2; cnt++) { + if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { + dprintk(verbose, DST_INFO, 1, "_write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, data[0]); + dst_error_recovery(state); + continue; + } else + break; + } + if (cnt >= 2) { + dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); + dst_error_bailout(state); + + return -1; + } + + return 0; +} +EXPORT_SYMBOL(write_dst); + +int read_dst(struct dst_state *state, u8 *ret, u8 len) +{ + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = ret, + .len = len + }; + + int err; + int cnt; + + for (cnt = 0; cnt < 2; cnt++) { + if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { + dprintk(verbose, DST_INFO, 1, "read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, ret[0]); + dst_error_recovery(state); + continue; + } else + break; + } + if (cnt >= 2) { + dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); + dst_error_bailout(state); + + return -1; + } + dprintk(verbose, DST_DEBUG, 1, "reply is 0x%x", ret[0]); + for (err = 1; err < len; err++) + dprintk(verbose, DST_DEBUG, 0, " 0x%x", ret[err]); + if (err > 1) + dprintk(verbose, DST_DEBUG, 0, "\n"); + + return 0; +} +EXPORT_SYMBOL(read_dst); + +static int dst_set_polarization(struct dst_state *state) +{ + switch (state->voltage) { + case SEC_VOLTAGE_13: /* Vertical */ + dprintk(verbose, DST_INFO, 1, "Polarization=[Vertical]"); + state->tx_tuna[8] &= ~0x40; + break; + case SEC_VOLTAGE_18: /* Horizontal */ + dprintk(verbose, DST_INFO, 1, "Polarization=[Horizontal]"); + state->tx_tuna[8] |= 0x40; + break; + case SEC_VOLTAGE_OFF: + break; + } + + return 0; +} + +static int dst_set_freq(struct dst_state *state, u32 freq) +{ + state->frequency = freq; + dprintk(verbose, DST_INFO, 1, "set Frequency %u", freq); + + if (state->dst_type == DST_TYPE_IS_SAT) { + freq = freq / 1000; + if (freq < 950 || freq > 2150) + return -EINVAL; + state->tx_tuna[2] = (freq >> 8); + state->tx_tuna[3] = (u8) freq; + state->tx_tuna[4] = 0x01; + state->tx_tuna[8] &= ~0x04; + if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { + if (freq < 1531) + state->tx_tuna[8] |= 0x04; + } + } else if (state->dst_type == DST_TYPE_IS_TERR) { + freq = freq / 1000; + if (freq < 137000 || freq > 858000) + return -EINVAL; + state->tx_tuna[2] = (freq >> 16) & 0xff; + state->tx_tuna[3] = (freq >> 8) & 0xff; + state->tx_tuna[4] = (u8) freq; + } else if (state->dst_type == DST_TYPE_IS_CABLE) { + freq = freq / 1000; + state->tx_tuna[2] = (freq >> 16) & 0xff; + state->tx_tuna[3] = (freq >> 8) & 0xff; + state->tx_tuna[4] = (u8) freq; + } else if (state->dst_type == DST_TYPE_IS_ATSC) { + freq = freq / 1000; + if (freq < 51000 || freq > 858000) + return -EINVAL; + state->tx_tuna[2] = (freq >> 16) & 0xff; + state->tx_tuna[3] = (freq >> 8) & 0xff; + state->tx_tuna[4] = (u8) freq; + state->tx_tuna[5] = 0x00; /* ATSC */ + state->tx_tuna[6] = 0x00; + if (state->dst_hw_cap & DST_TYPE_HAS_ANALOG) + state->tx_tuna[7] = 0x00; /* Digital */ + } else + return -EINVAL; + + return 0; +} + +static int dst_set_bandwidth(struct dst_state *state, u32 bandwidth) +{ + state->bandwidth = bandwidth; + + if (state->dst_type != DST_TYPE_IS_TERR) + return -EOPNOTSUPP; + + switch (bandwidth) { + case 6000000: + if (state->dst_hw_cap & DST_TYPE_HAS_CA) + state->tx_tuna[7] = 0x06; + else { + state->tx_tuna[6] = 0x06; + state->tx_tuna[7] = 0x00; + } + break; + case 7000000: + if (state->dst_hw_cap & DST_TYPE_HAS_CA) + state->tx_tuna[7] = 0x07; + else { + state->tx_tuna[6] = 0x07; + state->tx_tuna[7] = 0x00; + } + break; + case 8000000: + if (state->dst_hw_cap & DST_TYPE_HAS_CA) + state->tx_tuna[7] = 0x08; + else { + state->tx_tuna[6] = 0x08; + state->tx_tuna[7] = 0x00; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t inversion) +{ + state->inversion = inversion; + switch (inversion) { + case INVERSION_OFF: /* Inversion = Normal */ + state->tx_tuna[8] &= ~0x80; + break; + case INVERSION_ON: + state->tx_tuna[8] |= 0x80; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int dst_set_fec(struct dst_state *state, fe_code_rate_t fec) +{ + state->fec = fec; + return 0; +} + +static fe_code_rate_t dst_get_fec(struct dst_state *state) +{ + return state->fec; +} + +static int dst_set_symbolrate(struct dst_state *state, u32 srate) +{ + u32 symcalc; + u64 sval; + + state->symbol_rate = srate; + if (state->dst_type == DST_TYPE_IS_TERR) { + return -EOPNOTSUPP; + } + dprintk(verbose, DST_INFO, 1, "set symrate %u", srate); + srate /= 1000; + if (state->dst_type == DST_TYPE_IS_SAT) { + if (state->type_flags & DST_TYPE_HAS_SYMDIV) { + sval = srate; + sval <<= 20; + do_div(sval, 88000); + symcalc = (u32) sval; + dprintk(verbose, DST_INFO, 1, "set symcalc %u", symcalc); + state->tx_tuna[5] = (u8) (symcalc >> 12); + state->tx_tuna[6] = (u8) (symcalc >> 4); + state->tx_tuna[7] = (u8) (symcalc << 4); + } else { + state->tx_tuna[5] = (u8) (srate >> 16) & 0x7f; + state->tx_tuna[6] = (u8) (srate >> 8); + state->tx_tuna[7] = (u8) srate; + } + state->tx_tuna[8] &= ~0x20; + if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { + if (srate > 8000) + state->tx_tuna[8] |= 0x20; + } + } else if (state->dst_type == DST_TYPE_IS_CABLE) { + dprintk(verbose, DST_DEBUG, 1, "%s", state->fw_name); + if (!strncmp(state->fw_name, "DCTNEW", 6)) { + state->tx_tuna[5] = (u8) (srate >> 8); + state->tx_tuna[6] = (u8) srate; + state->tx_tuna[7] = 0x00; + } else if (!strncmp(state->fw_name, "DCT-CI", 6)) { + state->tx_tuna[5] = 0x00; + state->tx_tuna[6] = (u8) (srate >> 8); + state->tx_tuna[7] = (u8) srate; + } + } + return 0; +} + +static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulation) +{ + if (state->dst_type != DST_TYPE_IS_CABLE) + return -EOPNOTSUPP; + + state->modulation = modulation; + switch (modulation) { + case QAM_16: + state->tx_tuna[8] = 0x10; + break; + case QAM_32: + state->tx_tuna[8] = 0x20; + break; + case QAM_64: + state->tx_tuna[8] = 0x40; + break; + case QAM_128: + state->tx_tuna[8] = 0x80; + break; + case QAM_256: + if (!strncmp(state->fw_name, "DCTNEW", 6)) + state->tx_tuna[8] = 0xff; + else if (!strncmp(state->fw_name, "DCT-CI", 6)) + state->tx_tuna[8] = 0x00; + break; + case QPSK: + case QAM_AUTO: + case VSB_8: + case VSB_16: + default: + return -EINVAL; + + } + + return 0; +} + +static fe_modulation_t dst_get_modulation(struct dst_state *state) +{ + return state->modulation; +} + + +u8 dst_check_sum(u8 *buf, u32 len) +{ + u32 i; + u8 val = 0; + if (!len) + return 0; + for (i = 0; i < len; i++) { + val += buf[i]; + } + return ((~val) + 1); +} +EXPORT_SYMBOL(dst_check_sum); + +static void dst_type_flags_print(struct dst_state *state) +{ + u32 type_flags = state->type_flags; + + dprintk(verbose, DST_ERROR, 0, "DST type flags :"); + if (type_flags & DST_TYPE_HAS_TS188) + dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner", DST_TYPE_HAS_TS188); + if (type_flags & DST_TYPE_HAS_NEWTUNE_2) + dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner 2", DST_TYPE_HAS_NEWTUNE_2); + if (type_flags & DST_TYPE_HAS_TS204) + dprintk(verbose, DST_ERROR, 0, " 0x%x ts204", DST_TYPE_HAS_TS204); + if (type_flags & DST_TYPE_HAS_VLF) + dprintk(verbose, DST_ERROR, 0, " 0x%x VLF", DST_TYPE_HAS_VLF); + if (type_flags & DST_TYPE_HAS_SYMDIV) + dprintk(verbose, DST_ERROR, 0, " 0x%x symdiv", DST_TYPE_HAS_SYMDIV); + if (type_flags & DST_TYPE_HAS_FW_1) + dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 1", DST_TYPE_HAS_FW_1); + if (type_flags & DST_TYPE_HAS_FW_2) + dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 2", DST_TYPE_HAS_FW_2); + if (type_flags & DST_TYPE_HAS_FW_3) + dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 3", DST_TYPE_HAS_FW_3); + dprintk(verbose, DST_ERROR, 0, "\n"); +} + + +static int dst_type_print(struct dst_state *state, u8 type) +{ + char *otype; + switch (type) { + case DST_TYPE_IS_SAT: + otype = "satellite"; + break; + + case DST_TYPE_IS_TERR: + otype = "terrestrial"; + break; + + case DST_TYPE_IS_CABLE: + otype = "cable"; + break; + + case DST_TYPE_IS_ATSC: + otype = "atsc"; + break; + + default: + dprintk(verbose, DST_INFO, 1, "invalid dst type %d", type); + return -EINVAL; + } + dprintk(verbose, DST_INFO, 1, "DST type: %s", otype); + + return 0; +} + +static struct tuner_types tuner_list[] = { + { + .tuner_type = TUNER_TYPE_L64724, + .tuner_name = "L 64724", + .board_name = "UNKNOWN", + .fw_name = "UNKNOWN" + }, + + { + .tuner_type = TUNER_TYPE_STV0299, + .tuner_name = "STV 0299", + .board_name = "VP1020", + .fw_name = "DST-MOT" + }, + + { + .tuner_type = TUNER_TYPE_STV0299, + .tuner_name = "STV 0299", + .board_name = "VP1020", + .fw_name = "DST-03T" + }, + + { + .tuner_type = TUNER_TYPE_MB86A15, + .tuner_name = "MB 86A15", + .board_name = "VP1022", + .fw_name = "DST-03T" + }, + + { + .tuner_type = TUNER_TYPE_MB86A15, + .tuner_name = "MB 86A15", + .board_name = "VP1025", + .fw_name = "DST-03T" + }, + + { + .tuner_type = TUNER_TYPE_STV0299, + .tuner_name = "STV 0299", + .board_name = "VP1030", + .fw_name = "DST-CI" + }, + + { + .tuner_type = TUNER_TYPE_STV0299, + .tuner_name = "STV 0299", + .board_name = "VP1030", + .fw_name = "DSTMCI" + }, + + { + .tuner_type = TUNER_TYPE_UNKNOWN, + .tuner_name = "UNKNOWN", + .board_name = "VP2021", + .fw_name = "DCTNEW" + }, + + { + .tuner_type = TUNER_TYPE_UNKNOWN, + .tuner_name = "UNKNOWN", + .board_name = "VP2030", + .fw_name = "DCT-CI" + }, + + { + .tuner_type = TUNER_TYPE_UNKNOWN, + .tuner_name = "UNKNOWN", + .board_name = "VP2031", + .fw_name = "DCT-CI" + }, + + { + .tuner_type = TUNER_TYPE_UNKNOWN, + .tuner_name = "UNKNOWN", + .board_name = "VP2040", + .fw_name = "DCT-CI" + }, + + { + .tuner_type = TUNER_TYPE_UNKNOWN, + .tuner_name = "UNKNOWN", + .board_name = "VP3020", + .fw_name = "DTTFTA" + }, + + { + .tuner_type = TUNER_TYPE_UNKNOWN, + .tuner_name = "UNKNOWN", + .board_name = "VP3021", + .fw_name = "DTTFTA" + }, + + { + .tuner_type = TUNER_TYPE_TDA10046, + .tuner_name = "TDA10046", + .board_name = "VP3040", + .fw_name = "DTT-CI" + }, + + { + .tuner_type = TUNER_TYPE_UNKNOWN, + .tuner_name = "UNKNOWN", + .board_name = "VP3051", + .fw_name = "DTTNXT" + }, + + { + .tuner_type = TUNER_TYPE_NXT200x, + .tuner_name = "NXT200x", + .board_name = "VP3220", + .fw_name = "ATSCDI" + }, + + { + .tuner_type = TUNER_TYPE_NXT200x, + .tuner_name = "NXT200x", + .board_name = "VP3250", + .fw_name = "ATSCAD" + }, +}; + +/* + Known cards list + Satellite + ------------------- + 200103A + VP-1020 DST-MOT LG(old), TS=188 + + VP-1020 DST-03T LG(new), TS=204 + VP-1022 DST-03T LG(new), TS=204 + VP-1025 DST-03T LG(new), TS=204 + + VP-1030 DSTMCI, LG(new), TS=188 + VP-1032 DSTMCI, LG(new), TS=188 + + Cable + ------------------- + VP-2030 DCT-CI, Samsung, TS=204 + VP-2021 DCT-CI, Unknown, TS=204 + VP-2031 DCT-CI, Philips, TS=188 + VP-2040 DCT-CI, Philips, TS=188, with CA daughter board + VP-2040 DCT-CI, Philips, TS=204, without CA daughter board + + Terrestrial + ------------------- + VP-3050 DTTNXT TS=188 + VP-3040 DTT-CI, Philips, TS=188 + VP-3040 DTT-CI, Philips, TS=204 + + ATSC + ------------------- + VP-3220 ATSCDI, TS=188 + VP-3250 ATSCAD, TS=188 + +*/ + +static struct dst_types dst_tlist[] = { + { + .device_id = "200103A", + .offset = 0, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_OBS_REGS, + .dst_feature = 0, + .tuner_type = 0 + }, /* obsolete */ + + { + .device_id = "DST-020", + .offset = 0, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, + .dst_feature = 0, + .tuner_type = 0 + }, /* obsolete */ + + { + .device_id = "DST-030", + .offset = 0, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, + .dst_feature = 0, + .tuner_type = 0 + }, /* obsolete */ + + { + .device_id = "DST-03T", + .offset = 0, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_2, + .dst_feature = DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 | DST_TYPE_HAS_DISEQC5 + | DST_TYPE_HAS_MAC | DST_TYPE_HAS_MOTO, + .tuner_type = TUNER_TYPE_MULTI + }, + + { + .device_id = "DST-MOT", + .offset = 0, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, + .dst_feature = 0, + .tuner_type = 0 + }, /* obsolete */ + + { + .device_id = "DST-CI", + .offset = 1, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_1, + .dst_feature = DST_TYPE_HAS_CA, + .tuner_type = 0 + }, /* An OEM board */ + + { + .device_id = "DSTMCI", + .offset = 1, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_INC_COUNT | DST_TYPE_HAS_VLF, + .dst_feature = DST_TYPE_HAS_CA | DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 + | DST_TYPE_HAS_MOTO | DST_TYPE_HAS_MAC, + .tuner_type = TUNER_TYPE_MULTI + }, + + { + .device_id = "DSTFCI", + .offset = 1, + .dst_type = DST_TYPE_IS_SAT, + .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, + .dst_feature = 0, + .tuner_type = 0 + }, /* unknown to vendor */ + + { + .device_id = "DCT-CI", + .offset = 1, + .dst_type = DST_TYPE_IS_CABLE, + .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_VLF, + .dst_feature = DST_TYPE_HAS_CA, + .tuner_type = 0 + }, + + { + .device_id = "DCTNEW", + .offset = 1, + .dst_type = DST_TYPE_IS_CABLE, + .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_3 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_MULTI_FE, + .dst_feature = 0, + .tuner_type = 0 + }, + + { + .device_id = "DTT-CI", + .offset = 1, + .dst_type = DST_TYPE_IS_TERR, + .type_flags = DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_VLF, + .dst_feature = DST_TYPE_HAS_CA, + .tuner_type = 0 + }, + + { + .device_id = "DTTDIG", + .offset = 1, + .dst_type = DST_TYPE_IS_TERR, + .type_flags = DST_TYPE_HAS_FW_2, + .dst_feature = 0, + .tuner_type = 0 + }, + + { + .device_id = "DTTNXT", + .offset = 1, + .dst_type = DST_TYPE_IS_TERR, + .type_flags = DST_TYPE_HAS_FW_2, + .dst_feature = DST_TYPE_HAS_ANALOG, + .tuner_type = 0 + }, + + { + .device_id = "ATSCDI", + .offset = 1, + .dst_type = DST_TYPE_IS_ATSC, + .type_flags = DST_TYPE_HAS_FW_2, + .dst_feature = 0, + .tuner_type = 0 + }, + + { + .device_id = "ATSCAD", + .offset = 1, + .dst_type = DST_TYPE_IS_ATSC, + .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD, + .dst_feature = DST_TYPE_HAS_MAC | DST_TYPE_HAS_ANALOG, + .tuner_type = 0 + }, + + { } + +}; + +static int dst_get_mac(struct dst_state *state) +{ + u8 get_mac[] = { 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + get_mac[7] = dst_check_sum(get_mac, 7); + if (dst_command(state, get_mac, 8) < 0) { + dprintk(verbose, DST_INFO, 1, "Unsupported Command"); + return -1; + } + memset(&state->mac_address, '\0', 8); + memcpy(&state->mac_address, &state->rxbuffer, 6); + dprintk(verbose, DST_ERROR, 1, "MAC Address=[%pM]", state->mac_address); + + return 0; +} + +static int dst_fw_ver(struct dst_state *state) +{ + u8 get_ver[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + get_ver[7] = dst_check_sum(get_ver, 7); + if (dst_command(state, get_ver, 8) < 0) { + dprintk(verbose, DST_INFO, 1, "Unsupported Command"); + return -1; + } + memcpy(&state->fw_version, &state->rxbuffer, 8); + dprintk(verbose, DST_ERROR, 1, "Firmware Ver = %x.%x Build = %02x, on %x:%x, %x-%x-20%02x", + state->fw_version[0] >> 4, state->fw_version[0] & 0x0f, + state->fw_version[1], + state->fw_version[5], state->fw_version[6], + state->fw_version[4], state->fw_version[3], state->fw_version[2]); + + return 0; +} + +static int dst_card_type(struct dst_state *state) +{ + int j; + struct tuner_types *p_tuner_list = NULL; + + u8 get_type[] = { 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + get_type[7] = dst_check_sum(get_type, 7); + if (dst_command(state, get_type, 8) < 0) { + dprintk(verbose, DST_INFO, 1, "Unsupported Command"); + return -1; + } + memset(&state->card_info, '\0', 8); + memcpy(&state->card_info, &state->rxbuffer, 7); + dprintk(verbose, DST_ERROR, 1, "Device Model=[%s]", &state->card_info[0]); + + for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { + if (!strcmp(&state->card_info[0], p_tuner_list->board_name)) { + state->tuner_type = p_tuner_list->tuner_type; + dprintk(verbose, DST_ERROR, 1, "DST has [%s] tuner, tuner type=[%d]", + p_tuner_list->tuner_name, p_tuner_list->tuner_type); + } + } + + return 0; +} + +static int dst_get_vendor(struct dst_state *state) +{ + u8 get_vendor[] = { 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + get_vendor[7] = dst_check_sum(get_vendor, 7); + if (dst_command(state, get_vendor, 8) < 0) { + dprintk(verbose, DST_INFO, 1, "Unsupported Command"); + return -1; + } + memset(&state->vendor, '\0', 8); + memcpy(&state->vendor, &state->rxbuffer, 7); + dprintk(verbose, DST_ERROR, 1, "Vendor=[%s]", &state->vendor[0]); + + return 0; +} + +static void debug_dst_buffer(struct dst_state *state) +{ + int i; + + if (verbose > 2) { + printk("%s: [", __func__); + for (i = 0; i < 8; i++) + printk(" %02x", state->rxbuffer[i]); + printk("]\n"); + } +} + +static int dst_check_stv0299(struct dst_state *state) +{ + u8 check_stv0299[] = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + check_stv0299[7] = dst_check_sum(check_stv0299, 7); + if (dst_command(state, check_stv0299, 8) < 0) { + dprintk(verbose, DST_ERROR, 1, "Cmd=[0x04] failed"); + return -1; + } + debug_dst_buffer(state); + + if (memcmp(&check_stv0299, &state->rxbuffer, 8)) { + dprintk(verbose, DST_ERROR, 1, "Found a STV0299 NIM"); + state->tuner_type = TUNER_TYPE_STV0299; + return 0; + } + + return -1; +} + +static int dst_check_mb86a15(struct dst_state *state) +{ + u8 check_mb86a15[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + check_mb86a15[7] = dst_check_sum(check_mb86a15, 7); + if (dst_command(state, check_mb86a15, 8) < 0) { + dprintk(verbose, DST_ERROR, 1, "Cmd=[0x10], failed"); + return -1; + } + debug_dst_buffer(state); + + if (memcmp(&check_mb86a15, &state->rxbuffer, 8) < 0) { + dprintk(verbose, DST_ERROR, 1, "Found a MB86A15 NIM"); + state->tuner_type = TUNER_TYPE_MB86A15; + return 0; + } + + return -1; +} + +static int dst_get_tuner_info(struct dst_state *state) +{ + u8 get_tuner_1[] = { 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + u8 get_tuner_2[] = { 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + get_tuner_1[7] = dst_check_sum(get_tuner_1, 7); + get_tuner_2[7] = dst_check_sum(get_tuner_2, 7); + dprintk(verbose, DST_ERROR, 1, "DST TYpe = MULTI FE"); + if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { + if (dst_command(state, get_tuner_1, 8) < 0) { + dprintk(verbose, DST_INFO, 1, "Cmd=[0x13], Unsupported"); + goto force; + } + } else { + if (dst_command(state, get_tuner_2, 8) < 0) { + dprintk(verbose, DST_INFO, 1, "Cmd=[0xb], Unsupported"); + goto force; + } + } + memcpy(&state->board_info, &state->rxbuffer, 8); + if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { + dprintk(verbose, DST_ERROR, 1, "DST type has TS=188"); + } + if (state->board_info[0] == 0xbc) { + if (state->dst_type != DST_TYPE_IS_ATSC) + state->type_flags |= DST_TYPE_HAS_TS188; + else + state->type_flags |= DST_TYPE_HAS_NEWTUNE_2; + + if (state->board_info[1] == 0x01) { + state->dst_hw_cap |= DST_TYPE_HAS_DBOARD; + dprintk(verbose, DST_ERROR, 1, "DST has Daughterboard"); + } + } + + return 0; +force: + if (!strncmp(state->fw_name, "DCT-CI", 6)) { + state->type_flags |= DST_TYPE_HAS_TS204; + dprintk(verbose, DST_ERROR, 1, "Forcing [%s] to TS188", state->fw_name); + } + + return -1; +} + +static int dst_get_device_id(struct dst_state *state) +{ + u8 reply; + + int i, j; + struct dst_types *p_dst_type = NULL; + struct tuner_types *p_tuner_list = NULL; + + u8 use_dst_type = 0; + u32 use_type_flags = 0; + + static u8 device_type[8] = {0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; + + state->tuner_type = 0; + device_type[7] = dst_check_sum(device_type, 7); + + if (write_dst(state, device_type, FIXED_COMM)) + return -1; /* Write failed */ + if ((dst_pio_disable(state)) < 0) + return -1; + if (read_dst(state, &reply, GET_ACK)) + return -1; /* Read failure */ + if (reply != ACK) { + dprintk(verbose, DST_INFO, 1, "Write not Acknowledged! [Reply=0x%02x]", reply); + return -1; /* Unack'd write */ + } + if (!dst_wait_dst_ready(state, DEVICE_INIT)) + return -1; /* DST not ready yet */ + if (read_dst(state, state->rxbuffer, FIXED_COMM)) + return -1; + + dst_pio_disable(state); + if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { + dprintk(verbose, DST_INFO, 1, "Checksum failure!"); + return -1; /* Checksum failure */ + } + state->rxbuffer[7] = '\0'; + + for (i = 0, p_dst_type = dst_tlist; i < ARRAY_SIZE(dst_tlist); i++, p_dst_type++) { + if (!strncmp (&state->rxbuffer[p_dst_type->offset], p_dst_type->device_id, strlen (p_dst_type->device_id))) { + use_type_flags = p_dst_type->type_flags; + use_dst_type = p_dst_type->dst_type; + + /* Card capabilities */ + state->dst_hw_cap = p_dst_type->dst_feature; + dprintk(verbose, DST_ERROR, 1, "Recognise [%s]", p_dst_type->device_id); + strncpy(&state->fw_name[0], p_dst_type->device_id, 6); + /* Multiple tuners */ + if (p_dst_type->tuner_type & TUNER_TYPE_MULTI) { + switch (use_dst_type) { + case DST_TYPE_IS_SAT: + /* STV0299 check */ + if (dst_check_stv0299(state) < 0) { + dprintk(verbose, DST_ERROR, 1, "Unsupported"); + state->tuner_type = TUNER_TYPE_MB86A15; + } + break; + default: + break; + } + if (dst_check_mb86a15(state) < 0) + dprintk(verbose, DST_ERROR, 1, "Unsupported"); + /* Single tuner */ + } else { + state->tuner_type = p_dst_type->tuner_type; + } + for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { + if (!(strncmp(p_dst_type->device_id, p_tuner_list->fw_name, 7)) && + p_tuner_list->tuner_type == state->tuner_type) { + dprintk(verbose, DST_ERROR, 1, "[%s] has a [%s]", + p_dst_type->device_id, p_tuner_list->tuner_name); + } + } + break; + } + } + + if (i >= ARRAY_SIZE(dst_tlist)) { + dprintk(verbose, DST_ERROR, 1, "Unable to recognize %s or %s", &state->rxbuffer[0], &state->rxbuffer[1]); + dprintk(verbose, DST_ERROR, 1, "please email linux-dvb@linuxtv.org with this type in"); + use_dst_type = DST_TYPE_IS_SAT; + use_type_flags = DST_TYPE_HAS_SYMDIV; + } + dst_type_print(state, use_dst_type); + state->type_flags = use_type_flags; + state->dst_type = use_dst_type; + dst_type_flags_print(state); + + return 0; +} + +static int dst_probe(struct dst_state *state) +{ + mutex_init(&state->dst_mutex); + if (dst_addons & DST_TYPE_HAS_CA) { + if ((rdc_8820_reset(state)) < 0) { + dprintk(verbose, DST_ERROR, 1, "RDC 8820 RESET Failed."); + return -1; + } + msleep(4000); + } else { + msleep(100); + } + if ((dst_comm_init(state)) < 0) { + dprintk(verbose, DST_ERROR, 1, "DST Initialization Failed."); + return -1; + } + msleep(100); + if (dst_get_device_id(state) < 0) { + dprintk(verbose, DST_ERROR, 1, "unknown device."); + return -1; + } + if (dst_get_mac(state) < 0) { + dprintk(verbose, DST_INFO, 1, "MAC: Unsupported command"); + } + if ((state->type_flags & DST_TYPE_HAS_MULTI_FE) || (state->type_flags & DST_TYPE_HAS_FW_BUILD)) { + if (dst_get_tuner_info(state) < 0) + dprintk(verbose, DST_INFO, 1, "Tuner: Unsupported command"); + } + if (state->type_flags & DST_TYPE_HAS_TS204) { + dst_packsize(state, 204); + } + if (state->type_flags & DST_TYPE_HAS_FW_BUILD) { + if (dst_fw_ver(state) < 0) { + dprintk(verbose, DST_INFO, 1, "FW: Unsupported command"); + return 0; + } + if (dst_card_type(state) < 0) { + dprintk(verbose, DST_INFO, 1, "Card: Unsupported command"); + return 0; + } + if (dst_get_vendor(state) < 0) { + dprintk(verbose, DST_INFO, 1, "Vendor: Unsupported command"); + return 0; + } + } + + return 0; +} + +static int dst_command(struct dst_state *state, u8 *data, u8 len) +{ + u8 reply; + + mutex_lock(&state->dst_mutex); + if ((dst_comm_init(state)) < 0) { + dprintk(verbose, DST_NOTICE, 1, "DST Communication Initialization Failed."); + goto error; + } + if (write_dst(state, data, len)) { + dprintk(verbose, DST_INFO, 1, "Trying to recover.. "); + if ((dst_error_recovery(state)) < 0) { + dprintk(verbose, DST_ERROR, 1, "Recovery Failed."); + goto error; + } + goto error; + } + if ((dst_pio_disable(state)) < 0) { + dprintk(verbose, DST_ERROR, 1, "PIO Disable Failed."); + goto error; + } + if (state->type_flags & DST_TYPE_HAS_FW_1) + mdelay(3); + if (read_dst(state, &reply, GET_ACK)) { + dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); + if ((dst_error_recovery(state)) < 0) { + dprintk(verbose, DST_INFO, 1, "Recovery Failed."); + goto error; + } + goto error; + } + if (reply != ACK) { + dprintk(verbose, DST_INFO, 1, "write not acknowledged 0x%02x ", reply); + goto error; + } + if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3)) + goto error; + if (state->type_flags & DST_TYPE_HAS_FW_1) + mdelay(3); + else + udelay(2000); + if (!dst_wait_dst_ready(state, NO_DELAY)) + goto error; + if (read_dst(state, state->rxbuffer, FIXED_COMM)) { + dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); + if ((dst_error_recovery(state)) < 0) { + dprintk(verbose, DST_INFO, 1, "Recovery failed."); + goto error; + } + goto error; + } + if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { + dprintk(verbose, DST_INFO, 1, "checksum failure"); + goto error; + } + mutex_unlock(&state->dst_mutex); + return 0; + +error: + mutex_unlock(&state->dst_mutex); + return -EIO; + +} + +static int dst_get_signal(struct dst_state *state) +{ + int retval; + u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb }; + //dprintk("%s: Getting Signal strength and other parameters\n", __func__); + if ((state->diseq_flags & ATTEMPT_TUNE) == 0) { + state->decode_lock = state->decode_strength = state->decode_snr = 0; + return 0; + } + if (0 == (state->diseq_flags & HAS_LOCK)) { + state->decode_lock = state->decode_strength = state->decode_snr = 0; + return 0; + } + if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) { + retval = dst_command(state, get_signal, 8); + if (retval < 0) + return retval; + if (state->dst_type == DST_TYPE_IS_SAT) { + state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0; + state->decode_strength = state->rxbuffer[5] << 8; + state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; + } else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) { + state->decode_lock = (state->rxbuffer[1]) ? 1 : 0; + state->decode_strength = state->rxbuffer[4] << 8; + state->decode_snr = state->rxbuffer[3] << 8; + } else if (state->dst_type == DST_TYPE_IS_ATSC) { + state->decode_lock = (state->rxbuffer[6] == 0x00) ? 1 : 0; + state->decode_strength = state->rxbuffer[4] << 8; + state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; + } + state->cur_jiff = jiffies; + } + return 0; +} + +static int dst_tone_power_cmd(struct dst_state *state) +{ + u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 }; + + if (state->dst_type != DST_TYPE_IS_SAT) + return -EOPNOTSUPP; + paket[4] = state->tx_tuna[4]; + paket[2] = state->tx_tuna[2]; + paket[3] = state->tx_tuna[3]; + paket[7] = dst_check_sum (paket, 7); + return dst_command(state, paket, 8); +} + +static int dst_get_tuna(struct dst_state *state) +{ + int retval; + + if ((state->diseq_flags & ATTEMPT_TUNE) == 0) + return 0; + state->diseq_flags &= ~(HAS_LOCK); + if (!dst_wait_dst_ready(state, NO_DELAY)) + return -EIO; + if ((state->type_flags & DST_TYPE_HAS_VLF) && + !(state->dst_type == DST_TYPE_IS_ATSC)) + + retval = read_dst(state, state->rx_tuna, 10); + else + retval = read_dst(state, &state->rx_tuna[2], FIXED_COMM); + if (retval < 0) { + dprintk(verbose, DST_DEBUG, 1, "read not successful"); + return retval; + } + if ((state->type_flags & DST_TYPE_HAS_VLF) && + !(state->dst_type == DST_TYPE_IS_ATSC)) { + + if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) { + dprintk(verbose, DST_INFO, 1, "checksum failure ? "); + return -EIO; + } + } else { + if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) { + dprintk(verbose, DST_INFO, 1, "checksum failure? "); + return -EIO; + } + } + if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0) + return 0; + if (state->dst_type == DST_TYPE_IS_SAT) { + state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3]; + } else { + state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 16) + (state->rx_tuna[3] << 8) + state->rx_tuna[4]; + } + state->decode_freq = state->decode_freq * 1000; + state->decode_lock = 1; + state->diseq_flags |= HAS_LOCK; + + return 1; +} + +static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + +static int dst_write_tuna(struct dvb_frontend *fe) +{ + struct dst_state *state = fe->demodulator_priv; + int retval; + u8 reply; + + dprintk(verbose, DST_INFO, 1, "type_flags 0x%x ", state->type_flags); + state->decode_freq = 0; + state->decode_lock = state->decode_strength = state->decode_snr = 0; + if (state->dst_type == DST_TYPE_IS_SAT) { + if (!(state->diseq_flags & HAS_POWER)) + dst_set_voltage(fe, SEC_VOLTAGE_13); + } + state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE); + mutex_lock(&state->dst_mutex); + if ((dst_comm_init(state)) < 0) { + dprintk(verbose, DST_DEBUG, 1, "DST Communication initialization failed."); + goto error; + } +// if (state->type_flags & DST_TYPE_HAS_NEWTUNE) { + if ((state->type_flags & DST_TYPE_HAS_VLF) && + (!(state->dst_type == DST_TYPE_IS_ATSC))) { + + state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9); + retval = write_dst(state, &state->tx_tuna[0], 10); + } else { + state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7); + retval = write_dst(state, &state->tx_tuna[2], FIXED_COMM); + } + if (retval < 0) { + dst_pio_disable(state); + dprintk(verbose, DST_DEBUG, 1, "write not successful"); + goto werr; + } + if ((dst_pio_disable(state)) < 0) { + dprintk(verbose, DST_DEBUG, 1, "DST PIO disable failed !"); + goto error; + } + if ((read_dst(state, &reply, GET_ACK) < 0)) { + dprintk(verbose, DST_DEBUG, 1, "read verify not successful."); + goto error; + } + if (reply != ACK) { + dprintk(verbose, DST_DEBUG, 1, "write not acknowledged 0x%02x ", reply); + goto error; + } + state->diseq_flags |= ATTEMPT_TUNE; + retval = dst_get_tuna(state); +werr: + mutex_unlock(&state->dst_mutex); + return retval; + +error: + mutex_unlock(&state->dst_mutex); + return -EIO; +} + +/* + * line22k0 0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00 + * line22k1 0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00 + * line22k2 0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00 + * tone 0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00 + * data 0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00 + * power_off 0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 + * power_on 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 + * Diseqc 1 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec + * Diseqc 2 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8 + * Diseqc 3 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4 + * Diseqc 4 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0 + */ + +static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct dst_state *state = fe->demodulator_priv; + u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec }; + + if (state->dst_type != DST_TYPE_IS_SAT) + return -EOPNOTSUPP; + if (cmd->msg_len > 0 && cmd->msg_len < 5) + memcpy(&paket[3], cmd->msg, cmd->msg_len); + else if (cmd->msg_len == 5 && state->dst_hw_cap & DST_TYPE_HAS_DISEQC5) + memcpy(&paket[2], cmd->msg, cmd->msg_len); + else + return -EINVAL; + paket[7] = dst_check_sum(&paket[0], 7); + return dst_command(state, paket, 8); +} + +static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + int need_cmd, retval = 0; + struct dst_state *state = fe->demodulator_priv; + + state->voltage = voltage; + if (state->dst_type != DST_TYPE_IS_SAT) + return -EOPNOTSUPP; + + need_cmd = 0; + + switch (voltage) { + case SEC_VOLTAGE_13: + case SEC_VOLTAGE_18: + if ((state->diseq_flags & HAS_POWER) == 0) + need_cmd = 1; + state->diseq_flags |= HAS_POWER; + state->tx_tuna[4] = 0x01; + break; + case SEC_VOLTAGE_OFF: + need_cmd = 1; + state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE); + state->tx_tuna[4] = 0x00; + break; + default: + return -EINVAL; + } + + if (need_cmd) + retval = dst_tone_power_cmd(state); + + return retval; +} + +static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct dst_state *state = fe->demodulator_priv; + + state->tone = tone; + if (state->dst_type != DST_TYPE_IS_SAT) + return -EOPNOTSUPP; + + switch (tone) { + case SEC_TONE_OFF: + if (state->type_flags & DST_TYPE_HAS_OBS_REGS) + state->tx_tuna[2] = 0x00; + else + state->tx_tuna[2] = 0xff; + break; + + case SEC_TONE_ON: + state->tx_tuna[2] = 0x02; + break; + default: + return -EINVAL; + } + return dst_tone_power_cmd(state); +} + +static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd) +{ + struct dst_state *state = fe->demodulator_priv; + + if (state->dst_type != DST_TYPE_IS_SAT) + return -EOPNOTSUPP; + state->minicmd = minicmd; + switch (minicmd) { + case SEC_MINI_A: + state->tx_tuna[3] = 0x02; + break; + case SEC_MINI_B: + state->tx_tuna[3] = 0xff; + break; + } + return dst_tone_power_cmd(state); +} + + +static int dst_init(struct dvb_frontend *fe) +{ + struct dst_state *state = fe->demodulator_priv; + + static u8 sat_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x00, 0x73, 0x21, 0x00, 0x00 }; + static u8 sat_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x55, 0xbd, 0x50, 0x00, 0x00 }; + static u8 ter_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; + static u8 ter_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; + static u8 cab_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; + static u8 cab_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; + static u8 atsc_tuner[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; + + state->inversion = INVERSION_OFF; + state->voltage = SEC_VOLTAGE_13; + state->tone = SEC_TONE_OFF; + state->diseq_flags = 0; + state->k22 = 0x02; + state->bandwidth = 7000000; + state->cur_jiff = jiffies; + if (state->dst_type == DST_TYPE_IS_SAT) + memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? sat_tuna_188 : sat_tuna_204), sizeof (sat_tuna_204)); + else if (state->dst_type == DST_TYPE_IS_TERR) + memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? ter_tuna_188 : ter_tuna_204), sizeof (ter_tuna_204)); + else if (state->dst_type == DST_TYPE_IS_CABLE) + memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? cab_tuna_188 : cab_tuna_204), sizeof (cab_tuna_204)); + else if (state->dst_type == DST_TYPE_IS_ATSC) + memcpy(state->tx_tuna, atsc_tuner, sizeof (atsc_tuner)); + + return 0; +} + +static int dst_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct dst_state *state = fe->demodulator_priv; + + *status = 0; + if (state->diseq_flags & HAS_LOCK) { +// dst_get_signal(state); // don't require(?) to ask MCU + if (state->decode_lock) + *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI; + } + + return 0; +} + +static int dst_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dst_state *state = fe->demodulator_priv; + + int retval = dst_get_signal(state); + *strength = state->decode_strength; + + return retval; +} + +static int dst_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dst_state *state = fe->demodulator_priv; + + int retval = dst_get_signal(state); + *snr = state->decode_snr; + + return retval; +} + +static int dst_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int retval = -EINVAL; + struct dst_state *state = fe->demodulator_priv; + + if (p != NULL) { + retval = dst_set_freq(state, p->frequency); + if(retval != 0) + return retval; + dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); + + if (state->dst_type == DST_TYPE_IS_SAT) { + if (state->type_flags & DST_TYPE_HAS_OBS_REGS) + dst_set_inversion(state, p->inversion); + dst_set_fec(state, p->fec_inner); + dst_set_symbolrate(state, p->symbol_rate); + dst_set_polarization(state); + dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); + + } else if (state->dst_type == DST_TYPE_IS_TERR) + dst_set_bandwidth(state, p->bandwidth_hz); + else if (state->dst_type == DST_TYPE_IS_CABLE) { + dst_set_fec(state, p->fec_inner); + dst_set_symbolrate(state, p->symbol_rate); + dst_set_modulation(state, p->modulation); + } + retval = dst_write_tuna(fe); + } + + return retval; +} + +static int dst_tune_frontend(struct dvb_frontend* fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + fe_status_t *status) +{ + struct dst_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (re_tune) { + dst_set_freq(state, p->frequency); + dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); + + if (state->dst_type == DST_TYPE_IS_SAT) { + if (state->type_flags & DST_TYPE_HAS_OBS_REGS) + dst_set_inversion(state, p->inversion); + dst_set_fec(state, p->fec_inner); + dst_set_symbolrate(state, p->symbol_rate); + dst_set_polarization(state); + dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); + + } else if (state->dst_type == DST_TYPE_IS_TERR) + dst_set_bandwidth(state, p->bandwidth_hz); + else if (state->dst_type == DST_TYPE_IS_CABLE) { + dst_set_fec(state, p->fec_inner); + dst_set_symbolrate(state, p->symbol_rate); + dst_set_modulation(state, p->modulation); + } + dst_write_tuna(fe); + } + + if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) + dst_read_status(fe, status); + + *delay = HZ/10; + return 0; +} + +static int dst_get_tuning_algo(struct dvb_frontend *fe) +{ + return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW; +} + +static int dst_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dst_state *state = fe->demodulator_priv; + + p->frequency = state->decode_freq; + if (state->dst_type == DST_TYPE_IS_SAT) { + if (state->type_flags & DST_TYPE_HAS_OBS_REGS) + p->inversion = state->inversion; + p->symbol_rate = state->symbol_rate; + p->fec_inner = dst_get_fec(state); + } else if (state->dst_type == DST_TYPE_IS_TERR) { + p->bandwidth_hz = state->bandwidth; + } else if (state->dst_type == DST_TYPE_IS_CABLE) { + p->symbol_rate = state->symbol_rate; + p->fec_inner = dst_get_fec(state); + p->modulation = dst_get_modulation(state); + } + + return 0; +} + +static void dst_release(struct dvb_frontend *fe) +{ + struct dst_state *state = fe->demodulator_priv; + if (state->dst_ca) { + dvb_unregister_device(state->dst_ca); +#ifdef CONFIG_MEDIA_ATTACH + symbol_put(dst_ca_attach); +#endif + } + kfree(state); +} + +static struct dvb_frontend_ops dst_dvbt_ops; +static struct dvb_frontend_ops dst_dvbs_ops; +static struct dvb_frontend_ops dst_dvbc_ops; +static struct dvb_frontend_ops dst_atsc_ops; + +struct dst_state *dst_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter) +{ + /* check if the ASIC is there */ + if (dst_probe(state) < 0) { + kfree(state); + return NULL; + } + /* determine settings based on type */ + /* create dvb_frontend */ + switch (state->dst_type) { + case DST_TYPE_IS_TERR: + memcpy(&state->frontend.ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops)); + break; + case DST_TYPE_IS_CABLE: + memcpy(&state->frontend.ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops)); + break; + case DST_TYPE_IS_SAT: + memcpy(&state->frontend.ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops)); + break; + case DST_TYPE_IS_ATSC: + memcpy(&state->frontend.ops, &dst_atsc_ops, sizeof(struct dvb_frontend_ops)); + break; + default: + dprintk(verbose, DST_ERROR, 1, "unknown DST type. please report to the LinuxTV.org DVB mailinglist."); + kfree(state); + return NULL; + } + state->frontend.demodulator_priv = state; + + return state; /* Manu (DST is a card not a frontend) */ +} + +EXPORT_SYMBOL(dst_attach); + +static struct dvb_frontend_ops dst_dvbt_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "DST DVB-T", + .frequency_min = 137000000, + .frequency_max = 858000000, + .frequency_stepsize = 166667, + .caps = FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = dst_release, + .init = dst_init, + .tune = dst_tune_frontend, + .set_frontend = dst_set_frontend, + .get_frontend = dst_get_frontend, + .get_frontend_algo = dst_get_tuning_algo, + .read_status = dst_read_status, + .read_signal_strength = dst_read_signal_strength, + .read_snr = dst_read_snr, +}; + +static struct dvb_frontend_ops dst_dvbs_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "DST DVB-S", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, /* kHz for QPSK frontends */ + .frequency_tolerance = 29500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + /* . symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK + }, + + .release = dst_release, + .init = dst_init, + .tune = dst_tune_frontend, + .set_frontend = dst_set_frontend, + .get_frontend = dst_get_frontend, + .get_frontend_algo = dst_get_tuning_algo, + .read_status = dst_read_status, + .read_signal_strength = dst_read_signal_strength, + .read_snr = dst_read_snr, + .diseqc_send_burst = dst_send_burst, + .diseqc_send_master_cmd = dst_set_diseqc, + .set_voltage = dst_set_voltage, + .set_tone = dst_set_tone, +}; + +static struct dvb_frontend_ops dst_dvbc_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "DST DVB-C", + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 + }, + + .release = dst_release, + .init = dst_init, + .tune = dst_tune_frontend, + .set_frontend = dst_set_frontend, + .get_frontend = dst_get_frontend, + .get_frontend_algo = dst_get_tuning_algo, + .read_status = dst_read_status, + .read_signal_strength = dst_read_signal_strength, + .read_snr = dst_read_snr, +}; + +static struct dvb_frontend_ops dst_atsc_ops = { + .delsys = { SYS_ATSC }, + .info = { + .name = "DST ATSC", + .frequency_stepsize = 62500, + .frequency_min = 510000000, + .frequency_max = 858000000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .release = dst_release, + .init = dst_init, + .tune = dst_tune_frontend, + .set_frontend = dst_set_frontend, + .get_frontend = dst_get_frontend, + .get_frontend_algo = dst_get_tuning_algo, + .read_status = dst_read_status, + .read_signal_strength = dst_read_signal_strength, + .read_snr = dst_read_snr, +}; + +MODULE_DESCRIPTION("DST DVB-S/T/C/ATSC Combo Frontend driver"); +MODULE_AUTHOR("Jamie Honan, Manu Abraham"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c new file mode 100644 index 000000000000..ee3884fbc9ce --- /dev/null +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -0,0 +1,726 @@ +/* + CA-driver for TwinHan DST Frontend/Card + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "dvbdev.h" +#include "dvb_frontend.h" +#include "dst_ca.h" +#include "dst_common.h" + +#define DST_CA_ERROR 0 +#define DST_CA_NOTICE 1 +#define DST_CA_INFO 2 +#define DST_CA_DEBUG 3 + +#define dprintk(x, y, z, format, arg...) do { \ + if (z) { \ + if ((x > DST_CA_ERROR) && (x > y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((x > DST_CA_NOTICE) && (x > y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((x > DST_CA_INFO) && (x > y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((x > DST_CA_DEBUG) && (x > y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (x > y) \ + printk(format, ## arg); \ + } \ +} while(0) + + +static DEFINE_MUTEX(dst_ca_mutex); +static unsigned int verbose = 5; +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); + +/* Need some more work */ +static int ca_set_slot_descr(void) +{ + /* We could make this more graceful ? */ + return -EOPNOTSUPP; +} + +/* Need some more work */ +static int ca_set_pid(void) +{ + /* We could make this more graceful ? */ + return -EOPNOTSUPP; +} + +static void put_command_and_length(u8 *data, int command, int length) +{ + data[0] = (command >> 16) & 0xff; + data[1] = (command >> 8) & 0xff; + data[2] = command & 0xff; + data[3] = length; +} + +static void put_checksum(u8 *check_string, int length) +{ + dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum."); + dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length); + check_string[length] = dst_check_sum (check_string, length); + dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]); +} + +static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read) +{ + u8 reply; + + mutex_lock(&state->dst_mutex); + dst_comm_init(state); + msleep(65); + + if (write_dst(state, data, len)) { + dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover"); + dst_error_recovery(state); + goto error; + } + if ((dst_pio_disable(state)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed."); + goto error; + } + if (read_dst(state, &reply, GET_ACK) < 0) { + dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); + dst_error_recovery(state); + goto error; + } + if (read) { + if (! dst_wait_dst_ready(state, LONG_DELAY)) { + dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready"); + goto error; + } + if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */ + dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); + dst_error_recovery(state); + goto error; + } + } + mutex_unlock(&state->dst_mutex); + return 0; + +error: + mutex_unlock(&state->dst_mutex); + return -EIO; +} + + +static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read) +{ + u8 dst_ca_comm_err = 0; + + while (dst_ca_comm_err < RETRIES) { + dprintk(verbose, DST_CA_NOTICE, 1, " Put Command"); + if (dst_ci_command(state, data, ca_string, len, read)) { // If error + dst_error_recovery(state); + dst_ca_comm_err++; // work required here. + } else { + break; + } + } + + if(dst_ca_comm_err == RETRIES) + return -1; + + return 0; +} + + + +static int ca_get_app_info(struct dst_state *state) +{ + int length, str_length; + static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff}; + + put_checksum(&command[0], command[0]); + if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); + return -1; + } + dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); + dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================"); + dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]", + state->messages[7], (state->messages[8] << 8) | state->messages[9], + (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12])); + dprintk(verbose, DST_CA_INFO, 1, " =================================================================================================="); + + // Transform dst message to correct application_info message + length = state->messages[5]; + str_length = length - 6; + if (str_length < 0) { + str_length = 0; + dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering."); + } + + // First, the command and length fields + put_command_and_length(&state->messages[0], CA_APP_INFO, length); + + // Copy application_type, application_manufacturer and manufacturer_code + memcpy(&state->messages[4], &state->messages[7], 5); + + // Set string length and copy string + state->messages[9] = str_length; + memcpy(&state->messages[10], &state->messages[12], str_length); + + return 0; +} + +static int ca_get_ca_info(struct dst_state *state) +{ + int srcPtr, dstPtr, i, num_ids; + static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff}; + const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7; + + put_checksum(&slot_command[0], slot_command[0]); + if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); + return -1; + } + dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); + + // Print raw data + dprintk(verbose, DST_CA_INFO, 0, " DST data = ["); + for (i = 0; i < state->messages[0] + 1; i++) { + dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]); + } + dprintk(verbose, DST_CA_INFO, 0, "]\n"); + + // Set the command and length of the output + num_ids = state->messages[in_num_ids_pos]; + if (num_ids >= 100) { + num_ids = 100; + dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering."); + } + put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2); + + dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = ["); + srcPtr = in_system_id_pos; + dstPtr = out_system_id_pos; + for(i = 0; i < num_ids; i++) { + dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]); + // Append to output + state->messages[dstPtr + 0] = state->messages[srcPtr + 0]; + state->messages[dstPtr + 1] = state->messages[srcPtr + 1]; + srcPtr += 2; + dstPtr += 2; + } + dprintk(verbose, DST_CA_INFO, 0, "]\n"); + + return 0; +} + +static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg) +{ + int i; + u8 slot_cap[256]; + static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff}; + + put_checksum(&slot_command[0], slot_command[0]); + if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); + return -1; + } + dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !"); + + /* Will implement the rest soon */ + + dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]); + dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); + for (i = 0; i < slot_cap[0] + 1; i++) + dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]); + dprintk(verbose, DST_CA_INFO, 0, "\n"); + + p_ca_caps->slot_num = 1; + p_ca_caps->slot_type = 1; + p_ca_caps->descr_num = slot_cap[7]; + p_ca_caps->descr_type = 1; + + if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps))) + return -EFAULT; + + return 0; +} + +/* Need some more work */ +static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) +{ + return -EOPNOTSUPP; +} + + +static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg) +{ + int i; + static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; + + u8 *slot_info = state->messages; + + put_checksum(&slot_command[0], 7); + if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); + return -1; + } + dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); + + /* Will implement the rest soon */ + + dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]); + dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); + for (i = 0; i < 8; i++) + dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]); + dprintk(verbose, DST_CA_INFO, 0, "\n"); + + if (slot_info[4] & 0x80) { + p_ca_slot_info->flags = CA_CI_MODULE_PRESENT; + p_ca_slot_info->num = 1; + p_ca_slot_info->type = CA_CI; + } else if (slot_info[4] & 0x40) { + p_ca_slot_info->flags = CA_CI_MODULE_READY; + p_ca_slot_info->num = 1; + p_ca_slot_info->type = CA_CI; + } else + p_ca_slot_info->flags = 0; + + if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info))) + return -EFAULT; + + return 0; +} + + +static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) +{ + u8 i = 0; + u32 command = 0; + + if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) + return -EFAULT; + + if (p_ca_message->msg) { + dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]", + 3, p_ca_message->msg); + + for (i = 0; i < 3; i++) { + command = command | p_ca_message->msg[i]; + if (i < 2) + command = command << 8; + } + dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command); + + switch (command) { + case CA_APP_INFO: + memcpy(p_ca_message->msg, state->messages, 128); + if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) + return -EFAULT; + break; + case CA_INFO: + memcpy(p_ca_message->msg, state->messages, 128); + if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) + return -EFAULT; + break; + } + } + + return 0; +} + +static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length) +{ + if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) { + hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */ + hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */ + } else { + if (length > 247) { + dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !"); + return -1; + } + hw_buffer->msg[0] = (length & 0xff) + 7; + hw_buffer->msg[1] = 0x40; + hw_buffer->msg[2] = 0x03; + hw_buffer->msg[3] = 0x00; + hw_buffer->msg[4] = 0x03; + hw_buffer->msg[5] = length & 0xff; + hw_buffer->msg[6] = 0x00; + + /* + * Need to compute length for EN50221 section 8.3.2, for the time being + * assuming 8.3.2 is not applicable + */ + memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length); + } + + return 0; +} + +static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply) +{ + if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed."); + dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST."); + rdc_reset_state(state); + return -1; + } + dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success."); + + return 0; +} + +static u32 asn_1_decode(u8 *asn_1_array) +{ + u8 length_field = 0, word_count = 0, count = 0; + u32 length = 0; + + length_field = asn_1_array[0]; + dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field); + if (length_field < 0x80) { + length = length_field & 0x7f; + dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length); + } else { + word_count = length_field & 0x7f; + for (count = 0; count < word_count; count++) { + length = length << 8; + length += asn_1_array[count + 1]; + dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length); + } + } + return length; +} + +static int debug_string(u8 *msg, u32 length, u32 offset) +{ + u32 i; + + dprintk(verbose, DST_CA_DEBUG, 0, " String=[ "); + for (i = offset; i < length; i++) + dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]); + dprintk(verbose, DST_CA_DEBUG, 0, "]\n"); + + return 0; +} + + +static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query) +{ + u32 length = 0; + u8 tag_length = 8; + + length = asn_1_decode(&p_ca_message->msg[3]); + dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length); + debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */ + + memset(hw_buffer->msg, '\0', length); + handle_dst_tag(state, p_ca_message, hw_buffer, length); + put_checksum(hw_buffer->msg, hw_buffer->msg[0]); + + debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */ + write_to_8820(state, hw_buffer, (length + tag_length), reply); + + return 0; +} + + +/* Board supports CA PMT reply ? */ +static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer) +{ + int ca_pmt_reply_test = 0; + + /* Do test board */ + /* Not there yet but soon */ + + /* CA PMT Reply capable */ + if (ca_pmt_reply_test) { + if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); + return -1; + } + + /* Process CA PMT Reply */ + /* will implement soon */ + dprintk(verbose, DST_CA_ERROR, 1, " Not there yet"); + } + /* CA PMT Reply not capable */ + if (!ca_pmt_reply_test) { + if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); + return -1; + } + dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !"); + /* put a dummy message */ + + } + return 0; +} + +static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) +{ + int i = 0; + + u32 command = 0; + struct ca_msg *hw_buffer; + int result = 0; + + if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) { + dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); + return -ENOMEM; + } + dprintk(verbose, DST_CA_DEBUG, 1, " "); + + if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) { + result = -EFAULT; + goto free_mem_and_exit; + } + + + if (p_ca_message->msg) { + /* EN50221 tag */ + command = 0; + + for (i = 0; i < 3; i++) { + command = command | p_ca_message->msg[i]; + if (i < 2) + command = command << 8; + } + dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command); + + switch (command) { + case CA_PMT: + dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT"); + if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !"); + break; + case CA_PMT_REPLY: + dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY"); + /* Have to handle the 2 basic types of cards here */ + if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !"); + break; + case CA_APP_INFO_ENQUIRY: // only for debugging + dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information"); + + if ((ca_get_app_info(state)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !"); + break; + case CA_INFO_ENQUIRY: + dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information"); + + if ((ca_get_ca_info(state)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !"); + break; + } + } +free_mem_and_exit: + kfree (hw_buffer); + + return result; +} + +static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg) +{ + struct dvb_device *dvbdev; + struct dst_state *state; + struct ca_slot_info *p_ca_slot_info; + struct ca_caps *p_ca_caps; + struct ca_msg *p_ca_message; + void __user *arg = (void __user *)ioctl_arg; + int result = 0; + + mutex_lock(&dst_ca_mutex); + dvbdev = file->private_data; + state = (struct dst_state *)dvbdev->priv; + p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL); + p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL); + p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL); + if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) { + dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); + result = -ENOMEM; + goto free_mem_and_exit; + } + + /* We have now only the standard ioctl's, the driver is upposed to handle internals. */ + switch (cmd) { + case CA_SEND_MSG: + dprintk(verbose, DST_CA_INFO, 1, " Sending message"); + if ((ca_send_message(state, p_ca_message, arg)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !"); + result = -1; + goto free_mem_and_exit; + } + break; + case CA_GET_MSG: + dprintk(verbose, DST_CA_INFO, 1, " Getting message"); + if ((ca_get_message(state, p_ca_message, arg)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !"); + break; + case CA_RESET: + dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST"); + dst_error_bailout(state); + msleep(4000); + break; + case CA_GET_SLOT_INFO: + dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info"); + if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !"); + break; + case CA_GET_CAP: + dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities"); + if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !"); + break; + case CA_GET_DESCR_INFO: + dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description"); + if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !"); + break; + case CA_SET_DESCR: + dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler"); + if ((ca_set_slot_descr()) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !"); + break; + case CA_SET_PID: + dprintk(verbose, DST_CA_INFO, 1, " Setting PID"); + if ((ca_set_pid()) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !"); + default: + result = -EOPNOTSUPP; + }; + free_mem_and_exit: + kfree (p_ca_message); + kfree (p_ca_slot_info); + kfree (p_ca_caps); + + mutex_unlock(&dst_ca_mutex); + return result; +} + +static int dst_ca_open(struct inode *inode, struct file *file) +{ + dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file); + try_module_get(THIS_MODULE); + + return 0; +} + +static int dst_ca_release(struct inode *inode, struct file *file) +{ + dprintk(verbose, DST_CA_DEBUG, 1, " Device closed."); + module_put(THIS_MODULE); + + return 0; +} + +static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) +{ + ssize_t bytes_read = 0; + + dprintk(verbose, DST_CA_DEBUG, 1, " Device read."); + + return bytes_read; +} + +static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) +{ + dprintk(verbose, DST_CA_DEBUG, 1, " Device write."); + + return 0; +} + +static const struct file_operations dst_ca_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dst_ca_ioctl, + .open = dst_ca_open, + .release = dst_ca_release, + .read = dst_ca_read, + .write = dst_ca_write, + .llseek = noop_llseek, +}; + +static struct dvb_device dvbdev_ca = { + .priv = NULL, + .users = 1, + .readers = 1, + .writers = 1, + .fops = &dst_ca_fops +}; + +struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter) +{ + struct dvb_device *dvbdev; + + dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); + if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { + dst->dst_ca = dvbdev; + return dst->dst_ca; + } + + return NULL; +} + +EXPORT_SYMBOL(dst_ca_attach); + +MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/bt8xx/dst_ca.h b/drivers/media/pci/bt8xx/dst_ca.h new file mode 100644 index 000000000000..59cd0ddd6d8e --- /dev/null +++ b/drivers/media/pci/bt8xx/dst_ca.h @@ -0,0 +1,58 @@ +/* + CA-driver for TwinHan DST Frontend/Card + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _DST_CA_H_ +#define _DST_CA_H_ + +#define RETRIES 5 + + +#define CA_APP_INFO_ENQUIRY 0x9f8020 +#define CA_APP_INFO 0x9f8021 +#define CA_ENTER_MENU 0x9f8022 +#define CA_INFO_ENQUIRY 0x9f8030 +#define CA_INFO 0x9f8031 +#define CA_PMT 0x9f8032 +#define CA_PMT_REPLY 0x9f8033 + +#define CA_CLOSE_MMI 0x9f8800 +#define CA_DISPLAY_CONTROL 0x9f8801 +#define CA_DISPLAY_REPLY 0x9f8802 +#define CA_TEXT_LAST 0x9f8803 +#define CA_TEXT_MORE 0x9f8804 +#define CA_KEYPAD_CONTROL 0x9f8805 +#define CA_KEYPRESS 0x9f8806 + +#define CA_ENQUIRY 0x9f8807 +#define CA_ANSWER 0x9f8808 +#define CA_MENU_LAST 0x9f8809 +#define CA_MENU_MORE 0x9f880a +#define CA_MENU_ANSWER 0x9f880b +#define CA_LIST_LAST 0x9f880c +#define CA_LIST_MORE 0x9f880d + + +struct dst_ca_private { + struct dst_state *dst; + struct dvb_device *dvbdev; +}; + + +#endif diff --git a/drivers/media/pci/bt8xx/dst_common.h b/drivers/media/pci/bt8xx/dst_common.h new file mode 100644 index 000000000000..d70d98f1a571 --- /dev/null +++ b/drivers/media/pci/bt8xx/dst_common.h @@ -0,0 +1,182 @@ +/* + Frontend-driver for TwinHan DST Frontend + + Copyright (C) 2003 Jamie Honan + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef DST_COMMON_H +#define DST_COMMON_H + +#include +#include +#include +#include "bt878.h" + +#include "dst_ca.h" + + +#define NO_DELAY 0 +#define LONG_DELAY 1 +#define DEVICE_INIT 2 + +#define DELAY 1 + +#define DST_TYPE_IS_SAT 0 +#define DST_TYPE_IS_TERR 1 +#define DST_TYPE_IS_CABLE 2 +#define DST_TYPE_IS_ATSC 3 + +#define DST_TYPE_HAS_TS188 1 +#define DST_TYPE_HAS_TS204 2 +#define DST_TYPE_HAS_SYMDIV 4 +#define DST_TYPE_HAS_FW_1 8 +#define DST_TYPE_HAS_FW_2 16 +#define DST_TYPE_HAS_FW_3 32 +#define DST_TYPE_HAS_FW_BUILD 64 +#define DST_TYPE_HAS_OBS_REGS 128 +#define DST_TYPE_HAS_INC_COUNT 256 +#define DST_TYPE_HAS_MULTI_FE 512 +#define DST_TYPE_HAS_NEWTUNE_2 1024 +#define DST_TYPE_HAS_DBOARD 2048 +#define DST_TYPE_HAS_VLF 4096 + +/* Card capability list */ + +#define DST_TYPE_HAS_MAC 1 +#define DST_TYPE_HAS_DISEQC3 2 +#define DST_TYPE_HAS_DISEQC4 4 +#define DST_TYPE_HAS_DISEQC5 8 +#define DST_TYPE_HAS_MOTO 16 +#define DST_TYPE_HAS_CA 32 +#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */ +#define DST_TYPE_HAS_SESSION 128 + +#define TUNER_TYPE_MULTI 1 +#define TUNER_TYPE_UNKNOWN 2 +/* DVB-S */ +#define TUNER_TYPE_L64724 4 +#define TUNER_TYPE_STV0299 8 +#define TUNER_TYPE_MB86A15 16 + +/* DVB-T */ +#define TUNER_TYPE_TDA10046 32 + +/* ATSC */ +#define TUNER_TYPE_NXT200x 64 + + +#define RDC_8820_PIO_0_DISABLE 0 +#define RDC_8820_PIO_0_ENABLE 1 +#define RDC_8820_INT 2 +#define RDC_8820_RESET 4 + +/* DST Communication */ +#define GET_REPLY 1 +#define NO_REPLY 0 + +#define GET_ACK 1 +#define FIXED_COMM 8 + +#define ACK 0xff + +struct dst_state { + + struct i2c_adapter* i2c; + + struct bt878* bt; + + /* configuration settings */ + const struct dst_config* config; + + struct dvb_frontend frontend; + + /* private ASIC data */ + u8 tx_tuna[10]; + u8 rx_tuna[10]; + u8 rxbuffer[10]; + u8 diseq_flags; + u8 dst_type; + u32 type_flags; + u32 frequency; /* intermediate frequency in kHz for QPSK */ + fe_spectral_inversion_t inversion; + u32 symbol_rate; /* symbol rate in Symbols per second */ + fe_code_rate_t fec; + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t tone; + u32 decode_freq; + u8 decode_lock; + u16 decode_strength; + u16 decode_snr; + unsigned long cur_jiff; + u8 k22; + u32 bandwidth; + u32 dst_hw_cap; + u8 dst_fw_version; + fe_sec_mini_cmd_t minicmd; + fe_modulation_t modulation; + u8 messages[256]; + u8 mac_address[8]; + u8 fw_version[8]; + u8 card_info[8]; + u8 vendor[8]; + u8 board_info[8]; + u32 tuner_type; + char *tuner_name; + struct mutex dst_mutex; + u8 fw_name[8]; + struct dvb_device *dst_ca; +}; + +struct tuner_types { + u32 tuner_type; + char *tuner_name; + char *board_name; + char *fw_name; +}; + +struct dst_types { + char *device_id; + int offset; + u8 dst_type; + u32 type_flags; + u32 dst_feature; + u32 tuner_type; +}; + +struct dst_config +{ + /* the ASIC i2c address */ + u8 demod_address; +}; + +int rdc_reset_state(struct dst_state *state); + +int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode); +int dst_pio_disable(struct dst_state *state); +int dst_error_recovery(struct dst_state* state); +int dst_error_bailout(struct dst_state *state); +int dst_comm_init(struct dst_state* state); + +int write_dst(struct dst_state *state, u8 * data, u8 len); +int read_dst(struct dst_state *state, u8 * ret, u8 len); +u8 dst_check_sum(u8 * buf, u32 len); +struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter); +struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter); + + +#endif // DST_COMMON_H diff --git a/drivers/media/pci/bt8xx/dst_priv.h b/drivers/media/pci/bt8xx/dst_priv.h new file mode 100644 index 000000000000..3974a4c6ebe7 --- /dev/null +++ b/drivers/media/pci/bt8xx/dst_priv.h @@ -0,0 +1,35 @@ +/* + * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend + * + * Copyright (C) 2003 Jamie Honan + */ + +struct dst_gpio_enable { + u32 mask; + u32 enable; +}; + +struct dst_gpio_output { + u32 mask; + u32 highvals; +}; + +struct dst_gpio_read { + unsigned long value; +}; + +union dst_gpio_packet { + struct dst_gpio_enable enb; + struct dst_gpio_output outp; + struct dst_gpio_read rd; + int psize; +}; + +#define DST_IG_ENABLE 0 +#define DST_IG_WRITE 1 +#define DST_IG_READ 2 +#define DST_IG_TS 3 + +struct bt878; + +int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp); diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c new file mode 100644 index 000000000000..81fab9adc1ca --- /dev/null +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -0,0 +1,975 @@ +/* + * Bt8xx based DVB adapter driver + * + * Copyright (C) 2002,2003 Florian Schirmer + * + * This program is free software; 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) "dvb_bt8xx: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb-bt8xx.h" +#include "bt878.h" + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk( args... ) \ + do { \ + if (debug) printk(KERN_DEBUG args); \ + } while (0) + +#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ + +static void dvb_bt8xx_task(unsigned long data) +{ + struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data; + + //printk("%d ", card->bt->finished_block); + + while (card->bt->last_block != card->bt->finished_block) { + (card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) + (&card->demux, + &card->bt->buf_cpu[card->bt->last_block * + card->bt->block_bytes], + card->bt->block_bytes); + card->bt->last_block = (card->bt->last_block + 1) % + card->bt->block_count; + } +} + +static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux*dvbdmx = dvbdmxfeed->demux; + struct dvb_bt8xx_card *card = dvbdmx->priv; + int rc; + + dprintk("dvb_bt8xx: start_feed\n"); + + if (!dvbdmx->dmx.frontend) + return -EINVAL; + + mutex_lock(&card->lock); + card->nfeeds++; + rc = card->nfeeds; + if (card->nfeeds == 1) + bt878_start(card->bt, card->gpio_mode, + card->op_sync_orin, card->irq_err_ignore); + mutex_unlock(&card->lock); + return rc; +} + +static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct dvb_bt8xx_card *card = dvbdmx->priv; + + dprintk("dvb_bt8xx: stop_feed\n"); + + if (!dvbdmx->dmx.frontend) + return -EINVAL; + + mutex_lock(&card->lock); + card->nfeeds--; + if (card->nfeeds == 0) + bt878_stop(card->bt); + mutex_unlock(&card->lock); + + return 0; +} + +static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev) +{ + if ((adev->subsystem_vendor == bdev->subsystem_vendor) && + (adev->subsystem_device == bdev->subsystem_device) && + (adev->bus->number == bdev->bus->number) && + (PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn))) + return 1; + return 0; +} + +static struct bt878 __devinit *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev) +{ + unsigned int card_nr; + + /* Hmm, n squared. Hope n is small */ + for (card_nr = 0; card_nr < bt878_num; card_nr++) + if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev)) + return &bt878[card_nr]; + return NULL; +} + +static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) +{ + static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 }; + static u8 mt352_reset [] = { 0x50, 0x80 }; + static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 }; + static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 }; + static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); + + return 0; +} + +static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend *fe, u8* pllbuf, int buf_len) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 div; + unsigned char bs = 0; + unsigned char cp = 0; + + if (buf_len < 5) + return -EINVAL; + + div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; + + if (c->frequency < 542000000) + cp = 0xb4; + else if (c->frequency < 771000000) + cp = 0xbc; + else + cp = 0xf4; + + if (c->frequency == 0) + bs = 0x03; + else if (c->frequency < 443250000) + bs = 0x02; + else + bs = 0x08; + + pllbuf[0] = 0x60; + pllbuf[1] = div >> 8; + pllbuf[2] = div & 0xff; + pllbuf[3] = cp; + pllbuf[4] = bs; + + return 5; +} + +static struct mt352_config thomson_dtt7579_config = { + .demod_address = 0x0f, + .demod_init = thomson_dtt7579_demod_init, +}; + +static struct zl10353_config thomson_dtt7579_zl10353_config = { + .demod_address = 0x0f, +}; + +static int cx24108_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 freq = c->frequency; + int i, a, n, pump; + u32 band, pll; + u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000, + 1576000,1718000,1856000,2036000,2150000}; + u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000, + 0x00102000,0x00104000,0x00108000,0x00110000, + 0x00120000,0x00140000}; + + #define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */ + dprintk("cx24108 debug: entering SetTunerFreq, freq=%d\n", freq); + + /* This is really the bit driving the tuner chip cx24108 */ + + if (freq<950000) + freq = 950000; /* kHz */ + else if (freq>2150000) + freq = 2150000; /* satellite IF is 950..2150MHz */ + + /* decide which VCO to use for the input frequency */ + for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++); + dprintk("cx24108 debug: select vco #%d (f=%d)\n", i, freq); + band=bandsel[i]; + /* the gain values must be set by SetSymbolrate */ + /* compute the pll divider needed, from Conexant data sheet, + resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4, + depending on the divider bit. It is set to /4 on the 2 lowest + bands */ + n=((i<=2?2:1)*freq*10L)/(XTAL/100); + a=n%32; n/=32; if(a==0) n--; + pump=(freq<(osci[i-1]+osci[i])/2); + pll=0xf8000000| + ((pump?1:2)<<(14+11))| + ((n&0x1ff)<<(5+11))| + ((a&0x1f)<<11); + /* everything is shifted left 11 bits to left-align the bits in the + 32bit word. Output to the tuner goes MSB-aligned, after all */ + dprintk("cx24108 debug: pump=%d, n=%d, a=%d\n", pump, n, a); + cx24110_pll_write(fe,band); + /* set vga and vca to their widest-band settings, as a precaution. + SetSymbolrate might not be called to set this up */ + cx24110_pll_write(fe,0x500c0000); + cx24110_pll_write(fe,0x83f1f800); + cx24110_pll_write(fe,pll); + //writereg(client,0x56,0x7f); + + return 0; +} + +static int pinnsat_tuner_init(struct dvb_frontend* fe) +{ + struct dvb_bt8xx_card *card = fe->dvb->priv; + + bttv_gpio_enable(card->bttv_nr, 1, 1); /* output */ + bttv_write_gpio(card->bttv_nr, 1, 1); /* relay on */ + + return 0; +} + +static int pinnsat_tuner_sleep(struct dvb_frontend* fe) +{ + struct dvb_bt8xx_card *card = fe->dvb->priv; + + bttv_write_gpio(card->bttv_nr, 1, 0); /* relay off */ + + return 0; +} + +static struct cx24110_config pctvsat_config = { + .demod_address = 0x55, +}; + +static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; + u8 cfg, cpump, band_select; + u8 data[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (36000000 + c->frequency + 83333) / 166666; + cfg = 0x88; + + if (c->frequency < 175000000) + cpump = 2; + else if (c->frequency < 390000000) + cpump = 1; + else if (c->frequency < 470000000) + cpump = 2; + else if (c->frequency < 750000000) + cpump = 2; + else + cpump = 3; + + if (c->frequency < 175000000) + band_select = 0x0e; + else if (c->frequency < 470000000) + band_select = 0x05; + else + band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = (cpump << 6) | band_select; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(card->i2c_adapter, &msg, 1); + return (div * 166666 - 36000000); +} + +static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ + struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; + + return request_firmware(fw, name, &bt->bt->dev->dev); +} + +static struct sp887x_config microtune_mt7202dtf_config = { + .demod_address = 0x70, + .request_firmware = microtune_mt7202dtf_request_firmware, +}; + +static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) +{ + static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; + static u8 mt352_reset [] = { 0x50, 0x80 }; + static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF, + 0x00, 0xFF, 0x00, 0x40, 0x40 }; + static u8 mt352_av771_extra[] = { 0xB5, 0x7A }; + static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + + mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg)); + udelay(2000); + mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); + + return 0; +} + +static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 div; + unsigned char bs = 0; + unsigned char cp = 0; + + if (buf_len < 5) return -EINVAL; + + div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; + + if (c->frequency < 150000000) + cp = 0xB4; + else if (c->frequency < 173000000) + cp = 0xBC; + else if (c->frequency < 250000000) + cp = 0xB4; + else if (c->frequency < 400000000) + cp = 0xBC; + else if (c->frequency < 420000000) + cp = 0xF4; + else if (c->frequency < 470000000) + cp = 0xFC; + else if (c->frequency < 600000000) + cp = 0xBC; + else if (c->frequency < 730000000) + cp = 0xF4; + else + cp = 0xFC; + + if (c->frequency < 150000000) + bs = 0x01; + else if (c->frequency < 173000000) + bs = 0x01; + else if (c->frequency < 250000000) + bs = 0x02; + else if (c->frequency < 400000000) + bs = 0x02; + else if (c->frequency < 420000000) + bs = 0x02; + else if (c->frequency < 470000000) + bs = 0x02; + else if (c->frequency < 600000000) + bs = 0x08; + else if (c->frequency < 730000000) + bs = 0x08; + else + bs = 0x08; + + pllbuf[0] = 0x61; + pllbuf[1] = div >> 8; + pllbuf[2] = div & 0xff; + pllbuf[3] = cp; + pllbuf[4] = bs; + + return 5; +} + +static struct mt352_config advbt771_samsung_tdtc9251dh0_config = { + .demod_address = 0x0f, + .demod_init = advbt771_samsung_tdtc9251dh0_demod_init, +}; + +static struct dst_config dst_config = { + .demod_address = 0x55, +}; + +static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ + struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; + + return request_firmware(fw, name, &bt->bt->dev->dev); +} + +static void or51211_setmode(struct dvb_frontend * fe, int mode) +{ + struct dvb_bt8xx_card *bt = fe->dvb->priv; + bttv_write_gpio(bt->bttv_nr, 0x0002, mode); /* Reset */ + msleep(20); +} + +static void or51211_reset(struct dvb_frontend * fe) +{ + struct dvb_bt8xx_card *bt = fe->dvb->priv; + + /* RESET DEVICE + * reset is controlled by GPIO-0 + * when set to 0 causes reset and when to 1 for normal op + * must remain reset for 128 clock cycles on a 50Mhz clock + * also PRM1 PRM2 & PRM4 are controlled by GPIO-1,GPIO-2 & GPIO-4 + * We assume that the reset has be held low long enough or we + * have been reset by a power on. When the driver is unloaded + * reset set to 0 so if reloaded we have been reset. + */ + /* reset & PRM1,2&4 are outputs */ + int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F); + if (ret != 0) + printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR (%i)\n", ret); + bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000); /* Reset */ + msleep(20); + /* Now set for normal operation */ + bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001); + /* wait for operation to begin */ + msleep(500); +} + +static void or51211_sleep(struct dvb_frontend * fe) +{ + struct dvb_bt8xx_card *bt = fe->dvb->priv; + bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000); +} + +static struct or51211_config or51211_config = { + .demod_address = 0x15, + .request_firmware = or51211_request_firmware, + .setmode = or51211_setmode, + .reset = or51211_reset, + .sleep = or51211_sleep, +}; + +static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) }; + + div = (c->frequency + 36166667) / 166667; + + buf[0] = (div >> 8) & 0x7F; + buf[1] = div & 0xFF; + buf[2] = 0x85; + if ((c->frequency >= 47000000) && (c->frequency < 153000000)) + buf[3] = 0x01; + else if ((c->frequency >= 153000000) && (c->frequency < 430000000)) + buf[3] = 0x02; + else if ((c->frequency >= 430000000) && (c->frequency < 824000000)) + buf[3] = 0x0C; + else if ((c->frequency >= 824000000) && (c->frequency < 863000000)) + buf[3] = 0x8C; + else + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(card->i2c_adapter, &msg, 1); + return 0; +} + +static struct nxt6000_config vp3021_alps_tded4_config = { + .demod_address = 0x0a, + .clock_inversion = 1, +}; + +static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) +{ + static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; + static u8 mt352_reset [] = { 0x50, 0x80 }; + static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg [] = { 0x67, 0x20, 0xa0 }; + static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); + + return 0; +} + +static int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) +{ + u32 div; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (buf_len < 5) + return -EINVAL; + + div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; + + pllbuf[0] = 0x61; + pllbuf[1] = (div >> 8) & 0x7F; + pllbuf[2] = div & 0xFF; + pllbuf[3] = 0x85; + + dprintk("frequency %u, div %u\n", c->frequency, div); + + if (c->frequency < 470000000) + pllbuf[4] = 0x02; + else if (c->frequency > 823000000) + pllbuf[4] = 0x88; + else + pllbuf[4] = 0x08; + + if (c->bandwidth_hz == 8000000) + pllbuf[4] |= 0x04; + + return 5; +} + +static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) +{ + /* + * Reset the frontend, must be called before trying + * to initialise the MT352 or mt352_attach + * will fail. Same goes for the nxt6000 frontend. + * + */ + + int ret = bttv_gpio_enable(bt->bttv_nr, 0x08, 0x08); + if (ret != 0) + printk(KERN_WARNING "digitv_alps_tded4: Init Error - Can't Reset DVR (%i)\n", ret); + + /* Pulse the reset line */ + bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ + bttv_write_gpio(bt->bttv_nr, 0x08, 0x00); /* Low */ + msleep(100); + + bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ +} + +static struct mt352_config digitv_alps_tded4_config = { + .demod_address = 0x0a, + .demod_init = digitv_alps_tded4_demod_init, +}; + +static struct lgdt330x_config tdvs_tua6034_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ +}; + +static void lgdt330x_reset(struct dvb_bt8xx_card *bt) +{ + /* Set pin 27 of the lgdt3303 chip high to reset the frontend */ + + /* Pulse the reset line */ + bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ + bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000000); /* Low */ + msleep(100); + + bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ + msleep(100); +} + +static void frontend_init(struct dvb_bt8xx_card *card, u32 type) +{ + struct dst_state* state = NULL; + + switch(type) { + case BTTV_BOARD_DVICO_DVBT_LITE: + card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter); + + if (card->fe == NULL) + card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config, + card->i2c_adapter); + + if (card->fe != NULL) { + card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; + card->fe->ops.info.frequency_min = 174000000; + card->fe->ops.info.frequency_max = 862000000; + } + break; + + case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: + lgdt330x_reset(card); + card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); + if (card->fe != NULL) { + dvb_attach(simple_tuner_attach, card->fe, + card->i2c_adapter, 0x61, + TUNER_LG_TDVS_H06XF); + dprintk ("dvb_bt8xx: lgdt330x detected\n"); + } + break; + + case BTTV_BOARD_NEBULA_DIGITV: + /* + * It is possible to determine the correct frontend using the I2C bus (see the Nebula SDK); + * this would be a cleaner solution than trying each frontend in turn. + */ + + /* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */ + digitv_alps_tded4_reset(card); + card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter); + if (card->fe != NULL) { + card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params; + dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n"); + break; + } + + /* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */ + digitv_alps_tded4_reset(card); + card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter); + + if (card->fe != NULL) { + card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs; + dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n"); + } + break; + + case BTTV_BOARD_AVDVBT_761: + card->fe = dvb_attach(sp887x_attach, µtune_mt7202dtf_config, card->i2c_adapter); + if (card->fe) { + card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params; + } + break; + + case BTTV_BOARD_AVDVBT_771: + card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); + if (card->fe != NULL) { + card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; + card->fe->ops.info.frequency_min = 174000000; + card->fe->ops.info.frequency_max = 862000000; + } + break; + + case BTTV_BOARD_TWINHAN_DST: + /* DST is not a frontend driver !!! */ + state = kmalloc(sizeof (struct dst_state), GFP_KERNEL); + if (!state) { + pr_err("No memory\n"); + break; + } + /* Setup the Card */ + state->config = &dst_config; + state->i2c = card->i2c_adapter; + state->bt = card->bt; + state->dst_ca = NULL; + /* DST is not a frontend, attaching the ASIC */ + if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) { + pr_err("%s: Could not find a Twinhan DST\n", __func__); + break; + } + /* Attach other DST peripherals if any */ + /* Conditional Access device */ + card->fe = &state->frontend; + if (state->dst_hw_cap & DST_TYPE_HAS_CA) + dvb_attach(dst_ca_attach, state, &card->dvb_adapter); + break; + + case BTTV_BOARD_PINNACLESAT: + card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter); + if (card->fe) { + card->fe->ops.tuner_ops.init = pinnsat_tuner_init; + card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep; + card->fe->ops.tuner_ops.set_params = cx24108_tuner_set_params; + } + break; + + case BTTV_BOARD_PC_HDTV: + card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter); + if (card->fe != NULL) + dvb_attach(simple_tuner_attach, card->fe, + card->i2c_adapter, 0x61, + TUNER_PHILIPS_FCV1236D); + break; + } + + if (card->fe == NULL) + pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + card->bt->dev->vendor, + card->bt->dev->device, + card->bt->dev->subsystem_vendor, + card->bt->dev->subsystem_device); + else + if (dvb_register_frontend(&card->dvb_adapter, card->fe)) { + pr_err("Frontend registration failed!\n"); + dvb_frontend_detach(card->fe); + card->fe = NULL; + } +} + +static int __devinit dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) +{ + int result; + + result = dvb_register_adapter(&card->dvb_adapter, card->card_name, + THIS_MODULE, &card->bt->dev->dev, + adapter_nr); + if (result < 0) { + pr_err("dvb_register_adapter failed (errno = %d)\n", result); + return result; + } + card->dvb_adapter.priv = card; + + card->bt->adapter = card->i2c_adapter; + + memset(&card->demux, 0, sizeof(struct dvb_demux)); + + card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING; + + card->demux.priv = card; + card->demux.filternum = 256; + card->demux.feednum = 256; + card->demux.start_feed = dvb_bt8xx_start_feed; + card->demux.stop_feed = dvb_bt8xx_stop_feed; + card->demux.write_to_decoder = NULL; + + result = dvb_dmx_init(&card->demux); + if (result < 0) { + pr_err("dvb_dmx_init failed (errno = %d)\n", result); + goto err_unregister_adaptor; + } + + card->dmxdev.filternum = 256; + card->dmxdev.demux = &card->demux.dmx; + card->dmxdev.capabilities = 0; + + result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter); + if (result < 0) { + pr_err("dvb_dmxdev_init failed (errno = %d)\n", result); + goto err_dmx_release; + } + + card->fe_hw.source = DMX_FRONTEND_0; + + result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw); + if (result < 0) { + pr_err("dvb_dmx_init failed (errno = %d)\n", result); + goto err_dmxdev_release; + } + + card->fe_mem.source = DMX_MEMORY_FE; + + result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem); + if (result < 0) { + pr_err("dvb_dmx_init failed (errno = %d)\n", result); + goto err_remove_hw_frontend; + } + + result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw); + if (result < 0) { + pr_err("dvb_dmx_init failed (errno = %d)\n", result); + goto err_remove_mem_frontend; + } + + result = dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx); + if (result < 0) { + pr_err("dvb_net_init failed (errno = %d)\n", result); + goto err_disconnect_frontend; + } + + tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card); + + frontend_init(card, type); + + return 0; + +err_disconnect_frontend: + card->demux.dmx.disconnect_frontend(&card->demux.dmx); +err_remove_mem_frontend: + card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); +err_remove_hw_frontend: + card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); +err_dmxdev_release: + dvb_dmxdev_release(&card->dmxdev); +err_dmx_release: + dvb_dmx_release(&card->demux); +err_unregister_adaptor: + dvb_unregister_adapter(&card->dvb_adapter); + return result; +} + +static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub) +{ + struct dvb_bt8xx_card *card; + struct pci_dev* bttv_pci_dev; + int ret; + + if (!(card = kzalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL))) + return -ENOMEM; + + mutex_init(&card->lock); + card->bttv_nr = sub->core->nr; + strlcpy(card->card_name, sub->core->v4l2_dev.name, sizeof(card->card_name)); + card->i2c_adapter = &sub->core->i2c_adap; + + switch(sub->core->type) { + case BTTV_BOARD_PINNACLESAT: + card->gpio_mode = 0x0400c060; + /* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR, + BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */ + card->op_sync_orin = BT878_RISC_SYNC_MASK; + card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; + break; + + case BTTV_BOARD_DVICO_DVBT_LITE: + card->gpio_mode = 0x0400C060; + card->op_sync_orin = BT878_RISC_SYNC_MASK; + card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; + /* 26, 15, 14, 6, 5 + * A_PWRDN DA_DPM DA_SBR DA_IOM_DA + * DA_APP(parallel) */ + break; + + case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: + card->gpio_mode = 0x0400c060; + card->op_sync_orin = BT878_RISC_SYNC_MASK; + card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; + break; + + case BTTV_BOARD_NEBULA_DIGITV: + case BTTV_BOARD_AVDVBT_761: + card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5); + card->op_sync_orin = BT878_RISC_SYNC_MASK; + card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; + /* A_PWRDN DA_SBR DA_APP (high speed serial) */ + break; + + case BTTV_BOARD_AVDVBT_771: //case 0x07711461: + card->gpio_mode = 0x0400402B; + card->op_sync_orin = BT878_RISC_SYNC_MASK; + card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; + /* A_PWRDN DA_SBR DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/ + break; + + case BTTV_BOARD_TWINHAN_DST: + card->gpio_mode = 0x2204f2c; + card->op_sync_orin = BT878_RISC_SYNC_MASK; + card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR | + BT878_APPERR | BT878_AFBUS; + /* 25,21,14,11,10,9,8,3,2 then + * 0x33 = 5,4,1,0 + * A_SEL=SML, DA_MLB, DA_SBR, + * DA_SDR=f, fifo trigger = 32 DWORDS + * IOM = 0 == audio A/D + * DPM = 0 == digital audio mode + * == async data parallel port + * then 0x33 (13 is set by start_capture) + * DA_APP = async data parallel port, + * ACAP_EN = 1, + * RISC+FIFO ENABLE */ + break; + + case BTTV_BOARD_PC_HDTV: + card->gpio_mode = 0x0100EC7B; + card->op_sync_orin = BT878_RISC_SYNC_MASK; + card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; + break; + + default: + pr_err("Unknown bttv card type: %d\n", sub->core->type); + kfree(card); + return -ENODEV; + } + + dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name); + + if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) { + pr_err("no pci device for card %d\n", card->bttv_nr); + kfree(card); + return -ENODEV; + } + + if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) { + pr_err("unable to determine DMA core of card %d,\n", card->bttv_nr); + pr_err("if you have the ALSA bt87x audio driver installed, try removing it.\n"); + + kfree(card); + return -ENODEV; + } + + mutex_init(&card->bt->gpio_lock); + card->bt->bttv_nr = sub->core->nr; + + if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) { + kfree(card); + return ret; + } + + dev_set_drvdata(&sub->dev, card); + return 0; +} + +static void dvb_bt8xx_remove(struct bttv_sub_device *sub) +{ + struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev); + + dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr); + + bt878_stop(card->bt); + tasklet_kill(&card->bt->tasklet); + dvb_net_release(&card->dvbnet); + card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); + card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); + dvb_dmxdev_release(&card->dmxdev); + dvb_dmx_release(&card->demux); + if (card->fe) { + dvb_unregister_frontend(card->fe); + dvb_frontend_detach(card->fe); + } + dvb_unregister_adapter(&card->dvb_adapter); + + kfree(card); +} + +static struct bttv_sub_driver driver = { + .drv = { + .name = "dvb-bt8xx", + }, + .probe = dvb_bt8xx_probe, + .remove = dvb_bt8xx_remove, + /* FIXME: + * .shutdown = dvb_bt8xx_shutdown, + * .suspend = dvb_bt8xx_suspend, + * .resume = dvb_bt8xx_resume, + */ +}; + +static int __init dvb_bt8xx_init(void) +{ + return bttv_sub_register(&driver, "dvb"); +} + +static void __exit dvb_bt8xx_exit(void) +{ + bttv_sub_unregister(&driver); +} + +module_init(dvb_bt8xx_init); +module_exit(dvb_bt8xx_exit); + +MODULE_DESCRIPTION("Bt8xx based DVB adapter driver"); +MODULE_AUTHOR("Florian Schirmer "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.h b/drivers/media/pci/bt8xx/dvb-bt8xx.h new file mode 100644 index 000000000000..4499ed2ac0ed --- /dev/null +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.h @@ -0,0 +1,63 @@ +/* + * Bt8xx based DVB adapter driver + * + * Copyright (C) 2002,2003 Florian Schirmer + * Copyright (C) 2002 Peter Hettkamp + * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH + * Copyright (C) 1998,1999 Christian Theiss + * + * This program is free software; 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_BT8XX_H +#define DVB_BT8XX_H + +#include +#include +#include "dvbdev.h" +#include "dvb_net.h" +#include "bttv.h" +#include "mt352.h" +#include "sp887x.h" +#include "dst_common.h" +#include "nxt6000.h" +#include "cx24110.h" +#include "or51211.h" +#include "lgdt330x.h" +#include "zl10353.h" +#include "tuner-simple.h" + +struct dvb_bt8xx_card { + struct mutex lock; + int nfeeds; + char card_name[32]; + struct dvb_adapter dvb_adapter; + struct bt878 *bt; + unsigned int bttv_nr; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + u32 gpio_mode; + u32 op_sync_orin; + u32 irq_err_ignore; + struct i2c_adapter *i2c_adapter; + struct dvb_net dvbnet; + + struct dvb_frontend* fe; +}; + +#endif /* DVB_BT8XX_H */ diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig new file mode 100644 index 000000000000..d099e1a12c85 --- /dev/null +++ b/drivers/media/pci/ddbridge/Kconfig @@ -0,0 +1,18 @@ +config DVB_DDBRIDGE + tristate "Digital Devices bridge support" + depends on DVB_CORE && PCI && I2C + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_STV6110x if !DVB_FE_CUSTOMISE + select DVB_STV090x if !DVB_FE_CUSTOMISE + select DVB_DRXK if !DVB_FE_CUSTOMISE + select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE + ---help--- + Support for cards with the Digital Devices PCI express bridge: + - Octopus PCIe Bridge + - Octopus mini PCIe Bridge + - Octopus LE + - DuoFlex S2 Octopus + - DuoFlex CT Octopus + - cineS2(v6) + + Say Y if you own such a card and want to use it. diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile new file mode 100644 index 000000000000..9d083c98ce58 --- /dev/null +++ b/drivers/media/pci/ddbridge/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the ddbridge device driver +# + +ddbridge-objs := ddbridge-core.o + +obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o + +ccflags-y += -Idrivers/media/dvb-core/ +ccflags-y += -Idrivers/media/dvb-frontends/ +ccflags-y += -Idrivers/media/common/tuners/ + +# For the staging CI driver cxd2099 +ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c new file mode 100644 index 000000000000..ebf3f05839d2 --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -0,0 +1,1723 @@ +/* + * ddbridge.c: Digital Devices PCIe bridge driver + * + * Copyright (C) 2010-2011 Digital Devices GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ddbridge.h" + +#include "ddbridge-regs.h" + +#include "tda18271c2dd.h" +#include "stv6110x.h" +#include "stv090x.h" +#include "lnbh24.h" +#include "drxk.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* MSI had problems with lost interrupts, fixed but needs testing */ +#undef CONFIG_PCI_MSI + +/******************************************************************************/ + +static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) +{ + struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1 } }; + return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; +} + +static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1 } }; + return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +} + +static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, + u16 reg, u8 *val) +{ + u8 msg[2] = {reg>>8, reg&0xff}; + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = msg, .len = 2}, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1} }; + return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +} + +static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) +{ + struct ddb *dev = i2c->dev; + int stat; + u32 val; + + i2c->done = 0; + ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND); + stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ); + if (stat <= 0) { + printk(KERN_ERR "I2C timeout\n"); + { /* MSI debugging*/ + u32 istat = ddbreadl(INTERRUPT_STATUS); + printk(KERN_ERR "IRS %08x\n", istat); + ddbwritel(istat, INTERRUPT_ACK); + } + return -EIO; + } + val = ddbreadl(i2c->regs+I2C_COMMAND); + if (val & 0x70000) + return -EIO; + return 0; +} + +static int ddb_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msg[], int num) +{ + struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter); + struct ddb *dev = i2c->dev; + u8 addr = 0; + + if (num) + addr = msg[0].addr; + + if (num == 2 && msg[1].flags & I2C_M_RD && + !(msg[0].flags & I2C_M_RD)) { + memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf, + msg[0].buf, msg[0].len); + ddbwritel(msg[0].len|(msg[1].len << 16), + i2c->regs+I2C_TASKLENGTH); + if (!ddb_i2c_cmd(i2c, addr, 1)) { + memcpy_fromio(msg[1].buf, + dev->regs + I2C_TASKMEM_BASE + i2c->rbuf, + msg[1].len); + return num; + } + } + + if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len); + ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH); + if (!ddb_i2c_cmd(i2c, addr, 2)) + return num; + } + if (num == 1 && (msg[0].flags & I2C_M_RD)) { + ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH); + if (!ddb_i2c_cmd(i2c, addr, 3)) { + ddbcpyfrom(msg[0].buf, + I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len); + return num; + } + } + return -EIO; +} + + +static u32 ddb_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +struct i2c_algorithm ddb_i2c_algo = { + .master_xfer = ddb_i2c_master_xfer, + .functionality = ddb_i2c_functionality, +}; + +static void ddb_i2c_release(struct ddb *dev) +{ + int i; + struct ddb_i2c *i2c; + struct i2c_adapter *adap; + + for (i = 0; i < dev->info->port_num; i++) { + i2c = &dev->i2c[i]; + adap = &i2c->adap; + i2c_del_adapter(adap); + } +} + +static int ddb_i2c_init(struct ddb *dev) +{ + int i, j, stat = 0; + struct ddb_i2c *i2c; + struct i2c_adapter *adap; + + for (i = 0; i < dev->info->port_num; i++) { + i2c = &dev->i2c[i]; + i2c->dev = dev; + i2c->nr = i; + i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4); + i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8); + i2c->regs = 0x80 + i * 0x20; + ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING); + ddbwritel((i2c->rbuf << 16) | i2c->wbuf, + i2c->regs + I2C_TASKADDRESS); + init_waitqueue_head(&i2c->wq); + + adap = &i2c->adap; + i2c_set_adapdata(adap, i2c); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG; +#else +#ifdef I2C_CLASS_TV_ANALOG + adap->class = I2C_CLASS_TV_ANALOG; +#endif +#endif + strcpy(adap->name, "ddbridge"); + adap->algo = &ddb_i2c_algo; + adap->algo_data = (void *)i2c; + adap->dev.parent = &dev->pdev->dev; + stat = i2c_add_adapter(adap); + if (stat) + break; + } + if (stat) + for (j = 0; j < i; j++) { + i2c = &dev->i2c[j]; + adap = &i2c->adap; + i2c_del_adapter(adap); + } + return stat; +} + + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +#if 0 +static void set_table(struct ddb *dev, u32 off, + dma_addr_t *pbuf, u32 num) +{ + u32 i, base; + u64 mem; + + base = DMA_BASE_ADDRESS_TABLE + off; + for (i = 0; i < num; i++) { + mem = pbuf[i]; + ddbwritel(mem & 0xffffffff, base + i * 8); + ddbwritel(mem >> 32, base + i * 8 + 4); + } +} +#endif + +static void ddb_address_table(struct ddb *dev) +{ + u32 i, j, base; + u64 mem; + dma_addr_t *pbuf; + + for (i = 0; i < dev->info->port_num * 2; i++) { + base = DMA_BASE_ADDRESS_TABLE + i * 0x100; + pbuf = dev->input[i].pbuf; + for (j = 0; j < dev->input[i].dma_buf_num; j++) { + mem = pbuf[j]; + ddbwritel(mem & 0xffffffff, base + j * 8); + ddbwritel(mem >> 32, base + j * 8 + 4); + } + } + for (i = 0; i < dev->info->port_num; i++) { + base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100; + pbuf = dev->output[i].pbuf; + for (j = 0; j < dev->output[i].dma_buf_num; j++) { + mem = pbuf[j]; + ddbwritel(mem & 0xffffffff, base + j * 8); + ddbwritel(mem >> 32, base + j * 8 + 4); + } + } +} + +static void io_free(struct pci_dev *pdev, u8 **vbuf, + dma_addr_t *pbuf, u32 size, int num) +{ + int i; + + for (i = 0; i < num; i++) { + if (vbuf[i]) { + pci_free_consistent(pdev, size, vbuf[i], pbuf[i]); + vbuf[i] = 0; + } + } +} + +static int io_alloc(struct pci_dev *pdev, u8 **vbuf, + dma_addr_t *pbuf, u32 size, int num) +{ + int i; + + for (i = 0; i < num; i++) { + vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]); + if (!vbuf[i]) + return -ENOMEM; + } + return 0; +} + +static int ddb_buffers_alloc(struct ddb *dev) +{ + int i; + struct ddb_port *port; + + for (i = 0; i < dev->info->port_num; i++) { + port = &dev->port[i]; + switch (port->class) { + case DDB_PORT_TUNER: + if (io_alloc(dev->pdev, port->input[0]->vbuf, + port->input[0]->pbuf, + port->input[0]->dma_buf_size, + port->input[0]->dma_buf_num) < 0) + return -1; + if (io_alloc(dev->pdev, port->input[1]->vbuf, + port->input[1]->pbuf, + port->input[1]->dma_buf_size, + port->input[1]->dma_buf_num) < 0) + return -1; + break; + case DDB_PORT_CI: + if (io_alloc(dev->pdev, port->input[0]->vbuf, + port->input[0]->pbuf, + port->input[0]->dma_buf_size, + port->input[0]->dma_buf_num) < 0) + return -1; + if (io_alloc(dev->pdev, port->output->vbuf, + port->output->pbuf, + port->output->dma_buf_size, + port->output->dma_buf_num) < 0) + return -1; + break; + default: + break; + } + } + ddb_address_table(dev); + return 0; +} + +static void ddb_buffers_free(struct ddb *dev) +{ + int i; + struct ddb_port *port; + + for (i = 0; i < dev->info->port_num; i++) { + port = &dev->port[i]; + io_free(dev->pdev, port->input[0]->vbuf, + port->input[0]->pbuf, + port->input[0]->dma_buf_size, + port->input[0]->dma_buf_num); + io_free(dev->pdev, port->input[1]->vbuf, + port->input[1]->pbuf, + port->input[1]->dma_buf_size, + port->input[1]->dma_buf_num); + io_free(dev->pdev, port->output->vbuf, + port->output->pbuf, + port->output->dma_buf_size, + port->output->dma_buf_num); + } +} + +static void ddb_input_start(struct ddb_input *input) +{ + struct ddb *dev = input->port->dev; + + spin_lock_irq(&input->lock); + input->cbuf = 0; + input->coff = 0; + + /* reset */ + ddbwritel(0, TS_INPUT_CONTROL(input->nr)); + ddbwritel(2, TS_INPUT_CONTROL(input->nr)); + ddbwritel(0, TS_INPUT_CONTROL(input->nr)); + + ddbwritel((1 << 16) | + (input->dma_buf_num << 11) | + (input->dma_buf_size >> 7), + DMA_BUFFER_SIZE(input->nr)); + ddbwritel(0, DMA_BUFFER_ACK(input->nr)); + + ddbwritel(1, DMA_BASE_WRITE); + ddbwritel(3, DMA_BUFFER_CONTROL(input->nr)); + ddbwritel(9, TS_INPUT_CONTROL(input->nr)); + input->running = 1; + spin_unlock_irq(&input->lock); +} + +static void ddb_input_stop(struct ddb_input *input) +{ + struct ddb *dev = input->port->dev; + + spin_lock_irq(&input->lock); + ddbwritel(0, TS_INPUT_CONTROL(input->nr)); + ddbwritel(0, DMA_BUFFER_CONTROL(input->nr)); + input->running = 0; + spin_unlock_irq(&input->lock); +} + +static void ddb_output_start(struct ddb_output *output) +{ + struct ddb *dev = output->port->dev; + + spin_lock_irq(&output->lock); + output->cbuf = 0; + output->coff = 0; + ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); + ddbwritel(2, TS_OUTPUT_CONTROL(output->nr)); + ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); + ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr)); + ddbwritel((1 << 16) | + (output->dma_buf_num << 11) | + (output->dma_buf_size >> 7), + DMA_BUFFER_SIZE(output->nr + 8)); + ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8)); + + ddbwritel(1, DMA_BASE_READ); + ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8)); + /* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */ + ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr)); + output->running = 1; + spin_unlock_irq(&output->lock); +} + +static void ddb_output_stop(struct ddb_output *output) +{ + struct ddb *dev = output->port->dev; + + spin_lock_irq(&output->lock); + ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); + ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8)); + output->running = 0; + spin_unlock_irq(&output->lock); +} + +static u32 ddb_output_free(struct ddb_output *output) +{ + u32 idx, off, stat = output->stat; + s32 diff; + + idx = (stat >> 11) & 0x1f; + off = (stat & 0x7ff) << 7; + + if (output->cbuf != idx) { + if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && + (output->dma_buf_size - output->coff <= 188)) + return 0; + return 188; + } + diff = off - output->coff; + if (diff <= 0 || diff > 188) + return 188; + return 0; +} + +static ssize_t ddb_output_write(struct ddb_output *output, + const u8 *buf, size_t count) +{ + struct ddb *dev = output->port->dev; + u32 idx, off, stat = output->stat; + u32 left = count, len; + + idx = (stat >> 11) & 0x1f; + off = (stat & 0x7ff) << 7; + + while (left) { + len = output->dma_buf_size - output->coff; + if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && + (off == 0)) { + if (len <= 188) + break; + len -= 188; + } + if (output->cbuf == idx) { + if (off > output->coff) { +#if 1 + len = off - output->coff; + len -= (len % 188); + if (len <= 188) + +#endif + break; + len -= 188; + } + } + if (len > left) + len = left; + if (copy_from_user(output->vbuf[output->cbuf] + output->coff, + buf, len)) + return -EIO; + left -= len; + buf += len; + output->coff += len; + if (output->coff == output->dma_buf_size) { + output->coff = 0; + output->cbuf = ((output->cbuf + 1) % output->dma_buf_num); + } + ddbwritel((output->cbuf << 11) | (output->coff >> 7), + DMA_BUFFER_ACK(output->nr + 8)); + } + return count - left; +} + +static u32 ddb_input_avail(struct ddb_input *input) +{ + struct ddb *dev = input->port->dev; + u32 idx, off, stat = input->stat; + u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr)); + + idx = (stat >> 11) & 0x1f; + off = (stat & 0x7ff) << 7; + + if (ctrl & 4) { + printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl); + ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr)); + return 0; + } + if (input->cbuf != idx) + return 188; + return 0; +} + +static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count) +{ + struct ddb *dev = input->port->dev; + u32 left = count; + u32 idx, free, stat = input->stat; + int ret; + + idx = (stat >> 11) & 0x1f; + + while (left) { + if (input->cbuf == idx) + return count - left; + free = input->dma_buf_size - input->coff; + if (free > left) + free = left; + ret = copy_to_user(buf, input->vbuf[input->cbuf] + + input->coff, free); + if (ret) + return -EFAULT; + input->coff += free; + if (input->coff == input->dma_buf_size) { + input->coff = 0; + input->cbuf = (input->cbuf+1) % input->dma_buf_num; + } + left -= free; + ddbwritel((input->cbuf << 11) | (input->coff >> 7), + DMA_BUFFER_ACK(input->nr)); + } + return count; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +#if 0 +static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe) +{ + int i; + + for (i = 0; i < dev->info->port_num * 2; i++) { + if (dev->input[i].fe == fe) + return &dev->input[i]; + } + return NULL; +} +#endif + +static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct ddb_input *input = fe->sec_priv; + struct ddb_port *port = input->port; + int status; + + if (enable) { + mutex_lock(&port->i2c_gate_lock); + status = input->gate_ctrl(fe, 1); + } else { + status = input->gate_ctrl(fe, 0); + mutex_unlock(&port->i2c_gate_lock); + } + return status; +} + +static int demod_attach_drxk(struct ddb_input *input) +{ + struct i2c_adapter *i2c = &input->port->i2c->adap; + struct dvb_frontend *fe; + struct drxk_config config; + + memset(&config, 0, sizeof(config)); + config.microcode_name = "drxk_a3.mc"; + config.qam_demod_parameter_count = 4; + config.adr = 0x29 + (input->nr & 1); + + fe = input->fe = dvb_attach(drxk_attach, &config, i2c); + if (!input->fe) { + printk(KERN_ERR "No DRXK found!\n"); + return -ENODEV; + } + fe->sec_priv = input; + input->gate_ctrl = fe->ops.i2c_gate_ctrl; + fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; + return 0; +} + +static int tuner_attach_tda18271(struct ddb_input *input) +{ + struct i2c_adapter *i2c = &input->port->i2c->adap; + struct dvb_frontend *fe; + + if (input->fe->ops.i2c_gate_ctrl) + input->fe->ops.i2c_gate_ctrl(input->fe, 1); + fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60); + if (!fe) { + printk(KERN_ERR "No TDA18271 found!\n"); + return -ENODEV; + } + if (input->fe->ops.i2c_gate_ctrl) + input->fe->ops.i2c_gate_ctrl(input->fe, 0); + return 0; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +static struct stv090x_config stv0900 = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x69, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + + .diseqc_envelope_mode = true, +}; + +static struct stv090x_config stv0900_aa = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + + .diseqc_envelope_mode = true, +}; + +static struct stv6110x_config stv6110a = { + .addr = 0x60, + .refclk = 27000000, + .clk_div = 1, +}; + +static struct stv6110x_config stv6110b = { + .addr = 0x63, + .refclk = 27000000, + .clk_div = 1, +}; + +static int demod_attach_stv0900(struct ddb_input *input, int type) +{ + struct i2c_adapter *i2c = &input->port->i2c->adap; + struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; + + input->fe = dvb_attach(stv090x_attach, feconf, i2c, + (input->nr & 1) ? STV090x_DEMODULATOR_1 + : STV090x_DEMODULATOR_0); + if (!input->fe) { + printk(KERN_ERR "No STV0900 found!\n"); + return -ENODEV; + } + if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0, + 0, (input->nr & 1) ? + (0x09 - type) : (0x0b - type))) { + printk(KERN_ERR "No LNBH24 found!\n"); + return -ENODEV; + } + return 0; +} + +static int tuner_attach_stv6110(struct ddb_input *input, int type) +{ + struct i2c_adapter *i2c = &input->port->i2c->adap; + struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; + struct stv6110x_config *tunerconf = (input->nr & 1) ? + &stv6110b : &stv6110a; + struct stv6110x_devctl *ctl; + + ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c); + if (!ctl) { + printk(KERN_ERR "No STV6110X found!\n"); + return -ENODEV; + } + printk(KERN_INFO "attach tuner input %d adr %02x\n", + input->nr, tunerconf->addr); + + feconf->tuner_init = ctl->tuner_init; + feconf->tuner_sleep = ctl->tuner_sleep; + feconf->tuner_set_mode = ctl->tuner_set_mode; + feconf->tuner_set_frequency = ctl->tuner_set_frequency; + feconf->tuner_get_frequency = ctl->tuner_get_frequency; + feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; + feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; + feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; + feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; + feconf->tuner_set_refclk = ctl->tuner_set_refclk; + feconf->tuner_get_status = ctl->tuner_get_status; + + return 0; +} + +static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv) +{ + dvbdemux->priv = priv; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = start_feed; + dvbdemux->stop_feed = stop_feed; + dvbdemux->write_to_decoder = NULL; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + return dvb_dmx_init(dvbdemux); +} + +static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter) +{ + int ret; + + dmxdev->filternum = 256; + dmxdev->demux = &dvbdemux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adapter); + if (ret < 0) + return ret; + + hw_frontend->source = DMX_FRONTEND_0; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); + mem_frontend->source = DMX_MEMORY_FE; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); + return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +} + +static int start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ddb_input *input = dvbdmx->priv; + + if (!input->users) + ddb_input_start(input); + + return ++input->users; +} + +static int stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ddb_input *input = dvbdmx->priv; + + if (--input->users) + return input->users; + + ddb_input_stop(input); + return 0; +} + + +static void dvb_input_detach(struct ddb_input *input) +{ + struct dvb_adapter *adap = &input->adap; + struct dvb_demux *dvbdemux = &input->demux; + + switch (input->attached) { + case 5: + if (input->fe2) + dvb_unregister_frontend(input->fe2); + if (input->fe) { + dvb_unregister_frontend(input->fe); + dvb_frontend_detach(input->fe); + input->fe = NULL; + } + case 4: + dvb_net_release(&input->dvbnet); + + case 3: + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, + &input->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, + &input->mem_frontend); + dvb_dmxdev_release(&input->dmxdev); + + case 2: + dvb_dmx_release(&input->demux); + + case 1: + dvb_unregister_adapter(adap); + } + input->attached = 0; +} + +static int dvb_input_attach(struct ddb_input *input) +{ + int ret; + struct ddb_port *port = input->port; + struct dvb_adapter *adap = &input->adap; + struct dvb_demux *dvbdemux = &input->demux; + + ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE, + &input->port->dev->pdev->dev, + adapter_nr); + if (ret < 0) { + printk(KERN_ERR "ddbridge: Could not register adapter." + "Check if you enabled enough adapters in dvb-core!\n"); + return ret; + } + input->attached = 1; + + ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", + start_feed, + stop_feed, input); + if (ret < 0) + return ret; + input->attached = 2; + + ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux, + &input->hw_frontend, + &input->mem_frontend, adap); + if (ret < 0) + return ret; + input->attached = 3; + + ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux); + if (ret < 0) + return ret; + input->attached = 4; + + input->fe = 0; + switch (port->type) { + case DDB_TUNER_DVBS_ST: + if (demod_attach_stv0900(input, 0) < 0) + return -ENODEV; + if (tuner_attach_stv6110(input, 0) < 0) + return -ENODEV; + if (input->fe) { + if (dvb_register_frontend(adap, input->fe) < 0) + return -ENODEV; + } + break; + case DDB_TUNER_DVBS_ST_AA: + if (demod_attach_stv0900(input, 1) < 0) + return -ENODEV; + if (tuner_attach_stv6110(input, 1) < 0) + return -ENODEV; + if (input->fe) { + if (dvb_register_frontend(adap, input->fe) < 0) + return -ENODEV; + } + break; + case DDB_TUNER_DVBCT_TR: + if (demod_attach_drxk(input) < 0) + return -ENODEV; + if (tuner_attach_tda18271(input) < 0) + return -ENODEV; + if (input->fe) { + if (dvb_register_frontend(adap, input->fe) < 0) + return -ENODEV; + } + if (input->fe2) { + if (dvb_register_frontend(adap, input->fe2) < 0) + return -ENODEV; + input->fe2->tuner_priv = input->fe->tuner_priv; + memcpy(&input->fe2->ops.tuner_ops, + &input->fe->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + } + break; + } + input->attached = 5; + return 0; +} + +/****************************************************************************/ +/****************************************************************************/ + +static ssize_t ts_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ddb_output *output = dvbdev->priv; + size_t left = count; + int stat; + + while (left) { + if (ddb_output_free(output) < 188) { + if (file->f_flags & O_NONBLOCK) + break; + if (wait_event_interruptible( + output->wq, ddb_output_free(output) >= 188) < 0) + break; + } + stat = ddb_output_write(output, buf, left); + if (stat < 0) + break; + buf += stat; + left -= stat; + } + return (left == count) ? -EAGAIN : (count - left); +} + +static ssize_t ts_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ddb_output *output = dvbdev->priv; + struct ddb_input *input = output->port->input[0]; + int left, read; + + count -= count % 188; + left = count; + while (left) { + if (ddb_input_avail(input) < 188) { + if (file->f_flags & O_NONBLOCK) + break; + if (wait_event_interruptible( + input->wq, ddb_input_avail(input) >= 188) < 0) + break; + } + read = ddb_input_read(input, buf, left); + if (read < 0) + return read; + left -= read; + buf += read; + } + return (left == count) ? -EAGAIN : (count - left); +} + +static unsigned int ts_poll(struct file *file, poll_table *wait) +{ + /* + struct dvb_device *dvbdev = file->private_data; + struct ddb_output *output = dvbdev->priv; + struct ddb_input *input = output->port->input[0]; + */ + unsigned int mask = 0; + +#if 0 + if (data_avail_to_read) + mask |= POLLIN | POLLRDNORM; + if (data_avail_to_write) + mask |= POLLOUT | POLLWRNORM; + + poll_wait(file, &read_queue, wait); + poll_wait(file, &write_queue, wait); +#endif + return mask; +} + +static const struct file_operations ci_fops = { + .owner = THIS_MODULE, + .read = ts_read, + .write = ts_write, + .open = dvb_generic_open, + .release = dvb_generic_release, + .poll = ts_poll, + .mmap = 0, +}; + +static struct dvb_device dvbdev_ci = { + .priv = 0, + .readers = -1, + .writers = -1, + .users = -1, + .fops = &ci_fops, +}; + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static void input_tasklet(unsigned long data) +{ + struct ddb_input *input = (struct ddb_input *) data; + struct ddb *dev = input->port->dev; + + spin_lock(&input->lock); + if (!input->running) { + spin_unlock(&input->lock); + return; + } + input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); + + if (input->port->class == DDB_PORT_TUNER) { + if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr))) + printk(KERN_ERR "Overflow input %d\n", input->nr); + while (input->cbuf != ((input->stat >> 11) & 0x1f) + || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) { + dvb_dmx_swfilter_packets(&input->demux, + input->vbuf[input->cbuf], + input->dma_buf_size / 188); + + input->cbuf = (input->cbuf + 1) % input->dma_buf_num; + ddbwritel((input->cbuf << 11), + DMA_BUFFER_ACK(input->nr)); + input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); + } + } + if (input->port->class == DDB_PORT_CI) + wake_up(&input->wq); + spin_unlock(&input->lock); +} + +static void output_tasklet(unsigned long data) +{ + struct ddb_output *output = (struct ddb_output *) data; + struct ddb *dev = output->port->dev; + + spin_lock(&output->lock); + if (!output->running) { + spin_unlock(&output->lock); + return; + } + output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8)); + wake_up(&output->wq); + spin_unlock(&output->lock); +} + + +struct cxd2099_cfg cxd_cfg = { + .bitrate = 62000, + .adr = 0x40, + .polarity = 1, + .clock_mode = 1, +}; + +static int ddb_ci_attach(struct ddb_port *port) +{ + int ret; + + ret = dvb_register_adapter(&port->output->adap, + "DDBridge", + THIS_MODULE, + &port->dev->pdev->dev, + adapter_nr); + if (ret < 0) + return ret; + port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap); + if (!port->en) { + dvb_unregister_adapter(&port->output->adap); + return -ENODEV; + } + ddb_input_start(port->input[0]); + ddb_output_start(port->output); + dvb_ca_en50221_init(&port->output->adap, + port->en, 0, 1); + ret = dvb_register_device(&port->output->adap, &port->output->dev, + &dvbdev_ci, (void *) port->output, + DVB_DEVICE_SEC); + return ret; +} + +static int ddb_port_attach(struct ddb_port *port) +{ + int ret = 0; + + switch (port->class) { + case DDB_PORT_TUNER: + ret = dvb_input_attach(port->input[0]); + if (ret < 0) + break; + ret = dvb_input_attach(port->input[1]); + break; + case DDB_PORT_CI: + ret = ddb_ci_attach(port); + break; + default: + break; + } + if (ret < 0) + printk(KERN_ERR "port_attach on port %d failed\n", port->nr); + return ret; +} + +static int ddb_ports_attach(struct ddb *dev) +{ + int i, ret = 0; + struct ddb_port *port; + + for (i = 0; i < dev->info->port_num; i++) { + port = &dev->port[i]; + ret = ddb_port_attach(port); + if (ret < 0) + break; + } + return ret; +} + +static void ddb_ports_detach(struct ddb *dev) +{ + int i; + struct ddb_port *port; + + for (i = 0; i < dev->info->port_num; i++) { + port = &dev->port[i]; + switch (port->class) { + case DDB_PORT_TUNER: + dvb_input_detach(port->input[0]); + dvb_input_detach(port->input[1]); + break; + case DDB_PORT_CI: + if (port->output->dev) + dvb_unregister_device(port->output->dev); + if (port->en) { + ddb_input_stop(port->input[0]); + ddb_output_stop(port->output); + dvb_ca_en50221_release(port->en); + kfree(port->en); + port->en = 0; + dvb_unregister_adapter(&port->output->adap); + } + break; + } + } +} + +/****************************************************************************/ +/****************************************************************************/ + +static int port_has_ci(struct ddb_port *port) +{ + u8 val; + return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1; +} + +static int port_has_stv0900(struct ddb_port *port) +{ + u8 val; + if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0) + return 0; + return 1; +} + +static int port_has_stv0900_aa(struct ddb_port *port) +{ + u8 val; + if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0) + return 0; + return 1; +} + +static int port_has_drxks(struct ddb_port *port) +{ + u8 val; + if (i2c_read(&port->i2c->adap, 0x29, &val) < 0) + return 0; + if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0) + return 0; + return 1; +} + +static void ddb_port_probe(struct ddb_port *port) +{ + struct ddb *dev = port->dev; + char *modname = "NO MODULE"; + + port->class = DDB_PORT_NONE; + + if (port_has_ci(port)) { + modname = "CI"; + port->class = DDB_PORT_CI; + ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); + } else if (port_has_stv0900(port)) { + modname = "DUAL DVB-S2"; + port->class = DDB_PORT_TUNER; + port->type = DDB_TUNER_DVBS_ST; + ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); + } else if (port_has_stv0900_aa(port)) { + modname = "DUAL DVB-S2"; + port->class = DDB_PORT_TUNER; + port->type = DDB_TUNER_DVBS_ST_AA; + ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); + } else if (port_has_drxks(port)) { + modname = "DUAL DVB-C/T"; + port->class = DDB_PORT_TUNER; + port->type = DDB_TUNER_DVBCT_TR; + ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); + } + printk(KERN_INFO "Port %d (TAB %d): %s\n", + port->nr, port->nr+1, modname); +} + +static void ddb_input_init(struct ddb_port *port, int nr) +{ + struct ddb *dev = port->dev; + struct ddb_input *input = &dev->input[nr]; + + input->nr = nr; + input->port = port; + input->dma_buf_num = INPUT_DMA_BUFS; + input->dma_buf_size = INPUT_DMA_SIZE; + ddbwritel(0, TS_INPUT_CONTROL(nr)); + ddbwritel(2, TS_INPUT_CONTROL(nr)); + ddbwritel(0, TS_INPUT_CONTROL(nr)); + ddbwritel(0, DMA_BUFFER_ACK(nr)); + tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input); + spin_lock_init(&input->lock); + init_waitqueue_head(&input->wq); +} + +static void ddb_output_init(struct ddb_port *port, int nr) +{ + struct ddb *dev = port->dev; + struct ddb_output *output = &dev->output[nr]; + output->nr = nr; + output->port = port; + output->dma_buf_num = OUTPUT_DMA_BUFS; + output->dma_buf_size = OUTPUT_DMA_SIZE; + + ddbwritel(0, TS_OUTPUT_CONTROL(nr)); + ddbwritel(2, TS_OUTPUT_CONTROL(nr)); + ddbwritel(0, TS_OUTPUT_CONTROL(nr)); + tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output); + init_waitqueue_head(&output->wq); +} + +static void ddb_ports_init(struct ddb *dev) +{ + int i; + struct ddb_port *port; + + for (i = 0; i < dev->info->port_num; i++) { + port = &dev->port[i]; + port->dev = dev; + port->nr = i; + port->i2c = &dev->i2c[i]; + port->input[0] = &dev->input[2 * i]; + port->input[1] = &dev->input[2 * i + 1]; + port->output = &dev->output[i]; + + mutex_init(&port->i2c_gate_lock); + ddb_port_probe(port); + ddb_input_init(port, 2 * i); + ddb_input_init(port, 2 * i + 1); + ddb_output_init(port, i); + } +} + +static void ddb_ports_release(struct ddb *dev) +{ + int i; + struct ddb_port *port; + + for (i = 0; i < dev->info->port_num; i++) { + port = &dev->port[i]; + port->dev = dev; + tasklet_kill(&port->input[0]->tasklet); + tasklet_kill(&port->input[1]->tasklet); + tasklet_kill(&port->output->tasklet); + } +} + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static void irq_handle_i2c(struct ddb *dev, int n) +{ + struct ddb_i2c *i2c = &dev->i2c[n]; + + i2c->done = 1; + wake_up(&i2c->wq); +} + +static irqreturn_t irq_handler(int irq, void *dev_id) +{ + struct ddb *dev = (struct ddb *) dev_id; + u32 s = ddbreadl(INTERRUPT_STATUS); + + if (!s) + return IRQ_NONE; + + do { + ddbwritel(s, INTERRUPT_ACK); + + if (s & 0x00000001) + irq_handle_i2c(dev, 0); + if (s & 0x00000002) + irq_handle_i2c(dev, 1); + if (s & 0x00000004) + irq_handle_i2c(dev, 2); + if (s & 0x00000008) + irq_handle_i2c(dev, 3); + + if (s & 0x00000100) + tasklet_schedule(&dev->input[0].tasklet); + if (s & 0x00000200) + tasklet_schedule(&dev->input[1].tasklet); + if (s & 0x00000400) + tasklet_schedule(&dev->input[2].tasklet); + if (s & 0x00000800) + tasklet_schedule(&dev->input[3].tasklet); + if (s & 0x00001000) + tasklet_schedule(&dev->input[4].tasklet); + if (s & 0x00002000) + tasklet_schedule(&dev->input[5].tasklet); + if (s & 0x00004000) + tasklet_schedule(&dev->input[6].tasklet); + if (s & 0x00008000) + tasklet_schedule(&dev->input[7].tasklet); + + if (s & 0x00010000) + tasklet_schedule(&dev->output[0].tasklet); + if (s & 0x00020000) + tasklet_schedule(&dev->output[1].tasklet); + if (s & 0x00040000) + tasklet_schedule(&dev->output[2].tasklet); + if (s & 0x00080000) + tasklet_schedule(&dev->output[3].tasklet); + + /* if (s & 0x000f0000) printk(KERN_DEBUG "%08x\n", istat); */ + } while ((s = ddbreadl(INTERRUPT_STATUS))); + + return IRQ_HANDLED; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) +{ + u32 data, shift; + + if (wlen > 4) + ddbwritel(1, SPI_CONTROL); + while (wlen > 4) { + /* FIXME: check for big-endian */ + data = swab32(*(u32 *)wbuf); + wbuf += 4; + wlen -= 4; + ddbwritel(data, SPI_DATA); + while (ddbreadl(SPI_CONTROL) & 0x0004) + ; + } + + if (rlen) + ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); + else + ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); + + data = 0; + shift = ((4 - wlen) * 8); + while (wlen) { + data <<= 8; + data |= *wbuf; + wlen--; + wbuf++; + } + if (shift) + data <<= shift; + ddbwritel(data, SPI_DATA); + while (ddbreadl(SPI_CONTROL) & 0x0004) + ; + + if (!rlen) { + ddbwritel(0, SPI_CONTROL); + return 0; + } + if (rlen > 4) + ddbwritel(1, SPI_CONTROL); + + while (rlen > 4) { + ddbwritel(0xffffffff, SPI_DATA); + while (ddbreadl(SPI_CONTROL) & 0x0004) + ; + data = ddbreadl(SPI_DATA); + *(u32 *) rbuf = swab32(data); + rbuf += 4; + rlen -= 4; + } + ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL); + ddbwritel(0xffffffff, SPI_DATA); + while (ddbreadl(SPI_CONTROL) & 0x0004) + ; + + data = ddbreadl(SPI_DATA); + ddbwritel(0, SPI_CONTROL); + + if (rlen < 4) + data <<= ((4 - rlen) * 8); + + while (rlen > 0) { + *rbuf = ((data >> 24) & 0xff); + data <<= 8; + rbuf++; + rlen--; + } + return 0; +} + +#define DDB_MAGIC 'd' + +struct ddb_flashio { + __u8 *write_buf; + __u32 write_len; + __u8 *read_buf; + __u32 read_len; +}; + +#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio) + +#define DDB_NAME "ddbridge" + +static u32 ddb_num; +static struct ddb *ddbs[32]; +static struct class *ddb_class; +static int ddb_major; + +static int ddb_open(struct inode *inode, struct file *file) +{ + struct ddb *dev = ddbs[iminor(inode)]; + + file->private_data = dev; + return 0; +} + +static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ddb *dev = file->private_data; + void *parg = (void *)arg; + int res; + + switch (cmd) { + case IOCTL_DDB_FLASHIO: + { + struct ddb_flashio fio; + u8 *rbuf, *wbuf; + + if (copy_from_user(&fio, parg, sizeof(fio))) + return -EFAULT; + + if (fio.write_len > 1028 || fio.read_len > 1028) + return -EINVAL; + if (fio.write_len + fio.read_len > 1028) + return -EINVAL; + + wbuf = &dev->iobuf[0]; + rbuf = wbuf + fio.write_len; + + if (copy_from_user(wbuf, fio.write_buf, fio.write_len)) + return -EFAULT; + res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len); + if (res) + return res; + if (copy_to_user(fio.read_buf, rbuf, fio.read_len)) + return -EFAULT; + break; + } + default: + return -ENOTTY; + } + return 0; +} + +static const struct file_operations ddb_fops = { + .unlocked_ioctl = ddb_ioctl, + .open = ddb_open, +}; + +static char *ddb_devnode(struct device *device, umode_t *mode) +{ + struct ddb *dev = dev_get_drvdata(device); + + return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr); +} + +static int ddb_class_create(void) +{ + ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops); + if (ddb_major < 0) + return ddb_major; + + ddb_class = class_create(THIS_MODULE, DDB_NAME); + if (IS_ERR(ddb_class)) { + unregister_chrdev(ddb_major, DDB_NAME); + return -1; + } + ddb_class->devnode = ddb_devnode; + return 0; +} + +static void ddb_class_destroy(void) +{ + class_destroy(ddb_class); + unregister_chrdev(ddb_major, DDB_NAME); +} + +static int ddb_device_create(struct ddb *dev) +{ + dev->nr = ddb_num++; + dev->ddb_dev = device_create(ddb_class, NULL, + MKDEV(ddb_major, dev->nr), + dev, "ddbridge%d", dev->nr); + ddbs[dev->nr] = dev; + if (IS_ERR(dev->ddb_dev)) + return -1; + return 0; +} + +static void ddb_device_destroy(struct ddb *dev) +{ + ddb_num--; + if (IS_ERR(dev->ddb_dev)) + return; + device_destroy(ddb_class, MKDEV(ddb_major, 0)); +} + + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static void ddb_unmap(struct ddb *dev) +{ + if (dev->regs) + iounmap(dev->regs); + vfree(dev); +} + + +static void __devexit ddb_remove(struct pci_dev *pdev) +{ + struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev); + + ddb_ports_detach(dev); + ddb_i2c_release(dev); + + ddbwritel(0, INTERRUPT_ENABLE); + free_irq(dev->pdev->irq, dev); +#ifdef CONFIG_PCI_MSI + if (dev->msi) + pci_disable_msi(dev->pdev); +#endif + ddb_ports_release(dev); + ddb_buffers_free(dev); + ddb_device_destroy(dev); + + ddb_unmap(dev); + pci_set_drvdata(pdev, 0); + pci_disable_device(pdev); +} + + +static int __devinit ddb_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct ddb *dev; + int stat = 0; + int irq_flag = IRQF_SHARED; + + if (pci_enable_device(pdev) < 0) + return -ENODEV; + + dev = vmalloc(sizeof(struct ddb)); + if (dev == NULL) + return -ENOMEM; + memset(dev, 0, sizeof(struct ddb)); + + dev->pdev = pdev; + pci_set_drvdata(pdev, dev); + dev->info = (struct ddb_info *) id->driver_data; + printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name); + + dev->regs = ioremap(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); + if (!dev->regs) { + stat = -ENOMEM; + goto fail; + } + printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4)); + +#ifdef CONFIG_PCI_MSI + if (pci_msi_enabled()) + stat = pci_enable_msi(dev->pdev); + if (stat) { + printk(KERN_INFO ": MSI not available.\n"); + } else { + irq_flag = 0; + dev->msi = 1; + } +#endif + stat = request_irq(dev->pdev->irq, irq_handler, + irq_flag, "DDBridge", (void *) dev); + if (stat < 0) + goto fail1; + ddbwritel(0, DMA_BASE_WRITE); + ddbwritel(0, DMA_BASE_READ); + ddbwritel(0xffffffff, INTERRUPT_ACK); + ddbwritel(0xfff0f, INTERRUPT_ENABLE); + ddbwritel(0, MSI1_ENABLE); + + if (ddb_i2c_init(dev) < 0) + goto fail1; + ddb_ports_init(dev); + if (ddb_buffers_alloc(dev) < 0) { + printk(KERN_INFO ": Could not allocate buffer memory\n"); + goto fail2; + } + if (ddb_ports_attach(dev) < 0) + goto fail3; + ddb_device_create(dev); + return 0; + +fail3: + ddb_ports_detach(dev); + printk(KERN_ERR "fail3\n"); + ddb_ports_release(dev); +fail2: + printk(KERN_ERR "fail2\n"); + ddb_buffers_free(dev); +fail1: + printk(KERN_ERR "fail1\n"); + if (dev->msi) + pci_disable_msi(dev->pdev); + free_irq(dev->pdev->irq, dev); +fail: + printk(KERN_ERR "fail\n"); + ddb_unmap(dev); + pci_set_drvdata(pdev, 0); + pci_disable_device(pdev); + return -1; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +static struct ddb_info ddb_none = { + .type = DDB_NONE, + .name = "Digital Devices PCIe bridge", +}; + +static struct ddb_info ddb_octopus = { + .type = DDB_OCTOPUS, + .name = "Digital Devices Octopus DVB adapter", + .port_num = 4, +}; + +static struct ddb_info ddb_octopus_le = { + .type = DDB_OCTOPUS, + .name = "Digital Devices Octopus LE DVB adapter", + .port_num = 2, +}; + +static struct ddb_info ddb_v6 = { + .type = DDB_OCTOPUS, + .name = "Digital Devices Cine S2 V6 DVB adapter", + .port_num = 3, +}; + +#define DDVID 0xdd01 /* Digital Devices Vendor ID */ + +#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \ + .vendor = _vend, .device = _dev, \ + .subvendor = _subvend, .subdevice = _subdev, \ + .driver_data = (unsigned long)&_driverdata } + +static const struct pci_device_id ddb_id_tbl[] __devinitdata = { + DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus), + DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus), + DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le), + DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus), + DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6), + /* in case sub-ids got deleted in flash */ + DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none), + {0} +}; +MODULE_DEVICE_TABLE(pci, ddb_id_tbl); + + +static struct pci_driver ddb_pci_driver = { + .name = "DDBridge", + .id_table = ddb_id_tbl, + .probe = ddb_probe, + .remove = __devexit_p(ddb_remove), +}; + +static __init int module_init_ddbridge(void) +{ + printk(KERN_INFO "Digital Devices PCIE bridge driver, " + "Copyright (C) 2010-11 Digital Devices GmbH\n"); + if (ddb_class_create()) + return -1; + return pci_register_driver(&ddb_pci_driver); +} + +static __exit void module_exit_ddbridge(void) +{ + pci_unregister_driver(&ddb_pci_driver); + ddb_class_destroy(); +} + +module_init(module_init_ddbridge); +module_exit(module_exit_ddbridge); + +MODULE_DESCRIPTION("Digital Devices PCIe Bridge"); +MODULE_AUTHOR("Ralph Metzler"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.5"); diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h new file mode 100644 index 000000000000..a3ccb318b500 --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-regs.h @@ -0,0 +1,151 @@ +/* + * ddbridge-regs.h: Digital Devices PCIe bridge driver + * + * Copyright (C) 2010-2011 Digital Devices GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +/* DD-DVBBridgeV1.h 273 2010-09-17 05:03:16Z manfred */ + +/* Register Definitions */ + +#define CUR_REGISTERMAP_VERSION 0x10000 + +#define HARDWARE_VERSION 0x00 +#define REGISTERMAP_VERSION 0x04 + +/* ------------------------------------------------------------------------- */ +/* SPI Controller */ + +#define SPI_CONTROL 0x10 +#define SPI_DATA 0x14 + +/* ------------------------------------------------------------------------- */ + +/* Interrupt controller */ +/* How many MSI's are available depends on HW (Min 2 max 8) */ +/* How many are usable also depends on Host platform */ + +#define INTERRUPT_BASE (0x40) + +#define INTERRUPT_ENABLE (INTERRUPT_BASE + 0x00) +#define MSI0_ENABLE (INTERRUPT_BASE + 0x00) +#define MSI1_ENABLE (INTERRUPT_BASE + 0x04) +#define MSI2_ENABLE (INTERRUPT_BASE + 0x08) +#define MSI3_ENABLE (INTERRUPT_BASE + 0x0C) +#define MSI4_ENABLE (INTERRUPT_BASE + 0x10) +#define MSI5_ENABLE (INTERRUPT_BASE + 0x14) +#define MSI6_ENABLE (INTERRUPT_BASE + 0x18) +#define MSI7_ENABLE (INTERRUPT_BASE + 0x1C) + +#define INTERRUPT_STATUS (INTERRUPT_BASE + 0x20) +#define INTERRUPT_ACK (INTERRUPT_BASE + 0x20) + +#define INTMASK_I2C1 (0x00000001) +#define INTMASK_I2C2 (0x00000002) +#define INTMASK_I2C3 (0x00000004) +#define INTMASK_I2C4 (0x00000008) + +#define INTMASK_CIRQ1 (0x00000010) +#define INTMASK_CIRQ2 (0x00000020) +#define INTMASK_CIRQ3 (0x00000040) +#define INTMASK_CIRQ4 (0x00000080) + +#define INTMASK_TSINPUT1 (0x00000100) +#define INTMASK_TSINPUT2 (0x00000200) +#define INTMASK_TSINPUT3 (0x00000400) +#define INTMASK_TSINPUT4 (0x00000800) +#define INTMASK_TSINPUT5 (0x00001000) +#define INTMASK_TSINPUT6 (0x00002000) +#define INTMASK_TSINPUT7 (0x00004000) +#define INTMASK_TSINPUT8 (0x00008000) + +#define INTMASK_TSOUTPUT1 (0x00010000) +#define INTMASK_TSOUTPUT2 (0x00020000) +#define INTMASK_TSOUTPUT3 (0x00040000) +#define INTMASK_TSOUTPUT4 (0x00080000) + +/* ------------------------------------------------------------------------- */ +/* I2C Master Controller */ + +#define I2C_BASE (0x80) /* Byte offset */ + +#define I2C_COMMAND (0x00) +#define I2C_TIMING (0x04) +#define I2C_TASKLENGTH (0x08) /* High read, low write */ +#define I2C_TASKADDRESS (0x0C) /* High read, low write */ + +#define I2C_MONITOR (0x1C) + +#define I2C_BASE_1 (I2C_BASE + 0x00) +#define I2C_BASE_2 (I2C_BASE + 0x20) +#define I2C_BASE_3 (I2C_BASE + 0x40) +#define I2C_BASE_4 (I2C_BASE + 0x60) + +#define I2C_BASE_N(i) (I2C_BASE + (i) * 0x20) + +#define I2C_TASKMEM_BASE (0x1000) /* Byte offset */ +#define I2C_TASKMEM_SIZE (0x1000) + +#define I2C_SPEED_400 (0x04030404) +#define I2C_SPEED_200 (0x09080909) +#define I2C_SPEED_154 (0x0C0B0C0C) +#define I2C_SPEED_100 (0x13121313) +#define I2C_SPEED_77 (0x19181919) +#define I2C_SPEED_50 (0x27262727) + + +/* ------------------------------------------------------------------------- */ +/* DMA Controller */ + +#define DMA_BASE_WRITE (0x100) +#define DMA_BASE_READ (0x140) + +#define DMA_CONTROL (0x00) /* 64 */ +#define DMA_ERROR (0x04) /* 65 ( only read instance ) */ + +#define DMA_DIAG_CONTROL (0x1C) /* 71 */ +#define DMA_DIAG_PACKETCOUNTER_LOW (0x20) /* 72 */ +#define DMA_DIAG_PACKETCOUNTER_HIGH (0x24) /* 73 */ +#define DMA_DIAG_TIMECOUNTER_LOW (0x28) /* 74 */ +#define DMA_DIAG_TIMECOUNTER_HIGH (0x2C) /* 75 */ +#define DMA_DIAG_RECHECKCOUNTER (0x30) /* 76 ( Split completions on read ) */ +#define DMA_DIAG_WAITTIMEOUTINIT (0x34) /* 77 */ +#define DMA_DIAG_WAITOVERFLOWCOUNTER (0x38) /* 78 */ +#define DMA_DIAG_WAITCOUNTER (0x3C) /* 79 */ + +/* ------------------------------------------------------------------------- */ +/* DMA Buffer */ + +#define TS_INPUT_BASE (0x200) +#define TS_INPUT_CONTROL(i) (TS_INPUT_BASE + (i) * 16 + 0x00) + +#define TS_OUTPUT_BASE (0x280) +#define TS_OUTPUT_CONTROL(i) (TS_OUTPUT_BASE + (i) * 16 + 0x00) + +#define DMA_BUFFER_BASE (0x300) + +#define DMA_BUFFER_CONTROL(i) (DMA_BUFFER_BASE + (i) * 16 + 0x00) +#define DMA_BUFFER_ACK(i) (DMA_BUFFER_BASE + (i) * 16 + 0x04) +#define DMA_BUFFER_CURRENT(i) (DMA_BUFFER_BASE + (i) * 16 + 0x08) +#define DMA_BUFFER_SIZE(i) (DMA_BUFFER_BASE + (i) * 16 + 0x0c) + +#define DMA_BASE_ADDRESS_TABLE (0x2000) +#define DMA_BASE_ADDRESS_TABLE_ENTRIES (512) + diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h new file mode 100644 index 000000000000..8b1b41d2a52d --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -0,0 +1,185 @@ +/* + * ddbridge.h: Digital Devices PCIe bridge driver + * + * Copyright (C) 2010-2011 Digital Devices GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _DDBRIDGE_H_ +#define _DDBRIDGE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_ringbuffer.h" +#include "dvb_ca_en50221.h" +#include "dvb_net.h" +#include "cxd2099.h" + +#define DDB_MAX_I2C 4 +#define DDB_MAX_PORT 4 +#define DDB_MAX_INPUT 8 +#define DDB_MAX_OUTPUT 4 + +struct ddb_info { + int type; +#define DDB_NONE 0 +#define DDB_OCTOPUS 1 + char *name; + int port_num; + u32 port_type[DDB_MAX_PORT]; +}; + +/* DMA_SIZE MUST be divisible by 188 and 128 !!! */ + +#define INPUT_DMA_MAX_BUFS 32 /* hardware table limit */ +#define INPUT_DMA_BUFS 8 +#define INPUT_DMA_SIZE (128*47*21) + +#define OUTPUT_DMA_MAX_BUFS 32 +#define OUTPUT_DMA_BUFS 8 +#define OUTPUT_DMA_SIZE (128*47*21) + +struct ddb; +struct ddb_port; + +struct ddb_input { + struct ddb_port *port; + u32 nr; + int attached; + + dma_addr_t pbuf[INPUT_DMA_MAX_BUFS]; + u8 *vbuf[INPUT_DMA_MAX_BUFS]; + u32 dma_buf_num; + u32 dma_buf_size; + + struct tasklet_struct tasklet; + spinlock_t lock; + wait_queue_head_t wq; + int running; + u32 stat; + u32 cbuf; + u32 coff; + + struct dvb_adapter adap; + struct dvb_device *dev; + struct dvb_frontend *fe; + struct dvb_frontend *fe2; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvbnet; + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + int users; + int (*gate_ctrl)(struct dvb_frontend *, int); +}; + +struct ddb_output { + struct ddb_port *port; + u32 nr; + dma_addr_t pbuf[OUTPUT_DMA_MAX_BUFS]; + u8 *vbuf[OUTPUT_DMA_MAX_BUFS]; + u32 dma_buf_num; + u32 dma_buf_size; + struct tasklet_struct tasklet; + spinlock_t lock; + wait_queue_head_t wq; + int running; + u32 stat; + u32 cbuf; + u32 coff; + + struct dvb_adapter adap; + struct dvb_device *dev; +}; + +struct ddb_i2c { + struct ddb *dev; + u32 nr; + struct i2c_adapter adap; + struct i2c_adapter adap2; + u32 regs; + u32 rbuf; + u32 wbuf; + int done; + wait_queue_head_t wq; +}; + +struct ddb_port { + struct ddb *dev; + u32 nr; + struct ddb_i2c *i2c; + struct mutex i2c_gate_lock; + u32 class; +#define DDB_PORT_NONE 0 +#define DDB_PORT_CI 1 +#define DDB_PORT_TUNER 2 + u32 type; +#define DDB_TUNER_NONE 0 +#define DDB_TUNER_DVBS_ST 1 +#define DDB_TUNER_DVBS_ST_AA 2 +#define DDB_TUNER_DVBCT_TR 16 +#define DDB_TUNER_DVBCT_ST 17 + u32 adr; + + struct ddb_input *input[2]; + struct ddb_output *output; + struct dvb_ca_en50221 *en; +}; + +struct ddb { + struct pci_dev *pdev; + unsigned char *regs; + struct ddb_port port[DDB_MAX_PORT]; + struct ddb_i2c i2c[DDB_MAX_I2C]; + struct ddb_input input[DDB_MAX_INPUT]; + struct ddb_output output[DDB_MAX_OUTPUT]; + + struct device *ddb_dev; + int nr; + u8 iobuf[1028]; + + struct ddb_info *info; + int msi; +}; + +/****************************************************************************/ + +#define ddbwritel(_val, _adr) writel((_val), \ + (char *) (dev->regs+(_adr))) +#define ddbreadl(_adr) readl((char *) (dev->regs+(_adr))) +#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *) \ + (dev->regs+(_adr)), (_src), (_count)) +#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \ + (dev->regs+(_adr)), (_count)) + +/****************************************************************************/ + +#endif diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig new file mode 100644 index 000000000000..f3de0a4d63f2 --- /dev/null +++ b/drivers/media/pci/dm1105/Kconfig @@ -0,0 +1,20 @@ +config DVB_DM1105 + tristate "SDMC DM1105 based PCI cards" + depends on DVB_CORE && PCI && I2C + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_STV0288 if !DVB_FE_CUSTOMISE + select DVB_STB6000 if !DVB_FE_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE + select DVB_SI21XX if !DVB_FE_CUSTOMISE + select DVB_DS3000 if !DVB_FE_CUSTOMISE + depends on RC_CORE + help + Support for cards based on the SDMC DM1105 PCI chip like + DvbWorld 2002 + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the PCI bus, so you need + an external software decoder to watch TV on your computer. + + Say Y or M if you own such a device and want to use it. diff --git a/drivers/media/pci/dm1105/Makefile b/drivers/media/pci/dm1105/Makefile new file mode 100644 index 000000000000..327585143c83 --- /dev/null +++ b/drivers/media/pci/dm1105/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_DVB_DM1105) += dm1105.o + +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c new file mode 100644 index 000000000000..a609b3a9b146 --- /dev/null +++ b/drivers/media/pci/dm1105/dm1105.c @@ -0,0 +1,1248 @@ +/* + * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip + * + * Copyright (C) 2008 Igor M. Liplianin + * + * This program is free software; 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 +#include +#include + +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" +#include "dvb-pll.h" + +#include "stv0299.h" +#include "stv0288.h" +#include "stb6000.h" +#include "si21xx.h" +#include "cx24116.h" +#include "z0194a.h" +#include "ds3000.h" + +#define MODULE_NAME "dm1105" + +#define UNSET (-1U) + +#define DM1105_BOARD_NOAUTO UNSET +#define DM1105_BOARD_UNKNOWN 0 +#define DM1105_BOARD_DVBWORLD_2002 1 +#define DM1105_BOARD_DVBWORLD_2004 2 +#define DM1105_BOARD_AXESS_DM05 3 +#define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO 4 + +/* ----------------------------------------------- */ +/* + * PCI ID's + */ +#ifndef PCI_VENDOR_ID_TRIGEM +#define PCI_VENDOR_ID_TRIGEM 0x109f +#endif +#ifndef PCI_VENDOR_ID_AXESS +#define PCI_VENDOR_ID_AXESS 0x195d +#endif +#ifndef PCI_DEVICE_ID_DM1105 +#define PCI_DEVICE_ID_DM1105 0x036f +#endif +#ifndef PCI_DEVICE_ID_DW2002 +#define PCI_DEVICE_ID_DW2002 0x2002 +#endif +#ifndef PCI_DEVICE_ID_DW2004 +#define PCI_DEVICE_ID_DW2004 0x2004 +#endif +#ifndef PCI_DEVICE_ID_DM05 +#define PCI_DEVICE_ID_DM05 0x1105 +#endif +/* ----------------------------------------------- */ +/* sdmc dm1105 registers */ + +/* TS Control */ +#define DM1105_TSCTR 0x00 +#define DM1105_DTALENTH 0x04 + +/* GPIO Interface */ +#define DM1105_GPIOVAL 0x08 +#define DM1105_GPIOCTR 0x0c + +/* PID serial number */ +#define DM1105_PIDN 0x10 + +/* Odd-even secret key select */ +#define DM1105_CWSEL 0x14 + +/* Host Command Interface */ +#define DM1105_HOST_CTR 0x18 +#define DM1105_HOST_AD 0x1c + +/* PCI Interface */ +#define DM1105_CR 0x30 +#define DM1105_RST 0x34 +#define DM1105_STADR 0x38 +#define DM1105_RLEN 0x3c +#define DM1105_WRP 0x40 +#define DM1105_INTCNT 0x44 +#define DM1105_INTMAK 0x48 +#define DM1105_INTSTS 0x4c + +/* CW Value */ +#define DM1105_ODD 0x50 +#define DM1105_EVEN 0x58 + +/* PID Value */ +#define DM1105_PID 0x60 + +/* IR Control */ +#define DM1105_IRCTR 0x64 +#define DM1105_IRMODE 0x68 +#define DM1105_SYSTEMCODE 0x6c +#define DM1105_IRCODE 0x70 + +/* Unknown Values */ +#define DM1105_ENCRYPT 0x74 +#define DM1105_VER 0x7c + +/* I2C Interface */ +#define DM1105_I2CCTR 0x80 +#define DM1105_I2CSTS 0x81 +#define DM1105_I2CDAT 0x82 +#define DM1105_I2C_RA 0x83 +/* ----------------------------------------------- */ +/* Interrupt Mask Bits */ + +#define INTMAK_TSIRQM 0x01 +#define INTMAK_HIRQM 0x04 +#define INTMAK_IRM 0x08 +#define INTMAK_ALLMASK (INTMAK_TSIRQM | \ + INTMAK_HIRQM | \ + INTMAK_IRM) +#define INTMAK_NONEMASK 0x00 + +/* Interrupt Status Bits */ +#define INTSTS_TSIRQ 0x01 +#define INTSTS_HIRQ 0x04 +#define INTSTS_IR 0x08 + +/* IR Control Bits */ +#define DM1105_IR_EN 0x01 +#define DM1105_SYS_CHK 0x02 +#define DM1105_REP_FLG 0x08 + +/* EEPROM addr */ +#define IIC_24C01_addr 0xa0 +/* Max board count */ +#define DM1105_MAX 0x04 + +#define DRIVER_NAME "dm1105" +#define DM1105_I2C_GPIO_NAME "dm1105-gpio" + +#define DM1105_DMA_PACKETS 47 +#define DM1105_DMA_PACKET_LENGTH (128*4) +#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) + +/* */ +#define GPIO08 (1 << 8) +#define GPIO13 (1 << 13) +#define GPIO14 (1 << 14) +#define GPIO15 (1 << 15) +#define GPIO16 (1 << 16) +#define GPIO17 (1 << 17) +#define GPIO_ALL 0x03ffff + +/* GPIO's for LNB power control */ +#define DM1105_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) +#define DM1105_LNB_OFF GPIO17 +#define DM1105_LNB_13V (GPIO16 | GPIO08) +#define DM1105_LNB_18V GPIO08 + +/* GPIO's for LNB power control for Axess DM05 */ +#define DM05_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) +#define DM05_LNB_OFF GPIO17/* actually 13v */ +#define DM05_LNB_13V GPIO17 +#define DM05_LNB_18V (GPIO17 | GPIO16) + +/* GPIO's for LNB power control for unbranded with I2C on GPIO */ +#define UNBR_LNB_MASK (GPIO17 | GPIO16) +#define UNBR_LNB_OFF 0 +#define UNBR_LNB_13V GPIO17 +#define UNBR_LNB_18V (GPIO17 | GPIO16) + +static unsigned int card[] = {[0 ... 3] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +static int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); + +static unsigned int dm1105_devcount; + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct dm1105_board { + char *name; + struct { + u32 mask, off, v13, v18; + } lnb; + u32 gpio_scl, gpio_sda; +}; + +struct dm1105_subid { + u16 subvendor; + u16 subdevice; + u32 card; +}; + +static const struct dm1105_board dm1105_boards[] = { + [DM1105_BOARD_UNKNOWN] = { + .name = "UNKNOWN/GENERIC", + .lnb = { + .mask = DM1105_LNB_MASK, + .off = DM1105_LNB_OFF, + .v13 = DM1105_LNB_13V, + .v18 = DM1105_LNB_18V, + }, + }, + [DM1105_BOARD_DVBWORLD_2002] = { + .name = "DVBWorld PCI 2002", + .lnb = { + .mask = DM1105_LNB_MASK, + .off = DM1105_LNB_OFF, + .v13 = DM1105_LNB_13V, + .v18 = DM1105_LNB_18V, + }, + }, + [DM1105_BOARD_DVBWORLD_2004] = { + .name = "DVBWorld PCI 2004", + .lnb = { + .mask = DM1105_LNB_MASK, + .off = DM1105_LNB_OFF, + .v13 = DM1105_LNB_13V, + .v18 = DM1105_LNB_18V, + }, + }, + [DM1105_BOARD_AXESS_DM05] = { + .name = "Axess/EasyTv DM05", + .lnb = { + .mask = DM05_LNB_MASK, + .off = DM05_LNB_OFF, + .v13 = DM05_LNB_13V, + .v18 = DM05_LNB_18V, + }, + }, + [DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = { + .name = "Unbranded DM1105 with i2c on GPIOs", + .lnb = { + .mask = UNBR_LNB_MASK, + .off = UNBR_LNB_OFF, + .v13 = UNBR_LNB_13V, + .v18 = UNBR_LNB_18V, + }, + .gpio_scl = GPIO14, + .gpio_sda = GPIO13, + }, +}; + +static const struct dm1105_subid dm1105_subids[] = { + { + .subvendor = 0x0000, + .subdevice = 0x2002, + .card = DM1105_BOARD_DVBWORLD_2002, + }, { + .subvendor = 0x0001, + .subdevice = 0x2002, + .card = DM1105_BOARD_DVBWORLD_2002, + }, { + .subvendor = 0x0000, + .subdevice = 0x2004, + .card = DM1105_BOARD_DVBWORLD_2004, + }, { + .subvendor = 0x0001, + .subdevice = 0x2004, + .card = DM1105_BOARD_DVBWORLD_2004, + }, { + .subvendor = 0x195d, + .subdevice = 0x1105, + .card = DM1105_BOARD_AXESS_DM05, + }, +}; + +static void dm1105_card_list(struct pci_dev *pci) +{ + int i; + + if (0 == pci->subsystem_vendor && + 0 == pci->subsystem_device) { + printk(KERN_ERR + "dm1105: Your board has no valid PCI Subsystem ID\n" + "dm1105: and thus can't be autodetected\n" + "dm1105: Please pass card= insmod option to\n" + "dm1105: workaround that. Redirect complaints to\n" + "dm1105: the vendor of the TV card. Best regards,\n" + "dm1105: -- tux\n"); + } else { + printk(KERN_ERR + "dm1105: Your board isn't known (yet) to the driver.\n" + "dm1105: You can try to pick one of the existing\n" + "dm1105: card configs via card= insmod option.\n" + "dm1105: Updating to the latest version might help\n" + "dm1105: as well.\n"); + } + printk(KERN_ERR "Here is a list of valid choices for the card= " + "insmod option:\n"); + for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++) + printk(KERN_ERR "dm1105: card=%d -> %s\n", + i, dm1105_boards[i].name); +} + +/* infrared remote control */ +struct infrared { + struct rc_dev *dev; + char input_phys[32]; + struct work_struct work; + u32 ir_command; +}; + +struct dm1105_dev { + /* pci */ + struct pci_dev *pdev; + u8 __iomem *io_mem; + + /* ir */ + struct infrared ir; + + /* dvb */ + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + struct dmxdev dmxdev; + struct dvb_adapter dvb_adapter; + struct dvb_demux demux; + struct dvb_frontend *fe; + struct dvb_net dvbnet; + unsigned int full_ts_users; + unsigned int boardnr; + int nr; + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_adapter i2c_bb_adap; + struct i2c_algo_bit_data i2c_bit; + + /* irq */ + struct work_struct work; + struct workqueue_struct *wq; + char wqn[16]; + + /* dma */ + dma_addr_t dma_addr; + unsigned char *ts_buf; + u32 wrp; + u32 nextwrp; + u32 buffer_size; + unsigned int PacketErrorCount; + unsigned int dmarst; + spinlock_t lock; +}; + +#define dm_io_mem(reg) ((unsigned long)(&dev->io_mem[reg])) + +#define dm_readb(reg) inb(dm_io_mem(reg)) +#define dm_writeb(reg, value) outb((value), (dm_io_mem(reg))) + +#define dm_readw(reg) inw(dm_io_mem(reg)) +#define dm_writew(reg, value) outw((value), (dm_io_mem(reg))) + +#define dm_readl(reg) inl(dm_io_mem(reg)) +#define dm_writel(reg, value) outl((value), (dm_io_mem(reg))) + +#define dm_andorl(reg, mask, value) \ + outl((inl(dm_io_mem(reg)) & ~(mask)) |\ + ((value) & (mask)), (dm_io_mem(reg))) + +#define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) +#define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) + +/* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines, + so we can use only 3 GPIO's from GPIO15 to GPIO17. + Here I don't check whether HOST is enebled as it is not implemented yet. + */ +static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff); + +} + +static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff); + +} + +static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val); + +} + +static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff; + + return 0; +} + +static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if ((mask & 0x0003ffff) && asoutput) + dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff); + else if ((mask & 0x0003ffff) && !asoutput) + dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff); + +} + +static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state) +{ + if (state) + dm1105_gpio_enable(dev, line, 0); + else { + dm1105_gpio_enable(dev, line, 1); + dm1105_gpio_clear(dev, line); + } +} + +static void dm1105_setsda(void *data, int state) +{ + struct dm1105_dev *dev = data; + + dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state); +} + +static void dm1105_setscl(void *data, int state) +{ + struct dm1105_dev *dev = data; + + dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state); +} + +static int dm1105_getsda(void *data) +{ + struct dm1105_dev *dev = data; + + return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda) + ? 1 : 0; +} + +static int dm1105_getscl(void *data) +{ + struct dm1105_dev *dev = data; + + return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl) + ? 1 : 0; +} + +static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct dm1105_dev *dev ; + + int addr, rc, i, j, k, len, byte, data; + u8 status; + + dev = i2c_adap->algo_data; + for (i = 0; i < num; i++) { + dm_writeb(DM1105_I2CCTR, 0x00); + if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + addr = msgs[i].addr << 1; + addr |= 1; + dm_writeb(DM1105_I2CDAT, addr); + for (byte = 0; byte < msgs[i].len; byte++) + dm_writeb(DM1105_I2CDAT + byte + 1, 0); + + dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); + for (j = 0; j < 55; j++) { + mdelay(10); + status = dm_readb(DM1105_I2CSTS); + if ((status & 0xc0) == 0x40) + break; + } + if (j >= 55) + return -1; + + for (byte = 0; byte < msgs[i].len; byte++) { + rc = dm_readb(DM1105_I2CDAT + byte + 1); + if (rc < 0) + goto err; + msgs[i].buf[byte] = rc; + } + } else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { + /* prepaired for cx24116 firmware */ + /* Write in small blocks */ + len = msgs[i].len - 1; + k = 1; + do { + dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); + dm_writeb(DM1105_I2CDAT + 1, 0xf7); + for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { + data = msgs[i].buf[k + byte]; + dm_writeb(DM1105_I2CDAT + byte + 2, data); + } + dm_writeb(DM1105_I2CCTR, 0x82 + (len > 48 ? 48 : len)); + for (j = 0; j < 25; j++) { + mdelay(10); + status = dm_readb(DM1105_I2CSTS); + if ((status & 0xc0) == 0x40) + break; + } + + if (j >= 25) + return -1; + + k += 48; + len -= 48; + } while (len > 0); + } else { + /* write bytes */ + dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); + for (byte = 0; byte < msgs[i].len; byte++) { + data = msgs[i].buf[byte]; + dm_writeb(DM1105_I2CDAT + byte + 1, data); + } + dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); + for (j = 0; j < 25; j++) { + mdelay(10); + status = dm_readb(DM1105_I2CSTS); + if ((status & 0xc0) == 0x40) + break; + } + + if (j >= 25) + return -1; + } + } + return num; + err: + return rc; +} + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dm1105_algo = { + .master_xfer = dm1105_i2c_xfer, + .functionality = functionality, +}; + +static inline struct dm1105_dev *feed_to_dm1105_dev(struct dvb_demux_feed *feed) +{ + return container_of(feed->demux, struct dm1105_dev, demux); +} + +static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) +{ + return container_of(fe->dvb, struct dm1105_dev, dvb_adapter); +} + +static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); + + dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1); + if (voltage == SEC_VOLTAGE_18) + dm1105_gpio_andor(dev, + dm1105_boards[dev->boardnr].lnb.mask, + dm1105_boards[dev->boardnr].lnb.v18); + else if (voltage == SEC_VOLTAGE_13) + dm1105_gpio_andor(dev, + dm1105_boards[dev->boardnr].lnb.mask, + dm1105_boards[dev->boardnr].lnb.v13); + else + dm1105_gpio_andor(dev, + dm1105_boards[dev->boardnr].lnb.mask, + dm1105_boards[dev->boardnr].lnb.off); + + return 0; +} + +static void dm1105_set_dma_addr(struct dm1105_dev *dev) +{ + dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); +} + +static int __devinit dm1105_dma_map(struct dm1105_dev *dev) +{ + dev->ts_buf = pci_alloc_consistent(dev->pdev, + 6 * DM1105_DMA_BYTES, + &dev->dma_addr); + + return !dev->ts_buf; +} + +static void dm1105_dma_unmap(struct dm1105_dev *dev) +{ + pci_free_consistent(dev->pdev, + 6 * DM1105_DMA_BYTES, + dev->ts_buf, + dev->dma_addr); +} + +static void dm1105_enable_irqs(struct dm1105_dev *dev) +{ + dm_writeb(DM1105_INTMAK, INTMAK_ALLMASK); + dm_writeb(DM1105_CR, 1); +} + +static void dm1105_disable_irqs(struct dm1105_dev *dev) +{ + dm_writeb(DM1105_INTMAK, INTMAK_IRM); + dm_writeb(DM1105_CR, 0); +} + +static int dm1105_start_feed(struct dvb_demux_feed *f) +{ + struct dm1105_dev *dev = feed_to_dm1105_dev(f); + + if (dev->full_ts_users++ == 0) + dm1105_enable_irqs(dev); + + return 0; +} + +static int dm1105_stop_feed(struct dvb_demux_feed *f) +{ + struct dm1105_dev *dev = feed_to_dm1105_dev(f); + + if (--dev->full_ts_users == 0) + dm1105_disable_irqs(dev); + + return 0; +} + +/* ir work handler */ +static void dm1105_emit_key(struct work_struct *work) +{ + struct infrared *ir = container_of(work, struct infrared, work); + u32 ircom = ir->ir_command; + u8 data; + + if (ir_debug) + printk(KERN_INFO "%s: received byte 0x%04x\n", __func__, ircom); + + data = (ircom >> 8) & 0x7f; + + rc_keydown(ir->dev, data, 0); +} + +/* work handler */ +static void dm1105_dmx_buffer(struct work_struct *work) +{ + struct dm1105_dev *dev = container_of(work, struct dm1105_dev, work); + unsigned int nbpackets; + u32 oldwrp = dev->wrp; + u32 nextwrp = dev->nextwrp; + + if (!((dev->ts_buf[oldwrp] == 0x47) && + (dev->ts_buf[oldwrp + 188] == 0x47) && + (dev->ts_buf[oldwrp + 188 * 2] == 0x47))) { + dev->PacketErrorCount++; + /* bad packet found */ + if ((dev->PacketErrorCount >= 2) && + (dev->dmarst == 0)) { + dm_writeb(DM1105_RST, 1); + dev->wrp = 0; + dev->PacketErrorCount = 0; + dev->dmarst = 0; + return; + } + } + + if (nextwrp < oldwrp) { + memcpy(dev->ts_buf + dev->buffer_size, dev->ts_buf, nextwrp); + nbpackets = ((dev->buffer_size - oldwrp) + nextwrp) / 188; + } else + nbpackets = (nextwrp - oldwrp) / 188; + + dev->wrp = nextwrp; + dvb_dmx_swfilter_packets(&dev->demux, &dev->ts_buf[oldwrp], nbpackets); +} + +static irqreturn_t dm1105_irq(int irq, void *dev_id) +{ + struct dm1105_dev *dev = dev_id; + + /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */ + unsigned int intsts = dm_readb(DM1105_INTSTS); + dm_writeb(DM1105_INTSTS, intsts); + + switch (intsts) { + case INTSTS_TSIRQ: + case (INTSTS_TSIRQ | INTSTS_IR): + dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR); + queue_work(dev->wq, &dev->work); + break; + case INTSTS_IR: + dev->ir.ir_command = dm_readl(DM1105_IRCODE); + schedule_work(&dev->ir.work); + break; + } + + return IRQ_HANDLED; +} + +int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) +{ + struct rc_dev *dev; + int err = -ENOMEM; + + dev = rc_allocate_device(); + if (!dev) + return -ENOMEM; + + snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), + "pci-%s/ir0", pci_name(dm1105->pdev)); + + dev->driver_name = MODULE_NAME; + dev->map_name = RC_MAP_DM1105_NEC; + dev->driver_type = RC_DRIVER_SCANCODE; + dev->input_name = "DVB on-card IR receiver"; + dev->input_phys = dm1105->ir.input_phys; + dev->input_id.bustype = BUS_PCI; + dev->input_id.version = 1; + if (dm1105->pdev->subsystem_vendor) { + dev->input_id.vendor = dm1105->pdev->subsystem_vendor; + dev->input_id.product = dm1105->pdev->subsystem_device; + } else { + dev->input_id.vendor = dm1105->pdev->vendor; + dev->input_id.product = dm1105->pdev->device; + } + dev->dev.parent = &dm1105->pdev->dev; + + INIT_WORK(&dm1105->ir.work, dm1105_emit_key); + + err = rc_register_device(dev); + if (err < 0) { + rc_free_device(dev); + return err; + } + + dm1105->ir.dev = dev; + return 0; +} + +void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105) +{ + rc_unregister_device(dm1105->ir.dev); +} + +static int __devinit dm1105_hw_init(struct dm1105_dev *dev) +{ + dm1105_disable_irqs(dev); + + dm_writeb(DM1105_HOST_CTR, 0); + + /*DATALEN 188,*/ + dm_writeb(DM1105_DTALENTH, 188); + /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/ + dm_writew(DM1105_TSCTR, 0xc10a); + + /* map DMA and set address */ + dm1105_dma_map(dev); + dm1105_set_dma_addr(dev); + /* big buffer */ + dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); + dm_writeb(DM1105_INTCNT, 47); + + /* IR NEC mode enable */ + dm_writeb(DM1105_IRCTR, (DM1105_IR_EN | DM1105_SYS_CHK)); + dm_writeb(DM1105_IRMODE, 0); + dm_writew(DM1105_SYSTEMCODE, 0); + + return 0; +} + +static void dm1105_hw_exit(struct dm1105_dev *dev) +{ + dm1105_disable_irqs(dev); + + /* IR disable */ + dm_writeb(DM1105_IRCTR, 0); + dm_writeb(DM1105_INTMAK, INTMAK_NONEMASK); + + dm1105_dma_unmap(dev); +} + +static struct stv0299_config sharp_z0194a_config = { + .demod_address = 0x68, + .inittab = sharp_z0194a_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = sharp_z0194a_set_symbol_rate, +}; + +static struct stv0288_config earda_config = { + .demod_address = 0x68, + .min_delay_ms = 100, +}; + +static struct si21xx_config serit_config = { + .demod_address = 0x68, + .min_delay_ms = 100, + +}; + +static struct cx24116_config serit_sp2633_config = { + .demod_address = 0x55, +}; + +static struct ds3000_config dvbworld_ds3000_config = { + .demod_address = 0x68, +}; + +static int __devinit frontend_init(struct dm1105_dev *dev) +{ + int ret; + + switch (dev->boardnr) { + case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO: + dm1105_gpio_enable(dev, GPIO15, 1); + dm1105_gpio_clear(dev, GPIO15); + msleep(100); + dm1105_gpio_set(dev, GPIO15); + msleep(200); + dev->fe = dvb_attach( + stv0299_attach, &sharp_z0194a_config, + &dev->i2c_bb_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(dvb_pll_attach, dev->fe, 0x60, + &dev->i2c_bb_adap, DVB_PLL_OPERA1); + break; + } + + dev->fe = dvb_attach( + stv0288_attach, &earda_config, + &dev->i2c_bb_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(stb6000_attach, dev->fe, 0x61, + &dev->i2c_bb_adap); + break; + } + + dev->fe = dvb_attach( + si21xx_attach, &serit_config, + &dev->i2c_bb_adap); + if (dev->fe) + dev->fe->ops.set_voltage = dm1105_set_voltage; + break; + case DM1105_BOARD_DVBWORLD_2004: + dev->fe = dvb_attach( + cx24116_attach, &serit_sp2633_config, + &dev->i2c_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + break; + } + + dev->fe = dvb_attach( + ds3000_attach, &dvbworld_ds3000_config, + &dev->i2c_adap); + if (dev->fe) + dev->fe->ops.set_voltage = dm1105_set_voltage; + + break; + case DM1105_BOARD_DVBWORLD_2002: + case DM1105_BOARD_AXESS_DM05: + default: + dev->fe = dvb_attach( + stv0299_attach, &sharp_z0194a_config, + &dev->i2c_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(dvb_pll_attach, dev->fe, 0x60, + &dev->i2c_adap, DVB_PLL_OPERA1); + break; + } + + dev->fe = dvb_attach( + stv0288_attach, &earda_config, + &dev->i2c_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(stb6000_attach, dev->fe, 0x61, + &dev->i2c_adap); + break; + } + + dev->fe = dvb_attach( + si21xx_attach, &serit_config, + &dev->i2c_adap); + if (dev->fe) + dev->fe->ops.set_voltage = dm1105_set_voltage; + + } + + if (!dev->fe) { + dev_err(&dev->pdev->dev, "could not attach frontend\n"); + return -ENODEV; + } + + ret = dvb_register_frontend(&dev->dvb_adapter, dev->fe); + if (ret < 0) { + if (dev->fe->ops.release) + dev->fe->ops.release(dev->fe); + dev->fe = NULL; + return ret; + } + + return 0; +} + +static void __devinit dm1105_read_mac(struct dm1105_dev *dev, u8 *mac) +{ + static u8 command[1] = { 0x28 }; + + struct i2c_msg msg[] = { + { + .addr = IIC_24C01_addr >> 1, + .flags = 0, + .buf = command, + .len = 1 + }, { + .addr = IIC_24C01_addr >> 1, + .flags = I2C_M_RD, + .buf = mac, + .len = 6 + }, + }; + + dm1105_i2c_xfer(&dev->i2c_adap, msg , 2); + dev_info(&dev->pdev->dev, "MAC %pM\n", mac); +} + +static int __devinit dm1105_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct dm1105_dev *dev; + struct dvb_adapter *dvb_adapter; + struct dvb_demux *dvbdemux; + struct dmx_demux *dmx; + int ret = -ENOMEM; + int i; + + dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* board config */ + dev->nr = dm1105_devcount; + dev->boardnr = UNSET; + if (card[dev->nr] < ARRAY_SIZE(dm1105_boards)) + dev->boardnr = card[dev->nr]; + for (i = 0; UNSET == dev->boardnr && + i < ARRAY_SIZE(dm1105_subids); i++) + if (pdev->subsystem_vendor == + dm1105_subids[i].subvendor && + pdev->subsystem_device == + dm1105_subids[i].subdevice) + dev->boardnr = dm1105_subids[i].card; + + if (UNSET == dev->boardnr) { + dev->boardnr = DM1105_BOARD_UNKNOWN; + dm1105_card_list(pdev); + } + + dm1105_devcount++; + dev->pdev = pdev; + dev->buffer_size = 5 * DM1105_DMA_BYTES; + dev->PacketErrorCount = 0; + dev->dmarst = 0; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto err_kfree; + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret < 0) + goto err_pci_disable_device; + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret < 0) + goto err_pci_disable_device; + + dev->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!dev->io_mem) { + ret = -EIO; + goto err_pci_release_regions; + } + + spin_lock_init(&dev->lock); + pci_set_drvdata(pdev, dev); + + ret = dm1105_hw_init(dev); + if (ret < 0) + goto err_pci_iounmap; + + /* i2c */ + i2c_set_adapdata(&dev->i2c_adap, dev); + strcpy(dev->i2c_adap.name, DRIVER_NAME); + dev->i2c_adap.owner = THIS_MODULE; + dev->i2c_adap.dev.parent = &pdev->dev; + dev->i2c_adap.algo = &dm1105_algo; + dev->i2c_adap.algo_data = dev; + ret = i2c_add_adapter(&dev->i2c_adap); + + if (ret < 0) + goto err_dm1105_hw_exit; + + i2c_set_adapdata(&dev->i2c_bb_adap, dev); + strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME); + dev->i2c_bb_adap.owner = THIS_MODULE; + dev->i2c_bb_adap.dev.parent = &pdev->dev; + dev->i2c_bb_adap.algo_data = &dev->i2c_bit; + dev->i2c_bit.data = dev; + dev->i2c_bit.setsda = dm1105_setsda; + dev->i2c_bit.setscl = dm1105_setscl; + dev->i2c_bit.getsda = dm1105_getsda; + dev->i2c_bit.getscl = dm1105_getscl; + dev->i2c_bit.udelay = 10; + dev->i2c_bit.timeout = 10; + + /* Raise SCL and SDA */ + dm1105_setsda(dev, 1); + dm1105_setscl(dev, 1); + + ret = i2c_bit_add_bus(&dev->i2c_bb_adap); + if (ret < 0) + goto err_i2c_del_adapter; + + /* dvb */ + ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, + THIS_MODULE, &pdev->dev, adapter_nr); + if (ret < 0) + goto err_i2c_del_adapters; + + dvb_adapter = &dev->dvb_adapter; + + dm1105_read_mac(dev, dvb_adapter->proposed_mac); + + dvbdemux = &dev->demux; + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = dm1105_start_feed; + dvbdemux->stop_feed = dm1105_stop_feed; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); + ret = dvb_dmx_init(dvbdemux); + if (ret < 0) + goto err_dvb_unregister_adapter; + + dmx = &dvbdemux->dmx; + dev->dmxdev.filternum = 256; + dev->dmxdev.demux = dmx; + dev->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dev->dmxdev, dvb_adapter); + if (ret < 0) + goto err_dvb_dmx_release; + + dev->hw_frontend.source = DMX_FRONTEND_0; + + ret = dmx->add_frontend(dmx, &dev->hw_frontend); + if (ret < 0) + goto err_dvb_dmxdev_release; + + dev->mem_frontend.source = DMX_MEMORY_FE; + + ret = dmx->add_frontend(dmx, &dev->mem_frontend); + if (ret < 0) + goto err_remove_hw_frontend; + + ret = dmx->connect_frontend(dmx, &dev->hw_frontend); + if (ret < 0) + goto err_remove_mem_frontend; + + ret = dvb_net_init(dvb_adapter, &dev->dvbnet, dmx); + if (ret < 0) + goto err_disconnect_frontend; + + ret = frontend_init(dev); + if (ret < 0) + goto err_dvb_net; + + dm1105_ir_init(dev); + + INIT_WORK(&dev->work, dm1105_dmx_buffer); + sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num); + dev->wq = create_singlethread_workqueue(dev->wqn); + if (!dev->wq) + goto err_dvb_net; + + ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED, + DRIVER_NAME, dev); + if (ret < 0) + goto err_workqueue; + + return 0; + +err_workqueue: + destroy_workqueue(dev->wq); +err_dvb_net: + dvb_net_release(&dev->dvbnet); +err_disconnect_frontend: + dmx->disconnect_frontend(dmx); +err_remove_mem_frontend: + dmx->remove_frontend(dmx, &dev->mem_frontend); +err_remove_hw_frontend: + dmx->remove_frontend(dmx, &dev->hw_frontend); +err_dvb_dmxdev_release: + dvb_dmxdev_release(&dev->dmxdev); +err_dvb_dmx_release: + dvb_dmx_release(dvbdemux); +err_dvb_unregister_adapter: + dvb_unregister_adapter(dvb_adapter); +err_i2c_del_adapters: + i2c_del_adapter(&dev->i2c_bb_adap); +err_i2c_del_adapter: + i2c_del_adapter(&dev->i2c_adap); +err_dm1105_hw_exit: + dm1105_hw_exit(dev); +err_pci_iounmap: + pci_iounmap(pdev, dev->io_mem); +err_pci_release_regions: + pci_release_regions(pdev); +err_pci_disable_device: + pci_disable_device(pdev); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(dev); + return ret; +} + +static void __devexit dm1105_remove(struct pci_dev *pdev) +{ + struct dm1105_dev *dev = pci_get_drvdata(pdev); + struct dvb_adapter *dvb_adapter = &dev->dvb_adapter; + struct dvb_demux *dvbdemux = &dev->demux; + struct dmx_demux *dmx = &dvbdemux->dmx; + + dm1105_ir_exit(dev); + dmx->close(dmx); + dvb_net_release(&dev->dvbnet); + if (dev->fe) + dvb_unregister_frontend(dev->fe); + + dmx->disconnect_frontend(dmx); + dmx->remove_frontend(dmx, &dev->mem_frontend); + dmx->remove_frontend(dmx, &dev->hw_frontend); + dvb_dmxdev_release(&dev->dmxdev); + dvb_dmx_release(dvbdemux); + dvb_unregister_adapter(dvb_adapter); + if (&dev->i2c_adap) + i2c_del_adapter(&dev->i2c_adap); + + dm1105_hw_exit(dev); + synchronize_irq(pdev->irq); + free_irq(pdev->irq, dev); + pci_iounmap(pdev, dev->io_mem); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + dm1105_devcount--; + kfree(dev); +} + +static struct pci_device_id dm1105_id_table[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_TRIGEM, + .device = PCI_DEVICE_ID_DM1105, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, { + .vendor = PCI_VENDOR_ID_AXESS, + .device = PCI_DEVICE_ID_DM05, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, { + /* empty */ + }, +}; + +MODULE_DEVICE_TABLE(pci, dm1105_id_table); + +static struct pci_driver dm1105_driver = { + .name = DRIVER_NAME, + .id_table = dm1105_id_table, + .probe = dm1105_probe, + .remove = __devexit_p(dm1105_remove), +}; + +static int __init dm1105_init(void) +{ + return pci_register_driver(&dm1105_driver); +} + +static void __exit dm1105_exit(void) +{ + pci_unregister_driver(&dm1105_driver); +} + +module_init(dm1105_init); +module_exit(dm1105_exit); + +MODULE_AUTHOR("Igor M. Liplianin "); +MODULE_DESCRIPTION("SDMC DM1105 DVB driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/mantis/Kconfig b/drivers/media/pci/mantis/Kconfig new file mode 100644 index 000000000000..a13a50503134 --- /dev/null +++ b/drivers/media/pci/mantis/Kconfig @@ -0,0 +1,38 @@ +config MANTIS_CORE + tristate "Mantis/Hopper PCI bridge based devices" + depends on PCI && I2C && INPUT && RC_CORE + + help + Support for PCI cards based on the Mantis and Hopper PCi bridge. + + Say Y if you own such a device and want to use it. + +config DVB_MANTIS + tristate "MANTIS based cards" + depends on MANTIS_CORE && DVB_CORE && PCI && I2C + select DVB_MB86A16 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_TDA665x if !DVB_FE_CUSTOMISE + select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_PLL + help + Support for PCI cards based on the Mantis PCI bridge. + Say Y when you have a Mantis based DVB card and want to use it. + + If unsure say N. + +config DVB_HOPPER + tristate "HOPPER based cards" + depends on MANTIS_CORE && DVB_CORE && PCI && I2C + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_PLL + help + Support for PCI cards based on the Hopper PCI bridge. + Say Y when you have a Hopper based DVB card and want to use it. + + If unsure say N diff --git a/drivers/media/pci/mantis/Makefile b/drivers/media/pci/mantis/Makefile new file mode 100644 index 000000000000..f715051e4453 --- /dev/null +++ b/drivers/media/pci/mantis/Makefile @@ -0,0 +1,28 @@ +mantis_core-objs := mantis_ioc.o \ + mantis_uart.o \ + mantis_dma.o \ + mantis_pci.o \ + mantis_i2c.o \ + mantis_dvb.o \ + mantis_evm.o \ + mantis_hif.o \ + mantis_ca.o \ + mantis_pcmcia.o \ + mantis_input.o + +mantis-objs := mantis_cards.o \ + mantis_vp1033.o \ + mantis_vp1034.o \ + mantis_vp1041.o \ + mantis_vp2033.o \ + mantis_vp2040.o \ + mantis_vp3030.o + +hopper-objs := hopper_cards.o \ + hopper_vp3028.o + +obj-$(CONFIG_MANTIS_CORE) += mantis_core.o +obj-$(CONFIG_DVB_MANTIS) += mantis.o +obj-$(CONFIG_DVB_HOPPER) += hopper.o + +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ diff --git a/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c new file mode 100644 index 000000000000..cc0251e01077 --- /dev/null +++ b/drivers/media/pci/mantis/hopper_cards.c @@ -0,0 +1,277 @@ +/* + Hopper PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "hopper_vp3028.h" +#include "mantis_dma.h" +#include "mantis_dvb.h" +#include "mantis_uart.h" +#include "mantis_ioc.h" +#include "mantis_pci.h" +#include "mantis_i2c.h" +#include "mantis_reg.h" + +static unsigned int verbose; +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); + +#define DRIVER_NAME "Hopper" + +static char *label[10] = { + "DMA", + "IRQ-0", + "IRQ-1", + "OCERR", + "PABRT", + "RIPRR", + "PPERR", + "FTRGT", + "RISCI", + "RACK" +}; + +static int devs; + +static irqreturn_t hopper_irq_handler(int irq, void *dev_id) +{ + u32 stat = 0, mask = 0; + u32 rst_stat = 0, rst_mask = 0; + + struct mantis_pci *mantis; + struct mantis_ca *ca; + + mantis = (struct mantis_pci *) dev_id; + if (unlikely(mantis == NULL)) { + dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); + return IRQ_NONE; + } + ca = mantis->mantis_ca; + + stat = mmread(MANTIS_INT_STAT); + mask = mmread(MANTIS_INT_MASK); + if (!(stat & mask)) + return IRQ_NONE; + + rst_mask = MANTIS_GPIF_WRACK | + MANTIS_GPIF_OTHERR | + MANTIS_SBUF_WSTO | + MANTIS_GPIF_EXTIRQ; + + rst_stat = mmread(MANTIS_GPIF_STATUS); + rst_stat &= rst_mask; + mmwrite(rst_stat, MANTIS_GPIF_STATUS); + + mantis->mantis_int_stat = stat; + mantis->mantis_int_mask = mask; + dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); + if (stat & MANTIS_INT_RISCEN) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); + } + if (stat & MANTIS_INT_IRQ0) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); + mantis->gpif_status = rst_stat; + wake_up(&ca->hif_write_wq); + schedule_work(&ca->hif_evm_work); + } + if (stat & MANTIS_INT_IRQ1) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); + schedule_work(&mantis->uart_work); + } + if (stat & MANTIS_INT_OCERR) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); + } + if (stat & MANTIS_INT_PABORT) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); + } + if (stat & MANTIS_INT_RIPERR) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); + } + if (stat & MANTIS_INT_PPERR) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); + } + if (stat & MANTIS_INT_FTRGT) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); + } + if (stat & MANTIS_INT_RISCI) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); + mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; + tasklet_schedule(&mantis->tasklet); + } + if (stat & MANTIS_INT_I2CDONE) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); + wake_up(&mantis->i2c_wq); + } + mmwrite(stat, MANTIS_INT_STAT); + stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | + MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | + MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | + MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | + MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | + MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | + MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | + MANTIS_INT_PABORT | MANTIS_INT_RIPERR | + MANTIS_INT_PPERR | MANTIS_INT_FTRGT | + MANTIS_INT_RISCI); + + if (stat) + dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); + + dprintk(MANTIS_DEBUG, 0, "\n"); + return IRQ_HANDLED; +} + +static int __devinit hopper_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +{ + struct mantis_pci *mantis; + struct mantis_hwconfig *config; + int err = 0; + + mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); + if (mantis == NULL) { + printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); + err = -ENOMEM; + goto fail0; + } + + mantis->num = devs; + mantis->verbose = verbose; + mantis->pdev = pdev; + config = (struct mantis_hwconfig *) pci_id->driver_data; + config->irq_handler = &hopper_irq_handler; + mantis->hwconfig = config; + + err = mantis_pci_init(mantis); + if (err) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); + goto fail1; + } + + err = mantis_stream_control(mantis, STREAM_TO_HIF); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); + goto fail1; + } + + err = mantis_i2c_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); + goto fail2; + } + + err = mantis_get_mac(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); + goto fail2; + } + + err = mantis_dma_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); + goto fail3; + } + + err = mantis_dvb_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); + goto fail4; + } + devs++; + + return err; + +fail4: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); + mantis_dma_exit(mantis); + +fail3: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); + mantis_i2c_exit(mantis); + +fail2: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); + mantis_pci_exit(mantis); + +fail1: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); + kfree(mantis); + +fail0: + return err; +} + +static void __devexit hopper_pci_remove(struct pci_dev *pdev) +{ + struct mantis_pci *mantis = pci_get_drvdata(pdev); + + if (mantis) { + mantis_dvb_exit(mantis); + mantis_dma_exit(mantis); + mantis_i2c_exit(mantis); + mantis_pci_exit(mantis); + kfree(mantis); + } + return; + +} + +static struct pci_device_id hopper_pci_table[] = { + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3028_DVB_T, &vp3028_config), + { } +}; + +MODULE_DEVICE_TABLE(pci, hopper_pci_table); + +static struct pci_driver hopper_pci_driver = { + .name = DRIVER_NAME, + .id_table = hopper_pci_table, + .probe = hopper_pci_probe, + .remove = hopper_pci_remove, +}; + +static int __devinit hopper_init(void) +{ + return pci_register_driver(&hopper_pci_driver); +} + +static void __devexit hopper_exit(void) +{ + return pci_unregister_driver(&hopper_pci_driver); +} + +module_init(hopper_init); +module_exit(hopper_exit); + +MODULE_DESCRIPTION("HOPPER driver"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/mantis/hopper_vp3028.c b/drivers/media/pci/mantis/hopper_vp3028.c new file mode 100644 index 000000000000..68a29f8bdf73 --- /dev/null +++ b/drivers/media/pci/mantis/hopper_vp3028.c @@ -0,0 +1,88 @@ +/* + Hopper VP-3028 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "zl10353.h" +#include "mantis_common.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" +#include "hopper_vp3028.h" + +struct zl10353_config hopper_vp3028_config = { + .demod_address = 0x0f, +}; + +#define MANTIS_MODEL_NAME "VP-3028" +#define MANTIS_DEV_TYPE "DVB-T" + +static int vp3028_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +{ + struct i2c_adapter *adapter = &mantis->adapter; + struct mantis_hwconfig *config = mantis->hwconfig; + int err = 0; + + mantis_gpio_set_bits(mantis, config->reset, 0); + msleep(100); + err = mantis_frontend_power(mantis, POWER_ON); + msleep(100); + mantis_gpio_set_bits(mantis, config->reset, 1); + + err = mantis_frontend_power(mantis, POWER_ON); + if (err == 0) { + msleep(250); + dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); + fe = dvb_attach(zl10353_attach, &hopper_vp3028_config, adapter); + + if (!fe) + return -1; + } else { + dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", + adapter->name, + err); + + return -EIO; + } + dprintk(MANTIS_ERROR, 1, "Done!"); + + return 0; +} + +struct mantis_hwconfig vp3028_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_188, + + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, + + .frontend_init = vp3028_frontend_init, + .power = GPIF_A00, + .reset = GPIF_A03, +}; diff --git a/drivers/media/pci/mantis/hopper_vp3028.h b/drivers/media/pci/mantis/hopper_vp3028.h new file mode 100644 index 000000000000..57239498bc87 --- /dev/null +++ b/drivers/media/pci/mantis/hopper_vp3028.h @@ -0,0 +1,30 @@ +/* + Hopper VP-3028 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP3028_H +#define __MANTIS_VP3028_H + +#include "mantis_common.h" + +#define MANTIS_VP_3028_DVB_T 0x0028 + +extern struct mantis_hwconfig vp3028_config; + +#endif /* __MANTIS_VP3028_H */ diff --git a/drivers/media/pci/mantis/mantis_ca.c b/drivers/media/pci/mantis/mantis_ca.c new file mode 100644 index 000000000000..3d7046909009 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_ca.c @@ -0,0 +1,209 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_link.h" +#include "mantis_hif.h" +#include "mantis_reg.h" + +#include "mantis_ca.h" + +static int mantis_ca_read_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Read", slot); + + if (slot != 0) + return -EINVAL; + + return mantis_hif_read_mem(ca, addr); +} + +static int mantis_ca_write_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr, u8 data) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Write", slot); + + if (slot != 0) + return -EINVAL; + + return mantis_hif_write_mem(ca, addr, data); +} + +static int mantis_ca_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Read", slot); + + if (slot != 0) + return -EINVAL; + + return mantis_hif_read_iom(ca, addr); +} + +static int mantis_ca_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr, u8 data) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Write", slot); + + if (slot != 0) + return -EINVAL; + + return mantis_hif_write_iom(ca, addr, data); +} + +static int mantis_ca_slot_reset(struct dvb_ca_en50221 *en50221, int slot) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot RESET", slot); + udelay(500); /* Wait.. */ + mmwrite(0xda, MANTIS_PCMCIA_RESET); /* Leading edge assert */ + udelay(500); + mmwrite(0x00, MANTIS_PCMCIA_RESET); /* Trailing edge deassert */ + msleep(1000); + dvb_ca_en50221_camready_irq(&ca->en50221, 0); + + return 0; +} + +static int mantis_ca_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot shutdown", slot); + + return 0; +} + +static int mantis_ts_control(struct dvb_ca_en50221 *en50221, int slot) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): TS control", slot); +/* mantis_set_direction(mantis, 1); */ /* Enable TS through CAM */ + + return 0; +} + +static int mantis_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open) +{ + struct mantis_ca *ca = en50221->data; + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Slot(%d): Poll Slot status", slot); + + if (ca->slot_state == MODULE_INSERTED) { + dprintk(MANTIS_DEBUG, 1, "CA Module present and ready"); + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } else { + dprintk(MANTIS_DEBUG, 1, "CA Module not present or not ready"); + } + + return 0; +} + +int mantis_ca_init(struct mantis_pci *mantis) +{ + struct dvb_adapter *dvb_adapter = &mantis->dvb_adapter; + struct mantis_ca *ca; + int ca_flags = 0, result; + + dprintk(MANTIS_DEBUG, 1, "Initializing Mantis CA"); + ca = kzalloc(sizeof(struct mantis_ca), GFP_KERNEL); + if (!ca) { + dprintk(MANTIS_ERROR, 1, "Out of memory!, exiting .."); + result = -ENOMEM; + goto err; + } + + ca->ca_priv = mantis; + mantis->mantis_ca = ca; + ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE; + /* register CA interface */ + ca->en50221.owner = THIS_MODULE; + ca->en50221.read_attribute_mem = mantis_ca_read_attr_mem; + ca->en50221.write_attribute_mem = mantis_ca_write_attr_mem; + ca->en50221.read_cam_control = mantis_ca_read_cam_ctl; + ca->en50221.write_cam_control = mantis_ca_write_cam_ctl; + ca->en50221.slot_reset = mantis_ca_slot_reset; + ca->en50221.slot_shutdown = mantis_ca_slot_shutdown; + ca->en50221.slot_ts_enable = mantis_ts_control; + ca->en50221.poll_slot_status = mantis_slot_status; + ca->en50221.data = ca; + + mutex_init(&ca->ca_lock); + + init_waitqueue_head(&ca->hif_data_wq); + init_waitqueue_head(&ca->hif_opdone_wq); + init_waitqueue_head(&ca->hif_write_wq); + + dprintk(MANTIS_ERROR, 1, "Registering EN50221 device"); + result = dvb_ca_en50221_init(dvb_adapter, &ca->en50221, ca_flags, 1); + if (result != 0) { + dprintk(MANTIS_ERROR, 1, "EN50221: Initialization failed <%d>", result); + goto err; + } + dprintk(MANTIS_ERROR, 1, "Registered EN50221 device"); + mantis_evmgr_init(ca); + return 0; +err: + kfree(ca); + return result; +} +EXPORT_SYMBOL_GPL(mantis_ca_init); + +void mantis_ca_exit(struct mantis_pci *mantis) +{ + struct mantis_ca *ca = mantis->mantis_ca; + + dprintk(MANTIS_DEBUG, 1, "Mantis CA exit"); + + mantis_evmgr_exit(ca); + dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device"); + if (ca) + dvb_ca_en50221_release(&ca->en50221); + + kfree(ca); +} +EXPORT_SYMBOL_GPL(mantis_ca_exit); diff --git a/drivers/media/pci/mantis/mantis_ca.h b/drivers/media/pci/mantis/mantis_ca.h new file mode 100644 index 000000000000..dc63e55f7eca --- /dev/null +++ b/drivers/media/pci/mantis/mantis_ca.h @@ -0,0 +1,27 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_CA_H +#define __MANTIS_CA_H + +extern int mantis_ca_init(struct mantis_pci *mantis); +extern void mantis_ca_exit(struct mantis_pci *mantis); + +#endif /* __MANTIS_CA_H */ diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c new file mode 100644 index 000000000000..0207d1f064e0 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_cards.c @@ -0,0 +1,307 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" + +#include "mantis_vp1033.h" +#include "mantis_vp1034.h" +#include "mantis_vp1041.h" +#include "mantis_vp2033.h" +#include "mantis_vp2040.h" +#include "mantis_vp3030.h" + +#include "mantis_dma.h" +#include "mantis_ca.h" +#include "mantis_dvb.h" +#include "mantis_uart.h" +#include "mantis_ioc.h" +#include "mantis_pci.h" +#include "mantis_i2c.h" +#include "mantis_reg.h" + +static unsigned int verbose; +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); + +static int devs; + +#define DRIVER_NAME "Mantis" + +static char *label[10] = { + "DMA", + "IRQ-0", + "IRQ-1", + "OCERR", + "PABRT", + "RIPRR", + "PPERR", + "FTRGT", + "RISCI", + "RACK" +}; + +static irqreturn_t mantis_irq_handler(int irq, void *dev_id) +{ + u32 stat = 0, mask = 0; + u32 rst_stat = 0, rst_mask = 0; + + struct mantis_pci *mantis; + struct mantis_ca *ca; + + mantis = (struct mantis_pci *) dev_id; + if (unlikely(mantis == NULL)) { + dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); + return IRQ_NONE; + } + ca = mantis->mantis_ca; + + stat = mmread(MANTIS_INT_STAT); + mask = mmread(MANTIS_INT_MASK); + if (!(stat & mask)) + return IRQ_NONE; + + rst_mask = MANTIS_GPIF_WRACK | + MANTIS_GPIF_OTHERR | + MANTIS_SBUF_WSTO | + MANTIS_GPIF_EXTIRQ; + + rst_stat = mmread(MANTIS_GPIF_STATUS); + rst_stat &= rst_mask; + mmwrite(rst_stat, MANTIS_GPIF_STATUS); + + mantis->mantis_int_stat = stat; + mantis->mantis_int_mask = mask; + dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); + if (stat & MANTIS_INT_RISCEN) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); + } + if (stat & MANTIS_INT_IRQ0) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); + mantis->gpif_status = rst_stat; + wake_up(&ca->hif_write_wq); + schedule_work(&ca->hif_evm_work); + } + if (stat & MANTIS_INT_IRQ1) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); + schedule_work(&mantis->uart_work); + } + if (stat & MANTIS_INT_OCERR) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); + } + if (stat & MANTIS_INT_PABORT) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); + } + if (stat & MANTIS_INT_RIPERR) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); + } + if (stat & MANTIS_INT_PPERR) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); + } + if (stat & MANTIS_INT_FTRGT) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); + } + if (stat & MANTIS_INT_RISCI) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); + mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; + tasklet_schedule(&mantis->tasklet); + } + if (stat & MANTIS_INT_I2CDONE) { + dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); + wake_up(&mantis->i2c_wq); + } + mmwrite(stat, MANTIS_INT_STAT); + stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | + MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | + MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | + MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | + MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | + MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | + MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | + MANTIS_INT_PABORT | MANTIS_INT_RIPERR | + MANTIS_INT_PPERR | MANTIS_INT_FTRGT | + MANTIS_INT_RISCI); + + if (stat) + dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); + + dprintk(MANTIS_DEBUG, 0, "\n"); + return IRQ_HANDLED; +} + +static int __devinit mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +{ + struct mantis_pci *mantis; + struct mantis_hwconfig *config; + int err = 0; + + mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); + if (mantis == NULL) { + printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); + err = -ENOMEM; + goto fail0; + } + + mantis->num = devs; + mantis->verbose = verbose; + mantis->pdev = pdev; + config = (struct mantis_hwconfig *) pci_id->driver_data; + config->irq_handler = &mantis_irq_handler; + mantis->hwconfig = config; + + err = mantis_pci_init(mantis); + if (err) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); + goto fail1; + } + + err = mantis_stream_control(mantis, STREAM_TO_HIF); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); + goto fail1; + } + + err = mantis_i2c_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); + goto fail2; + } + + err = mantis_get_mac(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); + goto fail2; + } + + err = mantis_dma_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); + goto fail3; + } + + err = mantis_dvb_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); + goto fail4; + } + err = mantis_uart_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err); + goto fail6; + } + + devs++; + + return err; + + + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err); + mantis_uart_exit(mantis); + +fail6: +fail4: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); + mantis_dma_exit(mantis); + +fail3: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); + mantis_i2c_exit(mantis); + +fail2: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); + mantis_pci_exit(mantis); + +fail1: + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); + kfree(mantis); + +fail0: + return err; +} + +static void __devexit mantis_pci_remove(struct pci_dev *pdev) +{ + struct mantis_pci *mantis = pci_get_drvdata(pdev); + + if (mantis) { + + mantis_uart_exit(mantis); + mantis_dvb_exit(mantis); + mantis_dma_exit(mantis); + mantis_i2c_exit(mantis); + mantis_pci_exit(mantis); + kfree(mantis); + } + return; +} + +static struct pci_device_id mantis_pci_table[] = { + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config), + MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config), + MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config), + MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config), + MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config), + MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2040_config), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config), + { } +}; + +MODULE_DEVICE_TABLE(pci, mantis_pci_table); + +static struct pci_driver mantis_pci_driver = { + .name = DRIVER_NAME, + .id_table = mantis_pci_table, + .probe = mantis_pci_probe, + .remove = mantis_pci_remove, +}; + +static int __devinit mantis_init(void) +{ + return pci_register_driver(&mantis_pci_driver); +} + +static void __devexit mantis_exit(void) +{ + return pci_unregister_driver(&mantis_pci_driver); +} + +module_init(mantis_init); +module_exit(mantis_exit); + +MODULE_DESCRIPTION("MANTIS driver"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h new file mode 100644 index 000000000000..f2410cf0a6bf --- /dev/null +++ b/drivers/media/pci/mantis/mantis_common.h @@ -0,0 +1,179 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_COMMON_H +#define __MANTIS_COMMON_H + +#include +#include +#include + +#include "mantis_uart.h" + +#include "mantis_link.h" + +#define MANTIS_ERROR 0 +#define MANTIS_NOTICE 1 +#define MANTIS_INFO 2 +#define MANTIS_DEBUG 3 +#define MANTIS_TMG 9 + +#define dprintk(y, z, format, arg...) do { \ + if (z) { \ + if ((mantis->verbose > MANTIS_ERROR) && (mantis->verbose > y)) \ + printk(KERN_ERR "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ + else if ((mantis->verbose > MANTIS_NOTICE) && (mantis->verbose > y)) \ + printk(KERN_NOTICE "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ + else if ((mantis->verbose > MANTIS_INFO) && (mantis->verbose > y)) \ + printk(KERN_INFO "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ + else if ((mantis->verbose > MANTIS_DEBUG) && (mantis->verbose > y)) \ + printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ + else if ((mantis->verbose > MANTIS_TMG) && (mantis->verbose > y)) \ + printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ + } else { \ + if (mantis->verbose > y) \ + printk(format , ##arg); \ + } \ +} while(0) + +#define mwrite(dat, addr) writel((dat), addr) +#define mread(addr) readl(addr) + +#define mmwrite(dat, addr) mwrite((dat), (mantis->mmio + (addr))) +#define mmread(addr) mread(mantis->mmio + (addr)) + +#define MANTIS_TS_188 0 +#define MANTIS_TS_204 1 + +#define TWINHAN_TECHNOLOGIES 0x1822 +#define MANTIS 0x4e35 + +#define TECHNISAT 0x1ae4 +#define TERRATEC 0x153b + +#define MAKE_ENTRY(__subven, __subdev, __configptr) { \ + .vendor = TWINHAN_TECHNOLOGIES, \ + .device = MANTIS, \ + .subvendor = (__subven), \ + .subdevice = (__subdev), \ + .driver_data = (unsigned long) (__configptr) \ +} + +enum mantis_i2c_mode { + MANTIS_PAGE_MODE = 0, + MANTIS_BYTE_MODE, +}; + +struct mantis_pci; + +struct mantis_hwconfig { + char *model_name; + char *dev_type; + u32 ts_size; + + enum mantis_baud baud_rate; + enum mantis_parity parity; + u32 bytes; + + irqreturn_t (*irq_handler)(int irq, void *dev_id); + int (*frontend_init)(struct mantis_pci *mantis, struct dvb_frontend *fe); + + u8 power; + u8 reset; + + enum mantis_i2c_mode i2c_mode; +}; + +struct mantis_pci { + unsigned int verbose; + + /* PCI stuff */ + u16 vendor_id; + u16 device_id; + u16 subsystem_vendor; + u16 subsystem_device; + + u8 latency; + + struct pci_dev *pdev; + + unsigned long mantis_addr; + void __iomem *mmio; + + u8 irq; + u8 revision; + + unsigned int num; + + /* RISC Core */ + u32 busy_block; + u32 last_block; + u8 *buf_cpu; + dma_addr_t buf_dma; + u32 *risc_cpu; + dma_addr_t risc_dma; + + struct tasklet_struct tasklet; + + struct i2c_adapter adapter; + int i2c_rc; + wait_queue_head_t i2c_wq; + struct mutex i2c_lock; + + /* DVB stuff */ + struct dvb_adapter dvb_adapter; + struct dvb_frontend *fe; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net dvbnet; + + u8 feeds; + + struct mantis_hwconfig *hwconfig; + + u32 mantis_int_stat; + u32 mantis_int_mask; + + /* board specific */ + u8 mac_address[8]; + u32 sub_vendor_id; + u32 sub_device_id; + + /* A12 A13 A14 */ + u32 gpio_status; + + u32 gpif_status; + + struct mantis_ca *mantis_ca; + + wait_queue_head_t uart_wq; + struct work_struct uart_work; + spinlock_t uart_lock; + + struct rc_dev *rc; + char input_name[80]; + char input_phys[80]; +}; + +#define MANTIS_HIF_STATUS (mantis->gpio_status) + +#endif /* __MANTIS_COMMON_H */ diff --git a/drivers/media/pci/mantis/mantis_core.c b/drivers/media/pci/mantis/mantis_core.c new file mode 100644 index 000000000000..684d9061fe2a --- /dev/null +++ b/drivers/media/pci/mantis/mantis_core.c @@ -0,0 +1,235 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "mantis_common.h" +#include "mantis_core.h" +#include "mantis_vp1033.h" +#include "mantis_vp1034.h" +#include "mantis_vp1041.h" +#include "mantis_vp2033.h" +#include "mantis_vp2040.h" +#include "mantis_vp3030.h" + +static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) +{ + int err; + struct i2c_msg msg[] = { + { + .addr = 0x50, + .flags = 0, + .buf = data, + .len = 1 + }, { + .addr = 0x50, + .flags = I2C_M_RD, + .buf = data, + .len = length + }, + }; + + err = i2c_transfer(&mantis->adapter, msg, 2); + if (err < 0) { + dprintk(verbose, MANTIS_ERROR, 1, + "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", + err, data[0], data[1]); + + return err; + } + + return 0; +} + +static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) +{ + int err; + + struct i2c_msg msg = { + .addr = 0x50, + .flags = 0, + .buf = data, + .len = length + }; + + err = i2c_transfer(&mantis->adapter, &msg, 1); + if (err < 0) { + dprintk(verbose, MANTIS_ERROR, 1, + "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >", + err, length, data[0], data[1]); + + return err; + } + + return 0; +} + +static int get_mac_address(struct mantis_pci *mantis) +{ + int err; + + mantis->mac_address[0] = 0x08; + err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6); + if (err < 0) { + dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error"); + + return err; + } + dprintk(verbose, MANTIS_ERROR, 0, + " MAC Address=[%pM]\n", mantis->mac_address); + + return 0; +} + +#define MANTIS_MODEL_UNKNOWN "UNKNOWN" +#define MANTIS_DEV_UNKNOWN "UNKNOWN" + +struct mantis_hwconfig unknown_device = { + .model_name = MANTIS_MODEL_UNKNOWN, + .dev_type = MANTIS_DEV_UNKNOWN, +}; + +static void mantis_load_config(struct mantis_pci *mantis) +{ + switch (mantis->subsystem_device) { + case MANTIS_VP_1033_DVB_S: /* VP-1033 */ + mantis->hwconfig = &vp1033_mantis_config; + break; + case MANTIS_VP_1034_DVB_S: /* VP-1034 */ + mantis->hwconfig = &vp1034_mantis_config; + break; + case MANTIS_VP_1041_DVB_S2: /* VP-1041 */ + case TECHNISAT_SKYSTAR_HD2: + mantis->hwconfig = &vp1041_mantis_config; + break; + case MANTIS_VP_2033_DVB_C: /* VP-2033 */ + mantis->hwconfig = &vp2033_mantis_config; + break; + case MANTIS_VP_2040_DVB_C: /* VP-2040 */ + case CINERGY_C: /* VP-2040 clone */ + case TECHNISAT_CABLESTAR_HD2: + mantis->hwconfig = &vp2040_mantis_config; + break; + case MANTIS_VP_3030_DVB_T: /* VP-3030 */ + mantis->hwconfig = &vp3030_mantis_config; + break; + default: + mantis->hwconfig = &unknown_device; + break; + } +} + +int mantis_core_init(struct mantis_pci *mantis) +{ + int err = 0; + + mantis_load_config(mantis); + dprintk(verbose, MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", + mantis->hwconfig->model_name, mantis->hwconfig->dev_type, + mantis->pdev->bus->number, PCI_SLOT(mantis->pdev->devfn), PCI_FUNC(mantis->pdev->devfn)); + dprintk(verbose, MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", + mantis->revision, + mantis->subsystem_vendor, mantis->subsystem_device); + dprintk(verbose, MANTIS_ERROR, 0, + "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", + mantis->pdev->irq, mantis->latency, + mantis->mantis_addr, mantis->mantis_mmio); + + err = mantis_i2c_init(mantis); + if (err < 0) { + dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed"); + return err; + } + err = get_mac_address(mantis); + if (err < 0) { + dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed"); + return err; + } + err = mantis_dma_init(mantis); + if (err < 0) { + dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed"); + return err; + } + err = mantis_dvb_init(mantis); + if (err < 0) { + dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed"); + return err; + } + err = mantis_uart_init(mantis); + if (err < 0) { + dprintk(verbose, MANTIS_DEBUG, 1, "Mantis UART init failed"); + return err; + } + + return 0; +} + +int mantis_core_exit(struct mantis_pci *mantis) +{ + mantis_dma_stop(mantis); + dprintk(verbose, MANTIS_ERROR, 1, "DMA engine stopping"); + + mantis_uart_exit(mantis); + dprintk(verbose, MANTIS_ERROR, 1, "UART exit failed"); + + if (mantis_dma_exit(mantis) < 0) + dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed"); + if (mantis_dvb_exit(mantis) < 0) + dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed"); + if (mantis_i2c_exit(mantis) < 0) + dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed"); + + return 0; +} + +/* Turn the given bit on or off. */ +void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) +{ + u32 cur; + + cur = mmread(MANTIS_GPIF_ADDR); + if (value) + mantis->gpio_status = cur | (1 << bitpos); + else + mantis->gpio_status = cur & (~(1 << bitpos)); + + mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); + mmwrite(0x00, MANTIS_GPIF_DOUT); + udelay(100); +} + +/* direction = 0 , no CI passthrough ; 1 , CI passthrough */ +void mantis_set_direction(struct mantis_pci *mantis, int direction) +{ + u32 reg; + + reg = mmread(0x28); + dprintk(verbose, MANTIS_DEBUG, 1, "TS direction setup"); + if (direction == 0x01) { + /* to CI */ + reg |= 0x04; + mmwrite(reg, 0x28); + reg &= 0xff - 0x04; + mmwrite(reg, 0x28); + } else { + reg &= 0xff - 0x04; + mmwrite(reg, 0x28); + reg |= 0x04; + mmwrite(reg, 0x28); + } +} diff --git a/drivers/media/pci/mantis/mantis_core.h b/drivers/media/pci/mantis/mantis_core.h new file mode 100644 index 000000000000..833ee42e694e --- /dev/null +++ b/drivers/media/pci/mantis/mantis_core.h @@ -0,0 +1,57 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_CORE_H +#define __MANTIS_CORE_H + +#include "mantis_common.h" + + +#define FE_TYPE_SAT 0 +#define FE_TYPE_CAB 1 +#define FE_TYPE_TER 2 + +#define FE_TYPE_TS204 0 +#define FE_TYPE_TS188 1 + + +struct vendorname { + u8 *sub_vendor_name; + u32 sub_vendor_id; +}; + +struct devicetype { + u8 *sub_device_name; + u32 sub_device_id; + u8 device_type; + u32 type_flags; +}; + + +extern int mantis_dma_init(struct mantis_pci *mantis); +extern int mantis_dma_exit(struct mantis_pci *mantis); +extern void mantis_dma_start(struct mantis_pci *mantis); +extern void mantis_dma_stop(struct mantis_pci *mantis); +extern int mantis_i2c_init(struct mantis_pci *mantis); +extern int mantis_i2c_exit(struct mantis_pci *mantis); +extern int mantis_core_init(struct mantis_pci *mantis); +extern int mantis_core_exit(struct mantis_pci *mantis); + +#endif /* __MANTIS_CORE_H */ diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c new file mode 100644 index 000000000000..566c407175a4 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_dma.c @@ -0,0 +1,230 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_reg.h" +#include "mantis_dma.h" + +#define RISC_WRITE (0x01 << 28) +#define RISC_JUMP (0x07 << 28) +#define RISC_IRQ (0x01 << 24) + +#define RISC_STATUS(status) ((((~status) & 0x0f) << 20) | ((status & 0x0f) << 16)) +#define RISC_FLUSH(risc_pos) (risc_pos = 0) +#define RISC_INSTR(risc_pos, opcode) (mantis->risc_cpu[risc_pos++] = cpu_to_le32(opcode)) + +#define MANTIS_BUF_SIZE (64 * 1024) +#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE / 4) +#define MANTIS_DMA_TR_BYTES (2 * 1024) /* upper limit: 4095 bytes. */ +#define MANTIS_BLOCK_COUNT (MANTIS_BUF_SIZE / MANTIS_BLOCK_BYTES) + +#define MANTIS_DMA_TR_UNITS (MANTIS_BLOCK_BYTES / MANTIS_DMA_TR_BYTES) +/* MANTIS_BUF_SIZE / MANTIS_DMA_TR_UNITS must not exceed MANTIS_RISC_SIZE (4k RISC cmd buffer) */ +#define MANTIS_RISC_SIZE PAGE_SIZE /* RISC program must fit here. */ + +int mantis_dma_exit(struct mantis_pci *mantis) +{ + if (mantis->buf_cpu) { + dprintk(MANTIS_ERROR, 1, + "DMA=0x%lx cpu=0x%p size=%d", + (unsigned long) mantis->buf_dma, + mantis->buf_cpu, + MANTIS_BUF_SIZE); + + pci_free_consistent(mantis->pdev, MANTIS_BUF_SIZE, + mantis->buf_cpu, mantis->buf_dma); + + mantis->buf_cpu = NULL; + } + if (mantis->risc_cpu) { + dprintk(MANTIS_ERROR, 1, + "RISC=0x%lx cpu=0x%p size=%lx", + (unsigned long) mantis->risc_dma, + mantis->risc_cpu, + MANTIS_RISC_SIZE); + + pci_free_consistent(mantis->pdev, MANTIS_RISC_SIZE, + mantis->risc_cpu, mantis->risc_dma); + + mantis->risc_cpu = NULL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mantis_dma_exit); + +static inline int mantis_alloc_buffers(struct mantis_pci *mantis) +{ + if (!mantis->buf_cpu) { + mantis->buf_cpu = pci_alloc_consistent(mantis->pdev, + MANTIS_BUF_SIZE, + &mantis->buf_dma); + if (!mantis->buf_cpu) { + dprintk(MANTIS_ERROR, 1, + "DMA buffer allocation failed"); + + goto err; + } + dprintk(MANTIS_ERROR, 1, + "DMA=0x%lx cpu=0x%p size=%d", + (unsigned long) mantis->buf_dma, + mantis->buf_cpu, MANTIS_BUF_SIZE); + } + if (!mantis->risc_cpu) { + mantis->risc_cpu = pci_alloc_consistent(mantis->pdev, + MANTIS_RISC_SIZE, + &mantis->risc_dma); + + if (!mantis->risc_cpu) { + dprintk(MANTIS_ERROR, 1, + "RISC program allocation failed"); + + mantis_dma_exit(mantis); + + goto err; + } + dprintk(MANTIS_ERROR, 1, + "RISC=0x%lx cpu=0x%p size=%lx", + (unsigned long) mantis->risc_dma, + mantis->risc_cpu, MANTIS_RISC_SIZE); + } + + return 0; +err: + dprintk(MANTIS_ERROR, 1, "Out of memory (?) ....."); + return -ENOMEM; +} + +int mantis_dma_init(struct mantis_pci *mantis) +{ + int err = 0; + + dprintk(MANTIS_DEBUG, 1, "Mantis DMA init"); + if (mantis_alloc_buffers(mantis) < 0) { + dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer"); + + /* Stop RISC Engine */ + mmwrite(0, MANTIS_DMA_CTL); + + goto err; + } + + return 0; +err: + return err; +} +EXPORT_SYMBOL_GPL(mantis_dma_init); + +static inline void mantis_risc_program(struct mantis_pci *mantis) +{ + u32 buf_pos = 0; + u32 line, step; + u32 risc_pos; + + dprintk(MANTIS_DEBUG, 1, "Mantis create RISC program"); + RISC_FLUSH(risc_pos); + + dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u, bytes per DMA tr %u", + MANTIS_BLOCK_COUNT, MANTIS_BLOCK_BYTES, MANTIS_DMA_TR_BYTES); + + for (line = 0; line < MANTIS_BLOCK_COUNT; line++) { + for (step = 0; step < MANTIS_DMA_TR_UNITS; step++) { + dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d], step=[%d]", line, step); + if (step == 0) { + RISC_INSTR(risc_pos, RISC_WRITE | + RISC_IRQ | + RISC_STATUS(line) | + MANTIS_DMA_TR_BYTES); + } else { + RISC_INSTR(risc_pos, RISC_WRITE | MANTIS_DMA_TR_BYTES); + } + RISC_INSTR(risc_pos, mantis->buf_dma + buf_pos); + buf_pos += MANTIS_DMA_TR_BYTES; + } + } + RISC_INSTR(risc_pos, RISC_JUMP); + RISC_INSTR(risc_pos, mantis->risc_dma); +} + +void mantis_dma_start(struct mantis_pci *mantis) +{ + dprintk(MANTIS_DEBUG, 1, "Mantis Start DMA engine"); + + mantis_risc_program(mantis); + mmwrite(mantis->risc_dma, MANTIS_RISC_START); + mmwrite(mmread(MANTIS_GPIF_ADDR) | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); + + mmwrite(0, MANTIS_DMA_CTL); + mantis->last_block = mantis->busy_block = 0; + + mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK); + + mmwrite(MANTIS_FIFO_EN | MANTIS_DCAP_EN + | MANTIS_RISC_EN, MANTIS_DMA_CTL); + +} + +void mantis_dma_stop(struct mantis_pci *mantis) +{ + dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine"); + + mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR); + + mmwrite((mmread(MANTIS_DMA_CTL) & ~(MANTIS_FIFO_EN | + MANTIS_DCAP_EN | + MANTIS_RISC_EN)), MANTIS_DMA_CTL); + + mmwrite(mmread(MANTIS_INT_STAT), MANTIS_INT_STAT); + + mmwrite(mmread(MANTIS_INT_MASK) & ~(MANTIS_INT_RISCI | + MANTIS_INT_RISCEN), MANTIS_INT_MASK); +} + + +void mantis_dma_xfer(unsigned long data) +{ + struct mantis_pci *mantis = (struct mantis_pci *) data; + struct mantis_hwconfig *config = mantis->hwconfig; + + while (mantis->last_block != mantis->busy_block) { + dprintk(MANTIS_DEBUG, 1, "last block=[%d] finished block=[%d]", + mantis->last_block, mantis->busy_block); + + (config->ts_size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) + (&mantis->demux, &mantis->buf_cpu[mantis->last_block * MANTIS_BLOCK_BYTES], MANTIS_BLOCK_BYTES); + mantis->last_block = (mantis->last_block + 1) % MANTIS_BLOCK_COUNT; + } +} diff --git a/drivers/media/pci/mantis/mantis_dma.h b/drivers/media/pci/mantis/mantis_dma.h new file mode 100644 index 000000000000..6be00fa82094 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_dma.h @@ -0,0 +1,30 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_DMA_H +#define __MANTIS_DMA_H + +extern int mantis_dma_init(struct mantis_pci *mantis); +extern int mantis_dma_exit(struct mantis_pci *mantis); +extern void mantis_dma_start(struct mantis_pci *mantis); +extern void mantis_dma_stop(struct mantis_pci *mantis); +extern void mantis_dma_xfer(unsigned long data); + +#endif /* __MANTIS_DMA_H */ diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c new file mode 100644 index 000000000000..5d15c6b74d9b --- /dev/null +++ b/drivers/media/pci/mantis/mantis_dvb.c @@ -0,0 +1,301 @@ +/* + Mantis PCI bridge driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include + +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_dma.h" +#include "mantis_ca.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power) +{ + struct mantis_hwconfig *config = mantis->hwconfig; + + switch (power) { + case POWER_ON: + dprintk(MANTIS_DEBUG, 1, "Power ON"); + mantis_gpio_set_bits(mantis, config->power, POWER_ON); + msleep(100); + mantis_gpio_set_bits(mantis, config->power, POWER_ON); + msleep(100); + break; + + case POWER_OFF: + dprintk(MANTIS_DEBUG, 1, "Power OFF"); + mantis_gpio_set_bits(mantis, config->power, POWER_OFF); + msleep(100); + break; + + default: + dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power); + return -1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mantis_frontend_power); + +void mantis_frontend_soft_reset(struct mantis_pci *mantis) +{ + struct mantis_hwconfig *config = mantis->hwconfig; + + dprintk(MANTIS_DEBUG, 1, "Frontend RESET"); + mantis_gpio_set_bits(mantis, config->reset, 0); + msleep(100); + mantis_gpio_set_bits(mantis, config->reset, 0); + msleep(100); + mantis_gpio_set_bits(mantis, config->reset, 1); + msleep(100); + mantis_gpio_set_bits(mantis, config->reset, 1); + msleep(100); + + return; +} +EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset); + +static int mantis_frontend_shutdown(struct mantis_pci *mantis) +{ + int err; + + mantis_frontend_soft_reset(mantis); + err = mantis_frontend_power(mantis, POWER_OFF); + if (err != 0) { + dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err); + return 1; + } + + return 0; +} + +static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct mantis_pci *mantis = dvbdmx->priv; + + dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed"); + if (!dvbdmx->dmx.frontend) { + dprintk(MANTIS_DEBUG, 1, "no frontend ?"); + return -EINVAL; + } + + mantis->feeds++; + dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds); + + if (mantis->feeds == 1) { + dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma"); + mantis_dma_start(mantis); + tasklet_enable(&mantis->tasklet); + } + + return mantis->feeds; +} + +static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct mantis_pci *mantis = dvbdmx->priv; + + dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed"); + if (!dvbdmx->dmx.frontend) { + dprintk(MANTIS_DEBUG, 1, "no frontend ?"); + return -EINVAL; + } + + mantis->feeds--; + if (mantis->feeds == 0) { + dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma"); + tasklet_disable(&mantis->tasklet); + mantis_dma_stop(mantis); + } + + return 0; +} + +int __devinit mantis_dvb_init(struct mantis_pci *mantis) +{ + struct mantis_hwconfig *config = mantis->hwconfig; + int result = -1; + + dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter"); + + result = dvb_register_adapter(&mantis->dvb_adapter, + "Mantis DVB adapter", + THIS_MODULE, + &mantis->pdev->dev, + adapter_nr); + + if (result < 0) { + + dprintk(MANTIS_ERROR, 1, "Error registering adapter"); + return -ENODEV; + } + + mantis->dvb_adapter.priv = mantis; + mantis->demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + + mantis->demux.priv = mantis; + mantis->demux.filternum = 256; + mantis->demux.feednum = 256; + mantis->demux.start_feed = mantis_dvb_start_feed; + mantis->demux.stop_feed = mantis_dvb_stop_feed; + mantis->demux.write_to_decoder = NULL; + + dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init"); + result = dvb_dmx_init(&mantis->demux); + if (result < 0) { + dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); + + goto err0; + } + + mantis->dmxdev.filternum = 256; + mantis->dmxdev.demux = &mantis->demux.dmx; + mantis->dmxdev.capabilities = 0; + dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init"); + + result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter); + if (result < 0) { + + dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result); + goto err1; + } + + mantis->fe_hw.source = DMX_FRONTEND_0; + result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw); + if (result < 0) { + + dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); + goto err2; + } + + mantis->fe_mem.source = DMX_MEMORY_FE; + result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem); + if (result < 0) { + dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); + goto err3; + } + + result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw); + if (result < 0) { + dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); + goto err4; + } + + dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx); + tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis); + tasklet_disable(&mantis->tasklet); + if (mantis->hwconfig) { + result = config->frontend_init(mantis, mantis->fe); + if (result < 0) { + dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!"); + goto err5; + } else { + if (mantis->fe == NULL) { + dprintk(MANTIS_ERROR, 1, "FE "); + goto err5; + } + + if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) { + dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed"); + + if (mantis->fe->ops.release) + mantis->fe->ops.release(mantis->fe); + + mantis->fe = NULL; + goto err5; + } + } + } + + return 0; + + /* Error conditions .. */ +err5: + tasklet_kill(&mantis->tasklet); + dvb_net_release(&mantis->dvbnet); + if (mantis->fe) { + dvb_unregister_frontend(mantis->fe); + dvb_frontend_detach(mantis->fe); + } +err4: + mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); + +err3: + mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); + +err2: + dvb_dmxdev_release(&mantis->dmxdev); + +err1: + dvb_dmx_release(&mantis->demux); + +err0: + dvb_unregister_adapter(&mantis->dvb_adapter); + + return result; +} +EXPORT_SYMBOL_GPL(mantis_dvb_init); + +int __devexit mantis_dvb_exit(struct mantis_pci *mantis) +{ + int err; + + if (mantis->fe) { + /* mantis_ca_exit(mantis); */ + err = mantis_frontend_shutdown(mantis); + if (err != 0) + dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err); + dvb_unregister_frontend(mantis->fe); + dvb_frontend_detach(mantis->fe); + } + + tasklet_kill(&mantis->tasklet); + dvb_net_release(&mantis->dvbnet); + + mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); + mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); + + dvb_dmxdev_release(&mantis->dmxdev); + dvb_dmx_release(&mantis->demux); + + dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter"); + dvb_unregister_adapter(&mantis->dvb_adapter); + + return 0; +} +EXPORT_SYMBOL_GPL(mantis_dvb_exit); diff --git a/drivers/media/pci/mantis/mantis_dvb.h b/drivers/media/pci/mantis/mantis_dvb.h new file mode 100644 index 000000000000..464199db304e --- /dev/null +++ b/drivers/media/pci/mantis/mantis_dvb.h @@ -0,0 +1,35 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_DVB_H +#define __MANTIS_DVB_H + +enum mantis_power { + POWER_OFF = 0, + POWER_ON = 1 +}; + +extern int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power); +extern void mantis_frontend_soft_reset(struct mantis_pci *mantis); + +extern int mantis_dvb_init(struct mantis_pci *mantis); +extern int mantis_dvb_exit(struct mantis_pci *mantis); + +#endif /* __MANTIS_DVB_H */ diff --git a/drivers/media/pci/mantis/mantis_evm.c b/drivers/media/pci/mantis/mantis_evm.c new file mode 100644 index 000000000000..71ce52875c38 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_evm.c @@ -0,0 +1,117 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_link.h" +#include "mantis_hif.h" +#include "mantis_reg.h" + +static void mantis_hifevm_work(struct work_struct *work) +{ + struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work); + struct mantis_pci *mantis = ca->ca_priv; + + u32 gpif_stat; + + gpif_stat = mmread(MANTIS_GPIF_STATUS); + + if (gpif_stat & MANTIS_GPIF_DETSTAT) { + if (gpif_stat & MANTIS_CARD_PLUGIN) { + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Plugin", mantis->num); + mmwrite(0xdada0000, MANTIS_CARD_RESET); + mantis_event_cam_plugin(ca); + dvb_ca_en50221_camchange_irq(&ca->en50221, + 0, + DVB_CA_EN50221_CAMCHANGE_INSERTED); + } + } else { + if (gpif_stat & MANTIS_CARD_PLUGOUT) { + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Unplug", mantis->num); + mmwrite(0xdada0000, MANTIS_CARD_RESET); + mantis_event_cam_unplug(ca); + dvb_ca_en50221_camchange_irq(&ca->en50221, + 0, + DVB_CA_EN50221_CAMCHANGE_REMOVED); + } + } + + if (mantis->gpif_status & MANTIS_GPIF_EXTIRQ) + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Ext IRQ", mantis->num); + + if (mantis->gpif_status & MANTIS_SBUF_WSTO) + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Timeout", mantis->num); + + if (mantis->gpif_status & MANTIS_GPIF_OTHERR) + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Alignment Error", mantis->num); + + if (gpif_stat & MANTIS_SBUF_OVFLW) + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Overflow", mantis->num); + + if (gpif_stat & MANTIS_GPIF_BRRDY) + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Read Ready", mantis->num); + + if (gpif_stat & MANTIS_GPIF_INTSTAT) + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): GPIF IRQ", mantis->num); + + if (gpif_stat & MANTIS_SBUF_EMPTY) + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Empty", mantis->num); + + if (gpif_stat & MANTIS_SBUF_OPDONE) { + dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer operation complete", mantis->num); + ca->sbuf_status = MANTIS_SBUF_DATA_AVAIL; + ca->hif_event = MANTIS_SBUF_OPDONE; + wake_up(&ca->hif_opdone_wq); + } +} + +int mantis_evmgr_init(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Initializing Mantis Host I/F Event manager"); + INIT_WORK(&ca->hif_evm_work, mantis_hifevm_work); + mantis_pcmcia_init(ca); + schedule_work(&ca->hif_evm_work); + mantis_hif_init(ca); + return 0; +} + +void mantis_evmgr_exit(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + + dprintk(MANTIS_DEBUG, 1, "Mantis Host I/F Event manager exiting"); + flush_work_sync(&ca->hif_evm_work); + mantis_hif_exit(ca); + mantis_pcmcia_exit(ca); +} diff --git a/drivers/media/pci/mantis/mantis_hif.c b/drivers/media/pci/mantis/mantis_hif.c new file mode 100644 index 000000000000..10c68df7e16f --- /dev/null +++ b/drivers/media/pci/mantis/mantis_hif.c @@ -0,0 +1,239 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" + +#include "mantis_hif.h" +#include "mantis_link.h" /* temporary due to physical layer stuff */ + +#include "mantis_reg.h" + + +static int mantis_hif_sbuf_opdone_wait(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + int rc = 0; + + if (wait_event_timeout(ca->hif_opdone_wq, + ca->hif_event & MANTIS_SBUF_OPDONE, + msecs_to_jiffies(500)) == -ERESTARTSYS) { + + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Smart buffer operation timeout !", mantis->num); + rc = -EREMOTEIO; + } + dprintk(MANTIS_DEBUG, 1, "Smart Buffer Operation complete"); + ca->hif_event &= ~MANTIS_SBUF_OPDONE; + return rc; +} + +static int mantis_hif_write_wait(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + u32 opdone = 0, timeout = 0; + int rc = 0; + + if (wait_event_timeout(ca->hif_write_wq, + mantis->gpif_status & MANTIS_GPIF_WRACK, + msecs_to_jiffies(500)) == -ERESTARTSYS) { + + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write ACK timed out !", mantis->num); + rc = -EREMOTEIO; + } + dprintk(MANTIS_DEBUG, 1, "Write Acknowledged"); + mantis->gpif_status &= ~MANTIS_GPIF_WRACK; + while (!opdone) { + opdone = (mmread(MANTIS_GPIF_STATUS) & MANTIS_SBUF_OPDONE); + udelay(500); + timeout++; + if (timeout > 100) { + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write operation timed out!", mantis->num); + rc = -ETIMEDOUT; + break; + } + } + dprintk(MANTIS_DEBUG, 1, "HIF Write success"); + return rc; +} + + +int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr) +{ + struct mantis_pci *mantis = ca->ca_priv; + u32 hif_addr = 0, data, count = 4; + + dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Read", mantis->num); + mutex_lock(&ca->ca_lock); + hif_addr &= ~MANTIS_GPIF_PCMCIAREG; + hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; + hif_addr |= MANTIS_HIF_STATUS; + hif_addr |= addr; + + mmwrite(hif_addr, MANTIS_GPIF_BRADDR); + mmwrite(count, MANTIS_GPIF_BRBYTES); + udelay(20); + mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); + + if (mantis_hif_sbuf_opdone_wait(ca) != 0) { + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): GPIF Smart Buffer operation failed", mantis->num); + mutex_unlock(&ca->ca_lock); + return -EREMOTEIO; + } + data = mmread(MANTIS_GPIF_DIN); + mutex_unlock(&ca->ca_lock); + dprintk(MANTIS_DEBUG, 1, "Mem Read: 0x%02x", data); + return (data >> 24) & 0xff; +} + +int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data) +{ + struct mantis_slot *slot = ca->slot; + struct mantis_pci *mantis = ca->ca_priv; + u32 hif_addr = 0; + + dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Write", mantis->num); + mutex_lock(&ca->ca_lock); + hif_addr &= ~MANTIS_GPIF_HIFRDWRN; + hif_addr &= ~MANTIS_GPIF_PCMCIAREG; + hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; + hif_addr |= MANTIS_HIF_STATUS; + hif_addr |= addr; + + mmwrite(slot->slave_cfg, MANTIS_GPIF_CFGSLA); /* Slot0 alone for now */ + mmwrite(hif_addr, MANTIS_GPIF_ADDR); + mmwrite(data, MANTIS_GPIF_DOUT); + + if (mantis_hif_write_wait(ca) != 0) { + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); + mutex_unlock(&ca->ca_lock); + return -EREMOTEIO; + } + dprintk(MANTIS_DEBUG, 1, "Mem Write: (0x%02x to 0x%02x)", data, addr); + mutex_unlock(&ca->ca_lock); + + return 0; +} + +int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr) +{ + struct mantis_pci *mantis = ca->ca_priv; + u32 data, hif_addr = 0; + + dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Read", mantis->num); + mutex_lock(&ca->ca_lock); + hif_addr &= ~MANTIS_GPIF_PCMCIAREG; + hif_addr |= MANTIS_GPIF_PCMCIAIOM; + hif_addr |= MANTIS_HIF_STATUS; + hif_addr |= addr; + + mmwrite(hif_addr, MANTIS_GPIF_BRADDR); + mmwrite(1, MANTIS_GPIF_BRBYTES); + udelay(20); + mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); + + if (mantis_hif_sbuf_opdone_wait(ca) != 0) { + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); + mutex_unlock(&ca->ca_lock); + return -EREMOTEIO; + } + data = mmread(MANTIS_GPIF_DIN); + dprintk(MANTIS_DEBUG, 1, "I/O Read: 0x%02x", data); + udelay(50); + mutex_unlock(&ca->ca_lock); + + return (u8) data; +} + +int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data) +{ + struct mantis_pci *mantis = ca->ca_priv; + u32 hif_addr = 0; + + dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Write", mantis->num); + mutex_lock(&ca->ca_lock); + hif_addr &= ~MANTIS_GPIF_PCMCIAREG; + hif_addr &= ~MANTIS_GPIF_HIFRDWRN; + hif_addr |= MANTIS_GPIF_PCMCIAIOM; + hif_addr |= MANTIS_HIF_STATUS; + hif_addr |= addr; + + mmwrite(hif_addr, MANTIS_GPIF_ADDR); + mmwrite(data, MANTIS_GPIF_DOUT); + + if (mantis_hif_write_wait(ca) != 0) { + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); + mutex_unlock(&ca->ca_lock); + return -EREMOTEIO; + } + dprintk(MANTIS_DEBUG, 1, "I/O Write: (0x%02x to 0x%02x)", data, addr); + mutex_unlock(&ca->ca_lock); + udelay(50); + + return 0; +} + +int mantis_hif_init(struct mantis_ca *ca) +{ + struct mantis_slot *slot = ca->slot; + struct mantis_pci *mantis = ca->ca_priv; + u32 irqcfg; + + slot[0].slave_cfg = 0x70773028; + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Initializing Mantis Host Interface", mantis->num); + + mutex_lock(&ca->ca_lock); + irqcfg = mmread(MANTIS_GPIF_IRQCFG); + irqcfg = MANTIS_MASK_BRRDY | + MANTIS_MASK_WRACK | + MANTIS_MASK_EXTIRQ | + MANTIS_MASK_WSTO | + MANTIS_MASK_OTHERR | + MANTIS_MASK_OVFLW; + + mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); + mutex_unlock(&ca->ca_lock); + + return 0; +} + +void mantis_hif_exit(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + u32 irqcfg; + + dprintk(MANTIS_ERROR, 1, "Adapter(%d) Exiting Mantis Host Interface", mantis->num); + mutex_lock(&ca->ca_lock); + irqcfg = mmread(MANTIS_GPIF_IRQCFG); + irqcfg &= ~MANTIS_MASK_BRRDY; + mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); + mutex_unlock(&ca->ca_lock); +} diff --git a/drivers/media/pci/mantis/mantis_hif.h b/drivers/media/pci/mantis/mantis_hif.h new file mode 100644 index 000000000000..9094f9ed2362 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_hif.h @@ -0,0 +1,29 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_HIF_H +#define __MANTIS_HIF_H + +#define MANTIS_HIF_MEMRD 1 +#define MANTIS_HIF_MEMWR 2 +#define MANTIS_HIF_IOMRD 3 +#define MANTIS_HIF_IOMWR 4 + +#endif /* __MANTIS_HIF_H */ diff --git a/drivers/media/pci/mantis/mantis_i2c.c b/drivers/media/pci/mantis/mantis_i2c.c new file mode 100644 index 000000000000..e7794517fe26 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_i2c.c @@ -0,0 +1,266 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_reg.h" +#include "mantis_i2c.h" + +#define TRIALS 10000 + +static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg) +{ + u32 rxd, i, stat, trials; + + dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", + __func__, msg->addr); + + for (i = 0; i < msg->len; i++) { + rxd = (msg->addr << 25) | (1 << 24) + | MANTIS_I2C_RATE_3 + | MANTIS_I2C_STOP + | MANTIS_I2C_PGMODE; + + if (i == (msg->len - 1)) + rxd &= ~MANTIS_I2C_STOP; + + mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); + mmwrite(rxd, MANTIS_I2CDATA_CTL); + + /* wait for xfer completion */ + for (trials = 0; trials < TRIALS; trials++) { + stat = mmread(MANTIS_INT_STAT); + if (stat & MANTIS_INT_I2CDONE) + break; + } + + dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); + + /* wait for xfer completion */ + for (trials = 0; trials < TRIALS; trials++) { + stat = mmread(MANTIS_INT_STAT); + if (stat & MANTIS_INT_I2CRACK) + break; + } + + dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); + + rxd = mmread(MANTIS_I2CDATA_CTL); + msg->buf[i] = (u8)((rxd >> 8) & 0xFF); + dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); + } + dprintk(MANTIS_INFO, 0, "]\n"); + + return 0; +} + +static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg) +{ + int i; + u32 txd = 0, stat, trials; + + dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", + __func__, msg->addr); + + for (i = 0; i < msg->len; i++) { + dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); + txd = (msg->addr << 25) | (msg->buf[i] << 8) + | MANTIS_I2C_RATE_3 + | MANTIS_I2C_STOP + | MANTIS_I2C_PGMODE; + + if (i == (msg->len - 1)) + txd &= ~MANTIS_I2C_STOP; + + mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); + mmwrite(txd, MANTIS_I2CDATA_CTL); + + /* wait for xfer completion */ + for (trials = 0; trials < TRIALS; trials++) { + stat = mmread(MANTIS_INT_STAT); + if (stat & MANTIS_INT_I2CDONE) + break; + } + + dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); + + /* wait for xfer completion */ + for (trials = 0; trials < TRIALS; trials++) { + stat = mmread(MANTIS_INT_STAT); + if (stat & MANTIS_INT_I2CRACK) + break; + } + + dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); + } + dprintk(MANTIS_INFO, 0, "]\n"); + + return 0; +} + +static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +{ + int ret = 0, i = 0, trials; + u32 stat, data, txd; + struct mantis_pci *mantis; + struct mantis_hwconfig *config; + + mantis = i2c_get_adapdata(adapter); + BUG_ON(!mantis); + config = mantis->hwconfig; + BUG_ON(!config); + + dprintk(MANTIS_DEBUG, 1, "Messages:%d", num); + mutex_lock(&mantis->i2c_lock); + + while (i < num) { + /* Byte MODE */ + if ((config->i2c_mode & MANTIS_BYTE_MODE) && + ((i + 1) < num) && + (msgs[i].len < 2) && + (msgs[i + 1].len < 2) && + (msgs[i + 1].flags & I2C_M_RD)) { + + dprintk(MANTIS_DEBUG, 0, " Byte MODE:\n"); + + /* Read operation */ + txd = msgs[i].addr << 25 | (0x1 << 24) + | (msgs[i].buf[0] << 16) + | MANTIS_I2C_RATE_3; + + mmwrite(txd, MANTIS_I2CDATA_CTL); + /* wait for xfer completion */ + for (trials = 0; trials < TRIALS; trials++) { + stat = mmread(MANTIS_INT_STAT); + if (stat & MANTIS_INT_I2CDONE) + break; + } + + /* check for xfer completion */ + if (stat & MANTIS_INT_I2CDONE) { + /* check xfer was acknowledged */ + if (stat & MANTIS_INT_I2CRACK) { + data = mmread(MANTIS_I2CDATA_CTL); + msgs[i + 1].buf[0] = (data >> 8) & 0xff; + dprintk(MANTIS_DEBUG, 0, " Byte <%d> RXD=0x%02x [%02x]\n", 0x0, data, msgs[i + 1].buf[0]); + } else { + /* I/O error */ + dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); + ret = -EIO; + break; + } + } else { + /* I/O error */ + dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); + ret = -EIO; + break; + } + i += 2; /* Write/Read operation in one go */ + } + + if (i < num) { + if (msgs[i].flags & I2C_M_RD) + ret = mantis_i2c_read(mantis, &msgs[i]); + else + ret = mantis_i2c_write(mantis, &msgs[i]); + + i++; + if (ret < 0) + goto bail_out; + } + + } + + mutex_unlock(&mantis->i2c_lock); + + return num; + +bail_out: + mutex_unlock(&mantis->i2c_lock); + return ret; +} + +static u32 mantis_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm mantis_algo = { + .master_xfer = mantis_i2c_xfer, + .functionality = mantis_i2c_func, +}; + +int __devinit mantis_i2c_init(struct mantis_pci *mantis) +{ + u32 intstat, intmask; + struct i2c_adapter *i2c_adapter = &mantis->adapter; + struct pci_dev *pdev = mantis->pdev; + + init_waitqueue_head(&mantis->i2c_wq); + mutex_init(&mantis->i2c_lock); + strncpy(i2c_adapter->name, "Mantis I2C", sizeof(i2c_adapter->name)); + i2c_set_adapdata(i2c_adapter, mantis); + + i2c_adapter->owner = THIS_MODULE; + i2c_adapter->algo = &mantis_algo; + i2c_adapter->algo_data = NULL; + i2c_adapter->timeout = 500; + i2c_adapter->retries = 3; + i2c_adapter->dev.parent = &pdev->dev; + + mantis->i2c_rc = i2c_add_adapter(i2c_adapter); + if (mantis->i2c_rc < 0) + return mantis->i2c_rc; + + dprintk(MANTIS_DEBUG, 1, "Initializing I2C .."); + + intstat = mmread(MANTIS_INT_STAT); + intmask = mmread(MANTIS_INT_MASK); + mmwrite(intstat, MANTIS_INT_STAT); + dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); + intmask = mmread(MANTIS_INT_MASK); + mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); + + return 0; +} +EXPORT_SYMBOL_GPL(mantis_i2c_init); + +int mantis_i2c_exit(struct mantis_pci *mantis) +{ + u32 intmask; + + dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); + intmask = mmread(MANTIS_INT_MASK); + mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); + + dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter"); + return i2c_del_adapter(&mantis->adapter); +} +EXPORT_SYMBOL_GPL(mantis_i2c_exit); diff --git a/drivers/media/pci/mantis/mantis_i2c.h b/drivers/media/pci/mantis/mantis_i2c.h new file mode 100644 index 000000000000..1342df2faed8 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_i2c.h @@ -0,0 +1,30 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_I2C_H +#define __MANTIS_I2C_H + +#define I2C_STOP (1 << 0) +#define I2C_READ (1 << 1) + +extern int mantis_i2c_init(struct mantis_pci *mantis); +extern int mantis_i2c_exit(struct mantis_pci *mantis); + +#endif /* __MANTIS_I2C_H */ diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c new file mode 100644 index 000000000000..db6d54d3fec0 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_input.c @@ -0,0 +1,159 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_reg.h" +#include "mantis_uart.h" + +#define MODULE_NAME "mantis_core" +#define RC_MAP_MANTIS "rc-mantis" + +static struct rc_map_table mantis_ir_table[] = { + { 0x29, KEY_POWER }, + { 0x28, KEY_FAVORITES }, + { 0x30, KEY_TEXT }, + { 0x17, KEY_INFO }, /* Preview */ + { 0x23, KEY_EPG }, + { 0x3b, KEY_F22 }, /* Record List */ + { 0x3c, KEY_1 }, + { 0x3e, KEY_2 }, + { 0x39, KEY_3 }, + { 0x36, KEY_4 }, + { 0x22, KEY_5 }, + { 0x20, KEY_6 }, + { 0x32, KEY_7 }, + { 0x26, KEY_8 }, + { 0x24, KEY_9 }, + { 0x2a, KEY_0 }, + + { 0x33, KEY_CANCEL }, + { 0x2c, KEY_BACK }, + { 0x15, KEY_CLEAR }, + { 0x3f, KEY_TAB }, + { 0x10, KEY_ENTER }, + { 0x14, KEY_UP }, + { 0x0d, KEY_RIGHT }, + { 0x0e, KEY_DOWN }, + { 0x11, KEY_LEFT }, + + { 0x21, KEY_VOLUMEUP }, + { 0x35, KEY_VOLUMEDOWN }, + { 0x3d, KEY_CHANNELDOWN }, + { 0x3a, KEY_CHANNELUP }, + { 0x2e, KEY_RECORD }, + { 0x2b, KEY_PLAY }, + { 0x13, KEY_PAUSE }, + { 0x25, KEY_STOP }, + + { 0x1f, KEY_REWIND }, + { 0x2d, KEY_FASTFORWARD }, + { 0x1e, KEY_PREVIOUS }, /* Replay |< */ + { 0x1d, KEY_NEXT }, /* Skip >| */ + + { 0x0b, KEY_CAMERA }, /* Capture */ + { 0x0f, KEY_LANGUAGE }, /* SAP */ + { 0x18, KEY_MODE }, /* PIP */ + { 0x12, KEY_ZOOM }, /* Full screen */ + { 0x1c, KEY_SUBTITLE }, + { 0x2f, KEY_MUTE }, + { 0x16, KEY_F20 }, /* L/R */ + { 0x38, KEY_F21 }, /* Hibernate */ + + { 0x37, KEY_SWITCHVIDEOMODE }, /* A/V */ + { 0x31, KEY_AGAIN }, /* Recall */ + { 0x1a, KEY_KPPLUS }, /* Zoom+ */ + { 0x19, KEY_KPMINUS }, /* Zoom- */ + { 0x27, KEY_RED }, + { 0x0C, KEY_GREEN }, + { 0x01, KEY_YELLOW }, + { 0x00, KEY_BLUE }, +}; + +static struct rc_map_list ir_mantis_map = { + .map = { + .scan = mantis_ir_table, + .size = ARRAY_SIZE(mantis_ir_table), + .rc_type = RC_TYPE_UNKNOWN, + .name = RC_MAP_MANTIS, + } +}; + +int mantis_input_init(struct mantis_pci *mantis) +{ + struct rc_dev *dev; + int err; + + err = rc_map_register(&ir_mantis_map); + if (err) + goto out; + + dev = rc_allocate_device(); + if (!dev) { + dprintk(MANTIS_ERROR, 1, "Remote device allocation failed"); + err = -ENOMEM; + goto out_map; + } + + sprintf(mantis->input_name, "Mantis %s IR receiver", mantis->hwconfig->model_name); + sprintf(mantis->input_phys, "pci-%s/ir0", pci_name(mantis->pdev)); + + dev->input_name = mantis->input_name; + dev->input_phys = mantis->input_phys; + dev->input_id.bustype = BUS_PCI; + dev->input_id.vendor = mantis->vendor_id; + dev->input_id.product = mantis->device_id; + dev->input_id.version = 1; + dev->driver_name = MODULE_NAME; + dev->map_name = RC_MAP_MANTIS; + dev->dev.parent = &mantis->pdev->dev; + + err = rc_register_device(dev); + if (err) { + dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err); + goto out_dev; + } + + mantis->rc = dev; + return 0; + +out_dev: + rc_free_device(dev); +out_map: + rc_map_unregister(&ir_mantis_map); +out: + return err; +} + +int mantis_exit(struct mantis_pci *mantis) +{ + rc_unregister_device(mantis->rc); + rc_map_unregister(&ir_mantis_map); + return 0; +} + diff --git a/drivers/media/pci/mantis/mantis_ioc.c b/drivers/media/pci/mantis/mantis_ioc.c new file mode 100644 index 000000000000..24fcdc63d6d5 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_ioc.c @@ -0,0 +1,124 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_reg.h" +#include "mantis_ioc.h" + +static int read_eeprom_bytes(struct mantis_pci *mantis, u8 reg, u8 *data, u8 length) +{ + struct i2c_adapter *adapter = &mantis->adapter; + int err; + u8 buf = reg; + + struct i2c_msg msg[] = { + { .addr = 0x50, .flags = 0, .buf = &buf, .len = 1 }, + { .addr = 0x50, .flags = I2C_M_RD, .buf = data, .len = length }, + }; + + err = i2c_transfer(adapter, msg, 2); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", + err, data[0], data[1]); + + return err; + } + + return 0; +} +int mantis_get_mac(struct mantis_pci *mantis) +{ + int err; + u8 mac_addr[6] = {0}; + + err = read_eeprom_bytes(mantis, 0x08, mac_addr, 6); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Mantis EEPROM read error <%d>", err); + + return err; + } + + dprintk(MANTIS_ERROR, 0, " MAC Address=[%pM]\n", mac_addr); + + return 0; +} +EXPORT_SYMBOL_GPL(mantis_get_mac); + +/* Turn the given bit on or off. */ +void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) +{ + u32 cur; + + dprintk(MANTIS_DEBUG, 1, "Set Bit <%d> to <%d>", bitpos, value); + cur = mmread(MANTIS_GPIF_ADDR); + if (value) + mantis->gpio_status = cur | (1 << bitpos); + else + mantis->gpio_status = cur & (~(1 << bitpos)); + + dprintk(MANTIS_DEBUG, 1, "GPIO Value <%02x>", mantis->gpio_status); + mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); + mmwrite(0x00, MANTIS_GPIF_DOUT); +} +EXPORT_SYMBOL_GPL(mantis_gpio_set_bits); + +int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl) +{ + u32 reg; + + reg = mmread(MANTIS_CONTROL); + switch (stream_ctl) { + case STREAM_TO_HIF: + dprintk(MANTIS_DEBUG, 1, "Set stream to HIF"); + reg &= 0xff - MANTIS_BYPASS; + mmwrite(reg, MANTIS_CONTROL); + reg |= MANTIS_BYPASS; + mmwrite(reg, MANTIS_CONTROL); + break; + + case STREAM_TO_CAM: + dprintk(MANTIS_DEBUG, 1, "Set stream to CAM"); + reg |= MANTIS_BYPASS; + mmwrite(reg, MANTIS_CONTROL); + reg &= 0xff - MANTIS_BYPASS; + mmwrite(reg, MANTIS_CONTROL); + break; + default: + dprintk(MANTIS_ERROR, 1, "Unknown MODE <%02x>", stream_ctl); + return -1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mantis_stream_control); diff --git a/drivers/media/pci/mantis/mantis_ioc.h b/drivers/media/pci/mantis/mantis_ioc.h new file mode 100644 index 000000000000..d56e002b2955 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_ioc.h @@ -0,0 +1,51 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_IOC_H +#define __MANTIS_IOC_H + +#define GPIF_A00 0x00 +#define GPIF_A01 0x01 +#define GPIF_A02 0x02 +#define GPIF_A03 0x03 +#define GPIF_A04 0x04 +#define GPIF_A05 0x05 +#define GPIF_A06 0x06 +#define GPIF_A07 0x07 +#define GPIF_A08 0x08 +#define GPIF_A09 0x09 +#define GPIF_A10 0x0a +#define GPIF_A11 0x0b + +#define GPIF_A12 0x0c +#define GPIF_A13 0x0d +#define GPIF_A14 0x0e + +enum mantis_stream_control { + STREAM_TO_HIF = 0, + STREAM_TO_CAM +}; + +extern int mantis_get_mac(struct mantis_pci *mantis); +extern void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value); + +extern int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl); + +#endif /* __MANTIS_IOC_H */ diff --git a/drivers/media/pci/mantis/mantis_link.h b/drivers/media/pci/mantis/mantis_link.h new file mode 100644 index 000000000000..2a814774a001 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_link.h @@ -0,0 +1,83 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_LINK_H +#define __MANTIS_LINK_H + +#include +#include +#include "dvb_ca_en50221.h" + +enum mantis_sbuf_status { + MANTIS_SBUF_DATA_AVAIL = 1, + MANTIS_SBUF_DATA_EMPTY = 2, + MANTIS_SBUF_DATA_OVFLW = 3 +}; + +struct mantis_slot { + u32 timeout; + u32 slave_cfg; + u32 bar; +}; + +/* Physical layer */ +enum mantis_slot_state { + MODULE_INSERTED = 3, + MODULE_XTRACTED = 4 +}; + +struct mantis_ca { + struct mantis_slot slot[4]; + + struct work_struct hif_evm_work; + + u32 hif_event; + wait_queue_head_t hif_opdone_wq; + wait_queue_head_t hif_brrdyw_wq; + wait_queue_head_t hif_data_wq; + wait_queue_head_t hif_write_wq; /* HIF Write op */ + + enum mantis_sbuf_status sbuf_status; + + enum mantis_slot_state slot_state; + + void *ca_priv; + + struct dvb_ca_en50221 en50221; + struct mutex ca_lock; +}; + +/* CA */ +extern void mantis_event_cam_plugin(struct mantis_ca *ca); +extern void mantis_event_cam_unplug(struct mantis_ca *ca); +extern int mantis_pcmcia_init(struct mantis_ca *ca); +extern void mantis_pcmcia_exit(struct mantis_ca *ca); +extern int mantis_evmgr_init(struct mantis_ca *ca); +extern void mantis_evmgr_exit(struct mantis_ca *ca); + +/* HIF */ +extern int mantis_hif_init(struct mantis_ca *ca); +extern void mantis_hif_exit(struct mantis_ca *ca); +extern int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr); +extern int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data); +extern int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr); +extern int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data); + +#endif /* __MANTIS_LINK_H */ diff --git a/drivers/media/pci/mantis/mantis_pci.c b/drivers/media/pci/mantis/mantis_pci.c new file mode 100644 index 000000000000..371558af2d96 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_pci.c @@ -0,0 +1,170 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_reg.h" +#include "mantis_pci.h" + +#define DRIVER_NAME "Mantis Core" + +int __devinit mantis_pci_init(struct mantis_pci *mantis) +{ + u8 latency; + struct mantis_hwconfig *config = mantis->hwconfig; + struct pci_dev *pdev = mantis->pdev; + int err, ret = 0; + + dprintk(MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", + config->model_name, + config->dev_type, + mantis->pdev->bus->number, + PCI_SLOT(mantis->pdev->devfn), + PCI_FUNC(mantis->pdev->devfn)); + + err = pci_enable_device(pdev); + if (err != 0) { + ret = -ENODEV; + dprintk(MANTIS_ERROR, 1, "ERROR: PCI enable failed <%i>", err); + goto fail0; + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err != 0) { + dprintk(MANTIS_ERROR, 1, "ERROR: Unable to obtain 32 bit DMA <%i>", err); + ret = -ENOMEM; + goto fail1; + } + + pci_set_master(pdev); + + if (!request_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), + DRIVER_NAME)) { + + dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 Request failed !"); + ret = -ENODEV; + goto fail1; + } + + mantis->mmio = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + + if (!mantis->mmio) { + dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 remap failed !"); + ret = -ENODEV; + goto fail2; + } + + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency); + mantis->latency = latency; + mantis->revision = pdev->revision; + + dprintk(MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", + mantis->revision, + mantis->pdev->subsystem_vendor, + mantis->pdev->subsystem_device); + + dprintk(MANTIS_ERROR, 0, + "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", + mantis->pdev->irq, + mantis->latency, + mantis->mantis_addr, + mantis->mmio); + + err = request_irq(pdev->irq, + config->irq_handler, + IRQF_SHARED, + DRIVER_NAME, + mantis); + + if (err != 0) { + + dprintk(MANTIS_ERROR, 1, "ERROR: IRQ registration failed ! <%d>", err); + ret = -ENODEV; + goto fail3; + } + + pci_set_drvdata(pdev, mantis); + return ret; + + /* Error conditions */ +fail3: + dprintk(MANTIS_ERROR, 1, "ERROR: <%d> I/O unmap", ret); + if (mantis->mmio) + iounmap(mantis->mmio); + +fail2: + dprintk(MANTIS_ERROR, 1, "ERROR: <%d> releasing regions", ret); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + +fail1: + dprintk(MANTIS_ERROR, 1, "ERROR: <%d> disabling device", ret); + pci_disable_device(pdev); + +fail0: + dprintk(MANTIS_ERROR, 1, "ERROR: <%d> exiting", ret); + pci_set_drvdata(pdev, NULL); + return ret; +} +EXPORT_SYMBOL_GPL(mantis_pci_init); + +void mantis_pci_exit(struct mantis_pci *mantis) +{ + struct pci_dev *pdev = mantis->pdev; + + dprintk(MANTIS_NOTICE, 1, " mem: 0x%p", mantis->mmio); + free_irq(pdev->irq, mantis); + if (mantis->mmio) { + iounmap(mantis->mmio); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + } + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} +EXPORT_SYMBOL_GPL(mantis_pci_exit); + +MODULE_DESCRIPTION("Mantis PCI DTV bridge driver"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/mantis/mantis_pci.h b/drivers/media/pci/mantis/mantis_pci.h new file mode 100644 index 000000000000..65f004519086 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_pci.h @@ -0,0 +1,27 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_PCI_H +#define __MANTIS_PCI_H + +extern int mantis_pci_init(struct mantis_pci *mantis); +extern void mantis_pci_exit(struct mantis_pci *mantis); + +#endif /* __MANTIS_PCI_H */ diff --git a/drivers/media/pci/mantis/mantis_pcmcia.c b/drivers/media/pci/mantis/mantis_pcmcia.c new file mode 100644 index 000000000000..2f188c089666 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_pcmcia.c @@ -0,0 +1,121 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_link.h" /* temporary due to physical layer stuff */ +#include "mantis_reg.h" + +/* + * If Slot state is already PLUG_IN event and we are called + * again, definitely it is jitter alone + */ +void mantis_event_cam_plugin(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + + u32 gpif_irqcfg; + + if (ca->slot_state == MODULE_XTRACTED) { + dprintk(MANTIS_DEBUG, 1, "Event: CAM Plugged IN: Adapter(%d) Slot(0)", mantis->num); + udelay(50); + mmwrite(0xda000000, MANTIS_CARD_RESET); + gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); + gpif_irqcfg |= MANTIS_MASK_PLUGOUT; + gpif_irqcfg &= ~MANTIS_MASK_PLUGIN; + mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); + udelay(500); + ca->slot_state = MODULE_INSERTED; + } + udelay(100); +} + +/* + * If Slot state is already UN_PLUG event and we are called + * again, definitely it is jitter alone + */ +void mantis_event_cam_unplug(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + + u32 gpif_irqcfg; + + if (ca->slot_state == MODULE_INSERTED) { + dprintk(MANTIS_DEBUG, 1, "Event: CAM Unplugged: Adapter(%d) Slot(0)", mantis->num); + udelay(50); + mmwrite(0x00da0000, MANTIS_CARD_RESET); + gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); + gpif_irqcfg |= MANTIS_MASK_PLUGIN; + gpif_irqcfg &= ~MANTIS_MASK_PLUGOUT; + mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); + udelay(500); + ca->slot_state = MODULE_XTRACTED; + } + udelay(100); +} + +int mantis_pcmcia_init(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + + u32 gpif_stat, card_stat; + + mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_IRQ0, MANTIS_INT_MASK); + gpif_stat = mmread(MANTIS_GPIF_STATUS); + card_stat = mmread(MANTIS_GPIF_IRQCFG); + + if (gpif_stat & MANTIS_GPIF_DETSTAT) { + dprintk(MANTIS_DEBUG, 1, "CAM found on Adapter(%d) Slot(0)", mantis->num); + mmwrite(card_stat | MANTIS_MASK_PLUGOUT, MANTIS_GPIF_IRQCFG); + ca->slot_state = MODULE_INSERTED; + dvb_ca_en50221_camchange_irq(&ca->en50221, + 0, + DVB_CA_EN50221_CAMCHANGE_INSERTED); + } else { + dprintk(MANTIS_DEBUG, 1, "Empty Slot on Adapter(%d) Slot(0)", mantis->num); + mmwrite(card_stat | MANTIS_MASK_PLUGIN, MANTIS_GPIF_IRQCFG); + ca->slot_state = MODULE_XTRACTED; + dvb_ca_en50221_camchange_irq(&ca->en50221, + 0, + DVB_CA_EN50221_CAMCHANGE_REMOVED); + } + + return 0; +} + +void mantis_pcmcia_exit(struct mantis_ca *ca) +{ + struct mantis_pci *mantis = ca->ca_priv; + + mmwrite(mmread(MANTIS_GPIF_STATUS) & (~MANTIS_CARD_PLUGOUT | ~MANTIS_CARD_PLUGIN), MANTIS_GPIF_STATUS); + mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ0, MANTIS_INT_MASK); +} diff --git a/drivers/media/pci/mantis/mantis_reg.h b/drivers/media/pci/mantis/mantis_reg.h new file mode 100644 index 000000000000..7761f9dc7fe0 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_reg.h @@ -0,0 +1,197 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_REG_H +#define __MANTIS_REG_H + +/* Interrupts */ +#define MANTIS_INT_STAT 0x00 +#define MANTIS_INT_MASK 0x04 + +#define MANTIS_INT_RISCSTAT (0x0f << 28) +#define MANTIS_INT_RISCEN (0x01 << 27) +#define MANTIS_INT_I2CRACK (0x01 << 26) + +/* #define MANTIS_INT_GPIF (0xff << 12) */ + +#define MANTIS_INT_PCMCIA7 (0x01 << 19) +#define MANTIS_INT_PCMCIA6 (0x01 << 18) +#define MANTIS_INT_PCMCIA5 (0x01 << 17) +#define MANTIS_INT_PCMCIA4 (0x01 << 16) +#define MANTIS_INT_PCMCIA3 (0x01 << 15) +#define MANTIS_INT_PCMCIA2 (0x01 << 14) +#define MANTIS_INT_PCMCIA1 (0x01 << 13) +#define MANTIS_INT_PCMCIA0 (0x01 << 12) +#define MANTIS_INT_IRQ1 (0x01 << 11) +#define MANTIS_INT_IRQ0 (0x01 << 10) +#define MANTIS_INT_OCERR (0x01 << 8) +#define MANTIS_INT_PABORT (0x01 << 7) +#define MANTIS_INT_RIPERR (0x01 << 6) +#define MANTIS_INT_PPERR (0x01 << 5) +#define MANTIS_INT_FTRGT (0x01 << 3) +#define MANTIS_INT_RISCI (0x01 << 1) +#define MANTIS_INT_I2CDONE (0x01 << 0) + +/* DMA */ +#define MANTIS_DMA_CTL 0x08 +#define MANTIS_GPIF_RD (0xff << 24) +#define MANTIS_GPIF_WR (0xff << 16) +#define MANTIS_CPU_DO (0x01 << 10) +#define MANTIS_DRV_DO (0x01 << 9) +#define MANTIS_I2C_RD (0x01 << 7) +#define MANTIS_I2C_WR (0x01 << 6) +#define MANTIS_DCAP_MODE (0x01 << 5) +#define MANTIS_FIFO_TP_4 (0x00 << 3) +#define MANTIS_FIFO_TP_8 (0x01 << 3) +#define MANTIS_FIFO_TP_16 (0x02 << 3) +#define MANTIS_FIFO_EN (0x01 << 2) +#define MANTIS_DCAP_EN (0x01 << 1) +#define MANTIS_RISC_EN (0x01 << 0) + +/* DEBUG */ +#define MANTIS_DEBUGREG 0x0c +#define MANTIS_DATINV (0x0e << 7) +#define MANTIS_TOP_DEBUGSEL (0x07 << 4) +#define MANTIS_PCMCIA_DEBUGSEL (0x0f << 0) + +#define MANTIS_RISC_START 0x10 +#define MANTIS_RISC_PC 0x14 + +/* I2C */ +#define MANTIS_I2CDATA_CTL 0x18 +#define MANTIS_I2C_RATE_1 (0x00 << 6) +#define MANTIS_I2C_RATE_2 (0x01 << 6) +#define MANTIS_I2C_RATE_3 (0x02 << 6) +#define MANTIS_I2C_RATE_4 (0x03 << 6) +#define MANTIS_I2C_STOP (0x01 << 5) +#define MANTIS_I2C_PGMODE (0x01 << 3) + +/* DATA */ +#define MANTIS_CMD_DATA_R1 0x20 +#define MANTIS_CMD_DATA_3 (0xff << 24) +#define MANTIS_CMD_DATA_2 (0xff << 16) +#define MANTIS_CMD_DATA_1 (0xff << 8) +#define MANTIS_CMD_DATA_0 (0xff << 0) + +#define MANTIS_CMD_DATA_R2 0x24 +#define MANTIS_CMD_DATA_7 (0xff << 24) +#define MANTIS_CMD_DATA_6 (0xff << 16) +#define MANTIS_CMD_DATA_5 (0xff << 8) +#define MANTIS_CMD_DATA_4 (0xff << 0) + +#define MANTIS_CONTROL 0x28 +#define MANTIS_DET (0x01 << 7) +#define MANTIS_DAT_CF_EN (0x01 << 6) +#define MANTIS_ACS (0x03 << 4) +#define MANTIS_VCCEN (0x01 << 3) +#define MANTIS_BYPASS (0x01 << 2) +#define MANTIS_MRST (0x01 << 1) +#define MANTIS_CRST_INT (0x01 << 0) + +#define MANTIS_GPIF_CFGSLA 0x84 +#define MANTIS_GPIF_WAITSMPL (0x07 << 28) +#define MANTIS_GPIF_BYTEADDRSUB (0x01 << 25) +#define MANTIS_GPIF_WAITPOL (0x01 << 24) +#define MANTIS_GPIF_NCDELAY (0x07 << 20) +#define MANTIS_GPIF_RW2CSDELAY (0x07 << 16) +#define MANTIS_GPIF_SLFTIMEDMODE (0x01 << 15) +#define MANTIS_GPIF_SLFTIMEDDELY (0x7f << 8) +#define MANTIS_GPIF_DEVTYPE (0x07 << 4) +#define MANTIS_GPIF_BIGENDIAN (0x01 << 3) +#define MANTIS_GPIF_FETCHCMD (0x03 << 1) +#define MANTIS_GPIF_HWORDDEV (0x01 << 0) + +#define MANTIS_GPIF_WSTOPER 0x90 +#define MANTIS_GPIF_WSTOPERWREN3 (0x01 << 31) +#define MANTIS_GPIF_PARBOOTN (0x01 << 29) +#define MANTIS_GPIF_WSTOPERSLID3 (0x1f << 24) +#define MANTIS_GPIF_WSTOPERWREN2 (0x01 << 23) +#define MANTIS_GPIF_WSTOPERSLID2 (0x1f << 16) +#define MANTIS_GPIF_WSTOPERWREN1 (0x01 << 15) +#define MANTIS_GPIF_WSTOPERSLID1 (0x1f << 8) +#define MANTIS_GPIF_WSTOPERWREN0 (0x01 << 7) +#define MANTIS_GPIF_WSTOPERSLID0 (0x1f << 0) + +#define MANTIS_GPIF_CS2RW 0x94 +#define MANTIS_GPIF_CS2RWWREN3 (0x01 << 31) +#define MANTIS_GPIF_CS2RWDELY3 (0x3f << 24) +#define MANTIS_GPIF_CS2RWWREN2 (0x01 << 23) +#define MANTIS_GPIF_CS2RWDELY2 (0x3f << 16) +#define MANTIS_GPIF_CS2RWWREN1 (0x01 << 15) +#define MANTIS_GPIF_CS2RWDELY1 (0x3f << 8) +#define MANTIS_GPIF_CS2RWWREN0 (0x01 << 7) +#define MANTIS_GPIF_CS2RWDELY0 (0x3f << 0) + +#define MANTIS_GPIF_IRQCFG 0x98 +#define MANTIS_GPIF_IRQPOL (0x01 << 8) +#define MANTIS_MASK_WRACK (0x01 << 7) +#define MANTIS_MASK_BRRDY (0x01 << 6) +#define MANTIS_MASK_OVFLW (0x01 << 5) +#define MANTIS_MASK_OTHERR (0x01 << 4) +#define MANTIS_MASK_WSTO (0x01 << 3) +#define MANTIS_MASK_EXTIRQ (0x01 << 2) +#define MANTIS_MASK_PLUGIN (0x01 << 1) +#define MANTIS_MASK_PLUGOUT (0x01 << 0) + +#define MANTIS_GPIF_STATUS 0x9c +#define MANTIS_SBUF_KILLOP (0x01 << 15) +#define MANTIS_SBUF_OPDONE (0x01 << 14) +#define MANTIS_SBUF_EMPTY (0x01 << 13) +#define MANTIS_GPIF_DETSTAT (0x01 << 9) +#define MANTIS_GPIF_INTSTAT (0x01 << 8) +#define MANTIS_GPIF_WRACK (0x01 << 7) +#define MANTIS_GPIF_BRRDY (0x01 << 6) +#define MANTIS_SBUF_OVFLW (0x01 << 5) +#define MANTIS_GPIF_OTHERR (0x01 << 4) +#define MANTIS_SBUF_WSTO (0x01 << 3) +#define MANTIS_GPIF_EXTIRQ (0x01 << 2) +#define MANTIS_CARD_PLUGIN (0x01 << 1) +#define MANTIS_CARD_PLUGOUT (0x01 << 0) + +#define MANTIS_GPIF_BRADDR 0xa0 +#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) +#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) +#define MANTIS_GPIF_BR_ADDR (0xfffffff << 0) + +#define MANTIS_GPIF_BRBYTES 0xa4 +#define MANTIS_GPIF_BRCNT (0xfff << 0) + +#define MANTIS_PCMCIA_RESET 0xa8 +#define MANTIS_PCMCIA_RSTVAL (0xff << 0) + +#define MANTIS_CARD_RESET 0xac + +#define MANTIS_GPIF_ADDR 0xb0 +#define MANTIS_GPIF_HIFRDWRN (0x01 << 31) +#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) +#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) +#define MANTIS_GPIF_HIFADDR (0xfffffff << 0) + +#define MANTIS_GPIF_DOUT 0xb4 +#define MANTIS_GPIF_HIFDOUT (0xfffffff << 0) + +#define MANTIS_GPIF_DIN 0xb8 +#define MANTIS_GPIF_HIFDIN (0xfffffff << 0) + +#define MANTIS_GPIF_SPARE 0xbc +#define MANTIS_GPIF_LOGICRD (0xffff << 16) +#define MANTIS_GPIF_LOGICRW (0xffff << 0) + +#endif /* __MANTIS_REG_H */ diff --git a/drivers/media/pci/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c new file mode 100644 index 000000000000..18340dafa426 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_uart.c @@ -0,0 +1,188 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_reg.h" +#include "mantis_uart.h" + +struct mantis_uart_params { + enum mantis_baud baud_rate; + enum mantis_parity parity; +}; + +static struct { + char string[7]; +} rates[5] = { + { "9600" }, + { "19200" }, + { "38400" }, + { "57600" }, + { "115200" } +}; + +static struct { + char string[5]; +} parity[3] = { + { "NONE" }, + { "ODD" }, + { "EVEN" } +}; + +#define UART_MAX_BUF 16 + +int mantis_uart_read(struct mantis_pci *mantis, u8 *data) +{ + struct mantis_hwconfig *config = mantis->hwconfig; + u32 stat = 0, i; + + /* get data */ + for (i = 0; i < (config->bytes + 1); i++) { + + stat = mmread(MANTIS_UART_STAT); + + if (stat & MANTIS_UART_RXFIFO_FULL) { + dprintk(MANTIS_ERROR, 1, "RX Fifo FULL"); + } + data[i] = mmread(MANTIS_UART_RXD) & 0x3f; + + dprintk(MANTIS_DEBUG, 1, "Reading ... <%02x>", data[i] & 0x3f); + + if (data[i] & (1 << 7)) { + dprintk(MANTIS_ERROR, 1, "UART framing error"); + return -EINVAL; + } + if (data[i] & (1 << 6)) { + dprintk(MANTIS_ERROR, 1, "UART parity error"); + return -EINVAL; + } + } + + return 0; +} + +static void mantis_uart_work(struct work_struct *work) +{ + struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work); + struct mantis_hwconfig *config = mantis->hwconfig; + u8 buf[16]; + int i; + + mantis_uart_read(mantis, buf); + + for (i = 0; i < (config->bytes + 1); i++) + dprintk(MANTIS_INFO, 1, "UART BUF:%d <%02x> ", i, buf[i]); + + dprintk(MANTIS_DEBUG, 0, "\n"); +} + +static int mantis_uart_setup(struct mantis_pci *mantis, + struct mantis_uart_params *params) +{ + u32 reg; + + mmwrite((mmread(MANTIS_UART_CTL) | (params->parity & 0x3)), MANTIS_UART_CTL); + + reg = mmread(MANTIS_UART_BAUD); + + switch (params->baud_rate) { + case MANTIS_BAUD_9600: + reg |= 0xd8; + break; + case MANTIS_BAUD_19200: + reg |= 0x6c; + break; + case MANTIS_BAUD_38400: + reg |= 0x36; + break; + case MANTIS_BAUD_57600: + reg |= 0x23; + break; + case MANTIS_BAUD_115200: + reg |= 0x11; + break; + default: + return -EINVAL; + } + + mmwrite(reg, MANTIS_UART_BAUD); + + return 0; +} + +int mantis_uart_init(struct mantis_pci *mantis) +{ + struct mantis_hwconfig *config = mantis->hwconfig; + struct mantis_uart_params params; + + /* default parity: */ + params.baud_rate = config->baud_rate; + params.parity = config->parity; + dprintk(MANTIS_INFO, 1, "Initializing UART @ %sbps parity:%s", + rates[params.baud_rate].string, + parity[params.parity].string); + + init_waitqueue_head(&mantis->uart_wq); + spin_lock_init(&mantis->uart_lock); + + INIT_WORK(&mantis->uart_work, mantis_uart_work); + + /* disable interrupt */ + mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); + + mantis_uart_setup(mantis, ¶ms); + + /* default 1 byte */ + mmwrite((mmread(MANTIS_UART_BAUD) | (config->bytes << 8)), MANTIS_UART_BAUD); + + /* flush buffer */ + mmwrite((mmread(MANTIS_UART_CTL) | MANTIS_UART_RXFLUSH), MANTIS_UART_CTL); + + /* enable interrupt */ + mmwrite(mmread(MANTIS_INT_MASK) | 0x800, MANTIS_INT_MASK); + mmwrite(mmread(MANTIS_UART_CTL) | MANTIS_UART_RXINT, MANTIS_UART_CTL); + + schedule_work(&mantis->uart_work); + dprintk(MANTIS_DEBUG, 1, "UART successfully initialized"); + + return 0; +} +EXPORT_SYMBOL_GPL(mantis_uart_init); + +void mantis_uart_exit(struct mantis_pci *mantis) +{ + /* disable interrupt */ + mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); + flush_work_sync(&mantis->uart_work); +} +EXPORT_SYMBOL_GPL(mantis_uart_exit); diff --git a/drivers/media/pci/mantis/mantis_uart.h b/drivers/media/pci/mantis/mantis_uart.h new file mode 100644 index 000000000000..ffb62a0a5a13 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_uart.h @@ -0,0 +1,58 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_UART_H +#define __MANTIS_UART_H + +#define MANTIS_UART_CTL 0xe0 +#define MANTIS_UART_RXINT (1 << 4) +#define MANTIS_UART_RXFLUSH (1 << 2) + +#define MANTIS_UART_RXD 0xe8 +#define MANTIS_UART_BAUD 0xec + +#define MANTIS_UART_STAT 0xf0 +#define MANTIS_UART_RXFIFO_DATA (1 << 7) +#define MANTIS_UART_RXFIFO_EMPTY (1 << 6) +#define MANTIS_UART_RXFIFO_FULL (1 << 3) +#define MANTIS_UART_FRAME_ERR (1 << 2) +#define MANTIS_UART_PARITY_ERR (1 << 1) +#define MANTIS_UART_RXTHRESH_INT (1 << 0) + +enum mantis_baud { + MANTIS_BAUD_9600 = 0, + MANTIS_BAUD_19200, + MANTIS_BAUD_38400, + MANTIS_BAUD_57600, + MANTIS_BAUD_115200 +}; + +enum mantis_parity { + MANTIS_PARITY_NONE = 0, + MANTIS_PARITY_EVEN, + MANTIS_PARITY_ODD, +}; + +struct mantis_pci; + +extern int mantis_uart_init(struct mantis_pci *mantis); +extern void mantis_uart_exit(struct mantis_pci *mantis); + +#endif /* __MANTIS_UART_H */ diff --git a/drivers/media/pci/mantis/mantis_vp1033.c b/drivers/media/pci/mantis/mantis_vp1033.c new file mode 100644 index 000000000000..ad013e93ed11 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp1033.c @@ -0,0 +1,212 @@ +/* + Mantis VP-1033 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "stv0299.h" +#include "mantis_common.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" +#include "mantis_vp1033.h" +#include "mantis_reg.h" + +u8 lgtdqcs001f_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x2a, + 0x05, 0x85, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0x00, + 0x0c, 0x01, + 0x0d, 0x81, + 0x0e, 0x44, + 0x0f, 0x94, + 0x10, 0x3c, + 0x11, 0x84, + 0x12, 0xb9, + 0x13, 0xb5, + 0x14, 0x4f, + 0x15, 0xc9, + 0x16, 0x80, + 0x17, 0x36, + 0x18, 0xfb, + 0x19, 0xcf, + 0x1a, 0xbc, + 0x1c, 0x2b, + 0x1d, 0x27, + 0x1e, 0x00, + 0x1f, 0x0b, + 0x20, 0xa1, + 0x21, 0x60, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x05, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x13, + 0xff, 0xff, +}; + +#define MANTIS_MODEL_NAME "VP-1033" +#define MANTIS_DEV_TYPE "DVB-S/DSS" + +int lgtdqcs001f_tuner_set(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mantis_pci *mantis = fe->dvb->priv; + struct i2c_adapter *adapter = &mantis->adapter; + + u8 buf[4]; + u32 div; + + + struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf)}; + + div = p->frequency / 250; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x83; + buf[3] = 0xc0; + + if (p->frequency < 1531000) + buf[3] |= 0x04; + else + buf[3] &= ~0x04; + if (i2c_transfer(adapter, &msg, 1) < 0) { + dprintk(MANTIS_ERROR, 1, "Write: I2C Transfer failed"); + return -EIO; + } + msleep_interruptible(100); + + return 0; +} + +int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; + bclk = 0x47; + } else if (srate < 3000000) { + aclk = 0xb7; + bclk = 0x4b; + } else if (srate < 7000000) { + aclk = 0xb7; + bclk = 0x4f; + } else if (srate < 14000000) { + aclk = 0xb7; + bclk = 0x53; + } else if (srate < 30000000) { + aclk = 0xb6; + bclk = 0x53; + } else if (srate < 45000000) { + aclk = 0xb4; + bclk = 0x51; + } + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, ratio & 0xf0); + + return 0; +} + +struct stv0299_config lgtdqcs001f_config = { + .demod_address = 0x68, + .inittab = lgtdqcs001f_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = lgtdqcs001f_set_symbol_rate, +}; + +static int vp1033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +{ + struct i2c_adapter *adapter = &mantis->adapter; + + int err = 0; + + err = mantis_frontend_power(mantis, POWER_ON); + if (err == 0) { + mantis_frontend_soft_reset(mantis); + msleep(250); + + dprintk(MANTIS_ERROR, 1, "Probing for STV0299 (DVB-S)"); + fe = dvb_attach(stv0299_attach, &lgtdqcs001f_config, adapter); + + if (fe) { + fe->ops.tuner_ops.set_params = lgtdqcs001f_tuner_set; + dprintk(MANTIS_ERROR, 1, "found STV0299 DVB-S frontend @ 0x%02x", + lgtdqcs001f_config.demod_address); + + dprintk(MANTIS_ERROR, 1, "Mantis DVB-S STV0299 frontend attach success"); + } else { + return -1; + } + } else { + dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", + adapter->name, + err); + + return -EIO; + } + mantis->fe = fe; + dprintk(MANTIS_ERROR, 1, "Done!"); + + return 0; +} + +struct mantis_hwconfig vp1033_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_204, + + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, + + .frontend_init = vp1033_frontend_init, + .power = GPIF_A12, + .reset = GPIF_A13, +}; diff --git a/drivers/media/pci/mantis/mantis_vp1033.h b/drivers/media/pci/mantis/mantis_vp1033.h new file mode 100644 index 000000000000..7daaa1bf127d --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp1033.h @@ -0,0 +1,30 @@ +/* + Mantis VP-1033 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP1033_H +#define __MANTIS_VP1033_H + +#include "mantis_common.h" + +#define MANTIS_VP_1033_DVB_S 0x0016 + +extern struct mantis_hwconfig vp1033_config; + +#endif /* __MANTIS_VP1033_H */ diff --git a/drivers/media/pci/mantis/mantis_vp1034.c b/drivers/media/pci/mantis/mantis_vp1034.c new file mode 100644 index 000000000000..430ae84ce528 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp1034.c @@ -0,0 +1,120 @@ +/* + Mantis VP-1034 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mb86a16.h" +#include "mantis_common.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" +#include "mantis_vp1034.h" +#include "mantis_reg.h" + +struct mb86a16_config vp1034_mb86a16_config = { + .demod_address = 0x08, + .set_voltage = vp1034_set_voltage, +}; + +#define MANTIS_MODEL_NAME "VP-1034" +#define MANTIS_DEV_TYPE "DVB-S/DSS" + +int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct mantis_pci *mantis = fe->dvb->priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + dprintk(MANTIS_ERROR, 1, "Polarization=[13V]"); + mantis_gpio_set_bits(mantis, 13, 1); + mantis_gpio_set_bits(mantis, 14, 0); + break; + case SEC_VOLTAGE_18: + dprintk(MANTIS_ERROR, 1, "Polarization=[18V]"); + mantis_gpio_set_bits(mantis, 13, 1); + mantis_gpio_set_bits(mantis, 14, 1); + break; + case SEC_VOLTAGE_OFF: + dprintk(MANTIS_ERROR, 1, "Frontend (dummy) POWERDOWN"); + break; + default: + dprintk(MANTIS_ERROR, 1, "Invalid = (%d)", (u32) voltage); + return -EINVAL; + } + mmwrite(0x00, MANTIS_GPIF_DOUT); + + return 0; +} + +static int vp1034_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +{ + struct i2c_adapter *adapter = &mantis->adapter; + + int err = 0; + + err = mantis_frontend_power(mantis, POWER_ON); + if (err == 0) { + mantis_frontend_soft_reset(mantis); + msleep(250); + + dprintk(MANTIS_ERROR, 1, "Probing for MB86A16 (DVB-S/DSS)"); + fe = dvb_attach(mb86a16_attach, &vp1034_mb86a16_config, adapter); + if (fe) { + dprintk(MANTIS_ERROR, 1, + "found MB86A16 DVB-S/DSS frontend @0x%02x", + vp1034_mb86a16_config.demod_address); + + } else { + return -1; + } + } else { + dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", + adapter->name, + err); + + return -EIO; + } + mantis->fe = fe; + dprintk(MANTIS_ERROR, 1, "Done!"); + + return 0; +} + +struct mantis_hwconfig vp1034_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_204, + + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, + + .frontend_init = vp1034_frontend_init, + .power = GPIF_A12, + .reset = GPIF_A13, +}; diff --git a/drivers/media/pci/mantis/mantis_vp1034.h b/drivers/media/pci/mantis/mantis_vp1034.h new file mode 100644 index 000000000000..323f38ef8e3d --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp1034.h @@ -0,0 +1,33 @@ +/* + Mantis VP-1034 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP1034_H +#define __MANTIS_VP1034_H + +#include "dvb_frontend.h" +#include "mantis_common.h" + + +#define MANTIS_VP_1034_DVB_S 0x0014 + +extern struct mantis_hwconfig vp1034_config; +extern int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + +#endif /* __MANTIS_VP1034_H */ diff --git a/drivers/media/pci/mantis/mantis_vp1041.c b/drivers/media/pci/mantis/mantis_vp1041.c new file mode 100644 index 000000000000..07aa887a4b4a --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp1041.c @@ -0,0 +1,357 @@ +/* + Mantis VP-1041 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "mantis_common.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" +#include "mantis_vp1041.h" +#include "stb0899_reg.h" +#include "stb0899_drv.h" +#include "stb0899_cfg.h" +#include "stb6100_cfg.h" +#include "stb6100.h" +#include "lnbp21.h" + +#define MANTIS_MODEL_NAME "VP-1041" +#define MANTIS_DEV_TYPE "DSS/DVB-S/DVB-S2" + +static const struct stb0899_s1_reg vp1041_stb0899_s1_init_1[] = { + + /* 0x0000000b, *//* SYSREG */ + { STB0899_DEV_ID , 0x30 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x99 }, + { STB0899_DISF22RX , 0xa8 }, + /* SYSREG ? */ + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0xfe }, + { STB0899_IRQSTATUS_2 , 0x03 }, + { STB0899_IRQSTATUS_1 , 0x7c }, + { STB0899_IRQSTATUS_0 , 0xf4 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x58 }, + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x33 }, + { STB0899_IOPVALUE3 , 0x6d }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x60 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x01 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x01 }, + { STB0899_AGC1REF , 0x10 }, + { STB0899_RTC , 0x23 }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x34 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xf7 }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xf1 }, + { STB0899_LDT2 , 0xe3 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfd }, + { STB0899_QDCCOMP , 0xff }, + { STB0899_POWERI , 0x0c }, + { STB0899_POWERQ , 0x0f }, + { STB0899_RCOMP , 0x6c }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x06 }, + { STB0899_AGC2I2 , 0x00 }, + { STB0899_TLIR , 0x30 }, + { STB0899_RTF , 0x7f }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xbc }, + { STB0899_CFRM , 0xea }, + { STB0899_CFRL , 0x31 }, + { STB0899_NIRM , 0x2b }, + { STB0899_NIRL , 0x80 }, + { STB0899_ISYMB , 0x1d }, + { STB0899_QSYMB , 0xa6 }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0x02 }, + { STB0899_EQUAQ1 , 0xff }, + { STB0899_EQUAI2 , 0x04 }, + { STB0899_EQUAQ2 , 0x05 }, + { STB0899_EQUAI3 , 0x02 }, + { STB0899_EQUAQ3 , 0xfd }, + { STB0899_EQUAI4 , 0x03 }, + { STB0899_EQUAQ4 , 0x07 }, + { STB0899_EQUAI5 , 0x08 }, + { STB0899_EQUAQ5 , 0xf5 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0x86 }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x0a }, + { STB0899_ECNT3L , 0xad }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xb0 }, + { STB0899_VTH23 , 0x7a }, + { STB0899_VTH34 , 0x58 }, + { STB0899_VTH56 , 0x38 }, + { STB0899_VTH67 , 0x34 }, + { STB0899_VTH78 , 0x24 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x41 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x00 }, + { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x1b }, + { STB0899_TSLLSTKL , 0xb3 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0x00 }, + { STB0899_PCKLENUL , 0xbc }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xbd }, + { STB0899_TSSTATUS , 0x90 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x95 }, + { STB0899_ERRCTRL3 , 0x8d }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x19 }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0xf0 }, + { STB0899_MATSTRL , 0x02 }, + { STB0899_UPLSTRM , 0x45 }, + { STB0899_UPLSTRL , 0x60 }, + { STB0899_DFLSTRM , 0xe3 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x47 }, + { STB0899_SYNCDSTRM , 0x05 }, + { STB0899_SYNCDSTRL , 0x18 }, + { STB0899_CFGPDELSTATUS1 , 0x19 }, + { STB0899_CFGPDELSTATUS2 , 0x2b }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x01 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +struct stb0899_config vp1041_stb0899_config = { + .init_dev = vp1041_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = vp1041_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .demod_address = 0x68, /* 0xd0 >> 1 */ + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_ON, /* 1 */ + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL, +}; + +struct stb6100_config vp1041_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + +static int vp1041_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +{ + struct i2c_adapter *adapter = &mantis->adapter; + + int err = 0; + + err = mantis_frontend_power(mantis, POWER_ON); + if (err == 0) { + mantis_frontend_soft_reset(mantis); + msleep(250); + mantis->fe = dvb_attach(stb0899_attach, &vp1041_stb0899_config, adapter); + if (mantis->fe) { + dprintk(MANTIS_ERROR, 1, + "found STB0899 DVB-S/DVB-S2 frontend @0x%02x", + vp1041_stb0899_config.demod_address); + + if (dvb_attach(stb6100_attach, mantis->fe, &vp1041_stb6100_config, adapter)) { + if (!dvb_attach(lnbp21_attach, mantis->fe, adapter, 0, 0)) + dprintk(MANTIS_ERROR, 1, "No LNBP21 found!"); + } + } else { + return -EREMOTEIO; + } + } else { + dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", + adapter->name, + err); + + return -EIO; + } + + + dprintk(MANTIS_ERROR, 1, "Done!"); + + return 0; +} + +struct mantis_hwconfig vp1041_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_188, + + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, + + .frontend_init = vp1041_frontend_init, + .power = GPIF_A12, + .reset = GPIF_A13, +}; diff --git a/drivers/media/pci/mantis/mantis_vp1041.h b/drivers/media/pci/mantis/mantis_vp1041.h new file mode 100644 index 000000000000..1ae5b3de8081 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp1041.h @@ -0,0 +1,33 @@ +/* + Mantis VP-1041 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP1041_H +#define __MANTIS_VP1041_H + +#include "mantis_common.h" + +#define MANTIS_VP_1041_DVB_S2 0x0031 +#define SKYSTAR_HD2_10 0x0001 +#define SKYSTAR_HD2_20 0x0003 +#define CINERGY_S2_PCI_HD 0x1179 + +extern struct mantis_hwconfig vp1041_config; + +#endif /* __MANTIS_VP1041_H */ diff --git a/drivers/media/pci/mantis/mantis_vp2033.c b/drivers/media/pci/mantis/mantis_vp2033.c new file mode 100644 index 000000000000..1ca6837fbe46 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp2033.c @@ -0,0 +1,188 @@ +/* + Mantis VP-2033 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "tda1002x.h" +#include "mantis_common.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" +#include "mantis_vp2033.h" + +#define MANTIS_MODEL_NAME "VP-2033" +#define MANTIS_DEV_TYPE "DVB-C" + +struct tda1002x_config vp2033_tda1002x_cu1216_config = { + .demod_address = 0x18 >> 1, + .invert = 1, +}; + +struct tda10023_config vp2033_tda10023_cu1216_config = { + .demod_address = 0x18 >> 1, + .invert = 1, +}; + +static u8 read_pwm(struct mantis_pci *mantis) +{ + struct i2c_adapter *adapter = &mantis->adapter; + + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { + {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, + {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} + }; + + if ((i2c_transfer(adapter, msg, 2) != 2) + || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mantis_pci *mantis = fe->dvb->priv; + struct i2c_adapter *adapter = &mantis->adapter; + + u8 buf[6]; + struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; + int i; + +#define CU1216_IF 36125000 +#define TUNER_MUL 62500 + + u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0xce; + buf[3] = (p->frequency < 150000000 ? 0x01 : + p->frequency < 445000000 ? 0x02 : 0x04); + buf[4] = 0xde; + buf[5] = 0x20; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(adapter, &msg, 1) != 1) + return -EIO; + + /* wait for the pll lock */ + msg.flags = I2C_M_RD; + msg.len = 1; + for (i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) + break; + + msleep(10); + } + + /* switch the charge pump to the lower current */ + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[2]; + buf[2] &= ~0x40; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(adapter, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static int vp2033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +{ + struct i2c_adapter *adapter = &mantis->adapter; + + int err = 0; + + err = mantis_frontend_power(mantis, POWER_ON); + if (err == 0) { + mantis_frontend_soft_reset(mantis); + msleep(250); + + dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); + fe = dvb_attach(tda10021_attach, &vp2033_tda1002x_cu1216_config, + adapter, + read_pwm(mantis)); + + if (fe) { + dprintk(MANTIS_ERROR, 1, + "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", + vp2033_tda1002x_cu1216_config.demod_address); + } else { + fe = dvb_attach(tda10023_attach, &vp2033_tda10023_cu1216_config, + adapter, + read_pwm(mantis)); + + if (fe) { + dprintk(MANTIS_ERROR, 1, + "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", + vp2033_tda1002x_cu1216_config.demod_address); + } + } + + if (fe) { + fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; + dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); + } else { + return -1; + } + } else { + dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", + adapter->name, + err); + + return -EIO; + } + + mantis->fe = fe; + dprintk(MANTIS_DEBUG, 1, "Done!"); + + return 0; +} + +struct mantis_hwconfig vp2033_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_204, + + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, + + .frontend_init = vp2033_frontend_init, + .power = GPIF_A12, + .reset = GPIF_A13, +}; diff --git a/drivers/media/pci/mantis/mantis_vp2033.h b/drivers/media/pci/mantis/mantis_vp2033.h new file mode 100644 index 000000000000..c55242b79d54 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp2033.h @@ -0,0 +1,30 @@ +/* + Mantis VP-2033 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP2033_H +#define __MANTIS_VP2033_H + +#include "mantis_common.h" + +#define MANTIS_VP_2033_DVB_C 0x0008 + +extern struct mantis_hwconfig vp2033_config; + +#endif /* __MANTIS_VP2033_H */ diff --git a/drivers/media/pci/mantis/mantis_vp2040.c b/drivers/media/pci/mantis/mantis_vp2040.c new file mode 100644 index 000000000000..d480741afd78 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp2040.c @@ -0,0 +1,187 @@ +/* + Mantis VP-2040 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "tda1002x.h" +#include "mantis_common.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" +#include "mantis_vp2040.h" + +#define MANTIS_MODEL_NAME "VP-2040" +#define MANTIS_DEV_TYPE "DVB-C" + +struct tda1002x_config vp2040_tda1002x_cu1216_config = { + .demod_address = 0x18 >> 1, + .invert = 1, +}; + +struct tda10023_config vp2040_tda10023_cu1216_config = { + .demod_address = 0x18 >> 1, + .invert = 1, +}; + +static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mantis_pci *mantis = fe->dvb->priv; + struct i2c_adapter *adapter = &mantis->adapter; + + u8 buf[6]; + struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; + int i; + +#define CU1216_IF 36125000 +#define TUNER_MUL 62500 + + u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0xce; + buf[3] = (p->frequency < 150000000 ? 0x01 : + p->frequency < 445000000 ? 0x02 : 0x04); + buf[4] = 0xde; + buf[5] = 0x20; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(adapter, &msg, 1) != 1) + return -EIO; + + /* wait for the pll lock */ + msg.flags = I2C_M_RD; + msg.len = 1; + for (i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) + break; + + msleep(10); + } + + /* switch the charge pump to the lower current */ + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[2]; + buf[2] &= ~0x40; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(adapter, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static u8 read_pwm(struct mantis_pci *mantis) +{ + struct i2c_adapter *adapter = &mantis->adapter; + + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { + {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, + {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} + }; + + if ((i2c_transfer(adapter, msg, 2) != 2) + || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static int vp2040_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +{ + struct i2c_adapter *adapter = &mantis->adapter; + + int err = 0; + + err = mantis_frontend_power(mantis, POWER_ON); + if (err == 0) { + mantis_frontend_soft_reset(mantis); + msleep(250); + + dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); + fe = dvb_attach(tda10021_attach, &vp2040_tda1002x_cu1216_config, + adapter, + read_pwm(mantis)); + + if (fe) { + dprintk(MANTIS_ERROR, 1, + "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", + vp2040_tda1002x_cu1216_config.demod_address); + } else { + fe = dvb_attach(tda10023_attach, &vp2040_tda10023_cu1216_config, + adapter, + read_pwm(mantis)); + + if (fe) { + dprintk(MANTIS_ERROR, 1, + "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", + vp2040_tda1002x_cu1216_config.demod_address); + } + } + + if (fe) { + fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; + dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); + } else { + return -1; + } + } else { + dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", + adapter->name, + err); + + return -EIO; + } + mantis->fe = fe; + dprintk(MANTIS_DEBUG, 1, "Done!"); + + return 0; +} + +struct mantis_hwconfig vp2040_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_204, + + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, + + .frontend_init = vp2040_frontend_init, + .power = GPIF_A12, + .reset = GPIF_A13, +}; diff --git a/drivers/media/pci/mantis/mantis_vp2040.h b/drivers/media/pci/mantis/mantis_vp2040.h new file mode 100644 index 000000000000..d125e219b685 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp2040.h @@ -0,0 +1,32 @@ +/* + Mantis VP-2040 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP2040_H +#define __MANTIS_VP2040_H + +#include "mantis_common.h" + +#define MANTIS_VP_2040_DVB_C 0x0043 +#define CINERGY_C 0x1178 +#define CABLESTAR_HD2 0x0002 + +extern struct mantis_hwconfig vp2040_config; + +#endif /* __MANTIS_VP2040_H */ diff --git a/drivers/media/pci/mantis/mantis_vp3028.c b/drivers/media/pci/mantis/mantis_vp3028.c new file mode 100644 index 000000000000..4155c838a18a --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp3028.c @@ -0,0 +1,38 @@ +/* + Mantis VP-3028 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "mantis_common.h" +#include "mantis_vp3028.h" + +struct zl10353_config mantis_vp3028_config = { + .demod_address = 0x0f, +}; + +#define MANTIS_MODEL_NAME "VP-3028" +#define MANTIS_DEV_TYPE "DVB-T" + +struct mantis_hwconfig vp3028_mantis_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_188, + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, +}; diff --git a/drivers/media/pci/mantis/mantis_vp3028.h b/drivers/media/pci/mantis/mantis_vp3028.h new file mode 100644 index 000000000000..b07be6adc522 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp3028.h @@ -0,0 +1,33 @@ +/* + Mantis VP-3028 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP3028_H +#define __MANTIS_VP3028_H + +#include "dvb_frontend.h" +#include "mantis_common.h" +#include "zl10353.h" + +#define MANTIS_VP_3028_DVB_T 0x0028 + +extern struct zl10353_config mantis_vp3028_config; +extern struct mantis_hwconfig vp3028_mantis_config; + +#endif /* __MANTIS_VP3028_H */ diff --git a/drivers/media/pci/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c new file mode 100644 index 000000000000..c09308cd3ac6 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp3030.c @@ -0,0 +1,105 @@ +/* + Mantis VP-3030 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "zl10353.h" +#include "tda665x.h" +#include "mantis_common.h" +#include "mantis_ioc.h" +#include "mantis_dvb.h" +#include "mantis_vp3030.h" + +struct zl10353_config mantis_vp3030_config = { + .demod_address = 0x0f, +}; + +struct tda665x_config env57h12d5_config = { + .name = "ENV57H12D5 (ET-50DT)", + .addr = 0x60, + .frequency_min = 47000000, + .frequency_max = 862000000, + .frequency_offst = 3616667, + .ref_multiplier = 6, /* 1/6 MHz */ + .ref_divider = 100000, /* 1/6 MHz */ +}; + +#define MANTIS_MODEL_NAME "VP-3030" +#define MANTIS_DEV_TYPE "DVB-T" + + +static int vp3030_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +{ + struct i2c_adapter *adapter = &mantis->adapter; + struct mantis_hwconfig *config = mantis->hwconfig; + int err = 0; + + mantis_gpio_set_bits(mantis, config->reset, 0); + msleep(100); + err = mantis_frontend_power(mantis, POWER_ON); + msleep(100); + mantis_gpio_set_bits(mantis, config->reset, 1); + + if (err == 0) { + msleep(250); + dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); + fe = dvb_attach(zl10353_attach, &mantis_vp3030_config, adapter); + + if (!fe) + return -1; + + dvb_attach(tda665x_attach, fe, &env57h12d5_config, adapter); + } else { + dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", + adapter->name, + err); + + return -EIO; + + } + mantis->fe = fe; + dprintk(MANTIS_ERROR, 1, "Done!"); + + return 0; +} + +struct mantis_hwconfig vp3030_config = { + .model_name = MANTIS_MODEL_NAME, + .dev_type = MANTIS_DEV_TYPE, + .ts_size = MANTIS_TS_188, + + .baud_rate = MANTIS_BAUD_9600, + .parity = MANTIS_PARITY_NONE, + .bytes = 0, + + .frontend_init = vp3030_frontend_init, + .power = GPIF_A12, + .reset = GPIF_A13, + + .i2c_mode = MANTIS_BYTE_MODE +}; diff --git a/drivers/media/pci/mantis/mantis_vp3030.h b/drivers/media/pci/mantis/mantis_vp3030.h new file mode 100644 index 000000000000..5f12c4266277 --- /dev/null +++ b/drivers/media/pci/mantis/mantis_vp3030.h @@ -0,0 +1,30 @@ +/* + Mantis VP-3030 driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MANTIS_VP3030_H +#define __MANTIS_VP3030_H + +#include "mantis_common.h" + +#define MANTIS_VP_3030_DVB_T 0x0024 + +extern struct mantis_hwconfig vp3030_config; + +#endif /* __MANTIS_VP3030_H */ diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig new file mode 100644 index 000000000000..64c84702ba5c --- /dev/null +++ b/drivers/media/pci/ngene/Kconfig @@ -0,0 +1,13 @@ +config DVB_NGENE + tristate "Micronas nGene support" + depends on DVB_CORE && PCI && I2C + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_STV6110x if !DVB_FE_CUSTOMISE + select DVB_STV090x if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_DRXK if !DVB_FE_CUSTOMISE + select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE + ---help--- + Support for Micronas PCI express cards with nGene bridge. + diff --git a/drivers/media/pci/ngene/Makefile b/drivers/media/pci/ngene/Makefile new file mode 100644 index 000000000000..63997089f9d1 --- /dev/null +++ b/drivers/media/pci/ngene/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the nGene device driver +# + +ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o + +obj-$(CONFIG_DVB_NGENE) += ngene.o + +ccflags-y += -Idrivers/media/dvb-core/ +ccflags-y += -Idrivers/media/dvb-frontends/ +ccflags-y += -Idrivers/media/common/tuners/ + +# For the staging CI driver cxd2099 +ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c new file mode 100644 index 000000000000..a6cd6959ad19 --- /dev/null +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -0,0 +1,823 @@ +/* + * ngene-cards.c: nGene PCIe bridge driver - card specific info + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include + +#include "ngene.h" + +/* demods/tuners */ +#include "stv6110x.h" +#include "stv090x.h" +#include "lnbh24.h" +#include "lgdt330x.h" +#include "mt2131.h" +#include "tda18271c2dd.h" +#include "drxk.h" +#include "drxd.h" +#include "dvb-pll.h" + + +/****************************************************************************/ +/* Demod/tuner attachment ***************************************************/ +/****************************************************************************/ + +static int tuner_attach_stv6110(struct ngene_channel *chan) +{ + struct i2c_adapter *i2c; + struct stv090x_config *feconf = (struct stv090x_config *) + chan->dev->card_info->fe_config[chan->number]; + struct stv6110x_config *tunerconf = (struct stv6110x_config *) + chan->dev->card_info->tuner_config[chan->number]; + struct stv6110x_devctl *ctl; + + /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ + if (chan->number < 2) + i2c = &chan->dev->channel[0].i2c_adapter; + else + i2c = &chan->dev->channel[1].i2c_adapter; + + ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c); + if (ctl == NULL) { + printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); + return -ENODEV; + } + + feconf->tuner_init = ctl->tuner_init; + feconf->tuner_sleep = ctl->tuner_sleep; + feconf->tuner_set_mode = ctl->tuner_set_mode; + feconf->tuner_set_frequency = ctl->tuner_set_frequency; + feconf->tuner_get_frequency = ctl->tuner_get_frequency; + feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; + feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; + feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; + feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; + feconf->tuner_set_refclk = ctl->tuner_set_refclk; + feconf->tuner_get_status = ctl->tuner_get_status; + + return 0; +} + + +static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct ngene_channel *chan = fe->sec_priv; + int status; + + if (enable) { + down(&chan->dev->pll_mutex); + status = chan->gate_ctrl(fe, 1); + } else { + status = chan->gate_ctrl(fe, 0); + up(&chan->dev->pll_mutex); + } + return status; +} + +static int tuner_attach_tda18271(struct ngene_channel *chan) +{ + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + + i2c = &chan->dev->channel[0].i2c_adapter; + if (chan->fe->ops.i2c_gate_ctrl) + chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); + fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60); + if (chan->fe->ops.i2c_gate_ctrl) + chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); + if (!fe) { + printk(KERN_ERR "No TDA18271 found!\n"); + return -ENODEV; + } + + return 0; +} + +static int tuner_attach_probe(struct ngene_channel *chan) +{ + if (chan->demod_type == 0) + return tuner_attach_stv6110(chan); + if (chan->demod_type == 1) + return tuner_attach_tda18271(chan); + return -EINVAL; +} + +static int demod_attach_stv0900(struct ngene_channel *chan) +{ + struct i2c_adapter *i2c; + struct stv090x_config *feconf = (struct stv090x_config *) + chan->dev->card_info->fe_config[chan->number]; + + /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ + /* Note: Both adapters share the same i2c bus, but the demod */ + /* driver requires that each demod has its own i2c adapter */ + if (chan->number < 2) + i2c = &chan->dev->channel[0].i2c_adapter; + else + i2c = &chan->dev->channel[1].i2c_adapter; + + chan->fe = dvb_attach(stv090x_attach, feconf, i2c, + (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0 + : STV090x_DEMODULATOR_1); + if (chan->fe == NULL) { + printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); + return -ENODEV; + } + + /* store channel info */ + if (feconf->tuner_i2c_lock) + chan->fe->analog_demod_priv = chan; + + if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0, + 0, chan->dev->card_info->lnb[chan->number])) { + printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); + dvb_frontend_detach(chan->fe); + chan->fe = NULL; + return -ENODEV; + } + + return 0; +} + +static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock) +{ + struct ngene_channel *chan = fe->analog_demod_priv; + + if (lock) + down(&chan->dev->pll_mutex); + else + up(&chan->dev->pll_mutex); +} + +static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) +{ + struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1 } }; + return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; +} + +static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, + u16 reg, u8 *val) +{ + u8 msg[2] = {reg>>8, reg&0xff}; + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = msg, .len = 2}, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1} }; + return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +} + +static int port_has_stv0900(struct i2c_adapter *i2c, int port) +{ + u8 val; + if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0) + return 0; + return 1; +} + +static int port_has_drxk(struct i2c_adapter *i2c, int port) +{ + u8 val; + + if (i2c_read(i2c, 0x29+port, &val) < 0) + return 0; + return 1; +} + +static int demod_attach_drxk(struct ngene_channel *chan, + struct i2c_adapter *i2c) +{ + struct drxk_config config; + + memset(&config, 0, sizeof(config)); + config.microcode_name = "drxk_a3.mc"; + config.qam_demod_parameter_count = 4; + config.adr = 0x29 + (chan->number ^ 2); + + chan->fe = dvb_attach(drxk_attach, &config, i2c); + if (!chan->fe) { + printk(KERN_ERR "No DRXK found!\n"); + return -ENODEV; + } + chan->fe->sec_priv = chan; + chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl; + chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; + return 0; +} + +static int cineS2_probe(struct ngene_channel *chan) +{ + struct i2c_adapter *i2c; + struct stv090x_config *fe_conf; + u8 buf[3]; + struct i2c_msg i2c_msg = { .flags = 0, .buf = buf }; + int rc; + + /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ + if (chan->number < 2) + i2c = &chan->dev->channel[0].i2c_adapter; + else + i2c = &chan->dev->channel[1].i2c_adapter; + + if (port_has_stv0900(i2c, chan->number)) { + chan->demod_type = 0; + fe_conf = chan->dev->card_info->fe_config[chan->number]; + /* demod found, attach it */ + rc = demod_attach_stv0900(chan); + if (rc < 0 || chan->number < 2) + return rc; + + /* demod #2: reprogram outputs DPN1 & DPN2 */ + i2c_msg.addr = fe_conf->address; + i2c_msg.len = 3; + buf[0] = 0xf1; + switch (chan->number) { + case 2: + buf[1] = 0x5c; + buf[2] = 0xc2; + break; + case 3: + buf[1] = 0x61; + buf[2] = 0xcc; + break; + default: + return -ENODEV; + } + rc = i2c_transfer(i2c, &i2c_msg, 1); + if (rc != 1) { + printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n"); + return -EIO; + } + } else if (port_has_drxk(i2c, chan->number^2)) { + chan->demod_type = 1; + demod_attach_drxk(chan, i2c); + } else { + printk(KERN_ERR "No demod found on chan %d\n", chan->number); + return -ENODEV; + } + return 0; +} + + +static struct lgdt330x_config aver_m780 = { + .demod_address = 0xb2 >> 1, + .demod_chip = LGDT3303, + .serial_mpeg = 0x00, /* PARALLEL */ + .clock_polarity_flip = 1, +}; + +static struct mt2131_config m780_tunerconfig = { + 0xc0 >> 1 +}; + +/* A single func to attach the demo and tuner, rather than + * use two sep funcs like the current design mandates. + */ +static int demod_attach_lg330x(struct ngene_channel *chan) +{ + chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter); + if (chan->fe == NULL) { + printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n"); + return -ENODEV; + } + + dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter, + &m780_tunerconfig, 0); + + return (chan->fe) ? 0 : -ENODEV; +} + +static int demod_attach_drxd(struct ngene_channel *chan) +{ + struct drxd_config *feconf; + + feconf = chan->dev->card_info->fe_config[chan->number]; + + chan->fe = dvb_attach(drxd_attach, feconf, chan, + &chan->i2c_adapter, &chan->dev->pci_dev->dev); + if (!chan->fe) { + pr_err("No DRXD found!\n"); + return -ENODEV; + } + + if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, + &chan->i2c_adapter, + feconf->pll_type)) { + pr_err("No pll(%d) found!\n", feconf->pll_type); + return -ENODEV; + } + return 0; +} + +/****************************************************************************/ +/* EEPROM TAGS **************************************************************/ +/****************************************************************************/ + +#define MICNG_EE_START 0x0100 +#define MICNG_EE_END 0x0FF0 + +#define MICNG_EETAG_END0 0x0000 +#define MICNG_EETAG_END1 0xFFFF + +/* 0x0001 - 0x000F reserved for housekeeping */ +/* 0xFFFF - 0xFFFE reserved for housekeeping */ + +/* Micronas assigned tags + EEProm tags for hardware support */ + +#define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */ +#define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */ + +#define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */ +#define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */ + +/* Tag range for OEMs */ + +#define MICNG_EETAG_OEM_FIRST 0xC000 +#define MICNG_EETAG_OEM_LAST 0xFFEF + +static int i2c_write_eeprom(struct i2c_adapter *adapter, + u8 adr, u16 reg, u8 data) +{ + u8 m[3] = {(reg >> 8), (reg & 0xff), data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, + .len = sizeof(m)}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + pr_err(DEVICE_NAME ": Error writing EEPROM!\n"); + return -EIO; + } + return 0; +} + +static int i2c_read_eeprom(struct i2c_adapter *adapter, + u8 adr, u16 reg, u8 *data, int len) +{ + u8 msg[2] = {(reg >> 8), (reg & 0xff)}; + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = msg, .len = 2 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = len} }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + pr_err(DEVICE_NAME ": Error reading EEPROM\n"); + return -EIO; + } + return 0; +} + +static int ReadEEProm(struct i2c_adapter *adapter, + u16 Tag, u32 MaxLen, u8 *data, u32 *pLength) +{ + int status = 0; + u16 Addr = MICNG_EE_START, Length, tag = 0; + u8 EETag[3]; + + while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { + if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) + return -1; + tag = (EETag[0] << 8) | EETag[1]; + if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) + return -1; + if (tag == Tag) + break; + Addr += sizeof(u16) + 1 + EETag[2]; + } + if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { + pr_err(DEVICE_NAME + ": Reached EOEE @ Tag = %04x Length = %3d\n", + tag, EETag[2]); + return -1; + } + Length = EETag[2]; + if (Length > MaxLen) + Length = (u16) MaxLen; + if (Length > 0) { + Addr += sizeof(u16) + 1; + status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length); + if (!status) { + *pLength = EETag[2]; + if (Length < EETag[2]) + ; /*status=STATUS_BUFFER_OVERFLOW; */ + } + } + return status; +} + +static int WriteEEProm(struct i2c_adapter *adapter, + u16 Tag, u32 Length, u8 *data) +{ + int status = 0; + u16 Addr = MICNG_EE_START; + u8 EETag[3]; + u16 tag = 0; + int retry, i; + + while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { + if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) + return -1; + tag = (EETag[0] << 8) | EETag[1]; + if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) + return -1; + if (tag == Tag) + break; + Addr += sizeof(u16) + 1 + EETag[2]; + } + if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { + pr_err(DEVICE_NAME + ": Reached EOEE @ Tag = %04x Length = %3d\n", + tag, EETag[2]); + return -1; + } + + if (Length > EETag[2]) + return -EINVAL; + /* Note: We write the data one byte at a time to avoid + issues with page sizes. (which are different for + each manufacture and eeprom size) + */ + Addr += sizeof(u16) + 1; + for (i = 0; i < Length; i++, Addr++) { + status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]); + + if (status) + break; + + /* Poll for finishing write cycle */ + retry = 10; + while (retry) { + u8 Tmp; + + msleep(50); + status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1); + if (status) + break; + if (Tmp != data[i]) + pr_err(DEVICE_NAME + "eeprom write error\n"); + retry -= 1; + } + if (status) { + pr_err(DEVICE_NAME + ": Timeout polling eeprom\n"); + break; + } + } + return status; +} + +static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data) +{ + int stat; + u8 buf[2]; + u32 len = 0; + + stat = ReadEEProm(adapter, tag, 2, buf, &len); + if (stat) + return stat; + if (len != 2) + return -EINVAL; + + *data = (buf[0] << 8) | buf[1]; + return 0; +} + +static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data) +{ + int stat; + u8 buf[2]; + + buf[0] = data >> 8; + buf[1] = data & 0xff; + stat = WriteEEProm(adapter, tag, 2, buf); + if (stat) + return stat; + return 0; +} + +static s16 osc_deviation(void *priv, s16 deviation, int flag) +{ + struct ngene_channel *chan = priv; + struct i2c_adapter *adap = &chan->i2c_adapter; + u16 data = 0; + + if (flag) { + data = (u16) deviation; + pr_info(DEVICE_NAME ": write deviation %d\n", + deviation); + eeprom_write_ushort(adap, 0x1000 + chan->number, data); + } else { + if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data)) + data = 0; + pr_info(DEVICE_NAME ": read deviation %d\n", + (s16) data); + } + + return (s16) data; +} + +/****************************************************************************/ +/* Switch control (I2C gates, etc.) *****************************************/ +/****************************************************************************/ + + +static struct stv090x_config fe_cineS2 = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + + .diseqc_envelope_mode = true, + + .tuner_i2c_lock = cineS2_tuner_i2c_lock, +}; + +static struct stv090x_config fe_cineS2_2 = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x69, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + + .diseqc_envelope_mode = true, + + .tuner_i2c_lock = cineS2_tuner_i2c_lock, +}; + +static struct stv6110x_config tuner_cineS2_0 = { + .addr = 0x60, + .refclk = 27000000, + .clk_div = 1, +}; + +static struct stv6110x_config tuner_cineS2_1 = { + .addr = 0x63, + .refclk = 27000000, + .clk_div = 1, +}; + +static struct ngene_info ngene_info_cineS2 = { + .type = NGENE_SIDEWINDER, + .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 18, + .msi_supported = true, +}; + +static struct ngene_info ngene_info_satixS2 = { + .type = NGENE_SIDEWINDER, + .name = "Mystique SaTiX-S2 Dual", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 18, + .msi_supported = true, +}; + +static struct ngene_info ngene_info_satixS2v2 = { + .type = NGENE_SIDEWINDER, + .name = "Mystique SaTiX-S2 Dual (v2)", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSOUT}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, + .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08, 0x0b, 0x09}, + .tsf = {3, 3}, + .fw_version = 18, + .msi_supported = true, +}; + +static struct ngene_info ngene_info_cineS2v5 = { + .type = NGENE_SIDEWINDER, + .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSOUT}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, + .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08, 0x0b, 0x09}, + .tsf = {3, 3}, + .fw_version = 18, + .msi_supported = true, +}; + + +static struct ngene_info ngene_info_duoFlex = { + .type = NGENE_SIDEWINDER, + .name = "Digital Devices DuoFlex PCIe or miniPCIe", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSOUT}, + .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe}, + .tuner_attach = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe}, + .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08, 0x0b, 0x09}, + .tsf = {3, 3}, + .fw_version = 18, + .msi_supported = true, +}; + +static struct ngene_info ngene_info_m780 = { + .type = NGENE_APP, + .name = "Aver M780 ATSC/QAM-B", + + /* Channel 0 is analog, which is currently unsupported */ + .io_type = { NGENE_IO_NONE, NGENE_IO_TSIN }, + .demod_attach = { NULL, demod_attach_lg330x }, + + /* Ensure these are NULL else the frame will call them (as funcs) */ + .tuner_attach = { 0, 0, 0, 0 }, + .fe_config = { NULL, &aver_m780 }, + .avf = { 0 }, + + /* A custom electrical interface config for the demod to bridge */ + .tsf = { 4, 4 }, + .fw_version = 15, +}; + +static struct drxd_config fe_terratec_dvbt_0 = { + .index = 0, + .demod_address = 0x70, + .demod_revision = 0xa2, + .demoda_address = 0x00, + .pll_address = 0x60, + .pll_type = DVB_PLL_THOMSON_DTT7520X, + .clock = 20000, + .osc_deviation = osc_deviation, +}; + +static struct drxd_config fe_terratec_dvbt_1 = { + .index = 1, + .demod_address = 0x71, + .demod_revision = 0xa2, + .demoda_address = 0x00, + .pll_address = 0x60, + .pll_type = DVB_PLL_THOMSON_DTT7520X, + .clock = 20000, + .osc_deviation = osc_deviation, +}; + +static struct ngene_info ngene_info_terratec = { + .type = NGENE_TERRATEC, + .name = "Terratec Integra/Cinergy2400i Dual DVB-T", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, + .i2c_access = 1, +}; + +/****************************************************************************/ + + + +/****************************************************************************/ +/* PCI Subsystem ID *********************************************************/ +/****************************************************************************/ + +#define NGENE_ID(_subvend, _subdev, _driverdata) { \ + .vendor = NGENE_VID, .device = NGENE_PID, \ + .subvendor = _subvend, .subdevice = _subdev, \ + .driver_data = (unsigned long) &_driverdata } + +/****************************************************************************/ + +static const struct pci_device_id ngene_id_tbl[] __devinitdata = { + NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2), + NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2), + NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5), + NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex), + NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex), + NGENE_ID(0x1461, 0x062e, ngene_info_m780), + NGENE_ID(0x153b, 0x1167, ngene_info_terratec), + {0} +}; +MODULE_DEVICE_TABLE(pci, ngene_id_tbl); + +/****************************************************************************/ +/* Init/Exit ****************************************************************/ +/****************************************************************************/ + +static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, + enum pci_channel_state state) +{ + printk(KERN_ERR DEVICE_NAME ": PCI error\n"); + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + if (state == pci_channel_io_frozen) + return PCI_ERS_RESULT_NEED_RESET; + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t ngene_link_reset(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": link reset\n"); + return 0; +} + +static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": slot reset\n"); + return 0; +} + +static void ngene_resume(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": resume\n"); +} + +static struct pci_error_handlers ngene_errors = { + .error_detected = ngene_error_detected, + .link_reset = ngene_link_reset, + .slot_reset = ngene_slot_reset, + .resume = ngene_resume, +}; + +static struct pci_driver ngene_pci_driver = { + .name = "ngene", + .id_table = ngene_id_tbl, + .probe = ngene_probe, + .remove = __devexit_p(ngene_remove), + .err_handler = &ngene_errors, + .shutdown = ngene_shutdown, +}; + +static __init int module_init_ngene(void) +{ + printk(KERN_INFO + "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); + return pci_register_driver(&ngene_pci_driver); +} + +static __exit void module_exit_ngene(void) +{ + pci_unregister_driver(&ngene_pci_driver); +} + +module_init(module_init_ngene); +module_exit(module_exit_ngene); + +MODULE_DESCRIPTION("nGene"); +MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c new file mode 100644 index 000000000000..c8e0d5b99d4c --- /dev/null +++ b/drivers/media/pci/ngene/ngene-core.c @@ -0,0 +1,1707 @@ +/* + * ngene.c: nGene PCIe bridge driver + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ngene.h" + +static int one_adapter; +module_param(one_adapter, int, 0444); +MODULE_PARM_DESC(one_adapter, "Use only one adapter."); + +static int shutdown_workaround; +module_param(shutdown_workaround, int, 0644); +MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets."); + +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Print debugging information."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk if (debug) printk + +#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) +#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +#define ngreadl(adr) readl(dev->iomem + (adr)) +#define ngreadb(adr) readb(dev->iomem + (adr)) +#define ngcpyto(adr, src, count) memcpy_toio((char *) \ + (dev->iomem + (adr)), (src), (count)) +#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ + (dev->iomem + (adr)), (count)) + +/****************************************************************************/ +/* nGene interrupt handler **************************************************/ +/****************************************************************************/ + +static void event_tasklet(unsigned long data) +{ + struct ngene *dev = (struct ngene *)data; + + while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { + struct EVENT_BUFFER Event = + dev->EventQueue[dev->EventQueueReadIndex]; + dev->EventQueueReadIndex = + (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1); + + if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify)) + dev->TxEventNotify(dev, Event.TimeStamp); + if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify)) + dev->RxEventNotify(dev, Event.TimeStamp, + Event.RXCharacter); + } +} + +static void demux_tasklet(unsigned long data) +{ + struct ngene_channel *chan = (struct ngene_channel *)data; + struct SBufferHeader *Cur = chan->nextBuffer; + + spin_lock_irq(&chan->state_lock); + + while (Cur->ngeneBuffer.SR.Flags & 0x80) { + if (chan->mode & NGENE_IO_TSOUT) { + u32 Flags = chan->DataFormatFlags; + if (Cur->ngeneBuffer.SR.Flags & 0x20) + Flags |= BEF_OVERFLOW; + if (chan->pBufferExchange) { + if (!chan->pBufferExchange(chan, + Cur->Buffer1, + chan->Capture1Length, + Cur->ngeneBuffer.SR. + Clock, Flags)) { + /* + We didn't get data + Clear in service flag to make sure we + get called on next interrupt again. + leave fill/empty (0x80) flag alone + to avoid hardware running out of + buffers during startup, we hold only + in run state ( the source may be late + delivering data ) + */ + + if (chan->HWState == HWSTATE_RUN) { + Cur->ngeneBuffer.SR.Flags &= + ~0x40; + break; + /* Stop processing stream */ + } + } else { + /* We got a valid buffer, + so switch to run state */ + chan->HWState = HWSTATE_RUN; + } + } else { + printk(KERN_ERR DEVICE_NAME ": OOPS\n"); + if (chan->HWState == HWSTATE_RUN) { + Cur->ngeneBuffer.SR.Flags &= ~0x40; + break; /* Stop processing stream */ + } + } + if (chan->AudioDTOUpdated) { + printk(KERN_INFO DEVICE_NAME + ": Update AudioDTO = %d\n", + chan->AudioDTOValue); + Cur->ngeneBuffer.SR.DTOUpdate = + chan->AudioDTOValue; + chan->AudioDTOUpdated = 0; + } + } else { + if (chan->HWState == HWSTATE_RUN) { + u32 Flags = chan->DataFormatFlags; + IBufferExchange *exch1 = chan->pBufferExchange; + IBufferExchange *exch2 = chan->pBufferExchange2; + if (Cur->ngeneBuffer.SR.Flags & 0x01) + Flags |= BEF_EVEN_FIELD; + if (Cur->ngeneBuffer.SR.Flags & 0x20) + Flags |= BEF_OVERFLOW; + spin_unlock_irq(&chan->state_lock); + if (exch1) + exch1(chan, Cur->Buffer1, + chan->Capture1Length, + Cur->ngeneBuffer.SR.Clock, + Flags); + if (exch2) + exch2(chan, Cur->Buffer2, + chan->Capture2Length, + Cur->ngeneBuffer.SR.Clock, + Flags); + spin_lock_irq(&chan->state_lock); + } else if (chan->HWState != HWSTATE_STOP) + chan->HWState = HWSTATE_RUN; + } + Cur->ngeneBuffer.SR.Flags = 0x00; + Cur = Cur->Next; + } + chan->nextBuffer = Cur; + + spin_unlock_irq(&chan->state_lock); +} + +static irqreturn_t irq_handler(int irq, void *dev_id) +{ + struct ngene *dev = (struct ngene *)dev_id; + u32 icounts = 0; + irqreturn_t rc = IRQ_NONE; + u32 i = MAX_STREAM; + u8 *tmpCmdDoneByte; + + if (dev->BootFirmware) { + icounts = ngreadl(NGENE_INT_COUNTS); + if (icounts != dev->icounts) { + ngwritel(0, FORCE_NMI); + dev->cmd_done = 1; + wake_up(&dev->cmd_wq); + dev->icounts = icounts; + rc = IRQ_HANDLED; + } + return rc; + } + + ngwritel(0, FORCE_NMI); + + spin_lock(&dev->cmd_lock); + tmpCmdDoneByte = dev->CmdDoneByte; + if (tmpCmdDoneByte && + (*tmpCmdDoneByte || + (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) { + dev->CmdDoneByte = NULL; + dev->cmd_done = 1; + wake_up(&dev->cmd_wq); + rc = IRQ_HANDLED; + } + spin_unlock(&dev->cmd_lock); + + if (dev->EventBuffer->EventStatus & 0x80) { + u8 nextWriteIndex = + (dev->EventQueueWriteIndex + 1) & + (EVENT_QUEUE_SIZE - 1); + if (nextWriteIndex != dev->EventQueueReadIndex) { + dev->EventQueue[dev->EventQueueWriteIndex] = + *(dev->EventBuffer); + dev->EventQueueWriteIndex = nextWriteIndex; + } else { + printk(KERN_ERR DEVICE_NAME ": event overflow\n"); + dev->EventQueueOverflowCount += 1; + dev->EventQueueOverflowFlag = 1; + } + dev->EventBuffer->EventStatus &= ~0x80; + tasklet_schedule(&dev->event_tasklet); + rc = IRQ_HANDLED; + } + + while (i > 0) { + i--; + spin_lock(&dev->channel[i].state_lock); + /* if (dev->channel[i].State>=KSSTATE_RUN) { */ + if (dev->channel[i].nextBuffer) { + if ((dev->channel[i].nextBuffer-> + ngeneBuffer.SR.Flags & 0xC0) == 0x80) { + dev->channel[i].nextBuffer-> + ngeneBuffer.SR.Flags |= 0x40; + tasklet_schedule( + &dev->channel[i].demux_tasklet); + rc = IRQ_HANDLED; + } + } + spin_unlock(&dev->channel[i].state_lock); + } + + /* Request might have been processed by a previous call. */ + return IRQ_HANDLED; +} + +/****************************************************************************/ +/* nGene command interface **************************************************/ +/****************************************************************************/ + +static void dump_command_io(struct ngene *dev) +{ + u8 buf[8], *b; + + ngcpyfrom(buf, HOST_TO_NGENE, 8); + printk(KERN_ERR "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf); + + ngcpyfrom(buf, NGENE_TO_HOST, 8); + printk(KERN_ERR "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf); + + b = dev->hosttongene; + printk(KERN_ERR "dev->hosttongene (%p): %*ph\n", b, 8, b); + + b = dev->ngenetohost; + printk(KERN_ERR "dev->ngenetohost (%p): %*ph\n", b, 8, b); +} + +static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) +{ + int ret; + u8 *tmpCmdDoneByte; + + dev->cmd_done = 0; + + if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) { + dev->BootFirmware = 1; + dev->icounts = ngreadl(NGENE_INT_COUNTS); + ngwritel(0, NGENE_COMMAND); + ngwritel(0, NGENE_COMMAND_HI); + ngwritel(0, NGENE_STATUS); + ngwritel(0, NGENE_STATUS_HI); + ngwritel(0, NGENE_EVENT); + ngwritel(0, NGENE_EVENT_HI); + } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) { + u64 fwio = dev->PAFWInterfaceBuffer; + + ngwritel(fwio & 0xffffffff, NGENE_COMMAND); + ngwritel(fwio >> 32, NGENE_COMMAND_HI); + ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS); + ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI); + ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT); + ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI); + } + + memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2); + + if (dev->BootFirmware) + ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2); + + spin_lock_irq(&dev->cmd_lock); + tmpCmdDoneByte = dev->ngenetohost + com->out_len; + if (!com->out_len) + tmpCmdDoneByte++; + *tmpCmdDoneByte = 0; + dev->ngenetohost[0] = 0; + dev->ngenetohost[1] = 0; + dev->CmdDoneByte = tmpCmdDoneByte; + spin_unlock_irq(&dev->cmd_lock); + + /* Notify 8051. */ + ngwritel(1, FORCE_INT); + + ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ); + if (!ret) { + /*ngwritel(0, FORCE_NMI);*/ + + printk(KERN_ERR DEVICE_NAME + ": Command timeout cmd=%02x prev=%02x\n", + com->cmd.hdr.Opcode, dev->prev_cmd); + dump_command_io(dev); + return -1; + } + if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) + dev->BootFirmware = 0; + + dev->prev_cmd = com->cmd.hdr.Opcode; + + if (!com->out_len) + return 0; + + memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len); + + return 0; +} + +int ngene_command(struct ngene *dev, struct ngene_command *com) +{ + int result; + + down(&dev->cmd_mutex); + result = ngene_command_mutex(dev, com); + up(&dev->cmd_mutex); + return result; +} + + +static int ngene_command_load_firmware(struct ngene *dev, + u8 *ngene_fw, u32 size) +{ +#define FIRSTCHUNK (1024) + u32 cleft; + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE; + com.cmd.hdr.Length = 0; + com.in_len = 0; + com.out_len = 0; + + ngene_command(dev, &com); + + cleft = (size + 3) & ~3; + if (cleft > FIRSTCHUNK) { + ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK, + cleft - FIRSTCHUNK); + cleft = FIRSTCHUNK; + } + ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft); + + memset(&com, 0, sizeof(struct ngene_command)); + com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH; + com.cmd.hdr.Length = 4; + com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA; + com.cmd.FWLoadFinish.Length = (unsigned short)cleft; + com.in_len = 4; + com.out_len = 0; + + return ngene_command(dev, &com); +} + + +static int ngene_command_config_buf(struct ngene *dev, u8 config) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER; + com.cmd.hdr.Length = 1; + com.cmd.ConfigureBuffers.config = config; + com.in_len = 1; + com.out_len = 0; + + if (ngene_command(dev, &com) < 0) + return -EIO; + return 0; +} + +static int ngene_command_config_free_buf(struct ngene *dev, u8 *config) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER; + com.cmd.hdr.Length = 6; + memcpy(&com.cmd.ConfigureBuffers.config, config, 6); + com.in_len = 6; + com.out_len = 0; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + return 0; +} + +int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN; + com.cmd.hdr.Length = 1; + com.cmd.SetGpioPin.select = select | (level << 7); + com.in_len = 1; + com.out_len = 0; + + return ngene_command(dev, &com); +} + + +/* + 02000640 is sample on rising edge. + 02000740 is sample on falling edge. + 02000040 is ignore "valid" signal + + 0: FD_CTL1 Bit 7,6 must be 0,1 + 7 disable(fw controlled) + 6 0-AUX,1-TS + 5 0-par,1-ser + 4 0-lsb/1-msb + 3,2 reserved + 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both + 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge + 2: FD_STA is read-only. 0-sync + 3: FD_INSYNC is number of 47s to trigger "in sync". + 4: FD_OUTSYNC is number of 47s to trigger "out of sync". + 5: FD_MAXBYTE1 is low-order of bytes per packet. + 6: FD_MAXBYTE2 is high-order of bytes per packet. + 7: Top byte is unused. +*/ + +/****************************************************************************/ + +static u8 TSFeatureDecoderSetup[8 * 5] = { + 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */ + 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */ + 0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */ + 0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */ +}; + +/* Set NGENE I2S Config to 16 bit packed */ +static u8 I2SConfiguration[] = { + 0x00, 0x10, 0x00, 0x00, + 0x80, 0x10, 0x00, 0x00, +}; + +static u8 SPDIFConfiguration[10] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Set NGENE I2S Config to transport stream compatible mode */ + +static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 }; + +static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 }; + +static u8 ITUDecoderSetup[4][16] = { + {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */ + 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00}, + {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, + {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */ + 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, + {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */ + 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, +}; + +/* + * 50 48 60 gleich + * 27p50 9f 00 22 80 42 69 18 ... + * 27p60 93 00 22 80 82 69 1c ... + */ + +/* Maxbyte to 1144 (for raw data) */ +static u8 ITUFeatureDecoderSetup[8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00 +}; + +void FillTSBuffer(void *Buffer, int Length, u32 Flags) +{ + u32 *ptr = Buffer; + + memset(Buffer, TS_FILLER, Length); + while (Length > 0) { + if (Flags & DF_SWAP32) + *ptr = 0x471FFF10; + else + *ptr = 0x10FF1F47; + ptr += (188 / 4); + Length -= 188; + } +} + + +static void flush_buffers(struct ngene_channel *chan) +{ + u8 val; + + do { + msleep(1); + spin_lock_irq(&chan->state_lock); + val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80; + spin_unlock_irq(&chan->state_lock); + } while (val); +} + +static void clear_buffers(struct ngene_channel *chan) +{ + struct SBufferHeader *Cur = chan->nextBuffer; + + do { + memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR)); + if (chan->mode & NGENE_IO_TSOUT) + FillTSBuffer(Cur->Buffer1, + chan->Capture1Length, + chan->DataFormatFlags); + Cur = Cur->Next; + } while (Cur != chan->nextBuffer); + + if (chan->mode & NGENE_IO_TSOUT) { + chan->nextBuffer->ngeneBuffer.SR.DTOUpdate = + chan->AudioDTOValue; + chan->AudioDTOUpdated = 0; + + Cur = chan->TSIdleBuffer.Head; + + do { + memset(&Cur->ngeneBuffer.SR, 0, + sizeof(Cur->ngeneBuffer.SR)); + FillTSBuffer(Cur->Buffer1, + chan->Capture1Length, + chan->DataFormatFlags); + Cur = Cur->Next; + } while (Cur != chan->TSIdleBuffer.Head); + } +} + +static int ngene_command_stream_control(struct ngene *dev, u8 stream, + u8 control, u8 mode, u8 flags) +{ + struct ngene_channel *chan = &dev->channel[stream]; + struct ngene_command com; + u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300); + u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500); + u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700); + u16 BsSDO = 0x9B00; + + down(&dev->stream_mutex); + memset(&com, 0, sizeof(com)); + com.cmd.hdr.Opcode = CMD_CONTROL; + com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2; + com.cmd.StreamControl.Stream = stream | (control ? 8 : 0); + if (chan->mode & NGENE_IO_TSOUT) + com.cmd.StreamControl.Stream |= 0x07; + com.cmd.StreamControl.Control = control | + (flags & SFLAG_ORDER_LUMA_CHROMA); + com.cmd.StreamControl.Mode = mode; + com.in_len = sizeof(struct FW_STREAM_CONTROL); + com.out_len = 0; + + dprintk(KERN_INFO DEVICE_NAME + ": Stream=%02x, Control=%02x, Mode=%02x\n", + com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control, + com.cmd.StreamControl.Mode); + + chan->Mode = mode; + + if (!(control & 0x80)) { + spin_lock_irq(&chan->state_lock); + if (chan->State == KSSTATE_RUN) { + chan->State = KSSTATE_ACQUIRE; + chan->HWState = HWSTATE_STOP; + spin_unlock_irq(&chan->state_lock); + if (ngene_command(dev, &com) < 0) { + up(&dev->stream_mutex); + return -1; + } + /* clear_buffers(chan); */ + flush_buffers(chan); + up(&dev->stream_mutex); + return 0; + } + spin_unlock_irq(&chan->state_lock); + up(&dev->stream_mutex); + return 0; + } + + if (mode & SMODE_AUDIO_CAPTURE) { + com.cmd.StreamControl.CaptureBlockCount = + chan->Capture1Length / AUDIO_BLOCK_SIZE; + com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; + } else if (mode & SMODE_TRANSPORT_STREAM) { + com.cmd.StreamControl.CaptureBlockCount = + chan->Capture1Length / TS_BLOCK_SIZE; + com.cmd.StreamControl.MaxLinesPerField = + chan->Capture1Length / TS_BLOCK_SIZE; + com.cmd.StreamControl.Buffer_Address = + chan->TSRingBuffer.PAHead; + if (chan->mode & NGENE_IO_TSOUT) { + com.cmd.StreamControl.BytesPerVBILine = + chan->Capture1Length / TS_BLOCK_SIZE; + com.cmd.StreamControl.Stream |= 0x07; + } + } else { + com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine; + com.cmd.StreamControl.MaxLinesPerField = chan->nLines; + com.cmd.StreamControl.MinLinesPerField = 100; + com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; + + if (mode & SMODE_VBI_CAPTURE) { + com.cmd.StreamControl.MaxVBILinesPerField = + chan->nVBILines; + com.cmd.StreamControl.MinVBILinesPerField = 0; + com.cmd.StreamControl.BytesPerVBILine = + chan->nBytesPerVBILine; + } + if (flags & SFLAG_COLORBAR) + com.cmd.StreamControl.Stream |= 0x04; + } + + spin_lock_irq(&chan->state_lock); + if (mode & SMODE_AUDIO_CAPTURE) { + chan->nextBuffer = chan->RingBuffer.Head; + if (mode & SMODE_AUDIO_SPDIF) { + com.cmd.StreamControl.SetupDataLen = + sizeof(SPDIFConfiguration); + com.cmd.StreamControl.SetupDataAddr = BsSPI; + memcpy(com.cmd.StreamControl.SetupData, + SPDIFConfiguration, sizeof(SPDIFConfiguration)); + } else { + com.cmd.StreamControl.SetupDataLen = 4; + com.cmd.StreamControl.SetupDataAddr = BsSDI; + memcpy(com.cmd.StreamControl.SetupData, + I2SConfiguration + + 4 * dev->card_info->i2s[stream], 4); + } + } else if (mode & SMODE_TRANSPORT_STREAM) { + chan->nextBuffer = chan->TSRingBuffer.Head; + if (stream >= STREAM_AUDIOIN1) { + if (chan->mode & NGENE_IO_TSOUT) { + com.cmd.StreamControl.SetupDataLen = + sizeof(TS_I2SOutConfiguration); + com.cmd.StreamControl.SetupDataAddr = BsSDO; + memcpy(com.cmd.StreamControl.SetupData, + TS_I2SOutConfiguration, + sizeof(TS_I2SOutConfiguration)); + } else { + com.cmd.StreamControl.SetupDataLen = + sizeof(TS_I2SConfiguration); + com.cmd.StreamControl.SetupDataAddr = BsSDI; + memcpy(com.cmd.StreamControl.SetupData, + TS_I2SConfiguration, + sizeof(TS_I2SConfiguration)); + } + } else { + com.cmd.StreamControl.SetupDataLen = 8; + com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10; + memcpy(com.cmd.StreamControl.SetupData, + TSFeatureDecoderSetup + + 8 * dev->card_info->tsf[stream], 8); + } + } else { + chan->nextBuffer = chan->RingBuffer.Head; + com.cmd.StreamControl.SetupDataLen = + 16 + sizeof(ITUFeatureDecoderSetup); + com.cmd.StreamControl.SetupDataAddr = BsUVI; + memcpy(com.cmd.StreamControl.SetupData, + ITUDecoderSetup[chan->itumode], 16); + memcpy(com.cmd.StreamControl.SetupData + 16, + ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup)); + } + clear_buffers(chan); + chan->State = KSSTATE_RUN; + if (mode & SMODE_TRANSPORT_STREAM) + chan->HWState = HWSTATE_RUN; + else + chan->HWState = HWSTATE_STARTUP; + spin_unlock_irq(&chan->state_lock); + + if (ngene_command(dev, &com) < 0) { + up(&dev->stream_mutex); + return -1; + } + up(&dev->stream_mutex); + return 0; +} + +void set_transfer(struct ngene_channel *chan, int state) +{ + u8 control = 0, mode = 0, flags = 0; + struct ngene *dev = chan->dev; + int ret; + + /* + printk(KERN_INFO DEVICE_NAME ": st %d\n", state); + msleep(100); + */ + + if (state) { + if (chan->running) { + printk(KERN_INFO DEVICE_NAME ": already running\n"); + return; + } + } else { + if (!chan->running) { + printk(KERN_INFO DEVICE_NAME ": already stopped\n"); + return; + } + } + + if (dev->card_info->switch_ctrl) + dev->card_info->switch_ctrl(chan, 1, state ^ 1); + + if (state) { + spin_lock_irq(&chan->state_lock); + + /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", + ngreadl(0x9310)); */ + dvb_ringbuffer_flush(&dev->tsout_rbuf); + control = 0x80; + if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + chan->Capture1Length = 512 * 188; + mode = SMODE_TRANSPORT_STREAM; + } + if (chan->mode & NGENE_IO_TSOUT) { + chan->pBufferExchange = tsout_exchange; + /* 0x66666666 = 50MHz *2^33 /250MHz */ + chan->AudioDTOValue = 0x80000000; + chan->AudioDTOUpdated = 1; + } + if (chan->mode & NGENE_IO_TSIN) + chan->pBufferExchange = tsin_exchange; + spin_unlock_irq(&chan->state_lock); + } else + ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", + ngreadl(0x9310)); */ + + ret = ngene_command_stream_control(dev, chan->number, + control, mode, flags); + if (!ret) + chan->running = state; + else + printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n", + state); + if (!state) { + spin_lock_irq(&chan->state_lock); + chan->pBufferExchange = NULL; + dvb_ringbuffer_flush(&dev->tsout_rbuf); + spin_unlock_irq(&chan->state_lock); + } +} + + +/****************************************************************************/ +/* nGene hardware init and release functions ********************************/ +/****************************************************************************/ + +static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) +{ + struct SBufferHeader *Cur = rb->Head; + u32 j; + + if (!Cur) + return; + + for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) { + if (Cur->Buffer1) + pci_free_consistent(dev->pci_dev, + rb->Buffer1Length, + Cur->Buffer1, + Cur->scList1->Address); + + if (Cur->Buffer2) + pci_free_consistent(dev->pci_dev, + rb->Buffer2Length, + Cur->Buffer2, + Cur->scList2->Address); + } + + if (rb->SCListMem) + pci_free_consistent(dev->pci_dev, rb->SCListMemSize, + rb->SCListMem, rb->PASCListMem); + + pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead); +} + +static void free_idlebuffer(struct ngene *dev, + struct SRingBufferDescriptor *rb, + struct SRingBufferDescriptor *tb) +{ + int j; + struct SBufferHeader *Cur = tb->Head; + + if (!rb->Head) + return; + free_ringbuffer(dev, rb); + for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) { + Cur->Buffer2 = NULL; + Cur->scList2 = NULL; + Cur->ngeneBuffer.Address_of_first_entry_2 = 0; + Cur->ngeneBuffer.Number_of_entries_2 = 0; + } +} + +static void free_common_buffers(struct ngene *dev) +{ + u32 i; + struct ngene_channel *chan; + + for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { + chan = &dev->channel[i]; + free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer); + free_ringbuffer(dev, &chan->RingBuffer); + free_ringbuffer(dev, &chan->TSRingBuffer); + } + + if (dev->OverflowBuffer) + pci_free_consistent(dev->pci_dev, + OVERFLOW_BUFFER_SIZE, + dev->OverflowBuffer, dev->PAOverflowBuffer); + + if (dev->FWInterfaceBuffer) + pci_free_consistent(dev->pci_dev, + 4096, + dev->FWInterfaceBuffer, + dev->PAFWInterfaceBuffer); +} + +/****************************************************************************/ +/* Ring buffer handling *****************************************************/ +/****************************************************************************/ + +static int create_ring_buffer(struct pci_dev *pci_dev, + struct SRingBufferDescriptor *descr, u32 NumBuffers) +{ + dma_addr_t tmp; + struct SBufferHeader *Head; + u32 i; + u32 MemSize = SIZEOF_SBufferHeader * NumBuffers; + u64 PARingBufferHead; + u64 PARingBufferCur; + u64 PARingBufferNext; + struct SBufferHeader *Cur, *Next; + + descr->Head = NULL; + descr->MemSize = 0; + descr->PAHead = 0; + descr->NumBuffers = 0; + + if (MemSize < 4096) + MemSize = 4096; + + Head = pci_alloc_consistent(pci_dev, MemSize, &tmp); + PARingBufferHead = tmp; + + if (!Head) + return -ENOMEM; + + memset(Head, 0, MemSize); + + PARingBufferCur = PARingBufferHead; + Cur = Head; + + for (i = 0; i < NumBuffers - 1; i++) { + Next = (struct SBufferHeader *) + (((u8 *) Cur) + SIZEOF_SBufferHeader); + PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader; + Cur->Next = Next; + Cur->ngeneBuffer.Next = PARingBufferNext; + Cur = Next; + PARingBufferCur = PARingBufferNext; + } + /* Last Buffer points back to first one */ + Cur->Next = Head; + Cur->ngeneBuffer.Next = PARingBufferHead; + + descr->Head = Head; + descr->MemSize = MemSize; + descr->PAHead = PARingBufferHead; + descr->NumBuffers = NumBuffers; + + return 0; +} + +static int AllocateRingBuffers(struct pci_dev *pci_dev, + dma_addr_t of, + struct SRingBufferDescriptor *pRingBuffer, + u32 Buffer1Length, u32 Buffer2Length) +{ + dma_addr_t tmp; + u32 i, j; + int status = 0; + u32 SCListMemSize = pRingBuffer->NumBuffers + * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) : + NUM_SCATTER_GATHER_ENTRIES) + * sizeof(struct HW_SCATTER_GATHER_ELEMENT); + + u64 PASCListMem; + struct HW_SCATTER_GATHER_ELEMENT *SCListEntry; + u64 PASCListEntry; + struct SBufferHeader *Cur; + void *SCListMem; + + if (SCListMemSize < 4096) + SCListMemSize = 4096; + + SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp); + + PASCListMem = tmp; + if (SCListMem == NULL) + return -ENOMEM; + + memset(SCListMem, 0, SCListMemSize); + + pRingBuffer->SCListMem = SCListMem; + pRingBuffer->PASCListMem = PASCListMem; + pRingBuffer->SCListMemSize = SCListMemSize; + pRingBuffer->Buffer1Length = Buffer1Length; + pRingBuffer->Buffer2Length = Buffer2Length; + + SCListEntry = SCListMem; + PASCListEntry = PASCListMem; + Cur = pRingBuffer->Head; + + for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) { + u64 PABuffer; + + void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length, + &tmp); + PABuffer = tmp; + + if (Buffer == NULL) + return -ENOMEM; + + Cur->Buffer1 = Buffer; + + SCListEntry->Address = PABuffer; + SCListEntry->Length = Buffer1Length; + + Cur->scList1 = SCListEntry; + Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry; + Cur->ngeneBuffer.Number_of_entries_1 = + NUM_SCATTER_GATHER_ENTRIES; + + SCListEntry += 1; + PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); + +#if NUM_SCATTER_GATHER_ENTRIES > 1 + for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) { + SCListEntry->Address = of; + SCListEntry->Length = OVERFLOW_BUFFER_SIZE; + SCListEntry += 1; + PASCListEntry += + sizeof(struct HW_SCATTER_GATHER_ELEMENT); + } +#endif + + if (!Buffer2Length) + continue; + + Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp); + PABuffer = tmp; + + if (Buffer == NULL) + return -ENOMEM; + + Cur->Buffer2 = Buffer; + + SCListEntry->Address = PABuffer; + SCListEntry->Length = Buffer2Length; + + Cur->scList2 = SCListEntry; + Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry; + Cur->ngeneBuffer.Number_of_entries_2 = + NUM_SCATTER_GATHER_ENTRIES; + + SCListEntry += 1; + PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); + +#if NUM_SCATTER_GATHER_ENTRIES > 1 + for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) { + SCListEntry->Address = of; + SCListEntry->Length = OVERFLOW_BUFFER_SIZE; + SCListEntry += 1; + PASCListEntry += + sizeof(struct HW_SCATTER_GATHER_ELEMENT); + } +#endif + + } + + return status; +} + +static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer, + struct SRingBufferDescriptor *pRingBuffer) +{ + int status = 0; + + /* Copy pointer to scatter gather list in TSRingbuffer + structure for buffer 2 + Load number of buffer + */ + u32 n = pRingBuffer->NumBuffers; + + /* Point to first buffer entry */ + struct SBufferHeader *Cur = pRingBuffer->Head; + int i; + /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */ + for (i = 0; i < n; i++) { + Cur->Buffer2 = pIdleBuffer->Head->Buffer1; + Cur->scList2 = pIdleBuffer->Head->scList1; + Cur->ngeneBuffer.Address_of_first_entry_2 = + pIdleBuffer->Head->ngeneBuffer. + Address_of_first_entry_1; + Cur->ngeneBuffer.Number_of_entries_2 = + pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1; + Cur = Cur->Next; + } + return status; +} + +static u32 RingBufferSizes[MAX_STREAM] = { + RING_SIZE_VIDEO, + RING_SIZE_VIDEO, + RING_SIZE_AUDIO, + RING_SIZE_AUDIO, + RING_SIZE_AUDIO, +}; + +static u32 Buffer1Sizes[MAX_STREAM] = { + MAX_VIDEO_BUFFER_SIZE, + MAX_VIDEO_BUFFER_SIZE, + MAX_AUDIO_BUFFER_SIZE, + MAX_AUDIO_BUFFER_SIZE, + MAX_AUDIO_BUFFER_SIZE +}; + +static u32 Buffer2Sizes[MAX_STREAM] = { + MAX_VBI_BUFFER_SIZE, + MAX_VBI_BUFFER_SIZE, + 0, + 0, + 0 +}; + + +static int AllocCommonBuffers(struct ngene *dev) +{ + int status = 0, i; + + dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096, + &dev->PAFWInterfaceBuffer); + if (!dev->FWInterfaceBuffer) + return -ENOMEM; + dev->hosttongene = dev->FWInterfaceBuffer; + dev->ngenetohost = dev->FWInterfaceBuffer + 256; + dev->EventBuffer = dev->FWInterfaceBuffer + 512; + + dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev, + OVERFLOW_BUFFER_SIZE, + &dev->PAOverflowBuffer); + if (!dev->OverflowBuffer) + return -ENOMEM; + memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE); + + for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { + int type = dev->card_info->io_type[i]; + + dev->channel[i].State = KSSTATE_STOP; + + if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) { + status = create_ring_buffer(dev->pci_dev, + &dev->channel[i].RingBuffer, + RingBufferSizes[i]); + if (status < 0) + break; + + if (type & (NGENE_IO_TV | NGENE_IO_AIN)) { + status = AllocateRingBuffers(dev->pci_dev, + dev-> + PAOverflowBuffer, + &dev->channel[i]. + RingBuffer, + Buffer1Sizes[i], + Buffer2Sizes[i]); + if (status < 0) + break; + } else if (type & NGENE_IO_HDTV) { + status = AllocateRingBuffers(dev->pci_dev, + dev-> + PAOverflowBuffer, + &dev->channel[i]. + RingBuffer, + MAX_HDTV_BUFFER_SIZE, + 0); + if (status < 0) + break; + } + } + + if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + + status = create_ring_buffer(dev->pci_dev, + &dev->channel[i]. + TSRingBuffer, RING_SIZE_TS); + if (status < 0) + break; + + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &dev->channel[i]. + TSRingBuffer, + MAX_TS_BUFFER_SIZE, 0); + if (status) + break; + } + + if (type & NGENE_IO_TSOUT) { + status = create_ring_buffer(dev->pci_dev, + &dev->channel[i]. + TSIdleBuffer, 1); + if (status < 0) + break; + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &dev->channel[i]. + TSIdleBuffer, + MAX_TS_BUFFER_SIZE, 0); + if (status) + break; + FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer, + &dev->channel[i].TSRingBuffer); + } + } + return status; +} + +static void ngene_release_buffers(struct ngene *dev) +{ + if (dev->iomem) + iounmap(dev->iomem); + free_common_buffers(dev); + vfree(dev->tsout_buf); + vfree(dev->tsin_buf); + vfree(dev->ain_buf); + vfree(dev->vin_buf); + vfree(dev); +} + +static int ngene_get_buffers(struct ngene *dev) +{ + if (AllocCommonBuffers(dev)) + return -ENOMEM; + if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) { + dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE); + if (!dev->tsout_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->tsout_rbuf, + dev->tsout_buf, TSOUT_BUF_SIZE); + } + if (dev->card_info->io_type[2]&NGENE_IO_TSIN) { + dev->tsin_buf = vmalloc(TSIN_BUF_SIZE); + if (!dev->tsin_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->tsin_rbuf, + dev->tsin_buf, TSIN_BUF_SIZE); + } + if (dev->card_info->io_type[2] & NGENE_IO_AIN) { + dev->ain_buf = vmalloc(AIN_BUF_SIZE); + if (!dev->ain_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE); + } + if (dev->card_info->io_type[0] & NGENE_IO_HDTV) { + dev->vin_buf = vmalloc(VIN_BUF_SIZE); + if (!dev->vin_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE); + } + dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0), + pci_resource_len(dev->pci_dev, 0)); + if (!dev->iomem) + return -ENOMEM; + + return 0; +} + +static void ngene_init(struct ngene *dev) +{ + int i; + + tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev); + + memset_io(dev->iomem + 0xc000, 0x00, 0x220); + memset_io(dev->iomem + 0xc400, 0x00, 0x100); + + for (i = 0; i < MAX_STREAM; i++) { + dev->channel[i].dev = dev; + dev->channel[i].number = i; + } + + dev->fw_interface_version = 0; + + ngwritel(0, NGENE_INT_ENABLE); + + dev->icounts = ngreadl(NGENE_INT_COUNTS); + + dev->device_version = ngreadl(DEV_VER) & 0x0f; + printk(KERN_INFO DEVICE_NAME ": Device version %d\n", + dev->device_version); +} + +static int ngene_load_firm(struct ngene *dev) +{ + u32 size; + const struct firmware *fw = NULL; + u8 *ngene_fw; + char *fw_name; + int err, version; + + version = dev->card_info->fw_version; + + switch (version) { + default: + case 15: + version = 15; + size = 23466; + fw_name = "ngene_15.fw"; + dev->cmd_timeout_workaround = true; + break; + case 16: + size = 23498; + fw_name = "ngene_16.fw"; + dev->cmd_timeout_workaround = true; + break; + case 17: + size = 24446; + fw_name = "ngene_17.fw"; + dev->cmd_timeout_workaround = true; + break; + case 18: + size = 0; + fw_name = "ngene_18.fw"; + break; + } + + if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) { + printk(KERN_ERR DEVICE_NAME + ": Could not load firmware file %s.\n", fw_name); + printk(KERN_INFO DEVICE_NAME + ": Copy %s to your hotplug directory!\n", fw_name); + return -1; + } + if (size == 0) + size = fw->size; + if (size != fw->size) { + printk(KERN_ERR DEVICE_NAME + ": Firmware %s has invalid size!", fw_name); + err = -1; + } else { + printk(KERN_INFO DEVICE_NAME + ": Loading firmware file %s.\n", fw_name); + ngene_fw = (u8 *) fw->data; + err = ngene_command_load_firmware(dev, ngene_fw, size); + } + + release_firmware(fw); + + return err; +} + +static void ngene_stop(struct ngene *dev) +{ + down(&dev->cmd_mutex); + i2c_del_adapter(&(dev->channel[0].i2c_adapter)); + i2c_del_adapter(&(dev->channel[1].i2c_adapter)); + ngwritel(0, NGENE_INT_ENABLE); + ngwritel(0, NGENE_COMMAND); + ngwritel(0, NGENE_COMMAND_HI); + ngwritel(0, NGENE_STATUS); + ngwritel(0, NGENE_STATUS_HI); + ngwritel(0, NGENE_EVENT); + ngwritel(0, NGENE_EVENT_HI); + free_irq(dev->pci_dev->irq, dev); +#ifdef CONFIG_PCI_MSI + if (dev->msi_enabled) + pci_disable_msi(dev->pci_dev); +#endif +} + +static int ngene_buffer_config(struct ngene *dev) +{ + int stat; + + if (dev->card_info->fw_version >= 17) { + u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }; + u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 }; + u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 }; + u8 *bconf = tsin12_config; + + if (dev->card_info->io_type[2]&NGENE_IO_TSIN && + dev->card_info->io_type[3]&NGENE_IO_TSIN) { + bconf = tsin1234_config; + if (dev->card_info->io_type[4]&NGENE_IO_TSOUT && + dev->ci.en) + bconf = tsio1235_config; + } + stat = ngene_command_config_free_buf(dev, bconf); + } else { + int bconf = BUFFER_CONFIG_4422; + + if (dev->card_info->io_type[3] == NGENE_IO_TSIN) + bconf = BUFFER_CONFIG_3333; + stat = ngene_command_config_buf(dev, bconf); + } + return stat; +} + + +static int ngene_start(struct ngene *dev) +{ + int stat; + int i; + + pci_set_master(dev->pci_dev); + ngene_init(dev); + + stat = request_irq(dev->pci_dev->irq, irq_handler, + IRQF_SHARED, "nGene", + (void *)dev); + if (stat < 0) + return stat; + + init_waitqueue_head(&dev->cmd_wq); + init_waitqueue_head(&dev->tx_wq); + init_waitqueue_head(&dev->rx_wq); + sema_init(&dev->cmd_mutex, 1); + sema_init(&dev->stream_mutex, 1); + sema_init(&dev->pll_mutex, 1); + sema_init(&dev->i2c_switch_mutex, 1); + spin_lock_init(&dev->cmd_lock); + for (i = 0; i < MAX_STREAM; i++) + spin_lock_init(&dev->channel[i].state_lock); + ngwritel(1, TIMESTAMPS); + + ngwritel(1, NGENE_INT_ENABLE); + + stat = ngene_load_firm(dev); + if (stat < 0) + goto fail; + +#ifdef CONFIG_PCI_MSI + /* enable MSI if kernel and card support it */ + if (pci_msi_enabled() && dev->card_info->msi_supported) { + unsigned long flags; + + ngwritel(0, NGENE_INT_ENABLE); + free_irq(dev->pci_dev->irq, dev); + stat = pci_enable_msi(dev->pci_dev); + if (stat) { + printk(KERN_INFO DEVICE_NAME + ": MSI not available\n"); + flags = IRQF_SHARED; + } else { + flags = 0; + dev->msi_enabled = true; + } + stat = request_irq(dev->pci_dev->irq, irq_handler, + flags, "nGene", dev); + if (stat < 0) + goto fail2; + ngwritel(1, NGENE_INT_ENABLE); + } +#endif + + stat = ngene_i2c_init(dev, 0); + if (stat < 0) + goto fail; + + stat = ngene_i2c_init(dev, 1); + if (stat < 0) + goto fail; + + return 0; + +fail: + ngwritel(0, NGENE_INT_ENABLE); + free_irq(dev->pci_dev->irq, dev); +#ifdef CONFIG_PCI_MSI +fail2: + if (dev->msi_enabled) + pci_disable_msi(dev->pci_dev); +#endif + return stat; +} + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static void release_channel(struct ngene_channel *chan) +{ + struct dvb_demux *dvbdemux = &chan->demux; + struct ngene *dev = chan->dev; + + if (chan->running) + set_transfer(chan, 0); + + tasklet_kill(&chan->demux_tasklet); + + if (chan->ci_dev) { + dvb_unregister_device(chan->ci_dev); + chan->ci_dev = NULL; + } + + if (chan->fe2) + dvb_unregister_frontend(chan->fe2); + + if (chan->fe) { + dvb_unregister_frontend(chan->fe); + dvb_frontend_detach(chan->fe); + chan->fe = NULL; + } + + if (chan->has_demux) { + dvb_net_release(&chan->dvbnet); + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, + &chan->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, + &chan->mem_frontend); + dvb_dmxdev_release(&chan->dmxdev); + dvb_dmx_release(&chan->demux); + chan->has_demux = false; + } + + if (chan->has_adapter) { + dvb_unregister_adapter(&dev->adapter[chan->number]); + chan->has_adapter = false; + } +} + +static int init_channel(struct ngene_channel *chan) +{ + int ret = 0, nr = chan->number; + struct dvb_adapter *adapter = NULL; + struct dvb_demux *dvbdemux = &chan->demux; + struct ngene *dev = chan->dev; + struct ngene_info *ni = dev->card_info; + int io = ni->io_type[nr]; + + tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan); + chan->users = 0; + chan->type = io; + chan->mode = chan->type; /* for now only one mode */ + + if (io & NGENE_IO_TSIN) { + chan->fe = NULL; + if (ni->demod_attach[nr]) { + ret = ni->demod_attach[nr](chan); + if (ret < 0) + goto err; + } + if (chan->fe && ni->tuner_attach[nr]) { + ret = ni->tuner_attach[nr](chan); + if (ret < 0) + goto err; + } + } + + if (!dev->ci.en && (io & NGENE_IO_TSOUT)) + return 0; + + if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + if (nr >= STREAM_AUDIOIN1) + chan->DataFormatFlags = DF_SWAP32; + + if (nr == 0 || !one_adapter || dev->first_adapter == NULL) { + adapter = &dev->adapter[nr]; + ret = dvb_register_adapter(adapter, "nGene", + THIS_MODULE, + &chan->dev->pci_dev->dev, + adapter_nr); + if (ret < 0) + goto err; + if (dev->first_adapter == NULL) + dev->first_adapter = adapter; + chan->has_adapter = true; + } else + adapter = dev->first_adapter; + } + + if (dev->ci.en && (io & NGENE_IO_TSOUT)) { + dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); + set_transfer(chan, 1); + chan->dev->channel[2].DataFormatFlags = DF_SWAP32; + set_transfer(&chan->dev->channel[2], 1); + dvb_register_device(adapter, &chan->ci_dev, + &ngene_dvbdev_ci, (void *) chan, + DVB_DEVICE_SEC); + if (!chan->ci_dev) + goto err; + } + + if (chan->fe) { + if (dvb_register_frontend(adapter, chan->fe) < 0) + goto err; + chan->has_demux = true; + } + if (chan->fe2) { + if (dvb_register_frontend(adapter, chan->fe2) < 0) + goto err; + chan->fe2->tuner_priv = chan->fe->tuner_priv; + memcpy(&chan->fe2->ops.tuner_ops, + &chan->fe->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + } + + if (chan->has_demux) { + ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", + ngene_start_feed, + ngene_stop_feed, chan); + ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, + &chan->hw_frontend, + &chan->mem_frontend, adapter); + ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx); + } + + return ret; + +err: + if (chan->fe) { + dvb_frontend_detach(chan->fe); + chan->fe = NULL; + } + release_channel(chan); + return 0; +} + +static int init_channels(struct ngene *dev) +{ + int i, j; + + for (i = 0; i < MAX_STREAM; i++) { + dev->channel[i].number = i; + if (init_channel(&dev->channel[i]) < 0) { + for (j = i - 1; j >= 0; j--) + release_channel(&dev->channel[j]); + return -1; + } + } + return 0; +} + +static struct cxd2099_cfg cxd_cfg = { + .bitrate = 62000, + .adr = 0x40, + .polarity = 0, + .clock_mode = 0, +}; + +static void cxd_attach(struct ngene *dev) +{ + struct ngene_ci *ci = &dev->ci; + + ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter); + ci->dev = dev; + return; +} + +static void cxd_detach(struct ngene *dev) +{ + struct ngene_ci *ci = &dev->ci; + + dvb_ca_en50221_release(ci->en); + kfree(ci->en); + ci->en = 0; +} + +/***********************************/ +/* workaround for shutdown failure */ +/***********************************/ + +static void ngene_unlink(struct ngene *dev) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_MEM_WRITE; + com.cmd.hdr.Length = 3; + com.cmd.MemoryWrite.address = 0x910c; + com.cmd.MemoryWrite.data = 0xff; + com.in_len = 3; + com.out_len = 1; + + down(&dev->cmd_mutex); + ngwritel(0, NGENE_INT_ENABLE); + ngene_command_mutex(dev, &com); + up(&dev->cmd_mutex); +} + +void ngene_shutdown(struct pci_dev *pdev) +{ + struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev); + + if (!dev || !shutdown_workaround) + return; + + printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n"); + ngene_unlink(dev); + pci_disable_device(pdev); +} + +/****************************************************************************/ +/* device probe/remove calls ************************************************/ +/****************************************************************************/ + +void __devexit ngene_remove(struct pci_dev *pdev) +{ + struct ngene *dev = pci_get_drvdata(pdev); + int i; + + tasklet_kill(&dev->event_tasklet); + for (i = MAX_STREAM - 1; i >= 0; i--) + release_channel(&dev->channel[i]); + if (dev->ci.en) + cxd_detach(dev); + ngene_stop(dev); + ngene_release_buffers(dev); + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); +} + +int __devinit ngene_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct ngene *dev; + int stat = 0; + + if (pci_enable_device(pci_dev) < 0) + return -ENODEV; + + dev = vzalloc(sizeof(struct ngene)); + if (dev == NULL) { + stat = -ENOMEM; + goto fail0; + } + + dev->pci_dev = pci_dev; + dev->card_info = (struct ngene_info *)id->driver_data; + printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name); + + pci_set_drvdata(pci_dev, dev); + + /* Alloc buffers and start nGene */ + stat = ngene_get_buffers(dev); + if (stat < 0) + goto fail1; + stat = ngene_start(dev); + if (stat < 0) + goto fail1; + + cxd_attach(dev); + + stat = ngene_buffer_config(dev); + if (stat < 0) + goto fail1; + + + dev->i2c_current_bus = -1; + + /* Register DVB adapters and devices for both channels */ + if (init_channels(dev) < 0) + goto fail2; + + return 0; + +fail2: + ngene_stop(dev); +fail1: + ngene_release_buffers(dev); +fail0: + pci_disable_device(pci_dev); + pci_set_drvdata(pci_dev, NULL); + return stat; +} diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c new file mode 100644 index 000000000000..fcb16a615aab --- /dev/null +++ b/drivers/media/pci/ngene/ngene-dvb.c @@ -0,0 +1,261 @@ +/* + * ngene-dvb.c: nGene PCIe bridge driver - DVB functions + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ngene.h" + + +/****************************************************************************/ +/* COMMAND API interface ****************************************************/ +/****************************************************************************/ + +static ssize_t ts_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + + if (wait_event_interruptible(dev->tsout_rbuf.queue, + dvb_ringbuffer_free + (&dev->tsout_rbuf) >= count) < 0) + return 0; + + dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); + + return count; +} + +static ssize_t ts_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int left, avail; + + left = count; + while (left) { + if (wait_event_interruptible( + dev->tsin_rbuf.queue, + dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0) + return -EAGAIN; + avail = dvb_ringbuffer_avail(&dev->tsin_rbuf); + if (avail > left) + avail = left; + dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail); + left -= avail; + buf += avail; + } + return count; +} + +static const struct file_operations ci_fops = { + .owner = THIS_MODULE, + .read = ts_read, + .write = ts_write, + .open = dvb_generic_open, + .release = dvb_generic_release, +}; + +struct dvb_device ngene_dvbdev_ci = { + .priv = 0, + .readers = -1, + .writers = -1, + .users = -1, + .fops = &ci_fops, +}; + + +/****************************************************************************/ +/* DVB functions and API interface ******************************************/ +/****************************************************************************/ + +static void swap_buffer(u32 *p, u32 len) +{ + while (len) { + *p = swab32(*p); + p++; + len -= 4; + } +} + +/* start of filler packet */ +static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER }; + +/* #define DEBUG_CI_XFER */ +#ifdef DEBUG_CI_XFER +static u32 ok; +static u32 overflow; +static u32 stripped; +#endif + +void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + struct ngene *dev = chan->dev; + + + if (flags & DF_SWAP32) + swap_buffer(buf, len); + + if (dev->ci.en && chan->number == 2) { + while (len >= 188) { + if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) { + if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) { + dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188); + wake_up(&dev->tsin_rbuf.queue); +#ifdef DEBUG_CI_XFER + ok++; +#endif + } +#ifdef DEBUG_CI_XFER + else + overflow++; +#endif + } +#ifdef DEBUG_CI_XFER + else + stripped++; + + if (ok % 100 == 0 && overflow) + printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped); +#endif + buf += 188; + len -= 188; + } + return NULL; + } + + if (chan->users > 0) + dvb_dmx_swfilter(&chan->demux, buf, len); + + return NULL; +} + +void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + struct ngene *dev = chan->dev; + u32 alen; + + alen = dvb_ringbuffer_avail(&dev->tsout_rbuf); + alen -= alen % 188; + + if (alen < len) + FillTSBuffer(buf + alen, len - alen, flags); + else + alen = len; + dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen); + if (flags & DF_SWAP32) + swap_buffer((u32 *)buf, alen); + wake_up_interruptible(&dev->tsout_rbuf.queue); + return buf; +} + + + +int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ngene_channel *chan = dvbdmx->priv; + + if (chan->users == 0) { + if (!chan->dev->cmd_timeout_workaround || !chan->running) + set_transfer(chan, 1); + } + + return ++chan->users; +} + +int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ngene_channel *chan = dvbdmx->priv; + + if (--chan->users) + return chan->users; + + if (!chan->dev->cmd_timeout_workaround) + set_transfer(chan, 0); + + return 0; +} + +int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv) +{ + dvbdemux->priv = priv; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = start_feed; + dvbdemux->stop_feed = stop_feed; + dvbdemux->write_to_decoder = NULL; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + return dvb_dmx_init(dvbdemux); +} + +int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter) +{ + int ret; + + dmxdev->filternum = 256; + dmxdev->demux = &dvbdemux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adapter); + if (ret < 0) + return ret; + + hw_frontend->source = DMX_FRONTEND_0; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); + mem_frontend->source = DMX_MEMORY_FE; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); + return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +} diff --git a/drivers/media/pci/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c new file mode 100644 index 000000000000..d28554f8ce99 --- /dev/null +++ b/drivers/media/pci/ngene/ngene-i2c.c @@ -0,0 +1,176 @@ +/* + * ngene-i2c.c: nGene PCIe bridge driver i2c functions + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +/* FIXME - some of these can probably be removed */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ngene.h" + +/* Firmware command for i2c operations */ +static int ngene_command_i2c_read(struct ngene *dev, u8 adr, + u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_I2C_READ; + com.cmd.hdr.Length = outlen + 3; + com.cmd.I2CRead.Device = adr << 1; + memcpy(com.cmd.I2CRead.Data, out, outlen); + com.cmd.I2CRead.Data[outlen] = inlen; + com.cmd.I2CRead.Data[outlen + 1] = 0; + com.in_len = outlen + 3; + com.out_len = inlen + 1; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + if ((com.cmd.raw8[0] >> 1) != adr) + return -EIO; + + if (flag) + memcpy(in, com.cmd.raw8, inlen + 1); + else + memcpy(in, com.cmd.raw8 + 1, inlen); + return 0; +} + +static int ngene_command_i2c_write(struct ngene *dev, u8 adr, + u8 *out, u8 outlen) +{ + struct ngene_command com; + + + com.cmd.hdr.Opcode = CMD_I2C_WRITE; + com.cmd.hdr.Length = outlen + 1; + com.cmd.I2CRead.Device = adr << 1; + memcpy(com.cmd.I2CRead.Data, out, outlen); + com.in_len = outlen + 1; + com.out_len = 1; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + if (com.cmd.raw8[0] == 1) + return -EIO; + + return 0; +} + +static void ngene_i2c_set_bus(struct ngene *dev, int bus) +{ + if (!(dev->card_info->i2c_access & 2)) + return; + if (dev->i2c_current_bus == bus) + return; + + switch (bus) { + case 0: + ngene_command_gpio_set(dev, 3, 0); + ngene_command_gpio_set(dev, 2, 1); + break; + + case 1: + ngene_command_gpio_set(dev, 2, 0); + ngene_command_gpio_set(dev, 3, 1); + break; + } + dev->i2c_current_bus = bus; +} + +static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msg[], int num) +{ + struct ngene_channel *chan = + (struct ngene_channel *)i2c_get_adapdata(adapter); + struct ngene *dev = chan->dev; + + down(&dev->i2c_switch_mutex); + ngene_i2c_set_bus(dev, chan->number); + + if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_read(dev, msg[0].addr, + msg[0].buf, msg[0].len, + msg[1].buf, msg[1].len, 0)) + goto done; + + if (num == 1 && !(msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_write(dev, msg[0].addr, + msg[0].buf, msg[0].len)) + goto done; + if (num == 1 && (msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_read(dev, msg[0].addr, NULL, 0, + msg[0].buf, msg[0].len, 0)) + goto done; + + up(&dev->i2c_switch_mutex); + return -EIO; + +done: + up(&dev->i2c_switch_mutex); + return num; +} + + +static u32 ngene_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ngene_i2c_algo = { + .master_xfer = ngene_i2c_master_xfer, + .functionality = ngene_i2c_functionality, +}; + +int ngene_i2c_init(struct ngene *dev, int dev_nr) +{ + struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); + + i2c_set_adapdata(adap, &(dev->channel[dev_nr])); + + strcpy(adap->name, "nGene"); + + adap->algo = &ngene_i2c_algo; + adap->algo_data = (void *)&(dev->channel[dev_nr]); + adap->dev.parent = &dev->pci_dev->dev; + + return i2c_add_adapter(adap); +} + diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h new file mode 100644 index 000000000000..5443dc0caea5 --- /dev/null +++ b/drivers/media/pci/ngene/ngene.h @@ -0,0 +1,921 @@ +/* + * ngene.h: nGene PCIe bridge driver + * + * Copyright (C) 2005-2007 Micronas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _NGENE_H_ +#define _NGENE_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_ca_en50221.h" +#include "dvb_frontend.h" +#include "dvb_ringbuffer.h" +#include "dvb_net.h" +#include "cxd2099.h" + +#define DEVICE_NAME "ngene" + +#define NGENE_VID 0x18c3 +#define NGENE_PID 0x0720 + +#ifndef VIDEO_CAP_VC1 +#define VIDEO_CAP_AVC 128 +#define VIDEO_CAP_H264 128 +#define VIDEO_CAP_VC1 256 +#define VIDEO_CAP_WMV9 256 +#define VIDEO_CAP_MPEG4 512 +#endif + +enum STREAM { + STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */ + STREAM_VIDEOIN2, + STREAM_AUDIOIN1, /* I2S or SPI Input */ + STREAM_AUDIOIN2, + STREAM_AUDIOOUT, + MAX_STREAM +}; + +enum SMODE_BITS { + SMODE_AUDIO_SPDIF = 0x20, + SMODE_AVSYNC = 0x10, + SMODE_TRANSPORT_STREAM = 0x08, + SMODE_AUDIO_CAPTURE = 0x04, + SMODE_VBI_CAPTURE = 0x02, + SMODE_VIDEO_CAPTURE = 0x01 +}; + +enum STREAM_FLAG_BITS { + SFLAG_CHROMA_FORMAT_2COMP = 0x01, /* Chroma Format : 2's complement */ + SFLAG_CHROMA_FORMAT_OFFSET = 0x00, /* Chroma Format : Binary offset */ + SFLAG_ORDER_LUMA_CHROMA = 0x02, /* Byte order: Y,Cb,Y,Cr */ + SFLAG_ORDER_CHROMA_LUMA = 0x00, /* Byte order: Cb,Y,Cr,Y */ + SFLAG_COLORBAR = 0x04, /* Select colorbar */ +}; + +#define PROGRAM_ROM 0x0000 +#define PROGRAM_SRAM 0x1000 +#define PERIPHERALS0 0x8000 +#define PERIPHERALS1 0x9000 +#define SHARED_BUFFER 0xC000 + +#define HOST_TO_NGENE (SHARED_BUFFER+0x0000) +#define NGENE_TO_HOST (SHARED_BUFFER+0x0100) +#define NGENE_COMMAND (SHARED_BUFFER+0x0200) +#define NGENE_COMMAND_HI (SHARED_BUFFER+0x0204) +#define NGENE_STATUS (SHARED_BUFFER+0x0208) +#define NGENE_STATUS_HI (SHARED_BUFFER+0x020C) +#define NGENE_EVENT (SHARED_BUFFER+0x0210) +#define NGENE_EVENT_HI (SHARED_BUFFER+0x0214) +#define VARIABLES (SHARED_BUFFER+0x0210) + +#define NGENE_INT_COUNTS (SHARED_BUFFER+0x0260) +#define NGENE_INT_ENABLE (SHARED_BUFFER+0x0264) +#define NGENE_VBI_LINE_COUNT (SHARED_BUFFER+0x0268) + +#define BUFFER_GP_XMIT (SHARED_BUFFER+0x0800) +#define BUFFER_GP_RECV (SHARED_BUFFER+0x0900) +#define EEPROM_AREA (SHARED_BUFFER+0x0A00) + +#define SG_V_IN_1 (SHARED_BUFFER+0x0A80) +#define SG_VBI_1 (SHARED_BUFFER+0x0B00) +#define SG_A_IN_1 (SHARED_BUFFER+0x0B80) +#define SG_V_IN_2 (SHARED_BUFFER+0x0C00) +#define SG_VBI_2 (SHARED_BUFFER+0x0C80) +#define SG_A_IN_2 (SHARED_BUFFER+0x0D00) +#define SG_V_OUT (SHARED_BUFFER+0x0D80) +#define SG_A_OUT2 (SHARED_BUFFER+0x0E00) + +#define DATA_A_IN_1 (SHARED_BUFFER+0x0E80) +#define DATA_A_IN_2 (SHARED_BUFFER+0x0F00) +#define DATA_A_OUT (SHARED_BUFFER+0x0F80) +#define DATA_V_IN_1 (SHARED_BUFFER+0x1000) +#define DATA_V_IN_2 (SHARED_BUFFER+0x2000) +#define DATA_V_OUT (SHARED_BUFFER+0x3000) + +#define DATA_FIFO_AREA (SHARED_BUFFER+0x1000) + +#define TIMESTAMPS 0xA000 +#define SCRATCHPAD 0xA080 +#define FORCE_INT 0xA088 +#define FORCE_NMI 0xA090 +#define INT_STATUS 0xA0A0 + +#define DEV_VER 0x9004 + +#define FW_DEBUG_DEFAULT (PROGRAM_SRAM+0x00FF) + +struct SG_ADDR { + u64 start; + u64 curr; + u16 curr_ptr; + u16 elements; + u32 pad[3]; +} __attribute__ ((__packed__)); + +struct SHARED_MEMORY { + /* C000 */ + u32 HostToNgene[64]; + + /* C100 */ + u32 NgeneToHost[64]; + + /* C200 */ + u64 NgeneCommand; + u64 NgeneStatus; + u64 NgeneEvent; + + /* C210 */ + u8 pad1[0xc260 - 0xc218]; + + /* C260 */ + u32 IntCounts; + u32 IntEnable; + + /* C268 */ + u8 pad2[0xd000 - 0xc268]; + +} __attribute__ ((__packed__)); + +struct BUFFER_STREAM_RESULTS { + u32 Clock; /* Stream time in 100ns units */ + u16 RemainingLines; /* Remaining lines in this field. + 0 for complete field */ + u8 FieldCount; /* Video field number */ + u8 Flags; /* Bit 7 = Done, Bit 6 = seen, Bit 5 = overflow, + Bit 0 = FieldID */ + u16 BlockCount; /* Audio block count (unused) */ + u8 Reserved[2]; + u32 DTOUpdate; +} __attribute__ ((__packed__)); + +struct HW_SCATTER_GATHER_ELEMENT { + u64 Address; + u32 Length; + u32 Reserved; +} __attribute__ ((__packed__)); + +struct BUFFER_HEADER { + u64 Next; + struct BUFFER_STREAM_RESULTS SR; + + u32 Number_of_entries_1; + u32 Reserved5; + u64 Address_of_first_entry_1; + + u32 Number_of_entries_2; + u32 Reserved7; + u64 Address_of_first_entry_2; +} __attribute__ ((__packed__)); + +struct EVENT_BUFFER { + u32 TimeStamp; + u8 GPIOStatus; + u8 UARTStatus; + u8 RXCharacter; + u8 EventStatus; + u32 Reserved[2]; +} __attribute__ ((__packed__)); + +/* Firmware commands. */ + +enum OPCODES { + CMD_NOP = 0, + CMD_FWLOAD_PREPARE = 0x01, + CMD_FWLOAD_FINISH = 0x02, + CMD_I2C_READ = 0x03, + CMD_I2C_WRITE = 0x04, + + CMD_I2C_WRITE_NOSTOP = 0x05, + CMD_I2C_CONTINUE_WRITE = 0x06, + CMD_I2C_CONTINUE_WRITE_NOSTOP = 0x07, + + CMD_DEBUG_OUTPUT = 0x09, + + CMD_CONTROL = 0x10, + CMD_CONFIGURE_BUFFER = 0x11, + CMD_CONFIGURE_FREE_BUFFER = 0x12, + + CMD_SPI_READ = 0x13, + CMD_SPI_WRITE = 0x14, + + CMD_MEM_READ = 0x20, + CMD_MEM_WRITE = 0x21, + CMD_SFR_READ = 0x22, + CMD_SFR_WRITE = 0x23, + CMD_IRAM_READ = 0x24, + CMD_IRAM_WRITE = 0x25, + CMD_SET_GPIO_PIN = 0x26, + CMD_SET_GPIO_INT = 0x27, + CMD_CONFIGURE_UART = 0x28, + CMD_WRITE_UART = 0x29, + MAX_CMD +}; + +enum RESPONSES { + OK = 0, + ERROR = 1 +}; + +struct FW_HEADER { + u8 Opcode; + u8 Length; +} __attribute__ ((__packed__)); + +struct FW_I2C_WRITE { + struct FW_HEADER hdr; + u8 Device; + u8 Data[250]; +} __attribute__ ((__packed__)); + +struct FW_I2C_CONTINUE_WRITE { + struct FW_HEADER hdr; + u8 Data[250]; +} __attribute__ ((__packed__)); + +struct FW_I2C_READ { + struct FW_HEADER hdr; + u8 Device; + u8 Data[252]; /* followed by two bytes of read data count */ +} __attribute__ ((__packed__)); + +struct FW_SPI_WRITE { + struct FW_HEADER hdr; + u8 ModeSelect; + u8 Data[250]; +} __attribute__ ((__packed__)); + +struct FW_SPI_READ { + struct FW_HEADER hdr; + u8 ModeSelect; + u8 Data[252]; /* followed by two bytes of read data count */ +} __attribute__ ((__packed__)); + +struct FW_FWLOAD_PREPARE { + struct FW_HEADER hdr; +} __attribute__ ((__packed__)); + +struct FW_FWLOAD_FINISH { + struct FW_HEADER hdr; + u16 Address; /* address of final block */ + u16 Length; +} __attribute__ ((__packed__)); + +/* + * Meaning of FW_STREAM_CONTROL::Mode bits: + * Bit 7: Loopback PEXin to PEXout using TVOut channel + * Bit 6: AVLOOP + * Bit 5: Audio select; 0=I2S, 1=SPDIF + * Bit 4: AVSYNC + * Bit 3: Enable transport stream + * Bit 2: Enable audio capture + * Bit 1: Enable ITU-Video VBI capture + * Bit 0: Enable ITU-Video capture + * + * Meaning of FW_STREAM_CONTROL::Control bits (see UVI1_CTL) + * Bit 7: continuous capture + * Bit 6: capture one field + * Bit 5: capture one frame + * Bit 4: unused + * Bit 3: starting field; 0=odd, 1=even + * Bit 2: sample size; 0=8-bit, 1=10-bit + * Bit 1: data format; 0=UYVY, 1=YUY2 + * Bit 0: resets buffer pointers +*/ + +enum FSC_MODE_BITS { + SMODE_LOOPBACK = 0x80, + SMODE_AVLOOP = 0x40, + _SMODE_AUDIO_SPDIF = 0x20, + _SMODE_AVSYNC = 0x10, + _SMODE_TRANSPORT_STREAM = 0x08, + _SMODE_AUDIO_CAPTURE = 0x04, + _SMODE_VBI_CAPTURE = 0x02, + _SMODE_VIDEO_CAPTURE = 0x01 +}; + + +/* Meaning of FW_STREAM_CONTROL::Stream bits: + * Bit 3: Audio sample count: 0 = relative, 1 = absolute + * Bit 2: color bar select; 1=color bars, 0=CV3 decoder + * Bits 1-0: stream select, UVI1, UVI2, TVOUT + */ + +struct FW_STREAM_CONTROL { + struct FW_HEADER hdr; + u8 Stream; /* Stream number (UVI1, UVI2, TVOUT) */ + u8 Control; /* Value written to UVI1_CTL */ + u8 Mode; /* Controls clock source */ + u8 SetupDataLen; /* Length of setup data, MSB=1 write + backwards */ + u16 CaptureBlockCount; /* Blocks (a 256 Bytes) to capture per buffer + for TS and Audio */ + u64 Buffer_Address; /* Address of first buffer header */ + u16 BytesPerVideoLine; + u16 MaxLinesPerField; + u16 MinLinesPerField; + u16 Reserved_1; + u16 BytesPerVBILine; + u16 MaxVBILinesPerField; + u16 MinVBILinesPerField; + u16 SetupDataAddr; /* ngene relative address of setup data */ + u8 SetupData[32]; /* setup data */ +} __attribute__((__packed__)); + +#define AUDIO_BLOCK_SIZE 256 +#define TS_BLOCK_SIZE 256 + +struct FW_MEM_READ { + struct FW_HEADER hdr; + u16 address; +} __attribute__ ((__packed__)); + +struct FW_MEM_WRITE { + struct FW_HEADER hdr; + u16 address; + u8 data; +} __attribute__ ((__packed__)); + +struct FW_SFR_IRAM_READ { + struct FW_HEADER hdr; + u8 address; +} __attribute__ ((__packed__)); + +struct FW_SFR_IRAM_WRITE { + struct FW_HEADER hdr; + u8 address; + u8 data; +} __attribute__ ((__packed__)); + +struct FW_SET_GPIO_PIN { + struct FW_HEADER hdr; + u8 select; +} __attribute__ ((__packed__)); + +struct FW_SET_GPIO_INT { + struct FW_HEADER hdr; + u8 select; +} __attribute__ ((__packed__)); + +struct FW_SET_DEBUGMODE { + struct FW_HEADER hdr; + u8 debug_flags; +} __attribute__ ((__packed__)); + +struct FW_CONFIGURE_BUFFERS { + struct FW_HEADER hdr; + u8 config; +} __attribute__ ((__packed__)); + +enum _BUFFER_CONFIGS { + /* 4k UVI1, 4k UVI2, 2k AUD1, 2k AUD2 (standard usage) */ + BUFFER_CONFIG_4422 = 0, + /* 3k UVI1, 3k UVI2, 3k AUD1, 3k AUD2 (4x TS input usage) */ + BUFFER_CONFIG_3333 = 1, + /* 8k UVI1, 0k UVI2, 2k AUD1, 2k I2SOut (HDTV decoder usage) */ + BUFFER_CONFIG_8022 = 2, + BUFFER_CONFIG_FW17 = 255, /* Use new FW 17 command */ +}; + +struct FW_CONFIGURE_FREE_BUFFERS { + struct FW_HEADER hdr; + u8 UVI1_BufferLength; + u8 UVI2_BufferLength; + u8 TVO_BufferLength; + u8 AUD1_BufferLength; + u8 AUD2_BufferLength; + u8 TVA_BufferLength; +} __attribute__ ((__packed__)); + +struct FW_CONFIGURE_UART { + struct FW_HEADER hdr; + u8 UartControl; +} __attribute__ ((__packed__)); + +enum _UART_CONFIG { + _UART_BAUDRATE_19200 = 0, + _UART_BAUDRATE_9600 = 1, + _UART_BAUDRATE_4800 = 2, + _UART_BAUDRATE_2400 = 3, + _UART_RX_ENABLE = 0x40, + _UART_TX_ENABLE = 0x80, +}; + +struct FW_WRITE_UART { + struct FW_HEADER hdr; + u8 Data[252]; +} __attribute__ ((__packed__)); + + +struct ngene_command { + u32 in_len; + u32 out_len; + union { + u32 raw[64]; + u8 raw8[256]; + struct FW_HEADER hdr; + struct FW_I2C_WRITE I2CWrite; + struct FW_I2C_CONTINUE_WRITE I2CContinueWrite; + struct FW_I2C_READ I2CRead; + struct FW_STREAM_CONTROL StreamControl; + struct FW_FWLOAD_PREPARE FWLoadPrepare; + struct FW_FWLOAD_FINISH FWLoadFinish; + struct FW_MEM_READ MemoryRead; + struct FW_MEM_WRITE MemoryWrite; + struct FW_SFR_IRAM_READ SfrIramRead; + struct FW_SFR_IRAM_WRITE SfrIramWrite; + struct FW_SPI_WRITE SPIWrite; + struct FW_SPI_READ SPIRead; + struct FW_SET_GPIO_PIN SetGpioPin; + struct FW_SET_GPIO_INT SetGpioInt; + struct FW_SET_DEBUGMODE SetDebugMode; + struct FW_CONFIGURE_BUFFERS ConfigureBuffers; + struct FW_CONFIGURE_FREE_BUFFERS ConfigureFreeBuffers; + struct FW_CONFIGURE_UART ConfigureUart; + struct FW_WRITE_UART WriteUart; + } cmd; +} __attribute__ ((__packed__)); + +#define NGENE_INTERFACE_VERSION 0x103 +#define MAX_VIDEO_BUFFER_SIZE (417792) /* 288*1440 rounded up to next page */ +#define MAX_AUDIO_BUFFER_SIZE (8192) /* Gives room for about 23msec@48KHz */ +#define MAX_VBI_BUFFER_SIZE (28672) /* 1144*18 rounded up to next page */ +#define MAX_TS_BUFFER_SIZE (98304) /* 512*188 rounded up to next page */ +#define MAX_HDTV_BUFFER_SIZE (2080768) /* 541*1920*2 rounded up to next page + Max: (1920x1080i60) */ + +#define OVERFLOW_BUFFER_SIZE (8192) + +#define RING_SIZE_VIDEO 4 +#define RING_SIZE_AUDIO 8 +#define RING_SIZE_TS 8 + +#define NUM_SCATTER_GATHER_ENTRIES 8 + +#define MAX_DMA_LENGTH (((MAX_VIDEO_BUFFER_SIZE + MAX_VBI_BUFFER_SIZE) * \ + RING_SIZE_VIDEO * 2) + \ + (MAX_AUDIO_BUFFER_SIZE * RING_SIZE_AUDIO * 2) + \ + (MAX_TS_BUFFER_SIZE * RING_SIZE_TS * 4) + \ + (RING_SIZE_VIDEO * PAGE_SIZE * 2) + \ + (RING_SIZE_AUDIO * PAGE_SIZE * 2) + \ + (RING_SIZE_TS * PAGE_SIZE * 4) + \ + 8 * PAGE_SIZE + OVERFLOW_BUFFER_SIZE + PAGE_SIZE) + +#define EVENT_QUEUE_SIZE 16 + +/* Gathers the current state of a single channel. */ + +struct SBufferHeader { + struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */ + struct SBufferHeader *Next; + void *Buffer1; + struct HW_SCATTER_GATHER_ELEMENT *scList1; + void *Buffer2; + struct HW_SCATTER_GATHER_ELEMENT *scList2; +}; + +/* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */ +#define SIZEOF_SBufferHeader ((sizeof(struct SBufferHeader) + 63) & ~63) + +enum HWSTATE { + HWSTATE_STOP, + HWSTATE_STARTUP, + HWSTATE_RUN, + HWSTATE_PAUSE, +}; + +enum KSSTATE { + KSSTATE_STOP, + KSSTATE_ACQUIRE, + KSSTATE_PAUSE, + KSSTATE_RUN, +}; + +struct SRingBufferDescriptor { + struct SBufferHeader *Head; /* Points to first buffer in ring buffer + structure*/ + u64 PAHead; /* Physical address of first buffer */ + u32 MemSize; /* Memory size of allocated ring buffers + (needed for freeing) */ + u32 NumBuffers; /* Number of buffers in the ring */ + u32 Buffer1Length; /* Allocated length of Buffer 1 */ + u32 Buffer2Length; /* Allocated length of Buffer 2 */ + void *SCListMem; /* Memory to hold scatter gather lists for this + ring */ + u64 PASCListMem; /* Physical address .. */ + u32 SCListMemSize; /* Size of this memory */ +}; + +enum STREAMMODEFLAGS { + StreamMode_NONE = 0, /* Stream not used */ + StreamMode_ANALOG = 1, /* Analog: Stream 0,1 = Video, 2,3 = Audio */ + StreamMode_TSIN = 2, /* Transport stream input (all) */ + StreamMode_HDTV = 4, /* HDTV: Maximum 1920x1080p30,1920x1080i60 + (only stream 0) */ + StreamMode_TSOUT = 8, /* Transport stream output (only stream 3) */ +}; + + +enum BufferExchangeFlags { + BEF_EVEN_FIELD = 0x00000001, + BEF_CONTINUATION = 0x00000002, + BEF_MORE_DATA = 0x00000004, + BEF_OVERFLOW = 0x00000008, + DF_SWAP32 = 0x00010000, +}; + +typedef void *(IBufferExchange)(void *, void *, u32, u32, u32); + +struct MICI_STREAMINFO { + IBufferExchange *pExchange; + IBufferExchange *pExchangeVBI; /* Secondary (VBI, ancillary) */ + u8 Stream; + u8 Flags; + u8 Mode; + u8 Reserved; + u16 nLinesVideo; + u16 nBytesPerLineVideo; + u16 nLinesVBI; + u16 nBytesPerLineVBI; + u32 CaptureLength; /* Used for audio and transport stream */ +}; + +/****************************************************************************/ +/* STRUCTS ******************************************************************/ +/****************************************************************************/ + +/* sound hardware definition */ +#define MIXER_ADDR_TVTUNER 0 +#define MIXER_ADDR_LAST 0 + +struct ngene_channel; + +/*struct sound chip*/ + +struct mychip { + struct ngene_channel *chan; + struct snd_card *card; + struct pci_dev *pci; + struct snd_pcm_substream *substream; + struct snd_pcm *pcm; + unsigned long port; + int irq; + spinlock_t mixer_lock; + spinlock_t lock; + int mixer_volume[MIXER_ADDR_LAST + 1][2]; + int capture_source[MIXER_ADDR_LAST + 1][2]; +}; + +#ifdef NGENE_V4L +struct ngene_overlay { + int tvnorm; + struct v4l2_rect w; + enum v4l2_field field; + struct v4l2_clip *clips; + int nclips; + int setup_ok; +}; + +struct ngene_tvnorm { + int v4l2_id; + char *name; + u16 swidth, sheight; /* scaled standard width, height */ + int tuner_norm; + int soundstd; +}; + +struct ngene_vopen { + struct ngene_channel *ch; + enum v4l2_priority prio; + int width; + int height; + int depth; + struct videobuf_queue vbuf_q; + struct videobuf_queue vbi; + int fourcc; + int picxcount; + int resources; + enum v4l2_buf_type type; + const struct ngene_format *fmt; + + const struct ngene_format *ovfmt; + struct ngene_overlay ov; +}; +#endif + +struct ngene_channel { + struct device device; + struct i2c_adapter i2c_adapter; + + struct ngene *dev; + int number; + int type; + int mode; + bool has_adapter; + bool has_demux; + int demod_type; + int (*gate_ctrl)(struct dvb_frontend *, int); + + struct dvb_frontend *fe; + struct dvb_frontend *fe2; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvbnet; + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + int users; + struct video_device *v4l_dev; + struct dvb_device *ci_dev; + struct tasklet_struct demux_tasklet; + + struct SBufferHeader *nextBuffer; + enum KSSTATE State; + enum HWSTATE HWState; + u8 Stream; + u8 Flags; + u8 Mode; + IBufferExchange *pBufferExchange; + IBufferExchange *pBufferExchange2; + + spinlock_t state_lock; + u16 nLines; + u16 nBytesPerLine; + u16 nVBILines; + u16 nBytesPerVBILine; + u16 itumode; + u32 Capture1Length; + u32 Capture2Length; + struct SRingBufferDescriptor RingBuffer; + struct SRingBufferDescriptor TSRingBuffer; + struct SRingBufferDescriptor TSIdleBuffer; + + u32 DataFormatFlags; + + int AudioDTOUpdated; + u32 AudioDTOValue; + + int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t); + u8 lnbh; + + /* stuff from analog driver */ + + int minor; + struct mychip *mychip; + struct snd_card *soundcard; + u8 *evenbuffer; + u8 dma_on; + int soundstreamon; + int audiomute; + int soundbuffisallocated; + int sndbuffflag; + int tun_rdy; + int dec_rdy; + int tun_dec_rdy; + int lastbufferflag; + + struct ngene_tvnorm *tvnorms; + int tvnorm_num; + int tvnorm; + +#ifdef NGENE_V4L + int videousers; + struct v4l2_prio_state prio; + struct ngene_vopen init; + int resources; + struct v4l2_framebuffer fbuf; + struct ngene_buffer *screen; /* overlay */ + struct list_head capture; /* video capture queue */ + spinlock_t s_lock; + struct semaphore reslock; +#endif + + int running; +}; + + +struct ngene_ci { + struct device device; + struct i2c_adapter i2c_adapter; + + struct ngene *dev; + struct dvb_ca_en50221 *en; +}; + +struct ngene; + +typedef void (rx_cb_t)(struct ngene *, u32, u8); +typedef void (tx_cb_t)(struct ngene *, u32); + +struct ngene { + int nr; + struct pci_dev *pci_dev; + unsigned char *iomem; + + /*struct i2c_adapter i2c_adapter;*/ + + u32 device_version; + u32 fw_interface_version; + u32 icounts; + bool msi_enabled; + bool cmd_timeout_workaround; + + u8 *CmdDoneByte; + int BootFirmware; + void *OverflowBuffer; + dma_addr_t PAOverflowBuffer; + void *FWInterfaceBuffer; + dma_addr_t PAFWInterfaceBuffer; + u8 *ngenetohost; + u8 *hosttongene; + + struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE]; + int EventQueueOverflowCount; + int EventQueueOverflowFlag; + struct tasklet_struct event_tasklet; + struct EVENT_BUFFER *EventBuffer; + int EventQueueWriteIndex; + int EventQueueReadIndex; + + wait_queue_head_t cmd_wq; + int cmd_done; + struct semaphore cmd_mutex; + struct semaphore stream_mutex; + struct semaphore pll_mutex; + struct semaphore i2c_switch_mutex; + int i2c_current_channel; + int i2c_current_bus; + spinlock_t cmd_lock; + + struct dvb_adapter adapter[MAX_STREAM]; + struct dvb_adapter *first_adapter; /* "one_adapter" modprobe opt */ + struct ngene_channel channel[MAX_STREAM]; + + struct ngene_info *card_info; + + tx_cb_t *TxEventNotify; + rx_cb_t *RxEventNotify; + int tx_busy; + wait_queue_head_t tx_wq; + wait_queue_head_t rx_wq; +#define UART_RBUF_LEN 4096 + u8 uart_rbuf[UART_RBUF_LEN]; + int uart_rp, uart_wp; + +#define TS_FILLER 0x6f + + u8 *tsout_buf; +#define TSOUT_BUF_SIZE (512*188*8) + struct dvb_ringbuffer tsout_rbuf; + + u8 *tsin_buf; +#define TSIN_BUF_SIZE (512*188*8) + struct dvb_ringbuffer tsin_rbuf; + + u8 *ain_buf; +#define AIN_BUF_SIZE (128*1024) + struct dvb_ringbuffer ain_rbuf; + + + u8 *vin_buf; +#define VIN_BUF_SIZE (4*1920*1080) + struct dvb_ringbuffer vin_rbuf; + + unsigned long exp_val; + int prev_cmd; + + struct ngene_ci ci; +}; + +struct ngene_info { + int type; +#define NGENE_APP 0 +#define NGENE_TERRATEC 1 +#define NGENE_SIDEWINDER 2 +#define NGENE_RACER 3 +#define NGENE_VIPER 4 +#define NGENE_PYTHON 5 +#define NGENE_VBOX_V1 6 +#define NGENE_VBOX_V2 7 + + int fw_version; + bool msi_supported; + char *name; + + int io_type[MAX_STREAM]; +#define NGENE_IO_NONE 0 +#define NGENE_IO_TV 1 +#define NGENE_IO_HDTV 2 +#define NGENE_IO_TSIN 4 +#define NGENE_IO_TSOUT 8 +#define NGENE_IO_AIN 16 + + void *fe_config[4]; + void *tuner_config[4]; + + int (*demod_attach[4])(struct ngene_channel *); + int (*tuner_attach[4])(struct ngene_channel *); + + u8 avf[4]; + u8 msp[4]; + u8 demoda[4]; + u8 lnb[4]; + int i2c_access; + u8 ntsc; + u8 tsf[4]; + u8 i2s[4]; + + int (*gate_ctrl)(struct dvb_frontend *, int); + int (*switch_ctrl)(struct ngene_channel *, int, int); +}; + +#ifdef NGENE_V4L +struct ngene_format { + char *name; + int fourcc; /* video4linux 2 */ + int btformat; /* BT848_COLOR_FMT_* */ + int format; + int btswap; /* BT848_COLOR_CTL_* */ + int depth; /* bit/pixel */ + int flags; + int hshift, vshift; /* for planar modes */ + int palette; +}; + +#define RESOURCE_OVERLAY 1 +#define RESOURCE_VIDEO 2 +#define RESOURCE_VBI 4 + +struct ngene_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* ngene specific */ + const struct ngene_format *fmt; + int tvnorm; + int btformat; + int btswap; +}; +#endif + + +/* Provided by ngene-core.c */ +int __devinit ngene_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id); +void __devexit ngene_remove(struct pci_dev *pdev); +void ngene_shutdown(struct pci_dev *pdev); +int ngene_command(struct ngene *dev, struct ngene_command *com); +int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level); +void set_transfer(struct ngene_channel *chan, int state); +void FillTSBuffer(void *Buffer, int Length, u32 Flags); + +/* Provided by ngene-i2c.c */ +int ngene_i2c_init(struct ngene *dev, int dev_nr); + +/* Provided by ngene-dvb.c */ +extern struct dvb_device ngene_dvbdev_ci; +void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); +void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); +int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed); +int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv); +int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter); + +#endif + +/* LocalWords: Endif + */ diff --git a/drivers/media/pci/pluto2/Kconfig b/drivers/media/pci/pluto2/Kconfig new file mode 100644 index 000000000000..7d8e6e87bdbb --- /dev/null +++ b/drivers/media/pci/pluto2/Kconfig @@ -0,0 +1,15 @@ +config DVB_PLUTO2 + tristate "Pluto2 cards" + depends on DVB_CORE && PCI && I2C + select I2C_ALGOBIT + select DVB_TDA1004X + help + Support for PCI cards based on the Pluto2 FPGA like the Satelco + Easywatch Mobile Terrestrial DVB-T Receiver. + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the PCI bus, so you need + an external software decoder to watch TV on your computer. + + Say Y or M if you own such a device and want to use it. + diff --git a/drivers/media/pci/pluto2/Makefile b/drivers/media/pci/pluto2/Makefile new file mode 100644 index 000000000000..524bf841f42b --- /dev/null +++ b/drivers/media/pci/pluto2/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_DVB_PLUTO2) += pluto2.o + +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ diff --git a/drivers/media/pci/pluto2/pluto2.c b/drivers/media/pci/pluto2/pluto2.c new file mode 100644 index 000000000000..f148b19a206a --- /dev/null +++ b/drivers/media/pci/pluto2/pluto2.c @@ -0,0 +1,815 @@ +/* + * pluto2.c - Satelco Easywatch Mobile Terrestrial Receiver [DVB-T] + * + * Copyright (C) 2005 Andreas Oberritter + * + * based on pluto2.c 1.10 - http://instinct-wp8.no-ip.org/pluto/ + * by Dany Salman + * Copyright (c) 2004 TDF + * + * This program is free software; 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 + +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" +#include "tda1004x.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define DRIVER_NAME "pluto2" + +#define REG_PIDn(n) ((n) << 2) /* PID n pattern registers */ +#define REG_PCAR 0x0020 /* PC address register */ +#define REG_TSCR 0x0024 /* TS ctrl & status */ +#define REG_MISC 0x0028 /* miscellaneous */ +#define REG_MMAC 0x002c /* MSB MAC address */ +#define REG_IMAC 0x0030 /* ISB MAC address */ +#define REG_LMAC 0x0034 /* LSB MAC address */ +#define REG_SPID 0x0038 /* SPI data */ +#define REG_SLCS 0x003c /* serial links ctrl/status */ + +#define PID0_NOFIL (0x0001 << 16) +#define PIDn_ENP (0x0001 << 15) +#define PID0_END (0x0001 << 14) +#define PID0_AFIL (0x0001 << 13) +#define PIDn_PID (0x1fff << 0) + +#define TSCR_NBPACKETS (0x00ff << 24) +#define TSCR_DEM (0x0001 << 17) +#define TSCR_DE (0x0001 << 16) +#define TSCR_RSTN (0x0001 << 15) +#define TSCR_MSKO (0x0001 << 14) +#define TSCR_MSKA (0x0001 << 13) +#define TSCR_MSKL (0x0001 << 12) +#define TSCR_OVR (0x0001 << 11) +#define TSCR_AFUL (0x0001 << 10) +#define TSCR_LOCK (0x0001 << 9) +#define TSCR_IACK (0x0001 << 8) +#define TSCR_ADEF (0x007f << 0) + +#define MISC_DVR (0x0fff << 4) +#define MISC_ALED (0x0001 << 3) +#define MISC_FRST (0x0001 << 2) +#define MISC_LED1 (0x0001 << 1) +#define MISC_LED0 (0x0001 << 0) + +#define SPID_SPIDR (0x00ff << 0) + +#define SLCS_SCL (0x0001 << 7) +#define SLCS_SDA (0x0001 << 6) +#define SLCS_CSN (0x0001 << 2) +#define SLCS_OVR (0x0001 << 1) +#define SLCS_SWC (0x0001 << 0) + +#define TS_DMA_PACKETS (8) +#define TS_DMA_BYTES (188 * TS_DMA_PACKETS) + +#define I2C_ADDR_TDA10046 0x10 +#define I2C_ADDR_TUA6034 0xc2 +#define NHWFILTERS 8 + +struct pluto { + /* pci */ + struct pci_dev *pdev; + u8 __iomem *io_mem; + + /* dvb */ + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + struct dmxdev dmxdev; + struct dvb_adapter dvb_adapter; + struct dvb_demux demux; + struct dvb_frontend *fe; + struct dvb_net dvbnet; + unsigned int full_ts_users; + unsigned int users; + + /* i2c */ + struct i2c_algo_bit_data i2c_bit; + struct i2c_adapter i2c_adap; + unsigned int i2cbug; + + /* irq */ + unsigned int overflow; + unsigned int dead; + + /* dma */ + dma_addr_t dma_addr; + u8 dma_buf[TS_DMA_BYTES]; + u8 dummy[4096]; +}; + +static inline struct pluto *feed_to_pluto(struct dvb_demux_feed *feed) +{ + return container_of(feed->demux, struct pluto, demux); +} + +static inline struct pluto *frontend_to_pluto(struct dvb_frontend *fe) +{ + return container_of(fe->dvb, struct pluto, dvb_adapter); +} + +static inline u32 pluto_readreg(struct pluto *pluto, u32 reg) +{ + return readl(&pluto->io_mem[reg]); +} + +static inline void pluto_writereg(struct pluto *pluto, u32 reg, u32 val) +{ + writel(val, &pluto->io_mem[reg]); +} + +static inline void pluto_rw(struct pluto *pluto, u32 reg, u32 mask, u32 bits) +{ + u32 val = readl(&pluto->io_mem[reg]); + val &= ~mask; + val |= bits; + writel(val, &pluto->io_mem[reg]); +} + +static void pluto_write_tscr(struct pluto *pluto, u32 val) +{ + /* set the number of packets */ + val &= ~TSCR_ADEF; + val |= TS_DMA_PACKETS / 2; + + pluto_writereg(pluto, REG_TSCR, val); +} + +static void pluto_setsda(void *data, int state) +{ + struct pluto *pluto = data; + + if (state) + pluto_rw(pluto, REG_SLCS, SLCS_SDA, SLCS_SDA); + else + pluto_rw(pluto, REG_SLCS, SLCS_SDA, 0); +} + +static void pluto_setscl(void *data, int state) +{ + struct pluto *pluto = data; + + if (state) + pluto_rw(pluto, REG_SLCS, SLCS_SCL, SLCS_SCL); + else + pluto_rw(pluto, REG_SLCS, SLCS_SCL, 0); + + /* try to detect i2c_inb() to workaround hardware bug: + * reset SDA to high after SCL has been set to low */ + if ((state) && (pluto->i2cbug == 0)) { + pluto->i2cbug = 1; + } else { + if ((!state) && (pluto->i2cbug == 1)) + pluto_setsda(pluto, 1); + pluto->i2cbug = 0; + } +} + +static int pluto_getsda(void *data) +{ + struct pluto *pluto = data; + + return pluto_readreg(pluto, REG_SLCS) & SLCS_SDA; +} + +static int pluto_getscl(void *data) +{ + struct pluto *pluto = data; + + return pluto_readreg(pluto, REG_SLCS) & SLCS_SCL; +} + +static void pluto_reset_frontend(struct pluto *pluto, int reenable) +{ + u32 val = pluto_readreg(pluto, REG_MISC); + + if (val & MISC_FRST) { + val &= ~MISC_FRST; + pluto_writereg(pluto, REG_MISC, val); + } + if (reenable) { + val |= MISC_FRST; + pluto_writereg(pluto, REG_MISC, val); + } +} + +static void pluto_reset_ts(struct pluto *pluto, int reenable) +{ + u32 val = pluto_readreg(pluto, REG_TSCR); + + if (val & TSCR_RSTN) { + val &= ~TSCR_RSTN; + pluto_write_tscr(pluto, val); + } + if (reenable) { + val |= TSCR_RSTN; + pluto_write_tscr(pluto, val); + } +} + +static void pluto_set_dma_addr(struct pluto *pluto) +{ + pluto_writereg(pluto, REG_PCAR, pluto->dma_addr); +} + +static int __devinit pluto_dma_map(struct pluto *pluto) +{ + pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, + TS_DMA_BYTES, PCI_DMA_FROMDEVICE); + + return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr); +} + +static void pluto_dma_unmap(struct pluto *pluto) +{ + pci_unmap_single(pluto->pdev, pluto->dma_addr, + TS_DMA_BYTES, PCI_DMA_FROMDEVICE); +} + +static int pluto_start_feed(struct dvb_demux_feed *f) +{ + struct pluto *pluto = feed_to_pluto(f); + + /* enable PID filtering */ + if (pluto->users++ == 0) + pluto_rw(pluto, REG_PIDn(0), PID0_AFIL | PID0_NOFIL, 0); + + if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) + pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, PIDn_ENP | f->pid); + else if (pluto->full_ts_users++ == 0) + pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, PID0_NOFIL); + + return 0; +} + +static int pluto_stop_feed(struct dvb_demux_feed *f) +{ + struct pluto *pluto = feed_to_pluto(f); + + /* disable PID filtering */ + if (--pluto->users == 0) + pluto_rw(pluto, REG_PIDn(0), PID0_AFIL, PID0_AFIL); + + if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) + pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, 0x1fff); + else if (--pluto->full_ts_users == 0) + pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, 0); + + return 0; +} + +static void pluto_dma_end(struct pluto *pluto, unsigned int nbpackets) +{ + /* synchronize the DMA transfer with the CPU + * first so that we see updated contents. */ + pci_dma_sync_single_for_cpu(pluto->pdev, pluto->dma_addr, + TS_DMA_BYTES, PCI_DMA_FROMDEVICE); + + /* Workaround for broken hardware: + * [1] On startup NBPACKETS seems to contain an uninitialized value, + * but no packets have been transferred. + * [2] Sometimes (actually very often) NBPACKETS stays at zero + * although one packet has been transferred. + * [3] Sometimes (actually rarely), the card gets into an erroneous + * mode where it continuously generates interrupts, claiming it + * has received nbpackets>TS_DMA_PACKETS packets, but no packet + * has been transferred. Only a reset seems to solve this + */ + if ((nbpackets == 0) || (nbpackets > TS_DMA_PACKETS)) { + unsigned int i = 0; + while (pluto->dma_buf[i] == 0x47) + i += 188; + nbpackets = i / 188; + if (i == 0) { + pluto_reset_ts(pluto, 1); + dev_printk(KERN_DEBUG, &pluto->pdev->dev, "resetting TS because of invalid packet counter\n"); + } + } + + dvb_dmx_swfilter_packets(&pluto->demux, pluto->dma_buf, nbpackets); + + /* clear the dma buffer. this is needed to be able to identify + * new valid ts packets above */ + memset(pluto->dma_buf, 0, nbpackets * 188); + + /* reset the dma address */ + pluto_set_dma_addr(pluto); + + /* sync the buffer and give it back to the card */ + pci_dma_sync_single_for_device(pluto->pdev, pluto->dma_addr, + TS_DMA_BYTES, PCI_DMA_FROMDEVICE); +} + +static irqreturn_t pluto_irq(int irq, void *dev_id) +{ + struct pluto *pluto = dev_id; + u32 tscr; + + /* check whether an interrupt occurred on this device */ + tscr = pluto_readreg(pluto, REG_TSCR); + if (!(tscr & (TSCR_DE | TSCR_OVR))) + return IRQ_NONE; + + if (tscr == 0xffffffff) { + if (pluto->dead == 0) + dev_err(&pluto->pdev->dev, "card has hung or been ejected.\n"); + /* It's dead Jim */ + pluto->dead = 1; + return IRQ_HANDLED; + } + + /* dma end interrupt */ + if (tscr & TSCR_DE) { + pluto_dma_end(pluto, (tscr & TSCR_NBPACKETS) >> 24); + /* overflow interrupt */ + if (tscr & TSCR_OVR) + pluto->overflow++; + if (pluto->overflow) { + dev_err(&pluto->pdev->dev, "overflow irq (%d)\n", + pluto->overflow); + pluto_reset_ts(pluto, 1); + pluto->overflow = 0; + } + } else if (tscr & TSCR_OVR) { + pluto->overflow++; + } + + /* ACK the interrupt */ + pluto_write_tscr(pluto, tscr | TSCR_IACK); + + return IRQ_HANDLED; +} + +static void __devinit pluto_enable_irqs(struct pluto *pluto) +{ + u32 val = pluto_readreg(pluto, REG_TSCR); + + /* disable AFUL and LOCK interrupts */ + val |= (TSCR_MSKA | TSCR_MSKL); + /* enable DMA and OVERFLOW interrupts */ + val &= ~(TSCR_DEM | TSCR_MSKO); + /* clear pending interrupts */ + val |= TSCR_IACK; + + pluto_write_tscr(pluto, val); +} + +static void pluto_disable_irqs(struct pluto *pluto) +{ + u32 val = pluto_readreg(pluto, REG_TSCR); + + /* disable all interrupts */ + val |= (TSCR_DEM | TSCR_MSKO | TSCR_MSKA | TSCR_MSKL); + /* clear pending interrupts */ + val |= TSCR_IACK; + + pluto_write_tscr(pluto, val); +} + +static int __devinit pluto_hw_init(struct pluto *pluto) +{ + pluto_reset_frontend(pluto, 1); + + /* set automatic LED control by FPGA */ + pluto_rw(pluto, REG_MISC, MISC_ALED, MISC_ALED); + + /* set data endianess */ +#ifdef __LITTLE_ENDIAN + pluto_rw(pluto, REG_PIDn(0), PID0_END, PID0_END); +#else + pluto_rw(pluto, REG_PIDn(0), PID0_END, 0); +#endif + /* map DMA and set address */ + pluto_dma_map(pluto); + pluto_set_dma_addr(pluto); + + /* enable interrupts */ + pluto_enable_irqs(pluto); + + /* reset TS logic */ + pluto_reset_ts(pluto, 1); + + return 0; +} + +static void pluto_hw_exit(struct pluto *pluto) +{ + /* disable interrupts */ + pluto_disable_irqs(pluto); + + pluto_reset_ts(pluto, 0); + + /* LED: disable automatic control, enable yellow, disable green */ + pluto_rw(pluto, REG_MISC, MISC_ALED | MISC_LED1 | MISC_LED0, MISC_LED1); + + /* unmap DMA */ + pluto_dma_unmap(pluto); + + pluto_reset_frontend(pluto, 0); +} + +static inline u32 divide(u32 numerator, u32 denominator) +{ + if (denominator == 0) + return ~0; + + return DIV_ROUND_CLOSEST(numerator, denominator); +} + +/* LG Innotek TDTE-E001P (Infineon TUA6034) */ +static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct pluto *pluto = frontend_to_pluto(fe); + struct i2c_msg msg; + int ret; + u8 buf[4]; + u32 div; + + // Fref = 166.667 Hz + // Fref * 3 = 500.000 Hz + // IF = 36166667 + // IF / Fref = 217 + //div = divide(p->frequency + 36166667, 166667); + div = divide(p->frequency * 3, 500000) + 217; + buf[0] = (div >> 8) & 0x7f; + buf[1] = (div >> 0) & 0xff; + + if (p->frequency < 611000000) + buf[2] = 0xb4; + else if (p->frequency < 811000000) + buf[2] = 0xbc; + else + buf[2] = 0xf4; + + // VHF: 174-230 MHz + // center: 350 MHz + // UHF: 470-862 MHz + if (p->frequency < 350000000) + buf[3] = 0x02; + else + buf[3] = 0x04; + + if (p->bandwidth_hz == 8000000) + buf[3] |= 0x08; + + msg.addr = I2C_ADDR_TUA6034 >> 1; + msg.flags = 0; + msg.buf = buf; + msg.len = sizeof(buf); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = i2c_transfer(&pluto->i2c_adap, &msg, 1); + if (ret < 0) + return ret; + else if (ret == 0) + return -EREMOTEIO; + + return 0; +} + +static int pluto2_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct pluto *pluto = frontend_to_pluto(fe); + + return request_firmware(fw, name, &pluto->pdev->dev); +} + +static struct tda1004x_config pluto2_fe_config __devinitdata = { + .demod_address = I2C_ADDR_TDA10046 >> 1, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = pluto2_request_firmware, +}; + +static int __devinit frontend_init(struct pluto *pluto) +{ + int ret; + + pluto->fe = tda10046_attach(&pluto2_fe_config, &pluto->i2c_adap); + if (!pluto->fe) { + dev_err(&pluto->pdev->dev, "could not attach frontend\n"); + return -ENODEV; + } + pluto->fe->ops.tuner_ops.set_params = lg_tdtpe001p_tuner_set_params; + + ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe); + if (ret < 0) { + if (pluto->fe->ops.release) + pluto->fe->ops.release(pluto->fe); + return ret; + } + + return 0; +} + +static void __devinit pluto_read_rev(struct pluto *pluto) +{ + u32 val = pluto_readreg(pluto, REG_MISC) & MISC_DVR; + dev_info(&pluto->pdev->dev, "board revision %d.%d\n", + (val >> 12) & 0x0f, (val >> 4) & 0xff); +} + +static void __devinit pluto_read_mac(struct pluto *pluto, u8 *mac) +{ + u32 val = pluto_readreg(pluto, REG_MMAC); + mac[0] = (val >> 8) & 0xff; + mac[1] = (val >> 0) & 0xff; + + val = pluto_readreg(pluto, REG_IMAC); + mac[2] = (val >> 8) & 0xff; + mac[3] = (val >> 0) & 0xff; + + val = pluto_readreg(pluto, REG_LMAC); + mac[4] = (val >> 8) & 0xff; + mac[5] = (val >> 0) & 0xff; + + dev_info(&pluto->pdev->dev, "MAC %pM\n", mac); +} + +static int __devinit pluto_read_serial(struct pluto *pluto) +{ + struct pci_dev *pdev = pluto->pdev; + unsigned int i, j; + u8 __iomem *cis; + + cis = pci_iomap(pdev, 1, 0); + if (!cis) + return -EIO; + + dev_info(&pdev->dev, "S/N "); + + for (i = 0xe0; i < 0x100; i += 4) { + u32 val = readl(&cis[i]); + for (j = 0; j < 32; j += 8) { + if ((val & 0xff) == 0xff) + goto out; + printk("%c", val & 0xff); + val >>= 8; + } + } +out: + printk("\n"); + pci_iounmap(pdev, cis); + + return 0; +} + +static int __devinit pluto2_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct pluto *pluto; + struct dvb_adapter *dvb_adapter; + struct dvb_demux *dvbdemux; + struct dmx_demux *dmx; + int ret = -ENOMEM; + + pluto = kzalloc(sizeof(struct pluto), GFP_KERNEL); + if (!pluto) + goto out; + + pluto->pdev = pdev; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto err_kfree; + + /* enable interrupts */ + pci_write_config_dword(pdev, 0x6c, 0x8000); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret < 0) + goto err_pci_disable_device; + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret < 0) + goto err_pci_disable_device; + + pluto->io_mem = pci_iomap(pdev, 0, 0x40); + if (!pluto->io_mem) { + ret = -EIO; + goto err_pci_release_regions; + } + + pci_set_drvdata(pdev, pluto); + + ret = request_irq(pdev->irq, pluto_irq, IRQF_SHARED, DRIVER_NAME, pluto); + if (ret < 0) + goto err_pci_iounmap; + + ret = pluto_hw_init(pluto); + if (ret < 0) + goto err_free_irq; + + /* i2c */ + i2c_set_adapdata(&pluto->i2c_adap, pluto); + strcpy(pluto->i2c_adap.name, DRIVER_NAME); + pluto->i2c_adap.owner = THIS_MODULE; + pluto->i2c_adap.dev.parent = &pdev->dev; + pluto->i2c_adap.algo_data = &pluto->i2c_bit; + pluto->i2c_bit.data = pluto; + pluto->i2c_bit.setsda = pluto_setsda; + pluto->i2c_bit.setscl = pluto_setscl; + pluto->i2c_bit.getsda = pluto_getsda; + pluto->i2c_bit.getscl = pluto_getscl; + pluto->i2c_bit.udelay = 10; + pluto->i2c_bit.timeout = 10; + + /* Raise SCL and SDA */ + pluto_setsda(pluto, 1); + pluto_setscl(pluto, 1); + + ret = i2c_bit_add_bus(&pluto->i2c_adap); + if (ret < 0) + goto err_pluto_hw_exit; + + /* dvb */ + ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, + THIS_MODULE, &pdev->dev, adapter_nr); + if (ret < 0) + goto err_i2c_del_adapter; + + dvb_adapter = &pluto->dvb_adapter; + + pluto_read_rev(pluto); + pluto_read_serial(pluto); + pluto_read_mac(pluto, dvb_adapter->proposed_mac); + + dvbdemux = &pluto->demux; + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = pluto_start_feed; + dvbdemux->stop_feed = pluto_stop_feed; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); + ret = dvb_dmx_init(dvbdemux); + if (ret < 0) + goto err_dvb_unregister_adapter; + + dmx = &dvbdemux->dmx; + + pluto->hw_frontend.source = DMX_FRONTEND_0; + pluto->mem_frontend.source = DMX_MEMORY_FE; + pluto->dmxdev.filternum = NHWFILTERS; + pluto->dmxdev.demux = dmx; + + ret = dvb_dmxdev_init(&pluto->dmxdev, dvb_adapter); + if (ret < 0) + goto err_dvb_dmx_release; + + ret = dmx->add_frontend(dmx, &pluto->hw_frontend); + if (ret < 0) + goto err_dvb_dmxdev_release; + + ret = dmx->add_frontend(dmx, &pluto->mem_frontend); + if (ret < 0) + goto err_remove_hw_frontend; + + ret = dmx->connect_frontend(dmx, &pluto->hw_frontend); + if (ret < 0) + goto err_remove_mem_frontend; + + ret = frontend_init(pluto); + if (ret < 0) + goto err_disconnect_frontend; + + dvb_net_init(dvb_adapter, &pluto->dvbnet, dmx); +out: + return ret; + +err_disconnect_frontend: + dmx->disconnect_frontend(dmx); +err_remove_mem_frontend: + dmx->remove_frontend(dmx, &pluto->mem_frontend); +err_remove_hw_frontend: + dmx->remove_frontend(dmx, &pluto->hw_frontend); +err_dvb_dmxdev_release: + dvb_dmxdev_release(&pluto->dmxdev); +err_dvb_dmx_release: + dvb_dmx_release(dvbdemux); +err_dvb_unregister_adapter: + dvb_unregister_adapter(dvb_adapter); +err_i2c_del_adapter: + i2c_del_adapter(&pluto->i2c_adap); +err_pluto_hw_exit: + pluto_hw_exit(pluto); +err_free_irq: + free_irq(pdev->irq, pluto); +err_pci_iounmap: + pci_iounmap(pdev, pluto->io_mem); +err_pci_release_regions: + pci_release_regions(pdev); +err_pci_disable_device: + pci_disable_device(pdev); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(pluto); + goto out; +} + +static void __devexit pluto2_remove(struct pci_dev *pdev) +{ + struct pluto *pluto = pci_get_drvdata(pdev); + struct dvb_adapter *dvb_adapter = &pluto->dvb_adapter; + struct dvb_demux *dvbdemux = &pluto->demux; + struct dmx_demux *dmx = &dvbdemux->dmx; + + dmx->close(dmx); + dvb_net_release(&pluto->dvbnet); + if (pluto->fe) + dvb_unregister_frontend(pluto->fe); + + dmx->disconnect_frontend(dmx); + dmx->remove_frontend(dmx, &pluto->mem_frontend); + dmx->remove_frontend(dmx, &pluto->hw_frontend); + dvb_dmxdev_release(&pluto->dmxdev); + dvb_dmx_release(dvbdemux); + dvb_unregister_adapter(dvb_adapter); + i2c_del_adapter(&pluto->i2c_adap); + pluto_hw_exit(pluto); + free_irq(pdev->irq, pluto); + pci_iounmap(pdev, pluto->io_mem); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + kfree(pluto); +} + +#ifndef PCI_VENDOR_ID_SCM +#define PCI_VENDOR_ID_SCM 0x0432 +#endif +#ifndef PCI_DEVICE_ID_PLUTO2 +#define PCI_DEVICE_ID_PLUTO2 0x0001 +#endif + +static struct pci_device_id pluto2_id_table[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_SCM, + .device = PCI_DEVICE_ID_PLUTO2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, { + /* empty */ + }, +}; + +MODULE_DEVICE_TABLE(pci, pluto2_id_table); + +static struct pci_driver pluto2_driver = { + .name = DRIVER_NAME, + .id_table = pluto2_id_table, + .probe = pluto2_probe, + .remove = __devexit_p(pluto2_remove), +}; + +static int __init pluto2_init(void) +{ + return pci_register_driver(&pluto2_driver); +} + +static void __exit pluto2_exit(void) +{ + pci_unregister_driver(&pluto2_driver); +} + +module_init(pluto2_init); +module_exit(pluto2_exit); + +MODULE_AUTHOR("Andreas Oberritter "); +MODULE_DESCRIPTION("Pluto2 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/pt1/Kconfig b/drivers/media/pci/pt1/Kconfig new file mode 100644 index 000000000000..24501d5bf70d --- /dev/null +++ b/drivers/media/pci/pt1/Kconfig @@ -0,0 +1,12 @@ +config DVB_PT1 + tristate "PT1 cards" + depends on DVB_CORE && PCI && I2C + help + Support for Earthsoft PT1 PCI cards. + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the PCI bus, so you need + an external software decoder to watch TV on your computer. + + Say Y or M if you own such a device and want to use it. + diff --git a/drivers/media/pci/pt1/Makefile b/drivers/media/pci/pt1/Makefile new file mode 100644 index 000000000000..98e391295afe --- /dev/null +++ b/drivers/media/pci/pt1/Makefile @@ -0,0 +1,5 @@ +earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o + +obj-$(CONFIG_DVB_PT1) += earth-pt1.o + +ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c new file mode 100644 index 000000000000..15b35c4725f1 --- /dev/null +++ b/drivers/media/pci/pt1/pt1.c @@ -0,0 +1,1246 @@ +/* + * driver for Earthsoft PT1/PT2 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * This program is free software; 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 "dvbdev.h" +#include "dvb_demux.h" +#include "dmxdev.h" +#include "dvb_net.h" +#include "dvb_frontend.h" + +#include "va1j5jf8007t.h" +#include "va1j5jf8007s.h" + +#define DRIVER_NAME "earth-pt1" + +#define PT1_PAGE_SHIFT 12 +#define PT1_PAGE_SIZE (1 << PT1_PAGE_SHIFT) +#define PT1_NR_UPACKETS 1024 +#define PT1_NR_BUFS 511 + +struct pt1_buffer_page { + __le32 upackets[PT1_NR_UPACKETS]; +}; + +struct pt1_table_page { + __le32 next_pfn; + __le32 buf_pfns[PT1_NR_BUFS]; +}; + +struct pt1_buffer { + struct pt1_buffer_page *page; + dma_addr_t addr; +}; + +struct pt1_table { + struct pt1_table_page *page; + dma_addr_t addr; + struct pt1_buffer bufs[PT1_NR_BUFS]; +}; + +#define PT1_NR_ADAPS 4 + +struct pt1_adapter; + +struct pt1 { + struct pci_dev *pdev; + void __iomem *regs; + struct i2c_adapter i2c_adap; + int i2c_running; + struct pt1_adapter *adaps[PT1_NR_ADAPS]; + struct pt1_table *tables; + struct task_struct *kthread; + int table_index; + int buf_index; + + struct mutex lock; + int power; + int reset; +}; + +struct pt1_adapter { + struct pt1 *pt1; + int index; + + u8 *buf; + int upacket_count; + int packet_count; + int st_count; + + struct dvb_adapter adap; + struct dvb_demux demux; + int users; + struct dmxdev dmxdev; + struct dvb_frontend *fe; + int (*orig_set_voltage)(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); + int (*orig_sleep)(struct dvb_frontend *fe); + int (*orig_init)(struct dvb_frontend *fe); + + fe_sec_voltage_t voltage; + int sleep; +}; + +#define pt1_printk(level, pt1, format, arg...) \ + dev_printk(level, &(pt1)->pdev->dev, format, ##arg) + +static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) +{ + writel(data, pt1->regs + reg * 4); +} + +static u32 pt1_read_reg(struct pt1 *pt1, int reg) +{ + return readl(pt1->regs + reg * 4); +} + +static int pt1_nr_tables = 8; +module_param_named(nr_tables, pt1_nr_tables, int, 0); + +static void pt1_increment_table_count(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x00000020); +} + +static void pt1_init_table_count(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x00000010); +} + +static void pt1_register_tables(struct pt1 *pt1, u32 first_pfn) +{ + pt1_write_reg(pt1, 5, first_pfn); + pt1_write_reg(pt1, 0, 0x0c000040); +} + +static void pt1_unregister_tables(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x08080000); +} + +static int pt1_sync(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 57; i++) { + if (pt1_read_reg(pt1, 0) & 0x20000000) + return 0; + pt1_write_reg(pt1, 0, 0x00000008); + } + pt1_printk(KERN_ERR, pt1, "could not sync\n"); + return -EIO; +} + +static u64 pt1_identify(struct pt1 *pt1) +{ + int i; + u64 id; + id = 0; + for (i = 0; i < 57; i++) { + id |= (u64)(pt1_read_reg(pt1, 0) >> 30 & 1) << i; + pt1_write_reg(pt1, 0, 0x00000008); + } + return id; +} + +static int pt1_unlock(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x00000008); + for (i = 0; i < 3; i++) { + if (pt1_read_reg(pt1, 0) & 0x80000000) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not unlock\n"); + return -EIO; +} + +static int pt1_reset_pci(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x01010000); + pt1_write_reg(pt1, 0, 0x01000000); + for (i = 0; i < 10; i++) { + if (pt1_read_reg(pt1, 0) & 0x00000001) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not reset PCI\n"); + return -EIO; +} + +static int pt1_reset_ram(struct pt1 *pt1) +{ + int i; + pt1_write_reg(pt1, 0, 0x02020000); + pt1_write_reg(pt1, 0, 0x02000000); + for (i = 0; i < 10; i++) { + if (pt1_read_reg(pt1, 0) & 0x00000002) + return 0; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not reset RAM\n"); + return -EIO; +} + +static int pt1_do_enable_ram(struct pt1 *pt1) +{ + int i, j; + u32 status; + status = pt1_read_reg(pt1, 0) & 0x00000004; + pt1_write_reg(pt1, 0, 0x00000002); + for (i = 0; i < 10; i++) { + for (j = 0; j < 1024; j++) { + if ((pt1_read_reg(pt1, 0) & 0x00000004) != status) + return 0; + } + schedule_timeout_uninterruptible((HZ + 999) / 1000); + } + pt1_printk(KERN_ERR, pt1, "could not enable RAM\n"); + return -EIO; +} + +static int pt1_enable_ram(struct pt1 *pt1) +{ + int i, ret; + int phase; + schedule_timeout_uninterruptible((HZ + 999) / 1000); + phase = pt1->pdev->device == 0x211a ? 128 : 166; + for (i = 0; i < phase; i++) { + ret = pt1_do_enable_ram(pt1); + if (ret < 0) + return ret; + } + return 0; +} + +static void pt1_disable_ram(struct pt1 *pt1) +{ + pt1_write_reg(pt1, 0, 0x0b0b0000); +} + +static void pt1_set_stream(struct pt1 *pt1, int index, int enabled) +{ + pt1_write_reg(pt1, 2, 1 << (index + 8) | enabled << index); +} + +static void pt1_init_streams(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_set_stream(pt1, i, 0); +} + +static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) +{ + u32 upacket; + int i; + int index; + struct pt1_adapter *adap; + int offset; + u8 *buf; + int sc; + + if (!page->upackets[PT1_NR_UPACKETS - 1]) + return 0; + + for (i = 0; i < PT1_NR_UPACKETS; i++) { + upacket = le32_to_cpu(page->upackets[i]); + index = (upacket >> 29) - 1; + if (index < 0 || index >= PT1_NR_ADAPS) + continue; + + adap = pt1->adaps[index]; + if (upacket >> 25 & 1) + adap->upacket_count = 0; + else if (!adap->upacket_count) + continue; + + if (upacket >> 24 & 1) + printk_ratelimited(KERN_INFO "earth-pt1: device " + "buffer overflowing. table[%d] buf[%d]\n", + pt1->table_index, pt1->buf_index); + sc = upacket >> 26 & 0x7; + if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7)) + printk_ratelimited(KERN_INFO "earth-pt1: data loss" + " in streamID(adapter)[%d]\n", index); + adap->st_count = sc; + + buf = adap->buf; + offset = adap->packet_count * 188 + adap->upacket_count * 3; + buf[offset] = upacket >> 16; + buf[offset + 1] = upacket >> 8; + if (adap->upacket_count != 62) + buf[offset + 2] = upacket; + + if (++adap->upacket_count >= 63) { + adap->upacket_count = 0; + if (++adap->packet_count >= 21) { + dvb_dmx_swfilter_packets(&adap->demux, buf, 21); + adap->packet_count = 0; + } + } + } + + page->upackets[PT1_NR_UPACKETS - 1] = 0; + return 1; +} + +static int pt1_thread(void *data) +{ + struct pt1 *pt1; + struct pt1_buffer_page *page; + + pt1 = data; + set_freezable(); + + while (!kthread_should_stop()) { + try_to_freeze(); + + page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page; + if (!pt1_filter(pt1, page)) { + schedule_timeout_interruptible((HZ + 999) / 1000); + continue; + } + + if (++pt1->buf_index >= PT1_NR_BUFS) { + pt1_increment_table_count(pt1); + pt1->buf_index = 0; + if (++pt1->table_index >= pt1_nr_tables) + pt1->table_index = 0; + } + } + + return 0; +} + +static void pt1_free_page(struct pt1 *pt1, void *page, dma_addr_t addr) +{ + dma_free_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, page, addr); +} + +static void *pt1_alloc_page(struct pt1 *pt1, dma_addr_t *addrp, u32 *pfnp) +{ + void *page; + dma_addr_t addr; + + page = dma_alloc_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, &addr, + GFP_KERNEL); + if (page == NULL) + return NULL; + + BUG_ON(addr & (PT1_PAGE_SIZE - 1)); + BUG_ON(addr >> PT1_PAGE_SHIFT >> 31 >> 1); + + *addrp = addr; + *pfnp = addr >> PT1_PAGE_SHIFT; + return page; +} + +static void pt1_cleanup_buffer(struct pt1 *pt1, struct pt1_buffer *buf) +{ + pt1_free_page(pt1, buf->page, buf->addr); +} + +static int +pt1_init_buffer(struct pt1 *pt1, struct pt1_buffer *buf, u32 *pfnp) +{ + struct pt1_buffer_page *page; + dma_addr_t addr; + + page = pt1_alloc_page(pt1, &addr, pfnp); + if (page == NULL) + return -ENOMEM; + + page->upackets[PT1_NR_UPACKETS - 1] = 0; + + buf->page = page; + buf->addr = addr; + return 0; +} + +static void pt1_cleanup_table(struct pt1 *pt1, struct pt1_table *table) +{ + int i; + + for (i = 0; i < PT1_NR_BUFS; i++) + pt1_cleanup_buffer(pt1, &table->bufs[i]); + + pt1_free_page(pt1, table->page, table->addr); +} + +static int +pt1_init_table(struct pt1 *pt1, struct pt1_table *table, u32 *pfnp) +{ + struct pt1_table_page *page; + dma_addr_t addr; + int i, ret; + u32 buf_pfn; + + page = pt1_alloc_page(pt1, &addr, pfnp); + if (page == NULL) + return -ENOMEM; + + for (i = 0; i < PT1_NR_BUFS; i++) { + ret = pt1_init_buffer(pt1, &table->bufs[i], &buf_pfn); + if (ret < 0) + goto err; + + page->buf_pfns[i] = cpu_to_le32(buf_pfn); + } + + pt1_increment_table_count(pt1); + table->page = page; + table->addr = addr; + return 0; + +err: + while (i--) + pt1_cleanup_buffer(pt1, &table->bufs[i]); + + pt1_free_page(pt1, page, addr); + return ret; +} + +static void pt1_cleanup_tables(struct pt1 *pt1) +{ + struct pt1_table *tables; + int i; + + tables = pt1->tables; + pt1_unregister_tables(pt1); + + for (i = 0; i < pt1_nr_tables; i++) + pt1_cleanup_table(pt1, &tables[i]); + + vfree(tables); +} + +static int pt1_init_tables(struct pt1 *pt1) +{ + struct pt1_table *tables; + int i, ret; + u32 first_pfn, pfn; + + tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables); + if (tables == NULL) + return -ENOMEM; + + pt1_init_table_count(pt1); + + i = 0; + if (pt1_nr_tables) { + ret = pt1_init_table(pt1, &tables[0], &first_pfn); + if (ret) + goto err; + i++; + } + + while (i < pt1_nr_tables) { + ret = pt1_init_table(pt1, &tables[i], &pfn); + if (ret) + goto err; + tables[i - 1].page->next_pfn = cpu_to_le32(pfn); + i++; + } + + tables[pt1_nr_tables - 1].page->next_pfn = cpu_to_le32(first_pfn); + + pt1_register_tables(pt1, first_pfn); + pt1->tables = tables; + return 0; + +err: + while (i--) + pt1_cleanup_table(pt1, &tables[i]); + + vfree(tables); + return ret; +} + +static int pt1_start_polling(struct pt1 *pt1) +{ + int ret = 0; + + mutex_lock(&pt1->lock); + if (!pt1->kthread) { + pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1"); + if (IS_ERR(pt1->kthread)) { + ret = PTR_ERR(pt1->kthread); + pt1->kthread = NULL; + } + } + mutex_unlock(&pt1->lock); + return ret; +} + +static int pt1_start_feed(struct dvb_demux_feed *feed) +{ + struct pt1_adapter *adap; + adap = container_of(feed->demux, struct pt1_adapter, demux); + if (!adap->users++) { + int ret; + + ret = pt1_start_polling(adap->pt1); + if (ret) + return ret; + pt1_set_stream(adap->pt1, adap->index, 1); + } + return 0; +} + +static void pt1_stop_polling(struct pt1 *pt1) +{ + int i, count; + + mutex_lock(&pt1->lock); + for (i = 0, count = 0; i < PT1_NR_ADAPS; i++) + count += pt1->adaps[i]->users; + + if (count == 0 && pt1->kthread) { + kthread_stop(pt1->kthread); + pt1->kthread = NULL; + } + mutex_unlock(&pt1->lock); +} + +static int pt1_stop_feed(struct dvb_demux_feed *feed) +{ + struct pt1_adapter *adap; + adap = container_of(feed->demux, struct pt1_adapter, demux); + if (!--adap->users) { + pt1_set_stream(adap->pt1, adap->index, 0); + pt1_stop_polling(adap->pt1); + } + return 0; +} + +static void +pt1_update_power(struct pt1 *pt1) +{ + int bits; + int i; + struct pt1_adapter *adap; + static const int sleep_bits[] = { + 1 << 4, + 1 << 6 | 1 << 7, + 1 << 5, + 1 << 6 | 1 << 8, + }; + + bits = pt1->power | !pt1->reset << 3; + mutex_lock(&pt1->lock); + for (i = 0; i < PT1_NR_ADAPS; i++) { + adap = pt1->adaps[i]; + switch (adap->voltage) { + case SEC_VOLTAGE_13: /* actually 11V */ + bits |= 1 << 1; + break; + case SEC_VOLTAGE_18: /* actually 15V */ + bits |= 1 << 1 | 1 << 2; + break; + default: + break; + } + + /* XXX: The bits should be changed depending on adap->sleep. */ + bits |= sleep_bits[i]; + } + pt1_write_reg(pt1, 1, bits); + mutex_unlock(&pt1->lock); +} + +static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct pt1_adapter *adap; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + adap->voltage = voltage; + pt1_update_power(adap->pt1); + + if (adap->orig_set_voltage) + return adap->orig_set_voltage(fe, voltage); + else + return 0; +} + +static int pt1_sleep(struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + adap->sleep = 1; + pt1_update_power(adap->pt1); + + if (adap->orig_sleep) + return adap->orig_sleep(fe); + else + return 0; +} + +static int pt1_wakeup(struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + adap->sleep = 0; + pt1_update_power(adap->pt1); + schedule_timeout_uninterruptible((HZ + 999) / 1000); + + if (adap->orig_init) + return adap->orig_init(fe); + else + return 0; +} + +static void pt1_free_adapter(struct pt1_adapter *adap) +{ + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->adap); + free_page((unsigned long)adap->buf); + kfree(adap); +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct pt1_adapter * +pt1_alloc_adapter(struct pt1 *pt1) +{ + struct pt1_adapter *adap; + void *buf; + struct dvb_adapter *dvb_adap; + struct dvb_demux *demux; + struct dmxdev *dmxdev; + int ret; + + adap = kzalloc(sizeof(struct pt1_adapter), GFP_KERNEL); + if (!adap) { + ret = -ENOMEM; + goto err; + } + + adap->pt1 = pt1; + + adap->voltage = SEC_VOLTAGE_OFF; + adap->sleep = 1; + + buf = (u8 *)__get_free_page(GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err_kfree; + } + + adap->buf = buf; + adap->upacket_count = 0; + adap->packet_count = 0; + adap->st_count = -1; + + dvb_adap = &adap->adap; + dvb_adap->priv = adap; + ret = dvb_register_adapter(dvb_adap, DRIVER_NAME, THIS_MODULE, + &pt1->pdev->dev, adapter_nr); + if (ret < 0) + goto err_free_page; + + demux = &adap->demux; + demux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + demux->priv = adap; + demux->feednum = 256; + demux->filternum = 256; + demux->start_feed = pt1_start_feed; + demux->stop_feed = pt1_stop_feed; + demux->write_to_decoder = NULL; + ret = dvb_dmx_init(demux); + if (ret < 0) + goto err_unregister_adapter; + + dmxdev = &adap->dmxdev; + dmxdev->filternum = 256; + dmxdev->demux = &demux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adap); + if (ret < 0) + goto err_dmx_release; + + return adap; + +err_dmx_release: + dvb_dmx_release(demux); +err_unregister_adapter: + dvb_unregister_adapter(dvb_adap); +err_free_page: + free_page((unsigned long)buf); +err_kfree: + kfree(adap); +err: + return ERR_PTR(ret); +} + +static void pt1_cleanup_adapters(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_free_adapter(pt1->adaps[i]); +} + +static int pt1_init_adapters(struct pt1 *pt1) +{ + int i; + struct pt1_adapter *adap; + int ret; + + for (i = 0; i < PT1_NR_ADAPS; i++) { + adap = pt1_alloc_adapter(pt1); + if (IS_ERR(adap)) { + ret = PTR_ERR(adap); + goto err; + } + + adap->index = i; + pt1->adaps[i] = adap; + } + return 0; + +err: + while (i--) + pt1_free_adapter(pt1->adaps[i]); + + return ret; +} + +static void pt1_cleanup_frontend(struct pt1_adapter *adap) +{ + dvb_unregister_frontend(adap->fe); +} + +static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe) +{ + int ret; + + adap->orig_set_voltage = fe->ops.set_voltage; + adap->orig_sleep = fe->ops.sleep; + adap->orig_init = fe->ops.init; + fe->ops.set_voltage = pt1_set_voltage; + fe->ops.sleep = pt1_sleep; + fe->ops.init = pt1_wakeup; + + ret = dvb_register_frontend(&adap->adap, fe); + if (ret < 0) + return ret; + + adap->fe = fe; + return 0; +} + +static void pt1_cleanup_frontends(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_cleanup_frontend(pt1->adaps[i]); +} + +struct pt1_config { + struct va1j5jf8007s_config va1j5jf8007s_config; + struct va1j5jf8007t_config va1j5jf8007t_config; +}; + +static const struct pt1_config pt1_configs[2] = { + { + { + .demod_address = 0x1b, + .frequency = VA1J5JF8007S_20MHZ, + }, + { + .demod_address = 0x1a, + .frequency = VA1J5JF8007T_20MHZ, + }, + }, { + { + .demod_address = 0x19, + .frequency = VA1J5JF8007S_20MHZ, + }, + { + .demod_address = 0x18, + .frequency = VA1J5JF8007T_20MHZ, + }, + }, +}; + +static const struct pt1_config pt2_configs[2] = { + { + { + .demod_address = 0x1b, + .frequency = VA1J5JF8007S_25MHZ, + }, + { + .demod_address = 0x1a, + .frequency = VA1J5JF8007T_25MHZ, + }, + }, { + { + .demod_address = 0x19, + .frequency = VA1J5JF8007S_25MHZ, + }, + { + .demod_address = 0x18, + .frequency = VA1J5JF8007T_25MHZ, + }, + }, +}; + +static int pt1_init_frontends(struct pt1 *pt1) +{ + int i, j; + struct i2c_adapter *i2c_adap; + const struct pt1_config *configs, *config; + struct dvb_frontend *fe[4]; + int ret; + + i = 0; + j = 0; + + i2c_adap = &pt1->i2c_adap; + configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs; + do { + config = &configs[i / 2]; + + fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, + i2c_adap); + if (!fe[i]) { + ret = -ENODEV; /* This does not sound nice... */ + goto err; + } + i++; + + fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config, + i2c_adap); + if (!fe[i]) { + ret = -ENODEV; + goto err; + } + i++; + + ret = va1j5jf8007s_prepare(fe[i - 2]); + if (ret < 0) + goto err; + + ret = va1j5jf8007t_prepare(fe[i - 1]); + if (ret < 0) + goto err; + + } while (i < 4); + + do { + ret = pt1_init_frontend(pt1->adaps[j], fe[j]); + if (ret < 0) + goto err; + } while (++j < 4); + + return 0; + +err: + while (i-- > j) + fe[i]->ops.release(fe[i]); + + while (j--) + dvb_unregister_frontend(fe[j]); + + return ret; +} + +static void pt1_i2c_emit(struct pt1 *pt1, int addr, int busy, int read_enable, + int clock, int data, int next_addr) +{ + pt1_write_reg(pt1, 4, addr << 18 | busy << 13 | read_enable << 12 | + !clock << 11 | !data << 10 | next_addr); +} + +static void pt1_i2c_write_bit(struct pt1 *pt1, int addr, int *addrp, int data) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, data, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, data, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, data, addr + 3); + *addrp = addr + 3; +} + +static void pt1_i2c_read_bit(struct pt1 *pt1, int addr, int *addrp) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 1, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 1, 1, 1, addr + 3); + pt1_i2c_emit(pt1, addr + 3, 1, 0, 0, 1, addr + 4); + *addrp = addr + 4; +} + +static void pt1_i2c_write_byte(struct pt1 *pt1, int addr, int *addrp, int data) +{ + int i; + for (i = 0; i < 8; i++) + pt1_i2c_write_bit(pt1, addr, &addr, data >> (7 - i) & 1); + pt1_i2c_write_bit(pt1, addr, &addr, 1); + *addrp = addr; +} + +static void pt1_i2c_read_byte(struct pt1 *pt1, int addr, int *addrp, int last) +{ + int i; + for (i = 0; i < 8; i++) + pt1_i2c_read_bit(pt1, addr, &addr); + pt1_i2c_write_bit(pt1, addr, &addr, last); + *addrp = addr; +} + +static void pt1_i2c_prepare(struct pt1 *pt1, int addr, int *addrp) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, 0, addr + 3); + *addrp = addr + 3; +} + +static void +pt1_i2c_write_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +{ + int i; + pt1_i2c_prepare(pt1, addr, &addr); + pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1); + for (i = 0; i < msg->len; i++) + pt1_i2c_write_byte(pt1, addr, &addr, msg->buf[i]); + *addrp = addr; +} + +static void +pt1_i2c_read_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +{ + int i; + pt1_i2c_prepare(pt1, addr, &addr); + pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1 | 1); + for (i = 0; i < msg->len; i++) + pt1_i2c_read_byte(pt1, addr, &addr, i == msg->len - 1); + *addrp = addr; +} + +static int pt1_i2c_end(struct pt1 *pt1, int addr) +{ + pt1_i2c_emit(pt1, addr, 1, 0, 0, 0, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + pt1_i2c_emit(pt1, addr + 2, 1, 0, 1, 1, 0); + + pt1_write_reg(pt1, 0, 0x00000004); + do { + if (signal_pending(current)) + return -EINTR; + schedule_timeout_interruptible((HZ + 999) / 1000); + } while (pt1_read_reg(pt1, 0) & 0x00000080); + return 0; +} + +static void pt1_i2c_begin(struct pt1 *pt1, int *addrp) +{ + int addr; + addr = 0; + + pt1_i2c_emit(pt1, addr, 0, 0, 1, 1, addr /* itself */); + addr = addr + 1; + + if (!pt1->i2c_running) { + pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); + pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); + addr = addr + 2; + pt1->i2c_running = 1; + } + *addrp = addr; +} + +static int pt1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct pt1 *pt1; + int i; + struct i2c_msg *msg, *next_msg; + int addr, ret; + u16 len; + u32 word; + + pt1 = i2c_get_adapdata(adap); + + for (i = 0; i < num; i++) { + msg = &msgs[i]; + if (msg->flags & I2C_M_RD) + return -ENOTSUPP; + + if (i + 1 < num) + next_msg = &msgs[i + 1]; + else + next_msg = NULL; + + if (next_msg && next_msg->flags & I2C_M_RD) { + i++; + + len = next_msg->len; + if (len > 4) + return -ENOTSUPP; + + pt1_i2c_begin(pt1, &addr); + pt1_i2c_write_msg(pt1, addr, &addr, msg); + pt1_i2c_read_msg(pt1, addr, &addr, next_msg); + ret = pt1_i2c_end(pt1, addr); + if (ret < 0) + return ret; + + word = pt1_read_reg(pt1, 2); + while (len--) { + next_msg->buf[len] = word; + word >>= 8; + } + } else { + pt1_i2c_begin(pt1, &addr); + pt1_i2c_write_msg(pt1, addr, &addr, msg); + ret = pt1_i2c_end(pt1, addr); + if (ret < 0) + return ret; + } + } + + return num; +} + +static u32 pt1_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm pt1_i2c_algo = { + .master_xfer = pt1_i2c_xfer, + .functionality = pt1_i2c_func, +}; + +static void pt1_i2c_wait(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 128; i++) + pt1_i2c_emit(pt1, 0, 0, 0, 1, 1, 0); +} + +static void pt1_i2c_init(struct pt1 *pt1) +{ + int i; + for (i = 0; i < 1024; i++) + pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0); +} + +static void __devexit pt1_remove(struct pci_dev *pdev) +{ + struct pt1 *pt1; + void __iomem *regs; + + pt1 = pci_get_drvdata(pdev); + regs = pt1->regs; + + if (pt1->kthread) + kthread_stop(pt1->kthread); + pt1_cleanup_tables(pt1); + pt1_cleanup_frontends(pt1); + pt1_disable_ram(pt1); + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + pt1_cleanup_adapters(pt1); + i2c_del_adapter(&pt1->i2c_adap); + pci_set_drvdata(pdev, NULL); + kfree(pt1); + pci_iounmap(pdev, regs); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static int __devinit +pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret; + void __iomem *regs; + struct pt1 *pt1; + struct i2c_adapter *i2c_adap; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto err; + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret < 0) + goto err_pci_disable_device; + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret < 0) + goto err_pci_disable_device; + + regs = pci_iomap(pdev, 0, 0); + if (!regs) { + ret = -EIO; + goto err_pci_release_regions; + } + + pt1 = kzalloc(sizeof(struct pt1), GFP_KERNEL); + if (!pt1) { + ret = -ENOMEM; + goto err_pci_iounmap; + } + + mutex_init(&pt1->lock); + pt1->pdev = pdev; + pt1->regs = regs; + pci_set_drvdata(pdev, pt1); + + ret = pt1_init_adapters(pt1); + if (ret < 0) + goto err_kfree; + + mutex_init(&pt1->lock); + + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + + i2c_adap = &pt1->i2c_adap; + i2c_adap->algo = &pt1_i2c_algo; + i2c_adap->algo_data = NULL; + i2c_adap->dev.parent = &pdev->dev; + strcpy(i2c_adap->name, DRIVER_NAME); + i2c_set_adapdata(i2c_adap, pt1); + ret = i2c_add_adapter(i2c_adap); + if (ret < 0) + goto err_pt1_cleanup_adapters; + + pt1_i2c_init(pt1); + pt1_i2c_wait(pt1); + + ret = pt1_sync(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + pt1_identify(pt1); + + ret = pt1_unlock(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_reset_pci(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_reset_ram(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + ret = pt1_enable_ram(pt1); + if (ret < 0) + goto err_i2c_del_adapter; + + pt1_init_streams(pt1); + + pt1->power = 1; + pt1_update_power(pt1); + schedule_timeout_uninterruptible((HZ + 49) / 50); + + pt1->reset = 0; + pt1_update_power(pt1); + schedule_timeout_uninterruptible((HZ + 999) / 1000); + + ret = pt1_init_frontends(pt1); + if (ret < 0) + goto err_pt1_disable_ram; + + ret = pt1_init_tables(pt1); + if (ret < 0) + goto err_pt1_cleanup_frontends; + + return 0; + +err_pt1_cleanup_frontends: + pt1_cleanup_frontends(pt1); +err_pt1_disable_ram: + pt1_disable_ram(pt1); + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); +err_i2c_del_adapter: + i2c_del_adapter(i2c_adap); +err_pt1_cleanup_adapters: + pt1_cleanup_adapters(pt1); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(pt1); +err_pci_iounmap: + pci_iounmap(pdev, regs); +err_pci_release_regions: + pci_release_regions(pdev); +err_pci_disable_device: + pci_disable_device(pdev); +err: + return ret; + +} + +static struct pci_device_id pt1_id_table[] = { + { PCI_DEVICE(0x10ee, 0x211a) }, + { PCI_DEVICE(0x10ee, 0x222a) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, pt1_id_table); + +static struct pci_driver pt1_driver = { + .name = DRIVER_NAME, + .probe = pt1_probe, + .remove = __devexit_p(pt1_remove), + .id_table = pt1_id_table, +}; + + +static int __init pt1_init(void) +{ + return pci_register_driver(&pt1_driver); +} + + +static void __exit pt1_cleanup(void) +{ + pci_unregister_driver(&pt1_driver); +} + +module_init(pt1_init); +module_exit(pt1_cleanup); + +MODULE_AUTHOR("Takahito HIRANO "); +MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/pt1/va1j5jf8007s.c b/drivers/media/pci/pt1/va1j5jf8007s.c new file mode 100644 index 000000000000..d980dfb21e5e --- /dev/null +++ b/drivers/media/pci/pt1/va1j5jf8007s.c @@ -0,0 +1,735 @@ +/* + * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * This program is free software; 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 "dvb_frontend.h" +#include "va1j5jf8007s.h" + +enum va1j5jf8007s_tune_state { + VA1J5JF8007S_IDLE, + VA1J5JF8007S_SET_FREQUENCY_1, + VA1J5JF8007S_SET_FREQUENCY_2, + VA1J5JF8007S_SET_FREQUENCY_3, + VA1J5JF8007S_CHECK_FREQUENCY, + VA1J5JF8007S_SET_MODULATION, + VA1J5JF8007S_CHECK_MODULATION, + VA1J5JF8007S_SET_TS_ID, + VA1J5JF8007S_CHECK_TS_ID, + VA1J5JF8007S_TRACK, +}; + +struct va1j5jf8007s_state { + const struct va1j5jf8007s_config *config; + struct i2c_adapter *adap; + struct dvb_frontend fe; + enum va1j5jf8007s_tune_state tune_state; +}; + +static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct va1j5jf8007s_state *state; + u8 addr; + int i; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + s32 word, x1, x2, x3, x4, x5, y; + + state = fe->demodulator_priv; + addr = state->config->demod_address; + + word = 0; + for (i = 0; i < 2; i++) { + write_buf[0] = 0xbc + i; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + word <<= 8; + word |= read_buf[0]; + } + + word -= 3000; + if (word < 0) + word = 0; + + x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000); + x2 = (s64)x1 * x1 >> 31; + x3 = (s64)x2 * x1 >> 31; + x4 = (s64)x2 * x2 >> 31; + x5 = (s64)x4 * x1 >> 31; + + y = (58857ll << 23) / 1000; + y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30; + y += (s64)x2 * ((88977ll << 24) / 1000) >> 28; + y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27; + y += (s64)x4 * ((14341ll << 27) / 1000) >> 27; + y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28; + + *snr = y < 0 ? 0 : y >> 15; + return 0; +} + +static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int +va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct va1j5jf8007s_state *state; + + state = fe->demodulator_priv; + + switch (state->tune_state) { + case VA1J5JF8007S_IDLE: + case VA1J5JF8007S_SET_FREQUENCY_1: + case VA1J5JF8007S_SET_FREQUENCY_2: + case VA1J5JF8007S_SET_FREQUENCY_3: + case VA1J5JF8007S_CHECK_FREQUENCY: + *status = 0; + return 0; + + + case VA1J5JF8007S_SET_MODULATION: + case VA1J5JF8007S_CHECK_MODULATION: + *status |= FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_SET_TS_ID: + case VA1J5JF8007S_CHECK_TS_ID: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + + case VA1J5JF8007S_TRACK: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +struct va1j5jf8007s_cb_map { + u32 frequency; + u8 cb; +}; + +static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = { + { 986000, 0xb2 }, + { 1072000, 0xd2 }, + { 1154000, 0xe2 }, + { 1291000, 0x20 }, + { 1447000, 0x40 }, + { 1615000, 0x60 }, + { 1791000, 0x80 }, + { 1972000, 0xa0 }, +}; + +static u8 va1j5jf8007s_lookup_cb(u32 frequency) +{ + int i; + const struct va1j5jf8007s_cb_map *map; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) { + map = &va1j5jf8007s_cb_maps[i]; + if (frequency < map->frequency) + return map->cb; + } + return 0xc0; +} + +static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state) +{ + u32 frequency; + u16 word; + u8 buf[6]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + word = (frequency + 500) / 1000; + if (frequency < 1072000) + word = (word << 1 & ~0x1f) | (word & 0x0f); + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0x40 | word >> 8; + buf[3] = word; + buf[4] = 0xe0; + buf[5] = va1j5jf8007s_lookup_cb(frequency); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state) +{ + u8 buf[3]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xe4; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state) +{ + u32 frequency; + u8 buf[4]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xf4; + buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[2], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xfe; + write_buf[1] = 0xc1; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = read_buf[0] & 0x40; + return 0; +} + +static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x03; + buf[1] = 0x01; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xc3; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = !(read_buf[0] & 0x10); + return 0; +} + +static int +va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state) +{ + u32 ts_id; + u8 buf[3]; + struct i2c_msg msg; + + ts_id = state->fe.dtv_property_cache.isdbs_ts_id; + if (!ts_id) + return 0; + + buf[0] = 0x8f; + buf[1] = ts_id >> 8; + buf[2] = ts_id; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock) +{ + u8 addr; + u8 write_buf[1], read_buf[2]; + struct i2c_msg msgs[2]; + u32 ts_id; + + ts_id = state->fe.dtv_property_cache.isdbs_ts_id; + if (!ts_id) { + *lock = 1; + return 0; + } + + addr = state->config->demod_address; + + write_buf[0] = 0xe6; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id; + return 0; +} + +static int +va1j5jf8007s_tune(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, unsigned int *delay, + fe_status_t *status) +{ + struct va1j5jf8007s_state *state; + int ret; + int lock = 0; + + state = fe->demodulator_priv; + + if (re_tune) + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1; + + switch (state->tune_state) { + case VA1J5JF8007S_IDLE: + *delay = 3 * HZ; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_1: + ret = va1j5jf8007s_set_frequency_1(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_2: + ret = va1j5jf8007s_set_frequency_2(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3; + *delay = (HZ + 99) / 100; + *status = 0; + return 0; + + case VA1J5JF8007S_SET_FREQUENCY_3: + ret = va1j5jf8007s_set_frequency_3(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007S_CHECK_FREQUENCY: + ret = va1j5jf8007s_check_frequency(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 999) / 1000; + *status = 0; + return 0; + } + + state->tune_state = VA1J5JF8007S_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_SET_MODULATION: + ret = va1j5jf8007s_set_modulation(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007S_CHECK_MODULATION: + ret = va1j5jf8007s_check_modulation(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 49) / 50; + *status = FE_HAS_SIGNAL; + return 0; + } + + state->tune_state = VA1J5JF8007S_SET_TS_ID; + *delay = 0; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + + case VA1J5JF8007S_SET_TS_ID: + ret = va1j5jf8007s_set_ts_id(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007S_CHECK_TS_ID; + return 0; + + case VA1J5JF8007S_CHECK_TS_ID: + ret = va1j5jf8007s_check_ts_id(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 99) / 100; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + return 0; + } + + state->tune_state = VA1J5JF8007S_TRACK; + /* fall through */ + + case VA1J5JF8007S_TRACK: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state) +{ + u8 buf[4]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc0; + buf[2] = 0xf0; + buf[3] = 0x04; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x17; + buf[1] = sleep ? 0x01 : 0x00; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007s_sleep(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007s_init_frequency(state); + if (ret < 0) + return ret; + + return va1j5jf8007s_set_sleep(state, 1); +} + +static int va1j5jf8007s_init(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + + state = fe->demodulator_priv; + state->tune_state = VA1J5JF8007S_IDLE; + + return va1j5jf8007s_set_sleep(state, 0); +} + +static void va1j5jf8007s_release(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops va1j5jf8007s_ops = { + .delsys = { SYS_ISDBS }, + .info = { + .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .read_snr = va1j5jf8007s_read_snr, + .get_frontend_algo = va1j5jf8007s_get_frontend_algo, + .read_status = va1j5jf8007s_read_status, + .tune = va1j5jf8007s_tune, + .sleep = va1j5jf8007s_sleep, + .init = va1j5jf8007s_init, + .release = va1j5jf8007s_release, +}; + +static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0x07; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + if (read_buf[0] != 0x41) + return -EIO; + + return 0; +} + +static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = { + {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, + {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, + {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, + {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, +}; + +static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = { + {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, + {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, + {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, + {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0}, +}; + +static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) +{ + const u8 (*bufs)[2]; + int size; + u8 addr; + u8 buf[2]; + struct i2c_msg msg; + int i; + + switch (state->config->frequency) { + case VA1J5JF8007S_20MHZ: + bufs = va1j5jf8007s_20mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs); + break; + case VA1J5JF8007S_25MHZ: + bufs = va1j5jf8007s_25mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs); + break; + default: + return -EINVAL; + } + + addr = state->config->demod_address; + + msg.addr = addr; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + for (i = 0; i < size; i++) { + memcpy(buf, bufs[i], sizeof(buf)); + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + } + + return 0; +} + +/* must be called after va1j5jf8007t_attach */ +int va1j5jf8007s_prepare(struct dvb_frontend *fe) +{ + struct va1j5jf8007s_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007s_prepare_1(state); + if (ret < 0) + return ret; + + ret = va1j5jf8007s_prepare_2(state); + if (ret < 0) + return ret; + + return va1j5jf8007s_init_frequency(state); +} + +struct dvb_frontend * +va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, + struct i2c_adapter *adap) +{ + struct va1j5jf8007s_state *state; + struct dvb_frontend *fe; + u8 buf[2]; + struct i2c_msg msg; + + state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->adap = adap; + + fe = &state->fe; + memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops)); + fe->demodulator_priv = state; + + buf[0] = 0x01; + buf[1] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) { + kfree(state); + return NULL; + } + + return fe; +} diff --git a/drivers/media/pci/pt1/va1j5jf8007s.h b/drivers/media/pci/pt1/va1j5jf8007s.h new file mode 100644 index 000000000000..b7d6f05a0e02 --- /dev/null +++ b/drivers/media/pci/pt1/va1j5jf8007s.h @@ -0,0 +1,46 @@ +/* + * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * This program is free software; 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 VA1J5JF8007S_H +#define VA1J5JF8007S_H + +enum va1j5jf8007s_frequency { + VA1J5JF8007S_20MHZ, + VA1J5JF8007S_25MHZ, +}; + +struct va1j5jf8007s_config { + u8 demod_address; + enum va1j5jf8007s_frequency frequency; +}; + +struct i2c_adapter; + +struct dvb_frontend * +va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, + struct i2c_adapter *adap); + +/* must be called after va1j5jf8007t_attach */ +int va1j5jf8007s_prepare(struct dvb_frontend *fe); + +#endif diff --git a/drivers/media/pci/pt1/va1j5jf8007t.c b/drivers/media/pci/pt1/va1j5jf8007t.c new file mode 100644 index 000000000000..2db15159d514 --- /dev/null +++ b/drivers/media/pci/pt1/va1j5jf8007t.c @@ -0,0 +1,536 @@ +/* + * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * This program is free software; 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 "dvb_frontend.h" +#include "dvb_math.h" +#include "va1j5jf8007t.h" + +enum va1j5jf8007t_tune_state { + VA1J5JF8007T_IDLE, + VA1J5JF8007T_SET_FREQUENCY, + VA1J5JF8007T_CHECK_FREQUENCY, + VA1J5JF8007T_SET_MODULATION, + VA1J5JF8007T_CHECK_MODULATION, + VA1J5JF8007T_TRACK, + VA1J5JF8007T_ABORT, +}; + +struct va1j5jf8007t_state { + const struct va1j5jf8007t_config *config; + struct i2c_adapter *adap; + struct dvb_frontend fe; + enum va1j5jf8007t_tune_state tune_state; +}; + +static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct va1j5jf8007t_state *state; + u8 addr; + int i; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + s32 word, x, y; + + state = fe->demodulator_priv; + addr = state->config->demod_address; + + word = 0; + for (i = 0; i < 3; i++) { + write_buf[0] = 0x8b + i; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + word <<= 8; + word |= read_buf[0]; + } + + if (!word) + return -EIO; + + x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24)); + y = (24ll << 46) / 1000000; + y = ((s64)y * x >> 30) - (16ll << 40) / 10000; + y = ((s64)y * x >> 29) + (398ll << 35) / 10000; + y = ((s64)y * x >> 30) + (5491ll << 29) / 10000; + y = ((s64)y * x >> 30) + (30965ll << 23) / 10000; + *snr = y >> 15; + return 0; +} + +static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int +va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct va1j5jf8007t_state *state; + + state = fe->demodulator_priv; + + switch (state->tune_state) { + case VA1J5JF8007T_IDLE: + case VA1J5JF8007T_SET_FREQUENCY: + case VA1J5JF8007T_CHECK_FREQUENCY: + *status = 0; + return 0; + + + case VA1J5JF8007T_SET_MODULATION: + case VA1J5JF8007T_CHECK_MODULATION: + case VA1J5JF8007T_ABORT: + *status |= FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_TRACK: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + } + + BUG(); +} + +struct va1j5jf8007t_cb_map { + u32 frequency; + u8 cb; +}; + +static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = { + { 90000000, 0x80 }, + { 140000000, 0x81 }, + { 170000000, 0xa1 }, + { 220000000, 0x62 }, + { 330000000, 0xa2 }, + { 402000000, 0xe2 }, + { 450000000, 0x64 }, + { 550000000, 0x84 }, + { 600000000, 0xa4 }, + { 700000000, 0xc4 }, +}; + +static u8 va1j5jf8007t_lookup_cb(u32 frequency) +{ + int i; + const struct va1j5jf8007t_cb_map *map; + + for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) { + map = &va1j5jf8007t_cb_maps[i]; + if (frequency < map->frequency) + return map->cb; + } + return 0xe4; +} + +static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state) +{ + u32 frequency; + u16 word; + u8 buf[6]; + struct i2c_msg msg; + + frequency = state->fe.dtv_property_cache.frequency; + + word = (frequency + 71428) / 142857 + 399; + buf[0] = 0xfe; + buf[1] = 0xc2; + buf[2] = word >> 8; + buf[3] = word; + buf[4] = 0x80; + buf[5] = va1j5jf8007t_lookup_cb(frequency); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int +va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock) +{ + u8 addr; + u8 write_buf[2], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0xfe; + write_buf[1] = 0xc3; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = read_buf[0] & 0x40; + return 0; +} + +static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x01; + buf[1] = 0x40; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state, + int *lock, int *retry) +{ + u8 addr; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + + addr = state->config->demod_address; + + write_buf[0] = 0x80; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + *lock = !(read_buf[0] & 0x10); + *retry = read_buf[0] & 0x80; + return 0; +} + +static int +va1j5jf8007t_tune(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, unsigned int *delay, + fe_status_t *status) +{ + struct va1j5jf8007t_state *state; + int ret; + int lock = 0, retry = 0; + + state = fe->demodulator_priv; + + if (re_tune) + state->tune_state = VA1J5JF8007T_SET_FREQUENCY; + + switch (state->tune_state) { + case VA1J5JF8007T_IDLE: + *delay = 3 * HZ; + *status = 0; + return 0; + + case VA1J5JF8007T_SET_FREQUENCY: + ret = va1j5jf8007t_set_frequency(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY; + *delay = 0; + *status = 0; + return 0; + + case VA1J5JF8007T_CHECK_FREQUENCY: + ret = va1j5jf8007t_check_frequency(state, &lock); + if (ret < 0) + return ret; + + if (!lock) { + *delay = (HZ + 999) / 1000; + *status = 0; + return 0; + } + + state->tune_state = VA1J5JF8007T_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_SET_MODULATION: + ret = va1j5jf8007t_set_modulation(state); + if (ret < 0) + return ret; + + state->tune_state = VA1J5JF8007T_CHECK_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case VA1J5JF8007T_CHECK_MODULATION: + ret = va1j5jf8007t_check_modulation(state, &lock, &retry); + if (ret < 0) + return ret; + + if (!lock) { + if (!retry) { + state->tune_state = VA1J5JF8007T_ABORT; + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL; + return 0; + } + *delay = (HZ + 999) / 1000; + *status = FE_HAS_SIGNAL; + return 0; + } + + state->tune_state = VA1J5JF8007T_TRACK; + /* fall through */ + + case VA1J5JF8007T_TRACK: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + + case VA1J5JF8007T_ABORT: + *delay = 3 * HZ; + *status = FE_HAS_SIGNAL; + return 0; + } + + BUG(); +} + +static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state) +{ + u8 buf[7]; + struct i2c_msg msg; + + buf[0] = 0xfe; + buf[1] = 0xc2; + buf[2] = 0x01; + buf[3] = 0x8f; + buf[4] = 0xc1; + buf[5] = 0x80; + buf[6] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep) +{ + u8 buf[2]; + struct i2c_msg msg; + + buf[0] = 0x03; + buf[1] = sleep ? 0x90 : 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + + return 0; +} + +static int va1j5jf8007t_sleep(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + int ret; + + state = fe->demodulator_priv; + + ret = va1j5jf8007t_init_frequency(state); + if (ret < 0) + return ret; + + return va1j5jf8007t_set_sleep(state, 1); +} + +static int va1j5jf8007t_init(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + + state = fe->demodulator_priv; + state->tune_state = VA1J5JF8007T_IDLE; + + return va1j5jf8007t_set_sleep(state, 0); +} + +static void va1j5jf8007t_release(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops va1j5jf8007t_ops = { + .delsys = { SYS_ISDBT }, + .info = { + .name = "VA1J5JF8007/VA1J5JF8011 ISDB-T", + .frequency_min = 90000000, + .frequency_max = 770000000, + .frequency_stepsize = 142857, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .read_snr = va1j5jf8007t_read_snr, + .get_frontend_algo = va1j5jf8007t_get_frontend_algo, + .read_status = va1j5jf8007t_read_status, + .tune = va1j5jf8007t_tune, + .sleep = va1j5jf8007t_sleep, + .init = va1j5jf8007t_init, + .release = va1j5jf8007t_release, +}; + +static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = { + {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, + {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, + {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, + {0xef, 0x01} +}; + +static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = { + {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, + {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c}, + {0x77, 0x03}, {0xef, 0x01} +}; + +int va1j5jf8007t_prepare(struct dvb_frontend *fe) +{ + struct va1j5jf8007t_state *state; + const u8 (*bufs)[2]; + int size; + u8 buf[2]; + struct i2c_msg msg; + int i; + + state = fe->demodulator_priv; + + switch (state->config->frequency) { + case VA1J5JF8007T_20MHZ: + bufs = va1j5jf8007t_20mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs); + break; + case VA1J5JF8007T_25MHZ: + bufs = va1j5jf8007t_25mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs); + break; + default: + return -EINVAL; + } + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + for (i = 0; i < size; i++) { + memcpy(buf, bufs[i], sizeof(buf)); + if (i2c_transfer(state->adap, &msg, 1) != 1) + return -EREMOTEIO; + } + + return va1j5jf8007t_init_frequency(state); +} + +struct dvb_frontend * +va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, + struct i2c_adapter *adap) +{ + struct va1j5jf8007t_state *state; + struct dvb_frontend *fe; + u8 buf[2]; + struct i2c_msg msg; + + state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->adap = adap; + + fe = &state->fe; + memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops)); + fe->demodulator_priv = state; + + buf[0] = 0x01; + buf[1] = 0x80; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (i2c_transfer(state->adap, &msg, 1) != 1) { + kfree(state); + return NULL; + } + + return fe; +} diff --git a/drivers/media/pci/pt1/va1j5jf8007t.h b/drivers/media/pci/pt1/va1j5jf8007t.h new file mode 100644 index 000000000000..2903be519ef5 --- /dev/null +++ b/drivers/media/pci/pt1/va1j5jf8007t.h @@ -0,0 +1,46 @@ +/* + * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 + * + * Copyright (C) 2009 HIRANO Takahito + * + * based on pt1dvr - http://pt1dvr.sourceforge.jp/ + * by Tomoaki Ishikawa + * + * This program is free software; 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 VA1J5JF8007T_H +#define VA1J5JF8007T_H + +enum va1j5jf8007t_frequency { + VA1J5JF8007T_20MHZ, + VA1J5JF8007T_25MHZ, +}; + +struct va1j5jf8007t_config { + u8 demod_address; + enum va1j5jf8007t_frequency frequency; +}; + +struct i2c_adapter; + +struct dvb_frontend * +va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, + struct i2c_adapter *adap); + +/* must be called after va1j5jf8007s_attach */ +int va1j5jf8007t_prepare(struct dvb_frontend *fe); + +#endif diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig new file mode 100644 index 000000000000..9d83ced69dd6 --- /dev/null +++ b/drivers/media/pci/ttpci/Kconfig @@ -0,0 +1,159 @@ +config TTPCI_EEPROM + tristate + depends on I2C + default n + +config DVB_AV7110 + tristate "AV7110 cards" + depends on DVB_CORE && PCI && I2C + select TTPCI_EEPROM + select VIDEO_SAA7146_VV + depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV + select DVB_VES1820 if !DVB_FE_CUSTOMISE + select DVB_VES1X93 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_SP8870 if !DVB_FE_CUSTOMISE + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_L64781 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + help + Support for SAA7146 and AV7110 based DVB cards as produced + by Fujitsu-Siemens, Technotrend, Hauppauge and others. + + This driver only supports the fullfeatured cards with + onboard MPEG2 decoder. + + This driver needs an external firmware. Please use the script + "/Documentation/dvb/get_dvb_firmware av7110" to + download/extract it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + + Alternatively, you can download the file and use the kernel's + EXTRA_FIRMWARE configuration option to build it into your + kernel image by adding the filename to the EXTRA_FIRMWARE + configuration option string. + + Say Y if you own such a card and want to use it. + +config DVB_AV7110_OSD + bool "AV7110 OSD support" + depends on DVB_AV7110 + default y if DVB_AV7110=y || DVB_AV7110=m + help + The AV7110 firmware provides some code to generate an OnScreenDisplay + on the video output. This is kind of nonstandard and not guaranteed to + be maintained. + + Anyway, some popular DVB software like VDR uses this OSD to render + its menus, so say Y if you want to use this software. + + All other people say N. + +config DVB_BUDGET_CORE + tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)" + depends on DVB_CORE && PCI && I2C + select VIDEO_SAA7146 + select TTPCI_EEPROM + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder. + +config DVB_BUDGET + tristate "Budget cards" + depends on DVB_BUDGET_CORE && I2C + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_VES1X93 if !DVB_FE_CUSTOMISE + select DVB_VES1820 if !DVB_FE_CUSTOMISE + select DVB_L64781 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_S5H1420 if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_ISL6423 if !DVB_FE_CUSTOMISE + select DVB_STV090x if !DVB_FE_CUSTOMISE + select DVB_STV6110x if !DVB_FE_CUSTOMISE + help + Support for simple SAA7146 based DVB cards (so called Budget- + or Nova-PCI cards) without onboard MPEG2 decoder, and without + analog inputs or an onboard Common Interface connector. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget. + +config DVB_BUDGET_CI + tristate "Budget cards with onboard CI connector" + depends on DVB_BUDGET_CORE && I2C + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_STV0288 if !DVB_FE_CUSTOMISE + select DVB_STB6000 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE + depends on RC_CORE + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder, but with onboard Common Interface connector. + + Note: The Common Interface is not yet supported by this driver + due to lack of information from the vendor. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-ci. + +config DVB_BUDGET_AV + tristate "Budget cards with analog video inputs" + depends on DVB_BUDGET_CORE && I2C + select VIDEO_SAA7146_VV + depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_TDA8261 if !DVB_FE_CUSTOMISE + select DVB_TUA6100 if !DVB_FE_CUSTOMISE + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder, but with one or more analog video inputs. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-av. + +config DVB_BUDGET_PATCH + tristate "AV7110 cards with Budget Patch" + depends on DVB_BUDGET_CORE && I2C + depends on DVB_AV7110 + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_VES1X93 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE + help + Support for Budget Patch (full TS) modification on + SAA7146+AV7110 based cards (DVB-S cards). This + driver doesn't use onboard MPEG2 decoder. The + card is driven in Budget-only mode. Card is + required to have loaded firmware to tune properly. + Firmware can be loaded by insertion and removal of + standard AV7110 driver prior to loading this + driver. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-patch. diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile new file mode 100644 index 000000000000..22a235f3cc48 --- /dev/null +++ b/drivers/media/pci/ttpci/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for the kernel SAA7146 FULL TS DVB device driver +# and the AV7110 DVB device driver +# + +dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o + +ifdef CONFIG_INPUT_EVDEV +dvb-ttpci-objs += av7110_ir.o +endif + +obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o +obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o +obj-$(CONFIG_DVB_BUDGET) += budget.o +obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o +obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o +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 diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c new file mode 100644 index 000000000000..4bd8bd56befc --- /dev/null +++ b/drivers/media/pci/ttpci/av7110.c @@ -0,0 +1,2939 @@ +/* + * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) + * av7110.c: initialization and demux stuff + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include "dvb_frontend.h" + +#include "ttpci-eeprom.h" +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_av.h" +#include "av7110_ca.h" +#include "av7110_ipack.h" + +#include "bsbe1.h" +#include "lnbp21.h" +#include "bsru6.h" + +#define TS_WIDTH 376 +#define TS_HEIGHT 512 +#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) +#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) + + +int av7110_debug; + +static int vidmode = CVBS_RGB_OUT; +static int pids_off; +static int adac = DVB_ADAC_TI; +static int hw_sections; +static int rgb_on; +static int volume = 255; +static int budgetpatch; +static int wss_cfg_4_3 = 0x4008; +static int wss_cfg_16_9 = 0x0007; +static int tv_standard; +static int full_ts; + +module_param_named(debug, av7110_debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); +module_param(vidmode, int, 0444); +MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); +module_param(pids_off, int, 0444); +MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed"); +module_param(adac, int, 0444); +MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); +module_param(hw_sections, int, 0444); +MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware"); +module_param(rgb_on, int, 0444); +MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control" + " signal on SCART pin 16 to switch SCART video mode from CVBS to RGB"); +module_param(volume, int, 0444); +MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); +module_param(budgetpatch, int, 0444); +MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"); +module_param(full_ts, int, 0444); +MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable"); +module_param(wss_cfg_4_3, int, 0444); +MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); +module_param(wss_cfg_16_9, int, 0444); +MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); +module_param(tv_standard, int, 0444); +MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static void restart_feeds(struct av7110 *av7110); +static int budget_start_feed(struct dvb_demux_feed *feed); +static int budget_stop_feed(struct dvb_demux_feed *feed); + +static int av7110_num; + +#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ +{\ + if (fe_func != NULL) { \ + av7110_copy = fe_func; \ + fe_func = av7110_func; \ + } \ +} + + +static void init_av7110_av(struct av7110 *av7110) +{ + int ret; + struct saa7146_dev *dev = av7110->dev; + + /* set internal volume control to maximum */ + av7110->adac_type = DVB_ADAC_TI; + ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); + if (ret < 0) + printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); + + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, + 1, (u16) av7110->display_ar); + if (ret < 0) + printk("dvb-ttpci: unable to set aspect ratio\n"); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, + 1, av7110->display_panscan); + if (ret < 0) + printk("dvb-ttpci: unable to set pan scan\n"); + + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); + if (ret < 0) + printk("dvb-ttpci: unable to configure 4:3 wss\n"); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); + if (ret < 0) + printk("dvb-ttpci: unable to configure 16:9 wss\n"); + + ret = av7710_set_video_mode(av7110, vidmode); + if (ret < 0) + printk("dvb-ttpci:cannot set video mode:%d\n",ret); + + /* handle different card types */ + /* remaining inits according to card and frontend type */ + av7110->analog_tuner_flags = 0; + av7110->current_input = 0; + if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) + av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on + if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { + printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_CRYSTAL; + i2c_writereg(av7110, 0x20, 0x01, 0xd2); + i2c_writereg(av7110, 0x20, 0x02, 0x49); + i2c_writereg(av7110, 0x20, 0x03, 0x00); + i2c_writereg(av7110, 0x20, 0x04, 0x00); + + /** + * some special handling for the Siemens DVB-C cards... + */ + } else if (0 == av7110_init_analog_module(av7110)) { + /* done. */ + } + else if (dev->pci->subsystem_vendor == 0x110a) { + printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_NONE; + } + else { + av7110->adac_type = adac; + printk("dvb-ttpci: adac type set to %d @ card %d\n", + av7110->adac_type, av7110->dvb_adapter.num); + } + + if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) { + // switch DVB SCART on + ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); + if (ret < 0) + printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret); + ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); + if (ret < 0) + printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret); + if (rgb_on && + ((av7110->dev->pci->subsystem_vendor == 0x110a) || + (av7110->dev->pci->subsystem_vendor == 0x13c2)) && + (av7110->dev->pci->subsystem_device == 0x0000)) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 + //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 + } + } + + if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e) + av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on + + ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); + if (ret < 0) + printk("dvb-ttpci:cannot set volume :%d\n",ret); +} + +static void recover_arm(struct av7110 *av7110) +{ + dprintk(4, "%p\n",av7110); + + av7110_bootarm(av7110); + msleep(100); + + init_av7110_av(av7110); + + /* card-specific recovery */ + if (av7110->recover) + av7110->recover(av7110); + + restart_feeds(av7110); + +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_check_ir_config(av7110, true); +#endif +} + +static void av7110_arm_sync(struct av7110 *av7110) +{ + if (av7110->arm_thread) + kthread_stop(av7110->arm_thread); + + av7110->arm_thread = NULL; +} + +static int arm_thread(void *data) +{ + struct av7110 *av7110 = data; + u16 newloops = 0; + int timeout; + + dprintk(4, "%p\n",av7110); + + for (;;) { + timeout = wait_event_interruptible_timeout(av7110->arm_wait, + kthread_should_stop(), 5 * HZ); + + if (-ERESTARTSYS == timeout || kthread_should_stop()) { + /* got signal or told to quit*/ + break; + } + + if (!av7110->arm_ready) + continue; + +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_check_ir_config(av7110, false); +#endif + + if (mutex_lock_interruptible(&av7110->dcomlock)) + break; + newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); + mutex_unlock(&av7110->dcomlock); + + if (newloops == av7110->arm_loops || av7110->arm_errors > 3) { + printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", + av7110->dvb_adapter.num); + + recover_arm(av7110); + + if (mutex_lock_interruptible(&av7110->dcomlock)) + break; + newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1; + mutex_unlock(&av7110->dcomlock); + } + av7110->arm_loops = newloops; + av7110->arm_errors = 0; + } + + return 0; +} + + +/**************************************************************************** + * IRQ handling + ****************************************************************************/ + +static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, + u8 *buffer2, size_t buffer2_len, + struct dvb_demux_filter *dvbdmxfilter, + enum dmx_success success, + struct av7110 *av7110) +{ + if (!dvbdmxfilter->feed->demux->dmx.frontend) + return 0; + if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE) + return 0; + + switch (dvbdmxfilter->type) { + case DMX_TYPE_SEC: + if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len) + return 0; + if (dvbdmxfilter->doneq) { + struct dmx_section_filter *filter = &dvbdmxfilter->filter; + int i; + u8 xor, neq = 0; + + for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { + xor = filter->filter_value[i] ^ buffer1[i]; + neq |= dvbdmxfilter->maskandnotmode[i] & xor; + } + if (!neq) + return 0; + } + return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, + buffer2, buffer2_len, + &dvbdmxfilter->filter, + DMX_OK); + case DMX_TYPE_TS: + if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) + return 0; + if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) + return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, + buffer2, buffer2_len, + &dvbdmxfilter->feed->feed.ts, + DMX_OK); + else + av7110_p2t_write(buffer1, buffer1_len, + dvbdmxfilter->feed->pid, + &av7110->p2t_filter[dvbdmxfilter->index]); + default: + return 0; + } +} + + +//#define DEBUG_TIMING +static inline void print_time(char *s) +{ +#ifdef DEBUG_TIMING + struct timeval tv; + do_gettimeofday(&tv); + printk("%s: %d.%d\n", s, (int)tv.tv_sec, (int)tv.tv_usec); +#endif +} + +#define DEBI_READ 0 +#define DEBI_WRITE 1 +static inline void start_debi_dma(struct av7110 *av7110, int dir, + unsigned long addr, unsigned int len) +{ + dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); + if (saa7146_wait_for_debi_done(av7110->dev, 0)) { + printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); + return; + } + + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */ + SAA7146_IER_ENABLE(av7110->dev, MASK_19); + if (len < 5) + len = 5; /* we want a real DEBI DMA */ + if (dir == DEBI_WRITE) + iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3); + else + irdebi(av7110, DEBISWAB, addr, 0, len); +} + +static void debiirq(unsigned long cookie) +{ + struct av7110 *av7110 = (struct av7110 *)cookie; + int type = av7110->debitype; + int handle = (type >> 8) & 0x1f; + unsigned int xfer = 0; + + print_time("debi"); + dprintk(4, "type 0x%04x\n", type); + + if (type == -1) { + printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", + jiffies, saa7146_read(av7110->dev, PSR), + saa7146_read(av7110->dev, SSR)); + goto debi_done; + } + av7110->debitype = -1; + + switch (type & 0xff) { + + case DATA_TS_RECORD: + dvb_dmx_swfilter_packets(&av7110->demux, + (const u8 *) av7110->debi_virt, + av7110->debilen / 188); + xfer = RX_BUFF; + break; + + case DATA_PES_RECORD: + if (av7110->demux.recording) + av7110_record_cb(&av7110->p2t[handle], + (u8 *) av7110->debi_virt, + av7110->debilen); + xfer = RX_BUFF; + break; + + case DATA_IPMPE: + case DATA_FSECTION: + case DATA_PIPING: + if (av7110->handle2filter[handle]) + DvbDmxFilterCallback((u8 *)av7110->debi_virt, + av7110->debilen, NULL, 0, + av7110->handle2filter[handle], + DMX_OK, av7110); + xfer = RX_BUFF; + break; + + case DATA_CI_GET: + { + u8 *data = av7110->debi_virt; + + if ((data[0] < 2) && data[2] == 0xff) { + int flags = 0; + if (data[5] > 0) + flags |= CA_CI_MODULE_PRESENT; + if (data[5] > 5) + flags |= CA_CI_MODULE_READY; + av7110->ci_slot[data[0]].flags = flags; + } else + ci_get_data(&av7110->ci_rbuffer, + av7110->debi_virt, + av7110->debilen); + xfer = RX_BUFF; + break; + } + + case DATA_COMMON_INTERFACE: + CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen); +#if 0 + { + int i; + + printk("av7110%d: ", av7110->num); + printk("%02x ", *(u8 *)av7110->debi_virt); + printk("%02x ", *(1+(u8 *)av7110->debi_virt)); + for (i = 2; i < av7110->debilen; i++) + printk("%02x ", (*(i+(unsigned char *)av7110->debi_virt))); + for (i = 2; i < av7110->debilen; i++) + printk("%c", chtrans(*(i+(unsigned char *)av7110->debi_virt))); + + printk("\n"); + } +#endif + xfer = RX_BUFF; + break; + + case DATA_DEBUG_MESSAGE: + ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; + printk("%s\n", (s8 *) av7110->debi_virt); + xfer = RX_BUFF; + break; + + case DATA_CI_PUT: + dprintk(4, "debi DATA_CI_PUT\n"); + case DATA_MPEG_PLAY: + dprintk(4, "debi DATA_MPEG_PLAY\n"); + case DATA_BMP_LOAD: + dprintk(4, "debi DATA_BMP_LOAD\n"); + xfer = TX_BUFF; + break; + default: + break; + } +debi_done: + spin_lock(&av7110->debilock); + if (xfer) + iwdebi(av7110, DEBINOSWAP, xfer, 0, 2); + ARM_ClearMailBox(av7110); + spin_unlock(&av7110->debilock); +} + +/* irq from av7110 firmware writing the mailbox register in the DPRAM */ +static void gpioirq(unsigned long cookie) +{ + struct av7110 *av7110 = (struct av7110 *)cookie; + u32 rxbuf, txbuf; + int len; + + if (av7110->debitype != -1) + /* we shouldn't get any irq while a debi xfer is running */ + printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", + jiffies, saa7146_read(av7110->dev, PSR), + saa7146_read(av7110->dev, SSR)); + + if (saa7146_wait_for_debi_done(av7110->dev, 0)) { + printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); + BUG(); /* maybe we should try resetting the debi? */ + } + + spin_lock(&av7110->debilock); + ARM_ClearIrq(av7110); + + /* see what the av7110 wants */ + av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); + av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + len = (av7110->debilen + 3) & ~3; + + print_time("gpio"); + dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); + + switch (av7110->debitype & 0xff) { + + case DATA_TS_PLAY: + case DATA_PES_PLAY: + break; + + case DATA_MPEG_VIDEO_EVENT: + { + u32 h_ar; + struct video_event event; + + av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2); + h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2); + + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + + av7110->video_size.h = h_ar & 0xfff; + + event.type = VIDEO_EVENT_SIZE_CHANGED; + event.u.size.w = av7110->video_size.w; + event.u.size.h = av7110->video_size.h; + switch ((h_ar >> 12) & 0xf) + { + case 3: + av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; + event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; + av7110->videostate.video_format = VIDEO_FORMAT_16_9; + break; + case 4: + av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1; + event.u.size.aspect_ratio = VIDEO_FORMAT_221_1; + av7110->videostate.video_format = VIDEO_FORMAT_221_1; + break; + default: + av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3; + event.u.size.aspect_ratio = VIDEO_FORMAT_4_3; + av7110->videostate.video_format = VIDEO_FORMAT_4_3; + } + + dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n", + av7110->video_size.w, av7110->video_size.h, + av7110->video_size.aspect_ratio); + + dvb_video_add_event(av7110, &event); + break; + } + + case DATA_CI_PUT: + { + int avail; + struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer; + + avail = dvb_ringbuffer_avail(cibuf); + if (avail <= 2) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; + len |= DVB_RINGBUFFER_PEEK(cibuf, 1); + if (avail < len + 2) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + DVB_RINGBUFFER_SKIP(cibuf, 2); + + dvb_ringbuffer_read(cibuf, av7110->debi_virt, len); + + iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); + dprintk(8, "DMA: CI\n"); + start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); + spin_unlock(&av7110->debilock); + wake_up(&cibuf->queue); + return; + } + + case DATA_MPEG_PLAY: + if (!av7110->playing) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + len = 0; + if (av7110->debitype & 0x100) { + spin_lock(&av7110->aout.lock); + len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); + spin_unlock(&av7110->aout.lock); + } + if (len <= 0 && (av7110->debitype & 0x200) + &&av7110->videostate.play_state != VIDEO_FREEZED) { + spin_lock(&av7110->avout.lock); + len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); + spin_unlock(&av7110->avout.lock); + } + if (len <= 0) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len); + iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); + dprintk(8, "DMA: MPEG_PLAY\n"); + start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_BMP_LOAD: + len = av7110->debilen; + dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len); + if (!len) { + av7110->bmp_state = BMP_LOADED; + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + wake_up(&av7110->bmpq); + dprintk(8, "gpio DATA_BMP_LOAD done\n"); + break; + } + if (len > av7110->bmplen) + len = av7110->bmplen; + if (len > 2 * 1024) + len = 2 * 1024; + iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); + memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); + av7110->bmpp += len; + av7110->bmplen -= len; + dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len); + start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_CI_GET: + case DATA_COMMON_INTERFACE: + case DATA_FSECTION: + case DATA_IPMPE: + case DATA_PIPING: + if (!len || len > 4 * 1024) { + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + break; + } + /* fall through */ + + case DATA_TS_RECORD: + case DATA_PES_RECORD: + dprintk(8, "DMA: TS_REC etc.\n"); + start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_DEBUG_MESSAGE: + if (!len || len > 0xff) { + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + break; + } + start_debi_dma(av7110, DEBI_READ, Reserved, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_IRCOMMAND: + if (av7110->ir.ir_handler) + av7110->ir.ir_handler(av7110, + swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4))); + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + break; + + default: + printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n", + av7110->debitype, av7110->debilen); + break; + } + av7110->debitype = -1; + ARM_ClearMailBox(av7110); + spin_unlock(&av7110->debilock); +} + + +#ifdef CONFIG_DVB_AV7110_OSD +static int dvb_osd_ioctl(struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(4, "%p\n", av7110); + + if (cmd == OSD_SEND_CMD) + return av7110_osd_cmd(av7110, (osd_cmd_t *) parg); + if (cmd == OSD_GET_CAPABILITY) + return av7110_osd_capability(av7110, (osd_cap_t *) parg); + + return -EINVAL; +} + + +static const struct file_operations dvb_osd_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dvb_generic_ioctl, + .open = dvb_generic_open, + .release = dvb_generic_release, + .llseek = noop_llseek, +}; + +static struct dvb_device dvbdev_osd = { + .priv = NULL, + .users = 1, + .writers = 1, + .fops = &dvb_osd_fops, + .kernel_ioctl = dvb_osd_ioctl, +}; +#endif /* CONFIG_DVB_AV7110_OSD */ + + +static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, + u16 subpid, u16 pcrpid) +{ + u16 aflags = 0; + + dprintk(4, "%p\n", av7110); + + if (vpid == 0x1fff || apid == 0x1fff || + ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) { + vpid = apid = ttpid = subpid = pcrpid = 0; + av7110->pids[DMX_PES_VIDEO] = 0; + av7110->pids[DMX_PES_AUDIO] = 0; + av7110->pids[DMX_PES_TELETEXT] = 0; + av7110->pids[DMX_PES_PCR] = 0; + } + + if (av7110->audiostate.bypass_mode) + aflags |= 0x8000; + + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, + pcrpid, vpid, apid, ttpid, subpid, aflags); +} + +int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, + u16 subpid, u16 pcrpid) +{ + int ret = 0; + dprintk(4, "%p\n", av7110); + + if (mutex_lock_interruptible(&av7110->pid_mutex)) + return -ERESTARTSYS; + + if (!(vpid & 0x8000)) + av7110->pids[DMX_PES_VIDEO] = vpid; + if (!(apid & 0x8000)) + av7110->pids[DMX_PES_AUDIO] = apid; + if (!(ttpid & 0x8000)) + av7110->pids[DMX_PES_TELETEXT] = ttpid; + if (!(pcrpid & 0x8000)) + av7110->pids[DMX_PES_PCR] = pcrpid; + + av7110->pids[DMX_PES_SUBTITLE] = 0; + + if (av7110->fe_synced) { + pcrpid = av7110->pids[DMX_PES_PCR]; + ret = SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid); + } + + mutex_unlock(&av7110->pid_mutex); + return ret; +} + + +/****************************************************************************** + * hardware filter functions + ******************************************************************************/ + +static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) +{ + struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed; + struct av7110 *av7110 = dvbdmxfeed->demux->priv; + u16 buf[20]; + int ret, i; + u16 handle; +// u16 mode = 0x0320; + u16 mode = 0xb96a; + + dprintk(4, "%p\n", av7110); + + if (av7110->full_ts) + return 0; + + if (dvbdmxfilter->type == DMX_TYPE_SEC) { + if (hw_sections) { + buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) | + dvbdmxfilter->maskandmode[0]; + for (i = 3; i < 18; i++) + buf[i + 4 - 2] = + (dvbdmxfilter->filter.filter_value[i] << 8) | + dvbdmxfilter->maskandmode[i]; + mode = 4; + } + } else if ((dvbdmxfeed->ts_type & TS_PACKET) && + !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) { + av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); + } + + buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; + buf[1] = 16; + buf[2] = dvbdmxfeed->pid; + buf[3] = mode; + + ret = av7110_fw_request(av7110, buf, 20, &handle, 1); + if (ret != 0 || handle >= 32) { + printk("dvb-ttpci: %s error buf %04x %04x %04x %04x " + "ret %d handle %04x\n", + __func__, buf[0], buf[1], buf[2], buf[3], + ret, handle); + dvbdmxfilter->hw_handle = 0xffff; + if (!ret) + ret = -1; + return ret; + } + + av7110->handle2filter[handle] = dvbdmxfilter; + dvbdmxfilter->hw_handle = handle; + + return ret; +} + +static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) +{ + struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv; + u16 buf[3]; + u16 answ[2]; + int ret; + u16 handle; + + dprintk(4, "%p\n", av7110); + + if (av7110->full_ts) + return 0; + + handle = dvbdmxfilter->hw_handle; + if (handle >= 32) { + printk("%s tried to stop invalid filter %04x, filter type = %x\n", + __func__, handle, dvbdmxfilter->type); + return -EINVAL; + } + + av7110->handle2filter[handle] = NULL; + + buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; + buf[1] = 1; + buf[2] = handle; + ret = av7110_fw_request(av7110, buf, 3, answ, 2); + if (ret != 0 || answ[1] != handle) { + printk("dvb-ttpci: %s error cmd %04x %04x %04x ret %x " + "resp %04x %04x pid %d\n", + __func__, buf[0], buf[1], buf[2], ret, + answ[0], answ[1], dvbdmxfilter->feed->pid); + if (!ret) + ret = -1; + } + return ret; +} + + +static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct av7110 *av7110 = dvbdmx->priv; + u16 *pid = dvbdmx->pids, npids[5]; + int i; + int ret = 0; + + dprintk(4, "%p\n", av7110); + + npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; + i = dvbdmxfeed->pes_type; + npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; + if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { + npids[i] = 0; + ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); + if (!ret) + ret = StartHWFilter(dvbdmxfeed->filter); + return ret; + } + if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4) { + ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); + if (ret) + return ret; + } + + if (dvbdmxfeed->pes_type < 2 && npids[0]) + if (av7110->fe_synced) + { + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + if (ret) + return ret; + } + + if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) { + if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000)) + ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed); + if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000)) + ret = av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed); + } + return ret; +} + +static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct av7110 *av7110 = dvbdmx->priv; + u16 *pid = dvbdmx->pids, npids[5]; + int i; + + int ret = 0; + + dprintk(4, "%p\n", av7110); + + if (dvbdmxfeed->pes_type <= 1) { + ret = av7110_av_stop(av7110, dvbdmxfeed->pes_type ? RP_VIDEO : RP_AUDIO); + if (ret) + return ret; + if (!av7110->rec_mode) + dvbdmx->recording = 0; + if (!av7110->playing) + dvbdmx->playing = 0; + } + npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; + i = dvbdmxfeed->pes_type; + switch (i) { + case 2: //teletext + if (dvbdmxfeed->ts_type & TS_PACKET) + ret = StopHWFilter(dvbdmxfeed->filter); + npids[2] = 0; + break; + case 0: + case 1: + case 4: + if (!pids_off) + return 0; + npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; + break; + } + if (!ret) + ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); + return ret; +} + +static int av7110_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *av7110 = demux->priv; + int ret = 0; + + dprintk(4, "%p\n", av7110); + + if (!demux->dmx.frontend) + return -EINVAL; + + if (!av7110->full_ts && feed->pid > 0x1fff) + return -EINVAL; + + if (feed->type == DMX_TYPE_TS) { + if ((feed->ts_type & TS_DECODER) && + (feed->pes_type <= DMX_TS_PES_PCR)) { + switch (demux->dmx.frontend->source) { + case DMX_MEMORY_FE: + if (feed->ts_type & TS_DECODER) + if (feed->pes_type < 2 && + !(demux->pids[0] & 0x8000) && + !(demux->pids[1] & 0x8000)) { + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + ret = av7110_av_start_play(av7110,RP_AV); + if (!ret) + demux->playing = 1; + } + break; + default: + ret = dvb_feed_start_pid(feed); + break; + } + } else if ((feed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE)) { + ret = StartHWFilter(feed->filter); + } + } + + if (av7110->full_ts) { + budget_start_feed(feed); + return ret; + } + + if (feed->type == DMX_TYPE_SEC) { + int i; + + for (i = 0; i < demux->filternum; i++) { + if (demux->filter[i].state != DMX_STATE_READY) + continue; + if (demux->filter[i].type != DMX_TYPE_SEC) + continue; + if (demux->filter[i].filter.parent != &feed->feed.sec) + continue; + demux->filter[i].state = DMX_STATE_GO; + if (demux->dmx.frontend->source != DMX_MEMORY_FE) { + ret = StartHWFilter(&demux->filter[i]); + if (ret) + break; + } + } + } + + return ret; +} + + +static int av7110_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *av7110 = demux->priv; + int i, rc, ret = 0; + dprintk(4, "%p\n", av7110); + + if (feed->type == DMX_TYPE_TS) { + if (feed->ts_type & TS_DECODER) { + if (feed->pes_type >= DMX_TS_PES_OTHER || + !demux->pesfilter[feed->pes_type]) + return -EINVAL; + demux->pids[feed->pes_type] |= 0x8000; + demux->pesfilter[feed->pes_type] = NULL; + } + if (feed->ts_type & TS_DECODER && + feed->pes_type < DMX_TS_PES_OTHER) { + ret = dvb_feed_stop_pid(feed); + } else + if ((feed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE)) + ret = StopHWFilter(feed->filter); + } + + if (av7110->full_ts) { + budget_stop_feed(feed); + return ret; + } + + if (feed->type == DMX_TYPE_SEC) { + for (i = 0; ifilternum; i++) { + if (demux->filter[i].state == DMX_STATE_GO && + demux->filter[i].filter.parent == &feed->feed.sec) { + demux->filter[i].state = DMX_STATE_READY; + if (demux->dmx.frontend->source != DMX_MEMORY_FE) { + rc = StopHWFilter(&demux->filter[i]); + if (!ret) + ret = rc; + /* keep going, stop as many filters as possible */ + } + } + } + } + + return ret; +} + + +static void restart_feeds(struct av7110 *av7110) +{ + struct dvb_demux *dvbdmx = &av7110->demux; + struct dvb_demux_feed *feed; + int mode; + int feeding; + int i, j; + + dprintk(4, "%p\n", av7110); + + mode = av7110->playing; + av7110->playing = 0; + av7110->rec_mode = 0; + + feeding = av7110->feeding1; /* full_ts mod */ + + for (i = 0; i < dvbdmx->feednum; i++) { + feed = &dvbdmx->feed[i]; + if (feed->state == DMX_STATE_GO) { + if (feed->type == DMX_TYPE_SEC) { + for (j = 0; j < dvbdmx->filternum; j++) { + if (dvbdmx->filter[j].type != DMX_TYPE_SEC) + continue; + if (dvbdmx->filter[j].filter.parent != &feed->feed.sec) + continue; + if (dvbdmx->filter[j].state == DMX_STATE_GO) + dvbdmx->filter[j].state = DMX_STATE_READY; + } + } + av7110_start_feed(feed); + } + } + + av7110->feeding1 = feeding; /* full_ts mod */ + + if (mode) + av7110_av_start_play(av7110, mode); +} + +static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, + uint64_t *stc, unsigned int *base) +{ + int ret; + u16 fwstc[4]; + u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC); + struct dvb_demux *dvbdemux; + struct av7110 *av7110; + + /* pointer casting paranoia... */ + BUG_ON(!demux); + dvbdemux = demux->priv; + BUG_ON(!dvbdemux); + av7110 = dvbdemux->priv; + + dprintk(4, "%p\n", av7110); + + if (num != 0) + return -EINVAL; + + ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4); + if (ret) { + printk(KERN_ERR "%s: av7110_fw_request error\n", __func__); + return ret; + } + dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", + fwstc[0], fwstc[1], fwstc[2], fwstc[3]); + + *stc = (((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) | + (((uint64_t) fwstc[1]) << 16) | ((uint64_t) fwstc[0]); + *base = 1; + + dprintk(4, "stc = %lu\n", (unsigned long)*stc); + + return 0; +} + + +/****************************************************************************** + * SEC device file operations + ******************************************************************************/ + + +static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct av7110* av7110 = fe->dvb->priv; + + switch (tone) { + case SEC_TONE_ON: + return Set22K(av7110, 1); + + case SEC_TONE_OFF: + return Set22K(av7110, 0); + + default: + return -EINVAL; + } +} + +static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); +} + +static int av7110_diseqc_send_burst(struct dvb_frontend* fe, + fe_sec_mini_cmd_t minicmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + return av7110_diseqc_send(av7110, 0, NULL, minicmd); +} + +/* simplified code from budget-core.c */ +static int stop_ts_capture(struct av7110 *budget) +{ + dprintk(2, "budget: %p\n", budget); + + if (--budget->feeding1) + return budget->feeding1; + saa7146_write(budget->dev, MC1, MASK_20); /* DMA3 off */ + SAA7146_IER_DISABLE(budget->dev, MASK_10); + SAA7146_ISR_CLEAR(budget->dev, MASK_10); + return 0; +} + +static int start_ts_capture(struct av7110 *budget) +{ + dprintk(2, "budget: %p\n", budget); + + if (budget->feeding1) + return ++budget->feeding1; + memset(budget->grabbing, 0x00, TS_BUFLEN); + budget->ttbp = 0; + SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ + SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ + saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ + return ++budget->feeding1; +} + +static int budget_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *budget = demux->priv; + int status; + + dprintk(2, "av7110: %p\n", budget); + + spin_lock(&budget->feedlock1); + feed->pusi_seen = 0; /* have a clean section start */ + status = start_ts_capture(budget); + spin_unlock(&budget->feedlock1); + return status; +} + +static int budget_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *budget = demux->priv; + int status; + + dprintk(2, "budget: %p\n", budget); + + spin_lock(&budget->feedlock1); + status = stop_ts_capture(budget); + spin_unlock(&budget->feedlock1); + return status; +} + +static void vpeirq(unsigned long cookie) +{ + struct av7110 *budget = (struct av7110 *)cookie; + u8 *mem = (u8 *) (budget->grabbing); + u32 olddma = budget->ttbp; + u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; + + /* nearest lower position divisible by 188 */ + newdma -= newdma % 188; + + if (newdma >= TS_BUFLEN) + return; + + budget->ttbp = newdma; + + if (!budget->feeding1 || (newdma == olddma)) + return; + + /* Ensure streamed PCI data is synced to CPU */ + pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); + +#if 0 + /* track rps1 activity */ + printk("vpeirq: %02x Event Counter 1 0x%04x\n", + mem[olddma], + saa7146_read(budget->dev, EC1R) & 0x3fff); +#endif + + if (newdma > olddma) + /* no wraparound, dump olddma..newdma */ + dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); + else { + /* wraparound, dump olddma..buflen and 0..newdma */ + dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); + dvb_dmx_swfilter_packets(demux, mem, newdma / 188); + } +} + +static int av7110_register(struct av7110 *av7110) +{ + int ret, i; + struct dvb_demux *dvbdemux = &av7110->demux; + struct dvb_demux *dvbdemux1 = &av7110->demux1; + + dprintk(4, "%p\n", av7110); + + if (av7110->registered) + return -1; + + av7110->registered = 1; + + dvbdemux->priv = (void *) av7110; + + for (i = 0; i < 32; i++) + av7110->handle2filter[i] = NULL; + + dvbdemux->filternum = (av7110->full_ts) ? 256 : 32; + dvbdemux->feednum = (av7110->full_ts) ? 256 : 32; + dvbdemux->start_feed = av7110_start_feed; + dvbdemux->stop_feed = av7110_stop_feed; + dvbdemux->write_to_decoder = av7110_write_to_decoder; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + + dvb_dmx_init(&av7110->demux); + av7110->demux.dmx.get_stc = dvb_get_stc; + + av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32; + av7110->dmxdev.demux = &dvbdemux->dmx; + av7110->dmxdev.capabilities = 0; + + dvb_dmxdev_init(&av7110->dmxdev, &av7110->dvb_adapter); + + av7110->hw_frontend.source = DMX_FRONTEND_0; + + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend); + + if (ret < 0) + return ret; + + av7110->mem_frontend.source = DMX_MEMORY_FE; + + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend); + + if (ret < 0) + return ret; + + ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, + &av7110->hw_frontend); + if (ret < 0) + return ret; + + av7110_av_register(av7110); + av7110_ca_register(av7110); + +#ifdef CONFIG_DVB_AV7110_OSD + dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, + &dvbdev_osd, av7110, DVB_DEVICE_OSD); +#endif + + dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); + + if (budgetpatch) { + /* initialize software demux1 without its own frontend + * demux1 hardware is connected to frontend0 of demux0 + */ + dvbdemux1->priv = (void *) av7110; + + dvbdemux1->filternum = 256; + dvbdemux1->feednum = 256; + dvbdemux1->start_feed = budget_start_feed; + dvbdemux1->stop_feed = budget_stop_feed; + dvbdemux1->write_to_decoder = NULL; + + dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + + dvb_dmx_init(&av7110->demux1); + + av7110->dmxdev1.filternum = 256; + av7110->dmxdev1.demux = &dvbdemux1->dmx; + av7110->dmxdev1.capabilities = 0; + + dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter); + + dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx); + printk("dvb-ttpci: additional demux1 for budget-patch registered\n"); + } + return 0; +} + + +static void dvb_unregister(struct av7110 *av7110) +{ + struct dvb_demux *dvbdemux = &av7110->demux; + struct dvb_demux *dvbdemux1 = &av7110->demux1; + + dprintk(4, "%p\n", av7110); + + if (!av7110->registered) + return; + + if (budgetpatch) { + dvb_net_release(&av7110->dvb_net1); + dvbdemux->dmx.close(&dvbdemux1->dmx); + dvb_dmxdev_release(&av7110->dmxdev1); + dvb_dmx_release(&av7110->demux1); + } + + dvb_net_release(&av7110->dvb_net); + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend); + + dvb_dmxdev_release(&av7110->dmxdev); + dvb_dmx_release(&av7110->demux); + + if (av7110->fe != NULL) { + dvb_unregister_frontend(av7110->fe); + dvb_frontend_detach(av7110->fe); + } + dvb_unregister_device(av7110->osd_dev); + av7110_av_unregister(av7110); + av7110_ca_unregister(av7110); +} + + +/**************************************************************************** + * I2C client commands + ****************************************************************************/ + +int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) +{ + u8 msg[2] = { reg, val }; + struct i2c_msg msgs; + + msgs.flags = 0; + msgs.addr = id / 2; + msgs.len = 2; + msgs.buf = msg; + return i2c_transfer(&av7110->i2c_adap, &msgs, 1); +} + +u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) +{ + u8 mm1[] = {0x00}; + u8 mm2[] = {0x00}; + struct i2c_msg msgs[2]; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = id / 2; + mm1[0] = reg; + msgs[0].len = 1; msgs[1].len = 1; + msgs[0].buf = mm1; msgs[1].buf = mm2; + i2c_transfer(&av7110->i2c_adap, msgs, 2); + + return mm2[0]; +} + +/**************************************************************************** + * INITIALIZATION + ****************************************************************************/ + + +static int check_firmware(struct av7110* av7110) +{ + u32 crc = 0, len = 0; + unsigned char *ptr; + + /* check for firmware magic */ + ptr = av7110->bin_fw; + if (ptr[0] != 'A' || ptr[1] != 'V' || + ptr[2] != 'F' || ptr[3] != 'W') { + printk("dvb-ttpci: this is not an av7110 firmware\n"); + return -EINVAL; + } + ptr += 4; + + /* check dpram file */ + crc = get_unaligned_be32(ptr); + ptr += 4; + len = get_unaligned_be32(ptr); + ptr += 4; + if (len >= 512) { + printk("dvb-ttpci: dpram file is way too big.\n"); + return -EINVAL; + } + if (crc != crc32_le(0, ptr, len)) { + printk("dvb-ttpci: crc32 of dpram file does not match.\n"); + return -EINVAL; + } + av7110->bin_dpram = ptr; + av7110->size_dpram = len; + ptr += len; + + /* check root file */ + crc = get_unaligned_be32(ptr); + ptr += 4; + len = get_unaligned_be32(ptr); + ptr += 4; + + if (len <= 200000 || len >= 300000 || + len > ((av7110->bin_fw + av7110->size_fw) - ptr)) { + printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); + return -EINVAL; + } + if( crc != crc32_le(0, ptr, len)) { + printk("dvb-ttpci: crc32 of root file does not match.\n"); + return -EINVAL; + } + av7110->bin_root = ptr; + av7110->size_root = len; + return 0; +} + +static void put_firmware(struct av7110* av7110) +{ + vfree(av7110->bin_fw); +} + +static int get_firmware(struct av7110* av7110) +{ + int ret; + const struct firmware *fw; + + /* request the av7110 firmware, this will block until someone uploads it */ + ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev); + if (ret) { + if (ret == -ENOENT) { + printk(KERN_ERR "dvb-ttpci: could not load firmware," + " file not found: dvb-ttpci-01.fw\n"); + printk(KERN_ERR "dvb-ttpci: usually this should be in " + "/usr/lib/hotplug/firmware or /lib/firmware\n"); + printk(KERN_ERR "dvb-ttpci: and can be downloaded from" + " http://www.linuxtv.org/download/dvb/firmware/\n"); + } else + printk(KERN_ERR "dvb-ttpci: cannot request firmware" + " (error %i)\n", ret); + return -EINVAL; + } + + if (fw->size <= 200000) { + printk("dvb-ttpci: this firmware is way too small.\n"); + release_firmware(fw); + return -EINVAL; + } + + /* check if the firmware is available */ + av7110->bin_fw = vmalloc(fw->size); + if (NULL == av7110->bin_fw) { + dprintk(1, "out of memory\n"); + release_firmware(fw); + return -ENOMEM; + } + + memcpy(av7110->bin_fw, fw->data, fw->size); + av7110->size_fw = fw->size; + if ((ret = check_firmware(av7110))) + vfree(av7110->bin_fw); + + release_firmware(fw); + return ret; +} + +static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (p->frequency + 479500) / 125; + + if (p->frequency > 2000000) + pwr = 3; + else if (p->frequency > 1800000) + pwr = 2; + else if (p->frequency > 1600000) + pwr = 1; + else if (p->frequency > 1200000) + pwr = 0; + else if (p->frequency >= 1100000) + pwr = 1; + else + pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = { + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, +}; + +static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (p->frequency + 35937500 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + + + + +static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = p->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, +}; + + + +static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u32 f = p->frequency; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (f + 36125000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1820_config philips_cd1516_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + + + +static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div, pwr; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (p->frequency + 36200000) / 166666; + + if (p->frequency <= 782000000) + pwr = 1; + else + pwr = 2; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85; + data[3] = pwr << 6; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ +#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) + struct av7110* av7110 = fe->dvb->priv; + + return request_firmware(fw, name, &av7110->dev->pci->dev); +#else + return -EINVAL; +#endif +} + +static struct sp8870_config alps_tdlb7_config = { + + .demod_address = 0x71, + .request_firmware = alps_tdlb7_request_firmware, +}; + + +static u8 nexusca_stv0297_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x00, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x00, + 0x4b, 0x7b, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x10, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x04, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x49, + 0x62, 0x0b, + 0x53, 0x08, + 0x59, 0x08, + 0xff, 0xff, +}; + +static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; + struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; + int i; + + div = (p->frequency + 36150000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xce; + + if (p->frequency < 45000000) + return -EINVAL; + else if (p->frequency < 137000000) + data[3] = 0x01; + else if (p->frequency < 403000000) + data[3] = 0x02; + else if (p->frequency < 860000000) + data[3] = 0x04; + else + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { + printk("nexusca: pll transfer failed!\n"); + return -EIO; + } + + // wait for PLL lock + for(i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) + if (data[0] & 0x40) break; + msleep(10); + } + + return 0; +} + +static struct stv0297_config nexusca_stv0297_config = { + + .demod_address = 0x1C, + .inittab = nexusca_stv0297_inittab, + .invert = 1, + .stop_during_read = 1, +}; + + + +static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 cfg, cpump, band_select; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (36125000 + p->frequency) / 166666; + + cfg = 0x88; + + if (p->frequency < 175000000) + cpump = 2; + else if (p->frequency < 390000000) + cpump = 1; + else if (p->frequency < 470000000) + cpump = 2; + else if (p->frequency < 750000000) + cpump = 1; + else + cpump = 3; + + if (p->frequency < 175000000) + band_select = 0x0e; + else if (p->frequency < 470000000) + band_select = 0x05; + else + band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = (cpump << 6) | band_select; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct l64781_config grundig_29504_401_config = { + .demod_address = 0x55, +}; + + + +static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) +{ + int ret = 0; + int synced = (status & FE_HAS_LOCK) ? 1 : 0; + + av7110->fe_status = status; + + if (av7110->fe_synced == synced) + return 0; + + if (av7110->playing) { + av7110->fe_synced = synced; + return 0; + } + + if (mutex_lock_interruptible(&av7110->pid_mutex)) + return -ERESTARTSYS; + + if (synced) { + ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], + av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_TELETEXT], 0, + av7110->pids[DMX_PES_PCR]); + if (!ret) + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + } else { + ret = SetPIDs(av7110, 0, 0, 0, 0, 0); + if (!ret) { + ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); + if (!ret) + ret = av7110_wait_msgstate(av7110, GPMQBusy); + } + } + + if (!ret) + av7110->fe_synced = synced; + + mutex_unlock(&av7110->pid_mutex); + return ret; +} + +static int av7110_fe_set_frontend(struct dvb_frontend *fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_set_frontend(fe); + + return ret; +} + +static int av7110_fe_init(struct dvb_frontend* fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_init(fe); + return ret; +} + +static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct av7110* av7110 = fe->dvb->priv; + + /* call the real implementation */ + int ret = av7110->fe_read_status(fe, status); + if (!ret) + if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) + ret = av7110_fe_lock_fix(av7110, *status); + return ret; +} + +static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_diseqc_reset_overload(fe); + return ret; +} + +static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_master_cmd = *cmd; + ret = av7110->fe_diseqc_send_master_cmd(fe, cmd); + } + return ret; +} + +static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_minicmd = minicmd; + ret = av7110->fe_diseqc_send_burst(fe, minicmd); + } + return ret; +} + +static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_tone = tone; + ret = av7110->fe_set_tone(fe, tone); + } + return ret; +} + +static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_voltage = voltage; + ret = av7110->fe_set_voltage(fe, voltage); + } + return ret; +} + +static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd); + return ret; +} + +static void dvb_s_recover(struct av7110* av7110) +{ + av7110_fe_init(av7110->fe); + + av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage); + if (av7110->saved_master_cmd.msg_len) { + msleep(20); + av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd); + } + msleep(20); + av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd); + msleep(20); + av7110_fe_set_tone(av7110->fe, av7110->saved_tone); + + av7110_fe_set_frontend(av7110->fe); +} + +static u8 read_pwm(struct av7110* av7110) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static int frontend_init(struct av7110 *av7110) +{ + int ret; + + if (av7110->dev->pci->subsystem_vendor == 0x110a) { + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) + av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, + &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; + } + break; + } + + } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X + case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X + case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE + + // try the ALPS BSRV2 first of all + av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + break; + } + + // try the ALPS BSRU6 now + av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + break; + } + + // Try the grundig 29504-451 + av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + break; + } + + /* Try DVB-C cards */ + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: + /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ + av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, + read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; + } + break; + case 0x0003: + /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ + av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, + read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } + break; + } + break; + + case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X + // try ALPS TDLB7 first, then Grundig 29504-401 + av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; + break; + } + /* fall-thru */ + + case 0x0008: // Hauppauge/TT DVB-T + // Grundig 29504-401 + av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap); + if (av7110->fe) + av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + break; + + case 0x0002: // Hauppauge/TT DVB-C premium rev2.X + + av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } + break; + + case 0x0004: // Galaxis DVB-S rev1.3 + /* ALPS BSRV2 */ + av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + } + break; + + case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */ + /* Grundig 29504-451 */ + av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + } + break; + + case 0x000A: // Hauppauge/TT Nexus-CA rev1.X + + av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params; + + /* set TDA9819 into DVB mode */ + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) + + /* tuner on this needs a slower i2c bus speed */ + av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + break; + } + break; + + case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */ + /* ALPS BSBE1 */ + av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + + if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { + printk("dvb-ttpci: LNBP21 not found!\n"); + if (av7110->fe->ops.release) + av7110->fe->ops.release(av7110->fe); + av7110->fe = NULL; + } else { + av7110->fe->ops.dishnetwork_send_legacy_command = NULL; + av7110->recover = dvb_s_recover; + } + } + break; + } + } + + if (!av7110->fe) { + /* FIXME: propagate the failure code from the lower layers */ + ret = -ENOMEM; + printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + av7110->dev->pci->vendor, + av7110->dev->pci->device, + av7110->dev->pci->subsystem_vendor, + av7110->dev->pci->subsystem_device); + } else { + FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init); + FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status); + FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload); + FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd); + FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst); + FE_FUNC_OVERRIDE(av7110->fe->ops.set_tone, av7110->fe_set_tone, av7110_fe_set_tone); + FE_FUNC_OVERRIDE(av7110->fe->ops.set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage); + FE_FUNC_OVERRIDE(av7110->fe->ops.dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); + FE_FUNC_OVERRIDE(av7110->fe->ops.set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); + + ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); + if (ret < 0) { + printk("av7110: Frontend registration failed!\n"); + dvb_frontend_detach(av7110->fe); + av7110->fe = NULL; + } + } + return ret; +} + +/* Budgetpatch note: + * Original hardware design by Roberto Deza: + * There is a DVB_Wiki at + * http://www.linuxtv.org/ + * + * New software triggering design by Emard that works on + * original Roberto Deza's hardware: + * + * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. + * GPIO3 is in budget-patch hardware connectd to port B VSYNC + * HS is an internal event of 7146, accessible with RPS + * and temporarily raised high every n lines + * (n in defined in the RPS_THRESH1 counter threshold) + * I think HS is raised high on the beginning of the n-th line + * and remains high until this n-th line that triggered + * it is completely received. When the receiption of n-th line + * ends, HS is lowered. + * + * To transmit data over DMA, 7146 needs changing state at + * port B VSYNC pin. Any changing of port B VSYNC will + * cause some DMA data transfer, with more or less packets loss. + * It depends on the phase and frequency of VSYNC and + * the way of 7146 is instructed to trigger on port B (defined + * in DD1_INIT register, 3rd nibble from the right valid + * numbers are 0-7, see datasheet) + * + * The correct triggering can minimize packet loss, + * dvbtraffic should give this stable bandwidths: + * 22k transponder = 33814 kbit/s + * 27.5k transponder = 38045 kbit/s + * by experiment it is found that the best results + * (stable bandwidths and almost no packet loss) + * are obtained using DD1_INIT triggering number 2 + * (Va at rising edge of VS Fa = HS x VS-failing forced toggle) + * and a VSYNC phase that occurs in the middle of DMA transfer + * (about byte 188*512=96256 in the DMA window). + * + * Phase of HS is still not clear to me how to control, + * It just happens to be so. It can be seen if one enables + * RPS_IRQ and print Event Counter 1 in vpeirq(). Every + * time RPS_INTERRUPT is called, the Event Counter 1 will + * increment. That's how the 7146 is programmed to do event + * counting in this budget-patch.c + * I *think* HPS setting has something to do with the phase + * of HS but I can't be 100% sure in that. + * + * hardware debug note: a working budget card (including budget patch) + * with vpeirq() interrupt setup in mode "0x90" (every 64K) will + * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes + * and that means 3*25=75 Hz of interrupt freqency, as seen by + * watch cat /proc/interrupts + * + * If this frequency is 3x lower (and data received in the DMA + * buffer don't start with 0x47, but in the middle of packets, + * whose lengths appear to be like 188 292 188 104 etc. + * this means VSYNC line is not connected in the hardware. + * (check soldering pcb and pins) + * The same behaviour of missing VSYNC can be duplicated on budget + * cards, by seting DD1_INIT trigger mode 7 in 3rd nibble. + */ +static int __devinit av7110_attach(struct saa7146_dev* dev, + struct saa7146_pci_extension_data *pci_ext) +{ + const int length = TS_WIDTH * TS_HEIGHT; + struct pci_dev *pdev = dev->pci; + struct av7110 *av7110; + struct task_struct *thread; + int ret, count = 0; + + dprintk(4, "dev: %p\n", dev); + + /* Set RPS_IRQ to 1 to track rps1 activity. + * Enabling this won't send any interrupt to PC CPU. + */ +#define RPS_IRQ 0 + + if (budgetpatch == 1) { + budgetpatch = 0; + /* autodetect the presence of budget patch + * this only works if saa7146 has been recently + * reset with with MASK_31 to MC1 + * + * will wait for VBI_B event (vertical blank at port B) + * and will reset GPIO3 after VBI_B is detected. + * (GPIO3 should be raised high by CPU to + * test if GPIO3 will generate vertical blank signal + * in budget patch GPIO3 is connected to VSYNC_B + */ + + /* RESET SAA7146 */ + saa7146_write(dev, MC1, MASK_31); + /* autodetection success seems to be time-dependend after reset */ + + /* Fix VSYNC level */ + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + /* set vsync_b triggering */ + saa7146_write(dev, DD1_STREAM_B, 0); + /* port B VSYNC at rising edge */ + saa7146_write(dev, DD1_INIT, 0x00000200); + saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI + saa7146_write(dev, MC2, + 1 * (MASK_08 | MASK_24) | // BRS control + 0 * (MASK_09 | MASK_25) | // a + 1 * (MASK_10 | MASK_26) | // b + 0 * (MASK_06 | MASK_22) | // HPS_CTRL1 + 0 * (MASK_05 | MASK_21) | // HPS_CTRL2 + 0 * (MASK_01 | MASK_15) // DEBI + ); + + /* start writing RPS1 code from beginning */ + count = 0; + /* Disable RPS1 */ + saa7146_write(dev, MC1, MASK_29); + /* RPS1 timeout disable */ + saa7146_write(dev, RPS_TOV1, 0); + WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + /* issue RPS1 interrupt to increment counter */ + WRITE_RPS1(CMD_INTERRUPT); +#endif + WRITE_RPS1(CMD_STOP); + /* Jump to begin of RPS program as safety measure (p37) */ + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); + +#if RPS_IRQ + /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) + * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled + * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called + */ + saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + /* set event counter 1 threshold to maximum allowed value (rEC p55) */ + saa7146_write(dev, ECT1R, 0x3fff ); +#endif + /* Set RPS1 Address register to point to RPS code (r108 p42) */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + /* Enable RPS1, (rFC p33) */ + saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); + + mdelay(10); + /* now send VSYNC_B to rps1 by rising GPIO3 */ + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + mdelay(10); + /* if rps1 responded by lowering the GPIO3, + * then we have budgetpatch hardware + */ + if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) { + budgetpatch = 1; + printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); + } + /* Disable RPS1 */ + saa7146_write(dev, MC1, ( MASK_29 )); +#if RPS_IRQ + printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); +#endif + } + + /* prepare the av7110 device struct */ + av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL); + if (!av7110) { + dprintk(1, "out of memory\n"); + return -ENOMEM; + } + + av7110->card_name = (char*) pci_ext->ext_priv; + av7110->dev = dev; + dev->ext_priv = av7110; + + ret = get_firmware(av7110); + if (ret < 0) + goto err_kfree_0; + + ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, + THIS_MODULE, &dev->pci->dev, adapter_nr); + if (ret < 0) + goto err_put_firmware_1; + + /* the Siemens DVB needs this if you want to have the i2c chips + get recognized before the main driver is fully loaded */ + saa7146_write(dev, GPIO_CTRL, 0x500000); + + strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name)); + + saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ + + ret = i2c_add_adapter(&av7110->i2c_adap); + if (ret < 0) + goto err_dvb_unregister_adapter_2; + + ttpci_eeprom_parse_mac(&av7110->i2c_adap, + av7110->dvb_adapter.proposed_mac); + ret = -ENOMEM; + + /* full-ts mod? */ + if (full_ts) + av7110->full_ts = true; + + /* check for full-ts flag in eeprom */ + if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { + u8 flags = i2c_readreg(av7110, 0xaa, 2); + if (flags != 0xff && (flags & 0x01)) + av7110->full_ts = true; + } + + if (av7110->full_ts) { + printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); + spin_lock_init(&av7110->feedlock1); + av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, + &av7110->pt); + if (!av7110->grabbing) + goto err_i2c_del_3; + + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + saa7146_write(dev, BRS_CTRL, 0x60000000); + saa7146_write(dev, MC2, MASK_08 | MASK_24); + + /* dma3 */ + saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); + saa7146_write(dev, BASE_ODD3, 0); + saa7146_write(dev, BASE_EVEN3, 0); + saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); + saa7146_write(dev, PITCH3, TS_WIDTH); + saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); + saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); + saa7146_write(dev, MC2, MASK_04 | MASK_20); + + tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); + + } else if (budgetpatch) { + spin_lock_init(&av7110->feedlock1); + av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, + &av7110->pt); + if (!av7110->grabbing) + goto err_i2c_del_3; + + saa7146_write(dev, PCI_BT_V1, 0x1c1f101f); + saa7146_write(dev, BCS_CTRL, 0x80400040); + /* set dd1 stream a & b */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, DD1_INIT, 0x03000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + saa7146_write(dev, BASE_ODD3, 0); + saa7146_write(dev, BASE_EVEN3, 0); + saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); + saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); + + saa7146_write(dev, PITCH3, TS_WIDTH); + saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); + + /* upload all */ + saa7146_write(dev, MC2, 0x077c077c); + saa7146_write(dev, GPIO_CTRL, 0x000000); +#if RPS_IRQ + /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) + * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled + * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called + */ + saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + /* set event counter 1 threshold to maximum allowed value (rEC p55) */ + saa7146_write(dev, ECT1R, 0x3fff ); +#endif + /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ + count = 0; + + /* Wait Source Line Counter Threshold (p36) */ + WRITE_RPS1(CMD_PAUSE | EVT_HS); + /* Set GPIO3=1 (p42) */ + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); +#if RPS_IRQ + /* issue RPS1 interrupt */ + WRITE_RPS1(CMD_INTERRUPT); +#endif + /* Wait reset Source Line Counter Threshold (p36) */ + WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); + /* Set GPIO3=0 (p42) */ + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + /* issue RPS1 interrupt */ + WRITE_RPS1(CMD_INTERRUPT); +#endif + /* Jump to begin of RPS program (p37) */ + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); + + /* Fix VSYNC level */ + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + /* Set RPS1 Address register to point to RPS code (r108 p42) */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + /* Set Source Line Counter Threshold, using BRS (rCC p43) + * It generates HS event every TS_HEIGHT lines + * this is related to TS_WIDTH set in register + * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits + * are set to TS_WIDTH bytes (TS_WIDTH=2*188), + * then RPS_THRESH1 should be set to trigger + * every TS_HEIGHT (512) lines. + */ + saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); + + /* Enable RPS1 (rFC p33) */ + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); + + /* end of budgetpatch register initialization */ + tasklet_init (&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); + } else { + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + saa7146_write(dev, BCS_CTRL, 0x80400040); + + /* set dd1 stream a & b */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, DD1_INIT, 0x03000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + /* upload all */ + saa7146_write(dev, MC2, 0x077c077c); + saa7146_write(dev, GPIO_CTRL, 0x000000); + } + + tasklet_init (&av7110->debi_tasklet, debiirq, (unsigned long) av7110); + tasklet_init (&av7110->gpio_tasklet, gpioirq, (unsigned long) av7110); + + mutex_init(&av7110->pid_mutex); + + /* locks for data transfers from/to AV7110 */ + spin_lock_init(&av7110->debilock); + mutex_init(&av7110->dcomlock); + av7110->debitype = -1; + + /* default OSD window */ + av7110->osdwin = 1; + mutex_init(&av7110->osd_mutex); + + /* TV standard */ + av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC + : AV7110_VIDEO_MODE_PAL; + + /* ARM "watchdog" */ + init_waitqueue_head(&av7110->arm_wait); + av7110->arm_thread = NULL; + + /* allocate and init buffers */ + av7110->debi_virt = pci_alloc_consistent(pdev, 8192, &av7110->debi_bus); + if (!av7110->debi_virt) + goto err_saa71466_vfree_4; + + + av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); + if (!av7110->iobuf) + goto err_pci_free_5; + + ret = av7110_av_init(av7110); + if (ret < 0) + goto err_iobuf_vfree_6; + + /* init BMP buffer */ + av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN; + init_waitqueue_head(&av7110->bmpq); + + ret = av7110_ca_init(av7110); + if (ret < 0) + goto err_av7110_av_exit_7; + + /* load firmware into AV7110 cards */ + ret = av7110_bootarm(av7110); + if (ret < 0) + goto err_av7110_ca_exit_8; + + ret = av7110_firmversion(av7110); + if (ret < 0) + goto err_stop_arm_9; + + if (FW_VERSION(av7110->arm_app)<0x2501) + printk ("dvb-ttpci: Warning, firmware version 0x%04x is too old. " + "System might be unstable!\n", FW_VERSION(av7110->arm_app)); + + thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); + if (IS_ERR(thread)) { + ret = PTR_ERR(thread); + goto err_stop_arm_9; + } + av7110->arm_thread = thread; + + /* set initial volume in mixer struct */ + av7110->mixer.volume_left = volume; + av7110->mixer.volume_right = volume; + + ret = av7110_register(av7110); + if (ret < 0) + goto err_arm_thread_stop_10; + + init_av7110_av(av7110); + + /* special case DVB-C: these cards have an analog tuner + plus need some special handling, so we have separate + saa7146_ext_vv data for these... */ + ret = av7110_init_v4l(av7110); + if (ret < 0) + goto err_av7110_unregister_11; + + av7110->dvb_adapter.priv = av7110; + ret = frontend_init(av7110); + if (ret < 0) + goto err_av7110_exit_v4l_12; + +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_ir_init(av7110); +#endif + printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); + av7110_num++; +out: + return ret; + +err_av7110_exit_v4l_12: + av7110_exit_v4l(av7110); +err_av7110_unregister_11: + dvb_unregister(av7110); +err_arm_thread_stop_10: + av7110_arm_sync(av7110); +err_stop_arm_9: + /* Nothing to do. Rejoice. */ +err_av7110_ca_exit_8: + av7110_ca_exit(av7110); +err_av7110_av_exit_7: + av7110_av_exit(av7110); +err_iobuf_vfree_6: + vfree(av7110->iobuf); +err_pci_free_5: + pci_free_consistent(pdev, 8192, av7110->debi_virt, av7110->debi_bus); +err_saa71466_vfree_4: + if (av7110->grabbing) + saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt); +err_i2c_del_3: + i2c_del_adapter(&av7110->i2c_adap); +err_dvb_unregister_adapter_2: + dvb_unregister_adapter(&av7110->dvb_adapter); +err_put_firmware_1: + put_firmware(av7110); +err_kfree_0: + kfree(av7110); + goto out; +} + +static int __devexit av7110_detach(struct saa7146_dev* saa) +{ + struct av7110 *av7110 = saa->ext_priv; + dprintk(4, "%p\n", av7110); + +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_ir_exit(av7110); +#endif + if (budgetpatch || av7110->full_ts) { + if (budgetpatch) { + /* Disable RPS1 */ + saa7146_write(saa, MC1, MASK_29); + /* VSYNC LOW (inactive) */ + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + } + saa7146_write(saa, MC1, MASK_20); /* DMA3 off */ + SAA7146_IER_DISABLE(saa, MASK_10); + SAA7146_ISR_CLEAR(saa, MASK_10); + msleep(50); + tasklet_kill(&av7110->vpe_tasklet); + saa7146_vfree_destroy_pgtable(saa->pci, av7110->grabbing, &av7110->pt); + } + av7110_exit_v4l(av7110); + + av7110_arm_sync(av7110); + + tasklet_kill(&av7110->debi_tasklet); + tasklet_kill(&av7110->gpio_tasklet); + + dvb_unregister(av7110); + + SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); + + av7110_ca_exit(av7110); + av7110_av_exit(av7110); + + vfree(av7110->iobuf); + pci_free_consistent(saa->pci, 8192, av7110->debi_virt, + av7110->debi_bus); + + i2c_del_adapter(&av7110->i2c_adap); + + dvb_unregister_adapter (&av7110->dvb_adapter); + + av7110_num--; + + put_firmware(av7110); + + kfree(av7110); + + saa->ext_priv = NULL; + + return 0; +} + + +static void av7110_irq(struct saa7146_dev* dev, u32 *isr) +{ + struct av7110 *av7110 = dev->ext_priv; + + //print_time("av7110_irq"); + + /* Note: Don't try to handle the DEBI error irq (MASK_18), in + * intel mode the timeout is asserted all the time... + */ + + if (*isr & MASK_19) { + //printk("av7110_irq: DEBI\n"); + /* Note 1: The DEBI irq is level triggered: We must enable it + * only after we started a DMA xfer, and disable it here + * immediately, or it will be signalled all the time while + * DEBI is idle. + * Note 2: You would think that an irq which is masked is + * not signalled by the hardware. Not so for the SAA7146: + * An irq is signalled as long as the corresponding bit + * in the ISR is set, and disabling irqs just prevents the + * hardware from setting the ISR bit. This means a) that we + * must clear the ISR *after* disabling the irq (which is why + * we must do it here even though saa7146_core did it already), + * and b) that if we were to disable an edge triggered irq + * (like the gpio irqs sadly are) temporarily we would likely + * loose some. This sucks :-( + */ + SAA7146_IER_DISABLE(av7110->dev, MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); + tasklet_schedule(&av7110->debi_tasklet); + } + + if (*isr & MASK_03) { + //printk("av7110_irq: GPIO\n"); + tasklet_schedule(&av7110->gpio_tasklet); + } + + if (*isr & MASK_10) + tasklet_schedule(&av7110->vpe_tasklet); +} + + +static struct saa7146_extension av7110_extension_driver; + +#define MAKE_AV7110_INFO(x_var,x_name) \ +static struct saa7146_pci_extension_data x_var = { \ + .ext_priv = x_name, \ + .ext = &av7110_extension_driver } + +MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); +MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); +MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); +MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); +MAKE_AV7110_INFO(tts_2_X, "Technotrend/Hauppauge WinTV Nexus-S rev2.X"); +MAKE_AV7110_INFO(tts_2_3, "Technotrend/Hauppauge WinTV Nexus-S rev2.3"); +MAKE_AV7110_INFO(tts_1_3se, "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE"); +MAKE_AV7110_INFO(ttt, "Technotrend/Hauppauge DVB-T"); +MAKE_AV7110_INFO(fsc, "Fujitsu Siemens DVB-C"); +MAKE_AV7110_INFO(fss, "Fujitsu Siemens DVB-S rev1.6"); +MAKE_AV7110_INFO(gxs_1_3, "Galaxis DVB-S rev1.3"); + +static struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(fsc, 0x110a, 0x0000), + MAKE_EXTENSION_PCI(tts_1_X_fsc, 0x13c2, 0x0000), + MAKE_EXTENSION_PCI(ttt_1_X, 0x13c2, 0x0001), + MAKE_EXTENSION_PCI(ttc_2_X, 0x13c2, 0x0002), + MAKE_EXTENSION_PCI(tts_2_X, 0x13c2, 0x0003), + MAKE_EXTENSION_PCI(gxs_1_3, 0x13c2, 0x0004), + MAKE_EXTENSION_PCI(fss, 0x13c2, 0x0006), + MAKE_EXTENSION_PCI(ttt, 0x13c2, 0x0008), + MAKE_EXTENSION_PCI(ttc_1_X, 0x13c2, 0x000a), + MAKE_EXTENSION_PCI(tts_2_3, 0x13c2, 0x000e), + MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), + +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? + + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + + +static struct saa7146_extension av7110_extension_driver = { + .name = "av7110", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = &pci_tbl[0], + .attach = av7110_attach, + .detach = __devexit_p(av7110_detach), + + .irq_mask = MASK_19 | MASK_03 | MASK_10, + .irq_func = av7110_irq, +}; + + +static int __init av7110_init(void) +{ + int retval; + retval = saa7146_register_extension(&av7110_extension_driver); + return retval; +} + + +static void __exit av7110_exit(void) +{ + saa7146_unregister_extension(&av7110_extension_driver); +} + +module_init(av7110_init); +module_exit(av7110_exit); + +MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by " + "Siemens, Technotrend, Hauppauge"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h new file mode 100644 index 000000000000..88b3b2d6cc0e --- /dev/null +++ b/drivers/media/pci/ttpci/av7110.h @@ -0,0 +1,314 @@ +#ifndef _AV7110_H_ +#define _AV7110_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dvbdev.h" +#include "demux.h" +#include "dvb_demux.h" +#include "dmxdev.h" +#include "dvb_filter.h" +#include "dvb_net.h" +#include "dvb_ringbuffer.h" +#include "dvb_frontend.h" +#include "ves1820.h" +#include "ves1x93.h" +#include "stv0299.h" +#include "tda8083.h" +#include "sp8870.h" +#include "stv0297.h" +#include "l64781.h" + +#include + + +#define ANALOG_TUNER_VES1820 1 +#define ANALOG_TUNER_STV0297 2 + +extern int av7110_debug; + +#define dprintk(level,args...) \ + do { if ((av7110_debug & level)) { printk("dvb-ttpci: %s(): ", __func__); printk(args); } } while (0) + +#define MAXFILT 32 + +enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM}; + +enum av7110_video_mode { + AV7110_VIDEO_MODE_PAL = 0, + AV7110_VIDEO_MODE_NTSC = 1 +}; + +struct av7110_p2t { + u8 pes[TS_SIZE]; + u8 counter; + long int pos; + int frags; + struct dvb_demux_feed *feed; +}; + +/* video MPEG decoder events: */ +/* (code copied from dvb_frontend.c, should maybe be factored out...) */ +#define MAX_VIDEO_EVENT 8 +struct dvb_video_events { + struct video_event events[MAX_VIDEO_EVENT]; + int eventw; + int eventr; + int overflow; + wait_queue_head_t wait_queue; + spinlock_t lock; +}; + + +struct av7110; + +/* infrared remote control */ +struct infrared { + u16 key_map[256]; + struct input_dev *input_dev; + char input_phys[32]; + struct timer_list keyup_timer; + struct tasklet_struct ir_tasklet; + void (*ir_handler)(struct av7110 *av7110, u32 ircom); + u32 ir_command; + u32 ir_config; + u32 device_mask; + u8 protocol; + u8 inversion; + u16 last_key; + u16 last_toggle; + u8 delay_timer_finished; +}; + + +/* place to store all the necessary device information */ +struct av7110 { + + /* devices */ + + struct dvb_device dvb_dev; + struct dvb_net dvb_net; + + struct video_device *v4l_dev; + struct video_device *vbi_dev; + + struct saa7146_dev *dev; + + struct i2c_adapter i2c_adap; + + char *card_name; + + /* support for analog module of dvb-c */ + int analog_tuner_flags; + int current_input; + u32 current_freq; + + struct tasklet_struct debi_tasklet; + struct tasklet_struct gpio_tasklet; + + int adac_type; /* audio DAC type */ +#define DVB_ADAC_TI 0 +#define DVB_ADAC_CRYSTAL 1 +#define DVB_ADAC_MSP34x0 2 +#define DVB_ADAC_MSP34x5 3 +#define DVB_ADAC_NONE -1 + + + /* buffers */ + + void *iobuf; /* memory for all buffers */ + struct dvb_ringbuffer avout; /* buffer for video or A/V mux */ +#define AVOUTLEN (128*1024) + struct dvb_ringbuffer aout; /* buffer for audio */ +#define AOUTLEN (64*1024) + void *bmpbuf; +#define BMPLEN (8*32768+1024) + + /* bitmap buffers and states */ + + int bmpp; + int bmplen; + volatile int bmp_state; +#define BMP_NONE 0 +#define BMP_LOADING 1 +#define BMP_LOADED 2 + wait_queue_head_t bmpq; + + + /* DEBI and polled command interface */ + + spinlock_t debilock; + struct mutex dcomlock; + volatile int debitype; + volatile int debilen; + + + /* Recording and playback flags */ + + int rec_mode; + int playing; +#define RP_NONE 0 +#define RP_VIDEO 1 +#define RP_AUDIO 2 +#define RP_AV 3 + + + /* OSD */ + + int osdwin; /* currently active window */ + u16 osdbpp[8]; + struct mutex osd_mutex; + + /* CA */ + + ca_slot_info_t ci_slot[2]; + + enum av7110_video_mode vidmode; + struct dmxdev dmxdev; + struct dvb_demux demux; + + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + + /* for budget mode demux1 */ + struct dmxdev dmxdev1; + struct dvb_demux demux1; + struct dvb_net dvb_net1; + spinlock_t feedlock1; + int feeding1; + u32 ttbp; + unsigned char *grabbing; + struct saa7146_pgtable pt; + struct tasklet_struct vpe_tasklet; + bool full_ts; + + int fe_synced; + struct mutex pid_mutex; + + int video_blank; + struct video_status videostate; + u16 display_panscan; + int display_ar; + int trickmode; +#define TRICK_NONE 0 +#define TRICK_FAST 1 +#define TRICK_SLOW 2 +#define TRICK_FREEZE 3 + struct audio_status audiostate; + + struct dvb_demux_filter *handle2filter[32]; + struct av7110_p2t p2t_filter[MAXFILT]; + struct dvb_filter_pes2ts p2t[2]; + struct ipack ipack[2]; + u8 *kbuf[2]; + + int sinfo; + int feeding; + + int arm_errors; + int registered; + + + /* AV711X */ + + u32 arm_fw; + u32 arm_rtsl; + u32 arm_vid; + u32 arm_app; + u32 avtype; + int arm_ready; + struct task_struct *arm_thread; + wait_queue_head_t arm_wait; + u16 arm_loops; + + void *debi_virt; + dma_addr_t debi_bus; + + u16 pids[DMX_PES_OTHER]; + + struct dvb_ringbuffer ci_rbuffer; + struct dvb_ringbuffer ci_wbuffer; + + struct audio_mixer mixer; + + struct dvb_adapter dvb_adapter; + struct dvb_device *video_dev; + struct dvb_device *audio_dev; + struct dvb_device *ca_dev; + struct dvb_device *osd_dev; + + struct dvb_video_events video_events; + video_size_t video_size; + + u16 wssMode; + u16 wssData; + + struct infrared ir; + + /* firmware stuff */ + unsigned char *bin_fw; + unsigned long size_fw; + + unsigned char *bin_dpram; + unsigned long size_dpram; + + unsigned char *bin_root; + unsigned long size_root; + + struct dvb_frontend* fe; + fe_status_t fe_status; + + /* crash recovery */ + void (*recover)(struct av7110* av7110); + fe_sec_voltage_t saved_voltage; + fe_sec_tone_mode_t saved_tone; + struct dvb_diseqc_master_cmd saved_master_cmd; + fe_sec_mini_cmd_t saved_minicmd; + + int (*fe_init)(struct dvb_frontend* fe); + int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status); + int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe); + int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); + int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); + int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); + int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); + int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); + int (*fe_set_frontend)(struct dvb_frontend *fe); +}; + + +extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, + u16 subpid, u16 pcrpid); + +extern int av7110_check_ir_config(struct av7110 *av7110, int force); +extern int av7110_ir_init(struct av7110 *av7110); +extern void av7110_ir_exit(struct av7110 *av7110); + +/* msp3400 i2c subaddresses */ +#define MSP_WR_DEM 0x10 +#define MSP_RD_DEM 0x11 +#define MSP_WR_DSP 0x12 +#define MSP_RD_DSP 0x13 + +extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); +extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); +extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); + + +extern int av7110_init_analog_module(struct av7110 *av7110); +extern int av7110_init_v4l(struct av7110 *av7110); +extern int av7110_exit_v4l(struct av7110 *av7110); + +#endif /* _AV7110_H_ */ diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c new file mode 100644 index 000000000000..952b33dbac4f --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -0,0 +1,1626 @@ +/* + * av7110_av.c: audio and video MPEG decoder stuff + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_av.h" +#include "av7110_ipack.h" + +/* MPEG-2 (ISO 13818 / H.222.0) stream types */ +#define PROG_STREAM_MAP 0xBC +#define PRIVATE_STREAM1 0xBD +#define PADDING_STREAM 0xBE +#define PRIVATE_STREAM2 0xBF +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +#define PTS_DTS_FLAGS 0xC0 + +//pts_dts flags +#define PTS_ONLY 0x80 +#define PTS_DTS 0xC0 +#define TS_SIZE 188 +#define TRANS_ERROR 0x80 +#define PAY_START 0x40 +#define TRANS_PRIO 0x20 +#define PID_MASK_HI 0x1F +//flags +#define TRANS_SCRMBL1 0x80 +#define TRANS_SCRMBL2 0x40 +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define COUNT_MASK 0x0F + +// adaptation flags +#define DISCON_IND 0x80 +#define RAND_ACC_IND 0x40 +#define ES_PRI_IND 0x20 +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 +#define SPLICE_FLAG 0x04 +#define TRANS_PRIV 0x02 +#define ADAP_EXT_FLAG 0x01 + +// adaptation extension flags +#define LTW_FLAG 0x80 +#define PIECE_RATE 0x40 +#define SEAM_SPLICE 0x20 + + +static void p_to_t(u8 const *buf, long int length, u16 pid, + u8 *counter, struct dvb_demux_feed *feed); +static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); + + +int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) +{ + struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv; + + if (!(dvbdmxfeed->ts_type & TS_PACKET)) + return 0; + if (buf[3] == 0xe0) // video PES do not have a length in TS + buf[4] = buf[5] = 0; + if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) + return dvbdmxfeed->cb.ts(buf, len, NULL, 0, + &dvbdmxfeed->feed.ts, DMX_OK); + else + return dvb_filter_pes2ts(p2t, buf, len, 1); +} + +static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) +{ + struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; + + dvbdmxfeed->cb.ts(data, 188, NULL, 0, + &dvbdmxfeed->feed.ts, DMX_OK); + return 0; +} + +int av7110_av_start_record(struct av7110 *av7110, int av, + struct dvb_demux_feed *dvbdmxfeed) +{ + int ret = 0; + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + + dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed); + + if (av7110->playing || (av7110->rec_mode & av)) + return -EBUSY; + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + dvbdmx->recording = 1; + av7110->rec_mode |= av; + + switch (av7110->rec_mode) { + case RP_AUDIO: + dvb_filter_pes2ts_init(&av7110->p2t[0], + dvbdmx->pesfilter[0]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[0]); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + break; + + case RP_VIDEO: + dvb_filter_pes2ts_init(&av7110->p2t[1], + dvbdmx->pesfilter[1]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[1]); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + break; + + case RP_AV: + dvb_filter_pes2ts_init(&av7110->p2t[0], + dvbdmx->pesfilter[0]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[0]); + dvb_filter_pes2ts_init(&av7110->p2t[1], + dvbdmx->pesfilter[1]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[1]); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); + break; + } + return ret; +} + +int av7110_av_start_play(struct av7110 *av7110, int av) +{ + int ret = 0; + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->rec_mode) + return -EBUSY; + if (av7110->playing & av) + return -EBUSY; + + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + + if (av7110->playing == RP_NONE) { + av7110_ipack_reset(&av7110->ipack[0]); + av7110_ipack_reset(&av7110->ipack[1]); + } + + av7110->playing |= av; + switch (av7110->playing) { + case RP_AUDIO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + break; + case RP_VIDEO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + av7110->sinfo = 0; + break; + case RP_AV: + av7110->sinfo = 0; + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); + break; + } + return ret; +} + +int av7110_av_stop(struct av7110 *av7110, int av) +{ + int ret = 0; + dprintk(2, "av7110:%p, \n", av7110); + + if (!(av7110->playing & av) && !(av7110->rec_mode & av)) + return 0; + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + if (av7110->playing) { + av7110->playing &= ~av; + switch (av7110->playing) { + case RP_AUDIO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + break; + case RP_VIDEO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + break; + case RP_NONE: + ret = av7110_set_vidmode(av7110, av7110->vidmode); + break; + } + } else { + av7110->rec_mode &= ~av; + switch (av7110->rec_mode) { + case RP_AUDIO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + break; + case RP_VIDEO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + break; + case RP_NONE: + break; + } + } + return ret; +} + + +int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) +{ + int len; + u32 sync; + u16 blen; + + if (!dlen) { + wake_up(&buf->queue); + return -1; + } + while (1) { + len = dvb_ringbuffer_avail(buf); + if (len < 6) { + wake_up(&buf->queue); + return -1; + } + sync = DVB_RINGBUFFER_PEEK(buf, 0) << 24; + sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16; + sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8; + sync |= DVB_RINGBUFFER_PEEK(buf, 3); + + if (((sync &~ 0x0f) == 0x000001e0) || + ((sync &~ 0x1f) == 0x000001c0) || + (sync == 0x000001bd)) + break; + printk("resync\n"); + DVB_RINGBUFFER_SKIP(buf, 1); + } + blen = DVB_RINGBUFFER_PEEK(buf, 4) << 8; + blen |= DVB_RINGBUFFER_PEEK(buf, 5); + blen += 6; + if (len < blen || blen > dlen) { + //printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); + wake_up(&buf->queue); + return -1; + } + + dvb_ringbuffer_read(buf, dest, (size_t) blen); + + dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", + (unsigned long) buf->pread, (unsigned long) buf->pwrite); + wake_up(&buf->queue); + return blen; +} + + +int av7110_set_volume(struct av7110 *av7110, int volleft, int volright) +{ + int err, vol, val, balance = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + av7110->mixer.volume_left = volleft; + av7110->mixer.volume_right = volright; + + switch (av7110->adac_type) { + case DVB_ADAC_TI: + volleft = (volleft * 256) / 1036; + volright = (volright * 256) / 1036; + if (volleft > 0x3f) + volleft = 0x3f; + if (volright > 0x3f) + volright = 0x3f; + if ((err = SendDAC(av7110, 3, 0x80 + volleft))) + return err; + return SendDAC(av7110, 4, volright); + + case DVB_ADAC_CRYSTAL: + volleft = 127 - volleft / 2; + volright = 127 - volright / 2; + i2c_writereg(av7110, 0x20, 0x03, volleft); + i2c_writereg(av7110, 0x20, 0x04, volright); + return 0; + + case DVB_ADAC_MSP34x0: + vol = (volleft > volright) ? volleft : volright; + val = (vol * 0x73 / 255) << 8; + if (vol > 0) + balance = ((volright - volleft) * 127) / vol; + msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ + msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ + return 0; + + case DVB_ADAC_MSP34x5: + vol = (volleft > volright) ? volleft : volright; + val = (vol * 0x73 / 255) << 8; + if (vol > 0) + balance = ((volright - volleft) * 127) / vol; + msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ + return 0; + } + + return 0; +} + +int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) +{ + int ret; + dprintk(2, "av7110:%p, \n", av7110); + + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); + + if (!ret && !av7110->playing) { + ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], + av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_TELETEXT], + 0, av7110->pids[DMX_PES_PCR]); + if (!ret) + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + } + return ret; +} + + +static enum av7110_video_mode sw2mode[16] = { + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, + AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, +}; + +static int get_video_format(struct av7110 *av7110, u8 *buf, int count) +{ + int i; + int hsize, vsize; + int sw; + u8 *p; + int ret = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->sinfo) + return 0; + for (i = 7; i < count - 10; i++) { + p = buf + i; + if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3) + continue; + p += 4; + hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); + vsize = ((p[1] &0x0F) << 8) | (p[2]); + sw = (p[3] & 0x0F); + ret = av7110_set_vidmode(av7110, sw2mode[sw]); + if (!ret) { + dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw); + av7110->sinfo = 1; + } + break; + } + return ret; +} + + +/**************************************************************************** + * I/O buffer management and control + ****************************************************************************/ + +static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, + const u8 *buf, unsigned long count) +{ + unsigned long todo = count; + int free; + + while (todo > 0) { + if (dvb_ringbuffer_free(rbuf) < 2048) { + if (wait_event_interruptible(rbuf->queue, + (dvb_ringbuffer_free(rbuf) >= 2048))) + return count - todo; + } + free = dvb_ringbuffer_free(rbuf); + if (free > todo) + free = todo; + dvb_ringbuffer_write(rbuf, buf, free); + todo -= free; + buf += free; + } + + return count - todo; +} + +static void play_video_cb(u8 *buf, int count, void *priv) +{ + struct av7110 *av7110 = (struct av7110 *) priv; + dprintk(2, "av7110:%p, \n", av7110); + + if ((buf[3] & 0xe0) == 0xe0) { + get_video_format(av7110, buf, count); + aux_ring_buffer_write(&av7110->avout, buf, count); + } else + aux_ring_buffer_write(&av7110->aout, buf, count); +} + +static void play_audio_cb(u8 *buf, int count, void *priv) +{ + struct av7110 *av7110 = (struct av7110 *) priv; + dprintk(2, "av7110:%p, \n", av7110); + + aux_ring_buffer_write(&av7110->aout, buf, count); +} + + +#define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096) + +static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, + unsigned long count, int nonblock, int type) +{ + struct dvb_ringbuffer *rb; + u8 *kb; + unsigned long todo = count; + + dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count); + + rb = (type) ? &av7110->avout : &av7110->aout; + kb = av7110->kbuf[type]; + + if (!kb) + return -ENOBUFS; + + if (nonblock && !FREE_COND_TS) + return -EWOULDBLOCK; + + while (todo >= TS_SIZE) { + if (!FREE_COND_TS) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(rb->queue, FREE_COND_TS)) + return count - todo; + } + if (copy_from_user(kb, buf, TS_SIZE)) + return -EFAULT; + write_ts_to_decoder(av7110, type, kb, TS_SIZE); + todo -= TS_SIZE; + buf += TS_SIZE; + } + + return count - todo; +} + + +#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ + dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) + +static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, + unsigned long count, int nonblock, int type) +{ + unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + + if (nonblock && !FREE_COND) + return -EWOULDBLOCK; + + while (todo > 0) { + if (!FREE_COND) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(av7110->avout.queue, + FREE_COND)) + return count - todo; + } + n = todo; + if (n > IPACKS * 2) + n = IPACKS * 2; + if (copy_from_user(av7110->kbuf[type], buf, n)) + return -EFAULT; + av7110_ipack_instant_repack(av7110->kbuf[type], n, + &av7110->ipack[type]); + todo -= n; + buf += n; + } + return count - todo; +} + +static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, + unsigned long count, int nonblock, int type) +{ + unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + + if (nonblock && !FREE_COND) + return -EWOULDBLOCK; + + while (todo > 0) { + if (!FREE_COND) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(av7110->avout.queue, + FREE_COND)) + return count - todo; + } + n = todo; + if (n > IPACKS * 2) + n = IPACKS * 2; + av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]); + todo -= n; + buf += n; + } + return count - todo; +} + +static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, + unsigned long count, int nonblock, int type) +{ + unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) + return -EWOULDBLOCK; + + while (todo > 0) { + if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(av7110->aout.queue, + (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) + return count-todo; + } + n = todo; + if (n > IPACKS * 2) + n = IPACKS * 2; + if (copy_from_user(av7110->kbuf[type], buf, n)) + return -EFAULT; + av7110_ipack_instant_repack(av7110->kbuf[type], n, + &av7110->ipack[type]); + todo -= n; + buf += n; + } + return count - todo; +} + +void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed) +{ + memset(p->pes, 0, TS_SIZE); + p->counter = 0; + p->pos = 0; + p->frags = 0; + if (feed) + p->feed = feed; +} + +static void clear_p2t(struct av7110_p2t *p) +{ + memset(p->pes, 0, TS_SIZE); +// p->counter = 0; + p->pos = 0; + p->frags = 0; +} + + +static int find_pes_header(u8 const *buf, long int length, int *frags) +{ + int c = 0; + int found = 0; + + *frags = 0; + + while (c < length - 3 && !found) { + if (buf[c] == 0x00 && buf[c + 1] == 0x00 && + buf[c + 2] == 0x01) { + switch ( buf[c + 3] ) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + found = 1; + break; + + default: + c++; + break; + } + } else + c++; + } + if (c == length - 3 && !found) { + if (buf[length - 1] == 0x00) + *frags = 1; + if (buf[length - 2] == 0x00 && + buf[length - 1] == 0x00) + *frags = 2; + if (buf[length - 3] == 0x00 && + buf[length - 2] == 0x00 && + buf[length - 1] == 0x01) + *frags = 3; + return -1; + } + + return c; +} + +void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) +{ + int c, c2, l, add; + int check, rest; + + c = 0; + c2 = 0; + if (p->frags){ + check = 0; + switch(p->frags) { + case 1: + if (buf[c] == 0x00 && buf[c + 1] == 0x01) { + check = 1; + c += 2; + } + break; + case 2: + if (buf[c] == 0x01) { + check = 1; + c++; + } + break; + case 3: + check = 1; + } + if (check) { + switch (buf[c]) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + p->pes[0] = 0x00; + p->pes[1] = 0x00; + p->pes[2] = 0x01; + p->pes[3] = buf[c]; + p->pos = 4; + memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos); + c += (TS_SIZE - 4) - p->pos; + p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed); + clear_p2t(p); + break; + + default: + c = 0; + break; + } + } + p->frags = 0; + } + + if (p->pos) { + c2 = find_pes_header(buf + c, length - c, &p->frags); + if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos) + l = c2+c; + else + l = (TS_SIZE - 4) - p->pos; + memcpy(p->pes + p->pos, buf, l); + c += l; + p->pos += l; + p_to_t(p->pes, p->pos, pid, &p->counter, p->feed); + clear_p2t(p); + } + + add = 0; + while (c < length) { + c2 = find_pes_header(buf + c + add, length - c - add, &p->frags); + if (c2 >= 0) { + c2 += c + add; + if (c2 > c){ + p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed); + c = c2; + clear_p2t(p); + add = 0; + } else + add = 1; + } else { + l = length - c; + rest = l % (TS_SIZE - 4); + l -= rest; + p_to_t(buf + c, l, pid, &p->counter, p->feed); + memcpy(p->pes, buf + c + l, rest); + p->pos = rest; + c = length; + } + } +} + + +static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) +{ + int i; + int c = 0; + int fill; + u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 }; + + fill = (TS_SIZE - 4) - length; + if (pes_start) + tshead[1] = 0x40; + if (fill) + tshead[3] = 0x30; + tshead[1] |= (u8)((pid & 0x1F00) >> 8); + tshead[2] |= (u8)(pid & 0x00FF); + tshead[3] |= ((*counter)++ & 0x0F); + memcpy(buf, tshead, 4); + c += 4; + + if (fill) { + buf[4] = fill - 1; + c++; + if (fill > 1) { + buf[5] = 0x00; + c++; + } + for (i = 6; i < fill + 4; i++) { + buf[i] = 0xFF; + c++; + } + } + + return c; +} + + +static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, + struct dvb_demux_feed *feed) +{ + int l, pes_start; + u8 obuf[TS_SIZE]; + long c = 0; + + pes_start = 0; + if (length > 3 && + buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) + switch (buf[3]) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + pes_start = 1; + break; + + default: + break; + } + + while (c < length) { + memset(obuf, 0, TS_SIZE); + if (length - c >= (TS_SIZE - 4)){ + l = write_ts_header2(pid, counter, pes_start, + obuf, (TS_SIZE - 4)); + memcpy(obuf + l, buf + c, TS_SIZE - l); + c += TS_SIZE - l; + } else { + l = write_ts_header2(pid, counter, pes_start, + obuf, length - c); + memcpy(obuf + l, buf + c, TS_SIZE - l); + c = length; + } + feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, DMX_OK); + pes_start = 0; + } +} + + +static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len) +{ + struct ipack *ipack = &av7110->ipack[type]; + + if (buf[1] & TRANS_ERROR) { + av7110_ipack_reset(ipack); + return -1; + } + + if (!(buf[3] & PAYLOAD)) + return -1; + + if (buf[1] & PAY_START) + av7110_ipack_flush(ipack); + + if (buf[3] & ADAPT_FIELD) { + len -= buf[4] + 1; + buf += buf[4] + 1; + if (!len) + return 0; + } + + av7110_ipack_instant_repack(buf + 4, len - 4, ipack); + return 0; +} + + +int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *av7110 = (struct av7110 *) demux->priv; + + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) + return 0; + + switch (feed->pes_type) { + case 0: + if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) + return -EINVAL; + break; + case 1: + if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) + return -EINVAL; + break; + default: + return -1; + } + + return write_ts_to_decoder(av7110, feed->pes_type, buf, len); +} + + + +/****************************************************************************** + * Video MPEG decoder events + ******************************************************************************/ +void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) +{ + struct dvb_video_events *events = &av7110->video_events; + int wp; + + spin_lock_bh(&events->lock); + + wp = (events->eventw + 1) % MAX_VIDEO_EVENT; + if (wp == events->eventr) { + events->overflow = 1; + events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; + } + + //FIXME: timestamp? + memcpy(&events->events[events->eventw], event, sizeof(struct video_event)); + events->eventw = wp; + + spin_unlock_bh(&events->lock); + + wake_up_interruptible(&events->wait_queue); +} + + +static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) +{ + struct dvb_video_events *events = &av7110->video_events; + + if (events->overflow) { + events->overflow = 0; + return -EOVERFLOW; + } + if (events->eventw == events->eventr) { + int ret; + + if (flags & O_NONBLOCK) + return -EWOULDBLOCK; + + ret = wait_event_interruptible(events->wait_queue, + events->eventw != events->eventr); + if (ret < 0) + return ret; + } + + spin_lock_bh(&events->lock); + + memcpy(event, &events->events[events->eventr], + sizeof(struct video_event)); + events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; + + spin_unlock_bh(&events->lock); + + return 0; +} + + +/****************************************************************************** + * DVB device file operations + ******************************************************************************/ + +static unsigned int dvb_video_poll(struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned int mask = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) + poll_wait(file, &av7110->avout.queue, wait); + + poll_wait(file, &av7110->video_events.wait_queue, wait); + + if (av7110->video_events.eventw != av7110->video_events.eventr) + mask = POLLPRI; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (av7110->playing) { + if (FREE_COND) + mask |= (POLLOUT | POLLWRNORM); + } else /* if not playing: may play if asked for */ + mask |= (POLLOUT | POLLWRNORM); + } + + return mask; +} + +static ssize_t dvb_video_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned char c; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; + + if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY) + return -EPERM; + + if (get_user(c, buf)) + return -EFAULT; + if (c == 0x47 && count % TS_SIZE == 0) + return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); + else + return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); +} + +static unsigned int dvb_audio_poll(struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned int mask = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + poll_wait(file, &av7110->aout.queue, wait); + + if (av7110->playing) { + if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) + mask |= (POLLOUT | POLLWRNORM); + } else /* if not playing: may play if asked for */ + mask = (POLLOUT | POLLWRNORM); + + return mask; +} + +static ssize_t dvb_audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned char c; + + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) { + printk(KERN_ERR "not audio source memory\n"); + return -EPERM; + } + + if (get_user(c, buf)) + return -EFAULT; + if (c == 0x47 && count % TS_SIZE == 0) + return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); + else + return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); +} + +static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; + +#define MIN_IFRAME 400000 + +static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) +{ + unsigned i, n; + int progressive = 0; + int match = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + if (!(av7110->playing & RP_VIDEO)) { + if (av7110_av_start_play(av7110, RP_VIDEO) < 0) + return -EBUSY; + } + + /* search in buf for instances of 00 00 01 b5 1? */ + for (i = 0; i < len; i++) { + unsigned char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (match == 5) { + progressive = c & 0x08; + match = 0; + } + if (c == 0x00) { + match = (match == 1 || match == 2) ? 2 : 1; + continue; + } + switch (match++) { + case 2: if (c == 0x01) + continue; + break; + case 3: if (c == 0xb5) + continue; + break; + case 4: if ((c & 0xf0) == 0x10) + continue; + break; + } + match = 0; + } + + /* setting n always > 1, fixes problems when playing stillframes + consisting of I- and P-Frames */ + n = MIN_IFRAME / len + 1; + + /* FIXME: nonblock? */ + dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1); + + for (i = 0; i < n; i++) + dvb_play(av7110, buf, len, 0, 1); + + av7110_ipack_flush(&av7110->ipack[1]); + + if (progressive) + return vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); + else + return 0; +} + + +static int dvb_video_ioctl(struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + int ret = 0; + + dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && + cmd != VIDEO_GET_SIZE ) { + return -EPERM; + } + } + + switch (cmd) { + case VIDEO_STOP: + av7110->videostate.play_state = VIDEO_STOPPED; + if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) + ret = av7110_av_stop(av7110, RP_VIDEO); + else + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, + av7110->videostate.video_blank ? 0 : 1); + if (!ret) + av7110->trickmode = TRICK_NONE; + break; + + case VIDEO_PLAY: + av7110->trickmode = TRICK_NONE; + if (av7110->videostate.play_state == VIDEO_FREEZED) { + av7110->videostate.play_state = VIDEO_PLAYING; + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (ret) + break; + } + if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { + if (av7110->playing == RP_AV) { + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + if (ret) + break; + av7110->playing &= ~RP_VIDEO; + } + ret = av7110_av_start_play(av7110, RP_VIDEO); + } + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (!ret) + av7110->videostate.play_state = VIDEO_PLAYING; + break; + + case VIDEO_FREEZE: + av7110->videostate.play_state = VIDEO_FREEZED; + if (av7110->playing & RP_VIDEO) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); + else + ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); + if (!ret) + av7110->trickmode = TRICK_FREEZE; + break; + + case VIDEO_CONTINUE: + if (av7110->playing & RP_VIDEO) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (!ret) { + av7110->videostate.play_state = VIDEO_PLAYING; + av7110->trickmode = TRICK_NONE; + } + break; + + case VIDEO_SELECT_SOURCE: + av7110->videostate.stream_source = (video_stream_source_t) arg; + break; + + case VIDEO_SET_BLANK: + av7110->videostate.video_blank = (int) arg; + break; + + case VIDEO_GET_STATUS: + memcpy(parg, &av7110->videostate, sizeof(struct video_status)); + break; + + case VIDEO_GET_EVENT: + ret = dvb_video_get_event(av7110, parg, file->f_flags); + break; + + case VIDEO_GET_SIZE: + memcpy(parg, &av7110->video_size, sizeof(video_size_t)); + break; + + case VIDEO_SET_DISPLAY_FORMAT: + { + video_displayformat_t format = (video_displayformat_t) arg; + switch (format) { + case VIDEO_PAN_SCAN: + av7110->display_panscan = VID_PAN_SCAN_PREF; + break; + case VIDEO_LETTER_BOX: + av7110->display_panscan = VID_VC_AND_PS_PREF; + break; + case VIDEO_CENTER_CUT_OUT: + av7110->display_panscan = VID_CENTRE_CUT_PREF; + break; + default: + ret = -EINVAL; + } + if (ret < 0) + break; + av7110->videostate.display_format = format; + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, + 1, av7110->display_panscan); + break; + } + + case VIDEO_SET_FORMAT: + if (arg > 1) { + ret = -EINVAL; + break; + } + av7110->display_ar = arg; + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, + 1, (u16) arg); + break; + + case VIDEO_STILLPICTURE: + { + struct video_still_picture *pic = + (struct video_still_picture *) parg; + av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + ret = play_iframe(av7110, pic->iFrame, pic->size, + file->f_flags & O_NONBLOCK); + break; + } + + case VIDEO_FAST_FORWARD: + //note: arg is ignored by firmware + if (av7110->playing & RP_VIDEO) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Scan_I, 2, AV_PES, 0); + else + ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); + if (!ret) { + av7110->trickmode = TRICK_FAST; + av7110->videostate.play_state = VIDEO_PLAYING; + } + break; + + case VIDEO_SLOWMOTION: + if (av7110->playing&RP_VIDEO) { + if (av7110->trickmode != TRICK_SLOW) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); + } else { + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); + } + if (!ret) { + av7110->trickmode = TRICK_SLOW; + av7110->videostate.play_state = VIDEO_PLAYING; + } + break; + + case VIDEO_GET_CAPABILITIES: + *(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 | + VIDEO_CAP_SYS | VIDEO_CAP_PROG; + break; + + case VIDEO_CLEAR_BUFFER: + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + av7110_ipack_reset(&av7110->ipack[1]); + if (av7110->playing == RP_AV) { + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Play, 2, AV_PES, 0); + if (ret) + break; + if (av7110->trickmode == TRICK_FAST) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Scan_I, 2, AV_PES, 0); + if (av7110->trickmode == TRICK_SLOW) { + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Slow, 2, 0, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); + } + if (av7110->trickmode == TRICK_FREEZE) + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); + } + break; + + case VIDEO_SET_STREAMTYPE: + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +static int dvb_audio_ioctl(struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + int ret = 0; + + dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); + + if (((file->f_flags & O_ACCMODE) == O_RDONLY) && + (cmd != AUDIO_GET_STATUS)) + return -EPERM; + + switch (cmd) { + case AUDIO_STOP: + if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) + ret = av7110_av_stop(av7110, RP_AUDIO); + else + ret = audcom(av7110, AUDIO_CMD_MUTE); + if (!ret) + av7110->audiostate.play_state = AUDIO_STOPPED; + break; + + case AUDIO_PLAY: + if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) + ret = av7110_av_start_play(av7110, RP_AUDIO); + if (!ret) + ret = audcom(av7110, AUDIO_CMD_UNMUTE); + if (!ret) + av7110->audiostate.play_state = AUDIO_PLAYING; + break; + + case AUDIO_PAUSE: + ret = audcom(av7110, AUDIO_CMD_MUTE); + if (!ret) + av7110->audiostate.play_state = AUDIO_PAUSED; + break; + + case AUDIO_CONTINUE: + if (av7110->audiostate.play_state == AUDIO_PAUSED) { + av7110->audiostate.play_state = AUDIO_PLAYING; + ret = audcom(av7110, AUDIO_CMD_UNMUTE | AUDIO_CMD_PCM16); + } + break; + + case AUDIO_SELECT_SOURCE: + av7110->audiostate.stream_source = (audio_stream_source_t) arg; + break; + + case AUDIO_SET_MUTE: + { + ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE); + if (!ret) + av7110->audiostate.mute_state = (int) arg; + break; + } + + case AUDIO_SET_AV_SYNC: + av7110->audiostate.AV_sync_state = (int) arg; + ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF); + break; + + case AUDIO_SET_BYPASS_MODE: + if (FW_VERSION(av7110->arm_app) < 0x2621) + ret = -EINVAL; + av7110->audiostate.bypass_mode = (int)arg; + break; + + case AUDIO_CHANNEL_SELECT: + av7110->audiostate.channel_select = (audio_channel_select_t) arg; + switch(av7110->audiostate.channel_select) { + case AUDIO_STEREO: + ret = audcom(av7110, AUDIO_CMD_STEREO); + if (!ret) { + if (av7110->adac_type == DVB_ADAC_CRYSTAL) + i2c_writereg(av7110, 0x20, 0x02, 0x49); + else if (av7110->adac_type == DVB_ADAC_MSP34x5) + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); + } + break; + case AUDIO_MONO_LEFT: + ret = audcom(av7110, AUDIO_CMD_MONO_L); + if (!ret) { + if (av7110->adac_type == DVB_ADAC_CRYSTAL) + i2c_writereg(av7110, 0x20, 0x02, 0x4a); + else if (av7110->adac_type == DVB_ADAC_MSP34x5) + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200); + } + break; + case AUDIO_MONO_RIGHT: + ret = audcom(av7110, AUDIO_CMD_MONO_R); + if (!ret) { + if (av7110->adac_type == DVB_ADAC_CRYSTAL) + i2c_writereg(av7110, 0x20, 0x02, 0x45); + else if (av7110->adac_type == DVB_ADAC_MSP34x5) + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210); + } + break; + default: + ret = -EINVAL; + break; + } + break; + + case AUDIO_GET_STATUS: + memcpy(parg, &av7110->audiostate, sizeof(struct audio_status)); + break; + + case AUDIO_GET_CAPABILITIES: + if (FW_VERSION(av7110->arm_app) < 0x2621) + *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2; + else + *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_DTS | AUDIO_CAP_AC3 | + AUDIO_CAP_MP1 | AUDIO_CAP_MP2; + break; + + case AUDIO_CLEAR_BUFFER: + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + av7110_ipack_reset(&av7110->ipack[0]); + if (av7110->playing == RP_AV) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Play, 2, AV_PES, 0); + break; + + case AUDIO_SET_ID: + break; + + case AUDIO_SET_MIXER: + { + struct audio_mixer *amix = (struct audio_mixer *)parg; + ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right); + break; + } + + case AUDIO_SET_STREAMTYPE: + break; + + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} + + +static int dvb_video_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + int err; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((err = dvb_generic_open(inode, file)) < 0) + return err; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + av7110->video_blank = 1; + av7110->audiostate.AV_sync_state = 1; + av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; + + /* empty event queue */ + av7110->video_events.eventr = av7110->video_events.eventw = 0; + } + + return 0; +} + +static int dvb_video_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + av7110_av_stop(av7110, RP_VIDEO); + } + + return dvb_generic_release(inode, file); +} + +static int dvb_audio_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + int err = dvb_generic_open(inode, file); + + dprintk(2, "av7110:%p, \n", av7110); + + if (err < 0) + return err; + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; + return 0; +} + +static int dvb_audio_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(2, "av7110:%p, \n", av7110); + + av7110_av_stop(av7110, RP_AUDIO); + return dvb_generic_release(inode, file); +} + + + +/****************************************************************************** + * driver registration + ******************************************************************************/ + +static const struct file_operations dvb_video_fops = { + .owner = THIS_MODULE, + .write = dvb_video_write, + .unlocked_ioctl = dvb_generic_ioctl, + .open = dvb_video_open, + .release = dvb_video_release, + .poll = dvb_video_poll, + .llseek = noop_llseek, +}; + +static struct dvb_device dvbdev_video = { + .priv = NULL, + .users = 6, + .readers = 5, /* arbitrary */ + .writers = 1, + .fops = &dvb_video_fops, + .kernel_ioctl = dvb_video_ioctl, +}; + +static const struct file_operations dvb_audio_fops = { + .owner = THIS_MODULE, + .write = dvb_audio_write, + .unlocked_ioctl = dvb_generic_ioctl, + .open = dvb_audio_open, + .release = dvb_audio_release, + .poll = dvb_audio_poll, + .llseek = noop_llseek, +}; + +static struct dvb_device dvbdev_audio = { + .priv = NULL, + .users = 1, + .writers = 1, + .fops = &dvb_audio_fops, + .kernel_ioctl = dvb_audio_ioctl, +}; + + +int av7110_av_register(struct av7110 *av7110) +{ + av7110->audiostate.AV_sync_state = 0; + av7110->audiostate.mute_state = 0; + av7110->audiostate.play_state = AUDIO_STOPPED; + av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; + av7110->audiostate.channel_select = AUDIO_STEREO; + av7110->audiostate.bypass_mode = 0; + + av7110->videostate.video_blank = 0; + av7110->videostate.play_state = VIDEO_STOPPED; + av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; + av7110->videostate.video_format = VIDEO_FORMAT_4_3; + av7110->videostate.display_format = VIDEO_LETTER_BOX; + av7110->display_ar = VIDEO_FORMAT_4_3; + av7110->display_panscan = VID_VC_AND_PS_PREF; + + init_waitqueue_head(&av7110->video_events.wait_queue); + spin_lock_init(&av7110->video_events.lock); + av7110->video_events.eventw = av7110->video_events.eventr = 0; + av7110->video_events.overflow = 0; + memset(&av7110->video_size, 0, sizeof (video_size_t)); + + dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, + &dvbdev_video, av7110, DVB_DEVICE_VIDEO); + + dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, + &dvbdev_audio, av7110, DVB_DEVICE_AUDIO); + + return 0; +} + +void av7110_av_unregister(struct av7110 *av7110) +{ + dvb_unregister_device(av7110->audio_dev); + dvb_unregister_device(av7110->video_dev); +} + +int av7110_av_init(struct av7110 *av7110) +{ + void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; + int i, ret; + + for (i = 0; i < 2; i++) { + struct ipack *ipack = av7110->ipack + i; + + ret = av7110_ipack_init(ipack, IPACKS, play[i]); + if (ret < 0) { + if (i) + av7110_ipack_free(--ipack); + goto out; + } + ipack->data = av7110; + } + + dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN); + dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN); + + av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN); + av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS; +out: + return ret; +} + +void av7110_av_exit(struct av7110 *av7110) +{ + av7110_ipack_free(&av7110->ipack[0]); + av7110_ipack_free(&av7110->ipack[1]); +} diff --git a/drivers/media/pci/ttpci/av7110_av.h b/drivers/media/pci/ttpci/av7110_av.h new file mode 100644 index 000000000000..5f02ef85e47d --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_av.h @@ -0,0 +1,30 @@ +#ifndef _AV7110_AV_H_ +#define _AV7110_AV_H_ + +struct av7110; + +extern int av7110_set_vidmode(struct av7110 *av7110, + enum av7110_video_mode mode); + +extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); +extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); +extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); + +extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright); +extern int av7110_av_stop(struct av7110 *av7110, int av); +extern int av7110_av_start_record(struct av7110 *av7110, int av, + struct dvb_demux_feed *dvbdmxfeed); +extern int av7110_av_start_play(struct av7110 *av7110, int av); + +extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); + +extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); +extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p); + +extern int av7110_av_register(struct av7110 *av7110); +extern void av7110_av_unregister(struct av7110 *av7110); +extern int av7110_av_init(struct av7110 *av7110); +extern void av7110_av_exit(struct av7110 *av7110); + + +#endif /* _AV7110_AV_H_ */ diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c new file mode 100644 index 000000000000..9fc1dd0ba4c3 --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_ca.c @@ -0,0 +1,387 @@ +/* + * av7110_ca.c: CA and CI stuff + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_ca.h" + + +void CI_handle(struct av7110 *av7110, u8 *data, u16 len) +{ + dprintk(8, "av7110:%p\n",av7110); + + if (len < 3) + return; + switch (data[0]) { + case CI_MSG_CI_INFO: + if (data[2] != 1 && data[2] != 2) + break; + switch (data[1]) { + case 0: + av7110->ci_slot[data[2] - 1].flags = 0; + break; + case 1: + av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT; + break; + case 2: + av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY; + break; + } + break; + case CI_SWITCH_PRG_REPLY: + //av7110->ci_stat=data[1]; + break; + default: + break; + } +} + + +void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) +{ + if (dvb_ringbuffer_free(cibuf) < len + 2) + return; + + DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8); + DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff); + dvb_ringbuffer_write(cibuf, data, len); + wake_up_interruptible(&cibuf->queue); +} + + +/****************************************************************************** + * CI link layer file ops + ******************************************************************************/ + +static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) +{ + struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p; + void *data; + + for (p = tab; *p; p++) { + data = vmalloc(size); + if (!data) { + while (p-- != tab) { + vfree(p[0]->data); + p[0]->data = NULL; + } + return -ENOMEM; + } + dvb_ringbuffer_init(*p, data, size); + } + return 0; +} + +static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +{ + dvb_ringbuffer_flush_spinlock_wakeup(cirbuf); + dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf); +} + +static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +{ + vfree(cirbuf->data); + cirbuf->data = NULL; + vfree(ciwbuf->data); + ciwbuf->data = NULL; +} + +static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, + int slots, ca_slot_info_t *slot) +{ + int i; + int len = 0; + u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 }; + + for (i = 0; i < 2; i++) { + if (slots & (1 << i)) + len += 8; + } + + if (dvb_ringbuffer_free(cibuf) < len) + return -EBUSY; + + for (i = 0; i < 2; i++) { + if (slots & (1 << i)) { + msg[2] = i; + dvb_ringbuffer_write(cibuf, msg, 8); + slot[i].flags = 0; + } + } + + return 0; +} + +static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + int free; + int non_blocking = file->f_flags & O_NONBLOCK; + u8 *page = (u8 *)__get_free_page(GFP_USER); + int res; + + if (!page) + return -ENOMEM; + + res = -EINVAL; + if (count > 2048) + goto out; + + res = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + free = dvb_ringbuffer_free(cibuf); + if (count + 2 > free) { + res = -EWOULDBLOCK; + if (non_blocking) + goto out; + res = -ERESTARTSYS; + if (wait_event_interruptible(cibuf->queue, + (dvb_ringbuffer_free(cibuf) >= count + 2))) + goto out; + } + + DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8); + DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff); + + res = dvb_ringbuffer_write(cibuf, page, count); +out: + free_page((unsigned long)page); + return res; +} + +static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + int avail; + int non_blocking = file->f_flags & O_NONBLOCK; + ssize_t len; + + if (!cibuf->data || !count) + return 0; + if (non_blocking && (dvb_ringbuffer_empty(cibuf))) + return -EWOULDBLOCK; + if (wait_event_interruptible(cibuf->queue, + !dvb_ringbuffer_empty(cibuf))) + return -ERESTARTSYS; + avail = dvb_ringbuffer_avail(cibuf); + if (avail < 4) + return 0; + len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; + len |= DVB_RINGBUFFER_PEEK(cibuf, 1); + if (avail < len + 2 || count < len) + return -EINVAL; + DVB_RINGBUFFER_SKIP(cibuf, 2); + + return dvb_ringbuffer_read_user(cibuf, buf, len); +} + +static int dvb_ca_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + int err = dvb_generic_open(inode, file); + + dprintk(8, "av7110:%p\n",av7110); + + if (err < 0) + return err; + ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer); + return 0; +} + +static unsigned int dvb_ca_poll (struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; + struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; + unsigned int mask = 0; + + dprintk(8, "av7110:%p\n",av7110); + + poll_wait(file, &rbuf->queue, wait); + poll_wait(file, &wbuf->queue, wait); + + if (!dvb_ringbuffer_empty(rbuf)) + mask |= (POLLIN | POLLRDNORM); + + if (dvb_ringbuffer_free(wbuf) > 1024) + mask |= (POLLOUT | POLLWRNORM); + + return mask; +} + +static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + dprintk(8, "av7110:%p\n",av7110); + + switch (cmd) { + case CA_RESET: + return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]); + break; + case CA_GET_CAP: + { + ca_caps_t cap; + + cap.slot_num = 2; + cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI) | CA_DESCR; + cap.descr_num = 16; + cap.descr_type = CA_ECD; + memcpy(parg, &cap, sizeof(cap)); + break; + } + + case CA_GET_SLOT_INFO: + { + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num < 0 || info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + break; + } + + case CA_GET_MSG: + break; + + case CA_SEND_MSG: + break; + + case CA_GET_DESCR_INFO: + { + ca_descr_info_t info; + + info.num = 16; + info.type = CA_ECD; + memcpy(parg, &info, sizeof (info)); + break; + } + + case CA_SET_DESCR: + { + ca_descr_t *descr = (ca_descr_t*) parg; + + if (descr->index >= 16) + return -EINVAL; + if (descr->parity > 1) + return -EINVAL; + av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, + (descr->index<<8)|descr->parity, + (descr->cw[0]<<8)|descr->cw[1], + (descr->cw[2]<<8)|descr->cw[3], + (descr->cw[4]<<8)|descr->cw[5], + (descr->cw[6]<<8)|descr->cw[7]); + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static ssize_t dvb_ca_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(8, "av7110:%p\n",av7110); + return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); +} + +static ssize_t dvb_ca_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(8, "av7110:%p\n",av7110); + return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); +} + +static const struct file_operations dvb_ca_fops = { + .owner = THIS_MODULE, + .read = dvb_ca_read, + .write = dvb_ca_write, + .unlocked_ioctl = dvb_generic_ioctl, + .open = dvb_ca_open, + .release = dvb_generic_release, + .poll = dvb_ca_poll, + .llseek = default_llseek, +}; + +static struct dvb_device dvbdev_ca = { + .priv = NULL, + .users = 1, + .writers = 1, + .fops = &dvb_ca_fops, + .kernel_ioctl = dvb_ca_ioctl, +}; + + +int av7110_ca_register(struct av7110 *av7110) +{ + return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, + &dvbdev_ca, av7110, DVB_DEVICE_CA); +} + +void av7110_ca_unregister(struct av7110 *av7110) +{ + dvb_unregister_device(av7110->ca_dev); +} + +int av7110_ca_init(struct av7110* av7110) +{ + return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192); +} + +void av7110_ca_exit(struct av7110* av7110) +{ + ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer); +} diff --git a/drivers/media/pci/ttpci/av7110_ca.h b/drivers/media/pci/ttpci/av7110_ca.h new file mode 100644 index 000000000000..70ee855ece1b --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_ca.h @@ -0,0 +1,14 @@ +#ifndef _AV7110_CA_H_ +#define _AV7110_CA_H_ + +struct av7110; + +extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len); +extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); + +extern int av7110_ca_register(struct av7110 *av7110); +extern void av7110_ca_unregister(struct av7110 *av7110); +extern int av7110_ca_init(struct av7110* av7110); +extern void av7110_ca_exit(struct av7110* av7110); + +#endif /* _AV7110_CA_H_ */ diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c new file mode 100644 index 000000000000..f1cbfe526989 --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_hw.c @@ -0,0 +1,1208 @@ +/* + * av7110_hw.c: av7110 low level hardware access and firmware interface + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * the project's page is at http://www.linuxtv.org/ + */ + +/* for debugging ARM communication: */ +//#define COM_DEBUG + +#include +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" + +#define _NOHANDSHAKE + +/**************************************************************************** + * DEBI functions + ****************************************************************************/ + +/* This DEBI code is based on the Stradis driver + by Nathan Laredo */ + +int av7110_debiwrite(struct av7110 *av7110, u32 config, + int addr, u32 val, int count) +{ + struct saa7146_dev *dev = av7110->dev; + + if (count <= 0 || count > 32764) { + printk("%s: invalid count %d\n", __func__, count); + return -1; + } + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done failed\n", __func__); + return -1; + } + saa7146_write(dev, DEBI_CONFIG, config); + if (count <= 4) /* immediate transfer */ + saa7146_write(dev, DEBI_AD, val); + else /* block transfer */ + saa7146_write(dev, DEBI_AD, av7110->debi_bus); + saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff)); + saa7146_write(dev, MC2, (2 << 16) | 2); + return 0; +} + +u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count) +{ + struct saa7146_dev *dev = av7110->dev; + u32 result = 0; + + if (count > 32764 || count <= 0) { + printk("%s: invalid count %d\n", __func__, count); + return 0; + } + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done #1 failed\n", __func__); + return 0; + } + saa7146_write(dev, DEBI_AD, av7110->debi_bus); + saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); + + saa7146_write(dev, DEBI_CONFIG, config); + saa7146_write(dev, MC2, (2 << 16) | 2); + if (count > 4) + return count; + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done #2 failed\n", __func__); + return 0; + } + + result = saa7146_read(dev, DEBI_AD); + result &= (0xffffffffUL >> ((4 - count) * 8)); + return result; +} + + + +/* av7110 ARM core boot stuff */ +#if 0 +void av7110_reset_arm(struct av7110 *av7110) +{ + saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); + + /* Disable DEBI and GPIO irq */ + SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + + saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); + msleep(30); /* the firmware needs some time to initialize */ + + ARM_ResetMailBox(av7110); + + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); + + av7110->arm_ready = 1; + dprintk(1, "reset ARM\n"); +} +#endif /* 0 */ + +static int waitdebi(struct av7110 *av7110, int adr, int state) +{ + int k; + + dprintk(4, "%p\n", av7110); + + for (k = 0; k < 100; k++) { + if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state) + return 0; + udelay(5); + } + return -ETIMEDOUT; +} + +static int load_dram(struct av7110 *av7110, u32 *data, int len) +{ + int i; + int blocks, rest; + u32 base, bootblock = AV7110_BOOT_BLOCK; + + dprintk(4, "%p\n", av7110); + + blocks = len / AV7110_BOOT_MAX_SIZE; + rest = len % AV7110_BOOT_MAX_SIZE; + base = DRAM_START_CODE; + + for (i = 0; i < blocks; i++) { + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i); + return -ETIMEDOUT; + } + dprintk(4, "writing DRAM block %d\n", i); + mwdebi(av7110, DEBISWAB, bootblock, + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); + bootblock ^= 0x1400; + iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + base += AV7110_BOOT_MAX_SIZE; + } + + if (rest > 0) { + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n"); + return -ETIMEDOUT; + } + if (rest > 4) + mwdebi(av7110, DEBISWAB, bootblock, + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest); + else + mwdebi(av7110, DEBISWAB, bootblock, + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); + + iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + } + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n"); + return -ETIMEDOUT; + } + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n"); + return -ETIMEDOUT; + } + return 0; +} + + +/* we cannot write av7110 DRAM directly, so load a bootloader into + * the DPRAM which implements a simple boot protocol */ +int av7110_bootarm(struct av7110 *av7110) +{ + const struct firmware *fw; + const char *fw_name = "av7110/bootcode.bin"; + struct saa7146_dev *dev = av7110->dev; + u32 ret; + int i; + + dprintk(4, "%p\n", av7110); + + av7110->arm_ready = 0; + + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); + + /* Disable DEBI and GPIO irq */ + SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + + /* enable DEBI */ + saa7146_write(av7110->dev, MC1, 0x08800880); + saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); + saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + /* test DEBI */ + iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); + /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */ + iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); + + if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) { + printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: " + "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", + ret, 0x10325476); + return -1; + } + for (i = 0; i < 8192; i += 4) + iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4); + dprintk(2, "debi test OK\n"); + + /* boot */ + dprintk(1, "load boot code\n"); + saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO); + //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT); + //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT); + + ret = request_firmware(&fw, fw_name, &dev->pci->dev); + if (ret) { + printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", + fw_name); + return ret; + } + + mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size); + release_firmware(fw); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + + if (saa7146_wait_for_debi_done(av7110->dev, 1)) { + printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " + "saa7146_wait_for_debi_done() timed out\n"); + return -ETIMEDOUT; + } + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); + mdelay(1); + + dprintk(1, "load dram code\n"); + if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) { + printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " + "load_dram() failed\n"); + return -1; + } + + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); + mdelay(1); + + dprintk(1, "load dpram code\n"); + mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); + + if (saa7146_wait_for_debi_done(av7110->dev, 1)) { + printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " + "saa7146_wait_for_debi_done() timed out after loading DRAM\n"); + return -ETIMEDOUT; + } + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); + msleep(30); /* the firmware needs some time to initialize */ + + //ARM_ClearIrq(av7110); + ARM_ResetMailBox(av7110); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); + + av7110->arm_errors = 0; + av7110->arm_ready = 1; + return 0; +} +MODULE_FIRMWARE("av7110/bootcode.bin"); + +/**************************************************************************** + * DEBI command polling + ****************************************************************************/ + +int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) +{ + unsigned long start; + u32 stat; + int err; + + if (FW_VERSION(av7110->arm_app) <= 0x261c) { + /* not supported by old firmware */ + msleep(50); + return 0; + } + + /* new firmware */ + start = jiffies; + for (;;) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + mutex_unlock(&av7110->dcomlock); + if ((stat & flags) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", + __func__, stat & flags); + return -ETIMEDOUT; + } + msleep(1); + } + return 0; +} + +static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +{ + int i; + unsigned long start; + char *type = NULL; + u16 flags[2] = {0, 0}; + u32 stat; + int err; + +// dprintk(4, "%p\n", av7110); + + if (!av7110->arm_ready) { + dprintk(1, "arm not ready.\n"); + return -ENXIO; + } + + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__); + av7110->arm_errors++; + return -ETIMEDOUT; + } + msleep(1); + } + + if (FW_VERSION(av7110->arm_app) <= 0x261f) + wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2); + +#ifndef _NOHANDSHAKE + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_SHAKE); + if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__); + return -ETIMEDOUT; + } + msleep(1); + } +#endif + + switch ((buf[0] >> 8) & 0xff) { + case COMTYPE_PIDFILTER: + case COMTYPE_ENCODER: + case COMTYPE_REC_PLAY: + case COMTYPE_MPEGDECODER: + type = "MSG"; + flags[0] = GPMQOver; + flags[1] = GPMQFull; + break; + case COMTYPE_OSD: + type = "OSD"; + flags[0] = OSDQOver; + flags[1] = OSDQFull; + break; + case COMTYPE_MISC: + if (FW_VERSION(av7110->arm_app) >= 0x261d) { + type = "MSG"; + flags[0] = GPMQOver; + flags[1] = GPMQBusy; + } + break; + default: + break; + } + + if (type != NULL) { + /* non-immediate COMMAND type */ + start = jiffies; + for (;;) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & flags[0]) { + printk(KERN_ERR "%s: %s QUEUE overflow\n", + __func__, type); + return -1; + } + if ((stat & flags[1]) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", + __func__, type); + av7110->arm_errors++; + return -ETIMEDOUT; + } + msleep(1); + } + } + + for (i = 2; i < length; i++) + wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); + + if (length) + wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2); + else + wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2); + + wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); + + if (FW_VERSION(av7110->arm_app) <= 0x261f) + wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); + +#ifdef COM_DEBUG + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n", + __func__, (buf[0] >> 8) & 0xff); + return -ETIMEDOUT; + } + msleep(1); + } + + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & GPMQOver) { + printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); + return -ENOSPC; + } + else if (stat & OSDQOver) { + printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); + return -ENOSPC; + } +#endif + + return 0; +} + +static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +{ + int ret; + +// dprintk(4, "%p\n", av7110); + + if (!av7110->arm_ready) { + dprintk(1, "arm not ready.\n"); + return -1; + } + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + + ret = __av7110_send_fw_cmd(av7110, buf, length); + mutex_unlock(&av7110->dcomlock); + if (ret && ret!=-ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", + __func__, ret); + return ret; +} + +int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) +{ + va_list args; + u16 buf[num + 2]; + int i, ret; + +// dprintk(4, "%p\n", av7110); + + buf[0] = ((type << 8) | com); + buf[1] = num; + + if (num) { + va_start(args, num); + for (i = 0; i < num; i++) + buf[i + 2] = va_arg(args, u32); + va_end(args); + } + + ret = av7110_send_fw_cmd(av7110, buf, num + 2); + if (ret && ret != -ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret); + return ret; +} + +#if 0 +int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) +{ + int i, ret; + u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + dprintk(4, "%p\n", av7110); + + for(i = 0; i < len && i < 32; i++) + { + if(i % 2 == 0) + cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; + else + cmd[(i / 2) + 2] |= buf[i]; + } + + ret = av7110_send_fw_cmd(av7110, cmd, 18); + if (ret && ret != -ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret); + return ret; +} +#endif /* 0 */ + +int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, + int request_buf_len, u16 *reply_buf, int reply_buf_len) +{ + int err; + s16 i; + unsigned long start; +#ifdef COM_DEBUG + u32 stat; +#endif + + dprintk(4, "%p\n", av7110); + + if (!av7110->arm_ready) { + dprintk(1, "arm not ready.\n"); + return -1; + } + + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + + if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) { + mutex_unlock(&av7110->dcomlock); + printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); + return err; + } + + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } +#ifdef _NOHANDSHAKE + msleep(1); +#endif + } + +#ifndef _NOHANDSHAKE + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_SHAKE); + if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } +#endif + +#ifdef COM_DEBUG + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & GPMQOver) { + printk(KERN_ERR "%s: GPMQOver\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -1; + } + else if (stat & OSDQOver) { + printk(KERN_ERR "%s: OSDQOver\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -1; + } +#endif + + for (i = 0; i < reply_buf_len; i++) + reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2); + + mutex_unlock(&av7110->dcomlock); + return 0; +} + +static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) +{ + int ret; + ret = av7110_fw_request(av7110, &tag, 0, buf, length); + if (ret) + printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); + return ret; +} + + +/**************************************************************************** + * Firmware commands + ****************************************************************************/ + +/* get version of the firmware ROM, RTSL, video ucode and ARM application */ +int av7110_firmversion(struct av7110 *av7110) +{ + u16 buf[20]; + u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion); + + dprintk(4, "%p\n", av7110); + + if (av7110_fw_query(av7110, tag, buf, 16)) { + printk("dvb-ttpci: failed to boot firmware @ card %d\n", + av7110->dvb_adapter.num); + return -EIO; + } + + av7110->arm_fw = (buf[0] << 16) + buf[1]; + av7110->arm_rtsl = (buf[2] << 16) + buf[3]; + av7110->arm_vid = (buf[4] << 16) + buf[5]; + av7110->arm_app = (buf[6] << 16) + buf[7]; + av7110->avtype = (buf[8] << 16) + buf[9]; + + printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", + av7110->dvb_adapter.num, av7110->arm_fw, + av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); + + /* print firmware capabilities */ + if (FW_CI_LL_SUPPORT(av7110->arm_app)) + printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n", + av7110->dvb_adapter.num); + else + printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n", + av7110->dvb_adapter.num); + + return 0; +} + + +int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst) +{ + int i, ret; + u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC), + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + dprintk(4, "%p\n", av7110); + + if (len > 10) + len = 10; + + buf[1] = len + 2; + buf[2] = len; + + if (burst != -1) + buf[3] = burst ? 0x01 : 0x00; + else + buf[3] = 0xffff; + + for (i = 0; i < len; i++) + buf[i + 4] = msg[i]; + + ret = av7110_send_fw_cmd(av7110, buf, 18); + if (ret && ret!=-ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); + return ret; +} + + +#ifdef CONFIG_DVB_AV7110_OSD + +static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr); +} + +static inline int SetBlend_(struct av7110 *av7110, u8 windownr, + enum av7110_osd_palette_type colordepth, u16 index, u8 blending) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4, + windownr, colordepth, index, blending); +} + +static inline int SetColor_(struct av7110 *av7110, u8 windownr, + enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5, + windownr, colordepth, index, colorhi, colorlo); +} + +static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize, + u16 colorfg, u16 colorbg) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4, + windownr, fontsize, colorfg, colorbg); +} + +static int FlushText(struct av7110 *av7110) +{ + unsigned long start; + int err; + + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_OSD); + if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n", + __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } + mutex_unlock(&av7110->dcomlock); + return 0; +} + +static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) +{ + int i, ret; + unsigned long start; + int length = strlen(buf) + 1; + u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y }; + + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + + start = jiffies; + while (1) { + ret = time_after(jiffies, start + ARM_WAIT_OSD); + if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) + break; + if (ret) { + printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n", + __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } +#ifndef _NOHANDSHAKE + start = jiffies; + while (1) { + ret = time_after(jiffies, start + ARM_WAIT_SHAKE); + if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) + break; + if (ret) { + printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n", + __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } +#endif + for (i = 0; i < length / 2; i++) + wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, + swab16(*(u16 *)(buf + 2 * i)), 2); + if (length & 1) + wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2); + ret = __av7110_send_fw_cmd(av7110, cbuf, 5); + mutex_unlock(&av7110->dcomlock); + if (ret && ret!=-ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); + return ret; +} + +static inline int DrawLine(struct av7110 *av7110, u8 windownr, + u16 x, u16 y, u16 dx, u16 dy, u16 color) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6, + windownr, x, y, dx, dy, color); +} + +static inline int DrawBlock(struct av7110 *av7110, u8 windownr, + u16 x, u16 y, u16 dx, u16 dy, u16 color) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6, + windownr, x, y, dx, dy, color); +} + +static inline int HideWindow(struct av7110 *av7110, u8 windownr) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr); +} + +static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y); +} + +static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y); +} + +static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr); +} + +static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr, + osd_raw_window_t disptype, + u16 width, u16 height) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4, + windownr, disptype, width, height); +} + + +static enum av7110_osd_palette_type bpp2pal[8] = { + Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit +}; +static osd_raw_window_t bpp2bit[8] = { + OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 +}; + +static inline int WaitUntilBmpLoaded(struct av7110 *av7110) +{ + int ret = wait_event_timeout(av7110->bmpq, + av7110->bmp_state != BMP_LOADING, 10*HZ); + if (ret == 0) { + printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", + ret, av7110->bmp_state); + av7110->bmp_state = BMP_NONE; + return -ETIMEDOUT; + } + return 0; +} + +static inline int LoadBitmap(struct av7110 *av7110, + u16 dx, u16 dy, int inc, u8 __user * data) +{ + u16 format; + int bpp; + int i; + int d, delta; + u8 c; + int ret; + + dprintk(4, "%p\n", av7110); + + format = bpp2bit[av7110->osdbpp[av7110->osdwin]]; + + av7110->bmp_state = BMP_LOADING; + if (format == OSD_BITMAP8) { + bpp=8; delta = 1; + } else if (format == OSD_BITMAP4) { + bpp=4; delta = 2; + } else if (format == OSD_BITMAP2) { + bpp=2; delta = 4; + } else if (format == OSD_BITMAP1) { + bpp=1; delta = 8; + } else { + av7110->bmp_state = BMP_NONE; + return -EINVAL; + } + av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8; + av7110->bmpp = 0; + if (av7110->bmplen > 32768) { + av7110->bmp_state = BMP_NONE; + return -EINVAL; + } + for (i = 0; i < dy; i++) { + if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) { + av7110->bmp_state = BMP_NONE; + return -EINVAL; + } + } + if (format != OSD_BITMAP8) { + for (i = 0; i < dx * dy / delta; i++) { + c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1]; + for (d = delta - 2; d >= 0; d--) { + c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d] + << ((delta - d - 1) * bpp)); + ((u8 *)av7110->bmpbuf)[1024 + i] = c; + } + } + } + av7110->bmplen += 1024; + dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen); + ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy); + if (!ret) + ret = WaitUntilBmpLoaded(av7110); + return ret; +} + +static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y) +{ + dprintk(4, "%p\n", av7110); + + return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0); +} + +static inline int ReleaseBitmap(struct av7110 *av7110) +{ + dprintk(4, "%p\n", av7110); + + if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e) + return -1; + if (av7110->bmp_state == BMP_LOADING) + dprintk(1,"ReleaseBitmap called while BMP_LOADING\n"); + av7110->bmp_state = BMP_NONE; + return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0); +} + +static u32 RGB2YUV(u16 R, u16 G, u16 B) +{ + u16 y, u, v; + u16 Y, Cr, Cb; + + y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */ + u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */ + v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */ + + Y = y / 256; + Cb = u / 16; + Cr = v / 16; + + return Cr | (Cb << 16) | (Y << 8); +} + +static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend) +{ + int ret; + + u16 ch, cl; + u32 yuv; + + yuv = blend ? RGB2YUV(r,g,b) : 0; + cl = (yuv & 0xffff); + ch = ((yuv >> 16) & 0xffff); + ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], + color, ch, cl); + if (!ret) + ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], + color, ((blend >> 4) & 0x0f)); + return ret; +} + +static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last) +{ + int i; + int length = last - first + 1; + + if (length * 4 > DATA_BUFF3_SIZE) + return -EINVAL; + + for (i = 0; i < length; i++) { + u32 color, blend, yuv; + + if (get_user(color, colors + i)) + return -EFAULT; + blend = (color & 0xF0000000) >> 4; + yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF, + (color >> 16) & 0xFF) | blend : 0; + yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16); + wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4); + } + return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4, + av7110->osdwin, + bpp2pal[av7110->osdbpp[av7110->osdwin]], + first, last); +} + +static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, + int x1, int y1, int inc, u8 __user * data) +{ + uint w, h, bpp, bpl, size, lpb, bnum, brest; + int i; + int rc,release_rc; + + w = x1 - x0 + 1; + h = y1 - y0 + 1; + if (inc <= 0) + inc = w; + if (w <= 0 || w > 720 || h <= 0 || h > 576) + return -EINVAL; + bpp = av7110->osdbpp[av7110->osdwin] + 1; + bpl = ((w * bpp + 7) & ~7) / 8; + size = h * bpl; + lpb = (32 * 1024) / bpl; + bnum = size / (lpb * bpl); + brest = size - bnum * lpb * bpl; + + if (av7110->bmp_state == BMP_LOADING) { + /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */ + BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e); + rc = WaitUntilBmpLoaded(av7110); + if (rc) + return rc; + /* just continue. This should work for all fw versions + * if bnum==1 && !brest && LoadBitmap was successful + */ + } + + rc = 0; + for (i = 0; i < bnum; i++) { + rc = LoadBitmap(av7110, w, lpb, inc, data); + if (rc) + break; + rc = BlitBitmap(av7110, x0, y0 + i * lpb); + if (rc) + break; + data += lpb * inc; + } + if (!rc && brest) { + rc = LoadBitmap(av7110, w, brest / bpl, inc, data); + if (!rc) + rc = BlitBitmap(av7110, x0, y0 + bnum * lpb); + } + release_rc = ReleaseBitmap(av7110); + if (!rc) + rc = release_rc; + if (rc) + dprintk(1,"returns %d\n",rc); + return rc; +} + +int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) +{ + int ret; + + if (mutex_lock_interruptible(&av7110->osd_mutex)) + return -ERESTARTSYS; + + switch (dc->cmd) { + case OSD_Close: + ret = DestroyOSDWindow(av7110, av7110->osdwin); + break; + case OSD_Open: + av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7; + ret = CreateOSDWindow(av7110, av7110->osdwin, + bpp2bit[av7110->osdbpp[av7110->osdwin]], + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); + if (ret) + break; + if (!dc->data) { + ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); + if (ret) + break; + ret = SetColorBlend(av7110, av7110->osdwin); + } + break; + case OSD_Show: + ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0); + break; + case OSD_Hide: + ret = HideWindow(av7110, av7110->osdwin); + break; + case OSD_Clear: + ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0); + break; + case OSD_Fill: + ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color); + break; + case OSD_SetColor: + ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1); + break; + case OSD_SetPalette: + if (FW_VERSION(av7110->arm_app) >= 0x2618) + ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); + else { + int i, len = dc->x0-dc->color+1; + u8 __user *colors = (u8 __user *)dc->data; + u8 r, g = 0, b = 0, blend = 0; + ret = 0; + for (i = 0; icolor + i, r, g, b, blend); + if (ret) + break; + } + } + break; + case OSD_SetPixel: + ret = DrawLine(av7110, av7110->osdwin, + dc->x0, dc->y0, 0, 0, dc->color); + break; + case OSD_SetRow: + dc->y1 = dc->y0; + /* fall through */ + case OSD_SetBlock: + ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data); + break; + case OSD_FillRow: + ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, + dc->x1-dc->x0+1, dc->y1, dc->color); + break; + case OSD_FillBlock: + ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); + break; + case OSD_Line: + ret = DrawLine(av7110, av7110->osdwin, + dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); + break; + case OSD_Text: + { + char textbuf[240]; + + if (strncpy_from_user(textbuf, dc->data, 240) < 0) { + ret = -EFAULT; + break; + } + textbuf[239] = 0; + if (dc->x1 > 3) + dc->x1 = 3; + ret = SetFont(av7110, av7110->osdwin, dc->x1, + (u16) (dc->color & 0xffff), (u16) (dc->color >> 16)); + if (!ret) + ret = FlushText(av7110); + if (!ret) + ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf); + break; + } + case OSD_SetWindow: + if (dc->x0 < 1 || dc->x0 > 7) + ret = -EINVAL; + else { + av7110->osdwin = dc->x0; + ret = 0; + } + break; + case OSD_MoveWindow: + ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); + if (!ret) + ret = SetColorBlend(av7110, av7110->osdwin); + break; + case OSD_OpenRaw: + if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) { + ret = -EINVAL; + break; + } + if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR) + av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1; + else + av7110->osdbpp[av7110->osdwin] = 0; + ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color, + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); + if (ret) + break; + if (!dc->data) { + ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); + if (!ret) + ret = SetColorBlend(av7110, av7110->osdwin); + } + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&av7110->osd_mutex); + if (ret==-ERESTARTSYS) + dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); + else if (ret) + dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); + + return ret; +} + +int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap) +{ + switch (cap->cmd) { + case OSD_CAP_MEMSIZE: + if (FW_4M_SDRAM(av7110->arm_app)) + cap->val = 1000000; + else + cap->val = 92000; + return 0; + default: + return -EINVAL; + } +} +#endif /* CONFIG_DVB_AV7110_OSD */ diff --git a/drivers/media/pci/ttpci/av7110_hw.h b/drivers/media/pci/ttpci/av7110_hw.h new file mode 100644 index 000000000000..1634aba5cb84 --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_hw.h @@ -0,0 +1,495 @@ +#ifndef _AV7110_HW_H_ +#define _AV7110_HW_H_ + +#include "av7110.h" + +/* DEBI transfer mode defs */ + +#define DEBINOSWAP 0x000e0000 +#define DEBISWAB 0x001e0000 +#define DEBISWAP 0x002e0000 + +#define ARM_WAIT_FREE (HZ) +#define ARM_WAIT_SHAKE (HZ/5) +#define ARM_WAIT_OSD (HZ) + + +enum av7110_bootstate +{ + BOOTSTATE_BUFFER_EMPTY = 0, + BOOTSTATE_BUFFER_FULL = 1, + BOOTSTATE_AV7110_BOOT_COMPLETE = 2 +}; + +enum av7110_type_rec_play_format +{ RP_None, + AudioPES, + AudioMp2, + AudioPCM, + VideoPES, + AV_PES +}; + +enum av7110_osd_palette_type +{ + NoPalet = 0, /* No palette */ + Pal1Bit = 2, /* 2 colors for 1 Bit Palette */ + Pal2Bit = 4, /* 4 colors for 2 bit palette */ + Pal4Bit = 16, /* 16 colors for 4 bit palette */ + Pal8Bit = 256 /* 256 colors for 16 bit palette */ +}; + +/* switch defines */ +#define SB_GPIO 3 +#define SB_OFF SAA7146_GPIO_OUTLO /* SlowBlank off (TV-Mode) */ +#define SB_ON SAA7146_GPIO_INPUT /* SlowBlank on (AV-Mode) */ +#define SB_WIDE SAA7146_GPIO_OUTHI /* SlowBlank 6V (16/9-Mode) (not implemented) */ + +#define FB_GPIO 1 +#define FB_OFF SAA7146_GPIO_LO /* FastBlank off (CVBS-Mode) */ +#define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */ +#define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */ + +enum av7110_video_output_mode +{ + NO_OUT = 0, /* disable analog output */ + CVBS_RGB_OUT = 1, + CVBS_YC_OUT = 2, + YC_OUT = 3 +}; + +/* firmware internal msg q status: */ +#define GPMQFull 0x0001 /* Main Message Queue Full */ +#define GPMQOver 0x0002 /* Main Message Queue Overflow */ +#define HPQFull 0x0004 /* High Priority Msg Queue Full */ +#define HPQOver 0x0008 +#define OSDQFull 0x0010 /* OSD Queue Full */ +#define OSDQOver 0x0020 +#define GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */ +#define HPQBusy 0x0080 +#define OSDQBusy 0x0100 + +/* hw section filter flags */ +#define SECTION_EIT 0x01 +#define SECTION_SINGLE 0x00 +#define SECTION_CYCLE 0x02 +#define SECTION_CONTINUOS 0x04 +#define SECTION_MODE 0x06 +#define SECTION_IPMPE 0x0C /* size up to 4k */ +#define SECTION_HIGH_SPEED 0x1C /* larger buffer */ +#define DATA_PIPING_FLAG 0x20 /* for Data Piping Filter */ + +#define PBUFSIZE_NONE 0x0000 +#define PBUFSIZE_1P 0x0100 +#define PBUFSIZE_2P 0x0200 +#define PBUFSIZE_1K 0x0300 +#define PBUFSIZE_2K 0x0400 +#define PBUFSIZE_4K 0x0500 +#define PBUFSIZE_8K 0x0600 +#define PBUFSIZE_16K 0x0700 +#define PBUFSIZE_32K 0x0800 + + +/* firmware command codes */ +enum av7110_osd_command { + WCreate, + WDestroy, + WMoveD, + WMoveA, + WHide, + WTop, + DBox, + DLine, + DText, + Set_Font, + SetColor, + SetBlend, + SetWBlend, + SetCBlend, + SetNonBlend, + LoadBmp, + BlitBmp, + ReleaseBmp, + SetWTrans, + SetWNoTrans, + Set_Palette +}; + +enum av7110_pid_command { + MultiPID, + VideoPID, + AudioPID, + InitFilt, + FiltError, + NewVersion, + CacheError, + AddPIDFilter, + DelPIDFilter, + Scan, + SetDescr, + SetIR, + FlushTSQueue +}; + +enum av7110_mpeg_command { + SelAudChannels +}; + +enum av7110_audio_command { + AudioDAC, + CabADAC, + ON22K, + OFF22K, + MainSwitch, + ADSwitch, + SendDiSEqC, + SetRegister, + SpdifSwitch +}; + +enum av7110_request_command { + AudioState, + AudioBuffState, + VideoState1, + VideoState2, + VideoState3, + CrashCounter, + ReqVersion, + ReqVCXO, + ReqRegister, + ReqSecFilterError, + ReqSTC +}; + +enum av7110_encoder_command { + SetVidMode, + SetTestMode, + LoadVidCode, + SetMonitorType, + SetPanScanType, + SetFreezeMode, + SetWSSConfig +}; + +enum av7110_rec_play_state { + __Record, + __Stop, + __Play, + __Pause, + __Slow, + __FF_IP, + __Scan_I, + __Continue +}; + +enum av7110_fw_cmd_misc { + AV7110_FW_VIDEO_ZOOM = 1, + AV7110_FW_VIDEO_COMMAND, + AV7110_FW_AUDIO_COMMAND +}; + +enum av7110_command_type { + COMTYPE_NOCOM, + COMTYPE_PIDFILTER, + COMTYPE_MPEGDECODER, + COMTYPE_OSD, + COMTYPE_BMP, + COMTYPE_ENCODER, + COMTYPE_AUDIODAC, + COMTYPE_REQUEST, + COMTYPE_SYSTEM, + COMTYPE_REC_PLAY, + COMTYPE_COMMON_IF, + COMTYPE_PID_FILTER, + COMTYPE_PES, + COMTYPE_TS, + COMTYPE_VIDEO, + COMTYPE_AUDIO, + COMTYPE_CI_LL, + COMTYPE_MISC = 0x80 +}; + +#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */ +#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */ +#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */ +#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */ +#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ + +/* MPEG video decoder commands */ +#define AV_VIDEO_CMD_STOP 0x000e +#define AV_VIDEO_CMD_PLAY 0x000d +#define AV_VIDEO_CMD_FREEZE 0x0102 +#define AV_VIDEO_CMD_FFWD 0x0016 +#define AV_VIDEO_CMD_SLOW 0x0022 + +/* MPEG audio decoder commands */ +#define AUDIO_CMD_MUTE 0x0001 +#define AUDIO_CMD_UNMUTE 0x0002 +#define AUDIO_CMD_PCM16 0x0010 +#define AUDIO_CMD_STEREO 0x0080 +#define AUDIO_CMD_MONO_L 0x0100 +#define AUDIO_CMD_MONO_R 0x0200 +#define AUDIO_CMD_SYNC_OFF 0x000e +#define AUDIO_CMD_SYNC_ON 0x000f + +/* firmware data interface codes */ +#define DATA_NONE 0x00 +#define DATA_FSECTION 0x01 +#define DATA_IPMPE 0x02 +#define DATA_MPEG_RECORD 0x03 +#define DATA_DEBUG_MESSAGE 0x04 +#define DATA_COMMON_INTERFACE 0x05 +#define DATA_MPEG_PLAY 0x06 +#define DATA_BMP_LOAD 0x07 +#define DATA_IRCOMMAND 0x08 +#define DATA_PIPING 0x09 +#define DATA_STREAMING 0x0a +#define DATA_CI_GET 0x0b +#define DATA_CI_PUT 0x0c +#define DATA_MPEG_VIDEO_EVENT 0x0d + +#define DATA_PES_RECORD 0x10 +#define DATA_PES_PLAY 0x11 +#define DATA_TS_RECORD 0x12 +#define DATA_TS_PLAY 0x13 + +/* ancient CI command codes, only two are actually still used + * by the link level CI firmware */ +#define CI_CMD_ERROR 0x00 +#define CI_CMD_ACK 0x01 +#define CI_CMD_SYSTEM_READY 0x02 +#define CI_CMD_KEYPRESS 0x03 +#define CI_CMD_ON_TUNED 0x04 +#define CI_CMD_ON_SWITCH_PROGRAM 0x05 +#define CI_CMD_SECTION_ARRIVED 0x06 +#define CI_CMD_SECTION_TIMEOUT 0x07 +#define CI_CMD_TIME 0x08 +#define CI_CMD_ENTER_MENU 0x09 +#define CI_CMD_FAST_PSI 0x0a +#define CI_CMD_GET_SLOT_INFO 0x0b + +#define CI_MSG_NONE 0x00 +#define CI_MSG_CI_INFO 0x01 +#define CI_MSG_MENU 0x02 +#define CI_MSG_LIST 0x03 +#define CI_MSG_TEXT 0x04 +#define CI_MSG_REQUEST_INPUT 0x05 +#define CI_MSG_INPUT_COMPLETE 0x06 +#define CI_MSG_LIST_MORE 0x07 +#define CI_MSG_MENU_MORE 0x08 +#define CI_MSG_CLOSE_MMI_IMM 0x09 +#define CI_MSG_SECTION_REQUEST 0x0a +#define CI_MSG_CLOSE_FILTER 0x0b +#define CI_PSI_COMPLETE 0x0c +#define CI_MODULE_READY 0x0d +#define CI_SWITCH_PRG_REPLY 0x0e +#define CI_MSG_TEXT_MORE 0x0f + +#define CI_MSG_CA_PMT 0xe0 +#define CI_MSG_ERROR 0xf0 + + +/* base address of the dual ported RAM which serves as communication + * area between PCI bus and av7110, + * as seen by the DEBI bus of the saa7146 */ +#define DPRAM_BASE 0x4000 + +/* boot protocol area */ +#define AV7110_BOOT_STATE (DPRAM_BASE + 0x3F8) +#define AV7110_BOOT_SIZE (DPRAM_BASE + 0x3FA) +#define AV7110_BOOT_BASE (DPRAM_BASE + 0x3FC) +#define AV7110_BOOT_BLOCK (DPRAM_BASE + 0x400) +#define AV7110_BOOT_MAX_SIZE 0xc00 + +/* firmware command protocol area */ +#define IRQ_STATE (DPRAM_BASE + 0x0F4) +#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6) +#define MSGSTATE (DPRAM_BASE + 0x0F8) +#define COMMAND (DPRAM_BASE + 0x0FC) +#define COM_BUFF (DPRAM_BASE + 0x100) +#define COM_BUFF_SIZE 0x20 + +/* various data buffers */ +#define BUFF1_BASE (DPRAM_BASE + 0x120) +#define BUFF1_SIZE 0xE0 + +#define DATA_BUFF0_BASE (DPRAM_BASE + 0x200) +#define DATA_BUFF0_SIZE 0x0800 + +#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE) +#define DATA_BUFF1_SIZE 0x0800 + +#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE) +#define DATA_BUFF2_SIZE 0x0800 + +#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE) +#define DATA_BUFF3_SIZE 0x0400 + +#define Reserved (DPRAM_BASE + 0x1E00) +#define Reserved_SIZE 0x1C0 + + +/* firmware status area */ +#define STATUS_BASE (DPRAM_BASE + 0x1FC0) +#define STATUS_LOOPS (STATUS_BASE + 0x08) + +#define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C) +/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */ +#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E) + +/* firmware data protocol area */ +#define RX_TYPE (DPRAM_BASE + 0x1FE8) +#define RX_LEN (DPRAM_BASE + 0x1FEA) +#define TX_TYPE (DPRAM_BASE + 0x1FEC) +#define TX_LEN (DPRAM_BASE + 0x1FEE) + +#define RX_BUFF (DPRAM_BASE + 0x1FF4) +#define TX_BUFF (DPRAM_BASE + 0x1FF6) + +#define HANDSHAKE_REG (DPRAM_BASE + 0x1FF8) +#define COM_IF_LOCK (DPRAM_BASE + 0x1FFA) + +#define IRQ_RX (DPRAM_BASE + 0x1FFC) +#define IRQ_TX (DPRAM_BASE + 0x1FFE) + +/* used by boot protocol to load firmware into av7110 DRAM */ +#define DRAM_START_CODE 0x2e000404 +#define DRAM_MAX_CODE_SIZE 0x00100000 + +/* saa7146 gpio lines */ +#define RESET_LINE 2 +#define DEBI_DONE_LINE 1 +#define ARM_IRQ_LINE 0 + + + +extern int av7110_bootarm(struct av7110 *av7110); +extern int av7110_firmversion(struct av7110 *av7110); +#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) +#define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) +#define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) + +extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); +extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); +extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, + int request_buf_len, u16 *reply_buf, int reply_buf_len); + + +/* DEBI (saa7146 data extension bus interface) access */ +extern int av7110_debiwrite(struct av7110 *av7110, u32 config, + int addr, u32 val, int count); +extern u32 av7110_debiread(struct av7110 *av7110, u32 config, + int addr, int count); + + +/* DEBI during interrupt */ +/* single word writes */ +static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +{ + av7110_debiwrite(av7110, config, addr, val, count); +} + +/* buffer writes */ +static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, + const u8 *val, int count) +{ + memcpy(av7110->debi_virt, val, count); + av7110_debiwrite(av7110, config, addr, 0, count); +} + +static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +{ + u32 res; + + res=av7110_debiread(av7110, config, addr, count); + if (count<=4) + memcpy(av7110->debi_virt, (char *) &res, count); + return res; +} + +/* DEBI outside interrupts, only for count <= 4! */ +static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +{ + unsigned long flags; + + spin_lock_irqsave(&av7110->debilock, flags); + av7110_debiwrite(av7110, config, addr, val, count); + spin_unlock_irqrestore(&av7110->debilock, flags); +} + +static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +{ + unsigned long flags; + u32 res; + + spin_lock_irqsave(&av7110->debilock, flags); + res=av7110_debiread(av7110, config, addr, count); + spin_unlock_irqrestore(&av7110->debilock, flags); + return res; +} + +/* handle mailbox registers of the dual ported RAM */ +static inline void ARM_ResetMailBox(struct av7110 *av7110) +{ + unsigned long flags; + + spin_lock_irqsave(&av7110->debilock, flags); + av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2); + av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2); + spin_unlock_irqrestore(&av7110->debilock, flags); +} + +static inline void ARM_ClearMailBox(struct av7110 *av7110) +{ + iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); +} + +static inline void ARM_ClearIrq(struct av7110 *av7110) +{ + irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); +} + +/**************************************************************************** + * Firmware commands + ****************************************************************************/ + +static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) +{ + return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data); +} + +static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) +{ + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); +} + +static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) +{ + return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4, + (com>>16), (com&0xffff), + (arg>>16), (arg&0xffff)); +} + +static inline int audcom(struct av7110 *av7110, u32 com) +{ + return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2, + (com>>16), (com&0xffff)); +} + +static inline int Set22K(struct av7110 *av7110, int state) +{ + return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0); +} + + +extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); + + +#ifdef CONFIG_DVB_AV7110_OSD +extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); +extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); +#endif /* CONFIG_DVB_AV7110_OSD */ + + + +#endif /* _AV7110_HW_H_ */ diff --git a/drivers/media/pci/ttpci/av7110_ipack.c b/drivers/media/pci/ttpci/av7110_ipack.c new file mode 100644 index 000000000000..699ef8b5b99a --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_ipack.c @@ -0,0 +1,403 @@ +#include "dvb_filter.h" +#include "av7110_ipack.h" +#include /* for memcpy() */ +#include + + +void av7110_ipack_reset(struct ipack *p) +{ + p->found = 0; + p->cid = 0; + p->plength = 0; + p->flag1 = 0; + p->flag2 = 0; + p->hlength = 0; + p->mpeg = 0; + p->check = 0; + p->which = 0; + p->done = 0; + p->count = 0; +} + + +int av7110_ipack_init(struct ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)) +{ + if (!(p->buf = vmalloc(size*sizeof(u8)))) { + printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); + return -ENOMEM; + } + p->size = size; + p->func = func; + p->repack_subids = 0; + av7110_ipack_reset(p); + return 0; +} + + +void av7110_ipack_free(struct ipack *p) +{ + vfree(p->buf); +} + + +static void send_ipack(struct ipack *p) +{ + int off; + struct dvb_audio_info ai; + int ac3_off = 0; + int streamid = 0; + int nframes = 0; + int f = 0; + + switch (p->mpeg) { + case 2: + if (p->count < 10) + return; + p->buf[3] = p->cid; + p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); + p->buf[5] = (u8)((p->count - 6) & 0x00ff); + if (p->repack_subids && p->cid == PRIVATE_STREAM1) { + off = 9 + p->buf[8]; + streamid = p->buf[off]; + if ((streamid & 0xf8) == 0x80) { + ai.off = 0; + ac3_off = ((p->buf[off + 2] << 8)| + p->buf[off + 3]); + if (ac3_off < p->count) + f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, + p->count - ac3_off, &ai, 0); + if (!f) { + nframes = (p->count - off - 3 - ac3_off) / + ai.framesize + 1; + p->buf[off + 2] = (ac3_off >> 8) & 0xff; + p->buf[off + 3] = (ac3_off) & 0xff; + p->buf[off + 1] = nframes; + ac3_off += nframes * ai.framesize - p->count; + } + } + } + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x80; + p->buf[7] = 0x00; + p->buf[8] = 0x00; + p->count = 9; + if (p->repack_subids && p->cid == PRIVATE_STREAM1 + && (streamid & 0xf8) == 0x80) { + p->count += 4; + p->buf[9] = streamid; + p->buf[10] = (ac3_off >> 8) & 0xff; + p->buf[11] = (ac3_off) & 0xff; + p->buf[12] = 0; + } + break; + + case 1: + if (p->count < 8) + return; + p->buf[3] = p->cid; + p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); + p->buf[5] = (u8)((p->count - 6) & 0x00ff); + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x0f; + p->count = 7; + break; + } +} + + +void av7110_ipack_flush(struct ipack *p) +{ + if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) + return; + p->plength = p->found - 6; + p->found = 0; + send_ipack(p); + av7110_ipack_reset(p); +} + + +static void write_ipack(struct ipack *p, const u8 *data, int count) +{ + u8 headr[3] = { 0x00, 0x00, 0x01 }; + + if (p->count < 6) { + memcpy(p->buf, headr, 3); + p->count = 6; + } + + if (p->count + count < p->size){ + memcpy(p->buf+p->count, data, count); + p->count += count; + } else { + int rest = p->size - p->count; + memcpy(p->buf+p->count, data, rest); + p->count += rest; + send_ipack(p); + if (count - rest > 0) + write_ipack(p, data + rest, count - rest); + } +} + + +int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) +{ + int l; + int c = 0; + + while (c < count && (p->mpeg == 0 || + (p->mpeg == 1 && p->found < 7) || + (p->mpeg == 2 && p->found < 9)) + && (p->found < 5 || !p->done)) { + switch (p->found) { + case 0: + case 1: + if (buf[c] == 0x00) + p->found++; + else + p->found = 0; + c++; + break; + case 2: + if (buf[c] == 0x01) + p->found++; + else if (buf[c] == 0) + p->found = 2; + else + p->found = 0; + c++; + break; + case 3: + p->cid = 0; + switch (buf[c]) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + p->done = 1; + /* fall through */ + case PRIVATE_STREAM1: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + p->found++; + p->cid = buf[c]; + c++; + break; + default: + p->found = 0; + break; + } + break; + + case 4: + if (count-c > 1) { + p->plen[0] = buf[c]; + c++; + p->plen[1] = buf[c]; + c++; + p->found += 2; + p->plength = (p->plen[0] << 8) | p->plen[1]; + } else { + p->plen[0] = buf[c]; + p->found++; + return count; + } + break; + case 5: + p->plen[1] = buf[c]; + c++; + p->found++; + p->plength = (p->plen[0] << 8) | p->plen[1]; + break; + case 6: + if (!p->done) { + p->flag1 = buf[c]; + c++; + p->found++; + if ((p->flag1 & 0xc0) == 0x80) + p->mpeg = 2; + else { + p->hlength = 0; + p->which = 0; + p->mpeg = 1; + p->flag2 = 0; + } + } + break; + + case 7: + if (!p->done && p->mpeg == 2) { + p->flag2 = buf[c]; + c++; + p->found++; + } + break; + + case 8: + if (!p->done && p->mpeg == 2) { + p->hlength = buf[c]; + c++; + p->found++; + } + break; + } + } + + if (c == count) + return count; + + if (!p->plength) + p->plength = MMAX_PLENGTH - 6; + + if (p->done || ((p->mpeg == 2 && p->found >= 9) || + (p->mpeg == 1 && p->found >= 7))) { + switch (p->cid) { + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case PRIVATE_STREAM1: + if (p->mpeg == 2 && p->found == 9) { + write_ipack(p, &p->flag1, 1); + write_ipack(p, &p->flag2, 1); + write_ipack(p, &p->hlength, 1); + } + + if (p->mpeg == 1 && p->found == 7) + write_ipack(p, &p->flag1, 1); + + if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && + p->found < 14) { + while (c < count && p->found < 14) { + p->pts[p->found - 9] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + } + if (c == count) + return count; + } + + if (p->mpeg == 1 && p->which < 2000) { + + if (p->found == 7) { + p->check = p->flag1; + p->hlength = 1; + } + + while (!p->which && c < count && + p->check == 0xff){ + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + } + + if (c == count) + return count; + + if ((p->check & 0xc0) == 0x40 && !p->which) { + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + + p->which = 1; + if (c == count) + return count; + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if (c == count) + return count; + } + + if (p->which == 1) { + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if (c == count) + return count; + } + + if ((p->check & 0x30) && p->check != 0xff) { + p->flag2 = (p->check & 0xf0) << 2; + p->pts[0] = p->check; + p->which = 3; + } + + if (c == count) + return count; + if (p->which > 2){ + if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { + while (c < count && p->which < 7) { + p->pts[p->which - 2] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if (c == count) + return count; + } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { + while (c < count && p->which < 12) { + if (p->which < 7) + p->pts[p->which - 2] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if (c == count) + return count; + } + p->which = 2000; + } + + } + + while (c < count && p->found < p->plength + 6) { + l = count - c; + if (l + p->found > p->plength + 6) + l = p->plength + 6 - p->found; + write_ipack(p, buf + c, l); + p->found += l; + c += l; + } + break; + } + + + if (p->done) { + if (p->found + count - c < p->plength + 6) { + p->found += count - c; + c = count; + } else { + c += p->plength + 6 - p->found; + p->found = p->plength + 6; + } + } + + if (p->plength && p->found == p->plength + 6) { + send_ipack(p); + av7110_ipack_reset(p); + if (c < count) + av7110_ipack_instant_repack(buf + c, count - c, p); + } + } + return count; +} diff --git a/drivers/media/pci/ttpci/av7110_ipack.h b/drivers/media/pci/ttpci/av7110_ipack.h new file mode 100644 index 000000000000..becf94d3fdfa --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_ipack.h @@ -0,0 +1,12 @@ +#ifndef _AV7110_IPACK_H_ +#define _AV7110_IPACK_H_ + +extern int av7110_ipack_init(struct ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)); + +extern void av7110_ipack_reset(struct ipack *p); +extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); +extern void av7110_ipack_free(struct ipack * p); +extern void av7110_ipack_flush(struct ipack *p); + +#endif diff --git a/drivers/media/pci/ttpci/av7110_ir.c b/drivers/media/pci/ttpci/av7110_ir.c new file mode 100644 index 000000000000..908f272fe26c --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_ir.c @@ -0,0 +1,415 @@ +/* + * Driver for the remote control of SAA7146 based AV7110 cards + * + * Copyright (C) 1999-2003 Holger Waechtler + * Copyright (C) 2003-2007 Oliver Endriss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" + + +#define AV_CNT 4 + +#define IR_RC5 0 +#define IR_RCMM 1 +#define IR_RC5_EXT 2 /* internal only */ + +#define IR_ALL 0xffffffff + +#define UP_TIMEOUT (HZ*7/25) + + +/* Note: enable ir debugging by or'ing debug with 16 */ + +static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; +module_param_array(ir_protocol, int, NULL, 0644); +MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); + +static int ir_inversion[AV_CNT]; +module_param_array(ir_inversion, int, NULL, 0644); +MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); + +static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; +module_param_array(ir_device_mask, uint, NULL, 0644); +MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); + + +static int av_cnt; +static struct av7110 *av_list[AV_CNT]; + +static u16 default_key_map [256] = { + KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, + KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, + KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0, + 0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0, + KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0, + KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW, + KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN, + 0, 0, 0, 0, KEY_EPG, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR +}; + + +/* key-up timer */ +static void av7110_emit_keyup(unsigned long parm) +{ + struct infrared *ir = (struct infrared *) parm; + + if (!ir || !test_bit(ir->last_key, ir->input_dev->key)) + return; + + input_report_key(ir->input_dev, ir->last_key, 0); + input_sync(ir->input_dev); +} + + +/* tasklet */ +static void av7110_emit_key(unsigned long parm) +{ + struct infrared *ir = (struct infrared *) parm; + u32 ircom = ir->ir_command; + u8 data; + u8 addr; + u16 toggle; + u16 keycode; + + /* extract device address and data */ + switch (ir->protocol) { + case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ + data = ircom & 0x3f; + addr = (ircom >> 6) & 0x1f; + toggle = ircom & 0x0800; + break; + + case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ + data = ircom & 0xff; + addr = (ircom >> 8) & 0x1f; + toggle = ircom & 0x8000; + break; + + case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ + data = ircom & 0x3f; + addr = (ircom >> 6) & 0x1f; + /* invert 7th data bit for backward compatibility with RC5 keymaps */ + if (!(ircom & 0x1000)) + data |= 0x40; + toggle = ircom & 0x0800; + break; + + default: + printk("%s invalid protocol %x\n", __func__, ir->protocol); + return; + } + + input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); + input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); + + keycode = ir->key_map[data]; + + dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", + __func__, ircom, addr, data, keycode); + + /* check device address */ + if (!(ir->device_mask & (1 << addr))) + return; + + if (!keycode) { + printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", + __func__, ircom, addr, data); + return; + } + + if (timer_pending(&ir->keyup_timer)) { + del_timer(&ir->keyup_timer); + if (ir->last_key != keycode || toggle != ir->last_toggle) { + ir->delay_timer_finished = 0; + input_event(ir->input_dev, EV_KEY, ir->last_key, 0); + input_event(ir->input_dev, EV_KEY, keycode, 1); + input_sync(ir->input_dev); + } else if (ir->delay_timer_finished) { + input_event(ir->input_dev, EV_KEY, keycode, 2); + input_sync(ir->input_dev); + } + } else { + ir->delay_timer_finished = 0; + input_event(ir->input_dev, EV_KEY, keycode, 1); + input_sync(ir->input_dev); + } + + ir->last_key = keycode; + ir->last_toggle = toggle; + + ir->keyup_timer.expires = jiffies + UP_TIMEOUT; + add_timer(&ir->keyup_timer); + +} + + +/* register with input layer */ +static void input_register_keys(struct infrared *ir) +{ + int i; + + set_bit(EV_KEY, ir->input_dev->evbit); + set_bit(EV_REP, ir->input_dev->evbit); + set_bit(EV_MSC, ir->input_dev->evbit); + + set_bit(MSC_RAW, ir->input_dev->mscbit); + set_bit(MSC_SCAN, ir->input_dev->mscbit); + + memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); + + for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { + if (ir->key_map[i] > KEY_MAX) + ir->key_map[i] = 0; + else if (ir->key_map[i] > KEY_RESERVED) + set_bit(ir->key_map[i], ir->input_dev->keybit); + } + + ir->input_dev->keycode = ir->key_map; + ir->input_dev->keycodesize = sizeof(ir->key_map[0]); + ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); +} + + +/* called by the input driver after rep[REP_DELAY] ms */ +static void input_repeat_key(unsigned long parm) +{ + struct infrared *ir = (struct infrared *) parm; + + ir->delay_timer_finished = 1; +} + + +/* check for configuration changes */ +int av7110_check_ir_config(struct av7110 *av7110, int force) +{ + int i; + int modified = force; + int ret = -ENODEV; + + for (i = 0; i < av_cnt; i++) + if (av7110 == av_list[i]) + break; + + if (i < av_cnt && av7110) { + if ((av7110->ir.protocol & 1) != ir_protocol[i] || + av7110->ir.inversion != ir_inversion[i]) + modified = true; + + if (modified) { + /* protocol */ + if (ir_protocol[i]) { + ir_protocol[i] = 1; + av7110->ir.protocol = IR_RCMM; + av7110->ir.ir_config = 0x0001; + } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { + av7110->ir.protocol = IR_RC5_EXT; + av7110->ir.ir_config = 0x0002; + } else { + av7110->ir.protocol = IR_RC5; + av7110->ir.ir_config = 0x0000; + } + /* inversion */ + if (ir_inversion[i]) { + ir_inversion[i] = 1; + av7110->ir.ir_config |= 0x8000; + } + av7110->ir.inversion = ir_inversion[i]; + /* update ARM */ + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, + av7110->ir.ir_config); + } else + ret = 0; + + /* address */ + if (av7110->ir.device_mask != ir_device_mask[i]) + av7110->ir.device_mask = ir_device_mask[i]; + } + + return ret; +} + + +/* /proc/av7110_ir interface */ +static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + char *page; + u32 ir_config; + int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; + int i; + + if (count < size) + return -EINVAL; + + page = vmalloc(size); + if (!page) + return -ENOMEM; + + if (copy_from_user(page, buffer, size)) { + vfree(page); + return -EFAULT; + } + + memcpy(&ir_config, page, sizeof ir_config); + + for (i = 0; i < av_cnt; i++) { + /* keymap */ + memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, + sizeof(av_list[i]->ir.key_map)); + /* protocol, inversion, address */ + ir_protocol[i] = ir_config & 0x0001; + ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; + if (ir_config & 0x4000) + ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); + else + ir_device_mask[i] = IR_ALL; + /* update configuration */ + av7110_check_ir_config(av_list[i], false); + input_register_keys(&av_list[i]->ir); + } + vfree(page); + return count; +} + +static const struct file_operations av7110_ir_proc_fops = { + .owner = THIS_MODULE, + .write = av7110_ir_proc_write, + .llseek = noop_llseek, +}; + +/* interrupt handler */ +static void ir_handler(struct av7110 *av7110, u32 ircom) +{ + dprintk(4, "ir command = %08x\n", ircom); + av7110->ir.ir_command = ircom; + tasklet_schedule(&av7110->ir.ir_tasklet); +} + + +int __devinit av7110_ir_init(struct av7110 *av7110) +{ + struct input_dev *input_dev; + static struct proc_dir_entry *e; + int err; + + if (av_cnt >= ARRAY_SIZE(av_list)) + return -ENOSPC; + + av_list[av_cnt++] = av7110; + av7110_check_ir_config(av7110, true); + + init_timer(&av7110->ir.keyup_timer); + av7110->ir.keyup_timer.function = av7110_emit_keyup; + av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + av7110->ir.input_dev = input_dev; + snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), + "pci-%s/ir0", pci_name(av7110->dev->pci)); + + input_dev->name = "DVB on-card IR receiver"; + + input_dev->phys = av7110->ir.input_phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 2; + if (av7110->dev->pci->subsystem_vendor) { + input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; + input_dev->id.product = av7110->dev->pci->subsystem_device; + } else { + input_dev->id.vendor = av7110->dev->pci->vendor; + input_dev->id.product = av7110->dev->pci->device; + } + input_dev->dev.parent = &av7110->dev->pci->dev; + /* initial keymap */ + memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); + input_register_keys(&av7110->ir); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + input_dev->timer.function = input_repeat_key; + input_dev->timer.data = (unsigned long) &av7110->ir; + + if (av_cnt == 1) { + e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops); + if (e) + e->size = 4 + 256 * sizeof(u16); + } + + tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); + av7110->ir.ir_handler = ir_handler; + + return 0; +} + + +void __devexit av7110_ir_exit(struct av7110 *av7110) +{ + int i; + + if (av_cnt == 0) + return; + + del_timer_sync(&av7110->ir.keyup_timer); + av7110->ir.ir_handler = NULL; + tasklet_kill(&av7110->ir.ir_tasklet); + + for (i = 0; i < av_cnt; i++) + if (av_list[i] == av7110) { + av_list[i] = av_list[av_cnt-1]; + av_list[av_cnt-1] = NULL; + break; + } + + if (av_cnt == 1) + remove_proc_entry("av7110_ir", NULL); + + input_unregister_device(av7110->ir.input_dev); + + av_cnt--; +} + +//MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); +//MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c new file mode 100644 index 000000000000..1b2d15140a1d --- /dev/null +++ b/drivers/media/pci/ttpci/av7110_v4l.c @@ -0,0 +1,966 @@ +/* + * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * the project's page is at http://www.linuxtv.org/ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_av.h" + +int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) +{ + u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; + struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; + + switch (av7110->adac_type) { + case DVB_ADAC_MSP34x0: + msgs.addr = 0x40; + break; + case DVB_ADAC_MSP34x5: + msgs.addr = 0x42; + break; + default: + return 0; + } + + if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) { + dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n", + av7110->dvb_adapter.num, reg, val); + return -EIO; + } + return 0; +} + +static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) +{ + u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; + u8 msg2[2]; + struct i2c_msg msgs[2] = { + { .flags = 0 , .len = 3, .buf = msg1 }, + { .flags = I2C_M_RD, .len = 2, .buf = msg2 } + }; + + switch (av7110->adac_type) { + case DVB_ADAC_MSP34x0: + msgs[0].addr = 0x40; + msgs[1].addr = 0x40; + break; + case DVB_ADAC_MSP34x5: + msgs[0].addr = 0x42; + msgs[1].addr = 0x42; + break; + default: + return 0; + } + + if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) { + dprintk(1, "dvb-ttpci: failed @ card %d, %u\n", + av7110->dvb_adapter.num, reg); + return -EIO; + } + *val = (msg2[0] << 8) | msg2[1]; + return 0; +} + +static struct v4l2_input inputs[4] = { + { + .index = 0, + .name = "DVB", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 1, + .tuner = 0, /* ignored */ + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + }, { + .index = 1, + .name = "Television", + .type = V4L2_INPUT_TYPE_TUNER, + .audioset = 1, + .tuner = 0, + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + }, { + .index = 2, + .name = "Video", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 0, + .tuner = 0, + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + }, { + .index = 3, + .name = "Y/C", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 0, + .tuner = 0, + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + } +}; + +static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) +{ + struct av7110 *av7110 = dev->ext_priv; + u8 buf[] = { 0x00, reg, data }; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; + + dprintk(4, "dev: %p\n", dev); + + if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) + return -1; + return 0; +} + +static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) +{ + struct av7110 *av7110 = dev->ext_priv; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; + + dprintk(4, "dev: %p\n", dev); + + if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) + return -1; + return 0; +} + +static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) +{ + u32 div; + u8 config; + u8 buf[4]; + + dprintk(4, "freq: 0x%08x\n", freq); + + /* magic number: 614. tuning with the frequency given by v4l2 + is always off by 614*62.5 = 38375 kHz...*/ + div = freq + 614; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x8e; + + if (freq < (u32) (16 * 168.25)) + config = 0xa0; + else if (freq < (u32) (16 * 447.25)) + config = 0x90; + else + config = 0x30; + config &= ~0x02; + + buf[3] = config; + + return tuner_write(dev, 0x61, buf); +} + +static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) +{ + struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + u32 div; + u8 data[4]; + + div = (freq + 38900000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xce; + + if (freq < 45000000) + return -EINVAL; + else if (freq < 137000000) + data[3] = 0x01; + else if (freq < 403000000) + data[3] = 0x02; + else if (freq < 860000000) + data[3] = 0x04; + else + return -EINVAL; + + if (av7110->fe->ops.i2c_gate_ctrl) + av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); + return tuner_write(dev, 0x63, data); +} + + + +static struct saa7146_standard analog_standard[]; +static struct saa7146_standard dvb_standard[]; +static struct saa7146_standard standard[]; + +static struct v4l2_audio msp3400_v4l2_audio = { + .index = 0, + .name = "Television", + .capability = V4L2_AUDCAP_STEREO +}; + +static int av7110_dvb_c_switch(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + u16 adswitch; + int source, sync, err; + + dprintk(4, "%p\n", av7110); + + 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 (err != 0) { + dprintk(2, "suspending video failed\n"); + vv->ov_suspend = NULL; + } + } + + if (0 != av7110->current_input) { + dprintk(1, "switching to analog TV:\n"); + adswitch = 1; + source = SAA7146_HPS_SOURCE_PORT_B; + sync = SAA7146_HPS_SYNC_PORT_B; + memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); + + switch (av7110->current_input) { + case 1: + dprintk(1, "switching SAA7113 to Analog Tuner Input\n"); + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source + msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source + msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source + msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) + dprintk(1, "setting band in demodulator failed\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF) + } + if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1) + dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); + break; + case 2: + dprintk(1, "switching SAA7113 to Video AV CVBS Input\n"); + if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1) + dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); + break; + case 3: + dprintk(1, "switching SAA7113 to Video AV Y/C Input\n"); + if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1) + dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); + break; + default: + dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n"); + } + } else { + adswitch = 0; + source = SAA7146_HPS_SOURCE_PORT_A; + sync = SAA7146_HPS_SYNC_PORT_A; + memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); + dprintk(1, "switching DVB mode\n"); + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source + msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source + msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source + msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) + } + } + + /* hmm, this does not do anything!? */ + if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) + dprintk(1, "ADSwitch error\n"); + + saa7146_set_hps_source_and_sync(dev, source, sync); + + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + u16 stereo_det; + s8 stereo; + + dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); + + if (!av7110->analog_tuner_flags || t->index != 0) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strcpy((char *)t->name, "Television"); + + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ + t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ + /* FIXME: add the real signal strength here */ + t->signal = 0xffff; + t->afc = 0; + + /* FIXME: standard / stereo detection is still broken */ + msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det); + dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det); + msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det); + dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det); + stereo = (s8)(stereo_det >> 8); + if (stereo > 0x10) { + /* stereo */ + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + t->audmode = V4L2_TUNER_MODE_STEREO; + } else if (stereo < -0x10) { + /* bilingual */ + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + t->audmode = V4L2_TUNER_MODE_LANG1; + } else /* mono */ + t->rxsubchans = V4L2_TUNER_SUB_MONO; + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + u16 fm_matrix, src; + dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); + + if (!av7110->analog_tuner_flags || av7110->current_input != 1) + return -EINVAL; + + switch (t->audmode) { + case V4L2_TUNER_MODE_STEREO: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"); + fm_matrix = 0x3001; /* stereo */ + src = 0x0020; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"); + fm_matrix = 0x3000; /* bilingual */ + src = 0x0020; + break; + case V4L2_TUNER_MODE_LANG1: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"); + fm_matrix = 0x3000; /* mono */ + src = 0x0000; + break; + case V4L2_TUNER_MODE_LANG2: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"); + fm_matrix = 0x3000; /* mono */ + src = 0x0010; + break; + default: /* case V4L2_TUNER_MODE_MONO: */ + dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n"); + fm_matrix = 0x3000; /* mono */ + src = 0x0030; + break; + } + msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix); + msp_writereg(av7110, MSP_WR_DSP, 0x0008, src); + msp_writereg(av7110, MSP_WR_DSP, 0x0009, src); + msp_writereg(av7110, MSP_WR_DSP, 0x000a, src); + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency); + + if (!av7110->analog_tuner_flags || av7110->current_input != 1) + return -EINVAL; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = av7110->current_freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency); + + if (!av7110->analog_tuner_flags || av7110->current_input != 1) + return -EINVAL; + + if (V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */ + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); + + /* tune in desired frequency */ + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) + ves1820_set_tv_freq(dev, f->frequency); + else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) + stv0297_set_tv_freq(dev, f->frequency); + av7110->current_freq = f->frequency; + + msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */ + msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */ + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */ + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); + + if (av7110->analog_tuner_flags) { + if (i->index >= 4) + return -EINVAL; + } else { + if (i->index != 0) + return -EINVAL; + } + + memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); + + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + *input = av7110->current_input; + dprintk(2, "VIDIOC_G_INPUT: %d\n", *input); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_INPUT: %d\n", input); + + if (!av7110->analog_tuner_flags) + return input ? -EINVAL : 0; + + if (input >= 4) + return -EINVAL; + + av7110->current_input = input; + return av7110_dvb_c_switch(fh); +} + +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); + if (a->index != 0) + return -EINVAL; + *a = msp3400_v4l2_audio; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); + if (a->index != 0) + return -EINVAL; + if (av7110->current_input >= 2) + return -EINVAL; + *a = msp3400_v4l2_audio; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); + if (av7110->current_input >= 2) + return -EINVAL; + return a->index ? -EINVAL : 0; +} + +static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, + struct v4l2_sliced_vbi_cap *cap) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n"); + if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + return -EINVAL; + if (FW_VERSION(av7110->arm_app) >= 0x2623) { + cap->service_set = V4L2_SLICED_WSS_625; + cap->service_lines[0][23] = V4L2_SLICED_WSS_625; + } + return 0; +} + +static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_FMT:\n"); + if (FW_VERSION(av7110->arm_app) < 0x2623) + return -EINVAL; + memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); + if (av7110->wssMode) { + f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; + f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); + } + return 0; +} + +static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_FMT\n"); + if (FW_VERSION(av7110->arm_app) < 0x2623) + return -EINVAL; + if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 && + f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) { + memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); + /* WSS controlled by firmware */ + av7110->wssMode = 0; + av7110->wssData = 0; + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, + SetWSSConfig, 1, 0); + } else { + memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); + f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; + f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); + /* WSS controlled by userspace */ + av7110->wssMode = 1; + av7110->wssData = 0; + } + return 0; +} + +static int av7110_vbi_reset(struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + + dprintk(2, "%s\n", __func__); + av7110->wssMode = 0; + av7110->wssData = 0; + if (FW_VERSION(av7110->arm_app) < 0x2623) + return 0; + else + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0); +} + +static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + struct v4l2_sliced_vbi_data d; + int rc; + + dprintk(2, "%s\n", __func__); + if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) + return -EINVAL; + if (copy_from_user(&d, data, count)) + return -EFAULT; + if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) + return -EINVAL; + if (d.id) + av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; + else + av7110->wssData = 0x8000; + rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); + return (rc < 0) ? rc : count; +} + +/**************************************************************************** + * INITIALIZATION + ****************************************************************************/ + +static u8 saa7113_init_regs[] = { + 0x02, 0xd0, + 0x03, 0x23, + 0x04, 0x00, + 0x05, 0x00, + 0x06, 0xe9, + 0x07, 0x0d, + 0x08, 0x98, + 0x09, 0x02, + 0x0a, 0x80, + 0x0b, 0x40, + 0x0c, 0x40, + 0x0d, 0x00, + 0x0e, 0x01, + 0x0f, 0x7c, + 0x10, 0x48, + 0x11, 0x0c, + 0x12, 0x8b, + 0x13, 0x1a, + 0x14, 0x00, + 0x15, 0x00, + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1b, 0x00, + 0x1c, 0x00, + 0x1d, 0x00, + 0x1e, 0x00, + + 0x41, 0x77, + 0x42, 0x77, + 0x43, 0x77, + 0x44, 0x77, + 0x45, 0x77, + 0x46, 0x77, + 0x47, 0x77, + 0x48, 0x77, + 0x49, 0x77, + 0x4a, 0x77, + 0x4b, 0x77, + 0x4c, 0x77, + 0x4d, 0x77, + 0x4e, 0x77, + 0x4f, 0x77, + 0x50, 0x77, + 0x51, 0x77, + 0x52, 0x77, + 0x53, 0x77, + 0x54, 0x77, + 0x55, 0x77, + 0x56, 0x77, + 0x57, 0xff, + + 0xff +}; + + +static struct saa7146_ext_vv av7110_vv_data_st; +static struct saa7146_ext_vv av7110_vv_data_c; + +int av7110_init_analog_module(struct av7110 *av7110) +{ + u16 version1, version2; + + if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 && + i2c_writereg(av7110, 0x80, 0x0, 0) == 1) { + pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_MSP34x0; + } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 && + i2c_writereg(av7110, 0x84, 0x0, 0) == 1) { + pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_MSP34x5; + } else + return -ENODEV; + + msleep(100); // the probing above resets the msp... + msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); + msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); + dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", + av7110->dvb_adapter.num, version1, version2); + msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source + msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source + msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume + msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume + msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART + + if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { + pr_info("saa7113 not accessible\n"); + } else { + u8 *i = saa7113_init_regs; + + if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { + /* Fujitsu/Siemens DVB-Cable */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; + } + + /* setup for DVB by default */ + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) + } + + /* init the saa7113 */ + while (*i != 0xff) { + if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { + dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num); + break; + } + i += 2; + } + /* setup msp for analog sound: B/G Dual-FM */ + msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG + msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz + msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI + msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz + msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI + msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2 + } + + memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); + /* set dd1 stream a & b */ + saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); + saa7146_write(av7110->dev, DD1_INIT, 0x03000700); + saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + return 0; +} + +int av7110_init_v4l(struct av7110 *av7110) +{ + struct saa7146_dev* dev = av7110->dev; + struct saa7146_ext_vv *vv_data; + int ret; + + /* special case DVB-C: these cards have an analog tuner + plus need some special handling, so we have separate + saa7146_ext_vv data for these... */ + if (av7110->analog_tuner_flags) + vv_data = &av7110_vv_data_c; + else + vv_data = &av7110_vv_data_st; + ret = saa7146_vv_init(dev, vv_data); + + if (ret) { + ERR("cannot init capture device. skipping\n"); + return -ENODEV; + } + vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data->vid_ops.vidioc_g_input = vidioc_g_input; + vv_data->vid_ops.vidioc_s_input = vidioc_s_input; + vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; + vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; + + vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; + vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; + vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; + vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; + + if (FW_VERSION(av7110->arm_app) < 0x2623) + vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; + + if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { + ERR("cannot register capture device. skipping\n"); + saa7146_vv_release(dev); + return -ENODEV; + } + if (FW_VERSION(av7110->arm_app) >= 0x2623) { + if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) + ERR("cannot register vbi v4l2 device. skipping\n"); + } + return 0; +} + +int av7110_exit_v4l(struct av7110 *av7110) +{ + struct saa7146_dev* dev = av7110->dev; + + saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); + saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); + + saa7146_vv_release(dev); + + return 0; +} + + + +/* FIXME: these values are experimental values that look better than the + values from the latest "official" driver -- at least for me... (MiHu) */ +static struct saa7146_standard standard[] = { + { + .name = "PAL", .id = V4L2_STD_PAL_BG, + .v_offset = 0x15, .v_field = 288, + .h_offset = 0x48, .h_pixels = 708, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x10, .v_field = 244, + .h_offset = 0x40, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + } +}; + +static struct saa7146_standard analog_standard[] = { + { + .name = "PAL", .id = V4L2_STD_PAL_BG, + .v_offset = 0x1b, .v_field = 288, + .h_offset = 0x08, .h_pixels = 708, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x10, .v_field = 244, + .h_offset = 0x40, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + } +}; + +static struct saa7146_standard dvb_standard[] = { + { + .name = "PAL", .id = V4L2_STD_PAL_BG, + .v_offset = 0x14, .v_field = 288, + .h_offset = 0x48, .h_pixels = 708, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x10, .v_field = 244, + .h_offset = 0x40, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + } +}; + +static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) +{ + struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + + if (std->id & V4L2_STD_PAL) { + av7110->vidmode = AV7110_VIDEO_MODE_PAL; + av7110_set_vidmode(av7110, av7110->vidmode); + } + else if (std->id & V4L2_STD_NTSC) { + av7110->vidmode = AV7110_VIDEO_MODE_NTSC; + av7110_set_vidmode(av7110, av7110->vidmode); + } + else + return -1; + + return 0; +} + + +static struct saa7146_ext_vv av7110_vv_data_st = { + .inputs = 1, + .audios = 1, + .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, + .flags = 0, + + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), + .std_callback = &std_callback, + + .vbi_fops.open = av7110_vbi_reset, + .vbi_fops.release = av7110_vbi_reset, + .vbi_fops.write = av7110_vbi_write, +}; + +static struct saa7146_ext_vv av7110_vv_data_c = { + .inputs = 1, + .audios = 1, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, + .flags = SAA7146_USE_PORT_B_FOR_VBI, + + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), + .std_callback = &std_callback, + + .vbi_fops.open = av7110_vbi_reset, + .vbi_fops.release = av7110_vbi_reset, + .vbi_fops.write = av7110_vbi_write, +}; + diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c new file mode 100644 index 000000000000..12ddb53c58dc --- /dev/null +++ b/drivers/media/pci/ttpci/budget-av.c @@ -0,0 +1,1640 @@ +/* + * budget-av.c: driver for the SAA7146 based Budget DVB cards + * with analog video in + * + * Compiled from various sources by Michael Hunold + * + * CI interface support (c) 2004 Olivier Gournet & + * Andrew de Quincey + * + * Copyright (C) 2002 Ralph Metzler + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "budget.h" +#include "stv0299.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "tda8261.h" +#include "tda8261_cfg.h" +#include "tda1002x.h" +#include "tda1004x.h" +#include "tua6100.h" +#include "dvb-pll.h" +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_ca_en50221.h" + +#define DEBICICAM 0x02420000 + +#define SLOTSTATUS_NONE 1 +#define SLOTSTATUS_PRESENT 2 +#define SLOTSTATUS_RESET 4 +#define SLOTSTATUS_READY 8 +#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct budget_av { + struct budget budget; + struct video_device *vd; + int cur_input; + int has_saa7113; + struct tasklet_struct ciintf_irq_tasklet; + int slot_status; + struct dvb_ca_en50221 ca; + u8 reinitialise_demod:1; +}; + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); + + +/* GPIO Connections: + * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! + * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory + * 2 - CI Card Enable (Active Low) + * 3 - CI Card Detect + */ + +/**************************************************************************** + * INITIALIZATION + ****************************************************************************/ + +static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg) +{ + u8 mm1[] = { 0x00 }; + u8 mm2[] = { 0x00 }; + struct i2c_msg msgs[2]; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = id / 2; + mm1[0] = reg; + msgs[0].len = 1; + msgs[1].len = 1; + msgs[0].buf = mm1; + msgs[1].buf = mm2; + + i2c_transfer(i2c, msgs, 2); + + return mm2[0]; +} + +static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len) +{ + u8 mm1[] = { reg }; + struct i2c_msg msgs[2] = { + {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, + {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} + }; + + if (i2c_transfer(i2c, msgs, 2) != 2) + return -EIO; + + return 0; +} + +static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) +{ + u8 msg[2] = { reg, val }; + struct i2c_msg msgs; + + msgs.flags = 0; + msgs.addr = id / 2; + msgs.len = 2; + msgs.buf = msg; + return i2c_transfer(i2c, &msgs, 1); +} + +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + udelay(1); + + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 1\n"); + } + return result; +} + +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + udelay(1); + + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 2\n"); + } + return result; +} + +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + udelay(1); + + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 3\n"); + return -ETIMEDOUT; + } + return result; +} + +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + udelay(1); + + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 5\n"); + } + return result; +} + +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_reset\n"); + budget_av->slot_status = SLOTSTATUS_RESET; + + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ + msleep(2); + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ + msleep(20); /* 20 ms Vcc settling time */ + + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + msleep(20); + + /* reinitialise the frontend if necessary */ + if (budget_av->reinitialise_demod) + dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); + + return 0; +} + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_shutdown\n"); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + budget_av->slot_status = SLOTSTATUS_NONE; + + return 0; +} + +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + + return 0; +} + +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + int result; + + if (slot != 0) + return -EINVAL; + + /* test the card detect line - needs to be done carefully + * since it never goes high for some CAMs on this interface (e.g. topuptv) */ + if (budget_av->slot_status == SLOTSTATUS_NONE) { + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + udelay(1); + if (saa7146_read(saa, PSR) & MASK_06) { + if (budget_av->slot_status == SLOTSTATUS_NONE) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + pr_info("cam inserted A\n"); + } + } + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + } + + /* We also try and read from IO memory to work round the above detection bug. If + * there is no CAM, we will get a timeout. Only done if there is no cam + * present, since this test actually breaks some cams :( + * + * if the CI interface is not open, we also do the above test since we + * don't care if the cam has problems - we'll be resetting it on open() anyway */ + if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); + if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + pr_info("cam inserted B\n"); + } else if (result < 0) { + if (budget_av->slot_status != SLOTSTATUS_NONE) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 5\n"); + return 0; + } + } + } + + /* read from attribute memory in reset/ready state to know when the CAM is ready */ + if (budget_av->slot_status == SLOTSTATUS_RESET) { + result = ciintf_read_attribute_mem(ca, slot, 0); + if (result == 0x1d) { + budget_av->slot_status = SLOTSTATUS_READY; + } + } + + /* work out correct return code */ + if (budget_av->slot_status != SLOTSTATUS_NONE) { + if (budget_av->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } + return 0; +} + +static int ciintf_init(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + int result; + + memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + + /* Enable DEBI pins */ + saa7146_write(saa, MC1, MASK_27 | MASK_11); + + /* register CI interface */ + budget_av->ca.owner = THIS_MODULE; + budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; + budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; + budget_av->ca.read_cam_control = ciintf_read_cam_control; + budget_av->ca.write_cam_control = ciintf_write_cam_control; + budget_av->ca.slot_reset = ciintf_slot_reset; + budget_av->ca.slot_shutdown = ciintf_slot_shutdown; + budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_av->ca.poll_slot_status = ciintf_poll_slot_status; + budget_av->ca.data = budget_av; + budget_av->budget.ci_present = 1; + budget_av->slot_status = SLOTSTATUS_NONE; + + if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, + &budget_av->ca, 0, 1)) != 0) { + pr_err("ci initialisation failed\n"); + goto error; + } + + pr_info("ci interface initialised\n"); + return 0; + +error: + saa7146_write(saa, MC1, MASK_27); + return result; +} + +static void ciintf_deinit(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + + /* release the CA device */ + dvb_ca_en50221_release(&budget_av->ca); + + /* disable DEBI pins */ + saa7146_write(saa, MC1, MASK_27); +} + + +static const u8 saa7113_tab[] = { + 0x01, 0x08, + 0x02, 0xc0, + 0x03, 0x33, + 0x04, 0x00, + 0x05, 0x00, + 0x06, 0xeb, + 0x07, 0xe0, + 0x08, 0x28, + 0x09, 0x00, + 0x0a, 0x80, + 0x0b, 0x47, + 0x0c, 0x40, + 0x0d, 0x00, + 0x0e, 0x01, + 0x0f, 0x44, + + 0x10, 0x08, + 0x11, 0x0c, + 0x12, 0x7b, + 0x13, 0x00, + 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + + 0x57, 0xff, + 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, + 0x5b, 0x83, 0x5e, 0x00, + 0xff +}; + +static int saa7113_init(struct budget_av *budget_av) +{ + struct budget *budget = &budget_av->budget; + struct saa7146_dev *saa = budget->dev; + const u8 *data = saa7113_tab; + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); + msleep(200); + + if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { + dprintk(1, "saa7113 not found on KNC card\n"); + return -ENODEV; + } + + dprintk(1, "saa7113 detected and initializing\n"); + + while (*data != 0xff) { + i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); + data += 2; + } + + dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); + + return 0; +} + +static int saa7113_setinput(struct budget_av *budget_av, int input) +{ + struct budget *budget = &budget_av->budget; + + if (1 != budget_av->has_saa7113) + return -ENODEV; + + if (input == 1) { + i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7); + i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80); + } else if (input == 0) { + i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0); + i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00); + } else + return -EINVAL; + + budget_av->cur_input = input; + return 0; +} + + +static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + u8 m1; + + aclk = 0xb5; + if (srate < 2000000) + bclk = 0x86; + else if (srate < 5000000) + bclk = 0x89; + else if (srate < 15000000) + bclk = 0x8f; + else if (srate < 45000000) + bclk = 0x95; + + m1 = 0x14; + if (srate < 4000000) + m1 = 0x10; + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + stv0299_writereg(fe, 0x0f, 0x80 | m1); + + return 0; +} + +static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 div; + u8 buf[4]; + struct budget *budget = (struct budget *) fe->dvb->priv; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((c->frequency < 950000) || (c->frequency > 2150000)) + return -EINVAL; + + div = (c->frequency + (125 - 1)) / 125; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0x20; + + if (c->symbol_rate < 4000000) + buf[3] |= 1; + + if (c->frequency < 1250000) + buf[3] |= 0; + else if (c->frequency < 1550000) + buf[3] |= 0x40; + else if (c->frequency < 2050000) + buf[3] |= 0x80; + else if (c->frequency < 2150000) + buf[3] |= 0xC0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static u8 typhoon_cinergy1200s_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x92, + 0xff, 0xff +}; + +static struct stv0299_config typhoon_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + + +static struct stv0299_config cinergy_1200s_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + +static struct stv0299_config cinergy_1200s_1894_0010_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + +static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 buf[6]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + int i; + +#define CU1216_IF 36125000 +#define TUNER_MUL 62500 + + u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0xce; + buf[3] = (c->frequency < 150000000 ? 0x01 : + c->frequency < 445000000 ? 0x02 : 0x04); + buf[4] = 0xde; + buf[5] = 0x20; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + + /* wait for the pll lock */ + msg.flags = I2C_M_RD; + msg.len = 1; + for (i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) + break; + msleep(10); + } + + /* switch the charge pump to the lower current */ + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[2]; + buf[2] &= ~0x40; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct tda1002x_config philips_cu1216_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static struct tda1002x_config philips_cu1216_config_altaddress = { + .demod_address = 0x0d, + .invert = 0, +}; + +static struct tda10023_config philips_cu1216_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static int philips_tu1216_tuner_init(struct dvb_frontend *fe) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; + + // setup PLL configuration + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + return 0; +} + +static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = + sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = c->frequency + 36166000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (c->frequency < 49000000) + return -EINVAL; + else if (c->frequency < 161000000) + band = 1; + else if (c->frequency < 444000000) + band = 2; + else if (c->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter + switch (c->bandwidth_hz) { + case 6000000: + filter = 0; + break; + + case 7000000: + filter = 0; + break; + + case 8000000: + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; + + // setup tuner buffer + tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tu1216_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + +static struct tda1004x_config philips_tu1216_config = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 1, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tu1216_request_firmware, +}; + +static u8 philips_sd1878_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, + 0x05, 0x35, + 0x06, 0x40, + 0x07, 0x00, + 0x08, 0x43, + 0x09, 0x02, + 0x0C, 0x51, + 0x0D, 0x82, + 0x0E, 0x23, + 0x10, 0x3f, + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x93, + 0xff, 0xff +}; + +static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + u8 m1; + + aclk = 0xb5; + if (srate < 2000000) + bclk = 0x86; + else if (srate < 5000000) + bclk = 0x89; + else if (srate < 15000000) + bclk = 0x8f; + else if (srate < 45000000) + bclk = 0x95; + + m1 = 0x14; + if (srate < 4000000) + m1 = 0x10; + + stv0299_writereg(fe, 0x0e, 0x23); + stv0299_writereg(fe, 0x0f, 0x94); + stv0299_writereg(fe, 0x10, 0x39); + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x15, 0xc9); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + stv0299_writereg(fe, 0x0f, 0x80 | m1); + + return 0; +} + +static struct stv0299_config philips_sd1878_config = { + .demod_address = 0x68, + .inittab = philips_sd1878_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, +}; + +/* KNC1 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x08 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x33 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xee }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +/* STB0899 demodulator config for the KNC1 and clones */ +static struct stb0899_config knc1_dvbs2_config = { + .init_dev = knc1_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = knc1_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, +// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ + .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ +// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_OFF, /* 1 */ + + .lo_clk = 76500000, + .hi_clk = 90000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = tda8261_get_frequency, + .tuner_set_frequency = tda8261_set_frequency, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = tda8261_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +/* + * SD1878/SHA tuner config + * 1F, Single I/P, Horizontal mount, High Sensitivity + */ +static const struct tda8261_config sd1878c_config = { +// .name = "SD1878/SHA", + .addr = 0x60, + .step_size = TDA8261_STEP_1000 /* kHz */ +}; + +static u8 read_pwm(struct budget_av *budget_av) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, + {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} + }; + + if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) + || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +#define SUBID_DVBS_KNC1 0x0010 +#define SUBID_DVBS_KNC1_PLUS 0x0011 +#define SUBID_DVBS_TYPHOON 0x4f56 +#define SUBID_DVBS_CINERGY1200 0x1154 +#define SUBID_DVBS_CYNERGY1200N 0x1155 +#define SUBID_DVBS_TV_STAR 0x0014 +#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 +#define SUBID_DVBS_TV_STAR_CI 0x0016 +#define SUBID_DVBS2_KNC1 0x0018 +#define SUBID_DVBS2_KNC1_OEM 0x0019 +#define SUBID_DVBS_EASYWATCH_1 0x001a +#define SUBID_DVBS_EASYWATCH_2 0x001b +#define SUBID_DVBS2_EASYWATCH 0x001d +#define SUBID_DVBS_EASYWATCH 0x001e + +#define SUBID_DVBC_EASYWATCH 0x002a +#define SUBID_DVBC_EASYWATCH_MK3 0x002c +#define SUBID_DVBC_KNC1 0x0020 +#define SUBID_DVBC_KNC1_PLUS 0x0021 +#define SUBID_DVBC_KNC1_MK3 0x0022 +#define SUBID_DVBC_KNC1_TDA10024 0x0028 +#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 +#define SUBID_DVBC_CINERGY1200 0x1156 +#define SUBID_DVBC_CINERGY1200_MK3 0x1176 + +#define SUBID_DVBT_EASYWATCH 0x003a +#define SUBID_DVBT_KNC1_PLUS 0x0031 +#define SUBID_DVBT_KNC1 0x0030 +#define SUBID_DVBT_CINERGY1200 0x1157 + +static void frontend_init(struct budget_av *budget_av) +{ + struct saa7146_dev * saa = budget_av->budget.dev; + struct dvb_frontend * fe = NULL; + + /* Enable / PowerON Frontend */ + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + + /* Wait for PowerON */ + msleep(100); + + /* additional setup necessary for the PLUS cards */ + switch (saa->pci->subsystem_device) { + case SUBID_DVBS_KNC1_PLUS: + case SUBID_DVBC_KNC1_PLUS: + case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBC_EASYWATCH: + case SUBID_DVBC_KNC1_PLUS_MK3: + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); + break; + } + + switch (saa->pci->subsystem_device) { + + case SUBID_DVBS_KNC1: + /* + * maybe that setting is needed for other dvb-s cards as well, + * but so far it has been only confirmed for this type + */ + budget_av->reinitialise_demod = 1; + /* fall through */ + case SUBID_DVBS_KNC1_PLUS: + case SUBID_DVBS_EASYWATCH_1: + if (saa->pci->subsystem_vendor == 0x1894) { + fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, + &budget_av->budget.i2c_adap); + if (fe) { + dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); + } + } else { + fe = dvb_attach(stv0299_attach, &typhoon_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + } + break; + + case SUBID_DVBS_TV_STAR: + case SUBID_DVBS_TV_STAR_PLUS_X4: + case SUBID_DVBS_TV_STAR_CI: + case SUBID_DVBS_CYNERGY1200N: + case SUBID_DVBS_EASYWATCH: + case SUBID_DVBS_EASYWATCH_2: + fe = dvb_attach(stv0299_attach, &philips_sd1878_config, + &budget_av->budget.i2c_adap); + if (fe) { + dvb_attach(dvb_pll_attach, fe, 0x60, + &budget_av->budget.i2c_adap, + DVB_PLL_PHILIPS_SD1878_TDA8261); + } + break; + + case SUBID_DVBS_TYPHOON: + fe = dvb_attach(stv0299_attach, &typhoon_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + break; + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: + budget_av->reinitialise_demod = 1; + if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) + dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); + + break; + case SUBID_DVBS_CINERGY1200: + fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + break; + + case SUBID_DVBC_KNC1: + case SUBID_DVBC_KNC1_PLUS: + case SUBID_DVBC_CINERGY1200: + case SUBID_DVBC_EASYWATCH: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10021_attach, &philips_cu1216_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe == NULL) + fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBC_EASYWATCH_MK3: + case SUBID_DVBC_CINERGY1200_MK3: + case SUBID_DVBC_KNC1_MK3: + case SUBID_DVBC_KNC1_TDA10024: + case SUBID_DVBC_KNC1_PLUS_MK3: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10023_attach, + &philips_cu1216_tda10023_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBT_EASYWATCH: + case SUBID_DVBT_KNC1: + case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBT_CINERGY1200: + budget_av->reinitialise_demod = 1; + fe = dvb_attach(tda10046_attach, &philips_tu1216_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.init = philips_tu1216_tuner_init; + fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params; + } + break; + } + + if (fe == NULL) { + pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + saa->pci->vendor, + saa->pci->device, + saa->pci->subsystem_vendor, + saa->pci->subsystem_device); + return; + } + + budget_av->budget.dvb_frontend = fe; + + if (dvb_register_frontend(&budget_av->budget.dvb_adapter, + budget_av->budget.dvb_frontend)) { + pr_err("Frontend registration failed!\n"); + dvb_frontend_detach(budget_av->budget.dvb_frontend); + budget_av->budget.dvb_frontend = NULL; + } +} + + +static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) +{ + struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + + dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); + + if (*isr & MASK_10) + ttpci_budget_irq10_handler(dev, isr); +} + +static int budget_av_detach(struct saa7146_dev *dev) +{ + struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + int err; + + dprintk(2, "dev: %p\n", dev); + + if (1 == budget_av->has_saa7113) { + saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); + + msleep(200); + + saa7146_unregister_device(&budget_av->vd, dev); + + saa7146_vv_release(dev); + } + + if (budget_av->budget.ci_present) + ciintf_deinit(budget_av); + + if (budget_av->budget.dvb_frontend != NULL) { + dvb_unregister_frontend(budget_av->budget.dvb_frontend); + dvb_frontend_detach(budget_av->budget.dvb_frontend); + } + err = ttpci_budget_deinit(&budget_av->budget); + + kfree(budget_av); + + return err; +} + +#define KNC1_INPUTS 2 +static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { + { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, + V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, + V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, +}; + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index); + if (i->index >= KNC1_INPUTS) + return -EINVAL; + memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + + *i = budget_av->cur_input; + + dprintk(1, "VIDIOC_G_INPUT %d\n", *i); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + + dprintk(1, "VIDIOC_S_INPUT %d\n", input); + return saa7113_setinput(budget_av, input); +} + +static struct saa7146_ext_vv vv_data; + +static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct budget_av *budget_av; + u8 *mac; + int err; + + dprintk(2, "dev: %p\n", dev); + + if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL))) + return -ENOMEM; + + budget_av->has_saa7113 = 0; + budget_av->budget.ci_present = 0; + + dev->ext_priv = budget_av; + + err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) { + kfree(budget_av); + return err; + } + + /* knc1 initialization */ + saa7146_write(dev, DD1_STREAM_B, 0x04000000); + saa7146_write(dev, DD1_INIT, 0x07000600); + saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); + + if (saa7113_init(budget_av) == 0) { + budget_av->has_saa7113 = 1; + + if (0 != saa7146_vv_init(dev, &vv_data)) { + /* fixme: proper cleanup here */ + ERR("cannot init vv subsystem\n"); + return err; + } + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + + if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { + /* fixme: proper cleanup here */ + ERR("cannot register capture v4l2 device\n"); + saa7146_vv_release(dev); + return err; + } + + /* beware: this modifies dev->vv ... */ + saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, + SAA7146_HPS_SYNC_PORT_A); + + saa7113_setinput(budget_av, 0); + } + + /* fixme: find some sane values here... */ + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + + mac = budget_av->budget.dvb_adapter.proposed_mac; + if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { + pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", + budget_av->budget.dvb_adapter.num); + memset(mac, 0, 6); + } else { + pr_info("KNC1-%d: MAC addr = %pM\n", + budget_av->budget.dvb_adapter.num, mac); + } + + budget_av->budget.dvb_adapter.priv = budget_av; + frontend_init(budget_av); + ciintf_init(budget_av); + + ttpci_budget_init_hooks(&budget_av->budget); + + return 0; +} + +static struct saa7146_standard standard[] = { + {.name = "PAL",.id = V4L2_STD_PAL, + .v_offset = 0x17,.v_field = 288, + .h_offset = 0x14,.h_pixels = 680, + .v_max_out = 576,.h_max_out = 768 }, + + {.name = "NTSC",.id = V4L2_STD_NTSC, + .v_offset = 0x16,.v_field = 240, + .h_offset = 0x06,.h_pixels = 708, + .v_max_out = 480,.h_max_out = 640, }, +}; + +static struct saa7146_ext_vv vv_data = { + .inputs = 2, + .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 + .flags = 0, + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), +}; + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); +MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); +MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); +MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); +MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); +MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); +MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T); +MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); +MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP); +MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024); +MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); +MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); +MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); +MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); +MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); + +static struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), + MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), + MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), + MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), + MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), + MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), + MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), + MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), + MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), + MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), + MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), + MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), + MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), + MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), + MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a), + MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), + MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), + MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), + MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028), + MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), + MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), + MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), + MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), + MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), + MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), + MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), + MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget_av", + .flags = SAA7146_USE_I2C_IRQ, + + .pci_tbl = pci_tbl, + + .module = THIS_MODULE, + .attach = budget_av_attach, + .detach = budget_av_detach, + + .irq_mask = MASK_10, + .irq_func = budget_av_irq, +}; + +static int __init budget_av_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_av_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_av_init); +module_exit(budget_av_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called " + "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c new file mode 100644 index 000000000000..98e524178765 --- /dev/null +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -0,0 +1,1591 @@ +/* + * budget-ci.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold + * + * msp430 IR support contributed by Jack Thomasson + * partially based on the Siemens DVB driver by Ralph+Marcus Metzler + * + * CI interface support (c) 2004 Andrew de Quincey + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + +#include +#include +#include +#include +#include +#include + +#include "budget.h" + +#include "dvb_ca_en50221.h" +#include "stv0299.h" +#include "stv0297.h" +#include "tda1004x.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "stb6100.h" +#include "stb6100_cfg.h" +#include "lnbp21.h" +#include "bsbe1.h" +#include "bsru6.h" +#include "tda1002x.h" +#include "tda827x.h" +#include "bsbe1-d01a.h" + +#define MODULE_NAME "budget_ci" + +/* + * Regarding DEBIADDR_IR: + * Some CI modules hang if random addresses are read. + * Using address 0x4000 for the IR read means that we + * use the same address as for CI version, which should + * be a safe default. + */ +#define DEBIADDR_IR 0x4000 +#define DEBIADDR_CICONTROL 0x0000 +#define DEBIADDR_CIVERSION 0x4000 +#define DEBIADDR_IO 0x1000 +#define DEBIADDR_ATTR 0x3000 + +#define CICONTROL_RESET 0x01 +#define CICONTROL_ENABLETS 0x02 +#define CICONTROL_CAMDETECT 0x08 + +#define DEBICICTL 0x00420000 +#define DEBICICAM 0x02420000 + +#define SLOTSTATUS_NONE 1 +#define SLOTSTATUS_PRESENT 2 +#define SLOTSTATUS_RESET 4 +#define SLOTSTATUS_READY 8 +#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) + +/* RC5 device wildcard */ +#define IR_DEVICE_ANY 255 + +static int rc5_device = -1; +module_param(rc5_device, int, 0644); +MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); + +static int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct budget_ci_ir { + struct rc_dev *dev; + struct tasklet_struct msp430_irq_tasklet; + char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ + char phys[32]; + int rc5_device; + u32 ir_key; + bool have_command; + bool full_rc5; /* Outputs a full RC5 code */ +}; + +struct budget_ci { + struct budget budget; + struct tasklet_struct ciintf_irq_tasklet; + int slot_status; + int ci_irq; + struct dvb_ca_en50221 ca; + struct budget_ci_ir ir; + u8 tuner_pll_address; /* used for philips_tdm1316l configs */ +}; + +static void msp430_ir_interrupt(unsigned long data) +{ + struct budget_ci *budget_ci = (struct budget_ci *) data; + struct rc_dev *dev = budget_ci->ir.dev; + u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + + /* + * The msp430 chip can generate two different bytes, command and device + * + * type1: X1CCCCCC, C = command bits (0 - 63) + * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit + * + * Each signal from the remote control can generate one or more command + * bytes and one or more device bytes. For the repeated bytes, the + * highest bit (X) is set. The first command byte is always generated + * before the first device byte. Other than that, no specific order + * seems to apply. To make life interesting, bytes can also be lost. + * + * Only when we have a command and device byte, a keypress is + * generated. + */ + + if (ir_debug) + printk("budget_ci: received byte 0x%02x\n", command); + + /* Remove repeat bit, we use every command */ + command = command & 0x7f; + + /* Is this a RC5 command byte? */ + if (command & 0x40) { + budget_ci->ir.have_command = true; + budget_ci->ir.ir_key = command & 0x3f; + return; + } + + /* It's a RC5 device byte */ + if (!budget_ci->ir.have_command) + return; + budget_ci->ir.have_command = false; + + if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && + budget_ci->ir.rc5_device != (command & 0x1f)) + return; + + if (budget_ci->ir.full_rc5) { + rc_keydown(dev, + budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key, + (command & 0x20) ? 1 : 0); + return; + } + + /* FIXME: We should generate complete scancodes for all devices */ + rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); +} + +static int msp430_ir_init(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + struct rc_dev *dev; + int error; + + dev = rc_allocate_device(); + if (!dev) { + printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); + return -ENOMEM; + } + + snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), + "Budget-CI dvb ir receiver %s", saa->name); + snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), + "pci-%s/ir0", pci_name(saa->pci)); + + dev->driver_name = MODULE_NAME; + dev->input_name = budget_ci->ir.name; + dev->input_phys = budget_ci->ir.phys; + dev->input_id.bustype = BUS_PCI; + dev->input_id.version = 1; + if (saa->pci->subsystem_vendor) { + dev->input_id.vendor = saa->pci->subsystem_vendor; + dev->input_id.product = saa->pci->subsystem_device; + } else { + dev->input_id.vendor = saa->pci->vendor; + dev->input_id.product = saa->pci->device; + } + dev->dev.parent = &saa->pci->dev; + + if (rc5_device < 0) + budget_ci->ir.rc5_device = IR_DEVICE_ANY; + else + budget_ci->ir.rc5_device = rc5_device; + + /* Select keymap and address */ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: + case 0x100f: + case 0x1011: + case 0x1012: + /* The hauppauge keymap is a superset of these remotes */ + dev->map_name = RC_MAP_HAUPPAUGE; + budget_ci->ir.full_rc5 = true; + + if (rc5_device < 0) + budget_ci->ir.rc5_device = 0x1f; + break; + case 0x1010: + case 0x1017: + case 0x1019: + case 0x101a: + case 0x101b: + /* for the Technotrend 1500 bundled remote */ + dev->map_name = RC_MAP_TT_1500; + break; + default: + /* unknown remote */ + dev->map_name = RC_MAP_BUDGET_CI_OLD; + break; + } + if (!budget_ci->ir.full_rc5) + dev->scanmask = 0xff; + + error = rc_register_device(dev); + if (error) { + printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); + rc_free_device(dev); + return error; + } + + budget_ci->ir.dev = dev; + + tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, + (unsigned long) budget_ci); + + SAA7146_IER_ENABLE(saa, MASK_06); + saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); + + return 0; +} + +static void msp430_ir_deinit(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + + SAA7146_IER_DISABLE(saa, MASK_06); + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); + + rc_unregister_device(budget_ci->ir.dev); +} + +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); +} + +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); +} + +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, 1, 0); +} + +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, value, 1, 0); +} + +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + + if (slot != 0) + return -EINVAL; + + if (budget_ci->ci_irq) { + // trigger on RISING edge during reset so we know when READY is re-asserted + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } + budget_ci->slot_status = SLOTSTATUS_RESET; + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); + msleep(1); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + return 0; +} + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + return 0; +} + +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + int tmp; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); + + tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + tmp | CICONTROL_ENABLETS, 1, 0); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + return 0; +} + +static void ciintf_interrupt(unsigned long data) +{ + struct budget_ci *budget_ci = (struct budget_ci *) data; + struct saa7146_dev *saa = budget_ci->budget.dev; + unsigned int flags; + + // ensure we don't get spurious IRQs during initialisation + if (!budget_ci->budget.ci_present) + return; + + // read the CAM status + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + if (flags & CICONTROL_CAMDETECT) { + + // GPIO should be set to trigger on falling edge if a CAM is present + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); + + if (budget_ci->slot_status & SLOTSTATUS_NONE) { + // CAM insertion IRQ + budget_ci->slot_status = SLOTSTATUS_PRESENT; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_INSERTED); + + } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { + // CAM ready (reset completed) + budget_ci->slot_status = SLOTSTATUS_READY; + dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); + + } else if (budget_ci->slot_status & SLOTSTATUS_READY) { + // FR/DA IRQ + dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); + } + } else { + + // trigger on rising edge if a CAM is not present - when a CAM is inserted, we + // only want to get the IRQ when it sets READY. If we trigger on the falling edge, + // the CAM might not actually be ready yet. + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + + // generate a CAM removal IRQ if we haven't already + if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { + // CAM removal IRQ + budget_ci->slot_status = SLOTSTATUS_NONE; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_REMOVED); + } + } +} + +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + unsigned int flags; + + // ensure we don't get spurious IRQs during initialisation + if (!budget_ci->budget.ci_present) + return -EINVAL; + + // read the CAM status + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + if (flags & CICONTROL_CAMDETECT) { + // mark it as present if it wasn't before + if (budget_ci->slot_status & SLOTSTATUS_NONE) { + budget_ci->slot_status = SLOTSTATUS_PRESENT; + } + + // during a RESET, we check if we can read from IO memory to see when CAM is ready + if (budget_ci->slot_status & SLOTSTATUS_RESET) { + if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { + budget_ci->slot_status = SLOTSTATUS_READY; + } + } + } else { + budget_ci->slot_status = SLOTSTATUS_NONE; + } + + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + if (budget_ci->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } + + return 0; +} + +static int ciintf_init(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + int flags; + int result; + int ci_version; + int ca_flags; + + memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); + + // enable DEBI pins + saa7146_write(saa, MC1, MASK_27 | MASK_11); + + // test if it is there + ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); + if ((ci_version & 0xa0) != 0xa0) { + result = -ENODEV; + goto error; + } + + // determine whether a CAM is present or not + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + budget_ci->slot_status = SLOTSTATUS_NONE; + if (flags & CICONTROL_CAMDETECT) + budget_ci->slot_status = SLOTSTATUS_PRESENT; + + // version 0xa2 of the CI firmware doesn't generate interrupts + if (ci_version == 0xa2) { + ca_flags = 0; + budget_ci->ci_irq = 0; + } else { + ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | + DVB_CA_EN50221_FLAG_IRQ_FR | + DVB_CA_EN50221_FLAG_IRQ_DA; + budget_ci->ci_irq = 1; + } + + // register CI interface + budget_ci->ca.owner = THIS_MODULE; + budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; + budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; + budget_ci->ca.read_cam_control = ciintf_read_cam_control; + budget_ci->ca.write_cam_control = ciintf_write_cam_control; + budget_ci->ca.slot_reset = ciintf_slot_reset; + budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; + budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; + budget_ci->ca.data = budget_ci; + if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, + &budget_ci->ca, + ca_flags, 1)) != 0) { + printk("budget_ci: CI interface detected, but initialisation failed.\n"); + goto error; + } + + // Setup CI slot IRQ + if (budget_ci->ci_irq) { + tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); + } else { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } + SAA7146_IER_ENABLE(saa, MASK_03); + } + + // enable interface + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + // success! + printk("budget_ci: CI interface initialised\n"); + budget_ci->budget.ci_present = 1; + + // forge a fake CI IRQ so the CAM state is setup correctly + if (budget_ci->ci_irq) { + flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; + if (budget_ci->slot_status != SLOTSTATUS_NONE) + flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); + } + + return 0; + +error: + saa7146_write(saa, MC1, MASK_27); + return result; +} + +static void ciintf_deinit(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + + // disable CI interrupts + if (budget_ci->ci_irq) { + SAA7146_IER_DISABLE(saa, MASK_03); + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ciintf_irq_tasklet); + } + + // reset interface + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); + msleep(1); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + // disable TS data stream to CI interface + saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); + + // release the CA device + dvb_ca_en50221_release(&budget_ci->ca); + + // disable DEBI pins + saa7146_write(saa, MC1, MASK_27); +} + +static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) +{ + struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + + dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); + + if (*isr & MASK_06) + tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); + + if (*isr & MASK_10) + ttpci_budget_irq10_handler(dev, isr); + + if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) + tasklet_schedule(&budget_ci->ciintf_irq_tasklet); +} + +static u8 philips_su1278_tt_inittab[] = { + 0x01, 0x0f, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x5b, + 0x05, 0x85, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0x02, + 0x09, 0x00, + 0x0C, 0x01, + 0x0D, 0x81, + 0x0E, 0x44, + 0x0f, 0x14, + 0x10, 0x3c, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x97, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x93, + 0xff, 0xff +}; + +static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + stv0299_writereg(fe, 0x0e, 0x44); + if (srate >= 10000000) { + stv0299_writereg(fe, 0x13, 0x97); + stv0299_writereg(fe, 0x14, 0x95); + stv0299_writereg(fe, 0x15, 0xc9); + stv0299_writereg(fe, 0x17, 0x8c); + stv0299_writereg(fe, 0x1a, 0xfe); + stv0299_writereg(fe, 0x1c, 0x7f); + stv0299_writereg(fe, 0x2d, 0x09); + } else { + stv0299_writereg(fe, 0x13, 0x99); + stv0299_writereg(fe, 0x14, 0x8d); + stv0299_writereg(fe, 0x15, 0xce); + stv0299_writereg(fe, 0x17, 0x43); + stv0299_writereg(fe, 0x1a, 0x1d); + stv0299_writereg(fe, 0x1c, 0x12); + stv0299_writereg(fe, 0x2d, 0x05); + } + stv0299_writereg(fe, 0x0e, 0x23); + stv0299_writereg(fe, 0x0f, 0x94); + stv0299_writereg(fe, 0x10, 0x39); + stv0299_writereg(fe, 0x15, 0xc9); + + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u32 div; + u8 buf[4]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((p->frequency < 950000) || (p->frequency > 2150000)) + return -EINVAL; + + div = (p->frequency + (500 - 1)) / 500; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; + buf[3] = 0x20; + + if (p->symbol_rate < 4000000) + buf[3] |= 1; + + if (p->frequency < 1250000) + buf[3] |= 0; + else if (p->frequency < 1550000) + buf[3] |= 0x40; + else if (p->frequency < 2050000) + buf[3] |= 0x80; + else if (p->frequency < 2150000) + buf[3] |= 0xC0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct stv0299_config philips_su1278_tt_config = { + + .demod_address = 0x68, + .inittab = philips_su1278_tt_inittab, + .mclk = 64000000UL, + .invert = 0, + .skip_reinit = 1, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 50, + .set_symbol_rate = philips_su1278_tt_set_symbol_rate, +}; + + + +static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = + sizeof(td1316_init) }; + + // setup PLL configuration + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + // disable the mc44BC374c (do not check for errors) + tuner_msg.addr = 0x65; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); + } + + return 0; +} + +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency + 36130000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (p->frequency < 49000000) + return -EINVAL; + else if (p->frequency < 159000000) + band = 1; + else if (p->frequency < 444000000) + band = 2; + else if (p->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter and TDA9889 + switch (p->bandwidth_hz) { + case 6000000: + tda1004x_writereg(fe, 0x0C, 0x14); + filter = 0; + break; + + case 7000000: + tda1004x_writereg(fe, 0x0C, 0x80); + filter = 0; + break; + + case 8000000: + tda1004x_writereg(fe, 0x0C, 0x14); + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + + return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); +} + +static struct tda1004x_config philips_tdm1316l_config = { + + .demod_address = 0x8, + .invert = 0, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tdm1316l_request_firmware, +}; + +static struct tda1004x_config philips_tdm1316l_config_invert = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tdm1316l_request_firmware, +}; + +static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 tuner_buf[5]; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, + .flags = 0, + .buf = tuner_buf, + .len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency + 36125000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) { + cp = 3; + band = 1; + } else if (tuner_frequency < 160000000) { + cp = 5; + band = 1; + } else if (tuner_frequency < 200000000) { + cp = 6; + band = 1; + } else if (tuner_frequency < 290000000) { + cp = 3; + band = 2; + } else if (tuner_frequency < 420000000) { + cp = 5; + band = 2; + } else if (tuner_frequency < 480000000) { + cp = 6; + band = 2; + } else if (tuner_frequency < 620000000) { + cp = 3; + band = 4; + } else if (tuner_frequency < 830000000) { + cp = 5; + band = 4; + } else if (tuner_frequency < 895000000) { + cp = 7; + band = 4; + } else + return -EINVAL; + + // assume PLL filter should always be 8MHz for the moment. + filter = 1; + + // calculate divisor + tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xc8; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + tuner_buf[4] = 0x80; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(50); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + + return 0; +} + +static u8 dvbc_philips_tdm1316l_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x20, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x00, + 0x4b, 0x7b, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x10, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x04, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x38, + 0x62, 0x0a, + 0x53, 0x13, + 0x59, 0x08, + 0xff, 0xff, +}; + +static struct stv0297_config dvbc_philips_tdm1316l_config = { + .demod_address = 0x1c, + .inittab = dvbc_philips_tdm1316l_inittab, + .invert = 0, + .stop_during_read = 1, +}; + +static struct tda10023_config tda10023_config = { + .demod_address = 0xc, + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .deltaf = 0xa511, +}; + +static struct tda827x_config tda827x_config = { + .config = 0, +}; + +/* TT S2-3200 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x10 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x34 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xc7 }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +static struct stb0899_config tt3200_config = { + .init_dev = tt3200_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = tt3200_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_ON, /* 1 */ + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +static struct stb6100_config tt3200_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + +static void frontend_init(struct budget_ci *budget_ci) +{ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) + budget_ci->budget.dvb_frontend = + dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + break; + } + break; + + case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) + budget_ci->budget.dvb_frontend = + dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; + break; + } + break; + + case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) + budget_ci->tuner_pll_address = 0x61; + budget_ci->budget.dvb_frontend = + dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) + budget_ci->tuner_pll_address = 0x63; + budget_ci->budget.dvb_frontend = + dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) + budget_ci->tuner_pll_address = 0x60; + budget_ci->budget.dvb_frontend = + dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1017: // TT S-1500 PCI + budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + + budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ + budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) { + printk(KERN_ERR "%s: No tda827x found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */ + budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) { + if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { + printk(KERN_ERR "%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } else { + printk(KERN_ERR "%s: No STB6000 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x1019: // TT S2-3200 PCI + /* + * NOTE! on some STB0899 versions, the internal PLL takes a longer time + * to settle, aka LOCK. On the older revisions of the chip, we don't see + * this, as a result on the newer chips the entire clock tree, will not + * be stable after a freshly POWER 'ed up situation. + * In this case, we should RESET the STB0899 (Active LOW) and wait for + * PLL stabilization. + * + * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is + * connected to the SAA7146 GPIO, GPIO2, Pin 142 + */ + /* Reset Demodulator */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); + /* Wait for everything to die */ + msleep(50); + /* Pull it up out of Reset state */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); + /* Wait for PLL to stabilize */ + msleep(250); + /* + * PLL state should be stable now. Ideally, we should check + * for PLL LOCK status. But well, never mind! + */ + budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { + if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { + printk("%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } else { + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + } + + if (budget_ci->budget.dvb_frontend == NULL) { + printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget_ci->budget.dev->pci->vendor, + budget_ci->budget.dev->pci->device, + budget_ci->budget.dev->pci->subsystem_vendor, + budget_ci->budget.dev->pci->subsystem_device); + } else { + if (dvb_register_frontend + (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { + printk("budget-ci: Frontend registration failed!\n"); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } +} + +static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct budget_ci *budget_ci; + int err; + + budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); + if (!budget_ci) { + err = -ENOMEM; + goto out1; + } + + dprintk(2, "budget_ci: %p\n", budget_ci); + + dev->ext_priv = budget_ci; + + err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) + goto out2; + + err = msp430_ir_init(budget_ci); + if (err) + goto out3; + + ciintf_init(budget_ci); + + budget_ci->budget.dvb_adapter.priv = budget_ci; + frontend_init(budget_ci); + + ttpci_budget_init_hooks(&budget_ci->budget); + + return 0; + +out3: + ttpci_budget_deinit(&budget_ci->budget); +out2: + kfree(budget_ci); +out1: + return err; +} + +static int budget_ci_detach(struct saa7146_dev *dev) +{ + struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + struct saa7146_dev *saa = budget_ci->budget.dev; + int err; + + if (budget_ci->budget.ci_present) + ciintf_deinit(budget_ci); + msp430_ir_deinit(budget_ci); + if (budget_ci->budget.dvb_frontend) { + dvb_unregister_frontend(budget_ci->budget.dvb_frontend); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + } + err = ttpci_budget_deinit(&budget_ci->budget); + + // disable frontend and CI interface + saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); + + kfree(budget_ci); + + return err; +} + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); +MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT); + +static struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), + MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), + MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010), + MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), + MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), + MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), + MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), + MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), + MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget_ci dvb", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = &pci_tbl[0], + .attach = budget_ci_attach, + .detach = budget_ci_detach, + + .irq_mask = MASK_03 | MASK_06 | MASK_10, + .irq_func = budget_ci_irq, +}; + +static int __init budget_ci_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_ci_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_ci_init); +module_exit(budget_ci_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called " + "budget PCI DVB cards w/ CI-module produced by " + "Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c new file mode 100644 index 000000000000..37d02fe09137 --- /dev/null +++ b/drivers/media/pci/ttpci/budget-core.c @@ -0,0 +1,602 @@ +/* + * budget-core.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold + * + * Copyright (C) 2002 Ralph Metzler + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * 26feb2004 Support for FS Activy Card (Grundig tuner) by + * Michael Dreher , + * Oliver Endriss , + * Andreas 'randy' Weinberger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + + +#include "budget.h" +#include "ttpci-eeprom.h" + +#define TS_WIDTH (2 * TS_SIZE) +#define TS_WIDTH_ACTIVY TS_SIZE +#define TS_WIDTH_DVBC TS_SIZE +#define TS_HEIGHT_MASK 0xf00 +#define TS_HEIGHT_MASK_ACTIVY 0xc00 +#define TS_HEIGHT_MASK_DVBC 0xe00 +#define TS_MIN_BUFSIZE_K 188 +#define TS_MAX_BUFSIZE_K 1410 +#define TS_MAX_BUFSIZE_K_ACTIVY 564 +#define TS_MAX_BUFSIZE_K_DVBC 1316 +#define BUFFER_WARNING_WAIT (30*HZ) + +int budget_debug; +static int dma_buffer_size = TS_MIN_BUFSIZE_K; +module_param_named(debug, budget_debug, int, 0644); +module_param_named(bufsize, dma_buffer_size, int, 0444); +MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); +MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); + +/**************************************************************************** + * TT budget / WinTV Nova + ****************************************************************************/ + +static int stop_ts_capture(struct budget *budget) +{ + dprintk(2, "budget: %p\n", budget); + + saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off + SAA7146_IER_DISABLE(budget->dev, MASK_10); + return 0; +} + +static int start_ts_capture(struct budget *budget) +{ + struct saa7146_dev *dev = budget->dev; + + dprintk(2, "budget: %p\n", budget); + + if (!budget->feeding || !budget->fe_synced) + return 0; + + saa7146_write(dev, MC1, MASK_20); // DMA3 off + + memset(budget->grabbing, 0x00, budget->buffer_size); + + saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); + + budget->ttbp = 0; + + /* + * Signal path on the Activy: + * + * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory + * + * Since the tuner feeds 204 bytes packets into the SAA7146, + * DMA3 is configured to strip the trailing 16 FEC bytes: + * Pitch: 188, NumBytes3: 188, NumLines3: 1024 + */ + + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: + saa7146_write(dev, DD1_INIT, 0x04000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + break; + case BUDGET_PATCH: + saa7146_write(dev, DD1_INIT, 0x00000200); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + break; + case BUDGET_CIN1200C_MK3: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1C_TDA10024: + case BUDGET_KNC1CP_MK3: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + break; + default: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x02000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + } + + saa7146_write(dev, MC2, (MASK_08 | MASK_24)); + mdelay(10); + + saa7146_write(dev, BASE_ODD3, 0); + if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { + // using odd/even buffers + saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); + } else { + // using a single buffer + saa7146_write(dev, BASE_EVEN3, 0); + } + saa7146_write(dev, PROT_ADDR3, budget->buffer_size); + saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); + + saa7146_write(dev, PITCH3, budget->buffer_width); + saa7146_write(dev, NUM_LINE_BYTE3, + (budget->buffer_height << 16) | budget->buffer_width); + + saa7146_write(dev, MC2, (MASK_04 | MASK_20)); + + SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ + SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ + saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ + + return 0; +} + +static int budget_read_fe_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + int synced; + int ret; + + if (budget->read_fe_status) + ret = budget->read_fe_status(fe, status); + else + ret = -EINVAL; + + if (!ret) { + synced = (*status & FE_HAS_LOCK); + if (synced != budget->fe_synced) { + budget->fe_synced = synced; + spin_lock(&budget->feedlock); + if (synced) + start_ts_capture(budget); + else + stop_ts_capture(budget); + spin_unlock(&budget->feedlock); + } + } + return ret; +} + +static void vpeirq(unsigned long data) +{ + struct budget *budget = (struct budget *) data; + u8 *mem = (u8 *) (budget->grabbing); + u32 olddma = budget->ttbp; + u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + u32 count; + + /* Ensure streamed PCI data is synced to CPU */ + pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); + + /* nearest lower position divisible by 188 */ + newdma -= newdma % 188; + + if (newdma >= budget->buffer_size) + return; + + budget->ttbp = newdma; + + if (budget->feeding == 0 || newdma == olddma) + return; + + if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ + count = newdma - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + } else { /* wraparound, dump olddma..buflen and 0..newdma */ + count = budget->buffer_size - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + count += newdma; + dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); + } + + if (count > budget->buffer_warning_threshold) + budget->buffer_warnings++; + + if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { + printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", + budget->dev->name, __func__, budget->buffer_warnings, count); + budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; + budget->buffer_warnings = 0; + } +} + + +int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result = 0; + unsigned long flags = 0; + + if (count > 4 || count <= 0) + return 0; + + if (uselocks) + spin_lock_irqsave(&budget->debilock, flags); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, MC2, (2 << 16) | 2); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + result = saa7146_read(saa, DEBI_AD); + result &= (0xffffffffUL >> ((4 - count) * 8)); + + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + + return result; +} + +int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, + int count, u32 value, int uselocks, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + unsigned long flags = 0; + int result; + + if (count > 4 || count <= 0) + return 0; + + if (uselocks) + spin_lock_irqsave(&budget->debilock, flags); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, DEBI_AD, value); + saa7146_write(saa, MC2, (2 << 16) | 2); + + if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + + if (uselocks) + spin_unlock_irqrestore(&budget->debilock, flags); + return 0; +} + + +/**************************************************************************** + * DVB API SECTION + ****************************************************************************/ + +static int budget_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct budget *budget = (struct budget *) demux->priv; + int status = 0; + + dprintk(2, "budget: %p\n", budget); + + if (!demux->dmx.frontend) + return -EINVAL; + + spin_lock(&budget->feedlock); + feed->pusi_seen = 0; /* have a clean section start */ + if (budget->feeding++ == 0) + status = start_ts_capture(budget); + spin_unlock(&budget->feedlock); + return status; +} + +static int budget_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct budget *budget = (struct budget *) demux->priv; + int status = 0; + + dprintk(2, "budget: %p\n", budget); + + spin_lock(&budget->feedlock); + if (--budget->feeding == 0) + status = stop_ts_capture(budget); + spin_unlock(&budget->feedlock); + return status; +} + +static int budget_register(struct budget *budget) +{ + struct dvb_demux *dvbdemux = &budget->demux; + int ret; + + dprintk(2, "budget: %p\n", budget); + + dvbdemux->priv = (void *) budget; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = budget_start_feed; + dvbdemux->stop_feed = budget_stop_feed; + dvbdemux->write_to_decoder = NULL; + + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + + dvb_dmx_init(&budget->demux); + + budget->dmxdev.filternum = 256; + budget->dmxdev.demux = &dvbdemux->dmx; + budget->dmxdev.capabilities = 0; + + dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter); + + budget->hw_frontend.source = DMX_FRONTEND_0; + + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); + + if (ret < 0) + return ret; + + budget->mem_frontend.source = DMX_MEMORY_FE; + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); + if (ret < 0) + return ret; + + ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); + if (ret < 0) + return ret; + + dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); + + return 0; +} + +static void budget_unregister(struct budget *budget) +{ + struct dvb_demux *dvbdemux = &budget->demux; + + dprintk(2, "budget: %p\n", budget); + + dvb_net_release(&budget->dvb_net); + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend); + + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); +} + +int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner, short *adapter_nums) +{ + int ret = 0; + struct budget_info *bi = info->ext_priv; + int max_bufsize; + int height_mask; + + memset(budget, 0, sizeof(struct budget)); + + dprintk(2, "dev: %p, budget: %p\n", dev, budget); + + budget->card = bi; + budget->dev = (struct saa7146_dev *) dev; + + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: + budget->buffer_width = TS_WIDTH_ACTIVY; + max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; + height_mask = TS_HEIGHT_MASK_ACTIVY; + break; + + case BUDGET_KNC1C: + case BUDGET_KNC1CP: + case BUDGET_CIN1200C: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1C_TDA10024: + case BUDGET_KNC1CP_MK3: + case BUDGET_CIN1200C_MK3: + budget->buffer_width = TS_WIDTH_DVBC; + max_bufsize = TS_MAX_BUFSIZE_K_DVBC; + height_mask = TS_HEIGHT_MASK_DVBC; + break; + + default: + budget->buffer_width = TS_WIDTH; + max_bufsize = TS_MAX_BUFSIZE_K; + height_mask = TS_HEIGHT_MASK; + } + + if (dma_buffer_size < TS_MIN_BUFSIZE_K) + dma_buffer_size = TS_MIN_BUFSIZE_K; + else if (dma_buffer_size > max_bufsize) + dma_buffer_size = max_bufsize; + + budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; + if (budget->buffer_height > 0xfff) { + budget->buffer_height /= 2; + budget->buffer_height &= height_mask; + budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; + } else { + budget->buffer_height &= height_mask; + budget->buffer_size = budget->buffer_height * budget->buffer_width; + } + budget->buffer_warning_threshold = budget->buffer_size * 80/100; + budget->buffer_warnings = 0; + budget->buffer_warning_time = jiffies; + + dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", + budget->dev->name, + budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", + budget->buffer_width, budget->buffer_height); + printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); + + ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, + owner, &budget->dev->pci->dev, adapter_nums); + if (ret < 0) + return ret; + + /* set dd1 stream a & b */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25)); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + saa7146_write(dev, DD1_INIT, 0x02000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + if (bi->type != BUDGET_FS_ACTIVY) + budget->video_port = BUDGET_VIDEO_PORTB; + else + budget->video_port = BUDGET_VIDEO_PORTA; + spin_lock_init(&budget->feedlock); + spin_lock_init(&budget->debilock); + + /* the Siemens DVB needs this if you want to have the i2c chips + get recognized before the main driver is loaded */ + if (bi->type != BUDGET_FS_ACTIVY) + saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ + + strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name)); + + saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); + strcpy(budget->i2c_adap.name, budget->card->name); + + if (i2c_add_adapter(&budget->i2c_adap) < 0) { + ret = -ENOMEM; + goto err_dvb_unregister; + } + + ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); + + budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt); + if (NULL == budget->grabbing) { + ret = -ENOMEM; + goto err_del_i2c; + } + + saa7146_write(dev, PCI_BT_V1, 0x001c0000); + /* upload all */ + saa7146_write(dev, GPIO_CTRL, 0x000000); + + tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget); + + /* frontend power on */ + if (bi->type != BUDGET_FS_ACTIVY) + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + + if ((ret = budget_register(budget)) == 0) + return 0; /* Everything OK */ + + /* An error occurred, cleanup resources */ + saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); + +err_del_i2c: + i2c_del_adapter(&budget->i2c_adap); + +err_dvb_unregister: + dvb_unregister_adapter(&budget->dvb_adapter); + + return ret; +} + +void ttpci_budget_init_hooks(struct budget *budget) +{ + if (budget->dvb_frontend && !budget->read_fe_status) { + budget->read_fe_status = budget->dvb_frontend->ops.read_status; + budget->dvb_frontend->ops.read_status = budget_read_fe_status; + } +} + +int ttpci_budget_deinit(struct budget *budget) +{ + struct saa7146_dev *dev = budget->dev; + + dprintk(2, "budget: %p\n", budget); + + budget_unregister(budget); + + tasklet_kill(&budget->vpe_tasklet); + + saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); + + i2c_del_adapter(&budget->i2c_adap); + + dvb_unregister_adapter(&budget->dvb_adapter); + + return 0; +} + +void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) +{ + struct budget *budget = (struct budget *) dev->ext_priv; + + dprintk(8, "dev: %p, budget: %p\n", dev, budget); + + if (*isr & MASK_10) + tasklet_schedule(&budget->vpe_tasklet); +} + +void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) +{ + struct budget *budget = (struct budget *) dev->ext_priv; + + spin_lock(&budget->feedlock); + budget->video_port = video_port; + if (budget->feeding) { + stop_ts_capture(budget); + start_ts_capture(budget); + } + spin_unlock(&budget->feedlock); +} + +EXPORT_SYMBOL_GPL(ttpci_budget_debiread); +EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); +EXPORT_SYMBOL_GPL(ttpci_budget_init); +EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks); +EXPORT_SYMBOL_GPL(ttpci_budget_deinit); +EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); +EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); +EXPORT_SYMBOL_GPL(budget_debug); + +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ttpci/budget-patch.c b/drivers/media/pci/ttpci/budget-patch.c new file mode 100644 index 000000000000..2cb35c23d2ac --- /dev/null +++ b/drivers/media/pci/ttpci/budget-patch.c @@ -0,0 +1,680 @@ +/* + * budget-patch.c: driver for Budget Patch, + * hardware modification of DVB-S cards enabling full TS + * + * Written by Emard + * + * Original idea by Roberto Deza + * + * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic + * and Metzlerbros + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + +#include "av7110.h" +#include "av7110_hw.h" +#include "budget.h" +#include "stv0299.h" +#include "ves1x93.h" +#include "tda8083.h" + +#include "bsru6.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define budget_patch budget + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH); +//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC); + +static struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000), +// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), + { + .vendor = 0, + } +}; + +/* those lines are for budget-patch to be tried +** on a true budget card and observe the +** behaviour of VSYNC generated by rps1. +** this code was shamelessly copy/pasted from budget.c +*/ +static void gpio_Set22K (struct budget *budget, int state) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); +} + +/* Diseqc functions only for TT Budget card */ +/* taken from the Skyvision DVB driver by + Ralph Metzler */ + +static void DiseqcSendBit (struct budget *budget, int data) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + udelay(data ? 500 : 1000); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + udelay(data ? 1000 : 500); +} + +static void DiseqcSendByte (struct budget *budget, int data) +{ + int i, par=1, d; + + dprintk(2, "budget: %p\n", budget); + + for (i=7; i>=0; i--) { + d = (data>>i)&1; + par ^= d; + DiseqcSendBit(budget, d); + } + + DiseqcSendBit(budget, par); +} + +static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) +{ + struct saa7146_dev *dev=budget->dev; + int i; + + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + mdelay(16); + + for (i=0; idvb->priv; + + switch (tone) { + case SEC_TONE_ON: + gpio_Set22K (budget, 1); + break; + + case SEC_TONE_OFF: + gpio_Set22K (budget, 0); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); + + return 0; +} + +static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, 0, NULL, minicmd); + + return 0; +} + +static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length) +{ + int i; + + dprintk(2, "budget: %p\n", budget); + + for (i = 2; i < length; i++) + { + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0); + msleep(5); + } + if (length) + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0); + else + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0); + msleep(5); + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0); + msleep(5); + return 0; +} + +static void av7110_set22k(struct budget_patch *budget, int state) +{ + u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0}; + + dprintk(2, "budget: %p\n", budget); + budget_av7110_send_fw_cmd(budget, buf, 2); +} + +static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst) +{ + int i; + u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC), + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + dprintk(2, "budget: %p\n", budget); + + if (len>10) + len=10; + + buf[1] = len+2; + buf[2] = len; + + if (burst != -1) + buf[3]=burst ? 0x01 : 0x00; + else + buf[3]=0xffff; + + for (i=0; idvb->priv; + + switch (tone) { + case SEC_TONE_ON: + av7110_set22k (budget, 1); + break; + + case SEC_TONE_OFF: + av7110_set22k (budget, 0); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + + av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); + + return 0; +} + +static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + + av7110_send_diseqc_msg (budget, 0, NULL, minicmd); + + return 0; +} + +static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (p->frequency + 479500) / 125; + + if (p->frequency > 2000000) + pwr = 3; + else if (p->frequency > 1800000) + pwr = 2; + else if (p->frequency > 1600000) + pwr = 1; + else if (p->frequency > 1200000) + pwr = 0; + else if (p->frequency >= 1100000) + pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = { + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, +}; + +static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = p->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, +}; + +static void frontend_init(struct budget_patch* budget) +{ + switch(budget->dev->pci->subsystem_device) { + case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X + case 0x1013: // SATELCO Multimedia PCI + + // try the ALPS BSRV2 first of all + budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_patch_set_tone; + break; + } + + // try the ALPS BSRU6 now + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + break; + } + + // Try the grundig 29504-451 + budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + break; + } + break; + } + + if (budget->dvb_frontend == NULL) { + printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget->dev->pci->vendor, + budget->dev->pci->device, + budget->dev->pci->subsystem_vendor, + budget->dev->pci->subsystem_device); + } else { + if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { + printk("budget-av: Frontend registration failed!\n"); + dvb_frontend_detach(budget->dvb_frontend); + budget->dvb_frontend = NULL; + } + } +} + +/* written by Emard */ +static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +{ + struct budget_patch *budget; + int err; + int count = 0; + int detected = 0; + +#define PATCH_RESET 0 +#define RPS_IRQ 0 +#define HPS_SETUP 0 +#if PATCH_RESET + saa7146_write(dev, MC1, MASK_31); + msleep(40); +#endif +#if HPS_SETUP + // initialize registers. Better to have it like this + // than leaving something unconfigured + saa7146_write(dev, DD1_STREAM_B, 0); + // port B VSYNC at rising edge + saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too! + saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI + + // debi config + // saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18); + + // zero all HPS registers + saa7146_write(dev, HPS_H_PRESCALE, 0); // r68 + saa7146_write(dev, HPS_H_SCALE, 0); // r6c + saa7146_write(dev, BCS_CTRL, 0); // r70 + saa7146_write(dev, HPS_V_SCALE, 0); // r60 + saa7146_write(dev, HPS_V_GAIN, 0); // r64 + saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74 + saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78 + // Set HPS prescaler for port B input + saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) ); + saa7146_write(dev, MC2, + 0 * (MASK_08 | MASK_24) | // BRS control + 0 * (MASK_09 | MASK_25) | // a + 0 * (MASK_10 | MASK_26) | // b + 1 * (MASK_06 | MASK_22) | // HPS_CTRL1 + 1 * (MASK_05 | MASK_21) | // HPS_CTRL2 + 0 * (MASK_01 | MASK_15) // DEBI + ); +#endif + // Disable RPS1 and RPS0 + saa7146_write(dev, MC1, ( MASK_29 | MASK_28)); + // RPS1 timeout disable + saa7146_write(dev, RPS_TOV1, 0); + + // code for autodetection + // will wait for VBI_B event (vertical blank at port B) + // and will reset GPIO3 after VBI_B is detected. + // (GPIO3 should be raised high by CPU to + // test if GPIO3 will generate vertical blank signal + // in budget patch GPIO3 is connected to VSYNC_B + count = 0; +#if 0 + WRITE_RPS1(CMD_UPLOAD | + MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); +#endif + WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + // issue RPS1 interrupt to increment counter + WRITE_RPS1(CMD_INTERRUPT); + // at least a NOP is neede between two interrupts + WRITE_RPS1(CMD_NOP); + // interrupt again + WRITE_RPS1(CMD_INTERRUPT); +#endif + WRITE_RPS1(CMD_STOP); + +#if RPS_IRQ + // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) + // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled + // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called + saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + // set event counter 1 threshold to maximum allowed value (rEC p55) + saa7146_write(dev, ECT1R, 0x3fff ); +#endif + // Fix VSYNC level + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + // Set RPS1 Address register to point to RPS code (r108 p42) + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + // Enable RPS1, (rFC p33) + saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); + + + mdelay(50); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + mdelay(150); + + + if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) + detected = 1; + +#if RPS_IRQ + printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); +#endif + // Disable RPS1 + saa7146_write(dev, MC1, ( MASK_29 )); + + if(detected == 0) + printk("budget-patch not detected or saa7146 in non-default state.\n" + "try enabling ressetting of 7146 with MASK_31 in MC1 register\n"); + + else + printk("BUDGET-PATCH DETECTED.\n"); + + +/* OLD (Original design by Roberto Deza): +** This code will setup the SAA7146_RPS1 to generate a square +** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of +** TS_WIDTH packets) has been acquired on SAA7146_D1B video port; +** then, this GPIO3 output which is connected to the D1B_VSYNC +** input, will trigger the acquisition of the alternate field +** and so on. +** Currently, the TT_budget / WinTV_Nova cards have two ICs +** (74HCT4040, LVC74) for the generation of this VSYNC signal, +** which seems that can be done perfectly without this :-)). +*/ + +/* New design (By Emard) +** this rps1 code will copy internal HS event to GPIO3 pin. +** GPIO3 is in budget-patch hardware connected to port B VSYNC + +** HS is an internal event of 7146, accessible with RPS +** and temporarily raised high every n lines +** (n in defined in the RPS_THRESH1 counter threshold) +** I think HS is raised high on the beginning of the n-th line +** and remains high until this n-th line that triggered +** it is completely received. When the reception of n-th line +** ends, HS is lowered. + +** To transmit data over DMA, 7146 needs changing state at +** port B VSYNC pin. Any changing of port B VSYNC will +** cause some DMA data transfer, with more or less packets loss. +** It depends on the phase and frequency of VSYNC and +** the way of 7146 is instructed to trigger on port B (defined +** in DD1_INIT register, 3rd nibble from the right valid +** numbers are 0-7, see datasheet) +** +** The correct triggering can minimize packet loss, +** dvbtraffic should give this stable bandwidths: +** 22k transponder = 33814 kbit/s +** 27.5k transponder = 38045 kbit/s +** by experiment it is found that the best results +** (stable bandwidths and almost no packet loss) +** are obtained using DD1_INIT triggering number 2 +** (Va at rising edge of VS Fa = HS x VS-failing forced toggle) +** and a VSYNC phase that occurs in the middle of DMA transfer +** (about byte 188*512=96256 in the DMA window). +** +** Phase of HS is still not clear to me how to control, +** It just happens to be so. It can be seen if one enables +** RPS_IRQ and print Event Counter 1 in vpeirq(). Every +** time RPS_INTERRUPT is called, the Event Counter 1 will +** increment. That's how the 7146 is programmed to do event +** counting in this budget-patch.c +** I *think* HPS setting has something to do with the phase +** of HS but I can't be 100% sure in that. + +** hardware debug note: a working budget card (including budget patch) +** with vpeirq() interrupt setup in mode "0x90" (every 64K) will +** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes +** and that means 3*25=75 Hz of interrupt frequency, as seen by +** watch cat /proc/interrupts +** +** If this frequency is 3x lower (and data received in the DMA +** buffer don't start with 0x47, but in the middle of packets, +** whose lengths appear to be like 188 292 188 104 etc. +** this means VSYNC line is not connected in the hardware. +** (check soldering pcb and pins) +** The same behaviour of missing VSYNC can be duplicated on budget +** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. +*/ + + // Setup RPS1 "program" (p35) + count = 0; + + + // Wait Source Line Counter Threshold (p36) + WRITE_RPS1(CMD_PAUSE | EVT_HS); + // Set GPIO3=1 (p42) + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); +#if RPS_IRQ + // issue RPS1 interrupt + WRITE_RPS1(CMD_INTERRUPT); +#endif + // Wait reset Source Line Counter Threshold (p36) + WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); + // Set GPIO3=0 (p42) + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + // issue RPS1 interrupt + WRITE_RPS1(CMD_INTERRUPT); +#endif + // Jump to begin of RPS program (p37) + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); + + // Fix VSYNC level + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + // Set RPS1 Address register to point to RPS code (r108 p42) + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + + if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) + return -ENOMEM; + + dprintk(2, "budget: %p\n", budget); + + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { + kfree(budget); + return err; + } + + // Set Source Line Counter Threshold, using BRS (rCC p43) + // It generates HS event every TS_HEIGHT lines + // this is related to TS_WIDTH set in register + // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE + // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 + //,then RPS_THRESH1 + // should be set to trigger every TS_HEIGHT (512) lines. + // + saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); + + // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); + // Enable RPS1 (rFC p33) + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); + + + dev->ext_priv = budget; + + budget->dvb_adapter.priv = budget; + frontend_init(budget); + + ttpci_budget_init_hooks(budget); + + return 0; +} + +static int budget_patch_detach (struct saa7146_dev* dev) +{ + struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; + int err; + + if (budget->dvb_frontend) { + dvb_unregister_frontend(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); + } + err = ttpci_budget_deinit (budget); + + kfree (budget); + + return err; +} + +static int __init budget_patch_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_patch_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +static struct saa7146_extension budget_extension = { + .name = "budget_patch dvb", + .flags = 0, + + .module = THIS_MODULE, + .pci_tbl = pci_tbl, + .attach = budget_patch_attach, + .detach = budget_patch_detach, + + .irq_mask = MASK_10, + .irq_func = ttpci_budget_irq10_handler, +}; + +module_init(budget_patch_init); +module_exit(budget_patch_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others"); +MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 " + "based so-called Budget Patch cards"); diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c new file mode 100644 index 000000000000..7e6e43ae5c51 --- /dev/null +++ b/drivers/media/pci/ttpci/budget.c @@ -0,0 +1,871 @@ +/* + * budget.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold + * + * Copyright (C) 2002 Ralph Metzler + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * 26feb2004 Support for FS Activy Card (Grundig tuner) by + * Michael Dreher , + * Oliver Endriss and + * Andreas 'randy' Weinberger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/ + */ + +#include "budget.h" +#include "stv0299.h" +#include "ves1x93.h" +#include "ves1820.h" +#include "l64781.h" +#include "tda8083.h" +#include "s5h1420.h" +#include "tda10086.h" +#include "tda826x.h" +#include "lnbp21.h" +#include "bsru6.h" +#include "bsbe1.h" +#include "tdhd1.h" +#include "stv6110x.h" +#include "stv090x.h" +#include "isl6423.h" +#include "lnbh24.h" + + +static int diseqc_method; +module_param(diseqc_method, int, 0444); +MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static void Set22K (struct budget *budget, int state) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); +} + +/* Diseqc functions only for TT Budget card */ +/* taken from the Skyvision DVB driver by + Ralph Metzler */ + +static void DiseqcSendBit (struct budget *budget, int data) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + udelay(data ? 500 : 1000); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + udelay(data ? 1000 : 500); +} + +static void DiseqcSendByte (struct budget *budget, int data) +{ + int i, par=1, d; + + dprintk(2, "budget: %p\n", budget); + + for (i=7; i>=0; i--) { + d = (data>>i)&1; + par ^= d; + DiseqcSendBit(budget, d); + } + + DiseqcSendBit(budget, par); +} + +static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) +{ + struct saa7146_dev *dev=budget->dev; + int i; + + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + mdelay(16); + + for (i=0; idev; + + dprintk(2, "budget: %p\n", budget); + + switch (voltage) { + case SEC_VOLTAGE_13: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); + break; + case SEC_VOLTAGE_18: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + break; + case SEC_VOLTAGE_OFF: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + return SetVoltage_Activy (budget, voltage); +} + +static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + switch (tone) { + case SEC_TONE_ON: + Set22K (budget, 1); + break; + + case SEC_TONE_OFF: + Set22K (budget, 0); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); + + return 0; +} + +static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, 0, NULL, minicmd); + + return 0; +} + +static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (c->frequency + 479500) / 125; + + if (c->frequency > 2000000) + pwr = 3; + else if (c->frequency > 1800000) + pwr = 2; + else if (c->frequency > 1600000) + pwr = 1; + else if (c->frequency > 1200000) + pwr = 0; + else if (c->frequency >= 1100000) + pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = +{ + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, +}; + +static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (c->frequency + 35937500 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + +static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = fe->dvb->priv; + u8 *tuner_addr = fe->tuner_priv; + u32 div; + u8 cfg, cpump, band_select; + u8 data[4]; + struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) }; + + if (tuner_addr) + msg.addr = *tuner_addr; + else + msg.addr = 0x61; + + div = (36125000 + c->frequency) / 166666; + + cfg = 0x88; + + if (c->frequency < 175000000) + cpump = 2; + else if (c->frequency < 390000000) + cpump = 1; + else if (c->frequency < 470000000) + cpump = 2; + else if (c->frequency < 750000000) + cpump = 1; + else + cpump = 3; + + if (c->frequency < 175000000) + band_select = 0x0e; + else if (c->frequency < 470000000) + band_select = 0x05; + else + band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = (cpump << 6) | band_select; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct l64781_config grundig_29504_401_config = { + .demod_address = 0x55, +}; + +static struct l64781_config grundig_29504_401_config_activy = { + .demod_address = 0x54, +}; + +static u8 tuner_address_grundig_29504_401_activy = 0x60; + +static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = c->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, +}; + +static int s5h1420_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = c->frequency / 1000; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xc2; + + if (div < 1450) + data[3] = 0x00; + else if (div < 1850) + data[3] = 0x40; + else if (div < 2000) + data[3] = 0x80; + else + data[3] = 0xc0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + + return 0; +} + +static struct s5h1420_config s5h1420_config = { + .demod_address = 0x53, + .invert = 1, + .cdclk_polarity = 1, +}; + +static struct tda10086_config tda10086_config = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static struct stv0299_config alps_bsru6_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, +}; + +static struct stv0299_config alps_bsbe1_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsbe1_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsbe1_set_symbol_rate, +}; + +static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *)fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + + +static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) +{ + u8 val; + struct i2c_msg msg[] = { + { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } + }; + + return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; +} + +static u8 read_pwm(struct budget* budget) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static struct stv090x_config tt1600_stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 13500000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_DVBCI, + .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, + + .repeater_level = STV090x_RPTLEVEL_16, + + .tuner_init = NULL, + .tuner_sleep = NULL, + .tuner_set_mode = NULL, + .tuner_set_frequency = NULL, + .tuner_get_frequency = NULL, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = NULL, + .tuner_set_bbgain = NULL, + .tuner_get_bbgain = NULL, + .tuner_set_refclk = NULL, + .tuner_get_status = NULL, +}; + +static struct stv6110x_config tt1600_stv6110x_config = { + .addr = 0x60, + .refclk = 27000000, + .clk_div = 2, +}; + +static struct isl6423_config tt1600_isl6423_config = { + .current_max = SEC_CURRENT_515m, + .curlim = SEC_CURRENT_LIM_ON, + .mod_extern = 1, + .addr = 0x08, +}; + +static void frontend_init(struct budget *budget) +{ + (void)alps_bsbe1_config; /* avoid warning */ + + switch(budget->dev->pci->subsystem_device) { + case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) + case 0x1013: + // try the ALPS BSRV2 first of all + budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + break; + } + + // try the ALPS BSRU6 now + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + } + break; + } + break; + + case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) + + budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + break; + } + break; + + case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) + + budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + budget->dvb_frontend->tuner_priv = NULL; + break; + } + break; + + case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ + { + int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); + + if (subtype < 0) + break; + /* fixme: find a better way to identify the card */ + if (subtype < 0x36) { + /* assume ALPS BSRU6 */ + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } + } else { + /* assume ALPS BSBE1 */ + /* reset tuner */ + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); + msleep(250); + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } + } + break; + } + + case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) + budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + } + break; + + case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ + budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + } + break; + + case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ + budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy; + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + } + break; + + case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) + budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params; + if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + goto error_out; + } + break; + } + + case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) + // gpio2 is connected to CLB - reset it + leave it high + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(1); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(1); + + budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL) + printk("%s: No tda826x found!\n", __func__); + if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + goto error_out; + } + break; + } + + case 0x101c: { /* TT S2-1600 */ + struct stv6110x_devctl *ctl; + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(250); + + budget->dvb_frontend = dvb_attach(stv090x_attach, + &tt1600_stv090x_config, + &budget->i2c_adap, + STV090x_DEMODULATOR_0); + + if (budget->dvb_frontend) { + + ctl = dvb_attach(stv6110x_attach, + budget->dvb_frontend, + &tt1600_stv6110x_config, + &budget->i2c_adap); + + if (ctl) { + tt1600_stv090x_config.tuner_init = ctl->tuner_init; + tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; + tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (budget->dvb_frontend->ops.init) + budget->dvb_frontend->ops.init(budget->dvb_frontend); + + if (dvb_attach(isl6423_attach, + budget->dvb_frontend, + &budget->i2c_adap, + &tt1600_isl6423_config) == NULL) { + printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); + goto error_out; + } + } else { + printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); + goto error_out; + } + } + } + break; + + case 0x1020: { /* Omicom S2 */ + struct stv6110x_devctl *ctl; + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(250); + + budget->dvb_frontend = dvb_attach(stv090x_attach, + &tt1600_stv090x_config, + &budget->i2c_adap, + STV090x_DEMODULATOR_0); + + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: Omicom S2 detected\n"); + + ctl = dvb_attach(stv6110x_attach, + budget->dvb_frontend, + &tt1600_stv6110x_config, + &budget->i2c_adap); + + if (ctl) { + tt1600_stv090x_config.tuner_init = ctl->tuner_init; + tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; + tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (budget->dvb_frontend->ops.init) + budget->dvb_frontend->ops.init(budget->dvb_frontend); + + if (dvb_attach(lnbh24_attach, + budget->dvb_frontend, + &budget->i2c_adap, + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x14>>1) == NULL) { + printk(KERN_ERR + "No LNBH24 found!\n"); + goto error_out; + } + } else { + printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); + goto error_out; + } + } + } + break; + } + + if (budget->dvb_frontend == NULL) { + printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget->dev->pci->vendor, + budget->dev->pci->device, + budget->dev->pci->subsystem_vendor, + budget->dev->pci->subsystem_device); + } else { + if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) + goto error_out; + } + return; + +error_out: + printk("budget: Frontend registration failed!\n"); + dvb_frontend_detach(budget->dvb_frontend); + budget->dvb_frontend = NULL; + return; +} + +static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +{ + struct budget *budget = NULL; + int err; + + budget = kmalloc(sizeof(struct budget), GFP_KERNEL); + if( NULL == budget ) { + return -ENOMEM; + } + + dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget); + + dev->ext_priv = budget; + + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { + printk("==> failed\n"); + kfree (budget); + return err; + } + + budget->dvb_adapter.priv = budget; + frontend_init(budget); + + ttpci_budget_init_hooks(budget); + + return 0; +} + +static int budget_detach (struct saa7146_dev* dev) +{ + struct budget *budget = (struct budget*) dev->ext_priv; + int err; + + if (budget->dvb_frontend) { + dvb_unregister_frontend(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); + } + + err = ttpci_budget_deinit (budget); + + kfree (budget); + dev->ext_priv = NULL; + + return err; +} + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); +MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT); + +static struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), + MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), + MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), + MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), + MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), + MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), + MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c), + MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), + MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), + MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), + MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), + MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget dvb", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = pci_tbl, + .attach = budget_attach, + .detach = budget_detach, + + .irq_mask = MASK_10, + .irq_func = ttpci_budget_irq10_handler, +}; + +static int __init budget_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_init); +module_exit(budget_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called " + "budget PCI DVB cards by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h new file mode 100644 index 000000000000..3d8a806c20bb --- /dev/null +++ b/drivers/media/pci/ttpci/budget.h @@ -0,0 +1,124 @@ + +#ifndef __BUDGET_DVB__ +#define __BUDGET_DVB__ + +#include "dvb_frontend.h" +#include "dvbdev.h" +#include "demux.h" +#include "dvb_demux.h" +#include "dmxdev.h" +#include "dvb_filter.h" +#include "dvb_net.h" + +#include +#include + +#include + +extern int budget_debug; + +#ifdef dprintk +#undef dprintk +#endif + +#define dprintk(level,args...) \ + do { if ((budget_debug & level)) { printk("%s: %s(): ", KBUILD_MODNAME, __func__); printk(args); } } while (0) + +struct budget_info { + char *name; + int type; +}; + +/* place to store all the necessary device information */ +struct budget { + + /* devices */ + struct dvb_device dvb_dev; + struct dvb_net dvb_net; + + struct saa7146_dev *dev; + + struct i2c_adapter i2c_adap; + struct budget_info *card; + + unsigned char *grabbing; + struct saa7146_pgtable pt; + + struct tasklet_struct fidb_tasklet; + struct tasklet_struct vpe_tasklet; + + struct dmxdev dmxdev; + struct dvb_demux demux; + + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + + int ci_present; + int video_port; + + u32 buffer_width; + u32 buffer_height; + u32 buffer_size; + u32 buffer_warning_threshold; + u32 buffer_warnings; + unsigned long buffer_warning_time; + + u32 ttbp; + int feeding; + + spinlock_t feedlock; + + spinlock_t debilock; + + struct dvb_adapter dvb_adapter; + struct dvb_frontend *dvb_frontend; + int (*read_fe_status)(struct dvb_frontend *fe, fe_status_t *status); + int fe_synced; + + void *priv; +}; + +#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \ +static struct budget_info x_var ## _info = { \ + .name=x_name, \ + .type=x_type }; \ +static struct saa7146_pci_extension_data x_var = { \ + .ext_priv = &x_var ## _info, \ + .ext = &budget_extension }; + +#define BUDGET_TT 0 +#define BUDGET_TT_HW_DISEQC 1 +#define BUDGET_PATCH 3 +#define BUDGET_FS_ACTIVY 4 +#define BUDGET_CIN1200S 5 +#define BUDGET_CIN1200C 6 +#define BUDGET_CIN1200T 7 +#define BUDGET_KNC1S 8 +#define BUDGET_KNC1C 9 +#define BUDGET_KNC1T 10 +#define BUDGET_KNC1SP 11 +#define BUDGET_KNC1CP 12 +#define BUDGET_KNC1TP 13 +#define BUDGET_TVSTAR 14 +#define BUDGET_CIN1200C_MK3 15 +#define BUDGET_KNC1C_MK3 16 +#define BUDGET_KNC1CP_MK3 17 +#define BUDGET_KNC1S2 18 +#define BUDGET_KNC1C_TDA10024 19 + +#define BUDGET_VIDEO_PORTA 0 +#define BUDGET_VIDEO_PORTB 1 + +extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner, short *adapter_nums); +extern void ttpci_budget_init_hooks(struct budget *budget); +extern int ttpci_budget_deinit(struct budget *budget); +extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); +extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port); +extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop); +extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, + int uselocks, int nobusyloop); + +#endif diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c new file mode 100644 index 000000000000..32d43156c548 --- /dev/null +++ b/drivers/media/pci/ttpci/ttpci-eeprom.c @@ -0,0 +1,176 @@ +/* + Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM, + decode it and store it in the associated adapter struct for + use by dvb_net.c + + This card appear to have the 24C16 write protect held to ground, + thus permitting normal read/write operation. Theoretically it + would be possible to write routines to burn a different (encoded) + MAC address into the EEPROM. + + Robert Schlabbach GMX + Michael Glaum KVH Industries + Holger Waechtler Convergence + + Copyright (C) 2002-2003 Ralph Metzler + Metzler Brothers Systementwicklung GbR + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include + +#include "ttpci-eeprom.h" + +#if 1 +#define dprintk(x...) do { printk(x); } while (0) +#else +#define dprintk(x...) do { } while (0) +#endif + + +static int check_mac_tt(u8 *buf) +{ + int i; + u16 tmp = 0xffff; + + for (i = 0; i < 8; i++) { + tmp = (tmp << 8) | ((tmp >> 8) ^ buf[i]); + tmp ^= (tmp >> 4) & 0x0f; + tmp ^= (tmp << 12) ^ ((tmp & 0xff) << 5); + } + tmp ^= 0xffff; + return (((tmp >> 8) ^ buf[8]) | ((tmp & 0xff) ^ buf[9])); +} + +static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC) +{ + u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, + 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, + 0x1d, 0x36, 0x64, 0x78}; + u8 data[20]; + int i; + + /* In case there is a sig check failure have the orig contents available */ + memcpy(data, encodedMAC, 20); + + for (i = 0; i < 20; i++) + data[i] ^= xor[i]; + for (i = 0; i < 10; i++) + data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) + >> ((data[2 * i + 1] >> 6) & 3); + + if (check_mac_tt(data)) + return -ENODEV; + + decodedMAC[0] = data[2]; decodedMAC[1] = data[1]; decodedMAC[2] = data[0]; + decodedMAC[3] = data[6]; decodedMAC[4] = data[5]; decodedMAC[5] = data[4]; + return 0; +} + +int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC) +{ + u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, + 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, + 0x1d, 0x36, 0x64, 0x78}; + u8 data[20]; + int i; + + memcpy(data, encodedMAC, 20); + + for (i = 0; i < 20; i++) + data[i] ^= xor[i]; + for (i = 0; i < 10; i++) + data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) + >> ((data[2 * i + 1] >> 6) & 3); + + if (check_mac_tt(data)) + return -ENODEV; + + decodedMAC[0] = data[2]; + decodedMAC[1] = data[1]; + decodedMAC[2] = data[0]; + decodedMAC[3] = data[6]; + decodedMAC[4] = data[5]; + decodedMAC[5] = data[4]; + return 0; +} +EXPORT_SYMBOL(ttpci_eeprom_decode_mac); + +static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC) +{ + int ret; + u8 b0[] = { 0xcc }; + + struct i2c_msg msg[] = { + { .addr = 0x50, .flags = 0, .buf = b0, .len = 1 }, + { .addr = 0x50, .flags = I2C_M_RD, .buf = encodedMAC, .len = 20 } + }; + + /* dprintk("%s\n", __func__); */ + + ret = i2c_transfer(adapter, msg, 2); + + if (ret != 2) /* Assume EEPROM isn't there */ + return (-ENODEV); + + return 0; +} + + +int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) +{ + int ret, i; + u8 encodedMAC[20]; + u8 decodedMAC[6]; + + ret = ttpci_eeprom_read_encodedMAC(adapter, encodedMAC); + + if (ret != 0) { /* Will only be -ENODEV */ + dprintk("Couldn't read from EEPROM: not there?\n"); + memset(proposed_mac, 0, 6); + return ret; + } + + ret = getmac_tt(decodedMAC, encodedMAC); + if( ret != 0 ) { + dprintk("adapter failed MAC signature check\n"); + dprintk("encoded MAC from EEPROM was " ); + for(i=0; i<19; i++) { + dprintk( "%.2x:", encodedMAC[i]); + } + dprintk("%.2x\n", encodedMAC[19]); + memset(proposed_mac, 0, 6); + return ret; + } + + memcpy(proposed_mac, decodedMAC, 6); + dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + decodedMAC[0], decodedMAC[1], decodedMAC[2], + decodedMAC[3], decodedMAC[4], decodedMAC[5]); + return 0; +} + +EXPORT_SYMBOL(ttpci_eeprom_parse_mac); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); +MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards " + "made by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.h b/drivers/media/pci/ttpci/ttpci-eeprom.h new file mode 100644 index 000000000000..dcc33d5a5cb1 --- /dev/null +++ b/drivers/media/pci/ttpci/ttpci-eeprom.h @@ -0,0 +1,34 @@ +/* + Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM, + decode it and store it in associated adapter net device + + Robert Schlabbach GMX + Michael Glaum KVH Industries + Holger Waechtler Convergence + + This program is free software; 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 __TTPCI_EEPROM_H__ +#define __TTPCI_EEPROM_H__ + +#include +#include + +extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC); +extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac); + +#endif diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index a5630e30eadc..94bdf4d51712 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -79,4 +79,4 @@ 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/dvb/ttpci +ccflags-y += -I$(srctree)/drivers/media/pci/ttpci -- cgit v1.2.3 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/Kconfig | 4 +- drivers/media/common/Kconfig | 2 + drivers/media/common/Makefile | 2 +- drivers/media/common/b2c2/Kconfig | 31 ++ drivers/media/common/b2c2/Makefile | 7 + drivers/media/common/b2c2/flexcop-common.h | 185 +++++++ drivers/media/common/b2c2/flexcop-eeprom.c | 147 +++++ drivers/media/common/b2c2/flexcop-fe-tuner.c | 678 +++++++++++++++++++++++ drivers/media/common/b2c2/flexcop-hw-filter.c | 232 ++++++++ drivers/media/common/b2c2/flexcop-i2c.c | 288 ++++++++++ drivers/media/common/b2c2/flexcop-misc.c | 86 +++ drivers/media/common/b2c2/flexcop-reg.h | 166 ++++++ drivers/media/common/b2c2/flexcop-sram.c | 363 ++++++++++++ drivers/media/common/b2c2/flexcop.c | 324 +++++++++++ drivers/media/common/b2c2/flexcop.h | 29 + drivers/media/common/b2c2/flexcop_ibi_value_be.h | 455 +++++++++++++++ drivers/media/common/b2c2/flexcop_ibi_value_le.h | 455 +++++++++++++++ drivers/media/pci/Kconfig | 21 +- drivers/media/pci/Makefile | 3 +- drivers/media/pci/b2c2/Kconfig | 39 -- drivers/media/pci/b2c2/Makefile | 13 +- drivers/media/pci/b2c2/flexcop-common.h | 185 ------- drivers/media/pci/b2c2/flexcop-eeprom.c | 147 ----- drivers/media/pci/b2c2/flexcop-fe-tuner.c | 678 ----------------------- drivers/media/pci/b2c2/flexcop-hw-filter.c | 232 -------- drivers/media/pci/b2c2/flexcop-i2c.c | 288 ---------- drivers/media/pci/b2c2/flexcop-misc.c | 86 --- drivers/media/pci/b2c2/flexcop-reg.h | 166 ------ drivers/media/pci/b2c2/flexcop-sram.c | 363 ------------ drivers/media/pci/b2c2/flexcop-usb.c | 587 -------------------- drivers/media/pci/b2c2/flexcop-usb.h | 111 ---- drivers/media/pci/b2c2/flexcop.c | 324 ----------- drivers/media/pci/b2c2/flexcop.h | 29 - drivers/media/pci/b2c2/flexcop_ibi_value_be.h | 455 --------------- drivers/media/pci/b2c2/flexcop_ibi_value_le.h | 455 --------------- drivers/media/usb/Kconfig | 1 + drivers/media/usb/Makefile | 2 +- drivers/media/usb/b2c2/Kconfig | 6 + drivers/media/usb/b2c2/Makefile | 7 + drivers/media/usb/b2c2/flexcop-usb.c | 587 ++++++++++++++++++++ drivers/media/usb/b2c2/flexcop-usb.h | 111 ++++ 41 files changed, 4177 insertions(+), 4173 deletions(-) create mode 100644 drivers/media/common/b2c2/Kconfig create mode 100644 drivers/media/common/b2c2/Makefile create mode 100644 drivers/media/common/b2c2/flexcop-common.h create mode 100644 drivers/media/common/b2c2/flexcop-eeprom.c create mode 100644 drivers/media/common/b2c2/flexcop-fe-tuner.c create mode 100644 drivers/media/common/b2c2/flexcop-hw-filter.c create mode 100644 drivers/media/common/b2c2/flexcop-i2c.c create mode 100644 drivers/media/common/b2c2/flexcop-misc.c create mode 100644 drivers/media/common/b2c2/flexcop-reg.h create mode 100644 drivers/media/common/b2c2/flexcop-sram.c create mode 100644 drivers/media/common/b2c2/flexcop.c create mode 100644 drivers/media/common/b2c2/flexcop.h create mode 100644 drivers/media/common/b2c2/flexcop_ibi_value_be.h create mode 100644 drivers/media/common/b2c2/flexcop_ibi_value_le.h delete mode 100644 drivers/media/pci/b2c2/flexcop-common.h delete mode 100644 drivers/media/pci/b2c2/flexcop-eeprom.c delete mode 100644 drivers/media/pci/b2c2/flexcop-fe-tuner.c delete mode 100644 drivers/media/pci/b2c2/flexcop-hw-filter.c delete mode 100644 drivers/media/pci/b2c2/flexcop-i2c.c delete mode 100644 drivers/media/pci/b2c2/flexcop-misc.c delete mode 100644 drivers/media/pci/b2c2/flexcop-reg.h delete mode 100644 drivers/media/pci/b2c2/flexcop-sram.c delete mode 100644 drivers/media/pci/b2c2/flexcop-usb.c delete mode 100644 drivers/media/pci/b2c2/flexcop-usb.h delete mode 100644 drivers/media/pci/b2c2/flexcop.c delete mode 100644 drivers/media/pci/b2c2/flexcop.h delete mode 100644 drivers/media/pci/b2c2/flexcop_ibi_value_be.h delete mode 100644 drivers/media/pci/b2c2/flexcop_ibi_value_le.h create mode 100644 drivers/media/usb/b2c2/Kconfig create mode 100644 drivers/media/usb/b2c2/Makefile create mode 100644 drivers/media/usb/b2c2/flexcop-usb.c create mode 100644 drivers/media/usb/b2c2/flexcop-usb.h (limited to 'drivers/media/usb') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 81c8662a1a7c..6343e84b361a 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -139,7 +139,6 @@ config DVB_NET unsure say Y. comment "Media drivers" -source "drivers/media/common/Kconfig" source "drivers/media/rc/Kconfig" # @@ -173,4 +172,7 @@ comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb-frontends/Kconfig" +# Common drivers +source "drivers/media/common/Kconfig" + endif # MEDIA_SUPPORT diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 769c6f8142d2..4672f7d82f67 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -7,3 +7,5 @@ config VIDEO_SAA7146_VV depends on VIDEO_V4L2 select VIDEOBUF_DMA_SG select VIDEO_SAA7146 + +source "drivers/media/common/b2c2/Kconfig" 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 diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig new file mode 100644 index 000000000000..e270dd847342 --- /dev/null +++ b/drivers/media/common/b2c2/Kconfig @@ -0,0 +1,31 @@ +config DVB_B2C2_FLEXCOP + tristate + depends on DVB_CORE && I2C + depends on DVB_B2C2_FLEXCOP_PCI || DVB_B2C2_FLEXCOP_USB + default y + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_MT312 if !DVB_FE_CUSTOMISE + select DVB_NXT200X if !DVB_FE_CUSTOMISE + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_BCM3510 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_S5H1420 if !DVB_FE_CUSTOMISE + select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE + select DVB_ISL6421 if !DVB_FE_CUSTOMISE + select DVB_CX24123 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE + help + Support for the digital TV receiver chip made by B2C2 Inc. included in + Technisats PCI cards and USB boxes. + + Say Y if you own such a device and want to use it. + +config DVB_B2C2_FLEXCOP_DEBUG + bool "Enable debug for the B2C2 FlexCop drivers" + depends on DVB_B2C2_FLEXCOP + help + Say Y if you want to enable the module option to control debug messages + of all B2C2 FlexCop drivers. diff --git a/drivers/media/common/b2c2/Makefile b/drivers/media/common/b2c2/Makefile new file mode 100644 index 000000000000..377d051548a9 --- /dev/null +++ b/drivers/media/common/b2c2/Makefile @@ -0,0 +1,7 @@ +b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ + flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o +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/ diff --git a/drivers/media/common/b2c2/flexcop-common.h b/drivers/media/common/b2c2/flexcop-common.h new file mode 100644 index 000000000000..437912e49824 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-common.h @@ -0,0 +1,185 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-common.h - common header file for device-specific source files + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_COMMON_H__ +#define __FLEXCOP_COMMON_H__ + +#include +#include +#include + +#include "flexcop-reg.h" + +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_filter.h" +#include "dvb_net.h" +#include "dvb_frontend.h" + +#define FC_MAX_FEED 256 + +#ifndef FC_LOG_PREFIX +#warning please define a log prefix for your file, using a default one +#define FC_LOG_PREFIX "b2c2-undef" +#endif + +/* Steal from usb.h */ +#undef err +#define err(format, arg...) \ + printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg) +#undef info +#define info(format, arg...) \ + printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) \ + printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg) + +struct flexcop_dma { + struct pci_dev *pdev; + + u8 *cpu_addr0; + dma_addr_t dma_addr0; + u8 *cpu_addr1; + dma_addr_t dma_addr1; + u32 size; /* size of each address in bytes */ +}; + +struct flexcop_i2c_adapter { + struct flexcop_device *fc; + struct i2c_adapter i2c_adap; + + u8 no_base_addr; + flexcop_i2c_port_t port; +}; + +/* Control structure for data definitions that are common to + * the B2C2-based PCI and USB devices. + */ +struct flexcop_device { + /* general */ + struct device *dev; /* for firmware_class */ + +#define FC_STATE_DVB_INIT 0x01 +#define FC_STATE_I2C_INIT 0x02 +#define FC_STATE_FE_INIT 0x04 + int init_state; + + /* device information */ + int has_32_hw_pid_filter; + flexcop_revision_t rev; + flexcop_device_type_t dev_type; + flexcop_bus_t bus_type; + + /* dvb stuff */ + struct dvb_adapter dvb_adapter; + struct dvb_frontend *fe; + struct dvb_net dvbnet; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + int (*fe_sleep) (struct dvb_frontend *); + + struct flexcop_i2c_adapter fc_i2c_adap[3]; + struct mutex i2c_mutex; + struct module *owner; + + /* options and status */ + int extra_feedcount; + int feedcount; + int pid_filtering; + int fullts_streaming_state; + + /* bus specific callbacks */ + flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *, + flexcop_ibi_register); + int (*write_ibi_reg) (struct flexcop_device *, + flexcop_ibi_register, flexcop_ibi_value); + int (*i2c_request) (struct flexcop_i2c_adapter *, + flexcop_access_op_t, u8 chipaddr, u8 addr, u8 *buf, u16 len); + int (*stream_control) (struct flexcop_device *, int); + int (*get_mac_addr) (struct flexcop_device *fc, int extended); + void *bus_specific; +}; + +/* exported prototypes */ + +/* from flexcop.c */ +void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len); +void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no); + +struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len); +void flexcop_device_kfree(struct flexcop_device *); + +int flexcop_device_initialize(struct flexcop_device *); +void flexcop_device_exit(struct flexcop_device *fc); +void flexcop_reset_block_300(struct flexcop_device *fc); + +/* from flexcop-dma.c */ +int flexcop_dma_allocate(struct pci_dev *pdev, + struct flexcop_dma *dma, u32 size); +void flexcop_dma_free(struct flexcop_dma *dma); + +int flexcop_dma_control_timer_irq(struct flexcop_device *fc, + flexcop_dma_index_t no, int onoff); +int flexcop_dma_control_size_irq(struct flexcop_device *fc, + flexcop_dma_index_t no, int onoff); +int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, + flexcop_dma_index_t dma_idx); +int flexcop_dma_xfer_control(struct flexcop_device *fc, + flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index, + int onoff); +int flexcop_dma_config_timer(struct flexcop_device *fc, + flexcop_dma_index_t dma_idx, u8 cycles); + +/* from flexcop-eeprom.c */ +/* the PCI part uses this call to get the MAC address, the USB part has its own */ +int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended); + +/* from flexcop-i2c.c */ +/* the PCI part uses this a i2c_request callback, whereas the usb part has its own + * one. We have it in flexcop-i2c.c, because it is going via the actual + * I2C-channel of the flexcop. + */ +int flexcop_i2c_request(struct flexcop_i2c_adapter*, flexcop_access_op_t, + u8 chipaddr, u8 addr, u8 *buf, u16 len); + +/* from flexcop-sram.c */ +int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, + flexcop_sram_dest_target_t target); +void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s); +void flexcop_sram_ctrl(struct flexcop_device *fc, + int usb_wan, int sramdma, int maximumfill); + +/* global prototypes for the flexcop-chip */ +/* from flexcop-fe-tuner.c */ +int flexcop_frontend_init(struct flexcop_device *fc); +void flexcop_frontend_exit(struct flexcop_device *fc); + +/* from flexcop-i2c.c */ +int flexcop_i2c_init(struct flexcop_device *fc); +void flexcop_i2c_exit(struct flexcop_device *fc); + +/* from flexcop-sram.c */ +int flexcop_sram_init(struct flexcop_device *fc); + +/* from flexcop-misc.c */ +void flexcop_determine_revision(struct flexcop_device *fc); +void flexcop_device_name(struct flexcop_device *fc, + const char *prefix, const char *suffix); +void flexcop_dump_reg(struct flexcop_device *fc, + flexcop_ibi_register reg, int num); + +/* from flexcop-hw-filter.c */ +int flexcop_pid_feed_control(struct flexcop_device *fc, + struct dvb_demux_feed *dvbdmxfeed, int onoff); +void flexcop_hw_filter_init(struct flexcop_device *fc); + +void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff); + +void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]); +void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff); + +#endif diff --git a/drivers/media/common/b2c2/flexcop-eeprom.c b/drivers/media/common/b2c2/flexcop-eeprom.c new file mode 100644 index 000000000000..a25373a9bd84 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-eeprom.c @@ -0,0 +1,147 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-eeprom.c - eeprom access methods (currently only MAC address reading) + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +#if 0 +/*EEPROM (Skystar2 has one "24LC08B" chip on board) */ +static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len) +{ + return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len); +} + +static int eeprom_lrc_write(struct adapter *adapter, u32 addr, + u32 len, u8 *wbuf, u8 *rbuf, int retries) +{ +int i; + +for (i = 0; i < retries; i++) { + if (eeprom_write(adapter, addr, wbuf, len) == len) { + if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1) + return 1; + } + } + return 0; +} + +/* These functions could be used to unlock SkyStar2 cards. */ + +static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len) +{ + u8 rbuf[20]; + u8 wbuf[20]; + + if (len != 16) + return 0; + + memcpy(wbuf, key, len); + wbuf[16] = 0; + wbuf[17] = 0; + wbuf[18] = 0; + wbuf[19] = calc_lrc(wbuf, 19); + return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4); +} + +static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len) +{ + u8 buf[20]; + + if (len != 16) + return 0; + + if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0) + return 0; + + memcpy(key, buf, len); + return 1; +} + +static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac) +{ + u8 tmp[8]; + + if (type != 0) { + tmp[0] = mac[0]; + tmp[1] = mac[1]; + tmp[2] = mac[2]; + tmp[3] = mac[5]; + tmp[4] = mac[6]; + tmp[5] = mac[7]; + } else { + tmp[0] = mac[0]; + tmp[1] = mac[1]; + tmp[2] = mac[2]; + tmp[3] = mac[3]; + tmp[4] = mac[4]; + tmp[5] = mac[5]; + } + + tmp[6] = 0; + tmp[7] = calc_lrc(tmp, 7); + + if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8) + return 1; + return 0; +} + +static int flexcop_eeprom_read(struct flexcop_device *fc, + u16 addr, u8 *buf, u16 len) +{ + return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len); +} + +#endif + +static u8 calc_lrc(u8 *buf, int len) +{ + int i; + u8 sum = 0; + for (i = 0; i < len; i++) + sum = sum ^ buf[i]; + return sum; +} + +static int flexcop_eeprom_request(struct flexcop_device *fc, + flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries) +{ + int i,ret = 0; + u8 chipaddr = 0x50 | ((addr >> 8) & 3); + for (i = 0; i < retries; i++) { + ret = fc->i2c_request(&fc->fc_i2c_adap[1], op, chipaddr, + addr & 0xff, buf, len); + if (ret == 0) + break; + } + return ret; +} + +static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, + u8 *buf, u16 len, int retries) +{ + int ret = flexcop_eeprom_request(fc, FC_READ, addr, buf, len, retries); + if (ret == 0) + if (calc_lrc(buf, len - 1) != buf[len - 1]) + ret = -EINVAL; + return ret; +} + +/* JJ's comment about extended == 1: it is not presently used anywhere but was + * added to the low-level functions for possible support of EUI64 */ +int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended) +{ + u8 buf[8]; + int ret = 0; + + if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) { + if (extended != 0) { + err("TODO: extended (EUI64) MAC addresses aren't " + "completely supported yet"); + ret = -EINVAL; + } else + memcpy(fc->dvb_adapter.proposed_mac,buf,6); + } + return ret; +} +EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr); diff --git a/drivers/media/common/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c new file mode 100644 index 000000000000..850a6c606750 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-fe-tuner.c @@ -0,0 +1,678 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling + * see flexcop.c for copyright information + */ +#include +#include "flexcop.h" +#include "mt312.h" +#include "stv0299.h" +#include "s5h1420.h" +#include "itd1000.h" +#include "cx24113.h" +#include "cx24123.h" +#include "isl6421.h" +#include "mt352.h" +#include "bcm3510.h" +#include "nxt200x.h" +#include "dvb-pll.h" +#include "lgdt330x.h" +#include "tuner-simple.h" +#include "stv0297.h" + + +/* Can we use the specified front-end? Remember that if we are compiled + * into the kernel we can't call code that's in modules. */ +#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ + (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) + +/* lnb control */ +#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) +static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct flexcop_device *fc = fe->dvb->priv; + flexcop_ibi_value v; + deb_tuner("polarity/voltage = %u\n", voltage); + + v = fc->read_ibi_reg(fc, misc_204); + switch (voltage) { + case SEC_VOLTAGE_OFF: + v.misc_204.ACPI1_sig = 1; + break; + case SEC_VOLTAGE_13: + v.misc_204.ACPI1_sig = 0; + v.misc_204.LNB_L_H_sig = 0; + break; + case SEC_VOLTAGE_18: + v.misc_204.ACPI1_sig = 0; + v.misc_204.LNB_L_H_sig = 1; + break; + default: + err("unknown SEC_VOLTAGE value"); + return -EINVAL; + } + return fc->write_ibi_reg(fc, misc_204, v); +} +#endif + +#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312) +static int flexcop_sleep(struct dvb_frontend* fe) +{ + struct flexcop_device *fc = fe->dvb->priv; + if (fc->fe_sleep) + return fc->fe_sleep(fe); + return 0; +} +#endif + +/* SkyStar2 DVB-S rev 2.3 */ +#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL) +static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ +/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ + struct flexcop_device *fc = fe->dvb->priv; + flexcop_ibi_value v; + u16 ax; + v.raw = 0; + deb_tuner("tone = %u\n",tone); + + switch (tone) { + case SEC_TONE_ON: + ax = 0x01ff; + break; + case SEC_TONE_OFF: + ax = 0; + break; + default: + err("unknown SEC_TONE value"); + return -EINVAL; + } + + v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */ + v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax; + v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax; + return fc->write_ibi_reg(fc,lnb_switch_freq_200,v); +} + +static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data) +{ + flexcop_set_tone(fe, SEC_TONE_ON); + udelay(data ? 500 : 1000); + flexcop_set_tone(fe, SEC_TONE_OFF); + udelay(data ? 1000 : 500); +} + +static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data) +{ + int i, par = 1, d; + for (i = 7; i >= 0; i--) { + d = (data >> i) & 1; + par ^= d; + flexcop_diseqc_send_bit(fe, d); + } + flexcop_diseqc_send_bit(fe, par); +} + +static int flexcop_send_diseqc_msg(struct dvb_frontend *fe, + int len, u8 *msg, unsigned long burst) +{ + int i; + + flexcop_set_tone(fe, SEC_TONE_OFF); + mdelay(16); + + for (i = 0; i < len; i++) + flexcop_diseqc_send_byte(fe,msg[i]); + mdelay(16); + + if (burst != -1) { + if (burst) + flexcop_diseqc_send_byte(fe, 0xff); + else { + flexcop_set_tone(fe, SEC_TONE_ON); + mdelay(12); + udelay(500); + flexcop_set_tone(fe, SEC_TONE_OFF); + } + msleep(20); + } + return 0; +} + +static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0); +} + +static int flexcop_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t minicmd) +{ + return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd); +} + +static struct mt312_config skystar23_samsung_tbdu18132_config = { + .demod_address = 0x0e, +}; + +static int skystar2_rev23_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + struct dvb_frontend_ops *ops; + + fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c); + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBDU18132)) + return 0; + + ops = &fc->fe->ops; + ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; + ops->diseqc_send_burst = flexcop_diseqc_send_burst; + ops->set_tone = flexcop_set_tone; + ops->set_voltage = flexcop_set_voltage; + fc->fe_sleep = ops->sleep; + ops->sleep = flexcop_sleep; + return 1; +} +#else +#define skystar2_rev23_attach NULL +#endif + +/* SkyStar2 DVB-S rev 2.6 */ +#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL) +static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; bclk = 0x47; + } else if (srate < 3000000) { + aclk = 0xb7; bclk = 0x4b; + } else if (srate < 7000000) { + aclk = 0xb7; bclk = 0x4f; + } else if (srate < 14000000) { + aclk = 0xb7; bclk = 0x53; + } else if (srate < 30000000) { + aclk = 0xb6; bclk = 0x53; + } else if (srate < 45000000) { + aclk = 0xb4; bclk = 0x51; + } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, ratio & 0xf0); + return 0; +} + +static u8 samsung_tbmu24112_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7D, + 0x05, 0x35, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0xC3, + 0x0C, 0x00, + 0x0D, 0x81, + 0x0E, 0x23, + 0x0F, 0x12, + 0x10, 0x7E, + 0x11, 0x84, + 0x12, 0xB9, + 0x13, 0x88, + 0x14, 0x89, + 0x15, 0xC9, + 0x16, 0x00, + 0x17, 0x5C, + 0x18, 0x00, + 0x19, 0x00, + 0x1A, 0x00, + 0x1C, 0x00, + 0x1D, 0x00, + 0x1E, 0x00, + 0x1F, 0x3A, + 0x20, 0x2E, + 0x21, 0x80, + 0x22, 0xFF, + 0x23, 0xC1, + 0x28, 0x00, + 0x29, 0x1E, + 0x2A, 0x14, + 0x2B, 0x0F, + 0x2C, 0x09, + 0x2D, 0x05, + 0x31, 0x1F, + 0x32, 0x19, + 0x33, 0xFE, + 0x34, 0x93, + 0xff, 0xff, +}; + +static struct stv0299_config samsung_tbmu24112_config = { + .demod_address = 0x68, + .inittab = samsung_tbmu24112_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_LK, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = samsung_tbmu24112_set_symbol_rate, +}; + +static int skystar2_rev26_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c); + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBMU24112)) + return 0; + + fc->fe->ops.set_voltage = flexcop_set_voltage; + fc->fe_sleep = fc->fe->ops.sleep; + fc->fe->ops.sleep = flexcop_sleep; + return 1; + +} +#else +#define skystar2_rev26_attach NULL +#endif + +/* SkyStar2 DVB-S rev 2.7 */ +#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000) +static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { + .demod_address = 0x53, + .invert = 1, + .repeated_start_workaround = 1, + .serial_mpeg = 1, +}; + +static struct itd1000_config skystar2_rev2_7_itd1000_config = { + .i2c_address = 0x61, +}; + +static int skystar2_rev27_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + flexcop_ibi_value r108; + struct i2c_adapter *i2c_tuner; + + /* enable no_base_addr - no repeated start when reading */ + fc->fc_i2c_adap[0].no_base_addr = 1; + fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config, + i2c); + if (!fc->fe) + goto fail; + + i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe); + if (!i2c_tuner) + goto fail; + + fc->fe_sleep = fc->fe->ops.sleep; + fc->fe->ops.sleep = flexcop_sleep; + + /* enable no_base_addr - no repeated start when reading */ + fc->fc_i2c_adap[2].no_base_addr = 1; + if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, + 0x08, 1, 1)) { + err("ISL6421 could NOT be attached"); + goto fail_isl; + } + info("ISL6421 successfully attached"); + + /* the ITD1000 requires a lower i2c clock - is it a problem ? */ + r108.raw = 0x00000506; + fc->write_ibi_reg(fc, tw_sm_c_108, r108); + if (!dvb_attach(itd1000_attach, fc->fe, i2c_tuner, + &skystar2_rev2_7_itd1000_config)) { + err("ITD1000 could NOT be attached"); + /* Should i2c clock be restored? */ + goto fail_isl; + } + info("ITD1000 successfully attached"); + + return 1; + +fail_isl: + fc->fc_i2c_adap[2].no_base_addr = 0; +fail: + /* for the next devices we need it again */ + fc->fc_i2c_adap[0].no_base_addr = 0; + return 0; +} +#else +#define skystar2_rev27_attach NULL +#endif + +/* SkyStar2 rev 2.8 */ +#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113) +static struct cx24123_config skystar2_rev2_8_cx24123_config = { + .demod_address = 0x55, + .dont_use_pll = 1, + .agc_callback = cx24113_agc_callback, +}; + +static const struct cx24113_config skystar2_rev2_8_cx24113_config = { + .i2c_addr = 0x54, + .xtal_khz = 10111, +}; + +static int skystar2_rev28_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + struct i2c_adapter *i2c_tuner; + + fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config, + i2c); + if (!fc->fe) + return 0; + + i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe); + if (!i2c_tuner) + return 0; + + if (!dvb_attach(cx24113_attach, fc->fe, &skystar2_rev2_8_cx24113_config, + i2c_tuner)) { + err("CX24113 could NOT be attached"); + return 0; + } + info("CX24113 successfully attached"); + + fc->fc_i2c_adap[2].no_base_addr = 1; + if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, + 0x08, 0, 0)) { + err("ISL6421 could NOT be attached"); + fc->fc_i2c_adap[2].no_base_addr = 0; + return 0; + } + info("ISL6421 successfully attached"); + /* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an + * IR-receiver (PIC16F818) - but the card has no input for that ??? */ + return 1; +} +#else +#define skystar2_rev28_attach NULL +#endif + +/* AirStar DVB-T */ +#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL) +static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) +{ + static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; + static u8 mt352_reset[] = { 0x50, 0x80 }; + static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg[] = { 0x67, 0x28, 0xa1 }; + static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); + return 0; +} + +static struct mt352_config samsung_tdtc9251dh0_config = { + .demod_address = 0x0f, + .demod_init = samsung_tdtc9251dh0_demod_init, +}; + +static int airstar_dvbt_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c); + if (!fc->fe) + return 0; + + return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, + DVB_PLL_SAMSUNG_TDTC9251DH0); +} +#else +#define airstar_dvbt_attach NULL +#endif + +/* AirStar ATSC 1st generation */ +#if FE_SUPPORTED(BCM3510) +static int flexcop_fe_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char* name) +{ + struct flexcop_device *fc = fe->dvb->priv; + return request_firmware(fw, name, fc->dev); +} + +static struct bcm3510_config air2pc_atsc_first_gen_config = { + .demod_address = 0x0f, + .request_firmware = flexcop_fe_request_firmware, +}; + +static int airstar_atsc1_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c); + return fc->fe != NULL; +} +#else +#define airstar_atsc1_attach NULL +#endif + +/* AirStar ATSC 2nd generation */ +#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL) +static struct nxt200x_config samsung_tbmv_config = { + .demod_address = 0x0a, +}; + +static int airstar_atsc2_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c); + if (!fc->fe) + return 0; + + return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, + DVB_PLL_SAMSUNG_TBMV); +} +#else +#define airstar_atsc2_attach NULL +#endif + +/* AirStar ATSC 3rd generation */ +#if FE_SUPPORTED(LGDT330X) +static struct lgdt330x_config air2pc_atsc_hd5000_config = { + .demod_address = 0x59, + .demod_chip = LGDT3303, + .serial_mpeg = 0x04, + .clock_polarity_flip = 1, +}; + +static int airstar_atsc3_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c); + if (!fc->fe) + return 0; + + return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61, + TUNER_LG_TDVS_H06XF); +} +#else +#define airstar_atsc3_attach NULL +#endif + +/* CableStar2 DVB-C */ +#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL) +static u8 alps_tdee4_stv0297_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x48, + 0x01, 0x58, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x30, 0xff, + 0x31, 0x9d, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x29, + 0x35, 0x55, + 0x36, 0x80, + 0x37, 0x6e, + 0x38, 0x9c, + 0x40, 0x1a, + 0x41, 0xfe, + 0x42, 0x33, + 0x43, 0x00, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x51, + 0x4b, 0xf8, + 0x52, 0x30, + 0x53, 0x06, + 0x59, 0x06, + 0x5a, 0x5e, + 0x5b, 0x04, + 0x61, 0x49, + 0x62, 0x0a, + 0x70, 0xff, + 0x71, 0x04, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x20, + 0x81, 0x00, + 0x82, 0x30, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x22, + 0x86, 0x08, + 0x87, 0x1b, + 0x88, 0x00, + 0x89, 0x00, + 0x90, 0x00, + 0x91, 0x04, + 0xa0, 0x86, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x5b, + 0xc1, 0x10, + 0xc2, 0x12, + 0xd0, 0x02, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x02, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x01, + 0xff, 0xff, +}; + +static struct stv0297_config alps_tdee4_stv0297_config = { + .demod_address = 0x1c, + .inittab = alps_tdee4_stv0297_inittab, +}; + +static int cablestar2_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fc_i2c_adap[0].no_base_addr = 1; + fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); + if (!fc->fe) + goto fail; + + /* This tuner doesn't use the stv0297's I2C gate, but instead the + * tuner is connected to a different flexcop I2C adapter. */ + if (fc->fe->ops.i2c_gate_ctrl) + fc->fe->ops.i2c_gate_ctrl(fc->fe, 0); + fc->fe->ops.i2c_gate_ctrl = NULL; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, + &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) + goto fail; + + return 1; + +fail: + /* Reset for next frontend to try */ + fc->fc_i2c_adap[0].no_base_addr = 0; + return 0; +} +#else +#define cablestar2_attach NULL +#endif + +static struct { + flexcop_device_type_t type; + int (*attach)(struct flexcop_device *, struct i2c_adapter *); +} flexcop_frontends[] = { + { FC_SKY_REV27, skystar2_rev27_attach }, + { FC_SKY_REV28, skystar2_rev28_attach }, + { FC_SKY_REV26, skystar2_rev26_attach }, + { FC_AIR_DVBT, airstar_dvbt_attach }, + { FC_AIR_ATSC2, airstar_atsc2_attach }, + { FC_AIR_ATSC3, airstar_atsc3_attach }, + { FC_AIR_ATSC1, airstar_atsc1_attach }, + { FC_CABLE, cablestar2_attach }, + { FC_SKY_REV23, skystar2_rev23_attach }, +}; + +/* try to figure out the frontend */ +int flexcop_frontend_init(struct flexcop_device *fc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) { + if (!flexcop_frontends[i].attach) + continue; + /* type needs to be set before, because of some workarounds + * done based on the probed card type */ + fc->dev_type = flexcop_frontends[i].type; + if (flexcop_frontends[i].attach(fc, &fc->fc_i2c_adap[0].i2c_adap)) + goto fe_found; + /* Clean up partially attached frontend */ + if (fc->fe) { + dvb_frontend_detach(fc->fe); + fc->fe = NULL; + } + } + fc->dev_type = FC_UNK; + err("no frontend driver found for this B2C2/FlexCop adapter"); + return -ENODEV; + +fe_found: + info("found '%s' .", fc->fe->ops.info.name); + if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) { + err("frontend registration failed!"); + dvb_frontend_detach(fc->fe); + fc->fe = NULL; + return -EINVAL; + } + fc->init_state |= FC_STATE_FE_INIT; + return 0; +} + +void flexcop_frontend_exit(struct flexcop_device *fc) +{ + if (fc->init_state & FC_STATE_FE_INIT) { + dvb_unregister_frontend(fc->fe); + dvb_frontend_detach(fc->fe); + } + fc->init_state &= ~FC_STATE_FE_INIT; +} diff --git a/drivers/media/common/b2c2/flexcop-hw-filter.c b/drivers/media/common/b2c2/flexcop-hw-filter.c new file mode 100644 index 000000000000..77e45475f4c7 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-hw-filter.c @@ -0,0 +1,232 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-hw-filter.c - pid and mac address filtering and control functions + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff); + deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off"); +} + +void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff); +} + +static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff); +} + +void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]) +{ + flexcop_ibi_value v418, v41c; + v41c = fc->read_ibi_reg(fc, mac_address_41c); + + v418.mac_address_418.MAC1 = mac[0]; + v418.mac_address_418.MAC2 = mac[1]; + v418.mac_address_418.MAC3 = mac[2]; + v418.mac_address_418.MAC6 = mac[3]; + v41c.mac_address_41c.MAC7 = mac[4]; + v41c.mac_address_41c.MAC8 = mac[5]; + + fc->write_ibi_reg(fc, mac_address_418, v418); + fc->write_ibi_reg(fc, mac_address_41c, v41c); +} + +void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff); +} + +static void flexcop_pid_group_filter(struct flexcop_device *fc, + u16 pid, u16 mask) +{ + /* index_reg_310.extra_index_reg need to 0 or 7 to work */ + flexcop_ibi_value v30c; + v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid; + v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask; + fc->write_ibi_reg(fc, pid_filter_30c, v30c); +} + +static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff) +{ + flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff); +} + +/* this fancy define reduces the code size of the quite similar PID controlling of + * the first 6 PIDs + */ + +#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \ + flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \ +v208 = fc->read_ibi_reg(fc, ctrl_208); \ +vpid.vregname.field = onoff ? pid : 0x1fff; \ +vpid.vregname.trans_field = transval; \ +v208.ctrl_208.enablefield = onoff; \ +fc->write_ibi_reg(fc, vregname, vpid); \ +fc->write_ibi_reg(fc, ctrl_208, v208); + +static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig, + Stream1_trans, 0); +} + +static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig, + Stream2_trans, 0); +} + +static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0); +} + +static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0); +} + +static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0); +} + +static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, + u16 pid, int onoff) +{ + pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0); +} + +static void flexcop_pid_control(struct flexcop_device *fc, + int index, u16 pid, int onoff) +{ + if (pid == 0x2000) + return; + + deb_ts("setting pid: %5d %04x at index %d '%s'\n", + pid, pid, index, onoff ? "on" : "off"); + + /* We could use bit magic here to reduce source code size. + * I decided against it, but to use the real register names */ + switch (index) { + case 0: + flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff); + break; + case 1: + flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff); + break; + case 2: + flexcop_pid_PCR_PID_ctrl(fc, pid, onoff); + break; + case 3: + flexcop_pid_PMT_PID_ctrl(fc, pid, onoff); + break; + case 4: + flexcop_pid_EMM_PID_ctrl(fc, pid, onoff); + break; + case 5: + flexcop_pid_ECM_PID_ctrl(fc, pid, onoff); + break; + default: + if (fc->has_32_hw_pid_filter && index < 38) { + flexcop_ibi_value vpid, vid; + + /* set the index */ + vid = fc->read_ibi_reg(fc, index_reg_310); + vid.index_reg_310.index_reg = index - 6; + fc->write_ibi_reg(fc, index_reg_310, vid); + + vpid = fc->read_ibi_reg(fc, pid_n_reg_314); + vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff; + vpid.pid_n_reg_314.PID_enable_bit = onoff; + fc->write_ibi_reg(fc, pid_n_reg_314, vpid); + } + break; + } +} + +static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff) +{ + if (fc->fullts_streaming_state != onoff) { + deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling"); + flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff)); + flexcop_pid_group_filter_ctrl(fc, onoff); + fc->fullts_streaming_state = onoff; + } + return 0; +} + +int flexcop_pid_feed_control(struct flexcop_device *fc, + struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32; + + fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */ + if (dvbdmxfeed->index >= max_pid_filter) + fc->extra_feedcount += onoff ? 1 : -1; + + /* toggle complete-TS-streaming when: + * - pid_filtering is not enabled and it is the first or last feed requested + * - pid_filtering is enabled, + * - but the number of requested feeds is exceeded + * - or the requested pid is 0x2000 */ + + if (!fc->pid_filtering && fc->feedcount == onoff) + flexcop_toggle_fullts_streaming(fc, onoff); + + if (fc->pid_filtering) { + flexcop_pid_control \ + (fc, dvbdmxfeed->index, dvbdmxfeed->pid, onoff); + + if (fc->extra_feedcount > 0) + flexcop_toggle_fullts_streaming(fc, 1); + else if (dvbdmxfeed->pid == 0x2000) + flexcop_toggle_fullts_streaming(fc, onoff); + else + flexcop_toggle_fullts_streaming(fc, 0); + } + + /* if it was the first or last feed request change the stream-status */ + if (fc->feedcount == onoff) { + flexcop_rcv_data_ctrl(fc, onoff); + if (fc->stream_control) /* device specific stream control */ + fc->stream_control(fc, onoff); + + /* feeding stopped -> reset the flexcop filter*/ + if (onoff == 0) { + flexcop_reset_block_300(fc); + flexcop_hw_filter_init(fc); + } + } + return 0; +} +EXPORT_SYMBOL(flexcop_pid_feed_control); + +void flexcop_hw_filter_init(struct flexcop_device *fc) +{ + int i; + flexcop_ibi_value v; + for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++) + flexcop_pid_control(fc, i, 0x1fff, 0); + + flexcop_pid_group_filter(fc, 0, 0x1fe0); + flexcop_pid_group_filter_ctrl(fc, 0); + + v = fc->read_ibi_reg(fc, pid_filter_308); + v.pid_filter_308.EMM_filter_4 = 1; + v.pid_filter_308.EMM_filter_6 = 0; + fc->write_ibi_reg(fc, pid_filter_308, v); + + flexcop_null_filter_ctrl(fc, 1); +} diff --git a/drivers/media/common/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c new file mode 100644 index 000000000000..965d5eb33752 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-i2c.c @@ -0,0 +1,288 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +#define FC_MAX_I2C_RETRIES 100000 + +static int flexcop_i2c_operation(struct flexcop_device *fc, + flexcop_ibi_value *r100) +{ + int i; + flexcop_ibi_value r; + + r100->tw_sm_c_100.working_start = 1; + deb_i2c("r100 before: %08x\n",r100->raw); + + fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero); + fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */ + + for (i = 0; i < FC_MAX_I2C_RETRIES; i++) { + r = fc->read_ibi_reg(fc, tw_sm_c_100); + + if (!r.tw_sm_c_100.no_base_addr_ack_error) { + if (r.tw_sm_c_100.st_done) { + *r100 = r; + deb_i2c("i2c success\n"); + return 0; + } + } else { + deb_i2c("suffering from an i2c ack_error\n"); + return -EREMOTEIO; + } + } + deb_i2c("tried %d times i2c operation, " + "never finished or too many ack errors.\n", i); + return -EREMOTEIO; +} + +static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c, + flexcop_ibi_value r100, u8 *buf) +{ + flexcop_ibi_value r104; + int len = r100.tw_sm_c_100.total_bytes, + /* remember total_bytes is buflen-1 */ + ret; + + /* work-around to have CableStar2 and SkyStar2 rev 2.7 work + * correctly: + * + * the ITD1000 is behind an i2c-gate which closes automatically + * after an i2c-transaction the STV0297 needs 2 consecutive reads + * one with no_base_addr = 0 and one with 1 + * + * those two work-arounds are conflictin: we check for the card + * type, it is set when probing the ITD1000 */ + if (i2c->fc->dev_type == FC_SKY_REV27) + r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; + + ret = flexcop_i2c_operation(i2c->fc, &r100); + if (ret != 0) { + deb_i2c("Retrying operation\n"); + r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; + ret = flexcop_i2c_operation(i2c->fc, &r100); + } + if (ret != 0) { + deb_i2c("read failed. %d\n", ret); + return ret; + } + + buf[0] = r100.tw_sm_c_100.data1_reg; + + if (len > 0) { + r104 = i2c->fc->read_ibi_reg(i2c->fc, tw_sm_c_104); + deb_i2c("read: r100: %08x, r104: %08x\n", r100.raw, r104.raw); + + /* there is at least one more byte, otherwise we wouldn't be here */ + buf[1] = r104.tw_sm_c_104.data2_reg; + if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg; + if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg; + } + return 0; +} + +static int flexcop_i2c_write4(struct flexcop_device *fc, + flexcop_ibi_value r100, u8 *buf) +{ + flexcop_ibi_value r104; + int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */ + r104.raw = 0; + + /* there is at least one byte, otherwise we wouldn't be here */ + r100.tw_sm_c_100.data1_reg = buf[0]; + r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0; + r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0; + r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0; + + deb_i2c("write: r100: %08x, r104: %08x\n", r100.raw, r104.raw); + + /* write the additional i2c data before doing the actual i2c operation */ + fc->write_ibi_reg(fc, tw_sm_c_104, r104); + return flexcop_i2c_operation(fc, &r100); +} + +int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c, + flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) +{ + int ret; + +#ifdef DUMP_I2C_MESSAGES + int i; +#endif + + u16 bytes_to_transfer; + flexcop_ibi_value r100; + + deb_i2c("op = %d\n",op); + r100.raw = 0; + r100.tw_sm_c_100.chipaddr = chipaddr; + r100.tw_sm_c_100.twoWS_rw = op; + r100.tw_sm_c_100.twoWS_port_reg = i2c->port; + +#ifdef DUMP_I2C_MESSAGES + printk(KERN_DEBUG "%d ", i2c->port); + if (op == FC_READ) + printk("rd("); + else + printk("wr("); + printk("%02x): %02x ", chipaddr, addr); +#endif + + /* in that case addr is the only value -> + * we write it twice as baseaddr and val0 + * BBTI is doing it like that for ISL6421 at least */ + if (i2c->no_base_addr && len == 0 && op == FC_WRITE) { + buf = &addr; + len = 1; + } + + while (len != 0) { + bytes_to_transfer = len > 4 ? 4 : len; + + r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1; + r100.tw_sm_c_100.baseaddr = addr; + + if (op == FC_READ) + ret = flexcop_i2c_read4(i2c, r100, buf); + else + ret = flexcop_i2c_write4(i2c->fc, r100, buf); + +#ifdef DUMP_I2C_MESSAGES + for (i = 0; i < bytes_to_transfer; i++) + printk("%02x ", buf[i]); +#endif + + if (ret < 0) + return ret; + + buf += bytes_to_transfer; + addr += bytes_to_transfer; + len -= bytes_to_transfer; + } + +#ifdef DUMP_I2C_MESSAGES + printk("\n"); +#endif + + return 0; +} +/* exported for PCI i2c */ +EXPORT_SYMBOL(flexcop_i2c_request); + +/* master xfer callback for demodulator */ +static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); + int i, ret = 0; + + /* Some drivers use 1 byte or 0 byte reads as probes, which this + * driver doesn't support. These probes will always fail, so this + * hack makes them always succeed. If one knew how, it would of + * course be better to actually do the read. */ + if (num == 1 && msgs[0].flags == I2C_M_RD && msgs[0].len <= 1) + return 1; + + if (mutex_lock_interruptible(&i2c->fc->i2c_mutex)) + return -ERESTARTSYS; + + for (i = 0; i < num; i++) { + /* reading */ + if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) { + ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr, + msgs[i].buf[0], msgs[i+1].buf, + msgs[i+1].len); + i++; /* skip the following message */ + } else /* writing */ + ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr, + msgs[i].buf[0], &msgs[i].buf[1], + msgs[i].len - 1); + if (ret < 0) { + deb_i2c("i2c master_xfer failed"); + break; + } + } + + mutex_unlock(&i2c->fc->i2c_mutex); + + if (ret == 0) + ret = num; + return ret; +} + +static u32 flexcop_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm flexcop_algo = { + .master_xfer = flexcop_master_xfer, + .functionality = flexcop_i2c_func, +}; + +int flexcop_i2c_init(struct flexcop_device *fc) +{ + int ret; + mutex_init(&fc->i2c_mutex); + + fc->fc_i2c_adap[0].fc = fc; + fc->fc_i2c_adap[1].fc = fc; + fc->fc_i2c_adap[2].fc = fc; + fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD; + fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM; + fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER; + + strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod", + sizeof(fc->fc_i2c_adap[0].i2c_adap.name)); + strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom", + sizeof(fc->fc_i2c_adap[1].i2c_adap.name)); + strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner", + sizeof(fc->fc_i2c_adap[2].i2c_adap.name)); + + i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]); + i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]); + i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]); + + fc->fc_i2c_adap[0].i2c_adap.algo = + fc->fc_i2c_adap[1].i2c_adap.algo = + fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo; + fc->fc_i2c_adap[0].i2c_adap.algo_data = + fc->fc_i2c_adap[1].i2c_adap.algo_data = + fc->fc_i2c_adap[2].i2c_adap.algo_data = NULL; + fc->fc_i2c_adap[0].i2c_adap.dev.parent = + fc->fc_i2c_adap[1].i2c_adap.dev.parent = + fc->fc_i2c_adap[2].i2c_adap.dev.parent = fc->dev; + + ret = i2c_add_adapter(&fc->fc_i2c_adap[0].i2c_adap); + if (ret < 0) + return ret; + + ret = i2c_add_adapter(&fc->fc_i2c_adap[1].i2c_adap); + if (ret < 0) + goto adap_1_failed; + + ret = i2c_add_adapter(&fc->fc_i2c_adap[2].i2c_adap); + if (ret < 0) + goto adap_2_failed; + + fc->init_state |= FC_STATE_I2C_INIT; + return 0; + +adap_2_failed: + i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); +adap_1_failed: + i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); + return ret; +} + +void flexcop_i2c_exit(struct flexcop_device *fc) +{ + if (fc->init_state & FC_STATE_I2C_INIT) { + i2c_del_adapter(&fc->fc_i2c_adap[2].i2c_adap); + i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); + i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); + } + fc->init_state &= ~FC_STATE_I2C_INIT; +} diff --git a/drivers/media/common/b2c2/flexcop-misc.c b/drivers/media/common/b2c2/flexcop-misc.c new file mode 100644 index 000000000000..f06f3a9070f5 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-misc.c @@ -0,0 +1,86 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-misc.c - miscellaneous functions + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +void flexcop_determine_revision(struct flexcop_device *fc) +{ + flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); + + switch (v.misc_204.Rev_N_sig_revision_hi) { + case 0x2: + deb_info("found a FlexCopII.\n"); + fc->rev = FLEXCOP_II; + break; + case 0x3: + deb_info("found a FlexCopIIb.\n"); + fc->rev = FLEXCOP_IIB; + break; + case 0x0: + deb_info("found a FlexCopIII.\n"); + fc->rev = FLEXCOP_III; + break; + default: + err("unknown FlexCop Revision: %x. Please report this to " + "linux-dvb@linuxtv.org.", + v.misc_204.Rev_N_sig_revision_hi); + break; + } + + if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps)) + deb_info("this FlexCop has " + "the additional 32 hardware pid filter.\n"); + else + deb_info("this FlexCop has " + "the 6 basic main hardware pid filter.\n"); + /* bus parts have to decide if hw pid filtering is used or not. */ +} + +static const char *flexcop_revision_names[] = { + "Unknown chip", + "FlexCopII", + "FlexCopIIb", + "FlexCopIII", +}; + +static const char *flexcop_device_names[] = { + [FC_UNK] = "Unknown device", + [FC_CABLE] = "Cable2PC/CableStar 2 DVB-C", + [FC_AIR_DVBT] = "Air2PC/AirStar 2 DVB-T", + [FC_AIR_ATSC1] = "Air2PC/AirStar 2 ATSC 1st generation", + [FC_AIR_ATSC2] = "Air2PC/AirStar 2 ATSC 2nd generation", + [FC_AIR_ATSC3] = "Air2PC/AirStar 2 ATSC 3rd generation (HD5000)", + [FC_SKY_REV23] = "Sky2PC/SkyStar 2 DVB-S rev 2.3 (old version)", + [FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6", + [FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u", + [FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8", +}; + +static const char *flexcop_bus_names[] = { + "USB", + "PCI", +}; + +void flexcop_device_name(struct flexcop_device *fc, + const char *prefix, const char *suffix) +{ + info("%s '%s' at the '%s' bus controlled by a '%s' %s", + prefix, flexcop_device_names[fc->dev_type], + flexcop_bus_names[fc->bus_type], + flexcop_revision_names[fc->rev], suffix); +} + +void flexcop_dump_reg(struct flexcop_device *fc, + flexcop_ibi_register reg, int num) +{ + flexcop_ibi_value v; + int i; + for (i = 0; i < num; i++) { + v = fc->read_ibi_reg(fc, reg+4*i); + deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw); + } + deb_rdump("\n"); +} +EXPORT_SYMBOL(flexcop_dump_reg); diff --git a/drivers/media/common/b2c2/flexcop-reg.h b/drivers/media/common/b2c2/flexcop-reg.h new file mode 100644 index 000000000000..dc4528dcbb98 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-reg.h @@ -0,0 +1,166 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_REG_H__ +#define __FLEXCOP_REG_H__ + +typedef enum { + FLEXCOP_UNK = 0, + FLEXCOP_II, + FLEXCOP_IIB, + FLEXCOP_III, +} flexcop_revision_t; + +typedef enum { + FC_UNK = 0, + FC_CABLE, + FC_AIR_DVBT, + FC_AIR_ATSC1, + FC_AIR_ATSC2, + FC_AIR_ATSC3, + FC_SKY_REV23, + FC_SKY_REV26, + FC_SKY_REV27, + FC_SKY_REV28, +} flexcop_device_type_t; + +typedef enum { + FC_USB = 0, + FC_PCI, +} flexcop_bus_t; + +/* FlexCop IBI Registers */ +#if defined(__LITTLE_ENDIAN) +#include "flexcop_ibi_value_le.h" +#else +#if defined(__BIG_ENDIAN) +#include "flexcop_ibi_value_be.h" +#else +#error no endian defined +#endif +#endif + +#define fc_data_Tag_ID_DVB 0x3e +#define fc_data_Tag_ID_ATSC 0x3f +#define fc_data_Tag_ID_IDSB 0x8b + +#define fc_key_code_default 0x1 +#define fc_key_code_even 0x2 +#define fc_key_code_odd 0x3 + +extern flexcop_ibi_value ibi_zero; + +typedef enum { + FC_I2C_PORT_DEMOD = 1, + FC_I2C_PORT_EEPROM = 2, + FC_I2C_PORT_TUNER = 3, +} flexcop_i2c_port_t; + +typedef enum { + FC_WRITE = 0, + FC_READ = 1, +} flexcop_access_op_t; + +typedef enum { + FC_SRAM_DEST_NET = 1, + FC_SRAM_DEST_CAI = 2, + FC_SRAM_DEST_CAO = 4, + FC_SRAM_DEST_MEDIA = 8 +} flexcop_sram_dest_t; + +typedef enum { + FC_SRAM_DEST_TARGET_WAN_USB = 0, + FC_SRAM_DEST_TARGET_DMA1 = 1, + FC_SRAM_DEST_TARGET_DMA2 = 2, + FC_SRAM_DEST_TARGET_FC3_CA = 3 +} flexcop_sram_dest_target_t; + +typedef enum { + FC_SRAM_2_32KB = 0, /* 64KB */ + FC_SRAM_1_32KB = 1, /* 32KB - default fow FCII */ + FC_SRAM_1_128KB = 2, /* 128KB */ + FC_SRAM_1_48KB = 3, /* 48KB - default for FCIII */ +} flexcop_sram_type_t; + +typedef enum { + FC_WAN_SPEED_4MBITS = 0, + FC_WAN_SPEED_8MBITS = 1, + FC_WAN_SPEED_12MBITS = 2, + FC_WAN_SPEED_16MBITS = 3, +} flexcop_wan_speed_t; + +typedef enum { + FC_DMA_1 = 1, + FC_DMA_2 = 2, +} flexcop_dma_index_t; + +typedef enum { + FC_DMA_SUBADDR_0 = 1, + FC_DMA_SUBADDR_1 = 2, +} flexcop_dma_addr_index_t; + +/* names of the particular registers */ +typedef enum { + dma1_000 = 0x000, + dma1_004 = 0x004, + dma1_008 = 0x008, + dma1_00c = 0x00c, + dma2_010 = 0x010, + dma2_014 = 0x014, + dma2_018 = 0x018, + dma2_01c = 0x01c, + + tw_sm_c_100 = 0x100, + tw_sm_c_104 = 0x104, + tw_sm_c_108 = 0x108, + tw_sm_c_10c = 0x10c, + tw_sm_c_110 = 0x110, + + lnb_switch_freq_200 = 0x200, + misc_204 = 0x204, + ctrl_208 = 0x208, + irq_20c = 0x20c, + sw_reset_210 = 0x210, + misc_214 = 0x214, + mbox_v8_to_host_218 = 0x218, + mbox_host_to_v8_21c = 0x21c, + + pid_filter_300 = 0x300, + pid_filter_304 = 0x304, + pid_filter_308 = 0x308, + pid_filter_30c = 0x30c, + index_reg_310 = 0x310, + pid_n_reg_314 = 0x314, + mac_low_reg_318 = 0x318, + mac_high_reg_31c = 0x31c, + + data_tag_400 = 0x400, + card_id_408 = 0x408, + card_id_40c = 0x40c, + mac_address_418 = 0x418, + mac_address_41c = 0x41c, + + ci_600 = 0x600, + pi_604 = 0x604, + pi_608 = 0x608, + dvb_reg_60c = 0x60c, + + sram_ctrl_reg_700 = 0x700, + net_buf_reg_704 = 0x704, + cai_buf_reg_708 = 0x708, + cao_buf_reg_70c = 0x70c, + media_buf_reg_710 = 0x710, + sram_dest_reg_714 = 0x714, + net_buf_reg_718 = 0x718, + wan_ctrl_reg_71c = 0x71c, +} flexcop_ibi_register; + +#define flexcop_set_ibi_value(reg,attr,val) { \ + flexcop_ibi_value v = fc->read_ibi_reg(fc,reg); \ + v.reg.attr = val; \ + fc->write_ibi_reg(fc,reg,v); \ +} + +#endif diff --git a/drivers/media/common/b2c2/flexcop-sram.c b/drivers/media/common/b2c2/flexcop-sram.c new file mode 100644 index 000000000000..f2199e43e803 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop-sram.c @@ -0,0 +1,363 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-sram.c - functions for controlling the SRAM + * see flexcop.c for copyright information + */ +#include "flexcop.h" + +static void flexcop_sram_set_chip(struct flexcop_device *fc, + flexcop_sram_type_t type) +{ + flexcop_set_ibi_value(wan_ctrl_reg_71c, sram_chip, type); +} + +int flexcop_sram_init(struct flexcop_device *fc) +{ + switch (fc->rev) { + case FLEXCOP_II: + case FLEXCOP_IIB: + flexcop_sram_set_chip(fc, FC_SRAM_1_32KB); + break; + case FLEXCOP_III: + flexcop_sram_set_chip(fc, FC_SRAM_1_48KB); + break; + default: + return -EINVAL; + } + return 0; +} + +int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, + flexcop_sram_dest_target_t target) +{ + flexcop_ibi_value v; + v = fc->read_ibi_reg(fc, sram_dest_reg_714); + + if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) { + err("SRAM destination target to available on FlexCopII(b)\n"); + return -EINVAL; + } + deb_sram("sram dest: %x target: %x\n", dest, target); + + if (dest & FC_SRAM_DEST_NET) + v.sram_dest_reg_714.NET_Dest = target; + if (dest & FC_SRAM_DEST_CAI) + v.sram_dest_reg_714.CAI_Dest = target; + if (dest & FC_SRAM_DEST_CAO) + v.sram_dest_reg_714.CAO_Dest = target; + if (dest & FC_SRAM_DEST_MEDIA) + v.sram_dest_reg_714.MEDIA_Dest = target; + + fc->write_ibi_reg(fc,sram_dest_reg_714,v); + udelay(1000); /* TODO delay really necessary */ + + return 0; +} +EXPORT_SYMBOL(flexcop_sram_set_dest); + +void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s) +{ + flexcop_set_ibi_value(wan_ctrl_reg_71c,wan_speed_sig,s); +} +EXPORT_SYMBOL(flexcop_wan_set_speed); + +void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill) +{ + flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714); + v.sram_dest_reg_714.ctrl_usb_wan = usb_wan; + v.sram_dest_reg_714.ctrl_sramdma = sramdma; + v.sram_dest_reg_714.ctrl_maximumfill = maximumfill; + fc->write_ibi_reg(fc,sram_dest_reg_714,v); +} +EXPORT_SYMBOL(flexcop_sram_ctrl); + +#if 0 +static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) +{ + int i, retries; + u32 command; + + for (i = 0; i < len; i++) { + command = bank | addr | 0x04000000 | (*buf << 0x10); + + retries = 2; + + while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { + mdelay(1); + retries--; + }; + + if (retries == 0) + printk("%s: SRAM timeout\n", __func__); + + write_reg_dw(adapter, 0x700, command); + + buf++; + addr++; + } +} + +static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) +{ + int i, retries; + u32 command, value; + + for (i = 0; i < len; i++) { + command = bank | addr | 0x04008000; + + retries = 10000; + + while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { + mdelay(1); + retries--; + }; + + if (retries == 0) + printk("%s: SRAM timeout\n", __func__); + + write_reg_dw(adapter, 0x700, command); + + retries = 10000; + + while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { + mdelay(1); + retries--; + }; + + if (retries == 0) + printk("%s: SRAM timeout\n", __func__); + + value = read_reg_dw(adapter, 0x700) >> 0x10; + + *buf = (value & 0xff); + + addr++; + buf++; + } +} + +static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) +{ + u32 bank; + + bank = 0; + + if (adapter->dw_sram_type == 0x20000) { + bank = (addr & 0x18000) << 0x0d; + } + + if (adapter->dw_sram_type == 0x00000) { + if ((addr >> 0x0f) == 0) + bank = 0x20000000; + else + bank = 0x10000000; + } + flex_sram_write(adapter, bank, addr & 0x7fff, buf, len); +} + +static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) +{ + u32 bank; + bank = 0; + + if (adapter->dw_sram_type == 0x20000) { + bank = (addr & 0x18000) << 0x0d; + } + + if (adapter->dw_sram_type == 0x00000) { + if ((addr >> 0x0f) == 0) + bank = 0x20000000; + else + bank = 0x10000000; + } + flex_sram_read(adapter, bank, addr & 0x7fff, buf, len); +} + +static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len) +{ + u32 length; + while (len != 0) { + length = len; + /* check if the address range belongs to the same + * 32K memory chip. If not, the data is read + * from one chip at a time */ + if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { + length = (((addr >> 0x0f) + 1) << 0x0f) - addr; + } + + sram_read_chunk(adapter, addr, buf, length); + addr = addr + length; + buf = buf + length; + len = len - length; + } +} + +static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len) +{ + u32 length; + while (len != 0) { + length = len; + + /* check if the address range belongs to the same + * 32K memory chip. If not, the data is + * written to one chip at a time */ + if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { + length = (((addr >> 0x0f) + 1) << 0x0f) - addr; + } + + sram_write_chunk(adapter, addr, buf, length); + addr = addr + length; + buf = buf + length; + len = len - length; + } +} + +static void sram_set_size(struct adapter *adapter, u32 mask) +{ + write_reg_dw(adapter, 0x71c, + (mask | (~0x30000 & read_reg_dw(adapter, 0x71c)))); +} + +static void sram_init(struct adapter *adapter) +{ + u32 tmp; + tmp = read_reg_dw(adapter, 0x71c); + write_reg_dw(adapter, 0x71c, 1); + + if (read_reg_dw(adapter, 0x71c) != 0) { + write_reg_dw(adapter, 0x71c, tmp); + adapter->dw_sram_type = tmp & 0x30000; + ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); + } else { + adapter->dw_sram_type = 0x10000; + ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); + } +} + +static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr) +{ + u8 tmp1, tmp2; + dprintk("%s: mask = %x, addr = %x\n", __func__, mask, addr); + + sram_set_size(adapter, mask); + sram_init(adapter); + + tmp2 = 0xa5; + tmp1 = 0x4f; + + sram_write(adapter, addr, &tmp2, 1); + sram_write(adapter, addr + 4, &tmp1, 1); + + tmp2 = 0; + mdelay(20); + + sram_read(adapter, addr, &tmp2, 1); + sram_read(adapter, addr, &tmp2, 1); + + dprintk("%s: wrote 0xa5, read 0x%2x\n", __func__, tmp2); + + if (tmp2 != 0xa5) + return 0; + + tmp2 = 0x5a; + tmp1 = 0xf4; + + sram_write(adapter, addr, &tmp2, 1); + sram_write(adapter, addr + 4, &tmp1, 1); + + tmp2 = 0; + mdelay(20); + + sram_read(adapter, addr, &tmp2, 1); + sram_read(adapter, addr, &tmp2, 1); + + dprintk("%s: wrote 0x5a, read 0x%2x\n", __func__, tmp2); + + if (tmp2 != 0x5a) + return 0; + return 1; +} + +static u32 sram_length(struct adapter *adapter) +{ + if (adapter->dw_sram_type == 0x10000) + return 32768; /* 32K */ + if (adapter->dw_sram_type == 0x00000) + return 65536; /* 64K */ + if (adapter->dw_sram_type == 0x20000) + return 131072; /* 128K */ + return 32768; /* 32K */ +} + +/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory. + - for 128K there are 4x32K chips at bank 0,1,2,3. + - for 64K there are 2x32K chips at bank 1,2. + - for 32K there is one 32K chip at bank 0. + + FlexCop works only with one bank at a time. The bank is selected + by bits 28-29 of the 0x700 register. + + bank 0 covers addresses 0x00000-0x07fff + bank 1 covers addresses 0x08000-0x0ffff + bank 2 covers addresses 0x10000-0x17fff + bank 3 covers addresses 0x18000-0x1ffff */ + +static int flexcop_sram_detect(struct flexcop_device *fc) +{ + flexcop_ibi_value r208, r71c_0, vr71c_1; + r208 = fc->read_ibi_reg(fc, ctrl_208); + fc->write_ibi_reg(fc, ctrl_208, ibi_zero); + + r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c); + write_reg_dw(adapter, 0x71c, 1); + tmp3 = read_reg_dw(adapter, 0x71c); + dprintk("%s: tmp3 = %x\n", __func__, tmp3); + write_reg_dw(adapter, 0x71c, tmp2); + + // check for internal SRAM ??? + tmp3--; + if (tmp3 != 0) { + sram_set_size(adapter, 0x10000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 32K\n", __func__); + return 32; + } + + if (sram_test_location(adapter, 0x20000, 0x18000) != 0) { + sram_set_size(adapter, 0x20000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 128K\n", __func__); + return 128; + } + + if (sram_test_location(adapter, 0x00000, 0x10000) != 0) { + sram_set_size(adapter, 0x00000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 64K\n", __func__); + return 64; + } + + if (sram_test_location(adapter, 0x10000, 0x00000) != 0) { + sram_set_size(adapter, 0x10000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: sram size = 32K\n", __func__); + return 32; + } + + sram_set_size(adapter, 0x10000); + sram_init(adapter); + write_reg_dw(adapter, 0x208, tmp); + dprintk("%s: SRAM detection failed. Set to 32K \n", __func__); + return 0; +} + +static void sll_detect_sram_size(struct adapter *adapter) +{ + sram_detect_for_flex2(adapter); +} + +#endif diff --git a/drivers/media/common/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c new file mode 100644 index 000000000000..b1e8c99f469b --- /dev/null +++ b/drivers/media/common/b2c2/flexcop.c @@ -0,0 +1,324 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop.c - main module part + * Copyright (C) 2004-9 Patrick Boettcher + * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc + * + * Acknowledgements: + * John Jurrius from BBTI, Inc. for extensive support + * with code examples and data books + * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting) + * + * Contributions to the skystar2-driver have been done by + * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes) + * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code) + * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu) + * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac + * filtering) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "flexcop.h" + +#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip" +#define DRIVER_AUTHOR "Patrick Boettcher demux->priv; + return flexcop_pid_feed_control(fc, dvbdmxfeed, 1); +} + +static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct flexcop_device *fc = dvbdmxfeed->demux->priv; + return flexcop_pid_feed_control(fc, dvbdmxfeed, 0); +} + +static int flexcop_dvb_init(struct flexcop_device *fc) +{ + int ret = dvb_register_adapter(&fc->dvb_adapter, + "FlexCop Digital TV device", fc->owner, + fc->dev, adapter_nr); + if (ret < 0) { + err("error registering DVB adapter"); + return ret; + } + fc->dvb_adapter.priv = fc; + + fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING); + fc->demux.priv = fc; + fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED; + fc->demux.start_feed = flexcop_dvb_start_feed; + fc->demux.stop_feed = flexcop_dvb_stop_feed; + fc->demux.write_to_decoder = NULL; + + ret = dvb_dmx_init(&fc->demux); + if (ret < 0) { + err("dvb_dmx failed: error %d", ret); + goto err_dmx; + } + + fc->hw_frontend.source = DMX_FRONTEND_0; + + fc->dmxdev.filternum = fc->demux.feednum; + fc->dmxdev.demux = &fc->demux.dmx; + fc->dmxdev.capabilities = 0; + ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter); + if (ret < 0) { + err("dvb_dmxdev_init failed: error %d", ret); + goto err_dmx_dev; + } + + ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend); + if (ret < 0) { + err("adding hw_frontend to dmx failed: error %d", ret); + goto err_dmx_add_hw_frontend; + } + + fc->mem_frontend.source = DMX_MEMORY_FE; + ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend); + if (ret < 0) { + err("adding mem_frontend to dmx failed: error %d", ret); + goto err_dmx_add_mem_frontend; + } + + ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend); + if (ret < 0) { + err("connect frontend failed: error %d", ret); + goto err_connect_frontend; + } + + ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx); + if (ret < 0) { + err("dvb_net_init failed: error %d", ret); + goto err_net; + } + + fc->init_state |= FC_STATE_DVB_INIT; + return 0; + +err_net: + fc->demux.dmx.disconnect_frontend(&fc->demux.dmx); +err_connect_frontend: + fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend); +err_dmx_add_mem_frontend: + fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend); +err_dmx_add_hw_frontend: + dvb_dmxdev_release(&fc->dmxdev); +err_dmx_dev: + dvb_dmx_release(&fc->demux); +err_dmx: + dvb_unregister_adapter(&fc->dvb_adapter); + return ret; +} + +static void flexcop_dvb_exit(struct flexcop_device *fc) +{ + if (fc->init_state & FC_STATE_DVB_INIT) { + dvb_net_release(&fc->dvbnet); + + fc->demux.dmx.close(&fc->demux.dmx); + fc->demux.dmx.remove_frontend(&fc->demux.dmx, + &fc->mem_frontend); + fc->demux.dmx.remove_frontend(&fc->demux.dmx, + &fc->hw_frontend); + dvb_dmxdev_release(&fc->dmxdev); + dvb_dmx_release(&fc->demux); + dvb_unregister_adapter(&fc->dvb_adapter); + deb_info("deinitialized dvb stuff\n"); + } + fc->init_state &= ~FC_STATE_DVB_INIT; +} + +/* these methods are necessary to achieve the long-term-goal of hiding the + * struct flexcop_device from the bus-parts */ +void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len) +{ + dvb_dmx_swfilter(&fc->demux, buf, len); +} +EXPORT_SYMBOL(flexcop_pass_dmx_data); + +void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no) +{ + dvb_dmx_swfilter_packets(&fc->demux, buf, no); +} +EXPORT_SYMBOL(flexcop_pass_dmx_packets); + +static void flexcop_reset(struct flexcop_device *fc) +{ + flexcop_ibi_value v210, v204; + + /* reset the flexcop itself */ + fc->write_ibi_reg(fc,ctrl_208,ibi_zero); + + v210.raw = 0; + v210.sw_reset_210.reset_block_000 = 1; + v210.sw_reset_210.reset_block_100 = 1; + v210.sw_reset_210.reset_block_200 = 1; + v210.sw_reset_210.reset_block_300 = 1; + v210.sw_reset_210.reset_block_400 = 1; + v210.sw_reset_210.reset_block_500 = 1; + v210.sw_reset_210.reset_block_600 = 1; + v210.sw_reset_210.reset_block_700 = 1; + v210.sw_reset_210.Block_reset_enable = 0xb2; + v210.sw_reset_210.Special_controls = 0xc259; + fc->write_ibi_reg(fc,sw_reset_210,v210); + msleep(1); + + /* reset the periphical devices */ + + v204 = fc->read_ibi_reg(fc,misc_204); + v204.misc_204.Per_reset_sig = 0; + fc->write_ibi_reg(fc,misc_204,v204); + msleep(1); + v204.misc_204.Per_reset_sig = 1; + fc->write_ibi_reg(fc,misc_204,v204); +} + +void flexcop_reset_block_300(struct flexcop_device *fc) +{ + flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208), + v210 = fc->read_ibi_reg(fc, sw_reset_210); + + deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw); + fc->write_ibi_reg(fc,ctrl_208,ibi_zero); + + v210.sw_reset_210.reset_block_300 = 1; + v210.sw_reset_210.Block_reset_enable = 0xb2; + + fc->write_ibi_reg(fc,sw_reset_210,v210); + fc->write_ibi_reg(fc,ctrl_208,v208_save); +} + +struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len) +{ + void *bus; + struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device), + GFP_KERNEL); + if (!fc) { + err("no memory"); + return NULL; + } + + bus = kzalloc(bus_specific_len, GFP_KERNEL); + if (!bus) { + err("no memory"); + kfree(fc); + return NULL; + } + + fc->bus_specific = bus; + + return fc; +} +EXPORT_SYMBOL(flexcop_device_kmalloc); + +void flexcop_device_kfree(struct flexcop_device *fc) +{ + kfree(fc->bus_specific); + kfree(fc); +} +EXPORT_SYMBOL(flexcop_device_kfree); + +int flexcop_device_initialize(struct flexcop_device *fc) +{ + int ret; + ibi_zero.raw = 0; + + flexcop_reset(fc); + flexcop_determine_revision(fc); + flexcop_sram_init(fc); + flexcop_hw_filter_init(fc); + flexcop_smc_ctrl(fc, 0); + + ret = flexcop_dvb_init(fc); + if (ret) + goto error; + + /* i2c has to be done before doing EEProm stuff - + * because the EEProm is accessed via i2c */ + ret = flexcop_i2c_init(fc); + if (ret) + goto error; + + /* do the MAC address reading after initializing the dvb_adapter */ + if (fc->get_mac_addr(fc, 0) == 0) { + u8 *b = fc->dvb_adapter.proposed_mac; + info("MAC address = %pM", b); + flexcop_set_mac_filter(fc,b); + flexcop_mac_filter_ctrl(fc,1); + } else + warn("reading of MAC address failed.\n"); + + ret = flexcop_frontend_init(fc); + if (ret) + goto error; + + flexcop_device_name(fc,"initialization of","complete"); + return 0; + +error: + flexcop_device_exit(fc); + return ret; +} +EXPORT_SYMBOL(flexcop_device_initialize); + +void flexcop_device_exit(struct flexcop_device *fc) +{ + flexcop_frontend_exit(fc); + flexcop_i2c_exit(fc); + flexcop_dvb_exit(fc); +} +EXPORT_SYMBOL(flexcop_device_exit); + +static int flexcop_module_init(void) +{ + info(DRIVER_NAME " loaded successfully"); + return 0; +} + +static void flexcop_module_cleanup(void) +{ + info(DRIVER_NAME " unloaded successfully"); +} + +module_init(flexcop_module_init); +module_exit(flexcop_module_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/b2c2/flexcop.h b/drivers/media/common/b2c2/flexcop.h new file mode 100644 index 000000000000..897b10c85ad9 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop.h @@ -0,0 +1,29 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop.h - private header file for all flexcop-chip-source files + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_H__ +#define __FLEXCOP_H___ + +#define FC_LOG_PREFIX "b2c2-flexcop" +#include "flexcop-common.h" + +extern int b2c2_flexcop_debug; + +/* debug */ +#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG +#define dprintk(level,args...) \ + do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0) +#else +#define dprintk(level,args...) +#endif + +#define deb_info(args...) dprintk(0x01, args) +#define deb_tuner(args...) dprintk(0x02, args) +#define deb_i2c(args...) dprintk(0x04, args) +#define deb_ts(args...) dprintk(0x08, args) +#define deb_sram(args...) dprintk(0x10, args) +#define deb_rdump(args...) dprintk(0x20, args) + +#endif diff --git a/drivers/media/common/b2c2/flexcop_ibi_value_be.h b/drivers/media/common/b2c2/flexcop_ibi_value_be.h new file mode 100644 index 000000000000..8f64bdbd72bb --- /dev/null +++ b/drivers/media/common/b2c2/flexcop_ibi_value_be.h @@ -0,0 +1,455 @@ +/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * register descriptions + * see flexcop.c for copyright information + */ +/* This file is automatically generated, do not edit things here. */ +#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ +#define __FLEXCOP_IBI_VALUE_INCLUDED__ + +typedef union { + u32 raw; + + struct { + u32 dma_address0 :30; + u32 dma_0No_update : 1; + u32 dma_0start : 1; + } dma_0x0; + + struct { + u32 dma_addr_size :24; + u32 DMA_maxpackets : 8; + } dma_0x4_remap; + + struct { + u32 dma_addr_size :24; + u32 unused : 1; + u32 dma1timer : 7; + } dma_0x4_read; + + struct { + u32 dma_addr_size :24; + u32 dmatimer : 7; + u32 unused : 1; + } dma_0x4_write; + + struct { + u32 dma_cur_addr :30; + u32 unused : 2; + } dma_0x8; + + struct { + u32 dma_address1 :30; + u32 remap_enable : 1; + u32 dma_1start : 1; + } dma_0xc; + + struct { + u32 st_done : 1; + u32 no_base_addr_ack_error : 1; + u32 twoWS_port_reg : 2; + u32 total_bytes : 2; + u32 twoWS_rw : 1; + u32 working_start : 1; + u32 data1_reg : 8; + u32 baseaddr : 8; + u32 reserved1 : 1; + u32 chipaddr : 7; + } tw_sm_c_100; + + struct { + u32 unused : 6; + u32 force_stop : 1; + u32 exlicit_stops : 1; + u32 data4_reg : 8; + u32 data3_reg : 8; + u32 data2_reg : 8; + } tw_sm_c_104; + + struct { + u32 reserved2 :19; + u32 tlo1 : 5; + u32 reserved1 : 2; + u32 thi1 : 6; + } tw_sm_c_108; + + struct { + u32 reserved2 :19; + u32 tlo1 : 5; + u32 reserved1 : 2; + u32 thi1 : 6; + } tw_sm_c_10c; + + struct { + u32 reserved2 :19; + u32 tlo1 : 5; + u32 reserved1 : 2; + u32 thi1 : 6; + } tw_sm_c_110; + + struct { + u32 LNB_CTLPrescaler_sig : 2; + u32 LNB_CTLLowCount_sig :15; + u32 LNB_CTLHighCount_sig :15; + } lnb_switch_freq_200; + + struct { + u32 Rev_N_sig_reserved2 : 1; + u32 Rev_N_sig_caps : 1; + u32 Rev_N_sig_reserved1 : 2; + u32 Rev_N_sig_revision_hi : 4; + u32 reserved :20; + u32 Per_reset_sig : 1; + u32 LNB_L_H_sig : 1; + u32 ACPI3_sig : 1; + u32 ACPI1_sig : 1; + } misc_204; + + struct { + u32 unused : 9; + u32 Mailbox_from_V8_Enable_sig : 1; + u32 DMA2_Size_IRQ_Enable_sig : 1; + u32 DMA1_Size_IRQ_Enable_sig : 1; + u32 DMA2_Timer_Enable_sig : 1; + u32 DMA2_IRQ_Enable_sig : 1; + u32 DMA1_Timer_Enable_sig : 1; + u32 DMA1_IRQ_Enable_sig : 1; + u32 Rcv_Data_sig : 1; + u32 MAC_filter_Mode_sig : 1; + u32 Multi2_Enable_sig : 1; + u32 Per_CA_Enable_sig : 1; + u32 SMC_Enable_sig : 1; + u32 CA_Enable_sig : 1; + u32 WAN_CA_Enable_sig : 1; + u32 WAN_Enable_sig : 1; + u32 Mask_filter_sig : 1; + u32 Null_filter_sig : 1; + u32 ECM_filter_sig : 1; + u32 EMM_filter_sig : 1; + u32 PMT_filter_sig : 1; + u32 PCR_filter_sig : 1; + u32 Stream2_filter_sig : 1; + u32 Stream1_filter_sig : 1; + } ctrl_208; + + struct { + u32 reserved :21; + u32 Transport_Error : 1; + u32 LLC_SNAP_FLAG_set : 1; + u32 Continuity_error_flag : 1; + u32 Data_receiver_error : 1; + u32 Mailbox_from_V8_Status_sig : 1; + u32 DMA2_Size_IRQ_Status : 1; + u32 DMA1_Size_IRQ_Status : 1; + u32 DMA2_Timer_Status : 1; + u32 DMA2_IRQ_Status : 1; + u32 DMA1_Timer_Status : 1; + u32 DMA1_IRQ_Status : 1; + } irq_20c; + + struct { + u32 Special_controls :16; + u32 Block_reset_enable : 8; + u32 reset_block_700 : 1; + u32 reset_block_600 : 1; + u32 reset_block_500 : 1; + u32 reset_block_400 : 1; + u32 reset_block_300 : 1; + u32 reset_block_200 : 1; + u32 reset_block_100 : 1; + u32 reset_block_000 : 1; + } sw_reset_210; + + struct { + u32 unused2 :20; + u32 polarity_PS_ERR_sig : 1; + u32 polarity_PS_SYNC_sig : 1; + u32 polarity_PS_VALID_sig : 1; + u32 polarity_PS_CLK_sig : 1; + u32 unused1 : 3; + u32 s2p_sel_sig : 1; + u32 section_pkg_enable_sig : 1; + u32 halt_V8_sig : 1; + u32 v2WS_oe_sig : 1; + u32 vuart_oe_sig : 1; + } misc_214; + + struct { + u32 Mailbox_from_V8 :32; + } mbox_v8_to_host_218; + + struct { + u32 sysramaccess_busmuster : 1; + u32 sysramaccess_write : 1; + u32 unused : 7; + u32 sysramaccess_addr :15; + u32 sysramaccess_data : 8; + } mbox_host_to_v8_21c; + + struct { + u32 debug_fifo_problem : 1; + u32 debug_flag_write_status00 : 1; + u32 Stream2_trans : 1; + u32 Stream2_PID :13; + u32 debug_flag_pid_saved : 1; + u32 MAC_Multicast_filter : 1; + u32 Stream1_trans : 1; + u32 Stream1_PID :13; + } pid_filter_300; + + struct { + u32 reserved : 2; + u32 PMT_trans : 1; + u32 PMT_PID :13; + u32 debug_overrun2 : 1; + u32 debug_overrun3 : 1; + u32 PCR_trans : 1; + u32 PCR_PID :13; + } pid_filter_304; + + struct { + u32 reserved : 2; + u32 ECM_trans : 1; + u32 ECM_PID :13; + u32 EMM_filter_6 : 1; + u32 EMM_filter_4 : 1; + u32 EMM_trans : 1; + u32 EMM_PID :13; + } pid_filter_308; + + struct { + u32 unused2 : 3; + u32 Group_mask :13; + u32 unused1 : 2; + u32 Group_trans : 1; + u32 Group_PID :13; + } pid_filter_30c_ext_ind_0_7; + + struct { + u32 unused :15; + u32 net_master_read :17; + } pid_filter_30c_ext_ind_1; + + struct { + u32 unused :15; + u32 net_master_write :17; + } pid_filter_30c_ext_ind_2; + + struct { + u32 unused :15; + u32 next_net_master_write :17; + } pid_filter_30c_ext_ind_3; + + struct { + u32 reserved2 : 5; + u32 stack_read :10; + u32 reserved1 : 6; + u32 state_write :10; + u32 unused1 : 1; + } pid_filter_30c_ext_ind_4; + + struct { + u32 unused :22; + u32 stack_cnt :10; + } pid_filter_30c_ext_ind_5; + + struct { + u32 unused : 4; + u32 data_size_reg :12; + u32 write_status4 : 2; + u32 write_status1 : 2; + u32 pid_fsm_save_reg300 : 2; + u32 pid_fsm_save_reg4 : 2; + u32 pid_fsm_save_reg3 : 2; + u32 pid_fsm_save_reg2 : 2; + u32 pid_fsm_save_reg1 : 2; + u32 pid_fsm_save_reg0 : 2; + } pid_filter_30c_ext_ind_6; + + struct { + u32 unused :22; + u32 pass_alltables : 1; + u32 AB_select : 1; + u32 extra_index_reg : 3; + u32 index_reg : 5; + } index_reg_310; + + struct { + u32 reserved :17; + u32 PID_enable_bit : 1; + u32 PID_trans : 1; + u32 PID :13; + } pid_n_reg_314; + + struct { + u32 reserved : 6; + u32 HighAB_bit : 1; + u32 Enable_bit : 1; + u32 A6_byte : 8; + u32 A5_byte : 8; + u32 A4_byte : 8; + } mac_low_reg_318; + + struct { + u32 reserved : 8; + u32 A3_byte : 8; + u32 A2_byte : 8; + u32 A1_byte : 8; + } mac_high_reg_31c; + + struct { + u32 data_Tag_ID :16; + u32 reserved :16; + } data_tag_400; + + struct { + u32 Card_IDbyte3 : 8; + u32 Card_IDbyte4 : 8; + u32 Card_IDbyte5 : 8; + u32 Card_IDbyte6 : 8; + } card_id_408; + + struct { + u32 Card_IDbyte1 : 8; + u32 Card_IDbyte2 : 8; + } card_id_40c; + + struct { + u32 MAC6 : 8; + u32 MAC3 : 8; + u32 MAC2 : 8; + u32 MAC1 : 8; + } mac_address_418; + + struct { + u32 reserved :16; + u32 MAC8 : 8; + u32 MAC7 : 8; + } mac_address_41c; + + struct { + u32 reserved :21; + u32 txbuffempty : 1; + u32 ReceiveByteFrameError : 1; + u32 ReceiveDataReady : 1; + u32 transmitter_data_byte : 8; + } ci_600; + + struct { + u32 pi_component_reg : 3; + u32 pi_rw : 1; + u32 pi_ha :20; + u32 pi_d : 8; + } pi_604; + + struct { + u32 pi_busy_n : 1; + u32 pi_wait_n : 1; + u32 pi_timeout_status : 1; + u32 pi_CiMax_IRQ_n : 1; + u32 config_cclk : 1; + u32 config_cs_n : 1; + u32 config_wr_n : 1; + u32 config_Prog_n : 1; + u32 config_Init_stat : 1; + u32 config_Done_stat : 1; + u32 pcmcia_b_mod_pwr_n : 1; + u32 pcmcia_a_mod_pwr_n : 1; + u32 reserved : 3; + u32 Timer_addr : 5; + u32 unused : 1; + u32 timer_data : 7; + u32 Timer_Load_req : 1; + u32 Timer_Read_req : 1; + u32 oncecycle_read : 1; + u32 serialReset : 1; + } pi_608; + + struct { + u32 reserved : 6; + u32 rw_flag : 1; + u32 dvb_en : 1; + u32 key_array_row : 5; + u32 key_array_col : 3; + u32 key_code : 2; + u32 key_enable : 1; + u32 PID :13; + } dvb_reg_60c; + + struct { + u32 start_sram_ibi : 1; + u32 reserved2 : 1; + u32 ce_pin_reg : 1; + u32 oe_pin_reg : 1; + u32 reserved1 : 3; + u32 sc_xfer_bit : 1; + u32 sram_data : 8; + u32 sram_rw : 1; + u32 sram_addr :15; + } sram_ctrl_reg_700; + + struct { + u32 net_addr_write :16; + u32 net_addr_read :16; + } net_buf_reg_704; + + struct { + u32 cai_cnt : 4; + u32 reserved2 : 6; + u32 cai_write :11; + u32 reserved1 : 5; + u32 cai_read :11; + } cai_buf_reg_708; + + struct { + u32 cao_cnt : 4; + u32 reserved2 : 6; + u32 cap_write :11; + u32 reserved1 : 5; + u32 cao_read :11; + } cao_buf_reg_70c; + + struct { + u32 media_cnt : 4; + u32 reserved2 : 6; + u32 media_write :11; + u32 reserved1 : 5; + u32 media_read :11; + } media_buf_reg_710; + + struct { + u32 reserved :17; + u32 ctrl_maximumfill : 1; + u32 ctrl_sramdma : 1; + u32 ctrl_usb_wan : 1; + u32 cao_ovflow_error : 1; + u32 cai_ovflow_error : 1; + u32 media_ovflow_error : 1; + u32 net_ovflow_error : 1; + u32 MEDIA_Dest : 2; + u32 CAO_Dest : 2; + u32 CAI_Dest : 2; + u32 NET_Dest : 2; + } sram_dest_reg_714; + + struct { + u32 reserved3 :11; + u32 net_addr_write : 1; + u32 reserved2 : 3; + u32 net_addr_read : 1; + u32 reserved1 : 4; + u32 net_cnt :12; + } net_buf_reg_718; + + struct { + u32 reserved3 : 4; + u32 wan_pkt_frame : 4; + u32 reserved2 : 4; + u32 sram_memmap : 2; + u32 sram_chip : 2; + u32 wan_wait_state : 8; + u32 reserved1 : 6; + u32 wan_speed_sig : 2; + } wan_ctrl_reg_71c; +} flexcop_ibi_value; + +#endif diff --git a/drivers/media/common/b2c2/flexcop_ibi_value_le.h b/drivers/media/common/b2c2/flexcop_ibi_value_le.h new file mode 100644 index 000000000000..c75830d7d942 --- /dev/null +++ b/drivers/media/common/b2c2/flexcop_ibi_value_le.h @@ -0,0 +1,455 @@ +/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * register descriptions + * see flexcop.c for copyright information + */ +/* This file is automatically generated, do not edit things here. */ +#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ +#define __FLEXCOP_IBI_VALUE_INCLUDED__ + +typedef union { + u32 raw; + + struct { + u32 dma_0start : 1; + u32 dma_0No_update : 1; + u32 dma_address0 :30; + } dma_0x0; + + struct { + u32 DMA_maxpackets : 8; + u32 dma_addr_size :24; + } dma_0x4_remap; + + struct { + u32 dma1timer : 7; + u32 unused : 1; + u32 dma_addr_size :24; + } dma_0x4_read; + + struct { + u32 unused : 1; + u32 dmatimer : 7; + u32 dma_addr_size :24; + } dma_0x4_write; + + struct { + u32 unused : 2; + u32 dma_cur_addr :30; + } dma_0x8; + + struct { + u32 dma_1start : 1; + u32 remap_enable : 1; + u32 dma_address1 :30; + } dma_0xc; + + struct { + u32 chipaddr : 7; + u32 reserved1 : 1; + u32 baseaddr : 8; + u32 data1_reg : 8; + u32 working_start : 1; + u32 twoWS_rw : 1; + u32 total_bytes : 2; + u32 twoWS_port_reg : 2; + u32 no_base_addr_ack_error : 1; + u32 st_done : 1; + } tw_sm_c_100; + + struct { + u32 data2_reg : 8; + u32 data3_reg : 8; + u32 data4_reg : 8; + u32 exlicit_stops : 1; + u32 force_stop : 1; + u32 unused : 6; + } tw_sm_c_104; + + struct { + u32 thi1 : 6; + u32 reserved1 : 2; + u32 tlo1 : 5; + u32 reserved2 :19; + } tw_sm_c_108; + + struct { + u32 thi1 : 6; + u32 reserved1 : 2; + u32 tlo1 : 5; + u32 reserved2 :19; + } tw_sm_c_10c; + + struct { + u32 thi1 : 6; + u32 reserved1 : 2; + u32 tlo1 : 5; + u32 reserved2 :19; + } tw_sm_c_110; + + struct { + u32 LNB_CTLHighCount_sig :15; + u32 LNB_CTLLowCount_sig :15; + u32 LNB_CTLPrescaler_sig : 2; + } lnb_switch_freq_200; + + struct { + u32 ACPI1_sig : 1; + u32 ACPI3_sig : 1; + u32 LNB_L_H_sig : 1; + u32 Per_reset_sig : 1; + u32 reserved :20; + u32 Rev_N_sig_revision_hi : 4; + u32 Rev_N_sig_reserved1 : 2; + u32 Rev_N_sig_caps : 1; + u32 Rev_N_sig_reserved2 : 1; + } misc_204; + + struct { + u32 Stream1_filter_sig : 1; + u32 Stream2_filter_sig : 1; + u32 PCR_filter_sig : 1; + u32 PMT_filter_sig : 1; + u32 EMM_filter_sig : 1; + u32 ECM_filter_sig : 1; + u32 Null_filter_sig : 1; + u32 Mask_filter_sig : 1; + u32 WAN_Enable_sig : 1; + u32 WAN_CA_Enable_sig : 1; + u32 CA_Enable_sig : 1; + u32 SMC_Enable_sig : 1; + u32 Per_CA_Enable_sig : 1; + u32 Multi2_Enable_sig : 1; + u32 MAC_filter_Mode_sig : 1; + u32 Rcv_Data_sig : 1; + u32 DMA1_IRQ_Enable_sig : 1; + u32 DMA1_Timer_Enable_sig : 1; + u32 DMA2_IRQ_Enable_sig : 1; + u32 DMA2_Timer_Enable_sig : 1; + u32 DMA1_Size_IRQ_Enable_sig : 1; + u32 DMA2_Size_IRQ_Enable_sig : 1; + u32 Mailbox_from_V8_Enable_sig : 1; + u32 unused : 9; + } ctrl_208; + + struct { + u32 DMA1_IRQ_Status : 1; + u32 DMA1_Timer_Status : 1; + u32 DMA2_IRQ_Status : 1; + u32 DMA2_Timer_Status : 1; + u32 DMA1_Size_IRQ_Status : 1; + u32 DMA2_Size_IRQ_Status : 1; + u32 Mailbox_from_V8_Status_sig : 1; + u32 Data_receiver_error : 1; + u32 Continuity_error_flag : 1; + u32 LLC_SNAP_FLAG_set : 1; + u32 Transport_Error : 1; + u32 reserved :21; + } irq_20c; + + struct { + u32 reset_block_000 : 1; + u32 reset_block_100 : 1; + u32 reset_block_200 : 1; + u32 reset_block_300 : 1; + u32 reset_block_400 : 1; + u32 reset_block_500 : 1; + u32 reset_block_600 : 1; + u32 reset_block_700 : 1; + u32 Block_reset_enable : 8; + u32 Special_controls :16; + } sw_reset_210; + + struct { + u32 vuart_oe_sig : 1; + u32 v2WS_oe_sig : 1; + u32 halt_V8_sig : 1; + u32 section_pkg_enable_sig : 1; + u32 s2p_sel_sig : 1; + u32 unused1 : 3; + u32 polarity_PS_CLK_sig : 1; + u32 polarity_PS_VALID_sig : 1; + u32 polarity_PS_SYNC_sig : 1; + u32 polarity_PS_ERR_sig : 1; + u32 unused2 :20; + } misc_214; + + struct { + u32 Mailbox_from_V8 :32; + } mbox_v8_to_host_218; + + struct { + u32 sysramaccess_data : 8; + u32 sysramaccess_addr :15; + u32 unused : 7; + u32 sysramaccess_write : 1; + u32 sysramaccess_busmuster : 1; + } mbox_host_to_v8_21c; + + struct { + u32 Stream1_PID :13; + u32 Stream1_trans : 1; + u32 MAC_Multicast_filter : 1; + u32 debug_flag_pid_saved : 1; + u32 Stream2_PID :13; + u32 Stream2_trans : 1; + u32 debug_flag_write_status00 : 1; + u32 debug_fifo_problem : 1; + } pid_filter_300; + + struct { + u32 PCR_PID :13; + u32 PCR_trans : 1; + u32 debug_overrun3 : 1; + u32 debug_overrun2 : 1; + u32 PMT_PID :13; + u32 PMT_trans : 1; + u32 reserved : 2; + } pid_filter_304; + + struct { + u32 EMM_PID :13; + u32 EMM_trans : 1; + u32 EMM_filter_4 : 1; + u32 EMM_filter_6 : 1; + u32 ECM_PID :13; + u32 ECM_trans : 1; + u32 reserved : 2; + } pid_filter_308; + + struct { + u32 Group_PID :13; + u32 Group_trans : 1; + u32 unused1 : 2; + u32 Group_mask :13; + u32 unused2 : 3; + } pid_filter_30c_ext_ind_0_7; + + struct { + u32 net_master_read :17; + u32 unused :15; + } pid_filter_30c_ext_ind_1; + + struct { + u32 net_master_write :17; + u32 unused :15; + } pid_filter_30c_ext_ind_2; + + struct { + u32 next_net_master_write :17; + u32 unused :15; + } pid_filter_30c_ext_ind_3; + + struct { + u32 unused1 : 1; + u32 state_write :10; + u32 reserved1 : 6; + u32 stack_read :10; + u32 reserved2 : 5; + } pid_filter_30c_ext_ind_4; + + struct { + u32 stack_cnt :10; + u32 unused :22; + } pid_filter_30c_ext_ind_5; + + struct { + u32 pid_fsm_save_reg0 : 2; + u32 pid_fsm_save_reg1 : 2; + u32 pid_fsm_save_reg2 : 2; + u32 pid_fsm_save_reg3 : 2; + u32 pid_fsm_save_reg4 : 2; + u32 pid_fsm_save_reg300 : 2; + u32 write_status1 : 2; + u32 write_status4 : 2; + u32 data_size_reg :12; + u32 unused : 4; + } pid_filter_30c_ext_ind_6; + + struct { + u32 index_reg : 5; + u32 extra_index_reg : 3; + u32 AB_select : 1; + u32 pass_alltables : 1; + u32 unused :22; + } index_reg_310; + + struct { + u32 PID :13; + u32 PID_trans : 1; + u32 PID_enable_bit : 1; + u32 reserved :17; + } pid_n_reg_314; + + struct { + u32 A4_byte : 8; + u32 A5_byte : 8; + u32 A6_byte : 8; + u32 Enable_bit : 1; + u32 HighAB_bit : 1; + u32 reserved : 6; + } mac_low_reg_318; + + struct { + u32 A1_byte : 8; + u32 A2_byte : 8; + u32 A3_byte : 8; + u32 reserved : 8; + } mac_high_reg_31c; + + struct { + u32 reserved :16; + u32 data_Tag_ID :16; + } data_tag_400; + + struct { + u32 Card_IDbyte6 : 8; + u32 Card_IDbyte5 : 8; + u32 Card_IDbyte4 : 8; + u32 Card_IDbyte3 : 8; + } card_id_408; + + struct { + u32 Card_IDbyte2 : 8; + u32 Card_IDbyte1 : 8; + } card_id_40c; + + struct { + u32 MAC1 : 8; + u32 MAC2 : 8; + u32 MAC3 : 8; + u32 MAC6 : 8; + } mac_address_418; + + struct { + u32 MAC7 : 8; + u32 MAC8 : 8; + u32 reserved :16; + } mac_address_41c; + + struct { + u32 transmitter_data_byte : 8; + u32 ReceiveDataReady : 1; + u32 ReceiveByteFrameError : 1; + u32 txbuffempty : 1; + u32 reserved :21; + } ci_600; + + struct { + u32 pi_d : 8; + u32 pi_ha :20; + u32 pi_rw : 1; + u32 pi_component_reg : 3; + } pi_604; + + struct { + u32 serialReset : 1; + u32 oncecycle_read : 1; + u32 Timer_Read_req : 1; + u32 Timer_Load_req : 1; + u32 timer_data : 7; + u32 unused : 1; + u32 Timer_addr : 5; + u32 reserved : 3; + u32 pcmcia_a_mod_pwr_n : 1; + u32 pcmcia_b_mod_pwr_n : 1; + u32 config_Done_stat : 1; + u32 config_Init_stat : 1; + u32 config_Prog_n : 1; + u32 config_wr_n : 1; + u32 config_cs_n : 1; + u32 config_cclk : 1; + u32 pi_CiMax_IRQ_n : 1; + u32 pi_timeout_status : 1; + u32 pi_wait_n : 1; + u32 pi_busy_n : 1; + } pi_608; + + struct { + u32 PID :13; + u32 key_enable : 1; + u32 key_code : 2; + u32 key_array_col : 3; + u32 key_array_row : 5; + u32 dvb_en : 1; + u32 rw_flag : 1; + u32 reserved : 6; + } dvb_reg_60c; + + struct { + u32 sram_addr :15; + u32 sram_rw : 1; + u32 sram_data : 8; + u32 sc_xfer_bit : 1; + u32 reserved1 : 3; + u32 oe_pin_reg : 1; + u32 ce_pin_reg : 1; + u32 reserved2 : 1; + u32 start_sram_ibi : 1; + } sram_ctrl_reg_700; + + struct { + u32 net_addr_read :16; + u32 net_addr_write :16; + } net_buf_reg_704; + + struct { + u32 cai_read :11; + u32 reserved1 : 5; + u32 cai_write :11; + u32 reserved2 : 6; + u32 cai_cnt : 4; + } cai_buf_reg_708; + + struct { + u32 cao_read :11; + u32 reserved1 : 5; + u32 cap_write :11; + u32 reserved2 : 6; + u32 cao_cnt : 4; + } cao_buf_reg_70c; + + struct { + u32 media_read :11; + u32 reserved1 : 5; + u32 media_write :11; + u32 reserved2 : 6; + u32 media_cnt : 4; + } media_buf_reg_710; + + struct { + u32 NET_Dest : 2; + u32 CAI_Dest : 2; + u32 CAO_Dest : 2; + u32 MEDIA_Dest : 2; + u32 net_ovflow_error : 1; + u32 media_ovflow_error : 1; + u32 cai_ovflow_error : 1; + u32 cao_ovflow_error : 1; + u32 ctrl_usb_wan : 1; + u32 ctrl_sramdma : 1; + u32 ctrl_maximumfill : 1; + u32 reserved :17; + } sram_dest_reg_714; + + struct { + u32 net_cnt :12; + u32 reserved1 : 4; + u32 net_addr_read : 1; + u32 reserved2 : 3; + u32 net_addr_write : 1; + u32 reserved3 :11; + } net_buf_reg_718; + + struct { + u32 wan_speed_sig : 2; + u32 reserved1 : 6; + u32 wan_wait_state : 8; + u32 sram_chip : 2; + u32 sram_memmap : 2; + u32 reserved2 : 4; + u32 wan_pkt_frame : 4; + u32 reserved3 : 4; + } wan_ctrl_reg_71c; +} flexcop_ibi_value; + +#endif diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 3b9164af6ec4..b16529bf71b8 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -3,48 +3,39 @@ # menuconfig DVB_CAPTURE_DRIVERS - bool "DVB/ATSC adapters" + bool "DVB/ATSC PCI adapters" depends on DVB_CORE default y ---help--- Say Y to select Digital TV adapters -if DVB_CAPTURE_DRIVERS && DVB_CORE +if DVB_CAPTURE_DRIVERS && DVB_CORE && PCI && I2C comment "Supported SAA7146 based PCI Adapters" - depends on DVB_CORE && PCI && I2C source "drivers/media/pci/ttpci/Kconfig" -comment "Supported FlexCopII (B2C2) Adapters" - depends on DVB_CORE && (PCI || USB) && I2C +comment "Supported FlexCopII (B2C2) PCI Adapters" source "drivers/media/pci/b2c2/Kconfig" comment "Supported BT878 Adapters" - depends on DVB_CORE && PCI && I2C source "drivers/media/pci/bt8xx/Kconfig" comment "Supported Pluto2 Adapters" - depends on DVB_CORE && PCI && I2C source "drivers/media/pci/pluto2/Kconfig" comment "Supported SDMC DM1105 Adapters" - depends on DVB_CORE && PCI && I2C source "drivers/media/pci/dm1105/Kconfig" comment "Supported Earthsoft PT1 Adapters" - depends on DVB_CORE && PCI && I2C source "drivers/media/pci/pt1/Kconfig" comment "Supported Mantis Adapters" - depends on DVB_CORE && PCI && I2C - source "drivers/media/pci/mantis/Kconfig" +source "drivers/media/pci/mantis/Kconfig" comment "Supported nGene Adapters" - depends on DVB_CORE && PCI && I2C - source "drivers/media/pci/ngene/Kconfig" +source "drivers/media/pci/ngene/Kconfig" comment "Supported ddbridge ('Octopus') Adapters" - depends on DVB_CORE && PCI && I2C - source "drivers/media/pci/ddbridge/Kconfig" +source "drivers/media/pci/ddbridge/Kconfig" endif # DVB_CAPTURE_DRIVERS diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index c5fa43a275ae..1d44fbd772b2 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -10,4 +10,5 @@ obj-y := ttpci/ \ pt1/ \ mantis/ \ ngene/ \ - ddbridge/ + ddbridge/ \ + b2c2/ diff --git a/drivers/media/pci/b2c2/Kconfig b/drivers/media/pci/b2c2/Kconfig index 9e5781400744..aaa1f30f1ae0 100644 --- a/drivers/media/pci/b2c2/Kconfig +++ b/drivers/media/pci/b2c2/Kconfig @@ -1,45 +1,6 @@ -config DVB_B2C2_FLEXCOP - tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters" - depends on DVB_CORE && I2C - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_BCM3510 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_S5H1420 if !DVB_FE_CUSTOMISE - select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_CX24123 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE - help - Support for the digital TV receiver chip made by B2C2 Inc. included in - Technisats PCI cards and USB boxes. - - Say Y if you own such a device and want to use it. - config DVB_B2C2_FLEXCOP_PCI tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI" - depends on DVB_B2C2_FLEXCOP && PCI && I2C help Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2. Say Y if you own such a device and want to use it. - -config DVB_B2C2_FLEXCOP_USB - tristate "Technisat/B2C2 Air/Sky/Cable2PC USB" - depends on DVB_B2C2_FLEXCOP && USB && I2C - help - Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2, - - Say Y if you own such a device and want to use it. - -config DVB_B2C2_FLEXCOP_DEBUG - bool "Enable debug for the B2C2 FlexCop drivers" - depends on DVB_B2C2_FLEXCOP - help - Say Y if you want to enable the module option to control debug messages - of all B2C2 FlexCop drivers. diff --git a/drivers/media/pci/b2c2/Makefile b/drivers/media/pci/b2c2/Makefile index 7a1f5ce6d322..e90e2366265e 100644 --- a/drivers/media/pci/b2c2/Makefile +++ b/drivers/media/pci/b2c2/Makefile @@ -1,16 +1,11 @@ -b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ - flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o -obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o - ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),) -b2c2-flexcop-objs += flexcop-dma.o +b2c2-flexcop-pci-objs += flexcop-dma.o endif b2c2-flexcop-pci-objs = flexcop-pci.o obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o -b2c2-flexcop-usb-objs = flexcop-usb.o -obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o - -ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ +ccflags-y += -Idrivers/media/dvb-core/ +ccflags-y += -Idrivers/media/dvb-frontends/ ccflags-y += -Idrivers/media/common/tuners/ +ccflags-y += -Idrivers/media/common/b2c2/ diff --git a/drivers/media/pci/b2c2/flexcop-common.h b/drivers/media/pci/b2c2/flexcop-common.h deleted file mode 100644 index 437912e49824..000000000000 --- a/drivers/media/pci/b2c2/flexcop-common.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-common.h - common header file for device-specific source files - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_COMMON_H__ -#define __FLEXCOP_COMMON_H__ - -#include -#include -#include - -#include "flexcop-reg.h" - -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_filter.h" -#include "dvb_net.h" -#include "dvb_frontend.h" - -#define FC_MAX_FEED 256 - -#ifndef FC_LOG_PREFIX -#warning please define a log prefix for your file, using a default one -#define FC_LOG_PREFIX "b2c2-undef" -#endif - -/* Steal from usb.h */ -#undef err -#define err(format, arg...) \ - printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg) -#undef info -#define info(format, arg...) \ - printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg) -#undef warn -#define warn(format, arg...) \ - printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg) - -struct flexcop_dma { - struct pci_dev *pdev; - - u8 *cpu_addr0; - dma_addr_t dma_addr0; - u8 *cpu_addr1; - dma_addr_t dma_addr1; - u32 size; /* size of each address in bytes */ -}; - -struct flexcop_i2c_adapter { - struct flexcop_device *fc; - struct i2c_adapter i2c_adap; - - u8 no_base_addr; - flexcop_i2c_port_t port; -}; - -/* Control structure for data definitions that are common to - * the B2C2-based PCI and USB devices. - */ -struct flexcop_device { - /* general */ - struct device *dev; /* for firmware_class */ - -#define FC_STATE_DVB_INIT 0x01 -#define FC_STATE_I2C_INIT 0x02 -#define FC_STATE_FE_INIT 0x04 - int init_state; - - /* device information */ - int has_32_hw_pid_filter; - flexcop_revision_t rev; - flexcop_device_type_t dev_type; - flexcop_bus_t bus_type; - - /* dvb stuff */ - struct dvb_adapter dvb_adapter; - struct dvb_frontend *fe; - struct dvb_net dvbnet; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - int (*fe_sleep) (struct dvb_frontend *); - - struct flexcop_i2c_adapter fc_i2c_adap[3]; - struct mutex i2c_mutex; - struct module *owner; - - /* options and status */ - int extra_feedcount; - int feedcount; - int pid_filtering; - int fullts_streaming_state; - - /* bus specific callbacks */ - flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *, - flexcop_ibi_register); - int (*write_ibi_reg) (struct flexcop_device *, - flexcop_ibi_register, flexcop_ibi_value); - int (*i2c_request) (struct flexcop_i2c_adapter *, - flexcop_access_op_t, u8 chipaddr, u8 addr, u8 *buf, u16 len); - int (*stream_control) (struct flexcop_device *, int); - int (*get_mac_addr) (struct flexcop_device *fc, int extended); - void *bus_specific; -}; - -/* exported prototypes */ - -/* from flexcop.c */ -void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len); -void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no); - -struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len); -void flexcop_device_kfree(struct flexcop_device *); - -int flexcop_device_initialize(struct flexcop_device *); -void flexcop_device_exit(struct flexcop_device *fc); -void flexcop_reset_block_300(struct flexcop_device *fc); - -/* from flexcop-dma.c */ -int flexcop_dma_allocate(struct pci_dev *pdev, - struct flexcop_dma *dma, u32 size); -void flexcop_dma_free(struct flexcop_dma *dma); - -int flexcop_dma_control_timer_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, int onoff); -int flexcop_dma_control_size_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, int onoff); -int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, - flexcop_dma_index_t dma_idx); -int flexcop_dma_xfer_control(struct flexcop_device *fc, - flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index, - int onoff); -int flexcop_dma_config_timer(struct flexcop_device *fc, - flexcop_dma_index_t dma_idx, u8 cycles); - -/* from flexcop-eeprom.c */ -/* the PCI part uses this call to get the MAC address, the USB part has its own */ -int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended); - -/* from flexcop-i2c.c */ -/* the PCI part uses this a i2c_request callback, whereas the usb part has its own - * one. We have it in flexcop-i2c.c, because it is going via the actual - * I2C-channel of the flexcop. - */ -int flexcop_i2c_request(struct flexcop_i2c_adapter*, flexcop_access_op_t, - u8 chipaddr, u8 addr, u8 *buf, u16 len); - -/* from flexcop-sram.c */ -int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, - flexcop_sram_dest_target_t target); -void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s); -void flexcop_sram_ctrl(struct flexcop_device *fc, - int usb_wan, int sramdma, int maximumfill); - -/* global prototypes for the flexcop-chip */ -/* from flexcop-fe-tuner.c */ -int flexcop_frontend_init(struct flexcop_device *fc); -void flexcop_frontend_exit(struct flexcop_device *fc); - -/* from flexcop-i2c.c */ -int flexcop_i2c_init(struct flexcop_device *fc); -void flexcop_i2c_exit(struct flexcop_device *fc); - -/* from flexcop-sram.c */ -int flexcop_sram_init(struct flexcop_device *fc); - -/* from flexcop-misc.c */ -void flexcop_determine_revision(struct flexcop_device *fc); -void flexcop_device_name(struct flexcop_device *fc, - const char *prefix, const char *suffix); -void flexcop_dump_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, int num); - -/* from flexcop-hw-filter.c */ -int flexcop_pid_feed_control(struct flexcop_device *fc, - struct dvb_demux_feed *dvbdmxfeed, int onoff); -void flexcop_hw_filter_init(struct flexcop_device *fc); - -void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff); - -void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]); -void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff); - -#endif diff --git a/drivers/media/pci/b2c2/flexcop-eeprom.c b/drivers/media/pci/b2c2/flexcop-eeprom.c deleted file mode 100644 index a25373a9bd84..000000000000 --- a/drivers/media/pci/b2c2/flexcop-eeprom.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-eeprom.c - eeprom access methods (currently only MAC address reading) - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -#if 0 -/*EEPROM (Skystar2 has one "24LC08B" chip on board) */ -static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len) -{ - return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len); -} - -static int eeprom_lrc_write(struct adapter *adapter, u32 addr, - u32 len, u8 *wbuf, u8 *rbuf, int retries) -{ -int i; - -for (i = 0; i < retries; i++) { - if (eeprom_write(adapter, addr, wbuf, len) == len) { - if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1) - return 1; - } - } - return 0; -} - -/* These functions could be used to unlock SkyStar2 cards. */ - -static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len) -{ - u8 rbuf[20]; - u8 wbuf[20]; - - if (len != 16) - return 0; - - memcpy(wbuf, key, len); - wbuf[16] = 0; - wbuf[17] = 0; - wbuf[18] = 0; - wbuf[19] = calc_lrc(wbuf, 19); - return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4); -} - -static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len) -{ - u8 buf[20]; - - if (len != 16) - return 0; - - if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0) - return 0; - - memcpy(key, buf, len); - return 1; -} - -static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac) -{ - u8 tmp[8]; - - if (type != 0) { - tmp[0] = mac[0]; - tmp[1] = mac[1]; - tmp[2] = mac[2]; - tmp[3] = mac[5]; - tmp[4] = mac[6]; - tmp[5] = mac[7]; - } else { - tmp[0] = mac[0]; - tmp[1] = mac[1]; - tmp[2] = mac[2]; - tmp[3] = mac[3]; - tmp[4] = mac[4]; - tmp[5] = mac[5]; - } - - tmp[6] = 0; - tmp[7] = calc_lrc(tmp, 7); - - if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8) - return 1; - return 0; -} - -static int flexcop_eeprom_read(struct flexcop_device *fc, - u16 addr, u8 *buf, u16 len) -{ - return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len); -} - -#endif - -static u8 calc_lrc(u8 *buf, int len) -{ - int i; - u8 sum = 0; - for (i = 0; i < len; i++) - sum = sum ^ buf[i]; - return sum; -} - -static int flexcop_eeprom_request(struct flexcop_device *fc, - flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries) -{ - int i,ret = 0; - u8 chipaddr = 0x50 | ((addr >> 8) & 3); - for (i = 0; i < retries; i++) { - ret = fc->i2c_request(&fc->fc_i2c_adap[1], op, chipaddr, - addr & 0xff, buf, len); - if (ret == 0) - break; - } - return ret; -} - -static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, - u8 *buf, u16 len, int retries) -{ - int ret = flexcop_eeprom_request(fc, FC_READ, addr, buf, len, retries); - if (ret == 0) - if (calc_lrc(buf, len - 1) != buf[len - 1]) - ret = -EINVAL; - return ret; -} - -/* JJ's comment about extended == 1: it is not presently used anywhere but was - * added to the low-level functions for possible support of EUI64 */ -int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended) -{ - u8 buf[8]; - int ret = 0; - - if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) { - if (extended != 0) { - err("TODO: extended (EUI64) MAC addresses aren't " - "completely supported yet"); - ret = -EINVAL; - } else - memcpy(fc->dvb_adapter.proposed_mac,buf,6); - } - return ret; -} -EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr); diff --git a/drivers/media/pci/b2c2/flexcop-fe-tuner.c b/drivers/media/pci/b2c2/flexcop-fe-tuner.c deleted file mode 100644 index 850a6c606750..000000000000 --- a/drivers/media/pci/b2c2/flexcop-fe-tuner.c +++ /dev/null @@ -1,678 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling - * see flexcop.c for copyright information - */ -#include -#include "flexcop.h" -#include "mt312.h" -#include "stv0299.h" -#include "s5h1420.h" -#include "itd1000.h" -#include "cx24113.h" -#include "cx24123.h" -#include "isl6421.h" -#include "mt352.h" -#include "bcm3510.h" -#include "nxt200x.h" -#include "dvb-pll.h" -#include "lgdt330x.h" -#include "tuner-simple.h" -#include "stv0297.h" - - -/* Can we use the specified front-end? Remember that if we are compiled - * into the kernel we can't call code that's in modules. */ -#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ - (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) - -/* lnb control */ -#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) -static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct flexcop_device *fc = fe->dvb->priv; - flexcop_ibi_value v; - deb_tuner("polarity/voltage = %u\n", voltage); - - v = fc->read_ibi_reg(fc, misc_204); - switch (voltage) { - case SEC_VOLTAGE_OFF: - v.misc_204.ACPI1_sig = 1; - break; - case SEC_VOLTAGE_13: - v.misc_204.ACPI1_sig = 0; - v.misc_204.LNB_L_H_sig = 0; - break; - case SEC_VOLTAGE_18: - v.misc_204.ACPI1_sig = 0; - v.misc_204.LNB_L_H_sig = 1; - break; - default: - err("unknown SEC_VOLTAGE value"); - return -EINVAL; - } - return fc->write_ibi_reg(fc, misc_204, v); -} -#endif - -#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312) -static int flexcop_sleep(struct dvb_frontend* fe) -{ - struct flexcop_device *fc = fe->dvb->priv; - if (fc->fe_sleep) - return fc->fe_sleep(fe); - return 0; -} -#endif - -/* SkyStar2 DVB-S rev 2.3 */ -#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL) -static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) -{ -/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ - struct flexcop_device *fc = fe->dvb->priv; - flexcop_ibi_value v; - u16 ax; - v.raw = 0; - deb_tuner("tone = %u\n",tone); - - switch (tone) { - case SEC_TONE_ON: - ax = 0x01ff; - break; - case SEC_TONE_OFF: - ax = 0; - break; - default: - err("unknown SEC_TONE value"); - return -EINVAL; - } - - v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */ - v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax; - v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax; - return fc->write_ibi_reg(fc,lnb_switch_freq_200,v); -} - -static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data) -{ - flexcop_set_tone(fe, SEC_TONE_ON); - udelay(data ? 500 : 1000); - flexcop_set_tone(fe, SEC_TONE_OFF); - udelay(data ? 1000 : 500); -} - -static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data) -{ - int i, par = 1, d; - for (i = 7; i >= 0; i--) { - d = (data >> i) & 1; - par ^= d; - flexcop_diseqc_send_bit(fe, d); - } - flexcop_diseqc_send_bit(fe, par); -} - -static int flexcop_send_diseqc_msg(struct dvb_frontend *fe, - int len, u8 *msg, unsigned long burst) -{ - int i; - - flexcop_set_tone(fe, SEC_TONE_OFF); - mdelay(16); - - for (i = 0; i < len; i++) - flexcop_diseqc_send_byte(fe,msg[i]); - mdelay(16); - - if (burst != -1) { - if (burst) - flexcop_diseqc_send_byte(fe, 0xff); - else { - flexcop_set_tone(fe, SEC_TONE_ON); - mdelay(12); - udelay(500); - flexcop_set_tone(fe, SEC_TONE_OFF); - } - msleep(20); - } - return 0; -} - -static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe, - struct dvb_diseqc_master_cmd *cmd) -{ - return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0); -} - -static int flexcop_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t minicmd) -{ - return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd); -} - -static struct mt312_config skystar23_samsung_tbdu18132_config = { - .demod_address = 0x0e, -}; - -static int skystar2_rev23_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - struct dvb_frontend_ops *ops; - - fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c); - if (!fc->fe) - return 0; - - if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, - DVB_PLL_SAMSUNG_TBDU18132)) - return 0; - - ops = &fc->fe->ops; - ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; - ops->diseqc_send_burst = flexcop_diseqc_send_burst; - ops->set_tone = flexcop_set_tone; - ops->set_voltage = flexcop_set_voltage; - fc->fe_sleep = ops->sleep; - ops->sleep = flexcop_sleep; - return 1; -} -#else -#define skystar2_rev23_attach NULL -#endif - -/* SkyStar2 DVB-S rev 2.6 */ -#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL) -static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, - u32 srate, u32 ratio) -{ - u8 aclk = 0; - u8 bclk = 0; - - if (srate < 1500000) { - aclk = 0xb7; bclk = 0x47; - } else if (srate < 3000000) { - aclk = 0xb7; bclk = 0x4b; - } else if (srate < 7000000) { - aclk = 0xb7; bclk = 0x4f; - } else if (srate < 14000000) { - aclk = 0xb7; bclk = 0x53; - } else if (srate < 30000000) { - aclk = 0xb6; bclk = 0x53; - } else if (srate < 45000000) { - aclk = 0xb4; bclk = 0x51; - } - - stv0299_writereg(fe, 0x13, aclk); - stv0299_writereg(fe, 0x14, bclk); - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, ratio & 0xf0); - return 0; -} - -static u8 samsung_tbmu24112_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x7D, - 0x05, 0x35, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0xC3, - 0x0C, 0x00, - 0x0D, 0x81, - 0x0E, 0x23, - 0x0F, 0x12, - 0x10, 0x7E, - 0x11, 0x84, - 0x12, 0xB9, - 0x13, 0x88, - 0x14, 0x89, - 0x15, 0xC9, - 0x16, 0x00, - 0x17, 0x5C, - 0x18, 0x00, - 0x19, 0x00, - 0x1A, 0x00, - 0x1C, 0x00, - 0x1D, 0x00, - 0x1E, 0x00, - 0x1F, 0x3A, - 0x20, 0x2E, - 0x21, 0x80, - 0x22, 0xFF, - 0x23, 0xC1, - 0x28, 0x00, - 0x29, 0x1E, - 0x2A, 0x14, - 0x2B, 0x0F, - 0x2C, 0x09, - 0x2D, 0x05, - 0x31, 0x1F, - 0x32, 0x19, - 0x33, 0xFE, - 0x34, 0x93, - 0xff, 0xff, -}; - -static struct stv0299_config samsung_tbmu24112_config = { - .demod_address = 0x68, - .inittab = samsung_tbmu24112_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_LK, - .volt13_op0_op1 = STV0299_VOLT13_OP1, - .min_delay_ms = 100, - .set_symbol_rate = samsung_tbmu24112_set_symbol_rate, -}; - -static int skystar2_rev26_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c); - if (!fc->fe) - return 0; - - if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, - DVB_PLL_SAMSUNG_TBMU24112)) - return 0; - - fc->fe->ops.set_voltage = flexcop_set_voltage; - fc->fe_sleep = fc->fe->ops.sleep; - fc->fe->ops.sleep = flexcop_sleep; - return 1; - -} -#else -#define skystar2_rev26_attach NULL -#endif - -/* SkyStar2 DVB-S rev 2.7 */ -#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000) -static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { - .demod_address = 0x53, - .invert = 1, - .repeated_start_workaround = 1, - .serial_mpeg = 1, -}; - -static struct itd1000_config skystar2_rev2_7_itd1000_config = { - .i2c_address = 0x61, -}; - -static int skystar2_rev27_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - flexcop_ibi_value r108; - struct i2c_adapter *i2c_tuner; - - /* enable no_base_addr - no repeated start when reading */ - fc->fc_i2c_adap[0].no_base_addr = 1; - fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config, - i2c); - if (!fc->fe) - goto fail; - - i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe); - if (!i2c_tuner) - goto fail; - - fc->fe_sleep = fc->fe->ops.sleep; - fc->fe->ops.sleep = flexcop_sleep; - - /* enable no_base_addr - no repeated start when reading */ - fc->fc_i2c_adap[2].no_base_addr = 1; - if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, - 0x08, 1, 1)) { - err("ISL6421 could NOT be attached"); - goto fail_isl; - } - info("ISL6421 successfully attached"); - - /* the ITD1000 requires a lower i2c clock - is it a problem ? */ - r108.raw = 0x00000506; - fc->write_ibi_reg(fc, tw_sm_c_108, r108); - if (!dvb_attach(itd1000_attach, fc->fe, i2c_tuner, - &skystar2_rev2_7_itd1000_config)) { - err("ITD1000 could NOT be attached"); - /* Should i2c clock be restored? */ - goto fail_isl; - } - info("ITD1000 successfully attached"); - - return 1; - -fail_isl: - fc->fc_i2c_adap[2].no_base_addr = 0; -fail: - /* for the next devices we need it again */ - fc->fc_i2c_adap[0].no_base_addr = 0; - return 0; -} -#else -#define skystar2_rev27_attach NULL -#endif - -/* SkyStar2 rev 2.8 */ -#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113) -static struct cx24123_config skystar2_rev2_8_cx24123_config = { - .demod_address = 0x55, - .dont_use_pll = 1, - .agc_callback = cx24113_agc_callback, -}; - -static const struct cx24113_config skystar2_rev2_8_cx24113_config = { - .i2c_addr = 0x54, - .xtal_khz = 10111, -}; - -static int skystar2_rev28_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - struct i2c_adapter *i2c_tuner; - - fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config, - i2c); - if (!fc->fe) - return 0; - - i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe); - if (!i2c_tuner) - return 0; - - if (!dvb_attach(cx24113_attach, fc->fe, &skystar2_rev2_8_cx24113_config, - i2c_tuner)) { - err("CX24113 could NOT be attached"); - return 0; - } - info("CX24113 successfully attached"); - - fc->fc_i2c_adap[2].no_base_addr = 1; - if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, - 0x08, 0, 0)) { - err("ISL6421 could NOT be attached"); - fc->fc_i2c_adap[2].no_base_addr = 0; - return 0; - } - info("ISL6421 successfully attached"); - /* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an - * IR-receiver (PIC16F818) - but the card has no input for that ??? */ - return 1; -} -#else -#define skystar2_rev28_attach NULL -#endif - -/* AirStar DVB-T */ -#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL) -static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) -{ - static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; - static u8 mt352_reset[] = { 0x50, 0x80 }; - static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; - static u8 mt352_agc_cfg[] = { 0x67, 0x28, 0xa1 }; - static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 }; - - mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(fe, mt352_reset, sizeof(mt352_reset)); - mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - return 0; -} - -static struct mt352_config samsung_tdtc9251dh0_config = { - .demod_address = 0x0f, - .demod_init = samsung_tdtc9251dh0_demod_init, -}; - -static int airstar_dvbt_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c); - if (!fc->fe) - return 0; - - return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, - DVB_PLL_SAMSUNG_TDTC9251DH0); -} -#else -#define airstar_dvbt_attach NULL -#endif - -/* AirStar ATSC 1st generation */ -#if FE_SUPPORTED(BCM3510) -static int flexcop_fe_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char* name) -{ - struct flexcop_device *fc = fe->dvb->priv; - return request_firmware(fw, name, fc->dev); -} - -static struct bcm3510_config air2pc_atsc_first_gen_config = { - .demod_address = 0x0f, - .request_firmware = flexcop_fe_request_firmware, -}; - -static int airstar_atsc1_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c); - return fc->fe != NULL; -} -#else -#define airstar_atsc1_attach NULL -#endif - -/* AirStar ATSC 2nd generation */ -#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL) -static struct nxt200x_config samsung_tbmv_config = { - .demod_address = 0x0a, -}; - -static int airstar_atsc2_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c); - if (!fc->fe) - return 0; - - return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, - DVB_PLL_SAMSUNG_TBMV); -} -#else -#define airstar_atsc2_attach NULL -#endif - -/* AirStar ATSC 3rd generation */ -#if FE_SUPPORTED(LGDT330X) -static struct lgdt330x_config air2pc_atsc_hd5000_config = { - .demod_address = 0x59, - .demod_chip = LGDT3303, - .serial_mpeg = 0x04, - .clock_polarity_flip = 1, -}; - -static int airstar_atsc3_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c); - if (!fc->fe) - return 0; - - return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61, - TUNER_LG_TDVS_H06XF); -} -#else -#define airstar_atsc3_attach NULL -#endif - -/* CableStar2 DVB-C */ -#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL) -static u8 alps_tdee4_stv0297_inittab[] = { - 0x80, 0x01, - 0x80, 0x00, - 0x81, 0x01, - 0x81, 0x00, - 0x00, 0x48, - 0x01, 0x58, - 0x03, 0x00, - 0x04, 0x00, - 0x07, 0x00, - 0x08, 0x00, - 0x30, 0xff, - 0x31, 0x9d, - 0x32, 0xff, - 0x33, 0x00, - 0x34, 0x29, - 0x35, 0x55, - 0x36, 0x80, - 0x37, 0x6e, - 0x38, 0x9c, - 0x40, 0x1a, - 0x41, 0xfe, - 0x42, 0x33, - 0x43, 0x00, - 0x44, 0xff, - 0x45, 0x00, - 0x46, 0x00, - 0x49, 0x04, - 0x4a, 0x51, - 0x4b, 0xf8, - 0x52, 0x30, - 0x53, 0x06, - 0x59, 0x06, - 0x5a, 0x5e, - 0x5b, 0x04, - 0x61, 0x49, - 0x62, 0x0a, - 0x70, 0xff, - 0x71, 0x04, - 0x72, 0x00, - 0x73, 0x00, - 0x74, 0x0c, - 0x80, 0x20, - 0x81, 0x00, - 0x82, 0x30, - 0x83, 0x00, - 0x84, 0x04, - 0x85, 0x22, - 0x86, 0x08, - 0x87, 0x1b, - 0x88, 0x00, - 0x89, 0x00, - 0x90, 0x00, - 0x91, 0x04, - 0xa0, 0x86, - 0xa1, 0x00, - 0xa2, 0x00, - 0xb0, 0x91, - 0xb1, 0x0b, - 0xc0, 0x5b, - 0xc1, 0x10, - 0xc2, 0x12, - 0xd0, 0x02, - 0xd1, 0x00, - 0xd2, 0x00, - 0xd3, 0x00, - 0xd4, 0x02, - 0xd5, 0x00, - 0xde, 0x00, - 0xdf, 0x01, - 0xff, 0xff, -}; - -static struct stv0297_config alps_tdee4_stv0297_config = { - .demod_address = 0x1c, - .inittab = alps_tdee4_stv0297_inittab, -}; - -static int cablestar2_attach(struct flexcop_device *fc, - struct i2c_adapter *i2c) -{ - fc->fc_i2c_adap[0].no_base_addr = 1; - fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); - if (!fc->fe) - goto fail; - - /* This tuner doesn't use the stv0297's I2C gate, but instead the - * tuner is connected to a different flexcop I2C adapter. */ - if (fc->fe->ops.i2c_gate_ctrl) - fc->fe->ops.i2c_gate_ctrl(fc->fe, 0); - fc->fe->ops.i2c_gate_ctrl = NULL; - - if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, - &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) - goto fail; - - return 1; - -fail: - /* Reset for next frontend to try */ - fc->fc_i2c_adap[0].no_base_addr = 0; - return 0; -} -#else -#define cablestar2_attach NULL -#endif - -static struct { - flexcop_device_type_t type; - int (*attach)(struct flexcop_device *, struct i2c_adapter *); -} flexcop_frontends[] = { - { FC_SKY_REV27, skystar2_rev27_attach }, - { FC_SKY_REV28, skystar2_rev28_attach }, - { FC_SKY_REV26, skystar2_rev26_attach }, - { FC_AIR_DVBT, airstar_dvbt_attach }, - { FC_AIR_ATSC2, airstar_atsc2_attach }, - { FC_AIR_ATSC3, airstar_atsc3_attach }, - { FC_AIR_ATSC1, airstar_atsc1_attach }, - { FC_CABLE, cablestar2_attach }, - { FC_SKY_REV23, skystar2_rev23_attach }, -}; - -/* try to figure out the frontend */ -int flexcop_frontend_init(struct flexcop_device *fc) -{ - int i; - for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) { - if (!flexcop_frontends[i].attach) - continue; - /* type needs to be set before, because of some workarounds - * done based on the probed card type */ - fc->dev_type = flexcop_frontends[i].type; - if (flexcop_frontends[i].attach(fc, &fc->fc_i2c_adap[0].i2c_adap)) - goto fe_found; - /* Clean up partially attached frontend */ - if (fc->fe) { - dvb_frontend_detach(fc->fe); - fc->fe = NULL; - } - } - fc->dev_type = FC_UNK; - err("no frontend driver found for this B2C2/FlexCop adapter"); - return -ENODEV; - -fe_found: - info("found '%s' .", fc->fe->ops.info.name); - if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) { - err("frontend registration failed!"); - dvb_frontend_detach(fc->fe); - fc->fe = NULL; - return -EINVAL; - } - fc->init_state |= FC_STATE_FE_INIT; - return 0; -} - -void flexcop_frontend_exit(struct flexcop_device *fc) -{ - if (fc->init_state & FC_STATE_FE_INIT) { - dvb_unregister_frontend(fc->fe); - dvb_frontend_detach(fc->fe); - } - fc->init_state &= ~FC_STATE_FE_INIT; -} diff --git a/drivers/media/pci/b2c2/flexcop-hw-filter.c b/drivers/media/pci/b2c2/flexcop-hw-filter.c deleted file mode 100644 index 77e45475f4c7..000000000000 --- a/drivers/media/pci/b2c2/flexcop-hw-filter.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-hw-filter.c - pid and mac address filtering and control functions - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff); - deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off"); -} - -void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff); -} - -static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff); -} - -void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]) -{ - flexcop_ibi_value v418, v41c; - v41c = fc->read_ibi_reg(fc, mac_address_41c); - - v418.mac_address_418.MAC1 = mac[0]; - v418.mac_address_418.MAC2 = mac[1]; - v418.mac_address_418.MAC3 = mac[2]; - v418.mac_address_418.MAC6 = mac[3]; - v41c.mac_address_41c.MAC7 = mac[4]; - v41c.mac_address_41c.MAC8 = mac[5]; - - fc->write_ibi_reg(fc, mac_address_418, v418); - fc->write_ibi_reg(fc, mac_address_41c, v41c); -} - -void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff); -} - -static void flexcop_pid_group_filter(struct flexcop_device *fc, - u16 pid, u16 mask) -{ - /* index_reg_310.extra_index_reg need to 0 or 7 to work */ - flexcop_ibi_value v30c; - v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid; - v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask; - fc->write_ibi_reg(fc, pid_filter_30c, v30c); -} - -static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff) -{ - flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff); -} - -/* this fancy define reduces the code size of the quite similar PID controlling of - * the first 6 PIDs - */ - -#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \ - flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \ -v208 = fc->read_ibi_reg(fc, ctrl_208); \ -vpid.vregname.field = onoff ? pid : 0x1fff; \ -vpid.vregname.trans_field = transval; \ -v208.ctrl_208.enablefield = onoff; \ -fc->write_ibi_reg(fc, vregname, vpid); \ -fc->write_ibi_reg(fc, ctrl_208, v208); - -static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig, - Stream1_trans, 0); -} - -static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig, - Stream2_trans, 0); -} - -static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0); -} - -static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0); -} - -static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0); -} - -static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, - u16 pid, int onoff) -{ - pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0); -} - -static void flexcop_pid_control(struct flexcop_device *fc, - int index, u16 pid, int onoff) -{ - if (pid == 0x2000) - return; - - deb_ts("setting pid: %5d %04x at index %d '%s'\n", - pid, pid, index, onoff ? "on" : "off"); - - /* We could use bit magic here to reduce source code size. - * I decided against it, but to use the real register names */ - switch (index) { - case 0: - flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff); - break; - case 1: - flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff); - break; - case 2: - flexcop_pid_PCR_PID_ctrl(fc, pid, onoff); - break; - case 3: - flexcop_pid_PMT_PID_ctrl(fc, pid, onoff); - break; - case 4: - flexcop_pid_EMM_PID_ctrl(fc, pid, onoff); - break; - case 5: - flexcop_pid_ECM_PID_ctrl(fc, pid, onoff); - break; - default: - if (fc->has_32_hw_pid_filter && index < 38) { - flexcop_ibi_value vpid, vid; - - /* set the index */ - vid = fc->read_ibi_reg(fc, index_reg_310); - vid.index_reg_310.index_reg = index - 6; - fc->write_ibi_reg(fc, index_reg_310, vid); - - vpid = fc->read_ibi_reg(fc, pid_n_reg_314); - vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff; - vpid.pid_n_reg_314.PID_enable_bit = onoff; - fc->write_ibi_reg(fc, pid_n_reg_314, vpid); - } - break; - } -} - -static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff) -{ - if (fc->fullts_streaming_state != onoff) { - deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling"); - flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff)); - flexcop_pid_group_filter_ctrl(fc, onoff); - fc->fullts_streaming_state = onoff; - } - return 0; -} - -int flexcop_pid_feed_control(struct flexcop_device *fc, - struct dvb_demux_feed *dvbdmxfeed, int onoff) -{ - int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32; - - fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */ - if (dvbdmxfeed->index >= max_pid_filter) - fc->extra_feedcount += onoff ? 1 : -1; - - /* toggle complete-TS-streaming when: - * - pid_filtering is not enabled and it is the first or last feed requested - * - pid_filtering is enabled, - * - but the number of requested feeds is exceeded - * - or the requested pid is 0x2000 */ - - if (!fc->pid_filtering && fc->feedcount == onoff) - flexcop_toggle_fullts_streaming(fc, onoff); - - if (fc->pid_filtering) { - flexcop_pid_control \ - (fc, dvbdmxfeed->index, dvbdmxfeed->pid, onoff); - - if (fc->extra_feedcount > 0) - flexcop_toggle_fullts_streaming(fc, 1); - else if (dvbdmxfeed->pid == 0x2000) - flexcop_toggle_fullts_streaming(fc, onoff); - else - flexcop_toggle_fullts_streaming(fc, 0); - } - - /* if it was the first or last feed request change the stream-status */ - if (fc->feedcount == onoff) { - flexcop_rcv_data_ctrl(fc, onoff); - if (fc->stream_control) /* device specific stream control */ - fc->stream_control(fc, onoff); - - /* feeding stopped -> reset the flexcop filter*/ - if (onoff == 0) { - flexcop_reset_block_300(fc); - flexcop_hw_filter_init(fc); - } - } - return 0; -} -EXPORT_SYMBOL(flexcop_pid_feed_control); - -void flexcop_hw_filter_init(struct flexcop_device *fc) -{ - int i; - flexcop_ibi_value v; - for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++) - flexcop_pid_control(fc, i, 0x1fff, 0); - - flexcop_pid_group_filter(fc, 0, 0x1fe0); - flexcop_pid_group_filter_ctrl(fc, 0); - - v = fc->read_ibi_reg(fc, pid_filter_308); - v.pid_filter_308.EMM_filter_4 = 1; - v.pid_filter_308.EMM_filter_6 = 0; - fc->write_ibi_reg(fc, pid_filter_308, v); - - flexcop_null_filter_ctrl(fc, 1); -} diff --git a/drivers/media/pci/b2c2/flexcop-i2c.c b/drivers/media/pci/b2c2/flexcop-i2c.c deleted file mode 100644 index 965d5eb33752..000000000000 --- a/drivers/media/pci/b2c2/flexcop-i2c.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -#define FC_MAX_I2C_RETRIES 100000 - -static int flexcop_i2c_operation(struct flexcop_device *fc, - flexcop_ibi_value *r100) -{ - int i; - flexcop_ibi_value r; - - r100->tw_sm_c_100.working_start = 1; - deb_i2c("r100 before: %08x\n",r100->raw); - - fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero); - fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */ - - for (i = 0; i < FC_MAX_I2C_RETRIES; i++) { - r = fc->read_ibi_reg(fc, tw_sm_c_100); - - if (!r.tw_sm_c_100.no_base_addr_ack_error) { - if (r.tw_sm_c_100.st_done) { - *r100 = r; - deb_i2c("i2c success\n"); - return 0; - } - } else { - deb_i2c("suffering from an i2c ack_error\n"); - return -EREMOTEIO; - } - } - deb_i2c("tried %d times i2c operation, " - "never finished or too many ack errors.\n", i); - return -EREMOTEIO; -} - -static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c, - flexcop_ibi_value r100, u8 *buf) -{ - flexcop_ibi_value r104; - int len = r100.tw_sm_c_100.total_bytes, - /* remember total_bytes is buflen-1 */ - ret; - - /* work-around to have CableStar2 and SkyStar2 rev 2.7 work - * correctly: - * - * the ITD1000 is behind an i2c-gate which closes automatically - * after an i2c-transaction the STV0297 needs 2 consecutive reads - * one with no_base_addr = 0 and one with 1 - * - * those two work-arounds are conflictin: we check for the card - * type, it is set when probing the ITD1000 */ - if (i2c->fc->dev_type == FC_SKY_REV27) - r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; - - ret = flexcop_i2c_operation(i2c->fc, &r100); - if (ret != 0) { - deb_i2c("Retrying operation\n"); - r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; - ret = flexcop_i2c_operation(i2c->fc, &r100); - } - if (ret != 0) { - deb_i2c("read failed. %d\n", ret); - return ret; - } - - buf[0] = r100.tw_sm_c_100.data1_reg; - - if (len > 0) { - r104 = i2c->fc->read_ibi_reg(i2c->fc, tw_sm_c_104); - deb_i2c("read: r100: %08x, r104: %08x\n", r100.raw, r104.raw); - - /* there is at least one more byte, otherwise we wouldn't be here */ - buf[1] = r104.tw_sm_c_104.data2_reg; - if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg; - if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg; - } - return 0; -} - -static int flexcop_i2c_write4(struct flexcop_device *fc, - flexcop_ibi_value r100, u8 *buf) -{ - flexcop_ibi_value r104; - int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */ - r104.raw = 0; - - /* there is at least one byte, otherwise we wouldn't be here */ - r100.tw_sm_c_100.data1_reg = buf[0]; - r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0; - r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0; - r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0; - - deb_i2c("write: r100: %08x, r104: %08x\n", r100.raw, r104.raw); - - /* write the additional i2c data before doing the actual i2c operation */ - fc->write_ibi_reg(fc, tw_sm_c_104, r104); - return flexcop_i2c_operation(fc, &r100); -} - -int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c, - flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) -{ - int ret; - -#ifdef DUMP_I2C_MESSAGES - int i; -#endif - - u16 bytes_to_transfer; - flexcop_ibi_value r100; - - deb_i2c("op = %d\n",op); - r100.raw = 0; - r100.tw_sm_c_100.chipaddr = chipaddr; - r100.tw_sm_c_100.twoWS_rw = op; - r100.tw_sm_c_100.twoWS_port_reg = i2c->port; - -#ifdef DUMP_I2C_MESSAGES - printk(KERN_DEBUG "%d ", i2c->port); - if (op == FC_READ) - printk("rd("); - else - printk("wr("); - printk("%02x): %02x ", chipaddr, addr); -#endif - - /* in that case addr is the only value -> - * we write it twice as baseaddr and val0 - * BBTI is doing it like that for ISL6421 at least */ - if (i2c->no_base_addr && len == 0 && op == FC_WRITE) { - buf = &addr; - len = 1; - } - - while (len != 0) { - bytes_to_transfer = len > 4 ? 4 : len; - - r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1; - r100.tw_sm_c_100.baseaddr = addr; - - if (op == FC_READ) - ret = flexcop_i2c_read4(i2c, r100, buf); - else - ret = flexcop_i2c_write4(i2c->fc, r100, buf); - -#ifdef DUMP_I2C_MESSAGES - for (i = 0; i < bytes_to_transfer; i++) - printk("%02x ", buf[i]); -#endif - - if (ret < 0) - return ret; - - buf += bytes_to_transfer; - addr += bytes_to_transfer; - len -= bytes_to_transfer; - } - -#ifdef DUMP_I2C_MESSAGES - printk("\n"); -#endif - - return 0; -} -/* exported for PCI i2c */ -EXPORT_SYMBOL(flexcop_i2c_request); - -/* master xfer callback for demodulator */ -static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) -{ - struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); - int i, ret = 0; - - /* Some drivers use 1 byte or 0 byte reads as probes, which this - * driver doesn't support. These probes will always fail, so this - * hack makes them always succeed. If one knew how, it would of - * course be better to actually do the read. */ - if (num == 1 && msgs[0].flags == I2C_M_RD && msgs[0].len <= 1) - return 1; - - if (mutex_lock_interruptible(&i2c->fc->i2c_mutex)) - return -ERESTARTSYS; - - for (i = 0; i < num; i++) { - /* reading */ - if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) { - ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr, - msgs[i].buf[0], msgs[i+1].buf, - msgs[i+1].len); - i++; /* skip the following message */ - } else /* writing */ - ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr, - msgs[i].buf[0], &msgs[i].buf[1], - msgs[i].len - 1); - if (ret < 0) { - deb_i2c("i2c master_xfer failed"); - break; - } - } - - mutex_unlock(&i2c->fc->i2c_mutex); - - if (ret == 0) - ret = num; - return ret; -} - -static u32 flexcop_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm flexcop_algo = { - .master_xfer = flexcop_master_xfer, - .functionality = flexcop_i2c_func, -}; - -int flexcop_i2c_init(struct flexcop_device *fc) -{ - int ret; - mutex_init(&fc->i2c_mutex); - - fc->fc_i2c_adap[0].fc = fc; - fc->fc_i2c_adap[1].fc = fc; - fc->fc_i2c_adap[2].fc = fc; - fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD; - fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM; - fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER; - - strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod", - sizeof(fc->fc_i2c_adap[0].i2c_adap.name)); - strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom", - sizeof(fc->fc_i2c_adap[1].i2c_adap.name)); - strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner", - sizeof(fc->fc_i2c_adap[2].i2c_adap.name)); - - i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]); - i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]); - i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]); - - fc->fc_i2c_adap[0].i2c_adap.algo = - fc->fc_i2c_adap[1].i2c_adap.algo = - fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo; - fc->fc_i2c_adap[0].i2c_adap.algo_data = - fc->fc_i2c_adap[1].i2c_adap.algo_data = - fc->fc_i2c_adap[2].i2c_adap.algo_data = NULL; - fc->fc_i2c_adap[0].i2c_adap.dev.parent = - fc->fc_i2c_adap[1].i2c_adap.dev.parent = - fc->fc_i2c_adap[2].i2c_adap.dev.parent = fc->dev; - - ret = i2c_add_adapter(&fc->fc_i2c_adap[0].i2c_adap); - if (ret < 0) - return ret; - - ret = i2c_add_adapter(&fc->fc_i2c_adap[1].i2c_adap); - if (ret < 0) - goto adap_1_failed; - - ret = i2c_add_adapter(&fc->fc_i2c_adap[2].i2c_adap); - if (ret < 0) - goto adap_2_failed; - - fc->init_state |= FC_STATE_I2C_INIT; - return 0; - -adap_2_failed: - i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); -adap_1_failed: - i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); - return ret; -} - -void flexcop_i2c_exit(struct flexcop_device *fc) -{ - if (fc->init_state & FC_STATE_I2C_INIT) { - i2c_del_adapter(&fc->fc_i2c_adap[2].i2c_adap); - i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); - i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); - } - fc->init_state &= ~FC_STATE_I2C_INIT; -} diff --git a/drivers/media/pci/b2c2/flexcop-misc.c b/drivers/media/pci/b2c2/flexcop-misc.c deleted file mode 100644 index f06f3a9070f5..000000000000 --- a/drivers/media/pci/b2c2/flexcop-misc.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-misc.c - miscellaneous functions - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -void flexcop_determine_revision(struct flexcop_device *fc) -{ - flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); - - switch (v.misc_204.Rev_N_sig_revision_hi) { - case 0x2: - deb_info("found a FlexCopII.\n"); - fc->rev = FLEXCOP_II; - break; - case 0x3: - deb_info("found a FlexCopIIb.\n"); - fc->rev = FLEXCOP_IIB; - break; - case 0x0: - deb_info("found a FlexCopIII.\n"); - fc->rev = FLEXCOP_III; - break; - default: - err("unknown FlexCop Revision: %x. Please report this to " - "linux-dvb@linuxtv.org.", - v.misc_204.Rev_N_sig_revision_hi); - break; - } - - if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps)) - deb_info("this FlexCop has " - "the additional 32 hardware pid filter.\n"); - else - deb_info("this FlexCop has " - "the 6 basic main hardware pid filter.\n"); - /* bus parts have to decide if hw pid filtering is used or not. */ -} - -static const char *flexcop_revision_names[] = { - "Unknown chip", - "FlexCopII", - "FlexCopIIb", - "FlexCopIII", -}; - -static const char *flexcop_device_names[] = { - [FC_UNK] = "Unknown device", - [FC_CABLE] = "Cable2PC/CableStar 2 DVB-C", - [FC_AIR_DVBT] = "Air2PC/AirStar 2 DVB-T", - [FC_AIR_ATSC1] = "Air2PC/AirStar 2 ATSC 1st generation", - [FC_AIR_ATSC2] = "Air2PC/AirStar 2 ATSC 2nd generation", - [FC_AIR_ATSC3] = "Air2PC/AirStar 2 ATSC 3rd generation (HD5000)", - [FC_SKY_REV23] = "Sky2PC/SkyStar 2 DVB-S rev 2.3 (old version)", - [FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6", - [FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u", - [FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8", -}; - -static const char *flexcop_bus_names[] = { - "USB", - "PCI", -}; - -void flexcop_device_name(struct flexcop_device *fc, - const char *prefix, const char *suffix) -{ - info("%s '%s' at the '%s' bus controlled by a '%s' %s", - prefix, flexcop_device_names[fc->dev_type], - flexcop_bus_names[fc->bus_type], - flexcop_revision_names[fc->rev], suffix); -} - -void flexcop_dump_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, int num) -{ - flexcop_ibi_value v; - int i; - for (i = 0; i < num; i++) { - v = fc->read_ibi_reg(fc, reg+4*i); - deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw); - } - deb_rdump("\n"); -} -EXPORT_SYMBOL(flexcop_dump_reg); diff --git a/drivers/media/pci/b2c2/flexcop-reg.h b/drivers/media/pci/b2c2/flexcop-reg.h deleted file mode 100644 index dc4528dcbb98..000000000000 --- a/drivers/media/pci/b2c2/flexcop-reg.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_REG_H__ -#define __FLEXCOP_REG_H__ - -typedef enum { - FLEXCOP_UNK = 0, - FLEXCOP_II, - FLEXCOP_IIB, - FLEXCOP_III, -} flexcop_revision_t; - -typedef enum { - FC_UNK = 0, - FC_CABLE, - FC_AIR_DVBT, - FC_AIR_ATSC1, - FC_AIR_ATSC2, - FC_AIR_ATSC3, - FC_SKY_REV23, - FC_SKY_REV26, - FC_SKY_REV27, - FC_SKY_REV28, -} flexcop_device_type_t; - -typedef enum { - FC_USB = 0, - FC_PCI, -} flexcop_bus_t; - -/* FlexCop IBI Registers */ -#if defined(__LITTLE_ENDIAN) -#include "flexcop_ibi_value_le.h" -#else -#if defined(__BIG_ENDIAN) -#include "flexcop_ibi_value_be.h" -#else -#error no endian defined -#endif -#endif - -#define fc_data_Tag_ID_DVB 0x3e -#define fc_data_Tag_ID_ATSC 0x3f -#define fc_data_Tag_ID_IDSB 0x8b - -#define fc_key_code_default 0x1 -#define fc_key_code_even 0x2 -#define fc_key_code_odd 0x3 - -extern flexcop_ibi_value ibi_zero; - -typedef enum { - FC_I2C_PORT_DEMOD = 1, - FC_I2C_PORT_EEPROM = 2, - FC_I2C_PORT_TUNER = 3, -} flexcop_i2c_port_t; - -typedef enum { - FC_WRITE = 0, - FC_READ = 1, -} flexcop_access_op_t; - -typedef enum { - FC_SRAM_DEST_NET = 1, - FC_SRAM_DEST_CAI = 2, - FC_SRAM_DEST_CAO = 4, - FC_SRAM_DEST_MEDIA = 8 -} flexcop_sram_dest_t; - -typedef enum { - FC_SRAM_DEST_TARGET_WAN_USB = 0, - FC_SRAM_DEST_TARGET_DMA1 = 1, - FC_SRAM_DEST_TARGET_DMA2 = 2, - FC_SRAM_DEST_TARGET_FC3_CA = 3 -} flexcop_sram_dest_target_t; - -typedef enum { - FC_SRAM_2_32KB = 0, /* 64KB */ - FC_SRAM_1_32KB = 1, /* 32KB - default fow FCII */ - FC_SRAM_1_128KB = 2, /* 128KB */ - FC_SRAM_1_48KB = 3, /* 48KB - default for FCIII */ -} flexcop_sram_type_t; - -typedef enum { - FC_WAN_SPEED_4MBITS = 0, - FC_WAN_SPEED_8MBITS = 1, - FC_WAN_SPEED_12MBITS = 2, - FC_WAN_SPEED_16MBITS = 3, -} flexcop_wan_speed_t; - -typedef enum { - FC_DMA_1 = 1, - FC_DMA_2 = 2, -} flexcop_dma_index_t; - -typedef enum { - FC_DMA_SUBADDR_0 = 1, - FC_DMA_SUBADDR_1 = 2, -} flexcop_dma_addr_index_t; - -/* names of the particular registers */ -typedef enum { - dma1_000 = 0x000, - dma1_004 = 0x004, - dma1_008 = 0x008, - dma1_00c = 0x00c, - dma2_010 = 0x010, - dma2_014 = 0x014, - dma2_018 = 0x018, - dma2_01c = 0x01c, - - tw_sm_c_100 = 0x100, - tw_sm_c_104 = 0x104, - tw_sm_c_108 = 0x108, - tw_sm_c_10c = 0x10c, - tw_sm_c_110 = 0x110, - - lnb_switch_freq_200 = 0x200, - misc_204 = 0x204, - ctrl_208 = 0x208, - irq_20c = 0x20c, - sw_reset_210 = 0x210, - misc_214 = 0x214, - mbox_v8_to_host_218 = 0x218, - mbox_host_to_v8_21c = 0x21c, - - pid_filter_300 = 0x300, - pid_filter_304 = 0x304, - pid_filter_308 = 0x308, - pid_filter_30c = 0x30c, - index_reg_310 = 0x310, - pid_n_reg_314 = 0x314, - mac_low_reg_318 = 0x318, - mac_high_reg_31c = 0x31c, - - data_tag_400 = 0x400, - card_id_408 = 0x408, - card_id_40c = 0x40c, - mac_address_418 = 0x418, - mac_address_41c = 0x41c, - - ci_600 = 0x600, - pi_604 = 0x604, - pi_608 = 0x608, - dvb_reg_60c = 0x60c, - - sram_ctrl_reg_700 = 0x700, - net_buf_reg_704 = 0x704, - cai_buf_reg_708 = 0x708, - cao_buf_reg_70c = 0x70c, - media_buf_reg_710 = 0x710, - sram_dest_reg_714 = 0x714, - net_buf_reg_718 = 0x718, - wan_ctrl_reg_71c = 0x71c, -} flexcop_ibi_register; - -#define flexcop_set_ibi_value(reg,attr,val) { \ - flexcop_ibi_value v = fc->read_ibi_reg(fc,reg); \ - v.reg.attr = val; \ - fc->write_ibi_reg(fc,reg,v); \ -} - -#endif diff --git a/drivers/media/pci/b2c2/flexcop-sram.c b/drivers/media/pci/b2c2/flexcop-sram.c deleted file mode 100644 index f2199e43e803..000000000000 --- a/drivers/media/pci/b2c2/flexcop-sram.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-sram.c - functions for controlling the SRAM - * see flexcop.c for copyright information - */ -#include "flexcop.h" - -static void flexcop_sram_set_chip(struct flexcop_device *fc, - flexcop_sram_type_t type) -{ - flexcop_set_ibi_value(wan_ctrl_reg_71c, sram_chip, type); -} - -int flexcop_sram_init(struct flexcop_device *fc) -{ - switch (fc->rev) { - case FLEXCOP_II: - case FLEXCOP_IIB: - flexcop_sram_set_chip(fc, FC_SRAM_1_32KB); - break; - case FLEXCOP_III: - flexcop_sram_set_chip(fc, FC_SRAM_1_48KB); - break; - default: - return -EINVAL; - } - return 0; -} - -int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, - flexcop_sram_dest_target_t target) -{ - flexcop_ibi_value v; - v = fc->read_ibi_reg(fc, sram_dest_reg_714); - - if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) { - err("SRAM destination target to available on FlexCopII(b)\n"); - return -EINVAL; - } - deb_sram("sram dest: %x target: %x\n", dest, target); - - if (dest & FC_SRAM_DEST_NET) - v.sram_dest_reg_714.NET_Dest = target; - if (dest & FC_SRAM_DEST_CAI) - v.sram_dest_reg_714.CAI_Dest = target; - if (dest & FC_SRAM_DEST_CAO) - v.sram_dest_reg_714.CAO_Dest = target; - if (dest & FC_SRAM_DEST_MEDIA) - v.sram_dest_reg_714.MEDIA_Dest = target; - - fc->write_ibi_reg(fc,sram_dest_reg_714,v); - udelay(1000); /* TODO delay really necessary */ - - return 0; -} -EXPORT_SYMBOL(flexcop_sram_set_dest); - -void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s) -{ - flexcop_set_ibi_value(wan_ctrl_reg_71c,wan_speed_sig,s); -} -EXPORT_SYMBOL(flexcop_wan_set_speed); - -void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill) -{ - flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714); - v.sram_dest_reg_714.ctrl_usb_wan = usb_wan; - v.sram_dest_reg_714.ctrl_sramdma = sramdma; - v.sram_dest_reg_714.ctrl_maximumfill = maximumfill; - fc->write_ibi_reg(fc,sram_dest_reg_714,v); -} -EXPORT_SYMBOL(flexcop_sram_ctrl); - -#if 0 -static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) -{ - int i, retries; - u32 command; - - for (i = 0; i < len; i++) { - command = bank | addr | 0x04000000 | (*buf << 0x10); - - retries = 2; - - while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { - mdelay(1); - retries--; - }; - - if (retries == 0) - printk("%s: SRAM timeout\n", __func__); - - write_reg_dw(adapter, 0x700, command); - - buf++; - addr++; - } -} - -static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len) -{ - int i, retries; - u32 command, value; - - for (i = 0; i < len; i++) { - command = bank | addr | 0x04008000; - - retries = 10000; - - while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { - mdelay(1); - retries--; - }; - - if (retries == 0) - printk("%s: SRAM timeout\n", __func__); - - write_reg_dw(adapter, 0x700, command); - - retries = 10000; - - while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) { - mdelay(1); - retries--; - }; - - if (retries == 0) - printk("%s: SRAM timeout\n", __func__); - - value = read_reg_dw(adapter, 0x700) >> 0x10; - - *buf = (value & 0xff); - - addr++; - buf++; - } -} - -static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) -{ - u32 bank; - - bank = 0; - - if (adapter->dw_sram_type == 0x20000) { - bank = (addr & 0x18000) << 0x0d; - } - - if (adapter->dw_sram_type == 0x00000) { - if ((addr >> 0x0f) == 0) - bank = 0x20000000; - else - bank = 0x10000000; - } - flex_sram_write(adapter, bank, addr & 0x7fff, buf, len); -} - -static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) -{ - u32 bank; - bank = 0; - - if (adapter->dw_sram_type == 0x20000) { - bank = (addr & 0x18000) << 0x0d; - } - - if (adapter->dw_sram_type == 0x00000) { - if ((addr >> 0x0f) == 0) - bank = 0x20000000; - else - bank = 0x10000000; - } - flex_sram_read(adapter, bank, addr & 0x7fff, buf, len); -} - -static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len) -{ - u32 length; - while (len != 0) { - length = len; - /* check if the address range belongs to the same - * 32K memory chip. If not, the data is read - * from one chip at a time */ - if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { - length = (((addr >> 0x0f) + 1) << 0x0f) - addr; - } - - sram_read_chunk(adapter, addr, buf, length); - addr = addr + length; - buf = buf + length; - len = len - length; - } -} - -static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len) -{ - u32 length; - while (len != 0) { - length = len; - - /* check if the address range belongs to the same - * 32K memory chip. If not, the data is - * written to one chip at a time */ - if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) { - length = (((addr >> 0x0f) + 1) << 0x0f) - addr; - } - - sram_write_chunk(adapter, addr, buf, length); - addr = addr + length; - buf = buf + length; - len = len - length; - } -} - -static void sram_set_size(struct adapter *adapter, u32 mask) -{ - write_reg_dw(adapter, 0x71c, - (mask | (~0x30000 & read_reg_dw(adapter, 0x71c)))); -} - -static void sram_init(struct adapter *adapter) -{ - u32 tmp; - tmp = read_reg_dw(adapter, 0x71c); - write_reg_dw(adapter, 0x71c, 1); - - if (read_reg_dw(adapter, 0x71c) != 0) { - write_reg_dw(adapter, 0x71c, tmp); - adapter->dw_sram_type = tmp & 0x30000; - ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); - } else { - adapter->dw_sram_type = 0x10000; - ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type); - } -} - -static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr) -{ - u8 tmp1, tmp2; - dprintk("%s: mask = %x, addr = %x\n", __func__, mask, addr); - - sram_set_size(adapter, mask); - sram_init(adapter); - - tmp2 = 0xa5; - tmp1 = 0x4f; - - sram_write(adapter, addr, &tmp2, 1); - sram_write(adapter, addr + 4, &tmp1, 1); - - tmp2 = 0; - mdelay(20); - - sram_read(adapter, addr, &tmp2, 1); - sram_read(adapter, addr, &tmp2, 1); - - dprintk("%s: wrote 0xa5, read 0x%2x\n", __func__, tmp2); - - if (tmp2 != 0xa5) - return 0; - - tmp2 = 0x5a; - tmp1 = 0xf4; - - sram_write(adapter, addr, &tmp2, 1); - sram_write(adapter, addr + 4, &tmp1, 1); - - tmp2 = 0; - mdelay(20); - - sram_read(adapter, addr, &tmp2, 1); - sram_read(adapter, addr, &tmp2, 1); - - dprintk("%s: wrote 0x5a, read 0x%2x\n", __func__, tmp2); - - if (tmp2 != 0x5a) - return 0; - return 1; -} - -static u32 sram_length(struct adapter *adapter) -{ - if (adapter->dw_sram_type == 0x10000) - return 32768; /* 32K */ - if (adapter->dw_sram_type == 0x00000) - return 65536; /* 64K */ - if (adapter->dw_sram_type == 0x20000) - return 131072; /* 128K */ - return 32768; /* 32K */ -} - -/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory. - - for 128K there are 4x32K chips at bank 0,1,2,3. - - for 64K there are 2x32K chips at bank 1,2. - - for 32K there is one 32K chip at bank 0. - - FlexCop works only with one bank at a time. The bank is selected - by bits 28-29 of the 0x700 register. - - bank 0 covers addresses 0x00000-0x07fff - bank 1 covers addresses 0x08000-0x0ffff - bank 2 covers addresses 0x10000-0x17fff - bank 3 covers addresses 0x18000-0x1ffff */ - -static int flexcop_sram_detect(struct flexcop_device *fc) -{ - flexcop_ibi_value r208, r71c_0, vr71c_1; - r208 = fc->read_ibi_reg(fc, ctrl_208); - fc->write_ibi_reg(fc, ctrl_208, ibi_zero); - - r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c); - write_reg_dw(adapter, 0x71c, 1); - tmp3 = read_reg_dw(adapter, 0x71c); - dprintk("%s: tmp3 = %x\n", __func__, tmp3); - write_reg_dw(adapter, 0x71c, tmp2); - - // check for internal SRAM ??? - tmp3--; - if (tmp3 != 0) { - sram_set_size(adapter, 0x10000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 32K\n", __func__); - return 32; - } - - if (sram_test_location(adapter, 0x20000, 0x18000) != 0) { - sram_set_size(adapter, 0x20000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 128K\n", __func__); - return 128; - } - - if (sram_test_location(adapter, 0x00000, 0x10000) != 0) { - sram_set_size(adapter, 0x00000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 64K\n", __func__); - return 64; - } - - if (sram_test_location(adapter, 0x10000, 0x00000) != 0) { - sram_set_size(adapter, 0x10000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: sram size = 32K\n", __func__); - return 32; - } - - sram_set_size(adapter, 0x10000); - sram_init(adapter); - write_reg_dw(adapter, 0x208, tmp); - dprintk("%s: SRAM detection failed. Set to 32K \n", __func__); - return 0; -} - -static void sll_detect_sram_size(struct adapter *adapter) -{ - sram_detect_for_flex2(adapter); -} - -#endif diff --git a/drivers/media/pci/b2c2/flexcop-usb.c b/drivers/media/pci/b2c2/flexcop-usb.c deleted file mode 100644 index 8b6275f85908..000000000000 --- a/drivers/media/pci/b2c2/flexcop-usb.c +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-usb.c - covers the USB part - * see flexcop.c for copyright information - */ -#define FC_LOG_PREFIX "flexcop_usb" -#include "flexcop-usb.h" -#include "flexcop-common.h" - -/* Version information */ -#define DRIVER_VERSION "0.1" -#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver" -#define DRIVER_AUTHOR "Patrick Boettcher " - -/* debug */ -#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG -#define dprintk(level,args...) \ - do { if ((debug & level)) printk(args); } while (0) - -#define debug_dump(b, l, method) do {\ - int i; \ - for (i = 0; i < l; i++) \ - method("%02x ", b[i]); \ - method("\n"); \ -} while (0) - -#define DEBSTATUS "" -#else -#define dprintk(level, args...) -#define debug_dump(b, l, method) -#define DEBSTATUS " (debugging is not enabled)" -#endif - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2," - "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS); -#undef DEBSTATUS - -#define deb_info(args...) dprintk(0x01, args) -#define deb_ts(args...) dprintk(0x02, args) -#define deb_ctrl(args...) dprintk(0x04, args) -#define deb_i2c(args...) dprintk(0x08, args) -#define deb_v8(args...) dprintk(0x10, args) - -/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits - * in the IBI address, to make the V8 code simpler. - * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used) - * in general: 0000 0HHH 000L LL00 - * IBI ADDRESS FORMAT: RHHH BLLL - * - * where R is the read(1)/write(0) bit, B is the busy bit - * and HHH and LLL are the two sets of three bits from the PCI address. - */ -#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \ - (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70)) -#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \ - (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4)) - -/* - * DKT 020228 - * - forget about this VENDOR_BUFFER_SIZE, read and write register - * deal with DWORD or 4 bytes, that should be should from now on - * - from now on, we don't support anything older than firm 1.00 - * I eliminated the write register as a 2 trip of writing hi word and lo word - * and force this to write only 4 bytes at a time. - * NOTE: this should work with all the firmware from 1.00 and newer - */ -static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read) -{ - struct flexcop_usb *fc_usb = fc->bus_specific; - u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG; - u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR; - u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | - (read ? 0x80 : 0); - - int len = usb_control_msg(fc_usb->udev, - read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT, - request, - request_type, /* 0xc0 read or 0x40 write */ - wAddress, - 0, - val, - sizeof(u32), - B2C2_WAIT_FOR_OPERATION_RDW * HZ); - - if (len != sizeof(u32)) { - err("error while %s dword from %d (%d).", read ? "reading" : - "writing", wAddress, wRegOffsPCI); - return -EIO; - } - return 0; -} -/* - * DKT 010817 - add support for V8 memory read/write and flash update - */ -static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb, - flexcop_usb_request_t req, u8 page, u16 wAddress, - u8 *pbBuffer, u32 buflen) -{ - u8 request_type = USB_TYPE_VENDOR; - u16 wIndex; - int nWaitTime, pipe, len; - wIndex = page << 8; - - switch (req) { - case B2C2_USB_READ_V8_MEM: - nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ; - request_type |= USB_DIR_IN; - pipe = B2C2_USB_CTRL_PIPE_IN; - break; - case B2C2_USB_WRITE_V8_MEM: - wIndex |= pbBuffer[0]; - request_type |= USB_DIR_OUT; - nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE; - pipe = B2C2_USB_CTRL_PIPE_OUT; - break; - case B2C2_USB_FLASH_BLOCK: - request_type |= USB_DIR_OUT; - nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH; - pipe = B2C2_USB_CTRL_PIPE_OUT; - break; - default: - deb_info("unsupported request for v8_mem_req %x.\n", req); - return -EINVAL; - } - deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req, - wAddress, wIndex, buflen); - - len = usb_control_msg(fc_usb->udev, pipe, - req, - request_type, - wAddress, - wIndex, - pbBuffer, - buflen, - nWaitTime * HZ); - - debug_dump(pbBuffer, len, deb_v8); - return len == buflen ? 0 : -EIO; -} - -#define bytes_left_to_read_on_page(paddr,buflen) \ - ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \ - ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK))) - -static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, - flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, - u32 addr, int extended, u8 *buf, u32 len) -{ - int i,ret = 0; - u16 wMax; - u32 pagechunk = 0; - - switch(req) { - case B2C2_USB_READ_V8_MEM: - wMax = USB_MEM_READ_MAX; - break; - case B2C2_USB_WRITE_V8_MEM: - wMax = USB_MEM_WRITE_MAX; - break; - case B2C2_USB_FLASH_BLOCK: - wMax = USB_FLASH_MAX; - break; - default: - return -EINVAL; - break; - } - for (i = 0; i < len;) { - pagechunk = - wMax < bytes_left_to_read_on_page(addr, len) ? - wMax : - bytes_left_to_read_on_page(addr, len); - deb_info("%x\n", - (addr & V8_MEMORY_PAGE_MASK) | - (V8_MEMORY_EXTENDED*extended)); - - ret = flexcop_usb_v8_memory_req(fc_usb, req, - page_start + (addr / V8_MEMORY_PAGE_SIZE), - (addr & V8_MEMORY_PAGE_MASK) | - (V8_MEMORY_EXTENDED*extended), - &buf[i], pagechunk); - - if (ret < 0) - return ret; - addr += pagechunk; - len -= pagechunk; - } - return 0; -} - -static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended) -{ - return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM, - V8_MEMORY_PAGE_FLASH, 0x1f010, 1, - fc->dvb_adapter.proposed_mac, 6); -} - -#if 0 -static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set, - flexcop_usb_utility_function_t func, u8 extra, u16 wIndex, - u16 buflen, u8 *pvBuffer) -{ - u16 wValue; - u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR; - int nWaitTime = 2, - pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len; - wValue = (func << 8) | extra; - - len = usb_control_msg(fc_usb->udev,pipe, - B2C2_USB_UTILITY, - request_type, - wValue, - wIndex, - pvBuffer, - buflen, - nWaitTime * HZ); - return len == buflen ? 0 : -EIO; -} -#endif - -/* usb i2c stuff */ -static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c, - flexcop_usb_request_t req, flexcop_usb_i2c_function_t func, - u8 chipaddr, u8 addr, u8 *buf, u8 buflen) -{ - struct flexcop_usb *fc_usb = i2c->fc->bus_specific; - u16 wValue, wIndex; - int nWaitTime,pipe,len; - u8 request_type = USB_TYPE_VENDOR; - - switch (func) { - case USB_FUNC_I2C_WRITE: - case USB_FUNC_I2C_MULTIWRITE: - case USB_FUNC_I2C_REPEATWRITE: - /* DKT 020208 - add this to support special case of DiSEqC */ - case USB_FUNC_I2C_CHECKWRITE: - pipe = B2C2_USB_CTRL_PIPE_OUT; - nWaitTime = 2; - request_type |= USB_DIR_OUT; - break; - case USB_FUNC_I2C_READ: - case USB_FUNC_I2C_REPEATREAD: - pipe = B2C2_USB_CTRL_PIPE_IN; - nWaitTime = 2; - request_type |= USB_DIR_IN; - break; - default: - deb_info("unsupported function for i2c_req %x\n", func); - return -EINVAL; - } - wValue = (func << 8) | (i2c->port << 4); - wIndex = (chipaddr << 8 ) | addr; - - deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n", - func, request_type, req, - wValue & 0xff, wValue >> 8, - wIndex & 0xff, wIndex >> 8); - - len = usb_control_msg(fc_usb->udev,pipe, - req, - request_type, - wValue, - wIndex, - buf, - buflen, - nWaitTime * HZ); - return len == buflen ? 0 : -EREMOTEIO; -} - -/* actual bus specific access functions, - make sure prototype are/will be equal to pci */ -static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, - flexcop_ibi_register reg) -{ - flexcop_ibi_value val; - val.raw = 0; - flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1); - return val; -} - -static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, flexcop_ibi_value val) -{ - return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0); -} - -static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c, - flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) -{ - if (op == FC_READ) - return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, - USB_FUNC_I2C_READ, chipaddr, addr, buf, len); - else - return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, - USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len); -} - -static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, - u8 *buffer, int buffer_length) -{ - u8 *b; - int l; - - deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", - fc_usb->tmp_buffer_length, buffer_length); - - if (fc_usb->tmp_buffer_length > 0) { - memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, - buffer_length); - fc_usb->tmp_buffer_length += buffer_length; - b = fc_usb->tmp_buffer; - l = fc_usb->tmp_buffer_length; - } else { - b=buffer; - l=buffer_length; - } - - while (l >= 190) { - if (*b == 0xff) { - switch (*(b+1) & 0x03) { - case 0x01: /* media packet */ - if (*(b+2) == 0x47) - flexcop_pass_dmx_packets( - fc_usb->fc_dev, b+2, 1); - else - deb_ts("not ts packet %*ph\n", 4, b+2); - b += 190; - l -= 190; - break; - default: - deb_ts("wrong packet type\n"); - l = 0; - break; - } - } else { - deb_ts("wrong header\n"); - l = 0; - } - } - - if (l>0) - memcpy(fc_usb->tmp_buffer, b, l); - fc_usb->tmp_buffer_length = l; -} - -static void flexcop_usb_urb_complete(struct urb *urb) -{ - struct flexcop_usb *fc_usb = urb->context; - int i; - - if (urb->actual_length > 0) - deb_ts("urb completed, bufsize: %d actlen; %d\n", - urb->transfer_buffer_length, urb->actual_length); - - for (i = 0; i < urb->number_of_packets; i++) { - if (urb->iso_frame_desc[i].status < 0) { - err("iso frame descriptor %d has an error: %d\n", i, - urb->iso_frame_desc[i].status); - } else - if (urb->iso_frame_desc[i].actual_length > 0) { - deb_ts("passed %d bytes to the demux\n", - urb->iso_frame_desc[i].actual_length); - - flexcop_usb_process_frame(fc_usb, - urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); - } - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - usb_submit_urb(urb,GFP_ATOMIC); -} - -static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff) -{ - /* submit/kill iso packets */ - return 0; -} - -static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) -{ - int i; - for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) - if (fc_usb->iso_urb[i] != NULL) { - deb_ts("unlinking/killing urb no. %d\n",i); - usb_kill_urb(fc_usb->iso_urb[i]); - usb_free_urb(fc_usb->iso_urb[i]); - } - - if (fc_usb->iso_buffer != NULL) - pci_free_consistent(NULL, - fc_usb->buffer_size, fc_usb->iso_buffer, - fc_usb->dma_addr); -} - -static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) -{ - u16 frame_size = le16_to_cpu( - fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize); - int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * - frame_size, i, j, ret; - int buffer_offset = 0; - - deb_ts("creating %d iso-urbs with %d frames " - "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB, - B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize); - - fc_usb->iso_buffer = pci_alloc_consistent(NULL, - bufsize, &fc_usb->dma_addr); - if (fc_usb->iso_buffer == NULL) - return -ENOMEM; - - memset(fc_usb->iso_buffer, 0, bufsize); - fc_usb->buffer_size = bufsize; - - /* creating iso urbs */ - for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { - fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO, - GFP_ATOMIC); - if (fc_usb->iso_urb[i] == NULL) { - ret = -ENOMEM; - goto urb_error; - } - } - - /* initialising and submitting iso urbs */ - for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { - int frame_offset = 0; - struct urb *urb = fc_usb->iso_urb[i]; - deb_ts("initializing and submitting urb no. %d " - "(buf_offset: %d).\n", i, buffer_offset); - - urb->dev = fc_usb->udev; - urb->context = fc_usb; - urb->complete = flexcop_usb_urb_complete; - urb->pipe = B2C2_USB_DATA_PIPE; - urb->transfer_flags = URB_ISO_ASAP; - urb->interval = 1; - urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO; - urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO; - urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset; - - buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO; - for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) { - deb_ts("urb no: %d, frame: %d, frame_offset: %d\n", - i, j, frame_offset); - urb->iso_frame_desc[j].offset = frame_offset; - urb->iso_frame_desc[j].length = frame_size; - frame_offset += frame_size; - } - - if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) { - err("submitting urb %d failed with %d.", i, ret); - goto urb_error; - } - deb_ts("submitted urb no. %d.\n",i); - } - - /* SRAM */ - flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA | - FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, - FC_SRAM_DEST_TARGET_WAN_USB); - flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS); - flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1); - return 0; - -urb_error: - flexcop_usb_transfer_exit(fc_usb); - return ret; -} - -static int flexcop_usb_init(struct flexcop_usb *fc_usb) -{ - /* use the alternate setting with the larges buffer */ - usb_set_interface(fc_usb->udev,0,1); - switch (fc_usb->udev->speed) { - case USB_SPEED_LOW: - err("cannot handle USB speed because it is too slow."); - return -ENODEV; - break; - case USB_SPEED_FULL: - info("running at FULL speed."); - break; - case USB_SPEED_HIGH: - info("running at HIGH speed."); - break; - case USB_SPEED_UNKNOWN: /* fall through */ - default: - err("cannot handle USB speed because it is unknown."); - return -ENODEV; - } - usb_set_intfdata(fc_usb->uintf, fc_usb); - return 0; -} - -static void flexcop_usb_exit(struct flexcop_usb *fc_usb) -{ - usb_set_intfdata(fc_usb->uintf, NULL); -} - -static int flexcop_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct flexcop_usb *fc_usb = NULL; - struct flexcop_device *fc = NULL; - int ret; - - if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { - err("out of memory\n"); - return -ENOMEM; - } - - /* general flexcop init */ - fc_usb = fc->bus_specific; - fc_usb->fc_dev = fc; - - fc->read_ibi_reg = flexcop_usb_read_ibi_reg; - fc->write_ibi_reg = flexcop_usb_write_ibi_reg; - fc->i2c_request = flexcop_usb_i2c_request; - fc->get_mac_addr = flexcop_usb_get_mac_addr; - - fc->stream_control = flexcop_usb_stream_control; - - fc->pid_filtering = 1; - fc->bus_type = FC_USB; - - fc->dev = &udev->dev; - fc->owner = THIS_MODULE; - - /* bus specific part */ - fc_usb->udev = udev; - fc_usb->uintf = intf; - if ((ret = flexcop_usb_init(fc_usb)) != 0) - goto err_kfree; - - /* init flexcop */ - if ((ret = flexcop_device_initialize(fc)) != 0) - goto err_usb_exit; - - /* xfer init */ - if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0) - goto err_fc_exit; - - info("%s successfully initialized and connected.", DRIVER_NAME); - return 0; - -err_fc_exit: - flexcop_device_exit(fc); -err_usb_exit: - flexcop_usb_exit(fc_usb); -err_kfree: - flexcop_device_kfree(fc); - return ret; -} - -static void flexcop_usb_disconnect(struct usb_interface *intf) -{ - struct flexcop_usb *fc_usb = usb_get_intfdata(intf); - flexcop_usb_transfer_exit(fc_usb); - flexcop_device_exit(fc_usb->fc_dev); - flexcop_usb_exit(fc_usb); - flexcop_device_kfree(fc_usb->fc_dev); - info("%s successfully deinitialized and disconnected.", DRIVER_NAME); -} - -static struct usb_device_id flexcop_usb_table [] = { - { USB_DEVICE(0x0af7, 0x0101) }, - { } -}; -MODULE_DEVICE_TABLE (usb, flexcop_usb_table); - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver flexcop_usb_driver = { - .name = "b2c2_flexcop_usb", - .probe = flexcop_usb_probe, - .disconnect = flexcop_usb_disconnect, - .id_table = flexcop_usb_table, -}; - -module_usb_driver(flexcop_usb_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_NAME); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/b2c2/flexcop-usb.h b/drivers/media/pci/b2c2/flexcop-usb.h deleted file mode 100644 index 92529a9c4475..000000000000 --- a/drivers/media/pci/b2c2/flexcop-usb.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop-usb.h - header file for the USB part - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_USB_H_INCLUDED__ -#define __FLEXCOP_USB_H_INCLUDED__ - -#include - -/* transfer parameters */ -#define B2C2_USB_FRAMES_PER_ISO 4 -#define B2C2_USB_NUM_ISO_URB 4 - -#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0) -#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0) -#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81) - -struct flexcop_usb { - struct usb_device *udev; - struct usb_interface *uintf; - - u8 *iso_buffer; - int buffer_size; - dma_addr_t dma_addr; - - struct urb *iso_urb[B2C2_USB_NUM_ISO_URB]; - struct flexcop_device *fc_dev; - - u8 tmp_buffer[1023+190]; - int tmp_buffer_length; -}; - -#if 0 -/* request types TODO What is its use?*/ -typedef enum { - -} flexcop_usb_request_type_t; -#endif - -/* request */ -typedef enum { - B2C2_USB_WRITE_V8_MEM = 0x04, - B2C2_USB_READ_V8_MEM = 0x05, - B2C2_USB_READ_REG = 0x08, - B2C2_USB_WRITE_REG = 0x0A, - B2C2_USB_WRITEREGHI = 0x0B, - B2C2_USB_FLASH_BLOCK = 0x10, - B2C2_USB_I2C_REQUEST = 0x11, - B2C2_USB_UTILITY = 0x12, -} flexcop_usb_request_t; - -/* function definition for I2C_REQUEST */ -typedef enum { - USB_FUNC_I2C_WRITE = 0x01, - USB_FUNC_I2C_MULTIWRITE = 0x02, - USB_FUNC_I2C_READ = 0x03, - USB_FUNC_I2C_REPEATWRITE = 0x04, - USB_FUNC_GET_DESCRIPTOR = 0x05, - USB_FUNC_I2C_REPEATREAD = 0x06, - /* DKT 020208 - add this to support special case of DiSEqC */ - USB_FUNC_I2C_CHECKWRITE = 0x07, - USB_FUNC_I2C_CHECKRESULT = 0x08, -} flexcop_usb_i2c_function_t; - -/* function definition for UTILITY request 0x12 - * DKT 020304 - new utility function */ -typedef enum { - UTILITY_SET_FILTER = 0x01, - UTILITY_DATA_ENABLE = 0x02, - UTILITY_FLEX_MULTIWRITE = 0x03, - UTILITY_SET_BUFFER_SIZE = 0x04, - UTILITY_FLEX_OPERATOR = 0x05, - UTILITY_FLEX_RESET300_START = 0x06, - UTILITY_FLEX_RESET300_STOP = 0x07, - UTILITY_FLEX_RESET300 = 0x08, - UTILITY_SET_ISO_SIZE = 0x09, - UTILITY_DATA_RESET = 0x0A, - UTILITY_GET_DATA_STATUS = 0x10, - UTILITY_GET_V8_REG = 0x11, - /* DKT 020326 - add function for v1.14 */ - UTILITY_SRAM_WRITE = 0x12, - UTILITY_SRAM_READ = 0x13, - UTILITY_SRAM_TESTFILL = 0x14, - UTILITY_SRAM_TESTSET = 0x15, - UTILITY_SRAM_TESTVERIFY = 0x16, -} flexcop_usb_utility_function_t; - -#define B2C2_WAIT_FOR_OPERATION_RW (1*HZ) -#define B2C2_WAIT_FOR_OPERATION_RDW (3*HZ) -#define B2C2_WAIT_FOR_OPERATION_WDW (1*HZ) - -#define B2C2_WAIT_FOR_OPERATION_V8READ (3*HZ) -#define B2C2_WAIT_FOR_OPERATION_V8WRITE (3*HZ) -#define B2C2_WAIT_FOR_OPERATION_V8FLASH (3*HZ) - -typedef enum { - V8_MEMORY_PAGE_DVB_CI = 0x20, - V8_MEMORY_PAGE_DVB_DS = 0x40, - V8_MEMORY_PAGE_MULTI2 = 0x60, - V8_MEMORY_PAGE_FLASH = 0x80 -} flexcop_usb_mem_page_t; - -#define V8_MEMORY_EXTENDED (1 << 15) -#define USB_MEM_READ_MAX 32 -#define USB_MEM_WRITE_MAX 1 -#define USB_FLASH_MAX 8 -#define V8_MEMORY_PAGE_SIZE 0x8000 /* 32K */ -#define V8_MEMORY_PAGE_MASK 0x7FFF - -#endif diff --git a/drivers/media/pci/b2c2/flexcop.c b/drivers/media/pci/b2c2/flexcop.c deleted file mode 100644 index b1e8c99f469b..000000000000 --- a/drivers/media/pci/b2c2/flexcop.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop.c - main module part - * Copyright (C) 2004-9 Patrick Boettcher - * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc - * - * Acknowledgements: - * John Jurrius from BBTI, Inc. for extensive support - * with code examples and data books - * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting) - * - * Contributions to the skystar2-driver have been done by - * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes) - * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code) - * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu) - * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac - * filtering) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "flexcop.h" - -#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip" -#define DRIVER_AUTHOR "Patrick Boettcher demux->priv; - return flexcop_pid_feed_control(fc, dvbdmxfeed, 1); -} - -static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct flexcop_device *fc = dvbdmxfeed->demux->priv; - return flexcop_pid_feed_control(fc, dvbdmxfeed, 0); -} - -static int flexcop_dvb_init(struct flexcop_device *fc) -{ - int ret = dvb_register_adapter(&fc->dvb_adapter, - "FlexCop Digital TV device", fc->owner, - fc->dev, adapter_nr); - if (ret < 0) { - err("error registering DVB adapter"); - return ret; - } - fc->dvb_adapter.priv = fc; - - fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING - | DMX_MEMORY_BASED_FILTERING); - fc->demux.priv = fc; - fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED; - fc->demux.start_feed = flexcop_dvb_start_feed; - fc->demux.stop_feed = flexcop_dvb_stop_feed; - fc->demux.write_to_decoder = NULL; - - ret = dvb_dmx_init(&fc->demux); - if (ret < 0) { - err("dvb_dmx failed: error %d", ret); - goto err_dmx; - } - - fc->hw_frontend.source = DMX_FRONTEND_0; - - fc->dmxdev.filternum = fc->demux.feednum; - fc->dmxdev.demux = &fc->demux.dmx; - fc->dmxdev.capabilities = 0; - ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter); - if (ret < 0) { - err("dvb_dmxdev_init failed: error %d", ret); - goto err_dmx_dev; - } - - ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend); - if (ret < 0) { - err("adding hw_frontend to dmx failed: error %d", ret); - goto err_dmx_add_hw_frontend; - } - - fc->mem_frontend.source = DMX_MEMORY_FE; - ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend); - if (ret < 0) { - err("adding mem_frontend to dmx failed: error %d", ret); - goto err_dmx_add_mem_frontend; - } - - ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend); - if (ret < 0) { - err("connect frontend failed: error %d", ret); - goto err_connect_frontend; - } - - ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx); - if (ret < 0) { - err("dvb_net_init failed: error %d", ret); - goto err_net; - } - - fc->init_state |= FC_STATE_DVB_INIT; - return 0; - -err_net: - fc->demux.dmx.disconnect_frontend(&fc->demux.dmx); -err_connect_frontend: - fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend); -err_dmx_add_mem_frontend: - fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend); -err_dmx_add_hw_frontend: - dvb_dmxdev_release(&fc->dmxdev); -err_dmx_dev: - dvb_dmx_release(&fc->demux); -err_dmx: - dvb_unregister_adapter(&fc->dvb_adapter); - return ret; -} - -static void flexcop_dvb_exit(struct flexcop_device *fc) -{ - if (fc->init_state & FC_STATE_DVB_INIT) { - dvb_net_release(&fc->dvbnet); - - fc->demux.dmx.close(&fc->demux.dmx); - fc->demux.dmx.remove_frontend(&fc->demux.dmx, - &fc->mem_frontend); - fc->demux.dmx.remove_frontend(&fc->demux.dmx, - &fc->hw_frontend); - dvb_dmxdev_release(&fc->dmxdev); - dvb_dmx_release(&fc->demux); - dvb_unregister_adapter(&fc->dvb_adapter); - deb_info("deinitialized dvb stuff\n"); - } - fc->init_state &= ~FC_STATE_DVB_INIT; -} - -/* these methods are necessary to achieve the long-term-goal of hiding the - * struct flexcop_device from the bus-parts */ -void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len) -{ - dvb_dmx_swfilter(&fc->demux, buf, len); -} -EXPORT_SYMBOL(flexcop_pass_dmx_data); - -void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no) -{ - dvb_dmx_swfilter_packets(&fc->demux, buf, no); -} -EXPORT_SYMBOL(flexcop_pass_dmx_packets); - -static void flexcop_reset(struct flexcop_device *fc) -{ - flexcop_ibi_value v210, v204; - - /* reset the flexcop itself */ - fc->write_ibi_reg(fc,ctrl_208,ibi_zero); - - v210.raw = 0; - v210.sw_reset_210.reset_block_000 = 1; - v210.sw_reset_210.reset_block_100 = 1; - v210.sw_reset_210.reset_block_200 = 1; - v210.sw_reset_210.reset_block_300 = 1; - v210.sw_reset_210.reset_block_400 = 1; - v210.sw_reset_210.reset_block_500 = 1; - v210.sw_reset_210.reset_block_600 = 1; - v210.sw_reset_210.reset_block_700 = 1; - v210.sw_reset_210.Block_reset_enable = 0xb2; - v210.sw_reset_210.Special_controls = 0xc259; - fc->write_ibi_reg(fc,sw_reset_210,v210); - msleep(1); - - /* reset the periphical devices */ - - v204 = fc->read_ibi_reg(fc,misc_204); - v204.misc_204.Per_reset_sig = 0; - fc->write_ibi_reg(fc,misc_204,v204); - msleep(1); - v204.misc_204.Per_reset_sig = 1; - fc->write_ibi_reg(fc,misc_204,v204); -} - -void flexcop_reset_block_300(struct flexcop_device *fc) -{ - flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208), - v210 = fc->read_ibi_reg(fc, sw_reset_210); - - deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw); - fc->write_ibi_reg(fc,ctrl_208,ibi_zero); - - v210.sw_reset_210.reset_block_300 = 1; - v210.sw_reset_210.Block_reset_enable = 0xb2; - - fc->write_ibi_reg(fc,sw_reset_210,v210); - fc->write_ibi_reg(fc,ctrl_208,v208_save); -} - -struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len) -{ - void *bus; - struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device), - GFP_KERNEL); - if (!fc) { - err("no memory"); - return NULL; - } - - bus = kzalloc(bus_specific_len, GFP_KERNEL); - if (!bus) { - err("no memory"); - kfree(fc); - return NULL; - } - - fc->bus_specific = bus; - - return fc; -} -EXPORT_SYMBOL(flexcop_device_kmalloc); - -void flexcop_device_kfree(struct flexcop_device *fc) -{ - kfree(fc->bus_specific); - kfree(fc); -} -EXPORT_SYMBOL(flexcop_device_kfree); - -int flexcop_device_initialize(struct flexcop_device *fc) -{ - int ret; - ibi_zero.raw = 0; - - flexcop_reset(fc); - flexcop_determine_revision(fc); - flexcop_sram_init(fc); - flexcop_hw_filter_init(fc); - flexcop_smc_ctrl(fc, 0); - - ret = flexcop_dvb_init(fc); - if (ret) - goto error; - - /* i2c has to be done before doing EEProm stuff - - * because the EEProm is accessed via i2c */ - ret = flexcop_i2c_init(fc); - if (ret) - goto error; - - /* do the MAC address reading after initializing the dvb_adapter */ - if (fc->get_mac_addr(fc, 0) == 0) { - u8 *b = fc->dvb_adapter.proposed_mac; - info("MAC address = %pM", b); - flexcop_set_mac_filter(fc,b); - flexcop_mac_filter_ctrl(fc,1); - } else - warn("reading of MAC address failed.\n"); - - ret = flexcop_frontend_init(fc); - if (ret) - goto error; - - flexcop_device_name(fc,"initialization of","complete"); - return 0; - -error: - flexcop_device_exit(fc); - return ret; -} -EXPORT_SYMBOL(flexcop_device_initialize); - -void flexcop_device_exit(struct flexcop_device *fc) -{ - flexcop_frontend_exit(fc); - flexcop_i2c_exit(fc); - flexcop_dvb_exit(fc); -} -EXPORT_SYMBOL(flexcop_device_exit); - -static int flexcop_module_init(void) -{ - info(DRIVER_NAME " loaded successfully"); - return 0; -} - -static void flexcop_module_cleanup(void) -{ - info(DRIVER_NAME " unloaded successfully"); -} - -module_init(flexcop_module_init); -module_exit(flexcop_module_cleanup); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_NAME); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/b2c2/flexcop.h b/drivers/media/pci/b2c2/flexcop.h deleted file mode 100644 index 897b10c85ad9..000000000000 --- a/drivers/media/pci/b2c2/flexcop.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * flexcop.h - private header file for all flexcop-chip-source files - * see flexcop.c for copyright information - */ -#ifndef __FLEXCOP_H__ -#define __FLEXCOP_H___ - -#define FC_LOG_PREFIX "b2c2-flexcop" -#include "flexcop-common.h" - -extern int b2c2_flexcop_debug; - -/* debug */ -#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG -#define dprintk(level,args...) \ - do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0) -#else -#define dprintk(level,args...) -#endif - -#define deb_info(args...) dprintk(0x01, args) -#define deb_tuner(args...) dprintk(0x02, args) -#define deb_i2c(args...) dprintk(0x04, args) -#define deb_ts(args...) dprintk(0x08, args) -#define deb_sram(args...) dprintk(0x10, args) -#define deb_rdump(args...) dprintk(0x20, args) - -#endif diff --git a/drivers/media/pci/b2c2/flexcop_ibi_value_be.h b/drivers/media/pci/b2c2/flexcop_ibi_value_be.h deleted file mode 100644 index 8f64bdbd72bb..000000000000 --- a/drivers/media/pci/b2c2/flexcop_ibi_value_be.h +++ /dev/null @@ -1,455 +0,0 @@ -/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * register descriptions - * see flexcop.c for copyright information - */ -/* This file is automatically generated, do not edit things here. */ -#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ -#define __FLEXCOP_IBI_VALUE_INCLUDED__ - -typedef union { - u32 raw; - - struct { - u32 dma_address0 :30; - u32 dma_0No_update : 1; - u32 dma_0start : 1; - } dma_0x0; - - struct { - u32 dma_addr_size :24; - u32 DMA_maxpackets : 8; - } dma_0x4_remap; - - struct { - u32 dma_addr_size :24; - u32 unused : 1; - u32 dma1timer : 7; - } dma_0x4_read; - - struct { - u32 dma_addr_size :24; - u32 dmatimer : 7; - u32 unused : 1; - } dma_0x4_write; - - struct { - u32 dma_cur_addr :30; - u32 unused : 2; - } dma_0x8; - - struct { - u32 dma_address1 :30; - u32 remap_enable : 1; - u32 dma_1start : 1; - } dma_0xc; - - struct { - u32 st_done : 1; - u32 no_base_addr_ack_error : 1; - u32 twoWS_port_reg : 2; - u32 total_bytes : 2; - u32 twoWS_rw : 1; - u32 working_start : 1; - u32 data1_reg : 8; - u32 baseaddr : 8; - u32 reserved1 : 1; - u32 chipaddr : 7; - } tw_sm_c_100; - - struct { - u32 unused : 6; - u32 force_stop : 1; - u32 exlicit_stops : 1; - u32 data4_reg : 8; - u32 data3_reg : 8; - u32 data2_reg : 8; - } tw_sm_c_104; - - struct { - u32 reserved2 :19; - u32 tlo1 : 5; - u32 reserved1 : 2; - u32 thi1 : 6; - } tw_sm_c_108; - - struct { - u32 reserved2 :19; - u32 tlo1 : 5; - u32 reserved1 : 2; - u32 thi1 : 6; - } tw_sm_c_10c; - - struct { - u32 reserved2 :19; - u32 tlo1 : 5; - u32 reserved1 : 2; - u32 thi1 : 6; - } tw_sm_c_110; - - struct { - u32 LNB_CTLPrescaler_sig : 2; - u32 LNB_CTLLowCount_sig :15; - u32 LNB_CTLHighCount_sig :15; - } lnb_switch_freq_200; - - struct { - u32 Rev_N_sig_reserved2 : 1; - u32 Rev_N_sig_caps : 1; - u32 Rev_N_sig_reserved1 : 2; - u32 Rev_N_sig_revision_hi : 4; - u32 reserved :20; - u32 Per_reset_sig : 1; - u32 LNB_L_H_sig : 1; - u32 ACPI3_sig : 1; - u32 ACPI1_sig : 1; - } misc_204; - - struct { - u32 unused : 9; - u32 Mailbox_from_V8_Enable_sig : 1; - u32 DMA2_Size_IRQ_Enable_sig : 1; - u32 DMA1_Size_IRQ_Enable_sig : 1; - u32 DMA2_Timer_Enable_sig : 1; - u32 DMA2_IRQ_Enable_sig : 1; - u32 DMA1_Timer_Enable_sig : 1; - u32 DMA1_IRQ_Enable_sig : 1; - u32 Rcv_Data_sig : 1; - u32 MAC_filter_Mode_sig : 1; - u32 Multi2_Enable_sig : 1; - u32 Per_CA_Enable_sig : 1; - u32 SMC_Enable_sig : 1; - u32 CA_Enable_sig : 1; - u32 WAN_CA_Enable_sig : 1; - u32 WAN_Enable_sig : 1; - u32 Mask_filter_sig : 1; - u32 Null_filter_sig : 1; - u32 ECM_filter_sig : 1; - u32 EMM_filter_sig : 1; - u32 PMT_filter_sig : 1; - u32 PCR_filter_sig : 1; - u32 Stream2_filter_sig : 1; - u32 Stream1_filter_sig : 1; - } ctrl_208; - - struct { - u32 reserved :21; - u32 Transport_Error : 1; - u32 LLC_SNAP_FLAG_set : 1; - u32 Continuity_error_flag : 1; - u32 Data_receiver_error : 1; - u32 Mailbox_from_V8_Status_sig : 1; - u32 DMA2_Size_IRQ_Status : 1; - u32 DMA1_Size_IRQ_Status : 1; - u32 DMA2_Timer_Status : 1; - u32 DMA2_IRQ_Status : 1; - u32 DMA1_Timer_Status : 1; - u32 DMA1_IRQ_Status : 1; - } irq_20c; - - struct { - u32 Special_controls :16; - u32 Block_reset_enable : 8; - u32 reset_block_700 : 1; - u32 reset_block_600 : 1; - u32 reset_block_500 : 1; - u32 reset_block_400 : 1; - u32 reset_block_300 : 1; - u32 reset_block_200 : 1; - u32 reset_block_100 : 1; - u32 reset_block_000 : 1; - } sw_reset_210; - - struct { - u32 unused2 :20; - u32 polarity_PS_ERR_sig : 1; - u32 polarity_PS_SYNC_sig : 1; - u32 polarity_PS_VALID_sig : 1; - u32 polarity_PS_CLK_sig : 1; - u32 unused1 : 3; - u32 s2p_sel_sig : 1; - u32 section_pkg_enable_sig : 1; - u32 halt_V8_sig : 1; - u32 v2WS_oe_sig : 1; - u32 vuart_oe_sig : 1; - } misc_214; - - struct { - u32 Mailbox_from_V8 :32; - } mbox_v8_to_host_218; - - struct { - u32 sysramaccess_busmuster : 1; - u32 sysramaccess_write : 1; - u32 unused : 7; - u32 sysramaccess_addr :15; - u32 sysramaccess_data : 8; - } mbox_host_to_v8_21c; - - struct { - u32 debug_fifo_problem : 1; - u32 debug_flag_write_status00 : 1; - u32 Stream2_trans : 1; - u32 Stream2_PID :13; - u32 debug_flag_pid_saved : 1; - u32 MAC_Multicast_filter : 1; - u32 Stream1_trans : 1; - u32 Stream1_PID :13; - } pid_filter_300; - - struct { - u32 reserved : 2; - u32 PMT_trans : 1; - u32 PMT_PID :13; - u32 debug_overrun2 : 1; - u32 debug_overrun3 : 1; - u32 PCR_trans : 1; - u32 PCR_PID :13; - } pid_filter_304; - - struct { - u32 reserved : 2; - u32 ECM_trans : 1; - u32 ECM_PID :13; - u32 EMM_filter_6 : 1; - u32 EMM_filter_4 : 1; - u32 EMM_trans : 1; - u32 EMM_PID :13; - } pid_filter_308; - - struct { - u32 unused2 : 3; - u32 Group_mask :13; - u32 unused1 : 2; - u32 Group_trans : 1; - u32 Group_PID :13; - } pid_filter_30c_ext_ind_0_7; - - struct { - u32 unused :15; - u32 net_master_read :17; - } pid_filter_30c_ext_ind_1; - - struct { - u32 unused :15; - u32 net_master_write :17; - } pid_filter_30c_ext_ind_2; - - struct { - u32 unused :15; - u32 next_net_master_write :17; - } pid_filter_30c_ext_ind_3; - - struct { - u32 reserved2 : 5; - u32 stack_read :10; - u32 reserved1 : 6; - u32 state_write :10; - u32 unused1 : 1; - } pid_filter_30c_ext_ind_4; - - struct { - u32 unused :22; - u32 stack_cnt :10; - } pid_filter_30c_ext_ind_5; - - struct { - u32 unused : 4; - u32 data_size_reg :12; - u32 write_status4 : 2; - u32 write_status1 : 2; - u32 pid_fsm_save_reg300 : 2; - u32 pid_fsm_save_reg4 : 2; - u32 pid_fsm_save_reg3 : 2; - u32 pid_fsm_save_reg2 : 2; - u32 pid_fsm_save_reg1 : 2; - u32 pid_fsm_save_reg0 : 2; - } pid_filter_30c_ext_ind_6; - - struct { - u32 unused :22; - u32 pass_alltables : 1; - u32 AB_select : 1; - u32 extra_index_reg : 3; - u32 index_reg : 5; - } index_reg_310; - - struct { - u32 reserved :17; - u32 PID_enable_bit : 1; - u32 PID_trans : 1; - u32 PID :13; - } pid_n_reg_314; - - struct { - u32 reserved : 6; - u32 HighAB_bit : 1; - u32 Enable_bit : 1; - u32 A6_byte : 8; - u32 A5_byte : 8; - u32 A4_byte : 8; - } mac_low_reg_318; - - struct { - u32 reserved : 8; - u32 A3_byte : 8; - u32 A2_byte : 8; - u32 A1_byte : 8; - } mac_high_reg_31c; - - struct { - u32 data_Tag_ID :16; - u32 reserved :16; - } data_tag_400; - - struct { - u32 Card_IDbyte3 : 8; - u32 Card_IDbyte4 : 8; - u32 Card_IDbyte5 : 8; - u32 Card_IDbyte6 : 8; - } card_id_408; - - struct { - u32 Card_IDbyte1 : 8; - u32 Card_IDbyte2 : 8; - } card_id_40c; - - struct { - u32 MAC6 : 8; - u32 MAC3 : 8; - u32 MAC2 : 8; - u32 MAC1 : 8; - } mac_address_418; - - struct { - u32 reserved :16; - u32 MAC8 : 8; - u32 MAC7 : 8; - } mac_address_41c; - - struct { - u32 reserved :21; - u32 txbuffempty : 1; - u32 ReceiveByteFrameError : 1; - u32 ReceiveDataReady : 1; - u32 transmitter_data_byte : 8; - } ci_600; - - struct { - u32 pi_component_reg : 3; - u32 pi_rw : 1; - u32 pi_ha :20; - u32 pi_d : 8; - } pi_604; - - struct { - u32 pi_busy_n : 1; - u32 pi_wait_n : 1; - u32 pi_timeout_status : 1; - u32 pi_CiMax_IRQ_n : 1; - u32 config_cclk : 1; - u32 config_cs_n : 1; - u32 config_wr_n : 1; - u32 config_Prog_n : 1; - u32 config_Init_stat : 1; - u32 config_Done_stat : 1; - u32 pcmcia_b_mod_pwr_n : 1; - u32 pcmcia_a_mod_pwr_n : 1; - u32 reserved : 3; - u32 Timer_addr : 5; - u32 unused : 1; - u32 timer_data : 7; - u32 Timer_Load_req : 1; - u32 Timer_Read_req : 1; - u32 oncecycle_read : 1; - u32 serialReset : 1; - } pi_608; - - struct { - u32 reserved : 6; - u32 rw_flag : 1; - u32 dvb_en : 1; - u32 key_array_row : 5; - u32 key_array_col : 3; - u32 key_code : 2; - u32 key_enable : 1; - u32 PID :13; - } dvb_reg_60c; - - struct { - u32 start_sram_ibi : 1; - u32 reserved2 : 1; - u32 ce_pin_reg : 1; - u32 oe_pin_reg : 1; - u32 reserved1 : 3; - u32 sc_xfer_bit : 1; - u32 sram_data : 8; - u32 sram_rw : 1; - u32 sram_addr :15; - } sram_ctrl_reg_700; - - struct { - u32 net_addr_write :16; - u32 net_addr_read :16; - } net_buf_reg_704; - - struct { - u32 cai_cnt : 4; - u32 reserved2 : 6; - u32 cai_write :11; - u32 reserved1 : 5; - u32 cai_read :11; - } cai_buf_reg_708; - - struct { - u32 cao_cnt : 4; - u32 reserved2 : 6; - u32 cap_write :11; - u32 reserved1 : 5; - u32 cao_read :11; - } cao_buf_reg_70c; - - struct { - u32 media_cnt : 4; - u32 reserved2 : 6; - u32 media_write :11; - u32 reserved1 : 5; - u32 media_read :11; - } media_buf_reg_710; - - struct { - u32 reserved :17; - u32 ctrl_maximumfill : 1; - u32 ctrl_sramdma : 1; - u32 ctrl_usb_wan : 1; - u32 cao_ovflow_error : 1; - u32 cai_ovflow_error : 1; - u32 media_ovflow_error : 1; - u32 net_ovflow_error : 1; - u32 MEDIA_Dest : 2; - u32 CAO_Dest : 2; - u32 CAI_Dest : 2; - u32 NET_Dest : 2; - } sram_dest_reg_714; - - struct { - u32 reserved3 :11; - u32 net_addr_write : 1; - u32 reserved2 : 3; - u32 net_addr_read : 1; - u32 reserved1 : 4; - u32 net_cnt :12; - } net_buf_reg_718; - - struct { - u32 reserved3 : 4; - u32 wan_pkt_frame : 4; - u32 reserved2 : 4; - u32 sram_memmap : 2; - u32 sram_chip : 2; - u32 wan_wait_state : 8; - u32 reserved1 : 6; - u32 wan_speed_sig : 2; - } wan_ctrl_reg_71c; -} flexcop_ibi_value; - -#endif diff --git a/drivers/media/pci/b2c2/flexcop_ibi_value_le.h b/drivers/media/pci/b2c2/flexcop_ibi_value_le.h deleted file mode 100644 index c75830d7d942..000000000000 --- a/drivers/media/pci/b2c2/flexcop_ibi_value_le.h +++ /dev/null @@ -1,455 +0,0 @@ -/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III - * register descriptions - * see flexcop.c for copyright information - */ -/* This file is automatically generated, do not edit things here. */ -#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__ -#define __FLEXCOP_IBI_VALUE_INCLUDED__ - -typedef union { - u32 raw; - - struct { - u32 dma_0start : 1; - u32 dma_0No_update : 1; - u32 dma_address0 :30; - } dma_0x0; - - struct { - u32 DMA_maxpackets : 8; - u32 dma_addr_size :24; - } dma_0x4_remap; - - struct { - u32 dma1timer : 7; - u32 unused : 1; - u32 dma_addr_size :24; - } dma_0x4_read; - - struct { - u32 unused : 1; - u32 dmatimer : 7; - u32 dma_addr_size :24; - } dma_0x4_write; - - struct { - u32 unused : 2; - u32 dma_cur_addr :30; - } dma_0x8; - - struct { - u32 dma_1start : 1; - u32 remap_enable : 1; - u32 dma_address1 :30; - } dma_0xc; - - struct { - u32 chipaddr : 7; - u32 reserved1 : 1; - u32 baseaddr : 8; - u32 data1_reg : 8; - u32 working_start : 1; - u32 twoWS_rw : 1; - u32 total_bytes : 2; - u32 twoWS_port_reg : 2; - u32 no_base_addr_ack_error : 1; - u32 st_done : 1; - } tw_sm_c_100; - - struct { - u32 data2_reg : 8; - u32 data3_reg : 8; - u32 data4_reg : 8; - u32 exlicit_stops : 1; - u32 force_stop : 1; - u32 unused : 6; - } tw_sm_c_104; - - struct { - u32 thi1 : 6; - u32 reserved1 : 2; - u32 tlo1 : 5; - u32 reserved2 :19; - } tw_sm_c_108; - - struct { - u32 thi1 : 6; - u32 reserved1 : 2; - u32 tlo1 : 5; - u32 reserved2 :19; - } tw_sm_c_10c; - - struct { - u32 thi1 : 6; - u32 reserved1 : 2; - u32 tlo1 : 5; - u32 reserved2 :19; - } tw_sm_c_110; - - struct { - u32 LNB_CTLHighCount_sig :15; - u32 LNB_CTLLowCount_sig :15; - u32 LNB_CTLPrescaler_sig : 2; - } lnb_switch_freq_200; - - struct { - u32 ACPI1_sig : 1; - u32 ACPI3_sig : 1; - u32 LNB_L_H_sig : 1; - u32 Per_reset_sig : 1; - u32 reserved :20; - u32 Rev_N_sig_revision_hi : 4; - u32 Rev_N_sig_reserved1 : 2; - u32 Rev_N_sig_caps : 1; - u32 Rev_N_sig_reserved2 : 1; - } misc_204; - - struct { - u32 Stream1_filter_sig : 1; - u32 Stream2_filter_sig : 1; - u32 PCR_filter_sig : 1; - u32 PMT_filter_sig : 1; - u32 EMM_filter_sig : 1; - u32 ECM_filter_sig : 1; - u32 Null_filter_sig : 1; - u32 Mask_filter_sig : 1; - u32 WAN_Enable_sig : 1; - u32 WAN_CA_Enable_sig : 1; - u32 CA_Enable_sig : 1; - u32 SMC_Enable_sig : 1; - u32 Per_CA_Enable_sig : 1; - u32 Multi2_Enable_sig : 1; - u32 MAC_filter_Mode_sig : 1; - u32 Rcv_Data_sig : 1; - u32 DMA1_IRQ_Enable_sig : 1; - u32 DMA1_Timer_Enable_sig : 1; - u32 DMA2_IRQ_Enable_sig : 1; - u32 DMA2_Timer_Enable_sig : 1; - u32 DMA1_Size_IRQ_Enable_sig : 1; - u32 DMA2_Size_IRQ_Enable_sig : 1; - u32 Mailbox_from_V8_Enable_sig : 1; - u32 unused : 9; - } ctrl_208; - - struct { - u32 DMA1_IRQ_Status : 1; - u32 DMA1_Timer_Status : 1; - u32 DMA2_IRQ_Status : 1; - u32 DMA2_Timer_Status : 1; - u32 DMA1_Size_IRQ_Status : 1; - u32 DMA2_Size_IRQ_Status : 1; - u32 Mailbox_from_V8_Status_sig : 1; - u32 Data_receiver_error : 1; - u32 Continuity_error_flag : 1; - u32 LLC_SNAP_FLAG_set : 1; - u32 Transport_Error : 1; - u32 reserved :21; - } irq_20c; - - struct { - u32 reset_block_000 : 1; - u32 reset_block_100 : 1; - u32 reset_block_200 : 1; - u32 reset_block_300 : 1; - u32 reset_block_400 : 1; - u32 reset_block_500 : 1; - u32 reset_block_600 : 1; - u32 reset_block_700 : 1; - u32 Block_reset_enable : 8; - u32 Special_controls :16; - } sw_reset_210; - - struct { - u32 vuart_oe_sig : 1; - u32 v2WS_oe_sig : 1; - u32 halt_V8_sig : 1; - u32 section_pkg_enable_sig : 1; - u32 s2p_sel_sig : 1; - u32 unused1 : 3; - u32 polarity_PS_CLK_sig : 1; - u32 polarity_PS_VALID_sig : 1; - u32 polarity_PS_SYNC_sig : 1; - u32 polarity_PS_ERR_sig : 1; - u32 unused2 :20; - } misc_214; - - struct { - u32 Mailbox_from_V8 :32; - } mbox_v8_to_host_218; - - struct { - u32 sysramaccess_data : 8; - u32 sysramaccess_addr :15; - u32 unused : 7; - u32 sysramaccess_write : 1; - u32 sysramaccess_busmuster : 1; - } mbox_host_to_v8_21c; - - struct { - u32 Stream1_PID :13; - u32 Stream1_trans : 1; - u32 MAC_Multicast_filter : 1; - u32 debug_flag_pid_saved : 1; - u32 Stream2_PID :13; - u32 Stream2_trans : 1; - u32 debug_flag_write_status00 : 1; - u32 debug_fifo_problem : 1; - } pid_filter_300; - - struct { - u32 PCR_PID :13; - u32 PCR_trans : 1; - u32 debug_overrun3 : 1; - u32 debug_overrun2 : 1; - u32 PMT_PID :13; - u32 PMT_trans : 1; - u32 reserved : 2; - } pid_filter_304; - - struct { - u32 EMM_PID :13; - u32 EMM_trans : 1; - u32 EMM_filter_4 : 1; - u32 EMM_filter_6 : 1; - u32 ECM_PID :13; - u32 ECM_trans : 1; - u32 reserved : 2; - } pid_filter_308; - - struct { - u32 Group_PID :13; - u32 Group_trans : 1; - u32 unused1 : 2; - u32 Group_mask :13; - u32 unused2 : 3; - } pid_filter_30c_ext_ind_0_7; - - struct { - u32 net_master_read :17; - u32 unused :15; - } pid_filter_30c_ext_ind_1; - - struct { - u32 net_master_write :17; - u32 unused :15; - } pid_filter_30c_ext_ind_2; - - struct { - u32 next_net_master_write :17; - u32 unused :15; - } pid_filter_30c_ext_ind_3; - - struct { - u32 unused1 : 1; - u32 state_write :10; - u32 reserved1 : 6; - u32 stack_read :10; - u32 reserved2 : 5; - } pid_filter_30c_ext_ind_4; - - struct { - u32 stack_cnt :10; - u32 unused :22; - } pid_filter_30c_ext_ind_5; - - struct { - u32 pid_fsm_save_reg0 : 2; - u32 pid_fsm_save_reg1 : 2; - u32 pid_fsm_save_reg2 : 2; - u32 pid_fsm_save_reg3 : 2; - u32 pid_fsm_save_reg4 : 2; - u32 pid_fsm_save_reg300 : 2; - u32 write_status1 : 2; - u32 write_status4 : 2; - u32 data_size_reg :12; - u32 unused : 4; - } pid_filter_30c_ext_ind_6; - - struct { - u32 index_reg : 5; - u32 extra_index_reg : 3; - u32 AB_select : 1; - u32 pass_alltables : 1; - u32 unused :22; - } index_reg_310; - - struct { - u32 PID :13; - u32 PID_trans : 1; - u32 PID_enable_bit : 1; - u32 reserved :17; - } pid_n_reg_314; - - struct { - u32 A4_byte : 8; - u32 A5_byte : 8; - u32 A6_byte : 8; - u32 Enable_bit : 1; - u32 HighAB_bit : 1; - u32 reserved : 6; - } mac_low_reg_318; - - struct { - u32 A1_byte : 8; - u32 A2_byte : 8; - u32 A3_byte : 8; - u32 reserved : 8; - } mac_high_reg_31c; - - struct { - u32 reserved :16; - u32 data_Tag_ID :16; - } data_tag_400; - - struct { - u32 Card_IDbyte6 : 8; - u32 Card_IDbyte5 : 8; - u32 Card_IDbyte4 : 8; - u32 Card_IDbyte3 : 8; - } card_id_408; - - struct { - u32 Card_IDbyte2 : 8; - u32 Card_IDbyte1 : 8; - } card_id_40c; - - struct { - u32 MAC1 : 8; - u32 MAC2 : 8; - u32 MAC3 : 8; - u32 MAC6 : 8; - } mac_address_418; - - struct { - u32 MAC7 : 8; - u32 MAC8 : 8; - u32 reserved :16; - } mac_address_41c; - - struct { - u32 transmitter_data_byte : 8; - u32 ReceiveDataReady : 1; - u32 ReceiveByteFrameError : 1; - u32 txbuffempty : 1; - u32 reserved :21; - } ci_600; - - struct { - u32 pi_d : 8; - u32 pi_ha :20; - u32 pi_rw : 1; - u32 pi_component_reg : 3; - } pi_604; - - struct { - u32 serialReset : 1; - u32 oncecycle_read : 1; - u32 Timer_Read_req : 1; - u32 Timer_Load_req : 1; - u32 timer_data : 7; - u32 unused : 1; - u32 Timer_addr : 5; - u32 reserved : 3; - u32 pcmcia_a_mod_pwr_n : 1; - u32 pcmcia_b_mod_pwr_n : 1; - u32 config_Done_stat : 1; - u32 config_Init_stat : 1; - u32 config_Prog_n : 1; - u32 config_wr_n : 1; - u32 config_cs_n : 1; - u32 config_cclk : 1; - u32 pi_CiMax_IRQ_n : 1; - u32 pi_timeout_status : 1; - u32 pi_wait_n : 1; - u32 pi_busy_n : 1; - } pi_608; - - struct { - u32 PID :13; - u32 key_enable : 1; - u32 key_code : 2; - u32 key_array_col : 3; - u32 key_array_row : 5; - u32 dvb_en : 1; - u32 rw_flag : 1; - u32 reserved : 6; - } dvb_reg_60c; - - struct { - u32 sram_addr :15; - u32 sram_rw : 1; - u32 sram_data : 8; - u32 sc_xfer_bit : 1; - u32 reserved1 : 3; - u32 oe_pin_reg : 1; - u32 ce_pin_reg : 1; - u32 reserved2 : 1; - u32 start_sram_ibi : 1; - } sram_ctrl_reg_700; - - struct { - u32 net_addr_read :16; - u32 net_addr_write :16; - } net_buf_reg_704; - - struct { - u32 cai_read :11; - u32 reserved1 : 5; - u32 cai_write :11; - u32 reserved2 : 6; - u32 cai_cnt : 4; - } cai_buf_reg_708; - - struct { - u32 cao_read :11; - u32 reserved1 : 5; - u32 cap_write :11; - u32 reserved2 : 6; - u32 cao_cnt : 4; - } cao_buf_reg_70c; - - struct { - u32 media_read :11; - u32 reserved1 : 5; - u32 media_write :11; - u32 reserved2 : 6; - u32 media_cnt : 4; - } media_buf_reg_710; - - struct { - u32 NET_Dest : 2; - u32 CAI_Dest : 2; - u32 CAO_Dest : 2; - u32 MEDIA_Dest : 2; - u32 net_ovflow_error : 1; - u32 media_ovflow_error : 1; - u32 cai_ovflow_error : 1; - u32 cao_ovflow_error : 1; - u32 ctrl_usb_wan : 1; - u32 ctrl_sramdma : 1; - u32 ctrl_maximumfill : 1; - u32 reserved :17; - } sram_dest_reg_714; - - struct { - u32 net_cnt :12; - u32 reserved1 : 4; - u32 net_addr_read : 1; - u32 reserved2 : 3; - u32 net_addr_write : 1; - u32 reserved3 :11; - } net_buf_reg_718; - - struct { - u32 wan_speed_sig : 2; - u32 reserved1 : 6; - u32 wan_wait_state : 8; - u32 sram_chip : 2; - u32 sram_memmap : 2; - u32 reserved2 : 4; - u32 wan_pkt_frame : 4; - u32 reserved3 : 4; - } wan_ctrl_reg_71c; -} flexcop_ibi_value; - -#endif diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 70b1708db05a..53664b35af1c 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -14,5 +14,6 @@ source "drivers/media/usb/dvb-usb-v2/Kconfig" source "drivers/media/usb/ttusb-budget/Kconfig" source "drivers/media/usb/ttusb-dec/Kconfig" source "drivers/media/usb/siano/Kconfig" +source "drivers/media/usb/b2c2/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 44e29f340ebd..6b30ad13c38e 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -3,4 +3,4 @@ # # DVB USB-only drivers -obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ +obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ diff --git a/drivers/media/usb/b2c2/Kconfig b/drivers/media/usb/b2c2/Kconfig new file mode 100644 index 000000000000..3af7c4155473 --- /dev/null +++ b/drivers/media/usb/b2c2/Kconfig @@ -0,0 +1,6 @@ +config DVB_B2C2_FLEXCOP_USB + tristate "Technisat/B2C2 Air/Sky/Cable2PC USB" + help + Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2, + + Say Y if you own such a device and want to use it. diff --git a/drivers/media/usb/b2c2/Makefile b/drivers/media/usb/b2c2/Makefile new file mode 100644 index 000000000000..9eaf208bfa43 --- /dev/null +++ b/drivers/media/usb/b2c2/Makefile @@ -0,0 +1,7 @@ +b2c2-flexcop-usb-objs = flexcop-usb.o +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/common/b2c2/ diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c new file mode 100644 index 000000000000..8b6275f85908 --- /dev/null +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -0,0 +1,587 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-usb.c - covers the USB part + * see flexcop.c for copyright information + */ +#define FC_LOG_PREFIX "flexcop_usb" +#include "flexcop-usb.h" +#include "flexcop-common.h" + +/* Version information */ +#define DRIVER_VERSION "0.1" +#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver" +#define DRIVER_AUTHOR "Patrick Boettcher " + +/* debug */ +#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG +#define dprintk(level,args...) \ + do { if ((debug & level)) printk(args); } while (0) + +#define debug_dump(b, l, method) do {\ + int i; \ + for (i = 0; i < l; i++) \ + method("%02x ", b[i]); \ + method("\n"); \ +} while (0) + +#define DEBSTATUS "" +#else +#define dprintk(level, args...) +#define debug_dump(b, l, method) +#define DEBSTATUS " (debugging is not enabled)" +#endif + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2," + "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS); +#undef DEBSTATUS + +#define deb_info(args...) dprintk(0x01, args) +#define deb_ts(args...) dprintk(0x02, args) +#define deb_ctrl(args...) dprintk(0x04, args) +#define deb_i2c(args...) dprintk(0x08, args) +#define deb_v8(args...) dprintk(0x10, args) + +/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits + * in the IBI address, to make the V8 code simpler. + * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used) + * in general: 0000 0HHH 000L LL00 + * IBI ADDRESS FORMAT: RHHH BLLL + * + * where R is the read(1)/write(0) bit, B is the busy bit + * and HHH and LLL are the two sets of three bits from the PCI address. + */ +#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \ + (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70)) +#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \ + (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4)) + +/* + * DKT 020228 + * - forget about this VENDOR_BUFFER_SIZE, read and write register + * deal with DWORD or 4 bytes, that should be should from now on + * - from now on, we don't support anything older than firm 1.00 + * I eliminated the write register as a 2 trip of writing hi word and lo word + * and force this to write only 4 bytes at a time. + * NOTE: this should work with all the firmware from 1.00 and newer + */ +static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read) +{ + struct flexcop_usb *fc_usb = fc->bus_specific; + u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG; + u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR; + u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | + (read ? 0x80 : 0); + + int len = usb_control_msg(fc_usb->udev, + read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT, + request, + request_type, /* 0xc0 read or 0x40 write */ + wAddress, + 0, + val, + sizeof(u32), + B2C2_WAIT_FOR_OPERATION_RDW * HZ); + + if (len != sizeof(u32)) { + err("error while %s dword from %d (%d).", read ? "reading" : + "writing", wAddress, wRegOffsPCI); + return -EIO; + } + return 0; +} +/* + * DKT 010817 - add support for V8 memory read/write and flash update + */ +static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb, + flexcop_usb_request_t req, u8 page, u16 wAddress, + u8 *pbBuffer, u32 buflen) +{ + u8 request_type = USB_TYPE_VENDOR; + u16 wIndex; + int nWaitTime, pipe, len; + wIndex = page << 8; + + switch (req) { + case B2C2_USB_READ_V8_MEM: + nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ; + request_type |= USB_DIR_IN; + pipe = B2C2_USB_CTRL_PIPE_IN; + break; + case B2C2_USB_WRITE_V8_MEM: + wIndex |= pbBuffer[0]; + request_type |= USB_DIR_OUT; + nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE; + pipe = B2C2_USB_CTRL_PIPE_OUT; + break; + case B2C2_USB_FLASH_BLOCK: + request_type |= USB_DIR_OUT; + nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH; + pipe = B2C2_USB_CTRL_PIPE_OUT; + break; + default: + deb_info("unsupported request for v8_mem_req %x.\n", req); + return -EINVAL; + } + deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req, + wAddress, wIndex, buflen); + + len = usb_control_msg(fc_usb->udev, pipe, + req, + request_type, + wAddress, + wIndex, + pbBuffer, + buflen, + nWaitTime * HZ); + + debug_dump(pbBuffer, len, deb_v8); + return len == buflen ? 0 : -EIO; +} + +#define bytes_left_to_read_on_page(paddr,buflen) \ + ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \ + ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK))) + +static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, + flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, + u32 addr, int extended, u8 *buf, u32 len) +{ + int i,ret = 0; + u16 wMax; + u32 pagechunk = 0; + + switch(req) { + case B2C2_USB_READ_V8_MEM: + wMax = USB_MEM_READ_MAX; + break; + case B2C2_USB_WRITE_V8_MEM: + wMax = USB_MEM_WRITE_MAX; + break; + case B2C2_USB_FLASH_BLOCK: + wMax = USB_FLASH_MAX; + break; + default: + return -EINVAL; + break; + } + for (i = 0; i < len;) { + pagechunk = + wMax < bytes_left_to_read_on_page(addr, len) ? + wMax : + bytes_left_to_read_on_page(addr, len); + deb_info("%x\n", + (addr & V8_MEMORY_PAGE_MASK) | + (V8_MEMORY_EXTENDED*extended)); + + ret = flexcop_usb_v8_memory_req(fc_usb, req, + page_start + (addr / V8_MEMORY_PAGE_SIZE), + (addr & V8_MEMORY_PAGE_MASK) | + (V8_MEMORY_EXTENDED*extended), + &buf[i], pagechunk); + + if (ret < 0) + return ret; + addr += pagechunk; + len -= pagechunk; + } + return 0; +} + +static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended) +{ + return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM, + V8_MEMORY_PAGE_FLASH, 0x1f010, 1, + fc->dvb_adapter.proposed_mac, 6); +} + +#if 0 +static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set, + flexcop_usb_utility_function_t func, u8 extra, u16 wIndex, + u16 buflen, u8 *pvBuffer) +{ + u16 wValue; + u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR; + int nWaitTime = 2, + pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len; + wValue = (func << 8) | extra; + + len = usb_control_msg(fc_usb->udev,pipe, + B2C2_USB_UTILITY, + request_type, + wValue, + wIndex, + pvBuffer, + buflen, + nWaitTime * HZ); + return len == buflen ? 0 : -EIO; +} +#endif + +/* usb i2c stuff */ +static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c, + flexcop_usb_request_t req, flexcop_usb_i2c_function_t func, + u8 chipaddr, u8 addr, u8 *buf, u8 buflen) +{ + struct flexcop_usb *fc_usb = i2c->fc->bus_specific; + u16 wValue, wIndex; + int nWaitTime,pipe,len; + u8 request_type = USB_TYPE_VENDOR; + + switch (func) { + case USB_FUNC_I2C_WRITE: + case USB_FUNC_I2C_MULTIWRITE: + case USB_FUNC_I2C_REPEATWRITE: + /* DKT 020208 - add this to support special case of DiSEqC */ + case USB_FUNC_I2C_CHECKWRITE: + pipe = B2C2_USB_CTRL_PIPE_OUT; + nWaitTime = 2; + request_type |= USB_DIR_OUT; + break; + case USB_FUNC_I2C_READ: + case USB_FUNC_I2C_REPEATREAD: + pipe = B2C2_USB_CTRL_PIPE_IN; + nWaitTime = 2; + request_type |= USB_DIR_IN; + break; + default: + deb_info("unsupported function for i2c_req %x\n", func); + return -EINVAL; + } + wValue = (func << 8) | (i2c->port << 4); + wIndex = (chipaddr << 8 ) | addr; + + deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n", + func, request_type, req, + wValue & 0xff, wValue >> 8, + wIndex & 0xff, wIndex >> 8); + + len = usb_control_msg(fc_usb->udev,pipe, + req, + request_type, + wValue, + wIndex, + buf, + buflen, + nWaitTime * HZ); + return len == buflen ? 0 : -EREMOTEIO; +} + +/* actual bus specific access functions, + make sure prototype are/will be equal to pci */ +static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, + flexcop_ibi_register reg) +{ + flexcop_ibi_value val; + val.raw = 0; + flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1); + return val; +} + +static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, + flexcop_ibi_register reg, flexcop_ibi_value val) +{ + return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0); +} + +static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c, + flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) +{ + if (op == FC_READ) + return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, + USB_FUNC_I2C_READ, chipaddr, addr, buf, len); + else + return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, + USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len); +} + +static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, + u8 *buffer, int buffer_length) +{ + u8 *b; + int l; + + deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", + fc_usb->tmp_buffer_length, buffer_length); + + if (fc_usb->tmp_buffer_length > 0) { + memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, + buffer_length); + fc_usb->tmp_buffer_length += buffer_length; + b = fc_usb->tmp_buffer; + l = fc_usb->tmp_buffer_length; + } else { + b=buffer; + l=buffer_length; + } + + while (l >= 190) { + if (*b == 0xff) { + switch (*(b+1) & 0x03) { + case 0x01: /* media packet */ + if (*(b+2) == 0x47) + flexcop_pass_dmx_packets( + fc_usb->fc_dev, b+2, 1); + else + deb_ts("not ts packet %*ph\n", 4, b+2); + b += 190; + l -= 190; + break; + default: + deb_ts("wrong packet type\n"); + l = 0; + break; + } + } else { + deb_ts("wrong header\n"); + l = 0; + } + } + + if (l>0) + memcpy(fc_usb->tmp_buffer, b, l); + fc_usb->tmp_buffer_length = l; +} + +static void flexcop_usb_urb_complete(struct urb *urb) +{ + struct flexcop_usb *fc_usb = urb->context; + int i; + + if (urb->actual_length > 0) + deb_ts("urb completed, bufsize: %d actlen; %d\n", + urb->transfer_buffer_length, urb->actual_length); + + for (i = 0; i < urb->number_of_packets; i++) { + if (urb->iso_frame_desc[i].status < 0) { + err("iso frame descriptor %d has an error: %d\n", i, + urb->iso_frame_desc[i].status); + } else + if (urb->iso_frame_desc[i].actual_length > 0) { + deb_ts("passed %d bytes to the demux\n", + urb->iso_frame_desc[i].actual_length); + + flexcop_usb_process_frame(fc_usb, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + usb_submit_urb(urb,GFP_ATOMIC); +} + +static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff) +{ + /* submit/kill iso packets */ + return 0; +} + +static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) +{ + int i; + for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) + if (fc_usb->iso_urb[i] != NULL) { + deb_ts("unlinking/killing urb no. %d\n",i); + usb_kill_urb(fc_usb->iso_urb[i]); + usb_free_urb(fc_usb->iso_urb[i]); + } + + if (fc_usb->iso_buffer != NULL) + pci_free_consistent(NULL, + fc_usb->buffer_size, fc_usb->iso_buffer, + fc_usb->dma_addr); +} + +static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) +{ + u16 frame_size = le16_to_cpu( + fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize); + int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * + frame_size, i, j, ret; + int buffer_offset = 0; + + deb_ts("creating %d iso-urbs with %d frames " + "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB, + B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize); + + fc_usb->iso_buffer = pci_alloc_consistent(NULL, + bufsize, &fc_usb->dma_addr); + if (fc_usb->iso_buffer == NULL) + return -ENOMEM; + + memset(fc_usb->iso_buffer, 0, bufsize); + fc_usb->buffer_size = bufsize; + + /* creating iso urbs */ + for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { + fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO, + GFP_ATOMIC); + if (fc_usb->iso_urb[i] == NULL) { + ret = -ENOMEM; + goto urb_error; + } + } + + /* initialising and submitting iso urbs */ + for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { + int frame_offset = 0; + struct urb *urb = fc_usb->iso_urb[i]; + deb_ts("initializing and submitting urb no. %d " + "(buf_offset: %d).\n", i, buffer_offset); + + urb->dev = fc_usb->udev; + urb->context = fc_usb; + urb->complete = flexcop_usb_urb_complete; + urb->pipe = B2C2_USB_DATA_PIPE; + urb->transfer_flags = URB_ISO_ASAP; + urb->interval = 1; + urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO; + urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO; + urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset; + + buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO; + for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) { + deb_ts("urb no: %d, frame: %d, frame_offset: %d\n", + i, j, frame_offset); + urb->iso_frame_desc[j].offset = frame_offset; + urb->iso_frame_desc[j].length = frame_size; + frame_offset += frame_size; + } + + if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) { + err("submitting urb %d failed with %d.", i, ret); + goto urb_error; + } + deb_ts("submitted urb no. %d.\n",i); + } + + /* SRAM */ + flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA | + FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, + FC_SRAM_DEST_TARGET_WAN_USB); + flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS); + flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1); + return 0; + +urb_error: + flexcop_usb_transfer_exit(fc_usb); + return ret; +} + +static int flexcop_usb_init(struct flexcop_usb *fc_usb) +{ + /* use the alternate setting with the larges buffer */ + usb_set_interface(fc_usb->udev,0,1); + switch (fc_usb->udev->speed) { + case USB_SPEED_LOW: + err("cannot handle USB speed because it is too slow."); + return -ENODEV; + break; + case USB_SPEED_FULL: + info("running at FULL speed."); + break; + case USB_SPEED_HIGH: + info("running at HIGH speed."); + break; + case USB_SPEED_UNKNOWN: /* fall through */ + default: + err("cannot handle USB speed because it is unknown."); + return -ENODEV; + } + usb_set_intfdata(fc_usb->uintf, fc_usb); + return 0; +} + +static void flexcop_usb_exit(struct flexcop_usb *fc_usb) +{ + usb_set_intfdata(fc_usb->uintf, NULL); +} + +static int flexcop_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct flexcop_usb *fc_usb = NULL; + struct flexcop_device *fc = NULL; + int ret; + + if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { + err("out of memory\n"); + return -ENOMEM; + } + + /* general flexcop init */ + fc_usb = fc->bus_specific; + fc_usb->fc_dev = fc; + + fc->read_ibi_reg = flexcop_usb_read_ibi_reg; + fc->write_ibi_reg = flexcop_usb_write_ibi_reg; + fc->i2c_request = flexcop_usb_i2c_request; + fc->get_mac_addr = flexcop_usb_get_mac_addr; + + fc->stream_control = flexcop_usb_stream_control; + + fc->pid_filtering = 1; + fc->bus_type = FC_USB; + + fc->dev = &udev->dev; + fc->owner = THIS_MODULE; + + /* bus specific part */ + fc_usb->udev = udev; + fc_usb->uintf = intf; + if ((ret = flexcop_usb_init(fc_usb)) != 0) + goto err_kfree; + + /* init flexcop */ + if ((ret = flexcop_device_initialize(fc)) != 0) + goto err_usb_exit; + + /* xfer init */ + if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0) + goto err_fc_exit; + + info("%s successfully initialized and connected.", DRIVER_NAME); + return 0; + +err_fc_exit: + flexcop_device_exit(fc); +err_usb_exit: + flexcop_usb_exit(fc_usb); +err_kfree: + flexcop_device_kfree(fc); + return ret; +} + +static void flexcop_usb_disconnect(struct usb_interface *intf) +{ + struct flexcop_usb *fc_usb = usb_get_intfdata(intf); + flexcop_usb_transfer_exit(fc_usb); + flexcop_device_exit(fc_usb->fc_dev); + flexcop_usb_exit(fc_usb); + flexcop_device_kfree(fc_usb->fc_dev); + info("%s successfully deinitialized and disconnected.", DRIVER_NAME); +} + +static struct usb_device_id flexcop_usb_table [] = { + { USB_DEVICE(0x0af7, 0x0101) }, + { } +}; +MODULE_DEVICE_TABLE (usb, flexcop_usb_table); + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver flexcop_usb_driver = { + .name = "b2c2_flexcop_usb", + .probe = flexcop_usb_probe, + .disconnect = flexcop_usb_disconnect, + .id_table = flexcop_usb_table, +}; + +module_usb_driver(flexcop_usb_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/b2c2/flexcop-usb.h b/drivers/media/usb/b2c2/flexcop-usb.h new file mode 100644 index 000000000000..92529a9c4475 --- /dev/null +++ b/drivers/media/usb/b2c2/flexcop-usb.h @@ -0,0 +1,111 @@ +/* + * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III + * flexcop-usb.h - header file for the USB part + * see flexcop.c for copyright information + */ +#ifndef __FLEXCOP_USB_H_INCLUDED__ +#define __FLEXCOP_USB_H_INCLUDED__ + +#include + +/* transfer parameters */ +#define B2C2_USB_FRAMES_PER_ISO 4 +#define B2C2_USB_NUM_ISO_URB 4 + +#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0) +#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0) +#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81) + +struct flexcop_usb { + struct usb_device *udev; + struct usb_interface *uintf; + + u8 *iso_buffer; + int buffer_size; + dma_addr_t dma_addr; + + struct urb *iso_urb[B2C2_USB_NUM_ISO_URB]; + struct flexcop_device *fc_dev; + + u8 tmp_buffer[1023+190]; + int tmp_buffer_length; +}; + +#if 0 +/* request types TODO What is its use?*/ +typedef enum { + +} flexcop_usb_request_type_t; +#endif + +/* request */ +typedef enum { + B2C2_USB_WRITE_V8_MEM = 0x04, + B2C2_USB_READ_V8_MEM = 0x05, + B2C2_USB_READ_REG = 0x08, + B2C2_USB_WRITE_REG = 0x0A, + B2C2_USB_WRITEREGHI = 0x0B, + B2C2_USB_FLASH_BLOCK = 0x10, + B2C2_USB_I2C_REQUEST = 0x11, + B2C2_USB_UTILITY = 0x12, +} flexcop_usb_request_t; + +/* function definition for I2C_REQUEST */ +typedef enum { + USB_FUNC_I2C_WRITE = 0x01, + USB_FUNC_I2C_MULTIWRITE = 0x02, + USB_FUNC_I2C_READ = 0x03, + USB_FUNC_I2C_REPEATWRITE = 0x04, + USB_FUNC_GET_DESCRIPTOR = 0x05, + USB_FUNC_I2C_REPEATREAD = 0x06, + /* DKT 020208 - add this to support special case of DiSEqC */ + USB_FUNC_I2C_CHECKWRITE = 0x07, + USB_FUNC_I2C_CHECKRESULT = 0x08, +} flexcop_usb_i2c_function_t; + +/* function definition for UTILITY request 0x12 + * DKT 020304 - new utility function */ +typedef enum { + UTILITY_SET_FILTER = 0x01, + UTILITY_DATA_ENABLE = 0x02, + UTILITY_FLEX_MULTIWRITE = 0x03, + UTILITY_SET_BUFFER_SIZE = 0x04, + UTILITY_FLEX_OPERATOR = 0x05, + UTILITY_FLEX_RESET300_START = 0x06, + UTILITY_FLEX_RESET300_STOP = 0x07, + UTILITY_FLEX_RESET300 = 0x08, + UTILITY_SET_ISO_SIZE = 0x09, + UTILITY_DATA_RESET = 0x0A, + UTILITY_GET_DATA_STATUS = 0x10, + UTILITY_GET_V8_REG = 0x11, + /* DKT 020326 - add function for v1.14 */ + UTILITY_SRAM_WRITE = 0x12, + UTILITY_SRAM_READ = 0x13, + UTILITY_SRAM_TESTFILL = 0x14, + UTILITY_SRAM_TESTSET = 0x15, + UTILITY_SRAM_TESTVERIFY = 0x16, +} flexcop_usb_utility_function_t; + +#define B2C2_WAIT_FOR_OPERATION_RW (1*HZ) +#define B2C2_WAIT_FOR_OPERATION_RDW (3*HZ) +#define B2C2_WAIT_FOR_OPERATION_WDW (1*HZ) + +#define B2C2_WAIT_FOR_OPERATION_V8READ (3*HZ) +#define B2C2_WAIT_FOR_OPERATION_V8WRITE (3*HZ) +#define B2C2_WAIT_FOR_OPERATION_V8FLASH (3*HZ) + +typedef enum { + V8_MEMORY_PAGE_DVB_CI = 0x20, + V8_MEMORY_PAGE_DVB_DS = 0x40, + V8_MEMORY_PAGE_MULTI2 = 0x60, + V8_MEMORY_PAGE_FLASH = 0x80 +} flexcop_usb_mem_page_t; + +#define V8_MEMORY_EXTENDED (1 << 15) +#define USB_MEM_READ_MAX 32 +#define USB_MEM_WRITE_MAX 1 +#define USB_FLASH_MAX 8 +#define V8_MEMORY_PAGE_SIZE 0x8000 /* 32K */ +#define V8_MEMORY_PAGE_MASK 0x7FFF + +#endif -- 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/usb') 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 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/usb') 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 From b3f52608ba7b4c231621d7e4e9e1bfd8a390f910 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Aug 2012 23:43:08 -0300 Subject: [media] b2c2: frontends/tuners are not needed at the bridge binding The frontends/tuners are used inside the common part of the driver. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/b2c2/Makefile | 2 -- drivers/media/usb/b2c2/Makefile | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/pci/b2c2/Makefile b/drivers/media/pci/b2c2/Makefile index e90e2366265e..aedcac1dceb9 100644 --- a/drivers/media/pci/b2c2/Makefile +++ b/drivers/media/pci/b2c2/Makefile @@ -6,6 +6,4 @@ b2c2-flexcop-pci-objs = flexcop-pci.o obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o ccflags-y += -Idrivers/media/dvb-core/ -ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ ccflags-y += -Idrivers/media/common/b2c2/ diff --git a/drivers/media/usb/b2c2/Makefile b/drivers/media/usb/b2c2/Makefile index 2f7ee5cc5b29..ace9d76f0b38 100644 --- a/drivers/media/usb/b2c2/Makefile +++ b/drivers/media/usb/b2c2/Makefile @@ -2,6 +2,4 @@ b2c2-flexcop-usb-objs = flexcop-usb.o 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/tuners/ ccflags-y += -Idrivers/media/common/b2c2/ -- cgit v1.2.3 From 0c0d06cac63ee327ceaab4b5ffe2206574ab86bd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Aug 2012 00:13:22 -0300 Subject: [media] rename most media/video usb drivers to media/usb Rename all USB drivers with their own directory under drivers/media/video into drivers/media/usb and update the building system. Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 22 +- drivers/media/usb/Kconfig | 41 +- drivers/media/usb/Makefile | 14 + drivers/media/usb/au0828/Kconfig | 18 + drivers/media/usb/au0828/Makefile | 9 + drivers/media/usb/au0828/au0828-cards.c | 339 + drivers/media/usb/au0828/au0828-cards.h | 27 + drivers/media/usb/au0828/au0828-core.c | 282 + drivers/media/usb/au0828/au0828-dvb.c | 500 ++ drivers/media/usb/au0828/au0828-i2c.c | 401 ++ drivers/media/usb/au0828/au0828-reg.h | 66 + drivers/media/usb/au0828/au0828-vbi.c | 138 + drivers/media/usb/au0828/au0828-video.c | 2034 ++++++ drivers/media/usb/au0828/au0828.h | 304 + drivers/media/usb/cpia2/Kconfig | 9 + drivers/media/usb/cpia2/Makefile | 3 + drivers/media/usb/cpia2/cpia2.h | 487 ++ drivers/media/usb/cpia2/cpia2_core.c | 2417 +++++++ drivers/media/usb/cpia2/cpia2_registers.h | 476 ++ drivers/media/usb/cpia2/cpia2_usb.c | 955 +++ drivers/media/usb/cpia2/cpia2_v4l.c | 1267 ++++ drivers/media/usb/cx231xx/Kconfig | 51 + drivers/media/usb/cx231xx/Makefile | 15 + drivers/media/usb/cx231xx/cx231xx-417.c | 2197 ++++++ drivers/media/usb/cx231xx/cx231xx-audio.c | 780 +++ drivers/media/usb/cx231xx/cx231xx-avcore.c | 3087 +++++++++ drivers/media/usb/cx231xx/cx231xx-cards.c | 1370 ++++ drivers/media/usb/cx231xx/cx231xx-conf-reg.h | 495 ++ drivers/media/usb/cx231xx/cx231xx-core.c | 1736 +++++ drivers/media/usb/cx231xx/cx231xx-dif.h | 3178 +++++++++ drivers/media/usb/cx231xx/cx231xx-dvb.c | 796 +++ drivers/media/usb/cx231xx/cx231xx-i2c.c | 532 ++ drivers/media/usb/cx231xx/cx231xx-input.c | 119 + drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c | 795 +++ drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h | 231 + drivers/media/usb/cx231xx/cx231xx-reg.h | 1564 +++++ drivers/media/usb/cx231xx/cx231xx-vbi.c | 710 ++ drivers/media/usb/cx231xx/cx231xx-vbi.h | 65 + drivers/media/usb/cx231xx/cx231xx-video.c | 2670 ++++++++ drivers/media/usb/cx231xx/cx231xx.h | 1012 +++ drivers/media/usb/em28xx/Kconfig | 58 + drivers/media/usb/em28xx/Makefile | 15 + drivers/media/usb/em28xx/em28xx-audio.c | 751 +++ drivers/media/usb/em28xx/em28xx-cards.c | 3417 ++++++++++ drivers/media/usb/em28xx/em28xx-core.c | 1260 ++++ drivers/media/usb/em28xx/em28xx-dvb.c | 1197 ++++ drivers/media/usb/em28xx/em28xx-i2c.c | 568 ++ drivers/media/usb/em28xx/em28xx-input.c | 645 ++ drivers/media/usb/em28xx/em28xx-reg.h | 226 + drivers/media/usb/em28xx/em28xx-vbi.c | 145 + drivers/media/usb/em28xx/em28xx-video.c | 2624 ++++++++ drivers/media/usb/em28xx/em28xx.h | 809 +++ drivers/media/usb/gspca/Kconfig | 425 ++ drivers/media/usb/gspca/Makefile | 93 + drivers/media/usb/gspca/autogain_functions.c | 178 + drivers/media/usb/gspca/autogain_functions.h | 183 + drivers/media/usb/gspca/benq.c | 289 + drivers/media/usb/gspca/conex.c | 966 +++ drivers/media/usb/gspca/cpia1.c | 1905 ++++++ drivers/media/usb/gspca/etoms.c | 799 +++ drivers/media/usb/gspca/finepix.c | 306 + drivers/media/usb/gspca/gl860/Kconfig | 8 + drivers/media/usb/gspca/gl860/Makefile | 10 + drivers/media/usb/gspca/gl860/gl860-mi1320.c | 536 ++ drivers/media/usb/gspca/gl860/gl860-mi2020.c | 733 ++ drivers/media/usb/gspca/gl860/gl860-ov2640.c | 489 ++ drivers/media/usb/gspca/gl860/gl860-ov9655.c | 336 + drivers/media/usb/gspca/gl860/gl860.c | 725 ++ drivers/media/usb/gspca/gl860/gl860.h | 105 + drivers/media/usb/gspca/gspca.c | 2456 +++++++ drivers/media/usb/gspca/gspca.h | 259 + drivers/media/usb/gspca/jeilinj.c | 548 ++ drivers/media/usb/gspca/jl2005bcd.c | 557 ++ drivers/media/usb/gspca/jpeg.h | 168 + drivers/media/usb/gspca/kinect.c | 408 ++ drivers/media/usb/gspca/konica.c | 487 ++ drivers/media/usb/gspca/m5602/Kconfig | 11 + drivers/media/usb/gspca/m5602/Makefile | 11 + drivers/media/usb/gspca/m5602/m5602_bridge.h | 163 + drivers/media/usb/gspca/m5602/m5602_core.c | 424 ++ drivers/media/usb/gspca/m5602/m5602_mt9m111.c | 647 ++ drivers/media/usb/gspca/m5602/m5602_mt9m111.h | 271 + drivers/media/usb/gspca/m5602/m5602_ov7660.c | 488 ++ drivers/media/usb/gspca/m5602/m5602_ov7660.h | 260 + drivers/media/usb/gspca/m5602/m5602_ov9650.c | 881 +++ drivers/media/usb/gspca/m5602/m5602_ov9650.h | 307 + drivers/media/usb/gspca/m5602/m5602_po1030.c | 763 +++ drivers/media/usb/gspca/m5602/m5602_po1030.h | 272 + drivers/media/usb/gspca/m5602/m5602_s5k4aa.c | 726 ++ drivers/media/usb/gspca/m5602/m5602_s5k4aa.h | 283 + drivers/media/usb/gspca/m5602/m5602_s5k83a.c | 605 ++ drivers/media/usb/gspca/m5602/m5602_s5k83a.h | 193 + drivers/media/usb/gspca/m5602/m5602_sensor.h | 70 + drivers/media/usb/gspca/mars.c | 439 ++ drivers/media/usb/gspca/mr97310a.c | 1091 +++ drivers/media/usb/gspca/nw80x.c | 2110 ++++++ drivers/media/usb/gspca/ov519.c | 4991 ++++++++++++++ drivers/media/usb/gspca/ov534.c | 1544 +++++ drivers/media/usb/gspca/ov534_9.c | 1500 +++++ drivers/media/usb/gspca/pac207.c | 469 ++ drivers/media/usb/gspca/pac7302.c | 932 +++ drivers/media/usb/gspca/pac7311.c | 701 ++ drivers/media/usb/gspca/pac_common.h | 134 + drivers/media/usb/gspca/se401.c | 739 ++ drivers/media/usb/gspca/se401.h | 90 + drivers/media/usb/gspca/sn9c2028.c | 735 ++ drivers/media/usb/gspca/sn9c2028.h | 51 + drivers/media/usb/gspca/sn9c20x.c | 2428 +++++++ drivers/media/usb/gspca/sonixb.c | 1493 +++++ drivers/media/usb/gspca/sonixj.c | 3206 +++++++++ drivers/media/usb/gspca/spca1528.c | 444 ++ drivers/media/usb/gspca/spca500.c | 990 +++ drivers/media/usb/gspca/spca501.c | 2054 ++++++ drivers/media/usb/gspca/spca505.c | 807 +++ drivers/media/usb/gspca/spca506.c | 612 ++ drivers/media/usb/gspca/spca508.c | 1540 +++++ drivers/media/usb/gspca/spca561.c | 936 +++ drivers/media/usb/gspca/sq905.c | 440 ++ drivers/media/usb/gspca/sq905c.c | 344 + drivers/media/usb/gspca/sq930x.c | 1164 ++++ drivers/media/usb/gspca/stk014.c | 446 ++ drivers/media/usb/gspca/stv0680.c | 353 + drivers/media/usb/gspca/stv06xx/Kconfig | 9 + drivers/media/usb/gspca/stv06xx/Makefile | 10 + drivers/media/usb/gspca/stv06xx/stv06xx.c | 634 ++ drivers/media/usb/gspca/stv06xx/stv06xx.h | 116 + drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c | 540 ++ drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h | 206 + drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c | 434 ++ drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h | 144 + drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h | 87 + drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c | 285 + drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h | 52 + drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c | 272 + drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h | 255 + drivers/media/usb/gspca/sunplus.c | 1085 +++ drivers/media/usb/gspca/t613.c | 1054 +++ drivers/media/usb/gspca/topro.c | 4969 ++++++++++++++ drivers/media/usb/gspca/tv8532.c | 378 ++ drivers/media/usb/gspca/vc032x.c | 3850 +++++++++++ drivers/media/usb/gspca/vicam.c | 365 + drivers/media/usb/gspca/w996Xcf.c | 567 ++ drivers/media/usb/gspca/xirlink_cit.c | 3145 +++++++++ drivers/media/usb/gspca/zc3xx-reg.h | 251 + drivers/media/usb/gspca/zc3xx.c | 7024 ++++++++++++++++++++ drivers/media/usb/hdpvr/Kconfig | 10 + drivers/media/usb/hdpvr/Makefile | 7 + drivers/media/usb/hdpvr/hdpvr-control.c | 200 + drivers/media/usb/hdpvr/hdpvr-core.c | 472 ++ drivers/media/usb/hdpvr/hdpvr-i2c.c | 231 + drivers/media/usb/hdpvr/hdpvr-video.c | 1289 ++++ drivers/media/usb/hdpvr/hdpvr.h | 317 + drivers/media/usb/pvrusb2/Kconfig | 65 + drivers/media/usb/pvrusb2/Makefile | 22 + drivers/media/usb/pvrusb2/pvrusb2-audio.c | 96 + drivers/media/usb/pvrusb2/pvrusb2-audio.h | 37 + drivers/media/usb/pvrusb2/pvrusb2-context.c | 431 ++ drivers/media/usb/pvrusb2/pvrusb2-context.h | 94 + drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c | 95 + drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h | 48 + drivers/media/usb/pvrusb2/pvrusb2-ctrl.c | 609 ++ drivers/media/usb/pvrusb2/pvrusb2-ctrl.h | 122 + drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c | 166 + drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h | 52 + drivers/media/usb/pvrusb2/pvrusb2-debug.h | 69 + drivers/media/usb/pvrusb2/pvrusb2-debugifc.c | 335 + drivers/media/usb/pvrusb2/pvrusb2-debugifc.h | 52 + drivers/media/usb/pvrusb2/pvrusb2-devattr.c | 576 ++ drivers/media/usb/pvrusb2/pvrusb2-devattr.h | 199 + drivers/media/usb/pvrusb2/pvrusb2-dvb.c | 435 ++ drivers/media/usb/pvrusb2/pvrusb2-dvb.h | 41 + drivers/media/usb/pvrusb2/pvrusb2-eeprom.c | 164 + drivers/media/usb/pvrusb2/pvrusb2-eeprom.h | 39 + drivers/media/usb/pvrusb2/pvrusb2-encoder.c | 552 ++ drivers/media/usb/pvrusb2/pvrusb2-encoder.h | 42 + drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h | 72 + drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h | 406 ++ drivers/media/usb/pvrusb2/pvrusb2-hdw.c | 5202 +++++++++++++++ drivers/media/usb/pvrusb2/pvrusb2-hdw.h | 360 + drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c | 698 ++ drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h | 40 + drivers/media/usb/pvrusb2/pvrusb2-io.c | 695 ++ drivers/media/usb/pvrusb2/pvrusb2-io.h | 102 + drivers/media/usb/pvrusb2/pvrusb2-ioread.c | 512 ++ drivers/media/usb/pvrusb2/pvrusb2-ioread.h | 48 + drivers/media/usb/pvrusb2/pvrusb2-main.c | 182 + drivers/media/usb/pvrusb2/pvrusb2-std.c | 411 ++ drivers/media/usb/pvrusb2/pvrusb2-std.h | 59 + drivers/media/usb/pvrusb2/pvrusb2-sysfs.c | 861 +++ drivers/media/usb/pvrusb2/pvrusb2-sysfs.h | 46 + drivers/media/usb/pvrusb2/pvrusb2-util.h | 62 + drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 1413 ++++ drivers/media/usb/pvrusb2/pvrusb2-v4l2.h | 39 + drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c | 114 + drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h | 48 + drivers/media/usb/pvrusb2/pvrusb2-wm8775.c | 70 + drivers/media/usb/pvrusb2/pvrusb2-wm8775.h | 52 + drivers/media/usb/pvrusb2/pvrusb2.h | 42 + drivers/media/usb/pwc/Kconfig | 48 + drivers/media/usb/pwc/Makefile | 4 + drivers/media/usb/pwc/philips.txt | 236 + drivers/media/usb/pwc/pwc-ctrl.c | 553 ++ drivers/media/usb/pwc/pwc-dec1.c | 32 + drivers/media/usb/pwc/pwc-dec1.h | 39 + drivers/media/usb/pwc/pwc-dec23.c | 691 ++ drivers/media/usb/pwc/pwc-dec23.h | 61 + drivers/media/usb/pwc/pwc-if.c | 1165 ++++ drivers/media/usb/pwc/pwc-kiara.c | 892 +++ drivers/media/usb/pwc/pwc-kiara.h | 48 + drivers/media/usb/pwc/pwc-misc.c | 93 + drivers/media/usb/pwc/pwc-nala.h | 66 + drivers/media/usb/pwc/pwc-timon.c | 1448 ++++ drivers/media/usb/pwc/pwc-timon.h | 63 + drivers/media/usb/pwc/pwc-uncompress.c | 107 + drivers/media/usb/pwc/pwc-v4l.c | 1053 +++ drivers/media/usb/pwc/pwc.h | 393 ++ drivers/media/usb/sn9c102/Kconfig | 14 + drivers/media/usb/sn9c102/Makefile | 15 + drivers/media/usb/sn9c102/sn9c102.h | 211 + drivers/media/usb/sn9c102/sn9c102_config.h | 86 + drivers/media/usb/sn9c102/sn9c102_core.c | 3421 ++++++++++ drivers/media/usb/sn9c102/sn9c102_devtable.h | 147 + drivers/media/usb/sn9c102/sn9c102_hv7131d.c | 264 + drivers/media/usb/sn9c102/sn9c102_hv7131r.c | 363 + drivers/media/usb/sn9c102/sn9c102_mi0343.c | 352 + drivers/media/usb/sn9c102/sn9c102_mi0360.c | 453 ++ drivers/media/usb/sn9c102/sn9c102_mt9v111.c | 260 + drivers/media/usb/sn9c102/sn9c102_ov7630.c | 626 ++ drivers/media/usb/sn9c102/sn9c102_ov7660.c | 538 ++ drivers/media/usb/sn9c102/sn9c102_pas106b.c | 302 + drivers/media/usb/sn9c102/sn9c102_pas202bcb.c | 335 + drivers/media/usb/sn9c102/sn9c102_sensor.h | 307 + drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c | 154 + drivers/media/usb/sn9c102/sn9c102_tas5110d.c | 119 + drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c | 165 + drivers/media/usb/stk1160/Kconfig | 20 + drivers/media/usb/stk1160/Makefile | 11 + drivers/media/usb/stk1160/stk1160-ac97.c | 153 + drivers/media/usb/stk1160/stk1160-core.c | 432 ++ drivers/media/usb/stk1160/stk1160-i2c.c | 294 + drivers/media/usb/stk1160/stk1160-reg.h | 93 + drivers/media/usb/stk1160/stk1160-v4l.c | 738 ++ drivers/media/usb/stk1160/stk1160-video.c | 518 ++ drivers/media/usb/stk1160/stk1160.h | 208 + drivers/media/usb/tlg2300/Kconfig | 16 + drivers/media/usb/tlg2300/Makefile | 9 + drivers/media/usb/tlg2300/pd-alsa.c | 332 + drivers/media/usb/tlg2300/pd-common.h | 281 + drivers/media/usb/tlg2300/pd-dvb.c | 596 ++ drivers/media/usb/tlg2300/pd-main.c | 536 ++ drivers/media/usb/tlg2300/pd-radio.c | 421 ++ drivers/media/usb/tlg2300/pd-video.c | 1668 +++++ drivers/media/usb/tlg2300/vendorcmds.h | 243 + drivers/media/usb/tm6000/Kconfig | 33 + drivers/media/usb/tm6000/Makefile | 15 + drivers/media/usb/tm6000/tm6000-alsa.c | 528 ++ drivers/media/usb/tm6000/tm6000-cards.c | 1413 ++++ drivers/media/usb/tm6000/tm6000-core.c | 935 +++ drivers/media/usb/tm6000/tm6000-dvb.c | 467 ++ drivers/media/usb/tm6000/tm6000-i2c.c | 334 + drivers/media/usb/tm6000/tm6000-input.c | 498 ++ drivers/media/usb/tm6000/tm6000-regs.h | 600 ++ drivers/media/usb/tm6000/tm6000-stds.c | 638 ++ drivers/media/usb/tm6000/tm6000-usb-isoc.h | 50 + drivers/media/usb/tm6000/tm6000-video.c | 1852 ++++++ drivers/media/usb/tm6000/tm6000.h | 399 ++ drivers/media/usb/usbvision/Kconfig | 12 + drivers/media/usb/usbvision/Makefile | 6 + drivers/media/usb/usbvision/usbvision-cards.c | 1133 ++++ drivers/media/usb/usbvision/usbvision-cards.h | 69 + drivers/media/usb/usbvision/usbvision-core.c | 2518 +++++++ drivers/media/usb/usbvision/usbvision-i2c.c | 456 ++ drivers/media/usb/usbvision/usbvision-video.c | 1720 +++++ drivers/media/usb/usbvision/usbvision.h | 535 ++ drivers/media/usb/uvc/Kconfig | 19 + drivers/media/usb/uvc/Makefile | 6 + drivers/media/usb/uvc/uvc_ctrl.c | 2165 ++++++ drivers/media/usb/uvc/uvc_debugfs.c | 136 + drivers/media/usb/uvc/uvc_driver.c | 2472 +++++++ drivers/media/usb/uvc/uvc_entity.c | 126 + drivers/media/usb/uvc/uvc_isight.c | 137 + drivers/media/usb/uvc/uvc_queue.c | 359 + drivers/media/usb/uvc/uvc_status.c | 237 + drivers/media/usb/uvc/uvc_v4l2.c | 1317 ++++ drivers/media/usb/uvc/uvc_video.c | 1879 ++++++ drivers/media/usb/uvc/uvcvideo.h | 718 ++ drivers/media/video/Kconfig | 41 - drivers/media/video/Makefile | 16 - drivers/media/video/au0828/Kconfig | 18 - drivers/media/video/au0828/Makefile | 9 - drivers/media/video/au0828/au0828-cards.c | 339 - drivers/media/video/au0828/au0828-cards.h | 27 - drivers/media/video/au0828/au0828-core.c | 282 - drivers/media/video/au0828/au0828-dvb.c | 500 -- drivers/media/video/au0828/au0828-i2c.c | 401 -- drivers/media/video/au0828/au0828-reg.h | 66 - drivers/media/video/au0828/au0828-vbi.c | 138 - drivers/media/video/au0828/au0828-video.c | 2034 ------ drivers/media/video/au0828/au0828.h | 304 - drivers/media/video/cpia2/Kconfig | 9 - drivers/media/video/cpia2/Makefile | 3 - drivers/media/video/cpia2/cpia2.h | 487 -- drivers/media/video/cpia2/cpia2_core.c | 2417 ------- drivers/media/video/cpia2/cpia2_registers.h | 476 -- drivers/media/video/cpia2/cpia2_usb.c | 955 --- drivers/media/video/cpia2/cpia2_v4l.c | 1267 ---- drivers/media/video/cx231xx/Kconfig | 51 - drivers/media/video/cx231xx/Makefile | 15 - drivers/media/video/cx231xx/cx231xx-417.c | 2197 ------ drivers/media/video/cx231xx/cx231xx-audio.c | 780 --- drivers/media/video/cx231xx/cx231xx-avcore.c | 3087 --------- drivers/media/video/cx231xx/cx231xx-cards.c | 1370 ---- drivers/media/video/cx231xx/cx231xx-conf-reg.h | 495 -- drivers/media/video/cx231xx/cx231xx-core.c | 1736 ----- drivers/media/video/cx231xx/cx231xx-dif.h | 3178 --------- drivers/media/video/cx231xx/cx231xx-dvb.c | 796 --- drivers/media/video/cx231xx/cx231xx-i2c.c | 532 -- drivers/media/video/cx231xx/cx231xx-input.c | 119 - drivers/media/video/cx231xx/cx231xx-pcb-cfg.c | 795 --- drivers/media/video/cx231xx/cx231xx-pcb-cfg.h | 231 - drivers/media/video/cx231xx/cx231xx-reg.h | 1564 ----- drivers/media/video/cx231xx/cx231xx-vbi.c | 710 -- drivers/media/video/cx231xx/cx231xx-vbi.h | 65 - drivers/media/video/cx231xx/cx231xx-video.c | 2670 -------- drivers/media/video/cx231xx/cx231xx.h | 1012 --- drivers/media/video/em28xx/Kconfig | 58 - drivers/media/video/em28xx/Makefile | 15 - drivers/media/video/em28xx/em28xx-audio.c | 751 --- drivers/media/video/em28xx/em28xx-cards.c | 3417 ---------- drivers/media/video/em28xx/em28xx-core.c | 1260 ---- drivers/media/video/em28xx/em28xx-dvb.c | 1197 ---- drivers/media/video/em28xx/em28xx-i2c.c | 568 -- drivers/media/video/em28xx/em28xx-input.c | 645 -- drivers/media/video/em28xx/em28xx-reg.h | 226 - drivers/media/video/em28xx/em28xx-vbi.c | 145 - drivers/media/video/em28xx/em28xx-video.c | 2624 -------- drivers/media/video/em28xx/em28xx.h | 809 --- drivers/media/video/gspca/Kconfig | 425 -- drivers/media/video/gspca/Makefile | 93 - drivers/media/video/gspca/autogain_functions.c | 178 - drivers/media/video/gspca/autogain_functions.h | 183 - drivers/media/video/gspca/benq.c | 289 - drivers/media/video/gspca/conex.c | 966 --- drivers/media/video/gspca/cpia1.c | 1905 ------ drivers/media/video/gspca/etoms.c | 799 --- drivers/media/video/gspca/finepix.c | 306 - drivers/media/video/gspca/gl860/Kconfig | 8 - drivers/media/video/gspca/gl860/Makefile | 10 - drivers/media/video/gspca/gl860/gl860-mi1320.c | 536 -- drivers/media/video/gspca/gl860/gl860-mi2020.c | 733 -- drivers/media/video/gspca/gl860/gl860-ov2640.c | 489 -- drivers/media/video/gspca/gl860/gl860-ov9655.c | 336 - drivers/media/video/gspca/gl860/gl860.c | 725 -- drivers/media/video/gspca/gl860/gl860.h | 105 - drivers/media/video/gspca/gspca.c | 2456 ------- drivers/media/video/gspca/gspca.h | 259 - drivers/media/video/gspca/jeilinj.c | 548 -- drivers/media/video/gspca/jl2005bcd.c | 557 -- drivers/media/video/gspca/jpeg.h | 168 - drivers/media/video/gspca/kinect.c | 408 -- drivers/media/video/gspca/konica.c | 487 -- drivers/media/video/gspca/m5602/Kconfig | 11 - drivers/media/video/gspca/m5602/Makefile | 11 - drivers/media/video/gspca/m5602/m5602_bridge.h | 163 - drivers/media/video/gspca/m5602/m5602_core.c | 424 -- drivers/media/video/gspca/m5602/m5602_mt9m111.c | 647 -- drivers/media/video/gspca/m5602/m5602_mt9m111.h | 271 - drivers/media/video/gspca/m5602/m5602_ov7660.c | 488 -- drivers/media/video/gspca/m5602/m5602_ov7660.h | 260 - drivers/media/video/gspca/m5602/m5602_ov9650.c | 881 --- drivers/media/video/gspca/m5602/m5602_ov9650.h | 307 - drivers/media/video/gspca/m5602/m5602_po1030.c | 763 --- drivers/media/video/gspca/m5602/m5602_po1030.h | 272 - drivers/media/video/gspca/m5602/m5602_s5k4aa.c | 726 -- drivers/media/video/gspca/m5602/m5602_s5k4aa.h | 283 - drivers/media/video/gspca/m5602/m5602_s5k83a.c | 605 -- drivers/media/video/gspca/m5602/m5602_s5k83a.h | 193 - drivers/media/video/gspca/m5602/m5602_sensor.h | 70 - drivers/media/video/gspca/mars.c | 439 -- drivers/media/video/gspca/mr97310a.c | 1091 --- drivers/media/video/gspca/nw80x.c | 2110 ------ drivers/media/video/gspca/ov519.c | 4991 -------------- drivers/media/video/gspca/ov534.c | 1544 ----- drivers/media/video/gspca/ov534_9.c | 1500 ----- drivers/media/video/gspca/pac207.c | 469 -- drivers/media/video/gspca/pac7302.c | 932 --- drivers/media/video/gspca/pac7311.c | 701 -- drivers/media/video/gspca/pac_common.h | 134 - drivers/media/video/gspca/se401.c | 739 -- drivers/media/video/gspca/se401.h | 90 - drivers/media/video/gspca/sn9c2028.c | 735 -- drivers/media/video/gspca/sn9c2028.h | 51 - drivers/media/video/gspca/sn9c20x.c | 2428 ------- drivers/media/video/gspca/sonixb.c | 1493 ----- drivers/media/video/gspca/sonixj.c | 3206 --------- drivers/media/video/gspca/spca1528.c | 444 -- drivers/media/video/gspca/spca500.c | 990 --- drivers/media/video/gspca/spca501.c | 2054 ------ drivers/media/video/gspca/spca505.c | 807 --- drivers/media/video/gspca/spca506.c | 612 -- drivers/media/video/gspca/spca508.c | 1540 ----- drivers/media/video/gspca/spca561.c | 936 --- drivers/media/video/gspca/sq905.c | 440 -- drivers/media/video/gspca/sq905c.c | 344 - drivers/media/video/gspca/sq930x.c | 1164 ---- drivers/media/video/gspca/stk014.c | 446 -- drivers/media/video/gspca/stv0680.c | 353 - drivers/media/video/gspca/stv06xx/Kconfig | 9 - drivers/media/video/gspca/stv06xx/Makefile | 10 - drivers/media/video/gspca/stv06xx/stv06xx.c | 634 -- drivers/media/video/gspca/stv06xx/stv06xx.h | 116 - drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c | 540 -- drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h | 206 - drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c | 434 -- drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h | 144 - drivers/media/video/gspca/stv06xx/stv06xx_sensor.h | 87 - drivers/media/video/gspca/stv06xx/stv06xx_st6422.c | 285 - drivers/media/video/gspca/stv06xx/stv06xx_st6422.h | 52 - drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c | 272 - drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h | 255 - drivers/media/video/gspca/sunplus.c | 1085 --- drivers/media/video/gspca/t613.c | 1054 --- drivers/media/video/gspca/topro.c | 4969 -------------- drivers/media/video/gspca/tv8532.c | 378 -- drivers/media/video/gspca/vc032x.c | 3850 ----------- drivers/media/video/gspca/vicam.c | 365 - drivers/media/video/gspca/w996Xcf.c | 567 -- drivers/media/video/gspca/xirlink_cit.c | 3145 --------- drivers/media/video/gspca/zc3xx-reg.h | 251 - drivers/media/video/gspca/zc3xx.c | 7024 -------------------- drivers/media/video/hdpvr/Kconfig | 10 - drivers/media/video/hdpvr/Makefile | 7 - drivers/media/video/hdpvr/hdpvr-control.c | 200 - drivers/media/video/hdpvr/hdpvr-core.c | 472 -- drivers/media/video/hdpvr/hdpvr-i2c.c | 231 - drivers/media/video/hdpvr/hdpvr-video.c | 1289 ---- drivers/media/video/hdpvr/hdpvr.h | 317 - drivers/media/video/pvrusb2/Kconfig | 65 - drivers/media/video/pvrusb2/Makefile | 22 - drivers/media/video/pvrusb2/pvrusb2-audio.c | 96 - drivers/media/video/pvrusb2/pvrusb2-audio.h | 37 - drivers/media/video/pvrusb2/pvrusb2-context.c | 431 -- drivers/media/video/pvrusb2/pvrusb2-context.h | 94 - drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c | 95 - drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h | 48 - drivers/media/video/pvrusb2/pvrusb2-ctrl.c | 609 -- drivers/media/video/pvrusb2/pvrusb2-ctrl.h | 122 - drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 166 - drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h | 52 - drivers/media/video/pvrusb2/pvrusb2-debug.h | 69 - drivers/media/video/pvrusb2/pvrusb2-debugifc.c | 335 - drivers/media/video/pvrusb2/pvrusb2-debugifc.h | 52 - drivers/media/video/pvrusb2/pvrusb2-devattr.c | 576 -- drivers/media/video/pvrusb2/pvrusb2-devattr.h | 199 - drivers/media/video/pvrusb2/pvrusb2-dvb.c | 435 -- drivers/media/video/pvrusb2/pvrusb2-dvb.h | 41 - drivers/media/video/pvrusb2/pvrusb2-eeprom.c | 164 - drivers/media/video/pvrusb2/pvrusb2-eeprom.h | 39 - drivers/media/video/pvrusb2/pvrusb2-encoder.c | 552 -- drivers/media/video/pvrusb2/pvrusb2-encoder.h | 42 - drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h | 72 - drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 406 -- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 5202 --------------- drivers/media/video/pvrusb2/pvrusb2-hdw.h | 360 - drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 698 -- drivers/media/video/pvrusb2/pvrusb2-i2c-core.h | 40 - drivers/media/video/pvrusb2/pvrusb2-io.c | 695 -- drivers/media/video/pvrusb2/pvrusb2-io.h | 102 - drivers/media/video/pvrusb2/pvrusb2-ioread.c | 512 -- drivers/media/video/pvrusb2/pvrusb2-ioread.h | 48 - drivers/media/video/pvrusb2/pvrusb2-main.c | 182 - drivers/media/video/pvrusb2/pvrusb2-std.c | 411 -- drivers/media/video/pvrusb2/pvrusb2-std.h | 59 - drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 861 --- drivers/media/video/pvrusb2/pvrusb2-sysfs.h | 46 - drivers/media/video/pvrusb2/pvrusb2-util.h | 62 - drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 1413 ---- drivers/media/video/pvrusb2/pvrusb2-v4l2.h | 39 - drivers/media/video/pvrusb2/pvrusb2-video-v4l.c | 114 - drivers/media/video/pvrusb2/pvrusb2-video-v4l.h | 48 - drivers/media/video/pvrusb2/pvrusb2-wm8775.c | 70 - drivers/media/video/pvrusb2/pvrusb2-wm8775.h | 52 - drivers/media/video/pvrusb2/pvrusb2.h | 42 - drivers/media/video/pwc/Kconfig | 48 - drivers/media/video/pwc/Makefile | 4 - drivers/media/video/pwc/philips.txt | 236 - drivers/media/video/pwc/pwc-ctrl.c | 553 -- drivers/media/video/pwc/pwc-dec1.c | 32 - drivers/media/video/pwc/pwc-dec1.h | 39 - drivers/media/video/pwc/pwc-dec23.c | 691 -- drivers/media/video/pwc/pwc-dec23.h | 61 - drivers/media/video/pwc/pwc-if.c | 1165 ---- drivers/media/video/pwc/pwc-kiara.c | 892 --- drivers/media/video/pwc/pwc-kiara.h | 48 - drivers/media/video/pwc/pwc-misc.c | 93 - drivers/media/video/pwc/pwc-nala.h | 66 - drivers/media/video/pwc/pwc-timon.c | 1448 ---- drivers/media/video/pwc/pwc-timon.h | 63 - drivers/media/video/pwc/pwc-uncompress.c | 107 - drivers/media/video/pwc/pwc-v4l.c | 1053 --- drivers/media/video/pwc/pwc.h | 393 -- drivers/media/video/sn9c102/Kconfig | 14 - drivers/media/video/sn9c102/Makefile | 15 - drivers/media/video/sn9c102/sn9c102.h | 211 - drivers/media/video/sn9c102/sn9c102_config.h | 86 - drivers/media/video/sn9c102/sn9c102_core.c | 3421 ---------- drivers/media/video/sn9c102/sn9c102_devtable.h | 147 - drivers/media/video/sn9c102/sn9c102_hv7131d.c | 264 - drivers/media/video/sn9c102/sn9c102_hv7131r.c | 363 - drivers/media/video/sn9c102/sn9c102_mi0343.c | 352 - drivers/media/video/sn9c102/sn9c102_mi0360.c | 453 -- drivers/media/video/sn9c102/sn9c102_mt9v111.c | 260 - drivers/media/video/sn9c102/sn9c102_ov7630.c | 626 -- drivers/media/video/sn9c102/sn9c102_ov7660.c | 538 -- drivers/media/video/sn9c102/sn9c102_pas106b.c | 302 - drivers/media/video/sn9c102/sn9c102_pas202bcb.c | 335 - drivers/media/video/sn9c102/sn9c102_sensor.h | 307 - drivers/media/video/sn9c102/sn9c102_tas5110c1b.c | 154 - drivers/media/video/sn9c102/sn9c102_tas5110d.c | 119 - drivers/media/video/sn9c102/sn9c102_tas5130d1b.c | 165 - drivers/media/video/stk1160/Kconfig | 20 - drivers/media/video/stk1160/Makefile | 11 - drivers/media/video/stk1160/stk1160-ac97.c | 153 - drivers/media/video/stk1160/stk1160-core.c | 432 -- drivers/media/video/stk1160/stk1160-i2c.c | 294 - drivers/media/video/stk1160/stk1160-reg.h | 93 - drivers/media/video/stk1160/stk1160-v4l.c | 738 -- drivers/media/video/stk1160/stk1160-video.c | 518 -- drivers/media/video/stk1160/stk1160.h | 208 - drivers/media/video/tlg2300/Kconfig | 16 - drivers/media/video/tlg2300/Makefile | 9 - drivers/media/video/tlg2300/pd-alsa.c | 332 - drivers/media/video/tlg2300/pd-common.h | 281 - drivers/media/video/tlg2300/pd-dvb.c | 596 -- drivers/media/video/tlg2300/pd-main.c | 536 -- drivers/media/video/tlg2300/pd-radio.c | 421 -- drivers/media/video/tlg2300/pd-video.c | 1668 ----- drivers/media/video/tlg2300/vendorcmds.h | 243 - drivers/media/video/tm6000/Kconfig | 33 - drivers/media/video/tm6000/Makefile | 15 - drivers/media/video/tm6000/tm6000-alsa.c | 528 -- drivers/media/video/tm6000/tm6000-cards.c | 1413 ---- drivers/media/video/tm6000/tm6000-core.c | 935 --- drivers/media/video/tm6000/tm6000-dvb.c | 467 -- drivers/media/video/tm6000/tm6000-i2c.c | 334 - drivers/media/video/tm6000/tm6000-input.c | 498 -- drivers/media/video/tm6000/tm6000-regs.h | 600 -- drivers/media/video/tm6000/tm6000-stds.c | 638 -- drivers/media/video/tm6000/tm6000-usb-isoc.h | 50 - drivers/media/video/tm6000/tm6000-video.c | 1852 ------ drivers/media/video/tm6000/tm6000.h | 399 -- drivers/media/video/usbvision/Kconfig | 12 - drivers/media/video/usbvision/Makefile | 6 - drivers/media/video/usbvision/usbvision-cards.c | 1133 ---- drivers/media/video/usbvision/usbvision-cards.h | 69 - drivers/media/video/usbvision/usbvision-core.c | 2518 ------- drivers/media/video/usbvision/usbvision-i2c.c | 456 -- drivers/media/video/usbvision/usbvision-video.c | 1720 ----- drivers/media/video/usbvision/usbvision.h | 535 -- drivers/media/video/uvc/Kconfig | 19 - drivers/media/video/uvc/Makefile | 6 - drivers/media/video/uvc/uvc_ctrl.c | 2165 ------ drivers/media/video/uvc/uvc_debugfs.c | 136 - drivers/media/video/uvc/uvc_driver.c | 2472 ------- drivers/media/video/uvc/uvc_entity.c | 126 - drivers/media/video/uvc/uvc_isight.c | 137 - drivers/media/video/uvc/uvc_queue.c | 359 - drivers/media/video/uvc/uvc_status.c | 237 - drivers/media/video/uvc/uvc_v4l2.c | 1317 ---- drivers/media/video/uvc/uvc_video.c | 1879 ------ drivers/media/video/uvc/uvcvideo.h | 718 -- 571 files changed, 184646 insertions(+), 184658 deletions(-) create mode 100644 drivers/media/usb/au0828/Kconfig create mode 100644 drivers/media/usb/au0828/Makefile create mode 100644 drivers/media/usb/au0828/au0828-cards.c create mode 100644 drivers/media/usb/au0828/au0828-cards.h create mode 100644 drivers/media/usb/au0828/au0828-core.c create mode 100644 drivers/media/usb/au0828/au0828-dvb.c create mode 100644 drivers/media/usb/au0828/au0828-i2c.c create mode 100644 drivers/media/usb/au0828/au0828-reg.h create mode 100644 drivers/media/usb/au0828/au0828-vbi.c create mode 100644 drivers/media/usb/au0828/au0828-video.c create mode 100644 drivers/media/usb/au0828/au0828.h create mode 100644 drivers/media/usb/cpia2/Kconfig create mode 100644 drivers/media/usb/cpia2/Makefile create mode 100644 drivers/media/usb/cpia2/cpia2.h create mode 100644 drivers/media/usb/cpia2/cpia2_core.c create mode 100644 drivers/media/usb/cpia2/cpia2_registers.h create mode 100644 drivers/media/usb/cpia2/cpia2_usb.c create mode 100644 drivers/media/usb/cpia2/cpia2_v4l.c create mode 100644 drivers/media/usb/cx231xx/Kconfig create mode 100644 drivers/media/usb/cx231xx/Makefile create mode 100644 drivers/media/usb/cx231xx/cx231xx-417.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-audio.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-avcore.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-cards.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-conf-reg.h create mode 100644 drivers/media/usb/cx231xx/cx231xx-core.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-dif.h create mode 100644 drivers/media/usb/cx231xx/cx231xx-dvb.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-i2c.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-input.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h create mode 100644 drivers/media/usb/cx231xx/cx231xx-reg.h create mode 100644 drivers/media/usb/cx231xx/cx231xx-vbi.c create mode 100644 drivers/media/usb/cx231xx/cx231xx-vbi.h create mode 100644 drivers/media/usb/cx231xx/cx231xx-video.c create mode 100644 drivers/media/usb/cx231xx/cx231xx.h create mode 100644 drivers/media/usb/em28xx/Kconfig create mode 100644 drivers/media/usb/em28xx/Makefile create mode 100644 drivers/media/usb/em28xx/em28xx-audio.c create mode 100644 drivers/media/usb/em28xx/em28xx-cards.c create mode 100644 drivers/media/usb/em28xx/em28xx-core.c create mode 100644 drivers/media/usb/em28xx/em28xx-dvb.c create mode 100644 drivers/media/usb/em28xx/em28xx-i2c.c create mode 100644 drivers/media/usb/em28xx/em28xx-input.c create mode 100644 drivers/media/usb/em28xx/em28xx-reg.h create mode 100644 drivers/media/usb/em28xx/em28xx-vbi.c create mode 100644 drivers/media/usb/em28xx/em28xx-video.c create mode 100644 drivers/media/usb/em28xx/em28xx.h create mode 100644 drivers/media/usb/gspca/Kconfig create mode 100644 drivers/media/usb/gspca/Makefile create mode 100644 drivers/media/usb/gspca/autogain_functions.c create mode 100644 drivers/media/usb/gspca/autogain_functions.h create mode 100644 drivers/media/usb/gspca/benq.c create mode 100644 drivers/media/usb/gspca/conex.c create mode 100644 drivers/media/usb/gspca/cpia1.c create mode 100644 drivers/media/usb/gspca/etoms.c create mode 100644 drivers/media/usb/gspca/finepix.c create mode 100644 drivers/media/usb/gspca/gl860/Kconfig create mode 100644 drivers/media/usb/gspca/gl860/Makefile create mode 100644 drivers/media/usb/gspca/gl860/gl860-mi1320.c create mode 100644 drivers/media/usb/gspca/gl860/gl860-mi2020.c create mode 100644 drivers/media/usb/gspca/gl860/gl860-ov2640.c create mode 100644 drivers/media/usb/gspca/gl860/gl860-ov9655.c create mode 100644 drivers/media/usb/gspca/gl860/gl860.c create mode 100644 drivers/media/usb/gspca/gl860/gl860.h create mode 100644 drivers/media/usb/gspca/gspca.c create mode 100644 drivers/media/usb/gspca/gspca.h create mode 100644 drivers/media/usb/gspca/jeilinj.c create mode 100644 drivers/media/usb/gspca/jl2005bcd.c create mode 100644 drivers/media/usb/gspca/jpeg.h create mode 100644 drivers/media/usb/gspca/kinect.c create mode 100644 drivers/media/usb/gspca/konica.c create mode 100644 drivers/media/usb/gspca/m5602/Kconfig create mode 100644 drivers/media/usb/gspca/m5602/Makefile create mode 100644 drivers/media/usb/gspca/m5602/m5602_bridge.h create mode 100644 drivers/media/usb/gspca/m5602/m5602_core.c create mode 100644 drivers/media/usb/gspca/m5602/m5602_mt9m111.c create mode 100644 drivers/media/usb/gspca/m5602/m5602_mt9m111.h create mode 100644 drivers/media/usb/gspca/m5602/m5602_ov7660.c create mode 100644 drivers/media/usb/gspca/m5602/m5602_ov7660.h create mode 100644 drivers/media/usb/gspca/m5602/m5602_ov9650.c create mode 100644 drivers/media/usb/gspca/m5602/m5602_ov9650.h create mode 100644 drivers/media/usb/gspca/m5602/m5602_po1030.c create mode 100644 drivers/media/usb/gspca/m5602/m5602_po1030.h create mode 100644 drivers/media/usb/gspca/m5602/m5602_s5k4aa.c create mode 100644 drivers/media/usb/gspca/m5602/m5602_s5k4aa.h create mode 100644 drivers/media/usb/gspca/m5602/m5602_s5k83a.c create mode 100644 drivers/media/usb/gspca/m5602/m5602_s5k83a.h create mode 100644 drivers/media/usb/gspca/m5602/m5602_sensor.h create mode 100644 drivers/media/usb/gspca/mars.c create mode 100644 drivers/media/usb/gspca/mr97310a.c create mode 100644 drivers/media/usb/gspca/nw80x.c create mode 100644 drivers/media/usb/gspca/ov519.c create mode 100644 drivers/media/usb/gspca/ov534.c create mode 100644 drivers/media/usb/gspca/ov534_9.c create mode 100644 drivers/media/usb/gspca/pac207.c create mode 100644 drivers/media/usb/gspca/pac7302.c create mode 100644 drivers/media/usb/gspca/pac7311.c create mode 100644 drivers/media/usb/gspca/pac_common.h create mode 100644 drivers/media/usb/gspca/se401.c create mode 100644 drivers/media/usb/gspca/se401.h create mode 100644 drivers/media/usb/gspca/sn9c2028.c create mode 100644 drivers/media/usb/gspca/sn9c2028.h create mode 100644 drivers/media/usb/gspca/sn9c20x.c create mode 100644 drivers/media/usb/gspca/sonixb.c create mode 100644 drivers/media/usb/gspca/sonixj.c create mode 100644 drivers/media/usb/gspca/spca1528.c create mode 100644 drivers/media/usb/gspca/spca500.c create mode 100644 drivers/media/usb/gspca/spca501.c create mode 100644 drivers/media/usb/gspca/spca505.c create mode 100644 drivers/media/usb/gspca/spca506.c create mode 100644 drivers/media/usb/gspca/spca508.c create mode 100644 drivers/media/usb/gspca/spca561.c create mode 100644 drivers/media/usb/gspca/sq905.c create mode 100644 drivers/media/usb/gspca/sq905c.c create mode 100644 drivers/media/usb/gspca/sq930x.c create mode 100644 drivers/media/usb/gspca/stk014.c create mode 100644 drivers/media/usb/gspca/stv0680.c create mode 100644 drivers/media/usb/gspca/stv06xx/Kconfig create mode 100644 drivers/media/usb/gspca/stv06xx/Makefile create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx.c create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx.h create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c create mode 100644 drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h create mode 100644 drivers/media/usb/gspca/sunplus.c create mode 100644 drivers/media/usb/gspca/t613.c create mode 100644 drivers/media/usb/gspca/topro.c create mode 100644 drivers/media/usb/gspca/tv8532.c create mode 100644 drivers/media/usb/gspca/vc032x.c create mode 100644 drivers/media/usb/gspca/vicam.c create mode 100644 drivers/media/usb/gspca/w996Xcf.c create mode 100644 drivers/media/usb/gspca/xirlink_cit.c create mode 100644 drivers/media/usb/gspca/zc3xx-reg.h create mode 100644 drivers/media/usb/gspca/zc3xx.c create mode 100644 drivers/media/usb/hdpvr/Kconfig create mode 100644 drivers/media/usb/hdpvr/Makefile create mode 100644 drivers/media/usb/hdpvr/hdpvr-control.c create mode 100644 drivers/media/usb/hdpvr/hdpvr-core.c create mode 100644 drivers/media/usb/hdpvr/hdpvr-i2c.c create mode 100644 drivers/media/usb/hdpvr/hdpvr-video.c create mode 100644 drivers/media/usb/hdpvr/hdpvr.h create mode 100644 drivers/media/usb/pvrusb2/Kconfig create mode 100644 drivers/media/usb/pvrusb2/Makefile create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-audio.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-audio.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-context.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-context.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-ctrl.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-ctrl.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-debug.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-debugifc.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-debugifc.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-devattr.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-devattr.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-dvb.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-dvb.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-eeprom.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-eeprom.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-encoder.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-encoder.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-hdw.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-hdw.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-io.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-io.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-ioread.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-ioread.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-main.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-std.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-std.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-sysfs.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-sysfs.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-util.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-v4l2.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-v4l2.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-wm8775.c create mode 100644 drivers/media/usb/pvrusb2/pvrusb2-wm8775.h create mode 100644 drivers/media/usb/pvrusb2/pvrusb2.h create mode 100644 drivers/media/usb/pwc/Kconfig create mode 100644 drivers/media/usb/pwc/Makefile create mode 100644 drivers/media/usb/pwc/philips.txt create mode 100644 drivers/media/usb/pwc/pwc-ctrl.c create mode 100644 drivers/media/usb/pwc/pwc-dec1.c create mode 100644 drivers/media/usb/pwc/pwc-dec1.h create mode 100644 drivers/media/usb/pwc/pwc-dec23.c create mode 100644 drivers/media/usb/pwc/pwc-dec23.h create mode 100644 drivers/media/usb/pwc/pwc-if.c create mode 100644 drivers/media/usb/pwc/pwc-kiara.c create mode 100644 drivers/media/usb/pwc/pwc-kiara.h create mode 100644 drivers/media/usb/pwc/pwc-misc.c create mode 100644 drivers/media/usb/pwc/pwc-nala.h create mode 100644 drivers/media/usb/pwc/pwc-timon.c create mode 100644 drivers/media/usb/pwc/pwc-timon.h create mode 100644 drivers/media/usb/pwc/pwc-uncompress.c create mode 100644 drivers/media/usb/pwc/pwc-v4l.c create mode 100644 drivers/media/usb/pwc/pwc.h create mode 100644 drivers/media/usb/sn9c102/Kconfig create mode 100644 drivers/media/usb/sn9c102/Makefile create mode 100644 drivers/media/usb/sn9c102/sn9c102.h create mode 100644 drivers/media/usb/sn9c102/sn9c102_config.h create mode 100644 drivers/media/usb/sn9c102/sn9c102_core.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_devtable.h create mode 100644 drivers/media/usb/sn9c102/sn9c102_hv7131d.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_hv7131r.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_mi0343.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_mi0360.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_mt9v111.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_ov7630.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_ov7660.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_pas106b.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_pas202bcb.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_sensor.h create mode 100644 drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_tas5110d.c create mode 100644 drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c create mode 100644 drivers/media/usb/stk1160/Kconfig create mode 100644 drivers/media/usb/stk1160/Makefile create mode 100644 drivers/media/usb/stk1160/stk1160-ac97.c create mode 100644 drivers/media/usb/stk1160/stk1160-core.c create mode 100644 drivers/media/usb/stk1160/stk1160-i2c.c create mode 100644 drivers/media/usb/stk1160/stk1160-reg.h create mode 100644 drivers/media/usb/stk1160/stk1160-v4l.c create mode 100644 drivers/media/usb/stk1160/stk1160-video.c create mode 100644 drivers/media/usb/stk1160/stk1160.h create mode 100644 drivers/media/usb/tlg2300/Kconfig create mode 100644 drivers/media/usb/tlg2300/Makefile create mode 100644 drivers/media/usb/tlg2300/pd-alsa.c create mode 100644 drivers/media/usb/tlg2300/pd-common.h create mode 100644 drivers/media/usb/tlg2300/pd-dvb.c create mode 100644 drivers/media/usb/tlg2300/pd-main.c create mode 100644 drivers/media/usb/tlg2300/pd-radio.c create mode 100644 drivers/media/usb/tlg2300/pd-video.c create mode 100644 drivers/media/usb/tlg2300/vendorcmds.h create mode 100644 drivers/media/usb/tm6000/Kconfig create mode 100644 drivers/media/usb/tm6000/Makefile create mode 100644 drivers/media/usb/tm6000/tm6000-alsa.c create mode 100644 drivers/media/usb/tm6000/tm6000-cards.c create mode 100644 drivers/media/usb/tm6000/tm6000-core.c create mode 100644 drivers/media/usb/tm6000/tm6000-dvb.c create mode 100644 drivers/media/usb/tm6000/tm6000-i2c.c create mode 100644 drivers/media/usb/tm6000/tm6000-input.c create mode 100644 drivers/media/usb/tm6000/tm6000-regs.h create mode 100644 drivers/media/usb/tm6000/tm6000-stds.c create mode 100644 drivers/media/usb/tm6000/tm6000-usb-isoc.h create mode 100644 drivers/media/usb/tm6000/tm6000-video.c create mode 100644 drivers/media/usb/tm6000/tm6000.h create mode 100644 drivers/media/usb/usbvision/Kconfig create mode 100644 drivers/media/usb/usbvision/Makefile create mode 100644 drivers/media/usb/usbvision/usbvision-cards.c create mode 100644 drivers/media/usb/usbvision/usbvision-cards.h create mode 100644 drivers/media/usb/usbvision/usbvision-core.c create mode 100644 drivers/media/usb/usbvision/usbvision-i2c.c create mode 100644 drivers/media/usb/usbvision/usbvision-video.c create mode 100644 drivers/media/usb/usbvision/usbvision.h create mode 100644 drivers/media/usb/uvc/Kconfig create mode 100644 drivers/media/usb/uvc/Makefile create mode 100644 drivers/media/usb/uvc/uvc_ctrl.c create mode 100644 drivers/media/usb/uvc/uvc_debugfs.c create mode 100644 drivers/media/usb/uvc/uvc_driver.c create mode 100644 drivers/media/usb/uvc/uvc_entity.c create mode 100644 drivers/media/usb/uvc/uvc_isight.c create mode 100644 drivers/media/usb/uvc/uvc_queue.c create mode 100644 drivers/media/usb/uvc/uvc_status.c create mode 100644 drivers/media/usb/uvc/uvc_v4l2.c create mode 100644 drivers/media/usb/uvc/uvc_video.c create mode 100644 drivers/media/usb/uvc/uvcvideo.h delete mode 100644 drivers/media/video/au0828/Kconfig delete mode 100644 drivers/media/video/au0828/Makefile delete mode 100644 drivers/media/video/au0828/au0828-cards.c delete mode 100644 drivers/media/video/au0828/au0828-cards.h delete mode 100644 drivers/media/video/au0828/au0828-core.c delete mode 100644 drivers/media/video/au0828/au0828-dvb.c delete mode 100644 drivers/media/video/au0828/au0828-i2c.c delete mode 100644 drivers/media/video/au0828/au0828-reg.h delete mode 100644 drivers/media/video/au0828/au0828-vbi.c delete mode 100644 drivers/media/video/au0828/au0828-video.c delete mode 100644 drivers/media/video/au0828/au0828.h delete mode 100644 drivers/media/video/cpia2/Kconfig delete mode 100644 drivers/media/video/cpia2/Makefile delete mode 100644 drivers/media/video/cpia2/cpia2.h delete mode 100644 drivers/media/video/cpia2/cpia2_core.c delete mode 100644 drivers/media/video/cpia2/cpia2_registers.h delete mode 100644 drivers/media/video/cpia2/cpia2_usb.c delete mode 100644 drivers/media/video/cpia2/cpia2_v4l.c delete mode 100644 drivers/media/video/cx231xx/Kconfig delete mode 100644 drivers/media/video/cx231xx/Makefile delete mode 100644 drivers/media/video/cx231xx/cx231xx-417.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-audio.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-avcore.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-cards.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-conf-reg.h delete mode 100644 drivers/media/video/cx231xx/cx231xx-core.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-dif.h delete mode 100644 drivers/media/video/cx231xx/cx231xx-dvb.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-i2c.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-input.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-pcb-cfg.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-pcb-cfg.h delete mode 100644 drivers/media/video/cx231xx/cx231xx-reg.h delete mode 100644 drivers/media/video/cx231xx/cx231xx-vbi.c delete mode 100644 drivers/media/video/cx231xx/cx231xx-vbi.h delete mode 100644 drivers/media/video/cx231xx/cx231xx-video.c delete mode 100644 drivers/media/video/cx231xx/cx231xx.h delete mode 100644 drivers/media/video/em28xx/Kconfig delete mode 100644 drivers/media/video/em28xx/Makefile delete mode 100644 drivers/media/video/em28xx/em28xx-audio.c delete mode 100644 drivers/media/video/em28xx/em28xx-cards.c delete mode 100644 drivers/media/video/em28xx/em28xx-core.c delete mode 100644 drivers/media/video/em28xx/em28xx-dvb.c delete mode 100644 drivers/media/video/em28xx/em28xx-i2c.c delete mode 100644 drivers/media/video/em28xx/em28xx-input.c delete mode 100644 drivers/media/video/em28xx/em28xx-reg.h delete mode 100644 drivers/media/video/em28xx/em28xx-vbi.c delete mode 100644 drivers/media/video/em28xx/em28xx-video.c delete mode 100644 drivers/media/video/em28xx/em28xx.h delete mode 100644 drivers/media/video/gspca/Kconfig delete mode 100644 drivers/media/video/gspca/Makefile delete mode 100644 drivers/media/video/gspca/autogain_functions.c delete mode 100644 drivers/media/video/gspca/autogain_functions.h delete mode 100644 drivers/media/video/gspca/benq.c delete mode 100644 drivers/media/video/gspca/conex.c delete mode 100644 drivers/media/video/gspca/cpia1.c delete mode 100644 drivers/media/video/gspca/etoms.c delete mode 100644 drivers/media/video/gspca/finepix.c delete mode 100644 drivers/media/video/gspca/gl860/Kconfig delete mode 100644 drivers/media/video/gspca/gl860/Makefile delete mode 100644 drivers/media/video/gspca/gl860/gl860-mi1320.c delete mode 100644 drivers/media/video/gspca/gl860/gl860-mi2020.c delete mode 100644 drivers/media/video/gspca/gl860/gl860-ov2640.c delete mode 100644 drivers/media/video/gspca/gl860/gl860-ov9655.c delete mode 100644 drivers/media/video/gspca/gl860/gl860.c delete mode 100644 drivers/media/video/gspca/gl860/gl860.h delete mode 100644 drivers/media/video/gspca/gspca.c delete mode 100644 drivers/media/video/gspca/gspca.h delete mode 100644 drivers/media/video/gspca/jeilinj.c delete mode 100644 drivers/media/video/gspca/jl2005bcd.c delete mode 100644 drivers/media/video/gspca/jpeg.h delete mode 100644 drivers/media/video/gspca/kinect.c delete mode 100644 drivers/media/video/gspca/konica.c delete mode 100644 drivers/media/video/gspca/m5602/Kconfig delete mode 100644 drivers/media/video/gspca/m5602/Makefile delete mode 100644 drivers/media/video/gspca/m5602/m5602_bridge.h delete mode 100644 drivers/media/video/gspca/m5602/m5602_core.c delete mode 100644 drivers/media/video/gspca/m5602/m5602_mt9m111.c delete mode 100644 drivers/media/video/gspca/m5602/m5602_mt9m111.h delete mode 100644 drivers/media/video/gspca/m5602/m5602_ov7660.c delete mode 100644 drivers/media/video/gspca/m5602/m5602_ov7660.h delete mode 100644 drivers/media/video/gspca/m5602/m5602_ov9650.c delete mode 100644 drivers/media/video/gspca/m5602/m5602_ov9650.h delete mode 100644 drivers/media/video/gspca/m5602/m5602_po1030.c delete mode 100644 drivers/media/video/gspca/m5602/m5602_po1030.h delete mode 100644 drivers/media/video/gspca/m5602/m5602_s5k4aa.c delete mode 100644 drivers/media/video/gspca/m5602/m5602_s5k4aa.h delete mode 100644 drivers/media/video/gspca/m5602/m5602_s5k83a.c delete mode 100644 drivers/media/video/gspca/m5602/m5602_s5k83a.h delete mode 100644 drivers/media/video/gspca/m5602/m5602_sensor.h delete mode 100644 drivers/media/video/gspca/mars.c delete mode 100644 drivers/media/video/gspca/mr97310a.c delete mode 100644 drivers/media/video/gspca/nw80x.c delete mode 100644 drivers/media/video/gspca/ov519.c delete mode 100644 drivers/media/video/gspca/ov534.c delete mode 100644 drivers/media/video/gspca/ov534_9.c delete mode 100644 drivers/media/video/gspca/pac207.c delete mode 100644 drivers/media/video/gspca/pac7302.c delete mode 100644 drivers/media/video/gspca/pac7311.c delete mode 100644 drivers/media/video/gspca/pac_common.h delete mode 100644 drivers/media/video/gspca/se401.c delete mode 100644 drivers/media/video/gspca/se401.h delete mode 100644 drivers/media/video/gspca/sn9c2028.c delete mode 100644 drivers/media/video/gspca/sn9c2028.h delete mode 100644 drivers/media/video/gspca/sn9c20x.c delete mode 100644 drivers/media/video/gspca/sonixb.c delete mode 100644 drivers/media/video/gspca/sonixj.c delete mode 100644 drivers/media/video/gspca/spca1528.c delete mode 100644 drivers/media/video/gspca/spca500.c delete mode 100644 drivers/media/video/gspca/spca501.c delete mode 100644 drivers/media/video/gspca/spca505.c delete mode 100644 drivers/media/video/gspca/spca506.c delete mode 100644 drivers/media/video/gspca/spca508.c delete mode 100644 drivers/media/video/gspca/spca561.c delete mode 100644 drivers/media/video/gspca/sq905.c delete mode 100644 drivers/media/video/gspca/sq905c.c delete mode 100644 drivers/media/video/gspca/sq930x.c delete mode 100644 drivers/media/video/gspca/stk014.c delete mode 100644 drivers/media/video/gspca/stv0680.c delete mode 100644 drivers/media/video/gspca/stv06xx/Kconfig delete mode 100644 drivers/media/video/gspca/stv06xx/Makefile delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx.c delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx.h delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_sensor.h delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_st6422.c delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_st6422.h delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c delete mode 100644 drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h delete mode 100644 drivers/media/video/gspca/sunplus.c delete mode 100644 drivers/media/video/gspca/t613.c delete mode 100644 drivers/media/video/gspca/topro.c delete mode 100644 drivers/media/video/gspca/tv8532.c delete mode 100644 drivers/media/video/gspca/vc032x.c delete mode 100644 drivers/media/video/gspca/vicam.c delete mode 100644 drivers/media/video/gspca/w996Xcf.c delete mode 100644 drivers/media/video/gspca/xirlink_cit.c delete mode 100644 drivers/media/video/gspca/zc3xx-reg.h delete mode 100644 drivers/media/video/gspca/zc3xx.c delete mode 100644 drivers/media/video/hdpvr/Kconfig delete mode 100644 drivers/media/video/hdpvr/Makefile delete mode 100644 drivers/media/video/hdpvr/hdpvr-control.c delete mode 100644 drivers/media/video/hdpvr/hdpvr-core.c delete mode 100644 drivers/media/video/hdpvr/hdpvr-i2c.c delete mode 100644 drivers/media/video/hdpvr/hdpvr-video.c delete mode 100644 drivers/media/video/hdpvr/hdpvr.h delete mode 100644 drivers/media/video/pvrusb2/Kconfig delete mode 100644 drivers/media/video/pvrusb2/Makefile delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-audio.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-audio.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-context.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-context.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-ctrl.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-ctrl.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-debug.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-debugifc.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-debugifc.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-devattr.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-devattr.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-dvb.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-dvb.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-eeprom.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-eeprom.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-encoder.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-encoder.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-hdw.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-hdw.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-i2c-core.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-i2c-core.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-io.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-io.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-ioread.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-ioread.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-main.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-std.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-std.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-sysfs.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-sysfs.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-util.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-v4l2.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-v4l2.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-video-v4l.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-video-v4l.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-wm8775.c delete mode 100644 drivers/media/video/pvrusb2/pvrusb2-wm8775.h delete mode 100644 drivers/media/video/pvrusb2/pvrusb2.h delete mode 100644 drivers/media/video/pwc/Kconfig delete mode 100644 drivers/media/video/pwc/Makefile delete mode 100644 drivers/media/video/pwc/philips.txt delete mode 100644 drivers/media/video/pwc/pwc-ctrl.c delete mode 100644 drivers/media/video/pwc/pwc-dec1.c delete mode 100644 drivers/media/video/pwc/pwc-dec1.h delete mode 100644 drivers/media/video/pwc/pwc-dec23.c delete mode 100644 drivers/media/video/pwc/pwc-dec23.h delete mode 100644 drivers/media/video/pwc/pwc-if.c delete mode 100644 drivers/media/video/pwc/pwc-kiara.c delete mode 100644 drivers/media/video/pwc/pwc-kiara.h delete mode 100644 drivers/media/video/pwc/pwc-misc.c delete mode 100644 drivers/media/video/pwc/pwc-nala.h delete mode 100644 drivers/media/video/pwc/pwc-timon.c delete mode 100644 drivers/media/video/pwc/pwc-timon.h delete mode 100644 drivers/media/video/pwc/pwc-uncompress.c delete mode 100644 drivers/media/video/pwc/pwc-v4l.c delete mode 100644 drivers/media/video/pwc/pwc.h delete mode 100644 drivers/media/video/sn9c102/Kconfig delete mode 100644 drivers/media/video/sn9c102/Makefile delete mode 100644 drivers/media/video/sn9c102/sn9c102.h delete mode 100644 drivers/media/video/sn9c102/sn9c102_config.h delete mode 100644 drivers/media/video/sn9c102/sn9c102_core.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_devtable.h delete mode 100644 drivers/media/video/sn9c102/sn9c102_hv7131d.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_hv7131r.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_mi0343.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_mi0360.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_mt9v111.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_ov7630.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_ov7660.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_pas106b.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_pas202bcb.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_sensor.h delete mode 100644 drivers/media/video/sn9c102/sn9c102_tas5110c1b.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_tas5110d.c delete mode 100644 drivers/media/video/sn9c102/sn9c102_tas5130d1b.c delete mode 100644 drivers/media/video/stk1160/Kconfig delete mode 100644 drivers/media/video/stk1160/Makefile delete mode 100644 drivers/media/video/stk1160/stk1160-ac97.c delete mode 100644 drivers/media/video/stk1160/stk1160-core.c delete mode 100644 drivers/media/video/stk1160/stk1160-i2c.c delete mode 100644 drivers/media/video/stk1160/stk1160-reg.h delete mode 100644 drivers/media/video/stk1160/stk1160-v4l.c delete mode 100644 drivers/media/video/stk1160/stk1160-video.c delete mode 100644 drivers/media/video/stk1160/stk1160.h delete mode 100644 drivers/media/video/tlg2300/Kconfig delete mode 100644 drivers/media/video/tlg2300/Makefile delete mode 100644 drivers/media/video/tlg2300/pd-alsa.c delete mode 100644 drivers/media/video/tlg2300/pd-common.h delete mode 100644 drivers/media/video/tlg2300/pd-dvb.c delete mode 100644 drivers/media/video/tlg2300/pd-main.c delete mode 100644 drivers/media/video/tlg2300/pd-radio.c delete mode 100644 drivers/media/video/tlg2300/pd-video.c delete mode 100644 drivers/media/video/tlg2300/vendorcmds.h delete mode 100644 drivers/media/video/tm6000/Kconfig delete mode 100644 drivers/media/video/tm6000/Makefile delete mode 100644 drivers/media/video/tm6000/tm6000-alsa.c delete mode 100644 drivers/media/video/tm6000/tm6000-cards.c delete mode 100644 drivers/media/video/tm6000/tm6000-core.c delete mode 100644 drivers/media/video/tm6000/tm6000-dvb.c delete mode 100644 drivers/media/video/tm6000/tm6000-i2c.c delete mode 100644 drivers/media/video/tm6000/tm6000-input.c delete mode 100644 drivers/media/video/tm6000/tm6000-regs.h delete mode 100644 drivers/media/video/tm6000/tm6000-stds.c delete mode 100644 drivers/media/video/tm6000/tm6000-usb-isoc.h delete mode 100644 drivers/media/video/tm6000/tm6000-video.c delete mode 100644 drivers/media/video/tm6000/tm6000.h delete mode 100644 drivers/media/video/usbvision/Kconfig delete mode 100644 drivers/media/video/usbvision/Makefile delete mode 100644 drivers/media/video/usbvision/usbvision-cards.c delete mode 100644 drivers/media/video/usbvision/usbvision-cards.h delete mode 100644 drivers/media/video/usbvision/usbvision-core.c delete mode 100644 drivers/media/video/usbvision/usbvision-i2c.c delete mode 100644 drivers/media/video/usbvision/usbvision-video.c delete mode 100644 drivers/media/video/usbvision/usbvision.h delete mode 100644 drivers/media/video/uvc/Kconfig delete mode 100644 drivers/media/video/uvc/Makefile delete mode 100644 drivers/media/video/uvc/uvc_ctrl.c delete mode 100644 drivers/media/video/uvc/uvc_debugfs.c delete mode 100644 drivers/media/video/uvc/uvc_driver.c delete mode 100644 drivers/media/video/uvc/uvc_entity.c delete mode 100644 drivers/media/video/uvc/uvc_isight.c delete mode 100644 drivers/media/video/uvc/uvc_queue.c delete mode 100644 drivers/media/video/uvc/uvc_status.c delete mode 100644 drivers/media/video/uvc/uvc_v4l2.c delete mode 100644 drivers/media/video/uvc/uvc_video.c delete mode 100644 drivers/media/video/uvc/uvcvideo.h (limited to 'drivers/media/usb') diff --git a/MAINTAINERS b/MAINTAINERS index ceb5b55b2af8..13fd97f6466a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3119,49 +3119,49 @@ M: Frank Zago L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained -F: drivers/media/video/gspca/finepix.c +F: drivers/media/usb/gspca/finepix.c GSPCA GL860 SUBDRIVER M: Olivier Lorin L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained -F: drivers/media/video/gspca/gl860/ +F: drivers/media/usb/gspca/gl860/ GSPCA M5602 SUBDRIVER M: Erik Andren L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained -F: drivers/media/video/gspca/m5602/ +F: drivers/media/usb/gspca/m5602/ GSPCA PAC207 SONIXB SUBDRIVER M: Hans de Goede L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained -F: drivers/media/video/gspca/pac207.c +F: drivers/media/usb/gspca/pac207.c GSPCA SN9C20X SUBDRIVER M: Brian Johnson L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained -F: drivers/media/video/gspca/sn9c20x.c +F: drivers/media/usb/gspca/sn9c20x.c GSPCA T613 SUBDRIVER M: Leandro Costantino L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained -F: drivers/media/video/gspca/t613.c +F: drivers/media/usb/gspca/t613.c GSPCA USB WEBCAM DRIVER M: Hans de Goede L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained -F: drivers/media/video/gspca/ +F: drivers/media/usb/gspca/ HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER M: Frank Seidel @@ -5525,7 +5525,7 @@ W: http://www.isely.net/pvrusb2/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: Documentation/video4linux/README.pvrusb2 -F: drivers/media/video/pvrusb2/ +F: drivers/media/usb/pvrusb2/ PWM SUBSYSTEM M: Thierry Reding @@ -5956,7 +5956,7 @@ M: Huang Shijie M: Kang Yong M: Zhang Xiaobing S: Supported -F: drivers/media/video/tlg2300 +F: drivers/media/usb/tlg2300 SC1200 WDT DRIVER M: Zwane Mwaikambo @@ -7297,7 +7297,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.linux-projects.org S: Maintained F: Documentation/video4linux/sn9c102.txt -F: drivers/media/video/sn9c102/ +F: drivers/media/usb/sn9c102/ USB SUBSYSTEM M: Greg Kroah-Hartman @@ -7332,7 +7332,7 @@ L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.ideasonboard.org/uvc/ S: Maintained -F: drivers/media/video/uvc/ +F: drivers/media/usb/uvc/ USB W996[87]CF DRIVER M: Luca Risolia diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 53664b35af1c..a1e25ee9d671 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -2,18 +2,49 @@ # USB media device configuration # -menuconfig MEDIA_USB_DRIVERS - bool "Supported DVB USB Adapters" - depends on USB - default y +menu "Media USB Adapters" + visible if USB && MEDIA_SUPPORT -if MEDIA_USB_DRIVERS && DVB_CORE && I2C +if MEDIA_CAMERA_SUPPORT + comment "Webcam devices" +source "drivers/media/usb/uvc/Kconfig" +source "drivers/media/usb/gspca/Kconfig" +source "drivers/media/usb/pwc/Kconfig" +source "drivers/media/usb/cpia2/Kconfig" +source "drivers/media/usb/sn9c102/Kconfig" +endif + +if MEDIA_ANALOG_TV_SUPPORT + comment "Analog TV USB devices" +source "drivers/media/usb/au0828/Kconfig" +source "drivers/media/usb/pvrusb2/Kconfig" +source "drivers/media/usb/hdpvr/Kconfig" +source "drivers/media/usb/tlg2300/Kconfig" +source "drivers/media/usb/usbvision/Kconfig" +source "drivers/media/usb/stk1160/Kconfig" +endif + +if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) + comment "Analog/digital TV USB devices" +source "drivers/media/usb/cx231xx/Kconfig" +source "drivers/media/usb/tm6000/Kconfig" +endif + + +if I2C && MEDIA_DIGITAL_TV_SUPPORT + comment "Digital TV USB devices" source "drivers/media/usb/dvb-usb/Kconfig" source "drivers/media/usb/dvb-usb-v2/Kconfig" source "drivers/media/usb/ttusb-budget/Kconfig" source "drivers/media/usb/ttusb-dec/Kconfig" source "drivers/media/usb/siano/Kconfig" source "drivers/media/usb/b2c2/Kconfig" +endif +if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) + comment "Webcam, TV (analog/digital) USB devices" +source "drivers/media/usb/em28xx/Kconfig" endif + +endmenu diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 6b30ad13c38e..428827a4d97a 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -4,3 +4,17 @@ # DVB USB-only drivers obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ +obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ +obj-$(CONFIG_USB_GSPCA) += gspca/ +obj-$(CONFIG_USB_PWC) += pwc/ +obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ +obj-$(CONFIG_USB_SN9C102) += sn9c102/ +obj-$(CONFIG_VIDEO_AU0828) += au0828/ +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ +obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ +obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/ +obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ +obj-$(CONFIG_VIDEO_STK1160) += stk1160/ +obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ +obj-$(CONFIG_VIDEO_TM6000) += tm6000/ +obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig new file mode 100644 index 000000000000..23f7fd22f0eb --- /dev/null +++ b/drivers/media/usb/au0828/Kconfig @@ -0,0 +1,18 @@ + +config VIDEO_AU0828 + tristate "Auvitek AU0828 support" + depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 + depends on DVB_CAPTURE_DRIVERS + select I2C_ALGOBIT + select VIDEO_TVEEPROM + select VIDEOBUF_VMALLOC + select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE + select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + ---help--- + This is a video4linux driver for Auvitek's USB device. + + To compile this driver as a module, choose M here: the + module will be called au0828 diff --git a/drivers/media/usb/au0828/Makefile b/drivers/media/usb/au0828/Makefile new file mode 100644 index 000000000000..98cc20cc0ffb --- /dev/null +++ b/drivers/media/usb/au0828/Makefile @@ -0,0 +1,9 @@ +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o + +obj-$(CONFIG_VIDEO_AU0828) += au0828.o + +ccflags-y += -Idrivers/media/tuners +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends + +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c new file mode 100644 index 000000000000..448361c6a13e --- /dev/null +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -0,0 +1,339 @@ +/* + * Driver for the Auvitek USB bridge + * + * 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. + */ + +#include "au0828.h" +#include "au0828-cards.h" +#include "au8522.h" +#include "media/tuner.h" +#include "media/v4l2-common.h" + +void hvr950q_cs5340_audio(void *priv, int enable) +{ + /* Because the HVR-950q shares an i2s bus between the cs5340 and the + au8522, we need to hold cs5340 in reset when using the au8522 */ + struct au0828_dev *dev = priv; + if (enable == 1) + au0828_set(dev, REG_000, 0x10); + else + au0828_clear(dev, REG_000, 0x10); +} + +struct au0828_board au0828_boards[] = { + [AU0828_BOARD_UNKNOWN] = { + .name = "Unknown board", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + }, + [AU0828_BOARD_HAUPPAUGE_HVR850] = { + .name = "Hauppauge HVR850", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, + .input = { + { + .type = AU0828_VMUX_TELEVISION, + .vmux = AU8522_COMPOSITE_CH4_SIF, + .amux = AU8522_AUDIO_SIF, + }, + { + .type = AU0828_VMUX_COMPOSITE, + .vmux = AU8522_COMPOSITE_CH1, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, + }, + [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { + .name = "Hauppauge HVR950Q", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + /* The au0828 hardware i2c implementation does not properly + support the xc5000's i2c clock stretching. So we need to + lower the clock frequency enough where the 15us clock + stretch fits inside of a normal clock cycle, or else the + au0828 fails to set the STOP bit. A 30 KHz clock puts the + clock pulse width at 18us */ + .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, + .input = { + { + .type = AU0828_VMUX_TELEVISION, + .vmux = AU8522_COMPOSITE_CH4_SIF, + .amux = AU8522_AUDIO_SIF, + }, + { + .type = AU0828_VMUX_COMPOSITE, + .vmux = AU8522_COMPOSITE_CH1, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, + }, + [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { + .name = "Hauppauge HVR950Q rev xxF8", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, + }, + [AU0828_BOARD_DVICO_FUSIONHDTV7] = { + .name = "DViCO FusionHDTV USB", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, + }, + [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { + .name = "Hauppauge Woodbury", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, + }, +}; + +/* Tuner callback function for au0828 boards. Currently only needed + * for HVR1500Q, which has an xc5000 tuner. + */ +int au0828_tuner_callback(void *priv, int component, int command, int arg) +{ + struct au0828_dev *dev = priv; + + dprintk(1, "%s()\n", __func__); + + switch (dev->boardnr) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: + case AU0828_BOARD_DVICO_FUSIONHDTV7: + if (command == 0) { + /* Tuner Reset Command from xc5000 */ + /* Drive the tuner into reset and out */ + au0828_clear(dev, REG_001, 2); + mdelay(10); + au0828_set(dev, REG_001, 2); + mdelay(10); + return 0; + } else { + printk(KERN_ERR + "%s(): Unknown command.\n", __func__); + return -EINVAL; + } + break; + } + + return 0; /* Should never be here */ +} + +static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) +{ + struct tveeprom tv; + + tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); + dev->board.tuner_type = tv.tuner_type; + + /* Make sure we support the board model */ + switch (tv.model) { + case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ + case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ + case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ + case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ + case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ + case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ + break; + default: + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", __func__, tv.model); + break; + } + + printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + __func__, tv.model); +} + +void au0828_card_setup(struct au0828_dev *dev) +{ + static u8 eeprom[256]; + struct tuner_setup tun_setup; + struct v4l2_subdev *sd; + unsigned int mode_mask = T_ANALOG_TV; + + dprintk(1, "%s()\n", __func__); + + memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); + + if (dev->i2c_rc == 0) { + dev->i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); + } + + switch (dev->boardnr) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: + case AU0828_BOARD_HAUPPAUGE_WOODBURY: + if (dev->i2c_rc == 0) + hauppauge_eeprom(dev, eeprom+0xa0); + break; + } + + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { + /* Load the analog demodulator driver (note this would need to + be abstracted out if we ever need to support a different + demod) */ + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "au8522", 0x8e >> 1, NULL); + if (sd == NULL) + printk(KERN_ERR "analog subdev registration failed\n"); + } + + /* Setup tuners */ + if (dev->board.tuner_type != TUNER_ABSENT) { + /* Load the tuner module, which does the attach */ + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", dev->board.tuner_addr, NULL); + if (sd == NULL) + printk(KERN_ERR "tuner subdev registration fail\n"); + + tun_setup.mode_mask = mode_mask; + tun_setup.type = dev->board.tuner_type; + tun_setup.addr = dev->board.tuner_addr; + tun_setup.tuner_callback = au0828_tuner_callback; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, + &tun_setup); + } +} + +/* + * The bridge has between 8 and 12 gpios. + * Regs 1 and 0 deal with output enables. + * Regs 3 and 2 deal with direction. + */ +void au0828_gpio_setup(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + switch (dev->boardnr) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: + case AU0828_BOARD_HAUPPAUGE_WOODBURY: + /* GPIO's + * 4 - CS5340 + * 5 - AU8522 Demodulator + * 6 - eeprom W/P + * 7 - power supply + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset (leave the cs5340 in reset until needed) */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); + au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20); + + msleep(250); + break; + case AU0828_BOARD_DVICO_FUSIONHDTV7: + /* GPIO's + * 6 - ? + * 8 - AU8522 Demodulator + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_000, 0xa0); + msleep(250); + break; + } +} + +/* table of devices that work with this driver */ +struct usb_device_id au0828_usb_id_table[] = { + { USB_DEVICE(0x2040, 0x7200), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7240), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, + { USB_DEVICE(0x0fe9, 0xd620), + .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, + { USB_DEVICE(0x2040, 0x7210), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7217), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721b), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721e), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721f), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7280), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x0fd9, 0x0008), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7201), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, + { USB_DEVICE(0x2040, 0x7211), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, + { USB_DEVICE(0x2040, 0x7281), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, + { USB_DEVICE(0x05e1, 0x0480), + .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, + { USB_DEVICE(0x2040, 0x8200), + .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, + { USB_DEVICE(0x2040, 0x7260), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7213), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); diff --git a/drivers/media/usb/au0828/au0828-cards.h b/drivers/media/usb/au0828/au0828-cards.h new file mode 100644 index 000000000000..48a1882c2b6b --- /dev/null +++ b/drivers/media/usb/au0828/au0828-cards.h @@ -0,0 +1,27 @@ +/* + * Driver for the Auvitek USB bridge + * + * 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. + */ + +#define AU0828_BOARD_UNKNOWN 0 +#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1 +#define AU0828_BOARD_HAUPPAUGE_HVR850 2 +#define AU0828_BOARD_DVICO_FUSIONHDTV7 3 +#define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4 +#define AU0828_BOARD_HAUPPAUGE_WOODBURY 5 diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c new file mode 100644 index 000000000000..745a80a798c8 --- /dev/null +++ b/drivers/media/usb/au0828/au0828-core.c @@ -0,0 +1,282 @@ +/* + * Driver for the Auvitek USB bridge + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "au0828.h" + +/* + * 1 = General debug messages + * 2 = USB handling + * 4 = I2C related + * 8 = Bridge related + */ +int au0828_debug; +module_param_named(debug, au0828_debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +static unsigned int disable_usb_speed_check; +module_param(disable_usb_speed_check, int, 0444); +MODULE_PARM_DESC(disable_usb_speed_check, + "override min bandwidth requirement of 480M bps"); + +#define _AU0828_BULKPIPE 0x03 +#define _BULKPIPESIZE 0xffff + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index); +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size); + +/* USB Direction */ +#define CMD_REQUEST_IN 0x00 +#define CMD_REQUEST_OUT 0x01 + +u32 au0828_readreg(struct au0828_dev *dev, u16 reg) +{ + u8 result = 0; + + recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1); + dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result); + + return result; +} + +u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) +{ + dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); + return send_control_msg(dev, CMD_REQUEST_OUT, val, reg); +} + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index) +{ + int status = -ENODEV; + + if (dev->usbdev) { + + /* cp must be memory that has been allocated by kmalloc */ + status = usb_control_msg(dev->usbdev, + usb_sndctrlpipe(dev->usbdev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, + value, index, NULL, 0, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed sending control message, error %d.\n", + __func__, status); + } + + } + + return status; +} + +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size) +{ + int status = -ENODEV; + mutex_lock(&dev->mutex); + if (dev->usbdev) { + status = usb_control_msg(dev->usbdev, + usb_rcvctrlpipe(dev->usbdev, 0), + request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + dev->ctrlmsg, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed receiving control message, error %d.\n", + __func__, status); + } + + /* the host controller requires heap allocated memory, which + is why we didn't just pass "cp" into usb_control_msg */ + memcpy(cp, dev->ctrlmsg, size); + } + mutex_unlock(&dev->mutex); + return status; +} + +static void au0828_usb_disconnect(struct usb_interface *interface) +{ + struct au0828_dev *dev = usb_get_intfdata(interface); + + dprintk(1, "%s()\n", __func__); + + /* Digital TV */ + au0828_dvb_unregister(dev); + + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) + au0828_analog_unregister(dev); + + /* I2C */ + au0828_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + usb_set_intfdata(interface, NULL); + + mutex_lock(&dev->mutex); + dev->usbdev = NULL; + mutex_unlock(&dev->mutex); + + kfree(dev); + +} + +static int au0828_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ifnum, retval; + struct au0828_dev *dev; + struct usb_device *usbdev = interface_to_usbdev(interface); + + ifnum = interface->altsetting->desc.bInterfaceNumber; + + if (ifnum != 0) + return -ENODEV; + + dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__, + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct), + ifnum); + + /* + * Make sure we have 480 Mbps of bandwidth, otherwise things like + * video stream wouldn't likely work, since 12 Mbps is generally + * not enough even for most Digital TV streams. + */ + if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { + printk(KERN_ERR "au0828: Device initialization failed.\n"); + printk(KERN_ERR "au0828: Device must be connected to a " + "high-speed USB 2.0 port.\n"); + return -ENODEV; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "%s() Unable to allocate memory\n", __func__); + return -ENOMEM; + } + + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + mutex_init(&dev->mutex); + mutex_init(&dev->dvb.lock); + dev->usbdev = usbdev; + dev->boardnr = id->driver_info; + + /* Create the v4l2_device */ + retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (retval) { + printk(KERN_ERR "%s() v4l2_device_register failed\n", + __func__); + mutex_unlock(&dev->lock); + kfree(dev); + return -EIO; + } + + /* Power Up the bridge */ + au0828_write(dev, REG_600, 1 << 4); + + /* Bring up the GPIO's and supporting devices */ + au0828_gpio_setup(dev); + + /* I2C */ + au0828_i2c_register(dev); + + /* Setup */ + au0828_card_setup(dev); + + /* Analog TV */ + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) + au0828_analog_register(dev, interface); + + /* Digital TV */ + au0828_dvb_register(dev); + + /* Store the pointer to the au0828_dev so it can be accessed in + au0828_usb_disconnect */ + usb_set_intfdata(interface, dev); + + printk(KERN_INFO "Registered device AU0828 [%s]\n", + dev->board.name == NULL ? "Unset" : dev->board.name); + + mutex_unlock(&dev->lock); + + return 0; +} + +static struct usb_driver au0828_usb_driver = { + .name = DRIVER_NAME, + .probe = au0828_usb_probe, + .disconnect = au0828_usb_disconnect, + .id_table = au0828_usb_id_table, +}; + +static int __init au0828_init(void) +{ + int ret; + + if (au0828_debug & 1) + printk(KERN_INFO "%s() Debugging is enabled\n", __func__); + + if (au0828_debug & 2) + printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__); + + if (au0828_debug & 4) + printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__); + + if (au0828_debug & 8) + printk(KERN_INFO "%s() Bridge Debugging is enabled\n", + __func__); + + printk(KERN_INFO "au0828 driver loaded\n"); + + ret = usb_register(&au0828_usb_driver); + if (ret) + printk(KERN_ERR "usb_register failed, error = %d\n", ret); + + return ret; +} + +static void __exit au0828_exit(void) +{ + usb_deregister(&au0828_usb_driver); +} + +module_init(au0828_init); +module_exit(au0828_exit); + +MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); +MODULE_AUTHOR("Steven Toth "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.0.2"); diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c new file mode 100644 index 000000000000..b328f6550d0b --- /dev/null +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -0,0 +1,500 @@ +/* + * Driver for the Auvitek USB bridge + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "au0828.h" +#include "au8522.h" +#include "xc5000.h" +#include "mxl5007t.h" +#include "tda18271.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define _AU0828_BULKPIPE 0x83 +#define _BULKPIPESIZE 0xe522 + +static u8 hauppauge_hvr950q_led_states[] = { + 0x00, /* off */ + 0x02, /* yellow */ + 0x04, /* green */ +}; + +static struct au8522_led_config hauppauge_hvr950q_led_cfg = { + .gpio_output = 0x00e0, + .gpio_output_enable = 0x6006, + .gpio_output_disable = 0x0660, + + .gpio_leds = 0x00e2, + .led_states = hauppauge_hvr950q_led_states, + .num_led_states = sizeof(hauppauge_hvr950q_led_states), + + .vsb8_strong = 20 /* dB */ * 10, + .qam64_strong = 25 /* dB */ * 10, + .qam256_strong = 32 /* dB */ * 10, +}; + +static struct au8522_config hauppauge_hvr950q_config = { + .demod_address = 0x8e >> 1, + .status_mode = AU8522_DEMODLOCKING, + .qam_if = AU8522_IF_6MHZ, + .vsb_if = AU8522_IF_6MHZ, + .led_cfg = &hauppauge_hvr950q_led_cfg, +}; + +static struct au8522_config fusionhdtv7usb_config = { + .demod_address = 0x8e >> 1, + .status_mode = AU8522_DEMODLOCKING, + .qam_if = AU8522_IF_6MHZ, + .vsb_if = AU8522_IF_6MHZ, +}; + +static struct au8522_config hauppauge_woodbury_config = { + .demod_address = 0x8e >> 1, + .status_mode = AU8522_DEMODLOCKING, + .qam_if = AU8522_IF_4MHZ, + .vsb_if = AU8522_IF_3_25MHZ, +}; + +static struct xc5000_config hauppauge_xc5000a_config = { + .i2c_address = 0x61, + .if_khz = 6000, + .chip_id = XC5000A, +}; + +static struct xc5000_config hauppauge_xc5000c_config = { + .i2c_address = 0x61, + .if_khz = 6000, + .chip_id = XC5000C, +}; + +static struct mxl5007t_config mxl5007t_hvr950q_config = { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_6_MHZ, +}; + +static struct tda18271_config hauppauge_woodbury_tunerconfig = { + .gate = TDA18271_GATE_DIGITAL, +}; + +static void au0828_restart_dvb_streaming(struct work_struct *work); + +/*-------------------------------------------------------------------*/ +static void urb_completion(struct urb *purb) +{ + struct au0828_dev *dev = purb->context; + int ptype = usb_pipetype(purb->pipe); + unsigned char *ptr; + + dprintk(2, "%s()\n", __func__); + + if (!dev) + return; + + if (dev->urb_streaming == 0) + return; + + if (ptype != PIPE_BULK) { + printk(KERN_ERR "%s() Unsupported URB type %d\n", + __func__, ptype); + return; + } + + /* See if the stream is corrupted (to work around a hardware + bug where the stream gets misaligned */ + ptr = purb->transfer_buffer; + if (purb->actual_length > 0 && ptr[0] != 0x47) { + dprintk(1, "Need to restart streaming %02x len=%d!\n", + ptr[0], purb->actual_length); + schedule_work(&dev->restart_streaming); + return; + } + + /* Feed the transport payload into the kernel demux */ + dvb_dmx_swfilter_packets(&dev->dvb.demux, + purb->transfer_buffer, purb->actual_length / 188); + + /* Clean the buffer before we requeue */ + memset(purb->transfer_buffer, 0, URB_BUFSIZE); + + /* Requeue URB */ + usb_submit_urb(purb, GFP_ATOMIC); +} + +static int stop_urb_transfer(struct au0828_dev *dev) +{ + int i; + + dprintk(2, "%s()\n", __func__); + + dev->urb_streaming = 0; + for (i = 0; i < URB_COUNT; i++) { + usb_kill_urb(dev->urbs[i]); + kfree(dev->urbs[i]->transfer_buffer); + usb_free_urb(dev->urbs[i]); + } + + return 0; +} + +static int start_urb_transfer(struct au0828_dev *dev) +{ + struct urb *purb; + int i, ret = -ENOMEM; + + dprintk(2, "%s()\n", __func__); + + if (dev->urb_streaming) { + dprintk(2, "%s: bulk xfer already running!\n", __func__); + return 0; + } + + for (i = 0; i < URB_COUNT; i++) { + + dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urbs[i]) + goto err; + + purb = dev->urbs[i]; + + purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL); + if (!purb->transfer_buffer) { + usb_free_urb(purb); + dev->urbs[i] = NULL; + goto err; + } + + purb->status = -EINPROGRESS; + usb_fill_bulk_urb(purb, + dev->usbdev, + usb_rcvbulkpipe(dev->usbdev, + _AU0828_BULKPIPE), + purb->transfer_buffer, + URB_BUFSIZE, + urb_completion, + dev); + + } + + for (i = 0; i < URB_COUNT; i++) { + ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC); + if (ret != 0) { + stop_urb_transfer(dev); + printk(KERN_ERR "%s: failed urb submission, " + "err = %d\n", __func__, ret); + return ret; + } + } + + dev->urb_streaming = 1; + ret = 0; + +err: + return ret; +} + +static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (!demux->dmx.frontend) + return -EINVAL; + + if (dvb) { + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + au0828_write(dev, 0x608, 0x90); + au0828_write(dev, 0x609, 0x72); + au0828_write(dev, 0x60a, 0x71); + au0828_write(dev, 0x60b, 0x01); + ret = start_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (dvb) { + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + ret = stop_urb_transfer(dev); + au0828_write(dev, 0x60b, 0x00); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +static void au0828_restart_dvb_streaming(struct work_struct *work) +{ + struct au0828_dev *dev = container_of(work, struct au0828_dev, + restart_streaming); + struct au0828_dvb *dvb = &dev->dvb; + int ret; + + if (dev->urb_streaming == 0) + return; + + dprintk(1, "Restarting streaming...!\n"); + + mutex_lock(&dvb->lock); + + /* Stop transport */ + ret = stop_urb_transfer(dev); + au0828_write(dev, 0x608, 0x00); + au0828_write(dev, 0x609, 0x00); + au0828_write(dev, 0x60a, 0x00); + au0828_write(dev, 0x60b, 0x00); + + /* Start transport */ + au0828_write(dev, 0x608, 0x90); + au0828_write(dev, 0x609, 0x72); + au0828_write(dev, 0x60a, 0x71); + au0828_write(dev, 0x60b, 0x01); + ret = start_urb_transfer(dev); + + mutex_unlock(&dvb->lock); +} + +static int dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int result; + + dprintk(1, "%s()\n", __func__); + + INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, + &dev->usbdev->dev, adapter_nr); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_adapter failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_adapter; + } + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_frontend failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = au0828_dvb_start_feed; + dvb->demux.stop_feed = au0828_dvb_stop_feed; + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +void au0828_dvb_unregister(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + + dprintk(1, "%s()\n", __func__); + + if (dvb->frontend == NULL) + return; + + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +} + +/* All the DVB attach calls go here, this function get's modified + * for each new card. No other function in this file needs + * to change. + */ +int au0828_dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int ret; + + dprintk(1, "%s()\n", __func__); + + /* init frontend */ + switch (dev->boardnr) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + dvb->frontend = dvb_attach(au8522_attach, + &hauppauge_hvr950q_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) + switch (dev->board.tuner_type) { + default: + case TUNER_XC5000: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000a_config); + break; + case TUNER_XC5000C: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000c_config); + break; + } + break; + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: + dvb->frontend = dvb_attach(au8522_attach, + &hauppauge_hvr950q_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) + dvb_attach(mxl5007t_attach, dvb->frontend, + &dev->i2c_adap, 0x60, + &mxl5007t_hvr950q_config); + break; + case AU0828_BOARD_HAUPPAUGE_WOODBURY: + dvb->frontend = dvb_attach(au8522_attach, + &hauppauge_woodbury_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) + dvb_attach(tda18271_attach, dvb->frontend, + 0x60, &dev->i2c_adap, + &hauppauge_woodbury_tunerconfig); + break; + case AU0828_BOARD_DVICO_FUSIONHDTV7: + dvb->frontend = dvb_attach(au8522_attach, + &fusionhdtv7usb_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) { + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000a_config); + } + break; + default: + printk(KERN_WARNING "The frontend of your DVB/ATSC card " + "isn't supported yet\n"); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR "%s() Frontend initialization failed\n", + __func__); + return -1; + } + /* define general-purpose callback pointer */ + dvb->frontend->callback = au0828_tuner_callback; + + /* register everything */ + ret = dvb_register(dev); + if (ret < 0) { + if (dvb->frontend->ops.release) + dvb->frontend->ops.release(dvb->frontend); + return ret; + } + + return 0; +} diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c new file mode 100644 index 000000000000..4ded17fe1957 --- /dev/null +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -0,0 +1,401 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "au0828.h" +#include "media/tuner.h" +#include + +static int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +#define I2C_WAIT_DELAY 25 +#define I2C_WAIT_RETRY 1000 + +static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1; +} + +static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1; +} + +static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_slave_did_read_ack(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_READ_DONE ? 0 : 1; +} + +static int i2c_wait_read_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_read_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0; +} + +static int i2c_wait_write_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (i2c_is_write_done(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_BUSY ? 1 : 0; +} + +static int i2c_wait_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_sendbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined_rlen) +{ + int i, strobe = 0; + struct au0828_dev *dev = i2c_adap->algo_data; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); + + /* Set the I2C clock */ + if (((dev->board.tuner_type == TUNER_XC5000) || + (dev->board.tuner_type == TUNER_XC5000C)) && + (dev->board.tuner_addr == msg->addr) && + (msg->len == 64)) { + /* Hack to speed up firmware load. The xc5000 lets us do up + to 400 KHz when in firmware download mode */ + au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, + AU0828_I2C_CLK_250KHZ); + } else { + /* Use the i2c clock speed in the board configuration */ + au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, + dev->board.i2c_clk_divider); + } + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); + + dprintk(4, "SEND: %02x\n", msg->addr); + + /* Deal with i2c_scan */ + if (msg->len == 0) { + /* The analog tuner detection code makes use of the SMBUS_QUICK + message (which involves a zero length i2c write). To avoid + checking the status register when we didn't strobe out any + actual bytes to the bus, just do a read check. This is + consistent with how I saw i2c device checking done in the + USB trace of the Windows driver */ + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ); + + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + if (i2c_wait_read_ack(i2c_adap)) + return -EIO; + + return 0; + } + + for (i = 0; i < msg->len;) { + + dprintk(4, " %02x\n", msg->buf[i]); + + au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]); + + strobe++; + i++; + + if ((strobe >= 4) || (i >= msg->len)) { + + /* Strobe the byte into the bus */ + if (i < msg->len) + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_WRITE | + AU0828_I2C_TRIGGER_HOLD); + else + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_WRITE); + + /* Reset strobe trigger */ + strobe = 0; + + if (!i2c_wait_write_done(i2c_adap)) + return -EIO; + + } + + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_readbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + int i; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); + + /* Set the I2C clock */ + au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, + dev->board.i2c_clk_divider); + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); + + dprintk(4, " RECV:\n"); + + /* Deal with i2c_scan */ + if (msg->len == 0) { + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ); + + if (i2c_wait_read_ack(i2c_adap)) + return -EIO; + return 0; + } + + for (i = 0; i < msg->len;) { + + i++; + + if (i < msg->len) + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ | + AU0828_I2C_TRIGGER_HOLD); + else + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ); + + if (!i2c_wait_read_done(i2c_adap)) + return -EIO; + + msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) & + 0xff; + + dprintk(4, " %02x\n", msg->buf[i-1]); + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +static int i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + int i, retval = 0; + + dprintk(4, "%s(num = %d)\n", __func__, num); + + for (i = 0; i < num; i++) { + dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n", + __func__, num, msgs[i].addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read */ + retval = i2c_readbytes(i2c_adap, &msgs[i], 0); + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* write then read from same address */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], + msgs[i + 1].len); + if (retval < 0) + goto err; + i++; + retval = i2c_readbytes(i2c_adap, &msgs[i], 1); + } else { + /* write */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); + } + if (retval < 0) + goto err; + } + return num; + +err: + return retval; +} + +static u32 au0828_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm au0828_i2c_algo_template = { + .master_xfer = i2c_xfer, + .functionality = au0828_functionality, +}; + +/* ----------------------------------------------------------------------- */ + +static struct i2c_adapter au0828_i2c_adap_template = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .algo = &au0828_i2c_algo_template, +}; + +static struct i2c_client au0828_i2c_client_template = { + .name = "au0828 internal", +}; + +static char *i2c_devs[128] = { + [0x8e >> 1] = "au8522", + [0xa0 >> 1] = "eeprom", + [0xc2 >> 1] = "tuner/xc5000", +}; + +static void do_i2c_scan(char *name, struct i2c_client *c) +{ + unsigned char buf; + int i, rc; + + for (i = 0; i < 128; i++) { + c->addr = i; + rc = i2c_master_recv(c, &buf, 0); + if (rc < 0) + continue; + printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", + name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + } +} + +/* init + register i2c adapter */ +int au0828_i2c_register(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, + sizeof(dev->i2c_adap)); + memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, + sizeof(dev->i2c_algo)); + memcpy(&dev->i2c_client, &au0828_i2c_client_template, + sizeof(dev->i2c_client)); + + dev->i2c_adap.dev.parent = &dev->usbdev->dev; + + strlcpy(dev->i2c_adap.name, DRIVER_NAME, + sizeof(dev->i2c_adap.name)); + + dev->i2c_adap.algo = &dev->i2c_algo; + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client.adapter = &dev->i2c_adap; + + if (0 == dev->i2c_rc) { + printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME); + if (i2c_scan) + do_i2c_scan(DRIVER_NAME, &dev->i2c_client); + } else + printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME); + + return dev->i2c_rc; +} + +int au0828_i2c_unregister(struct au0828_dev *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} + diff --git a/drivers/media/usb/au0828/au0828-reg.h b/drivers/media/usb/au0828/au0828-reg.h new file mode 100644 index 000000000000..2140f4cfb645 --- /dev/null +++ b/drivers/media/usb/au0828/au0828-reg.h @@ -0,0 +1,66 @@ +/* + * Driver for the Auvitek USB bridge + * + * 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. + */ + +/* We'll start to rename these registers once we have a better + * understanding of their meaning. + */ +#define REG_000 0x000 +#define REG_001 0x001 +#define REG_002 0x002 +#define REG_003 0x003 + +#define AU0828_SENSORCTRL_100 0x100 +#define AU0828_SENSORCTRL_VBI_103 0x103 + +/* I2C registers */ +#define AU0828_I2C_TRIGGER_200 0x200 +#define AU0828_I2C_STATUS_201 0x201 +#define AU0828_I2C_CLK_DIVIDER_202 0x202 +#define AU0828_I2C_DEST_ADDR_203 0x203 +#define AU0828_I2C_WRITE_FIFO_205 0x205 +#define AU0828_I2C_READ_FIFO_209 0x209 +#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff + +/* Audio registers */ +#define AU0828_AUDIOCTRL_50C 0x50C + +#define REG_600 0x600 + +/*********************************************************************/ +/* Here are constants for values associated with the above registers */ + +/* I2C Trigger (Reg 0x200) */ +#define AU0828_I2C_TRIGGER_WRITE 0x01 +#define AU0828_I2C_TRIGGER_READ 0x20 +#define AU0828_I2C_TRIGGER_HOLD 0x40 + +/* I2C Status (Reg 0x201) */ +#define AU0828_I2C_STATUS_READ_DONE 0x01 +#define AU0828_I2C_STATUS_NO_READ_ACK 0x02 +#define AU0828_I2C_STATUS_WRITE_DONE 0x04 +#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08 +#define AU0828_I2C_STATUS_BUSY 0x10 + +/* I2C Clock Divider (Reg 0x202) */ +#define AU0828_I2C_CLK_250KHZ 0x07 +#define AU0828_I2C_CLK_100KHZ 0x14 +#define AU0828_I2C_CLK_30KHZ 0x40 +#define AU0828_I2C_CLK_20KHZ 0x60 diff --git a/drivers/media/usb/au0828/au0828-vbi.c b/drivers/media/usb/au0828/au0828-vbi.c new file mode 100644 index 000000000000..63f593070ee8 --- /dev/null +++ b/drivers/media/usb/au0828/au0828-vbi.c @@ -0,0 +1,138 @@ +/* + au0828-vbi.c - VBI driver for au0828 + + Copyright (C) 2010 Devin Heitmueller + + This work was sponsored by GetWellNetwork 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "au0828.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct au0828_fh *fh = q->priv_data; + struct au0828_dev *dev = fh->dev; + + *size = dev->vbi_width * dev->vbi_height * 2; + + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct au0828_fh *fh = q->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + int rc = 0; + + buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->vbi_width; + buf->vb.height = dev->vbi_height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops au0828_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c new file mode 100644 index 000000000000..fa0fa9ae91c7 --- /dev/null +++ b/drivers/media/usb/au0828/au0828-video.c @@ -0,0 +1,2034 @@ +/* + * Auvitek AU0828 USB Bridge (Analog video support) + * + * Copyright (C) 2009 Devin Heitmueller + * Copyright (C) 2005-2008 Auvitek International, Ltd. + * + * This program is free software; 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. + */ + +/* Developer Notes: + * + * VBI support is not yet working + * The hardware scaler supported is unimplemented + * AC97 audio support is unimplemented (only i2s audio mode) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "au0828.h" +#include "au0828-reg.h" + +static DEFINE_MUTEX(au0828_sysfs_lock); + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define au0828_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "au0828 %s :"fmt, \ + __func__ , ##arg); \ + } \ + } while (0) + +static inline void print_err_status(struct au0828_dev *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + au0828_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + au0828_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static int check_dev(struct au0828_dev *dev) +{ + if (dev->dev_state & DEV_DISCONNECTED) { + printk(KERN_INFO "v4l2 ioctl: device not present\n"); + return -ENODEV; + } + + if (dev->dev_state & DEV_MISCONFIGURED) { + printk(KERN_INFO "v4l2 ioctl: device is misconfigured; " + "close and open it again\n"); + return -EIO; + } + return 0; +} + +/* + * IRQ callback, called by URB callback + */ +static void au0828_irq_callback(struct urb *urb) +{ + struct au0828_dmaqueue *dma_q = urb->context; + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); + unsigned long flags = 0; + int i; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + au0828_isocdbg("au0828_irq_callback called: status kill\n"); + return; + default: /* unknown error */ + au0828_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock_irqsave(&dev->slock, flags); + dev->isoc_ctl.isoc_copy(dev, urb); + spin_unlock_irqrestore(&dev->slock, flags); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + au0828_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void au0828_uninit_isoc(struct au0828_dev *dev) +{ + struct urb *urb; + int i; + + au0828_isocdbg("au0828: called au0828_uninit_isoc\n"); + + dev->isoc_ctl.nfields = -1; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_free_coherent(dev->usbdev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; +} + +/* + * Allocate URBs and start IRQ + */ +int au0828_init_isoc(struct au0828_dev *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb)) +{ + struct au0828_dmaqueue *dma_q = &dev->vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + au0828_isocdbg("au0828: called au0828_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + au0828_uninit_isoc(dev); + + dev->isoc_ctl.isoc_copy = isoc_copy; + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + au0828_isocdbg("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + au0828_isocdbg("cannot allocate memory for usb transfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dev->isoc_ctl.max_pkt_size = max_pkt_size; + dev->isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i); + au0828_uninit_isoc(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + printk("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + au0828_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + pipe = usb_rcvisocpipe(dev->usbdev, + dev->isoc_in_endpointaddr), + + usb_fill_int_urb(urb, dev->usbdev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + au0828_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->isoc_ctl.max_pkt_size; + k += dev->isoc_ctl.max_pkt_size; + } + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + au0828_isocdbg("submit of urb %i failed (error=%i)\n", + i, rc); + au0828_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf) +{ + /* Advice that buffer was filled */ + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void vbi_buffer_filled(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf) +{ + /* Advice that buffer was filled */ + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the buffer header type and properly handles + */ +static void au0828_copy_video(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *fieldstart, *startwrite, *startread; + int linesdone, currlinedone, offset, lencopy, remain; + int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */ + + if (len == 0) + return; + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + remain = len; + + /* Interlaces frame */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + + linesdone = dma_q->pos / bytesperline; + currlinedone = dma_q->pos % bytesperline; + offset = linesdone * bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; + lencopy = bytesperline - currlinedone; + lencopy = lencopy > remain ? remain : lencopy; + + if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + remain = (char *)outp + buf->vb.size - (char *)startwrite; + lencopy = remain; + } + if (lencopy <= 0) + return; + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + + while (remain > 0) { + startwrite += lencopy + bytesperline; + startread += lencopy; + if (bytesperline > remain) + lencopy = remain; + else + lencopy = bytesperline; + + if ((char *)startwrite + lencopy > (char *)outp + + buf->vb.size) { + au0828_isocdbg("Overflow %zi bytes past buf end (2)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + lencopy = remain = (char *)outp + buf->vb.size - + (char *)startwrite; + } + if (lencopy <= 0) + break; + + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + } + + if (offset > 1440) { + /* We have enough data to check for greenscreen */ + if (outp[0] < 0x60 && outp[1440] < 0x60) + dev->greenscreen_detected = 1; + } + + dma_q->pos += len; +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct au0828_dmaqueue *dma_q, + struct au0828_buffer **buf) +{ + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); + + if (list_empty(&dma_q->active)) { + au0828_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + dev->isoc_ctl.buf = *buf; + + return; +} + +static void au0828_copy_vbi(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + unsigned char *startwrite, *startread; + int bytesperline; + int i, j = 0; + + if (dev == NULL) { + au0828_isocdbg("dev is null\n"); + return; + } + + if (dma_q == NULL) { + au0828_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) + return; + if (p == NULL) { + au0828_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + au0828_isocdbg("outp is null\n"); + return; + } + + bytesperline = dev->vbi_width; + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + startwrite = outp + (dma_q->pos / 2); + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) + startwrite += bytesperline * dev->vbi_height; + + for (i = 0; i < len; i += 2) + startwrite[j++] = startread[i+1]; + + dma_q->pos += len; +} + + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q, + struct au0828_buffer **buf) +{ + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq); + char *outp; + + if (list_empty(&dma_q->active)) { + au0828_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + /* Cleans up buffer - Useful for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); + + dev->isoc_ctl.vbi_buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) +{ + struct au0828_buffer *buf; + struct au0828_buffer *vbi_buf; + struct au0828_dmaqueue *dma_q = urb->context; + struct au0828_dmaqueue *vbi_dma_q = &dev->vbiq; + unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + unsigned char fbyte; + unsigned int vbi_field_size; + unsigned int remain, lencopy; + + if (!dev) + return 0; + + if ((dev->dev_state & DEV_DISCONNECTED) || + (dev->dev_state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + if (urb->iso_frame_desc[i].actual_length <= 0) + continue; + + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + au0828_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + fbyte = p[0]; + len = urb->iso_frame_desc[i].actual_length - 4; + p += 4; + + if (fbyte & 0x80) { + len -= 4; + p += 4; + au0828_isocdbg("Video frame %s\n", + (fbyte & 0x40) ? "odd" : "even"); + if (fbyte & 0x40) { + /* VBI */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + + /* Video */ + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + + /* As long as isoc traffic is arriving, keep + resetting the timer */ + if (dev->vid_timeout_running) + mod_timer(&dev->vid_timeout, + jiffies + (HZ / 10)); + if (dev->vbi_timeout_running) + mod_timer(&dev->vbi_timeout, + jiffies + (HZ / 10)); + } + + if (buf != NULL) { + if (fbyte & 0x40) + buf->top_field = 1; + else + buf->top_field = 0; + } + + if (vbi_buf != NULL) { + if (fbyte & 0x40) + vbi_buf->top_field = 1; + else + vbi_buf->top_field = 0; + } + + dev->vbi_read = 0; + vbi_dma_q->pos = 0; + dma_q->pos = 0; + } + + vbi_field_size = dev->vbi_width * dev->vbi_height * 2; + if (dev->vbi_read < vbi_field_size) { + remain = vbi_field_size - dev->vbi_read; + if (len < remain) + lencopy = len; + else + lencopy = remain; + + if (vbi_buf != NULL) + au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + + len -= lencopy; + p += lencopy; + dev->vbi_read += lencopy; + } + + if (dev->vbi_read >= vbi_field_size && buf != NULL) + au0828_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct au0828_fh *fh = vq->priv_data; + *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + + if (0 == *count) + *count = AU0828_DEF_BUF; + + if (*count < AU0828_MIN_BUF) + *count = AU0828_MIN_BUF; + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + struct au0828_dev *dev = fh->dev; + int rc = 0, urb_init = 0; + + buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) { + printk(KERN_INFO "videobuf_iolock failed\n"); + goto fail; + } + } + + if (!dev->isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB, + AU0828_MAX_ISO_BUFS, dev->max_pkt_size, + au0828_isoc_copy); + if (rc < 0) { + printk(KERN_INFO "au0828_init_isoc failed\n"); + goto fail; + } + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops au0828_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + V4L2 interface + ------------------------------------------------------------------*/ + +static int au0828_i2s_init(struct au0828_dev *dev) +{ + /* Enable i2s mode */ + au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01); + return 0; +} + +/* + * Auvitek au0828 analog stream enable + * Please set interface0 to AS5 before enable the stream + */ +int au0828_analog_stream_enable(struct au0828_dev *d) +{ + dprintk(1, "au0828_analog_stream_enable called\n"); + au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00); + au0828_writereg(d, 0x106, 0x00); + /* set x position */ + au0828_writereg(d, 0x110, 0x00); + au0828_writereg(d, 0x111, 0x00); + au0828_writereg(d, 0x114, 0xa0); + au0828_writereg(d, 0x115, 0x05); + /* set y position */ + au0828_writereg(d, 0x112, 0x00); + au0828_writereg(d, 0x113, 0x00); + au0828_writereg(d, 0x116, 0xf2); + au0828_writereg(d, 0x117, 0x00); + au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3); + + return 0; +} + +int au0828_analog_stream_disable(struct au0828_dev *d) +{ + dprintk(1, "au0828_analog_stream_disable called\n"); + au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0); + return 0; +} + +void au0828_analog_stream_reset(struct au0828_dev *dev) +{ + dprintk(1, "au0828_analog_stream_reset called\n"); + au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0); + mdelay(30); + au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3); +} + +/* + * Some operations needs to stop current streaming + */ +static int au0828_stream_interrupt(struct au0828_dev *dev) +{ + int ret = 0; + + dev->stream_state = STREAM_INTERRUPT; + if (dev->dev_state == DEV_DISCONNECTED) + return -ENODEV; + else if (ret) { + dev->dev_state = DEV_MISCONFIGURED; + dprintk(1, "%s device is misconfigured!\n", __func__); + return ret; + } + return 0; +} + +/* + * au0828_release_resources + * unregister v4l2 devices + */ +void au0828_analog_unregister(struct au0828_dev *dev) +{ + dprintk(1, "au0828_release_resources called\n"); + mutex_lock(&au0828_sysfs_lock); + + if (dev->vdev) + video_unregister_device(dev->vdev); + if (dev->vbi_dev) + video_unregister_device(dev->vbi_dev); + + mutex_unlock(&au0828_sysfs_lock); +} + + +/* Usage lock check functions */ +static int res_get(struct au0828_fh *fh, unsigned int bit) +{ + struct au0828_dev *dev = fh->dev; + + if (fh->resources & bit) + /* have it already allocated */ + return 1; + + /* is it free? */ + if (dev->resources & bit) { + /* no, someone else uses it */ + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + dprintk(1, "res: get %d\n", bit); + + return 1; +} + +static int res_check(struct au0828_fh *fh, unsigned int bit) +{ + return fh->resources & bit; +} + +static int res_locked(struct au0828_dev *dev, unsigned int bit) +{ + return dev->resources & bit; +} + +static void res_free(struct au0828_fh *fh, unsigned int bits) +{ + struct au0828_dev *dev = fh->dev; + + BUG_ON((fh->resources & bits) != bits); + + fh->resources &= ~bits; + dev->resources &= ~bits; + dprintk(1, "res: put %d\n", bits); +} + +static int get_ressource(struct au0828_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return AU0828_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return AU0828_RESOURCE_VBI; + default: + BUG(); + return 0; + } +} + +/* This function ensures that video frames continue to be delivered even if + the ITU-656 input isn't receiving any data (thereby preventing applications + such as tvtime from hanging) */ +void au0828_vid_buffer_timeout(unsigned long data) +{ + struct au0828_dev *dev = (struct au0828_dev *) data; + struct au0828_dmaqueue *dma_q = &dev->vidq; + struct au0828_buffer *buf; + unsigned char *vid_data; + unsigned long flags = 0; + + spin_lock_irqsave(&dev->slock, flags); + + buf = dev->isoc_ctl.buf; + if (buf != NULL) { + vid_data = videobuf_to_vmalloc(&buf->vb); + memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */ + buffer_filled(dev, dma_q, buf); + } + get_next_buf(dma_q, &buf); + + if (dev->vid_timeout_running == 1) + mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); + + spin_unlock_irqrestore(&dev->slock, flags); +} + +void au0828_vbi_buffer_timeout(unsigned long data) +{ + struct au0828_dev *dev = (struct au0828_dev *) data; + struct au0828_dmaqueue *dma_q = &dev->vbiq; + struct au0828_buffer *buf; + unsigned char *vbi_data; + unsigned long flags = 0; + + spin_lock_irqsave(&dev->slock, flags); + + buf = dev->isoc_ctl.vbi_buf; + if (buf != NULL) { + vbi_data = videobuf_to_vmalloc(&buf->vb); + memset(vbi_data, 0x00, buf->vb.size); + vbi_buffer_filled(dev, dma_q, buf); + } + vbi_get_next_buf(dma_q, &buf); + + if (dev->vbi_timeout_running == 1) + mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); + spin_unlock_irqrestore(&dev->slock, flags); +} + + +static int au0828_v4l2_open(struct file *filp) +{ + int ret = 0; + struct video_device *vdev = video_devdata(filp); + struct au0828_dev *dev = video_drvdata(filp); + struct au0828_fh *fh; + int type; + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + default: + return -EINVAL; + } + + fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL); + if (NULL == fh) { + dprintk(1, "Failed allocate au0828_fh struct!\n"); + return -ENOMEM; + } + + fh->type = type; + fh->dev = dev; + filp->private_data = fh; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + /* set au0828 interface0 to AS5 here again */ + ret = usb_set_interface(dev->usbdev, 0, 5); + if (ret < 0) { + printk(KERN_INFO "Au0828 can't set alternate to 5!\n"); + return -EBUSY; + } + dev->width = NTSC_STD_W; + dev->height = NTSC_STD_H; + dev->frame_size = dev->width * dev->height * 2; + dev->field_size = dev->width * dev->height; + dev->bytesperline = dev->width * 2; + + au0828_analog_stream_enable(dev); + au0828_analog_stream_reset(dev); + + /* If we were doing ac97 instead of i2s, it would go here...*/ + au0828_i2s_init(dev); + + dev->stream_state = STREAM_OFF; + dev->dev_state |= DEV_INITIALIZED; + } + + dev->users++; + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct au0828_buffer), fh, + &dev->lock); + + /* VBI Setup */ + dev->vbi_width = 720; + dev->vbi_height = 1; + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct au0828_buffer), fh, + &dev->lock); + return ret; +} + +static int au0828_v4l2_close(struct file *filp) +{ + int ret; + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + + if (res_check(fh, AU0828_RESOURCE_VIDEO)) { + /* Cancel timeout thread in case they didn't call streamoff */ + dev->vid_timeout_running = 0; + del_timer_sync(&dev->vid_timeout); + + videobuf_stop(&fh->vb_vidq); + res_free(fh, AU0828_RESOURCE_VIDEO); + } + + if (res_check(fh, AU0828_RESOURCE_VBI)) { + /* Cancel timeout thread in case they didn't call streamoff */ + dev->vbi_timeout_running = 0; + del_timer_sync(&dev->vbi_timeout); + + videobuf_stop(&fh->vb_vbiq); + res_free(fh, AU0828_RESOURCE_VBI); + } + + if (dev->users == 1) { + if (dev->dev_state & DEV_DISCONNECTED) { + au0828_analog_unregister(dev); + kfree(dev); + return 0; + } + + au0828_analog_stream_disable(dev); + + au0828_uninit_isoc(dev); + + /* Save some power by putting tuner to sleep */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + + /* When close the device, set the usb intf0 into alt0 to free + USB bandwidth */ + ret = usb_set_interface(dev->usbdev, 0, 0); + if (ret < 0) + printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); + } + + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + return 0; +} + +static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (res_locked(dev, AU0828_RESOURCE_VIDEO)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VBI)) + return -EBUSY; + + if (dev->vbi_timeout_running == 0) { + /* Handle case where caller tries to read without + calling streamon first */ + dev->vbi_timeout_running = 1; + mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); + } + + return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + + return 0; +} + +static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { + return POLLERR; + } +} + +static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); + + return rc; +} + +static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, + struct v4l2_format *format) +{ + int ret; + int width = format->fmt.pix.width; + int height = format->fmt.pix.height; + + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* If they are demanding a format other than the one we support, + bail out (tvtime asks for UYVY and then retries with YUYV) */ + if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY) + return -EINVAL; + + /* format->fmt.pix.width only support 720 and height 480 */ + if (width != 720) + width = 720; + if (height != 480) + height = 480; + + format->fmt.pix.width = width; + format->fmt.pix.height = height; + format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + format->fmt.pix.bytesperline = width * 2; + format->fmt.pix.sizeimage = width * height * 2; + format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + format->fmt.pix.field = V4L2_FIELD_INTERLACED; + + if (cmd == VIDIOC_TRY_FMT) + return 0; + + /* maybe set new image format, driver current only support 720*480 */ + dev->width = width; + dev->height = height; + dev->frame_size = width * height * 2; + dev->field_size = width * height; + dev->bytesperline = width * 2; + + if (dev->stream_state == STREAM_ON) { + dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n"); + ret = au0828_stream_interrupt(dev); + if (ret != 0) { + dprintk(1, "error interrupting video stream!\n"); + return ret; + } + } + + /* set au0828 interface0 to AS5 here again */ + ret = usb_set_interface(dev->usbdev, 0, 5); + if (ret < 0) { + printk(KERN_INFO "Au0828 can't set alt setting to 5!\n"); + return -EBUSY; + } + + au0828_analog_stream_enable(dev); + + return 0; +} + + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); + if (qc->type) + return 0; + else + return -EINVAL; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + strlcpy(cap->driver, "au0828", sizeof(cap->driver)); + strlcpy(cap->card, dev->board.name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); + + /*set the device capabilities */ + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(f->description, "Packed YUV2"); + + f->flags = 0; + f->pixelformat = V4L2_PIX_FMT_UYVY; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + f->fmt.pix.bytesperline = dev->bytesperline; + f->fmt.pix.sizeimage = dev->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */ + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + return au0828_set_format(dev, VIDIOC_TRY_FMT, f); +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + printk(KERN_INFO "%s queue busy\n", __func__); + rc = -EBUSY; + goto out; + } + + rc = au0828_set_format(dev, VIDIOC_S_FMT, f); +out: + return rc; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) + dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); + + /* FIXME: when we support something other than NTSC, we are going to + have to make the au0828 bridge adjust the size of its capture + buffer, which is currently hardcoded at 720x480 */ + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm); + dev->std_set_in_tuner_core = 1; + + if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) + dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); + + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + unsigned int tmp; + + static const char *inames[] = { + [AU0828_VMUX_UNDEFINED] = "Undefined", + [AU0828_VMUX_COMPOSITE] = "Composite", + [AU0828_VMUX_SVIDEO] = "S-Video", + [AU0828_VMUX_CABLE] = "Cable TV", + [AU0828_VMUX_TELEVISION] = "Television", + [AU0828_VMUX_DVB] = "DVB", + [AU0828_VMUX_DEBUG] = "tv debug" + }; + + tmp = input->index; + + if (tmp >= AU0828_MAX_INPUT) + return -EINVAL; + if (AUVI_INPUT(tmp).type == 0) + return -EINVAL; + + input->index = tmp; + strcpy(input->name, inames[AUVI_INPUT(tmp).type]); + if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) || + (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE)) + input->type |= V4L2_INPUT_TYPE_TUNER; + else + input->type |= V4L2_INPUT_TYPE_CAMERA; + + input->std = dev->vdev->tvnorms; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + *i = dev->ctrl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int index) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int i; + + dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, + index); + if (index >= AU0828_MAX_INPUT) + return -EINVAL; + if (AUVI_INPUT(index).type == 0) + return -EINVAL; + dev->ctrl_input = index; + + switch (AUVI_INPUT(index).type) { + case AU0828_VMUX_SVIDEO: + dev->input_type = AU0828_VMUX_SVIDEO; + break; + case AU0828_VMUX_COMPOSITE: + dev->input_type = AU0828_VMUX_COMPOSITE; + break; + case AU0828_VMUX_TELEVISION: + dev->input_type = AU0828_VMUX_TELEVISION; + break; + default: + dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n", + AUVI_INPUT(index).type); + break; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + AUVI_INPUT(index).vmux, 0, 0); + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + int enable = 0; + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + + if (i == index) + enable = 1; + else + enable = 0; + if (enable) { + (AUVI_INPUT(i).audio_setup)(dev, enable); + } else { + /* Make sure we leave it turned on if some + other input is routed to this callback */ + if ((AUVI_INPUT(i).audio_setup) != + ((AUVI_INPUT(index).audio_setup))) { + (AUVI_INPUT(i).audio_setup)(dev, enable); + } + } + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + AUVI_INPUT(index).amux, 0, 0); + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + unsigned int index = a->index; + + if (a->index > 1) + return -EINVAL; + + index = dev->ctrl_ainput; + if (index == 0) + strcpy(a->name, "Television"); + else + strcpy(a->name, "Line in"); + + a->capability = V4L2_AUDCAP_STEREO; + a->index = index; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + if (a->index != dev->ctrl_ainput) + return -EINVAL; + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); + return 0; + +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (t->index != 0) + return -EINVAL; + + strcpy(t->name, "Auvitek tuner"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (t->index != 0) + return -EINVAL; + + t->type = V4L2_TUNER_ANALOG_TV; + + if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) + dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) + dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); + + dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal, + t->afc); + + return 0; + +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + freq->type = V4L2_TUNER_ANALOG_TV; + freq->frequency = dev->ctrl_freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (freq->tuner != 0) + return -EINVAL; + if (freq->type != V4L2_TUNER_ANALOG_TV) + return -EINVAL; + + dev->ctrl_freq = freq->frequency; + + if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) + dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); + + if (dev->std_set_in_tuner_core == 0) { + /* If we've never sent the standard in tuner core, do so now. We + don't do this at device probe because we don't want to incur + the cost of a firmware load */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, + dev->vdev->tvnorms); + dev->std_set_in_tuner_core = 1; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq); + + if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) + dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); + + au0828_analog_stream_reset(dev); + + return 0; +} + + +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + format->fmt.vbi.samples_per_line = dev->vbi_width; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; + + format->fmt.vbi.count[0] = dev->vbi_height; + format->fmt.vbi.count[1] = dev->vbi_height; + format->fmt.vbi.start[0] = 21; + format->fmt.vbi.start[1] = 284; + + return 0; +} + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = V4L2_IDENT_AU0828; + return 0; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); + if (chip->ident == V4L2_IDENT_NONE) + return -EINVAL; + + return 0; +} + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cc->bounds.left = 0; + cc->bounds.top = 0; + cc->bounds.width = dev->width; + cc->bounds.height = dev->height; + + cc->defrect = cc->bounds; + + cc->pixelaspect.numerator = 54; + cc->pixelaspect.denominator = 59; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc = -EINVAL; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (unlikely(type != fh->type)) + return -EINVAL; + + dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + au0828_analog_stream_enable(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + } + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + rc = videobuf_streamon(&fh->vb_vidq); + dev->vid_timeout_running = 1; + mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + rc = videobuf_streamon(&fh->vb_vbiq); + dev->vbi_timeout_running = 1; + mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); + } + + return rc; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + int i; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) + return -EINVAL; + if (type != fh->type) + return -EINVAL; + + dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev->vid_timeout_running = 0; + del_timer_sync(&dev->vid_timeout); + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + rc = au0828_stream_interrupt(dev); + if (rc != 0) + return rc; + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + (AUVI_INPUT(i).audio_setup)(dev, 0); + } + + if (res_check(fh, AU0828_RESOURCE_VIDEO)) { + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, AU0828_RESOURCE_VIDEO); + } + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + dev->vbi_timeout_running = 0; + del_timer_sync(&dev->vbi_timeout); + + if (res_check(fh, AU0828_RESOURCE_VBI)) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, AU0828_RESOURCE_VBI); + } + } + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + reg->val = au0828_read(dev, reg->reg); + return 0; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + return au0828_writereg(dev, reg->reg, reg->val); +} +#endif + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_reqbufs(&fh->vb_vidq, rb); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_reqbufs(&fh->vb_vbiq, rb); + + return rc; +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_querybuf(&fh->vb_vidq, b); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_querybuf(&fh->vb_vbiq, b); + + return rc; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_qbuf(&fh->vb_vidq, b); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_qbuf(&fh->vb_vbiq, b); + + return rc; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + /* Workaround for a bug in the au0828 hardware design that sometimes + results in the colorspace being inverted */ + if (dev->greenscreen_detected == 1) { + dprintk(1, "Detected green frame. Resetting stream...\n"); + au0828_analog_stream_reset(dev); + dev->greenscreen_detected = 0; + } + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK); + + return rc; +} + +static struct v4l2_file_operations au0828_v4l_fops = { + .owner = THIS_MODULE, + .open = au0828_v4l2_open, + .release = au0828_v4l2_close, + .read = au0828_v4l2_read, + .poll = au0828_v4l2_poll, + .mmap = au0828_v4l2_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .vidioc_g_chip_ident = vidioc_g_chip_ident, +}; + +static const struct video_device au0828_video_template = { + .fops = &au0828_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, +}; + +/**************************************************************************/ + +int au0828_analog_register(struct au0828_dev *dev, + struct usb_interface *interface) +{ + int retval = -ENOMEM; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i, ret; + + dprintk(1, "au0828_analog_register called!\n"); + + /* set au0828 usb interface0 to as5 */ + retval = usb_set_interface(dev->usbdev, + interface->cur_altsetting->desc.bInterfaceNumber, 5); + if (retval != 0) { + printk(KERN_INFO "Failure setting usb interface0 to as5\n"); + return retval; + } + + /* Figure out which endpoint has the isoc interface */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_ISOC)) { + + /* we find our isoc in endpoint */ + u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize); + dev->max_pkt_size = (tmp & 0x07ff) * + (((tmp & 0x1800) >> 11) + 1); + dev->isoc_in_endpointaddr = endpoint->bEndpointAddress; + } + } + if (!(dev->isoc_in_endpointaddr)) { + printk(KERN_INFO "Could not locate isoc endpoint\n"); + kfree(dev); + return -ENODEV; + } + + init_waitqueue_head(&dev->open); + spin_lock_init(&dev->slock); + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); + + dev->vid_timeout.function = au0828_vid_buffer_timeout; + dev->vid_timeout.data = (unsigned long) dev; + init_timer(&dev->vid_timeout); + + dev->vbi_timeout.function = au0828_vbi_buffer_timeout; + dev->vbi_timeout.data = (unsigned long) dev; + init_timer(&dev->vbi_timeout); + + dev->width = NTSC_STD_W; + dev->height = NTSC_STD_H; + dev->field_size = dev->width * dev->height; + dev->frame_size = dev->field_size << 1; + dev->bytesperline = dev->width << 1; + dev->ctrl_ainput = 0; + + /* allocate and fill v4l2 video struct */ + dev->vdev = video_device_alloc(); + if (NULL == dev->vdev) { + dprintk(1, "Can't allocate video_device.\n"); + return -ENOMEM; + } + + /* allocate the VBI struct */ + dev->vbi_dev = video_device_alloc(); + if (NULL == dev->vbi_dev) { + dprintk(1, "Can't allocate vbi_device.\n"); + ret = -ENOMEM; + goto err_vdev; + } + + /* Fill the video capture device struct */ + *dev->vdev = au0828_video_template; + dev->vdev->parent = &dev->usbdev->dev; + dev->vdev->lock = &dev->lock; + strcpy(dev->vdev->name, "au0828a video"); + + /* Setup the VBI device */ + *dev->vbi_dev = au0828_video_template; + dev->vbi_dev->parent = &dev->usbdev->dev; + dev->vbi_dev->lock = &dev->lock; + strcpy(dev->vbi_dev->name, "au0828a vbi"); + + /* Register the v4l2 device */ + video_set_drvdata(dev->vdev, dev); + retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1); + if (retval != 0) { + dprintk(1, "unable to register video device (error = %d).\n", + retval); + ret = -ENODEV; + goto err_vbi_dev; + } + + /* Register the vbi device */ + video_set_drvdata(dev->vbi_dev, dev); + retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1); + if (retval != 0) { + dprintk(1, "unable to register vbi device (error = %d).\n", + retval); + ret = -ENODEV; + goto err_vbi_dev; + } + + dprintk(1, "%s completed!\n", __func__); + + return 0; + +err_vbi_dev: + video_device_release(dev->vbi_dev); +err_vdev: + video_device_release(dev->vdev); + return ret; +} + diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h new file mode 100644 index 000000000000..66a56ef7bbe4 --- /dev/null +++ b/drivers/media/usb/au0828/au0828.h @@ -0,0 +1,304 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * 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. + */ + +#include +#include +#include +#include + +/* Analog */ +#include +#include +#include + +/* DVB */ +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" + +#include "au0828-reg.h" +#include "au0828-cards.h" + +#define DRIVER_NAME "au0828" +#define URB_COUNT 16 +#define URB_BUFSIZE (0xe522) + +/* Analog constants */ +#define NTSC_STD_W 720 +#define NTSC_STD_H 480 + +#define AU0828_INTERLACED_DEFAULT 1 +#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0) + +/* Defination for AU0828 USB transfer */ +#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */ +#define AU0828_ISO_PACKETS_PER_URB 128 + +#define AU0828_MIN_BUF 4 +#define AU0828_DEF_BUF 8 + +#define AU0828_MAX_INPUT 4 + +/* au0828 resource types (used for res_get/res_lock etc */ +#define AU0828_RESOURCE_VIDEO 0x01 +#define AU0828_RESOURCE_VBI 0x02 + +enum au0828_itype { + AU0828_VMUX_UNDEFINED = 0, + AU0828_VMUX_COMPOSITE, + AU0828_VMUX_SVIDEO, + AU0828_VMUX_CABLE, + AU0828_VMUX_TELEVISION, + AU0828_VMUX_DVB, + AU0828_VMUX_DEBUG +}; + +struct au0828_input { + enum au0828_itype type; + unsigned int vmux; + unsigned int amux; + void (*audio_setup) (void *priv, int enable); +}; + +struct au0828_board { + char *name; + unsigned int tuner_type; + unsigned char tuner_addr; + unsigned char i2c_clk_divider; + struct au0828_input input[AU0828_MAX_INPUT]; + +}; + +struct au0828_dvb { + struct mutex lock; + struct dvb_adapter adapter; + struct dvb_frontend *frontend; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; + int feeding; +}; + +enum au0828_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON +}; + +#define AUVI_INPUT(nr) (dev->board.input[nr]) + +/* device state */ +enum au0828_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04 +}; + +struct au0828_fh { + struct au0828_dev *dev; + unsigned int resources; + + struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; + enum v4l2_buf_type type; +}; + +struct au0828_usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct au0828_buffer *buf; + struct au0828_buffer *vbi_buf; + + /* Stores the number of received fields */ + int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb); + +}; + +/* buffer for one video frame */ +struct au0828_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct list_head frame; + int top_field; + int receiving; +}; + +struct au0828_dmaqueue { + struct list_head active; + struct list_head queued; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; +}; + +struct au0828_dev { + struct mutex mutex; + struct usb_device *usbdev; + int boardnr; + struct au0828_board board; + u8 ctrlmsg[64]; + + /* I2C */ + struct i2c_adapter i2c_adap; + struct i2c_algorithm i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* Digital */ + struct au0828_dvb dvb; + struct work_struct restart_streaming; + + /* Analog */ + struct v4l2_device v4l2_dev; + int users; + unsigned int resources; /* resources in use */ + struct video_device *vdev; + struct video_device *vbi_dev; + struct timer_list vid_timeout; + int vid_timeout_running; + struct timer_list vbi_timeout; + int vbi_timeout_running; + int width; + int height; + int vbi_width; + int vbi_height; + u32 vbi_read; + u32 field_size; + u32 frame_size; + u32 bytesperline; + int type; + u8 ctrl_ainput; + __u8 isoc_in_endpointaddr; + u8 isoc_init_ok; + int greenscreen_detected; + unsigned int frame_count; + int ctrl_freq; + int input_type; + int std_set_in_tuner_core; + unsigned int ctrl_input; + enum au0828_dev_state dev_state; + enum au0828_stream_state stream_state; + wait_queue_head_t open; + + struct mutex lock; + + /* Isoc control struct */ + struct au0828_dmaqueue vidq; + struct au0828_dmaqueue vbiq; + struct au0828_usb_isoc_ctl isoc_ctl; + spinlock_t slock; + + /* usb transfer */ + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */ + char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc + transfer */ + + /* USB / URB Related */ + int urb_streaming; + struct urb *urbs[URB_COUNT]; +}; + +/* ----------------------------------------------------------- */ +#define au0828_read(dev, reg) au0828_readreg(dev, reg) +#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value) +#define au0828_andor(dev, reg, mask, value) \ + au0828_writereg(dev, reg, \ + (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask))) + +#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit)) +#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0) + +/* ----------------------------------------------------------- */ +/* au0828-core.c */ +extern u32 au0828_read(struct au0828_dev *dev, u16 reg); +extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val); +extern int au0828_debug; + +/* ----------------------------------------------------------- */ +/* au0828-cards.c */ +extern struct au0828_board au0828_boards[]; +extern struct usb_device_id au0828_usb_id_table[]; +extern void au0828_gpio_setup(struct au0828_dev *dev); +extern int au0828_tuner_callback(void *priv, int component, + int command, int arg); +extern void au0828_card_setup(struct au0828_dev *dev); + +/* ----------------------------------------------------------- */ +/* au0828-i2c.c */ +extern int au0828_i2c_register(struct au0828_dev *dev); +extern int au0828_i2c_unregister(struct au0828_dev *dev); + +/* ----------------------------------------------------------- */ +/* au0828-video.c */ +int au0828_analog_register(struct au0828_dev *dev, + struct usb_interface *interface); +int au0828_analog_stream_disable(struct au0828_dev *d); +void au0828_analog_unregister(struct au0828_dev *dev); + +/* ----------------------------------------------------------- */ +/* au0828-dvb.c */ +extern int au0828_dvb_register(struct au0828_dev *dev); +extern void au0828_dvb_unregister(struct au0828_dev *dev); + +/* au0828-vbi.c */ +extern struct videobuf_queue_ops au0828_vbi_qops; + +#define dprintk(level, fmt, arg...)\ + do { if (au0828_debug & level)\ + printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ + } while (0) diff --git a/drivers/media/usb/cpia2/Kconfig b/drivers/media/usb/cpia2/Kconfig new file mode 100644 index 000000000000..66e9283f5993 --- /dev/null +++ b/drivers/media/usb/cpia2/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_CPIA2 + tristate "CPiA2 Video For Linux" + depends on VIDEO_DEV && USB && VIDEO_V4L2 + ---help--- + This is the video4linux driver for cameras based on Vision's CPiA2 + (Colour Processor Interface ASIC), such as the Digital Blue QX5 + Microscope. If you have one of these cameras, say Y here + + This driver is also available as a module (cpia2). diff --git a/drivers/media/usb/cpia2/Makefile b/drivers/media/usb/cpia2/Makefile new file mode 100644 index 000000000000..828cf1b1df86 --- /dev/null +++ b/drivers/media/usb/cpia2/Makefile @@ -0,0 +1,3 @@ +cpia2-objs := cpia2_v4l.o cpia2_usb.o cpia2_core.o + +obj-$(CONFIG_VIDEO_CPIA2) += cpia2.o diff --git a/drivers/media/usb/cpia2/cpia2.h b/drivers/media/usb/cpia2/cpia2.h new file mode 100644 index 000000000000..cdef677d57ec --- /dev/null +++ b/drivers/media/usb/cpia2/cpia2.h @@ -0,0 +1,487 @@ +/**************************************************************************** + * + * Filename: cpia2.h + * + * Copyright 2001, STMicrolectronics, Inc. + * + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPiA2 based video cameras. + * + * This driver is modelled on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * This program is free software; 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 __CPIA2_H__ +#define __CPIA2_H__ + +#include +#include +#include +#include +#include +#include + +#include "cpia2_registers.h" + +/* define for verbose debug output */ +//#define _CPIA2_DEBUG_ + +/*** + * Image defines + ***/ + +/* Misc constants */ +#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */ + +/* USB Transfer mode */ +#define XFER_ISOC 0 +#define XFER_BULK 1 + +/* USB Alternates */ +#define USBIF_CMDONLY 0 +#define USBIF_BULK 1 +#define USBIF_ISO_1 2 /* 128 bytes/ms */ +#define USBIF_ISO_2 3 /* 384 bytes/ms */ +#define USBIF_ISO_3 4 /* 640 bytes/ms */ +#define USBIF_ISO_4 5 /* 768 bytes/ms */ +#define USBIF_ISO_5 6 /* 896 bytes/ms */ +#define USBIF_ISO_6 7 /* 1023 bytes/ms */ + +/* Flicker Modes */ +#define NEVER_FLICKER 0 +#define FLICKER_60 60 +#define FLICKER_50 50 + +/* Debug flags */ +#define DEBUG_NONE 0 +#define DEBUG_REG 0x00000001 +#define DEBUG_DUMP_PATCH 0x00000002 +#define DEBUG_DUMP_REGS 0x00000004 + +/*** + * Video frame sizes + ***/ +enum { + VIDEOSIZE_VGA = 0, /* 640x480 */ + VIDEOSIZE_CIF, /* 352x288 */ + VIDEOSIZE_QVGA, /* 320x240 */ + VIDEOSIZE_QCIF, /* 176x144 */ + VIDEOSIZE_288_216, + VIDEOSIZE_256_192, + VIDEOSIZE_224_168, + VIDEOSIZE_192_144, +}; + +#define STV_IMAGE_CIF_ROWS 288 +#define STV_IMAGE_CIF_COLS 352 + +#define STV_IMAGE_QCIF_ROWS 144 +#define STV_IMAGE_QCIF_COLS 176 + +#define STV_IMAGE_VGA_ROWS 480 +#define STV_IMAGE_VGA_COLS 640 + +#define STV_IMAGE_QVGA_ROWS 240 +#define STV_IMAGE_QVGA_COLS 320 + +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ + +/*** + * Enums + ***/ +/* Sensor types available with cpia2 asics */ +enum sensors { + CPIA2_SENSOR_410, + CPIA2_SENSOR_500 +}; + +/* Asic types available in the CPiA2 architecture */ +#define CPIA2_ASIC_672 0x67 + +/* Device types (stv672, stv676, etc) */ +#define DEVICE_STV_672 0x0001 +#define DEVICE_STV_676 0x0002 + +enum frame_status { + FRAME_EMPTY, + FRAME_READING, /* In the process of being grabbed into */ + FRAME_READY, /* Ready to be read */ + FRAME_ERROR, +}; + +/*** + * Register access (for USB request byte) + ***/ +enum { + CAMERAACCESS_SYSTEM = 0, + CAMERAACCESS_VC, + CAMERAACCESS_VP, + CAMERAACCESS_IDATA +}; + +#define CAMERAACCESS_TYPE_BLOCK 0x00 +#define CAMERAACCESS_TYPE_RANDOM 0x04 +#define CAMERAACCESS_TYPE_MASK 0x08 +#define CAMERAACCESS_TYPE_REPEAT 0x0C + +#define TRANSFER_READ 0 +#define TRANSFER_WRITE 1 + +#define DEFAULT_ALT USBIF_ISO_6 +#define DEFAULT_BRIGHTNESS 0x46 +#define DEFAULT_CONTRAST 0x93 +#define DEFAULT_SATURATION 0x7f + +/* Power state */ +#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER +#define LO_POWER_MODE CPIA2_SYSTEM_CONTROL_LOW_POWER + + +/******** + * Commands + *******/ +enum { + CPIA2_CMD_NONE = 0, + CPIA2_CMD_GET_VERSION, + CPIA2_CMD_GET_PNP_ID, + CPIA2_CMD_GET_ASIC_TYPE, + CPIA2_CMD_GET_SENSOR, + CPIA2_CMD_GET_VP_DEVICE, + CPIA2_CMD_GET_VP_BRIGHTNESS, + CPIA2_CMD_SET_VP_BRIGHTNESS, + CPIA2_CMD_GET_CONTRAST, + CPIA2_CMD_SET_CONTRAST, + CPIA2_CMD_GET_VP_SATURATION, + CPIA2_CMD_SET_VP_SATURATION, + CPIA2_CMD_GET_VP_GPIO_DIRECTION, + CPIA2_CMD_SET_VP_GPIO_DIRECTION, + CPIA2_CMD_GET_VP_GPIO_DATA, + CPIA2_CMD_SET_VP_GPIO_DATA, + CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + CPIA2_CMD_GET_VC_MP_GPIO_DATA, + CPIA2_CMD_SET_VC_MP_GPIO_DATA, + CPIA2_CMD_ENABLE_PACKET_CTRL, + CPIA2_CMD_GET_FLICKER_MODES, + CPIA2_CMD_SET_FLICKER_MODES, + CPIA2_CMD_RESET_FIFO, /* clear fifo and enable stream block */ + CPIA2_CMD_SET_HI_POWER, + CPIA2_CMD_SET_LOW_POWER, + CPIA2_CMD_CLEAR_V2W_ERR, + CPIA2_CMD_SET_USER_MODE, + CPIA2_CMD_GET_USER_MODE, + CPIA2_CMD_FRAMERATE_REQ, + CPIA2_CMD_SET_COMPRESSION_STATE, + CPIA2_CMD_GET_WAKEUP, + CPIA2_CMD_SET_WAKEUP, + CPIA2_CMD_GET_PW_CONTROL, + CPIA2_CMD_SET_PW_CONTROL, + CPIA2_CMD_GET_SYSTEM_CTRL, + CPIA2_CMD_SET_SYSTEM_CTRL, + CPIA2_CMD_GET_VP_SYSTEM_STATE, + CPIA2_CMD_GET_VP_SYSTEM_CTRL, + CPIA2_CMD_SET_VP_SYSTEM_CTRL, + CPIA2_CMD_GET_VP_EXP_MODES, + CPIA2_CMD_SET_VP_EXP_MODES, + CPIA2_CMD_GET_DEVICE_CONFIG, + CPIA2_CMD_SET_DEVICE_CONFIG, + CPIA2_CMD_SET_SERIAL_ADDR, + CPIA2_CMD_SET_SENSOR_CR1, + CPIA2_CMD_GET_VC_CONTROL, + CPIA2_CMD_SET_VC_CONTROL, + CPIA2_CMD_SET_TARGET_KB, + CPIA2_CMD_SET_DEF_JPEG_OPT, + CPIA2_CMD_REHASH_VP4, + CPIA2_CMD_GET_USER_EFFECTS, + CPIA2_CMD_SET_USER_EFFECTS +}; + +enum user_cmd { + COMMAND_NONE = 0x00000001, + COMMAND_SET_FPS = 0x00000002, + COMMAND_SET_COLOR_PARAMS = 0x00000004, + COMMAND_GET_COLOR_PARAMS = 0x00000008, + COMMAND_SET_FORMAT = 0x00000010, /* size, etc */ + COMMAND_SET_FLICKER = 0x00000020 +}; + +/*** + * Some defines specific to the 676 chip + ***/ +#define CAMACC_CIF 0x01 +#define CAMACC_VGA 0x02 +#define CAMACC_QCIF 0x04 +#define CAMACC_QVGA 0x08 + + +struct cpia2_register { + u8 index; + u8 value; +}; + +struct cpia2_reg_mask { + u8 index; + u8 and_mask; + u8 or_mask; + u8 fill; +}; + +struct cpia2_command { + u32 command; + u8 req_mode; /* (Block or random) | registerBank */ + u8 reg_count; + u8 direction; + u8 start; + union reg_types { + struct cpia2_register registers[32]; + struct cpia2_reg_mask masks[16]; + u8 block_data[64]; + u8 *patch_data; /* points to function defined block */ + } buffer; +}; + +struct camera_params { + struct { + u8 firmware_revision_hi; /* For system register set (bank 0) */ + u8 firmware_revision_lo; + u8 asic_id; /* Video Compressor set (bank 1) */ + u8 asic_rev; + u8 vp_device_hi; /* Video Processor set (bank 2) */ + u8 vp_device_lo; + u8 sensor_flags; + u8 sensor_rev; + } version; + + struct { + u32 device_type; /* enumerated from vendor/product ids. + * Currently, either STV_672 or STV_676 */ + u16 vendor; + u16 product; + u16 device_revision; + } pnp_id; + + struct { + u8 brightness; /* CPIA2_VP_EXPOSURE_TARGET */ + u8 contrast; /* Note: this is CPIA2_VP_YRANGE */ + u8 saturation; /* CPIA2_VP_SATURATION */ + } color_params; + + struct { + u8 cam_register; + u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ + } flicker_control; + + struct { + u8 jpeg_options; + u8 creep_period; + u8 user_squeeze; + u8 inhibit_htables; + } compression; + + struct { + u8 ohsize; /* output image size */ + u8 ovsize; + u8 hcrop; /* cropping start_pos/4 */ + u8 vcrop; + u8 hphase; /* scaling registers */ + u8 vphase; + u8 hispan; + u8 vispan; + u8 hicrop; + u8 vicrop; + u8 hifraction; + u8 vifraction; + } image_size; + + struct { + int width; /* actual window width */ + int height; /* actual window height */ + } roi; + + struct { + u8 video_mode; + u8 frame_rate; + u8 video_size; /* Not a register, just a convenience for cropped sizes */ + u8 gpio_direction; + u8 gpio_data; + u8 system_ctrl; + u8 system_state; + u8 lowlight_boost; /* Bool: 0 = off, 1 = on */ + u8 device_config; + u8 exposure_modes; + u8 user_effects; + } vp_params; + + struct { + u8 pw_control; + u8 wakeup; + u8 vc_control; + u8 vc_mp_direction; + u8 vc_mp_data; + u8 quality; + } vc_params; + + struct { + u8 power_mode; + u8 system_ctrl; + u8 stream_mode; /* This is the current alternate for usb drivers */ + u8 allow_corrupt; + } camera_state; +}; + +#define NUM_SBUF 2 + +struct cpia2_sbuf { + char *data; + struct urb *urb; +}; + +struct framebuf { + struct timeval timestamp; + unsigned long seq; + int num; + int length; + int max_length; + volatile enum frame_status status; + u8 *data; + struct framebuf *next; +}; + +struct camera_data { + /* locks */ + struct v4l2_device v4l2_dev; + struct mutex v4l2_lock; /* serialize file operations */ + struct v4l2_ctrl_handler hdl; + struct { + /* Lights control cluster */ + struct v4l2_ctrl *top_light; + struct v4l2_ctrl *bottom_light; + }; + struct v4l2_ctrl *usb_alt; + + /* camera status */ + int first_image_seen; + enum sensors sensor_type; + u8 flush; + struct v4l2_fh *stream_fh; + u8 mmapped; + int streaming; /* 0 = no, 1 = yes */ + int xfer_mode; /* XFER_BULK or XFER_ISOC */ + struct camera_params params; /* camera settings */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + struct video_device vdev; /* v4l videodev */ + u32 width; + u32 height; /* Its size */ + __u32 pixelformat; /* Format fourcc */ + + /* USB */ + struct usb_device *dev; + unsigned char iface; + unsigned int cur_alt; + unsigned int old_alt; + struct cpia2_sbuf sbuf[NUM_SBUF]; /* Double buffering */ + + wait_queue_head_t wq_stream; + + /* Buffering */ + u32 frame_size; + int num_frames; + unsigned long frame_count; + u8 *frame_buffer; /* frame buffer data */ + struct framebuf *buffers; + struct framebuf * volatile curbuff; + struct framebuf *workbuff; + + /* MJPEG Extension */ + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ +}; + +/* v4l */ +int cpia2_register_camera(struct camera_data *cam); +void cpia2_unregister_camera(struct camera_data *cam); +void cpia2_camera_release(struct v4l2_device *v4l2_dev); + +/* core */ +int cpia2_reset_camera(struct camera_data *cam); +int cpia2_set_low_power(struct camera_data *cam); +void cpia2_dbg_dump_registers(struct camera_data *cam); +int cpia2_match_video_size(int width, int height); +void cpia2_set_camera_state(struct camera_data *cam); +void cpia2_save_camera_state(struct camera_data *cam); +void cpia2_set_color_params(struct camera_data *cam); +void cpia2_set_brightness(struct camera_data *cam, unsigned char value); +void cpia2_set_contrast(struct camera_data *cam, unsigned char value); +void cpia2_set_saturation(struct camera_data *cam, unsigned char value); +int cpia2_set_flicker_mode(struct camera_data *cam, int mode); +void cpia2_set_format(struct camera_data *cam); +int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); +int cpia2_do_command(struct camera_data *cam, + unsigned int command, + unsigned char direction, unsigned char param); +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); +int cpia2_init_camera(struct camera_data *cam); +int cpia2_allocate_buffers(struct camera_data *cam); +void cpia2_free_buffers(struct camera_data *cam); +long cpia2_read(struct camera_data *cam, + char __user *buf, unsigned long count, int noblock); +unsigned int cpia2_poll(struct camera_data *cam, + struct file *filp, poll_table *wait); +int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); +void cpia2_set_property_flip(struct camera_data *cam, int prop_val); +void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); +int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); +int cpia2_set_fps(struct camera_data *cam, int framerate); + +/* usb */ +int cpia2_usb_init(void); +void cpia2_usb_cleanup(void); +int cpia2_usb_transfer_cmd(struct camera_data *cam, void *registers, + u8 request, u8 start, u8 count, u8 direction); +int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate); +int cpia2_usb_stream_stop(struct camera_data *cam); +int cpia2_usb_stream_pause(struct camera_data *cam); +int cpia2_usb_stream_resume(struct camera_data *cam); +int cpia2_usb_change_streaming_alternate(struct camera_data *cam, + unsigned int alt); + + +/* ----------------------- debug functions ---------------------- */ +#ifdef _CPIA2_DEBUG_ +#define ALOG(lev, fmt, args...) printk(lev "%s:%d %s(): " fmt, __FILE__, __LINE__, __func__, ## args) +#define LOG(fmt, args...) ALOG(KERN_INFO, fmt, ## args) +#define ERR(fmt, args...) ALOG(KERN_ERR, fmt, ## args) +#define DBG(fmt, args...) ALOG(KERN_DEBUG, fmt, ## args) +#else +#define ALOG(fmt,args...) printk(fmt,##args) +#define LOG(fmt,args...) ALOG(KERN_INFO "cpia2: "fmt,##args) +#define ERR(fmt,args...) ALOG(KERN_ERR "cpia2: "fmt,##args) +#define DBG(fmn,args...) do {} while(0) +#endif +/* No function or lineno, for shorter lines */ +#define KINFO(fmt, args...) printk(KERN_INFO fmt,##args) + +#endif diff --git a/drivers/media/usb/cpia2/cpia2_core.c b/drivers/media/usb/cpia2/cpia2_core.c new file mode 100644 index 000000000000..187012ce444b --- /dev/null +++ b/drivers/media/usb/cpia2/cpia2_core.c @@ -0,0 +1,2417 @@ +/**************************************************************************** + * + * Filename: cpia2_core.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * This program is free software; 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. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox + * + ****************************************************************************/ + +#include "cpia2.h" + +#include +#include +#include +#include +#include + +#define FIRMWARE "cpia2/stv0672_vp4.bin" +MODULE_FIRMWARE(FIRMWARE); + +/* #define _CPIA2_DEBUG_ */ + +#ifdef _CPIA2_DEBUG_ + +static const char *block_name[] = { + "System", + "VC", + "VP", + "IDATA" +}; +#endif + +static unsigned int debugs_on; /* default 0 - DEBUG_REG */ + + +/****************************************************************************** + * + * Forward Declarations + * + *****************************************************************************/ +static int apply_vp_patch(struct camera_data *cam); +static int set_default_user_mode(struct camera_data *cam); +static int set_vw_size(struct camera_data *cam, int size); +static int configure_sensor(struct camera_data *cam, + int reqwidth, int reqheight); +static int config_sensor_410(struct camera_data *cam, + int reqwidth, int reqheight); +static int config_sensor_500(struct camera_data *cam, + int reqwidth, int reqheight); +static int set_all_properties(struct camera_data *cam); +static void wake_system(struct camera_data *cam); +static void set_lowlight_boost(struct camera_data *cam); +static void reset_camera_struct(struct camera_data *cam); +static int cpia2_set_high_power(struct camera_data *cam); + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + /* Round it off to PAGE_SIZE */ + size = PAGE_ALIGN(size); + + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + + while ((long)size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + size = PAGE_ALIGN(size); + + adr = (unsigned long) mem; + while ((long)size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +/****************************************************************************** + * + * cpia2_do_command + * + * Send an arbitrary command to the camera. For commands that read from + * the camera, copy the buffers into the proper param structures. + *****************************************************************************/ +int cpia2_do_command(struct camera_data *cam, + u32 command, u8 direction, u8 param) +{ + int retval = 0; + struct cpia2_command cmd; + unsigned int device = cam->params.pnp_id.device_type; + + cmd.command = command; + cmd.reg_count = 2; /* default */ + cmd.direction = direction; + + /*** + * Set up the command. + ***/ + switch (command) { + case CPIA2_CMD_GET_VERSION: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.start = CPIA2_SYSTEM_DEVICE_HI; + break; + case CPIA2_CMD_GET_PNP_ID: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 8; + cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI; + break; + case CPIA2_CMD_GET_ASIC_TYPE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_ASIC_ID; + break; + case CPIA2_CMD_GET_SENSOR: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.start = CPIA2_VP_SENSOR_FLAGS; + break; + case CPIA2_CMD_GET_VP_DEVICE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.start = CPIA2_VP_DEVICEH; + break; + case CPIA2_CMD_SET_VP_BRIGHTNESS: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VP_BRIGHTNESS: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_EXPOSURE_TARGET; + else + cmd.start = CPIA2_VP5_EXPOSURE_TARGET; + break; + case CPIA2_CMD_SET_CONTRAST: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_CONTRAST: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_YRANGE; + break; + case CPIA2_CMD_SET_VP_SATURATION: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VP_SATURATION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP_SATURATION; + else + cmd.start = CPIA2_VP5_MCUVSATURATION; + break; + case CPIA2_CMD_SET_VP_GPIO_DATA: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VP_GPIO_DATA: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_GPIO_DATA; + break; + case CPIA2_CMD_SET_VP_GPIO_DIRECTION: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VP_GPIO_DIRECTION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_GPIO_DIRECTION; + break; + case CPIA2_CMD_SET_VC_MP_GPIO_DATA: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VC_MP_GPIO_DATA: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_MP_DATA; + break; + case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_MP_DIR; + break; + case CPIA2_CMD_ENABLE_PACKET_CTRL: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL; + cmd.reg_count = 1; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_FLICKER_MODES: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_FLICKER_MODES: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_FLICKER_MODES; + break; + case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 2; + cmd.start = 0; + cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; + cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | + CPIA2_VC_ST_CTRL_EOF_DETECT | + CPIA2_VC_ST_CTRL_FIFO_ENABLE; + break; + case CPIA2_CMD_SET_HI_POWER: + cmd.req_mode = + CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; + cmd.reg_count = 2; + cmd.buffer.registers[0].index = + CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.registers[1].index = + CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; + cmd.buffer.registers[1].value = + CPIA2_SYSTEM_CONTROL_HIGH_POWER; + break; + case CPIA2_CMD_SET_LOW_POWER: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.block_data[0] = 0; + break; + case CPIA2_CMD_CLEAR_V2W_ERR: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; + break; + case CPIA2_CMD_SET_USER_MODE: /* Then fall through */ + cmd.buffer.block_data[0] = param; + case CPIA2_CMD_GET_USER_MODE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_USER_MODE; + else + cmd.start = CPIA2_VP5_USER_MODE; + break; + case CPIA2_CMD_FRAMERATE_REQ: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_FRAMERATE_REQUEST; + else + cmd.start = CPIA2_VP5_FRAMERATE_REQUEST; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_WAKEUP: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_WAKEUP: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_WAKEUP; + break; + case CPIA2_CMD_SET_PW_CONTROL: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_PW_CONTROL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_PW_CTRL; + break; + case CPIA2_CMD_GET_VP_SYSTEM_STATE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_SYSTEMSTATE; + break; + case CPIA2_CMD_SET_SYSTEM_CTRL: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_SYSTEM_CTRL: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + break; + case CPIA2_CMD_SET_VP_SYSTEM_CTRL: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VP_SYSTEM_CTRL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_SYSTEMCTRL; + break; + case CPIA2_CMD_SET_VP_EXP_MODES: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VP_EXP_MODES: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_EXPOSURE_MODES; + break; + case CPIA2_CMD_SET_DEVICE_CONFIG: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_DEVICE_CONFIG: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_DEVICE_CONFIG; + break; + case CPIA2_CMD_SET_SERIAL_ADDR: + cmd.buffer.block_data[0] = param; + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR; + break; + case CPIA2_CMD_SET_SENSOR_CR1: + cmd.buffer.block_data[0] = param; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_SENSOR_CR1; + break; + case CPIA2_CMD_SET_VC_CONTROL: + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_VC_CONTROL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_VC_CTRL; + break; + case CPIA2_CMD_SET_TARGET_KB: + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB; + cmd.buffer.registers[0].value = param; + break; + case CPIA2_CMD_SET_DEF_JPEG_OPT: + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 4; + cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT; + cmd.buffer.registers[0].value = + CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE; + cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE; + cmd.buffer.registers[1].value = 20; + cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD; + cmd.buffer.registers[2].value = 2; + cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT; + cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT; + break; + case CPIA2_CMD_REHASH_VP4: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_REHASH_VALUES; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as + this register can also affect + flicker modes */ + cmd.buffer.block_data[0] = param; /* Then fall through */ + case CPIA2_CMD_GET_USER_EFFECTS: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_USER_EFFECTS; + else + cmd.start = CPIA2_VP5_USER_EFFECTS; + break; + default: + LOG("DoCommand received invalid command\n"); + return -EINVAL; + } + + retval = cpia2_send_command(cam, &cmd); + if (retval) { + return retval; + } + + /*** + * Now copy any results from a read into the appropriate param struct. + ***/ + switch (command) { + case CPIA2_CMD_GET_VERSION: + cam->params.version.firmware_revision_hi = + cmd.buffer.block_data[0]; + cam->params.version.firmware_revision_lo = + cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_PNP_ID: + cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) | + cmd.buffer.block_data[1]; + cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) | + cmd.buffer.block_data[3]; + cam->params.pnp_id.device_revision = + (cmd.buffer.block_data[4] << 8) | + cmd.buffer.block_data[5]; + if (cam->params.pnp_id.vendor == 0x553) { + if (cam->params.pnp_id.product == 0x100) { + cam->params.pnp_id.device_type = DEVICE_STV_672; + } else if (cam->params.pnp_id.product == 0x140 || + cam->params.pnp_id.product == 0x151) { + cam->params.pnp_id.device_type = DEVICE_STV_676; + } + } + break; + case CPIA2_CMD_GET_ASIC_TYPE: + cam->params.version.asic_id = cmd.buffer.block_data[0]; + cam->params.version.asic_rev = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_SENSOR: + cam->params.version.sensor_flags = cmd.buffer.block_data[0]; + cam->params.version.sensor_rev = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_VP_DEVICE: + cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; + cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_VP_GPIO_DATA: + cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_GPIO_DIRECTION: + cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: + cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_MP_GPIO_DATA: + cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_FLICKER_MODES: + cam->params.flicker_control.cam_register = + cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_WAKEUP: + cam->params.vc_params.wakeup = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_PW_CONTROL: + cam->params.vc_params.pw_control = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_SYSTEM_CTRL: + cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_SYSTEM_STATE: + cam->params.vp_params.system_state = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_SYSTEM_CTRL: + cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_EXP_MODES: + cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_DEVICE_CONFIG: + cam->params.vp_params.device_config = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_CONTROL: + cam->params.vc_params.vc_control = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_USER_MODE: + cam->params.vp_params.video_mode = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_USER_EFFECTS: + cam->params.vp_params.user_effects = cmd.buffer.block_data[0]; + break; + default: + break; + } + return retval; +} + +/****************************************************************************** + * + * cpia2_send_command + * + *****************************************************************************/ + +#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read") +#define BINDEX(cmd) (cmd->req_mode & 0x03) + +int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) +{ + u8 count; + u8 start; + u8 *buffer; + int retval; + + switch (cmd->req_mode & 0x0c) { + case CAMERAACCESS_TYPE_RANDOM: + count = cmd->reg_count * sizeof(struct cpia2_register); + start = 0; + buffer = (u8 *) & cmd->buffer; + if (debugs_on & DEBUG_REG) + DBG("%s Random: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_BLOCK: + count = cmd->reg_count; + start = cmd->start; + buffer = cmd->buffer.block_data; + if (debugs_on & DEBUG_REG) + DBG("%s Block: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_MASK: + count = cmd->reg_count * sizeof(struct cpia2_reg_mask); + start = 0; + buffer = (u8 *) & cmd->buffer; + if (debugs_on & DEBUG_REG) + DBG("%s Mask: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */ + count = cmd->reg_count; + start = cmd->start; + buffer = cmd->buffer.block_data; + if (debugs_on & DEBUG_REG) + DBG("%s Repeat: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + default: + LOG("%s: invalid request mode\n",__func__); + return -EINVAL; + } + + retval = cpia2_usb_transfer_cmd(cam, + buffer, + cmd->req_mode, + start, count, cmd->direction); +#ifdef _CPIA2_DEBUG_ + if (debugs_on & DEBUG_REG) { + int i; + for (i = 0; i < cmd->reg_count; i++) { + if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK) + KINFO("%s Block: [0x%02X] = 0x%02X\n", + DIR(cmd), start + i, buffer[i]); + if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM) + KINFO("%s Random: [0x%02X] = 0x%02X\n", + DIR(cmd), cmd->buffer.registers[i].index, + cmd->buffer.registers[i].value); + } + } +#endif + + return retval; +}; + +/************* + * Functions to implement camera functionality + *************/ +/****************************************************************************** + * + * cpia2_get_version_info + * + *****************************************************************************/ +static void cpia2_get_version_info(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0); +} + +/****************************************************************************** + * + * cpia2_reset_camera + * + * Called at least during the open process, sets up initial params. + *****************************************************************************/ +int cpia2_reset_camera(struct camera_data *cam) +{ + u8 tmp_reg; + int retval = 0; + int target_kb; + int i; + struct cpia2_command cmd; + + /*** + * VC setup + ***/ + retval = configure_sensor(cam, + cam->params.roi.width, + cam->params.roi.height); + if (retval < 0) { + ERR("Couldn't configure sensor, error=%d\n", retval); + return retval; + } + + /* Clear FIFO and route/enable stream block */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + cmd.reg_count = 2; + cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; + cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | + CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE; + + cpia2_send_command(cam, &cmd); + + cpia2_set_high_power(cam); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + /* Enable button notification */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; + cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL; + cmd.buffer.registers[0].value = + CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + } + + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + retval = apply_vp_patch(cam); + + /* wait for vp to go to sleep */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + /*** + * If this is a 676, apply VP5 fixes before we start streaming + ***/ + if (cam->params.pnp_id.device_type == DEVICE_STV_676) { + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + + /* The following writes improve the picture */ + cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL; + cmd.buffer.registers[0].value = 0; /* reduce from the default + * rec 601 pedestal of 16 */ + cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE; + cmd.buffer.registers[1].value = 0x92; /* increase from 100% to + * (256/256 - 31) to fill + * available range */ + cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING; + cmd.buffer.registers[2].value = 0xFF; /* Increase from the + * default rec 601 ceiling + * of 240 */ + cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION; + cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec + * 601 100% level (128) + * to 145-192 */ + cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP; + cmd.buffer.registers[4].value = 0x80; /* Inhibit the + * anti-flicker */ + + /* The following 4 writes are a fix to allow QVGA to work at 30 fps */ + cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H; + cmd.buffer.registers[5].value = 0x01; + cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L; + cmd.buffer.registers[6].value = 0xE3; + cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA; + cmd.buffer.registers[7].value = 0x02; + cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA; + cmd.buffer.registers[8].value = 0xFC; + + cmd.direction = TRANSFER_WRITE; + cmd.reg_count = 9; + + cpia2_send_command(cam, &cmd); + } + + /* Activate all settings and start the data stream */ + /* Set user mode */ + set_default_user_mode(cam); + + /* Give VP time to wake up */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + set_all_properties(cam); + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); + DBG("After SetAllProperties(cam), user mode is 0x%0X\n", + cam->params.vp_params.video_mode); + + /*** + * Set audio regulator off. This and the code to set the compresison + * state are too complex to form a CPIA2_CMD_, and seem to be somewhat + * intertwined. This stuff came straight from the windows driver. + ***/ + /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */ + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); + tmp_reg = cam->params.vp_params.system_ctrl; + cmd.buffer.registers[0].value = tmp_reg & + (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF)); + + cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); + cmd.buffer.registers[1].value = cam->params.vp_params.device_config | + CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE; + cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL; + cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG; + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + cmd.reg_count = 2; + cmd.direction = TRANSFER_WRITE; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + + /* Set the correct I2C address in the CPiA-2 system register */ + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR); + + /* Now have sensor access - set bit to turn the audio regulator off */ + cpia2_do_command(cam, + CPIA2_CMD_SET_SENSOR_CR1, + TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR); + + /* Set the correct I2C address in the CPiA-2 system register */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88 + else + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a + + /* increase signal drive strength */ + if (cam->params.pnp_id.device_type == DEVICE_STV_676) + cpia2_do_command(cam, + CPIA2_CMD_SET_VP_EXP_MODES, + TRANSFER_WRITE, + CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP); + + /* Start autoexposure */ + cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); + cmd.buffer.registers[0].value = cam->params.vp_params.device_config & + (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF); + + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); + cmd.buffer.registers[1].value = + cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL; + + cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG; + cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL; + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + cmd.reg_count = 2; + cmd.direction = TRANSFER_WRITE; + + cpia2_send_command(cam, &cmd); + + /* Set compression state */ + cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0); + if (cam->params.compression.inhibit_htables) { + tmp_reg = cam->params.vc_params.vc_control | + CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; + } else { + tmp_reg = cam->params.vc_params.vc_control & + ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; + } + cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); + + /* Set target size (kb) on vc + This is a heuristic based on the quality parameter and the raw + framesize in kB divided by 16 (the compression factor when the + quality is 100%) */ + target_kb = (cam->width * cam->height * 2 / 16384) * + cam->params.vc_params.quality / 100; + if (target_kb < 1) + target_kb = 1; + cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, + TRANSFER_WRITE, target_kb); + + /* Wiggle VC Reset */ + /*** + * First read and wait a bit. + ***/ + for (i = 0; i < 50; i++) { + cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL, + TRANSFER_READ, 0); + } + + tmp_reg = cam->params.vc_params.pw_control; + tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N; + + cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); + + tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N; + cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); + + cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0); + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); + DBG("After VC RESET, user mode is 0x%0X\n", + cam->params.vp_params.video_mode); + + return retval; +} + +/****************************************************************************** + * + * cpia2_set_high_power + * + *****************************************************************************/ +static int cpia2_set_high_power(struct camera_data *cam) +{ + int i; + for (i = 0; i <= 50; i++) { + /* Read system status */ + cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0); + + /* If there is an error, clear it */ + if(cam->params.camera_state.system_ctrl & + CPIA2_SYSTEM_CONTROL_V2W_ERR) + cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR, + TRANSFER_WRITE, 0); + + /* Try to set high power mode */ + cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, + TRANSFER_WRITE, 1); + + /* Try to read something in VP to check if everything is awake */ + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE, + TRANSFER_READ, 0); + if (cam->params.vp_params.system_state & + CPIA2_VP_SYSTEMSTATE_HK_ALIVE) { + break; + } else if (i == 50) { + cam->params.camera_state.power_mode = LO_POWER_MODE; + ERR("Camera did not wake up\n"); + return -EIO; + } + } + + DBG("System now in high power state\n"); + cam->params.camera_state.power_mode = HI_POWER_MODE; + return 0; +} + +/****************************************************************************** + * + * cpia2_set_low_power + * + *****************************************************************************/ +int cpia2_set_low_power(struct camera_data *cam) +{ + cam->params.camera_state.power_mode = LO_POWER_MODE; + cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0); + return 0; +} + +/****************************************************************************** + * + * apply_vp_patch + * + *****************************************************************************/ +static int cpia2_send_onebyte_command(struct camera_data *cam, + struct cpia2_command *cmd, + u8 start, u8 datum) +{ + cmd->buffer.block_data[0] = datum; + cmd->start = start; + cmd->reg_count = 1; + return cpia2_send_command(cam, cmd); +} + +static int apply_vp_patch(struct camera_data *cam) +{ + const struct firmware *fw; + const char fw_name[] = FIRMWARE; + int i, ret; + struct cpia2_command cmd; + + ret = request_firmware(&fw, fw_name, &cam->dev->dev); + if (ret) { + printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n", + fw_name); + return ret; + } + + cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP; + cmd.direction = TRANSFER_WRITE; + + /* First send the start address... */ + cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ + cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ + + /* ... followed by the data payload */ + for (i = 2; i < fw->size; i += 64) { + cmd.start = 0x0C; /* Data */ + cmd.reg_count = min_t(int, 64, fw->size - i); + memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count); + cpia2_send_command(cam, &cmd); + } + + /* Next send the start address... */ + cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ + cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ + + /* ... followed by the 'goto' command */ + cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1); + + release_firmware(fw); + return 0; +} + +/****************************************************************************** + * + * set_default_user_mode + * + *****************************************************************************/ +static int set_default_user_mode(struct camera_data *cam) +{ + unsigned char user_mode; + unsigned char frame_rate; + int width = cam->params.roi.width; + int height = cam->params.roi.height; + + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + case CPIA2_VP_SENSOR_FLAGS_407: + case CPIA2_VP_SENSOR_FLAGS_409: + case CPIA2_VP_SENSOR_FLAGS_410: + if ((width > STV_IMAGE_QCIF_COLS) + || (height > STV_IMAGE_QCIF_ROWS)) { + user_mode = CPIA2_VP_USER_MODE_CIF; + } else { + user_mode = CPIA2_VP_USER_MODE_QCIFDS; + } + frame_rate = CPIA2_VP_FRAMERATE_30; + break; + case CPIA2_VP_SENSOR_FLAGS_500: + if ((width > STV_IMAGE_CIF_COLS) + || (height > STV_IMAGE_CIF_ROWS)) { + user_mode = CPIA2_VP_USER_MODE_VGA; + } else { + user_mode = CPIA2_VP_USER_MODE_QVGADS; + } + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + frame_rate = CPIA2_VP_FRAMERATE_15; + else + frame_rate = CPIA2_VP_FRAMERATE_30; + break; + default: + LOG("%s: Invalid sensor flag value 0x%0X\n",__func__, + cam->params.version.sensor_flags); + return -EINVAL; + } + + DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n", + cam->params.version.sensor_flags, user_mode, frame_rate); + cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE, + user_mode); + if(cam->params.vp_params.frame_rate > 0 && + frame_rate > cam->params.vp_params.frame_rate) + frame_rate = cam->params.vp_params.frame_rate; + + cpia2_set_fps(cam, frame_rate); + +// if (cam->params.pnp_id.device_type == DEVICE_STV_676) +// cpia2_do_command(cam, +// CPIA2_CMD_SET_VP_SYSTEM_CTRL, +// TRANSFER_WRITE, +// CPIA2_VP_SYSTEMCTRL_HK_CONTROL | +// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL); + + return 0; +} + +/****************************************************************************** + * + * cpia2_match_video_size + * + * return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. + *****************************************************************************/ +int cpia2_match_video_size(int width, int height) +{ + if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS) + return VIDEOSIZE_VGA; + + if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS) + return VIDEOSIZE_CIF; + + if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS) + return VIDEOSIZE_QVGA; + + if (width >= 288 && height >= 216) + return VIDEOSIZE_288_216; + + if (width >= 256 && height >= 192) + return VIDEOSIZE_256_192; + + if (width >= 224 && height >= 168) + return VIDEOSIZE_224_168; + + if (width >= 192 && height >= 144) + return VIDEOSIZE_192_144; + + if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS) + return VIDEOSIZE_QCIF; + + return -1; +} + +/****************************************************************************** + * + * SetVideoSize + * + *****************************************************************************/ +static int set_vw_size(struct camera_data *cam, int size) +{ + int retval = 0; + + cam->params.vp_params.video_size = size; + + switch (size) { + case VIDEOSIZE_VGA: + DBG("Setting size to VGA\n"); + cam->params.roi.width = STV_IMAGE_VGA_COLS; + cam->params.roi.height = STV_IMAGE_VGA_ROWS; + cam->width = STV_IMAGE_VGA_COLS; + cam->height = STV_IMAGE_VGA_ROWS; + break; + case VIDEOSIZE_CIF: + DBG("Setting size to CIF\n"); + cam->params.roi.width = STV_IMAGE_CIF_COLS; + cam->params.roi.height = STV_IMAGE_CIF_ROWS; + cam->width = STV_IMAGE_CIF_COLS; + cam->height = STV_IMAGE_CIF_ROWS; + break; + case VIDEOSIZE_QVGA: + DBG("Setting size to QVGA\n"); + cam->params.roi.width = STV_IMAGE_QVGA_COLS; + cam->params.roi.height = STV_IMAGE_QVGA_ROWS; + cam->width = STV_IMAGE_QVGA_COLS; + cam->height = STV_IMAGE_QVGA_ROWS; + break; + case VIDEOSIZE_288_216: + cam->params.roi.width = 288; + cam->params.roi.height = 216; + cam->width = 288; + cam->height = 216; + break; + case VIDEOSIZE_256_192: + cam->width = 256; + cam->height = 192; + cam->params.roi.width = 256; + cam->params.roi.height = 192; + break; + case VIDEOSIZE_224_168: + cam->width = 224; + cam->height = 168; + cam->params.roi.width = 224; + cam->params.roi.height = 168; + break; + case VIDEOSIZE_192_144: + cam->width = 192; + cam->height = 144; + cam->params.roi.width = 192; + cam->params.roi.height = 144; + break; + case VIDEOSIZE_QCIF: + DBG("Setting size to QCIF\n"); + cam->params.roi.width = STV_IMAGE_QCIF_COLS; + cam->params.roi.height = STV_IMAGE_QCIF_ROWS; + cam->width = STV_IMAGE_QCIF_COLS; + cam->height = STV_IMAGE_QCIF_ROWS; + break; + default: + retval = -EINVAL; + } + return retval; +} + +/****************************************************************************** + * + * configure_sensor + * + *****************************************************************************/ +static int configure_sensor(struct camera_data *cam, + int req_width, int req_height) +{ + int retval; + + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + case CPIA2_VP_SENSOR_FLAGS_407: + case CPIA2_VP_SENSOR_FLAGS_409: + case CPIA2_VP_SENSOR_FLAGS_410: + retval = config_sensor_410(cam, req_width, req_height); + break; + case CPIA2_VP_SENSOR_FLAGS_500: + retval = config_sensor_500(cam, req_width, req_height); + break; + default: + return -EINVAL; + } + + return retval; +} + +/****************************************************************************** + * + * config_sensor_410 + * + *****************************************************************************/ +static int config_sensor_410(struct camera_data *cam, + int req_width, int req_height) +{ + struct cpia2_command cmd; + int i = 0; + int image_size; + int image_type; + int width = req_width; + int height = req_height; + + /*** + * Make sure size doesn't exceed CIF. + ***/ + if (width > STV_IMAGE_CIF_COLS) + width = STV_IMAGE_CIF_COLS; + if (height > STV_IMAGE_CIF_ROWS) + height = STV_IMAGE_CIF_ROWS; + + image_size = cpia2_match_video_size(width, height); + + DBG("Config 410: width = %d, height = %d\n", width, height); + DBG("Image size returned is %d\n", image_size); + if (image_size >= 0) { + set_vw_size(cam, image_size); + width = cam->params.roi.width; + height = cam->params.roi.height; + + DBG("After set_vw_size(), width = %d, height = %d\n", + width, height); + if (width <= 176 && height <= 144) { + DBG("image type = VIDEOSIZE_QCIF\n"); + image_type = VIDEOSIZE_QCIF; + } + else if (width <= 320 && height <= 240) { + DBG("image type = VIDEOSIZE_QVGA\n"); + image_type = VIDEOSIZE_QVGA; + } + else { + DBG("image type = VIDEOSIZE_CIF\n"); + image_type = VIDEOSIZE_CIF; + } + } else { + ERR("ConfigSensor410 failed\n"); + return -EINVAL; + } + + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + + /* VC Format */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; + if (image_type == VIDEOSIZE_CIF) { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_FORMAT_UFIRST | + CPIA2_VC_VC_FORMAT_SHORTLINE); + } else { + cmd.buffer.registers[i++].value = + (u8) CPIA2_VC_VC_FORMAT_UFIRST; + } + + /* VC Clocks */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; + if (image_type == VIDEOSIZE_QCIF) { + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.buffer.registers[i++].value= + (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_672_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + DBG("VC_Clocks (0xc4) should be B\n"); + } + else { + cmd.buffer.registers[i++].value= + (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + } + } else { + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_CLOCKS_LOGDIV0); + } + else { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_676_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV0); + } + } + DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value); + + /* Input reqWidth from VC */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (STV_IMAGE_QCIF_COLS / 4); + else + cmd.buffer.registers[i++].value = + (u8) (STV_IMAGE_CIF_COLS / 4); + + /* Timings */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 208; + else + cmd.buffer.registers[i++].value = (u8) 160; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 160; + else + cmd.buffer.registers[i++].value = (u8) 64; + + /* Output Image Size */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; + cmd.buffer.registers[i++].value = cam->params.roi.width / 4; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; + cmd.buffer.registers[i++].value = cam->params.roi.height / 4; + + /* Cropping */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); + else + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); + else + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); + + /* Scaling registers (defaults) */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; + cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; + cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ + + cmd.reg_count = i; + + cpia2_send_command(cam, &cmd); + + return i; +} + + +/****************************************************************************** + * + * config_sensor_500(cam) + * + *****************************************************************************/ +static int config_sensor_500(struct camera_data *cam, + int req_width, int req_height) +{ + struct cpia2_command cmd; + int i = 0; + int image_size = VIDEOSIZE_CIF; + int image_type = VIDEOSIZE_VGA; + int width = req_width; + int height = req_height; + unsigned int device = cam->params.pnp_id.device_type; + + image_size = cpia2_match_video_size(width, height); + + if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS) + image_type = VIDEOSIZE_VGA; + else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS) + image_type = VIDEOSIZE_CIF; + else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS) + image_type = VIDEOSIZE_QVGA; + else + image_type = VIDEOSIZE_QCIF; + + if (image_size >= 0) { + set_vw_size(cam, image_size); + width = cam->params.roi.width; + height = cam->params.roi.height; + } else { + ERR("ConfigSensor500 failed\n"); + return -EINVAL; + } + + DBG("image_size = %d, width = %d, height = %d, type = %d\n", + image_size, width, height, image_type); + + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + i = 0; + + /* VC Format */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; + cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING; + i++; + + /* VC Clocks */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; + if (device == DEVICE_STV_672) { + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1; + else + cmd.buffer.registers[i].value = + (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV3); + } else { + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0; + else + cmd.buffer.registers[i].value = + (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + } + i++; + + DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value); + + /* Input width from VP */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8) (STV_IMAGE_VGA_COLS / 4); + else + cmd.buffer.registers[i].value = + (u8) (STV_IMAGE_QVGA_COLS / 4); + i++; + DBG("Input width = %d\n", cmd.buffer.registers[i-1].value); + + /* Timings */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 2; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 250; + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = (u8) 125; + else + cmd.buffer.registers[i++].value = (u8) 160; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 2; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 12; + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = (u8) 64; + else + cmd.buffer.registers[i++].value = (u8) 6; + + /* Output Image Size */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4; + else + cmd.buffer.registers[i++].value = width / 4; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4; + else + cmd.buffer.registers[i++].value = height / 4; + + /* Cropping */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2); + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2); + else if (image_type == VIDEOSIZE_CIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); + else /*if (image_type == VIDEOSIZE_QCIF)*/ + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2); + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2); + else if (image_type == VIDEOSIZE_CIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); + else /*if (image_type == VIDEOSIZE_QCIF)*/ + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); + + /* Scaling registers (defaults) */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 36; + else + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 32; + else + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 26; + else + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 21; + else + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */ + else + cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */ + else + cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ + + cmd.reg_count = i; + + cpia2_send_command(cam, &cmd); + + return i; +} + + +/****************************************************************************** + * + * setallproperties + * + * This sets all user changeable properties to the values in cam->params. + *****************************************************************************/ +static int set_all_properties(struct camera_data *cam) +{ + /** + * Don't set target_kb here, it will be set later. + * framerate and user_mode were already set (set_default_user_mode). + **/ + + cpia2_usb_change_streaming_alternate(cam, + cam->params.camera_state.stream_mode); + + cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + TRANSFER_WRITE, cam->params.vp_params.gpio_direction); + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, + cam->params.vp_params.gpio_data); + + v4l2_ctrl_handler_setup(&cam->hdl); + + wake_system(cam); + + set_lowlight_boost(cam); + + return 0; +} + +/****************************************************************************** + * + * cpia2_save_camera_state + * + *****************************************************************************/ +void cpia2_save_camera_state(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, + 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0); + /* Don't get framerate or target_kb. Trust the values we already have */ +} + + +/****************************************************************************** + * + * cpia2_set_flicker_mode + * + *****************************************************************************/ +int cpia2_set_flicker_mode(struct camera_data *cam, int mode) +{ + unsigned char cam_reg; + int err = 0; + + if(cam->params.pnp_id.device_type != DEVICE_STV_672) + return -EINVAL; + + /* Set the appropriate bits in FLICKER_MODES, preserving the rest */ + if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, + TRANSFER_READ, 0))) + return err; + cam_reg = cam->params.flicker_control.cam_register; + + switch(mode) { + case NEVER_FLICKER: + cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; + break; + case FLICKER_60: + cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; + break; + case FLICKER_50: + cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ; + break; + default: + return -EINVAL; + } + + if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES, + TRANSFER_WRITE, cam_reg))) + return err; + + /* Set the appropriate bits in EXP_MODES, preserving the rest */ + if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES, + TRANSFER_READ, 0))) + return err; + cam_reg = cam->params.vp_params.exposure_modes; + + if (mode == NEVER_FLICKER) { + cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; + } else { + cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; + } + + if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES, + TRANSFER_WRITE, cam_reg))) + return err; + + if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, + TRANSFER_WRITE, 1))) + return err; + + switch(mode) { + case NEVER_FLICKER: + case FLICKER_60: + case FLICKER_50: + cam->params.flicker_control.flicker_mode_req = mode; + break; + default: + err = -EINVAL; + } + + return err; +} + +/****************************************************************************** + * + * cpia2_set_property_flip + * + *****************************************************************************/ +void cpia2_set_property_flip(struct camera_data *cam, int prop_val) +{ + unsigned char cam_reg; + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cam_reg = cam->params.vp_params.user_effects; + + if (prop_val) + { + cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP; + } + else + { + cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; + } + cam->params.vp_params.user_effects = cam_reg; + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam_reg); +} + +/****************************************************************************** + * + * cpia2_set_property_mirror + * + *****************************************************************************/ +void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) +{ + unsigned char cam_reg; + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cam_reg = cam->params.vp_params.user_effects; + + if (prop_val) + { + cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR; + } + else + { + cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; + } + cam->params.vp_params.user_effects = cam_reg; + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam_reg); +} + +/****************************************************************************** + * + * cpia2_set_gpio + * + *****************************************************************************/ +int cpia2_set_gpio(struct camera_data *cam, unsigned char setting) +{ + int ret; + + /* Set the microport direction (register 0x90, should be defined + * already) to 1 (user output), and set the microport data (0x91) to + * the value in the ioctl argument. + */ + + ret = cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + CPIA2_VC_MP_DIR_OUTPUT, + 255); + if (ret < 0) + return ret; + cam->params.vp_params.gpio_direction = 255; + + ret = cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DATA, + CPIA2_VC_MP_DIR_OUTPUT, + setting); + if (ret < 0) + return ret; + cam->params.vp_params.gpio_data = setting; + + return 0; +} + +/****************************************************************************** + * + * cpia2_set_fps + * + *****************************************************************************/ +int cpia2_set_fps(struct camera_data *cam, int framerate) +{ + int retval; + + switch(framerate) { + case CPIA2_VP_FRAMERATE_30: + case CPIA2_VP_FRAMERATE_25: + if(cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == + CPIA2_VP_SENSOR_FLAGS_500) { + return -EINVAL; + } + /* Fall through */ + case CPIA2_VP_FRAMERATE_15: + case CPIA2_VP_FRAMERATE_12_5: + case CPIA2_VP_FRAMERATE_7_5: + case CPIA2_VP_FRAMERATE_6_25: + break; + default: + return -EINVAL; + } + + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + framerate == CPIA2_VP_FRAMERATE_15) + framerate = 0; /* Work around bug in VP4 */ + + retval = cpia2_do_command(cam, + CPIA2_CMD_FRAMERATE_REQ, + TRANSFER_WRITE, + framerate); + + if(retval == 0) + cam->params.vp_params.frame_rate = framerate; + + return retval; +} + +/****************************************************************************** + * + * cpia2_set_brightness + * + *****************************************************************************/ +void cpia2_set_brightness(struct camera_data *cam, unsigned char value) +{ + /*** + * Don't let the register be set to zero - bug in VP4 - flash of full + * brightness + ***/ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) + value++; + DBG("Setting brightness to %d (0x%0x)\n", value, value); + cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); +} + +/****************************************************************************** + * + * cpia2_set_contrast + * + *****************************************************************************/ +void cpia2_set_contrast(struct camera_data *cam, unsigned char value) +{ + DBG("Setting contrast to %d (0x%0x)\n", value, value); + cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); +} + +/****************************************************************************** + * + * cpia2_set_saturation + * + *****************************************************************************/ +void cpia2_set_saturation(struct camera_data *cam, unsigned char value) +{ + DBG("Setting saturation to %d (0x%0x)\n", value, value); + cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); +} + +/****************************************************************************** + * + * wake_system + * + *****************************************************************************/ +static void wake_system(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0); +} + +/****************************************************************************** + * + * set_lowlight_boost + * + * Valid for STV500 sensor only + *****************************************************************************/ +static void set_lowlight_boost(struct camera_data *cam) +{ + struct cpia2_command cmd; + + if (cam->params.pnp_id.device_type != DEVICE_STV_672 || + cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500) + return; + + cmd.direction = TRANSFER_WRITE; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 3; + cmd.start = CPIA2_VP_RAM_ADDR_H; + + cmd.buffer.block_data[0] = 0; /* High byte of address to write to */ + cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */ + cmd.buffer.block_data[2] = 0; /* High byte of data to write */ + + cpia2_send_command(cam, &cmd); + + if (cam->params.vp_params.lowlight_boost) { + cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */ + } else { + cmd.buffer.block_data[0] = 0x06; + } + cmd.start = CPIA2_VP_RAM_DATA; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + + /* Rehash the VP4 values */ + cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1); +} + +/****************************************************************************** + * + * cpia2_set_format + * + * Assumes that new size is already set in param struct. + *****************************************************************************/ +void cpia2_set_format(struct camera_data *cam) +{ + cam->flush = true; + + cpia2_usb_stream_pause(cam); + + /* reset camera to new size */ + cpia2_set_low_power(cam); + cpia2_reset_camera(cam); + cam->flush = false; + + cpia2_dbg_dump_registers(cam); + + cpia2_usb_stream_resume(cam); +} + +/****************************************************************************** + * + * cpia2_dbg_dump_registers + * + *****************************************************************************/ +void cpia2_dbg_dump_registers(struct camera_data *cam) +{ +#ifdef _CPIA2_DEBUG_ + struct cpia2_command cmd; + + if (!(debugs_on & DEBUG_DUMP_REGS)) + return; + + cmd.direction = TRANSFER_READ; + + /* Start with bank 0 (SYSTEM) */ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 3; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "System Device Hi = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "System Device Lo = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "System_system control = 0x%X\n", + cmd.buffer.block_data[2]); + + /* Bank 1 (VC) */ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 4; + cmd.start = 0x80; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "ASIC_ID = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "ASIC_REV = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "PW_CONTRL = 0x%X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "WAKEUP = 0x%X\n", + cmd.buffer.block_data[3]); + + cmd.start = 0xA0; /* ST_CTRL */ + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "Stream ctrl = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xA4; /* Stream status */ + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "Stream status = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xA8; /* USB status */ + cmd.reg_count = 3; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "USB_CTRL = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "USB_STRM = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "USB_STATUS = 0x%X\n", + cmd.buffer.block_data[2]); + + cmd.start = 0xAF; /* USB settings */ + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "USB settings = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xC0; /* VC stuff */ + cmd.reg_count = 26; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VC Control = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VC Format = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VC Clocks = 0x%0X\n", + cmd.buffer.block_data[4]); + printk(KERN_DEBUG "VC IHSize = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n", + cmd.buffer.block_data[8]); + printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n", + cmd.buffer.block_data[9]); + printk(KERN_DEBUG "VC OHSize = 0x%0X\n", + cmd.buffer.block_data[10]); + printk(KERN_DEBUG "VC OVSize = 0x%0X\n", + cmd.buffer.block_data[11]); + printk(KERN_DEBUG "VC HCrop = 0x%0X\n", + cmd.buffer.block_data[12]); + printk(KERN_DEBUG "VC VCrop = 0x%0X\n", + cmd.buffer.block_data[13]); + printk(KERN_DEBUG "VC HPhase = 0x%0X\n", + cmd.buffer.block_data[14]); + printk(KERN_DEBUG "VC VPhase = 0x%0X\n", + cmd.buffer.block_data[15]); + printk(KERN_DEBUG "VC HIspan = 0x%0X\n", + cmd.buffer.block_data[16]); + printk(KERN_DEBUG "VC VIspan = 0x%0X\n", + cmd.buffer.block_data[17]); + printk(KERN_DEBUG "VC HiCrop = 0x%0X\n", + cmd.buffer.block_data[18]); + printk(KERN_DEBUG "VC ViCrop = 0x%0X\n", + cmd.buffer.block_data[19]); + printk(KERN_DEBUG "VC HiFract = 0x%0X\n", + cmd.buffer.block_data[20]); + printk(KERN_DEBUG "VC ViFract = 0x%0X\n", + cmd.buffer.block_data[21]); + printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n", + cmd.buffer.block_data[22]); + printk(KERN_DEBUG "VC Creep Per = 0x%0X\n", + cmd.buffer.block_data[23]); + printk(KERN_DEBUG "VC User Sq. = 0x%0X\n", + cmd.buffer.block_data[24]); + printk(KERN_DEBUG "VC Target KB = 0x%0X\n", + cmd.buffer.block_data[25]); + + /*** VP ***/ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 14; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + + printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Sys State = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP Dev Config = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n", + cmd.buffer.block_data[8]); + printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n", + cmd.buffer.block_data[9]); + printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n", + cmd.buffer.block_data[10]); + printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n", + cmd.buffer.block_data[11]); + printk(KERN_DEBUG "VP RAM Data = 0x%0X\n", + cmd.buffer.block_data[12]); + printk(KERN_DEBUG "Do Call = 0x%0X\n", + cmd.buffer.block_data[13]); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.reg_count = 9; + cmd.start = 0x0E; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP Framerate = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", + cmd.buffer.block_data[4]); + printk(KERN_DEBUG "VP White Bal = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP WB thresh = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VP Exp Target = 0x%0X\n", + cmd.buffer.block_data[8]); + + cmd.reg_count = 1; + cmd.start = 0x1B; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n", + cmd.buffer.block_data[0]); + } else { + cmd.reg_count = 8 ; + cmd.start = 0x0E; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP Framerate = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", + cmd.buffer.block_data[7]); + + cmd.reg_count = 1; + cmd.start = CPIA2_VP5_EXPOSURE_TARGET; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n", + cmd.buffer.block_data[0]); + + cmd.reg_count = 4; + cmd.start = 0x3A; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n", + cmd.buffer.block_data[3]); + } +#endif +} + +/****************************************************************************** + * + * reset_camera_struct + * + * Sets all values to the defaults + *****************************************************************************/ +static void reset_camera_struct(struct camera_data *cam) +{ + /*** + * The following parameter values are the defaults from the register map. + ***/ + cam->params.vp_params.lowlight_boost = 0; + + /* FlickerModes */ + cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; + + /* jpeg params */ + cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; + cam->params.compression.creep_period = 2; + cam->params.compression.user_squeeze = 20; + cam->params.compression.inhibit_htables = false; + + /* gpio params */ + cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */ + cam->params.vp_params.gpio_data = 0; + + /* Target kb params */ + cam->params.vc_params.quality = 100; + + /*** + * Set Sensor FPS as fast as possible. + ***/ + if(cam->params.pnp_id.device_type == DEVICE_STV_672) { + if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15; + else + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; + } else { + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; + } + + /*** + * Set default video mode as large as possible : + * for vga sensor set to vga, for cif sensor set to CIF. + ***/ + if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) { + cam->sensor_type = CPIA2_SENSOR_500; + cam->video_size = VIDEOSIZE_VGA; + cam->params.roi.width = STV_IMAGE_VGA_COLS; + cam->params.roi.height = STV_IMAGE_VGA_ROWS; + } else { + cam->sensor_type = CPIA2_SENSOR_410; + cam->video_size = VIDEOSIZE_CIF; + cam->params.roi.width = STV_IMAGE_CIF_COLS; + cam->params.roi.height = STV_IMAGE_CIF_ROWS; + } + + cam->width = cam->params.roi.width; + cam->height = cam->params.roi.height; +} + +/****************************************************************************** + * + * cpia2_init_camera_struct + * + * Initializes camera struct, does not call reset to fill in defaults. + *****************************************************************************/ +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) +{ + struct camera_data *cam; + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + + if (!cam) { + ERR("couldn't kmalloc cpia2 struct\n"); + return NULL; + } + + cam->v4l2_dev.release = cpia2_camera_release; + if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { + v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); + kfree(cam); + return NULL; + } + + mutex_init(&cam->v4l2_lock); + init_waitqueue_head(&cam->wq_stream); + + return cam; +} + +/****************************************************************************** + * + * cpia2_init_camera + * + * Initializes camera. + *****************************************************************************/ +int cpia2_init_camera(struct camera_data *cam) +{ + DBG("Start\n"); + + cam->mmapped = false; + + /* Get sensor and asic types before reset. */ + cpia2_set_high_power(cam); + cpia2_get_version_info(cam); + if (cam->params.version.asic_id != CPIA2_ASIC_672) { + ERR("Device IO error (asicID has incorrect value of 0x%X\n", + cam->params.version.asic_id); + return -ENODEV; + } + + /* Set GPIO direction and data to a safe state. */ + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + TRANSFER_WRITE, 0); + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, + TRANSFER_WRITE, 0); + + /* resetting struct requires version info for sensor and asic types */ + reset_camera_struct(cam); + + cpia2_set_low_power(cam); + + DBG("End\n"); + + return 0; +} + +/****************************************************************************** + * + * cpia2_allocate_buffers + * + *****************************************************************************/ +int cpia2_allocate_buffers(struct camera_data *cam) +{ + int i; + + if(!cam->buffers) { + u32 size = cam->num_frames*sizeof(struct framebuf); + cam->buffers = kmalloc(size, GFP_KERNEL); + if(!cam->buffers) { + ERR("couldn't kmalloc frame buffer structures\n"); + return -ENOMEM; + } + } + + if(!cam->frame_buffer) { + cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames); + if (!cam->frame_buffer) { + ERR("couldn't vmalloc frame buffer data area\n"); + kfree(cam->buffers); + cam->buffers = NULL; + return -ENOMEM; + } + } + + for(i=0; inum_frames-1; ++i) { + cam->buffers[i].next = &cam->buffers[i+1]; + cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + cam->buffers[i].max_length = 0; + cam->buffers[i].num = i; + } + cam->buffers[i].next = cam->buffers; + cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + cam->buffers[i].max_length = 0; + cam->buffers[i].num = i; + cam->curbuff = cam->buffers; + cam->workbuff = cam->curbuff->next; + DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff, + cam->workbuff); + return 0; +} + +/****************************************************************************** + * + * cpia2_free_buffers + * + *****************************************************************************/ +void cpia2_free_buffers(struct camera_data *cam) +{ + if(cam->buffers) { + kfree(cam->buffers); + cam->buffers = NULL; + } + if(cam->frame_buffer) { + rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames); + cam->frame_buffer = NULL; + } +} + +/****************************************************************************** + * + * cpia2_read + * + *****************************************************************************/ +long cpia2_read(struct camera_data *cam, + char __user *buf, unsigned long count, int noblock) +{ + struct framebuf *frame; + + if (!count) + return 0; + + if (!buf) { + ERR("%s: buffer NULL\n",__func__); + return -EINVAL; + } + + if (!cam) { + ERR("%s: Internal error, camera_data NULL!\n",__func__); + return -EINVAL; + } + + if (!cam->streaming) { + /* Start streaming */ + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + + /* Copy cam->curbuff in case it changes while we're processing */ + frame = cam->curbuff; + if (noblock && frame->status != FRAME_READY) { + return -EAGAIN; + } + + if (frame->status != FRAME_READY) { + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !video_is_registered(&cam->vdev) || + (frame = cam->curbuff)->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return 0; + } + + /* copy data to user space */ + if (frame->length > count) + return -EFAULT; + if (copy_to_user(buf, frame->data, frame->length)) + return -EFAULT; + + count = frame->length; + + frame->status = FRAME_EMPTY; + + return count; +} + +/****************************************************************************** + * + * cpia2_poll + * + *****************************************************************************/ +unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, + poll_table *wait) +{ + unsigned int status = v4l2_ctrl_poll(filp, wait); + + if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) && + !cam->streaming) { + /* Start streaming */ + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + + poll_wait(filp, &cam->wq_stream, wait); + + if (cam->curbuff->status == FRAME_READY) + status |= POLLIN | POLLRDNORM; + + return status; +} + +/****************************************************************************** + * + * cpia2_remap_buffer + * + *****************************************************************************/ +int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) +{ + const char *adr = (const char *)vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long start = (unsigned long) adr; + unsigned long page, pos; + + DBG("mmap offset:%ld size:%ld\n", start_offset, size); + + if (!video_is_registered(&cam->vdev)) + return -ENODEV; + + if (size > cam->frame_size*cam->num_frames || + (start_offset % cam->frame_size) != 0 || + (start_offset+size > cam->frame_size*cam->num_frames)) + return -EINVAL; + + pos = ((unsigned long) (cam->frame_buffer)) + start_offset; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + cam->mmapped = true; + return 0; +} diff --git a/drivers/media/usb/cpia2/cpia2_registers.h b/drivers/media/usb/cpia2/cpia2_registers.h new file mode 100644 index 000000000000..3bbec514a967 --- /dev/null +++ b/drivers/media/usb/cpia2/cpia2_registers.h @@ -0,0 +1,476 @@ +/**************************************************************************** + * + * Filename: cpia2registers.h + * + * Copyright 2001, STMicrolectronics, Inc. + * + * Description: + * Definitions for the CPia2 register set + * + * This program is free software; 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 CPIA2_REGISTER_HEADER +#define CPIA2_REGISTER_HEADER + +/*** + * System register set (Bank 0) + ***/ +#define CPIA2_SYSTEM_DEVICE_HI 0x00 +#define CPIA2_SYSTEM_DEVICE_LO 0x01 + +#define CPIA2_SYSTEM_SYSTEM_CONTROL 0x02 +#define CPIA2_SYSTEM_CONTROL_LOW_POWER 0x00 +#define CPIA2_SYSTEM_CONTROL_HIGH_POWER 0x01 +#define CPIA2_SYSTEM_CONTROL_SUSPEND 0x02 +#define CPIA2_SYSTEM_CONTROL_V2W_ERR 0x10 +#define CPIA2_SYSTEM_CONTROL_RB_ERR 0x10 +#define CPIA2_SYSTEM_CONTROL_CLEAR_ERR 0x80 + +#define CPIA2_SYSTEM_INT_PACKET_CTRL 0x04 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX 0x01 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_EOF 0x02 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_INT1 0x04 + +#define CPIA2_SYSTEM_CACHE_CTRL 0x05 +#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_RESET 0x01 +#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_FLUSH 0x02 + +#define CPIA2_SYSTEM_SERIAL_CTRL 0x06 +#define CPIA2_SYSTEM_SERIAL_CTRL_NULL_CMD 0x00 +#define CPIA2_SYSTEM_SERIAL_CTRL_START_CMD 0x01 +#define CPIA2_SYSTEM_SERIAL_CTRL_STOP_CMD 0x02 +#define CPIA2_SYSTEM_SERIAL_CTRL_WRITE_CMD 0x03 +#define CPIA2_SYSTEM_SERIAL_CTRL_READ_ACK_CMD 0x04 +#define CPIA2_SYSTEM_SERIAL_CTRL_READ_NACK_CMD 0x05 + +#define CPIA2_SYSTEM_SERIAL_DATA 0x07 + +#define CPIA2_SYSTEM_VP_SERIAL_ADDR 0x08 + +/*** + * I2C addresses for various devices in CPiA2 + ***/ +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR 0x20 +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_VP 0x88 +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP 0x8A + +#define CPIA2_SYSTEM_SPARE_REG1 0x09 +#define CPIA2_SYSTEM_SPARE_REG2 0x0A +#define CPIA2_SYSTEM_SPARE_REG3 0x0B + +#define CPIA2_SYSTEM_MC_PORT_0 0x0C +#define CPIA2_SYSTEM_MC_PORT_1 0x0D +#define CPIA2_SYSTEM_MC_PORT_2 0x0E +#define CPIA2_SYSTEM_MC_PORT_3 0x0F + +#define CPIA2_SYSTEM_STATUS_PKT 0x20 +#define CPIA2_SYSTEM_STATUS_PKT_END 0x27 + +#define CPIA2_SYSTEM_DESCRIP_VID_HI 0x30 +#define CPIA2_SYSTEM_DESCRIP_VID_LO 0x31 +#define CPIA2_SYSTEM_DESCRIP_PID_HI 0x32 +#define CPIA2_SYSTEM_DESCRIP_PID_LO 0x33 + +#define CPIA2_SYSTEM_FW_VERSION_HI 0x34 +#define CPIA2_SYSTEM_FW_VERSION_LO 0x35 + +#define CPIA2_SYSTEM_CACHE_START_INDEX 0x80 +#define CPIA2_SYSTEM_CACHE_MAX_WRITES 0x10 + +/*** + * VC register set (Bank 1) + ***/ +#define CPIA2_VC_ASIC_ID 0x80 + +#define CPIA2_VC_ASIC_REV 0x81 + +#define CPIA2_VC_PW_CTRL 0x82 +#define CPIA2_VC_PW_CTRL_COLDSTART 0x01 +#define CPIA2_VC_PW_CTRL_CP_CLK_EN 0x02 +#define CPIA2_VC_PW_CTRL_VP_RESET_N 0x04 +#define CPIA2_VC_PW_CTRL_VC_CLK_EN 0x08 +#define CPIA2_VC_PW_CTRL_VC_RESET_N 0x10 +#define CPIA2_VC_PW_CTRL_GOTO_SUSPEND 0x20 +#define CPIA2_VC_PW_CTRL_UDC_SUSPEND 0x40 +#define CPIA2_VC_PW_CTRL_PWR_DOWN 0x80 + +#define CPIA2_VC_WAKEUP 0x83 +#define CPIA2_VC_WAKEUP_SW_ENABLE 0x01 +#define CPIA2_VC_WAKEUP_XX_ENABLE 0x02 +#define CPIA2_VC_WAKEUP_SW_ATWAKEUP 0x04 +#define CPIA2_VC_WAKEUP_XX_ATWAKEUP 0x08 + +#define CPIA2_VC_CLOCK_CTRL 0x84 +#define CPIA2_VC_CLOCK_CTRL_TESTUP72 0x01 + +#define CPIA2_VC_INT_ENABLE 0x88 +#define CPIA2_VC_INT_ENABLE_XX_IE 0x01 +#define CPIA2_VC_INT_ENABLE_SW_IE 0x02 +#define CPIA2_VC_INT_ENABLE_VC_IE 0x04 +#define CPIA2_VC_INT_ENABLE_USBDATA_IE 0x08 +#define CPIA2_VC_INT_ENABLE_USBSETUP_IE 0x10 +#define CPIA2_VC_INT_ENABLE_USBCFG_IE 0x20 + +#define CPIA2_VC_INT_FLAG 0x89 +#define CPIA2_VC_INT_ENABLE_XX_FLAG 0x01 +#define CPIA2_VC_INT_ENABLE_SW_FLAG 0x02 +#define CPIA2_VC_INT_ENABLE_VC_FLAG 0x04 +#define CPIA2_VC_INT_ENABLE_USBDATA_FLAG 0x08 +#define CPIA2_VC_INT_ENABLE_USBSETUP_FLAG 0x10 +#define CPIA2_VC_INT_ENABLE_USBCFG_FLAG 0x20 +#define CPIA2_VC_INT_ENABLE_SET_RESET_BIT 0x80 + +#define CPIA2_VC_INT_STATE 0x8A +#define CPIA2_VC_INT_STATE_XX_STATE 0x01 +#define CPIA2_VC_INT_STATE_SW_STATE 0x02 + +#define CPIA2_VC_MP_DIR 0x90 +#define CPIA2_VC_MP_DIR_INPUT 0x00 +#define CPIA2_VC_MP_DIR_OUTPUT 0x01 + +#define CPIA2_VC_MP_DATA 0x91 + +#define CPIA2_VC_DP_CTRL 0x98 +#define CPIA2_VC_DP_CTRL_MODE_0 0x00 +#define CPIA2_VC_DP_CTRL_MODE_A 0x01 +#define CPIA2_VC_DP_CTRL_MODE_B 0x02 +#define CPIA2_VC_DP_CTRL_MODE_C 0x03 +#define CPIA2_VC_DP_CTRL_FAKE_FST 0x04 + +#define CPIA2_VC_AD_CTRL 0x99 +#define CPIA2_VC_AD_CTRL_SRC_0 0x00 +#define CPIA2_VC_AD_CTRL_SRC_DIGI_A 0x01 +#define CPIA2_VC_AD_CTRL_SRC_REG 0x02 +#define CPIA2_VC_AD_CTRL_DST_USB 0x00 +#define CPIA2_VC_AD_CTRL_DST_REG 0x04 + +#define CPIA2_VC_AD_TEST_IN 0x9B + +#define CPIA2_VC_AD_TEST_OUT 0x9C + +#define CPIA2_VC_AD_STATUS 0x9D +#define CPIA2_VC_AD_STATUS_EMPTY 0x01 +#define CPIA2_VC_AD_STATUS_FULL 0x02 + +#define CPIA2_VC_DP_DATA 0x9E + +#define CPIA2_VC_ST_CTRL 0xA0 +#define CPIA2_VC_ST_CTRL_SRC_VC 0x00 +#define CPIA2_VC_ST_CTRL_SRC_DP 0x01 +#define CPIA2_VC_ST_CTRL_SRC_REG 0x02 + +#define CPIA2_VC_ST_CTRL_RAW_SELECT 0x04 + +#define CPIA2_VC_ST_CTRL_DST_USB 0x00 +#define CPIA2_VC_ST_CTRL_DST_DP 0x08 +#define CPIA2_VC_ST_CTRL_DST_REG 0x10 + +#define CPIA2_VC_ST_CTRL_FIFO_ENABLE 0x20 +#define CPIA2_VC_ST_CTRL_EOF_DETECT 0x40 + +#define CPIA2_VC_ST_TEST 0xA1 +#define CPIA2_VC_ST_TEST_MODE_MANUAL 0x00 +#define CPIA2_VC_ST_TEST_MODE_INCREMENT 0x02 + +#define CPIA2_VC_ST_TEST_AUTO_FILL 0x08 + +#define CPIA2_VC_ST_TEST_REPEAT_FIFO 0x10 + +#define CPIA2_VC_ST_TEST_IN 0xA2 + +#define CPIA2_VC_ST_TEST_OUT 0xA3 + +#define CPIA2_VC_ST_STATUS 0xA4 +#define CPIA2_VC_ST_STATUS_EMPTY 0x01 +#define CPIA2_VC_ST_STATUS_FULL 0x02 + +#define CPIA2_VC_ST_FRAME_DETECT_1 0xA5 + +#define CPIA2_VC_ST_FRAME_DETECT_2 0xA6 + +#define CPIA2_VC_USB_CTRL 0xA8 +#define CPIA2_VC_USB_CTRL_CMD_STALLED 0x01 +#define CPIA2_VC_USB_CTRL_CMD_READY 0x02 +#define CPIA2_VC_USB_CTRL_CMD_STATUS 0x04 +#define CPIA2_VC_USB_CTRL_CMD_STATUS_DIR 0x08 +#define CPIA2_VC_USB_CTRL_CMD_NO_CLASH 0x10 +#define CPIA2_VC_USB_CTRL_CMD_MICRO_ACCESS 0x80 + +#define CPIA2_VC_USB_STRM 0xA9 +#define CPIA2_VC_USB_STRM_ISO_ENABLE 0x01 +#define CPIA2_VC_USB_STRM_BLK_ENABLE 0x02 +#define CPIA2_VC_USB_STRM_INT_ENABLE 0x04 +#define CPIA2_VC_USB_STRM_AUD_ENABLE 0x08 + +#define CPIA2_VC_USB_STATUS 0xAA +#define CPIA2_VC_USB_STATUS_CMD_IN_PROGRESS 0x01 +#define CPIA2_VC_USB_STATUS_CMD_STATUS_STALL 0x02 +#define CPIA2_VC_USB_STATUS_CMD_HANDSHAKE 0x04 +#define CPIA2_VC_USB_STATUS_CMD_OVERRIDE 0x08 +#define CPIA2_VC_USB_STATUS_CMD_FIFO_BUSY 0x10 +#define CPIA2_VC_USB_STATUS_BULK_REPEAT_TXN 0x20 +#define CPIA2_VC_USB_STATUS_CONFIG_DONE 0x40 +#define CPIA2_VC_USB_STATUS_USB_SUSPEND 0x80 + +#define CPIA2_VC_USB_CMDW 0xAB + +#define CPIA2_VC_USB_DATARW 0xAC + +#define CPIA2_VC_USB_INFO 0xAD + +#define CPIA2_VC_USB_CONFIG 0xAE + +#define CPIA2_VC_USB_SETTINGS 0xAF +#define CPIA2_VC_USB_SETTINGS_CONFIG_MASK 0x03 +#define CPIA2_VC_USB_SETTINGS_INTERFACE_MASK 0x0C +#define CPIA2_VC_USB_SETTINGS_ALTERNATE_MASK 0x70 + +#define CPIA2_VC_USB_ISOLIM 0xB0 + +#define CPIA2_VC_USB_ISOFAILS 0xB1 + +#define CPIA2_VC_USB_ISOMAXPKTHI 0xB2 + +#define CPIA2_VC_USB_ISOMAXPKTLO 0xB3 + +#define CPIA2_VC_V2W_CTRL 0xB8 +#define CPIA2_VC_V2W_SELECT 0x01 + +#define CPIA2_VC_V2W_SCL 0xB9 + +#define CPIA2_VC_V2W_SDA 0xBA + +#define CPIA2_VC_VC_CTRL 0xC0 +#define CPIA2_VC_VC_CTRL_RUN 0x01 +#define CPIA2_VC_VC_CTRL_SINGLESHOT 0x02 +#define CPIA2_VC_VC_CTRL_IDLING 0x04 +#define CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES 0x10 +#define CPIA2_VC_VC_CTRL_INHIBIT_Q_TABLES 0x20 +#define CPIA2_VC_VC_CTRL_INHIBIT_PRIVATE 0x40 + +#define CPIA2_VC_VC_RESTART_IVAL_HI 0xC1 + +#define CPIA2_VC_VC_RESTART_IVAL_LO 0xC2 + +#define CPIA2_VC_VC_FORMAT 0xC3 +#define CPIA2_VC_VC_FORMAT_UFIRST 0x01 +#define CPIA2_VC_VC_FORMAT_MONO 0x02 +#define CPIA2_VC_VC_FORMAT_DECIMATING 0x04 +#define CPIA2_VC_VC_FORMAT_SHORTLINE 0x08 +#define CPIA2_VC_VC_FORMAT_SELFTEST 0x10 + +#define CPIA2_VC_VC_CLOCKS 0xC4 +#define CPIA2_VC_VC_CLOCKS_CLKDIV_MASK 0x03 +#define CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 0x04 +#define CPIA2_VC_VC_672_CLOCKS_SCALING 0x08 +#define CPIA2_VC_VC_CLOCKS_LOGDIV0 0x00 +#define CPIA2_VC_VC_CLOCKS_LOGDIV1 0x01 +#define CPIA2_VC_VC_CLOCKS_LOGDIV2 0x02 +#define CPIA2_VC_VC_CLOCKS_LOGDIV3 0x03 +#define CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 0x08 +#define CPIA2_VC_VC_676_CLOCKS_SCALING 0x10 + +#define CPIA2_VC_VC_IHSIZE_LO 0xC5 + +#define CPIA2_VC_VC_XLIM_HI 0xC6 + +#define CPIA2_VC_VC_XLIM_LO 0xC7 + +#define CPIA2_VC_VC_YLIM_HI 0xC8 + +#define CPIA2_VC_VC_YLIM_LO 0xC9 + +#define CPIA2_VC_VC_OHSIZE 0xCA + +#define CPIA2_VC_VC_OVSIZE 0xCB + +#define CPIA2_VC_VC_HCROP 0xCC + +#define CPIA2_VC_VC_VCROP 0xCD + +#define CPIA2_VC_VC_HPHASE 0xCE + +#define CPIA2_VC_VC_VPHASE 0xCF + +#define CPIA2_VC_VC_HISPAN 0xD0 + +#define CPIA2_VC_VC_VISPAN 0xD1 + +#define CPIA2_VC_VC_HICROP 0xD2 + +#define CPIA2_VC_VC_VICROP 0xD3 + +#define CPIA2_VC_VC_HFRACT 0xD4 +#define CPIA2_VC_VC_HFRACT_DEN_MASK 0x0F +#define CPIA2_VC_VC_HFRACT_NUM_MASK 0xF0 + +#define CPIA2_VC_VC_VFRACT 0xD5 +#define CPIA2_VC_VC_VFRACT_DEN_MASK 0x0F +#define CPIA2_VC_VC_VFRACT_NUM_MASK 0xF0 + +#define CPIA2_VC_VC_JPEG_OPT 0xD6 +#define CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE 0x01 +#define CPIA2_VC_VC_JPEG_OPT_NO_DC_AUTO_SQUEEZE 0x02 +#define CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE 0x04 +#define CPIA2_VC_VC_JPEG_OPT_DEFAULT (CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE|\ + CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE) + + +#define CPIA2_VC_VC_CREEP_PERIOD 0xD7 +#define CPIA2_VC_VC_USER_SQUEEZE 0xD8 +#define CPIA2_VC_VC_TARGET_KB 0xD9 + +#define CPIA2_VC_VC_AUTO_SQUEEZE 0xE6 + + +/*** + * VP register set (Bank 2) + ***/ +#define CPIA2_VP_DEVICEH 0 +#define CPIA2_VP_DEVICEL 1 + +#define CPIA2_VP_SYSTEMSTATE 0x02 +#define CPIA2_VP_SYSTEMSTATE_HK_ALIVE 0x01 + +#define CPIA2_VP_SYSTEMCTRL 0x03 +#define CPIA2_VP_SYSTEMCTRL_REQ_CLEAR_ERROR 0x80 +#define CPIA2_VP_SYSTEMCTRL_POWER_DOWN_PLL 0x20 +#define CPIA2_VP_SYSTEMCTRL_REQ_SUSPEND_STATE 0x10 +#define CPIA2_VP_SYSTEMCTRL_REQ_SERIAL_WAKEUP 0x08 +#define CPIA2_VP_SYSTEMCTRL_REQ_AUTOLOAD 0x04 +#define CPIA2_VP_SYSTEMCTRL_HK_CONTROL 0x02 +#define CPIA2_VP_SYSTEMCTRL_POWER_CONTROL 0x01 + +#define CPIA2_VP_SENSOR_FLAGS 0x05 +#define CPIA2_VP_SENSOR_FLAGS_404 0x01 +#define CPIA2_VP_SENSOR_FLAGS_407 0x02 +#define CPIA2_VP_SENSOR_FLAGS_409 0x04 +#define CPIA2_VP_SENSOR_FLAGS_410 0x08 +#define CPIA2_VP_SENSOR_FLAGS_500 0x10 + +#define CPIA2_VP_SENSOR_REV 0x06 + +#define CPIA2_VP_DEVICE_CONFIG 0x07 +#define CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE 0x01 + +#define CPIA2_VP_GPIO_DIRECTION 0x08 +#define CPIA2_VP_GPIO_READ 0xFF +#define CPIA2_VP_GPIO_WRITE 0x00 + +#define CPIA2_VP_GPIO_DATA 0x09 + +#define CPIA2_VP_RAM_ADDR_H 0x0A +#define CPIA2_VP_RAM_ADDR_L 0x0B +#define CPIA2_VP_RAM_DATA 0x0C + +#define CPIA2_VP_PATCH_REV 0x0F + +#define CPIA2_VP4_USER_MODE 0x10 +#define CPIA2_VP5_USER_MODE 0x13 +#define CPIA2_VP_USER_MODE_CIF 0x01 +#define CPIA2_VP_USER_MODE_QCIFDS 0x02 +#define CPIA2_VP_USER_MODE_QCIFPTC 0x04 +#define CPIA2_VP_USER_MODE_QVGADS 0x08 +#define CPIA2_VP_USER_MODE_QVGAPTC 0x10 +#define CPIA2_VP_USER_MODE_VGA 0x20 + +#define CPIA2_VP4_FRAMERATE_REQUEST 0x11 +#define CPIA2_VP5_FRAMERATE_REQUEST 0x14 +#define CPIA2_VP_FRAMERATE_60 0x80 +#define CPIA2_VP_FRAMERATE_50 0x40 +#define CPIA2_VP_FRAMERATE_30 0x20 +#define CPIA2_VP_FRAMERATE_25 0x10 +#define CPIA2_VP_FRAMERATE_15 0x08 +#define CPIA2_VP_FRAMERATE_12_5 0x04 +#define CPIA2_VP_FRAMERATE_7_5 0x02 +#define CPIA2_VP_FRAMERATE_6_25 0x01 + +#define CPIA2_VP4_USER_EFFECTS 0x12 +#define CPIA2_VP5_USER_EFFECTS 0x15 +#define CPIA2_VP_USER_EFFECTS_COLBARS 0x01 +#define CPIA2_VP_USER_EFFECTS_COLBARS_GRAD 0x02 +#define CPIA2_VP_USER_EFFECTS_MIRROR 0x04 +#define CPIA2_VP_USER_EFFECTS_FLIP 0x40 // VP5 only + +/* NOTE: CPIA2_VP_EXPOSURE_MODES shares the same register as VP5 User + * Effects */ +#define CPIA2_VP_EXPOSURE_MODES 0x15 +#define CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER 0x20 +#define CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP 0x10 + +#define CPIA2_VP4_EXPOSURE_TARGET 0x16 // VP4 +#define CPIA2_VP5_EXPOSURE_TARGET 0x20 // VP5 + +#define CPIA2_VP_FLICKER_MODES 0x1B +#define CPIA2_VP_FLICKER_MODES_50HZ 0x80 +#define CPIA2_VP_FLICKER_MODES_CUSTOM_FLT_FFREQ 0x40 +#define CPIA2_VP_FLICKER_MODES_NEVER_FLICKER 0x20 +#define CPIA2_VP_FLICKER_MODES_INHIBIT_RUB 0x10 +#define CPIA2_VP_FLICKER_MODES_ADJUST_LINE_FREQ 0x08 +#define CPIA2_VP_FLICKER_MODES_CUSTOM_INT_FFREQ 0x04 + +#define CPIA2_VP_UMISC 0x1D +#define CPIA2_VP_UMISC_FORCE_MONO 0x80 +#define CPIA2_VP_UMISC_FORCE_ID_MASK 0x40 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_FGS 0x20 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_DIMS 0x08 +#define CPIA2_VP_UMISC_OPT_FOR_SENSOR_DS 0x04 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_MODE_INT 0x02 + +#define CPIA2_VP5_ANTIFLKRSETUP 0x22 //34 + +#define CPIA2_VP_INTERPOLATION 0x24 +#define CPIA2_VP_INTERPOLATION_EVEN_FIRST 0x40 +#define CPIA2_VP_INTERPOLATION_HJOG 0x20 +#define CPIA2_VP_INTERPOLATION_VJOG 0x10 + +#define CPIA2_VP_GAMMA 0x25 +#define CPIA2_VP_DEFAULT_GAMMA 0x10 + +#define CPIA2_VP_YRANGE 0x26 + +#define CPIA2_VP_SATURATION 0x27 + +#define CPIA2_VP5_MYBLACK_LEVEL 0x3A //58 +#define CPIA2_VP5_MCYRANGE 0x3B //59 +#define CPIA2_VP5_MYCEILING 0x3C //60 +#define CPIA2_VP5_MCUVSATURATION 0x3D //61 + + +#define CPIA2_VP_REHASH_VALUES 0x60 + + +/*** + * Common sensor registers + ***/ +#define CPIA2_SENSOR_DEVICE_H 0x00 +#define CPIA2_SENSOR_DEVICE_L 0x01 + +#define CPIA2_SENSOR_DATA_FORMAT 0x16 +#define CPIA2_SENSOR_DATA_FORMAT_HMIRROR 0x08 +#define CPIA2_SENSOR_DATA_FORMAT_VMIRROR 0x10 + +#define CPIA2_SENSOR_CR1 0x76 +#define CPIA2_SENSOR_CR1_STAND_BY 0x01 +#define CPIA2_SENSOR_CR1_DOWN_RAMP_GEN 0x02 +#define CPIA2_SENSOR_CR1_DOWN_COLUMN_ADC 0x04 +#define CPIA2_SENSOR_CR1_DOWN_CAB_REGULATOR 0x08 +#define CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR 0x10 +#define CPIA2_SENSOR_CR1_DOWN_VRT_AMP 0x20 +#define CPIA2_SENSOR_CR1_DOWN_BAND_GAP 0x40 + +#endif diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c new file mode 100644 index 000000000000..95b5d6e7cdc4 --- /dev/null +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -0,0 +1,955 @@ +/**************************************************************************** + * + * Filename: cpia2_usb.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * This program is free software; 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. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox + ****************************************************************************/ + +#include +#include +#include +#include + +#include "cpia2.h" + +static int frame_sizes[] = { + 0, // USBIF_CMDONLY + 0, // USBIF_BULK + 128, // USBIF_ISO_1 + 384, // USBIF_ISO_2 + 640, // USBIF_ISO_3 + 768, // USBIF_ISO_4 + 896, // USBIF_ISO_5 + 1023, // USBIF_ISO_6 +}; + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt] + +static void process_frame(struct camera_data *cam); +static void cpia2_usb_complete(struct urb *urb); +static int cpia2_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void cpia2_usb_disconnect(struct usb_interface *intf); +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); +static int cpia2_usb_resume(struct usb_interface *intf); + +static void free_sbufs(struct camera_data *cam); +static void add_APPn(struct camera_data *cam); +static void add_COM(struct camera_data *cam); +static int submit_urbs(struct camera_data *cam); +static int set_alternate(struct camera_data *cam, unsigned int alt); +static int configure_transfer_mode(struct camera_data *cam, unsigned int alt); + +static struct usb_device_id cpia2_id_table[] = { + {USB_DEVICE(0x0553, 0x0100)}, + {USB_DEVICE(0x0553, 0x0140)}, + {USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */ + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, cpia2_id_table); + +static struct usb_driver cpia2_driver = { + .name = "cpia2", + .probe = cpia2_usb_probe, + .disconnect = cpia2_usb_disconnect, + .suspend = cpia2_usb_suspend, + .resume = cpia2_usb_resume, + .reset_resume = cpia2_usb_resume, + .id_table = cpia2_id_table +}; + + +/****************************************************************************** + * + * process_frame + * + *****************************************************************************/ +static void process_frame(struct camera_data *cam) +{ + static int frame_count; + + unsigned char *inbuff = cam->workbuff->data; + + DBG("Processing frame #%d, current:%d\n", + cam->workbuff->num, cam->curbuff->num); + + if(cam->workbuff->length > cam->workbuff->max_length) + cam->workbuff->max_length = cam->workbuff->length; + + if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) { + frame_count++; + } else { + cam->workbuff->status = FRAME_ERROR; + DBG("Start of frame not found\n"); + return; + } + + /*** + * Now the output buffer should have a JPEG image in it. + ***/ + if(!cam->first_image_seen) { + /* Always skip the first image after streaming + * starts. It is almost certainly corrupt. */ + cam->first_image_seen = 1; + cam->workbuff->status = FRAME_EMPTY; + return; + } + if (cam->workbuff->length > 3) { + if(cam->mmapped && + cam->workbuff->length < cam->workbuff->max_length) { + /* No junk in the buffers */ + memset(cam->workbuff->data+cam->workbuff->length, + 0, cam->workbuff->max_length- + cam->workbuff->length); + } + cam->workbuff->max_length = cam->workbuff->length; + cam->workbuff->status = FRAME_READY; + + if(!cam->mmapped && cam->num_frames > 2) { + /* During normal reading, the most recent + * frame will be read. If the current frame + * hasn't started reading yet, it will never + * be read, so mark it empty. If the buffer is + * mmapped, or we have few buffers, we need to + * wait for the user to free the buffer. + * + * NOTE: This is not entirely foolproof with 3 + * buffers, but it would take an EXTREMELY + * overloaded system to cause problems (possible + * image data corruption). Basically, it would + * need to take more time to execute cpia2_read + * than it would for the camera to send + * cam->num_frames-2 frames before problems + * could occur. + */ + cam->curbuff->status = FRAME_EMPTY; + } + cam->curbuff = cam->workbuff; + cam->workbuff = cam->workbuff->next; + DBG("Changed buffers, work:%d, current:%d\n", + cam->workbuff->num, cam->curbuff->num); + return; + } else { + DBG("Not enough data for an image.\n"); + } + + cam->workbuff->status = FRAME_ERROR; + return; +} + +/****************************************************************************** + * + * add_APPn + * + * Adds a user specified APPn record + *****************************************************************************/ +static void add_APPn(struct camera_data *cam) +{ + if(cam->APP_len > 0) { + cam->workbuff->data[cam->workbuff->length++] = 0xFF; + cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn; + cam->workbuff->data[cam->workbuff->length++] = 0; + cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2; + memcpy(cam->workbuff->data+cam->workbuff->length, + cam->APP_data, cam->APP_len); + cam->workbuff->length += cam->APP_len; + } +} + +/****************************************************************************** + * + * add_COM + * + * Adds a user specified COM record + *****************************************************************************/ +static void add_COM(struct camera_data *cam) +{ + if(cam->COM_len > 0) { + cam->workbuff->data[cam->workbuff->length++] = 0xFF; + cam->workbuff->data[cam->workbuff->length++] = 0xFE; + cam->workbuff->data[cam->workbuff->length++] = 0; + cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2; + memcpy(cam->workbuff->data+cam->workbuff->length, + cam->COM_data, cam->COM_len); + cam->workbuff->length += cam->COM_len; + } +} + +/****************************************************************************** + * + * cpia2_usb_complete + * + * callback when incoming packet is received + *****************************************************************************/ +static void cpia2_usb_complete(struct urb *urb) +{ + int i; + unsigned char *cdata; + static int frame_ready = false; + struct camera_data *cam = (struct camera_data *) urb->context; + + if (urb->status!=0) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + { + DBG("urb->status = %d!\n", urb->status); + } + DBG("Stopping streaming\n"); + return; + } + + if (!cam->streaming || !video_is_registered(&cam->vdev)) { + LOG("Will now stop the streaming: streaming = %d, present=%d\n", + cam->streaming, video_is_registered(&cam->vdev)); + return; + } + + /*** + * Packet collater + ***/ + //DBG("Collating %d packets\n", urb->number_of_packets); + for (i = 0; i < urb->number_of_packets; i++) { + u16 checksum, iso_checksum; + int j; + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + if(cam->workbuff->status == FRAME_READY) { + struct framebuf *ptr; + /* Try to find an available buffer */ + DBG("workbuff full, searching\n"); + for (ptr = cam->workbuff->next; + ptr != cam->workbuff; + ptr = ptr->next) + { + if (ptr->status == FRAME_EMPTY) { + ptr->status = FRAME_READING; + ptr->length = 0; + break; + } + } + if (ptr == cam->workbuff) + break; /* No READING or EMPTY buffers left */ + + cam->workbuff = ptr; + } + + if (cam->workbuff->status == FRAME_EMPTY || + cam->workbuff->status == FRAME_ERROR) { + cam->workbuff->status = FRAME_READING; + cam->workbuff->length = 0; + } + + //DBG(" Packet %d length = %d, status = %d\n", i, n, st); + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) { + LOG("cpia2 data error: [%d] len=%d, status = %d\n", + i, n, st); + if(!ALLOW_CORRUPT) + cam->workbuff->status = FRAME_ERROR; + continue; + } + + if(n<=2) + continue; + + checksum = 0; + for(j=0; jworkbuff->status = FRAME_ERROR; + continue; + } + } + n -= 2; + + if(cam->workbuff->status != FRAME_READING) { + if((0xFF == cdata[0] && 0xD8 == cdata[1]) || + (0xD8 == cdata[0] && 0xFF == cdata[1] && + 0 != cdata[2])) { + /* frame is skipped, but increment total + * frame count anyway */ + cam->frame_count++; + } + DBG("workbuff not reading, status=%d\n", + cam->workbuff->status); + continue; + } + + if (cam->frame_size < cam->workbuff->length + n) { + ERR("buffer overflow! length: %d, n: %d\n", + cam->workbuff->length, n); + cam->workbuff->status = FRAME_ERROR; + if(cam->workbuff->length > cam->workbuff->max_length) + cam->workbuff->max_length = + cam->workbuff->length; + continue; + } + + if (cam->workbuff->length == 0) { + int data_offset; + if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) { + data_offset = 1; + } else if((0xFF == cdata[0]) && (0xD8 == cdata[1]) + && (0xFF == cdata[2])) { + data_offset = 2; + } else { + DBG("Ignoring packet, not beginning!\n"); + continue; + } + DBG("Start of frame pattern found\n"); + do_gettimeofday(&cam->workbuff->timestamp); + cam->workbuff->seq = cam->frame_count++; + cam->workbuff->data[0] = 0xFF; + cam->workbuff->data[1] = 0xD8; + cam->workbuff->length = 2; + add_APPn(cam); + add_COM(cam); + memcpy(cam->workbuff->data+cam->workbuff->length, + cdata+data_offset, n-data_offset); + cam->workbuff->length += n-data_offset; + } else if (cam->workbuff->length > 0) { + memcpy(cam->workbuff->data + cam->workbuff->length, + cdata, n); + cam->workbuff->length += n; + } + + if ((cam->workbuff->length >= 3) && + (cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) && + (cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) && + (cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) { + frame_ready = true; + cam->workbuff->data[cam->workbuff->length - 1] = 0; + cam->workbuff->length -= 1; + } else if ((cam->workbuff->length >= 2) && + (cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) && + (cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) { + frame_ready = true; + } + + if (frame_ready) { + DBG("Workbuff image size = %d\n",cam->workbuff->length); + process_frame(cam); + + frame_ready = false; + + if (waitqueue_active(&cam->wq_stream)) + wake_up_interruptible(&cam->wq_stream); + } + } + + if(cam->streaming) { + /* resubmit */ + urb->dev = cam->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + ERR("%s: usb_submit_urb ret %d!\n", __func__, i); + } +} + +/****************************************************************************** + * + * configure_transfer_mode + * + *****************************************************************************/ +static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) +{ + static unsigned char iso_regs[8][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0xB9, 0x00, 0x00, 0x7E}, + {0xB9, 0x00, 0x01, 0x7E}, + {0xB9, 0x00, 0x02, 0x7E}, + {0xB9, 0x00, 0x02, 0xFE}, + {0xB9, 0x00, 0x03, 0x7E}, + {0xB9, 0x00, 0x03, 0xFD} + }; + struct cpia2_command cmd; + unsigned char reg; + + if (!video_is_registered(&cam->vdev)) + return -ENODEV; + + /*** + * Write the isoc registers according to the alternate selected + ***/ + cmd.direction = TRANSFER_WRITE; + cmd.buffer.block_data[0] = iso_regs[alt][0]; + cmd.buffer.block_data[1] = iso_regs[alt][1]; + cmd.buffer.block_data[2] = iso_regs[alt][2]; + cmd.buffer.block_data[3] = iso_regs[alt][3]; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_USB_ISOLIM; + cmd.reg_count = 4; + cpia2_send_command(cam, &cmd); + + /*** + * Enable relevant streams before starting polling. + * First read USB Stream Config Register. + ***/ + cmd.direction = TRANSFER_READ; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_USB_STRM; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + reg = cmd.buffer.block_data[0]; + + /* Clear iso, bulk, and int */ + reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE | + CPIA2_VC_USB_STRM_ISO_ENABLE | + CPIA2_VC_USB_STRM_INT_ENABLE); + + if (alt == USBIF_BULK) { + DBG("Enabling bulk xfer\n"); + reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */ + cam->xfer_mode = XFER_BULK; + } else if (alt >= USBIF_ISO_1) { + DBG("Enabling ISOC xfer\n"); + reg |= CPIA2_VC_USB_STRM_ISO_ENABLE; + cam->xfer_mode = XFER_ISOC; + } + + cmd.buffer.block_data[0] = reg; + cmd.direction = TRANSFER_WRITE; + cmd.start = CPIA2_VC_USB_STRM; + cmd.reg_count = 1; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cpia2_send_command(cam, &cmd); + + return 0; +} + +/****************************************************************************** + * + * cpia2_usb_change_streaming_alternate + * + *****************************************************************************/ +int cpia2_usb_change_streaming_alternate(struct camera_data *cam, + unsigned int alt) +{ + int ret = 0; + + if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6) + return -EINVAL; + + if(alt == cam->params.camera_state.stream_mode) + return 0; + + cpia2_usb_stream_pause(cam); + + configure_transfer_mode(cam, alt); + + cam->params.camera_state.stream_mode = alt; + + /* Reset the camera to prevent image quality degradation */ + cpia2_reset_camera(cam); + + cpia2_usb_stream_resume(cam); + + return ret; +} + +/****************************************************************************** + * + * set_alternate + * + *****************************************************************************/ +static int set_alternate(struct camera_data *cam, unsigned int alt) +{ + int ret = 0; + + if(alt == cam->cur_alt) + return 0; + + if (cam->cur_alt != USBIF_CMDONLY) { + DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY); + ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY); + if (ret != 0) + return ret; + } + if (alt != USBIF_CMDONLY) { + DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt); + ret = usb_set_interface(cam->dev, cam->iface, alt); + if (ret != 0) + return ret; + } + + cam->old_alt = cam->cur_alt; + cam->cur_alt = alt; + + return ret; +} + +/****************************************************************************** + * + * free_sbufs + * + * Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL + * are assumed to be allocated. Non-NULL .urb members are also assumed to be + * submitted (and must therefore be killed before they are freed). + *****************************************************************************/ +static void free_sbufs(struct camera_data *cam) +{ + int i; + + for (i = 0; i < NUM_SBUF; i++) { + if(cam->sbuf[i].urb) { + usb_kill_urb(cam->sbuf[i].urb); + usb_free_urb(cam->sbuf[i].urb); + cam->sbuf[i].urb = NULL; + } + if(cam->sbuf[i].data) { + kfree(cam->sbuf[i].data); + cam->sbuf[i].data = NULL; + } + } +} + +/******* +* Convenience functions +*******/ +/**************************************************************************** + * + * write_packet + * + ***************************************************************************/ +static int write_packet(struct usb_device *udev, + u8 request, u8 * registers, u16 start, size_t size) +{ + if (!registers || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + start, /* value */ + 0, /* index */ + registers, /* buffer */ + size, + HZ); +} + +/**************************************************************************** + * + * read_packet + * + ***************************************************************************/ +static int read_packet(struct usb_device *udev, + u8 request, u8 * registers, u16 start, size_t size) +{ + if (!registers || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + request, + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE, + start, /* value */ + 0, /* index */ + registers, /* buffer */ + size, + HZ); +} + +/****************************************************************************** + * + * cpia2_usb_transfer_cmd + * + *****************************************************************************/ +int cpia2_usb_transfer_cmd(struct camera_data *cam, + void *registers, + u8 request, u8 start, u8 count, u8 direction) +{ + int err = 0; + struct usb_device *udev = cam->dev; + + if (!udev) { + ERR("%s: Internal driver error: udev is NULL\n", __func__); + return -EINVAL; + } + + if (!registers) { + ERR("%s: Internal driver error: register array is NULL\n", __func__); + return -EINVAL; + } + + if (direction == TRANSFER_READ) { + err = read_packet(udev, request, (u8 *)registers, start, count); + if (err > 0) + err = 0; + } else if (direction == TRANSFER_WRITE) { + err =write_packet(udev, request, (u8 *)registers, start, count); + if (err < 0) { + LOG("Control message failed, err val = %d\n", err); + LOG("Message: request = 0x%0X, start = 0x%0X\n", + request, start); + LOG("Message: count = %d, register[0] = 0x%0X\n", + count, ((unsigned char *) registers)[0]); + } else + err=0; + } else { + LOG("Unexpected first byte of direction: %d\n", + direction); + return -EINVAL; + } + + if(err != 0) + LOG("Unexpected error: %d\n", err); + return err; +} + + +/****************************************************************************** + * + * submit_urbs + * + *****************************************************************************/ +static int submit_urbs(struct camera_data *cam) +{ + struct urb *urb; + int fx, err, i, j; + + for(i=0; isbuf[i].data) + continue; + cam->sbuf[i].data = + kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!cam->sbuf[i].data) { + while (--i >= 0) { + kfree(cam->sbuf[i].data); + cam->sbuf[i].data = NULL; + } + return -ENOMEM; + } + } + + /* We double buffer the Isoc lists, and also know the polling + * interval is every frame (1 == (1 << (bInterval -1))). + */ + for(i=0; isbuf[i].urb) { + continue; + } + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + ERR("%s: usb_alloc_urb error!\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(cam->sbuf[j].urb); + return -ENOMEM; + } + + cam->sbuf[i].urb = urb; + urb->dev = cam->dev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = cam->sbuf[i].data; + urb->complete = cpia2_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = + FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = + FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + } + + + /* Queue the ISO urbs, and resubmit in the completion handler */ + for(i=0; isbuf[i].urb, GFP_KERNEL); + if (err) { + ERR("usb_submit_urb[%d]() = %d\n", i, err); + return err; + } + } + + return 0; +} + +/****************************************************************************** + * + * cpia2_usb_stream_start + * + *****************************************************************************/ +int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate) +{ + int ret; + int old_alt; + + if(cam->streaming) + return 0; + + if (cam->flush) { + int i; + DBG("Flushing buffers\n"); + for(i=0; inum_frames; ++i) { + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + } + cam->curbuff = &cam->buffers[0]; + cam->workbuff = cam->curbuff->next; + cam->flush = false; + } + + old_alt = cam->params.camera_state.stream_mode; + cam->params.camera_state.stream_mode = 0; + ret = cpia2_usb_change_streaming_alternate(cam, alternate); + if (ret < 0) { + int ret2; + ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret); + cam->params.camera_state.stream_mode = old_alt; + ret2 = set_alternate(cam, USBIF_CMDONLY); + if (ret2 < 0) { + ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already " + "failed. Then tried to call " + "set_alternate(USBIF_CMDONLY) = %d.\n", + alternate, ret, ret2); + } + } else { + cam->frame_count = 0; + cam->streaming = 1; + ret = cpia2_usb_stream_resume(cam); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_pause + * + *****************************************************************************/ +int cpia2_usb_stream_pause(struct camera_data *cam) +{ + int ret = 0; + if(cam->streaming) { + free_sbufs(cam); + ret = set_alternate(cam, USBIF_CMDONLY); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_resume + * + *****************************************************************************/ +int cpia2_usb_stream_resume(struct camera_data *cam) +{ + int ret = 0; + if(cam->streaming) { + cam->first_image_seen = 0; + ret = set_alternate(cam, cam->params.camera_state.stream_mode); + if(ret == 0) { + /* for some reason the user effects need to be set + again when starting streaming. */ + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam->params.vp_params.user_effects); + ret = submit_urbs(cam); + } + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_stop + * + *****************************************************************************/ +int cpia2_usb_stream_stop(struct camera_data *cam) +{ + int ret; + + ret = cpia2_usb_stream_pause(cam); + cam->streaming = 0; + configure_transfer_mode(cam, 0); + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_probe + * + * Probe and initialize. + *****************************************************************************/ +static int cpia2_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_interface_descriptor *interface; + struct camera_data *cam; + int ret; + + /* A multi-config CPiA2 camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + interface = &intf->cur_altsetting->desc; + + /* If we get to this point, we found a CPiA2 camera */ + LOG("CPiA2 USB camera found\n"); + + cam = cpia2_init_camera_struct(intf); + if (cam == NULL) + return -ENOMEM; + + cam->dev = udev; + cam->iface = interface->bInterfaceNumber; + + ret = set_alternate(cam, USBIF_CMDONLY); + if (ret < 0) { + ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret); + kfree(cam); + return ret; + } + + + if((ret = cpia2_init_camera(cam)) < 0) { + ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); + kfree(cam); + return ret; + } + LOG(" CPiA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmware_revision_hi, + cam->params.version.firmware_revision_lo, + cam->params.version.asic_id, + cam->params.version.asic_rev); + LOG(" CPiA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnp_id.vendor, + cam->params.pnp_id.product, + cam->params.pnp_id.device_revision); + LOG(" SensorID: %d.(version %d)\n", + cam->params.version.sensor_flags, + cam->params.version.sensor_rev); + + usb_set_intfdata(intf, cam); + + ret = cpia2_register_camera(cam); + if (ret < 0) { + ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); + kfree(cam); + return ret; + } + + return 0; +} + +/****************************************************************************** + * + * cpia2_disconnect + * + *****************************************************************************/ +static void cpia2_usb_disconnect(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + DBG("Stopping stream\n"); + cpia2_usb_stream_stop(cam); + + mutex_lock(&cam->v4l2_lock); + DBG("Unregistering camera\n"); + cpia2_unregister_camera(cam); + v4l2_device_disconnect(&cam->v4l2_dev); + mutex_unlock(&cam->v4l2_lock); + v4l2_device_put(&cam->v4l2_dev); + + if(cam->buffers) { + DBG("Wakeup waiting processes\n"); + cam->curbuff->status = FRAME_READY; + cam->curbuff->length = 0; + if (waitqueue_active(&cam->wq_stream)) + wake_up_interruptible(&cam->wq_stream); + } + + DBG("Releasing interface\n"); + usb_driver_release_interface(&cpia2_driver, intf); + + LOG("CPiA2 camera disconnected.\n"); +} + +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + if (cam->streaming) { + cpia2_usb_stream_stop(cam); + cam->streaming = 1; + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "going into suspend..\n"); + return 0; +} + +/* Resume device - start device. */ +static int cpia2_usb_resume(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + v4l2_ctrl_handler_setup(&cam->hdl); + if (cam->streaming) { + cam->streaming = 0; + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "coming out of suspend..\n"); + return 0; +} + +/****************************************************************************** + * + * usb_cpia2_init + * + *****************************************************************************/ +int cpia2_usb_init(void) +{ + return usb_register(&cpia2_driver); +} + +/****************************************************************************** + * + * usb_cpia_cleanup + * + *****************************************************************************/ +void cpia2_usb_cleanup(void) +{ + schedule_timeout(2 * HZ); + usb_deregister(&cpia2_driver); +} diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c new file mode 100644 index 000000000000..5ca6f44b4c63 --- /dev/null +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -0,0 +1,1267 @@ +/**************************************************************************** + * + * Filename: cpia2_v4l.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * Copyright 2001,2005, Scott J. Bertin + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * This program is free software; 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. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox + ****************************************************************************/ + +#define CPIA_VERSION "3.0.1" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia2.h" + +static int video_nr = -1; +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); + +static int buffer_size = 68 * 1024; +module_param(buffer_size, int, 0); +MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); + +static int num_buffers = 3; +module_param(num_buffers, int, 0); +MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-" + __stringify(VIDEO_MAX_FRAME) ", default 3)"); + +static int alternate = DEFAULT_ALT; +module_param(alternate, int, 0); +MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" + __stringify(USBIF_ISO_6) ", default " + __stringify(DEFAULT_ALT) ")"); + +static int flicker_mode; +module_param(flicker_mode, int, 0); +MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " + __stringify(60) ", default 0)"); + +MODULE_AUTHOR("Steve Miller (STMicroelectronics) "); +MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); +MODULE_SUPPORTED_DEVICE("video"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CPIA_VERSION); + +#define ABOUT "V4L-Driver for Vision CPiA2 based cameras" +#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) + +/****************************************************************************** + * + * cpia2_open + * + *****************************************************************************/ +static int cpia2_open(struct file *file) +{ + struct camera_data *cam = video_drvdata(file); + int retval; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + retval = v4l2_fh_open(file); + if (retval) + goto open_unlock; + + if (v4l2_fh_is_singular_file(file)) { + if (cpia2_allocate_buffers(cam)) { + v4l2_fh_release(file); + retval = -ENOMEM; + goto open_unlock; + } + + /* reset the camera */ + if (cpia2_reset_camera(cam) < 0) { + v4l2_fh_release(file); + retval = -EIO; + goto open_unlock; + } + + cam->APP_len = 0; + cam->COM_len = 0; + } + + cpia2_dbg_dump_registers(cam); +open_unlock: + mutex_unlock(&cam->v4l2_lock); + return retval; +} + +/****************************************************************************** + * + * cpia2_close + * + *****************************************************************************/ +static int cpia2_close(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct camera_data *cam = video_get_drvdata(dev); + + mutex_lock(&cam->v4l2_lock); + if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { + cpia2_usb_stream_stop(cam); + + /* save camera state for later open */ + cpia2_save_camera_state(cam); + + cpia2_set_low_power(cam); + cpia2_free_buffers(cam); + } + + if (cam->stream_fh == file->private_data) { + cam->stream_fh = NULL; + cam->mmapped = 0; + } + mutex_unlock(&cam->v4l2_lock); + return v4l2_fh_release(file); +} + +/****************************************************************************** + * + * cpia2_v4l_read + * + *****************************************************************************/ +static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, + loff_t *off) +{ + struct camera_data *cam = video_drvdata(file); + int noblock = file->f_flags&O_NONBLOCK; + ssize_t ret; + + if(!cam) + return -EINVAL; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + ret = cpia2_read(cam, buf, count, noblock); + mutex_unlock(&cam->v4l2_lock); + return ret; +} + + +/****************************************************************************** + * + * cpia2_v4l_poll + * + *****************************************************************************/ +static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct camera_data *cam = video_drvdata(filp); + unsigned int res; + + mutex_lock(&cam->v4l2_lock); + res = cpia2_poll(cam, filp, wait); + mutex_unlock(&cam->v4l2_lock); + return res; +} + + +static int sync(struct camera_data *cam, int frame_nr) +{ + struct framebuf *frame = &cam->buffers[frame_nr]; + + while (1) { + if (frame->status == FRAME_READY) + return 0; + + if (!cam->streaming) { + frame->status = FRAME_READY; + frame->length = 0; + return 0; + } + + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !cam->streaming || + frame->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return -ENOTTY; + } +} + +/****************************************************************************** + * + * ioctl_querycap + * + * V4L2 device capabilities + * + *****************************************************************************/ + +static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc) +{ + struct camera_data *cam = video_drvdata(file); + + strcpy(vc->driver, "cpia2"); + + if (cam->params.pnp_id.product == 0x151) + strcpy(vc->card, "QX5 Microscope"); + else + strcpy(vc->card, "CPiA2 Camera"); + switch (cam->params.pnp_id.device_type) { + case DEVICE_STV_672: + strcat(vc->card, " (672/"); + break; + case DEVICE_STV_676: + strcat(vc->card, " (676/"); + break; + default: + strcat(vc->card, " (XXX/"); + break; + } + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + strcat(vc->card, "404)"); + break; + case CPIA2_VP_SENSOR_FLAGS_407: + strcat(vc->card, "407)"); + break; + case CPIA2_VP_SENSOR_FLAGS_409: + strcat(vc->card, "409)"); + break; + case CPIA2_VP_SENSOR_FLAGS_410: + strcat(vc->card, "410)"); + break; + case CPIA2_VP_SENSOR_FLAGS_500: + strcat(vc->card, "500)"); + break; + default: + strcat(vc->card, "XXX)"); + break; + } + + if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0) + memset(vc->bus_info,0, sizeof(vc->bus_info)); + + vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + vc->capabilities = vc->device_caps | + V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +/****************************************************************************** + * + * ioctl_input + * + * V4L2 input get/set/enumerate + * + *****************************************************************************/ + +static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index) + return -EINVAL; + strcpy(i->name, "Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int cpia2_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cpia2_s_input(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +/****************************************************************************** + * + * ioctl_enum_fmt + * + * V4L2 format enumerate + * + *****************************************************************************/ + +static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + int index = f->index; + + if (index < 0 || index > 1) + return -EINVAL; + + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + switch(index) { + case 0: + strcpy(f->description, "MJPEG"); + f->pixelformat = V4L2_PIX_FMT_MJPEG; + break; + case 1: + strcpy(f->description, "JPEG"); + f->pixelformat = V4L2_PIX_FMT_JPEG; + break; + default: + return -EINVAL; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_try_fmt + * + * V4L2 format try + * + *****************************************************************************/ + +static int cpia2_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = cam->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + + switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) { + case VIDEOSIZE_VGA: + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + break; + case VIDEOSIZE_CIF: + f->fmt.pix.width = 352; + f->fmt.pix.height = 288; + break; + case VIDEOSIZE_QVGA: + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + break; + case VIDEOSIZE_288_216: + f->fmt.pix.width = 288; + f->fmt.pix.height = 216; + break; + case VIDEOSIZE_256_192: + f->fmt.pix.width = 256; + f->fmt.pix.height = 192; + break; + case VIDEOSIZE_224_168: + f->fmt.pix.width = 224; + f->fmt.pix.height = 168; + break; + case VIDEOSIZE_192_144: + f->fmt.pix.width = 192; + f->fmt.pix.height = 144; + break; + case VIDEOSIZE_QCIF: + default: + f->fmt.pix.width = 176; + f->fmt.pix.height = 144; + break; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_set_fmt + * + * V4L2 format set + * + *****************************************************************************/ + +static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + int err, frame; + + err = cpia2_try_fmt_vid_cap(file, _fh, f); + if(err != 0) + return err; + + cam->pixelformat = f->fmt.pix.pixelformat; + + /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle + * the missing Huffman table properly. */ + cam->params.compression.inhibit_htables = 0; + /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/ + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + DBG("Requested width = %d, height = %d\n", + f->fmt.pix.width, f->fmt.pix.height); + if (f->fmt.pix.width != cam->width || + f->fmt.pix.height != cam->height) { + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + cam->params.roi.width = f->fmt.pix.width; + cam->params.roi.height = f->fmt.pix.height; + cpia2_set_format(cam); + } + + for (frame = 0; frame < cam->num_frames; ++frame) { + if (cam->buffers[frame].status == FRAME_READING) + if ((err = sync(cam, frame)) < 0) + return err; + + cam->buffers[frame].status = FRAME_EMPTY; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_get_fmt + * + * V4L2 format get + * + *****************************************************************************/ + +static int cpia2_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.pixelformat = cam->pixelformat; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = cam->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + + return 0; +} + +/****************************************************************************** + * + * ioctl_cropcap + * + * V4L2 query cropping capabilities + * NOTE: cropping is currently disabled + * + *****************************************************************************/ + +static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c) +{ + struct camera_data *cam = video_drvdata(file); + + if (c->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + c->bounds.left = 0; + c->bounds.top = 0; + c->bounds.width = cam->width; + c->bounds.height = cam->height; + c->defrect.left = 0; + c->defrect.top = 0; + c->defrect.width = cam->width; + c->defrect.height = cam->height; + c->pixelaspect.numerator = 1; + c->pixelaspect.denominator = 1; + + return 0; +} + +struct framerate_info { + int value; + struct v4l2_fract period; +}; + +static const struct framerate_info framerate_controls[] = { + { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, + { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, + { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, + { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, + { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, + { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, +}; + +static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) +{ + struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + int i; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cap->capability = V4L2_CAP_TIMEPERFRAME; + cap->readbuffers = cam->num_frames; + for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) + if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { + cap->timeperframe = framerate_controls[i].period; + break; + } + return 0; +} + +static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) +{ + struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + struct v4l2_fract tpf = cap->timeperframe; + int max = ARRAY_SIZE(framerate_controls) - 1; + int ret; + int i; + + ret = cpia2_g_parm(file, fh, p); + if (ret || !tpf.denominator || !tpf.numerator) + return ret; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + for (i = 0; i <= max; i++) { + struct v4l2_fract f1 = tpf; + struct v4l2_fract f2 = framerate_controls[i].period; + + f1.numerator *= f2.denominator; + f2.numerator *= f1.denominator; + if (f1.numerator >= f2.numerator) + break; + } + if (i > max) + i = max; + cap->timeperframe = framerate_controls[i].period; + return cpia2_set_fps(cam, framerate_controls[i].value); +} + +static const struct { + u32 width; + u32 height; +} cpia2_framesizes[] = { + { 640, 480 }, + { 352, 288 }, + { 320, 240 }, + { 288, 216 }, + { 256, 192 }, + { 224, 168 }, + { 192, 144 }, + { 176, 144 }, +}; + +static int cpia2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + + if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && + fsize->pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = cpia2_framesizes[fsize->index].width; + fsize->discrete.height = cpia2_framesizes[fsize->index].height; + + return 0; +} + +static int cpia2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct camera_data *cam = video_drvdata(file); + int max = ARRAY_SIZE(framerate_controls) - 1; + int i; + + if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && + fival->pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + if (fival->index > max) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) + if (fival->width == cpia2_framesizes[i].width && + fival->height == cpia2_framesizes[i].height) + break; + if (i == ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = framerate_controls[fival->index].period; + return 0; +} + +/****************************************************************************** + * + * ioctl_s_ctrl + * + * V4L2 set the value of a control variable + * + *****************************************************************************/ + +static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct camera_data *cam = + container_of(ctrl->handler, struct camera_data, hdl); + static const int flicker_table[] = { + NEVER_FLICKER, + FLICKER_50, + FLICKER_60, + }; + + DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cpia2_set_brightness(cam, ctrl->val); + break; + case V4L2_CID_CONTRAST: + cpia2_set_contrast(cam, ctrl->val); + break; + case V4L2_CID_SATURATION: + cpia2_set_saturation(cam, ctrl->val); + break; + case V4L2_CID_HFLIP: + cpia2_set_property_mirror(cam, ctrl->val); + break; + case V4L2_CID_VFLIP: + cpia2_set_property_flip(cam, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); + case V4L2_CID_ILLUMINATORS_1: + return cpia2_set_gpio(cam, (cam->top_light->val << 6) | + (cam->bottom_light->val << 7)); + case V4L2_CID_JPEG_ACTIVE_MARKER: + cam->params.compression.inhibit_htables = + !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + cam->params.vc_params.quality = ctrl->val; + break; + case CPIA2_CID_USB_ALT: + cam->params.camera_state.stream_mode = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_g_jpegcomp + * + * V4L2 get the JPEG compression parameters + * + *****************************************************************************/ + +static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) +{ + struct camera_data *cam = video_drvdata(file); + + memset(parms, 0, sizeof(*parms)); + + parms->quality = 80; // TODO: Can this be made meaningful? + + parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; + if(!cam->params.compression.inhibit_htables) { + parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT; + } + + parms->APPn = cam->APPn; + parms->APP_len = cam->APP_len; + if(cam->APP_len > 0) { + memcpy(parms->APP_data, cam->APP_data, cam->APP_len); + parms->jpeg_markers |= V4L2_JPEG_MARKER_APP; + } + + parms->COM_len = cam->COM_len; + if(cam->COM_len > 0) { + memcpy(parms->COM_data, cam->COM_data, cam->COM_len); + parms->jpeg_markers |= JPEG_MARKER_COM; + } + + DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n", + parms->APP_len, parms->COM_len); + + return 0; +} + +/****************************************************************************** + * + * ioctl_s_jpegcomp + * + * V4L2 set the JPEG compression parameters + * NOTE: quality and some jpeg_markers are ignored. + * + *****************************************************************************/ + +static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) +{ + struct camera_data *cam = video_drvdata(file); + + DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n", + parms->APP_len, parms->COM_len); + + cam->params.compression.inhibit_htables = + !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); + parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | + V4L2_JPEG_MARKER_DHT; + + if(parms->APP_len != 0) { + if(parms->APP_len > 0 && + parms->APP_len <= sizeof(cam->APP_data) && + parms->APPn >= 0 && parms->APPn <= 15) { + cam->APPn = parms->APPn; + cam->APP_len = parms->APP_len; + memcpy(cam->APP_data, parms->APP_data, parms->APP_len); + } else { + LOG("Bad APPn Params n=%d len=%d\n", + parms->APPn, parms->APP_len); + return -EINVAL; + } + } else { + cam->APP_len = 0; + } + + if(parms->COM_len != 0) { + if(parms->COM_len > 0 && + parms->COM_len <= sizeof(cam->COM_data)) { + cam->COM_len = parms->COM_len; + memcpy(cam->COM_data, parms->COM_data, parms->COM_len); + } else { + LOG("Bad COM_len=%d\n", parms->COM_len); + return -EINVAL; + } + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_reqbufs + * + * V4L2 Initiate memory mapping. + * NOTE: The user's request is ignored. For now the buffers are fixed. + * + *****************************************************************************/ + +static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) +{ + struct camera_data *cam = video_drvdata(file); + + if(req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames); + req->count = cam->num_frames; + memset(&req->reserved, 0, sizeof(req->reserved)); + + return 0; +} + +/****************************************************************************** + * + * ioctl_querybuf + * + * V4L2 Query memory buffer status. + * + *****************************************************************************/ + +static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + + if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->index > cam->num_frames) + return -EINVAL; + + buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; + buf->length = cam->frame_size; + + buf->memory = V4L2_MEMORY_MMAP; + + if(cam->mmapped) + buf->flags = V4L2_BUF_FLAG_MAPPED; + else + buf->flags = 0; + + switch (cam->buffers[buf->index].status) { + case FRAME_EMPTY: + case FRAME_ERROR: + case FRAME_READING: + buf->bytesused = 0; + buf->flags = V4L2_BUF_FLAG_QUEUED; + break; + case FRAME_READY: + buf->bytesused = cam->buffers[buf->index].length; + buf->timestamp = cam->buffers[buf->index].timestamp; + buf->sequence = cam->buffers[buf->index].seq; + buf->flags = V4L2_BUF_FLAG_DONE; + break; + } + + DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n", + buf->index, buf->m.offset, buf->flags, buf->sequence, + buf->bytesused); + + return 0; +} + +/****************************************************************************** + * + * ioctl_qbuf + * + * V4L2 User is freeing buffer + * + *****************************************************************************/ + +static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + + if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP || + buf->index > cam->num_frames) + return -EINVAL; + + DBG("QBUF #%d\n", buf->index); + + if(cam->buffers[buf->index].status == FRAME_READY) + cam->buffers[buf->index].status = FRAME_EMPTY; + + return 0; +} + +/****************************************************************************** + * + * find_earliest_filled_buffer + * + * Helper for ioctl_dqbuf. Find the next ready buffer. + * + *****************************************************************************/ + +static int find_earliest_filled_buffer(struct camera_data *cam) +{ + int i; + int found = -1; + for (i=0; inum_frames; i++) { + if(cam->buffers[i].status == FRAME_READY) { + if(found < 0) { + found = i; + } else { + /* find which buffer is earlier */ + struct timeval *tv1, *tv2; + tv1 = &cam->buffers[i].timestamp; + tv2 = &cam->buffers[found].timestamp; + if(tv1->tv_sec < tv2->tv_sec || + (tv1->tv_sec == tv2->tv_sec && + tv1->tv_usec < tv2->tv_usec)) + found = i; + } + } + } + return found; +} + +/****************************************************************************** + * + * ioctl_dqbuf + * + * V4L2 User is asking for a filled buffer. + * + *****************************************************************************/ + +static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + int frame; + + if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + frame = find_earliest_filled_buffer(cam); + + if(frame < 0 && file->f_flags&O_NONBLOCK) + return -EAGAIN; + + if(frame < 0) { + /* Wait for a frame to become available */ + struct framebuf *cb=cam->curbuff; + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !video_is_registered(&cam->vdev) || + (cb=cam->curbuff)->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return -ENOTTY; + frame = cb->num; + } + + + buf->index = frame; + buf->bytesused = cam->buffers[buf->index].length; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; + buf->field = V4L2_FIELD_NONE; + buf->timestamp = cam->buffers[buf->index].timestamp; + buf->sequence = cam->buffers[buf->index].seq; + buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; + buf->length = cam->frame_size; + buf->reserved2 = 0; + buf->reserved = 0; + memset(&buf->timecode, 0, sizeof(buf->timecode)); + + DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index, + cam->buffers[buf->index].status, buf->sequence, buf->bytesused); + + return 0; +} + +static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; + + DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!cam->streaming) { + ret = cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, true); + } + return ret; +} + +static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; + + DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->streaming) { + ret = cpia2_usb_stream_stop(cam); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, false); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_mmap + * + *****************************************************************************/ +static int cpia2_mmap(struct file *file, struct vm_area_struct *area) +{ + struct camera_data *cam = video_drvdata(file); + int retval; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + retval = cpia2_remap_buffer(cam, area); + + if(!retval) + cam->stream_fh = file->private_data; + mutex_unlock(&cam->v4l2_lock); + return retval; +} + +/****************************************************************************** + * + * reset_camera_struct_v4l + * + * Sets all values to the defaults + *****************************************************************************/ +static void reset_camera_struct_v4l(struct camera_data *cam) +{ + cam->width = cam->params.roi.width; + cam->height = cam->params.roi.height; + + cam->frame_size = buffer_size; + cam->num_frames = num_buffers; + + /* Flicker modes */ + cam->params.flicker_control.flicker_mode_req = flicker_mode; + + /* stream modes */ + cam->params.camera_state.stream_mode = alternate; + + cam->pixelformat = V4L2_PIX_FMT_JPEG; +} + +static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { + .vidioc_querycap = cpia2_querycap, + .vidioc_enum_input = cpia2_enum_input, + .vidioc_g_input = cpia2_g_input, + .vidioc_s_input = cpia2_s_input, + .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, + .vidioc_g_jpegcomp = cpia2_g_jpegcomp, + .vidioc_s_jpegcomp = cpia2_s_jpegcomp, + .vidioc_cropcap = cpia2_cropcap, + .vidioc_reqbufs = cpia2_reqbufs, + .vidioc_querybuf = cpia2_querybuf, + .vidioc_qbuf = cpia2_qbuf, + .vidioc_dqbuf = cpia2_dqbuf, + .vidioc_streamon = cpia2_streamon, + .vidioc_streamoff = cpia2_streamoff, + .vidioc_s_parm = cpia2_s_parm, + .vidioc_g_parm = cpia2_g_parm, + .vidioc_enum_framesizes = cpia2_enum_framesizes, + .vidioc_enum_frameintervals = cpia2_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/*** + * The v4l video device structure initialized for this device + ***/ +static const struct v4l2_file_operations cpia2_fops = { + .owner = THIS_MODULE, + .open = cpia2_open, + .release = cpia2_close, + .read = cpia2_v4l_read, + .poll = cpia2_v4l_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = cpia2_mmap, +}; + +static struct video_device cpia2_template = { + /* I could not find any place for the old .initialize initializer?? */ + .name = "CPiA2 Camera", + .fops = &cpia2_fops, + .ioctl_ops = &cpia2_ioctl_ops, + .release = video_device_release_empty, +}; + +void cpia2_camera_release(struct v4l2_device *v4l2_dev) +{ + struct camera_data *cam = + container_of(v4l2_dev, struct camera_data, v4l2_dev); + + v4l2_ctrl_handler_free(&cam->hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { + .s_ctrl = cpia2_s_ctrl, +}; + +/****************************************************************************** + * + * cpia2_register_camera + * + *****************************************************************************/ +int cpia2_register_camera(struct camera_data *cam) +{ + struct v4l2_ctrl_handler *hdl = &cam->hdl; + struct v4l2_ctrl_config cpia2_usb_alt = { + .ops = &cpia2_ctrl_ops, + .id = CPIA2_CID_USB_ALT, + .name = "USB Alternate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = USBIF_ISO_1, + .max = USBIF_ISO_6, + .step = 1, + }; + int ret; + + v4l2_ctrl_handler_init(hdl, 12); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_BRIGHTNESS, + cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, + 255, 1, DEFAULT_BRIGHTNESS); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, + 100, 1, 100); + cpia2_usb_alt.def = alternate; + cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); + /* VP5 Only */ + if (cam->params.pnp_id.device_type != DEVICE_STV_672) + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* Flicker control only valid for 672 */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); + /* Light control only valid for the QX5 Microscope */ + if (cam->params.pnp_id.product == 0x151) { + cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &cam->top_light); + } + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + cam->vdev = cpia2_template; + video_set_drvdata(&cam->vdev, cam); + cam->vdev.lock = &cam->v4l2_lock; + cam->vdev.ctrl_handler = hdl; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); + + reset_camera_struct_v4l(cam); + + /* register v4l device */ + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + ERR("video_register_device failed\n"); + return -ENODEV; + } + + return 0; +} + +/****************************************************************************** + * + * cpia2_unregister_camera + * + *****************************************************************************/ +void cpia2_unregister_camera(struct camera_data *cam) +{ + video_unregister_device(&cam->vdev); +} + +/****************************************************************************** + * + * check_parameters + * + * Make sure that all user-supplied parameters are sensible + *****************************************************************************/ +static void __init check_parameters(void) +{ + if(buffer_size < PAGE_SIZE) { + buffer_size = PAGE_SIZE; + LOG("buffer_size too small, setting to %d\n", buffer_size); + } else if(buffer_size > 1024*1024) { + /* arbitrary upper limiit */ + buffer_size = 1024*1024; + LOG("buffer_size ridiculously large, setting to %d\n", + buffer_size); + } else { + buffer_size += PAGE_SIZE-1; + buffer_size &= ~(PAGE_SIZE-1); + } + + if(num_buffers < 1) { + num_buffers = 1; + LOG("num_buffers too small, setting to %d\n", num_buffers); + } else if(num_buffers > VIDEO_MAX_FRAME) { + num_buffers = VIDEO_MAX_FRAME; + LOG("num_buffers too large, setting to %d\n", num_buffers); + } + + if(alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) { + alternate = DEFAULT_ALT; + LOG("alternate specified is invalid, using %d\n", alternate); + } + + if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { + flicker_mode = 0; + LOG("Flicker mode specified is invalid, using %d\n", + flicker_mode); + } + + DBG("Using %d buffers, each %d bytes, alternate=%d\n", + num_buffers, buffer_size, alternate); +} + +/************ Module Stuff ***************/ + + +/****************************************************************************** + * + * cpia2_init/module_init + * + *****************************************************************************/ +static int __init cpia2_init(void) +{ + LOG("%s v%s\n", + ABOUT, CPIA_VERSION); + check_parameters(); + cpia2_usb_init(); + return 0; +} + + +/****************************************************************************** + * + * cpia2_exit/module_exit + * + *****************************************************************************/ +static void __exit cpia2_exit(void) +{ + cpia2_usb_cleanup(); + schedule_timeout(2 * HZ); +} + +module_init(cpia2_init); +module_exit(cpia2_exit); diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig new file mode 100644 index 000000000000..446f692aabb7 --- /dev/null +++ b/drivers/media/usb/cx231xx/Kconfig @@ -0,0 +1,51 @@ +config VIDEO_CX231XX + tristate "Conexant cx231xx USB video capture support" + depends on VIDEO_DEV && I2C + select VIDEO_TUNER + select VIDEO_TVEEPROM + depends on RC_CORE + select VIDEOBUF_VMALLOC + select VIDEO_CX25840 + select VIDEO_CX2341X + + ---help--- + This is a video4linux driver for Conexant 231xx USB based TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx231xx + +config VIDEO_CX231XX_RC + bool "Conexant cx231xx Remote Controller additional support" + depends on RC_CORE + depends on VIDEO_CX231XX + default y + ---help--- + cx231xx hardware has a builtin RX/TX support. However, a few + designs opted to not use it, but, instead, some other hardware. + This module enables the usage of those other hardware, like the + ones used with ISDB-T boards. + + On most cases, all you need for IR is mceusb module. + +config VIDEO_CX231XX_ALSA + tristate "Conexant Cx231xx ALSA audio module" + depends on VIDEO_CX231XX && SND + select SND_PCM + + ---help--- + This is an ALSA driver for Cx231xx USB based TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx231xx-alsa + +config VIDEO_CX231XX_DVB + tristate "DVB/ATSC Support for Cx231xx based TV cards" + depends on VIDEO_CX231XX && DVB_CORE && DVB_CAPTURE_DRIVERS + select VIDEOBUF_DVB + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select DVB_MB86A20S if !DVB_FE_CUSTOMISE + + ---help--- + This adds support for DVB cards based on the + Conexant cx231xx chips. diff --git a/drivers/media/usb/cx231xx/Makefile b/drivers/media/usb/cx231xx/Makefile new file mode 100644 index 000000000000..1d40fce77601 --- /dev/null +++ b/drivers/media/usb/cx231xx/Makefile @@ -0,0 +1,15 @@ +cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o +cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o +cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.o + +cx231xx-alsa-objs := cx231xx-audio.o + +obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o +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/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/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c new file mode 100644 index 000000000000..b024e5197a75 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -0,0 +1,2197 @@ +/* + * + * Support for a cx23417 mpeg encoder via cx231xx host port. + * + * (c) 2004 Jelle Foks + * (c) 2004 Gerd Knorr + * (c) 2008 Steven Toth + * - CX23885/7/8 support + * + * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cx231xx.h" +/*#include "cx23885-ioctl.h"*/ + +#define CX231xx_FIRM_IMAGE_SIZE 376836 +#define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" + +/* for polaris ITVC */ +#define ITVC_WRITE_DIR 0x03FDFC00 +#define ITVC_READ_DIR 0x0001FC00 + +#define MCI_MEMORY_DATA_BYTE0 0x00 +#define MCI_MEMORY_DATA_BYTE1 0x08 +#define MCI_MEMORY_DATA_BYTE2 0x10 +#define MCI_MEMORY_DATA_BYTE3 0x18 + +#define MCI_MEMORY_ADDRESS_BYTE2 0x20 +#define MCI_MEMORY_ADDRESS_BYTE1 0x28 +#define MCI_MEMORY_ADDRESS_BYTE0 0x30 + +#define MCI_REGISTER_DATA_BYTE0 0x40 +#define MCI_REGISTER_DATA_BYTE1 0x48 +#define MCI_REGISTER_DATA_BYTE2 0x50 +#define MCI_REGISTER_DATA_BYTE3 0x58 + +#define MCI_REGISTER_ADDRESS_BYTE0 0x60 +#define MCI_REGISTER_ADDRESS_BYTE1 0x68 + +#define MCI_REGISTER_MODE 0x70 + +/* Read and write modes for polaris ITVC */ +#define MCI_MODE_REGISTER_READ 0x000 +#define MCI_MODE_REGISTER_WRITE 0x100 +#define MCI_MODE_MEMORY_READ 0x000 +#define MCI_MODE_MEMORY_WRITE 0x4000 + +static unsigned int mpegbufs = 8; +module_param(mpegbufs, int, 0644); +MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32"); +static unsigned int mpeglines = 128; +module_param(mpeglines, int, 0644); +MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32"); +static unsigned int mpeglinesize = 512; +module_param(mpeglinesize, int, 0644); +MODULE_PARM_DESC(mpeglinesize, + "number of bytes in each line of an MPEG buffer, range 512-1024"); + +static unsigned int v4l_debug = 1; +module_param(v4l_debug, int, 0644); +MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); +struct cx231xx_dmaqueue *dma_qq; +#define dprintk(level, fmt, arg...)\ + do { if (v4l_debug >= level) \ + printk(KERN_INFO "%s: " fmt, \ + (dev) ? dev->name : "cx231xx[?]", ## arg); \ + } while (0) + +static struct cx231xx_tvnorm cx231xx_tvnorms[] = { + { + .name = "NTSC-M", + .id = V4L2_STD_NTSC_M, + }, { + .name = "NTSC-JP", + .id = V4L2_STD_NTSC_M_JP, + }, { + .name = "PAL-BG", + .id = V4L2_STD_PAL_BG, + }, { + .name = "PAL-DK", + .id = V4L2_STD_PAL_DK, + }, { + .name = "PAL-I", + .id = V4L2_STD_PAL_I, + }, { + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + }, { + .name = "PAL-N", + .id = V4L2_STD_PAL_N, + }, { + .name = "PAL-Nc", + .id = V4L2_STD_PAL_Nc, + }, { + .name = "PAL-60", + .id = V4L2_STD_PAL_60, + }, { + .name = "SECAM-L", + .id = V4L2_STD_SECAM_L, + }, { + .name = "SECAM-DK", + .id = V4L2_STD_SECAM_DK, + } +}; + +/* ------------------------------------------------------------------ */ +enum cx231xx_capture_type { + CX231xx_MPEG_CAPTURE, + CX231xx_RAW_CAPTURE, + CX231xx_RAW_PASSTHRU_CAPTURE +}; +enum cx231xx_capture_bits { + CX231xx_RAW_BITS_NONE = 0x00, + CX231xx_RAW_BITS_YUV_CAPTURE = 0x01, + CX231xx_RAW_BITS_PCM_CAPTURE = 0x02, + CX231xx_RAW_BITS_VBI_CAPTURE = 0x04, + CX231xx_RAW_BITS_PASSTHRU_CAPTURE = 0x08, + CX231xx_RAW_BITS_TO_HOST_CAPTURE = 0x10 +}; +enum cx231xx_capture_end { + CX231xx_END_AT_GOP, /* stop at the end of gop, generate irq */ + CX231xx_END_NOW, /* stop immediately, no irq */ +}; +enum cx231xx_framerate { + CX231xx_FRAMERATE_NTSC_30, /* NTSC: 30fps */ + CX231xx_FRAMERATE_PAL_25 /* PAL: 25fps */ +}; +enum cx231xx_stream_port { + CX231xx_OUTPUT_PORT_MEMORY, + CX231xx_OUTPUT_PORT_STREAMING, + CX231xx_OUTPUT_PORT_SERIAL +}; +enum cx231xx_data_xfer_status { + CX231xx_MORE_BUFFERS_FOLLOW, + CX231xx_LAST_BUFFER, +}; +enum cx231xx_picture_mask { + CX231xx_PICTURE_MASK_NONE, + CX231xx_PICTURE_MASK_I_FRAMES, + CX231xx_PICTURE_MASK_I_P_FRAMES = 0x3, + CX231xx_PICTURE_MASK_ALL_FRAMES = 0x7, +}; +enum cx231xx_vbi_mode_bits { + CX231xx_VBI_BITS_SLICED, + CX231xx_VBI_BITS_RAW, +}; +enum cx231xx_vbi_insertion_bits { + CX231xx_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, + CX231xx_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, + CX231xx_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, + CX231xx_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, + CX231xx_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, +}; +enum cx231xx_dma_unit { + CX231xx_DMA_BYTES, + CX231xx_DMA_FRAMES, +}; +enum cx231xx_dma_transfer_status_bits { + CX231xx_DMA_TRANSFER_BITS_DONE = 0x01, + CX231xx_DMA_TRANSFER_BITS_ERROR = 0x04, + CX231xx_DMA_TRANSFER_BITS_LL_ERROR = 0x10, +}; +enum cx231xx_pause { + CX231xx_PAUSE_ENCODING, + CX231xx_RESUME_ENCODING, +}; +enum cx231xx_copyright { + CX231xx_COPYRIGHT_OFF, + CX231xx_COPYRIGHT_ON, +}; +enum cx231xx_notification_type { + CX231xx_NOTIFICATION_REFRESH, +}; +enum cx231xx_notification_status { + CX231xx_NOTIFICATION_OFF, + CX231xx_NOTIFICATION_ON, +}; +enum cx231xx_notification_mailbox { + CX231xx_NOTIFICATION_NO_MAILBOX = -1, +}; +enum cx231xx_field1_lines { + CX231xx_FIELD1_SAA7114 = 0x00EF, /* 239 */ + CX231xx_FIELD1_SAA7115 = 0x00F0, /* 240 */ + CX231xx_FIELD1_MICRONAS = 0x0105, /* 261 */ +}; +enum cx231xx_field2_lines { + CX231xx_FIELD2_SAA7114 = 0x00EF, /* 239 */ + CX231xx_FIELD2_SAA7115 = 0x00F0, /* 240 */ + CX231xx_FIELD2_MICRONAS = 0x0106, /* 262 */ +}; +enum cx231xx_custom_data_type { + CX231xx_CUSTOM_EXTENSION_USR_DATA, + CX231xx_CUSTOM_PRIVATE_PACKET, +}; +enum cx231xx_mute { + CX231xx_UNMUTE, + CX231xx_MUTE, +}; +enum cx231xx_mute_video_mask { + CX231xx_MUTE_VIDEO_V_MASK = 0x0000FF00, + CX231xx_MUTE_VIDEO_U_MASK = 0x00FF0000, + CX231xx_MUTE_VIDEO_Y_MASK = 0xFF000000, +}; +enum cx231xx_mute_video_shift { + CX231xx_MUTE_VIDEO_V_SHIFT = 8, + CX231xx_MUTE_VIDEO_U_SHIFT = 16, + CX231xx_MUTE_VIDEO_Y_SHIFT = 24, +}; + +/* defines below are from ivtv-driver.h */ +#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF + +/* Firmware API commands */ +#define IVTV_API_STD_TIMEOUT 500 + +/* Registers */ +/* IVTV_REG_OFFSET */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) +#define IVTV_REG_SPU (0x9050) +#define IVTV_REG_HW_BLOCKS (0x9054) +#define IVTV_REG_VPU (0x9058) +#define IVTV_REG_APU (0xA064) + +/* + * Bit definitions for MC417_RWD and MC417_OEN registers + * + * bits 31-16 + *+-----------+ + *| Reserved | + *|+-----------+ + *| bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 + *|+-------+-------+-------+-------+-------+-------+-------+-------+ + *|| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0| + *|+-------+-------+-------+-------+-------+-------+-------+-------+ + *| bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 + *|+-------+-------+-------+-------+-------+-------+-------+-------+ + *||MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0| + *|+-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#define MC417_MIWR 0x8000 +#define MC417_MIRD 0x4000 +#define MC417_MICS 0x2000 +#define MC417_MIRDY 0x1000 +#define MC417_MIADDR 0x0F00 +#define MC417_MIDATA 0x00FF + + +/* Bit definitions for MC417_CTL register **** + *bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0 + *+--------+-------------+--------+--------------+------------+ + *|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN| + *+--------+-------------+--------+--------------+------------+ + */ +#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030) +#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006) +#define MC417_UART_GPIO_EN 0x00000001 + +/* Values for speed control */ +#define MC417_SPD_CTL_SLOW 0x1 +#define MC417_SPD_CTL_MEDIUM 0x0 +#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */ + +/* Values for GPIO select */ +#define MC417_GPIO_SEL_GPIO3 0x3 +#define MC417_GPIO_SEL_GPIO2 0x2 +#define MC417_GPIO_SEL_GPIO1 0x1 +#define MC417_GPIO_SEL_GPIO0 0x0 + + +#define CX23417_GPIO_MASK 0xFC0003FF +static int setITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 value) +{ + int status = 0; + u32 _gpio_direction = 0; + + _gpio_direction = _gpio_direction & CX23417_GPIO_MASK; + _gpio_direction = _gpio_direction|gpio_direction; + status = cx231xx_send_gpio_cmd(dev, _gpio_direction, + (u8 *)&value, 4, 0, 0); + return status; +} +static int getITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 *pValue) +{ + int status = 0; + u32 _gpio_direction = 0; + + _gpio_direction = _gpio_direction & CX23417_GPIO_MASK; + _gpio_direction = _gpio_direction|gpio_direction; + + status = cx231xx_send_gpio_cmd(dev, _gpio_direction, + (u8 *)pValue, 4, 0, 1); + return status; +} + +static int waitForMciComplete(struct cx231xx *dev) +{ + u32 gpio; + u32 gpio_driection = 0; + u8 count = 0; + getITVCReg(dev, gpio_driection, &gpio); + + while (!(gpio&0x020000)) { + msleep(10); + + getITVCReg(dev, gpio_driection, &gpio); + + if (count++ > 100) { + dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio); + return -1; + } + } + return 0; +} + +static int mc417_register_write(struct cx231xx *dev, u16 address, u32 value) +{ + u32 temp; + int status = 0; + + temp = 0x82|MCI_REGISTER_DATA_BYTE0|((value&0x000000FF)<<8); + temp = temp<<10; + status = setITVCReg(dev, ITVC_WRITE_DIR, temp); + if (status < 0) + return status; + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write data byte 1;*/ + temp = 0x82|MCI_REGISTER_DATA_BYTE1|(value&0x0000FF00); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write data byte 2;*/ + temp = 0x82|MCI_REGISTER_DATA_BYTE2|((value&0x00FF0000)>>8); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write data byte 3;*/ + temp = 0x82|MCI_REGISTER_DATA_BYTE3|((value&0xFF000000)>>16); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write address byte 0;*/ + temp = 0x82|MCI_REGISTER_ADDRESS_BYTE0|((address&0x000000FF)<<8); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write address byte 1;*/ + temp = 0x82|MCI_REGISTER_ADDRESS_BYTE1|(address&0x0000FF00); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*Write that the mode is write.*/ + temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_WRITE; + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + return waitForMciComplete(dev); +} + +static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value) +{ + /*write address byte 0;*/ + u32 temp; + u32 return_value = 0; + int ret = 0; + + temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x00FF) << 8); + temp = temp << 10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp | ((0x05) << 10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write address byte 1;*/ + temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0xFF00); + temp = temp << 10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp | ((0x05) << 10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write that the mode is read;*/ + temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_READ; + temp = temp << 10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp | ((0x05) << 10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*wait for the MIRDY line to be asserted , + signalling that the read is done;*/ + ret = waitForMciComplete(dev); + + /*switch the DATA- GPIO to input mode;*/ + + /*Read data byte 0;*/ + temp = (0x82 | MCI_REGISTER_DATA_BYTE0) << 10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_REGISTER_DATA_BYTE0) << 10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp & 0x03FC0000) >> 18); + setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + + /* Read data byte 1;*/ + temp = (0x82 | MCI_REGISTER_DATA_BYTE1) << 10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_REGISTER_DATA_BYTE1) << 10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + + return_value |= ((temp & 0x03FC0000) >> 10); + setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + + /*Read data byte 2;*/ + temp = (0x82 | MCI_REGISTER_DATA_BYTE2) << 10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_REGISTER_DATA_BYTE2) << 10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp & 0x03FC0000) >> 2); + setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + + /*Read data byte 3;*/ + temp = (0x82 | MCI_REGISTER_DATA_BYTE3) << 10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81 | MCI_REGISTER_DATA_BYTE3) << 10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp & 0x03FC0000) << 6); + setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); + + *value = return_value; + + + return ret; +} + +static int mc417_memory_write(struct cx231xx *dev, u32 address, u32 value) +{ + /*write data byte 0;*/ + + u32 temp; + int ret = 0; + + temp = 0x82 | MCI_MEMORY_DATA_BYTE0|((value & 0x000000FF) << 8); + temp = temp << 10; + ret = setITVCReg(dev, ITVC_WRITE_DIR, temp); + if (ret < 0) + return ret; + temp = temp | ((0x05) << 10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write data byte 1;*/ + temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00); + temp = temp << 10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp | ((0x05) << 10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write data byte 2;*/ + temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write data byte 3;*/ + temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /* write address byte 2;*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | + ((address & 0x003F0000)>>8); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /* write address byte 1;*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /* write address byte 0;*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*wait for MIRDY line;*/ + waitForMciComplete(dev); + + return 0; +} + +static int mc417_memory_read(struct cx231xx *dev, u32 address, u32 *value) +{ + u32 temp = 0; + u32 return_value = 0; + int ret = 0; + + /*write address byte 2;*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ | + ((address & 0x003F0000)>>8); + temp = temp<<10; + ret = setITVCReg(dev, ITVC_WRITE_DIR, temp); + if (ret < 0) + return ret; + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write address byte 1*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*write address byte 0*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF)<<8); + temp = temp<<10; + setITVCReg(dev, ITVC_WRITE_DIR, temp); + temp = temp|((0x05)<<10); + setITVCReg(dev, ITVC_WRITE_DIR, temp); + + /*Wait for MIRDY line*/ + ret = waitForMciComplete(dev); + + + /*Read data byte 3;*/ + temp = (0x82|MCI_MEMORY_DATA_BYTE3)<<10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81|MCI_MEMORY_DATA_BYTE3)<<10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp&0x03FC0000)<<6); + setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + + /*Read data byte 2;*/ + temp = (0x82|MCI_MEMORY_DATA_BYTE2)<<10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81|MCI_MEMORY_DATA_BYTE2)<<10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp&0x03FC0000)>>2); + setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + + /* Read data byte 1;*/ + temp = (0x82|MCI_MEMORY_DATA_BYTE1)<<10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81|MCI_MEMORY_DATA_BYTE1)<<10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp&0x03FC0000)>>10); + setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + + /*Read data byte 0;*/ + temp = (0x82|MCI_MEMORY_DATA_BYTE0)<<10; + setITVCReg(dev, ITVC_READ_DIR, temp); + temp = ((0x81|MCI_MEMORY_DATA_BYTE0)<<10); + setITVCReg(dev, ITVC_READ_DIR, temp); + getITVCReg(dev, ITVC_READ_DIR, &temp); + return_value |= ((temp&0x03FC0000)>>18); + setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); + + *value = return_value; + return ret; +} + +/* ------------------------------------------------------------------ */ + +/* MPEG encoder API */ +static char *cmd_to_str(int cmd) +{ + switch (cmd) { + case CX2341X_ENC_PING_FW: + return "PING_FW"; + case CX2341X_ENC_START_CAPTURE: + return "START_CAPTURE"; + case CX2341X_ENC_STOP_CAPTURE: + return "STOP_CAPTURE"; + case CX2341X_ENC_SET_AUDIO_ID: + return "SET_AUDIO_ID"; + case CX2341X_ENC_SET_VIDEO_ID: + return "SET_VIDEO_ID"; + case CX2341X_ENC_SET_PCR_ID: + return "SET_PCR_PID"; + case CX2341X_ENC_SET_FRAME_RATE: + return "SET_FRAME_RATE"; + case CX2341X_ENC_SET_FRAME_SIZE: + return "SET_FRAME_SIZE"; + case CX2341X_ENC_SET_BIT_RATE: + return "SET_BIT_RATE"; + case CX2341X_ENC_SET_GOP_PROPERTIES: + return "SET_GOP_PROPERTIES"; + case CX2341X_ENC_SET_ASPECT_RATIO: + return "SET_ASPECT_RATIO"; + case CX2341X_ENC_SET_DNR_FILTER_MODE: + return "SET_DNR_FILTER_PROPS"; + case CX2341X_ENC_SET_DNR_FILTER_PROPS: + return "SET_DNR_FILTER_PROPS"; + case CX2341X_ENC_SET_CORING_LEVELS: + return "SET_CORING_LEVELS"; + case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: + return "SET_SPATIAL_FILTER_TYPE"; + case CX2341X_ENC_SET_VBI_LINE: + return "SET_VBI_LINE"; + case CX2341X_ENC_SET_STREAM_TYPE: + return "SET_STREAM_TYPE"; + case CX2341X_ENC_SET_OUTPUT_PORT: + return "SET_OUTPUT_PORT"; + case CX2341X_ENC_SET_AUDIO_PROPERTIES: + return "SET_AUDIO_PROPERTIES"; + case CX2341X_ENC_HALT_FW: + return "HALT_FW"; + case CX2341X_ENC_GET_VERSION: + return "GET_VERSION"; + case CX2341X_ENC_SET_GOP_CLOSURE: + return "SET_GOP_CLOSURE"; + case CX2341X_ENC_GET_SEQ_END: + return "GET_SEQ_END"; + case CX2341X_ENC_SET_PGM_INDEX_INFO: + return "SET_PGM_INDEX_INFO"; + case CX2341X_ENC_SET_VBI_CONFIG: + return "SET_VBI_CONFIG"; + case CX2341X_ENC_SET_DMA_BLOCK_SIZE: + return "SET_DMA_BLOCK_SIZE"; + case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10: + return "GET_PREV_DMA_INFO_MB_10"; + case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9: + return "GET_PREV_DMA_INFO_MB_9"; + case CX2341X_ENC_SCHED_DMA_TO_HOST: + return "SCHED_DMA_TO_HOST"; + case CX2341X_ENC_INITIALIZE_INPUT: + return "INITIALIZE_INPUT"; + case CX2341X_ENC_SET_FRAME_DROP_RATE: + return "SET_FRAME_DROP_RATE"; + case CX2341X_ENC_PAUSE_ENCODER: + return "PAUSE_ENCODER"; + case CX2341X_ENC_REFRESH_INPUT: + return "REFRESH_INPUT"; + case CX2341X_ENC_SET_COPYRIGHT: + return "SET_COPYRIGHT"; + case CX2341X_ENC_SET_EVENT_NOTIFICATION: + return "SET_EVENT_NOTIFICATION"; + case CX2341X_ENC_SET_NUM_VSYNC_LINES: + return "SET_NUM_VSYNC_LINES"; + case CX2341X_ENC_SET_PLACEHOLDER: + return "SET_PLACEHOLDER"; + case CX2341X_ENC_MUTE_VIDEO: + return "MUTE_VIDEO"; + case CX2341X_ENC_MUTE_AUDIO: + return "MUTE_AUDIO"; + case CX2341X_ENC_MISC: + return "MISC"; + default: + return "UNKNOWN"; + } +} + +static int cx231xx_mbox_func(void *priv, + u32 command, + int in, + int out, + u32 data[CX2341X_MBOX_MAX_DATA]) +{ + struct cx231xx *dev = priv; + unsigned long timeout; + u32 value, flag, retval = 0; + int i; + + dprintk(3, "%s: command(0x%X) = %s\n", __func__, command, + cmd_to_str(command)); + + /* this may not be 100% safe if we can't read any memory location + without side effects */ + mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value); + if (value != 0x12345678) { + dprintk(3, + "Firmware and/or mailbox pointer not initialized " + "or corrupted, signature = 0x%x, cmd = %s\n", value, + cmd_to_str(command)); + return -1; + } + + /* This read looks at 32 bits, but flag is only 8 bits. + * Seems we also bail if CMD or TIMEOUT bytes are set??? + */ + mc417_memory_read(dev, dev->cx23417_mailbox, &flag); + if (flag) { + dprintk(3, "ERROR: Mailbox appears to be in use " + "(%x), cmd = %s\n", flag, cmd_to_str(command)); + return -1; + } + + flag |= 1; /* tell 'em we're working on it */ + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + /* write command + args + fill remaining with zeros */ + /* command code */ + mc417_memory_write(dev, dev->cx23417_mailbox + 1, command); + mc417_memory_write(dev, dev->cx23417_mailbox + 3, + IVTV_API_STD_TIMEOUT); /* timeout */ + for (i = 0; i < in; i++) { + mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]); + dprintk(3, "API Input %d = %d\n", i, data[i]); + } + for (; i < CX2341X_MBOX_MAX_DATA; i++) + mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0); + + flag |= 3; /* tell 'em we're done writing */ + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + /* wait for firmware to handle the API command */ + timeout = jiffies + msecs_to_jiffies(10); + for (;;) { + mc417_memory_read(dev, dev->cx23417_mailbox, &flag); + if (0 != (flag & 4)) + break; + if (time_after(jiffies, timeout)) { + dprintk(3, "ERROR: API Mailbox timeout\n"); + return -1; + } + udelay(10); + } + + /* read output values */ + for (i = 0; i < out; i++) { + mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i); + dprintk(3, "API Output %d = %d\n", i, data[i]); + } + + mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval); + dprintk(3, "API result = %d\n", retval); + + flag = 0; + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + return retval; +} + +/* We don't need to call the API often, so using just one + * mailbox will probably suffice + */ +static int cx231xx_api_cmd(struct cx231xx *dev, + u32 command, + u32 inputcnt, + u32 outputcnt, + ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i, err; + + dprintk(3, "%s() cmds = 0x%08x\n", __func__, command); + + va_start(vargs, outputcnt); + for (i = 0; i < inputcnt; i++) + data[i] = va_arg(vargs, int); + + err = cx231xx_mbox_func(dev, command, inputcnt, outputcnt, data); + for (i = 0; i < outputcnt; i++) { + int *vptr = va_arg(vargs, int *); + *vptr = data[i]; + } + va_end(vargs); + + return err; +} + +static int cx231xx_find_mailbox(struct cx231xx *dev) +{ + u32 signature[4] = { + 0x12345678, 0x34567812, 0x56781234, 0x78123456 + }; + int signaturecnt = 0; + u32 value; + int i; + int ret = 0; + + dprintk(2, "%s()\n", __func__); + + for (i = 0; i < 0x100; i++) {/*CX231xx_FIRM_IMAGE_SIZE*/ + ret = mc417_memory_read(dev, i, &value); + if (ret < 0) + return ret; + if (value == signature[signaturecnt]) + signaturecnt++; + else + signaturecnt = 0; + if (4 == signaturecnt) { + dprintk(1, "Mailbox signature found at 0x%x\n", i+1); + return i+1; + } + } + dprintk(3, "Mailbox signature values not found!\n"); + return -1; +} + +static void mciWriteMemoryToGPIO(struct cx231xx *dev, u32 address, u32 value, + u32 *p_fw_image) +{ + + u32 temp = 0; + int i = 0; + + temp = 0x82|MCI_MEMORY_DATA_BYTE0|((value&0x000000FF)<<8); + temp = temp<<10; + *p_fw_image = temp; + p_fw_image++; + temp = temp|((0x05)<<10); + *p_fw_image = temp; + p_fw_image++; + + /*write data byte 1;*/ + temp = 0x82|MCI_MEMORY_DATA_BYTE1|(value&0x0000FF00); + temp = temp<<10; + *p_fw_image = temp; + p_fw_image++; + temp = temp|((0x05)<<10); + *p_fw_image = temp; + p_fw_image++; + + /*write data byte 2;*/ + temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8); + temp = temp<<10; + *p_fw_image = temp; + p_fw_image++; + temp = temp|((0x05)<<10); + *p_fw_image = temp; + p_fw_image++; + + /*write data byte 3;*/ + temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16); + temp = temp<<10; + *p_fw_image = temp; + p_fw_image++; + temp = temp|((0x05)<<10); + *p_fw_image = temp; + p_fw_image++; + + /* write address byte 2;*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | + ((address & 0x003F0000)>>8); + temp = temp<<10; + *p_fw_image = temp; + p_fw_image++; + temp = temp|((0x05)<<10); + *p_fw_image = temp; + p_fw_image++; + + /* write address byte 1;*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); + temp = temp<<10; + *p_fw_image = temp; + p_fw_image++; + temp = temp|((0x05)<<10); + *p_fw_image = temp; + p_fw_image++; + + /* write address byte 0;*/ + temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8); + temp = temp<<10; + *p_fw_image = temp; + p_fw_image++; + temp = temp|((0x05)<<10); + *p_fw_image = temp; + p_fw_image++; + + for (i = 0; i < 6; i++) { + *p_fw_image = 0xFFFFFFFF; + p_fw_image++; + } +} + + +static int cx231xx_load_firmware(struct cx231xx *dev) +{ + static const unsigned char magic[8] = { + 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa + }; + const struct firmware *firmware; + int i, retval = 0; + u32 value = 0; + u32 gpio_output = 0; + /*u32 checksum = 0;*/ + /*u32 *dataptr;*/ + u32 transfer_size = 0; + u32 fw_data = 0; + u32 address = 0; + /*u32 current_fw[800];*/ + u32 *p_current_fw, *p_fw; + u32 *p_fw_data; + int frame = 0; + u16 _buffer_size = 4096; + u8 *p_buffer; + + p_current_fw = vmalloc(1884180 * 4); + p_fw = p_current_fw; + if (p_current_fw == NULL) { + dprintk(2, "FAIL!!!\n"); + return -1; + } + + p_buffer = vmalloc(4096); + if (p_buffer == NULL) { + dprintk(2, "FAIL!!!\n"); + return -1; + } + + dprintk(2, "%s()\n", __func__); + + /* Save GPIO settings before reset of APU */ + retval |= mc417_memory_read(dev, 0x9020, &gpio_output); + retval |= mc417_memory_read(dev, 0x900C, &value); + + retval = mc417_register_write(dev, + IVTV_REG_VPU, 0xFFFFFFED); + retval |= mc417_register_write(dev, + IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); + retval |= mc417_register_write(dev, + IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800); + retval |= mc417_register_write(dev, + IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); + retval |= mc417_register_write(dev, + IVTV_REG_APU, 0); + + if (retval != 0) { + printk(KERN_ERR "%s: Error with mc417_register_write\n", + __func__); + return -1; + } + + retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME, + &dev->udev->dev); + + if (retval != 0) { + printk(KERN_ERR + "ERROR: Hotplug firmware request failed (%s).\n", + CX231xx_FIRM_IMAGE_NAME); + printk(KERN_ERR "Please fix your hotplug setup, the board will " + "not work without firmware loaded!\n"); + return -1; + } + + if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) { + printk(KERN_ERR "ERROR: Firmware size mismatch " + "(have %zd, expected %d)\n", + firmware->size, CX231xx_FIRM_IMAGE_SIZE); + release_firmware(firmware); + return -1; + } + + if (0 != memcmp(firmware->data, magic, 8)) { + printk(KERN_ERR + "ERROR: Firmware magic mismatch, wrong file?\n"); + release_firmware(firmware); + return -1; + } + + initGPIO(dev); + + /* transfer to the chip */ + dprintk(2, "Loading firmware to GPIO...\n"); + p_fw_data = (u32 *)firmware->data; + dprintk(2, "firmware->size=%zd\n", firmware->size); + for (transfer_size = 0; transfer_size < firmware->size; + transfer_size += 4) { + fw_data = *p_fw_data; + + mciWriteMemoryToGPIO(dev, address, fw_data, p_current_fw); + address = address + 1; + p_current_fw += 20; + p_fw_data += 1; + } + + /*download the firmware by ep5-out*/ + + for (frame = 0; frame < (int)(CX231xx_FIRM_IMAGE_SIZE*20/_buffer_size); + frame++) { + for (i = 0; i < _buffer_size; i++) { + *(p_buffer + i) = (u8)(*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x000000FF); + i++; + *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x0000FF00) >> 8); + i++; + *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x00FF0000) >> 16); + i++; + *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0xFF000000) >> 24); + } + cx231xx_ep5_bulkout(dev, p_buffer, _buffer_size); + } + + p_current_fw = p_fw; + vfree(p_current_fw); + p_current_fw = NULL; + uninitGPIO(dev); + release_firmware(firmware); + dprintk(1, "Firmware upload successful.\n"); + + retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, + IVTV_CMD_HW_BLOCKS_RST); + if (retval < 0) { + printk(KERN_ERR "%s: Error with mc417_register_write\n", + __func__); + return retval; + } + /* F/W power up disturbs the GPIOs, restore state */ + retval |= mc417_register_write(dev, 0x9020, gpio_output); + retval |= mc417_register_write(dev, 0x900C, value); + + retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); + retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8); + + if (retval < 0) { + printk(KERN_ERR "%s: Error with mc417_register_write\n", + __func__); + return retval; + } + return 0; +} + +static void cx231xx_417_check_encoder(struct cx231xx *dev) +{ + u32 status, seq; + + status = 0; + seq = 0; + cx231xx_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq); + dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq); +} + +static void cx231xx_codec_settings(struct cx231xx *dev) +{ + dprintk(1, "%s()\n", __func__); + + /* assign frame size */ + cx231xx_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, + dev->ts1.height, dev->ts1.width); + + dev->mpeg_params.width = dev->ts1.width; + dev->mpeg_params.height = dev->ts1.height; + + cx2341x_update(dev, cx231xx_mbox_func, NULL, &dev->mpeg_params); + + cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); + cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); +} + +static int cx231xx_initialize_codec(struct cx231xx *dev) +{ + int version; + int retval; + u32 i; + u32 val = 0; + + dprintk(1, "%s()\n", __func__); + cx231xx_disable656(dev); + retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ + if (retval < 0) { + dprintk(2, "%s() PING OK\n", __func__); + retval = cx231xx_load_firmware(dev); + if (retval < 0) { + printk(KERN_ERR "%s() f/w load failed\n", __func__); + return retval; + } + retval = cx231xx_find_mailbox(dev); + if (retval < 0) { + printk(KERN_ERR "%s() mailbox < 0, error\n", + __func__); + return -1; + } + dev->cx23417_mailbox = retval; + retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); + if (retval < 0) { + printk(KERN_ERR + "ERROR: cx23417 firmware ping failed!\n"); + return -1; + } + retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, + &version); + if (retval < 0) { + printk(KERN_ERR "ERROR: cx23417 firmware get encoder :" + "version failed!\n"); + return -1; + } + dprintk(1, "cx23417 firmware version is 0x%08x\n", version); + msleep(200); + } + + for (i = 0; i < 1; i++) { + retval = mc417_register_read(dev, 0x20f8, &val); + dprintk(3, "***before enable656() VIM Capture Lines =%d ***\n", + val); + if (retval < 0) + return retval; + } + + cx231xx_enable656(dev); + /* stop mpeg capture */ + cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, + 3, 0, 1, 3, 4); + + cx231xx_codec_settings(dev); + msleep(60); + +/* cx231xx_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, + CX231xx_FIELD1_SAA7115, CX231xx_FIELD2_SAA7115); + cx231xx_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, + CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0); +*/ + +#if 0 + /* TODO */ + u32 data[7]; + + /* Setup to capture VBI */ + data[0] = 0x0001BD00; + data[1] = 1; /* frames per interrupt */ + data[2] = 4; /* total bufs */ + data[3] = 0x91559155; /* start codes */ + data[4] = 0x206080C0; /* stop codes */ + data[5] = 6; /* lines */ + data[6] = 64; /* BPL */ + + cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], + data[2], data[3], data[4], data[5], data[6]); + + for (i = 2; i <= 24; i++) { + int valid; + + valid = ((i >= 19) && (i <= 21)); + cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i, + valid, 0 , 0, 0); + cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, + i | 0x80000000, valid, 0, 0, 0); + } +#endif +/* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE); + msleep(60); +*/ + /* initialize the video input */ + retval = cx231xx_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); + if (retval < 0) + return retval; + msleep(60); + + /* Enable VIP style pixel invalidation so we work with scaled mode */ + mc417_memory_write(dev, 2120, 0x00000080); + + /* start capturing to the host interface */ + retval = cx231xx_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, + CX231xx_MPEG_CAPTURE, CX231xx_RAW_BITS_NONE); + if (retval < 0) + return retval; + msleep(10); + + for (i = 0; i < 1; i++) { + mc417_register_read(dev, 0x20f8, &val); + dprintk(3, "***VIM Capture Lines =%d ***\n", val); + } + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int bb_buf_setup(struct videobuf_queue *q, + unsigned int *count, unsigned int *size) +{ + struct cx231xx_fh *fh = q->priv_data; + + fh->dev->ts1.ts_packet_size = mpeglinesize; + fh->dev->ts1.ts_packet_count = mpeglines; + + *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; + *count = mpegbufs; + + return 0; +} +static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + unsigned long flags = 0; + + if (in_interrupt()) + BUG(); + + spin_lock_irqsave(&dev->video_mode.slock, flags); + if (dev->USE_ISO) { + if (dev->video_mode.isoc_ctl.buf == buf) + dev->video_mode.isoc_ctl.buf = NULL; + } else { + if (dev->video_mode.bulk_ctl.buf == buf) + dev->video_mode.bulk_ctl.buf = NULL; + } + spin_unlock_irqrestore(&dev->video_mode.slock, flags); + videobuf_waiton(vq, &buf->vb, 0, 0); + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb, + struct cx231xx_dmaqueue *dma_q) +{ + void *vbuf; + struct cx231xx_buffer *buf; + u32 tail_data = 0; + char *p_data; + + if (dma_q->mpeg_buffer_done == 0) { + if (list_empty(&dma_q->active)) + return; + + buf = list_entry(dma_q->active.next, + struct cx231xx_buffer, vb.queue); + dev->video_mode.isoc_ctl.buf = buf; + dma_q->mpeg_buffer_done = 1; + } + /* Fill buffer */ + buf = dev->video_mode.isoc_ctl.buf; + vbuf = videobuf_to_vmalloc(&buf->vb); + + if ((dma_q->mpeg_buffer_completed+len) < + mpeglines*mpeglinesize) { + if (dma_q->add_ps_package_head == + CX231XX_NEED_ADD_PS_PACKAGE_HEAD) { + memcpy(vbuf+dma_q->mpeg_buffer_completed, + dma_q->ps_head, 3); + dma_q->mpeg_buffer_completed = + dma_q->mpeg_buffer_completed + 3; + dma_q->add_ps_package_head = + CX231XX_NONEED_PS_PACKAGE_HEAD; + } + memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len); + dma_q->mpeg_buffer_completed = + dma_q->mpeg_buffer_completed + len; + } else { + dma_q->mpeg_buffer_done = 0; + + tail_data = + mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed; + memcpy(vbuf+dma_q->mpeg_buffer_completed, + data, tail_data); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); + dma_q->mpeg_buffer_completed = 0; + + if (len - tail_data > 0) { + p_data = data + tail_data; + dma_q->left_data_count = len - tail_data; + memcpy(dma_q->p_left_data, + p_data, len - tail_data); + } + + } + + return; +} + +static void buffer_filled(char *data, int len, struct urb *urb, + struct cx231xx_dmaqueue *dma_q) +{ + void *vbuf; + struct cx231xx_buffer *buf; + + if (list_empty(&dma_q->active)) + return; + + + buf = list_entry(dma_q->active.next, + struct cx231xx_buffer, vb.queue); + + + /* Fill buffer */ + vbuf = videobuf_to_vmalloc(&buf->vb); + memcpy(vbuf, data, len); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); + + return; +} +static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + unsigned char *p_buffer; + u32 buffer_size = 0; + u32 i = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + if (dma_q->left_data_count > 0) { + buffer_copy(dev, dma_q->p_left_data, + dma_q->left_data_count, urb, dma_q); + dma_q->mpeg_buffer_completed = dma_q->left_data_count; + dma_q->left_data_count = 0; + } + + p_buffer = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + buffer_size = urb->iso_frame_desc[i].actual_length; + + if (buffer_size > 0) + buffer_copy(dev, p_buffer, buffer_size, urb, dma_q); + } + + return 0; +} +static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) +{ + + /*char *outp;*/ + /*struct cx231xx_buffer *buf;*/ + struct cx231xx_dmaqueue *dma_q = urb->context; + unsigned char *p_buffer, *buffer; + u32 buffer_size = 0; + + p_buffer = urb->transfer_buffer; + buffer_size = urb->actual_length; + + buffer = kmalloc(buffer_size, GFP_ATOMIC); + + memcpy(buffer, dma_q->ps_head, 3); + memcpy(buffer+3, p_buffer, buffer_size-3); + memcpy(dma_q->ps_head, p_buffer+buffer_size-3, 3); + + p_buffer = buffer; + buffer_filled(p_buffer, buffer_size, urb, dma_q); + + kfree(buffer); + return 0; +} + +static int bb_buf_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct cx231xx_fh *fh = q->priv_data; + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + int rc = 0, urb_init = 0; + int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; + + dma_qq = &dev->video_mode.vidq; + + if (0 != buf->vb.baddr && buf->vb.bsize < size) + return -EINVAL; + buf->vb.width = fh->dev->ts1.ts_packet_size; + buf->vb.height = fh->dev->ts1.ts_packet_count; + buf->vb.size = size; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (dev->USE_ISO) { + if (!dev->video_mode.isoc_ctl.num_bufs) + urb_init = 1; + } else { + if (!dev->video_mode.bulk_ctl.num_bufs) + urb_init = 1; + } + /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n", + urb_init, dev->video_mode.max_pkt_size);*/ + dev->mode_tv = 1; + + if (urb_init) { + rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + rc = cx231xx_unmute_audio(dev); + if (dev->USE_ISO) { + cx231xx_set_alt_setting(dev, INDEX_TS1, 4); + rc = cx231xx_init_isoc(dev, mpeglines, + mpegbufs, + dev->ts1_mode.max_pkt_size, + cx231xx_isoc_copy); + } else { + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + rc = cx231xx_init_bulk(dev, mpeglines, + mpegbufs, + dev->ts1_mode.max_pkt_size, + cx231xx_bulk_copy); + } + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void bb_buf_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct cx231xx_fh *fh = q->priv_data; + + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void bb_buf_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + /*struct cx231xx_fh *fh = q->priv_data;*/ + /*struct cx231xx *dev = (struct cx231xx *)fh->dev;*/ + + free_buffer(q, buf); +} + +static struct videobuf_queue_ops cx231xx_qops = { + .buf_setup = bb_buf_setup, + .buf_prepare = bb_buf_prepare, + .buf_queue = bb_buf_queue, + .buf_release = bb_buf_release, +}; + +/* ------------------------------------------------------------------ */ + +static const u32 *ctrl_classes[] = { + cx2341x_mpeg_ctrls, + NULL +}; + +static int cx231xx_queryctrl(struct cx231xx *dev, + struct v4l2_queryctrl *qctrl) +{ + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (qctrl->id == 0) + return -EINVAL; + + /* MPEG V4L2 controls */ + if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + + return 0; +} + +static int cx231xx_querymenu(struct cx231xx *dev, + struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + + qctrl.id = qmenu->id; + cx231xx_queryctrl(dev, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, + cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id)); +} + +static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + + *norm = dev->encodernorm.id; + return 0; +} +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++) + if (*id & cx231xx_tvnorms[i].id) + break; + if (i == ARRAY_SIZE(cx231xx_tvnorms)) + return -EINVAL; + dev->encodernorm = cx231xx_tvnorms[i]; + + if (dev->encodernorm.id & 0xb000) { + dprintk(3, "encodernorm set to NTSC\n"); + dev->norm = V4L2_STD_NTSC; + dev->ts1.height = 480; + dev->mpeg_params.is_50hz = 0; + } else { + dprintk(3, "encodernorm set to PAL\n"); + dev->norm = V4L2_STD_PAL_B; + dev->ts1.height = 576; + dev->mpeg_params.is_50hz = 1; + } + call_all(dev, core, s_std, dev->norm); + /* do mode control overrides */ + cx231xx_do_mode_ctrl_overrides(dev); + + dprintk(3, "exit vidioc_s_std() i=0x%x\n", i); + return 0; +} +static int vidioc_g_audio(struct file *file, void *fh, + struct v4l2_audio *a) +{ + struct v4l2_audio *vin = a; + + int ret = -EINVAL; + if (vin->index > 0) + return ret; + strncpy(vin->name, "VideoGrabber Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; +return 0; +} +static int vidioc_enumaudio(struct file *file, void *fh, + struct v4l2_audio *a) +{ + struct v4l2_audio *vin = a; + + int ret = -EINVAL; + + if (vin->index > 0) + return ret; + strncpy(vin->name, "VideoGrabber Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + + +return 0; +} +static const char *iname[] = { + [CX231XX_VMUX_COMPOSITE1] = "Composite1", + [CX231XX_VMUX_SVIDEO] = "S-Video", + [CX231XX_VMUX_TELEVISION] = "Television", + [CX231XX_VMUX_CABLE] = "Cable TV", + [CX231XX_VMUX_DVB] = "DVB", + [CX231XX_VMUX_DEBUG] = "for debug only", +}; +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + struct cx231xx_input *input; + int n; + dprintk(3, "enter vidioc_enum_input()i->index=%d\n", i->index); + + if (i->index >= 4) + return -EINVAL; + + + input = &cx231xx_boards[dev->model].input[i->index]; + + if (input->type == 0) + return -EINVAL; + + /* FIXME + * strcpy(i->name, input->name); */ + + n = i->index; + strcpy(i->name, iname[INPUT(n)->type]); + + if (input->type == CX231XX_VMUX_TELEVISION || + input->type == CX231XX_VMUX_CABLE) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; + + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + + dprintk(3, "enter vidioc_s_input() i=%d\n", i); + + mutex_lock(&dev->lock); + + video_mux(dev, i); + + mutex_unlock(&dev->lock); + + if (i >= 4) + return -EINVAL; + dev->input = i; + dprintk(3, "exit vidioc_s_input()\n"); + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + + + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_s_ctrl()\n"); + /* Update the A/V core */ + call_all(dev, core, s_ctrl, ctl); + dprintk(3, "exit vidioc_s_ctrl()\n"); + return 0; +} +static struct v4l2_capability pvr_capability = { + .driver = "cx231xx", + .card = "VideoGrabber", + .bus_info = "usb", + .version = 1, + .capabilities = (V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE), +}; +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + + + + memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + + if (f->index != 0) + return -EINVAL; + + strlcpy(f->description, "MPEG", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_MPEG; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_g_fmt_vid_cap()\n"); + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + f->fmt.pix.width = dev->ts1.width; + f->fmt.pix.height = dev->ts1.height; + f->fmt.pix.field = fh->vidq.field; + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->vidq.field); + dprintk(3, "exit vidioc_g_fmt_vid_cap()\n"); + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_try_fmt_vid_cap()\n"); + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->vidq.field); + dprintk(3, "exit vidioc_try_fmt_vid_cap()\n"); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct cx231xx_fh *fh = file->private_data; + + return videobuf_reqbufs(&fh->vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct cx231xx_fh *fh = file->private_data; + + return videobuf_querybuf(&fh->vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct cx231xx_fh *fh = file->private_data; + + return videobuf_qbuf(&fh->vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + + return videobuf_dqbuf(&fh->vidq, b, file->f_flags & O_NONBLOCK); +} + + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct cx231xx_fh *fh = file->private_data; + + struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_streamon()\n"); + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + if (dev->USE_ISO) + cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_isoc_copy); + else { + cx231xx_init_bulk(dev, 320, + 5, + dev->ts1_mode.max_pkt_size, + cx231xx_bulk_copy); + } + dprintk(3, "exit vidioc_streamon()\n"); + return videobuf_streamon(&fh->vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx231xx_fh *fh = file->private_data; + + return videobuf_streamoff(&fh->vidq); +} + +static int vidioc_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_g_ext_ctrls()\n"); + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + dprintk(3, "exit vidioc_g_ext_ctrls()\n"); + return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS); +} + +static int vidioc_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + struct cx2341x_mpeg_params p; + int err; + dprintk(3, "enter vidioc_s_ext_ctrls()\n"); + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + p = dev->mpeg_params; + err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); + if (err == 0) { + err = cx2341x_update(dev, cx231xx_mbox_func, + &dev->mpeg_params, &p); + dev->mpeg_params = p; + } + + return err; + + +return 0; +} + +static int vidioc_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + struct cx2341x_mpeg_params p; + int err; + dprintk(3, "enter vidioc_try_ext_ctrls()\n"); + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + p = dev->mpeg_params; + err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); + dprintk(3, "exit vidioc_try_ext_ctrls() err=%d\n", err); + return err; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + dprintk(3, + "%s/2: ============ START LOG STATUS ============\n", + dev->name); + call_all(dev, core, log_status); + cx2341x_log_status(&dev->mpeg_params, name); + dprintk(3, + "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *a) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_querymenu()\n"); + dprintk(3, "exit vidioc_querymenu()\n"); + return cx231xx_querymenu(dev, a); +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + dprintk(3, "enter vidioc_queryctrl()\n"); + dprintk(3, "exit vidioc_queryctrl()\n"); + return cx231xx_queryctrl(dev, c); +} + +static int mpeg_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx231xx *h, *dev = NULL; + /*struct list_head *list;*/ + struct cx231xx_fh *fh; + /*u32 value = 0;*/ + + dprintk(2, "%s()\n", __func__); + + list_for_each_entry(h, &cx231xx_devlist, devlist) { + if (h->v4l_device->minor == minor) + dev = h; + } + + if (dev == NULL) + return -ENODEV; + + mutex_lock(&dev->lock); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + mutex_unlock(&dev->lock); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + + + videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops, + NULL, &dev->video_mode.slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, + sizeof(struct cx231xx_buffer), fh, NULL); +/* + videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops, + &dev->udev->dev, &dev->ts1.slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx231xx_buffer), + fh, NULL); +*/ + + + cx231xx_set_alt_setting(dev, INDEX_VANC, 1); + cx231xx_set_gpio_value(dev, 2, 0); + + cx231xx_initialize_codec(dev); + + mutex_unlock(&dev->lock); + cx231xx_start_TS1(dev); + + return 0; +} + +static int mpeg_release(struct file *file) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + + dprintk(3, "mpeg_release()! dev=0x%p\n", dev); + + if (!dev) { + dprintk(3, "abort!!!\n"); + return 0; + } + + mutex_lock(&dev->lock); + + cx231xx_stop_TS1(dev); + + /* do this before setting alternate! */ + if (dev->USE_ISO) + cx231xx_uninit_isoc(dev); + else + cx231xx_uninit_bulk(dev); + cx231xx_set_mode(dev, CX231XX_SUSPEND); + + cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX231xx_END_NOW, CX231xx_MPEG_CAPTURE, + CX231xx_RAW_BITS_NONE); + + /* FIXME: Review this crap */ + /* Shut device down on last close */ + if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { + if (atomic_dec_return(&dev->v4l_reader_count) == 0) { + /* stop mpeg capture */ + + msleep(500); + cx231xx_417_check_encoder(dev); + + } + } + + if (fh->vidq.streaming) + videobuf_streamoff(&fh->vidq); + if (fh->vidq.reading) + videobuf_read_stop(&fh->vidq); + + videobuf_mmap_free(&fh->vidq); + file->private_data = NULL; + kfree(fh); + mutex_unlock(&dev->lock); + return 0; +} + +static ssize_t mpeg_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + + + /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ + /* Start mpeg encoder on first read. */ + if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { + if (atomic_inc_return(&dev->v4l_reader_count) == 1) { + if (cx231xx_initialize_codec(dev) < 0) + return -EINVAL; + } + } + + return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static unsigned int mpeg_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx231xx_fh *fh = file->private_data; + /*struct cx231xx *dev = fh->dev;*/ + + /*dprintk(2, "%s\n", __func__);*/ + + return videobuf_poll_stream(file, &fh->vidq, wait); +} + +static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cx231xx_fh *fh = file->private_data; + struct cx231xx *dev = fh->dev; + + dprintk(2, "%s()\n", __func__); + + return videobuf_mmap_mapper(&fh->vidq, vma); +} + +static struct v4l2_file_operations mpeg_fops = { + .owner = THIS_MODULE, + .open = mpeg_open, + .release = mpeg_release, + .read = mpeg_read, + .poll = mpeg_poll, + .mmap = mpeg_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, + .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_log_status = vidioc_log_status, + .vidioc_querymenu = vidioc_querymenu, + .vidioc_queryctrl = vidioc_queryctrl, +/* .vidioc_g_chip_ident = cx231xx_g_chip_ident,*/ +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* .vidioc_g_register = cx231xx_g_register,*/ +/* .vidioc_s_register = cx231xx_s_register,*/ +#endif +}; + +static struct video_device cx231xx_mpeg_template = { + .name = "cx231xx", + .fops = &mpeg_fops, + .ioctl_ops = &mpeg_ioctl_ops, + .minor = -1, + .tvnorms = CX231xx_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; + +void cx231xx_417_unregister(struct cx231xx *dev) +{ + dprintk(1, "%s()\n", __func__); + dprintk(3, "%s()\n", __func__); + + if (dev->v4l_device) { + if (-1 != dev->v4l_device->minor) + video_unregister_device(dev->v4l_device); + else + video_device_release(dev->v4l_device); + dev->v4l_device = NULL; + } +} + +static struct video_device *cx231xx_video_dev_alloc( + struct cx231xx *dev, + struct usb_device *usbdev, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + + dprintk(1, "%s()\n", __func__); + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; + snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, + type, cx231xx_boards[dev->model].name); + + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + + return vfd; + +} + +int cx231xx_417_register(struct cx231xx *dev) +{ + /* FIXME: Port1 hardcoded here */ + int err = -ENODEV; + struct cx231xx_tsport *tsport = &dev->ts1; + + dprintk(1, "%s()\n", __func__); + + /* Set default TV standard */ + dev->encodernorm = cx231xx_tvnorms[0]; + + if (dev->encodernorm.id & V4L2_STD_525_60) + tsport->height = 480; + else + tsport->height = 576; + + tsport->width = 720; + cx2341x_fill_defaults(&dev->mpeg_params); + dev->norm = V4L2_STD_NTSC; + + dev->mpeg_params.port = CX2341X_PORT_SERIAL; + + /* Allocate and initialize V4L video device */ + dev->v4l_device = cx231xx_video_dev_alloc(dev, + dev->udev, &cx231xx_mpeg_template, "mpeg"); + err = video_register_device(dev->v4l_device, + VFL_TYPE_GRABBER, -1); + if (err < 0) { + dprintk(3, "%s: can't register mpeg device\n", dev->name); + return err; + } + + dprintk(3, "%s: registered device video%d [mpeg]\n", + dev->name, dev->v4l_device->num); + + return 0; +} + +MODULE_FIRMWARE(CX231xx_FIRM_IMAGE_NAME); diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c new file mode 100644 index 000000000000..b4c99c7270cf --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -0,0 +1,780 @@ +/* + * Conexant Cx231xx audio extension + * + * Copyright (C) 2008 + * Based on em28xx driver + * + * + * This program is free software; 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "cx231xx.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_INFO "cx231xx-audio %s: " fmt, \ + __func__, ##arg); \ + } while (0) + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + +static int cx231xx_isoc_audio_deinit(struct cx231xx *dev) +{ + int i; + + dprintk("Stopping isoc\n"); + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + if (dev->adev.urb[i]) { + if (!irqs_disabled()) + usb_kill_urb(dev->adev.urb[i]); + else + usb_unlink_urb(dev->adev.urb[i]); + + usb_free_urb(dev->adev.urb[i]); + dev->adev.urb[i] = NULL; + + kfree(dev->adev.transfer_buffer[i]); + dev->adev.transfer_buffer[i] = NULL; + } + } + + return 0; +} + +static int cx231xx_bulk_audio_deinit(struct cx231xx *dev) +{ + int i; + + dprintk("Stopping bulk\n"); + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + if (dev->adev.urb[i]) { + if (!irqs_disabled()) + usb_kill_urb(dev->adev.urb[i]); + else + usb_unlink_urb(dev->adev.urb[i]); + + usb_free_urb(dev->adev.urb[i]); + dev->adev.urb[i] = NULL; + + kfree(dev->adev.transfer_buffer[i]); + dev->adev.transfer_buffer[i] = NULL; + } + } + + return 0; +} + +static void cx231xx_audio_isocirq(struct urb *urb) +{ + struct cx231xx *dev = urb->context; + int i; + unsigned int oldptr; + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + + if (dev->state & DEV_DISCONNECTED) + return; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dprintk("urb completition error %d.\n", urb->status); + break; + } + + if (atomic_read(&dev->stream_started) == 0) + return; + + if (dev->adev.capture_pcm_substream) { + substream = dev->adev.capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + int length = urb->iso_frame_desc[i].actual_length / + stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!length) + continue; + + oldptr = dev->adev.hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt; + + cnt = runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + + snd_pcm_stream_lock(substream); + + dev->adev.hwptr_done_capture += length; + if (dev->adev.hwptr_done_capture >= + runtime->buffer_size) + dev->adev.hwptr_done_capture -= + runtime->buffer_size; + + dev->adev.capture_transfer_done += length; + if (dev->adev.capture_transfer_done >= + runtime->period_size) { + dev->adev.capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + snd_pcm_stream_unlock(substream); + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + cx231xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static void cx231xx_audio_bulkirq(struct urb *urb) +{ + struct cx231xx *dev = urb->context; + unsigned int oldptr; + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + + if (dev->state & DEV_DISCONNECTED) + return; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dprintk("urb completition error %d.\n", urb->status); + break; + } + + if (atomic_read(&dev->stream_started) == 0) + return; + + if (dev->adev.capture_pcm_substream) { + substream = dev->adev.capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + if (1) { + int length = urb->actual_length / + stride; + cp = (unsigned char *)urb->transfer_buffer; + + oldptr = dev->adev.hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt; + + cnt = runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + + snd_pcm_stream_lock(substream); + + dev->adev.hwptr_done_capture += length; + if (dev->adev.hwptr_done_capture >= + runtime->buffer_size) + dev->adev.hwptr_done_capture -= + runtime->buffer_size; + + dev->adev.capture_transfer_done += length; + if (dev->adev.capture_transfer_done >= + runtime->period_size) { + dev->adev.capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + snd_pcm_stream_unlock(substream); + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + cx231xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static int cx231xx_init_audio_isoc(struct cx231xx *dev) +{ + int i, errCode; + int sb_size; + + cx231xx_info("%s: Starting ISO AUDIO transfers\n", __func__); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + + dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev.transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev.transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) { + cx231xx_errdev("usb_alloc_urb failed!\n"); + for (j = 0; j < i; j++) { + usb_free_urb(dev->adev.urb[j]); + kfree(dev->adev.transfer_buffer[j]); + } + return -ENOMEM; + } + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, + dev->adev.end_point_addr); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev.transfer_buffer[i]; + urb->interval = 1; + urb->complete = cx231xx_audio_isocirq; + urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + + for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS; + j++, k += dev->adev.max_pkt_size) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = dev->adev.max_pkt_size; + } + dev->adev.urb[i] = urb; + } + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); + if (errCode < 0) { + cx231xx_isoc_audio_deinit(dev); + return errCode; + } + } + + return errCode; +} + +static int cx231xx_init_audio_bulk(struct cx231xx *dev) +{ + int i, errCode; + int sb_size; + + cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j; + + dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev.transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev.transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) { + cx231xx_errdev("usb_alloc_urb failed!\n"); + for (j = 0; j < i; j++) { + usb_free_urb(dev->adev.urb[j]); + kfree(dev->adev.transfer_buffer[j]); + } + return -ENOMEM; + } + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvbulkpipe(dev->udev, + dev->adev.end_point_addr); + urb->transfer_flags = 0; + urb->transfer_buffer = dev->adev.transfer_buffer[i]; + urb->complete = cx231xx_audio_bulkirq; + urb->transfer_buffer_length = sb_size; + + dev->adev.urb[i] = urb; + + } + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); + if (errCode < 0) { + cx231xx_bulk_audio_deinit(dev); + return errCode; + } + } + + return errCode; +} + +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) +{ + struct snd_pcm_runtime *runtime = subs->runtime; + + dprintk("Allocating vbuffer\n"); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + + runtime->dma_bytes = size; + + return 0; +} + +static struct snd_pcm_hardware snd_cx231xx_hw_capture = { + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, /* 12544/2, */ + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, /* 12544, */ +}; + +static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream) +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + dprintk("opening device and trying to acquire exclusive lock\n"); + + if (!dev) { + cx231xx_errdev("BUG: cx231xx can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + + if (dev->state & DEV_DISCONNECTED) { + cx231xx_errdev("Can't open. the device was removed.\n"); + return -ENODEV; + } + + /* Sets volume, mute, etc */ + dev->mute = 0; + + /* set alternate setting for audio interface */ + /* 1 - 48000 samples per sec */ + mutex_lock(&dev->lock); + if (dev->USE_ISO) + ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1); + else + ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); + mutex_unlock(&dev->lock); + if (ret < 0) { + cx231xx_errdev("failed to set alternate setting !\n"); + + return ret; + } + + runtime->hw = snd_cx231xx_hw_capture; + + mutex_lock(&dev->lock); + /* inform hardware to start streaming */ + ret = cx231xx_capture_start(dev, 1, Audio); + + dev->adev.users++; + mutex_unlock(&dev->lock); + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + dev->adev.capture_pcm_substream = substream; + runtime->private_data = dev; + + return 0; +} + +static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) +{ + int ret; + struct cx231xx *dev = snd_pcm_substream_chip(substream); + + dprintk("closing device\n"); + + /* inform hardware to stop streaming */ + mutex_lock(&dev->lock); + ret = cx231xx_capture_start(dev, 0, Audio); + + /* set alternate setting for audio interface */ + /* 1 - 48000 samples per sec */ + ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); + if (ret < 0) { + cx231xx_errdev("failed to set alternate setting !\n"); + + mutex_unlock(&dev->lock); + return ret; + } + + dev->mute = 1; + dev->adev.users--; + mutex_unlock(&dev->lock); + + if (dev->adev.users == 0 && dev->adev.shutdown == 1) { + dprintk("audio users: %d\n", dev->adev.users); + dprintk("disabling audio stream!\n"); + dev->adev.shutdown = 0; + dprintk("released lock\n"); + if (atomic_read(&dev->stream_started) > 0) { + atomic_set(&dev->stream_started, 0); + schedule_work(&dev->wq_trigger); + } + } + return 0; +} + +static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret; + + dprintk("Setting capture parameters\n"); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +#if 0 + /* TODO: set up cx231xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + unsigned int channels, rate, format; + + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); +#endif + + return ret; +} + +static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream) +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Stop capture, if needed\n"); + + if (atomic_read(&dev->stream_started) > 0) { + atomic_set(&dev->stream_started, 0); + schedule_work(&dev->wq_trigger); + } + + return 0; +} + +static int snd_cx231xx_prepare(struct snd_pcm_substream *substream) +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + + dev->adev.hwptr_done_capture = 0; + dev->adev.capture_transfer_done = 0; + + return 0; +} + +static void audio_trigger(struct work_struct *work) +{ + struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger); + + if (atomic_read(&dev->stream_started)) { + dprintk("starting capture"); + if (is_fw_load(dev) == 0) + cx25840_call(dev, core, load_fw); + if (dev->USE_ISO) + cx231xx_init_audio_isoc(dev); + else + cx231xx_init_audio_bulk(dev); + } else { + dprintk("stopping capture"); + cx231xx_isoc_audio_deinit(dev); + } +} + +static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + int retval = 0; + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + spin_lock(&dev->adev.slock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + atomic_set(&dev->stream_started, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + atomic_set(&dev->stream_started, 0); + break; + default: + retval = -EINVAL; + break; + } + spin_unlock(&dev->adev.slock); + + schedule_work(&dev->wq_trigger); + + return retval; +} + +static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream + *substream) +{ + struct cx231xx *dev; + unsigned long flags; + snd_pcm_uframes_t hwptr_done; + + dev = snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&dev->adev.slock, flags); + hwptr_done = dev->adev.hwptr_done_capture; + spin_unlock_irqrestore(&dev->adev.slock, flags); + + return hwptr_done; +} + +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +static struct snd_pcm_ops snd_cx231xx_pcm_capture = { + .open = snd_cx231xx_capture_open, + .close = snd_cx231xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cx231xx_hw_capture_params, + .hw_free = snd_cx231xx_hw_capture_free, + .prepare = snd_cx231xx_prepare, + .trigger = snd_cx231xx_capture_trigger, + .pointer = snd_cx231xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +static int cx231xx_audio_init(struct cx231xx *dev) +{ + struct cx231xx_audio *adev = &dev->adev; + struct snd_pcm *pcm; + struct snd_card *card; + static int devnr; + int err; + struct usb_interface *uif; + int i, isoc_pipe = 0; + + if (dev->has_alsa_audio != 1) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + cx231xx_info("cx231xx-audio.c: probing for cx231xx " + "non standard usbaudio\n"); + + err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE, + 0, &card); + if (err < 0) + return err; + + spin_lock_init(&adev->slock); + err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); + if (err < 0) { + snd_card_free(card); + return err; + } + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_cx231xx_pcm_capture); + pcm->info_flags = 0; + pcm->private_data = dev; + strcpy(pcm->name, "Conexant cx231xx Capture"); + snd_card_set_dev(card, &dev->udev->dev); + strcpy(card->driver, "Cx231xx-Audio"); + strcpy(card->shortname, "Cx231xx Audio"); + strcpy(card->longname, "Conexant cx231xx Audio"); + + INIT_WORK(&dev->wq_trigger, audio_trigger); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + adev->sndcard = card; + adev->udev = dev->udev; + + /* compute alternate max packet sizes for Audio */ + uif = + dev->udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + audio_index + 1]; + + adev->end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + adev->num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + adev->end_point_addr, adev->num_alt); + adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL); + + if (adev->alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < adev->num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. + wMaxPacketSize); + adev->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + adev->alt_max_pkt_size[i]); + } + + return 0; +} + +static int cx231xx_audio_fini(struct cx231xx *dev) +{ + if (dev == NULL) + return 0; + + if (dev->has_alsa_audio != 1) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + if (dev->adev.sndcard) { + snd_card_free(dev->adev.sndcard); + kfree(dev->adev.alt_max_pkt_size); + dev->adev.sndcard = NULL; + } + + return 0; +} + +static struct cx231xx_ops audio_ops = { + .id = CX231XX_AUDIO, + .name = "Cx231xx Audio Extension", + .init = cx231xx_audio_init, + .fini = cx231xx_audio_fini, +}; + +static int __init cx231xx_alsa_register(void) +{ + return cx231xx_register_extension(&audio_ops); +} + +static void __exit cx231xx_alsa_unregister(void) +{ + cx231xx_unregister_extension(&audio_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Srinivasa Deevi "); +MODULE_DESCRIPTION("Cx231xx Audio driver"); + +module_init(cx231xx_alsa_register); +module_exit(cx231xx_alsa_unregister); diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c new file mode 100644 index 000000000000..447148eff958 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -0,0 +1,3087 @@ +/* + cx231xx_avcore.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 + + This program contains the specific code to control the avdecoder chip and + other related usb control functions for cx231xx based chipset. + + This program is free software; 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 +#include + +#include +#include +#include + +#include "cx231xx.h" +#include "cx231xx-dif.h" + +#define TUNER_MODE_FM_RADIO 0 +/****************************************************************************** + -: BLOCK ARRANGEMENT :- + I2S block ----------------------| + [I2S audio] | + | + Analog Front End --> Direct IF -|-> Cx25840 --> Audio + [video & audio] | [Audio] + | + |-> Cx25840 --> Video + [Video] + +*******************************************************************************/ +/****************************************************************************** + * VERVE REGISTER * + * * + ******************************************************************************/ +static int verve_write_byte(struct cx231xx *dev, u8 saddr, u8 data) +{ + return cx231xx_write_i2c_data(dev, VERVE_I2C_ADDRESS, + saddr, 1, data, 1); +} + +static int verve_read_byte(struct cx231xx *dev, u8 saddr, u8 *data) +{ + int status; + u32 temp = 0; + + status = cx231xx_read_i2c_data(dev, VERVE_I2C_ADDRESS, + saddr, 1, &temp, 1); + *data = (u8) temp; + return status; +} +void initGPIO(struct cx231xx *dev) +{ + u32 _gpio_direction = 0; + u32 value = 0; + u8 val = 0; + + _gpio_direction = _gpio_direction & 0xFC0003FF; + _gpio_direction = _gpio_direction | 0x03FDFC00; + cx231xx_send_gpio_cmd(dev, _gpio_direction, (u8 *)&value, 4, 0, 0); + + verve_read_byte(dev, 0x07, &val); + cx231xx_info(" verve_read_byte address0x07=0x%x\n", val); + verve_write_byte(dev, 0x07, 0xF4); + verve_read_byte(dev, 0x07, &val); + cx231xx_info(" verve_read_byte address0x07=0x%x\n", val); + + cx231xx_capture_start(dev, 1, Vbi); + + cx231xx_mode_register(dev, EP_MODE_SET, 0x0500FE00); + cx231xx_mode_register(dev, GBULK_BIT_EN, 0xFFFDFFFF); + +} +void uninitGPIO(struct cx231xx *dev) +{ + u8 value[4] = { 0, 0, 0, 0 }; + + cx231xx_capture_start(dev, 0, Vbi); + verve_write_byte(dev, 0x07, 0x14); + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + 0x68, value, 4); +} + +/****************************************************************************** + * A F E - B L O C K C O N T R O L functions * + * [ANALOG FRONT END] * + ******************************************************************************/ +static int afe_write_byte(struct cx231xx *dev, u16 saddr, u8 data) +{ + return cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS, + saddr, 2, data, 1); +} + +static int afe_read_byte(struct cx231xx *dev, u16 saddr, u8 *data) +{ + int status; + u32 temp = 0; + + status = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, + saddr, 2, &temp, 1); + *data = (u8) temp; + return status; +} + +int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count) +{ + int status = 0; + u8 temp = 0; + u8 afe_power_status = 0; + int i = 0; + + /* super block initialize */ + temp = (u8) (ref_count & 0xff); + status = afe_write_byte(dev, SUP_BLK_TUNE2, temp); + if (status < 0) + return status; + + status = afe_read_byte(dev, SUP_BLK_TUNE2, &afe_power_status); + if (status < 0) + return status; + + temp = (u8) ((ref_count & 0x300) >> 8); + temp |= 0x40; + status = afe_write_byte(dev, SUP_BLK_TUNE1, temp); + if (status < 0) + return status; + + status = afe_write_byte(dev, SUP_BLK_PLL2, 0x0f); + if (status < 0) + return status; + + /* enable pll */ + while (afe_power_status != 0x18) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18); + if (status < 0) { + cx231xx_info( + ": Init Super Block failed in send cmd\n"); + break; + } + + status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status); + afe_power_status &= 0xff; + if (status < 0) { + cx231xx_info( + ": Init Super Block failed in receive cmd\n"); + break; + } + i++; + if (i == 10) { + cx231xx_info( + ": Init Super Block force break in loop !!!!\n"); + status = -1; + break; + } + } + + if (status < 0) + return status; + + /* start tuning filter */ + status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x40); + if (status < 0) + return status; + + msleep(5); + + /* exit tuning */ + status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x00); + + return status; +} + +int cx231xx_afe_init_channels(struct cx231xx *dev) +{ + int status = 0; + + /* power up all 3 channels, clear pd_buffer */ + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, 0x00); + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, 0x00); + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, 0x00); + + /* Enable quantizer calibration */ + status = afe_write_byte(dev, ADC_COM_QUANT, 0x02); + + /* channel initialize, force modulator (fb) reset */ + status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x17); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x17); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x17); + + /* start quantilizer calibration */ + status = afe_write_byte(dev, ADC_CAL_ATEST_CH1, 0x10); + status = afe_write_byte(dev, ADC_CAL_ATEST_CH2, 0x10); + status = afe_write_byte(dev, ADC_CAL_ATEST_CH3, 0x10); + msleep(5); + + /* exit modulator (fb) reset */ + status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x07); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x07); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x07); + + /* enable the pre_clamp in each channel for single-ended input */ + status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH1, 0xf0); + status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH2, 0xf0); + status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, 0xf0); + + /* use diode instead of resistor, so set term_en to 0, res_en to 0 */ + status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00); + status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00); + status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00); + + /* dynamic element matching off */ + status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH1, 0x03); + status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH2, 0x03); + status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, 0x03); + + return status; +} + +int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev) +{ + u8 c_value = 0; + int status = 0; + + status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH2, &c_value); + c_value &= (~(0x50)); + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, c_value); + + return status; +} + +/* + The Analog Front End in Cx231xx has 3 channels. These + channels are used to share between different inputs + like tuner, s-video and composite inputs. + + channel 1 ----- pin 1 to pin4(in reg is 1-4) + channel 2 ----- pin 5 to pin8(in reg is 5-8) + channel 3 ----- pin 9 to pin 12(in reg is 9-11) +*/ +int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux) +{ + u8 ch1_setting = (u8) input_mux; + u8 ch2_setting = (u8) (input_mux >> 8); + u8 ch3_setting = (u8) (input_mux >> 16); + int status = 0; + u8 value = 0; + + if (ch1_setting != 0) { + status = afe_read_byte(dev, ADC_INPUT_CH1, &value); + value &= ~INPUT_SEL_MASK; + value |= (ch1_setting - 1) << 4; + value &= 0xff; + status = afe_write_byte(dev, ADC_INPUT_CH1, value); + } + + if (ch2_setting != 0) { + status = afe_read_byte(dev, ADC_INPUT_CH2, &value); + value &= ~INPUT_SEL_MASK; + value |= (ch2_setting - 1) << 4; + value &= 0xff; + status = afe_write_byte(dev, ADC_INPUT_CH2, value); + } + + /* For ch3_setting, the value to put in the register is + 7 less than the input number */ + if (ch3_setting != 0) { + status = afe_read_byte(dev, ADC_INPUT_CH3, &value); + value &= ~INPUT_SEL_MASK; + value |= (ch3_setting - 1) << 4; + value &= 0xff; + status = afe_write_byte(dev, ADC_INPUT_CH3, value); + } + + return status; +} + +int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode) +{ + int status = 0; + + /* + * FIXME: We need to implement the AFE code for LOW IF and for HI IF. + * Currently, only baseband works. + */ + + switch (mode) { + case AFE_MODE_LOW_IF: + cx231xx_Setup_AFE_for_LowIF(dev); + break; + case AFE_MODE_BASEBAND: + status = cx231xx_afe_setup_AFE_for_baseband(dev); + break; + case AFE_MODE_EU_HI_IF: + /* SetupAFEforEuHiIF(); */ + break; + case AFE_MODE_US_HI_IF: + /* SetupAFEforUsHiIF(); */ + break; + case AFE_MODE_JAPAN_HI_IF: + /* SetupAFEforJapanHiIF(); */ + break; + } + + if ((mode != dev->afe_mode) && + (dev->video_input == CX231XX_VMUX_TELEVISION)) + status = cx231xx_afe_adjust_ref_count(dev, + CX231XX_VMUX_TELEVISION); + + dev->afe_mode = mode; + + return status; +} + +int cx231xx_afe_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode) +{ + u8 afe_power_status = 0; + int status = 0; + + switch (dev->model) { + case CX231XX_BOARD_CNXT_CARRAERA: + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_SHELBY: + case CX231XX_BOARD_CNXT_RDU_250: + case CX231XX_BOARD_CNXT_RDE_253S: + case CX231XX_BOARD_CNXT_RDU_253S: + case CX231XX_BOARD_CNXT_VIDEO_GRABBER: + case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_HAUPPAUGE_USBLIVE2: + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: + if (avmode == POLARIS_AVMODE_ANALOGT_TV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x00); + } else if (avmode == POLARIS_AVMODE_DIGITAL) { + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x70); + + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + afe_power_status |= FLD_PWRDN_PD_BANDGAP | + FLD_PWRDN_PD_BIAS | + FLD_PWRDN_PD_TUNECK; + status |= afe_write_byte(dev, SUP_BLK_PWRDN, + afe_power_status); + } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x00); + } else { + cx231xx_info("Invalid AV mode input\n"); + status = -1; + } + break; + default: + if (avmode == POLARIS_AVMODE_ANALOGT_TV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x40); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x40); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x00); + } else if (avmode == POLARIS_AVMODE_DIGITAL) { + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x70); + + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + afe_power_status |= FLD_PWRDN_PD_BANDGAP | + FLD_PWRDN_PD_BIAS | + FLD_PWRDN_PD_TUNECK; + status |= afe_write_byte(dev, SUP_BLK_PWRDN, + afe_power_status); + } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x40); + } else { + cx231xx_info("Invalid AV mode input\n"); + status = -1; + } + } /* switch */ + + return status; +} + +int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input) +{ + u8 input_mode = 0; + u8 ntf_mode = 0; + int status = 0; + + dev->video_input = video_input; + + if (video_input == CX231XX_VMUX_TELEVISION) { + status = afe_read_byte(dev, ADC_INPUT_CH3, &input_mode); + status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, + &ntf_mode); + } else { + status = afe_read_byte(dev, ADC_INPUT_CH1, &input_mode); + status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH1, + &ntf_mode); + } + + input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1); + + switch (input_mode) { + case SINGLE_ENDED: + dev->afe_ref_count = 0x23C; + break; + case LOW_IF: + dev->afe_ref_count = 0x24C; + break; + case EU_IF: + dev->afe_ref_count = 0x258; + break; + case US_IF: + dev->afe_ref_count = 0x260; + break; + default: + break; + } + + status = cx231xx_afe_init_super_block(dev, dev->afe_ref_count); + + return status; +} + +/****************************************************************************** + * V I D E O / A U D I O D E C O D E R C O N T R O L functions * + ******************************************************************************/ +static int vid_blk_write_byte(struct cx231xx *dev, u16 saddr, u8 data) +{ + return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, data, 1); +} + +static int vid_blk_read_byte(struct cx231xx *dev, u16 saddr, u8 *data) +{ + int status; + u32 temp = 0; + + status = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, &temp, 1); + *data = (u8) temp; + return status; +} + +static int vid_blk_write_word(struct cx231xx *dev, u16 saddr, u32 data) +{ + return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, data, 4); +} + +static int vid_blk_read_word(struct cx231xx *dev, u16 saddr, u32 *data) +{ + return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, data, 4); +} +int cx231xx_check_fw(struct cx231xx *dev) +{ + u8 temp = 0; + int status = 0; + status = vid_blk_read_byte(dev, DL_CTL_ADDRESS_LOW, &temp); + if (status < 0) + return status; + else + return temp; + +} + +int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input) +{ + int status = 0; + + switch (INPUT(input)->type) { + case CX231XX_VMUX_COMPOSITE1: + case CX231XX_VMUX_SVIDEO: + if ((dev->current_pcb_config.type == USB_BUS_POWER) && + (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) { + /* External AV */ + status = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ENXTERNAL_AV); + if (status < 0) { + cx231xx_errdev("%s: set_power_mode : Failed to" + " set Power - errCode [%d]!\n", + __func__, status); + return status; + } + } + status = cx231xx_set_decoder_video_input(dev, + INPUT(input)->type, + INPUT(input)->vmux); + break; + case CX231XX_VMUX_TELEVISION: + case CX231XX_VMUX_CABLE: + if ((dev->current_pcb_config.type == USB_BUS_POWER) && + (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) { + /* Tuner */ + status = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ANALOGT_TV); + if (status < 0) { + cx231xx_errdev("%s: set_power_mode:Failed" + " to set Power - errCode [%d]!\n", + __func__, status); + return status; + } + } + if (dev->tuner_type == TUNER_NXP_TDA18271) + status = cx231xx_set_decoder_video_input(dev, + CX231XX_VMUX_TELEVISION, + INPUT(input)->vmux); + else + status = cx231xx_set_decoder_video_input(dev, + CX231XX_VMUX_COMPOSITE1, + INPUT(input)->vmux); + + break; + default: + cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n", + __func__, INPUT(input)->type); + break; + } + + /* save the selection */ + dev->video_input = input; + + return status; +} + +int cx231xx_set_decoder_video_input(struct cx231xx *dev, + u8 pin_type, u8 input) +{ + int status = 0; + u32 value = 0; + + if (pin_type != dev->video_input) { + status = cx231xx_afe_adjust_ref_count(dev, pin_type); + if (status < 0) { + cx231xx_errdev("%s: adjust_ref_count :Failed to set" + "AFE input mux - errCode [%d]!\n", + __func__, status); + return status; + } + } + + /* call afe block to set video inputs */ + status = cx231xx_afe_set_input_mux(dev, input); + if (status < 0) { + cx231xx_errdev("%s: set_input_mux :Failed to set" + " AFE input mux - errCode [%d]!\n", + __func__, status); + return status; + } + + switch (pin_type) { + case CX231XX_VMUX_COMPOSITE1: + status = vid_blk_read_word(dev, AFE_CTRL, &value); + value |= (0 << 13) | (1 << 4); + value &= ~(1 << 5); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1ff8000)); + /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ + value |= 0x1000000; + status = vid_blk_write_word(dev, AFE_CTRL, value); + + status = vid_blk_read_word(dev, OUT_CTRL1, &value); + value |= (1 << 7); + status = vid_blk_write_word(dev, OUT_CTRL1, value); + + /* Set output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, + FLD_OUT_MODE, + dev->board.output_mode); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0)); + break; + case CX231XX_VMUX_SVIDEO: + /* Disable the use of DIF */ + + status = vid_blk_read_word(dev, AFE_CTRL, &value); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1ff8000)); + /* set FUNC_MODE[24:23] = 2 + IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */ + value |= 0x1000010; + status = vid_blk_write_word(dev, AFE_CTRL, value); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set YC input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, + FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1)); + + /* Chroma to ADC2 */ + status = vid_blk_read_word(dev, AFE_CTRL, &value); + value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */ + + /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8) + This sets them to use video + rather than audio. Only one of the two will be in use. */ + value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3); + + status = vid_blk_write_word(dev, AFE_CTRL, value); + + status = cx231xx_afe_set_mode(dev, AFE_MODE_BASEBAND); + break; + case CX231XX_VMUX_TELEVISION: + case CX231XX_VMUX_CABLE: + default: + /* TODO: Test if this is also needed for xc2028/xc3028 */ + if (dev->board.tuner_type == TUNER_XC5000) { + /* Disable the use of DIF */ + + status = vid_blk_read_word(dev, AFE_CTRL, &value); + value |= (0 << 13) | (1 << 4); + value &= ~(1 << 5); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1FF8000)); + /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ + value |= 0x1000000; + status = vid_blk_write_word(dev, AFE_CTRL, value); + + status = vid_blk_read_word(dev, OUT_CTRL1, &value); + value |= (1 << 7); + status = vid_blk_write_word(dev, OUT_CTRL1, value); + + /* Set output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, FLD_OUT_MODE, + dev->board.output_mode); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, + DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, + INPUT_MODE_CVBS_0)); + } else { + /* Enable the DIF for the tuner */ + + /* Reinitialize the DIF */ + status = cx231xx_dif_set_standard(dev, dev->norm); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Make sure bypass is cleared */ + status = vid_blk_read_word(dev, DIF_MISC_CTRL, &value); + + /* Clear the bypass bit */ + value &= ~FLD_DIF_DIF_BYPASS; + + /* Enable the use of the DIF block */ + status = vid_blk_write_word(dev, DIF_MISC_CTRL, value); + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* Disable the VBI_GATE_EN */ + value &= ~FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable, AGC, and + set the skip count to 2 */ + value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Wait until AGC locks up */ + msleep(1); + + /* Disable the auto-VGA enable AGC */ + value &= ~(FLD_VGA_AUTO_EN); + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Enable Polaris B0 AGC output */ + status = vid_blk_read_word(dev, PIN_CTRL, &value); + value |= (FLD_OEF_AGC_RF) | + (FLD_OEF_AGC_IFVGA) | + (FLD_OEF_AGC_IF); + status = vid_blk_write_word(dev, PIN_CTRL, value); + + /* Set output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, FLD_OUT_MODE, + dev->board.output_mode); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, + INPUT_MODE_CVBS_0)); + + /* Set some bits in AFE_CTRL so that channel 2 or 3 + * is ready to receive audio */ + /* Clear clamp for channels 2 and 3 (bit 16-17) */ + /* Clear droop comp (bit 19-20) */ + /* Set VGA_SEL (for audio control) (bit 7-8) */ + status = vid_blk_read_word(dev, AFE_CTRL, &value); + + /*Set Func mode:01-DIF 10-baseband 11-YUV*/ + value &= (~(FLD_FUNC_MODE)); + value |= 0x800000; + + value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2; + + status = vid_blk_write_word(dev, AFE_CTRL, value); + + if (dev->tuner_type == TUNER_NXP_TDA18271) { + status = vid_blk_read_word(dev, PIN_CTRL, + &value); + status = vid_blk_write_word(dev, PIN_CTRL, + (value & 0xFFFFFFEF)); + } + + break; + + } + break; + } + + /* Set raw VBI mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, FLD_VBIHACTRAW_EN, + cx231xx_set_field(FLD_VBIHACTRAW_EN, 1)); + + status = vid_blk_read_word(dev, OUT_CTRL1, &value); + if (value & 0x02) { + value |= (1 << 19); + status = vid_blk_write_word(dev, OUT_CTRL1, value); + } + + return status; +} + +void cx231xx_enable656(struct cx231xx *dev) +{ + u8 temp = 0; + /*enable TS1 data[0:7] as output to export 656*/ + + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); + + /*enable TS1 clock as output to export 656*/ + + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + temp = temp|0x04; + + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); +} +EXPORT_SYMBOL_GPL(cx231xx_enable656); + +void cx231xx_disable656(struct cx231xx *dev) +{ + u8 temp = 0; + + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); + + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + temp = temp&0xFB; + + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); +} +EXPORT_SYMBOL_GPL(cx231xx_disable656); + +/* + * Handle any video-mode specific overrides that are different + * on a per video standards basis after touching the MODE_CTRL + * register which resets many values for autodetect + */ +int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev) +{ + int status = 0; + + cx231xx_info("do_mode_ctrl_overrides : 0x%x\n", + (unsigned int)dev->norm); + + /* Change the DFE_CTRL3 bp_percent to fix flagging */ + status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280); + + if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) { + cx231xx_info("do_mode_ctrl_overrides NTSC\n"); + + /* Move the close caption lines out of active video, + adjust the active video start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x18); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VACTIVE_CNT, + 0x1E7000); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_V656BLANK_CNT, + 0x1C000000); + + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x79)); + + } else if (dev->norm & V4L2_STD_SECAM) { + cx231xx_info("do_mode_ctrl_overrides SECAM\n"); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x20); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VACTIVE_CNT, + cx231xx_set_field + (FLD_VACTIVE_CNT, + 0x244)); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_V656BLANK_CNT, + cx231xx_set_field + (FLD_V656BLANK_CNT, + 0x24)); + /* Adjust the active video horizontal start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x85)); + } else { + cx231xx_info("do_mode_ctrl_overrides PAL\n"); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x20); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VACTIVE_CNT, + cx231xx_set_field + (FLD_VACTIVE_CNT, + 0x244)); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_V656BLANK_CNT, + cx231xx_set_field + (FLD_V656BLANK_CNT, + 0x24)); + /* Adjust the active video horizontal start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x85)); + + } + + return status; +} + +int cx231xx_unmute_audio(struct cx231xx *dev) +{ + return vid_blk_write_byte(dev, PATH1_VOL_CTL, 0x24); +} +EXPORT_SYMBOL_GPL(cx231xx_unmute_audio); + +int stopAudioFirmware(struct cx231xx *dev) +{ + return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x03); +} + +int restartAudioFirmware(struct cx231xx *dev) +{ + return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x13); +} + +int cx231xx_set_audio_input(struct cx231xx *dev, u8 input) +{ + int status = 0; + enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE; + + switch (INPUT(input)->amux) { + case CX231XX_AMUX_VIDEO: + ainput = AUDIO_INPUT_TUNER_TV; + break; + case CX231XX_AMUX_LINE_IN: + status = cx231xx_i2s_blk_set_audio_input(dev, input); + ainput = AUDIO_INPUT_LINE; + break; + default: + break; + } + + status = cx231xx_set_audio_decoder_input(dev, ainput); + + return status; +} + +int cx231xx_set_audio_decoder_input(struct cx231xx *dev, + enum AUDIO_INPUT audio_input) +{ + u32 dwval; + int status; + u8 gen_ctrl; + u32 value = 0; + + /* Put it in soft reset */ + status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl); + gen_ctrl |= 1; + status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl); + + switch (audio_input) { + case AUDIO_INPUT_LINE: + /* setup AUD_IO control from Merlin paralle output */ + value = cx231xx_set_field(FLD_AUD_CHAN1_SRC, + AUD_CHAN_SRC_PARALLEL); + status = vid_blk_write_word(dev, AUD_IO_CTRL, value); + + /* setup input to Merlin, SRC2 connect to AC97 + bypass upsample-by-2, slave mode, sony mode, left justify + adr 091c, dat 01000000 */ + status = vid_blk_read_word(dev, AC97_CTL, &dwval); + + status = vid_blk_write_word(dev, AC97_CTL, + (dwval | FLD_AC97_UP2X_BYPASS)); + + /* select the parallel1 and SRC3 */ + status = vid_blk_write_word(dev, BAND_OUT_SEL, + cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) | + cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) | + cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0)); + + /* unmute all, AC97 in, independence mode + adr 08d0, data 0x00063073 */ + status = vid_blk_write_word(dev, DL_CTL, 0x3000001); + status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073); + + /* set AVC maximum threshold, adr 08d4, dat ffff0024 */ + status = vid_blk_read_word(dev, PATH1_VOL_CTL, &dwval); + status = vid_blk_write_word(dev, PATH1_VOL_CTL, + (dwval | FLD_PATH1_AVC_THRESHOLD)); + + /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */ + status = vid_blk_read_word(dev, PATH1_SC_CTL, &dwval); + status = vid_blk_write_word(dev, PATH1_SC_CTL, + (dwval | FLD_PATH1_SC_THRESHOLD)); + break; + + case AUDIO_INPUT_TUNER_TV: + default: + status = stopAudioFirmware(dev); + /* Setup SRC sources and clocks */ + status = vid_blk_write_word(dev, BAND_OUT_SEL, + cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) | + cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) | + cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) | + cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) | + cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) | + cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) | + cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) | + cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) | + cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) | + cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01)); + + /* Setup the AUD_IO control */ + status = vid_blk_write_word(dev, AUD_IO_CTRL, + cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) | + cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03)); + + status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870); + + /* setAudioStandard(_audio_standard); */ + status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870); + + status = restartAudioFirmware(dev); + + switch (dev->board.tuner_type) { + case TUNER_XC5000: + /* SIF passthrough at 28.6363 MHz sample rate */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + CHIP_CTRL, + FLD_SIF_EN, + cx231xx_set_field(FLD_SIF_EN, 1)); + break; + case TUNER_NXP_TDA18271: + /* Normal mode: SIF passthrough at 14.32 MHz */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + CHIP_CTRL, + FLD_SIF_EN, + cx231xx_set_field(FLD_SIF_EN, 0)); + break; + default: + /* This is just a casual suggestion to people adding + new boards in case they use a tuner type we don't + currently know about */ + printk(KERN_INFO "Unknown tuner type configuring SIF"); + break; + } + break; + + case AUDIO_INPUT_TUNER_FM: + /* use SIF for FM radio + setupFM(); + setAudioStandard(_audio_standard); + */ + break; + + case AUDIO_INPUT_MUTE: + status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F011012); + break; + } + + /* Take it out of soft reset */ + status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl); + gen_ctrl &= ~1; + status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl); + + return status; +} + +/****************************************************************************** + * C H I P Specific C O N T R O L functions * + ******************************************************************************/ +int cx231xx_init_ctrl_pin_status(struct cx231xx *dev) +{ + u32 value; + int status = 0; + + status = vid_blk_read_word(dev, PIN_CTRL, &value); + value |= (~dev->board.ctl_pin_status_mask); + status = vid_blk_write_word(dev, PIN_CTRL, value); + + return status; +} + +int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, + u8 analog_or_digital) +{ + int status = 0; + + /* first set the direction to output */ + status = cx231xx_set_gpio_direction(dev, + dev->board. + agc_analog_digital_select_gpio, 1); + + /* 0 - demod ; 1 - Analog mode */ + status = cx231xx_set_gpio_value(dev, + dev->board.agc_analog_digital_select_gpio, + analog_or_digital); + + return status; +} + +int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3) +{ + u8 value[4] = { 0, 0, 0, 0 }; + int status = 0; + bool current_is_port_3; + + if (dev->board.dont_use_port_3) + is_port_3 = false; + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, + PWR_CTL_EN, value, 4); + if (status < 0) + return status; + + current_is_port_3 = value[0] & I2C_DEMOD_EN ? true : false; + + /* Just return, if already using the right port */ + if (current_is_port_3 == is_port_3) + return 0; + + if (is_port_3) + value[0] |= I2C_DEMOD_EN; + else + value[0] &= ~I2C_DEMOD_EN; + + cx231xx_info("Changing the i2c master port to %d\n", + is_port_3 ? 3 : 1); + + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + + return status; + +} +EXPORT_SYMBOL_GPL(cx231xx_enable_i2c_port_3); + +void update_HH_register_after_set_DIF(struct cx231xx *dev) +{ +/* + u8 status = 0; + u32 value = 0; + + vid_blk_write_word(dev, PIN_CTRL, 0xA0FFF82F); + vid_blk_write_word(dev, DIF_MISC_CTRL, 0x0A203F11); + vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0x1BEFBF06); + + status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390); + status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); +*/ +} + +void cx231xx_dump_HH_reg(struct cx231xx *dev) +{ + u32 value = 0; + u16 i = 0; + + value = 0x45005390; + vid_blk_write_word(dev, 0x104, value); + + for (i = 0x100; i < 0x140; i++) { + vid_blk_read_word(dev, i, &value); + cx231xx_info("reg0x%x=0x%x\n", i, value); + i = i+3; + } + + for (i = 0x300; i < 0x400; i++) { + vid_blk_read_word(dev, i, &value); + cx231xx_info("reg0x%x=0x%x\n", i, value); + i = i+3; + } + + for (i = 0x400; i < 0x440; i++) { + vid_blk_read_word(dev, i, &value); + cx231xx_info("reg0x%x=0x%x\n", i, value); + i = i+3; + } + + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); + vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390); + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); +} + +void cx231xx_dump_SC_reg(struct cx231xx *dev) +{ + u8 value[4] = { 0, 0, 0, 0 }; + cx231xx_info("cx231xx_dump_SC_reg!\n"); + + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0], + value[1], value[2], value[3]); + + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0], + value[1], value[2], value[3]); + + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0], + value[1], value[2], value[3]); + + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0], + value[1], value[2], value[3]); + + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0], + value[1], value[2], value[3]); + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + value, 4); + cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0], + value[1], value[2], value[3]); + + +} + +void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) + +{ + u8 value = 0; + + afe_read_byte(dev, ADC_STATUS2_CH3, &value); + value = (value & 0xFE)|0x01; + afe_write_byte(dev, ADC_STATUS2_CH3, value); + + afe_read_byte(dev, ADC_STATUS2_CH3, &value); + value = (value & 0xFE)|0x00; + afe_write_byte(dev, ADC_STATUS2_CH3, value); + + +/* + config colibri to lo-if mode + + FIXME: ntf_mode = 2'b00 by default. But set 0x1 would reduce + the diff IF input by half, + + for low-if agc defect +*/ + + afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); + value = (value & 0xFC)|0x00; + afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); + + afe_read_byte(dev, ADC_INPUT_CH3, &value); + value = (value & 0xF9)|0x02; + afe_write_byte(dev, ADC_INPUT_CH3, value); + + afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); + value = (value & 0xFB)|0x04; + afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); + + afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); + value = (value & 0xFC)|0x03; + afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); + + afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); + value = (value & 0xFB)|0x04; + afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); + + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + value = (value & 0xF8)|0x06; + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + value = (value & 0x8F)|0x40; + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + + afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); + value = (value & 0xDF)|0x20; + afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); +} + +void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, + u8 spectral_invert, u32 mode) +{ + u32 colibri_carrier_offset = 0; + u32 func_mode = 0x01; /* Device has a DIF if this function is called */ + u32 standard = 0; + u8 value[4] = { 0, 0, 0, 0 }; + + cx231xx_info("Enter cx231xx_set_Colibri_For_LowIF()\n"); + value[0] = (u8) 0x6F; + value[1] = (u8) 0x6F; + value[2] = (u8) 0x6F; + value[3] = (u8) 0x6F; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + + /*Set colibri for low IF*/ + cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); + + /* Set C2HH for low IF operation.*/ + standard = dev->norm; + cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, + func_mode, standard); + + /* Get colibri offsets.*/ + colibri_carrier_offset = cx231xx_Get_Colibri_CarrierOffset(mode, + standard); + + cx231xx_info("colibri_carrier_offset=%d, standard=0x%x\n", + colibri_carrier_offset, standard); + + /* Set the band Pass filter for DIF*/ + cx231xx_set_DIF_bandpass(dev, (if_freq+colibri_carrier_offset), + spectral_invert, mode); +} + +u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd) +{ + u32 colibri_carrier_offset = 0; + + if (mode == TUNER_MODE_FM_RADIO) { + colibri_carrier_offset = 1100000; + } else if (standerd & (V4L2_STD_MN | V4L2_STD_NTSC_M_JP)) { + colibri_carrier_offset = 4832000; /*4.83MHz */ + } else if (standerd & (V4L2_STD_PAL_B | V4L2_STD_PAL_G)) { + colibri_carrier_offset = 2700000; /*2.70MHz */ + } else if (standerd & (V4L2_STD_PAL_D | V4L2_STD_PAL_I + | V4L2_STD_SECAM)) { + colibri_carrier_offset = 2100000; /*2.10MHz */ + } + + return colibri_carrier_offset; +} + +void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, + u8 spectral_invert, u32 mode) +{ + unsigned long pll_freq_word; + u32 dif_misc_ctrl_value = 0; + u64 pll_freq_u64 = 0; + u32 i = 0; + + cx231xx_info("if_freq=%d;spectral_invert=0x%x;mode=0x%x\n", + if_freq, spectral_invert, mode); + + + if (mode == TUNER_MODE_FM_RADIO) { + pll_freq_word = 0x905A1CAC; + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + + } else /*KSPROPERTY_TUNER_MODE_TV*/{ + /* Calculate the PLL frequency word based on the adjusted if_freq*/ + pll_freq_word = if_freq; + pll_freq_u64 = (u64)pll_freq_word << 28L; + do_div(pll_freq_u64, 50000000); + pll_freq_word = (u32)pll_freq_u64; + /*pll_freq_word = 0x3463497;*/ + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + + if (spectral_invert) { + if_freq -= 400000; + /* Enable Spectral Invert*/ + vid_blk_read_word(dev, DIF_MISC_CTRL, + &dif_misc_ctrl_value); + dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000; + vid_blk_write_word(dev, DIF_MISC_CTRL, + dif_misc_ctrl_value); + } else { + if_freq += 400000; + /* Disable Spectral Invert*/ + vid_blk_read_word(dev, DIF_MISC_CTRL, + &dif_misc_ctrl_value); + dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF; + vid_blk_write_word(dev, DIF_MISC_CTRL, + dif_misc_ctrl_value); + } + + if_freq = (if_freq/100000)*100000; + + if (if_freq < 3000000) + if_freq = 3000000; + + if (if_freq > 16000000) + if_freq = 16000000; + } + + cx231xx_info("Enter IF=%zd\n", + ARRAY_SIZE(Dif_set_array)); + for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) { + if (Dif_set_array[i].if_freq == if_freq) { + vid_blk_write_word(dev, + Dif_set_array[i].register_address, Dif_set_array[i].value); + } + } +} + +/****************************************************************************** + * D I F - B L O C K C O N T R O L functions * + ******************************************************************************/ +int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, + u32 function_mode, u32 standard) +{ + int status = 0; + + + if (mode == V4L2_TUNER_RADIO) { + /* C2HH */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } else if (standard != DIF_USE_BASEBAND) { + if (standard & V4L2_STD_MN) { + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + /* 0x124, AUD_CHAN1_SRC = 0x3 */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AUD_IO_CTRL, 0, 31, 0x00000003); + } else if ((standard == V4L2_STD_PAL_I) | + (standard & V4L2_STD_PAL_D) | + (standard & V4L2_STD_SECAM)) { + /* C2HH setup */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } else { + /* default PAL BG */ + /* C2HH setup */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } + } + + return status; +} + +int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard) +{ + int status = 0; + u32 dif_misc_ctrl_value = 0; + u32 func_mode = 0; + + cx231xx_info("%s: setStandard to %x\n", __func__, standard); + + status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); + if (standard != DIF_USE_BASEBAND) + dev->norm = standard; + + switch (dev->model) { + case CX231XX_BOARD_CNXT_CARRAERA: + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_SHELBY: + case CX231XX_BOARD_CNXT_RDU_250: + case CX231XX_BOARD_CNXT_VIDEO_GRABBER: + case CX231XX_BOARD_HAUPPAUGE_EXETER: + func_mode = 0x03; + break; + case CX231XX_BOARD_CNXT_RDE_253S: + case CX231XX_BOARD_CNXT_RDU_253S: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: + func_mode = 0x01; + break; + default: + func_mode = 0x01; + } + + status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, + func_mode, standard); + + if (standard == DIF_USE_BASEBAND) { /* base band */ + /* There is a different SRC_PHASE_INC value + for baseband vs. DIF */ + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0xDF7DF83); + status = vid_blk_read_word(dev, DIF_MISC_CTRL, + &dif_misc_ctrl_value); + dif_misc_ctrl_value |= FLD_DIF_DIF_BYPASS; + status = vid_blk_write_word(dev, DIF_MISC_CTRL, + dif_misc_ctrl_value); + } else if (standard & V4L2_STD_PAL_D) { + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3934EA); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + } else if (standard & V4L2_STD_PAL_I) { + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x5F39A934); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a033F11; + } else if (standard & V4L2_STD_PAL_M) { + /* improved Low Frequency Phase Noise */ + status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C); + status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85); + status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a); + status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380); + status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, + 0x26001700); + status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, + 0x00002660); + status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, + 0x72500800); + status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, + 0x27000100); + status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x012c405d); + status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, + 0x009f50c1); + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, + 0x1befbf06); + status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, + 0x000035e8); + status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB, + 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3A0A3F10; + } else if (standard & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { + /* improved Low Frequency Phase Noise */ + status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C); + status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85); + status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a); + status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380); + status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, + 0x26001700); + status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, + 0x00002660); + status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, + 0x72500800); + status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, + 0x27000100); + status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, + 0x012c405d); + status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, + 0x009f50c1); + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, + 0x1befbf06); + status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, + 0x000035e8); + status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB, + 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value = 0x3A093F10; + } else if (standard & + (V4L2_STD_SECAM_B | V4L2_STD_SECAM_D | V4L2_STD_SECAM_G | + V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)) { + + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x888C0380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0xf4000000); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + } else if (standard & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) { + /* Is it SECAM_L1? */ + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x888C0380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0xf2560000); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + + } else if (standard & V4L2_STD_NTSC_M) { + /* V4L2_STD_NTSC_M (75 IRE Setup) Or + V4L2_STD_NTSC_M_JP (Japan, 0 IRE Setup) */ + + /* For NTSC the centre frequency of video coming out of + sidewinder is around 7.1MHz or 3.6MHz depending on the + spectral inversion. so for a non spectrally inverted channel + the pll freq word is 0x03420c49 + */ + + status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0x6503BC0C); + status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xBD038C85); + status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1DB4640A); + status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C0380); + status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, + 0x26001700); + status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, + 0x00002660); + status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, + 0x04000800); + status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, + 0x27000100); + status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x01296e1f); + + status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, + 0x009f50c1); + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, + 0x1befbf06); + status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, + 0x000035e8); + + status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600); + status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT, + 0xC2262600); + status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a003F10; + } else { + /* default PAL BG */ + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530EC); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00A653A8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a013F11; + } + + /* The AGC values should be the same for all standards, + AUD_SRC_SEL[19] should always be disabled */ + dif_misc_ctrl_value &= ~FLD_DIF_AUD_SRC_SEL; + + /* It is still possible to get Set Standard calls even when we + are in FM mode. + This is done to override the value for FM. */ + if (dev->active_mode == V4L2_TUNER_RADIO) + dif_misc_ctrl_value = 0x7a080000; + + /* Write the calculated value for misc ontrol register */ + status = vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); + + return status; +} + +int cx231xx_tuner_pre_channel_change(struct cx231xx *dev) +{ + int status = 0; + u32 dwval; + + /* Set the RF and IF k_agc values to 3 */ + status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval); + dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); + dwval |= 0x33000000; + + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval); + + return status; +} + +int cx231xx_tuner_post_channel_change(struct cx231xx *dev) +{ + int status = 0; + u32 dwval; + cx231xx_info("cx231xx_tuner_post_channel_change dev->tuner_type =0%d\n", + dev->tuner_type); + /* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for + * SECAM L/B/D standards */ + status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval); + dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); + + if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B | + V4L2_STD_SECAM_D)) { + if (dev->tuner_type == TUNER_NXP_TDA18271) { + dwval &= ~FLD_DIF_IF_REF; + dwval |= 0x88000300; + } else + dwval |= 0x88000000; + } else { + if (dev->tuner_type == TUNER_NXP_TDA18271) { + dwval &= ~FLD_DIF_IF_REF; + dwval |= 0xCC000300; + } else + dwval |= 0x44000000; + } + + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval); + + return status; +} + +/****************************************************************************** + * I 2 S - B L O C K C O N T R O L functions * + ******************************************************************************/ +int cx231xx_i2s_blk_initialize(struct cx231xx *dev) +{ + int status = 0; + u32 value; + + status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, &value, 1); + /* enables clock to delta-sigma and decimation filter */ + value |= 0x80; + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, value, 1); + /* power up all channel */ + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + + return status; +} + +int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode) +{ + int status = 0; + u32 value = 0; + + if (avmode != POLARIS_AVMODE_ENXTERNAL_AV) { + status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, &value, 1); + value |= 0xfe; + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, value, 1); + } else { + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + } + + return status; +} + +/* set i2s_blk for audio input types */ +int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input) +{ + int status = 0; + + switch (audio_input) { + case CX231XX_AMUX_LINE_IN: + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, 0x80, 1); + break; + case CX231XX_AMUX_VIDEO: + default: + break; + } + + dev->ctl_ainput = audio_input; + + return status; +} + +/****************************************************************************** + * P O W E R C O N T R O L functions * + ******************************************************************************/ +int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode) +{ + u8 value[4] = { 0, 0, 0, 0 }; + u32 tmp = 0; + int status = 0; + + if (dev->power_mode != mode) + dev->power_mode = mode; + else { + cx231xx_info(" setPowerMode::mode = %d, No Change req.\n", + mode); + return 0; + } + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, + 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + + switch (mode) { + case POLARIS_AVMODE_ENXTERNAL_AV: + + tmp &= (~PWR_MODE_MASK); + + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, + value, 4); + msleep(PWR_SLEEP_INTERVAL); + + tmp |= POLARIS_AVMODE_ENXTERNAL_AV; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + + /* reset state of xceive tuner */ + dev->xc_fw_load_done = 0; + break; + + case POLARIS_AVMODE_ANALOGT_TV: + + tmp |= PWR_DEMOD_EN; + tmp |= (I2C_DEMOD_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + if (!(tmp & PWR_TUNER_EN)) { + tmp |= (PWR_TUNER_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (!(tmp & PWR_AV_EN)) { + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_ISO_EN)) { + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (!(tmp & POLARIS_AVMODE_ANALOGT_TV)) { + tmp |= POLARIS_AVMODE_ANALOGT_TV; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (dev->board.tuner_type != TUNER_ABSENT) { + /* Enable tuner */ + cx231xx_enable_i2c_port_3(dev, true); + + /* reset the Tuner */ + if (dev->board.tuner_gpio) + cx231xx_gpio_set(dev, dev->board.tuner_gpio); + + if (dev->cx231xx_reset_analog_tuner) + dev->cx231xx_reset_analog_tuner(dev); + } + + break; + + case POLARIS_AVMODE_DIGITAL: + if (!(tmp & PWR_TUNER_EN)) { + tmp |= (PWR_TUNER_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_AV_EN)) { + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_ISO_EN)) { + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + tmp &= (~PWR_AV_MODE); + tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + if (!(tmp & PWR_DEMOD_EN)) { + tmp |= PWR_DEMOD_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (dev->board.tuner_type != TUNER_ABSENT) { + /* + * Enable tuner + * Hauppauge Exeter seems to need to do something different! + */ + if (dev->model == CX231XX_BOARD_HAUPPAUGE_EXETER) + cx231xx_enable_i2c_port_3(dev, false); + else + cx231xx_enable_i2c_port_3(dev, true); + + /* reset the Tuner */ + if (dev->board.tuner_gpio) + cx231xx_gpio_set(dev, dev->board.tuner_gpio); + + if (dev->cx231xx_reset_analog_tuner) + dev->cx231xx_reset_analog_tuner(dev); + } + break; + + default: + break; + } + + msleep(PWR_SLEEP_INTERVAL); + + /* For power saving, only enable Pwr_resetout_n + when digital TV is selected. */ + if (mode == POLARIS_AVMODE_DIGITAL) { + tmp |= PWR_RESETOUT_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + /* update power control for afe */ + status = cx231xx_afe_update_power_control(dev, mode); + + /* update power control for i2s_blk */ + status = cx231xx_i2s_blk_update_power_control(dev, mode); + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, + 4); + + return status; +} + +int cx231xx_power_suspend(struct cx231xx *dev) +{ + u8 value[4] = { 0, 0, 0, 0 }; + u32 tmp = 0; + int status = 0; + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + value, 4); + if (status > 0) + return status; + + tmp = *((u32 *) value); + tmp &= (~PWR_MODE_MASK); + + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, + value, 4); + + return status; +} + +/****************************************************************************** + * S T R E A M C O N T R O L functions * + ******************************************************************************/ +int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask); + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, + value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp |= ep_mask; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, + value, 4); + + return status; +} + +int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask); + status = + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp &= (~ep_mask); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, + value, 4); + + return status; +} + +int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) +{ + int status = 0; + u32 value = 0; + u8 val[4] = { 0, 0, 0, 0 }; + + if (dev->udev->speed == USB_SPEED_HIGH) { + switch (media_type) { + case Audio: + cx231xx_info("%s: Audio enter HANC\n", __func__); + status = + cx231xx_mode_register(dev, TS_MODE_REG, 0x9300); + break; + + case Vbi: + cx231xx_info("%s: set vanc registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300); + break; + + case Sliced_cc: + cx231xx_info("%s: set hanc registers\n", __func__); + status = + cx231xx_mode_register(dev, TS_MODE_REG, 0x1300); + break; + + case Raw_Video: + cx231xx_info("%s: set video registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); + break; + + case TS1_serial_mode: + cx231xx_info("%s: set ts1 registers", __func__); + + if (dev->board.has_417) { + cx231xx_info(" MPEG\n"); + value &= 0xFFFFFFFC; + value |= 0x3; + + status = cx231xx_mode_register(dev, TS_MODE_REG, value); + + val[0] = 0x04; + val[1] = 0xA3; + val[2] = 0x3B; + val[3] = 0x00; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); + + val[0] = 0x00; + val[1] = 0x08; + val[2] = 0x00; + val[3] = 0x08; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_LENGTH_REG, val, 4); + + } else { + cx231xx_info(" BDA\n"); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); + status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x010); + } + break; + + case TS1_parallel_mode: + cx231xx_info("%s: set ts1 parallel mode registers\n", + __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); + status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400); + break; + } + } else { + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); + } + + return status; +} + +int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) +{ + int rc = -1; + u32 ep_mask = -1; + struct pcb_config *pcb_config; + + /* get EP for media type */ + pcb_config = (struct pcb_config *)&dev->current_pcb_config; + + if (pcb_config->config_num) { + switch (media_type) { + case Raw_Video: + ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ + break; + case Audio: + ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ + break; + case Vbi: + ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ + break; + case Sliced_cc: + ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ + break; + case TS1_serial_mode: + case TS1_parallel_mode: + ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ + break; + case TS2: + ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ + break; + } + } + + if (start) { + rc = cx231xx_initialize_stream_xfer(dev, media_type); + + if (rc < 0) + return rc; + + /* enable video capture */ + if (ep_mask > 0) + rc = cx231xx_start_stream(dev, ep_mask); + } else { + /* disable video capture */ + if (ep_mask > 0) + rc = cx231xx_stop_stream(dev, ep_mask); + } + + if (dev->mode == CX231XX_ANALOG_MODE) + ;/* do any in Analog mode */ + else + ;/* do any in digital mode */ + + return rc; +} +EXPORT_SYMBOL_GPL(cx231xx_capture_start); + +/***************************************************************************** +* G P I O B I T control functions * +******************************************************************************/ +int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val) +{ + int status = 0; + + status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0); + + return status; +} + +int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val) +{ + int status = 0; + + status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1); + + return status; +} + +/* +* cx231xx_set_gpio_direction +* Sets the direction of the GPIO pin to input or output +* +* Parameters : +* pin_number : The GPIO Pin number to program the direction for +* from 0 to 31 +* pin_value : The Direction of the GPIO Pin under reference. +* 0 = Input direction +* 1 = Output direction +*/ +int cx231xx_set_gpio_direction(struct cx231xx *dev, + int pin_number, int pin_value) +{ + int status = 0; + u32 value = 0; + + /* Check for valid pin_number - if 32 , bail out */ + if (pin_number >= 32) + return -EINVAL; + + /* input */ + if (pin_value == 0) + value = dev->gpio_dir & (~(1 << pin_number)); /* clear */ + else + value = dev->gpio_dir | (1 << pin_number); + + status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val); + + /* cache the value for future */ + dev->gpio_dir = value; + + return status; +} + +/* +* cx231xx_set_gpio_value +* Sets the value of the GPIO pin to Logic high or low. The Pin under +* reference should ALREADY BE SET IN OUTPUT MODE !!!!!!!!! +* +* Parameters : +* pin_number : The GPIO Pin number to program the direction for +* pin_value : The value of the GPIO Pin under reference. +* 0 = set it to 0 +* 1 = set it to 1 +*/ +int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value) +{ + int status = 0; + u32 value = 0; + + /* Check for valid pin_number - if 0xFF , bail out */ + if (pin_number >= 32) + return -EINVAL; + + /* first do a sanity check - if the Pin is not output, make it output */ + if ((dev->gpio_dir & (1 << pin_number)) == 0x00) { + /* It was in input mode */ + value = dev->gpio_dir | (1 << pin_number); + dev->gpio_dir = value; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *) &dev->gpio_val); + value = 0; + } + + if (pin_value == 0) + value = dev->gpio_val & (~(1 << pin_number)); + else + value = dev->gpio_val | (1 << pin_number); + + /* store the value */ + dev->gpio_val = value; + + /* toggle bit0 of GP_IO */ + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +/***************************************************************************** +* G P I O I2C related functions * +******************************************************************************/ +int cx231xx_gpio_i2c_start(struct cx231xx *dev) +{ + int status = 0; + + /* set SCL to output 1 ; set SDA to output 1 */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + return status; +} + +int cx231xx_gpio_i2c_end(struct cx231xx *dev) +{ + int status = 0; + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to input ,release SCL cable control + set SDA to input ,release SDA cable control */ + dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + + status = + cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + return status; +} + +int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data) +{ + int status = 0; + u8 i; + + /* set SCL to output ; set SDA to output */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + + for (i = 0; i < 8; i++) { + if (((data << i) & 0x80) == 0) { + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + } else { + /* set SCL to output 0; set SDA to output 1 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to output 1 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 0; set SDA to output 1 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + } + } + return status; +} + +int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf) +{ + u8 value = 0; + int status = 0; + u32 gpio_logic_value = 0; + u8 i; + + /* read byte */ + for (i = 0; i < 8; i++) { /* send write I2c addr */ + + /* set SCL to output 0; set SDA to input */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to input */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* get SDA data bit */ + gpio_logic_value = dev->gpio_val; + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0) + value |= (1 << (8 - i - 1)); + + dev->gpio_val = gpio_logic_value; + } + + /* set SCL to output 0,finish the read latest SCL signal. + !!!set SDA to input, never to modify SDA direction at + the same times */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* store the value */ + *buf = value & 0xff; + + return status; +} + +int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) +{ + int status = 0; + u32 gpio_logic_value = 0; + int nCnt = 10; + int nInit = nCnt; + + /* clock stretch; set SCL to input; set SDA to input; + get SCL value till SCL = 1 */ + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); + + gpio_logic_value = dev->gpio_val; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + do { + msleep(2); + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + nCnt--; + } while (((dev->gpio_val & + (1 << dev->board.tuner_scl_gpio)) == 0) && + (nCnt > 0)); + + if (nCnt == 0) + cx231xx_info("No ACK after %d msec -GPIO I2C failed!", + nInit * 10); + + /* + * readAck + * through clock stretch, slave has given a SCL signal, + * so the SDA data can be directly read. + */ + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) { + dev->gpio_val = gpio_logic_value; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + status = 0; + } else { + dev->gpio_val = gpio_logic_value; + dev->gpio_val |= (1 << dev->board.tuner_sda_gpio); + } + + /* read SDA end, set the SCL to output 0, after this operation, + SDA direction can be changed. */ + dev->gpio_val = gpio_logic_value; + dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev) +{ + int status = 0; + + /* set SDA to ouput */ + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 0 (output); set SDA = 0 (output) */ + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 1 (output); set SDA = 0 (output) */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 0 (output); set SDA = 0 (output) */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SDA to input,and then the slave will read data from SDA. */ + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev) +{ + int status = 0; + + /* set scl to output ; set sda to input */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set scl to output 0; set sda to input */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set scl to output 1; set sda to input */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +/***************************************************************************** +* G P I O I2C related functions * +******************************************************************************/ +/* cx231xx_gpio_i2c_read + * Function to read data from gpio based I2C interface + */ +int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) +{ + int status = 0; + int i = 0; + + /* get the lock */ + mutex_lock(&dev->gpio_i2c_lock); + + /* start */ + status = cx231xx_gpio_i2c_start(dev); + + /* write dev_addr */ + status = cx231xx_gpio_i2c_write_byte(dev, (dev_addr << 1) + 1); + + /* readAck */ + status = cx231xx_gpio_i2c_read_ack(dev); + + /* read data */ + for (i = 0; i < len; i++) { + /* read data */ + buf[i] = 0; + status = cx231xx_gpio_i2c_read_byte(dev, &buf[i]); + + if ((i + 1) != len) { + /* only do write ack if we more length */ + status = cx231xx_gpio_i2c_write_ack(dev); + } + } + + /* write NAK - inform reads are complete */ + status = cx231xx_gpio_i2c_write_nak(dev); + + /* write end */ + status = cx231xx_gpio_i2c_end(dev); + + /* release the lock */ + mutex_unlock(&dev->gpio_i2c_lock); + + return status; +} + +/* cx231xx_gpio_i2c_write + * Function to write data to gpio based I2C interface + */ +int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) +{ + int i = 0; + + /* get the lock */ + mutex_lock(&dev->gpio_i2c_lock); + + /* start */ + cx231xx_gpio_i2c_start(dev); + + /* write dev_addr */ + cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); + + /* read Ack */ + cx231xx_gpio_i2c_read_ack(dev); + + for (i = 0; i < len; i++) { + /* Write data */ + cx231xx_gpio_i2c_write_byte(dev, buf[i]); + + /* read Ack */ + cx231xx_gpio_i2c_read_ack(dev); + } + + /* write End */ + cx231xx_gpio_i2c_end(dev); + + /* release the lock */ + mutex_unlock(&dev->gpio_i2c_lock); + + return 0; +} diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c new file mode 100644 index 000000000000..02d4d36735d3 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -0,0 +1,1370 @@ +/* + cx231xx-cards.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 + Based on em28xx driver + + This program is free software; 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 +#include + +#include +#include "dvb-usb-ids.h" +#include "xc5000.h" +#include "tda18271.h" + +#include "cx231xx.h" + +static int tuner = -1; +module_param(tuner, int, 0444); +MODULE_PARM_DESC(tuner, "tuner type"); + +static int transfer_mode = 1; +module_param(transfer_mode, int, 0444); +MODULE_PARM_DESC(transfer_mode, "transfer mode (1-ISO or 0-BULK)"); + +static unsigned int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); + +/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */ +static unsigned long cx231xx_devused; + +/* + * Reset sequences for analog/digital modes + */ + +static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = { + {0x03, 0x01, 10}, + {0x03, 0x00, 30}, + {0x03, 0x01, 10}, + {-1, -1, -1}, +}; + +/* + * Board definitions + */ +struct cx231xx_board cx231xx_boards[] = { + [CX231XX_BOARD_UNKNOWN] = { + .name = "Unknown CX231xx video grabber", + .tuner_type = TUNER_ABSENT, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, + [CX231XX_BOARD_CNXT_CARRAERA] = { + .name = "Conexant Hybrid TV - CARRAERA", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x02, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, + [CX231XX_BOARD_CNXT_SHELBY] = { + .name = "Conexant Hybrid TV - SHELBY", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x32, + .norm = V4L2_STD_NTSC, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, + [CX231XX_BOARD_CNXT_RDE_253S] = { + .name = "Conexant Hybrid TV - RDE253S", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x1c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x02, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, + + [CX231XX_BOARD_CNXT_RDU_253S] = { + .name = "Conexant Hybrid TV - RDU253S", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x1c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x02, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, + [CX231XX_BOARD_CNXT_VIDEO_GRABBER] = { + .name = "Conexant VIDEO GRABBER", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x1c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_PAL, + .no_alt_vanc = 1, + .external_av = 1, + .has_417 = 1, + + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, + [CX231XX_BOARD_CNXT_RDE_250] = { + .name = "Conexant Hybrid TV - rde 250", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x02, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + } + }, + }, + [CX231XX_BOARD_CNXT_RDU_250] = { + .name = "Conexant Hybrid TV - RDU 250", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x32, + .norm = V4L2_STD_NTSC, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + } + }, + }, + [CX231XX_BOARD_HAUPPAUGE_EXETER] = { + .name = "Hauppauge EXETER", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x0e, + .norm = V4L2_STD_NTSC, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, + [CX231XX_BOARD_HAUPPAUGE_USBLIVE2] = { + .name = "Hauppauge USB Live 2", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, + .dont_use_port_3 = 1, + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, + [CX231XX_BOARD_KWORLD_UB430_USB_HYBRID] = { + .name = "Kworld UB430 USB Hybrid", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x11, /* According with PV cxPolaris.inf file */ + .tuner_sif_gpio = -1, + .tuner_scl_gpio = -1, + .tuner_sda_gpio = -1, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 2, + .demod_i2c_master = 1, + .ir_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x10, + .norm = V4L2_STD_PAL_M, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, + [CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = { + .name = "Pixelview PlayTV USB Hybrid", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */ + .tuner_sif_gpio = -1, + .tuner_scl_gpio = -1, + .tuner_sda_gpio = -1, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 2, + .demod_i2c_master = 1, + .ir_i2c_master = 2, + .rc_map_name = RC_MAP_PIXELVIEW_002T, + .has_dvb = 1, + .demod_addr = 0x10, + .norm = V4L2_STD_PAL_M, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, + [CX231XX_BOARD_PV_XCAPTURE_USB] = { + .name = "Pixelview Xcapture USB", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, + .dont_use_port_3 = 1, + + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, + + [CX231XX_BOARD_ICONBIT_U100] = { + .name = "Iconbit Analog Stick U100 FM", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x1C, + .gpio_pin_status_mask = 0x4001000, + + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, + [CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL] = { + .name = "Hauppauge WinTV USB2 FM (PAL)", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, + [CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC] = { + .name = "Hauppauge WinTV USB2 FM (NTSC)", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .norm = V4L2_STD_NTSC, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, +}; +const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); + +/* table of devices that work with this driver */ +struct usb_device_id cx231xx_id_table[] = { + {USB_DEVICE(0x0572, 0x5A3C), + .driver_info = CX231XX_BOARD_UNKNOWN}, + {USB_DEVICE(0x0572, 0x58A2), + .driver_info = CX231XX_BOARD_CNXT_CARRAERA}, + {USB_DEVICE(0x0572, 0x58A1), + .driver_info = CX231XX_BOARD_CNXT_SHELBY}, + {USB_DEVICE(0x0572, 0x58A4), + .driver_info = CX231XX_BOARD_CNXT_RDE_253S}, + {USB_DEVICE(0x0572, 0x58A5), + .driver_info = CX231XX_BOARD_CNXT_RDU_253S}, + {USB_DEVICE(0x0572, 0x58A6), + .driver_info = CX231XX_BOARD_CNXT_VIDEO_GRABBER}, + {USB_DEVICE(0x0572, 0x589E), + .driver_info = CX231XX_BOARD_CNXT_RDE_250}, + {USB_DEVICE(0x0572, 0x58A0), + .driver_info = CX231XX_BOARD_CNXT_RDU_250}, + {USB_DEVICE(0x2040, 0xb110), + .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL}, + {USB_DEVICE(0x2040, 0xb111), + .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC}, + {USB_DEVICE(0x2040, 0xb120), + .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, + {USB_DEVICE(0x2040, 0xb140), + .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, + {USB_DEVICE(0x2040, 0xc200), + .driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2}, + {USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001), + .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID}, + {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014), + .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB}, + {USB_DEVICE(0x1b80, 0xe424), + .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID}, + {USB_DEVICE(0x1f4d, 0x0237), + .driver_info = CX231XX_BOARD_ICONBIT_U100}, + {}, +}; + +MODULE_DEVICE_TABLE(usb, cx231xx_id_table); + +/* cx231xx_tuner_callback + * will be used to reset XC5000 tuner using GPIO pin + */ + +int cx231xx_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct cx231xx *dev = ptr; + + if (dev->tuner_type == TUNER_XC5000) { + if (command == XC5000_TUNER_RESET) { + cx231xx_info + ("Tuner CB: RESET: cmd %d : tuner type %d \n", + command, dev->tuner_type); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 1); + msleep(10); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 0); + msleep(330); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 1); + msleep(10); + } + } else if (dev->tuner_type == TUNER_NXP_TDA18271) { + switch (command) { + case TDA18271_CALLBACK_CMD_AGC_ENABLE: + if (dev->model == CX231XX_BOARD_PV_PLAYTV_USB_HYBRID) + rc = cx231xx_set_agc_analog_digital_mux_select(dev, arg); + break; + default: + rc = -EINVAL; + break; + } + } + return rc; +} +EXPORT_SYMBOL_GPL(cx231xx_tuner_callback); + +void cx231xx_reset_out(struct cx231xx *dev) +{ + cx231xx_set_gpio_value(dev, CX23417_RESET, 1); + msleep(200); + cx231xx_set_gpio_value(dev, CX23417_RESET, 0); + msleep(200); + cx231xx_set_gpio_value(dev, CX23417_RESET, 1); +} +void cx231xx_enable_OSC(struct cx231xx *dev) +{ + cx231xx_set_gpio_value(dev, CX23417_OSC_EN, 1); +} +void cx231xx_sleep_s5h1432(struct cx231xx *dev) +{ + cx231xx_set_gpio_value(dev, SLEEP_S5H1432, 0); +} + +static inline void cx231xx_set_model(struct cx231xx *dev) +{ + memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board)); +} + +/* Since cx231xx_pre_card_setup() requires a proper dev->model, + * this won't work for boards with generic PCI IDs + */ +void cx231xx_pre_card_setup(struct cx231xx *dev) +{ + + cx231xx_set_model(dev); + + cx231xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); + + /* set the direction for GPIO pins */ + if (dev->board.tuner_gpio) { + cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1); + } + if (dev->board.tuner_sif_gpio >= 0) + cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1); + + /* request some modules if any required */ + + /* set the mode to Analog mode initially */ + cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); + + /* Unlock device */ + /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */ + +} + +static void cx231xx_config_tuner(struct cx231xx *dev) +{ + struct tuner_setup tun_setup; + struct v4l2_frequency f; + + if (dev->tuner_type == TUNER_ABSENT) + return; + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + tun_setup.tuner_callback = cx231xx_tuner_callback; + + tuner_call(dev, tuner, s_type_addr, &tun_setup); + +#if 0 + if (tun_setup.type == TUNER_XC5000) { + static struct xc2028_ctrl ctrl = { + .fname = XC5000_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = 0; + }; + struct v4l2_priv_tun_config cfg = { + .tuner = dev->tuner_type, + .priv = &ctrl, + }; + tuner_call(dev, tuner, s_config, &cfg); + } +#endif + /* configure tuner */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 9076; /* just a magic number */ + dev->ctl_freq = f.frequency; + call_all(dev, tuner, s_frequency, &f); + +} + +void cx231xx_card_setup(struct cx231xx *dev) +{ + + cx231xx_set_model(dev); + + dev->tuner_type = cx231xx_boards[dev->model].tuner_type; + if (cx231xx_boards[dev->model].tuner_addr) + dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr; + + /* request some modules */ + if (dev->board.decoder == CX231XX_AVDECODER) { + dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_bus[0].i2c_adap, + "cx25840", 0x88 >> 1, NULL); + if (dev->sd_cx25840 == NULL) + cx231xx_info("cx25840 subdev registration failure\n"); + cx25840_call(dev, core, load_fw); + + } + + /* Initialize the tuner */ + if (dev->board.tuner_type != TUNER_ABSENT) { + dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + "tuner", + dev->tuner_addr, NULL); + if (dev->sd_tuner == NULL) + cx231xx_info("tuner subdev registration failure\n"); + else + cx231xx_config_tuner(dev); + } +} + +/* + * cx231xx_config() + * inits registers with sane defaults + */ +int cx231xx_config(struct cx231xx *dev) +{ + /* TBD need to add cx231xx specific code */ + dev->mute = 1; /* maybe not the right place... */ + dev->volume = 0x1f; + + return 0; +} + +/* + * cx231xx_config_i2c() + * configure i2c attached devices + */ +void cx231xx_config_i2c(struct cx231xx *dev) +{ + /* u32 input = INPUT(dev->video_input)->vmux; */ + + call_all(dev, video, s_stream, 1); +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_release_resources(struct cx231xx *dev) +{ + cx231xx_release_analog_resources(dev); + + cx231xx_remove_from_devlist(dev); + + cx231xx_ir_exit(dev); + + /* Release I2C buses */ + cx231xx_dev_uninit(dev); + + /* delete v4l2 device */ + v4l2_device_unregister(&dev->v4l2_dev); + + usb_put_dev(dev->udev); + + /* Mark device as unused */ + clear_bit(dev->devno, &cx231xx_devused); + + kfree(dev->video_mode.alt_max_pkt_size); + kfree(dev->vbi_mode.alt_max_pkt_size); + kfree(dev->sliced_cc_mode.alt_max_pkt_size); + kfree(dev->ts1_mode.alt_max_pkt_size); + kfree(dev); +} + +/* + * cx231xx_init_dev() + * allocates and inits the device structs, registers i2c bus and v4l device + */ +static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev, + int minor) +{ + int retval = -ENOMEM; + int errCode; + unsigned int maxh, maxw; + + dev->udev = udev; + mutex_init(&dev->lock); + mutex_init(&dev->ctrl_urb_lock); + mutex_init(&dev->gpio_i2c_lock); + mutex_init(&dev->i2c_lock); + + spin_lock_init(&dev->video_mode.slock); + spin_lock_init(&dev->vbi_mode.slock); + spin_lock_init(&dev->sliced_cc_mode.slock); + + init_waitqueue_head(&dev->open); + init_waitqueue_head(&dev->wait_frame); + init_waitqueue_head(&dev->wait_stream); + + dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg; + dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg; + dev->cx231xx_send_usb_command = cx231xx_send_usb_command; + dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read; + dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write; + + /* Query cx231xx to find what pcb config it is related to */ + initialize_cx231xx(dev); + + /*To workaround error number=-71 on EP0 for VideoGrabber, + need set alt here.*/ + if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER || + dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) { + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3); + cx231xx_set_alt_setting(dev, INDEX_VANC, 1); + } + /* Cx231xx pre card setup */ + cx231xx_pre_card_setup(dev); + + errCode = cx231xx_config(dev); + if (errCode) { + cx231xx_errdev("error configuring device\n"); + return -ENOMEM; + } + + /* set default norm */ + dev->norm = dev->board.norm; + + /* register i2c bus */ + errCode = cx231xx_dev_init(dev); + if (errCode < 0) { + cx231xx_dev_uninit(dev); + cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Do board specific init */ + cx231xx_card_setup(dev); + + /* configure the device */ + cx231xx_config_i2c(dev); + + maxw = norm_maxw(dev); + maxh = norm_maxh(dev); + + /* set default image size */ + dev->width = maxw; + dev->height = maxh; + dev->interlaced = 0; + dev->video_input = 0; + + errCode = cx231xx_config(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->video_mode.vidq.active); + INIT_LIST_HEAD(&dev->video_mode.vidq.queued); + + /* init vbi dma queues */ + INIT_LIST_HEAD(&dev->vbi_mode.vidq.active); + INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued); + + /* Reset other chips required if they are tied up with GPIO pins */ + cx231xx_add_into_devlist(dev); + + if (dev->board.has_417) { + printk(KERN_INFO "attach 417 %d\n", dev->model); + if (cx231xx_417_register(dev) < 0) { + printk(KERN_ERR + "%s() Failed to register 417 on VID_B\n", + __func__); + } + } + + retval = cx231xx_register_analog_devices(dev); + if (retval < 0) { + cx231xx_release_resources(dev); + return retval; + } + + cx231xx_ir_init(dev); + + cx231xx_init_extension(dev); + + return 0; +} + +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct cx231xx *dev = container_of(work, + struct cx231xx, request_module_wk); + + if (dev->has_alsa_audio) + request_module("cx231xx-alsa"); + + if (dev->board.has_dvb) + request_module("cx231xx-dvb"); + +} + +static void request_modules(struct cx231xx *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} + +static void flush_request_modules(struct cx231xx *dev) +{ + flush_work_sync(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#define flush_request_modules(dev) +#endif /* CONFIG_MODULES */ + +/* + * cx231xx_usb_probe() + * checks for supported devices + */ +static int cx231xx_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_interface *uif; + struct cx231xx *dev = NULL; + int retval = -ENODEV; + int nr = 0, ifnum; + int i, isoc_pipe = 0; + char *speed; + struct usb_interface_assoc_descriptor *assoc_desc; + + udev = usb_get_dev(interface_to_usbdev(interface)); + ifnum = interface->altsetting[0].desc.bInterfaceNumber; + + /* + * Interface number 0 - IR interface (handled by mceusb driver) + * Interface number 1 - AV interface (handled by this driver) + */ + if (ifnum != 1) + return -ENODEV; + + /* Check to see next free device and mark as used */ + do { + nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS); + if (nr >= CX231XX_MAXBOARDS) { + /* No free device slots */ + cx231xx_err(DRIVER_NAME ": Supports only %i devices.\n", + CX231XX_MAXBOARDS); + return -ENOMEM; + } + } while (test_and_set_bit(nr, &cx231xx_devused)); + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + cx231xx_err(DRIVER_NAME ": out of memory!\n"); + clear_bit(nr, &cx231xx_devused); + return -ENOMEM; + } + + snprintf(dev->name, 29, "cx231xx #%d", nr); + dev->devno = nr; + dev->model = id->driver_info; + dev->video_mode.alt = -1; + + dev->interface_count++; + /* reset gpio dir and value */ + dev->gpio_dir = 0; + dev->gpio_val = 0; + dev->xc_fw_load_done = 0; + dev->has_alsa_audio = 1; + dev->power_mode = -1; + atomic_set(&dev->devlist_count, 0); + + /* 0 - vbi ; 1 -sliced cc mode */ + dev->vbi_or_sliced_cc_mode = 0; + + /* get maximum no.of IAD interfaces */ + assoc_desc = udev->actconfig->intf_assoc[0]; + dev->max_iad_interface_count = assoc_desc->bInterfaceCount; + + /* init CIR module TBD */ + + /*mode_tv: digital=1 or analog=0*/ + dev->mode_tv = 0; + + dev->USE_ISO = transfer_mode; + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + cx231xx_info("New device %s %s @ %s Mbps " + "(%04x:%04x) with %d interfaces\n", + udev->manufacturer ? udev->manufacturer : "", + udev->product ? udev->product : "", + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + dev->max_iad_interface_count); + + /* increment interface count */ + dev->interface_count++; + + /* get device number */ + nr = dev->devno; + + assoc_desc = udev->actconfig->intf_assoc[0]; + if (assoc_desc->bFirstInterface != ifnum) { + cx231xx_err(DRIVER_NAME ": Not found " + "matching IAD interface\n"); + clear_bit(dev->devno, &cx231xx_devused); + kfree(dev); + dev = NULL; + return -ENODEV; + } + + cx231xx_info("registering interface %d\n", ifnum); + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* + * AV device initialization - only done at the last interface + */ + + /* Create v4l2 device */ + retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (retval) { + cx231xx_errdev("v4l2_device_register failed\n"); + clear_bit(dev->devno, &cx231xx_devused); + kfree(dev); + dev = NULL; + return -EIO; + } + /* allocate device struct */ + retval = cx231xx_init_dev(dev, udev, nr); + if (retval) { + clear_bit(dev->devno, &cx231xx_devused); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + dev = NULL; + usb_set_intfdata(interface, NULL); + + return retval; + } + + /* compute alternate max packet sizes for video */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info.video_index + 1]; + + dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0]. + endpoint[isoc_pipe].desc.bEndpointAddress); + + dev->video_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->video_mode.end_point_addr, + dev->video_mode.num_alt); + dev->video_mode.alt_max_pkt_size = + kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL); + + if (dev->video_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + clear_bit(dev->devno, &cx231xx_devused); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + dev = NULL; + return -ENOMEM; + } + + for (i = 0; i < dev->video_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->video_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->video_mode.alt_max_pkt_size[i]); + } + + /* compute alternate max packet sizes for vbi */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + vanc_index + 1]; + + dev->vbi_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + dev->vbi_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->vbi_mode.end_point_addr, + dev->vbi_mode.num_alt); + dev->vbi_mode.alt_max_pkt_size = + kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL); + + if (dev->vbi_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + clear_bit(dev->devno, &cx231xx_devused); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + dev = NULL; + return -ENOMEM; + } + + for (i = 0; i < dev->vbi_mode.num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->vbi_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->vbi_mode.alt_max_pkt_size[i]); + } + + /* compute alternate max packet sizes for sliced CC */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + hanc_index + 1]; + + dev->sliced_cc_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + dev->sliced_cc_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->sliced_cc_mode.end_point_addr, + dev->sliced_cc_mode.num_alt); + dev->sliced_cc_mode.alt_max_pkt_size = + kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL); + + if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + clear_bit(dev->devno, &cx231xx_devused); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + dev = NULL; + return -ENOMEM; + } + + for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->sliced_cc_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->sliced_cc_mode.alt_max_pkt_size[i]); + } + + if (dev->current_pcb_config.ts1_source != 0xff) { + /* compute alternate max packet sizes for TS1 */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0]. + interface_info. + ts1_index + 1]; + + dev->ts1_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe]. + desc.bEndpointAddress); + + dev->ts1_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->ts1_mode.end_point_addr, + dev->ts1_mode.num_alt); + dev->ts1_mode.alt_max_pkt_size = + kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL); + + if (dev->ts1_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + clear_bit(dev->devno, &cx231xx_devused); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + dev = NULL; + return -ENOMEM; + } + + for (i = 0; i < dev->ts1_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i]. + endpoint[isoc_pipe].desc. + wMaxPacketSize); + dev->ts1_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->ts1_mode.alt_max_pkt_size[i]); + } + } + + if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) { + cx231xx_enable_OSC(dev); + cx231xx_reset_out(dev); + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3); + } + + if (dev->model == CX231XX_BOARD_CNXT_RDE_253S) + cx231xx_sleep_s5h1432(dev); + + /* load other modules required */ + request_modules(dev); + + return 0; +} + +/* + * cx231xx_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void cx231xx_usb_disconnect(struct usb_interface *interface) +{ + struct cx231xx *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + if (!dev->udev) + return; + + dev->state |= DEV_DISCONNECTED; + + flush_request_modules(dev); + + /* wait until all current v4l2 io is finished then deallocate + resources */ + mutex_lock(&dev->lock); + + wake_up_interruptible_all(&dev->open); + + if (dev->users) { + cx231xx_warn + ("device %s is open! Deregistration and memory " + "deallocation are deferred on close.\n", + video_device_node_name(dev->vdev)); + + /* Even having users, it is safe to remove the RC i2c driver */ + cx231xx_ir_exit(dev); + + if (dev->USE_ISO) + cx231xx_uninit_isoc(dev); + else + cx231xx_uninit_bulk(dev); + wake_up_interruptible(&dev->wait_frame); + wake_up_interruptible(&dev->wait_stream); + } else { + } + + cx231xx_close_extension(dev); + + mutex_unlock(&dev->lock); + + if (!dev->users) + cx231xx_release_resources(dev); +} + +static struct usb_driver cx231xx_usb_driver = { + .name = "cx231xx", + .probe = cx231xx_usb_probe, + .disconnect = cx231xx_usb_disconnect, + .id_table = cx231xx_id_table, +}; + +module_usb_driver(cx231xx_usb_driver); diff --git a/drivers/media/usb/cx231xx/cx231xx-conf-reg.h b/drivers/media/usb/cx231xx/cx231xx-conf-reg.h new file mode 100644 index 000000000000..25593f212abf --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-conf-reg.h @@ -0,0 +1,495 @@ +/* + cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB + video capture devices + + Copyright (C) 2008 + + This program is free software; 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 _POLARIS_REG_H_ +#define _POLARIS_REG_H_ + +#define BOARD_CFG_STAT 0x0 +#define TS_MODE_REG 0x4 +#define TS1_CFG_REG 0x8 +#define TS1_LENGTH_REG 0xc +#define TS2_CFG_REG 0x10 +#define TS2_LENGTH_REG 0x14 +#define EP_MODE_SET 0x18 +#define CIR_PWR_PTN1 0x1c +#define CIR_PWR_PTN2 0x20 +#define CIR_PWR_PTN3 0x24 +#define CIR_PWR_MASK0 0x28 +#define CIR_PWR_MASK1 0x2c +#define CIR_PWR_MASK2 0x30 +#define CIR_GAIN 0x34 +#define CIR_CAR_REG 0x38 +#define CIR_OT_CFG1 0x40 +#define CIR_OT_CFG2 0x44 +#define GBULK_BIT_EN 0x68 +#define PWR_CTL_EN 0x74 + +/* Polaris Endpoints capture mask for register EP_MODE_SET */ +#define ENABLE_EP1 0x01 /* Bit[0]=1 */ +#define ENABLE_EP2 0x02 /* Bit[1]=1 */ +#define ENABLE_EP3 0x04 /* Bit[2]=1 */ +#define ENABLE_EP4 0x08 /* Bit[3]=1 */ +#define ENABLE_EP5 0x10 /* Bit[4]=1 */ +#define ENABLE_EP6 0x20 /* Bit[5]=1 */ + +/* Bit definition for register PWR_CTL_EN */ +#define PWR_MODE_MASK 0x17f +#define PWR_AV_EN 0x08 /* bit3 */ +#define PWR_ISO_EN 0x40 /* bit6 */ +#define PWR_AV_MODE 0x30 /* bit4,5 */ +#define PWR_TUNER_EN 0x04 /* bit2 */ +#define PWR_DEMOD_EN 0x02 /* bit1 */ +#define I2C_DEMOD_EN 0x01 /* bit0 */ +#define PWR_RESETOUT_EN 0x100 /* bit8 */ + +enum AV_MODE{ + POLARIS_AVMODE_DEFAULT = 0, + POLARIS_AVMODE_DIGITAL = 0x10, + POLARIS_AVMODE_ANALOGT_TV = 0x20, + POLARIS_AVMODE_ENXTERNAL_AV = 0x30, + +}; + +/* Colibri Registers */ + +#define SINGLE_ENDED 0x0 +#define LOW_IF 0x4 +#define EU_IF 0x9 +#define US_IF 0xa + +#define SUP_BLK_TUNE1 0x00 +#define SUP_BLK_TUNE2 0x01 +#define SUP_BLK_TUNE3 0x02 +#define SUP_BLK_XTAL 0x03 +#define SUP_BLK_PLL1 0x04 +#define SUP_BLK_PLL2 0x05 +#define SUP_BLK_PLL3 0x06 +#define SUP_BLK_REF 0x07 +#define SUP_BLK_PWRDN 0x08 +#define SUP_BLK_TESTPAD 0x09 +#define ADC_COM_INT5_STAB_REF 0x0a +#define ADC_COM_QUANT 0x0b +#define ADC_COM_BIAS1 0x0c +#define ADC_COM_BIAS2 0x0d +#define ADC_COM_BIAS3 0x0e +#define TESTBUS_CTRL 0x12 + +#define FLD_PWRDN_TUNING_BIAS 0x10 +#define FLD_PWRDN_ENABLE_PLL 0x08 +#define FLD_PWRDN_PD_BANDGAP 0x04 +#define FLD_PWRDN_PD_BIAS 0x02 +#define FLD_PWRDN_PD_TUNECK 0x01 + + +#define ADC_STATUS_CH1 0x20 +#define ADC_STATUS_CH2 0x40 +#define ADC_STATUS_CH3 0x60 + +#define ADC_STATUS2_CH1 0x21 +#define ADC_STATUS2_CH2 0x41 +#define ADC_STATUS2_CH3 0x61 + +#define ADC_CAL_ATEST_CH1 0x22 +#define ADC_CAL_ATEST_CH2 0x42 +#define ADC_CAL_ATEST_CH3 0x62 + +#define ADC_PWRDN_CLAMP_CH1 0x23 +#define ADC_PWRDN_CLAMP_CH2 0x43 +#define ADC_PWRDN_CLAMP_CH3 0x63 + +#define ADC_CTRL_DAC23_CH1 0x24 +#define ADC_CTRL_DAC23_CH2 0x44 +#define ADC_CTRL_DAC23_CH3 0x64 + +#define ADC_CTRL_DAC1_CH1 0x25 +#define ADC_CTRL_DAC1_CH2 0x45 +#define ADC_CTRL_DAC1_CH3 0x65 + +#define ADC_DCSERVO_DEM_CH1 0x26 +#define ADC_DCSERVO_DEM_CH2 0x46 +#define ADC_DCSERVO_DEM_CH3 0x66 + +#define ADC_FB_FRCRST_CH1 0x27 +#define ADC_FB_FRCRST_CH2 0x47 +#define ADC_FB_FRCRST_CH3 0x67 + +#define ADC_INPUT_CH1 0x28 +#define ADC_INPUT_CH2 0x48 +#define ADC_INPUT_CH3 0x68 +#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */ + +#define ADC_NTF_PRECLMP_EN_CH1 0x29 +#define ADC_NTF_PRECLMP_EN_CH2 0x49 +#define ADC_NTF_PRECLMP_EN_CH3 0x69 + +#define ADC_QGAIN_RES_TRM_CH1 0x2a +#define ADC_QGAIN_RES_TRM_CH2 0x4a +#define ADC_QGAIN_RES_TRM_CH3 0x6a + +#define ADC_SOC_PRECLMP_TERM_CH1 0x2b +#define ADC_SOC_PRECLMP_TERM_CH2 0x4b +#define ADC_SOC_PRECLMP_TERM_CH3 0x6b + +#define TESTBUS_CTRL_CH1 0x32 +#define TESTBUS_CTRL_CH2 0x52 +#define TESTBUS_CTRL_CH3 0x72 + +/****************************************************************************** + * DIF registers * + ******************************************************************************/ +#define DIRECT_IF_REVB_BASE 0x00300 + +/*****************************************************************************/ +#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000) +/*****************************************************************************/ +#define FLD_DIF_PLL_LOCK 0x80000000 +/* Reserved [30:29] */ +#define FLD_DIF_PLL_FREE_RUN 0x10000000 +#define FLD_DIF_PLL_FREQ 0x0fffffff + +/*****************************************************************************/ +#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004) +/*****************************************************************************/ +#define FLD_DIF_KD_PD 0xff000000 +/* Reserved [23:20] */ +#define FLD_DIF_KDS_PD 0x000f0000 +#define FLD_DIF_KI_PD 0x0000ff00 +/* Reserved [7:4] */ +#define FLD_DIF_KIS_PD 0x0000000f + +/*****************************************************************************/ +#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008) +/*****************************************************************************/ +#define FLD_DIF_KD_FD 0xff000000 +/* Reserved [23:20] */ +#define FLD_DIF_KDS_FD 0x000f0000 +#define FLD_DIF_KI_FD 0x0000ff00 +#define FLD_DIF_SIG_PROP_SZ 0x000000f0 +#define FLD_DIF_KIS_FD 0x0000000f + +/*****************************************************************************/ +#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c) +/*****************************************************************************/ +#define FLD_DIF_PLL_AGC_REF 0xfff00000 +#define FLD_DIF_PLL_AGC_KI 0x000f0000 +/* Reserved [15] */ +#define FLD_DIF_FREQ_LIMIT 0x00007000 +#define FLD_DIF_K_FD 0x00000f00 +#define FLD_DIF_DOWNSMPL_FD 0x000000ff + +/*****************************************************************************/ +#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_PLL_AGC_EN 0x00008000 +/* Reserved [14:12] */ +#define FLD_DIF_PLL_MAN_GAIN 0x00000fff + +/*****************************************************************************/ +#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014) +/*****************************************************************************/ +#define FLD_DIF_K_AGC_RF 0xf0000000 +#define FLD_DIF_K_AGC_IF 0x0f000000 +#define FLD_DIF_K_AGC_INT 0x00f00000 +/* Reserved [19:12] */ +#define FLD_DIF_IF_REF 0x00000fff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018) +/*****************************************************************************/ +#define FLD_DIF_IF_MAX 0xff000000 +#define FLD_DIF_IF_MIN 0x00ff0000 +#define FLD_DIF_IF_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c) +/*****************************************************************************/ +#define FLD_DIF_INT_MAX 0xff000000 +#define FLD_DIF_INT_MIN 0x00ff0000 +#define FLD_DIF_INT_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020) +/*****************************************************************************/ +#define FLD_DIF_RF_MAX 0xff000000 +#define FLD_DIF_RF_MIN 0x00ff0000 +#define FLD_DIF_RF_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024) +/*****************************************************************************/ +#define FLD_DIF_IF_AGC_IN 0xffff0000 +#define FLD_DIF_INT_AGC_IN 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_RF_AGC_IN 0x0000ffff + +/*****************************************************************************/ +#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c) +/*****************************************************************************/ +#define FLD_DIF_AFD 0xc0000000 +#define FLD_DIF_K_VID_AGC 0x30000000 +#define FLD_DIF_LINE_LENGTH 0x0fff0000 +#define FLD_DIF_AGC_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030) +/*****************************************************************************/ +#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000 +/* Reserved [30:30] */ +#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000 +/* Reserved [23:17] */ +#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000 +#define FLD_DIF_VID_MAN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034) +/*****************************************************************************/ +#define FLD_DIF_LPF_FREQ 0xc0000000 +#define FLD_DIF_AV_PHASE_INC 0x3f000000 +#define FLD_DIF_AUDIO_FREQ 0x00ffffff + +/*****************************************************************************/ +#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038) +/*****************************************************************************/ +/* Reserved [31:24] */ +#define FLD_DIF_IIR23_R2 0x00ff0000 +#define FLD_DIF_IIR23_R1 0x0000ff00 +#define FLD_DIF_IIR1_R1 0x000000ff + +/*****************************************************************************/ +#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c) +/*****************************************************************************/ +#define FLD_DIF_DIF_BYPASS 0x80000000 +#define FLD_DIF_FM_NYQ_GAIN 0x40000000 +#define FLD_DIF_RF_AGC_ENA 0x20000000 +#define FLD_DIF_INT_AGC_ENA 0x10000000 +#define FLD_DIF_IF_AGC_ENA 0x08000000 +#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000 +#define FLD_DIF_VIDEO_AGC_ENA 0x02000000 +#define FLD_DIF_RF_AGC_INV 0x01000000 +#define FLD_DIF_INT_AGC_INV 0x00800000 +#define FLD_DIF_IF_AGC_INV 0x00400000 +#define FLD_DIF_SPEC_INV 0x00200000 +#define FLD_DIF_AUD_FULL_BW 0x00100000 +#define FLD_DIF_AUD_SRC_SEL 0x00080000 +/* Reserved [18] */ +#define FLD_DIF_IF_FREQ 0x00030000 +/* Reserved [15:14] */ +#define FLD_DIF_TIP_OFFSET 0x00003f00 +/* Reserved [7:5] */ +#define FLD_DIF_DITHER_ENA 0x00000010 +/* Reserved [3:1] */ +#define FLD_DIF_RF_IF_LOCK 0x00000001 + +/*****************************************************************************/ +#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_PHASE_INC 0x1fffffff + +/*****************************************************************************/ +#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_SRC_KI 0x0000ff00 +#define FLD_DIF_SRC_KD 0x000000ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048) +/*****************************************************************************/ +/* Reserved [31:19] */ +#define FLD_DIF_BPF_COEFF_0 0x00070000 +/* Reserved [15:4] */ +#define FLD_DIF_BPF_COEFF_1 0x0000000f + +/*****************************************************************************/ +#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c) +/*****************************************************************************/ +/* Reserved [31:22] */ +#define FLD_DIF_BPF_COEFF_2 0x003f0000 +/* Reserved [15:7] */ +#define FLD_DIF_BPF_COEFF_3 0x0000007f + +/*****************************************************************************/ +#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050) +/*****************************************************************************/ +/* Reserved [31:24] */ +#define FLD_DIF_BPF_COEFF_4 0x00ff0000 +/* Reserved [15:8] */ +#define FLD_DIF_BPF_COEFF_5 0x000000ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054) +/*****************************************************************************/ +/* Reserved [31:25] */ +#define FLD_DIF_BPF_COEFF_6 0x01ff0000 +/* Reserved [15:9] */ +#define FLD_DIF_BPF_COEFF_7 0x000001ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058) +/*****************************************************************************/ +/* Reserved [31:26] */ +#define FLD_DIF_BPF_COEFF_8 0x03ff0000 +/* Reserved [15:10] */ +#define FLD_DIF_BPF_COEFF_9 0x000003ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c) +/*****************************************************************************/ +/* Reserved [31:27] */ +#define FLD_DIF_BPF_COEFF_10 0x07ff0000 +/* Reserved [15:11] */ +#define FLD_DIF_BPF_COEFF_11 0x000007ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060) +/*****************************************************************************/ +/* Reserved [31:27] */ +#define FLD_DIF_BPF_COEFF_12 0x07ff0000 +/* Reserved [15:12] */ +#define FLD_DIF_BPF_COEFF_13 0x00000fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064) +/*****************************************************************************/ +/* Reserved [31:28] */ +#define FLD_DIF_BPF_COEFF_14 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_DIF_BPF_COEFF_15 0x00000fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_16 0x1fff0000 +/* Reserved [15:13] */ +#define FLD_DIF_BPF_COEFF_17 0x00001fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_18 0x1fff0000 +/* Reserved [15:13] */ +#define FLD_DIF_BPF_COEFF_19 0x00001fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_20 0x1fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_21 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_22 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_23 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_24 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_25 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_26 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_27 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_28 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_29 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_30 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_31 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_32 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_33 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_34 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_35 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_36 0x3fff0000 +/* Reserved [15:0] */ + +/*****************************************************************************/ +#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094) +/*****************************************************************************/ +/* Reserved [31:20] */ +#define FLD_DIF_RPT_VARIANCE 0x000fffff + +/*****************************************************************************/ +#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098) +/*****************************************************************************/ +/* Reserved [31:8] */ +#define FLD_DIF_DIF_SOFT_RST 0x00000080 +#define FLD_DIF_DIF_REG_RST_MSK 0x00000040 +#define FLD_DIF_AGC_RST_MSK 0x00000020 +#define FLD_DIF_CMP_RST_MSK 0x00000010 +#define FLD_DIF_AVS_RST_MSK 0x00000008 +#define FLD_DIF_NYQ_RST_MSK 0x00000004 +#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002 +#define FLD_DIF_PLL_RST_MSK 0x00000001 + +/*****************************************************************************/ +#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c) +/*****************************************************************************/ +/* Reserved [31:25] */ +#define FLD_DIF_CTL_IP 0x01ffffff + +#endif diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c new file mode 100644 index 000000000000..05358d486135 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -0,0 +1,1736 @@ +/* + cx231xx-core.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 + Based on em28xx driver + + This program is free software; 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 "cx231xx.h" +#include "cx231xx-reg.h" + +/* #define ENABLE_DEBUG_ISOC_FRAMES */ + +static unsigned int core_debug; +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); + +#define cx231xx_coredbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int reg_debug; +module_param(reg_debug, int, 0644); +MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); + +static int alt = CX231XX_PINOUT; +module_param(alt, int, 0644); +MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); + +#define cx231xx_isocdbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +/***************************************************************** +* Device control list functions * +******************************************************************/ + +LIST_HEAD(cx231xx_devlist); +static DEFINE_MUTEX(cx231xx_devlist_mutex); + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_remove_from_devlist(struct cx231xx *dev) +{ + if (dev == NULL) + return; + if (dev->udev == NULL) + return; + + if (atomic_read(&dev->devlist_count) > 0) { + mutex_lock(&cx231xx_devlist_mutex); + list_del(&dev->devlist); + atomic_dec(&dev->devlist_count); + mutex_unlock(&cx231xx_devlist_mutex); + } +}; + +void cx231xx_add_into_devlist(struct cx231xx *dev) +{ + mutex_lock(&cx231xx_devlist_mutex); + list_add_tail(&dev->devlist, &cx231xx_devlist); + atomic_inc(&dev->devlist_count); + mutex_unlock(&cx231xx_devlist_mutex); +}; + +static LIST_HEAD(cx231xx_extension_devlist); + +int cx231xx_register_extension(struct cx231xx_ops *ops) +{ + struct cx231xx *dev = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + list_add_tail(&ops->next, &cx231xx_extension_devlist); + list_for_each_entry(dev, &cx231xx_devlist, devlist) + ops->init(dev); + + printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name); + mutex_unlock(&cx231xx_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(cx231xx_register_extension); + +void cx231xx_unregister_extension(struct cx231xx_ops *ops) +{ + struct cx231xx *dev = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + list_for_each_entry(dev, &cx231xx_devlist, devlist) + ops->fini(dev); + + + printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name); + list_del(&ops->next); + mutex_unlock(&cx231xx_devlist_mutex); +} +EXPORT_SYMBOL(cx231xx_unregister_extension); + +void cx231xx_init_extension(struct cx231xx *dev) +{ + struct cx231xx_ops *ops = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + if (!list_empty(&cx231xx_extension_devlist)) { + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + } + mutex_unlock(&cx231xx_devlist_mutex); +} + +void cx231xx_close_extension(struct cx231xx *dev) +{ + struct cx231xx_ops *ops = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + if (!list_empty(&cx231xx_extension_devlist)) { + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + } + mutex_unlock(&cx231xx_devlist_mutex); +} + +/**************************************************************** +* U S B related functions * +*****************************************************************/ +int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data) +{ + int status = 0; + struct cx231xx *dev = i2c_bus->dev; + struct VENDOR_REQUEST_IN ven_req; + + u8 saddr_len = 0; + u8 _i2c_period = 0; + u8 _i2c_nostop = 0; + u8 _i2c_reserve = 0; + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + /* Get the I2C period, nostop and reserve parameters */ + _i2c_period = i2c_bus->i2c_period; + _i2c_nostop = i2c_bus->i2c_nostop; + _i2c_reserve = i2c_bus->i2c_reserve; + + saddr_len = req_data->saddr_len; + + /* Set wValue */ + if (saddr_len == 1) /* need check saddr_len == 0 */ + ven_req.wValue = + req_data-> + dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | + _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; + else + ven_req.wValue = + req_data-> + dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | + _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; + + /* set channel number */ + if (req_data->direction & I2C_M_RD) { + /* channel number, for read,spec required channel_num +4 */ + ven_req.bRequest = i2c_bus->nr + 4; + } else + ven_req.bRequest = i2c_bus->nr; /* channel number, */ + + /* set index value */ + switch (saddr_len) { + case 0: + ven_req.wIndex = 0; /* need check */ + break; + case 1: + ven_req.wIndex = (req_data->saddr_dat & 0xff); + break; + case 2: + ven_req.wIndex = req_data->saddr_dat; + break; + } + + /* set wLength value */ + ven_req.wLength = req_data->buf_size; + + /* set bData value */ + ven_req.bData = 0; + + /* set the direction */ + if (req_data->direction) { + ven_req.direction = USB_DIR_IN; + memset(req_data->p_buffer, 0x00, ven_req.wLength); + } else + ven_req.direction = USB_DIR_OUT; + + /* set the buffer for read / write */ + ven_req.pBuff = req_data->p_buffer; + + + /* call common vendor command request */ + status = cx231xx_send_vendor_cmd(dev, &ven_req); + if (status < 0) { + cx231xx_info + ("UsbInterface::sendCommand, failed with status -%d\n", + status); + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_send_usb_command); + +/* + * Sends/Receives URB control messages, assuring to use a kalloced buffer + * for all operations (dev->urb_buf), to avoid using stacked buffers, as + * they aren't safe for usage with USB, due to DMA restrictions. + * Also implements the debug code for control URB's. + */ +static int __usb_control_msg(struct cx231xx *dev, unsigned int pipe, + __u8 request, __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout) +{ + int rc, i; + + if (reg_debug) { + printk(KERN_DEBUG "%s: (pipe 0x%08x): " + "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", + dev->name, + pipe, + (requesttype & USB_DIR_IN) ? "IN" : "OUT", + requesttype, + request, + value & 0xff, value >> 8, + index & 0xff, index >> 8, + size & 0xff, size >> 8); + if (!(requesttype & USB_DIR_IN)) { + printk(KERN_CONT ">>>"); + for (i = 0; i < size; i++) + printk(KERN_CONT " %02x", + ((unsigned char *)data)[i]); + } + } + + /* Do the real call to usb_control_msg */ + mutex_lock(&dev->ctrl_urb_lock); + if (!(requesttype & USB_DIR_IN) && size) + memcpy(dev->urb_buf, data, size); + rc = usb_control_msg(dev->udev, pipe, request, requesttype, value, + index, dev->urb_buf, size, timeout); + if ((requesttype & USB_DIR_IN) && size) + memcpy(data, dev->urb_buf, size); + mutex_unlock(&dev->ctrl_urb_lock); + + if (reg_debug) { + if (unlikely(rc < 0)) { + printk(KERN_CONT "FAILED!\n"); + return rc; + } + + if ((requesttype & USB_DIR_IN)) { + printk(KERN_CONT "<<<"); + for (i = 0; i < size; i++) + printk(KERN_CONT " %02x", + ((unsigned char *)data)[i]); + } + printk(KERN_CONT "\n"); + } + + return rc; +} + + +/* + * cx231xx_read_ctrl_reg() + * reads data from the usb device specifying bRequest and wValue + */ +int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len) +{ + u8 val = 0; + int ret; + int pipe = usb_rcvctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if (len > URB_MAX_CTRL_SIZE) + return -EINVAL; + + switch (len) { + case 1: + val = ENABLE_ONE_BYTE; + break; + case 2: + val = ENABLE_TWE_BYTE; + break; + case 3: + val = ENABLE_THREE_BYTE; + break; + case 4: + val = ENABLE_FOUR_BYTE; + break; + default: + val = 0xFF; /* invalid option */ + } + + if (val == 0xFF) + return -EINVAL; + + ret = __usb_control_msg(dev, pipe, req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, reg, buf, len, HZ); + return ret; +} + +int cx231xx_send_vendor_cmd(struct cx231xx *dev, + struct VENDOR_REQUEST_IN *ven_req) +{ + int ret; + int pipe = 0; + int unsend_size = 0; + u8 *pdata; + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if ((ven_req->wLength > URB_MAX_CTRL_SIZE)) + return -EINVAL; + + if (ven_req->direction) + pipe = usb_rcvctrlpipe(dev->udev, 0); + else + pipe = usb_sndctrlpipe(dev->udev, 0); + + /* + * If the cx23102 read more than 4 bytes with i2c bus, + * need chop to 4 byte per request + */ + if ((ven_req->wLength > 4) && ((ven_req->bRequest == 0x4) || + (ven_req->bRequest == 0x5) || + (ven_req->bRequest == 0x6))) { + unsend_size = 0; + pdata = ven_req->pBuff; + + + unsend_size = ven_req->wLength; + + /* the first package */ + ven_req->wValue = ven_req->wValue & 0xFFFB; + ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x2; + ret = __usb_control_msg(dev, pipe, ven_req->bRequest, + ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->wValue, ven_req->wIndex, pdata, + 0x0004, HZ); + unsend_size = unsend_size - 4; + + /* the middle package */ + ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x42; + while (unsend_size - 4 > 0) { + pdata = pdata + 4; + ret = __usb_control_msg(dev, pipe, + ven_req->bRequest, + ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->wValue, ven_req->wIndex, pdata, + 0x0004, HZ); + unsend_size = unsend_size - 4; + } + + /* the last package */ + ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x40; + pdata = pdata + 4; + ret = __usb_control_msg(dev, pipe, ven_req->bRequest, + ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->wValue, ven_req->wIndex, pdata, + unsend_size, HZ); + } else { + ret = __usb_control_msg(dev, pipe, ven_req->bRequest, + ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->wValue, ven_req->wIndex, + ven_req->pBuff, ven_req->wLength, HZ); + } + + return ret; +} + +/* + * cx231xx_write_ctrl_reg() + * sends data to the usb device, specifying bRequest + */ +int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf, + int len) +{ + u8 val = 0; + int ret; + int pipe = usb_sndctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) + return -EINVAL; + + switch (len) { + case 1: + val = ENABLE_ONE_BYTE; + break; + case 2: + val = ENABLE_TWE_BYTE; + break; + case 3: + val = ENABLE_THREE_BYTE; + break; + case 4: + val = ENABLE_FOUR_BYTE; + break; + default: + val = 0xFF; /* invalid option */ + } + + if (val == 0xFF) + return -EINVAL; + + if (reg_debug) { + int byte; + + cx231xx_isocdbg("(pipe 0x%08x): " + "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, val, reg & 0xff, + reg >> 8, len & 0xff, len >> 8); + + for (byte = 0; byte < len; byte++) + cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]); + cx231xx_isocdbg("\n"); + } + + ret = __usb_control_msg(dev, pipe, req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, reg, buf, len, HZ); + + return ret; +} + +/**************************************************************** +* USB Alternate Setting functions * +*****************************************************************/ + +int cx231xx_set_video_alternate(struct cx231xx *dev) +{ + int errCode, prev_alt = dev->video_mode.alt; + unsigned int min_pkt_size = dev->width * 2 + 4; + u32 usb_interface_index = 0; + + /* When image size is bigger than a certain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (dev->width * 2 * dev->height > 720 * 240 * 2) + min_pkt_size *= 2; + + if (dev->width > 360) { + /* resolutions: 720,704,640 */ + dev->video_mode.alt = 3; + } else if (dev->width > 180) { + /* resolutions: 360,352,320,240 */ + dev->video_mode.alt = 2; + } else if (dev->width > 0) { + /* resolutions: 180,176,160,128,88 */ + dev->video_mode.alt = 1; + } else { + /* Change to alt0 BULK to release USB bandwidth */ + dev->video_mode.alt = 0; + } + + if (dev->USE_ISO == 0) + dev->video_mode.alt = 0; + + cx231xx_coredbg("dev->video_mode.alt= %d\n", dev->video_mode.alt); + + /* Get the correct video interface Index */ + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + video_index + 1; + + if (dev->video_mode.alt != prev_alt) { + cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->video_mode.alt); + + if (dev->video_mode.alt_max_pkt_size != NULL) + dev->video_mode.max_pkt_size = + dev->video_mode.alt_max_pkt_size[dev->video_mode.alt]; + cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->video_mode.alt, + dev->video_mode.max_pkt_size); + errCode = + usb_set_interface(dev->udev, usb_interface_index, + dev->video_mode.alt); + if (errCode < 0) { + cx231xx_errdev + ("cannot change alt number to %d (error=%i)\n", + dev->video_mode.alt, errCode); + return errCode; + } + } + return 0; +} + +int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt) +{ + int status = 0; + u32 usb_interface_index = 0; + u32 max_pkt_size = 0; + + switch (index) { + case INDEX_TS1: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + ts1_index + 1; + dev->ts1_mode.alt = alt; + if (dev->ts1_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->ts1_mode.max_pkt_size = + dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt]; + break; + case INDEX_TS2: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + ts2_index + 1; + break; + case INDEX_AUDIO: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + audio_index + 1; + dev->adev.alt = alt; + if (dev->adev.alt_max_pkt_size != NULL) + max_pkt_size = dev->adev.max_pkt_size = + dev->adev.alt_max_pkt_size[dev->adev.alt]; + break; + case INDEX_VIDEO: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + video_index + 1; + dev->video_mode.alt = alt; + if (dev->video_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->video_mode.max_pkt_size = + dev->video_mode.alt_max_pkt_size[dev->video_mode. + alt]; + break; + case INDEX_VANC: + if (dev->board.no_alt_vanc) + return 0; + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + vanc_index + 1; + dev->vbi_mode.alt = alt; + if (dev->vbi_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->vbi_mode.max_pkt_size = + dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt]; + break; + case INDEX_HANC: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + hanc_index + 1; + dev->sliced_cc_mode.alt = alt; + if (dev->sliced_cc_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->sliced_cc_mode.max_pkt_size = + dev->sliced_cc_mode.alt_max_pkt_size[dev-> + sliced_cc_mode. + alt]; + break; + default: + break; + } + + if (alt > 0 && max_pkt_size == 0) { + cx231xx_errdev + ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n", + usb_interface_index, alt); + /*To workaround error number=-71 on EP0 for videograbber, + need add following codes.*/ + if (dev->board.no_alt_vanc) + return -1; + } + + cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u," + "Interface = %d\n", alt, max_pkt_size, + usb_interface_index); + + if (usb_interface_index > 0) { + status = usb_set_interface(dev->udev, usb_interface_index, alt); + if (status < 0) { + cx231xx_errdev + ("can't change interface %d alt no. to %d (err=%i)\n", + usb_interface_index, alt, status); + return status; + } + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting); + +int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio) +{ + int rc = 0; + + if (!gpio) + return rc; + + /* Send GPIO reset sequences specified at board entry */ + while (gpio->sleep >= 0) { + rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val); + if (rc < 0) + return rc; + + if (gpio->sleep > 0) + msleep(gpio->sleep); + + gpio++; + } + return rc; +} + +int cx231xx_demod_reset(struct cx231xx *dev) +{ + + u8 status = 0; + u8 value[4] = { 0, 0, 0, 0 }; + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + value, 4); + + cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, + value[0], value[1], value[2], value[3]); + + cx231xx_coredbg("Enter cx231xx_demod_reset()\n"); + + value[1] = (u8) 0x3; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(10); + + value[1] = (u8) 0x0; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(10); + + value[1] = (u8) 0x3; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(10); + + + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + value, 4); + + cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, + value[0], value[1], value[2], value[3]); + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_demod_reset); +int is_fw_load(struct cx231xx *dev) +{ + return cx231xx_check_fw(dev); +} +EXPORT_SYMBOL_GPL(is_fw_load); + +int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) +{ + int errCode = 0; + + if (dev->mode == set_mode) + return 0; + + if (set_mode == CX231XX_SUSPEND) { + /* Set the chip in power saving mode */ + dev->mode = set_mode; + } + + /* Resource is locked */ + if (dev->mode != CX231XX_SUSPEND) + return -EINVAL; + + dev->mode = set_mode; + + if (dev->mode == CX231XX_DIGITAL_MODE)/* Set Digital power mode */ { + /* set AGC mode to Digital */ + switch (dev->model) { + case CX231XX_BOARD_CNXT_CARRAERA: + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_SHELBY: + case CX231XX_BOARD_CNXT_RDU_250: + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); + break; + case CX231XX_BOARD_CNXT_RDE_253S: + case CX231XX_BOARD_CNXT_RDU_253S: + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); + break; + case CX231XX_BOARD_HAUPPAUGE_EXETER: + errCode = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_DIGITAL); + break; + default: + break; + } + } else/* Set Analog Power mode */ { + /* set AGC mode to Analog */ + switch (dev->model) { + case CX231XX_BOARD_CNXT_CARRAERA: + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_SHELBY: + case CX231XX_BOARD_CNXT_RDU_250: + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); + break; + case CX231XX_BOARD_CNXT_RDE_253S: + case CX231XX_BOARD_CNXT_RDU_253S: + case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); + break; + default: + break; + } + } + + return errCode ? -EINVAL : 0; +} +EXPORT_SYMBOL_GPL(cx231xx_set_mode); + +int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size) +{ + int errCode = 0; + int actlen, ret = -ENOMEM; + u32 *buffer; + + buffer = kzalloc(4096, GFP_KERNEL); + if (buffer == NULL) { + cx231xx_info("out of mem\n"); + return -ENOMEM; + } + memcpy(&buffer[0], firmware, 4096); + + ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5), + buffer, 4096, &actlen, 2000); + + if (ret) + cx231xx_info("bulk message failed: %d (%d/%d)", ret, + size, actlen); + else { + errCode = actlen != size ? -1 : 0; + } + kfree(buffer); + return errCode; +} + +/***************************************************************** +* URB Streaming functions * +******************************************************************/ + +/* + * IRQ callback, called by URB callback + */ +static void cx231xx_isoc_irq_callback(struct urb *urb) +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + int i; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + cx231xx_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->video_mode.slock); + dev->video_mode.isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->video_mode.slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + cx231xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} +/***************************************************************** +* URB Streaming functions * +******************************************************************/ + +/* + * IRQ callback, called by URB callback + */ +static void cx231xx_bulk_irq_callback(struct urb *urb) +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + cx231xx_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->video_mode.slock); + dev->video_mode.bulk_ctl.bulk_copy(dev, urb); + spin_unlock(&dev->video_mode.slock); + + /* Reset urb buffers */ + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + cx231xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} +/* + * Stop and Deallocate URBs + */ +void cx231xx_uninit_isoc(struct cx231xx *dev) +{ + struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; + struct urb *urb; + int i; + + cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n"); + + dev->video_mode.isoc_ctl.nfields = -1; + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + urb = dev->video_mode.isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->video_mode.isoc_ctl.transfer_buffer[i]) { + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->video_mode.isoc_ctl. + transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->video_mode.isoc_ctl.urb[i] = NULL; + } + dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->video_mode.isoc_ctl.urb); + kfree(dev->video_mode.isoc_ctl.transfer_buffer); + kfree(dma_q->p_left_data); + + dev->video_mode.isoc_ctl.urb = NULL; + dev->video_mode.isoc_ctl.transfer_buffer = NULL; + dev->video_mode.isoc_ctl.num_bufs = 0; + dma_q->p_left_data = NULL; + + if (dev->mode_tv == 0) + cx231xx_capture_start(dev, 0, Raw_Video); + else + cx231xx_capture_start(dev, 0, TS1_serial_mode); + + +} +EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc); + +/* + * Stop and Deallocate URBs + */ +void cx231xx_uninit_bulk(struct cx231xx *dev) +{ + struct urb *urb; + int i; + + cx231xx_isocdbg("cx231xx: called cx231xx_uninit_bulk\n"); + + dev->video_mode.bulk_ctl.nfields = -1; + for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) { + urb = dev->video_mode.bulk_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->video_mode.bulk_ctl.transfer_buffer[i]) { + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->video_mode.isoc_ctl. + transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->video_mode.bulk_ctl.urb[i] = NULL; + } + dev->video_mode.bulk_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->video_mode.bulk_ctl.urb); + kfree(dev->video_mode.bulk_ctl.transfer_buffer); + + dev->video_mode.bulk_ctl.urb = NULL; + dev->video_mode.bulk_ctl.transfer_buffer = NULL; + dev->video_mode.bulk_ctl.num_bufs = 0; + + if (dev->mode_tv == 0) + cx231xx_capture_start(dev, 0, Raw_Video); + else + cx231xx_capture_start(dev, 0, TS1_serial_mode); + + +} +EXPORT_SYMBOL_GPL(cx231xx_uninit_bulk); + +/* + * Allocate URBs and start IRQ + */ +int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, struct urb *urb)) +{ + struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + /* De-allocates all pending stuff */ + cx231xx_uninit_isoc(dev); + + dma_q->p_left_data = kzalloc(4096, GFP_KERNEL); + if (dma_q->p_left_data == NULL) { + cx231xx_info("out of mem\n"); + return -ENOMEM; + } + + + + dev->video_mode.isoc_ctl.isoc_copy = isoc_copy; + dev->video_mode.isoc_ctl.num_bufs = num_bufs; + dma_q->pos = 0; + dma_q->is_partial_line = 0; + dma_q->last_sav = 0; + dma_q->current_field = -1; + dma_q->field1_done = 0; + dma_q->lines_per_field = dev->height / 2; + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; + dma_q->mpeg_buffer_done = 0; + dma_q->left_data_count = 0; + dma_q->mpeg_buffer_completed = 0; + dma_q->add_ps_package_head = CX231XX_NEED_ADD_PS_PACKAGE_HEAD; + dma_q->ps_head[0] = 0x00; + dma_q->ps_head[1] = 0x00; + dma_q->ps_head[2] = 0x01; + dma_q->ps_head[3] = 0xBA; + for (i = 0; i < 8; i++) + dma_q->partial_buf[i] = 0; + + dev->video_mode.isoc_ctl.urb = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.isoc_ctl.urb) { + cx231xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->video_mode.isoc_ctl.transfer_buffer = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.isoc_ctl.transfer_buffer) { + cx231xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->video_mode.isoc_ctl.urb); + return -ENOMEM; + } + + dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size; + dev->video_mode.isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size; + + if (dev->mode_tv == 1) + dev->video_mode.end_point_addr = 0x81; + else + dev->video_mode.end_point_addr = 0x84; + + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i); + cx231xx_uninit_isoc(dev); + return -ENOMEM; + } + dev->video_mode.isoc_ctl.urb[i] = urb; + + dev->video_mode.isoc_ctl.transfer_buffer[i] = + usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, + &urb->transfer_dma); + if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) { + cx231xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + cx231xx_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size); + + pipe = + usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr); + + usb_fill_int_urb(urb, dev->udev, pipe, + dev->video_mode.isoc_ctl.transfer_buffer[i], + sb_size, cx231xx_isoc_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->video_mode.isoc_ctl.max_pkt_size; + k += dev->video_mode.isoc_ctl.max_pkt_size; + } + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i], + GFP_ATOMIC); + if (rc) { + cx231xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + cx231xx_uninit_isoc(dev); + return rc; + } + } + + if (dev->mode_tv == 0) + cx231xx_capture_start(dev, 1, Raw_Video); + else + cx231xx_capture_start(dev, 1, TS1_serial_mode); + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_init_isoc); + +/* + * Allocate URBs and start IRQ + */ +int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*bulk_copy) (struct cx231xx *dev, struct urb *urb)) +{ + struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int rc; + + dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; + + cx231xx_coredbg("Setting Video mux to %d\n", dev->video_input); + + video_mux(dev, dev->video_input); + + /* De-allocates all pending stuff */ + cx231xx_uninit_bulk(dev); + + dev->video_mode.bulk_ctl.bulk_copy = bulk_copy; + dev->video_mode.bulk_ctl.num_bufs = num_bufs; + dma_q->pos = 0; + dma_q->is_partial_line = 0; + dma_q->last_sav = 0; + dma_q->current_field = -1; + dma_q->field1_done = 0; + dma_q->lines_per_field = dev->height / 2; + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; + dma_q->mpeg_buffer_done = 0; + dma_q->left_data_count = 0; + dma_q->mpeg_buffer_completed = 0; + dma_q->ps_head[0] = 0x00; + dma_q->ps_head[1] = 0x00; + dma_q->ps_head[2] = 0x01; + dma_q->ps_head[3] = 0xBA; + for (i = 0; i < 8; i++) + dma_q->partial_buf[i] = 0; + + dev->video_mode.bulk_ctl.urb = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.bulk_ctl.urb) { + cx231xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->video_mode.bulk_ctl.transfer_buffer = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.bulk_ctl.transfer_buffer) { + cx231xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->video_mode.bulk_ctl.urb); + return -ENOMEM; + } + + dev->video_mode.bulk_ctl.max_pkt_size = max_pkt_size; + dev->video_mode.bulk_ctl.buf = NULL; + + sb_size = max_packets * dev->video_mode.bulk_ctl.max_pkt_size; + + if (dev->mode_tv == 1) + dev->video_mode.end_point_addr = 0x81; + else + dev->video_mode.end_point_addr = 0x84; + + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i); + cx231xx_uninit_bulk(dev); + return -ENOMEM; + } + dev->video_mode.bulk_ctl.urb[i] = urb; + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + + dev->video_mode.bulk_ctl.transfer_buffer[i] = + usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, + &urb->transfer_dma); + if (!dev->video_mode.bulk_ctl.transfer_buffer[i]) { + cx231xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + cx231xx_uninit_bulk(dev); + return -ENOMEM; + } + memset(dev->video_mode.bulk_ctl.transfer_buffer[i], 0, sb_size); + + pipe = usb_rcvbulkpipe(dev->udev, + dev->video_mode.end_point_addr); + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->video_mode.bulk_ctl.transfer_buffer[i], + sb_size, cx231xx_bulk_irq_callback, dma_q); + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->video_mode.bulk_ctl.urb[i], + GFP_ATOMIC); + if (rc) { + cx231xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + cx231xx_uninit_bulk(dev); + return rc; + } + } + + if (dev->mode_tv == 0) + cx231xx_capture_start(dev, 1, Raw_Video); + else + cx231xx_capture_start(dev, 1, TS1_serial_mode); + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_init_bulk); +void cx231xx_stop_TS1(struct cx231xx *dev) +{ + u8 val[4] = { 0, 0, 0, 0 }; + + val[0] = 0x00; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); + + val[0] = 0x00; + val[1] = 0x70; + val[2] = 0x04; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); +} +/* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */ +void cx231xx_start_TS1(struct cx231xx *dev) +{ + u8 val[4] = { 0, 0, 0, 0 }; + + val[0] = 0x03; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); + + val[0] = 0x04; + val[1] = 0xA3; + val[2] = 0x3B; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); +} +/* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */ +/***************************************************************** +* Device Init/UnInit functions * +******************************************************************/ +int cx231xx_dev_init(struct cx231xx *dev) +{ + int errCode = 0; + + /* Initialize I2C bus */ + + /* External Master 1 Bus */ + dev->i2c_bus[0].nr = 0; + dev->i2c_bus[0].dev = dev; + dev->i2c_bus[0].i2c_period = I2C_SPEED_100K; /* 100 KHz */ + dev->i2c_bus[0].i2c_nostop = 0; + dev->i2c_bus[0].i2c_reserve = 0; + + /* External Master 2 Bus */ + dev->i2c_bus[1].nr = 1; + dev->i2c_bus[1].dev = dev; + dev->i2c_bus[1].i2c_period = I2C_SPEED_100K; /* 100 KHz */ + dev->i2c_bus[1].i2c_nostop = 0; + dev->i2c_bus[1].i2c_reserve = 0; + + /* Internal Master 3 Bus */ + dev->i2c_bus[2].nr = 2; + dev->i2c_bus[2].dev = dev; + dev->i2c_bus[2].i2c_period = I2C_SPEED_100K; /* 100kHz */ + dev->i2c_bus[2].i2c_nostop = 0; + dev->i2c_bus[2].i2c_reserve = 0; + + /* register I2C buses */ + cx231xx_i2c_register(&dev->i2c_bus[0]); + cx231xx_i2c_register(&dev->i2c_bus[1]); + cx231xx_i2c_register(&dev->i2c_bus[2]); + + /* init hardware */ + /* Note : with out calling set power mode function, + afe can not be set up correctly */ + if (dev->board.external_av) { + errCode = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ENXTERNAL_AV); + if (errCode < 0) { + cx231xx_errdev + ("%s: Failed to set Power - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + } else { + errCode = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ANALOGT_TV); + if (errCode < 0) { + cx231xx_errdev + ("%s: Failed to set Power - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + } + + /* reset the Tuner, if it is a Xceive tuner */ + if ((dev->board.tuner_type == TUNER_XC5000) || + (dev->board.tuner_type == TUNER_XC2028)) + cx231xx_gpio_set(dev, dev->board.tuner_gpio); + + /* initialize Colibri block */ + errCode = cx231xx_afe_init_super_block(dev, 0x23c); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_afe init super block - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + errCode = cx231xx_afe_init_channels(dev); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_afe init channels - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Set DIF in By pass mode */ + errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* I2S block related functions */ + errCode = cx231xx_i2s_blk_initialize(dev); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_i2s block initialize - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* init control pins */ + errCode = cx231xx_init_ctrl_pin_status(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* set AGC mode to Analog */ + switch (dev->model) { + case CX231XX_BOARD_CNXT_CARRAERA: + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_SHELBY: + case CX231XX_BOARD_CNXT_RDU_250: + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); + break; + case CX231XX_BOARD_CNXT_RDE_253S: + case CX231XX_BOARD_CNXT_RDU_253S: + case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: + case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); + break; + default: + break; + } + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* set all alternate settings to zero initially */ + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + if (dev->board.has_dvb) + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + + /* set the I2C master port to 3 on channel 1 */ + errCode = cx231xx_enable_i2c_port_3(dev, true); + + return errCode; +} +EXPORT_SYMBOL_GPL(cx231xx_dev_init); + +void cx231xx_dev_uninit(struct cx231xx *dev) +{ + /* Un Initialize I2C bus */ + cx231xx_i2c_unregister(&dev->i2c_bus[2]); + cx231xx_i2c_unregister(&dev->i2c_bus[1]); + cx231xx_i2c_unregister(&dev->i2c_bus[0]); +} +EXPORT_SYMBOL_GPL(cx231xx_dev_uninit); + +/***************************************************************** +* G P I O related functions * +******************************************************************/ +int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, + u8 len, u8 request, u8 direction) +{ + int status = 0; + struct VENDOR_REQUEST_IN ven_req; + + /* Set wValue */ + ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff); + + /* set request */ + if (!request) { + if (direction) + ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */ + else + ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */ + } else { + if (direction) + ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */ + else + ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */ + } + + /* set index value */ + ven_req.wIndex = (u16) (gpio_bit & 0xffff); + + /* set wLength value */ + ven_req.wLength = len; + + /* set bData value */ + ven_req.bData = 0; + + /* set the buffer for read / write */ + ven_req.pBuff = gpio_val; + + /* set the direction */ + if (direction) { + ven_req.direction = USB_DIR_IN; + memset(ven_req.pBuff, 0x00, ven_req.wLength); + } else + ven_req.direction = USB_DIR_OUT; + + + /* call common vendor command request */ + status = cx231xx_send_vendor_cmd(dev, &ven_req); + if (status < 0) { + cx231xx_info + ("UsbInterface::sendCommand, failed with status -%d\n", + status); + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd); + +/***************************************************************** + * C O N T R O L - Register R E A D / W R I T E functions * + *****************************************************************/ +int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + status = + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp |= mode; + + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4); + + return status; +} + +/***************************************************************** + * I 2 C Internal C O N T R O L functions * + *****************************************************************/ +int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 *data, u8 data_len, int master) +{ + int status = 0; + struct cx231xx_i2c_xfer_data req_data; + u8 value[64] = "0"; + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 1) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = I2C_M_RD; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = (u8 *) value; + + /* usb send command */ + if (master == 0) + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], + &req_data); + else if (master == 1) + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1], + &req_data); + else if (master == 2) + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2], + &req_data); + + if (status >= 0) { + /* Copy the data read back to main buffer */ + if (data_len == 1) + *data = value[0]; + else if (data_len == 4) + *data = + value[0] | value[1] << 8 | value[2] << 16 | value[3] + << 24; + else if (data_len > 4) + *data = value[saddr]; + } + + return status; +} + +int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 data, u8 data_len, int master) +{ + int status = 0; + u8 value[4] = { 0, 0, 0, 0 }; + struct cx231xx_i2c_xfer_data req_data; + + value[0] = (u8) data; + value[1] = (u8) (data >> 8); + value[2] = (u8) (data >> 16); + value[3] = (u8) (data >> 24); + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 1) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = 0; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = value; + + /* usb send command */ + if (master == 0) + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], + &req_data); + else if (master == 1) + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1], + &req_data); + else if (master == 2) + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2], + &req_data); + + return status; +} + +int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 *data, u8 data_len) +{ + int status = 0; + struct cx231xx_i2c_xfer_data req_data; + u8 value[4] = { 0, 0, 0, 0 }; + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 1) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = I2C_M_RD; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = (u8 *) value; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); + + if (status >= 0) { + /* Copy the data read back to main buffer */ + if (data_len == 1) + *data = value[0]; + else + *data = + value[0] | value[1] << 8 | value[2] << 16 | value[3] + << 24; + } + + return status; +} + +int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 data, u8 data_len) +{ + int status = 0; + u8 value[4] = { 0, 0, 0, 0 }; + struct cx231xx_i2c_xfer_data req_data; + + value[0] = (u8) data; + value[1] = (u8) (data >> 8); + value[2] = (u8) (data >> 16); + value[3] = (u8) (data >> 24); + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 1) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = 0; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = value; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); + + return status; +} + +int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, + u16 register_address, u8 bit_start, u8 bit_end, + u32 value) +{ + int status = 0; + u32 tmp; + u32 mask = 0; + int i; + + if (bit_start > (size - 1) || bit_end > (size - 1)) + return -1; + + if (size == 8) { + status = + cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, + &tmp, 1); + } else { + status = + cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, + &tmp, 4); + } + + if (status < 0) + return status; + + mask = 1 << bit_end; + for (i = bit_end; i > bit_start && i > 0; i--) + mask = mask + (1 << (i - 1)); + + value <<= bit_start; + + if (size == 8) { + tmp &= ~mask; + tmp |= value; + tmp &= 0xff; + status = + cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, + tmp, 1); + } else { + tmp &= ~mask; + tmp |= value; + status = + cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, + tmp, 4); + } + + return status; +} + +int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u32 mask, u32 value) +{ + u32 temp; + int status = 0; + + status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4); + + if (status < 0) + return status; + + temp &= ~mask; + temp |= value; + + status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4); + + return status; +} + +u32 cx231xx_set_field(u32 field_mask, u32 data) +{ + u32 temp; + + for (temp = field_mask; (temp & 1) == 0; temp >>= 1) + data <<= 1; + + return data; +} diff --git a/drivers/media/usb/cx231xx/cx231xx-dif.h b/drivers/media/usb/cx231xx/cx231xx-dif.h new file mode 100644 index 000000000000..2b63c2f6d3b0 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-dif.h @@ -0,0 +1,3178 @@ +/* + * cx231xx-dif.h - driver for Conexant Cx23100/101/102 USB video capture devices + * + * Copyright {C} 2009 + * + * This program is free software, 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 _CX231XX_DIF_H +#define _CX231XX_DIF_H + +#include "cx231xx-reg.h" + +struct dif_settings{ + u32 if_freq; + u32 register_address; + u32 value; +}; + +static struct dif_settings Dif_set_array[] = { + +/*case 3000000:*/ +/* BEGIN - DIF BPF register values from 30_quant.dat*/ +{3000000, DIF_BPF_COEFF01, 0x00000002}, +{3000000, DIF_BPF_COEFF23, 0x00080012}, +{3000000, DIF_BPF_COEFF45, 0x001e0024}, +{3000000, DIF_BPF_COEFF67, 0x001bfff8}, +{3000000, DIF_BPF_COEFF89, 0xffb4ff50}, +{3000000, DIF_BPF_COEFF1011, 0xfed8fe68}, +{3000000, DIF_BPF_COEFF1213, 0xfe24fe34}, +{3000000, DIF_BPF_COEFF1415, 0xfebaffc7}, +{3000000, DIF_BPF_COEFF1617, 0x014d031f}, +{3000000, DIF_BPF_COEFF1819, 0x04f0065d}, +{3000000, DIF_BPF_COEFF2021, 0x07010688}, +{3000000, DIF_BPF_COEFF2223, 0x04c901d6}, +{3000000, DIF_BPF_COEFF2425, 0xfe00f9d3}, +{3000000, DIF_BPF_COEFF2627, 0xf600f342}, +{3000000, DIF_BPF_COEFF2829, 0xf235f337}, +{3000000, DIF_BPF_COEFF3031, 0xf64efb22}, +{3000000, DIF_BPF_COEFF3233, 0x0105070f}, +{3000000, DIF_BPF_COEFF3435, 0x0c460fce}, +{3000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 30_quant.dat*/ + + +/*case 3100000:*/ +/* BEGIN - DIF BPF register values from 31_quant.dat*/ +{3100000, DIF_BPF_COEFF01, 0x00000001}, +{3100000, DIF_BPF_COEFF23, 0x00070012}, +{3100000, DIF_BPF_COEFF45, 0x00220032}, +{3100000, DIF_BPF_COEFF67, 0x00370026}, +{3100000, DIF_BPF_COEFF89, 0xfff0ff91}, +{3100000, DIF_BPF_COEFF1011, 0xff0efe7c}, +{3100000, DIF_BPF_COEFF1213, 0xfe01fdcc}, +{3100000, DIF_BPF_COEFF1415, 0xfe0afedb}, +{3100000, DIF_BPF_COEFF1617, 0x00440224}, +{3100000, DIF_BPF_COEFF1819, 0x0434060c}, +{3100000, DIF_BPF_COEFF2021, 0x0738074e}, +{3100000, DIF_BPF_COEFF2223, 0x06090361}, +{3100000, DIF_BPF_COEFF2425, 0xff99fb39}, +{3100000, DIF_BPF_COEFF2627, 0xf6fef3b6}, +{3100000, DIF_BPF_COEFF2829, 0xf21af2a5}, +{3100000, DIF_BPF_COEFF3031, 0xf573fa33}, +{3100000, DIF_BPF_COEFF3233, 0x0034067d}, +{3100000, DIF_BPF_COEFF3435, 0x0bfb0fb9}, +{3100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 31_quant.dat*/ + + +/*case 3200000:*/ +/* BEGIN - DIF BPF register values from 32_quant.dat*/ +{3200000, DIF_BPF_COEFF01, 0x00000000}, +{3200000, DIF_BPF_COEFF23, 0x0004000e}, +{3200000, DIF_BPF_COEFF45, 0x00200038}, +{3200000, DIF_BPF_COEFF67, 0x004c004f}, +{3200000, DIF_BPF_COEFF89, 0x002fffdf}, +{3200000, DIF_BPF_COEFF1011, 0xff5cfeb6}, +{3200000, DIF_BPF_COEFF1213, 0xfe0dfd92}, +{3200000, DIF_BPF_COEFF1415, 0xfd7ffe03}, +{3200000, DIF_BPF_COEFF1617, 0xff36010a}, +{3200000, DIF_BPF_COEFF1819, 0x03410575}, +{3200000, DIF_BPF_COEFF2021, 0x072607d2}, +{3200000, DIF_BPF_COEFF2223, 0x071804d5}, +{3200000, DIF_BPF_COEFF2425, 0x0134fcb7}, +{3200000, DIF_BPF_COEFF2627, 0xf81ff451}, +{3200000, DIF_BPF_COEFF2829, 0xf223f22e}, +{3200000, DIF_BPF_COEFF3031, 0xf4a7f94b}, +{3200000, DIF_BPF_COEFF3233, 0xff6405e8}, +{3200000, DIF_BPF_COEFF3435, 0x0bae0fa4}, +{3200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 32_quant.dat*/ + + +/*case 3300000:*/ +/* BEGIN - DIF BPF register values from 33_quant.dat*/ +{3300000, DIF_BPF_COEFF01, 0x0000ffff}, +{3300000, DIF_BPF_COEFF23, 0x00000008}, +{3300000, DIF_BPF_COEFF45, 0x001a0036}, +{3300000, DIF_BPF_COEFF67, 0x0056006d}, +{3300000, DIF_BPF_COEFF89, 0x00670030}, +{3300000, DIF_BPF_COEFF1011, 0xffbdff10}, +{3300000, DIF_BPF_COEFF1213, 0xfe46fd8d}, +{3300000, DIF_BPF_COEFF1415, 0xfd25fd4f}, +{3300000, DIF_BPF_COEFF1617, 0xfe35ffe0}, +{3300000, DIF_BPF_COEFF1819, 0x0224049f}, +{3300000, DIF_BPF_COEFF2021, 0x06c9080e}, +{3300000, DIF_BPF_COEFF2223, 0x07ef0627}, +{3300000, DIF_BPF_COEFF2425, 0x02c9fe45}, +{3300000, DIF_BPF_COEFF2627, 0xf961f513}, +{3300000, DIF_BPF_COEFF2829, 0xf250f1d2}, +{3300000, DIF_BPF_COEFF3031, 0xf3ecf869}, +{3300000, DIF_BPF_COEFF3233, 0xfe930552}, +{3300000, DIF_BPF_COEFF3435, 0x0b5f0f8f}, +{3300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 33_quant.dat*/ + + +/*case 3400000:*/ +/* BEGIN - DIF BPF register values from 34_quant.dat*/ +{3400000, DIF_BPF_COEFF01, 0xfffffffe}, +{3400000, DIF_BPF_COEFF23, 0xfffd0001}, +{3400000, DIF_BPF_COEFF45, 0x000f002c}, +{3400000, DIF_BPF_COEFF67, 0x0054007d}, +{3400000, DIF_BPF_COEFF89, 0x0093007c}, +{3400000, DIF_BPF_COEFF1011, 0x0024ff82}, +{3400000, DIF_BPF_COEFF1213, 0xfea6fdbb}, +{3400000, DIF_BPF_COEFF1415, 0xfd03fcca}, +{3400000, DIF_BPF_COEFF1617, 0xfd51feb9}, +{3400000, DIF_BPF_COEFF1819, 0x00eb0392}, +{3400000, DIF_BPF_COEFF2021, 0x06270802}, +{3400000, DIF_BPF_COEFF2223, 0x08880750}, +{3400000, DIF_BPF_COEFF2425, 0x044dffdb}, +{3400000, DIF_BPF_COEFF2627, 0xfabdf5f8}, +{3400000, DIF_BPF_COEFF2829, 0xf2a0f193}, +{3400000, DIF_BPF_COEFF3031, 0xf342f78f}, +{3400000, DIF_BPF_COEFF3233, 0xfdc404b9}, +{3400000, DIF_BPF_COEFF3435, 0x0b0e0f78}, +{3400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 34_quant.dat*/ + + +/*case 3500000:*/ +/* BEGIN - DIF BPF register values from 35_quant.dat*/ +{3500000, DIF_BPF_COEFF01, 0xfffffffd}, +{3500000, DIF_BPF_COEFF23, 0xfffafff9}, +{3500000, DIF_BPF_COEFF45, 0x0002001b}, +{3500000, DIF_BPF_COEFF67, 0x0046007d}, +{3500000, DIF_BPF_COEFF89, 0x00ad00ba}, +{3500000, DIF_BPF_COEFF1011, 0x00870000}, +{3500000, DIF_BPF_COEFF1213, 0xff26fe1a}, +{3500000, DIF_BPF_COEFF1415, 0xfd1bfc7e}, +{3500000, DIF_BPF_COEFF1617, 0xfc99fda4}, +{3500000, DIF_BPF_COEFF1819, 0xffa5025c}, +{3500000, DIF_BPF_COEFF2021, 0x054507ad}, +{3500000, DIF_BPF_COEFF2223, 0x08dd0847}, +{3500000, DIF_BPF_COEFF2425, 0x05b80172}, +{3500000, DIF_BPF_COEFF2627, 0xfc2ef6ff}, +{3500000, DIF_BPF_COEFF2829, 0xf313f170}, +{3500000, DIF_BPF_COEFF3031, 0xf2abf6bd}, +{3500000, DIF_BPF_COEFF3233, 0xfcf6041f}, +{3500000, DIF_BPF_COEFF3435, 0x0abc0f61}, +{3500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 35_quant.dat*/ + + +/*case 3600000:*/ +/* BEGIN - DIF BPF register values from 36_quant.dat*/ +{3600000, DIF_BPF_COEFF01, 0xfffffffd}, +{3600000, DIF_BPF_COEFF23, 0xfff8fff3}, +{3600000, DIF_BPF_COEFF45, 0xfff50006}, +{3600000, DIF_BPF_COEFF67, 0x002f006c}, +{3600000, DIF_BPF_COEFF89, 0x00b200e3}, +{3600000, DIF_BPF_COEFF1011, 0x00dc007e}, +{3600000, DIF_BPF_COEFF1213, 0xffb9fea0}, +{3600000, DIF_BPF_COEFF1415, 0xfd6bfc71}, +{3600000, DIF_BPF_COEFF1617, 0xfc17fcb1}, +{3600000, DIF_BPF_COEFF1819, 0xfe65010b}, +{3600000, DIF_BPF_COEFF2021, 0x042d0713}, +{3600000, DIF_BPF_COEFF2223, 0x08ec0906}, +{3600000, DIF_BPF_COEFF2425, 0x07020302}, +{3600000, DIF_BPF_COEFF2627, 0xfdaff823}, +{3600000, DIF_BPF_COEFF2829, 0xf3a7f16a}, +{3600000, DIF_BPF_COEFF3031, 0xf228f5f5}, +{3600000, DIF_BPF_COEFF3233, 0xfc2a0384}, +{3600000, DIF_BPF_COEFF3435, 0x0a670f4a}, +{3600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 36_quant.dat*/ + + +/*case 3700000:*/ +/* BEGIN - DIF BPF register values from 37_quant.dat*/ +{3700000, DIF_BPF_COEFF01, 0x0000fffd}, +{3700000, DIF_BPF_COEFF23, 0xfff7ffef}, +{3700000, DIF_BPF_COEFF45, 0xffe9fff1}, +{3700000, DIF_BPF_COEFF67, 0x0010004d}, +{3700000, DIF_BPF_COEFF89, 0x00a100f2}, +{3700000, DIF_BPF_COEFF1011, 0x011a00f0}, +{3700000, DIF_BPF_COEFF1213, 0x0053ff44}, +{3700000, DIF_BPF_COEFF1415, 0xfdedfca2}, +{3700000, DIF_BPF_COEFF1617, 0xfbd3fbef}, +{3700000, DIF_BPF_COEFF1819, 0xfd39ffae}, +{3700000, DIF_BPF_COEFF2021, 0x02ea0638}, +{3700000, DIF_BPF_COEFF2223, 0x08b50987}, +{3700000, DIF_BPF_COEFF2425, 0x08230483}, +{3700000, DIF_BPF_COEFF2627, 0xff39f960}, +{3700000, DIF_BPF_COEFF2829, 0xf45bf180}, +{3700000, DIF_BPF_COEFF3031, 0xf1b8f537}, +{3700000, DIF_BPF_COEFF3233, 0xfb6102e7}, +{3700000, DIF_BPF_COEFF3435, 0x0a110f32}, +{3700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 37_quant.dat*/ + + +/*case 3800000:*/ +/* BEGIN - DIF BPF register values from 38_quant.dat*/ +{3800000, DIF_BPF_COEFF01, 0x0000fffe}, +{3800000, DIF_BPF_COEFF23, 0xfff9ffee}, +{3800000, DIF_BPF_COEFF45, 0xffe1ffdd}, +{3800000, DIF_BPF_COEFF67, 0xfff00024}, +{3800000, DIF_BPF_COEFF89, 0x007c00e5}, +{3800000, DIF_BPF_COEFF1011, 0x013a014a}, +{3800000, DIF_BPF_COEFF1213, 0x00e6fff8}, +{3800000, DIF_BPF_COEFF1415, 0xfe98fd0f}, +{3800000, DIF_BPF_COEFF1617, 0xfbd3fb67}, +{3800000, DIF_BPF_COEFF1819, 0xfc32fe54}, +{3800000, DIF_BPF_COEFF2021, 0x01880525}, +{3800000, DIF_BPF_COEFF2223, 0x083909c7}, +{3800000, DIF_BPF_COEFF2425, 0x091505ee}, +{3800000, DIF_BPF_COEFF2627, 0x00c7fab3}, +{3800000, DIF_BPF_COEFF2829, 0xf52df1b4}, +{3800000, DIF_BPF_COEFF3031, 0xf15df484}, +{3800000, DIF_BPF_COEFF3233, 0xfa9b0249}, +{3800000, DIF_BPF_COEFF3435, 0x09ba0f19}, +{3800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 38_quant.dat*/ + + +/*case 3900000:*/ +/* BEGIN - DIF BPF register values from 39_quant.dat*/ +{3900000, DIF_BPF_COEFF01, 0x00000000}, +{3900000, DIF_BPF_COEFF23, 0xfffbfff0}, +{3900000, DIF_BPF_COEFF45, 0xffdeffcf}, +{3900000, DIF_BPF_COEFF67, 0xffd1fff6}, +{3900000, DIF_BPF_COEFF89, 0x004800be}, +{3900000, DIF_BPF_COEFF1011, 0x01390184}, +{3900000, DIF_BPF_COEFF1213, 0x016300ac}, +{3900000, DIF_BPF_COEFF1415, 0xff5efdb1}, +{3900000, DIF_BPF_COEFF1617, 0xfc17fb23}, +{3900000, DIF_BPF_COEFF1819, 0xfb5cfd0d}, +{3900000, DIF_BPF_COEFF2021, 0x001703e4}, +{3900000, DIF_BPF_COEFF2223, 0x077b09c4}, +{3900000, DIF_BPF_COEFF2425, 0x09d2073c}, +{3900000, DIF_BPF_COEFF2627, 0x0251fc18}, +{3900000, DIF_BPF_COEFF2829, 0xf61cf203}, +{3900000, DIF_BPF_COEFF3031, 0xf118f3dc}, +{3900000, DIF_BPF_COEFF3233, 0xf9d801aa}, +{3900000, DIF_BPF_COEFF3435, 0x09600eff}, +{3900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 39_quant.dat*/ + + +/*case 4000000:*/ +/* BEGIN - DIF BPF register values from 40_quant.dat*/ +{4000000, DIF_BPF_COEFF01, 0x00000001}, +{4000000, DIF_BPF_COEFF23, 0xfffefff4}, +{4000000, DIF_BPF_COEFF45, 0xffe1ffc8}, +{4000000, DIF_BPF_COEFF67, 0xffbaffca}, +{4000000, DIF_BPF_COEFF89, 0x000b0082}, +{4000000, DIF_BPF_COEFF1011, 0x01170198}, +{4000000, DIF_BPF_COEFF1213, 0x01c10152}, +{4000000, DIF_BPF_COEFF1415, 0x0030fe7b}, +{4000000, DIF_BPF_COEFF1617, 0xfc99fb24}, +{4000000, DIF_BPF_COEFF1819, 0xfac3fbe9}, +{4000000, DIF_BPF_COEFF2021, 0xfea5027f}, +{4000000, DIF_BPF_COEFF2223, 0x0683097f}, +{4000000, DIF_BPF_COEFF2425, 0x0a560867}, +{4000000, DIF_BPF_COEFF2627, 0x03d2fd89}, +{4000000, DIF_BPF_COEFF2829, 0xf723f26f}, +{4000000, DIF_BPF_COEFF3031, 0xf0e8f341}, +{4000000, DIF_BPF_COEFF3233, 0xf919010a}, +{4000000, DIF_BPF_COEFF3435, 0x09060ee5}, +{4000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 40_quant.dat*/ + + +/*case 4100000:*/ +/* BEGIN - DIF BPF register values from 41_quant.dat*/ +{4100000, DIF_BPF_COEFF01, 0x00010002}, +{4100000, DIF_BPF_COEFF23, 0x0002fffb}, +{4100000, DIF_BPF_COEFF45, 0xffe8ffca}, +{4100000, DIF_BPF_COEFF67, 0xffacffa4}, +{4100000, DIF_BPF_COEFF89, 0xffcd0036}, +{4100000, DIF_BPF_COEFF1011, 0x00d70184}, +{4100000, DIF_BPF_COEFF1213, 0x01f601dc}, +{4100000, DIF_BPF_COEFF1415, 0x00ffff60}, +{4100000, DIF_BPF_COEFF1617, 0xfd51fb6d}, +{4100000, DIF_BPF_COEFF1819, 0xfa6efaf5}, +{4100000, DIF_BPF_COEFF2021, 0xfd410103}, +{4100000, DIF_BPF_COEFF2223, 0x055708f9}, +{4100000, DIF_BPF_COEFF2425, 0x0a9e0969}, +{4100000, DIF_BPF_COEFF2627, 0x0543ff02}, +{4100000, DIF_BPF_COEFF2829, 0xf842f2f5}, +{4100000, DIF_BPF_COEFF3031, 0xf0cef2b2}, +{4100000, DIF_BPF_COEFF3233, 0xf85e006b}, +{4100000, DIF_BPF_COEFF3435, 0x08aa0ecb}, +{4100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 41_quant.dat*/ + + +/*case 4200000:*/ +/* BEGIN - DIF BPF register values from 42_quant.dat*/ +{4200000, DIF_BPF_COEFF01, 0x00010003}, +{4200000, DIF_BPF_COEFF23, 0x00050003}, +{4200000, DIF_BPF_COEFF45, 0xfff3ffd3}, +{4200000, DIF_BPF_COEFF67, 0xffaaff8b}, +{4200000, DIF_BPF_COEFF89, 0xff95ffe5}, +{4200000, DIF_BPF_COEFF1011, 0x0080014a}, +{4200000, DIF_BPF_COEFF1213, 0x01fe023f}, +{4200000, DIF_BPF_COEFF1415, 0x01ba0050}, +{4200000, DIF_BPF_COEFF1617, 0xfe35fbf8}, +{4200000, DIF_BPF_COEFF1819, 0xfa62fa3b}, +{4200000, DIF_BPF_COEFF2021, 0xfbf9ff7e}, +{4200000, DIF_BPF_COEFF2223, 0x04010836}, +{4200000, DIF_BPF_COEFF2425, 0x0aa90a3d}, +{4200000, DIF_BPF_COEFF2627, 0x069f007f}, +{4200000, DIF_BPF_COEFF2829, 0xf975f395}, +{4200000, DIF_BPF_COEFF3031, 0xf0cbf231}, +{4200000, DIF_BPF_COEFF3233, 0xf7a9ffcb}, +{4200000, DIF_BPF_COEFF3435, 0x084c0eaf}, +{4200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 42_quant.dat*/ + + +/*case 4300000:*/ +/* BEGIN - DIF BPF register values from 43_quant.dat*/ +{4300000, DIF_BPF_COEFF01, 0x00010003}, +{4300000, DIF_BPF_COEFF23, 0x0008000a}, +{4300000, DIF_BPF_COEFF45, 0x0000ffe4}, +{4300000, DIF_BPF_COEFF67, 0xffb4ff81}, +{4300000, DIF_BPF_COEFF89, 0xff6aff96}, +{4300000, DIF_BPF_COEFF1011, 0x001c00f0}, +{4300000, DIF_BPF_COEFF1213, 0x01d70271}, +{4300000, DIF_BPF_COEFF1415, 0x0254013b}, +{4300000, DIF_BPF_COEFF1617, 0xff36fcbd}, +{4300000, DIF_BPF_COEFF1819, 0xfa9ff9c5}, +{4300000, DIF_BPF_COEFF2021, 0xfadbfdfe}, +{4300000, DIF_BPF_COEFF2223, 0x028c073b}, +{4300000, DIF_BPF_COEFF2425, 0x0a750adf}, +{4300000, DIF_BPF_COEFF2627, 0x07e101fa}, +{4300000, DIF_BPF_COEFF2829, 0xfab8f44e}, +{4300000, DIF_BPF_COEFF3031, 0xf0ddf1be}, +{4300000, DIF_BPF_COEFF3233, 0xf6f9ff2b}, +{4300000, DIF_BPF_COEFF3435, 0x07ed0e94}, +{4300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 43_quant.dat*/ + + +/*case 4400000:*/ +/* BEGIN - DIF BPF register values from 44_quant.dat*/ +{4400000, DIF_BPF_COEFF01, 0x00000003}, +{4400000, DIF_BPF_COEFF23, 0x0009000f}, +{4400000, DIF_BPF_COEFF45, 0x000efff8}, +{4400000, DIF_BPF_COEFF67, 0xffc9ff87}, +{4400000, DIF_BPF_COEFF89, 0xff52ff54}, +{4400000, DIF_BPF_COEFF1011, 0xffb5007e}, +{4400000, DIF_BPF_COEFF1213, 0x01860270}, +{4400000, DIF_BPF_COEFF1415, 0x02c00210}, +{4400000, DIF_BPF_COEFF1617, 0x0044fdb2}, +{4400000, DIF_BPF_COEFF1819, 0xfb22f997}, +{4400000, DIF_BPF_COEFF2021, 0xf9f2fc90}, +{4400000, DIF_BPF_COEFF2223, 0x0102060f}, +{4400000, DIF_BPF_COEFF2425, 0x0a050b4c}, +{4400000, DIF_BPF_COEFF2627, 0x0902036e}, +{4400000, DIF_BPF_COEFF2829, 0xfc0af51e}, +{4400000, DIF_BPF_COEFF3031, 0xf106f15a}, +{4400000, DIF_BPF_COEFF3233, 0xf64efe8b}, +{4400000, DIF_BPF_COEFF3435, 0x078d0e77}, +{4400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 44_quant.dat*/ + + +/*case 4500000:*/ +/* BEGIN - DIF BPF register values from 45_quant.dat*/ +{4500000, DIF_BPF_COEFF01, 0x00000002}, +{4500000, DIF_BPF_COEFF23, 0x00080012}, +{4500000, DIF_BPF_COEFF45, 0x0019000e}, +{4500000, DIF_BPF_COEFF67, 0xffe5ff9e}, +{4500000, DIF_BPF_COEFF89, 0xff4fff25}, +{4500000, DIF_BPF_COEFF1011, 0xff560000}, +{4500000, DIF_BPF_COEFF1213, 0x0112023b}, +{4500000, DIF_BPF_COEFF1415, 0x02f702c0}, +{4500000, DIF_BPF_COEFF1617, 0x014dfec8}, +{4500000, DIF_BPF_COEFF1819, 0xfbe5f9b3}, +{4500000, DIF_BPF_COEFF2021, 0xf947fb41}, +{4500000, DIF_BPF_COEFF2223, 0xff7004b9}, +{4500000, DIF_BPF_COEFF2425, 0x095a0b81}, +{4500000, DIF_BPF_COEFF2627, 0x0a0004d8}, +{4500000, DIF_BPF_COEFF2829, 0xfd65f603}, +{4500000, DIF_BPF_COEFF3031, 0xf144f104}, +{4500000, DIF_BPF_COEFF3233, 0xf5aafdec}, +{4500000, DIF_BPF_COEFF3435, 0x072b0e5a}, +{4500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 45_quant.dat*/ + + +/*case 4600000:*/ +/* BEGIN - DIF BPF register values from 46_quant.dat*/ +{4600000, DIF_BPF_COEFF01, 0x00000001}, +{4600000, DIF_BPF_COEFF23, 0x00060012}, +{4600000, DIF_BPF_COEFF45, 0x00200022}, +{4600000, DIF_BPF_COEFF67, 0x0005ffc1}, +{4600000, DIF_BPF_COEFF89, 0xff61ff10}, +{4600000, DIF_BPF_COEFF1011, 0xff09ff82}, +{4600000, DIF_BPF_COEFF1213, 0x008601d7}, +{4600000, DIF_BPF_COEFF1415, 0x02f50340}, +{4600000, DIF_BPF_COEFF1617, 0x0241fff0}, +{4600000, DIF_BPF_COEFF1819, 0xfcddfa19}, +{4600000, DIF_BPF_COEFF2021, 0xf8e2fa1e}, +{4600000, DIF_BPF_COEFF2223, 0xfde30343}, +{4600000, DIF_BPF_COEFF2425, 0x08790b7f}, +{4600000, DIF_BPF_COEFF2627, 0x0ad50631}, +{4600000, DIF_BPF_COEFF2829, 0xfec7f6fc}, +{4600000, DIF_BPF_COEFF3031, 0xf198f0bd}, +{4600000, DIF_BPF_COEFF3233, 0xf50dfd4e}, +{4600000, DIF_BPF_COEFF3435, 0x06c90e3d}, +{4600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 46_quant.dat*/ + + +/*case 4700000:*/ +/* BEGIN - DIF BPF register values from 47_quant.dat*/ +{4700000, DIF_BPF_COEFF01, 0x0000ffff}, +{4700000, DIF_BPF_COEFF23, 0x0003000f}, +{4700000, DIF_BPF_COEFF45, 0x00220030}, +{4700000, DIF_BPF_COEFF67, 0x0025ffed}, +{4700000, DIF_BPF_COEFF89, 0xff87ff15}, +{4700000, DIF_BPF_COEFF1011, 0xfed6ff10}, +{4700000, DIF_BPF_COEFF1213, 0xffed014c}, +{4700000, DIF_BPF_COEFF1415, 0x02b90386}, +{4700000, DIF_BPF_COEFF1617, 0x03110119}, +{4700000, DIF_BPF_COEFF1819, 0xfdfefac4}, +{4700000, DIF_BPF_COEFF2021, 0xf8c6f92f}, +{4700000, DIF_BPF_COEFF2223, 0xfc6701b7}, +{4700000, DIF_BPF_COEFF2425, 0x07670b44}, +{4700000, DIF_BPF_COEFF2627, 0x0b7e0776}, +{4700000, DIF_BPF_COEFF2829, 0x002df807}, +{4700000, DIF_BPF_COEFF3031, 0xf200f086}, +{4700000, DIF_BPF_COEFF3233, 0xf477fcb1}, +{4700000, DIF_BPF_COEFF3435, 0x06650e1e}, +{4700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 47_quant.dat*/ + + +/*case 4800000:*/ +/* BEGIN - DIF BPF register values from 48_quant.dat*/ +{4800000, DIF_BPF_COEFF01, 0xfffffffe}, +{4800000, DIF_BPF_COEFF23, 0xffff0009}, +{4800000, DIF_BPF_COEFF45, 0x001e0038}, +{4800000, DIF_BPF_COEFF67, 0x003f001b}, +{4800000, DIF_BPF_COEFF89, 0xffbcff36}, +{4800000, DIF_BPF_COEFF1011, 0xfec2feb6}, +{4800000, DIF_BPF_COEFF1213, 0xff5600a5}, +{4800000, DIF_BPF_COEFF1415, 0x0248038d}, +{4800000, DIF_BPF_COEFF1617, 0x03b00232}, +{4800000, DIF_BPF_COEFF1819, 0xff39fbab}, +{4800000, DIF_BPF_COEFF2021, 0xf8f4f87f}, +{4800000, DIF_BPF_COEFF2223, 0xfb060020}, +{4800000, DIF_BPF_COEFF2425, 0x062a0ad2}, +{4800000, DIF_BPF_COEFF2627, 0x0bf908a3}, +{4800000, DIF_BPF_COEFF2829, 0x0192f922}, +{4800000, DIF_BPF_COEFF3031, 0xf27df05e}, +{4800000, DIF_BPF_COEFF3233, 0xf3e8fc14}, +{4800000, DIF_BPF_COEFF3435, 0x06000e00}, +{4800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 48_quant.dat*/ + + +/*case 4900000:*/ +/* BEGIN - DIF BPF register values from 49_quant.dat*/ +{4900000, DIF_BPF_COEFF01, 0xfffffffd}, +{4900000, DIF_BPF_COEFF23, 0xfffc0002}, +{4900000, DIF_BPF_COEFF45, 0x00160037}, +{4900000, DIF_BPF_COEFF67, 0x00510046}, +{4900000, DIF_BPF_COEFF89, 0xfff9ff6d}, +{4900000, DIF_BPF_COEFF1011, 0xfed0fe7c}, +{4900000, DIF_BPF_COEFF1213, 0xfecefff0}, +{4900000, DIF_BPF_COEFF1415, 0x01aa0356}, +{4900000, DIF_BPF_COEFF1617, 0x0413032b}, +{4900000, DIF_BPF_COEFF1819, 0x007ffcc5}, +{4900000, DIF_BPF_COEFF2021, 0xf96cf812}, +{4900000, DIF_BPF_COEFF2223, 0xf9cefe87}, +{4900000, DIF_BPF_COEFF2425, 0x04c90a2c}, +{4900000, DIF_BPF_COEFF2627, 0x0c4309b4}, +{4900000, DIF_BPF_COEFF2829, 0x02f3fa4a}, +{4900000, DIF_BPF_COEFF3031, 0xf30ef046}, +{4900000, DIF_BPF_COEFF3233, 0xf361fb7a}, +{4900000, DIF_BPF_COEFF3435, 0x059b0de0}, +{4900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 49_quant.dat*/ + + +/*case 5000000:*/ +/* BEGIN - DIF BPF register values from 50_quant.dat*/ +{5000000, DIF_BPF_COEFF01, 0xfffffffd}, +{5000000, DIF_BPF_COEFF23, 0xfff9fffa}, +{5000000, DIF_BPF_COEFF45, 0x000a002d}, +{5000000, DIF_BPF_COEFF67, 0x00570067}, +{5000000, DIF_BPF_COEFF89, 0x0037ffb5}, +{5000000, DIF_BPF_COEFF1011, 0xfefffe68}, +{5000000, DIF_BPF_COEFF1213, 0xfe62ff3d}, +{5000000, DIF_BPF_COEFF1415, 0x00ec02e3}, +{5000000, DIF_BPF_COEFF1617, 0x043503f6}, +{5000000, DIF_BPF_COEFF1819, 0x01befe05}, +{5000000, DIF_BPF_COEFF2021, 0xfa27f7ee}, +{5000000, DIF_BPF_COEFF2223, 0xf8c6fcf8}, +{5000000, DIF_BPF_COEFF2425, 0x034c0954}, +{5000000, DIF_BPF_COEFF2627, 0x0c5c0aa4}, +{5000000, DIF_BPF_COEFF2829, 0x044cfb7e}, +{5000000, DIF_BPF_COEFF3031, 0xf3b1f03f}, +{5000000, DIF_BPF_COEFF3233, 0xf2e2fae1}, +{5000000, DIF_BPF_COEFF3435, 0x05340dc0}, +{5000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 50_quant.dat*/ + + +/*case 5100000:*/ +/* BEGIN - DIF BPF register values from 51_quant.dat*/ +{5100000, DIF_BPF_COEFF01, 0x0000fffd}, +{5100000, DIF_BPF_COEFF23, 0xfff8fff4}, +{5100000, DIF_BPF_COEFF45, 0xfffd001e}, +{5100000, DIF_BPF_COEFF67, 0x0051007b}, +{5100000, DIF_BPF_COEFF89, 0x006e0006}, +{5100000, DIF_BPF_COEFF1011, 0xff48fe7c}, +{5100000, DIF_BPF_COEFF1213, 0xfe1bfe9a}, +{5100000, DIF_BPF_COEFF1415, 0x001d023e}, +{5100000, DIF_BPF_COEFF1617, 0x04130488}, +{5100000, DIF_BPF_COEFF1819, 0x02e6ff5b}, +{5100000, DIF_BPF_COEFF2021, 0xfb1ef812}, +{5100000, DIF_BPF_COEFF2223, 0xf7f7fb7f}, +{5100000, DIF_BPF_COEFF2425, 0x01bc084e}, +{5100000, DIF_BPF_COEFF2627, 0x0c430b72}, +{5100000, DIF_BPF_COEFF2829, 0x059afcba}, +{5100000, DIF_BPF_COEFF3031, 0xf467f046}, +{5100000, DIF_BPF_COEFF3233, 0xf26cfa4a}, +{5100000, DIF_BPF_COEFF3435, 0x04cd0da0}, +{5100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 51_quant.dat*/ + + +/*case 5200000:*/ +/* BEGIN - DIF BPF register values from 52_quant.dat*/ +{5200000, DIF_BPF_COEFF01, 0x0000fffe}, +{5200000, DIF_BPF_COEFF23, 0xfff8ffef}, +{5200000, DIF_BPF_COEFF45, 0xfff00009}, +{5200000, DIF_BPF_COEFF67, 0x003f007f}, +{5200000, DIF_BPF_COEFF89, 0x00980056}, +{5200000, DIF_BPF_COEFF1011, 0xffa5feb6}, +{5200000, DIF_BPF_COEFF1213, 0xfe00fe15}, +{5200000, DIF_BPF_COEFF1415, 0xff4b0170}, +{5200000, DIF_BPF_COEFF1617, 0x03b004d7}, +{5200000, DIF_BPF_COEFF1819, 0x03e800b9}, +{5200000, DIF_BPF_COEFF2021, 0xfc48f87f}, +{5200000, DIF_BPF_COEFF2223, 0xf768fa23}, +{5200000, DIF_BPF_COEFF2425, 0x0022071f}, +{5200000, DIF_BPF_COEFF2627, 0x0bf90c1b}, +{5200000, DIF_BPF_COEFF2829, 0x06dafdfd}, +{5200000, DIF_BPF_COEFF3031, 0xf52df05e}, +{5200000, DIF_BPF_COEFF3233, 0xf1fef9b5}, +{5200000, DIF_BPF_COEFF3435, 0x04640d7f}, +{5200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 52_quant.dat*/ + + +/*case 5300000:*/ +/* BEGIN - DIF BPF register values from 53_quant.dat*/ +{5300000, DIF_BPF_COEFF01, 0x0000ffff}, +{5300000, DIF_BPF_COEFF23, 0xfff9ffee}, +{5300000, DIF_BPF_COEFF45, 0xffe6fff3}, +{5300000, DIF_BPF_COEFF67, 0x00250072}, +{5300000, DIF_BPF_COEFF89, 0x00af009c}, +{5300000, DIF_BPF_COEFF1011, 0x000cff10}, +{5300000, DIF_BPF_COEFF1213, 0xfe13fdb8}, +{5300000, DIF_BPF_COEFF1415, 0xfe870089}, +{5300000, DIF_BPF_COEFF1617, 0x031104e1}, +{5300000, DIF_BPF_COEFF1819, 0x04b8020f}, +{5300000, DIF_BPF_COEFF2021, 0xfd98f92f}, +{5300000, DIF_BPF_COEFF2223, 0xf71df8f0}, +{5300000, DIF_BPF_COEFF2425, 0xfe8805ce}, +{5300000, DIF_BPF_COEFF2627, 0x0b7e0c9c}, +{5300000, DIF_BPF_COEFF2829, 0x0808ff44}, +{5300000, DIF_BPF_COEFF3031, 0xf603f086}, +{5300000, DIF_BPF_COEFF3233, 0xf19af922}, +{5300000, DIF_BPF_COEFF3435, 0x03fb0d5e}, +{5300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 53_quant.dat*/ + + +/*case 5400000:*/ +/* BEGIN - DIF BPF register values from 54_quant.dat*/ +{5400000, DIF_BPF_COEFF01, 0x00000001}, +{5400000, DIF_BPF_COEFF23, 0xfffcffef}, +{5400000, DIF_BPF_COEFF45, 0xffe0ffe0}, +{5400000, DIF_BPF_COEFF67, 0x00050056}, +{5400000, DIF_BPF_COEFF89, 0x00b000d1}, +{5400000, DIF_BPF_COEFF1011, 0x0071ff82}, +{5400000, DIF_BPF_COEFF1213, 0xfe53fd8c}, +{5400000, DIF_BPF_COEFF1415, 0xfddfff99}, +{5400000, DIF_BPF_COEFF1617, 0x024104a3}, +{5400000, DIF_BPF_COEFF1819, 0x054a034d}, +{5400000, DIF_BPF_COEFF2021, 0xff01fa1e}, +{5400000, DIF_BPF_COEFF2223, 0xf717f7ed}, +{5400000, DIF_BPF_COEFF2425, 0xfcf50461}, +{5400000, DIF_BPF_COEFF2627, 0x0ad50cf4}, +{5400000, DIF_BPF_COEFF2829, 0x0921008d}, +{5400000, DIF_BPF_COEFF3031, 0xf6e7f0bd}, +{5400000, DIF_BPF_COEFF3233, 0xf13ff891}, +{5400000, DIF_BPF_COEFF3435, 0x03920d3b}, +{5400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 54_quant.dat*/ + + +/*case 5500000:*/ +/* BEGIN - DIF BPF register values from 55_quant.dat*/ +{5500000, DIF_BPF_COEFF01, 0x00010002}, +{5500000, DIF_BPF_COEFF23, 0xfffffff3}, +{5500000, DIF_BPF_COEFF45, 0xffdeffd1}, +{5500000, DIF_BPF_COEFF67, 0xffe5002f}, +{5500000, DIF_BPF_COEFF89, 0x009c00ed}, +{5500000, DIF_BPF_COEFF1011, 0x00cb0000}, +{5500000, DIF_BPF_COEFF1213, 0xfebafd94}, +{5500000, DIF_BPF_COEFF1415, 0xfd61feb0}, +{5500000, DIF_BPF_COEFF1617, 0x014d0422}, +{5500000, DIF_BPF_COEFF1819, 0x05970464}, +{5500000, DIF_BPF_COEFF2021, 0x0074fb41}, +{5500000, DIF_BPF_COEFF2223, 0xf759f721}, +{5500000, DIF_BPF_COEFF2425, 0xfb7502de}, +{5500000, DIF_BPF_COEFF2627, 0x0a000d21}, +{5500000, DIF_BPF_COEFF2829, 0x0a2201d4}, +{5500000, DIF_BPF_COEFF3031, 0xf7d9f104}, +{5500000, DIF_BPF_COEFF3233, 0xf0edf804}, +{5500000, DIF_BPF_COEFF3435, 0x03280d19}, +{5500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 55_quant.dat*/ + + +/*case 5600000:*/ +/* BEGIN - DIF BPF register values from 56_quant.dat*/ +{5600000, DIF_BPF_COEFF01, 0x00010003}, +{5600000, DIF_BPF_COEFF23, 0x0003fffa}, +{5600000, DIF_BPF_COEFF45, 0xffe3ffc9}, +{5600000, DIF_BPF_COEFF67, 0xffc90002}, +{5600000, DIF_BPF_COEFF89, 0x007500ef}, +{5600000, DIF_BPF_COEFF1011, 0x010e007e}, +{5600000, DIF_BPF_COEFF1213, 0xff3dfdcf}, +{5600000, DIF_BPF_COEFF1415, 0xfd16fddd}, +{5600000, DIF_BPF_COEFF1617, 0x00440365}, +{5600000, DIF_BPF_COEFF1819, 0x059b0548}, +{5600000, DIF_BPF_COEFF2021, 0x01e3fc90}, +{5600000, DIF_BPF_COEFF2223, 0xf7dff691}, +{5600000, DIF_BPF_COEFF2425, 0xfa0f014d}, +{5600000, DIF_BPF_COEFF2627, 0x09020d23}, +{5600000, DIF_BPF_COEFF2829, 0x0b0a0318}, +{5600000, DIF_BPF_COEFF3031, 0xf8d7f15a}, +{5600000, DIF_BPF_COEFF3233, 0xf0a5f779}, +{5600000, DIF_BPF_COEFF3435, 0x02bd0cf6}, +{5600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 56_quant.dat*/ + + +/*case 5700000:*/ +/* BEGIN - DIF BPF register values from 57_quant.dat*/ +{5700000, DIF_BPF_COEFF01, 0x00010003}, +{5700000, DIF_BPF_COEFF23, 0x00060001}, +{5700000, DIF_BPF_COEFF45, 0xffecffc9}, +{5700000, DIF_BPF_COEFF67, 0xffb4ffd4}, +{5700000, DIF_BPF_COEFF89, 0x004000d5}, +{5700000, DIF_BPF_COEFF1011, 0x013600f0}, +{5700000, DIF_BPF_COEFF1213, 0xffd3fe39}, +{5700000, DIF_BPF_COEFF1415, 0xfd04fd31}, +{5700000, DIF_BPF_COEFF1617, 0xff360277}, +{5700000, DIF_BPF_COEFF1819, 0x055605ef}, +{5700000, DIF_BPF_COEFF2021, 0x033efdfe}, +{5700000, DIF_BPF_COEFF2223, 0xf8a5f642}, +{5700000, DIF_BPF_COEFF2425, 0xf8cbffb6}, +{5700000, DIF_BPF_COEFF2627, 0x07e10cfb}, +{5700000, DIF_BPF_COEFF2829, 0x0bd50456}, +{5700000, DIF_BPF_COEFF3031, 0xf9dff1be}, +{5700000, DIF_BPF_COEFF3233, 0xf067f6f2}, +{5700000, DIF_BPF_COEFF3435, 0x02520cd2}, +{5700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 57_quant.dat*/ + + +/*case 5800000:*/ +/* BEGIN - DIF BPF register values from 58_quant.dat*/ +{5800000, DIF_BPF_COEFF01, 0x00000003}, +{5800000, DIF_BPF_COEFF23, 0x00080009}, +{5800000, DIF_BPF_COEFF45, 0xfff8ffd2}, +{5800000, DIF_BPF_COEFF67, 0xffaaffac}, +{5800000, DIF_BPF_COEFF89, 0x000200a3}, +{5800000, DIF_BPF_COEFF1011, 0x013c014a}, +{5800000, DIF_BPF_COEFF1213, 0x006dfec9}, +{5800000, DIF_BPF_COEFF1415, 0xfd2bfcb7}, +{5800000, DIF_BPF_COEFF1617, 0xfe350165}, +{5800000, DIF_BPF_COEFF1819, 0x04cb0651}, +{5800000, DIF_BPF_COEFF2021, 0x0477ff7e}, +{5800000, DIF_BPF_COEFF2223, 0xf9a5f635}, +{5800000, DIF_BPF_COEFF2425, 0xf7b1fe20}, +{5800000, DIF_BPF_COEFF2627, 0x069f0ca8}, +{5800000, DIF_BPF_COEFF2829, 0x0c81058b}, +{5800000, DIF_BPF_COEFF3031, 0xfaf0f231}, +{5800000, DIF_BPF_COEFF3233, 0xf033f66d}, +{5800000, DIF_BPF_COEFF3435, 0x01e60cae}, +{5800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 58_quant.dat*/ + + +/*case 5900000:*/ +/* BEGIN - DIF BPF register values from 59_quant.dat*/ +{5900000, DIF_BPF_COEFF01, 0x00000002}, +{5900000, DIF_BPF_COEFF23, 0x0009000e}, +{5900000, DIF_BPF_COEFF45, 0x0005ffe1}, +{5900000, DIF_BPF_COEFF67, 0xffacff90}, +{5900000, DIF_BPF_COEFF89, 0xffc5005f}, +{5900000, DIF_BPF_COEFF1011, 0x01210184}, +{5900000, DIF_BPF_COEFF1213, 0x00fcff72}, +{5900000, DIF_BPF_COEFF1415, 0xfd8afc77}, +{5900000, DIF_BPF_COEFF1617, 0xfd51003f}, +{5900000, DIF_BPF_COEFF1819, 0x04020669}, +{5900000, DIF_BPF_COEFF2021, 0x05830103}, +{5900000, DIF_BPF_COEFF2223, 0xfad7f66b}, +{5900000, DIF_BPF_COEFF2425, 0xf6c8fc93}, +{5900000, DIF_BPF_COEFF2627, 0x05430c2b}, +{5900000, DIF_BPF_COEFF2829, 0x0d0d06b5}, +{5900000, DIF_BPF_COEFF3031, 0xfc08f2b2}, +{5900000, DIF_BPF_COEFF3233, 0xf00af5ec}, +{5900000, DIF_BPF_COEFF3435, 0x017b0c89}, +{5900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 59_quant.dat*/ + + +/*case 6000000:*/ +/* BEGIN - DIF BPF register values from 60_quant.dat*/ +{6000000, DIF_BPF_COEFF01, 0x00000001}, +{6000000, DIF_BPF_COEFF23, 0x00070012}, +{6000000, DIF_BPF_COEFF45, 0x0012fff5}, +{6000000, DIF_BPF_COEFF67, 0xffbaff82}, +{6000000, DIF_BPF_COEFF89, 0xff8e000f}, +{6000000, DIF_BPF_COEFF1011, 0x00e80198}, +{6000000, DIF_BPF_COEFF1213, 0x01750028}, +{6000000, DIF_BPF_COEFF1415, 0xfe18fc75}, +{6000000, DIF_BPF_COEFF1617, 0xfc99ff15}, +{6000000, DIF_BPF_COEFF1819, 0x03050636}, +{6000000, DIF_BPF_COEFF2021, 0x0656027f}, +{6000000, DIF_BPF_COEFF2223, 0xfc32f6e2}, +{6000000, DIF_BPF_COEFF2425, 0xf614fb17}, +{6000000, DIF_BPF_COEFF2627, 0x03d20b87}, +{6000000, DIF_BPF_COEFF2829, 0x0d7707d2}, +{6000000, DIF_BPF_COEFF3031, 0xfd26f341}, +{6000000, DIF_BPF_COEFF3233, 0xefeaf56f}, +{6000000, DIF_BPF_COEFF3435, 0x010f0c64}, +{6000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 60_quant.dat*/ + + +/*case 6100000:*/ +/* BEGIN - DIF BPF register values from 61_quant.dat*/ +{6100000, DIF_BPF_COEFF01, 0xffff0000}, +{6100000, DIF_BPF_COEFF23, 0x00050012}, +{6100000, DIF_BPF_COEFF45, 0x001c000b}, +{6100000, DIF_BPF_COEFF67, 0xffd1ff84}, +{6100000, DIF_BPF_COEFF89, 0xff66ffbe}, +{6100000, DIF_BPF_COEFF1011, 0x00960184}, +{6100000, DIF_BPF_COEFF1213, 0x01cd00da}, +{6100000, DIF_BPF_COEFF1415, 0xfeccfcb2}, +{6100000, DIF_BPF_COEFF1617, 0xfc17fdf9}, +{6100000, DIF_BPF_COEFF1819, 0x01e005bc}, +{6100000, DIF_BPF_COEFF2021, 0x06e703e4}, +{6100000, DIF_BPF_COEFF2223, 0xfdabf798}, +{6100000, DIF_BPF_COEFF2425, 0xf599f9b3}, +{6100000, DIF_BPF_COEFF2627, 0x02510abd}, +{6100000, DIF_BPF_COEFF2829, 0x0dbf08df}, +{6100000, DIF_BPF_COEFF3031, 0xfe48f3dc}, +{6100000, DIF_BPF_COEFF3233, 0xefd5f4f6}, +{6100000, DIF_BPF_COEFF3435, 0x00a20c3e}, +{6100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 61_quant.dat*/ + + +/*case 6200000:*/ +/* BEGIN - DIF BPF register values from 62_quant.dat*/ +{6200000, DIF_BPF_COEFF01, 0xfffffffe}, +{6200000, DIF_BPF_COEFF23, 0x0002000f}, +{6200000, DIF_BPF_COEFF45, 0x0021001f}, +{6200000, DIF_BPF_COEFF67, 0xfff0ff97}, +{6200000, DIF_BPF_COEFF89, 0xff50ff74}, +{6200000, DIF_BPF_COEFF1011, 0x0034014a}, +{6200000, DIF_BPF_COEFF1213, 0x01fa0179}, +{6200000, DIF_BPF_COEFF1415, 0xff97fd2a}, +{6200000, DIF_BPF_COEFF1617, 0xfbd3fcfa}, +{6200000, DIF_BPF_COEFF1819, 0x00a304fe}, +{6200000, DIF_BPF_COEFF2021, 0x07310525}, +{6200000, DIF_BPF_COEFF2223, 0xff37f886}, +{6200000, DIF_BPF_COEFF2425, 0xf55cf86e}, +{6200000, DIF_BPF_COEFF2627, 0x00c709d0}, +{6200000, DIF_BPF_COEFF2829, 0x0de209db}, +{6200000, DIF_BPF_COEFF3031, 0xff6df484}, +{6200000, DIF_BPF_COEFF3233, 0xefcbf481}, +{6200000, DIF_BPF_COEFF3435, 0x00360c18}, +{6200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 62_quant.dat*/ + + +/*case 6300000:*/ +/* BEGIN - DIF BPF register values from 63_quant.dat*/ +{6300000, DIF_BPF_COEFF01, 0xfffffffd}, +{6300000, DIF_BPF_COEFF23, 0xfffe000a}, +{6300000, DIF_BPF_COEFF45, 0x0021002f}, +{6300000, DIF_BPF_COEFF67, 0x0010ffb8}, +{6300000, DIF_BPF_COEFF89, 0xff50ff3b}, +{6300000, DIF_BPF_COEFF1011, 0xffcc00f0}, +{6300000, DIF_BPF_COEFF1213, 0x01fa01fa}, +{6300000, DIF_BPF_COEFF1415, 0x0069fdd4}, +{6300000, DIF_BPF_COEFF1617, 0xfbd3fc26}, +{6300000, DIF_BPF_COEFF1819, 0xff5d0407}, +{6300000, DIF_BPF_COEFF2021, 0x07310638}, +{6300000, DIF_BPF_COEFF2223, 0x00c9f9a8}, +{6300000, DIF_BPF_COEFF2425, 0xf55cf74e}, +{6300000, DIF_BPF_COEFF2627, 0xff3908c3}, +{6300000, DIF_BPF_COEFF2829, 0x0de20ac3}, +{6300000, DIF_BPF_COEFF3031, 0x0093f537}, +{6300000, DIF_BPF_COEFF3233, 0xefcbf410}, +{6300000, DIF_BPF_COEFF3435, 0xffca0bf2}, +{6300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 63_quant.dat*/ + + +/*case 6400000:*/ +/* BEGIN - DIF BPF register values from 64_quant.dat*/ +{6400000, DIF_BPF_COEFF01, 0xfffffffd}, +{6400000, DIF_BPF_COEFF23, 0xfffb0003}, +{6400000, DIF_BPF_COEFF45, 0x001c0037}, +{6400000, DIF_BPF_COEFF67, 0x002fffe2}, +{6400000, DIF_BPF_COEFF89, 0xff66ff17}, +{6400000, DIF_BPF_COEFF1011, 0xff6a007e}, +{6400000, DIF_BPF_COEFF1213, 0x01cd0251}, +{6400000, DIF_BPF_COEFF1415, 0x0134fea5}, +{6400000, DIF_BPF_COEFF1617, 0xfc17fb8b}, +{6400000, DIF_BPF_COEFF1819, 0xfe2002e0}, +{6400000, DIF_BPF_COEFF2021, 0x06e70713}, +{6400000, DIF_BPF_COEFF2223, 0x0255faf5}, +{6400000, DIF_BPF_COEFF2425, 0xf599f658}, +{6400000, DIF_BPF_COEFF2627, 0xfdaf0799}, +{6400000, DIF_BPF_COEFF2829, 0x0dbf0b96}, +{6400000, DIF_BPF_COEFF3031, 0x01b8f5f5}, +{6400000, DIF_BPF_COEFF3233, 0xefd5f3a3}, +{6400000, DIF_BPF_COEFF3435, 0xff5e0bca}, +{6400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 64_quant.dat*/ + + +/*case 6500000:*/ +/* BEGIN - DIF BPF register values from 65_quant.dat*/ +{6500000, DIF_BPF_COEFF01, 0x0000fffd}, +{6500000, DIF_BPF_COEFF23, 0xfff9fffb}, +{6500000, DIF_BPF_COEFF45, 0x00120037}, +{6500000, DIF_BPF_COEFF67, 0x00460010}, +{6500000, DIF_BPF_COEFF89, 0xff8eff0f}, +{6500000, DIF_BPF_COEFF1011, 0xff180000}, +{6500000, DIF_BPF_COEFF1213, 0x01750276}, +{6500000, DIF_BPF_COEFF1415, 0x01e8ff8d}, +{6500000, DIF_BPF_COEFF1617, 0xfc99fb31}, +{6500000, DIF_BPF_COEFF1819, 0xfcfb0198}, +{6500000, DIF_BPF_COEFF2021, 0x065607ad}, +{6500000, DIF_BPF_COEFF2223, 0x03cefc64}, +{6500000, DIF_BPF_COEFF2425, 0xf614f592}, +{6500000, DIF_BPF_COEFF2627, 0xfc2e0656}, +{6500000, DIF_BPF_COEFF2829, 0x0d770c52}, +{6500000, DIF_BPF_COEFF3031, 0x02daf6bd}, +{6500000, DIF_BPF_COEFF3233, 0xefeaf33b}, +{6500000, DIF_BPF_COEFF3435, 0xfef10ba3}, +{6500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 65_quant.dat*/ + + +/*case 6600000:*/ +/* BEGIN - DIF BPF register values from 66_quant.dat*/ +{6600000, DIF_BPF_COEFF01, 0x0000fffe}, +{6600000, DIF_BPF_COEFF23, 0xfff7fff5}, +{6600000, DIF_BPF_COEFF45, 0x0005002f}, +{6600000, DIF_BPF_COEFF67, 0x0054003c}, +{6600000, DIF_BPF_COEFF89, 0xffc5ff22}, +{6600000, DIF_BPF_COEFF1011, 0xfedfff82}, +{6600000, DIF_BPF_COEFF1213, 0x00fc0267}, +{6600000, DIF_BPF_COEFF1415, 0x0276007e}, +{6600000, DIF_BPF_COEFF1617, 0xfd51fb1c}, +{6600000, DIF_BPF_COEFF1819, 0xfbfe003e}, +{6600000, DIF_BPF_COEFF2021, 0x05830802}, +{6600000, DIF_BPF_COEFF2223, 0x0529fdec}, +{6600000, DIF_BPF_COEFF2425, 0xf6c8f4fe}, +{6600000, DIF_BPF_COEFF2627, 0xfabd04ff}, +{6600000, DIF_BPF_COEFF2829, 0x0d0d0cf6}, +{6600000, DIF_BPF_COEFF3031, 0x03f8f78f}, +{6600000, DIF_BPF_COEFF3233, 0xf00af2d7}, +{6600000, DIF_BPF_COEFF3435, 0xfe850b7b}, +{6600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 66_quant.dat*/ + + +/*case 6700000:*/ +/* BEGIN - DIF BPF register values from 67_quant.dat*/ +{6700000, DIF_BPF_COEFF01, 0x0000ffff}, +{6700000, DIF_BPF_COEFF23, 0xfff8fff0}, +{6700000, DIF_BPF_COEFF45, 0xfff80020}, +{6700000, DIF_BPF_COEFF67, 0x00560060}, +{6700000, DIF_BPF_COEFF89, 0x0002ff4e}, +{6700000, DIF_BPF_COEFF1011, 0xfec4ff10}, +{6700000, DIF_BPF_COEFF1213, 0x006d0225}, +{6700000, DIF_BPF_COEFF1415, 0x02d50166}, +{6700000, DIF_BPF_COEFF1617, 0xfe35fb4e}, +{6700000, DIF_BPF_COEFF1819, 0xfb35fee1}, +{6700000, DIF_BPF_COEFF2021, 0x0477080e}, +{6700000, DIF_BPF_COEFF2223, 0x065bff82}, +{6700000, DIF_BPF_COEFF2425, 0xf7b1f4a0}, +{6700000, DIF_BPF_COEFF2627, 0xf9610397}, +{6700000, DIF_BPF_COEFF2829, 0x0c810d80}, +{6700000, DIF_BPF_COEFF3031, 0x0510f869}, +{6700000, DIF_BPF_COEFF3233, 0xf033f278}, +{6700000, DIF_BPF_COEFF3435, 0xfe1a0b52}, +{6700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 67_quant.dat*/ + + +/*case 6800000:*/ +/* BEGIN - DIF BPF register values from 68_quant.dat*/ +{6800000, DIF_BPF_COEFF01, 0x00010000}, +{6800000, DIF_BPF_COEFF23, 0xfffaffee}, +{6800000, DIF_BPF_COEFF45, 0xffec000c}, +{6800000, DIF_BPF_COEFF67, 0x004c0078}, +{6800000, DIF_BPF_COEFF89, 0x0040ff8e}, +{6800000, DIF_BPF_COEFF1011, 0xfecafeb6}, +{6800000, DIF_BPF_COEFF1213, 0xffd301b6}, +{6800000, DIF_BPF_COEFF1415, 0x02fc0235}, +{6800000, DIF_BPF_COEFF1617, 0xff36fbc5}, +{6800000, DIF_BPF_COEFF1819, 0xfaaafd90}, +{6800000, DIF_BPF_COEFF2021, 0x033e07d2}, +{6800000, DIF_BPF_COEFF2223, 0x075b011b}, +{6800000, DIF_BPF_COEFF2425, 0xf8cbf47a}, +{6800000, DIF_BPF_COEFF2627, 0xf81f0224}, +{6800000, DIF_BPF_COEFF2829, 0x0bd50def}, +{6800000, DIF_BPF_COEFF3031, 0x0621f94b}, +{6800000, DIF_BPF_COEFF3233, 0xf067f21e}, +{6800000, DIF_BPF_COEFF3435, 0xfdae0b29}, +{6800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 68_quant.dat*/ + + +/*case 6900000:*/ +/* BEGIN - DIF BPF register values from 69_quant.dat*/ +{6900000, DIF_BPF_COEFF01, 0x00010001}, +{6900000, DIF_BPF_COEFF23, 0xfffdffef}, +{6900000, DIF_BPF_COEFF45, 0xffe3fff6}, +{6900000, DIF_BPF_COEFF67, 0x0037007f}, +{6900000, DIF_BPF_COEFF89, 0x0075ffdc}, +{6900000, DIF_BPF_COEFF1011, 0xfef2fe7c}, +{6900000, DIF_BPF_COEFF1213, 0xff3d0122}, +{6900000, DIF_BPF_COEFF1415, 0x02ea02dd}, +{6900000, DIF_BPF_COEFF1617, 0x0044fc79}, +{6900000, DIF_BPF_COEFF1819, 0xfa65fc5d}, +{6900000, DIF_BPF_COEFF2021, 0x01e3074e}, +{6900000, DIF_BPF_COEFF2223, 0x082102ad}, +{6900000, DIF_BPF_COEFF2425, 0xfa0ff48c}, +{6900000, DIF_BPF_COEFF2627, 0xf6fe00a9}, +{6900000, DIF_BPF_COEFF2829, 0x0b0a0e43}, +{6900000, DIF_BPF_COEFF3031, 0x0729fa33}, +{6900000, DIF_BPF_COEFF3233, 0xf0a5f1c9}, +{6900000, DIF_BPF_COEFF3435, 0xfd430b00}, +{6900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 69_quant.dat*/ + + +/*case 7000000:*/ +/* BEGIN - DIF BPF register values from 70_quant.dat*/ +{7000000, DIF_BPF_COEFF01, 0x00010002}, +{7000000, DIF_BPF_COEFF23, 0x0001fff3}, +{7000000, DIF_BPF_COEFF45, 0xffdeffe2}, +{7000000, DIF_BPF_COEFF67, 0x001b0076}, +{7000000, DIF_BPF_COEFF89, 0x009c002d}, +{7000000, DIF_BPF_COEFF1011, 0xff35fe68}, +{7000000, DIF_BPF_COEFF1213, 0xfeba0076}, +{7000000, DIF_BPF_COEFF1415, 0x029f0352}, +{7000000, DIF_BPF_COEFF1617, 0x014dfd60}, +{7000000, DIF_BPF_COEFF1819, 0xfa69fb53}, +{7000000, DIF_BPF_COEFF2021, 0x00740688}, +{7000000, DIF_BPF_COEFF2223, 0x08a7042d}, +{7000000, DIF_BPF_COEFF2425, 0xfb75f4d6}, +{7000000, DIF_BPF_COEFF2627, 0xf600ff2d}, +{7000000, DIF_BPF_COEFF2829, 0x0a220e7a}, +{7000000, DIF_BPF_COEFF3031, 0x0827fb22}, +{7000000, DIF_BPF_COEFF3233, 0xf0edf17a}, +{7000000, DIF_BPF_COEFF3435, 0xfcd80ad6}, +{7000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 70_quant.dat*/ + + +/*case 7100000:*/ +/* BEGIN - DIF BPF register values from 71_quant.dat*/ +{7100000, DIF_BPF_COEFF01, 0x00000003}, +{7100000, DIF_BPF_COEFF23, 0x0004fff9}, +{7100000, DIF_BPF_COEFF45, 0xffe0ffd2}, +{7100000, DIF_BPF_COEFF67, 0xfffb005e}, +{7100000, DIF_BPF_COEFF89, 0x00b0007a}, +{7100000, DIF_BPF_COEFF1011, 0xff8ffe7c}, +{7100000, DIF_BPF_COEFF1213, 0xfe53ffc1}, +{7100000, DIF_BPF_COEFF1415, 0x0221038c}, +{7100000, DIF_BPF_COEFF1617, 0x0241fe6e}, +{7100000, DIF_BPF_COEFF1819, 0xfab6fa80}, +{7100000, DIF_BPF_COEFF2021, 0xff010587}, +{7100000, DIF_BPF_COEFF2223, 0x08e90590}, +{7100000, DIF_BPF_COEFF2425, 0xfcf5f556}, +{7100000, DIF_BPF_COEFF2627, 0xf52bfdb3}, +{7100000, DIF_BPF_COEFF2829, 0x09210e95}, +{7100000, DIF_BPF_COEFF3031, 0x0919fc15}, +{7100000, DIF_BPF_COEFF3233, 0xf13ff12f}, +{7100000, DIF_BPF_COEFF3435, 0xfc6e0aab}, +{7100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 71_quant.dat*/ + + +/*case 7200000:*/ +/* BEGIN - DIF BPF register values from 72_quant.dat*/ +{7200000, DIF_BPF_COEFF01, 0x00000003}, +{7200000, DIF_BPF_COEFF23, 0x00070000}, +{7200000, DIF_BPF_COEFF45, 0xffe6ffc9}, +{7200000, DIF_BPF_COEFF67, 0xffdb0039}, +{7200000, DIF_BPF_COEFF89, 0x00af00b8}, +{7200000, DIF_BPF_COEFF1011, 0xfff4feb6}, +{7200000, DIF_BPF_COEFF1213, 0xfe13ff10}, +{7200000, DIF_BPF_COEFF1415, 0x01790388}, +{7200000, DIF_BPF_COEFF1617, 0x0311ff92}, +{7200000, DIF_BPF_COEFF1819, 0xfb48f9ed}, +{7200000, DIF_BPF_COEFF2021, 0xfd980453}, +{7200000, DIF_BPF_COEFF2223, 0x08e306cd}, +{7200000, DIF_BPF_COEFF2425, 0xfe88f60a}, +{7200000, DIF_BPF_COEFF2627, 0xf482fc40}, +{7200000, DIF_BPF_COEFF2829, 0x08080e93}, +{7200000, DIF_BPF_COEFF3031, 0x09fdfd0c}, +{7200000, DIF_BPF_COEFF3233, 0xf19af0ea}, +{7200000, DIF_BPF_COEFF3435, 0xfc050a81}, +{7200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 72_quant.dat*/ + + +/*case 7300000:*/ +/* BEGIN - DIF BPF register values from 73_quant.dat*/ +{7300000, DIF_BPF_COEFF01, 0x00000002}, +{7300000, DIF_BPF_COEFF23, 0x00080008}, +{7300000, DIF_BPF_COEFF45, 0xfff0ffc9}, +{7300000, DIF_BPF_COEFF67, 0xffc1000d}, +{7300000, DIF_BPF_COEFF89, 0x009800e2}, +{7300000, DIF_BPF_COEFF1011, 0x005bff10}, +{7300000, DIF_BPF_COEFF1213, 0xfe00fe74}, +{7300000, DIF_BPF_COEFF1415, 0x00b50345}, +{7300000, DIF_BPF_COEFF1617, 0x03b000bc}, +{7300000, DIF_BPF_COEFF1819, 0xfc18f9a1}, +{7300000, DIF_BPF_COEFF2021, 0xfc4802f9}, +{7300000, DIF_BPF_COEFF2223, 0x089807dc}, +{7300000, DIF_BPF_COEFF2425, 0x0022f6f0}, +{7300000, DIF_BPF_COEFF2627, 0xf407fada}, +{7300000, DIF_BPF_COEFF2829, 0x06da0e74}, +{7300000, DIF_BPF_COEFF3031, 0x0ad3fe06}, +{7300000, DIF_BPF_COEFF3233, 0xf1fef0ab}, +{7300000, DIF_BPF_COEFF3435, 0xfb9c0a55}, +{7300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 73_quant.dat*/ + + +/*case 7400000:*/ +/* BEGIN - DIF BPF register values from 74_quant.dat*/ +{7400000, DIF_BPF_COEFF01, 0x00000001}, +{7400000, DIF_BPF_COEFF23, 0x0008000e}, +{7400000, DIF_BPF_COEFF45, 0xfffdffd0}, +{7400000, DIF_BPF_COEFF67, 0xffafffdf}, +{7400000, DIF_BPF_COEFF89, 0x006e00f2}, +{7400000, DIF_BPF_COEFF1011, 0x00b8ff82}, +{7400000, DIF_BPF_COEFF1213, 0xfe1bfdf8}, +{7400000, DIF_BPF_COEFF1415, 0xffe302c8}, +{7400000, DIF_BPF_COEFF1617, 0x041301dc}, +{7400000, DIF_BPF_COEFF1819, 0xfd1af99e}, +{7400000, DIF_BPF_COEFF2021, 0xfb1e0183}, +{7400000, DIF_BPF_COEFF2223, 0x080908b5}, +{7400000, DIF_BPF_COEFF2425, 0x01bcf801}, +{7400000, DIF_BPF_COEFF2627, 0xf3bdf985}, +{7400000, DIF_BPF_COEFF2829, 0x059a0e38}, +{7400000, DIF_BPF_COEFF3031, 0x0b99ff03}, +{7400000, DIF_BPF_COEFF3233, 0xf26cf071}, +{7400000, DIF_BPF_COEFF3435, 0xfb330a2a}, +{7400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 74_quant.dat*/ + + +/*case 7500000:*/ +/* BEGIN - DIF BPF register values from 75_quant.dat*/ +{7500000, DIF_BPF_COEFF01, 0xffff0000}, +{7500000, DIF_BPF_COEFF23, 0x00070011}, +{7500000, DIF_BPF_COEFF45, 0x000affdf}, +{7500000, DIF_BPF_COEFF67, 0xffa9ffb5}, +{7500000, DIF_BPF_COEFF89, 0x003700e6}, +{7500000, DIF_BPF_COEFF1011, 0x01010000}, +{7500000, DIF_BPF_COEFF1213, 0xfe62fda8}, +{7500000, DIF_BPF_COEFF1415, 0xff140219}, +{7500000, DIF_BPF_COEFF1617, 0x043502e1}, +{7500000, DIF_BPF_COEFF1819, 0xfe42f9e6}, +{7500000, DIF_BPF_COEFF2021, 0xfa270000}, +{7500000, DIF_BPF_COEFF2223, 0x073a0953}, +{7500000, DIF_BPF_COEFF2425, 0x034cf939}, +{7500000, DIF_BPF_COEFF2627, 0xf3a4f845}, +{7500000, DIF_BPF_COEFF2829, 0x044c0de1}, +{7500000, DIF_BPF_COEFF3031, 0x0c4f0000}, +{7500000, DIF_BPF_COEFF3233, 0xf2e2f03c}, +{7500000, DIF_BPF_COEFF3435, 0xfacc09fe}, +{7500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 75_quant.dat*/ + + +/*case 7600000:*/ +/* BEGIN - DIF BPF register values from 76_quant.dat*/ +{7600000, DIF_BPF_COEFF01, 0xffffffff}, +{7600000, DIF_BPF_COEFF23, 0x00040012}, +{7600000, DIF_BPF_COEFF45, 0x0016fff3}, +{7600000, DIF_BPF_COEFF67, 0xffafff95}, +{7600000, DIF_BPF_COEFF89, 0xfff900c0}, +{7600000, DIF_BPF_COEFF1011, 0x0130007e}, +{7600000, DIF_BPF_COEFF1213, 0xfecefd89}, +{7600000, DIF_BPF_COEFF1415, 0xfe560146}, +{7600000, DIF_BPF_COEFF1617, 0x041303bc}, +{7600000, DIF_BPF_COEFF1819, 0xff81fa76}, +{7600000, DIF_BPF_COEFF2021, 0xf96cfe7d}, +{7600000, DIF_BPF_COEFF2223, 0x063209b1}, +{7600000, DIF_BPF_COEFF2425, 0x04c9fa93}, +{7600000, DIF_BPF_COEFF2627, 0xf3bdf71e}, +{7600000, DIF_BPF_COEFF2829, 0x02f30d6e}, +{7600000, DIF_BPF_COEFF3031, 0x0cf200fd}, +{7600000, DIF_BPF_COEFF3233, 0xf361f00e}, +{7600000, DIF_BPF_COEFF3435, 0xfa6509d1}, +{7600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 76_quant.dat*/ + + +/*case 7700000:*/ +/* BEGIN - DIF BPF register values from 77_quant.dat*/ +{7700000, DIF_BPF_COEFF01, 0xfffffffe}, +{7700000, DIF_BPF_COEFF23, 0x00010010}, +{7700000, DIF_BPF_COEFF45, 0x001e0008}, +{7700000, DIF_BPF_COEFF67, 0xffc1ff84}, +{7700000, DIF_BPF_COEFF89, 0xffbc0084}, +{7700000, DIF_BPF_COEFF1011, 0x013e00f0}, +{7700000, DIF_BPF_COEFF1213, 0xff56fd9f}, +{7700000, DIF_BPF_COEFF1415, 0xfdb8005c}, +{7700000, DIF_BPF_COEFF1617, 0x03b00460}, +{7700000, DIF_BPF_COEFF1819, 0x00c7fb45}, +{7700000, DIF_BPF_COEFF2021, 0xf8f4fd07}, +{7700000, DIF_BPF_COEFF2223, 0x04fa09ce}, +{7700000, DIF_BPF_COEFF2425, 0x062afc07}, +{7700000, DIF_BPF_COEFF2627, 0xf407f614}, +{7700000, DIF_BPF_COEFF2829, 0x01920ce0}, +{7700000, DIF_BPF_COEFF3031, 0x0d8301fa}, +{7700000, DIF_BPF_COEFF3233, 0xf3e8efe5}, +{7700000, DIF_BPF_COEFF3435, 0xfa0009a4}, +{7700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 77_quant.dat*/ + + +/*case 7800000:*/ +/* BEGIN - DIF BPF register values from 78_quant.dat*/ +{7800000, DIF_BPF_COEFF01, 0x0000fffd}, +{7800000, DIF_BPF_COEFF23, 0xfffd000b}, +{7800000, DIF_BPF_COEFF45, 0x0022001d}, +{7800000, DIF_BPF_COEFF67, 0xffdbff82}, +{7800000, DIF_BPF_COEFF89, 0xff870039}, +{7800000, DIF_BPF_COEFF1011, 0x012a014a}, +{7800000, DIF_BPF_COEFF1213, 0xffedfde7}, +{7800000, DIF_BPF_COEFF1415, 0xfd47ff6b}, +{7800000, DIF_BPF_COEFF1617, 0x031104c6}, +{7800000, DIF_BPF_COEFF1819, 0x0202fc4c}, +{7800000, DIF_BPF_COEFF2021, 0xf8c6fbad}, +{7800000, DIF_BPF_COEFF2223, 0x039909a7}, +{7800000, DIF_BPF_COEFF2425, 0x0767fd8e}, +{7800000, DIF_BPF_COEFF2627, 0xf482f52b}, +{7800000, DIF_BPF_COEFF2829, 0x002d0c39}, +{7800000, DIF_BPF_COEFF3031, 0x0e0002f4}, +{7800000, DIF_BPF_COEFF3233, 0xf477efc2}, +{7800000, DIF_BPF_COEFF3435, 0xf99b0977}, +{7800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 78_quant.dat*/ + + +/*case 7900000:*/ +/* BEGIN - DIF BPF register values from 79_quant.dat*/ +{7900000, DIF_BPF_COEFF01, 0x0000fffd}, +{7900000, DIF_BPF_COEFF23, 0xfffa0004}, +{7900000, DIF_BPF_COEFF45, 0x0020002d}, +{7900000, DIF_BPF_COEFF67, 0xfffbff91}, +{7900000, DIF_BPF_COEFF89, 0xff61ffe8}, +{7900000, DIF_BPF_COEFF1011, 0x00f70184}, +{7900000, DIF_BPF_COEFF1213, 0x0086fe5c}, +{7900000, DIF_BPF_COEFF1415, 0xfd0bfe85}, +{7900000, DIF_BPF_COEFF1617, 0x024104e5}, +{7900000, DIF_BPF_COEFF1819, 0x0323fd7d}, +{7900000, DIF_BPF_COEFF2021, 0xf8e2fa79}, +{7900000, DIF_BPF_COEFF2223, 0x021d093f}, +{7900000, DIF_BPF_COEFF2425, 0x0879ff22}, +{7900000, DIF_BPF_COEFF2627, 0xf52bf465}, +{7900000, DIF_BPF_COEFF2829, 0xfec70b79}, +{7900000, DIF_BPF_COEFF3031, 0x0e6803eb}, +{7900000, DIF_BPF_COEFF3233, 0xf50defa5}, +{7900000, DIF_BPF_COEFF3435, 0xf937094a}, +{7900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 79_quant.dat*/ + + +/*case 8000000:*/ +/* BEGIN - DIF BPF register values from 80_quant.dat*/ +{8000000, DIF_BPF_COEFF01, 0x0000fffe}, +{8000000, DIF_BPF_COEFF23, 0xfff8fffd}, +{8000000, DIF_BPF_COEFF45, 0x00190036}, +{8000000, DIF_BPF_COEFF67, 0x001bffaf}, +{8000000, DIF_BPF_COEFF89, 0xff4fff99}, +{8000000, DIF_BPF_COEFF1011, 0x00aa0198}, +{8000000, DIF_BPF_COEFF1213, 0x0112fef3}, +{8000000, DIF_BPF_COEFF1415, 0xfd09fdb9}, +{8000000, DIF_BPF_COEFF1617, 0x014d04be}, +{8000000, DIF_BPF_COEFF1819, 0x041bfecc}, +{8000000, DIF_BPF_COEFF2021, 0xf947f978}, +{8000000, DIF_BPF_COEFF2223, 0x00900897}, +{8000000, DIF_BPF_COEFF2425, 0x095a00b9}, +{8000000, DIF_BPF_COEFF2627, 0xf600f3c5}, +{8000000, DIF_BPF_COEFF2829, 0xfd650aa3}, +{8000000, DIF_BPF_COEFF3031, 0x0ebc04de}, +{8000000, DIF_BPF_COEFF3233, 0xf5aaef8e}, +{8000000, DIF_BPF_COEFF3435, 0xf8d5091c}, +{8000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 80_quant.dat*/ + + +/*case 8100000:*/ +/* BEGIN - DIF BPF register values from 81_quant.dat*/ +{8100000, DIF_BPF_COEFF01, 0x0000ffff}, +{8100000, DIF_BPF_COEFF23, 0xfff7fff6}, +{8100000, DIF_BPF_COEFF45, 0x000e0038}, +{8100000, DIF_BPF_COEFF67, 0x0037ffd7}, +{8100000, DIF_BPF_COEFF89, 0xff52ff56}, +{8100000, DIF_BPF_COEFF1011, 0x004b0184}, +{8100000, DIF_BPF_COEFF1213, 0x0186ffa1}, +{8100000, DIF_BPF_COEFF1415, 0xfd40fd16}, +{8100000, DIF_BPF_COEFF1617, 0x00440452}, +{8100000, DIF_BPF_COEFF1819, 0x04de0029}, +{8100000, DIF_BPF_COEFF2021, 0xf9f2f8b2}, +{8100000, DIF_BPF_COEFF2223, 0xfefe07b5}, +{8100000, DIF_BPF_COEFF2425, 0x0a05024d}, +{8100000, DIF_BPF_COEFF2627, 0xf6fef34d}, +{8100000, DIF_BPF_COEFF2829, 0xfc0a09b8}, +{8100000, DIF_BPF_COEFF3031, 0x0efa05cd}, +{8100000, DIF_BPF_COEFF3233, 0xf64eef7d}, +{8100000, DIF_BPF_COEFF3435, 0xf87308ed}, +{8100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 81_quant.dat*/ + + +/*case 8200000:*/ +/* BEGIN - DIF BPF register values from 82_quant.dat*/ +{8200000, DIF_BPF_COEFF01, 0x00010000}, +{8200000, DIF_BPF_COEFF23, 0xfff8fff0}, +{8200000, DIF_BPF_COEFF45, 0x00000031}, +{8200000, DIF_BPF_COEFF67, 0x004c0005}, +{8200000, DIF_BPF_COEFF89, 0xff6aff27}, +{8200000, DIF_BPF_COEFF1011, 0xffe4014a}, +{8200000, DIF_BPF_COEFF1213, 0x01d70057}, +{8200000, DIF_BPF_COEFF1415, 0xfdacfca6}, +{8200000, DIF_BPF_COEFF1617, 0xff3603a7}, +{8200000, DIF_BPF_COEFF1819, 0x05610184}, +{8200000, DIF_BPF_COEFF2021, 0xfadbf82e}, +{8200000, DIF_BPF_COEFF2223, 0xfd74069f}, +{8200000, DIF_BPF_COEFF2425, 0x0a7503d6}, +{8200000, DIF_BPF_COEFF2627, 0xf81ff2ff}, +{8200000, DIF_BPF_COEFF2829, 0xfab808b9}, +{8200000, DIF_BPF_COEFF3031, 0x0f2306b5}, +{8200000, DIF_BPF_COEFF3233, 0xf6f9ef72}, +{8200000, DIF_BPF_COEFF3435, 0xf81308bf}, +{8200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 82_quant.dat*/ + + +/*case 8300000:*/ +/* BEGIN - DIF BPF register values from 83_quant.dat*/ +{8300000, DIF_BPF_COEFF01, 0x00010001}, +{8300000, DIF_BPF_COEFF23, 0xfffbffee}, +{8300000, DIF_BPF_COEFF45, 0xfff30022}, +{8300000, DIF_BPF_COEFF67, 0x00560032}, +{8300000, DIF_BPF_COEFF89, 0xff95ff10}, +{8300000, DIF_BPF_COEFF1011, 0xff8000f0}, +{8300000, DIF_BPF_COEFF1213, 0x01fe0106}, +{8300000, DIF_BPF_COEFF1415, 0xfe46fc71}, +{8300000, DIF_BPF_COEFF1617, 0xfe3502c7}, +{8300000, DIF_BPF_COEFF1819, 0x059e02ce}, +{8300000, DIF_BPF_COEFF2021, 0xfbf9f7f2}, +{8300000, DIF_BPF_COEFF2223, 0xfbff055b}, +{8300000, DIF_BPF_COEFF2425, 0x0aa9054c}, +{8300000, DIF_BPF_COEFF2627, 0xf961f2db}, +{8300000, DIF_BPF_COEFF2829, 0xf97507aa}, +{8300000, DIF_BPF_COEFF3031, 0x0f350797}, +{8300000, DIF_BPF_COEFF3233, 0xf7a9ef6d}, +{8300000, DIF_BPF_COEFF3435, 0xf7b40890}, +{8300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 83_quant.dat*/ + + +/*case 8400000:*/ +/* BEGIN - DIF BPF register values from 84_quant.dat*/ +{8400000, DIF_BPF_COEFF01, 0x00010002}, +{8400000, DIF_BPF_COEFF23, 0xfffeffee}, +{8400000, DIF_BPF_COEFF45, 0xffe8000f}, +{8400000, DIF_BPF_COEFF67, 0x00540058}, +{8400000, DIF_BPF_COEFF89, 0xffcdff14}, +{8400000, DIF_BPF_COEFF1011, 0xff29007e}, +{8400000, DIF_BPF_COEFF1213, 0x01f6019e}, +{8400000, DIF_BPF_COEFF1415, 0xff01fc7c}, +{8400000, DIF_BPF_COEFF1617, 0xfd5101bf}, +{8400000, DIF_BPF_COEFF1819, 0x059203f6}, +{8400000, DIF_BPF_COEFF2021, 0xfd41f7fe}, +{8400000, DIF_BPF_COEFF2223, 0xfaa903f3}, +{8400000, DIF_BPF_COEFF2425, 0x0a9e06a9}, +{8400000, DIF_BPF_COEFF2627, 0xfabdf2e2}, +{8400000, DIF_BPF_COEFF2829, 0xf842068b}, +{8400000, DIF_BPF_COEFF3031, 0x0f320871}, +{8400000, DIF_BPF_COEFF3233, 0xf85eef6e}, +{8400000, DIF_BPF_COEFF3435, 0xf7560860}, +{8400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 84_quant.dat*/ + + +/*case 8500000:*/ +/* BEGIN - DIF BPF register values from 85_quant.dat*/ +{8500000, DIF_BPF_COEFF01, 0x00000003}, +{8500000, DIF_BPF_COEFF23, 0x0002fff2}, +{8500000, DIF_BPF_COEFF45, 0xffe1fff9}, +{8500000, DIF_BPF_COEFF67, 0x00460073}, +{8500000, DIF_BPF_COEFF89, 0x000bff34}, +{8500000, DIF_BPF_COEFF1011, 0xfee90000}, +{8500000, DIF_BPF_COEFF1213, 0x01c10215}, +{8500000, DIF_BPF_COEFF1415, 0xffd0fcc5}, +{8500000, DIF_BPF_COEFF1617, 0xfc99009d}, +{8500000, DIF_BPF_COEFF1819, 0x053d04f1}, +{8500000, DIF_BPF_COEFF2021, 0xfea5f853}, +{8500000, DIF_BPF_COEFF2223, 0xf97d0270}, +{8500000, DIF_BPF_COEFF2425, 0x0a5607e4}, +{8500000, DIF_BPF_COEFF2627, 0xfc2ef314}, +{8500000, DIF_BPF_COEFF2829, 0xf723055f}, +{8500000, DIF_BPF_COEFF3031, 0x0f180943}, +{8500000, DIF_BPF_COEFF3233, 0xf919ef75}, +{8500000, DIF_BPF_COEFF3435, 0xf6fa0830}, +{8500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 85_quant.dat*/ + + +/*case 8600000:*/ +/* BEGIN - DIF BPF register values from 86_quant.dat*/ +{8600000, DIF_BPF_COEFF01, 0x00000003}, +{8600000, DIF_BPF_COEFF23, 0x0005fff8}, +{8600000, DIF_BPF_COEFF45, 0xffdeffe4}, +{8600000, DIF_BPF_COEFF67, 0x002f007f}, +{8600000, DIF_BPF_COEFF89, 0x0048ff6b}, +{8600000, DIF_BPF_COEFF1011, 0xfec7ff82}, +{8600000, DIF_BPF_COEFF1213, 0x0163025f}, +{8600000, DIF_BPF_COEFF1415, 0x00a2fd47}, +{8600000, DIF_BPF_COEFF1617, 0xfc17ff73}, +{8600000, DIF_BPF_COEFF1819, 0x04a405b2}, +{8600000, DIF_BPF_COEFF2021, 0x0017f8ed}, +{8600000, DIF_BPF_COEFF2223, 0xf88500dc}, +{8600000, DIF_BPF_COEFF2425, 0x09d208f9}, +{8600000, DIF_BPF_COEFF2627, 0xfdaff370}, +{8600000, DIF_BPF_COEFF2829, 0xf61c0429}, +{8600000, DIF_BPF_COEFF3031, 0x0ee80a0b}, +{8600000, DIF_BPF_COEFF3233, 0xf9d8ef82}, +{8600000, DIF_BPF_COEFF3435, 0xf6a00800}, +{8600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 86_quant.dat*/ + + +/*case 8700000:*/ +/* BEGIN - DIF BPF register values from 87_quant.dat*/ +{8700000, DIF_BPF_COEFF01, 0x00000003}, +{8700000, DIF_BPF_COEFF23, 0x0007ffff}, +{8700000, DIF_BPF_COEFF45, 0xffe1ffd4}, +{8700000, DIF_BPF_COEFF67, 0x0010007a}, +{8700000, DIF_BPF_COEFF89, 0x007cffb2}, +{8700000, DIF_BPF_COEFF1011, 0xfec6ff10}, +{8700000, DIF_BPF_COEFF1213, 0x00e60277}, +{8700000, DIF_BPF_COEFF1415, 0x0168fdf9}, +{8700000, DIF_BPF_COEFF1617, 0xfbd3fe50}, +{8700000, DIF_BPF_COEFF1819, 0x03ce0631}, +{8700000, DIF_BPF_COEFF2021, 0x0188f9c8}, +{8700000, DIF_BPF_COEFF2223, 0xf7c7ff43}, +{8700000, DIF_BPF_COEFF2425, 0x091509e3}, +{8700000, DIF_BPF_COEFF2627, 0xff39f3f6}, +{8700000, DIF_BPF_COEFF2829, 0xf52d02ea}, +{8700000, DIF_BPF_COEFF3031, 0x0ea30ac9}, +{8700000, DIF_BPF_COEFF3233, 0xfa9bef95}, +{8700000, DIF_BPF_COEFF3435, 0xf64607d0}, +{8700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 87_quant.dat*/ + + +/*case 8800000:*/ +/* BEGIN - DIF BPF register values from 88_quant.dat*/ +{8800000, DIF_BPF_COEFF01, 0x00000002}, +{8800000, DIF_BPF_COEFF23, 0x00090007}, +{8800000, DIF_BPF_COEFF45, 0xffe9ffca}, +{8800000, DIF_BPF_COEFF67, 0xfff00065}, +{8800000, DIF_BPF_COEFF89, 0x00a10003}, +{8800000, DIF_BPF_COEFF1011, 0xfee6feb6}, +{8800000, DIF_BPF_COEFF1213, 0x0053025b}, +{8800000, DIF_BPF_COEFF1415, 0x0213fed0}, +{8800000, DIF_BPF_COEFF1617, 0xfbd3fd46}, +{8800000, DIF_BPF_COEFF1819, 0x02c70668}, +{8800000, DIF_BPF_COEFF2021, 0x02eafadb}, +{8800000, DIF_BPF_COEFF2223, 0xf74bfdae}, +{8800000, DIF_BPF_COEFF2425, 0x08230a9c}, +{8800000, DIF_BPF_COEFF2627, 0x00c7f4a3}, +{8800000, DIF_BPF_COEFF2829, 0xf45b01a6}, +{8800000, DIF_BPF_COEFF3031, 0x0e480b7c}, +{8800000, DIF_BPF_COEFF3233, 0xfb61efae}, +{8800000, DIF_BPF_COEFF3435, 0xf5ef079f}, +{8800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 88_quant.dat*/ + + +/*case 8900000:*/ +/* BEGIN - DIF BPF register values from 89_quant.dat*/ +{8900000, DIF_BPF_COEFF01, 0xffff0000}, +{8900000, DIF_BPF_COEFF23, 0x0008000d}, +{8900000, DIF_BPF_COEFF45, 0xfff5ffc8}, +{8900000, DIF_BPF_COEFF67, 0xffd10043}, +{8900000, DIF_BPF_COEFF89, 0x00b20053}, +{8900000, DIF_BPF_COEFF1011, 0xff24fe7c}, +{8900000, DIF_BPF_COEFF1213, 0xffb9020c}, +{8900000, DIF_BPF_COEFF1415, 0x0295ffbb}, +{8900000, DIF_BPF_COEFF1617, 0xfc17fc64}, +{8900000, DIF_BPF_COEFF1819, 0x019b0654}, +{8900000, DIF_BPF_COEFF2021, 0x042dfc1c}, +{8900000, DIF_BPF_COEFF2223, 0xf714fc2a}, +{8900000, DIF_BPF_COEFF2425, 0x07020b21}, +{8900000, DIF_BPF_COEFF2627, 0x0251f575}, +{8900000, DIF_BPF_COEFF2829, 0xf3a7005e}, +{8900000, DIF_BPF_COEFF3031, 0x0dd80c24}, +{8900000, DIF_BPF_COEFF3233, 0xfc2aefcd}, +{8900000, DIF_BPF_COEFF3435, 0xf599076e}, +{8900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 89_quant.dat*/ + + +/*case 9000000:*/ +/* BEGIN - DIF BPF register values from 90_quant.dat*/ +{9000000, DIF_BPF_COEFF01, 0xffffffff}, +{9000000, DIF_BPF_COEFF23, 0x00060011}, +{9000000, DIF_BPF_COEFF45, 0x0002ffcf}, +{9000000, DIF_BPF_COEFF67, 0xffba0018}, +{9000000, DIF_BPF_COEFF89, 0x00ad009a}, +{9000000, DIF_BPF_COEFF1011, 0xff79fe68}, +{9000000, DIF_BPF_COEFF1213, 0xff260192}, +{9000000, DIF_BPF_COEFF1415, 0x02e500ab}, +{9000000, DIF_BPF_COEFF1617, 0xfc99fbb6}, +{9000000, DIF_BPF_COEFF1819, 0x005b05f7}, +{9000000, DIF_BPF_COEFF2021, 0x0545fd81}, +{9000000, DIF_BPF_COEFF2223, 0xf723fabf}, +{9000000, DIF_BPF_COEFF2425, 0x05b80b70}, +{9000000, DIF_BPF_COEFF2627, 0x03d2f669}, +{9000000, DIF_BPF_COEFF2829, 0xf313ff15}, +{9000000, DIF_BPF_COEFF3031, 0x0d550cbf}, +{9000000, DIF_BPF_COEFF3233, 0xfcf6eff2}, +{9000000, DIF_BPF_COEFF3435, 0xf544073d}, +{9000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 90_quant.dat*/ + + +/*case 9100000:*/ +/* BEGIN - DIF BPF register values from 91_quant.dat*/ +{9100000, DIF_BPF_COEFF01, 0xfffffffe}, +{9100000, DIF_BPF_COEFF23, 0x00030012}, +{9100000, DIF_BPF_COEFF45, 0x000fffdd}, +{9100000, DIF_BPF_COEFF67, 0xffacffea}, +{9100000, DIF_BPF_COEFF89, 0x009300cf}, +{9100000, DIF_BPF_COEFF1011, 0xffdcfe7c}, +{9100000, DIF_BPF_COEFF1213, 0xfea600f7}, +{9100000, DIF_BPF_COEFF1415, 0x02fd0190}, +{9100000, DIF_BPF_COEFF1617, 0xfd51fb46}, +{9100000, DIF_BPF_COEFF1819, 0xff150554}, +{9100000, DIF_BPF_COEFF2021, 0x0627fefd}, +{9100000, DIF_BPF_COEFF2223, 0xf778f978}, +{9100000, DIF_BPF_COEFF2425, 0x044d0b87}, +{9100000, DIF_BPF_COEFF2627, 0x0543f77d}, +{9100000, DIF_BPF_COEFF2829, 0xf2a0fdcf}, +{9100000, DIF_BPF_COEFF3031, 0x0cbe0d4e}, +{9100000, DIF_BPF_COEFF3233, 0xfdc4f01d}, +{9100000, DIF_BPF_COEFF3435, 0xf4f2070b}, +{9100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 91_quant.dat*/ + + +/*case 9200000:*/ +/* BEGIN - DIF BPF register values from 92_quant.dat*/ +{9200000, DIF_BPF_COEFF01, 0x0000fffd}, +{9200000, DIF_BPF_COEFF23, 0x00000010}, +{9200000, DIF_BPF_COEFF45, 0x001afff0}, +{9200000, DIF_BPF_COEFF67, 0xffaaffbf}, +{9200000, DIF_BPF_COEFF89, 0x006700ed}, +{9200000, DIF_BPF_COEFF1011, 0x0043feb6}, +{9200000, DIF_BPF_COEFF1213, 0xfe460047}, +{9200000, DIF_BPF_COEFF1415, 0x02db0258}, +{9200000, DIF_BPF_COEFF1617, 0xfe35fb1b}, +{9200000, DIF_BPF_COEFF1819, 0xfddc0473}, +{9200000, DIF_BPF_COEFF2021, 0x06c90082}, +{9200000, DIF_BPF_COEFF2223, 0xf811f85e}, +{9200000, DIF_BPF_COEFF2425, 0x02c90b66}, +{9200000, DIF_BPF_COEFF2627, 0x069ff8ad}, +{9200000, DIF_BPF_COEFF2829, 0xf250fc8d}, +{9200000, DIF_BPF_COEFF3031, 0x0c140dcf}, +{9200000, DIF_BPF_COEFF3233, 0xfe93f04d}, +{9200000, DIF_BPF_COEFF3435, 0xf4a106d9}, +{9200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 92_quant.dat*/ + + +/*case 9300000:*/ +/* BEGIN - DIF BPF register values from 93_quant.dat*/ +{9300000, DIF_BPF_COEFF01, 0x0000fffd}, +{9300000, DIF_BPF_COEFF23, 0xfffc000c}, +{9300000, DIF_BPF_COEFF45, 0x00200006}, +{9300000, DIF_BPF_COEFF67, 0xffb4ff9c}, +{9300000, DIF_BPF_COEFF89, 0x002f00ef}, +{9300000, DIF_BPF_COEFF1011, 0x00a4ff10}, +{9300000, DIF_BPF_COEFF1213, 0xfe0dff92}, +{9300000, DIF_BPF_COEFF1415, 0x028102f7}, +{9300000, DIF_BPF_COEFF1617, 0xff36fb37}, +{9300000, DIF_BPF_COEFF1819, 0xfcbf035e}, +{9300000, DIF_BPF_COEFF2021, 0x07260202}, +{9300000, DIF_BPF_COEFF2223, 0xf8e8f778}, +{9300000, DIF_BPF_COEFF2425, 0x01340b0d}, +{9300000, DIF_BPF_COEFF2627, 0x07e1f9f4}, +{9300000, DIF_BPF_COEFF2829, 0xf223fb51}, +{9300000, DIF_BPF_COEFF3031, 0x0b590e42}, +{9300000, DIF_BPF_COEFF3233, 0xff64f083}, +{9300000, DIF_BPF_COEFF3435, 0xf45206a7}, +{9300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 93_quant.dat*/ + + +/*case 9400000:*/ +/* BEGIN - DIF BPF register values from 94_quant.dat*/ +{9400000, DIF_BPF_COEFF01, 0x0000fffd}, +{9400000, DIF_BPF_COEFF23, 0xfff90005}, +{9400000, DIF_BPF_COEFF45, 0x0022001a}, +{9400000, DIF_BPF_COEFF67, 0xffc9ff86}, +{9400000, DIF_BPF_COEFF89, 0xfff000d7}, +{9400000, DIF_BPF_COEFF1011, 0x00f2ff82}, +{9400000, DIF_BPF_COEFF1213, 0xfe01fee5}, +{9400000, DIF_BPF_COEFF1415, 0x01f60362}, +{9400000, DIF_BPF_COEFF1617, 0x0044fb99}, +{9400000, DIF_BPF_COEFF1819, 0xfbcc0222}, +{9400000, DIF_BPF_COEFF2021, 0x07380370}, +{9400000, DIF_BPF_COEFF2223, 0xf9f7f6cc}, +{9400000, DIF_BPF_COEFF2425, 0xff990a7e}, +{9400000, DIF_BPF_COEFF2627, 0x0902fb50}, +{9400000, DIF_BPF_COEFF2829, 0xf21afa1f}, +{9400000, DIF_BPF_COEFF3031, 0x0a8d0ea6}, +{9400000, DIF_BPF_COEFF3233, 0x0034f0bf}, +{9400000, DIF_BPF_COEFF3435, 0xf4050675}, +{9400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 94_quant.dat*/ + + +/*case 9500000:*/ +/* BEGIN - DIF BPF register values from 95_quant.dat*/ +{9500000, DIF_BPF_COEFF01, 0x0000fffe}, +{9500000, DIF_BPF_COEFF23, 0xfff8fffe}, +{9500000, DIF_BPF_COEFF45, 0x001e002b}, +{9500000, DIF_BPF_COEFF67, 0xffe5ff81}, +{9500000, DIF_BPF_COEFF89, 0xffb400a5}, +{9500000, DIF_BPF_COEFF1011, 0x01280000}, +{9500000, DIF_BPF_COEFF1213, 0xfe24fe50}, +{9500000, DIF_BPF_COEFF1415, 0x01460390}, +{9500000, DIF_BPF_COEFF1617, 0x014dfc3a}, +{9500000, DIF_BPF_COEFF1819, 0xfb1000ce}, +{9500000, DIF_BPF_COEFF2021, 0x070104bf}, +{9500000, DIF_BPF_COEFF2223, 0xfb37f65f}, +{9500000, DIF_BPF_COEFF2425, 0xfe0009bc}, +{9500000, DIF_BPF_COEFF2627, 0x0a00fcbb}, +{9500000, DIF_BPF_COEFF2829, 0xf235f8f8}, +{9500000, DIF_BPF_COEFF3031, 0x09b20efc}, +{9500000, DIF_BPF_COEFF3233, 0x0105f101}, +{9500000, DIF_BPF_COEFF3435, 0xf3ba0642}, +{9500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 95_quant.dat*/ + + +/*case 9600000:*/ +/* BEGIN - DIF BPF register values from 96_quant.dat*/ +{9600000, DIF_BPF_COEFF01, 0x0001ffff}, +{9600000, DIF_BPF_COEFF23, 0xfff8fff7}, +{9600000, DIF_BPF_COEFF45, 0x00150036}, +{9600000, DIF_BPF_COEFF67, 0x0005ff8c}, +{9600000, DIF_BPF_COEFF89, 0xff810061}, +{9600000, DIF_BPF_COEFF1011, 0x013d007e}, +{9600000, DIF_BPF_COEFF1213, 0xfe71fddf}, +{9600000, DIF_BPF_COEFF1415, 0x007c0380}, +{9600000, DIF_BPF_COEFF1617, 0x0241fd13}, +{9600000, DIF_BPF_COEFF1819, 0xfa94ff70}, +{9600000, DIF_BPF_COEFF2021, 0x068005e2}, +{9600000, DIF_BPF_COEFF2223, 0xfc9bf633}, +{9600000, DIF_BPF_COEFF2425, 0xfc7308ca}, +{9600000, DIF_BPF_COEFF2627, 0x0ad5fe30}, +{9600000, DIF_BPF_COEFF2829, 0xf274f7e0}, +{9600000, DIF_BPF_COEFF3031, 0x08c90f43}, +{9600000, DIF_BPF_COEFF3233, 0x01d4f147}, +{9600000, DIF_BPF_COEFF3435, 0xf371060f}, +{9600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 96_quant.dat*/ + + +/*case 9700000:*/ +/* BEGIN - DIF BPF register values from 97_quant.dat*/ +{9700000, DIF_BPF_COEFF01, 0x00010001}, +{9700000, DIF_BPF_COEFF23, 0xfff9fff1}, +{9700000, DIF_BPF_COEFF45, 0x00090038}, +{9700000, DIF_BPF_COEFF67, 0x0025ffa7}, +{9700000, DIF_BPF_COEFF89, 0xff5e0012}, +{9700000, DIF_BPF_COEFF1011, 0x013200f0}, +{9700000, DIF_BPF_COEFF1213, 0xfee3fd9b}, +{9700000, DIF_BPF_COEFF1415, 0xffaa0331}, +{9700000, DIF_BPF_COEFF1617, 0x0311fe15}, +{9700000, DIF_BPF_COEFF1819, 0xfa60fe18}, +{9700000, DIF_BPF_COEFF2021, 0x05bd06d1}, +{9700000, DIF_BPF_COEFF2223, 0xfe1bf64a}, +{9700000, DIF_BPF_COEFF2425, 0xfafa07ae}, +{9700000, DIF_BPF_COEFF2627, 0x0b7effab}, +{9700000, DIF_BPF_COEFF2829, 0xf2d5f6d7}, +{9700000, DIF_BPF_COEFF3031, 0x07d30f7a}, +{9700000, DIF_BPF_COEFF3233, 0x02a3f194}, +{9700000, DIF_BPF_COEFF3435, 0xf32905dc}, +{9700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 97_quant.dat*/ + + +/*case 9800000:*/ +/* BEGIN - DIF BPF register values from 98_quant.dat*/ +{9800000, DIF_BPF_COEFF01, 0x00010002}, +{9800000, DIF_BPF_COEFF23, 0xfffcffee}, +{9800000, DIF_BPF_COEFF45, 0xfffb0032}, +{9800000, DIF_BPF_COEFF67, 0x003fffcd}, +{9800000, DIF_BPF_COEFF89, 0xff4effc1}, +{9800000, DIF_BPF_COEFF1011, 0x0106014a}, +{9800000, DIF_BPF_COEFF1213, 0xff6efd8a}, +{9800000, DIF_BPF_COEFF1415, 0xfedd02aa}, +{9800000, DIF_BPF_COEFF1617, 0x03b0ff34}, +{9800000, DIF_BPF_COEFF1819, 0xfa74fcd7}, +{9800000, DIF_BPF_COEFF2021, 0x04bf0781}, +{9800000, DIF_BPF_COEFF2223, 0xffaaf6a3}, +{9800000, DIF_BPF_COEFF2425, 0xf99e066b}, +{9800000, DIF_BPF_COEFF2627, 0x0bf90128}, +{9800000, DIF_BPF_COEFF2829, 0xf359f5e1}, +{9800000, DIF_BPF_COEFF3031, 0x06d20fa2}, +{9800000, DIF_BPF_COEFF3233, 0x0370f1e5}, +{9800000, DIF_BPF_COEFF3435, 0xf2e405a8}, +{9800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 98_quant.dat*/ + + +/*case 9900000:*/ +/* BEGIN - DIF BPF register values from 99_quant.dat*/ +{9900000, DIF_BPF_COEFF01, 0x00000003}, +{9900000, DIF_BPF_COEFF23, 0xffffffee}, +{9900000, DIF_BPF_COEFF45, 0xffef0024}, +{9900000, DIF_BPF_COEFF67, 0x0051fffa}, +{9900000, DIF_BPF_COEFF89, 0xff54ff77}, +{9900000, DIF_BPF_COEFF1011, 0x00be0184}, +{9900000, DIF_BPF_COEFF1213, 0x0006fdad}, +{9900000, DIF_BPF_COEFF1415, 0xfe2701f3}, +{9900000, DIF_BPF_COEFF1617, 0x0413005e}, +{9900000, DIF_BPF_COEFF1819, 0xfad1fbba}, +{9900000, DIF_BPF_COEFF2021, 0x039007ee}, +{9900000, DIF_BPF_COEFF2223, 0x013bf73d}, +{9900000, DIF_BPF_COEFF2425, 0xf868050a}, +{9900000, DIF_BPF_COEFF2627, 0x0c4302a1}, +{9900000, DIF_BPF_COEFF2829, 0xf3fdf4fe}, +{9900000, DIF_BPF_COEFF3031, 0x05c70fba}, +{9900000, DIF_BPF_COEFF3233, 0x043bf23c}, +{9900000, DIF_BPF_COEFF3435, 0xf2a10575}, +{9900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 99_quant.dat*/ + + +/*case 10000000:*/ +/* BEGIN - DIF BPF register values from 100_quant.dat*/ +{10000000, DIF_BPF_COEFF01, 0x00000003}, +{10000000, DIF_BPF_COEFF23, 0x0003fff1}, +{10000000, DIF_BPF_COEFF45, 0xffe50011}, +{10000000, DIF_BPF_COEFF67, 0x00570027}, +{10000000, DIF_BPF_COEFF89, 0xff70ff3c}, +{10000000, DIF_BPF_COEFF1011, 0x00620198}, +{10000000, DIF_BPF_COEFF1213, 0x009efe01}, +{10000000, DIF_BPF_COEFF1415, 0xfd95011a}, +{10000000, DIF_BPF_COEFF1617, 0x04350183}, +{10000000, DIF_BPF_COEFF1819, 0xfb71fad0}, +{10000000, DIF_BPF_COEFF2021, 0x023c0812}, +{10000000, DIF_BPF_COEFF2223, 0x02c3f811}, +{10000000, DIF_BPF_COEFF2425, 0xf75e0390}, +{10000000, DIF_BPF_COEFF2627, 0x0c5c0411}, +{10000000, DIF_BPF_COEFF2829, 0xf4c1f432}, +{10000000, DIF_BPF_COEFF3031, 0x04b30fc1}, +{10000000, DIF_BPF_COEFF3233, 0x0503f297}, +{10000000, DIF_BPF_COEFF3435, 0xf2610541}, +{10000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 100_quant.dat*/ + + +/*case 10100000:*/ +/* BEGIN - DIF BPF register values from 101_quant.dat*/ +{10100000, DIF_BPF_COEFF01, 0x00000003}, +{10100000, DIF_BPF_COEFF23, 0x0006fff7}, +{10100000, DIF_BPF_COEFF45, 0xffdffffc}, +{10100000, DIF_BPF_COEFF67, 0x00510050}, +{10100000, DIF_BPF_COEFF89, 0xff9dff18}, +{10100000, DIF_BPF_COEFF1011, 0xfffc0184}, +{10100000, DIF_BPF_COEFF1213, 0x0128fe80}, +{10100000, DIF_BPF_COEFF1415, 0xfd32002e}, +{10100000, DIF_BPF_COEFF1617, 0x04130292}, +{10100000, DIF_BPF_COEFF1819, 0xfc4dfa21}, +{10100000, DIF_BPF_COEFF2021, 0x00d107ee}, +{10100000, DIF_BPF_COEFF2223, 0x0435f91c}, +{10100000, DIF_BPF_COEFF2425, 0xf6850205}, +{10100000, DIF_BPF_COEFF2627, 0x0c430573}, +{10100000, DIF_BPF_COEFF2829, 0xf5a1f37d}, +{10100000, DIF_BPF_COEFF3031, 0x03990fba}, +{10100000, DIF_BPF_COEFF3233, 0x05c7f2f8}, +{10100000, DIF_BPF_COEFF3435, 0xf222050d}, +{10100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 101_quant.dat*/ + + +/*case 10200000:*/ +/* BEGIN - DIF BPF register values from 102_quant.dat*/ +{10200000, DIF_BPF_COEFF01, 0x00000002}, +{10200000, DIF_BPF_COEFF23, 0x0008fffe}, +{10200000, DIF_BPF_COEFF45, 0xffdfffe7}, +{10200000, DIF_BPF_COEFF67, 0x003f006e}, +{10200000, DIF_BPF_COEFF89, 0xffd6ff0f}, +{10200000, DIF_BPF_COEFF1011, 0xff96014a}, +{10200000, DIF_BPF_COEFF1213, 0x0197ff1f}, +{10200000, DIF_BPF_COEFF1415, 0xfd05ff3e}, +{10200000, DIF_BPF_COEFF1617, 0x03b0037c}, +{10200000, DIF_BPF_COEFF1819, 0xfd59f9b7}, +{10200000, DIF_BPF_COEFF2021, 0xff5d0781}, +{10200000, DIF_BPF_COEFF2223, 0x0585fa56}, +{10200000, DIF_BPF_COEFF2425, 0xf5e4006f}, +{10200000, DIF_BPF_COEFF2627, 0x0bf906c4}, +{10200000, DIF_BPF_COEFF2829, 0xf69df2e0}, +{10200000, DIF_BPF_COEFF3031, 0x02790fa2}, +{10200000, DIF_BPF_COEFF3233, 0x0688f35d}, +{10200000, DIF_BPF_COEFF3435, 0xf1e604d8}, +{10200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 102_quant.dat*/ + + +/*case 10300000:*/ +/* BEGIN - DIF BPF register values from 103_quant.dat*/ +{10300000, DIF_BPF_COEFF01, 0xffff0001}, +{10300000, DIF_BPF_COEFF23, 0x00090005}, +{10300000, DIF_BPF_COEFF45, 0xffe4ffd6}, +{10300000, DIF_BPF_COEFF67, 0x0025007e}, +{10300000, DIF_BPF_COEFF89, 0x0014ff20}, +{10300000, DIF_BPF_COEFF1011, 0xff3c00f0}, +{10300000, DIF_BPF_COEFF1213, 0x01e1ffd0}, +{10300000, DIF_BPF_COEFF1415, 0xfd12fe5c}, +{10300000, DIF_BPF_COEFF1617, 0x03110433}, +{10300000, DIF_BPF_COEFF1819, 0xfe88f996}, +{10300000, DIF_BPF_COEFF2021, 0xfdf106d1}, +{10300000, DIF_BPF_COEFF2223, 0x06aafbb7}, +{10300000, DIF_BPF_COEFF2425, 0xf57efed8}, +{10300000, DIF_BPF_COEFF2627, 0x0b7e07ff}, +{10300000, DIF_BPF_COEFF2829, 0xf7b0f25e}, +{10300000, DIF_BPF_COEFF3031, 0x01560f7a}, +{10300000, DIF_BPF_COEFF3233, 0x0745f3c7}, +{10300000, DIF_BPF_COEFF3435, 0xf1ac04a4}, +{10300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 103_quant.dat*/ + + +/*case 10400000:*/ +/* BEGIN - DIF BPF register values from 104_quant.dat*/ +{10400000, DIF_BPF_COEFF01, 0xffffffff}, +{10400000, DIF_BPF_COEFF23, 0x0008000c}, +{10400000, DIF_BPF_COEFF45, 0xffedffcb}, +{10400000, DIF_BPF_COEFF67, 0x0005007d}, +{10400000, DIF_BPF_COEFF89, 0x0050ff4c}, +{10400000, DIF_BPF_COEFF1011, 0xfef6007e}, +{10400000, DIF_BPF_COEFF1213, 0x01ff0086}, +{10400000, DIF_BPF_COEFF1415, 0xfd58fd97}, +{10400000, DIF_BPF_COEFF1617, 0x024104ad}, +{10400000, DIF_BPF_COEFF1819, 0xffcaf9c0}, +{10400000, DIF_BPF_COEFF2021, 0xfc9905e2}, +{10400000, DIF_BPF_COEFF2223, 0x079afd35}, +{10400000, DIF_BPF_COEFF2425, 0xf555fd46}, +{10400000, DIF_BPF_COEFF2627, 0x0ad50920}, +{10400000, DIF_BPF_COEFF2829, 0xf8d9f1f6}, +{10400000, DIF_BPF_COEFF3031, 0x00310f43}, +{10400000, DIF_BPF_COEFF3233, 0x07fdf435}, +{10400000, DIF_BPF_COEFF3435, 0xf174046f}, +{10400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 104_quant.dat*/ + + +/*case 10500000:*/ +/* BEGIN - DIF BPF register values from 105_quant.dat*/ +{10500000, DIF_BPF_COEFF01, 0xfffffffe}, +{10500000, DIF_BPF_COEFF23, 0x00050011}, +{10500000, DIF_BPF_COEFF45, 0xfffaffc8}, +{10500000, DIF_BPF_COEFF67, 0xffe5006b}, +{10500000, DIF_BPF_COEFF89, 0x0082ff8c}, +{10500000, DIF_BPF_COEFF1011, 0xfecc0000}, +{10500000, DIF_BPF_COEFF1213, 0x01f00130}, +{10500000, DIF_BPF_COEFF1415, 0xfdd2fcfc}, +{10500000, DIF_BPF_COEFF1617, 0x014d04e3}, +{10500000, DIF_BPF_COEFF1819, 0x010efa32}, +{10500000, DIF_BPF_COEFF2021, 0xfb6404bf}, +{10500000, DIF_BPF_COEFF2223, 0x084efec5}, +{10500000, DIF_BPF_COEFF2425, 0xf569fbc2}, +{10500000, DIF_BPF_COEFF2627, 0x0a000a23}, +{10500000, DIF_BPF_COEFF2829, 0xfa15f1ab}, +{10500000, DIF_BPF_COEFF3031, 0xff0b0efc}, +{10500000, DIF_BPF_COEFF3233, 0x08b0f4a7}, +{10500000, DIF_BPF_COEFF3435, 0xf13f043a}, +{10500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 105_quant.dat*/ + + +/*case 10600000:*/ +/* BEGIN - DIF BPF register values from 106_quant.dat*/ +{10600000, DIF_BPF_COEFF01, 0x0000fffd}, +{10600000, DIF_BPF_COEFF23, 0x00020012}, +{10600000, DIF_BPF_COEFF45, 0x0007ffcd}, +{10600000, DIF_BPF_COEFF67, 0xffc9004c}, +{10600000, DIF_BPF_COEFF89, 0x00a4ffd9}, +{10600000, DIF_BPF_COEFF1011, 0xfec3ff82}, +{10600000, DIF_BPF_COEFF1213, 0x01b401c1}, +{10600000, DIF_BPF_COEFF1415, 0xfe76fc97}, +{10600000, DIF_BPF_COEFF1617, 0x004404d2}, +{10600000, DIF_BPF_COEFF1819, 0x0245fae8}, +{10600000, DIF_BPF_COEFF2021, 0xfa5f0370}, +{10600000, DIF_BPF_COEFF2223, 0x08c1005f}, +{10600000, DIF_BPF_COEFF2425, 0xf5bcfa52}, +{10600000, DIF_BPF_COEFF2627, 0x09020b04}, +{10600000, DIF_BPF_COEFF2829, 0xfb60f17b}, +{10600000, DIF_BPF_COEFF3031, 0xfde70ea6}, +{10600000, DIF_BPF_COEFF3233, 0x095df51e}, +{10600000, DIF_BPF_COEFF3435, 0xf10c0405}, +{10600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 106_quant.dat*/ + + +/*case 10700000:*/ +/* BEGIN - DIF BPF register values from 107_quant.dat*/ +{10700000, DIF_BPF_COEFF01, 0x0000fffd}, +{10700000, DIF_BPF_COEFF23, 0xffff0011}, +{10700000, DIF_BPF_COEFF45, 0x0014ffdb}, +{10700000, DIF_BPF_COEFF67, 0xffb40023}, +{10700000, DIF_BPF_COEFF89, 0x00b2002a}, +{10700000, DIF_BPF_COEFF1011, 0xfedbff10}, +{10700000, DIF_BPF_COEFF1213, 0x0150022d}, +{10700000, DIF_BPF_COEFF1415, 0xff38fc6f}, +{10700000, DIF_BPF_COEFF1617, 0xff36047b}, +{10700000, DIF_BPF_COEFF1819, 0x035efbda}, +{10700000, DIF_BPF_COEFF2021, 0xf9940202}, +{10700000, DIF_BPF_COEFF2223, 0x08ee01f5}, +{10700000, DIF_BPF_COEFF2425, 0xf649f8fe}, +{10700000, DIF_BPF_COEFF2627, 0x07e10bc2}, +{10700000, DIF_BPF_COEFF2829, 0xfcb6f169}, +{10700000, DIF_BPF_COEFF3031, 0xfcc60e42}, +{10700000, DIF_BPF_COEFF3233, 0x0a04f599}, +{10700000, DIF_BPF_COEFF3435, 0xf0db03d0}, +{10700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 107_quant.dat*/ + + +/*case 10800000:*/ +/* BEGIN - DIF BPF register values from 108_quant.dat*/ +{10800000, DIF_BPF_COEFF01, 0x0000fffd}, +{10800000, DIF_BPF_COEFF23, 0xfffb000d}, +{10800000, DIF_BPF_COEFF45, 0x001dffed}, +{10800000, DIF_BPF_COEFF67, 0xffaafff5}, +{10800000, DIF_BPF_COEFF89, 0x00aa0077}, +{10800000, DIF_BPF_COEFF1011, 0xff13feb6}, +{10800000, DIF_BPF_COEFF1213, 0x00ce026b}, +{10800000, DIF_BPF_COEFF1415, 0x000afc85}, +{10800000, DIF_BPF_COEFF1617, 0xfe3503e3}, +{10800000, DIF_BPF_COEFF1819, 0x044cfcfb}, +{10800000, DIF_BPF_COEFF2021, 0xf90c0082}, +{10800000, DIF_BPF_COEFF2223, 0x08d5037f}, +{10800000, DIF_BPF_COEFF2425, 0xf710f7cc}, +{10800000, DIF_BPF_COEFF2627, 0x069f0c59}, +{10800000, DIF_BPF_COEFF2829, 0xfe16f173}, +{10800000, DIF_BPF_COEFF3031, 0xfbaa0dcf}, +{10800000, DIF_BPF_COEFF3233, 0x0aa5f617}, +{10800000, DIF_BPF_COEFF3435, 0xf0ad039b}, +{10800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 108_quant.dat*/ + + +/*case 10900000:*/ +/* BEGIN - DIF BPF register values from 109_quant.dat*/ +{10900000, DIF_BPF_COEFF01, 0x0000fffe}, +{10900000, DIF_BPF_COEFF23, 0xfff90006}, +{10900000, DIF_BPF_COEFF45, 0x00210003}, +{10900000, DIF_BPF_COEFF67, 0xffacffc8}, +{10900000, DIF_BPF_COEFF89, 0x008e00b6}, +{10900000, DIF_BPF_COEFF1011, 0xff63fe7c}, +{10900000, DIF_BPF_COEFF1213, 0x003a0275}, +{10900000, DIF_BPF_COEFF1415, 0x00dafcda}, +{10900000, DIF_BPF_COEFF1617, 0xfd510313}, +{10900000, DIF_BPF_COEFF1819, 0x0501fe40}, +{10900000, DIF_BPF_COEFF2021, 0xf8cbfefd}, +{10900000, DIF_BPF_COEFF2223, 0x087604f0}, +{10900000, DIF_BPF_COEFF2425, 0xf80af6c2}, +{10900000, DIF_BPF_COEFF2627, 0x05430cc8}, +{10900000, DIF_BPF_COEFF2829, 0xff7af19a}, +{10900000, DIF_BPF_COEFF3031, 0xfa940d4e}, +{10900000, DIF_BPF_COEFF3233, 0x0b3ff699}, +{10900000, DIF_BPF_COEFF3435, 0xf0810365}, +{10900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 109_quant.dat*/ + + +/*case 11000000:*/ +/* BEGIN - DIF BPF register values from 110_quant.dat*/ +{11000000, DIF_BPF_COEFF01, 0x0001ffff}, +{11000000, DIF_BPF_COEFF23, 0xfff8ffff}, +{11000000, DIF_BPF_COEFF45, 0x00210018}, +{11000000, DIF_BPF_COEFF67, 0xffbaffa3}, +{11000000, DIF_BPF_COEFF89, 0x006000e1}, +{11000000, DIF_BPF_COEFF1011, 0xffc4fe68}, +{11000000, DIF_BPF_COEFF1213, 0xffa0024b}, +{11000000, DIF_BPF_COEFF1415, 0x019afd66}, +{11000000, DIF_BPF_COEFF1617, 0xfc990216}, +{11000000, DIF_BPF_COEFF1819, 0x0575ff99}, +{11000000, DIF_BPF_COEFF2021, 0xf8d4fd81}, +{11000000, DIF_BPF_COEFF2223, 0x07d40640}, +{11000000, DIF_BPF_COEFF2425, 0xf932f5e6}, +{11000000, DIF_BPF_COEFF2627, 0x03d20d0d}, +{11000000, DIF_BPF_COEFF2829, 0x00dff1de}, +{11000000, DIF_BPF_COEFF3031, 0xf9860cbf}, +{11000000, DIF_BPF_COEFF3233, 0x0bd1f71e}, +{11000000, DIF_BPF_COEFF3435, 0xf058032f}, +{11000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 110_quant.dat*/ + + +/*case 11100000:*/ +/* BEGIN - DIF BPF register values from 111_quant.dat*/ +{11100000, DIF_BPF_COEFF01, 0x00010000}, +{11100000, DIF_BPF_COEFF23, 0xfff8fff8}, +{11100000, DIF_BPF_COEFF45, 0x001b0029}, +{11100000, DIF_BPF_COEFF67, 0xffd1ff8a}, +{11100000, DIF_BPF_COEFF89, 0x002600f2}, +{11100000, DIF_BPF_COEFF1011, 0x002cfe7c}, +{11100000, DIF_BPF_COEFF1213, 0xff0f01f0}, +{11100000, DIF_BPF_COEFF1415, 0x023bfe20}, +{11100000, DIF_BPF_COEFF1617, 0xfc1700fa}, +{11100000, DIF_BPF_COEFF1819, 0x05a200f7}, +{11100000, DIF_BPF_COEFF2021, 0xf927fc1c}, +{11100000, DIF_BPF_COEFF2223, 0x06f40765}, +{11100000, DIF_BPF_COEFF2425, 0xfa82f53b}, +{11100000, DIF_BPF_COEFF2627, 0x02510d27}, +{11100000, DIF_BPF_COEFF2829, 0x0243f23d}, +{11100000, DIF_BPF_COEFF3031, 0xf8810c24}, +{11100000, DIF_BPF_COEFF3233, 0x0c5cf7a7}, +{11100000, DIF_BPF_COEFF3435, 0xf03102fa}, +{11100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 111_quant.dat*/ + + +/*case 11200000:*/ +/* BEGIN - DIF BPF register values from 112_quant.dat*/ +{11200000, DIF_BPF_COEFF01, 0x00010002}, +{11200000, DIF_BPF_COEFF23, 0xfffafff2}, +{11200000, DIF_BPF_COEFF45, 0x00110035}, +{11200000, DIF_BPF_COEFF67, 0xfff0ff81}, +{11200000, DIF_BPF_COEFF89, 0xffe700e7}, +{11200000, DIF_BPF_COEFF1011, 0x008ffeb6}, +{11200000, DIF_BPF_COEFF1213, 0xfe94016d}, +{11200000, DIF_BPF_COEFF1415, 0x02b0fefb}, +{11200000, DIF_BPF_COEFF1617, 0xfbd3ffd1}, +{11200000, DIF_BPF_COEFF1819, 0x05850249}, +{11200000, DIF_BPF_COEFF2021, 0xf9c1fadb}, +{11200000, DIF_BPF_COEFF2223, 0x05de0858}, +{11200000, DIF_BPF_COEFF2425, 0xfbf2f4c4}, +{11200000, DIF_BPF_COEFF2627, 0x00c70d17}, +{11200000, DIF_BPF_COEFF2829, 0x03a0f2b8}, +{11200000, DIF_BPF_COEFF3031, 0xf7870b7c}, +{11200000, DIF_BPF_COEFF3233, 0x0cdff833}, +{11200000, DIF_BPF_COEFF3435, 0xf00d02c4}, +{11200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 112_quant.dat*/ + + +/*case 11300000:*/ +/* BEGIN - DIF BPF register values from 113_quant.dat*/ +{11300000, DIF_BPF_COEFF01, 0x00000003}, +{11300000, DIF_BPF_COEFF23, 0xfffdffee}, +{11300000, DIF_BPF_COEFF45, 0x00040038}, +{11300000, DIF_BPF_COEFF67, 0x0010ff88}, +{11300000, DIF_BPF_COEFF89, 0xffac00c2}, +{11300000, DIF_BPF_COEFF1011, 0x00e2ff10}, +{11300000, DIF_BPF_COEFF1213, 0xfe3900cb}, +{11300000, DIF_BPF_COEFF1415, 0x02f1ffe9}, +{11300000, DIF_BPF_COEFF1617, 0xfbd3feaa}, +{11300000, DIF_BPF_COEFF1819, 0x05210381}, +{11300000, DIF_BPF_COEFF2021, 0xfa9cf9c8}, +{11300000, DIF_BPF_COEFF2223, 0x04990912}, +{11300000, DIF_BPF_COEFF2425, 0xfd7af484}, +{11300000, DIF_BPF_COEFF2627, 0xff390cdb}, +{11300000, DIF_BPF_COEFF2829, 0x04f4f34d}, +{11300000, DIF_BPF_COEFF3031, 0xf69a0ac9}, +{11300000, DIF_BPF_COEFF3233, 0x0d5af8c1}, +{11300000, DIF_BPF_COEFF3435, 0xefec028e}, +{11300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 113_quant.dat*/ + + +/*case 11400000:*/ +/* BEGIN - DIF BPF register values from 114_quant.dat*/ +{11400000, DIF_BPF_COEFF01, 0x00000003}, +{11400000, DIF_BPF_COEFF23, 0x0000ffee}, +{11400000, DIF_BPF_COEFF45, 0xfff60033}, +{11400000, DIF_BPF_COEFF67, 0x002fff9f}, +{11400000, DIF_BPF_COEFF89, 0xff7b0087}, +{11400000, DIF_BPF_COEFF1011, 0x011eff82}, +{11400000, DIF_BPF_COEFF1213, 0xfe080018}, +{11400000, DIF_BPF_COEFF1415, 0x02f900d8}, +{11400000, DIF_BPF_COEFF1617, 0xfc17fd96}, +{11400000, DIF_BPF_COEFF1819, 0x04790490}, +{11400000, DIF_BPF_COEFF2021, 0xfbadf8ed}, +{11400000, DIF_BPF_COEFF2223, 0x032f098e}, +{11400000, DIF_BPF_COEFF2425, 0xff10f47d}, +{11400000, DIF_BPF_COEFF2627, 0xfdaf0c75}, +{11400000, DIF_BPF_COEFF2829, 0x063cf3fc}, +{11400000, DIF_BPF_COEFF3031, 0xf5ba0a0b}, +{11400000, DIF_BPF_COEFF3233, 0x0dccf952}, +{11400000, DIF_BPF_COEFF3435, 0xefcd0258}, +{11400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 114_quant.dat*/ + + +/*case 11500000:*/ +/* BEGIN - DIF BPF register values from 115_quant.dat*/ +{11500000, DIF_BPF_COEFF01, 0x00000003}, +{11500000, DIF_BPF_COEFF23, 0x0004fff1}, +{11500000, DIF_BPF_COEFF45, 0xffea0026}, +{11500000, DIF_BPF_COEFF67, 0x0046ffc3}, +{11500000, DIF_BPF_COEFF89, 0xff5a003c}, +{11500000, DIF_BPF_COEFF1011, 0x013b0000}, +{11500000, DIF_BPF_COEFF1213, 0xfe04ff63}, +{11500000, DIF_BPF_COEFF1415, 0x02c801b8}, +{11500000, DIF_BPF_COEFF1617, 0xfc99fca6}, +{11500000, DIF_BPF_COEFF1819, 0x0397056a}, +{11500000, DIF_BPF_COEFF2021, 0xfcecf853}, +{11500000, DIF_BPF_COEFF2223, 0x01ad09c9}, +{11500000, DIF_BPF_COEFF2425, 0x00acf4ad}, +{11500000, DIF_BPF_COEFF2627, 0xfc2e0be7}, +{11500000, DIF_BPF_COEFF2829, 0x0773f4c2}, +{11500000, DIF_BPF_COEFF3031, 0xf4e90943}, +{11500000, DIF_BPF_COEFF3233, 0x0e35f9e6}, +{11500000, DIF_BPF_COEFF3435, 0xefb10221}, +{11500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 115_quant.dat*/ + + +/*case 11600000:*/ +/* BEGIN - DIF BPF register values from 116_quant.dat*/ +{11600000, DIF_BPF_COEFF01, 0x00000002}, +{11600000, DIF_BPF_COEFF23, 0x0007fff6}, +{11600000, DIF_BPF_COEFF45, 0xffe20014}, +{11600000, DIF_BPF_COEFF67, 0x0054ffee}, +{11600000, DIF_BPF_COEFF89, 0xff4effeb}, +{11600000, DIF_BPF_COEFF1011, 0x0137007e}, +{11600000, DIF_BPF_COEFF1213, 0xfe2efebb}, +{11600000, DIF_BPF_COEFF1415, 0x0260027a}, +{11600000, DIF_BPF_COEFF1617, 0xfd51fbe6}, +{11600000, DIF_BPF_COEFF1819, 0x02870605}, +{11600000, DIF_BPF_COEFF2021, 0xfe4af7fe}, +{11600000, DIF_BPF_COEFF2223, 0x001d09c1}, +{11600000, DIF_BPF_COEFF2425, 0x0243f515}, +{11600000, DIF_BPF_COEFF2627, 0xfabd0b32}, +{11600000, DIF_BPF_COEFF2829, 0x0897f59e}, +{11600000, DIF_BPF_COEFF3031, 0xf4280871}, +{11600000, DIF_BPF_COEFF3233, 0x0e95fa7c}, +{11600000, DIF_BPF_COEFF3435, 0xef9701eb}, +{11600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 116_quant.dat*/ + + +/*case 11700000:*/ +/* BEGIN - DIF BPF register values from 117_quant.dat*/ +{11700000, DIF_BPF_COEFF01, 0xffff0001}, +{11700000, DIF_BPF_COEFF23, 0x0008fffd}, +{11700000, DIF_BPF_COEFF45, 0xffdeffff}, +{11700000, DIF_BPF_COEFF67, 0x0056001d}, +{11700000, DIF_BPF_COEFF89, 0xff57ff9c}, +{11700000, DIF_BPF_COEFF1011, 0x011300f0}, +{11700000, DIF_BPF_COEFF1213, 0xfe82fe2e}, +{11700000, DIF_BPF_COEFF1415, 0x01ca0310}, +{11700000, DIF_BPF_COEFF1617, 0xfe35fb62}, +{11700000, DIF_BPF_COEFF1819, 0x0155065a}, +{11700000, DIF_BPF_COEFF2021, 0xffbaf7f2}, +{11700000, DIF_BPF_COEFF2223, 0xfe8c0977}, +{11700000, DIF_BPF_COEFF2425, 0x03cef5b2}, +{11700000, DIF_BPF_COEFF2627, 0xf9610a58}, +{11700000, DIF_BPF_COEFF2829, 0x09a5f68f}, +{11700000, DIF_BPF_COEFF3031, 0xf3790797}, +{11700000, DIF_BPF_COEFF3233, 0x0eebfb14}, +{11700000, DIF_BPF_COEFF3435, 0xef8001b5}, +{11700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 117_quant.dat*/ + + +/*case 11800000:*/ +/* BEGIN - DIF BPF register values from 118_quant.dat*/ +{11800000, DIF_BPF_COEFF01, 0xffff0000}, +{11800000, DIF_BPF_COEFF23, 0x00080004}, +{11800000, DIF_BPF_COEFF45, 0xffe0ffe9}, +{11800000, DIF_BPF_COEFF67, 0x004c0047}, +{11800000, DIF_BPF_COEFF89, 0xff75ff58}, +{11800000, DIF_BPF_COEFF1011, 0x00d1014a}, +{11800000, DIF_BPF_COEFF1213, 0xfef9fdc8}, +{11800000, DIF_BPF_COEFF1415, 0x0111036f}, +{11800000, DIF_BPF_COEFF1617, 0xff36fb21}, +{11800000, DIF_BPF_COEFF1819, 0x00120665}, +{11800000, DIF_BPF_COEFF2021, 0x012df82e}, +{11800000, DIF_BPF_COEFF2223, 0xfd0708ec}, +{11800000, DIF_BPF_COEFF2425, 0x0542f682}, +{11800000, DIF_BPF_COEFF2627, 0xf81f095c}, +{11800000, DIF_BPF_COEFF2829, 0x0a9af792}, +{11800000, DIF_BPF_COEFF3031, 0xf2db06b5}, +{11800000, DIF_BPF_COEFF3233, 0x0f38fbad}, +{11800000, DIF_BPF_COEFF3435, 0xef6c017e}, +{11800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 118_quant.dat*/ + + +/*case 11900000:*/ +/* BEGIN - DIF BPF register values from 119_quant.dat*/ +{11900000, DIF_BPF_COEFF01, 0xffffffff}, +{11900000, DIF_BPF_COEFF23, 0x0007000b}, +{11900000, DIF_BPF_COEFF45, 0xffe7ffd8}, +{11900000, DIF_BPF_COEFF67, 0x00370068}, +{11900000, DIF_BPF_COEFF89, 0xffa4ff28}, +{11900000, DIF_BPF_COEFF1011, 0x00790184}, +{11900000, DIF_BPF_COEFF1213, 0xff87fd91}, +{11900000, DIF_BPF_COEFF1415, 0x00430392}, +{11900000, DIF_BPF_COEFF1617, 0x0044fb26}, +{11900000, DIF_BPF_COEFF1819, 0xfece0626}, +{11900000, DIF_BPF_COEFF2021, 0x0294f8b2}, +{11900000, DIF_BPF_COEFF2223, 0xfb990825}, +{11900000, DIF_BPF_COEFF2425, 0x0698f77f}, +{11900000, DIF_BPF_COEFF2627, 0xf6fe0842}, +{11900000, DIF_BPF_COEFF2829, 0x0b73f8a7}, +{11900000, DIF_BPF_COEFF3031, 0xf25105cd}, +{11900000, DIF_BPF_COEFF3233, 0x0f7bfc48}, +{11900000, DIF_BPF_COEFF3435, 0xef5a0148}, +{11900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 119_quant.dat*/ + + +/*case 12000000:*/ +/* BEGIN - DIF BPF register values from 120_quant.dat*/ +{12000000, DIF_BPF_COEFF01, 0x0000fffe}, +{12000000, DIF_BPF_COEFF23, 0x00050010}, +{12000000, DIF_BPF_COEFF45, 0xfff2ffcc}, +{12000000, DIF_BPF_COEFF67, 0x001b007b}, +{12000000, DIF_BPF_COEFF89, 0xffdfff10}, +{12000000, DIF_BPF_COEFF1011, 0x00140198}, +{12000000, DIF_BPF_COEFF1213, 0x0020fd8e}, +{12000000, DIF_BPF_COEFF1415, 0xff710375}, +{12000000, DIF_BPF_COEFF1617, 0x014dfb73}, +{12000000, DIF_BPF_COEFF1819, 0xfd9a059f}, +{12000000, DIF_BPF_COEFF2021, 0x03e0f978}, +{12000000, DIF_BPF_COEFF2223, 0xfa4e0726}, +{12000000, DIF_BPF_COEFF2425, 0x07c8f8a7}, +{12000000, DIF_BPF_COEFF2627, 0xf600070c}, +{12000000, DIF_BPF_COEFF2829, 0x0c2ff9c9}, +{12000000, DIF_BPF_COEFF3031, 0xf1db04de}, +{12000000, DIF_BPF_COEFF3233, 0x0fb4fce5}, +{12000000, DIF_BPF_COEFF3435, 0xef4b0111}, +{12000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 120_quant.dat*/ + + +/*case 12100000:*/ +/* BEGIN - DIF BPF register values from 121_quant.dat*/ +{12100000, DIF_BPF_COEFF01, 0x0000fffd}, +{12100000, DIF_BPF_COEFF23, 0x00010012}, +{12100000, DIF_BPF_COEFF45, 0xffffffc8}, +{12100000, DIF_BPF_COEFF67, 0xfffb007e}, +{12100000, DIF_BPF_COEFF89, 0x001dff14}, +{12100000, DIF_BPF_COEFF1011, 0xffad0184}, +{12100000, DIF_BPF_COEFF1213, 0x00b7fdbe}, +{12100000, DIF_BPF_COEFF1415, 0xfea9031b}, +{12100000, DIF_BPF_COEFF1617, 0x0241fc01}, +{12100000, DIF_BPF_COEFF1819, 0xfc8504d6}, +{12100000, DIF_BPF_COEFF2021, 0x0504fa79}, +{12100000, DIF_BPF_COEFF2223, 0xf93005f6}, +{12100000, DIF_BPF_COEFF2425, 0x08caf9f2}, +{12100000, DIF_BPF_COEFF2627, 0xf52b05c0}, +{12100000, DIF_BPF_COEFF2829, 0x0ccbfaf9}, +{12100000, DIF_BPF_COEFF3031, 0xf17903eb}, +{12100000, DIF_BPF_COEFF3233, 0x0fe3fd83}, +{12100000, DIF_BPF_COEFF3435, 0xef3f00db}, +{12100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 121_quant.dat*/ + + +/*case 12200000:*/ +/* BEGIN - DIF BPF register values from 122_quant.dat*/ +{12200000, DIF_BPF_COEFF01, 0x0000fffd}, +{12200000, DIF_BPF_COEFF23, 0xfffe0011}, +{12200000, DIF_BPF_COEFF45, 0x000cffcc}, +{12200000, DIF_BPF_COEFF67, 0xffdb0071}, +{12200000, DIF_BPF_COEFF89, 0x0058ff32}, +{12200000, DIF_BPF_COEFF1011, 0xff4f014a}, +{12200000, DIF_BPF_COEFF1213, 0x013cfe1f}, +{12200000, DIF_BPF_COEFF1415, 0xfdfb028a}, +{12200000, DIF_BPF_COEFF1617, 0x0311fcc9}, +{12200000, DIF_BPF_COEFF1819, 0xfb9d03d6}, +{12200000, DIF_BPF_COEFF2021, 0x05f4fbad}, +{12200000, DIF_BPF_COEFF2223, 0xf848049d}, +{12200000, DIF_BPF_COEFF2425, 0x0999fb5b}, +{12200000, DIF_BPF_COEFF2627, 0xf4820461}, +{12200000, DIF_BPF_COEFF2829, 0x0d46fc32}, +{12200000, DIF_BPF_COEFF3031, 0xf12d02f4}, +{12200000, DIF_BPF_COEFF3233, 0x1007fe21}, +{12200000, DIF_BPF_COEFF3435, 0xef3600a4}, +{12200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 122_quant.dat*/ + + +/*case 12300000:*/ +/* BEGIN - DIF BPF register values from 123_quant.dat*/ +{12300000, DIF_BPF_COEFF01, 0x0000fffe}, +{12300000, DIF_BPF_COEFF23, 0xfffa000e}, +{12300000, DIF_BPF_COEFF45, 0x0017ffd9}, +{12300000, DIF_BPF_COEFF67, 0xffc10055}, +{12300000, DIF_BPF_COEFF89, 0x0088ff68}, +{12300000, DIF_BPF_COEFF1011, 0xff0400f0}, +{12300000, DIF_BPF_COEFF1213, 0x01a6fea7}, +{12300000, DIF_BPF_COEFF1415, 0xfd7501cc}, +{12300000, DIF_BPF_COEFF1617, 0x03b0fdc0}, +{12300000, DIF_BPF_COEFF1819, 0xfaef02a8}, +{12300000, DIF_BPF_COEFF2021, 0x06a7fd07}, +{12300000, DIF_BPF_COEFF2223, 0xf79d0326}, +{12300000, DIF_BPF_COEFF2425, 0x0a31fcda}, +{12300000, DIF_BPF_COEFF2627, 0xf40702f3}, +{12300000, DIF_BPF_COEFF2829, 0x0d9ffd72}, +{12300000, DIF_BPF_COEFF3031, 0xf0f601fa}, +{12300000, DIF_BPF_COEFF3233, 0x1021fec0}, +{12300000, DIF_BPF_COEFF3435, 0xef2f006d}, +{12300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 123_quant.dat*/ + + +/*case 12400000:*/ +/* BEGIN - DIF BPF register values from 124_quant.dat*/ +{12400000, DIF_BPF_COEFF01, 0x0001ffff}, +{12400000, DIF_BPF_COEFF23, 0xfff80007}, +{12400000, DIF_BPF_COEFF45, 0x001fffeb}, +{12400000, DIF_BPF_COEFF67, 0xffaf002d}, +{12400000, DIF_BPF_COEFF89, 0x00a8ffb0}, +{12400000, DIF_BPF_COEFF1011, 0xfed3007e}, +{12400000, DIF_BPF_COEFF1213, 0x01e9ff4c}, +{12400000, DIF_BPF_COEFF1415, 0xfd2000ee}, +{12400000, DIF_BPF_COEFF1617, 0x0413fed8}, +{12400000, DIF_BPF_COEFF1819, 0xfa82015c}, +{12400000, DIF_BPF_COEFF2021, 0x0715fe7d}, +{12400000, DIF_BPF_COEFF2223, 0xf7340198}, +{12400000, DIF_BPF_COEFF2425, 0x0a8dfe69}, +{12400000, DIF_BPF_COEFF2627, 0xf3bd017c}, +{12400000, DIF_BPF_COEFF2829, 0x0dd5feb8}, +{12400000, DIF_BPF_COEFF3031, 0xf0d500fd}, +{12400000, DIF_BPF_COEFF3233, 0x1031ff60}, +{12400000, DIF_BPF_COEFF3435, 0xef2b0037}, +{12400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 124_quant.dat*/ + + +/*case 12500000:*/ +/* BEGIN - DIF BPF register values from 125_quant.dat*/ +{12500000, DIF_BPF_COEFF01, 0x00010000}, +{12500000, DIF_BPF_COEFF23, 0xfff70000}, +{12500000, DIF_BPF_COEFF45, 0x00220000}, +{12500000, DIF_BPF_COEFF67, 0xffa90000}, +{12500000, DIF_BPF_COEFF89, 0x00b30000}, +{12500000, DIF_BPF_COEFF1011, 0xfec20000}, +{12500000, DIF_BPF_COEFF1213, 0x02000000}, +{12500000, DIF_BPF_COEFF1415, 0xfd030000}, +{12500000, DIF_BPF_COEFF1617, 0x04350000}, +{12500000, DIF_BPF_COEFF1819, 0xfa5e0000}, +{12500000, DIF_BPF_COEFF2021, 0x073b0000}, +{12500000, DIF_BPF_COEFF2223, 0xf7110000}, +{12500000, DIF_BPF_COEFF2425, 0x0aac0000}, +{12500000, DIF_BPF_COEFF2627, 0xf3a40000}, +{12500000, DIF_BPF_COEFF2829, 0x0de70000}, +{12500000, DIF_BPF_COEFF3031, 0xf0c90000}, +{12500000, DIF_BPF_COEFF3233, 0x10360000}, +{12500000, DIF_BPF_COEFF3435, 0xef290000}, +{12500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 125_quant.dat*/ + + +/*case 12600000:*/ +/* BEGIN - DIF BPF register values from 126_quant.dat*/ +{12600000, DIF_BPF_COEFF01, 0x00010001}, +{12600000, DIF_BPF_COEFF23, 0xfff8fff9}, +{12600000, DIF_BPF_COEFF45, 0x001f0015}, +{12600000, DIF_BPF_COEFF67, 0xffafffd3}, +{12600000, DIF_BPF_COEFF89, 0x00a80050}, +{12600000, DIF_BPF_COEFF1011, 0xfed3ff82}, +{12600000, DIF_BPF_COEFF1213, 0x01e900b4}, +{12600000, DIF_BPF_COEFF1415, 0xfd20ff12}, +{12600000, DIF_BPF_COEFF1617, 0x04130128}, +{12600000, DIF_BPF_COEFF1819, 0xfa82fea4}, +{12600000, DIF_BPF_COEFF2021, 0x07150183}, +{12600000, DIF_BPF_COEFF2223, 0xf734fe68}, +{12600000, DIF_BPF_COEFF2425, 0x0a8d0197}, +{12600000, DIF_BPF_COEFF2627, 0xf3bdfe84}, +{12600000, DIF_BPF_COEFF2829, 0x0dd50148}, +{12600000, DIF_BPF_COEFF3031, 0xf0d5ff03}, +{12600000, DIF_BPF_COEFF3233, 0x103100a0}, +{12600000, DIF_BPF_COEFF3435, 0xef2bffc9}, +{12600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 126_quant.dat*/ + + +/*case 12700000:*/ +/* BEGIN - DIF BPF register values from 127_quant.dat*/ +{12700000, DIF_BPF_COEFF01, 0x00000002}, +{12700000, DIF_BPF_COEFF23, 0xfffafff2}, +{12700000, DIF_BPF_COEFF45, 0x00170027}, +{12700000, DIF_BPF_COEFF67, 0xffc1ffab}, +{12700000, DIF_BPF_COEFF89, 0x00880098}, +{12700000, DIF_BPF_COEFF1011, 0xff04ff10}, +{12700000, DIF_BPF_COEFF1213, 0x01a60159}, +{12700000, DIF_BPF_COEFF1415, 0xfd75fe34}, +{12700000, DIF_BPF_COEFF1617, 0x03b00240}, +{12700000, DIF_BPF_COEFF1819, 0xfaeffd58}, +{12700000, DIF_BPF_COEFF2021, 0x06a702f9}, +{12700000, DIF_BPF_COEFF2223, 0xf79dfcda}, +{12700000, DIF_BPF_COEFF2425, 0x0a310326}, +{12700000, DIF_BPF_COEFF2627, 0xf407fd0d}, +{12700000, DIF_BPF_COEFF2829, 0x0d9f028e}, +{12700000, DIF_BPF_COEFF3031, 0xf0f6fe06}, +{12700000, DIF_BPF_COEFF3233, 0x10210140}, +{12700000, DIF_BPF_COEFF3435, 0xef2fff93}, +{12700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 127_quant.dat*/ + + +/*case 12800000:*/ +/* BEGIN - DIF BPF register values from 128_quant.dat*/ +{12800000, DIF_BPF_COEFF01, 0x00000003}, +{12800000, DIF_BPF_COEFF23, 0xfffeffef}, +{12800000, DIF_BPF_COEFF45, 0x000c0034}, +{12800000, DIF_BPF_COEFF67, 0xffdbff8f}, +{12800000, DIF_BPF_COEFF89, 0x005800ce}, +{12800000, DIF_BPF_COEFF1011, 0xff4ffeb6}, +{12800000, DIF_BPF_COEFF1213, 0x013c01e1}, +{12800000, DIF_BPF_COEFF1415, 0xfdfbfd76}, +{12800000, DIF_BPF_COEFF1617, 0x03110337}, +{12800000, DIF_BPF_COEFF1819, 0xfb9dfc2a}, +{12800000, DIF_BPF_COEFF2021, 0x05f40453}, +{12800000, DIF_BPF_COEFF2223, 0xf848fb63}, +{12800000, DIF_BPF_COEFF2425, 0x099904a5}, +{12800000, DIF_BPF_COEFF2627, 0xf482fb9f}, +{12800000, DIF_BPF_COEFF2829, 0x0d4603ce}, +{12800000, DIF_BPF_COEFF3031, 0xf12dfd0c}, +{12800000, DIF_BPF_COEFF3233, 0x100701df}, +{12800000, DIF_BPF_COEFF3435, 0xef36ff5c}, +{12800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 128_quant.dat*/ + + +/*case 12900000:*/ +/* BEGIN - DIF BPF register values from 129_quant.dat*/ +{12900000, DIF_BPF_COEFF01, 0x00000003}, +{12900000, DIF_BPF_COEFF23, 0x0001ffee}, +{12900000, DIF_BPF_COEFF45, 0xffff0038}, +{12900000, DIF_BPF_COEFF67, 0xfffbff82}, +{12900000, DIF_BPF_COEFF89, 0x001d00ec}, +{12900000, DIF_BPF_COEFF1011, 0xffadfe7c}, +{12900000, DIF_BPF_COEFF1213, 0x00b70242}, +{12900000, DIF_BPF_COEFF1415, 0xfea9fce5}, +{12900000, DIF_BPF_COEFF1617, 0x024103ff}, +{12900000, DIF_BPF_COEFF1819, 0xfc85fb2a}, +{12900000, DIF_BPF_COEFF2021, 0x05040587}, +{12900000, DIF_BPF_COEFF2223, 0xf930fa0a}, +{12900000, DIF_BPF_COEFF2425, 0x08ca060e}, +{12900000, DIF_BPF_COEFF2627, 0xf52bfa40}, +{12900000, DIF_BPF_COEFF2829, 0x0ccb0507}, +{12900000, DIF_BPF_COEFF3031, 0xf179fc15}, +{12900000, DIF_BPF_COEFF3233, 0x0fe3027d}, +{12900000, DIF_BPF_COEFF3435, 0xef3fff25}, +{12900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 129_quant.dat*/ + + +/*case 113000000:*/ +/* BEGIN - DIF BPF register values from 130_quant.dat*/ +{13000000, DIF_BPF_COEFF01, 0x00000002}, +{13000000, DIF_BPF_COEFF23, 0x0005fff0}, +{13000000, DIF_BPF_COEFF45, 0xfff20034}, +{13000000, DIF_BPF_COEFF67, 0x001bff85}, +{13000000, DIF_BPF_COEFF89, 0xffdf00f0}, +{13000000, DIF_BPF_COEFF1011, 0x0014fe68}, +{13000000, DIF_BPF_COEFF1213, 0x00200272}, +{13000000, DIF_BPF_COEFF1415, 0xff71fc8b}, +{13000000, DIF_BPF_COEFF1617, 0x014d048d}, +{13000000, DIF_BPF_COEFF1819, 0xfd9afa61}, +{13000000, DIF_BPF_COEFF2021, 0x03e00688}, +{13000000, DIF_BPF_COEFF2223, 0xfa4ef8da}, +{13000000, DIF_BPF_COEFF2425, 0x07c80759}, +{13000000, DIF_BPF_COEFF2627, 0xf600f8f4}, +{13000000, DIF_BPF_COEFF2829, 0x0c2f0637}, +{13000000, DIF_BPF_COEFF3031, 0xf1dbfb22}, +{13000000, DIF_BPF_COEFF3233, 0x0fb4031b}, +{13000000, DIF_BPF_COEFF3435, 0xef4bfeef}, +{13000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 130_quant.dat*/ + + +/*case 13100000:*/ +/* BEGIN - DIF BPF register values from 131_quant.dat*/ +{13100000, DIF_BPF_COEFF01, 0xffff0001}, +{13100000, DIF_BPF_COEFF23, 0x0007fff5}, +{13100000, DIF_BPF_COEFF45, 0xffe70028}, +{13100000, DIF_BPF_COEFF67, 0x0037ff98}, +{13100000, DIF_BPF_COEFF89, 0xffa400d8}, +{13100000, DIF_BPF_COEFF1011, 0x0079fe7c}, +{13100000, DIF_BPF_COEFF1213, 0xff87026f}, +{13100000, DIF_BPF_COEFF1415, 0x0043fc6e}, +{13100000, DIF_BPF_COEFF1617, 0x004404da}, +{13100000, DIF_BPF_COEFF1819, 0xfecef9da}, +{13100000, DIF_BPF_COEFF2021, 0x0294074e}, +{13100000, DIF_BPF_COEFF2223, 0xfb99f7db}, +{13100000, DIF_BPF_COEFF2425, 0x06980881}, +{13100000, DIF_BPF_COEFF2627, 0xf6fef7be}, +{13100000, DIF_BPF_COEFF2829, 0x0b730759}, +{13100000, DIF_BPF_COEFF3031, 0xf251fa33}, +{13100000, DIF_BPF_COEFF3233, 0x0f7b03b8}, +{13100000, DIF_BPF_COEFF3435, 0xef5afeb8}, +{13100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 131_quant.dat*/ + + +/*case 13200000:*/ +/* BEGIN - DIF BPF register values from 132_quant.dat*/ +{13200000, DIF_BPF_COEFF01, 0xffff0000}, +{13200000, DIF_BPF_COEFF23, 0x0008fffc}, +{13200000, DIF_BPF_COEFF45, 0xffe00017}, +{13200000, DIF_BPF_COEFF67, 0x004cffb9}, +{13200000, DIF_BPF_COEFF89, 0xff7500a8}, +{13200000, DIF_BPF_COEFF1011, 0x00d1feb6}, +{13200000, DIF_BPF_COEFF1213, 0xfef90238}, +{13200000, DIF_BPF_COEFF1415, 0x0111fc91}, +{13200000, DIF_BPF_COEFF1617, 0xff3604df}, +{13200000, DIF_BPF_COEFF1819, 0x0012f99b}, +{13200000, DIF_BPF_COEFF2021, 0x012d07d2}, +{13200000, DIF_BPF_COEFF2223, 0xfd07f714}, +{13200000, DIF_BPF_COEFF2425, 0x0542097e}, +{13200000, DIF_BPF_COEFF2627, 0xf81ff6a4}, +{13200000, DIF_BPF_COEFF2829, 0x0a9a086e}, +{13200000, DIF_BPF_COEFF3031, 0xf2dbf94b}, +{13200000, DIF_BPF_COEFF3233, 0x0f380453}, +{13200000, DIF_BPF_COEFF3435, 0xef6cfe82}, +{13200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 132_quant.dat*/ + + +/*case 13300000:*/ +/* BEGIN - DIF BPF register values from 133_quant.dat*/ +{13300000, DIF_BPF_COEFF01, 0xffffffff}, +{13300000, DIF_BPF_COEFF23, 0x00080003}, +{13300000, DIF_BPF_COEFF45, 0xffde0001}, +{13300000, DIF_BPF_COEFF67, 0x0056ffe3}, +{13300000, DIF_BPF_COEFF89, 0xff570064}, +{13300000, DIF_BPF_COEFF1011, 0x0113ff10}, +{13300000, DIF_BPF_COEFF1213, 0xfe8201d2}, +{13300000, DIF_BPF_COEFF1415, 0x01cafcf0}, +{13300000, DIF_BPF_COEFF1617, 0xfe35049e}, +{13300000, DIF_BPF_COEFF1819, 0x0155f9a6}, +{13300000, DIF_BPF_COEFF2021, 0xffba080e}, +{13300000, DIF_BPF_COEFF2223, 0xfe8cf689}, +{13300000, DIF_BPF_COEFF2425, 0x03ce0a4e}, +{13300000, DIF_BPF_COEFF2627, 0xf961f5a8}, +{13300000, DIF_BPF_COEFF2829, 0x09a50971}, +{13300000, DIF_BPF_COEFF3031, 0xf379f869}, +{13300000, DIF_BPF_COEFF3233, 0x0eeb04ec}, +{13300000, DIF_BPF_COEFF3435, 0xef80fe4b}, +{13300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 133_quant.dat*/ + + +/*case 13400000:*/ +/* BEGIN - DIF BPF register values from 134_quant.dat*/ +{13400000, DIF_BPF_COEFF01, 0x0000fffe}, +{13400000, DIF_BPF_COEFF23, 0x0007000a}, +{13400000, DIF_BPF_COEFF45, 0xffe2ffec}, +{13400000, DIF_BPF_COEFF67, 0x00540012}, +{13400000, DIF_BPF_COEFF89, 0xff4e0015}, +{13400000, DIF_BPF_COEFF1011, 0x0137ff82}, +{13400000, DIF_BPF_COEFF1213, 0xfe2e0145}, +{13400000, DIF_BPF_COEFF1415, 0x0260fd86}, +{13400000, DIF_BPF_COEFF1617, 0xfd51041a}, +{13400000, DIF_BPF_COEFF1819, 0x0287f9fb}, +{13400000, DIF_BPF_COEFF2021, 0xfe4a0802}, +{13400000, DIF_BPF_COEFF2223, 0x001df63f}, +{13400000, DIF_BPF_COEFF2425, 0x02430aeb}, +{13400000, DIF_BPF_COEFF2627, 0xfabdf4ce}, +{13400000, DIF_BPF_COEFF2829, 0x08970a62}, +{13400000, DIF_BPF_COEFF3031, 0xf428f78f}, +{13400000, DIF_BPF_COEFF3233, 0x0e950584}, +{13400000, DIF_BPF_COEFF3435, 0xef97fe15}, +{13400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 134_quant.dat*/ + + +/*case 13500000:*/ +/* BEGIN - DIF BPF register values from 135_quant.dat*/ +{13500000, DIF_BPF_COEFF01, 0x0000fffd}, +{13500000, DIF_BPF_COEFF23, 0x0004000f}, +{13500000, DIF_BPF_COEFF45, 0xffeaffda}, +{13500000, DIF_BPF_COEFF67, 0x0046003d}, +{13500000, DIF_BPF_COEFF89, 0xff5affc4}, +{13500000, DIF_BPF_COEFF1011, 0x013b0000}, +{13500000, DIF_BPF_COEFF1213, 0xfe04009d}, +{13500000, DIF_BPF_COEFF1415, 0x02c8fe48}, +{13500000, DIF_BPF_COEFF1617, 0xfc99035a}, +{13500000, DIF_BPF_COEFF1819, 0x0397fa96}, +{13500000, DIF_BPF_COEFF2021, 0xfcec07ad}, +{13500000, DIF_BPF_COEFF2223, 0x01adf637}, +{13500000, DIF_BPF_COEFF2425, 0x00ac0b53}, +{13500000, DIF_BPF_COEFF2627, 0xfc2ef419}, +{13500000, DIF_BPF_COEFF2829, 0x07730b3e}, +{13500000, DIF_BPF_COEFF3031, 0xf4e9f6bd}, +{13500000, DIF_BPF_COEFF3233, 0x0e35061a}, +{13500000, DIF_BPF_COEFF3435, 0xefb1fddf}, +{13500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 135_quant.dat*/ + + +/*case 13600000:*/ +/* BEGIN - DIF BPF register values from 136_quant.dat*/ +{13600000, DIF_BPF_COEFF01, 0x0000fffd}, +{13600000, DIF_BPF_COEFF23, 0x00000012}, +{13600000, DIF_BPF_COEFF45, 0xfff6ffcd}, +{13600000, DIF_BPF_COEFF67, 0x002f0061}, +{13600000, DIF_BPF_COEFF89, 0xff7bff79}, +{13600000, DIF_BPF_COEFF1011, 0x011e007e}, +{13600000, DIF_BPF_COEFF1213, 0xfe08ffe8}, +{13600000, DIF_BPF_COEFF1415, 0x02f9ff28}, +{13600000, DIF_BPF_COEFF1617, 0xfc17026a}, +{13600000, DIF_BPF_COEFF1819, 0x0479fb70}, +{13600000, DIF_BPF_COEFF2021, 0xfbad0713}, +{13600000, DIF_BPF_COEFF2223, 0x032ff672}, +{13600000, DIF_BPF_COEFF2425, 0xff100b83}, +{13600000, DIF_BPF_COEFF2627, 0xfdaff38b}, +{13600000, DIF_BPF_COEFF2829, 0x063c0c04}, +{13600000, DIF_BPF_COEFF3031, 0xf5baf5f5}, +{13600000, DIF_BPF_COEFF3233, 0x0dcc06ae}, +{13600000, DIF_BPF_COEFF3435, 0xefcdfda8}, +{13600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 136_quant.dat*/ + + +/*case 13700000:*/ +/* BEGIN - DIF BPF register values from 137_quant.dat*/ +{13700000, DIF_BPF_COEFF01, 0x0000fffd}, +{13700000, DIF_BPF_COEFF23, 0xfffd0012}, +{13700000, DIF_BPF_COEFF45, 0x0004ffc8}, +{13700000, DIF_BPF_COEFF67, 0x00100078}, +{13700000, DIF_BPF_COEFF89, 0xffacff3e}, +{13700000, DIF_BPF_COEFF1011, 0x00e200f0}, +{13700000, DIF_BPF_COEFF1213, 0xfe39ff35}, +{13700000, DIF_BPF_COEFF1415, 0x02f10017}, +{13700000, DIF_BPF_COEFF1617, 0xfbd30156}, +{13700000, DIF_BPF_COEFF1819, 0x0521fc7f}, +{13700000, DIF_BPF_COEFF2021, 0xfa9c0638}, +{13700000, DIF_BPF_COEFF2223, 0x0499f6ee}, +{13700000, DIF_BPF_COEFF2425, 0xfd7a0b7c}, +{13700000, DIF_BPF_COEFF2627, 0xff39f325}, +{13700000, DIF_BPF_COEFF2829, 0x04f40cb3}, +{13700000, DIF_BPF_COEFF3031, 0xf69af537}, +{13700000, DIF_BPF_COEFF3233, 0x0d5a073f}, +{13700000, DIF_BPF_COEFF3435, 0xefecfd72}, +{13700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 137_quant.dat*/ + + +/*case 13800000:*/ +/* BEGIN - DIF BPF register values from 138_quant.dat*/ +{13800000, DIF_BPF_COEFF01, 0x0001fffe}, +{13800000, DIF_BPF_COEFF23, 0xfffa000e}, +{13800000, DIF_BPF_COEFF45, 0x0011ffcb}, +{13800000, DIF_BPF_COEFF67, 0xfff0007f}, +{13800000, DIF_BPF_COEFF89, 0xffe7ff19}, +{13800000, DIF_BPF_COEFF1011, 0x008f014a}, +{13800000, DIF_BPF_COEFF1213, 0xfe94fe93}, +{13800000, DIF_BPF_COEFF1415, 0x02b00105}, +{13800000, DIF_BPF_COEFF1617, 0xfbd3002f}, +{13800000, DIF_BPF_COEFF1819, 0x0585fdb7}, +{13800000, DIF_BPF_COEFF2021, 0xf9c10525}, +{13800000, DIF_BPF_COEFF2223, 0x05def7a8}, +{13800000, DIF_BPF_COEFF2425, 0xfbf20b3c}, +{13800000, DIF_BPF_COEFF2627, 0x00c7f2e9}, +{13800000, DIF_BPF_COEFF2829, 0x03a00d48}, +{13800000, DIF_BPF_COEFF3031, 0xf787f484}, +{13800000, DIF_BPF_COEFF3233, 0x0cdf07cd}, +{13800000, DIF_BPF_COEFF3435, 0xf00dfd3c}, +{13800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 138_quant.dat*/ + + +/*case 13900000:*/ +/* BEGIN - DIF BPF register values from 139_quant.dat*/ +{13900000, DIF_BPF_COEFF01, 0x00010000}, +{13900000, DIF_BPF_COEFF23, 0xfff80008}, +{13900000, DIF_BPF_COEFF45, 0x001bffd7}, +{13900000, DIF_BPF_COEFF67, 0xffd10076}, +{13900000, DIF_BPF_COEFF89, 0x0026ff0e}, +{13900000, DIF_BPF_COEFF1011, 0x002c0184}, +{13900000, DIF_BPF_COEFF1213, 0xff0ffe10}, +{13900000, DIF_BPF_COEFF1415, 0x023b01e0}, +{13900000, DIF_BPF_COEFF1617, 0xfc17ff06}, +{13900000, DIF_BPF_COEFF1819, 0x05a2ff09}, +{13900000, DIF_BPF_COEFF2021, 0xf92703e4}, +{13900000, DIF_BPF_COEFF2223, 0x06f4f89b}, +{13900000, DIF_BPF_COEFF2425, 0xfa820ac5}, +{13900000, DIF_BPF_COEFF2627, 0x0251f2d9}, +{13900000, DIF_BPF_COEFF2829, 0x02430dc3}, +{13900000, DIF_BPF_COEFF3031, 0xf881f3dc}, +{13900000, DIF_BPF_COEFF3233, 0x0c5c0859}, +{13900000, DIF_BPF_COEFF3435, 0xf031fd06}, +{13900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 139_quant.dat*/ + + +/*case 14000000:*/ +/* BEGIN - DIF BPF register values from 140_quant.dat*/ +{14000000, DIF_BPF_COEFF01, 0x00010001}, +{14000000, DIF_BPF_COEFF23, 0xfff80001}, +{14000000, DIF_BPF_COEFF45, 0x0021ffe8}, +{14000000, DIF_BPF_COEFF67, 0xffba005d}, +{14000000, DIF_BPF_COEFF89, 0x0060ff1f}, +{14000000, DIF_BPF_COEFF1011, 0xffc40198}, +{14000000, DIF_BPF_COEFF1213, 0xffa0fdb5}, +{14000000, DIF_BPF_COEFF1415, 0x019a029a}, +{14000000, DIF_BPF_COEFF1617, 0xfc99fdea}, +{14000000, DIF_BPF_COEFF1819, 0x05750067}, +{14000000, DIF_BPF_COEFF2021, 0xf8d4027f}, +{14000000, DIF_BPF_COEFF2223, 0x07d4f9c0}, +{14000000, DIF_BPF_COEFF2425, 0xf9320a1a}, +{14000000, DIF_BPF_COEFF2627, 0x03d2f2f3}, +{14000000, DIF_BPF_COEFF2829, 0x00df0e22}, +{14000000, DIF_BPF_COEFF3031, 0xf986f341}, +{14000000, DIF_BPF_COEFF3233, 0x0bd108e2}, +{14000000, DIF_BPF_COEFF3435, 0xf058fcd1}, +{14000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 140_quant.dat*/ + + +/*case 14100000:*/ +/* BEGIN - DIF BPF register values from 141_quant.dat*/ +{14100000, DIF_BPF_COEFF01, 0x00000002}, +{14100000, DIF_BPF_COEFF23, 0xfff9fffa}, +{14100000, DIF_BPF_COEFF45, 0x0021fffd}, +{14100000, DIF_BPF_COEFF67, 0xffac0038}, +{14100000, DIF_BPF_COEFF89, 0x008eff4a}, +{14100000, DIF_BPF_COEFF1011, 0xff630184}, +{14100000, DIF_BPF_COEFF1213, 0x003afd8b}, +{14100000, DIF_BPF_COEFF1415, 0x00da0326}, +{14100000, DIF_BPF_COEFF1617, 0xfd51fced}, +{14100000, DIF_BPF_COEFF1819, 0x050101c0}, +{14100000, DIF_BPF_COEFF2021, 0xf8cb0103}, +{14100000, DIF_BPF_COEFF2223, 0x0876fb10}, +{14100000, DIF_BPF_COEFF2425, 0xf80a093e}, +{14100000, DIF_BPF_COEFF2627, 0x0543f338}, +{14100000, DIF_BPF_COEFF2829, 0xff7a0e66}, +{14100000, DIF_BPF_COEFF3031, 0xfa94f2b2}, +{14100000, DIF_BPF_COEFF3233, 0x0b3f0967}, +{14100000, DIF_BPF_COEFF3435, 0xf081fc9b}, +{14100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 141_quant.dat*/ + + +/*case 14200000:*/ +/* BEGIN - DIF BPF register values from 142_quant.dat*/ +{14200000, DIF_BPF_COEFF01, 0x00000003}, +{14200000, DIF_BPF_COEFF23, 0xfffbfff3}, +{14200000, DIF_BPF_COEFF45, 0x001d0013}, +{14200000, DIF_BPF_COEFF67, 0xffaa000b}, +{14200000, DIF_BPF_COEFF89, 0x00aaff89}, +{14200000, DIF_BPF_COEFF1011, 0xff13014a}, +{14200000, DIF_BPF_COEFF1213, 0x00cefd95}, +{14200000, DIF_BPF_COEFF1415, 0x000a037b}, +{14200000, DIF_BPF_COEFF1617, 0xfe35fc1d}, +{14200000, DIF_BPF_COEFF1819, 0x044c0305}, +{14200000, DIF_BPF_COEFF2021, 0xf90cff7e}, +{14200000, DIF_BPF_COEFF2223, 0x08d5fc81}, +{14200000, DIF_BPF_COEFF2425, 0xf7100834}, +{14200000, DIF_BPF_COEFF2627, 0x069ff3a7}, +{14200000, DIF_BPF_COEFF2829, 0xfe160e8d}, +{14200000, DIF_BPF_COEFF3031, 0xfbaaf231}, +{14200000, DIF_BPF_COEFF3233, 0x0aa509e9}, +{14200000, DIF_BPF_COEFF3435, 0xf0adfc65}, +{14200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 142_quant.dat*/ + + +/*case 14300000:*/ +/* BEGIN - DIF BPF register values from 143_quant.dat*/ +{14300000, DIF_BPF_COEFF01, 0x00000003}, +{14300000, DIF_BPF_COEFF23, 0xffffffef}, +{14300000, DIF_BPF_COEFF45, 0x00140025}, +{14300000, DIF_BPF_COEFF67, 0xffb4ffdd}, +{14300000, DIF_BPF_COEFF89, 0x00b2ffd6}, +{14300000, DIF_BPF_COEFF1011, 0xfedb00f0}, +{14300000, DIF_BPF_COEFF1213, 0x0150fdd3}, +{14300000, DIF_BPF_COEFF1415, 0xff380391}, +{14300000, DIF_BPF_COEFF1617, 0xff36fb85}, +{14300000, DIF_BPF_COEFF1819, 0x035e0426}, +{14300000, DIF_BPF_COEFF2021, 0xf994fdfe}, +{14300000, DIF_BPF_COEFF2223, 0x08eefe0b}, +{14300000, DIF_BPF_COEFF2425, 0xf6490702}, +{14300000, DIF_BPF_COEFF2627, 0x07e1f43e}, +{14300000, DIF_BPF_COEFF2829, 0xfcb60e97}, +{14300000, DIF_BPF_COEFF3031, 0xfcc6f1be}, +{14300000, DIF_BPF_COEFF3233, 0x0a040a67}, +{14300000, DIF_BPF_COEFF3435, 0xf0dbfc30}, +{14300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 143_quant.dat*/ + + +/*case 14400000:*/ +/* BEGIN - DIF BPF register values from 144_quant.dat*/ +{14400000, DIF_BPF_COEFF01, 0x00000003}, +{14400000, DIF_BPF_COEFF23, 0x0002ffee}, +{14400000, DIF_BPF_COEFF45, 0x00070033}, +{14400000, DIF_BPF_COEFF67, 0xffc9ffb4}, +{14400000, DIF_BPF_COEFF89, 0x00a40027}, +{14400000, DIF_BPF_COEFF1011, 0xfec3007e}, +{14400000, DIF_BPF_COEFF1213, 0x01b4fe3f}, +{14400000, DIF_BPF_COEFF1415, 0xfe760369}, +{14400000, DIF_BPF_COEFF1617, 0x0044fb2e}, +{14400000, DIF_BPF_COEFF1819, 0x02450518}, +{14400000, DIF_BPF_COEFF2021, 0xfa5ffc90}, +{14400000, DIF_BPF_COEFF2223, 0x08c1ffa1}, +{14400000, DIF_BPF_COEFF2425, 0xf5bc05ae}, +{14400000, DIF_BPF_COEFF2627, 0x0902f4fc}, +{14400000, DIF_BPF_COEFF2829, 0xfb600e85}, +{14400000, DIF_BPF_COEFF3031, 0xfde7f15a}, +{14400000, DIF_BPF_COEFF3233, 0x095d0ae2}, +{14400000, DIF_BPF_COEFF3435, 0xf10cfbfb}, +{14400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 144_quant.dat*/ + + +/*case 14500000:*/ +/* BEGIN - DIF BPF register values from 145_quant.dat*/ +{14500000, DIF_BPF_COEFF01, 0xffff0002}, +{14500000, DIF_BPF_COEFF23, 0x0005ffef}, +{14500000, DIF_BPF_COEFF45, 0xfffa0038}, +{14500000, DIF_BPF_COEFF67, 0xffe5ff95}, +{14500000, DIF_BPF_COEFF89, 0x00820074}, +{14500000, DIF_BPF_COEFF1011, 0xfecc0000}, +{14500000, DIF_BPF_COEFF1213, 0x01f0fed0}, +{14500000, DIF_BPF_COEFF1415, 0xfdd20304}, +{14500000, DIF_BPF_COEFF1617, 0x014dfb1d}, +{14500000, DIF_BPF_COEFF1819, 0x010e05ce}, +{14500000, DIF_BPF_COEFF2021, 0xfb64fb41}, +{14500000, DIF_BPF_COEFF2223, 0x084e013b}, +{14500000, DIF_BPF_COEFF2425, 0xf569043e}, +{14500000, DIF_BPF_COEFF2627, 0x0a00f5dd}, +{14500000, DIF_BPF_COEFF2829, 0xfa150e55}, +{14500000, DIF_BPF_COEFF3031, 0xff0bf104}, +{14500000, DIF_BPF_COEFF3233, 0x08b00b59}, +{14500000, DIF_BPF_COEFF3435, 0xf13ffbc6}, +{14500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 145_quant.dat*/ + + +/*case 14600000:*/ +/* BEGIN - DIF BPF register values from 146_quant.dat*/ +{14600000, DIF_BPF_COEFF01, 0xffff0001}, +{14600000, DIF_BPF_COEFF23, 0x0008fff4}, +{14600000, DIF_BPF_COEFF45, 0xffed0035}, +{14600000, DIF_BPF_COEFF67, 0x0005ff83}, +{14600000, DIF_BPF_COEFF89, 0x005000b4}, +{14600000, DIF_BPF_COEFF1011, 0xfef6ff82}, +{14600000, DIF_BPF_COEFF1213, 0x01ffff7a}, +{14600000, DIF_BPF_COEFF1415, 0xfd580269}, +{14600000, DIF_BPF_COEFF1617, 0x0241fb53}, +{14600000, DIF_BPF_COEFF1819, 0xffca0640}, +{14600000, DIF_BPF_COEFF2021, 0xfc99fa1e}, +{14600000, DIF_BPF_COEFF2223, 0x079a02cb}, +{14600000, DIF_BPF_COEFF2425, 0xf55502ba}, +{14600000, DIF_BPF_COEFF2627, 0x0ad5f6e0}, +{14600000, DIF_BPF_COEFF2829, 0xf8d90e0a}, +{14600000, DIF_BPF_COEFF3031, 0x0031f0bd}, +{14600000, DIF_BPF_COEFF3233, 0x07fd0bcb}, +{14600000, DIF_BPF_COEFF3435, 0xf174fb91}, +{14600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 146_quant.dat*/ + + +/*case 14700000:*/ +/* BEGIN - DIF BPF register values from 147_quant.dat*/ +{14700000, DIF_BPF_COEFF01, 0xffffffff}, +{14700000, DIF_BPF_COEFF23, 0x0009fffb}, +{14700000, DIF_BPF_COEFF45, 0xffe4002a}, +{14700000, DIF_BPF_COEFF67, 0x0025ff82}, +{14700000, DIF_BPF_COEFF89, 0x001400e0}, +{14700000, DIF_BPF_COEFF1011, 0xff3cff10}, +{14700000, DIF_BPF_COEFF1213, 0x01e10030}, +{14700000, DIF_BPF_COEFF1415, 0xfd1201a4}, +{14700000, DIF_BPF_COEFF1617, 0x0311fbcd}, +{14700000, DIF_BPF_COEFF1819, 0xfe88066a}, +{14700000, DIF_BPF_COEFF2021, 0xfdf1f92f}, +{14700000, DIF_BPF_COEFF2223, 0x06aa0449}, +{14700000, DIF_BPF_COEFF2425, 0xf57e0128}, +{14700000, DIF_BPF_COEFF2627, 0x0b7ef801}, +{14700000, DIF_BPF_COEFF2829, 0xf7b00da2}, +{14700000, DIF_BPF_COEFF3031, 0x0156f086}, +{14700000, DIF_BPF_COEFF3233, 0x07450c39}, +{14700000, DIF_BPF_COEFF3435, 0xf1acfb5c}, +{14700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 147_quant.dat*/ + + +/*case 14800000:*/ +/* BEGIN - DIF BPF register values from 148_quant.dat*/ +{14800000, DIF_BPF_COEFF01, 0x0000fffe}, +{14800000, DIF_BPF_COEFF23, 0x00080002}, +{14800000, DIF_BPF_COEFF45, 0xffdf0019}, +{14800000, DIF_BPF_COEFF67, 0x003fff92}, +{14800000, DIF_BPF_COEFF89, 0xffd600f1}, +{14800000, DIF_BPF_COEFF1011, 0xff96feb6}, +{14800000, DIF_BPF_COEFF1213, 0x019700e1}, +{14800000, DIF_BPF_COEFF1415, 0xfd0500c2}, +{14800000, DIF_BPF_COEFF1617, 0x03b0fc84}, +{14800000, DIF_BPF_COEFF1819, 0xfd590649}, +{14800000, DIF_BPF_COEFF2021, 0xff5df87f}, +{14800000, DIF_BPF_COEFF2223, 0x058505aa}, +{14800000, DIF_BPF_COEFF2425, 0xf5e4ff91}, +{14800000, DIF_BPF_COEFF2627, 0x0bf9f93c}, +{14800000, DIF_BPF_COEFF2829, 0xf69d0d20}, +{14800000, DIF_BPF_COEFF3031, 0x0279f05e}, +{14800000, DIF_BPF_COEFF3233, 0x06880ca3}, +{14800000, DIF_BPF_COEFF3435, 0xf1e6fb28}, +{14800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 148_quant.dat*/ + + +/*case 14900000:*/ +/* BEGIN - DIF BPF register values from 149_quant.dat*/ +{14900000, DIF_BPF_COEFF01, 0x0000fffd}, +{14900000, DIF_BPF_COEFF23, 0x00060009}, +{14900000, DIF_BPF_COEFF45, 0xffdf0004}, +{14900000, DIF_BPF_COEFF67, 0x0051ffb0}, +{14900000, DIF_BPF_COEFF89, 0xff9d00e8}, +{14900000, DIF_BPF_COEFF1011, 0xfffcfe7c}, +{14900000, DIF_BPF_COEFF1213, 0x01280180}, +{14900000, DIF_BPF_COEFF1415, 0xfd32ffd2}, +{14900000, DIF_BPF_COEFF1617, 0x0413fd6e}, +{14900000, DIF_BPF_COEFF1819, 0xfc4d05df}, +{14900000, DIF_BPF_COEFF2021, 0x00d1f812}, +{14900000, DIF_BPF_COEFF2223, 0x043506e4}, +{14900000, DIF_BPF_COEFF2425, 0xf685fdfb}, +{14900000, DIF_BPF_COEFF2627, 0x0c43fa8d}, +{14900000, DIF_BPF_COEFF2829, 0xf5a10c83}, +{14900000, DIF_BPF_COEFF3031, 0x0399f046}, +{14900000, DIF_BPF_COEFF3233, 0x05c70d08}, +{14900000, DIF_BPF_COEFF3435, 0xf222faf3}, +{14900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 149_quant.dat*/ + + +/*case 15000000:*/ +/* BEGIN - DIF BPF register values from 150_quant.dat*/ +{15000000, DIF_BPF_COEFF01, 0x0000fffd}, +{15000000, DIF_BPF_COEFF23, 0x0003000f}, +{15000000, DIF_BPF_COEFF45, 0xffe5ffef}, +{15000000, DIF_BPF_COEFF67, 0x0057ffd9}, +{15000000, DIF_BPF_COEFF89, 0xff7000c4}, +{15000000, DIF_BPF_COEFF1011, 0x0062fe68}, +{15000000, DIF_BPF_COEFF1213, 0x009e01ff}, +{15000000, DIF_BPF_COEFF1415, 0xfd95fee6}, +{15000000, DIF_BPF_COEFF1617, 0x0435fe7d}, +{15000000, DIF_BPF_COEFF1819, 0xfb710530}, +{15000000, DIF_BPF_COEFF2021, 0x023cf7ee}, +{15000000, DIF_BPF_COEFF2223, 0x02c307ef}, +{15000000, DIF_BPF_COEFF2425, 0xf75efc70}, +{15000000, DIF_BPF_COEFF2627, 0x0c5cfbef}, +{15000000, DIF_BPF_COEFF2829, 0xf4c10bce}, +{15000000, DIF_BPF_COEFF3031, 0x04b3f03f}, +{15000000, DIF_BPF_COEFF3233, 0x05030d69}, +{15000000, DIF_BPF_COEFF3435, 0xf261fabf}, +{15000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 150_quant.dat*/ + + +/*case 15100000:*/ +/* BEGIN - DIF BPF register values from 151_quant.dat*/ +{15100000, DIF_BPF_COEFF01, 0x0000fffd}, +{15100000, DIF_BPF_COEFF23, 0xffff0012}, +{15100000, DIF_BPF_COEFF45, 0xffefffdc}, +{15100000, DIF_BPF_COEFF67, 0x00510006}, +{15100000, DIF_BPF_COEFF89, 0xff540089}, +{15100000, DIF_BPF_COEFF1011, 0x00befe7c}, +{15100000, DIF_BPF_COEFF1213, 0x00060253}, +{15100000, DIF_BPF_COEFF1415, 0xfe27fe0d}, +{15100000, DIF_BPF_COEFF1617, 0x0413ffa2}, +{15100000, DIF_BPF_COEFF1819, 0xfad10446}, +{15100000, DIF_BPF_COEFF2021, 0x0390f812}, +{15100000, DIF_BPF_COEFF2223, 0x013b08c3}, +{15100000, DIF_BPF_COEFF2425, 0xf868faf6}, +{15100000, DIF_BPF_COEFF2627, 0x0c43fd5f}, +{15100000, DIF_BPF_COEFF2829, 0xf3fd0b02}, +{15100000, DIF_BPF_COEFF3031, 0x05c7f046}, +{15100000, DIF_BPF_COEFF3233, 0x043b0dc4}, +{15100000, DIF_BPF_COEFF3435, 0xf2a1fa8b}, +{15100000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 151_quant.dat*/ + + +/*case 15200000:*/ +/* BEGIN - DIF BPF register values from 152_quant.dat*/ +{15200000, DIF_BPF_COEFF01, 0x0001fffe}, +{15200000, DIF_BPF_COEFF23, 0xfffc0012}, +{15200000, DIF_BPF_COEFF45, 0xfffbffce}, +{15200000, DIF_BPF_COEFF67, 0x003f0033}, +{15200000, DIF_BPF_COEFF89, 0xff4e003f}, +{15200000, DIF_BPF_COEFF1011, 0x0106feb6}, +{15200000, DIF_BPF_COEFF1213, 0xff6e0276}, +{15200000, DIF_BPF_COEFF1415, 0xfeddfd56}, +{15200000, DIF_BPF_COEFF1617, 0x03b000cc}, +{15200000, DIF_BPF_COEFF1819, 0xfa740329}, +{15200000, DIF_BPF_COEFF2021, 0x04bff87f}, +{15200000, DIF_BPF_COEFF2223, 0xffaa095d}, +{15200000, DIF_BPF_COEFF2425, 0xf99ef995}, +{15200000, DIF_BPF_COEFF2627, 0x0bf9fed8}, +{15200000, DIF_BPF_COEFF2829, 0xf3590a1f}, +{15200000, DIF_BPF_COEFF3031, 0x06d2f05e}, +{15200000, DIF_BPF_COEFF3233, 0x03700e1b}, +{15200000, DIF_BPF_COEFF3435, 0xf2e4fa58}, +{15200000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 152_quant.dat*/ + + +/*case 115300000:*/ +/* BEGIN - DIF BPF register values from 153_quant.dat*/ +{15300000, DIF_BPF_COEFF01, 0x0001ffff}, +{15300000, DIF_BPF_COEFF23, 0xfff9000f}, +{15300000, DIF_BPF_COEFF45, 0x0009ffc8}, +{15300000, DIF_BPF_COEFF67, 0x00250059}, +{15300000, DIF_BPF_COEFF89, 0xff5effee}, +{15300000, DIF_BPF_COEFF1011, 0x0132ff10}, +{15300000, DIF_BPF_COEFF1213, 0xfee30265}, +{15300000, DIF_BPF_COEFF1415, 0xffaafccf}, +{15300000, DIF_BPF_COEFF1617, 0x031101eb}, +{15300000, DIF_BPF_COEFF1819, 0xfa6001e8}, +{15300000, DIF_BPF_COEFF2021, 0x05bdf92f}, +{15300000, DIF_BPF_COEFF2223, 0xfe1b09b6}, +{15300000, DIF_BPF_COEFF2425, 0xfafaf852}, +{15300000, DIF_BPF_COEFF2627, 0x0b7e0055}, +{15300000, DIF_BPF_COEFF2829, 0xf2d50929}, +{15300000, DIF_BPF_COEFF3031, 0x07d3f086}, +{15300000, DIF_BPF_COEFF3233, 0x02a30e6c}, +{15300000, DIF_BPF_COEFF3435, 0xf329fa24}, +{15300000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 153_quant.dat*/ + + +/*case 115400000:*/ +/* BEGIN - DIF BPF register values from 154_quant.dat*/ +{15400000, DIF_BPF_COEFF01, 0x00010001}, +{15400000, DIF_BPF_COEFF23, 0xfff80009}, +{15400000, DIF_BPF_COEFF45, 0x0015ffca}, +{15400000, DIF_BPF_COEFF67, 0x00050074}, +{15400000, DIF_BPF_COEFF89, 0xff81ff9f}, +{15400000, DIF_BPF_COEFF1011, 0x013dff82}, +{15400000, DIF_BPF_COEFF1213, 0xfe710221}, +{15400000, DIF_BPF_COEFF1415, 0x007cfc80}, +{15400000, DIF_BPF_COEFF1617, 0x024102ed}, +{15400000, DIF_BPF_COEFF1819, 0xfa940090}, +{15400000, DIF_BPF_COEFF2021, 0x0680fa1e}, +{15400000, DIF_BPF_COEFF2223, 0xfc9b09cd}, +{15400000, DIF_BPF_COEFF2425, 0xfc73f736}, +{15400000, DIF_BPF_COEFF2627, 0x0ad501d0}, +{15400000, DIF_BPF_COEFF2829, 0xf2740820}, +{15400000, DIF_BPF_COEFF3031, 0x08c9f0bd}, +{15400000, DIF_BPF_COEFF3233, 0x01d40eb9}, +{15400000, DIF_BPF_COEFF3435, 0xf371f9f1}, +{15400000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 154_quant.dat*/ + + +/*case 115500000:*/ +/* BEGIN - DIF BPF register values from 155_quant.dat*/ +{15500000, DIF_BPF_COEFF01, 0x00000002}, +{15500000, DIF_BPF_COEFF23, 0xfff80002}, +{15500000, DIF_BPF_COEFF45, 0x001effd5}, +{15500000, DIF_BPF_COEFF67, 0xffe5007f}, +{15500000, DIF_BPF_COEFF89, 0xffb4ff5b}, +{15500000, DIF_BPF_COEFF1011, 0x01280000}, +{15500000, DIF_BPF_COEFF1213, 0xfe2401b0}, +{15500000, DIF_BPF_COEFF1415, 0x0146fc70}, +{15500000, DIF_BPF_COEFF1617, 0x014d03c6}, +{15500000, DIF_BPF_COEFF1819, 0xfb10ff32}, +{15500000, DIF_BPF_COEFF2021, 0x0701fb41}, +{15500000, DIF_BPF_COEFF2223, 0xfb3709a1}, +{15500000, DIF_BPF_COEFF2425, 0xfe00f644}, +{15500000, DIF_BPF_COEFF2627, 0x0a000345}, +{15500000, DIF_BPF_COEFF2829, 0xf2350708}, +{15500000, DIF_BPF_COEFF3031, 0x09b2f104}, +{15500000, DIF_BPF_COEFF3233, 0x01050eff}, +{15500000, DIF_BPF_COEFF3435, 0xf3baf9be}, +{15500000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 155_quant.dat*/ + + +/*case 115600000:*/ +/* BEGIN - DIF BPF register values from 156_quant.dat*/ +{15600000, DIF_BPF_COEFF01, 0x00000003}, +{15600000, DIF_BPF_COEFF23, 0xfff9fffb}, +{15600000, DIF_BPF_COEFF45, 0x0022ffe6}, +{15600000, DIF_BPF_COEFF67, 0xffc9007a}, +{15600000, DIF_BPF_COEFF89, 0xfff0ff29}, +{15600000, DIF_BPF_COEFF1011, 0x00f2007e}, +{15600000, DIF_BPF_COEFF1213, 0xfe01011b}, +{15600000, DIF_BPF_COEFF1415, 0x01f6fc9e}, +{15600000, DIF_BPF_COEFF1617, 0x00440467}, +{15600000, DIF_BPF_COEFF1819, 0xfbccfdde}, +{15600000, DIF_BPF_COEFF2021, 0x0738fc90}, +{15600000, DIF_BPF_COEFF2223, 0xf9f70934}, +{15600000, DIF_BPF_COEFF2425, 0xff99f582}, +{15600000, DIF_BPF_COEFF2627, 0x090204b0}, +{15600000, DIF_BPF_COEFF2829, 0xf21a05e1}, +{15600000, DIF_BPF_COEFF3031, 0x0a8df15a}, +{15600000, DIF_BPF_COEFF3233, 0x00340f41}, +{15600000, DIF_BPF_COEFF3435, 0xf405f98b}, +{15600000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 156_quant.dat*/ + + +/*case 115700000:*/ +/* BEGIN - DIF BPF register values from 157_quant.dat*/ +{15700000, DIF_BPF_COEFF01, 0x00000003}, +{15700000, DIF_BPF_COEFF23, 0xfffcfff4}, +{15700000, DIF_BPF_COEFF45, 0x0020fffa}, +{15700000, DIF_BPF_COEFF67, 0xffb40064}, +{15700000, DIF_BPF_COEFF89, 0x002fff11}, +{15700000, DIF_BPF_COEFF1011, 0x00a400f0}, +{15700000, DIF_BPF_COEFF1213, 0xfe0d006e}, +{15700000, DIF_BPF_COEFF1415, 0x0281fd09}, +{15700000, DIF_BPF_COEFF1617, 0xff3604c9}, +{15700000, DIF_BPF_COEFF1819, 0xfcbffca2}, +{15700000, DIF_BPF_COEFF2021, 0x0726fdfe}, +{15700000, DIF_BPF_COEFF2223, 0xf8e80888}, +{15700000, DIF_BPF_COEFF2425, 0x0134f4f3}, +{15700000, DIF_BPF_COEFF2627, 0x07e1060c}, +{15700000, DIF_BPF_COEFF2829, 0xf22304af}, +{15700000, DIF_BPF_COEFF3031, 0x0b59f1be}, +{15700000, DIF_BPF_COEFF3233, 0xff640f7d}, +{15700000, DIF_BPF_COEFF3435, 0xf452f959}, +{15700000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 157_quant.dat*/ + + +/*case 115800000:*/ +/* BEGIN - DIF BPF register values from 158_quant.dat*/ +{15800000, DIF_BPF_COEFF01, 0x00000003}, +{15800000, DIF_BPF_COEFF23, 0x0000fff0}, +{15800000, DIF_BPF_COEFF45, 0x001a0010}, +{15800000, DIF_BPF_COEFF67, 0xffaa0041}, +{15800000, DIF_BPF_COEFF89, 0x0067ff13}, +{15800000, DIF_BPF_COEFF1011, 0x0043014a}, +{15800000, DIF_BPF_COEFF1213, 0xfe46ffb9}, +{15800000, DIF_BPF_COEFF1415, 0x02dbfda8}, +{15800000, DIF_BPF_COEFF1617, 0xfe3504e5}, +{15800000, DIF_BPF_COEFF1819, 0xfddcfb8d}, +{15800000, DIF_BPF_COEFF2021, 0x06c9ff7e}, +{15800000, DIF_BPF_COEFF2223, 0xf81107a2}, +{15800000, DIF_BPF_COEFF2425, 0x02c9f49a}, +{15800000, DIF_BPF_COEFF2627, 0x069f0753}, +{15800000, DIF_BPF_COEFF2829, 0xf2500373}, +{15800000, DIF_BPF_COEFF3031, 0x0c14f231}, +{15800000, DIF_BPF_COEFF3233, 0xfe930fb3}, +{15800000, DIF_BPF_COEFF3435, 0xf4a1f927}, +{15800000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 158_quant.dat*/ + + +/*case 115900000:*/ +/* BEGIN - DIF BPF register values from 159_quant.dat*/ +{15900000, DIF_BPF_COEFF01, 0xffff0002}, +{15900000, DIF_BPF_COEFF23, 0x0003ffee}, +{15900000, DIF_BPF_COEFF45, 0x000f0023}, +{15900000, DIF_BPF_COEFF67, 0xffac0016}, +{15900000, DIF_BPF_COEFF89, 0x0093ff31}, +{15900000, DIF_BPF_COEFF1011, 0xffdc0184}, +{15900000, DIF_BPF_COEFF1213, 0xfea6ff09}, +{15900000, DIF_BPF_COEFF1415, 0x02fdfe70}, +{15900000, DIF_BPF_COEFF1617, 0xfd5104ba}, +{15900000, DIF_BPF_COEFF1819, 0xff15faac}, +{15900000, DIF_BPF_COEFF2021, 0x06270103}, +{15900000, DIF_BPF_COEFF2223, 0xf7780688}, +{15900000, DIF_BPF_COEFF2425, 0x044df479}, +{15900000, DIF_BPF_COEFF2627, 0x05430883}, +{15900000, DIF_BPF_COEFF2829, 0xf2a00231}, +{15900000, DIF_BPF_COEFF3031, 0x0cbef2b2}, +{15900000, DIF_BPF_COEFF3233, 0xfdc40fe3}, +{15900000, DIF_BPF_COEFF3435, 0xf4f2f8f5}, +{15900000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 159_quant.dat*/ + + +/*case 116000000:*/ +/* BEGIN - DIF BPF register values from 160_quant.dat*/ +{16000000, DIF_BPF_COEFF01, 0xffff0001}, +{16000000, DIF_BPF_COEFF23, 0x0006ffef}, +{16000000, DIF_BPF_COEFF45, 0x00020031}, +{16000000, DIF_BPF_COEFF67, 0xffbaffe8}, +{16000000, DIF_BPF_COEFF89, 0x00adff66}, +{16000000, DIF_BPF_COEFF1011, 0xff790198}, +{16000000, DIF_BPF_COEFF1213, 0xff26fe6e}, +{16000000, DIF_BPF_COEFF1415, 0x02e5ff55}, +{16000000, DIF_BPF_COEFF1617, 0xfc99044a}, +{16000000, DIF_BPF_COEFF1819, 0x005bfa09}, +{16000000, DIF_BPF_COEFF2021, 0x0545027f}, +{16000000, DIF_BPF_COEFF2223, 0xf7230541}, +{16000000, DIF_BPF_COEFF2425, 0x05b8f490}, +{16000000, DIF_BPF_COEFF2627, 0x03d20997}, +{16000000, DIF_BPF_COEFF2829, 0xf31300eb}, +{16000000, DIF_BPF_COEFF3031, 0x0d55f341}, +{16000000, DIF_BPF_COEFF3233, 0xfcf6100e}, +{16000000, DIF_BPF_COEFF3435, 0xf544f8c3}, +{16000000, DIF_BPF_COEFF36, 0x110d0000}, +/* END - DIF BPF register values from 160_quant.dat*/ +}; + +#endif diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c new file mode 100644 index 000000000000..7c4e360ba9bc --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -0,0 +1,796 @@ +/* + DVB device driver for cx231xx + + Copyright (C) 2008 + Based on em28xx driver + + This program is free software; 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 "cx231xx.h" +#include +#include + +#include "xc5000.h" +#include "s5h1432.h" +#include "tda18271.h" +#include "s5h1411.h" +#include "lgdt3305.h" +#include "mb86a20s.h" + +MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); +MODULE_AUTHOR("Srinivasa Deevi "); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ +} while (0) + +#define CX231XX_DVB_NUM_BUFS 5 +#define CX231XX_DVB_MAX_PACKETSIZE 564 +#define CX231XX_DVB_MAX_PACKETS 64 + +struct cx231xx_dvb { + struct dvb_frontend *frontend; + + /* feed count management */ + struct mutex lock; + int nfeeds; + + /* general boilerplate stuff */ + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; +}; + +static struct s5h1432_config dvico_s5h1432_config = { + .output_mode = S5H1432_SERIAL_OUTPUT, + .gpio = S5H1432_GPIO_ON, + .qam_if = S5H1432_IF_4000, + .vsb_if = S5H1432_IF_4000, + .inversion = S5H1432_INVERSION_OFF, + .status_mode = S5H1432_DEMODLOCKING, + .mpeg_timing = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + +static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = { + .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 4000, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4000, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_std_map mb86a20s_tda18271_config = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 7, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config cnxt_rde253s_tunerconfig = { + .std_map = &cnxt_rde253s_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + +static struct s5h1411_config tda18271_s5h1411_config = { + .output_mode = S5H1411_SERIAL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .vsb_if = S5H1411_IF_3250, + .qam_if = S5H1411_IF_4000, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; +static struct s5h1411_config xc5000_s5h1411_config = { + .output_mode = S5H1411_SERIAL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .vsb_if = S5H1411_IF_3250, + .qam_if = S5H1411_IF_3250, + .inversion = S5H1411_INVERSION_OFF, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + +static struct lgdt3305_config hcw_lgdt3305_config = { + .i2c_addr = 0x0e, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x58, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x58, }, +}; + +static struct tda18271_config hcw_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_DIGITAL, +}; + +static const struct mb86a20s_config pv_mb86a20s_config = { + .demod_address = 0x10, + .is_serial = true, +}; + +static struct tda18271_config pv_tda18271_config = { + .std_map = &mb86a20s_tda18271_config, + .gate = TDA18271_GATE_DIGITAL, + .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, +}; + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(1, "URB status %d [%s].\n", status, errmsg); + } else { + dprintk(1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb) +{ + int i; + + if (!dev) + return 0; + + if (dev->state & DEV_DISCONNECTED) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + dvb_dmx_swfilter(&dev->dvb->demux, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + + return 0; +} + +static inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb) +{ + if (!dev) + return 0; + + if (dev->state & DEV_DISCONNECTED) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + /* Feed the transport payload into the kernel demux */ + dvb_dmx_swfilter(&dev->dvb->demux, + urb->transfer_buffer, urb->actual_length); + + return 0; +} + +static int start_streaming(struct cx231xx_dvb *dvb) +{ + int rc; + struct cx231xx *dev = dvb->adapter.priv; + + if (dev->USE_ISO) { + cx231xx_info("DVB transfer mode is ISO.\n"); + mutex_lock(&dev->i2c_lock); + cx231xx_enable_i2c_port_3(dev, false); + cx231xx_set_alt_setting(dev, INDEX_TS1, 4); + cx231xx_enable_i2c_port_3(dev, true); + mutex_unlock(&dev->i2c_lock); + rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + if (rc < 0) + return rc; + dev->mode_tv = 1; + return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS, + CX231XX_DVB_NUM_BUFS, + dev->ts1_mode.max_pkt_size, + dvb_isoc_copy); + } else { + cx231xx_info("DVB transfer mode is BULK.\n"); + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + if (rc < 0) + return rc; + dev->mode_tv = 1; + return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS, + CX231XX_DVB_NUM_BUFS, + dev->ts1_mode.max_pkt_size, + dvb_bulk_copy); + } + +} + +static int stop_streaming(struct cx231xx_dvb *dvb) +{ + struct cx231xx *dev = dvb->adapter.priv; + + if (dev->USE_ISO) + cx231xx_uninit_isoc(dev); + else + cx231xx_uninit_bulk(dev); + + cx231xx_set_mode(dev, CX231XX_SUSPEND); + + return 0; +} + +static int start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx231xx_dvb *dvb = demux->priv; + int rc, ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->lock); + return rc; +} + +static int stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx231xx_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + + if (0 == dvb->nfeeds) + err = stop_streaming(dvb); + + mutex_unlock(&dvb->lock); + return err; +} + +/* ------------------------------------------------------------------ */ +static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct cx231xx *dev = fe->dvb->priv; + + if (acquire) + return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + else + return cx231xx_set_mode(dev, CX231XX_SUSPEND); +} + +/* ------------------------------------------------------------------ */ + +static struct xc5000_config cnxt_rde250_tunerconfig = { + .i2c_address = 0x61, + .if_khz = 4000, +}; +static struct xc5000_config cnxt_rdu250_tunerconfig = { + .i2c_address = 0x61, + .if_khz = 3250, +}; + +/* ------------------------------------------------------------------ */ +#if 0 +static int attach_xc5000(u8 addr, struct cx231xx *dev) +{ + + struct dvb_frontend *fe; + struct xc5000_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_adap = &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap; + cfg.i2c_addr = addr; + + if (!dev->dvb->frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc5000\n", dev->name); + return -EINVAL; + } + + fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name); + dvb_frontend_detach(dev->dvb->frontend); + dev->dvb->frontend = NULL; + return -EINVAL; + } + + printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name); + + return 0; +} +#endif + +int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) +{ + int status = 0; + + if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + + struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + + if (dops->set_analog_params != NULL) { + struct analog_parameters params; + + params.frequency = freq; + params.std = dev->norm; + params.mode = 0; /* 0- Air; 1 - cable */ + /*params.audmode = ; */ + + /* Set the analog parameters to set the frequency */ + dops->set_analog_params(dev->dvb->frontend, ¶ms); + } + + } + + return status; +} + +int cx231xx_reset_analog_tuner(struct cx231xx *dev) +{ + int status = 0; + + if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + + struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + + if (dops->init != NULL && !dev->xc_fw_load_done) { + + cx231xx_info("Reloading firmware for XC5000\n"); + status = dops->init(dev->dvb->frontend); + if (status == 0) { + dev->xc_fw_load_done = 1; + cx231xx_info + ("XC5000 firmware download completed\n"); + } else { + dev->xc_fw_load_done = 0; + cx231xx_info + ("XC5000 firmware download failed !!!\n"); + } + } + + } + + return status; +} + +/* ------------------------------------------------------------------ */ + +static int register_dvb(struct cx231xx_dvb *dvb, + struct module *module, + struct cx231xx *dev, struct device *device) +{ + int result; + + mutex_init(&dvb->lock); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, + adapter_nr); + if (result < 0) { + printk(KERN_WARNING + "%s: dvb_register_adapter failed (errno = %d)\n", + dev->name, result); + goto fail_adapter; + } + + /* Ensure all frontends negotiate bus access */ + dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; + + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (result < 0) { + printk(KERN_WARNING + "%s: dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = start_feed; + dvb->demux.stop_feed = stop_feed; + + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING + "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_WARNING + "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dev->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING + "%s: connect_frontend failed (errno = %d)\n", dev->name, + result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +static void unregister_dvb(struct cx231xx_dvb *dvb) +{ + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +} + +static int dvb_init(struct cx231xx *dev) +{ + int result = 0; + struct cx231xx_dvb *dvb; + + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + return 0; + } + + dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL); + + if (dvb == NULL) { + printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n"); + return -ENOMEM; + } + dev->dvb = dvb; + dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq; + dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner; + + mutex_lock(&dev->lock); + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + cx231xx_demod_reset(dev); + /* init frontend */ + switch (dev->model) { + case CX231XX_BOARD_CNXT_CARRAERA: + case CX231XX_BOARD_CNXT_RDE_250: + + dev->dvb->frontend = dvb_attach(s5h1432_attach, + &dvico_s5h1432_config, + &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach s5h1432 front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (!dvb_attach(xc5000_attach, dev->dvb->frontend, + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &cnxt_rde250_tunerconfig)) { + result = -EINVAL; + goto out_free; + } + + break; + case CX231XX_BOARD_CNXT_SHELBY: + case CX231XX_BOARD_CNXT_RDU_250: + + dev->dvb->frontend = dvb_attach(s5h1411_attach, + &xc5000_s5h1411_config, + &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach s5h1411 front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (!dvb_attach(xc5000_attach, dev->dvb->frontend, + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &cnxt_rdu250_tunerconfig)) { + result = -EINVAL; + goto out_free; + } + break; + case CX231XX_BOARD_CNXT_RDE_253S: + + dev->dvb->frontend = dvb_attach(s5h1432_attach, + &dvico_s5h1432_config, + &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach s5h1432 front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (!dvb_attach(tda18271_attach, dev->dvb->frontend, + 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &cnxt_rde253s_tunerconfig)) { + result = -EINVAL; + goto out_free; + } + break; + case CX231XX_BOARD_CNXT_RDU_253S: + + dev->dvb->frontend = dvb_attach(s5h1411_attach, + &tda18271_s5h1411_config, + &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach s5h1411 front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (!dvb_attach(tda18271_attach, dev->dvb->frontend, + 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &cnxt_rde253s_tunerconfig)) { + result = -EINVAL; + goto out_free; + } + break; + case CX231XX_BOARD_HAUPPAUGE_EXETER: + + printk(KERN_INFO "%s: looking for tuner / demod on i2c bus: %d\n", + __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap)); + + dev->dvb->frontend = dvb_attach(lgdt3305_attach, + &hcw_lgdt3305_config, + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach LG3305 front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + dvb_attach(tda18271_attach, dev->dvb->frontend, + 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &hcw_tda18271_config); + break; + + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: + case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: + + printk(KERN_INFO "%s: looking for demod on i2c bus: %d\n", + __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap)); + + dev->dvb->frontend = dvb_attach(mb86a20s_attach, + &pv_mb86a20s_config, + &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach mb86a20s demod\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + dvb_attach(tda18271_attach, dev->dvb->frontend, + 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &pv_tda18271_config); + break; + + default: + printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n", dev->name); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR + "%s/2: frontend initialization failed\n", dev->name); + result = -EINVAL; + goto out_free; + } + + /* register everything */ + result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + + if (result < 0) + goto out_free; + + + printk(KERN_INFO "Successfully loaded cx231xx-dvb\n"); + +ret: + cx231xx_set_mode(dev, CX231XX_SUSPEND); + mutex_unlock(&dev->lock); + return result; + +out_free: + kfree(dvb); + dev->dvb = NULL; + goto ret; +} + +static int dvb_fini(struct cx231xx *dev) +{ + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + return 0; + } + + if (dev->dvb) { + unregister_dvb(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct cx231xx_ops dvb_ops = { + .id = CX231XX_DVB, + .name = "Cx231xx dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init cx231xx_dvb_register(void) +{ + return cx231xx_register_extension(&dvb_ops); +} + +static void __exit cx231xx_dvb_unregister(void) +{ + cx231xx_unregister_extension(&dvb_ops); +} + +module_init(cx231xx_dvb_register); +module_exit(cx231xx_dvb_unregister); diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c new file mode 100644 index 000000000000..781feed406f7 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -0,0 +1,532 @@ +/* + cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 + Based on em28xx driver + Based on Cx23885 driver + + This program is free software; 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 "cx231xx.h" + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define dprintk1(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(fmt, ##args); \ + } \ +} while (0) + +#define dprintk2(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__ , ##args); \ + } \ +} while (0) + +static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus, + const struct i2c_msg *msg, int tuner_type) +{ + if (bus->nr != dev->board.tuner_i2c_master) + return false; + + if (msg->addr != dev->board.tuner_addr) + return false; + + if (dev->tuner_type != tuner_type) + return false; + + return true; +} + +/* + * cx231xx_i2c_send_bytes() + */ +int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 size = 0; + u8 loop = 0; + u8 saddr_len = 1; + u8 *buf_ptr = NULL; + u16 saddr = 0; + u8 need_gpio = 0; + + if (is_tuner(dev, bus, msg, TUNER_XC5000)) { + size = msg->len; + + if (size == 2) { /* register write sub addr */ + /* Just writing sub address will cause problem + * to XC5000. So ignore the request */ + return 0; + } else if (size == 4) { /* register write with sub addr */ + if (msg->len >= 2) + saddr = msg->buf[0] << 8 | msg->buf[1]; + else if (msg->len == 1) + saddr = msg->buf[0]; + + switch (saddr) { + case 0x0000: /* start tuner calibration mode */ + need_gpio = 1; + /* FW Loading is done */ + dev->xc_fw_load_done = 1; + break; + case 0x000D: /* Set signal source */ + case 0x0001: /* Set TV standard - Video */ + case 0x0002: /* Set TV standard - Audio */ + case 0x0003: /* Set RF Frequency */ + need_gpio = 1; + break; + default: + if (dev->xc_fw_load_done) + need_gpio = 1; + break; + } + + if (need_gpio) { + dprintk1(1, + "GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n", + msg->addr, msg->len, saddr); + + return dev->cx231xx_gpio_i2c_write(dev, + msg->addr, + msg->buf, + msg->len); + } + } + + /* special case for Xc5000 tuner case */ + saddr_len = 1; + + /* adjust the length to correct length */ + size -= saddr_len; + buf_ptr = (u8 *) (msg->buf + 1); + + do { + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = msg->buf[0]; + req_data.buf_size = size > 16 ? 16 : size; + req_data.p_buffer = (u8 *) (buf_ptr + loop * 16); + + bus->i2c_nostop = (size > 16) ? 1 : 0; + bus->i2c_reserve = (loop == 0) ? 0 : 1; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + loop++; + + if (size >= 16) + size -= 16; + else + size = 0; + + } while (size > 0); + + bus->i2c_nostop = 0; + bus->i2c_reserve = 0; + + } else { /* regular case */ + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + } + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_recv_bytes() + * read a byte from the i2c device + */ +static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 saddr = 0; + u8 need_gpio = 0; + + if (is_tuner(dev, bus, msg, TUNER_XC5000)) { + if (msg->len == 2) + saddr = msg->buf[0] << 8 | msg->buf[1]; + else if (msg->len == 1) + saddr = msg->buf[0]; + + if (dev->xc_fw_load_done) { + + switch (saddr) { + case 0x0009: /* BUSY check */ + dprintk1(1, + "GPIO R E A D: Special case BUSY check \n"); + /*Try read BUSY register, just set it to zero*/ + msg->buf[0] = 0; + if (msg->len == 2) + msg->buf[1] = 0; + return 0; + case 0x0004: /* read Lock status */ + need_gpio = 1; + break; + + } + + if (need_gpio) { + /* this is a special case to handle Xceive tuner + clock stretch issue with gpio based I2C */ + + dprintk1(1, + "GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n", + msg->addr, msg->len, + msg->buf[0] << 8 | msg->buf[1]); + + status = + dev->cx231xx_gpio_i2c_write(dev, msg->addr, + msg->buf, + msg->len); + status = + dev->cx231xx_gpio_i2c_read(dev, msg->addr, + msg->buf, + msg->len); + return status; + } + } + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = msg->len; + req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1]; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + } else { + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + } + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_recv_bytes_with_saddr() + * read a byte from the i2c device + */ +static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg1, + const struct i2c_msg *msg2) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 saddr = 0; + u8 need_gpio = 0; + + if (msg1->len == 2) + saddr = msg1->buf[0] << 8 | msg1->buf[1]; + else if (msg1->len == 1) + saddr = msg1->buf[0]; + + if (is_tuner(dev, bus, msg2, TUNER_XC5000)) { + if ((msg2->len < 16)) { + + dprintk1(1, + "i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n", + msg2->addr, msg2->len, saddr, msg1->len); + + switch (saddr) { + case 0x0008: /* read FW load status */ + need_gpio = 1; + break; + case 0x0004: /* read Lock status */ + need_gpio = 1; + break; + } + + if (need_gpio) { + status = + dev->cx231xx_gpio_i2c_write(dev, msg1->addr, + msg1->buf, + msg1->len); + status = + dev->cx231xx_gpio_i2c_read(dev, msg2->addr, + msg2->buf, + msg2->len); + return status; + } + } + } + + /* prepare xfer_data struct */ + req_data.dev_addr = msg2->addr; + req_data.direction = msg2->flags; + req_data.saddr_len = msg1->len; + req_data.saddr_dat = saddr; + req_data.buf_size = msg2->len; + req_data.p_buffer = msg2->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_check_for_device() + * check if there is a i2c_device at the supplied address + */ +static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = 0; + req_data.p_buffer = NULL; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_xfer() + * the main i2c transfer function + */ +static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + mutex_lock(&dev->i2c_lock); + for (i = 0; i < num; i++) { + + addr = msgs[i].addr >> 1; + + dprintk2(2, "%s %s addr=%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (!msgs[i].len) { + /* no len: check only for device presence */ + rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]); + if (rc < 0) { + dprintk2(2, " no device\n"); + mutex_unlock(&dev->i2c_lock); + return rc; + } + + } else if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]); + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr + && (msgs[i].len <= 2) && (bus->nr < 3)) { + /* read bytes */ + rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap, + &msgs[i], + &msgs[i + 1]); + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + i++; + } else { + /* write bytes */ + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]); + } + if (rc < 0) + goto err; + if (i2c_debug >= 2) + printk("\n"); + } + mutex_unlock(&dev->i2c_lock); + return num; +err: + dprintk2(2, " ERROR: %i\n", rc); + mutex_unlock(&dev->i2c_lock); + return rc; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm cx231xx_algo = { + .master_xfer = cx231xx_i2c_xfer, + .functionality = functionality, +}; + +static struct i2c_adapter cx231xx_adap_template = { + .owner = THIS_MODULE, + .name = "cx231xx", + .algo = &cx231xx_algo, +}; + +static struct i2c_client cx231xx_client_template = { + .name = "cx231xx internal", +}; + +/* ----------------------------------------------------------- */ + +/* + * i2c_devs + * incomplete list of known devices + */ +static char *i2c_devs[128] = { + [0x60 >> 1] = "colibri", + [0x88 >> 1] = "hammerhead", + [0x8e >> 1] = "CIR", + [0x32 >> 1] = "GeminiIII", + [0x02 >> 1] = "Aquarius", + [0xa0 >> 1] = "eeprom", + [0xc0 >> 1] = "tuner", + [0xc2 >> 1] = "tuner", +}; + +/* + * cx231xx_do_i2c_scan() + * check i2c address range for devices + */ +void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c) +{ + unsigned char buf; + int i, rc; + + cx231xx_info(": Checking for I2C devices ..\n"); + for (i = 0; i < 128; i++) { + c->addr = i; + rc = i2c_master_recv(c, &buf, 0); + if (rc < 0) + continue; + cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n", + dev->name, i << 1, + i2c_devs[i] ? i2c_devs[i] : "???"); + } + cx231xx_info(": Completed Checking for I2C devices.\n"); +} + +/* + * cx231xx_i2c_register() + * register i2c bus + */ +int cx231xx_i2c_register(struct cx231xx_i2c *bus) +{ + struct cx231xx *dev = bus->dev; + + BUG_ON(!dev->cx231xx_send_usb_command); + + bus->i2c_adap = cx231xx_adap_template; + bus->i2c_client = cx231xx_client_template; + bus->i2c_adap.dev.parent = &dev->udev->dev; + + strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name)); + + bus->i2c_adap.algo_data = bus; + i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); + i2c_add_adapter(&bus->i2c_adap); + + bus->i2c_client.adapter = &bus->i2c_adap; + + if (0 == bus->i2c_rc) { + if (i2c_scan) + cx231xx_do_i2c_scan(dev, &bus->i2c_client); + } else + cx231xx_warn("%s: i2c bus %d register FAILED\n", + dev->name, bus->nr); + + return bus->i2c_rc; +} + +/* + * cx231xx_i2c_unregister() + * unregister i2c_bus + */ +int cx231xx_i2c_unregister(struct cx231xx_i2c *bus) +{ + i2c_del_adapter(&bus->i2c_adap); + return 0; +} diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c new file mode 100644 index 000000000000..96176e9db5a2 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-input.c @@ -0,0 +1,119 @@ +/* + * cx231xx IR glue driver + * + * Copyright (C) 2010 Mauro Carvalho Chehab + * + * Polaris (cx231xx) has its support for IR's with a design close to MCE. + * however, a few designs are using an external I2C chip for IR, instead + * of using the one provided by the chip. + * This driver provides support for those extra devices + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include "cx231xx.h" +#include +#include + +#define MODULE_NAME "cx231xx-input" + +static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + int rc; + u8 cmd, scancode; + + dev_dbg(&ir->rc->input_dev->dev, "%s\n", __func__); + + /* poll IR chip */ + rc = i2c_master_recv(ir->c, &cmd, 1); + if (rc < 0) + return rc; + if (rc != 1) + return -EIO; + + /* it seems that 0xFE indicates that a button is still hold + down, while 0xff indicates that no button is hold + down. 0xfe sequences are sometimes interrupted by 0xFF */ + + if (cmd == 0xff) + return 0; + + scancode = + ((cmd & 0x01) ? 0x80 : 0) | + ((cmd & 0x02) ? 0x40 : 0) | + ((cmd & 0x04) ? 0x20 : 0) | + ((cmd & 0x08) ? 0x10 : 0) | + ((cmd & 0x10) ? 0x08 : 0) | + ((cmd & 0x20) ? 0x04 : 0) | + ((cmd & 0x40) ? 0x02 : 0) | + ((cmd & 0x80) ? 0x01 : 0); + + dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n", + cmd, scancode); + + *ir_key = scancode; + *ir_raw = scancode; + return 1; +} + +int cx231xx_ir_init(struct cx231xx *dev) +{ + struct i2c_board_info info; + u8 ir_i2c_bus; + + dev_dbg(&dev->udev->dev, "%s\n", __func__); + + /* Only initialize if a rc keycode map is defined */ + if (!cx231xx_boards[dev->model].rc_map_name) + return -ENODEV; + + request_module("ir-kbd-i2c"); + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + dev->init_data.rc_dev = rc_allocate_device(); + if (!dev->init_data.rc_dev) + return -ENOMEM; + + dev->init_data.name = cx231xx_boards[dev->model].name; + + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + info.platform_data = &dev->init_data; + + /* + * Board-dependent values + * + * For now, there's just one type of hardware design using + * an i2c device. + */ + dev->init_data.get_key = get_key_isdbt; + dev->init_data.ir_codes = cx231xx_boards[dev->model].rc_map_name; + /* The i2c micro-controller only outputs the cmd part of NEC protocol */ + dev->init_data.rc_dev->scanmask = 0xff; + dev->init_data.rc_dev->driver_name = "cx231xx"; + dev->init_data.type = RC_TYPE_NEC; + info.addr = 0x30; + + /* Load and bind ir-kbd-i2c */ + ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master; + dev_dbg(&dev->udev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n", + ir_i2c_bus, info.addr); + dev->ir_i2c_client = i2c_new_device(&dev->i2c_bus[ir_i2c_bus].i2c_adap, &info); + + return 0; +} + +void cx231xx_ir_exit(struct cx231xx *dev) +{ + if (dev->ir_i2c_client) + i2c_unregister_device(dev->ir_i2c_client); + dev->ir_i2c_client = NULL; +} diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c new file mode 100644 index 000000000000..7473c33e823e --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c @@ -0,0 +1,795 @@ +/* + cx231xx-pcb-config.c - driver for Conexant + Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 + + This program is free software; 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 "cx231xx.h" +#include "cx231xx-conf-reg.h" + +static unsigned int pcb_debug; +module_param(pcb_debug, int, 0644); +MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]"); + +/******************************************************************************/ + +struct pcb_config cx231xx_Scenario[] = { + { + INDEX_SELFPOWER_DIGITAL_ONLY, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode */ + SOURCE_TS_BDA, /* ts1_source, digital tv only */ + NOT_SUPPORTED, /* ts2_source */ + NOT_SUPPORTED, /* analog source */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + , + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed config */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_DUAL_DIGITAL, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode */ + SOURCE_TS_BDA, /* ts1_source, digital tv only */ + 0, /* ts2_source,need update from register */ + NOT_SUPPORTED, /* analog source */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_ANALOG_ONLY, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */ + NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */ + NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ + 0, /* analog source, need update */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + 3, /* VANC */ + 4, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_DUAL, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv and digital path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + 0, /* ts1_source,will update in register */ + NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ + 0, /* analog source need update */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_TRIPLE, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv and digital path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + 0, /* ts1_source, update in register */ + 0, /* ts2_source,update in register */ + 0, /* analog source, need update */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + 3, /* AUDIO */ + 4, /* VIDEO */ + 5, /* VANC */ + 6, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + 3, /* AUDIO */ + 4, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_COMPRESSOR, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv AND DIGITAL path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + NOT_SUPPORTED, /* ts1_source, disable */ + SOURCE_TS_BDA, /* ts2_source */ + 0, /* analog source,need update */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + 1, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + 1, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_BUSPOWER_DIGITAL_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */ + SOURCE_TS_BDA, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + NOT_SUPPORTED, /* analog source */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index = 2 */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index = 2 */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + { + INDEX_BUSPOWER_ANALOG_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */ + NOT_SUPPORTED, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + SOURCE_ANALOG, /* analog source--analog */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + 3, /* VANC */ + 4, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + { /* full-speed */ + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + { + INDEX_BUSPOWER_DIF_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv AND DIGITAL path */ + MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL, + SOURCE_TS_BDA, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + { /* full speed */ + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + +}; + +/*****************************************************************/ + +u32 initialize_cx231xx(struct cx231xx *dev) +{ + u32 config_info = 0; + struct pcb_config *p_pcb_info; + u8 usb_speed = 1; /* from register,1--HS, 0--FS */ + u8 data[4] = { 0, 0, 0, 0 }; + u32 ts1_source = 0; + u32 ts2_source = 0; + u32 analog_source = 0; + u8 _current_scenario_idx = 0xff; + + ts1_source = SOURCE_TS_BDA; + ts2_source = SOURCE_TS_BDA; + + /* read board config register to find out which + pcb config it is related to */ + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4); + + config_info = *((u32 *) data); + usb_speed = (u8) (config_info & 0x1); + + /* Verify this device belongs to Bus power or Self power device */ + if (config_info & BUS_POWER) { /* bus-power */ + switch (config_info & BUSPOWER_MASK) { + case TS1_PORT | BUS_POWER: + cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY; + break; + case AVDEC_ENABLE | BUS_POWER: + cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY; + break; + case AVDEC_ENABLE | BUS_POWER | TS1_PORT: + cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed = + usb_speed; + p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY; + break; + default: + cx231xx_info("bad config in buspower!!!!\n"); + cx231xx_info("config_info=%x\n", + (config_info & BUSPOWER_MASK)); + return 1; + } + } else { /* self-power */ + + switch (config_info & SELFPOWER_MASK) { + case TS1_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY]; + _current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY; + break; + case TS1_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]. + ts2_source = ts2_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]; + _current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL; + break; + case AVDEC_ENABLE | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]. + analog_source = analog_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]; + _current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY; + break; + case AVDEC_ENABLE | TS1_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source = + ts1_source; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source = + analog_source; + p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL]; + _current_scenario_idx = INDEX_SELFPOWER_DUAL; + break; + case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source = + ts1_source; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source = + ts2_source; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source = + analog_source; + p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE]; + _current_scenario_idx = INDEX_SELFPOWER_TRIPLE; + break; + case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]. + analog_source = analog_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]; + _current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR; + break; + default: + cx231xx_info("bad senario!!!!!\n"); + cx231xx_info("config_info=%x\n", + (config_info & SELFPOWER_MASK)); + return 1; + } + } + + dev->current_scenario_idx = _current_scenario_idx; + + memcpy(&dev->current_pcb_config, p_pcb_info, + sizeof(struct pcb_config)); + + if (pcb_debug) { + cx231xx_info("SC(0x00) register = 0x%x\n", config_info); + cx231xx_info("scenario %d\n", + (dev->current_pcb_config.index) + 1); + cx231xx_info("type=%x\n", dev->current_pcb_config.type); + cx231xx_info("mode=%x\n", dev->current_pcb_config.mode); + cx231xx_info("speed=%x\n", dev->current_pcb_config.speed); + cx231xx_info("ts1_source=%x\n", + dev->current_pcb_config.ts1_source); + cx231xx_info("ts2_source=%x\n", + dev->current_pcb_config.ts2_source); + cx231xx_info("analog_source=%x\n", + dev->current_pcb_config.analog_source); + } + + return 0; +} diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h new file mode 100644 index 000000000000..f5e46e89f3ab --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h @@ -0,0 +1,231 @@ +/* + cx231xx-pcb-cfg.h - driver for Conexant + Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 + + This program is free software; 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 _PCB_CONFIG_H_ +#define _PCB_CONFIG_H_ + +#include +#include + +/*************************************************************************** + * Class Information * +***************************************************************************/ +#define CLASS_DEFAULT 0xFF + +enum VENDOR_REQUEST_TYPE { + /* Set/Get I2C */ + VRT_SET_I2C0 = 0x0, + VRT_SET_I2C1 = 0x1, + VRT_SET_I2C2 = 0x2, + VRT_GET_I2C0 = 0x4, + VRT_GET_I2C1 = 0x5, + VRT_GET_I2C2 = 0x6, + + /* Set/Get GPIO */ + VRT_SET_GPIO = 0x8, + VRT_GET_GPIO = 0x9, + + /* Set/Get GPIE */ + VRT_SET_GPIE = 0xA, + VRT_GET_GPIE = 0xB, + + /* Set/Get Register Control/Status */ + VRT_SET_REGISTER = 0xC, + VRT_GET_REGISTER = 0xD, + + /* Get Extended Compat ID Descriptor */ + VRT_GET_EXTCID_DESC = 0xFF, +}; + +enum BYTE_ENABLE_MASK { + ENABLE_ONE_BYTE = 0x1, + ENABLE_TWE_BYTE = 0x3, + ENABLE_THREE_BYTE = 0x7, + ENABLE_FOUR_BYTE = 0xF, +}; + +#define SPEED_MASK 0x1 +enum USB_SPEED{ + FULL_SPEED = 0x0, /* 0: full speed */ + HIGH_SPEED = 0x1 /* 1: high speed */ +}; + +enum _true_false{ + FALSE = 0, + TRUE = 1 +}; + +#define TS_MASK 0x6 +enum TS_PORT{ + NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid, + only offers Analog TV or Video */ + TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode : + Digital or External Analog/Compressed source) */ + TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs + (Dual inputs from Digital and/or + External Analog/Compressed sources) */ + TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector + to external clock */ + TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output, + TS2 Input (from Compressor) */ +}; + +#define EAVP_MASK 0x8 +enum EAV_PRESENT{ + NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs + (no need for i2s blcok), + Analog Tuner must be present */ + EXTERNAL_AV = 0x8 /* 1: External A/V inputs + present (requires i2s blk) */ +}; + +#define ATM_MASK 0x30 +enum AT_MODE{ + DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */ + BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite & + Sound-IF Signals present */ + NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */ +}; + +#define PWR_SEL_MASK 0x40 +enum POWE_TYPE{ + SELF_POWER = 0x0, /* 0: self power */ + BUS_POWER = 0x40 /* 1: bus power */ +}; + +enum USB_POWE_TYPE{ + USB_SELF_POWER = 0, + USB_BUS_POWER +}; + +#define BO_0_MASK 0x80 +enum AVDEC_STATUS{ + AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */ + AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */ +}; + +#define BO_1_MASK 0x100 + +#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */ +#define SELFPOWER_MASK 0x86 + +/***************************************************************************/ +#define NOT_DECIDE_YET 0xFE +#define NOT_SUPPORTED 0xFF + +/*************************************************************************** + * for mod field use * +***************************************************************************/ +#define MOD_DIGITAL 0x1 +#define MOD_ANALOG 0x2 +#define MOD_DIF 0x4 +#define MOD_EXTERNAL 0x8 +#define CAP_ALL_MOD 0x0f + +/*************************************************************************** + * source define * +***************************************************************************/ +#define SOURCE_DIGITAL 0x1 +#define SOURCE_ANALOG 0x2 +#define SOURCE_DIF 0x4 +#define SOURCE_EXTERNAL 0x8 +#define SOURCE_TS_BDA 0x10 +#define SOURCE_TS_ENCODE 0x20 +#define SOURCE_TS_EXTERNAL 0x40 + +/*************************************************************************** + * interface information define * +***************************************************************************/ +struct INTERFACE_INFO { + u8 interrupt_index; + u8 ts1_index; + u8 ts2_index; + u8 audio_index; + u8 video_index; + u8 vanc_index; /* VBI */ + u8 hanc_index; /* Sliced CC */ + u8 ir_index; +}; + +enum INDEX_INTERFACE_INFO{ + INDEX_INTERRUPT = 0x0, + INDEX_TS1, + INDEX_TS2, + INDEX_AUDIO, + INDEX_VIDEO, + INDEX_VANC, + INDEX_HANC, + INDEX_IR, +}; + +/*************************************************************************** + * configuration information define * +***************************************************************************/ +struct CONFIG_INFO { + u8 config_index; + struct INTERFACE_INFO interface_info; +}; + +struct pcb_config { + u8 index; + u8 type; /* bus power or self power, + self power--0, bus_power--1 */ + u8 speed; /* usb speed, 2.0--1, 1.1--0 */ + u8 mode; /* digital , anlog, dif or external A/V */ + u32 ts1_source; /* three source -- BDA,External,encode */ + u32 ts2_source; + u32 analog_source; + u8 digital_index; /* bus-power used */ + u8 analog_index; /* bus-power used */ + u8 dif_index; /* bus-power used */ + u8 external_index; /* bus-power used */ + u8 config_num; /* current config num, 0,1,2, + for self-power, always 0 */ + struct CONFIG_INFO hs_config_info[3]; + struct CONFIG_INFO fs_config_info[3]; +}; + +enum INDEX_PCB_CONFIG{ + INDEX_SELFPOWER_DIGITAL_ONLY = 0x0, + INDEX_SELFPOWER_DUAL_DIGITAL, + INDEX_SELFPOWER_ANALOG_ONLY, + INDEX_SELFPOWER_DUAL, + INDEX_SELFPOWER_TRIPLE, + INDEX_SELFPOWER_COMPRESSOR, + INDEX_BUSPOWER_DIGITAL_ONLY, + INDEX_BUSPOWER_ANALOG_ONLY, + INDEX_BUSPOWER_DIF_ONLY, + INDEX_BUSPOWER_EXTERNAL_ONLY, + INDEX_BUSPOWER_EXTERNAL_ANALOG, + INDEX_BUSPOWER_EXTERNAL_DIF, + INDEX_BUSPOWER_EXTERNAL_DIGITAL, + INDEX_BUSPOWER_DIGITAL_ANALOG, + INDEX_BUSPOWER_DIGITAL_DIF, + INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL, + INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL, +}; + +/***************************************************************************/ +struct cx231xx; + +u32 initialize_cx231xx(struct cx231xx *p_dev); + +#endif diff --git a/drivers/media/usb/cx231xx/cx231xx-reg.h b/drivers/media/usb/cx231xx/cx231xx-reg.h new file mode 100644 index 000000000000..750c5d37d569 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-reg.h @@ -0,0 +1,1564 @@ +/* + cx231xx-reg.h - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 + + This program is free software; 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 _CX231XX_REG_H +#define _CX231XX_REG_H + +/***************************************************************************** + * VBI codes * +*****************************************************************************/ + +#define SAV_ACTIVE_VIDEO_FIELD1 0x80 +#define EAV_ACTIVE_VIDEO_FIELD1 0x90 + +#define SAV_ACTIVE_VIDEO_FIELD2 0xc0 +#define EAV_ACTIVE_VIDEO_FIELD2 0xd0 + +#define SAV_VBLANK_FIELD1 0xa0 +#define EAV_VBLANK_FIELD1 0xb0 + +#define SAV_VBLANK_FIELD2 0xe0 +#define EAV_VBLANK_FIELD2 0xf0 + +#define SAV_VBI_FIELD1 0x20 +#define EAV_VBI_FIELD1 0x30 + +#define SAV_VBI_FIELD2 0x60 +#define EAV_VBI_FIELD2 0x70 + +/*****************************************************************************/ +/* Audio ADC Registers */ +#define CH_PWR_CTRL1 0x0000000e +#define CH_PWR_CTRL2 0x0000000f +/*****************************************************************************/ + +#define HOST_REG1 0x000 +#define FLD_FORCE_CHIP_SEL 0x80 +#define FLD_AUTO_INC_DIS 0x20 +#define FLD_PREFETCH_EN 0x10 +/* Reserved [2:3] */ +#define FLD_DIGITAL_PWR_DN 0x02 +#define FLD_SLEEP 0x01 + +/*****************************************************************************/ +#define HOST_REG2 0x001 + +/*****************************************************************************/ +#define HOST_REG3 0x002 + +/*****************************************************************************/ +/* added for polaris */ +#define GPIO_PIN_CTL0 0x3 +#define GPIO_PIN_CTL1 0x4 +#define GPIO_PIN_CTL2 0x5 +#define GPIO_PIN_CTL3 0x6 +#define TS1_PIN_CTL0 0x7 +#define TS1_PIN_CTL1 0x8 +/*****************************************************************************/ + +#define FLD_CLK_IN_EN 0x80 +#define FLD_XTAL_CTRL 0x70 +#define FLD_BB_CLK_MODE 0x0C +#define FLD_REF_DIV_PLL 0x02 +#define FLD_REF_SEL_PLL1 0x01 + +/*****************************************************************************/ +#define CHIP_CTRL 0x100 +/* Reserved [27] */ +/* Reserved [31:21] */ +#define FLD_CHIP_ACFG_DIS 0x00100000 +/* Reserved [19] */ +#define FLD_DUAL_MODE_ADC2 0x00040000 +#define FLD_SIF_EN 0x00020000 +#define FLD_SOFT_RST 0x00010000 +#define FLD_DEVICE_ID 0x0000ffff + +/*****************************************************************************/ +#define AFE_CTRL 0x104 +#define AFE_CTRL_C2HH_SRC_CTRL 0x104 +#define FLD_DIF_OUT_SEL 0xc0000000 +#define FLD_AUX_PLL_CLK_ALT_SEL 0x3c000000 +#define FLD_UV_ORDER_MODE 0x02000000 +#define FLD_FUNC_MODE 0x01800000 +#define FLD_ROT1_PHASE_CTL 0x007f8000 +#define FLD_AUD_IN_SEL 0x00004000 +#define FLD_LUMA_IN_SEL 0x00002000 +#define FLD_CHROMA_IN_SEL 0x00001000 +/* reserve [11:10] */ +#define FLD_INV_SPEC_DIS 0x00000200 +#define FLD_VGA_SEL_CH3 0x00000100 +#define FLD_VGA_SEL_CH2 0x00000080 +#define FLD_VGA_SEL_CH1 0x00000040 +#define FLD_DCR_BYP_CH1 0x00000020 +#define FLD_DCR_BYP_CH2 0x00000010 +#define FLD_DCR_BYP_CH3 0x00000008 +#define FLD_EN_12DB_CH3 0x00000004 +#define FLD_EN_12DB_CH2 0x00000002 +#define FLD_EN_12DB_CH1 0x00000001 + +/* redefine in Cx231xx */ +/*****************************************************************************/ +#define DC_CTRL1 0x108 +/* reserve [31:30] */ +#define FLD_CLAMP_LVL_CH1 0x3fff8000 +#define FLD_CLAMP_LVL_CH2 0x00007fff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL2 0x10c +/* reserve [31:28] */ +#define FLD_CLAMP_LVL_CH3 0x00fffe00 +#define FLD_CLAMP_WIND_LENTH 0x000001e0 +#define FLD_C2HH_SAT_MIN 0x0000001e +#define FLD_FLT_BYP_SEL 0x00000001 +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL3 0x110 +/* reserve [31:16] */ +#define FLD_ERR_GAIN_CTL 0x00070000 +#define FLD_LPF_MIN 0x0000ffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL4 0x114 +/* reserve [31:31] */ +#define FLD_INTG_CH1 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL5 0x118 +/* reserve [31:31] */ +#define FLD_INTG_CH2 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL6 0x11c +/* reserve [31:31] */ +#define FLD_INTG_CH3 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define PIN_CTRL 0x120 +#define FLD_OEF_AGC_RF 0x00000001 +#define FLD_OEF_AGC_IFVGA 0x00000002 +#define FLD_OEF_AGC_IF 0x00000004 +#define FLD_REG_BO_PUD 0x80000000 +#define FLD_IR_IRQ_STAT 0x40000000 +#define FLD_AUD_IRQ_STAT 0x20000000 +#define FLD_VID_IRQ_STAT 0x10000000 +/* Reserved [27:26] */ +#define FLD_IRQ_N_OUT_EN 0x02000000 +#define FLD_IRQ_N_POLAR 0x01000000 +/* Reserved [23:6] */ +#define FLD_OE_AUX_PLL_CLK 0x00000020 +#define FLD_OE_I2S_BCLK 0x00000010 +#define FLD_OE_I2S_WCLK 0x00000008 +#define FLD_OE_AGC_IF 0x00000004 +#define FLD_OE_AGC_IFVGA 0x00000002 +#define FLD_OE_AGC_RF 0x00000001 + +/*****************************************************************************/ +#define AUD_IO_CTRL 0x124 +/* Reserved [31:8] */ +#define FLD_I2S_PORT_DIR 0x00000080 +#define FLD_I2S_OUT_SRC 0x00000040 +#define FLD_AUD_CHAN3_SRC 0x00000030 +#define FLD_AUD_CHAN2_SRC 0x0000000c +#define FLD_AUD_CHAN1_SRC 0x00000003 + +/*****************************************************************************/ +#define AUD_LOCK1 0x128 +#define FLD_AUD_LOCK_KI_SHIFT 0xc0000000 +#define FLD_AUD_LOCK_KD_SHIFT 0x30000000 +/* Reserved [27:25] */ +#define FLD_EN_AV_LOCK 0x01000000 +#define FLD_VID_COUNT 0x00ffffff + +/*****************************************************************************/ +#define AUD_LOCK2 0x12c +#define FLD_AUD_LOCK_KI_MULT 0xf0000000 +#define FLD_AUD_LOCK_KD_MULT 0x0F000000 +/* Reserved [23:22] */ +#define FLD_AUD_LOCK_FREQ_SHIFT 0x00300000 +#define FLD_AUD_COUNT 0x000fffff + +/*****************************************************************************/ +#define AFE_DIAG_CTRL1 0x134 +/* Reserved [31:16] */ +#define FLD_CUV_DLY_LENGTH 0x0000ff00 +#define FLD_YC_DLY_LENGTH 0x000000ff + +/*****************************************************************************/ +/* Poalris redefine */ +#define AFE_DIAG_CTRL3 0x138 +/* Reserved [31:26] */ +#define FLD_AUD_DUAL_FLAG_POL 0x02000000 +#define FLD_VID_DUAL_FLAG_POL 0x01000000 +/* Reserved [23:23] */ +#define FLD_COL_CLAMP_DIS_CH1 0x00400000 +#define FLD_COL_CLAMP_DIS_CH2 0x00200000 +#define FLD_COL_CLAMP_DIS_CH3 0x00100000 + +#define TEST_CTRL1 0x144 +/* Reserved [31:29] */ +#define FLD_LBIST_EN 0x10000000 +/* Reserved [27:10] */ +#define FLD_FI_BIST_INTR_R 0x0000200 +#define FLD_FI_BIST_INTR_L 0x0000100 +#define FLD_BIST_FAIL_AUD_PLL 0x0000080 +#define FLD_BIST_INTR_AUD_PLL 0x0000040 +#define FLD_BIST_FAIL_VID_PLL 0x0000020 +#define FLD_BIST_INTR_VID_PLL 0x0000010 +/* Reserved [3:1] */ +#define FLD_CIR_TEST_DIS 0x00000001 + +/*****************************************************************************/ +#define TEST_CTRL2 0x148 +#define FLD_TSXCLK_POL_CTL 0x80000000 +#define FLD_ISO_CTL_SEL 0x40000000 +#define FLD_ISO_CTL_EN 0x20000000 +#define FLD_BIST_DEBUGZ 0x10000000 +#define FLD_AUD_BIST_TEST_H 0x0f000000 +/* Reserved [23:22] */ +#define FLD_FLTRN_BIST_TEST_H 0x00020000 +#define FLD_VID_BIST_TEST_H 0x00010000 +/* Reserved [19:17] */ +#define FLD_BIST_TEST_H 0x00010000 +/* Reserved [15:13] */ +#define FLD_TAB_EN 0x00001000 +/* Reserved [11:0] */ + +/*****************************************************************************/ +#define BIST_STAT 0x14c +#define FLD_AUD_BIST_FAIL_H 0xfff00000 +#define FLD_FLTRN_BIST_FAIL_H 0x00180000 +#define FLD_VID_BIST_FAIL_H 0x00070000 +#define FLD_AUD_BIST_TST_DONE 0x0000fff0 +#define FLD_FLTRN_BIST_TST_DONE 0x00000008 +#define FLD_VID_BIST_TST_DONE 0x00000007 + +/*****************************************************************************/ +/* DirectIF registers definition have been moved to DIF_reg.h */ +/*****************************************************************************/ +#define MODE_CTRL 0x400 +#define FLD_AFD_PAL60_DIS 0x20000000 +#define FLD_AFD_FORCE_SECAM 0x10000000 +#define FLD_AFD_FORCE_PALNC 0x08000000 +#define FLD_AFD_FORCE_PAL 0x04000000 +#define FLD_AFD_PALM_SEL 0x03000000 +#define FLD_CKILL_MODE 0x00300000 +#define FLD_COMB_NOTCH_MODE 0x00c00000 /* bit[19:18] */ +#define FLD_CLR_LOCK_STAT 0x00020000 +#define FLD_FAST_LOCK_MD 0x00010000 +#define FLD_WCEN 0x00008000 +#define FLD_CAGCEN 0x00004000 +#define FLD_CKILLEN 0x00002000 +#define FLD_AUTO_SC_LOCK 0x00001000 +#define FLD_MAN_SC_FAST_LOCK 0x00000800 +#define FLD_INPUT_MODE 0x00000600 +#define FLD_AFD_ACQUIRE 0x00000100 +#define FLD_AFD_NTSC_SEL 0x00000080 +#define FLD_AFD_PAL_SEL 0x00000040 +#define FLD_ACFG_DIS 0x00000020 +#define FLD_SQ_PIXEL 0x00000010 +#define FLD_VID_FMT_SEL 0x0000000f + +/*****************************************************************************/ +#define OUT_CTRL1 0x404 +#define FLD_POLAR 0x7f000000 +/* Reserved [23] */ +#define FLD_RND_MODE 0x00600000 +#define FLD_VIPCLAMP_EN 0x00100000 +#define FLD_VIPBLANK_EN 0x00080000 +#define FLD_VIP_OPT_AL 0x00040000 +#define FLD_IDID0_SOURCE 0x00020000 +#define FLD_DCMODE 0x00010000 +#define FLD_CLK_GATING 0x0000c000 +#define FLD_CLK_INVERT 0x00002000 +#define FLD_HSFMT 0x00001000 +#define FLD_VALIDFMT 0x00000800 +#define FLD_ACTFMT 0x00000400 +#define FLD_SWAPRAW 0x00000200 +#define FLD_CLAMPRAW_EN 0x00000100 +#define FLD_BLUE_FIELD_EN 0x00000080 +#define FLD_BLUE_FIELD_ACT 0x00000040 +#define FLD_TASKBIT_VAL 0x00000020 +#define FLD_ANC_DATA_EN 0x00000010 +#define FLD_VBIHACTRAW_EN 0x00000008 +#define FLD_MODE10B 0x00000004 +#define FLD_OUT_MODE 0x00000003 + +/*****************************************************************************/ +#define OUT_CTRL2 0x408 +#define FLD_AUD_GRP 0xc0000000 +#define FLD_SAMPLE_RATE 0x30000000 +#define FLD_AUD_ANC_EN 0x08000000 +#define FLD_EN_C 0x04000000 +#define FLD_EN_B 0x02000000 +#define FLD_EN_A 0x01000000 +/* Reserved [23:20] */ +#define FLD_IDID1_LSB 0x000c0000 +#define FLD_IDID0_LSB 0x00030000 +#define FLD_IDID1_MSB 0x0000ff00 +#define FLD_IDID0_MSB 0x000000ff + +/*****************************************************************************/ +#define GEN_STAT 0x40c +#define FLD_VCR_DETECT 0x00800000 +#define FLD_SPECIAL_PLAY_N 0x00400000 +#define FLD_VPRES 0x00200000 +#define FLD_AGC_LOCK 0x00100000 +#define FLD_CSC_LOCK 0x00080000 +#define FLD_VLOCK 0x00040000 +#define FLD_SRC_LOCK 0x00020000 +#define FLD_HLOCK 0x00010000 +#define FLD_VSYNC_N 0x00008000 +#define FLD_SRC_FIFO_UFLOW 0x00004000 +#define FLD_SRC_FIFO_OFLOW 0x00002000 +#define FLD_FIELD 0x00001000 +#define FLD_AFD_FMT_STAT 0x00000f00 +#define FLD_MV_TYPE2_PAIR 0x00000080 +#define FLD_MV_T3CS 0x00000040 +#define FLD_MV_CS 0x00000020 +#define FLD_MV_PSP 0x00000010 +/* Reserved [3] */ +#define FLD_MV_CDAT 0x00000003 + +/*****************************************************************************/ +#define INT_STAT_MASK 0x410 +#define FLD_COMB_3D_FIFO_MSK 0x80000000 +#define FLD_WSS_DAT_AVAIL_MSK 0x40000000 +#define FLD_GS2_DAT_AVAIL_MSK 0x20000000 +#define FLD_GS1_DAT_AVAIL_MSK 0x10000000 +#define FLD_CC_DAT_AVAIL_MSK 0x08000000 +#define FLD_VPRES_CHANGE_MSK 0x04000000 +#define FLD_MV_CHANGE_MSK 0x02000000 +#define FLD_END_VBI_EVEN_MSK 0x01000000 +#define FLD_END_VBI_ODD_MSK 0x00800000 +#define FLD_FMT_CHANGE_MSK 0x00400000 +#define FLD_VSYNC_TRAIL_MSK 0x00200000 +#define FLD_HLOCK_CHANGE_MSK 0x00100000 +#define FLD_VLOCK_CHANGE_MSK 0x00080000 +#define FLD_CSC_LOCK_CHANGE_MSK 0x00040000 +#define FLD_SRC_FIFO_UFLOW_MSK 0x00020000 +#define FLD_SRC_FIFO_OFLOW_MSK 0x00010000 +#define FLD_COMB_3D_FIFO_STAT 0x00008000 +#define FLD_WSS_DAT_AVAIL_STAT 0x00004000 +#define FLD_GS2_DAT_AVAIL_STAT 0x00002000 +#define FLD_GS1_DAT_AVAIL_STAT 0x00001000 +#define FLD_CC_DAT_AVAIL_STAT 0x00000800 +#define FLD_VPRES_CHANGE_STAT 0x00000400 +#define FLD_MV_CHANGE_STAT 0x00000200 +#define FLD_END_VBI_EVEN_STAT 0x00000100 +#define FLD_END_VBI_ODD_STAT 0x00000080 +#define FLD_FMT_CHANGE_STAT 0x00000040 +#define FLD_VSYNC_TRAIL_STAT 0x00000020 +#define FLD_HLOCK_CHANGE_STAT 0x00000010 +#define FLD_VLOCK_CHANGE_STAT 0x00000008 +#define FLD_CSC_LOCK_CHANGE_STAT 0x00000004 +#define FLD_SRC_FIFO_UFLOW_STAT 0x00000002 +#define FLD_SRC_FIFO_OFLOW_STAT 0x00000001 + +/*****************************************************************************/ +#define LUMA_CTRL 0x414 +#define BRIGHTNESS_CTRL_BYTE 0x414 +#define CONTRAST_CTRL_BYTE 0x415 +#define LUMA_CTRL_BYTE_3 0x416 +#define FLD_LUMA_CORE_SEL 0x00c00000 +#define FLD_RANGE 0x00300000 +/* Reserved [19] */ +#define FLD_PEAK_EN 0x00040000 +#define FLD_PEAK_SEL 0x00030000 +#define FLD_CNTRST 0x0000ff00 +#define FLD_BRITE 0x000000ff + +/*****************************************************************************/ +#define HSCALE_CTRL 0x418 +#define FLD_HFILT 0x03000000 +#define FLD_HSCALE 0x00ffffff + +/*****************************************************************************/ +#define VSCALE_CTRL 0x41c +#define FLD_LINE_AVG_DIS 0x01000000 +/* Reserved [23:20] */ +#define FLD_VS_INTRLACE 0x00080000 +#define FLD_VFILT 0x00070000 +/* Reserved [15:13] */ +#define FLD_VSCALE 0x00001fff + +/*****************************************************************************/ +#define CHROMA_CTRL 0x420 +#define USAT_CTRL_BYTE 0x420 +#define VSAT_CTRL_BYTE 0x421 +#define HUE_CTRL_BYTE 0x422 +#define FLD_C_LPF_EN 0x20000000 +#define FLD_CHR_DELAY 0x1c000000 +#define FLD_C_CORE_SEL 0x03000000 +#define FLD_HUE 0x00ff0000 +#define FLD_VSAT 0x0000ff00 +#define FLD_USAT 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL1 0x424 +#define FLD_VBI_MD_LINE4 0xff000000 +#define FLD_VBI_MD_LINE3 0x00ff0000 +#define FLD_VBI_MD_LINE2 0x0000ff00 +#define FLD_VBI_MD_LINE1 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL2 0x428 +#define FLD_VBI_MD_LINE8 0xff000000 +#define FLD_VBI_MD_LINE7 0x00ff0000 +#define FLD_VBI_MD_LINE6 0x0000ff00 +#define FLD_VBI_MD_LINE5 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL3 0x42c +#define FLD_VBI_MD_LINE12 0xff000000 +#define FLD_VBI_MD_LINE11 0x00ff0000 +#define FLD_VBI_MD_LINE10 0x0000ff00 +#define FLD_VBI_MD_LINE9 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL4 0x430 +#define FLD_VBI_MD_LINE16 0xff000000 +#define FLD_VBI_MD_LINE15 0x00ff0000 +#define FLD_VBI_MD_LINE14 0x0000ff00 +#define FLD_VBI_MD_LINE13 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL5 0x434 +#define FLD_VBI_MD_LINE17 0x000000ff + +/*****************************************************************************/ +#define VBI_FC_CFG 0x438 +#define FLD_FC_ALT2 0xff000000 +#define FLD_FC_ALT1 0x00ff0000 +#define FLD_FC_ALT2_TYPE 0x0000f000 +#define FLD_FC_ALT1_TYPE 0x00000f00 +/* Reserved [7:1] */ +#define FLD_FC_SEARCH_MODE 0x00000001 + +/*****************************************************************************/ +#define VBI_MISC_CFG1 0x43c +#define FLD_TTX_PKTADRU 0xfff00000 +#define FLD_TTX_PKTADRL 0x000fff00 +/* Reserved [7:6] */ +#define FLD_MOJI_PACK_DIS 0x00000020 +#define FLD_VPS_DEC_DIS 0x00000010 +#define FLD_CRI_MARG_SCALE 0x0000000c +#define FLD_EDGE_RESYNC_EN 0x00000002 +#define FLD_ADAPT_SLICE_DIS 0x00000001 + +/*****************************************************************************/ +#define VBI_MISC_CFG2 0x440 +#define FLD_HAMMING_TYPE 0x0f000000 +/* Reserved [23:20] */ +#define FLD_WSS_FIFO_RST 0x00080000 +#define FLD_GS2_FIFO_RST 0x00040000 +#define FLD_GS1_FIFO_RST 0x00020000 +#define FLD_CC_FIFO_RST 0x00010000 +/* Reserved [15:12] */ +#define FLD_VBI3_SDID 0x00000f00 +#define FLD_VBI2_SDID 0x000000f0 +#define FLD_VBI1_SDID 0x0000000f + +/*****************************************************************************/ +#define VBI_PAY1 0x444 +#define FLD_GS1_FIFO_DAT 0xFF000000 +#define FLD_GS1_STAT 0x00FF0000 +#define FLD_CC_FIFO_DAT 0x0000FF00 +#define FLD_CC_STAT 0x000000FF + +/*****************************************************************************/ +#define VBI_PAY2 0x448 +#define FLD_WSS_FIFO_DAT 0xff000000 +#define FLD_WSS_STAT 0x00ff0000 +#define FLD_GS2_FIFO_DAT 0x0000ff00 +#define FLD_GS2_STAT 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST1_CFG1 0x44c +/* Reserved [31] */ +#define FLD_VBI1_CRIWIN 0x7f000000 +#define FLD_VBI1_SLICE_DIST 0x00f00000 +#define FLD_VBI1_BITINC 0x000fff00 +#define FLD_VBI1_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST1_CFG2 0x450 +#define FLD_VBI1_FC_LENGTH 0x1f000000 +#define FLD_VBI1_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST1_CFG3 0x454 +#define FLD_VBI1_HAM_EN 0x80000000 +#define FLD_VBI1_FIFO_MODE 0x70000000 +#define FLD_VBI1_FORMAT_TYPE 0x0f000000 +#define FLD_VBI1_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI1_CRI_LENGTH 0x0000f000 +#define FLD_VBI1_CRI_MARGIN 0x00000f00 +#define FLD_VBI1_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST2_CFG1 0x458 +/* Reserved [31] */ +#define FLD_VBI2_CRIWIN 0x7f000000 +#define FLD_VBI2_SLICE_DIST 0x00f00000 +#define FLD_VBI2_BITINC 0x000fff00 +#define FLD_VBI2_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST2_CFG2 0x45c +#define FLD_VBI2_FC_LENGTH 0x1f000000 +#define FLD_VBI2_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST2_CFG3 0x460 +#define FLD_VBI2_HAM_EN 0x80000000 +#define FLD_VBI2_FIFO_MODE 0x70000000 +#define FLD_VBI2_FORMAT_TYPE 0x0f000000 +#define FLD_VBI2_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI2_CRI_LENGTH 0x0000f000 +#define FLD_VBI2_CRI_MARGIN 0x00000f00 +#define FLD_VBI2_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST3_CFG1 0x464 +/* Reserved [31] */ +#define FLD_VBI3_CRIWIN 0x7f000000 +#define FLD_VBI3_SLICE_DIST 0x00f00000 +#define FLD_VBI3_BITINC 0x000fff00 +#define FLD_VBI3_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST3_CFG2 0x468 +#define FLD_VBI3_FC_LENGTH 0x1f000000 +#define FLD_VBI3_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST3_CFG3 0x46c +#define FLD_VBI3_HAM_EN 0x80000000 +#define FLD_VBI3_FIFO_MODE 0x70000000 +#define FLD_VBI3_FORMAT_TYPE 0x0f000000 +#define FLD_VBI3_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI3_CRI_LENGTH 0x0000f000 +#define FLD_VBI3_CRI_MARGIN 0x00000f00 +#define FLD_VBI3_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define HORIZ_TIM_CTRL 0x470 +#define FLD_BGDEL_CNT 0xff000000 +/* Reserved [23:22] */ +#define FLD_HACTIVE_CNT 0x003ff000 +/* Reserved [11:10] */ +#define FLD_HBLANK_CNT 0x000003ff + +/*****************************************************************************/ +#define VERT_TIM_CTRL 0x474 +#define FLD_V656BLANK_CNT 0xff000000 +/* Reserved [23:22] */ +#define FLD_VACTIVE_CNT 0x003ff000 +/* Reserved [11:10] */ +#define FLD_VBLANK_CNT 0x000003ff + +/*****************************************************************************/ +#define SRC_COMB_CFG 0x478 +#define FLD_CCOMB_2LN_CHECK 0x80000000 +#define FLD_CCOMB_3LN_EN 0x40000000 +#define FLD_CCOMB_2LN_EN 0x20000000 +#define FLD_CCOMB_3D_EN 0x10000000 +/* Reserved [27] */ +#define FLD_LCOMB_3LN_EN 0x04000000 +#define FLD_LCOMB_2LN_EN 0x02000000 +#define FLD_LCOMB_3D_EN 0x01000000 +#define FLD_LUMA_LPF_SEL 0x00c00000 +#define FLD_UV_LPF_SEL 0x00300000 +#define FLD_BLEND_SLOPE 0x000f0000 +#define FLD_CCOMB_REDUCE_EN 0x00008000 +/* Reserved [14:10] */ +#define FLD_SRC_DECIM_RATIO 0x000003ff + +/*****************************************************************************/ +#define CHROMA_VBIOFF_CFG 0x47c +#define FLD_VBI_VOFFSET 0x1f000000 +/* Reserved [23:20] */ +#define FLD_SC_STEP 0x000fffff + +/*****************************************************************************/ +#define FIELD_COUNT 0x480 +#define FLD_FIELD_COUNT_FLD 0x000003ff + +/*****************************************************************************/ +#define MISC_TIM_CTRL 0x484 +#define FLD_DEBOUNCE_COUNT 0xc0000000 +#define FLD_VT_LINE_CNT_HYST 0x30000000 +/* Reserved [27] */ +#define FLD_AFD_STAT 0x07ff0000 +#define FLD_VPRES_VERT_EN 0x00008000 +/* Reserved [14:12] */ +#define FLD_HR32 0x00000800 +#define FLD_TDALGN 0x00000400 +#define FLD_TDFIELD 0x00000200 +/* Reserved [8:6] */ +#define FLD_TEMPDEC 0x0000003f + +/*****************************************************************************/ +#define DFE_CTRL1 0x488 +#define FLD_CLAMP_AUTO_EN 0x80000000 +#define FLD_AGC_AUTO_EN 0x40000000 +#define FLD_VGA_CRUSH_EN 0x20000000 +#define FLD_VGA_AUTO_EN 0x10000000 +#define FLD_VBI_GATE_EN 0x08000000 +#define FLD_CLAMP_LEVEL 0x07000000 +/* Reserved [23:22] */ +#define FLD_CLAMP_SKIP_CNT 0x00300000 +#define FLD_AGC_GAIN 0x000fff00 +/* Reserved [7:6] */ +#define FLD_VGA_GAIN 0x0000003f + +/*****************************************************************************/ +#define DFE_CTRL2 0x48c +#define FLD_VGA_ACQUIRE_RANGE 0x00ff0000 +#define FLD_VGA_TRACK_RANGE 0x0000ff00 +#define FLD_VGA_SYNC 0x000000ff + +/*****************************************************************************/ +#define DFE_CTRL3 0x490 +#define FLD_BP_PERCENT 0xff000000 +#define FLD_DFT_THRESHOLD 0x00ff0000 +/* Reserved [15:12] */ +#define FLD_SYNC_WIDTH_SEL 0x00000600 +#define FLD_BP_LOOP_GAIN 0x00000300 +#define FLD_SYNC_LOOP_GAIN 0x000000c0 +/* Reserved [5:4] */ +#define FLD_AGC_LOOP_GAIN 0x0000000c +#define FLD_DCC_LOOP_GAIN 0x00000003 + +/*****************************************************************************/ +#define PLL_CTRL 0x494 +#define FLD_PLL_KD 0xff000000 +#define FLD_PLL_KI 0x00ff0000 +#define FLD_PLL_MAX_OFFSET 0x0000ffff + +/*****************************************************************************/ +#define HTL_CTRL 0x498 +/* Reserved [31:24] */ +#define FLD_AUTO_LOCK_SPD 0x00080000 +#define FLD_MAN_FAST_LOCK 0x00040000 +#define FLD_HTL_15K_EN 0x00020000 +#define FLD_HTL_500K_EN 0x00010000 +#define FLD_HTL_KD 0x0000ff00 +#define FLD_HTL_KI 0x000000ff + +/*****************************************************************************/ +#define COMB_CTRL 0x49c +#define FLD_COMB_PHASE_LIMIT 0xff000000 +#define FLD_CCOMB_ERR_LIMIT 0x00ff0000 +#define FLD_LUMA_THRESHOLD 0x0000ff00 +#define FLD_LCOMB_ERR_LIMIT 0x000000ff + +/*****************************************************************************/ +#define CRUSH_CTRL 0x4a0 +#define FLD_WTW_EN 0x00400000 +#define FLD_CRUSH_FREQ 0x00200000 +#define FLD_MAJ_SEL_EN 0x00100000 +#define FLD_MAJ_SEL 0x000c0000 +/* Reserved [17:15] */ +#define FLD_SYNC_TIP_REDUCE 0x00007e00 +/* Reserved [8:6] */ +#define FLD_SYNC_TIP_INC 0x0000003f + +/*****************************************************************************/ +#define SOFT_RST_CTRL 0x4a4 +#define FLD_VD_SOFT_RST 0x00008000 +/* Reserved [14:12] */ +#define FLD_REG_RST_MSK 0x00000800 +#define FLD_VOF_RST_MSK 0x00000400 +#define FLD_MVDET_RST_MSK 0x00000200 +#define FLD_VBI_RST_MSK 0x00000100 +#define FLD_SCALE_RST_MSK 0x00000080 +#define FLD_CHROMA_RST_MSK 0x00000040 +#define FLD_LUMA_RST_MSK 0x00000020 +#define FLD_VTG_RST_MSK 0x00000010 +#define FLD_YCSEP_RST_MSK 0x00000008 +#define FLD_SRC_RST_MSK 0x00000004 +#define FLD_DFE_RST_MSK 0x00000002 +/* Reserved [0] */ + +/*****************************************************************************/ +#define MV_DT_CTRL1 0x4a8 +/* Reserved [31:29] */ +#define FLD_PSP_STOP_LINE 0x1f000000 +/* Reserved [23:21] */ +#define FLD_PSP_STRT_LINE 0x001f0000 +/* Reserved [15] */ +#define FLD_PSP_LLIMW 0x00007f00 +/* Reserved [7] */ +#define FLD_PSP_ULIMW 0x0000007f + +/*****************************************************************************/ +#define MV_DT_CTRL2 0x4aC +#define FLD_CS_STOPWIN 0xff000000 +#define FLD_CS_STRTWIN 0x00ff0000 +#define FLD_CS_WIDTH 0x0000ff00 +#define FLD_PSP_SPEC_VAL 0x000000ff + +/*****************************************************************************/ +#define MV_DT_CTRL3 0x4B0 +#define FLD_AUTO_RATE_DIS 0x80000000 +#define FLD_HLOCK_DIS 0x40000000 +#define FLD_SEL_FIELD_CNT 0x20000000 +#define FLD_CS_TYPE2_SEL 0x10000000 +#define FLD_CS_LINE_THRSH_SEL 0x08000000 +#define FLD_CS_ATHRESH_SEL 0x04000000 +#define FLD_PSP_SPEC_SEL 0x02000000 +#define FLD_PSP_LINES_SEL 0x01000000 +#define FLD_FIELD_CNT 0x00f00000 +#define FLD_CS_TYPE2_CNT 0x000fc000 +#define FLD_CS_LINE_CNT 0x00003f00 +#define FLD_CS_ATHRESH_LEV 0x000000ff + +/*****************************************************************************/ +#define CHIP_VERSION 0x4b4 +/* Cx231xx redefine */ +#define VERSION 0x4b4 +#define FLD_REV_ID 0x000000ff + +/*****************************************************************************/ +#define MISC_DIAG_CTRL 0x4b8 +/* Reserved [31:24] */ +#define FLD_SC_CONVERGE_THRESH 0x00ff0000 +#define FLD_CCOMB_ERR_LIMIT_3D 0x0000ff00 +#define FLD_LCOMB_ERR_LIMIT_3D 0x000000ff + +/*****************************************************************************/ +#define VBI_PASS_CTRL 0x4bc +#define FLD_VBI_PASS_MD 0x00200000 +#define FLD_VBI_SETUP_DIS 0x00100000 +#define FLD_PASS_LINE_CTRL 0x000fffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define VCR_DET_CTRL 0x4c0 +#define FLD_EN_FIELD_PHASE_DET 0x80000000 +#define FLD_EN_HEAD_SW_DET 0x40000000 +#define FLD_FIELD_PHASE_LENGTH 0x01ff0000 +/* Reserved [29:25] */ +#define FLD_FIELD_PHASE_DELAY 0x0000ff00 +#define FLD_FIELD_PHASE_LIMIT 0x000000f0 +#define FLD_HEAD_SW_DET_LIMIT 0x0000000f + +/*****************************************************************************/ +#define DL_CTL 0x800 +#define DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */ +#define DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */ +#define DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */ +#define DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */ +/* Reserved [31:5] */ +#define FLD_START_8051 0x10000000 +#define FLD_DL_ENABLE 0x08000000 +#define FLD_DL_AUTO_INC 0x04000000 +#define FLD_DL_MAP 0x03000000 + +/*****************************************************************************/ +#define STD_DET_STATUS 0x804 +#define FLD_SPARE_STATUS1 0xff000000 +#define FLD_SPARE_STATUS0 0x00ff0000 +#define FLD_MOD_DET_STATUS1 0x0000ff00 +#define FLD_MOD_DET_STATUS0 0x000000ff + +/*****************************************************************************/ +#define AUD_BUILD_NUM 0x806 +#define AUD_VER_NUM 0x807 +#define STD_DET_CTL 0x808 +#define STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */ +#define STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */ +#define FLD_SPARE_CTL0 0xff000000 +#define FLD_DIS_DBX 0x00800000 +#define FLD_DIS_BTSC 0x00400000 +#define FLD_DIS_NICAM_A2 0x00200000 +#define FLD_VIDEO_PRESENT 0x00100000 +#define FLD_DW8051_VIDEO_FORMAT 0x000f0000 +#define FLD_PREF_DEC_MODE 0x0000ff00 +#define FLD_AUD_CONFIG 0x000000ff + +/*****************************************************************************/ +#define DW8051_INT 0x80c +#define FLD_VIDEO_PRESENT_CHANGE 0x80000000 +#define FLD_VIDEO_CHANGE 0x40000000 +#define FLD_RDS_READY 0x20000000 +#define FLD_AC97_INT 0x10000000 +#define FLD_NICAM_BIT_ERROR_TOO_HIGH 0x08000000 +#define FLD_NICAM_LOCK 0x04000000 +#define FLD_NICAM_UNLOCK 0x02000000 +#define FLD_DFT4_TH_CMP 0x01000000 +/* Reserved [23:22] */ +#define FLD_LOCK_IND_INT 0x00200000 +#define FLD_DFT3_TH_CMP 0x00100000 +#define FLD_DFT2_TH_CMP 0x00080000 +#define FLD_DFT1_TH_CMP 0x00040000 +#define FLD_FM2_DFT_TH_CMP 0x00020000 +#define FLD_FM1_DFT_TH_CMP 0x00010000 +#define FLD_VIDEO_PRESENT_EN 0x00008000 +#define FLD_VIDEO_CHANGE_EN 0x00004000 +#define FLD_RDS_READY_EN 0x00002000 +#define FLD_AC97_INT_EN 0x00001000 +#define FLD_NICAM_BIT_ERROR_TOO_HIGH_EN 0x00000800 +#define FLD_NICAM_LOCK_EN 0x00000400 +#define FLD_NICAM_UNLOCK_EN 0x00000200 +#define FLD_DFT4_TH_CMP_EN 0x00000100 +/* Reserved [7] */ +#define FLD_DW8051_INT6_CTL1 0x00000040 +#define FLD_DW8051_INT5_CTL1 0x00000020 +#define FLD_DW8051_INT4_CTL1 0x00000010 +#define FLD_DW8051_INT3_CTL1 0x00000008 +#define FLD_DW8051_INT2_CTL1 0x00000004 +#define FLD_DW8051_INT1_CTL1 0x00000002 +#define FLD_DW8051_INT0_CTL1 0x00000001 + +/*****************************************************************************/ +#define GENERAL_CTL 0x810 +#define FLD_RDS_INT 0x80000000 +#define FLD_NBER_INT 0x40000000 +#define FLD_NLL_INT 0x20000000 +#define FLD_IFL_INT 0x10000000 +#define FLD_FDL_INT 0x08000000 +#define FLD_AFC_INT 0x04000000 +#define FLD_AMC_INT 0x02000000 +#define FLD_AC97_INT_CTL 0x01000000 +#define FLD_RDS_INT_DIS 0x00800000 +#define FLD_NBER_INT_DIS 0x00400000 +#define FLD_NLL_INT_DIS 0x00200000 +#define FLD_IFL_INT_DIS 0x00100000 +#define FLD_FDL_INT_DIS 0x00080000 +#define FLD_FC_INT_DIS 0x00040000 +#define FLD_AMC_INT_DIS 0x00020000 +#define FLD_AC97_INT_DIS 0x00010000 +#define FLD_REV_NUM 0x0000ff00 +/* Reserved [7:5] */ +#define FLD_DBX_SOFT_RESET_REG 0x00000010 +#define FLD_AD_SOFT_RESET_REG 0x00000008 +#define FLD_SRC_SOFT_RESET_REG 0x00000004 +#define FLD_CDMOD_SOFT_RESET 0x00000002 +#define FLD_8051_SOFT_RESET 0x00000001 + +/*****************************************************************************/ +#define AAGC_CTL 0x814 +#define FLD_AFE_12DB_EN 0x80000000 +#define FLD_AAGC_DEFAULT_EN 0x40000000 +#define FLD_AAGC_DEFAULT 0x3f000000 +/* Reserved [23] */ +#define FLD_AAGC_GAIN 0x00600000 +#define FLD_AAGC_TH 0x001f0000 +/* Reserved [15:14] */ +#define FLD_AAGC_HYST2 0x00003f00 +/* Reserved [7:6] */ +#define FLD_AAGC_HYST1 0x0000003f + +/*****************************************************************************/ +#define IF_SRC_CTL 0x818 +#define FLD_DBX_BYPASS 0x80000000 +/* Reserved [30:25] */ +#define FLD_IF_SRC_MODE 0x01000000 +/* Reserved [23:18] */ +#define FLD_IF_SRC_PHASE_INC 0x0001ffff + +/*****************************************************************************/ +#define ANALOG_DEMOD_CTL 0x81c +#define FLD_ROT1_PHACC_PROG 0xffff0000 +/* Reserved [15] */ +#define FLD_FM1_DELAY_FIX 0x00007000 +#define FLD_PDF4_SHIFT 0x00000c00 +#define FLD_PDF3_SHIFT 0x00000300 +#define FLD_PDF2_SHIFT 0x000000c0 +#define FLD_PDF1_SHIFT 0x00000030 +#define FLD_FMBYPASS_MODE2 0x00000008 +#define FLD_FMBYPASS_MODE1 0x00000004 +#define FLD_NICAM_MODE 0x00000002 +#define FLD_BTSC_FMRADIO_MODE 0x00000001 + +/*****************************************************************************/ +#define ROT_FREQ_CTL 0x820 +#define FLD_ROT3_PHACC_PROG 0xffff0000 +#define FLD_ROT2_PHACC_PROG 0x0000ffff + +/*****************************************************************************/ +#define FM_CTL 0x824 +#define FLD_FM2_DC_FB_SHIFT 0xf0000000 +#define FLD_FM2_DC_INT_SHIFT 0x0f000000 +#define FLD_FM2_AFC_RESET 0x00800000 +#define FLD_FM2_DC_PASS_IN 0x00400000 +#define FLD_FM2_DAGC_SHIFT 0x00380000 +#define FLD_FM2_CORDIC_SHIFT 0x00070000 +#define FLD_FM1_DC_FB_SHIFT 0x0000f000 +#define FLD_FM1_DC_INT_SHIFT 0x00000f00 +#define FLD_FM1_AFC_RESET 0x00000080 +#define FLD_FM1_DC_PASS_IN 0x00000040 +#define FLD_FM1_DAGC_SHIFT 0x00000038 +#define FLD_FM1_CORDIC_SHIFT 0x00000007 + +/*****************************************************************************/ +#define LPF_PDF_CTL 0x828 +/* Reserved [31:30] */ +#define FLD_LPF32_SHIFT1 0x30000000 +#define FLD_LPF32_SHIFT2 0x0c000000 +#define FLD_LPF160_SHIFTA 0x03000000 +#define FLD_LPF160_SHIFTB 0x00c00000 +#define FLD_LPF160_SHIFTC 0x00300000 +#define FLD_LPF32_COEF_SEL2 0x000c0000 +#define FLD_LPF32_COEF_SEL1 0x00030000 +#define FLD_LPF160_COEF_SELC 0x0000c000 +#define FLD_LPF160_COEF_SELB 0x00003000 +#define FLD_LPF160_COEF_SELA 0x00000c00 +#define FLD_LPF160_IN_EN_REG 0x00000300 +#define FLD_PDF4_PDF_SEL 0x000000c0 +#define FLD_PDF3_PDF_SEL 0x00000030 +#define FLD_PDF2_PDF_SEL 0x0000000c +#define FLD_PDF1_PDF_SEL 0x00000003 + +/*****************************************************************************/ +#define DFT1_CTL1 0x82c +#define FLD_DFT1_DWELL 0xffff0000 +#define FLD_DFT1_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT1_CTL2 0x830 +#define FLD_DFT1_THRESHOLD 0xffffff00 +#define FLD_DFT1_CMP_CTL 0x00000080 +#define FLD_DFT1_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT1_START 0x00000001 + +/*****************************************************************************/ +#define DFT1_STATUS 0x834 +#define FLD_DFT1_DONE 0x80000000 +#define FLD_DFT1_TH_CMP_STAT 0x40000000 +#define FLD_DFT1_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT2_CTL1 0x838 +#define FLD_DFT2_DWELL 0xffff0000 +#define FLD_DFT2_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT2_CTL2 0x83C +#define FLD_DFT2_THRESHOLD 0xffffff00 +#define FLD_DFT2_CMP_CTL 0x00000080 +#define FLD_DFT2_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT2_START 0x00000001 + +/*****************************************************************************/ +#define DFT2_STATUS 0x840 +#define FLD_DFT2_DONE 0x80000000 +#define FLD_DFT2_TH_CMP_STAT 0x40000000 +#define FLD_DFT2_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT3_CTL1 0x844 +#define FLD_DFT3_DWELL 0xffff0000 +#define FLD_DFT3_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT3_CTL2 0x848 +#define FLD_DFT3_THRESHOLD 0xffffff00 +#define FLD_DFT3_CMP_CTL 0x00000080 +#define FLD_DFT3_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT3_START 0x00000001 + +/*****************************************************************************/ +#define DFT3_STATUS 0x84c +#define FLD_DFT3_DONE 0x80000000 +#define FLD_DFT3_TH_CMP_STAT 0x40000000 +#define FLD_DFT3_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT4_CTL1 0x850 +#define FLD_DFT4_DWELL 0xffff0000 +#define FLD_DFT4_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT4_CTL2 0x854 +#define FLD_DFT4_THRESHOLD 0xffffff00 +#define FLD_DFT4_CMP_CTL 0x00000080 +#define FLD_DFT4_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT4_START 0x00000001 + +/*****************************************************************************/ +#define DFT4_STATUS 0x858 +#define FLD_DFT4_DONE 0x80000000 +#define FLD_DFT4_TH_CMP_STAT 0x40000000 +#define FLD_DFT4_RESULT 0x3fffffff + +/*****************************************************************************/ +#define AM_MTS_DET 0x85c +#define FLD_AM_MTS_MODE 0x80000000 +/* Reserved [30:26] */ +#define FLD_AM_SUB 0x02000000 +#define FLD_AM_GAIN_EN 0x01000000 +/* Reserved [23:16] */ +#define FLD_AMMTS_GAIN_SCALE 0x0000e000 +#define FLD_MTS_PDF_SHIFT 0x00001800 +#define FLD_AM_REG_GAIN 0x00000700 +#define FLD_AGC_REF 0x000000ff + +/*****************************************************************************/ +#define ANALOG_MUX_CTL 0x860 +/* Reserved [31:29] */ +#define FLD_MUX21_SEL 0x10000000 +#define FLD_MUX20_SEL 0x08000000 +#define FLD_MUX19_SEL 0x04000000 +#define FLD_MUX18_SEL 0x02000000 +#define FLD_MUX17_SEL 0x01000000 +#define FLD_MUX16_SEL 0x00800000 +#define FLD_MUX15_SEL 0x00400000 +#define FLD_MUX14_SEL 0x00300000 +#define FLD_MUX13_SEL 0x000C0000 +#define FLD_MUX12_SEL 0x00020000 +#define FLD_MUX11_SEL 0x00018000 +#define FLD_MUX10_SEL 0x00004000 +#define FLD_MUX9_SEL 0x00002000 +#define FLD_MUX8_SEL 0x00001000 +#define FLD_MUX7_SEL 0x00000800 +#define FLD_MUX6_SEL 0x00000600 +#define FLD_MUX5_SEL 0x00000100 +#define FLD_MUX4_SEL 0x000000c0 +#define FLD_MUX3_SEL 0x00000030 +#define FLD_MUX2_SEL 0x0000000c +#define FLD_MUX1_SEL 0x00000003 + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL1 0x864 +#define DIG_PLL_CTL1 0x864 + +#define FLD_PLL_STATUS 0x07000000 +#define FLD_BANDWIDTH_SELECT 0x00030000 +#define FLD_PLL_SHIFT_REG 0x00007000 +#define FLD_PHASE_SHIFT 0x000007ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL2 0x868 +#define DIG_PLL_CTL2 0x868 +#define FLD_PLL_UNLOCK_THR 0xff000000 +#define FLD_PLL_LOCK_THR 0x00ff0000 +/* Reserved [15:8] */ +#define FLD_AM_PDF_SEL2 0x000000c0 +#define FLD_AM_PDF_SEL1 0x00000030 +#define FLD_DPLL_FSM_CTRL 0x0000000c +/* Reserved [1] */ +#define FLD_PLL_PILOT_DET 0x00000001 + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL3 0x86c +#define DIG_PLL_CTL3 0x86c +#define FLD_DISABLE_LOOP 0x01000000 +#define FLD_A1_DS1_SEL 0x000c0000 +#define FLD_A1_DS2_SEL 0x00030000 +#define FLD_A1_KI 0x0000ff00 +#define FLD_A1_KD 0x000000ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL4 0x870 +#define DIG_PLL_CTL4 0x870 +#define FLD_A2_DS1_SEL 0x000c0000 +#define FLD_A2_DS2_SEL 0x00030000 +#define FLD_A2_KI 0x0000ff00 +#define FLD_A2_KD 0x000000ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL5 0x874 +#define DIG_PLL_CTL5 0x874 +#define FLD_TRK_DS1_SEL 0x000c0000 +#define FLD_TRK_DS2_SEL 0x00030000 +#define FLD_TRK_KI 0x0000ff00 +#define FLD_TRK_KD 0x000000ff + +/*****************************************************************************/ +#define DEEMPH_GAIN_CTL 0x878 +#define FLD_DEEMPH2_GAIN 0xFFFF0000 +#define FLD_DEEMPH1_GAIN 0x0000FFFF + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_COEFF1 0x87c +#define DEEMPH_COEF1 0x87c +#define FLD_DEEMPH_B0 0xffff0000 +#define FLD_DEEMPH_A0 0x0000ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_COEFF2 0x880 +#define DEEMPH_COEF2 0x880 +#define FLD_DEEMPH_B1 0xFFFF0000 +#define FLD_DEEMPH_A1 0x0000FFFF + +/*****************************************************************************/ +#define DBX1_CTL1 0x884 +#define FLD_DBX1_WBE_GAIN 0xffff0000 +#define FLD_DBX1_IN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX1_CTL2 0x888 +#define FLD_DBX1_SE_BYPASS 0xffff0000 +#define FLD_DBX1_SE_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX1_RMS_SE 0x88C +#define FLD_DBX1_RMS_WBE 0xffff0000 +#define FLD_DBX1_RMS_SE_FLD 0x0000ffff + +/*****************************************************************************/ +#define DBX2_CTL1 0x890 +#define FLD_DBX2_WBE_GAIN 0xffff0000 +#define FLD_DBX2_IN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX2_CTL2 0x894 +#define FLD_DBX2_SE_BYPASS 0xffff0000 +#define FLD_DBX2_SE_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX2_RMS_SE 0x898 +#define FLD_DBX2_RMS_WBE 0xffff0000 +#define FLD_DBX2_RMS_SE_FLD 0x0000ffff + +/*****************************************************************************/ +#define AM_FM_DIFF 0x89c +/* Reserved [31] */ +#define FLD_FM_DIFF_OUT 0x7fff0000 +/* Reserved [15] */ +#define FLD_AM_DIFF_OUT 0x00007fff + +/*****************************************************************************/ +#define NICAM_FAW 0x8a0 +#define FLD_FAWDETWINEND 0xFc000000 +#define FLD_FAWDETWINSTR 0x03ff0000 +/* Reserved [15:12] */ +#define FLD_FAWDETTHRSHLD3 0x00000f00 +#define FLD_FAWDETTHRSHLD2 0x000000f0 +#define FLD_FAWDETTHRSHLD1 0x0000000f + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_GAIN 0x8a4 +#define NICAM_DEEMPHGAIN 0x8a4 +/* Reserved [31:18] */ +#define FLD_DEEMPHGAIN 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_NUMER1 0x8a8 +#define NICAM_DEEMPHNUMER1 0x8a8 +/* Reserved [31:18] */ +#define FLD_DEEMPHNUMER1 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_NUMER2 0x8ac +#define NICAM_DEEMPHNUMER2 0x8ac +/* Reserved [31:18] */ +#define FLD_DEEMPHNUMER2 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_DENOM1 0x8b0 +#define NICAM_DEEMPHDENOM1 0x8b0 +/* Reserved [31:18] */ +#define FLD_DEEMPHDENOM1 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_DENOM2 0x8b4 +#define NICAM_DEEMPHDENOM2 0x8b4 +/* Reserved [31:18] */ +#define FLD_DEEMPHDENOM2 0x0003ffff + +/*****************************************************************************/ +#define NICAM_ERRLOG_CTL1 0x8B8 +/* Reserved [31:28] */ +#define FLD_ERRINTRPTTHSHLD1 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRLOGPERIOD 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_CTL2 0x8bc +/* Reserved [31:28] */ +#define FLD_ERRINTRPTTHSHLD3 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRINTRPTTHSHLD2 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_STS1 0x8c0 +/* Reserved [31:28] */ +#define FLD_ERRLOG2 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRLOG1 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_STS2 0x8c4 +/* Reserved [31:12] */ +#define FLD_ERRLOG3 0x00000fff + +/*****************************************************************************/ +#define NICAM_STATUS 0x8c8 +/* Reserved [31:20] */ +#define FLD_NICAM_CIB 0x000c0000 +#define FLD_NICAM_LOCK_STAT 0x00020000 +#define FLD_NICAM_MUTE 0x00010000 +#define FLD_NICAMADDIT_DATA 0x0000ffe0 +#define FLD_NICAMCNTRL 0x0000001f + +/*****************************************************************************/ +#define DEMATRIX_CTL 0x8cc +#define FLD_AC97_IN_SHIFT 0xf0000000 +#define FLD_I2S_IN_SHIFT 0x0f000000 +#define FLD_DEMATRIX_SEL_CTL 0x00ff0000 +/* Reserved [15:11] */ +#define FLD_DMTRX_BYPASS 0x00000400 +#define FLD_DEMATRIX_MODE 0x00000300 +/* Reserved [7:6] */ +#define FLD_PH_DBX_SEL 0x00000020 +#define FLD_PH_CH_SEL 0x00000010 +#define FLD_PHASE_FIX 0x0000000f + +/*****************************************************************************/ +#define PATH1_CTL1 0x8d0 +/* Reserved [31:29] */ +#define FLD_PATH1_MUTE_CTL 0x1f000000 +/* Reserved [23:22] */ +#define FLD_PATH1_AVC_CG 0x00300000 +#define FLD_PATH1_AVC_RT 0x000f0000 +#define FLD_PATH1_AVC_AT 0x0000f000 +#define FLD_PATH1_AVC_STEREO 0x00000800 +#define FLD_PATH1_AVC_CR 0x00000700 +#define FLD_PATH1_AVC_RMS_CON 0x000000f0 +#define FLD_PATH1_SEL_CTL 0x0000000f + +/*****************************************************************************/ +#define PATH1_VOL_CTL 0x8d4 +#define FLD_PATH1_AVC_THRESHOLD 0x7fff0000 +#define FLD_PATH1_BAL_LEFT 0x00008000 +#define FLD_PATH1_BAL_LEVEL 0x00007f00 +#define FLD_PATH1_VOLUME 0x000000ff + +/*****************************************************************************/ +#define PATH1_EQ_CTL 0x8d8 +/* Reserved [31:30] */ +#define FLD_PATH1_EQ_TREBLE_VOL 0x3f000000 +/* Reserved [23:22] */ +#define FLD_PATH1_EQ_MID_VOL 0x003f0000 +/* Reserved [15:14] */ +#define FLD_PATH1_EQ_BASS_VOL 0x00003f00 +/* Reserved [7:1] */ +#define FLD_PATH1_EQ_BAND_SEL 0x00000001 + +/*****************************************************************************/ +#define PATH1_SC_CTL 0x8dc +#define FLD_PATH1_SC_THRESHOLD 0x7fff0000 +#define FLD_PATH1_SC_RT 0x0000f000 +#define FLD_PATH1_SC_AT 0x00000f00 +#define FLD_PATH1_SC_STEREO 0x00000080 +#define FLD_PATH1_SC_CR 0x00000070 +#define FLD_PATH1_SC_RMS_CON 0x0000000f + +/*****************************************************************************/ +#define PATH2_CTL1 0x8e0 +/* Reserved [31:26] */ +#define FLD_PATH2_MUTE_CTL 0x03000000 +/* Reserved [23:22] */ +#define FLD_PATH2_AVC_CG 0x00300000 +#define FLD_PATH2_AVC_RT 0x000f0000 +#define FLD_PATH2_AVC_AT 0x0000f000 +#define FLD_PATH2_AVC_STEREO 0x00000800 +#define FLD_PATH2_AVC_CR 0x00000700 +#define FLD_PATH2_AVC_RMS_CON 0x000000f0 +#define FLD_PATH2_SEL_CTL 0x0000000f + +/*****************************************************************************/ +#define PATH2_VOL_CTL 0x8e4 +#define FLD_PATH2_AVC_THRESHOLD 0xffff0000 +#define FLD_PATH2_BAL_LEFT 0x00008000 +#define FLD_PATH2_BAL_LEVEL 0x00007f00 +#define FLD_PATH2_VOLUME 0x000000ff + +/*****************************************************************************/ +#define PATH2_EQ_CTL 0x8e8 +/* Reserved [31:30] */ +#define FLD_PATH2_EQ_TREBLE_VOL 0x3f000000 +/* Reserved [23:22] */ +#define FLD_PATH2_EQ_MID_VOL 0x003f0000 +/* Reserved [15:14] */ +#define FLD_PATH2_EQ_BASS_VOL 0x00003f00 +/* Reserved [7:1] */ +#define FLD_PATH2_EQ_BAND_SEL 0x00000001 + +/*****************************************************************************/ +#define PATH2_SC_CTL 0x8eC +#define FLD_PATH2_SC_THRESHOLD 0xffff0000 +#define FLD_PATH2_SC_RT 0x0000f000 +#define FLD_PATH2_SC_AT 0x00000f00 +#define FLD_PATH2_SC_STEREO 0x00000080 +#define FLD_PATH2_SC_CR 0x00000070 +#define FLD_PATH2_SC_RMS_CON 0x0000000f + +/*****************************************************************************/ +#define SRC_CTL 0x8f0 +#define FLD_SRC_STATUS 0xffffff00 +#define FLD_FIFO_LF_EN 0x000000fc +#define FLD_BYPASS_LI 0x00000002 +#define FLD_BYPASS_PF 0x00000001 + +/*****************************************************************************/ +#define SRC_LF_COEF 0x8f4 +#define FLD_LOOP_FILTER_COEF2 0xffff0000 +#define FLD_LOOP_FILTER_COEF1 0x0000ffff + +/*****************************************************************************/ +#define SRC1_CTL 0x8f8 +/* Reserved [31:28] */ +#define FLD_SRC1_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC1_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC2_CTL 0x8fc +/* Reserved [31:28] */ +#define FLD_SRC2_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC2_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC3_CTL 0x900 +/* Reserved [31:28] */ +#define FLD_SRC3_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC3_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC4_CTL 0x904 +/* Reserved [31:28] */ +#define FLD_SRC4_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC4_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC5_CTL 0x908 +/* Reserved [31:28] */ +#define FLD_SRC5_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC5_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC6_CTL 0x90c +/* Reserved [31:28] */ +#define FLD_SRC6_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC6_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define BAND_OUT_SEL 0x910 +#define FLD_SRC6_IN_SEL 0xc0000000 +#define FLD_SRC6_CLK_SEL 0x30000000 +#define FLD_SRC5_IN_SEL 0x0c000000 +#define FLD_SRC5_CLK_SEL 0x03000000 +#define FLD_SRC4_IN_SEL 0x00c00000 +#define FLD_SRC4_CLK_SEL 0x00300000 +#define FLD_SRC3_IN_SEL 0x000c0000 +#define FLD_SRC3_CLK_SEL 0x00030000 +#define FLD_BASEBAND_BYPASS_CTL 0x0000ff00 +#define FLD_AC97_SRC_SEL 0x000000c0 +#define FLD_I2S_SRC_SEL 0x00000030 +#define FLD_PARALLEL2_SRC_SEL 0x0000000c +#define FLD_PARALLEL1_SRC_SEL 0x00000003 + +/*****************************************************************************/ +#define I2S_IN_CTL 0x914 +/* Reserved [31:11] */ +#define FLD_I2S_UP2X_BW20K 0x00000400 +#define FLD_I2S_UP2X_BYPASS 0x00000200 +#define FLD_I2S_IN_MASTER_MODE 0x00000100 +#define FLD_I2S_IN_SONY_MODE 0x00000080 +#define FLD_I2S_IN_RIGHT_JUST 0x00000040 +#define FLD_I2S_IN_WS_SEL 0x00000020 +#define FLD_I2S_IN_BCN_DEL 0x0000001f + +/*****************************************************************************/ +#define I2S_OUT_CTL 0x918 +/* Reserved [31:17] */ +#define FLD_I2S_OUT_SOFT_RESET_EN 0x00010000 +/* Reserved [15:9] */ +#define FLD_I2S_OUT_MASTER_MODE 0x00000100 +#define FLD_I2S_OUT_SONY_MODE 0x00000080 +#define FLD_I2S_OUT_RIGHT_JUST 0x00000040 +#define FLD_I2S_OUT_WS_SEL 0x00000020 +#define FLD_I2S_OUT_BCN_DEL 0x0000001f + +/*****************************************************************************/ +#define AC97_CTL 0x91c +/* Reserved [31:26] */ +#define FLD_AC97_UP2X_BW20K 0x02000000 +#define FLD_AC97_UP2X_BYPASS 0x01000000 +/* Reserved [23:17] */ +#define FLD_AC97_RST_ACL 0x00010000 +/* Reserved [15:9] */ +#define FLD_AC97_WAKE_UP_SYNC 0x00000100 +/* Reserved [7:1] */ +#define FLD_AC97_SHUTDOWN 0x00000001 + +/* Cx231xx redefine */ +#define QPSK_IAGC_CTL1 0x94c +#define QPSK_IAGC_CTL2 0x950 +#define QPSK_FEPR_FREQ 0x954 +#define QPSK_BTL_CTL1 0x958 +#define QPSK_BTL_CTL2 0x95c +#define QPSK_CTL_CTL1 0x960 +#define QPSK_CTL_CTL2 0x964 +#define QPSK_MF_FAGC_CTL 0x968 +#define QPSK_EQ_CTL 0x96c +#define QPSK_LOCK_CTL 0x970 + +/*****************************************************************************/ +#define FM1_DFT_CTL 0x9a8 +#define FLD_FM1_DFT_THRESHOLD 0xffff0000 +/* Reserved [15:8] */ +#define FLD_FM1_DFT_CMP_CTL 0x00000080 +#define FLD_FM1_DFT_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_FM1_DFT_START 0x00000001 + +/*****************************************************************************/ +#define FM1_DFT_STATUS 0x9ac +#define FLD_FM1_DFT_DONE 0x80000000 +/* Reserved [30:19] */ +#define FLD_FM_DFT_TH_CMP 0x00040000 +#define FLD_FM1_DFT 0x0003ffff + +/*****************************************************************************/ +#define FM2_DFT_CTL 0x9b0 +#define FLD_FM2_DFT_THRESHOLD 0xffff0000 +/* Reserved [15:8] */ +#define FLD_FM2_DFT_CMP_CTL 0x00000080 +#define FLD_FM2_DFT_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_FM2_DFT_START 0x00000001 + +/*****************************************************************************/ +#define FM2_DFT_STATUS 0x9b4 +#define FLD_FM2_DFT_DONE 0x80000000 +/* Reserved [30:19] */ +#define FLD_FM2_DFT_TH_CMP_STAT 0x00040000 +#define FLD_FM2_DFT 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define AAGC_STATUS_REG 0x9b8 +#define AAGC_STATUS 0x9b8 +/* Reserved [31:27] */ +#define FLD_FM2_DAGC_OUT 0x07000000 +/* Reserved [23:19] */ +#define FLD_FM1_DAGC_OUT 0x00070000 +/* Reserved [15:6] */ +#define FLD_AFE_VGA_OUT 0x0000003f + +/*****************************************************************************/ +#define MTS_GAIN_STATUS 0x9bc +/* Reserved [31:14] */ +#define FLD_MTS_GAIN 0x00003fff + +#define RDS_OUT 0x9c0 +#define FLD_RDS_Q 0xffff0000 +#define FLD_RDS_I 0x0000ffff + +/*****************************************************************************/ +#define AUTOCONFIG_REG 0x9c4 +/* Reserved [31:4] */ +#define FLD_AUTOCONFIG_MODE 0x0000000f + +#define FM_AFC 0x9c8 +#define FLD_FM2_AFC 0xffff0000 +#define FLD_FM1_AFC 0x0000ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define NEW_SPARE 0x9cc +#define NEW_SPARE_REG 0x9cc + +/*****************************************************************************/ +#define DBX_ADJ 0x9d0 +/* Reserved [31:28] */ +#define FLD_DBX2_ADJ 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_DBX1_ADJ 0x00000fff + +#define VID_FMT_AUTO 0 +#define VID_FMT_NTSC_M 1 +#define VID_FMT_NTSC_J 2 +#define VID_FMT_NTSC_443 3 +#define VID_FMT_PAL_BDGHI 4 +#define VID_FMT_PAL_M 5 +#define VID_FMT_PAL_N 6 +#define VID_FMT_PAL_NC 7 +#define VID_FMT_PAL_60 8 +#define VID_FMT_SECAM 12 +#define VID_FMT_SECAM_60 13 + +#define INPUT_MODE_CVBS_0 0 /* INPUT_MODE_VALUE(0) */ +#define INPUT_MODE_YC_1 1 /* INPUT_MODE_VALUE(1) */ +#define INPUT_MODE_YC2_2 2 /* INPUT_MODE_VALUE(2) */ +#define INPUT_MODE_YUV_3 3 /* INPUT_MODE_VALUE(3) */ + +#define LUMA_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ +#define LUMA_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ +#define LUMA_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ + +#define UV_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ +#define UV_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ +#define UV_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ + +#define TWO_TAP_FILT 0 +#define THREE_TAP_FILT 1 +#define FOUR_TAP_FILT 2 +#define FIVE_TAP_FILT 3 + +#define AUD_CHAN_SRC_PARALLEL 0 +#define AUD_CHAN_SRC_I2S_INPUT 1 +#define AUD_CHAN_SRC_FLATIRON 2 +#define AUD_CHAN_SRC_PARALLEL3 3 + +#define OUT_MODE_601 0 +#define OUT_MODE_656 1 +#define OUT_MODE_VIP11 2 +#define OUT_MODE_VIP20 3 + +#define PHASE_INC_49MHZ 0x0df22 +#define PHASE_INC_56MHZ 0x0fa5b +#define PHASE_INC_28MHZ 0x010000 + +#endif diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c new file mode 100644 index 000000000000..ac7db52f404f --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -0,0 +1,710 @@ +/* + cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 + Based on cx88 driver + + This program is free software; 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 +#include + +#include +#include +#include +#include +#include + +#include "cx231xx.h" +#include "cx231xx-vbi.h" + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status, + errmsg); + } else { + cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + int rc = 1; + unsigned char *p_buffer; + u32 bytes_parsed = 0, buffer_size = 0; + u8 sav_eav = 0; + + if (!dev) + return 0; + + if (dev->state & DEV_DISCONNECTED) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + /* get buffer pointer and length */ + p_buffer = urb->transfer_buffer; + buffer_size = urb->actual_length; + + if (buffer_size > 0) { + bytes_parsed = 0; + + if (dma_q->is_partial_line) { + /* Handle the case where we were working on a partial + line */ + sav_eav = dma_q->last_sav; + } else { + /* Check for a SAV/EAV overlapping the + buffer boundary */ + + sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer, + dma_q->partial_buf, + &bytes_parsed); + } + + sav_eav &= 0xF0; + /* Get the first line if we have some portion of an SAV/EAV from + the last buffer or a partial line */ + if (sav_eav) { + bytes_parsed += cx231xx_get_vbi_line(dev, dma_q, + sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed); /* buffer size */ + } + + /* Now parse data that is completely in this buffer */ + dma_q->is_partial_line = 0; + + while (bytes_parsed < buffer_size) { + u32 bytes_used = 0; + + sav_eav = cx231xx_find_next_SAV_EAV( + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed, /* buffer size */ + &bytes_used); /* bytes used to get SAV/EAV */ + + bytes_parsed += bytes_used; + + sav_eav &= 0xF0; + if (sav_eav && (bytes_parsed < buffer_size)) { + bytes_parsed += cx231xx_get_vbi_line(dev, + dma_q, sav_eav, /* SAV/EAV */ + p_buffer+bytes_parsed, /* p_buffer */ + buffer_size-bytes_parsed);/*buf size*/ + } + } + + /* Save the last four bytes of the buffer so we can + check the buffer boundary condition next time */ + memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); + bytes_parsed = 0; + } + + return rc; +} + +/* ------------------------------------------------------------------ + Vbi buf operations + ------------------------------------------------------------------*/ + +static int +vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + + *size = (dev->width * height * 2 * 2); + if (0 == *count) + *count = CX231XX_DEF_VBI_BUF; + + if (*count < CX231XX_MIN_BUF) + *count = CX231XX_MIN_BUF; + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->vbi_mode.slock, flags); + if (dev->vbi_mode.bulk_ctl.buf == buf) + dev->vbi_mode.bulk_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + int rc = 0, urb_init = 0; + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + buf->vb.size = ((dev->width << 1) * height * 2); + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = height; + buf->vb.field = field; + buf->vb.field = V4L2_FIELD_SEQ_TB; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (!dev->vbi_mode.bulk_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS, + CX231XX_NUM_VBI_BUFS, + dev->vbi_mode.alt_max_pkt_size[0], + cx231xx_isoc_vbi_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void vbi_buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + + + free_buffer(vq, buf); +} + +struct videobuf_queue_ops cx231xx_vbi_qops = { + .buf_setup = vbi_buffer_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .buf_release = vbi_buffer_release, +}; + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +static void cx231xx_irq_vbi_callback(struct urb *urb) +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + cx231xx_err(DRIVER_NAME "urb completition error %d.\n", + urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->vbi_mode.slock); + dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); + spin_unlock(&dev->vbi_mode.slock); + + /* Reset status */ + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void cx231xx_uninit_vbi_isoc(struct cx231xx *dev) +{ + struct urb *urb; + int i; + + cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n"); + + dev->vbi_mode.bulk_ctl.nfields = -1; + for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { + urb = dev->vbi_mode.bulk_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) { + + kfree(dev->vbi_mode.bulk_ctl. + transfer_buffer[i]); + dev->vbi_mode.bulk_ctl.transfer_buffer[i] = + NULL; + } + usb_free_urb(urb); + dev->vbi_mode.bulk_ctl.urb[i] = NULL; + } + dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->vbi_mode.bulk_ctl.urb); + kfree(dev->vbi_mode.bulk_ctl.transfer_buffer); + + dev->vbi_mode.bulk_ctl.urb = NULL; + dev->vbi_mode.bulk_ctl.transfer_buffer = NULL; + dev->vbi_mode.bulk_ctl.num_bufs = 0; + + cx231xx_capture_start(dev, 0, Vbi); +} +EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc); + +/* + * Allocate URBs and start IRQ + */ +int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*bulk_copy) (struct cx231xx *dev, + struct urb *urb)) +{ + struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int rc; + + cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + cx231xx_uninit_vbi_isoc(dev); + + /* clear if any halt */ + usb_clear_halt(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->vbi_mode.end_point_addr)); + + dev->vbi_mode.bulk_ctl.bulk_copy = bulk_copy; + dev->vbi_mode.bulk_ctl.num_bufs = num_bufs; + dma_q->pos = 0; + dma_q->is_partial_line = 0; + dma_q->last_sav = 0; + dma_q->current_field = -1; + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + dma_q->lines_completed = 0; + for (i = 0; i < 8; i++) + dma_q->partial_buf[i] = 0; + + dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs, + GFP_KERNEL); + if (!dev->vbi_mode.bulk_ctl.urb) { + cx231xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->vbi_mode.bulk_ctl.transfer_buffer = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->vbi_mode.bulk_ctl.transfer_buffer) { + cx231xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->vbi_mode.bulk_ctl.urb); + return -ENOMEM; + } + + dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size; + dev->vbi_mode.bulk_ctl.buf = NULL; + + sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + cx231xx_err(DRIVER_NAME + ": cannot alloc bulk_ctl.urb %i\n", i); + cx231xx_uninit_vbi_isoc(dev); + return -ENOMEM; + } + dev->vbi_mode.bulk_ctl.urb[i] = urb; + urb->transfer_flags = 0; + + dev->vbi_mode.bulk_ctl.transfer_buffer[i] = + kzalloc(sb_size, GFP_KERNEL); + if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) { + cx231xx_err(DRIVER_NAME + ": unable to allocate %i bytes for transfer" + " buffer %i%s\n", sb_size, i, + in_interrupt() ? " while in int" : ""); + cx231xx_uninit_vbi_isoc(dev); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr); + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->vbi_mode.bulk_ctl.transfer_buffer[i], + sb_size, cx231xx_irq_vbi_callback, dma_q); + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC); + if (rc) { + cx231xx_err(DRIVER_NAME + ": submit of urb %i failed (error=%i)\n", i, + rc); + cx231xx_uninit_vbi_isoc(dev); + return rc; + } + } + + cx231xx_capture_start(dev, 1, Vbi); + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc); + +u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size) +{ + u32 bytes_copied = 0; + int current_field = -1; + + switch (sav_eav) { + + case SAV_VBI_FIELD1: + current_field = 1; + break; + + case SAV_VBI_FIELD2: + current_field = 2; + break; + default: + break; + } + + if (current_field < 0) + return bytes_copied; + + dma_q->last_sav = sav_eav; + + bytes_copied = + cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size, + current_field); + + return bytes_copied; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void vbi_buffer_filled(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer *buf) +{ + /* Advice that buffer was filled */ + /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */ + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->vbi_mode.bulk_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number) +{ + u32 bytes_to_copy; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width * 2; + + if (dma_q->current_field == -1) { + /* Just starting up */ + cx231xx_reset_vbi_buffer(dev, dma_q); + } + + if (dma_q->current_field != field_number) + dma_q->lines_completed = 0; + + /* get the buffer pointer */ + buf = dev->vbi_mode.bulk_ctl.buf; + + /* Remember the field number for next time */ + dma_q->current_field = field_number; + + bytes_to_copy = dma_q->bytes_left_in_line; + if (bytes_to_copy > length) + bytes_to_copy = length; + + if (dma_q->lines_completed >= dma_q->lines_per_field) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = + (dma_q->bytes_left_in_line == 0) ? 0 : 1; + return 0; + } + + dma_q->is_partial_line = 1; + + /* If we don't have a buffer, just return the number of bytes we would + have copied if we had a buffer. */ + if (!buf) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = + (dma_q->bytes_left_in_line == 0) ? 0 : 1; + return bytes_to_copy; + } + + /* copy the data to video buffer */ + cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy); + + dma_q->pos += bytes_to_copy; + dma_q->bytes_left_in_line -= bytes_to_copy; + + if (dma_q->bytes_left_in_line == 0) { + + dma_q->bytes_left_in_line = _line_size; + dma_q->lines_completed++; + dma_q->is_partial_line = 0; + + if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) { + + vbi_buffer_filled(dev, dma_q, buf); + + dma_q->pos = 0; + dma_q->lines_completed = 0; + cx231xx_reset_vbi_buffer(dev, dma_q); + } + } + + return bytes_to_copy; +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer **buf) +{ + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + char *outp; + + if (list_empty(&dma_q->active)) { + cx231xx_err(DRIVER_NAME ": No active queue to serve\n"); + dev->vbi_mode.bulk_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + + /* Cleans up buffer - Useful for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + dev->vbi_mode.bulk_ctl.buf = *buf; + + return; +} + +void cx231xx_reset_vbi_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + struct cx231xx_buffer *buf; + + buf = dev->vbi_mode.bulk_ctl.buf; + + if (buf == NULL) { + /* first try to get the buffer */ + get_next_vbi_buf(dma_q, &buf); + + dma_q->pos = 0; + dma_q->current_field = -1; + } + + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; +} + +int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy) +{ + u8 *p_out_buffer = NULL; + u32 current_line_bytes_copied = 0; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width << 1; + void *startwrite; + int offset, lencopy; + + buf = dev->vbi_mode.bulk_ctl.buf; + + if (buf == NULL) + return -EINVAL; + + p_out_buffer = videobuf_to_vmalloc(&buf->vb); + + if (dma_q->bytes_left_in_line != _line_size) { + current_line_bytes_copied = + _line_size - dma_q->bytes_left_in_line; + } + + offset = (dma_q->lines_completed * _line_size) + + current_line_bytes_copied; + + if (dma_q->current_field == 2) { + /* Populate the second half of the frame */ + offset += (dev->width * 2 * dma_q->lines_per_field); + } + + /* prepare destination address */ + startwrite = p_out_buffer + offset; + + lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? + bytes_to_copy : dma_q->bytes_left_in_line; + + memcpy(startwrite, p_buffer, lencopy); + + return 0; +} + +u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + if (dma_q->lines_completed == height && dma_q->current_field == 2) + return 1; + else + return 0; +} diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.h b/drivers/media/usb/cx231xx/cx231xx-vbi.h new file mode 100644 index 000000000000..16c7d20a22a4 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.h @@ -0,0 +1,65 @@ +/* + cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 + Based on cx88 driver + + This program is free software; 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 _CX231XX_VBI_H +#define _CX231XX_VBI_H + +extern struct videobuf_queue_ops cx231xx_vbi_qops; + +#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ +#define NTSC_VBI_END_LINE 21 +#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1) + +#define PAL_VBI_START_LINE 6 +#define PAL_VBI_END_LINE 23 +#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1) + +#define VBI_STRIDE 1440 +#define VBI_SAMPLES_PER_LINE 1440 + +#define CX231XX_NUM_VBI_PACKETS 4 +#define CX231XX_NUM_VBI_BUFS 5 + +/* stream functions */ +int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*bulk_copy) (struct cx231xx *dev, + struct urb *urb)); + +void cx231xx_uninit_vbi_isoc(struct cx231xx *dev); + +/* vbi data copy functions */ +u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size); + +u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number); + +void cx231xx_reset_vbi_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); + +int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy); + +u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); + +#endif diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c new file mode 100644 index 000000000000..790b28d7f764 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -0,0 +1,2670 @@ +/* + cx231xx-video.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 + Based on em28xx driver + Based on cx23885 driver + Based on cx88 driver + + This program is free software; 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 +#include + +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "cx231xx.h" +#include "cx231xx-vbi.h" + +#define CX231XX_VERSION "0.0.2" + +#define DRIVER_AUTHOR "Srinivasa Deevi " +#define DRIVER_DESC "Conexant cx231xx based USB video device driver" + +#define cx231xx_videodbg(fmt, arg...) do {\ + if (video_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define cx231xx_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); \ + } \ + } while (0) + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CX231XX_VERSION); + +static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); +module_param_array(video_nr, int, NULL, 0444); +module_param_array(vbi_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); + +MODULE_PARM_DESC(card, "card type"); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); + +static unsigned int video_debug; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); + +/* supported video standards */ +static struct cx231xx_fmt format[] = { + { + .name = "16bpp YUY2, 4:2:2, packed", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .reg = 0, + }, +}; + +/* supported controls */ +/* Common to all boards */ + +/* ------------------------------------------------------------------- */ + +static const struct v4l2_queryctrl no_ctl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +static struct cx231xx_ctrl cx231xx_ctls[] = { + /* --- video --- */ + { + .v = { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 128, + .reg = LUMA_CTRL, + .mask = 0x00ff, + .shift = 0, + }, { + .v = { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x3f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 0, + .reg = LUMA_CTRL, + .mask = 0xff00, + .shift = 8, + }, { + .v = { + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 128, + .reg = CHROMA_CTRL, + .mask = 0xff0000, + .shift = 16, + }, { + /* strictly, this only describes only U saturation. + * V saturation is handled specially through code. + */ + .v = { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 0, + .reg = CHROMA_CTRL, + .mask = 0x00ff, + .shift = 0, + }, { + /* --- audio --- */ + .v = { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, + .reg = PATH1_CTL1, + .mask = (0x1f << 24), + .shift = 24, + }, { + .v = { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 0x3f, + .step = 1, + .default_value = 0x3f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .reg = PATH1_VOL_CTL, + .mask = 0xff, + .shift = 0, + } +}; +static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls); + +static const u32 cx231xx_user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, +#if 0 + V4L2_CID_AUDIO_BALANCE, +#endif + V4L2_CID_AUDIO_MUTE, + 0 +}; + +static const u32 *ctrl_classes[] = { + cx231xx_user_ctrls, + NULL +}; + +/* ------------------------------------------------------------------ + Video buffer and parser functions + ------------------------------------------------------------------*/ + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer *buf) +{ + /* Advice that buffer was filled */ + cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + if (dev->USE_ISO) + dev->video_mode.isoc_ctl.buf = NULL; + else + dev->video_mode.bulk_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + cx231xx_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer **buf) +{ + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + + char *outp; + + if (list_empty(&dma_q->active)) { + cx231xx_isocdbg("No active queue to serve\n"); + if (dev->USE_ISO) + dev->video_mode.isoc_ctl.buf = NULL; + else + dev->video_mode.bulk_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + + /* Cleans up buffer - Useful for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + if (dev->USE_ISO) + dev->video_mode.isoc_ctl.buf = *buf; + else + dev->video_mode.bulk_ctl.buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + int i, rc = 1; + unsigned char *p_buffer; + u32 bytes_parsed = 0, buffer_size = 0; + u8 sav_eav = 0; + + if (!dev) + return 0; + + if (dev->state & DEV_DISCONNECTED) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* cx231xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->video_mode.max_pkt_size) { + cx231xx_isocdbg("packet bigger than packet size"); + continue; + } + + /* get buffer pointer and length */ + p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + buffer_size = urb->iso_frame_desc[i].actual_length; + bytes_parsed = 0; + + if (dma_q->is_partial_line) { + /* Handle the case of a partial line */ + sav_eav = dma_q->last_sav; + } else { + /* Check for a SAV/EAV overlapping + the buffer boundary */ + sav_eav = + cx231xx_find_boundary_SAV_EAV(p_buffer, + dma_q->partial_buf, + &bytes_parsed); + } + + sav_eav &= 0xF0; + /* Get the first line if we have some portion of an SAV/EAV from + the last buffer or a partial line */ + if (sav_eav) { + bytes_parsed += cx231xx_get_video_line(dev, dma_q, + sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed);/* buf size */ + } + + /* Now parse data that is completely in this buffer */ + /* dma_q->is_partial_line = 0; */ + + while (bytes_parsed < buffer_size) { + u32 bytes_used = 0; + + sav_eav = cx231xx_find_next_SAV_EAV( + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed, /* buf size */ + &bytes_used);/* bytes used to get SAV/EAV */ + + bytes_parsed += bytes_used; + + sav_eav &= 0xF0; + if (sav_eav && (bytes_parsed < buffer_size)) { + bytes_parsed += cx231xx_get_video_line(dev, + dma_q, sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed,/* p_buffer */ + buffer_size - bytes_parsed);/*buf size*/ + } + } + + /* Save the last four bytes of the buffer so we can check the + buffer boundary condition next time */ + memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); + bytes_parsed = 0; + + } + return rc; +} + +static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + int rc = 1; + unsigned char *p_buffer; + u32 bytes_parsed = 0, buffer_size = 0; + u8 sav_eav = 0; + + if (!dev) + return 0; + + if (dev->state & DEV_DISCONNECTED) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + if (1) { + + /* get buffer pointer and length */ + p_buffer = urb->transfer_buffer; + buffer_size = urb->actual_length; + bytes_parsed = 0; + + if (dma_q->is_partial_line) { + /* Handle the case of a partial line */ + sav_eav = dma_q->last_sav; + } else { + /* Check for a SAV/EAV overlapping + the buffer boundary */ + sav_eav = + cx231xx_find_boundary_SAV_EAV(p_buffer, + dma_q->partial_buf, + &bytes_parsed); + } + + sav_eav &= 0xF0; + /* Get the first line if we have some portion of an SAV/EAV from + the last buffer or a partial line */ + if (sav_eav) { + bytes_parsed += cx231xx_get_video_line(dev, dma_q, + sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed);/* buf size */ + } + + /* Now parse data that is completely in this buffer */ + /* dma_q->is_partial_line = 0; */ + + while (bytes_parsed < buffer_size) { + u32 bytes_used = 0; + + sav_eav = cx231xx_find_next_SAV_EAV( + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed, /* buf size */ + &bytes_used);/* bytes used to get SAV/EAV */ + + bytes_parsed += bytes_used; + + sav_eav &= 0xF0; + if (sav_eav && (bytes_parsed < buffer_size)) { + bytes_parsed += cx231xx_get_video_line(dev, + dma_q, sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed,/* p_buffer */ + buffer_size - bytes_parsed);/*buf size*/ + } + } + + /* Save the last four bytes of the buffer so we can check the + buffer boundary condition next time */ + memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); + bytes_parsed = 0; + + } + return rc; +} + + +u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, + u32 *p_bytes_used) +{ + u32 bytes_used; + u8 boundary_bytes[8]; + u8 sav_eav = 0; + + *p_bytes_used = 0; + + /* Create an array of the last 4 bytes of the last buffer and the first + 4 bytes of the current buffer. */ + + memcpy(boundary_bytes, partial_buf, 4); + memcpy(boundary_bytes + 4, p_buffer, 4); + + /* Check for the SAV/EAV in the boundary buffer */ + sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8, + &bytes_used); + + if (sav_eav) { + /* found a boundary SAV/EAV. Updates the bytes used to reflect + only those used in the new buffer */ + *p_bytes_used = bytes_used - 4; + } + + return sav_eav; +} + +u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used) +{ + u32 i; + u8 sav_eav = 0; + + /* + * Don't search if the buffer size is less than 4. It causes a page + * fault since buffer_size - 4 evaluates to a large number in that + * case. + */ + if (buffer_size < 4) { + *p_bytes_used = buffer_size; + return 0; + } + + for (i = 0; i < (buffer_size - 3); i++) { + + if ((p_buffer[i] == 0xFF) && + (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) { + + *p_bytes_used = i + 4; + sav_eav = p_buffer[i + 3]; + return sav_eav; + } + } + + *p_bytes_used = buffer_size; + return 0; +} + +u32 cx231xx_get_video_line(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, u8 sav_eav, + u8 *p_buffer, u32 buffer_size) +{ + u32 bytes_copied = 0; + int current_field = -1; + + switch (sav_eav) { + case SAV_ACTIVE_VIDEO_FIELD1: + /* looking for skipped line which occurred in PAL 720x480 mode. + In this case, there will be no active data contained + between the SAV and EAV */ + if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && + (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && + ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || + (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || + (p_buffer[3] == EAV_VBLANK_FIELD1) || + (p_buffer[3] == EAV_VBLANK_FIELD2))) + return bytes_copied; + current_field = 1; + break; + + case SAV_ACTIVE_VIDEO_FIELD2: + /* looking for skipped line which occurred in PAL 720x480 mode. + In this case, there will be no active data contained between + the SAV and EAV */ + if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && + (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && + ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || + (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || + (p_buffer[3] == EAV_VBLANK_FIELD1) || + (p_buffer[3] == EAV_VBLANK_FIELD2))) + return bytes_copied; + current_field = 2; + break; + } + + dma_q->last_sav = sav_eav; + + bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer, + buffer_size, current_field); + + return bytes_copied; +} + +u32 cx231xx_copy_video_line(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, u8 *p_line, + u32 length, int field_number) +{ + u32 bytes_to_copy; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width * 2; + + if (dma_q->current_field != field_number) + cx231xx_reset_video_buffer(dev, dma_q); + + /* get the buffer pointer */ + if (dev->USE_ISO) + buf = dev->video_mode.isoc_ctl.buf; + else + buf = dev->video_mode.bulk_ctl.buf; + + /* Remember the field number for next time */ + dma_q->current_field = field_number; + + bytes_to_copy = dma_q->bytes_left_in_line; + if (bytes_to_copy > length) + bytes_to_copy = length; + + if (dma_q->lines_completed >= dma_q->lines_per_field) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ? + 0 : 1; + return 0; + } + + dma_q->is_partial_line = 1; + + /* If we don't have a buffer, just return the number of bytes we would + have copied if we had a buffer. */ + if (!buf) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) + ? 0 : 1; + return bytes_to_copy; + } + + /* copy the data to video buffer */ + cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy); + + dma_q->pos += bytes_to_copy; + dma_q->bytes_left_in_line -= bytes_to_copy; + + if (dma_q->bytes_left_in_line == 0) { + dma_q->bytes_left_in_line = _line_size; + dma_q->lines_completed++; + dma_q->is_partial_line = 0; + + if (cx231xx_is_buffer_done(dev, dma_q) && buf) { + buffer_filled(dev, dma_q, buf); + + dma_q->pos = 0; + buf = NULL; + dma_q->lines_completed = 0; + } + } + + return bytes_to_copy; +} + +void cx231xx_reset_video_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + struct cx231xx_buffer *buf; + + /* handle the switch from field 1 to field 2 */ + if (dma_q->current_field == 1) { + if (dma_q->lines_completed >= dma_q->lines_per_field) + dma_q->field1_done = 1; + else + dma_q->field1_done = 0; + } + + if (dev->USE_ISO) + buf = dev->video_mode.isoc_ctl.buf; + else + buf = dev->video_mode.bulk_ctl.buf; + + if (buf == NULL) { + /* first try to get the buffer */ + get_next_buf(dma_q, &buf); + + dma_q->pos = 0; + dma_q->field1_done = 0; + dma_q->current_field = -1; + } + + /* reset the counters */ + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; +} + +int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy) +{ + u8 *p_out_buffer = NULL; + u32 current_line_bytes_copied = 0; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width << 1; + void *startwrite; + int offset, lencopy; + + if (dev->USE_ISO) + buf = dev->video_mode.isoc_ctl.buf; + else + buf = dev->video_mode.bulk_ctl.buf; + + if (buf == NULL) + return -1; + + p_out_buffer = videobuf_to_vmalloc(&buf->vb); + + current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line; + + /* Offset field 2 one line from the top of the buffer */ + offset = (dma_q->current_field == 1) ? 0 : _line_size; + + /* Offset for field 2 */ + startwrite = p_out_buffer + offset; + + /* lines already completed in the current field */ + startwrite += (dma_q->lines_completed * _line_size * 2); + + /* bytes already completed in the current line */ + startwrite += current_line_bytes_copied; + + lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? + bytes_to_copy : dma_q->bytes_left_in_line; + + if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size)) + return 0; + + /* The below copies the UYVY data straight into video buffer */ + cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy); + + return 0; +} + +void cx231xx_swab(u16 *from, u16 *to, u16 len) +{ + u16 i; + + if (len <= 0) + return; + + for (i = 0; i < len / 2; i++) + to[i] = (from[i] << 8) | (from[i] >> 8); +} + +u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q) +{ + u8 buffer_complete = 0; + + /* Dual field stream */ + buffer_complete = ((dma_q->current_field == 2) && + (dma_q->lines_completed >= dma_q->lines_per_field) && + dma_q->field1_done); + + return buffer_complete; +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3; + if (0 == *count) + *count = CX231XX_DEF_BUF; + + if (*count < CX231XX_MIN_BUF) + *count = CX231XX_MIN_BUF; + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + unsigned long flags = 0; + + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->video_mode.slock, flags); + if (dev->USE_ISO) { + if (dev->video_mode.isoc_ctl.buf == buf) + dev->video_mode.isoc_ctl.buf = NULL; + } else { + if (dev->video_mode.bulk_ctl.buf == buf) + dev->video_mode.bulk_ctl.buf = NULL; + } + spin_unlock_irqrestore(&dev->video_mode.slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + int rc = 0, urb_init = 0; + + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (dev->USE_ISO) { + if (!dev->video_mode.isoc_ctl.num_bufs) + urb_init = 1; + } else { + if (!dev->video_mode.bulk_ctl.num_bufs) + urb_init = 1; + } + /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n", + urb_init, dev->video_mode.max_pkt_size);*/ + if (urb_init) { + dev->mode_tv = 0; + if (dev->USE_ISO) + rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_isoc_copy); + else + rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_bulk_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = (struct cx231xx *)fh->dev; + + cx231xx_isocdbg("cx231xx: called buffer_release\n"); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops cx231xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************* v4l2 interface **************************************/ + +void video_mux(struct cx231xx *dev, int index) +{ + dev->video_input = index; + dev->ctl_ainput = INPUT(index)->amux; + + cx231xx_set_video_input_mux(dev, index); + + cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0); + + cx231xx_set_audio_input(dev, dev->ctl_ainput); + + cx231xx_info("video_mux : %d\n", index); + + /* do mode control overrides if required */ + cx231xx_do_mode_ctrl_overrides(dev); +} + +/* Usage lock check functions */ +static int res_get(struct cx231xx_fh *fh) +{ + struct cx231xx *dev = fh->dev; + int rc = 0; + + /* This instance already has stream_on */ + if (fh->stream_on) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (dev->stream_on) + return -EBUSY; + dev->stream_on = 1; + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (dev->vbi_stream_on) + return -EBUSY; + dev->vbi_stream_on = 1; + } else + return -EINVAL; + + fh->stream_on = 1; + + return rc; +} + +static int res_check(struct cx231xx_fh *fh) +{ + return fh->stream_on; +} + +static void res_free(struct cx231xx_fh *fh) +{ + struct cx231xx *dev = fh->dev; + + fh->stream_on = 0; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + dev->stream_on = 0; + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + dev->vbi_stream_on = 0; +} + +static int check_dev(struct cx231xx *dev) +{ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_errdev("v4l2 ioctl: device not present\n"); + return -ENODEV; + } + return 0; +} + +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = dev->format->fourcc; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + return 0; +} + +static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return &format[i]; + + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + unsigned int width = f->fmt.pix.width; + unsigned int height = f->fmt.pix.height; + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + struct cx231xx_fmt *fmt; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + cx231xx_videodbg("Fourcc format (%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + /* width must even because of the YUYV format + height must be even because of interlacing */ + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); + + f->fmt.pix.width = width; + f->fmt.pix.height = height; + f->fmt.pix.pixelformat = fmt->fourcc; + f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + struct cx231xx_fmt *fmt; + struct v4l2_mbus_framefmt mbus_fmt; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + vidioc_try_fmt_vid_cap(file, priv, f); + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) + return -EINVAL; + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + cx231xx_errdev("%s queue busy\n", __func__); + return -EBUSY; + } + + if (dev->stream_on && !fh->stream_on) { + cx231xx_errdev("%s device in use by another fh\n", __func__); + return -EBUSY; + } + + /* set new image size */ + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->format = fmt; + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); + call_all(dev, video, s_mbus_fmt, &mbus_fmt); + v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + + return rc; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + *id = dev->norm; + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_format f; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm); + + dev->norm = *norm; + + /* Adjusts width/height, if needed */ + f.fmt.pix.width = dev->width; + f.fmt.pix.height = dev->height; + vidioc_try_fmt_vid_cap(file, priv, &f); + + call_all(dev, core, s_std, dev->norm); + + /* We need to reset basic properties in the decoder related to + resolution (since a standard change effects things like the number + of lines in VACT, etc) */ + v4l2_fill_mbus_format(&mbus_fmt, &f.fmt.pix, V4L2_MBUS_FMT_FIXED); + call_all(dev, video, s_mbus_fmt, &mbus_fmt); + v4l2_fill_pix_format(&f.fmt.pix, &mbus_fmt); + + /* set new image size */ + dev->width = f.fmt.pix.width; + dev->height = f.fmt.pix.height; + + /* do mode control overrides */ + cx231xx_do_mode_ctrl_overrides(dev); + + return 0; +} + +static const char *iname[] = { + [CX231XX_VMUX_COMPOSITE1] = "Composite1", + [CX231XX_VMUX_SVIDEO] = "S-Video", + [CX231XX_VMUX_TELEVISION] = "Television", + [CX231XX_VMUX_CABLE] = "Cable TV", + [CX231XX_VMUX_DVB] = "DVB", + [CX231XX_VMUX_DEBUG] = "for debug only", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + u32 gen_stat; + unsigned int ret, n; + + n = i->index; + if (n >= MAX_CX231XX_INPUT) + return -EINVAL; + if (0 == INPUT(n)->type) + return -EINVAL; + + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + + strcpy(i->name, iname[INPUT(n)->type]); + + if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) || + (CX231XX_VMUX_CABLE == INPUT(n)->type)) + i->type = V4L2_INPUT_TYPE_TUNER; + + i->std = dev->vdev->tvnorms; + + /* If they are asking about the active input, read signal status */ + if (n == dev->video_input) { + ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + GEN_STAT, 2, &gen_stat, 4); + if (ret > 0) { + if ((gen_stat & FLD_VPRES) == 0x00) + i->status |= V4L2_IN_ST_NO_SIGNAL; + if ((gen_stat & FLD_HLOCK) == 0x00) + i->status |= V4L2_IN_ST_NO_H_LOCK; + } + } + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + *i = dev->video_input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + dev->mode_tv = 0; + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (i >= MAX_CX231XX_INPUT) + return -EINVAL; + if (0 == INPUT(i)->type) + return -EINVAL; + + video_mux(dev, i); + + if (INPUT(i)->type == CX231XX_VMUX_TELEVISION || + INPUT(i)->type == CX231XX_VMUX_CABLE) { + /* There's a tuner, so reset the standard and put it on the + last known frequency (since it was probably powered down + until now */ + call_all(dev, core, s_std, dev->norm); + } + + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + switch (a->index) { + case CX231XX_AMUX_VIDEO: + strcpy(a->name, "Television"); + break; + case CX231XX_AMUX_LINE_IN: + strcpy(a->name, "Line In"); + break; + default: + return -EINVAL; + } + + a->index = dev->ctl_ainput; + a->capability = V4L2_AUDCAP_STEREO; + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int status = 0; + + /* Doesn't allow manual routing */ + if (a->index != dev->ctl_ainput) + return -EINVAL; + + dev->ctl_ainput = INPUT(a->index)->amux; + status = cx231xx_set_audio_input(dev, dev->ctl_ainput); + + return status; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int id = qc->id; + int i; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + qc->id = v4l2_ctrl_next(ctrl_classes, qc->id); + if (unlikely(qc->id == 0)) + return -EINVAL; + + memset(qc, 0, sizeof(*qc)); + + qc->id = id; + + if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1) + return -EINVAL; + + for (i = 0; i < CX231XX_CTLS; i++) + if (cx231xx_ctls[i].v.id == qc->id) + break; + + if (i == CX231XX_CTLS) { + *qc = no_ctl; + return 0; + } + *qc = cx231xx_ctls[i].v; + + call_all(dev, core, queryctrl, qc); + + if (qc->type) + return 0; + else + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + call_all(dev, core, g_ctrl, ctrl); + return rc; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + call_all(dev, core, s_ctrl, ctrl); + return rc; +} + +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Tuner"); + + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->signal = 0xffff; /* LOCKED */ + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; +#if 0 + call_all(dev, tuner, s_tuner, t); +#endif + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; + + call_all(dev, tuner, g_frequency, f); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + u32 if_frequency = 5400000; + + cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n", + f->frequency, f->type); + /*cx231xx_info("f->type: 1-radio 2-analogTV 3-digitalTV\n");*/ + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != f->tuner) + return -EINVAL; + + if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) + return -EINVAL; + + /* set pre channel change settings in DIF first */ + rc = cx231xx_tuner_pre_channel_change(dev); + + dev->ctl_freq = f->frequency; + call_all(dev, tuner, s_frequency, f); + + /* set post channel change settings in DIF first */ + rc = cx231xx_tuner_post_channel_change(dev); + + if (dev->tuner_type == TUNER_NXP_TDA18271) { + if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443)) + if_frequency = 5400000; /*5.4MHz */ + else if (dev->norm & V4L2_STD_B) + if_frequency = 6000000; /*6.0MHz */ + else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK)) + if_frequency = 6900000; /*6.9MHz */ + else if (dev->norm & V4L2_STD_GH) + if_frequency = 7100000; /*7.1MHz */ + else if (dev->norm & V4L2_STD_PAL_I) + if_frequency = 7250000; /*7.25MHz */ + else if (dev->norm & V4L2_STD_SECAM_L) + if_frequency = 6900000; /*6.9MHz */ + else if (dev->norm & V4L2_STD_SECAM_LC) + if_frequency = 1250000; /*1.25MHz */ + + cx231xx_info("if_frequency is set to %d\n", if_frequency); + cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1); + + update_HH_register_after_set_DIF(dev); + } + + cx231xx_info("Set New FREQUENCY to %d\n", f->frequency); + + return rc; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG + +/* + -R, --list-registers=type=, + chip=[,min=,max=] + dump registers from to [VIDIOC_DBG_G_REGISTER] + -r, --set-register=type=, + chip=,reg=,val= + set the register [VIDIOC_DBG_S_REGISTER] + + if type == host, then is the hosts chip ID (default 0) + if type == i2cdrv (default), then is the I2C driver name or ID + if type == i2caddr, then is the 7-bit I2C address +*/ + +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int ret = 0; + u8 value[4] = { 0, 0, 0, 0 }; + u32 data = 0; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + switch (reg->match.addr) { + case 0: /* Cx231xx - internal registers */ + ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, + (u16)reg->reg, value, 4); + reg->val = value[0] | value[1] << 8 | + value[2] << 16 | value[3] << 24; + break; + case 1: /* AFE - read byte */ + ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 14: /* AFE - read dword */ + ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, &data, 4); + reg->val = le32_to_cpu(data); + break; + case 2: /* Video Block - read byte */ + ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 24: /* Video Block - read dword */ + ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, &data, 4); + reg->val = le32_to_cpu(data); + break; + case 3: /* I2S block - read byte */ + ret = cx231xx_read_i2c_data(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 34: /* I2S Block - read dword */ + ret = + cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, &data, 4); + reg->val = le32_to_cpu(data); + break; + } + return ret < 0 ? ret : 0; + + case V4L2_CHIP_MATCH_I2C_DRIVER: + call_all(dev, core, g_register, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR:/*for register debug*/ + switch (reg->match.addr) { + case 0: /* Cx231xx - internal registers */ + ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, + (u16)reg->reg, value, 4); + reg->val = value[0] | value[1] << 8 | + value[2] << 16 | value[3] << 24; + + break; + case 0x600:/* AFE - read byte */ + ret = cx231xx_read_i2c_master(dev, AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, + &data, 1 , 0); + reg->val = le32_to_cpu(data & 0xff); + break; + + case 0x880:/* Video Block - read byte */ + if (reg->reg < 0x0b) { + ret = cx231xx_read_i2c_master(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + &data, 1 , 0); + reg->val = le32_to_cpu(data & 0xff); + } else { + ret = cx231xx_read_i2c_master(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + &data, 4 , 0); + reg->val = le32_to_cpu(data); + } + break; + case 0x980: + ret = cx231xx_read_i2c_master(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + &data, 1 , 0); + reg->val = le32_to_cpu(data & 0xff); + break; + case 0x400: + ret = + cx231xx_read_i2c_master(dev, 0x40, + (u16)reg->reg, 1, + &data, 1 , 0); + reg->val = le32_to_cpu(data & 0xff); + break; + case 0xc01: + ret = + cx231xx_read_i2c_master(dev, 0xc0, + (u16)reg->reg, 2, + &data, 38, 1); + reg->val = le32_to_cpu(data); + break; + case 0x022: + ret = + cx231xx_read_i2c_master(dev, 0x02, + (u16)reg->reg, 1, + &data, 1, 2); + reg->val = le32_to_cpu(data & 0xff); + break; + case 0x322: + ret = cx231xx_read_i2c_master(dev, + 0x32, + (u16)reg->reg, 1, + &data, 4 , 2); + reg->val = le32_to_cpu(data); + break; + case 0x342: + ret = cx231xx_read_i2c_master(dev, + 0x34, + (u16)reg->reg, 1, + &data, 4 , 2); + reg->val = le32_to_cpu(data); + break; + + default: + cx231xx_info("no match device address!!\n"); + break; + } + return ret < 0 ? ret : 0; + /*return -EINVAL;*/ + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + call_all(dev, core, g_register, reg); + + return ret; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int ret = 0; + __le64 buf; + u32 value; + u8 data[4] = { 0, 0, 0, 0 }; + + buf = cpu_to_le64(reg->val); + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + { + value = (u32) buf & 0xffffffff; + + switch (reg->match.addr) { + case 0: /* cx231xx internal registers */ + data[0] = (u8) value; + data[1] = (u8) (value >> 8); + data[2] = (u8) (value >> 16); + data[3] = (u8) (value >> 24); + ret = cx231xx_write_ctrl_reg(dev, + VRT_SET_REGISTER, + (u16)reg->reg, data, + 4); + break; + case 1: /* AFE - read byte */ + ret = cx231xx_write_i2c_data(dev, + AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, + value, 1); + break; + case 14: /* AFE - read dword */ + ret = cx231xx_write_i2c_data(dev, + AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, + value, 4); + break; + case 2: /* Video Block - read byte */ + ret = + cx231xx_write_i2c_data(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 1); + break; + case 24: /* Video Block - read dword */ + ret = + cx231xx_write_i2c_data(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 4); + break; + case 3: /* I2S block - read byte */ + ret = + cx231xx_write_i2c_data(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + value, 1); + break; + case 34: /* I2S block - read dword */ + ret = + cx231xx_write_i2c_data(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + value, 4); + break; + } + } + return ret < 0 ? ret : 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + { + value = (u32) buf & 0xffffffff; + + switch (reg->match.addr) { + case 0:/*cx231xx internal registers*/ + data[0] = (u8) value; + data[1] = (u8) (value >> 8); + data[2] = (u8) (value >> 16); + data[3] = (u8) (value >> 24); + ret = cx231xx_write_ctrl_reg(dev, + VRT_SET_REGISTER, + (u16)reg->reg, data, + 4); + break; + case 0x600:/* AFE - read byte */ + ret = cx231xx_write_i2c_master(dev, + AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, + value, 1 , 0); + break; + + case 0x880:/* Video Block - read byte */ + if (reg->reg < 0x0b) + cx231xx_write_i2c_master(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 1, 0); + else + cx231xx_write_i2c_master(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 4, 0); + break; + case 0x980: + ret = + cx231xx_write_i2c_master(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + value, 1, 0); + break; + case 0x400: + ret = + cx231xx_write_i2c_master(dev, + 0x40, + (u16)reg->reg, 1, + value, 1, 0); + break; + case 0xc01: + ret = + cx231xx_write_i2c_master(dev, + 0xc0, + (u16)reg->reg, 1, + value, 1, 1); + break; + + case 0x022: + ret = + cx231xx_write_i2c_master(dev, + 0x02, + (u16)reg->reg, 1, + value, 1, 2); + case 0x322: + ret = + cx231xx_write_i2c_master(dev, + 0x32, + (u16)reg->reg, 1, + value, 4, 2); + break; + + case 0x342: + ret = + cx231xx_write_i2c_master(dev, + 0x34, + (u16)reg->reg, 1, + value, 4, 2); + break; + default: + cx231xx_info("no match device address, " + "the value is %x\n", reg->match.addr); + break; + + } + + } + default: + break; + } + + call_all(dev, core, s_register, reg); + + return ret; +} +#endif + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cc->bounds.left = 0; + cc->bounds.top = 0; + cc->bounds.width = dev->width; + cc->bounds.height = dev->height; + cc->defrect = cc->bounds; + cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ + cc->pixelaspect.denominator = 59; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + rc = res_get(fh); + + if (likely(rc >= 0)) + rc = videobuf_streamon(&fh->vb_vidq); + + call_all(dev, video, s_stream, 1); + + return rc; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + if (type != fh->type) + return -EINVAL; + + cx25840_call(dev, video, s_stream, 0); + + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); + strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->capabilities = V4L2_CAP_VBI_CAPTURE | +#if 0 + V4L2_CAP_SLICED_VBI_CAPTURE | +#endif + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + + return 0; +} + +/* Sliced VBI ioctls */ +static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + f->fmt.sliced.service_set = 0; + + call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); + + if (f->fmt.sliced.service_set == 0) + rc = -EINVAL; + + return rc; +} + +static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); + + if (f->fmt.sliced.service_set == 0) + return -EINVAL; + + return 0; +} + +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + f->fmt.vbi.sampling_rate = 6750000 * 4; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 0; + f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE : NTSC_VBI_START_LINE; + f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES; + f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + + return 0; + +} + +static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + if (dev->vbi_stream_on && !fh->stream_on) { + cx231xx_errdev("%s device in use by another fh\n", __func__); + return -EBUSY; + } + + f->type = V4L2_BUF_TYPE_VBI_CAPTURE; + f->fmt.vbi.sampling_rate = 6750000 * 4; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 0; + f->fmt.vbi.flags = 0; + f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE : NTSC_VBI_START_LINE; + f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES; + f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + + return 0; + +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_reqbufs(&fh->vb_vidq, rb); +} + +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_querybuf(&fh->vb_vidq, b); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_qbuf(&fh->vb_vidq, b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); +} + +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ +/* ----------------------------------------------------------- */ + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); + strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + if (unlikely(t->index > 0)) + return -EINVAL; + + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + call_all(dev, tuner, s_tuner, t); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; + + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + if (0 != t->index) + return -EINVAL; + + call_all(dev, tuner, s_tuner, t); + + return 0; +} + +static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *file, void *fh, unsigned int i) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + int i; + + if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + for (i = 0; i < CX231XX_CTLS; i++) { + if (cx231xx_ctls[i].v.id == c->id) + break; + } + if (i == CX231XX_CTLS) + return -EINVAL; + *c = cx231xx_ctls[i].v; + } else + *c = no_ctl; + return 0; +} + +/* + * cx231xx_v4l2_open() + * inits the device and starts isoc transfer + */ +static int cx231xx_v4l2_open(struct file *filp) +{ + int errCode = 0, radio = 0; + struct video_device *vdev = video_devdata(filp); + struct cx231xx *dev = video_drvdata(filp); + struct cx231xx_fh *fh; + enum v4l2_buf_type fh_type = 0; + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + } + + cx231xx_videodbg("open dev=%s type=%s users=%d\n", + video_device_node_name(vdev), v4l2_type_names[fh_type], + dev->users); + +#if 0 + errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); + if (errCode < 0) { + cx231xx_errdev + ("Device locked on digital mode. Can't open analog\n"); + return -EBUSY; + } +#endif + + fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL); + if (!fh) { + cx231xx_errdev("cx231xx-video.c: Out of memory?!\n"); + return -ENOMEM; + } + if (mutex_lock_interruptible(&dev->lock)) { + kfree(fh); + return -ERESTARTSYS; + } + fh->dev = dev; + fh->radio = radio; + fh->type = fh_type; + filp->private_data = fh; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + + /* Power up in Analog TV mode */ + if (dev->board.external_av) + cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ENXTERNAL_AV); + else + cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV); + +#if 0 + cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); +#endif + + /* set video alternate setting */ + cx231xx_set_video_alternate(dev); + + /* Needed, since GPIO might have disabled power of + some i2c device */ + cx231xx_config_i2c(dev); + + /* device needs to be initialized before isoc transfer */ + dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; + + } + if (fh->radio) { + cx231xx_videodbg("video_open: setting radio device\n"); + + /* cx231xx_start_radio(dev); */ + + call_all(dev, tuner, s_radio); + } + + dev->users++; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops, + NULL, &dev->video_mode.slock, + fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct cx231xx_buffer), + fh, &dev->lock); + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* Set the required alternate setting VBI interface works in + Bulk mode only */ + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops, + NULL, &dev->vbi_mode.slock, + fh->type, V4L2_FIELD_SEQ_TB, + sizeof(struct cx231xx_buffer), + fh, &dev->lock); + } + mutex_unlock(&dev->lock); + + return errCode; +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_release_analog_resources(struct cx231xx *dev) +{ + + /*FIXME: I2C IR should be disconnected */ + + if (dev->radio_dev) { + if (video_is_registered(dev->radio_dev)) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + if (dev->vbi_dev) { + cx231xx_info("V4L2 device %s deregistered\n", + video_device_node_name(dev->vbi_dev)); + if (video_is_registered(dev->vbi_dev)) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + } + if (dev->vdev) { + cx231xx_info("V4L2 device %s deregistered\n", + video_device_node_name(dev->vdev)); + + if (dev->board.has_417) + cx231xx_417_unregister(dev); + + if (video_is_registered(dev->vdev)) + video_unregister_device(dev->vdev); + else + video_device_release(dev->vdev); + dev->vdev = NULL; + } +} + +/* + * cx231xx_close() + * stops streaming and deallocates all resources allocated by the v4l2 + * calls and ioctls + */ +static int cx231xx_close(struct file *filp) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + + cx231xx_videodbg("users=%d\n", dev->users); + + cx231xx_videodbg("users=%d\n", dev->users); + if (res_check(fh)) + res_free(fh); + + /* + * To workaround error number=-71 on EP0 for VideoGrabber, + * need exclude following. + * FIXME: It is probably safe to remove most of these, as we're + * now avoiding the alternate setting for INDEX_VANC + */ + if (!dev->board.no_alt_vanc) + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + /* the device is already disconnect, + free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + if (atomic_read(&dev->devlist_count) > 0) { + cx231xx_release_resources(dev); + fh->dev = NULL; + return 0; + } + return 0; + } + + /* do this before setting alternate! */ + cx231xx_uninit_vbi_isoc(dev); + + /* set alternate 0 */ + if (!dev->vbi_or_sliced_cc_mode) + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + else + cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + return 0; + } + + dev->users--; + if (!dev->users) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + /* the device is already disconnect, + free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_release_resources(dev); + fh->dev = NULL; + return 0; + } + + /* Save some power by putting tuner to sleep */ + call_all(dev, core, s_power, 0); + + /* do this before setting alternate! */ + if (dev->USE_ISO) + cx231xx_uninit_isoc(dev); + else + cx231xx_uninit_bulk(dev); + cx231xx_set_mode(dev, CX231XX_SUSPEND); + + /* set alternate 0 */ + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); + } + kfree(fh); + wake_up_interruptible_nr(&dev->open, 1); + return 0; +} + +static int cx231xx_v4l2_close(struct file *filp) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + mutex_lock(&dev->lock); + rc = cx231xx_close(filp); + mutex_unlock(&dev->lock); + return rc; +} + +/* + * cx231xx_v4l2_read() + * will allocate buffers when called for the first time + */ +static ssize_t +cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) { + rc = res_get(fh); + + if (unlikely(rc < 0)) + return rc; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + mutex_unlock(&dev->lock); + return rc; + } + return 0; +} + +/* + * cx231xx_v4l2_poll() + * will allocate buffers when called for the first time + */ +static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + rc = res_get(fh); + + if (unlikely(rc < 0)) + return POLLERR; + + if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) || + (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) { + unsigned int res; + + mutex_lock(&dev->lock); + res = videobuf_poll_stream(filp, &fh->vb_vidq, wait); + mutex_unlock(&dev->lock); + return res; + } + return POLLERR; +} + +/* + * cx231xx_v4l2_mmap() + */ +static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + rc = res_get(fh); + + if (unlikely(rc < 0)) + return rc; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + + cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - + (unsigned long)vma->vm_start, rc); + + return rc; +} + +static const struct v4l2_file_operations cx231xx_v4l_fops = { + .owner = THIS_MODULE, + .open = cx231xx_v4l2_open, + .release = cx231xx_v4l2_close, + .read = cx231xx_v4l2_read, + .poll = cx231xx_v4l2_poll, + .mmap = cx231xx_v4l2_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device cx231xx_vbi_template; + +static const struct video_device cx231xx_video_template = { + .fops = &cx231xx_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = V4L2_STD_ALL, + .current_norm = V4L2_STD_PAL, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = cx231xx_v4l2_open, + .release = cx231xx_v4l2_close, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device cx231xx_radio_template = { + .name = "cx231xx-radio", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + +/******************************** usb interface ******************************/ + +static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, + const struct video_device + *template, const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = video_debug; + vfd->lock = &dev->lock; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + video_set_drvdata(vfd, dev); + return vfd; +} + +int cx231xx_register_analog_devices(struct cx231xx *dev) +{ + int ret; + + cx231xx_info("%s: v4l2 driver version %s\n", + dev->name, CX231XX_VERSION); + + /* set default norm */ + /*dev->norm = cx231xx_video_template.current_norm; */ + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + dev->interlaced = 0; + + /* Analog specific initialization */ + dev->format = &format[0]; + + /* Set the initial input */ + video_mux(dev, dev->video_input); + + /* Audio defaults */ + dev->mute = 1; + dev->volume = 0x1f; + + /* enable vbi capturing */ + /* write code here... */ + + /* allocate and fill video video_device struct */ + dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video"); + if (!dev->vdev) { + cx231xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + + /* register v4l2 video video_device */ + ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + video_nr[dev->devno]); + if (ret) { + cx231xx_errdev("unable to register video device (error=%i).\n", + ret); + return ret; + } + + cx231xx_info("%s/0: registered device %s [v4l2]\n", + dev->name, video_device_node_name(dev->vdev)); + + /* Initialize VBI template */ + memcpy(&cx231xx_vbi_template, &cx231xx_video_template, + sizeof(cx231xx_vbi_template)); + strcpy(cx231xx_vbi_template.name, "cx231xx-vbi"); + + /* Allocate and fill vbi video_device struct */ + dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi"); + + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + cx231xx_errdev("unable to register vbi device\n"); + return ret; + } + + cx231xx_info("%s/0: registered device %s\n", + dev->name, video_device_node_name(dev->vbi_dev)); + + if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) { + dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template, + "radio"); + if (!dev->radio_dev) { + cx231xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr[dev->devno]); + if (ret < 0) { + cx231xx_errdev("can't register radio device\n"); + return ret; + } + cx231xx_info("Registered radio device as %s\n", + video_device_node_name(dev->radio_dev)); + } + + cx231xx_info("V4L2 device registered as %s and %s\n", + video_device_node_name(dev->vdev), + video_device_node_name(dev->vbi_dev)); + + return 0; +} diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h new file mode 100644 index 000000000000..a89d020de948 --- /dev/null +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -0,0 +1,1012 @@ +/* + cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 + Based on em28xx driver + + This program is free software; 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 _CX231XX_H +#define _CX231XX_H + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "cx231xx-reg.h" +#include "cx231xx-pcb-cfg.h" +#include "cx231xx-conf-reg.h" + +#define DRIVER_NAME "cx231xx" +#define PWR_SLEEP_INTERVAL 10 + +/* I2C addresses for control block in Cx231xx */ +#define AFE_DEVICE_ADDRESS 0x60 +#define I2S_BLK_DEVICE_ADDRESS 0x98 +#define VID_BLK_I2C_ADDRESS 0x88 +#define VERVE_I2C_ADDRESS 0x40 +#define DIF_USE_BASEBAND 0xFFFFFFFF + +/* Boards supported by driver */ +#define CX231XX_BOARD_UNKNOWN 0 +#define CX231XX_BOARD_CNXT_CARRAERA 1 +#define CX231XX_BOARD_CNXT_SHELBY 2 +#define CX231XX_BOARD_CNXT_RDE_253S 3 +#define CX231XX_BOARD_CNXT_RDU_253S 4 +#define CX231XX_BOARD_CNXT_VIDEO_GRABBER 5 +#define CX231XX_BOARD_CNXT_RDE_250 6 +#define CX231XX_BOARD_CNXT_RDU_250 7 +#define CX231XX_BOARD_HAUPPAUGE_EXETER 8 +#define CX231XX_BOARD_HAUPPAUGE_USBLIVE2 9 +#define CX231XX_BOARD_PV_PLAYTV_USB_HYBRID 10 +#define CX231XX_BOARD_PV_XCAPTURE_USB 11 +#define CX231XX_BOARD_KWORLD_UB430_USB_HYBRID 12 +#define CX231XX_BOARD_ICONBIT_U100 13 +#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14 +#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15 + +/* Limits minimum and default number of buffers */ +#define CX231XX_MIN_BUF 4 +#define CX231XX_DEF_BUF 12 +#define CX231XX_DEF_VBI_BUF 6 + +#define VBI_LINE_COUNT 17 +#define VBI_LINE_LENGTH 1440 + +/*Limits the max URB message size */ +#define URB_MAX_CTRL_SIZE 80 + +/* Params for validated field */ +#define CX231XX_BOARD_NOT_VALIDATED 1 +#define CX231XX_BOARD_VALIDATED 0 + +/* maximum number of cx231xx boards */ +#define CX231XX_MAXBOARDS 8 + +/* maximum number of frames that can be queued */ +#define CX231XX_NUM_FRAMES 5 + +/* number of buffers for isoc transfers */ +#define CX231XX_NUM_BUFS 8 + +/* number of packets for each buffer + windows requests only 40 packets .. so we better do the same + this is what I found out for all alternate numbers there! + */ +#define CX231XX_NUM_PACKETS 40 + +/* default alternate; 0 means choose the best */ +#define CX231XX_PINOUT 0 + +#define CX231XX_INTERLACED_DEFAULT 1 + +/* time to wait when stopping the isoc transfer */ +#define CX231XX_URB_TIMEOUT \ + msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS) + +#define CX231xx_NORMS (\ + V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \ + V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \ + V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK) + +#define SLEEP_S5H1432 30 +#define CX23417_OSC_EN 8 +#define CX23417_RESET 9 + +struct cx23417_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int flags; + u32 cxformat; +}; +enum cx231xx_mode { + CX231XX_SUSPEND, + CX231XX_ANALOG_MODE, + CX231XX_DIGITAL_MODE, +}; + +enum cx231xx_std_mode { + CX231XX_TV_AIR = 0, + CX231XX_TV_CABLE +}; + +enum cx231xx_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct cx231xx; + +struct cx231xx_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct cx231xx_buffer *buf; + + /* Stores the number of received fields */ + int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct cx231xx *dev, struct urb *urb); +}; + +struct cx231xx_bulk_ctl { + /* max packet size of bulk transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for bulk transfers */ + struct urb **urb; + + /* transfer buffers for bulk transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct cx231xx_buffer *buf; + + /* Stores the number of received fields */ + int nfields; + + /* bulk urb callback */ + int (*bulk_copy) (struct cx231xx *dev, struct urb *urb); +}; + +struct cx231xx_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int reg; +}; + +/* buffer for one video frame */ +struct cx231xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct list_head frame; + int top_field; + int receiving; +}; + +enum ps_package_head { + CX231XX_NEED_ADD_PS_PACKAGE_HEAD = 0, + CX231XX_NONEED_PS_PACKAGE_HEAD +}; + +struct cx231xx_dmaqueue { + struct list_head active; + struct list_head queued; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; + u8 is_partial_line; + u8 partial_buf[8]; + u8 last_sav; + int current_field; + u32 bytes_left_in_line; + u32 lines_completed; + u8 field1_done; + u32 lines_per_field; + + /*Mpeg2 control buffer*/ + u8 *p_left_data; + u32 left_data_count; + u8 mpeg_buffer_done; + u32 mpeg_buffer_completed; + enum ps_package_head add_ps_package_head; + char ps_head[10]; +}; + +/* inputs */ + +#define MAX_CX231XX_INPUT 4 + +enum cx231xx_itype { + CX231XX_VMUX_COMPOSITE1 = 1, + CX231XX_VMUX_SVIDEO, + CX231XX_VMUX_TELEVISION, + CX231XX_VMUX_CABLE, + CX231XX_RADIO, + CX231XX_VMUX_DVB, + CX231XX_VMUX_DEBUG +}; + +enum cx231xx_v_input { + CX231XX_VIN_1_1 = 0x1, + CX231XX_VIN_2_1, + CX231XX_VIN_3_1, + CX231XX_VIN_4_1, + CX231XX_VIN_1_2 = 0x01, + CX231XX_VIN_2_2, + CX231XX_VIN_3_2, + CX231XX_VIN_1_3 = 0x1, + CX231XX_VIN_2_3, + CX231XX_VIN_3_3, +}; + +/* cx231xx has two audio inputs: tuner and line in */ +enum cx231xx_amux { + /* This is the only entry for cx231xx tuner input */ + CX231XX_AMUX_VIDEO, /* cx231xx tuner */ + CX231XX_AMUX_LINE_IN, /* Line In */ +}; + +struct cx231xx_reg_seq { + unsigned char bit; + unsigned char val; + int sleep; +}; + +struct cx231xx_input { + enum cx231xx_itype type; + unsigned int vmux; + enum cx231xx_amux amux; + struct cx231xx_reg_seq *gpio; +}; + +#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr]) + +enum cx231xx_decoder { + CX231XX_NODECODER, + CX231XX_AVDECODER +}; + +enum CX231XX_I2C_MASTER_PORT { + I2C_0 = 0, + I2C_1 = 1, + I2C_2 = 2, + I2C_3 = 3 +}; + +struct cx231xx_board { + char *name; + int vchannels; + int tuner_type; + int tuner_addr; + v4l2_std_id norm; /* tv norm */ + + /* demod related */ + int demod_addr; + u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */ + + /* GPIO Pins */ + struct cx231xx_reg_seq *dvb_gpio; + struct cx231xx_reg_seq *suspend_gpio; + struct cx231xx_reg_seq *tuner_gpio; + /* Negative means don't use it */ + s8 tuner_sif_gpio; + s8 tuner_scl_gpio; + s8 tuner_sda_gpio; + + /* PIN ctrl */ + u32 ctl_pin_status_mask; + u8 agc_analog_digital_select_gpio; + u32 gpio_pin_status_mask; + + /* i2c masters */ + u8 tuner_i2c_master; + u8 demod_i2c_master; + u8 ir_i2c_master; + + /* for devices with I2C chips for IR */ + char *rc_map_name; + + unsigned int max_range_640_480:1; + unsigned int has_dvb:1; + unsigned int has_417:1; + unsigned int valid:1; + unsigned int no_alt_vanc:1; + unsigned int external_av:1; + unsigned int dont_use_port_3:1; + + unsigned char xclk, i2c_speed; + + enum cx231xx_decoder decoder; + int output_mode; + + struct cx231xx_input input[MAX_CX231XX_INPUT]; + struct cx231xx_input radio; + struct rc_map *ir_codes; +}; + +/* device states */ +enum cx231xx_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, +}; + +enum AFE_MODE { + AFE_MODE_LOW_IF, + AFE_MODE_BASEBAND, + AFE_MODE_EU_HI_IF, + AFE_MODE_US_HI_IF, + AFE_MODE_JAPAN_HI_IF +}; + +enum AUDIO_INPUT { + AUDIO_INPUT_MUTE, + AUDIO_INPUT_LINE, + AUDIO_INPUT_TUNER_TV, + AUDIO_INPUT_SPDIF, + AUDIO_INPUT_TUNER_FM +}; + +#define CX231XX_AUDIO_BUFS 5 +#define CX231XX_NUM_AUDIO_PACKETS 16 +#define CX231XX_ISO_NUM_AUDIO_PACKETS 64 + +/* cx231xx extensions */ +#define CX231XX_AUDIO 0x10 +#define CX231XX_DVB 0x20 + +struct cx231xx_audio { + char name[50]; + char *transfer_buffer[CX231XX_AUDIO_BUFS]; + struct urb *urb[CX231XX_AUDIO_BUFS]; + struct usb_device *udev; + unsigned int capture_transfer_done; + struct snd_pcm_substream *capture_pcm_substream; + + unsigned int hwptr_done_capture; + struct snd_card *sndcard; + + int users, shutdown; + /* locks */ + spinlock_t slock; + + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + u16 end_point_addr; +}; + +struct cx231xx; + +struct cx231xx_fh { + struct cx231xx *dev; + unsigned int stream_on:1; /* Locks streams */ + int radio; + + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; + + + +/*following is copyed from cx23885.h*/ + u32 resources; + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip *clips; + unsigned int nclips; + + /* video capture */ + struct cx23417_fmt *fmt; + unsigned int width, height; + + /* vbi capture */ + struct videobuf_queue vidq; + struct videobuf_queue vbiq; + + /* MPEG Encoder specifics ONLY */ + + atomic_t v4l_reading; +}; + +/*****************************************************************/ +/* set/get i2c */ +/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */ +#define I2C_SPEED_1M 0x0 +#define I2C_SPEED_400K 0x1 +#define I2C_SPEED_100K 0x2 +#define I2C_SPEED_5M 0x3 + +/* 0-- STOP transaction */ +#define I2C_STOP 0x0 +/* 1-- do not transmit STOP at end of transaction */ +#define I2C_NOSTOP 0x1 +/* 1--allow slave to insert clock wait states */ +#define I2C_SYNC 0x1 + +struct cx231xx_i2c { + struct cx231xx *dev; + + int nr; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* different settings for each bus */ + u8 i2c_period; + u8 i2c_nostop; + u8 i2c_reserve; +}; + +struct cx231xx_i2c_xfer_data { + u8 dev_addr; + u8 direction; /* 1 - IN, 0 - OUT */ + u8 saddr_len; /* sub address len */ + u16 saddr_dat; /* sub addr data */ + u8 buf_size; /* buffer size */ + u8 *p_buffer; /* pointer to the buffer */ +}; + +struct VENDOR_REQUEST_IN { + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; + u8 direction; + u8 bData; + u8 *pBuff; +}; + +struct cx231xx_tvnorm { + char *name; + v4l2_std_id id; + u32 cxiformat; + u32 cxoformat; +}; + +struct cx231xx_ctrl { + struct v4l2_queryctrl v; + u32 off; + u32 reg; + u32 mask; + u32 shift; +}; + +enum TRANSFER_TYPE { + Raw_Video = 0, + Audio, + Vbi, /* VANC */ + Sliced_cc, /* HANC */ + TS1_serial_mode, + TS2, + TS1_parallel_mode +} ; + +struct cx231xx_video_mode { + /* Isoc control struct */ + struct cx231xx_dmaqueue vidq; + struct cx231xx_isoc_ctl isoc_ctl; + struct cx231xx_bulk_ctl bulk_ctl; + /* locks */ + spinlock_t slock; + + /* usb transfer */ + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + u16 end_point_addr; +}; +/* +struct cx23885_dmaqueue { + struct list_head active; + struct list_head queued; + struct timer_list timeout; + struct btcx_riscmem stopper; + u32 count; +}; +*/ +struct cx231xx_tsport { + struct cx231xx *dev; + + int nr; + int sram_chno; + + struct videobuf_dvb_frontends frontends; + + /* dma queues */ + + u32 ts_packet_size; + u32 ts_packet_count; + + int width; + int height; + + /* locks */ + spinlock_t slock; + + /* registers */ + u32 reg_gpcnt; + u32 reg_gpcnt_ctl; + u32 reg_dma_ctl; + u32 reg_lngth; + u32 reg_hw_sop_ctrl; + u32 reg_gen_ctrl; + u32 reg_bd_pkt_status; + u32 reg_sop_status; + u32 reg_fifo_ovfl_stat; + u32 reg_vld_misc; + u32 reg_ts_clk_en; + u32 reg_ts_int_msk; + u32 reg_ts_int_stat; + u32 reg_src_sel; + + /* Default register vals */ + int pci_irqmask; + u32 dma_ctl_val; + u32 ts_int_msk_val; + u32 gen_ctrl_val; + u32 ts_clk_en_val; + u32 src_sel_val; + u32 vld_misc_val; + u32 hw_sop_ctrl_val; + + /* Allow a single tsport to have multiple frontends */ + u32 num_frontends; + void *port_priv; +}; + +/* main device struct */ +struct cx231xx { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + + struct cx231xx_board board; + + /* For I2C IR support */ + struct IR_i2c_init_data init_data; + struct i2c_client *ir_i2c_client; + + unsigned int stream_on:1; /* Locks streams */ + unsigned int vbi_stream_on:1; /* Locks streams for VBI */ + unsigned int has_audio_class:1; + unsigned int has_alsa_audio:1; + + struct cx231xx_fmt *format; + + struct v4l2_device v4l2_dev; + struct v4l2_subdev *sd_cx25840; + struct v4l2_subdev *sd_tuner; + + struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ + atomic_t stream_started; /* stream should be running if true */ + + struct list_head devlist; + + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ + struct cx231xx_i2c i2c_bus[3]; + unsigned int xc_fw_load_done:1; + /* locks */ + struct mutex gpio_i2c_lock; + struct mutex i2c_lock; + + /* video for linux */ + int users; /* user count for exclusive use */ + struct video_device *vdev; /* video for linux device struct */ + v4l2_std_id norm; /* selected tv norm */ + int ctl_freq; /* selected frequency */ + unsigned int ctl_ainput; /* selected audio input */ + int mute; + int volume; + + /* frame properties */ + int width; /* current frame width */ + int height; /* current frame height */ + int interlaced; /* 1=interlace fileds, 0=just top fileds */ + + struct cx231xx_audio adev; + + /* states */ + enum cx231xx_dev_state state; + + struct work_struct request_module_wk; + + /* locks */ + struct mutex lock; + struct mutex ctrl_urb_lock; /* protects urb_buf */ + struct list_head inqueue, outqueue; + wait_queue_head_t open, wait_frame, wait_stream; + struct video_device *vbi_dev; + struct video_device *radio_dev; + + unsigned char eedata[256]; + + struct cx231xx_video_mode video_mode; + struct cx231xx_video_mode vbi_mode; + struct cx231xx_video_mode sliced_cc_mode; + struct cx231xx_video_mode ts1_mode; + + atomic_t devlist_count; + + struct usb_device *udev; /* the usb device */ + char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ + + /* helper funcs that call usb_control_msg */ + int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data); + int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr, + u8 *buf, u8 len); + int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr, + u8 *buf, u8 len); + + int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq); + int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev); + + enum cx231xx_mode mode; + + struct cx231xx_dvb *dvb; + + /* Cx231xx supported PCB config's */ + struct pcb_config current_pcb_config; + u8 current_scenario_idx; + u8 interface_count; + u8 max_iad_interface_count; + + /* GPIO related register direction and values */ + u32 gpio_dir; + u32 gpio_val; + + /* Power Modes */ + int power_mode; + + /* afe parameters */ + enum AFE_MODE afe_mode; + u32 afe_ref_count; + + /* video related parameters */ + u32 video_input; + u32 active_mode; + u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */ + enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */ + + /*mode: digital=1 or analog=0*/ + u8 mode_tv; + + u8 USE_ISO; + struct cx231xx_tvnorm encodernorm; + struct cx231xx_tsport ts1, ts2; + struct cx2341x_mpeg_params mpeg_params; + struct video_device *v4l_device; + atomic_t v4l_reader_count; + u32 freq; + unsigned int input; + u32 cx23417_mailbox; + u32 __iomem *lmmio; + u8 __iomem *bmmio; +}; + +extern struct list_head cx231xx_devlist; + +#define cx25840_call(cx231xx, o, f, args...) \ + v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args) +#define tuner_call(cx231xx, o, f, args...) \ + v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args) +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) + +struct cx231xx_ops { + struct list_head next; + char *name; + int id; + int (*init) (struct cx231xx *); + int (*fini) (struct cx231xx *); +}; + +/* call back functions in dvb module */ +int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq); +int cx231xx_reset_analog_tuner(struct cx231xx *dev); + +/* Provided by cx231xx-i2c.c */ +void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c); +int cx231xx_i2c_register(struct cx231xx_i2c *bus); +int cx231xx_i2c_unregister(struct cx231xx_i2c *bus); + +/* Internal block control functions */ +int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 *data, u8 data_len, int master); +int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 data, u8 data_len, int master); +int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u8 saddr_len, u32 *data, u8 data_len); +int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u8 saddr_len, u32 data, u8 data_len); +int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, + u16 register_address, u8 bit_start, u8 bit_end, + u32 value); +int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u32 mask, u32 value); +u32 cx231xx_set_field(u32 field_mask, u32 data); + +/*verve r/w*/ +void initGPIO(struct cx231xx *dev); +void uninitGPIO(struct cx231xx *dev); +/* afe related functions */ +int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count); +int cx231xx_afe_init_channels(struct cx231xx *dev); +int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev); +int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux); +int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode); +int cx231xx_afe_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode); +int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input); + +/* i2s block related functions */ +int cx231xx_i2s_blk_initialize(struct cx231xx *dev); +int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode); +int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input); + +/* DIF related functions */ +int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, + u32 function_mode, u32 standard); +void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, + u8 spectral_invert, u32 mode); +u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd); +void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, + u8 spectral_invert, u32 mode); +void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev); +void reset_s5h1432_demod(struct cx231xx *dev); +void cx231xx_dump_HH_reg(struct cx231xx *dev); +void update_HH_register_after_set_DIF(struct cx231xx *dev); +void cx231xx_dump_SC_reg(struct cx231xx *dev); + + + +int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard); +int cx231xx_tuner_pre_channel_change(struct cx231xx *dev); +int cx231xx_tuner_post_channel_change(struct cx231xx *dev); + +/* video parser functions */ +u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, + u32 *p_bytes_used); +u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, + u32 *p_bytes_used); +int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy); +void cx231xx_reset_video_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); +u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q); +u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number); +u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size); +void cx231xx_swab(u16 *from, u16 *to, u16 len); + +/* Provided by cx231xx-core.c */ + +u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count); +void cx231xx_queue_unusedframes(struct cx231xx *dev); +void cx231xx_release_buffers(struct cx231xx *dev); + +/* read from control pipe */ +int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + +/* write to control pipe */ +int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); +int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode); + +int cx231xx_send_vendor_cmd(struct cx231xx *dev, + struct VENDOR_REQUEST_IN *ven_req); +int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data); + +/* Gpio related functions */ +int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, + u8 len, u8 request, u8 direction); +int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); +int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); +int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value); +int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number, + int pin_value); + +int cx231xx_gpio_i2c_start(struct cx231xx *dev); +int cx231xx_gpio_i2c_end(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data); +int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf); +int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev); + +int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); +int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); + +/* audio related functions */ +int cx231xx_set_audio_decoder_input(struct cx231xx *dev, + enum AUDIO_INPUT audio_input); + +int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type); +int cx231xx_set_video_alternate(struct cx231xx *dev); +int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt); +int is_fw_load(struct cx231xx *dev); +int cx231xx_check_fw(struct cx231xx *dev); +int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, + struct urb *urb)); +int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*bulk_copy) (struct cx231xx *dev, + struct urb *urb)); +void cx231xx_stop_TS1(struct cx231xx *dev); +void cx231xx_start_TS1(struct cx231xx *dev); +void cx231xx_uninit_isoc(struct cx231xx *dev); +void cx231xx_uninit_bulk(struct cx231xx *dev); +int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode); +int cx231xx_unmute_audio(struct cx231xx *dev); +int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size); +void cx231xx_disable656(struct cx231xx *dev); +void cx231xx_enable656(struct cx231xx *dev); +int cx231xx_demod_reset(struct cx231xx *dev); +int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio); + +/* Device list functions */ +void cx231xx_release_resources(struct cx231xx *dev); +void cx231xx_release_analog_resources(struct cx231xx *dev); +int cx231xx_register_analog_devices(struct cx231xx *dev); +void cx231xx_remove_from_devlist(struct cx231xx *dev); +void cx231xx_add_into_devlist(struct cx231xx *dev); +void cx231xx_init_extension(struct cx231xx *dev); +void cx231xx_close_extension(struct cx231xx *dev); + +/* hardware init functions */ +int cx231xx_dev_init(struct cx231xx *dev); +void cx231xx_dev_uninit(struct cx231xx *dev); +void cx231xx_config_i2c(struct cx231xx *dev); +int cx231xx_config(struct cx231xx *dev); + +/* Stream control functions */ +int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask); +int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask); + +int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type); + +/* Power control functions */ +int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode); +int cx231xx_power_suspend(struct cx231xx *dev); + +/* chip specific control functions */ +int cx231xx_init_ctrl_pin_status(struct cx231xx *dev); +int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, + u8 analog_or_digital); +int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3); + +/* video audio decoder related functions */ +void video_mux(struct cx231xx *dev, int index); +int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input); +int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input); +int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev); +int cx231xx_set_audio_input(struct cx231xx *dev, u8 input); + +/* Provided by cx231xx-video.c */ +int cx231xx_register_extension(struct cx231xx_ops *dev); +void cx231xx_unregister_extension(struct cx231xx_ops *dev); +void cx231xx_init_extension(struct cx231xx *dev); +void cx231xx_close_extension(struct cx231xx *dev); + +/* Provided by cx231xx-cards.c */ +extern void cx231xx_pre_card_setup(struct cx231xx *dev); +extern void cx231xx_card_setup(struct cx231xx *dev); +extern struct cx231xx_board cx231xx_boards[]; +extern struct usb_device_id cx231xx_id_table[]; +extern const unsigned int cx231xx_bcount; +int cx231xx_tuner_callback(void *ptr, int component, int command, int arg); + +/* cx23885-417.c */ +extern int cx231xx_417_register(struct cx231xx *dev); +extern void cx231xx_417_unregister(struct cx231xx *dev); + +/* cx23885-input.c */ + +#if defined(CONFIG_VIDEO_CX231XX_RC) +int cx231xx_ir_init(struct cx231xx *dev); +void cx231xx_ir_exit(struct cx231xx *dev); +#else +#define cx231xx_ir_init(dev) (0) +#define cx231xx_ir_exit(dev) (0) +#endif + + +/* printk macros */ + +#define cx231xx_err(fmt, arg...) do {\ + printk(KERN_ERR fmt , ##arg); } while (0) + +#define cx231xx_errdev(fmt, arg...) do {\ + printk(KERN_ERR "%s: "fmt,\ + dev->name , ##arg); } while (0) + +#define cx231xx_info(fmt, arg...) do {\ + printk(KERN_INFO "%s: "fmt,\ + dev->name , ##arg); } while (0) +#define cx231xx_warn(fmt, arg...) do {\ + printk(KERN_WARNING "%s: "fmt,\ + dev->name , ##arg); } while (0) + +static inline unsigned int norm_maxw(struct cx231xx *dev) +{ + if (dev->board.max_range_640_480) + return 640; + else + return 720; +} + +static inline unsigned int norm_maxh(struct cx231xx *dev) +{ + if (dev->board.max_range_640_480) + return 480; + else + return (dev->norm & V4L2_STD_625_50) ? 576 : 480; +} +#endif diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig new file mode 100644 index 000000000000..928ef0d0429f --- /dev/null +++ b/drivers/media/usb/em28xx/Kconfig @@ -0,0 +1,58 @@ +config VIDEO_EM28XX + tristate "Empia EM28xx USB video capture support" + depends on VIDEO_DEV && I2C + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEOBUF_VMALLOC + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO + + ---help--- + This is a video4linux driver for Empia 28xx based TV cards. + + To compile this driver as a module, choose M here: the + module will be called em28xx + +config VIDEO_EM28XX_ALSA + depends on VIDEO_EM28XX && SND + select SND_PCM + tristate "Empia EM28xx ALSA audio module" + ---help--- + This is an ALSA driver for some Empia 28xx based TV cards. + + This is not required for em2800/em2820/em2821 boards. However, + newer em28xx devices uses Vendor Class for audio, instead of + implementing the USB Audio Class. For those chips, this module + will enable digital audio. + + To compile this driver as a module, choose M here: the + module will be called em28xx-alsa + +config VIDEO_EM28XX_DVB + tristate "DVB/ATSC Support for em28xx based TV cards" + depends on VIDEO_EM28XX && DVB_CORE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_S921 if !DVB_FE_CUSTOMISE + select DVB_DRXD if !DVB_FE_CUSTOMISE + select DVB_CXD2820R if !DVB_FE_CUSTOMISE + select DVB_DRXK if !DVB_FE_CUSTOMISE + select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE + select DVB_TDA10071 if !DVB_FE_CUSTOMISE + select DVB_A8293 if !DVB_FE_CUSTOMISE + select VIDEOBUF_DVB + ---help--- + This adds support for DVB cards based on the + Empiatech em28xx chips. + +config VIDEO_EM28XX_RC + tristate "EM28XX Remote Controller support" + depends on RC_CORE + depends on VIDEO_EM28XX + depends on !(RC_CORE=m && VIDEO_EM28XX=y) + default VIDEO_EM28XX + ---help--- + Enables Remote Controller support on em28xx driver. diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile new file mode 100644 index 000000000000..65c7c29e4161 --- /dev/null +++ b/drivers/media/usb/em28xx/Makefile @@ -0,0 +1,15 @@ +em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o +em28xx-y += em28xx-core.o em28xx-vbi.o + +em28xx-alsa-objs := em28xx-audio.o +em28xx-rc-objs := em28xx-input.o + +obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o +obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o +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/tuners +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c new file mode 100644 index 000000000000..2fdb66ee44ab --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -0,0 +1,751 @@ +/* + * Empiatech em28x1 audio extension + * + * Copyright (C) 2006 Markus Rechberger + * + * Copyright (C) 2007-2011 Mauro Carvalho Chehab + * - Port to work with the in-kernel driver + * - Cleanups, fixes, alsa-controls, etc. + * + * This driver is based on my previous au600 usb pstn audio driver + * and inherits all the copyrights + * + * This program is free software; 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "em28xx.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_INFO "em28xx-audio %s: " fmt, \ + __func__, ##arg); \ + } while (0) + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + +static int em28xx_deinit_isoc_audio(struct em28xx *dev) +{ + int i; + + dprintk("Stopping isoc\n"); + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + if (!irqs_disabled()) + usb_kill_urb(dev->adev.urb[i]); + else + usb_unlink_urb(dev->adev.urb[i]); + + usb_free_urb(dev->adev.urb[i]); + dev->adev.urb[i] = NULL; + + kfree(dev->adev.transfer_buffer[i]); + dev->adev.transfer_buffer[i] = NULL; + } + + return 0; +} + +static void em28xx_audio_isocirq(struct urb *urb) +{ + struct em28xx *dev = urb->context; + int i; + unsigned int oldptr; + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dprintk("urb completition error %d.\n", urb->status); + break; + } + + if (atomic_read(&dev->stream_started) == 0) + return; + + if (dev->adev.capture_pcm_substream) { + substream = dev->adev.capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + int length = + urb->iso_frame_desc[i].actual_length / stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!length) + continue; + + oldptr = dev->adev.hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + + snd_pcm_stream_lock(substream); + + dev->adev.hwptr_done_capture += length; + if (dev->adev.hwptr_done_capture >= + runtime->buffer_size) + dev->adev.hwptr_done_capture -= + runtime->buffer_size; + + dev->adev.capture_transfer_done += length; + if (dev->adev.capture_transfer_done >= + runtime->period_size) { + dev->adev.capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock(substream); + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + em28xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static int em28xx_init_audio_isoc(struct em28xx *dev) +{ + int i, errCode; + const int sb_size = EM28XX_NUM_AUDIO_PACKETS * + EM28XX_AUDIO_MAX_PACKET_SIZE; + + dprintk("Starting isoc transfers\n"); + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + + dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev.transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev.transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) { + em28xx_errdev("usb_alloc_urb failed!\n"); + for (j = 0; j < i; j++) { + usb_free_urb(dev->adev.urb[j]); + kfree(dev->adev.transfer_buffer[j]); + } + return -ENOMEM; + } + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev.transfer_buffer[i]; + urb->interval = 1; + urb->complete = em28xx_audio_isocirq; + urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + + for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; + j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + EM28XX_AUDIO_MAX_PACKET_SIZE; + } + dev->adev.urb[i] = urb; + } + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); + if (errCode) { + em28xx_errdev("submit of audio urb failed\n"); + em28xx_deinit_isoc_audio(dev); + atomic_set(&dev->stream_started, 0); + return errCode; + } + + } + + return 0; +} + +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) +{ + struct snd_pcm_runtime *runtime = subs->runtime; + + dprintk("Allocating vbuffer\n"); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + + runtime->dma_bytes = size; + + return 0; +} + +static struct snd_pcm_hardware snd_em28xx_hw_capture = { + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, /* 12544/2, */ + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, /* 12544, */ +}; + +static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + dprintk("opening device and trying to acquire exclusive lock\n"); + + if (!dev) { + em28xx_err("BUG: em28xx can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + + runtime->hw = snd_em28xx_hw_capture; + if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) { + if (dev->audio_ifnum) + dev->alt = 1; + else + dev->alt = 7; + + dprintk("changing alternate number on interface %d to %d\n", + dev->audio_ifnum, dev->alt); + usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt); + + /* Sets volume, mute, etc */ + dev->mute = 0; + mutex_lock(&dev->lock); + ret = em28xx_audio_analog_set(dev); + if (ret < 0) + goto err; + + dev->adev.users++; + mutex_unlock(&dev->lock); + } + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + dev->adev.capture_pcm_substream = substream; + + return 0; +err: + mutex_unlock(&dev->lock); + + em28xx_err("Error while configuring em28xx mixer\n"); + return ret; +} + +static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + + dprintk("closing device\n"); + + dev->mute = 1; + mutex_lock(&dev->lock); + dev->adev.users--; + if (atomic_read(&dev->stream_started) > 0) { + atomic_set(&dev->stream_started, 0); + schedule_work(&dev->wq_trigger); + } + + em28xx_audio_analog_set(dev); + if (substream->runtime->dma_area) { + dprintk("freeing\n"); + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + } + mutex_unlock(&dev->lock); + + return 0; +} + +static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret; + + dprintk("Setting capture parameters\n"); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) + return ret; +#if 0 + /* TODO: set up em28xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + unsigned int channels, rate, format; + + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); +#endif + + return 0; +} + +static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Stop capture, if needed\n"); + + if (atomic_read(&dev->stream_started) > 0) { + atomic_set(&dev->stream_started, 0); + schedule_work(&dev->wq_trigger); + } + + return 0; +} + +static int snd_em28xx_prepare(struct snd_pcm_substream *substream) +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + + dev->adev.hwptr_done_capture = 0; + dev->adev.capture_transfer_done = 0; + + return 0; +} + +static void audio_trigger(struct work_struct *work) +{ + struct em28xx *dev = container_of(work, struct em28xx, wq_trigger); + + if (atomic_read(&dev->stream_started)) { + dprintk("starting capture"); + em28xx_init_audio_isoc(dev); + } else { + dprintk("stopping capture"); + em28xx_deinit_isoc_audio(dev); + } +} + +static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + int retval = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ + case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ + case SNDRV_PCM_TRIGGER_START: + atomic_set(&dev->stream_started, 1); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ + case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ + case SNDRV_PCM_TRIGGER_STOP: + atomic_set(&dev->stream_started, 0); + break; + default: + retval = -EINVAL; + } + schedule_work(&dev->wq_trigger); + return retval; +} + +static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream + *substream) +{ + unsigned long flags; + struct em28xx *dev; + snd_pcm_uframes_t hwptr_done; + + dev = snd_pcm_substream_chip(substream); + spin_lock_irqsave(&dev->adev.slock, flags); + hwptr_done = dev->adev.hwptr_done_capture; + spin_unlock_irqrestore(&dev->adev.slock, flags); + + return hwptr_done; +} + +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +/* + * AC97 volume control support + */ +static int em28xx_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 0x1f; + + return 0; +} + +static int em28xx_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct em28xx *dev = snd_kcontrol_chip(kcontrol); + u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) | + (0x1f - (value->value.integer.value[1] & 0x1f)) << 8; + int rc; + + mutex_lock(&dev->lock); + rc = em28xx_read_ac97(dev, kcontrol->private_value); + if (rc < 0) + goto err; + + val |= rc & 0x8000; /* Preserve the mute flag */ + + rc = em28xx_write_ac97(dev, kcontrol->private_value, val); + if (rc < 0) + goto err; + + dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n", + (val & 0x8000) ? "muted " : "", + 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), + val, (int)kcontrol->private_value); + +err: + mutex_unlock(&dev->lock); + return rc; +} + +static int em28xx_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct em28xx *dev = snd_kcontrol_chip(kcontrol); + int val; + + mutex_lock(&dev->lock); + val = em28xx_read_ac97(dev, kcontrol->private_value); + mutex_unlock(&dev->lock); + if (val < 0) + return val; + + dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n", + (val & 0x8000) ? "muted " : "", + 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), + val, (int)kcontrol->private_value); + + value->value.integer.value[0] = 0x1f - (val & 0x1f); + value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f); + + return 0; +} + +static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct em28xx *dev = snd_kcontrol_chip(kcontrol); + u16 val = value->value.integer.value[0]; + int rc; + + mutex_lock(&dev->lock); + rc = em28xx_read_ac97(dev, kcontrol->private_value); + if (rc < 0) + goto err; + + if (val) + rc &= 0x1f1f; + else + rc |= 0x8000; + + rc = em28xx_write_ac97(dev, kcontrol->private_value, rc); + if (rc < 0) + goto err; + + dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n", + (val & 0x8000) ? "muted " : "", + 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), + val, (int)kcontrol->private_value); + +err: + mutex_unlock(&dev->lock); + return rc; +} + +static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct em28xx *dev = snd_kcontrol_chip(kcontrol); + int val; + + mutex_lock(&dev->lock); + val = em28xx_read_ac97(dev, kcontrol->private_value); + mutex_unlock(&dev->lock); + if (val < 0) + return val; + + if (val & 0x8000) + value->value.integer.value[0] = 0; + else + value->value.integer.value[0] = 1; + + dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n", + (val & 0x8000) ? "muted " : "", + 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), + val, (int)kcontrol->private_value); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0); + +static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev, + char *name, int id) +{ + int err; + char ctl_name[44]; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new tmp; + + memset (&tmp, 0, sizeof(tmp)); + tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + tmp.private_value = id, + tmp.name = ctl_name, + + /* Add Mute Control */ + sprintf(ctl_name, "%s Switch", name); + tmp.get = em28xx_vol_get_mute; + tmp.put = em28xx_vol_put_mute; + tmp.info = snd_ctl_boolean_mono_info; + kctl = snd_ctl_new1(&tmp, dev); + err = snd_ctl_add(card, kctl); + if (err < 0) + return err; + dprintk("Added control %s for ac97 volume control 0x%04x\n", + ctl_name, id); + + memset (&tmp, 0, sizeof(tmp)); + tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + tmp.private_value = id, + tmp.name = ctl_name, + + /* Add Volume Control */ + sprintf(ctl_name, "%s Volume", name); + tmp.get = em28xx_vol_get; + tmp.put = em28xx_vol_put; + tmp.info = em28xx_vol_info; + tmp.tlv.p = em28xx_db_scale, + kctl = snd_ctl_new1(&tmp, dev); + err = snd_ctl_add(card, kctl); + if (err < 0) + return err; + dprintk("Added control %s for ac97 volume control 0x%04x\n", + ctl_name, id); + + return 0; +} + +/* + * register/unregister code and data + */ +static struct snd_pcm_ops snd_em28xx_pcm_capture = { + .open = snd_em28xx_capture_open, + .close = snd_em28xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_em28xx_hw_capture_params, + .hw_free = snd_em28xx_hw_capture_free, + .prepare = snd_em28xx_prepare, + .trigger = snd_em28xx_capture_trigger, + .pointer = snd_em28xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +static int em28xx_audio_init(struct em28xx *dev) +{ + struct em28xx_audio *adev = &dev->adev; + struct snd_pcm *pcm; + struct snd_card *card; + static int devnr; + int err; + + if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n"); + printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " + "Rechberger\n"); + printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n"); + + err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, + &card); + if (err < 0) + return err; + + spin_lock_init(&adev->slock); + err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + if (err < 0) { + snd_card_free(card); + return err; + } + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); + pcm->info_flags = 0; + pcm->private_data = dev; + strcpy(pcm->name, "Empia 28xx Capture"); + + snd_card_set_dev(card, &dev->udev->dev); + strcpy(card->driver, "Em28xx-Audio"); + strcpy(card->shortname, "Em28xx Audio"); + strcpy(card->longname, "Empia Em28xx Audio"); + + INIT_WORK(&dev->wq_trigger, audio_trigger); + + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + em28xx_cvol_new(card, dev, "Video", AC97_VIDEO); + em28xx_cvol_new(card, dev, "Line In", AC97_LINE); + em28xx_cvol_new(card, dev, "Phone", AC97_PHONE); + em28xx_cvol_new(card, dev, "Microphone", AC97_MIC); + em28xx_cvol_new(card, dev, "CD", AC97_CD); + em28xx_cvol_new(card, dev, "AUX", AC97_AUX); + em28xx_cvol_new(card, dev, "PCM", AC97_PCM); + + em28xx_cvol_new(card, dev, "Master", AC97_MASTER); + em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE); + em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO); + em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER); + em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); + } + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + adev->sndcard = card; + adev->udev = dev->udev; + + return 0; +} + +static int em28xx_audio_fini(struct em28xx *dev) +{ + if (dev == NULL) + return 0; + + if (dev->has_alsa_audio != 1) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + if (dev->adev.sndcard) { + snd_card_free(dev->adev.sndcard); + dev->adev.sndcard = NULL; + } + + return 0; +} + +static struct em28xx_ops audio_ops = { + .id = EM28XX_AUDIO, + .name = "Em28xx Audio Extension", + .init = em28xx_audio_init, + .fini = em28xx_audio_fini, +}; + +static int __init em28xx_alsa_register(void) +{ + return em28xx_register_extension(&audio_ops); +} + +static void __exit em28xx_alsa_unregister(void) +{ + em28xx_unregister_extension(&audio_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Markus Rechberger "); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("Em28xx Audio driver"); + +module_init(em28xx_alsa_register); +module_exit(em28xx_alsa_unregister); diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c new file mode 100644 index 000000000000..ca62b9981380 --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -0,0 +1,3417 @@ +/* + em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB + video capture devices + + Copyright (C) 2005 Ludovico Cavedon + Markus Rechberger + Mauro Carvalho Chehab + Sascha Sommer + + This program is free software; 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 +#include +#include +#include +#include +#include +#include +#include + +#include "em28xx.h" + +#define DRIVER_NAME "em28xx" + +static int tuner = -1; +module_param(tuner, int, 0444); +MODULE_PARM_DESC(tuner, "tuner type"); + +static unsigned int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); + +static unsigned int disable_usb_speed_check; +module_param(disable_usb_speed_check, int, 0444); +MODULE_PARM_DESC(disable_usb_speed_check, + "override min bandwidth requirement of 480M bps"); + +static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ +static unsigned long em28xx_devused; + +struct em28xx_hash_table { + unsigned long hash; + unsigned int model; + unsigned int tuner; +}; + +static void em28xx_pre_card_setup(struct em28xx *dev); + +/* + * Reset sequences for analog/digital modes + */ + +/* Reset for the most [analog] boards */ +static struct em28xx_reg_seq default_analog[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Reset for the most [digital] boards */ +static struct em28xx_reg_seq default_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 analog */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { + {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, + {0x05, 0xff, 0x10, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 digital */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { + {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0x0f, 10}, + {EM2880_R04_GPO, 0x0c, 0x0f, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 (R2) digital */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = { + {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x0c, 0x0f, 10}, + { -1, -1, -1, -1}, +}; + +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ +static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { + {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ + +/* Board - EM2870 Kworld 355u + Analog - No input analog */ + +/* Board - EM2882 Kworld 315U digital */ +static struct em28xx_reg_seq em2882_kworld_315u_digital[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM2880_R04_GPO, 0x04, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 10}, + {EM28XX_R08_GPIO, 0x7e, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = { + {EM2880_R04_GPO, 0x08, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 10}, + {EM2880_R04_GPO, 0x08, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq kworld_330u_analog[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x00, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq kworld_330u_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x08, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +/* Evga inDtube + GPIO0 - Enable digital power (s5h1409) - low to enable + GPIO1 - Enable analog power (tvp5150/emp202) - low to enable + GPIO4 - xc3028 reset + GOP3 - s5h1409 reset + */ +static struct em28xx_reg_seq evga_indtube_analog[] = { + {EM28XX_R08_GPIO, 0x79, 0xff, 60}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq evga_indtube_digital[] = { + {EM28XX_R08_GPIO, 0x7a, 0xff, 1}, + {EM2880_R04_GPO, 0x04, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + +/* + * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map: + * EM_GPIO_0 - currently unknown + * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on) + * EM_GPIO_2 - currently unknown + * EM_GPIO_3 - currently unknown + * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset) + * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset) + * EM_GPIO_6 - currently unknown + * EM_GPIO_7 - currently unknown + */ +static struct em28xx_reg_seq kworld_a340_digital[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Pinnacle Hybrid Pro eb1a:2881 */ +static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { + {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */ + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x00, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x08, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +/* eb1a:2868 Reddo DVB-C USB TV Box + GPIO4 - CU1216L NIM + Other GPIOs seems to be don't care. */ +static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xde, 0xff, 10}, + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM28XX_R08_GPIO, 0x7f, 0xff, 10}, + {EM28XX_R08_GPIO, 0x6f, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {-1, -1, -1, -1}, +}; + +/* Callback for the most boards */ +static struct em28xx_reg_seq default_tuner_gpio[] = { + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Mute/unmute */ +static struct em28xx_reg_seq compro_unmute_tv_gpio[] = { + {EM28XX_R08_GPIO, 5, 7, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq compro_unmute_svid_gpio[] = { + {EM28XX_R08_GPIO, 4, 7, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq compro_mute_gpio[] = { + {EM28XX_R08_GPIO, 6, 7, 10}, + { -1, -1, -1, -1}, +}; + +/* Terratec AV350 */ +static struct em28xx_reg_seq terratec_av350_mute_gpio[] = { + {EM28XX_R08_GPIO, 0xff, 0x7f, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq silvercrest_reg_seq[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM28XX_R08_GPIO, 0x01, 0xf7, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq vc211a_enable[] = { + {EM28XX_R08_GPIO, 0xff, 0x07, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0f, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0b, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq dikom_dk300_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x08, 0xff, 10}, + { -1, -1, -1, -1}, +}; + + +/* Reset for the most [digital] boards */ +static struct em28xx_reg_seq leadership_digital[] = { + {EM2874_R80_GPIO, 0x70, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq leadership_reset[] = { + {EM2874_R80_GPIO, 0xf0, 0xff, 10}, + {EM2874_R80_GPIO, 0xb0, 0xff, 10}, + {EM2874_R80_GPIO, 0xf0, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +/* 2013:024f PCTV nanoStick T2 290e + * GPIO_6 - demod reset + * GPIO_7 - LED + */ +static struct em28xx_reg_seq pctv_290e[] = { + {EM2874_R80_GPIO, 0x00, 0xff, 80}, + {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */ + {-1, -1, -1, -1}, +}; + +#if 0 +static struct em28xx_reg_seq terratec_h5_gpio[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xf6, 0xff, 100}, + {EM2874_R80_GPIO, 0xf2, 0xff, 50}, + {EM2874_R80_GPIO, 0xf6, 0xff, 50}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq terratec_h5_digital[] = { + {EM2874_R80_GPIO, 0xf6, 0xff, 10}, + {EM2874_R80_GPIO, 0xe6, 0xff, 100}, + {EM2874_R80_GPIO, 0xa6, 0xff, 10}, + { -1, -1, -1, -1}, +}; +#endif + +/* 2013:024f PCTV DVB-S2 Stick 460e + * GPIO_0 - POWER_ON + * GPIO_1 - BOOST + * GPIO_2 - VUV_LNB (red LED) + * GPIO_3 - EXT_12V + * GPIO_4 - INT_DEM (DEMOD GPIO_0) + * GPIO_5 - INT_LNB + * GPIO_6 - RESET_DEM + * GPIO_7 - LED (green LED) + */ +static struct em28xx_reg_seq pctv_460e[] = { + {EM2874_R80_GPIO, 0x01, 0xff, 50}, + {0x0d, 0xff, 0xff, 50}, + {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */ + {0x0d, 0x42, 0xff, 50}, + {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */ + { -1, -1, -1, -1}, +}; + +#if 0 +static struct em28xx_reg_seq hauppauge_930c_gpio[] = { + {EM2874_R80_GPIO, 0x6f, 0xff, 10}, + {EM2874_R80_GPIO, 0x4f, 0xff, 10}, /* xc5000 reset */ + {EM2874_R80_GPIO, 0x6f, 0xff, 10}, + {EM2874_R80_GPIO, 0x4f, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq hauppauge_930c_digital[] = { + {EM2874_R80_GPIO, 0xf6, 0xff, 10}, + {EM2874_R80_GPIO, 0xe6, 0xff, 100}, + {EM2874_R80_GPIO, 0xa6, 0xff, 10}, + { -1, -1, -1, -1}, +}; +#endif + +/* 1b80:e425 MaxMedia UB425-TC + * GPIO_6 - demod reset, 0=active + * GPIO_7 - LED, 0=active + */ +static struct em28xx_reg_seq maxmedia_ub425_tc[] = { + {EM2874_R80_GPIO, 0x83, 0xff, 100}, + {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */ + {-1, -1, -1, -1}, +}; + +/* 2304:0242 PCTV QuatroStick (510e) + * GPIO_2: decoder reset, 0=active + * GPIO_4: decoder suspend, 0=active + * GPIO_6: demod reset, 0=active + * GPIO_7: LED, 1=active + */ +static struct em28xx_reg_seq pctv_510e[] = { + {EM2874_R80_GPIO, 0x10, 0xff, 100}, + {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ + {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ + { -1, -1, -1, -1}, +}; + +/* 2013:0251 PCTV QuatroStick nano (520e) + * GPIO_2: decoder reset, 0=active + * GPIO_4: decoder suspend, 0=active + * GPIO_6: demod reset, 0=active + * GPIO_7: LED, 1=active + */ +static struct em28xx_reg_seq pctv_520e[] = { + {EM2874_R80_GPIO, 0x10, 0xff, 100}, + {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ + {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */ + { -1, -1, -1, -1}, +}; + +/* + * Board definitions + */ +struct em28xx_board em28xx_boards[] = { + [EM2750_BOARD_UNKNOWN] = { + .name = "EM2710/EM2750/EM2751 webcam grabber", + .xclk = EM28XX_XCLK_FREQUENCY_20MHZ, + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = silvercrest_reg_seq, + } }, + }, + [EM2800_BOARD_UNKNOWN] = { + .name = "Unknown EM2800 video grabber", + .is_em2800 = 1, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_UNKNOWN] = { + .name = "Unknown EM2750/28xx video grabber", + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, /* To enable sensor probe */ + }, + [EM2750_BOARD_DLCW_130] = { + /* Beijing Huaqi Information Digital Technology Co., Ltd */ + .name = "Huaqi DLCW-130", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = EM28XX_AMUX_VIDEO, + } }, + }, + [EM2820_BOARD_KWORLD_PVRTV2800RF] = { + .name = "Kworld PVR TV 2800 RF", + .tuner_type = TUNER_TEMIC_PAL, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_GADMEI_TVR200] = { + .name = "Gadmei TVR200", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_TERRATEC_CINERGY_250] = { + .name = "Terratec Cinergy 250 USB", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_PINNACLE_USB_2] = { + .name = "Pinnacle PCTV USB 2", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = { + .name = "Hauppauge WinTV USB 2", + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .tda9887_conf = TDA9887_PRESENT | + TDA9887_PORT1_ACTIVE | + TDA9887_PORT2_ACTIVE, + .decoder = EM28XX_TVP5150, + .has_msp34xx = 1, + .has_ir_i2c = 1, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = MSP_INPUT_DEFAULT, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART), + } }, + }, + [EM2820_BOARD_DLINK_USB_TV] = { + .name = "D-Link DUB-T210 TV Tuner", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_HERCULES_SMART_TV_USB2] = { + .name = "Hercules Smart TV USB 2.0", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = { + .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_GADMEI_UTV310] = { + .name = "Gadmei UTV310", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = { + .name = "Leadtek Winfast USB II Deluxe", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .has_ir_i2c = 1, + .tvaudio_addr = 0x58, + .tda9887_conf = TDA9887_PRESENT | + TDA9887_PORT2_ACTIVE | + TDA9887_QSS, + .decoder = EM28XX_SAA711X, + .adecoder = EM28XX_TVAUDIO, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE4, + .amux = EM28XX_AMUX_AUX, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE5, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + .radio = { + .type = EM28XX_RADIO, + .amux = EM28XX_AMUX_AUX, + } + }, + [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { + .name = "Videology 20K14XUSB USB2.0", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = EM28XX_AMUX_VIDEO, + } }, + }, + [EM2820_BOARD_SILVERCREST_WEBCAM] = { + .name = "Silvercrest Webcam 1.3mpix", + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = silvercrest_reg_seq, + } }, + }, + [EM2821_BOARD_SUPERCOMP_USB_2] = { + .name = "Supercomp USB 2.0 TV", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .tda9887_conf = TDA9887_PRESENT | + TDA9887_PORT1_ACTIVE | + TDA9887_PORT2_ACTIVE, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2821_BOARD_USBGEAR_VD204] = { + .name = "Usbgear VD204v9", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2860_BOARD_NETGMBH_CAM] = { + /* Beijing Huaqi Information Digital Technology Co., Ltd */ + .name = "NetGMBH Cam", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = EM28XX_AMUX_VIDEO, + } }, + }, + [EM2860_BOARD_TYPHOON_DVD_MAKER] = { + .name = "Typhoon DVD Maker", + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2860_BOARD_GADMEI_UTV330] = { + .name = "Gadmei UTV330", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2861_BOARD_GADMEI_UTV330PLUS] = { + .name = "Gadmei UTV330+", + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .ir_codes = RC_MAP_GADMEI_RM008Z, + .decoder = EM28XX_SAA711X, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2860_BOARD_TERRATEC_HYBRID_XS] = { + .name = "Terratec Cinergy A Hybrid XS", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2861_BOARD_KWORLD_PVRTV_300U] = { + .name = "KWorld PVRTV 300U", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { + .name = "Yakumo MovieMixer", + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = { + .name = "EM2860/TVP5150 Reference Design", + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2861_BOARD_PLEXTOR_PX_TV100U] = { + .name = "Plextor ConvertX PX-TV100U", + .tuner_type = TUNER_TNF_5335MF, + .xclk = EM28XX_XCLK_I2S_MSB_TIMING | + EM28XX_XCLK_FREQUENCY_12MHZ, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_TVP5150, + .has_msp34xx = 1, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, + } }, + }, + + /* Those boards with em2870 are DVB Only*/ + + [EM2870_BOARD_TERRATEC_XS] = { + .name = "Terratec Cinergy T XS", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + }, + [EM2870_BOARD_TERRATEC_XS_MT2060] = { + .name = "Terratec Cinergy T XS (MT2060)", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* MT2060 */ + }, + [EM2870_BOARD_KWORLD_350U] = { + .name = "Kworld 350 U DVB-T", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + }, + [EM2870_BOARD_KWORLD_355U] = { + .name = "Kworld 355 U DVB-T", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = default_tuner_gpio, + .has_dvb = 1, + .dvb_gpio = default_digital, + }, + [EM2870_BOARD_PINNACLE_PCTV_DVB] = { + .name = "Pinnacle PCTV DVB-T", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* MT2060 */ + /* djh - I have serious doubts this is right... */ + .xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_10MHZ, + }, + [EM2870_BOARD_COMPRO_VIDEOMATE] = { + .name = "Compro, VideoMate U3", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* MT2060 */ + }, + + [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = { + .name = "Terratec Hybrid XS Secam", + .has_msp34xx = 1, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = terratec_cinergy_USB_XS_FR_digital, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = terratec_cinergy_USB_XS_FR_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = terratec_cinergy_USB_XS_FR_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = terratec_cinergy_USB_XS_FR_analog, + } }, + }, + [EM2884_BOARD_TERRATEC_H5] = { + .name = "Terratec Cinergy H5", + .has_dvb = 1, +#if 0 + .tuner_type = TUNER_PHILIPS_TDA8290, + .tuner_addr = 0x41, + .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */ + .tuner_gpio = terratec_h5_gpio, +#else + .tuner_type = TUNER_ABSENT, +#endif + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = { + .name = "Hauppauge WinTV HVR 930C", + .has_dvb = 1, +#if 0 /* FIXME: Add analog support */ + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x41, + .dvb_gpio = hauppauge_930c_digital, + .tuner_gpio = hauppauge_930c_gpio, +#else + .tuner_type = TUNER_ABSENT, +#endif + .ir_codes = RC_MAP_HAUPPAUGE, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + [EM2884_BOARD_CINERGY_HTC_STICK] = { + .name = "Terratec Cinergy HTC Stick", + .has_dvb = 1, + .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, + .tuner_type = TUNER_ABSENT, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { + .name = "Hauppauge WinTV HVR 900", + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = RC_MAP_HAUPPAUGE, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = { + .name = "Hauppauge WinTV HVR 900 (R2)", + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900R2_digital, + .ir_codes = RC_MAP_HAUPPAUGE, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = { + .name = "Hauppauge WinTV HVR 850", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = RC_MAP_HAUPPAUGE, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { + .name = "Hauppauge WinTV HVR 950", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = RC_MAP_HAUPPAUGE, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { + .name = "Pinnacle PCTV HD Pro Stick", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { + .name = "AMD ATI TV Wonder HD 600", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = RC_MAP_ATI_TV_WONDER_HD_600, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2880_BOARD_TERRATEC_HYBRID_XS] = { + .name = "Terratec Hybrid XS", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = default_digital, + .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + } }, + }, + /* maybe there's a reason behind it why Terratec sells the Hybrid XS + as Prodigy XS with a different PID, let's keep it separated for now + maybe we'll need it lateron */ + [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { + .name = "Terratec Prodigy XS", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2820_BOARD_MSI_VOX_USB_2] = { + .name = "MSI VOX USB 2.0", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT | + TDA9887_PORT1_ACTIVE | + TDA9887_PORT2_ACTIVE, + .max_range_640_480 = 1, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE4, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2800_BOARD_TERRATEC_CINERGY_200] = { + .name = "Terratec Cinergy 200 USB", + .is_em2800 = 1, + .has_ir_i2c = 1, + .tuner_type = TUNER_LG_TALN, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2800_BOARD_GRABBEEX_USB2800] = { + .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", + .is_em2800 = 1, + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* capture only board */ + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2800_BOARD_VC211A] = { + .name = "Actionmaster/LinXcel/Digitus VC211A", + .is_em2800 = 1, + .tuner_type = TUNER_ABSENT, /* Capture-only board */ + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + } }, + }, + [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { + .name = "Leadtek Winfast USB II", + .is_em2800 = 1, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2800_BOARD_KWORLD_USB2800] = { + .name = "Kworld USB2800", + .is_em2800 = 1, + .tuner_type = TUNER_PHILIPS_FCV1236D, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_PINNACLE_DVC_90] = { + .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker " + "/ Kworld DVD Maker 2 / Plextor ConvertX PX-AV100U", + .tuner_type = TUNER_ABSENT, /* capture only board */ + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2800_BOARD_VGEAR_POCKETTV] = { + .name = "V-Gear PocketTV", + .is_em2800 = 1, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = { + .name = "Pixelview PlayTV Box 4 USB 2.0", + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_YMEC_TVF_5533MF, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + .aout = EM28XX_AOUT_MONO | /* I2S */ + EM28XX_AOUT_MASTER, /* Line out pin */ + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { + .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0", + .has_snapshot_button = 1, + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_YMEC_TVF_5533MF, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + .aout = EM28XX_AOUT_MONO | /* I2S */ + EM28XX_AOUT_MASTER, /* Line out pin */ + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = { + .name = "EM2860/SAA711X Reference Design", + .has_snapshot_button = 1, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + } }, + }, + + [EM2874_BOARD_LEADERSHIP_ISDBT] = { + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ, + .xclk = EM28XX_XCLK_FREQUENCY_10MHZ, + .name = "EM2874 Leadership ISDBT", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = leadership_reset, + .dvb_gpio = leadership_digital, + .has_dvb = 1, + }, + + [EM2880_BOARD_MSI_DIGIVOX_AD] = { + .name = "MSI DigiVox A/D", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, + } }, + }, + [EM2880_BOARD_MSI_DIGIVOX_AD_II] = { + .name = "MSI DigiVox A/D II", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, + } }, + }, + [EM2880_BOARD_KWORLD_DVB_305U] = { + .name = "KWorld DVB-T 305U", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2880_BOARD_KWORLD_DVB_310U] = { + .name = "KWorld DVB-T 310U", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .has_dvb = 1, + .dvb_gpio = default_digital, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + }, { /* S-video has not been tested yet */ + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + } }, + }, + [EM2882_BOARD_KWORLD_ATSC_315U] = { + .name = "KWorld ATSC 315U HDTV TV Box", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_THOMSON_DTT761X, + .tuner_gpio = em2882_kworld_315u_tuner_gpio, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .has_dvb = 1, + .dvb_gpio = em2882_kworld_315u_digital, + .ir_codes = RC_MAP_KWORLD_315U, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, + /* Analog mode - still not ready */ + /*.input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + .gpio = em2882_kworld_315u_analog, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2882_kworld_315u_analog1, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2882_kworld_315u_analog1, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + } }, */ + }, + [EM2880_BOARD_EMPIRE_DUAL_TV] = { + .name = "Empire dual TV", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .has_dvb = 1, + .dvb_gpio = default_digital, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + } }, + }, + [EM2881_BOARD_DNT_DA2_HYBRID] = { + .name = "DNT DA2 Hybrid", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + } }, + }, + [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { + .name = "Pinnacle Hybrid Pro", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = pinnacle_hybrid_pro_digital, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = pinnacle_hybrid_pro_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, + } }, + }, + [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = { + .name = "Pinnacle Hybrid Pro (330e)", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900R2_digital, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2882_BOARD_KWORLD_VS_DVBT] = { + .name = "Kworld VS-DVB-T 323UR", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = kworld_330u_digital, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .ir_codes = RC_MAP_KWORLD_315U, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2882_BOARD_TERRATEC_HYBRID_XS] = { + .name = "Terratec Cinnergy Hybrid T USB XS (em2882)", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2882_BOARD_DIKOM_DK300] = { + .name = "Dikom DK300", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = dikom_dk300_digital, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, + } }, + }, + [EM2883_BOARD_KWORLD_HYBRID_330U] = { + .name = "Kworld PlusTV HD Hybrid 330", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = kworld_330u_digital, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_EEPROM_ON_BOARD | + EM28XX_I2C_EEPROM_KEY_VALID, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = kworld_330u_analog, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = kworld_330u_analog, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = kworld_330u_analog, + } }, + }, + [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { + .name = "Compro VideoMate ForYou/Stereo", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tvaudio_addr = 0xb0, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_TVP5150, + .adecoder = EM28XX_TVAUDIO, + .mute_gpio = compro_mute_gpio, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = compro_unmute_tv_gpio, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = compro_unmute_svid_gpio, + } }, + }, + [EM2860_BOARD_KAIOMY_TVNPC_U2] = { + .name = "Kaiomy TVnPC U2", + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .tuner_gpio = default_tuner_gpio, + .ir_codes = RC_MAP_KAIOMY, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + .radio = { + .type = EM28XX_RADIO, + .amux = EM28XX_AMUX_LINE_IN, + } + }, + [EM2860_BOARD_EASYCAP] = { + .name = "Easy Cap Capture DC-60", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_IODATA_GVMVP_SZ] = { + .name = "IO-DATA GV-MVP/SZ", + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .tuner_gpio = default_tuner_gpio, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { /* Composite has not been tested yet */ + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_VIDEO, + }, { /* S-video has not been tested yet */ + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_VIDEO, + } }, + }, + [EM2860_BOARD_TERRATEC_GRABBY] = { + .name = "Terratec Grabby", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2860_BOARD_TERRATEC_AV350] = { + .name = "Terratec AV350", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_TVP5150, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .mute_gpio = terratec_av350_mute_gpio, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AUDIO_SRC_LINE, + .gpio = terratec_av350_unmute_gpio, + + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AUDIO_SRC_LINE, + .gpio = terratec_av350_unmute_gpio, + } }, + }, + + [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = { + .name = "Elgato Video Capture", + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + + [EM2882_BOARD_EVGA_INDTUBE] = { + .name = "Evga inDtube", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = evga_indtube_digital, + .ir_codes = RC_MAP_EVGA_INDTUBE, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + } }, + }, + /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 + + Infineon TUA6034) */ + [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = { + .name = "Reddo DVB-C USB TV Box", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = reddo_dvb_c_usb_box, + .has_dvb = 1, + }, + /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold + * initially as the KWorld PlusTV 340U, then as the UB435-Q. + * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */ + [EM2870_BOARD_KWORLD_A340] = { + .name = "KWorld PlusTV 340U or UB435-Q (ATSC)", + .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */ + .has_dvb = 1, + .dvb_gpio = kworld_a340_digital, + .tuner_gpio = default_tuner_gpio, + }, + /* 2013:024f PCTV nanoStick T2 290e. + * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */ + [EM28174_BOARD_PCTV_290E] = { + .name = "PCTV nanoStick T2 290e", + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_290e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, + /* 2013:024f PCTV DVB-S2 Stick 460e + * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */ + [EM28174_BOARD_PCTV_460E] = { + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .name = "PCTV DVB-S2 Stick (460e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_460e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, + /* eb1a:5006 Honestech VIDBOX NW03 + * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */ + [EM2860_BOARD_HT_VIDBOX_NW03] = { + .name = "Honestech Vidbox NW03", + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs confirming */ + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + /* 1b80:e425 MaxMedia UB425-TC + * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */ + [EM2874_BOARD_MAXMEDIA_UB425_TC] = { + .name = "MaxMedia UB425-TC", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = maxmedia_ub425_tc, + .has_dvb = 1, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + /* 2304:0242 PCTV QuatroStick (510e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + [EM2884_BOARD_PCTV_510E] = { + .name = "PCTV QuatroStick (510e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_510e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + /* 2013:0251 PCTV QuatroStick nano (520e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + [EM2884_BOARD_PCTV_520E] = { + .name = "PCTV QuatroStick nano (520e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_520e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, +}; +const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); + +/* table of devices that work with this driver */ +struct usb_device_id em28xx_id_table[] = { + { USB_DEVICE(0xeb1a, 0x2750), + .driver_info = EM2750_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2751), + .driver_info = EM2750_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2800), + .driver_info = EM2800_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2710), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2820), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2821), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2860), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2861), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2862), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2863), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2870), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2881), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2883), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2868), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2875), + .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0xe300), + .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, + { USB_DEVICE(0xeb1a, 0xe303), + .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 }, + { USB_DEVICE(0xeb1a, 0xe305), + .driver_info = EM2880_BOARD_KWORLD_DVB_305U }, + { USB_DEVICE(0xeb1a, 0xe310), + .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD }, + { USB_DEVICE(0xeb1a, 0xa313), + .driver_info = EM2882_BOARD_KWORLD_ATSC_315U }, + { USB_DEVICE(0xeb1a, 0xa316), + .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U }, + { USB_DEVICE(0xeb1a, 0xe320), + .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II }, + { USB_DEVICE(0xeb1a, 0xe323), + .driver_info = EM2882_BOARD_KWORLD_VS_DVBT }, + { USB_DEVICE(0xeb1a, 0xe350), + .driver_info = EM2870_BOARD_KWORLD_350U }, + { USB_DEVICE(0xeb1a, 0xe355), + .driver_info = EM2870_BOARD_KWORLD_355U }, + { USB_DEVICE(0xeb1a, 0x2801), + .driver_info = EM2800_BOARD_GRABBEEX_USB2800 }, + { USB_DEVICE(0xeb1a, 0xe357), + .driver_info = EM2870_BOARD_KWORLD_355U }, + { USB_DEVICE(0xeb1a, 0xe359), + .driver_info = EM2870_BOARD_KWORLD_355U }, + { USB_DEVICE(0x1b80, 0xe302), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ + { USB_DEVICE(0x1b80, 0xe304), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */ + { USB_DEVICE(0x0ccd, 0x0036), + .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, + { USB_DEVICE(0x0ccd, 0x004c), + .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR }, + { USB_DEVICE(0x0ccd, 0x004f), + .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS }, + { USB_DEVICE(0x0ccd, 0x005e), + .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS }, + { USB_DEVICE(0x0ccd, 0x0042), + .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS }, + { USB_DEVICE(0x0ccd, 0x0043), + .driver_info = EM2870_BOARD_TERRATEC_XS }, + { USB_DEVICE(0x0ccd, 0x008e), /* Cinergy HTC USB XS Rev. 1 */ + .driver_info = EM2884_BOARD_TERRATEC_H5 }, + { USB_DEVICE(0x0ccd, 0x00ac), /* Cinergy HTC USB XS Rev. 2 */ + .driver_info = EM2884_BOARD_TERRATEC_H5 }, + { USB_DEVICE(0x0ccd, 0x10a2), /* H5 Rev. 1 */ + .driver_info = EM2884_BOARD_TERRATEC_H5 }, + { USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */ + .driver_info = EM2884_BOARD_TERRATEC_H5 }, + { USB_DEVICE(0x0ccd, 0x0084), + .driver_info = EM2860_BOARD_TERRATEC_AV350 }, + { USB_DEVICE(0x0ccd, 0x0096), + .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, + { USB_DEVICE(0x0ccd, 0x10AF), + .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, + { USB_DEVICE(0x0ccd, 0x00b2), + .driver_info = EM2884_BOARD_CINERGY_HTC_STICK }, + { USB_DEVICE(0x0fd9, 0x0033), + .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE}, + { USB_DEVICE(0x185b, 0x2870), + .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE }, + { USB_DEVICE(0x185b, 0x2041), + .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU }, + { USB_DEVICE(0x2040, 0x4200), + .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 }, + { USB_DEVICE(0x2040, 0x4201), + .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 }, + { USB_DEVICE(0x2040, 0x6500), + .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 }, + { USB_DEVICE(0x2040, 0x6502), + .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 }, + { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */ + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */ + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */ + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x651f), + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, + { USB_DEVICE(0x0438, 0xb002), + .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, + { USB_DEVICE(0x2001, 0xf112), + .driver_info = EM2820_BOARD_DLINK_USB_TV }, + { USB_DEVICE(0x2304, 0x0207), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, + { USB_DEVICE(0x2304, 0x0208), + .driver_info = EM2820_BOARD_PINNACLE_USB_2 }, + { USB_DEVICE(0x2304, 0x021a), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, + { USB_DEVICE(0x2304, 0x0226), + .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E }, + { USB_DEVICE(0x2304, 0x0227), + .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO }, + { USB_DEVICE(0x0413, 0x6023), + .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII }, + { USB_DEVICE(0x093b, 0xa003), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, + { USB_DEVICE(0x093b, 0xa005), + .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, + { USB_DEVICE(0x04bb, 0x0515), + .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, + { USB_DEVICE(0xeb1a, 0x50a6), + .driver_info = EM2860_BOARD_GADMEI_UTV330 }, + { USB_DEVICE(0x1b80, 0xa340), + .driver_info = EM2870_BOARD_KWORLD_A340 }, + { USB_DEVICE(0x2013, 0x024f), + .driver_info = EM28174_BOARD_PCTV_290E }, + { USB_DEVICE(0x2013, 0x024c), + .driver_info = EM28174_BOARD_PCTV_460E }, + { USB_DEVICE(0x2040, 0x1605), + .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C }, + { USB_DEVICE(0xeb1a, 0x5006), + .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 }, + { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */ + .driver_info = EM2860_BOARD_EASYCAP }, + { USB_DEVICE(0x1b80, 0xe425), + .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC }, + { USB_DEVICE(0x2304, 0x0242), + .driver_info = EM2884_BOARD_PCTV_510E }, + { USB_DEVICE(0x2013, 0x0251), + .driver_info = EM2884_BOARD_PCTV_520E }, + { }, +}; +MODULE_DEVICE_TABLE(usb, em28xx_id_table); + +/* + * EEPROM hash table for devices with generic USB IDs + */ +static struct em28xx_hash_table em28xx_eeprom_hash[] = { + /* P/N: SA 60002070465 Tuner: TVF7533-MF */ + {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, + {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, + {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, + {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, + {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, + {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, + {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, + {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028}, +}; + +/* I2C devicelist hash table for devices with generic USB IDs */ +static struct em28xx_hash_table em28xx_i2c_hash[] = { + {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, + {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, + {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, + {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT}, + {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, + {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, + {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT}, +}; + +/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ +static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; + +static unsigned short tvp5150_addrs[] = { + 0xb8 >> 1, + 0xba >> 1, + I2C_CLIENT_END +}; + +static unsigned short msp3400_addrs[] = { + 0x80 >> 1, + 0x88 >> 1, + I2C_CLIENT_END +}; + +int em28xx_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct em28xx *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000) + return 0; + + if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET) + return 0; + + rc = em28xx_gpio_set(dev, dev->board.tuner_gpio); + + return rc; +} +EXPORT_SYMBOL_GPL(em28xx_tuner_callback); + +static inline void em28xx_set_model(struct em28xx *dev) +{ + memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board)); + + /* Those are the default values for the majority of boards + Use those values if not specified otherwise at boards entry + */ + if (!dev->board.xclk) + dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ; + + if (!dev->board.i2c_speed) + dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ; +} + + +/* FIXME: Should be replaced by a proper mt9m111 driver */ +static int em28xx_initialize_mt9m111(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, /* reset and use defaults */ + { 0x0d, 0x00, 0x00, }, + { 0x0a, 0x00, 0x21, }, + { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */ + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, ®s[i][0], 3); + + return 0; +} + + +/* FIXME: Should be replaced by a proper mt9m001 driver */ +static int em28xx_initialize_mt9m001(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, + { 0x0d, 0x00, 0x00, }, + { 0x04, 0x05, 0x00, }, /* hres = 1280 */ + { 0x03, 0x04, 0x00, }, /* vres = 1024 */ + { 0x20, 0x11, 0x00, }, + { 0x06, 0x00, 0x10, }, + { 0x2b, 0x00, 0x24, }, + { 0x2e, 0x00, 0x24, }, + { 0x35, 0x00, 0x24, }, + { 0x2d, 0x00, 0x20, }, + { 0x2c, 0x00, 0x20, }, + { 0x09, 0x0a, 0xd4, }, + { 0x35, 0x00, 0x57, }, + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, ®s[i][0], 3); + + return 0; +} + +/* HINT method: webcam I2C chips + * + * This method works for webcams with Micron sensors + */ +static int em28xx_hint_sensor(struct em28xx *dev) +{ + int rc; + char *sensor_name; + unsigned char cmd; + __be16 version_be; + u16 version; + + /* Micron sensor detection */ + dev->i2c_client.addr = 0xba >> 1; + cmd = 0; + i2c_master_send(&dev->i2c_client, &cmd, 1); + rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2); + if (rc != 2) + return -EINVAL; + + version = be16_to_cpu(version_be); + switch (version) { + case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */ + case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */ + dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; + em28xx_set_model(dev); + + sensor_name = "mt9v011"; + dev->em28xx_sensor = EM28XX_MT9V011; + dev->sensor_xres = 640; + dev->sensor_yres = 480; + /* + * FIXME: mt9v011 uses I2S speed as xtal clk - at least with + * the Silvercrest cam I have here for testing - for higher + * resolutions, a high clock cause horizontal artifacts, so we + * need to use a lower xclk frequency. + * Yet, it would be possible to adjust xclk depending on the + * desired resolution, since this affects directly the + * frame rate. + */ + dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; + dev->sensor_xtal = 4300000; + + /* probably means GRGB 16 bit bayer */ + dev->vinmode = 0x0d; + dev->vinctl = 0x00; + + break; + + case 0x143a: /* MT9M111 as found in the ECS G200 */ + dev->model = EM2750_BOARD_UNKNOWN; + em28xx_set_model(dev); + + sensor_name = "mt9m111"; + dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ; + dev->em28xx_sensor = EM28XX_MT9M111; + em28xx_initialize_mt9m111(dev); + dev->sensor_xres = 640; + dev->sensor_yres = 512; + + dev->vinmode = 0x0a; + dev->vinctl = 0x00; + + break; + + case 0x8431: + dev->model = EM2750_BOARD_UNKNOWN; + em28xx_set_model(dev); + + sensor_name = "mt9m001"; + dev->em28xx_sensor = EM28XX_MT9M001; + em28xx_initialize_mt9m001(dev); + dev->sensor_xres = 1280; + dev->sensor_yres = 1024; + + /* probably means BGGR 16 bit bayer */ + dev->vinmode = 0x0c; + dev->vinctl = 0x00; + + break; + default: + printk("Unknown Micron Sensor 0x%04x\n", version); + return -EINVAL; + } + + /* Setup webcam defaults */ + em28xx_pre_card_setup(dev); + + em28xx_errdev("Sensor is %s, using model %s entry.\n", + sensor_name, em28xx_boards[dev->model].name); + + return 0; +} + +/* Since em28xx_pre_card_setup() requires a proper dev->model, + * this won't work for boards with generic PCI IDs + */ +static void em28xx_pre_card_setup(struct em28xx *dev) +{ + /* Set the initial XCLK and I2C clock values based on the board + definition */ + em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); + if (!dev->board.is_em2800) + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); + msleep(50); + + /* request some modules */ + switch (dev->model) { + case EM2861_BOARD_PLEXTOR_PX_TV100U: + /* Sets the msp34xx I2S speed */ + dev->i2s_speed = 2048000; + break; + case EM2861_BOARD_KWORLD_PVRTV_300U: + case EM2880_BOARD_KWORLD_DVB_305U: + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d); + msleep(10); + break; + case EM2870_BOARD_COMPRO_VIDEOMATE: + /* TODO: someone can do some cleanup here... + not everything's needed */ + em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); + msleep(10); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x01); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + mdelay(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); + mdelay(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc); + mdelay(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); + mdelay(70); + break; + case EM2870_BOARD_TERRATEC_XS_MT2060: + /* this device needs some gpio writes to get the DVB-T + demod work */ + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + mdelay(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); + mdelay(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + mdelay(70); + break; + case EM2870_BOARD_PINNACLE_PCTV_DVB: + /* this device needs some gpio writes to get the + DVB-T demod work */ + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + mdelay(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); + mdelay(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + mdelay(70); + break; + case EM2820_BOARD_GADMEI_UTV310: + case EM2820_BOARD_MSI_VOX_USB_2: + /* enables audio for that devices */ + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + break; + + case EM2882_BOARD_KWORLD_ATSC_315U: + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + msleep(10); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); + msleep(10); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x08); + msleep(10); + break; + + case EM2860_BOARD_KAIOMY_TVNPC_U2: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_regs(dev, 0x0d, "\x42", 1); + em28xx_write_regs(dev, 0x08, "\xfd", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\xff", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x7f", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x6b", 1); + + break; + case EM2860_BOARD_EASYCAP: + em28xx_write_regs(dev, 0x08, "\xf8", 1); + break; + + case EM2820_BOARD_IODATA_GVMVP_SZ: + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + msleep(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + msleep(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + msleep(70); + break; + } + + em28xx_gpio_set(dev, dev->board.tuner_gpio); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + + /* Unlock device */ + em28xx_set_mode(dev, EM28XX_SUSPEND); +} + +static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) +{ + memset(ctl, 0, sizeof(*ctl)); + + ctl->fname = XC2028_DEFAULT_FIRMWARE; + ctl->max_len = 64; + ctl->mts = em28xx_boards[dev->model].mts_firmware; + + switch (dev->model) { + case EM2880_BOARD_EMPIRE_DUAL_TV: + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: + ctl->demod = XC3028_FE_ZARLINK456; + break; + case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: + ctl->demod = XC3028_FE_ZARLINK456; + break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: + case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: + ctl->demod = XC3028_FE_DEFAULT; + break; + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: + ctl->demod = XC3028_FE_DEFAULT; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + /* FIXME: Better to specify the needed IF */ + ctl->demod = XC3028_FE_DEFAULT; + break; + case EM2883_BOARD_KWORLD_HYBRID_330U: + case EM2882_BOARD_DIKOM_DK300: + case EM2882_BOARD_KWORLD_VS_DVBT: + ctl->demod = XC3028_FE_CHINA; + ctl->fname = XC2028_DEFAULT_FIRMWARE; + break; + case EM2882_BOARD_EVGA_INDTUBE: + ctl->demod = XC3028_FE_CHINA; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; + default: + ctl->demod = XC3028_FE_OREN538; + } +} + +static void em28xx_tuner_setup(struct em28xx *dev) +{ + struct tuner_setup tun_setup; + struct v4l2_frequency f; + + if (dev->tuner_type == TUNER_ABSENT) + return; + + memset(&tun_setup, 0, sizeof(tun_setup)); + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.tuner_callback = em28xx_tuner_callback; + + if (dev->board.radio.type) { + tun_setup.type = dev->board.radio.type; + tun_setup.addr = dev->board.radio_addr; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + } + + if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + } + + if (dev->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg); + } + + if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + em28xx_setup_xc3028(dev, &ctl); + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg); + } + + /* configure tuner */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 9076; /* just a magic number */ + dev->ctl_freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); +} + +static int em28xx_hint_board(struct em28xx *dev) +{ + int i; + + /* HINT method: EEPROM + * + * This method works only for boards with eeprom. + * Uses a hash of all eeprom bytes. The hash should be + * unique for a vendor/tuner pair. + * There are a high chance that tuners for different + * video standards produce different hashes. + */ + for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) { + if (dev->hash == em28xx_eeprom_hash[i].hash) { + dev->model = em28xx_eeprom_hash[i].model; + dev->tuner_type = em28xx_eeprom_hash[i].tuner; + + em28xx_errdev("Your board has no unique USB ID.\n"); + em28xx_errdev("A hint were successfully done, " + "based on eeprom hash.\n"); + em28xx_errdev("This method is not 100%% failproof.\n"); + em28xx_errdev("If the board were missdetected, " + "please email this log to:\n"); + em28xx_errdev("\tV4L Mailing List " + " \n"); + em28xx_errdev("Board detected as %s\n", + em28xx_boards[dev->model].name); + + return 0; + } + } + + /* HINT method: I2C attached devices + * + * This method works for all boards. + * Uses a hash of i2c scanned devices. + * Devices with the same i2c attached chips will + * be considered equal. + * This method is less precise than the eeprom one. + */ + + /* user did not request i2c scanning => do it now */ + if (!dev->i2c_hash) + em28xx_do_i2c_scan(dev); + + for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) { + if (dev->i2c_hash == em28xx_i2c_hash[i].hash) { + dev->model = em28xx_i2c_hash[i].model; + dev->tuner_type = em28xx_i2c_hash[i].tuner; + em28xx_errdev("Your board has no unique USB ID.\n"); + em28xx_errdev("A hint were successfully done, " + "based on i2c devicelist hash.\n"); + em28xx_errdev("This method is not 100%% failproof.\n"); + em28xx_errdev("If the board were missdetected, " + "please email this log to:\n"); + em28xx_errdev("\tV4L Mailing List " + " \n"); + em28xx_errdev("Board detected as %s\n", + em28xx_boards[dev->model].name); + + return 0; + } + } + + em28xx_errdev("Your board has no unique USB ID and thus need a " + "hint to be detected.\n"); + em28xx_errdev("You may try to use card= insmod option to " + "workaround that.\n"); + em28xx_errdev("Please send an email with this log to:\n"); + em28xx_errdev("\tV4L Mailing List \n"); + em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash); + em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash); + + em28xx_errdev("Here is a list of valid choices for the card=" + " insmod option:\n"); + for (i = 0; i < em28xx_bcount; i++) { + em28xx_errdev(" card=%d -> %s\n", + i, em28xx_boards[i].name); + } + return -1; +} + +static void em28xx_card_setup(struct em28xx *dev) +{ + /* + * If the device can be a webcam, seek for a sensor. + * If sensor is not found, then it isn't a webcam. + */ + if (dev->board.is_webcam) { + if (em28xx_hint_sensor(dev) < 0) + dev->board.is_webcam = 0; + else + dev->progressive = 1; + } + + if (!dev->board.is_webcam) { + switch (dev->model) { + case EM2820_BOARD_UNKNOWN: + case EM2800_BOARD_UNKNOWN: + /* + * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD. + * + * This occurs because they share identical USB vendor and + * product IDs. + * + * What we do here is look up the EEPROM hash of the K-WORLD + * and if it is found then we decide that we do not have + * a DIGIVOX and reset the device to the K-WORLD instead. + * + * This solution is only valid if they do not share eeprom + * hash identities which has not been determined as yet. + */ + if (em28xx_hint_board(dev) < 0) + em28xx_errdev("Board not discovered\n"); + else { + em28xx_set_model(dev); + em28xx_pre_card_setup(dev); + } + break; + default: + em28xx_set_model(dev); + } + } + + em28xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); + + dev->tuner_type = em28xx_boards[dev->model].tuner_type; + if (em28xx_boards[dev->model].tuner_addr) + dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; + + if (em28xx_boards[dev->model].tda9887_conf) + dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; + + /* request some modules */ + switch (dev->model) { + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: + { + struct tveeprom tv; +#if defined(CONFIG_MODULES) && defined(MODULE) + request_module("tveeprom"); +#endif + /* Call first TVeeprom */ + + dev->i2c_client.addr = 0xa0 >> 1; + tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata); + + dev->tuner_type = tv.tuner_type; + + if (tv.audio_processor == V4L2_IDENT_MSPX4XX) { + dev->i2s_speed = 2048000; + dev->board.has_msp34xx = 1; + } + break; + } + case EM2882_BOARD_KWORLD_ATSC_315U: + em28xx_write_reg(dev, 0x0d, 0x42); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + msleep(10); + break; + case EM2820_BOARD_KWORLD_PVRTV2800RF: + /* GPIO enables sound on KWORLD PVR TV 2800RF */ + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9); + break; + case EM2820_BOARD_UNKNOWN: + case EM2800_BOARD_UNKNOWN: + /* + * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD. + * + * This occurs because they share identical USB vendor and + * product IDs. + * + * What we do here is look up the EEPROM hash of the K-WORLD + * and if it is found then we decide that we do not have + * a DIGIVOX and reset the device to the K-WORLD instead. + * + * This solution is only valid if they do not share eeprom + * hash identities which has not been determined as yet. + */ + case EM2880_BOARD_MSI_DIGIVOX_AD: + if (!em28xx_hint_board(dev)) + em28xx_set_model(dev); + + /* In cases where we had to use a board hint, the call to + em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + so make the call now so the analog GPIOs are set properly + before probing the i2c bus. */ + em28xx_gpio_set(dev, dev->board.tuner_gpio); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + break; + +/* + * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR. + * + * This occurs because they share identical USB vendor and + * product IDs. + * + * What we do here is look up the EEPROM hash of the Dikom + * and if it is found then we decide that we do not have + * a Kworld and reset the device to the Dikom instead. + * + * This solution is only valid if they do not share eeprom + * hash identities which has not been determined as yet. + */ + case EM2882_BOARD_KWORLD_VS_DVBT: + if (!em28xx_hint_board(dev)) + em28xx_set_model(dev); + + /* In cases where we had to use a board hint, the call to + em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + so make the call now so the analog GPIOs are set properly + before probing the i2c bus. */ + em28xx_gpio_set(dev, dev->board.tuner_gpio); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + break; + } + + if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { + em28xx_errdev("\n\n"); + em28xx_errdev("The support for this board weren't " + "valid yet.\n"); + em28xx_errdev("Please send a report of having this working\n"); + em28xx_errdev("not to V4L mailing list (and/or to other " + "addresses)\n\n"); + } + + /* Allow override tuner type by a module parameter */ + if (tuner >= 0) + dev->tuner_type = tuner; + + /* request some modules */ + if (dev->board.has_msp34xx) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "msp3400", 0, msp3400_addrs); + + if (dev->board.decoder == EM28XX_SAA711X) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "saa7115_auto", 0, saa711x_addrs); + + if (dev->board.decoder == EM28XX_TVP5150) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvp5150", 0, tvp5150_addrs); + + if (dev->em28xx_sensor == EM28XX_MT9V011) { + struct mt9v011_platform_data pdata; + struct i2c_board_info mt9v011_info = { + .type = "mt9v011", + .addr = 0xba >> 1, + .platform_data = &pdata, + }; + + pdata.xtal = dev->sensor_xtal; + v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap, + &mt9v011_info, NULL); + } + + + if (dev->board.adecoder == EM28XX_TVAUDIO) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", dev->board.tvaudio_addr, NULL); + + if (dev->board.tuner_type != TUNER_ABSENT) { + int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); + + if (dev->board.radio.type) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", dev->board.radio_addr, NULL); + + if (has_demod) + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + if (dev->tuner_addr == 0) { + enum v4l2_i2c_tuner_type type = + has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", + 0, v4l2_i2c_tuner_addrs(type)); + + if (sd) + dev->tuner_addr = v4l2_i2c_subdev_addr(sd); + } else { + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", dev->tuner_addr, NULL); + } + } + + em28xx_tuner_setup(dev); +} + + +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct em28xx *dev = container_of(work, + struct em28xx, request_module_wk); + + if (dev->has_audio_class) + request_module("snd-usb-audio"); + else if (dev->has_alsa_audio) + request_module("em28xx-alsa"); + + if (dev->board.has_dvb) + request_module("em28xx-dvb"); + if (dev->board.ir_codes && !disable_ir) + request_module("em28xx-rc"); +} + +static void request_modules(struct em28xx *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} + +static void flush_request_modules(struct em28xx *dev) +{ + flush_work_sync(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#define flush_request_modules(dev) +#endif /* CONFIG_MODULES */ + +/* + * em28xx_release_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconnected or at module unload +*/ +void em28xx_release_resources(struct em28xx *dev) +{ + /*FIXME: I2C IR should be disconnected */ + + em28xx_release_analog_resources(dev); + + em28xx_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + usb_put_dev(dev->udev); + + /* Mark device as unused */ + clear_bit(dev->devno, &em28xx_devused); +}; + +/* + * em28xx_init_dev() + * allocates and inits the device structs, registers i2c bus and v4l device + */ +static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, + struct usb_interface *interface, + int minor) +{ + int retval; + + dev->udev = udev; + mutex_init(&dev->ctrl_urb_lock); + spin_lock_init(&dev->slock); + + dev->em28xx_write_regs = em28xx_write_regs; + dev->em28xx_read_reg = em28xx_read_reg; + dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; + dev->em28xx_write_regs_req = em28xx_write_regs_req; + dev->em28xx_read_reg_req = em28xx_read_reg_req; + dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; + + em28xx_set_model(dev); + + /* Set the default GPO/GPIO for legacy devices */ + dev->reg_gpo_num = EM2880_R04_GPO; + dev->reg_gpio_num = EM28XX_R08_GPIO; + + dev->wait_after_write = 5; + + /* Based on the Chip ID, set the device configuration */ + retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); + if (retval > 0) { + dev->chip_id = retval; + + switch (dev->chip_id) { + case CHIP_ID_EM2800: + em28xx_info("chip ID is em2800\n"); + break; + case CHIP_ID_EM2710: + em28xx_info("chip ID is em2710\n"); + break; + case CHIP_ID_EM2750: + em28xx_info("chip ID is em2750\n"); + break; + case CHIP_ID_EM2820: + em28xx_info("chip ID is em2820 (or em2710)\n"); + break; + case CHIP_ID_EM2840: + em28xx_info("chip ID is em2840\n"); + break; + case CHIP_ID_EM2860: + em28xx_info("chip ID is em2860\n"); + break; + case CHIP_ID_EM2870: + em28xx_info("chip ID is em2870\n"); + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2874: + em28xx_info("chip ID is em2874\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; + case CHIP_ID_EM28174: + em28xx_info("chip ID is em28174\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2883: + em28xx_info("chip ID is em2882/em2883\n"); + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2884: + em28xx_info("chip ID is em2884\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; + default: + em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + } + } + + if (dev->is_audio_only) { + retval = em28xx_audio_setup(dev); + if (retval) + return -ENODEV; + em28xx_init_extension(dev); + + return 0; + } + + /* Prepopulate cached GPO register content */ + retval = em28xx_read_reg(dev, dev->reg_gpo_num); + if (retval >= 0) + dev->reg_gpo = retval; + + em28xx_pre_card_setup(dev); + + if (!dev->board.is_em2800) { + /* Resets I2C speed */ + retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_reg failed!" + " retval [%d]\n", + __func__, retval); + return retval; + } + } + + retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (retval < 0) { + em28xx_errdev("Call to v4l2_device_register() failed!\n"); + return retval; + } + + /* register i2c bus */ + retval = em28xx_i2c_register(dev); + if (retval < 0) { + em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n", + __func__, retval); + goto unregister_dev; + } + + /* + * Default format, used for tvp5150 or saa711x output formats + */ + dev->vinmode = 0x10; + dev->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; + + /* Do board specific init and eeprom reading */ + em28xx_card_setup(dev); + + /* Configure audio */ + retval = em28xx_audio_setup(dev); + if (retval < 0) { + em28xx_errdev("%s: Error while setting audio - error [%d]!\n", + __func__, retval); + goto fail; + } + + /* wake i2c devices */ + em28xx_wake_i2c(dev); + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vbiq.active); + + if (dev->board.has_msp34xx) { + /* Send a reset to other chips via gpio */ + retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_reg - " + "msp34xx(1) failed! error [%d]\n", + __func__, retval); + goto fail; + } + msleep(3); + + retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_reg - " + "msp34xx(2) failed! error [%d]\n", + __func__, retval); + goto fail; + } + msleep(3); + } + + retval = em28xx_register_analog_devices(dev); + if (retval < 0) { + goto fail; + } + + /* Save some power by putting tuner to sleep */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + + return 0; + +fail: + em28xx_i2c_unregister(dev); + +unregister_dev: + v4l2_device_unregister(&dev->v4l2_dev); + + return retval; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +/* + * em28xx_usb_probe() + * checks for supported devices + */ +static int em28xx_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct em28xx *dev = NULL; + int retval; + bool has_audio = false, has_video = false, has_dvb = false; + int i, nr; + const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; + char *speed; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + /* Check to see next free device and mark as used */ + do { + nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); + if (nr >= EM28XX_MAXBOARDS) { + /* No free device slots */ + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + retval = -ENOMEM; + goto err_no_slot; + } + } while (test_and_set_bit(nr, &em28xx_devused)); + + /* Don't register audio interfaces */ + if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + em28xx_err(DRIVER_NAME " audio device (%04x:%04x): " + "interface %i, class %i\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting[0].desc.bInterfaceClass); + + retval = -ENODEV; + goto err; + } + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + em28xx_err(DRIVER_NAME ": out of memory!\n"); + retval = -ENOMEM; + goto err; + } + + /* compute alternate max packet sizes */ + dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) * + interface->num_altsetting, GFP_KERNEL); + if (dev->alt_max_pkt_size == NULL) { + em28xx_errdev("out of memory!\n"); + kfree(dev); + retval = -ENOMEM; + goto err; + } + + /* Get endpoints */ + for (i = 0; i < interface->num_altsetting; i++) { + int ep; + + for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { + const struct usb_endpoint_descriptor *e; + int sizedescr, size; + + e = &interface->altsetting[i].endpoint[ep].desc; + + sizedescr = le16_to_cpu(e->wMaxPacketSize); + size = sizedescr & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult(sizedescr); + + if (usb_endpoint_xfer_isoc(e) && + usb_endpoint_dir_in(e)) { + switch (e->bEndpointAddress) { + case EM28XX_EP_AUDIO: + has_audio = true; + break; + case EM28XX_EP_ANALOG: + has_video = true; + dev->alt_max_pkt_size[i] = size; + break; + case EM28XX_EP_DIGITAL: + has_dvb = true; + if (size > dev->dvb_max_pkt_size) { + dev->dvb_max_pkt_size = size; + dev->dvb_alt = i; + } + break; + } + } + } + } + + if (!(has_audio || has_video || has_dvb)) { + retval = -ENODEV; + goto err_free; + } + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + printk(KERN_INFO DRIVER_NAME + ": New device %s %s @ %s Mbps " + "(%04x:%04x, interface %d, class %d)\n", + udev->manufacturer ? udev->manufacturer : "", + udev->product ? udev->product : "", + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting->desc.bInterfaceNumber); + + if (has_audio) + printk(KERN_INFO DRIVER_NAME + ": Audio Vendor Class interface %i found\n", + ifnum); + if (has_video) + printk(KERN_INFO DRIVER_NAME + ": Video interface %i found\n", + ifnum); + if (has_dvb) + printk(KERN_INFO DRIVER_NAME + ": DVB interface %i found\n", + ifnum); + + /* + * Make sure we have 480 Mbps of bandwidth, otherwise things like + * video stream wouldn't likely work, since 12 Mbps is generally + * not enough even for most Digital TV streams. + */ + if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { + printk(DRIVER_NAME ": Device initialization failed.\n"); + printk(DRIVER_NAME ": Device must be connected to a high-speed" + " USB 2.0 port.\n"); + retval = -ENODEV; + goto err_free; + } + + snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); + dev->devno = nr; + dev->model = id->driver_info; + dev->alt = -1; + dev->is_audio_only = has_audio && !(has_video || has_dvb); + dev->has_alsa_audio = has_audio; + dev->audio_ifnum = ifnum; + + /* Checks if audio is provided by some interface */ + for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { + struct usb_interface *uif = udev->config->interface[i]; + if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + dev->has_audio_class = 1; + break; + } + } + + dev->num_alt = interface->num_altsetting; + + if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) + dev->model = card[nr]; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* allocate device struct */ + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + retval = em28xx_init_dev(dev, udev, interface, nr); + if (retval) { + goto unlock_and_free; + } + + if (has_dvb) { + /* pre-allocate DVB isoc transfer buffers */ + retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, + EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size); + if (retval) { + goto unlock_and_free; + } + } + + request_modules(dev); + + /* Should be the last thing to do, to avoid newer udev's to + open the device before fully initializing it + */ + mutex_unlock(&dev->lock); + + /* + * These extensions can be modules. If the modules are already + * loaded then we can initialise the device now, otherwise we + * will initialise it when the modules load instead. + */ + em28xx_init_extension(dev); + + return 0; + +unlock_and_free: + mutex_unlock(&dev->lock); + +err_free: + kfree(dev->alt_max_pkt_size); + kfree(dev); + +err: + clear_bit(nr, &em28xx_devused); + +err_no_slot: + usb_put_dev(udev); + return retval; +} + +/* + * em28xx_usb_disconnect() + * called when the device gets disconnected + * video device will be unregistered on v4l2_close in case it is still open + */ +static void em28xx_usb_disconnect(struct usb_interface *interface) +{ + struct em28xx *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + if (dev->is_audio_only) { + mutex_lock(&dev->lock); + em28xx_close_extension(dev); + mutex_unlock(&dev->lock); + return; + } + + em28xx_info("disconnecting %s\n", dev->vdev->name); + + flush_request_modules(dev); + + /* wait until all current v4l2 io is finished then deallocate + resources */ + mutex_lock(&dev->lock); + + v4l2_device_disconnect(&dev->v4l2_dev); + + if (dev->users) { + em28xx_warn + ("device %s is open! Deregistration and memory " + "deallocation are deferred on close.\n", + video_device_node_name(dev->vdev)); + + dev->state |= DEV_MISCONFIGURED; + em28xx_uninit_isoc(dev, dev->mode); + dev->state |= DEV_DISCONNECTED; + } else { + dev->state |= DEV_DISCONNECTED; + em28xx_release_resources(dev); + } + + /* free DVB isoc buffers */ + em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); + + mutex_unlock(&dev->lock); + + em28xx_close_extension(dev); + + if (!dev->users) { + kfree(dev->alt_max_pkt_size); + kfree(dev); + } +} + +static struct usb_driver em28xx_usb_driver = { + .name = "em28xx", + .probe = em28xx_usb_probe, + .disconnect = em28xx_usb_disconnect, + .id_table = em28xx_id_table, +}; + +module_usb_driver(em28xx_usb_driver); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c new file mode 100644 index 000000000000..bed07a6c33f8 --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -0,0 +1,1260 @@ +/* + em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices + + Copyright (C) 2005 Ludovico Cavedon + Markus Rechberger + Mauro Carvalho Chehab + Sascha Sommer + + This program is free software; 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 "em28xx.h" + +/* #define ENABLE_DEBUG_ISOC_FRAMES */ + +static unsigned int core_debug; +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); + +#define em28xx_coredbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int reg_debug; +module_param(reg_debug, int, 0644); +MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); + +#define em28xx_regdbg(fmt, arg...) do {\ + if (reg_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static int alt; +module_param(alt, int, 0644); +MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); + +static unsigned int disable_vbi; +module_param(disable_vbi, int, 0644); +MODULE_PARM_DESC(disable_vbi, "disable vbi support"); + +/* FIXME */ +#define em28xx_isocdbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +/* + * em28xx_read_reg_req() + * reads data from the usb device specifying bRequest + */ +int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, + char *buf, int len) +{ + int ret; + int pipe = usb_rcvctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if (len > URB_MAX_CTRL_SIZE) + return -EINVAL; + + if (reg_debug) { + printk(KERN_DEBUG "(pipe 0x%08x): " + "IN: %02x %02x %02x %02x %02x %02x %02x %02x ", + pipe, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8); + } + + mutex_lock(&dev->ctrl_urb_lock); + ret = usb_control_msg(dev->udev, pipe, req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0000, reg, dev->urb_buf, len, HZ); + if (ret < 0) { + if (reg_debug) + printk(" failed!\n"); + mutex_unlock(&dev->ctrl_urb_lock); + return ret; + } + + if (len) + memcpy(buf, dev->urb_buf, len); + + mutex_unlock(&dev->ctrl_urb_lock); + + if (reg_debug) { + int byte; + + printk("<<<"); + for (byte = 0; byte < len; byte++) + printk(" %02x", (unsigned char)buf[byte]); + printk("\n"); + } + + return ret; +} + +/* + * em28xx_read_reg_req() + * reads data from the usb device specifying bRequest + */ +int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg) +{ + int ret; + u8 val; + + ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1); + if (ret < 0) + return ret; + + return val; +} + +int em28xx_read_reg(struct em28xx *dev, u16 reg) +{ + return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg); +} +EXPORT_SYMBOL_GPL(em28xx_read_reg); + +/* + * em28xx_write_regs_req() + * sends data to the usb device, specifying bRequest + */ +int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, + int len) +{ + int ret; + int pipe = usb_sndctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) + return -EINVAL; + + if (reg_debug) { + int byte; + + printk(KERN_DEBUG "(pipe 0x%08x): " + "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8); + + for (byte = 0; byte < len; byte++) + printk(" %02x", (unsigned char)buf[byte]); + printk("\n"); + } + + mutex_lock(&dev->ctrl_urb_lock); + memcpy(dev->urb_buf, buf, len); + ret = usb_control_msg(dev->udev, pipe, req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0000, reg, dev->urb_buf, len, HZ); + mutex_unlock(&dev->ctrl_urb_lock); + + if (dev->wait_after_write) + msleep(dev->wait_after_write); + + return ret; +} + +int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) +{ + int rc; + + rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); + + /* Stores GPO/GPIO values at the cache, if changed + Only write values should be stored, since input on a GPIO + register will return the input bits. + Not sure what happens on reading GPO register. + */ + if (rc >= 0) { + if (reg == dev->reg_gpo_num) + dev->reg_gpo = buf[0]; + else if (reg == dev->reg_gpio_num) + dev->reg_gpio = buf[0]; + } + + return rc; +} +EXPORT_SYMBOL_GPL(em28xx_write_regs); + +/* Write a single register */ +int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) +{ + return em28xx_write_regs(dev, reg, &val, 1); +} +EXPORT_SYMBOL_GPL(em28xx_write_reg); + +/* + * em28xx_write_reg_bits() + * sets only some bits (specified by bitmask) of a register, by first reading + * the actual value + */ +int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, + u8 bitmask) +{ + int oldval; + u8 newval; + + /* Uses cache for gpo/gpio registers */ + if (reg == dev->reg_gpo_num) + oldval = dev->reg_gpo; + else if (reg == dev->reg_gpio_num) + oldval = dev->reg_gpio; + else + oldval = em28xx_read_reg(dev, reg); + + if (oldval < 0) + return oldval; + + newval = (((u8) oldval) & ~bitmask) | (val & bitmask); + + return em28xx_write_regs(dev, reg, &newval, 1); +} +EXPORT_SYMBOL_GPL(em28xx_write_reg_bits); + +/* + * em28xx_is_ac97_ready() + * Checks if ac97 is ready + */ +static int em28xx_is_ac97_ready(struct em28xx *dev) +{ + int ret, i; + + /* Wait up to 50 ms for AC97 command to complete */ + for (i = 0; i < 10; i++, msleep(5)) { + ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); + if (ret < 0) + return ret; + + if (!(ret & 0x01)) + return 0; + } + + em28xx_warn("AC97 command still being executed: not handled properly!\n"); + return -EBUSY; +} + +/* + * em28xx_read_ac97() + * write a 16 bit value to the specified AC97 address (LSB first!) + */ +int em28xx_read_ac97(struct em28xx *dev, u8 reg) +{ + int ret; + u8 addr = (reg & 0x7f) | 0x80; + u16 val; + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) + return ret; + + ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB, + (u8 *)&val, sizeof(val)); + + if (ret < 0) + return ret; + return le16_to_cpu(val); +} +EXPORT_SYMBOL_GPL(em28xx_read_ac97); + +/* + * em28xx_write_ac97() + * write a 16 bit value to the specified AC97 address (LSB first!) + */ +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) +{ + int ret; + u8 addr = reg & 0x7f; + __le16 value; + + value = cpu_to_le16(val); + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; + + ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2); + if (ret < 0) + return ret; + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_write_ac97); + +struct em28xx_vol_itable { + enum em28xx_amux mux; + u8 reg; +}; + +static struct em28xx_vol_itable inputs[] = { + { EM28XX_AMUX_VIDEO, AC97_VIDEO }, + { EM28XX_AMUX_LINE_IN, AC97_LINE }, + { EM28XX_AMUX_PHONE, AC97_PHONE }, + { EM28XX_AMUX_MIC, AC97_MIC }, + { EM28XX_AMUX_CD, AC97_CD }, + { EM28XX_AMUX_AUX, AC97_AUX }, + { EM28XX_AMUX_PCM_OUT, AC97_PCM }, +}; + +static int set_ac97_input(struct em28xx *dev) +{ + int ret, i; + enum em28xx_amux amux = dev->ctl_ainput; + + /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that + em28xx should point to LINE IN, while AC97 should use VIDEO + */ + if (amux == EM28XX_AMUX_VIDEO2) + amux = EM28XX_AMUX_VIDEO; + + /* Mute all entres but the one that were selected */ + for (i = 0; i < ARRAY_SIZE(inputs); i++) { + if (amux == inputs[i].mux) + ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808); + else + ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000); + + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + inputs[i].reg); + } + return 0; +} + +static int em28xx_set_audio_source(struct em28xx *dev) +{ + int ret; + u8 input; + + if (dev->board.is_em2800) { + if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) + input = EM2800_AUDIO_SRC_TUNER; + else + input = EM2800_AUDIO_SRC_LINE; + + ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1); + if (ret < 0) + return ret; + } + + if (dev->board.has_msp34xx) + input = EM28XX_AUDIO_SRC_TUNER; + else { + switch (dev->ctl_ainput) { + case EM28XX_AMUX_VIDEO: + input = EM28XX_AUDIO_SRC_TUNER; + break; + default: + input = EM28XX_AUDIO_SRC_LINE; + break; + } + } + + if (dev->board.mute_gpio && dev->mute) + em28xx_gpio_set(dev, dev->board.mute_gpio); + else + em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); + + ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0); + if (ret < 0) + return ret; + msleep(5); + + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + break; + default: + ret = set_ac97_input(dev); + } + + return ret; +} + +struct em28xx_vol_otable { + enum em28xx_aout mux; + u8 reg; +}; + +static const struct em28xx_vol_otable outputs[] = { + { EM28XX_AOUT_MASTER, AC97_MASTER }, + { EM28XX_AOUT_LINE, AC97_HEADPHONE }, + { EM28XX_AOUT_MONO, AC97_MASTER_MONO }, + { EM28XX_AOUT_LFE, AC97_CENTER_LFE_MASTER }, + { EM28XX_AOUT_SURR, AC97_SURROUND_MASTER }, +}; + +int em28xx_audio_analog_set(struct em28xx *dev) +{ + int ret, i; + u8 xclk; + + if (!dev->audio_mode.has_audio) + return 0; + + /* It is assumed that all devices use master volume for output. + It would be possible to use also line output. + */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + /* Mute all outputs */ + for (i = 0; i < ARRAY_SIZE(outputs); i++) { + ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000); + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + outputs[i].reg); + } + } + + xclk = dev->board.xclk & 0x7f; + if (!dev->mute) + xclk |= EM28XX_XCLK_AUDIO_UNMUTE; + + ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk); + if (ret < 0) + return ret; + msleep(10); + + /* Selects the proper audio input */ + ret = em28xx_set_audio_source(dev); + + /* Sets volume */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + int vol; + + em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200); + em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031); + em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80); + + /* LSB: left channel - both channels with the same level */ + vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8); + + /* Mute device, if needed */ + if (dev->mute) + vol |= 0x8000; + + /* Sets volume */ + for (i = 0; i < ARRAY_SIZE(outputs); i++) { + if (dev->ctl_aoutput & outputs[i].mux) + ret = em28xx_write_ac97(dev, outputs[i].reg, + vol); + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + outputs[i].reg); + } + + if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) { + int sel = ac97_return_record_select(dev->ctl_aoutput); + + /* Use the same input for both left and right + channels */ + sel |= (sel << 8); + + em28xx_write_ac97(dev, AC97_REC_SEL, sel); + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); + +int em28xx_audio_setup(struct em28xx *dev) +{ + int vid1, vid2, feat, cfg; + u32 vid; + + if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874 + || dev->chip_id == CHIP_ID_EM28174) { + /* Digital only device - don't load any alsa module */ + dev->audio_mode.has_audio = false; + dev->has_audio_class = false; + dev->has_alsa_audio = false; + return 0; + } + + dev->audio_mode.has_audio = true; + + /* See how this device is configured */ + cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); + em28xx_info("Config register raw data: 0x%02x\n", cfg); + if (cfg < 0) { + /* Register read error? */ + cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */ + } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) { + /* The device doesn't have vendor audio at all */ + dev->has_alsa_audio = false; + dev->audio_mode.has_audio = false; + return 0; + } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_3_SAMPRATES) { + em28xx_info("I2S Audio (3 sample rates)\n"); + dev->audio_mode.i2s_3rates = 1; + } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_5_SAMPRATES) { + em28xx_info("I2S Audio (5 sample rates)\n"); + dev->audio_mode.i2s_5rates = 1; + } + + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) { + /* Skip the code that does AC97 vendor detection */ + dev->audio_mode.ac97 = EM28XX_NO_AC97; + goto init_audio; + } + + dev->audio_mode.ac97 = EM28XX_AC97_OTHER; + + vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); + if (vid1 < 0) { + /* + * Device likely doesn't support AC97 + * Note: (some) em2800 devices without eeprom reports 0x91 on + * CHIPCFG register, even not having an AC97 chip + */ + em28xx_warn("AC97 chip type couldn't be determined\n"); + dev->audio_mode.ac97 = EM28XX_NO_AC97; + dev->has_alsa_audio = false; + dev->audio_mode.has_audio = false; + goto init_audio; + } + + vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2); + if (vid2 < 0) + goto init_audio; + + vid = vid1 << 16 | vid2; + + dev->audio_mode.ac97_vendor_id = vid; + em28xx_warn("AC97 vendor ID = 0x%08x\n", vid); + + feat = em28xx_read_ac97(dev, AC97_RESET); + if (feat < 0) + goto init_audio; + + dev->audio_mode.ac97_feat = feat; + em28xx_warn("AC97 features = 0x%04x\n", feat); + + /* Try to identify what audio processor we have */ + if (((vid == 0xffffffff) || (vid == 0x83847650)) && (feat == 0x6a90)) + dev->audio_mode.ac97 = EM28XX_AC97_EM202; + else if ((vid >> 8) == 0x838476) + dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL; + +init_audio: + /* Reports detected AC97 processor */ + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + em28xx_info("No AC97 audio processor\n"); + break; + case EM28XX_AC97_EM202: + em28xx_info("Empia 202 AC97 audio processor detected\n"); + break; + case EM28XX_AC97_SIGMATEL: + em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n", + dev->audio_mode.ac97_vendor_id & 0xff); + break; + case EM28XX_AC97_OTHER: + em28xx_warn("Unknown AC97 audio processor detected!\n"); + break; + default: + break; + } + + return em28xx_audio_analog_set(dev); +} +EXPORT_SYMBOL_GPL(em28xx_audio_setup); + +int em28xx_colorlevels_set_default(struct em28xx *dev) +{ + em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */ + em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */ + em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */ + em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00); + + em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20); + em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); + return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); +} + +int em28xx_capture_start(struct em28xx *dev, int start) +{ + int rc; + + if (dev->chip_id == CHIP_ID_EM2874 || + dev->chip_id == CHIP_ID_EM2884 || + dev->chip_id == CHIP_ID_EM28174) { + /* The Transport Stream Enable Register moved in em2874 */ + if (!start) { + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + 0x00, + EM2874_TS1_CAPTURE_ENABLE); + return rc; + } + + /* Enable Transport Stream */ + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + EM2874_TS1_CAPTURE_ENABLE, + EM2874_TS1_CAPTURE_ENABLE); + return rc; + } + + + /* FIXME: which is the best order? */ + /* video registers are sampled by VREF */ + rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, + start ? 0x10 : 0x00, 0x10); + if (rc < 0) + return rc; + + if (!start) { + /* disable video capture */ + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); + return rc; + } + + if (dev->board.is_webcam) + rc = em28xx_write_reg(dev, 0x13, 0x0c); + + /* enable video capture */ + rc = em28xx_write_reg(dev, 0x48, 0x00); + + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); + else + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); + + msleep(6); + + return rc; +} + +int em28xx_vbi_supported(struct em28xx *dev) +{ + /* Modprobe option to manually disable */ + if (disable_vbi == 1) + return 0; + + if (dev->chip_id == CHIP_ID_EM2860 || + dev->chip_id == CHIP_ID_EM2883) + return 1; + + /* Version of em28xx that does not support VBI */ + return 0; +} + +int em28xx_set_outfmt(struct em28xx *dev) +{ + int ret; + u8 vinctrl; + + ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, + dev->format->reg | 0x20, 0xff); + if (ret < 0) + return ret; + + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); + if (ret < 0) + return ret; + + vinctrl = dev->vinctl; + if (em28xx_vbi_supported(dev) == 1) { + vinctrl |= EM28XX_VINCTRL_VBI_RAW; + em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height); + if (dev->norm & V4L2_STD_525_60) { + /* NTSC */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + } else if (dev->norm & V4L2_STD_625_50) { + /* PAL */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); + } + } + + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); +} + +static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, + u8 ymin, u8 ymax) +{ + em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", + xmin, ymin, xmax, ymax); + + em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); + em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); + em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); + return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); +} + +static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, + u16 width, u16 height) +{ + u8 cwidth = width; + u8 cheight = height; + u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01); + + em28xx_coredbg("em28xx Area Set: (%d,%d)\n", + (width | (overflow & 2) << 7), + (height | (overflow & 1) << 8)); + + em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); + em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); + em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); + em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); + return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); +} + +static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) +{ + u8 mode; + /* the em2800 scaler only supports scaling down to 50% */ + + if (dev->board.is_em2800) { + mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); + } else { + u8 buf[2]; + + buf[0] = h; + buf[1] = h >> 8; + em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); + + buf[0] = v; + buf[1] = v >> 8; + em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); + /* it seems that both H and V scalers must be active + to work correctly */ + mode = (h || v) ? 0x30 : 0x00; + } + return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); +} + +/* FIXME: this only function read values from dev */ +int em28xx_resolution_set(struct em28xx *dev) +{ + int width, height; + width = norm_maxw(dev); + height = norm_maxh(dev); + + /* Properly setup VBI */ + dev->vbi_width = 720; + if (dev->norm & V4L2_STD_525_60) + dev->vbi_height = 12; + else + dev->vbi_height = 18; + + em28xx_set_outfmt(dev); + + em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); + + /* If we don't set the start position to 2 in VBI mode, we end up + with line 20/21 being YUYV encoded instead of being in 8-bit + greyscale. The core of the issue is that line 21 (and line 23 for + PAL WSS) are inside of active video region, and as a result they + get the pixelformatting associated with that area. So by cropping + it out, we end up with the same format as the rest of the VBI + region */ + if (em28xx_vbi_supported(dev) == 1) + em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2); + else + em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); + + return em28xx_scaler_set(dev, dev->hscale, dev->vscale); +} + +int em28xx_set_alternate(struct em28xx *dev) +{ + int errCode, prev_alt = dev->alt; + int i; + unsigned int min_pkt_size = dev->width * 2 + 4; + + /* + * alt = 0 is used only for control messages, so, only values + * greater than 0 can be used for streaming. + */ + if (alt && alt < dev->num_alt) { + em28xx_coredbg("alternate forced to %d\n", dev->alt); + dev->alt = alt; + goto set_alt; + } + + /* When image size is bigger than a certain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (dev->width * 2 * dev->height > 720 * 240 * 2) + min_pkt_size *= 2; + + for (i = 0; i < dev->num_alt; i++) { + /* stop when the selected alt setting offers enough bandwidth */ + if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + dev->alt = i; + break; + /* otherwise make sure that we end up with the maximum bandwidth + because the min_pkt_size equation might be wrong... + */ + } else if (dev->alt_max_pkt_size[i] > + dev->alt_max_pkt_size[dev->alt]) + dev->alt = i; + } + +set_alt: + if (dev->alt != prev_alt) { + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; + em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->alt, dev->max_pkt_size); + errCode = usb_set_interface(dev->udev, 0, dev->alt); + if (errCode < 0) { + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", + dev->alt, errCode); + return errCode; + } + } + return 0; +} + +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) +{ + int rc = 0; + + if (!gpio) + return rc; + + if (dev->mode != EM28XX_SUSPEND) { + em28xx_write_reg(dev, 0x48, 0x00); + if (dev->mode == EM28XX_ANALOG_MODE) + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); + else + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); + msleep(6); + } + + /* Send GPIO reset sequences specified at board entry */ + while (gpio->sleep >= 0) { + if (gpio->reg >= 0) { + rc = em28xx_write_reg_bits(dev, + gpio->reg, + gpio->val, + gpio->mask); + if (rc < 0) + return rc; + } + if (gpio->sleep > 0) + msleep(gpio->sleep); + + gpio++; + } + return rc; +} +EXPORT_SYMBOL_GPL(em28xx_gpio_set); + +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) +{ + if (dev->mode == set_mode) + return 0; + + if (set_mode == EM28XX_SUSPEND) { + dev->mode = set_mode; + + /* FIXME: add suspend support for ac97 */ + + return em28xx_gpio_set(dev, dev->board.suspend_gpio); + } + + dev->mode = set_mode; + + if (dev->mode == EM28XX_DIGITAL_MODE) + return em28xx_gpio_set(dev, dev->board.dvb_gpio); + else + return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); +} +EXPORT_SYMBOL_GPL(em28xx_set_mode); + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +static void em28xx_irq_callback(struct urb *urb) +{ + struct em28xx *dev = urb->context; + int i; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + em28xx_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->slock); + dev->isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + em28xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) +{ + struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs; + int i; + + em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; + + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (isoc_bufs->transfer_buffer[i]) { + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + isoc_bufs->transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + isoc_bufs->urb[i] = NULL; + } + isoc_bufs->transfer_buffer[i] = NULL; + } + + kfree(isoc_bufs->urb); + kfree(isoc_bufs->transfer_buffer); + + isoc_bufs->urb = NULL; + isoc_bufs->transfer_buffer = NULL; + isoc_bufs->num_bufs = 0; + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); + +/* + * Stop URBs + */ +void em28xx_stop_urbs(struct em28xx *dev) +{ + int i; + struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + + em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); + + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + } + } + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_stop_urbs); + +/* + * Allocate URBs + */ +int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size) +{ + struct em28xx_usb_isoc_bufs *isoc_bufs; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + + em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; + + /* De-allocates all pending stuff */ + em28xx_uninit_isoc(dev, mode); + + isoc_bufs->num_bufs = num_bufs; + + isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!isoc_bufs->urb) { + em28xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!isoc_bufs->transfer_buffer) { + em28xx_errdev("cannot allocate memory for usb transfer\n"); + kfree(isoc_bufs->urb); + return -ENOMEM; + } + + isoc_bufs->max_pkt_size = max_pkt_size; + isoc_bufs->num_packets = max_packets; + dev->isoc_ctl.vid_buf = NULL; + dev->isoc_ctl.vbi_buf = NULL; + + sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); + if (!urb) { + em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); + em28xx_uninit_isoc(dev, mode); + return -ENOMEM; + } + isoc_bufs->urb[i] = urb; + + isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!isoc_bufs->transfer_buffer[i]) { + em28xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + em28xx_uninit_isoc(dev, mode); + return -ENOMEM; + } + memset(isoc_bufs->transfer_buffer[i], 0, sb_size); + + /* FIXME: this is a hack - should be + 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' + should also be using 'desc.bInterval' + */ + pipe = usb_rcvisocpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); + + usb_fill_int_urb(urb, dev->udev, pipe, + isoc_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev, 1); + + urb->number_of_packets = isoc_bufs->num_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + k = 0; + for (j = 0; j < isoc_bufs->num_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + isoc_bufs->max_pkt_size; + k += isoc_bufs->max_pkt_size; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); + +/* + * Allocate URBs and start IRQ + */ +int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +{ + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + struct em28xx_usb_isoc_bufs *isoc_bufs; + int i; + int rc; + int alloc; + + em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + + dev->isoc_ctl.isoc_copy = isoc_copy; + + if (mode == EM28XX_DIGITAL_MODE) { + isoc_bufs = &dev->isoc_ctl.digital_bufs; + /* no need to free/alloc isoc buffers in digital mode */ + alloc = 0; + } else { + isoc_bufs = &dev->isoc_ctl.analog_bufs; + alloc = 1; + } + + if (alloc) { + rc = em28xx_alloc_isoc(dev, mode, max_packets, + num_bufs, max_pkt_size); + if (rc) + return rc; + } + + init_waitqueue_head(&dma_q->wq); + init_waitqueue_head(&vbi_dma_q->wq); + + em28xx_capture_start(dev, 1); + + /* submit urbs and enables IRQ */ + for (i = 0; i < isoc_bufs->num_bufs; i++) { + rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); + if (rc) { + em28xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + em28xx_uninit_isoc(dev, mode); + return rc; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_init_isoc); + +/* + * em28xx_wake_i2c() + * configure i2c attached devices + */ +void em28xx_wake_i2c(struct em28xx *dev) +{ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + INPUT(dev->ctl_input)->vmux, 0, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); +} + +/* + * Device control list + */ + +static LIST_HEAD(em28xx_devlist); +static DEFINE_MUTEX(em28xx_devlist_mutex); + +/* + * Extension interface + */ + +static LIST_HEAD(em28xx_extension_devlist); + +int em28xx_register_extension(struct em28xx_ops *ops) +{ + struct em28xx *dev = NULL; + + mutex_lock(&em28xx_devlist_mutex); + list_add_tail(&ops->next, &em28xx_extension_devlist); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + ops->init(dev); + } + mutex_unlock(&em28xx_devlist_mutex); + printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); + return 0; +} +EXPORT_SYMBOL(em28xx_register_extension); + +void em28xx_unregister_extension(struct em28xx_ops *ops) +{ + struct em28xx *dev = NULL; + + mutex_lock(&em28xx_devlist_mutex); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + ops->fini(dev); + } + list_del(&ops->next); + mutex_unlock(&em28xx_devlist_mutex); + printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); +} +EXPORT_SYMBOL(em28xx_unregister_extension); + +void em28xx_init_extension(struct em28xx *dev) +{ + const struct em28xx_ops *ops = NULL; + + mutex_lock(&em28xx_devlist_mutex); + list_add_tail(&dev->devlist, &em28xx_devlist); + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + mutex_unlock(&em28xx_devlist_mutex); +} + +void em28xx_close_extension(struct em28xx *dev) +{ + const struct em28xx_ops *ops = NULL; + + mutex_lock(&em28xx_devlist_mutex); + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + list_del(&dev->devlist); + mutex_unlock(&em28xx_devlist_mutex); +} diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c new file mode 100644 index 000000000000..a16531fa937a --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -0,0 +1,1197 @@ +/* + DVB device driver for em28xx + + (c) 2008-2011 Mauro Carvalho Chehab + + (c) 2008 Devin Heitmueller + - Fixes for the driver to properly work with HVR-950 + - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick + - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600 + + (c) 2008 Aidan Thornton + + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: + (c) 2004, 2005 Chris Pascoe + (c) 2004 Gerd Knorr [SuSE Labs] + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + */ + +#include +#include +#include + +#include "em28xx.h" +#include +#include +#include +#include "tuner-simple.h" + +#include "lgdt330x.h" +#include "lgdt3305.h" +#include "zl10353.h" +#include "s5h1409.h" +#include "mt352.h" +#include "mt352_priv.h" /* FIXME */ +#include "tda1002x.h" +#include "tda18271.h" +#include "s921.h" +#include "drxd.h" +#include "cxd2820r.h" +#include "tda18271c2dd.h" +#include "drxk.h" +#include "tda10071.h" +#include "a8293.h" +#include "qt1010.h" + +MODULE_DESCRIPTION("driver for em28xx based DVB cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ +} while (0) + +struct em28xx_dvb { + struct dvb_frontend *fe[2]; + + /* feed count management */ + struct mutex lock; + int nfeeds; + + /* general boilerplate stuff */ + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; + + /* Due to DRX-K - probably need changes */ + int (*gate_ctrl)(struct dvb_frontend *, int); + struct semaphore pll_mutex; + bool dont_attach_fe1; +}; + + +static inline void print_err_status(struct em28xx *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(1, "URB status %d [%s].\n", status, errmsg); + } else { + dprintk(1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +{ + int i; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + + return 0; +} + +static int em28xx_start_streaming(struct em28xx_dvb *dvb) +{ + int rc; + struct em28xx *dev = dvb->adapter.priv; + int max_dvb_packet_size; + + usb_set_interface(dev->udev, 0, dev->dvb_alt); + rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + if (rc < 0) + return rc; + + max_dvb_packet_size = dev->dvb_max_pkt_size; + if (max_dvb_packet_size < 0) + return max_dvb_packet_size; + dprintk(1, "Using %d buffers each with %d x %d bytes\n", + EM28XX_DVB_NUM_BUFS, + EM28XX_DVB_MAX_PACKETS, + max_dvb_packet_size); + + return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, + EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, + max_dvb_packet_size, em28xx_dvb_isoc_copy); +} + +static int em28xx_stop_streaming(struct em28xx_dvb *dvb) +{ + struct em28xx *dev = dvb->adapter.priv; + + em28xx_stop_urbs(dev); + + em28xx_set_mode(dev, EM28XX_SUSPEND); + + return 0; +} + +static int em28xx_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int rc, ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = em28xx_start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->lock); + return rc; +} + +static int em28xx_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + + if (0 == dvb->nfeeds) + err = em28xx_stop_streaming(dvb); + + mutex_unlock(&dvb->lock); + return err; +} + + + +/* ------------------------------------------------------------------ */ +static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct em28xx *dev = fe->dvb->priv; + + if (acquire) + return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + else + return em28xx_set_mode(dev, EM28XX_SUSPEND); +} + +/* ------------------------------------------------------------------ */ + +static struct lgdt330x_config em2880_lgdt3303_dev = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, +}; + +static struct lgdt3305_config em2870_lgdt3304_dev = { + .i2c_addr = 0x0e, + .demod_chip = LGDT3304, + .spectral_inversion = 1, + .deny_i2c_rptr = 1, + .mpeg_mode = LGDT3305_MPEG_PARALLEL, + .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .vsb_if_khz = 3250, + .qam_if_khz = 4000, +}; + +static struct s921_config sharp_isdbt = { + .demod_address = 0x30 >> 1 +}; + +static struct zl10353_config em28xx_zl10353_with_xc3028 = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45600, +}; + +static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_PARALLEL_OUTPUT, + .gpio = S5H1409_GPIO_OFF, + .inversion = S5H1409_INVERSION_OFF, + .status_mode = S5H1409_DEMODLOCKING, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK +}; + +static struct tda18271_std_map kworld_a340_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0, + .if_lvl = 1, .rfagc_top = 0x37, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config kworld_a340_config = { + .std_map = &kworld_a340_std_map, +}; + +static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .disable_i2c_gate_ctrl = 1, + .parallel_ts = 1, + .if2 = 45600, +}; + +static struct drxd_config em28xx_drxd = { + .demod_address = 0x70, + .demod_revision = 0xa2, + .pll_type = DRXD_PLL_NONE, + .clock = 12000, + .insert_rs_byte = 1, + .IF = 42800000, + .disable_i2c_gate_ctrl = 1, +}; + +static struct drxk_config terratec_h5_drxk = { + .adr = 0x29, + .single_master = 1, + .no_i2c_bridge = 1, + .microcode_name = "dvb-usb-terratec-h5-drxk.fw", + .qam_demod_parameter_count = 2, +}; + +static struct drxk_config hauppauge_930c_drxk = { + .adr = 0x29, + .single_master = 1, + .no_i2c_bridge = 1, + .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw", + .chunk_size = 56, + .qam_demod_parameter_count = 2, +}; + +struct drxk_config terratec_htc_stick_drxk = { + .adr = 0x29, + .single_master = 1, + .no_i2c_bridge = 1, + .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw", + .chunk_size = 54, + .qam_demod_parameter_count = 2, + /* Required for the antenna_gpio to disable LNA. */ + .antenna_dvbt = true, + /* The windows driver uses the same. This will disable LNA. */ + .antenna_gpio = 0x6, +}; + +static struct drxk_config maxmedia_ub425_tc_drxk = { + .adr = 0x29, + .single_master = 1, + .no_i2c_bridge = 1, +}; + +static struct drxk_config pctv_520e_drxk = { + .adr = 0x29, + .single_master = 1, + .microcode_name = "dvb-demod-drxk-pctv.fw", + .qam_demod_parameter_count = 2, + .chunk_size = 58, + .antenna_dvbt = true, /* disable LNA */ + .antenna_gpio = (1 << 2), /* disable LNA */ +}; + +static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct em28xx_dvb *dvb = fe->sec_priv; + int status; + + if (!dvb) + return -EINVAL; + + if (enable) { + down(&dvb->pll_mutex); + status = dvb->gate_ctrl(fe, 1); + } else { + status = dvb->gate_ctrl(fe, 0); + up(&dvb->pll_mutex); + } + return status; +} + +static void hauppauge_hvr930c_init(struct em28xx *dev) +{ + int i; + + struct em28xx_reg_seq hauppauge_hvr930c_init[] = { + {EM2874_R80_GPIO, 0xff, 0xff, 0x65}, + {EM2874_R80_GPIO, 0xfb, 0xff, 0x32}, + {EM2874_R80_GPIO, 0xff, 0xff, 0xb8}, + { -1, -1, -1, -1}, + }; + struct em28xx_reg_seq hauppauge_hvr930c_end[] = { + {EM2874_R80_GPIO, 0xef, 0xff, 0x01}, + {EM2874_R80_GPIO, 0xaf, 0xff, 0x65}, + {EM2874_R80_GPIO, 0xef, 0xff, 0x76}, + {EM2874_R80_GPIO, 0xef, 0xff, 0x01}, + {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b}, + {EM2874_R80_GPIO, 0xef, 0xff, 0x40}, + + {EM2874_R80_GPIO, 0xcf, 0xff, 0x65}, + {EM2874_R80_GPIO, 0xef, 0xff, 0x65}, + {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b}, + {EM2874_R80_GPIO, 0xef, 0xff, 0x65}, + + { -1, -1, -1, -1}, + }; + + struct { + unsigned char r[4]; + int len; + } regs[] = { + {{ 0x06, 0x02, 0x00, 0x31 }, 4}, + {{ 0x01, 0x02 }, 2}, + {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0xff, 0xaf }, 4}, + {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0x73, 0xaf }, 4}, + {{ 0x04, 0x00 }, 2}, + {{ 0x00, 0x04 }, 2}, + {{ 0x00, 0x04, 0x00, 0x0a }, 4}, + {{ 0x04, 0x14 }, 2}, + {{ 0x04, 0x14, 0x00, 0x00 }, 4}, + }; + + em28xx_gpio_set(dev, hauppauge_hvr930c_init); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); + msleep(10); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); + msleep(10); + + dev->i2c_client.addr = 0x82 >> 1; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + em28xx_gpio_set(dev, hauppauge_hvr930c_end); + + msleep(100); + + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); + msleep(30); + + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); + msleep(10); + +} + +static void terratec_h5_init(struct em28xx *dev) +{ + int i; + struct em28xx_reg_seq terratec_h5_init[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xf6, 0xff, 100}, + {EM2874_R80_GPIO, 0xf2, 0xff, 50}, + {EM2874_R80_GPIO, 0xf6, 0xff, 100}, + { -1, -1, -1, -1}, + }; + struct em28xx_reg_seq terratec_h5_end[] = { + {EM2874_R80_GPIO, 0xe6, 0xff, 100}, + {EM2874_R80_GPIO, 0xa6, 0xff, 50}, + {EM2874_R80_GPIO, 0xe6, 0xff, 100}, + { -1, -1, -1, -1}, + }; + struct { + unsigned char r[4]; + int len; + } regs[] = { + {{ 0x06, 0x02, 0x00, 0x31 }, 4}, + {{ 0x01, 0x02 }, 2}, + {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0xff, 0xaf }, 4}, + {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0x73, 0xaf }, 4}, + {{ 0x04, 0x00 }, 2}, + {{ 0x00, 0x04 }, 2}, + {{ 0x00, 0x04, 0x00, 0x0a }, 4}, + {{ 0x04, 0x14 }, 2}, + {{ 0x04, 0x14, 0x00, 0x00 }, 4}, + }; + + em28xx_gpio_set(dev, terratec_h5_init); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); + msleep(10); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); + msleep(10); + + dev->i2c_client.addr = 0x82 >> 1; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + em28xx_gpio_set(dev, terratec_h5_end); +}; + +static void terratec_htc_stick_init(struct em28xx *dev) +{ + int i; + + /* + * GPIO configuration: + * 0xff: unknown (does not affect DVB-T). + * 0xf6: DRX-K (demodulator). + * 0xe6: unknown (does not affect DVB-T). + * 0xb6: unknown (does not affect DVB-T). + */ + struct em28xx_reg_seq terratec_htc_stick_init[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xf6, 0xff, 100}, + {EM2874_R80_GPIO, 0xe6, 0xff, 50}, + {EM2874_R80_GPIO, 0xf6, 0xff, 100}, + { -1, -1, -1, -1}, + }; + struct em28xx_reg_seq terratec_htc_stick_end[] = { + {EM2874_R80_GPIO, 0xb6, 0xff, 100}, + {EM2874_R80_GPIO, 0xf6, 0xff, 50}, + { -1, -1, -1, -1}, + }; + + /* Init the analog decoder? */ + struct { + unsigned char r[4]; + int len; + } regs[] = { + {{ 0x06, 0x02, 0x00, 0x31 }, 4}, + {{ 0x01, 0x02 }, 2}, + {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0xff, 0xaf }, 4}, + }; + + em28xx_gpio_set(dev, terratec_htc_stick_init); + + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); + msleep(10); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); + msleep(10); + + dev->i2c_client.addr = 0x82 >> 1; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + + em28xx_gpio_set(dev, terratec_htc_stick_end); +}; + +static void pctv_520e_init(struct em28xx *dev) +{ + /* + * Init AVF4910B analog decoder. Looks like I2C traffic to + * digital demodulator and tuner are routed via AVF4910B. + */ + int i; + struct { + unsigned char r[4]; + int len; + } regs[] = { + {{ 0x06, 0x02, 0x00, 0x31 }, 4}, + {{ 0x01, 0x02 }, 2}, + {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0xff, 0xaf }, 4}, + {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0x73, 0xaf }, 4}, + }; + + dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */ + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); +}; + +static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) +{ + /* Values extracted from a USB trace of the Terratec Windows driver */ + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 }; + static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 }; + static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 }; + static u8 tuner_go[] = { TUNER_GO, 0x01}; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg)); + mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg)); + mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg)); + mt352_write(fe, tuner_go, sizeof(tuner_go)); + return 0; +} + +static struct mt352_config terratec_xs_mt352_cfg = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, + .demod_init = em28xx_mt352_terratec_xs_init, +}; + +static struct tda10023_config em28xx_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static struct cxd2820r_config em28xx_cxd2820r_config = { + .i2c_address = (0xd8 >> 1), + .ts_mode = CXD2820R_TS_SERIAL, + + /* enable LNA for DVB-T, DVB-T2 and DVB-C */ + .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, + .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, + .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, +}; + +static struct tda18271_config em28xx_cxd2820r_tda18271_config = { + .output_opt = TDA18271_OUTPUT_LT_OFF, + .gate = TDA18271_GATE_DIGITAL, +}; + +static const struct tda10071_config em28xx_tda10071_config = { + .i2c_address = 0x55, /* (0xaa >> 1) */ + .i2c_wr_max = 64, + .ts_mode = TDA10071_TS_SERIAL, + .spec_inv = 0, + .xtal = 40444000, /* 40.444 MHz */ + .pll_multiplier = 20, +}; + +static const struct a8293_config em28xx_a8293_config = { + .i2c_addr = 0x08, /* (0x10 >> 1) */ +}; + +static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = { + .demod_address = (0x1e >> 1), + .disable_i2c_gate_ctrl = 1, + .no_tuner = 1, + .parallel_ts = 1, +}; +static struct qt1010_config em28xx_qt1010_config = { + .i2c_address = 0x62 + +}; + +/* ------------------------------------------------------------------ */ + +static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) +{ + struct dvb_frontend *fe; + struct xc2028_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_adap = &dev->i2c_adap; + cfg.i2c_addr = addr; + + if (!dev->dvb->fe[0]) { + em28xx_errdev("/2: dvb frontend not attached. " + "Can't attach xc3028\n"); + return -EINVAL; + } + + fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg); + if (!fe) { + em28xx_errdev("/2: xc3028 attach failed\n"); + dvb_frontend_detach(dev->dvb->fe[0]); + dev->dvb->fe[0] = NULL; + return -EINVAL; + } + + em28xx_info("%s/2: xc3028 attached\n", dev->name); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module, + struct em28xx *dev, struct device *device) +{ + int result; + + mutex_init(&dvb->lock); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, + adapter_nr); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", + dev->name, result); + goto fail_adapter; + } + + /* Ensure all frontends negotiate bus access */ + dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; + if (dvb->fe[1]) + dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; + + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend0; + } + + /* register 2nd frontend */ + if (dvb->fe[1]) { + result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]); + if (result < 0) { + printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend1; + } + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = em28xx_start_feed; + dvb->demux.stop_feed = em28xx_stop_feed; + + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dev->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + if (dvb->fe[1]) + dvb_unregister_frontend(dvb->fe[1]); + dvb_unregister_frontend(dvb->fe[0]); +fail_frontend1: + if (dvb->fe[1]) + dvb_frontend_detach(dvb->fe[1]); +fail_frontend0: + dvb_frontend_detach(dvb->fe[0]); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) +{ + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + if (dvb->fe[1]) + dvb_unregister_frontend(dvb->fe[1]); + dvb_unregister_frontend(dvb->fe[0]); + if (dvb->fe[1] && !dvb->dont_attach_fe1) + dvb_frontend_detach(dvb->fe[1]); + dvb_frontend_detach(dvb->fe[0]); + dvb_unregister_adapter(&dvb->adapter); +} + +static int em28xx_dvb_init(struct em28xx *dev) +{ + int result = 0, mfe_shared = 0; + struct em28xx_dvb *dvb; + + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + printk(KERN_INFO "em28xx_dvb: This device does not support the extension\n"); + return 0; + } + + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); + + if (dvb == NULL) { + em28xx_info("em28xx_dvb: memory allocation failed\n"); + return -ENOMEM; + } + dev->dvb = dvb; + dvb->fe[0] = dvb->fe[1] = NULL; + + mutex_lock(&dev->lock); + em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + /* init frontend */ + switch (dev->model) { + case EM2874_BOARD_LEADERSHIP_ISDBT: + dvb->fe[0] = dvb_attach(s921_attach, + &sharp_isdbt, &dev->i2c_adap); + + if (!dvb->fe[0]) { + result = -EINVAL; + goto out_free; + } + + break; + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: + dvb->fe[0] = dvb_attach(lgdt330x_attach, + &em2880_lgdt3303_dev, + &dev->i2c_adap); + if (em28xx_attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2880_BOARD_KWORLD_DVB_310U: + dvb->fe[0] = dvb_attach(zl10353_attach, + &em28xx_zl10353_with_xc3028, + &dev->i2c_adap); + if (em28xx_attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: + case EM2880_BOARD_EMPIRE_DUAL_TV: + dvb->fe[0] = dvb_attach(zl10353_attach, + &em28xx_zl10353_xc3028_no_i2c_gate, + &dev->i2c_adap); + if (em28xx_attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: + case EM2882_BOARD_DIKOM_DK300: + case EM2882_BOARD_KWORLD_VS_DVBT: + dvb->fe[0] = dvb_attach(zl10353_attach, + &em28xx_zl10353_xc3028_no_i2c_gate, + &dev->i2c_adap); + if (dvb->fe[0] == NULL) { + /* This board could have either a zl10353 or a mt352. + If the chip id isn't for zl10353, try mt352 */ + dvb->fe[0] = dvb_attach(mt352_attach, + &terratec_xs_mt352_cfg, + &dev->i2c_adap); + } + + if (em28xx_attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2870_BOARD_KWORLD_355U: + dvb->fe[0] = dvb_attach(zl10353_attach, + &em28xx_zl10353_no_i2c_gate_dev, + &dev->i2c_adap); + if (dvb->fe[0] != NULL) + dvb_attach(qt1010_attach, dvb->fe[0], + &dev->i2c_adap, &em28xx_qt1010_config); + break; + case EM2883_BOARD_KWORLD_HYBRID_330U: + case EM2882_BOARD_EVGA_INDTUBE: + dvb->fe[0] = dvb_attach(s5h1409_attach, + &em28xx_s5h1409_with_xc3028, + &dev->i2c_adap); + if (em28xx_attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2882_BOARD_KWORLD_ATSC_315U: + dvb->fe[0] = dvb_attach(lgdt330x_attach, + &em2880_lgdt3303_dev, + &dev->i2c_adap); + if (dvb->fe[0] != NULL) { + if (!dvb_attach(simple_tuner_attach, dvb->fe[0], + &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) { + result = -EINVAL; + goto out_free; + } + } + break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: + case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: + dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL, + &dev->i2c_adap, &dev->udev->dev); + if (em28xx_attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2870_BOARD_REDDO_DVB_C_USB_BOX: + /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ + dvb->fe[0] = dvb_attach(tda10023_attach, + &em28xx_tda10023_config, + &dev->i2c_adap, 0x48); + if (dvb->fe[0]) { + if (!dvb_attach(simple_tuner_attach, dvb->fe[0], + &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) { + result = -EINVAL; + goto out_free; + } + } + break; + case EM2870_BOARD_KWORLD_A340: + dvb->fe[0] = dvb_attach(lgdt3305_attach, + &em2870_lgdt3304_dev, + &dev->i2c_adap); + if (dvb->fe[0] != NULL) + dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap, &kworld_a340_config); + break; + case EM28174_BOARD_PCTV_290E: + dvb->fe[0] = dvb_attach(cxd2820r_attach, + &em28xx_cxd2820r_config, + &dev->i2c_adap); + if (dvb->fe[0]) { + /* FE 0 attach tuner */ + if (!dvb_attach(tda18271_attach, + dvb->fe[0], + 0x60, + &dev->i2c_adap, + &em28xx_cxd2820r_tda18271_config)) { + + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + } + break; + case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: + { + struct xc5000_config cfg; + hauppauge_hvr930c_init(dev); + + dvb->fe[0] = dvb_attach(drxk_attach, + &hauppauge_930c_drxk, &dev->i2c_adap); + if (!dvb->fe[0]) { + result = -EINVAL; + goto out_free; + } + /* FIXME: do we need a pll semaphore? */ + dvb->fe[0]->sec_priv = dvb; + sema_init(&dvb->pll_mutex, 1); + dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl; + dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; + + /* Attach xc5000 */ + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_address = 0x61; + cfg.if_khz = 4000; + + if (dvb->fe[0]->ops.i2c_gate_ctrl) + dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); + if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap, + &cfg)) { + result = -EINVAL; + goto out_free; + } + if (dvb->fe[0]->ops.i2c_gate_ctrl) + dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); + + break; + } + case EM2884_BOARD_TERRATEC_H5: + terratec_h5_init(dev); + + dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap); + if (!dvb->fe[0]) { + result = -EINVAL; + goto out_free; + } + /* FIXME: do we need a pll semaphore? */ + dvb->fe[0]->sec_priv = dvb; + sema_init(&dvb->pll_mutex, 1); + dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl; + dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; + + /* Attach tda18271 to DVB-C frontend */ + if (dvb->fe[0]->ops.i2c_gate_ctrl) + dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); + if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) { + result = -EINVAL; + goto out_free; + } + if (dvb->fe[0]->ops.i2c_gate_ctrl) + dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); + + break; + case EM28174_BOARD_PCTV_460E: + /* attach demod */ + dvb->fe[0] = dvb_attach(tda10071_attach, + &em28xx_tda10071_config, &dev->i2c_adap); + + /* attach SEC */ + if (dvb->fe[0]) + dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap, + &em28xx_a8293_config); + break; + case EM2874_BOARD_MAXMEDIA_UB425_TC: + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk, + &dev->i2c_adap); + + if (dvb->fe[0]) { + /* disable I2C-gate */ + dvb->fe[0]->ops.i2c_gate_ctrl = NULL; + + /* attach tuner */ + if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], + &dev->i2c_adap, 0x60)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + } + + /* TODO: we need drx-3913k firmware in order to support DVB-T */ + em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \ + "driver version\n"); + + break; + case EM2884_BOARD_PCTV_510E: + case EM2884_BOARD_PCTV_520E: + pctv_520e_init(dev); + + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk, + &dev->i2c_adap); + + if (dvb->fe[0]) { + /* attach tuner */ + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap, + &em28xx_cxd2820r_tda18271_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + } + break; + case EM2884_BOARD_CINERGY_HTC_STICK: + terratec_htc_stick_init(dev); + + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk, + &dev->i2c_adap); + if (!dvb->fe[0]) { + result = -EINVAL; + goto out_free; + } + + /* Attach the demodulator. */ + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap, + &em28xx_cxd2820r_tda18271_config)) { + result = -EINVAL; + goto out_free; + } + break; + default: + em28xx_errdev("/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n"); + break; + } + if (NULL == dvb->fe[0]) { + em28xx_errdev("/2: frontend initialization failed\n"); + result = -EINVAL; + goto out_free; + } + /* define general-purpose callback pointer */ + dvb->fe[0]->callback = em28xx_tuner_callback; + if (dvb->fe[1]) + dvb->fe[1]->callback = em28xx_tuner_callback; + + /* register everything */ + result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + + if (result < 0) + goto out_free; + + /* MFE lock */ + dvb->adapter.mfe_shared = mfe_shared; + + em28xx_info("Successfully loaded em28xx-dvb\n"); +ret: + em28xx_set_mode(dev, EM28XX_SUSPEND); + mutex_unlock(&dev->lock); + return result; + +out_free: + kfree(dvb); + dev->dvb = NULL; + goto ret; +} + +static inline void prevent_sleep(struct dvb_frontend_ops *ops) +{ + ops->set_voltage = NULL; + ops->sleep = NULL; + ops->tuner_ops.sleep = NULL; +} + +static int em28xx_dvb_fini(struct em28xx *dev) +{ + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + return 0; + } + + if (dev->dvb) { + struct em28xx_dvb *dvb = dev->dvb; + + if (dev->state & DEV_DISCONNECTED) { + /* We cannot tell the device to sleep + * once it has been unplugged. */ + if (dvb->fe[0]) + prevent_sleep(&dvb->fe[0]->ops); + if (dvb->fe[1]) + prevent_sleep(&dvb->fe[1]->ops); + } + + em28xx_unregister_dvb(dvb); + kfree(dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct em28xx_ops dvb_ops = { + .id = EM28XX_DVB, + .name = "Em28xx dvb Extension", + .init = em28xx_dvb_init, + .fini = em28xx_dvb_fini, +}; + +static int __init em28xx_dvb_register(void) +{ + return em28xx_register_extension(&dvb_ops); +} + +static void __exit em28xx_dvb_unregister(void) +{ + em28xx_unregister_extension(&dvb_ops); +} + +module_init(em28xx_dvb_register); +module_exit(em28xx_dvb_unregister); diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c new file mode 100644 index 000000000000..1683bd9d51ee --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -0,0 +1,568 @@ +/* + em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices + + Copyright (C) 2005 Ludovico Cavedon + Markus Rechberger + Mauro Carvalho Chehab + Sascha Sommer + + This program is free software; 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 "em28xx.h" +#include "tuner-xc2028.h" +#include +#include + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define dprintk2(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__ , ##args); \ + } \ +} while (0) + +/* + * em2800_i2c_send_max4() + * send up to 4 bytes to the i2c device + */ +static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, + char *buf, int len) +{ + int ret; + int write_timeout; + unsigned char b2[6]; + BUG_ON(len < 1 || len > 4); + b2[5] = 0x80 + len - 1; + b2[4] = addr; + b2[3] = buf[0]; + if (len > 1) + b2[2] = buf[1]; + if (len > 2) + b2[1] = buf[2]; + if (len > 3) + b2[0] = buf[3]; + + ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); + if (ret != 2 + len) { + em28xx_warn("writing to i2c device failed (error=%i)\n", ret); + return -EIO; + } + for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + write_timeout -= 5) { + ret = dev->em28xx_read_reg(dev, 0x05); + if (ret == 0x80 + len - 1) + return len; + msleep(5); + } + em28xx_warn("i2c write timed out\n"); + return -EIO; +} + +/* + * em2800_i2c_send_bytes() + */ +static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, + short len) +{ + char *bufPtr = buf; + int ret; + int wrcount = 0; + int count; + int maxLen = 4; + struct em28xx *dev = (struct em28xx *)data; + while (len > 0) { + count = (len > maxLen) ? maxLen : len; + ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); + if (ret > 0) { + len -= count; + bufPtr += count; + wrcount += count; + } else + return (ret < 0) ? ret : -EFAULT; + } + return wrcount; +} + +/* + * em2800_i2c_check_for_device() + * check if there is a i2c_device at the supplied address + */ +static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +{ + char msg; + int ret; + int write_timeout; + msg = addr; + ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1); + if (ret < 0) { + em28xx_warn("setting i2c device address failed (error=%i)\n", + ret); + return ret; + } + msg = 0x84; + ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1); + if (ret < 0) { + em28xx_warn("preparing i2c read failed (error=%i)\n", ret); + return ret; + } + for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + write_timeout -= 5) { + unsigned reg = dev->em28xx_read_reg(dev, 0x5); + + if (reg == 0x94) + return -ENODEV; + else if (reg == 0x84) + return 0; + msleep(5); + } + return -ENODEV; +} + +/* + * em2800_i2c_recv_bytes() + * read from the i2c device + */ +static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, + char *buf, int len) +{ + int ret; + /* check for the device and set i2c read address */ + ret = em2800_i2c_check_for_device(dev, addr); + if (ret) { + em28xx_warn + ("preparing read at i2c address 0x%x failed (error=%i)\n", + addr, ret); + return ret; + } + ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len); + if (ret < 0) { + em28xx_warn("reading from i2c device at 0x%x failed (error=%i)", + addr, ret); + return ret; + } + return ret; +} + +/* + * em28xx_i2c_send_bytes() + */ +static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, + short len, int stop) +{ + int wrcount = 0; + struct em28xx *dev = (struct em28xx *)data; + int write_timeout, ret; + + wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); + + /* Seems to be required after a write */ + for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + write_timeout -= 5) { + ret = dev->em28xx_read_reg(dev, 0x05); + if (!ret) + break; + msleep(5); + } + + return wrcount; +} + +/* + * em28xx_i2c_recv_bytes() + * read a byte from the i2c device + */ +static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, + char *buf, int len) +{ + int ret; + ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); + if (ret < 0) { + em28xx_warn("reading i2c device failed (error=%i)\n", ret); + return ret; + } + if (dev->em28xx_read_reg(dev, 0x5) != 0) + return -ENODEV; + return ret; +} + +/* + * em28xx_i2c_check_for_device() + * check if there is a i2c_device at the supplied address + */ +static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +{ + int ret; + + ret = dev->em28xx_read_reg_req(dev, 2, addr); + if (ret < 0) { + em28xx_warn("reading from i2c device failed (error=%i)\n", ret); + return ret; + } + if (dev->em28xx_read_reg(dev, 0x5) != 0) + return -ENODEV; + return 0; +} + +/* + * em28xx_i2c_xfer() + * the main i2c transfer function + */ +static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct em28xx *dev = i2c_adap->algo_data; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + for (i = 0; i < num; i++) { + addr = msgs[i].addr << 1; + dprintk2(2, "%s %s addr=%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (!msgs[i].len) { /* no len: check only for device presence */ + if (dev->board.is_em2800) + rc = em2800_i2c_check_for_device(dev, addr); + else + rc = em28xx_i2c_check_for_device(dev, addr); + if (rc < 0) { + dprintk2(2, " no device\n"); + return rc; + } + + } else if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + if (dev->board.is_em2800) + rc = em2800_i2c_recv_bytes(dev, addr, + msgs[i].buf, + msgs[i].len); + else + rc = em28xx_i2c_recv_bytes(dev, addr, + msgs[i].buf, + msgs[i].len); + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + } else { + /* write bytes */ + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + if (dev->board.is_em2800) + rc = em2800_i2c_send_bytes(dev, addr, + msgs[i].buf, + msgs[i].len); + else + rc = em28xx_i2c_send_bytes(dev, addr, + msgs[i].buf, + msgs[i].len, + i == num - 1); + } + if (rc < 0) + goto err; + if (i2c_debug >= 2) + printk("\n"); + } + + return num; +err: + dprintk2(2, " ERROR: %i\n", rc); + return rc; +} + +/* based on linux/sunrpc/svcauth.h and linux/hash.h + * The original hash function returns a different value, if arch is x86_64 + * or i386. + */ +static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits) +{ + unsigned long hash = 0; + unsigned long l = 0; + int len = 0; + unsigned char c; + do { + if (len == length) { + c = (char)len; + len = -1; + } else + c = *buf++; + l = (l << 8) | c; + len++; + if ((len & (32 / 8 - 1)) == 0) + hash = ((hash^l) * 0x9e370001UL); + } while (len); + + return (hash >> (32 - bits)) & 0xffffffffUL; +} + +static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) +{ + unsigned char buf, *p = eedata; + struct em28xx_eeprom *em_eeprom = (void *)eedata; + int i, err, size = len, block; + + if (dev->chip_id == CHIP_ID_EM2874 || + dev->chip_id == CHIP_ID_EM28174 || + dev->chip_id == CHIP_ID_EM2884) { + /* Empia switched to a 16-bit addressable eeprom in newer + devices. While we could certainly write a routine to read + the eeprom, there is nothing of use in there that cannot be + accessed through registers, and there is the risk that we + could corrupt the eeprom (since a 16-bit read call is + interpreted as a write call by 8-bit eeproms). + */ + return 0; + } + + dev->i2c_client.addr = 0xa0 >> 1; + + /* Check if board has eeprom */ + err = i2c_master_recv(&dev->i2c_client, &buf, 0); + if (err < 0) { + em28xx_errdev("board has no eeprom\n"); + memset(eedata, 0, len); + return -ENODEV; + } + + buf = 0; + + err = i2c_master_send(&dev->i2c_client, &buf, 1); + if (err != 1) { + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name, err); + return err; + } + while (size > 0) { + if (size > 16) + block = 16; + else + block = size; + + if (block != + (err = i2c_master_recv(&dev->i2c_client, p, block))) { + printk(KERN_WARNING + "%s: i2c eeprom read error (err=%d)\n", + dev->name, err); + return err; + } + size -= block; + p += block; + } + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); + printk(" %02x", eedata[i]); + if (15 == (i % 16)) + printk("\n"); + } + + if (em_eeprom->id == 0x9567eb1a) + dev->hash = em28xx_hash_mem(eedata, len, 32); + + printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n", + dev->name, em_eeprom->id, dev->hash); + + printk(KERN_INFO "%s: EEPROM info:\n", dev->name); + + switch (em_eeprom->chip_conf >> 4 & 0x3) { + case 0: + printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name); + break; + case 1: + printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n", + dev->name); + break; + case 2: + printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", + dev->name); + break; + case 3: + printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", + dev->name); + break; + } + + if (em_eeprom->chip_conf & 1 << 3) + printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name); + + if (em_eeprom->chip_conf & 1 << 2) + printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name); + + switch (em_eeprom->chip_conf & 0x3) { + case 0: + printk(KERN_INFO "%s:\t500mA max power\n", dev->name); + break; + case 1: + printk(KERN_INFO "%s:\t400mA max power\n", dev->name); + break; + case 2: + printk(KERN_INFO "%s:\t300mA max power\n", dev->name); + break; + case 3: + printk(KERN_INFO "%s:\t200mA max power\n", dev->name); + break; + } + printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", + dev->name, + em_eeprom->string_idx_table, + em_eeprom->string1, + em_eeprom->string2, + em_eeprom->string3); + + return 0; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm em28xx_algo = { + .master_xfer = em28xx_i2c_xfer, + .functionality = functionality, +}; + +static struct i2c_adapter em28xx_adap_template = { + .owner = THIS_MODULE, + .name = "em28xx", + .algo = &em28xx_algo, +}; + +static struct i2c_client em28xx_client_template = { + .name = "em28xx internal", +}; + +/* ----------------------------------------------------------- */ + +/* + * i2c_devs + * incomplete list of known devices + */ +static char *i2c_devs[128] = { + [0x4a >> 1] = "saa7113h", + [0x52 >> 1] = "drxk", + [0x60 >> 1] = "remote IR sensor", + [0x8e >> 1] = "remote IR sensor", + [0x86 >> 1] = "tda9887", + [0x80 >> 1] = "msp34xx", + [0x88 >> 1] = "msp34xx", + [0xa0 >> 1] = "eeprom", + [0xb0 >> 1] = "tda9874", + [0xb8 >> 1] = "tvp5150a", + [0xba >> 1] = "webcam sensor or tvp5150a", + [0xc0 >> 1] = "tuner (analog)", + [0xc2 >> 1] = "tuner (analog)", + [0xc4 >> 1] = "tuner (analog)", + [0xc6 >> 1] = "tuner (analog)", +}; + +/* + * do_i2c_scan() + * check i2c address range for devices + */ +void em28xx_do_i2c_scan(struct em28xx *dev) +{ + u8 i2c_devicelist[128]; + unsigned char buf; + int i, rc; + + memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist)); + + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { + dev->i2c_client.addr = i; + rc = i2c_master_recv(&dev->i2c_client, &buf, 0); + if (rc < 0) + continue; + i2c_devicelist[i] = i; + printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n", + dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + } + + dev->i2c_hash = em28xx_hash_mem(i2c_devicelist, + ARRAY_SIZE(i2c_devicelist), 32); +} + +/* + * em28xx_i2c_register() + * register i2c bus + */ +int em28xx_i2c_register(struct em28xx *dev) +{ + int retval; + + BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg); + BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req); + dev->i2c_adap = em28xx_adap_template; + dev->i2c_adap.dev.parent = &dev->udev->dev; + strcpy(dev->i2c_adap.name, dev->name); + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + + retval = i2c_add_adapter(&dev->i2c_adap); + if (retval < 0) { + em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n", + __func__, retval); + return retval; + } + + dev->i2c_client = em28xx_client_template; + dev->i2c_client.adapter = &dev->i2c_adap; + + retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); + if ((retval < 0) && (retval != -ENODEV)) { + em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", + __func__, retval); + + return retval; + } + + if (i2c_scan) + em28xx_do_i2c_scan(dev); + + return 0; +} + +/* + * em28xx_i2c_unregister() + * unregister i2c_bus + */ +int em28xx_i2c_unregister(struct em28xx *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c new file mode 100644 index 000000000000..97d36b4f19db --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -0,0 +1,645 @@ +/* + handle em28xx IR remotes via linux kernel input layer. + + Copyright (C) 2005 Ludovico Cavedon + Markus Rechberger + Mauro Carvalho Chehab + Sascha Sommer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "em28xx.h" + +#define EM28XX_SNAPSHOT_KEY KEY_CAMERA +#define EM28XX_SBUTTON_QUERY_INTERVAL 500 +#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20 + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); + +#define MODULE_NAME "em28xx" + +#define i2cdprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +/********************************************************** + Polling structure used by em28xx IR's + **********************************************************/ + +struct em28xx_ir_poll_result { + unsigned int toggle_bit:1; + unsigned int read_count:7; + u8 rc_address; + u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ +}; + +struct em28xx_IR { + struct em28xx *dev; + struct rc_dev *rc; + char name[32]; + char phys[32]; + + /* poll external decoder */ + int polling; + struct delayed_work work; + unsigned int full_code:1; + unsigned int last_readcount; + + int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); +}; + +/********************************************************** + I2C IR based get keycodes - should be used with ir-kbd-i2c + **********************************************************/ + +static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char b; + + /* poll IR chip */ + if (1 != i2c_master_recv(ir->c, &b, 1)) { + i2cdprintk("read error\n"); + return -EIO; + } + + /* it seems that 0xFE indicates that a button is still hold + down, while 0xff indicates that no button is hold + down. 0xfe sequences are sometimes interrupted by 0xFF */ + + i2cdprintk("key %02x\n", b); + + if (b == 0xff) + return 0; + + if (b == 0xfe) + /* keep old data */ + return 1; + + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char buf[2]; + u16 code; + int size; + + /* poll IR chip */ + size = i2c_master_recv(ir->c, buf, sizeof(buf)); + + if (size != 2) + return -EIO; + + /* Does eliminate repeated parity code */ + if (buf[1] == 0xff) + return 0; + + ir->old = buf[1]; + + /* + * Rearranges bits to the right order. + * The bit order were determined experimentally by using + * The original Hauppauge Grey IR and another RC5 that uses addr=0x08 + * The RC5 code has 14 bits, but we've experimentally determined + * the meaning for only 11 bits. + * So, the code translation is not complete. Yet, it is enough to + * work with the provided RC5 IR. + */ + code = + ((buf[0] & 0x01) ? 0x0020 : 0) | /* 0010 0000 */ + ((buf[0] & 0x02) ? 0x0010 : 0) | /* 0001 0000 */ + ((buf[0] & 0x04) ? 0x0008 : 0) | /* 0000 1000 */ + ((buf[0] & 0x08) ? 0x0004 : 0) | /* 0000 0100 */ + ((buf[0] & 0x10) ? 0x0002 : 0) | /* 0000 0010 */ + ((buf[0] & 0x20) ? 0x0001 : 0) | /* 0000 0001 */ + ((buf[1] & 0x08) ? 0x1000 : 0) | /* 0001 0000 */ + ((buf[1] & 0x10) ? 0x0800 : 0) | /* 0000 1000 */ + ((buf[1] & 0x20) ? 0x0400 : 0) | /* 0000 0100 */ + ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */ + ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */ + + i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x%02x)\n", + code, buf[1], buf[0]); + + /* return key */ + *ir_key = code; + *ir_raw = code; + return 1; +} + +static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + unsigned char buf[3]; + + /* poll IR chip */ + + if (3 != i2c_master_recv(ir->c, buf, 3)) { + i2cdprintk("read error\n"); + return -EIO; + } + + i2cdprintk("key %02x\n", buf[2]&0x3f); + if (buf[0] != 0x00) + return 0; + + *ir_key = buf[2]&0x3f; + *ir_raw = buf[2]&0x3f; + + return 1; +} + +static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + unsigned char subaddr, keydetect, key; + + struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1}, + + { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + + subaddr = 0x10; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + i2cdprintk("read error\n"); + return -EIO; + } + if (keydetect == 0x00) + return 0; + + subaddr = 0x00; + msg[1].buf = &key; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + i2cdprintk("read error\n"); + return -EIO; + } + if (key == 0x00) + return 0; + + *ir_key = key; + *ir_raw = key; + return 1; +} + +/********************************************************** + Poll based get keycode functions + **********************************************************/ + +/* This is for the em2860/em2880 */ +static int default_polling_getkey(struct em28xx_IR *ir, + struct em28xx_ir_poll_result *poll_result) +{ + struct em28xx *dev = ir->dev; + int rc; + u8 msg[3] = { 0, 0, 0 }; + + /* Read key toggle, brand, and key code + on registers 0x45, 0x46 and 0x47 + */ + rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR, + msg, sizeof(msg)); + if (rc < 0) + return rc; + + /* Infrared toggle (Reg 0x45[7]) */ + poll_result->toggle_bit = (msg[0] >> 7); + + /* Infrared read count (Reg 0x45[6:0] */ + poll_result->read_count = (msg[0] & 0x7f); + + /* Remote Control Address (Reg 0x46) */ + poll_result->rc_address = msg[1]; + + /* Remote Control Data (Reg 0x47) */ + poll_result->rc_data[0] = msg[2]; + + return 0; +} + +static int em2874_polling_getkey(struct em28xx_IR *ir, + struct em28xx_ir_poll_result *poll_result) +{ + struct em28xx *dev = ir->dev; + int rc; + u8 msg[5] = { 0, 0, 0, 0, 0 }; + + /* Read key toggle, brand, and key code + on registers 0x51-55 + */ + rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR, + msg, sizeof(msg)); + if (rc < 0) + return rc; + + /* Infrared toggle (Reg 0x51[7]) */ + poll_result->toggle_bit = (msg[0] >> 7); + + /* Infrared read count (Reg 0x51[6:0] */ + poll_result->read_count = (msg[0] & 0x7f); + + /* Remote Control Address (Reg 0x52) */ + poll_result->rc_address = msg[1]; + + /* Remote Control Data (Reg 0x53-55) */ + poll_result->rc_data[0] = msg[2]; + poll_result->rc_data[1] = msg[3]; + poll_result->rc_data[2] = msg[4]; + + return 0; +} + +/********************************************************** + Polling code for em28xx + **********************************************************/ + +static void em28xx_ir_handle_key(struct em28xx_IR *ir) +{ + int result; + struct em28xx_ir_poll_result poll_result; + + /* read the registers containing the IR status */ + result = ir->get_key(ir, &poll_result); + if (unlikely(result < 0)) { + dprintk("ir->get_key() failed %d\n", result); + return; + } + + if (unlikely(poll_result.read_count != ir->last_readcount)) { + dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, + poll_result.toggle_bit, poll_result.read_count, + poll_result.rc_address, poll_result.rc_data[0]); + if (ir->full_code) + rc_keydown(ir->rc, + poll_result.rc_address << 8 | + poll_result.rc_data[0], + poll_result.toggle_bit); + else + rc_keydown(ir->rc, + poll_result.rc_data[0], + poll_result.toggle_bit); + + if (ir->dev->chip_id == CHIP_ID_EM2874 || + ir->dev->chip_id == CHIP_ID_EM2884) + /* The em2874 clears the readcount field every time the + register is read. The em2860/2880 datasheet says that it + is supposed to clear the readcount, but it doesn't. So with + the em2874, we are looking for a non-zero read count as + opposed to a readcount that is incrementing */ + ir->last_readcount = 0; + else + ir->last_readcount = poll_result.read_count; + } +} + +static void em28xx_ir_work(struct work_struct *work) +{ + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); + + em28xx_ir_handle_key(ir); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + +static int em28xx_ir_start(struct rc_dev *rc) +{ + struct em28xx_IR *ir = rc->priv; + + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); + schedule_delayed_work(&ir->work, 0); + + return 0; +} + +static void em28xx_ir_stop(struct rc_dev *rc) +{ + struct em28xx_IR *ir = rc->priv; + + cancel_delayed_work_sync(&ir->work); +} + +static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) +{ + int rc = 0; + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + u8 ir_config = EM2874_IR_RC5; + + /* Adjust xclk based o IR table for RC5/NEC tables */ + + if (rc_type == RC_TYPE_RC5) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + } else if (rc_type == RC_TYPE_NEC) { + dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; + ir_config = EM2874_IR_NEC; + ir->full_code = 1; + } else if (rc_type != RC_TYPE_UNKNOWN) + rc = -EINVAL; + + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, + EM28XX_XCLK_IR_RC5_MODE); + + /* Setup the proper handler based on the chip */ + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + ir->get_key = default_polling_getkey; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + ir->get_key = em2874_polling_getkey; + em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); + break; + default: + printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", + dev->chip_id); + rc = -EINVAL; + } + + return rc; +} + +static void em28xx_register_i2c_ir(struct em28xx *dev) +{ + /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ + /* at address 0x18, so if that address is needed for another board in */ + /* the future, please put it after 0x1f. */ + struct i2c_board_info info; + const unsigned short addr_list[] = { + 0x1f, 0x30, 0x47, I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + + /* detect & configure */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; + break; + case EM2820_BOARD_PINNACLE_USB_2: + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; + dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; + dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + break; + } + + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); +} + +/********************************************************** + Handle Webcam snapshot button + **********************************************************/ + +static void em28xx_query_sbutton(struct work_struct *work) +{ + /* Poll the register and see if the button is depressed */ + struct em28xx *dev = + container_of(work, struct em28xx, sbutton_query_work.work); + int ret; + + ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); + + if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { + u8 cleared; + /* Button is depressed, clear the register */ + cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; + em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); + + /* Not emulate the keypress */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 1); + /* Now unpress the key */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 0); + } + + /* Schedule next poll */ + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); +} + +static void em28xx_register_snapshot_button(struct em28xx *dev) +{ + struct input_dev *input_dev; + int err; + + em28xx_info("Registering snapshot button...\n"); + input_dev = input_allocate_device(); + if (!input_dev) { + em28xx_errdev("input_allocate_device failed\n"); + return; + } + + usb_make_path(dev->udev, dev->snapshot_button_path, + sizeof(dev->snapshot_button_path)); + strlcat(dev->snapshot_button_path, "/sbutton", + sizeof(dev->snapshot_button_path)); + INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); + + input_dev->name = "em28xx snapshot button"; + input_dev->phys = dev->snapshot_button_path; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); + input_dev->keycodesize = 0; + input_dev->keycodemax = 0; + input_dev->id.bustype = BUS_USB; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + input_dev->id.version = 1; + input_dev->dev.parent = &dev->udev->dev; + + err = input_register_device(input_dev); + if (err) { + em28xx_errdev("input_register_device failed\n"); + input_free_device(input_dev); + return; + } + + dev->sbutton_input_dev = input_dev; + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return; + +} + +static void em28xx_deregister_snapshot_button(struct em28xx *dev) +{ + if (dev->sbutton_input_dev != NULL) { + em28xx_info("Deregistering snapshot button\n"); + cancel_delayed_work_sync(&dev->sbutton_query_work); + input_unregister_device(dev->sbutton_input_dev); + dev->sbutton_input_dev = NULL; + } + return; +} + +static int em28xx_ir_init(struct em28xx *dev) +{ + struct em28xx_IR *ir; + struct rc_dev *rc; + int err = -ENOMEM; + + if (dev->board.ir_codes == NULL) { + /* No remote control support */ + em28xx_warn("Remote control support is not available for " + "this card.\n"); + return 0; + } + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + rc = rc_allocate_device(); + if (!ir || !rc) + goto err_out_free; + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + ir->rc = rc; + + /* + * em2874 supports more protocols. For now, let's just announce + * the two protocols that were already tested + */ + rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC; + rc->priv = ir; + rc->change_protocol = em28xx_ir_change_protocol; + rc->open = em28xx_ir_start; + rc->close = em28xx_ir_stop; + + /* By default, keep protocol field untouched */ + err = em28xx_ir_change_protocol(rc, RC_TYPE_UNKNOWN); + if (err) + goto err_out_free; + + /* This is how often we ask the chip for IR information */ + ir->polling = 100; /* ms */ + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + rc->input_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_USB; + rc->input_id.version = 1; + rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + rc->dev.parent = &dev->udev->dev; + rc->map_name = dev->board.ir_codes; + rc->driver_name = MODULE_NAME; + + /* all done */ + err = rc_register_device(rc); + if (err) + goto err_out_stop; + + em28xx_register_i2c_ir(dev); + +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c) + request_module("ir-kbd-i2c"); +#endif + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + + return 0; + + err_out_stop: + dev->ir = NULL; + err_out_free: + rc_free_device(rc); + kfree(ir); + return err; +} + +static int em28xx_ir_fini(struct em28xx *dev) +{ + struct em28xx_IR *ir = dev->ir; + + em28xx_deregister_snapshot_button(dev); + + /* skip detach on non attached boards */ + if (!ir) + return 0; + + if (ir->rc) + rc_unregister_device(ir->rc); + + /* done */ + kfree(ir); + dev->ir = NULL; + return 0; +} + +static struct em28xx_ops rc_ops = { + .id = EM28XX_RC, + .name = "Em28xx Input Extension", + .init = em28xx_ir_init, + .fini = em28xx_ir_fini, +}; + +static int __init em28xx_rc_register(void) +{ + return em28xx_register_extension(&rc_ops); +} + +static void __exit em28xx_rc_unregister(void) +{ + em28xx_unregister_extension(&rc_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("Em28xx Input driver"); + +module_init(em28xx_rc_register); +module_exit(em28xx_rc_unregister); diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h new file mode 100644 index 000000000000..6ff368297f6e --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -0,0 +1,226 @@ +#define EM_GPIO_0 (1 << 0) +#define EM_GPIO_1 (1 << 1) +#define EM_GPIO_2 (1 << 2) +#define EM_GPIO_3 (1 << 3) +#define EM_GPIO_4 (1 << 4) +#define EM_GPIO_5 (1 << 5) +#define EM_GPIO_6 (1 << 6) +#define EM_GPIO_7 (1 << 7) + +#define EM_GPO_0 (1 << 0) +#define EM_GPO_1 (1 << 1) +#define EM_GPO_2 (1 << 2) +#define EM_GPO_3 (1 << 3) + +/* em28xx endpoints */ +#define EM28XX_EP_ANALOG 0x82 +#define EM28XX_EP_AUDIO 0x83 +#define EM28XX_EP_DIGITAL 0x84 + +/* em2800 registers */ +#define EM2800_R08_AUDIOSRC 0x08 + +/* em28xx registers */ + +#define EM28XX_R00_CHIPCFG 0x00 + +/* em28xx Chip Configuration 0x00 */ +#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80 +#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40 +#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30 +#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20 +#define EM28XX_CHIPCFG_AC97 0x10 +#define EM28XX_CHIPCFG_AUDIOMASK 0x30 + +#define EM28XX_R01_CHIPCFG2 0x01 + +/* em28xx Chip Configuration 2 0x01 */ +#define EM28XX_CHIPCFG2_TS_PRESENT 0x10 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_MASK 0x0c /* bits 3-2 */ +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_1MF 0x00 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_2MF 0x04 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_4MF 0x08 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_8MF 0x0c +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK 0x03 /* bits 0-1 */ +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_188 0x00 +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_376 0x01 +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_564 0x02 +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_752 0x03 + + + /* GPIO/GPO registers */ +#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ +#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ + +#define EM28XX_R06_I2C_CLK 0x06 + +/* em28xx I2C Clock Register (0x06) */ +#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80 +#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40 +#define EM28XX_I2C_EEPROM_ON_BOARD 0x08 +#define EM28XX_I2C_EEPROM_KEY_VALID 0x04 +#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */ +#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */ +#define EM28XX_I2C_FREQ_25_KHZ 0x02 +#define EM28XX_I2C_FREQ_400_KHZ 0x01 +#define EM28XX_I2C_FREQ_100_KHZ 0x00 + + +#define EM28XX_R0A_CHIPID 0x0a +#define EM28XX_R0C_USBSUSP 0x0c /* */ + +#define EM28XX_R0E_AUDIOSRC 0x0e +#define EM28XX_R0F_XCLK 0x0f + +/* em28xx XCLK Register (0x0f) */ +#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */ +#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */ +#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */ +#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10 +#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */ +#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01 +#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02 +#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03 +#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04 +#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05 +#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06 +#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07 +#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08 +#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09 +#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a +#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b + +#define EM28XX_R10_VINMODE 0x10 + +#define EM28XX_R11_VINCTRL 0x11 + +/* em28xx Video Input Control Register 0x11 */ +#define EM28XX_VINCTRL_VBI_SLICED 0x80 +#define EM28XX_VINCTRL_VBI_RAW 0x40 +#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */ +#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10 +#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */ +#define EM28XX_VINCTRL_FID_ON_HREF 0x04 +#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02 +#define EM28XX_VINCTRL_INTERLACED 0x01 + +#define EM28XX_R12_VINENABLE 0x12 /* */ + +#define EM28XX_R14_GAMMA 0x14 +#define EM28XX_R15_RGAIN 0x15 +#define EM28XX_R16_GGAIN 0x16 +#define EM28XX_R17_BGAIN 0x17 +#define EM28XX_R18_ROFFSET 0x18 +#define EM28XX_R19_GOFFSET 0x19 +#define EM28XX_R1A_BOFFSET 0x1a + +#define EM28XX_R1B_OFLOW 0x1b +#define EM28XX_R1C_HSTART 0x1c +#define EM28XX_R1D_VSTART 0x1d +#define EM28XX_R1E_CWIDTH 0x1e +#define EM28XX_R1F_CHEIGHT 0x1f + +#define EM28XX_R20_YGAIN 0x20 +#define EM28XX_R21_YOFFSET 0x21 +#define EM28XX_R22_UVGAIN 0x22 +#define EM28XX_R23_UOFFSET 0x23 +#define EM28XX_R24_VOFFSET 0x24 +#define EM28XX_R25_SHARPNESS 0x25 + +#define EM28XX_R26_COMPR 0x26 +#define EM28XX_R27_OUTFMT 0x27 + +/* em28xx Output Format Register (0x27) */ +#define EM28XX_OUTFMT_RGB_8_RGRG 0x00 +#define EM28XX_OUTFMT_RGB_8_GRGR 0x01 +#define EM28XX_OUTFMT_RGB_8_GBGB 0x02 +#define EM28XX_OUTFMT_RGB_8_BGBG 0x03 +#define EM28XX_OUTFMT_RGB_16_656 0x04 +#define EM28XX_OUTFMT_RGB_8_BAYER 0x08 /* Pattern in Reg 0x10[1-0] */ +#define EM28XX_OUTFMT_YUV211 0x10 +#define EM28XX_OUTFMT_YUV422_Y0UY1V 0x14 +#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15 +#define EM28XX_OUTFMT_YUV411 0x18 + + +#define EM28XX_R28_XMIN 0x28 +#define EM28XX_R29_XMAX 0x29 +#define EM28XX_R2A_YMIN 0x2a +#define EM28XX_R2B_YMAX 0x2b + +#define EM28XX_R30_HSCALELOW 0x30 +#define EM28XX_R31_HSCALEHIGH 0x31 +#define EM28XX_R32_VSCALELOW 0x32 +#define EM28XX_R33_VSCALEHIGH 0x33 +#define EM28XX_R34_VBI_START_H 0x34 +#define EM28XX_R35_VBI_START_V 0x35 +#define EM28XX_R36_VBI_WIDTH 0x36 +#define EM28XX_R37_VBI_HEIGHT 0x37 + +#define EM28XX_R40_AC97LSB 0x40 +#define EM28XX_R41_AC97MSB 0x41 +#define EM28XX_R42_AC97ADDR 0x42 +#define EM28XX_R43_AC97BUSY 0x43 + +#define EM28XX_R45_IR 0x45 + /* 0x45 bit 7 - parity bit + bits 6-0 - count + 0x46 IR brand + 0x47 IR data + */ + +/* em2874 registers */ +#define EM2874_R50_IR_CONFIG 0x50 +#define EM2874_R51_IR 0x51 +#define EM2874_R5F_TS_ENABLE 0x5f +#define EM2874_R80_GPIO 0x80 + +/* em2874 IR config register (0x50) */ +#define EM2874_IR_NEC 0x00 +#define EM2874_IR_RC5 0x04 +#define EM2874_IR_RC6_MODE_0 0x08 +#define EM2874_IR_RC6_MODE_6A 0x0b + +/* em2874 Transport Stream Enable Register (0x5f) */ +#define EM2874_TS1_CAPTURE_ENABLE (1 << 0) +#define EM2874_TS1_FILTER_ENABLE (1 << 1) +#define EM2874_TS1_NULL_DISCARD (1 << 2) +#define EM2874_TS2_CAPTURE_ENABLE (1 << 4) +#define EM2874_TS2_FILTER_ENABLE (1 << 5) +#define EM2874_TS2_NULL_DISCARD (1 << 6) + +/* register settings */ +#define EM2800_AUDIO_SRC_TUNER 0x0d +#define EM2800_AUDIO_SRC_LINE 0x0c +#define EM28XX_AUDIO_SRC_TUNER 0xc0 +#define EM28XX_AUDIO_SRC_LINE 0x80 + +/* FIXME: Need to be populated with the other chip ID's */ +enum em28xx_chip_id { + CHIP_ID_EM2800 = 7, + CHIP_ID_EM2710 = 17, + CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ + CHIP_ID_EM2840 = 20, + CHIP_ID_EM2750 = 33, + CHIP_ID_EM2860 = 34, + CHIP_ID_EM2870 = 35, + CHIP_ID_EM2883 = 36, + CHIP_ID_EM2874 = 65, + CHIP_ID_EM2884 = 68, + CHIP_ID_EM28174 = 113, +}; + +/* + * Registers used by em202 + */ + +/* EMP202 vendor registers */ +#define EM202_EXT_MODEM_CTRL 0x3e +#define EM202_GPIO_CONF 0x4c +#define EM202_GPIO_POLARITY 0x4e +#define EM202_GPIO_STICKY 0x50 +#define EM202_GPIO_MASK 0x52 +#define EM202_GPIO_STATUS 0x54 +#define EM202_SPDIF_OUT_SEL 0x6a +#define EM202_ANTIPOP 0x72 +#define EM202_EAPD_GPIO_ACCESS 0x74 diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c new file mode 100644 index 000000000000..2b4c9cba2d67 --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -0,0 +1,145 @@ +/* + em28xx-vbi.c - VBI driver for em28xx + + Copyright (C) 2009 Devin Heitmueller + + This work was sponsored by EyeMagnet Limited. + + This program is free software; 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 +#include +#include +#include + +#include "em28xx.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +static unsigned int vbi_debug; +module_param(vbi_debug, int, 0644); +MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); + +#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct em28xx_fh *fh = q->priv_data; + struct em28xx *dev = fh->dev; + + *size = dev->vbi_width * dev->vbi_height * 2; + + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_fh *fh = q->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + int rc = 0; + + buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->vbi_width; + buf->vb.height = dev->vbi_height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops em28xx_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c new file mode 100644 index 000000000000..ecb23df7f16e --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -0,0 +1,2624 @@ +/* + em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB + video capture devices + + Copyright (C) 2005 Ludovico Cavedon + Markus Rechberger + Mauro Carvalho Chehab + Sascha Sommer + + Some parts based on SN9C10x PC Camera Controllers GPL driver made + by Luca Risolia + + This program is free software; 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 +#include + +#include "em28xx.h" +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Ludovico Cavedon , " \ + "Markus Rechberger , " \ + "Mauro Carvalho Chehab , " \ + "Sascha Sommer " + +#define DRIVER_DESC "Empia em28xx based USB video device driver" + +#define EM28XX_VERSION "0.1.3" + +#define em28xx_videodbg(fmt, arg...) do {\ + if (video_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define em28xx_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); \ + } \ + } while (0) + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(EM28XX_VERSION); + +static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; + +module_param_array(video_nr, int, NULL, 0444); +module_param_array(vbi_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); + +static unsigned int video_debug; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); + +/* supported video standards */ +static struct em28xx_fmt format[] = { + { + .name = "16 bpp YUY2, 4:2:2, packed", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .reg = EM28XX_OUTFMT_YUV422_Y0UY1V, + }, { + .name = "16 bpp RGB 565, LE", + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .reg = EM28XX_OUTFMT_RGB_16_656, + }, { + .name = "8 bpp Bayer BGBG..GRGR", + .fourcc = V4L2_PIX_FMT_SBGGR8, + .depth = 8, + .reg = EM28XX_OUTFMT_RGB_8_BGBG, + }, { + .name = "8 bpp Bayer GRGR..BGBG", + .fourcc = V4L2_PIX_FMT_SGRBG8, + .depth = 8, + .reg = EM28XX_OUTFMT_RGB_8_GRGR, + }, { + .name = "8 bpp Bayer GBGB..RGRG", + .fourcc = V4L2_PIX_FMT_SGBRG8, + .depth = 8, + .reg = EM28XX_OUTFMT_RGB_8_GBGB, + }, { + .name = "12 bpp YUV411", + .fourcc = V4L2_PIX_FMT_YUV411P, + .depth = 12, + .reg = EM28XX_OUTFMT_YUV411, + }, +}; + +/* supported controls */ +/* Common to all boards */ +static struct v4l2_queryctrl ac97_qctrl[] = { + { + .id = V4L2_CID_AUDIO_VOLUME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Volume", + .minimum = 0x0, + .maximum = 0x1f, + .step = 0x1, + .default_value = 0x1f, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + } +}; + +/* ------------------------------------------------------------------ + DMA and thread functions + ------------------------------------------------------------------*/ + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vid_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void vbi_buffer_filled(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the buffer header type and properly handles + */ +static void em28xx_copy_video(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *fieldstart, *startwrite, *startread; + int linesdone, currlinedone, offset, lencopy, remain; + int bytesperline = dev->width << 1; + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + remain = len; + + if (dev->progressive) + fieldstart = outp; + else { + /* Interlaces two half frames */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + } + + linesdone = dma_q->pos / bytesperline; + currlinedone = dma_q->pos % bytesperline; + + if (dev->progressive) + offset = linesdone * bytesperline + currlinedone; + else + offset = linesdone * bytesperline * 2 + currlinedone; + + startwrite = fieldstart + offset; + lencopy = bytesperline - currlinedone; + lencopy = lencopy > remain ? remain : lencopy; + + if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + remain = (char *)outp + buf->vb.size - (char *)startwrite; + lencopy = remain; + } + if (lencopy <= 0) + return; + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + + while (remain > 0) { + startwrite += lencopy + bytesperline; + startread += lencopy; + if (bytesperline > remain) + lencopy = remain; + else + lencopy = bytesperline; + + if ((char *)startwrite + lencopy > (char *)outp + + buf->vb.size) { + em28xx_isocdbg("Overflow of %zi bytes past buffer end" + "(2)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + lencopy = remain = (char *)outp + buf->vb.size - + (char *)startwrite; + } + if (lencopy <= 0) + break; + + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + } + + dma_q->pos += len; +} + +static void em28xx_copy_vbi(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *startwrite, *startread; + int offset; + int bytesperline; + + if (dev == NULL) { + em28xx_isocdbg("dev is null\n"); + return; + } + bytesperline = dev->vbi_width; + + if (dma_q == NULL) { + em28xx_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) { + return; + } + if (p == NULL) { + em28xx_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + em28xx_isocdbg("outp is null\n"); + return; + } + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + + startwrite = outp + dma_q->pos; + offset = dma_q->pos; + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) { + startwrite += bytesperline * dev->vbi_height; + offset += bytesperline * dev->vbi_height; + } + + memcpy(startwrite, startread, len); + dma_q->pos += len; +} + +static inline void print_err_status(struct em28xx *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + em28xx_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + em28xx_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + char *outp; + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vid_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + + /* Cleans up buffer - Useful for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + dev->isoc_ctl.vid_buf = *buf; + + return; +} + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); + char *outp; + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + /* Cleans up buffer - Useful for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); + + dev->isoc_ctl.vbi_buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) +{ + struct em28xx_buffer *buf; + struct em28xx_dmaqueue *dma_q = &dev->vidq; + unsigned char *outp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.vid_buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len = urb->iso_frame_desc[i].actual_length - 4; + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* FIXME: incomplete buffer checks where removed to make + logic simpler. Impacts of those changes should be evaluated + */ + if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { + em28xx_isocdbg("VBI HEADER!!!\n"); + /* FIXME: Should add vbi copy */ + continue; + } + if (p[0] == 0x22 && p[1] == 0x5a) { + em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], + len, (p[2] & 1) ? "odd" : "even"); + + if (dev->progressive || !(p[2] & 1)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + + if (buf != NULL) { + if (p[2] & 1) + buf->top_field = 0; + else + buf->top_field = 1; + } + + dma_q->pos = 0; + } + if (buf != NULL) { + if (p[0] != 0x88 && p[0] != 0x22) { + em28xx_isocdbg("frame is not complete\n"); + len += 4; + } else { + p += 4; + } + em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } + } + return rc; +} + +/* Version of isoc handler that takes into account a mixture of video and + VBI data */ +static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +{ + struct em28xx_buffer *buf, *vbi_buf; + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + int vbi_size; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.vid_buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len = urb->iso_frame_desc[i].actual_length; + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* capture type 0 = vbi start + capture type 1 = video start + capture type 2 = video in progress */ + if (p[0] == 0x33 && p[1] == 0x95) { + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER!!!\n"); + dev->cur_field = p[2]; + p += 4; + len -= 4; + } else if (p[0] == 0x88 && p[1] == 0x88 && + p[2] == 0x88 && p[3] == 0x88) { + /* continuation */ + p += 4; + len -= 4; + } else if (p[0] == 0x22 && p[1] == 0x5a) { + /* start video */ + p += 4; + len -= 4; + } + + vbi_size = dev->vbi_width * dev->vbi_height; + + if (dev->capture_type == 0) { + if (dev->vbi_read >= vbi_size) { + /* We've already read all the VBI data, so + treat the rest as video */ + em28xx_isocdbg("dev->vbi_read > vbi_size\n"); + } else if ((dev->vbi_read + len) < vbi_size) { + /* This entire frame is VBI data */ + if (dev->vbi_read == 0 && + (!(dev->cur_field & 1))) { + /* Brand new frame */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + } + + if (dev->vbi_read == 0) { + vbi_dma_q->pos = 0; + if (vbi_buf != NULL) { + if (dev->cur_field & 1) + vbi_buf->top_field = 0; + else + vbi_buf->top_field = 1; + } + } + + dev->vbi_read += len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + } else { + /* Some of this frame is VBI data and some is + video data */ + int vbi_data_len = vbi_size - dev->vbi_read; + dev->vbi_read += vbi_data_len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, vbi_data_len); + dev->capture_type = 1; + p += vbi_data_len; + len -= vbi_data_len; + } + } + + if (dev->capture_type == 1) { + dev->capture_type = 2; + if (dev->progressive || !(dev->cur_field & 1)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + if (buf != NULL) { + if (dev->cur_field & 1) + buf->top_field = 0; + else + buf->top_field = 1; + } + + dma_q->pos = 0; + } + + if (buf != NULL && dev->capture_type == 2) { + if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && + p[2] == 0x88 && p[3] == 0x88) { + p += 4; + len -= 4; + } + if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { + em28xx_isocdbg("Video frame %d, len=%i, %s\n", + p[2], len, (p[2] & 1) ? + "odd" : "even"); + p += 4; + len -= 4; + } + + if (len > 0) + em28xx_copy_video(dev, dma_q, buf, p, outp, + len); + } + } + return rc; +} + + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct v4l2_frequency f; + + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) + >> 3; + + if (0 == *count) + *count = EM28XX_DEF_BUF; + + if (*count < EM28XX_MIN_BUF) + *count = EM28XX_MIN_BUF; + + /* Ask tuner to go to analog or radio mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vid_buf == buf) + dev->isoc_ctl.vid_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx *dev = fh->dev; + int rc = 0, urb_init = 0; + + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (!dev->isoc_ctl.analog_bufs.num_bufs) + urb_init = 1; + + if (urb_init) { + if (em28xx_vbi_supported(dev) == 1) + rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, + EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy_vbi); + else + rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, + EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = (struct em28xx *)fh->dev; + + em28xx_isocdbg("em28xx: called buffer_release\n"); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops em28xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************* v4l2 interface **************************************/ + +static void video_mux(struct em28xx *dev, int index) +{ + dev->ctl_input = index; + dev->ctl_ainput = INPUT(index)->amux; + dev->ctl_aoutput = INPUT(index)->aout; + + if (!dev->ctl_aoutput) + dev->ctl_aoutput = EM28XX_AOUT_MASTER; + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + INPUT(index)->vmux, 0, 0); + + if (dev->board.has_msp34xx) { + if (dev->i2s_speed) { + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, + s_i2s_clock_freq, dev->i2s_speed); + } + /* Note: this is msp3400 specific */ + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); + } + + if (dev->board.adecoder != EM28XX_NOADECODER) { + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + dev->ctl_ainput, dev->ctl_aoutput, 0); + } + + em28xx_audio_analog_set(dev); +} + +/* Usage lock check functions */ +static int res_get(struct em28xx_fh *fh, unsigned int bit) +{ + struct em28xx *dev = fh->dev; + + if (fh->resources & bit) + /* have it already allocated */ + return 1; + + /* is it free? */ + if (dev->resources & bit) { + /* no, someone else uses it */ + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + em28xx_videodbg("res: get %d\n", bit); + return 1; +} + +static int res_check(struct em28xx_fh *fh, unsigned int bit) +{ + return fh->resources & bit; +} + +static int res_locked(struct em28xx *dev, unsigned int bit) +{ + return dev->resources & bit; +} + +static void res_free(struct em28xx_fh *fh, unsigned int bits) +{ + struct em28xx *dev = fh->dev; + + BUG_ON((fh->resources & bits) != bits); + + fh->resources &= ~bits; + dev->resources &= ~bits; + em28xx_videodbg("res: put %d\n", bits); +} + +static int get_ressource(struct em28xx_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } +} + +/* + * ac97_queryctrl() + * return the ac97 supported controls + */ +static int ac97_queryctrl(struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + /* Control is not ac97 related */ + return 1; +} + +/* + * ac97_get_ctrl() + * return the current values for ac97 mute and volume + */ +static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = dev->mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = dev->volume; + return 0; + default: + /* Control is not ac97 related */ + return 1; + } +} + +/* + * ac97_set_ctrl() + * set values for ac97 mute and volume + */ +static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) + if (ctrl->id == ac97_qctrl[i].id) + goto handle; + + /* Announce that hasn't handle it */ + return 1; + +handle: + if (ctrl->value < ac97_qctrl[i].minimum || + ctrl->value > ac97_qctrl[i].maximum) + return -ERANGE; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->mute = ctrl->value; + break; + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + break; + } + + return em28xx_audio_analog_set(dev); +} + +static int check_dev(struct em28xx *dev) +{ + if (dev->state & DEV_DISCONNECTED) { + em28xx_errdev("v4l2 ioctl: device not present\n"); + return -ENODEV; + } + + if (dev->state & DEV_MISCONFIGURED) { + em28xx_errdev("v4l2 ioctl: device is misconfigured; " + "close and open it again\n"); + return -EIO; + } + return 0; +} + +static void get_scale(struct em28xx *dev, + unsigned int width, unsigned int height, + unsigned int *hscale, unsigned int *vscale) +{ + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + + *hscale = (((unsigned long)maxw) << 12) / width - 4096L; + if (*hscale >= 0x4000) + *hscale = 0x3fff; + + *vscale = (((unsigned long)maxh) << 12) / height - 4096L; + if (*vscale >= 0x4000) + *vscale = 0x3fff; +} + +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = dev->format->fourcc; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; + return 0; +} + +static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return &format[i]; + + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + unsigned int width = f->fmt.pix.width; + unsigned int height = f->fmt.pix.height; + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + unsigned int hscale, vscale; + struct em28xx_fmt *fmt; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + em28xx_videodbg("Fourcc format (%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + if (dev->board.is_em2800) { + /* the em2800 can only scale down to 50% */ + height = height > (3 * maxh / 4) ? maxh : maxh / 2; + width = width > (3 * maxw / 4) ? maxw : maxw / 2; + /* MaxPacketSize for em2800 is too small to capture at full resolution + * use half of maxw as the scaler can only scale to 50% */ + if (width == maxw && height == maxh) + width /= 2; + } else { + /* width must even because of the YUYV format + height must be even because of interlacing */ + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, + 1, 0); + } + + get_scale(dev, width, height, &hscale, &vscale); + + width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + height = (((unsigned long)maxh) << 12) / (vscale + 4096L); + + f->fmt.pix.width = width; + f->fmt.pix.height = height; + f->fmt.pix.pixelformat = fmt->fourcc; + f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; + + return 0; +} + +static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, + unsigned width, unsigned height) +{ + struct em28xx_fmt *fmt; + + fmt = format_by_fourcc(fourcc); + if (!fmt) + return -EINVAL; + + dev->format = fmt; + dev->width = width; + dev->height = height; + + /* set new image size */ + get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + + em28xx_set_alternate(dev); + em28xx_resolution_set(dev); + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + vidioc_try_fmt_vid_cap(file, priv, f); + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + em28xx_errdev("%s queue busy\n", __func__); + return -EBUSY; + } + + return em28xx_set_video_format(dev, f->fmt.pix.pixelformat, + f->fmt.pix.width, f->fmt.pix.height); +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + *norm = dev->norm; + + return 0; +} + +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); + + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct v4l2_format f; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + dev->norm = *norm; + + /* Adjusts width/height, if needed */ + f.fmt.pix.width = dev->width; + f.fmt.pix.height = dev->height; + vidioc_try_fmt_vid_cap(file, priv, &f); + + /* set new image size */ + dev->width = f.fmt.pix.width; + dev->height = f.fmt.pix.height; + get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + + em28xx_resolution_set(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc = 0; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (dev->board.is_webcam) + rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, + video, g_parm, p); + else + v4l2_video_std_frame_period(dev->norm, + &p->parm.capture.timeperframe); + + return rc; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (!dev->board.is_webcam) + return -EINVAL; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); +} + +static const char *iname[] = { + [EM28XX_VMUX_COMPOSITE1] = "Composite1", + [EM28XX_VMUX_COMPOSITE2] = "Composite2", + [EM28XX_VMUX_COMPOSITE3] = "Composite3", + [EM28XX_VMUX_COMPOSITE4] = "Composite4", + [EM28XX_VMUX_SVIDEO] = "S-Video", + [EM28XX_VMUX_TELEVISION] = "Television", + [EM28XX_VMUX_CABLE] = "Cable TV", + [EM28XX_VMUX_DVB] = "DVB", + [EM28XX_VMUX_DEBUG] = "for debug only", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= MAX_EM28XX_INPUT) + return -EINVAL; + if (0 == INPUT(n)->type) + return -EINVAL; + + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + + strcpy(i->name, iname[INPUT(n)->type]); + + if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) || + (EM28XX_VMUX_CABLE == INPUT(n)->type)) + i->type = V4L2_INPUT_TYPE_TUNER; + + i->std = dev->vdev->tvnorms; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + *i = dev->ctl_input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (i >= MAX_EM28XX_INPUT) + return -EINVAL; + if (0 == INPUT(i)->type) + return -EINVAL; + + video_mux(dev, i); + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (!dev->audio_mode.has_audio) + return -EINVAL; + + switch (a->index) { + case EM28XX_AMUX_VIDEO: + strcpy(a->name, "Television"); + break; + case EM28XX_AMUX_LINE_IN: + strcpy(a->name, "Line In"); + break; + case EM28XX_AMUX_VIDEO2: + strcpy(a->name, "Television alt"); + break; + case EM28XX_AMUX_PHONE: + strcpy(a->name, "Phone"); + break; + case EM28XX_AMUX_MIC: + strcpy(a->name, "Mic"); + break; + case EM28XX_AMUX_CD: + strcpy(a->name, "CD"); + break; + case EM28XX_AMUX_AUX: + strcpy(a->name, "Aux"); + break; + case EM28XX_AMUX_PCM_OUT: + strcpy(a->name, "PCM"); + break; + default: + return -EINVAL; + } + + a->index = dev->ctl_ainput; + a->capability = V4L2_AUDCAP_STEREO; + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + + if (!dev->audio_mode.has_audio) + return -EINVAL; + + if (a->index >= MAX_EM28XX_INPUT) + return -EINVAL; + if (0 == INPUT(a->index)->type) + return -EINVAL; + + dev->ctl_ainput = INPUT(a->index)->amux; + dev->ctl_aoutput = INPUT(a->index)->aout; + + if (!dev->ctl_aoutput) + dev->ctl_aoutput = EM28XX_AOUT_MASTER; + + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int id = qc->id; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + memset(qc, 0, sizeof(*qc)); + + qc->id = id; + + /* enumerate AC97 controls */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + rc = ac97_queryctrl(qc); + if (!rc) + return 0; + } + + /* enumerate V4L2 device controls */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); + + if (qc->type) + return 0; + else + return -EINVAL; +} + +/* + * FIXME: This is an indirect way to check if a control exists at a + * subdev. Instead of that hack, maybe the better would be to change all + * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported. + */ +static int check_subdev_ctrl(struct em28xx *dev, int id) +{ + struct v4l2_queryctrl qc; + + memset(&qc, 0, sizeof(qc)); + qc.id = id; + + /* enumerate V4L2 device controls */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc); + + if (qc.type) + return 0; + else + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + rc = 0; + + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_get_ctrl(dev, ctrl); + else + rc = 1; + + /* It were not an AC97 control. Sends it to the v4l2 dev interface */ + if (rc == 1) { + if (check_subdev_ctrl(dev, ctrl->id)) + return -EINVAL; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); + rc = 0; + } + + return rc; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_set_ctrl(dev, ctrl); + else + rc = 1; + + /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ + if (rc == 1) { + rc = check_subdev_ctrl(dev, ctrl->id); + if (!rc) + v4l2_device_call_all(&dev->v4l2_dev, 0, + core, s_ctrl, ctrl); + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->mute = ctrl->value; + rc = em28xx_audio_analog_set(dev); + break; + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + rc = em28xx_audio_analog_set(dev); + } + } + return (rc < 0) ? rc : 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Tuner"); + t->type = V4L2_TUNER_ANALOG_TV; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != f->tuner) + return -EINVAL; + + if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) + return -EINVAL; + + dev->ctl_freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int em28xx_reg_len(int reg) +{ + switch (reg) { + case EM28XX_R40_AC97LSB: + case EM28XX_R30_HSCALELOW: + case EM28XX_R32_VSCALELOW: + return 2; + default: + return 1; + } +} + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); + + return 0; +} + + +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int ret; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_AC97: + ret = em28xx_read_ac97(dev, reg->reg); + if (ret < 0) + return ret; + + reg->val = ret; + reg->size = 1; + return 0; + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + /* Match host */ + reg->size = em28xx_reg_len(reg->reg); + if (reg->size == 1) { + ret = em28xx_read_reg(dev, reg->reg); + + if (ret < 0) + return ret; + + reg->val = ret; + } else { + __le16 val = 0; + ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, + reg->reg, (char *)&val, 2); + if (ret < 0) + return ret; + + reg->val = le16_to_cpu(val); + } + + return 0; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + __le16 buf; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_AC97: + return em28xx_write_ac97(dev, reg->reg, reg->val); + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + /* Match host */ + buf = cpu_to_le16(reg->val); + + return em28xx_write_regs(dev, reg->reg, (char *)&buf, + em28xx_reg_len(reg->reg)); +} +#endif + + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cc->bounds.left = 0; + cc->bounds.top = 0; + cc->bounds.width = dev->width; + cc->bounds.height = dev->height; + cc->defrect = cc->bounds; + cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ + cc->pixelaspect.denominator = 59; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc = -EINVAL; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (unlikely(type != fh->type)) + return -EINVAL; + + em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_streamon(&fh->vb_vidq); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_streamon(&fh->vb_vbiq); + + return rc; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) + return -EINVAL; + if (type != fh->type) + return -EINVAL; + + em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (res_check(fh, EM28XX_RESOURCE_VBI)) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } + } + + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); + strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->capabilities = + V4L2_CAP_SLICED_VBI_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + + if (dev->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + + if (dev->audio_mode.has_audio) + cap->capabilities |= V4L2_CAP_AUDIO; + + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + + return 0; +} + +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_fmt *fmt; + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + + fmt = format_by_fourcc(fsize->pixel_format); + if (!fmt) { + em28xx_videodbg("Fourcc format (%08x) invalid.\n", + fsize->pixel_format); + return -EINVAL; + } + + if (dev->board.is_em2800) { + if (fsize->index > 1) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = maxw / (1 + fsize->index); + fsize->discrete.height = maxh / (1 + fsize->index); + return 0; + } + + if (fsize->index != 0) + return -EINVAL; + + /* Report a continuous range */ + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = 48; + fsize->stepwise.min_height = 32; + fsize->stepwise.max_width = maxw; + fsize->stepwise.max_height = maxh; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + return 0; +} + +/* Sliced VBI ioctls */ +static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + f->fmt.sliced.service_set = 0; + v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); + + if (f->fmt.sliced.service_set == 0) + rc = -EINVAL; + + return rc; +} + +static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); + + if (f->fmt.sliced.service_set == 0) + return -EINVAL; + + return 0; +} + +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + format->fmt.vbi.samples_per_line = dev->vbi_width; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; + format->fmt.vbi.count[0] = dev->vbi_height; + format->fmt.vbi.count[1] = dev->vbi_height; + + /* Varies by video standard (NTSC, PAL, etc.) */ + if (dev->norm & V4L2_STD_525_60) { + /* NTSC */ + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + } else if (dev->norm & V4L2_STD_625_50) { + /* PAL */ + format->fmt.vbi.start[0] = 6; + format->fmt.vbi.start[1] = 318; + } + + return 0; +} + +static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + format->fmt.vbi.samples_per_line = dev->vbi_width; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; + format->fmt.vbi.count[0] = dev->vbi_height; + format->fmt.vbi.count[1] = dev->vbi_height; + + /* Varies by video standard (NTSC, PAL, etc.) */ + if (dev->norm & V4L2_STD_525_60) { + /* NTSC */ + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + } else if (dev->norm & V4L2_STD_625_50) { + /* PAL */ + format->fmt.vbi.start[0] = 6; + format->fmt.vbi.start[1] = 318; + } + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_reqbufs(&fh->vb_vidq, rb); + else + return videobuf_reqbufs(&fh->vb_vbiq, rb); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_querybuf(&fh->vb_vidq, b); + else { + /* FIXME: I'm not sure yet whether this is a bug in zvbi or + the videobuf framework, but we probably shouldn't be + returning a buffer larger than that which was asked for. + At a minimum, it causes a crash in zvbi since it does + a memcpy based on the source buffer length */ + int result = videobuf_querybuf(&fh->vb_vbiq, b); + b->length = dev->vbi_width * dev->vbi_height * 2; + + return result; + } +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_qbuf(&fh->vb_vidq, b); + else + return videobuf_qbuf(&fh->vb_vbiq, b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & + O_NONBLOCK); + else + return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & + O_NONBLOCK); +} + +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ +/* ----------------------------------------------------------- */ + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); + strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + if (unlikely(t->index > 0)) + return -EINVAL; + + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; + + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + if (0 != t->index) + return -EINVAL; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int radio_s_audio(struct file *file, void *fh, + struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *file, void *fh, unsigned int i) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + if (qc->id < V4L2_CID_BASE || + qc->id >= V4L2_CID_LASTP1) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + return -EINVAL; +} + +/* + * em28xx_v4l2_open() + * inits the device and starts isoc transfer + */ +static int em28xx_v4l2_open(struct file *filp) +{ + int errCode = 0, radio = 0; + struct video_device *vdev = video_devdata(filp); + struct em28xx *dev = video_drvdata(filp); + enum v4l2_buf_type fh_type = 0; + struct em28xx_fh *fh; + enum v4l2_field field; + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + } + + em28xx_videodbg("open dev=%s type=%s users=%d\n", + video_device_node_name(vdev), v4l2_type_names[fh_type], + dev->users); + + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); + if (!fh) { + em28xx_errdev("em28xx-video.c: Out of memory?!\n"); + mutex_unlock(&dev->lock); + return -ENOMEM; + } + fh->dev = dev; + fh->radio = radio; + fh->type = fh_type; + filp->private_data = fh; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + em28xx_set_alternate(dev); + em28xx_resolution_set(dev); + + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_wake_i2c(dev); + + } + if (fh->radio) { + em28xx_videodbg("video_open: setting radio device\n"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + } + + dev->users++; + + if (dev->progressive) + field = V4L2_FIELD_NONE; + else + field = V4L2_FIELD_INTERLACED; + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, field, + sizeof(struct em28xx_buffer), fh, &dev->lock); + + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct em28xx_buffer), fh, &dev->lock); + mutex_unlock(&dev->lock); + + return errCode; +} + +/* + * em28xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void em28xx_release_analog_resources(struct em28xx *dev) +{ + + /*FIXME: I2C IR should be disconnected */ + + if (dev->radio_dev) { + if (video_is_registered(dev->radio_dev)) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + if (dev->vbi_dev) { + em28xx_info("V4L2 device %s deregistered\n", + video_device_node_name(dev->vbi_dev)); + if (video_is_registered(dev->vbi_dev)) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + } + if (dev->vdev) { + em28xx_info("V4L2 device %s deregistered\n", + video_device_node_name(dev->vdev)); + if (video_is_registered(dev->vdev)) + video_unregister_device(dev->vdev); + else + video_device_release(dev->vdev); + dev->vdev = NULL; + } +} + +/* + * em28xx_v4l2_close() + * stops streaming and deallocates all resources allocated by the v4l2 + * calls and ioctls + */ +static int em28xx_v4l2_close(struct file *filp) +{ + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; + int errCode; + + em28xx_videodbg("users=%d\n", dev->users); + + mutex_lock(&dev->lock); + if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { + videobuf_stop(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } + + if (res_check(fh, EM28XX_RESOURCE_VBI)) { + videobuf_stop(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } + + if (dev->users == 1) { + /* the device is already disconnect, + free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + em28xx_release_resources(dev); + kfree(dev->alt_max_pkt_size); + kfree(dev); + kfree(fh); + mutex_unlock(&dev->lock); + return 0; + } + + /* Save some power by putting tuner to sleep */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + + /* do this before setting alternate! */ + em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); + em28xx_set_mode(dev, EM28XX_SUSPEND); + + /* set alternate 0 */ + dev->alt = 0; + em28xx_videodbg("setting alternate 0\n"); + errCode = usb_set_interface(dev->udev, 0, 0); + if (errCode < 0) { + em28xx_errdev("cannot change alternate number to " + "0 (error=%i)\n", errCode); + } + } + + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); + kfree(fh); + dev->users--; + mutex_unlock(&dev->lock); + return 0; +} + +/* + * em28xx_v4l2_read() + * will allocate buffers when called for the first time + */ +static ssize_t +em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + /* FIXME: read() is not prepared to allow changing the video + resolution while streaming. Seems a bug at em28xx_set_fmt + */ + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) + rc = -EBUSY; + else + rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + rc = -EBUSY; + else + rc = videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + mutex_unlock(&dev->lock); + + return rc; +} + +/* + * em28xx_poll() + * will allocate buffers when called for the first time + */ +static unsigned int em28xx_poll(struct file *filp, poll_table *wait) +{ + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { + return POLLERR; + } +} + +static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) +{ + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; + unsigned int res; + + mutex_lock(&dev->lock); + res = em28xx_poll(filp, wait); + mutex_unlock(&dev->lock); + return res; +} + +/* + * em28xx_v4l2_mmap() + */ +static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); + mutex_unlock(&dev->lock); + + em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + rc); + + return rc; +} + +static const struct v4l2_file_operations em28xx_v4l_fops = { + .owner = THIS_MODULE, + .open = em28xx_v4l2_open, + .release = em28xx_v4l2_close, + .read = em28xx_v4l2_read, + .poll = em28xx_v4l2_poll, + .mmap = em28xx_v4l2_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, + .vidioc_g_chip_ident = vidioc_g_chip_ident, +#endif +}; + +static const struct video_device em28xx_video_template = { + .fops = &em28xx_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + + .tvnorms = V4L2_STD_ALL, + .current_norm = V4L2_STD_PAL, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = em28xx_v4l2_open, + .release = em28xx_v4l2_close, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device em28xx_radio_template = { + .name = "em28xx-radio", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + +/******************************** usb interface ******************************/ + + + +static struct video_device *em28xx_vdev_init(struct em28xx *dev, + const struct video_device *template, + const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = video_debug; + vfd->lock = &dev->lock; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", + dev->name, type_name); + + video_set_drvdata(vfd, dev); + return vfd; +} + +int em28xx_register_analog_devices(struct em28xx *dev) +{ + u8 val; + int ret; + unsigned int maxw; + + printk(KERN_INFO "%s: v4l2 driver version %s\n", + dev->name, EM28XX_VERSION); + + /* set default norm */ + dev->norm = em28xx_video_template.current_norm; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + dev->interlaced = EM28XX_INTERLACED_DEFAULT; + + /* Analog specific initialization */ + dev->format = &format[0]; + + maxw = norm_maxw(dev); + /* MaxPacketSize for em2800 is too small to capture at full resolution + * use half of maxw as the scaler can only scale to 50% */ + if (dev->board.is_em2800) + maxw /= 2; + + em28xx_set_video_format(dev, format[0].fourcc, + maxw, norm_maxh(dev)); + + video_mux(dev, 0); + + /* Audio defaults */ + dev->mute = 1; + dev->volume = 0x1f; + +/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ + val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + (EM28XX_XCLK_AUDIO_UNMUTE | val)); + + em28xx_set_outfmt(dev); + em28xx_colorlevels_set_default(dev); + em28xx_compression_disable(dev); + + /* allocate and fill video video_device struct */ + dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); + if (!dev->vdev) { + em28xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + + /* register v4l2 video video_device */ + ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + video_nr[dev->devno]); + if (ret) { + em28xx_errdev("unable to register video device (error=%i).\n", + ret); + return ret; + } + + /* Allocate and fill vbi video_device struct */ + if (em28xx_vbi_supported(dev) == 1) { + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + "vbi"); + + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("unable to register vbi device\n"); + return ret; + } + } + + if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { + dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, + "radio"); + if (!dev->radio_dev) { + em28xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("can't register radio device\n"); + return ret; + } + em28xx_info("Registered radio device as %s\n", + video_device_node_name(dev->radio_dev)); + } + + em28xx_info("V4L2 video device registered as %s\n", + video_device_node_name(dev->vdev)); + + if (dev->vbi_dev) + em28xx_info("V4L2 VBI device registered as %s\n", + video_device_node_name(dev->vbi_dev)); + + return 0; +} diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h new file mode 100644 index 000000000000..8757523e6863 --- /dev/null +++ b/drivers/media/usb/em28xx/em28xx.h @@ -0,0 +1,809 @@ +/* + em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices + + Copyright (C) 2005 Markus Rechberger + Ludovico Cavedon + Mauro Carvalho Chehab + + Based on the em2800 driver from Sascha Sommer + + This program is free software; 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 _EM28XX_H +#define _EM28XX_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) +#include +#endif +#include "tuner-xc2028.h" +#include "xc5000.h" +#include "em28xx-reg.h" + +/* Boards supported by driver */ +#define EM2800_BOARD_UNKNOWN 0 +#define EM2820_BOARD_UNKNOWN 1 +#define EM2820_BOARD_TERRATEC_CINERGY_250 2 +#define EM2820_BOARD_PINNACLE_USB_2 3 +#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 +#define EM2820_BOARD_MSI_VOX_USB_2 5 +#define EM2800_BOARD_TERRATEC_CINERGY_200 6 +#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 +#define EM2800_BOARD_KWORLD_USB2800 8 +#define EM2820_BOARD_PINNACLE_DVC_90 9 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 +#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 +#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 +#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 +#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 +#define EM2800_BOARD_VGEAR_POCKETTV 15 +#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16 +#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18 +#define EM2860_BOARD_SAA711X_REFERENCE_DESIGN 19 +#define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20 +#define EM2800_BOARD_GRABBEEX_USB2800 21 +#define EM2750_BOARD_UNKNOWN 22 +#define EM2750_BOARD_DLCW_130 23 +#define EM2820_BOARD_DLINK_USB_TV 24 +#define EM2820_BOARD_GADMEI_UTV310 25 +#define EM2820_BOARD_HERCULES_SMART_TV_USB2 26 +#define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27 +#define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28 +#define EM2860_BOARD_TVP5150_REFERENCE_DESIGN 29 +#define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 +#define EM2821_BOARD_USBGEAR_VD204 31 +#define EM2821_BOARD_SUPERCOMP_USB_2 32 +#define EM2860_BOARD_ELGATO_VIDEO_CAPTURE 33 +#define EM2860_BOARD_TERRATEC_HYBRID_XS 34 +#define EM2860_BOARD_TYPHOON_DVD_MAKER 35 +#define EM2860_BOARD_NETGMBH_CAM 36 +#define EM2860_BOARD_GADMEI_UTV330 37 +#define EM2861_BOARD_YAKUMO_MOVIE_MIXER 38 +#define EM2861_BOARD_KWORLD_PVRTV_300U 39 +#define EM2861_BOARD_PLEXTOR_PX_TV100U 40 +#define EM2870_BOARD_KWORLD_350U 41 +#define EM2870_BOARD_KWORLD_355U 42 +#define EM2870_BOARD_TERRATEC_XS 43 +#define EM2870_BOARD_TERRATEC_XS_MT2060 44 +#define EM2870_BOARD_PINNACLE_PCTV_DVB 45 +#define EM2870_BOARD_COMPRO_VIDEOMATE 46 +#define EM2880_BOARD_KWORLD_DVB_305U 47 +#define EM2880_BOARD_KWORLD_DVB_310U 48 +#define EM2880_BOARD_MSI_DIGIVOX_AD 49 +#define EM2880_BOARD_MSI_DIGIVOX_AD_II 50 +#define EM2880_BOARD_TERRATEC_HYBRID_XS_FR 51 +#define EM2881_BOARD_DNT_DA2_HYBRID 52 +#define EM2881_BOARD_PINNACLE_HYBRID_PRO 53 +#define EM2882_BOARD_KWORLD_VS_DVBT 54 +#define EM2882_BOARD_TERRATEC_HYBRID_XS 55 +#define EM2882_BOARD_PINNACLE_HYBRID_PRO_330E 56 +#define EM2883_BOARD_KWORLD_HYBRID_330U 57 +#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 +#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 +#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 +#define EM2820_BOARD_GADMEI_TVR200 62 +#define EM2860_BOARD_KAIOMY_TVNPC_U2 63 +#define EM2860_BOARD_EASYCAP 64 +#define EM2820_BOARD_IODATA_GVMVP_SZ 65 +#define EM2880_BOARD_EMPIRE_DUAL_TV 66 +#define EM2860_BOARD_TERRATEC_GRABBY 67 +#define EM2860_BOARD_TERRATEC_AV350 68 +#define EM2882_BOARD_KWORLD_ATSC_315U 69 +#define EM2882_BOARD_EVGA_INDTUBE 70 +#define EM2820_BOARD_SILVERCREST_WEBCAM 71 +#define EM2861_BOARD_GADMEI_UTV330PLUS 72 +#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 +#define EM2800_BOARD_VC211A 74 +#define EM2882_BOARD_DIKOM_DK300 75 +#define EM2870_BOARD_KWORLD_A340 76 +#define EM2874_BOARD_LEADERSHIP_ISDBT 77 +#define EM28174_BOARD_PCTV_290E 78 +#define EM2884_BOARD_TERRATEC_H5 79 +#define EM28174_BOARD_PCTV_460E 80 +#define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81 +#define EM2884_BOARD_CINERGY_HTC_STICK 82 +#define EM2860_BOARD_HT_VIDBOX_NW03 83 +#define EM2874_BOARD_MAXMEDIA_UB425_TC 84 +#define EM2884_BOARD_PCTV_510E 85 +#define EM2884_BOARD_PCTV_520E 86 + +/* Limits minimum and default number of buffers */ +#define EM28XX_MIN_BUF 4 +#define EM28XX_DEF_BUF 8 + +/*Limits the max URB message size */ +#define URB_MAX_CTRL_SIZE 80 + +/* Params for validated field */ +#define EM28XX_BOARD_NOT_VALIDATED 1 +#define EM28XX_BOARD_VALIDATED 0 + +/* Params for em28xx_cmd() audio */ +#define EM28XX_START_AUDIO 1 +#define EM28XX_STOP_AUDIO 0 + +/* maximum number of em28xx boards */ +#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ + +/* maximum number of frames that can be queued */ +#define EM28XX_NUM_FRAMES 5 +/* number of frames that get used for v4l2_read() */ +#define EM28XX_NUM_READ_FRAMES 2 + +/* number of buffers for isoc transfers */ +#define EM28XX_NUM_BUFS 5 +#define EM28XX_DVB_NUM_BUFS 5 + +/* number of packets for each buffer + windows requests only 64 packets .. so we better do the same + this is what I found out for all alternate numbers there! + */ +#define EM28XX_NUM_PACKETS 64 +#define EM28XX_DVB_MAX_PACKETS 64 + +#define EM28XX_INTERLACED_DEFAULT 1 + +/* +#define (use usbview if you want to get the other alternate number infos) +#define +#define alternate number 2 +#define Endpoint Address: 82 + Direction: in + Attribute: 1 + Type: Isoc + Max Packet Size: 1448 + Interval: 125us + + alternate number 7 + + Endpoint Address: 82 + Direction: in + Attribute: 1 + Type: Isoc + Max Packet Size: 3072 + Interval: 125us +*/ + +/* time to wait when stopping the isoc transfer */ +#define EM28XX_URB_TIMEOUT \ + msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) + +/* time in msecs to wait for i2c writes to finish */ +#define EM2800_I2C_WRITE_TIMEOUT 20 + +enum em28xx_mode { + EM28XX_SUSPEND, + EM28XX_ANALOG_MODE, + EM28XX_DIGITAL_MODE, +}; + + +struct em28xx; + +struct em28xx_usb_isoc_bufs { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of packets in each buffer */ + int num_packets; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; +}; + +struct em28xx_usb_isoc_ctl { + /* isoc transfer buffers for analog mode */ + struct em28xx_usb_isoc_bufs analog_bufs; + + /* isoc transfer buffers for digital mode */ + struct em28xx_usb_isoc_bufs digital_bufs; + + /* Stores already requested buffers */ + struct em28xx_buffer *vid_buf; + struct em28xx_buffer *vbi_buf; + + /* isoc urb callback */ + int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + +}; + +/* Struct to enumberate video formats */ +struct em28xx_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int reg; +}; + +/* buffer for one video frame */ +struct em28xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct list_head frame; + int top_field; +}; + +struct em28xx_dmaqueue { + struct list_head active; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; +}; + +/* inputs */ + +#define MAX_EM28XX_INPUT 4 +enum enum28xx_itype { + EM28XX_VMUX_COMPOSITE1 = 1, + EM28XX_VMUX_COMPOSITE2, + EM28XX_VMUX_COMPOSITE3, + EM28XX_VMUX_COMPOSITE4, + EM28XX_VMUX_SVIDEO, + EM28XX_VMUX_TELEVISION, + EM28XX_VMUX_CABLE, + EM28XX_VMUX_DVB, + EM28XX_VMUX_DEBUG, + EM28XX_RADIO, +}; + +enum em28xx_ac97_mode { + EM28XX_NO_AC97 = 0, + EM28XX_AC97_EM202, + EM28XX_AC97_SIGMATEL, + EM28XX_AC97_OTHER, +}; + +struct em28xx_audio_mode { + enum em28xx_ac97_mode ac97; + + u16 ac97_feat; + u32 ac97_vendor_id; + + unsigned int has_audio:1; + + unsigned int i2s_3rates:1; + unsigned int i2s_5rates:1; +}; + +/* em28xx has two audio inputs: tuner and line in. + However, on most devices, an auxiliary AC97 codec device is used. + The AC97 device may have several different inputs and outputs, + depending on their model. So, it is possible to use AC97 mixer to + address more than two different entries. + */ +enum em28xx_amux { + /* This is the only entry for em28xx tuner input */ + EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */ + + EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */ + + /* Some less-common mixer setups */ + EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */ + EM28XX_AMUX_PHONE, + EM28XX_AMUX_MIC, + EM28XX_AMUX_CD, + EM28XX_AMUX_AUX, + EM28XX_AMUX_PCM_OUT, +}; + +enum em28xx_aout { + /* AC97 outputs */ + EM28XX_AOUT_MASTER = 1 << 0, + EM28XX_AOUT_LINE = 1 << 1, + EM28XX_AOUT_MONO = 1 << 2, + EM28XX_AOUT_LFE = 1 << 3, + EM28XX_AOUT_SURR = 1 << 4, + + /* PCM IN Mixer - used by AC97_RECORD_SELECT register */ + EM28XX_AOUT_PCM_IN = 1 << 7, + + /* Bits 10-8 are used to indicate the PCM IN record select */ + EM28XX_AOUT_PCM_MIC_PCM = 0 << 8, + EM28XX_AOUT_PCM_CD = 1 << 8, + EM28XX_AOUT_PCM_VIDEO = 2 << 8, + EM28XX_AOUT_PCM_AUX = 3 << 8, + EM28XX_AOUT_PCM_LINE = 4 << 8, + EM28XX_AOUT_PCM_STEREO = 5 << 8, + EM28XX_AOUT_PCM_MONO = 6 << 8, + EM28XX_AOUT_PCM_PHONE = 7 << 8, +}; + +static inline int ac97_return_record_select(int a_out) +{ + return (a_out & 0x700) >> 8; +} + +struct em28xx_reg_seq { + int reg; + unsigned char val, mask; + int sleep; +}; + +struct em28xx_input { + enum enum28xx_itype type; + unsigned int vmux; + enum em28xx_amux amux; + enum em28xx_aout aout; + struct em28xx_reg_seq *gpio; +}; + +#define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) + +enum em28xx_decoder { + EM28XX_NODECODER = 0, + EM28XX_TVP5150, + EM28XX_SAA711X, +}; + +enum em28xx_sensor { + EM28XX_NOSENSOR = 0, + EM28XX_MT9V011, + EM28XX_MT9M001, + EM28XX_MT9M111, +}; + +enum em28xx_adecoder { + EM28XX_NOADECODER = 0, + EM28XX_TVAUDIO, +}; + +struct em28xx_board { + char *name; + int vchannels; + int tuner_type; + int tuner_addr; + + /* i2c flags */ + unsigned int tda9887_conf; + + /* GPIO sequences */ + struct em28xx_reg_seq *dvb_gpio; + struct em28xx_reg_seq *suspend_gpio; + struct em28xx_reg_seq *tuner_gpio; + struct em28xx_reg_seq *mute_gpio; + + unsigned int is_em2800:1; + unsigned int has_msp34xx:1; + unsigned int mts_firmware:1; + unsigned int max_range_640_480:1; + unsigned int has_dvb:1; + unsigned int has_snapshot_button:1; + unsigned int is_webcam:1; + unsigned int valid:1; + unsigned int has_ir_i2c:1; + + unsigned char xclk, i2c_speed; + unsigned char radio_addr; + unsigned short tvaudio_addr; + + enum em28xx_decoder decoder; + enum em28xx_adecoder adecoder; + + struct em28xx_input input[MAX_EM28XX_INPUT]; + struct em28xx_input radio; + char *ir_codes; +}; + +struct em28xx_eeprom { + u32 id; /* 0x9567eb1a */ + u16 vendor_ID; + u16 product_ID; + + u16 chip_conf; + + u16 board_conf; + + u16 string1, string2, string3; + + u8 string_idx_table; +}; + +/* device states */ +enum em28xx_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +#define EM28XX_AUDIO_BUFS 5 +#define EM28XX_NUM_AUDIO_PACKETS 64 +#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ +#define EM28XX_CAPTURE_STREAM_EN 1 + +/* em28xx extensions */ +#define EM28XX_AUDIO 0x10 +#define EM28XX_DVB 0x20 +#define EM28XX_RC 0x30 + +/* em28xx resource types (used for res_get/res_lock etc */ +#define EM28XX_RESOURCE_VIDEO 0x01 +#define EM28XX_RESOURCE_VBI 0x02 + +struct em28xx_audio { + char name[50]; + char *transfer_buffer[EM28XX_AUDIO_BUFS]; + struct urb *urb[EM28XX_AUDIO_BUFS]; + struct usb_device *udev; + unsigned int capture_transfer_done; + struct snd_pcm_substream *capture_pcm_substream; + + unsigned int hwptr_done_capture; + struct snd_card *sndcard; + + int users; + spinlock_t slock; +}; + +struct em28xx; + +struct em28xx_fh { + struct em28xx *dev; + int radio; + unsigned int resources; + + struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; + + enum v4l2_buf_type type; +}; + +/* main device struct */ +struct em28xx { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + enum em28xx_chip_id chip_id; + + int audio_ifnum; + + struct v4l2_device v4l2_dev; + struct em28xx_board board; + + /* Webcam specific fields */ + enum em28xx_sensor em28xx_sensor; + int sensor_xres, sensor_yres; + int sensor_xtal; + + /* Allows progressive (e. g. non-interlaced) mode */ + int progressive; + + /* Vinmode/Vinctl used at the driver */ + int vinmode, vinctl; + + unsigned int has_audio_class:1; + unsigned int has_alsa_audio:1; + unsigned int is_audio_only:1; + + /* Controls audio streaming */ + struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ + atomic_t stream_started; /* stream should be running if true */ + + struct em28xx_fmt *format; + + struct em28xx_IR *ir; + + /* Some older em28xx chips needs a waiting time after writing */ + unsigned int wait_after_write; + + struct list_head devlist; + + u32 i2s_speed; /* I2S speed for audio digital stream */ + + struct em28xx_audio_mode audio_mode; + + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int tda9887_conf; + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + /* video for linux */ + int users; /* user count for exclusive use */ + struct video_device *vdev; /* video for linux device struct */ + v4l2_std_id norm; /* selected tv norm */ + int ctl_freq; /* selected frequency */ + unsigned int ctl_input; /* selected input */ + unsigned int ctl_ainput;/* selected audio input */ + unsigned int ctl_aoutput;/* selected audio output */ + int mute; + int volume; + /* frame properties */ + int width; /* current frame width */ + int height; /* current frame height */ + unsigned hscale; /* horizontal scale factor (see datasheet) */ + unsigned vscale; /* vertical scale factor (see datasheet) */ + int interlaced; /* 1=interlace fileds, 0=just top fileds */ + unsigned int video_bytesread; /* Number of bytes read */ + + unsigned long hash; /* eeprom hash - for boards with generic ID */ + unsigned long i2c_hash; /* i2c devicelist hash - + for boards with generic ID */ + + struct em28xx_audio adev; + + /* states */ + enum em28xx_dev_state state; + + /* vbi related state tracking */ + int capture_type; + int vbi_read; + unsigned char cur_field; + unsigned int vbi_width; + unsigned int vbi_height; /* lines per field */ + + struct work_struct request_module_wk; + + /* locks */ + struct mutex lock; + struct mutex ctrl_urb_lock; /* protects urb_buf */ + /* spinlock_t queue_lock; */ + struct list_head inqueue, outqueue; + struct video_device *vbi_dev; + struct video_device *radio_dev; + + /* resources in use */ + unsigned int resources; + + unsigned char eedata[256]; + + /* Isoc control struct */ + struct em28xx_dmaqueue vidq; + struct em28xx_dmaqueue vbiq; + struct em28xx_usb_isoc_ctl isoc_ctl; + spinlock_t slock; + + /* usb transfer */ + struct usb_device *udev; /* the usb device */ + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + int dvb_alt; /* alternate for DVB */ + unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ + char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ + + /* helper funcs that call usb_control_msg */ + int (*em28xx_write_regs) (struct em28xx *dev, u16 reg, + char *buf, int len); + int (*em28xx_read_reg) (struct em28xx *dev, u16 reg); + int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg); + + enum em28xx_mode mode; + + /* register numbers for GPO/GPIO registers */ + u16 reg_gpo_num, reg_gpio_num; + + /* Caches GPO and GPIO registers */ + unsigned char reg_gpo, reg_gpio; + + /* Snapshot button */ + char snapshot_button_path[30]; /* path of the input dev */ + struct input_dev *sbutton_input_dev; + struct delayed_work sbutton_query_work; + + struct em28xx_dvb *dvb; + + /* I2C keyboard data */ + struct IR_i2c_init_data init_data; +}; + +struct em28xx_ops { + struct list_head next; + char *name; + int id; + int (*init)(struct em28xx *); + int (*fini)(struct em28xx *); +}; + +/* Provided by em28xx-i2c.c */ +void em28xx_do_i2c_scan(struct em28xx *dev); +int em28xx_i2c_register(struct em28xx *dev); +int em28xx_i2c_unregister(struct em28xx *dev); + +/* Provided by em28xx-core.c */ + +u32 em28xx_request_buffers(struct em28xx *dev, u32 count); +void em28xx_queue_unusedframes(struct em28xx *dev); +void em28xx_release_buffers(struct em28xx *dev); + +int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, + char *buf, int len); +int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg); +int em28xx_read_reg(struct em28xx *dev, u16 reg); +int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, + int len); +int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); +int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val); +int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, + u8 bitmask); + +int em28xx_read_ac97(struct em28xx *dev, u8 reg); +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); + +int em28xx_audio_analog_set(struct em28xx *dev); +int em28xx_audio_setup(struct em28xx *dev); + +int em28xx_colorlevels_set_default(struct em28xx *dev); +int em28xx_capture_start(struct em28xx *dev, int start); +int em28xx_vbi_supported(struct em28xx *dev); +int em28xx_set_outfmt(struct em28xx *dev); +int em28xx_resolution_set(struct em28xx *dev); +int em28xx_set_alternate(struct em28xx *dev); +int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size); +int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); +void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +void em28xx_stop_urbs(struct em28xx *dev); +int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); +void em28xx_wake_i2c(struct em28xx *dev); +int em28xx_register_extension(struct em28xx_ops *dev); +void em28xx_unregister_extension(struct em28xx_ops *dev); +void em28xx_init_extension(struct em28xx *dev); +void em28xx_close_extension(struct em28xx *dev); + +/* Provided by em28xx-video.c */ +int em28xx_register_analog_devices(struct em28xx *dev); +void em28xx_release_analog_resources(struct em28xx *dev); + +/* Provided by em28xx-cards.c */ +extern int em2800_variant_detect(struct usb_device *udev, int model); +extern struct em28xx_board em28xx_boards[]; +extern struct usb_device_id em28xx_id_table[]; +extern const unsigned int em28xx_bcount; +int em28xx_tuner_callback(void *ptr, int component, int command, int arg); +void em28xx_release_resources(struct em28xx *dev); + +/* Provided by em28xx-vbi.c */ +extern struct videobuf_queue_ops em28xx_vbi_qops; + +/* printk macros */ + +#define em28xx_err(fmt, arg...) do {\ + printk(KERN_ERR fmt , ##arg); } while (0) + +#define em28xx_errdev(fmt, arg...) do {\ + printk(KERN_ERR "%s: "fmt,\ + dev->name , ##arg); } while (0) + +#define em28xx_info(fmt, arg...) do {\ + printk(KERN_INFO "%s: "fmt,\ + dev->name , ##arg); } while (0) +#define em28xx_warn(fmt, arg...) do {\ + printk(KERN_WARNING "%s: "fmt,\ + dev->name , ##arg); } while (0) + +static inline int em28xx_compression_disable(struct em28xx *dev) +{ + /* side effect of disabling scaler and mixer */ + return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00); +} + +static inline int em28xx_contrast_get(struct em28xx *dev) +{ + return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f; +} + +static inline int em28xx_brightness_get(struct em28xx *dev) +{ + return em28xx_read_reg(dev, EM28XX_R21_YOFFSET); +} + +static inline int em28xx_saturation_get(struct em28xx *dev) +{ + return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f; +} + +static inline int em28xx_u_balance_get(struct em28xx *dev) +{ + return em28xx_read_reg(dev, EM28XX_R23_UOFFSET); +} + +static inline int em28xx_v_balance_get(struct em28xx *dev) +{ + return em28xx_read_reg(dev, EM28XX_R24_VOFFSET); +} + +static inline int em28xx_gamma_get(struct em28xx *dev) +{ + return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f; +} + +static inline int em28xx_contrast_set(struct em28xx *dev, s32 val) +{ + u8 tmp = (u8) val; + return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1); +} + +static inline int em28xx_brightness_set(struct em28xx *dev, s32 val) +{ + u8 tmp = (u8) val; + return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1); +} + +static inline int em28xx_saturation_set(struct em28xx *dev, s32 val) +{ + u8 tmp = (u8) val; + return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1); +} + +static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val) +{ + u8 tmp = (u8) val; + return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1); +} + +static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val) +{ + u8 tmp = (u8) val; + return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1); +} + +static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) +{ + u8 tmp = (u8) val; + return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1); +} + +/*FIXME: maxw should be dependent of alt mode */ +static inline unsigned int norm_maxw(struct em28xx *dev) +{ + if (dev->board.is_webcam) + return dev->sensor_xres; + + if (dev->board.max_range_640_480) + return 640; + + return 720; +} + +static inline unsigned int norm_maxh(struct em28xx *dev) +{ + if (dev->board.is_webcam) + return dev->sensor_yres; + + if (dev->board.max_range_640_480) + return 480; + + return (dev->norm & V4L2_STD_625_50) ? 576 : 480; +} +#endif diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig new file mode 100644 index 000000000000..6345f9331e7f --- /dev/null +++ b/drivers/media/usb/gspca/Kconfig @@ -0,0 +1,425 @@ +menuconfig USB_GSPCA + tristate "GSPCA based webcams" + depends on VIDEO_V4L2 + default m + ---help--- + Say Y here if you want to enable selecting webcams based + on the GSPCA framework. + + See for more info. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as modules, choose M here: the + module will be called gspca_main. + + +if USB_GSPCA && VIDEO_V4L2 + +source "drivers/media/usb/gspca/m5602/Kconfig" +source "drivers/media/usb/gspca/stv06xx/Kconfig" +source "drivers/media/usb/gspca/gl860/Kconfig" + +config USB_GSPCA_BENQ + tristate "Benq USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for the Benq DC E300 camera. + + To compile this driver as a module, choose M here: the + module will be called gspca_benq. + +config USB_GSPCA_CONEX + tristate "Conexant Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Conexant chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_conex. + +config USB_GSPCA_CPIA1 + tristate "cpia CPiA (version 1) Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for USB cameras based on the cpia + CPiA chip. Note that you need atleast version 0.6.4 of libv4l for + applications to understand the videoformat generated by this driver. + + To compile this driver as a module, choose M here: the + module will be called gspca_cpia1. + +config USB_GSPCA_ETOMS + tristate "Etoms USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Etoms chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_etoms. + +config USB_GSPCA_FINEPIX + tristate "Fujifilm FinePix USB V4L2 driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the FinePix chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_finepix. + +config USB_GSPCA_JEILINJ + tristate "Jeilin JPEG USB V4L2 driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on this Jeilin chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_jeilinj. + +config USB_GSPCA_JL2005BCD + tristate "JL2005B/C/D USB V4L2 driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based the + JL2005B, JL2005C, or JL2005D chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_jl2005bcd. + +config USB_GSPCA_KINECT + tristate "Kinect sensor device USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for the Microsoft Kinect sensor device. + + To compile this driver as a module, choose M here: the + module will be called gspca_kinect. + +config USB_GSPCA_KONICA + tristate "Konica USB Camera V4L2 driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Konica chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_konica. + +config USB_GSPCA_MARS + tristate "Mars USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Mars chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_mars. + +config USB_GSPCA_MR97310A + tristate "Mars-Semi MR97310A USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the MR97310A chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_mr97310a. + +config USB_GSPCA_NW80X + tristate "Divio based (NW80x) USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the NW80x chips. + + To compile this driver as a module, choose M here: the + module will be called gspca_nw80x. + +config USB_GSPCA_OV519 + tristate "OV51x / OVFX2 / W996xCF USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on one of these: + OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF + + To compile this driver as a module, choose M here: the + module will be called gspca_ov519. + +config USB_GSPCA_OV534 + tristate "OV534 OV772x USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the OV534 chip + and sensor OV772x (e.g. Sony Playstation EYE) + + To compile this driver as a module, choose M here: the + module will be called gspca_ov534. + +config USB_GSPCA_OV534_9 + tristate "OV534 OV965x USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the OV534 chip + and sensor OV965x (e.g. Hercules Dualpix) + + To compile this driver as a module, choose M here: the + module will be called gspca_ov534_9. + +config USB_GSPCA_PAC207 + tristate "Pixart PAC207 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC207 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac207. + +config USB_GSPCA_PAC7302 + tristate "Pixart PAC7302 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC7302 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac7302. + +config USB_GSPCA_PAC7311 + tristate "Pixart PAC7311 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC7311 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac7311. + +config USB_GSPCA_SE401 + tristate "SE401 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + Endpoints (formerly known as AOX) se401 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_se401. + +config USB_GSPCA_SN9C2028 + tristate "SONIX Dual-Mode USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want streaming support for Sonix SN9C2028 cameras. + These are supported as stillcams in libgphoto2/camlibs/sonix. + + To compile this driver as a module, choose M here: the + module will be called gspca_sn9c2028. + +config USB_GSPCA_SN9C20X + tristate "SN9C20X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + sn9c20x chips (SN9C201 and SN9C202). + + To compile this driver as a module, choose M here: the + module will be called gspca_sn9c20x. + +config USB_GSPCA_SONIXB + tristate "SONIX Bayer USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Sonix + chips with Bayer format (SN9C101, SN9C102 and SN9C103). + + To compile this driver as a module, choose M here: the + module will be called gspca_sonixb. + +config USB_GSPCA_SONIXJ + tristate "SONIX JPEG USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Sonix + chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110). + + To compile this driver as a module, choose M here: the + module will be called gspca_sonixj + +config USB_GSPCA_SPCA500 + tristate "SPCA500 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA500 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca500. + +config USB_GSPCA_SPCA501 + tristate "SPCA501 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA501 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca501. + +config USB_GSPCA_SPCA505 + tristate "SPCA505 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA505 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca505. + +config USB_GSPCA_SPCA506 + tristate "SPCA506 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA506 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca506. + +config USB_GSPCA_SPCA508 + tristate "SPCA508 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA508 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca508. + +config USB_GSPCA_SPCA561 + tristate "SPCA561 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA561 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca561. + +config USB_GSPCA_SPCA1528 + tristate "SPCA1528 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA1528 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca1528. + +config USB_GSPCA_SQ905 + tristate "SQ Technologies SQ905 based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ905 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq905. + +config USB_GSPCA_SQ905C + tristate "SQ Technologies SQ905C based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ905C chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq905c. + +config USB_GSPCA_SQ930X + tristate "SQ Technologies SQ930X based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ930X chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq930x. + +config USB_GSPCA_STK014 + tristate "Syntek DV4000 (STK014) USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the STK014 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stk014. + +config USB_GSPCA_STV0680 + tristate "STV0680 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the STV0680 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stv0680. + +config USB_GSPCA_SUNPLUS + tristate "SUNPLUS USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Sunplus + SPCA504(abc) SPCA533 SPCA536 chips. + + To compile this driver as a module, choose M here: the + module will be called gspca_sunplus. + +config USB_GSPCA_T613 + tristate "T613 (JPEG Compliance) USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the T613 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_t613. + +config USB_GSPCA_TOPRO + tristate "TOPRO USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + TP6800 and TP6810 Topro chips. + + To compile this driver as a module, choose M here: the + module will be called gspca_topro. + +config USB_GSPCA_TV8532 + tristate "TV8532 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the TV8531 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_tv8532. + +config USB_GSPCA_VC032X + tristate "VC032X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the VC032X chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_vc032x. + +config USB_GSPCA_VICAM + tristate "ViCam USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for the 3com homeconnect camera + (vicam). + + To compile this driver as a module, choose M here: the + module will be called gspca_vicam. + +config USB_GSPCA_XIRLINK_CIT + tristate "Xirlink C-It USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for Xirlink C-It bases cameras. + + To compile this driver as a module, choose M here: the + module will be called gspca_xirlink_cit. + +config USB_GSPCA_ZC3XX + tristate "ZC3XX USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the ZC3XX chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_zc3xx. + +endif diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile new file mode 100644 index 000000000000..c901da0bd657 --- /dev/null +++ b/drivers/media/usb/gspca/Makefile @@ -0,0 +1,93 @@ +obj-$(CONFIG_USB_GSPCA) += gspca_main.o +obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o +obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o +obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o +obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o +obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o +obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o +obj-$(CONFIG_USB_GSPCA_JL2005BCD) += gspca_jl2005bcd.o +obj-$(CONFIG_USB_GSPCA_KINECT) += gspca_kinect.o +obj-$(CONFIG_USB_GSPCA_KONICA) += gspca_konica.o +obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o +obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o +obj-$(CONFIG_USB_GSPCA_NW80X) += gspca_nw80x.o +obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o +obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o +obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o +obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o +obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SE401) += gspca_se401.o +obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o +obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o +obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o +obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o +obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o +obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o +obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o +obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o +obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o +obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o +obj-$(CONFIG_USB_GSPCA_SPCA1528) += gspca_spca1528.o +obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o +obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o +obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o +obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o +obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o +obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o +obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o +obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o +obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o +obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o +obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o +obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o + +gspca_main-objs := gspca.o autogain_functions.o +gspca_benq-objs := benq.o +gspca_conex-objs := conex.o +gspca_cpia1-objs := cpia1.o +gspca_etoms-objs := etoms.o +gspca_finepix-objs := finepix.o +gspca_jeilinj-objs := jeilinj.o +gspca_jl2005bcd-objs := jl2005bcd.o +gspca_kinect-objs := kinect.o +gspca_konica-objs := konica.o +gspca_mars-objs := mars.o +gspca_mr97310a-objs := mr97310a.o +gspca_nw80x-objs := nw80x.o +gspca_ov519-objs := ov519.o +gspca_ov534-objs := ov534.o +gspca_ov534_9-objs := ov534_9.o +gspca_pac207-objs := pac207.o +gspca_pac7302-objs := pac7302.o +gspca_pac7311-objs := pac7311.o +gspca_se401-objs := se401.o +gspca_sn9c2028-objs := sn9c2028.o +gspca_sn9c20x-objs := sn9c20x.o +gspca_sonixb-objs := sonixb.o +gspca_sonixj-objs := sonixj.o +gspca_spca500-objs := spca500.o +gspca_spca501-objs := spca501.o +gspca_spca505-objs := spca505.o +gspca_spca506-objs := spca506.o +gspca_spca508-objs := spca508.o +gspca_spca561-objs := spca561.o +gspca_spca1528-objs := spca1528.o +gspca_sq905-objs := sq905.o +gspca_sq905c-objs := sq905c.o +gspca_sq930x-objs := sq930x.o +gspca_stk014-objs := stk014.o +gspca_stv0680-objs := stv0680.o +gspca_sunplus-objs := sunplus.o +gspca_t613-objs := t613.o +gspca_topro-objs := topro.o +gspca_tv8532-objs := tv8532.o +gspca_vc032x-objs := vc032x.o +gspca_vicam-objs := vicam.o +gspca_xirlink_cit-objs := xirlink_cit.o +gspca_zc3xx-objs := zc3xx.o + +obj-$(CONFIG_USB_M5602) += m5602/ +obj-$(CONFIG_USB_STV06XX) += stv06xx/ +obj-$(CONFIG_USB_GL860) += gl860/ diff --git a/drivers/media/usb/gspca/autogain_functions.c b/drivers/media/usb/gspca/autogain_functions.c new file mode 100644 index 000000000000..67db674bb044 --- /dev/null +++ b/drivers/media/usb/gspca/autogain_functions.c @@ -0,0 +1,178 @@ +/* + * Functions for auto gain. + * + * Copyright (C) 2010-2012 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "gspca.h" + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone, + int gain_knee, + int exposure_knee) +{ + s32 gain, orig_gain, exposure, orig_exposure; + int i, steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = abs(desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > gspca_dev->gain->default_value) + gain--; + else if (exposure > gspca_dev->exposure->minimum) + exposure--; + else if (gain > gspca_dev->gain->minimum) + gain--; + else + break; + } else { + if (gain < gspca_dev->gain->default_value) + gain++; + else if (exposure < exposure_knee) + exposure++; + else if (gain < gain_knee) + gain++; + else if (exposure < gspca_dev->exposure->maximum) + exposure++; + else if (gain < gspca_dev->gain->maximum) + gain++; + else + break; + } + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_expo_autogain); + +/* Autogain + exposure algorithm for cameras with a coarse exposure control + (usually this means we can only control the clockdiv to change exposure) + As changing the clockdiv so that the fps drops from 30 to 15 fps for + example, will lead to a huge exposure change (it effectively doubles), + this algorithm normally tries to only adjust the gain (between 40 and + 80 %) and if that does not help, only then changes exposure. This leads + to a much more stable image then using the knee algorithm which at + certain points of the knee graph will only try to adjust exposure, + which leads to oscilating as one exposure step is huge. + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_coarse_grained_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone) +{ + s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure; + int steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 2 + gspca_dev->gain->minimum; + gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 4 + gspca_dev->gain->minimum; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = (desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + if ((gain + steps) > gain_high && + exposure < gspca_dev->exposure->maximum) { + gain = gain_high; + gspca_dev->exp_too_low_cnt++; + gspca_dev->exp_too_high_cnt = 0; + } else if ((gain + steps) < gain_low && + exposure > gspca_dev->exposure->minimum) { + gain = gain_low; + gspca_dev->exp_too_high_cnt++; + gspca_dev->exp_too_low_cnt = 0; + } else { + gain += steps; + if (gain > gspca_dev->gain->maximum) + gain = gspca_dev->gain->maximum; + else if (gain < gspca_dev->gain->minimum) + gain = gspca_dev->gain->minimum; + gspca_dev->exp_too_high_cnt = 0; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gspca_dev->exp_too_high_cnt > 3) { + exposure--; + gspca_dev->exp_too_high_cnt = 0; + } else if (gspca_dev->exp_too_low_cnt > 3) { + exposure++; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain); diff --git a/drivers/media/usb/gspca/autogain_functions.h b/drivers/media/usb/gspca/autogain_functions.h new file mode 100644 index 000000000000..d625eafe63eb --- /dev/null +++ b/drivers/media/usb/gspca/autogain_functions.h @@ -0,0 +1,183 @@ +/* + * Functions for auto gain. + * + * Copyright (C) 2010-2011 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef WANT_REGULAR_AUTOGAIN +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +static inline int auto_gain_n_exposure( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone, + int gain_knee, + int exposure_knee) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, steps, gain, orig_gain, exposure, orig_exposure; + int retval = 0; + + orig_gain = gain = sd->ctrls[GAIN].val; + orig_exposure = exposure = sd->ctrls[EXPOSURE].val; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = abs(desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > sd->ctrls[GAIN].def) + gain--; + else if (exposure > sd->ctrls[EXPOSURE].min) + exposure--; + else if (gain > sd->ctrls[GAIN].min) + gain--; + else + break; + } else { + if (gain < sd->ctrls[GAIN].def) + gain++; + else if (exposure < exposure_knee) + exposure++; + else if (gain < gain_knee) + gain++; + else if (exposure < sd->ctrls[EXPOSURE].max) + exposure++; + else if (gain < sd->ctrls[GAIN].max) + gain++; + else + break; + } + } + + if (gain != orig_gain) { + sd->ctrls[GAIN].val = gain; + setgain(gspca_dev); + retval = 1; + } + if (exposure != orig_exposure) { + sd->ctrls[EXPOSURE].val = exposure; + setexposure(gspca_dev); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +#endif + +#ifdef WANT_COARSE_EXPO_AUTOGAIN +/* Autogain + exposure algorithm for cameras with a coarse exposure control + (usually this means we can only control the clockdiv to change exposure) + As changing the clockdiv so that the fps drops from 30 to 15 fps for + example, will lead to a huge exposure change (it effectively doubles), + this algorithm normally tries to only adjust the gain (between 40 and + 80 %) and if that does not help, only then changes exposure. This leads + to a much more stable image then using the knee algorithm which at + certain points of the knee graph will only try to adjust exposure, + which leads to oscilating as one exposure step is huge. + + Note this assumes that the sd struct for the cam in question has + exp_too_low_cnt and exp_too_high_cnt int members for use by this function. + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +static inline int coarse_grained_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone) +{ + struct sd *sd = (struct sd *) gspca_dev; + int steps, gain, orig_gain, exposure, orig_exposure; + int gain_low, gain_high; + int retval = 0; + + orig_gain = gain = sd->ctrls[GAIN].val; + orig_exposure = exposure = sd->ctrls[EXPOSURE].val; + + gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2; + gain_low += sd->ctrls[GAIN].min; + gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4; + gain_high += sd->ctrls[GAIN].min; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = (desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + if ((gain + steps) > gain_high && + exposure < sd->ctrls[EXPOSURE].max) { + gain = gain_high; + sd->exp_too_low_cnt++; + sd->exp_too_high_cnt = 0; + } else if ((gain + steps) < gain_low && + exposure > sd->ctrls[EXPOSURE].min) { + gain = gain_low; + sd->exp_too_high_cnt++; + sd->exp_too_low_cnt = 0; + } else { + gain += steps; + if (gain > sd->ctrls[GAIN].max) + gain = sd->ctrls[GAIN].max; + else if (gain < sd->ctrls[GAIN].min) + gain = sd->ctrls[GAIN].min; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; + } + + if (sd->exp_too_high_cnt > 3) { + exposure--; + sd->exp_too_high_cnt = 0; + } else if (sd->exp_too_low_cnt > 3) { + exposure++; + sd->exp_too_low_cnt = 0; + } + + if (gain != orig_gain) { + sd->ctrls[GAIN].val = gain; + setgain(gspca_dev); + retval = 1; + } + if (exposure != orig_exposure) { + sd->ctrls[EXPOSURE].val = exposure; + setexposure(gspca_dev); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +#endif diff --git a/drivers/media/usb/gspca/benq.c b/drivers/media/usb/gspca/benq.c new file mode 100644 index 000000000000..352f32190e68 --- /dev/null +++ b/drivers/media/usb/gspca/benq.c @@ -0,0 +1,289 @@ +/* + * Benq DC E300 subdriver + * + * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "benq" + +#include "gspca.h" + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static void sd_isoc_irq(struct urb *urb); + +/* -- write a register -- */ +static void reg_w(struct gspca_dev *gspca_dev, + u16 value, u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.no_urb_create = 1; + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct urb *urb; + int i, n; + + /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */ +#if MAX_NURBS < 4 +#error "Not enough URBs in the gspca table" +#endif +#define SD_PKT_SZ 64 +#define SD_NPKT 32 + for (n = 0; n < 4; n++) { + urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); + if (!urb) { + pr_err("usb_alloc_urb failed\n"); + return -ENOMEM; + } + gspca_dev->urb[n] = urb; + urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, + SD_PKT_SZ * SD_NPKT, + GFP_KERNEL, + &urb->transfer_dma); + + if (urb->transfer_buffer == NULL) { + pr_err("usb_alloc_coherent failed\n"); + return -ENOMEM; + } + urb->dev = gspca_dev->dev; + urb->context = gspca_dev; + urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT; + urb->pipe = usb_rcvisocpipe(gspca_dev->dev, + n & 1 ? 0x82 : 0x83); + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; + urb->interval = 1; + urb->complete = sd_isoc_irq; + urb->number_of_packets = SD_NPKT; + for (i = 0; i < SD_NPKT; i++) { + urb->iso_frame_desc[i].length = SD_PKT_SZ; + urb->iso_frame_desc[i].offset = SD_PKT_SZ * i; + } + } + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_interface *intf; + + reg_w(gspca_dev, 0x003c, 0x0003); + reg_w(gspca_dev, 0x003c, 0x0004); + reg_w(gspca_dev, 0x003c, 0x0005); + reg_w(gspca_dev, 0x003c, 0x0006); + reg_w(gspca_dev, 0x003c, 0x0007); + + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + usb_set_interface(gspca_dev->dev, gspca_dev->iface, + intf->num_altsetting - 1); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* unused */ +} + +/* reception of an URB */ +static void sd_isoc_irq(struct urb *urb) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + struct urb *urb0; + u8 *data; + int i, st; + + PDEBUG(D_PACK, "sd isoc irq"); + if (!gspca_dev->streaming) + return; + if (urb->status != 0) { + if (urb->status == -ESHUTDOWN) + return; /* disconnection */ +#ifdef CONFIG_PM + if (gspca_dev->frozen) + return; +#endif + pr_err("urb status: %d\n", urb->status); + return; + } + + /* if this is a control URN (ep 0x83), wait */ + if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2]) + return; + + /* scan both received URBs */ + if (urb == gspca_dev->urb[1]) + urb0 = gspca_dev->urb[0]; + else + urb0 = gspca_dev->urb[2]; + for (i = 0; i < urb->number_of_packets; i++) { + + /* check the packet status and length */ + if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ + || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) { + PDEBUG(D_ERR, "ISOC bad lengths %d / %d", + urb0->iso_frame_desc[i].actual_length, + urb->iso_frame_desc[i].actual_length); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + st = urb0->iso_frame_desc[i].status; + if (st == 0) + st = urb->iso_frame_desc[i].status; + if (st) { + pr_err("ISOC data error: [%d] status=%d\n", + i, st); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + + /* + * The images are received in URBs of different endpoints + * (0x83 and 0x82). + * Image pieces in URBs of ep 0x83 are continuated in URBs of + * ep 0x82 of the same index. + * The packets in the URBs of endpoint 0x83 start with: + * - 80 ba/bb 00 00 = start of image followed by 'ff d8' + * - 04 ba/bb oo oo = image piece + * where 'oo oo' is the image offset + (not cheked) + * - (other -> bad frame) + * The images are JPEG encoded with full header and + * normal ff escape. + * The end of image ('ff d9') may occur in any URB. + * (not cheked) + */ + data = (u8 *) urb0->transfer_buffer + + urb0->iso_frame_desc[i].offset; + if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) { + + /* new image */ + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + data + 4, SD_PKT_SZ - 4); + } else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 4, SD_PKT_SZ - 4); + } else { + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + data = (u8 *) urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + gspca_frame_add(gspca_dev, INTER_PACKET, + data, SD_PKT_SZ); + } + + /* resubmit the URBs */ + st = usb_submit_urb(urb0, GFP_ATOMIC); + if (st < 0) + pr_err("usb_submit_urb(0) ret %d\n", st); + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + pr_err("usb_submit_urb() ret %d\n", st); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x04a5, 0x3035)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/conex.c b/drivers/media/usb/gspca/conex.c new file mode 100644 index 000000000000..c9052f20435e --- /dev/null +++ b/drivers/media/usb/gspca/conex.c @@ -0,0 +1,966 @@ +/* + * Connexant Cx11646 library + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "conex" + +#include "gspca.h" +#define CONEX_CAM 1 /* special JPEG header */ +#include "jpeg.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver"); +MODULE_LICENSE("GPL"); + +#define QUALITY 50 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *sat; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* the read bytes are found in gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + pr_err("reg_r: buffer overflow\n"); + return; + } +#endif + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, gspca_dev->usb_buf, len, + 500); + PDEBUG(D_USBI, "reg read [%02x] -> %02x ..", + index, gspca_dev->usb_buf[0]); +} + +/* the bytes to write are in gspca_dev->usb_buf */ +static void reg_w_val(struct gspca_dev *gspca_dev, + __u16 index, + __u8 val) +{ + struct usb_device *dev = gspca_dev->dev; + + gspca_dev->usb_buf[0] = val; + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, gspca_dev->usb_buf, 1, 500); +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 index, + const __u8 *buffer, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + pr_err("reg_w: buffer overflow\n"); + return; + } + PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, gspca_dev->usb_buf, len, 500); +} + +static const __u8 cx_sensor_init[][4] = { + {0x88, 0x11, 0x01, 0x01}, + {0x88, 0x12, 0x70, 0x01}, + {0x88, 0x0f, 0x00, 0x01}, + {0x88, 0x05, 0x01, 0x01}, + {} +}; + +static const __u8 cx11646_fw1[][3] = { + {0x00, 0x02, 0x00}, + {0x01, 0x43, 0x00}, + {0x02, 0xA7, 0x00}, + {0x03, 0x8B, 0x01}, + {0x04, 0xE9, 0x02}, + {0x05, 0x08, 0x04}, + {0x06, 0x08, 0x05}, + {0x07, 0x07, 0x06}, + {0x08, 0xE7, 0x06}, + {0x09, 0xC6, 0x07}, + {0x0A, 0x86, 0x08}, + {0x0B, 0x46, 0x09}, + {0x0C, 0x05, 0x0A}, + {0x0D, 0xA5, 0x0A}, + {0x0E, 0x45, 0x0B}, + {0x0F, 0xE5, 0x0B}, + {0x10, 0x85, 0x0C}, + {0x11, 0x25, 0x0D}, + {0x12, 0xC4, 0x0D}, + {0x13, 0x45, 0x0E}, + {0x14, 0xE4, 0x0E}, + {0x15, 0x64, 0x0F}, + {0x16, 0xE4, 0x0F}, + {0x17, 0x64, 0x10}, + {0x18, 0xE4, 0x10}, + {0x19, 0x64, 0x11}, + {0x1A, 0xE4, 0x11}, + {0x1B, 0x64, 0x12}, + {0x1C, 0xE3, 0x12}, + {0x1D, 0x44, 0x13}, + {0x1E, 0xC3, 0x13}, + {0x1F, 0x24, 0x14}, + {0x20, 0xA3, 0x14}, + {0x21, 0x04, 0x15}, + {0x22, 0x83, 0x15}, + {0x23, 0xE3, 0x15}, + {0x24, 0x43, 0x16}, + {0x25, 0xA4, 0x16}, + {0x26, 0x23, 0x17}, + {0x27, 0x83, 0x17}, + {0x28, 0xE3, 0x17}, + {0x29, 0x43, 0x18}, + {0x2A, 0xA3, 0x18}, + {0x2B, 0x03, 0x19}, + {0x2C, 0x63, 0x19}, + {0x2D, 0xC3, 0x19}, + {0x2E, 0x22, 0x1A}, + {0x2F, 0x63, 0x1A}, + {0x30, 0xC3, 0x1A}, + {0x31, 0x23, 0x1B}, + {0x32, 0x83, 0x1B}, + {0x33, 0xE2, 0x1B}, + {0x34, 0x23, 0x1C}, + {0x35, 0x83, 0x1C}, + {0x36, 0xE2, 0x1C}, + {0x37, 0x23, 0x1D}, + {0x38, 0x83, 0x1D}, + {0x39, 0xE2, 0x1D}, + {0x3A, 0x23, 0x1E}, + {0x3B, 0x82, 0x1E}, + {0x3C, 0xC3, 0x1E}, + {0x3D, 0x22, 0x1F}, + {0x3E, 0x63, 0x1F}, + {0x3F, 0xC1, 0x1F}, + {} +}; +static void cx11646_fw(struct gspca_dev*gspca_dev) +{ + int i = 0; + + reg_w_val(gspca_dev, 0x006a, 0x02); + while (cx11646_fw1[i][1]) { + reg_w(gspca_dev, 0x006b, cx11646_fw1[i], 3); + i++; + } + reg_w_val(gspca_dev, 0x006a, 0x00); +} + +static const __u8 cxsensor[] = { + 0x88, 0x12, 0x70, 0x01, + 0x88, 0x0d, 0x02, 0x01, + 0x88, 0x0f, 0x00, 0x01, + 0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, /* 3 */ + 0x88, 0x02, 0x10, 0x01, + 0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, /* 5 */ + 0x88, 0x0B, 0x00, 0x01, + 0x88, 0x0A, 0x0A, 0x01, + 0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, /* 8 */ + 0x88, 0x05, 0x01, 0x01, + 0xA1, 0x18, 0x00, 0x01, + 0x00 +}; + +static const __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff }; +static const __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff }; +static const __u8 reg10[] = { 0xb1, 0xb1 }; +static const __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e }; /* 640 */ +static const __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f }; + /* 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 */ +static const __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 }; + /* 320{0x04,0x0c,0x05,0x0f}; //320 */ +static const __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 }; /* 176 */ +static const __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }; + +static void cx_sensor(struct gspca_dev*gspca_dev) +{ + int i = 0; + int length; + const __u8 *ptsensor = cxsensor; + + reg_w(gspca_dev, 0x0020, reg20, 8); + reg_w(gspca_dev, 0x0028, reg28, 8); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0092, 0x03); + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + reg_w(gspca_dev, 0x0071, reg71a, 4); + break; + case 1: + reg_w(gspca_dev, 0x0071, reg71b, 4); + break; + default: +/* case 2: */ + reg_w(gspca_dev, 0x0071, reg71c, 4); + break; + case 3: + reg_w(gspca_dev, 0x0071, reg71d, 4); + break; + } + reg_w(gspca_dev, 0x007b, reg7b, 6); + reg_w_val(gspca_dev, 0x00f8, 0x00); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0098, 0x41); + for (i = 0; i < 11; i++) { + if (i == 3 || i == 5 || i == 8) + length = 8; + else + length = 4; + reg_w(gspca_dev, 0x00e5, ptsensor, length); + if (length == 4) + reg_r(gspca_dev, 0x00e8, 1); + else + reg_r(gspca_dev, 0x00e8, length); + ptsensor += length; + } + reg_r(gspca_dev, 0x00e7, 8); +} + +static const __u8 cx_inits_176[] = { + 0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, /* 176x144 */ + 0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03, + 0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30, + 0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF, + 0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02, + 0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const __u8 cx_inits_320[] = { + 0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01, + 0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02, + 0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const __u8 cx_inits_352[] = { + 0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03, + 0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b, + 0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25, + 0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00, + 0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02, + 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const __u8 cx_inits_640[] = { + 0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01, + 0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02, + 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static void cx11646_initsize(struct gspca_dev *gspca_dev) +{ + const __u8 *cxinit; + static const __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 }; + static const __u8 reg17[] = + { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 }; + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + cxinit = cx_inits_640; + break; + case 1: + cxinit = cx_inits_352; + break; + default: +/* case 2: */ + cxinit = cx_inits_320; + break; + case 3: + cxinit = cx_inits_176; + break; + } + reg_w_val(gspca_dev, 0x009a, 0x01); + reg_w_val(gspca_dev, 0x0010, 0x10); + reg_w(gspca_dev, 0x0012, reg12, 5); + reg_w(gspca_dev, 0x0017, reg17, 8); + reg_w_val(gspca_dev, 0x00c0, 0x00); + reg_w_val(gspca_dev, 0x00c1, 0x04); + reg_w_val(gspca_dev, 0x00c2, 0x04); + + reg_w(gspca_dev, 0x0061, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x00ca, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x00d2, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x00da, cxinit, 6); + cxinit += 8; + reg_w(gspca_dev, 0x0041, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x0049, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x0051, cxinit, 2); + + reg_r(gspca_dev, 0x0010, 1); +} + +static const __u8 cx_jpeg_init[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x15}, /* 1 */ + {0x0f, 0x10, 0x12, 0x10, 0x0d, 0x15, 0x12, 0x11}, + {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22}, + {0x20, 0x1d, 0x1d, 0x20, 0x41, 0x2e, 0x31, 0x26}, + {0x35, 0x4d, 0x43, 0x51, 0x4f, 0x4b, 0x43, 0x4a}, + {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73}, + {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D}, + {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0}, + {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01}, + {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12}, + {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35}, + {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31}, + {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43}, + {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A}, + {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73}, + {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95}, + {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83}, + {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, + {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, + {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, + {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00}, + {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05}, + {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01}, + {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21}, + {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22}, + {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23}, + {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24}, + {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17}, + {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29}, + {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A}, + {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A}, + {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A}, + {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A}, + {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A}, + {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A}, + {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99}, + {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8}, + {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, + {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6}, + {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5}, + {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3}, + {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1}, + {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9}, + {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04}, + {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01}, + {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04}, + {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07}, + {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14}, + {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33}, + {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16}, + {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19}, + {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36}, + {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46}, + {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56}, + {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66}, + {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76}, + {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85}, + {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94}, + {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3}, + {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2}, + {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA}, + {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9}, + {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8}, + {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7}, + {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6}, + {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F}, + {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22}, + {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11}, + {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00}, + {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08}, + {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00}, + {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA}, + {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02}, + {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00} /* 79 */ +}; + + +static const __u8 cxjpeg_640[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x10}, /* 1 */ + {0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d}, + {0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a}, + {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d}, + {0x28, 0x3a, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38}, + {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57}, + {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F}, + {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79}, + {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01}, + {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E}, + {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28}, + {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25}, + {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33}, + {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44}, + {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57}, + {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71}, + {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63}, + {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static const __u8 cxjpeg_352[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0b, 0x09, 0x08, 0x0D, 0x0b, 0x0a}, + {0x0b, 0x0e, 0x0d, 0x0d, 0x0f, 0x13, 0x1f, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1b, 0x1d, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +static const __u8 cxjpeg_320[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x05}, + {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04}, + {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08}, + {0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09}, + {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0f, 0x11}, + {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A}, + {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D}, + {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24}, + {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01}, + {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04}, + {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C}, + {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B}, + {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F}, + {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14}, + {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A}, + {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22}, + {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E}, + {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static const __u8 cxjpeg_176[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, + {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +/* 640 take with the zcx30x part */ +static const __u8 cxjpeg_qtable[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08}, + {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07}, + {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a}, + {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f}, + {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c}, + {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c}, + {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30}, + {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d}, + {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01}, + {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a}, + {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 18 */ +}; + + +static void cx11646_jpegInit(struct gspca_dev*gspca_dev) +{ + int i; + int length; + + reg_w_val(gspca_dev, 0x00c0, 0x01); + reg_w_val(gspca_dev, 0x00c3, 0x00); + reg_w_val(gspca_dev, 0x00c0, 0x00); + reg_r(gspca_dev, 0x0001, 1); + length = 8; + for (i = 0; i < 79; i++) { + if (i == 78) + length = 6; + reg_w(gspca_dev, 0x0008, cx_jpeg_init[i], length); + } + reg_r(gspca_dev, 0x0002, 1); + reg_w_val(gspca_dev, 0x0055, 0x14); +} + +static const __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 }; +static const __u8 regE5_8[] = + { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; +static const __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 }; +static const __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 }; +static const __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 }; +static const __u8 reg51[] = { 0x77, 0x03 }; +#define reg70 0x03 + +static void cx11646_jpeg(struct gspca_dev*gspca_dev) +{ + int i; + int length; + __u8 Reg55; + int retry; + + reg_w_val(gspca_dev, 0x00c0, 0x01); + reg_w_val(gspca_dev, 0x00c3, 0x00); + reg_w_val(gspca_dev, 0x00c0, 0x00); + reg_r(gspca_dev, 0x0001, 1); + length = 8; + switch (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv) { + case 0: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_640[i], length); + } + Reg55 = 0x28; + break; + case 1: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_352[i], length); + } + Reg55 = 0x16; + break; + default: +/* case 2: */ + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_320[i], length); + } + Reg55 = 0x14; + break; + case 3: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_176[i], length); + } + Reg55 = 0x0B; + break; + } + + reg_r(gspca_dev, 0x0002, 1); + reg_w_val(gspca_dev, 0x0055, Reg55); + reg_r(gspca_dev, 0x0002, 1); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0054, 0x02); + reg_w_val(gspca_dev, 0x0054, 0x01); + reg_w_val(gspca_dev, 0x0000, 0x94); + reg_w_val(gspca_dev, 0x0053, 0xc0); + reg_w_val(gspca_dev, 0x00fc, 0xe1); + reg_w_val(gspca_dev, 0x0000, 0x00); + /* wait for completion */ + retry = 50; + do { + reg_r(gspca_dev, 0x0002, 1); + /* 0x07 until 0x00 */ + if (gspca_dev->usb_buf[0] == 0x00) + break; + reg_w_val(gspca_dev, 0x0053, 0x00); + } while (--retry); + if (retry == 0) + PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); + /* send the qtable now */ + reg_r(gspca_dev, 0x0001, 1); /* -> 0x18 */ + length = 8; + for (i = 0; i < 18; i++) { + if (i == 17) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_qtable[i], length); + + } + reg_r(gspca_dev, 0x0002, 1); /* 0x00 */ + reg_r(gspca_dev, 0x0053, 1); /* 0x00 */ + reg_w_val(gspca_dev, 0x0054, 0x02); + reg_w_val(gspca_dev, 0x0054, 0x01); + reg_w_val(gspca_dev, 0x0000, 0x94); + reg_w_val(gspca_dev, 0x0053, 0xc0); + + reg_r(gspca_dev, 0x0038, 1); /* 0x40 */ + reg_r(gspca_dev, 0x0038, 1); /* 0x40 */ + reg_r(gspca_dev, 0x001f, 1); /* 0x38 */ + reg_w(gspca_dev, 0x0012, reg12, 5); + reg_w(gspca_dev, 0x00e5, regE5_8, 8); + reg_r(gspca_dev, 0x00e8, 8); + reg_w(gspca_dev, 0x00e5, regE5a, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + reg_w_val(gspca_dev, 0x009a, 0x01); + reg_w(gspca_dev, 0x00e5, regE5b, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + reg_w(gspca_dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + + reg_w(gspca_dev, 0x0051, reg51, 2); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0070, reg70); +} + +static void cx11646_init1(struct gspca_dev *gspca_dev) +{ + int i = 0; + + reg_w_val(gspca_dev, 0x0010, 0x00); + reg_w_val(gspca_dev, 0x0053, 0x00); + reg_w_val(gspca_dev, 0x0052, 0x00); + reg_w_val(gspca_dev, 0x009b, 0x2f); + reg_w_val(gspca_dev, 0x009c, 0x10); + reg_r(gspca_dev, 0x0098, 1); + reg_w_val(gspca_dev, 0x0098, 0x40); + reg_r(gspca_dev, 0x0099, 1); + reg_w_val(gspca_dev, 0x0099, 0x07); + reg_w_val(gspca_dev, 0x0039, 0x40); + reg_w_val(gspca_dev, 0x003c, 0xff); + reg_w_val(gspca_dev, 0x003f, 0x1f); + reg_w_val(gspca_dev, 0x003d, 0x40); +/* reg_w_val(gspca_dev, 0x003d, 0x60); */ + reg_r(gspca_dev, 0x0099, 1); /* ->0x07 */ + + while (cx_sensor_init[i][0]) { + reg_w_val(gspca_dev, 0x00e5, cx_sensor_init[i][0]); + reg_r(gspca_dev, 0x00e8, 1); /* -> 0x00 */ + if (i == 1) { + reg_w_val(gspca_dev, 0x00ed, 0x01); + reg_r(gspca_dev, 0x00ed, 1); /* -> 0x01 */ + } + i++; + } + reg_w_val(gspca_dev, 0x00c3, 0x00); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + cx11646_init1(gspca_dev); + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpegInit(gspca_dev); + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* create the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, QUALITY); + + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpeg(gspca_dev); + return 0; +} + +/* called on streamoff with alt 0 and on disconnect */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + int retry = 50; + + if (!gspca_dev->present) + return; + reg_w_val(gspca_dev, 0x0000, 0x00); + reg_r(gspca_dev, 0x0002, 1); + reg_w_val(gspca_dev, 0x0053, 0x00); + + while (retry--) { +/* reg_r(gspca_dev, 0x0002, 1);*/ + reg_r(gspca_dev, 0x0053, 1); + if (gspca_dev->usb_buf[0] == 0) + break; + } + reg_w_val(gspca_dev, 0x0000, 0x00); + reg_r(gspca_dev, 0x0002, 1); + + reg_w_val(gspca_dev, 0x0010, 0x00); + reg_r(gspca_dev, 0x0033, 1); + reg_w_val(gspca_dev, 0x00fc, 0xe0); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] == 0xff && data[1] == 0xd8) { + + /* start of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* put the JPEG header in the new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + data += 2; + len -= 2; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val, s32 sat) +{ + __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; + __u8 reg51c[2]; + + regE5cbx[2] = val; + reg_w(gspca_dev, 0x00e5, regE5cbx, 8); + reg_r(gspca_dev, 0x00e8, 8); + reg_w(gspca_dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + + reg51c[0] = 0x77; + reg51c[1] = sat; + reg_w(gspca_dev, 0x0051, reg51c, 2); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0070, reg70); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val, s32 sat) +{ + __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */ +/* __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01}; * LSB */ + __u8 reg51c[2]; + + regE5acx[2] = val; + reg_w(gspca_dev, 0x00e5, regE5acx, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + reg51c[0] = 0x77; + reg51c[1] = sat; + reg_w(gspca_dev, 0x0051, reg51c, 2); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0070, reg70); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val, sd->sat->cur.val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val, sd->sat->cur.val); + break; + case V4L2_CID_SATURATION: + setbrightness(gspca_dev, sd->brightness->cur.val, ctrl->val); + setcontrast(gspca_dev, sd->contrast->cur.val, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 3); + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 0xd4); + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0x0a, 0x1f, 1, 0x0c); + sd->sat = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 7, 1, 3); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0572, 0x0041)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c new file mode 100644 index 000000000000..2499a881d9a3 --- /dev/null +++ b/drivers/media/usb/gspca/cpia1.c @@ -0,0 +1,1905 @@ +/* + * cpia CPiA (1) gspca driver + * + * Copyright (C) 2010-2011 Hans de Goede + * + * This module is adapted from the in kernel v4l1 cpia driver which is : + * + * (C) Copyright 1999-2000 Peter Pregler + * (C) Copyright 1999-2000 Scott J. Bertin + * (C) Copyright 1999-2000 Johannes Erdfelt + * (C) Copyright 2000 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "cpia1" + +#include +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Vision CPiA"); +MODULE_LICENSE("GPL"); + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xc0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINEOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* default (minimum) compensation values */ +#define COMP_RED 220 +#define COMP_GREEN1 214 +#define COMP_GREEN2 COMP_GREEN1 +#define COMP_BLUE 230 + +/* exposure status */ +#define EXPOSURE_VERY_LIGHT 0 +#define EXPOSURE_LIGHT 1 +#define EXPOSURE_NORMAL 2 +#define EXPOSURE_DARK 3 +#define EXPOSURE_VERY_DARK 4 + +#define CPIA_MODULE_CPIA (0 << 5) +#define CPIA_MODULE_SYSTEM (1 << 5) +#define CPIA_MODULE_VP_CTRL (5 << 5) +#define CPIA_MODULE_CAPTURE (6 << 5) +#define CPIA_MODULE_DEBUG (7 << 5) + +#define INPUT (DATA_IN << 8) +#define OUTPUT (DATA_OUT << 8) + +#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) +#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) +#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) +#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) +#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) +#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) +#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) +#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) + +#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) +#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) +#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) +#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) +#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) +#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) +#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) +#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) +#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) +#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) +#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) +#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) +#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) + +#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) +#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) +#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) +#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) +#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) +#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) +#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) +#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) +#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) +#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) +#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) +#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) +#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) +#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) +#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) +#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) + +#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) +#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) +#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) +#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) +#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) +#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) +#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) +#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) +#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) +#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) +#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) +#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) +#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) +#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) +#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) + +#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) +#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) +#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) +#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) +#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) +#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) +#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) + +#define ROUND_UP_EXP_FOR_FLICKER 15 + +/* Constants for automatic frame rate adjustment */ +#define MAX_EXP 302 +#define MAX_EXP_102 255 +#define LOW_EXP 140 +#define VERY_LOW_EXP 70 +#define TC 94 +#define EXP_ACC_DARK 50 +#define EXP_ACC_LIGHT 90 +#define HIGH_COMP_102 160 +#define MAX_COMP 239 +#define DARK_TIME 3 +#define LIGHT_TIME 3 + +#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \ + sd->params.version.firmwareRevision == (y)) + +#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000) +#define BRIGHTNESS_DEF 50 +#define CONTRAST_DEF 48 +#define SATURATION_DEF 50 +#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ +#define ILLUMINATORS_1_DEF 0 +#define ILLUMINATORS_2_DEF 0 +#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY + +/* Developer's Guide Table 5 p 3-34 + * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ +static u8 flicker_jumps[2][2][4] = +{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, + { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceMode; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 disabled; + u8 flickerMode; + u8 coarseJump; + u8 allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + struct { /* Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct cam_params params; /* camera settings */ + + atomic_t cam_exposure; + atomic_t fps; + int exposure_count; + u8 exposure_status; + struct v4l2_ctrl *freq; + u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */ + u8 first_frame; +}; + +static const struct v4l2_pix_format mode[] = { + {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + /* The sizeimage is trial and error, as with low framerates + the camera will pad out usb frames, making the image + data larger then strictly necessary */ + .bytesperline = 160, + .sizeimage = 65536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + .bytesperline = 172, + .sizeimage = 65536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 262144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 262144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/********************************************************************** + * + * General functions + * + **********************************************************************/ + +static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command) +{ + u8 requesttype; + unsigned int pipe; + int ret, databytes = command[6] | (command[7] << 8); + /* Sometimes we see spurious EPIPE errors */ + int retries = 3; + + if (command[0] == DATA_IN) { + pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); + requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + } else if (command[0] == DATA_OUT) { + pipe = usb_sndctrlpipe(gspca_dev->dev, 0); + requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE; + } else { + PDEBUG(D_ERR, "Unexpected first byte of command: %x", + command[0]); + return -EINVAL; + } + +retry: + ret = usb_control_msg(gspca_dev->dev, pipe, + command[1], + requesttype, + command[2] | (command[3] << 8), + command[4] | (command[5] << 8), + gspca_dev->usb_buf, databytes, 1000); + + if (ret < 0) + pr_err("usb_control_msg %02x, error %d\n", command[1], ret); + + if (ret == -EPIPE && retries > 0) { + retries--; + goto retry; + } + + return (ret < 0) ? ret : 0; +} + +/* send an arbitrary command to the camera */ +static int do_command(struct gspca_dev *gspca_dev, u16 command, + u8 a, u8 b, u8 c, u8 d) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret, datasize; + u8 cmd[8]; + + switch (command) { + case CPIA_COMMAND_GetCPIAVersion: + case CPIA_COMMAND_GetPnPID: + case CPIA_COMMAND_GetCameraStatus: + case CPIA_COMMAND_GetVPVersion: + case CPIA_COMMAND_GetColourParams: + case CPIA_COMMAND_GetColourBalance: + case CPIA_COMMAND_GetExposure: + datasize = 8; + break; + case CPIA_COMMAND_ReadMCPorts: + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + break; + default: + datasize = 0; + break; + } + + cmd[0] = command >> 8; + cmd[1] = command & 0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = datasize; + cmd[7] = 0; + + ret = cpia_usb_transferCmd(gspca_dev, cmd); + if (ret) + return ret; + + switch (command) { + case CPIA_COMMAND_GetCPIAVersion: + sd->params.version.firmwareVersion = gspca_dev->usb_buf[0]; + sd->params.version.firmwareRevision = gspca_dev->usb_buf[1]; + sd->params.version.vcVersion = gspca_dev->usb_buf[2]; + sd->params.version.vcRevision = gspca_dev->usb_buf[3]; + break; + case CPIA_COMMAND_GetPnPID: + sd->params.pnpID.vendor = + gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8); + sd->params.pnpID.product = + gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8); + sd->params.pnpID.deviceRevision = + gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8); + break; + case CPIA_COMMAND_GetCameraStatus: + sd->params.status.systemState = gspca_dev->usb_buf[0]; + sd->params.status.grabState = gspca_dev->usb_buf[1]; + sd->params.status.streamState = gspca_dev->usb_buf[2]; + sd->params.status.fatalError = gspca_dev->usb_buf[3]; + sd->params.status.cmdError = gspca_dev->usb_buf[4]; + sd->params.status.debugFlags = gspca_dev->usb_buf[5]; + sd->params.status.vpStatus = gspca_dev->usb_buf[6]; + sd->params.status.errorCode = gspca_dev->usb_buf[7]; + break; + case CPIA_COMMAND_GetVPVersion: + sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0]; + sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1]; + sd->params.vpVersion.cameraHeadID = + gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8); + break; + case CPIA_COMMAND_GetColourParams: + sd->params.colourParams.brightness = gspca_dev->usb_buf[0]; + sd->params.colourParams.contrast = gspca_dev->usb_buf[1]; + sd->params.colourParams.saturation = gspca_dev->usb_buf[2]; + break; + case CPIA_COMMAND_GetColourBalance: + sd->params.colourBalance.redGain = gspca_dev->usb_buf[0]; + sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1]; + sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2]; + break; + case CPIA_COMMAND_GetExposure: + sd->params.exposure.gain = gspca_dev->usb_buf[0]; + sd->params.exposure.fineExp = gspca_dev->usb_buf[1]; + sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2]; + sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3]; + sd->params.exposure.redComp = gspca_dev->usb_buf[4]; + sd->params.exposure.green1Comp = gspca_dev->usb_buf[5]; + sd->params.exposure.green2Comp = gspca_dev->usb_buf[6]; + sd->params.exposure.blueComp = gspca_dev->usb_buf[7]; + break; + + case CPIA_COMMAND_ReadMCPorts: + /* test button press */ + a = ((gspca_dev->usb_buf[1] & 0x02) == 0); + if (a != sd->params.qx3.button) { +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + input_report_key(gspca_dev->input_dev, KEY_CAMERA, a); + input_sync(gspca_dev->input_dev); +#endif + sd->params.qx3.button = a; + } + if (sd->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + 3, 0xdf, 0xdf, 0); + do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + 3, 0xff, 0xff, 0); + } + + /* test whether microscope is cradled */ + sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0); + break; + } + + return 0; +} + +/* send a command to the camera with an additional data transaction */ +static int do_command_extended(struct gspca_dev *gspca_dev, u16 command, + u8 a, u8 b, u8 c, u8 d, + u8 e, u8 f, u8 g, u8 h, + u8 i, u8 j, u8 k, u8 l) +{ + u8 cmd[8]; + + cmd[0] = command >> 8; + cmd[1] = command & 0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = 8; + cmd[7] = 0; + gspca_dev->usb_buf[0] = e; + gspca_dev->usb_buf[1] = f; + gspca_dev->usb_buf[2] = g; + gspca_dev->usb_buf[3] = h; + gspca_dev->usb_buf[4] = i; + gspca_dev->usb_buf[5] = j; + gspca_dev->usb_buf[6] = k; + gspca_dev->usb_buf[7] = l; + + return cpia_usb_transferCmd(gspca_dev, cmd); +} + +/* find_over_exposure + * Finds a suitable value of OverExposure for use with SetFlickerCtrl + * Some calculation is required because this value changes with the brightness + * set with SetColourParameters + * + * Parameters: Brightness - last brightness value set with SetColourParameters + * + * Returns: OverExposure value to use with SetFlickerCtrl + */ +#define FLICKER_MAX_EXPOSURE 250 +#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 +#define FLICKER_BRIGHTNESS_CONSTANT 59 +static int find_over_exposure(int brightness) +{ + int MaxAllowableOverExposure, OverExposure; + + MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - + FLICKER_BRIGHTNESS_CONSTANT; + + if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) + OverExposure = MaxAllowableOverExposure; + else + OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; + + return OverExposure; +} +#undef FLICKER_MAX_EXPOSURE +#undef FLICKER_ALLOWABLE_OVER_EXPOSURE +#undef FLICKER_BRIGHTNESS_CONSTANT + +/* initialise cam_data structure */ +static void reset_camera_params(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam_params *params = &sd->params; + + /* The following parameter values are the defaults from + * "Software Developer's Guide for CPiA Cameras". Any changes + * to the defaults are noted in comments. */ + params->colourParams.brightness = BRIGHTNESS_DEF; + params->colourParams.contrast = CONTRAST_DEF; + params->colourParams.saturation = SATURATION_DEF; + params->exposure.gainMode = 4; + params->exposure.expMode = 2; /* AEC */ + params->exposure.compMode = 1; + params->exposure.centreWeight = 1; + params->exposure.gain = 0; + params->exposure.fineExp = 0; + params->exposure.coarseExpLo = 185; + params->exposure.coarseExpHi = 0; + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + params->colourBalance.balanceMode = 2; /* ACB */ + params->colourBalance.redGain = 32; + params->colourBalance.greenGain = 6; + params->colourBalance.blueGain = 92; + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x16; + params->apcor.gain4 = 0x24; + params->apcor.gain8 = 0x34; + params->vlOffset.gain1 = 20; + params->vlOffset.gain2 = 24; + params->vlOffset.gain4 = 26; + params->vlOffset.gain8 = 26; + params->compressionParams.hysteresis = 3; + params->compressionParams.threshMax = 11; + params->compressionParams.smallStep = 1; + params->compressionParams.largeStep = 3; + params->compressionParams.decimationHysteresis = 2; + params->compressionParams.frDiffStepThresh = 5; + params->compressionParams.qDiffStepThresh = 3; + params->compressionParams.decimationThreshMod = 2; + /* End of default values from Software Developer's Guide */ + + /* Set Sensor FPS to 15fps. This seems better than 30fps + * for indoor lighting. */ + params->sensorFps.divisor = 1; + params->sensorFps.baserate = 1; + + params->flickerControl.flickerMode = 0; + params->flickerControl.disabled = 1; + params->flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [params->sensorFps.baserate] + [params->sensorFps.divisor]; + params->flickerControl.allowableOverExposure = + find_over_exposure(params->colourParams.brightness); + + params->yuvThreshold.yThreshold = 6; /* From windows driver */ + params->yuvThreshold.uvThreshold = 6; /* From windows driver */ + + params->format.subSample = SUBSAMPLE_420; + params->format.yuvOrder = YUVORDER_YUYV; + + params->compression.mode = CPIA_COMPRESSION_AUTO; + params->compression.decimation = NO_DECIMATION; + + params->compressionTarget.frTargeting = COMP_TARGET_DEF; + params->compressionTarget.targetFR = 15; /* From windows driver */ + params->compressionTarget.targetQ = 5; /* From windows driver */ + + params->qx3.qx3_detected = 0; + params->qx3.toplight = 0; + params->qx3.bottomlight = 0; + params->qx3.button = 0; + params->qx3.cradled = 0; +} + +static void printstatus(struct cam_params *params) +{ + PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x", + params->status.systemState, params->status.grabState, + params->status.streamState, params->status.fatalError, + params->status.cmdError, params->status.debugFlags, + params->status.vpStatus, params->status.errorCode); +} + +static int goto_low_power(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0); + if (ret) + return ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + if (ret) + return ret; + + if (sd->params.status.systemState != LO_POWER_STATE) { + if (sd->params.status.systemState != WARM_BOOT_STATE) { + PDEBUG(D_ERR, + "unexpected state after lo power cmd: %02x", + sd->params.status.systemState); + printstatus(&sd->params); + } + return -EIO; + } + + PDEBUG(D_CONF, "camera now in LOW power state"); + return 0; +} + +static int goto_high_power(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0); + if (ret) + return ret; + + msleep_interruptible(40); /* windows driver does it too */ + + if (signal_pending(current)) + return -EINTR; + + do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + if (ret) + return ret; + + if (sd->params.status.systemState != HI_POWER_STATE) { + PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x", + sd->params.status.systemState); + printstatus(&sd->params); + return -EIO; + } + + PDEBUG(D_CONF, "camera now in HIGH power state"); + return 0; +} + +static int get_version_information(struct gspca_dev *gspca_dev) +{ + int ret; + + /* GetCPIAVersion */ + ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); + if (ret) + return ret; + + /* GetPnPID */ + return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); +} + +static int save_camera_state(struct gspca_dev *gspca_dev) +{ + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); +} + +static int command_setformat(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat, + sd->params.format.videoSize, + sd->params.format.subSample, + sd->params.format.yuvOrder, 0); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_SetROI, + sd->params.roi.colStart, sd->params.roi.colEnd, + sd->params.roi.rowStart, sd->params.roi.rowEnd); +} + +static int command_setcolourparams(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + return do_command(gspca_dev, CPIA_COMMAND_SetColourParams, + sd->params.colourParams.brightness, + sd->params.colourParams.contrast, + sd->params.colourParams.saturation, 0); +} + +static int command_setapcor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + return do_command(gspca_dev, CPIA_COMMAND_SetApcor, + sd->params.apcor.gain1, + sd->params.apcor.gain2, + sd->params.apcor.gain4, + sd->params.apcor.gain8); +} + +static int command_setvloffset(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset, + sd->params.vlOffset.gain1, + sd->params.vlOffset.gain2, + sd->params.vlOffset.gain4, + sd->params.vlOffset.gain8); +} + +static int command_setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure, + sd->params.exposure.gainMode, + 1, + sd->params.exposure.compMode, + sd->params.exposure.centreWeight, + sd->params.exposure.gain, + sd->params.exposure.fineExp, + sd->params.exposure.coarseExpLo, + sd->params.exposure.coarseExpHi, + sd->params.exposure.redComp, + sd->params.exposure.green1Comp, + sd->params.exposure.green2Comp, + sd->params.exposure.blueComp); + if (ret) + return ret; + + if (sd->params.exposure.expMode != 1) { + ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure, + 0, + sd->params.exposure.expMode, + 0, 0, + sd->params.exposure.gain, + sd->params.exposure.fineExp, + sd->params.exposure.coarseExpLo, + sd->params.exposure.coarseExpHi, + 0, 0, 0, 0); + } + + return ret; +} + +static int command_setcolourbalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->params.colourBalance.balanceMode == 1) { + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 1, + sd->params.colourBalance.redGain, + sd->params.colourBalance.greenGain, + sd->params.colourBalance.blueGain); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + if (sd->params.colourBalance.balanceMode == 2) { + return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } + if (sd->params.colourBalance.balanceMode == 3) { + return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + + return -EINVAL; +} + +static int command_setcompressiontarget(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget, + sd->params.compressionTarget.frTargeting, + sd->params.compressionTarget.targetFR, + sd->params.compressionTarget.targetQ, 0); +} + +static int command_setyuvtresh(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh, + sd->params.yuvThreshold.yThreshold, + sd->params.yuvThreshold.uvThreshold, 0, 0); +} + +static int command_setcompressionparams(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command_extended(gspca_dev, + CPIA_COMMAND_SetCompressionParams, + 0, 0, 0, 0, + sd->params.compressionParams.hysteresis, + sd->params.compressionParams.threshMax, + sd->params.compressionParams.smallStep, + sd->params.compressionParams.largeStep, + sd->params.compressionParams.decimationHysteresis, + sd->params.compressionParams.frDiffStepThresh, + sd->params.compressionParams.qDiffStepThresh, + sd->params.compressionParams.decimationThreshMod); +} + +static int command_setcompression(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetCompression, + sd->params.compression.mode, + sd->params.compression.decimation, 0, 0); +} + +static int command_setsensorfps(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS, + sd->params.sensorFps.divisor, + sd->params.sensorFps.baserate, 0, 0); +} + +static int command_setflickerctrl(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl, + sd->params.flickerControl.flickerMode, + sd->params.flickerControl.coarseJump, + sd->params.flickerControl.allowableOverExposure, + 0); +} + +static int command_setecptiming(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming, + sd->params.ecpTiming, 0, 0, 0); +} + +static int command_pause(struct gspca_dev *gspca_dev) +{ + return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); +} + +static int command_resume(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap, + 0, sd->params.streamStartLine, 0, 0); +} + +static int command_setlights(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret, p1, p2; + + p1 = (sd->params.qx3.bottomlight == 0) << 1; + p2 = (sd->params.qx3.toplight == 0) << 3; + + ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg, + 0x90, 0x8f, 0x50, 0); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0, + p1 | p2 | 0xe0, 0); +} + +static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply) +{ + /* Everything in here is from the Windows driver */ +/* define for compgain calculation */ +#if 0 +#define COMPGAIN(base, curexp, newexp) \ + (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)((float)curexp * (float)(u8)(curcomp + 128) / \ + (float)(u8)(basecomp - 128)) +#else + /* equivalent functions without floating point math */ +#define COMPGAIN(base, curexp, newexp) \ + (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp))) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) +#endif + + struct sd *sd = (struct sd *) gspca_dev; + int currentexp = sd->params.exposure.coarseExpLo + + sd->params.exposure.coarseExpHi * 256; + int ret, startexp; + + if (on) { + int cj = sd->params.flickerControl.coarseJump; + sd->params.flickerControl.flickerMode = 1; + sd->params.flickerControl.disabled = 0; + if (sd->params.exposure.expMode != 2) { + sd->params.exposure.expMode = 2; + sd->exposure_status = EXPOSURE_NORMAL; + } + currentexp = currentexp << sd->params.exposure.gain; + sd->params.exposure.gain = 0; + /* round down current exposure to nearest value */ + startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; + if (startexp < 1) + startexp = 1; + startexp = (startexp * cj) - 1; + if (FIRMWARE_VERSION(1, 2)) + while (startexp > MAX_EXP_102) + startexp -= cj; + else + while (startexp > MAX_EXP) + startexp -= cj; + sd->params.exposure.coarseExpLo = startexp & 0xff; + sd->params.exposure.coarseExpHi = startexp >> 8; + if (currentexp > startexp) { + if (currentexp > (2 * startexp)) + currentexp = 2 * startexp; + sd->params.exposure.redComp = + COMPGAIN(COMP_RED, currentexp, startexp); + sd->params.exposure.green1Comp = + COMPGAIN(COMP_GREEN1, currentexp, startexp); + sd->params.exposure.green2Comp = + COMPGAIN(COMP_GREEN2, currentexp, startexp); + sd->params.exposure.blueComp = + COMPGAIN(COMP_BLUE, currentexp, startexp); + } else { + sd->params.exposure.redComp = COMP_RED; + sd->params.exposure.green1Comp = COMP_GREEN1; + sd->params.exposure.green2Comp = COMP_GREEN2; + sd->params.exposure.blueComp = COMP_BLUE; + } + if (FIRMWARE_VERSION(1, 2)) + sd->params.exposure.compMode = 0; + else + sd->params.exposure.compMode = 1; + + sd->params.apcor.gain1 = 0x18; + sd->params.apcor.gain2 = 0x18; + sd->params.apcor.gain4 = 0x16; + sd->params.apcor.gain8 = 0x14; + } else { + sd->params.flickerControl.flickerMode = 0; + sd->params.flickerControl.disabled = 1; + /* Average equivalent coarse for each comp channel */ + startexp = EXP_FROM_COMP(COMP_RED, + sd->params.exposure.redComp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN1, + sd->params.exposure.green1Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN2, + sd->params.exposure.green2Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_BLUE, + sd->params.exposure.blueComp, currentexp); + startexp = startexp >> 2; + while (startexp > MAX_EXP && sd->params.exposure.gain < + sd->params.exposure.gainMode - 1) { + startexp = startexp >> 1; + ++sd->params.exposure.gain; + } + if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102) + startexp = MAX_EXP_102; + if (startexp > MAX_EXP) + startexp = MAX_EXP; + sd->params.exposure.coarseExpLo = startexp & 0xff; + sd->params.exposure.coarseExpHi = startexp >> 8; + sd->params.exposure.redComp = COMP_RED; + sd->params.exposure.green1Comp = COMP_GREEN1; + sd->params.exposure.green2Comp = COMP_GREEN2; + sd->params.exposure.blueComp = COMP_BLUE; + sd->params.exposure.compMode = 1; + sd->params.apcor.gain1 = 0x18; + sd->params.apcor.gain2 = 0x16; + sd->params.apcor.gain4 = 0x24; + sd->params.apcor.gain8 = 0x34; + } + sd->params.vlOffset.gain1 = 20; + sd->params.vlOffset.gain2 = 24; + sd->params.vlOffset.gain4 = 26; + sd->params.vlOffset.gain8 = 26; + + if (apply) { + ret = command_setexposure(gspca_dev); + if (ret) + return ret; + + ret = command_setapcor(gspca_dev); + if (ret) + return ret; + + ret = command_setvloffset(gspca_dev); + if (ret) + return ret; + + ret = command_setflickerctrl(gspca_dev); + if (ret) + return ret; + } + + return 0; +#undef EXP_FROM_COMP +#undef COMPGAIN +} + +/* monitor the exposure and adjust the sensor frame rate if needed */ +static void monitor_exposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 exp_acc, bcomp, cmd[8]; + int ret, light_exp, dark_exp, very_dark_exp; + int old_exposure, new_exposure, framerate; + int setfps = 0, setexp = 0, setflicker = 0; + + /* get necessary stats and register settings from camera */ + /* do_command can't handle this, so do it ourselves */ + cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8; + cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff; + cmd[2] = 30; + cmd[3] = 4; + cmd[4] = 9; + cmd[5] = 8; + cmd[6] = 8; + cmd[7] = 0; + ret = cpia_usb_transferCmd(gspca_dev, cmd); + if (ret) { + pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret); + return; + } + exp_acc = gspca_dev->usb_buf[0]; + bcomp = gspca_dev->usb_buf[1]; + + light_exp = sd->params.colourParams.brightness + + TC - 50 + EXP_ACC_LIGHT; + if (light_exp > 255) + light_exp = 255; + dark_exp = sd->params.colourParams.brightness + + TC - 50 - EXP_ACC_DARK; + if (dark_exp < 0) + dark_exp = 0; + very_dark_exp = dark_exp / 2; + + old_exposure = sd->params.exposure.coarseExpHi * 256 + + sd->params.exposure.coarseExpLo; + + if (!sd->params.flickerControl.disabled) { + /* Flicker control on */ + int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP : + HIGH_COMP_102; + bcomp += 128; /* decode */ + if (bcomp >= max_comp && exp_acc < dark_exp) { + /* dark */ + if (exp_acc < very_dark_exp) { + /* very dark */ + if (sd->exposure_status == EXPOSURE_VERY_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_DARK; + sd->exposure_count = 1; + } + } else { + /* just dark */ + if (sd->exposure_status == EXPOSURE_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_DARK; + sd->exposure_count = 1; + } + } + } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if (old_exposure <= VERY_LOW_EXP) { + /* very light */ + if (sd->exposure_status == EXPOSURE_VERY_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_LIGHT; + sd->exposure_count = 1; + } + } else { + /* just light */ + if (sd->exposure_status == EXPOSURE_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_LIGHT; + sd->exposure_count = 1; + } + } + } else { + /* not dark or light */ + sd->exposure_status = EXPOSURE_NORMAL; + } + } else { + /* Flicker control off */ + if (old_exposure >= MAX_EXP && exp_acc < dark_exp) { + /* dark */ + if (exp_acc < very_dark_exp) { + /* very dark */ + if (sd->exposure_status == EXPOSURE_VERY_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_DARK; + sd->exposure_count = 1; + } + } else { + /* just dark */ + if (sd->exposure_status == EXPOSURE_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_DARK; + sd->exposure_count = 1; + } + } + } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if (old_exposure <= VERY_LOW_EXP) { + /* very light */ + if (sd->exposure_status == EXPOSURE_VERY_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_LIGHT; + sd->exposure_count = 1; + } + } else { + /* just light */ + if (sd->exposure_status == EXPOSURE_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_LIGHT; + sd->exposure_count = 1; + } + } + } else { + /* not dark or light */ + sd->exposure_status = EXPOSURE_NORMAL; + } + } + + framerate = atomic_read(&sd->fps); + if (framerate > 30 || framerate < 1) + framerate = 1; + + if (!sd->params.flickerControl.disabled) { + /* Flicker control on */ + if ((sd->exposure_status == EXPOSURE_VERY_DARK || + sd->exposure_status == EXPOSURE_DARK) && + sd->exposure_count >= DARK_TIME * framerate && + sd->params.sensorFps.divisor < 2) { + + /* dark for too long */ + ++sd->params.sensorFps.divisor; + setfps = 1; + + sd->params.flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [sd->params.sensorFps.baserate] + [sd->params.sensorFps.divisor]; + setflicker = 1; + + new_exposure = sd->params.flickerControl.coarseJump-1; + while (new_exposure < old_exposure / 2) + new_exposure += + sd->params.flickerControl.coarseJump; + sd->params.exposure.coarseExpLo = new_exposure & 0xff; + sd->params.exposure.coarseExpHi = new_exposure >> 8; + setexp = 1; + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically decreasing sensor_fps"); + + } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT || + sd->exposure_status == EXPOSURE_LIGHT) && + sd->exposure_count >= LIGHT_TIME * framerate && + sd->params.sensorFps.divisor > 0) { + + /* light for too long */ + int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 : + MAX_EXP; + --sd->params.sensorFps.divisor; + setfps = 1; + + sd->params.flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [sd->params.sensorFps.baserate] + [sd->params.sensorFps.divisor]; + setflicker = 1; + + new_exposure = sd->params.flickerControl.coarseJump-1; + while (new_exposure < 2 * old_exposure && + new_exposure + + sd->params.flickerControl.coarseJump < max_exp) + new_exposure += + sd->params.flickerControl.coarseJump; + sd->params.exposure.coarseExpLo = new_exposure & 0xff; + sd->params.exposure.coarseExpHi = new_exposure >> 8; + setexp = 1; + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically increasing sensor_fps"); + } + } else { + /* Flicker control off */ + if ((sd->exposure_status == EXPOSURE_VERY_DARK || + sd->exposure_status == EXPOSURE_DARK) && + sd->exposure_count >= DARK_TIME * framerate && + sd->params.sensorFps.divisor < 2) { + + /* dark for too long */ + ++sd->params.sensorFps.divisor; + setfps = 1; + + if (sd->params.exposure.gain > 0) { + --sd->params.exposure.gain; + setexp = 1; + } + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically decreasing sensor_fps"); + + } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT || + sd->exposure_status == EXPOSURE_LIGHT) && + sd->exposure_count >= LIGHT_TIME * framerate && + sd->params.sensorFps.divisor > 0) { + + /* light for too long */ + --sd->params.sensorFps.divisor; + setfps = 1; + + if (sd->params.exposure.gain < + sd->params.exposure.gainMode - 1) { + ++sd->params.exposure.gain; + setexp = 1; + } + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically increasing sensor_fps"); + } + } + + if (setexp) + command_setexposure(gspca_dev); + + if (setfps) + command_setsensorfps(gspca_dev); + + if (setflicker) + command_setflickerctrl(gspca_dev); +} + +/*-----------------------------------------------------------------*/ +/* if flicker is switched off, this function switches it back on.It checks, + however, that conditions are suitable before restarting it. + This should only be called for firmware version 1.2. + + It also adjust the colour balance when an exposure step is detected - as + long as flicker is running +*/ +static void restart_flicker(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int cam_exposure, old_exp; + + if (!FIRMWARE_VERSION(1, 2)) + return; + + cam_exposure = atomic_read(&sd->cam_exposure); + + if (sd->params.flickerControl.flickerMode == 0 || + cam_exposure == 0) + return; + + old_exp = sd->params.exposure.coarseExpLo + + sd->params.exposure.coarseExpHi*256; + /* + see how far away camera exposure is from a valid + flicker exposure value + */ + cam_exposure %= sd->params.flickerControl.coarseJump; + if (!sd->params.flickerControl.disabled && + cam_exposure <= sd->params.flickerControl.coarseJump - 3) { + /* Flicker control auto-disabled */ + sd->params.flickerControl.disabled = 1; + } + + if (sd->params.flickerControl.disabled && + old_exp > sd->params.flickerControl.coarseJump + + ROUND_UP_EXP_FOR_FLICKER) { + /* exposure is now high enough to switch + flicker control back on */ + set_flicker(gspca_dev, 1, 1); + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ; + reset_camera_params(gspca_dev); + + PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)", + id->idVendor, id->idProduct); + + cam = &gspca_dev->cam; + cam->cam_mode = mode; + cam->nmodes = ARRAY_SIZE(mode); + + goto_low_power(gspca_dev); + /* Check the firmware version. */ + sd->params.version.firmwareVersion = 0; + get_version_information(gspca_dev); + if (sd->params.version.firmwareVersion != 1) { + PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)", + sd->params.version.firmwareVersion); + return -ENODEV; + } + + /* A bug in firmware 1-02 limits gainMode to 2 */ + if (sd->params.version.firmwareRevision <= 2 && + sd->params.exposure.gainMode > 2) { + sd->params.exposure.gainMode = 2; + } + + /* set QX3 detected flag */ + sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 && + sd->params.pnpID.product == 0x0001); + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int priv, ret; + + /* Start the camera in low power mode */ + if (goto_low_power(gspca_dev)) { + if (sd->params.status.systemState != WARM_BOOT_STATE) { + PDEBUG(D_ERR, "unexpected systemstate: %02x", + sd->params.status.systemState); + printstatus(&sd->params); + return -ENODEV; + } + + /* FIXME: this is just dirty trial and error */ + ret = goto_high_power(gspca_dev); + if (ret) + return ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame, + 0, 0, 0, 0); + if (ret) + return ret; + + ret = goto_low_power(gspca_dev); + if (ret) + return ret; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version. */ + sd->params.version.firmwareVersion = 0; + get_version_information(gspca_dev); + + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ + + /* Set streamState before transition to high power to avoid bug + * in firmware 1-02 */ + ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus, + STREAMSTATE, 0, STREAM_NOT_READY, 0); + if (ret) + return ret; + + /* GotoHiPower */ + ret = goto_high_power(gspca_dev); + if (ret) + return ret; + + /* Check the camera status */ + ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + if (ret) + return ret; + + if (sd->params.status.fatalError) { + PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x", + sd->params.status.fatalError, + sd->params.status.vpStatus); + return -EIO; + } + + /* VPVersion can't be retrieved before the camera is in HiPower, + * so get it here instead of in get_version_information. */ + ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); + if (ret) + return ret; + + /* Determine video mode settings */ + sd->params.streamStartLine = 120; + + priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + if (priv & 0x01) { /* crop */ + sd->params.roi.colStart = 2; + sd->params.roi.rowStart = 6; + } else { + sd->params.roi.colStart = 0; + sd->params.roi.rowStart = 0; + } + + if (priv & 0x02) { /* quarter */ + sd->params.format.videoSize = VIDEOSIZE_QCIF; + sd->params.roi.colStart /= 2; + sd->params.roi.rowStart /= 2; + sd->params.streamStartLine /= 2; + } else + sd->params.format.videoSize = VIDEOSIZE_CIF; + + sd->params.roi.colEnd = sd->params.roi.colStart + + (gspca_dev->width >> 3); + sd->params.roi.rowEnd = sd->params.roi.rowStart + + (gspca_dev->height >> 2); + + /* And now set the camera to a known state */ + ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINEOUS, 0, 0, 0); + if (ret) + return ret; + /* We start with compression disabled, as we need one uncompressed + frame to handle later compressed frames */ + ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + if (ret) + return ret; + ret = command_setcompressiontarget(gspca_dev); + if (ret) + return ret; + ret = command_setcolourparams(gspca_dev); + if (ret) + return ret; + ret = command_setformat(gspca_dev); + if (ret) + return ret; + ret = command_setyuvtresh(gspca_dev); + if (ret) + return ret; + ret = command_setecptiming(gspca_dev); + if (ret) + return ret; + ret = command_setcompressionparams(gspca_dev); + if (ret) + return ret; + ret = command_setexposure(gspca_dev); + if (ret) + return ret; + ret = command_setcolourbalance(gspca_dev); + if (ret) + return ret; + ret = command_setsensorfps(gspca_dev); + if (ret) + return ret; + ret = command_setapcor(gspca_dev); + if (ret) + return ret; + ret = command_setflickerctrl(gspca_dev); + if (ret) + return ret; + ret = command_setvloffset(gspca_dev); + if (ret) + return ret; + + /* Start stream */ + ret = command_resume(gspca_dev); + if (ret) + return ret; + + /* Wait 6 frames before turning compression on for the sensor to get + all settings and AEC/ACB to settle */ + sd->first_frame = 6; + sd->exposure_status = EXPOSURE_NORMAL; + sd->exposure_count = 0; + atomic_set(&sd->cam_exposure, 0); + atomic_set(&sd->fps, 0); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + command_pause(gspca_dev); + + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(gspca_dev); + + /* GotoLoPower */ + goto_low_power(gspca_dev); + + /* Update the camera status */ + do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + /* If the last button state is pressed, release it now! */ + if (sd->params.qx3.button) { + /* The camera latch will hold the pressed state until we reset + the latch, so we do not reset sd->params.qx3.button now, to + avoid a false keypress being reported the next sd_start */ + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + } +#endif +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + /* Start / Stop the camera to make sure we are talking to + a supported camera, and to get some information from it + to print. */ + ret = sd_start(gspca_dev); + if (ret) + return ret; + + /* Ensure the QX3 illuminators' states are restored upon resume, + or disable the illuminator controls, if this isn't a QX3 */ + if (sd->params.qx3.qx3_detected) + command_setlights(gspca_dev); + + sd_stopN(gspca_dev); + + PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)", + sd->params.version.firmwareVersion, + sd->params.version.firmwareRevision, + sd->params.version.vcVersion, + sd->params.version.vcRevision); + PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x", + sd->params.pnpID.vendor, sd->params.pnpID.product, + sd->params.pnpID.deviceRevision); + PDEBUG(D_PROBE, "VP-Version: %d.%d %04x", + sd->params.vpVersion.vpVersion, + sd->params.vpVersion.vpRevision, + sd->params.vpVersion.cameraHeadID); + + return 0; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Check for SOF */ + if (len >= 64 && + data[0] == MAGIC_0 && data[1] == MAGIC_1 && + data[16] == sd->params.format.videoSize && + data[17] == sd->params.format.subSample && + data[18] == sd->params.format.yuvOrder && + data[24] == sd->params.roi.colStart && + data[25] == sd->params.roi.colEnd && + data[26] == sd->params.roi.rowStart && + data[27] == sd->params.roi.rowEnd) { + u8 *image; + + atomic_set(&sd->cam_exposure, data[39] * 2); + atomic_set(&sd->fps, data[41]); + + /* Check for proper EOF for last frame */ + image = gspca_dev->image; + if (image != NULL && + gspca_dev->image_len > 4 && + image[gspca_dev->image_len - 4] == 0xff && + image[gspca_dev->image_len - 3] == 0xff && + image[gspca_dev->image_len - 2] == 0xff && + image[gspca_dev->image_len - 1] == 0xff) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + return; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Set the normal compression settings once we have captured a + few uncompressed frames (and AEC has hopefully settled) */ + if (sd->first_frame) { + sd->first_frame--; + if (sd->first_frame == 0) + command_setcompression(gspca_dev); + } + + /* Switch flicker control back on if it got turned off */ + restart_flicker(gspca_dev); + + /* If AEC is enabled, monitor the exposure and + adjust the sensor frame rate if needed */ + if (sd->params.exposure.expMode == 2) + monitor_exposure(gspca_dev); + + /* Update our knowledge of the camera state */ + do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + sd->params.colourParams.brightness = ctrl->val; + sd->params.flickerControl.allowableOverExposure = + find_over_exposure(sd->params.colourParams.brightness); + gspca_dev->usb_err = command_setcolourparams(gspca_dev); + if (!gspca_dev->usb_err) + gspca_dev->usb_err = command_setflickerctrl(gspca_dev); + break; + case V4L2_CID_CONTRAST: + sd->params.colourParams.contrast = ctrl->val; + gspca_dev->usb_err = command_setcolourparams(gspca_dev); + break; + case V4L2_CID_SATURATION: + sd->params.colourParams.saturation = ctrl->val; + gspca_dev->usb_err = command_setcolourparams(gspca_dev); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ; + sd->params.flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [sd->params.sensorFps.baserate] + [sd->params.sensorFps.divisor]; + + gspca_dev->usb_err = set_flicker(gspca_dev, + ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, + gspca_dev->streaming); + break; + case V4L2_CID_ILLUMINATORS_1: + sd->params.qx3.bottomlight = ctrl->val; + gspca_dev->usb_err = command_setlights(gspca_dev); + break; + case V4L2_CID_ILLUMINATORS_2: + sd->params.qx3.toplight = ctrl->val; + gspca_dev->usb_err = command_setlights(gspca_dev); + break; + case CPIA1_CID_COMP_TARGET: + sd->params.compressionTarget.frTargeting = ctrl->val; + gspca_dev->usb_err = command_setcompressiontarget(gspca_dev); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + static const char * const comp_target_menu[] = { + "Quality", + "Framerate", + NULL + }; + static const struct v4l2_ctrl_config comp_target = { + .ops = &sd_ctrl_ops, + .id = CPIA1_CID_COMP_TARGET, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Compression Target", + .qmenu = comp_target_menu, + .max = 1, + .def = COMP_TARGET_DEF, + }; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 7); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF); + sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + FREQ_DEF); + if (sd->params.qx3.qx3_detected) { + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, + ILLUMINATORS_1_DEF); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, + ILLUMINATORS_2_DEF); + } + v4l2_ctrl_new_custom(hdl, &comp_target, NULL); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .dq_callback = sd_dq_callback, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .other_input = 1, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0553, 0x0002)}, + {USB_DEVICE(0x0813, 0x0001)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/etoms.c b/drivers/media/usb/gspca/etoms.c new file mode 100644 index 000000000000..38f68e11c3a2 --- /dev/null +++ b/drivers/media/usb/gspca/etoms.c @@ -0,0 +1,799 @@ +/* + * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004) + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "etoms" + +#include "gspca.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("Etoms USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char autogain; + + char sensor; +#define SENSOR_PAS106 0 +#define SENSOR_TAS5130CXX 1 + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, +/* {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, */ +}; + +static const struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define ETOMS_ALT_SIZE_1000 12 + +#define ET_GPIO_DIR_CTRL 0x04 /* Control IO bit[0..5] (0 in 1 out) */ +#define ET_GPIO_OUT 0x05 /* Only IO data */ +#define ET_GPIO_IN 0x06 /* Read Only IO data */ +#define ET_RESET_ALL 0x03 +#define ET_ClCK 0x01 +#define ET_CTRL 0x02 /* enable i2c OutClck Powerdown mode */ + +#define ET_COMP 0x12 /* Compression register */ +#define ET_MAXQt 0x13 +#define ET_MINQt 0x14 +#define ET_COMP_VAL0 0x02 +#define ET_COMP_VAL1 0x03 + +#define ET_REG1d 0x1d +#define ET_REG1e 0x1e +#define ET_REG1f 0x1f +#define ET_REG20 0x20 +#define ET_REG21 0x21 +#define ET_REG22 0x22 +#define ET_REG23 0x23 +#define ET_REG24 0x24 +#define ET_REG25 0x25 +/* base registers for luma calculation */ +#define ET_LUMA_CENTER 0x39 + +#define ET_G_RED 0x4d +#define ET_G_GREEN1 0x4e +#define ET_G_BLUE 0x4f +#define ET_G_GREEN2 0x50 +#define ET_G_GR_H 0x51 +#define ET_G_GB_H 0x52 + +#define ET_O_RED 0x34 +#define ET_O_GREEN1 0x35 +#define ET_O_BLUE 0x36 +#define ET_O_GREEN2 0x37 + +#define ET_SYNCHRO 0x68 +#define ET_STARTX 0x69 +#define ET_STARTY 0x6a +#define ET_WIDTH_LOW 0x6b +#define ET_HEIGTH_LOW 0x6c +#define ET_W_H_HEIGTH 0x6d + +#define ET_REG6e 0x6e /* OBW */ +#define ET_REG6f 0x6f /* OBW */ +#define ET_REG70 0x70 /* OBW_AWB */ +#define ET_REG71 0x71 /* OBW_AWB */ +#define ET_REG72 0x72 /* OBW_AWB */ +#define ET_REG73 0x73 /* Clkdelay ns */ +#define ET_REG74 0x74 /* test pattern */ +#define ET_REG75 0x75 /* test pattern */ + +#define ET_I2C_CLK 0x8c +#define ET_PXL_CLK 0x60 + +#define ET_I2C_BASE 0x89 +#define ET_I2C_COUNT 0x8a +#define ET_I2C_PREFETCH 0x8b +#define ET_I2C_REG 0x88 +#define ET_I2C_DATA7 0x87 +#define ET_I2C_DATA6 0x86 +#define ET_I2C_DATA5 0x85 +#define ET_I2C_DATA4 0x84 +#define ET_I2C_DATA3 0x83 +#define ET_I2C_DATA2 0x82 +#define ET_I2C_DATA1 0x81 +#define ET_I2C_DATA0 0x80 + +#define PAS106_REG2 0x02 /* pxlClk = systemClk/(reg2) */ +#define PAS106_REG3 0x03 /* line/frame H [11..4] */ +#define PAS106_REG4 0x04 /* line/frame L [3..0] */ +#define PAS106_REG5 0x05 /* exposure time line offset(default 5) */ +#define PAS106_REG6 0x06 /* exposure time pixel offset(default 6) */ +#define PAS106_REG7 0x07 /* signbit Dac (default 0) */ +#define PAS106_REG9 0x09 +#define PAS106_REG0e 0x0e /* global gain [4..0](default 0x0e) */ +#define PAS106_REG13 0x13 /* end i2c write */ + +static const __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + +static const __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d }; + +static const __u8 I2c3[] = { 0x12, 0x05 }; + +static const __u8 I2c4[] = { 0x41, 0x08 }; + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + pr_err("reg_r: buffer overflow\n"); + return; + } +#endif + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, + index, gspca_dev->usb_buf, len, 500); + PDEBUG(D_USBI, "reg read [%02x] -> %02x ..", + index, gspca_dev->usb_buf[0]); +} + +static void reg_w_val(struct gspca_dev *gspca_dev, + __u16 index, + __u8 val) +{ + struct usb_device *dev = gspca_dev->dev; + + gspca_dev->usb_buf[0] = val; + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, + index, gspca_dev->usb_buf, 1, 500); +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 index, + const __u8 *buffer, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + pr_err("reg_w: buffer overflow\n"); + return; + } + PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, index, gspca_dev->usb_buf, len, 500); +} + +static int i2c_w(struct gspca_dev *gspca_dev, + __u8 reg, + const __u8 *buffer, + int len, __u8 mode) +{ + /* buffer should be [D0..D7] */ + __u8 ptchcount; + + /* set the base address */ + reg_w_val(gspca_dev, ET_I2C_BASE, 0x40); + /* sensor base for the pas106 */ + /* set count and prefetch */ + ptchcount = ((len & 0x07) << 4) | (mode & 0x03); + reg_w_val(gspca_dev, ET_I2C_COUNT, ptchcount); + /* set the register base */ + reg_w_val(gspca_dev, ET_I2C_REG, reg); + while (--len >= 0) + reg_w_val(gspca_dev, ET_I2C_DATA0 + len, buffer[len]); + return 0; +} + +static int i2c_r(struct gspca_dev *gspca_dev, + __u8 reg) +{ + /* set the base address */ + reg_w_val(gspca_dev, ET_I2C_BASE, 0x40); + /* sensor base for the pas106 */ + /* set count and prefetch (cnd: 4 bits - mode: 4 bits) */ + reg_w_val(gspca_dev, ET_I2C_COUNT, 0x11); + reg_w_val(gspca_dev, ET_I2C_REG, reg); /* set the register base */ + reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x02); /* prefetch */ + reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x00); + reg_r(gspca_dev, ET_I2C_DATA0, 1); /* read one byte */ + return 0; +} + +static int Et_WaitStatus(struct gspca_dev *gspca_dev) +{ + int retry = 10; + + while (retry--) { + reg_r(gspca_dev, ET_ClCK, 1); + if (gspca_dev->usb_buf[0] != 0) + return 1; + } + return 0; +} + +static int et_video(struct gspca_dev *gspca_dev, + int on) +{ + int ret; + + reg_w_val(gspca_dev, ET_GPIO_OUT, + on ? 0x10 /* startvideo - set Bit5 */ + : 0); /* stopvideo */ + ret = Et_WaitStatus(gspca_dev); + if (ret != 0) + PDEBUG(D_ERR, "timeout video on/off"); + return ret; +} + +static void Et_init2(struct gspca_dev *gspca_dev) +{ + __u8 value; + static const __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 }; + + PDEBUG(D_STREAM, "Open Init2 ET"); + reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 0x2f); + reg_w_val(gspca_dev, ET_GPIO_OUT, 0x10); + reg_r(gspca_dev, ET_GPIO_IN, 1); + reg_w_val(gspca_dev, ET_ClCK, 0x14); /* 0x14 // 0x16 enabled pattern */ + reg_w_val(gspca_dev, ET_CTRL, 0x1b); + + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) + value = ET_COMP_VAL1; /* 320 */ + else + value = ET_COMP_VAL0; /* 640 */ + reg_w_val(gspca_dev, ET_COMP, value); + reg_w_val(gspca_dev, ET_MAXQt, 0x1f); + reg_w_val(gspca_dev, ET_MINQt, 0x04); + /* undocumented registers */ + reg_w_val(gspca_dev, ET_REG1d, 0xff); + reg_w_val(gspca_dev, ET_REG1e, 0xff); + reg_w_val(gspca_dev, ET_REG1f, 0xff); + reg_w_val(gspca_dev, ET_REG20, 0x35); + reg_w_val(gspca_dev, ET_REG21, 0x01); + reg_w_val(gspca_dev, ET_REG22, 0x00); + reg_w_val(gspca_dev, ET_REG23, 0xff); + reg_w_val(gspca_dev, ET_REG24, 0xff); + reg_w_val(gspca_dev, ET_REG25, 0x0f); + /* colors setting */ + reg_w_val(gspca_dev, 0x30, 0x11); /* 0x30 */ + reg_w_val(gspca_dev, 0x31, 0x40); + reg_w_val(gspca_dev, 0x32, 0x00); + reg_w_val(gspca_dev, ET_O_RED, 0x00); /* 0x34 */ + reg_w_val(gspca_dev, ET_O_GREEN1, 0x00); + reg_w_val(gspca_dev, ET_O_BLUE, 0x00); + reg_w_val(gspca_dev, ET_O_GREEN2, 0x00); + /*************/ + reg_w_val(gspca_dev, ET_G_RED, 0x80); /* 0x4d */ + reg_w_val(gspca_dev, ET_G_GREEN1, 0x80); + reg_w_val(gspca_dev, ET_G_BLUE, 0x80); + reg_w_val(gspca_dev, ET_G_GREEN2, 0x80); + reg_w_val(gspca_dev, ET_G_GR_H, 0x00); + reg_w_val(gspca_dev, ET_G_GB_H, 0x00); /* 0x52 */ + /* Window control registers */ + reg_w_val(gspca_dev, 0x61, 0x80); /* use cmc_out */ + reg_w_val(gspca_dev, 0x62, 0x02); + reg_w_val(gspca_dev, 0x63, 0x03); + reg_w_val(gspca_dev, 0x64, 0x14); + reg_w_val(gspca_dev, 0x65, 0x0e); + reg_w_val(gspca_dev, 0x66, 0x02); + reg_w_val(gspca_dev, 0x67, 0x02); + + /**************************************/ + reg_w_val(gspca_dev, ET_SYNCHRO, 0x8f); /* 0x68 */ + reg_w_val(gspca_dev, ET_STARTX, 0x69); /* 0x6a //0x69 */ + reg_w_val(gspca_dev, ET_STARTY, 0x0d); /* 0x0d //0x0c */ + reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x80); + reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0xe0); + reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x60); /* 6d */ + reg_w_val(gspca_dev, ET_REG6e, 0x86); + reg_w_val(gspca_dev, ET_REG6f, 0x01); + reg_w_val(gspca_dev, ET_REG70, 0x26); + reg_w_val(gspca_dev, ET_REG71, 0x7a); + reg_w_val(gspca_dev, ET_REG72, 0x01); + /* Clock Pattern registers ***************** */ + reg_w_val(gspca_dev, ET_REG73, 0x00); + reg_w_val(gspca_dev, ET_REG74, 0x18); /* 0x28 */ + reg_w_val(gspca_dev, ET_REG75, 0x0f); /* 0x01 */ + /**********************************************/ + reg_w_val(gspca_dev, 0x8a, 0x20); + reg_w_val(gspca_dev, 0x8d, 0x0f); + reg_w_val(gspca_dev, 0x8e, 0x08); + /**************************************/ + reg_w_val(gspca_dev, 0x03, 0x08); + reg_w_val(gspca_dev, ET_PXL_CLK, 0x03); + reg_w_val(gspca_dev, 0x81, 0xff); + reg_w_val(gspca_dev, 0x80, 0x00); + reg_w_val(gspca_dev, 0x81, 0xff); + reg_w_val(gspca_dev, 0x80, 0x20); + reg_w_val(gspca_dev, 0x03, 0x01); + reg_w_val(gspca_dev, 0x03, 0x00); + reg_w_val(gspca_dev, 0x03, 0x08); + /********************************************/ + +/* reg_r(gspca_dev, ET_I2C_BASE, 1); + always 0x40 as the pas106 ??? */ + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) + value = 0x04; /* 320 */ + else /* 640 */ + value = 0x1e; /* 0x17 * setting PixelClock + * 0x03 mean 24/(3+1) = 6 Mhz + * 0x05 -> 24/(5+1) = 4 Mhz + * 0x0b -> 24/(11+1) = 2 Mhz + * 0x17 -> 24/(23+1) = 1 Mhz + */ + reg_w_val(gspca_dev, ET_PXL_CLK, value); + /* now set by fifo the FormatLine setting */ + reg_w(gspca_dev, 0x62, FormLine, 6); + + /* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */ + reg_w_val(gspca_dev, 0x81, 0x47); /* 0x47; */ + reg_w_val(gspca_dev, 0x80, 0x40); /* 0x40; */ + /* Pedro change */ + /* Brightness change Brith+ decrease value */ + /* Brigth- increase value */ + /* original value = 0x70; */ + reg_w_val(gspca_dev, 0x81, 0x30); /* 0x20; - set brightness */ + reg_w_val(gspca_dev, 0x80, 0x20); /* 0x20; */ +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + int i; + + for (i = 0; i < 4; i++) + reg_w_val(gspca_dev, ET_O_RED + i, val); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + + memset(RGBG, val, sizeof(RGBG) - 2); + reg_w(gspca_dev, ET_G_RED, RGBG, 6); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d }; + __u8 i2cflags = 0x01; + /* __u8 green = 0; */ + + I2cc[3] = val; /* red */ + I2cc[0] = 15 - val; /* blue */ + /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */ + /* I2cc[1] = I2cc[2] = green; */ + if (sd->sensor == SENSOR_PAS106) { + i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3); + i2c_w(gspca_dev, PAS106_REG9, I2cc, sizeof I2cc, 1); + } +/* PDEBUG(D_CONF , "Etoms red %d blue %d green %d", + I2cc[3], I2cc[0], green); */ +} + +static s32 getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) { +/* i2c_r(gspca_dev, PAS106_REG9); * blue */ + i2c_r(gspca_dev, PAS106_REG9 + 3); /* red */ + return gspca_dev->usb_buf[0] & 0x0f; + } + return 0; +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->autogain) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; +} + +static void Et_init1(struct gspca_dev *gspca_dev) +{ + __u8 value; +/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0x22, 0xac, 0x00, 0x01, 0x00}; */ + __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 }; + /* try 1/120 0x6d 0xcd 0x40 */ +/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0xfe, 0xfe, 0xc0, 0x01, 0x00}; + * 1/60000 hmm ?? */ + + PDEBUG(D_STREAM, "Open Init1 ET"); + reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 7); + reg_r(gspca_dev, ET_GPIO_IN, 1); + reg_w_val(gspca_dev, ET_RESET_ALL, 1); + reg_w_val(gspca_dev, ET_RESET_ALL, 0); + reg_w_val(gspca_dev, ET_ClCK, 0x10); + reg_w_val(gspca_dev, ET_CTRL, 0x19); + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) + value = ET_COMP_VAL1; + else + value = ET_COMP_VAL0; + PDEBUG(D_STREAM, "Open mode %d Compression %d", + gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv, + value); + reg_w_val(gspca_dev, ET_COMP, value); + reg_w_val(gspca_dev, ET_MAXQt, 0x1d); + reg_w_val(gspca_dev, ET_MINQt, 0x02); + /* undocumented registers */ + reg_w_val(gspca_dev, ET_REG1d, 0xff); + reg_w_val(gspca_dev, ET_REG1e, 0xff); + reg_w_val(gspca_dev, ET_REG1f, 0xff); + reg_w_val(gspca_dev, ET_REG20, 0x35); + reg_w_val(gspca_dev, ET_REG21, 0x01); + reg_w_val(gspca_dev, ET_REG22, 0x00); + reg_w_val(gspca_dev, ET_REG23, 0xf7); + reg_w_val(gspca_dev, ET_REG24, 0xff); + reg_w_val(gspca_dev, ET_REG25, 0x07); + /* colors setting */ + reg_w_val(gspca_dev, ET_G_RED, 0x80); + reg_w_val(gspca_dev, ET_G_GREEN1, 0x80); + reg_w_val(gspca_dev, ET_G_BLUE, 0x80); + reg_w_val(gspca_dev, ET_G_GREEN2, 0x80); + reg_w_val(gspca_dev, ET_G_GR_H, 0x00); + reg_w_val(gspca_dev, ET_G_GB_H, 0x00); + /* Window control registers */ + reg_w_val(gspca_dev, ET_SYNCHRO, 0xf0); + reg_w_val(gspca_dev, ET_STARTX, 0x56); /* 0x56 */ + reg_w_val(gspca_dev, ET_STARTY, 0x05); /* 0x04 */ + reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x60); + reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0x20); + reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x50); + reg_w_val(gspca_dev, ET_REG6e, 0x86); + reg_w_val(gspca_dev, ET_REG6f, 0x01); + reg_w_val(gspca_dev, ET_REG70, 0x86); + reg_w_val(gspca_dev, ET_REG71, 0x14); + reg_w_val(gspca_dev, ET_REG72, 0x00); + /* Clock Pattern registers */ + reg_w_val(gspca_dev, ET_REG73, 0x00); + reg_w_val(gspca_dev, ET_REG74, 0x00); + reg_w_val(gspca_dev, ET_REG75, 0x0a); + reg_w_val(gspca_dev, ET_I2C_CLK, 0x04); + reg_w_val(gspca_dev, ET_PXL_CLK, 0x01); + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + I2c0[0] = 0x06; + i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1); + i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1); + value = 0x06; + i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1); + i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1); + /* value = 0x1f; */ + value = 0x04; + i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1); + } else { + I2c0[0] = 0x0a; + + i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1); + i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1); + value = 0x0a; + i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1); + i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1); + value = 0x04; + /* value = 0x10; */ + i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1); + /* bit 2 enable bit 1:2 select 0 1 2 3 + value = 0x07; * curve 0 * + i2c_w(gspca_dev, PAS106_REG0f, &value, 1, 1); + */ + } + +/* value = 0x01; */ +/* value = 0x22; */ +/* i2c_w(gspca_dev, PAS106_REG5, &value, 1, 1); */ + /* magnetude and sign bit for DAC */ + i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1); + /* now set by fifo the whole colors setting */ + reg_w(gspca_dev, ET_G_RED, GainRGBG, 6); + setcolors(gspca_dev, getcolors(gspca_dev)); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + sd->sensor = id->driver_info; + if (sd->sensor == SENSOR_PAS106) { + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + } else { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + sd->ag_cnt = -1; + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + reg_w_val(gspca_dev, ET_RESET_ALL, 0x08); + et_video(gspca_dev, 0); /* video off */ + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + + setautogain(gspca_dev); + + reg_w_val(gspca_dev, ET_RESET_ALL, 0x08); + et_video(gspca_dev, 1); /* video on */ + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + et_video(gspca_dev, 0); /* video off */ +} + +static __u8 Et_getgainG(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) { + i2c_r(gspca_dev, PAS106_REG0e); + PDEBUG(D_CONF, "Etoms gain G %d", gspca_dev->usb_buf[0]); + return gspca_dev->usb_buf[0]; + } + return 0x1f; +} + +static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) { + __u8 i2cflags = 0x01; + + i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3); + i2c_w(gspca_dev, PAS106_REG0e, &gain, 1, 1); + } +} + +#define BLIMIT(bright) \ + (u8)((bright > 0x1f) ? 0x1f : ((bright < 4) ? 3 : bright)) +#define LIMIT(color) \ + (u8)((color > 0xff) ? 0xff : ((color < 0) ? 0 : color)) + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 luma; + __u8 luma_mean = 128; + __u8 luma_delta = 20; + __u8 spring = 4; + int Gbright; + __u8 r, g, b; + + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; + + Gbright = Et_getgainG(gspca_dev); + reg_r(gspca_dev, ET_LUMA_CENTER, 4); + g = (gspca_dev->usb_buf[0] + gspca_dev->usb_buf[3]) >> 1; + r = gspca_dev->usb_buf[1]; + b = gspca_dev->usb_buf[2]; + r = ((r << 8) - (r << 4) - (r << 3)) >> 10; + b = ((b << 7) >> 10); + g = ((g << 9) + (g << 7) + (g << 5)) >> 10; + luma = LIMIT(r + g + b); + PDEBUG(D_FRAM, "Etoms luma G %d", luma); + if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + Gbright = BLIMIT(Gbright); + PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright); + Et_setgainG(gspca_dev, (__u8) Gbright); + } +} + +#undef BLIMIT +#undef LIMIT + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int seqframe; + + seqframe = data[0] & 0x3f; + len = (int) (((data[0] & 0xc0) << 2) | data[1]); + if (seqframe == 0x3f) { + PDEBUG(D_FRAM, + "header packet found datalength %d !!", len); + PDEBUG(D_FRAM, "G %d R %d G %d B %d", + data[2], data[3], data[4], data[5]); + data += 30; + /* don't change datalength as the chips provided it */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + return; + } + if (len) { + data += 8; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + } else { /* Drop Packet */ + gspca_dev->last_packet_type = DISCARD_PACKET; + } +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + sd->autogain = ctrl->val; + setautogain(gspca_dev); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 1, 127, 1, 63); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + if (sd->sensor == SENSOR_PAS106) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 15, 1, 7); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106}, +#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE + {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX}, +#endif + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c new file mode 100644 index 000000000000..c8f2201cc35a --- /dev/null +++ b/drivers/media/usb/gspca/finepix.c @@ -0,0 +1,306 @@ +/* + * Fujifilm Finepix subdriver + * + * Copyright (C) 2008 Frank Zago + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "finepix" + +#include "gspca.h" + +MODULE_AUTHOR("Frank Zago "); +MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver"); +MODULE_LICENSE("GPL"); + +/* Default timeout, in ms */ +#define FPIX_TIMEOUT 250 + +/* Maximum transfer size to use. The windows driver reads by chunks of + * 0x2000 bytes, so do the same. Note: reading more seems to work + * too. */ +#define FPIX_MAX_TRANSFER 0x2000 + +/* Structure to hold all of our device specific stuff */ +struct usb_fpix { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct work_struct work_struct; + struct workqueue_struct *work_thread; +}; + +/* Delay after which claim the next frame. If the delay is too small, + * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms + * will fail every 4 or 5 frames, but 30ms is perfect. On the A210, + * 30ms is bad while 35ms is perfect. */ +#define NEXT_FRAME_DELAY 35 + +/* These cameras only support 320x200. */ +static const struct v4l2_pix_format fpix_mode[1] = { + { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0} +}; + +/* send a command to the webcam */ +static int command(struct gspca_dev *gspca_dev, + int order) /* 0: reset, 1: frame request */ +{ + static u8 order_values[2][12] = { + {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */ + {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */ + }; + + memcpy(gspca_dev->usb_buf, order_values[order], 12); + return usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, + 12, FPIX_TIMEOUT); +} + +/* workqueue */ +static void dostream(struct work_struct *work) +{ + struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + struct urb *urb = gspca_dev->urb[0]; + u8 *data = urb->transfer_buffer; + int ret = 0; + int len; + + /* synchronize with the main driver */ + mutex_lock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "dostream started"); + + /* loop reading a frame */ +again: + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + + /* request a frame */ + mutex_lock(&gspca_dev->usb_lock); + ret = command(gspca_dev, 1); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + break; +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) + break; + + /* the frame comes in parts */ + for (;;) { + ret = usb_bulk_msg(gspca_dev->dev, + urb->pipe, + data, + FPIX_MAX_TRANSFER, + &len, FPIX_TIMEOUT); + if (ret < 0) { + /* Most of the time we get a timeout + * error. Just restart. */ + goto again; + } +#ifdef CONFIG_PM + if (gspca_dev->frozen) + goto out; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) + goto out; + if (len < FPIX_MAX_TRANSFER || + (data[len - 2] == 0xff && + data[len - 1] == 0xd9)) { + + /* If the result is less than what was asked + * for, then it's the end of the + * frame. Sometimes the jpeg is not complete, + * but there's nothing we can do. We also end + * here if the the jpeg ends right at the end + * of the frame. */ + gspca_frame_add(gspca_dev, LAST_PACKET, + data, len); + break; + } + + /* got a partial image */ + gspca_frame_add(gspca_dev, + gspca_dev->last_packet_type + == LAST_PACKET + ? FIRST_PACKET : INTER_PACKET, + data, len); + } + + /* We must wait before trying reading the next + * frame. If we don't, or if the delay is too short, + * the camera will disconnect. */ + msleep(NEXT_FRAME_DELAY); + } + +out: + PDEBUG(D_STREAM, "dostream stopped"); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + cam->cam_mode = fpix_mode; + cam->nmodes = 1; + cam->bulk = 1; + cam->bulk_size = FPIX_MAX_TRANSFER; + + INIT_WORK(&dev->work_struct, dostream); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* start the camera */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + int ret, len; + + /* Init the device */ + ret = command(gspca_dev, 0); + if (ret < 0) { + pr_err("init failed %d\n", ret); + return ret; + } + + /* Read the result of the command. Ignore the result, for it + * varies with the device. */ + ret = usb_bulk_msg(gspca_dev->dev, + gspca_dev->urb[0]->pipe, + gspca_dev->urb[0]->transfer_buffer, + FPIX_MAX_TRANSFER, &len, + FPIX_TIMEOUT); + if (ret < 0) { + pr_err("usb_bulk_msg failed %d\n", ret); + return ret; + } + + /* Request a frame, but don't read it */ + ret = command(gspca_dev, 1); + if (ret < 0) { + pr_err("frame request failed %d\n", ret); + return ret; + } + + /* Again, reset bulk in endpoint */ + usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); + + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(dev->work_thread, &dev->work_struct); + + return 0; +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(dev->work_thread); + mutex_lock(&gspca_dev->usb_lock); + dev->work_thread = NULL; +} + +/* Table of supported USB devices */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x04cb, 0x0104)}, + {USB_DEVICE(0x04cb, 0x0109)}, + {USB_DEVICE(0x04cb, 0x010b)}, + {USB_DEVICE(0x04cb, 0x010f)}, + {USB_DEVICE(0x04cb, 0x0111)}, + {USB_DEVICE(0x04cb, 0x0113)}, + {USB_DEVICE(0x04cb, 0x0115)}, + {USB_DEVICE(0x04cb, 0x0117)}, + {USB_DEVICE(0x04cb, 0x0119)}, + {USB_DEVICE(0x04cb, 0x011b)}, + {USB_DEVICE(0x04cb, 0x011d)}, + {USB_DEVICE(0x04cb, 0x0121)}, + {USB_DEVICE(0x04cb, 0x0123)}, + {USB_DEVICE(0x04cb, 0x0125)}, + {USB_DEVICE(0x04cb, 0x0127)}, + {USB_DEVICE(0x04cb, 0x0129)}, + {USB_DEVICE(0x04cb, 0x012b)}, + {USB_DEVICE(0x04cb, 0x012d)}, + {USB_DEVICE(0x04cb, 0x012f)}, + {USB_DEVICE(0x04cb, 0x0131)}, + {USB_DEVICE(0x04cb, 0x013b)}, + {USB_DEVICE(0x04cb, 0x013d)}, + {USB_DEVICE(0x04cb, 0x013f)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct usb_fpix), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/gl860/Kconfig b/drivers/media/usb/gspca/gl860/Kconfig new file mode 100644 index 000000000000..22772f53ec7b --- /dev/null +++ b/drivers/media/usb/gspca/gl860/Kconfig @@ -0,0 +1,8 @@ +config USB_GL860 + tristate "GL860 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the GL860 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_gl860. diff --git a/drivers/media/usb/gspca/gl860/Makefile b/drivers/media/usb/gspca/gl860/Makefile new file mode 100644 index 000000000000..cf6397415aad --- /dev/null +++ b/drivers/media/usb/gspca/gl860/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_USB_GL860) += gspca_gl860.o + +gspca_gl860-objs := gl860.o \ + gl860-mi1320.o \ + gl860-ov2640.o \ + gl860-ov9655.o \ + gl860-mi2020.o + +ccflags-y += -I$(srctree)/drivers/media/usb/gspca + diff --git a/drivers/media/usb/gspca/gl860/gl860-mi1320.c b/drivers/media/usb/gspca/gl860/gl860-mi1320.c new file mode 100644 index 000000000000..b57160e04866 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-mi1320.c @@ -0,0 +1,536 @@ +/* Subdriver for the GL860 chip with the MI1320 sensor + * Author Olivier LORIN from own logs + * + * This program is free software; 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 + * 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 . + */ + +/* Sensor : MI1320 */ + +#include "gl860.h" + +static struct validx tbl_common[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0xffff, 0xffff}, + {0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1}, + {0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1}, + {0xba70, 0x0006}, {0xba0e, 0x00f1}, + {0xffff, 0xffff}, + {0xba74, 0x0006}, {0xba0e, 0x00f1}, + {0xffff, 0xffff}, + {0x0061, 0x0000}, {0x0068, 0x000d}, +}; + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, + {35, 0xffff}, + {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006}, + {0x006a, 0x000d}, +}; + +static struct validx tbl_sensor_settings_common[] = { + {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000}, + {0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006}, +}; +static struct validx tbl_sensor_settings_1280[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_sensor_settings_800[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_sensor_settings_640[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1}, + {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_post_unset_alt[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0x0061, 0x0000}, {0x0068, 0x000d}, +}; + +static u8 *tbl_1280[] = { + "\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" + "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" + "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" + "\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08" + , + "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" + "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1" +}; + +static u8 *tbl_800[] = { + "\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" + "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" + "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" + "\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08" + , + "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" + "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59" +}; + +static u8 *tbl_640[] = { + "\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c" + "\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01" + "\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04" + "\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09" + , + "\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6" + "\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\x00\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1" +}; + +static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d}; +static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70}; +static s32 tbl_backlight[] = {0x0e, 0x06, 0x02}; + +static s32 tbl_cntr1[] = { + 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0}; +static s32 tbl_cntr2[] = { + 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10}; + +static u8 dat_wbalNL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10" + "\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20" + "\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00"; + +static u8 dat_wbalLL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40" + "\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00" + "\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00"; + +static u8 dat_wbalBL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae" + "\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20" + "\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00"; + +static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00}; + +static u8 dat_common00[] = + "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42" + "\xd8\x04\x58\x00\x04\x02"; +static u8 dat_common01[] = + "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d" + "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0"; +static u8 dat_common02[] = + "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e" + "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00" + "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff"; +static u8 dat_common03[] = + "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda" + "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c" + "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c"; +static u8 dat_common04[] = + "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43"; +static u8 dat_common05[] = + "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68" + "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82" + "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b"; +static u8 dat_common06[] = + "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06" + "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4" + "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f"; +static u8 dat_common07[] = + "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72" + "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03" + "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea" + "\xe1\xff\xf1\x00"; +static u8 dat_common08[] = + "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7" + "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06" + "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a"; +static u8 dat_common09[] = + "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03" + "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa" + "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14"; +static u8 dat_common10[] = + "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00" + "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f" + "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01" + "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10"; +static u8 dat_common11[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10" + "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00" + "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81"; + +static int mi1320_init_at_startup(struct gspca_dev *gspca_dev); +static int mi1320_configure_alt(struct gspca_dev *gspca_dev); +static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev); +static int mi1320_init_post_alt(struct gspca_dev *gspca_dev); +static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev); +static int mi1320_sensor_settings(struct gspca_dev *gspca_dev); +static int mi1320_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void mi1320_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 0; + sd->vcur.sharpness = 6; + sd->vcur.contrast = 10; + sd->vcur.gamma = 20; + sd->vcur.hue = 0; + sd->vcur.saturation = 6; + sd->vcur.whitebal = 0; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; + sd->vcur.AC50Hz = 1; + + sd->vmax.backlight = 2; + sd->vmax.brightness = 8; + sd->vmax.sharpness = 7; + sd->vmax.contrast = 0; /* 10 but not working with this driver */ + sd->vmax.gamma = 40; + sd->vmax.hue = 5 + 1; + sd->vmax.saturation = 8; + sd->vmax.whitebal = 2; + sd->vmax.mirror = 1; + sd->vmax.flip = 1; + sd->vmax.AC50Hz = 1; + + sd->dev_camera_settings = mi1320_camera_settings; + sd->dev_init_at_startup = mi1320_init_at_startup; + sd->dev_configure_alt = mi1320_configure_alt; + sd->dev_init_pre_alt = mi1320_init_pre_alt; + sd->dev_post_unset_alt = mi1320_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + s32 n; /* reserved for FETCH functions */ + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01); + n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); +} + +static int mi1320_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + common(gspca_dev); + +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ + + return 0; +} + +static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirrorMask = 0; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.saturation = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.whitebal = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; + sd->vold.AC50Hz = -1; + + common(gspca_dev); + + mi1320_sensor_settings(gspca_dev); + + mi1320_init_post_alt(gspca_dev); + + return 0; +} + +static int mi1320_init_post_alt(struct gspca_dev *gspca_dev) +{ + mi1320_camera_settings(gspca_dev); + + return 0; +} + +static int mi1320_sensor_settings(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + fetch_validx(gspca_dev, tbl_sensor_settings_common, + ARRAY_SIZE(tbl_sensor_settings_common)); + + switch (reso) { + case IMAGE_1280: + fetch_validx(gspca_dev, tbl_sensor_settings_1280, + ARRAY_SIZE(tbl_sensor_settings_1280)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_1280[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_1280[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_1280[2]); + break; + + case IMAGE_800: + fetch_validx(gspca_dev, tbl_sensor_settings_800, + ARRAY_SIZE(tbl_sensor_settings_800)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_800[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_800[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_800[2]); + break; + + default: + fetch_validx(gspca_dev, tbl_sensor_settings_640, + ARRAY_SIZE(tbl_sensor_settings_640)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 60, tbl_640[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_640[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_640[2]); + break; + } + return 0; +} + +static int mi1320_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int mi1320_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 cntr = sd->vcur.contrast; + s32 gam = sd->vcur.gamma; + s32 hue = sd->vcur.hue; + s32 sat = sd->vcur.saturation; + s32 wbal = sd->vcur.whitebal; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + s32 i; + + if (freq != sd->vold.AC50Hz) { + sd->vold.AC50Hz = freq; + + freq = 2 * (freq == 0); + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba02, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x005b, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01 + freq, 0x00f1, 0, NULL); + } + + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + for (i = 0; i < 2; i++) { + if (wbal == 0) { /* Normal light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 48, dat_wbalNL); + } + + if (wbal == 1) { /* Low light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0004, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0043, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 48, dat_wbalLL); + } + + if (wbal == 2) { /* Back light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 44, dat_wbalBL); + } + } + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + bright = tbl_bright[bright]; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x0034, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x00f1, 0, NULL); + } + + if (sat != sd->vold.saturation) { + sd->vold.saturation = sat; + if (sat < 0 || sat > sd->vmax.saturation) + sat = 0; + + sat = tbl_sat[sat]; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0025, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sat, 0x00f1, 0, NULL); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0005, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sharp, 0x00f1, 0, NULL); + } + + if (hue != sd->vold.hue) { + /* 0=normal 1=NB 2="sepia" 3=negative 4=other 5=other2 */ + if (hue < 0 || hue > sd->vmax.hue) + hue = 0; + if (hue == sd->vmax.hue) + sd->swapRB = 1; + else + sd->swapRB = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, + 0, NULL); + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + backlight = tbl_backlight[backlight]; + for (i = 0; i < 2; i++) { + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba74, 0x0006, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba80 + backlight, 0x00f1, + 0, NULL); + } + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, + 0, NULL); + } + + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + u8 dat_hvflip2[4] = {0x20, 0x01, 0xf1, 0x00}; + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + dat_hvflip2[3] = flip + 2 * mirror; + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip2); + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + gam = 2 * gam; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba04 , 0x003b, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba02 + gam, 0x00f1, 0, NULL); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr1[cntr], 0x0035, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr2[cntr], 0x00f1, + 0, NULL); + } + + return 0; +} + +static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + + fetch_validx(gspca_dev, tbl_post_unset_alt, + ARRAY_SIZE(tbl_post_unset_alt)); +} diff --git a/drivers/media/usb/gspca/gl860/gl860-mi2020.c b/drivers/media/usb/gspca/gl860/gl860-mi2020.c new file mode 100644 index 000000000000..2edda6b7d653 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-mi2020.c @@ -0,0 +1,733 @@ +/* Subdriver for the GL860 chip with the MI2020 sensor + * Author Olivier LORIN, from logs by Iceman/Soro2005 + Fret_saw/Hulkie/Tricid + * with the help of Kytrix/BUGabundo/Blazercist. + * Driver achieved thanks to a webcam gift by Kytrix. + * + * This program is free software; 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 + * 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 . + */ + +/* Sensor : MI2020 */ + +#include "gl860.h" + +static u8 dat_wbal1[] = {0x8c, 0xa2, 0x0c}; + +static u8 dat_bright1[] = {0x8c, 0xa2, 0x06}; +static u8 dat_bright3[] = {0x8c, 0xa1, 0x02}; +static u8 dat_bright4[] = {0x90, 0x00, 0x0f}; +static u8 dat_bright5[] = {0x8c, 0xa1, 0x03}; +static u8 dat_bright6[] = {0x90, 0x00, 0x05}; + +static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19}; +static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b}; +static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03}; +static u8 dat_hvflip6[] = {0x90, 0x00, 0x06}; + +static struct idxdata tbl_middle_hvflip_low[] = { + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_middle_hvflip_big[] = { + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {102, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, +}; + +static struct idxdata tbl_end_hvflip[] = { + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, +}; + +static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; + +static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; +static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + {53, 0xffff}, + {0x0040, 0x0000}, {0x0063, 0x0006}, +}; + +static struct validx tbl_common_0B[] = { + {0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, + {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, +}; + +static struct idxdata tbl_common_3B[] = { + {0x33, "\x86\x25\x01"}, {0x33, "\x86\x25\x00"}, + {2, "\xff\xff\xff"}, + {0x30, "\x1a\x0a\xcc"}, {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, + {6, "\xff\xff\xff"}, /* 12 */ + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, /* - */ + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"}, + {0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"}, + {0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"}, + {0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"}, + {0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"}, + {0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"}, + {0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"}, + {0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, + {0x33, "\x78\x00\x00"}, + {2, "\xff\xff\xff"}, + {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"}, + {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"}, + {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"}, + {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"}, + {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, {0x33, "\x8c\xa4\x04"}, + {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, + {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x25"}, + {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x47"}, + {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x02\x84"}, + {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, {0x33, "\x8c\x27\x07"}, + {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, {0x33, "\x90\x04\xb0"}, + {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x0f"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, {0x33, "\x90\x04\xbd"}, + {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, {0x33, "\x8c\x27\x15"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, {0x33, "\x8c\x27\x1b"}, + {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, {0x33, "\x90\x01\x02"}, + {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, {0x33, "\x8c\x27\x21"}, + {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, {0x33, "\x90\x02\x85"}, + {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x27"}, + {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, {0x33, "\x90\x20\x20"}, + {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, {0x33, "\x8c\x27\x2d"}, + {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x33"}, + {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, {0x33, "\x90\x06\x4b"}, + {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x39"}, + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, + {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x41"}, + {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, {0x33, "\x90\x04\xed"}, + {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x51"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, {0x33, "\x90\x03\x20"}, + {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x57"}, + {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x63"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, {0x33, "\x90\x04\xb0"}, + {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\xa4\x08"}, + {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, + {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, + {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa1"}, + {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, {0x33, "\x8c\x24\x15"}, + {0x33, "\x90\x00\x6a"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\x80"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {3, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_init_post_alt_low1[] = { + {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, + {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, + {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, + {0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"}, + {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"}, + {0x33, "\x90\x00\x9b"}, +}; + +static struct idxdata tbl_init_post_alt_low2[] = { + {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, + {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_init_post_alt_low3[] = { + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, + {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, +}; + +static struct idxdata tbl_init_post_alt_big[] = { + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"}, + {0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"}, + {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"}, + {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {0x33, "\x8c\x27\x97"}, {0x33, "\x90\x01\x00"}, + {51, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {51, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {51, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_init_post_alt_3B[] = { + {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, +}; + +static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81"; +static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21"; +static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01"; +static u8 *dat_1600 = "\xd0\x02\xd1\x20\xd2\xaf\xd3\x02\xd4\x30\xd5\x41"; + +static int mi2020_init_at_startup(struct gspca_dev *gspca_dev); +static int mi2020_configure_alt(struct gspca_dev *gspca_dev); +static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev); +static int mi2020_init_post_alt(struct gspca_dev *gspca_dev); +static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev); +static int mi2020_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void mi2020_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 70; + sd->vcur.sharpness = 20; + sd->vcur.contrast = 0; + sd->vcur.gamma = 0; + sd->vcur.hue = 0; + sd->vcur.saturation = 60; + sd->vcur.whitebal = 0; /* 50, not done by hardware */ + sd->vcur.mirror = 0; + sd->vcur.flip = 0; + sd->vcur.AC50Hz = 1; + + sd->vmax.backlight = 64; + sd->vmax.brightness = 128; + sd->vmax.sharpness = 40; + sd->vmax.contrast = 3; + sd->vmax.gamma = 2; + sd->vmax.hue = 0 + 1; /* 200, not done by hardware */ + sd->vmax.saturation = 0; /* 100, not done by hardware */ + sd->vmax.whitebal = 2; /* 100, not done by hardware */ + sd->vmax.mirror = 1; + sd->vmax.flip = 1; + sd->vmax.AC50Hz = 1; + + sd->dev_camera_settings = mi2020_camera_settings; + sd->dev_init_at_startup = mi2020_init_at_startup; + sd->dev_configure_alt = mi2020_configure_alt; + sd->dev_init_pre_alt = mi2020_init_pre_alt; + sd->dev_post_unset_alt = mi2020_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_common_0B, ARRAY_SIZE(tbl_common_0B)); + fetch_idxdata(gspca_dev, tbl_common_3B, ARRAY_SIZE(tbl_common_3B)); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); +} + +static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) +{ + u8 c; + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); + + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &c); + + common(gspca_dev); + + msleep(61); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ +/* msleep(36); */ + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); + + return 0; +} + +static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirrorMask = 0; + sd->vold.hue = -1; + + /* These controls need to be reset */ + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + + /* If not different from default, they do not need to be set */ + sd->vold.contrast = 0; + sd->vold.gamma = 0; + sd->vold.backlight = 0; + + mi2020_init_post_alt(gspca_dev); + + return 0; +} + +static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + s32 wbal = sd->vcur.whitebal; + + u8 dat_freq2[] = {0x90, 0x00, 0x80}; + u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi2[] = {0x90, 0x00, 0x00}; + u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi4[] = {0x90, 0x00, 0x00}; + u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; + u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 dat_wbal2[] = {0x90, 0x00, 0x00}; + u8 c; + + sd->nbIm = -1; + + dat_freq2[2] = freq ? 0xc0 : 0x80; + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + if (wbal == 0) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x17; + } else if (wbal == 1) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x35; + } else if (wbal == 2) { + dat_multi4[2] = dat_multi2[2] = 0x20; + dat_wbal2[2] = 0x17; + } + dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); + dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + + msleep(200); + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + msleep(2); + + common(gspca_dev); + + msleep(142); + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); + + switch (reso) { + case IMAGE_640: + case IMAGE_800: + if (reso != IMAGE_800) + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_640); + else + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_800); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, + ARRAY_SIZE(tbl_init_post_alt_low1)); + + if (reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_init_post_alt_low2, + ARRAY_SIZE(tbl_init_post_alt_low2)); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_low3, + ARRAY_SIZE(tbl_init_post_alt_low3)); + + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(120); + break; + + case IMAGE_1280: + case IMAGE_1600: + if (reso == IMAGE_1280) { + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1280); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x07"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x05\x04"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x09"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x04\x02"); + } else { + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1600); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x07"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x06\x40"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x09"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x04\xb0"); + } + + fetch_idxdata(gspca_dev, tbl_init_post_alt_big, + ARRAY_SIZE(tbl_init_post_alt_big)); + + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(1850); + } + + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(40); + + /* AC power frequency */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(33); + /* light source */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + msleep(7); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, + ARRAY_SIZE(tbl_init_post_alt_3B)); + + /* hvflip */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + msleep(250); + + if (reso == IMAGE_640 || reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, + ARRAY_SIZE(tbl_middle_hvflip_low)); + else + fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, + ARRAY_SIZE(tbl_middle_hvflip_big)); + + fetch_idxdata(gspca_dev, tbl_end_hvflip, + ARRAY_SIZE(tbl_end_hvflip)); + + sd->nbIm = 0; + + sd->vold.mirror = mirror; + sd->vold.flip = flip; + sd->vold.AC50Hz = freq; + sd->vold.whitebal = wbal; + + mi2020_camera_settings(gspca_dev); + + return 0; +} + +static int mi2020_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + case IMAGE_1600: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int mi2020_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 cntr = sd->vcur.contrast; + s32 gam = sd->vcur.gamma; + s32 hue = (sd->vcur.hue > 0); + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + s32 wbal = sd->vcur.whitebal; + + u8 dat_sharp[] = {0x6c, 0x00, 0x08}; + u8 dat_bright2[] = {0x90, 0x00, 0x00}; + u8 dat_freq2[] = {0x90, 0x00, 0x80}; + u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi2[] = {0x90, 0x00, 0x00}; + u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi4[] = {0x90, 0x00, 0x00}; + u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; + u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 dat_wbal2[] = {0x90, 0x00, 0x00}; + + /* Less than 4 images received -> too early to set the settings */ + if (sd->nbIm < 4) { + sd->waitSet = 1; + return 0; + } + sd->waitSet = 0; + + if (freq != sd->vold.AC50Hz) { + sd->vold.AC50Hz = freq; + + dat_freq2[2] = freq ? 0xc0 : 0x80; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(20); + } + + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + if (wbal == 0) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x17; + } else if (wbal == 1) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x35; + } else if (wbal == 2) { + dat_multi4[2] = dat_multi2[2] = 0x20; + dat_wbal2[2] = 0x17; + } + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); + dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, + ARRAY_SIZE(tbl_init_post_alt_3B)); + + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + msleep(40); + + if (reso == IMAGE_640 || reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, + ARRAY_SIZE(tbl_middle_hvflip_low)); + else + fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, + ARRAY_SIZE(tbl_middle_hvflip_big)); + + fetch_idxdata(gspca_dev, tbl_end_hvflip, + ARRAY_SIZE(tbl_end_hvflip)); + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + dat_bright2[2] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); + } + + if (cntr != sd->vold.contrast || gam != sd->vold.gamma) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + dat_multi1[2] = 0x6d; + dat_multi3[2] = dat_multi1[2] + 1; + if (cntr == 0) + cntr = 4; + dat_multi4[2] = dat_multi2[2] = cntr * 0x10 + 2 - gam; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = backlight; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + dat_sharp[1] = sharp; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0032, 3, dat_sharp); + } + + if (hue != sd->vold.hue) { + sd->swapRB = hue; + sd->vold.hue = hue; + } + + return 0; +} + +static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + msleep(40); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); +} diff --git a/drivers/media/usb/gspca/gl860/gl860-ov2640.c b/drivers/media/usb/gspca/gl860/gl860-ov2640.c new file mode 100644 index 000000000000..768cac5cd72b --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-ov2640.c @@ -0,0 +1,489 @@ +/* Subdriver for the GL860 chip with the OV2640 sensor + * Author Olivier LORIN, from Malmostoso's logs + * + * This program is free software; 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 + * 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 . + */ + +/* Sensor : OV2640 */ + +#include "gl860.h" + +static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01"; + +static u8 c61[] = {0x61}; /* expected */ +static u8 c51[] = {0x51}; /* expected */ +static u8 c50[] = {0x50}; /* expected */ +static u8 c28[] = {0x28}; /* expected */ +static u8 ca8[] = {0xa8}; /* expected */ + +static u8 dat_post[] = + "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01"; + +static u8 dat_640[] = "\xd0\x01\xd1\x08\xd2\xe0\xd3\x02\xd4\x10\xd5\x81"; +static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21"; +static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01"; +static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41"; + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + {0x0050, 0x0000}, {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0061, 0x0006}, + {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, + {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, + {0x0041, 0x0000}, {0x0061, 0x0000}, +}; + +static struct validx tbl_common[] = { + {0x6000, 0x00ff}, {0x60ff, 0x002c}, {0x60df, 0x002e}, {0x6001, 0x00ff}, + {0x6080, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, + {0x6035, 0x003c}, {0x6000, 0x0011}, {0x6028, 0x0004}, {0x60e5, 0x0013}, + {0x6088, 0x0014}, {0x600c, 0x002c}, {0x6078, 0x0033}, {0x60f7, 0x003b}, + {0x6000, 0x003e}, {0x6011, 0x0043}, {0x6010, 0x0016}, {0x6082, 0x0039}, + {0x6088, 0x0035}, {0x600a, 0x0022}, {0x6040, 0x0037}, {0x6000, 0x0023}, + {0x60a0, 0x0034}, {0x601a, 0x0036}, {0x6002, 0x0006}, {0x60c0, 0x0007}, + {0x60b7, 0x000d}, {0x6001, 0x000e}, {0x6000, 0x004c}, {0x6081, 0x004a}, + {0x6099, 0x0021}, {0x6002, 0x0009}, {0x603e, 0x0024}, {0x6034, 0x0025}, + {0x6081, 0x0026}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, + {0x6000, 0x005c}, {0x6000, 0x0063}, {0x6000, 0x007c}, {0x6070, 0x0061}, + {0x6080, 0x0062}, {0x6080, 0x0020}, {0x6030, 0x0028}, {0x6000, 0x006c}, + {0x6000, 0x006e}, {0x6002, 0x0070}, {0x6094, 0x0071}, {0x60c1, 0x0073}, + {0x6034, 0x003d}, {0x6057, 0x005a}, {0x60bb, 0x004f}, {0x609c, 0x0050}, + {0x6080, 0x006d}, {0x6002, 0x0039}, {0x6033, 0x003a}, {0x60f1, 0x003b}, + {0x6031, 0x003c}, {0x6000, 0x00ff}, {0x6014, 0x00e0}, {0x60ff, 0x0076}, + {0x60a0, 0x0033}, {0x6020, 0x0042}, {0x6018, 0x0043}, {0x6000, 0x004c}, + {0x60d0, 0x0087}, {0x600f, 0x0088}, {0x6003, 0x00d7}, {0x6010, 0x00d9}, + {0x6005, 0x00da}, {0x6082, 0x00d3}, {0x60c0, 0x00f9}, {0x6006, 0x0044}, + {0x6007, 0x00d1}, {0x6002, 0x00d2}, {0x6000, 0x00d2}, {0x6011, 0x00d8}, + {0x6008, 0x00c8}, {0x6080, 0x00c9}, {0x6008, 0x007c}, {0x6020, 0x007d}, + {0x6020, 0x007d}, {0x6000, 0x0090}, {0x600e, 0x0091}, {0x601a, 0x0091}, + {0x6031, 0x0091}, {0x605a, 0x0091}, {0x6069, 0x0091}, {0x6075, 0x0091}, + {0x607e, 0x0091}, {0x6088, 0x0091}, {0x608f, 0x0091}, {0x6096, 0x0091}, + {0x60a3, 0x0091}, {0x60af, 0x0091}, {0x60c4, 0x0091}, {0x60d7, 0x0091}, + {0x60e8, 0x0091}, {0x6020, 0x0091}, {0x6000, 0x0092}, {0x6006, 0x0093}, + {0x60e3, 0x0093}, {0x6005, 0x0093}, {0x6005, 0x0093}, {0x6000, 0x0093}, + {0x6004, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, + {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, + {0x6000, 0x0096}, {0x6008, 0x0097}, {0x6019, 0x0097}, {0x6002, 0x0097}, + {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, {0x6028, 0x0097}, + {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6098, 0x0097}, {0x6080, 0x0097}, + {0x6000, 0x0097}, {0x6000, 0x0097}, {0x60ed, 0x00c3}, {0x609a, 0x00c4}, + {0x6000, 0x00a4}, {0x6011, 0x00c5}, {0x6051, 0x00c6}, {0x6010, 0x00c7}, + {0x6066, 0x00b6}, {0x60a5, 0x00b8}, {0x6064, 0x00b7}, {0x607c, 0x00b9}, + {0x60af, 0x00b3}, {0x6097, 0x00b4}, {0x60ff, 0x00b5}, {0x60c5, 0x00b0}, + {0x6094, 0x00b1}, {0x600f, 0x00b2}, {0x605c, 0x00c4}, {0x6000, 0x00a8}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x601d, 0x0086}, {0x6000, 0x0050}, + {0x6090, 0x0051}, {0x6018, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054}, + {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6090, 0x005a}, {0x6018, 0x005b}, + {0x6005, 0x005c}, {0x60ed, 0x00c3}, {0x6000, 0x007f}, {0x6005, 0x00da}, + {0x601f, 0x00e5}, {0x6067, 0x00e1}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, + {0x6000, 0x0005}, {0x6001, 0x00ff}, {0x6000, 0x0000}, {0x6000, 0x0045}, + {0x6000, 0x0010}, +}; + +static struct validx tbl_sensor_settings_common1[] = { + {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, + {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000}, + {50, 0xffff}, + {0x0061, 0x0000}, + {0xffff, 0xffff}, + {0x6000, 0x00ff}, {0x6000, 0x007c}, {0x6007, 0x007d}, + {30, 0xffff}, + {0x0040, 0x0000}, +}; + +static struct validx tbl_sensor_settings_common2[] = { + {0x6001, 0x00ff}, {0x6038, 0x000c}, + {10, 0xffff}, + {0x6000, 0x0011}, +}; + +static struct validx tbl_640[] = { + {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, + {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, + {0x6075, 0x0018}, {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, + {0x60bb, 0x004f}, {0x6057, 0x005a}, {0x609c, 0x0050}, {0x6080, 0x006d}, + {0x6092, 0x0026}, {0x60ff, 0x0020}, {0x6000, 0x0027}, {0x6000, 0x00ff}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, {0x603d, 0x0086}, + {0x6089, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053}, + {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x60a0, 0x005a}, + {0x6078, 0x005b}, {0x6000, 0x005c}, {0x6004, 0x00d3}, {0x6000, 0x00e0}, + {0x60ff, 0x00dd}, {0x60a1, 0x005a}, +}; + +static struct validx tbl_800[] = { + {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, + {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6040, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, + {0x6043, 0x0018}, {0x6000, 0x0019}, {0x604b, 0x001a}, {0x6009, 0x0032}, + {0x60ca, 0x004f}, {0x60a8, 0x0050}, {0x6000, 0x006d}, {0x6038, 0x003d}, + {0x60c8, 0x0035}, {0x6000, 0x0022}, {0x6092, 0x0026}, {0x60ff, 0x0020}, + {0x6000, 0x0027}, {0x6000, 0x00ff}, {0x6064, 0x00c0}, {0x604b, 0x00c1}, + {0x6000, 0x008c}, {0x601d, 0x0086}, {0x6082, 0x00d3}, {0x6000, 0x00e0}, + {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018}, +}; + +static struct validx tbl_big1[] = { + {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, + {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018}, + {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, {0x60bb, 0x004f}, + {0x609c, 0x0050}, {0x6057, 0x005a}, {0x6080, 0x006d}, {0x6043, 0x000f}, + {0x608f, 0x0003}, {0x6005, 0x007c}, {0x6081, 0x0026}, {0x6000, 0x00ff}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, +}; + +static struct validx tbl_big2[] = { + {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, + {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, + {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3}, + {0x6000, 0x008e}, +}; + +static struct validx tbl_big3[] = { + {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd}, + {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7}, + {0x6000, 0x0092}, {0x6006, 0x0093}, {0x60e3, 0x0093}, {0x6005, 0x0093}, + {0x6005, 0x0093}, {0x60ed, 0x00c3}, {0x6000, 0x00a4}, {0x60d0, 0x0087}, + {0x6003, 0x0096}, {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, + {0x6028, 0x0097}, {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6001, 0x00ff}, + {0x6043, 0x000f}, {0x608f, 0x0003}, {0x6000, 0x002d}, {0x6000, 0x002e}, + {0x600a, 0x0022}, {0x6002, 0x0070}, {0x6008, 0x0014}, {0x6048, 0x0014}, + {0x6000, 0x00ff}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, +}; + +static struct validx tbl_post_unset_alt[] = { + {0x006a, 0x000d}, {0x6001, 0x00ff}, {0x6081, 0x0026}, {0x6000, 0x0000}, + {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6068, 0x000d}, + {50, 0xffff}, + {0x0021, 0x0000}, +}; + +static int ov2640_init_at_startup(struct gspca_dev *gspca_dev); +static int ov2640_configure_alt(struct gspca_dev *gspca_dev); +static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev); +static int ov2640_init_post_alt(struct gspca_dev *gspca_dev); +static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev); +static int ov2640_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void ov2640_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 32; + sd->vcur.brightness = 0; + sd->vcur.sharpness = 6; + sd->vcur.contrast = 0; + sd->vcur.gamma = 32; + sd->vcur.hue = 0; + sd->vcur.saturation = 128; + sd->vcur.whitebal = 64; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; + + sd->vmax.backlight = 64; + sd->vmax.brightness = 255; + sd->vmax.sharpness = 31; + sd->vmax.contrast = 255; + sd->vmax.gamma = 64; + sd->vmax.hue = 254 + 2; + sd->vmax.saturation = 255; + sd->vmax.whitebal = 128; + sd->vmax.mirror = 1; + sd->vmax.flip = 1; + sd->vmax.AC50Hz = 0; + + sd->dev_camera_settings = ov2640_camera_settings; + sd->dev_init_at_startup = ov2640_init_at_startup; + sd->dev_configure_alt = ov2640_configure_alt; + sd->dev_init_pre_alt = ov2640_init_pre_alt; + sd->dev_post_unset_alt = ov2640_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); +} + +static int ov2640_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_init1); + + common(gspca_dev); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61); + + ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51); + + ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ + + return 0; +} + +static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirrorMask = 0; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.saturation = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.whitebal = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; + + ov2640_init_post_alt(gspca_dev); + + return 0; +} + +static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + s32 n; /* reserved for FETCH functions */ + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + n = fetch_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post); + common(gspca_dev); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1), n); + + switch (reso) { + case IMAGE_640: + n = fetch_validx(gspca_dev, tbl_640, ARRAY_SIZE(tbl_640)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_640); + break; + + case IMAGE_800: + n = fetch_validx(gspca_dev, tbl_800, ARRAY_SIZE(tbl_800)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800); + break; + + case IMAGE_1600: + case IMAGE_1280: + n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1)); + + if (reso == IMAGE_1280) { + n = fetch_validx(gspca_dev, tbl_big2, + ARRAY_SIZE(tbl_big2)); + } else { + ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL); + } + + n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3)); + + if (reso == IMAGE_1280) { + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1280); + } else { + ctrl_out(gspca_dev, 0x40, 1, 0x6020, 0x008c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6076, 0x0018, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1600); + } + break; + } + + n = fetch_validx(gspca_dev, tbl_sensor_settings_common2, + ARRAY_SIZE(tbl_sensor_settings_common2)); + + ov2640_camera_settings(gspca_dev); + + return 0; +} + +static int ov2640_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + case IMAGE_1600: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int ov2640_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 gam = sd->vcur.gamma; + s32 cntr = sd->vcur.contrast; + s32 sat = sd->vcur.saturation; + s32 hue = sd->vcur.hue; + s32 wbal = sd->vcur.whitebal; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0); + + if (backlight != sd->vold.backlight) { + /* No sd->vold.backlight=backlight; (to be done again later) */ + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, + 0, NULL); + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6009 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + bright, 0x007d, 0, NULL); + } + + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6003 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + wbal, 0x007d, 0, NULL); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6007 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + cntr, 0x007d, 0, NULL); + } + + if (sat != sd->vold.saturation) { + sd->vold.saturation = sat; + if (sat < 0 || sat > sd->vmax.saturation) + sat = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + sat, 0x007d, 0, NULL); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x0092, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x60c0 + sharp, 0x0093, 0, NULL); + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + if (hue < 0 || hue > sd->vmax.hue) + hue = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d, + 0, NULL); + if (hue >= 255) + sd->swapRB = 1; + else + sd->swapRB = 0; + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6008 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL); + } + + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + mirror = 0x80 * mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL); + + flip = 0x50 * flip + mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, + 0, NULL); + } + + return 0; +} + +static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + msleep(20); + fetch_validx(gspca_dev, tbl_post_unset_alt, + ARRAY_SIZE(tbl_post_unset_alt)); +} diff --git a/drivers/media/usb/gspca/gl860/gl860-ov9655.c b/drivers/media/usb/gspca/gl860/gl860-ov9655.c new file mode 100644 index 000000000000..5ae9619d72a5 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-ov9655.c @@ -0,0 +1,336 @@ +/* Subdriver for the GL860 chip with the OV9655 sensor + * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt + * on dsd's weblog + * + * This program is free software; 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 + * 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 . + */ + +/* Sensor : OV9655 */ + +#include "gl860.h" + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + + {0x0040, 0x0000}, +}; + +static struct validx tbl_commmon[] = { + {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, + {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000}, + {0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000}, +}; + +static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12}; + +static u8 *tbl_640[] = { + "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" + , + "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61" + "\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00" + "\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" + "\x29\x15\x2a\x00\x2b\x00\x2c\x08" + , + "\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00" + "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" + "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7" + "\x4d\xe7\x4e\xe7" + , + "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" + "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" + "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04" + "\x6d\x55\x6e\x00\x6f\x9d" + , + "\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02" + "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" + "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" + "\x8a\x23\x8c\x8d\x90\x7c\x91\x7b" + , + "\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70" + "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" + "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" + , + "\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01" + "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" + , + "\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80" +}; + +static u8 *tbl_1280[] = { + "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" + , + "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61" + "\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb" + "\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" + "\x29\x15\x2a\x00\x2b\x00\x2c\x08" + , + "\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00" + "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" + "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8" + "\x4d\xe8\x4e\xe8" + , + "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" + "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" + "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04" + "\x6d\x55\x6e\x00\x6f\x9d" + , + "\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02" + "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" + "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" + "\x8a\x23\x8c\x0d\x90\x90\x91\x90" + , + "\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70" + "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" + "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" + , + "\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01" + "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" + , + "\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00" +}; + +static u8 c04[] = {0x04}; +static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; +static u8 dat_post2[] = "\x10\x10\xc1\x02"; +static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; +static u8 dat_post4[] = "\x10\x02\xc1\x06"; +static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; +static u8 dat_post6[] = "\x10\x10\xc1\x05"; +static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; +static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; + +static struct validx tbl_init_post_alt[] = { + {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff}, + {0x6003, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6001, 0x00ff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, + {0xffff, 0xffff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, + {0xffff, 0xffff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, +}; + +static int ov9655_init_at_startup(struct gspca_dev *gspca_dev); +static int ov9655_configure_alt(struct gspca_dev *gspca_dev); +static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev); +static int ov9655_init_post_alt(struct gspca_dev *gspca_dev); +static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev); +static int ov9655_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void ov9655_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 128; + sd->vcur.sharpness = 0; + sd->vcur.contrast = 0; + sd->vcur.gamma = 0; + sd->vcur.hue = 0; + sd->vcur.saturation = 0; + sd->vcur.whitebal = 0; + + sd->vmax.backlight = 0; + sd->vmax.brightness = 255; + sd->vmax.sharpness = 0; + sd->vmax.contrast = 0; + sd->vmax.gamma = 0; + sd->vmax.hue = 0 + 1; + sd->vmax.saturation = 0; + sd->vmax.whitebal = 0; + sd->vmax.mirror = 0; + sd->vmax.flip = 0; + sd->vmax.AC50Hz = 0; + + sd->dev_camera_settings = ov9655_camera_settings; + sd->dev_init_at_startup = ov9655_init_at_startup; + sd->dev_configure_alt = ov9655_configure_alt; + sd->dev_init_pre_alt = ov9655_init_pre_alt; + sd->dev_post_unset_alt = ov9655_post_unset_alt; +} + +/*==========================================================================*/ + +static int ov9655_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/ + + return 0; +} + +static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vold.brightness = -1; + sd->vold.hue = -1; + + fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); + + ov9655_init_post_alt(gspca_dev); + + return 0; +} + +static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + s32 n; /* reserved for FETCH functions */ + s32 i; + u8 **tbl; + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + tbl = (reso == IMAGE_640) ? tbl_640 : tbl_1280; + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + tbl_length[0], tbl[0]); + for (i = 1; i < 7; i++) + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, + tbl_length[i], tbl[i]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + tbl_length[7], tbl[7]); + + n = fetch_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt)); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7); + + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8); + + ov9655_camera_settings(gspca_dev); + + return 0; +} + +static int ov9655_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 1 + 1; + break; + + default: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int ov9655_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70"; + + s32 bright = sd->vcur.brightness; + s32 hue = sd->vcur.hue; + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + dat_bright[3] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_bright); + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + sd->swapRB = (hue != 0); + } + + return 0; +} + +static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0061, 0x0000, 0, NULL); +} diff --git a/drivers/media/usb/gspca/gl860/gl860.c b/drivers/media/usb/gspca/gl860/gl860.c new file mode 100644 index 000000000000..ced3b71f14e5 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860.c @@ -0,0 +1,725 @@ +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver core + * + * 2009/09/24 Olivier Lorin + * GSPCA by Jean-Francois Moine + * Thanks BUGabundo and Malmostoso for your amazing help! + * + * This program is free software; 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 + * 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 . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "gspca.h" +#include "gl860.h" + +MODULE_AUTHOR("Olivier Lorin "); +MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver"); +MODULE_LICENSE("GPL"); + +/*======================== static function declarations ====================*/ + +static void (*dev_init_settings)(struct gspca_dev *gspca_dev); + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id); +static int sd_init(struct gspca_dev *gspca_dev); +static int sd_isoc_init(struct gspca_dev *gspca_dev); +static int sd_start(struct gspca_dev *gspca_dev); +static void sd_stop0(struct gspca_dev *gspca_dev); +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len); +static void sd_callback(struct gspca_dev *gspca_dev); + +static int gl860_guess_sensor(struct gspca_dev *gspca_dev, + u16 vendor_id, u16 product_id); + +/*============================ driver options ==============================*/ + +static s32 AC50Hz = 0xff; +module_param(AC50Hz, int, 0644); +MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); + +static char sensor[7]; +module_param_string(sensor, sensor, sizeof(sensor), 0644); +MODULE_PARM_DESC(sensor, + " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')"); + +/*============================ webcam controls =============================*/ + +/* Functions to get and set a control value */ +#define SD_SETGET(thename) \ +static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ +{\ + struct sd *sd = (struct sd *) gspca_dev;\ +\ + sd->vcur.thename = val;\ + if (gspca_dev->streaming)\ + sd->waitSet = 1;\ + return 0;\ +} \ +static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ +{\ + struct sd *sd = (struct sd *) gspca_dev;\ +\ + *val = sd->vcur.thename;\ + return 0;\ +} + +SD_SETGET(mirror) +SD_SETGET(flip) +SD_SETGET(AC50Hz) +SD_SETGET(backlight) +SD_SETGET(brightness) +SD_SETGET(gamma) +SD_SETGET(hue) +SD_SETGET(saturation) +SD_SETGET(sharpness) +SD_SETGET(whitebal) +SD_SETGET(contrast) + +#define GL860_NCTRLS 11 + +/* control table */ +static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; +static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; +static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; +static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; + +#define SET_MY_CTRL(theid, \ + thetype, thelabel, thename) \ + if (sd->vmax.thename != 0) {\ + sd_ctrls[nCtrls].qctrl.id = theid;\ + sd_ctrls[nCtrls].qctrl.type = thetype;\ + strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\ + sd_ctrls[nCtrls].qctrl.minimum = 0;\ + sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\ + sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\ + sd_ctrls[nCtrls].qctrl.step = \ + (sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\ + sd_ctrls[nCtrls].set = sd_set_##thename;\ + sd_ctrls[nCtrls].get = sd_get_##thename;\ + nCtrls++;\ + } + +static int gl860_build_control_table(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct ctrl *sd_ctrls; + int nCtrls = 0; + + if (_MI1320_) + sd_ctrls = sd_ctrls_mi1320; + else if (_MI2020_) + sd_ctrls = sd_ctrls_mi2020; + else if (_OV2640_) + sd_ctrls = sd_ctrls_ov2640; + else if (_OV9655_) + sd_ctrls = sd_ctrls_ov9655; + else + return 0; + + memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl)); + + SET_MY_CTRL(V4L2_CID_BRIGHTNESS, + V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness) + SET_MY_CTRL(V4L2_CID_SHARPNESS, + V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness) + SET_MY_CTRL(V4L2_CID_CONTRAST, + V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast) + SET_MY_CTRL(V4L2_CID_GAMMA, + V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma) + SET_MY_CTRL(V4L2_CID_HUE, + V4L2_CTRL_TYPE_INTEGER, "Palette", hue) + SET_MY_CTRL(V4L2_CID_SATURATION, + V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation) + SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE, + V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal) + SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION, + V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight) + + SET_MY_CTRL(V4L2_CID_HFLIP, + V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror) + SET_MY_CTRL(V4L2_CID_VFLIP, + V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) + SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz) + + return nCtrls; +} + +/*==================== sud-driver structure initialisation =================*/ + +static const struct sd_desc sd_desc_mi1320 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi1320, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static const struct sd_desc sd_desc_mi2020 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi2020, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static const struct sd_desc sd_desc_ov2640 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov2640, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static const struct sd_desc sd_desc_ov9655 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov9655, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +/*=========================== sub-driver image sizes =======================*/ + +static struct v4l2_pix_format mi2020_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 598, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, + {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1198, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 + }, +}; + +static struct v4l2_pix_format ov2640_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, + {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 + }, +}; + +static struct v4l2_pix_format mi1320_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, +}; + +static struct v4l2_pix_format ov9655_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, +}; + +/*========================= sud-driver functions ===========================*/ + +/* This function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + u16 vendor_id, product_id; + + /* Get USB VendorID and ProductID */ + vendor_id = id->idVendor; + product_id = id->idProduct; + + sd->nbRightUp = 1; + sd->nbIm = -1; + + sd->sensor = 0xff; + if (strcmp(sensor, "MI1320") == 0) + sd->sensor = ID_MI1320; + else if (strcmp(sensor, "OV2640") == 0) + sd->sensor = ID_OV2640; + else if (strcmp(sensor, "OV9655") == 0) + sd->sensor = ID_OV9655; + else if (strcmp(sensor, "MI2020") == 0) + sd->sensor = ID_MI2020; + + /* Get sensor and set the suitable init/start/../stop functions */ + if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) + return -1; + + cam = &gspca_dev->cam; + + switch (sd->sensor) { + case ID_MI1320: + gspca_dev->sd_desc = &sd_desc_mi1320; + cam->cam_mode = mi1320_mode; + cam->nmodes = ARRAY_SIZE(mi1320_mode); + dev_init_settings = mi1320_init_settings; + break; + + case ID_MI2020: + gspca_dev->sd_desc = &sd_desc_mi2020; + cam->cam_mode = mi2020_mode; + cam->nmodes = ARRAY_SIZE(mi2020_mode); + dev_init_settings = mi2020_init_settings; + break; + + case ID_OV2640: + gspca_dev->sd_desc = &sd_desc_ov2640; + cam->cam_mode = ov2640_mode; + cam->nmodes = ARRAY_SIZE(ov2640_mode); + dev_init_settings = ov2640_init_settings; + break; + + case ID_OV9655: + gspca_dev->sd_desc = &sd_desc_ov9655; + cam->cam_mode = ov9655_mode; + cam->nmodes = ARRAY_SIZE(ov9655_mode); + dev_init_settings = ov9655_init_settings; + break; + } + + dev_init_settings(gspca_dev); + if (AC50Hz != 0xff) + ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; + gl860_build_control_table(gspca_dev); + + return 0; +} + +/* This function is called at probe time after sd_config */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_init_at_startup(gspca_dev); +} + +/* This function is called before to choose the alt setting */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_configure_alt(gspca_dev); +} + +/* This function is called to start the webcam */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_init_pre_alt(gspca_dev); +} + +/* This function is called to stop the webcam */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->gspca_dev.present) + return; + + return sd->dev_post_unset_alt(gspca_dev); +} + +/* This function is called when an image is being received */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + static s32 nSkipped; + + s32 mode = (s32) gspca_dev->curr_mode; + s32 nToSkip = + sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); + + /* Test only against 0202h, so endianess does not matter */ + switch (*(s16 *) data) { + case 0x0202: /* End of frame, start a new one */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + nSkipped = 0; + if (sd->nbIm >= 0 && sd->nbIm < 10) + sd->nbIm++; + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + break; + + default: + data += 2; + len -= 2; + if (nSkipped + len <= nToSkip) + nSkipped += len; + else { + if (nSkipped < nToSkip && nSkipped + len > nToSkip) { + data += nToSkip - nSkipped; + len -= nToSkip - nSkipped; + nSkipped = nToSkip + 1; + } + gspca_frame_add(gspca_dev, + INTER_PACKET, data, len); + } + break; + } +} + +/* This function is called when an image has been read */ +/* This function is used to monitor webcam orientation */ +static void sd_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!_OV9655_) { + u8 state; + u8 upsideDown; + + /* Probe sensor orientation */ + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state); + + /* C8/40 means upside-down (looking backwards) */ + /* D8/50 means right-up (looking onwards) */ + upsideDown = (state == 0xc8 || state == 0x40); + + if (upsideDown && sd->nbRightUp > -4) { + if (sd->nbRightUp > 0) + sd->nbRightUp = 0; + if (sd->nbRightUp == -3) { + sd->mirrorMask = 1; + sd->waitSet = 1; + } + sd->nbRightUp--; + } + if (!upsideDown && sd->nbRightUp < 4) { + if (sd->nbRightUp < 0) + sd->nbRightUp = 0; + if (sd->nbRightUp == 3) { + sd->mirrorMask = 0; + sd->waitSet = 1; + } + sd->nbRightUp++; + } + } + + if (sd->waitSet) + sd->dev_camera_settings(gspca_dev); +} + +/*=================== USB driver structure initialisation ==================*/ + +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x05e3, 0x0503)}, + {USB_DEVICE(0x05e3, 0xf191)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); +} + +static void sd_disconnect(struct usb_interface *intf) +{ + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +/*====================== Init and Exit module functions ====================*/ + +module_usb_driver(sd_driver); + +/*==========================================================================*/ + +int gl860_RTx(struct gspca_dev *gspca_dev, + unsigned char pref, u32 req, u16 val, u16 index, + s32 len, void *pdata) +{ + struct usb_device *udev = gspca_dev->dev; + s32 r = 0; + + if (pref == 0x40) { /* Send */ + if (len > 0) { + memcpy(gspca_dev->usb_buf, pdata, len); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + req, pref, val, index, + gspca_dev->usb_buf, + len, 400 + 200 * (len > 1)); + } else { + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + req, pref, val, index, NULL, len, 400); + } + } else { /* Receive */ + if (len > 0) { + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + req, pref, val, index, + gspca_dev->usb_buf, + len, 400 + 200 * (len > 1)); + memcpy(pdata, gspca_dev->usb_buf, len); + } else { + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + req, pref, val, index, NULL, len, 400); + } + } + + if (r < 0) + pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n", + r, pref, req, val, index, len); + else if (len > 1 && r < len) + PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); + + msleep(1); + + return r; +} + +int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len) +{ + int n; + + for (n = 0; n < len; n++) { + if (tbl[n].idx != 0xffff) + ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, + tbl[n].idx, 0, NULL); + else if (tbl[n].val == 0xffff) + break; + else + msleep(tbl[n].val); + } + return n; +} + +int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, + int len, int n) +{ + while (++n < len) { + if (tbl[n].idx != 0xffff) + ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx, + 0, NULL); + else if (tbl[n].val == 0xffff) + break; + else + msleep(tbl[n].val); + } + return n; +} + +void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len) +{ + int n; + + for (n = 0; n < len; n++) { + if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0) + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx, + 3, tbl[n].data); + else + msleep(tbl[n].idx); + } +} + +static int gl860_guess_sensor(struct gspca_dev *gspca_dev, + u16 vendor_id, u16 product_id) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 probe, nb26, nb96, nOV, ntry; + + if (product_id == 0xf191) + sd->sensor = ID_MI1320; + + if (sd->sensor == 0xff) { + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); + + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); + msleep(56); + + PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX"); + nOV = 0; + for (ntry = 0; ntry < 4; ntry++) { + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); + msleep(10); + ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); + PDEBUG(D_PROBE, "probe=0x%02x", probe); + if (probe == 0xff) + nOV++; + } + + if (nOV) { + PDEBUG(D_PROBE, "0xff -> OVXXXX"); + PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655"); + + nb26 = nb96 = 0; + for (ntry = 0; ntry < 4; ntry++) { + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, + 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, + 0, NULL); + msleep(10); + + /* Wait for 26(OV2640) or 96(OV9655) */ + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, + 1, &probe); + + if (probe == 0x26 || probe == 0x40) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV2640", + probe); + sd->sensor = ID_OV2640; + nb26 += 4; + break; + } + if (probe == 0x96 || probe == 0x55) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV9655", + probe); + sd->sensor = ID_OV9655; + nb96 += 4; + break; + } + PDEBUG(D_PROBE, "probe=0x%02x", probe); + if (probe == 0x00) + nb26++; + if (probe == 0xff) + nb96++; + msleep(3); + } + if (nb26 < 4 && nb96 < 4) + return -1; + } else { + PDEBUG(D_PROBE, "Not any 0xff -> MI2020"); + sd->sensor = ID_MI2020; + } + } + + if (_MI1320_) { + PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); + } else if (_MI2020_) { + PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); + } else if (_OV9655_) { + PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); + } else if (_OV2640_) { + PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)"); + } else { + PDEBUG(D_PROBE, "***** Unknown sensor *****"); + return -1; + } + + return 0; +} diff --git a/drivers/media/usb/gspca/gl860/gl860.h b/drivers/media/usb/gspca/gl860/gl860.h new file mode 100644 index 000000000000..0330a0293b9c --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860.h @@ -0,0 +1,105 @@ +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver declarations + * + * 2009/10/14 Olivier LORIN + * + * This program is free software; 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 + * 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 GL860_DEV_H +#define GL860_DEV_H + +#include "gspca.h" + +#define MODULE_NAME "gspca_gl860" +#define DRIVER_VERSION "0.9d10" + +#define ctrl_in gl860_RTx +#define ctrl_out gl860_RTx + +#define ID_MI1320 1 +#define ID_OV2640 2 +#define ID_OV9655 4 +#define ID_MI2020 8 + +#define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320) +#define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020) +#define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640) +#define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655) + +#define IMAGE_640 0 +#define IMAGE_800 1 +#define IMAGE_1280 2 +#define IMAGE_1600 3 + +struct sd_gl860 { + u16 backlight; + u16 brightness; + u16 sharpness; + u16 contrast; + u16 gamma; + u16 hue; + u16 saturation; + u16 whitebal; + u8 mirror; + u8 flip; + u8 AC50Hz; +}; + +/* Specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct sd_gl860 vcur; + struct sd_gl860 vold; + struct sd_gl860 vmax; + + int (*dev_configure_alt) (struct gspca_dev *); + int (*dev_init_at_startup)(struct gspca_dev *); + int (*dev_init_pre_alt) (struct gspca_dev *); + void (*dev_post_unset_alt) (struct gspca_dev *); + int (*dev_camera_settings)(struct gspca_dev *); + + u8 swapRB; + u8 mirrorMask; + u8 sensor; + s32 nbIm; + s32 nbRightUp; + u8 waitSet; +}; + +struct validx { + u16 val; + u16 idx; +}; + +struct idxdata { + u8 idx; + u8 data[3]; +}; + +int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len); +int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, + int len, int n); +void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len); + +int gl860_RTx(struct gspca_dev *gspca_dev, + unsigned char pref, u32 req, u16 val, u16 index, + s32 len, void *pdata); + +void mi1320_init_settings(struct gspca_dev *); +void ov2640_init_settings(struct gspca_dev *); +void ov9655_init_settings(struct gspca_dev *); +void mi2020_init_settings(struct gspca_dev *); + +#endif diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c new file mode 100644 index 000000000000..d4e8343f5b10 --- /dev/null +++ b/drivers/media/usb/gspca/gspca.c @@ -0,0 +1,2456 @@ +/* + * Main USB camera driver + * + * Copyright (C) 2008-2011 Jean-François Moine + * + * Camera button input handling by Márton Németh + * Copyright (C) 2009-2010 Márton Németh + * + * This program is free software; 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 + +#define GSPCA_VERSION "2.14.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gspca.h" + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +#include +#include +#endif + +/* global values */ +#define DEF_NURBS 3 /* default number of URBs */ +#if DEF_NURBS > MAX_NURBS +#error "DEF_NURBS too big" +#endif + +MODULE_AUTHOR("Jean-François Moine "); +MODULE_DESCRIPTION("GSPCA USB Camera Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(GSPCA_VERSION); + +#ifdef GSPCA_DEBUG +int gspca_debug = D_ERR | D_PROBE; +EXPORT_SYMBOL(gspca_debug); + +static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) +{ + if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') { + PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d", + txt, + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24, + w, h); + } else { + PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d", + txt, + pixfmt, + w, h); + } +} +#else +#define PDEBUG_MODE(txt, pixfmt, w, h) +#endif + +/* specific memory types - !! should be different from V4L2_MEMORY_xxx */ +#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ +#define GSPCA_MEMORY_READ 7 + +#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) + +/* + * VMA operations. + */ +static void gspca_vm_open(struct vm_area_struct *vma) +{ + struct gspca_frame *frame = vma->vm_private_data; + + frame->vma_use_count++; + frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED; +} + +static void gspca_vm_close(struct vm_area_struct *vma) +{ + struct gspca_frame *frame = vma->vm_private_data; + + if (--frame->vma_use_count <= 0) + frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; +} + +static const struct vm_operations_struct gspca_vm_ops = { + .open = gspca_vm_open, + .close = gspca_vm_close, +}; + +/* + * Input and interrupt endpoint handling functions + */ +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static void int_irq(struct urb *urb) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + int ret; + + ret = urb->status; + switch (ret) { + case 0: + if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev, + urb->transfer_buffer, urb->actual_length) < 0) { + PDEBUG(D_ERR, "Unknown packet received"); + } + break; + + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + /* Stop is requested either by software or hardware is gone, + * keep the ret value non-zero and don't resubmit later. + */ + break; + + default: + PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status); + urb->status = 0; + ret = 0; + } + + if (ret == 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + pr_err("Resubmit URB failed with error %i\n", ret); + } +} + +static int gspca_input_connect(struct gspca_dev *dev) +{ + struct input_dev *input_dev; + int err = 0; + + dev->input_dev = NULL; + if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) { + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + usb_make_path(dev->dev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + input_dev->name = dev->sd_desc->name; + input_dev->phys = dev->phys; + + usb_to_input_id(dev->dev, &input_dev->id); + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); + input_dev->dev.parent = &dev->dev->dev; + + err = input_register_device(input_dev); + if (err) { + pr_err("Input device registration failed with error %i\n", + err); + input_dev->dev.parent = NULL; + input_free_device(input_dev); + } else { + dev->input_dev = input_dev; + } + } + + return err; +} + +static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, + struct usb_endpoint_descriptor *ep) +{ + unsigned int buffer_len; + int interval; + struct urb *urb; + struct usb_device *dev; + void *buffer = NULL; + int ret = -EINVAL; + + buffer_len = le16_to_cpu(ep->wMaxPacketSize); + interval = ep->bInterval; + PDEBUG(D_CONF, "found int in endpoint: 0x%x, " + "buffer_len=%u, interval=%u", + ep->bEndpointAddress, buffer_len, interval); + + dev = gspca_dev->dev; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + ret = -ENOMEM; + goto error; + } + + buffer = usb_alloc_coherent(dev, buffer_len, + GFP_KERNEL, &urb->transfer_dma); + if (!buffer) { + ret = -ENOMEM; + goto error_buffer; + } + usb_fill_int_urb(urb, dev, + usb_rcvintpipe(dev, ep->bEndpointAddress), + buffer, buffer_len, + int_irq, (void *)gspca_dev, interval); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret < 0) { + PDEBUG(D_ERR, "submit int URB failed with error %i", ret); + goto error_submit; + } + gspca_dev->int_urb = urb; + return ret; + +error_submit: + usb_free_coherent(dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); +error_buffer: + usb_free_urb(urb); +error: + return ret; +} + +static void gspca_input_create_urb(struct gspca_dev *gspca_dev) +{ + struct usb_interface *intf; + struct usb_host_interface *intf_desc; + struct usb_endpoint_descriptor *ep; + int i; + + if (gspca_dev->sd_desc->int_pkt_scan) { + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + intf_desc = intf->cur_altsetting; + for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { + ep = &intf_desc->endpoint[i].desc; + if (usb_endpoint_dir_in(ep) && + usb_endpoint_xfer_int(ep)) { + + alloc_and_submit_int_urb(gspca_dev, ep); + break; + } + } + } +} + +static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) +{ + struct urb *urb; + + urb = gspca_dev->int_urb; + if (urb) { + gspca_dev->int_urb = NULL; + usb_kill_urb(urb); + usb_free_coherent(gspca_dev->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); + } +} +#else +static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) +{ +} + +static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev) +{ +} + +static inline int gspca_input_connect(struct gspca_dev *dev) +{ + return 0; +} +#endif + +/* + * fill a video frame from an URB and resubmit + */ +static void fill_frame(struct gspca_dev *gspca_dev, + struct urb *urb) +{ + u8 *data; /* address of data in the iso message */ + int i, len, st; + cam_pkt_op pkt_scan; + + if (urb->status != 0) { + if (urb->status == -ESHUTDOWN) + return; /* disconnection */ +#ifdef CONFIG_PM + if (gspca_dev->frozen) + return; +#endif + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; + } + pkt_scan = gspca_dev->sd_desc->pkt_scan; + for (i = 0; i < urb->number_of_packets; i++) { + len = urb->iso_frame_desc[i].actual_length; + + /* check the packet status and length */ + st = urb->iso_frame_desc[i].status; + if (st) { + pr_err("ISOC data error: [%d] len=%d, status=%d\n", + i, len, st); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + if (len == 0) { + if (gspca_dev->empty_packet == 0) + gspca_dev->empty_packet = 1; + continue; + } + + /* let the packet be analyzed by the subdriver */ + PDEBUG(D_PACK, "packet [%d] o:%d l:%d", + i, urb->iso_frame_desc[i].offset, len); + data = (u8 *) urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + pkt_scan(gspca_dev, data, len); + } + +resubmit: + /* resubmit the URB */ + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + pr_err("usb_submit_urb() ret %d\n", st); +} + +/* + * ISOC message interrupt from the USB device + * + * Analyse each packet and call the subdriver for copy to the frame buffer. + */ +static void isoc_irq(struct urb *urb) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + + PDEBUG(D_PACK, "isoc irq"); + if (!gspca_dev->streaming) + return; + fill_frame(gspca_dev, urb); +} + +/* + * bulk message interrupt from the USB device + */ +static void bulk_irq(struct urb *urb) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + int st; + + PDEBUG(D_PACK, "bulk irq"); + if (!gspca_dev->streaming) + return; + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + return; /* disconnection */ + default: +#ifdef CONFIG_PM + if (gspca_dev->frozen) + return; +#endif + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; + } + + PDEBUG(D_PACK, "packet l:%d", urb->actual_length); + gspca_dev->sd_desc->pkt_scan(gspca_dev, + urb->transfer_buffer, + urb->actual_length); + +resubmit: + /* resubmit the URB */ + if (gspca_dev->cam.bulk_nurbs != 0) { + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + pr_err("usb_submit_urb() ret %d\n", st); + } +} + +/* + * add data to the current frame + * + * This function is called by the subdrivers at interrupt level. + * + * To build a frame, these ones must add + * - one FIRST_PACKET + * - 0 or many INTER_PACKETs + * - one LAST_PACKET + * DISCARD_PACKET invalidates the whole frame. + */ +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len) +{ + struct gspca_frame *frame; + int i, j; + + PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); + + if (packet_type == FIRST_PACKET) { + i = atomic_read(&gspca_dev->fr_i); + + /* if there are no queued buffer, discard the whole frame */ + if (i == atomic_read(&gspca_dev->fr_q)) { + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_dev->sequence++; + return; + } + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; + frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get()); + frame->v4l2_buf.sequence = gspca_dev->sequence++; + gspca_dev->image = frame->data; + gspca_dev->image_len = 0; + } else { + switch (gspca_dev->last_packet_type) { + case DISCARD_PACKET: + if (packet_type == LAST_PACKET) { + gspca_dev->last_packet_type = packet_type; + gspca_dev->image = NULL; + gspca_dev->image_len = 0; + } + return; + case LAST_PACKET: + return; + } + } + + /* append the packet to the frame buffer */ + if (len > 0) { + if (gspca_dev->image_len + len > gspca_dev->frsz) { + PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d", + gspca_dev->image_len + len, + gspca_dev->frsz); + packet_type = DISCARD_PACKET; + } else { +/* !! image is NULL only when last pkt is LAST or DISCARD + if (gspca_dev->image == NULL) { + pr_err("gspca_frame_add() image == NULL\n"); + return; + } + */ + memcpy(gspca_dev->image + gspca_dev->image_len, + data, len); + gspca_dev->image_len += len; + } + } + gspca_dev->last_packet_type = packet_type; + + /* if last packet, invalidate packet concatenation until + * next first packet, wake up the application and advance + * in the queue */ + if (packet_type == LAST_PACKET) { + i = atomic_read(&gspca_dev->fr_i); + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; + frame->v4l2_buf.bytesused = gspca_dev->image_len; + frame->v4l2_buf.flags = (frame->v4l2_buf.flags + | V4L2_BUF_FLAG_DONE) + & ~V4L2_BUF_FLAG_QUEUED; + i = (i + 1) % GSPCA_MAX_FRAMES; + atomic_set(&gspca_dev->fr_i, i); + wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ + PDEBUG(D_FRAM, "frame complete len:%d", + frame->v4l2_buf.bytesused); + gspca_dev->image = NULL; + gspca_dev->image_len = 0; + } +} +EXPORT_SYMBOL(gspca_frame_add); + +static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, + enum v4l2_memory memory, unsigned int count) +{ + struct gspca_frame *frame; + unsigned int frsz; + int i; + + i = gspca_dev->curr_mode; + frsz = gspca_dev->cam.cam_mode[i].sizeimage; + PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); + frsz = PAGE_ALIGN(frsz); + if (count >= GSPCA_MAX_FRAMES) + count = GSPCA_MAX_FRAMES - 1; + gspca_dev->frbuf = vmalloc_32(frsz * count); + if (!gspca_dev->frbuf) { + pr_err("frame alloc failed\n"); + return -ENOMEM; + } + gspca_dev->capt_file = file; + gspca_dev->memory = memory; + gspca_dev->frsz = frsz; + gspca_dev->nframes = count; + for (i = 0; i < count; i++) { + frame = &gspca_dev->frame[i]; + frame->v4l2_buf.index = i; + frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + frame->v4l2_buf.flags = 0; + frame->v4l2_buf.field = V4L2_FIELD_NONE; + frame->v4l2_buf.length = frsz; + frame->v4l2_buf.memory = memory; + frame->v4l2_buf.sequence = 0; + frame->data = gspca_dev->frbuf + i * frsz; + frame->v4l2_buf.m.offset = i * frsz; + } + atomic_set(&gspca_dev->fr_q, 0); + atomic_set(&gspca_dev->fr_i, 0); + gspca_dev->fr_o = 0; + return 0; +} + +static void frame_free(struct gspca_dev *gspca_dev) +{ + int i; + + PDEBUG(D_STREAM, "frame free"); + if (gspca_dev->frbuf != NULL) { + vfree(gspca_dev->frbuf); + gspca_dev->frbuf = NULL; + for (i = 0; i < gspca_dev->nframes; i++) + gspca_dev->frame[i].data = NULL; + } + gspca_dev->nframes = 0; + gspca_dev->frsz = 0; + gspca_dev->capt_file = NULL; + gspca_dev->memory = GSPCA_MEMORY_NO; +} + +static void destroy_urbs(struct gspca_dev *gspca_dev) +{ + struct urb *urb; + unsigned int i; + + PDEBUG(D_STREAM, "kill transfer"); + for (i = 0; i < MAX_NURBS; i++) { + urb = gspca_dev->urb[i]; + if (urb == NULL) + break; + + gspca_dev->urb[i] = NULL; + usb_kill_urb(urb); + if (urb->transfer_buffer != NULL) + usb_free_coherent(gspca_dev->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); + } +} + +static int gspca_set_alt0(struct gspca_dev *gspca_dev) +{ + int ret; + + if (gspca_dev->alt == 0) + return 0; + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); + if (ret < 0) + pr_err("set alt 0 err %d\n", ret); + return ret; +} + +/* Note: both the queue and the usb locks should be held when calling this */ +static void gspca_stream_off(struct gspca_dev *gspca_dev) +{ + gspca_dev->streaming = 0; + gspca_dev->usb_err = 0; + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + gspca_input_create_urb(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + PDEBUG(D_STREAM, "stream off OK"); +} + +/* + * look for an input transfer endpoint in an alternate setting + */ +static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, + int xfer) +{ + struct usb_host_endpoint *ep; + int i, attr; + + for (i = 0; i < alt->desc.bNumEndpoints; i++) { + ep = &alt->endpoint[i]; + attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (attr == xfer + && ep->desc.wMaxPacketSize != 0 + && usb_endpoint_dir_in(&ep->desc)) + return ep; + } + return NULL; +} + +/* compute the minimum bandwidth for the current transfer */ +static u32 which_bandwidth(struct gspca_dev *gspca_dev) +{ + u32 bandwidth; + int i; + + /* get the (max) image size */ + i = gspca_dev->curr_mode; + bandwidth = gspca_dev->cam.cam_mode[i].sizeimage; + + /* if the image is compressed, estimate its mean size */ + if (!gspca_dev->cam.needs_full_bandwidth && + bandwidth < gspca_dev->cam.cam_mode[i].width * + gspca_dev->cam.cam_mode[i].height) + bandwidth = bandwidth * 3 / 8; /* 0.375 */ + + /* estimate the frame rate */ + if (gspca_dev->sd_desc->get_streamparm) { + struct v4l2_streamparm parm; + + gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm); + bandwidth *= parm.parm.capture.timeperframe.denominator; + bandwidth /= parm.parm.capture.timeperframe.numerator; + } else { + + /* don't hope more than 15 fps with USB 1.1 and + * image resolution >= 640x480 */ + if (gspca_dev->width >= 640 + && gspca_dev->dev->speed == USB_SPEED_FULL) + bandwidth *= 15; /* 15 fps */ + else + bandwidth *= 30; /* 30 fps */ + } + + PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth); + return bandwidth; +} + +/* endpoint table */ +#define MAX_ALT 16 +struct ep_tb_s { + u32 alt; + u32 bandwidth; +}; + +/* + * build the table of the endpoints + * and compute the minimum bandwidth for the image transfer + */ +static int build_isoc_ep_tb(struct gspca_dev *gspca_dev, + struct usb_interface *intf, + struct ep_tb_s *ep_tb) +{ + struct usb_host_endpoint *ep; + int i, j, nbalt, psize, found; + u32 bandwidth, last_bw; + + nbalt = intf->num_altsetting; + if (nbalt > MAX_ALT) + nbalt = MAX_ALT; /* fixme: should warn */ + + /* build the endpoint table */ + i = 0; + last_bw = 0; + for (;;) { + ep_tb->bandwidth = 2000 * 2000 * 120; + found = 0; + for (j = 0; j < nbalt; j++) { + ep = alt_xfer(&intf->altsetting[j], + USB_ENDPOINT_XFER_ISOC); + if (ep == NULL) + continue; + if (ep->desc.bInterval == 0) { + pr_err("alt %d iso endp with 0 interval\n", j); + continue; + } + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + bandwidth = psize * 1000; + if (gspca_dev->dev->speed == USB_SPEED_HIGH + || gspca_dev->dev->speed == USB_SPEED_SUPER) + bandwidth *= 8; + bandwidth /= 1 << (ep->desc.bInterval - 1); + if (bandwidth <= last_bw) + continue; + if (bandwidth < ep_tb->bandwidth) { + ep_tb->bandwidth = bandwidth; + ep_tb->alt = j; + found = 1; + } + } + if (!found) + break; + PDEBUG(D_STREAM, "alt %d bandwidth %d", + ep_tb->alt, ep_tb->bandwidth); + last_bw = ep_tb->bandwidth; + i++; + ep_tb++; + } + + /* + * If the camera: + * has a usb audio class interface (a built in usb mic); and + * is a usb 1 full speed device; and + * uses the max full speed iso bandwidth; and + * and has more than 1 alt setting + * then skip the highest alt setting to spare bandwidth for the mic + */ + if (gspca_dev->audio && + gspca_dev->dev->speed == USB_SPEED_FULL && + last_bw >= 1000000 && + i > 1) { + PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt"); + i--; + ep_tb--; + } + + /* get the requested bandwidth and start at the highest atlsetting */ + bandwidth = which_bandwidth(gspca_dev); + ep_tb--; + while (i > 1) { + ep_tb--; + if (ep_tb->bandwidth < bandwidth) + break; + i--; + } + return i; +} + +/* + * create the URBs for image transfer + */ +static int create_urbs(struct gspca_dev *gspca_dev, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + int n, nurbs, i, psize, npkt, bsize; + + /* calculate the packet size and the number of packets */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + + if (!gspca_dev->cam.bulk) { /* isoc */ + + /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ + if (gspca_dev->pkt_size == 0) + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + else + psize = gspca_dev->pkt_size; + npkt = gspca_dev->cam.npkt; + if (npkt == 0) + npkt = 32; /* default value */ + bsize = psize * npkt; + PDEBUG(D_STREAM, + "isoc %d pkts size %d = bsize:%d", + npkt, psize, bsize); + nurbs = DEF_NURBS; + } else { /* bulk */ + npkt = 0; + bsize = gspca_dev->cam.bulk_size; + if (bsize == 0) + bsize = psize; + PDEBUG(D_STREAM, "bulk bsize:%d", bsize); + if (gspca_dev->cam.bulk_nurbs != 0) + nurbs = gspca_dev->cam.bulk_nurbs; + else + nurbs = 1; + } + + for (n = 0; n < nurbs; n++) { + urb = usb_alloc_urb(npkt, GFP_KERNEL); + if (!urb) { + pr_err("usb_alloc_urb failed\n"); + return -ENOMEM; + } + gspca_dev->urb[n] = urb; + urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, + bsize, + GFP_KERNEL, + &urb->transfer_dma); + + if (urb->transfer_buffer == NULL) { + pr_err("usb_alloc_coherent failed\n"); + return -ENOMEM; + } + urb->dev = gspca_dev->dev; + urb->context = gspca_dev; + urb->transfer_buffer_length = bsize; + if (npkt != 0) { /* ISOC */ + urb->pipe = usb_rcvisocpipe(gspca_dev->dev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; + urb->interval = 1 << (ep->desc.bInterval - 1); + urb->complete = isoc_irq; + urb->number_of_packets = npkt; + for (i = 0; i < npkt; i++) { + urb->iso_frame_desc[i].length = psize; + urb->iso_frame_desc[i].offset = psize * i; + } + } else { /* bulk */ + urb->pipe = usb_rcvbulkpipe(gspca_dev->dev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->complete = bulk_irq; + } + } + return 0; +} + +/* + * start the USB transfer + */ +static int gspca_init_transfer(struct gspca_dev *gspca_dev) +{ + struct usb_interface *intf; + struct usb_host_endpoint *ep; + struct urb *urb; + struct ep_tb_s ep_tb[MAX_ALT]; + int n, ret, xfer, alt, alt_idx; + + /* reset the streaming variables */ + gspca_dev->image = NULL; + gspca_dev->image_len = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_dev->sequence = 0; + + gspca_dev->usb_err = 0; + + /* do the specific subdriver stuff before endpoint selection */ + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0; + if (gspca_dev->sd_desc->isoc_init) { + ret = gspca_dev->sd_desc->isoc_init(gspca_dev); + if (ret < 0) + return ret; + } + xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK + : USB_ENDPOINT_XFER_ISOC; + + /* if bulk or the subdriver forced an altsetting, get the endpoint */ + if (gspca_dev->alt != 0) { + gspca_dev->alt--; /* (previous version compatibility) */ + ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); + if (ep == NULL) { + pr_err("bad altsetting %d\n", gspca_dev->alt); + return -EIO; + } + ep_tb[0].alt = gspca_dev->alt; + alt_idx = 1; + } else { + + /* else, compute the minimum bandwidth + * and build the endpoint table */ + alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); + if (alt_idx <= 0) { + pr_err("no transfer endpoint found\n"); + return -EIO; + } + } + + /* set the highest alternate setting and + * loop until urb submit succeeds */ + gspca_input_destroy_urb(gspca_dev); + + gspca_dev->alt = ep_tb[--alt_idx].alt; + alt = -1; + for (;;) { + if (alt != gspca_dev->alt) { + alt = gspca_dev->alt; + if (intf->num_altsetting > 1) { + ret = usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + alt); + if (ret < 0) { + if (ret == -ENOSPC) + goto retry; /*fixme: ugly*/ + pr_err("set alt %d err %d\n", alt, ret); + goto out; + } + } + } + if (!gspca_dev->cam.no_urb_create) { + PDEBUG(D_STREAM, "init transfer alt %d", alt); + ret = create_urbs(gspca_dev, + alt_xfer(&intf->altsetting[alt], xfer)); + if (ret < 0) { + destroy_urbs(gspca_dev); + goto out; + } + } + + /* clear the bulk endpoint */ + if (gspca_dev->cam.bulk) + usb_clear_halt(gspca_dev->dev, + gspca_dev->urb[0]->pipe); + + /* start the cam */ + ret = gspca_dev->sd_desc->start(gspca_dev); + if (ret < 0) { + destroy_urbs(gspca_dev); + goto out; + } + gspca_dev->streaming = 1; + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + + /* some bulk transfers are started by the subdriver */ + if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) + break; + + /* submit the URBs */ + for (n = 0; n < MAX_NURBS; n++) { + urb = gspca_dev->urb[n]; + if (urb == NULL) + break; + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret < 0) + break; + } + if (ret >= 0) + break; /* transfer is started */ + + /* something when wrong + * stop the webcam and free the transfer resources */ + gspca_stream_off(gspca_dev); + if (ret != -ENOSPC) { + pr_err("usb_submit_urb alt %d err %d\n", + gspca_dev->alt, ret); + goto out; + } + + /* the bandwidth is not wide enough + * negotiate or try a lower alternate setting */ +retry: + PDEBUG(D_ERR|D_STREAM, + "alt %d - bandwidth not wide enough - trying again", + alt); + msleep(20); /* wait for kill complete */ + if (gspca_dev->sd_desc->isoc_nego) { + ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); + if (ret < 0) + goto out; + } else { + if (alt_idx <= 0) { + pr_err("no transfer endpoint found\n"); + ret = -EIO; + goto out; + } + gspca_dev->alt = ep_tb[--alt_idx].alt; + } + } +out: + gspca_input_create_urb(gspca_dev); + return ret; +} + +static void gspca_set_default_mode(struct gspca_dev *gspca_dev) +{ + struct gspca_ctrl *ctrl; + int i; + + i = gspca_dev->cam.nmodes - 1; /* take the highest mode */ + gspca_dev->curr_mode = i; + gspca_dev->width = gspca_dev->cam.cam_mode[i].width; + gspca_dev->height = gspca_dev->cam.cam_mode[i].height; + gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat; + + /* set the current control values to their default values + * which may have changed in sd_init() */ + /* does nothing if ctrl_handler == NULL */ + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + ctrl = gspca_dev->cam.ctrls; + if (ctrl != NULL) { + for (i = 0; + i < gspca_dev->sd_desc->nctrls; + i++, ctrl++) + ctrl->val = ctrl->def; + } +} + +static int wxh_to_mode(struct gspca_dev *gspca_dev, + int width, int height) +{ + int i; + + for (i = gspca_dev->cam.nmodes; --i > 0; ) { + if (width >= gspca_dev->cam.cam_mode[i].width + && height >= gspca_dev->cam.cam_mode[i].height) + break; + } + return i; +} + +/* + * search a mode with the right pixel format + */ +static int gspca_get_mode(struct gspca_dev *gspca_dev, + int mode, + int pixfmt) +{ + int modeU, modeD; + + modeU = modeD = mode; + while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) { + if (--modeD >= 0) { + if (gspca_dev->cam.cam_mode[modeD].pixelformat + == pixfmt) + return modeD; + } + if (++modeU < gspca_dev->cam.nmodes) { + if (gspca_dev->cam.cam_mode[modeU].pixelformat + == pixfmt) + return modeU; + } + } + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + gspca_dev->usb_err = 0; + return gspca_dev->sd_desc->get_register(gspca_dev, reg); +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + gspca_dev->usb_err = 0; + return gspca_dev->sd_desc->set_register(gspca_dev, reg); +} +#endif + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + gspca_dev->usb_err = 0; + return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmtdesc) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int i, j, index; + __u32 fmt_tb[8]; + + /* give an index to each format */ + index = 0; + j = 0; + for (i = gspca_dev->cam.nmodes; --i >= 0; ) { + fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat; + j = 0; + for (;;) { + if (fmt_tb[j] == fmt_tb[index]) + break; + j++; + } + if (j == index) { + if (fmtdesc->index == index) + break; /* new format */ + index++; + if (index >= ARRAY_SIZE(fmt_tb)) + return -EINVAL; + } + } + if (i < 0) + return -EINVAL; /* no more format */ + + fmtdesc->pixelformat = fmt_tb[index]; + if (gspca_dev->cam.cam_mode[i].sizeimage < + gspca_dev->cam.cam_mode[i].width * + gspca_dev->cam.cam_mode[i].height) + fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; + fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; + fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; + fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff; + fmtdesc->description[3] = fmtdesc->pixelformat >> 24; + fmtdesc->description[4] = '\0'; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int mode; + + mode = gspca_dev->curr_mode; + fmt->fmt.pix = gspca_dev->cam.cam_mode[mode]; + /* some drivers use priv internally, zero it before giving it to + userspace */ + fmt->fmt.pix.priv = 0; + return 0; +} + +static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, + struct v4l2_format *fmt) +{ + int w, h, mode, mode2; + + w = fmt->fmt.pix.width; + h = fmt->fmt.pix.height; + +#ifdef GSPCA_DEBUG + if (gspca_debug & D_CONF) + PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h); +#endif + /* search the closest mode for width and height */ + mode = wxh_to_mode(gspca_dev, w, h); + + /* OK if right palette */ + if (gspca_dev->cam.cam_mode[mode].pixelformat + != fmt->fmt.pix.pixelformat) { + + /* else, search the closest mode with the same pixel format */ + mode2 = gspca_get_mode(gspca_dev, mode, + fmt->fmt.pix.pixelformat); + if (mode2 >= 0) + mode = mode2; +/* else + ; * no chance, return this mode */ + } + fmt->fmt.pix = gspca_dev->cam.cam_mode[mode]; + /* some drivers use priv internally, zero it before giving it to + userspace */ + fmt->fmt.pix.priv = 0; + return mode; /* used when s_fmt */ +} + +static int vidioc_try_fmt_vid_cap(struct file *file, + void *priv, + struct v4l2_format *fmt) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int ret; + + ret = try_fmt_vid_cap(gspca_dev, fmt); + if (ret < 0) + return ret; + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int ret; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + ret = try_fmt_vid_cap(gspca_dev, fmt); + if (ret < 0) + goto out; + + if (gspca_dev->nframes != 0 + && fmt->fmt.pix.sizeimage > gspca_dev->frsz) { + ret = -EINVAL; + goto out; + } + + if (ret == gspca_dev->curr_mode) { + ret = 0; + goto out; /* same mode */ + } + + if (gspca_dev->streaming) { + ret = -EBUSY; + goto out; + } + gspca_dev->width = fmt->fmt.pix.width; + gspca_dev->height = fmt->fmt.pix.height; + gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; + gspca_dev->curr_mode = ret; + + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int i; + __u32 index = 0; + + for (i = 0; i < gspca_dev->cam.nmodes; i++) { + if (fsize->pixel_format != + gspca_dev->cam.cam_mode[i].pixelformat) + continue; + + if (fsize->index == index) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = + gspca_dev->cam.cam_mode[i].width; + fsize->discrete.height = + gspca_dev->cam.cam_mode[i].height; + return 0; + } + index++; + } + + return -EINVAL; +} + +static int vidioc_enum_frameintervals(struct file *filp, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct gspca_dev *gspca_dev = video_drvdata(filp); + int mode = wxh_to_mode(gspca_dev, fival->width, fival->height); + __u32 i; + + if (gspca_dev->cam.mode_framerates == NULL || + gspca_dev->cam.mode_framerates[mode].nrates == 0) + return -EINVAL; + + if (fival->pixel_format != + gspca_dev->cam.cam_mode[mode].pixelformat) + return -EINVAL; + + for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) { + if (fival->index == i) { + fival->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fival->discrete.numerator = 1; + fival->discrete.denominator = + gspca_dev->cam.mode_framerates[mode].rates[i]; + return 0; + } + } + + return -EINVAL; +} + +static void gspca_release(struct v4l2_device *v4l2_device) +{ + struct gspca_dev *gspca_dev = + container_of(v4l2_device, struct gspca_dev, v4l2_dev); + + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); + v4l2_device_unregister(&gspca_dev->v4l2_dev); + kfree(gspca_dev->usb_buf); + kfree(gspca_dev); +} + +static int dev_open(struct file *file) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + PDEBUG(D_STREAM, "[%s] open", current->comm); + + /* protect the subdriver against rmmod */ + if (!try_module_get(gspca_dev->module)) + return -ENODEV; + +#ifdef GSPCA_DEBUG + /* activate the v4l2 debug */ + if (gspca_debug & D_V4L2) + gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL + | V4L2_DEBUG_IOCTL_ARG; + else + gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL + | V4L2_DEBUG_IOCTL_ARG); +#endif + return v4l2_fh_open(file); +} + +static int dev_close(struct file *file) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + PDEBUG(D_STREAM, "[%s] close", current->comm); + + /* Needed for gspca_stream_off, always lock before queue_lock! */ + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { + mutex_unlock(&gspca_dev->usb_lock); + return -ERESTARTSYS; + } + + /* if the file did the capture, free the streaming resources */ + if (gspca_dev->capt_file == file) { + if (gspca_dev->streaming) + gspca_stream_off(gspca_dev); + frame_free(gspca_dev); + } + module_put(gspca_dev->module); + mutex_unlock(&gspca_dev->queue_lock); + mutex_unlock(&gspca_dev->usb_lock); + + PDEBUG(D_STREAM, "close done"); + + return v4l2_fh_release(file); +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + strlcpy((char *) cap->driver, gspca_dev->sd_desc->name, + sizeof cap->driver); + if (gspca_dev->dev->product != NULL) { + strlcpy((char *) cap->card, gspca_dev->dev->product, + sizeof cap->card); + } else { + snprintf((char *) cap->card, sizeof cap->card, + "USB Camera (%04x:%04x)", + le16_to_cpu(gspca_dev->dev->descriptor.idVendor), + le16_to_cpu(gspca_dev->dev->descriptor.idProduct)); + } + usb_make_path(gspca_dev->dev, (char *) cap->bus_info, + sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_STREAMING + | V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int get_ctrl(struct gspca_dev *gspca_dev, + int id) +{ + const struct ctrl *ctrls; + int i; + + for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; + i < gspca_dev->sd_desc->nctrls; + i++, ctrls++) { + if (gspca_dev->ctrl_dis & (1 << i)) + continue; + if (id == ctrls->qctrl.id) + return i; + } + return -1; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *q_ctrl) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + const struct ctrl *ctrls; + struct gspca_ctrl *gspca_ctrl; + int i, idx; + u32 id; + + id = q_ctrl->id; + if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { + id &= V4L2_CTRL_ID_MASK; + id++; + idx = -1; + for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (gspca_dev->ctrl_dis & (1 << i)) + continue; + if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) + continue; + if (idx >= 0 + && gspca_dev->sd_desc->ctrls[i].qctrl.id + > gspca_dev->sd_desc->ctrls[idx].qctrl.id) + continue; + idx = i; + } + } else { + idx = get_ctrl(gspca_dev, id); + } + if (idx < 0) + return -EINVAL; + ctrls = &gspca_dev->sd_desc->ctrls[idx]; + memcpy(q_ctrl, &ctrls->qctrl, sizeof *q_ctrl); + if (gspca_dev->cam.ctrls != NULL) { + gspca_ctrl = &gspca_dev->cam.ctrls[idx]; + q_ctrl->default_value = gspca_ctrl->def; + q_ctrl->minimum = gspca_ctrl->min; + q_ctrl->maximum = gspca_ctrl->max; + } + if (gspca_dev->ctrl_inac & (1 << idx)) + q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + const struct ctrl *ctrls; + struct gspca_ctrl *gspca_ctrl; + int idx; + + idx = get_ctrl(gspca_dev, ctrl->id); + if (idx < 0) + return -EINVAL; + if (gspca_dev->ctrl_inac & (1 << idx)) + return -EINVAL; + ctrls = &gspca_dev->sd_desc->ctrls[idx]; + if (gspca_dev->cam.ctrls != NULL) { + gspca_ctrl = &gspca_dev->cam.ctrls[idx]; + if (ctrl->value < gspca_ctrl->min + || ctrl->value > gspca_ctrl->max) + return -ERANGE; + } else { + gspca_ctrl = NULL; + if (ctrl->value < ctrls->qctrl.minimum + || ctrl->value > ctrls->qctrl.maximum) + return -ERANGE; + } + PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); + gspca_dev->usb_err = 0; + if (ctrls->set != NULL) + return ctrls->set(gspca_dev, ctrl->value); + if (gspca_ctrl != NULL) { + gspca_ctrl->val = ctrl->value; + if (ctrls->set_control != NULL + && gspca_dev->streaming) + ctrls->set_control(gspca_dev); + } + return gspca_dev->usb_err; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + const struct ctrl *ctrls; + int idx; + + idx = get_ctrl(gspca_dev, ctrl->id); + if (idx < 0) + return -EINVAL; + ctrls = &gspca_dev->sd_desc->ctrls[idx]; + + gspca_dev->usb_err = 0; + if (ctrls->get != NULL) + return ctrls->get(gspca_dev, &ctrl->value); + if (gspca_dev->cam.ctrls != NULL) + ctrl->value = gspca_dev->cam.ctrls[idx].val; + return 0; +} + +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + if (!gspca_dev->sd_desc->querymenu) + return -ENOTTY; + return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu); +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + if (input->index != 0) + return -EINVAL; + input->type = V4L2_INPUT_TYPE_CAMERA; + input->status = gspca_dev->cam.input_flags; + strlcpy(input->name, gspca_dev->sd_desc->name, + sizeof input->name); + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return (0); +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int i, ret = 0, streaming; + + i = rb->memory; /* (avoid compilation warning) */ + switch (i) { + case GSPCA_MEMORY_READ: /* (internal call) */ + case V4L2_MEMORY_MMAP: + case V4L2_MEMORY_USERPTR: + break; + default: + return -EINVAL; + } + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + if (gspca_dev->memory != GSPCA_MEMORY_NO + && gspca_dev->memory != GSPCA_MEMORY_READ + && gspca_dev->memory != rb->memory) { + ret = -EBUSY; + goto out; + } + + /* only one file may do the capture */ + if (gspca_dev->capt_file != NULL + && gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + + /* if allocated, the buffers must not be mapped */ + for (i = 0; i < gspca_dev->nframes; i++) { + if (gspca_dev->frame[i].vma_use_count) { + ret = -EBUSY; + goto out; + } + } + + /* stop streaming */ + streaming = gspca_dev->streaming; + if (streaming) { + gspca_stream_off(gspca_dev); + + /* Don't restart the stream when switching from read + * to mmap mode */ + if (gspca_dev->memory == GSPCA_MEMORY_READ) + streaming = 0; + } + + /* free the previous allocated buffers, if any */ + if (gspca_dev->nframes != 0) + frame_free(gspca_dev); + if (rb->count == 0) /* unrequest */ + goto out; + ret = frame_alloc(gspca_dev, file, rb->memory, rb->count); + if (ret == 0) { + rb->count = gspca_dev->nframes; + if (streaming) + ret = gspca_init_transfer(gspca_dev); + } +out: + mutex_unlock(&gspca_dev->queue_lock); + PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count); + return ret; +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *v4l2_buf) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + struct gspca_frame *frame; + + if (v4l2_buf->index < 0 + || v4l2_buf->index >= gspca_dev->nframes) + return -EINVAL; + + frame = &gspca_dev->frame[v4l2_buf->index]; + memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int ret; + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + /* check the capture file */ + if (gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + + if (gspca_dev->nframes == 0 + || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { + ret = -EINVAL; + goto out; + } + if (!gspca_dev->streaming) { + ret = gspca_init_transfer(gspca_dev); + if (ret < 0) + goto out; + } +#ifdef GSPCA_DEBUG + if (gspca_debug & D_STREAM) { + PDEBUG_MODE("stream on OK", + gspca_dev->pixfmt, + gspca_dev->width, + gspca_dev->height); + } +#endif + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + int i, ret; + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + if (!gspca_dev->streaming) { + ret = 0; + goto out; + } + + /* check the capture file */ + if (gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + + /* stop streaming */ + gspca_stream_off(gspca_dev); + /* In case another thread is waiting in dqbuf */ + wake_up_interruptible(&gspca_dev->wq); + + /* empty the transfer queues */ + for (i = 0; i < gspca_dev->nframes; i++) + gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; + atomic_set(&gspca_dev->fr_q, 0); + atomic_set(&gspca_dev->fr_i, 0); + gspca_dev->fr_o = 0; + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jpegcomp) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + gspca_dev->usb_err = 0; + return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jpegcomp) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + gspca_dev->usb_err = 0; + return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); +} + +static int vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct gspca_dev *gspca_dev = video_drvdata(filp); + + parm->parm.capture.readbuffers = gspca_dev->nbufread; + + if (gspca_dev->sd_desc->get_streamparm) { + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; + } + return 0; +} + +static int vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct gspca_dev *gspca_dev = video_drvdata(filp); + int n; + + n = parm->parm.capture.readbuffers; + if (n == 0 || n >= GSPCA_MAX_FRAMES) + parm->parm.capture.readbuffers = gspca_dev->nbufread; + else + gspca_dev->nbufread = n; + + if (gspca_dev->sd_desc->set_streamparm) { + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; + } + + return 0; +} + +static int dev_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + struct gspca_frame *frame; + struct page *page; + unsigned long addr, start, size; + int i, ret; + + start = vma->vm_start; + size = vma->vm_end - vma->vm_start; + PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + if (gspca_dev->capt_file != file) { + ret = -EINVAL; + goto out; + } + + frame = NULL; + for (i = 0; i < gspca_dev->nframes; ++i) { + if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { + PDEBUG(D_STREAM, "mmap bad memory type"); + break; + } + if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) + == vma->vm_pgoff) { + frame = &gspca_dev->frame[i]; + break; + } + } + if (frame == NULL) { + PDEBUG(D_STREAM, "mmap no frame buffer found"); + ret = -EINVAL; + goto out; + } + if (size != frame->v4l2_buf.length) { + PDEBUG(D_STREAM, "mmap bad size"); + ret = -EINVAL; + goto out; + } + + /* + * - VM_IO marks the area as being a mmaped region for I/O to a + * device. It also prevents the region from being core dumped. + */ + vma->vm_flags |= VM_IO; + + addr = (unsigned long) frame->data; + while (size > 0) { + page = vmalloc_to_page((void *) addr); + ret = vm_insert_page(vma, start, page); + if (ret < 0) + goto out; + start += PAGE_SIZE; + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &gspca_vm_ops; + vma->vm_private_data = frame; + gspca_vm_open(vma); + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, + enum v4l2_memory memory) +{ + if (!gspca_dev->present) + return -ENODEV; + if (gspca_dev->capt_file != file || gspca_dev->memory != memory || + !gspca_dev->streaming) + return -EINVAL; + + /* check if a frame is ready */ + return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); +} + +static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, + enum v4l2_memory memory) +{ + int ret; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + ret = frame_ready_nolock(gspca_dev, file, memory); + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +/* + * dequeue a video buffer + * + * If nonblock_ing is false, block until a buffer is available. + */ +static int vidioc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *v4l2_buf) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + struct gspca_frame *frame; + int i, j, ret; + + PDEBUG(D_FRAM, "dqbuf"); + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + for (;;) { + ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); + if (ret < 0) + goto out; + if (ret > 0) + break; + + mutex_unlock(&gspca_dev->queue_lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + /* wait till a frame is ready */ + ret = wait_event_interruptible_timeout(gspca_dev->wq, + frame_ready(gspca_dev, file, v4l2_buf->memory), + msecs_to_jiffies(3000)); + if (ret < 0) + return ret; + if (ret == 0) + return -EIO; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + } + + i = gspca_dev->fr_o; + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; + + gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; + + frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; + memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); + PDEBUG(D_FRAM, "dqbuf %d", j); + ret = 0; + + if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { + if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, + frame->data, + frame->v4l2_buf.bytesused)) { + PDEBUG(D_ERR|D_STREAM, + "dqbuf cp to user failed"); + ret = -EFAULT; + } + } +out: + mutex_unlock(&gspca_dev->queue_lock); + + if (ret == 0 && gspca_dev->sd_desc->dq_callback) { + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->usb_err = 0; + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } + + return ret; +} + +/* + * queue a video buffer + * + * Attempting to queue a buffer that has already been + * queued will return -EINVAL. + */ +static int vidioc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *v4l2_buf) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + struct gspca_frame *frame; + int i, index, ret; + + PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + index = v4l2_buf->index; + if ((unsigned) index >= gspca_dev->nframes) { + PDEBUG(D_FRAM, + "qbuf idx %d >= %d", index, gspca_dev->nframes); + ret = -EINVAL; + goto out; + } + if (v4l2_buf->memory != gspca_dev->memory) { + PDEBUG(D_FRAM, "qbuf bad memory type"); + ret = -EINVAL; + goto out; + } + + frame = &gspca_dev->frame[index]; + if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) { + PDEBUG(D_FRAM, "qbuf bad state"); + ret = -EINVAL; + goto out; + } + + frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; + + if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { + frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; + frame->v4l2_buf.length = v4l2_buf->length; + } + + /* put the buffer in the 'queued' queue */ + i = atomic_read(&gspca_dev->fr_q); + gspca_dev->fr_queue[i] = index; + atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); + + v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; + v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +/* + * allocate the resources for read() + */ +static int read_alloc(struct gspca_dev *gspca_dev, + struct file *file) +{ + struct v4l2_buffer v4l2_buf; + int i, ret; + + PDEBUG(D_STREAM, "read alloc"); + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + + if (gspca_dev->nframes == 0) { + struct v4l2_requestbuffers rb; + + memset(&rb, 0, sizeof rb); + rb.count = gspca_dev->nbufread; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rb.memory = GSPCA_MEMORY_READ; + ret = vidioc_reqbufs(file, gspca_dev, &rb); + if (ret != 0) { + PDEBUG(D_STREAM, "read reqbuf err %d", ret); + goto out; + } + memset(&v4l2_buf, 0, sizeof v4l2_buf); + v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_buf.memory = GSPCA_MEMORY_READ; + for (i = 0; i < gspca_dev->nbufread; i++) { + v4l2_buf.index = i; + ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); + if (ret != 0) { + PDEBUG(D_STREAM, "read qbuf err: %d", ret); + goto out; + } + } + } + + /* start streaming */ + ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (ret != 0) + PDEBUG(D_STREAM, "read streamon err %d", ret); +out: + mutex_unlock(&gspca_dev->usb_lock); + return ret; +} + +static unsigned int dev_poll(struct file *file, poll_table *wait) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + unsigned long req_events = poll_requested_events(wait); + int ret = 0; + + PDEBUG(D_FRAM, "poll"); + + if (req_events & POLLPRI) + ret |= v4l2_ctrl_poll(file, wait); + + if (req_events & (POLLIN | POLLRDNORM)) { + /* if reqbufs is not done, the user would use read() */ + if (gspca_dev->memory == GSPCA_MEMORY_NO) { + if (read_alloc(gspca_dev, file) != 0) { + ret |= POLLERR; + goto out; + } + } + + poll_wait(file, &gspca_dev->wq, wait); + + /* check if an image has been received */ + if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { + ret |= POLLERR; + goto out; + } + if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) + ret |= POLLIN | POLLRDNORM; + mutex_unlock(&gspca_dev->queue_lock); + } + +out: + if (!gspca_dev->present) + ret |= POLLHUP; + + return ret; +} + +static ssize_t dev_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + struct gspca_frame *frame; + struct v4l2_buffer v4l2_buf; + struct timeval timestamp; + int n, ret, ret2; + + PDEBUG(D_FRAM, "read (%zd)", count); + if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ + ret = read_alloc(gspca_dev, file); + if (ret != 0) + return ret; + } + + /* get a frame */ + timestamp = ktime_to_timeval(ktime_get()); + timestamp.tv_sec--; + n = 2; + for (;;) { + memset(&v4l2_buf, 0, sizeof v4l2_buf); + v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_buf.memory = GSPCA_MEMORY_READ; + ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf); + if (ret != 0) { + PDEBUG(D_STREAM, "read dqbuf err %d", ret); + return ret; + } + + /* if the process slept for more than 1 second, + * get a newer frame */ + frame = &gspca_dev->frame[v4l2_buf.index]; + if (--n < 0) + break; /* avoid infinite loop */ + if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec) + break; + ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); + if (ret != 0) { + PDEBUG(D_STREAM, "read qbuf err %d", ret); + return ret; + } + } + + /* copy the frame */ + if (count > frame->v4l2_buf.bytesused) + count = frame->v4l2_buf.bytesused; + ret = copy_to_user(data, frame->data, count); + if (ret != 0) { + PDEBUG(D_ERR|D_STREAM, + "read cp to user lack %d / %zd", ret, count); + ret = -EFAULT; + goto out; + } + ret = count; +out: + /* in each case, requeue the buffer */ + ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf); + if (ret2 != 0) + return ret2; + return ret; +} + +static struct v4l2_file_operations dev_fops = { + .owner = THIS_MODULE, + .open = dev_open, + .release = dev_close, + .read = dev_read, + .mmap = dev_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = dev_poll, +}; + +static const struct v4l2_ioctl_ops dev_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_streamon = vidioc_streamon, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_querymenu = vidioc_querymenu, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .vidioc_g_chip_ident = vidioc_g_chip_ident, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct video_device gspca_template = { + .name = "gspca main driver", + .fops = &dev_fops, + .ioctl_ops = &dev_ioctl_ops, + .release = video_device_release_empty, /* We use v4l2_dev.release */ +}; + +/* initialize the controls */ +static void ctrls_init(struct gspca_dev *gspca_dev) +{ + struct gspca_ctrl *ctrl; + int i; + + for (i = 0, ctrl = gspca_dev->cam.ctrls; + i < gspca_dev->sd_desc->nctrls; + i++, ctrl++) { + ctrl->def = gspca_dev->sd_desc->ctrls[i].qctrl.default_value; + ctrl->val = ctrl->def; + ctrl->min = gspca_dev->sd_desc->ctrls[i].qctrl.minimum; + ctrl->max = gspca_dev->sd_desc->ctrls[i].qctrl.maximum; + } +} + +/* + * probe and create a new gspca device + * + * This function must be called by the sub-driver when it is + * called for probing a new device. + */ +int gspca_dev_probe2(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module) +{ + struct gspca_dev *gspca_dev; + struct usb_device *dev = interface_to_usbdev(intf); + int ret; + + pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n", + sd_desc->name, id->idVendor, id->idProduct); + + /* create the device */ + if (dev_size < sizeof *gspca_dev) + dev_size = sizeof *gspca_dev; + gspca_dev = kzalloc(dev_size, GFP_KERNEL); + if (!gspca_dev) { + pr_err("couldn't kzalloc gspca struct\n"); + return -ENOMEM; + } + gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); + if (!gspca_dev->usb_buf) { + pr_err("out of memory\n"); + ret = -ENOMEM; + goto out; + } + gspca_dev->dev = dev; + gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber; + + /* check if any audio device */ + if (dev->actconfig->desc.bNumInterfaces != 1) { + int i; + struct usb_interface *intf2; + + for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { + intf2 = dev->actconfig->interface[i]; + if (intf2 != NULL + && intf2->altsetting != NULL + && intf2->altsetting->desc.bInterfaceClass == + USB_CLASS_AUDIO) { + gspca_dev->audio = 1; + break; + } + } + } + + gspca_dev->v4l2_dev.release = gspca_release; + ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); + if (ret) + goto out; + gspca_dev->sd_desc = sd_desc; + gspca_dev->nbufread = 2; + gspca_dev->empty_packet = -1; /* don't check the empty packets */ + gspca_dev->vdev = gspca_template; + gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; + video_set_drvdata(&gspca_dev->vdev, gspca_dev); + set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags); + gspca_dev->module = module; + gspca_dev->present = 1; + + mutex_init(&gspca_dev->usb_lock); + gspca_dev->vdev.lock = &gspca_dev->usb_lock; + mutex_init(&gspca_dev->queue_lock); + init_waitqueue_head(&gspca_dev->wq); + + /* configure the subdriver and initialize the USB device */ + ret = sd_desc->config(gspca_dev, id); + if (ret < 0) + goto out; + if (gspca_dev->cam.ctrls != NULL) + ctrls_init(gspca_dev); + ret = sd_desc->init(gspca_dev); + if (ret < 0) + goto out; + if (sd_desc->init_controls) + ret = sd_desc->init_controls(gspca_dev); + if (ret < 0) + goto out; + gspca_set_default_mode(gspca_dev); + + ret = gspca_input_connect(gspca_dev); + if (ret) + goto out; + + /* + * Don't take usb_lock for these ioctls. This improves latency if + * usb_lock is taken for a long time, e.g. when changing a control + * value, and a new frame is ready to be dequeued. + */ + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); + if (!gspca_dev->sd_desc->get_chip_ident) + v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT); +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (!gspca_dev->sd_desc->get_chip_ident || + !gspca_dev->sd_desc->get_register) + v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER); + if (!gspca_dev->sd_desc->get_chip_ident || + !gspca_dev->sd_desc->set_register) + v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER); +#endif + if (!gspca_dev->sd_desc->get_jcomp) + v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP); + if (!gspca_dev->sd_desc->set_jcomp) + v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP); + + /* init video stuff */ + ret = video_register_device(&gspca_dev->vdev, + VFL_TYPE_GRABBER, + -1); + if (ret < 0) { + pr_err("video_register_device err %d\n", ret); + goto out; + } + + usb_set_intfdata(intf, gspca_dev); + PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev)); + + gspca_input_create_urb(gspca_dev); + + return 0; +out: +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + if (gspca_dev->input_dev) + input_unregister_device(gspca_dev->input_dev); +#endif + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); + kfree(gspca_dev->usb_buf); + kfree(gspca_dev); + return ret; +} +EXPORT_SYMBOL(gspca_dev_probe2); + +/* same function as the previous one, but check the interface */ +int gspca_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module) +{ + struct usb_device *dev = interface_to_usbdev(intf); + + /* we don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) { + pr_err("%04x:%04x too many config\n", + id->idVendor, id->idProduct); + return -ENODEV; + } + + /* the USB video interface must be the first one */ + if (dev->actconfig->desc.bNumInterfaces != 1 + && intf->cur_altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + + return gspca_dev_probe2(intf, id, sd_desc, dev_size, module); +} +EXPORT_SYMBOL(gspca_dev_probe); + +/* + * USB disconnection + * + * This function must be called by the sub-driver + * when the device disconnects, after the specific resources are freed. + */ +void gspca_disconnect(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + struct input_dev *input_dev; +#endif + + PDEBUG(D_PROBE, "%s disconnect", + video_device_node_name(&gspca_dev->vdev)); + + mutex_lock(&gspca_dev->usb_lock); + + usb_set_intfdata(intf, NULL); + gspca_dev->dev = NULL; + gspca_dev->present = 0; + destroy_urbs(gspca_dev); + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + gspca_input_destroy_urb(gspca_dev); + input_dev = gspca_dev->input_dev; + if (input_dev) { + gspca_dev->input_dev = NULL; + input_unregister_device(input_dev); + } +#endif + /* Free subdriver's streaming resources / stop sd workqueue(s) */ + if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) + gspca_dev->sd_desc->stop0(gspca_dev); + gspca_dev->streaming = 0; + wake_up_interruptible(&gspca_dev->wq); + + v4l2_device_disconnect(&gspca_dev->v4l2_dev); + video_unregister_device(&gspca_dev->vdev); + + mutex_unlock(&gspca_dev->usb_lock); + + /* (this will call gspca_release() immediately or on last close) */ + v4l2_device_put(&gspca_dev->v4l2_dev); +} +EXPORT_SYMBOL(gspca_disconnect); + +#ifdef CONFIG_PM +int gspca_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + if (!gspca_dev->streaming) + return 0; + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->frozen = 1; /* avoid urb error messages */ + gspca_dev->usb_err = 0; + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + return 0; +} +EXPORT_SYMBOL(gspca_suspend); + +int gspca_resume(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + int streaming, ret = 0; + + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->frozen = 0; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->init(gspca_dev); + gspca_input_create_urb(gspca_dev); + /* + * Most subdrivers send all ctrl values on sd_start and thus + * only write to the device registers on s_ctrl when streaming -> + * Clear streaming to avoid setting all ctrls twice. + */ + streaming = gspca_dev->streaming; + gspca_dev->streaming = 0; + if (streaming) + ret = gspca_init_transfer(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + return ret; +} +EXPORT_SYMBOL(gspca_resume); +#endif + +/* -- module insert / remove -- */ +static int __init gspca_init(void) +{ + pr_info("v" GSPCA_VERSION " registered\n"); + return 0; +} +static void __exit gspca_exit(void) +{ +} + +module_init(gspca_init); +module_exit(gspca_exit); + +#ifdef GSPCA_DEBUG +module_param_named(debug, gspca_debug, int, 0644); +MODULE_PARM_DESC(debug, + "Debug (bit) 0x01:error 0x02:probe 0x04:config" + " 0x08:stream 0x10:frame 0x20:packet" + " 0x0100: v4l2"); +#endif diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h new file mode 100644 index 000000000000..dc688c7f5e48 --- /dev/null +++ b/drivers/media/usb/gspca/gspca.h @@ -0,0 +1,259 @@ +#ifndef GSPCAV2_H +#define GSPCAV2_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* compilation option */ +/*#define GSPCA_DEBUG 1*/ + +#ifdef GSPCA_DEBUG +/* GSPCA our debug messages */ +extern int gspca_debug; +#define PDEBUG(level, fmt, ...) \ +do { \ + if (gspca_debug & (level)) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + +#define D_ERR 0x01 +#define D_PROBE 0x02 +#define D_CONF 0x04 +#define D_STREAM 0x08 +#define D_FRAM 0x10 +#define D_PACK 0x20 +#define D_USBI 0x00 +#define D_USBO 0x00 +#define D_V4L2 0x0100 +#else +#define PDEBUG(level, fmt, ...) +#endif + +#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ +/* image transfers */ +#define MAX_NURBS 4 /* max number of URBs */ + + +/* used to list framerates supported by a camera mode (resolution) */ +struct framerates { + const u8 *rates; + int nrates; +}; + +/* control definition */ +struct gspca_ctrl { + s16 val; /* current value */ + s16 def; /* default value */ + s16 min, max; /* minimum and maximum values */ +}; + +/* device information - set at probe time */ +struct cam { + const struct v4l2_pix_format *cam_mode; /* size nmodes */ + const struct framerates *mode_framerates; /* must have size nmodes, + * just like cam_mode */ + struct gspca_ctrl *ctrls; /* control table - size nctrls */ + /* may be NULL */ + u32 bulk_size; /* buffer size when image transfer by bulk */ + u32 input_flags; /* value for ENUM_INPUT status flags */ + u8 nmodes; /* size of cam_mode */ + u8 no_urb_create; /* don't create transfer URBs */ + u8 bulk_nurbs; /* number of URBs in bulk mode + * - cannot be > MAX_NURBS + * - when 0 and bulk_size != 0 means + * 1 URB and submit done by subdriver */ + u8 bulk; /* image transfer by 0:isoc / 1:bulk */ + u8 npkt; /* number of packets in an ISOC message + * 0 is the default value: 32 packets */ + u8 needs_full_bandwidth;/* Set this flag to notify the bandwidth calc. + * code that the cam fills all image buffers to + * the max, even when using compression. */ +}; + +struct gspca_dev; +struct gspca_frame; + +/* subdriver operations */ +typedef int (*cam_op) (struct gspca_dev *); +typedef void (*cam_v_op) (struct gspca_dev *); +typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); +typedef int (*cam_jpg_op) (struct gspca_dev *, + struct v4l2_jpegcompression *); +typedef int (*cam_reg_op) (struct gspca_dev *, + struct v4l2_dbg_register *); +typedef int (*cam_ident_op) (struct gspca_dev *, + struct v4l2_dbg_chip_ident *); +typedef void (*cam_streamparm_op) (struct gspca_dev *, + struct v4l2_streamparm *); +typedef int (*cam_qmnu_op) (struct gspca_dev *, + struct v4l2_querymenu *); +typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, + u8 *data, + int len); +typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev, + u8 *data, + int len); + +struct ctrl { + struct v4l2_queryctrl qctrl; + int (*set)(struct gspca_dev *, __s32); + int (*get)(struct gspca_dev *, __s32 *); + cam_v_op set_control; +}; + +/* subdriver description */ +struct sd_desc { +/* information */ + const char *name; /* sub-driver name */ +/* controls */ + const struct ctrl *ctrls; /* static control definition */ + int nctrls; +/* mandatory operations */ + cam_cf_op config; /* called on probe */ + cam_op init; /* called on probe and resume */ + cam_op init_controls; /* called on probe */ + cam_op start; /* called on stream on after URBs creation */ + cam_pkt_op pkt_scan; +/* optional operations */ + cam_op isoc_init; /* called on stream on before getting the EP */ + cam_op isoc_nego; /* called when URB submit failed with NOSPC */ + cam_v_op stopN; /* called on stream off - main alt */ + cam_v_op stop0; /* called on stream off & disconnect - alt 0 */ + cam_v_op dq_callback; /* called when a frame has been dequeued */ + cam_jpg_op get_jcomp; + cam_jpg_op set_jcomp; + cam_qmnu_op querymenu; + cam_streamparm_op get_streamparm; + cam_streamparm_op set_streamparm; +#ifdef CONFIG_VIDEO_ADV_DEBUG + cam_reg_op set_register; + cam_reg_op get_register; +#endif + cam_ident_op get_chip_ident; +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + cam_int_pkt_op int_pkt_scan; + /* other_input makes the gspca core create gspca_dev->input even when + int_pkt_scan is NULL, for cams with non interrupt driven buttons */ + u8 other_input; +#endif +}; + +/* packet types when moving from iso buf to frame buf */ +enum gspca_packet_type { + DISCARD_PACKET, + FIRST_PACKET, + INTER_PACKET, + LAST_PACKET +}; + +struct gspca_frame { + __u8 *data; /* frame buffer */ + int vma_use_count; + struct v4l2_buffer v4l2_buf; +}; + +struct gspca_dev { + struct video_device vdev; /* !! must be the first item */ + struct module *module; /* subdriver handling the device */ + struct v4l2_device v4l2_dev; + struct usb_device *dev; + struct file *capt_file; /* file doing video capture */ + /* protected by queue_lock */ +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + struct input_dev *input_dev; + char phys[64]; /* physical device path */ +#endif + + struct cam cam; /* device information */ + const struct sd_desc *sd_desc; /* subdriver description */ + unsigned ctrl_dis; /* disabled controls (bit map) */ + unsigned ctrl_inac; /* inactive controls (bit map) */ + struct v4l2_ctrl_handler ctrl_handler; + + /* autogain and exposure or gain control cluster, these are global as + the autogain/exposure functions in autogain_functions.c use them */ + struct { + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + int exp_too_low_cnt, exp_too_high_cnt; + }; + +#define USB_BUF_SZ 64 + __u8 *usb_buf; /* buffer for USB exchanges */ + struct urb *urb[MAX_NURBS]; +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + struct urb *int_urb; +#endif + + __u8 *frbuf; /* buffer for nframes */ + struct gspca_frame frame[GSPCA_MAX_FRAMES]; + u8 *image; /* image beeing filled */ + __u32 frsz; /* frame size */ + u32 image_len; /* current length of image */ + atomic_t fr_q; /* next frame to queue */ + atomic_t fr_i; /* frame being filled */ + signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ + char nframes; /* number of frames */ + u8 fr_o; /* next frame to dequeue */ + __u8 last_packet_type; + __s8 empty_packet; /* if (-1) don't check empty packets */ + __u8 streaming; /* protected by both mutexes (*) */ + + __u8 curr_mode; /* current camera mode */ + __u32 pixfmt; /* current mode parameters */ + __u16 width; + __u16 height; + __u32 sequence; /* frame sequence number */ + + wait_queue_head_t wq; /* wait queue */ + struct mutex usb_lock; /* usb exchange protection */ + struct mutex queue_lock; /* ISOC queue protection */ + int usb_err; /* USB error - protected by usb_lock */ + u16 pkt_size; /* ISOC packet size */ +#ifdef CONFIG_PM + char frozen; /* suspend - resume */ +#endif + char present; /* device connected */ + char nbufread; /* number of buffers for read() */ + char memory; /* memory type (V4L2_MEMORY_xxx) */ + __u8 iface; /* USB interface number */ + __u8 alt; /* USB alternate setting */ + u8 audio; /* presence of audio device */ + + /* (*) These variables are proteced by both usb_lock and queue_lock, + that is any code setting them is holding *both*, which means that + any code getting them needs to hold at least one of them */ +}; + +int gspca_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module); +int gspca_dev_probe2(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module); +void gspca_disconnect(struct usb_interface *intf); +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len); +#ifdef CONFIG_PM +int gspca_suspend(struct usb_interface *intf, pm_message_t message); +int gspca_resume(struct usb_interface *intf); +#endif +int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum, + int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); +int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, + int avg_lum, int desired_avg_lum, int deadzone); + +#endif /* GSPCAV2_H */ diff --git a/drivers/media/usb/gspca/jeilinj.c b/drivers/media/usb/gspca/jeilinj.c new file mode 100644 index 000000000000..26b99310d628 --- /dev/null +++ b/drivers/media/usb/gspca/jeilinj.c @@ -0,0 +1,548 @@ +/* + * Jeilinj subdriver + * + * Supports some Jeilin dual-mode cameras which use bulk transport and + * download raw JPEG data. + * + * Copyright (C) 2009 Theodore Kilgore + * + * Sportscam DV15 support and control settings are + * Copyright (C) 2011 Patrice Chotard + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "jeilinj" + +#include +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Theodore Kilgore "); +MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define JEILINJ_CMD_TIMEOUT 500 +#define JEILINJ_CMD_DELAY 160 +#define JEILINJ_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define JEILINJ_MAX_TRANSFER 0x200 +#define FRAME_HEADER_LEN 0x10 +#define FRAME_START 0xFFFFFFFF + +enum { + SAKAR_57379, + SPORTSCAM_DV15, +}; + +#define CAMQUALITY_MIN 0 /* highest cam quality */ +#define CAMQUALITY_MAX 97 /* lowest cam quality */ + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + int blocks_left; + const struct v4l2_pix_format *cap_mode; + struct v4l2_ctrl *freq; + struct v4l2_ctrl *jpegqual; + /* Driver stuff */ + u8 type; + u8 quality; /* image quality */ +#define QUALITY_MIN 35 +#define QUALITY_MAX 85 +#define QUALITY_DEF 85 + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +struct jlj_command { + unsigned char instruction[2]; + unsigned char ack_wanted; + unsigned char delay; +}; + +/* AFAICT these cameras will only do 320x240. */ +static struct v4l2_pix_format jlj_mode[] = { + { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, + { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0} +}; + +/* + * cam uses endpoint 0x03 to send commands, 0x84 for read commands, + * and 0x82 for bulk transfer. + */ + +/* All commands are two bytes only */ +static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) +{ + int retval; + + if (gspca_dev->usb_err < 0) + return; + memcpy(gspca_dev->usb_buf, command, 2); + retval = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 3), + gspca_dev->usb_buf, 2, NULL, 500); + if (retval < 0) { + pr_err("command write [%02x] error %d\n", + gspca_dev->usb_buf[0], retval); + gspca_dev->usb_err = retval; + } +} + +/* Responses are one byte only */ +static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) +{ + int retval; + + if (gspca_dev->usb_err < 0) + return; + retval = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x84), + gspca_dev->usb_buf, 1, NULL, 500); + response = gspca_dev->usb_buf[0]; + if (retval < 0) { + pr_err("read command [%02x] error %d\n", + gspca_dev->usb_buf[0], retval); + gspca_dev->usb_err = retval; + } +} + +static void setfreq(struct gspca_dev *gspca_dev, s32 val) +{ + u8 freq_commands[][2] = { + {0x71, 0x80}, + {0x70, 0x07} + }; + + freq_commands[0][1] |= val >> 1; + + jlj_write2(gspca_dev, freq_commands[0]); + jlj_write2(gspca_dev, freq_commands[1]); +} + +static void setcamquality(struct gspca_dev *gspca_dev, s32 val) +{ + u8 quality_commands[][2] = { + {0x71, 0x1E}, + {0x70, 0x06} + }; + u8 camquality; + + /* adapt camera quality from jpeg quality */ + camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX) + / (QUALITY_MAX - QUALITY_MIN); + quality_commands[0][1] += camquality; + + jlj_write2(gspca_dev, quality_commands[0]); + jlj_write2(gspca_dev, quality_commands[1]); +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 val) +{ + u8 autogain_commands[][2] = { + {0x94, 0x02}, + {0xcf, 0x00} + }; + + autogain_commands[1][1] = val << 4; + + jlj_write2(gspca_dev, autogain_commands[0]); + jlj_write2(gspca_dev, autogain_commands[1]); +} + +static void setred(struct gspca_dev *gspca_dev, s32 val) +{ + u8 setred_commands[][2] = { + {0x94, 0x02}, + {0xe6, 0x00} + }; + + setred_commands[1][1] = val; + + jlj_write2(gspca_dev, setred_commands[0]); + jlj_write2(gspca_dev, setred_commands[1]); +} + +static void setgreen(struct gspca_dev *gspca_dev, s32 val) +{ + u8 setgreen_commands[][2] = { + {0x94, 0x02}, + {0xe7, 0x00} + }; + + setgreen_commands[1][1] = val; + + jlj_write2(gspca_dev, setgreen_commands[0]); + jlj_write2(gspca_dev, setgreen_commands[1]); +} + +static void setblue(struct gspca_dev *gspca_dev, s32 val) +{ + u8 setblue_commands[][2] = { + {0x94, 0x02}, + {0xe9, 0x00} + }; + + setblue_commands[1][1] = val; + + jlj_write2(gspca_dev, setblue_commands[0]); + jlj_write2(gspca_dev, setblue_commands[1]); +} + +static int jlj_start(struct gspca_dev *gspca_dev) +{ + int i; + int start_commands_size; + u8 response = 0xff; + struct sd *sd = (struct sd *) gspca_dev; + struct jlj_command start_commands[] = { + {{0x71, 0x81}, 0, 0}, + {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY}, + {{0x95, 0x70}, 1, 0}, + {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0}, + {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY}, + {{0x95, 0x70}, 1, 0}, + {{0x71, 0x00}, 0, 0}, /* start streaming ??*/ + {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY}, + {{0x95, 0x70}, 1, 0}, +#define SPORTSCAM_DV15_CMD_SIZE 9 + {{0x94, 0x02}, 0, 0}, + {{0xde, 0x24}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xdd, 0xf0}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe3, 0x2c}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe4, 0x00}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe5, 0x00}, 0, 0}, + {{0x94, 0x02}, 0, 0}, + {{0xe6, 0x2c}, 0, 0}, + {{0x94, 0x03}, 0, 0}, + {{0xaa, 0x00}, 0, 0} + }; + + sd->blocks_left = 0; + /* Under Windows, USB spy shows that only the 9 first start + * commands are used for SPORTSCAM_DV15 webcam + */ + if (sd->type == SPORTSCAM_DV15) + start_commands_size = SPORTSCAM_DV15_CMD_SIZE; + else + start_commands_size = ARRAY_SIZE(start_commands); + + for (i = 0; i < start_commands_size; i++) { + jlj_write2(gspca_dev, start_commands[i].instruction); + if (start_commands[i].delay) + msleep(start_commands[i].delay); + if (start_commands[i].ack_wanted) + jlj_read1(gspca_dev, response); + } + setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); + msleep(2); + setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); + if (gspca_dev->usb_err < 0) + PDEBUG(D_ERR, "Start streaming command failed"); + return gspca_dev->usb_err; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int packet_type; + u32 header_marker; + + PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0", + len, JEILINJ_MAX_TRANSFER); + if (len != JEILINJ_MAX_TRANSFER) { + PDEBUG(D_PACK, "bad length"); + goto discard; + } + /* check if it's start of frame */ + header_marker = ((u32 *)data)[0]; + if (header_marker == FRAME_START) { + sd->blocks_left = data[0x0a] - 1; + PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left); + /* Start a new frame, and add the JPEG header, first thing */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* Toss line 0 of data block 0, keep the rest. */ + gspca_frame_add(gspca_dev, INTER_PACKET, + data + FRAME_HEADER_LEN, + JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); + } else if (sd->blocks_left > 0) { + PDEBUG(D_STREAM, "%d blocks remaining for frame", + sd->blocks_left); + sd->blocks_left -= 1; + if (sd->blocks_left == 0) + packet_type = LAST_PACKET; + else + packet_type = INTER_PACKET; + gspca_frame_add(gspca_dev, packet_type, + data, JEILINJ_MAX_TRANSFER); + } else + goto discard; + return; +discard: + /* Discard data until a new frame starts. */ + gspca_dev->last_packet_type = DISCARD_PACKET; +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *dev = (struct sd *) gspca_dev; + + dev->type = id->driver_info; + dev->quality = QUALITY_DEF; + + cam->cam_mode = jlj_mode; + cam->nmodes = ARRAY_SIZE(jlj_mode); + cam->bulk = 1; + cam->bulk_nurbs = 1; + cam->bulk_size = JEILINJ_MAX_TRANSFER; + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int i; + u8 *buf; + static u8 stop_commands[][2] = { + {0x71, 0x00}, + {0x70, 0x09}, + {0x71, 0x80}, + {0x70, 0x05} + }; + + for (;;) { + /* get the image remaining blocks */ + usb_bulk_msg(gspca_dev->dev, + gspca_dev->urb[0]->pipe, + gspca_dev->urb[0]->transfer_buffer, + JEILINJ_MAX_TRANSFER, NULL, + JEILINJ_DATA_TIMEOUT); + + /* search for 0xff 0xd9 (EOF for JPEG) */ + i = 0; + buf = gspca_dev->urb[0]->transfer_buffer; + while ((i < (JEILINJ_MAX_TRANSFER - 1)) && + ((buf[i] != 0xff) || (buf[i+1] != 0xd9))) + i++; + + if (i != (JEILINJ_MAX_TRANSFER - 1)) + /* last remaining block found */ + break; + } + + for (i = 0; i < ARRAY_SIZE(stop_commands); i++) + jlj_write2(gspca_dev, stop_commands[i]); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return gspca_dev->usb_err; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + + /* create the JPEG header */ + jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(dev->jpeg_hdr, dev->quality); + PDEBUG(D_STREAM, "Start streaming at %dx%d", + gspca_dev->height, gspca_dev->width); + jlj_start(gspca_dev); + return gspca_dev->usb_err; +} + +/* Table of supported USB devices */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379}, + {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + setfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + setred(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + setgreen(gspca_dev, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + setblue(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + jpeg_set_qual(sd->jpeg_hdr, ctrl->val); + setcamquality(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + static const struct v4l2_ctrl_config custom_autogain = { + .ops = &sd_ctrl_ops, + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain (and Exposure)", + .max = 3, + .step = 1, + .def = 0, + }; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 6); + sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ); + v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 3, 1, 2); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 3, 1, 2); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2); + sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + + +/* sub-driver description */ +static const struct sd_desc sd_desc_sakar_57379 = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* sub-driver description */ +static const struct sd_desc sd_desc_sportscam_dv15 = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, +}; + +static const struct sd_desc *sd_desc[2] = { + &sd_desc_sakar_57379, + &sd_desc_sportscam_dv15 +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + sd_desc[id->driver_info], + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c new file mode 100644 index 000000000000..cf9d9fca5b84 --- /dev/null +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -0,0 +1,557 @@ +/* + * Jeilin JL2005B/C/D library + * + * Copyright (C) 2011 Theodore Kilgore + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "jl2005bcd" + +#include +#include +#include "gspca.h" + + +MODULE_AUTHOR("Theodore Kilgore "); +MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define JL2005C_CMD_TIMEOUT 500 +#define JL2005C_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define JL2005C_MAX_TRANSFER 0x200 +#define FRAME_HEADER_LEN 16 + + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + unsigned char firmware_id[6]; + const struct v4l2_pix_format *cap_mode; + /* Driver stuff */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; + u8 frame_brightness; + int block_size; /* block size of camera */ + int vga; /* 1 if vga cam, 0 if cif cam */ +}; + + +/* Camera has two resolution settings. What they are depends on model. */ +static const struct v4l2_pix_format cif_mode[] = { + {176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* + * cam uses endpoint 0x03 to send commands, 0x84 for read commands, + * and 0x82 for bulk data transfer. + */ + +/* All commands are two bytes only */ +static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command) +{ + int retval; + + memcpy(gspca_dev->usb_buf, command, 2); + retval = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 3), + gspca_dev->usb_buf, 2, NULL, 500); + if (retval < 0) + pr_err("command write [%02x] error %d\n", + gspca_dev->usb_buf[0], retval); + return retval; +} + +/* Response to a command is one byte in usb_buf[0], only if requested. */ +static int jl2005c_read1(struct gspca_dev *gspca_dev) +{ + int retval; + + retval = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x84), + gspca_dev->usb_buf, 1, NULL, 500); + if (retval < 0) + pr_err("read command [0x%02x] error %d\n", + gspca_dev->usb_buf[0], retval); + return retval; +} + +/* Response appears in gspca_dev->usb_buf[0] */ +static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg) +{ + int retval; + + static u8 instruction[2] = {0x95, 0x00}; + /* put register to read in byte 1 */ + instruction[1] = reg; + /* Send the read request */ + retval = jl2005c_write2(gspca_dev, instruction); + if (retval < 0) + return retval; + retval = jl2005c_read1(gspca_dev); + + return retval; +} + +static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev) +{ + int i; + int retval; + int frame_brightness = 0; + + static u8 instruction[2] = {0x7f, 0x01}; + + retval = jl2005c_write2(gspca_dev, instruction); + if (retval < 0) + return retval; + + i = 0; + while (i < 20 && !frame_brightness) { + /* If we tried 20 times, give up. */ + retval = jl2005c_read_reg(gspca_dev, 0x7e); + if (retval < 0) + return retval; + frame_brightness = gspca_dev->usb_buf[0]; + retval = jl2005c_read_reg(gspca_dev, 0x7d); + if (retval < 0) + return retval; + i++; + } + PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]); + return retval; +} + +static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg, + unsigned char value) +{ + int retval; + u8 instruction[2]; + + instruction[0] = reg; + instruction[1] = value; + + retval = jl2005c_write2(gspca_dev, instruction); + if (retval < 0) + return retval; + + return retval; +} + +static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + int i = 0; + int retval = -1; + unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f}; + + PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id"); + /* Read the first ID byte once for warmup */ + retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]); + PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]); + if (retval < 0) + return retval; + /* Now actually get the ID string */ + for (i = 0; i < 6; i++) { + retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]); + if (retval < 0) + return retval; + sd->firmware_id[i] = gspca_dev->usb_buf[0]; + } + PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x", + sd->firmware_id[0], + sd->firmware_id[1], + sd->firmware_id[2], + sd->firmware_id[3], + sd->firmware_id[4], + sd->firmware_id[5]); + return 0; +} + +static int jl2005c_stream_start_vga_lg + (struct gspca_dev *gspca_dev) +{ + int i; + int retval = -1; + static u8 instruction[][2] = { + {0x05, 0x00}, + {0x7c, 0x00}, + {0x7d, 0x18}, + {0x02, 0x00}, + {0x01, 0x00}, + {0x04, 0x52}, + }; + + for (i = 0; i < ARRAY_SIZE(instruction); i++) { + msleep(60); + retval = jl2005c_write2(gspca_dev, instruction[i]); + if (retval < 0) + return retval; + } + msleep(60); + return retval; +} + +static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev) +{ + int i; + int retval = -1; + static u8 instruction[][2] = { + {0x06, 0x00}, + {0x7c, 0x00}, + {0x7d, 0x1a}, + {0x02, 0x00}, + {0x01, 0x00}, + {0x04, 0x52}, + }; + + for (i = 0; i < ARRAY_SIZE(instruction); i++) { + msleep(60); + retval = jl2005c_write2(gspca_dev, instruction[i]); + if (retval < 0) + return retval; + } + msleep(60); + return retval; +} + +static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev) +{ + int i; + int retval = -1; + static u8 instruction[][2] = { + {0x05, 0x00}, + {0x7c, 0x00}, + {0x7d, 0x30}, + {0x02, 0x00}, + {0x01, 0x00}, + {0x04, 0x42}, + }; + + for (i = 0; i < ARRAY_SIZE(instruction); i++) { + msleep(60); + retval = jl2005c_write2(gspca_dev, instruction[i]); + if (retval < 0) + return retval; + } + msleep(60); + return retval; +} + +static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev) +{ + int i; + int retval = -1; + static u8 instruction[][2] = { + {0x06, 0x00}, + {0x7c, 0x00}, + {0x7d, 0x32}, + {0x02, 0x00}, + {0x01, 0x00}, + {0x04, 0x42}, + }; + + for (i = 0; i < ARRAY_SIZE(instruction); i++) { + msleep(60); + retval = jl2005c_write2(gspca_dev, instruction[i]); + if (retval < 0) + return retval; + } + msleep(60); + return retval; +} + + +static int jl2005c_stop(struct gspca_dev *gspca_dev) +{ + int retval; + + retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00); + return retval; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface the gspca usb_lock is + * used when performing the one USB control operation inside the workqueue, + * which tells the camera to close the stream. In practice the only thing + * which needs to be protected against is the usb_set_interface call that + * gspca makes during stream_off. Otherwise the camera doesn't provide any + * controls that the user could try to change. + */ +static void jl2005c_dostream(struct work_struct *work) +{ + struct sd *dev = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + int bytes_left = 0; /* bytes remaining in current frame. */ + int data_len; /* size to use for the next read. */ + int header_read = 0; + unsigned char header_sig[2] = {0x4a, 0x4c}; + int act_len; + int packet_type; + int ret; + u8 *buffer; + + buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + if (!buffer) { + pr_err("Couldn't allocate USB buffer\n"); + goto quit_stream; + } + + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + /* Check if this is a new frame. If so, start the frame first */ + if (!header_read) { + mutex_lock(&gspca_dev->usb_lock); + ret = jl2005c_start_new_frame(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + goto quit_stream; + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x82), + buffer, JL2005C_MAX_TRANSFER, &act_len, + JL2005C_DATA_TIMEOUT); + PDEBUG(D_PACK, + "Got %d bytes out of %d for header", + act_len, JL2005C_MAX_TRANSFER); + if (ret < 0 || act_len < JL2005C_MAX_TRANSFER) + goto quit_stream; + /* Check whether we actually got the first blodk */ + if (memcmp(header_sig, buffer, 2) != 0) { + pr_err("First block is not the first block\n"); + goto quit_stream; + } + /* total size to fetch is byte 7, times blocksize + * of which we already got act_len */ + bytes_left = buffer[0x07] * dev->block_size - act_len; + PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left); + /* We keep the header. It has other information, too.*/ + packet_type = FIRST_PACKET; + gspca_frame_add(gspca_dev, packet_type, + buffer, act_len); + header_read = 1; + } + while (bytes_left > 0 && gspca_dev->dev) { + data_len = bytes_left > JL2005C_MAX_TRANSFER ? + JL2005C_MAX_TRANSFER : bytes_left; + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x82), + buffer, data_len, &act_len, + JL2005C_DATA_TIMEOUT); + if (ret < 0 || act_len < data_len) + goto quit_stream; + PDEBUG(D_PACK, + "Got %d bytes out of %d for frame", + data_len, bytes_left); + bytes_left -= data_len; + if (bytes_left == 0) { + packet_type = LAST_PACKET; + header_read = 0; + } else + packet_type = INTER_PACKET; + gspca_frame_add(gspca_dev, packet_type, + buffer, data_len); + } + } +quit_stream: + if (gspca_dev->dev) { + mutex_lock(&gspca_dev->usb_lock); + jl2005c_stop(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } + kfree(buffer); +} + + + + +/* This function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + struct sd *sd = (struct sd *) gspca_dev; + + cam = &gspca_dev->cam; + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk_size = 64; + cam->bulk = 1; + /* For the rest, the camera needs to be detected */ + jl2005c_get_firmware_id(gspca_dev); + /* Here are some known firmware IDs + * First some JL2005B cameras + * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2} Sakar KidzCam + * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2} No-name JL2005B + * JL2005C cameras + * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8} Argus DC-1512 + * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8} ICarly + * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4} Jazz + * + * Based upon this scanty evidence, we can detect a CIF camera by + * testing byte 0 for 0x4x. + */ + if ((sd->firmware_id[0] & 0xf0) == 0x40) { + cam->cam_mode = cif_mode; + cam->nmodes = ARRAY_SIZE(cif_mode); + sd->block_size = 0x80; + } else { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + sd->block_size = 0x200; + } + + INIT_WORK(&sd->work_struct, jl2005c_dostream); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + + struct sd *sd = (struct sd *) gspca_dev; + sd->cap_mode = gspca_dev->cam.cam_mode; + + switch (gspca_dev->width) { + case 640: + PDEBUG(D_STREAM, "Start streaming at vga resolution"); + jl2005c_stream_start_vga_lg(gspca_dev); + break; + case 320: + PDEBUG(D_STREAM, "Start streaming at qvga resolution"); + jl2005c_stream_start_vga_small(gspca_dev); + break; + case 352: + PDEBUG(D_STREAM, "Start streaming at cif resolution"); + jl2005c_stream_start_cif_lg(gspca_dev); + break; + case 176: + PDEBUG(D_STREAM, "Start streaming at qcif resolution"); + jl2005c_stream_start_cif_small(gspca_dev); + break; + default: + pr_err("Unknown resolution specified\n"); + return -1; + } + + /* Start the workqueue function to do the streaming */ + sd->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(sd->work_thread, &sd->work_struct); + + return 0; +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for sq905c_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); +} + + + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0979, 0x0227)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/usb/gspca/jpeg.h b/drivers/media/usb/gspca/jpeg.h new file mode 100644 index 000000000000..ab54910418b4 --- /dev/null +++ b/drivers/media/usb/gspca/jpeg.h @@ -0,0 +1,168 @@ +#ifndef JPEG_H +#define JPEG_H 1 +/* + * Insert a JPEG header at start of frame + * + * This module is used by the gspca subdrivers. + * A special case is done for Conexant webcams. + * + * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * generation options + * CONEX_CAM Conexant if present + */ + +/* JPEG header */ +static const u8 jpeg_head[] = { + 0xff, 0xd8, /* jpeg */ + +/* quantization table quality 50% */ + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, +#define JPEG_QT0_OFFSET 7 + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, +1, +#define JPEG_QT1_OFFSET 72 + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + +/* huffman table */ + 0xff, 0xc4, 0x01, 0xa2, + 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, + 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, + 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, + 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, + 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, + 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, + 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, + 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, + 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, + 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, + 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, + 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, + 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, + 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, + 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, + 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, + 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, + 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, +#ifdef CONEX_CAM +/* the Conexant frames start with SOF0 */ +#define JPEG_HDR_SZ 556 +#else + 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ + 0x08, /* data precision */ +#define JPEG_HEIGHT_OFFSET 561 + 0x01, 0xe0, /* height */ + 0x02, 0x80, /* width */ + 0x03, /* component number */ + 0x01, + 0x21, /* samples Y */ + 0x00, /* quant Y */ + 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ + 0x03, 0x11, 0x01, + + 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ + 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +#define JPEG_HDR_SZ 589 +#endif +}; + +/* define the JPEG header */ +static void jpeg_define(u8 *jpeg_hdr, + int height, + int width, + int samplesY) +{ + memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); +#ifndef CONEX_CAM + jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY; +#endif +} + +/* set the JPEG quality */ +static void jpeg_set_qual(u8 *jpeg_hdr, + int quality) +{ + int i, sc; + + if (quality < 50) + sc = 5000 / quality; + else + sc = 200 - quality * 2; + for (i = 0; i < 64; i++) { + jpeg_hdr[JPEG_QT0_OFFSET + i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + jpeg_hdr[JPEG_QT1_OFFSET + i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + } +} +#endif diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c new file mode 100644 index 000000000000..40ad6687ee5d --- /dev/null +++ b/drivers/media/usb/gspca/kinect.c @@ -0,0 +1,408 @@ +/* + * kinect sensor device camera, gspca driver + * + * Copyright (C) 2011 Antonio Ospite + * + * Based on the OpenKinect project and libfreenect + * http://openkinect.org/wiki/Init_Analysis + * + * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect + * sensor device which I tested the driver on. + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "kinect" + +#include "gspca.h" + +#define CTRL_TIMEOUT 500 + +MODULE_AUTHOR("Antonio Ospite "); +MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); +MODULE_LICENSE("GPL"); + +struct pkt_hdr { + uint8_t magic[2]; + uint8_t pad; + uint8_t flag; + uint8_t unk1; + uint8_t seq; + uint8_t unk2; + uint8_t unk3; + uint32_t timestamp; +}; + +struct cam_hdr { + uint8_t magic[2]; + uint16_t len; + uint16_t cmd; + uint16_t tag; +}; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + uint16_t cam_tag; /* a sequence number for packets */ + uint8_t stream_flag; /* to identify different stream types */ + uint8_t obuf[0x400]; /* output buffer for control commands */ + uint8_t ibuf[0x200]; /* input buffer for control commands */ +}; + +#define MODE_640x480 0x0001 +#define MODE_640x488 0x0002 +#define MODE_1280x1024 0x0004 + +#define FORMAT_BAYER 0x0010 +#define FORMAT_UYVY 0x0020 +#define FORMAT_Y10B 0x0040 + +#define FPS_HIGH 0x0100 + +static const struct v4l2_pix_format video_camera_mode[] = { + {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH}, + {640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_640x480 | FORMAT_UYVY}, + {1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_1280x1024 | FORMAT_BAYER}, + {640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, + .bytesperline = 640 * 10 / 8, + .sizeimage = 640 * 488 * 10 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH}, + {1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, + .bytesperline = 1280 * 10 / 8, + .sizeimage = 1280 * 1024 * 10 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_1280x1024 | FORMAT_Y10B}, +}; + +static int kinect_write(struct usb_device *udev, uint8_t *data, + uint16_t wLength) +{ + return usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x00, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, data, wLength, CTRL_TIMEOUT); +} + +static int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength) +{ + return usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, data, wLength, CTRL_TIMEOUT); +} + +static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, + unsigned int cmd_len, void *replybuf, unsigned int reply_len) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *udev = gspca_dev->dev; + int res, actual_len; + uint8_t *obuf = sd->obuf; + uint8_t *ibuf = sd->ibuf; + struct cam_hdr *chdr = (void *)obuf; + struct cam_hdr *rhdr = (void *)ibuf; + + if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) { + pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len); + return -1; + } + + chdr->magic[0] = 0x47; + chdr->magic[1] = 0x4d; + chdr->cmd = cpu_to_le16(cmd); + chdr->tag = cpu_to_le16(sd->cam_tag); + chdr->len = cpu_to_le16(cmd_len / 2); + + memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len); + + res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr)); + PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd, + sd->cam_tag, cmd_len, res); + if (res < 0) { + pr_err("send_cmd: Output control transfer failed (%d)\n", res); + return res; + } + + do { + actual_len = kinect_read(udev, ibuf, 0x200); + } while (actual_len == 0); + PDEBUG(D_USBO, "Control reply: %d", res); + if (actual_len < sizeof(*rhdr)) { + pr_err("send_cmd: Input control transfer failed (%d)\n", res); + return res; + } + actual_len -= sizeof(*rhdr); + + if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) { + pr_err("send_cmd: Bad magic %02x %02x\n", + rhdr->magic[0], rhdr->magic[1]); + return -1; + } + if (rhdr->cmd != chdr->cmd) { + pr_err("send_cmd: Bad cmd %02x != %02x\n", + rhdr->cmd, chdr->cmd); + return -1; + } + if (rhdr->tag != chdr->tag) { + pr_err("send_cmd: Bad tag %04x != %04x\n", + rhdr->tag, chdr->tag); + return -1; + } + if (cpu_to_le16(rhdr->len) != (actual_len/2)) { + pr_err("send_cmd: Bad len %04x != %04x\n", + cpu_to_le16(rhdr->len), (int)(actual_len/2)); + return -1; + } + + if (actual_len > reply_len) { + pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n", + reply_len, actual_len); + memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len); + } else { + memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len); + } + + sd->cam_tag++; + + return actual_len; +} + +static int write_register(struct gspca_dev *gspca_dev, uint16_t reg, + uint16_t data) +{ + uint16_t reply[2]; + uint16_t cmd[2]; + int res; + + cmd[0] = cpu_to_le16(reg); + cmd[1] = cpu_to_le16(data); + + PDEBUG(D_USBO, "Write Reg 0x%04x <= 0x%02x", reg, data); + res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4); + if (res < 0) + return res; + if (res != 2) { + pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n", + res, reply[0], reply[1]); + } + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + sd->cam_tag = 0; + + /* Only video stream is supported for now, + * which has stream flag = 0x80 */ + sd->stream_flag = 0x80; + + cam = &gspca_dev->cam; + + cam->cam_mode = video_camera_mode; + cam->nmodes = ARRAY_SIZE(video_camera_mode); + +#if 0 + /* Setting those values is not needed for video stream */ + cam->npkt = 15; + gspca_dev->pkt_size = 960 * 2; +#endif + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + PDEBUG(D_PROBE, "Kinect Camera device."); + + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + int mode; + uint8_t fmt_reg, fmt_val; + uint8_t res_reg, res_val; + uint8_t fps_reg, fps_val; + uint8_t mode_val; + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + + if (mode & FORMAT_Y10B) { + fmt_reg = 0x19; + res_reg = 0x1a; + fps_reg = 0x1b; + mode_val = 0x03; + } else { + fmt_reg = 0x0c; + res_reg = 0x0d; + fps_reg = 0x0e; + mode_val = 0x01; + } + + /* format */ + if (mode & FORMAT_UYVY) + fmt_val = 0x05; + else + fmt_val = 0x00; + + if (mode & MODE_1280x1024) + res_val = 0x02; + else + res_val = 0x01; + + if (mode & FPS_HIGH) + fps_val = 0x1e; + else + fps_val = 0x0f; + + + /* turn off IR-reset function */ + write_register(gspca_dev, 0x105, 0x00); + + /* Reset video stream */ + write_register(gspca_dev, 0x05, 0x00); + + /* Due to some ridiculous condition in the firmware, we have to start + * and stop the depth stream before the camera will hand us 1280x1024 + * IR. This is a stupid workaround, but we've yet to find a better + * solution. + * + * Thanks to Drew Fisher for figuring this out. + */ + if (mode & (FORMAT_Y10B | MODE_1280x1024)) { + write_register(gspca_dev, 0x13, 0x01); + write_register(gspca_dev, 0x14, 0x1e); + write_register(gspca_dev, 0x06, 0x02); + write_register(gspca_dev, 0x06, 0x00); + } + + write_register(gspca_dev, fmt_reg, fmt_val); + write_register(gspca_dev, res_reg, res_val); + write_register(gspca_dev, fps_reg, fps_val); + + /* Start video stream */ + write_register(gspca_dev, 0x05, mode_val); + + /* disable Hflip */ + write_register(gspca_dev, 0x47, 0x00); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* reset video stream */ + write_register(gspca_dev, 0x05, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + struct pkt_hdr *hdr = (void *)__data; + uint8_t *data = __data + sizeof(*hdr); + int datalen = len - sizeof(*hdr); + + uint8_t sof = sd->stream_flag | 1; + uint8_t mof = sd->stream_flag | 2; + uint8_t eof = sd->stream_flag | 5; + + if (len < 12) + return; + + if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') { + pr_warn("[Stream %02x] Invalid magic %02x%02x\n", + sd->stream_flag, hdr->magic[0], hdr->magic[1]); + return; + } + + if (hdr->flag == sof) + gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen); + + else if (hdr->flag == mof) + gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen); + + else if (hdr->flag == eof) + gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen); + + else + pr_warn("Packet type not recognized...\n"); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + /* + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, + */ +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x045e, 0x02ae)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c new file mode 100644 index 000000000000..bbf91e07e38b --- /dev/null +++ b/drivers/media/usb/gspca/konica.c @@ -0,0 +1,487 @@ +/* + * Driver for USB webcams based on Konica chipset. This + * chipset is used in Intel YC76 camera. + * + * Copyright (C) 2010 Hans de Goede + * + * Based on the usbvideo v4l1 konicawc driver which is: + * + * Copyright (C) 2002 Simon Evans + * + * The code for making gspca work with a webcam with 2 isoc endpoints was + * taken from the benq gspca subdriver which is: + * + * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "konica" + +#include +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Konica chipset USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define WHITEBAL_REG 0x01 +#define BRIGHTNESS_REG 0x02 +#define SHARPNESS_REG 0x03 +#define CONTRAST_REG 0x04 +#define SATURATION_REG 0x05 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct urb *last_data_urb; + u8 snapshot_pressed; +}; + + +/* .priv is what goes to register 8 for this mode, known working values: + 0x00 -> 176x144, cropped + 0x01 -> 176x144, cropped + 0x02 -> 176x144, cropped + 0x03 -> 176x144, cropped + 0x04 -> 176x144, binned + 0x05 -> 320x240 + 0x06 -> 320x240 + 0x07 -> 160x120, cropped + 0x08 -> 160x120, cropped + 0x09 -> 160x120, binned (note has 136 lines) + 0x0a -> 160x120, binned (note has 136 lines) + 0x0b -> 160x120, cropped +*/ +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 136 * 3 / 2 + 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0x0a}, + {176, 144, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 2 + 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0x04}, + {320, 240, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2 + 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0x05}, +}; + +static void sd_isoc_irq(struct urb *urb); + +static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 1000); + if (ret < 0) { + pr_err("reg_w err writing %02x to %02x: %d\n", + value, index, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x03, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + gspca_dev->usb_buf, + 2, + 1000); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void konica_stream_on(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 1, 0x0b); +} + +static void konica_stream_off(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0, 0x0b); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.no_urb_create = 1; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int i; + + /* + * The konica needs a freaking large time to "boot" (approx 6.5 sec.), + * and does not want to be bothered while doing so :| + * Register 0x10 counts from 1 - 3, with 3 being "ready" + */ + msleep(6000); + for (i = 0; i < 20; i++) { + reg_r(gspca_dev, 0, 0x10); + if (gspca_dev->usb_buf[0] == 3) + break; + msleep(100); + } + reg_w(gspca_dev, 0, 0x0d); + + return gspca_dev->usb_err; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct urb *urb; + int i, n, packet_size; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + pr_err("Couldn't get altsetting\n"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + + n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + reg_w(gspca_dev, n, 0x08); + + konica_stream_on(gspca_dev); + + if (gspca_dev->usb_err) + return gspca_dev->usb_err; + + /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */ +#if MAX_NURBS < 4 +#error "Not enough URBs in the gspca table" +#endif +#define SD_NPKT 32 + for (n = 0; n < 4; n++) { + i = n & 1 ? 0 : 1; + packet_size = + le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize); + urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); + if (!urb) { + pr_err("usb_alloc_urb failed\n"); + return -ENOMEM; + } + gspca_dev->urb[n] = urb; + urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, + packet_size * SD_NPKT, + GFP_KERNEL, + &urb->transfer_dma); + if (urb->transfer_buffer == NULL) { + pr_err("usb_buffer_alloc failed\n"); + return -ENOMEM; + } + + urb->dev = gspca_dev->dev; + urb->context = gspca_dev; + urb->transfer_buffer_length = packet_size * SD_NPKT; + urb->pipe = usb_rcvisocpipe(gspca_dev->dev, + n & 1 ? 0x81 : 0x82); + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; + urb->interval = 1; + urb->complete = sd_isoc_irq; + urb->number_of_packets = SD_NPKT; + for (i = 0; i < SD_NPKT; i++) { + urb->iso_frame_desc[i].length = packet_size; + urb->iso_frame_desc[i].offset = packet_size * i; + } + } + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + konica_stream_off(gspca_dev); +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + /* Don't keep the button in the pressed state "forever" if it was + pressed when streaming is stopped */ + if (sd->snapshot_pressed) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + sd->snapshot_pressed = 0; + } +#endif +} + +/* reception of an URB */ +static void sd_isoc_irq(struct urb *urb) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + struct sd *sd = (struct sd *) gspca_dev; + struct urb *data_urb, *status_urb; + u8 *data; + int i, st; + + PDEBUG(D_PACK, "sd isoc irq"); + if (!gspca_dev->streaming) + return; + + if (urb->status != 0) { + if (urb->status == -ESHUTDOWN) + return; /* disconnection */ +#ifdef CONFIG_PM + if (gspca_dev->frozen) + return; +#endif + PDEBUG(D_ERR, "urb status: %d", urb->status); + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + pr_err("resubmit urb error %d\n", st); + return; + } + + /* if this is a data URB (ep 0x82), wait */ + if (urb->transfer_buffer_length > 32) { + sd->last_data_urb = urb; + return; + } + + status_urb = urb; + data_urb = sd->last_data_urb; + sd->last_data_urb = NULL; + + if (!data_urb || data_urb->start_frame != status_urb->start_frame) { + PDEBUG(D_ERR|D_PACK, "lost sync on frames"); + goto resubmit; + } + + if (data_urb->number_of_packets != status_urb->number_of_packets) { + PDEBUG(D_ERR|D_PACK, + "no packets does not match, data: %d, status: %d", + data_urb->number_of_packets, + status_urb->number_of_packets); + goto resubmit; + } + + for (i = 0; i < status_urb->number_of_packets; i++) { + if (data_urb->iso_frame_desc[i].status || + status_urb->iso_frame_desc[i].status) { + PDEBUG(D_ERR|D_PACK, + "pkt %d data-status %d, status-status %d", i, + data_urb->iso_frame_desc[i].status, + status_urb->iso_frame_desc[i].status); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + + if (status_urb->iso_frame_desc[i].actual_length != 1) { + PDEBUG(D_ERR|D_PACK, + "bad status packet length %d", + status_urb->iso_frame_desc[i].actual_length); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + + st = *((u8 *)status_urb->transfer_buffer + + status_urb->iso_frame_desc[i].offset); + + data = (u8 *)data_urb->transfer_buffer + + data_urb->iso_frame_desc[i].offset; + + /* st: 0x80-0xff: frame start with frame number (ie 0-7f) + * otherwise: + * bit 0 0: keep packet + * 1: drop packet (padding data) + * + * bit 4 0 button not clicked + * 1 button clicked + * button is used to `take a picture' (in software) + */ + if (st & 0x80) { + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + } else { +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + u8 button_state = st & 0x40 ? 1 : 0; + if (sd->snapshot_pressed != button_state) { + input_report_key(gspca_dev->input_dev, + KEY_CAMERA, + button_state); + input_sync(gspca_dev->input_dev); + sd->snapshot_pressed = button_state; + } +#endif + if (st & 0x01) + continue; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, + data_urb->iso_frame_desc[i].actual_length); + } + +resubmit: + if (data_urb) { + st = usb_submit_urb(data_urb, GFP_ATOMIC); + if (st < 0) + PDEBUG(D_ERR|D_PACK, + "usb_submit_urb(data_urb) ret %d", st); + } + st = usb_submit_urb(status_urb, GFP_ATOMIC); + if (st < 0) + pr_err("usb_submit_urb(status_urb) ret %d\n", st); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + konica_stream_off(gspca_dev); + reg_w(gspca_dev, ctrl->val, BRIGHTNESS_REG); + konica_stream_on(gspca_dev); + break; + case V4L2_CID_CONTRAST: + konica_stream_off(gspca_dev); + reg_w(gspca_dev, ctrl->val, CONTRAST_REG); + konica_stream_on(gspca_dev); + break; + case V4L2_CID_SATURATION: + konica_stream_off(gspca_dev); + reg_w(gspca_dev, ctrl->val, SATURATION_REG); + konica_stream_on(gspca_dev); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + konica_stream_off(gspca_dev); + reg_w(gspca_dev, ctrl->val, WHITEBAL_REG); + konica_stream_on(gspca_dev); + break; + case V4L2_CID_SHARPNESS: + konica_stream_off(gspca_dev); + reg_w(gspca_dev, ctrl->val, SHARPNESS_REG); + konica_stream_on(gspca_dev); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 9, 1, 4); + /* Needs to be verified */ + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 9, 1, 4); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 9, 1, 4); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, 33, 1, 25); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 9, 1, 4); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .other_input = 1, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x04c8, 0x0720)}, /* Intel YC 76 */ + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/m5602/Kconfig b/drivers/media/usb/gspca/m5602/Kconfig new file mode 100644 index 000000000000..5a69016ed75f --- /dev/null +++ b/drivers/media/usb/gspca/m5602/Kconfig @@ -0,0 +1,11 @@ +config USB_M5602 + tristate "ALi USB m5602 Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + ALi m5602 connected to various image sensors. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called gspca_m5602. diff --git a/drivers/media/usb/gspca/m5602/Makefile b/drivers/media/usb/gspca/m5602/Makefile new file mode 100644 index 000000000000..8e1fb5a1d2a1 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_USB_M5602) += gspca_m5602.o + +gspca_m5602-objs := m5602_core.o \ + m5602_ov9650.o \ + m5602_ov7660.o \ + m5602_mt9m111.o \ + m5602_po1030.o \ + m5602_s5k83a.o \ + m5602_s5k4aa.o + +ccflags-y += -I$(srctree)/drivers/media/usb/gspca diff --git a/drivers/media/usb/gspca/m5602/m5602_bridge.h b/drivers/media/usb/gspca/m5602/m5602_bridge.h new file mode 100644 index 000000000000..51af3ee3ab85 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_bridge.h @@ -0,0 +1,163 @@ +/* + * USB Driver for ALi m5602 based webcams + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_BRIDGE_H_ +#define M5602_BRIDGE_H_ + +#include +#include "gspca.h" + +#define MODULE_NAME "ALi m5602" + +/*****************************************************************************/ + +#define M5602_XB_SENSOR_TYPE 0x00 +#define M5602_XB_SENSOR_CTRL 0x01 +#define M5602_XB_LINE_OF_FRAME_H 0x02 +#define M5602_XB_LINE_OF_FRAME_L 0x03 +#define M5602_XB_PIX_OF_LINE_H 0x04 +#define M5602_XB_PIX_OF_LINE_L 0x05 +#define M5602_XB_VSYNC_PARA 0x06 +#define M5602_XB_HSYNC_PARA 0x07 +#define M5602_XB_TEST_MODE_1 0x08 +#define M5602_XB_TEST_MODE_2 0x09 +#define M5602_XB_SIG_INI 0x0a +#define M5602_XB_DS_PARA 0x0e +#define M5602_XB_TRIG_PARA 0x0f +#define M5602_XB_CLK_PD 0x10 +#define M5602_XB_MCU_CLK_CTRL 0x12 +#define M5602_XB_MCU_CLK_DIV 0x13 +#define M5602_XB_SEN_CLK_CTRL 0x14 +#define M5602_XB_SEN_CLK_DIV 0x15 +#define M5602_XB_AUD_CLK_CTRL 0x16 +#define M5602_XB_AUD_CLK_DIV 0x17 +#define M5602_OB_AC_LINK_STATE 0x22 +#define M5602_OB_PCM_SLOT_INDEX 0x24 +#define M5602_OB_GPIO_SLOT_INDEX 0x25 +#define M5602_OB_ACRX_STATUS_ADDRESS_H 0x28 +#define M5602_OB_ACRX_STATUS_DATA_L 0x29 +#define M5602_OB_ACRX_STATUS_DATA_H 0x2a +#define M5602_OB_ACTX_COMMAND_ADDRESS 0x31 +#define M5602_OB_ACRX_COMMAND_DATA_L 0x32 +#define M5602_OB_ACTX_COMMAND_DATA_H 0X33 +#define M5602_XB_DEVCTR1 0x41 +#define M5602_XB_EPSETR0 0x42 +#define M5602_XB_EPAFCTR 0x47 +#define M5602_XB_EPBFCTR 0x49 +#define M5602_XB_EPEFCTR 0x4f +#define M5602_XB_TEST_REG 0x53 +#define M5602_XB_ALT2SIZE 0x54 +#define M5602_XB_ALT3SIZE 0x55 +#define M5602_XB_OBSFRAME 0x56 +#define M5602_XB_PWR_CTL 0x59 +#define M5602_XB_ADC_CTRL 0x60 +#define M5602_XB_ADC_DATA 0x61 +#define M5602_XB_MISC_CTRL 0x62 +#define M5602_XB_SNAPSHOT 0x63 +#define M5602_XB_SCRATCH_1 0x64 +#define M5602_XB_SCRATCH_2 0x65 +#define M5602_XB_SCRATCH_3 0x66 +#define M5602_XB_SCRATCH_4 0x67 +#define M5602_XB_I2C_CTRL 0x68 +#define M5602_XB_I2C_CLK_DIV 0x69 +#define M5602_XB_I2C_DEV_ADDR 0x6a +#define M5602_XB_I2C_REG_ADDR 0x6b +#define M5602_XB_I2C_DATA 0x6c +#define M5602_XB_I2C_STATUS 0x6d +#define M5602_XB_GPIO_DAT_H 0x70 +#define M5602_XB_GPIO_DAT_L 0x71 +#define M5602_XB_GPIO_DIR_H 0x72 +#define M5602_XB_GPIO_DIR_L 0x73 +#define M5602_XB_GPIO_EN_H 0x74 +#define M5602_XB_GPIO_EN_L 0x75 +#define M5602_XB_GPIO_DAT 0x76 +#define M5602_XB_GPIO_DIR 0x77 +#define M5602_XB_SEN_CLK_CONTROL 0x80 +#define M5602_XB_SEN_CLK_DIVISION 0x81 +#define M5602_XB_CPR_CLK_CONTROL 0x82 +#define M5602_XB_CPR_CLK_DIVISION 0x83 +#define M5602_XB_MCU_CLK_CONTROL 0x84 +#define M5602_XB_MCU_CLK_DIVISION 0x85 +#define M5602_XB_DCT_CLK_CONTROL 0x86 +#define M5602_XB_DCT_CLK_DIVISION 0x87 +#define M5602_XB_EC_CLK_CONTROL 0x88 +#define M5602_XB_EC_CLK_DIVISION 0x89 +#define M5602_XB_LBUF_CLK_CONTROL 0x8a +#define M5602_XB_LBUF_CLK_DIVISION 0x8b + +#define I2C_BUSY 0x80 + +/*****************************************************************************/ + +/* Driver info */ +#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project" +#define DRIVER_DESC "ALi m5602 webcam driver" + +#define M5602_ISOC_ENDPOINT_ADDR 0x81 +#define M5602_INTR_ENDPOINT_ADDR 0x82 + +#define M5602_URB_MSG_TIMEOUT 5000 + +/*****************************************************************************/ + +/* A skeleton used for sending messages to the m5602 bridge */ +static const unsigned char bridge_urb_skeleton[] = { + 0x13, 0x00, 0x81, 0x00 +}; + +/* A skeleton used for sending messages to the sensor */ +static const unsigned char sensor_urb_skeleton[] = { + 0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06, + 0x23, M5602_XB_MISC_CTRL, 0x81, 0x80, + 0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00, + 0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00, + 0x13, M5602_XB_I2C_DATA, 0x81, 0x00, + 0x13, M5602_XB_I2C_CTRL, 0x81, 0x11 +}; + +struct sd { + struct gspca_dev gspca_dev; + + /* A pointer to the currently connected sensor */ + const struct m5602_sensor *sensor; + + struct sd_desc *desc; + + /* Sensor private data */ + void *sensor_priv; + + /* The current frame's id, used to detect frame boundaries */ + u8 frame_id; + + /* The current frame count */ + u32 frame_count; +}; + +int m5602_read_bridge( + struct sd *sd, const u8 address, u8 *i2c_data); + +int m5602_write_bridge( + struct sd *sd, const u8 address, const u8 i2c_data); + +int m5602_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +int m5602_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +#endif diff --git a/drivers/media/usb/gspca/m5602/m5602_core.c b/drivers/media/usb/gspca/m5602/m5602_core.c new file mode 100644 index 000000000000..ed22638978ce --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_core.c @@ -0,0 +1,424 @@ + /* + * USB Driver for ALi m5602 based webcams + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "m5602_ov9650.h" +#include "m5602_ov7660.h" +#include "m5602_mt9m111.h" +#include "m5602_po1030.h" +#include "m5602_s5k83a.h" +#include "m5602_s5k4aa.h" + +/* Kernel module parameters */ +int force_sensor; +static bool dump_bridge; +bool dump_sensor; + +static const struct usb_device_id m5602_table[] = { + {USB_DEVICE(0x0402, 0x5602)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, m5602_table); + +/* Reads a byte from the m5602 */ +int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x04, 0xc0, 0x14, + 0x8100 + address, buf, + 1, M5602_URB_MSG_TIMEOUT); + *i2c_data = buf[0]; + + PDEBUG(D_CONF, "Reading bridge register 0x%x containing 0x%x", + address, *i2c_data); + + /* usb_control_msg(...) returns the number of bytes sent upon success, + mask that and return zero instead*/ + return (err < 0) ? err : 0; +} + +/* Writes a byte to the m5602 */ +int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + PDEBUG(D_CONF, "Writing bridge register 0x%x with 0x%x", + address, i2c_data); + + memcpy(buf, bridge_urb_skeleton, + sizeof(bridge_urb_skeleton)); + buf[1] = address; + buf[3] = i2c_data; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 4, M5602_URB_MSG_TIMEOUT); + + /* usb_control_msg(...) returns the number of bytes sent upon success, + mask that and return zero instead */ + return (err < 0) ? err : 0; +} + +static int m5602_wait_for_i2c(struct sd *sd) +{ + int err; + u8 data; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, &data); + } while ((data & I2C_BUSY) && !err); + return err; +} + +int m5602_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + + if (!len || len > sd->sensor->i2c_regW) + return -EINVAL; + + err = m5602_wait_for_i2c(sd); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, + sd->sensor->i2c_slave_id); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); + if (err < 0) + return err; + + /* Sensors with registers that are of only + one byte width are differently read */ + + /* FIXME: This works with the ov9650, but has issues with the po1030 */ + if (sd->sensor->i2c_regW == 1) { + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 1); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); + } else { + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); + } + + for (i = 0; (i < len) && !err; i++) { + err = m5602_wait_for_i2c(sd); + if (err < 0) + return err; + + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); + + PDEBUG(D_CONF, "Reading sensor register " + "0x%x containing 0x%x ", address, *i2c_data); + } + return err; +} + +int m5602_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + u8 *p; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* No sensor with a data width larger than 16 bits has yet been seen */ + if (len > sd->sensor->i2c_regW || !len) + return -EINVAL; + + memcpy(buf, sensor_urb_skeleton, + sizeof(sensor_urb_skeleton)); + + buf[11] = sd->sensor->i2c_slave_id; + buf[15] = address; + + /* Special case larger sensor writes */ + p = buf + 16; + + /* Copy a four byte write sequence for each byte to be written to */ + for (i = 0; i < len; i++) { + memcpy(p, sensor_urb_skeleton + 16, 4); + p[3] = i2c_data[i]; + p += 4; + PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", + address, i2c_data[i]); + } + + /* Copy the tailer */ + memcpy(p, sensor_urb_skeleton + 20, 4); + + /* Set the total length */ + p[3] = 0x10 + len; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 20 + len * 4, M5602_URB_MSG_TIMEOUT); + + return (err < 0) ? err : 0; +} + +/* Dump all the registers of the m5602 bridge, + unfortunately this breaks the camera until it's power cycled */ +static void m5602_dump_bridge(struct sd *sd) +{ + int i; + for (i = 0; i < 0x80; i++) { + unsigned char val = 0; + m5602_read_bridge(sd, i, &val); + pr_info("ALi m5602 address 0x%x contains 0x%x\n", i, val); + } + pr_info("Warning: The ALi m5602 webcam probably won't work until it's power cycled\n"); +} + +static int m5602_probe_sensor(struct sd *sd) +{ + /* Try the po1030 */ + sd->sensor = &po1030; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the mt9m111 sensor */ + sd->sensor = &mt9m111; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the s5k4aa */ + sd->sensor = &s5k4aa; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the ov9650 */ + sd->sensor = &ov9650; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the ov7660 */ + sd->sensor = &ov7660; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the s5k83a */ + sd->sensor = &s5k83a; + if (!sd->sensor->probe(sd)) + return 0; + + /* More sensor probe function goes here */ + pr_info("Failed to find a sensor\n"); + sd->sensor = NULL; + return -ENODEV; +} + +static int m5602_configure(struct gspca_dev *gspca_dev, + const struct usb_device_id *id); + +static int m5602_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + + PDEBUG(D_CONF, "Initializing ALi m5602 webcam"); + /* Run the init sequence */ + err = sd->sensor->init(sd); + + return err; +} + +static int m5602_start_transfer(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *buf = sd->gspca_dev.usb_buf; + int err; + + /* Send start command to the camera */ + const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01}; + + if (sd->sensor->start) + sd->sensor->start(sd); + + memcpy(buf, buffer, sizeof(buffer)); + err = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x04, 0x40, 0x19, 0x0000, buf, + sizeof(buffer), M5602_URB_MSG_TIMEOUT); + + PDEBUG(D_STREAM, "Transfer started"); + return (err < 0) ? err : 0; +} + +static void m5602_urb_complete(struct gspca_dev *gspca_dev, + u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (len < 6) { + PDEBUG(D_PACK, "Packet is less than 6 bytes"); + return; + } + + /* Frame delimiter: ff xx xx xx ff ff */ + if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff && + data[2] != sd->frame_id) { + PDEBUG(D_FRAM, "Frame delimiter detected"); + sd->frame_id = data[2]; + + /* Remove the extra fluff appended on each header */ + data += 6; + len -= 6; + + /* Complete the last frame (if any) */ + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + sd->frame_count++; + + /* Create a new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + + PDEBUG(D_FRAM, "Starting new frame %d", + sd->frame_count); + + } else { + int cur_frame_len; + + cur_frame_len = gspca_dev->image_len; + /* Remove urb header */ + data += 4; + len -= 4; + + if (cur_frame_len + len <= gspca_dev->frsz) { + PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes", + sd->frame_count, len); + + gspca_frame_add(gspca_dev, INTER_PACKET, + data, len); + } else { + /* Add the remaining data up to frame size */ + gspca_frame_add(gspca_dev, INTER_PACKET, data, + gspca_dev->frsz - cur_frame_len); + } + } +} + +static void m5602_stop_transfer(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Run the sensor specific end transfer sequence */ + if (sd->sensor->stop) + sd->sensor->stop(sd); +} + +/* sub-driver description, the ctrl and nctrl is filled at probe time */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = m5602_configure, + .init = m5602_init, + .start = m5602_start_transfer, + .stopN = m5602_stop_transfer, + .pkt_scan = m5602_urb_complete +}; + +/* this function is called at probe time */ +static int m5602_configure(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int err; + + cam = &gspca_dev->cam; + sd->desc = &sd_desc; + + if (dump_bridge) + m5602_dump_bridge(sd); + + /* Probe sensor */ + err = m5602_probe_sensor(sd); + if (err) + goto fail; + + return 0; + +fail: + PDEBUG(D_ERR, "ALi m5602 webcam failed"); + cam->cam_mode = NULL; + cam->nmodes = 0; + + return err; +} + +static int m5602_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static void m5602_disconnect(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor->disconnect) + sd->sensor->disconnect(sd); + + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = m5602_table, + .probe = m5602_probe, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif + .disconnect = m5602_disconnect +}; + +module_usb_driver(sd_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +module_param(force_sensor, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(force_sensor, + "forces detection of a sensor, " + "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, " + "4 = MT9M111, 5 = PO1030, 6 = OV7660"); + +module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); + +module_param(dump_sensor, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers " + "at startup providing a sensor is found"); diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c new file mode 100644 index 000000000000..6268aa24ec5d --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c @@ -0,0 +1,647 @@ +/* + * Driver for the mt9m111 sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "m5602_mt9m111.h" + +static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); + +static struct v4l2_pix_format mt9m111_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static const struct ctrl mt9m111_ctrls[] = { +#define VFLIP_IDX 0 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = mt9m111_set_vflip, + .get = mt9m111_get_vflip + }, +#define HFLIP_IDX 1 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = mt9m111_set_hflip, + .get = mt9m111_get_hflip + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0, + .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, + .step = 1, + .default_value = MT9M111_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_gain, + .get = mt9m111_get_gain + }, +#define AUTO_WHITE_BALANCE_IDX 3 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = mt9m111_set_auto_white_balance, + .get = mt9m111_get_auto_white_balance + }, +#define GREEN_BALANCE_IDX 4 + { + { + .id = M5602_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7ff, + .step = 0x1, + .default_value = MT9M111_GREEN_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_green_balance, + .get = mt9m111_get_green_balance + }, +#define BLUE_BALANCE_IDX 5 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7ff, + .step = 0x1, + .default_value = MT9M111_BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_blue_balance, + .get = mt9m111_get_blue_balance + }, +#define RED_BALANCE_IDX 5 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7ff, + .step = 0x1, + .default_value = MT9M111_RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_red_balance, + .get = mt9m111_get_red_balance + }, +}; + +static void mt9m111_dump_registers(struct sd *sd); + +int mt9m111_probe(struct sd *sd) +{ + u8 data[2] = {0x00, 0x00}; + int i; + s32 *sensor_settings; + + if (force_sensor) { + if (force_sensor == MT9M111_SENSOR) { + pr_info("Forcing a %s sensor\n", mt9m111.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + PDEBUG(D_PROBE, "Probing for a mt9m111 sensor"); + + /* Do the preinit */ + for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) { + if (preinit_mt9m111[i][0] == BRIDGE) { + m5602_write_bridge(sd, + preinit_mt9m111[i][1], + preinit_mt9m111[i][2]); + } else { + data[0] = preinit_mt9m111[i][2]; + data[1] = preinit_mt9m111[i][3]; + m5602_write_sensor(sd, + preinit_mt9m111[i][1], data, 2); + } + } + + if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2)) + return -ENODEV; + + if ((data[0] == 0x14) && (data[1] == 0x3a)) { + pr_info("Detected a mt9m111 sensor\n"); + goto sensor_found; + } + + return -ENODEV; + +sensor_found: + sensor_settings = kmalloc(ARRAY_SIZE(mt9m111_ctrls) * sizeof(s32), + GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = mt9m111_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes); + sd->desc->ctrls = mt9m111_ctrls; + sd->desc->nctrls = ARRAY_SIZE(mt9m111_ctrls); + + for (i = 0; i < ARRAY_SIZE(mt9m111_ctrls); i++) + sensor_settings[i] = mt9m111_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + + return 0; +} + +int mt9m111_init(struct sd *sd) +{ + int i, err = 0; + s32 *sensor_settings = sd->sensor_priv; + + /* Init the sensor */ + for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) { + u8 data[2]; + + if (init_mt9m111[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + init_mt9m111[i][1], + init_mt9m111[i][2]); + } else { + data[0] = init_mt9m111[i][2]; + data[1] = init_mt9m111[i][3]; + err = m5602_write_sensor(sd, + init_mt9m111[i][1], data, 2); + } + } + + if (dump_sensor) + mt9m111_dump_registers(sd); + + err = mt9m111_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_green_balance(&sd->gspca_dev, + sensor_settings[GREEN_BALANCE_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_blue_balance(&sd->gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_red_balance(&sd->gspca_dev, + sensor_settings[RED_BALANCE_IDX]); + if (err < 0) + return err; + + return mt9m111_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); +} + +int mt9m111_start(struct sd *sd) +{ + int i, err = 0; + u8 data[2]; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + + int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1; + int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; + + for (i = 0; i < ARRAY_SIZE(start_mt9m111) && !err; i++) { + if (start_mt9m111[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + start_mt9m111[i][1], + start_mt9m111[i][2]); + } else { + data[0] = start_mt9m111[i][2]; + data[1] = start_mt9m111[i][3]; + err = m5602_write_sensor(sd, + start_mt9m111[i][1], data, 2); + } + } + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + (width >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, width & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + switch (width) { + case 640: + PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + data[0] = MT9M111_RMB_OVER_SIZED; + data[1] = MT9M111_RMB_ROW_SKIP_2X | + MT9M111_RMB_COLUMN_SKIP_2X | + (sensor_settings[VFLIP_IDX] << 0) | + (sensor_settings[HFLIP_IDX] << 1); + + err = m5602_write_sensor(sd, + MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + break; + + case 320: + PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); + data[0] = MT9M111_RMB_OVER_SIZED; + data[1] = MT9M111_RMB_ROW_SKIP_4X | + MT9M111_RMB_COLUMN_SKIP_4X | + (sensor_settings[VFLIP_IDX] << 0) | + (sensor_settings[HFLIP_IDX] << 1); + err = m5602_write_sensor(sd, + MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + break; + } + return err; +} + +void mt9m111_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[VFLIP_IDX]; + PDEBUG(D_V4L2, "Read vertical flip %d", *val); + + return 0; +} + +static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set vertical flip to %d", val); + + sensor_settings[VFLIP_IDX] = val; + + /* The mt9m111 is flipped by default */ + val = !val; + + /* Set the correct page map */ + err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + if (err < 0) + return err; + + err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + if (err < 0) + return err; + + data[1] = (data[1] & 0xfe) | val; + err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + data, 2); + return err; +} + +static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[HFLIP_IDX]; + PDEBUG(D_V4L2, "Read horizontal flip %d", *val); + + return 0; +} + +static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + + sensor_settings[HFLIP_IDX] = val; + + /* The mt9m111 is flipped by default */ + val = !val; + + /* Set the correct page map */ + err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + if (err < 0) + return err; + + err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + if (err < 0) + return err; + + data[1] = (data[1] & 0xfd) | ((val << 1) & 0x02); + err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + data, 2); + return err; +} + +static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read gain %d", *val); + + return 0; +} + +static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + int err; + u8 data[2]; + + err = m5602_read_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2); + if (err < 0) + return err; + + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val & 0x01; + data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1)); + + err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2); + + PDEBUG(D_V4L2, "Set auto white balance %d", val); + return err; +} + +static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) { + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read auto white balance %d", *val); + return 0; +} + +static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err, tmp; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[GAIN_IDX] = val; + + /* Set the correct page map */ + err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + if (err < 0) + return err; + + if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2) + return -EINVAL; + + if ((val >= INITIAL_MAX_GAIN * 2 * 2) && + (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2)) + tmp = (1 << 10) | (val << 9) | + (val << 8) | (val / 8); + else if ((val >= INITIAL_MAX_GAIN * 2) && + (val < INITIAL_MAX_GAIN * 2 * 2)) + tmp = (1 << 9) | (1 << 8) | (val / 4); + else if ((val >= INITIAL_MAX_GAIN) && + (val < INITIAL_MAX_GAIN * 2)) + tmp = (1 << 8) | (val / 2); + else + tmp = val; + + data[1] = (tmp & 0xff); + data[0] = (tmp & 0xff00) >> 8; + PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp, + data[1], data[0]); + + err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN, + data, 2); + + return err; +} + +static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[GREEN_BALANCE_IDX] = val; + data[1] = (val & 0xff); + data[0] = (val & 0xff00) >> 8; + + PDEBUG(D_V4L2, "Set green balance %d", val); + err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN, + data, 2); + if (err < 0) + return err; + + return m5602_write_sensor(sd, MT9M111_SC_GREEN_2_GAIN, + data, 2); +} + +static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GREEN_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read green balance %d", *val); + return 0; +} + +static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[BLUE_BALANCE_IDX] = val; + data[1] = (val & 0xff); + data[0] = (val & 0xff00) >> 8; + + PDEBUG(D_V4L2, "Set blue balance %d", val); + + return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN, + data, 2); +} + +static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BLUE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read blue balance %d", *val); + return 0; +} + +static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[RED_BALANCE_IDX] = val; + data[1] = (val & 0xff); + data[0] = (val & 0xff00) >> 8; + + PDEBUG(D_V4L2, "Set red balance %d", val); + + return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN, + data, 2); +} + +static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[RED_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read red balance %d", *val); + return 0; +} + +static void mt9m111_dump_registers(struct sd *sd) +{ + u8 address, value[2] = {0x00, 0x00}; + + pr_info("Dumping the mt9m111 register state\n"); + + pr_info("Dumping the mt9m111 sensor core registers\n"); + value[1] = MT9M111_SENSOR_CORE; + m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + for (address = 0; address < 0xff; address++) { + m5602_read_sensor(sd, address, value, 2); + pr_info("register 0x%x contains 0x%x%x\n", + address, value[0], value[1]); + } + + pr_info("Dumping the mt9m111 color pipeline registers\n"); + value[1] = MT9M111_COLORPIPE; + m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + for (address = 0; address < 0xff; address++) { + m5602_read_sensor(sd, address, value, 2); + pr_info("register 0x%x contains 0x%x%x\n", + address, value[0], value[1]); + } + + pr_info("Dumping the mt9m111 camera control registers\n"); + value[1] = MT9M111_CAMERA_CONTROL; + m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + for (address = 0; address < 0xff; address++) { + m5602_read_sensor(sd, address, value, 2); + pr_info("register 0x%x contains 0x%x%x\n", + address, value[0], value[1]); + } + + pr_info("mt9m111 register state dump complete\n"); +} diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.h b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h new file mode 100644 index 000000000000..8c672b5c8c6a --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h @@ -0,0 +1,271 @@ +/* + * Driver for the mt9m111 sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * Some defines taken from the mt9m111 sensor driver + * Copyright (C) 2008, Robert Jarzmik + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_MT9M111_H_ +#define M5602_MT9M111_H_ + +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define MT9M111_SC_CHIPVER 0x00 +#define MT9M111_SC_ROWSTART 0x01 +#define MT9M111_SC_COLSTART 0x02 +#define MT9M111_SC_WINDOW_HEIGHT 0x03 +#define MT9M111_SC_WINDOW_WIDTH 0x04 +#define MT9M111_SC_HBLANK_CONTEXT_B 0x05 +#define MT9M111_SC_VBLANK_CONTEXT_B 0x06 +#define MT9M111_SC_HBLANK_CONTEXT_A 0x07 +#define MT9M111_SC_VBLANK_CONTEXT_A 0x08 +#define MT9M111_SC_SHUTTER_WIDTH 0x09 +#define MT9M111_SC_ROW_SPEED 0x0a +#define MT9M111_SC_EXTRA_DELAY 0x0b +#define MT9M111_SC_SHUTTER_DELAY 0x0c +#define MT9M111_SC_RESET 0x0d +#define MT9M111_SC_R_MODE_CONTEXT_B 0x20 +#define MT9M111_SC_R_MODE_CONTEXT_A 0x21 +#define MT9M111_SC_FLASH_CONTROL 0x23 +#define MT9M111_SC_GREEN_1_GAIN 0x2b +#define MT9M111_SC_BLUE_GAIN 0x2c +#define MT9M111_SC_RED_GAIN 0x2d +#define MT9M111_SC_GREEN_2_GAIN 0x2e +#define MT9M111_SC_GLOBAL_GAIN 0x2f + +#define MT9M111_CONTEXT_CONTROL 0xc8 +#define MT9M111_PAGE_MAP 0xf0 +#define MT9M111_BYTEWISE_ADDRESS 0xf1 + +#define MT9M111_CP_OPERATING_MODE_CTL 0x06 +#define MT9M111_CP_LUMA_OFFSET 0x34 +#define MT9M111_CP_LUMA_CLIP 0x35 +#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a +#define MT9M111_CP_LENS_CORRECTION_1 0x3b +#define MT9M111_CP_DEFECT_CORR_CONTEXT_A 0x4c +#define MT9M111_CP_DEFECT_CORR_CONTEXT_B 0x4d +#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b +#define MT9M111_CP_GLOBAL_CLK_CONTROL 0xb3 + +#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18 0x65 +#define MT9M111_CC_AWB_PARAMETER_7 0x28 + +#define MT9M111_SENSOR_CORE 0x00 +#define MT9M111_COLORPIPE 0x01 +#define MT9M111_CAMERA_CONTROL 0x02 + +#define MT9M111_RESET (1 << 0) +#define MT9M111_RESTART (1 << 1) +#define MT9M111_ANALOG_STANDBY (1 << 2) +#define MT9M111_CHIP_ENABLE (1 << 3) +#define MT9M111_CHIP_DISABLE (0 << 3) +#define MT9M111_OUTPUT_DISABLE (1 << 4) +#define MT9M111_SHOW_BAD_FRAMES (1 << 0) +#define MT9M111_RESTART_BAD_FRAMES (1 << 1) +#define MT9M111_SYNCHRONIZE_CHANGES (1 << 7) + +#define MT9M111_RMB_OVER_SIZED (1 << 0) +#define MT9M111_RMB_MIRROR_ROWS (1 << 0) +#define MT9M111_RMB_MIRROR_COLS (1 << 1) +#define MT9M111_RMB_ROW_SKIP_2X (1 << 2) +#define MT9M111_RMB_COLUMN_SKIP_2X (1 << 3) +#define MT9M111_RMB_ROW_SKIP_4X (1 << 4) +#define MT9M111_RMB_COLUMN_SKIP_4X (1 << 5) + +#define MT9M111_COLOR_MATRIX_BYPASS (1 << 4) +#define MT9M111_SEL_CONTEXT_B (1 << 3) + +#define MT9M111_TRISTATE_PIN_IN_STANDBY (1 << 1) +#define MT9M111_SOC_SOFT_STANDBY (1 << 0) + +#define MT9M111_2D_DEFECT_CORRECTION_ENABLE (1 << 0) + +#define INITIAL_MAX_GAIN 64 +#define MT9M111_DEFAULT_GAIN 283 +#define MT9M111_GREEN_GAIN_DEFAULT 0x20 +#define MT9M111_BLUE_GAIN_DEFAULT 0x20 +#define MT9M111_RED_GAIN_DEFAULT 0x20 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern bool dump_sensor; + +int mt9m111_probe(struct sd *sd); +int mt9m111_init(struct sd *sd); +int mt9m111_start(struct sd *sd); +void mt9m111_disconnect(struct sd *sd); + +static const struct m5602_sensor mt9m111 = { + .name = "MT9M111", + + .i2c_slave_id = 0xba, + .i2c_regW = 2, + + .probe = mt9m111_probe, + .init = mt9m111_init, + .disconnect = mt9m111_disconnect, + .start = mt9m111_start, +}; + +static const unsigned char preinit_mt9m111[][4] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, + MT9M111_RESET | + MT9M111_RESTART | + MT9M111_ANALOG_STANDBY | + MT9M111_CHIP_DISABLE, + MT9M111_SHOW_BAD_FRAMES | + MT9M111_RESTART_BAD_FRAMES | + MT9M111_SYNCHRONIZE_CHANGES}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00} +}; + +static const unsigned char init_mt9m111[][4] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, + + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, + MT9M111_CP_OPERATING_MODE_CTL}, + {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, + MT9M111_2D_DEFECT_CORRECTION_ENABLE}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, + MT9M111_2D_DEFECT_CORRECTION_ENABLE}, + {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, + {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, + {SENSOR, 0xcd, 0x00, 0x0e}, + {SENSOR, 0xd0, 0x00, 0x40}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, + {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, 0x33, 0x03, 0x49}, + {SENSOR, 0x34, 0xc0, 0x19}, + {SENSOR, 0x3f, 0x20, 0x20}, + {SENSOR, 0x40, 0x20, 0x20}, + {SENSOR, 0x5a, 0xc0, 0x0a}, + {SENSOR, 0x70, 0x7b, 0x0a}, + {SENSOR, 0x71, 0xff, 0x00}, + {SENSOR, 0x72, 0x19, 0x0e}, + {SENSOR, 0x73, 0x18, 0x0f}, + {SENSOR, 0x74, 0x57, 0x32}, + {SENSOR, 0x75, 0x56, 0x34}, + {SENSOR, 0x76, 0x73, 0x35}, + {SENSOR, 0x77, 0x30, 0x12}, + {SENSOR, 0x78, 0x79, 0x02}, + {SENSOR, 0x79, 0x75, 0x06}, + {SENSOR, 0x7a, 0x77, 0x0a}, + {SENSOR, 0x7b, 0x78, 0x09}, + {SENSOR, 0x7c, 0x7d, 0x06}, + {SENSOR, 0x7d, 0x31, 0x10}, + {SENSOR, 0x7e, 0x00, 0x7e}, + {SENSOR, 0x80, 0x59, 0x04}, + {SENSOR, 0x81, 0x59, 0x04}, + {SENSOR, 0x82, 0x57, 0x0a}, + {SENSOR, 0x83, 0x58, 0x0b}, + {SENSOR, 0x84, 0x47, 0x0c}, + {SENSOR, 0x85, 0x48, 0x0e}, + {SENSOR, 0x86, 0x5b, 0x02}, + {SENSOR, 0x87, 0x00, 0x5c}, + {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B}, + {SENSOR, 0x60, 0x00, 0x80}, + {SENSOR, 0x61, 0x00, 0x00}, + {SENSOR, 0x62, 0x00, 0x00}, + {SENSOR, 0x63, 0x00, 0x00}, + {SENSOR, 0x64, 0x00, 0x00}, + + {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */ + {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */ + {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */ + {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */ + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */ + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */ + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */ + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */ + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */ + {SENSOR, 0x30, 0x04, 0x00}, + /* Set number of blank rows chosen to 400 */ + {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90}, +}; + +static const unsigned char start_mt9m111[][4] = { + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, +}; +#endif diff --git a/drivers/media/usb/gspca/m5602/m5602_ov7660.c b/drivers/media/usb/gspca/m5602/m5602_ov7660.c new file mode 100644 index 000000000000..9a14835c128f --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.c @@ -0,0 +1,488 @@ +/* + * Driver for the ov7660 sensor + * + * Copyright (C) 2009 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "m5602_ov7660.h" + +static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val); + +static const struct ctrl ov7660_ctrls[] = { +#define GAIN_IDX 1 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = OV7660_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov7660_set_gain, + .get = ov7660_get_gain + }, +#define BLUE_BALANCE_IDX 2 +#define RED_BALANCE_IDX 3 +#define AUTO_WHITE_BALANCE_IDX 4 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov7660_set_auto_white_balance, + .get = ov7660_get_auto_white_balance + }, +#define AUTO_GAIN_CTRL_IDX 5 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto gain control", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov7660_set_auto_gain, + .get = ov7660_get_auto_gain + }, +#define AUTO_EXPOSURE_IDX 6 + { + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov7660_set_auto_exposure, + .get = ov7660_get_auto_exposure + }, +#define HFLIP_IDX 7 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov7660_set_hflip, + .get = ov7660_get_hflip + }, +#define VFLIP_IDX 8 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov7660_set_vflip, + .get = ov7660_get_vflip + }, + +}; + +static struct v4l2_pix_format ov7660_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static void ov7660_dump_registers(struct sd *sd); + +int ov7660_probe(struct sd *sd) +{ + int err = 0, i; + u8 prod_id = 0, ver_id = 0; + + s32 *sensor_settings; + + if (force_sensor) { + if (force_sensor == OV7660_SENSOR) { + pr_info("Forcing an %s sensor\n", ov7660.name); + goto sensor_found; + } + /* If we want to force another sensor, + don't try to probe this one */ + return -ENODEV; + } + + /* Do the preinit */ + for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) { + u8 data[2]; + + if (preinit_ov7660[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + preinit_ov7660[i][1], + preinit_ov7660[i][2]); + } else { + data[0] = preinit_ov7660[i][2]; + err = m5602_write_sensor(sd, + preinit_ov7660[i][1], data, 1); + } + } + if (err < 0) + return err; + + if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1)) + return -ENODEV; + + if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1)) + return -ENODEV; + + pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id); + + if ((prod_id == 0x76) && (ver_id == 0x60)) { + pr_info("Detected a ov7660 sensor\n"); + goto sensor_found; + } + return -ENODEV; + +sensor_found: + sensor_settings = kmalloc( + ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = ov7660_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes); + sd->desc->ctrls = ov7660_ctrls; + sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls); + + for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++) + sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + + return 0; +} + +int ov7660_init(struct sd *sd) +{ + int i, err = 0; + s32 *sensor_settings = sd->sensor_priv; + + /* Init the sensor */ + for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) { + u8 data[2]; + + if (init_ov7660[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + init_ov7660[i][1], + init_ov7660[i][2]); + } else { + data[0] = init_ov7660[i][2]; + err = m5602_write_sensor(sd, + init_ov7660[i][1], data, 1); + } + } + + if (dump_sensor) + ov7660_dump_registers(sd); + + err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = ov7660_set_auto_white_balance(&sd->gspca_dev, + sensor_settings[AUTO_WHITE_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov7660_set_auto_gain(&sd->gspca_dev, + sensor_settings[AUTO_GAIN_CTRL_IDX]); + if (err < 0) + return err; + + err = ov7660_set_auto_exposure(&sd->gspca_dev, + sensor_settings[AUTO_EXPOSURE_IDX]); + if (err < 0) + return err; + err = ov7660_set_hflip(&sd->gspca_dev, + sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = ov7660_set_vflip(&sd->gspca_dev, + sensor_settings[VFLIP_IDX]); + + return err; +} + +int ov7660_start(struct sd *sd) +{ + return 0; +} + +int ov7660_stop(struct sd *sd) +{ + return 0; +} + +void ov7660_disconnect(struct sd *sd) +{ + ov7660_stop(sd); + + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read gain %d", *val); + return 0; +} + +static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Setting gain to %d", val); + + sensor_settings[GAIN_IDX] = val; + + err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1); + return err; +} + + +static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + return 0; +} + +static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto white balance to %d", val); + + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; + err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); + err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); + + return err; +} + +static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; + PDEBUG(D_V4L2, "Read auto gain control %d", *val); + return 0; +} + +static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto gain control to %d", val); + + sensor_settings[AUTO_GAIN_CTRL_IDX] = val; + err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); + + return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); +} + +static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Read auto exposure control %d", *val); + return 0; +} + +static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto exposure control to %d", val); + + sensor_settings[AUTO_EXPOSURE_IDX] = val; + err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); + + return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); +} + +static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[HFLIP_IDX]; + PDEBUG(D_V4L2, "Read horizontal flip %d", *val); + return 0; +} + +static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + + sensor_settings[HFLIP_IDX] = val; + + i2c_data = ((val & 0x01) << 5) | + (sensor_settings[VFLIP_IDX] << 4); + + err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); + + return err; +} + +static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[VFLIP_IDX]; + PDEBUG(D_V4L2, "Read vertical flip %d", *val); + + return 0; +} + +static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set vertical flip to %d", val); + sensor_settings[VFLIP_IDX] = val; + + i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); + err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); + if (err < 0) + return err; + + /* When vflip is toggled we need to readjust the bridge hsync/vsync */ + if (gspca_dev->streaming) + err = ov7660_start(sd); + + return err; +} + +static void ov7660_dump_registers(struct sd *sd) +{ + int address; + pr_info("Dumping the ov7660 register state\n"); + for (address = 0; address < 0xa9; address++) { + u8 value; + m5602_read_sensor(sd, address, &value, 1); + pr_info("register 0x%x contains 0x%x\n", address, value); + } + + pr_info("ov7660 register state dump complete\n"); + + pr_info("Probing for which registers that are read/write\n"); + for (address = 0; address < 0xff; address++) { + u8 old_value, ctrl_value; + u8 test_value[2] = {0xff, 0xff}; + + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value[0]) + pr_info("register 0x%x is writeable\n", address); + else + pr_info("register 0x%x is read only\n", address); + + /* Restore original value */ + m5602_write_sensor(sd, address, &old_value, 1); + } +} diff --git a/drivers/media/usb/gspca/m5602/m5602_ov7660.h b/drivers/media/usb/gspca/m5602/m5602_ov7660.h new file mode 100644 index 000000000000..2b6a13b508f7 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.h @@ -0,0 +1,260 @@ +/* + * Driver for the ov7660 sensor + * + * Copyright (C) 2009 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_OV7660_H_ +#define M5602_OV7660_H_ + +#include "m5602_sensor.h" + +#define OV7660_GAIN 0x00 +#define OV7660_BLUE_GAIN 0x01 +#define OV7660_RED_GAIN 0x02 +#define OV7660_VREF 0x03 +#define OV7660_COM1 0x04 +#define OV7660_BAVE 0x05 +#define OV7660_GEAVE 0x06 +#define OV7660_AECHH 0x07 +#define OV7660_RAVE 0x08 +#define OV7660_COM2 0x09 +#define OV7660_PID 0x0a +#define OV7660_VER 0x0b +#define OV7660_COM3 0x0c +#define OV7660_COM4 0x0d +#define OV7660_COM5 0x0e +#define OV7660_COM6 0x0f +#define OV7660_AECH 0x10 +#define OV7660_CLKRC 0x11 +#define OV7660_COM7 0x12 +#define OV7660_COM8 0x13 +#define OV7660_COM9 0x14 +#define OV7660_COM10 0x15 +#define OV7660_RSVD16 0x16 +#define OV7660_HSTART 0x17 +#define OV7660_HSTOP 0x18 +#define OV7660_VSTART 0x19 +#define OV7660_VSTOP 0x1a +#define OV7660_PSHFT 0x1b +#define OV7660_MIDH 0x1c +#define OV7660_MIDL 0x1d +#define OV7660_MVFP 0x1e +#define OV7660_LAEC 0x1f +#define OV7660_BOS 0x20 +#define OV7660_GBOS 0x21 +#define OV7660_GROS 0x22 +#define OV7660_ROS 0x23 +#define OV7660_AEW 0x24 +#define OV7660_AEB 0x25 +#define OV7660_VPT 0x26 +#define OV7660_BBIAS 0x27 +#define OV7660_GbBIAS 0x28 +#define OV7660_RSVD29 0x29 +#define OV7660_RBIAS 0x2c +#define OV7660_HREF 0x32 +#define OV7660_ADC 0x37 +#define OV7660_OFON 0x39 +#define OV7660_TSLB 0x3a +#define OV7660_COM12 0x3c +#define OV7660_COM13 0x3d +#define OV7660_LCC1 0x62 +#define OV7660_LCC2 0x63 +#define OV7660_LCC3 0x64 +#define OV7660_LCC4 0x65 +#define OV7660_LCC5 0x66 +#define OV7660_HV 0x69 +#define OV7660_RSVDA1 0xa1 + +#define OV7660_DEFAULT_GAIN 0x0e +#define OV7660_DEFAULT_RED_GAIN 0x80 +#define OV7660_DEFAULT_BLUE_GAIN 0x80 +#define OV7660_DEFAULT_SATURATION 0x00 +#define OV7660_DEFAULT_EXPOSURE 0x20 + +/* Kernel module parameters */ +extern int force_sensor; +extern bool dump_sensor; + +int ov7660_probe(struct sd *sd); +int ov7660_init(struct sd *sd); +int ov7660_start(struct sd *sd); +int ov7660_stop(struct sd *sd); +void ov7660_disconnect(struct sd *sd); + +static const struct m5602_sensor ov7660 = { + .name = "ov7660", + .i2c_slave_id = 0x42, + .i2c_regW = 1, + .probe = ov7660_probe, + .init = ov7660_init, + .start = ov7660_start, + .stop = ov7660_stop, + .disconnect = ov7660_disconnect, +}; + +static const unsigned char preinit_ov7660[][4] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00} +}; + +static const unsigned char init_ov7660[][4] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + {SENSOR, OV7660_COM7, 0x80}, + {SENSOR, OV7660_CLKRC, 0x80}, + {SENSOR, OV7660_COM9, 0x4c}, + {SENSOR, OV7660_OFON, 0x43}, + {SENSOR, OV7660_COM12, 0x28}, + {SENSOR, OV7660_COM8, 0x00}, + {SENSOR, OV7660_COM10, 0x40}, + {SENSOR, OV7660_HSTART, 0x0c}, + {SENSOR, OV7660_HSTOP, 0x61}, + {SENSOR, OV7660_HREF, 0xa4}, + {SENSOR, OV7660_PSHFT, 0x0b}, + {SENSOR, OV7660_VSTART, 0x01}, + {SENSOR, OV7660_VSTOP, 0x7a}, + {SENSOR, OV7660_VSTOP, 0x00}, + {SENSOR, OV7660_COM7, 0x05}, + {SENSOR, OV7660_COM6, 0x42}, + {SENSOR, OV7660_BBIAS, 0x94}, + {SENSOR, OV7660_GbBIAS, 0x94}, + {SENSOR, OV7660_RSVD29, 0x94}, + {SENSOR, OV7660_RBIAS, 0x94}, + {SENSOR, OV7660_COM1, 0x00}, + {SENSOR, OV7660_AECH, 0x00}, + {SENSOR, OV7660_AECHH, 0x00}, + {SENSOR, OV7660_ADC, 0x05}, + {SENSOR, OV7660_COM13, 0x00}, + {SENSOR, OV7660_RSVDA1, 0x23}, + {SENSOR, OV7660_TSLB, 0x0d}, + {SENSOR, OV7660_HV, 0x80}, + {SENSOR, OV7660_LCC1, 0x00}, + {SENSOR, OV7660_LCC2, 0x00}, + {SENSOR, OV7660_LCC3, 0x10}, + {SENSOR, OV7660_LCC4, 0x40}, + {SENSOR, OV7660_LCC5, 0x01}, + + {SENSOR, OV7660_AECH, 0x20}, + {SENSOR, OV7660_COM1, 0x00}, + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + {SENSOR, OV7660_AECH, 0x5f}, + {SENSOR, OV7660_COM1, 0x03}, + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_SIG_INI, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x08}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x27}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0xa7}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, +}; +#endif diff --git a/drivers/media/usb/gspca/m5602/m5602_ov9650.c b/drivers/media/usb/gspca/m5602/m5602_ov9650.c new file mode 100644 index 000000000000..2114a8b90ec9 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.c @@ -0,0 +1,881 @@ +/* + * Driver for the ov9650 sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "m5602_ov9650.h" + +static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); + +/* Vertically and horizontally flips the image if matched, needed for machines + where the sensor is mounted upside down */ +static + const + struct dmi_system_id ov9650_flip_dmi_table[] = { + { + .ident = "ASUS A6Ja", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6J") + } + }, + { + .ident = "ASUS A6JC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") + } + }, + { + .ident = "ASUS A6K", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6K") + } + }, + { + .ident = "ASUS A6Kt", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") + } + }, + { + .ident = "ASUS A6VA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VA") + } + }, + { + + .ident = "ASUS A6VC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") + } + }, + { + .ident = "ASUS A6VM", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") + } + }, + { + .ident = "ASUS A7V", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A7V") + } + }, + { + .ident = "Alienware Aurora m9700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700") + } + }, + {} +}; + +static const struct ctrl ov9650_ctrls[] = { +#define EXPOSURE_IDX 0 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x1ff, + .step = 0x4, + .default_value = EXPOSURE_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_exposure, + .get = ov9650_get_exposure + }, +#define GAIN_IDX 1 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0x3ff, + .step = 0x1, + .default_value = GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_gain, + .get = ov9650_get_gain + }, +#define RED_BALANCE_IDX 2 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_red_balance, + .get = ov9650_get_red_balance + }, +#define BLUE_BALANCE_IDX 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_blue_balance, + .get = ov9650_get_blue_balance + }, +#define HFLIP_IDX 4 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_hflip, + .get = ov9650_get_hflip + }, +#define VFLIP_IDX 5 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_vflip, + .get = ov9650_get_vflip + }, +#define AUTO_WHITE_BALANCE_IDX 6 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov9650_set_auto_white_balance, + .get = ov9650_get_auto_white_balance + }, +#define AUTO_GAIN_CTRL_IDX 7 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto gain control", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov9650_set_auto_gain, + .get = ov9650_get_auto_gain + }, +#define AUTO_EXPOSURE_IDX 8 + { + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov9650_set_auto_exposure, + .get = ov9650_get_auto_exposure + } + +}; + +static struct v4l2_pix_format ov9650_modes[] = { + { + 176, + 144, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 176 * 144, + .bytesperline = 176, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 9 + }, { + 320, + 240, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 8 + }, { + 352, + 288, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 352 * 288, + .bytesperline = 352, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 9 + }, { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 9 + } +}; + +static void ov9650_dump_registers(struct sd *sd); + +int ov9650_probe(struct sd *sd) +{ + int err = 0; + u8 prod_id = 0, ver_id = 0, i; + s32 *sensor_settings; + + if (force_sensor) { + if (force_sensor == OV9650_SENSOR) { + pr_info("Forcing an %s sensor\n", ov9650.name); + goto sensor_found; + } + /* If we want to force another sensor, + don't try to probe this one */ + return -ENODEV; + } + + PDEBUG(D_PROBE, "Probing for an ov9650 sensor"); + + /* Run the pre-init before probing the sensor */ + for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) { + u8 data = preinit_ov9650[i][2]; + if (preinit_ov9650[i][0] == SENSOR) + err = m5602_write_sensor(sd, + preinit_ov9650[i][1], &data, 1); + else + err = m5602_write_bridge(sd, + preinit_ov9650[i][1], data); + } + + if (err < 0) + return err; + + if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1)) + return -ENODEV; + + if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1)) + return -ENODEV; + + if ((prod_id == 0x96) && (ver_id == 0x52)) { + pr_info("Detected an ov9650 sensor\n"); + goto sensor_found; + } + return -ENODEV; + +sensor_found: + sensor_settings = kmalloc( + ARRAY_SIZE(ov9650_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = ov9650_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes); + sd->desc->ctrls = ov9650_ctrls; + sd->desc->nctrls = ARRAY_SIZE(ov9650_ctrls); + + for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++) + sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + return 0; +} + +int ov9650_init(struct sd *sd) +{ + int i, err = 0; + u8 data; + s32 *sensor_settings = sd->sensor_priv; + + if (dump_sensor) + ov9650_dump_registers(sd); + + for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) { + data = init_ov9650[i][2]; + if (init_ov9650[i][0] == SENSOR) + err = m5602_write_sensor(sd, init_ov9650[i][1], + &data, 1); + else + err = m5602_write_bridge(sd, init_ov9650[i][1], data); + } + + err = ov9650_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = ov9650_set_red_balance(&sd->gspca_dev, + sensor_settings[RED_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_blue_balance(&sd->gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = ov9650_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + err = ov9650_set_auto_exposure(&sd->gspca_dev, + sensor_settings[AUTO_EXPOSURE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_auto_white_balance(&sd->gspca_dev, + sensor_settings[AUTO_WHITE_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_auto_gain(&sd->gspca_dev, + sensor_settings[AUTO_GAIN_CTRL_IDX]); + return err; +} + +int ov9650_start(struct sd *sd) +{ + u8 data; + int i, err = 0; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + + int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; + int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; + int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + int hor_offs = OV9650_LEFT_OFFSET; + + if ((!dmi_check_system(ov9650_flip_dmi_table) && + sensor_settings[VFLIP_IDX]) || + (dmi_check_system(ov9650_flip_dmi_table) && + !sensor_settings[VFLIP_IDX])) + ver_offs--; + + if (width <= 320) + hor_offs /= 2; + + /* Synthesize the vsync/hsync setup */ + for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) { + if (res_init_ov9650[i][0] == BRIDGE) + err = m5602_write_bridge(sd, res_init_ov9650[i][1], + res_init_ov9650[i][2]); + else if (res_init_ov9650[i][0] == SENSOR) { + data = res_init_ov9650[i][2]; + err = m5602_write_sensor(sd, + res_init_ov9650[i][1], &data, 1); + } + } + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, + ((ver_offs >> 8) & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + (hor_offs >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + ((width + hor_offs) >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + ((width + hor_offs) & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + switch (width) { + case 640: + PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + + data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + + case 352: + PDEBUG(D_V4L2, "Configuring camera for CIF mode"); + + data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + + case 320: + PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); + + data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + + case 176: + PDEBUG(D_V4L2, "Configuring camera for QCIF mode"); + + data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + } + return err; +} + +int ov9650_stop(struct sd *sd) +{ + u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X; + return m5602_write_sensor(sd, OV9650_COM2, &data, 1); +} + +void ov9650_disconnect(struct sd *sd) +{ + ov9650_stop(sd); + + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Read exposure %d", *val); + return 0; +} + +static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + PDEBUG(D_V4L2, "Set exposure to %d", val); + + sensor_settings[EXPOSURE_IDX] = val; + /* The 6 MSBs */ + i2c_data = (val >> 10) & 0x3f; + err = m5602_write_sensor(sd, OV9650_AECHM, + &i2c_data, 1); + if (err < 0) + return err; + + /* The 8 middle bits */ + i2c_data = (val >> 2) & 0xff; + err = m5602_write_sensor(sd, OV9650_AECH, + &i2c_data, 1); + if (err < 0) + return err; + + /* The 2 LSBs */ + i2c_data = val & 0x03; + err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1); + return err; +} + +static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read gain %d", *val); + return 0; +} + +static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Setting gain to %d", val); + + sensor_settings[GAIN_IDX] = val; + + /* The 2 MSB */ + /* Read the OV9650_VREF register first to avoid + corrupting the VREF high and low bits */ + err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + if (err < 0) + return err; + + /* Mask away all uninteresting bits */ + i2c_data = ((val & 0x0300) >> 2) | + (i2c_data & 0x3f); + err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); + if (err < 0) + return err; + + /* The 8 LSBs */ + i2c_data = val & 0xff; + err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); + return err; +} + +static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[RED_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read red gain %d", *val); + return 0; +} + +static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set red gain to %d", val); + + sensor_settings[RED_BALANCE_IDX] = val; + + i2c_data = val & 0xff; + err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); + return err; +} + +static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BLUE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read blue gain %d", *val); + + return 0; +} + +static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set blue gain to %d", val); + + sensor_settings[BLUE_BALANCE_IDX] = val; + + i2c_data = val & 0xff; + err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); + return err; +} + +static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[HFLIP_IDX]; + PDEBUG(D_V4L2, "Read horizontal flip %d", *val); + return 0; +} + +static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + + sensor_settings[HFLIP_IDX] = val; + + if (!dmi_check_system(ov9650_flip_dmi_table)) + i2c_data = ((val & 0x01) << 5) | + (sensor_settings[VFLIP_IDX] << 4); + else + i2c_data = ((val & 0x01) << 5) | + (!sensor_settings[VFLIP_IDX] << 4); + + err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); + + return err; +} + +static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[VFLIP_IDX]; + PDEBUG(D_V4L2, "Read vertical flip %d", *val); + + return 0; +} + +static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set vertical flip to %d", val); + sensor_settings[VFLIP_IDX] = val; + + if (dmi_check_system(ov9650_flip_dmi_table)) + val = !val; + + i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); + err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); + if (err < 0) + return err; + + /* When vflip is toggled we need to readjust the bridge hsync/vsync */ + if (gspca_dev->streaming) + err = ov9650_start(sd); + + return err; +} + +static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Read auto exposure control %d", *val); + return 0; +} + +static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto exposure control to %d", val); + + sensor_settings[AUTO_EXPOSURE_IDX] = val; + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); + + return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); +} + +static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + return 0; +} + +static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto white balance to %d", val); + + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); + err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); + + return err; +} + +static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; + PDEBUG(D_V4L2, "Read auto gain control %d", *val); + return 0; +} + +static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set auto gain control to %d", val); + + sensor_settings[AUTO_GAIN_CTRL_IDX] = val; + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); + + return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); +} + +static void ov9650_dump_registers(struct sd *sd) +{ + int address; + pr_info("Dumping the ov9650 register state\n"); + for (address = 0; address < 0xa9; address++) { + u8 value; + m5602_read_sensor(sd, address, &value, 1); + pr_info("register 0x%x contains 0x%x\n", address, value); + } + + pr_info("ov9650 register state dump complete\n"); + + pr_info("Probing for which registers that are read/write\n"); + for (address = 0; address < 0xff; address++) { + u8 old_value, ctrl_value; + u8 test_value[2] = {0xff, 0xff}; + + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value[0]) + pr_info("register 0x%x is writeable\n", address); + else + pr_info("register 0x%x is read only\n", address); + + /* Restore original value */ + m5602_write_sensor(sd, address, &old_value, 1); + } +} diff --git a/drivers/media/usb/gspca/m5602/m5602_ov9650.h b/drivers/media/usb/gspca/m5602/m5602_ov9650.h new file mode 100644 index 000000000000..f7aa5bf68983 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.h @@ -0,0 +1,307 @@ +/* + * Driver for the ov9650 sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_OV9650_H_ +#define M5602_OV9650_H_ + +#include +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define OV9650_GAIN 0x00 +#define OV9650_BLUE 0x01 +#define OV9650_RED 0x02 +#define OV9650_VREF 0x03 +#define OV9650_COM1 0x04 +#define OV9650_BAVE 0x05 +#define OV9650_GEAVE 0x06 +#define OV9650_RSVD7 0x07 +#define OV9650_COM2 0x09 +#define OV9650_PID 0x0a +#define OV9650_VER 0x0b +#define OV9650_COM3 0x0c +#define OV9650_COM4 0x0d +#define OV9650_COM5 0x0e +#define OV9650_COM6 0x0f +#define OV9650_AECH 0x10 +#define OV9650_CLKRC 0x11 +#define OV9650_COM7 0x12 +#define OV9650_COM8 0x13 +#define OV9650_COM9 0x14 +#define OV9650_COM10 0x15 +#define OV9650_RSVD16 0x16 +#define OV9650_HSTART 0x17 +#define OV9650_HSTOP 0x18 +#define OV9650_VSTRT 0x19 +#define OV9650_VSTOP 0x1a +#define OV9650_PSHFT 0x1b +#define OV9650_MVFP 0x1e +#define OV9650_AEW 0x24 +#define OV9650_AEB 0x25 +#define OV9650_VPT 0x26 +#define OV9650_BBIAS 0x27 +#define OV9650_GbBIAS 0x28 +#define OV9650_Gr_COM 0x29 +#define OV9650_RBIAS 0x2c +#define OV9650_HREF 0x32 +#define OV9650_CHLF 0x33 +#define OV9650_ARBLM 0x34 +#define OV9650_RSVD35 0x35 +#define OV9650_RSVD36 0x36 +#define OV9650_ADC 0x37 +#define OV9650_ACOM38 0x38 +#define OV9650_OFON 0x39 +#define OV9650_TSLB 0x3a +#define OV9650_COM12 0x3c +#define OV9650_COM13 0x3d +#define OV9650_COM15 0x40 +#define OV9650_COM16 0x41 +#define OV9650_LCC1 0x62 +#define OV9650_LCC2 0x63 +#define OV9650_LCC3 0x64 +#define OV9650_LCC4 0x65 +#define OV9650_LCC5 0x66 +#define OV9650_HV 0x69 +#define OV9650_DBLV 0x6b +#define OV9650_COM21 0x8b +#define OV9650_COM22 0x8c +#define OV9650_COM24 0x8e +#define OV9650_DBLC1 0x8f +#define OV9650_RSVD94 0x94 +#define OV9650_RSVD95 0x95 +#define OV9650_RSVD96 0x96 +#define OV9650_LCCFB 0x9d +#define OV9650_LCCFR 0x9e +#define OV9650_AECHM 0xa1 +#define OV9650_COM26 0xa5 +#define OV9650_ACOMA8 0xa8 +#define OV9650_ACOMA9 0xa9 + +#define OV9650_REGISTER_RESET (1 << 7) +#define OV9650_VGA_SELECT (1 << 6) +#define OV9650_CIF_SELECT (1 << 5) +#define OV9650_QVGA_SELECT (1 << 4) +#define OV9650_QCIF_SELECT (1 << 3) +#define OV9650_RGB_SELECT (1 << 2) +#define OV9650_RAW_RGB_SELECT (1 << 0) + +#define OV9650_FAST_AGC_AEC (1 << 7) +#define OV9650_AEC_UNLIM_STEP_SIZE (1 << 6) +#define OV9650_BANDING (1 << 5) +#define OV9650_AGC_EN (1 << 2) +#define OV9650_AWB_EN (1 << 1) +#define OV9650_AEC_EN (1 << 0) + +#define OV9650_VARIOPIXEL (1 << 2) +#define OV9650_SYSTEM_CLK_SEL (1 << 7) +#define OV9650_SLAM_MODE (1 << 4) + +#define OV9650_QVGA_VARIOPIXEL (1 << 7) + +#define OV9650_VFLIP (1 << 4) +#define OV9650_HFLIP (1 << 5) + +#define OV9650_SOFT_SLEEP (1 << 4) +#define OV9650_OUTPUT_DRIVE_2X (1 << 0) + +#define OV9650_DENOISE_ENABLE (1 << 5) +#define OV9650_WHITE_PIXEL_ENABLE (1 << 1) +#define OV9650_WHITE_PIXEL_OPTION (1 << 0) + +#define OV9650_LEFT_OFFSET 0x62 + +#define GAIN_DEFAULT 0x14 +#define RED_GAIN_DEFAULT 0x70 +#define BLUE_GAIN_DEFAULT 0x20 +#define EXPOSURE_DEFAULT 0x1ff + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern bool dump_sensor; + +int ov9650_probe(struct sd *sd); +int ov9650_init(struct sd *sd); +int ov9650_start(struct sd *sd); +int ov9650_stop(struct sd *sd); +void ov9650_disconnect(struct sd *sd); + +static const struct m5602_sensor ov9650 = { + .name = "OV9650", + .i2c_slave_id = 0x60, + .i2c_regW = 1, + .probe = ov9650_probe, + .init = ov9650_init, + .start = ov9650_start, + .stop = ov9650_stop, + .disconnect = ov9650_disconnect, +}; + +static const unsigned char preinit_ov9650[][3] = { + /* [INITCAM] */ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, + /* Reset chip */ + {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, + /* Enable double clock */ + {SENSOR, OV9650_CLKRC, 0x80}, + /* Do something out of spec with the power */ + {SENSOR, OV9650_OFON, 0x40} +}; + +static const unsigned char init_ov9650[][3] = { + /* [INITCAM] */ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, + + /* Reset chip */ + {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, + /* One extra reset is needed in order to make the sensor behave + properly when resuming from ram, could be a timing issue */ + {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, + + /* Enable double clock */ + {SENSOR, OV9650_CLKRC, 0x80}, + /* Do something out of spec with the power */ + {SENSOR, OV9650_OFON, 0x40}, + + /* Set fast AGC/AEC algorithm with unlimited step size */ + {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | + OV9650_AEC_UNLIM_STEP_SIZE}, + + {SENSOR, OV9650_CHLF, 0x10}, + {SENSOR, OV9650_ARBLM, 0xbf}, + {SENSOR, OV9650_ACOM38, 0x81}, + /* Turn off color matrix coefficient double option */ + {SENSOR, OV9650_COM16, 0x00}, + /* Enable color matrix for RGB/YUV, Delay Y channel, + set output Y/UV delay to 1 */ + {SENSOR, OV9650_COM13, 0x19}, + /* Enable digital BLC, Set output mode to U Y V Y */ + {SENSOR, OV9650_TSLB, 0x0c}, + /* Limit the AGC/AEC stable upper region */ + {SENSOR, OV9650_COM24, 0x00}, + /* Enable HREF and some out of spec things */ + {SENSOR, OV9650_COM12, 0x73}, + /* Set all DBLC offset signs to positive and + do some out of spec stuff */ + {SENSOR, OV9650_DBLC1, 0xdf}, + {SENSOR, OV9650_COM21, 0x06}, + {SENSOR, OV9650_RSVD35, 0x91}, + /* Necessary, no camera stream without it */ + {SENSOR, OV9650_RSVD16, 0x06}, + {SENSOR, OV9650_RSVD94, 0x99}, + {SENSOR, OV9650_RSVD95, 0x99}, + {SENSOR, OV9650_RSVD96, 0x04}, + /* Enable full range output */ + {SENSOR, OV9650_COM15, 0x0}, + /* Enable HREF at optical black, enable ADBLC bias, + enable ADBLC, reset timings at format change */ + {SENSOR, OV9650_COM6, 0x4b}, + /* Subtract 32 from the B channel bias */ + {SENSOR, OV9650_BBIAS, 0xa0}, + /* Subtract 32 from the Gb channel bias */ + {SENSOR, OV9650_GbBIAS, 0xa0}, + /* Do not bypass the analog BLC and to some out of spec stuff */ + {SENSOR, OV9650_Gr_COM, 0x00}, + /* Subtract 32 from the R channel bias */ + {SENSOR, OV9650_RBIAS, 0xa0}, + /* Subtract 32 from the R channel bias */ + {SENSOR, OV9650_RBIAS, 0x0}, + {SENSOR, OV9650_COM26, 0x80}, + {SENSOR, OV9650_ACOMA9, 0x98}, + /* Set the AGC/AEC stable region upper limit */ + {SENSOR, OV9650_AEW, 0x68}, + /* Set the AGC/AEC stable region lower limit */ + {SENSOR, OV9650_AEB, 0x5c}, + /* Set the high and low limit nibbles to 3 */ + {SENSOR, OV9650_VPT, 0xc3}, + /* Set the Automatic Gain Ceiling (AGC) to 128x, + drop VSYNC at frame drop, + limit exposure timing, + drop frame when the AEC step is larger than the exposure gap */ + {SENSOR, OV9650_COM9, 0x6e}, + /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync) + and set PWDN to SLVS (slave mode vertical sync) */ + {SENSOR, OV9650_COM10, 0x42}, + /* Set horizontal column start high to default value */ + {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */ + /* Set horizontal column end */ + {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */ + /* Complementing register to the two writes above */ + {SENSOR, OV9650_HREF, 0xb2}, + /* Set vertical row start high bits */ + {SENSOR, OV9650_VSTRT, 0x02}, + /* Set vertical row end low bits */ + {SENSOR, OV9650_VSTOP, 0x7e}, + /* Set complementing vertical frame control */ + {SENSOR, OV9650_VREF, 0x10}, + {SENSOR, OV9650_ADC, 0x04}, + {SENSOR, OV9650_HV, 0x40}, + + /* Enable denoise, and white-pixel erase */ + {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE | + OV9650_WHITE_PIXEL_ENABLE | + OV9650_WHITE_PIXEL_OPTION}, + + /* Enable VARIOPIXEL */ + {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, + {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL}, + + /* Put the sensor in soft sleep mode */ + {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X}, +}; + +static const unsigned char res_init_ov9650[][3] = { + {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X}, + + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01} +}; +#endif diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.c b/drivers/media/usb/gspca/m5602/m5602_po1030.c new file mode 100644 index 000000000000..b8771698cbcb --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c @@ -0,0 +1,763 @@ +/* + * Driver for the po1030 sensor + * + * Copyright (c) 2008 Erik Andrén + * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (c) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "m5602_po1030.h" + +static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val); +static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, + __s32 *val); + +static struct v4l2_pix_format po1030_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + } +}; + +static const struct ctrl po1030_ctrls[] = { +#define GAIN_IDX 0 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0x4f, + .step = 0x1, + .default_value = PO1030_GLOBAL_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_gain, + .get = po1030_get_gain + }, +#define EXPOSURE_IDX 1 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x02ff, + .step = 0x1, + .default_value = PO1030_EXPOSURE_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_exposure, + .get = po1030_get_exposure + }, +#define RED_BALANCE_IDX 2 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_red_balance, + .get = po1030_get_red_balance + }, +#define BLUE_BALANCE_IDX 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_blue_balance, + .get = po1030_get_blue_balance + }, +#define HFLIP_IDX 4 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_hflip, + .get = po1030_get_hflip + }, +#define VFLIP_IDX 5 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_vflip, + .get = po1030_get_vflip + }, +#define AUTO_WHITE_BALANCE_IDX 6 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_auto_white_balance, + .get = po1030_get_auto_white_balance + }, +#define AUTO_EXPOSURE_IDX 7 + { + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_auto_exposure, + .get = po1030_get_auto_exposure + }, +#define GREEN_BALANCE_IDX 8 + { + { + .id = M5602_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_GREEN_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_green_balance, + .get = po1030_get_green_balance + }, +}; + +static void po1030_dump_registers(struct sd *sd); + +int po1030_probe(struct sd *sd) +{ + u8 dev_id_h = 0, i; + s32 *sensor_settings; + + if (force_sensor) { + if (force_sensor == PO1030_SENSOR) { + pr_info("Forcing a %s sensor\n", po1030.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + PDEBUG(D_PROBE, "Probing for a po1030 sensor"); + + /* Run the pre-init to actually probe the unit */ + for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { + u8 data = preinit_po1030[i][2]; + if (preinit_po1030[i][0] == SENSOR) + m5602_write_sensor(sd, + preinit_po1030[i][1], &data, 1); + else + m5602_write_bridge(sd, preinit_po1030[i][1], data); + } + + if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1)) + return -ENODEV; + + if (dev_id_h == 0x30) { + pr_info("Detected a po1030 sensor\n"); + goto sensor_found; + } + return -ENODEV; + +sensor_found: + sensor_settings = kmalloc( + ARRAY_SIZE(po1030_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = po1030_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes); + sd->desc->ctrls = po1030_ctrls; + sd->desc->nctrls = ARRAY_SIZE(po1030_ctrls); + + for (i = 0; i < ARRAY_SIZE(po1030_ctrls); i++) + sensor_settings[i] = po1030_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + + return 0; +} + +int po1030_init(struct sd *sd) +{ + s32 *sensor_settings = sd->sensor_priv; + int i, err = 0; + + /* Init the sensor */ + for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) { + u8 data[2] = {0x00, 0x00}; + + switch (init_po1030[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + init_po1030[i][1], + init_po1030[i][2]); + break; + + case SENSOR: + data[0] = init_po1030[i][2]; + err = m5602_write_sensor(sd, + init_po1030[i][1], data, 1); + break; + + default: + pr_info("Invalid stream command, exiting init\n"); + return -EINVAL; + } + } + if (err < 0) + return err; + + if (dump_sensor) + po1030_dump_registers(sd); + + err = po1030_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = po1030_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = po1030_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = po1030_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + err = po1030_set_red_balance(&sd->gspca_dev, + sensor_settings[RED_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_blue_balance(&sd->gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_green_balance(&sd->gspca_dev, + sensor_settings[GREEN_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_auto_white_balance(&sd->gspca_dev, + sensor_settings[AUTO_WHITE_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_auto_exposure(&sd->gspca_dev, + sensor_settings[AUTO_EXPOSURE_IDX]); + return err; +} + +int po1030_start(struct sd *sd) +{ + struct cam *cam = &sd->gspca_dev.cam; + int i, err = 0; + int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; + int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; + int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + u8 data; + + switch (width) { + case 320: + data = PO1030_SUBSAMPLING; + err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); + if (err < 0) + return err; + + data = ((width + 3) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); + if (err < 0) + return err; + + data = (width + 3) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); + if (err < 0) + return err; + + data = ((height + 1) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); + if (err < 0) + return err; + + data = (height + 1) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); + + height += 6; + width -= 1; + break; + + case 640: + data = 0; + err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); + if (err < 0) + return err; + + data = ((width + 7) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); + if (err < 0) + return err; + + data = (width + 7) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); + if (err < 0) + return err; + + data = ((height + 3) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); + if (err < 0) + return err; + + data = (height + 3) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); + + height += 12; + width -= 2; + break; + } + err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, + ((ver_offs >> 8) & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + return err; +} + +static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Exposure read as %d", *val); + return 0; +} + +static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[EXPOSURE_IDX] = val; + PDEBUG(D_V4L2, "Set exposure to %d", val & 0xffff); + + i2c_data = ((val & 0xff00) >> 8); + PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x", + i2c_data); + + err = m5602_write_sensor(sd, PO1030_INTEGLINES_H, + &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = (val & 0xff); + PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x", + i2c_data); + err = m5602_write_sensor(sd, PO1030_INTEGLINES_M, + &i2c_data, 1); + + return err; +} + +static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read global gain %d", *val); + return 0; +} + +static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[GAIN_IDX] = val; + + i2c_data = val & 0xff; + PDEBUG(D_V4L2, "Set global gain to %d", i2c_data); + err = m5602_write_sensor(sd, PO1030_GLOBALGAIN, + &i2c_data, 1); + return err; +} + +static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[HFLIP_IDX]; + PDEBUG(D_V4L2, "Read hflip %d", *val); + + return 0; +} + +static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[HFLIP_IDX] = val; + + PDEBUG(D_V4L2, "Set hflip %d", val); + err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7); + + err = m5602_write_sensor(sd, PO1030_CONTROL2, + &i2c_data, 1); + + return err; +} + +static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[VFLIP_IDX]; + PDEBUG(D_V4L2, "Read vflip %d", *val); + + return 0; +} + +static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[VFLIP_IDX] = val; + + PDEBUG(D_V4L2, "Set vflip %d", val); + err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6); + + err = m5602_write_sensor(sd, PO1030_CONTROL2, + &i2c_data, 1); + + return err; +} + +static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[RED_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read red gain %d", *val); + return 0; +} + +static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[RED_BALANCE_IDX] = val; + + i2c_data = val & 0xff; + PDEBUG(D_V4L2, "Set red gain to %d", i2c_data); + err = m5602_write_sensor(sd, PO1030_RED_GAIN, + &i2c_data, 1); + return err; +} + +static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BLUE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read blue gain %d", *val); + + return 0; +} + +static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[BLUE_BALANCE_IDX] = val; + + i2c_data = val & 0xff; + PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data); + err = m5602_write_sensor(sd, PO1030_BLUE_GAIN, + &i2c_data, 1); + + return err; +} + +static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GREEN_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read green gain %d", *val); + + return 0; +} + +static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[GREEN_BALANCE_IDX] = val; + i2c_data = val & 0xff; + PDEBUG(D_V4L2, "Set green gain to %d", i2c_data); + + err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN, + &i2c_data, 1); + if (err < 0) + return err; + + return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN, + &i2c_data, 1); +} + +static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Auto white balancing is %d", *val); + + return 0; +} + +static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; + + err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); + if (err < 0) + return err; + + PDEBUG(D_V4L2, "Set auto white balance to %d", val); + i2c_data = (i2c_data & 0xfe) | (val & 0x01); + err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); + return err; +} + +static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, + __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Auto exposure is %d", *val); + return 0; +} + +static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[AUTO_EXPOSURE_IDX] = val; + err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); + if (err < 0) + return err; + + PDEBUG(D_V4L2, "Set auto exposure to %d", val); + i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1); + return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); +} + +void po1030_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static void po1030_dump_registers(struct sd *sd) +{ + int address; + u8 value = 0; + + pr_info("Dumping the po1030 sensor core registers\n"); + for (address = 0; address < 0x7f; address++) { + m5602_read_sensor(sd, address, &value, 1); + pr_info("register 0x%x contains 0x%x\n", address, value); + } + + pr_info("po1030 register state dump complete\n"); + + pr_info("Probing for which registers that are read/write\n"); + for (address = 0; address < 0xff; address++) { + u8 old_value, ctrl_value; + u8 test_value[2] = {0xff, 0xff}; + + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value[0]) + pr_info("register 0x%x is writeable\n", address); + else + pr_info("register 0x%x is read only\n", address); + + /* Restore original value */ + m5602_write_sensor(sd, address, &old_value, 1); + } +} diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.h b/drivers/media/usb/gspca/m5602/m5602_po1030.h new file mode 100644 index 000000000000..81a2bcb88fe3 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.h @@ -0,0 +1,272 @@ +/* + * Driver for the po1030 sensor. + * + * Copyright (c) 2008 Erik Andrén + * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (c) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * Register defines taken from Pascal Stangs Procyon Armlib + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_PO1030_H_ +#define M5602_PO1030_H_ + +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define PO1030_DEVID_H 0x00 +#define PO1030_DEVID_L 0x01 +#define PO1030_FRAMEWIDTH_H 0x04 +#define PO1030_FRAMEWIDTH_L 0x05 +#define PO1030_FRAMEHEIGHT_H 0x06 +#define PO1030_FRAMEHEIGHT_L 0x07 +#define PO1030_WINDOWX_H 0x08 +#define PO1030_WINDOWX_L 0x09 +#define PO1030_WINDOWY_H 0x0a +#define PO1030_WINDOWY_L 0x0b +#define PO1030_WINDOWWIDTH_H 0x0c +#define PO1030_WINDOWWIDTH_L 0x0d +#define PO1030_WINDOWHEIGHT_H 0x0e +#define PO1030_WINDOWHEIGHT_L 0x0f + +#define PO1030_GLOBALIBIAS 0x12 +#define PO1030_PIXELIBIAS 0x13 + +#define PO1030_GLOBALGAIN 0x15 +#define PO1030_RED_GAIN 0x16 +#define PO1030_GREEN_1_GAIN 0x17 +#define PO1030_BLUE_GAIN 0x18 +#define PO1030_GREEN_2_GAIN 0x19 + +#define PO1030_INTEGLINES_H 0x1a +#define PO1030_INTEGLINES_M 0x1b +#define PO1030_INTEGLINES_L 0x1c + +#define PO1030_CONTROL1 0x1d +#define PO1030_CONTROL2 0x1e +#define PO1030_CONTROL3 0x1f +#define PO1030_CONTROL4 0x20 + +#define PO1030_PERIOD50_H 0x23 +#define PO1030_PERIOD50_L 0x24 +#define PO1030_PERIOD60_H 0x25 +#define PO1030_PERIOD60_L 0x26 +#define PO1030_REGCLK167 0x27 +#define PO1030_FLICKER_DELTA50 0x28 +#define PO1030_FLICKERDELTA60 0x29 + +#define PO1030_ADCOFFSET 0x2c + +/* Gamma Correction Coeffs */ +#define PO1030_GC0 0x2d +#define PO1030_GC1 0x2e +#define PO1030_GC2 0x2f +#define PO1030_GC3 0x30 +#define PO1030_GC4 0x31 +#define PO1030_GC5 0x32 +#define PO1030_GC6 0x33 +#define PO1030_GC7 0x34 + +/* Color Transform Matrix */ +#define PO1030_CT0 0x35 +#define PO1030_CT1 0x36 +#define PO1030_CT2 0x37 +#define PO1030_CT3 0x38 +#define PO1030_CT4 0x39 +#define PO1030_CT5 0x3a +#define PO1030_CT6 0x3b +#define PO1030_CT7 0x3c +#define PO1030_CT8 0x3d + +#define PO1030_AUTOCTRL1 0x3e +#define PO1030_AUTOCTRL2 0x3f + +#define PO1030_YTARGET 0x40 +#define PO1030_GLOBALGAINMIN 0x41 +#define PO1030_GLOBALGAINMAX 0x42 + +#define PO1030_AWB_RED_TUNING 0x47 +#define PO1030_AWB_BLUE_TUNING 0x48 + +/* Output format control */ +#define PO1030_OUTFORMCTRL1 0x5a +#define PO1030_OUTFORMCTRL2 0x5b +#define PO1030_OUTFORMCTRL3 0x5c +#define PO1030_OUTFORMCTRL4 0x5d +#define PO1030_OUTFORMCTRL5 0x5e + +#define PO1030_EDGE_ENH_OFF 0x5f +#define PO1030_EGA 0x60 + +#define PO1030_Cb_U_GAIN 0x63 +#define PO1030_Cr_V_GAIN 0x64 + +#define PO1030_YCONTRAST 0x74 +#define PO1030_YSATURATION 0x75 + +#define PO1030_HFLIP (1 << 7) +#define PO1030_VFLIP (1 << 6) + +#define PO1030_HREF_ENABLE (1 << 6) + +#define PO1030_RAW_RGB_BAYER 0x4 + +#define PO1030_FRAME_EQUAL (1 << 3) +#define PO1030_AUTO_SUBSAMPLING (1 << 4) + +#define PO1030_WEIGHT_WIN_2X (1 << 3) + +#define PO1030_SHUTTER_MODE (1 << 6) +#define PO1030_AUTO_SUBSAMPLING (1 << 4) +#define PO1030_FRAME_EQUAL (1 << 3) + +#define PO1030_SENSOR_RESET (1 << 5) + +#define PO1030_SUBSAMPLING (1 << 6) + +/*****************************************************************************/ + +#define PO1030_GLOBAL_GAIN_DEFAULT 0x12 +#define PO1030_EXPOSURE_DEFAULT 0x0085 +#define PO1030_BLUE_GAIN_DEFAULT 0x36 +#define PO1030_RED_GAIN_DEFAULT 0x36 +#define PO1030_GREEN_GAIN_DEFAULT 0x40 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern bool dump_sensor; + +int po1030_probe(struct sd *sd); +int po1030_init(struct sd *sd); +int po1030_start(struct sd *sd); +void po1030_disconnect(struct sd *sd); + +static const struct m5602_sensor po1030 = { + .name = "PO1030", + + .i2c_slave_id = 0xdc, + .i2c_regW = 1, + + .probe = po1030_probe, + .init = po1030_init, + .start = po1030_start, + .disconnect = po1030_disconnect, +}; + +static const unsigned char preinit_po1030[][3] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + + {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00} +}; + +static const unsigned char init_po1030[][3] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + + {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + + {SENSOR, PO1030_AUTOCTRL2, 0x04}, + + {SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER}, + {SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X}, + + {SENSOR, PO1030_CONTROL2, 0x03}, + {SENSOR, 0x21, 0x90}, + {SENSOR, PO1030_YTARGET, 0x60}, + {SENSOR, 0x59, 0x13}, + {SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE}, + {SENSOR, PO1030_EDGE_ENH_OFF, 0x00}, + {SENSOR, PO1030_EGA, 0x80}, + {SENSOR, 0x78, 0x14}, + {SENSOR, 0x6f, 0x01}, + {SENSOR, PO1030_GLOBALGAINMAX, 0x14}, + {SENSOR, PO1030_Cb_U_GAIN, 0x38}, + {SENSOR, PO1030_Cr_V_GAIN, 0x38}, + {SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE | + PO1030_AUTO_SUBSAMPLING | + PO1030_FRAME_EQUAL}, + {SENSOR, PO1030_GC0, 0x10}, + {SENSOR, PO1030_GC1, 0x20}, + {SENSOR, PO1030_GC2, 0x40}, + {SENSOR, PO1030_GC3, 0x60}, + {SENSOR, PO1030_GC4, 0x80}, + {SENSOR, PO1030_GC5, 0xa0}, + {SENSOR, PO1030_GC6, 0xc0}, + {SENSOR, PO1030_GC7, 0xff}, + + /* Set the width to 751 */ + {SENSOR, PO1030_FRAMEWIDTH_H, 0x02}, + {SENSOR, PO1030_FRAMEWIDTH_L, 0xef}, + + /* Set the height to 540 */ + {SENSOR, PO1030_FRAMEHEIGHT_H, 0x02}, + {SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c}, + + /* Set the x window to 1 */ + {SENSOR, PO1030_WINDOWX_H, 0x00}, + {SENSOR, PO1030_WINDOWX_L, 0x01}, + + /* Set the y window to 1 */ + {SENSOR, PO1030_WINDOWY_H, 0x00}, + {SENSOR, PO1030_WINDOWY_L, 0x01}, + + /* with a very low lighted environment increase the exposure but + * decrease the FPS (Frame Per Second) */ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, +}; +#endif diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c new file mode 100644 index 000000000000..cc8ec3f7e8dc --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c @@ -0,0 +1,726 @@ +/* + * Driver for the s5k4aa sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "m5602_s5k4aa.h" + +static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val); + +static + const + struct dmi_system_id s5k4aa_vflip_dmi_table[] = { + { + .ident = "BRUNEINIT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"), + DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"), + DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001") + } + }, { + .ident = "Fujitsu-Siemens Amilo Xa 2528", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528") + } + }, { + .ident = "Fujitsu-Siemens Amilo Xi 2428", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428") + } + }, { + .ident = "Fujitsu-Siemens Amilo Xi 2528", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528") + } + }, { + .ident = "Fujitsu-Siemens Amilo Xi 2550", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") + } + }, { + .ident = "Fujitsu-Siemens Amilo Pa 2548", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548") + } + }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "12/02/2008") + } + }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") + } + }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "07/19/2007") + } + }, { + .ident = "MSI GX700/GX705/EX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700") + } + }, { + .ident = "MSI L735", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X") + } + }, { + .ident = "Lenovo Y300", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"), + DMI_MATCH(DMI_PRODUCT_NAME, "Y300") + } + }, + { } +}; + +static struct v4l2_pix_format s5k4aa_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { + 1280, + 1024, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 1280 * 1024, + .bytesperline = 1280, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static const struct ctrl s5k4aa_ctrls[] = { +#define VFLIP_IDX 0 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k4aa_set_vflip, + .get = s5k4aa_get_vflip + }, +#define HFLIP_IDX 1 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k4aa_set_hflip, + .get = s5k4aa_get_hflip + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = S5K4AA_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k4aa_set_gain, + .get = s5k4aa_get_gain + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 13, + .maximum = 0xfff, + .step = 1, + .default_value = 0x100, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k4aa_set_exposure, + .get = s5k4aa_get_exposure + }, +#define NOISE_SUPP_IDX 4 + { + { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Noise suppression (smoothing)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = s5k4aa_set_noise, + .get = s5k4aa_get_noise + }, +#define BRIGHTNESS_IDX 5 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0x1f, + .step = 1, + .default_value = S5K4AA_DEFAULT_BRIGHTNESS, + }, + .set = s5k4aa_set_brightness, + .get = s5k4aa_get_brightness + }, + +}; + +static void s5k4aa_dump_registers(struct sd *sd); + +int s5k4aa_probe(struct sd *sd) +{ + u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75}; + int i, err = 0; + s32 *sensor_settings; + + if (force_sensor) { + if (force_sensor == S5K4AA_SENSOR) { + pr_info("Forcing a %s sensor\n", s5k4aa.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + PDEBUG(D_PROBE, "Probing for a s5k4aa sensor"); + + /* Preinit the sensor */ + for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) { + u8 data[2] = {0x00, 0x00}; + + switch (preinit_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + preinit_s5k4aa[i][1], + preinit_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = preinit_s5k4aa[i][2]; + err = m5602_write_sensor(sd, + preinit_s5k4aa[i][1], + data, 1); + break; + + case SENSOR_LONG: + data[0] = preinit_s5k4aa[i][2]; + data[1] = preinit_s5k4aa[i][3]; + err = m5602_write_sensor(sd, + preinit_s5k4aa[i][1], + data, 2); + break; + default: + pr_info("Invalid stream command, exiting init\n"); + return -EINVAL; + } + } + + /* Test some registers, but we don't know their exact meaning yet */ + if (m5602_read_sensor(sd, 0x00, prod_id, 2)) + return -ENODEV; + if (m5602_read_sensor(sd, 0x02, prod_id+2, 2)) + return -ENODEV; + if (m5602_read_sensor(sd, 0x04, prod_id+4, 2)) + return -ENODEV; + + if (memcmp(prod_id, expected_prod_id, sizeof(prod_id))) + return -ENODEV; + else + pr_info("Detected a s5k4aa sensor\n"); + +sensor_found: + sensor_settings = kmalloc( + ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = s5k4aa_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes); + sd->desc->ctrls = s5k4aa_ctrls; + sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls); + + for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++) + sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + + return 0; +} + +int s5k4aa_start(struct sd *sd) +{ + int i, err = 0; + u8 data[2]; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + + switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) { + case 1280: + PDEBUG(D_V4L2, "Configuring camera for SXGA mode"); + + for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) { + switch (SXGA_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + SXGA_s5k4aa[i][1], + SXGA_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = SXGA_s5k4aa[i][2]; + err = m5602_write_sensor(sd, + SXGA_s5k4aa[i][1], + data, 1); + break; + + case SENSOR_LONG: + data[0] = SXGA_s5k4aa[i][2]; + data[1] = SXGA_s5k4aa[i][3]; + err = m5602_write_sensor(sd, + SXGA_s5k4aa[i][1], + data, 2); + break; + + default: + pr_err("Invalid stream command, exiting init\n"); + return -EINVAL; + } + } + err = s5k4aa_set_noise(&sd->gspca_dev, 0); + if (err < 0) + return err; + break; + + case 640: + PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + + for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) { + switch (VGA_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + VGA_s5k4aa[i][1], + VGA_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = VGA_s5k4aa[i][2]; + err = m5602_write_sensor(sd, + VGA_s5k4aa[i][1], + data, 1); + break; + + case SENSOR_LONG: + data[0] = VGA_s5k4aa[i][2]; + data[1] = VGA_s5k4aa[i][3]; + err = m5602_write_sensor(sd, + VGA_s5k4aa[i][1], + data, 2); + break; + + default: + pr_err("Invalid stream command, exiting init\n"); + return -EINVAL; + } + } + err = s5k4aa_set_noise(&sd->gspca_dev, 1); + if (err < 0) + return err; + break; + } + if (err < 0) + return err; + + err = s5k4aa_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); +} + +int s5k4aa_init(struct sd *sd) +{ + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) { + u8 data[2] = {0x00, 0x00}; + + switch (init_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + init_s5k4aa[i][1], + init_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = init_s5k4aa[i][2]; + err = m5602_write_sensor(sd, + init_s5k4aa[i][1], data, 1); + break; + + case SENSOR_LONG: + data[0] = init_s5k4aa[i][2]; + data[1] = init_s5k4aa[i][3]; + err = m5602_write_sensor(sd, + init_s5k4aa[i][1], data, 2); + break; + default: + pr_info("Invalid stream command, exiting init\n"); + return -EINVAL; + } + } + + if (dump_sensor) + s5k4aa_dump_registers(sd); + + return err; +} + +static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Read exposure %d", *val); + + return 0; +} + +static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + sensor_settings[EXPOSURE_IDX] = val; + PDEBUG(D_V4L2, "Set exposure to %d", val); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + return err; + data = (val >> 8) & 0xff; + err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); + if (err < 0) + return err; + data = val & 0xff; + err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); + + return err; +} + +static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[VFLIP_IDX]; + PDEBUG(D_V4L2, "Read vertical flip %d", *val); + + return 0; +} + +static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + sensor_settings[VFLIP_IDX] = val; + + PDEBUG(D_V4L2, "Set vertical flip to %d", val); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + return err; + + err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + return err; + + if (dmi_check_system(s5k4aa_vflip_dmi_table)) + val = !val; + + data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7)); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + return err; + + err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + if (err < 0) + return err; + if (val) + data &= 0xfe; + else + data |= 0x01; + err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + return err; +} + +static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[HFLIP_IDX]; + PDEBUG(D_V4L2, "Read horizontal flip %d", *val); + + return 0; +} + +static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + sensor_settings[HFLIP_IDX] = val; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + return err; + + err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + return err; + + if (dmi_check_system(s5k4aa_vflip_dmi_table)) + val = !val; + + data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + return err; + + err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + if (err < 0) + return err; + if (val) + data &= 0xfe; + else + data |= 0x01; + err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + return err; +} + +static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read gain %d", *val); + return 0; +} + +static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + sensor_settings[GAIN_IDX] = val; + + PDEBUG(D_V4L2, "Set gain to %d", val); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + return err; + + data = val & 0xff; + err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1); + + return err; +} + +static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BRIGHTNESS_IDX]; + PDEBUG(D_V4L2, "Read brightness %d", *val); + return 0; +} + +static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + sensor_settings[BRIGHTNESS_IDX] = val; + + PDEBUG(D_V4L2, "Set brightness to %d", val); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + return err; + + data = val & 0xff; + return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1); +} + +static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[NOISE_SUPP_IDX]; + PDEBUG(D_V4L2, "Read noise %d", *val); + return 0; +} + +static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + sensor_settings[NOISE_SUPP_IDX] = val; + + PDEBUG(D_V4L2, "Set noise to %d", val); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + return err; + + data = val & 0x01; + return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1); +} + +void s5k4aa_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static void s5k4aa_dump_registers(struct sd *sd) +{ + int address; + u8 page, old_page; + m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); + for (page = 0; page < 16; page++) { + m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); + pr_info("Dumping the s5k4aa register state for page 0x%x\n", + page); + for (address = 0; address <= 0xff; address++) { + u8 value = 0; + m5602_read_sensor(sd, address, &value, 1); + pr_info("register 0x%x contains 0x%x\n", + address, value); + } + } + pr_info("s5k4aa register state dump complete\n"); + + for (page = 0; page < 16; page++) { + m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); + pr_info("Probing for which registers that are read/write for page 0x%x\n", + page); + for (address = 0; address <= 0xff; address++) { + u8 old_value, ctrl_value, test_value = 0xff; + + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, &test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value) + pr_info("register 0x%x is writeable\n", + address); + else + pr_info("register 0x%x is read only\n", + address); + + /* Restore original value */ + m5602_write_sensor(sd, address, &old_value, 1); + } + } + pr_info("Read/write register probing complete\n"); + m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); +} diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h new file mode 100644 index 000000000000..8e0035e731c7 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h @@ -0,0 +1,283 @@ +/* + * Driver for the s5k4aa sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_S5K4AA_H_ +#define M5602_S5K4AA_H_ + +#include + +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define S5K4AA_PAGE_MAP 0xec + +#define S5K4AA_PAGE_MAP_0 0x00 +#define S5K4AA_PAGE_MAP_1 0x01 +#define S5K4AA_PAGE_MAP_2 0x02 + +/* Sensor register definitions for page 0x02 */ +#define S5K4AA_READ_MODE 0x03 +#define S5K4AA_ROWSTART_HI 0x04 +#define S5K4AA_ROWSTART_LO 0x05 +#define S5K4AA_COLSTART_HI 0x06 +#define S5K4AA_COLSTART_LO 0x07 +#define S5K4AA_WINDOW_HEIGHT_HI 0x08 +#define S5K4AA_WINDOW_HEIGHT_LO 0x09 +#define S5K4AA_WINDOW_WIDTH_HI 0x0a +#define S5K4AA_WINDOW_WIDTH_LO 0x0b +#define S5K4AA_GLOBAL_GAIN__ 0x0f +/* sync lost, if too low, reduces frame rate if too high */ +#define S5K4AA_H_BLANK_HI__ 0x1d +#define S5K4AA_H_BLANK_LO__ 0x1e +#define S5K4AA_EXPOSURE_HI 0x17 +#define S5K4AA_EXPOSURE_LO 0x18 +#define S5K4AA_BRIGHTNESS 0x1f /* (digital?) gain : 5 bits */ +#define S5K4AA_GAIN 0x20 /* (analogue?) gain : 7 bits */ +#define S5K4AA_NOISE_SUPP 0x37 + +#define S5K4AA_RM_ROW_SKIP_4X 0x08 +#define S5K4AA_RM_ROW_SKIP_2X 0x04 +#define S5K4AA_RM_COL_SKIP_4X 0x02 +#define S5K4AA_RM_COL_SKIP_2X 0x01 +#define S5K4AA_RM_H_FLIP 0x40 +#define S5K4AA_RM_V_FLIP 0x80 + +#define S5K4AA_DEFAULT_GAIN 0x5f +#define S5K4AA_DEFAULT_BRIGHTNESS 0x10 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern bool dump_sensor; + +int s5k4aa_probe(struct sd *sd); +int s5k4aa_init(struct sd *sd); +int s5k4aa_start(struct sd *sd); +void s5k4aa_disconnect(struct sd *sd); + +static const struct m5602_sensor s5k4aa = { + .name = "S5K4AA", + .i2c_slave_id = 0x5a, + .i2c_regW = 2, + + .probe = s5k4aa_probe, + .init = s5k4aa_init, + .start = s5k4aa_start, + .disconnect = s5k4aa_disconnect, +}; + +static const unsigned char preinit_s5k4aa[][4] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00} +}; + +static const unsigned char init_s5k4aa[][4] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00}, + {SENSOR, 0x36, 0x01, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}, + {SENSOR, 0x7b, 0xff, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, 0x0c, 0x05, 0x00}, + {SENSOR, 0x02, 0x0e, 0x00}, + {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00}, + {SENSOR, 0x37, 0x00, 0x00}, +}; + +static const unsigned char VGA_s5k4aa[][4] = { + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */ + + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X + | S5K4AA_RM_COL_SKIP_2X, 0x00}, + /* 0x37 : Fix image stability when light is too bright and improves + * image quality in 640x480, but worsens it in 1280x1024 */ + {SENSOR, 0x37, 0x01, 0x00}, + /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */ + {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00}, + {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00}, + /* window_height_hi, window_height_lo : 960 = 0x03c0 */ + {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00}, + /* window_width_hi, window_width_lo : 1280 = 0x0500 */ + {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00}, + {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00}, + {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */ + {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00}, + {SENSOR, 0x11, 0x04, 0x00}, + {SENSOR, 0x12, 0xc3, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, 0x02, 0x0e, 0x00}, +}; + +static const unsigned char SXGA_s5k4aa[][4] = { + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + /* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + /* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */ + + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00}, + {SENSOR, 0x37, 0x01, 0x00}, + {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00}, + {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00}, + {SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00}, + {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00}, + {SENSOR, 0x11, 0x04, 0x00}, + {SENSOR, 0x12, 0xc3, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, 0x02, 0x0e, 0x00}, +}; +#endif diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c new file mode 100644 index 000000000000..1de743a02b02 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c @@ -0,0 +1,605 @@ +/* + * Driver for the s5k83a sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "m5602_s5k83a.h" + +static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val); + +static struct v4l2_pix_format s5k83a_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static const struct ctrl s5k83a_ctrls[] = { +#define GAIN_IDX 0 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = S5K83A_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_gain, + .get = s5k83a_get_gain + + }, +#define BRIGHTNESS_IDX 1 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = S5K83A_DEFAULT_BRIGHTNESS, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_brightness, + .get = s5k83a_get_brightness, + }, +#define EXPOSURE_IDX 2 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = S5K83A_MAXIMUM_EXPOSURE, + .step = 0x01, + .default_value = S5K83A_DEFAULT_EXPOSURE, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_exposure, + .get = s5k83a_get_exposure + }, +#define HFLIP_IDX 3 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k83a_set_hflip, + .get = s5k83a_get_hflip + }, +#define VFLIP_IDX 4 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k83a_set_vflip, + .get = s5k83a_get_vflip + } +}; + +static void s5k83a_dump_registers(struct sd *sd); +static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data); +static int s5k83a_set_led_indication(struct sd *sd, u8 val); +static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, + __s32 vflip, __s32 hflip); + +int s5k83a_probe(struct sd *sd) +{ + struct s5k83a_priv *sens_priv; + u8 prod_id = 0, ver_id = 0; + int i, err = 0; + + if (force_sensor) { + if (force_sensor == S5K83A_SENSOR) { + pr_info("Forcing a %s sensor\n", s5k83a.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + PDEBUG(D_PROBE, "Probing for a s5k83a sensor"); + + /* Preinit the sensor */ + for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) { + u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]}; + if (preinit_s5k83a[i][0] == SENSOR) + err = m5602_write_sensor(sd, preinit_s5k83a[i][1], + data, 2); + else + err = m5602_write_bridge(sd, preinit_s5k83a[i][1], + data[0]); + } + + /* We don't know what register (if any) that contain the product id + * Just pick the first addresses that seem to produce the same results + * on multiple machines */ + if (m5602_read_sensor(sd, 0x00, &prod_id, 1)) + return -ENODEV; + + if (m5602_read_sensor(sd, 0x01, &ver_id, 1)) + return -ENODEV; + + if ((prod_id == 0xff) || (ver_id == 0xff)) + return -ENODEV; + else + pr_info("Detected a s5k83a sensor\n"); + +sensor_found: + sens_priv = kmalloc( + sizeof(struct s5k83a_priv), GFP_KERNEL); + if (!sens_priv) + return -ENOMEM; + + sens_priv->settings = + kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); + if (!sens_priv->settings) { + kfree(sens_priv); + return -ENOMEM; + } + + sd->gspca_dev.cam.cam_mode = s5k83a_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); + sd->desc->ctrls = s5k83a_ctrls; + sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls); + + /* null the pointer! thread is't running now */ + sens_priv->rotation_thread = NULL; + + for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++) + sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value; + + sd->sensor_priv = sens_priv; + return 0; +} + +int s5k83a_init(struct sd *sd) +{ + int i, err = 0; + s32 *sensor_settings = + ((struct s5k83a_priv *) sd->sensor_priv)->settings; + + for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) { + u8 data[2] = {0x00, 0x00}; + + switch (init_s5k83a[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + init_s5k83a[i][1], + init_s5k83a[i][2]); + break; + + case SENSOR: + data[0] = init_s5k83a[i][2]; + err = m5602_write_sensor(sd, + init_s5k83a[i][1], data, 1); + break; + + case SENSOR_LONG: + data[0] = init_s5k83a[i][2]; + data[1] = init_s5k83a[i][3]; + err = m5602_write_sensor(sd, + init_s5k83a[i][1], data, 2); + break; + default: + pr_info("Invalid stream command, exiting init\n"); + return -EINVAL; + } + } + + if (dump_sensor) + s5k83a_dump_registers(sd); + + err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + + return err; +} + +static int rotation_thread_function(void *data) +{ + struct sd *sd = (struct sd *) data; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + u8 reg, previous_rotation = 0; + __s32 vflip, hflip; + + set_current_state(TASK_INTERRUPTIBLE); + while (!schedule_timeout(100)) { + if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock)) + break; + + s5k83a_get_rotation(sd, ®); + if (previous_rotation != reg) { + previous_rotation = reg; + pr_info("Camera was flipped\n"); + + s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); + s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); + + if (reg) { + vflip = !vflip; + hflip = !hflip; + } + s5k83a_set_flip_real((struct gspca_dev *) sd, + vflip, hflip); + } + + mutex_unlock(&sd->gspca_dev.usb_lock); + set_current_state(TASK_INTERRUPTIBLE); + } + + /* return to "front" flip */ + if (previous_rotation) { + s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); + s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); + s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip); + } + + sens_priv->rotation_thread = NULL; + return 0; +} + +int s5k83a_start(struct sd *sd) +{ + int i, err = 0; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + /* Create another thread, polling the GPIO ports of the camera to check + if it got rotated. This is how the windows driver does it so we have + to assume that there is no better way of accomplishing this */ + sens_priv->rotation_thread = kthread_create(rotation_thread_function, + sd, "rotation thread"); + wake_up_process(sens_priv->rotation_thread); + + /* Preinit the sensor */ + for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) { + u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]}; + if (start_s5k83a[i][0] == SENSOR) + err = m5602_write_sensor(sd, start_s5k83a[i][1], + data, 2); + else + err = m5602_write_bridge(sd, start_s5k83a[i][1], + data[0]); + } + if (err < 0) + return err; + + return s5k83a_set_led_indication(sd, 1); +} + +int s5k83a_stop(struct sd *sd) +{ + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + if (sens_priv->rotation_thread) + kthread_stop(sens_priv->rotation_thread); + + return s5k83a_set_led_indication(sd, 0); +} + +void s5k83a_disconnect(struct sd *sd) +{ + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + s5k83a_stop(sd); + + sd->sensor = NULL; + kfree(sens_priv->settings); + kfree(sens_priv); +} + +static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + *val = sens_priv->settings[GAIN_IDX]; + return 0; +} + +static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + sens_priv->settings[GAIN_IDX] = val; + + data[0] = 0x00; + data[1] = 0x20; + err = m5602_write_sensor(sd, 0x14, data, 2); + if (err < 0) + return err; + + data[0] = 0x01; + data[1] = 0x00; + err = m5602_write_sensor(sd, 0x0d, data, 2); + if (err < 0) + return err; + + /* FIXME: This is not sane, we need to figure out the composition + of these registers */ + data[0] = val >> 3; /* gain, high 5 bits */ + data[1] = val >> 1; /* gain, high 7 bits */ + err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2); + + return err; +} + +static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + *val = sens_priv->settings[BRIGHTNESS_IDX]; + return 0; +} + +static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[1]; + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + sens_priv->settings[BRIGHTNESS_IDX] = val; + data[0] = val; + err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1); + return err; +} + +static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + *val = sens_priv->settings[EXPOSURE_IDX]; + return 0; +} + +static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + sens_priv->settings[EXPOSURE_IDX] = val; + data[0] = 0; + data[1] = val; + err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2); + return err; +} + +static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + *val = sens_priv->settings[VFLIP_IDX]; + return 0; +} + +static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, + __s32 vflip, __s32 hflip) +{ + int err; + u8 data[1]; + struct sd *sd = (struct sd *) gspca_dev; + + data[0] = 0x05; + err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); + if (err < 0) + return err; + + /* six bit is vflip, seven is hflip */ + data[0] = S5K83A_FLIP_MASK; + data[0] = (vflip) ? data[0] | 0x40 : data[0]; + data[0] = (hflip) ? data[0] | 0x80 : data[0]; + + err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); + if (err < 0) + return err; + + data[0] = (vflip) ? 0x0b : 0x0a; + err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1); + if (err < 0) + return err; + + data[0] = (hflip) ? 0x0a : 0x0b; + err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); + return err; +} + +static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 reg; + __s32 hflip; + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + sens_priv->settings[VFLIP_IDX] = val; + + s5k83a_get_hflip(gspca_dev, &hflip); + + err = s5k83a_get_rotation(sd, ®); + if (err < 0) + return err; + if (reg) { + val = !val; + hflip = !hflip; + } + + err = s5k83a_set_flip_real(gspca_dev, val, hflip); + return err; +} + +static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + *val = sens_priv->settings[HFLIP_IDX]; + return 0; +} + +static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 reg; + __s32 vflip; + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + sens_priv->settings[HFLIP_IDX] = val; + + s5k83a_get_vflip(gspca_dev, &vflip); + + err = s5k83a_get_rotation(sd, ®); + if (err < 0) + return err; + if (reg) { + val = !val; + vflip = !vflip; + } + + err = s5k83a_set_flip_real(gspca_dev, vflip, val); + return err; +} + +static int s5k83a_set_led_indication(struct sd *sd, u8 val) +{ + int err = 0; + u8 data[1]; + + err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data); + if (err < 0) + return err; + + if (val) + data[0] = data[0] | S5K83A_GPIO_LED_MASK; + else + data[0] = data[0] & ~S5K83A_GPIO_LED_MASK; + + err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]); + + return err; +} + +/* Get camera rotation on Acer notebooks */ +static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data) +{ + int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data); + *reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1; + return err; +} + +static void s5k83a_dump_registers(struct sd *sd) +{ + int address; + u8 page, old_page; + m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); + + for (page = 0; page < 16; page++) { + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + pr_info("Dumping the s5k83a register state for page 0x%x\n", + page); + for (address = 0; address <= 0xff; address++) { + u8 val = 0; + m5602_read_sensor(sd, address, &val, 1); + pr_info("register 0x%x contains 0x%x\n", address, val); + } + } + pr_info("s5k83a register state dump complete\n"); + + for (page = 0; page < 16; page++) { + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + pr_info("Probing for which registers that are read/write for page 0x%x\n", + page); + for (address = 0; address <= 0xff; address++) { + u8 old_val, ctrl_val, test_val = 0xff; + + m5602_read_sensor(sd, address, &old_val, 1); + m5602_write_sensor(sd, address, &test_val, 1); + m5602_read_sensor(sd, address, &ctrl_val, 1); + + if (ctrl_val == test_val) + pr_info("register 0x%x is writeable\n", + address); + else + pr_info("register 0x%x is read only\n", + address); + + /* Restore original val */ + m5602_write_sensor(sd, address, &old_val, 1); + } + } + pr_info("Read/write register probing complete\n"); + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); +} diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.h b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h new file mode 100644 index 000000000000..79952247b534 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h @@ -0,0 +1,193 @@ +/* + * Driver for the s5k83a sensor + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_S5K83A_H_ +#define M5602_S5K83A_H_ + +#include "m5602_sensor.h" + +#define S5K83A_FLIP 0x01 +#define S5K83A_HFLIP_TUNE 0x03 +#define S5K83A_VFLIP_TUNE 0x05 +#define S5K83A_BRIGHTNESS 0x0a +#define S5K83A_EXPOSURE 0x18 +#define S5K83A_GAIN 0x1b +#define S5K83A_PAGE_MAP 0xec + +#define S5K83A_DEFAULT_GAIN 0x71 +#define S5K83A_DEFAULT_BRIGHTNESS 0x7e +#define S5K83A_DEFAULT_EXPOSURE 0x00 +#define S5K83A_MAXIMUM_EXPOSURE 0x3c +#define S5K83A_FLIP_MASK 0x10 +#define S5K83A_GPIO_LED_MASK 0x10 +#define S5K83A_GPIO_ROTATION_MASK 0x40 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern bool dump_sensor; + +int s5k83a_probe(struct sd *sd); +int s5k83a_init(struct sd *sd); +int s5k83a_start(struct sd *sd); +int s5k83a_stop(struct sd *sd); +void s5k83a_disconnect(struct sd *sd); + +static const struct m5602_sensor s5k83a = { + .name = "S5K83A", + .probe = s5k83a_probe, + .init = s5k83a_init, + .start = s5k83a_start, + .stop = s5k83a_stop, + .disconnect = s5k83a_disconnect, + .i2c_slave_id = 0x5a, + .i2c_regW = 2, +}; + +struct s5k83a_priv { + /* We use another thread periodically + probing the orientation of the camera */ + struct task_struct *rotation_thread; + s32 *settings; +}; + +static const unsigned char preinit_s5k83a[][4] = { + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, +}; + +/* This could probably be considerably shortened. + I don't have the hardware to experiment with it, patches welcome +*/ +static const unsigned char init_s5k83a[][4] = { + /* The following sequence is useless after a clean boot + but is necessary after resume from suspend */ + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, + {SENSOR, 0xaf, 0x01, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}, + {SENSOR, 0x7b, 0xff, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x01, 0x50, 0x00}, + {SENSOR, 0x12, 0x20, 0x00}, + {SENSOR, 0x17, 0x40, 0x00}, + {SENSOR, 0x1c, 0x00, 0x00}, + {SENSOR, 0x02, 0x70, 0x00}, + {SENSOR, 0x03, 0x0b, 0x00}, + {SENSOR, 0x04, 0xf0, 0x00}, + {SENSOR, 0x05, 0x0b, 0x00}, + {SENSOR, 0x06, 0x71, 0x00}, + {SENSOR, 0x07, 0xe8, 0x00}, /* 488 */ + {SENSOR, 0x08, 0x02, 0x00}, + {SENSOR, 0x09, 0x88, 0x00}, /* 648 */ + {SENSOR, 0x14, 0x00, 0x00}, + {SENSOR, 0x15, 0x20, 0x00}, /* 32 */ + {SENSOR, 0x19, 0x00, 0x00}, + {SENSOR, 0x1a, 0x98, 0x00}, /* 152 */ + {SENSOR, 0x0f, 0x02, 0x00}, + {SENSOR, 0x10, 0xe5, 0x00}, /* 741 */ + /* normal colors + (this is value after boot, but after tries can be different) */ + {SENSOR, 0x00, 0x06, 0x00}, +}; + +static const unsigned char start_s5k83a[][4] = { + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */ + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, +}; +#endif diff --git a/drivers/media/usb/gspca/m5602/m5602_sensor.h b/drivers/media/usb/gspca/m5602/m5602_sensor.h new file mode 100644 index 000000000000..edff4f1f586f --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_sensor.h @@ -0,0 +1,70 @@ +/* + * USB Driver for ALi m5602 based webcams + * + * Copyright (C) 2008 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_SENSOR_H_ +#define M5602_SENSOR_H_ + +#include "m5602_bridge.h" + +#define M5602_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 0) +#define M5602_V4L2_CID_NOISE_SUPPRESION (V4L2_CID_PRIVATE_BASE + 1) + +/* Enumerates all supported sensors */ +enum sensors { + OV9650_SENSOR = 1, + S5K83A_SENSOR = 2, + S5K4AA_SENSOR = 3, + MT9M111_SENSOR = 4, + PO1030_SENSOR = 5, + OV7660_SENSOR = 6, +}; + +/* Enumerates all possible instruction types */ +enum instruction { + BRIDGE, + SENSOR, + SENSOR_LONG +}; + +struct m5602_sensor { + /* Defines the name of a sensor */ + char name[32]; + + /* What i2c address the sensor is connected to */ + u8 i2c_slave_id; + + /* Width of each i2c register (in bytes) */ + u8 i2c_regW; + + /* Probes if the sensor is connected */ + int (*probe)(struct sd *sd); + + /* Performs a initialization sequence */ + int (*init)(struct sd *sd); + + /* Executed when the camera starts to send data */ + int (*start)(struct sd *sd); + + /* Executed when the camera ends to send data */ + int (*stop)(struct sd *sd); + + /* Executed when the device is disconnected */ + void (*disconnect)(struct sd *sd); +}; + +#endif diff --git a/drivers/media/usb/gspca/mars.c b/drivers/media/usb/gspca/mars.c new file mode 100644 index 000000000000..ff2c5abf115b --- /dev/null +++ b/drivers/media/usb/gspca/mars.c @@ -0,0 +1,439 @@ +/* + * Mars-Semi MR97311A library + * Copyright (C) 2005 + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "mars" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define QUALITY 50 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *gamma; + struct { /* illuminator control cluster */ + struct v4l2_ctrl *illum_top; + struct v4l2_ctrl *illum_bottom; + }; + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +/* V4L2 controls supported by the driver */ +static void setbrightness(struct gspca_dev *gspca_dev, s32 val); +static void setcolors(struct gspca_dev *gspca_dev, s32 val); +static void setgamma(struct gspca_dev *gspca_dev, s32 val); +static void setsharpness(struct gspca_dev *gspca_dev, s32 val); + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +static const __u8 mi_data[0x20] = { +/* 01 02 03 04 05 06 07 08 */ + 0x48, 0x22, 0x01, 0x47, 0x10, 0x00, 0x00, 0x00, +/* 09 0a 0b 0c 0d 0e 0f 10 */ + 0x00, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, +/* 11 12 13 14 15 16 17 18 */ + 0x30, 0x00, 0x04, 0x00, 0x06, 0x01, 0xe2, 0x02, +/* 19 1a 1b 1c 1d 1e 1f 20 */ + 0x82, 0x00, 0x20, 0x17, 0x80, 0x08, 0x0c, 0x00 +}; + +/* write bytes from gspca_dev->usb_buf */ +static void reg_w(struct gspca_dev *gspca_dev, + int len) +{ + int alen, ret; + + if (gspca_dev->usb_err < 0) + return; + + ret = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 4), + gspca_dev->usb_buf, + len, + &alen, + 500); /* timeout in milliseconds */ + if (ret < 0) { + pr_err("reg write [%02x] error %d\n", + gspca_dev->usb_buf[0], ret); + gspca_dev->usb_err = ret; + } +} + +static void mi_w(struct gspca_dev *gspca_dev, + u8 addr, + u8 value) +{ + gspca_dev->usb_buf[0] = 0x1f; + gspca_dev->usb_buf[1] = 0; /* control byte */ + gspca_dev->usb_buf[2] = addr; + gspca_dev->usb_buf[3] = value; + + reg_w(gspca_dev, 4); +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + gspca_dev->usb_buf[0] = 0x61; + gspca_dev->usb_buf[1] = val; + reg_w(gspca_dev, 2); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + gspca_dev->usb_buf[0] = 0x5f; + gspca_dev->usb_buf[1] = val << 3; + gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04; + reg_w(gspca_dev, 3); +} + +static void setgamma(struct gspca_dev *gspca_dev, s32 val) +{ + gspca_dev->usb_buf[0] = 0x06; + gspca_dev->usb_buf[1] = val * 0x40; + reg_w(gspca_dev, 2); +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + gspca_dev->usb_buf[0] = 0x67; + gspca_dev->usb_buf[1] = val * 4 + 3; + reg_w(gspca_dev, 2); +} + +static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom) +{ + /* both are off if not streaming */ + gspca_dev->usb_buf[0] = 0x22; + if (top) + gspca_dev->usb_buf[1] = 0x76; + else if (bottom) + gspca_dev->usb_buf[1] = 0x7a; + else + gspca_dev->usb_buf[1] = 0x7e; + reg_w(gspca_dev, 2); +} + +static int mars_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_ILLUMINATORS_1) { + /* only one can be on at a time */ + if (ctrl->is_new && ctrl->val) + sd->illum_bottom->val = 0; + if (sd->illum_bottom->is_new && sd->illum_bottom->val) + sd->illum_top->val = 0; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAMMA: + setgamma(gspca_dev, ctrl->val); + break; + case V4L2_CID_ILLUMINATORS_1: + setilluminators(gspca_dev, sd->illum_top->val, + sd->illum_bottom->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops mars_ctrl_ops = { + .s_ctrl = mars_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 6); + sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 30, 1, 15); + sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 200); + sd->gamma = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_GAMMA, 0, 3, 1, 1); + sd->sharpness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 2, 1, 1); + sd->illum_top = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE; + sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE; + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(2, &sd->illum_top); + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 *data; + int i; + + /* create the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, QUALITY); + + data = gspca_dev->usb_buf; + + data[0] = 0x01; /* address */ + data[1] = 0x01; + reg_w(gspca_dev, 2); + + /* + Initialize the MR97113 chip register + */ + data[0] = 0x00; /* address */ + data[1] = 0x0c | 0x01; /* reg 0 */ + data[2] = 0x01; /* reg 1 */ + data[3] = gspca_dev->width / 8; /* h_size , reg 2 */ + data[4] = gspca_dev->height / 8; /* v_size , reg 3 */ + data[5] = 0x30; /* reg 4, MI, PAS5101 : + * 0x30 for 24mhz , 0x28 for 12mhz */ + data[6] = 0x02; /* reg 5, H start - was 0x04 */ + data[7] = v4l2_ctrl_g_ctrl(sd->gamma) * 0x40; /* reg 0x06: gamma */ + data[8] = 0x01; /* reg 7, V start - was 0x03 */ +/* if (h_size == 320 ) */ +/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ +/* else */ + data[9] = 0x52; /* reg 8, 24MHz, no scale down */ +/*jfm: from win trace*/ + data[10] = 0x18; + + reg_w(gspca_dev, 11); + + data[0] = 0x23; /* address */ + data[1] = 0x09; /* reg 35, append frame header */ + + reg_w(gspca_dev, 2); + + data[0] = 0x3c; /* address */ +/* if (gspca_dev->width == 1280) */ +/* data[1] = 200; * reg 60, pc-cam frame size + * (unit: 4KB) 800KB */ +/* else */ + data[1] = 50; /* 50 reg 60, pc-cam frame size + * (unit: 4KB) 200KB */ + reg_w(gspca_dev, 2); + + /* auto dark-gain */ + data[0] = 0x5e; /* address */ + data[1] = 0; /* reg 94, Y Gain (auto) */ +/*jfm: from win trace*/ + /* reg 0x5f/0x60 (LE) = saturation */ + /* h (60): xxxx x100 + * l (5f): xxxx x000 */ + data[2] = v4l2_ctrl_g_ctrl(sd->saturation) << 3; + data[3] = ((v4l2_ctrl_g_ctrl(sd->saturation) >> 2) & 0xf8) | 0x04; + data[4] = v4l2_ctrl_g_ctrl(sd->brightness); /* reg 0x61 = brightness */ + data[5] = 0x00; + + reg_w(gspca_dev, 6); + + data[0] = 0x67; +/*jfm: from win trace*/ + data[1] = v4l2_ctrl_g_ctrl(sd->sharpness) * 4 + 3; + data[2] = 0x14; + reg_w(gspca_dev, 3); + + data[0] = 0x69; + data[1] = 0x2f; + data[2] = 0x28; + data[3] = 0x42; + reg_w(gspca_dev, 4); + + data[0] = 0x63; + data[1] = 0x07; + reg_w(gspca_dev, 2); +/*jfm: win trace - many writes here to reg 0x64*/ + + /* initialize the MI sensor */ + for (i = 0; i < sizeof mi_data; i++) + mi_w(gspca_dev, i + 1, mi_data[i]); + + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transferring enable... */ + reg_w(gspca_dev, 2); + + setilluminators(gspca_dev, v4l2_ctrl_g_ctrl(sd->illum_top), + v4l2_ctrl_g_ctrl(sd->illum_bottom)); + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (v4l2_ctrl_g_ctrl(sd->illum_top) || + v4l2_ctrl_g_ctrl(sd->illum_bottom)) { + setilluminators(gspca_dev, false, false); + msleep(20); + } + + gspca_dev->usb_buf[0] = 1; + gspca_dev->usb_buf[1] = 0; + reg_w(gspca_dev, 2); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int p; + + if (len < 6) { +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + for (p = 0; p < len - 6; p++) { + if (data[0 + p] == 0xff + && data[1 + p] == 0xff + && data[2 + p] == 0x00 + && data[3 + p] == 0xff + && data[4 + p] == 0x96) { + if (data[5 + p] == 0x64 + || data[5 + p] == 0x65 + || data[5 + p] == 0x66 + || data[5 + p] == 0x67) { + PDEBUG(D_PACK, "sof offset: %d len: %d", + p, len); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, p); + + /* put the JPEG header */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + data += p + 16; + len -= p + 16; + break; + } + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x050f)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/mr97310a.c b/drivers/media/usb/gspca/mr97310a.c new file mode 100644 index 000000000000..8f4714df5990 --- /dev/null +++ b/drivers/media/usb/gspca/mr97310a.c @@ -0,0 +1,1091 @@ +/* + * Mars MR97310A library + * + * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is + * Copyright (C) 2009 Kyle Guinn + * + * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ + * and for the routines for detecting and classifying these various cameras, + * is Copyright (C) 2009 Theodore Kilgore + * + * Support for the control settings for the CIF cameras is + * Copyright (C) 2009 Hans de Goede and + * Thomas Kaiser + * + * Support for the control settings for the VGA cameras is + * Copyright (C) 2009 Theodore Kilgore + * + * Several previously unsupported cameras are owned and have been tested by + * Hans de Goede and + * Thomas Kaiser and + * Theodore Kilgore and + * Edmond Rodriguez and + * Aurelien Jacobs + * + * The MR97311A support in gspca/mars.c has been helpful in understanding some + * of the registers in these cameras. + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "mr97310a" + +#include "gspca.h" + +#define CAM_TYPE_CIF 0 +#define CAM_TYPE_VGA 1 + +#define MR97310A_BRIGHTNESS_DEFAULT 0 + +#define MR97310A_EXPOSURE_MIN 0 +#define MR97310A_EXPOSURE_MAX 4095 +#define MR97310A_EXPOSURE_DEFAULT 1000 + +#define MR97310A_GAIN_MIN 0 +#define MR97310A_GAIN_MAX 31 +#define MR97310A_GAIN_DEFAULT 25 + +#define MR97310A_CONTRAST_MIN 0 +#define MR97310A_CONTRAST_MAX 31 +#define MR97310A_CONTRAST_DEFAULT 23 + +#define MR97310A_CS_GAIN_MIN 0 +#define MR97310A_CS_GAIN_MAX 0x7ff +#define MR97310A_CS_GAIN_DEFAULT 0x110 + +#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000) +#define MR97310A_MIN_CLOCKDIV_MIN 3 +#define MR97310A_MIN_CLOCKDIV_MAX 8 +#define MR97310A_MIN_CLOCKDIV_DEFAULT 3 + +MODULE_AUTHOR("Kyle Guinn ," + "Theodore Kilgore "); +MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* global parameters */ +static int force_sensor_type = -1; +module_param(force_sensor_type, int, 0644); +MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct { /* exposure/min_clockdiv control cluster */ + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *min_clockdiv; + }; + u8 sof_read; + u8 cam_type; /* 0 is CIF and 1 is VGA */ + u8 sensor_type; /* We use 0 and 1 here, too. */ + u8 do_lcd_stop; + u8 adj_colors; +}; + +struct sensor_w_data { + u8 reg; + u8 flags; + u8 data[16]; + int len; +}; + +static void sd_stopN(struct gspca_dev *gspca_dev); + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, + {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* the bytes to write are in gspca_dev->usb_buf */ +static int mr_write(struct gspca_dev *gspca_dev, int len) +{ + int rc; + + rc = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 4), + gspca_dev->usb_buf, len, NULL, 500); + if (rc < 0) + pr_err("reg write [%02x] error %d\n", + gspca_dev->usb_buf[0], rc); + return rc; +} + +/* the bytes are read into gspca_dev->usb_buf */ +static int mr_read(struct gspca_dev *gspca_dev, int len) +{ + int rc; + + rc = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 3), + gspca_dev->usb_buf, len, NULL, 500); + if (rc < 0) + pr_err("reg read [%02x] error %d\n", + gspca_dev->usb_buf[0], rc); + return rc; +} + +static int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags, + const u8 *data, int len) +{ + gspca_dev->usb_buf[0] = 0x1f; + gspca_dev->usb_buf[1] = flags; + gspca_dev->usb_buf[2] = reg; + memcpy(gspca_dev->usb_buf + 3, data, len); + + return mr_write(gspca_dev, len + 3); +} + +static int sensor_write_regs(struct gspca_dev *gspca_dev, + const struct sensor_w_data *data, int len) +{ + int i, rc; + + for (i = 0; i < len; i++) { + rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags, + data[i].data, data[i].len); + if (rc < 0) + return rc; + } + + return 0; +} + +static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 buf, confirm_reg; + int rc; + + buf = data; + if (sd->cam_type == CAM_TYPE_CIF) { + rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + confirm_reg = sd->sensor_type ? 0x13 : 0x11; + } else { + rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1); + confirm_reg = 0x11; + } + if (rc < 0) + return rc; + + buf = 0x01; + rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); + if (rc < 0) + return rc; + + return 0; +} + +static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose) +{ + int err_code; + + gspca_dev->usb_buf[0] = reg; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + if (err_code < 0) + return err_code; + + if (verbose) + PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg, + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); + + return 0; +} + +static int zero_the_pointer(struct gspca_dev *gspca_dev) +{ + __u8 *data = gspca_dev->usb_buf; + int err_code; + u8 status = 0; + int tries = 0; + + err_code = cam_get_response16(gspca_dev, 0x21, 0); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0x51; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev, 0x21, 0); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0xba; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev, 0x21, 0); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev, 0x21, 0); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + while (status != 0x0a && tries < 256) { + err_code = cam_get_response16(gspca_dev, 0x21, 0); + status = data[0]; + tries++; + if (err_code < 0) + return err_code; + } + if (status != 0x0a) + PDEBUG(D_ERR, "status is %02x", status); + + tries = 0; + while (tries < 4) { + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev, 0x21, 0); + status = data[0]; + tries++; + if (err_code < 0) + return err_code; + } + + data[0] = 0x19; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + if (err_code < 0) + return err_code; + + return 0; +} + +static int stream_start(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x01; + return mr_write(gspca_dev, 2); +} + +static void stream_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x00; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "Stream Stop failed"); +} + +static void lcd_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x19; + gspca_dev->usb_buf[1] = 0x54; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "LCD Stop failed"); +} + +static int isoc_enable(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x00; + gspca_dev->usb_buf[1] = 0x4d; /* ISOC transferring enable... */ + return mr_write(gspca_dev, 2); +} + +/* This function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int err_code; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + sd->do_lcd_stop = 0; + + /* Several of the supported CIF cameras share the same USB ID but + * require different initializations and different control settings. + * The same is true of the VGA cameras. Therefore, we are forced + * to start the initialization process in order to determine which + * camera is present. Some of the supported cameras require the + * memory pointer to be set to 0 as the very first item of business + * or else they will not stream. So we do that immediately. + */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; + + /* Now, the query for sensor type. */ + err_code = cam_get_response16(gspca_dev, 0x07, 1); + if (err_code < 0) + return err_code; + + if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { + sd->cam_type = CAM_TYPE_CIF; + cam->nmodes--; + /* + * All but one of the known CIF cameras share the same USB ID, + * but two different init routines are in use, and the control + * settings are different, too. We need to detect which camera + * of the two known varieties is connected! + * + * A list of known CIF cameras follows. They all report either + * 0200 for type 0 or 0300 for type 1. + * If you have another to report, please do + * + * Name sd->sensor_type reported by + * + * Sakar 56379 Spy-shot 0 T. Kilgore + * Innovage 0 T. Kilgore + * Vivitar Mini 0 H. De Goede + * Vivitar Mini 0 E. Rodriguez + * Vivitar Mini 1 T. Kilgore + * Elta-Media 8212dc 1 T. Kaiser + * Philips dig. keych. 1 T. Kilgore + * Trust Spyc@m 100 1 A. Jacobs + */ + switch (gspca_dev->usb_buf[0]) { + case 2: + sd->sensor_type = 0; + break; + case 3: + sd->sensor_type = 1; + break; + default: + pr_err("Unknown CIF Sensor id : %02x\n", + gspca_dev->usb_buf[1]); + return -ENODEV; + } + PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d", + sd->sensor_type); + } else { + sd->cam_type = CAM_TYPE_VGA; + + /* + * Here is a table of the responses to the query for sensor + * type, from the known MR97310A VGA cameras. Six different + * cameras of which five share the same USB ID. + * + * Name gspca_dev->usb_buf[] sd->sensor_type + * sd->do_lcd_stop + * Aiptek Pencam VGA+ 0300 0 1 + * ION digital 0300 0 1 + * Argus DC-1620 0450 1 0 + * Argus QuickClix 0420 1 1 + * Sakar 77379 Digital 0350 0 1 + * Sakar 1638x CyberPix 0120 0 2 + * + * Based upon these results, we assume default settings + * and then correct as necessary, as follows. + * + */ + + sd->sensor_type = 1; + sd->do_lcd_stop = 0; + sd->adj_colors = 0; + if (gspca_dev->usb_buf[0] == 0x01) { + sd->sensor_type = 2; + } else if ((gspca_dev->usb_buf[0] != 0x03) && + (gspca_dev->usb_buf[0] != 0x04)) { + pr_err("Unknown VGA Sensor id Byte 0: %02x\n", + gspca_dev->usb_buf[0]); + pr_err("Defaults assumed, may not work\n"); + pr_err("Please report this\n"); + } + /* Sakar Digital color needs to be adjusted. */ + if ((gspca_dev->usb_buf[0] == 0x03) && + (gspca_dev->usb_buf[1] == 0x50)) + sd->adj_colors = 1; + if (gspca_dev->usb_buf[0] == 0x04) { + sd->do_lcd_stop = 1; + switch (gspca_dev->usb_buf[1]) { + case 0x50: + sd->sensor_type = 0; + PDEBUG(D_PROBE, "sensor_type corrected to 0"); + break; + case 0x20: + /* Nothing to do here. */ + break; + default: + pr_err("Unknown VGA Sensor id Byte 1: %02x\n", + gspca_dev->usb_buf[1]); + pr_err("Defaults assumed, may not work\n"); + pr_err("Please report this\n"); + } + } + PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d", + sd->sensor_type); + } + /* Stop streaming as we've started it only to probe the sensor type. */ + sd_stopN(gspca_dev); + + if (force_sensor_type != -1) { + sd->sensor_type = !!force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int start_cif_cam(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *data = gspca_dev->usb_buf; + int err_code; + static const __u8 startup_string[] = { + 0x00, + 0x0d, + 0x01, + 0x00, /* Hsize/8 for 352 or 320 */ + 0x00, /* Vsize/4 for 288 or 240 */ + 0x13, /* or 0xbb, depends on sensor */ + 0x00, /* Hstart, depends on res. */ + 0x00, /* reserved ? */ + 0x00, /* Vstart, depends on res. and sensor */ + 0x50, /* 0x54 to get 176 or 160 */ + 0xc0 + }; + + /* Note: Some of the above descriptions guessed from MR97113A driver */ + + memcpy(data, startup_string, 11); + if (sd->sensor_type) + data[5] = 0xbb; + + switch (gspca_dev->width) { + case 160: + data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */ + /* fall thru */ + case 320: + default: + data[3] = 0x28; /* reg 2, H size/8 */ + data[4] = 0x3c; /* reg 3, V size/4 */ + data[6] = 0x14; /* reg 5, H start */ + data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */ + break; + case 176: + data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */ + /* fall thru */ + case 352: + data[3] = 0x2c; /* reg 2, H size/8 */ + data[4] = 0x48; /* reg 3, V size/4 */ + data[6] = 0x06; /* reg 5, H start */ + data[8] = 0x06 - sd->sensor_type; /* reg 7, V start */ + break; + } + err_code = mr_write(gspca_dev, 11); + if (err_code < 0) + return err_code; + + if (!sd->sensor_type) { + static const struct sensor_w_data cif_sensor0_init_data[] = { + {0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01, + 0x0f, 0x14, 0x0f, 0x10}, 8}, + {0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5}, + {0x12, 0x00, {0x07}, 1}, + {0x1f, 0x00, {0x06}, 1}, + {0x27, 0x00, {0x04}, 1}, + {0x29, 0x00, {0x0c}, 1}, + {0x40, 0x00, {0x40, 0x00, 0x04}, 3}, + {0x50, 0x00, {0x60}, 1}, + {0x60, 0x00, {0x06}, 1}, + {0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6}, + {0x72, 0x00, {0x1e, 0x56}, 2}, + {0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02, + 0x31, 0x80, 0x00}, 9}, + {0x11, 0x00, {0x01}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data, + ARRAY_SIZE(cif_sensor0_init_data)); + } else { /* sd->sensor_type = 1 */ + static const struct sensor_w_data cif_sensor1_init_data[] = { + /* Reg 3,4, 7,8 get set by the controls */ + {0x02, 0x00, {0x10}, 1}, + {0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */ + {0x06, 0x01, {0x00}, 1}, + {0x09, 0x02, {0x0e}, 1}, + {0x0a, 0x02, {0x05}, 1}, + {0x0b, 0x02, {0x05}, 1}, + {0x0c, 0x02, {0x0f}, 1}, + {0x0d, 0x02, {0x07}, 1}, + {0x0e, 0x02, {0x0c}, 1}, + {0x0f, 0x00, {0x00}, 1}, + {0x10, 0x00, {0x06}, 1}, + {0x11, 0x00, {0x07}, 1}, + {0x12, 0x00, {0x00}, 1}, + {0x13, 0x00, {0x01}, 1}, + {0, 0, {0}, 0} + }; + /* Without this command the cam won't work with USB-UHCI */ + gspca_dev->usb_buf[0] = 0x0a; + gspca_dev->usb_buf[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, + ARRAY_SIZE(cif_sensor1_init_data)); + } + return err_code; +} + +static int start_vga_cam(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *data = gspca_dev->usb_buf; + int err_code; + static const __u8 startup_string[] = + {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, + 0x00, 0x50, 0xc0}; + /* What some of these mean is explained in start_cif_cam(), above */ + + memcpy(data, startup_string, 11); + if (!sd->sensor_type) { + data[5] = 0x00; + data[10] = 0x91; + } + if (sd->sensor_type == 2) { + data[5] = 0x00; + data[10] = 0x18; + } + + switch (gspca_dev->width) { + case 160: + data[9] |= 0x0c; /* reg 8, 4:1 scale down */ + /* fall thru */ + case 320: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 640: + default: + data[3] = 0x50; /* reg 2, H size/8 */ + data[4] = 0x78; /* reg 3, V size/4 */ + data[6] = 0x04; /* reg 5, H start */ + data[8] = 0x03; /* reg 7, V start */ + if (sd->sensor_type == 2) { + data[6] = 2; + data[8] = 1; + } + if (sd->do_lcd_stop) + data[8] = 0x04; /* Bayer tile shifted */ + break; + + case 176: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 352: + data[3] = 0x2c; /* reg 2, H size */ + data[4] = 0x48; /* reg 3, V size */ + data[6] = 0x94; /* reg 5, H start */ + data[8] = 0x63; /* reg 7, V start */ + if (sd->do_lcd_stop) + data[8] = 0x64; /* Bayer tile shifted */ + break; + } + + err_code = mr_write(gspca_dev, 11); + if (err_code < 0) + return err_code; + + if (!sd->sensor_type) { + static const struct sensor_w_data vga_sensor0_init_data[] = { + {0x01, 0x00, {0x0c, 0x00, 0x04}, 3}, + {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4}, + {0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4}, + {0x25, 0x00, {0x03, 0xa9, 0x80}, 3}, + {0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, + ARRAY_SIZE(vga_sensor0_init_data)); + } else if (sd->sensor_type == 1) { + static const struct sensor_w_data color_adj[] = { + {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, + /* adjusted blue, green, red gain correct + too much blue from the Sakar Digital */ + 0x05, 0x01, 0x04}, 8} + }; + + static const struct sensor_w_data color_no_adj[] = { + {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, + /* default blue, green, red gain settings */ + 0x07, 0x00, 0x01}, 8} + }; + + static const struct sensor_w_data vga_sensor1_init_data[] = { + {0x11, 0x04, {0x01}, 1}, + {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, + /* These settings may be better for some cameras */ + /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */ + 0x00, 0x0a}, 7}, + {0x11, 0x04, {0x01}, 1}, + {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, + {0x11, 0x04, {0x01}, 1}, + {0, 0, {0}, 0} + }; + + if (sd->adj_colors) + err_code = sensor_write_regs(gspca_dev, color_adj, + ARRAY_SIZE(color_adj)); + else + err_code = sensor_write_regs(gspca_dev, color_no_adj, + ARRAY_SIZE(color_no_adj)); + + if (err_code < 0) + return err_code; + + err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, + ARRAY_SIZE(vga_sensor1_init_data)); + } else { /* sensor type == 2 */ + static const struct sensor_w_data vga_sensor2_init_data[] = { + + {0x01, 0x00, {0x48}, 1}, + {0x02, 0x00, {0x22}, 1}, + /* Reg 3 msb and 4 is lsb of the exposure setting*/ + {0x05, 0x00, {0x10}, 1}, + {0x06, 0x00, {0x00}, 1}, + {0x07, 0x00, {0x00}, 1}, + {0x08, 0x00, {0x00}, 1}, + {0x09, 0x00, {0x00}, 1}, + /* The following are used in the gain control + * which is BTW completely borked in the OEM driver + * The values for each color go from 0 to 0x7ff + *{0x0a, 0x00, {0x01}, 1}, green1 gain msb + *{0x0b, 0x00, {0x10}, 1}, green1 gain lsb + *{0x0c, 0x00, {0x01}, 1}, red gain msb + *{0x0d, 0x00, {0x10}, 1}, red gain lsb + *{0x0e, 0x00, {0x01}, 1}, blue gain msb + *{0x0f, 0x00, {0x10}, 1}, blue gain lsb + *{0x10, 0x00, {0x01}, 1}, green2 gain msb + *{0x11, 0x00, {0x10}, 1}, green2 gain lsb + */ + {0x12, 0x00, {0x00}, 1}, + {0x13, 0x00, {0x04}, 1}, /* weird effect on colors */ + {0x14, 0x00, {0x00}, 1}, + {0x15, 0x00, {0x06}, 1}, + {0x16, 0x00, {0x01}, 1}, + {0x17, 0x00, {0xe2}, 1}, /* vertical alignment */ + {0x18, 0x00, {0x02}, 1}, + {0x19, 0x00, {0x82}, 1}, /* don't mess with */ + {0x1a, 0x00, {0x00}, 1}, + {0x1b, 0x00, {0x20}, 1}, + /* {0x1c, 0x00, {0x17}, 1}, contrast control */ + {0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */ + {0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */ + {0x1f, 0x00, {0x0c}, 1}, + {0x20, 0x00, {0x00}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data, + ARRAY_SIZE(vga_sensor2_init_data)); + } + return err_code; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err_code; + + sd->sof_read = 0; + + /* Some of the VGA cameras require the memory pointer + * to be set to 0 again. We have been forced to start the + * stream in sd_config() to detect the hardware, and closed it. + * Thus, we need here to do a completely fresh and clean start. */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; + + if (sd->cam_type == CAM_TYPE_CIF) { + err_code = start_cif_cam(gspca_dev); + } else { + err_code = start_vga_cam(gspca_dev); + } + if (err_code < 0) + return err_code; + + return isoc_enable(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + stream_stop(gspca_dev); + /* Not all the cams need this, but even if not, probably a good idea */ + zero_the_pointer(gspca_dev); + if (sd->do_lcd_stop) + lcd_stop(gspca_dev); +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */ + u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */ + static const u8 quick_clix_table[] = + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15}; + if (sd->cam_type == CAM_TYPE_VGA) { + sign_reg += 4; + value_reg += 4; + } + + /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */ + if (val > 0) { + sensor_write1(gspca_dev, sign_reg, 0x00); + } else { + sensor_write1(gspca_dev, sign_reg, 0x01); + val = 257 - val; + } + /* Use lookup table for funky Argus QuickClix brightness */ + if (sd->do_lcd_stop) + val = quick_clix_table[val]; + + sensor_write1(gspca_dev, value_reg, val); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) +{ + struct sd *sd = (struct sd *) gspca_dev; + int exposure = MR97310A_EXPOSURE_DEFAULT; + u8 buf[2]; + + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { + /* This cam does not like exposure settings < 300, + so scale 0 - 4095 to 300 - 4095 */ + exposure = (expo * 9267) / 10000 + 300; + sensor_write1(gspca_dev, 3, exposure >> 4); + sensor_write1(gspca_dev, 4, exposure & 0x0f); + } else if (sd->sensor_type == 2) { + exposure = expo; + exposure >>= 3; + sensor_write1(gspca_dev, 3, exposure >> 8); + sensor_write1(gspca_dev, 4, exposure & 0xff); + } else { + /* We have both a clock divider and an exposure register. + We first calculate the clock divider, as that determines + the maximum exposure and then we calculate the exposure + register setting (which goes from 0 - 511). + + Note our 0 - 4095 exposure is mapped to 0 - 511 + milliseconds exposure time */ + u8 clockdiv = (60 * expo + 7999) / 8000; + + /* Limit framerate to not exceed usb bandwidth */ + if (clockdiv < min_clockdiv && gspca_dev->width >= 320) + clockdiv = min_clockdiv; + else if (clockdiv < 2) + clockdiv = 2; + + if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4) + clockdiv = 4; + + /* Frame exposure time in ms = 1000 * clockdiv / 60 -> + exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ + exposure = (60 * 511 * expo) / (8000 * clockdiv); + if (exposure > 511) + exposure = 511; + + /* exposure register value is reversed! */ + exposure = 511 - exposure; + + buf[0] = exposure & 0xff; + buf[1] = exposure >> 8; + sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2); + sensor_write1(gspca_dev, 0x02, clockdiv); + } +} + +static void setgain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gainreg; + + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) + sensor_write1(gspca_dev, 0x0e, val); + else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2) + for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) { + sensor_write1(gspca_dev, gainreg, val >> 8); + sensor_write1(gspca_dev, gainreg + 1, val & 0xff); + } + else + sensor_write1(gspca_dev, 0x10, val); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + sensor_write1(gspca_dev, 0x1c, val); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, sd->exposure->val, + sd->min_clockdiv ? sd->min_clockdiv->val : 0); + break; + case V4L2_CID_GAIN: + setgain(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + static const struct v4l2_ctrl_config clockdiv = { + .ops = &sd_ctrl_ops, + .id = MR97310A_CID_CLOCKDIV, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Minimum Clock Divider", + .min = MR97310A_MIN_CLOCKDIV_MIN, + .max = MR97310A_MIN_CLOCKDIV_MAX, + .step = 1, + .def = MR97310A_MIN_CLOCKDIV_DEFAULT, + }; + bool has_brightness = false; + bool has_argus_brightness = false; + bool has_contrast = false; + bool has_gain = false; + bool has_cs_gain = false; + bool has_exposure = false; + bool has_clockdiv = false; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + /* Setup controls depending on camera type */ + if (sd->cam_type == CAM_TYPE_CIF) { + /* No brightness for sensor_type 0 */ + if (sd->sensor_type == 0) + has_exposure = has_gain = has_clockdiv = true; + else + has_exposure = has_gain = has_brightness = true; + } else { + /* All controls need to be disabled if VGA sensor_type is 0 */ + if (sd->sensor_type == 0) + ; /* no controls! */ + else if (sd->sensor_type == 2) + has_exposure = has_cs_gain = has_contrast = true; + else if (sd->do_lcd_stop) + has_exposure = has_gain = has_argus_brightness = + has_clockdiv = true; + else + has_exposure = has_gain = has_brightness = + has_clockdiv = true; + } + + /* Separate brightness control description for Argus QuickClix as it has + * different limits from the other mr97310a cameras, and separate gain + * control for Sakar CyberPix camera. */ + /* + * This control is disabled for CIF type 1 and VGA type 0 cameras. + * It does not quite act linearly for the Argus QuickClix camera, + * but it does control brightness. The values are 0 - 15 only, and + * the table above makes them act consecutively. + */ + if (has_brightness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, -254, 255, 1, + MR97310A_BRIGHTNESS_DEFAULT); + else if (has_argus_brightness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 15, 1, + MR97310A_BRIGHTNESS_DEFAULT); + if (has_contrast) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN, + MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT); + if (has_gain) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX, + 1, MR97310A_GAIN_DEFAULT); + else if (has_cs_gain) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN, + MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX, + 1, MR97310A_CS_GAIN_DEFAULT); + if (has_exposure) + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN, + MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT); + if (has_clockdiv) + sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + if (has_exposure && has_clockdiv) + v4l2_ctrl_cluster(2, &sd->exposure); + return 0; +} + +/* Include pac common sof detection functions */ +#include "pac_common.h" + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char *sof; + + sof = pac_find_sof(&sd->sof_read, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sizeof pac_sof_marker) + n -= sizeof pac_sof_marker; + else + n = 0; + gspca_frame_add(gspca_dev, LAST_PACKET, + data, n); + /* Start next frame. */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_sof_marker, sizeof pac_sof_marker); + len -= sof - data; + data = sof; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ + {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ + {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ + {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/nw80x.c b/drivers/media/usb/gspca/nw80x.c new file mode 100644 index 000000000000..44c9964b1b3e --- /dev/null +++ b/drivers/media/usb/gspca/nw80x.c @@ -0,0 +1,2110 @@ +/* + * DivIO nw80x subdriver + * + * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr) + * Copyright (C) 2003 Sylvain Munaut + * Kjell Claesson + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "nw80x" + +#include "gspca.h" + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("NW80x USB Camera Driver"); +MODULE_LICENSE("GPL"); + +static int webcam; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u32 ae_res; + s8 ag_cnt; +#define AG_CNT_START 13 + u8 exp_too_low_cnt; + u8 exp_too_high_cnt; + + u8 bridge; + u8 webcam; +}; + +enum bridges { + BRIDGE_NW800, /* and et31x110 */ + BRIDGE_NW801, + BRIDGE_NW802, +}; +enum webcams { + Generic800, + SpaceCam, /* Trust 120 SpaceCam */ + SpaceCam2, /* other Trust 120 SpaceCam */ + Cvideopro, /* Conceptronic Video Pro */ + Dlink350c, + DS3303u, + Kr651us, + Kritter, + Mustek300, + Proscope, + Twinkle, + DvcV6, + P35u, + Generic802, + NWEBCAMS /* number of webcams */ +}; + +static const u8 webcam_chip[NWEBCAMS] = { + [Generic800] = BRIDGE_NW800, /* 06a5:0000 + * Typhoon Webcam 100 USB */ + + [SpaceCam] = BRIDGE_NW800, /* 06a5:d800 + * Trust SpaceCam120 or SpaceCam100 PORTABLE */ + + [SpaceCam2] = BRIDGE_NW800, /* 06a5:d800 - pas106 + * other Trust SpaceCam120 or SpaceCam100 PORTABLE */ + + [Cvideopro] = BRIDGE_NW802, /* 06a5:d001 + * Conceptronic Video Pro 'CVIDEOPRO USB Webcam CCD' */ + + [Dlink350c] = BRIDGE_NW802, /* 06a5:d001 + * D-Link NetQam Pro 250plus */ + + [DS3303u] = BRIDGE_NW801, /* 06a5:d001 + * Plustek Opticam 500U or ProLink DS3303u */ + + [Kr651us] = BRIDGE_NW802, /* 06a5:d001 + * Panasonic GP-KR651US */ + + [Kritter] = BRIDGE_NW802, /* 06a5:d001 + * iRez Kritter cam */ + + [Mustek300] = BRIDGE_NW802, /* 055f:d001 + * Mustek Wcam 300 mini */ + + [Proscope] = BRIDGE_NW802, /* 06a5:d001 + * Scalar USB Microscope (ProScope) */ + + [Twinkle] = BRIDGE_NW800, /* 06a5:d800 - hv7121b? (seems pas106) + * Divio Chicony TwinkleCam + * DSB-C110 */ + + [DvcV6] = BRIDGE_NW802, /* 0502:d001 + * DVC V6 */ + + [P35u] = BRIDGE_NW801, /* 052b:d001, 06a5:d001 and 06be:d001 + * EZCam Pro p35u */ + + [Generic802] = BRIDGE_NW802, +}; +/* + * other webcams: + * - nw801 046d:d001 + * Logitech QuickCam Pro (dark focus ring) + * - nw801 0728:d001 + * AVerMedia Camguard + * - nw??? 06a5:d001 + * D-Link NetQam Pro 250plus + * - nw800 065a:d800 + * Showcam NGS webcam + * - nw??? ????:???? + * Sceptre svc300 + */ + +/* + * registers + * nw800/et31x110 nw801 nw802 + * 0000..009e 0000..00a1 0000..009e + * 0200..0211 id id + * 0300..0302 id id + * 0400..0406 (inex) 0400..0406 + * 0500..0505 0500..0506 (inex) + * 0600..061a 0600..0601 0600..0601 + * 0800..0814 id id + * 1000..109c 1000..10a1 1000..109a + */ + +/* resolutions + * nw800: 320x240, 352x288 + * nw801/802: 320x240, 640x480 + */ +static const struct v4l2_pix_format cif_mode[] = { + {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8, + .colorspace = V4L2_COLORSPACE_JPEG}, + {352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 4 / 8, + .colorspace = V4L2_COLORSPACE_JPEG} +}; +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +/* + * The sequences below contain: + * - 1st and 2nd bytes: either + * - register number (BE) + * - I2C0 + i2c address + * - 3rd byte: data length (=0 for end of sequence) + * - n bytes: data + */ +#define I2C0 0xff + +static const u8 nw800_init[] = { + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x05, 0x01, 0x00, + 0, 0, 0 +}; +static const u8 nw800_start[] = { + 0x04, 0x06, 0x01, 0xc0, + 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, + 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, + 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0, + 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + + 0x04, 0x04, 0x01, 0xff, + 0x04, 0x06, 0x01, 0xc4, + + 0x04, 0x06, 0x01, 0xc0, + 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, + 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, + 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0, + 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, + 0x40, + 0x00, 0x80, 0x01, 0xa0, + 0x10, 0x1a, 0x01, 0x00, + 0x00, 0x91, 0x02, 0x6c, 0x01, + 0x00, 0x03, 0x02, 0xc8, 0x01, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0x83, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x10, 0x1b, 0x02, 0x69, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x05, 0x02, 0x01, 0x02, + 0x06, 0x00, 0x02, 0x04, 0xd9, + 0x05, 0x05, 0x01, 0x20, + 0x05, 0x05, 0x01, 0x21, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83, + 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0, + 0xea, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x13, 0x13, + 0x10, 0x03, 0x01, 0x14, + 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83, + 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0, + 0xea, + 0x10, 0x0b, 0x01, 0x14, + 0x10, 0x0d, 0x01, 0x20, + 0x10, 0x0c, 0x01, 0x34, + 0x04, 0x06, 0x01, 0xc3, + 0x04, 0x04, 0x01, 0x00, + 0x05, 0x02, 0x01, 0x02, + 0x06, 0x00, 0x02, 0x00, 0x48, + 0x05, 0x05, 0x01, 0x20, + 0x05, 0x05, 0x01, 0x21, + 0, 0, 0 +}; + +/* 06a5:d001 - nw801 - Panasonic + * P35u */ +static const u8 nw801_start_1[] = { + 0x05, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, + 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x69, 0xa8, 0x1f, 0x00, + 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00, + 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00, + 0x36, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x22, 0x02, 0x80, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x15, 0x08, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x35, 0xfd, 0x07, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x14, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7, + 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80, + 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, 0xa4, + 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, 0xcf, + 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, + 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, + 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x82, 0x02, + 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01, + 0xf0, 0x00, + 0, 0, 0, +}; +static const u8 nw801_start_qvga[] = { + 0x02, 0x00, 0x10, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x18, 0x0b, 0x06, 0xa2, 0x86, 0x78, + 0x02, 0x0f, 0x01, 0x6b, + 0x10, 0x1a, 0x01, 0x15, + 0x00, 0x00, 0x01, 0x1e, + 0x10, 0x00, 0x01, 0x2f, + 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00, + /* AE window */ + 0, 0, 0, +}; +static const u8 nw801_start_vga[] = { + 0x02, 0x00, 0x10, 0x78, 0xa0, 0x97, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xf0, + 0x02, 0x0f, 0x01, 0xd5, + 0x10, 0x1a, 0x01, 0x15, + 0x00, 0x00, 0x01, 0x0e, + 0x10, 0x00, 0x01, 0x22, + 0x10, 0x8c, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, + 0, 0, 0, +}; +static const u8 nw801_start_2[] = { + 0x10, 0x04, 0x01, 0x1a, + 0x10, 0x19, 0x01, 0x09, /* clock */ + 0x10, 0x24, 0x06, 0xc0, 0x00, 0x3f, 0x02, 0x00, 0x01, + /* .. gain .. */ + 0x00, 0x03, 0x02, 0x92, 0x03, + 0x00, 0x1d, 0x04, 0xf2, 0x00, 0x24, 0x07, + 0x00, 0x7b, 0x01, 0xcf, + 0x10, 0x94, 0x01, 0x07, + 0x05, 0x05, 0x01, 0x01, + 0x05, 0x04, 0x01, 0x01, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8, + 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0, + 0xf0, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x08, + 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8, + 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0, + 0xf0, + 0x10, 0x0b, 0x01, 0x0b, + 0x10, 0x0d, 0x01, 0x0b, + 0x10, 0x0c, 0x01, 0x1f, + 0x05, 0x06, 0x01, 0x03, + 0, 0, 0 +}; + +/* nw802 (sharp IR3Y38M?) */ +static const u8 nw802_start[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x4d, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x08, 0x00, 0x18, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0xff, 0x01, 0xc0, 0x00, 0x14, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0xad, + 0x00, 0x00, 0x01, 0x08, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00, + 0x10, 0x1d, 0x08, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, + 0x10, 0x0e, 0x01, 0x27, + 0x10, 0x41, 0x11, 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b, + 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2, + 0xd8, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x14, 0x14, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, 0xf1, + 0xff, +/* 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b, + * 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2, + * 0xd8, */ + 0x10, 0x0b, 0x01, 0x10, + 0x10, 0x0d, 0x01, 0x11, + 0x10, 0x0c, 0x01, 0x1c, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; +/* et31x110 - Trust 120 SpaceCam */ +static const u8 spacecam_init[] = { + 0x04, 0x05, 0x01, 0x01, + 0x04, 0x04, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x05, 0x01, 0x00, + 0, 0, 0 +}; +static const u8 spacecam_start[] = { + 0x04, 0x06, 0x01, 0x44, + 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, + 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, + 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x7c, 0x00, 0x80, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + 0x04, 0x06, 0x01, 0xc0, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, + 0x40, + 0x00, 0x80, 0x01, 0xa0, + 0x10, 0x1a, 0x01, 0x00, + 0x00, 0x91, 0x02, 0x32, 0x01, + 0x00, 0x03, 0x02, 0x08, 0x02, + 0x10, 0x00, 0x01, 0x83, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x13, 0x13, + 0x10, 0x03, 0x01, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x0b, 0x01, 0x08, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1f, + 0x04, 0x06, 0x01, 0xc3, + 0x04, 0x05, 0x01, 0x40, + 0x04, 0x04, 0x01, 0x40, + 0, 0, 0 +}; +/* et31x110 - pas106 - other Trust SpaceCam120 */ +static const u8 spacecam2_start[] = { + 0x04, 0x06, 0x01, 0x44, + 0x04, 0x06, 0x01, 0x00, + 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, + 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, + 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x04, 0x04, 0x01, 0x40, + 0x04, 0x04, 0x01, 0x00, + I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x05, + I2C0, 0x40, 0x02, 0x11, 0x06, + I2C0, 0x40, 0x02, 0x14, 0x00, + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, + 0x40, + I2C0, 0x40, 0x02, 0x02, 0x0c, /* pixel clock */ + I2C0, 0x40, 0x02, 0x0f, 0x00, + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ + 0x10, 0x00, 0x01, 0x01, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, + I2C0, 0x40, 0x02, 0x05, 0x0f, /* exposure */ + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ + I2C0, 0x40, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, + /* gains */ + I2C0, 0x40, 0x03, 0x12, 0x04, 0x01, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x13, 0x13, + 0x10, 0x03, 0x01, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x0b, 0x01, 0x11, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x14, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - Conceptronic Video Pro */ +static const u8 cvideopro_start[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, + 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, + 0x40, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xac, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x3b, 0x01, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x1d, 0x02, 0x40, 0x06, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0, + 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc, + 0xdc, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x12, 0x12, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0, + 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc, + 0xdc, + 0x10, 0x0b, 0x01, 0x09, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x2f, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - D-link dru-350c cam */ +static const u8 dlink_start[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0xad, + 0x00, 0x00, 0x01, 0x08, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00, + 0x10, 0x1d, 0x08, 0x40, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x0e, 0x01, 0x20, + 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f, + 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf, + 0xea, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x11, 0x11, + 0x10, 0x03, 0x01, 0x10, + 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f, + 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf, + 0xea, + 0x10, 0x0b, 0x01, 0x19, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1e, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* 06a5:d001 - nw801 - Sony + * Plustek Opticam 500U or ProLink DS3303u (Hitachi HD49322BF) */ +/*fixme: 320x240 only*/ +static const u8 ds3303_start[] = { + 0x05, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x16, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, + 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa9, 0xa8, 0x1f, 0x00, + 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00, + 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00, + 0x36, 0x00, + 0x02, 0x00, 0x12, 0x03, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0x50, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x2f, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a, + 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4, + 0x00, 0x01, 0x15, 0xfd, 0x07, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x8c, 0x04, 0x01, 0x20, + 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7, + 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80, + 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, 0x88, + 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, 0xcb, + 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, + 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, + 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, 0x01, + 0x00, 0x00, 0xef, 0x00, 0x02, 0x0a, 0x82, 0x02, + 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01, + 0xf0, 0x00, + + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x00, 0xf2, 0x8f, 0x81, + 0x40, + 0x10, 0x1a, 0x01, 0x15, + 0x10, 0x00, 0x01, 0x2f, + 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x26, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x24, 0x02, 0x40, 0x06, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6, + 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x16, 0x16, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6, + 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x0b, 0x01, 0x26, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1c, + 0x05, 0x06, 0x01, 0x03, + 0x05, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* 06a5:d001 - nw802 - Panasonic + * GP-KR651US (Philips TDA8786) */ +static const u8 kr651_start_1[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x48, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, + 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0, 0, 0 +}; +static const u8 kr651_start_qvga[] = { + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xac, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00, + 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, + 0x10, 0x1d, 0x02, 0x28, 0x01, + 0, 0, 0 +}; +static const u8 kr651_start_vga[] = { + 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x30, 0x03, 0x01, 0x82, 0x82, 0x98, + 0x80, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xa0, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, + 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, + 0x10, 0x1d, 0x02, 0x68, 0x00, +}; +static const u8 kr651_start_2[] = { + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8, + 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdc, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8, + 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdc, + 0x10, 0x0b, 0x01, 0x10, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x2d, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - iRez Kritter cam */ +static const u8 kritter_start[] = { + 0x04, 0x06, 0x01, 0x06, + 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0x94, 0x03, 0x18, 0x00, 0x48, + 0x0f, 0x1e, 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0x0a, 0x01, 0x28, + 0x07, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0x0e, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0b, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, + 0x40, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xaf, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x3b, 0x01, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, + 0x10, 0x1d, 0x02, 0x00, 0x00, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86, + 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4, + 0xcb, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0d, 0x0d, + 0x10, 0x03, 0x01, 0x02, + 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86, + 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4, + 0xcb, + 0x10, 0x0b, 0x01, 0x17, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1e, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - Mustek Wcam 300 mini */ +static const u8 mustek_start[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, + 0x00, 0x10, 0x06, 0xfc, 0x05, 0x0c, 0x06, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0xad, + 0x00, 0x00, 0x01, 0x08, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1d, 0x08, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x10, 0x0e, 0x01, 0x0f, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e, + 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9, + 0xff, + 0x10, 0x0f, 0x02, 0x11, 0x11, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e, + 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9, + 0xff, + 0x10, 0x0b, 0x01, 0x1c, + 0x10, 0x0d, 0x01, 0x1a, + 0x10, 0x0c, 0x01, 0x34, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x40, + 0x04, 0x06, 0x01, 0x03, + 0, 0, 0 +}; + +/* nw802 - Scope USB Microscope M2 (ProScope) (Hitachi HD49322BF) */ +static const u8 proscope_init[] = { + 0x04, 0x05, 0x01, 0x21, + 0x04, 0x04, 0x01, 0x01, + 0, 0, 0 +}; +static const u8 proscope_start_1[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x01, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x04, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x08, 0x00, 0x17, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0xce, 0x00, 0xf8, 0x03, 0x3e, 0x00, 0x86, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0xb6, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xf6, 0x03, 0x34, 0x04, 0xf6, 0x03, 0x34, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xe8, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x1f, 0x0f, 0x08, 0x20, 0xa8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xad, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a, + 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x8c, 0x04, 0x01, + 0x20, 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, + 0x88, 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, + 0xcb, 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, + 0x01, 0x00, 0x00, 0xef, 0x00, 0x09, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0, 0, 0 +}; +static const u8 proscope_start_qvga[] = { + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x06, + 0x00, 0x03, 0x02, 0xf9, 0x02, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x0e, 0x01, 0x10, + 0, 0, 0 +}; +static const u8 proscope_start_vga[] = { + 0x00, 0x03, 0x02, 0xf9, 0x02, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x16, 0x00, 0x00, 0x82, 0x84, 0x00, + 0x80, + 0x10, 0x1a, 0x01, 0x06, + 0x10, 0x00, 0x01, 0xa1, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + 0x10, 0x0e, 0x01, 0x10, + 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae, + 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0, 0, 0 +}; +static const u8 proscope_start_2[] = { + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae, + 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2, + 0xf9, + 0x10, 0x0b, 0x01, 0x0b, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1b, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x05, 0x01, 0x21, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw800 - hv7121b? (seems pas106) - Divio Chicony TwinkleCam */ +static const u8 twinkle_start[] = { + 0x04, 0x06, 0x01, 0x44, + 0x04, 0x06, 0x01, 0x00, + 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, + 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, + 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x08, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x10, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x00, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x04, 0x04, 0x01, 0x10, + 0x04, 0x04, 0x01, 0x00, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, + I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, + I2C0, 0x40, 0x02, 0x11, 0x06, + I2C0, 0x40, 0x02, 0x14, 0x00, + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ + I2C0, 0x40, 0x02, 0x07, 0x01, + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, + 0x40, + I2C0, 0x40, 0x02, 0x02, 0x0c, + I2C0, 0x40, 0x02, 0x13, 0x01, + 0x10, 0x00, 0x01, 0x01, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, + I2C0, 0x40, 0x02, 0x05, 0x0f, + I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, + I2C0, 0x40, 0x03, 0x12, 0x00, 0x01, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + I2C0, 0x40, 0x02, 0x12, 0x00, + I2C0, 0x40, 0x02, 0x0e, 0x00, + I2C0, 0x40, 0x02, 0x11, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x0b, 0x01, 0x19, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x0d, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x41, + 0, 0, 0 +}; + +/* nw802 dvc-v6 */ +static const u8 dvcv6_start[] = { + 0x04, 0x06, 0x01, 0x06, + 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, + 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x00, 0x03, 0x02, 0x94, 0x03, + 0x00, 0x1d, 0x04, 0x0a, 0x01, 0x28, 0x07, + 0x00, 0x7b, 0x02, 0xe0, 0x00, + 0x10, 0x8d, 0x01, 0x00, + 0x00, 0x09, 0x04, 0x1e, 0x00, 0x0c, 0x02, + 0x00, 0x91, 0x02, 0x0b, 0x02, + 0x10, 0x00, 0x01, 0xaf, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8f, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, + 0x40, + 0x10, 0x1a, 0x01, 0x02, + 0x10, 0x00, 0x01, 0xaf, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x07, 0x01, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x1d, 0x02, 0x40, 0x06, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa, + 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc, + 0xdc, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x12, 0x12, + 0x10, 0x03, 0x01, 0x11, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa, + 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc, + 0xdc, + 0x10, 0x0b, 0x01, 0x16, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1a, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, +}; + +static const u8 *webcam_start[] = { + [Generic800] = nw800_start, + [SpaceCam] = spacecam_start, + [SpaceCam2] = spacecam2_start, + [Cvideopro] = cvideopro_start, + [Dlink350c] = dlink_start, + [DS3303u] = ds3303_start, + [Kr651us] = kr651_start_1, + [Kritter] = kritter_start, + [Mustek300] = mustek_start, + [Proscope] = proscope_start_1, + [Twinkle] = twinkle_start, + [DvcV6] = dvcv6_start, + [P35u] = nw801_start_1, + [Generic802] = nw802_start, +}; + +/* -- write a register -- */ +static void reg_w(struct gspca_dev *gspca_dev, + u16 index, + const u8 *data, + int len) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + if (len == 1) + PDEBUG(D_USBO, "SET 00 0000 %04x %02x", index, *data); + else + PDEBUG(D_USBO, "SET 00 0000 %04x %02x %02x ...", + index, *data, data[1]); + memcpy(gspca_dev->usb_buf, data, len); + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x00, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, /* value */ + index, + gspca_dev->usb_buf, + len, + 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* -- read registers in usb_buf -- */ +static void reg_r(struct gspca_dev *gspca_dev, + u16 index, + int len) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, index, + gspca_dev->usb_buf, len, 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + return; + } + if (len == 1) + PDEBUG(D_USBI, "GET 00 0000 %04x %02x", + index, gspca_dev->usb_buf[0]); + else + PDEBUG(D_USBI, "GET 00 0000 %04x %02x %02x ..", + index, gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1]); +} + +static void i2c_w(struct gspca_dev *gspca_dev, + u8 i2c_addr, + const u8 *data, + int len) +{ + u8 val[2]; + int i; + + reg_w(gspca_dev, 0x0600, data + 1, len - 1); + reg_w(gspca_dev, 0x0600, data, len); + val[0] = len; + val[1] = i2c_addr; + reg_w(gspca_dev, 0x0502, val, 2); + val[0] = 0x01; + reg_w(gspca_dev, 0x0501, val, 1); + for (i = 5; --i >= 0; ) { + msleep(4); + reg_r(gspca_dev, 0x0505, 1); + if (gspca_dev->usb_err < 0) + return; + if (gspca_dev->usb_buf[0] == 0) + return; + } + gspca_dev->usb_err = -ETIME; +} + +static void reg_w_buf(struct gspca_dev *gspca_dev, + const u8 *cmd) +{ + u16 reg; + int len; + + for (;;) { + reg = *cmd++ << 8; + reg += *cmd++; + len = *cmd++; + if (len == 0) + break; + if (cmd[-3] != I2C0) + reg_w(gspca_dev, reg, cmd, len); + else + i2c_w(gspca_dev, reg, cmd, len); + cmd += len; + } +} + +static int swap_bits(int v) +{ + int r, i; + + r = 0; + for (i = 0; i < 8; i++) { + r <<= 1; + if (v & 1) + r++; + v >>= 1; + } + return r; +} + +static void setgain(struct gspca_dev *gspca_dev, u8 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 v[2]; + + switch (sd->webcam) { + case P35u: + reg_w(gspca_dev, 0x1026, &val, 1); + break; + case Kr651us: + /* 0 - 253 */ + val = swap_bits(val); + v[0] = val << 3; + v[1] = val >> 5; + reg_w(gspca_dev, 0x101d, v, 2); /* SIF reg0/1 (AGC) */ + break; + } +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 v[2]; + + switch (sd->webcam) { + case P35u: + v[0] = ((9 - val) << 3) | 0x01; + reg_w(gspca_dev, 0x1019, v, 1); + break; + case Cvideopro: + case DvcV6: + case Kritter: + case Kr651us: + v[0] = val; + v[1] = val >> 8; + reg_w(gspca_dev, 0x101b, v, 2); + break; + } +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int w, h; + + if (!val) { + sd->ag_cnt = -1; + return; + } + sd->ag_cnt = AG_CNT_START; + + reg_r(gspca_dev, 0x1004, 1); + if (gspca_dev->usb_buf[0] & 0x04) { /* if AE_FULL_FRM */ + sd->ae_res = gspca_dev->width * gspca_dev->height; + } else { /* get the AE window size */ + reg_r(gspca_dev, 0x1011, 8); + w = (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0] + - (gspca_dev->usb_buf[3] << 8) - gspca_dev->usb_buf[2]; + h = (gspca_dev->usb_buf[5] << 8) + gspca_dev->usb_buf[4] + - (gspca_dev->usb_buf[7] << 8) - gspca_dev->usb_buf[6]; + sd->ae_res = h * w; + if (sd->ae_res == 0) + sd->ae_res = gspca_dev->width * gspca_dev->height; + } +} + +static int nw802_test_reg(struct gspca_dev *gspca_dev, + u16 index, + u8 value) +{ + /* write the value */ + reg_w(gspca_dev, index, &value, 1); + + /* read it */ + reg_r(gspca_dev, index, 1); + + return gspca_dev->usb_buf[0] == value; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if ((unsigned) webcam >= NWEBCAMS) + webcam = 0; + sd->webcam = webcam; + gspca_dev->cam.needs_full_bandwidth = 1; + sd->ag_cnt = -1; + + /* + * Autodetect sequence inspired from some log. + * We try to detect what registers exist or not. + * If 0x0500 does not exist => NW802 + * If it does, test 0x109b. If it doesn't exist, + * then it's a NW801. Else, a NW800 + * If a et31x110 (nw800 and 06a5:d800) + * get the sensor ID + */ + if (!nw802_test_reg(gspca_dev, 0x0500, 0x55)) { + sd->bridge = BRIDGE_NW802; + if (sd->webcam == Generic800) + sd->webcam = Generic802; + } else if (!nw802_test_reg(gspca_dev, 0x109b, 0xaa)) { + sd->bridge = BRIDGE_NW801; + if (sd->webcam == Generic800) + sd->webcam = P35u; + } else if (id->idVendor == 0x06a5 && id->idProduct == 0xd800) { + reg_r(gspca_dev, 0x0403, 1); /* GPIO */ + PDEBUG(D_PROBE, "et31x110 sensor type %02x", + gspca_dev->usb_buf[0]); + switch (gspca_dev->usb_buf[0] >> 1) { + case 0x00: /* ?? */ + if (sd->webcam == Generic800) + sd->webcam = SpaceCam; + break; + case 0x01: /* Hynix? */ + if (sd->webcam == Generic800) + sd->webcam = Twinkle; + break; + case 0x0a: /* Pixart */ + if (sd->webcam == Generic800) + sd->webcam = SpaceCam2; + break; + } + } + if (webcam_chip[sd->webcam] != sd->bridge) { + pr_err("Bad webcam type %d for NW80%d\n", + sd->webcam, sd->bridge); + gspca_dev->usb_err = -ENODEV; + return gspca_dev->usb_err; + } + PDEBUG(D_PROBE, "Bridge nw80%d - type: %d", sd->bridge, sd->webcam); + + if (sd->bridge == BRIDGE_NW800) { + switch (sd->webcam) { + case DS3303u: + gspca_dev->cam.cam_mode = cif_mode; /* qvga */ + break; + default: + gspca_dev->cam.cam_mode = &cif_mode[1]; /* cif */ + break; + } + gspca_dev->cam.nmodes = 1; + } else { + gspca_dev->cam.cam_mode = vga_mode; + switch (sd->webcam) { + case Kr651us: + case Proscope: + case P35u: + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + break; + default: + gspca_dev->cam.nmodes = 1; /* qvga only */ + break; + } + } + + return gspca_dev->usb_err; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + case BRIDGE_NW800: + switch (sd->webcam) { + case SpaceCam: + reg_w_buf(gspca_dev, spacecam_init); + break; + default: + reg_w_buf(gspca_dev, nw800_init); + break; + } + break; + default: + switch (sd->webcam) { + case Mustek300: + case P35u: + case Proscope: + reg_w_buf(gspca_dev, proscope_init); + break; + } + break; + } + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const u8 *cmd; + + cmd = webcam_start[sd->webcam]; + reg_w_buf(gspca_dev, cmd); + switch (sd->webcam) { + case P35u: + if (gspca_dev->width == 320) + reg_w_buf(gspca_dev, nw801_start_qvga); + else + reg_w_buf(gspca_dev, nw801_start_vga); + reg_w_buf(gspca_dev, nw801_start_2); + break; + case Kr651us: + if (gspca_dev->width == 320) + reg_w_buf(gspca_dev, kr651_start_qvga); + else + reg_w_buf(gspca_dev, kr651_start_vga); + reg_w_buf(gspca_dev, kr651_start_2); + break; + case Proscope: + if (gspca_dev->width == 320) + reg_w_buf(gspca_dev, proscope_start_qvga); + else + reg_w_buf(gspca_dev, proscope_start_vga); + reg_w_buf(gspca_dev, proscope_start_2); + break; + } + + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 value; + + /* 'go' off */ + if (sd->bridge != BRIDGE_NW801) { + value = 0x02; + reg_w(gspca_dev, 0x0406, &value, 1); + } + + /* LED off */ + switch (sd->webcam) { + case Cvideopro: + case Kr651us: + case DvcV6: + case Kritter: + value = 0xff; + break; + case Dlink350c: + value = 0x21; + break; + case SpaceCam: + case SpaceCam2: + case Proscope: + case Twinkle: + value = 0x01; + break; + default: + return; + } + reg_w(gspca_dev, 0x0404, &value, 1); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* + * frame header = '00 00 hh ww ss xx ff ff' + * with: + * - 'hh': height / 4 + * - 'ww': width / 4 + * - 'ss': frame sequence number c0..dd + */ + if (data[0] == 0x00 && data[1] == 0x00 + && data[6] == 0xff && data[7] == 0xff) { + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, data + 8, len - 8); + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + } +} + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int luma; + + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; + + /* get the average luma */ + reg_r(gspca_dev, sd->bridge == BRIDGE_NW801 ? 0x080d : 0x080c, 4); + luma = (gspca_dev->usb_buf[3] << 24) + (gspca_dev->usb_buf[2] << 16) + + (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; + luma /= sd->ae_res; + + switch (sd->webcam) { + case P35u: + gspca_coarse_grained_expo_autogain(gspca_dev, luma, 100, 5); + break; + default: + gspca_expo_autogain(gspca_dev, luma, 100, 5, 230, 0); + break; + } +} + + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* autogain/gain/exposure control cluster */ + case V4L2_CID_AUTOGAIN: + if (ctrl->is_new) + setautogain(gspca_dev, ctrl->val); + if (!ctrl->val) { + if (gspca_dev->gain->is_new) + setgain(gspca_dev, gspca_dev->gain->val); + if (gspca_dev->exposure->is_new) + setexposure(gspca_dev, + gspca_dev->exposure->val); + } + break; + /* Some webcams only have exposure, so handle that separately from the + autogain/gain/exposure cluster in the previous case. */ + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, gspca_dev->exposure->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 3); + switch (sd->webcam) { + case P35u: + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + /* For P35u choose coarse expo auto gain function gain minimum, + * to avoid a large settings jump the first auto adjustment */ + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 127 / 5 * 2); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 9, 1, 9); + break; + case Kr651us: + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 253, 1, 128); + /* fall through */ + case Cvideopro: + case DvcV6: + case Kritter: + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 315, 1, 150); + break; + default: + break; + } + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + if (gspca_dev->autogain) + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0xd001)}, + {USB_DEVICE(0x0502, 0xd001)}, + {USB_DEVICE(0x052b, 0xd001)}, + {USB_DEVICE(0x055f, 0xd001)}, + {USB_DEVICE(0x06a5, 0x0000)}, + {USB_DEVICE(0x06a5, 0xd001)}, + {USB_DEVICE(0x06a5, 0xd800)}, + {USB_DEVICE(0x06be, 0xd001)}, + {USB_DEVICE(0x0728, 0xd001)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); + +module_param(webcam, int, 0644); +MODULE_PARM_DESC(webcam, + "Webcam type\n" + "0: generic\n" + "1: Trust 120 SpaceCam\n" + "2: other Trust 120 SpaceCam\n" + "3: Conceptronic Video Pro\n" + "4: D-link dru-350c\n" + "5: Plustek Opticam 500U\n" + "6: Panasonic GP-KR651US\n" + "7: iRez Kritter\n" + "8: Mustek Wcam 300 mini\n" + "9: Scalar USB Microscope M2 (Proscope)\n" + "10: Divio Chicony TwinkleCam\n" + "11: DVC-V6\n"); diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c new file mode 100644 index 000000000000..bfc7cefa59f8 --- /dev/null +++ b/drivers/media/usb/gspca/ov519.c @@ -0,0 +1,4991 @@ +/** + * OV519 driver + * + * Copyright (C) 2008-2011 Jean-François Moine + * Copyright (C) 2009 Hans de Goede + * + * This module is adapted from the ov51x-jpeg package, which itself + * was adapted from the ov511 driver. + * + * Original copyright for the ov511 driver is: + * + * Copyright (c) 1999-2006 Mark W. McClelland + * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach + * Many improvements by Bret Wallach + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * OV7620 fixes by Charl P. Botha + * Changes by Claudio Matsuoka + * + * ov51x-jpeg original copyright is: + * + * Copyright (c) 2004-2007 Romain Beauxis + * Support for OV7670 sensors was contributed by Sam Skipsey + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "ov519" + +#include +#include "gspca.h" + +/* The jpeg_hdr is used by w996Xcf only */ +/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ +#define CONEX_CAM +#include "jpeg.h" + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("OV519 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* global parameters */ +static int frame_rate; + +/* Number of times to retry a failed I2C transaction. Increase this if you + * are getting "Failed to read sensor ID..." */ +static int i2c_detect_tries = 10; + +/* ov519 device descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct v4l2_ctrl *jpegqual; + struct v4l2_ctrl *freq; + struct { /* h/vflip control cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct { /* autobrightness/brightness control cluster */ + struct v4l2_ctrl *autobright; + struct v4l2_ctrl *brightness; + }; + + u8 packet_nr; + + char bridge; +#define BRIDGE_OV511 0 +#define BRIDGE_OV511PLUS 1 +#define BRIDGE_OV518 2 +#define BRIDGE_OV518PLUS 3 +#define BRIDGE_OV519 4 /* = ov530 */ +#define BRIDGE_OVFX2 5 +#define BRIDGE_W9968CF 6 +#define BRIDGE_MASK 7 + + char invert_led; +#define BRIDGE_INVERT_LED 8 + + char snapshot_pressed; + char snapshot_needs_reset; + + /* Determined by sensor type */ + u8 sif; + +#define QUALITY_MIN 50 +#define QUALITY_MAX 70 +#define QUALITY_DEF 50 + + u8 stopped; /* Streaming is temporarily paused */ + u8 first_frame; + + u8 frame_rate; /* current Framerate */ + u8 clockdiv; /* clockdiv override */ + + s8 sensor; /* Type of image sensor chip (SEN_*) */ + + u8 sensor_addr; + u16 sensor_width; + u16 sensor_height; + s16 sensor_reg_cache[256]; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; +enum sensors { + SEN_OV2610, + SEN_OV2610AE, + SEN_OV3610, + SEN_OV6620, + SEN_OV6630, + SEN_OV66308AF, + SEN_OV7610, + SEN_OV7620, + SEN_OV7620AE, + SEN_OV7640, + SEN_OV7648, + SEN_OV7660, + SEN_OV7670, + SEN_OV76BE, + SEN_OV8610, + SEN_OV9600, +}; + +/* Note this is a bit of a hack, but the w9968cf driver needs the code for all + the ov sensors which is already present here. When we have the time we + really should move the sensor drivers to v4l2 sub drivers. */ +#include "w996Xcf.c" + +/* table of the disabled controls */ +struct ctrl_valid { + int has_brightness:1; + int has_contrast:1; + int has_exposure:1; + int has_autogain:1; + int has_sat:1; + int has_hvflip:1; + int has_autobright:1; + int has_freq:1; +}; + +static const struct ctrl_valid valid_controls[] = { + [SEN_OV2610] = { + .has_exposure = 1, + .has_autogain = 1, + }, + [SEN_OV2610AE] = { + .has_exposure = 1, + .has_autogain = 1, + }, + [SEN_OV3610] = { + /* No controls */ + }, + [SEN_OV6620] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + .has_freq = 1, + }, + [SEN_OV6630] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + .has_freq = 1, + }, + [SEN_OV66308AF] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + .has_freq = 1, + }, + [SEN_OV7610] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + .has_freq = 1, + }, + [SEN_OV7620] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + .has_freq = 1, + }, + [SEN_OV7620AE] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + .has_freq = 1, + }, + [SEN_OV7640] = { + .has_brightness = 1, + .has_sat = 1, + .has_freq = 1, + }, + [SEN_OV7648] = { + .has_brightness = 1, + .has_sat = 1, + .has_freq = 1, + }, + [SEN_OV7660] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_hvflip = 1, + .has_freq = 1, + }, + [SEN_OV7670] = { + .has_brightness = 1, + .has_contrast = 1, + .has_hvflip = 1, + .has_freq = 1, + }, + [SEN_OV76BE] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + .has_freq = 1, + }, + [SEN_OV8610] = { + .has_brightness = 1, + .has_contrast = 1, + .has_sat = 1, + .has_autobright = 1, + }, + [SEN_OV9600] = { + .has_exposure = 1, + .has_autogain = 1, + }, +}; + +static const struct v4l2_pix_format ov519_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov519_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* Note some of the sizeimage values for the ov511 / ov518 may seem + larger then necessary, however they need to be this big as the ov511 / + ov518 always fills the entire isoc frame, using 0 padding bytes when + it doesn't have any data. So with low framerates the amount of data + transferred can become quite large (libv4l will remove all the 0 padding + in userspace). */ +static const struct v4l2_pix_format ov518_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov518_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const struct v4l2_pix_format ov511_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov511_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const struct v4l2_pix_format ovfx2_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_cif_mode[] = { + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_ov2610_mode[] = { + {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; +static const struct v4l2_pix_format ovfx2_ov3610_mode[] = { + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 2048, + .sizeimage = 2048 * 1536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_ov9600_mode[] = { + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; + +/* Registers common to OV511 / OV518 */ +#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ +#define R51x_SYS_RESET 0x50 + /* Reset type flags */ + #define OV511_RESET_OMNICE 0x08 +#define R51x_SYS_INIT 0x53 +#define R51x_SYS_SNAP 0x52 +#define R51x_SYS_CUST_ID 0x5f +#define R51x_COMP_LUT_BEGIN 0x80 + +/* OV511 Camera interface register numbers */ +#define R511_CAM_DELAY 0x10 +#define R511_CAM_EDGE 0x11 +#define R511_CAM_PXCNT 0x12 +#define R511_CAM_LNCNT 0x13 +#define R511_CAM_PXDIV 0x14 +#define R511_CAM_LNDIV 0x15 +#define R511_CAM_UV_EN 0x16 +#define R511_CAM_LINE_MODE 0x17 +#define R511_CAM_OPTS 0x18 + +#define R511_SNAP_FRAME 0x19 +#define R511_SNAP_PXCNT 0x1a +#define R511_SNAP_LNCNT 0x1b +#define R511_SNAP_PXDIV 0x1c +#define R511_SNAP_LNDIV 0x1d +#define R511_SNAP_UV_EN 0x1e +#define R511_SNAP_OPTS 0x1f + +#define R511_DRAM_FLOW_CTL 0x20 +#define R511_FIFO_OPTS 0x31 +#define R511_I2C_CTL 0x40 +#define R511_SYS_LED_CTL 0x55 /* OV511+ only */ +#define R511_COMP_EN 0x78 +#define R511_COMP_LUT_EN 0x79 + +/* OV518 Camera interface register numbers */ +#define R518_GPIO_OUT 0x56 /* OV518(+) only */ +#define R518_GPIO_CTL 0x57 /* OV518(+) only */ + +/* OV519 Camera interface register numbers */ +#define OV519_R10_H_SIZE 0x10 +#define OV519_R11_V_SIZE 0x11 +#define OV519_R12_X_OFFSETL 0x12 +#define OV519_R13_X_OFFSETH 0x13 +#define OV519_R14_Y_OFFSETL 0x14 +#define OV519_R15_Y_OFFSETH 0x15 +#define OV519_R16_DIVIDER 0x16 +#define OV519_R20_DFR 0x20 +#define OV519_R25_FORMAT 0x25 + +/* OV519 System Controller register numbers */ +#define OV519_R51_RESET1 0x51 +#define OV519_R54_EN_CLK1 0x54 +#define OV519_R57_SNAPSHOT 0x57 + +#define OV519_GPIO_DATA_OUT0 0x71 +#define OV519_GPIO_IO_CTRL0 0x72 + +/*#define OV511_ENDPOINT_ADDRESS 1 * Isoc endpoint number */ + +/* + * The FX2 chip does not give us a zero length read at end of frame. + * It does, however, give a short read at the end of a frame, if + * necessary, rather than run two frames together. + * + * By choosing the right bulk transfer size, we are guaranteed to always + * get a short read for the last read of each frame. Frame sizes are + * always a composite number (width * height, or a multiple) so if we + * choose a prime number, we are guaranteed that the last read of a + * frame will be short. + * + * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB, + * otherwise EOVERFLOW "babbling" errors occur. I have not been able + * to figure out why. [PMiller] + * + * The constant (13 * 4096) is the largest "prime enough" number less than 64KB. + * + * It isn't enough to know the number of bytes per frame, in case we + * have data dropouts or buffer overruns (even though the FX2 double + * buffers, there are some pretty strict real time constraints for + * isochronous transfer for larger frame sizes). + */ +/*jfm: this value does not work for 800x600 - see isoc_init */ +#define OVFX2_BULK_SIZE (13 * 4096) + +/* I2C registers */ +#define R51x_I2C_W_SID 0x41 +#define R51x_I2C_SADDR_3 0x42 +#define R51x_I2C_SADDR_2 0x43 +#define R51x_I2C_R_SID 0x44 +#define R51x_I2C_DATA 0x45 +#define R518_I2C_CTL 0x47 /* OV518(+) only */ +#define OVFX2_I2C_ADDR 0x00 + +/* I2C ADDRESSES */ +#define OV7xx0_SID 0x42 +#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */ +#define OV8xx0_SID 0xa0 +#define OV6xx0_SID 0xc0 + +/* OV7610 registers */ +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_BLUE 0x01 /* blue channel balance */ +#define OV7610_REG_RED 0x02 /* red channel balance */ +#define OV7610_REG_SAT 0x03 /* saturation */ +#define OV8610_REG_HUE 0x04 /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ + +/* OV7660 and OV7670 registers */ +#define OV7670_R00_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define OV7670_R01_BLUE 0x01 /* blue gain */ +#define OV7670_R02_RED 0x02 /* red gain */ +#define OV7670_R03_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define OV7670_R04_COM1 0x04 /* Control 1 */ +/*#define OV7670_R07_AECHH 0x07 * AEC MS 5 bits */ +#define OV7670_R0C_COM3 0x0c /* Control 3 */ +#define OV7670_R0D_COM4 0x0d /* Control 4 */ +#define OV7670_R0E_COM5 0x0e /* All "reserved" */ +#define OV7670_R0F_COM6 0x0f /* Control 6 */ +#define OV7670_R10_AECH 0x10 /* More bits of AEC value */ +#define OV7670_R11_CLKRC 0x11 /* Clock control */ +#define OV7670_R12_COM7 0x12 /* Control 7 */ +#define OV7670_COM7_FMT_VGA 0x00 +/*#define OV7670_COM7_YUV 0x00 * YUV */ +#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */ +#define OV7670_COM7_FMT_MASK 0x38 +#define OV7670_COM7_RESET 0x80 /* Register reset */ +#define OV7670_R13_COM8 0x13 /* Control 8 */ +#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */ +#define OV7670_COM8_AWB 0x02 /* White balance enable */ +#define OV7670_COM8_AGC 0x04 /* Auto gain enable */ +#define OV7670_COM8_BFILT 0x20 /* Band filter enable */ +#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define OV7670_R14_COM9 0x14 /* Control 9 - gain ceiling */ +#define OV7670_R15_COM10 0x15 /* Control 10 */ +#define OV7670_R17_HSTART 0x17 /* Horiz start high bits */ +#define OV7670_R18_HSTOP 0x18 /* Horiz stop high bits */ +#define OV7670_R19_VSTART 0x19 /* Vert start high bits */ +#define OV7670_R1A_VSTOP 0x1a /* Vert stop high bits */ +#define OV7670_R1E_MVFP 0x1e /* Mirror / vflip */ +#define OV7670_MVFP_VFLIP 0x10 /* vertical flip */ +#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */ +#define OV7670_R24_AEW 0x24 /* AGC upper limit */ +#define OV7670_R25_AEB 0x25 /* AGC lower limit */ +#define OV7670_R26_VPT 0x26 /* AGC/AEC fast mode op region */ +#define OV7670_R32_HREF 0x32 /* HREF pieces */ +#define OV7670_R3A_TSLB 0x3a /* lots of stuff */ +#define OV7670_R3B_COM11 0x3b /* Control 11 */ +#define OV7670_COM11_EXP 0x02 +#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define OV7670_R3C_COM12 0x3c /* Control 12 */ +#define OV7670_R3D_COM13 0x3d /* Control 13 */ +#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */ +#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define OV7670_R3E_COM14 0x3e /* Control 14 */ +#define OV7670_R3F_EDGE 0x3f /* Edge enhancement factor */ +#define OV7670_R40_COM15 0x40 /* Control 15 */ +/*#define OV7670_COM15_R00FF 0xc0 * 00 to FF */ +#define OV7670_R41_COM16 0x41 /* Control 16 */ +#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */ +/* end of ov7660 common registers */ +#define OV7670_R55_BRIGHT 0x55 /* Brightness */ +#define OV7670_R56_CONTRAS 0x56 /* Contrast control */ +#define OV7670_R69_GFIX 0x69 /* Fix gain control */ +/*#define OV7670_R8C_RGB444 0x8c * RGB 444 control */ +#define OV7670_R9F_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define OV7670_RA0_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ +#define OV7670_RA5_BD50MAX 0xa5 /* 50hz banding step limit */ +#define OV7670_RA6_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define OV7670_RA7_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define OV7670_RA8_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define OV7670_RA9_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define OV7670_RAA_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define OV7670_RAB_BD60MAX 0xab /* 60hz banding step limit */ + +struct ov_regvals { + u8 reg; + u8 val; +}; +struct ov_i2c_regvals { + u8 reg; + u8 val; +}; + +/* Settings for OV2610 camera chip */ +static const struct ov_i2c_regvals norm_2610[] = { + { 0x12, 0x80 }, /* reset */ +}; + +static const struct ov_i2c_regvals norm_2610ae[] = { + {0x12, 0x80}, /* reset */ + {0x13, 0xcd}, + {0x09, 0x01}, + {0x0d, 0x00}, + {0x11, 0x80}, + {0x12, 0x20}, /* 1600x1200 */ + {0x33, 0x0c}, + {0x35, 0x90}, + {0x36, 0x37}, +/* ms-win traces */ + {0x11, 0x83}, /* clock / 3 ? */ + {0x2d, 0x00}, /* 60 Hz filter */ + {0x24, 0xb0}, /* normal colors */ + {0x25, 0x90}, + {0x10, 0x43}, +}; + +static const struct ov_i2c_regvals norm_3620b[] = { + /* + * From the datasheet: "Note that after writing to register COMH + * (0x12) to change the sensor mode, registers related to the + * sensor’s cropping window will be reset back to their default + * values." + * + * "wait 4096 external clock ... to make sure the sensor is + * stable and ready to access registers" i.e. 160us at 24MHz + */ + { 0x12, 0x80 }, /* COMH reset */ + { 0x12, 0x00 }, /* QXGA, master */ + + /* + * 11 CLKRC "Clock Rate Control" + * [7] internal frequency doublers: on + * [6] video port mode: master + * [5:0] clock divider: 1 + */ + { 0x11, 0x80 }, + + /* + * 13 COMI "Common Control I" + * = 192 (0xC0) 11000000 + * COMI[7] "AEC speed selection" + * = 1 (0x01) 1....... "Faster AEC correction" + * COMI[6] "AEC speed step selection" + * = 1 (0x01) .1...... "Big steps, fast" + * COMI[5] "Banding filter on off" + * = 0 (0x00) ..0..... "Off" + * COMI[4] "Banding filter option" + * = 0 (0x00) ...0.... "Main clock is 48 MHz and + * the PLL is ON" + * COMI[3] "Reserved" + * = 0 (0x00) ....0... + * COMI[2] "AGC auto manual control selection" + * = 0 (0x00) .....0.. "Manual" + * COMI[1] "AWB auto manual control selection" + * = 0 (0x00) ......0. "Manual" + * COMI[0] "Exposure control" + * = 0 (0x00) .......0 "Manual" + */ + { 0x13, 0xc0 }, + + /* + * 09 COMC "Common Control C" + * = 8 (0x08) 00001000 + * COMC[7:5] "Reserved" + * = 0 (0x00) 000..... + * COMC[4] "Sleep Mode Enable" + * = 0 (0x00) ...0.... "Normal mode" + * COMC[3:2] "Sensor sampling reset timing selection" + * = 2 (0x02) ....10.. "Longer reset time" + * COMC[1:0] "Output drive current select" + * = 0 (0x00) ......00 "Weakest" + */ + { 0x09, 0x08 }, + + /* + * 0C COMD "Common Control D" + * = 8 (0x08) 00001000 + * COMD[7] "Reserved" + * = 0 (0x00) 0....... + * COMD[6] "Swap MSB and LSB at the output port" + * = 0 (0x00) .0...... "False" + * COMD[5:3] "Reserved" + * = 1 (0x01) ..001... + * COMD[2] "Output Average On Off" + * = 0 (0x00) .....0.. "Output Normal" + * COMD[1] "Sensor precharge voltage selection" + * = 0 (0x00) ......0. "Selects internal + * reference precharge + * voltage" + * COMD[0] "Snapshot option" + * = 0 (0x00) .......0 "Enable live video output + * after snapshot sequence" + */ + { 0x0c, 0x08 }, + + /* + * 0D COME "Common Control E" + * = 161 (0xA1) 10100001 + * COME[7] "Output average option" + * = 1 (0x01) 1....... "Output average of 4 pixels" + * COME[6] "Anti-blooming control" + * = 0 (0x00) .0...... "Off" + * COME[5:3] "Reserved" + * = 4 (0x04) ..100... + * COME[2] "Clock output power down pin status" + * = 0 (0x00) .....0.. "Tri-state data output pin + * on power down" + * COME[1] "Data output pin status selection at power down" + * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK, + * HREF, and CHSYNC pins on + * power down" + * COME[0] "Auto zero circuit select" + * = 1 (0x01) .......1 "On" + */ + { 0x0d, 0xa1 }, + + /* + * 0E COMF "Common Control F" + * = 112 (0x70) 01110000 + * COMF[7] "System clock selection" + * = 0 (0x00) 0....... "Use 24 MHz system clock" + * COMF[6:4] "Reserved" + * = 7 (0x07) .111.... + * COMF[3] "Manual auto negative offset canceling selection" + * = 0 (0x00) ....0... "Auto detect negative + * offset and cancel it" + * COMF[2:0] "Reserved" + * = 0 (0x00) .....000 + */ + { 0x0e, 0x70 }, + + /* + * 0F COMG "Common Control G" + * = 66 (0x42) 01000010 + * COMG[7] "Optical black output selection" + * = 0 (0x00) 0....... "Disable" + * COMG[6] "Black level calibrate selection" + * = 1 (0x01) .1...... "Use optical black pixels + * to calibrate" + * COMG[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMG[3] "Channel offset adjustment" + * = 0 (0x00) ....0... "Disable offset adjustment" + * COMG[2] "ADC black level calibration option" + * = 0 (0x00) .....0.. "Use B/G line and G/R + * line to calibrate each + * channel's black level" + * COMG[1] "Reserved" + * = 1 (0x01) ......1. + * COMG[0] "ADC black level calibration enable" + * = 0 (0x00) .......0 "Disable" + */ + { 0x0f, 0x42 }, + + /* + * 14 COMJ "Common Control J" + * = 198 (0xC6) 11000110 + * COMJ[7:6] "AGC gain ceiling" + * = 3 (0x03) 11...... "8x" + * COMJ[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMJ[3] "Auto banding filter" + * = 0 (0x00) ....0... "Banding filter is always + * on off depending on + * COMI[5] setting" + * COMJ[2] "VSYNC drop option" + * = 1 (0x01) .....1.. "SYNC is dropped if frame + * data is dropped" + * COMJ[1] "Frame data drop" + * = 1 (0x01) ......1. "Drop frame data if + * exposure is not within + * tolerance. In AEC mode, + * data is normally dropped + * when data is out of + * range." + * COMJ[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x14, 0xc6 }, + + /* + * 15 COMK "Common Control K" + * = 2 (0x02) 00000010 + * COMK[7] "CHSYNC pin output swap" + * = 0 (0x00) 0....... "CHSYNC" + * COMK[6] "HREF pin output swap" + * = 0 (0x00) .0...... "HREF" + * COMK[5] "PCLK output selection" + * = 0 (0x00) ..0..... "PCLK always output" + * COMK[4] "PCLK edge selection" + * = 0 (0x00) ...0.... "Data valid on falling edge" + * COMK[3] "HREF output polarity" + * = 0 (0x00) ....0... "positive" + * COMK[2] "Reserved" + * = 0 (0x00) .....0.. + * COMK[1] "VSYNC polarity" + * = 1 (0x01) ......1. "negative" + * COMK[0] "HSYNC polarity" + * = 0 (0x00) .......0 "positive" + */ + { 0x15, 0x02 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 36 VCHG "Sensor Precharge Voltage Control" + * = 0 (0x00) 00000000 + * VCHG[7] "Reserved" + * = 0 (0x00) 0....... + * VCHG[6:4] "Sensor precharge voltage control" + * = 0 (0x00) .000.... + * VCHG[3:0] "Sensor array common reference" + * = 0 (0x00) ....0000 + */ + { 0x36, 0x00 }, + + /* + * 37 ADC "ADC Reference Control" + * = 4 (0x04) 00000100 + * ADC[7:4] "Reserved" + * = 0 (0x00) 0000.... + * ADC[3] "ADC input signal range" + * = 0 (0x00) ....0... "Input signal 1.0x" + * ADC[2:0] "ADC range control" + * = 4 (0x04) .....100 + */ + { 0x37, 0x04 }, + + /* + * 38 ACOM "Analog Common Ground" + * = 82 (0x52) 01010010 + * ACOM[7] "Analog gain control" + * = 0 (0x00) 0....... "Gain 1x" + * ACOM[6] "Analog black level calibration" + * = 1 (0x01) .1...... "On" + * ACOM[5:0] "Reserved" + * = 18 (0x12) ..010010 + */ + { 0x38, 0x52 }, + + /* + * 3A FREFA "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFA[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3a, 0x00 }, + + /* + * 3C FVOPT "Internal Reference Adjustment" + * = 31 (0x1F) 00011111 + * FVOPT[7:0] "Range" + * = 31 (0x1F) 00011111 + */ + { 0x3c, 0x1f }, + + /* + * 44 Undocumented = 0 (0x00) 00000000 + * 44[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x44, 0x00 }, + + /* + * 40 Undocumented = 0 (0x00) 00000000 + * 40[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x40, 0x00 }, + + /* + * 41 Undocumented = 0 (0x00) 00000000 + * 41[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x41, 0x00 }, + + /* + * 42 Undocumented = 0 (0x00) 00000000 + * 42[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x42, 0x00 }, + + /* + * 43 Undocumented = 0 (0x00) 00000000 + * 43[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x43, 0x00 }, + + /* + * 45 Undocumented = 128 (0x80) 10000000 + * 45[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x45, 0x80 }, + + /* + * 48 Undocumented = 192 (0xC0) 11000000 + * 48[7:0] "It's a secret" + * = 192 (0xC0) 11000000 + */ + { 0x48, 0xc0 }, + + /* + * 49 Undocumented = 25 (0x19) 00011001 + * 49[7:0] "It's a secret" + * = 25 (0x19) 00011001 + */ + { 0x49, 0x19 }, + + /* + * 4B Undocumented = 128 (0x80) 10000000 + * 4B[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x4b, 0x80 }, + + /* + * 4D Undocumented = 196 (0xC4) 11000100 + * 4D[7:0] "It's a secret" + * = 196 (0xC4) 11000100 + */ + { 0x4d, 0xc4 }, + + /* + * 35 VREF "Reference Voltage Control" + * = 76 (0x4c) 01001100 + * VREF[7:5] "Column high reference control" + * = 2 (0x02) 010..... "higher voltage" + * VREF[4:2] "Column low reference control" + * = 3 (0x03) ...011.. "Highest voltage" + * VREF[1:0] "Reserved" + * = 0 (0x00) ......00 + */ + { 0x35, 0x4c }, + + /* + * 3D Undocumented = 0 (0x00) 00000000 + * 3D[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3d, 0x00 }, + + /* + * 3E Undocumented = 0 (0x00) 00000000 + * 3E[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3e, 0x00 }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 24 (0x18) 00011000 + * FREFB[7:0] "Range" + * = 24 (0x18) 00011000 + */ + { 0x3b, 0x18 }, + + /* + * 33 CHLF "Current Control" + * = 25 (0x19) 00011001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 1 (0x01) ...1.... "double current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x19 }, + + /* + * 34 VBLM "Blooming Control" + * = 90 (0x5A) 01011010 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 10 (0x0A) ....1010 + */ + { 0x34, 0x5a }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFB[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3b, 0x00 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1f }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5f }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4a }, + + /* + * 11 CLKRC "Clock Rate Control" + * = 128 (0x80) 10000000 + * CLKRC[7] "Internal frequency doublers on off seclection" + * = 1 (0x01) 1....... "On" + * CLKRC[6] "Digital video master slave selection" + * = 0 (0x00) .0...... "Master mode, sensor + * provides PCLK" + * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }" + * = 0 (0x00) ..000000 + */ + { 0x11, 0x80 }, + + /* + * 12 COMH "Common Control H" + * = 0 (0x00) 00000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 0 (0x00) .000.... "QXGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x00 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1f }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5f }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4a }, + + /* + * 02 RED "Red Gain Control" + * = 175 (0xAF) 10101111 + * RED[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * RED[6:0] "Value" + * = 47 (0x2F) .0101111 + */ + { 0x02, 0xaf }, + + /* + * 2D ADDVSL "VSYNC Pulse Width" + * = 210 (0xD2) 11010010 + * ADDVSL[7:0] "VSYNC pulse width, LSB" + * = 210 (0xD2) 11010010 + */ + { 0x2d, 0xd2 }, + + /* + * 00 GAIN = 24 (0x18) 00011000 + * GAIN[7:6] "Reserved" + * = 0 (0x00) 00...... + * GAIN[5] "Double" + * = 0 (0x00) ..0..... "False" + * GAIN[4] "Double" + * = 1 (0x01) ...1.... "True" + * GAIN[3:0] "Range" + * = 8 (0x08) ....1000 + */ + { 0x00, 0x18 }, + + /* + * 01 BLUE "Blue Gain Control" + * = 240 (0xF0) 11110000 + * BLUE[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * BLUE[6:0] "Value" + * = 112 (0x70) .1110000 + */ + { 0x01, 0xf0 }, + + /* + * 10 AEC "Automatic Exposure Control" + * = 10 (0x0A) 00001010 + * AEC[7:0] "Automatic Exposure Control, 8 MSBs" + * = 10 (0x0A) 00001010 + */ + { 0x10, 0x0a }, + + { 0xe1, 0x67 }, + { 0xe3, 0x03 }, + { 0xe4, 0x26 }, + { 0xe5, 0x3e }, + { 0xf8, 0x01 }, + { 0xff, 0x01 }, +}; + +static const struct ov_i2c_regvals norm_6x20[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC */ + { 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { 0x16, 0x06 }, +/* { 0x20, 0x30 }, * Aperture correction enable */ + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ +/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ + { 0x2d, 0x85 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. + * They control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, +/* Do 50-53 have any effect? */ +/* Toggle 0x12[2] off and on here? */ +}; + +static const struct ov_i2c_regvals norm_6x30[] = { + { 0x12, 0x80 }, /* Reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */ + { 0x0f, 0x05 }, + { 0x10, 0x9a }, + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* 60 Hz power */ + { 0x2b, 0xa8 }, /* 60 Hz power */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV avg., col. killer: max */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + { 0x12, 0x20 }, /* Toggle AWB */ + { 0x12, 0x24 }, +}; + +/* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ +static const struct ov_i2c_regvals norm_7610[] = { + { 0x10, 0xff }, + { 0x16, 0x06 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, +}; + +static const struct ov_i2c_regvals norm_7620[] = { + { 0x12, 0x80 }, /* reset */ + { 0x00, 0x00 }, /* gain */ + { 0x01, 0x80 }, /* blue gain */ + { 0x02, 0x80 }, /* red gain */ + { 0x03, 0xc0 }, /* OV7670_R03_VREF */ + { 0x06, 0x60 }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */ + { 0x29, 0x00 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, +}; + +/* 7640 and 7648. The defaults should be OK for most registers. */ +static const struct ov_i2c_regvals norm_7640[] = { + { 0x12, 0x80 }, + { 0x12, 0x14 }, +}; + +static const struct ov_regvals init_519_ov7660[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9b }, /* 0x9f enables the (unused) microcontroller */ + { 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x20, 0x0c }, /* 0x0d does U <-> V swap */ + { 0x21, 0x38 }, + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ +}; +static const struct ov_i2c_regvals norm_7660[] = { + {OV7670_R12_COM7, OV7670_COM7_RESET}, + {OV7670_R11_CLKRC, 0x81}, + {0x92, 0x00}, /* DM_LNL */ + {0x93, 0x00}, /* DM_LNH */ + {0x9d, 0x4c}, /* BD50ST */ + {0x9e, 0x3f}, /* BD60ST */ + {OV7670_R3B_COM11, 0x02}, + {OV7670_R13_COM8, 0xf5}, + {OV7670_R10_AECH, 0x00}, + {OV7670_R00_GAIN, 0x00}, + {OV7670_R01_BLUE, 0x7c}, + {OV7670_R02_RED, 0x9d}, + {OV7670_R12_COM7, 0x00}, + {OV7670_R04_COM1, 00}, + {OV7670_R18_HSTOP, 0x01}, + {OV7670_R17_HSTART, 0x13}, + {OV7670_R32_HREF, 0x92}, + {OV7670_R19_VSTART, 0x02}, + {OV7670_R1A_VSTOP, 0x7a}, + {OV7670_R03_VREF, 0x00}, + {OV7670_R0E_COM5, 0x04}, + {OV7670_R0F_COM6, 0x62}, + {OV7670_R15_COM10, 0x00}, + {0x16, 0x02}, /* RSVD */ + {0x1b, 0x00}, /* PSHFT */ + {OV7670_R1E_MVFP, 0x01}, + {0x29, 0x3c}, /* RSVD */ + {0x33, 0x00}, /* CHLF */ + {0x34, 0x07}, /* ARBLM */ + {0x35, 0x84}, /* RSVD */ + {0x36, 0x00}, /* RSVD */ + {0x37, 0x04}, /* ADC */ + {0x39, 0x43}, /* OFON */ + {OV7670_R3A_TSLB, 0x00}, + {OV7670_R3C_COM12, 0x6c}, + {OV7670_R3D_COM13, 0x98}, + {OV7670_R3F_EDGE, 0x23}, + {OV7670_R40_COM15, 0xc1}, + {OV7670_R41_COM16, 0x22}, + {0x6b, 0x0a}, /* DBLV */ + {0xa1, 0x08}, /* RSVD */ + {0x69, 0x80}, /* HV */ + {0x43, 0xf0}, /* RSVD.. */ + {0x44, 0x10}, + {0x45, 0x78}, + {0x46, 0xa8}, + {0x47, 0x60}, + {0x48, 0x80}, + {0x59, 0xba}, + {0x5a, 0x9a}, + {0x5b, 0x22}, + {0x5c, 0xb9}, + {0x5d, 0x9b}, + {0x5e, 0x10}, + {0x5f, 0xe0}, + {0x60, 0x85}, + {0x61, 0x60}, + {0x9f, 0x9d}, /* RSVD */ + {0xa0, 0xa0}, /* DSPC2 */ + {0x4f, 0x60}, /* matrix */ + {0x50, 0x64}, + {0x51, 0x04}, + {0x52, 0x18}, + {0x53, 0x3c}, + {0x54, 0x54}, + {0x55, 0x40}, + {0x56, 0x40}, + {0x57, 0x40}, + {0x58, 0x0d}, /* matrix sign */ + {0x8b, 0xcc}, /* RSVD */ + {0x8c, 0xcc}, + {0x8d, 0xcf}, + {0x6c, 0x40}, /* gamma curve */ + {0x6d, 0xe0}, + {0x6e, 0xa0}, + {0x6f, 0x80}, + {0x70, 0x70}, + {0x71, 0x80}, + {0x72, 0x60}, + {0x73, 0x60}, + {0x74, 0x50}, + {0x75, 0x40}, + {0x76, 0x38}, + {0x77, 0x3c}, + {0x78, 0x32}, + {0x79, 0x1a}, + {0x7a, 0x28}, + {0x7b, 0x24}, + {0x7c, 0x04}, /* gamma curve */ + {0x7d, 0x12}, + {0x7e, 0x26}, + {0x7f, 0x46}, + {0x80, 0x54}, + {0x81, 0x64}, + {0x82, 0x70}, + {0x83, 0x7c}, + {0x84, 0x86}, + {0x85, 0x8e}, + {0x86, 0x9c}, + {0x87, 0xab}, + {0x88, 0xc4}, + {0x89, 0xd1}, + {0x8a, 0xe5}, + {OV7670_R14_COM9, 0x1e}, + {OV7670_R24_AEW, 0x80}, + {OV7670_R25_AEB, 0x72}, + {OV7670_R26_VPT, 0xb3}, + {0x62, 0x80}, /* LCC1 */ + {0x63, 0x80}, /* LCC2 */ + {0x64, 0x06}, /* LCC3 */ + {0x65, 0x00}, /* LCC4 */ + {0x66, 0x01}, /* LCC5 */ + {0x94, 0x0e}, /* RSVD.. */ + {0x95, 0x14}, + {OV7670_R13_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | 0x10 + | OV7670_COM8_AGC + | OV7670_COM8_AWB + | OV7670_COM8_AEC}, + {0xa1, 0xc8} +}; +static const struct ov_i2c_regvals norm_9600[] = { + {0x12, 0x80}, + {0x0c, 0x28}, + {0x11, 0x80}, + {0x13, 0xb5}, + {0x14, 0x3e}, + {0x1b, 0x04}, + {0x24, 0xb0}, + {0x25, 0x90}, + {0x26, 0x94}, + {0x35, 0x90}, + {0x37, 0x07}, + {0x38, 0x08}, + {0x01, 0x8e}, + {0x02, 0x85} +}; + +/* 7670. Defaults taken from OmniVision provided data, +* as provided by Jonathan Corbet of OLPC */ +static const struct ov_i2c_regvals norm_7670[] = { + { OV7670_R12_COM7, OV7670_COM7_RESET }, + { OV7670_R3A_TSLB, 0x04 }, /* OV */ + { OV7670_R12_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ + { OV7670_R11_CLKRC, 0x01 }, +/* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { OV7670_R17_HSTART, 0x13 }, + { OV7670_R18_HSTOP, 0x01 }, + { OV7670_R32_HREF, 0xb6 }, + { OV7670_R19_VSTART, 0x02 }, + { OV7670_R1A_VSTOP, 0x7a }, + { OV7670_R03_VREF, 0x0a }, + + { OV7670_R0C_COM3, 0x00 }, + { OV7670_R3E_COM14, 0x00 }, +/* Mystery scaling numbers */ + { 0x70, 0x3a }, + { 0x71, 0x35 }, + { 0x72, 0x11 }, + { 0x73, 0xf0 }, + { 0xa2, 0x02 }, +/* { OV7670_R15_COM10, 0x0 }, */ + +/* Gamma curve values */ + { 0x7a, 0x20 }, + { 0x7b, 0x10 }, + { 0x7c, 0x1e }, + { 0x7d, 0x35 }, + { 0x7e, 0x5a }, + { 0x7f, 0x69 }, + { 0x80, 0x76 }, + { 0x81, 0x80 }, + { 0x82, 0x88 }, + { 0x83, 0x8f }, + { 0x84, 0x96 }, + { 0x85, 0xa3 }, + { 0x86, 0xaf }, + { 0x87, 0xc4 }, + { 0x88, 0xd7 }, + { 0x89, 0xe8 }, + +/* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { OV7670_R13_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT }, + { OV7670_R00_GAIN, 0x00 }, + { OV7670_R10_AECH, 0x00 }, + { OV7670_R0D_COM4, 0x40 }, /* magic reserved bit */ + { OV7670_R14_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { OV7670_RA5_BD50MAX, 0x05 }, + { OV7670_RAB_BD60MAX, 0x07 }, + { OV7670_R24_AEW, 0x95 }, + { OV7670_R25_AEB, 0x33 }, + { OV7670_R26_VPT, 0xe3 }, + { OV7670_R9F_HAECC1, 0x78 }, + { OV7670_RA0_HAECC2, 0x68 }, + { 0xa1, 0x03 }, /* magic */ + { OV7670_RA6_HAECC3, 0xd8 }, + { OV7670_RA7_HAECC4, 0xd8 }, + { OV7670_RA8_HAECC5, 0xf0 }, + { OV7670_RA9_HAECC6, 0x90 }, + { OV7670_RAA_HAECC7, 0x94 }, + { OV7670_R13_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC }, + +/* Almost all of these are magic "reserved" values. */ + { OV7670_R0E_COM5, 0x61 }, + { OV7670_R0F_COM6, 0x4b }, + { 0x16, 0x02 }, + { OV7670_R1E_MVFP, 0x07 }, + { 0x21, 0x02 }, + { 0x22, 0x91 }, + { 0x29, 0x07 }, + { 0x33, 0x0b }, + { 0x35, 0x0b }, + { 0x37, 0x1d }, + { 0x38, 0x71 }, + { 0x39, 0x2a }, + { OV7670_R3C_COM12, 0x78 }, + { 0x4d, 0x40 }, + { 0x4e, 0x20 }, + { OV7670_R69_GFIX, 0x00 }, + { 0x6b, 0x4a }, + { 0x74, 0x10 }, + { 0x8d, 0x4f }, + { 0x8e, 0x00 }, + { 0x8f, 0x00 }, + { 0x90, 0x00 }, + { 0x91, 0x00 }, + { 0x96, 0x00 }, + { 0x9a, 0x00 }, + { 0xb0, 0x84 }, + { 0xb1, 0x0c }, + { 0xb2, 0x0e }, + { 0xb3, 0x82 }, + { 0xb8, 0x0a }, + +/* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, + { 0x44, 0xf0 }, + { 0x45, 0x34 }, + { 0x46, 0x58 }, + { 0x47, 0x28 }, + { 0x48, 0x3a }, + { 0x59, 0x88 }, + { 0x5a, 0x88 }, + { 0x5b, 0x44 }, + { 0x5c, 0x67 }, + { 0x5d, 0x49 }, + { 0x5e, 0x0e }, + { 0x6c, 0x0a }, + { 0x6d, 0x55 }, + { 0x6e, 0x11 }, + { 0x6f, 0x9f }, /* "9e for advance AWB" */ + { 0x6a, 0x40 }, + { OV7670_R01_BLUE, 0x40 }, + { OV7670_R02_RED, 0x60 }, + { OV7670_R13_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC + | OV7670_COM8_AWB }, + +/* Matrix coefficients */ + { 0x4f, 0x80 }, + { 0x50, 0x80 }, + { 0x51, 0x00 }, + { 0x52, 0x22 }, + { 0x53, 0x5e }, + { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { OV7670_R41_COM16, OV7670_COM16_AWBGAIN }, + { OV7670_R3F_EDGE, 0x00 }, + { 0x75, 0x05 }, + { 0x76, 0xe1 }, + { 0x4c, 0x00 }, + { 0x77, 0x01 }, + { OV7670_R3D_COM13, OV7670_COM13_GAMMA + | OV7670_COM13_UVSAT + | 2}, /* was 3 */ + { 0x4b, 0x09 }, + { 0xc9, 0x60 }, + { OV7670_R41_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, + { OV7670_R3B_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, + { 0xa4, 0x88 }, + { 0x96, 0x00 }, + { 0x97, 0x30 }, + { 0x98, 0x20 }, + { 0x99, 0x30 }, + { 0x9a, 0x84 }, + { 0x9b, 0x29 }, + { 0x9c, 0x03 }, + { 0x9d, 0x4c }, + { 0x9e, 0x3f }, + { 0x78, 0x04 }, + +/* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, + { 0xc8, 0xf0 }, + { 0x79, 0x0f }, + { 0xc8, 0x00 }, + { 0x79, 0x10 }, + { 0xc8, 0x7e }, + { 0x79, 0x0a }, + { 0xc8, 0x80 }, + { 0x79, 0x0b }, + { 0xc8, 0x01 }, + { 0x79, 0x0c }, + { 0xc8, 0x0f }, + { 0x79, 0x0d }, + { 0xc8, 0x20 }, + { 0x79, 0x09 }, + { 0xc8, 0x80 }, + { 0x79, 0x02 }, + { 0xc8, 0xc0 }, + { 0x79, 0x03 }, + { 0xc8, 0x40 }, + { 0x79, 0x05 }, + { 0xc8, 0x30 }, + { 0x79, 0x26 }, +}; + +static const struct ov_i2c_regvals norm_8610[] = { + { 0x12, 0x80 }, + { 0x00, 0x00 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, 0xc0 }, + { 0x04, 0x30 }, + { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ + { 0x0a, 0x86 }, + { 0x0b, 0xb0 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x12, 0x25 }, + { 0x13, 0x01 }, + { 0x14, 0x04 }, + { 0x15, 0x01 }, /* Lin and Win think different about UV order */ + { 0x16, 0x03 }, + { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ + { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ + { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ + { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ + { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ + { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x80 }, + { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ + { 0x2c, 0xac }, + { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ + { 0x2e, 0x80 }, + { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ + { 0x4c, 0x00 }, + { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ + { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ + { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ + { 0x63, 0xff }, + { 0x64, 0x53 }, /* new windrv 090403 says 0x57, + * maybe thats wrong */ + { 0x65, 0x00 }, + { 0x66, 0x55 }, + { 0x67, 0xb0 }, + { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ + { 0x69, 0x02 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but + * deleting bit7 colors the first images red */ + { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6f, 0x01 }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ + { 0x75, 0x0e }, + { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ + { 0x7c, 0x00 }, + { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ + { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ + { 0x7f, 0xfb }, + { 0x80, 0x28 }, + { 0x81, 0x00 }, + { 0x82, 0x23 }, + { 0x83, 0x0b }, + { 0x84, 0x00 }, + { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ + { 0x86, 0xc9 }, + { 0x87, 0x00 }, + { 0x88, 0x00 }, + { 0x89, 0x01 }, + { 0x12, 0x20 }, + { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ +}; + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + return (128 - v) | 0x80; +} + +/* Write a OV519 register */ +static void reg_w(struct sd *sd, u16 index, u16 value) +{ + int ret, req = 0; + + if (sd->gspca_dev.usb_err < 0) + return; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 2; + break; + case BRIDGE_OVFX2: + req = 0x0a; + /* fall through */ + case BRIDGE_W9968CF: + PDEBUG(D_USBO, "SET %02x %04x %04x", + req, value, index); + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + goto leave; + default: + req = 1; + } + + PDEBUG(D_USBO, "SET %02x 0000 %04x %02x", + req, index, value); + sd->gspca_dev.usb_buf[0] = value; + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, + sd->gspca_dev.usb_buf, 1, 500); +leave: + if (ret < 0) { + pr_err("reg_w %02x failed %d\n", index, ret); + sd->gspca_dev.usb_err = ret; + return; + } +} + +/* Read from a OV519 register, note not valid for the w9968cf!! */ +/* returns: negative is error, pos or zero is data */ +static int reg_r(struct sd *sd, u16 index) +{ + int ret; + int req; + + if (sd->gspca_dev.usb_err < 0) + return -1; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 3; + break; + case BRIDGE_OVFX2: + req = 0x0b; + break; + default: + req = 1; + } + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, sd->gspca_dev.usb_buf, 1, 500); + + if (ret >= 0) { + ret = sd->gspca_dev.usb_buf[0]; + PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", + req, index, ret); + } else { + pr_err("reg_r %02x failed %d\n", index, ret); + sd->gspca_dev.usb_err = ret; + } + + return ret; +} + +/* Read 8 values from a OV519 register */ +static int reg_r8(struct sd *sd, + u16 index) +{ + int ret; + + if (sd->gspca_dev.usb_err < 0) + return -1; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, sd->gspca_dev.usb_buf, 8, 500); + + if (ret >= 0) { + ret = sd->gspca_dev.usb_buf[0]; + } else { + pr_err("reg_r8 %02x failed %d\n", index, ret); + sd->gspca_dev.usb_err = ret; + } + + return ret; +} + +/* + * Writes bits at positions specified by mask to an OV51x reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static void reg_w_mask(struct sd *sd, + u16 index, + u8 value, + u8 mask) +{ + int ret; + u8 oldval; + + if (mask != 0xff) { + value &= mask; /* Enforce mask on value */ + ret = reg_r(sd, index); + if (ret < 0) + return; + + oldval = ret & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + } + reg_w(sd, index, value); +} + +/* + * Writes multiple (n) byte value to a single register. Only valid with certain + * registers (0x30 and 0xc4 - 0xce). + */ +static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n) +{ + int ret; + + if (sd->gspca_dev.usb_err < 0) + return; + + *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value); + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 1 /* REG_IO */, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, + sd->gspca_dev.usb_buf, n, 500); + if (ret < 0) { + pr_err("reg_w32 %02x failed %d\n", index, ret); + sd->gspca_dev.usb_err = ret; + } +} + +static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value) +{ + int rc, retries; + + PDEBUG(D_USBO, "ov511_i2c_w %02x %02x", reg, value); + + /* Three byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + reg_w(sd, R51x_I2C_SADDR_3, reg); + + /* Write "value" to I2C data port of OV511 */ + reg_w(sd, R51x_I2C_DATA, value); + + /* Initiate 3-byte write cycle */ + reg_w(sd, R511_I2C_CTL, 0x01); + + do { + rc = reg_r(sd, R511_I2C_CTL); + } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return; + + if ((rc & 2) == 0) /* Ack? */ + break; + if (--retries < 0) { + PDEBUG(D_USBO, "i2c write retries exhausted"); + return; + } + } +} + +static int ov511_i2c_r(struct sd *sd, u8 reg) +{ + int rc, value, retries; + + /* Two byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + reg_w(sd, R51x_I2C_SADDR_2, reg); + + /* Initiate 2-byte write cycle */ + reg_w(sd, R511_I2C_CTL, 0x03); + + do { + rc = reg_r(sd, R511_I2C_CTL); + } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + reg_w(sd, R511_I2C_CTL, 0x10); + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c write retries exhausted"); + return -1; + } + } + + /* Two byte read cycle */ + for (retries = 6; ; ) { + /* Initiate 2-byte read cycle */ + reg_w(sd, R511_I2C_CTL, 0x05); + + do { + rc = reg_r(sd, R511_I2C_CTL); + } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + reg_w(sd, R511_I2C_CTL, 0x10); + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c read retries exhausted"); + return -1; + } + } + + value = reg_r(sd, R51x_I2C_DATA); + + PDEBUG(D_USBI, "ov511_i2c_r %02x %02x", reg, value); + + /* This is needed to make i2c_w() work */ + reg_w(sd, R511_I2C_CTL, 0x05); + + return value; +} + +/* + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_w(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static void ov518_i2c_w(struct sd *sd, + u8 reg, + u8 value) +{ + PDEBUG(D_USBO, "ov518_i2c_w %02x %02x", reg, value); + + /* Select camera register */ + reg_w(sd, R51x_I2C_SADDR_3, reg); + + /* Write "value" to I2C data port of OV511 */ + reg_w(sd, R51x_I2C_DATA, value); + + /* Initiate 3-byte write cycle */ + reg_w(sd, R518_I2C_CTL, 0x01); + + /* wait for write complete */ + msleep(4); + reg_r8(sd, R518_I2C_CTL); +} + +/* + * returns: negative is error, pos or zero is data + * + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_r(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int ov518_i2c_r(struct sd *sd, u8 reg) +{ + int value; + + /* Select camera register */ + reg_w(sd, R51x_I2C_SADDR_2, reg); + + /* Initiate 2-byte write cycle */ + reg_w(sd, R518_I2C_CTL, 0x03); + reg_r8(sd, R518_I2C_CTL); + + /* Initiate 2-byte read cycle */ + reg_w(sd, R518_I2C_CTL, 0x05); + reg_r8(sd, R518_I2C_CTL); + + value = reg_r(sd, R51x_I2C_DATA); + PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value); + return value; +} + +static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value) +{ + int ret; + + if (sd->gspca_dev.usb_err < 0) + return; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (u16) value, (u16) reg, NULL, 0, 500); + + if (ret < 0) { + pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret); + sd->gspca_dev.usb_err = ret; + } + + PDEBUG(D_USBO, "ovfx2_i2c_w %02x %02x", reg, value); +} + +static int ovfx2_i2c_r(struct sd *sd, u8 reg) +{ + int ret; + + if (sd->gspca_dev.usb_err < 0) + return -1; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 0x03, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, (u16) reg, sd->gspca_dev.usb_buf, 1, 500); + + if (ret >= 0) { + ret = sd->gspca_dev.usb_buf[0]; + PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret); + } else { + pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret); + sd->gspca_dev.usb_err = ret; + } + + return ret; +} + +static void i2c_w(struct sd *sd, u8 reg, u8 value) +{ + if (sd->sensor_reg_cache[reg] == value) + return; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ov511_i2c_w(sd, reg, value); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + ov518_i2c_w(sd, reg, value); + break; + case BRIDGE_OVFX2: + ovfx2_i2c_w(sd, reg, value); + break; + case BRIDGE_W9968CF: + w9968cf_i2c_w(sd, reg, value); + break; + } + + if (sd->gspca_dev.usb_err >= 0) { + /* Up on sensor reset empty the register cache */ + if (reg == 0x12 && (value & 0x80)) + memset(sd->sensor_reg_cache, -1, + sizeof(sd->sensor_reg_cache)); + else + sd->sensor_reg_cache[reg] = value; + } +} + +static int i2c_r(struct sd *sd, u8 reg) +{ + int ret = -1; + + if (sd->sensor_reg_cache[reg] != -1) + return sd->sensor_reg_cache[reg]; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ret = ov511_i2c_r(sd, reg); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + ret = ov518_i2c_r(sd, reg); + break; + case BRIDGE_OVFX2: + ret = ovfx2_i2c_r(sd, reg); + break; + case BRIDGE_W9968CF: + ret = w9968cf_i2c_r(sd, reg); + break; + } + + if (ret >= 0) + sd->sensor_reg_cache[reg] = ret; + + return ret; +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static void i2c_w_mask(struct sd *sd, + u8 reg, + u8 value, + u8 mask) +{ + int rc; + u8 oldval; + + value &= mask; /* Enforce mask on value */ + rc = i2c_r(sd, reg); + if (rc < 0) + return; + oldval = rc & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + i2c_w(sd, reg, value); +} + +/* Temporarily stops OV511 from functioning. Must do this before changing + * registers while the camera is streaming */ +static inline void ov51x_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "stopping"); + sd->stopped = 1; + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + reg_w(sd, R51x_SYS_RESET, 0x3d); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a); + break; + case BRIDGE_OV519: + reg_w(sd, OV519_R51_RESET1, 0x0f); + reg_w(sd, OV519_R51_RESET1, 0x00); + reg_w(sd, 0x22, 0x00); /* FRAR */ + break; + case BRIDGE_OVFX2: + reg_w_mask(sd, 0x0f, 0x00, 0x02); + break; + case BRIDGE_W9968CF: + reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */ + break; + } +} + +/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not + * actually stopped (for performance). */ +static inline void ov51x_restart(struct sd *sd) +{ + PDEBUG(D_STREAM, "restarting"); + if (!sd->stopped) + return; + sd->stopped = 0; + + /* Reinitialize the stream */ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + reg_w(sd, R51x_SYS_RESET, 0x00); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + reg_w(sd, 0x2f, 0x80); + reg_w(sd, R51x_SYS_RESET, 0x00); + break; + case BRIDGE_OV519: + reg_w(sd, OV519_R51_RESET1, 0x0f); + reg_w(sd, OV519_R51_RESET1, 0x00); + reg_w(sd, 0x22, 0x1d); /* FRAR */ + break; + case BRIDGE_OVFX2: + reg_w_mask(sd, 0x0f, 0x02, 0x02); + break; + case BRIDGE_W9968CF: + reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */ + break; + } +} + +static void ov51x_set_slave_ids(struct sd *sd, u8 slave); + +/* This does an initial reset of an OmniVision sensor and ensures that I2C + * is synchronized. Returns <0 on failure. + */ +static int init_ov_sensor(struct sd *sd, u8 slave) +{ + int i; + + ov51x_set_slave_ids(sd, slave); + + /* Reset the sensor */ + i2c_w(sd, 0x12, 0x80); + + /* Wait for it to initialize */ + msleep(150); + + for (i = 0; i < i2c_detect_tries; i++) { + if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f && + i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) { + PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i); + return 0; + } + + /* Reset the sensor */ + i2c_w(sd, 0x12, 0x80); + + /* Wait for it to initialize */ + msleep(150); + + /* Dummy read to sync I2C */ + if (i2c_r(sd, 0x00) < 0) + return -1; + } + return -1; +} + +/* Set the read and write slave IDs. The "slave" argument is the write slave, + * and the read slave will be set to (slave + 1). + * This should not be called from outside the i2c I/O functions. + * Sets I2C read and write slave IDs. Returns <0 for error + */ +static void ov51x_set_slave_ids(struct sd *sd, + u8 slave) +{ + switch (sd->bridge) { + case BRIDGE_OVFX2: + reg_w(sd, OVFX2_I2C_ADDR, slave); + return; + case BRIDGE_W9968CF: + sd->sensor_addr = slave; + return; + } + + reg_w(sd, R51x_I2C_W_SID, slave); + reg_w(sd, R51x_I2C_R_SID, slave + 1); +} + +static void write_regvals(struct sd *sd, + const struct ov_regvals *regvals, + int n) +{ + while (--n >= 0) { + reg_w(sd, regvals->reg, regvals->val); + regvals++; + } +} + +static void write_i2c_regvals(struct sd *sd, + const struct ov_i2c_regvals *regvals, + int n) +{ + while (--n >= 0) { + i2c_w(sd, regvals->reg, regvals->val); + regvals++; + } +} + +/**************************************************************************** + * + * OV511 and sensor configuration + * + ***************************************************************************/ + +/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */ +static void ov_hires_configure(struct sd *sd) +{ + int high, low; + + if (sd->bridge != BRIDGE_OVFX2) { + pr_err("error hires sensors only supported with ovfx2\n"); + return; + } + + PDEBUG(D_PROBE, "starting ov hires configuration"); + + /* Detect sensor (sub)type */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + switch (high) { + case 0x96: + switch (low) { + case 0x40: + PDEBUG(D_PROBE, "Sensor is a OV2610"); + sd->sensor = SEN_OV2610; + return; + case 0x41: + PDEBUG(D_PROBE, "Sensor is a OV2610AE"); + sd->sensor = SEN_OV2610AE; + return; + case 0xb1: + PDEBUG(D_PROBE, "Sensor is a OV9600"); + sd->sensor = SEN_OV9600; + return; + } + break; + case 0x36: + if ((low & 0x0f) == 0x00) { + PDEBUG(D_PROBE, "Sensor is a OV3610"); + sd->sensor = SEN_OV3610; + return; + } + break; + } + pr_err("Error unknown sensor type: %02x%02x\n", high, low); +} + +/* This initializes the OV8110, OV8610 sensor. The OV8110 uses + * the same register settings as the OV8610, since they are very similar. + */ +static void ov8xx0_configure(struct sd *sd) +{ + int rc; + + PDEBUG(D_PROBE, "starting ov8xx0 configuration"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return; + } + if ((rc & 3) == 1) + sd->sensor = SEN_OV8610; + else + pr_err("Unknown image sensor version: %d\n", rc & 3); +} + +/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses + * the same register settings as the OV7610, since they are very similar. + */ +static void ov7xx0_configure(struct sd *sd) +{ + int rc, high, low; + + PDEBUG(D_PROBE, "starting OV7xx0 configuration"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + + /* add OV7670 here + * it appears to be wrongly detected as a 7610 by default */ + if (rc < 0) { + pr_err("Error detecting sensor type\n"); + return; + } + if ((rc & 3) == 3) { + /* quick hack to make OV7670s work */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + if (high == 0x76 && (low & 0xf0) == 0x70) { + PDEBUG(D_PROBE, "Sensor is an OV76%02x", low); + sd->sensor = SEN_OV7670; + } else { + PDEBUG(D_PROBE, "Sensor is an OV7610"); + sd->sensor = SEN_OV7610; + } + } else if ((rc & 3) == 1) { + /* I don't know what's different about the 76BE yet. */ + if (i2c_r(sd, 0x15) & 1) { + PDEBUG(D_PROBE, "Sensor is an OV7620AE"); + sd->sensor = SEN_OV7620AE; + } else { + PDEBUG(D_PROBE, "Sensor is an OV76BE"); + sd->sensor = SEN_OV76BE; + } + } else if ((rc & 3) == 0) { + /* try to read product id registers */ + high = i2c_r(sd, 0x0a); + if (high < 0) { + pr_err("Error detecting camera chip PID\n"); + return; + } + low = i2c_r(sd, 0x0b); + if (low < 0) { + pr_err("Error detecting camera chip VER\n"); + return; + } + if (high == 0x76) { + switch (low) { + case 0x30: + pr_err("Sensor is an OV7630/OV7635\n"); + pr_err("7630 is not supported by this driver\n"); + return; + case 0x40: + PDEBUG(D_PROBE, "Sensor is an OV7645"); + sd->sensor = SEN_OV7640; /* FIXME */ + break; + case 0x45: + PDEBUG(D_PROBE, "Sensor is an OV7645B"); + sd->sensor = SEN_OV7640; /* FIXME */ + break; + case 0x48: + PDEBUG(D_PROBE, "Sensor is an OV7648"); + sd->sensor = SEN_OV7648; + break; + case 0x60: + PDEBUG(D_PROBE, "Sensor is a OV7660"); + sd->sensor = SEN_OV7660; + break; + default: + pr_err("Unknown sensor: 0x76%02x\n", low); + return; + } + } else { + PDEBUG(D_PROBE, "Sensor is an OV7620"); + sd->sensor = SEN_OV7620; + } + } else { + pr_err("Unknown image sensor version: %d\n", rc & 3); + } +} + +/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ +static void ov6xx0_configure(struct sd *sd) +{ + int rc; + PDEBUG(D_PROBE, "starting OV6xx0 configuration"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + pr_err("Error detecting sensor type\n"); + return; + } + + /* Ugh. The first two bits are the version bits, but + * the entire register value must be used. I guess OVT + * underestimated how many variants they would make. */ + switch (rc) { + case 0x00: + sd->sensor = SEN_OV6630; + pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n"); + break; + case 0x01: + sd->sensor = SEN_OV6620; + PDEBUG(D_PROBE, "Sensor is an OV6620"); + break; + case 0x02: + sd->sensor = SEN_OV6630; + PDEBUG(D_PROBE, "Sensor is an OV66308AE"); + break; + case 0x03: + sd->sensor = SEN_OV66308AF; + PDEBUG(D_PROBE, "Sensor is an OV66308AF"); + break; + case 0x90: + sd->sensor = SEN_OV6630; + pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n"); + break; + default: + pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc); + return; + } + + /* Set sensor-specific vars */ + sd->sif = 1; +} + +/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ +static void ov51x_led_control(struct sd *sd, int on) +{ + if (sd->invert_led) + on = !on; + + switch (sd->bridge) { + /* OV511 has no LED control */ + case BRIDGE_OV511PLUS: + reg_w(sd, R511_SYS_LED_CTL, on); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + reg_w_mask(sd, R518_GPIO_OUT, 0x02 * on, 0x02); + break; + case BRIDGE_OV519: + reg_w_mask(sd, OV519_GPIO_DATA_OUT0, on, 1); + break; + } +} + +static void sd_reset_snapshot(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->snapshot_needs_reset) + return; + + /* Note it is important that we clear sd->snapshot_needs_reset, + before actually clearing the snapshot state in the bridge + otherwise we might race with the pkt_scan interrupt handler */ + sd->snapshot_needs_reset = 0; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + reg_w(sd, R51x_SYS_SNAP, 0x02); + reg_w(sd, R51x_SYS_SNAP, 0x00); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */ + reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */ + break; + case BRIDGE_OV519: + reg_w(sd, R51x_SYS_RESET, 0x40); + reg_w(sd, R51x_SYS_RESET, 0x00); + break; + } +} + +static void ov51x_upload_quan_tables(struct sd *sd) +{ + const unsigned char yQuanTable511[] = { + 0, 1, 1, 2, 2, 3, 3, 4, + 1, 1, 1, 2, 2, 3, 4, 4, + 1, 1, 2, 2, 3, 4, 4, 4, + 2, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 5, 5, 5, + 3, 3, 4, 4, 5, 5, 5, 5, + 3, 4, 4, 4, 5, 5, 5, 5, + 4, 4, 4, 4, 5, 5, 5, 5 + }; + + const unsigned char uvQuanTable511[] = { + 0, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 2, 4, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 4, 4, 4, + 3, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4 + }; + + /* OV518 quantization tables are 8x4 (instead of 8x8) */ + const unsigned char yQuanTable518[] = { + 5, 4, 5, 6, 6, 7, 7, 7, + 5, 5, 5, 5, 6, 7, 7, 7, + 6, 6, 6, 6, 7, 7, 7, 8, + 7, 7, 6, 7, 7, 7, 8, 8 + }; + const unsigned char uvQuanTable518[] = { + 6, 6, 6, 7, 7, 7, 7, 7, + 6, 6, 6, 7, 7, 7, 7, 7, + 6, 6, 6, 7, 7, 7, 7, 8, + 7, 7, 7, 7, 7, 7, 8, 8 + }; + + const unsigned char *pYTable, *pUVTable; + unsigned char val0, val1; + int i, size, reg = R51x_COMP_LUT_BEGIN; + + PDEBUG(D_PROBE, "Uploading quantization tables"); + + if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) { + pYTable = yQuanTable511; + pUVTable = uvQuanTable511; + size = 32; + } else { + pYTable = yQuanTable518; + pUVTable = uvQuanTable518; + size = 16; + } + + for (i = 0; i < size; i++) { + val0 = *pYTable++; + val1 = *pYTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + reg_w(sd, reg, val0); + + val0 = *pUVTable++; + val1 = *pUVTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + reg_w(sd, reg + size, val0); + + reg++; + } +} + +/* This initializes the OV511/OV511+ and the sensor */ +static void ov511_configure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* For 511 and 511+ */ + const struct ov_regvals init_511[] = { + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3d }, + }; + + const struct ov_regvals norm_511[] = { + { R511_DRAM_FLOW_CTL, 0x01 }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0x1f }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + + const struct ov_regvals norm_511_p[] = { + { R511_DRAM_FLOW_CTL, 0xff }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0xff }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + + const struct ov_regvals compress_511[] = { + { 0x70, 0x1f }, + { 0x71, 0x05 }, + { 0x72, 0x06 }, + { 0x73, 0x06 }, + { 0x74, 0x14 }, + { 0x75, 0x03 }, + { 0x76, 0x04 }, + { 0x77, 0x04 }, + }; + + PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID)); + + write_regvals(sd, init_511, ARRAY_SIZE(init_511)); + + switch (sd->bridge) { + case BRIDGE_OV511: + write_regvals(sd, norm_511, ARRAY_SIZE(norm_511)); + break; + case BRIDGE_OV511PLUS: + write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p)); + break; + } + + /* Init compression */ + write_regvals(sd, compress_511, ARRAY_SIZE(compress_511)); + + ov51x_upload_quan_tables(sd); +} + +/* This initializes the OV518/OV518+ and the sensor */ +static void ov518_configure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* For 518 and 518+ */ + const struct ov_regvals init_518[] = { + { R51x_SYS_RESET, 0x40 }, + { R51x_SYS_INIT, 0xe1 }, + { R51x_SYS_RESET, 0x3e }, + { R51x_SYS_INIT, 0xe1 }, + { R51x_SYS_RESET, 0x00 }, + { R51x_SYS_INIT, 0xe1 }, + { 0x46, 0x00 }, + { 0x5d, 0x03 }, + }; + + const struct ov_regvals norm_518[] = { + { R51x_SYS_SNAP, 0x02 }, /* Reset */ + { R51x_SYS_SNAP, 0x01 }, /* Enable */ + { 0x31, 0x0f }, + { 0x5d, 0x03 }, + { 0x24, 0x9f }, + { 0x25, 0x90 }, + { 0x20, 0x00 }, + { 0x51, 0x04 }, + { 0x71, 0x19 }, + { 0x2f, 0x80 }, + }; + + const struct ov_regvals norm_518_p[] = { + { R51x_SYS_SNAP, 0x02 }, /* Reset */ + { R51x_SYS_SNAP, 0x01 }, /* Enable */ + { 0x31, 0x0f }, + { 0x5d, 0x03 }, + { 0x24, 0x9f }, + { 0x25, 0x90 }, + { 0x20, 0x60 }, + { 0x51, 0x02 }, + { 0x71, 0x19 }, + { 0x40, 0xff }, + { 0x41, 0x42 }, + { 0x46, 0x00 }, + { 0x33, 0x04 }, + { 0x21, 0x19 }, + { 0x3f, 0x10 }, + { 0x2f, 0x80 }, + }; + + /* First 5 bits of custom ID reg are a revision ID on OV518 */ + PDEBUG(D_PROBE, "Device revision %d", + 0x1f & reg_r(sd, R51x_SYS_CUST_ID)); + + write_regvals(sd, init_518, ARRAY_SIZE(init_518)); + + /* Set LED GPIO pin to output mode */ + reg_w_mask(sd, R518_GPIO_CTL, 0x00, 0x02); + + switch (sd->bridge) { + case BRIDGE_OV518: + write_regvals(sd, norm_518, ARRAY_SIZE(norm_518)); + break; + case BRIDGE_OV518PLUS: + write_regvals(sd, norm_518_p, ARRAY_SIZE(norm_518_p)); + break; + } + + ov51x_upload_quan_tables(sd); + + reg_w(sd, 0x2f, 0x80); +} + +static void ov519_configure(struct sd *sd) +{ + static const struct ov_regvals init_519[] = { + { 0x5a, 0x6d }, /* EnableSystem */ + { 0x53, 0x9b }, /* don't enable the microcontroller */ + { OV519_R54_EN_CLK1, 0xff }, /* set bit2 to enable jpeg */ + { 0x5d, 0x03 }, + { 0x49, 0x01 }, + { 0x48, 0x00 }, + /* Set LED pin to output mode. Bit 4 must be cleared or sensor + * detection will fail. This deserves further investigation. */ + { OV519_GPIO_IO_CTRL0, 0xee }, + { OV519_R51_RESET1, 0x0f }, + { OV519_R51_RESET1, 0x00 }, + { 0x22, 0x00 }, + /* windows reads 0x55 at this point*/ + }; + + write_regvals(sd, init_519, ARRAY_SIZE(init_519)); +} + +static void ovfx2_configure(struct sd *sd) +{ + static const struct ov_regvals init_fx2[] = { + { 0x00, 0x60 }, + { 0x02, 0x01 }, + { 0x0f, 0x1d }, + { 0xe9, 0x82 }, + { 0xea, 0xc7 }, + { 0xeb, 0x10 }, + { 0xec, 0xf6 }, + }; + + sd->stopped = 1; + + write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2)); +} + +/* set the mode */ +/* This function works for ov7660 only */ +static void ov519_set_mode(struct sd *sd) +{ + static const struct ov_regvals bridge_ov7660[2][10] = { + {{0x10, 0x14}, {0x11, 0x1e}, {0x12, 0x00}, {0x13, 0x00}, + {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c}, + {0x25, 0x01}, {0x26, 0x00}}, + {{0x10, 0x28}, {0x11, 0x3c}, {0x12, 0x00}, {0x13, 0x00}, + {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c}, + {0x25, 0x03}, {0x26, 0x00}} + }; + static const struct ov_i2c_regvals sensor_ov7660[2][3] = { + {{0x12, 0x00}, {0x24, 0x00}, {0x0c, 0x0c}}, + {{0x12, 0x00}, {0x04, 0x00}, {0x0c, 0x00}} + }; + static const struct ov_i2c_regvals sensor_ov7660_2[] = { + {OV7670_R17_HSTART, 0x13}, + {OV7670_R18_HSTOP, 0x01}, + {OV7670_R32_HREF, 0x92}, + {OV7670_R19_VSTART, 0x02}, + {OV7670_R1A_VSTOP, 0x7a}, + {OV7670_R03_VREF, 0x00}, +/* {0x33, 0x00}, */ +/* {0x34, 0x07}, */ +/* {0x36, 0x00}, */ +/* {0x6b, 0x0a}, */ + }; + + write_regvals(sd, bridge_ov7660[sd->gspca_dev.curr_mode], + ARRAY_SIZE(bridge_ov7660[0])); + write_i2c_regvals(sd, sensor_ov7660[sd->gspca_dev.curr_mode], + ARRAY_SIZE(sensor_ov7660[0])); + write_i2c_regvals(sd, sensor_ov7660_2, + ARRAY_SIZE(sensor_ov7660_2)); +} + +/* set the frame rate */ +/* This function works for sensors ov7640, ov7648 ov7660 and ov7670 only */ +static void ov519_set_fr(struct sd *sd) +{ + int fr; + u8 clock; + /* frame rate table with indices: + * - mode = 0: 320x240, 1: 640x480 + * - fr rate = 0: 30, 1: 25, 2: 20, 3: 15, 4: 10, 5: 5 + * - reg = 0: bridge a4, 1: bridge 23, 2: sensor 11 (clock) + */ + static const u8 fr_tb[2][6][3] = { + {{0x04, 0xff, 0x00}, + {0x04, 0x1f, 0x00}, + {0x04, 0x1b, 0x00}, + {0x04, 0x15, 0x00}, + {0x04, 0x09, 0x00}, + {0x04, 0x01, 0x00}}, + {{0x0c, 0xff, 0x00}, + {0x0c, 0x1f, 0x00}, + {0x0c, 0x1b, 0x00}, + {0x04, 0xff, 0x01}, + {0x04, 0x1f, 0x01}, + {0x04, 0x1b, 0x01}}, + }; + + if (frame_rate > 0) + sd->frame_rate = frame_rate; + if (sd->frame_rate >= 30) + fr = 0; + else if (sd->frame_rate >= 25) + fr = 1; + else if (sd->frame_rate >= 20) + fr = 2; + else if (sd->frame_rate >= 15) + fr = 3; + else if (sd->frame_rate >= 10) + fr = 4; + else + fr = 5; + reg_w(sd, 0xa4, fr_tb[sd->gspca_dev.curr_mode][fr][0]); + reg_w(sd, 0x23, fr_tb[sd->gspca_dev.curr_mode][fr][1]); + clock = fr_tb[sd->gspca_dev.curr_mode][fr][2]; + if (sd->sensor == SEN_OV7660) + clock |= 0x80; /* enable double clock */ + ov518_i2c_w(sd, OV7670_R11_CLKRC, clock); +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + sd->bridge = id->driver_info & BRIDGE_MASK; + sd->invert_led = (id->driver_info & BRIDGE_INVERT_LED) != 0; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + cam->cam_mode = ov511_vga_mode; + cam->nmodes = ARRAY_SIZE(ov511_vga_mode); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + cam->cam_mode = ov518_vga_mode; + cam->nmodes = ARRAY_SIZE(ov518_vga_mode); + break; + case BRIDGE_OV519: + cam->cam_mode = ov519_vga_mode; + cam->nmodes = ARRAY_SIZE(ov519_vga_mode); + break; + case BRIDGE_OVFX2: + cam->cam_mode = ov519_vga_mode; + cam->nmodes = ARRAY_SIZE(ov519_vga_mode); + cam->bulk_size = OVFX2_BULK_SIZE; + cam->bulk_nurbs = MAX_NURBS; + cam->bulk = 1; + break; + case BRIDGE_W9968CF: + cam->cam_mode = w9968cf_vga_mode; + cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode); + break; + } + + sd->frame_rate = 15; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ov511_configure(gspca_dev); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + ov518_configure(gspca_dev); + break; + case BRIDGE_OV519: + ov519_configure(sd); + break; + case BRIDGE_OVFX2: + ovfx2_configure(sd); + break; + case BRIDGE_W9968CF: + w9968cf_configure(sd); + break; + } + + /* The OV519 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + sd->sensor = -1; + + /* Test for 76xx */ + if (init_ov_sensor(sd, OV7xx0_SID) >= 0) { + ov7xx0_configure(sd); + + /* Test for 6xx0 */ + } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) { + ov6xx0_configure(sd); + + /* Test for 8xx0 */ + } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) { + ov8xx0_configure(sd); + + /* Test for 3xxx / 2xxx */ + } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) { + ov_hires_configure(sd); + } else { + pr_err("Can't determine sensor slave IDs\n"); + goto error; + } + + if (sd->sensor < 0) + goto error; + + ov51x_led_control(sd, 0); /* turn LED off */ + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + if (sd->sif) { + cam->cam_mode = ov511_sif_mode; + cam->nmodes = ARRAY_SIZE(ov511_sif_mode); + } + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + if (sd->sif) { + cam->cam_mode = ov518_sif_mode; + cam->nmodes = ARRAY_SIZE(ov518_sif_mode); + } + break; + case BRIDGE_OV519: + if (sd->sif) { + cam->cam_mode = ov519_sif_mode; + cam->nmodes = ARRAY_SIZE(ov519_sif_mode); + } + break; + case BRIDGE_OVFX2: + switch (sd->sensor) { + case SEN_OV2610: + case SEN_OV2610AE: + cam->cam_mode = ovfx2_ov2610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode); + break; + case SEN_OV3610: + cam->cam_mode = ovfx2_ov3610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode); + break; + case SEN_OV9600: + cam->cam_mode = ovfx2_ov9600_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode); + break; + default: + if (sd->sif) { + cam->cam_mode = ov519_sif_mode; + cam->nmodes = ARRAY_SIZE(ov519_sif_mode); + } + break; + } + break; + case BRIDGE_W9968CF: + if (sd->sif) + cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode) - 1; + + /* w9968cf needs initialisation once the sensor is known */ + w9968cf_init(sd); + break; + } + + /* initialize the sensor */ + switch (sd->sensor) { + case SEN_OV2610: + write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610)); + + /* Enable autogain, autoexpo, awb, bandfilter */ + i2c_w_mask(sd, 0x13, 0x27, 0x27); + break; + case SEN_OV2610AE: + write_i2c_regvals(sd, norm_2610ae, ARRAY_SIZE(norm_2610ae)); + + /* enable autoexpo */ + i2c_w_mask(sd, 0x13, 0x05, 0x05); + break; + case SEN_OV3610: + write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b)); + + /* Enable autogain, autoexpo, awb, bandfilter */ + i2c_w_mask(sd, 0x13, 0x27, 0x27); + break; + case SEN_OV6620: + write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20)); + break; + case SEN_OV6630: + case SEN_OV66308AF: + write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30)); + break; + default: +/* case SEN_OV7610: */ +/* case SEN_OV76BE: */ + write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610)); + i2c_w_mask(sd, 0x0e, 0x00, 0x40); + break; + case SEN_OV7620: + case SEN_OV7620AE: + write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620)); + break; + case SEN_OV7640: + case SEN_OV7648: + write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640)); + break; + case SEN_OV7660: + i2c_w(sd, OV7670_R12_COM7, OV7670_COM7_RESET); + msleep(14); + reg_w(sd, OV519_R57_SNAPSHOT, 0x23); + write_regvals(sd, init_519_ov7660, + ARRAY_SIZE(init_519_ov7660)); + write_i2c_regvals(sd, norm_7660, ARRAY_SIZE(norm_7660)); + sd->gspca_dev.curr_mode = 1; /* 640x480 */ + ov519_set_mode(sd); + ov519_set_fr(sd); + sd_reset_snapshot(gspca_dev); + ov51x_restart(sd); + ov51x_stop(sd); /* not in win traces */ + ov51x_led_control(sd, 0); + break; + case SEN_OV7670: + write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670)); + break; + case SEN_OV8610: + write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610)); + break; + case SEN_OV9600: + write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600)); + + /* enable autoexpo */ +/* i2c_w_mask(sd, 0x13, 0x05, 0x05); */ + break; + } + return gspca_dev->usb_err; +error: + PDEBUG(D_ERR, "OV519 Config failed"); + return -EINVAL; +} + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + case BRIDGE_OVFX2: + if (gspca_dev->width != 800) + gspca_dev->cam.bulk_size = OVFX2_BULK_SIZE; + else + gspca_dev->cam.bulk_size = 7 * 4096; + break; + } + return 0; +} + +/* Set up the OV511/OV511+ with the given image parameters. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static void ov511_mode_init_regs(struct sd *sd) +{ + int hsegs, vsegs, packet_size, fps, needed; + int interlaced = 0; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + pr_err("Couldn't get altsetting\n"); + sd->gspca_dev.usb_err = -EIO; + return; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5); + + reg_w(sd, R511_CAM_UV_EN, 0x01); + reg_w(sd, R511_SNAP_UV_EN, 0x01); + reg_w(sd, R511_SNAP_OPTS, 0x03); + + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + hsegs = (sd->gspca_dev.width >> 3) - 1; + vsegs = (sd->gspca_dev.height >> 3) - 1; + + reg_w(sd, R511_CAM_PXCNT, hsegs); + reg_w(sd, R511_CAM_LNCNT, vsegs); + reg_w(sd, R511_CAM_PXDIV, 0x00); + reg_w(sd, R511_CAM_LNDIV, 0x00); + + /* YUV420, low pass filter on */ + reg_w(sd, R511_CAM_OPTS, 0x03); + + /* Snapshot additions */ + reg_w(sd, R511_SNAP_PXCNT, hsegs); + reg_w(sd, R511_SNAP_LNCNT, vsegs); + reg_w(sd, R511_SNAP_PXDIV, 0x00); + reg_w(sd, R511_SNAP_LNDIV, 0x00); + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + + switch (sd->sensor) { + case SEN_OV6620: + /* No framerate control, doesn't like higher rates yet */ + sd->clockdiv = 3; + break; + + /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed + for more sensors we need to do this for them too */ + case SEN_OV7620: + case SEN_OV7620AE: + case SEN_OV7640: + case SEN_OV7648: + case SEN_OV76BE: + if (sd->gspca_dev.width == 320) + interlaced = 1; + /* Fall through */ + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7670: + switch (sd->frame_rate) { + case 30: + case 25: + /* Not enough bandwidth to do 640x480 @ 30 fps */ + if (sd->gspca_dev.width != 640) { + sd->clockdiv = 0; + break; + } + /* Fall through for 640x480 case */ + default: +/* case 20: */ +/* case 15: */ + sd->clockdiv = 1; + break; + case 10: + sd->clockdiv = 2; + break; + case 5: + sd->clockdiv = 5; + break; + } + if (interlaced) { + sd->clockdiv = (sd->clockdiv + 1) * 2 - 1; + /* Higher then 10 does not work */ + if (sd->clockdiv > 10) + sd->clockdiv = 10; + } + break; + + case SEN_OV8610: + /* No framerate control ?? */ + sd->clockdiv = 0; + break; + } + + /* Check if we have enough bandwidth to disable compression */ + fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1; + needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2; + /* 1000 isoc packets/sec */ + if (needed > 1000 * packet_size) { + /* Enable Y and UV quantization and compression */ + reg_w(sd, R511_COMP_EN, 0x07); + reg_w(sd, R511_COMP_LUT_EN, 0x03); + } else { + reg_w(sd, R511_COMP_EN, 0x06); + reg_w(sd, R511_COMP_LUT_EN, 0x00); + } + + reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE); + reg_w(sd, R51x_SYS_RESET, 0); +} + +/* Sets up the OV518/OV518+ with the given image parameters + * + * OV518 needs a completely different approach, until we can figure out what + * the individual registers do. Also, only 15 FPS is supported now. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static void ov518_mode_init_regs(struct sd *sd) +{ + int hsegs, vsegs, packet_size; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + pr_err("Couldn't get altsetting\n"); + sd->gspca_dev.usb_err = -EIO; + return; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2); + + /******** Set the mode ********/ + reg_w(sd, 0x2b, 0); + reg_w(sd, 0x2c, 0); + reg_w(sd, 0x2d, 0); + reg_w(sd, 0x2e, 0); + reg_w(sd, 0x3b, 0); + reg_w(sd, 0x3c, 0); + reg_w(sd, 0x3d, 0); + reg_w(sd, 0x3e, 0); + + if (sd->bridge == BRIDGE_OV518) { + /* Set 8-bit (YVYU) input format */ + reg_w_mask(sd, 0x20, 0x08, 0x08); + + /* Set 12-bit (4:2:0) output format */ + reg_w_mask(sd, 0x28, 0x80, 0xf0); + reg_w_mask(sd, 0x38, 0x80, 0xf0); + } else { + reg_w(sd, 0x28, 0x80); + reg_w(sd, 0x38, 0x80); + } + + hsegs = sd->gspca_dev.width / 16; + vsegs = sd->gspca_dev.height / 4; + + reg_w(sd, 0x29, hsegs); + reg_w(sd, 0x2a, vsegs); + + reg_w(sd, 0x39, hsegs); + reg_w(sd, 0x3a, vsegs); + + /* Windows driver does this here; who knows why */ + reg_w(sd, 0x2f, 0x80); + + /******** Set the framerate ********/ + sd->clockdiv = 1; + + /* Mode independent, but framerate dependent, regs */ + /* 0x51: Clock divider; Only works on some cams which use 2 crystals */ + reg_w(sd, 0x51, 0x04); + reg_w(sd, 0x22, 0x18); + reg_w(sd, 0x23, 0xff); + + if (sd->bridge == BRIDGE_OV518PLUS) { + switch (sd->sensor) { + case SEN_OV7620AE: + if (sd->gspca_dev.width == 320) { + reg_w(sd, 0x20, 0x00); + reg_w(sd, 0x21, 0x19); + } else { + reg_w(sd, 0x20, 0x60); + reg_w(sd, 0x21, 0x1f); + } + break; + case SEN_OV7620: + reg_w(sd, 0x20, 0x00); + reg_w(sd, 0x21, 0x19); + break; + default: + reg_w(sd, 0x21, 0x19); + } + } else + reg_w(sd, 0x71, 0x17); /* Compression-related? */ + + /* FIXME: Sensor-specific */ + /* Bit 5 is what matters here. Of course, it is "reserved" */ + i2c_w(sd, 0x54, 0x23); + + reg_w(sd, 0x2f, 0x80); + + if (sd->bridge == BRIDGE_OV518PLUS) { + reg_w(sd, 0x24, 0x94); + reg_w(sd, 0x25, 0x90); + ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(sd, 0xc6, 540, 2); /* 21ch */ + ov518_reg_w32(sd, 0xc7, 540, 2); /* 21ch */ + ov518_reg_w32(sd, 0xc8, 108, 2); /* 6ch */ + ov518_reg_w32(sd, 0xca, 131098, 3); /* 2001ah */ + ov518_reg_w32(sd, 0xcb, 532, 2); /* 214h */ + ov518_reg_w32(sd, 0xcc, 2400, 2); /* 960h */ + ov518_reg_w32(sd, 0xcd, 32, 2); /* 20h */ + ov518_reg_w32(sd, 0xce, 608, 2); /* 260h */ + } else { + reg_w(sd, 0x24, 0x9f); + reg_w(sd, 0x25, 0x90); + ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(sd, 0xc6, 381, 2); /* 17dh */ + ov518_reg_w32(sd, 0xc7, 381, 2); /* 17dh */ + ov518_reg_w32(sd, 0xc8, 128, 2); /* 80h */ + ov518_reg_w32(sd, 0xca, 183331, 3); /* 2cc23h */ + ov518_reg_w32(sd, 0xcb, 746, 2); /* 2eah */ + ov518_reg_w32(sd, 0xcc, 1750, 2); /* 6d6h */ + ov518_reg_w32(sd, 0xcd, 45, 2); /* 2dh */ + ov518_reg_w32(sd, 0xce, 851, 2); /* 353h */ + } + + reg_w(sd, 0x2f, 0x80); +} + +/* Sets up the OV519 with the given image parameters + * + * OV519 needs a completely different approach, until we can figure out what + * the individual registers do. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static void ov519_mode_init_regs(struct sd *sd) +{ + static const struct ov_regvals mode_init_519_ov7670[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x20, 0x0c }, + { 0x21, 0x38 }, + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + + static const struct ov_regvals mode_init_519[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + + /******** Set the mode ********/ + switch (sd->sensor) { + default: + write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519)); + if (sd->sensor == SEN_OV7640 || + sd->sensor == SEN_OV7648) { + /* Select 8-bit input mode */ + reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10); + } + break; + case SEN_OV7660: + return; /* done by ov519_set_mode/fr() */ + case SEN_OV7670: + write_regvals(sd, mode_init_519_ov7670, + ARRAY_SIZE(mode_init_519_ov7670)); + break; + } + + reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4); + reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3); + if (sd->sensor == SEN_OV7670 && + sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) + reg_w(sd, OV519_R12_X_OFFSETL, 0x04); + else if (sd->sensor == SEN_OV7648 && + sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) + reg_w(sd, OV519_R12_X_OFFSETL, 0x01); + else + reg_w(sd, OV519_R12_X_OFFSETL, 0x00); + reg_w(sd, OV519_R13_X_OFFSETH, 0x00); + reg_w(sd, OV519_R14_Y_OFFSETL, 0x00); + reg_w(sd, OV519_R15_Y_OFFSETH, 0x00); + reg_w(sd, OV519_R16_DIVIDER, 0x00); + reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */ + reg_w(sd, 0x26, 0x00); /* Undocumented */ + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + +/* FIXME: These are only valid at the max resolution. */ + sd->clockdiv = 0; + switch (sd->sensor) { + case SEN_OV7640: + case SEN_OV7648: + switch (sd->frame_rate) { + default: +/* case 30: */ + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0xff); + break; + case 25: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1f); + break; + case 20: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1b); + break; + case 15: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + case 10: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1f); + sd->clockdiv = 1; + break; + case 5: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1b); + sd->clockdiv = 1; + break; + } + break; + case SEN_OV8610: + switch (sd->frame_rate) { + default: /* 15 fps */ +/* case 15: */ + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0xff); + break; + case 10: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1f); + break; + case 5: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1b); + break; + } + break; + case SEN_OV7670: /* guesses, based on 7640 */ + PDEBUG(D_STREAM, "Setting framerate to %d fps", + (sd->frame_rate == 0) ? 15 : sd->frame_rate); + reg_w(sd, 0xa4, 0x10); + switch (sd->frame_rate) { + case 30: + reg_w(sd, 0x23, 0xff); + break; + case 20: + reg_w(sd, 0x23, 0x1b); + break; + default: +/* case 15: */ + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + } + break; + } +} + +static void mode_init_ov_sensor_regs(struct sd *sd) +{ + struct gspca_dev *gspca_dev; + int qvga, xstart, xend, ystart, yend; + u8 v; + + gspca_dev = &sd->gspca_dev; + qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1; + + /******** Mode (VGA/QVGA) and sensor specific regs ********/ + switch (sd->sensor) { + case SEN_OV2610: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); + i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + return; + case SEN_OV2610AE: { + u8 v; + + /* frame rates: + * 10fps / 5 fps for 1600x1200 + * 40fps / 20fps for 800x600 + */ + v = 80; + if (qvga) { + if (sd->frame_rate < 25) + v = 0x81; + } else { + if (sd->frame_rate < 10) + v = 0x81; + } + i2c_w(sd, 0x11, v); + i2c_w(sd, 0x12, qvga ? 0x60 : 0x20); + return; + } + case SEN_OV3610: + if (qvga) { + xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4); + ystart = (776 - gspca_dev->height) / 2; + } else { + xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4); + ystart = (1544 - gspca_dev->height) / 2; + } + xend = xstart + gspca_dev->width; + yend = ystart + gspca_dev->height; + /* Writing to the COMH register resets the other windowing regs + to their default values, so we must do this first. */ + i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0); + i2c_w_mask(sd, 0x32, + (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7), + 0x3f); + i2c_w_mask(sd, 0x03, + (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3), + 0x0f); + i2c_w(sd, 0x17, xstart >> 4); + i2c_w(sd, 0x18, xend >> 4); + i2c_w(sd, 0x19, ystart >> 3); + i2c_w(sd, 0x1a, yend >> 3); + return; + case SEN_OV8610: + /* For OV8610 qvga means qsvga */ + i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */ + i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */ + break; + case SEN_OV7610: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + break; + case SEN_OV7620: + case SEN_OV7620AE: + case SEN_OV76BE: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); + i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + if (sd->sensor == SEN_OV76BE) + i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); + break; + case SEN_OV7640: + case SEN_OV7648: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + /* Setting this undocumented bit in qvga mode removes a very + annoying vertical shaking of the image */ + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + /* Unknown */ + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + /* Allow higher automatic gain (to allow higher framerates) */ + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */ + break; + case SEN_OV7670: + /* set COM7_FMT_VGA or COM7_FMT_QVGA + * do we need to set anything else? + * HSTART etc are set in set_ov_sensor_window itself */ + i2c_w_mask(sd, OV7670_R12_COM7, + qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, + OV7670_COM7_FMT_MASK); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_AWB, + OV7670_COM8_AWB); + if (qvga) { /* QVGA from ov7670.c by + * Jonathan Corbet */ + xstart = 164; + xend = 28; + ystart = 14; + yend = 494; + } else { /* VGA */ + xstart = 158; + xend = 14; + ystart = 10; + yend = 490; + } + /* OV7670 hardware window registers are split across + * multiple locations */ + i2c_w(sd, OV7670_R17_HSTART, xstart >> 3); + i2c_w(sd, OV7670_R18_HSTOP, xend >> 3); + v = i2c_r(sd, OV7670_R32_HREF); + v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_R32_HREF, v); + + i2c_w(sd, OV7670_R19_VSTART, ystart >> 2); + i2c_w(sd, OV7670_R1A_VSTOP, yend >> 2); + v = i2c_r(sd, OV7670_R03_VREF); + v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_R03_VREF, v); + break; + case SEN_OV6620: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + break; + case SEN_OV6630: + case SEN_OV66308AF: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + break; + case SEN_OV9600: { + const struct ov_i2c_regvals *vals; + static const struct ov_i2c_regvals sxga_15[] = { + {0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75} + }; + static const struct ov_i2c_regvals sxga_7_5[] = { + {0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75} + }; + static const struct ov_i2c_regvals vga_30[] = { + {0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60} + }; + static const struct ov_i2c_regvals vga_15[] = { + {0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70} + }; + + /* frame rates: + * 15fps / 7.5 fps for 1280x1024 + * 30fps / 15fps for 640x480 + */ + i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40); + if (qvga) + vals = sd->frame_rate < 30 ? vga_15 : vga_30; + else + vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15; + write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15)); + return; + } + default: + return; + } + + /******** Clock programming ********/ + i2c_w(sd, 0x11, sd->clockdiv); +} + +/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */ +static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->gspca_dev.streaming) + reg_w(sd, OV519_R51_RESET1, 0x0f); /* block stream */ + i2c_w_mask(sd, OV7670_R1E_MVFP, + OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip, + OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP); + if (sd->gspca_dev.streaming) + reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */ +} + +static void set_ov_sensor_window(struct sd *sd) +{ + struct gspca_dev *gspca_dev; + int qvga, crop; + int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; + + /* mode setup is fully handled in mode_init_ov_sensor_regs for these */ + switch (sd->sensor) { + case SEN_OV2610: + case SEN_OV2610AE: + case SEN_OV3610: + case SEN_OV7670: + case SEN_OV9600: + mode_init_ov_sensor_regs(sd); + return; + case SEN_OV7660: + ov519_set_mode(sd); + ov519_set_fr(sd); + return; + } + + gspca_dev = &sd->gspca_dev; + qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1; + crop = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 2; + + /* The different sensor ICs handle setting up of window differently. + * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ + switch (sd->sensor) { + case SEN_OV8610: + hwsbase = 0x1e; + hwebase = 0x1e; + vwsbase = 0x02; + vwebase = 0x02; + break; + case SEN_OV7610: + case SEN_OV76BE: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV66308AF: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; + if (sd->sensor == SEN_OV66308AF && qvga) + /* HDG: this fixes U and V getting swapped */ + hwsbase++; + if (crop) { + hwsbase += 8; + hwebase += 8; + vwsbase += 11; + vwebase += 11; + } + break; + case SEN_OV7620: + case SEN_OV7620AE: + hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ + hwebase = 0x2f; + vwsbase = vwebase = 0x05; + break; + case SEN_OV7640: + case SEN_OV7648: + hwsbase = 0x1a; + hwebase = 0x1a; + vwsbase = vwebase = 0x03; + break; + default: + return; + } + + switch (sd->sensor) { + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV66308AF: + if (qvga) { /* QCIF */ + hwscale = 0; + vwscale = 0; + } else { /* CIF */ + hwscale = 1; + vwscale = 1; /* The datasheet says 0; + * it's wrong */ + } + break; + case SEN_OV8610: + if (qvga) { /* QSVGA */ + hwscale = 1; + vwscale = 1; + } else { /* SVGA */ + hwscale = 2; + vwscale = 2; + } + break; + default: /* SEN_OV7xx0 */ + if (qvga) { /* QVGA */ + hwscale = 1; + vwscale = 0; + } else { /* VGA */ + hwscale = 2; + vwscale = 1; + } + } + + mode_init_ov_sensor_regs(sd); + + i2c_w(sd, 0x17, hwsbase); + i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale)); + i2c_w(sd, 0x19, vwsbase); + i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale)); +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Default for most bridges, allow bridge_mode_init_regs to override */ + sd->sensor_width = sd->gspca_dev.width; + sd->sensor_height = sd->gspca_dev.height; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ov511_mode_init_regs(sd); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + ov518_mode_init_regs(sd); + break; + case BRIDGE_OV519: + ov519_mode_init_regs(sd); + break; + /* case BRIDGE_OVFX2: nothing to do */ + case BRIDGE_W9968CF: + w9968cf_mode_init_regs(sd); + break; + } + + set_ov_sensor_window(sd); + + /* Force clear snapshot state in case the snapshot button was + pressed while we weren't streaming */ + sd->snapshot_needs_reset = 1; + sd_reset_snapshot(gspca_dev); + + sd->first_frame = 3; + + ov51x_restart(sd); + ov51x_led_control(sd, 1); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + ov51x_stop(sd); + ov51x_led_control(sd, 0); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->gspca_dev.present) + return; + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_stop0(sd); + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + /* If the last button state is pressed, release it now! */ + if (sd->snapshot_pressed) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + sd->snapshot_pressed = 0; + } +#endif + if (sd->bridge == BRIDGE_OV519) + reg_w(sd, OV519_R57_SNAPSHOT, 0x23); +} + +static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->snapshot_pressed != state) { +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + input_report_key(gspca_dev->input_dev, KEY_CAMERA, state); + input_sync(gspca_dev->input_dev); +#endif + if (state) + sd->snapshot_needs_reset = 1; + + sd->snapshot_pressed = state; + } else { + /* On the ov511 / ov519 we need to reset the button state + multiple times, as resetting does not work as long as the + button stays pressed */ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + case BRIDGE_OV519: + if (state) + sd->snapshot_needs_reset = 1; + break; + } + } +} + +static void ov511_pkt_scan(struct gspca_dev *gspca_dev, + u8 *in, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th bytes. The 9th byte is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot button on + * 1: snapshot frame + * 0: even/odd field + */ + if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) && + (in[8] & 0x08)) { + ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1); + if (in[8] & 0x80) { + /* Frame end */ + if ((in[9] + 1) * 8 != gspca_dev->width || + (in[10] + 1) * 8 != gspca_dev->height) { + PDEBUG(D_ERR, "Invalid frame size, got: %dx%d," + " requested: %dx%d\n", + (in[9] + 1) * 8, (in[10] + 1) * 8, + gspca_dev->width, gspca_dev->height); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + /* Add 11 byte footer to frame, might be useful */ + gspca_frame_add(gspca_dev, LAST_PACKET, in, 11); + return; + } else { + /* Frame start */ + gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0); + sd->packet_nr = 0; + } + } + + /* Ignore the packet number */ + len--; + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, in, len); +} + +static void ov518_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* A false positive here is likely, until OVT gives me + * the definitive SOF/EOF format */ + if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) { + ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + sd->packet_nr = 0; + } + + if (gspca_dev->last_packet_type == DISCARD_PACKET) + return; + + /* Does this device use packet numbers ? */ + if (len & 7) { + len--; + if (sd->packet_nr == data[len]) + sd->packet_nr++; + /* The last few packets of the frame (which are all 0's + except that they may contain part of the footer), are + numbered 0 */ + else if (sd->packet_nr == 0 || data[len]) { + PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)", + (int)data[len], (int)sd->packet_nr); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + } + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void ov519_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* Header of ov519 is 16 bytes: + * Byte Value Description + * 0 0xff magic + * 1 0xff magic + * 2 0xff magic + * 3 0xXX 0x50 = SOF, 0x51 = EOF + * 9 0xXX 0x01 initial frame without data, + * 0x00 standard frame with image + * 14 Lo in EOF: length of image data / 8 + * 15 Hi + */ + + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { + switch (data[3]) { + case 0x50: /* start of frame */ + /* Don't check the button state here, as the state + usually (always ?) changes at EOF and checking it + here leads to unnecessary snapshot state resets. */ +#define HDRSZ 16 + data += HDRSZ; + len -= HDRSZ; +#undef HDRSZ + if (data[0] == 0xff || data[1] == 0xd8) + gspca_frame_add(gspca_dev, FIRST_PACKET, + data, len); + else + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + case 0x51: /* end of frame */ + ov51x_handle_button(gspca_dev, data[11] & 1); + if (data[9] != 0) + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + return; + } + } + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + + /* A short read signals EOF */ + if (len < gspca_dev->cam.bulk_size) { + /* If the frame is short, and it is one of the first ones + the sensor and bridge are still syncing, so drop it. */ + if (sd->first_frame) { + sd->first_frame--; + if (gspca_dev->image_len < + sd->gspca_dev.width * sd->gspca_dev.height) + gspca_dev->last_packet_type = DISCARD_PACKET; + } + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ov511_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + ov518_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_OV519: + ov519_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_OVFX2: + ovfx2_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_W9968CF: + w9968cf_pkt_scan(gspca_dev, data, len); + break; + } +} + +/* -- management routines -- */ + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct ov_i2c_regvals brit_7660[][7] = { + {{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90}, + {0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}}, + {{0x0f, 0x6a}, {0x24, 0x50}, {0x25, 0x40}, {0x26, 0xa1}, + {0x27, 0xc0}, {0x28, 0xc0}, {0x2c, 0xc0}}, + {{0x0f, 0x6a}, {0x24, 0x68}, {0x25, 0x58}, {0x26, 0xc2}, + {0x27, 0xa0}, {0x28, 0xa0}, {0x2c, 0xa0}}, + {{0x0f, 0x6a}, {0x24, 0x70}, {0x25, 0x68}, {0x26, 0xd3}, + {0x27, 0x80}, {0x28, 0x80}, {0x2c, 0x80}}, + {{0x0f, 0x6a}, {0x24, 0x80}, {0x25, 0x70}, {0x26, 0xd3}, + {0x27, 0x20}, {0x28, 0x20}, {0x2c, 0x20}}, + {{0x0f, 0x6a}, {0x24, 0x88}, {0x25, 0x78}, {0x26, 0xd3}, + {0x27, 0x40}, {0x28, 0x40}, {0x2c, 0x40}}, + {{0x0f, 0x6a}, {0x24, 0x90}, {0x25, 0x80}, {0x26, 0xd4}, + {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}} + }; + + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV66308AF: + case SEN_OV7640: + case SEN_OV7648: + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7620: + case SEN_OV7620AE: + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7660: + write_i2c_regvals(sd, brit_7660[val], + ARRAY_SIZE(brit_7660[0])); + break; + case SEN_OV7670: +/*win trace + * i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_AEC); */ + i2c_w(sd, OV7670_R55_BRIGHT, ov7670_abs_to_sm(val)); + break; + } +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct ov_i2c_regvals contrast_7660[][31] = { + {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0}, + {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30}, + {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x24}, {0x77, 0x24}, + {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x34}, + {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x65}, + {0x80, 0x70}, {0x81, 0x77}, {0x82, 0x7d}, {0x83, 0x83}, + {0x84, 0x88}, {0x85, 0x8d}, {0x86, 0x96}, {0x87, 0x9f}, + {0x88, 0xb0}, {0x89, 0xc4}, {0x8a, 0xd9}}, + {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0x94}, + {0x70, 0x58}, {0x71, 0x40}, {0x72, 0x30}, {0x73, 0x30}, + {0x74, 0x30}, {0x75, 0x30}, {0x76, 0x2c}, {0x77, 0x24}, + {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x31}, + {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x62}, + {0x80, 0x6d}, {0x81, 0x75}, {0x82, 0x7b}, {0x83, 0x81}, + {0x84, 0x87}, {0x85, 0x8d}, {0x86, 0x98}, {0x87, 0xa1}, + {0x88, 0xb2}, {0x89, 0xc6}, {0x8a, 0xdb}}, + {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x84}, + {0x70, 0x58}, {0x71, 0x48}, {0x72, 0x40}, {0x73, 0x40}, + {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x28}, {0x77, 0x24}, + {0x78, 0x26}, {0x79, 0x28}, {0x7a, 0x28}, {0x7b, 0x34}, + {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x5d}, + {0x80, 0x68}, {0x81, 0x71}, {0x82, 0x79}, {0x83, 0x81}, + {0x84, 0x86}, {0x85, 0x8b}, {0x86, 0x95}, {0x87, 0x9e}, + {0x88, 0xb1}, {0x89, 0xc5}, {0x8a, 0xd9}}, + {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x70}, + {0x70, 0x58}, {0x71, 0x58}, {0x72, 0x48}, {0x73, 0x48}, + {0x74, 0x38}, {0x75, 0x40}, {0x76, 0x34}, {0x77, 0x34}, + {0x78, 0x2e}, {0x79, 0x28}, {0x7a, 0x24}, {0x7b, 0x22}, + {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x58}, + {0x80, 0x63}, {0x81, 0x6e}, {0x82, 0x77}, {0x83, 0x80}, + {0x84, 0x87}, {0x85, 0x8f}, {0x86, 0x9c}, {0x87, 0xa9}, + {0x88, 0xc0}, {0x89, 0xd4}, {0x8a, 0xe6}}, + {{0x6c, 0xa0}, {0x6d, 0xf0}, {0x6e, 0x90}, {0x6f, 0x80}, + {0x70, 0x70}, {0x71, 0x80}, {0x72, 0x60}, {0x73, 0x60}, + {0x74, 0x58}, {0x75, 0x60}, {0x76, 0x4c}, {0x77, 0x38}, + {0x78, 0x38}, {0x79, 0x2a}, {0x7a, 0x20}, {0x7b, 0x0e}, + {0x7c, 0x0a}, {0x7d, 0x14}, {0x7e, 0x26}, {0x7f, 0x46}, + {0x80, 0x54}, {0x81, 0x64}, {0x82, 0x70}, {0x83, 0x7c}, + {0x84, 0x87}, {0x85, 0x93}, {0x86, 0xa6}, {0x87, 0xb4}, + {0x88, 0xd0}, {0x89, 0xe5}, {0x8a, 0xf5}}, + {{0x6c, 0x60}, {0x6d, 0x80}, {0x6e, 0x60}, {0x6f, 0x80}, + {0x70, 0x80}, {0x71, 0x80}, {0x72, 0x88}, {0x73, 0x30}, + {0x74, 0x70}, {0x75, 0x68}, {0x76, 0x64}, {0x77, 0x50}, + {0x78, 0x3c}, {0x79, 0x22}, {0x7a, 0x10}, {0x7b, 0x08}, + {0x7c, 0x06}, {0x7d, 0x0e}, {0x7e, 0x1a}, {0x7f, 0x3a}, + {0x80, 0x4a}, {0x81, 0x5a}, {0x82, 0x6b}, {0x83, 0x7b}, + {0x84, 0x89}, {0x85, 0x96}, {0x86, 0xaf}, {0x87, 0xc3}, + {0x88, 0xe1}, {0x89, 0xf2}, {0x8a, 0xfa}}, + {{0x6c, 0x20}, {0x6d, 0x40}, {0x6e, 0x20}, {0x6f, 0x60}, + {0x70, 0x88}, {0x71, 0xc8}, {0x72, 0xc0}, {0x73, 0xb8}, + {0x74, 0xa8}, {0x75, 0xb8}, {0x76, 0x80}, {0x77, 0x5c}, + {0x78, 0x26}, {0x79, 0x10}, {0x7a, 0x08}, {0x7b, 0x04}, + {0x7c, 0x02}, {0x7d, 0x06}, {0x7e, 0x0a}, {0x7f, 0x22}, + {0x80, 0x33}, {0x81, 0x4c}, {0x82, 0x64}, {0x83, 0x7b}, + {0x84, 0x90}, {0x85, 0xa7}, {0x86, 0xc7}, {0x87, 0xde}, + {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}}, + }; + + switch (sd->sensor) { + case SEN_OV7610: + case SEN_OV6620: + i2c_w(sd, OV7610_REG_CNT, val); + break; + case SEN_OV6630: + case SEN_OV66308AF: + i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); + break; + case SEN_OV8610: { + static const u8 ctab[] = { + 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 5]); + break; + } + case SEN_OV7620: + case SEN_OV7620AE: { + static const u8 ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 4]); + break; + } + case SEN_OV7660: + write_i2c_regvals(sd, contrast_7660[val], + ARRAY_SIZE(contrast_7660[0])); + break; + case SEN_OV7670: + /* check that this isn't just the same as ov7610 */ + i2c_w(sd, OV7670_R56_CONTRAS, val >> 1); + break; + } +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w(sd, 0x10, val); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct ov_i2c_regvals colors_7660[][6] = { + {{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a}, + {0x53, 0x19}, {0x54, 0x23}}, + {{0x4f, 0x47}, {0x50, 0x4a}, {0x51, 0x03}, {0x52, 0x11}, + {0x53, 0x2c}, {0x54, 0x3e}}, + {{0x4f, 0x66}, {0x50, 0x6b}, {0x51, 0x05}, {0x52, 0x19}, + {0x53, 0x40}, {0x54, 0x59}}, + {{0x4f, 0x84}, {0x50, 0x8b}, {0x51, 0x06}, {0x52, 0x20}, + {0x53, 0x53}, {0x54, 0x73}}, + {{0x4f, 0xa3}, {0x50, 0xab}, {0x51, 0x08}, {0x52, 0x28}, + {0x53, 0x66}, {0x54, 0x8e}}, + }; + + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV66308AF: + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7620: + case SEN_OV7620AE: + /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ +/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e); + if (rc < 0) + goto out; */ + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7640: + case SEN_OV7648: + i2c_w(sd, OV7610_REG_SAT, val & 0xf0); + break; + case SEN_OV7660: + write_i2c_regvals(sd, colors_7660[val], + ARRAY_SIZE(colors_7660[0])); + break; + case SEN_OV7670: + /* supported later once I work out how to do it + * transparently fail now! */ + /* set REG_COM13 values for UV sat auto mode */ + break; + } +} + +static void setautobright(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10); +} + +static void setfreq_i(struct sd *sd, s32 val) +{ + if (sd->sensor == SEN_OV7660 + || sd->sensor == SEN_OV7670) { + switch (val) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT); + break; + case 1: /* 50 hz */ + i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_R3B_COM11, 0x08, 0x18); + break; + case 2: /* 60 hz */ + i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_R3B_COM11, 0x00, 0x18); + break; + case 3: /* Auto hz - ov7670 only */ + i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_R3B_COM11, OV7670_COM11_HZAUTO, + 0x18); + break; + } + } else { + switch (val) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, 0x2d, 0x00, 0x04); + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + break; + case 1: /* 50 hz (filter on and framerate adj) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + /* 20 fps -> 16.667 fps */ + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) + i2c_w(sd, 0x2b, 0x5e); + else + i2c_w(sd, 0x2b, 0xac); + break; + case 2: /* 60 hz (filter on, ...) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) { + /* 20 fps -> 15 fps */ + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + i2c_w(sd, 0x2b, 0xa8); + } else { + /* no framerate adj. */ + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + } + break; + } + } +} + +static void setfreq(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + setfreq_i(sd, val); + + /* Ugly but necessary */ + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_set_crop_window(sd); +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -ENOTTY; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | + V4L2_JPEG_MARKER_DRI; + return 0; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -ENOTTY; + + v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + return 0; +} + +static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + gspca_dev->exposure->val = i2c_r(sd, 0x10); + break; + } + return 0; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOBRIGHTNESS: + if (ctrl->is_new) + setautobright(gspca_dev, ctrl->val); + if (!ctrl->val && sd->brightness->is_new) + setbrightness(gspca_dev, sd->brightness->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev, ctrl->val, sd->vflip->val); + break; + case V4L2_CID_AUTOGAIN: + if (ctrl->is_new) + setautogain(gspca_dev, ctrl->val); + if (!ctrl->val && gspca_dev->exposure->is_new) + setexposure(gspca_dev, gspca_dev->exposure->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + return -EBUSY; /* Should never happen, as we grab the ctrl */ + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .g_volatile_ctrl = sd_g_volatile_ctrl, + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 10); + if (valid_controls[sd->sensor].has_brightness) + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, + sd->sensor == SEN_OV7660 ? 6 : 255, 1, + sd->sensor == SEN_OV7660 ? 3 : 127); + if (valid_controls[sd->sensor].has_contrast) { + if (sd->sensor == SEN_OV7660) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 6, 1, 3); + else + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, + (sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) ? 200 : 127); + } + if (valid_controls[sd->sensor].has_sat) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, + sd->sensor == SEN_OV7660 ? 4 : 255, 1, + sd->sensor == SEN_OV7660 ? 2 : 127); + if (valid_controls[sd->sensor].has_exposure) + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 255, 1, 127); + if (valid_controls[sd->sensor].has_hvflip) { + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } + if (valid_controls[sd->sensor].has_autobright) + sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1); + if (valid_controls[sd->sensor].has_autogain) + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (valid_controls[sd->sensor].has_freq) { + if (sd->sensor == SEN_OV7670) + sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + else + sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); + } + if (sd->bridge == BRIDGE_W9968CF) + sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + if (gspca_dev->autogain) + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true); + if (sd->autobright) + v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false); + if (sd->hflip) + v4l2_ctrl_cluster(2, &sd->hflip); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_reset_snapshot, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .other_input = 1, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF }, + {USB_DEVICE(0x041e, 0x4052), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x045e, 0x028c), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 }, + {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, + {USB_DEVICE(0x05a9, 0x0519), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x05a9, 0x0530), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS }, + {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, + {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, + {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF }, + {USB_DEVICE(0x8020, 0xef04), .driver_info = BRIDGE_OVFX2 }, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); + +module_param(frame_rate, int, 0644); +MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)"); diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c new file mode 100644 index 000000000000..bb09d7884b89 --- /dev/null +++ b/drivers/media/usb/gspca/ov534.c @@ -0,0 +1,1544 @@ +/* + * ov534-ov7xxx gspca driver + * + * Copyright (C) 2008 Antonio Ospite + * Copyright (C) 2008 Jim Paris + * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr + * + * Based on a prototype written by Mark Ferrell + * USB protocol reverse engineered by Jim Paris + * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ + * + * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr + * PS3 Eye camera - brightness, contrast, awb, agc, aec controls + * added by Max Thrun + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "ov534" + +#include "gspca.h" + +#include +#include + +#define OV534_REG_ADDRESS 0xf1 /* sensor address */ +#define OV534_REG_SUBADDR 0xf2 +#define OV534_REG_WRITE 0xf3 +#define OV534_REG_READ 0xf4 +#define OV534_REG_OPERATION 0xf5 +#define OV534_REG_STATUS 0xf6 + +#define OV534_OP_WRITE_3 0x37 +#define OV534_OP_WRITE_2 0x33 +#define OV534_OP_READ_2 0xf9 + +#define CTRL_TIMEOUT 500 + +MODULE_AUTHOR("Antonio Ospite "); +MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *hue; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct { /* gain control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *autowhitebalance; + struct { /* exposure control cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *plfreq; + + __u32 last_pts; + u16 last_fid; + u8 frame_rate; + + u8 sensor; +}; +enum sensors { + SENSOR_OV767x, + SENSOR_OV772x, + NSENSORS +}; + +static int sd_start(struct gspca_dev *gspca_dev); +static void sd_stopN(struct gspca_dev *gspca_dev); + + +static const struct v4l2_pix_format ov772x_mode[] = { + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ov767x_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30}; +static const u8 vga_rates[] = {60, 50, 40, 30, 15}; + +static const struct framerates ov772x_framerates[] = { + { /* 320x240 */ + .rates = qvga_rates, + .nrates = ARRAY_SIZE(qvga_rates), + }, + { /* 640x480 */ + .rates = vga_rates, + .nrates = ARRAY_SIZE(vga_rates), + }, +}; + +struct reg_array { + const u8 (*val)[2]; + int len; +}; + +static const u8 bridge_init_767x[][2] = { +/* comments from the ms-win file apollo7670.set */ +/* str1 */ + {0xf1, 0x42}, + {0x88, 0xf8}, + {0x89, 0xff}, + {0x76, 0x03}, + {0x92, 0x03}, + {0x95, 0x10}, + {0xe2, 0x00}, + {0xe7, 0x3e}, + {0x8d, 0x1c}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x1f, 0x00}, + {0xc3, 0xf9}, + {0x89, 0xff}, + {0x88, 0xf8}, + {0x76, 0x03}, + {0x92, 0x01}, + {0x93, 0x18}, + {0x1c, 0x00}, + {0x1d, 0x48}, + {0x1d, 0x00}, + {0x1d, 0xff}, + {0x1d, 0x02}, + {0x1d, 0x58}, + {0x1d, 0x00}, + {0x1c, 0x0a}, + {0x1d, 0x0a}, + {0x1d, 0x0e}, + {0xc0, 0x50}, /* HSize 640 */ + {0xc1, 0x3c}, /* VSize 480 */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xc2, 0x0c}, /* Input YUV */ + {0xc3, 0xf9}, /* enable PRE */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xe7, 0x2e}, /* this solves failure of "SuspendResumeTest" */ + {0x31, 0xf9}, /* enable 1.8V Suspend */ + {0x35, 0x02}, /* turn on JPEG */ + {0xd9, 0x10}, + {0x25, 0x42}, /* GPIO[8]:Input */ + {0x94, 0x11}, /* If the default setting is loaded when + * system boots up, this flag is closed here */ +}; +static const u8 sensor_init_767x[][2] = { + {0x12, 0x80}, + {0x11, 0x03}, + {0x3a, 0x04}, + {0x12, 0x00}, + {0x17, 0x13}, + {0x18, 0x01}, + {0x32, 0xb6}, + {0x19, 0x02}, + {0x1a, 0x7a}, + {0x03, 0x0a}, + {0x0c, 0x00}, + {0x3e, 0x00}, + {0x70, 0x3a}, + {0x71, 0x35}, + {0x72, 0x11}, + {0x73, 0xf0}, + {0xa2, 0x02}, + {0x7a, 0x2a}, /* set Gamma=1.6 below */ + {0x7b, 0x12}, + {0x7c, 0x1d}, + {0x7d, 0x2d}, + {0x7e, 0x45}, + {0x7f, 0x50}, + {0x80, 0x59}, + {0x81, 0x62}, + {0x82, 0x6b}, + {0x83, 0x73}, + {0x84, 0x7b}, + {0x85, 0x8a}, + {0x86, 0x98}, + {0x87, 0xb2}, + {0x88, 0xca}, + {0x89, 0xe0}, + {0x13, 0xe0}, + {0x00, 0x00}, + {0x10, 0x00}, + {0x0d, 0x40}, + {0x14, 0x38}, /* gain max 16x */ + {0xa5, 0x05}, + {0xab, 0x07}, + {0x24, 0x95}, + {0x25, 0x33}, + {0x26, 0xe3}, + {0x9f, 0x78}, + {0xa0, 0x68}, + {0xa1, 0x03}, + {0xa6, 0xd8}, + {0xa7, 0xd8}, + {0xa8, 0xf0}, + {0xa9, 0x90}, + {0xaa, 0x94}, + {0x13, 0xe5}, + {0x0e, 0x61}, + {0x0f, 0x4b}, + {0x16, 0x02}, + {0x21, 0x02}, + {0x22, 0x91}, + {0x29, 0x07}, + {0x33, 0x0b}, + {0x35, 0x0b}, + {0x37, 0x1d}, + {0x38, 0x71}, + {0x39, 0x2a}, + {0x3c, 0x78}, + {0x4d, 0x40}, + {0x4e, 0x20}, + {0x69, 0x00}, + {0x6b, 0x4a}, + {0x74, 0x10}, + {0x8d, 0x4f}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x90, 0x00}, + {0x91, 0x00}, + {0x96, 0x00}, + {0x9a, 0x80}, + {0xb0, 0x84}, + {0xb1, 0x0c}, + {0xb2, 0x0e}, + {0xb3, 0x82}, + {0xb8, 0x0a}, + {0x43, 0x0a}, + {0x44, 0xf0}, + {0x45, 0x34}, + {0x46, 0x58}, + {0x47, 0x28}, + {0x48, 0x3a}, + {0x59, 0x88}, + {0x5a, 0x88}, + {0x5b, 0x44}, + {0x5c, 0x67}, + {0x5d, 0x49}, + {0x5e, 0x0e}, + {0x6c, 0x0a}, + {0x6d, 0x55}, + {0x6e, 0x11}, + {0x6f, 0x9f}, + {0x6a, 0x40}, + {0x01, 0x40}, + {0x02, 0x40}, + {0x13, 0xe7}, + {0x4f, 0x80}, + {0x50, 0x80}, + {0x51, 0x00}, + {0x52, 0x22}, + {0x53, 0x5e}, + {0x54, 0x80}, + {0x58, 0x9e}, + {0x41, 0x08}, + {0x3f, 0x00}, + {0x75, 0x04}, + {0x76, 0xe1}, + {0x4c, 0x00}, + {0x77, 0x01}, + {0x3d, 0xc2}, + {0x4b, 0x09}, + {0xc9, 0x60}, + {0x41, 0x38}, /* jfm: auto sharpness + auto de-noise */ + {0x56, 0x40}, + {0x34, 0x11}, + {0x3b, 0xc2}, + {0xa4, 0x8a}, /* Night mode trigger point */ + {0x96, 0x00}, + {0x97, 0x30}, + {0x98, 0x20}, + {0x99, 0x20}, + {0x9a, 0x84}, + {0x9b, 0x29}, + {0x9c, 0x03}, + {0x9d, 0x4c}, + {0x9e, 0x3f}, + {0x78, 0x04}, + {0x79, 0x01}, + {0xc8, 0xf0}, + {0x79, 0x0f}, + {0xc8, 0x00}, + {0x79, 0x10}, + {0xc8, 0x7e}, + {0x79, 0x0a}, + {0xc8, 0x80}, + {0x79, 0x0b}, + {0xc8, 0x01}, + {0x79, 0x0c}, + {0xc8, 0x0f}, + {0x79, 0x0d}, + {0xc8, 0x20}, + {0x79, 0x09}, + {0xc8, 0x80}, + {0x79, 0x02}, + {0xc8, 0xc0}, + {0x79, 0x03}, + {0xc8, 0x20}, + {0x79, 0x26}, +}; +static const u8 bridge_start_vga_767x[][2] = { +/* str59 JPG */ + {0x94, 0xaa}, + {0xf1, 0x42}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0xc2, 0x0c}, + {0x35, 0x02}, /* turn on JPEG */ + {0xd9, 0x10}, + {0xda, 0x00}, /* for higher clock rate(30fps) */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xc3, 0xf9}, /* enable PRE */ + {0x8c, 0x00}, /* CIF VSize LSB[2:0] */ + {0x8d, 0x1c}, /* output YUV */ +/* {0x34, 0x05}, * enable Audio Suspend mode (?) */ + {0x50, 0x00}, /* H/V divider=0 */ + {0x51, 0xa0}, /* input H=640/4 */ + {0x52, 0x3c}, /* input V=480/4 */ + {0x53, 0x00}, /* offset X=0 */ + {0x54, 0x00}, /* offset Y=0 */ + {0x55, 0x00}, /* H/V size[8]=0 */ + {0x57, 0x00}, /* H-size[9]=0 */ + {0x5c, 0x00}, /* output size[9:8]=0 */ + {0x5a, 0xa0}, /* output H=640/4 */ + {0x5b, 0x78}, /* output V=480/4 */ + {0x1c, 0x0a}, + {0x1d, 0x0a}, + {0x94, 0x11}, +}; +static const u8 sensor_start_vga_767x[][2] = { + {0x11, 0x01}, + {0x1e, 0x04}, + {0x19, 0x02}, + {0x1a, 0x7a}, +}; +static const u8 bridge_start_qvga_767x[][2] = { +/* str86 JPG */ + {0x94, 0xaa}, + {0xf1, 0x42}, + {0xe5, 0x04}, + {0xc0, 0x80}, + {0xc1, 0x60}, + {0xc2, 0x0c}, + {0x35, 0x02}, /* turn on JPEG */ + {0xd9, 0x10}, + {0xc0, 0x50}, /* CIF HSize 640 */ + {0xc1, 0x3c}, /* CIF VSize 480 */ + {0x8c, 0x00}, /* CIF VSize LSB[2:0] */ + {0x8d, 0x1c}, /* output YUV */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xc2, 0x4c}, /* output YUV and Enable DCW */ + {0xc3, 0xf9}, /* enable PRE */ + {0x1c, 0x00}, /* indirect addressing */ + {0x1d, 0x48}, /* output YUV422 */ + {0x50, 0x89}, /* H/V divider=/2; plus DCW AVG */ + {0x51, 0xa0}, /* DCW input H=640/4 */ + {0x52, 0x78}, /* DCW input V=480/4 */ + {0x53, 0x00}, /* offset X=0 */ + {0x54, 0x00}, /* offset Y=0 */ + {0x55, 0x00}, /* H/V size[8]=0 */ + {0x57, 0x00}, /* H-size[9]=0 */ + {0x5c, 0x00}, /* DCW output size[9:8]=0 */ + {0x5a, 0x50}, /* DCW output H=320/4 */ + {0x5b, 0x3c}, /* DCW output V=240/4 */ + {0x1c, 0x0a}, + {0x1d, 0x0a}, + {0x94, 0x11}, +}; +static const u8 sensor_start_qvga_767x[][2] = { + {0x11, 0x01}, + {0x1e, 0x04}, + {0x19, 0x02}, + {0x1a, 0x7a}, +}; + +static const u8 bridge_init_772x[][2] = { + { 0xc2, 0x0c }, + { 0x88, 0xf8 }, + { 0xc3, 0x69 }, + { 0x89, 0xff }, + { 0x76, 0x03 }, + { 0x92, 0x01 }, + { 0x93, 0x18 }, + { 0x94, 0x10 }, + { 0x95, 0x10 }, + { 0xe2, 0x00 }, + { 0xe7, 0x3e }, + + { 0x96, 0x00 }, + + { 0x97, 0x20 }, + { 0x97, 0x20 }, + { 0x97, 0x20 }, + { 0x97, 0x0a }, + { 0x97, 0x3f }, + { 0x97, 0x4a }, + { 0x97, 0x20 }, + { 0x97, 0x15 }, + { 0x97, 0x0b }, + + { 0x8e, 0x40 }, + { 0x1f, 0x81 }, + { 0x34, 0x05 }, + { 0xe3, 0x04 }, + { 0x88, 0x00 }, + { 0x89, 0x00 }, + { 0x76, 0x00 }, + { 0xe7, 0x2e }, + { 0x31, 0xf9 }, + { 0x25, 0x42 }, + { 0x21, 0xf0 }, + + { 0x1c, 0x00 }, + { 0x1d, 0x40 }, + { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ + { 0x1d, 0x00 }, /* payload size */ + + { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ + { 0x1d, 0x58 }, /* frame size */ + { 0x1d, 0x00 }, /* frame size */ + + { 0x1c, 0x0a }, + { 0x1d, 0x08 }, /* turn on UVC header */ + { 0x1d, 0x0e }, /* .. */ + + { 0x8d, 0x1c }, + { 0x8e, 0x80 }, + { 0xe5, 0x04 }, + + { 0xc0, 0x50 }, + { 0xc1, 0x3c }, + { 0xc2, 0x0c }, +}; +static const u8 sensor_init_772x[][2] = { + { 0x12, 0x80 }, + { 0x11, 0x01 }, +/*fixme: better have a delay?*/ + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + + { 0x3d, 0x03 }, + { 0x17, 0x26 }, + { 0x18, 0xa0 }, + { 0x19, 0x07 }, + { 0x1a, 0xf0 }, + { 0x32, 0x00 }, + { 0x29, 0xa0 }, + { 0x2c, 0xf0 }, + { 0x65, 0x20 }, + { 0x11, 0x01 }, + { 0x42, 0x7f }, + { 0x63, 0xaa }, /* AWB - was e0 */ + { 0x64, 0xff }, + { 0x66, 0x00 }, + { 0x13, 0xf0 }, /* com8 */ + { 0x0d, 0x41 }, + { 0x0f, 0xc5 }, + { 0x14, 0x11 }, + + { 0x22, 0x7f }, + { 0x23, 0x03 }, + { 0x24, 0x40 }, + { 0x25, 0x30 }, + { 0x26, 0xa1 }, + { 0x2a, 0x00 }, + { 0x2b, 0x00 }, + { 0x6b, 0xaa }, + { 0x13, 0xff }, /* AWB */ + + { 0x90, 0x05 }, + { 0x91, 0x01 }, + { 0x92, 0x03 }, + { 0x93, 0x00 }, + { 0x94, 0x60 }, + { 0x95, 0x3c }, + { 0x96, 0x24 }, + { 0x97, 0x1e }, + { 0x98, 0x62 }, + { 0x99, 0x80 }, + { 0x9a, 0x1e }, + { 0x9b, 0x08 }, + { 0x9c, 0x20 }, + { 0x9e, 0x81 }, + + { 0xa6, 0x07 }, + { 0x7e, 0x0c }, + { 0x7f, 0x16 }, + { 0x80, 0x2a }, + { 0x81, 0x4e }, + { 0x82, 0x61 }, + { 0x83, 0x6f }, + { 0x84, 0x7b }, + { 0x85, 0x86 }, + { 0x86, 0x8e }, + { 0x87, 0x97 }, + { 0x88, 0xa4 }, + { 0x89, 0xaf }, + { 0x8a, 0xc5 }, + { 0x8b, 0xd7 }, + { 0x8c, 0xe8 }, + { 0x8d, 0x20 }, + + { 0x0c, 0x90 }, + + { 0x2b, 0x00 }, + { 0x22, 0x7f }, + { 0x23, 0x03 }, + { 0x11, 0x01 }, + { 0x0c, 0xd0 }, + { 0x64, 0xff }, + { 0x0d, 0x41 }, + + { 0x14, 0x41 }, + { 0x0e, 0xcd }, + { 0xac, 0xbf }, + { 0x8e, 0x00 }, /* De-noise threshold */ + { 0x0c, 0xd0 } +}; +static const u8 bridge_start_vga_772x[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x58}, + {0x1d, 0x00}, + {0xc0, 0x50}, + {0xc1, 0x3c}, +}; +static const u8 sensor_start_vga_772x[][2] = { + {0x12, 0x00}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, +}; +static const u8 bridge_start_qvga_772x[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0xc0, 0x28}, + {0xc1, 0x1e}, +}; +static const u8 sensor_start_qvga_772x[][2] = { + {0x12, 0x40}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, +}; + +static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + + PDEBUG(D_USBO, "SET 01 0000 %04x %02x", reg, val); + gspca_dev->usb_buf[0] = val; + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + if (ret < 0) { + pr_err("write failed %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return 0; + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x01, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("read failed %d\n", ret); + gspca_dev->usb_err = ret; + } + return gspca_dev->usb_buf[0]; +} + +/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. + * (direction and output)? */ +static void ov534_set_led(struct gspca_dev *gspca_dev, int status) +{ + u8 data; + + PDEBUG(D_CONF, "led status: %d", status); + + data = ov534_reg_read(gspca_dev, 0x21); + data |= 0x80; + ov534_reg_write(gspca_dev, 0x21, data); + + data = ov534_reg_read(gspca_dev, 0x23); + if (status) + data |= 0x80; + else + data &= ~0x80; + + ov534_reg_write(gspca_dev, 0x23, data); + + if (!status) { + data = ov534_reg_read(gspca_dev, 0x21); + data &= ~0x80; + ov534_reg_write(gspca_dev, 0x21, data); + } +} + +static int sccb_check_status(struct gspca_dev *gspca_dev) +{ + u8 data; + int i; + + for (i = 0; i < 5; i++) { + msleep(10); + data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); + + switch (data) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", + data, i + 1); + } + } + return 0; +} + +static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + PDEBUG(D_USBO, "sccb write: %02x %02x", reg, val); + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); + + if (!sccb_check_status(gspca_dev)) { + pr_err("sccb_reg_write failed\n"); + gspca_dev->usb_err = -EIO; + } +} + +static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); + if (!sccb_check_status(gspca_dev)) + pr_err("sccb_reg_read failed 1\n"); + + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); + if (!sccb_check_status(gspca_dev)) + pr_err("sccb_reg_read failed 2\n"); + + return ov534_reg_read(gspca_dev, OV534_REG_READ); +} + +/* output a bridge sequence (reg - val) */ +static void reg_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + ov534_reg_write(gspca_dev, (*data)[0], (*data)[1]); + data++; + } +} + +/* output a sensor sequence (reg - val) */ +static void sccb_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + if ((*data)[0] != 0xff) { + sccb_reg_write(gspca_dev, (*data)[0], (*data)[1]); + } else { + sccb_reg_read(gspca_dev, (*data)[1]); + sccb_reg_write(gspca_dev, 0xff, 0x00); + } + data++; + } +} + +/* ov772x specific controls */ +static void set_frame_rate(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + struct rate_s { + u8 fps; + u8 r11; + u8 r0d; + u8 re5; + }; + const struct rate_s *r; + static const struct rate_s rate_0[] = { /* 640x480 */ + {60, 0x01, 0xc1, 0x04}, + {50, 0x01, 0x41, 0x02}, + {40, 0x02, 0xc1, 0x04}, + {30, 0x04, 0x81, 0x02}, + {15, 0x03, 0x41, 0x04}, + }; + static const struct rate_s rate_1[] = { /* 320x240 */ + {125, 0x02, 0x81, 0x02}, + {100, 0x02, 0xc1, 0x04}, + {75, 0x03, 0xc1, 0x04}, + {60, 0x04, 0xc1, 0x04}, + {50, 0x02, 0x41, 0x04}, + {40, 0x03, 0x41, 0x04}, + {30, 0x04, 0x41, 0x04}, + }; + + if (sd->sensor != SENSOR_OV772x) + return; + if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) { + r = rate_0; + i = ARRAY_SIZE(rate_0); + } else { + r = rate_1; + i = ARRAY_SIZE(rate_1); + } + while (--i > 0) { + if (sd->frame_rate >= r->fps) + break; + r++; + } + + sccb_reg_write(gspca_dev, 0x11, r->r11); + sccb_reg_write(gspca_dev, 0x0d, r->r0d); + ov534_reg_write(gspca_dev, 0xe5, r->re5); + + PDEBUG(D_PROBE, "frame_rate: %d", r->fps); +} + +static void sethue(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV767x) { + /* TBD */ + } else { + s16 huesin; + s16 huecos; + + /* fixp_sin and fixp_cos accept only positive values, while + * our val is between -90 and 90 + */ + val += 360; + + /* According to the datasheet the registers expect HUESIN and + * HUECOS to be the result of the trigonometric functions, + * scaled by 0x80. + * + * The 0x100 here represents the maximun absolute value + * returned byt fixp_sin and fixp_cos, so the scaling will + * consider the result like in the interval [-1.0, 1.0]. + */ + huesin = fixp_sin(val) * 0x80 / 0x100; + huecos = fixp_cos(val) * 0x80 / 0x100; + + if (huesin < 0) { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) | 0x2); + huesin = -huesin; + } else { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) & ~0x2); + + } + sccb_reg_write(gspca_dev, 0xa9, (u8)huecos); + sccb_reg_write(gspca_dev, 0xaa, (u8)huesin); + } +} + +static void setsaturation(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV767x) { + int i; + static u8 color_tb[][6] = { + {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, + {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, + {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, + {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, + {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, + {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, + {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, + }; + + for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) + sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); + } else { + sccb_reg_write(gspca_dev, 0xa7, val); /* U saturation */ + sccb_reg_write(gspca_dev, 0xa8, val); /* V saturation */ + } +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV767x) { + if (val < 0) + val = 0x80 - val; + sccb_reg_write(gspca_dev, 0x55, val); /* bright */ + } else { + sccb_reg_write(gspca_dev, 0x9b, val); + } +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV767x) + sccb_reg_write(gspca_dev, 0x56, val); /* contras */ + else + sccb_reg_write(gspca_dev, 0x9c, val); +} + +static void setgain(struct gspca_dev *gspca_dev, s32 val) +{ + switch (val & 0x30) { + case 0x00: + val &= 0x0f; + break; + case 0x10: + val &= 0x0f; + val |= 0x30; + break; + case 0x20: + val &= 0x0f; + val |= 0x70; + break; + default: +/* case 0x30: */ + val &= 0x0f; + val |= 0xf0; + break; + } + sccb_reg_write(gspca_dev, 0x00, val); +} + +static s32 getgain(struct gspca_dev *gspca_dev) +{ + return sccb_reg_read(gspca_dev, 0x00); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV767x) { + + /* set only aec[9:2] */ + sccb_reg_write(gspca_dev, 0x10, val); /* aech */ + } else { + + /* 'val' is one byte and represents half of the exposure value + * we are going to set into registers, a two bytes value: + * + * MSB: ((u16) val << 1) >> 8 == val >> 7 + * LSB: ((u16) val << 1) & 0xff == val << 1 + */ + sccb_reg_write(gspca_dev, 0x08, val >> 7); + sccb_reg_write(gspca_dev, 0x10, val << 1); + } +} + +static s32 getexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV767x) { + /* get only aec[9:2] */ + return sccb_reg_read(gspca_dev, 0x10); /* aech */ + } else { + u8 hi = sccb_reg_read(gspca_dev, 0x08); + u8 lo = sccb_reg_read(gspca_dev, 0x10); + return (hi << 8 | lo) >> 1; + } +} + +static void setagc(struct gspca_dev *gspca_dev, s32 val) +{ + if (val) { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x04); + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) | 0x03); + } else { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x04); + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) & ~0x03); + } +} + +static void setawb(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (val) { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x02); + if (sd->sensor == SENSOR_OV772x) + sccb_reg_write(gspca_dev, 0x63, + sccb_reg_read(gspca_dev, 0x63) | 0xc0); + } else { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x02); + if (sd->sensor == SENSOR_OV772x) + sccb_reg_write(gspca_dev, 0x63, + sccb_reg_read(gspca_dev, 0x63) & ~0xc0); + } +} + +static void setaec(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data; + + data = sd->sensor == SENSOR_OV767x ? + 0x05 : /* agc + aec */ + 0x01; /* agc */ + switch (val) { + case V4L2_EXPOSURE_AUTO: + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | data); + break; + case V4L2_EXPOSURE_MANUAL: + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~data); + break; + } +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */ + sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */ +} + +static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + if (sd->sensor == SENSOR_OV767x) { + val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */ + val &= ~0x30; + if (hflip) + val |= 0x20; + if (vflip) + val |= 0x10; + sccb_reg_write(gspca_dev, 0x1e, val); + } else { + val = sccb_reg_read(gspca_dev, 0x0c); + val &= ~0xc0; + if (hflip == 0) + val |= 0x40; + if (vflip == 0) + val |= 0x80; + sccb_reg_write(gspca_dev, 0x0c, val); + } +} + +static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + val = val ? 0x9e : 0x00; + if (sd->sensor == SENSOR_OV767x) { + sccb_reg_write(gspca_dev, 0x2a, 0x00); + if (val) + val = 0x9d; /* insert dummy to 25fps for 50Hz */ + } + sccb_reg_write(gspca_dev, 0x2b, val); +} + + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + cam->cam_mode = ov772x_mode; + cam->nmodes = ARRAY_SIZE(ov772x_mode); + + sd->frame_rate = 30; + + return 0; +} + +static int ov534_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + gspca_dev->usb_err = 0; + if (ctrl->val && sd->gain && gspca_dev->streaming) + sd->gain->val = getgain(gspca_dev); + return gspca_dev->usb_err; + + case V4L2_CID_EXPOSURE_AUTO: + gspca_dev->usb_err = 0; + if (ctrl->val == V4L2_EXPOSURE_AUTO && sd->exposure && + gspca_dev->streaming) + sd->exposure->val = getexposure(gspca_dev); + return gspca_dev->usb_err; + } + return -EINVAL; +} + +static int ov534_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + gspca_dev->usb_err = 0; + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HUE: + sethue(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setsaturation(gspca_dev, ctrl->val); + break; + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + /* case V4L2_CID_GAIN: */ + setagc(gspca_dev, ctrl->val); + if (!gspca_dev->usb_err && !ctrl->val && sd->gain) + setgain(gspca_dev, sd->gain->val); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + setawb(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + /* case V4L2_CID_EXPOSURE: */ + setaec(gspca_dev, ctrl->val); + if (!gspca_dev->usb_err && ctrl->val == V4L2_EXPOSURE_MANUAL && + sd->exposure) + setexposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev, ctrl->val, sd->vflip->val); + break; + case V4L2_CID_VFLIP: + sethvflip(gspca_dev, sd->hflip->val, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops ov534_ctrl_ops = { + .g_volatile_ctrl = ov534_g_volatile_ctrl, + .s_ctrl = ov534_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + /* parameters with different values between the supported sensors */ + int saturation_min; + int saturation_max; + int saturation_def; + int brightness_min; + int brightness_max; + int brightness_def; + int contrast_max; + int contrast_def; + int exposure_min; + int exposure_max; + int exposure_def; + int hflip_def; + + if (sd->sensor == SENSOR_OV767x) { + saturation_min = 0, + saturation_max = 6, + saturation_def = 3, + brightness_min = -127; + brightness_max = 127; + brightness_def = 0; + contrast_max = 0x80; + contrast_def = 0x40; + exposure_min = 0x08; + exposure_max = 0x60; + exposure_def = 0x13; + hflip_def = 1; + } else { + saturation_min = 0, + saturation_max = 255, + saturation_def = 64, + brightness_min = 0; + brightness_max = 255; + brightness_def = 0; + contrast_max = 255; + contrast_def = 32; + exposure_min = 0; + exposure_max = 255; + exposure_def = 120; + hflip_def = 0; + } + + gspca_dev->vdev.ctrl_handler = hdl; + + v4l2_ctrl_handler_init(hdl, 13); + + if (sd->sensor == SENSOR_OV772x) + sd->hue = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_HUE, -90, 90, 1, 0); + + sd->saturation = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_SATURATION, saturation_min, saturation_max, 1, + saturation_def); + sd->brightness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_BRIGHTNESS, brightness_min, brightness_max, 1, + brightness_def); + sd->contrast = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_CONTRAST, 0, contrast_max, 1, contrast_def); + + if (sd->sensor == SENSOR_OV772x) { + sd->autogain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + sd->gain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_GAIN, 0, 63, 1, 20); + } + + sd->autoexposure = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + sd->exposure = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_EXPOSURE, exposure_min, exposure_max, 1, + exposure_def); + + sd->autowhitebalance = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + + if (sd->sensor == SENSOR_OV772x) + sd->sharpness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 63, 1, 0); + + sd->hflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, hflip_def); + sd->vflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + if (sd->sensor == SENSOR_OV772x) + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); + + v4l2_ctrl_auto_cluster(2, &sd->autoexposure, V4L2_EXPOSURE_MANUAL, + true); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 sensor_id; + static const struct reg_array bridge_init[NSENSORS] = { + [SENSOR_OV767x] = {bridge_init_767x, ARRAY_SIZE(bridge_init_767x)}, + [SENSOR_OV772x] = {bridge_init_772x, ARRAY_SIZE(bridge_init_772x)}, + }; + static const struct reg_array sensor_init[NSENSORS] = { + [SENSOR_OV767x] = {sensor_init_767x, ARRAY_SIZE(sensor_init_767x)}, + [SENSOR_OV772x] = {sensor_init_772x, ARRAY_SIZE(sensor_init_772x)}, + }; + + /* reset bridge */ + ov534_reg_write(gspca_dev, 0xe7, 0x3a); + ov534_reg_write(gspca_dev, 0xe0, 0x08); + msleep(100); + + /* initialize the sensor address */ + ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, 0x42); + + /* reset sensor */ + sccb_reg_write(gspca_dev, 0x12, 0x80); + msleep(10); + + /* probe the sensor */ + sccb_reg_read(gspca_dev, 0x0a); + sensor_id = sccb_reg_read(gspca_dev, 0x0a) << 8; + sccb_reg_read(gspca_dev, 0x0b); + sensor_id |= sccb_reg_read(gspca_dev, 0x0b); + PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); + + if ((sensor_id & 0xfff0) == 0x7670) { + sd->sensor = SENSOR_OV767x; + gspca_dev->cam.cam_mode = ov767x_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode); + } else { + sd->sensor = SENSOR_OV772x; + gspca_dev->cam.bulk = 1; + gspca_dev->cam.bulk_size = 16384; + gspca_dev->cam.bulk_nurbs = 2; + gspca_dev->cam.mode_framerates = ov772x_framerates; + } + + /* initialize */ + reg_w_array(gspca_dev, bridge_init[sd->sensor].val, + bridge_init[sd->sensor].len); + ov534_set_led(gspca_dev, 1); + sccb_w_array(gspca_dev, sensor_init[sd->sensor].val, + sensor_init[sd->sensor].len); + if (sd->sensor == SENSOR_OV767x) + sd_start(gspca_dev); + sd_stopN(gspca_dev); +/* set_frame_rate(gspca_dev); */ + + return gspca_dev->usb_err; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode; + static const struct reg_array bridge_start[NSENSORS][2] = { + [SENSOR_OV767x] = {{bridge_start_qvga_767x, + ARRAY_SIZE(bridge_start_qvga_767x)}, + {bridge_start_vga_767x, + ARRAY_SIZE(bridge_start_vga_767x)}}, + [SENSOR_OV772x] = {{bridge_start_qvga_772x, + ARRAY_SIZE(bridge_start_qvga_772x)}, + {bridge_start_vga_772x, + ARRAY_SIZE(bridge_start_vga_772x)}}, + }; + static const struct reg_array sensor_start[NSENSORS][2] = { + [SENSOR_OV767x] = {{sensor_start_qvga_767x, + ARRAY_SIZE(sensor_start_qvga_767x)}, + {sensor_start_vga_767x, + ARRAY_SIZE(sensor_start_vga_767x)}}, + [SENSOR_OV772x] = {{sensor_start_qvga_772x, + ARRAY_SIZE(sensor_start_qvga_772x)}, + {sensor_start_vga_772x, + ARRAY_SIZE(sensor_start_vga_772x)}}, + }; + + /* (from ms-win trace) */ + if (sd->sensor == SENSOR_OV767x) + sccb_reg_write(gspca_dev, 0x1e, 0x04); + /* black sun enable ? */ + + mode = gspca_dev->curr_mode; /* 0: 320x240, 1: 640x480 */ + reg_w_array(gspca_dev, bridge_start[sd->sensor][mode].val, + bridge_start[sd->sensor][mode].len); + sccb_w_array(gspca_dev, sensor_start[sd->sensor][mode].val, + sensor_start[sd->sensor][mode].len); + + set_frame_rate(gspca_dev); + + if (sd->hue) + sethue(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue)); + setsaturation(gspca_dev, v4l2_ctrl_g_ctrl(sd->saturation)); + if (sd->autogain) + setagc(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); + setawb(gspca_dev, v4l2_ctrl_g_ctrl(sd->autowhitebalance)); + setaec(gspca_dev, v4l2_ctrl_g_ctrl(sd->autoexposure)); + if (sd->gain) + setgain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); + setbrightness(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness)); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); + if (sd->sharpness) + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); + sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), + v4l2_ctrl_g_ctrl(sd->vflip)); + setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq)); + + ov534_set_led(gspca_dev, 1); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); +} + +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u32 this_pts; + u16 this_fid; + int remaining_len = len; + int payload_len; + + payload_len = gspca_dev->cam.bulk ? 2048 : 2040; + do { + len = min(remaining_len, payload_len); + + /* Payloads are prefixed with a UVC-style header. We + consider a frame to start when the FID toggles, or the PTS + changes. A frame ends when EOF is set, and we've received + the correct number of bytes. */ + + /* Verify UVC header. Header length is always 12 */ + if (data[0] != 12 || len < 12) { + PDEBUG(D_PACK, "bad header"); + goto discard; + } + + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + PDEBUG(D_PACK, "payload error"); + goto discard; + } + + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + PDEBUG(D_PACK, "PTS not present"); + goto discard; + } + this_pts = (data[5] << 24) | (data[4] << 16) + | (data[3] << 8) | data[2]; + this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; + + /* If PTS or FID has changed, start a new frame. */ + if (this_pts != sd->last_pts || this_fid != sd->last_fid) { + if (gspca_dev->last_packet_type == INTER_PACKET) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + sd->last_pts = this_pts; + sd->last_fid = this_fid; + gspca_frame_add(gspca_dev, FIRST_PACKET, + data + 12, len - 12); + /* If this packet is marked as EOF, end the frame */ + } else if (data[1] & UVC_STREAM_EOF) { + sd->last_pts = 0; + if (gspca_dev->pixfmt == V4L2_PIX_FMT_YUYV + && gspca_dev->image_len + len - 12 != + gspca_dev->width * gspca_dev->height * 2) { + PDEBUG(D_PACK, "wrong sized frame"); + goto discard; + } + gspca_frame_add(gspca_dev, LAST_PACKET, + data + 12, len - 12); + } else { + + /* Add the data from this payload */ + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 12, len - 12); + } + + /* Done this payload */ + goto scan_next; + +discard: + /* Discard data until a new frame starts. */ + gspca_dev->last_packet_type = DISCARD_PACKET; + +scan_next: + remaining_len -= len; + data += len; + } while (remaining_len > 0); +} + +/* get stream parameters (framerate) */ +static void sd_get_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct sd *sd = (struct sd *) gspca_dev; + + cp->capability |= V4L2_CAP_TIMEPERFRAME; + tpf->numerator = 1; + tpf->denominator = sd->frame_rate; +} + +/* set stream parameters (framerate) */ +static void sd_set_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct sd *sd = (struct sd *) gspca_dev; + + /* Set requested framerate */ + sd->frame_rate = tpf->denominator / tpf->numerator; + if (gspca_dev->streaming) + set_frame_rate(gspca_dev); + + /* Return the actual framerate */ + tpf->numerator = 1; + tpf->denominator = sd->frame_rate; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x1415, 0x2000)}, + {USB_DEVICE(0x06f8, 0x3002)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/ov534_9.c b/drivers/media/usb/gspca/ov534_9.c new file mode 100644 index 000000000000..c4cd028fe0b4 --- /dev/null +++ b/drivers/media/usb/gspca/ov534_9.c @@ -0,0 +1,1500 @@ +/* + * ov534-ov9xxx gspca driver + * + * Copyright (C) 2009-2011 Jean-Francois Moine http://moinejf.free.fr + * Copyright (C) 2008 Antonio Ospite + * Copyright (C) 2008 Jim Paris + * + * Based on a prototype written by Mark Ferrell + * USB protocol reverse engineered by Jim Paris + * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "ov534_9" + +#include "gspca.h" + +#define OV534_REG_ADDRESS 0xf1 /* sensor address */ +#define OV534_REG_SUBADDR 0xf2 +#define OV534_REG_WRITE 0xf3 +#define OV534_REG_READ 0xf4 +#define OV534_REG_OPERATION 0xf5 +#define OV534_REG_STATUS 0xf6 + +#define OV534_OP_WRITE_3 0x37 +#define OV534_OP_WRITE_2 0x33 +#define OV534_OP_READ_2 0xf9 + +#define CTRL_TIMEOUT 500 + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + __u32 last_pts; + u8 last_fid; + + u8 sensor; +}; +enum sensors { + SENSOR_OV965x, /* ov9657 */ + SENSOR_OV971x, /* ov9712 */ + SENSOR_OV562x, /* ov5621 */ + NSENSORS +}; + +static const struct v4l2_pix_format ov965x_mode[] = { +#define QVGA_MODE 0 + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define VGA_MODE 1 + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define SVGA_MODE 2 + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define XGA_MODE 3 + {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define SXGA_MODE 4 + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static const struct v4l2_pix_format ov971x_mode[] = { + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB + } +}; + +static const struct v4l2_pix_format ov562x_mode[] = { + {2592, 1680, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 2592, + .sizeimage = 2592 * 1680, + .colorspace = V4L2_COLORSPACE_SRGB + } +}; + +static const u8 bridge_init[][2] = { + {0x88, 0xf8}, + {0x89, 0xff}, + {0x76, 0x03}, + {0x92, 0x03}, + {0x95, 0x10}, + {0xe2, 0x00}, + {0xe7, 0x3e}, + {0x8d, 0x1c}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x1f, 0x00}, + {0xc3, 0xf9}, + {0x89, 0xff}, + {0x88, 0xf8}, + {0x76, 0x03}, + {0x92, 0x01}, + {0x93, 0x18}, + {0x1c, 0x0a}, + {0x1d, 0x48}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0x34, 0x05}, + {0xe7, 0x2e}, + {0x31, 0xf9}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x25, 0x42}, + {0x94, 0x11}, +}; + +static const u8 ov965x_init[][2] = { + {0x12, 0x80}, /* com7 - SSCB reset */ + {0x00, 0x00}, /* gain */ + {0x01, 0x80}, /* blue */ + {0x02, 0x80}, /* red */ + {0x03, 0x1b}, /* vref */ + {0x04, 0x03}, /* com1 - exposure low bits */ + {0x0b, 0x57}, /* ver */ + {0x0e, 0x61}, /* com5 */ + {0x0f, 0x42}, /* com6 */ + {0x11, 0x00}, /* clkrc */ + {0x12, 0x02}, /* com7 - 15fps VGA YUYV */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x14, 0x28}, /* com9 */ + {0x16, 0x24}, /* reg16 */ + {0x17, 0x1d}, /* hstart*/ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop*/ + {0x1e, 0x04}, /* mvfp */ + {0x24, 0x3c}, /* aew */ + {0x25, 0x36}, /* aeb */ + {0x26, 0x71}, /* vpt */ + {0x27, 0x08}, /* bbias */ + {0x28, 0x08}, /* gbbias */ + {0x29, 0x15}, /* gr com */ + {0x2a, 0x00}, /* exhch */ + {0x2b, 0x00}, /* exhcl */ + {0x2c, 0x08}, /* rbias */ + {0x32, 0xff}, /* href */ + {0x33, 0x00}, /* chlf */ + {0x34, 0x3f}, /* aref1 */ + {0x35, 0x00}, /* aref2 */ + {0x36, 0xf8}, /* aref3 */ + {0x38, 0x72}, /* adc2 */ + {0x39, 0x57}, /* aref4 */ + {0x3a, 0x80}, /* tslb - yuyv */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x3d, 0x99}, /* com13 */ + {0x3f, 0xc1}, /* edge */ + {0x40, 0xc0}, /* com15 */ + {0x41, 0x40}, /* com16 */ + {0x42, 0xc0}, /* com17 */ + {0x43, 0x0a}, /* rsvd */ + {0x44, 0xf0}, + {0x45, 0x46}, + {0x46, 0x62}, + {0x47, 0x2a}, + {0x48, 0x3c}, + {0x4a, 0xfc}, + {0x4b, 0xfc}, + {0x4c, 0x7f}, + {0x4d, 0x7f}, + {0x4e, 0x7f}, + {0x4f, 0x98}, /* matrix */ + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, /* matrix coef sign */ + {0x59, 0x85}, /* AWB control */ + {0x5a, 0xa9}, + {0x5b, 0x64}, + {0x5c, 0x84}, + {0x5d, 0x53}, + {0x5e, 0x0e}, + {0x5f, 0xf0}, /* AWB blue limit */ + {0x60, 0xf0}, /* AWB red limit */ + {0x61, 0xf0}, /* AWB green limit */ + {0x62, 0x00}, /* lcc1 */ + {0x63, 0x00}, /* lcc2 */ + {0x64, 0x02}, /* lcc3 */ + {0x65, 0x16}, /* lcc4 */ + {0x66, 0x01}, /* lcc5 */ + {0x69, 0x02}, /* hv */ + {0x6b, 0x5a}, /* dbvl */ + {0x6c, 0x04}, + {0x6d, 0x55}, + {0x6e, 0x00}, + {0x6f, 0x9d}, + {0x70, 0x21}, /* dnsth */ + {0x71, 0x78}, + {0x72, 0x00}, /* poidx */ + {0x73, 0x01}, /* pckdv */ + {0x74, 0x3a}, /* xindx */ + {0x75, 0x35}, /* yindx */ + {0x76, 0x01}, + {0x77, 0x02}, + {0x7a, 0x12}, /* gamma curve */ + {0x7b, 0x08}, + {0x7c, 0x16}, + {0x7d, 0x30}, + {0x7e, 0x5e}, + {0x7f, 0x72}, + {0x80, 0x82}, + {0x81, 0x8e}, + {0x82, 0x9a}, + {0x83, 0xa4}, + {0x84, 0xac}, + {0x85, 0xb8}, + {0x86, 0xc3}, + {0x87, 0xd6}, + {0x88, 0xe6}, + {0x89, 0xf2}, + {0x8a, 0x03}, + {0x8c, 0x89}, /* com19 */ + {0x14, 0x28}, /* com9 */ + {0x90, 0x7d}, + {0x91, 0x7b}, + {0x9d, 0x03}, /* lcc6 */ + {0x9e, 0x04}, /* lcc7 */ + {0x9f, 0x7a}, + {0xa0, 0x79}, + {0xa1, 0x40}, /* aechm */ + {0xa4, 0x50}, /* com21 */ + {0xa5, 0x68}, /* com26 */ + {0xa6, 0x4a}, /* AWB green */ + {0xa8, 0xc1}, /* refa8 */ + {0xa9, 0xef}, /* refa9 */ + {0xaa, 0x92}, + {0xab, 0x04}, + {0xac, 0x80}, /* black level control */ + {0xad, 0x80}, + {0xae, 0x80}, + {0xaf, 0x80}, + {0xb2, 0xf2}, + {0xb3, 0x20}, + {0xb4, 0x20}, /* ctrlb4 */ + {0xb5, 0x00}, + {0xb6, 0xaf}, + {0xbb, 0xae}, + {0xbc, 0x7f}, /* ADC channel offsets */ + {0xdb, 0x7f}, + {0xbe, 0x7f}, + {0xbf, 0x7f}, + {0xc0, 0xe2}, + {0xc1, 0xc0}, + {0xc2, 0x01}, + {0xc3, 0x4e}, + {0xc6, 0x85}, + {0xc7, 0x80}, /* com24 */ + {0xc9, 0xe0}, + {0xca, 0xe8}, + {0xcb, 0xf0}, + {0xcc, 0xd8}, + {0xcd, 0xf1}, + {0x4f, 0x98}, /* matrix */ + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0xff, 0x41}, /* read 41, write ff 00 */ + {0x41, 0x40}, /* com16 */ + + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + + {0x12, 0x62}, /* com7 - 30fps VGA YUV */ + {0x36, 0xfa}, /* aref3 */ + {0x69, 0x0a}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x00}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, + {0x03, 0x12}, /* vref */ + {0x17, 0x16}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x3d}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xaa}, +}; + +static const u8 bridge_init_2[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x01}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x3c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0xa0}, + {0x5b, 0x78}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 ov965x_init_2[][2] = { + {0x3b, 0xc4}, + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, /* gain */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x03}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x05}, + {0xc5, 0x07}, + {0xa2, 0x4b}, + {0xa3, 0x3e}, + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc0}, /* com17 */ + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, /* com17 */ +/* sharpness */ + {0x3f, 0x01}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, /* com17 */ +/* saturation */ + {0x4f, 0x98}, /* matrix */ + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0xff, 0x41}, /* read 41, write ff 00 */ + {0x41, 0x40}, /* com16 */ +/* contrast */ + {0x56, 0x40}, +/* brightness */ + {0x55, 0x8f}, +/* expo */ + {0x10, 0x25}, /* aech - exposure high bits */ + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ +}; + +static const u8 ov971x_init[][2] = { + {0x12, 0x80}, + {0x09, 0x10}, + {0x1e, 0x07}, + {0x5f, 0x18}, + {0x69, 0x04}, + {0x65, 0x2a}, + {0x68, 0x0a}, + {0x39, 0x28}, + {0x4d, 0x90}, + {0xc1, 0x80}, + {0x0c, 0x30}, + {0x6d, 0x02}, + {0x96, 0xf1}, + {0xbc, 0x68}, + {0x12, 0x00}, + {0x3b, 0x00}, + {0x97, 0x80}, + {0x17, 0x25}, + {0x18, 0xa2}, + {0x19, 0x01}, + {0x1a, 0xca}, + {0x03, 0x0a}, + {0x32, 0x07}, + {0x98, 0x40}, /*{0x98, 0x00},*/ + {0x99, 0xA0}, /*{0x99, 0x00},*/ + {0x9a, 0x01}, /*{0x9a, 0x00},*/ + {0x57, 0x00}, + {0x58, 0x78}, /*{0x58, 0xc8},*/ + {0x59, 0x50}, /*{0x59, 0xa0},*/ + {0x4c, 0x13}, + {0x4b, 0x36}, + {0x3d, 0x3c}, + {0x3e, 0x03}, + {0xbd, 0x50}, /*{0xbd, 0xa0},*/ + {0xbe, 0x78}, /*{0xbe, 0xc8},*/ + {0x4e, 0x55}, + {0x4f, 0x55}, + {0x50, 0x55}, + {0x51, 0x55}, + {0x24, 0x55}, + {0x25, 0x40}, + {0x26, 0xa1}, + {0x5c, 0x59}, + {0x5d, 0x00}, + {0x11, 0x00}, + {0x2a, 0x98}, + {0x2b, 0x06}, + {0x2d, 0x00}, + {0x2e, 0x00}, + {0x13, 0xa5}, + {0x14, 0x40}, + {0x4a, 0x00}, + {0x49, 0xce}, + {0x22, 0x03}, + {0x09, 0x00} +}; + +static const u8 ov965x_start_1_vga[][2] = { /* same for qvga */ + {0x12, 0x62}, /* com7 - 30fps VGA YUV */ + {0x36, 0xfa}, /* aref3 */ + {0x69, 0x0a}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x00}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x12}, /* vref */ + {0x17, 0x16}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x3d}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xaa}, +}; + +static const u8 ov965x_start_1_svga[][2] = { + {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x0d}, /* com22 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 ov965x_start_1_xga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 ov965x_start_1_sxga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 bridge_start_qvga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x78}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0x50}, + {0x5b, 0x3c}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_vga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x01}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x3c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0xa0}, + {0x5b, 0x78}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_svga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0x50, 0x00}, + {0x51, 0x40}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x88}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0xc8}, + {0x5b, 0x96}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0xda, 0x00}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_xga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0x50, 0x00}, + {0x51, 0x40}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x88}, + {0x57, 0x00}, + {0x5c, 0x01}, + {0x5a, 0x00}, + {0x5b, 0xc0}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0xda, 0x01}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_sxga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 ov965x_start_2_qvga[][2] = { + {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0xa2, 0x96}, /* bd50 */ + {0xa3, 0x7d}, /* bd60 */ + + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, + {0x3a, 0x80}, /* tslb - yuyv */ +}; + +static const u8 ov965x_start_2_vga[][2] = { + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x03}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x05}, /* 50 Hz banding filter */ + {0xc5, 0x07}, /* 60 Hz banding filter */ + {0xa2, 0x4b}, /* bd50 */ + {0xa3, 0x3e}, /* bd60 */ + + {0x2d, 0x00}, /* advfl */ +}; + +static const u8 ov965x_start_2_svga[][2] = { /* same for xga */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ +}; + +static const u8 ov965x_start_2_sxga[][2] = { + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ +}; + +static const u8 ov562x_init[][2] = { + {0x88, 0x20}, + {0x89, 0x0a}, + {0x8a, 0x90}, + {0x8b, 0x06}, + {0x8c, 0x01}, + {0x8d, 0x10}, + {0x1c, 0x00}, + {0x1d, 0x48}, + {0x1d, 0x00}, + {0x1d, 0xff}, + {0x1c, 0x0a}, + {0x1d, 0x2e}, + {0x1d, 0x1e}, +}; + +static const u8 ov562x_init_2[][2] = { + {0x12, 0x80}, + {0x11, 0x41}, + {0x13, 0x00}, + {0x10, 0x1e}, + {0x3b, 0x07}, + {0x5b, 0x40}, + {0x39, 0x07}, + {0x53, 0x02}, + {0x54, 0x60}, + {0x04, 0x20}, + {0x27, 0x04}, + {0x3d, 0x40}, + {0x36, 0x00}, + {0xc5, 0x04}, + {0x4e, 0x00}, + {0x4f, 0x93}, + {0x50, 0x7b}, + {0xca, 0x0c}, + {0xcb, 0x0f}, + {0x39, 0x07}, + {0x4a, 0x10}, + {0x3e, 0x0a}, + {0x3d, 0x00}, + {0x0c, 0x38}, + {0x38, 0x90}, + {0x46, 0x30}, + {0x4f, 0x93}, + {0x50, 0x7b}, + {0xab, 0x00}, + {0xca, 0x0c}, + {0xcb, 0x0f}, + {0x37, 0x02}, + {0x44, 0x48}, + {0x8d, 0x44}, + {0x2a, 0x00}, + {0x2b, 0x00}, + {0x32, 0x00}, + {0x38, 0x90}, + {0x53, 0x02}, + {0x54, 0x60}, + {0x12, 0x00}, + {0x17, 0x12}, + {0x18, 0xb4}, + {0x19, 0x0c}, + {0x1a, 0xf4}, + {0x03, 0x4a}, + {0x89, 0x20}, + {0x83, 0x80}, + {0xb7, 0x9d}, + {0xb6, 0x11}, + {0xb5, 0x55}, + {0xb4, 0x00}, + {0xa9, 0xf0}, + {0xa8, 0x0a}, + {0xb8, 0xf0}, + {0xb9, 0xf0}, + {0xba, 0xf0}, + {0x81, 0x07}, + {0x63, 0x44}, + {0x13, 0xc7}, + {0x14, 0x60}, + {0x33, 0x75}, + {0x2c, 0x00}, + {0x09, 0x00}, + {0x35, 0x30}, + {0x27, 0x04}, + {0x3c, 0x07}, + {0x3a, 0x0a}, + {0x3b, 0x07}, + {0x01, 0x40}, + {0x02, 0x40}, + {0x16, 0x40}, + {0x52, 0xb0}, + {0x51, 0x83}, + {0x21, 0xbb}, + {0x22, 0x10}, + {0x23, 0x03}, + {0x35, 0x38}, + {0x20, 0x90}, + {0x28, 0x30}, + {0x73, 0xe1}, + {0x6c, 0x00}, + {0x6d, 0x80}, + {0x6e, 0x00}, + {0x70, 0x04}, + {0x71, 0x00}, + {0x8d, 0x04}, + {0x64, 0x00}, + {0x65, 0x00}, + {0x66, 0x00}, + {0x67, 0x00}, + {0x68, 0x00}, + {0x69, 0x00}, + {0x6a, 0x00}, + {0x6b, 0x00}, + {0x71, 0x94}, + {0x74, 0x20}, + {0x80, 0x09}, + {0x85, 0xc0}, +}; + +static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + gspca_dev->usb_buf[0] = val; + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + if (ret < 0) { + pr_err("reg_w failed %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + PDEBUG(D_USBO, "reg_w [%04x] = %02x", reg, val); + reg_w_i(gspca_dev, reg, val); +} + +static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return 0; + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x01, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } + return gspca_dev->usb_buf[0]; +} + +static int sccb_check_status(struct gspca_dev *gspca_dev) +{ + u8 data; + int i; + + for (i = 0; i < 5; i++) { + msleep(10); + data = reg_r(gspca_dev, OV534_REG_STATUS); + + switch (data) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + PDEBUG(D_USBI|D_USBO, + "sccb status 0x%02x, attempt %d/5", + data, i + 1); + } + } + return 0; +} + +static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + PDEBUG(D_USBO, "sccb_write [%02x] = %02x", reg, val); + reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg); + reg_w_i(gspca_dev, OV534_REG_WRITE, val); + reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); + + if (!sccb_check_status(gspca_dev)) + pr_err("sccb_write failed\n"); +} + +static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg) +{ + reg_w(gspca_dev, OV534_REG_SUBADDR, reg); + reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); + if (!sccb_check_status(gspca_dev)) + pr_err("sccb_read failed 1\n"); + + reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); + if (!sccb_check_status(gspca_dev)) + pr_err("sccb_read failed 2\n"); + + return reg_r(gspca_dev, OV534_REG_READ); +} + +/* output a bridge sequence (reg - val) */ +static void reg_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + reg_w(gspca_dev, (*data)[0], (*data)[1]); + data++; + } +} + +/* output a sensor sequence (reg - val) */ +static void sccb_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + if ((*data)[0] != 0xff) { + sccb_write(gspca_dev, (*data)[0], (*data)[1]); + } else { + sccb_read(gspca_dev, (*data)[1]); + sccb_write(gspca_dev, 0xff, 0x00); + } + data++; + } +} + +/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. + * (direction and output)? */ +static void set_led(struct gspca_dev *gspca_dev, int status) +{ + u8 data; + + PDEBUG(D_CONF, "led status: %d", status); + + data = reg_r(gspca_dev, 0x21); + data |= 0x80; + reg_w(gspca_dev, 0x21, data); + + data = reg_r(gspca_dev, 0x23); + if (status) + data |= 0x80; + else + data &= ~0x80; + + reg_w(gspca_dev, 0x23, data); + + if (!status) { + data = reg_r(gspca_dev, 0x21); + data &= ~0x80; + reg_w(gspca_dev, 0x21, data); + } +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + s8 sval; + + if (sd->sensor == SENSOR_OV562x) { + sval = brightness; + val = 0x76; + val += sval; + sccb_write(gspca_dev, 0x24, val); + val = 0x6a; + val += sval; + sccb_write(gspca_dev, 0x25, val); + if (sval < -40) + val = 0x71; + else if (sval < 20) + val = 0x94; + else + val = 0xe6; + sccb_write(gspca_dev, 0x26, val); + } else { + val = brightness; + if (val < 8) + val = 15 - val; /* f .. 8 */ + else + val = val - 8; /* 0 .. 7 */ + sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ + 0x0f | (val << 4)); + } +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */ + val << 4); +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 autogain) +{ + u8 val; + +/*fixme: should adjust agc/awb/aec by different controls */ + val = sccb_read(gspca_dev, 0x13); /* com8 */ + sccb_write(gspca_dev, 0xff, 0x00); + if (autogain) + val |= 0x05; /* agc & aec */ + else + val &= 0xfa; + sccb_write(gspca_dev, 0x13, val); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 exposure) +{ + static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; + u8 val; + + sccb_write(gspca_dev, 0x10, expo[exposure]); /* aec[9:2] */ + + val = sccb_read(gspca_dev, 0x13); /* com8 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x13, val); + + val = sccb_read(gspca_dev, 0xa1); /* aech */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */ +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + if (val < 0) { /* auto */ + val = sccb_read(gspca_dev, 0x42); /* com17 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x42, val | 0x40); + /* Edge enhancement strength auto adjust */ + return; + } + if (val != 0) + val = 1 << (val - 1); + sccb_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */ + val); + val = sccb_read(gspca_dev, 0x42); /* com17 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x42, val & 0xbf); +} + +static void setsatur(struct gspca_dev *gspca_dev, s32 val) +{ + u8 val1, val2, val3; + static const u8 matrix[5][2] = { + {0x14, 0x38}, + {0x1e, 0x54}, + {0x28, 0x70}, + {0x32, 0x8c}, + {0x48, 0x90} + }; + + val1 = matrix[val][0]; + val2 = matrix[val][1]; + val3 = val1 + val2; + sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */ + sccb_write(gspca_dev, 0x50, val3); + sccb_write(gspca_dev, 0x51, 0x00); + sccb_write(gspca_dev, 0x52, val1); + sccb_write(gspca_dev, 0x53, val2); + sccb_write(gspca_dev, 0x54, val3); + sccb_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */ + + val1 = sccb_read(gspca_dev, 0x41); /* com16 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x41, val1); +} + +static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq) +{ + u8 val; + + val = sccb_read(gspca_dev, 0x13); /* com8 */ + sccb_write(gspca_dev, 0xff, 0x00); + if (freq == 0) { + sccb_write(gspca_dev, 0x13, val & 0xdf); + return; + } + sccb_write(gspca_dev, 0x13, val | 0x20); + + val = sccb_read(gspca_dev, 0x42); /* com17 */ + sccb_write(gspca_dev, 0xff, 0x00); + if (freq == 1) + val |= 0x01; + else + val &= 0xfe; + sccb_write(gspca_dev, 0x42, val); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 sensor_id; + + /* reset bridge */ + reg_w(gspca_dev, 0xe7, 0x3a); + reg_w(gspca_dev, 0xe0, 0x08); + msleep(100); + + /* initialize the sensor address */ + reg_w(gspca_dev, OV534_REG_ADDRESS, 0x60); + + /* reset sensor */ + sccb_write(gspca_dev, 0x12, 0x80); + msleep(10); + + /* probe the sensor */ + sccb_read(gspca_dev, 0x0a); + sensor_id = sccb_read(gspca_dev, 0x0a) << 8; + sccb_read(gspca_dev, 0x0b); + sensor_id |= sccb_read(gspca_dev, 0x0b); + PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); + + /* initialize */ + if ((sensor_id & 0xfff0) == 0x9650) { + sd->sensor = SENSOR_OV965x; + + gspca_dev->cam.cam_mode = ov965x_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode); + + reg_w_array(gspca_dev, bridge_init, + ARRAY_SIZE(bridge_init)); + sccb_w_array(gspca_dev, ov965x_init, + ARRAY_SIZE(ov965x_init)); + reg_w_array(gspca_dev, bridge_init_2, + ARRAY_SIZE(bridge_init_2)); + sccb_w_array(gspca_dev, ov965x_init_2, + ARRAY_SIZE(ov965x_init_2)); + reg_w(gspca_dev, 0xe0, 0x00); + reg_w(gspca_dev, 0xe0, 0x01); + set_led(gspca_dev, 0); + reg_w(gspca_dev, 0xe0, 0x00); + } else if ((sensor_id & 0xfff0) == 0x9710) { + const char *p; + int l; + + sd->sensor = SENSOR_OV971x; + + gspca_dev->cam.cam_mode = ov971x_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode); + + gspca_dev->cam.bulk = 1; + gspca_dev->cam.bulk_size = 16384; + gspca_dev->cam.bulk_nurbs = 2; + + sccb_w_array(gspca_dev, ov971x_init, + ARRAY_SIZE(ov971x_init)); + + /* set video format on bridge processor */ + /* access bridge processor's video format registers at: 0x00 */ + reg_w(gspca_dev, 0x1c, 0x00); + /*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/ + reg_w(gspca_dev, 0x1d, 0x00); + + /* Will W. specific stuff + * set VSYNC to + * output (0x1f) if first webcam + * input (0x17) if 2nd or 3rd webcam */ + p = video_device_node_name(&gspca_dev->vdev); + l = strlen(p) - 1; + if (p[l] == '0') + reg_w(gspca_dev, 0x56, 0x1f); + else + reg_w(gspca_dev, 0x56, 0x17); + } else if ((sensor_id & 0xfff0) == 0x5620) { + sd->sensor = SENSOR_OV562x; + gspca_dev->cam.cam_mode = ov562x_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode); + + reg_w_array(gspca_dev, ov562x_init, + ARRAY_SIZE(ov562x_init)); + sccb_w_array(gspca_dev, ov562x_init_2, + ARRAY_SIZE(ov562x_init_2)); + reg_w(gspca_dev, 0xe0, 0x00); + } else { + pr_err("Unknown sensor %04x", sensor_id); + return -EINVAL; + } + + return gspca_dev->usb_err; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV971x) + return gspca_dev->usb_err; + if (sd->sensor == SENSOR_OV562x) + return gspca_dev->usb_err; + + switch (gspca_dev->curr_mode) { + case QVGA_MODE: /* 320x240 */ + sccb_w_array(gspca_dev, ov965x_start_1_vga, + ARRAY_SIZE(ov965x_start_1_vga)); + reg_w_array(gspca_dev, bridge_start_qvga, + ARRAY_SIZE(bridge_start_qvga)); + sccb_w_array(gspca_dev, ov965x_start_2_qvga, + ARRAY_SIZE(ov965x_start_2_qvga)); + break; + case VGA_MODE: /* 640x480 */ + sccb_w_array(gspca_dev, ov965x_start_1_vga, + ARRAY_SIZE(ov965x_start_1_vga)); + reg_w_array(gspca_dev, bridge_start_vga, + ARRAY_SIZE(bridge_start_vga)); + sccb_w_array(gspca_dev, ov965x_start_2_vga, + ARRAY_SIZE(ov965x_start_2_vga)); + break; + case SVGA_MODE: /* 800x600 */ + sccb_w_array(gspca_dev, ov965x_start_1_svga, + ARRAY_SIZE(ov965x_start_1_svga)); + reg_w_array(gspca_dev, bridge_start_svga, + ARRAY_SIZE(bridge_start_svga)); + sccb_w_array(gspca_dev, ov965x_start_2_svga, + ARRAY_SIZE(ov965x_start_2_svga)); + break; + case XGA_MODE: /* 1024x768 */ + sccb_w_array(gspca_dev, ov965x_start_1_xga, + ARRAY_SIZE(ov965x_start_1_xga)); + reg_w_array(gspca_dev, bridge_start_xga, + ARRAY_SIZE(bridge_start_xga)); + sccb_w_array(gspca_dev, ov965x_start_2_svga, + ARRAY_SIZE(ov965x_start_2_svga)); + break; + default: +/* case SXGA_MODE: * 1280x1024 */ + sccb_w_array(gspca_dev, ov965x_start_1_sxga, + ARRAY_SIZE(ov965x_start_1_sxga)); + reg_w_array(gspca_dev, bridge_start_sxga, + ARRAY_SIZE(bridge_start_sxga)); + sccb_w_array(gspca_dev, ov965x_start_2_sxga, + ARRAY_SIZE(ov965x_start_2_sxga)); + break; + } + + reg_w(gspca_dev, 0xe0, 0x00); + reg_w(gspca_dev, 0xe0, 0x00); + set_led(gspca_dev, 1); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0xe0, 0x01); + set_led(gspca_dev, 0); + reg_w(gspca_dev, 0xe0, 0x00); +} + +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u32 this_pts; + u8 this_fid; + int remaining_len = len; + int payload_len; + + payload_len = gspca_dev->cam.bulk ? 2048 : 2040; + do { + len = min(remaining_len, payload_len); + + /* Payloads are prefixed with a UVC-style header. We + consider a frame to start when the FID toggles, or the PTS + changes. A frame ends when EOF is set, and we've received + the correct number of bytes. */ + + /* Verify UVC header. Header length is always 12 */ + if (data[0] != 12 || len < 12) { + PDEBUG(D_PACK, "bad header"); + goto discard; + } + + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + PDEBUG(D_PACK, "payload error"); + goto discard; + } + + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + PDEBUG(D_PACK, "PTS not present"); + goto discard; + } + this_pts = (data[5] << 24) | (data[4] << 16) + | (data[3] << 8) | data[2]; + this_fid = data[1] & UVC_STREAM_FID; + + /* If PTS or FID has changed, start a new frame. */ + if (this_pts != sd->last_pts || this_fid != sd->last_fid) { + if (gspca_dev->last_packet_type == INTER_PACKET) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + sd->last_pts = this_pts; + sd->last_fid = this_fid; + gspca_frame_add(gspca_dev, FIRST_PACKET, + data + 12, len - 12); + /* If this packet is marked as EOF, end the frame */ + } else if (data[1] & UVC_STREAM_EOF) { + sd->last_pts = 0; + gspca_frame_add(gspca_dev, LAST_PACKET, + data + 12, len - 12); + } else { + + /* Add the data from this payload */ + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 12, len - 12); + } + + /* Done this payload */ + goto scan_next; + +discard: + /* Discard data until a new frame starts. */ + gspca_dev->last_packet_type = DISCARD_PACKET; + +scan_next: + remaining_len -= len; + data += len; + } while (remaining_len > 0); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setsatur(gspca_dev, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (ctrl->is_new) + setautogain(gspca_dev, ctrl->val); + if (!ctrl->val && gspca_dev->exposure->is_new) + setexposure(gspca_dev, gspca_dev->exposure->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + if (sd->sensor == SENSOR_OV971x) + return 0; + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 7); + if (sd->sensor == SENSOR_OV562x) { + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, -90, 90, 1, 0); + } else { + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 15, 1, 7); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 3); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 4, 1, 2); + /* -1 = auto */ + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, -1, 4, 1, -1); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 3, 1, 0); + v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + } + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x05a9, 0x8065)}, + {USB_DEVICE(0x06f8, 0x3003)}, + {USB_DEVICE(0x05a9, 0x1550)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c new file mode 100644 index 000000000000..d236d1791f78 --- /dev/null +++ b/drivers/media/usb/gspca/pac207.c @@ -0,0 +1,469 @@ +/* + * Pixart PAC207BCA library + * + * Copyright (C) 2008 Hans de Goede + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "pac207" + +#include +#include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Pixart PAC207"); +MODULE_LICENSE("GPL"); + +#define PAC207_CTRL_TIMEOUT 100 /* ms */ + +#define PAC207_BRIGHTNESS_MIN 0 +#define PAC207_BRIGHTNESS_MAX 255 +#define PAC207_BRIGHTNESS_DEFAULT 46 +#define PAC207_BRIGHTNESS_REG 0x08 + +#define PAC207_EXPOSURE_MIN 3 +#define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */ +#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ +#define PAC207_EXPOSURE_REG 0x02 + +#define PAC207_GAIN_MIN 0 +#define PAC207_GAIN_MAX 31 +#define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */ +#define PAC207_GAIN_REG 0x0e + +#define PAC207_AUTOGAIN_DEADZONE 30 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct v4l2_ctrl *brightness; + + u8 mode; + u8 sof_read; + u8 header_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; +}; + +static const struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = (176 + 2) * 144, + /* uncompressed, add 2 bytes / line for line header */ + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, + .bytesperline = 352, + /* compressed, but only when needed (not compressed + when the framerate is low) */ + .sizeimage = (352 + 2) * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +static const __u8 pac207_sensor_init[][8] = { + {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84}, + {0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, + {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00}, + {0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00}, +}; + +static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, + const u8 *buffer, u16 length) +{ + struct usb_device *udev = gspca_dev->dev; + int err; + + if (gspca_dev->usb_err < 0) + return; + + memcpy(gspca_dev->usb_buf, buffer, length); + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, index, + gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); + if (err < 0) { + pr_err("Failed to write registers to index 0x%04X, error %d\n", + index, err); + gspca_dev->usb_err = err; + } +} + +static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) +{ + struct usb_device *udev = gspca_dev->dev; + int err; + + if (gspca_dev->usb_err < 0) + return; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, NULL, 0, PAC207_CTRL_TIMEOUT); + if (err) { + pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", + index, value, err); + gspca_dev->usb_err = err; + } +} + +static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) +{ + struct usb_device *udev = gspca_dev->dev; + int res; + + if (gspca_dev->usb_err < 0) + return 0; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, index, + gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT); + if (res < 0) { + pr_err("Failed to read a register (index 0x%04X, error %d)\n", + index, res); + gspca_dev->usb_err = res; + return 0; + } + + return gspca_dev->usb_buf[0]; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + u8 idreg[2]; + + idreg[0] = pac207_read_reg(gspca_dev, 0x0000); + idreg[1] = pac207_read_reg(gspca_dev, 0x0001); + idreg[0] = ((idreg[0] & 0x0f) << 4) | ((idreg[1] & 0xf0) >> 4); + idreg[1] = idreg[1] & 0x0f; + PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X", + idreg[0], idreg[1]); + + if (idreg[0] != 0x27) { + PDEBUG(D_PROBE, "Error invalid sensor ID!"); + return -ENODEV; + } + + PDEBUG(D_PROBE, + "Pixart PAC207BCA Image Processor and Control Chip detected" + " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + + cam = &gspca_dev->cam; + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + pac207_write_reg(gspca_dev, 0x41, 0x00); + /* Bit_0=Image Format, + * Bit_1=LED, + * Bit_2=Compression test mode enable */ + pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ + + return gspca_dev->usb_err; +} + +static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val) +{ + pac207_write_reg(gspca_dev, reg, val); + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->exposure->val = PAC207_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC207_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_EXPOSURE_REG, + gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_GAIN_REG, + gspca_dev->gain->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, + PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX, + 1, PAC207_BRIGHTNESS_DEFAULT); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, + PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX, + 1, PAC207_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, + PAC207_GAIN_MIN, PAC207_GAIN_MAX, + 1, PAC207_GAIN_DEFAULT); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 mode; + + pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */ + pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8); + pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8); + pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8); + pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8); + + /* Compression Balance */ + if (gspca_dev->width == 176) + pac207_write_reg(gspca_dev, 0x4a, 0xff); + else + pac207_write_reg(gspca_dev, 0x4a, 0x30); + pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ + pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness)); + + /* PGA global gain (Bit 4-0) */ + pac207_write_reg(gspca_dev, 0x0e, + v4l2_ctrl_g_ctrl(gspca_dev->gain)); + pac207_write_reg(gspca_dev, 0x02, + v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ + + mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (gspca_dev->width == 176) { /* 176x144 */ + mode |= 0x01; + PDEBUG(D_STREAM, "pac207_start mode 176x144"); + } else { /* 352x288 */ + PDEBUG(D_STREAM, "pac207_start mode 352x288"); + } + pac207_write_reg(gspca_dev, 0x41, mode); + + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ + msleep(10); + pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */ + + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ + pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ + pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ +} + + +static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + + if (avg_lum == -1) + return; + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + 90, PAC207_AUTOGAIN_DEADZONE)) + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char *sof; + + sof = pac_find_sof(&sd->sof_read, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sizeof pac_sof_marker) + n -= sizeof pac_sof_marker; + else + n = 0; + gspca_frame_add(gspca_dev, LAST_PACKET, + data, n); + sd->header_read = 0; + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + len -= sof - data; + data = sof; + } + if (sd->header_read < 11) { + int needed; + + /* get average lumination from frame header (byte 5) */ + if (sd->header_read < 5) { + needed = 5 - sd->header_read; + if (len >= needed) + atomic_set(&sd->avg_lum, data[needed - 1]); + } + /* skip the rest of the header */ + needed = 11 - sd->header_read; + if (len <= needed) { + sd->header_read += len; + return; + } + data += needed; + len -= needed; + sd->header_read = 11; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrput packet length */ +{ + int ret = -EINVAL; + + if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .dq_callback = pac207_do_auto_gain, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4028)}, + {USB_DEVICE(0x093a, 0x2460)}, + {USB_DEVICE(0x093a, 0x2461)}, + {USB_DEVICE(0x093a, 0x2463)}, + {USB_DEVICE(0x093a, 0x2464)}, + {USB_DEVICE(0x093a, 0x2468)}, + {USB_DEVICE(0x093a, 0x2470)}, + {USB_DEVICE(0x093a, 0x2471)}, + {USB_DEVICE(0x093a, 0x2472)}, + {USB_DEVICE(0x093a, 0x2474)}, + {USB_DEVICE(0x093a, 0x2476)}, + {USB_DEVICE(0x145f, 0x013a)}, + {USB_DEVICE(0x2001, 0xf115)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c new file mode 100644 index 000000000000..4877f7ab3d59 --- /dev/null +++ b/drivers/media/usb/gspca/pac7302.c @@ -0,0 +1,932 @@ +/* + * Pixart PAC7302 driver + * + * Copyright (C) 2008-2012 Jean-Francois Moine + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * Separated from Pixart PAC7311 library by Márton Németh + * Camera button input handling by Márton Németh + * Copyright (C) 2009-2010 Márton Németh + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Some documentation about various registers as determined by trial and error. + * + * Register page 1: + * + * Address Description + * 0x78 Global control, bit 6 controls the LED (inverted) + * 0x80 Compression balance, 2 interesting settings: + * 0x0f Default + * 0x50 Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Only usable when running + * at < 15 fps! Note currently the driver does not use this + * as the quality gain is small and the generated JPG-s are + * only understood by v4l-utils >= 0.8.9 + * + * Register page 3: + * + * Address Description + * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on + * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps + * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, + * 63 -> ~27 fps, the 2 msb's must always be 1 !! + * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: + * 1 -> ~30 fps, 2 -> ~20 fps + * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time + * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all + * 0x10 Gain 0-31 + * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an + * amplification value of 1 rather then 0 at its lowest setting + * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * 0x80 Another framerate control, best left at 1, moving it from 1 to + * 2 causes the framerate to become 3/4th of what it was, and + * also seems to cause pixel averaging, resulting in an effective + * resolution of 320x240 and thus a much blockier image + * + * The registers are accessed in the following functions: + * + * Page | Register | Function + * -----+------------+--------------------------------------------------- + * 0 | 0x0f..0x20 | setcolors() + * 0 | 0xa2..0xab | setbrightcont() + * 0 | 0xc5 | setredbalance() + * 0 | 0xc6 | setwhitebalance() + * 0 | 0xc7 | setbluebalance() + * 0 | 0xdc | setbrightcont(), setcolors() + * 3 | 0x02 | setexposure() + * 3 | 0x10, 0x12 | setgain() + * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() + * 3 | 0x21 | sethvflip() + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" + +#define PAC7302_GAIN_DEFAULT 15 +#define PAC7302_GAIN_KNEE 42 +#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */ +#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */ + +MODULE_AUTHOR("Jean-Francois Moine , " + "Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7302"); +MODULE_LICENSE("GPL"); + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct { /* brightness / contrast cluster */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + }; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *white_balance; + struct v4l2_ctrl *red_balance; + struct v4l2_ctrl *blue_balance; + struct { /* flip cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + u8 flags; +#define FL_HFLIP 0x01 /* mirrored by default */ +#define FL_VFLIP 0x02 /* vertical flipped by default */ + + u8 sof_read; + s8 autogain_ignore_frames; + + atomic_t avg_lum; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +#define LOAD_PAGE3 255 +#define END_OF_SEQUENCE 0 + +static const u8 init_7302[] = { +/* index,value */ + 0xff, 0x01, /* page 1 */ + 0x78, 0x00, /* deactivate */ + 0xff, 0x01, + 0x78, 0x40, /* led off */ +}; +static const u8 start_7302[] = { +/* index, len, [value]* */ + 0xff, 1, 0x00, /* page 0 */ + 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, + 0x00, 0x00, 0x00, 0x00, + 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, + 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, + 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, + 0x26, 2, 0xaa, 0xaa, + 0x2e, 1, 0x31, + 0x38, 1, 0x01, + 0x3a, 3, 0x14, 0xff, 0x5a, + 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, + 0x00, 0x54, 0x11, + 0x55, 1, 0x00, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x6b, 1, 0x00, + 0x6e, 3, 0x08, 0x06, 0x00, + 0x72, 3, 0x00, 0xff, 0x00, + 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, + 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, + 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, + 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, + 0xd2, 0xeb, + 0xaf, 1, 0x02, + 0xb5, 2, 0x08, 0x08, + 0xb8, 2, 0x08, 0x88, + 0xc4, 4, 0xae, 0x01, 0x04, 0x01, + 0xcc, 1, 0x00, + 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, + 0xc1, 0xd7, 0xec, + 0xdc, 1, 0x01, + 0xff, 1, 0x01, /* page 1 */ + 0x12, 3, 0x02, 0x00, 0x01, + 0x3e, 2, 0x00, 0x00, + 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, + 0x7c, 1, 0x00, + 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, + 0x02, 0x00, + 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, + 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, + 0xd8, 1, 0x01, + 0xdb, 2, 0x00, 0x01, + 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, + 0xe6, 4, 0x00, 0x00, 0x00, 0x01, + 0xeb, 1, 0x00, + 0xff, 1, 0x02, /* page 2 */ + 0x22, 1, 0x00, + 0xff, 1, 0x03, /* page 3 */ + 0, LOAD_PAGE3, /* load the page 3 */ + 0x11, 1, 0x01, + 0xff, 1, 0x02, /* page 2 */ + 0x13, 1, 0x00, + 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, + 0x27, 2, 0x14, 0x0c, + 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, + 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, + 0x6e, 1, 0x08, + 0xff, 1, 0x01, /* page 1 */ + 0x78, 1, 0x00, + 0, END_OF_SEQUENCE /* end of sequence */ +}; + +#define SKIP 0xaa +/* page 3 - the value SKIP says skip the index - see reg_w_page() */ +static const u8 page3_7302[] = { + 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16, + 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, + 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, + SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, + 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, + 0x00 +}; + +static void reg_w_buf(struct gspca_dev *gspca_dev, + u8 index, + const u8 *buffer, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + memcpy(gspca_dev->usb_buf, buffer, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + pr_err("reg_w_buf failed i: %02x error %d\n", + index, ret); + gspca_dev->usb_err = ret; + } +} + + +static void reg_w(struct gspca_dev *gspca_dev, + u8 index, + u8 value) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + gspca_dev->usb_buf[0] = value; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_w() failed i: %02x v: %02x error %d\n", + index, value, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w_seq(struct gspca_dev *gspca_dev, + const u8 *seq, int len) +{ + while (--len >= 0) { + reg_w(gspca_dev, seq[0], seq[1]); + seq += 2; + } +} + +/* load the beginning of a page */ +static void reg_w_page(struct gspca_dev *gspca_dev, + const u8 *page, int len) +{ + int index; + int ret = 0; + + if (gspca_dev->usb_err < 0) + return; + for (index = 0; index < len; index++) { + if (page[index] == SKIP) /* skip this index */ + continue; + gspca_dev->usb_buf[0] = page[index]; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_w_page() failed i: %02x v: %02x error %d\n", + index, page[index], ret); + gspca_dev->usb_err = ret; + break; + } + } +} + +/* output a variable sequence */ +static void reg_w_var(struct gspca_dev *gspca_dev, + const u8 *seq, + const u8 *page3, unsigned int page3_len) +{ + int index, len; + + for (;;) { + index = *seq++; + len = *seq++; + switch (len) { + case END_OF_SEQUENCE: + return; + case LOAD_PAGE3: + reg_w_page(gspca_dev, page3, page3_len); + break; + default: +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + PDEBUG(D_ERR|D_STREAM, + "Incorrect variable sequence"); + return; + } +#endif + while (len > 0) { + if (len < 8) { + reg_w_buf(gspca_dev, + index, seq, len); + seq += len; + break; + } + reg_w_buf(gspca_dev, index, seq, 8); + seq += 8; + index += 8; + len -= 8; + } + } + } + /* not reached */ +} + +/* this function is called at probe time for pac7302 */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + cam->cam_mode = vga_mode; /* only 640x480 */ + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->flags = id->driver_info; + return 0; +} + +static void setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + static const u8 max[10] = + {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, + 0xd4, 0xec}; + static const u8 delta[10] = + {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, + 0x11, 0x0b}; + + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 10; i++) { + v = max[i]; + v += (sd->brightness->val - sd->brightness->maximum) + * 150 / sd->brightness->maximum; /* 200 ? */ + v -= delta[i] * sd->contrast->val / sd->contrast->maximum; + if (v < 0) + v = 0; + else if (v > 0xff) + v = 0xff; + reg_w(gspca_dev, 0xa2 + i, v); + } + reg_w(gspca_dev, 0xdc, 0x01); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + static const int a[9] = + {217, -212, 0, -101, 170, -67, -38, -315, 355}; + static const int b[9] = + {19, 106, 0, 19, 106, 1, 19, 106, 1}; + + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x11, 0x01); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 9; i++) { + v = a[i] * sd->saturation->val / sd->saturation->maximum; + v += b[i]; + reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); + reg_w(gspca_dev, 0x0f + 2 * i + 1, v); + } + reg_w(gspca_dev, 0xdc, 0x01); +} + +static void setwhitebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xc6, sd->white_balance->val); + + reg_w(gspca_dev, 0xdc, 0x01); +} + +static void setredbalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xc5, sd->red_balance->val); + + reg_w(gspca_dev, 0xdc, 0x01); +} + +static void setbluebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xc7, sd->blue_balance->val); + + reg_w(gspca_dev, 0xdc, 0x01); +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + u8 reg10, reg12; + + if (gspca_dev->gain->val < 32) { + reg10 = gspca_dev->gain->val; + reg12 = 0; + } else { + reg10 = 31; + reg12 = gspca_dev->gain->val - 31; + } + + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x10, reg10); + reg_w(gspca_dev, 0x12, reg12); + + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + u8 clockdiv; + u16 exposure; + + /* + * Register 2 of frame 3 contains the clock divider configuring the + * no fps according to the formula: 90 / reg. sd->exposure is the + * desired exposure time in 0.5 ms. + */ + clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000; + + /* + * Note clockdiv = 3 also works, but when running at 30 fps, depending + * on the scene being recorded, the camera switches to another + * quantization table for certain JPEG blocks, and we don't know how + * to decompress these blocks. So we cap the framerate at 15 fps. + */ + if (clockdiv < 6) + clockdiv = 6; + else if (clockdiv > 63) + clockdiv = 63; + + /* + * Register 2 MUST be a multiple of 3, except when between 6 and 12? + * Always round up, otherwise we cannot get the desired frametime + * using the partial frame time exposure control. + */ + if (clockdiv < 6 || clockdiv > 12) + clockdiv = ((clockdiv + 2) / 3) * 3; + + /* + * frame exposure time in ms = 1000 * clockdiv / 90 -> + * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) + */ + exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv); + /* 0 = use full frametime, 448 = no exposure, reverse it */ + exposure = 448 - exposure; + + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x02, clockdiv); + reg_w(gspca_dev, 0x0e, exposure & 0xff); + reg_w(gspca_dev, 0x0f, exposure >> 8); + + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} + +static void sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data, hflip, vflip; + + hflip = sd->hflip->val; + if (sd->flags & FL_HFLIP) + hflip = !hflip; + vflip = sd->vflip->val; + if (sd->flags & FL_VFLIP) + vflip = !vflip; + + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00); + reg_w(gspca_dev, 0x21, data); + + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} + +/* this function is called at probe and resume time for pac7302 */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2); + return gspca_dev->usb_err; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->exposure->val = PAC7302_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC7302_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightcont(gspca_dev); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + setwhitebalance(gspca_dev); + break; + case V4L2_CID_RED_BALANCE: + setredbalance(gspca_dev); + break; + case V4L2_CID_BLUE_BALANCE: + setbluebalance(gspca_dev); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setexposure(gspca_dev); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setgain(gspca_dev); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 11); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 32, 1, 16); + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + + sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 127); + sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, 255, 1, 4); + sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 3, 1, 1); + sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 3, 1, 1); + + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 1023, 1, + PAC7302_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 62, 1, + PAC7302_GAIN_DEFAULT); + + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_cluster(2, &sd->brightness); + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + v4l2_ctrl_cluster(2, &sd->hflip); + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w_var(gspca_dev, start_7302, + page3_7302, sizeof(page3_7302)); + setbrightcont(gspca_dev); + setcolors(gspca_dev); + setwhitebalance(gspca_dev); + setredbalance(gspca_dev); + setbluebalance(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); + sethvflip(gspca_dev); + + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, 270 + sd->brightness->val); + + /* start stream */ + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x01); + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + + /* stop stream */ + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x00); +} + +/* called on streamoff with alt 0 and on disconnect for pac7302 */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + if (!gspca_dev->present) + return; + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x40); +} + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + int desired_lum; + const int deadzone = 30; + + if (sd->autogain_ignore_frames < 0) + return; + + if (sd->autogain_ignore_frames > 0) { + sd->autogain_ignore_frames--; + } else { + desired_lum = 270 + sd->brightness->val; + + if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum, + deadzone, PAC7302_GAIN_KNEE, + PAC7302_EXPOSURE_KNEE)) + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + } +} + +/* JPEG header */ +static const u8 jpeg_header[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08, /* Precision: 8 */ + 0x02, 0x80, /* height = 640 (image rotated) */ + 0x01, 0xe0, /* width = 480 */ + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ +}; + +/* this function is run at interrupt level */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 *image; + u8 *sof; + + sof = pac_find_sof(&sd->sof_read, data, len); + if (sof) { + int n, lum_offset, footer_length; + + /* + * 6 bytes after the FF D9 EOF marker a number of lumination + * bytes are send corresponding to different parts of the + * image, the 14th and 15th byte after the EOF seem to + * correspond to the center of the image. + */ + lum_offset = 61 + sizeof pac_sof_marker; + footer_length = 74; + + /* Finish decoding current frame */ + n = (sof - data) - (footer_length + sizeof pac_sof_marker); + if (n < 0) { + gspca_dev->image_len += n; + n = 0; + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); + } + + image = gspca_dev->image; + if (image != NULL + && image[gspca_dev->image_len - 2] == 0xff + && image[gspca_dev->image_len - 1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + n = sof - data; + len -= n; + data = sof; + + /* Get average lumination */ + if (gspca_dev->last_packet_type == LAST_PACKET && + n >= lum_offset) + atomic_set(&sd->avg_lum, data[-lum_offset] + + data[-lum_offset + 1]); + + /* Start the new frame with the jpeg header */ + /* The PAC7302 has the image rotated 90 degrees */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + jpeg_header, sizeof jpeg_header); + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + u8 index; + u8 value; + + /* + * reg->reg: bit0..15: reserved for register index (wIndex is 16bit + * long on the USB bus) + */ + if (reg->match.type == V4L2_CHIP_MATCH_HOST && + reg->match.addr == 0 && + (reg->reg < 0x000000ff) && + (reg->val <= 0x000000ff) + ) { + /* Currently writing to page 0 is only supported. */ + /* reg_w() only supports 8bit index */ + index = reg->reg; + value = reg->val; + + /* + * Note that there shall be no access to other page + * by any other function between the page switch and + * the actual register write. + */ + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, index, value); + + reg_w(gspca_dev, 0xdc, 0x01); + } + return gspca_dev->usb_err; +} + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + int ret = -EINVAL; + + if (chip->match.type == V4L2_CHIP_MATCH_HOST && + chip->match.addr == 0) { + chip->revision = 0; + chip->ident = V4L2_IDENT_UNKNOWN; + ret = 0; + } + return ret; +} +#endif + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrput packet length */ +{ + int ret = -EINVAL; + u8 data0, data1; + + if (len == 2) { + data0 = data[0]; + data1 = data[1]; + if ((data0 == 0x00 && data1 == 0x11) || + (data0 == 0x22 && data1 == 0x33) || + (data0 == 0x44 && data1 == 0x55) || + (data0 == 0x66 && data1 == 0x77) || + (data0 == 0x88 && data1 == 0x99) || + (data0 == 0xaa && data1 == 0xbb) || + (data0 == 0xcc && data1 == 0xdd) || + (data0 == 0xee && data1 == 0xff)) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + } + + return ret; +} +#endif + +/* sub-driver description for pac7302 */ +static const struct sd_desc sd_desc = { + .name = KBUILD_MODNAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_chip_ident = sd_chip_ident, +#endif +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3009)}, + {USB_DEVICE(0x06f8, 0x301b)}, + {USB_DEVICE(0x093a, 0x2620)}, + {USB_DEVICE(0x093a, 0x2621)}, + {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, + {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, + {USB_DEVICE(0x093a, 0x2625)}, + {USB_DEVICE(0x093a, 0x2626)}, + {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP}, + {USB_DEVICE(0x093a, 0x2628)}, + {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP}, + {USB_DEVICE(0x093a, 0x262a)}, + {USB_DEVICE(0x093a, 0x262c)}, + {USB_DEVICE(0x145f, 0x013c)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = KBUILD_MODNAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/pac7311.c b/drivers/media/usb/gspca/pac7311.c new file mode 100644 index 000000000000..ba3558d3f017 --- /dev/null +++ b/drivers/media/usb/gspca/pac7311.c @@ -0,0 +1,701 @@ +/* + * Pixart PAC7311 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Some documentation about various registers as determined by trial and error. + * + * Register page 1: + * + * Address Description + * 0x08 Unknown compressor related, must always be 8 except when not + * in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + * 0x1b Auto white balance related, bit 0 is AWB enable (inverted) + * bits 345 seem to toggle per color gains on/off (inverted) + * 0x78 Global control, bit 6 controls the LED (inverted) + * 0x80 Compression balance, interesting settings: + * 0x01 Use this to allow the camera to switch to higher compr. + * on the fly. Needed to stay within bandwidth @ 640x480@30 + * 0x1c From usb captures under Windows for 640x480 + * 0x2a Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Usable only at 640x480@ < + * 15 fps or 320x240 / 160x120. Note currently the driver + * does not use this as the quality gain is small and the + * generated JPG-s are only understood by v4l-utils >= 0.8.9 + * 0x3f From usb captures under Windows for 320x240 + * 0x69 From usb captures under Windows for 160x120 + * + * Register page 4: + * + * Address Description + * 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + * 0x0f Master gain 1-245, low value = high gain + * 0x10 Another gain 0-15, limited influence (1-2x gain I guess) + * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * Note setting vflip disabled leads to a much lower image quality, + * so we always vflip, and tell userspace to flip it back + * 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + * completely disable the analog amplification block. Set to 0x68 + * for max gain, 0x14 for minimal gain. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "pac7311" + +#include +#include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" + +#define PAC7311_GAIN_DEFAULT 122 +#define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */ + +MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7311"); +MODULE_LICENSE("GPL"); + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *hflip; + + u8 sof_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +#define LOAD_PAGE4 254 +#define END_OF_SEQUENCE 0 + +static const __u8 init_7311[] = { + 0xff, 0x01, + 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ + 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ + 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */ + 0xff, 0x04, + 0x27, 0x80, + 0x28, 0xca, + 0x29, 0x53, + 0x2a, 0x0e, + 0xff, 0x01, + 0x3e, 0x20, +}; + +static const __u8 start_7311[] = { +/* index, len, [value]* */ + 0xff, 1, 0x01, /* page 1 */ + 0x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c, + 0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e, + 0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49, + 0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48, + 0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78, + 0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b, + 0xd0, 0xff, + 0x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80, + 0x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84, + 0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14, + 0x18, 0x20, + 0x96, 3, 0x01, 0x08, 0x04, + 0xa0, 4, 0x44, 0x44, 0x44, 0x04, + 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, + 0x3f, 0x00, 0x0a, 0x01, 0x00, + 0xff, 1, 0x04, /* page 4 */ + 0, LOAD_PAGE4, /* load the page 4 */ + 0x11, 1, 0x01, + 0, END_OF_SEQUENCE /* end of sequence */ +}; + +#define SKIP 0xaa +/* page 4 - the value SKIP says skip the index - see reg_w_page() */ +static const __u8 page4_7311[] = { + SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, + 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62, + 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP, + SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68, + 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 +}; + +static void reg_w_buf(struct gspca_dev *gspca_dev, + __u8 index, + const u8 *buffer, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + memcpy(gspca_dev->usb_buf, buffer, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + pr_err("reg_w_buf() failed index 0x%02x, error %d\n", + index, ret); + gspca_dev->usb_err = ret; + } +} + + +static void reg_w(struct gspca_dev *gspca_dev, + __u8 index, + __u8 value) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + gspca_dev->usb_buf[0] = value; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n", + index, value, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w_seq(struct gspca_dev *gspca_dev, + const __u8 *seq, int len) +{ + while (--len >= 0) { + reg_w(gspca_dev, seq[0], seq[1]); + seq += 2; + } +} + +/* load the beginning of a page */ +static void reg_w_page(struct gspca_dev *gspca_dev, + const __u8 *page, int len) +{ + int index; + int ret = 0; + + if (gspca_dev->usb_err < 0) + return; + for (index = 0; index < len; index++) { + if (page[index] == SKIP) /* skip this index */ + continue; + gspca_dev->usb_buf[0] = page[index]; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n", + index, page[index], ret); + gspca_dev->usb_err = ret; + break; + } + } +} + +/* output a variable sequence */ +static void reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq, + const __u8 *page4, unsigned int page4_len) +{ + int index, len; + + for (;;) { + index = *seq++; + len = *seq++; + switch (len) { + case END_OF_SEQUENCE: + return; + case LOAD_PAGE4: + reg_w_page(gspca_dev, page4, page4_len); + break; + default: + if (len > USB_BUF_SZ) { + PDEBUG(D_ERR|D_STREAM, + "Incorrect variable sequence"); + return; + } + while (len > 0) { + if (len < 8) { + reg_w_buf(gspca_dev, + index, seq, len); + seq += len; + break; + } + reg_w_buf(gspca_dev, index, seq, 8); + seq += 8; + index += 8; + len -= 8; + } + } + } + /* not reached */ +} + +/* this function is called at probe time for pac7311 */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + cam->input_flags = V4L2_IN_ST_VFLIP; + + return 0; +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x10, val); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} + +static void setgain(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + reg_w(gspca_dev, 0x0e, 0x00); + reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1); + + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + reg_w(gspca_dev, 0x02, val); + + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + + /* + * Page 1 register 8 must always be 0x08 except when not in + * 640x480 mode and page 4 reg 2 <= 3 then it must be 9 + */ + reg_w(gspca_dev, 0xff, 0x01); + if (gspca_dev->width != 640 && val <= 3) + reg_w(gspca_dev, 0x08, 0x09); + else + reg_w(gspca_dev, 0x08, 0x08); + + /* + * Page1 register 80 sets the compression balance, normally we + * want / use 0x1c, but for 640x480@30fps we must allow the + * camera to use higher compression or we may run out of + * bandwidth. + */ + if (gspca_dev->width == 640 && val == 2) + reg_w(gspca_dev, 0x80, 0x01); + else + reg_w(gspca_dev, 0x80, 0x1c); + + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} + +static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) +{ + __u8 data; + + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + data = (hflip ? 0x04 : 0x00) | + (vflip ? 0x08 : 0x00); + reg_w(gspca_dev, 0x21, data); + + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} + +/* this function is called at probe and resume time for pac7311 */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2); + return gspca_dev->usb_err; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC7311_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setexposure(gspca_dev, gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setgain(gspca_dev, gspca_dev->gain->val); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev, sd->hflip->val, 1); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 7); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 2, 63, 1, + PAC7311_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 244, 1, + PAC7311_GAIN_DEFAULT); + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sof_read = 0; + + reg_w_var(gspca_dev, start_7311, + page4_7311, sizeof(page4_7311)); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); + setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain)); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1); + + /* set correct resolution */ + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 2: /* 160x120 */ + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x17, 0x20); + reg_w(gspca_dev, 0x87, 0x10); + break; + case 1: /* 320x240 */ + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x17, 0x30); + reg_w(gspca_dev, 0x87, 0x11); + break; + case 0: /* 640x480 */ + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x17, 0x00); + reg_w(gspca_dev, 0x87, 0x12); + break; + } + + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); + + /* start stream */ + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x05); + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x27, 0x80); + reg_w(gspca_dev, 0x28, 0xca); + reg_w(gspca_dev, 0x29, 0x53); + reg_w(gspca_dev, 0x2a, 0x0e); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x3e, 0x20); + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ +} + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + int desired_lum, deadzone; + + if (avg_lum == -1) + return; + + desired_lum = 170; + deadzone = 20; + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + desired_lum, deadzone)) + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; +} + +/* JPEG header, part 1 */ +static const unsigned char pac_jpeg_header1[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08 /* Precision: 8 */ + /* 2 bytes is placed here: number of image lines */ + /* 2 bytes is placed here: samples per line */ +}; + +/* JPEG header, continued */ +static const unsigned char pac_jpeg_header2[] = { + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ +}; + +static void pac_start_frame(struct gspca_dev *gspca_dev, + __u16 lines, __u16 samples_per_line) +{ + unsigned char tmpbuf[4]; + + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_jpeg_header1, sizeof(pac_jpeg_header1)); + + tmpbuf[0] = lines >> 8; + tmpbuf[1] = lines & 0xff; + tmpbuf[2] = samples_per_line >> 8; + tmpbuf[3] = samples_per_line & 0xff; + + gspca_frame_add(gspca_dev, INTER_PACKET, + tmpbuf, sizeof(tmpbuf)); + gspca_frame_add(gspca_dev, INTER_PACKET, + pac_jpeg_header2, sizeof(pac_jpeg_header2)); +} + +/* this function is run at interrupt level */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 *image; + unsigned char *sof; + + sof = pac_find_sof(&sd->sof_read, data, len); + if (sof) { + int n, lum_offset, footer_length; + + /* + * 6 bytes after the FF D9 EOF marker a number of lumination + * bytes are send corresponding to different parts of the + * image, the 14th and 15th byte after the EOF seem to + * correspond to the center of the image. + */ + lum_offset = 24 + sizeof pac_sof_marker; + footer_length = 26; + + /* Finish decoding current frame */ + n = (sof - data) - (footer_length + sizeof pac_sof_marker); + if (n < 0) { + gspca_dev->image_len += n; + n = 0; + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); + } + image = gspca_dev->image; + if (image != NULL + && image[gspca_dev->image_len - 2] == 0xff + && image[gspca_dev->image_len - 1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + n = sof - data; + len -= n; + data = sof; + + /* Get average lumination */ + if (gspca_dev->last_packet_type == LAST_PACKET && + n >= lum_offset) + atomic_set(&sd->avg_lum, data[-lum_offset] + + data[-lum_offset + 1]); + else + atomic_set(&sd->avg_lum, -1); + + /* Start the new frame with the jpeg header */ + pac_start_frame(gspca_dev, + gspca_dev->height, gspca_dev->width); + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + u8 data0, data1; + + if (len == 2) { + data0 = data[0]; + data1 = data[1]; + if ((data0 == 0x00 && data1 == 0x11) || + (data0 == 0x22 && data1 == 0x33) || + (data0 == 0x44 && data1 == 0x55) || + (data0 == 0x66 && data1 == 0x77) || + (data0 == 0x88 && data1 == 0x99) || + (data0 == 0xaa && data1 == 0xbb) || + (data0 == 0xcc && data1 == 0xdd) || + (data0 == 0xee && data1 == 0xff)) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + } + + return ret; +} +#endif + +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x2600)}, + {USB_DEVICE(0x093a, 0x2601)}, + {USB_DEVICE(0x093a, 0x2603)}, + {USB_DEVICE(0x093a, 0x2608)}, + {USB_DEVICE(0x093a, 0x260e)}, + {USB_DEVICE(0x093a, 0x260f)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/pac_common.h b/drivers/media/usb/gspca/pac_common.h new file mode 100644 index 000000000000..8462a7c1a338 --- /dev/null +++ b/drivers/media/usb/gspca/pac_common.h @@ -0,0 +1,134 @@ +/* + * Pixart PAC207BCA / PAC73xx common functions + * + * Copyright (C) 2008 Hans de Goede + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* We calculate the autogain at the end of the transfer of a frame, at this + moment a frame with the old settings is being captured and transmitted. So + if we adjust the gain or exposure we must ignore atleast the next frame for + the new settings to come into effect before doing any other adjustments. */ +#define PAC_AUTOGAIN_IGNORE_FRAMES 2 + +static const unsigned char pac_sof_marker[5] = + { 0xff, 0xff, 0x00, 0xff, 0x96 }; + +/* + The following state machine finds the SOF marker sequence + 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream. + + +----------+ + | 0: START |<---------------\ + +----------+<-\ | + | \---/otherwise | + v 0xff | + +----------+ otherwise | + | 1 |--------------->* + | | ^ + +----------+ | + | | + v 0xff | + +----------+<-\0xff | + /->| |--/ | + | | 2 |--------------->* + | | | otherwise ^ + | +----------+ | + | | | + | v 0x00 | + | +----------+ | + | | 3 | | + | | |--------------->* + | +----------+ otherwise ^ + | | | + 0xff | v 0xff | + | +----------+ | + \--| 4 | | + | |----------------/ + +----------+ otherwise + | + v 0x96 + +----------+ + | FOUND | + +----------+ +*/ + +static unsigned char *pac_find_sof(u8 *sof_read, + unsigned char *m, int len) +{ + int i; + + /* Search for the SOF marker (fixed part) in the header */ + for (i = 0; i < len; i++) { + switch (*sof_read) { + case 0: + if (m[i] == 0xff) + *sof_read = 1; + break; + case 1: + if (m[i] == 0xff) + *sof_read = 2; + else + *sof_read = 0; + break; + case 2: + switch (m[i]) { + case 0x00: + *sof_read = 3; + break; + case 0xff: + /* stay in this state */ + break; + default: + *sof_read = 0; + } + break; + case 3: + if (m[i] == 0xff) + *sof_read = 4; + else + *sof_read = 0; + break; + case 4: + switch (m[i]) { + case 0x96: + /* Pattern found */ + PDEBUG(D_FRAM, + "SOF found, bytes to analyze: %u." + " Frame starts at byte #%u", + len, i + 1); + *sof_read = 0; + return m + i + 1; + break; + case 0xff: + *sof_read = 2; + break; + default: + *sof_read = 0; + } + break; + default: + *sof_read = 0; + } + } + + return NULL; +} diff --git a/drivers/media/usb/gspca/se401.c b/drivers/media/usb/gspca/se401.c new file mode 100644 index 000000000000..a33cb78a839c --- /dev/null +++ b/drivers/media/usb/gspca/se401.c @@ -0,0 +1,739 @@ +/* + * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver + * + * Copyright (C) 2011 Hans de Goede + * + * Based on the v4l1 se401 driver which is: + * + * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "se401" + +#define BULK_SIZE 4096 +#define PACKET_SIZE 1024 +#define READ_REQ_SIZE 64 +#define MAX_MODES ((READ_REQ_SIZE - 6) / 4) +/* The se401 compression algorithm uses a fixed quant factor, which + can be configured by setting the high nibble of the SE401_OPERATINGMODE + feature. This needs to exactly match what is in libv4l! */ +#define SE401_QUANT_FACT 8 + +#include +#include +#include "gspca.h" +#include "se401.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Endpoints se401"); +MODULE_LICENSE("GPL"); + +/* exposure change state machine states */ +enum { + EXPO_CHANGED, + EXPO_DROP_FRAME, + EXPO_NO_CHANGE, +}; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct { /* exposure/freq control cluster */ + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *freq; + }; + bool has_brightness; + struct v4l2_pix_format fmts[MAX_MODES]; + int pixels_read; + int packet_read; + u8 packet[PACKET_SIZE]; + u8 restart_stream; + u8 button_state; + u8 resetlevel; + u8 resetlevel_frame_count; + int resetlevel_adjust_dir; + int expo_change_state; +}; + + +static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value, + int silent) +{ + int err; + + if (gspca_dev->usb_err < 0) + return; + + err = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0, NULL, 0, 1000); + if (err < 0) { + if (!silent) + pr_err("write req failed req %#04x val %#04x error %d\n", + req, value, err); + gspca_dev->usb_err = err; + } +} + +static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent) +{ + int err; + + if (gspca_dev->usb_err < 0) + return; + + if (USB_BUF_SZ < READ_REQ_SIZE) { + pr_err("USB_BUF_SZ too small!!\n"); + gspca_dev->usb_err = -ENOBUFS; + return; + } + + err = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000); + if (err < 0) { + if (!silent) + pr_err("read req failed req %#04x error %d\n", + req, err); + gspca_dev->usb_err = err; + } +} + +static void se401_set_feature(struct gspca_dev *gspca_dev, + u16 selector, u16 param) +{ + int err; + + if (gspca_dev->usb_err < 0) + return; + + err = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + SE401_REQ_SET_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + param, selector, NULL, 0, 1000); + if (err < 0) { + pr_err("set feature failed sel %#04x param %#04x error %d\n", + selector, param, err); + gspca_dev->usb_err = err; + } +} + +static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector) +{ + int err; + + if (gspca_dev->usb_err < 0) + return gspca_dev->usb_err; + + if (USB_BUF_SZ < 2) { + pr_err("USB_BUF_SZ too small!!\n"); + gspca_dev->usb_err = -ENOBUFS; + return gspca_dev->usb_err; + } + + err = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + SE401_REQ_GET_EXT_FEATURE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, selector, gspca_dev->usb_buf, 2, 1000); + if (err < 0) { + pr_err("get feature failed sel %#04x error %d\n", + selector, err); + gspca_dev->usb_err = err; + return err; + } + return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8); +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + /* HDG: this does not seem to do anything on my cam */ + se401_write_req(gspca_dev, SE401_REQ_SET_BRT, val, 0); +} + +static void setgain(struct gspca_dev *gspca_dev, s32 val) +{ + u16 gain = 63 - val; + + /* red color gain */ + se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain); + /* green color gain */ + se401_set_feature(gspca_dev, HV7131_REG_AGCG, gain); + /* blue color gain */ + se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq) +{ + struct sd *sd = (struct sd *) gspca_dev; + int integration = val << 6; + u8 expose_h, expose_m, expose_l; + + /* Do this before the set_feature calls, for proper timing wrt + the interrupt driven pkt_scan. Note we may still race but that + is not a big issue, the expo change state machine is merely for + avoiding underexposed frames getting send out, if one sneaks + through so be it */ + sd->expo_change_state = EXPO_CHANGED; + + if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) + integration = integration - integration % 106667; + if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) + integration = integration - integration % 88889; + + expose_h = (integration >> 16); + expose_m = (integration >> 8); + expose_l = integration; + + /* integration time low */ + se401_set_feature(gspca_dev, HV7131_REG_TITL, expose_l); + /* integration time mid */ + se401_set_feature(gspca_dev, HV7131_REG_TITM, expose_m); + /* integration time high */ + se401_set_feature(gspca_dev, HV7131_REG_TITU, expose_h); +} + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct cam *cam = &gspca_dev->cam; + u8 *cd = gspca_dev->usb_buf; + int i, j, n; + int widths[MAX_MODES], heights[MAX_MODES]; + + /* Read the camera descriptor */ + se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 1); + if (gspca_dev->usb_err) { + /* Sometimes after being idle for a while the se401 won't + respond and needs a good kicking */ + usb_reset_device(gspca_dev->dev); + gspca_dev->usb_err = 0; + se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0); + } + + /* Some cameras start with their LED on */ + se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0); + if (gspca_dev->usb_err) + return gspca_dev->usb_err; + + if (cd[1] != 0x41) { + pr_err("Wrong descriptor type\n"); + return -ENODEV; + } + + if (!(cd[2] & SE401_FORMAT_BAYER)) { + pr_err("Bayer format not supported!\n"); + return -ENODEV; + } + + if (cd[3]) + pr_info("ExtraFeatures: %d\n", cd[3]); + + n = cd[4] | (cd[5] << 8); + if (n > MAX_MODES) { + pr_err("Too many frame sizes\n"); + return -ENODEV; + } + + for (i = 0; i < n ; i++) { + widths[i] = cd[6 + i * 4 + 0] | (cd[6 + i * 4 + 1] << 8); + heights[i] = cd[6 + i * 4 + 2] | (cd[6 + i * 4 + 3] << 8); + } + + for (i = 0; i < n ; i++) { + sd->fmts[i].width = widths[i]; + sd->fmts[i].height = heights[i]; + sd->fmts[i].field = V4L2_FIELD_NONE; + sd->fmts[i].colorspace = V4L2_COLORSPACE_SRGB; + sd->fmts[i].priv = 1; + + /* janggu compression only works for 1/4th or 1/16th res */ + for (j = 0; j < n; j++) { + if (widths[j] / 2 == widths[i] && + heights[j] / 2 == heights[i]) { + sd->fmts[i].priv = 2; + break; + } + } + /* 1/16th if available too is better then 1/4th, because + we then use a larger area of the sensor */ + for (j = 0; j < n; j++) { + if (widths[j] / 4 == widths[i] && + heights[j] / 4 == heights[i]) { + sd->fmts[i].priv = 4; + break; + } + } + + if (sd->fmts[i].priv == 1) { + /* Not a 1/4th or 1/16th res, use bayer */ + sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8; + sd->fmts[i].bytesperline = widths[i]; + sd->fmts[i].sizeimage = widths[i] * heights[i]; + pr_info("Frame size: %dx%d bayer\n", + widths[i], heights[i]); + } else { + /* Found a match use janggu compression */ + sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401; + sd->fmts[i].bytesperline = 0; + sd->fmts[i].sizeimage = widths[i] * heights[i] * 3; + pr_info("Frame size: %dx%d 1/%dth janggu\n", + widths[i], heights[i], + sd->fmts[i].priv * sd->fmts[i].priv); + } + } + + cam->cam_mode = sd->fmts; + cam->nmodes = n; + cam->bulk = 1; + cam->bulk_size = BULK_SIZE; + cam->bulk_nurbs = 4; + sd->resetlevel = 0x2d; /* Set initial resetlevel */ + + /* See if the camera supports brightness */ + se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1); + sd->has_brightness = !!gspca_dev->usb_err; + gspca_dev->usb_err = 0; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + gspca_dev->alt = 1; /* Ignore the bogus isoc alt settings */ + + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + int mode = 0; + + se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 1); + if (gspca_dev->usb_err) { + /* Sometimes after being idle for a while the se401 won't + respond and needs a good kicking */ + usb_reset_device(gspca_dev->dev); + gspca_dev->usb_err = 0; + se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 0); + } + se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 1, 0); + + se401_set_feature(gspca_dev, HV7131_REG_MODE_B, 0x05); + + /* set size + mode */ + se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH, + gspca_dev->width * mult, 0); + se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT, + gspca_dev->height * mult, 0); + /* + * HDG: disabled this as it does not seem to do anything + * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE, + * SE401_FORMAT_BAYER, 0); + */ + + switch (mult) { + case 1: /* Raw bayer */ + mode = 0x03; break; + case 2: /* 1/4th janggu */ + mode = SE401_QUANT_FACT << 4; break; + case 4: /* 1/16th janggu */ + mode = (SE401_QUANT_FACT << 4) | 0x02; break; + } + se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode); + + se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel); + + sd->packet_read = 0; + sd->pixels_read = 0; + sd->restart_stream = 0; + sd->resetlevel_frame_count = 0; + sd->resetlevel_adjust_dir = 0; + sd->expo_change_state = EXPO_NO_CHANGE; + + se401_write_req(gspca_dev, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, 0); + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + se401_write_req(gspca_dev, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, 0); + se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0); + se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 0, 0); +} + +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + unsigned int ahrc, alrc; + int oldreset, adjust_dir; + + /* Restart the stream if requested do so by pkt_scan */ + if (sd->restart_stream) { + sd_stopN(gspca_dev); + sd_start(gspca_dev); + sd->restart_stream = 0; + } + + /* Automatically adjust sensor reset level + Hyundai have some really nice docs about this and other sensor + related stuff on their homepage: www.hei.co.kr */ + sd->resetlevel_frame_count++; + if (sd->resetlevel_frame_count < 20) + return; + + /* For some reason this normally read-only register doesn't get reset + to zero after reading them just once... */ + se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH); + se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL); + se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH); + se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL); + ahrc = 256*se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH) + + se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL); + alrc = 256*se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH) + + se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL); + + /* Not an exact science, but it seems to work pretty well... */ + oldreset = sd->resetlevel; + if (alrc > 10) { + while (alrc >= 10 && sd->resetlevel < 63) { + sd->resetlevel++; + alrc /= 2; + } + } else if (ahrc > 20) { + while (ahrc >= 20 && sd->resetlevel > 0) { + sd->resetlevel--; + ahrc /= 2; + } + } + /* Detect ping-pong-ing and halve adjustment to avoid overshoot */ + if (sd->resetlevel > oldreset) + adjust_dir = 1; + else + adjust_dir = -1; + if (sd->resetlevel_adjust_dir && + sd->resetlevel_adjust_dir != adjust_dir) + sd->resetlevel = oldreset + (sd->resetlevel - oldreset) / 2; + + if (sd->resetlevel != oldreset) { + sd->resetlevel_adjust_dir = adjust_dir; + se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel); + } + + sd->resetlevel_frame_count = 0; +} + +static void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len) +{ + struct sd *sd = (struct sd *)gspca_dev; + + switch (sd->expo_change_state) { + case EXPO_CHANGED: + /* The exposure was changed while this frame + was being send, so this frame is ok */ + sd->expo_change_state = EXPO_DROP_FRAME; + break; + case EXPO_DROP_FRAME: + /* The exposure was changed while this frame + was being captured, drop it! */ + gspca_dev->last_packet_type = DISCARD_PACKET; + sd->expo_change_state = EXPO_NO_CHANGE; + break; + case EXPO_NO_CHANGE: + break; + } + gspca_frame_add(gspca_dev, LAST_PACKET, data, len); +} + +static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len) +{ + struct sd *sd = (struct sd *)gspca_dev; + int imagesize = gspca_dev->width * gspca_dev->height; + int i, plen, bits, pixels, info, count; + + if (sd->restart_stream) + return; + + /* Sometimes a 1024 bytes garbage bulk packet is send between frames */ + if (gspca_dev->last_packet_type == LAST_PACKET && len == 1024) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + i = 0; + while (i < len) { + /* Read header if not already be present from prev bulk pkt */ + if (sd->packet_read < 4) { + count = 4 - sd->packet_read; + if (count > len - i) + count = len - i; + memcpy(&sd->packet[sd->packet_read], &data[i], count); + sd->packet_read += count; + i += count; + if (sd->packet_read < 4) + break; + } + bits = sd->packet[3] + (sd->packet[2] << 8); + pixels = sd->packet[1] + ((sd->packet[0] & 0x3f) << 8); + info = (sd->packet[0] & 0xc0) >> 6; + plen = ((bits + 47) >> 4) << 1; + /* Sanity checks */ + if (plen > 1024) { + pr_err("invalid packet len %d restarting stream\n", + plen); + goto error; + } + if (info == 3) { + pr_err("unknown frame info value restarting stream\n"); + goto error; + } + + /* Read (remainder of) packet contents */ + count = plen - sd->packet_read; + if (count > len - i) + count = len - i; + memcpy(&sd->packet[sd->packet_read], &data[i], count); + sd->packet_read += count; + i += count; + if (sd->packet_read < plen) + break; + + sd->pixels_read += pixels; + sd->packet_read = 0; + + switch (info) { + case 0: /* Frame data */ + gspca_frame_add(gspca_dev, INTER_PACKET, sd->packet, + plen); + break; + case 1: /* EOF */ + if (sd->pixels_read != imagesize) { + pr_err("frame size %d expected %d\n", + sd->pixels_read, imagesize); + goto error; + } + sd_complete_frame(gspca_dev, sd->packet, plen); + return; /* Discard the rest of the bulk packet !! */ + case 2: /* SOF */ + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->packet, + plen); + sd->pixels_read = pixels; + break; + } + } + return; + +error: + sd->restart_stream = 1; + /* Give userspace a 0 bytes frame, so our dq callback gets + called and it can restart the stream */ + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); +} + +static void sd_pkt_scan_bayer(struct gspca_dev *gspca_dev, u8 *data, int len) +{ + struct cam *cam = &gspca_dev->cam; + int imagesize = cam->cam_mode[gspca_dev->curr_mode].sizeimage; + + if (gspca_dev->image_len == 0) { + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + return; + } + + if (gspca_dev->image_len + len >= imagesize) { + sd_complete_frame(gspca_dev, data, len); + return; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len) +{ + int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + + if (len == 0) + return; + + if (mult == 1) /* mult == 1 means raw bayer */ + sd_pkt_scan_bayer(gspca_dev, data, len); + else + sd_pkt_scan_janggu(gspca_dev, data, len); +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len) +{ + struct sd *sd = (struct sd *)gspca_dev; + u8 state; + + if (len != 2) + return -EINVAL; + + switch (data[0]) { + case 0: + case 1: + state = data[0]; + break; + default: + return -EINVAL; + } + if (sd->button_state != state) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, state); + input_sync(gspca_dev->input_dev); + sd->button_state = state; + } + + return 0; +} +#endif + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + setgain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, ctrl->val, sd->freq->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + if (sd->has_brightness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 15); + /* max is really 63 but > 50 is not pretty */ + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 50, 1, 25); + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 32767, 1, 15000); + sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(2, &sd->exposure); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .dq_callback = sd_dq_callback, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x03e8, 0x0004)}, /* Endpoints/Aox SE401 */ + {USB_DEVICE(0x0471, 0x030b)}, /* Philips PCVC665K */ + {USB_DEVICE(0x047d, 0x5001)}, /* Kensington 67014 */ + {USB_DEVICE(0x047d, 0x5002)}, /* Kensington 6701(5/7) */ + {USB_DEVICE(0x047d, 0x5003)}, /* Kensington 67016 */ + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static int sd_pre_reset(struct usb_interface *intf) +{ + return 0; +} + +static int sd_post_reset(struct usb_interface *intf) +{ + return 0; +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif + .pre_reset = sd_pre_reset, + .post_reset = sd_post_reset, +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/se401.h b/drivers/media/usb/gspca/se401.h new file mode 100644 index 000000000000..96d8ebf3cf59 --- /dev/null +++ b/drivers/media/usb/gspca/se401.h @@ -0,0 +1,90 @@ +/* + * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver + * + * Copyright (C) 2011 Hans de Goede + * + * Based on the v4l1 se401 driver which is: + * + * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 +#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 +#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 +#define SE401_REQ_CAPTURE_FRAME 0x43 +#define SE401_REQ_GET_BRT 0x44 +#define SE401_REQ_SET_BRT 0x45 +#define SE401_REQ_GET_WIDTH 0x4c +#define SE401_REQ_SET_WIDTH 0x4d +#define SE401_REQ_GET_HEIGHT 0x4e +#define SE401_REQ_SET_HEIGHT 0x4f +#define SE401_REQ_GET_OUTPUT_MODE 0x50 +#define SE401_REQ_SET_OUTPUT_MODE 0x51 +#define SE401_REQ_GET_EXT_FEATURE 0x52 +#define SE401_REQ_SET_EXT_FEATURE 0x53 +#define SE401_REQ_CAMERA_POWER 0x56 +#define SE401_REQ_LED_CONTROL 0x57 +#define SE401_REQ_BIOS 0xff + +#define SE401_BIOS_READ 0x07 + +#define SE401_FORMAT_BAYER 0x40 + +/* Hyundai hv7131b registers + 7121 and 7141 should be the same (haven't really checked...) */ +/* Mode registers: */ +#define HV7131_REG_MODE_A 0x00 +#define HV7131_REG_MODE_B 0x01 +#define HV7131_REG_MODE_C 0x02 +/* Frame registers: */ +#define HV7131_REG_FRSU 0x10 +#define HV7131_REG_FRSL 0x11 +#define HV7131_REG_FCSU 0x12 +#define HV7131_REG_FCSL 0x13 +#define HV7131_REG_FWHU 0x14 +#define HV7131_REG_FWHL 0x15 +#define HV7131_REG_FWWU 0x16 +#define HV7131_REG_FWWL 0x17 +/* Timing registers: */ +#define HV7131_REG_THBU 0x20 +#define HV7131_REG_THBL 0x21 +#define HV7131_REG_TVBU 0x22 +#define HV7131_REG_TVBL 0x23 +#define HV7131_REG_TITU 0x25 +#define HV7131_REG_TITM 0x26 +#define HV7131_REG_TITL 0x27 +#define HV7131_REG_TMCD 0x28 +/* Adjust Registers: */ +#define HV7131_REG_ARLV 0x30 +#define HV7131_REG_ARCG 0x31 +#define HV7131_REG_AGCG 0x32 +#define HV7131_REG_ABCG 0x33 +#define HV7131_REG_APBV 0x34 +#define HV7131_REG_ASLP 0x54 +/* Offset Registers: */ +#define HV7131_REG_OFSR 0x50 +#define HV7131_REG_OFSG 0x51 +#define HV7131_REG_OFSB 0x52 +/* REset level statistics registers: */ +#define HV7131_REG_LOREFNOH 0x57 +#define HV7131_REG_LOREFNOL 0x58 +#define HV7131_REG_HIREFNOH 0x59 +#define HV7131_REG_HIREFNOL 0x5a + +/* se401 registers */ +#define SE401_OPERATINGMODE 0x2000 diff --git a/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c new file mode 100644 index 000000000000..03fa3fd940b4 --- /dev/null +++ b/drivers/media/usb/gspca/sn9c2028.c @@ -0,0 +1,735 @@ +/* + * SN9C2028 library + * + * Copyright (C) 2009 Theodore Kilgore + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "sn9c2028" + +#include "gspca.h" + +MODULE_AUTHOR("Theodore Kilgore"); +MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + u8 sof_read; + u16 model; +}; + +struct init_command { + unsigned char instruction[6]; + unsigned char to_read; /* length to read. 0 means no reply requested */ +}; + +/* How to change the resolution of any of the VGA cams is unknown */ +static const struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* No way to change the resolution of the CIF cams is known */ +static const struct v4l2_pix_format cif_mode[] = { + {352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* the bytes to write are in gspca_dev->usb_buf */ +static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command) +{ + int rc; + + PDEBUG(D_USBO, "sending command %02x%02x%02x%02x%02x%02x", command[0], + command[1], command[2], command[3], command[4], command[5]); + + memcpy(gspca_dev->usb_buf, command, 6); + rc = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_CONFIGURATION, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 2, 0, gspca_dev->usb_buf, 6, 500); + if (rc < 0) { + pr_err("command write [%02x] error %d\n", + gspca_dev->usb_buf[0], rc); + return rc; + } + + return 0; +} + +static int sn9c2028_read1(struct gspca_dev *gspca_dev) +{ + int rc; + + rc = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 1, 0, gspca_dev->usb_buf, 1, 500); + if (rc != 1) { + pr_err("read1 error %d\n", rc); + return (rc < 0) ? rc : -EIO; + } + PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]); + return gspca_dev->usb_buf[0]; +} + +static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading) +{ + int rc; + rc = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 4, 0, gspca_dev->usb_buf, 4, 500); + if (rc != 4) { + pr_err("read4 error %d\n", rc); + return (rc < 0) ? rc : -EIO; + } + memcpy(reading, gspca_dev->usb_buf, 4); + PDEBUG(D_USBI, "read4 response %02x%02x%02x%02x", reading[0], + reading[1], reading[2], reading[3]); + return rc; +} + +static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command) +{ + int i, status; + __u8 reading[4]; + + status = sn9c2028_command(gspca_dev, command); + if (status < 0) + return status; + + status = -1; + for (i = 0; i < 256 && status < 2; i++) + status = sn9c2028_read1(gspca_dev); + if (status != 2) { + pr_err("long command status read error %d\n", status); + return (status < 0) ? status : -EIO; + } + + memset(reading, 0, 4); + status = sn9c2028_read4(gspca_dev, reading); + if (status < 0) + return status; + + /* in general, the first byte of the response is the first byte of + * the command, or'ed with 8 */ + status = sn9c2028_read1(gspca_dev); + if (status < 0) + return status; + + return 0; +} + +static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command) +{ + int err_code; + + err_code = sn9c2028_command(gspca_dev, command); + if (err_code < 0) + return err_code; + + err_code = sn9c2028_read1(gspca_dev); + if (err_code < 0) + return err_code; + + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + PDEBUG(D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)", + id->idVendor, id->idProduct); + + sd->model = id->idProduct; + + switch (sd->model) { + case 0x7005: + PDEBUG(D_PROBE, "Genius Smart 300 camera"); + break; + case 0x8000: + PDEBUG(D_PROBE, "DC31VC"); + break; + case 0x8001: + PDEBUG(D_PROBE, "Spy camera"); + break; + case 0x8003: + PDEBUG(D_PROBE, "CIF camera"); + break; + case 0x8008: + PDEBUG(D_PROBE, "Mini-Shotz ms-350 camera"); + break; + case 0x800a: + PDEBUG(D_PROBE, "Vivitar 3350b type camera"); + cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP; + break; + } + + switch (sd->model) { + case 0x8000: + case 0x8001: + case 0x8003: + cam->cam_mode = cif_mode; + cam->nmodes = ARRAY_SIZE(cif_mode); + break; + default: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int status = -1; + + sn9c2028_read1(gspca_dev); + sn9c2028_read1(gspca_dev); + status = sn9c2028_read1(gspca_dev); + + return (status < 0) ? status : 0; +} + +static int run_start_commands(struct gspca_dev *gspca_dev, + struct init_command *cam_commands, int n) +{ + int i, err_code = -1; + + for (i = 0; i < n; i++) { + switch (cam_commands[i].to_read) { + case 4: + err_code = sn9c2028_long_command(gspca_dev, + cam_commands[i].instruction); + break; + case 1: + err_code = sn9c2028_short_command(gspca_dev, + cam_commands[i].instruction); + break; + case 0: + err_code = sn9c2028_command(gspca_dev, + cam_commands[i].instruction); + break; + } + if (err_code < 0) + return err_code; + } + return 0; +} + +static int start_spy_cam(struct gspca_dev *gspca_dev) +{ + struct init_command spy_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width 352 */ + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */ + /* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */ + {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/ + /* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */ + {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4}, + /* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */ + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + /* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */ + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, + /* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */ + {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/ + /*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */ + {{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */ + /*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */ + {{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */ + {{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */ + {{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4}, + /*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */ + {{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4}, + /* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */ + /* brightness or gain. 0 is default. 4 is good + * indoors at night with incandescent lighting */ + {{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/ + {{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4}, + /* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */ + {{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */ + /* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */ + {{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */ + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, spy_start_commands, + ARRAY_SIZE(spy_start_commands)); +} + +static int start_cif_cam(struct gspca_dev *gspca_dev) +{ + struct init_command cif_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + /* The entire sequence below seems redundant */ + /* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width? + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height? + {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample? + {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/ + {{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */ + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */ + /* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample? + * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing + * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */ + /* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, + * causes subsampling + * but not a change in the resolution setting! */ + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */ + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, cif_start_commands, + ARRAY_SIZE(cif_start_commands)); +} + +static int start_ms350_cam(struct gspca_dev *gspca_dev) +{ + struct init_command ms350_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width */ + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */ + {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */ + {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */ + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0}, + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, ms350_start_commands, + ARRAY_SIZE(ms350_start_commands)); +} + +static int start_genius_cam(struct gspca_dev *gspca_dev) +{ + struct init_command genius_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, + /* "preliminary" width and height settings */ + {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */ + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */ + {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4}, + {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1}, + {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0} + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, genius_start_commands, + ARRAY_SIZE(genius_start_commands)); +} + +static int start_vivitar_cam(struct gspca_dev *gspca_dev) +{ + struct init_command vivitar_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4}, + /* + * Above is changed from OEM 0x0b. Fixes Bayer tiling. + * Presumably gives a vertical shift of one row. + */ + {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4}, + /* Above seems to do horizontal shift. */ + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + /* Above three commands seem to relate to brightness. */ + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1}, + /* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */ + {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1}, + {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1}, + /* Above is brightness; OEM driver setting is 0x10 */ + {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1} + }; + + return run_start_commands(gspca_dev, vivitar_start_commands, + ARRAY_SIZE(vivitar_start_commands)); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err_code; + + sd->sof_read = 0; + + switch (sd->model) { + case 0x7005: + err_code = start_genius_cam(gspca_dev); + break; + case 0x8001: + err_code = start_spy_cam(gspca_dev); + break; + case 0x8003: + err_code = start_cif_cam(gspca_dev); + break; + case 0x8008: + err_code = start_ms350_cam(gspca_dev); + break; + case 0x800a: + err_code = start_vivitar_cam(gspca_dev); + break; + default: + pr_err("Starting unknown camera, please report this\n"); + return -ENXIO; + } + + return err_code; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int result; + __u8 data[6]; + + result = sn9c2028_read1(gspca_dev); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop read failed"); + + memset(data, 0, 6); + data[0] = 0x14; + result = sn9c2028_command(gspca_dev, data); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop command failed"); +} + +/* Include sn9c2028 sof detection functions */ +#include "sn9c2028.h" + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + unsigned char *sof; + + sof = sn9c2028_find_sof(gspca_dev, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sizeof sn9c2028_sof_marker) + n -= sizeof sn9c2028_sof_marker; + else + n = 0; + gspca_frame_add(gspca_dev, LAST_PACKET, data, n); + /* Start next frame. */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sn9c2028_sof_marker, sizeof sn9c2028_sof_marker); + len -= sof - data; + data = sof; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */ + /* The Genius Smart is untested. I can't find an owner ! */ + /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */ + {USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */ + {USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */ + /* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */ + {USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */ + {USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */ + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/sn9c2028.h b/drivers/media/usb/gspca/sn9c2028.h new file mode 100644 index 000000000000..8fd1d3e05665 --- /dev/null +++ b/drivers/media/usb/gspca/sn9c2028.h @@ -0,0 +1,51 @@ +/* + * SN9C2028 common functions + * + * Copyright (C) 2009 Theodore Kilgore + * + * Based closely upon the file gspca/pac_common.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static const unsigned char sn9c2028_sof_marker[5] = + { 0xff, 0xff, 0x00, 0xc4, 0xc4 }; + +static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev, + unsigned char *m, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + /* Search for the SOF marker (fixed part) in the header */ + for (i = 0; i < len; i++) { + if (m[i] == sn9c2028_sof_marker[sd->sof_read]) { + sd->sof_read++; + if (sd->sof_read == sizeof(sn9c2028_sof_marker)) { + PDEBUG(D_FRAM, + "SOF found, bytes to analyze: %u." + " Frame starts at byte #%u", + len, i + 1); + sd->sof_read = 0; + return m + i + 1; + } + } else { + sd->sof_read = 0; + } + } + + return NULL; +} diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c new file mode 100644 index 000000000000..b9c6f17eabb2 --- /dev/null +++ b/drivers/media/usb/gspca/sn9c20x.c @@ -0,0 +1,2428 @@ +/* + * Sonix sn9c201 sn9c202 library + * + * Copyright (C) 2012 Jean-Francois Moine + * Copyright (C) 2008-2009 microdia project + * Copyright (C) 2009 Brian Johnson + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include "gspca.h" +#include "jpeg.h" + +#include +#include + +MODULE_AUTHOR("Brian Johnson , " + "microdia project "); +MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* + * Pixel format private data + */ +#define SCALE_MASK 0x0f +#define SCALE_160x120 0 +#define SCALE_320x240 1 +#define SCALE_640x480 2 +#define SCALE_1280x1024 3 +#define MODE_RAW 0x10 +#define MODE_JPEG 0x20 +#define MODE_SXGA 0x80 + +#define SENSOR_OV9650 0 +#define SENSOR_OV9655 1 +#define SENSOR_SOI968 2 +#define SENSOR_OV7660 3 +#define SENSOR_OV7670 4 +#define SENSOR_MT9V011 5 +#define SENSOR_MT9V111 6 +#define SENSOR_MT9V112 7 +#define SENSOR_MT9M001 8 +#define SENSOR_MT9M111 9 +#define SENSOR_MT9M112 10 +#define SENSOR_HV7131R 11 +#define SENSOR_MT9VPRB 12 + +/* camera flags */ +#define HAS_NO_BUTTON 0x1 +#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ +#define FLIP_DETECT 0x4 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; + + struct { /* color control cluster */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + }; + struct { /* blue/red balance control cluster */ + struct v4l2_ctrl *blue; + struct v4l2_ctrl *red; + }; + struct { /* h/vflip control cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct v4l2_ctrl *gamma; + struct { /* autogain and exposure or gain control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *jpegqual; + + struct work_struct work; + struct workqueue_struct *work_thread; + + u32 pktsz; /* (used by pkt_scan) */ + u16 npkt; + s8 nchg; + u8 fmt; /* (used for JPEG QTAB update */ + +#define MIN_AVG_LUM 80 +#define MAX_AVG_LUM 130 + atomic_t avg_lum; + u8 old_step; + u8 older_step; + u8 exposure_step; + + u8 i2c_addr; + u8 i2c_intf; + u8 sensor; + u8 hstart; + u8 vstart; + + u8 jpeg_hdr[JPEG_HDR_SZ]; + + u8 flags; +}; + +static void qual_upd(struct work_struct *work); + +struct i2c_reg_u8 { + u8 reg; + u8 val; +}; + +struct i2c_reg_u16 { + u8 reg; + u16 val; +}; + +static const struct dmi_system_id flip_dmi_table[] = { + { + .ident = "MSI MS-1034", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0341") + } + }, + { + .ident = "MSI MS-1632", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1632") + } + }, + { + .ident = "MSI MS-1633X", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1633X") + } + }, + { + .ident = "MSI MS-1635X", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1635X") + } + }, + { + .ident = "ASUSTeK W7J", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "W7J ") + } + }, + {} +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = SCALE_160x120 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_160x120 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_160x120}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = SCALE_320x240 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_320x240 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_320x240}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = SCALE_640x480 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_640x480 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_640x480}, +}; + +static const struct v4l2_pix_format sxga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = SCALE_160x120 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_160x120 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_160x120}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = SCALE_320x240 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_320x240 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_320x240}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = SCALE_640x480 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_640x480 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_640x480}, + {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA}, +}; + +static const struct v4l2_pix_format mono_mode[] = { + {160, 120, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_160x120 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_320x240 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_640x480 | MODE_RAW}, + {1280, 1024, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA}, +}; + +static const s16 hsv_red_x[] = { + 41, 44, 46, 48, 50, 52, 54, 56, + 58, 60, 62, 64, 66, 68, 70, 72, + 74, 76, 78, 80, 81, 83, 85, 87, + 88, 90, 92, 93, 95, 97, 98, 100, + 101, 102, 104, 105, 107, 108, 109, 110, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 123, 124, 125, 125, + 126, 127, 127, 128, 128, 129, 129, 129, + 130, 130, 130, 130, 131, 131, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 128, 128, 127, 127, 126, + 125, 125, 124, 123, 122, 122, 121, 120, + 119, 118, 117, 116, 115, 114, 112, 111, + 110, 109, 107, 106, 105, 103, 102, 101, + 99, 98, 96, 94, 93, 91, 90, 88, + 86, 84, 83, 81, 79, 77, 75, 74, + 72, 70, 68, 66, 64, 62, 60, 58, + 56, 54, 52, 49, 47, 45, 43, 41, + 39, 36, 34, 32, 30, 28, 25, 23, + 21, 19, 16, 14, 12, 9, 7, 5, + 3, 0, -1, -3, -6, -8, -10, -12, + -15, -17, -19, -22, -24, -26, -28, -30, + -33, -35, -37, -39, -41, -44, -46, -48, + -50, -52, -54, -56, -58, -60, -62, -64, + -66, -68, -70, -72, -74, -76, -78, -80, + -81, -83, -85, -87, -88, -90, -92, -93, + -95, -97, -98, -100, -101, -102, -104, -105, + -107, -108, -109, -110, -112, -113, -114, -115, + -116, -117, -118, -119, -120, -121, -122, -123, + -123, -124, -125, -125, -126, -127, -127, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -127, -127, -126, -125, -125, -124, -123, + -122, -122, -121, -120, -119, -118, -117, -116, + -115, -114, -112, -111, -110, -109, -107, -106, + -105, -103, -102, -101, -99, -98, -96, -94, + -93, -91, -90, -88, -86, -84, -83, -81, + -79, -77, -75, -74, -72, -70, -68, -66, + -64, -62, -60, -58, -56, -54, -52, -49, + -47, -45, -43, -41, -39, -36, -34, -32, + -30, -28, -25, -23, -21, -19, -16, -14, + -12, -9, -7, -5, -3, 0, 1, 3, + 6, 8, 10, 12, 15, 17, 19, 22, + 24, 26, 28, 30, 33, 35, 37, 39, 41 +}; + +static const s16 hsv_red_y[] = { + 82, 80, 78, 76, 74, 73, 71, 69, + 67, 65, 63, 61, 58, 56, 54, 52, + 50, 48, 46, 44, 41, 39, 37, 35, + 32, 30, 28, 26, 23, 21, 19, 16, + 14, 12, 10, 7, 5, 3, 0, -1, + -3, -6, -8, -10, -13, -15, -17, -19, + -22, -24, -26, -29, -31, -33, -35, -38, + -40, -42, -44, -46, -48, -51, -53, -55, + -57, -59, -61, -63, -65, -67, -69, -71, + -73, -75, -77, -79, -81, -82, -84, -86, + -88, -89, -91, -93, -94, -96, -98, -99, + -101, -102, -104, -105, -106, -108, -109, -110, + -112, -113, -114, -115, -116, -117, -119, -120, + -120, -121, -122, -123, -124, -125, -126, -126, + -127, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -127, -127, -126, -125, -125, -124, -123, -122, + -121, -120, -119, -118, -117, -116, -115, -114, + -113, -111, -110, -109, -107, -106, -105, -103, + -102, -100, -99, -97, -96, -94, -92, -91, + -89, -87, -85, -84, -82, -80, -78, -76, + -74, -73, -71, -69, -67, -65, -63, -61, + -58, -56, -54, -52, -50, -48, -46, -44, + -41, -39, -37, -35, -32, -30, -28, -26, + -23, -21, -19, -16, -14, -12, -10, -7, + -5, -3, 0, 1, 3, 6, 8, 10, + 13, 15, 17, 19, 22, 24, 26, 29, + 31, 33, 35, 38, 40, 42, 44, 46, + 48, 51, 53, 55, 57, 59, 61, 63, + 65, 67, 69, 71, 73, 75, 77, 79, + 81, 82, 84, 86, 88, 89, 91, 93, + 94, 96, 98, 99, 101, 102, 104, 105, + 106, 108, 109, 110, 112, 113, 114, 115, + 116, 117, 119, 120, 120, 121, 122, 123, + 124, 125, 126, 126, 127, 128, 128, 129, + 129, 130, 130, 131, 131, 131, 131, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 131, 131, 131, 130, 130, + 130, 129, 129, 128, 127, 127, 126, 125, + 125, 124, 123, 122, 121, 120, 119, 118, + 117, 116, 115, 114, 113, 111, 110, 109, + 107, 106, 105, 103, 102, 100, 99, 97, + 96, 94, 92, 91, 89, 87, 85, 84, 82 +}; + +static const s16 hsv_green_x[] = { + -124, -124, -125, -125, -125, -125, -125, -125, + -125, -126, -126, -125, -125, -125, -125, -125, + -125, -124, -124, -124, -123, -123, -122, -122, + -121, -121, -120, -120, -119, -118, -117, -117, + -116, -115, -114, -113, -112, -111, -110, -109, + -108, -107, -105, -104, -103, -102, -100, -99, + -98, -96, -95, -93, -92, -91, -89, -87, + -86, -84, -83, -81, -79, -77, -76, -74, + -72, -70, -69, -67, -65, -63, -61, -59, + -57, -55, -53, -51, -49, -47, -45, -43, + -41, -39, -37, -35, -33, -30, -28, -26, + -24, -22, -20, -18, -15, -13, -11, -9, + -7, -4, -2, 0, 1, 3, 6, 8, + 10, 12, 14, 17, 19, 21, 23, 25, + 27, 29, 32, 34, 36, 38, 40, 42, + 44, 46, 48, 50, 52, 54, 56, 58, + 60, 62, 64, 66, 68, 70, 71, 73, + 75, 77, 78, 80, 82, 83, 85, 87, + 88, 90, 91, 93, 94, 96, 97, 98, + 100, 101, 102, 104, 105, 106, 107, 108, + 109, 111, 112, 113, 113, 114, 115, 116, + 117, 118, 118, 119, 120, 120, 121, 122, + 122, 123, 123, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 125, 126, 126, 125, + 125, 125, 125, 125, 125, 124, 124, 124, + 123, 123, 122, 122, 121, 121, 120, 120, + 119, 118, 117, 117, 116, 115, 114, 113, + 112, 111, 110, 109, 108, 107, 105, 104, + 103, 102, 100, 99, 98, 96, 95, 93, + 92, 91, 89, 87, 86, 84, 83, 81, + 79, 77, 76, 74, 72, 70, 69, 67, + 65, 63, 61, 59, 57, 55, 53, 51, + 49, 47, 45, 43, 41, 39, 37, 35, + 33, 30, 28, 26, 24, 22, 20, 18, + 15, 13, 11, 9, 7, 4, 2, 0, + -1, -3, -6, -8, -10, -12, -14, -17, + -19, -21, -23, -25, -27, -29, -32, -34, + -36, -38, -40, -42, -44, -46, -48, -50, + -52, -54, -56, -58, -60, -62, -64, -66, + -68, -70, -71, -73, -75, -77, -78, -80, + -82, -83, -85, -87, -88, -90, -91, -93, + -94, -96, -97, -98, -100, -101, -102, -104, + -105, -106, -107, -108, -109, -111, -112, -113, + -113, -114, -115, -116, -117, -118, -118, -119, + -120, -120, -121, -122, -122, -123, -123, -124, -124 +}; + +static const s16 hsv_green_y[] = { + -100, -99, -98, -97, -95, -94, -93, -91, + -90, -89, -87, -86, -84, -83, -81, -80, + -78, -76, -75, -73, -71, -70, -68, -66, + -64, -63, -61, -59, -57, -55, -53, -51, + -49, -48, -46, -44, -42, -40, -38, -36, + -34, -32, -30, -27, -25, -23, -21, -19, + -17, -15, -13, -11, -9, -7, -4, -2, + 0, 1, 3, 5, 7, 9, 11, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 59, 61, + 63, 65, 67, 68, 70, 72, 74, 75, + 77, 78, 80, 82, 83, 85, 86, 88, + 89, 90, 92, 93, 95, 96, 97, 98, + 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 112, 113, 114, + 115, 115, 116, 116, 117, 117, 118, 118, + 119, 119, 119, 120, 120, 120, 120, 120, + 121, 121, 121, 121, 121, 121, 120, 120, + 120, 120, 120, 119, 119, 119, 118, 118, + 117, 117, 116, 116, 115, 114, 114, 113, + 112, 111, 111, 110, 109, 108, 107, 106, + 105, 104, 103, 102, 100, 99, 98, 97, + 95, 94, 93, 91, 90, 89, 87, 86, + 84, 83, 81, 80, 78, 76, 75, 73, + 71, 70, 68, 66, 64, 63, 61, 59, + 57, 55, 53, 51, 49, 48, 46, 44, + 42, 40, 38, 36, 34, 32, 30, 27, + 25, 23, 21, 19, 17, 15, 13, 11, + 9, 7, 4, 2, 0, -1, -3, -5, + -7, -9, -11, -14, -16, -18, -20, -22, + -24, -26, -28, -30, -32, -34, -36, -38, + -40, -42, -44, -46, -48, -50, -52, -54, + -56, -58, -59, -61, -63, -65, -67, -68, + -70, -72, -74, -75, -77, -78, -80, -82, + -83, -85, -86, -88, -89, -90, -92, -93, + -95, -96, -97, -98, -100, -101, -102, -103, + -104, -105, -106, -107, -108, -109, -110, -111, + -112, -112, -113, -114, -115, -115, -116, -116, + -117, -117, -118, -118, -119, -119, -119, -120, + -120, -120, -120, -120, -121, -121, -121, -121, + -121, -121, -120, -120, -120, -120, -120, -119, + -119, -119, -118, -118, -117, -117, -116, -116, + -115, -114, -114, -113, -112, -111, -111, -110, + -109, -108, -107, -106, -105, -104, -103, -102, -100 +}; + +static const s16 hsv_blue_x[] = { + 112, 113, 114, 114, 115, 116, 117, 117, + 118, 118, 119, 119, 120, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 121, 121, + 121, 120, 120, 120, 119, 119, 118, 118, + 117, 116, 116, 115, 114, 113, 113, 112, + 111, 110, 109, 108, 107, 106, 105, 104, + 103, 102, 100, 99, 98, 97, 95, 94, + 93, 91, 90, 88, 87, 85, 84, 82, + 80, 79, 77, 76, 74, 72, 70, 69, + 67, 65, 63, 61, 60, 58, 56, 54, + 52, 50, 48, 46, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 19, 17, 15, 13, 11, 9, 7, 5, + 2, 0, -1, -3, -5, -7, -9, -12, + -14, -16, -18, -20, -22, -24, -26, -28, + -31, -33, -35, -37, -39, -41, -43, -45, + -47, -49, -51, -53, -54, -56, -58, -60, + -62, -64, -66, -67, -69, -71, -73, -74, + -76, -78, -79, -81, -83, -84, -86, -87, + -89, -90, -92, -93, -94, -96, -97, -98, + -99, -101, -102, -103, -104, -105, -106, -107, + -108, -109, -110, -111, -112, -113, -114, -114, + -115, -116, -117, -117, -118, -118, -119, -119, + -120, -120, -120, -121, -121, -121, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -121, -121, -121, -120, -120, -120, + -119, -119, -118, -118, -117, -116, -116, -115, + -114, -113, -113, -112, -111, -110, -109, -108, + -107, -106, -105, -104, -103, -102, -100, -99, + -98, -97, -95, -94, -93, -91, -90, -88, + -87, -85, -84, -82, -80, -79, -77, -76, + -74, -72, -70, -69, -67, -65, -63, -61, + -60, -58, -56, -54, -52, -50, -48, -46, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -19, -17, -15, -13, + -11, -9, -7, -5, -2, 0, 1, 3, + 5, 7, 9, 12, 14, 16, 18, 20, + 22, 24, 26, 28, 31, 33, 35, 37, + 39, 41, 43, 45, 47, 49, 51, 53, + 54, 56, 58, 60, 62, 64, 66, 67, + 69, 71, 73, 74, 76, 78, 79, 81, + 83, 84, 86, 87, 89, 90, 92, 93, + 94, 96, 97, 98, 99, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112 +}; + +static const s16 hsv_blue_y[] = { + -11, -13, -15, -17, -19, -21, -23, -25, + -27, -29, -31, -33, -35, -37, -39, -41, + -43, -45, -46, -48, -50, -52, -54, -55, + -57, -59, -61, -62, -64, -66, -67, -69, + -71, -72, -74, -75, -77, -78, -80, -81, + -83, -84, -86, -87, -88, -90, -91, -92, + -93, -95, -96, -97, -98, -99, -100, -101, + -102, -103, -104, -105, -106, -106, -107, -108, + -109, -109, -110, -111, -111, -112, -112, -113, + -113, -114, -114, -114, -115, -115, -115, -115, + -116, -116, -116, -116, -116, -116, -116, -116, + -116, -115, -115, -115, -115, -114, -114, -114, + -113, -113, -112, -112, -111, -111, -110, -110, + -109, -108, -108, -107, -106, -105, -104, -103, + -102, -101, -100, -99, -98, -97, -96, -95, + -94, -93, -91, -90, -89, -88, -86, -85, + -84, -82, -81, -79, -78, -76, -75, -73, + -71, -70, -68, -67, -65, -63, -62, -60, + -58, -56, -55, -53, -51, -49, -47, -45, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -20, -18, -16, -14, + -12, -10, -8, -6, -4, -2, 0, 1, + 3, 5, 7, 9, 11, 13, 15, 17, + 19, 21, 23, 25, 27, 29, 31, 33, + 35, 37, 39, 41, 43, 45, 46, 48, + 50, 52, 54, 55, 57, 59, 61, 62, + 64, 66, 67, 69, 71, 72, 74, 75, + 77, 78, 80, 81, 83, 84, 86, 87, + 88, 90, 91, 92, 93, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 106, 107, 108, 109, 109, 110, 111, + 111, 112, 112, 113, 113, 114, 114, 114, + 115, 115, 115, 115, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 115, 115, 115, + 115, 114, 114, 114, 113, 113, 112, 112, + 111, 111, 110, 110, 109, 108, 108, 107, + 106, 105, 104, 103, 102, 101, 100, 99, + 98, 97, 96, 95, 94, 93, 91, 90, + 89, 88, 86, 85, 84, 82, 81, 79, + 78, 76, 75, 73, 71, 70, 68, 67, + 65, 63, 62, 60, 58, 56, 55, 53, + 51, 49, 47, 45, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 20, 18, 16, 14, 12, 10, 8, 6, + 4, 2, 0, -1, -3, -5, -7, -9, -11 +}; + +static const u16 i2c_ident[] = { + V4L2_IDENT_OV9650, + V4L2_IDENT_OV9655, + V4L2_IDENT_SOI968, + V4L2_IDENT_OV7660, + V4L2_IDENT_OV7670, + V4L2_IDENT_MT9V011, + V4L2_IDENT_MT9V111, + V4L2_IDENT_MT9V112, + V4L2_IDENT_MT9M001C12ST, + V4L2_IDENT_MT9M111, + V4L2_IDENT_MT9M112, + V4L2_IDENT_HV7131R, +[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN, +}; + +static const u16 bridge_init[][2] = { + {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, + {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, + {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, + {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00}, + {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50}, + {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50}, + {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04}, + {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05}, + {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00}, + {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d}, + {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04}, + {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8}, + {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32}, + {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd}, + {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01}, + {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f}, + {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f}, + {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01}, + {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80}, + {0x1007, 0x00} +}; + +/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ +static const u8 ov_gain[] = { + 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, + 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, + 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, + 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */, + 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */, + 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */, + 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */, + 0x70 /* 8x */ +}; + +/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ +static const u16 micron1_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0020, 0x0028, 0x0030, 0x0038, + /* 2x 2.25x 2.5x 2.75x */ + 0x00a0, 0x00a4, 0x00a8, 0x00ac, + /* 3x 3.25x 3.5x 3.75x */ + 0x00b0, 0x00b4, 0x00b8, 0x00bc, + /* 4x 4.25x 4.5x 4.75x */ + 0x00c0, 0x00c4, 0x00c8, 0x00cc, + /* 5x 5.25x 5.5x 5.75x */ + 0x00d0, 0x00d4, 0x00d8, 0x00dc, + /* 6x 6.25x 6.5x 6.75x */ + 0x00e0, 0x00e4, 0x00e8, 0x00ec, + /* 7x 7.25x 7.5x 7.75x */ + 0x00f0, 0x00f4, 0x00f8, 0x00fc, + /* 8x */ + 0x01c0 +}; + +/* mt9m001 sensor uses a different gain formula then other micron sensors */ +/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ +static const u16 micron2_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0008, 0x000a, 0x000c, 0x000e, + /* 2x 2.25x 2.5x 2.75x */ + 0x0010, 0x0012, 0x0014, 0x0016, + /* 3x 3.25x 3.5x 3.75x */ + 0x0018, 0x001a, 0x001c, 0x001e, + /* 4x 4.25x 4.5x 4.75x */ + 0x0020, 0x0051, 0x0052, 0x0053, + /* 5x 5.25x 5.5x 5.75x */ + 0x0054, 0x0055, 0x0056, 0x0057, + /* 6x 6.25x 6.5x 6.75x */ + 0x0058, 0x0059, 0x005a, 0x005b, + /* 7x 7.25x 7.5x 7.75x */ + 0x005c, 0x005d, 0x005e, 0x005f, + /* 8x */ + 0x0060 +}; + +/* Gain = .5 + bit[7:0] / 16 */ +static const u8 hv7131r_gain[] = { + 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, + 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, + 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, + 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */, + 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */, + 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */, + 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */, + 0x78 /* 8x */ +}; + +static const struct i2c_reg_u8 soi968_init[] = { + {0x0c, 0x00}, {0x0f, 0x1f}, + {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, + {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, + {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, + {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20}, + {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e}, + {0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13}, + {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79}, + {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40}, + {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32}, + {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, +}; + +static const struct i2c_reg_u8 ov7660_init[] = { + {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, + {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, + {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, + /* HDG Set hstart and hstop, datasheet default 0x11, 0x61, using + 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */ + {0x17, 0x10}, {0x18, 0x61}, + {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, + {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0x00}, + {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50}, +}; + +static const struct i2c_reg_u8 ov7670_init[] = { + {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, + {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, + {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, + {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00}, + {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07}, + {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75}, + {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8}, + {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5}, + {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27}, + {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b}, + {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a}, + {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00}, + {0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00}, + {0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80}, + {0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82}, + {0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20}, + {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c}, + {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66}, + {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11}, + {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40}, + {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02}, + {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a}, + {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08}, + {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04}, + {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30}, + {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88}, + {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30}, + {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99}, + {0x9e, 0x7f}, {0x78, 0x04}, {0x79, 0x01}, {0xc8, 0xf0}, + {0x79, 0x0f}, {0xc8, 0x00}, {0x79, 0x10}, {0xc8, 0x7e}, + {0x79, 0x0a}, {0xc8, 0x80}, {0x79, 0x0b}, {0xc8, 0x01}, + {0x79, 0x0c}, {0xc8, 0x0f}, {0x79, 0x0d}, {0xc8, 0x20}, + {0x79, 0x09}, {0xc8, 0x80}, {0x79, 0x02}, {0xc8, 0xc0}, + {0x79, 0x03}, {0xc8, 0x40}, {0x79, 0x05}, {0xc8, 0x30}, + {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06}, + {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a}, + {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a}, + {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84}, + {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d}, + {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d}, + {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00}, + {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60}, + {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, + {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e}, + {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56}, + {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03}, + {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47}, + {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74}, + {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2}, + {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00}, + {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a}, + {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00}, + {0x93, 0x00}, +}; + +static const struct i2c_reg_u8 ov9650_init[] = { + {0x00, 0x00}, {0x01, 0x78}, + {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, + {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, + {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00}, + {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c}, + {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2}, + {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07}, + {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00}, + {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04}, + {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68}, + {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80}, + {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00}, + {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30}, + {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf}, + {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00}, + {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01}, + {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19}, + {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1}, + {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80}, + {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00}, + {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20}, + {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf}, + {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88}, + {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00}, + {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8}, + {0xaa, 0x92}, {0xab, 0x0a}, +}; + +static const struct i2c_reg_u8 ov9655_init[] = { + {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, + {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, + {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, + {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57}, + {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19}, + {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80}, + {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c}, + {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc}, + {0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05}, + {0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e}, + {0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, + {0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, + {0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, + {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, + {0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, + {0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01}, + {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0}, + {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00}, + {0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61}, + {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a}, + {0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01}, + {0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a}, + {0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c}, + {0x04, 0x03}, {0x00, 0x13}, +}; + +static const struct i2c_reg_u16 mt9v112_init[] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, + {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, + {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, + {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a}, + {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58}, + {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001}, + {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020}, + {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020}, + {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2}, + {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae}, + {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, +}; + +static const struct i2c_reg_u16 mt9v111_init[] = { + {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0}, + {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e}, + {0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016}, + {0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004}, + {0x06, 0x002d}, {0x07, 0x3002}, {0x08, 0x0008}, + {0x0e, 0x0008}, {0x20, 0x0000} +}; + +static const struct i2c_reg_u16 mt9v011_init[] = { + {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, + {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, + {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000}, + {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000}, + {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000}, + {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000}, + {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000}, + {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000}, + {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000}, + {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000}, + {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000}, + {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024}, + {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000}, + {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100}, + {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1}, + {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000}, + {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000}, + {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101}, + {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003}, + {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, +}; + +static const struct i2c_reg_u16 mt9m001_init[] = { + {0x0d, 0x0001}, + {0x0d, 0x0000}, + {0x04, 0x0500}, /* hres = 1280 */ + {0x03, 0x0400}, /* vres = 1024 */ + {0x20, 0x1100}, + {0x06, 0x0010}, + {0x2b, 0x0024}, + {0x2e, 0x0024}, + {0x35, 0x0024}, + {0x2d, 0x0020}, + {0x2c, 0x0020}, + {0x09, 0x0ad4}, + {0x35, 0x0057}, +}; + +static const struct i2c_reg_u16 mt9m111_init[] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, + {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, + {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, + {0xf0, 0x0000}, +}; + +static const struct i2c_reg_u16 mt9m112_init[] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, + {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, + {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, + {0xf0, 0x0000}, +}; + +static const struct i2c_reg_u8 hv7131r_init[] = { + {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, + {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, + {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, + {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07}, + {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62}, + {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10}, + {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, + {0x23, 0x09}, {0x01, 0x08}, +}; + +static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + + if (gspca_dev->usb_err < 0) + return; + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + pr_err("Read register %02x failed %d\n", reg, result); + gspca_dev->usb_err = result; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 reg, + const u8 *buffer, int length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + + if (gspca_dev->usb_err < 0) + return; + memcpy(gspca_dev->usb_buf, buffer, length); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + pr_err("Write register %02x failed %d\n", reg, result); + gspca_dev->usb_err = result; + } +} + +static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +{ + reg_w(gspca_dev, reg, &value, 1); +} + +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +{ + int i; + + reg_w(gspca_dev, 0x10c0, buffer, 8); + for (i = 0; i < 5; i++) { + reg_r(gspca_dev, 0x10c0, 1); + if (gspca_dev->usb_err < 0) + return; + if (gspca_dev->usb_buf[0] & 0x04) { + if (gspca_dev->usb_buf[0] & 0x08) { + pr_err("i2c_w error\n"); + gspca_dev->usb_err = -EIO; + } + return; + } + msleep(10); + } + pr_err("i2c_w reg %02x no response\n", buffer[2]); +/* gspca_dev->usb_err = -EIO; fixme: may occur */ +} + +static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = sd->i2c_intf | (2 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = val; + row[4] = 0x00; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + i2c_w(gspca_dev, row); +} + +static void i2c_w1_buf(struct gspca_dev *gspca_dev, + const struct i2c_reg_u8 *buf, int sz) +{ + while (--sz >= 0) { + i2c_w1(gspca_dev, buf->reg, buf->val); + buf++; + } +} + +static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = sd->i2c_intf | (3 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = val >> 8; + row[4] = val; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + i2c_w(gspca_dev, row); +} + +static void i2c_w2_buf(struct gspca_dev *gspca_dev, + const struct i2c_reg_u16 *buf, int sz) +{ + while (--sz >= 0) { + i2c_w2(gspca_dev, buf->reg, buf->val); + buf++; + } +} + +static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = sd->i2c_intf | (1 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + i2c_w(gspca_dev, row); + row[0] = sd->i2c_intf | (1 << 4) | 0x02; + row[2] = 0; + i2c_w(gspca_dev, row); + reg_r(gspca_dev, 0x10c2, 5); + *val = gspca_dev->usb_buf[4]; +} + +static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = sd->i2c_intf | (1 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + i2c_w(gspca_dev, row); + row[0] = sd->i2c_intf | (2 << 4) | 0x02; + row[2] = 0; + i2c_w(gspca_dev, row); + reg_r(gspca_dev, 0x10c2, 5); + *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; +} + +static void ov9650_init_sensor(struct gspca_dev *gspca_dev) +{ + u16 id; + struct sd *sd = (struct sd *) gspca_dev; + + i2c_r2(gspca_dev, 0x1c, &id); + if (gspca_dev->usb_err < 0) + return; + + if (id != 0x7fa2) { + pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id); + gspca_dev->usb_err = -ENODEV; + return; + } + + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV9650 sensor initialization failed\n"); + sd->hstart = 1; + sd->vstart = 7; +} + +static void ov9655_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV9655 sensor initialization failed\n"); + + sd->hstart = 1; + sd->vstart = 2; +} + +static void soi968_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init)); + if (gspca_dev->usb_err < 0) + pr_err("SOI968 sensor initialization failed\n"); + + sd->hstart = 60; + sd->vstart = 11; +} + +static void ov7660_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV7660 sensor initialization failed\n"); + sd->hstart = 3; + sd->vstart = 3; +} + +static void ov7670_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV7670 sensor initialization failed\n"); + + sd->hstart = 0; + sd->vstart = 1; +} + +static void mt9v_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 value; + + sd->i2c_addr = 0x5d; + i2c_r2(gspca_dev, 0xff, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x8243) { + i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V011 sensor initialization failed\n"); + return; + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V011; + pr_info("MT9V011 sensor detected\n"); + return; + } + + gspca_dev->usb_err = 0; + sd->i2c_addr = 0x5c; + i2c_w2(gspca_dev, 0x01, 0x0004); + i2c_r2(gspca_dev, 0xff, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x823a) { + i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V111 sensor initialization failed\n"); + return; + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V111; + pr_info("MT9V111 sensor detected\n"); + return; + } + + gspca_dev->usb_err = 0; + sd->i2c_addr = 0x5d; + i2c_w2(gspca_dev, 0xf0, 0x0000); + if (gspca_dev->usb_err < 0) { + gspca_dev->usb_err = 0; + sd->i2c_addr = 0x48; + i2c_w2(gspca_dev, 0xf0, 0x0000); + } + i2c_r2(gspca_dev, 0x00, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x1229) { + i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V112 sensor initialization failed\n"); + return; + } + sd->hstart = 6; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V112; + pr_info("MT9V112 sensor detected\n"); + return; + } + + gspca_dev->usb_err = -ENODEV; +} + +static void mt9m112_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M112 sensor initialization failed\n"); + + sd->hstart = 0; + sd->vstart = 2; +} + +static void mt9m111_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M111 sensor initialization failed\n"); + + sd->hstart = 0; + sd->vstart = 2; +} + +static void mt9m001_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 id; + + i2c_r2(gspca_dev, 0x00, &id); + if (gspca_dev->usb_err < 0) + return; + + /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ + switch (id) { + case 0x8411: + case 0x8421: + pr_info("MT9M001 color sensor detected\n"); + break; + case 0x8431: + pr_info("MT9M001 mono sensor detected\n"); + break; + default: + pr_err("No MT9M001 chip detected, ID = %x\n\n", id); + gspca_dev->usb_err = -ENODEV; + return; + } + + i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M001 sensor initialization failed\n"); + + sd->hstart = 1; + sd->vstart = 1; +} + +static void hv7131r_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init)); + if (gspca_dev->usb_err < 0) + pr_err("HV7131R Sensor initialization failed\n"); + + sd->hstart = 0; + sd->vstart = 1; +} + +static void set_cmatrix(struct gspca_dev *gspca_dev, + s32 brightness, s32 contrast, s32 satur, s32 hue) +{ + s32 hue_coord, hue_index = 180 + hue; + u8 cmatrix[21]; + + memset(cmatrix, 0, sizeof cmatrix); + cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26; + cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; + cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; + cmatrix[18] = brightness - 0x80; + + hue_coord = (hsv_red_x[hue_index] * satur) >> 8; + cmatrix[6] = hue_coord; + cmatrix[7] = (hue_coord >> 8) & 0x0f; + + hue_coord = (hsv_red_y[hue_index] * satur) >> 8; + cmatrix[8] = hue_coord; + cmatrix[9] = (hue_coord >> 8) & 0x0f; + + hue_coord = (hsv_green_x[hue_index] * satur) >> 8; + cmatrix[10] = hue_coord; + cmatrix[11] = (hue_coord >> 8) & 0x0f; + + hue_coord = (hsv_green_y[hue_index] * satur) >> 8; + cmatrix[12] = hue_coord; + cmatrix[13] = (hue_coord >> 8) & 0x0f; + + hue_coord = (hsv_blue_x[hue_index] * satur) >> 8; + cmatrix[14] = hue_coord; + cmatrix[15] = (hue_coord >> 8) & 0x0f; + + hue_coord = (hsv_blue_y[hue_index] * satur) >> 8; + cmatrix[16] = hue_coord; + cmatrix[17] = (hue_coord >> 8) & 0x0f; + + reg_w(gspca_dev, 0x10e1, cmatrix, 21); +} + +static void set_gamma(struct gspca_dev *gspca_dev, s32 val) +{ + u8 gamma[17]; + u8 gval = val * 0xb8 / 0x100; + + gamma[0] = 0x0a; + gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); + gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8); + gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8); + gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8); + gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8); + gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8); + gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8); + gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8); + gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8); + gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8); + gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8); + gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8); + gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8); + gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8); + gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8); + gamma[16] = 0xf5; + + reg_w(gspca_dev, 0x1190, gamma, 17); +} + +static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red) +{ + reg_w1(gspca_dev, 0x118c, red); + reg_w1(gspca_dev, 0x118f, blue); +} + +static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) +{ + u8 value, tslb; + u16 value2; + struct sd *sd = (struct sd *) gspca_dev; + + if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { + hflip = !hflip; + vflip = !vflip; + } + + switch (sd->sensor) { + case SENSOR_OV7660: + value = 0x01; + if (hflip) + value |= 0x20; + if (vflip) { + value |= 0x10; + sd->vstart = 2; + } else { + sd->vstart = 3; + } + reg_w1(gspca_dev, 0x1182, sd->vstart); + i2c_w1(gspca_dev, 0x1e, value); + break; + case SENSOR_OV9650: + i2c_r1(gspca_dev, 0x1e, &value); + value &= ~0x30; + tslb = 0x01; + if (hflip) + value |= 0x20; + if (vflip) { + value |= 0x10; + tslb = 0x49; + } + i2c_w1(gspca_dev, 0x1e, value); + i2c_w1(gspca_dev, 0x3a, tslb); + break; + case SENSOR_MT9V111: + case SENSOR_MT9V011: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0xc0a0; + if (hflip) + value2 |= 0x8080; + if (vflip) + value2 |= 0x4020; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_MT9M112: + case SENSOR_MT9M111: + case SENSOR_MT9V112: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0x0003; + if (hflip) + value2 |= 0x0002; + if (vflip) + value2 |= 0x0001; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_HV7131R: + i2c_r1(gspca_dev, 0x01, &value); + value &= ~0x03; + if (vflip) + value |= 0x01; + if (hflip) + value |= 0x02; + i2c_w1(gspca_dev, 0x01, value); + break; + } +} + +static void set_exposure(struct gspca_dev *gspca_dev, s32 expo) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + int expo2; + + if (gspca_dev->streaming) + exp[7] = 0x1e; + + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_OV9655: + case SENSOR_OV9650: + if (expo > 547) + expo2 = 547; + else + expo2 = expo; + exp[0] |= (2 << 4); + exp[2] = 0x10; /* AECH */ + exp[3] = expo2 >> 2; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + exp[2] = 0x04; /* COM1 */ + exp[3] = expo2 & 0x0003; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + expo -= expo2; + exp[7] = 0x1e; + exp[0] |= (3 << 4); + exp[2] = 0x2d; /* ADVFL & ADVFH */ + exp[3] = expo; + exp[4] = expo >> 8; + break; + case SENSOR_MT9M001: + case SENSOR_MT9V112: + case SENSOR_MT9V011: + exp[0] |= (3 << 4); + exp[2] = 0x09; + exp[3] = expo >> 8; + exp[4] = expo; + break; + case SENSOR_HV7131R: + exp[0] |= (4 << 4); + exp[2] = 0x25; + exp[3] = expo >> 5; + exp[4] = expo << 3; + exp[5] = 0; + break; + default: + return; + } + i2c_w(gspca_dev, exp); +} + +static void set_gain(struct gspca_dev *gspca_dev, s32 g) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gain[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + + if (gspca_dev->streaming) + gain[7] = 0x15; /* or 1d ? */ + + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_SOI968: + case SENSOR_OV9655: + case SENSOR_OV9650: + gain[0] |= (2 << 4); + gain[3] = ov_gain[g]; + break; + case SENSOR_MT9V011: + gain[0] |= (3 << 4); + gain[2] = 0x35; + gain[3] = micron1_gain[g] >> 8; + gain[4] = micron1_gain[g]; + break; + case SENSOR_MT9V112: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron1_gain[g] >> 8; + gain[4] = micron1_gain[g]; + break; + case SENSOR_MT9M001: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron2_gain[g] >> 8; + gain[4] = micron2_gain[g]; + break; + case SENSOR_HV7131R: + gain[0] |= (2 << 4); + gain[2] = 0x30; + gain[3] = hv7131r_gain[g]; + break; + default: + return; + } + i2c_w(gspca_dev, gain); +} + +static void set_quality(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + jpeg_set_qual(sd->jpeg_hdr, val); + reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */ + reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */ + reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); + reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); + reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */ + reg_w1(gspca_dev, 0x10e0, sd->fmt); + sd->fmt ^= 0x0c; /* invert QTAB use + write */ + reg_w1(gspca_dev, 0x10e0, sd->fmt); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_g_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + reg_r(gspca_dev, reg->reg, 1); + reg->val = gspca_dev->usb_buf[0]; + return gspca_dev->usb_err; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M112) { + i2c_r2(gspca_dev, reg->reg, (u16 *) ®->val); + } else { + i2c_r1(gspca_dev, reg->reg, (u8 *) ®->val); + } + return gspca_dev->usb_err; + } + return -EINVAL; +} + +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + reg_w1(gspca_dev, reg->reg, reg->val); + return gspca_dev->usb_err; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M112) { + i2c_w2(gspca_dev, reg->reg, reg->val); + } else { + i2c_w1(gspca_dev, reg->reg, reg->val); + } + return gspca_dev->usb_err; + } + return -EINVAL; +} +#endif + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (chip->match.addr != 0) + return -EINVAL; + chip->revision = 0; + chip->ident = V4L2_IDENT_SN9C20X; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (chip->match.addr != sd->i2c_addr) + return -EINVAL; + chip->revision = 0; + chip->ident = i2c_ident[sd->sensor]; + return 0; + } + return -EINVAL; +} + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->needs_full_bandwidth = 1; + + sd->sensor = id->driver_info >> 8; + sd->i2c_addr = id->driver_info; + sd->flags = id->driver_info >> 16; + sd->i2c_intf = 0x80; /* i2c 100 Kb/s */ + + switch (sd->sensor) { + case SENSOR_MT9M112: + case SENSOR_MT9M111: + case SENSOR_OV9650: + case SENSOR_SOI968: + cam->cam_mode = sxga_mode; + cam->nmodes = ARRAY_SIZE(sxga_mode); + break; + case SENSOR_MT9M001: + cam->cam_mode = mono_mode; + cam->nmodes = ARRAY_SIZE(mono_mode); + break; + case SENSOR_HV7131R: + sd->i2c_intf = 0x81; /* i2c 400 Kb/s */ + /* fall thru */ + default: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + break; + } + + sd->old_step = 0; + sd->older_step = 0; + sd->exposure_step = 16; + + INIT_WORK(&sd->work, qual_upd); + + return 0; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* color control cluster */ + case V4L2_CID_BRIGHTNESS: + set_cmatrix(gspca_dev, sd->brightness->val, + sd->contrast->val, sd->saturation->val, sd->hue->val); + break; + case V4L2_CID_GAMMA: + set_gamma(gspca_dev, ctrl->val); + break; + /* blue/red balance cluster */ + case V4L2_CID_BLUE_BALANCE: + set_redblue(gspca_dev, sd->blue->val, sd->red->val); + break; + /* h/vflip cluster */ + case V4L2_CID_HFLIP: + set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val); + break; + /* standalone exposure control */ + case V4L2_CID_EXPOSURE: + set_exposure(gspca_dev, ctrl->val); + break; + /* standalone gain control */ + case V4L2_CID_GAIN: + set_gain(gspca_dev, ctrl->val); + break; + /* autogain + exposure or gain control cluster */ + case V4L2_CID_AUTOGAIN: + if (sd->sensor == SENSOR_SOI968) + set_gain(gspca_dev, sd->gain->val); + else + set_exposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + set_quality(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 13); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 127); + sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, -180, 180, 1, 0); + + sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 0x10); + + sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28); + sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28); + + if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 && + sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 && + sd->sensor != SENSOR_MT9VPRB) { + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } + + if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB && + sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 && + sd->sensor != SENSOR_MT9V111) + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33); + + if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 && + sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) { + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 28, 1, 0); + sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + } + + sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_cluster(4, &sd->brightness); + v4l2_ctrl_cluster(2, &sd->blue); + if (sd->hflip) + v4l2_ctrl_cluster(2, &sd->hflip); + if (sd->autogain) { + if (sd->sensor == SENSOR_SOI968) + /* this sensor doesn't have the exposure control and + autogain is clustered with gain instead. This works + because sd->exposure == NULL. */ + v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false); + else + /* Otherwise autogain is clustered with exposure. */ + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); + } + return 0; +} + +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u8 value; + u8 i2c_init[9] = + {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + + for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { + value = bridge_init[i][1]; + reg_w(gspca_dev, bridge_init[i][0], &value, 1); + if (gspca_dev->usb_err < 0) { + pr_err("Device initialization failed\n"); + return gspca_dev->usb_err; + } + } + + if (sd->flags & LED_REVERSE) + reg_w1(gspca_dev, 0x1006, 0x00); + else + reg_w1(gspca_dev, 0x1006, 0x20); + + reg_w(gspca_dev, 0x10c0, i2c_init, 9); + if (gspca_dev->usb_err < 0) { + pr_err("Device initialization failed\n"); + return gspca_dev->usb_err; + } + + switch (sd->sensor) { + case SENSOR_OV9650: + ov9650_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("OV9650 sensor detected\n"); + break; + case SENSOR_OV9655: + ov9655_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("OV9655 sensor detected\n"); + break; + case SENSOR_SOI968: + soi968_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("SOI968 sensor detected\n"); + break; + case SENSOR_OV7660: + ov7660_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("OV7660 sensor detected\n"); + break; + case SENSOR_OV7670: + ov7670_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("OV7670 sensor detected\n"); + break; + case SENSOR_MT9VPRB: + mt9v_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("MT9VPRB sensor detected\n"); + break; + case SENSOR_MT9M111: + mt9m111_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("MT9M111 sensor detected\n"); + break; + case SENSOR_MT9M112: + mt9m112_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("MT9M112 sensor detected\n"); + break; + case SENSOR_MT9M001: + mt9m001_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + break; + case SENSOR_HV7131R: + hv7131r_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("HV7131R sensor detected\n"); + break; + default: + pr_err("Unsupported sensor\n"); + gspca_dev->usb_err = -ENODEV; + } + return gspca_dev->usb_err; +} + +static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 value; + + switch (sd->sensor) { + case SENSOR_SOI968: + if (mode & MODE_SXGA) { + i2c_w1(gspca_dev, 0x17, 0x1d); + i2c_w1(gspca_dev, 0x18, 0xbd); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x81); + i2c_w1(gspca_dev, 0x12, 0x00); + sd->hstart = 140; + sd->vstart = 19; + } else { + i2c_w1(gspca_dev, 0x17, 0x13); + i2c_w1(gspca_dev, 0x18, 0x63); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x79); + i2c_w1(gspca_dev, 0x12, 0x40); + sd->hstart = 60; + sd->vstart = 11; + } + break; + case SENSOR_OV9650: + if (mode & MODE_SXGA) { + i2c_w1(gspca_dev, 0x17, 0x1b); + i2c_w1(gspca_dev, 0x18, 0xbc); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x82); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, value & 0x07); + } else { + i2c_w1(gspca_dev, 0x17, 0x24); + i2c_w1(gspca_dev, 0x18, 0xc5); + i2c_w1(gspca_dev, 0x19, 0x00); + i2c_w1(gspca_dev, 0x1a, 0x3c); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); + } + break; + case SENSOR_MT9M112: + case SENSOR_MT9M111: + if (mode & MODE_SXGA) { + i2c_w2(gspca_dev, 0xf0, 0x0002); + i2c_w2(gspca_dev, 0xc8, 0x970b); + i2c_w2(gspca_dev, 0xf0, 0x0000); + } else { + i2c_w2(gspca_dev, 0xf0, 0x0002); + i2c_w2(gspca_dev, 0xc8, 0x8000); + i2c_w2(gspca_dev, 0xf0, 0x0000); + } + break; + } +} + +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct usb_interface *intf; + u32 flags = gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv; + + /* + * When using the SN9C20X_I420 fmt the sn9c20x needs more bandwidth + * than our regular bandwidth calculations reserve, so we force the + * use of a specific altsetting when using the SN9C20X_I420 fmt. + */ + if (!(flags & (MODE_RAW | MODE_JPEG))) { + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + + if (intf->num_altsetting != 9) { + pr_warn("sn9c20x camera with unknown number of alt " + "settings (%d), please report!\n", + intf->num_altsetting); + gspca_dev->alt = intf->num_altsetting; + return 0; + } + + switch (gspca_dev->width) { + case 160: /* 160x120 */ + gspca_dev->alt = 2; + break; + case 320: /* 320x240 */ + gspca_dev->alt = 6; + break; + default: /* >= 640x480 */ + gspca_dev->alt = 9; + break; + } + } + + return 0; +} + +#define HW_WIN(mode, hstart, vstart) \ +((const u8 []){hstart, 0, vstart, 0, \ +(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \ +(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)}) + +#define CLR_WIN(width, height) \ +((const u8 [])\ +{0, width >> 2, 0, height >> 1,\ +((width >> 10) & 0x01) | ((height >> 8) & 0x6)}) + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + int width = gspca_dev->width; + int height = gspca_dev->height; + u8 fmt, scale = 0; + + jpeg_define(sd->jpeg_hdr, height, width, + 0x21); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); + + if (mode & MODE_RAW) + fmt = 0x2d; + else if (mode & MODE_JPEG) + fmt = 0x24; + else + fmt = 0x2f; /* YUV 420 */ + sd->fmt = fmt; + + switch (mode & SCALE_MASK) { + case SCALE_1280x1024: + scale = 0xc0; + pr_info("Set 1280x1024\n"); + break; + case SCALE_640x480: + scale = 0x80; + pr_info("Set 640x480\n"); + break; + case SCALE_320x240: + scale = 0x90; + pr_info("Set 320x240\n"); + break; + case SCALE_160x120: + scale = 0xa0; + pr_info("Set 160x120\n"); + break; + } + + configure_sensor_output(gspca_dev, mode); + reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); + reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); + reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5); + reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6); + reg_w1(gspca_dev, 0x1189, scale); + reg_w1(gspca_dev, 0x10e0, fmt); + + set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast), + v4l2_ctrl_g_ctrl(sd->saturation), + v4l2_ctrl_g_ctrl(sd->hue)); + set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); + set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + if (sd->gain) + set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); + if (sd->exposure) + set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); + if (sd->hflip) + set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), + v4l2_ctrl_g_ctrl(sd->vflip)); + + reg_w1(gspca_dev, 0x1007, 0x20); + reg_w1(gspca_dev, 0x1061, 0x03); + + /* if JPEG, prepare the compression quality update */ + if (mode & MODE_JPEG) { + sd->pktsz = sd->npkt = 0; + sd->nchg = 0; + sd->work_thread = + create_singlethread_workqueue(KBUILD_MODNAME); + } + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w1(gspca_dev, 0x1007, 0x00); + reg_w1(gspca_dev, 0x1061, 0x01); +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } +} + +static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure); + s32 max = sd->exposure->maximum - sd->exposure_step; + s32 min = sd->exposure->minimum + sd->exposure_step; + s16 new_exp; + + /* + * some hardcoded values are present + * like those for maximal/minimal exposure + * and exposure steps + */ + if (avg_lum < MIN_AVG_LUM) { + if (cur_exp > max) + return; + + new_exp = cur_exp + sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); + + sd->older_step = sd->old_step; + sd->old_step = 1; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } + if (avg_lum > MAX_AVG_LUM) { + if (cur_exp < min) + return; + new_exp = cur_exp - sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); + sd->older_step = sd->old_step; + sd->old_step = 0; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } +} + +static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain); + + if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1); + if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1); +} + +static void sd_dqcallback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum; + + if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain)) + return; + + avg_lum = atomic_read(&sd->avg_lum); + if (sd->sensor == SENSOR_SOI968) + do_autogain(gspca_dev, avg_lum); + else + do_autoexposure(gspca_dev, avg_lum); +} + +/* JPEG quality update */ +/* This function is executed from a work queue. */ +static void qual_upd(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual); + + mutex_lock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "qual_upd %d%%", qual); + set_quality(gspca_dev, qual); + mutex_unlock(&gspca_dev->usb_lock); +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet */ + int len) /* interrupt packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!(sd->flags & HAS_NO_BUTTON) && len == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + return 0; + } + return -EINVAL; +} +#endif + +/* check the JPEG compression */ +static void transfer_check(struct gspca_dev *gspca_dev, + u8 *data) +{ + struct sd *sd = (struct sd *) gspca_dev; + int new_qual, r; + + new_qual = 0; + + /* if USB error, discard the frame and decrease the quality */ + if (data[6] & 0x08) { /* USB FIFO full */ + gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -5; + } else { + + /* else, compute the filling rate and a new JPEG quality */ + r = (sd->pktsz * 100) / + (sd->npkt * + gspca_dev->urb[0]->iso_frame_desc[0].length); + if (r >= 85) + new_qual = -3; + else if (r < 75) + new_qual = 2; + } + if (new_qual != 0) { + sd->nchg += new_qual; + if (sd->nchg < -6 || sd->nchg >= 12) { + /* Note: we are in interrupt context, so we can't + use v4l2_ctrl_g/s_ctrl here. Access the value + directly instead. */ + s32 curqual = sd->jpegqual->cur.val; + sd->nchg = 0; + new_qual += curqual; + if (new_qual < sd->jpegqual->minimum) + new_qual = sd->jpegqual->minimum; + else if (new_qual > sd->jpegqual->maximum) + new_qual = sd->jpegqual->maximum; + if (new_qual != curqual) { + sd->jpegqual->cur.val = new_qual; + queue_work(sd->work_thread, &sd->work); + } + } + } else { + sd->nchg = 0; + } + sd->pktsz = sd->npkt = 0; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum, is_jpeg; + static const u8 frame_header[] = + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + + is_jpeg = (sd->fmt & 0x03) == 0; + if (len >= 64 && memcmp(data, frame_header, 6) == 0) { + avg_lum = ((data[35] >> 2) & 3) | + (data[20] << 2) | + (data[19] << 10); + avg_lum += ((data[35] >> 4) & 3) | + (data[22] << 2) | + (data[21] << 10); + avg_lum += ((data[35] >> 6) & 3) | + (data[24] << 2) | + (data[23] << 10); + avg_lum += (data[36] & 3) | + (data[26] << 2) | + (data[25] << 10); + avg_lum += ((data[36] >> 2) & 3) | + (data[28] << 2) | + (data[27] << 10); + avg_lum += ((data[36] >> 4) & 3) | + (data[30] << 2) | + (data[29] << 10); + avg_lum += ((data[36] >> 6) & 3) | + (data[32] << 2) | + (data[31] << 10); + avg_lum += ((data[44] >> 4) & 3) | + (data[34] << 2) | + (data[33] << 10); + avg_lum >>= 9; + atomic_set(&sd->avg_lum, avg_lum); + + if (is_jpeg) + transfer_check(gspca_dev, data); + + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + len -= 64; + if (len == 0) + return; + data += 64; + } + if (gspca_dev->last_packet_type == LAST_PACKET) { + if (is_jpeg) { + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, + data, len); + } else { + gspca_frame_add(gspca_dev, FIRST_PACKET, + data, len); + } + } else { + /* if JPEG, count the packets and their size */ + if (is_jpeg) { + sd->npkt++; + sd->pktsz += len; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + } +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = KBUILD_MODNAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif + .dq_callback = sd_dqcallback, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_register = sd_dbg_g_register, +#endif + .get_chip_ident = sd_chip_ident, +}; + +#define SN9C20X(sensor, i2c_addr, flags) \ + .driver_info = ((flags & 0xff) << 16) \ + | (SENSOR_ ## sensor << 8) \ + | (i2c_addr) + +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)}, + {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, + (FLIP_DETECT | HAS_NO_BUTTON))}, + {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, FLIP_DETECT)}, + {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)}, + {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)}, + {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = KBUILD_MODNAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c new file mode 100644 index 000000000000..fd1f8d2d3b0b --- /dev/null +++ b/drivers/media/usb/gspca/sonixb.c @@ -0,0 +1,1493 @@ +/* + * sonix sn9c102 (bayer) library + * + * Copyright (C) 2009-2011 Jean-François Moine + * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr + * Add Pas106 Stefano Mozzi (C) 2004 + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Some documentation on known sonixb registers: + +Reg Use +sn9c101 / sn9c102: +0x10 high nibble red gain low nibble blue gain +0x11 low nibble green gain +sn9c103: +0x05 red gain 0-127 +0x06 blue gain 0-127 +0x07 green gain 0-127 +all: +0x08-0x0f i2c / 3wire registers +0x12 hstart +0x13 vstart +0x15 hsize (hsize = register-value * 16) +0x16 vsize (vsize = register-value * 16) +0x17 bit 0 toggle compression quality (according to sn9c102 driver) +0x18 bit 7 enables compression, bit 4-5 set image down scaling: + 00 scale 1, 01 scale 1/2, 10, scale 1/4 +0x19 high-nibble is sensor clock divider, changes exposure on sensors which + use a clock generated by the bridge. Some sensors have their own clock. +0x1c auto_exposure area (for avg_lum) startx (startx = register-value * 32) +0x1d auto_exposure area (for avg_lum) starty (starty = register-value * 32) +0x1e auto_exposure area (for avg_lum) stopx (hsize = (0x1e - 0x1c) * 32) +0x1f auto_exposure area (for avg_lum) stopy (vsize = (0x1f - 0x1d) * 32) +*/ + +#define MODULE_NAME "sonixb" + +#include +#include "gspca.h" + +MODULE_AUTHOR("Jean-François Moine "); +MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *plfreq; + + atomic_t avg_lum; + int prev_avg_lum; + int exposure_knee; + int header_read; + u8 header[12]; /* Header without sof marker */ + + unsigned char autogain_ignore_frames; + unsigned char frames_to_drop; + + __u8 bridge; /* Type of bridge */ +#define BRIDGE_101 0 +#define BRIDGE_102 0 /* We make no difference between 101 and 102 */ +#define BRIDGE_103 1 + + __u8 sensor; /* Type of image sensor chip */ +#define SENSOR_HV7131D 0 +#define SENSOR_HV7131R 1 +#define SENSOR_OV6650 2 +#define SENSOR_OV7630 3 +#define SENSOR_PAS106 4 +#define SENSOR_PAS202 5 +#define SENSOR_TAS5110C 6 +#define SENSOR_TAS5110D 7 +#define SENSOR_TAS5130CXX 8 + __u8 reg11; +}; + +typedef const __u8 sensor_init_t[8]; + +struct sensor_data { + const __u8 *bridge_init; + sensor_init_t *sensor_init; + int sensor_init_size; + int flags; + __u8 sensor_addr; +}; + +/* sensor_data flags */ +#define F_SIF 0x01 /* sif or vga */ + +/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ +#define MODE_RAW 0x10 /* raw bayer mode */ +#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ + +#define COMP 0xc7 /* 0x87 //0x07 */ +#define COMP1 0xc9 /* 0x89 //0x09 */ + +#define MCK_INIT 0x63 +#define MCK_INIT1 0x20 /*fixme: Bayer - 0x50 for JPEG ??*/ + +#define SYS_CLK 0x04 + +#define SENS(bridge, sensor, _flags, _sensor_addr) \ +{ \ + .bridge_init = bridge, \ + .sensor_init = sensor, \ + .sensor_init_size = sizeof(sensor), \ + .flags = _flags, .sensor_addr = _sensor_addr \ +} + +/* We calculate the autogain at the end of the transfer of a frame, at this + moment a frame with the old settings is being captured and transmitted. So + if we adjust the gain or exposure we must ignore atleast the next frame for + the new settings to come into effect before doing any other adjustments. */ +#define AUTOGAIN_IGNORE_FRAMES 1 + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format sif_mode[] = { + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW | MODE_REDUCED_SIF}, + {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_REDUCED_SIF}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_REDUCED_SIF}, + {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +static const __u8 initHv7131d[] = { + 0x04, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, + 0x28, 0x1e, 0x60, 0x8e, 0x42, +}; +static const __u8 hv7131d_sensor_init[][8] = { + {0xa0, 0x11, 0x01, 0x04, 0x00, 0x00, 0x00, 0x17}, + {0xa0, 0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x17}, + {0xa0, 0x11, 0x28, 0x00, 0x00, 0x00, 0x00, 0x17}, + {0xa0, 0x11, 0x30, 0x30, 0x00, 0x00, 0x00, 0x17}, /* reset level */ + {0xa0, 0x11, 0x34, 0x02, 0x00, 0x00, 0x00, 0x17}, /* pixel bias volt */ +}; + +static const __u8 initHv7131r[] = { + 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, + 0x28, 0x1e, 0x60, 0x8a, 0x20, +}; +static const __u8 hv7131r_sensor_init[][8] = { + {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, + {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10}, + {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10}, + {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16}, + {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15}, +}; +static const __u8 initOv6650[] = { + 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b, + 0x10, +}; +static const __u8 ov6650_sensor_init[][8] = { + /* Bright, contrast, etc are set through SCBB interface. + * AVCAP on win2 do not send any data on this controls. */ + /* Anyway, some registers appears to alter bright and constrat */ + + /* Reset sensor */ + {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + /* Set clock register 0x11 low nibble is clock divider */ + {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10}, + /* Next some unknown stuff */ + {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10}, +/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10}, + * THIS SET GREEN SCREEN + * (pixels could be innverted in decode kind of "brg", + * but blue wont be there. Avoid this data ... */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, + {0xa0, 0x60, 0x30, 0x3d, 0x0a, 0xd8, 0xa4, 0x10}, + /* Enable rgb brightness control */ + {0xa0, 0x60, 0x61, 0x08, 0x00, 0x00, 0x00, 0x10}, + /* HDG: Note windows uses the line below, which sets both register 0x60 + and 0x61 I believe these registers of the ov6650 are identical as + those of the ov7630, because if this is true the windows settings + add a bit additional red gain and a lot additional blue gain, which + matches my findings that the windows settings make blue much too + blue and red a little too red. + {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, */ + /* Some more unknown stuff */ + {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10}, + {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */ +}; + +static const __u8 initOv7630[] = { + 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ + 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ + 0x28, 0x1e, /* H & V sizes r15 .. r16 */ + 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ +}; +static const __u8 ov7630_sensor_init[][8] = { + {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, +/* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ + {0xd0, 0x21, 0x12, 0x5c, 0x00, 0x80, 0x34, 0x10}, /* jfm */ + {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, + {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10}, +/* {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, * jfm */ + {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10}, + {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10}, + {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10}, + {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, + {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, +}; + +static const __u8 initPas106[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x16, 0x12, 0x24, COMP1, MCK_INIT1, +}; +/* compression 0x86 mckinit1 0x2b */ + +/* "Known" PAS106B registers: + 0x02 clock divider + 0x03 Variable framerate bits 4-11 + 0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !! + The variable framerate control must never be set lower then 300, + which sets the framerate at 90 / reg02, otherwise vsync is lost. + 0x05 Shutter Time Line Offset, this can be used as an exposure control: + 0 = use full frame time, 255 = no exposure at all + Note this may never be larger then "var-framerate control" / 2 - 2. + When var-framerate control is < 514, no exposure is reached at the max + allowed value for the framerate control value, rather then at 255. + 0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but + only a very little bit, leave at 0xcd + 0x07 offset sign bit (bit0 1 > negative offset) + 0x08 offset + 0x09 Blue Gain + 0x0a Green1 Gain + 0x0b Green2 Gain + 0x0c Red Gain + 0x0e Global gain + 0x13 Write 1 to commit settings to sensor +*/ + +static const __u8 pas106_sensor_init[][8] = { + /* Pixel Clock Divider 6 */ + { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Frame Time MSB (also seen as 0x12) */ + { 0xa1, 0x40, 0x03, 0x13, 0x00, 0x00, 0x00, 0x14 }, + /* Frame Time LSB (also seen as 0x05) */ + { 0xa1, 0x40, 0x04, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* Shutter Time Line Offset (also seen as 0x6d) */ + { 0xa1, 0x40, 0x05, 0x65, 0x00, 0x00, 0x00, 0x14 }, + /* Shutter Time Pixel Offset (also seen as 0xb1) */ + { 0xa1, 0x40, 0x06, 0xcd, 0x00, 0x00, 0x00, 0x14 }, + /* Black Level Subtract Sign (also seen 0x00) */ + { 0xa1, 0x40, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x14 }, + /* Black Level Subtract Level (also seen 0x01) */ + { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, + { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain B Pixel 5 a */ + { 0xa1, 0x40, 0x09, 0x05, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain G1 Pixel 1 5 */ + { 0xa1, 0x40, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain G2 Pixel 1 0 5 */ + { 0xa1, 0x40, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain R Pixel 3 1 */ + { 0xa1, 0x40, 0x0c, 0x05, 0x00, 0x00, 0x00, 0x14 }, + /* Color GainH Pixel */ + { 0xa1, 0x40, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x14 }, + /* Global Gain */ + { 0xa1, 0x40, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x14 }, + /* Contrast */ + { 0xa1, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14 }, + /* H&V synchro polarity */ + { 0xa1, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* ?default */ + { 0xa1, 0x40, 0x11, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* DAC scale */ + { 0xa1, 0x40, 0x12, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* ?default */ + { 0xa1, 0x40, 0x14, 0x02, 0x00, 0x00, 0x00, 0x14 }, + /* Validate Settings */ + { 0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14 }, +}; + +static const __u8 initPas202[] = { + 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, + 0x28, 0x1e, 0x20, 0x89, 0x20, +}; + +/* "Known" PAS202BCB registers: + 0x02 clock divider + 0x04 Variable framerate bits 6-11 (*) + 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !! + 0x07 Blue Gain + 0x08 Green Gain + 0x09 Red Gain + 0x0b offset sign bit (bit0 1 > negative offset) + 0x0c offset + 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too, + leave at 1 otherwise we get a jump in our exposure control + 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all + 0x10 Master gain 0 - 31 + 0x11 write 1 to apply changes + (*) The variable framerate control must never be set lower then 500 + which sets the framerate at 30 / reg02, otherwise vsync is lost. +*/ +static const __u8 pas202_sensor_init[][8] = { + /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like + to set it lower, but for some reason the bridge starts missing + vsync's then */ + {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, + {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, + {0xd0, 0x40, 0x0c, 0x00, 0x0c, 0x01, 0x32, 0x10}, + {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, +}; + +static const __u8 initTas5110c[] = { + 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a, + 0x16, 0x12, 0x60, 0x86, 0x2b, +}; +/* Same as above, except a different hstart */ +static const __u8 initTas5110d[] = { + 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, + 0x16, 0x12, 0x60, 0x86, 0x2b, +}; +/* tas5110c is 3 wire, tas5110d is 2 wire (regular i2c) */ +static const __u8 tas5110c_sensor_init[][8] = { + {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, + {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, +}; +/* Known TAS5110D registers + * reg02: gain, bit order reversed!! 0 == max gain, 255 == min gain + * reg03: bit3: vflip, bit4: ~hflip, bit7: ~gainboost (~ == inverted) + * Note: writing reg03 seems to only work when written together with 02 + */ +static const __u8 tas5110d_sensor_init[][8] = { + {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, /* reset */ +}; + +static const __u8 initTas5130[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a, + 0x28, 0x1e, 0x60, COMP, MCK_INIT, +}; +static const __u8 tas5130_sensor_init[][8] = { +/* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10}, + * shutter 0x47 short exposure? */ + {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10}, + /* shutter 0x01 long exposure */ + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, +}; + +static const struct sensor_data sensor_data[] = { + SENS(initHv7131d, hv7131d_sensor_init, 0, 0), + SENS(initHv7131r, hv7131r_sensor_init, 0, 0), + SENS(initOv6650, ov6650_sensor_init, F_SIF, 0x60), + SENS(initOv7630, ov7630_sensor_init, 0, 0x21), + SENS(initPas106, pas106_sensor_init, F_SIF, 0), + SENS(initPas202, pas202_sensor_init, 0, 0), + SENS(initTas5110c, tas5110c_sensor_init, F_SIF, 0), + SENS(initTas5110d, tas5110d_sensor_init, F_SIF, 0), + SENS(initTas5130, tas5130_sensor_init, 0, 0), +}; + +/* get one byte in gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 value) +{ + int res; + + if (gspca_dev->usb_err < 0) + return; + + res = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + gspca_dev->usb_buf, 1, + 500); + + if (res < 0) { + dev_err(gspca_dev->v4l2_dev.dev, + "Error reading register %02x: %d\n", value, res); + gspca_dev->usb_err = res; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 value, + const __u8 *buffer, + int len) +{ + int res; + + if (gspca_dev->usb_err < 0) + return; + + memcpy(gspca_dev->usb_buf, buffer, len); + res = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + gspca_dev->usb_buf, len, + 500); + + if (res < 0) { + dev_err(gspca_dev->v4l2_dev.dev, + "Error writing register %02x: %d\n", value, res); + gspca_dev->usb_err = res; + } +} + +static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +{ + int retry = 60; + + if (gspca_dev->usb_err < 0) + return; + + /* is i2c ready */ + reg_w(gspca_dev, 0x08, buffer, 8); + while (retry--) { + if (gspca_dev->usb_err < 0) + return; + msleep(10); + reg_r(gspca_dev, 0x08); + if (gspca_dev->usb_buf[0] & 0x04) { + if (gspca_dev->usb_buf[0] & 0x08) { + dev_err(gspca_dev->v4l2_dev.dev, + "i2c write error\n"); + gspca_dev->usb_err = -EIO; + } + return; + } + } + + dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n"); + gspca_dev->usb_err = -EIO; +} + +static void i2c_w_vector(struct gspca_dev *gspca_dev, + const __u8 buffer[][8], int len) +{ + for (;;) { + if (gspca_dev->usb_err < 0) + return; + reg_w(gspca_dev, 0x08, *buffer, 8); + len -= 8; + if (len <= 0) + break; + buffer++; + } +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_OV6650: + case SENSOR_OV7630: { + __u8 i2cOV[] = + {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; + + /* change reg 0x06 */ + i2cOV[1] = sensor_data[sd->sensor].sensor_addr; + i2cOV[3] = sd->brightness->val; + i2c_w(gspca_dev, i2cOV); + break; + } + case SENSOR_PAS106: + case SENSOR_PAS202: { + __u8 i2cpbright[] = + {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16}; + __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + + /* PAS106 uses reg 7 and 8 instead of b and c */ + if (sd->sensor == SENSOR_PAS106) { + i2cpbright[2] = 7; + i2cpdoit[2] = 0x13; + } + + if (sd->brightness->val < 127) { + /* change reg 0x0b, signreg */ + i2cpbright[3] = 0x01; + /* set reg 0x0c, offset */ + i2cpbright[4] = 127 - sd->brightness->val; + } else + i2cpbright[4] = sd->brightness->val - 127; + + i2c_w(gspca_dev, i2cpbright); + i2c_w(gspca_dev, i2cpdoit); + break; + } + default: + break; + } +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gain = gspca_dev->gain->val; + + switch (sd->sensor) { + case SENSOR_HV7131D: { + __u8 i2c[] = + {0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17}; + + i2c[3] = 0x3f - gain; + i2c[4] = 0x3f - gain; + i2c[5] = 0x3f - gain; + + i2c_w(gspca_dev, i2c); + break; + } + case SENSOR_TAS5110C: + case SENSOR_TAS5130CXX: { + __u8 i2c[] = + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; + + i2c[4] = 255 - gain; + i2c_w(gspca_dev, i2c); + break; + } + case SENSOR_TAS5110D: { + __u8 i2c[] = { + 0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 }; + gain = 255 - gain; + /* The bits in the register are the wrong way around!! */ + i2c[3] |= (gain & 0x80) >> 7; + i2c[3] |= (gain & 0x40) >> 5; + i2c[3] |= (gain & 0x20) >> 3; + i2c[3] |= (gain & 0x10) >> 1; + i2c[3] |= (gain & 0x08) << 1; + i2c[3] |= (gain & 0x04) << 3; + i2c[3] |= (gain & 0x02) << 5; + i2c[3] |= (gain & 0x01) << 7; + i2c_w(gspca_dev, i2c); + break; + } + case SENSOR_OV6650: + case SENSOR_OV7630: { + __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + + /* + * The ov7630's gain is weird, at 32 the gain drops to the + * same level as at 16, so skip 32-47 (of the 0-63 scale). + */ + if (sd->sensor == SENSOR_OV7630 && gain >= 32) + gain += 16; + + i2c[1] = sensor_data[sd->sensor].sensor_addr; + i2c[3] = gain; + i2c_w(gspca_dev, i2c); + break; + } + case SENSOR_PAS106: + case SENSOR_PAS202: { + __u8 i2cpgain[] = + {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15}; + __u8 i2cpcolorgain[] = + {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; + __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + + /* PAS106 uses different regs (and has split green gains) */ + if (sd->sensor == SENSOR_PAS106) { + i2cpgain[2] = 0x0e; + i2cpcolorgain[0] = 0xd0; + i2cpcolorgain[2] = 0x09; + i2cpdoit[2] = 0x13; + } + + i2cpgain[3] = gain; + i2cpcolorgain[3] = gain >> 1; + i2cpcolorgain[4] = gain >> 1; + i2cpcolorgain[5] = gain >> 1; + i2cpcolorgain[6] = gain >> 1; + + i2c_w(gspca_dev, i2cpgain); + i2c_w(gspca_dev, i2cpcolorgain); + i2c_w(gspca_dev, i2cpdoit); + break; + } + default: + if (sd->bridge == BRIDGE_103) { + u8 buf[3] = { gain, gain, gain }; /* R, G, B */ + reg_w(gspca_dev, 0x05, buf, 3); + } else { + u8 buf[2]; + buf[0] = gain << 4 | gain; /* Red and blue */ + buf[1] = gain; /* Green */ + reg_w(gspca_dev, 0x10, buf, 2); + } + } +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_HV7131D: { + /* Note the datasheet wrongly says line mode exposure uses reg + 0x26 and 0x27, testing has shown 0x25 + 0x26 */ + __u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17}; + u16 reg = gspca_dev->exposure->val; + + i2c[3] = reg >> 8; + i2c[4] = reg & 0xff; + i2c_w(gspca_dev, i2c); + break; + } + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: { + /* register 19's high nibble contains the sn9c10x clock divider + The high nibble configures the no fps according to the + formula: 60 / high_nibble. With a maximum of 30 fps */ + u8 reg = gspca_dev->exposure->val; + + reg = (reg << 4) | 0x0b; + reg_w(gspca_dev, 0x19, ®, 1); + break; + } + case SENSOR_OV6650: + case SENSOR_OV7630: { + /* The ov6650 / ov7630 have 2 registers which both influence + exposure, register 11, whose low nibble sets the nr off fps + according to: fps = 30 / (low_nibble + 1) + + The fps configures the maximum exposure setting, but it is + possible to use less exposure then what the fps maximum + allows by setting register 10. register 10 configures the + actual exposure as quotient of the full exposure, with 0 + being no exposure at all (not very useful) and reg10_max + being max exposure possible at that framerate. + + The code maps our 0 - 510 ms exposure ctrl to these 2 + registers, trying to keep fps as high as possible. + */ + __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}; + int reg10, reg11, reg10_max; + + /* ov6645 datasheet says reg10_max is 9a, but that uses + tline * 2 * reg10 as formula for calculating texpo, the + ov6650 probably uses the same formula as the 7730 which uses + tline * 4 * reg10, which explains why the reg10max we've + found experimentally for the ov6650 is exactly half that of + the ov6645. The ov7630 datasheet says the max is 0x41. */ + if (sd->sensor == SENSOR_OV6650) { + reg10_max = 0x4d; + i2c[4] = 0xc0; /* OV6650 needs non default vsync pol */ + } else + reg10_max = 0x41; + + reg11 = (15 * gspca_dev->exposure->val + 999) / 1000; + if (reg11 < 1) + reg11 = 1; + else if (reg11 > 16) + reg11 = 16; + + /* In 640x480, if the reg11 has less than 4, the image is + unstable (the bridge goes into a higher compression mode + which we have not reverse engineered yet). */ + if (gspca_dev->width == 640 && reg11 < 4) + reg11 = 4; + + /* frame exposure time in ms = 1000 * reg11 / 30 -> + reg10 = (gspca_dev->exposure->val / 2) * reg10_max + / (1000 * reg11 / 30) */ + reg10 = (gspca_dev->exposure->val * 15 * reg10_max) + / (1000 * reg11); + + /* Don't allow this to get below 10 when using autogain, the + steps become very large (relatively) when below 10 causing + the image to oscilate from much too dark, to much too bright + and back again. */ + if (gspca_dev->autogain->val && reg10 < 10) + reg10 = 10; + else if (reg10 > reg10_max) + reg10 = reg10_max; + + /* Write reg 10 and reg11 low nibble */ + i2c[1] = sensor_data[sd->sensor].sensor_addr; + i2c[3] = reg10; + i2c[4] |= reg11 - 1; + + /* If register 11 didn't change, don't change it */ + if (sd->reg11 == reg11) + i2c[0] = 0xa0; + + i2c_w(gspca_dev, i2c); + if (gspca_dev->usb_err == 0) + sd->reg11 = reg11; + break; + } + case SENSOR_PAS202: { + __u8 i2cpframerate[] = + {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16}; + __u8 i2cpexpo[] = + {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16}; + const __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + int framerate_ctrl; + + /* The exposure knee for the autogain algorithm is 200 + (100 ms / 10 fps on other sensors), for values below this + use the control for setting the partial frame expose time, + above that use variable framerate. This way we run at max + framerate (640x480@7.5 fps, 320x240@10fps) until the knee + is reached. Using the variable framerate control above 200 + is better then playing around with both clockdiv + partial + frame exposure times (like we are doing with the ov chips), + as that sometimes leads to jumps in the exposure control, + which are bad for auto exposure. */ + if (gspca_dev->exposure->val < 200) { + i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255) + / 200; + framerate_ctrl = 500; + } else { + /* The PAS202's exposure control goes from 0 - 4095, + but anything below 500 causes vsync issues, so scale + our 200-1023 to 500-4095 */ + framerate_ctrl = (gspca_dev->exposure->val - 200) + * 1000 / 229 + 500; + } + + i2cpframerate[3] = framerate_ctrl >> 6; + i2cpframerate[4] = framerate_ctrl & 0x3f; + i2c_w(gspca_dev, i2cpframerate); + i2c_w(gspca_dev, i2cpexpo); + i2c_w(gspca_dev, i2cpdoit); + break; + } + case SENSOR_PAS106: { + __u8 i2cpframerate[] = + {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14}; + __u8 i2cpexpo[] = + {0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14}; + const __u8 i2cpdoit[] = + {0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14}; + int framerate_ctrl; + + /* For values below 150 use partial frame exposure, above + that use framerate ctrl */ + if (gspca_dev->exposure->val < 150) { + i2cpexpo[3] = 150 - gspca_dev->exposure->val; + framerate_ctrl = 300; + } else { + /* The PAS106's exposure control goes from 0 - 4095, + but anything below 300 causes vsync issues, so scale + our 150-1023 to 300-4095 */ + framerate_ctrl = (gspca_dev->exposure->val - 150) + * 1000 / 230 + 300; + } + + i2cpframerate[3] = framerate_ctrl >> 4; + i2cpframerate[4] = framerate_ctrl & 0x0f; + i2c_w(gspca_dev, i2cpframerate); + i2c_w(gspca_dev, i2cpexpo); + i2c_w(gspca_dev, i2cpdoit); + break; + } + default: + break; + } +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) { + /* Framerate adjust register for artificial light 50 hz flicker + compensation, for the ov6650 this is identical to ov6630 + 0x2b register, see ov6630 datasheet. + 0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */ + __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; + switch (sd->plfreq->val) { + default: +/* case 0: * no filter*/ +/* case 2: * 60 hz */ + i2c[3] = 0; + break; + case 1: /* 50 hz */ + i2c[3] = (sd->sensor == SENSOR_OV6650) + ? 0x4f : 0x8a; + break; + } + i2c[1] = sensor_data[sd->sensor].sensor_addr; + i2c_w(gspca_dev, i2c); + } +} + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int deadzone, desired_avg_lum, avg_lum; + + avg_lum = atomic_read(&sd->avg_lum); + if (avg_lum == -1) + return; + + if (sd->autogain_ignore_frames > 0) { + sd->autogain_ignore_frames--; + return; + } + + /* SIF / VGA sensors have a different autoexposure area and thus + different avg_lum values for the same picture brightness */ + if (sensor_data[sd->sensor].flags & F_SIF) { + deadzone = 500; + /* SIF sensors tend to overexpose, so keep this small */ + desired_avg_lum = 5000; + } else { + deadzone = 1500; + desired_avg_lum = 13000; + } + + if (sd->brightness) + desired_avg_lum = sd->brightness->val * desired_avg_lum / 127; + + if (gspca_dev->exposure->maximum < 500) { + if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + desired_avg_lum, deadzone)) + sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; + } else { + int gain_knee = gspca_dev->gain->maximum * 9 / 10; + if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum, + deadzone, gain_knee, sd->exposure_knee)) + sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + reg_r(gspca_dev, 0x00); + if (gspca_dev->usb_buf[0] != 0x10) + return -ENODEV; + + /* copy the webcam info from the device id */ + sd->sensor = id->driver_info >> 8; + sd->bridge = id->driver_info & 0xff; + + cam = &gspca_dev->cam; + if (!(sensor_data[sd->sensor].flags & F_SIF)) { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } else { + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + } + cam->npkt = 36; /* 36 packets per ISOC message */ + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + const __u8 stop = 0x09; /* Disable stream turn of LED */ + + reg_w(gspca_dev, 0x01, &stop, 1); + + return gspca_dev->usb_err; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->gain->val = gspca_dev->gain->default_value; + gspca_dev->exposure->val = gspca_dev->exposure->default_value; + sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setexposure(gspca_dev); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setgain(gspca_dev); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setfreq(gspca_dev); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + + if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630 || + sd->sensor == SENSOR_PAS106 || sd->sensor == SENSOR_PAS202) + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + + /* Gain range is sensor dependent */ + switch (sd->sensor) { + case SENSOR_OV6650: + case SENSOR_PAS106: + case SENSOR_PAS202: + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 31, 1, 15); + break; + case SENSOR_OV7630: + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 47, 1, 31); + break; + case SENSOR_HV7131D: + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 63, 1, 31); + break; + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: + case SENSOR_TAS5130CXX: + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 127); + break; + default: + if (sd->bridge == BRIDGE_103) { + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 63); + } else { + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 15, 1, 7); + } + } + + /* Exposure range is sensor dependent, and not all have exposure */ + switch (sd->sensor) { + case SENSOR_HV7131D: + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 8191, 1, 482); + sd->exposure_knee = 964; + break; + case SENSOR_OV6650: + case SENSOR_OV7630: + case SENSOR_PAS106: + case SENSOR_PAS202: + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 1023, 1, 66); + sd->exposure_knee = 200; + break; + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 2, 15, 1, 2); + break; + } + + if (gspca_dev->exposure) { + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + } + + if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) + sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + if (gspca_dev->autogain) + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + int i, mode; + __u8 regs[0x31]; + + mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07; + /* Copy registers 0x01 - 0x19 from the template */ + memcpy(®s[0x01], sensor_data[sd->sensor].bridge_init, 0x19); + /* Set the mode */ + regs[0x18] |= mode << 4; + + /* Set bridge gain to 1.0 */ + if (sd->bridge == BRIDGE_103) { + regs[0x05] = 0x20; /* Red */ + regs[0x06] = 0x20; /* Green */ + regs[0x07] = 0x20; /* Blue */ + } else { + regs[0x10] = 0x00; /* Red and blue */ + regs[0x11] = 0x00; /* Green */ + } + + /* Setup pixel numbers and auto exposure window */ + if (sensor_data[sd->sensor].flags & F_SIF) { + regs[0x1a] = 0x14; /* HO_SIZE 640, makes no sense */ + regs[0x1b] = 0x0a; /* VO_SIZE 320, makes no sense */ + regs[0x1c] = 0x02; /* AE H-start 64 */ + regs[0x1d] = 0x02; /* AE V-start 64 */ + regs[0x1e] = 0x09; /* AE H-end 288 */ + regs[0x1f] = 0x07; /* AE V-end 224 */ + } else { + regs[0x1a] = 0x1d; /* HO_SIZE 960, makes no sense */ + regs[0x1b] = 0x10; /* VO_SIZE 512, makes no sense */ + regs[0x1c] = 0x05; /* AE H-start 160 */ + regs[0x1d] = 0x03; /* AE V-start 96 */ + regs[0x1e] = 0x0f; /* AE H-end 480 */ + regs[0x1f] = 0x0c; /* AE V-end 384 */ + } + + /* Setup the gamma table (only used with the sn9c103 bridge) */ + for (i = 0; i < 16; i++) + regs[0x20 + i] = i * 16; + regs[0x20 + i] = 255; + + /* Special cases where some regs depend on mode or bridge */ + switch (sd->sensor) { + case SENSOR_TAS5130CXX: + /* FIXME / TESTME + probably not mode specific at all most likely the upper + nibble of 0x19 is exposure (clock divider) just as with + the tas5110, we need someone to test this. */ + regs[0x19] = mode ? 0x23 : 0x43; + break; + case SENSOR_OV7630: + /* FIXME / TESTME for some reason with the 101/102 bridge the + clock is set to 12 Mhz (reg1 == 0x04), rather then 24. + Also the hstart needs to go from 1 to 2 when using a 103, + which is likely related. This does not seem right. */ + if (sd->bridge == BRIDGE_103) { + regs[0x01] = 0x44; /* Select 24 Mhz clock */ + regs[0x12] = 0x02; /* Set hstart to 2 */ + } + } + /* Disable compression when the raw bayer format has been selected */ + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) + regs[0x18] &= ~0x80; + + /* Vga mode emulation on SIF sensor? */ + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) { + regs[0x12] += 16; /* hstart adjust */ + regs[0x13] += 24; /* vstart adjust */ + regs[0x15] = 320 / 16; /* hsize */ + regs[0x16] = 240 / 16; /* vsize */ + } + + /* reg 0x01 bit 2 video transfert on */ + reg_w(gspca_dev, 0x01, ®s[0x01], 1); + /* reg 0x17 SensorClk enable inv Clk 0x60 */ + reg_w(gspca_dev, 0x17, ®s[0x17], 1); + /* Set the registers from the template */ + reg_w(gspca_dev, 0x01, ®s[0x01], + (sd->bridge == BRIDGE_103) ? 0x30 : 0x1f); + + /* Init the sensor */ + i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init, + sensor_data[sd->sensor].sensor_init_size); + + /* Mode / bridge specific sensor setup */ + switch (sd->sensor) { + case SENSOR_PAS202: { + const __u8 i2cpclockdiv[] = + {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}; + /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */ + if (mode) + i2c_w(gspca_dev, i2cpclockdiv); + break; + } + case SENSOR_OV7630: + /* FIXME / TESTME We should be able to handle this identical + for the 101/102 and the 103 case */ + if (sd->bridge == BRIDGE_103) { + const __u8 i2c[] = { 0xa0, 0x21, 0x13, + 0x80, 0x00, 0x00, 0x00, 0x10 }; + i2c_w(gspca_dev, i2c); + } + break; + } + /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ + reg_w(gspca_dev, 0x15, ®s[0x15], 2); + /* compression register */ + reg_w(gspca_dev, 0x18, ®s[0x18], 1); + /* H_start */ + reg_w(gspca_dev, 0x12, ®s[0x12], 1); + /* V_START */ + reg_w(gspca_dev, 0x13, ®s[0x13], 1); + /* reset 0x17 SensorClk enable inv Clk 0x60 */ + /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ + reg_w(gspca_dev, 0x17, ®s[0x17], 1); + /*MCKSIZE ->3 */ /*fixme: not ov7630*/ + reg_w(gspca_dev, 0x19, ®s[0x19], 1); + /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ + reg_w(gspca_dev, 0x1c, ®s[0x1c], 4); + /* Enable video transfert */ + reg_w(gspca_dev, 0x01, ®s[0x01], 1); + /* Compression */ + reg_w(gspca_dev, 0x18, ®s[0x18], 2); + msleep(20); + + sd->reg11 = -1; + + setgain(gspca_dev); + setbrightness(gspca_dev); + setexposure(gspca_dev); + setfreq(gspca_dev); + + sd->frames_to_drop = 0; + sd->autogain_ignore_frames = 0; + gspca_dev->exp_too_high_cnt = 0; + gspca_dev->exp_too_low_cnt = 0; + atomic_set(&sd->avg_lum, -1); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + sd_init(gspca_dev); +} + +static u8* find_sof(struct gspca_dev *gspca_dev, u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, header_size = (sd->bridge == BRIDGE_103) ? 18 : 12; + + /* frames start with: + * ff ff 00 c4 c4 96 synchro + * 00 (unknown) + * xx (frame sequence / size / compression) + * (xx) (idem - extra byte for sn9c103) + * ll mm brightness sum inside auto exposure + * ll mm brightness sum outside auto exposure + * (xx xx xx xx xx) audio values for snc103 + */ + for (i = 0; i < len; i++) { + switch (sd->header_read) { + case 0: + if (data[i] == 0xff) + sd->header_read++; + break; + case 1: + if (data[i] == 0xff) + sd->header_read++; + else + sd->header_read = 0; + break; + case 2: + if (data[i] == 0x00) + sd->header_read++; + else if (data[i] != 0xff) + sd->header_read = 0; + break; + case 3: + if (data[i] == 0xc4) + sd->header_read++; + else if (data[i] == 0xff) + sd->header_read = 1; + else + sd->header_read = 0; + break; + case 4: + if (data[i] == 0xc4) + sd->header_read++; + else if (data[i] == 0xff) + sd->header_read = 1; + else + sd->header_read = 0; + break; + case 5: + if (data[i] == 0x96) + sd->header_read++; + else if (data[i] == 0xff) + sd->header_read = 1; + else + sd->header_read = 0; + break; + default: + sd->header[sd->header_read - 6] = data[i]; + sd->header_read++; + if (sd->header_read == header_size) { + sd->header_read = 0; + return data + i + 1; + } + } + } + return NULL; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int fr_h_sz = 0, lum_offset = 0, len_after_sof = 0; + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + u8 *sof; + + sof = find_sof(gspca_dev, data, len); + if (sof) { + if (sd->bridge == BRIDGE_103) { + fr_h_sz = 18; + lum_offset = 3; + } else { + fr_h_sz = 12; + lum_offset = 2; + } + + len_after_sof = len - (sof - data); + len = (sof - data) - fr_h_sz; + if (len < 0) + len = 0; + } + + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { + /* In raw mode we sometimes get some garbage after the frame + ignore this */ + int used; + int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; + + used = gspca_dev->image_len; + if (used + len > size) + len = size - used; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + + if (sof) { + int lum = sd->header[lum_offset] + + (sd->header[lum_offset + 1] << 8); + + /* When exposure changes midway a frame we + get a lum of 0 in this case drop 2 frames + as the frames directly after an exposure + change have an unstable image. Sometimes lum + *really* is 0 (cam used in low light with + low exposure setting), so do not drop frames + if the previous lum was 0 too. */ + if (lum == 0 && sd->prev_avg_lum != 0) { + lum = -1; + sd->frames_to_drop = 2; + sd->prev_avg_lum = 0; + } else + sd->prev_avg_lum = lum; + atomic_set(&sd->avg_lum, lum); + + if (sd->frames_to_drop) + sd->frames_to_drop--; + else + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + gspca_frame_add(gspca_dev, FIRST_PACKET, sof, len_after_sof); + } +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + + if (len == 1 && data[0] == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, + .dq_callback = do_autogain, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +/* -- module initialisation -- */ +#define SB(sensor, bridge) \ + .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge + + +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */ + {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, + {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, + {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, + {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, + {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, +#endif + {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, + {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, + {USB_DEVICE(0x0c45, 0x602a), SB(HV7131D, 102)}, + /* {USB_DEVICE(0x0c45, 0x602b), SB(MI0343, 102)}, */ + {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, + {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, + {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)}, + /* {USB_DEVICE(0x0c45, 0x6030), SB(MI03XX, 102)}, */ /* MI0343 MI0360 MI0330 */ + /* {USB_DEVICE(0x0c45, 0x6082), SB(MI03XX, 103)}, */ /* MI0343 MI0360 */ + {USB_DEVICE(0x0c45, 0x6083), SB(HV7131D, 103)}, + {USB_DEVICE(0x0c45, 0x608c), SB(HV7131R, 103)}, + /* {USB_DEVICE(0x0c45, 0x608e), SB(CISVF10, 103)}, */ + {USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)}, + {USB_DEVICE(0x0c45, 0x60a8), SB(PAS106, 103)}, + {USB_DEVICE(0x0c45, 0x60aa), SB(TAS5130CXX, 103)}, + {USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)}, + {USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c new file mode 100644 index 000000000000..150b2df40f7f --- /dev/null +++ b/drivers/media/usb/gspca/sonixj.c @@ -0,0 +1,3206 @@ +/* + * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver + * + * Copyright (C) 2009-2011 Jean-François Moine + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "sonixj" + +#include +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Jean-François Moine "); +MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* controls */ +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + COLORS, + BLUE, + RED, + GAMMA, + EXPOSURE, + AUTOGAIN, + GAIN, + HFLIP, + VFLIP, + SHARPNESS, + ILLUM, + FREQ, + NCTRLS /* number of controls */ +}; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct gspca_ctrl ctrls[NCTRLS]; + + atomic_t avg_lum; + u32 exposure; + + struct work_struct work; + struct workqueue_struct *work_thread; + + u32 pktsz; /* (used by pkt_scan) */ + u16 npkt; + s8 nchg; + s8 short_mark; + + u8 quality; /* image quality */ +#define QUALITY_MIN 25 +#define QUALITY_MAX 90 +#define QUALITY_DEF 70 + + u8 reg01; + u8 reg17; + u8 reg18; + u8 flags; + + s8 ag_cnt; +#define AG_CNT_START 13 + + u8 bridge; +#define BRIDGE_SN9C102P 0 +#define BRIDGE_SN9C105 1 +#define BRIDGE_SN9C110 2 +#define BRIDGE_SN9C120 3 + u8 sensor; /* Type of image sensor chip */ + u8 i2c_addr; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; +enum sensors { + SENSOR_ADCM1700, + SENSOR_GC0307, + SENSOR_HV7131R, + SENSOR_MI0360, + SENSOR_MI0360B, + SENSOR_MO4000, + SENSOR_MT9V111, + SENSOR_OM6802, + SENSOR_OV7630, + SENSOR_OV7648, + SENSOR_OV7660, + SENSOR_PO1030, + SENSOR_PO2030N, + SENSOR_SOI768, + SENSOR_SP80708, +}; + +static void qual_upd(struct work_struct *work); + +/* device flags */ +#define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */ +#define F_ILLUM 0x02 /* presence of illuminator */ + +/* sn9c1xx definitions */ +/* register 0x01 */ +#define S_PWR_DN 0x01 /* sensor power down */ +#define S_PDN_INV 0x02 /* inverse pin S_PWR_DN */ +#define V_TX_EN 0x04 /* video transfer enable */ +#define LED 0x08 /* output to pin LED */ +#define SCL_SEL_OD 0x20 /* open-drain mode */ +#define SYS_SEL_48M 0x40 /* system clock 0: 24MHz, 1: 48MHz */ +/* register 0x17 */ +#define MCK_SIZE_MASK 0x1f /* sensor master clock */ +#define SEN_CLK_EN 0x20 /* enable sensor clock */ +#define DEF_EN 0x80 /* defect pixel by 0: soft, 1: hard */ + +/* V4L2 controls supported by the driver */ +static void setbrightness(struct gspca_dev *gspca_dev); +static void setcontrast(struct gspca_dev *gspca_dev); +static void setcolors(struct gspca_dev *gspca_dev); +static void setredblue(struct gspca_dev *gspca_dev); +static void setgamma(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static void setgain(struct gspca_dev *gspca_dev); +static void sethvflip(struct gspca_dev *gspca_dev); +static void setsharpness(struct gspca_dev *gspca_dev); +static void setillum(struct gspca_dev *gspca_dev); +static void setfreq(struct gspca_dev *gspca_dev); + +static const struct ctrl sd_ctrls[NCTRLS] = { +[BRIGHTNESS] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x80, + }, + .set_control = setbrightness + }, +[CONTRAST] = { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, +#define CONTRAST_MAX 127 + .maximum = CONTRAST_MAX, + .step = 1, + .default_value = 20, + }, + .set_control = setcontrast + }, +[COLORS] = { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 40, + .step = 1, +#define COLORS_DEF 25 + .default_value = COLORS_DEF, + }, + .set_control = setcolors + }, +[BLUE] = { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 24, + .maximum = 40, + .step = 1, + .default_value = 32, + }, + .set_control = setredblue + }, +[RED] = { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 24, + .maximum = 40, + .step = 1, + .default_value = 32, + }, + .set_control = setredblue + }, +[GAMMA] = { + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 40, + .step = 1, +#define GAMMA_DEF 20 + .default_value = GAMMA_DEF, + }, + .set_control = setgamma + }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 500, + .maximum = 1500, + .step = 1, + .default_value = 1024 + }, + .set_control = setexposure + }, +[AUTOGAIN] = { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = sd_setautogain, + }, +[GAIN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 4, + .maximum = 49, + .step = 1, + .default_value = 15 + }, + .set_control = setgain + }, +[HFLIP] = { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set_control = sethvflip + }, +[VFLIP] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set_control = sethvflip + }, +[SHARPNESS] = { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 90, + }, + .set_control = setsharpness + }, +[ILLUM] = { + { + .id = V4L2_CID_ILLUMINATORS_1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Illuminator / infrared", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set_control = setillum + }, +/* ov7630/ov7648/ov7660 only */ +[FREQ] = { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set_control = setfreq + }, +}; + +/* table of the disabled controls */ +static const __u32 ctrl_dis[] = { +[SENSOR_ADCM1700] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_GC0307] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_HV7131R] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << FREQ), + +[SENSOR_MI0360] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_MI0360B] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_MO4000] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_MT9V111] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_OM6802] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_OV7630] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP), + +[SENSOR_OV7648] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP), + +[SENSOR_OV7660] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP), + +[SENSOR_PO1030] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_PO2030N] = (1 << FREQ), + +[SENSOR_SOI768] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), + +[SENSOR_SP80708] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | + (1 << HFLIP) | + (1 << VFLIP) | + (1 << FREQ), +}; + +static const struct v4l2_pix_format cif_mode[] = { + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */ + .sizeimage = 640 * 480 * 3 / 4 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const u8 sn_adcm1700[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + +static const u8 sn_gc0307[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x61, 0x62, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x03, 0x01, 0x08, 0x28, 0x1e, 0x02, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + +static const u8 sn_hv7131[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x03, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, +/* reg18 reg19 reg1a reg1b */ + 0x0a, 0x00, 0x00, 0x00 +}; + +static const u8 sn_mi0360[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + +static const u8 sn_mi0360b[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x40, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + +static const u8 sn_mo4000[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40, +/* reg18 reg19 reg1a reg1b */ + 0x08, 0x00, 0x00, 0x00 +}; + +static const u8 sn_mt9v111[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + +static const u8 sn_om6802[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40, +/* reg18 reg19 reg1a reg1b */ + 0x05, 0x00, 0x00, 0x00 +}; + +static const u8 sn_ov7630[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, +/* reg18 reg19 reg1a reg1b */ + 0x0b, 0x00, 0x00, 0x00 +}; + +static const u8 sn_ov7648[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x0b, 0x00, 0x00, 0x00 +}; + +static const u8 sn_ov7660[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +static const u8 sn_po1030[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +static const u8 sn_po2030n[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x14, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +static const u8 sn_soi768[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x08, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +static const u8 sn_sp80708[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +/* sequence specific to the sensors - !! index = SENSOR_xxx */ +static const u8 *sn_tb[] = { +[SENSOR_ADCM1700] = sn_adcm1700, +[SENSOR_GC0307] = sn_gc0307, +[SENSOR_HV7131R] = sn_hv7131, +[SENSOR_MI0360] = sn_mi0360, +[SENSOR_MI0360B] = sn_mi0360b, +[SENSOR_MO4000] = sn_mo4000, +[SENSOR_MT9V111] = sn_mt9v111, +[SENSOR_OM6802] = sn_om6802, +[SENSOR_OV7630] = sn_ov7630, +[SENSOR_OV7648] = sn_ov7648, +[SENSOR_OV7660] = sn_ov7660, +[SENSOR_PO1030] = sn_po1030, +[SENSOR_PO2030N] = sn_po2030n, +[SENSOR_SOI768] = sn_soi768, +[SENSOR_SP80708] = sn_sp80708, +}; + +/* default gamma table */ +static const u8 gamma_def[17] = { + 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, + 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff +}; +/* gamma for sensor ADCM1700 */ +static const u8 gamma_spec_0[17] = { + 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4, + 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5 +}; +/* gamma for sensors HV7131R and MT9V111 */ +static const u8 gamma_spec_1[17] = { + 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d, + 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5 +}; +/* gamma for sensor GC0307 */ +static const u8 gamma_spec_2[17] = { + 0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab, + 0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb +}; +/* gamma for sensor SP80708 */ +static const u8 gamma_spec_3[17] = { + 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab, + 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6 +}; + +/* color matrix and offsets */ +static const u8 reg84[] = { + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */ + 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */ + 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */ + 0x00, 0x00, 0x00 /* YUV offsets */ +}; + +#define DELAY 0xdd + +static const u8 adcm1700_sensor_init[][8] = { + {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 adcm1700_sensor_param1[][8] = { + {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */ + {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10}, + + {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10}, + {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */ + + {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10}, + {} +}; +static const u8 gc0307_sensor_init[][8] = { + {0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/ + {0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 gc0307_sensor_param1[][8] = { + {0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10}, + {0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10}, + {0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10}, +/*param3*/ + {0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10}, + {} +}; + +static const u8 hv7131r_sensor_init[][8] = { + {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10}, + {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10}, +/* {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, */ + {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10}, +/* {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, */ + + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x11, 0x25, 0x00, 0x61, 0xa8, 0x00, 0x10}, + {0xa1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x11, 0x31, 0x20, 0x2e, 0x20, 0x00, 0x10}, + {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10}, + {0xa1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */ + {0xc1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */ + + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, + + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* set sensor clock */ + {} +}; +static const u8 mi0360_sensor_init[][8] = { + {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, + {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10}, + {0xd1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, + {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10}, + {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, + {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10}, + {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, + {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10}, + {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, + + {0xb1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x2b, 0x00, 0xa0, 0x00, 0xb0, 0x10}, + {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0xa0, 0x10}, + + {0xb1, 0x5d, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */ + {0xb1, 0x5d, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ + + {0xd1, 0x5d, 0x2b, 0x00, 0xb9, 0x00, 0xe3, 0x10}, + {0xd1, 0x5d, 0x2d, 0x00, 0x5f, 0x00, 0xb9, 0x10}, /* 42 */ +/* {0xb1, 0x5d, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */ +/* {0xb1, 0x5d, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */ + {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ + {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ + {} +}; +static const u8 mi0360b_sensor_init[][8] = { + {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/ + {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/ + {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, + {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10}, + {0xd1, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, + {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10}, + {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, + {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10}, + {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, + {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10}, + {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, + + {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10}, + {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10}, + {} +}; +static const u8 mi0360b_sensor_param1[][8] = { + {0xb1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x06, 0x00, 0x53, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ + + {0xd1, 0x5d, 0x2b, 0x00, 0xd1, 0x01, 0xc9, 0x10}, + {0xd1, 0x5d, 0x2d, 0x00, 0xed, 0x00, 0xd1, 0x10}, + {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ + {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ + {} +}; +static const u8 mo4000_sensor_init[][8] = { + {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 mt9v111_sensor_init[][8] = { + {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */ + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */ + {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */ + {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */ + {0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */ + {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */ + {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */ + {0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */ + {0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */ + {0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */ + {0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */ + {0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */ + {0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */ + {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */ + {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */ + {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 mt9v111_sensor_param1[][8] = { + {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xad, 0x10}, /* G1 and B gains */ + {0xd1, 0x5c, 0x2d, 0x00, 0xad, 0x00, 0x33, 0x10}, /* R and G2 gains */ + {0xb1, 0x5c, 0x06, 0x00, 0x40, 0x00, 0x00, 0x10}, /* vert blanking */ + {0xb1, 0x5c, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, /* horiz blanking */ + {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */ + {} +}; +static const u8 om6802_init0[2][8] = { +/*fixme: variable*/ + {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10}, +}; +static const u8 om6802_sensor_init[][8] = { + {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10}, + /* factory mode */ + {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* output raw RGB */ + {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, +/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */ + {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10}, + /* auto-exposure speed (0) / white balance mode (auto RGB) */ +/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10}, + * set color mode */ +/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10}, + * max AGC value in AE */ +/* {0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset AGC */ +/* {0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset brightness */ +/* {0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset contrast */ +/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10}, + * preset gamma */ + {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10}, + /* luminance mode (0x4f -> AutoExpo on) */ + {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10}, + /* preset shutter */ +/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10}, + * auto frame rate */ +/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */ + {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 om6802_sensor_param1[][8] = { + {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 ov7630_sensor_init[][8] = { + {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, +/* win: i2c_r from 00 to 80 */ + {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, + {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10}, +/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30) + 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */ + {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10}, + {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10}, + {0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10}, + {0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10}, + {0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10}, + {0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10}, + {0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10}, + {0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10}, + {0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10}, + {0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10}, + {0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10}, + {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 ov7630_sensor_param1[][8] = { + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, +/*fixme: + 0x12, 0x04*/ +/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN + * set by setvflip */ + {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10}, +/* */ +/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ +/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ +/* */ + {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ + {} +}; + +static const u8 ov7648_sensor_init[][8] = { + {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10}, + {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10}, + {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10}, + {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10}, + {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10}, + {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10}, + {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10}, + {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10}, + {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10}, + {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10}, + {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10}, + {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10}, + {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10}, + + {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10}, +/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ +/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ +/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */ + {} +}; +static const u8 ov7648_sensor_param1[][8] = { +/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN + * set by setvflip */ + {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */ +/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */ +/*...*/ + {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */ +/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */ + + {} +}; + +static const u8 ov7660_sensor_init[][8] = { + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, + /* Outformat = rawRGB */ + {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ + {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, + /* GAIN BLUE RED VREF */ + {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, + /* COM 1 BAVE GEAVE AECHH */ + {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */ + {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */ + {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, + /* AECH CLKRC COM7 COM8 */ + {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */ + {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10}, + /* HSTART HSTOP VSTRT VSTOP */ + {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */ + {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */ + {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10}, + /* BOS GBOS GROS ROS (BGGR offset) */ +/* {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, */ + {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10}, + /* AEW AEB VPT BBIAS */ + {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10}, + /* GbBIAS RSVD EXHCH EXHCL */ + {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10}, + /* RBIAS ADVFL ASDVFH YAVE */ + {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10}, + /* HSYST HSYEN HREF */ + {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10}, + /* ADC ACOM OFON TSLB */ + {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10}, + /* COM11 COM12 COM13 COM14 */ + {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10}, + /* EDGE COM15 COM16 COM17 */ + {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */ + {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */ + {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */ + {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */ + {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */ + {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */ + {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10}, + /* LCC1 LCC2 LCC3 LCC4 */ + {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */ + {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, /* MANU */ + {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10}, + /* band gap reference [0:3] DBLV */ + {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ + {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */ +/* not in all ms-win traces*/ + {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 ov7660_sensor_param1[][8] = { + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */ + /* bits[3..0]reserved */ + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + /* VREF vertical frame ctrl */ + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* AECH 0x20 */ + {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFL */ + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFH */ + {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */ +/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */ +/****** (some exchanges in the win trace) ******/ +/*fixme:param2*/ + {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ + {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */ + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */ + {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCL */ +/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */ +/****** (some exchanges in the win trace) ******/ +/******!! startsensor KO if changed !!****/ +/*fixme: param3*/ + {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10}, + {} +}; + +static const u8 po1030_sensor_init[][8] = { +/* the sensor registers are described in m5602/m5602_po1030.h */ + {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */ + {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10}, + {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10}, + {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, + {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */ + {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10}, + {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */ + {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10}, + {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */ + {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10}, + {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10}, + {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, + {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10}, + {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10}, + {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10}, + {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */ + {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10}, + + {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10}, + {} +}; +static const u8 po1030_sensor_param1[][8] = { +/* from ms-win traces - these values change with auto gain/expo/wb.. */ + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, +/* mean values */ + {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */ + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */ + {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */ + + {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */ + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */ + {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10}, +/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */ + {} +}; + +static const u8 po2030n_sensor_init[][8] = { + {0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ + {0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ + {0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10}, + {0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10}, + {0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10}, + {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10}, + {0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10}, + {0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10}, + {0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10}, + {0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10}, + {0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10}, + {0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10}, + {0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10}, + {0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10}, + {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10}, + {0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10}, + {} +}; +static const u8 po2030n_sensor_param1[][8] = { + {0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10}, + {DELAY, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */ + {0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x40, 0x10}, /* RGBG gains */ +/*param2*/ + {0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, + {} +}; + +static const u8 soi768_sensor_init[][8] = { + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {DELAY, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */ + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 soi768_sensor_param1[][8] = { + {0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10}, +/* */ +/* {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */ + {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */ + {0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10}, +/* the next sequence should be used for auto gain */ + {0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10}, + /* global gain ? : 07 - change with 0x15 at the end */ + {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */ + {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10}, + /* exposure ? : 0200 - change with 0x1e at the end */ + {} +}; + +static const u8 sp80708_sensor_init[][8] = { + {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 sp80708_sensor_param1[][8] = { + {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10}, + {} +}; + +static const u8 (*sensor_init[])[8] = { +[SENSOR_ADCM1700] = adcm1700_sensor_init, +[SENSOR_GC0307] = gc0307_sensor_init, +[SENSOR_HV7131R] = hv7131r_sensor_init, +[SENSOR_MI0360] = mi0360_sensor_init, +[SENSOR_MI0360B] = mi0360b_sensor_init, +[SENSOR_MO4000] = mo4000_sensor_init, +[SENSOR_MT9V111] = mt9v111_sensor_init, +[SENSOR_OM6802] = om6802_sensor_init, +[SENSOR_OV7630] = ov7630_sensor_init, +[SENSOR_OV7648] = ov7648_sensor_init, +[SENSOR_OV7660] = ov7660_sensor_init, +[SENSOR_PO1030] = po1030_sensor_init, +[SENSOR_PO2030N] = po2030n_sensor_init, +[SENSOR_SOI768] = soi768_sensor_init, +[SENSOR_SP80708] = sp80708_sensor_init, +}; + +/* read bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + u16 value, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + pr_err("reg_r: buffer overflow\n"); + return; + } +#endif + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + gspca_dev->usb_buf, len, + 500); + PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w1(struct gspca_dev *gspca_dev, + u16 value, + u8 data) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data); + gspca_dev->usb_buf[0] = data; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, + gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_w1 err %d\n", ret); + gspca_dev->usb_err = ret; + } +} +static void reg_w(struct gspca_dev *gspca_dev, + u16 value, + const u8 *buffer, + int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..", + value, buffer[0], buffer[1]); +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + pr_err("reg_w: buffer overflow\n"); + return; + } +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* I2C write 1 byte */ +static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val); + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_OM6802: + case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ + gspca_dev->usb_buf[0] = 0x80 | (2 << 4); + break; + default: /* i2c command = a1 (400 kHz) */ + gspca_dev->usb_buf[0] = 0x81 | (2 << 4); + break; + } + gspca_dev->usb_buf[1] = sd->i2c_addr; + gspca_dev->usb_buf[2] = reg; + gspca_dev->usb_buf[3] = val; + gspca_dev->usb_buf[4] = 0; + gspca_dev->usb_buf[5] = 0; + gspca_dev->usb_buf[6] = 0; + gspca_dev->usb_buf[7] = 0x10; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x08, /* value = i2c */ + 0, + gspca_dev->usb_buf, 8, + 500); + if (ret < 0) { + pr_err("i2c_w1 err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* I2C write 8 bytes */ +static void i2c_w8(struct gspca_dev *gspca_dev, + const u8 *buffer) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..", + buffer[2], buffer[3]); + memcpy(gspca_dev->usb_buf, buffer, 8); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x08, 0, /* value, index */ + gspca_dev->usb_buf, 8, + 500); + msleep(2); + if (ret < 0) { + pr_err("i2c_w8 err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */ +static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 mode[8]; + + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_OM6802: + case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ + mode[0] = 0x80 | 0x10; + break; + default: /* i2c command = 91 (400 kHz) */ + mode[0] = 0x81 | 0x10; + break; + } + mode[1] = sd->i2c_addr; + mode[2] = reg; + mode[3] = 0; + mode[4] = 0; + mode[5] = 0; + mode[6] = 0; + mode[7] = 0x10; + i2c_w8(gspca_dev, mode); + msleep(2); + mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02; + mode[2] = 0; + i2c_w8(gspca_dev, mode); + msleep(2); + reg_r(gspca_dev, 0x0a, 5); +} + +static void i2c_w_seq(struct gspca_dev *gspca_dev, + const u8 (*data)[8]) +{ + while ((*data)[0] != 0) { + if ((*data)[0] != DELAY) + i2c_w8(gspca_dev, *data); + else + msleep((*data)[1]); + data++; + } +} + +/* check the ID of the hv7131 sensor */ +/* this sequence is needed because it activates the sensor */ +static void hv7131r_probe(struct gspca_dev *gspca_dev) +{ + i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ + msleep(10); + reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ + msleep(10); + i2c_r(gspca_dev, 0, 5); /* read sensor id */ + if (gspca_dev->usb_buf[0] == 0x02 /* chip ID (02 is R) */ + && gspca_dev->usb_buf[1] == 0x09 + && gspca_dev->usb_buf[2] == 0x01) { + PDEBUG(D_PROBE, "Sensor HV7131R found"); + return; + } + pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); +} + +static void mi0360_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, j; + u16 val = 0; + static const u8 probe_tb[][4][8] = { + { /* mi0360 */ + {0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10} + }, + { /* mt9v111 */ + {0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, + {0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} + }, + }; + + for (i = 0; i < ARRAY_SIZE(probe_tb); i++) { + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + for (j = 0; j < 3; j++) + i2c_w8(gspca_dev, probe_tb[i][j]); + msleep(2); + reg_r(gspca_dev, 0x0a, 5); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + if (probe_tb[i][3][0] != 0) + i2c_w8(gspca_dev, probe_tb[i][3]); + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val != 0xffff) + break; + } + if (gspca_dev->usb_err < 0) + return; + switch (val) { + case 0x8221: + PDEBUG(D_PROBE, "Sensor mi0360b"); + sd->sensor = SENSOR_MI0360B; + break; + case 0x823a: + PDEBUG(D_PROBE, "Sensor mt9v111"); + sd->sensor = SENSOR_MT9V111; + break; + case 0x8243: + PDEBUG(D_PROBE, "Sensor mi0360"); + break; + default: + PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val); + break; + } +} + +static void ov7630_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 val; + + /* check ov76xx */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x0a, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (gspca_dev->usb_err < 0) + return; + if (val == 0x7628) { /* soi768 */ + sd->sensor = SENSOR_SOI768; +/*fixme: only valid for 0c45:613e?*/ + gspca_dev->cam.input_flags = + V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP; + PDEBUG(D_PROBE, "Sensor soi768"); + return; + } + PDEBUG(D_PROBE, "Sensor ov%04x", val); +} + +static void ov7648_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 val; + + /* check ov76xx */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x0a, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if ((val & 0xff00) == 0x7600) { /* ov76xx */ + PDEBUG(D_PROBE, "Sensor ov%04x", val); + return; + } + + /* check po1030 */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x6e; + i2c_r(gspca_dev, 0x00, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (gspca_dev->usb_err < 0) + return; + if (val == 0x1030) { /* po1030 */ + PDEBUG(D_PROBE, "Sensor po1030"); + sd->sensor = SENSOR_PO1030; + return; + } + pr_err("Unknown sensor %04x\n", val); +} + +/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */ +static void po2030n_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 val; + + /* check gc0307 */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + reg_w1(gspca_dev, 0x02, 0x22); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x00, 1); + val = gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); /* reset */ + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x99) { /* gc0307 (?) */ + PDEBUG(D_PROBE, "Sensor gc0307"); + sd->sensor = SENSOR_GC0307; + return; + } + + /* check po2030n */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x0a); + sd->i2c_addr = 0x6e; + i2c_r(gspca_dev, 0x00, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (gspca_dev->usb_err < 0) + return; + if (val == 0x2030) { + PDEBUG(D_PROBE, "Sensor po2030n"); +/* sd->sensor = SENSOR_PO2030N; */ + } else { + pr_err("Unknown sensor ID %04x\n", val); + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + sd->bridge = id->driver_info >> 16; + sd->sensor = id->driver_info >> 8; + sd->flags = id->driver_info; + + cam = &gspca_dev->cam; + if (sd->sensor == SENSOR_ADCM1700) { + cam->cam_mode = cif_mode; + cam->nmodes = ARRAY_SIZE(cif_mode); + } else { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + cam->npkt = 24; /* 24 packets per ISOC message */ + cam->ctrls = sd->ctrls; + + sd->ag_cnt = -1; + sd->quality = QUALITY_DEF; + + INIT_WORK(&sd->work, qual_upd); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const u8 *sn9c1xx; + u8 regGpio[] = { 0x29, 0x70 }; /* no audio */ + u8 regF1; + + /* setup a selector by bridge */ + reg_w1(gspca_dev, 0xf1, 0x01); + reg_r(gspca_dev, 0x00, 1); + reg_w1(gspca_dev, 0xf1, 0x00); + reg_r(gspca_dev, 0x00, 1); /* get sonix chip id */ + regF1 = gspca_dev->usb_buf[0]; + if (gspca_dev->usb_err < 0) + return gspca_dev->usb_err; + PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1); + if (gspca_dev->audio) + regGpio[1] |= 0x04; /* with audio */ + switch (sd->bridge) { + case BRIDGE_SN9C102P: + case BRIDGE_SN9C105: + if (regF1 != 0x11) + return -ENODEV; + break; + default: +/* case BRIDGE_SN9C110: */ +/* case BRIDGE_SN9C120: */ + if (regF1 != 0x12) + return -ENODEV; + } + + switch (sd->sensor) { + case SENSOR_MI0360: + mi0360_probe(gspca_dev); + break; + case SENSOR_OV7630: + ov7630_probe(gspca_dev); + break; + case SENSOR_OV7648: + ov7648_probe(gspca_dev); + break; + case SENSOR_PO2030N: + po2030n_probe(gspca_dev); + break; + } + + switch (sd->bridge) { + case BRIDGE_SN9C102P: + reg_w1(gspca_dev, 0x02, regGpio[1]); + break; + default: + reg_w(gspca_dev, 0x01, regGpio, 2); + break; + } + + if (sd->sensor == SENSOR_OM6802) + sd->ctrls[SHARPNESS].def = 0x10; + + /* Note we do not disable the sensor clock here (power saving mode), + as that also disables the button on the cam. */ + reg_w1(gspca_dev, 0xf1, 0x00); + + /* set the i2c address */ + sn9c1xx = sn_tb[sd->sensor]; + sd->i2c_addr = sn9c1xx[9]; + + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; + if (!(sd->flags & F_ILLUM)) + gspca_dev->ctrl_dis |= (1 << ILLUM); + + return gspca_dev->usb_err; +} + +static u32 expo_adjust(struct gspca_dev *gspca_dev, + u32 expo) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_GC0307: { + int a, b; + + /* expo = 0..255 -> a = 19..43 */ + a = 19 + expo * 25 / 256; + i2c_w1(gspca_dev, 0x68, a); + a -= 12; + b = a * a * 4; /* heuristic */ + i2c_w1(gspca_dev, 0x03, b >> 8); + i2c_w1(gspca_dev, 0x04, b); + break; + } + case SENSOR_HV7131R: { + u8 Expodoit[] = + { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 }; + + Expodoit[3] = expo >> 16; + Expodoit[4] = expo >> 8; + Expodoit[5] = expo; + i2c_w8(gspca_dev, Expodoit); + break; + } + case SENSOR_MI0360: + case SENSOR_MI0360B: { + u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ + { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 }; + static const u8 doit[] = /* update sensor */ + { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; + static const u8 sensorgo[] = /* sensor on */ + { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; + + if (expo > 0x0635) + expo = 0x0635; + else if (expo < 0x0001) + expo = 0x0001; + expoMi[3] = expo >> 8; + expoMi[4] = expo; + i2c_w8(gspca_dev, expoMi); + i2c_w8(gspca_dev, doit); + i2c_w8(gspca_dev, sensorgo); + break; + } + case SENSOR_MO4000: { + u8 expoMof[] = + { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 }; + u8 expoMo10[] = + { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 }; + static const u8 gainMo[] = + { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; + + if (expo > 0x1fff) + expo = 0x1fff; + else if (expo < 0x0001) + expo = 0x0001; + expoMof[3] = (expo & 0x03fc) >> 2; + i2c_w8(gspca_dev, expoMof); + expoMo10[3] = ((expo & 0x1c00) >> 10) + | ((expo & 0x0003) << 4); + i2c_w8(gspca_dev, expoMo10); + i2c_w8(gspca_dev, gainMo); + PDEBUG(D_FRAM, "set exposure %d", + ((expoMo10[3] & 0x07) << 10) + | (expoMof[3] << 2) + | ((expoMo10[3] & 0x30) >> 4)); + break; + } + case SENSOR_MT9V111: { + u8 expo_c1[] = + { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 }; + + if (expo > 0x0390) + expo = 0x0390; + else if (expo < 0x0060) + expo = 0x0060; + expo_c1[3] = expo >> 8; + expo_c1[4] = expo; + i2c_w8(gspca_dev, expo_c1); + break; + } + case SENSOR_OM6802: { + u8 gainOm[] = + { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; + /* preset AGC - works when AutoExpo = off */ + + if (expo > 0x03ff) + expo = 0x03ff; + if (expo < 0x0001) + expo = 0x0001; + gainOm[3] = expo >> 2; + i2c_w8(gspca_dev, gainOm); + reg_w1(gspca_dev, 0x96, expo >> 5); + PDEBUG(D_FRAM, "set exposure %d", gainOm[3]); + break; + } + } + return expo; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned int expo; + int brightness; + u8 k2; + + brightness = sd->ctrls[BRIGHTNESS].val; + k2 = (brightness - 0x80) >> 2; + switch (sd->sensor) { + case SENSOR_ADCM1700: + if (k2 > 0x1f) + k2 = 0; /* only positive Y offset */ + break; + case SENSOR_HV7131R: + expo = brightness << 12; + if (expo > 0x002dc6c0) + expo = 0x002dc6c0; + else if (expo < 0x02a0) + expo = 0x02a0; + sd->exposure = expo_adjust(gspca_dev, expo); + break; + case SENSOR_MI0360: + case SENSOR_MO4000: + expo = brightness << 4; + sd->exposure = expo_adjust(gspca_dev, expo); + break; + case SENSOR_MI0360B: + expo = brightness << 2; + sd->exposure = expo_adjust(gspca_dev, expo); + break; + case SENSOR_GC0307: + expo = brightness; + sd->exposure = expo_adjust(gspca_dev, expo); + return; /* don't set the Y offset */ + case SENSOR_MT9V111: + expo = brightness << 2; + sd->exposure = expo_adjust(gspca_dev, expo); + return; /* don't set the Y offset */ + case SENSOR_OM6802: + expo = brightness << 2; + sd->exposure = expo_adjust(gspca_dev, expo); + return; /* Y offset already set */ + } + + reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 k2; + u8 contrast[6]; + + k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1) + + 37; /* 37..73 */ + contrast[0] = (k2 + 1) / 2; /* red */ + contrast[1] = 0; + contrast[2] = k2; /* green */ + contrast[3] = 0; + contrast[4] = k2 / 5; /* blue */ + contrast[5] = 0; + reg_w(gspca_dev, 0x84, contrast, sizeof contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v, colors; + const s16 *uv; + u8 reg8a[12]; /* U & V gains */ + static const s16 uv_com[6] = { /* same as reg84 in signed decimal */ + -24, -38, 64, /* UR UG UB */ + 62, -51, -9 /* VR VG VB */ + }; + static const s16 uv_mi0360b[6] = { + -20, -38, 64, /* UR UG UB */ + 60, -51, -9 /* VR VG VB */ + }; + + colors = sd->ctrls[COLORS].val; + if (sd->sensor == SENSOR_MI0360B) + uv = uv_mi0360b; + else + uv = uv_com; + for (i = 0; i < 6; i++) { + v = uv[i] * colors / COLORS_DEF; + reg8a[i * 2] = v; + reg8a[i * 2 + 1] = (v >> 8) & 0x0f; + } + reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a); +} + +static void setredblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PO2030N) { + u8 rg1b[] = /* red green1 blue (no g2) */ + {0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10}; + + /* 0x40 = normal value = gain x 1 */ + rg1b[3] = sd->ctrls[RED].val * 2; + rg1b[5] = sd->ctrls[BLUE].val * 2; + i2c_w8(gspca_dev, rg1b); + return; + } + reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val); +/* reg_w1(gspca_dev, 0x07, 32); */ + reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val); +} + +static void setgamma(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, val; + u8 gamma[17]; + const u8 *gamma_base; + static const u8 delta[17] = { + 0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a, + 0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00 + }; + + switch (sd->sensor) { + case SENSOR_ADCM1700: + gamma_base = gamma_spec_0; + break; + case SENSOR_HV7131R: + case SENSOR_MI0360B: + case SENSOR_MT9V111: + gamma_base = gamma_spec_1; + break; + case SENSOR_GC0307: + gamma_base = gamma_spec_2; + break; + case SENSOR_SP80708: + gamma_base = gamma_spec_3; + break; + default: + gamma_base = gamma_def; + break; + } + + val = sd->ctrls[GAMMA].val; + for (i = 0; i < sizeof gamma; i++) + gamma[i] = gamma_base[i] + + delta[i] * (val - GAMMA_DEF) / 32; + reg_w(gspca_dev, 0x20, gamma, sizeof gamma); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PO2030N) { + u8 rexpo[] = /* 1a: expo H, 1b: expo M */ + {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10}; + + rexpo[3] = sd->ctrls[EXPOSURE].val >> 8; + i2c_w8(gspca_dev, rexpo); + msleep(6); + rexpo[2] = 0x1b; + rexpo[3] = sd->ctrls[EXPOSURE].val; + i2c_w8(gspca_dev, rexpo); + } +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN)) + return; + switch (sd->sensor) { + case SENSOR_OV7630: + case SENSOR_OV7648: { + u8 comb; + + if (sd->sensor == SENSOR_OV7630) + comb = 0xc0; + else + comb = 0xa0; + if (sd->ctrls[AUTOGAIN].val) + comb |= 0x03; + i2c_w1(&sd->gspca_dev, 0x13, comb); + return; + } + } + if (sd->ctrls[AUTOGAIN].val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PO2030N) { + u8 rgain[] = /* 15: gain */ + {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15}; + + rgain[3] = sd->ctrls[GAIN].val; + i2c_w8(gspca_dev, rgain); + } +} + +static void sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 comn; + + switch (sd->sensor) { + case SENSOR_HV7131R: + comn = 0x18; /* clkdiv = 1, ablcen = 1 */ + if (sd->ctrls[VFLIP].val) + comn |= 0x01; + i2c_w1(gspca_dev, 0x01, comn); /* sctra */ + break; + case SENSOR_OV7630: + comn = 0x02; + if (!sd->ctrls[VFLIP].val) + comn |= 0x80; + i2c_w1(gspca_dev, 0x75, comn); + break; + case SENSOR_OV7648: + comn = 0x06; + if (sd->ctrls[VFLIP].val) + comn |= 0x80; + i2c_w1(gspca_dev, 0x75, comn); + break; + case SENSOR_PO2030N: + /* Reg. 0x1E: Timing Generator Control Register 2 (Tgcontrol2) + * (reset value: 0x0A) + * bit7: HM: Horizontal Mirror: 0: disable, 1: enable + * bit6: VM: Vertical Mirror: 0: disable, 1: enable + * bit5: ST: Shutter Selection: 0: electrical, 1: mechanical + * bit4: FT: Single Frame Transfer: 0: disable, 1: enable + * bit3-0: X + */ + comn = 0x0a; + if (sd->ctrls[HFLIP].val) + comn |= 0x80; + if (sd->ctrls[VFLIP].val) + comn |= 0x40; + i2c_w1(&sd->gspca_dev, 0x1e, comn); + break; + } +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val); +} + +static void setillum(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << ILLUM)) + return; + switch (sd->sensor) { + case SENSOR_ADCM1700: + reg_w1(gspca_dev, 0x02, /* gpio */ + sd->ctrls[ILLUM].val ? 0x64 : 0x60); + break; + case SENSOR_MT9V111: + reg_w1(gspca_dev, 0x02, + sd->ctrls[ILLUM].val ? 0x77 : 0x74); +/* should have been: */ +/* 0x55 : 0x54); * 370i */ +/* 0x66 : 0x64); * Clip */ + break; + } +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << FREQ)) + return; + if (sd->sensor == SENSOR_OV7660) { + u8 com8; + + com8 = 0xdf; /* auto gain/wb/expo */ + switch (sd->ctrls[FREQ].val) { + case 0: /* Banding filter disabled */ + i2c_w1(gspca_dev, 0x13, com8 | 0x20); + break; + case 1: /* 50 hz */ + i2c_w1(gspca_dev, 0x13, com8); + i2c_w1(gspca_dev, 0x3b, 0x0a); + break; + case 2: /* 60 hz */ + i2c_w1(gspca_dev, 0x13, com8); + i2c_w1(gspca_dev, 0x3b, 0x02); + break; + } + } else { + u8 reg2a = 0, reg2b = 0, reg2d = 0; + + /* Get reg2a / reg2d base values */ + switch (sd->sensor) { + case SENSOR_OV7630: + reg2a = 0x08; + reg2d = 0x01; + break; + case SENSOR_OV7648: + reg2a = 0x11; + reg2d = 0x81; + break; + } + + switch (sd->ctrls[FREQ].val) { + case 0: /* Banding filter disabled */ + break; + case 1: /* 50 hz (filter on and framerate adj) */ + reg2a |= 0x80; + reg2b = 0xac; + reg2d |= 0x04; + break; + case 2: /* 60 hz (filter on, no framerate adj) */ + reg2a |= 0x80; + reg2d |= 0x04; + break; + } + i2c_w1(gspca_dev, 0x2a, reg2a); + i2c_w1(gspca_dev, 0x2b, reg2b); + i2c_w1(gspca_dev, 0x2d, reg2d); + } +} + +static void setjpegqual(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + jpeg_set_qual(sd->jpeg_hdr, sd->quality); +#if USB_BUF_SZ < 64 +#error "No room enough in usb_buf for quantization table" +#endif + memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0100, 0, + gspca_dev->usb_buf, 64, + 500); + memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0140, 0, + gspca_dev->usb_buf, 64, + 500); + + sd->reg18 ^= 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); +} + +/* JPEG quality update */ +/* This function is executed from a work queue. */ +static void qual_upd(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + mutex_lock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality); + setjpegqual(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u8 reg01, reg17; + u8 reg0102[2]; + const u8 *sn9c1xx; + const u8 (*init)[8]; + const u8 *reg9a; + int mode; + static const u8 reg9a_def[] = + {0x00, 0x40, 0x20, 0x00, 0x00, 0x00}; + static const u8 reg9a_spec[] = + {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; + static const u8 regd4[] = {0x60, 0x00, 0x00}; + static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; + static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; + static const u8 CA_adcm1700[] = + { 0x14, 0xec, 0x0a, 0xf6 }; + static const u8 CA_po2030n[] = + { 0x1e, 0xe2, 0x14, 0xec }; + static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ + static const u8 CE_gc0307[] = + { 0x32, 0xce, 0x2d, 0xd3 }; + static const u8 CE_ov76xx[] = + { 0x32, 0xdd, 0x32, 0xdd }; + static const u8 CE_po2030n[] = + { 0x14, 0xe7, 0x1e, 0xdd }; + + /* create the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + + /* initialize the bridge */ + sn9c1xx = sn_tb[sd->sensor]; + + /* sensor clock already enabled in sd_init */ + /* reg_w1(gspca_dev, 0xf1, 0x00); */ + reg01 = sn9c1xx[1]; + if (sd->flags & F_PDN_INV) + reg01 ^= S_PDN_INV; /* power down inverted */ + reg_w1(gspca_dev, 0x01, reg01); + + /* configure gpio */ + reg0102[0] = reg01; + reg0102[1] = sn9c1xx[2]; + if (gspca_dev->audio) + reg0102[1] |= 0x04; /* keep the audio connection */ + reg_w(gspca_dev, 0x01, reg0102, 2); + reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); + reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); + switch (sd->sensor) { + case SENSOR_GC0307: + case SENSOR_OV7660: + case SENSOR_PO1030: + case SENSOR_PO2030N: + case SENSOR_SOI768: + case SENSOR_SP80708: + reg9a = reg9a_spec; + break; + default: + reg9a = reg9a_def; + break; + } + reg_w(gspca_dev, 0x9a, reg9a, 6); + + reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); + + reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); + + reg17 = sn9c1xx[0x17]; + switch (sd->sensor) { + case SENSOR_GC0307: + msleep(50); /*fixme: is it useful? */ + break; + case SENSOR_OM6802: + msleep(10); + reg_w1(gspca_dev, 0x02, 0x73); + reg17 |= SEN_CLK_EN; + reg_w1(gspca_dev, 0x17, reg17); + reg_w1(gspca_dev, 0x01, 0x22); + msleep(100); + reg01 = SCL_SEL_OD | S_PDN_INV; + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x04; /* clock / 4 */ + break; + } + reg01 |= SYS_SEL_48M; + reg_w1(gspca_dev, 0x01, reg01); + reg17 |= SEN_CLK_EN; + reg_w1(gspca_dev, 0x17, reg17); + reg01 &= ~S_PWR_DN; /* sensor power on */ + reg_w1(gspca_dev, 0x01, reg01); + reg01 &= ~SCL_SEL_OD; /* remove open-drain mode */ + reg_w1(gspca_dev, 0x01, reg01); + + switch (sd->sensor) { + case SENSOR_HV7131R: + hv7131r_probe(gspca_dev); /*fixme: is it useful? */ + break; + case SENSOR_OM6802: + msleep(10); + reg_w1(gspca_dev, 0x01, reg01); + i2c_w8(gspca_dev, om6802_init0[0]); + i2c_w8(gspca_dev, om6802_init0[1]); + msleep(15); + reg_w1(gspca_dev, 0x02, 0x71); + msleep(150); + break; + case SENSOR_SP80708: + msleep(100); + reg_w1(gspca_dev, 0x02, 0x62); + break; + } + + /* initialize the sensor */ + i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); + + reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); + reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); + reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); + reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]); + reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); + if (sd->sensor == SENSOR_ADCM1700) { + reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */ + reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */ + } else { + reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */ + reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */ + } + reg_w1(gspca_dev, 0xc6, 0x00); + reg_w1(gspca_dev, 0xc7, 0x00); + if (sd->sensor == SENSOR_ADCM1700) { + reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */ + reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */ + } else { + reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */ + reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */ + } + reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); + switch (sd->sensor) { + case SENSOR_OM6802: +/* case SENSOR_OV7648: * fixme: sometimes */ + break; + default: + reg17 |= DEF_EN; + break; + } + reg_w1(gspca_dev, 0x17, reg17); + + reg_w1(gspca_dev, 0x05, 0x00); /* red */ + reg_w1(gspca_dev, 0x07, 0x00); /* green */ + reg_w1(gspca_dev, 0x06, 0x00); /* blue */ + reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); + + setgamma(gspca_dev); + +/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */ + for (i = 0; i < 8; i++) + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_OV7660: + case SENSOR_SP80708: + reg_w1(gspca_dev, 0x9a, 0x05); + break; + case SENSOR_GC0307: + case SENSOR_MT9V111: + case SENSOR_MI0360B: + reg_w1(gspca_dev, 0x9a, 0x07); + break; + case SENSOR_OV7630: + case SENSOR_OV7648: + reg_w1(gspca_dev, 0x9a, 0x0a); + break; + case SENSOR_PO2030N: + case SENSOR_SOI768: + reg_w1(gspca_dev, 0x9a, 0x06); + break; + default: + reg_w1(gspca_dev, 0x9a, 0x08); + break; + } + setsharpness(gspca_dev); + + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); + reg_w1(gspca_dev, 0x05, 0x20); /* red */ + reg_w1(gspca_dev, 0x07, 0x20); /* green */ + reg_w1(gspca_dev, 0x06, 0x20); /* blue */ + + init = NULL; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + reg01 |= SYS_SEL_48M | V_TX_EN; + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x02; /* clock / 2 */ + switch (sd->sensor) { + case SENSOR_ADCM1700: + init = adcm1700_sensor_param1; + break; + case SENSOR_GC0307: + init = gc0307_sensor_param1; + break; + case SENSOR_HV7131R: + case SENSOR_MI0360: + if (!mode) + reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */ + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x01; /* clock / 1 */ + break; + case SENSOR_MI0360B: + init = mi0360b_sensor_param1; + break; + case SENSOR_MO4000: + if (mode) { /* if 320x240 */ + reg01 &= ~SYS_SEL_48M; /* clk 24Mz */ + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x01; /* clock / 1 */ + } + break; + case SENSOR_MT9V111: + init = mt9v111_sensor_param1; + break; + case SENSOR_OM6802: + init = om6802_sensor_param1; + if (!mode) { /* if 640x480 */ + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x04; /* clock / 4 */ + } else { + reg01 &= ~SYS_SEL_48M; /* clk 24Mz */ + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x02; /* clock / 2 */ + } + break; + case SENSOR_OV7630: + init = ov7630_sensor_param1; + break; + case SENSOR_OV7648: + init = ov7648_sensor_param1; + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x01; /* clock / 1 */ + break; + case SENSOR_OV7660: + init = ov7660_sensor_param1; + break; + case SENSOR_PO1030: + init = po1030_sensor_param1; + break; + case SENSOR_PO2030N: + init = po2030n_sensor_param1; + break; + case SENSOR_SOI768: + init = soi768_sensor_param1; + break; + case SENSOR_SP80708: + init = sp80708_sensor_param1; + break; + } + + /* more sensor initialization - param1 */ + if (init != NULL) { + i2c_w_seq(gspca_dev, init); +/* init = NULL; */ + } + + reg_w(gspca_dev, 0xc0, C0, 6); + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_GC0307: + case SENSOR_SOI768: + reg_w(gspca_dev, 0xca, CA_adcm1700, 4); + break; + case SENSOR_PO2030N: + reg_w(gspca_dev, 0xca, CA_po2030n, 4); + break; + default: + reg_w(gspca_dev, 0xca, CA, 4); + break; + } + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_OV7630: + case SENSOR_OV7648: + case SENSOR_OV7660: + case SENSOR_SOI768: + reg_w(gspca_dev, 0xce, CE_ov76xx, 4); + break; + case SENSOR_GC0307: + reg_w(gspca_dev, 0xce, CE_gc0307, 4); + break; + case SENSOR_PO2030N: + reg_w(gspca_dev, 0xce, CE_po2030n, 4); + break; + default: + reg_w(gspca_dev, 0xce, CE, 4); + /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */ + break; + } + + /* here change size mode 0 -> VGA; 1 -> CIF */ + sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); + setjpegqual(gspca_dev); + + reg_w1(gspca_dev, 0x17, reg17); + reg_w1(gspca_dev, 0x01, reg01); + sd->reg01 = reg01; + sd->reg17 = reg17; + + sethvflip(gspca_dev); + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); + setautogain(gspca_dev); + if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) { + setexposure(gspca_dev); + setgain(gspca_dev); + } + setfreq(gspca_dev); + + sd->pktsz = sd->npkt = 0; + sd->nchg = sd->short_mark = 0; + sd->work_thread = create_singlethread_workqueue(MODULE_NAME); + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const u8 stophv7131[] = + { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; + static const u8 stopmi0360[] = + { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; + static const u8 stopov7648[] = + { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 }; + static const u8 stopsoi768[] = + { 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 }; + u8 reg01; + u8 reg17; + + reg01 = sd->reg01; + reg17 = sd->reg17 & ~SEN_CLK_EN; + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_GC0307: + case SENSOR_PO2030N: + case SENSOR_SP80708: + reg01 |= LED; + reg_w1(gspca_dev, 0x01, reg01); + reg01 &= ~(LED | V_TX_EN); + reg_w1(gspca_dev, 0x01, reg01); +/* reg_w1(gspca_dev, 0x02, 0x??); * LED off ? */ + break; + case SENSOR_HV7131R: + reg01 &= ~V_TX_EN; + reg_w1(gspca_dev, 0x01, reg01); + i2c_w8(gspca_dev, stophv7131); + break; + case SENSOR_MI0360: + case SENSOR_MI0360B: + reg01 &= ~V_TX_EN; + reg_w1(gspca_dev, 0x01, reg01); +/* reg_w1(gspca_dev, 0x02, 0x40); * LED off ? */ + i2c_w8(gspca_dev, stopmi0360); + break; + case SENSOR_MT9V111: + case SENSOR_OM6802: + case SENSOR_PO1030: + reg01 &= ~V_TX_EN; + reg_w1(gspca_dev, 0x01, reg01); + break; + case SENSOR_OV7630: + case SENSOR_OV7648: + reg01 &= ~V_TX_EN; + reg_w1(gspca_dev, 0x01, reg01); + i2c_w8(gspca_dev, stopov7648); + break; + case SENSOR_OV7660: + reg01 &= ~V_TX_EN; + reg_w1(gspca_dev, 0x01, reg01); + break; + case SENSOR_SOI768: + i2c_w8(gspca_dev, stopsoi768); + break; + } + + reg01 |= SCL_SEL_OD; + reg_w1(gspca_dev, 0x01, reg01); + reg01 |= S_PWR_DN; /* sensor power down */ + reg_w1(gspca_dev, 0x01, reg01); + reg_w1(gspca_dev, 0x17, reg17); + reg01 &= ~SYS_SEL_48M; /* clock 24MHz */ + reg_w1(gspca_dev, 0x01, reg01); + reg01 |= LED; + reg_w1(gspca_dev, 0x01, reg01); + /* Don't disable sensor clock as that disables the button on the cam */ + /* reg_w1(gspca_dev, 0xf1, 0x01); */ +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } +} + +#define WANT_REGULAR_AUTOGAIN +#include "autogain_functions.h" + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int delta; + int expotimes; + u8 luma_mean = 130; + u8 luma_delta = 20; + + /* Thanks S., without your advice, autobright should not work :) */ + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; + + delta = atomic_read(&sd->avg_lum); + PDEBUG(D_FRAM, "mean lum %d", delta); + + if (sd->sensor == SENSOR_PO2030N) { + auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta, + 15, 1024); + return; + } + + if (delta < luma_mean - luma_delta || + delta > luma_mean + luma_delta) { + switch (sd->sensor) { + case SENSOR_GC0307: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 6; + if (expotimes < 0) + expotimes = 0; + sd->exposure = expo_adjust(gspca_dev, + (unsigned int) expotimes); + break; + case SENSOR_HV7131R: + expotimes = sd->exposure >> 8; + expotimes += (luma_mean - delta) >> 4; + if (expotimes < 0) + expotimes = 0; + sd->exposure = expo_adjust(gspca_dev, + (unsigned int) (expotimes << 8)); + break; + case SENSOR_OM6802: + case SENSOR_MT9V111: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 2; + if (expotimes < 0) + expotimes = 0; + sd->exposure = expo_adjust(gspca_dev, + (unsigned int) expotimes); + setredblue(gspca_dev); + break; + default: +/* case SENSOR_MO4000: */ +/* case SENSOR_MI0360: */ +/* case SENSOR_MI0360B: */ + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 6; + if (expotimes < 0) + expotimes = 0; + sd->exposure = expo_adjust(gspca_dev, + (unsigned int) expotimes); + setredblue(gspca_dev); + break; + } + } +} + +/* set the average luminosity from an isoc marker */ +static void set_lum(struct sd *sd, + u8 *data) +{ + int avg_lum; + + /* w0 w1 w2 + * w3 w4 w5 + * w6 w7 w8 + */ + avg_lum = (data[27] << 8) + data[28] /* w3 */ + + + (data[31] << 8) + data[32] /* w5 */ + + + (data[23] << 8) + data[24] /* w1 */ + + + (data[35] << 8) + data[36] /* w7 */ + + + (data[29] << 10) + (data[30] << 2); /* w4 * 4 */ + avg_lum >>= 10; + atomic_set(&sd->avg_lum, avg_lum); +} + +/* scan the URB packets */ +/* This function is run at interrupt level. */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, new_qual; + + /* + * A frame ends on the marker + * ff ff 00 c4 c4 96 .. + * which is 62 bytes long and is followed by various information + * including statuses and luminosity. + * + * A marker may be splitted on two packets. + * + * The 6th byte of a marker contains the bits: + * 0x08: USB full + * 0xc0: frame sequence + * When the bit 'USB full' is set, the frame must be discarded; + * this is also the case when the 2 bytes before the marker are + * not the JPEG end of frame ('ff d9'). + */ + + /* count the packets and their size */ + sd->npkt++; + sd->pktsz += len; + +/*fixme: assumption about the following code: + * - there can be only one marker in a packet + */ + + /* skip the remaining bytes of a short marker */ + i = sd->short_mark; + if (i != 0) { + sd->short_mark = 0; + if (i < 0 /* if 'ff' at end of previous packet */ + && data[0] == 0xff + && data[1] == 0x00) + goto marker_found; + if (data[0] == 0xff && data[1] == 0xff) { + i = 0; + goto marker_found; + } + len -= i; + if (len <= 0) + return; + data += i; + } + + /* search backwards if there is a marker in the packet */ + for (i = len - 1; --i >= 0; ) { + if (data[i] != 0xff) { + i--; + continue; + } + if (data[i + 1] == 0xff) { + + /* (there may be 'ff ff' inside a marker) */ + if (i + 2 >= len || data[i + 2] == 0x00) + goto marker_found; + } + } + + /* no marker found */ + /* add the JPEG header if first fragment */ + if (data[len - 1] == 0xff) + sd->short_mark = -1; + if (gspca_dev->last_packet_type == LAST_PACKET) + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + return; + + /* marker found */ + /* if some error, discard the frame and decrease the quality */ +marker_found: + new_qual = 0; + if (i > 2) { + if (data[i - 2] != 0xff || data[i - 1] != 0xd9) { + gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -3; + } + } else if (i + 6 < len) { + if (data[i + 6] & 0x08) { + gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -5; + } + } + + gspca_frame_add(gspca_dev, LAST_PACKET, data, i); + + /* compute the filling rate and a new JPEG quality */ + if (new_qual == 0) { + int r; + + r = (sd->pktsz * 100) / + (sd->npkt * + gspca_dev->urb[0]->iso_frame_desc[0].length); + if (r >= 85) + new_qual = -3; + else if (r < 75) + new_qual = 2; + } + if (new_qual != 0) { + sd->nchg += new_qual; + if (sd->nchg < -6 || sd->nchg >= 12) { + sd->nchg = 0; + new_qual += sd->quality; + if (new_qual < QUALITY_MIN) + new_qual = QUALITY_MIN; + else if (new_qual > QUALITY_MAX) + new_qual = QUALITY_MAX; + if (new_qual != sd->quality) { + sd->quality = new_qual; + queue_work(sd->work_thread, &sd->work); + } + } + } else { + sd->nchg = 0; + } + sd->pktsz = sd->npkt = 0; + + /* if the marker is smaller than 62 bytes, + * memorize the number of bytes to skip in the next packet */ + if (i + 62 > len) { /* no more usable data */ + sd->short_mark = i + 62 - len; + return; + } + if (sd->ag_cnt >= 0) + set_lum(sd, data + i); + + /* if more data, start a new frame */ + i += 62; + if (i < len) { + data += i; + len -= i; + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + } +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->ctrls[AUTOGAIN].val = val; + if (val) + gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); + else + gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN); + if (gspca_dev->streaming) + setautogain(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + + if (len == 1 && data[0] == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = NCTRLS, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, + .querymenu = sd_querymenu, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +/* -- module initialisation -- */ +#define BS(bridge, sensor) \ + .driver_info = (BRIDGE_ ## bridge << 16) \ + | (SENSOR_ ## sensor << 8) +#define BSF(bridge, sensor, flags) \ + .driver_info = (BRIDGE_ ## bridge << 16) \ + | (SENSOR_ ## sensor << 8) \ + | (flags) +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0458, 0x7025), BSF(SN9C120, MI0360B, F_PDN_INV)}, + {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, + {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, F_PDN_INV)}, + {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, F_PDN_INV)}, + {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)}, +/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */ + {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */ +/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */ + {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */ + {USB_DEVICE(0x0c45, 0x60c0), BSF(SN9C105, MI0360, F_ILLUM)}, + /* or MT9V111 */ +/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */ +/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */ +/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */ + {USB_DEVICE(0x0c45, 0x60ce), BS(SN9C105, SP80708)}, + {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)}, +/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */ +/* {USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */ + {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)}, + {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, + {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/ +/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ + {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/ +/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */ +/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */ + {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/ +/*bw600.inf:*/ + {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)}, + {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)}, + {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)}, +/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */ + {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)}, + /* or MT9V111 / MI0360B */ +/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */ + {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)}, + {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)}, + {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)}, + {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, + {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, + {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/ + /* or GC0305 / GC0307 */ + {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x614a), BSF(SN9C120, ADCM1700, F_ILLUM)}, +/* {USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */ /*sn9c120b*/ + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/spca1528.c b/drivers/media/usb/gspca/spca1528.c new file mode 100644 index 000000000000..14d635277d71 --- /dev/null +++ b/drivers/media/usb/gspca/spca1528.c @@ -0,0 +1,444 @@ +/* + * spca1528 subdriver + * + * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "spca1528" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("SPCA1528 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 pkt_seq; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +static const struct v4l2_pix_format vga_mode[] = { +/* (does not work correctly) + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 5 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, +*/ + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +/* read bytes to gspca usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + u8 req, + u16 index, + int len) +{ +#if USB_BUF_SZ < 64 +#error "USB buffer too small" +#endif + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0000, /* value */ + index, + gspca_dev->usb_buf, len, + 500); + PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, + gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + NULL, 0, 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_wb(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index, + u8 byte) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x %02x", req, value, index, byte); + gspca_dev->usb_buf[0] = byte; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void wait_status_0(struct gspca_dev *gspca_dev) +{ + int i, w; + + i = 16; + w = 0; + do { + reg_r(gspca_dev, 0x21, 0x0000, 1); + if (gspca_dev->usb_buf[0] == 0) + return; + w += 15; + msleep(w); + } while (--i > 0); + PDEBUG(D_ERR, "wait_status_0 timeout"); + gspca_dev->usb_err = -ETIME; +} + +static void wait_status_1(struct gspca_dev *gspca_dev) +{ + int i; + + i = 10; + do { + reg_r(gspca_dev, 0x21, 0x0001, 1); + msleep(10); + if (gspca_dev->usb_buf[0] == 1) { + reg_wb(gspca_dev, 0x21, 0x0000, 0x0001, 0x00); + reg_r(gspca_dev, 0x21, 0x0001, 1); + return; + } + } while (--i > 0); + PDEBUG(D_ERR, "wait_status_1 timeout"); + gspca_dev->usb_err = -ETIME; +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, val); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, val); +} + +static void sethue(struct gspca_dev *gspca_dev, s32 val) +{ + reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, val); +} + +static void setcolor(struct gspca_dev *gspca_dev, s32 val) +{ + reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, val); +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, val); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */ + /*fixme: 256 in ms-win traces*/ + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0x00, 0x0001, 0x2067); + reg_w(gspca_dev, 0x00, 0x00d0, 0x206b); + reg_w(gspca_dev, 0x00, 0x0000, 0x206c); + reg_w(gspca_dev, 0x00, 0x0001, 0x2069); + msleep(8); + reg_w(gspca_dev, 0x00, 0x00c0, 0x206b); + reg_w(gspca_dev, 0x00, 0x0000, 0x206c); + reg_w(gspca_dev, 0x00, 0x0001, 0x2069); + + reg_r(gspca_dev, 0x20, 0x0000, 1); + reg_r(gspca_dev, 0x20, 0x0000, 5); + reg_r(gspca_dev, 0x23, 0x0000, 64); + PDEBUG(D_PROBE, "%s%s", &gspca_dev->usb_buf[0x1c], + &gspca_dev->usb_buf[0x30]); + reg_r(gspca_dev, 0x23, 0x0001, 64); + return gspca_dev->usb_err; +} + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + u8 mode; + + reg_r(gspca_dev, 0x00, 0x2520, 1); + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0xc5, 0x0003, 0x0000); + wait_status_1(gspca_dev); + + wait_status_0(gspca_dev); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode); + reg_r(gspca_dev, 0x25, 0x0004, 1); + reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); /* 420 */ + reg_r(gspca_dev, 0x27, 0x0000, 1); + +/* not useful.. + gspca_dev->alt = 4; * use alternate setting 3 */ + + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* initialize the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + + /* the JPEG quality shall be 85% */ + jpeg_set_qual(sd->jpeg_hdr, 85); + + reg_r(gspca_dev, 0x00, 0x2520, 1); + msleep(8); + + /* start the capture */ + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0x31, 0x0000, 0x0004); /* start request */ + wait_status_1(gspca_dev); + wait_status_0(gspca_dev); + msleep(200); + + sd->pkt_seq = 0; + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* stop the capture */ + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0x31, 0x0000, 0x0000); /* stop request */ + wait_status_1(gspca_dev); + wait_status_0(gspca_dev); +} + +/* move a packet adding 0x00 after 0xff */ +static void add_packet(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + int i; + + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + } while (++i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + static const u8 ffd9[] = {0xff, 0xd9}; + + /* image packets start with: + * 02 8n + * with bit: + * 0x01: even (0) / odd (1) image + * 0x02: end of image when set + */ + if (len < 3) + return; /* empty packet */ + if (*data == 0x02) { + if (data[1] & 0x02) { + sd->pkt_seq = !(data[1] & 1); + add_packet(gspca_dev, data + 2, len - 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); + return; + } + if ((data[1] & 1) != sd->pkt_seq) + goto err; + if (gspca_dev->last_packet_type == LAST_PACKET) + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + add_packet(gspca_dev, data + 2, len - 2); + return; + } +err: + gspca_dev->last_packet_type = DISCARD_PACKET; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_HUE: + sethue(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolor(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 8, 1, 1); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 0); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 8, 1, 1); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 255, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x04fc, 0x1528)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + /* the video interface for isochronous transfer is 1 */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 1) + return -ENODEV; + + return gspca_dev_probe2(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/spca500.c b/drivers/media/usb/gspca/spca500.c new file mode 100644 index 000000000000..25cb68d0556d --- /dev/null +++ b/drivers/media/usb/gspca/spca500.c @@ -0,0 +1,990 @@ +/* + * SPCA500 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "spca500" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define QUALITY 85 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + char subtype; +#define AgfaCl20 0 +#define AiptekPocketDV 1 +#define BenqDC1016 2 +#define CreativePCCam300 3 +#define DLinkDSC350 4 +#define Gsmartmini 5 +#define IntelPocketPCCamera 6 +#define KodakEZ200 7 +#define LogitechClickSmart310 8 +#define LogitechClickSmart510 9 +#define LogitechTraveler 10 +#define MustekGsmart300 11 +#define Optimedia 12 +#define PalmPixDC85 13 +#define ToptroIndus 14 + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* Frame packet header offsets for the spca500 */ +#define SPCA500_OFFSET_PADDINGLB 2 +#define SPCA500_OFFSET_PADDINGHB 3 +#define SPCA500_OFFSET_MODE 4 +#define SPCA500_OFFSET_IMGWIDTH 5 +#define SPCA500_OFFSET_IMGHEIGHT 6 +#define SPCA500_OFFSET_IMGMODE 7 +#define SPCA500_OFFSET_QTBLINDEX 8 +#define SPCA500_OFFSET_FRAMSEQ 9 +#define SPCA500_OFFSET_CDSPINFO 10 +#define SPCA500_OFFSET_GPIO 11 +#define SPCA500_OFFSET_AUGPIO 12 +#define SPCA500_OFFSET_DATA 16 + + +static const __u16 spca500_visual_defaults[][3] = { + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, + * saturation/hue enable, + * brightness/contrast enable. + */ + {0x00, 0x0000, 0x8167}, /* brightness = 0 */ + {0x00, 0x0020, 0x8168}, /* contrast = 0 */ + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, saturation/hue enable, + * brightness/contrast enable. + * was 0x0003, now 0x0000. + */ + {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */ + {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */ + {0x00, 0x0050, 0x8157}, /* edge gain high threshold */ + {0x00, 0x0030, 0x8158}, /* edge gain low threshold */ + {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */ + {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */ + {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */ + {0x0c, 0x0004, 0x0000}, + /* set interface */ + {} +}; +static const __u16 Clicksmart510_defaults[][3] = { + {0x00, 0x00, 0x8211}, + {0x00, 0x01, 0x82c0}, + {0x00, 0x10, 0x82cb}, + {0x00, 0x0f, 0x800d}, + {0x00, 0x82, 0x8225}, + {0x00, 0x21, 0x8228}, + {0x00, 0x00, 0x8203}, + {0x00, 0x00, 0x8204}, + {0x00, 0x08, 0x8205}, + {0x00, 0xf8, 0x8206}, + {0x00, 0x28, 0x8207}, + {0x00, 0xa0, 0x8208}, + {0x00, 0x08, 0x824a}, + {0x00, 0x08, 0x8214}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x00, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x04, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0xfc, 0x8100}, + {0x00, 0xfc, 0x8105}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8102}, + {0x00, 0x00, 0x8103}, + {0x00, 0x66, 0x8107}, + {0x00, 0x00, 0x816b}, + {0x00, 0x00, 0x8155}, + {0x00, 0x01, 0x8156}, + {0x00, 0x60, 0x8157}, + {0x00, 0x40, 0x8158}, + {0x00, 0x0a, 0x8159}, + {0x00, 0x06, 0x815a}, + {0x00, 0x00, 0x813f}, + {0x00, 0x00, 0x8200}, + {0x00, 0x19, 0x8201}, + {0x00, 0x00, 0x82c1}, + {0x00, 0xa0, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x00, 0x8117}, + {0x00, 0x00, 0x8118}, + {0x00, 0x65, 0x8119}, + {0x00, 0x00, 0x811a}, + {0x00, 0x00, 0x811b}, + {0x00, 0x55, 0x811c}, + {0x00, 0x65, 0x811d}, + {0x00, 0x55, 0x811e}, + {0x00, 0x16, 0x811f}, + {0x00, 0x19, 0x8120}, + {0x00, 0x80, 0x8103}, + {0x00, 0x83, 0x816b}, + {0x00, 0x25, 0x8168}, + {0x00, 0x01, 0x820f}, + {0x00, 0xff, 0x8115}, + {0x00, 0x48, 0x8116}, + {0x00, 0x50, 0x8151}, + {0x00, 0x40, 0x8152}, + {0x00, 0x78, 0x8153}, + {0x00, 0x40, 0x8154}, + {0x00, 0x00, 0x8167}, + {0x00, 0x20, 0x8168}, + {0x00, 0x00, 0x816a}, + {0x00, 0x03, 0x816b}, + {0x00, 0x20, 0x8169}, + {0x00, 0x60, 0x8157}, + {0x00, 0x00, 0x8190}, + {0x00, 0x00, 0x81a1}, + {0x00, 0x00, 0x81b2}, + {0x00, 0x27, 0x8191}, + {0x00, 0x27, 0x81a2}, + {0x00, 0x27, 0x81b3}, + {0x00, 0x4b, 0x8192}, + {0x00, 0x4b, 0x81a3}, + {0x00, 0x4b, 0x81b4}, + {0x00, 0x66, 0x8193}, + {0x00, 0x66, 0x81a4}, + {0x00, 0x66, 0x81b5}, + {0x00, 0x79, 0x8194}, + {0x00, 0x79, 0x81a5}, + {0x00, 0x79, 0x81b6}, + {0x00, 0x8a, 0x8195}, + {0x00, 0x8a, 0x81a6}, + {0x00, 0x8a, 0x81b7}, + {0x00, 0x9b, 0x8196}, + {0x00, 0x9b, 0x81a7}, + {0x00, 0x9b, 0x81b8}, + {0x00, 0xa6, 0x8197}, + {0x00, 0xa6, 0x81a8}, + {0x00, 0xa6, 0x81b9}, + {0x00, 0xb2, 0x8198}, + {0x00, 0xb2, 0x81a9}, + {0x00, 0xb2, 0x81ba}, + {0x00, 0xbe, 0x8199}, + {0x00, 0xbe, 0x81aa}, + {0x00, 0xbe, 0x81bb}, + {0x00, 0xc8, 0x819a}, + {0x00, 0xc8, 0x81ab}, + {0x00, 0xc8, 0x81bc}, + {0x00, 0xd2, 0x819b}, + {0x00, 0xd2, 0x81ac}, + {0x00, 0xd2, 0x81bd}, + {0x00, 0xdb, 0x819c}, + {0x00, 0xdb, 0x81ad}, + {0x00, 0xdb, 0x81be}, + {0x00, 0xe4, 0x819d}, + {0x00, 0xe4, 0x81ae}, + {0x00, 0xe4, 0x81bf}, + {0x00, 0xed, 0x819e}, + {0x00, 0xed, 0x81af}, + {0x00, 0xed, 0x81c0}, + {0x00, 0xf7, 0x819f}, + {0x00, 0xf7, 0x81b0}, + {0x00, 0xf7, 0x81c1}, + {0x00, 0xff, 0x81a0}, + {0x00, 0xff, 0x81b1}, + {0x00, 0xff, 0x81c2}, + {0x00, 0x03, 0x8156}, + {0x00, 0x00, 0x8211}, + {0x00, 0x20, 0x8168}, + {0x00, 0x01, 0x8202}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8111}, + {0x00, 0x00, 0x8112}, + {0x00, 0x00, 0x8113}, + {0x00, 0x00, 0x8114}, + {} +}; + +static const __u8 qtable_creative_pccam[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +static const __u8 qtable_kodak_ez200[2][64] = { + { /* Q-table Y-components */ + 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, + 0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06, + 0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06, + 0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06, + 0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08, + 0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09, + 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a, + 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a}, + { /* Q-table C-components */ + 0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a} +}; + +static const __u8 qtable_pocketdv[2][64] = { + { /* Q-table Y-components start registers 0x8800 */ + 0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18, + 0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16, + 0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16, + 0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19, + 0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f, + 0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25, + 0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28, + 0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28, + }, + { /* Q-table C-components start registers 0x8840 */ + 0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28, + 0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28, + 0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28} +}; + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, + __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, 500); +} + +static int reg_w(struct gspca_dev *gspca_dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + PDEBUG(D_USBO, "reg write: [0x%02x] = 0x%02x", index, value); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + if (ret < 0) + pr_err("reg write: error %d\n", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_r_12(struct gspca_dev *gspca_dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + + gspca_dev->usb_buf[1] = 0; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, length, + 500); /* timeout */ + if (ret < 0) { + pr_err("reg_r_12 err %d\n", ret); + return ret; + } + return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; +} + +/* + * Simple function to wait for a given 8-bit value to be returned from + * a reg_read call. + * Returns: negative is error or timeout, zero is success. + */ +static int reg_r_wait(struct gspca_dev *gspca_dev, + __u16 reg, __u16 index, __u16 value) +{ + int ret, cnt = 20; + + while (--cnt > 0) { + ret = reg_r_12(gspca_dev, reg, index, 1); + if (ret == value) + return 0; + msleep(50); + } + return -EIO; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][3]) +{ + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_w(gspca_dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, + unsigned int request, + unsigned int ybase, + unsigned int cbase, + const __u8 qtable[2][64]) +{ + int i, err; + + /* loop over y components */ + for (i = 0; i < 64; i++) { + err = reg_w(gspca_dev, request, ybase + i, qtable[0][i]); + if (err < 0) + return err; + } + + /* loop over c components */ + for (i = 0; i < 64; i++) { + err = reg_w(gspca_dev, request, cbase + i, qtable[1][i]); + if (err < 0) + return err; + } + return 0; +} + +static void spca500_ping310(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x0d04, 2); + PDEBUG(D_STREAM, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); +} + +static void spca500_clksmart310_init(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x0d05, 2); + PDEBUG(D_STREAM, "ClickSmart310 init 0x0d05 0x%02x 0x%02x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + reg_w(gspca_dev, 0x00, 0x8167, 0x5a); + spca500_ping310(gspca_dev); + + reg_w(gspca_dev, 0x00, 0x8168, 0x22); + reg_w(gspca_dev, 0x00, 0x816a, 0xc0); + reg_w(gspca_dev, 0x00, 0x816b, 0x0b); + reg_w(gspca_dev, 0x00, 0x8169, 0x25); + reg_w(gspca_dev, 0x00, 0x8157, 0x5b); + reg_w(gspca_dev, 0x00, 0x8158, 0x5b); + reg_w(gspca_dev, 0x00, 0x813f, 0x03); + reg_w(gspca_dev, 0x00, 0x8151, 0x4a); + reg_w(gspca_dev, 0x00, 0x8153, 0x78); + reg_w(gspca_dev, 0x00, 0x0d01, 0x04); + /* 00 for adjust shutter */ + reg_w(gspca_dev, 0x00, 0x0d02, 0x01); + reg_w(gspca_dev, 0x00, 0x8169, 0x25); + reg_w(gspca_dev, 0x00, 0x0d01, 0x02); +} + +static void spca500_setmode(struct gspca_dev *gspca_dev, + __u8 xmult, __u8 ymult) +{ + int mode; + + /* set x multiplier */ + reg_w(gspca_dev, 0, 0x8001, xmult); + + /* set y multiplier */ + reg_w(gspca_dev, 0, 0x8002, ymult); + + /* use compressed mode, VGA, with mode specific subsample */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_w(gspca_dev, 0, 0x8003, mode << 4); +} + +static int spca500_full_reset(struct gspca_dev *gspca_dev) +{ + int err; + + /* send the reset command */ + err = reg_w(gspca_dev, 0xe0, 0x0001, 0x0000); + if (err < 0) + return err; + + /* wait for the reset to complete */ + err = reg_r_wait(gspca_dev, 0x06, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_w(gspca_dev, 0xe0, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_r_wait(gspca_dev, 0x06, 0, 0); + if (err < 0) { + PDEBUG(D_ERR, "reg_r_wait() failed"); + return err; + } + /* all ok */ + return 0; +} + +/* Synchro the Bridge with sensor */ +/* Maybe that will work on all spca500 chip */ +/* because i only own a clicksmart310 try for that chip */ +/* using spca50x_set_packet_size() cause an Ooops here */ +/* usb_set_interface from kernel 2.6.x clear all the urb stuff */ +/* up-port the same feature as in 2.4.x kernel */ +static int spca500_synch310(struct gspca_dev *gspca_dev) +{ + if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + spca500_ping310(gspca_dev); + + reg_r(gspca_dev, 0x0d00, 1); + + /* need alt setting here */ + PDEBUG(D_PACK, "ClickSmart310 sync alt: %d", gspca_dev->alt); + + /* Windoze use pipe with altsetting 6 why 7 here */ + if (usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + gspca_dev->alt) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + return 0; +error: + return -EBUSY; +} + +static void spca500_reinit(struct gspca_dev *gspca_dev) +{ + int err; + __u8 Data; + + /* some unknown command from Aiptek pocket dv and family300 */ + + reg_w(gspca_dev, 0x00, 0x0d01, 0x01); + reg_w(gspca_dev, 0x00, 0x0d03, 0x00); + reg_w(gspca_dev, 0x00, 0x0d02, 0x01); + + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, + qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init"); + + /* set qtable index */ + reg_w(gspca_dev, 0x00, 0x8880, 2); + /* family cam Quicksmart stuff */ + reg_w(gspca_dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced between frames */ + reg_w(gspca_dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + /*Start init sequence or stream */ + reg_w(gspca_dev, 0, 0x8003, 0x00); + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + msleep(2000); + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) { + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + sd->subtype = id->driver_info; + if (sd->subtype != LogitechClickSmart310) { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } else { + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + } + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* initialisation of spca500 based cameras is deferred */ + PDEBUG(D_STREAM, "SPCA500 init"); + if (sd->subtype == LogitechClickSmart310) + spca500_clksmart310_init(gspca_dev); +/* else + spca500_initialise(gspca_dev); */ + PDEBUG(D_STREAM, "SPCA500 init done"); + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + __u8 Data; + __u8 xmult, ymult; + + /* create the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, QUALITY); + + if (sd->subtype == LogitechClickSmart310) { + xmult = 0x16; + ymult = 0x12; + } else { + xmult = 0x28; + ymult = 0x1e; + } + + /* is there a sensor here ? */ + reg_r(gspca_dev, 0x8a04, 1); + PDEBUG(D_STREAM, "Spca500 Sensor Address 0x%02x", + gspca_dev->usb_buf[0]); + PDEBUG(D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02x, Ymult: 0x%02x", + gspca_dev->curr_mode, xmult, ymult); + + /* setup qtable */ + switch (sd->subtype) { + case LogitechClickSmart310: + spca500_setmode(gspca_dev, xmult, ymult); + + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + msleep(500); + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + + spca500_synch310(gspca_dev); + + write_vector(gspca_dev, spca500_visual_defaults); + spca500_setmode(gspca_dev, xmult, ymult); + /* enable drop packet */ + err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + if (err < 0) + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + break; + case CreativePCCam300: /* Creative PC-CAM 300 640x480 CCD */ + case IntelPocketPCCamera: /* FIXME: Temporary fix for + * Intel Pocket PC Camera + * - NWG (Sat 29th March 2003) */ + + /* do a full reset */ + err = spca500_full_reset(gspca_dev); + if (err < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + + /* enable drop packet */ + err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + if (err < 0) + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + spca500_setmode(gspca_dev, xmult, ymult); + reg_w(gspca_dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + +/* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + case KodakEZ200: /* Kodak EZ200 */ + + /* do a full reset */ + err = spca500_full_reset(gspca_dev); + if (err < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + reg_w(gspca_dev, 0x00, 0x8880, 0); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_kodak_ez200); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + spca500_setmode(gspca_dev, xmult, ymult); + + reg_w(gspca_dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + +/* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + + case BenqDC1016: + case DLinkDSC350: /* FamilyCam 300 */ + case AiptekPocketDV: /* Aiptek PocketDV */ + case Gsmartmini: /*Mustek Gsmart Mini */ + case MustekGsmart300: /* Mustek Gsmart 300 */ + case PalmPixDC85: + case Optimedia: + case ToptroIndus: + case AgfaCl20: + spca500_reinit(gspca_dev); + reg_w(gspca_dev, 0x00, 0x0d01, 0x01); + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_w(gspca_dev, 0x00, 0x8880, 2); + + /* familycam Quicksmart pocketDV stuff */ + reg_w(gspca_dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced between frames */ + reg_w(gspca_dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev, xmult, ymult); + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + reg_r_wait(gspca_dev, 0, 0x8000, 0x44); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + break; + case LogitechTraveler: + case LogitechClickSmart510: + reg_w(gspca_dev, 0x02, 0x00, 0x00); + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, + 0x8840, qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + reg_w(gspca_dev, 0x00, 0x800a, 0x00); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev, xmult, ymult); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + reg_r_wait(gspca_dev, 0, 0x8000, 0x44); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + write_vector(gspca_dev, Clicksmart510_defaults); + break; + } + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0, 0x8003, 0x00); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + reg_r(gspca_dev, 0x8000, 1); + PDEBUG(D_STREAM, "stop SPCA500 done reg8000: 0x%2x", + gspca_dev->usb_buf[0]); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + static __u8 ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); + + /* put the JPEG header in the new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + + data += SPCA500_OFFSET_DATA; + len -= SPCA500_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + + /* add 0x00 after 0xff */ + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + i++; + } while (i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w(gspca_dev, 0x00, 0x8167, + (__u8) (val - 128)); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w(gspca_dev, 0x00, 0x8168, val); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w(gspca_dev, 0x00, 0x8169, val); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 3); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 31); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 63, 1, 31); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0300), .driver_info = KodakEZ200}, + {USB_DEVICE(0x041e, 0x400a), .driver_info = CreativePCCam300}, + {USB_DEVICE(0x046d, 0x0890), .driver_info = LogitechTraveler}, + {USB_DEVICE(0x046d, 0x0900), .driver_info = LogitechClickSmart310}, + {USB_DEVICE(0x046d, 0x0901), .driver_info = LogitechClickSmart510}, + {USB_DEVICE(0x04a5, 0x300c), .driver_info = BenqDC1016}, + {USB_DEVICE(0x04fc, 0x7333), .driver_info = PalmPixDC85}, + {USB_DEVICE(0x055f, 0xc200), .driver_info = MustekGsmart300}, + {USB_DEVICE(0x055f, 0xc220), .driver_info = Gsmartmini}, + {USB_DEVICE(0x06bd, 0x0404), .driver_info = AgfaCl20}, + {USB_DEVICE(0x06be, 0x0800), .driver_info = Optimedia}, + {USB_DEVICE(0x084d, 0x0003), .driver_info = DLinkDSC350}, + {USB_DEVICE(0x08ca, 0x0103), .driver_info = AiptekPocketDV}, + {USB_DEVICE(0x2899, 0x012c), .driver_info = ToptroIndus}, + {USB_DEVICE(0x8086, 0x0630), .driver_info = IntelPocketPCCamera}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/spca501.c b/drivers/media/usb/gspca/spca501.c new file mode 100644 index 000000000000..3b7f777785b4 --- /dev/null +++ b/drivers/media/usb/gspca/spca501.c @@ -0,0 +1,2054 @@ +/* + * SPCA501 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "spca501" + +#include "gspca.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned short contrast; + __u8 brightness; + __u8 colors; + __u8 blue_balance; + __u8 red_balance; + + char subtype; +#define Arowana300KCMOSCamera 0 +#define IntelCreateAndShare 1 +#define KodakDVC325 2 +#define MystFromOriUnknownCamera 3 +#define SmileIntlCamera 4 +#define ThreeComHomeConnectLite 5 +#define ViewQuestM318B 6 +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define SPCA50X_REG_USB 0x2 /* spca505 501 */ +/* + * Data to initialize a SPCA501. From a capture file provided by Bill Roehl + * With SPCA501 chip description + */ +#define CCDSP_SET /* set CCDSP parameters */ +#define TG_SET /* set time generator set */ +#undef DSPWIN_SET /* set DSP windows parameters */ +#undef ALTER_GAMA /* Set alternate set to YUV transform coeffs. */ +#define SPCA501_SNAPBIT 0x80 +#define SPCA501_SNAPCTRL 0x10 +/* Frame packet header offsets for the spca501 */ +#define SPCA501_OFFSET_GPIO 1 +#define SPCA501_OFFSET_TYPE 2 +#define SPCA501_OFFSET_TURN3A 3 +#define SPCA501_OFFSET_FRAMSEQ 4 +#define SPCA501_OFFSET_COMPRESS 5 +#define SPCA501_OFFSET_QUANT 6 +#define SPCA501_OFFSET_QUANT2 7 +#define SPCA501_OFFSET_DATA 8 + +#define SPCA501_PROP_COMP_ENABLE(d) ((d) & 1) +#define SPCA501_PROP_SNAP(d) ((d) & 0x40) +#define SPCA501_PROP_SNAP_CTRL(d) ((d) & 0x10) +#define SPCA501_PROP_COMP_THRESH(d) (((d) & 0x0e) >> 1) +#define SPCA501_PROP_COMP_QUANT(d) (((d) & 0x70) >> 4) + +/* SPCA501 CCDSP control */ +#define SPCA501_REG_CCDSP 0x01 +/* SPCA501 control/status registers */ +#define SPCA501_REG_CTLRL 0x02 + +/* registers for color correction and YUV transformation */ +#define SPCA501_A11 0x08 +#define SPCA501_A12 0x09 +#define SPCA501_A13 0x0A +#define SPCA501_A21 0x0B +#define SPCA501_A22 0x0C +#define SPCA501_A23 0x0D +#define SPCA501_A31 0x0E +#define SPCA501_A32 0x0F +#define SPCA501_A33 0x10 + +/* Data for video camera initialization before capturing */ +static const __u16 spca501_open_data[][3] = { + /* bmRequest,value,index */ + + {0x2, 0x50, 0x00}, /* C/S enable soft reset */ + {0x2, 0x40, 0x00}, /* C/S disable soft reset */ + {0x2, 0x02, 0x05}, /* C/S general purpose I/O data */ + {0x2, 0x03, 0x05}, /* C/S general purpose I/O data */ + +#ifdef CCDSP_SET + {0x1, 0x38, 0x01}, /* CCDSP options */ + {0x1, 0x05, 0x02}, /* CCDSP Optical black level for user settings */ + {0x1, 0xC0, 0x03}, /* CCDSP Optical black settings */ + + {0x1, 0x67, 0x07}, + {0x1, 0x63, 0x3f}, /* CCDSP CCD gamma enable */ + {0x1, 0x03, 0x56}, /* Add gamma correction */ + + {0x1, 0xFF, 0x15}, /* CCDSP High luminance for white balance */ + {0x1, 0x01, 0x16}, /* CCDSP Low luminance for white balance */ + +/* Color correction and RGB-to-YUV transformation coefficients changing */ +#ifdef ALTER_GAMA + {0x0, 0x00, 0x08}, /* A11 */ + {0x0, 0x00, 0x09}, /* A12 */ + {0x0, 0x90, 0x0A}, /* A13 */ + {0x0, 0x12, 0x0B}, /* A21 */ + {0x0, 0x00, 0x0C}, /* A22 */ + {0x0, 0x00, 0x0D}, /* A23 */ + {0x0, 0x00, 0x0E}, /* A31 */ + {0x0, 0x02, 0x0F}, /* A32 */ + {0x0, 0x00, 0x10}, /* A33 */ +#else + {0x1, 0x2a, 0x08}, /* A11 0x31 */ + {0x1, 0xf8, 0x09}, /* A12 f8 */ + {0x1, 0xf8, 0x0A}, /* A13 f8 */ + {0x1, 0xf8, 0x0B}, /* A21 f8 */ + {0x1, 0x14, 0x0C}, /* A22 0x14 */ + {0x1, 0xf8, 0x0D}, /* A23 f8 */ + {0x1, 0xf8, 0x0E}, /* A31 f8 */ + {0x1, 0xf8, 0x0F}, /* A32 f8 */ + {0x1, 0x20, 0x10}, /* A33 0x20 */ +#endif + {0x1, 0x00, 0x11}, /* R offset */ + {0x1, 0x00, 0x12}, /* G offset */ + {0x1, 0x00, 0x13}, /* B offset */ + {0x1, 0x00, 0x14}, /* GB offset */ + +#endif + +#ifdef TG_SET + /* Time generator manipulations */ + {0x0, 0xfc, 0x0}, /* Set up high bits of shutter speed */ + {0x0, 0x01, 0x1}, /* Set up low bits of shutter speed */ + + {0x0, 0xe4, 0x04}, /* DCLK*2 clock phase adjustment */ + {0x0, 0x08, 0x05}, /* ADCK phase adjustment, inv. ext. VB */ + {0x0, 0x03, 0x06}, /* FR phase adjustment */ + {0x0, 0x01, 0x07}, /* FCDS phase adjustment */ + {0x0, 0x39, 0x08}, /* FS phase adjustment */ + {0x0, 0x88, 0x0a}, /* FH1 phase and delay adjustment */ + {0x0, 0x03, 0x0f}, /* pixel identification */ + {0x0, 0x00, 0x11}, /* clock source selection (default) */ + + /*VERY strange manipulations with + * select DMCLP or OBPX to be ADCLP output (0x0C) + * OPB always toggle or not (0x0D) but they allow + * us to set up brightness + */ + {0x0, 0x01, 0x0c}, + {0x0, 0xe0, 0x0d}, + /* Done */ +#endif + +#ifdef DSPWIN_SET + {0x1, 0xa0, 0x01}, /* Setting image processing parameters */ + {0x1, 0x1c, 0x17}, /* Changing Windows positions X1 */ + {0x1, 0xe2, 0x19}, /* X2 */ + {0x1, 0x1c, 0x1b}, /* X3 */ + {0x1, 0xe2, 0x1d}, /* X4 */ + {0x1, 0x5f, 0x1f}, /* X5 */ + {0x1, 0x32, 0x20}, /* Y5 */ + {0x1, 0x01, 0x10}, /* Changing A33 */ +#endif + + {0x2, 0x204a, 0x07},/* Setting video compression & resolution 160x120 */ + {0x2, 0x94, 0x06}, /* Setting video no compression */ + {} +}; + +/* + The SPCAxxx docs from Sunplus document these values + in tables, one table per register number. In the data + below, dmRequest is the register number, index is the Addr, + and value is a combination of Bit values. + Bit Value (hex) + 0 01 + 1 02 + 2 04 + 3 08 + 4 10 + 5 20 + 6 40 + 7 80 + */ + +/* Data for chip initialization (set default values) */ +static const __u16 spca501_init_data[][3] = { + /* Set all the values to powerup defaults */ + /* bmRequest,value,index */ + {0x0, 0xAA, 0x00}, + {0x0, 0x02, 0x01}, + {0x0, 0x01, 0x02}, + {0x0, 0x02, 0x03}, + {0x0, 0xCE, 0x04}, + {0x0, 0x00, 0x05}, + {0x0, 0x00, 0x06}, + {0x0, 0x00, 0x07}, + {0x0, 0x00, 0x08}, + {0x0, 0x00, 0x09}, + {0x0, 0x90, 0x0A}, + {0x0, 0x12, 0x0B}, + {0x0, 0x00, 0x0C}, + {0x0, 0x00, 0x0D}, + {0x0, 0x00, 0x0E}, + {0x0, 0x02, 0x0F}, + {0x0, 0x00, 0x10}, + {0x0, 0x00, 0x11}, + {0x0, 0x00, 0x12}, + {0x0, 0x00, 0x13}, + {0x0, 0x00, 0x14}, + {0x0, 0x00, 0x15}, + {0x0, 0x00, 0x16}, + {0x0, 0x00, 0x17}, + {0x0, 0x00, 0x18}, + {0x0, 0x00, 0x19}, + {0x0, 0x00, 0x1A}, + {0x0, 0x00, 0x1B}, + {0x0, 0x00, 0x1C}, + {0x0, 0x00, 0x1D}, + {0x0, 0x00, 0x1E}, + {0x0, 0x00, 0x1F}, + {0x0, 0x00, 0x20}, + {0x0, 0x00, 0x21}, + {0x0, 0x00, 0x22}, + {0x0, 0x00, 0x23}, + {0x0, 0x00, 0x24}, + {0x0, 0x00, 0x25}, + {0x0, 0x00, 0x26}, + {0x0, 0x00, 0x27}, + {0x0, 0x00, 0x28}, + {0x0, 0x00, 0x29}, + {0x0, 0x00, 0x2A}, + {0x0, 0x00, 0x2B}, + {0x0, 0x00, 0x2C}, + {0x0, 0x00, 0x2D}, + {0x0, 0x00, 0x2E}, + {0x0, 0x00, 0x2F}, + {0x0, 0x00, 0x30}, + {0x0, 0x00, 0x31}, + {0x0, 0x00, 0x32}, + {0x0, 0x00, 0x33}, + {0x0, 0x00, 0x34}, + {0x0, 0x00, 0x35}, + {0x0, 0x00, 0x36}, + {0x0, 0x00, 0x37}, + {0x0, 0x00, 0x38}, + {0x0, 0x00, 0x39}, + {0x0, 0x00, 0x3A}, + {0x0, 0x00, 0x3B}, + {0x0, 0x00, 0x3C}, + {0x0, 0x00, 0x3D}, + {0x0, 0x00, 0x3E}, + {0x0, 0x00, 0x3F}, + {0x0, 0x00, 0x40}, + {0x0, 0x00, 0x41}, + {0x0, 0x00, 0x42}, + {0x0, 0x00, 0x43}, + {0x0, 0x00, 0x44}, + {0x0, 0x00, 0x45}, + {0x0, 0x00, 0x46}, + {0x0, 0x00, 0x47}, + {0x0, 0x00, 0x48}, + {0x0, 0x00, 0x49}, + {0x0, 0x00, 0x4A}, + {0x0, 0x00, 0x4B}, + {0x0, 0x00, 0x4C}, + {0x0, 0x00, 0x4D}, + {0x0, 0x00, 0x4E}, + {0x0, 0x00, 0x4F}, + {0x0, 0x00, 0x50}, + {0x0, 0x00, 0x51}, + {0x0, 0x00, 0x52}, + {0x0, 0x00, 0x53}, + {0x0, 0x00, 0x54}, + {0x0, 0x00, 0x55}, + {0x0, 0x00, 0x56}, + {0x0, 0x00, 0x57}, + {0x0, 0x00, 0x58}, + {0x0, 0x00, 0x59}, + {0x0, 0x00, 0x5A}, + {0x0, 0x00, 0x5B}, + {0x0, 0x00, 0x5C}, + {0x0, 0x00, 0x5D}, + {0x0, 0x00, 0x5E}, + {0x0, 0x00, 0x5F}, + {0x0, 0x00, 0x60}, + {0x0, 0x00, 0x61}, + {0x0, 0x00, 0x62}, + {0x0, 0x00, 0x63}, + {0x0, 0x00, 0x64}, + {0x0, 0x00, 0x65}, + {0x0, 0x00, 0x66}, + {0x0, 0x00, 0x67}, + {0x0, 0x00, 0x68}, + {0x0, 0x00, 0x69}, + {0x0, 0x00, 0x6A}, + {0x0, 0x00, 0x6B}, + {0x0, 0x00, 0x6C}, + {0x0, 0x00, 0x6D}, + {0x0, 0x00, 0x6E}, + {0x0, 0x00, 0x6F}, + {0x0, 0x00, 0x70}, + {0x0, 0x00, 0x71}, + {0x0, 0x00, 0x72}, + {0x0, 0x00, 0x73}, + {0x0, 0x00, 0x74}, + {0x0, 0x00, 0x75}, + {0x0, 0x00, 0x76}, + {0x0, 0x00, 0x77}, + {0x0, 0x00, 0x78}, + {0x0, 0x00, 0x79}, + {0x0, 0x00, 0x7A}, + {0x0, 0x00, 0x7B}, + {0x0, 0x00, 0x7C}, + {0x0, 0x00, 0x7D}, + {0x0, 0x00, 0x7E}, + {0x0, 0x00, 0x7F}, + {0x0, 0x00, 0x80}, + {0x0, 0x00, 0x81}, + {0x0, 0x00, 0x82}, + {0x0, 0x00, 0x83}, + {0x0, 0x00, 0x84}, + {0x0, 0x00, 0x85}, + {0x0, 0x00, 0x86}, + {0x0, 0x00, 0x87}, + {0x0, 0x00, 0x88}, + {0x0, 0x00, 0x89}, + {0x0, 0x00, 0x8A}, + {0x0, 0x00, 0x8B}, + {0x0, 0x00, 0x8C}, + {0x0, 0x00, 0x8D}, + {0x0, 0x00, 0x8E}, + {0x0, 0x00, 0x8F}, + {0x0, 0x00, 0x90}, + {0x0, 0x00, 0x91}, + {0x0, 0x00, 0x92}, + {0x0, 0x00, 0x93}, + {0x0, 0x00, 0x94}, + {0x0, 0x00, 0x95}, + {0x0, 0x00, 0x96}, + {0x0, 0x00, 0x97}, + {0x0, 0x00, 0x98}, + {0x0, 0x00, 0x99}, + {0x0, 0x00, 0x9A}, + {0x0, 0x00, 0x9B}, + {0x0, 0x00, 0x9C}, + {0x0, 0x00, 0x9D}, + {0x0, 0x00, 0x9E}, + {0x0, 0x00, 0x9F}, + {0x0, 0x00, 0xA0}, + {0x0, 0x00, 0xA1}, + {0x0, 0x00, 0xA2}, + {0x0, 0x00, 0xA3}, + {0x0, 0x00, 0xA4}, + {0x0, 0x00, 0xA5}, + {0x0, 0x00, 0xA6}, + {0x0, 0x00, 0xA7}, + {0x0, 0x00, 0xA8}, + {0x0, 0x00, 0xA9}, + {0x0, 0x00, 0xAA}, + {0x0, 0x00, 0xAB}, + {0x0, 0x00, 0xAC}, + {0x0, 0x00, 0xAD}, + {0x0, 0x00, 0xAE}, + {0x0, 0x00, 0xAF}, + {0x0, 0x00, 0xB0}, + {0x0, 0x00, 0xB1}, + {0x0, 0x00, 0xB2}, + {0x0, 0x00, 0xB3}, + {0x0, 0x00, 0xB4}, + {0x0, 0x00, 0xB5}, + {0x0, 0x00, 0xB6}, + {0x0, 0x00, 0xB7}, + {0x0, 0x00, 0xB8}, + {0x0, 0x00, 0xB9}, + {0x0, 0x00, 0xBA}, + {0x0, 0x00, 0xBB}, + {0x0, 0x00, 0xBC}, + {0x0, 0x00, 0xBD}, + {0x0, 0x00, 0xBE}, + {0x0, 0x00, 0xBF}, + {0x0, 0x00, 0xC0}, + {0x0, 0x00, 0xC1}, + {0x0, 0x00, 0xC2}, + {0x0, 0x00, 0xC3}, + {0x0, 0x00, 0xC4}, + {0x0, 0x00, 0xC5}, + {0x0, 0x00, 0xC6}, + {0x0, 0x00, 0xC7}, + {0x0, 0x00, 0xC8}, + {0x0, 0x00, 0xC9}, + {0x0, 0x00, 0xCA}, + {0x0, 0x00, 0xCB}, + {0x0, 0x00, 0xCC}, + {0x1, 0xF4, 0x00}, + {0x1, 0x38, 0x01}, + {0x1, 0x40, 0x02}, + {0x1, 0x0A, 0x03}, + {0x1, 0x40, 0x04}, + {0x1, 0x40, 0x05}, + {0x1, 0x40, 0x06}, + {0x1, 0x67, 0x07}, + {0x1, 0x31, 0x08}, + {0x1, 0x00, 0x09}, + {0x1, 0x00, 0x0A}, + {0x1, 0x00, 0x0B}, + {0x1, 0x14, 0x0C}, + {0x1, 0x00, 0x0D}, + {0x1, 0x00, 0x0E}, + {0x1, 0x00, 0x0F}, + {0x1, 0x1E, 0x10}, + {0x1, 0x00, 0x11}, + {0x1, 0x00, 0x12}, + {0x1, 0x00, 0x13}, + {0x1, 0x00, 0x14}, + {0x1, 0xFF, 0x15}, + {0x1, 0x01, 0x16}, + {0x1, 0x32, 0x17}, + {0x1, 0x23, 0x18}, + {0x1, 0xCE, 0x19}, + {0x1, 0x23, 0x1A}, + {0x1, 0x32, 0x1B}, + {0x1, 0x8D, 0x1C}, + {0x1, 0xCE, 0x1D}, + {0x1, 0x8D, 0x1E}, + {0x1, 0x00, 0x1F}, + {0x1, 0x00, 0x20}, + {0x1, 0xFF, 0x3E}, + {0x1, 0x02, 0x3F}, + {0x1, 0x00, 0x40}, + {0x1, 0x00, 0x41}, + {0x1, 0x00, 0x42}, + {0x1, 0x00, 0x43}, + {0x1, 0x00, 0x44}, + {0x1, 0x00, 0x45}, + {0x1, 0x00, 0x46}, + {0x1, 0x00, 0x47}, + {0x1, 0x00, 0x48}, + {0x1, 0x00, 0x49}, + {0x1, 0x00, 0x4A}, + {0x1, 0x00, 0x4B}, + {0x1, 0x00, 0x4C}, + {0x1, 0x00, 0x4D}, + {0x1, 0x00, 0x4E}, + {0x1, 0x00, 0x4F}, + {0x1, 0x00, 0x50}, + {0x1, 0x00, 0x51}, + {0x1, 0x00, 0x52}, + {0x1, 0x00, 0x53}, + {0x1, 0x00, 0x54}, + {0x1, 0x00, 0x55}, + {0x1, 0x00, 0x56}, + {0x1, 0x00, 0x57}, + {0x1, 0x00, 0x58}, + {0x1, 0x00, 0x59}, + {0x1, 0x00, 0x5A}, + {0x2, 0x03, 0x00}, + {0x2, 0x00, 0x01}, + {0x2, 0x00, 0x05}, + {0x2, 0x00, 0x06}, + {0x2, 0x00, 0x07}, + {0x2, 0x00, 0x10}, + {0x2, 0x00, 0x11}, + /* Strange - looks like the 501 driver doesn't do anything + * at insert time except read the EEPROM + */ + {} +}; + +/* Data for video camera init before capture. + * Capture and decoding by Colin Peart. + * This is is for the 3com HomeConnect Lite which is spca501a based. + */ +static const __u16 spca501_3com_open_data[][3] = { + /* bmRequest,value,index */ + {0x2, 0x0050, 0x0000}, /* C/S Enable TG soft reset, timing mode=010 */ + {0x2, 0x0043, 0x0000}, /* C/S Disable TG soft reset, timing mode=010 */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + +#ifdef CCDSP_SET + {0x1, 0x0020, 0x0001}, /* CCDSP Options */ + + {0x1, 0x0020, 0x0002}, /* CCDSP Black Level */ + {0x1, 0x006e, 0x0007}, /* CCDSP Gamma options */ + {0x1, 0x0090, 0x0015}, /* CCDSP Luminance Low */ + {0x1, 0x00ff, 0x0016}, /* CCDSP Luminance High */ + {0x1, 0x0003, 0x003F}, /* CCDSP Gamma correction toggle */ + +#ifdef ALTER_GAMMA + {0x1, 0x0010, 0x0008}, /* CCDSP YUV A11 */ + {0x1, 0x0000, 0x0009}, /* CCDSP YUV A12 */ + {0x1, 0x0000, 0x000a}, /* CCDSP YUV A13 */ + {0x1, 0x0000, 0x000b}, /* CCDSP YUV A21 */ + {0x1, 0x0010, 0x000c}, /* CCDSP YUV A22 */ + {0x1, 0x0000, 0x000d}, /* CCDSP YUV A23 */ + {0x1, 0x0000, 0x000e}, /* CCDSP YUV A31 */ + {0x1, 0x0000, 0x000f}, /* CCDSP YUV A32 */ + {0x1, 0x0010, 0x0010}, /* CCDSP YUV A33 */ + {0x1, 0x0000, 0x0011}, /* CCDSP R Offset */ + {0x1, 0x0000, 0x0012}, /* CCDSP G Offset */ + {0x1, 0x0001, 0x0013}, /* CCDSP B Offset */ + {0x1, 0x0001, 0x0014}, /* CCDSP BG Offset */ + {0x1, 0x003f, 0x00C1}, /* CCDSP Gamma Correction Enable */ +#endif +#endif + +#ifdef TG_SET + {0x0, 0x00fc, 0x0000}, /* TG Shutter Speed High Bits */ + {0x0, 0x0000, 0x0001}, /* TG Shutter Speed Low Bits */ + {0x0, 0x00e4, 0x0004}, /* TG DCLK*2 Adjust */ + {0x0, 0x0008, 0x0005}, /* TG ADCK Adjust */ + {0x0, 0x0003, 0x0006}, /* TG FR Phase Adjust */ + {0x0, 0x0001, 0x0007}, /* TG FCDS Phase Adjust */ + {0x0, 0x0039, 0x0008}, /* TG FS Phase Adjust */ + {0x0, 0x0088, 0x000a}, /* TG MH1 */ + {0x0, 0x0003, 0x000f}, /* TG Pixel ID */ + + /* Like below, unexplained toglleing */ + {0x0, 0x0080, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0000, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0040, 0x000c}, + {0x0, 0x0017, 0x000d}, + {0x0, 0x00c0, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0006, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0002, 0x0003}, +#endif + +#ifdef DSPWIN_SET + {0x1, 0x001c, 0x0017}, /* CCDSP W1 Start X */ + {0x1, 0x00e2, 0x0019}, /* CCDSP W2 Start X */ + {0x1, 0x001c, 0x001b}, /* CCDSP W3 Start X */ + {0x1, 0x00e2, 0x001d}, /* CCDSP W4 Start X */ + {0x1, 0x00aa, 0x001f}, /* CCDSP W5 Start X */ + {0x1, 0x0070, 0x0020}, /* CCDSP W5 Start Y */ +#endif + {0x0, 0x0001, 0x0010}, /* TG Start Clock */ + +/* {0x2, 0x006a, 0x0001}, * C/S Enable ISOSYNCH Packet Engine */ + {0x2, 0x0068, 0x0001}, /* C/S Diable ISOSYNCH Packet Engine */ + {0x2, 0x0000, 0x0005}, + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + + {0x2, 0x006a, 0x0001}, /* C/S Enable ISOSYNCH Packet Engine */ + {} +}; + +/* + * Data used to initialize a SPCA501C with HV7131B sensor. + * From a capture file taken with USBSnoop v 1.5 + * I have a "SPCA501C pc camera chipset" manual by sunplus, but some + * of the value meanings are obscure or simply "reserved". + * to do list: + * 1) Understand what every value means + * 2) Understand why some values seem to appear more than once + * 3) Write a small comment for each line of the following arrays. + */ +static const __u16 spca501c_arowana_open_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {} +}; + +static const __u16 spca501c_arowana_init_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, + {0x01, 0x0032, 0x0016}, + {0x01, 0x0000, 0x0011}, + {0x01, 0x0000, 0x0012}, + {0x01, 0x0000, 0x0013}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x000f, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x001e, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x002d, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {} +}; + +/* Unknown camera from Ori Usbid 0x0000:0x0000 */ +/* Based on snoops from Ori Cohen */ +static const __u16 spca501c_mysterious_open_data[][3] = { + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, +/* DSP Registers */ + {0x01, 0x0016, 0x0011}, /* RGB offset */ + {0x01, 0x0000, 0x0012}, + {0x01, 0x0006, 0x0013}, + {0x01, 0x0078, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0046, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x00, 0x0025, 0x0000}, +/* {0x00, 0x0000, 0x0000 }, */ +/* Part 2 */ +/* TG Registers */ + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {} +}; + +/* Based on snoops from Ori Cohen */ +static const __u16 spca501c_mysterious_init_data[][3] = { +/* Part 3 */ +/* TG registers */ +/* {0x00, 0x0000, 0x0000}, */ + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x0006, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0001, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, /* 640 */ + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, /* 480 */ + {0x00, 0x0000, 0x0024}, /* Offset H hight */ + {0x00, 0x00d3, 0x0025}, /* low */ + {0x00, 0x0000, 0x0026}, /* Offset V */ + {0x00, 0x000d, 0x0027}, /* low */ + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, +/* DSP Registers */ + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, /* Level Calc bit7 ->1 Auto */ + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x000f, 0x0008}, /* A11 Color correction coeff */ + {0x01, 0x002d, 0x0009}, /* A12 */ + {0x01, 0x0005, 0x000a}, /* A13 */ + {0x01, 0x0023, 0x000b}, /* A21 */ + {0x01, 0x00e0, 0x000c}, /* A22 */ + {0x01, 0x00fd, 0x000d}, /* A23 */ + {0x01, 0x00f4, 0x000e}, /* A31 */ + {0x01, 0x00e4, 0x000f}, /* A32 */ + {0x01, 0x0028, 0x0010}, /* A33 */ + {0x01, 0x00ff, 0x0015}, /* Reserved */ + {0x01, 0x0001, 0x0016}, /* Reserved */ + {0x01, 0x0032, 0x0017}, /* Win1 Start begin */ + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, /* Win1 Start end */ + {0x01, 0x00ff, 0x003e}, /* Reserved begin */ + {0x01, 0x0002, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0003, 0x0056}, /* Reserved end */ + {0x01, 0x0060, 0x0057}, /* Edge Gain */ + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, /* Edge Bandwidth */ + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x200a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, +/* Part 4 */ + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x004e, 0x0000}, +/* Part 5 */ + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x000f, 0x0008}, + {0x01, 0x002d, 0x0009}, + {0x01, 0x0005, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffe0, 0x000c}, + {0x01, 0xfffd, 0x000d}, + {0x01, 0xfff4, 0x000e}, + {0x01, 0xffe4, 0x000f}, + {0x01, 0x0028, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, /* c8 Poids fort Luma */ + {0x01, 0x0032, 0x0016}, /* 32 */ + {0x01, 0x0016, 0x0011}, /* R 00 */ + {0x01, 0x0016, 0x0012}, /* G 00 */ + {0x01, 0x0016, 0x0013}, /* B 00 */ + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x", + req, index, value); + if (ret < 0) + pr_err("reg write: error %d\n", ret); + return ret; +} + + +static int write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Reg write failed for 0x%02x,0x%02x,0x%02x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, val); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + reg_write(gspca_dev->dev, 0x00, 0x00, + (val >> 8) & 0xff); + reg_write(gspca_dev->dev, 0x00, 0x01, + val & 0xff); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, val); +} + +static void setblue_balance(struct gspca_dev *gspca_dev, s32 val) +{ + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, val); +} + +static void setred_balance(struct gspca_dev *gspca_dev, s32 val) +{ + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, val); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + sd->subtype = id->driver_info; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->subtype) { + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_arowana_init_data)) + goto error; + break; + case MystFromOriUnknownCamera: + /* Unknown Ori CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_mysterious_open_data)) + goto error; + break; + default: + /* generic spca501 init data */ + if (write_vector(gspca_dev, spca501_init_data)) + goto error; + break; + } + PDEBUG(D_STREAM, "Initializing SPCA501 finished"); + return 0; +error: + return -EINVAL; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int mode; + + switch (sd->subtype) { + case ThreeComHomeConnectLite: + /* Special handling for 3com data */ + write_vector(gspca_dev, spca501_3com_open_data); + break; + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + write_vector(gspca_dev, spca501c_arowana_open_data); + break; + case MystFromOriUnknownCamera: + /* Unknown CMOS Camera data */ + write_vector(gspca_dev, spca501c_mysterious_init_data); + break; + default: + /* Generic 501 open data */ + write_vector(gspca_dev, spca501_open_data); + } + + /* memorize the wanted pixel format */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + + /* Enable ISO packet machine CTRL reg=2, + * index=1 bitmask=0x2 (bit ordinal 1) */ + reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94); + switch (mode) { + case 0: /* 640x480 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a); + break; + case 1: /* 320x240 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a); + break; + default: +/* case 2: * 160x120 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a); + break; + } + reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet + * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */ + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00); +} + +/* called on streamoff with alt 0 and on disconnect */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + if (!gspca_dev->present) + return; + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + switch (data[0]) { + case 0: /* start of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + data += SPCA501_OFFSET_DATA; + len -= SPCA501_OFFSET_DATA; + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data++; + len--; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + setblue_balance(gspca_dev, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + setred_balance(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 64725, 1, 64725); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 63, 1, 20); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0002), .driver_info = KodakDVC325}, + {USB_DEVICE(0x0497, 0xc001), .driver_info = SmileIntlCamera}, + {USB_DEVICE(0x0506, 0x00df), .driver_info = ThreeComHomeConnectLite}, + {USB_DEVICE(0x0733, 0x0401), .driver_info = IntelCreateAndShare}, + {USB_DEVICE(0x0733, 0x0402), .driver_info = ViewQuestM318B}, + {USB_DEVICE(0x1776, 0x501c), .driver_info = Arowana300KCMOSCamera}, + {USB_DEVICE(0x0000, 0x0000), .driver_info = MystFromOriUnknownCamera}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/spca505.c b/drivers/media/usb/gspca/spca505.c new file mode 100644 index 000000000000..bc7d67c3cb04 --- /dev/null +++ b/drivers/media/usb/gspca/spca505.c @@ -0,0 +1,807 @@ +/* + * SPCA505 chip based cameras initialization data + * + * V4L2 by Jean-Francis Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "spca505" + +#include "gspca.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 subtype; +#define IntelPCCameraPro 0 +#define Nxultra 1 +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SPCA50X_REG_USB 0x02 /* spca505 501 */ + +#define SPCA50X_USB_CTRL 0x00 /* spca505 */ +#define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */ + +#define SPCA50X_REG_GLOBAL 0x03 /* spca505 */ +#define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */ +#define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */ + +#define SPCA50X_GLOBAL_MISC1 0x01 /* 505 */ +#define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */ +#define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */ + +/* Image format and compression control */ +#define SPCA50X_REG_COMPRESS 0x04 + +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static const u8 spca505_init_data[][3] = { + /* bmRequest,value,index */ + {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3}, + /* Sensor reset */ + {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3}, + {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1}, + /* Block USB reset */ + {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, SPCA50X_GLOBAL_MISC0}, + + {0x05, 0x01, 0x10}, + /* Maybe power down some stuff */ + {0x05, 0x0f, 0x11}, + + /* Setup internal CCD ? */ + {0x06, 0x10, 0x08}, + {0x06, 0x00, 0x09}, + {0x06, 0x00, 0x0a}, + {0x06, 0x00, 0x0b}, + {0x06, 0x10, 0x0c}, + {0x06, 0x00, 0x0d}, + {0x06, 0x00, 0x0e}, + {0x06, 0x00, 0x0f}, + {0x06, 0x10, 0x10}, + {0x06, 0x02, 0x11}, + {0x06, 0x00, 0x12}, + {0x06, 0x04, 0x13}, + {0x06, 0x02, 0x14}, + {0x06, 0x8a, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0xb6, 0x53}, + {0x06, 0x3d, 0x54}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static const u8 spca505_open_data_ccd[][3] = { + /* bmRequest,value,index */ + /* Internal CCD data set */ + {0x03, 0x04, 0x01}, + /* This could be a reset */ + {0x03, 0x00, 0x01}, + + /* Setup compression and image registers. 0x6 and 0x7 seem to be + related to H&V hold, and are resolution mode specific */ + {0x04, 0x10, 0x01}, + /* DIFF(0x50), was (0x10) */ + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x20, 0x06}, + {0x04, 0x20, 0x07}, + + {0x08, 0x0a, 0x00}, + /* DIFF (0x4a), was (0xa) */ + + {0x05, 0x00, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x00, 0x00}, + /* DIFF not written */ + {0x05, 0x00, 0x01}, + /* DIFF not written */ + {0x05, 0x00, 0x02}, + /* DIFF not written */ + {0x05, 0x00, 0x03}, + /* DIFF not written */ + {0x05, 0x00, 0x04}, + /* DIFF not written */ + {0x05, 0x80, 0x05}, + /* DIFF not written */ + {0x05, 0xe0, 0x06}, + /* DIFF not written */ + {0x05, 0x20, 0x07}, + /* DIFF not written */ + {0x05, 0xa0, 0x08}, + /* DIFF not written */ + {0x05, 0x0, 0x12}, + /* DIFF not written */ + {0x05, 0x02, 0x0f}, + /* DIFF not written */ + {0x05, 0x10, 0x46}, + /* DIFF not written */ + {0x05, 0x8, 0x4a}, + /* DIFF not written */ + + {0x03, 0x08, 0x03}, + /* DIFF (0x3,0x28,0x3) */ + {0x03, 0x08, 0x01}, + {0x03, 0x0c, 0x03}, + /* DIFF not written */ + {0x03, 0x21, 0x00}, + /* DIFF (0x39) */ + +/* Extra block copied from init to hopefully ensure CCD is in a sane state */ + {0x06, 0x10, 0x08}, + {0x06, 0x00, 0x09}, + {0x06, 0x00, 0x0a}, + {0x06, 0x00, 0x0b}, + {0x06, 0x10, 0x0c}, + {0x06, 0x00, 0x0d}, + {0x06, 0x00, 0x0e}, + {0x06, 0x00, 0x0f}, + {0x06, 0x10, 0x10}, + {0x06, 0x02, 0x11}, + {0x06, 0x00, 0x12}, + {0x06, 0x04, 0x13}, + {0x06, 0x02, 0x14}, + {0x06, 0x8a, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0xb6, 0x53}, + {0x06, 0x3d, 0x54}, + /* End of extra block */ + + {0x06, 0x3f, 0x1}, + /* Block skipped */ + {0x06, 0x10, 0x02}, + {0x06, 0x64, 0x07}, + {0x06, 0x10, 0x08}, + {0x06, 0x00, 0x09}, + {0x06, 0x00, 0x0a}, + {0x06, 0x00, 0x0b}, + {0x06, 0x10, 0x0c}, + {0x06, 0x00, 0x0d}, + {0x06, 0x00, 0x0e}, + {0x06, 0x00, 0x0f}, + {0x06, 0x10, 0x10}, + {0x06, 0x02, 0x11}, + {0x06, 0x00, 0x12}, + {0x06, 0x04, 0x13}, + {0x06, 0x02, 0x14}, + {0x06, 0x8a, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0xb6, 0x53}, + {0x06, 0x3d, 0x54}, + {0x06, 0x60, 0x57}, + {0x06, 0x20, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x05, 0x5a}, + + {0x05, 0x01, 0xc0}, + {0x05, 0x10, 0xcb}, + {0x05, 0x80, 0xc1}, + /* */ + {0x05, 0x0, 0xc2}, + /* 4 was 0 */ + {0x05, 0x00, 0xca}, + {0x05, 0x80, 0xc1}, + /* */ + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x0, 0xc1}, + /* */ + {0x05, 0x00, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + /* */ + {0x05, 0x17, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x80, 0xc1}, + /* */ + {0x05, 0x06, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x80, 0xc1}, + /* */ + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, + + {0x03, 0x4c, 0x3}, + {0x03, 0x18, 0x1}, + + {0x06, 0x70, 0x51}, + {0x06, 0xbe, 0x53}, + {0x06, 0x71, 0x57}, + {0x06, 0x20, 0x58}, + {0x06, 0x05, 0x59}, + {0x06, 0x15, 0x5a}, + + {0x04, 0x00, 0x08}, + /* Compress = OFF (0x1 to turn on) */ + {0x04, 0x12, 0x09}, + {0x04, 0x21, 0x0a}, + {0x04, 0x10, 0x0b}, + {0x04, 0x21, 0x0c}, + {0x04, 0x05, 0x00}, + /* was 5 (Image Type ? ) */ + {0x04, 0x00, 0x01}, + + {0x06, 0x3f, 0x01}, + + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x40, 0x06}, + {0x04, 0x40, 0x07}, + + {0x06, 0x1c, 0x17}, + {0x06, 0xe2, 0x19}, + {0x06, 0x1c, 0x1b}, + {0x06, 0xe2, 0x1d}, + {0x06, 0xaa, 0x1f}, + {0x06, 0x70, 0x20}, + + {0x05, 0x01, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x01, 0x00}, + {0x05, 0x05, 0x01}, + {0x05, 0x00, 0xc1}, + /* */ + {0x05, 0x00, 0xc2}, + {0x05, 0x00, 0xca}, + + {0x06, 0x70, 0x51}, + {0x06, 0xbe, 0x53}, + {} +}; + +/* + * Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) + * SPCA505b chip based cameras initialization data + */ +/* jfm */ +#define initial_brightness 0x7f /* 0x0(white)-0xff(black) */ +/* #define initial_brightness 0x0 //0x0(white)-0xff(black) */ +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static const u8 spca505b_init_data[][3] = { +/* start */ + {0x02, 0x00, 0x00}, /* init */ + {0x02, 0x00, 0x01}, + {0x02, 0x00, 0x02}, + {0x02, 0x00, 0x03}, + {0x02, 0x00, 0x04}, + {0x02, 0x00, 0x05}, + {0x02, 0x00, 0x06}, + {0x02, 0x00, 0x07}, + {0x02, 0x00, 0x08}, + {0x02, 0x00, 0x09}, + {0x03, 0x00, 0x00}, + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x02}, + {0x03, 0x00, 0x03}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x05}, + {0x03, 0x00, 0x06}, + {0x04, 0x00, 0x00}, + {0x04, 0x00, 0x02}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x00, 0x06}, + {0x04, 0x00, 0x07}, + {0x04, 0x00, 0x08}, + {0x04, 0x00, 0x09}, + {0x04, 0x00, 0x0a}, + {0x04, 0x00, 0x0b}, + {0x04, 0x00, 0x0c}, + {0x07, 0x00, 0x00}, + {0x07, 0x00, 0x03}, + {0x08, 0x00, 0x00}, + {0x08, 0x00, 0x01}, + {0x08, 0x00, 0x02}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x11}, + {0x06, 0x00, 0x14}, + {0x06, 0x00, 0x13}, + {0x06, 0x28, 0x51}, + {0x06, 0xff, 0x53}, + {0x02, 0x00, 0x08}, + + {0x03, 0x00, 0x03}, + {0x03, 0x10, 0x03}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static const u8 spca505b_open_data_ccd[][3] = { + +/* {0x02,0x00,0x00}, */ + {0x03, 0x04, 0x01}, /* rst */ + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x00}, + {0x03, 0x21, 0x00}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x03}, + {0x03, 0x18, 0x03}, + {0x03, 0x08, 0x01}, + {0x03, 0x1c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x18, 0x01}, + +/* same as 505 */ + {0x04, 0x10, 0x01}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x20, 0x06}, + {0x04, 0x20, 0x07}, + + {0x08, 0x0a, 0x00}, + + {0x05, 0x00, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x00, 0x12}, + {0x05, 0x6f, 0x00}, + {0x05, initial_brightness >> 6, 0x00}, + {0x05, (initial_brightness << 2) & 0xff, 0x01}, + {0x05, 0x00, 0x02}, + {0x05, 0x01, 0x03}, + {0x05, 0x00, 0x04}, + {0x05, 0x03, 0x05}, + {0x05, 0xe0, 0x06}, + {0x05, 0x20, 0x07}, + {0x05, 0xa0, 0x08}, + {0x05, 0x00, 0x12}, + {0x05, 0x02, 0x0f}, + {0x05, 0x80, 0x14}, /* max exposure off (0=on) */ + {0x05, 0x01, 0xb0}, + {0x05, 0x01, 0xbf}, + {0x03, 0x02, 0x06}, + {0x05, 0x10, 0x46}, + {0x05, 0x08, 0x4a}, + + {0x06, 0x00, 0x01}, + {0x06, 0x10, 0x02}, + {0x06, 0x64, 0x07}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x04, 0x00, 0x01}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x11, 0x10}, /* contrast */ + {0x06, 0x00, 0x11}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x13}, + {0x06, 0x00, 0x14}, + {0x06, 0x9d, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0x7c, 0x53}, + {0x06, 0x40, 0x54}, + {0x06, 0x02, 0x57}, + {0x06, 0x03, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x05, 0x5a}, + {0x06, 0x03, 0x56}, + {0x06, 0x02, 0x3f}, + {0x06, 0x00, 0x40}, + {0x06, 0x39, 0x41}, + {0x06, 0x69, 0x42}, + {0x06, 0x87, 0x43}, + {0x06, 0x9e, 0x44}, + {0x06, 0xb1, 0x45}, + {0x06, 0xbf, 0x46}, + {0x06, 0xcc, 0x47}, + {0x06, 0xd5, 0x48}, + {0x06, 0xdd, 0x49}, + {0x06, 0xe3, 0x4a}, + {0x06, 0xe8, 0x4b}, + {0x06, 0xed, 0x4c}, + {0x06, 0xf2, 0x4d}, + {0x06, 0xf7, 0x4e}, + {0x06, 0xfc, 0x4f}, + {0x06, 0xff, 0x50}, + + {0x05, 0x01, 0xc0}, + {0x05, 0x10, 0xcb}, + {0x05, 0x40, 0xc1}, + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0xc0, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x59, 0xc2}, + {0x05, 0x00, 0xca}, + {0x04, 0x00, 0x01}, + {0x05, 0x80, 0xc1}, + {0x05, 0xec, 0xc2}, + {0x05, 0x0, 0xca}, + + {0x06, 0x02, 0x57}, + {0x06, 0x01, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x0a, 0x5a}, + {0x06, 0x01, 0x57}, + {0x06, 0x8a, 0x03}, + {0x06, 0x0a, 0x6c}, + {0x06, 0x30, 0x01}, + {0x06, 0x20, 0x02}, + {0x06, 0x00, 0x03}, + + {0x05, 0x8c, 0x25}, + + {0x06, 0x4d, 0x51}, /* maybe saturation (4d) */ + {0x06, 0x84, 0x53}, /* making green (84) */ + {0x06, 0x00, 0x57}, /* sharpness (1) */ + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, /* maybe hue (18) */ + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, /* maybe contrast (18) */ + + {0x05, 0x01, 0x02}, + + {0x04, 0x00, 0x08}, /* compression */ + {0x04, 0x12, 0x09}, + {0x04, 0x21, 0x0a}, + {0x04, 0x10, 0x0b}, + {0x04, 0x21, 0x0c}, + {0x04, 0x1d, 0x00}, /* imagetype (1d) */ + {0x04, 0x41, 0x01}, /* hardware snapcontrol */ + + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x10, 0x06}, + {0x04, 0x10, 0x07}, + {0x04, 0x40, 0x06}, + {0x04, 0x40, 0x07}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + + {0x06, 0x1c, 0x17}, + {0x06, 0xe2, 0x19}, + {0x06, 0x1c, 0x1b}, + {0x06, 0xe2, 0x1d}, + {0x06, 0x5f, 0x1f}, + {0x06, 0x32, 0x20}, + + {0x05, initial_brightness >> 6, 0x00}, + {0x05, (initial_brightness << 2) & 0xff, 0x01}, + {0x05, 0x06, 0xc1}, + {0x05, 0x58, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x00, 0x11}, + {} +}; + +static int reg_write(struct usb_device *dev, + u16 req, u16 index, u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d", + req, index, value, ret); + if (ret < 0) + pr_err("reg write: error %d\n", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct gspca_dev *gspca_dev, + u16 req, /* bRequest */ + u16 index) /* wIndex */ +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, 2, + 500); /* timeout */ + if (ret < 0) + return ret; + return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const u8 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + sd->subtype = id->driver_info; + if (sd->subtype != IntelPCCameraPro) + cam->nmodes = ARRAY_SIZE(vga_mode); + else /* no 640x480 for IntelPCCameraPro */ + cam->nmodes = ARRAY_SIZE(vga_mode) - 1; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (write_vector(gspca_dev, + sd->subtype == Nxultra + ? spca505b_init_data + : spca505_init_data)) + return -EIO; + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +{ + reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6); + reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int ret, mode; + static u8 mode_tb[][3] = { + /* r00 r06 r07 */ + {0x00, 0x10, 0x10}, /* 640x480 */ + {0x01, 0x1a, 0x1a}, /* 352x288 */ + {0x02, 0x1c, 0x1d}, /* 320x240 */ + {0x04, 0x34, 0x34}, /* 176x144 */ + {0x05, 0x40, 0x40} /* 160x120 */ + }; + + if (sd->subtype == Nxultra) + write_vector(gspca_dev, spca505b_open_data_ccd); + else + write_vector(gspca_dev, spca505_open_data_ccd); + ret = reg_read(gspca_dev, 0x06, 0x16); + + if (ret < 0) { + PDEBUG(D_ERR|D_CONF, + "register read failed err: %d", + ret); + return ret; + } + if (ret != 0x0101) { + pr_err("After vector read returns 0x%04x should be 0x0101\n", + ret); + } + + ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a); + if (ret < 0) + return ret; + reg_write(gspca_dev->dev, 0x05, 0xc2, 0x12); + + /* necessary because without it we can see stream + * only once after loading module */ + /* stopping usb registers Tomasz change */ + reg_write(dev, 0x02, 0x00, 0x00); + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_write(dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]); + reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]); + reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]); + + return reg_write(dev, SPCA50X_REG_USB, + SPCA50X_USB_CTRL, + SPCA50X_CUSB_ENABLE); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet machine */ + reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); +} + +/* called on streamoff with alt 0 and on disconnect */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + if (!gspca_dev->present) + return; + + /* This maybe reset or power control */ + reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); + reg_write(gspca_dev->dev, 0x03, 0x01, 0x00); + reg_write(gspca_dev->dev, 0x03, 0x00, 0x01); + reg_write(gspca_dev->dev, 0x05, 0x10, 0x01); + reg_write(gspca_dev->dev, 0x05, 0x11, 0x0f); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + switch (data[0]) { + case 0: /* start of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + break; + case 0xff: /* drop */ + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + break; + } +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init_controls = sd_init_controls, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401d), .driver_info = Nxultra}, + {USB_DEVICE(0x0733, 0x0430), .driver_info = IntelPCCameraPro}, +/*fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/spca506.c b/drivers/media/usb/gspca/spca506.c new file mode 100644 index 000000000000..969bb5a4cd93 --- /dev/null +++ b/drivers/media/usb/gspca/spca506.c @@ -0,0 +1,612 @@ +/* + * SPCA506 chip based cameras function + * M Xhaard 15/04/2004 based on different work Mark Taylor and others + * and my own snoopy file on a pv-321c donate by a german compagny + * "Firma Frank Gmbh" from Saarbruecken + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca506" + +#include "gspca.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + char norme; + char channel; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 5}, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SAA7113_bright 0x0a /* defaults 0x80 */ +#define SAA7113_contrast 0x0b /* defaults 0x47 */ +#define SAA7113_saturation 0x0c /* defaults 0x40 */ +#define SAA7113_hue 0x0d /* defaults 0x00 */ +#define SAA7113_I2C_BASE_WRITE 0x4a + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 req, + __u16 index, + __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + NULL, 0, 500); +} + +static void spca506_Initi2c(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); +} + +static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur, + __u16 reg) +{ + int retry = 60; + + reg_w(gspca_dev->dev, 0x07, reg, 0x0001); + reg_w(gspca_dev->dev, 0x07, valeur, 0x0000); + while (retry--) { + reg_r(gspca_dev, 0x07, 0x0003, 2); + if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00) + break; + } +} + +static void spca506_SetNormeInput(struct gspca_dev *gspca_dev, + __u16 norme, + __u16 channel) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* fixme: check if channel == 0..3 and 6..9 (8 values) */ + __u8 setbit0 = 0x00; + __u8 setbit1 = 0x00; + __u8 videomask = 0x00; + + PDEBUG(D_STREAM, "** Open Set Norme **"); + spca506_Initi2c(gspca_dev); + /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */ + /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */ + /* and exclude SAA7113 reserved channel set default 0 otherwise */ + if (norme & V4L2_STD_NTSC) + setbit0 = 0x01; + if (channel == 4 || channel == 5 || channel > 9) + channel = 0; + if (channel < 4) + setbit1 = 0x02; + videomask = (0x48 | setbit0 | setbit1); + reg_w(gspca_dev->dev, 0x08, videomask, 0x0000); + spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02); + + if (norme & V4L2_STD_NTSC) + spca506_WriteI2c(gspca_dev, 0x33, 0x0e); + /* Chrominance Control NTSC N */ + else if (norme & V4L2_STD_SECAM) + spca506_WriteI2c(gspca_dev, 0x53, 0x0e); + /* Chrominance Control SECAM */ + else + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); + /* Chrominance Control PAL BGHIV */ + + sd->norme = norme; + sd->channel = channel; + PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask); + PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel); +} + +static void spca506_GetNormeInput(struct gspca_dev *gspca_dev, + __u16 *norme, __u16 *channel) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Read the register is not so good value change so + we use your own copy in spca50x struct */ + *norme = sd->norme; + *channel = sd->channel; + PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel); +} + +static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code, + __u16 xmult, __u16 ymult) +{ + struct usb_device *dev = gspca_dev->dev; + + PDEBUG(D_STREAM, "** SetSize **"); + reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000); + /* Soft snap 0x40 Hard 0x41 */ + reg_w(dev, 0x04, 0x41, 0x0001); + reg_w(dev, 0x04, 0x00, 0x0002); + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0003); + + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0004); + /* reserved */ + reg_w(dev, 0x04, 0x01, 0x0005); + /* reserced */ + reg_w(dev, 0x04, xmult, 0x0006); + /* reserved */ + reg_w(dev, 0x04, ymult, 0x0007); + /* compression 1 */ + reg_w(dev, 0x04, 0x00, 0x0008); + /* T=64 -> 2 */ + reg_w(dev, 0x04, 0x00, 0x0009); + /* threshold2D */ + reg_w(dev, 0x04, 0x21, 0x000a); + /* quantization */ + reg_w(dev, 0x04, 0x00, 0x000b); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0000); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + /* Init on PAL and composite input0 */ + spca506_SetNormeInput(gspca_dev, 0, 0); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + reg_w(dev, 0x05, 0x00, 0x0000); + reg_w(dev, 0x05, 0xef, 0x0001); + reg_w(dev, 0x05, 0x00, 0x00c1); + reg_w(dev, 0x05, 0x00, 0x00c2); + reg_w(dev, 0x06, 0x18, 0x0002); + reg_w(dev, 0x06, 0xf5, 0x0011); + reg_w(dev, 0x06, 0x02, 0x0012); + reg_w(dev, 0x06, 0xfb, 0x0013); + reg_w(dev, 0x06, 0x00, 0x0014); + reg_w(dev, 0x06, 0xa4, 0x0051); + reg_w(dev, 0x06, 0x40, 0x0052); + reg_w(dev, 0x06, 0x71, 0x0053); + reg_w(dev, 0x06, 0x40, 0x0054); + /************************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + /* for a better reading mx :) */ + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); + spca506_WriteI2c(gspca_dev, 0xc0, 0x02); + /* input composite video */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + spca506_WriteI2c(gspca_dev, 0x98, 0x08); + spca506_WriteI2c(gspca_dev, 0x03, 0x09); + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); + spca506_WriteI2c(gspca_dev, 0x01, 0x13); + spca506_WriteI2c(gspca_dev, 0x00, 0x14); + spca506_WriteI2c(gspca_dev, 0x00, 0x15); + spca506_WriteI2c(gspca_dev, 0x00, 0x16); + spca506_WriteI2c(gspca_dev, 0x00, 0x17); + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + PDEBUG(D_STREAM, "** Close Init *"); + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u16 norme; + __u16 channel; + + /**************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */ +/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + /* Analog Input Control 2 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + /* Analog Input Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + /* Analog Input Control 4 */ + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + /* Horizontal Sync Start 0xe9-0x0d */ + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + /* Horizontal Sync Stop 0x0d-0xf0 */ + + spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */ +/* Defaults value */ + spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */ + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + /* Luminance Brightness */ + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */ + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + /* Chrominance Saturation */ + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + /* Chrominance Hue Control */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + /* Chrominance Gain Control */ + /**************************************/ + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + /* Format/Delay Control */ + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */ + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */ + spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */ + spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */ + spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */ + spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */ + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + /**************************************/ + reg_w(dev, 0x05, 0x00, 0x0003); + reg_w(dev, 0x05, 0x00, 0x0004); + reg_w(dev, 0x03, 0x10, 0x0001); + reg_w(dev, 0x03, 0x78, 0x0000); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + spca506_Setsize(gspca_dev, 0, 0x10, 0x10); + break; + case 1: + spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a); + break; + case 2: + spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c); + break; + case 4: + spca506_Setsize(gspca_dev, 4, 0x34, 0x34); + break; + default: +/* case 5: */ + spca506_Setsize(gspca_dev, 5, 0x40, 0x40); + break; + } + + /* compress setting and size */ + /* set i2c luma */ + reg_w(dev, 0x02, 0x01, 0x0000); + reg_w(dev, 0x03, 0x12, 0x0000); + reg_r(gspca_dev, 0x04, 0x0001, 2); + PDEBUG(D_STREAM, "webcam started"); + spca506_GetNormeInput(gspca_dev, &norme, &channel); + spca506_SetNormeInput(gspca_dev, norme, channel); + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + switch (data[0]) { + case 0: /* start of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + break; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + break; + } +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, val, SAA7113_bright); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, val, SAA7113_contrast); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, val, SAA7113_saturation); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void sethue(struct gspca_dev *gspca_dev, s32 val) +{ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, val, SAA7113_hue); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_HUE: + sethue(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 0x47); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 0x40); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] __devinitconst = { + {USB_DEVICE(0x06e1, 0xa190)}, +/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505 + {USB_DEVICE(0x0733, 0x0430)}, */ + {USB_DEVICE(0x0734, 0x043b)}, + {USB_DEVICE(0x99fa, 0x8988)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int __devinit sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/spca508.c b/drivers/media/usb/gspca/spca508.c new file mode 100644 index 000000000000..1286b4170b88 --- /dev/null +++ b/drivers/media/usb/gspca/spca508.c @@ -0,0 +1,1540 @@ +/* + * SPCA508 chip based cameras subdriver + * + * Copyright (C) 2009 Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "spca508" + +#include "gspca.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 subtype; +#define CreativeVista 0 +#define HamaUSBSightcam 1 +#define HamaUSBSightcam2 2 +#define IntelEasyPCCamera 3 +#define MicroInnovationIC200 4 +#define ViewQuestVQ110 5 +}; + +static const struct v4l2_pix_format sif_mode[] = { + {160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* Frame packet header offsets for the spca508 */ +#define SPCA508_OFFSET_DATA 37 + +/* + * Initialization data: this is the first set-up data written to the + * device (before the open data). + */ +static const u16 spca508_init_data[][2] = { + {0x0000, 0x870b}, + + {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ + {0x0003, 0x8111}, /* Reset compression & memory */ + {0x0000, 0x8110}, /* Disable all outputs */ + /* READ {0x0000, 0x8114} -> 0000: 00 */ + {0x0000, 0x8114}, /* SW GPIO data */ + {0x0008, 0x8110}, /* Enable charge pump output */ + {0x0002, 0x8116}, /* 200 kHz pump clock */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ + {0x0003, 0x8111}, /* Reset compression & memory */ + {0x0000, 0x8111}, /* Normal mode (not reset) */ + {0x0098, 0x8110}, + /* Enable charge pump output, sync.serial,external 2x clock */ + {0x000d, 0x8114}, /* SW GPIO data */ + {0x0002, 0x8116}, /* 200 kHz pump clock */ + {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ +/* --------------------------------------- */ + {0x000f, 0x8402}, /* memory bank */ + {0x0000, 0x8403}, /* ... address */ +/* --------------------------------------- */ +/* 0x88__ is Synchronous Serial Interface. */ +/* TBD: This table could be expressed more compactly */ +/* using spca508_write_i2c_vector(). */ +/* TBD: Should see if the values in spca50x_i2c_data */ +/* would work with the VQ110 instead of the values */ +/* below. */ + {0x00c0, 0x8804}, /* SSI slave addr */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + {0x0012, 0x8801}, /* SSI reg addr */ + {0x0080, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + {0x0012, 0x8801}, /* SSI reg addr */ + {0x0000, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + {0x0011, 0x8801}, /* SSI reg addr */ + {0x0040, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0013, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0014, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0015, 0x8801}, + {0x0001, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0016, 0x8801}, + {0x0003, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0017, 0x8801}, + {0x0036, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0018, 0x8801}, + {0x00ec, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x001a, 0x8801}, + {0x0094, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x001b, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0027, 0x8801}, + {0x00a2, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0028, 0x8801}, + {0x0040, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002a, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002b, 0x8801}, + {0x00a8, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002c, 0x8801}, + {0x00fe, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002d, 0x8801}, + {0x0003, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0038, 0x8801}, + {0x0083, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0033, 0x8801}, + {0x0081, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0034, 0x8801}, + {0x004a, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0039, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0010, 0x8801}, + {0x00a8, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0006, 0x8801}, + {0x0058, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0000, 0x8801}, + {0x0004, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0040, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0041, 0x8801}, + {0x000c, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0042, 0x8801}, + {0x000c, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0043, 0x8801}, + {0x0028, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0044, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0045, 0x8801}, + {0x0020, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0046, 0x8801}, + {0x0020, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0047, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0048, 0x8801}, + {0x004c, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0049, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x004a, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x004b, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* --------------------------------------- */ + {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + {0x0000, 0x8701}, /* CKx1 clock delay adj */ + {0x0000, 0x8701}, /* CKx1 clock delay adj */ + {0x0001, 0x870c}, /* CKOx2 output */ + /* --------------------------------------- */ + {0x0080, 0x8600}, /* Line memory read counter (L) */ + {0x0001, 0x8606}, /* reserved */ + {0x0064, 0x8607}, /* Line memory read counter (H) 0x6480=25,728 */ + {0x002a, 0x8601}, /* CDSP sharp interpolation mode, + * line sel for color sep, edge enhance enab */ + {0x0000, 0x8602}, /* optical black level for user settng = 0 */ + {0x0080, 0x8600}, /* Line memory read counter (L) */ + {0x000a, 0x8603}, /* optical black level calc mode: + * auto; optical black offset = 10 */ + {0x00df, 0x865b}, /* Horiz offset for valid pixels (L)=0xdf */ + {0x0012, 0x865c}, /* Vert offset for valid lines (L)=0x12 */ + +/* The following two lines seem to be the "wrong" resolution. */ +/* But perhaps these indicate the actual size of the sensor */ +/* rather than the size of the current video mode. */ + {0x0058, 0x865d}, /* Horiz valid pixels (*4) (L) = 352 */ + {0x0048, 0x865e}, /* Vert valid lines (*4) (L) = 288 */ + + {0x0015, 0x8608}, /* A11 Coef ... */ + {0x0030, 0x8609}, + {0x00fb, 0x860a}, + {0x003e, 0x860b}, + {0x00ce, 0x860c}, + {0x00f4, 0x860d}, + {0x00eb, 0x860e}, + {0x00dc, 0x860f}, + {0x0039, 0x8610}, + {0x0001, 0x8611}, /* R offset for white balance ... */ + {0x0000, 0x8612}, + {0x0001, 0x8613}, + {0x0000, 0x8614}, + {0x005b, 0x8651}, /* R gain for white balance ... */ + {0x0040, 0x8652}, + {0x0060, 0x8653}, + {0x0040, 0x8654}, + {0x0000, 0x8655}, + {0x0001, 0x863f}, /* Fixed gamma correction enable, USB control, + * lum filter disable, lum noise clip disable */ + {0x00a1, 0x8656}, /* Window1 size 256x256, Windows2 size 64x64, + * gamma look-up disable, + * new edge enhancement enable */ + {0x0018, 0x8657}, /* Edge gain high thresh */ + {0x0020, 0x8658}, /* Edge gain low thresh */ + {0x000a, 0x8659}, /* Edge bandwidth high threshold */ + {0x0005, 0x865a}, /* Edge bandwidth low threshold */ + /* -------------------------------- */ + {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0xa908, 0x8802}, + {0x0034, 0x8801}, /* SSI reg addr */ + {0x00ca, 0x8800}, + /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x1f08, 0x8802}, + {0x0006, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + +/* ----- Read back coefs we wrote earlier. */ + /* READ { 0x0000, 0x8608 } -> 0000: 15 */ + /* READ { 0x0000, 0x8609 } -> 0000: 30 */ + /* READ { 0x0000, 0x860a } -> 0000: fb */ + /* READ { 0x0000, 0x860b } -> 0000: 3e */ + /* READ { 0x0000, 0x860c } -> 0000: ce */ + /* READ { 0x0000, 0x860d } -> 0000: f4 */ + /* READ { 0x0000, 0x860e } -> 0000: eb */ + /* READ { 0x0000, 0x860f } -> 0000: dc */ + /* READ { 0x0000, 0x8610 } -> 0000: 39 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0xb008, 0x8802}, + {0x0006, 0x8801}, + {0x007d, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + + /* This chunk is seemingly redundant with */ + /* earlier commands (A11 Coef...), but if I disable it, */ + /* the image appears too dark. Maybe there was some kind of */ + /* reset since the earlier commands, so this is necessary again. */ + {0x0015, 0x8608}, + {0x0030, 0x8609}, + {0xfffb, 0x860a}, + {0x003e, 0x860b}, + {0xffce, 0x860c}, + {0xfff4, 0x860d}, + {0xffeb, 0x860e}, + {0xffdc, 0x860f}, + {0x0039, 0x8610}, + {0x0018, 0x8657}, + + {0x0000, 0x8508}, /* Disable compression. */ + /* Previous line was: + {0x0021, 0x8508}, * Enable compression. */ + {0x0032, 0x850b}, /* compression stuff */ + {0x0003, 0x8509}, /* compression stuff */ + {0x0011, 0x850a}, /* compression stuff */ + {0x0021, 0x850d}, /* compression stuff */ + {0x0010, 0x850c}, /* compression stuff */ + {0x0003, 0x8500}, /* *** Video mode: 160x120 */ + {0x0001, 0x8501}, /* Hardware-dominated snap control */ + {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, + * new edge enhancement enable */ + {0x0018, 0x8617}, /* Window1 start X (*2) */ + {0x0008, 0x8618}, /* Window1 start Y (*2) */ + {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, + * new edge enhancement enable */ + {0x0058, 0x8619}, /* Window2 start X (*2) */ + {0x0008, 0x861a}, /* Window2 start Y (*2) */ + {0x00ff, 0x8615}, /* High lum thresh for white balance */ + {0x0000, 0x8616}, /* Low lum thresh for white balance */ + {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* READ { 0x0000, 0x8656 } -> 0000: 61 */ + {0x0028, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 28 */ + {0x1f28, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + {0x0010, 0x8801}, /* SSI reg addr */ + {0x003e, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + {0x0028, 0x8802}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 28 */ + {0x1f28, 0x8802}, + {0x0000, 0x8801}, + {0x001f, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + {0x0001, 0x8602}, /* optical black level for user settning = 1 */ + + /* Original: */ + {0x0023, 0x8700}, /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ + {0x000f, 0x8602}, /* optical black level for user settning = 15 */ + + {0x0028, 0x8802}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 28 */ + {0x1f28, 0x8802}, + {0x0010, 0x8801}, + {0x007b, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + {0x002f, 0x8651}, /* R gain for white balance ... */ + {0x0080, 0x8653}, + /* READ { 0x0000, 0x8655 } -> 0000: 00 */ + {0x0000, 0x8655}, + + {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */ + {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ + {} +}; + +/* + * Initialization data for Intel EasyPC Camera CS110 + */ +static const u16 spca508cs110_init_data[][2] = { + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8111}, /* Normal operation on reset */ + {0x0090, 0x8110}, + /* External Clock 2x & Synchronous Serial Interface Output */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + /* Initial sequence Synchronous Serial Interface */ + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ + + {0x0001, 0x8801}, + {0x000a, 0x8805}, /* a - NWG: Dunno what this is about */ + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x009a, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ + {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ + + {0x0006, 0x8660}, /* Nibble data + input order */ + + {0x000a, 0x8602}, /* Optical black level set to 0x0a */ + {0x0000, 0x8603}, /* Optical black level Offset */ + +/* {0x0000, 0x8611}, * 0 R Offset for white Balance */ +/* {0x0000, 0x8612}, * 1 Gr Offset for white Balance */ +/* {0x0000, 0x8613}, * 1f B Offset for white Balance */ +/* {0x0000, 0x8614}, * f0 Gb Offset for white Balance */ + + {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ + {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ + {0x0035, 0x8653}, /* 26 RED gain for white balance */ + {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ + {0x0041, 0x863f}, + /* Fixed Gamma correction enabled (makes colours look better) */ + + {0x0000, 0x8655}, + /* High bits for white balance*****brightness control*** */ + {} +}; + +static const u16 spca508_sightcam_init_data[][2] = { +/* This line seems to setup the frame/canvas */ + {0x000f, 0x8402}, + +/* These 6 lines are needed to startup the webcam */ + {0x0090, 0x8110}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + {0x0080, 0x8804}, + +/* This part seems to make the pictures darker? (autobrightness?) */ + {0x0001, 0x8801}, + {0x0004, 0x8800}, + {0x0003, 0x8801}, + {0x00e0, 0x8800}, + {0x0004, 0x8801}, + {0x00b4, 0x8800}, + {0x0005, 0x8801}, + {0x0000, 0x8800}, + + {0x0006, 0x8801}, + {0x00e0, 0x8800}, + {0x0007, 0x8801}, + {0x000c, 0x8800}, + +/* This section is just needed, it probably + * does something like the previous section, + * but the cam won't start if it's not included. + */ + {0x0014, 0x8801}, + {0x0008, 0x8800}, + {0x0015, 0x8801}, + {0x0067, 0x8800}, + {0x0016, 0x8801}, + {0x0000, 0x8800}, + {0x0017, 0x8801}, + {0x0020, 0x8800}, + {0x0018, 0x8801}, + {0x0044, 0x8800}, + +/* Makes the picture darker - and the + * cam won't start if not included + */ + {0x001e, 0x8801}, + {0x00ea, 0x8800}, + {0x001f, 0x8801}, + {0x0001, 0x8800}, + {0x0003, 0x8801}, + {0x00e0, 0x8800}, + +/* seems to place the colors ontop of each other #1 */ + {0x0006, 0x8704}, + {0x0001, 0x870c}, + {0x0016, 0x8600}, + {0x0002, 0x8606}, + +/* if not included the pictures becomes _very_ dark */ + {0x0064, 0x8607}, + {0x003a, 0x8601}, + {0x0000, 0x8602}, + +/* seems to place the colors ontop of each other #2 */ + {0x0016, 0x8600}, + {0x0018, 0x8617}, + {0x0008, 0x8618}, + {0x00a1, 0x8656}, + +/* webcam won't start if not included */ + {0x0007, 0x865b}, + {0x0001, 0x865c}, + {0x0058, 0x865d}, + {0x0048, 0x865e}, + +/* adjusts the colors */ + {0x0049, 0x8651}, + {0x0040, 0x8652}, + {0x004c, 0x8653}, + {0x0040, 0x8654}, + {} +}; + +static const u16 spca508_sightcam2_init_data[][2] = { + {0x0020, 0x8112}, + + {0x000f, 0x8402}, + {0x0000, 0x8403}, + + {0x0008, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0009, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000a, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000b, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000c, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000d, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000e, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0007, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000f, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + + {0x0018, 0x8660}, + {0x0010, 0x8201}, + + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0011, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + + {0x0000, 0x86b0}, + {0x0034, 0x86b1}, + {0x0000, 0x86b2}, + {0x0049, 0x86b3}, + {0x0000, 0x86b4}, + {0x0000, 0x86b4}, + + {0x0012, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0013, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + + {0x0001, 0x86b0}, + {0x00aa, 0x86b1}, + {0x0000, 0x86b2}, + {0x00e4, 0x86b3}, + {0x0000, 0x86b4}, + {0x0000, 0x86b4}, + + {0x0018, 0x8660}, + + {0x0090, 0x8110}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + {0x0080, 0x8804}, + {0x0003, 0x8801}, + {0x0012, 0x8800}, + {0x0004, 0x8801}, + {0x0005, 0x8800}, + {0x0005, 0x8801}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x0000, 0x8800}, + {0x0008, 0x8801}, + {0x0005, 0x8800}, + {0x000a, 0x8700}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x0005, 0x8801}, + {0x0047, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x00c0, 0x8800}, + {0x0008, 0x8801}, + {0x0003, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + {0x0009, 0x8801}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, + {0x0000, 0x8800}, + {0x000b, 0x8801}, + {0x0000, 0x8800}, + {0x000c, 0x8801}, + {0x0000, 0x8800}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x000f, 0x8801}, + {0x0000, 0x8800}, + {0x0010, 0x8801}, + {0x0006, 0x8800}, + {0x0011, 0x8801}, + {0x0006, 0x8800}, + {0x0012, 0x8801}, + {0x0000, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + + {0x000a, 0x8700}, + {0x0000, 0x8702}, + {0x0000, 0x8703}, + {0x00c2, 0x8704}, + {0x0001, 0x870c}, + + {0x0044, 0x8600}, + {0x0002, 0x8606}, + {0x0064, 0x8607}, + {0x003a, 0x8601}, + {0x0008, 0x8602}, + {0x0044, 0x8600}, + {0x0018, 0x8617}, + {0x0008, 0x8618}, + {0x00a1, 0x8656}, + {0x0004, 0x865b}, + {0x0002, 0x865c}, + {0x0058, 0x865d}, + {0x0048, 0x865e}, + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x002c, 0x860b}, + {0x00db, 0x860c}, + {0x00f9, 0x860d}, + {0x00f1, 0x860e}, + {0x00e3, 0x860f}, + {0x002c, 0x8610}, + {0x006c, 0x8651}, + {0x0041, 0x8652}, + {0x0059, 0x8653}, + {0x0040, 0x8654}, + {0x00fa, 0x8611}, + {0x00ff, 0x8612}, + {0x00f8, 0x8613}, + {0x0000, 0x8614}, + {0x0001, 0x863f}, + {0x0000, 0x8640}, + {0x0026, 0x8641}, + {0x0045, 0x8642}, + {0x0060, 0x8643}, + {0x0075, 0x8644}, + {0x0088, 0x8645}, + {0x009b, 0x8646}, + {0x00b0, 0x8647}, + {0x00c5, 0x8648}, + {0x00d2, 0x8649}, + {0x00dc, 0x864a}, + {0x00e5, 0x864b}, + {0x00eb, 0x864c}, + {0x00f0, 0x864d}, + {0x00f6, 0x864e}, + {0x00fa, 0x864f}, + {0x00ff, 0x8650}, + {0x0060, 0x8657}, + {0x0010, 0x8658}, + {0x0018, 0x8659}, + {0x0005, 0x865a}, + {0x0018, 0x8660}, + {0x0003, 0x8509}, + {0x0011, 0x850a}, + {0x0032, 0x850b}, + {0x0010, 0x850c}, + {0x0021, 0x850d}, + {0x0001, 0x8500}, + {0x0000, 0x8508}, + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x0039, 0x860b}, + {0x00d0, 0x860c}, + {0x00f7, 0x860d}, + {0x00ed, 0x860e}, + {0x00db, 0x860f}, + {0x0039, 0x8610}, + {0x0012, 0x8657}, + {0x000c, 0x8619}, + {0x0004, 0x861a}, + {0x00a1, 0x8656}, + {0x00c8, 0x8615}, + {0x0032, 0x8616}, + + {0x0030, 0x8112}, + {0x0020, 0x8112}, + {0x0020, 0x8112}, + {0x000f, 0x8402}, + {0x0000, 0x8403}, + + {0x0090, 0x8110}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + {0x0080, 0x8804}, + + {0x0003, 0x8801}, + {0x0012, 0x8800}, + {0x0004, 0x8801}, + {0x0005, 0x8800}, + {0x0005, 0x8801}, + {0x0047, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x00c0, 0x8800}, + {0x0008, 0x8801}, + {0x0003, 0x8800}, + {0x000a, 0x8700}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x0005, 0x8801}, + {0x0047, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x00c0, 0x8800}, + {0x0008, 0x8801}, + {0x0003, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + {0x0009, 0x8801}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, + {0x0000, 0x8800}, + {0x000b, 0x8801}, + {0x0000, 0x8800}, + {0x000c, 0x8801}, + {0x0000, 0x8800}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x000f, 0x8801}, + {0x0000, 0x8800}, + {0x0010, 0x8801}, + {0x0006, 0x8800}, + {0x0011, 0x8801}, + {0x0006, 0x8800}, + {0x0012, 0x8801}, + {0x0000, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + {0x000a, 0x8700}, + {0x0000, 0x8702}, + {0x0000, 0x8703}, + {0x00c2, 0x8704}, + {0x0001, 0x870c}, + {0x0044, 0x8600}, + {0x0002, 0x8606}, + {0x0064, 0x8607}, + {0x003a, 0x8601}, + {0x0008, 0x8602}, + {0x0044, 0x8600}, + {0x0018, 0x8617}, + {0x0008, 0x8618}, + {0x00a1, 0x8656}, + {0x0004, 0x865b}, + {0x0002, 0x865c}, + {0x0058, 0x865d}, + {0x0048, 0x865e}, + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x002c, 0x860b}, + {0x00db, 0x860c}, + {0x00f9, 0x860d}, + {0x00f1, 0x860e}, + {0x00e3, 0x860f}, + {0x002c, 0x8610}, + {0x006c, 0x8651}, + {0x0041, 0x8652}, + {0x0059, 0x8653}, + {0x0040, 0x8654}, + {0x00fa, 0x8611}, + {0x00ff, 0x8612}, + {0x00f8, 0x8613}, + {0x0000, 0x8614}, + {0x0001, 0x863f}, + {0x0000, 0x8640}, + {0x0026, 0x8641}, + {0x0045, 0x8642}, + {0x0060, 0x8643}, + {0x0075, 0x8644}, + {0x0088, 0x8645}, + {0x009b, 0x8646}, + {0x00b0, 0x8647}, + {0x00c5, 0x8648}, + {0x00d2, 0x8649}, + {0x00dc, 0x864a}, + {0x00e5, 0x864b}, + {0x00eb, 0x864c}, + {0x00f0, 0x864d}, + {0x00f6, 0x864e}, + {0x00fa, 0x864f}, + {0x00ff, 0x8650}, + {0x0060, 0x8657}, + {0x0010, 0x8658}, + {0x0018, 0x8659}, + {0x0005, 0x865a}, + {0x0018, 0x8660}, + {0x0003, 0x8509}, + {0x0011, 0x850a}, + {0x0032, 0x850b}, + {0x0010, 0x850c}, + {0x0021, 0x850d}, + {0x0001, 0x8500}, + {0x0000, 0x8508}, + + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x0039, 0x860b}, + {0x00d0, 0x860c}, + {0x00f7, 0x860d}, + {0x00ed, 0x860e}, + {0x00db, 0x860f}, + {0x0039, 0x8610}, + {0x0012, 0x8657}, + {0x0064, 0x8619}, + +/* This line starts it all, it is not needed here */ +/* since it has been build into the driver */ +/* jfm: don't start now */ +/* {0x0030, 0x8112}, */ + {} +}; + +/* + * Initialization data for Creative Webcam Vista + */ +static const u16 spca508_vista_init_data[][2] = { + {0x0008, 0x8200}, /* Clear register */ + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8110}, /* Disable everything */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8114}, + + {0x0003, 0x8111}, + {0x0000, 0x8111}, + {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ + {0x0020, 0x8112}, + {0x0000, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */ + {0x0020, 0x8801}, /* Register address for SSI read/write */ + {0x0044, 0x8805}, /* DATA2 */ + {0x0004, 0x8800}, /* DATA1 -> write triggered */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x003c, 0x8801}, + {0x0001, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0001, 0x8801}, + {0x000a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x000e, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0028, 0x8801}, + {0x002e, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0039, 0x8801}, + {0x0013, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x003b, 0x8801}, + {0x000c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0035, 0x8801}, + {0x0028, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + + {0x0050, 0x8703}, + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x870c}, /* Select CKOx2 output */ + {0x009a, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x0023, 0x8601}, + {0x0010, 0x8602}, + {0x000a, 0x8603}, + {0x009a, 0x8600}, + {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865c}, /* Vertical offset for valid lines (L) */ + {0x0058, 0x865d}, /* Horizontal valid pixels window (L) */ + {0x0048, 0x865e}, /* Vertical valid lines window (L) */ + {0x0000, 0x865f}, + + {0x0006, 0x8660}, + /* Enable nibble data input, select nibble input order */ + + {0x0013, 0x8608}, /* A11 Coeficients for color correction */ + {0x0028, 0x8609}, + /* Note: these values are confirmed at the end of array */ + {0x0005, 0x860a}, /* ... */ + {0x0025, 0x860b}, + {0x00e1, 0x860c}, + {0x00fa, 0x860d}, + {0x00f4, 0x860e}, + {0x00e8, 0x860f}, + {0x0025, 0x8610}, /* A33 Coef. */ + {0x00fc, 0x8611}, /* White balance offset: R */ + {0x0001, 0x8612}, /* White balance offset: Gr */ + {0x00fe, 0x8613}, /* White balance offset: B */ + {0x0000, 0x8614}, /* White balance offset: Gb */ + + {0x0064, 0x8651}, /* R gain for white balance (L) */ + {0x0040, 0x8652}, /* Gr gain for white balance (L) */ + {0x0066, 0x8653}, /* B gain for white balance (L) */ + {0x0040, 0x8654}, /* Gb gain for white balance (L) */ + {0x0001, 0x863f}, /* Enable fixed gamma correction */ + + {0x00a1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128, + * UV division: UV no change, + * Enable New edge enhancement */ + {0x0018, 0x8657}, /* Edge gain high threshold */ + {0x0020, 0x8658}, /* Edge gain low threshold */ + {0x000a, 0x8659}, /* Edge bandwidth high threshold */ + {0x0005, 0x865a}, /* Edge bandwidth low threshold */ + {0x0064, 0x8607}, /* UV filter enable */ + + {0x0016, 0x8660}, + {0x0000, 0x86b0}, /* Bad pixels compensation address */ + {0x00dc, 0x86b1}, /* X coord for bad pixels compensation (L) */ + {0x0000, 0x86b2}, + {0x0009, 0x86b3}, /* Y coord for bad pixels compensation (L) */ + {0x0000, 0x86b4}, + + {0x0001, 0x86b0}, + {0x00f5, 0x86b1}, + {0x0000, 0x86b2}, + {0x00c6, 0x86b3}, + {0x0000, 0x86b4}, + + {0x0002, 0x86b0}, + {0x001c, 0x86b1}, + {0x0001, 0x86b2}, + {0x00d7, 0x86b3}, + {0x0000, 0x86b4}, + + {0x0003, 0x86b0}, + {0x001c, 0x86b1}, + {0x0001, 0x86b2}, + {0x00d8, 0x86b3}, + {0x0000, 0x86b4}, + + {0x0004, 0x86b0}, + {0x001d, 0x86b1}, + {0x0001, 0x86b2}, + {0x00d8, 0x86b3}, + {0x0000, 0x86b4}, + {0x001e, 0x8660}, + + /* READ { 0x0000, 0x8608 } -> 0000: 13 */ + /* READ { 0x0000, 0x8609 } -> 0000: 28 */ + /* READ { 0x0000, 0x8610 } -> 0000: 05 */ + /* READ { 0x0000, 0x8611 } -> 0000: 25 */ + /* READ { 0x0000, 0x8612 } -> 0000: e1 */ + /* READ { 0x0000, 0x8613 } -> 0000: fa */ + /* READ { 0x0000, 0x8614 } -> 0000: f4 */ + /* READ { 0x0000, 0x8615 } -> 0000: e8 */ + /* READ { 0x0000, 0x8616 } -> 0000: 25 */ + {} +}; + +static int reg_write(struct usb_device *dev, + u16 index, u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x", + index, value); + if (ret < 0) + pr_err("reg write: error %d\n", ret); + return ret; +} + +/* read 1 byte */ +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct gspca_dev *gspca_dev, + u16 index) /* wIndex */ +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* register */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, 1, + 500); /* timeout */ + PDEBUG(D_USBI, "reg read i:%04x --> %02x", + index, gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("reg_read err %d\n", ret); + return ret; + } + return gspca_dev->usb_buf[0]; +} + +/* send 1 or 2 bytes to the sensor via the Synchronous Serial Interface */ +static int ssi_w(struct gspca_dev *gspca_dev, + u16 reg, u16 val) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, retry; + + ret = reg_write(dev, 0x8802, reg >> 8); + if (ret < 0) + goto out; + ret = reg_write(dev, 0x8801, reg & 0x00ff); + if (ret < 0) + goto out; + if ((reg & 0xff00) == 0x1000) { /* if 2 bytes */ + ret = reg_write(dev, 0x8805, val & 0x00ff); + if (ret < 0) + goto out; + val >>= 8; + } + ret = reg_write(dev, 0x8800, val); + if (ret < 0) + goto out; + + /* poll until not busy */ + retry = 10; + for (;;) { + ret = reg_read(gspca_dev, 0x8803); + if (ret < 0) + break; + if (gspca_dev->usb_buf[0] == 0) + break; + if (--retry <= 0) { + PDEBUG(D_ERR, "ssi_w busy %02x", + gspca_dev->usb_buf[0]); + ret = -1; + break; + } + msleep(8); + } + +out: + return ret; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const u16 (*data)[2]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret = 0; + + while ((*data)[1] != 0) { + if ((*data)[1] & 0x8000) { + if ((*data)[1] == 0xdd00) /* delay */ + msleep((*data)[0]); + else + ret = reg_write(dev, (*data)[1], (*data)[0]); + } else { + ret = ssi_w(gspca_dev, (*data)[1], (*data)[0]); + } + if (ret < 0) + break; + data++; + } + return ret; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + const u16 (*init_data)[2]; + static const u16 (*(init_data_tb[]))[2] = { + spca508_vista_init_data, /* CreativeVista 0 */ + spca508_sightcam_init_data, /* HamaUSBSightcam 1 */ + spca508_sightcam2_init_data, /* HamaUSBSightcam2 2 */ + spca508cs110_init_data, /* IntelEasyPCCamera 3 */ + spca508cs110_init_data, /* MicroInnovationIC200 4 */ + spca508_init_data, /* ViewQuestVQ110 5 */ + }; + +#ifdef GSPCA_DEBUG + int data1, data2; + + /* Read from global register the USB product and vendor IDs, just to + * prove that we can communicate with the device. This works, which + * confirms at we are communicating properly and that the device + * is a 508. */ + data1 = reg_read(gspca_dev, 0x8104); + data2 = reg_read(gspca_dev, 0x8105); + PDEBUG(D_PROBE, "Webcam Vendor ID: 0x%02x%02x", data2, data1); + + data1 = reg_read(gspca_dev, 0x8106); + data2 = reg_read(gspca_dev, 0x8107); + PDEBUG(D_PROBE, "Webcam Product ID: 0x%02x%02x", data2, data1); + + data1 = reg_read(gspca_dev, 0x8621); + PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1); +#endif + + cam = &gspca_dev->cam; + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + + sd->subtype = id->driver_info; + + init_data = init_data_tb[sd->subtype]; + return write_vector(gspca_dev, init_data); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + int mode; + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + reg_write(gspca_dev->dev, 0x8500, mode); + switch (mode) { + case 0: + case 1: + reg_write(gspca_dev->dev, 0x8700, 0x28); /* clock */ + break; + default: +/* case 2: */ +/* case 3: */ + reg_write(gspca_dev->dev, 0x8700, 0x23); /* clock */ + break; + } + reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20); + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Video ISO disable, Video Drop Packet enable: */ + reg_write(gspca_dev->dev, 0x8112, 0x20); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + switch (data[0]) { + case 0: /* start of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + data += SPCA508_OFFSET_DATA; + len -= SPCA508_OFFSET_DATA; + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + break; + case 0xff: /* drop */ + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + break; + } +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +{ + /* MX seem contrast */ + reg_write(gspca_dev->dev, 0x8651, brightness); + reg_write(gspca_dev->dev, 0x8652, brightness); + reg_write(gspca_dev->dev, 0x8653, brightness); + reg_write(gspca_dev->dev, 0x8654, brightness); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0130, 0x0130), .driver_info = HamaUSBSightcam}, + {USB_DEVICE(0x041e, 0x4018), .driver_info = CreativeVista}, + {USB_DEVICE(0x0733, 0x0110), .driver_info = ViewQuestVQ110}, + {USB_DEVICE(0x0af9, 0x0010), .driver_info = HamaUSBSightcam}, + {USB_DEVICE(0x0af9, 0x0011), .driver_info = HamaUSBSightcam2}, + {USB_DEVICE(0x8086, 0x0110), .driver_info = IntelEasyPCCamera}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/spca561.c b/drivers/media/usb/gspca/spca561.c new file mode 100644 index 000000000000..cfe71dd6747d --- /dev/null +++ b/drivers/media/usb/gspca/spca561.c @@ -0,0 +1,936 @@ +/* + * Sunplus spca561 subdriver + * + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "spca561" + +#include +#include "gspca.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define EXPOSURE_MAX (2047 + 325) + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct { /* hue/contrast control cluster */ + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *hue; + }; + struct v4l2_ctrl *autogain; + +#define EXPO12A_DEF 3 + __u8 expo12a; /* expo/gain? for rev 12a */ + + __u8 chip_revision; +#define Rev012A 0 +#define Rev072A 1 + + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +static const struct v4l2_pix_format sif_012a_mode[] = { + {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 4 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +static const struct v4l2_pix_format sif_072a_mode[] = { + {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* + * Initialization data + * I'm not very sure how to split initialization from open data + * chunks. For now, we'll consider everything as initialization + */ +/* Frame packet header offsets for the spca561 */ +#define SPCA561_OFFSET_SNAP 1 +#define SPCA561_OFFSET_TYPE 2 +#define SPCA561_OFFSET_COMPRESS 3 +#define SPCA561_OFFSET_FRAMSEQ 4 +#define SPCA561_OFFSET_GPIO 5 +#define SPCA561_OFFSET_USBBUFF 6 +#define SPCA561_OFFSET_WIN2GRAVE 7 +#define SPCA561_OFFSET_WIN2RAVE 8 +#define SPCA561_OFFSET_WIN2BAVE 9 +#define SPCA561_OFFSET_WIN2GBAVE 10 +#define SPCA561_OFFSET_WIN1GRAVE 11 +#define SPCA561_OFFSET_WIN1RAVE 12 +#define SPCA561_OFFSET_WIN1BAVE 13 +#define SPCA561_OFFSET_WIN1GBAVE 14 +#define SPCA561_OFFSET_FREQ 15 +#define SPCA561_OFFSET_VSYNC 16 +#define SPCA561_INDEX_I2C_BASE 0x8800 +#define SPCA561_SNAPBIT 0x20 +#define SPCA561_SNAPCTRL 0x40 + +static const u16 rev72a_reset[][2] = { + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8112}, /* Some kind of reset */ + {} +}; +static const __u16 rev72a_init_data1[][2] = { + {0x0003, 0x8701}, /* PCLK clock delay adjustment */ + {0x0001, 0x8703}, /* HSYNC from cmos inverted */ + {0x0011, 0x8118}, /* Enable and conf sensor */ + {0x0001, 0x8118}, /* Conf sensor */ + {0x0092, 0x8804}, /* I know nothing about these */ + {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ + {} +}; +static const u16 rev72a_init_sensor1[][2] = { + {0x0001, 0x000d}, + {0x0002, 0x0018}, + {0x0004, 0x0165}, + {0x0005, 0x0021}, + {0x0007, 0x00aa}, + {0x0020, 0x1504}, + {0x0039, 0x0002}, + {0x0035, 0x0010}, + {0x0009, 0x1049}, + {0x0028, 0x000b}, + {0x003b, 0x000f}, + {0x003c, 0x0000}, + {} +}; +static const __u16 rev72a_init_data2[][2] = { + {0x0018, 0x8601}, /* Pixel/line selection for color separation */ + {0x0000, 0x8602}, /* Optical black level for user setting */ + {0x0060, 0x8604}, /* Optical black horizontal offset */ + {0x0002, 0x8605}, /* Optical black vertical offset */ + {0x0000, 0x8603}, /* Non-automatic optical black level */ + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */ + {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */ + {0x0090, 0x865e}, /* Vertical valid lines window (x2) */ + {0x00e0, 0x8406}, /* Memory buffer threshold */ + {0x0000, 0x8660}, /* Compensation memory stuff */ + {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ + {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ + {0x0001, 0x8200}, /* OprMode to be executed by hardware */ +/* from ms-win */ + {0x0000, 0x8611}, /* R offset for white balance */ + {0x00fd, 0x8612}, /* Gr offset for white balance */ + {0x0003, 0x8613}, /* B offset for white balance */ + {0x0000, 0x8614}, /* Gb offset for white balance */ +/* from ms-win */ + {0x0035, 0x8651}, /* R gain for white balance */ + {0x0040, 0x8652}, /* Gr gain for white balance */ + {0x005f, 0x8653}, /* B gain for white balance */ + {0x0040, 0x8654}, /* Gb gain for white balance */ + {0x0002, 0x8502}, /* Maximum average bit rate stuff */ + {0x0011, 0x8802}, + + {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ + {0x0081, 0x8702}, /* Master clock output enable */ + + {0x0000, 0x8500}, /* Set image type (352x288 no compression) */ + /* Originally was 0x0010 (352x288 compression) */ + + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0003, 0x865c}, /* Vertical offset for valid lines */ + {} +}; +static const u16 rev72a_init_sensor2[][2] = { + {0x0003, 0x0121}, + {0x0004, 0x0165}, + {0x0005, 0x002f}, /* blanking control column */ + {0x0006, 0x0000}, /* blanking mode row*/ + {0x000a, 0x0002}, + {0x0009, 0x1061}, /* setexposure times && pixel clock + * 0001 0 | 000 0110 0001 */ + {0x0035, 0x0014}, + {} +}; + +/******************** QC Express etch2 stuff ********************/ +static const __u16 Pb100_1map8300[][2] = { + /* reg, value */ + {0x8320, 0x3304}, + + {0x8303, 0x0125}, /* image area */ + {0x8304, 0x0169}, + {0x8328, 0x000b}, + {0x833c, 0x0001}, /*fixme: win:07*/ + + {0x832f, 0x1904}, /*fixme: was 0419*/ + {0x8307, 0x00aa}, + {0x8301, 0x0003}, + {0x8302, 0x000e}, + {} +}; +static const __u16 Pb100_2map8300[][2] = { + /* reg, value */ + {0x8339, 0x0000}, + {0x8307, 0x00aa}, + {} +}; + +static const __u16 spca561_161rev12A_data1[][2] = { + {0x29, 0x8118}, /* Control register (various enable bits) */ + {0x08, 0x8114}, /* GPIO: Led off */ + {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */ + {0x00, 0x8102}, /* white balance - new */ + {0x92, 0x8804}, + {0x04, 0x8802}, /* windows uses 08 */ + {} +}; +static const __u16 spca561_161rev12A_data2[][2] = { + {0x21, 0x8118}, + {0x10, 0x8500}, + {0x07, 0x8601}, + {0x07, 0x8602}, + {0x04, 0x8501}, + + {0x07, 0x8201}, /* windows uses 02 */ + {0x08, 0x8200}, + {0x01, 0x8200}, + + {0x90, 0x8604}, + {0x00, 0x8605}, + {0xb0, 0x8603}, + + /* sensor gains */ + {0x07, 0x8601}, /* white balance - new */ + {0x07, 0x8602}, /* white balance - new */ + {0x00, 0x8610}, /* *red */ + {0x00, 0x8611}, /* 3f *green */ + {0x00, 0x8612}, /* green *blue */ + {0x00, 0x8613}, /* blue *green */ + {0x43, 0x8614}, /* green *red - white balance - was 0x35 */ + {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */ + {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */ + {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */ + + {0x0c, 0x8620}, /* 0c */ + {0xc8, 0x8631}, /* c8 */ + {0xc8, 0x8634}, /* c8 */ + {0x23, 0x8635}, /* 23 */ + {0x1f, 0x8636}, /* 1f */ + {0xdd, 0x8637}, /* dd */ + {0xe1, 0x8638}, /* e1 */ + {0x1d, 0x8639}, /* 1d */ + {0x21, 0x863a}, /* 21 */ + {0xe3, 0x863b}, /* e3 */ + {0xdf, 0x863c}, /* df */ + {0xf0, 0x8505}, + {0x32, 0x850a}, +/* {0x99, 0x8700}, * - white balance - new (removed) */ + /* HDG we used to do this in stop0, making the init state and the state + after a start / stop different, so do this here instead. */ + {0x29, 0x8118}, + {} +}; + +static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) +{ + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); + if (ret < 0) + pr_err("reg write: error %d\n", ret); +} + +static void write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][2]) +{ + struct usb_device *dev = gspca_dev->dev; + int i; + + i = 0; + while (data[i][1] != 0) { + reg_w_val(dev, data[i][1], data[i][0]); + i++; + } +} + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, 500); +} + +/* write 'len' bytes from gspca_dev->usb_buf */ +static void reg_w_buf(struct gspca_dev *gspca_dev, + __u16 index, __u16 len) +{ + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, 500); +} + +static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) +{ + int retry = 60; + + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8805, value); + reg_w_val(gspca_dev->dev, 0x8800, value >> 8); + do { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf[0]) + return; + msleep(10); + } while (--retry); +} + +static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) +{ + int retry = 60; + __u8 value; + + reg_w_val(gspca_dev->dev, 0x8804, 0x92); + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01); + do { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf[0]) { + reg_r(gspca_dev, 0x8800, 1); + value = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8805, 1); + return ((int) value << 8) | gspca_dev->usb_buf[0]; + } + msleep(10); + } while (--retry); + return -1; +} + +static void sensor_mapwrite(struct gspca_dev *gspca_dev, + const __u16 (*sensormap)[2]) +{ + while ((*sensormap)[0]) { + gspca_dev->usb_buf[0] = (*sensormap)[1]; + gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8; + reg_w_buf(gspca_dev, (*sensormap)[0], 2); + sensormap++; + } +} + +static void write_sensor_72a(struct gspca_dev *gspca_dev, + const __u16 (*sensor)[2]) +{ + while ((*sensor)[0]) { + i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]); + sensor++; + } +} + +static void init_161rev12A(struct gspca_dev *gspca_dev) +{ + write_vector(gspca_dev, spca561_161rev12A_data1); + sensor_mapwrite(gspca_dev, Pb100_1map8300); +/*fixme: should be in sd_start*/ + write_vector(gspca_dev, spca561_161rev12A_data2); + sensor_mapwrite(gspca_dev, Pb100_2map8300); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor, product; + __u8 data1, data2; + + /* Read frm global register the USB product and vendor IDs, just to + * prove that we can communicate with the device. This works, which + * confirms at we are communicating properly and that the device + * is a 561. */ + reg_r(gspca_dev, 0x8104, 1); + data1 = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8105, 1); + data2 = gspca_dev->usb_buf[0]; + vendor = (data2 << 8) | data1; + reg_r(gspca_dev, 0x8106, 1); + data1 = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8107, 1); + data2 = gspca_dev->usb_buf[0]; + product = (data2 << 8) | data1; + if (vendor != id->idVendor || product != id->idProduct) { + PDEBUG(D_PROBE, "Bad vendor / product from device"); + return -EINVAL; + } + + cam = &gspca_dev->cam; + cam->needs_full_bandwidth = 1; + + sd->chip_revision = id->driver_info; + if (sd->chip_revision == Rev012A) { + cam->cam_mode = sif_012a_mode; + cam->nmodes = ARRAY_SIZE(sif_012a_mode); + } else { + cam->cam_mode = sif_072a_mode; + cam->nmodes = ARRAY_SIZE(sif_072a_mode); + } + sd->expo12a = EXPO12A_DEF; + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init_12a(struct gspca_dev *gspca_dev) +{ + PDEBUG(D_STREAM, "Chip revision: 012a"); + init_161rev12A(gspca_dev); + return 0; +} +static int sd_init_72a(struct gspca_dev *gspca_dev) +{ + PDEBUG(D_STREAM, "Chip revision: 072a"); + write_vector(gspca_dev, rev72a_reset); + msleep(200); + write_vector(gspca_dev, rev72a_init_data1); + write_sensor_72a(gspca_dev, rev72a_init_sensor1); + write_vector(gspca_dev, rev72a_init_data2); + write_sensor_72a(gspca_dev, rev72a_init_sensor2); + reg_w_val(gspca_dev->dev, 0x8112, 0x30); + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u16 reg; + + if (sd->chip_revision == Rev012A) + reg = 0x8610; + else + reg = 0x8611; + + reg_w_val(dev, reg + 0, val); /* R */ + reg_w_val(dev, reg + 1, val); /* Gr */ + reg_w_val(dev, reg + 2, val); /* B */ + reg_w_val(dev, reg + 3, val); /* Gb */ +} + +static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 blue, red; + __u16 reg; + + /* try to emulate MS-win as possible */ + red = 0x20 + white * 3 / 8; + blue = 0x90 - white * 5 / 8; + if (sd->chip_revision == Rev012A) { + reg = 0x8614; + } else { + reg = 0x8651; + red += contrast - 0x20; + blue += contrast - 0x20; + reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */ + reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */ + } + reg_w_val(dev, reg, red); + reg_w_val(dev, reg + 2, blue); +} + +/* rev 12a only */ +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + int i, expo = 0; + + /* Register 0x8309 controls exposure for the spca561, + the basic exposure setting goes from 1-2047, where 1 is completely + dark and 2047 is very bright. It not only influences exposure but + also the framerate (to allow for longer exposure) from 1 - 300 it + only raises the exposure time then from 300 - 600 it halves the + framerate to be able to further raise the exposure time and for every + 300 more it halves the framerate again. This allows for a maximum + exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps). + Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12 + configure a divider for the base framerate which us used at the + exposure setting of 1-300. These bits configure the base framerate + according to the following formula: fps = 60 / (value + 2) */ + + /* We choose to use the high bits setting the fixed framerate divisor + asap, as setting high basic exposure setting without the fixed + divider in combination with high gains makes the cam stop */ + int table[] = { 0, 450, 550, 625, EXPOSURE_MAX }; + + for (i = 0; i < ARRAY_SIZE(table) - 1; i++) { + if (val <= table[i + 1]) { + expo = val - table[i]; + if (i) + expo += 300; + expo |= i << 11; + break; + } + } + + gspca_dev->usb_buf[0] = expo; + gspca_dev->usb_buf[1] = expo >> 8; + reg_w_buf(gspca_dev, 0x8309, 2); +} + +/* rev 12a only */ +static void setgain(struct gspca_dev *gspca_dev, s32 val) +{ + /* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the + sensitivity when set, so 31 + one of them set == 63, and 15 + with both of them set == 63 */ + if (val < 64) + gspca_dev->usb_buf[0] = val; + else if (val < 128) + gspca_dev->usb_buf[0] = (val / 2) | 0x40; + else + gspca_dev->usb_buf[0] = (val / 4) | 0xc0; + + gspca_dev->usb_buf[1] = 0; + reg_w_buf(gspca_dev, 0x8335, 2); +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; +} + +static int sd_start_12a(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int mode; + static const __u8 Reg8391[8] = + {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00}; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + if (mode <= 1) { + /* Use compression on 320x240 and above */ + reg_w_val(dev, 0x8500, 0x10 | mode); + } else { + /* I couldn't get the compression to work below 320x240 + * Fortunately at these resolutions the bandwidth + * is sufficient to push raw frames at ~20fps */ + reg_w_val(dev, 0x8500, mode); + } /* -- qq@kuku.eu.org */ + + gspca_dev->usb_buf[0] = 0xaa; + gspca_dev->usb_buf[1] = 0x00; + reg_w_buf(gspca_dev, 0x8307, 2); + /* clock - lower 0x8X values lead to fps > 30 */ + reg_w_val(gspca_dev->dev, 0x8700, 0x8a); + /* 0x8f 0x85 0x27 clock */ + reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); + reg_w_val(gspca_dev->dev, 0x850b, 0x03); + memcpy(gspca_dev->usb_buf, Reg8391, 8); + reg_w_buf(gspca_dev, 0x8391, 8); + reg_w_buf(gspca_dev, 0x8390, 8); + + /* Led ON (bit 3 -> 0 */ + reg_w_val(gspca_dev->dev, 0x8114, 0x00); + return 0; +} +static int sd_start_72a(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int Clck; + int mode; + + write_vector(gspca_dev, rev72a_reset); + msleep(200); + write_vector(gspca_dev, rev72a_init_data1); + write_sensor_72a(gspca_dev, rev72a_init_sensor1); + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + switch (mode) { + default: + case 0: + Clck = 0x27; /* ms-win 0x87 */ + break; + case 1: + Clck = 0x25; + break; + case 2: + Clck = 0x22; + break; + case 3: + Clck = 0x21; + break; + } + reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ + reg_w_val(dev, 0x8702, 0x81); + reg_w_val(dev, 0x8500, mode); /* mode */ + write_sensor_72a(gspca_dev, rev72a_init_sensor2); + setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue), + v4l2_ctrl_g_ctrl(sd->contrast)); +/* setbrightness(gspca_dev); * fixme: bad values */ + setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); + reg_w_val(dev, 0x8112, 0x10 | 0x20); + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->chip_revision == Rev012A) { + reg_w_val(gspca_dev->dev, 0x8112, 0x0e); + /* Led Off (bit 3 -> 1 */ + reg_w_val(gspca_dev->dev, 0x8114, 0x08); + } else { + reg_w_val(gspca_dev->dev, 0x8112, 0x20); +/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ + } +} + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int expotimes; + int pixelclk; + int gainG; + __u8 R, Gr, Gb, B; + int y; + __u8 luma_mean = 110; + __u8 luma_delta = 20; + __u8 spring = 4; + + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; + + switch (sd->chip_revision) { + case Rev072A: + reg_r(gspca_dev, 0x8621, 1); + Gr = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8622, 1); + R = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8623, 1); + B = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8624, 1); + Gb = gspca_dev->usb_buf[0]; + y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; + /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */ + /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */ + /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */ + + if (y < luma_mean - luma_delta || + y > luma_mean + luma_delta) { + expotimes = i2c_read(gspca_dev, 0x09, 0x10); + pixelclk = 0x0800; + expotimes = expotimes & 0x07ff; + /* PDEBUG(D_PACK, + "Exposition Times 0x%03X Clock 0x%04X ", + expotimes,pixelclk); */ + gainG = i2c_read(gspca_dev, 0x35, 0x10); + /* PDEBUG(D_PACK, + "reading Gain register %d", gainG); */ + + expotimes += (luma_mean - y) >> spring; + gainG += (luma_mean - y) / 50; + /* PDEBUG(D_PACK, + "compute expotimes %d gain %d", + expotimes,gainG); */ + + if (gainG > 0x3f) + gainG = 0x3f; + else if (gainG < 3) + gainG = 3; + i2c_write(gspca_dev, gainG, 0x35); + + if (expotimes > 0x0256) + expotimes = 0x0256; + else if (expotimes < 3) + expotimes = 3; + i2c_write(gspca_dev, expotimes | pixelclk, 0x09); + } + break; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + len--; + switch (*data++) { /* sequence number */ + case 0: /* start of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* This should never happen */ + if (len < 2) { + PDEBUG(D_ERR, "Short SOF packet, ignoring"); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + if (data[0] & 0x20) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + } +#endif + + if (data[1] & 0x10) { + /* compressed bayer */ + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + } else { + /* raw bayer (with a header, which we skip) */ + if (sd->chip_revision == Rev012A) { + data += 20; + len -= 20; + } else { + data += 16; + len -= 16; + } + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + } + return; + case 0xff: /* drop (empty mpackets) */ + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + /* hue/contrast control cluster for 72a */ + setwhite(gspca_dev, sd->hue->val, ctrl->val); + break; + case V4L2_CID_HUE: + /* just plain hue control for 12a */ + setwhite(gspca_dev, ctrl->val, 0); + break; + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + setgain(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls_12a(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 3); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, 1, 0x7f, 1, 0x40); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 63); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +static int sd_init_controls_72a(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20); + sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, 1, 0x7f, 1, 0x40); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20); + sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(2, &sd->contrast); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc_12a = { + .name = MODULE_NAME, + .init_controls = sd_init_controls_12a, + .config = sd_config, + .init = sd_init_12a, + .start = sd_start_12a, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .other_input = 1, +#endif +}; +static const struct sd_desc sd_desc_72a = { + .name = MODULE_NAME, + .init_controls = sd_init_controls_72a, + .config = sd_config, + .init = sd_init_72a, + .start = sd_start_72a, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .other_input = 1, +#endif +}; +static const struct sd_desc *sd_desc[2] = { + &sd_desc_12a, + &sd_desc_72a +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A}, + {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A}, + {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A}, + {USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A}, + {USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A}, + {USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A}, + {USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A}, + {USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A}, + {USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + sd_desc[id->driver_info], + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c new file mode 100644 index 000000000000..a8ac97931ad6 --- /dev/null +++ b/drivers/media/usb/gspca/sq905.c @@ -0,0 +1,440 @@ +/* + * SQ905 subdriver + * + * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * History and Acknowledgments + * + * The original Linux driver for SQ905 based cameras was written by + * Marcell Lengyel and furter developed by many other contributors + * and is available from http://sourceforge.net/projects/sqcam/ + * + * This driver takes advantage of the reverse engineering work done for + * that driver and for libgphoto2 but shares no code with them. + * + * This driver has used as a base the finepix driver and other gspca + * based drivers and may still contain code fragments taken from those + * drivers. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "sq905" + +#include +#include +#include "gspca.h" + +MODULE_AUTHOR("Adam Baker , " + "Theodore Kilgore "); +MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define SQ905_CMD_TIMEOUT 500 +#define SQ905_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define SQ905_MAX_TRANSFER 0x8000 +#define FRAME_HEADER_LEN 64 + +/* The known modes, or registers. These go in the "value" slot. */ + +/* 00 is "none" obviously */ + +#define SQ905_BULK_READ 0x03 /* precedes any bulk read */ +#define SQ905_COMMAND 0x06 /* precedes the command codes below */ +#define SQ905_PING 0x07 /* when reading an "idling" command */ +#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */ + +/* Any non-zero value in the bottom 2 bits of the 2nd byte of + * the ID appears to indicate the camera can do 640*480. If the + * LSB of that byte is set the image is just upside down, otherwise + * it is rotated 180 degrees. */ +#define SQ905_HIRES_MASK 0x00000300 +#define SQ905_ORIENTATION_MASK 0x00000100 + +/* Some command codes. These go in the "index" slot. */ + +#define SQ905_ID 0xf0 /* asks for model string */ +#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */ +#define SQ905_DATA 0x30 /* accesses photo data, not used here */ +#define SQ905_CLEAR 0xa0 /* clear everything */ +#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */ +#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */ +#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */ +/* note that the capture command also controls the output dimensions */ + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + /* + * Driver stuff + */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; +}; + +static struct v4l2_pix_format sq905_mode[] = { + { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0} +}; + +/* + * Send a command to the camera. + */ +static int sq905_command(struct gspca_dev *gspca_dev, u16 index) +{ + int ret; + + gspca_dev->usb_buf[0] = '\0'; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_COMMAND, index, gspca_dev->usb_buf, 1, + SQ905_CMD_TIMEOUT); + if (ret < 0) { + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); + return ret; + } + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_PING, 0, gspca_dev->usb_buf, 1, + SQ905_CMD_TIMEOUT); + if (ret < 0) { + pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +/* + * Acknowledge the end of a frame - see warning on sq905_command. + */ +static int sq905_ack_frame(struct gspca_dev *gspca_dev) +{ + int ret; + + gspca_dev->usb_buf[0] = '\0'; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1, + SQ905_CMD_TIMEOUT); + if (ret < 0) { + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +/* + * request and read a block of data - see warning on sq905_command. + */ +static int +sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) +{ + int ret; + int act_len; + + gspca_dev->usb_buf[0] = '\0'; + if (need_lock) + mutex_lock(&gspca_dev->usb_lock); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_BULK_READ, size, gspca_dev->usb_buf, + 1, SQ905_CMD_TIMEOUT); + if (need_lock) + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) { + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); + return ret; + } + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + data, size, &act_len, SQ905_DATA_TIMEOUT); + + /* successful, it returns 0, otherwise negative */ + if (ret < 0 || act_len != size) { + pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size); + return -EIO; + } + return 0; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface we take the gspca + * usb_lock when performing USB operations. In practice the only thing we need + * to protect against is the usb_set_interface call that gspca makes during + * stream_off as the camera doesn't provide any controls that the user could try + * to change. + */ +static void sq905_dostream(struct work_struct *work) +{ + struct sd *dev = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + int bytes_left; /* bytes remaining in current frame. */ + int data_len; /* size to use for the next read. */ + int header_read; /* true if we have already read the frame header. */ + int packet_type; + int frame_sz; + int ret; + u8 *data; + u8 *buffer; + + buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + if (!buffer) { + pr_err("Couldn't allocate USB buffer\n"); + goto quit_stream; + } + + frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + + FRAME_HEADER_LEN; + + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + /* request some data and then read it until we have + * a complete frame. */ + bytes_left = frame_sz; + header_read = 0; + + /* Note we do not check for gspca_dev->streaming here, as + we must finish reading an entire frame, otherwise the + next time we stream we start reading in the middle of a + frame. */ + while (bytes_left > 0 && gspca_dev->dev) { + data_len = bytes_left > SQ905_MAX_TRANSFER ? + SQ905_MAX_TRANSFER : bytes_left; + ret = sq905_read_data(gspca_dev, buffer, data_len, 1); + if (ret < 0) + goto quit_stream; + PDEBUG(D_PACK, + "Got %d bytes out of %d for frame", + data_len, bytes_left); + bytes_left -= data_len; + data = buffer; + if (!header_read) { + packet_type = FIRST_PACKET; + /* The first 64 bytes of each frame are + * a header full of FF 00 bytes */ + data += FRAME_HEADER_LEN; + data_len -= FRAME_HEADER_LEN; + header_read = 1; + } else if (bytes_left == 0) { + packet_type = LAST_PACKET; + } else { + packet_type = INTER_PACKET; + } + gspca_frame_add(gspca_dev, packet_type, + data, data_len); + /* If entire frame fits in one packet we still + need to add a LAST_PACKET */ + if (packet_type == FIRST_PACKET && + bytes_left == 0) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + } + if (gspca_dev->dev) { + /* acknowledge the frame */ + mutex_lock(&gspca_dev->usb_lock); + ret = sq905_ack_frame(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + goto quit_stream; + } + } +quit_stream: + if (gspca_dev->dev) { + mutex_lock(&gspca_dev->usb_lock); + sq905_command(gspca_dev, SQ905_CLEAR); + mutex_unlock(&gspca_dev->usb_lock); + } + kfree(buffer); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *dev = (struct sd *) gspca_dev; + + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk = 1; + cam->bulk_size = 64; + + INIT_WORK(&dev->work_struct, sq905_dostream); + + return 0; +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for sq905_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + u32 ident; + int ret; + + /* connect to the camera and read + * the model ID and process that and put it away. + */ + ret = sq905_command(gspca_dev, SQ905_CLEAR); + if (ret < 0) + return ret; + ret = sq905_command(gspca_dev, SQ905_ID); + if (ret < 0) + return ret; + ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0); + if (ret < 0) + return ret; + /* usb_buf is allocated with kmalloc so is aligned. + * Camera model number is the right way round if we assume this + * reverse engineered ID is supposed to be big endian. */ + ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf); + ret = sq905_command(gspca_dev, SQ905_CLEAR); + if (ret < 0) + return ret; + PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident); + gspca_dev->cam.cam_mode = sq905_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode); + if (!(ident & SQ905_HIRES_MASK)) + gspca_dev->cam.nmodes--; + + if (ident & SQ905_ORIENTATION_MASK) + gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP; + else + gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP | + V4L2_IN_ST_HFLIP; + return 0; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + int ret; + + /* "Open the shutter" and set size, to start capture */ + switch (gspca_dev->curr_mode) { + default: +/* case 2: */ + PDEBUG(D_STREAM, "Start streaming at high resolution"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH); + break; + case 1: + PDEBUG(D_STREAM, "Start streaming at medium resolution"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED); + break; + case 0: + PDEBUG(D_STREAM, "Start streaming at low resolution"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW); + } + + if (ret < 0) { + PDEBUG(D_ERR, "Start streaming command failed"); + return ret; + } + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(dev->work_thread, &dev->work_struct); + + return 0; +} + +/* Table of supported USB devices */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x2770, 0x9120)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c new file mode 100644 index 000000000000..70fae6982e96 --- /dev/null +++ b/drivers/media/usb/gspca/sq905c.c @@ -0,0 +1,344 @@ +/* + * SQ905C subdriver + * + * Copyright (C) 2009 Theodore Kilgore + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * + * This driver uses work done in + * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore. + * + * This driver has also used as a base the sq905c driver + * and may contain code fragments from it. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "sq905c" + +#include +#include +#include "gspca.h" + +MODULE_AUTHOR("Theodore Kilgore "); +MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define SQ905C_CMD_TIMEOUT 500 +#define SQ905C_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define SQ905C_MAX_TRANSFER 0x8000 + +#define FRAME_HEADER_LEN 0x50 + +/* Commands. These go in the "value" slot. */ +#define SQ905C_CLEAR 0xa0 /* clear everything */ +#define SQ905C_GET_ID 0x14f4 /* Read version number */ +#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */ +#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */ +#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */ + +/* For capture, this must go in the "index" slot. */ +#define SQ905C_CAPTURE_INDEX 0x110f + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + const struct v4l2_pix_format *cap_mode; + /* Driver stuff */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; +}; + +/* + * Most of these cameras will do 640x480 and 320x240. 160x120 works + * in theory but gives very poor output. Therefore, not supported. + * The 0x2770:0x9050 cameras have max resolution of 320x240. + */ +static struct v4l2_pix_format sq905c_mode[] = { + { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0} +}; + +/* Send a command to the camera. */ +static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index) +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + command, index, NULL, 0, + SQ905C_CMD_TIMEOUT); + if (ret < 0) { + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index, + int size) +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + command, index, gspca_dev->usb_buf, size, + SQ905C_CMD_TIMEOUT); + if (ret < 0) { + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface the gspca usb_lock is + * used when performing the one USB control operation inside the workqueue, + * which tells the camera to close the stream. In practice the only thing + * which needs to be protected against is the usb_set_interface call that + * gspca makes during stream_off. Otherwise the camera doesn't provide any + * controls that the user could try to change. + */ +static void sq905c_dostream(struct work_struct *work) +{ + struct sd *dev = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + int bytes_left; /* bytes remaining in current frame. */ + int data_len; /* size to use for the next read. */ + int act_len; + int packet_type; + int ret; + u8 *buffer; + + buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + if (!buffer) { + pr_err("Couldn't allocate USB buffer\n"); + goto quit_stream; + } + + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + /* Request the header, which tells the size to download */ + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + buffer, FRAME_HEADER_LEN, &act_len, + SQ905C_DATA_TIMEOUT); + PDEBUG(D_STREAM, + "Got %d bytes out of %d for header", + act_len, FRAME_HEADER_LEN); + if (ret < 0 || act_len < FRAME_HEADER_LEN) + goto quit_stream; + /* size is read from 4 bytes starting 0x40, little endian */ + bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16) + |(buffer[0x43]<<24); + PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left); + /* We keep the header. It has other information, too. */ + packet_type = FIRST_PACKET; + gspca_frame_add(gspca_dev, packet_type, + buffer, FRAME_HEADER_LEN); + while (bytes_left > 0 && gspca_dev->dev) { + data_len = bytes_left > SQ905C_MAX_TRANSFER ? + SQ905C_MAX_TRANSFER : bytes_left; + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + buffer, data_len, &act_len, + SQ905C_DATA_TIMEOUT); + if (ret < 0 || act_len < data_len) + goto quit_stream; + PDEBUG(D_STREAM, + "Got %d bytes out of %d for frame", + data_len, bytes_left); + bytes_left -= data_len; + if (bytes_left == 0) + packet_type = LAST_PACKET; + else + packet_type = INTER_PACKET; + gspca_frame_add(gspca_dev, packet_type, + buffer, data_len); + } + } +quit_stream: + if (gspca_dev->dev) { + mutex_lock(&gspca_dev->usb_lock); + sq905c_command(gspca_dev, SQ905C_CLEAR, 0); + mutex_unlock(&gspca_dev->usb_lock); + } + kfree(buffer); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *dev = (struct sd *) gspca_dev; + int ret; + + PDEBUG(D_PROBE, + "SQ9050 camera detected" + " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + + ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0); + if (ret < 0) { + PDEBUG(D_ERR, "Get version command failed"); + return ret; + } + + ret = sq905c_read(gspca_dev, 0xf5, 0, 20); + if (ret < 0) { + PDEBUG(D_ERR, "Reading version command failed"); + return ret; + } + /* Note we leave out the usb id and the manufacturing date */ + PDEBUG(D_PROBE, + "SQ9050 ID string: %02x - %*ph", + gspca_dev->usb_buf[3], 6, gspca_dev->usb_buf + 14); + + cam->cam_mode = sq905c_mode; + cam->nmodes = 2; + if (gspca_dev->usb_buf[15] == 0) + cam->nmodes = 1; + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk_size = 32; + cam->bulk = 1; + INIT_WORK(&dev->work_struct, sq905c_dostream); + return 0; +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for sq905c_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int ret; + + /* connect to the camera and reset it. */ + ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0); + return ret; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + int ret; + + dev->cap_mode = gspca_dev->cam.cam_mode; + /* "Open the shutter" and set size, to start capture */ + switch (gspca_dev->width) { + case 640: + PDEBUG(D_STREAM, "Start streaming at high resolution"); + dev->cap_mode++; + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI, + SQ905C_CAPTURE_INDEX); + break; + default: /* 320 */ + PDEBUG(D_STREAM, "Start streaming at medium resolution"); + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED, + SQ905C_CAPTURE_INDEX); + } + + if (ret < 0) { + PDEBUG(D_ERR, "Start streaming command failed"); + return ret; + } + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(dev->work_thread, &dev->work_struct); + + return 0; +} + +/* Table of supported USB devices */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x2770, 0x905c)}, + {USB_DEVICE(0x2770, 0x9050)}, + {USB_DEVICE(0x2770, 0x9051)}, + {USB_DEVICE(0x2770, 0x9052)}, + {USB_DEVICE(0x2770, 0x913d)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/sq930x.c b/drivers/media/usb/gspca/sq930x.c new file mode 100644 index 000000000000..7e8748b31e85 --- /dev/null +++ b/drivers/media/usb/gspca/sq930x.c @@ -0,0 +1,1164 @@ +/* + * SQ930x subdriver + * + * Copyright (C) 2010 Jean-François Moine + * Copyright (C) 2006 -2008 Gerard Klaver + * Copyright (C) 2007 Sam Revitch + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "sq930x" + +#include "gspca.h" + +MODULE_AUTHOR("Jean-Francois Moine \n" + "Gerard Klaver "); +MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct { /* exposure/gain control cluster */ + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + }; + + u8 do_ctrl; + u8 gpio[2]; + u8 sensor; + u8 type; +#define Generic 0 +#define Creative_live_motion 1 +}; +enum sensors { + SENSOR_ICX098BQ, + SENSOR_LZ24BP, + SENSOR_MI0360, + SENSOR_MT9V111, /* = MI360SOC */ + SENSOR_OV7660, + SENSOR_OV9630, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, +}; + +/* sq930x registers */ +#define SQ930_CTRL_UCBUS_IO 0x0001 +#define SQ930_CTRL_I2C_IO 0x0002 +#define SQ930_CTRL_GPIO 0x0005 +#define SQ930_CTRL_CAP_START 0x0010 +#define SQ930_CTRL_CAP_STOP 0x0011 +#define SQ930_CTRL_SET_EXPOSURE 0x001d +#define SQ930_CTRL_RESET 0x001e +#define SQ930_CTRL_GET_DEV_INFO 0x001f + +/* gpio 1 (8..15) */ +#define SQ930_GPIO_DFL_I2C_SDA 0x0001 +#define SQ930_GPIO_DFL_I2C_SCL 0x0002 +#define SQ930_GPIO_RSTBAR 0x0004 +#define SQ930_GPIO_EXTRA1 0x0040 +#define SQ930_GPIO_EXTRA2 0x0080 +/* gpio 3 (24..31) */ +#define SQ930_GPIO_POWER 0x0200 +#define SQ930_GPIO_DFL_LED 0x1000 + +struct ucbus_write_cmd { + u16 bw_addr; + u8 bw_data; +}; +struct i2c_write_cmd { + u8 reg; + u16 val; +}; + +static const struct ucbus_write_cmd icx098bq_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce}, + {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e}, + {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02}, + {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02}, + {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00}, + {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04}, + {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00}, + {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48}, + {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, + {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, + {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff}, + {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, + {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00}, + {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, + {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, + {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c}, + {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30}, + {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30}, + {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc}, + {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, + {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00}, + {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00}, + {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa}, + {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa}, + {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, + {0xf800, 0x03} +}; +static const struct ucbus_write_cmd icx098bq_start_1[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xc0}, + {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xc0}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd icx098bq_start_2[] = { + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03} +}; + +static const struct ucbus_write_cmd lz24bp_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe}, + {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06}, + {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02}, + {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00}, + {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00}, + {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03}, + {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00}, + {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48}, + {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, + {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, + {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0}, + {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, + {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00}, + {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, + {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, + {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30}, + {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c}, + {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c}, + {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d}, + {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, + {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d}, + {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d}, + {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04}, + {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04}, + {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, + {0xf800, 0x03} +}; +static const struct ucbus_write_cmd lz24bp_start_1_gen[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xb3}, + {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xb3}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd lz24bp_start_1_clm[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, + {0xf5f4, 0xc0}, + {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, + {0xf5f4, 0xc0}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd lz24bp_start_2[] = { + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03} +}; + +static const struct ucbus_write_cmd mi0360_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc}, + {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00} +}; +static const struct i2c_write_cmd mi0360_init_23[] = { + {0x30, 0x0040}, /* reserved - def 0x0005 */ + {0x31, 0x0000}, /* reserved - def 0x002a */ + {0x34, 0x0100}, /* reserved - def 0x0100 */ + {0x3d, 0x068f}, /* reserved - def 0x068f */ +}; +static const struct i2c_write_cmd mi0360_init_24[] = { + {0x03, 0x01e5}, /* window height */ + {0x04, 0x0285}, /* window width */ +}; +static const struct i2c_write_cmd mi0360_init_25[] = { + {0x35, 0x0020}, /* global gain */ + {0x2b, 0x0020}, /* green1 gain */ + {0x2c, 0x002a}, /* blue gain */ + {0x2d, 0x0028}, /* red gain */ + {0x2e, 0x0020}, /* green2 gain */ +}; +static const struct ucbus_write_cmd mi0360_start_1[] = { + {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xa6}, + {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xa6}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; +static const struct i2c_write_cmd mi0360_start_2[] = { + {0x62, 0x041d}, /* reserved - def 0x0418 */ +}; +static const struct i2c_write_cmd mi0360_start_3[] = { + {0x05, 0x007b}, /* horiz blanking */ +}; +static const struct i2c_write_cmd mi0360_start_4[] = { + {0x05, 0x03f5}, /* horiz blanking */ +}; + +static const struct i2c_write_cmd mt9v111_init_0[] = { + {0x01, 0x0001}, /* select IFP/SOC registers */ + {0x06, 0x300c}, /* operating mode control */ + {0x08, 0xcc00}, /* output format control (RGB) */ + {0x01, 0x0004}, /* select sensor core registers */ +}; +static const struct i2c_write_cmd mt9v111_init_1[] = { + {0x03, 0x01e5}, /* window height */ + {0x04, 0x0285}, /* window width */ +}; +static const struct i2c_write_cmd mt9v111_init_2[] = { + {0x30, 0x7800}, + {0x31, 0x0000}, + {0x07, 0x3002}, /* output control */ + {0x35, 0x0020}, /* global gain */ + {0x2b, 0x0020}, /* green1 gain */ + {0x2c, 0x0020}, /* blue gain */ + {0x2d, 0x0020}, /* red gain */ + {0x2e, 0x0020}, /* green2 gain */ +}; +static const struct ucbus_write_cmd mt9v111_start_1[] = { + {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xaa}, + {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xaa}, + {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a}, + {0xf5f9, 0x0a} +}; +static const struct i2c_write_cmd mt9v111_init_3[] = { + {0x62, 0x0405}, +}; +static const struct i2c_write_cmd mt9v111_init_4[] = { +/* {0x05, 0x00ce}, */ + {0x05, 0x005d}, /* horizontal blanking */ +}; + +static const struct ucbus_write_cmd ov7660_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0}, + {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03} +}; + +static const struct ucbus_write_cmd ov9630_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00}, + {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03} +}; + +/* start parameters indexed by [sensor][mode] */ +static const struct cap_s { + u8 cc_sizeid; + u8 cc_bytes[32]; +} capconfig[4][2] = { + [SENSOR_ICX098BQ] = { + {2, /* Bayer 320x240 */ + {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, + [SENSOR_LZ24BP] = { + {2, /* Bayer 320x240 */ + {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, + [SENSOR_MI0360] = { + {2, /* Bayer 320x240 */ + {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, + [SENSOR_MT9V111] = { + {2, /* Bayer 320x240 */ + {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, +}; + +struct sensor_s { + const char *name; + u8 i2c_addr; + u8 i2c_dum; + u8 gpio[5]; + u8 cmd_len; + const struct ucbus_write_cmd *cmd; +}; + +static const struct sensor_s sensor_tb[] = { + [SENSOR_ICX098BQ] = { + "icx098bp", + 0x00, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 8, icx098bq_start_0 + }, + [SENSOR_LZ24BP] = { + "lz24bp", + 0x00, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 8, lz24bp_start_0 + }, + [SENSOR_MI0360] = { + "mi0360", + 0x5d, 0x80, + {SQ930_GPIO_RSTBAR, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + 0 + }, + 7, mi0360_start_0 + }, + [SENSOR_MT9V111] = { + "mt9v111", + 0x5c, 0x7f, + {SQ930_GPIO_RSTBAR, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + 0 + }, + 7, mi0360_start_0 + }, + [SENSOR_OV7660] = { + "ov7660", + 0x21, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 7, ov7660_start_0 + }, + [SENSOR_OV9630] = { + "ov9630", + 0x30, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 7, ov9630_start_0 + }, +}; + +static void reg_r(struct gspca_dev *gspca_dev, + u16 value, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0x0c, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0, gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + pr_err("reg_r %04x failed %d\n", value, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); + msleep(30); + if (ret < 0) { + pr_err("reg_w %04x %04x failed %d\n", value, index, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index, + const u8 *data, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x", + value, index, *data, data[len - 1]); + memcpy(gspca_dev->usb_buf, data, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, gspca_dev->usb_buf, len, + 1000); + msleep(30); + if (ret < 0) { + pr_err("reg_wb %04x %04x failed %d\n", value, index, ret); + gspca_dev->usb_err = ret; + } +} + +static void i2c_write(struct sd *sd, + const struct i2c_write_cmd *cmd, + int ncmds) +{ + struct gspca_dev *gspca_dev = &sd->gspca_dev; + const struct sensor_s *sensor; + u16 val, idx; + u8 *buf; + int ret; + + if (gspca_dev->usb_err < 0) + return; + + sensor = &sensor_tb[sd->sensor]; + + val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO; + idx = (cmd->val & 0xff00) | cmd->reg; + + buf = gspca_dev->usb_buf; + *buf++ = sensor->i2c_dum; + *buf++ = cmd->val; + + while (--ncmds > 0) { + cmd++; + *buf++ = cmd->reg; + *buf++ = cmd->val >> 8; + *buf++ = sensor->i2c_dum; + *buf++ = cmd->val; + } + + PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x", + val, idx, gspca_dev->usb_buf[0], buf[-1]); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, idx, + gspca_dev->usb_buf, buf - gspca_dev->usb_buf, + 500); + if (ret < 0) { + pr_err("i2c_write failed %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void ucbus_write(struct gspca_dev *gspca_dev, + const struct ucbus_write_cmd *cmd, + int ncmds, + int batchsize) +{ + u8 *buf; + u16 val, idx; + int len, ret; + + if (gspca_dev->usb_err < 0) + return; + +#ifdef GSPCA_DEBUG + if ((batchsize - 1) * 3 > USB_BUF_SZ) { + pr_err("Bug: usb_buf overflow\n"); + gspca_dev->usb_err = -ENOMEM; + return; + } +#endif + + for (;;) { + len = ncmds; + if (len > batchsize) + len = batchsize; + ncmds -= len; + + val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO; + idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8); + + buf = gspca_dev->usb_buf; + while (--len > 0) { + cmd++; + *buf++ = cmd->bw_addr; + *buf++ = cmd->bw_addr >> 8; + *buf++ = cmd->bw_data; + } + if (buf != gspca_dev->usb_buf) + PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x", + val, idx, + gspca_dev->usb_buf[0], buf[-1]); + else + PDEBUG(D_USBO, "ucbus v: %04x i: %04x", + val, idx); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, idx, + gspca_dev->usb_buf, buf - gspca_dev->usb_buf, + 500); + if (ret < 0) { + pr_err("ucbus_write failed %d\n", ret); + gspca_dev->usb_err = ret; + return; + } + msleep(30); + if (ncmds <= 0) + break; + cmd++; + } +} + +static void gpio_set(struct sd *sd, u16 val, u16 mask) +{ + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + if (mask & 0x00ff) { + sd->gpio[0] &= ~mask; + sd->gpio[0] |= val; + reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO, + ~sd->gpio[0] << 8); + } + mask >>= 8; + val >>= 8; + if (mask) { + sd->gpio[1] &= ~mask; + sd->gpio[1] |= val; + reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO, + ~sd->gpio[1] << 8); + } +} + +static void gpio_init(struct sd *sd, + const u8 *gpio) +{ + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio, 0x000f); +} + +static void bridge_init(struct sd *sd) +{ + static const struct ucbus_write_cmd clkfreq_cmd = { + 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */ + }; + + ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1); + + gpio_set(sd, SQ930_GPIO_POWER, 0xff00); +} + +static void cmos_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + const struct sensor_s *sensor; + static const u8 probe_order[] = { +/* SENSOR_LZ24BP, (tested as ccd) */ + SENSOR_OV9630, + SENSOR_MI0360, + SENSOR_OV7660, + SENSOR_MT9V111, + }; + + for (i = 0; i < ARRAY_SIZE(probe_order); i++) { + sensor = &sensor_tb[probe_order[i]]; + ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8); + gpio_init(sd, sensor->gpio); + msleep(100); + reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1); + msleep(100); + if (gspca_dev->usb_buf[0] != 0) + break; + } + if (i >= ARRAY_SIZE(probe_order)) { + pr_err("Unknown sensor\n"); + gspca_dev->usb_err = -EINVAL; + return; + } + sd->sensor = probe_order[i]; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV9630: + pr_err("Sensor %s not yet treated\n", + sensor_tb[sd->sensor].name); + gspca_dev->usb_err = -EINVAL; + break; + } +} + +static void mt9v111_init(struct gspca_dev *gspca_dev) +{ + int i, nwait; + static const u8 cmd_001b[] = { + 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00 + }; + static const u8 cmd_011b[][7] = { + {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00}, + {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00}, + {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00}, + {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00}, + }; + + reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b); + for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) { + reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i], + ARRAY_SIZE(cmd_011b[0])); + msleep(400); + nwait = 20; + for (;;) { + reg_r(gspca_dev, 0x031b, 1); + if (gspca_dev->usb_buf[0] == 0 + || gspca_dev->usb_err != 0) + break; + if (--nwait < 0) { + PDEBUG(D_PROBE, "mt9v111_init timeout"); + gspca_dev->usb_err = -ETIME; + return; + } + msleep(50); + } + } +} + +static void global_init(struct sd *sd, int first_time) +{ + switch (sd->sensor) { + case SENSOR_ICX098BQ: + if (first_time) + ucbus_write(&sd->gspca_dev, + icx098bq_start_0, + 8, 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + case SENSOR_LZ24BP: + if (sd->type != Creative_live_motion) + gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff); + else + gpio_set(sd, 0, 0x00ff); + msleep(50); + if (first_time) + ucbus_write(&sd->gspca_dev, + lz24bp_start_0, + 8, 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + case SENSOR_MI0360: + if (first_time) + ucbus_write(&sd->gspca_dev, + mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2); + break; + default: +/* case SENSOR_MT9V111: */ + if (first_time) + mt9v111_init(&sd->gspca_dev); + else + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + } +} + +static void lz24bp_ppl(struct sd *sd, u16 ppl) +{ + struct ucbus_write_cmd cmds[2] = { + {0xf810, ppl >> 8}, + {0xf811, ppl} + }; + + ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, integclks, intstartclk, frameclks, min_frclk; + const struct sensor_s *sensor; + u16 cmd; + u8 buf[15]; + + integclks = expo; + i = 0; + cmd = SQ930_CTRL_SET_EXPOSURE; + + switch (sd->sensor) { + case SENSOR_ICX098BQ: /* ccd */ + case SENSOR_LZ24BP: + min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f; + if (integclks >= min_frclk) { + intstartclk = 0; + frameclks = integclks; + } else { + intstartclk = min_frclk - integclks; + frameclks = min_frclk; + } + buf[i++] = intstartclk >> 8; + buf[i++] = intstartclk; + buf[i++] = frameclks >> 8; + buf[i++] = frameclks; + buf[i++] = gain; + break; + default: /* cmos */ +/* case SENSOR_MI0360: */ +/* case SENSOR_MT9V111: */ + cmd |= 0x0100; + sensor = &sensor_tb[sd->sensor]; + buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */ + buf[i++] = 0x08; /* 2 * ni2c */ + buf[i++] = 0x09; /* reg = shutter width */ + buf[i++] = integclks >> 8; /* val H */ + buf[i++] = sensor->i2c_dum; + buf[i++] = integclks; /* val L */ + buf[i++] = 0x35; /* reg = global gain */ + buf[i++] = 0x00; /* val H */ + buf[i++] = sensor->i2c_dum; + buf[i++] = 0x80 + gain / 2; /* val L */ + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x83; + break; + } + reg_wb(gspca_dev, cmd, 0, buf, i); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + sd->sensor = id->driver_info >> 8; + sd->type = id->driver_info; + + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + cam->bulk = 1; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */ + +/*fixme: is this needed for icx098bp and mi0360? + if (sd->sensor != SENSOR_LZ24BP) + reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000); + */ + + reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8); + if (gspca_dev->usb_err < 0) + return gspca_dev->usb_err; + +/* it returns: + * 03 00 12 93 0b f6 c9 00 live! ultra + * 03 00 07 93 0b f6 ca 00 live! ultra for notebook + * 03 00 12 93 0b fe c8 00 Trust WB-3500T + * 02 00 06 93 0b fe c8 00 Joy-IT 318S + * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq + * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam + * + * byte + * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit) + * 1: 00 + * 2: 06 / 07 / 12 = mode webcam? firmware?? + * 3: 93 chip = 930b (930b or 930c) + * 4: 0b + * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors) + * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam? + * 7: 00 + */ + PDEBUG(D_PROBE, "info: %*ph", 8, gspca_dev->usb_buf); + + bridge_init(sd); + + if (sd->sensor == SENSOR_MI0360) { + + /* no sensor probe for icam tracer */ + if (gspca_dev->usb_buf[5] == 0xf6) /* if ccd */ + sd->sensor = SENSOR_ICX098BQ; + else + cmos_probe(gspca_dev); + } + if (gspca_dev->usb_err >= 0) { + PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name); + global_init(sd, 1); + } + return gspca_dev->usb_err; +} + +/* send the start/stop commands to the webcam */ +static void send_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const struct cap_s *cap; + int mode; + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + cap = &capconfig[sd->sensor][mode]; + reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START, + 0x0a00 | cap->cc_sizeid, + cap->cc_bytes, 32); +} + +static void send_stop(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0); +} + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */ + sd->do_ctrl = 0; + gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8; + return 0; +} + +/* start the capture */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode; + + bridge_init(sd); + global_init(sd, 0); + msleep(100); + + switch (sd->sensor) { + case SENSOR_ICX098BQ: + ucbus_write(gspca_dev, icx098bq_start_0, + ARRAY_SIZE(icx098bq_start_0), + 8); + ucbus_write(gspca_dev, icx098bq_start_1, + ARRAY_SIZE(icx098bq_start_1), + 5); + ucbus_write(gspca_dev, icx098bq_start_2, + ARRAY_SIZE(icx098bq_start_2), + 6); + msleep(50); + + /* 1st start */ + send_start(gspca_dev); + gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); + msleep(70); + reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000); + gpio_set(sd, 0x7f, 0x00ff); + + /* 2nd start */ + send_start(gspca_dev); + gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); + goto out; + case SENSOR_LZ24BP: + ucbus_write(gspca_dev, lz24bp_start_0, + ARRAY_SIZE(lz24bp_start_0), + 8); + if (sd->type != Creative_live_motion) + ucbus_write(gspca_dev, lz24bp_start_1_gen, + ARRAY_SIZE(lz24bp_start_1_gen), + 5); + else + ucbus_write(gspca_dev, lz24bp_start_1_clm, + ARRAY_SIZE(lz24bp_start_1_clm), + 5); + ucbus_write(gspca_dev, lz24bp_start_2, + ARRAY_SIZE(lz24bp_start_2), + 6); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310); + msleep(10); + break; + case SENSOR_MI0360: + ucbus_write(gspca_dev, mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + i2c_write(sd, mi0360_init_23, + ARRAY_SIZE(mi0360_init_23)); + i2c_write(sd, mi0360_init_24, + ARRAY_SIZE(mi0360_init_24)); + i2c_write(sd, mi0360_init_25, + ARRAY_SIZE(mi0360_init_25)); + ucbus_write(gspca_dev, mi0360_start_1, + ARRAY_SIZE(mi0360_start_1), + 5); + i2c_write(sd, mi0360_start_2, + ARRAY_SIZE(mi0360_start_2)); + i2c_write(sd, mi0360_start_3, + ARRAY_SIZE(mi0360_start_3)); + + /* 1st start */ + send_start(gspca_dev); + msleep(60); + send_stop(gspca_dev); + + i2c_write(sd, + mi0360_start_4, ARRAY_SIZE(mi0360_start_4)); + break; + default: +/* case SENSOR_MT9V111: */ + ucbus_write(gspca_dev, mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + i2c_write(sd, mt9v111_init_0, + ARRAY_SIZE(mt9v111_init_0)); + i2c_write(sd, mt9v111_init_1, + ARRAY_SIZE(mt9v111_init_1)); + i2c_write(sd, mt9v111_init_2, + ARRAY_SIZE(mt9v111_init_2)); + ucbus_write(gspca_dev, mt9v111_start_1, + ARRAY_SIZE(mt9v111_start_1), + 5); + i2c_write(sd, mt9v111_init_3, + ARRAY_SIZE(mt9v111_init_3)); + i2c_write(sd, mt9v111_init_4, + ARRAY_SIZE(mt9v111_init_4)); + break; + } + + send_start(gspca_dev); +out: + msleep(1000); + + if (sd->sensor == SENSOR_MT9V111) + gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED); + + sd->do_ctrl = 1; /* set the exposure */ + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_MT9V111) + gpio_set(sd, 0, SQ930_GPIO_DFL_LED); + send_stop(gspca_dev); +} + +/* function called when the application gets a new frame */ +/* It sets the exposure if required and restart the bulk transfer. */ +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0) + return; + sd->do_ctrl = 0; + + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure), + v4l2_ctrl_g_ctrl(sd->gain)); + + gspca_dev->cam.bulk_nurbs = 1; + ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC); + if (ret < 0) + pr_err("sd_dq_callback() err %d\n", ret); + + /* wait a little time, otherwise the webcam crashes */ + msleep(100); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->do_ctrl) + gspca_dev->cam.bulk_nurbs = 0; + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, ctrl->val, sd->gain->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 2); + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356); + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 1, 255, 1, 0x8d); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(2, &sd->exposure); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_dq_callback, +}; + +/* Table of supported USB devices */ +#define ST(sensor, type) \ + .driver_info = (SENSOR_ ## sensor << 8) \ + | (type) +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)}, + {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)}, + {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)}, + {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)}, + {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)}, + {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/stk014.c b/drivers/media/usb/gspca/stk014.c new file mode 100644 index 000000000000..8c0982607f25 --- /dev/null +++ b/drivers/media/usb/gspca/stk014.c @@ -0,0 +1,446 @@ +/* + * Syntek DV4000 (STK014) subdriver + * + * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "stk014" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define QUALITY 50 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* -- read a register -- */ +static u8 reg_r(struct gspca_dev *gspca_dev, + __u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return 0; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, + index, + gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + return 0; + } + return gspca_dev->usb_buf[0]; +} + +/* -- write a register -- */ +static void reg_w(struct gspca_dev *gspca_dev, + __u16 index, __u16 value) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* -- get a bulk value (4 bytes) -- */ +static void rcv_val(struct gspca_dev *gspca_dev, + int ads) +{ + struct usb_device *dev = gspca_dev->dev; + int alen, ret; + + reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff); + reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x636, ads & 0xff); + reg_w(gspca_dev, 0x637, 0); + reg_w(gspca_dev, 0x638, 4); /* len & 0xff */ + reg_w(gspca_dev, 0x639, 0); /* len >> 8 */ + reg_w(gspca_dev, 0x63a, 0); + reg_w(gspca_dev, 0x63b, 0); + reg_w(gspca_dev, 0x630, 5); + if (gspca_dev->usb_err < 0) + return; + ret = usb_bulk_msg(dev, + usb_rcvbulkpipe(dev, 0x05), + gspca_dev->usb_buf, + 4, /* length */ + &alen, + 500); /* timeout in milliseconds */ + if (ret < 0) { + pr_err("rcv_val err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* -- send a bulk value -- */ +static void snd_val(struct gspca_dev *gspca_dev, + int ads, + unsigned int val) +{ + struct usb_device *dev = gspca_dev->dev; + int alen, ret; + __u8 seq = 0; + + if (ads == 0x003f08) { + reg_r(gspca_dev, 0x0704); + seq = reg_r(gspca_dev, 0x0705); + reg_r(gspca_dev, 0x0650); + reg_w(gspca_dev, 0x654, seq); + } else { + reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff); + } + reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x656, ads & 0xff); + reg_w(gspca_dev, 0x657, 0); + reg_w(gspca_dev, 0x658, 0x04); /* size */ + reg_w(gspca_dev, 0x659, 0); + reg_w(gspca_dev, 0x65a, 0); + reg_w(gspca_dev, 0x65b, 0); + reg_w(gspca_dev, 0x650, 5); + if (gspca_dev->usb_err < 0) + return; + gspca_dev->usb_buf[0] = val >> 24; + gspca_dev->usb_buf[1] = val >> 16; + gspca_dev->usb_buf[2] = val >> 8; + gspca_dev->usb_buf[3] = val; + ret = usb_bulk_msg(dev, + usb_sndbulkpipe(dev, 6), + gspca_dev->usb_buf, + 4, + &alen, + 500); /* timeout in milliseconds */ + if (ret < 0) { + pr_err("snd_val err %d\n", ret); + gspca_dev->usb_err = ret; + } else { + if (ads == 0x003f08) { + seq += 4; + seq &= 0x3f; + reg_w(gspca_dev, 0x705, seq); + } + } +} + +/* set a camera parameter */ +static void set_par(struct gspca_dev *gspca_dev, + int parval) +{ + snd_val(gspca_dev, 0x003f08, parval); +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + int parval; + + parval = 0x06000000 /* whiteness */ + + (val << 16); + set_par(gspca_dev, parval); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + int parval; + + parval = 0x07000000 /* contrast */ + + (val << 16); + set_par(gspca_dev, parval); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + int parval; + + parval = 0x08000000 /* saturation */ + + (val << 16); + set_par(gspca_dev, parval); +} + +static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) +{ + set_par(gspca_dev, val == 1 + ? 0x33640000 /* 50 Hz */ + : 0x33780000); /* 60 Hz */ +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + u8 ret; + + /* check if the device responds */ + usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); + ret = reg_r(gspca_dev, 0x0740); + if (gspca_dev->usb_err >= 0) { + if (ret != 0xff) { + pr_err("init reg: 0x%02x\n", ret); + gspca_dev->usb_err = -EIO; + } + } + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret, value; + + /* create the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, QUALITY); + + /* work on alternate 1 */ + usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); + + set_par(gspca_dev, 0x10000000); + set_par(gspca_dev, 0x00000000); + set_par(gspca_dev, 0x8002e001); + set_par(gspca_dev, 0x14000000); + if (gspca_dev->width > 320) + value = 0x8002e001; /* 640x480 */ + else + value = 0x4001f000; /* 320x240 */ + set_par(gspca_dev, value); + ret = usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + gspca_dev->alt); + if (ret < 0) { + pr_err("set intf %d %d failed\n", + gspca_dev->iface, gspca_dev->alt); + gspca_dev->usb_err = ret; + goto out; + } + reg_r(gspca_dev, 0x0630); + rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ + reg_r(gspca_dev, 0x0650); + snd_val(gspca_dev, 0x000020, 0xffffffff); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); + set_par(gspca_dev, 0x09800000); /* Red ? */ + set_par(gspca_dev, 0x0a800000); /* Green ? */ + set_par(gspca_dev, 0x0b800000); /* Blue ? */ + set_par(gspca_dev, 0x0d030000); /* Gamma ? */ + + /* start the video flow */ + set_par(gspca_dev, 0x01000000); + set_par(gspca_dev, 0x01000000); + if (gspca_dev->usb_err >= 0) + PDEBUG(D_STREAM, "camera started alt: 0x%02x", + gspca_dev->alt); +out: + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + set_par(gspca_dev, 0x02000000); + set_par(gspca_dev, 0x02000000); + usb_set_interface(dev, gspca_dev->iface, 1); + reg_r(gspca_dev, 0x0630); + rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ + reg_r(gspca_dev, 0x0650); + snd_val(gspca_dev, 0x000020, 0xffffffff); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); + PDEBUG(D_STREAM, "camera stopped"); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + static unsigned char ffd9[] = {0xff, 0xd9}; + + /* a frame starts with: + * - 0xff 0xfe + * - 0x08 0x00 - length (little endian ?!) + * - 4 bytes = size of whole frame (BE - including header) + * - 0x00 0x0c + * - 0xff 0xd8 + * - .. JPEG image with escape sequences (ff 00) + * (without ending - ff d9) + */ + if (data[0] == 0xff && data[1] == 0xfe) { + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); + + /* put the JPEG 411 header */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + + /* beginning of the frame */ +#define STKHDRSZ 12 + data += STKHDRSZ; + len -= STKHDRSZ; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 127); + v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x05e1, 0x0893)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/stv0680.c b/drivers/media/usb/gspca/stv0680.c new file mode 100644 index 000000000000..67605272aaa8 --- /dev/null +++ b/drivers/media/usb/gspca/stv0680.c @@ -0,0 +1,353 @@ +/* + * STV0680 USB Camera Driver + * + * Copyright (C) 2009 Hans de Goede + * + * This module is adapted from the in kernel v4l1 stv680 driver: + * + * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) + * + * Thanks to STMicroelectronics for information on the usb commands, and + * to Steve Miller at STM for his help and encouragement while I was + * writing this driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "stv0680" + +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("STV0680 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_pix_format mode; + u8 orig_mode; + u8 video_mode; + u8 current_mode; +}; + +static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, + int size) +{ + int ret = -1; + u8 req_type = 0; + unsigned int pipe = 0; + + switch (set) { + case 0: /* 0xc1 */ + req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); + break; + case 1: /* 0x41 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + pipe = usb_sndctrlpipe(gspca_dev->dev, 0); + break; + case 2: /* 0x80 */ + req_type = USB_DIR_IN | USB_RECIP_DEVICE; + pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); + break; + case 3: /* 0x40 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + pipe = usb_sndctrlpipe(gspca_dev->dev, 0); + break; + } + + ret = usb_control_msg(gspca_dev->dev, pipe, + req, req_type, + val, 0, gspca_dev->usb_buf, size, 500); + + if ((ret < 0) && (req != 0x0a)) + pr_err("usb_control_msg error %i, request = 0x%x, error = %i\n", + set, req, ret); + + return ret; +} + +static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret) +{ + stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */ + PDEBUG(D_ERR, "last error: %i, command = 0x%x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + return ret; +} + +static int stv0680_get_video_mode(struct gspca_dev *gspca_dev) +{ + /* Note not sure if this init of usb_buf is really necessary */ + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = 0x0f; + + if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) { + PDEBUG(D_ERR, "Get_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ +} + +static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->current_mode == mode) + return 0; + + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = mode; + + if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) { + PDEBUG(D_ERR, "Set_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + /* Verify we got what we've asked for */ + if (stv0680_get_video_mode(gspca_dev) != mode) { + PDEBUG(D_ERR, "Error setting camera video mode!"); + return -EIO; + } + + sd->current_mode = mode; + + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + /* Give the camera some time to settle, otherwise initalization will + fail on hotplug, and yes it really needs a full second. */ + msleep(1000); + + /* ping camera to be sure STV0680 is present */ + if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 || + gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) { + PDEBUG(D_ERR, "STV(e): camera ping failed!!"); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + + /* get camera descriptor */ + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 || + gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) { + PDEBUG(D_ERR, "Could not get descriptor 0200."); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (!(gspca_dev->usb_buf[7] & 0x09)) { + PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode"); + return -ENODEV; + } + if (gspca_dev->usb_buf[7] & 0x01) + PDEBUG(D_PROBE, "Camera supports CIF mode"); + if (gspca_dev->usb_buf[7] & 0x02) + PDEBUG(D_PROBE, "Camera supports VGA mode"); + if (gspca_dev->usb_buf[7] & 0x04) + PDEBUG(D_PROBE, "Camera supports QCIF mode"); + if (gspca_dev->usb_buf[7] & 0x08) + PDEBUG(D_PROBE, "Camera supports QVGA mode"); + + if (gspca_dev->usb_buf[7] & 0x01) + sd->video_mode = 0x00; /* CIF */ + else + sd->video_mode = 0x03; /* QVGA */ + + /* FW rev, ASIC rev, sensor ID */ + PDEBUG(D_PROBE, "Firmware rev is %i.%i", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + PDEBUG(D_PROBE, "ASIC rev is %i.%i", + gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]); + PDEBUG(D_PROBE, "Sensor ID is %i", + (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4)); + + + ret = stv0680_get_video_mode(gspca_dev); + if (ret < 0) + return ret; + sd->current_mode = sd->orig_mode = ret; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + /* Get mode details */ + if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + cam->bulk = 1; + cam->bulk_nurbs = 1; /* The cam cannot handle more */ + cam->bulk_size = (gspca_dev->usb_buf[0] << 24) | + (gspca_dev->usb_buf[1] << 16) | + (gspca_dev->usb_buf[2] << 8) | + (gspca_dev->usb_buf[3]); + sd->mode.width = (gspca_dev->usb_buf[4] << 8) | + (gspca_dev->usb_buf[5]); /* 322, 356, 644 */ + sd->mode.height = (gspca_dev->usb_buf[6] << 8) | + (gspca_dev->usb_buf[7]); /* 242, 292, 484 */ + sd->mode.pixelformat = V4L2_PIX_FMT_STV0680; + sd->mode.field = V4L2_FIELD_NONE; + sd->mode.bytesperline = sd->mode.width; + sd->mode.sizeimage = cam->bulk_size; + sd->mode.colorspace = V4L2_COLORSPACE_SRGB; + + /* origGain = gspca_dev->usb_buf[12]; */ + + cam->cam_mode = &sd->mode; + cam->nmodes = 1; + + + ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 || + gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) { + pr_err("Could not get descriptor 0100\n"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + /* Start stream at: + 0x0000 = CIF (352x288) + 0x0100 = VGA (640x480) + 0x0300 = QVGA (320x240) */ + if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0) + return stv0680_handle_error(gspca_dev, -EIO); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* This is a high priority command; it stops all lower order cmds */ + if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0) + stv0680_handle_error(gspca_dev, -EIO); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->gspca_dev.present) + return; + + stv0680_set_video_mode(gspca_dev, sd->orig_mode); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Every now and then the camera sends a 16 byte packet, no idea + what it contains, but it is not image data, when this + happens the frame received before this packet is corrupt, + so discard it. */ + if (len != sd->mode.sizeimage) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* Finish the previous frame, we do this upon reception of the next + packet, even though it is already complete so that the strange 16 + byte packets send after a corrupt frame can discard it. */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* Store the just received frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0553, 0x0202)}, + {USB_DEVICE(0x041e, 0x4007)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/stv06xx/Kconfig b/drivers/media/usb/gspca/stv06xx/Kconfig new file mode 100644 index 000000000000..634ad38d9fb8 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/Kconfig @@ -0,0 +1,9 @@ +config USB_STV06XX + tristate "STV06XX USB Camera Driver" + depends on USB_GSPCA + help + Say Y here if you want support for cameras based on + the ST STV06XX chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stv06xx. diff --git a/drivers/media/usb/gspca/stv06xx/Makefile b/drivers/media/usb/gspca/stv06xx/Makefile new file mode 100644 index 000000000000..3a4b2f899049 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o + +gspca_stv06xx-objs := stv06xx.o \ + stv06xx_vv6410.o \ + stv06xx_hdcs.o \ + stv06xx_pb0100.o \ + stv06xx_st6422.o + +ccflags-y += -I$(srctree)/drivers/media/usb/gspca + diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.c b/drivers/media/usb/gspca/stv06xx/stv06xx.c new file mode 100644 index 000000000000..999ec7764449 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "stv06xx_sensor.h" + +MODULE_AUTHOR("Erik Andrén"); +MODULE_DESCRIPTION("STV06XX USB Camera Driver"); +MODULE_LICENSE("GPL"); + +static bool dump_bridge; +static bool dump_sensor; + +int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + u8 len = (i2c_data > 0xff) ? 2 : 1; + + buf[0] = i2c_data & 0xff; + buf[1] = (i2c_data >> 8) & 0xff; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, address, 0, buf, len, + STV06XX_URB_MSG_TIMEOUT); + + PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d", + i2c_data, address, err); + + return (err < 0) ? err : 0; +} + +int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x04, 0xc0, address, 0, buf, 1, + STV06XX_URB_MSG_TIMEOUT); + + *i2c_data = buf[0]; + + PDEBUG(D_CONF, "Reading 0x%x from address 0x%x, status %d", + *i2c_data, address, err); + + return (err < 0) ? err : 0; +} + +/* Wraps the normal write sensor bytes / words functions for writing a + single value */ +int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value) +{ + if (sd->sensor->i2c_len == 2) { + u16 data[2] = { address, value }; + return stv06xx_write_sensor_words(sd, data, 1); + } else { + u8 data[2] = { address, value }; + return stv06xx_write_sensor_bytes(sd, data, 1); + } +} + +static int stv06xx_write_sensor_finish(struct sd *sd) +{ + int err = 0; + + if (sd->bridge == BRIDGE_STV610) { + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + buf[0] = 0; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x1704, 0, buf, 1, + STV06XX_URB_MSG_TIMEOUT); + } + + return (err < 0) ? err : 0; +} + +int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) +{ + int err, i, j; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len); + for (i = 0; i < len;) { + /* Build the command buffer */ + memset(buf, 0, I2C_BUFFER_LENGTH); + for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) { + buf[j] = data[2*i]; + buf[0x10 + j] = data[2*i+1]; + PDEBUG(D_CONF, "I2C: Writing 0x%02x to reg 0x%02x", + data[2*i+1], data[2*i]); + } + buf[0x20] = sd->sensor->i2c_addr; + buf[0x21] = j - 1; /* Number of commands to send - 1 */ + buf[0x22] = I2C_WRITE_CMD; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x0400, 0, buf, + I2C_BUFFER_LENGTH, + STV06XX_URB_MSG_TIMEOUT); + if (err < 0) + return err; + } + return stv06xx_write_sensor_finish(sd); +} + +int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) +{ + int err, i, j; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len); + + for (i = 0; i < len;) { + /* Build the command buffer */ + memset(buf, 0, I2C_BUFFER_LENGTH); + for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) { + buf[j] = data[2*i]; + buf[0x10 + j * 2] = data[2*i+1]; + buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8; + PDEBUG(D_CONF, "I2C: Writing 0x%04x to reg 0x%02x", + data[2*i+1], data[2*i]); + } + buf[0x20] = sd->sensor->i2c_addr; + buf[0x21] = j - 1; /* Number of commands to send - 1 */ + buf[0x22] = I2C_WRITE_CMD; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x0400, 0, buf, + I2C_BUFFER_LENGTH, + STV06XX_URB_MSG_TIMEOUT); + if (err < 0) + return err; + } + return stv06xx_write_sensor_finish(sd); +} + +int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush); + if (err < 0) + return err; + + /* Clear mem */ + memset(buf, 0, I2C_BUFFER_LENGTH); + + buf[0] = address; + buf[0x20] = sd->sensor->i2c_addr; + buf[0x21] = 0; + + /* Read I2C register */ + buf[0x22] = I2C_READ_CMD; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH, + STV06XX_URB_MSG_TIMEOUT); + if (err < 0) { + pr_err("I2C: Read error writing address: %d\n", err); + return err; + } + + err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len, + STV06XX_URB_MSG_TIMEOUT); + if (sd->sensor->i2c_len == 2) + *value = buf[0] | (buf[1] << 8); + else + *value = buf[0]; + + PDEBUG(D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d", + *value, address, err); + + return (err < 0) ? err : 0; +} + +/* Dumps all bridge registers */ +static void stv06xx_dump_bridge(struct sd *sd) +{ + int i; + u8 data, buf; + + pr_info("Dumping all stv06xx bridge registers\n"); + for (i = 0x1400; i < 0x160f; i++) { + stv06xx_read_bridge(sd, i, &data); + + pr_info("Read 0x%x from address 0x%x\n", data, i); + } + + pr_info("Testing stv06xx bridge registers for writability\n"); + for (i = 0x1400; i < 0x160f; i++) { + stv06xx_read_bridge(sd, i, &data); + buf = data; + + stv06xx_write_bridge(sd, i, 0xff); + stv06xx_read_bridge(sd, i, &data); + if (data == 0xff) + pr_info("Register 0x%x is read/write\n", i); + else if (data != buf) + pr_info("Register 0x%x is read/write, but only partially\n", + i); + else + pr_info("Register 0x%x is read-only\n", i); + + stv06xx_write_bridge(sd, i, buf); + } +} + +/* this function is called at probe and resume time */ +static int stv06xx_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + + PDEBUG(D_PROBE, "Initializing camera"); + + /* Let the usb init settle for a bit + before performing the initialization */ + msleep(250); + + err = sd->sensor->init(sd); + + if (dump_sensor && sd->sensor->dump) + sd->sensor->dump(sd); + + return (err < 0) ? err : 0; +} + +/* this function is called at probe time */ +static int stv06xx_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_PROBE, "Initializing controls"); + + gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler; + return sd->sensor->init_controls(sd); +} + +/* Start the camera */ +static int stv06xx_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_host_interface *alt; + struct usb_interface *intf; + int err, packet_size; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size); + if (err < 0) + return err; + + /* Prepare the sensor for start */ + err = sd->sensor->start(sd); + if (err < 0) + goto out; + + /* Start isochronous streaming */ + err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1); + +out: + if (err < 0) + PDEBUG(D_STREAM, "Starting stream failed"); + else + PDEBUG(D_STREAM, "Started streaming"); + + return (err < 0) ? err : 0; +} + +static int stv06xx_isoc_init(struct gspca_dev *gspca_dev) +{ + struct usb_host_interface *alt; + struct sd *sd = (struct sd *) gspca_dev; + + /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ + alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; + alt->endpoint[0].desc.wMaxPacketSize = + cpu_to_le16(sd->sensor->max_packet_size[gspca_dev->curr_mode]); + + return 0; +} + +static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev) +{ + int ret, packet_size, min_packet_size; + struct usb_host_interface *alt; + struct sd *sd = (struct sd *) gspca_dev; + + alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode]; + if (packet_size <= min_packet_size) + return -EIO; + + packet_size -= 100; + if (packet_size < min_packet_size) + packet_size = min_packet_size; + alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size); + + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); + if (ret < 0) + PDEBUG(D_ERR|D_STREAM, "set alt 1 err %d", ret); + + return ret; +} + +static void stv06xx_stopN(struct gspca_dev *gspca_dev) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + + /* stop ISO-streaming */ + err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0); + if (err < 0) + goto out; + + err = sd->sensor->stop(sd); + +out: + if (err < 0) + PDEBUG(D_STREAM, "Failed to stop stream"); + else + PDEBUG(D_STREAM, "Stopped streaming"); +} + +/* + * Analyse an USB packet of the data stream and store it appropriately. + * Each packet contains an integral number of chunks. Each chunk has + * 2-bytes identification, followed by 2-bytes that describe the chunk + * length. Known/guessed chunk identifications are: + * 8001/8005/C001/C005 - Begin new frame + * 8002/8006/C002/C006 - End frame + * 0200/4200 - Contains actual image data, bayer or compressed + * 0005 - 11 bytes of unknown data + * 0100 - 2 bytes of unknown data + * The 0005 and 0100 chunks seem to appear only in compressed stream. + */ +static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_PACK, "Packet of length %d arrived", len); + + /* A packet may contain several frames + loop until the whole packet is reached */ + while (len) { + int id, chunk_len; + + if (len < 4) { + PDEBUG(D_PACK, "Packet is smaller than 4 bytes"); + return; + } + + /* Capture the id */ + id = (data[0] << 8) | data[1]; + + /* Capture the chunk length */ + chunk_len = (data[2] << 8) | data[3]; + PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len); + + data += 4; + len -= 4; + + if (len < chunk_len) { + PDEBUG(D_ERR, "URB packet length is smaller" + " than the specified chunk length"); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* First byte seem to be 02=data 2nd byte is unknown??? */ + if (sd->bridge == BRIDGE_ST6422 && (id & 0xff00) == 0x0200) + goto frame_data; + + switch (id) { + case 0x0200: + case 0x4200: +frame_data: + PDEBUG(D_PACK, "Frame data packet detected"); + + if (sd->to_skip) { + int skip = (sd->to_skip < chunk_len) ? + sd->to_skip : chunk_len; + data += skip; + len -= skip; + chunk_len -= skip; + sd->to_skip -= skip; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, + data, chunk_len); + break; + + case 0x8001: + case 0x8005: + case 0xc001: + case 0xc005: + PDEBUG(D_PACK, "Starting new frame"); + + /* Create a new frame, chunk length should be zero */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + NULL, 0); + + if (sd->bridge == BRIDGE_ST6422) + sd->to_skip = gspca_dev->width * 4; + + if (chunk_len) + PDEBUG(D_ERR, "Chunk length is " + "non-zero on a SOF"); + break; + + case 0x8002: + case 0x8006: + case 0xc002: + PDEBUG(D_PACK, "End of frame detected"); + + /* Complete the last frame (if any) */ + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + + if (chunk_len) + PDEBUG(D_ERR, "Chunk length is " + "non-zero on a EOF"); + break; + + case 0x0005: + PDEBUG(D_PACK, "Chunk 0x005 detected"); + /* Unknown chunk with 11 bytes of data, + occurs just before end of each frame + in compressed mode */ + break; + + case 0x0100: + PDEBUG(D_PACK, "Chunk 0x0100 detected"); + /* Unknown chunk with 2 bytes of data, + occurs 2-3 times per USB interrupt */ + break; + case 0x42ff: + PDEBUG(D_PACK, "Chunk 0x42ff detected"); + /* Special chunk seen sometimes on the ST6422 */ + break; + default: + PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id); + /* Unknown chunk */ + } + data += chunk_len; + len -= chunk_len; + } +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + + if (len == 1 && data[0] == 0x80) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + if (len == 1 && data[0] == 0x88) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + +static int stv06xx_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = stv06xx_config, + .init = stv06xx_init, + .init_controls = stv06xx_init_controls, + .start = stv06xx_start, + .stopN = stv06xx_stopN, + .pkt_scan = stv06xx_pkt_scan, + .isoc_init = stv06xx_isoc_init, + .isoc_nego = stv06xx_isoc_nego, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +/* This function is called at probe time */ +static int stv06xx_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_PROBE, "Configuring camera"); + + sd->bridge = id->driver_info; + gspca_dev->sd_desc = &sd_desc; + + if (dump_bridge) + stv06xx_dump_bridge(sd); + + sd->sensor = &stv06xx_sensor_st6422; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = &stv06xx_sensor_vv6410; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = &stv06xx_sensor_hdcs1x00; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = &stv06xx_sensor_hdcs1020; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = &stv06xx_sensor_pb0100; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = NULL; + return -ENODEV; +} + + + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + /* QuickCam Express */ + {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 }, + /* LEGO cam / QuickCam Web */ + {USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 }, + /* Dexxa WebCam USB */ + {USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 }, + /* QuickCam Messenger */ + {USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Communicate */ + {USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Messenger (new) */ + {USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 }, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + PDEBUG(D_PROBE, "Probing for a stv06xx device"); + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static void sd_disconnect(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + struct sd *sd = (struct sd *) gspca_dev; + void *priv = sd->sensor_priv; + PDEBUG(D_PROBE, "Disconnecting the stv06xx device"); + + sd->sensor = NULL; + gspca_disconnect(intf); + kfree(priv); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); + +module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); + +module_param(dump_sensor, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup"); diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.h b/drivers/media/usb/gspca/stv06xx/stv06xx.h new file mode 100644 index 000000000000..34957a4ec150 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_H_ +#define STV06XX_H_ + +#include +#include "gspca.h" + +#define MODULE_NAME "STV06xx" + +#define STV_ISOC_ENDPOINT_ADDR 0x81 + +#define STV_R 0x0509 + +#define STV_REG23 0x0423 + +/* Control registers of the STV0600 ASIC */ +#define STV_I2C_PARTNER 0x1420 +#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421 +#define STV_I2C_READ_WRITE_TOGGLE 0x1422 +#define STV_I2C_FLUSH 0x1423 +#define STV_I2C_SUCC_READ_REG_VALS 0x1424 + +#define STV_ISO_ENABLE 0x1440 +#define STV_SCAN_RATE 0x1443 +#define STV_LED_CTRL 0x1445 +#define STV_STV0600_EMULATION 0x1446 +#define STV_REG00 0x1500 +#define STV_REG01 0x1501 +#define STV_REG02 0x1502 +#define STV_REG03 0x1503 +#define STV_REG04 0x1504 + +#define STV_ISO_SIZE_L 0x15c1 +#define STV_ISO_SIZE_H 0x15c2 + +/* Refers to the CIF 352x288 and QCIF 176x144 */ +/* 1: 288 lines, 2: 144 lines */ +#define STV_Y_CTRL 0x15c3 + +#define STV_RESET 0x1620 + +/* 0xa: 352 columns, 0x6: 176 columns */ +#define STV_X_CTRL 0x1680 + +#define STV06XX_URB_MSG_TIMEOUT 5000 + +#define I2C_MAX_BYTES 16 +#define I2C_MAX_WORDS 8 + +#define I2C_BUFFER_LENGTH 0x23 +#define I2C_READ_CMD 3 +#define I2C_WRITE_CMD 1 + +#define LED_ON 1 +#define LED_OFF 0 + +/* STV06xx device descriptor */ +struct sd { + struct gspca_dev gspca_dev; + + /* A pointer to the currently connected sensor */ + const struct stv06xx_sensor *sensor; + + /* Sensor private data */ + void *sensor_priv; + + /* The first 4 lines produced by the stv6422 are no good, this keeps + track of how many bytes we still need to skip during a frame */ + int to_skip; + + /* Bridge / Camera type */ + u8 bridge; + #define BRIDGE_STV600 0 + #define BRIDGE_STV602 1 + #define BRIDGE_STV610 2 + #define BRIDGE_ST6422 3 /* With integrated sensor */ +}; + +int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data); +int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data); + +int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len); +int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len); + +int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value); +int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value); + +#endif diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c new file mode 100644 index 000000000000..06fa54c5efb2 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * Copyright (c) 2008 Chia-I Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "stv06xx_hdcs.h" + +static struct v4l2_pix_format hdcs1x00_mode[] = { + { + HDCS_1X00_DEF_WIDTH, + HDCS_1X00_DEF_HEIGHT, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = + HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, + .bytesperline = HDCS_1X00_DEF_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } +}; + +static struct v4l2_pix_format hdcs1020_mode[] = { + { + HDCS_1020_DEF_WIDTH, + HDCS_1020_DEF_HEIGHT, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = + HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, + .bytesperline = HDCS_1020_DEF_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } +}; + +enum hdcs_power_state { + HDCS_STATE_SLEEP, + HDCS_STATE_IDLE, + HDCS_STATE_RUN +}; + +/* no lock? */ +struct hdcs { + enum hdcs_power_state state; + int w, h; + + /* visible area of the sensor array */ + struct { + int left, top; + int width, height; + int border; + } array; + + struct { + /* Column timing overhead */ + u8 cto; + /* Column processing overhead */ + u8 cpo; + /* Row sample period constant */ + u16 rs; + /* Exposure reset duration */ + u16 er; + } exp; + + int psmp; +}; + +static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) +{ + u8 regs[I2C_MAX_BYTES * 2]; + int i; + + if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) || + (reg + len > 0xff))) + return -EINVAL; + + for (i = 0; i < len; i++) { + regs[2 * i] = reg; + regs[2 * i + 1] = vals[i]; + /* All addresses are shifted left one bit + * as bit 0 toggles r/w */ + reg += 2; + } + + return stv06xx_write_sensor_bytes(sd, regs, len); +} + +static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) +{ + struct hdcs *hdcs = sd->sensor_priv; + u8 val; + int ret; + + if (hdcs->state == state) + return 0; + + /* we need to go idle before running or sleeping */ + if (hdcs->state != HDCS_STATE_IDLE) { + ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); + if (ret) + return ret; + } + + hdcs->state = HDCS_STATE_IDLE; + + if (state == HDCS_STATE_IDLE) + return 0; + + switch (state) { + case HDCS_STATE_SLEEP: + val = HDCS_SLEEP_MODE; + break; + + case HDCS_STATE_RUN: + val = HDCS_RUN_ENABLE; + break; + + default: + return -EINVAL; + } + + ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); + + /* Update the state if the write succeeded */ + if (!ret) + hdcs->state = state; + + return ret; +} + +static int hdcs_reset(struct sd *sd) +{ + struct hdcs *hdcs = sd->sensor_priv; + int err; + + err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1); + if (err < 0) + return err; + + err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); + if (err < 0) + hdcs->state = HDCS_STATE_IDLE; + + return err; +} + +static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct hdcs *hdcs = sd->sensor_priv; + int rowexp, srowexp; + int max_srowexp; + /* Column time period */ + int ct; + /* Column processing period */ + int cp; + /* Row processing period */ + int rp; + /* Minimum number of column timing periods + within the column processing period */ + int mnct; + int cycles, err; + u8 exp[14]; + + cycles = val * HDCS_CLK_FREQ_MHZ * 257; + + ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); + cp = hdcs->exp.cto + (hdcs->w * ct / 2); + + /* the cycles one row takes */ + rp = hdcs->exp.rs + cp; + + rowexp = cycles / rp; + + /* the remaining cycles */ + cycles -= rowexp * rp; + + /* calculate sub-row exposure */ + if (IS_1020(sd)) { + /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */ + srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct; + + mnct = (hdcs->exp.er + 12 + ct - 1) / ct; + max_srowexp = hdcs->w - mnct; + } else { + /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */ + srowexp = cp - hdcs->exp.er - 6 - cycles; + + mnct = (hdcs->exp.er + 5 + ct - 1) / ct; + max_srowexp = cp - mnct * ct - 1; + } + + if (srowexp < 0) + srowexp = 0; + else if (srowexp > max_srowexp) + srowexp = max_srowexp; + + if (IS_1020(sd)) { + exp[0] = HDCS20_CONTROL; + exp[1] = 0x00; /* Stop streaming */ + exp[2] = HDCS_ROWEXPL; + exp[3] = rowexp & 0xff; + exp[4] = HDCS_ROWEXPH; + exp[5] = rowexp >> 8; + exp[6] = HDCS20_SROWEXP; + exp[7] = (srowexp >> 2) & 0xff; + exp[8] = HDCS20_ERROR; + exp[9] = 0x10; /* Clear exposure error flag*/ + exp[10] = HDCS20_CONTROL; + exp[11] = 0x04; /* Restart streaming */ + err = stv06xx_write_sensor_bytes(sd, exp, 6); + } else { + exp[0] = HDCS00_CONTROL; + exp[1] = 0x00; /* Stop streaming */ + exp[2] = HDCS_ROWEXPL; + exp[3] = rowexp & 0xff; + exp[4] = HDCS_ROWEXPH; + exp[5] = rowexp >> 8; + exp[6] = HDCS00_SROWEXPL; + exp[7] = srowexp & 0xff; + exp[8] = HDCS00_SROWEXPH; + exp[9] = srowexp >> 8; + exp[10] = HDCS_STATUS; + exp[11] = 0x10; /* Clear exposure error flag*/ + exp[12] = HDCS00_CONTROL; + exp[13] = 0x04; /* Restart streaming */ + err = stv06xx_write_sensor_bytes(sd, exp, 7); + if (err < 0) + return err; + } + PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d", + val, rowexp, srowexp); + return err; +} + +static int hdcs_set_gains(struct sd *sd, u8 g) +{ + int err; + u8 gains[4]; + + /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ + if (g > 127) + g = 0x80 | (g / 2); + + gains[0] = g; + gains[1] = g; + gains[2] = g; + gains[3] = g; + + err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); + return err; +} + +static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + PDEBUG(D_V4L2, "Writing gain %d", val); + return hdcs_set_gains((struct sd *) gspca_dev, + val & 0xff); +} + +static int hdcs_set_size(struct sd *sd, + unsigned int width, unsigned int height) +{ + struct hdcs *hdcs = sd->sensor_priv; + u8 win[4]; + unsigned int x, y; + int err; + + /* must be multiple of 4 */ + width = (width + 3) & ~0x3; + height = (height + 3) & ~0x3; + + if (width > hdcs->array.width) + width = hdcs->array.width; + + if (IS_1020(sd)) { + /* the borders are also invalid */ + if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP + > hdcs->array.height) + height = hdcs->array.height - 2 * hdcs->array.border - + HDCS_1020_BOTTOM_Y_SKIP; + + y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2 + + hdcs->array.top; + } else { + if (height > hdcs->array.height) + height = hdcs->array.height; + + y = hdcs->array.top + (hdcs->array.height - height) / 2; + } + + x = hdcs->array.left + (hdcs->array.width - width) / 2; + + win[0] = y / 4; + win[1] = x / 4; + win[2] = (y + height) / 4 - 1; + win[3] = (x + width) / 4 - 1; + + err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4); + if (err < 0) + return err; + + /* Update the current width and height */ + hdcs->w = width; + hdcs->h = height; + return err; +} + +static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err = hdcs_set_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = hdcs_set_exposure(gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops hdcs_ctrl_ops = { + .s_ctrl = hdcs_s_ctrl, +}; + +static int hdcs_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 2); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN); + return hdl->error; +} + +static int hdcs_probe_1x00(struct sd *sd) +{ + struct hdcs *hdcs; + u16 sensor; + int ret; + + ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); + if (ret < 0 || sensor != 0x08) + return -ENODEV; + + pr_info("HDCS-1000/1100 sensor detected\n"); + + sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); + + hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); + if (!hdcs) + return -ENOMEM; + + hdcs->array.left = 8; + hdcs->array.top = 8; + hdcs->array.width = HDCS_1X00_DEF_WIDTH; + hdcs->array.height = HDCS_1X00_DEF_HEIGHT; + hdcs->array.border = 4; + + hdcs->exp.cto = 4; + hdcs->exp.cpo = 2; + hdcs->exp.rs = 186; + hdcs->exp.er = 100; + + /* + * Frame rate on HDCS-1000 with STV600 depends on PSMP: + * 4 = doesn't work at all + * 5 = 7.8 fps, + * 6 = 6.9 fps, + * 8 = 6.3 fps, + * 10 = 5.5 fps, + * 15 = 4.4 fps, + * 31 = 2.8 fps + * + * Frame rate on HDCS-1000 with STV602 depends on PSMP: + * 15 = doesn't work at all + * 18 = doesn't work at all + * 19 = 7.3 fps + * 20 = 7.4 fps + * 21 = 7.4 fps + * 22 = 7.4 fps + * 24 = 6.3 fps + * 30 = 5.4 fps + */ + hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5; + + sd->sensor_priv = hdcs; + + return 0; +} + +static int hdcs_probe_1020(struct sd *sd) +{ + struct hdcs *hdcs; + u16 sensor; + int ret; + + ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); + if (ret < 0 || sensor != 0x10) + return -ENODEV; + + pr_info("HDCS-1020 sensor detected\n"); + + sd->gspca_dev.cam.cam_mode = hdcs1020_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); + + hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); + if (!hdcs) + return -ENOMEM; + + /* + * From Andrey's test image: looks like HDCS-1020 upper-left + * visible pixel is at 24,8 (y maybe even smaller?) and lower-right + * visible pixel at 375,299 (x maybe even larger?) + */ + hdcs->array.left = 24; + hdcs->array.top = 4; + hdcs->array.width = HDCS_1020_DEF_WIDTH; + hdcs->array.height = 304; + hdcs->array.border = 4; + + hdcs->psmp = 6; + + hdcs->exp.cto = 3; + hdcs->exp.cpo = 3; + hdcs->exp.rs = 155; + hdcs->exp.er = 96; + + sd->sensor_priv = hdcs; + + return 0; +} + +static int hdcs_start(struct sd *sd) +{ + PDEBUG(D_STREAM, "Starting stream"); + + return hdcs_set_state(sd, HDCS_STATE_RUN); +} + +static int hdcs_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "Halting stream"); + + return hdcs_set_state(sd, HDCS_STATE_SLEEP); +} + +static int hdcs_init(struct sd *sd) +{ + struct hdcs *hdcs = sd->sensor_priv; + int i, err = 0; + + /* Set the STV0602AA in STV0600 emulation mode */ + if (sd->bridge == BRIDGE_STV602) + stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); + + /* Execute the bridge init */ + for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) { + err = stv06xx_write_bridge(sd, stv_bridge_init[i][0], + stv_bridge_init[i][1]); + } + if (err < 0) + return err; + + /* sensor soft reset */ + hdcs_reset(sd); + + /* Execute the sensor init */ + for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) { + err = stv06xx_write_sensor(sd, stv_sensor_init[i][0], + stv_sensor_init[i][1]); + } + if (err < 0) + return err; + + /* Enable continuous frame capture, bit 2: stop when frame complete */ + err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3)); + if (err < 0) + return err; + + /* Set PGA sample duration + (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */ + if (IS_1020(sd)) + err = stv06xx_write_sensor(sd, HDCS_TCTRL, + (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); + else + err = stv06xx_write_sensor(sd, HDCS_TCTRL, + (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp); + if (err < 0) + return err; + + return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); +} + +static int hdcs_dump(struct sd *sd) +{ + u16 reg, val; + + pr_info("Dumping sensor registers:\n"); + + for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { + stv06xx_read_sensor(sd, reg, &val); + pr_info("reg 0x%02x = 0x%02x\n", reg, val); + } + return 0; +} diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h new file mode 100644 index 000000000000..1ba9158d0102 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * Copyright (c) 2008 Chia-I Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_HDCS_H_ +#define STV06XX_HDCS_H_ + +#include "stv06xx_sensor.h" + +#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG) +#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL) + +#define HDCS_1X00_DEF_WIDTH 360 +#define HDCS_1X00_DEF_HEIGHT 296 + +#define HDCS_1020_DEF_WIDTH 352 +#define HDCS_1020_DEF_HEIGHT 292 + +#define HDCS_1020_BOTTOM_Y_SKIP 4 + +#define HDCS_CLK_FREQ_MHZ 25 + +#define HDCS_ADC_START_SIG_DUR 3 + +/* LSB bit of I2C or register address signifies write (0) or read (1) */ +/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */ +/* Identifications Register */ +#define HDCS_IDENT (0x00 << 1) +/* Status Register */ +#define HDCS_STATUS (0x01 << 1) +/* Interrupt Mask Register */ +#define HDCS_IMASK (0x02 << 1) +/* Pad Control Register */ +#define HDCS_PCTRL (0x03 << 1) +/* Pad Drive Control Register */ +#define HDCS_PDRV (0x04 << 1) +/* Interface Control Register */ +#define HDCS_ICTRL (0x05 << 1) +/* Interface Timing Register */ +#define HDCS_ITMG (0x06 << 1) +/* Baud Fraction Register */ +#define HDCS_BFRAC (0x07 << 1) +/* Baud Rate Register */ +#define HDCS_BRATE (0x08 << 1) +/* ADC Control Register */ +#define HDCS_ADCCTRL (0x09 << 1) +/* First Window Row Register */ +#define HDCS_FWROW (0x0a << 1) +/* First Window Column Register */ +#define HDCS_FWCOL (0x0b << 1) +/* Last Window Row Register */ +#define HDCS_LWROW (0x0c << 1) +/* Last Window Column Register */ +#define HDCS_LWCOL (0x0d << 1) +/* Timing Control Register */ +#define HDCS_TCTRL (0x0e << 1) +/* PGA Gain Register: Even Row, Even Column */ +#define HDCS_ERECPGA (0x0f << 1) +/* PGA Gain Register: Even Row, Odd Column */ +#define HDCS_EROCPGA (0x10 << 1) +/* PGA Gain Register: Odd Row, Even Column */ +#define HDCS_ORECPGA (0x11 << 1) +/* PGA Gain Register: Odd Row, Odd Column */ +#define HDCS_OROCPGA (0x12 << 1) +/* Row Exposure Low Register */ +#define HDCS_ROWEXPL (0x13 << 1) +/* Row Exposure High Register */ +#define HDCS_ROWEXPH (0x14 << 1) + +/* I2C Registers only for HDCS-1000/1100 */ +/* Sub-Row Exposure Low Register */ +#define HDCS00_SROWEXPL (0x15 << 1) +/* Sub-Row Exposure High Register */ +#define HDCS00_SROWEXPH (0x16 << 1) +/* Configuration Register */ +#define HDCS00_CONFIG (0x17 << 1) +/* Control Register */ +#define HDCS00_CONTROL (0x18 << 1) + +/* I2C Registers only for HDCS-1020 */ +/* Sub-Row Exposure Register */ +#define HDCS20_SROWEXP (0x15 << 1) +/* Error Control Register */ +#define HDCS20_ERROR (0x16 << 1) +/* Interface Timing 2 Register */ +#define HDCS20_ITMG2 (0x17 << 1) +/* Interface Control 2 Register */ +#define HDCS20_ICTRL2 (0x18 << 1) +/* Horizontal Blank Register */ +#define HDCS20_HBLANK (0x19 << 1) +/* Vertical Blank Register */ +#define HDCS20_VBLANK (0x1a << 1) +/* Configuration Register */ +#define HDCS20_CONFIG (0x1b << 1) +/* Control Register */ +#define HDCS20_CONTROL (0x1c << 1) + +#define HDCS_RUN_ENABLE (1 << 2) +#define HDCS_SLEEP_MODE (1 << 1) + +#define HDCS_DEFAULT_EXPOSURE 48 +#define HDCS_DEFAULT_GAIN 50 + +static int hdcs_probe_1x00(struct sd *sd); +static int hdcs_probe_1020(struct sd *sd); +static int hdcs_start(struct sd *sd); +static int hdcs_init(struct sd *sd); +static int hdcs_init_controls(struct sd *sd); +static int hdcs_stop(struct sd *sd); +static int hdcs_dump(struct sd *sd); + +static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { + .name = "HP HDCS-1000/1100", + .i2c_flush = 0, + .i2c_addr = (0x55 << 1), + .i2c_len = 1, + + /* FIXME (see if we can lower min_packet_size, needs testing, and also + adjusting framerate when the bandwidth gets lower) */ + .min_packet_size = { 847 }, + .max_packet_size = { 847 }, + + .init = hdcs_init, + .init_controls = hdcs_init_controls, + .probe = hdcs_probe_1x00, + .start = hdcs_start, + .stop = hdcs_stop, + .dump = hdcs_dump, +}; + +const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { + .name = "HDCS-1020", + .i2c_flush = 0, + .i2c_addr = (0x55 << 1), + .i2c_len = 1, + + /* FIXME (see if we can lower min_packet_size, needs testing, and also + adjusting framerate when the bandwidthm gets lower) */ + .min_packet_size = { 847 }, + .max_packet_size = { 847 }, + + .init = hdcs_init, + .init_controls = hdcs_init_controls, + .probe = hdcs_probe_1020, + .start = hdcs_start, + .stop = hdcs_stop, + .dump = hdcs_dump, +}; + +static const u16 stv_bridge_init[][2] = { + {STV_ISO_ENABLE, 0}, + {STV_REG23, 0}, + {STV_REG00, 0x1d}, + {STV_REG01, 0xb5}, + {STV_REG02, 0xa8}, + {STV_REG03, 0x95}, + {STV_REG04, 0x07}, + + {STV_SCAN_RATE, 0x20}, + {STV_Y_CTRL, 0x01}, + {STV_X_CTRL, 0x0a} +}; + +static const u8 stv_sensor_init[][2] = { + /* Clear status (writing 1 will clear the corresponding status bit) */ + {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, + /* Disable all interrupts */ + {HDCS_IMASK, 0x00}, + {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)}, + {HDCS_PDRV, 0x00}, + {HDCS_ICTRL, BIT(5)}, + {HDCS_ITMG, BIT(4) | BIT(1)}, + /* ADC output resolution to 10 bits */ + {HDCS_ADCCTRL, 10} +}; + +#endif diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c new file mode 100644 index 000000000000..cdfc3d05ab6b --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +/* + * The spec file for the PB-0100 suggests the following for best quality + * images after the sensor has been reset : + * + * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC + to produce good black level + * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes + through R53 + * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for + auto-exposure + * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain + * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value + * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on + auto-exposure routine + * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "stv06xx_pb0100.h" + +struct pb0100_ctrls { + struct { /* one big happy control cluster... */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *red; + struct v4l2_ctrl *blue; + struct v4l2_ctrl *natural; + }; + struct v4l2_ctrl *target; +}; + +static struct v4l2_pix_format pb0100_mode[] = { +/* low res / subsample modes disabled as they are only half res horizontal, + halving the vertical resolution does not seem to work */ + { + 320, + 240, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = PB0100_CROP_TO_VGA + }, + { + 352, + 288, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 352 * 288, + .bytesperline = 352, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + struct pb0100_ctrls *ctrls = sd->sensor_priv; + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + err = pb0100_set_autogain(gspca_dev, ctrl->val); + if (err) + break; + if (ctrl->val) + break; + err = pb0100_set_gain(gspca_dev, ctrls->gain->val); + if (err) + break; + err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val); + break; + case V4L2_CTRL_CLASS_USER + 0x1001: + err = pb0100_set_autogain_target(gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops pb0100_ctrl_ops = { + .s_ctrl = pb0100_s_ctrl, +}; + +static int pb0100_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + struct pb0100_ctrls *ctrls; + static const struct v4l2_ctrl_config autogain_target = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1000, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain Target", + .max = 255, + .step = 1, + .def = 128, + }; + static const struct v4l2_ctrl_config natural_light = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Natural Light Source", + .max = 1, + .step = 1, + .def = 1, + }; + + ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL); + if (!ctrls) + return -ENOMEM; + + v4l2_ctrl_handler_init(hdl, 6); + ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 511, 1, 12); + ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 128); + ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_RED_BALANCE, -255, 255, 1, 0); + ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0); + ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL); + ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL); + if (hdl->error) { + kfree(ctrls); + return hdl->error; + } + sd->sensor_priv = ctrls; + v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false); + return 0; +} + +static int pb0100_probe(struct sd *sd) +{ + u16 sensor; + int err; + + err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); + + if (err < 0) + return -ENODEV; + if ((sensor >> 8) != 0x64) + return -ENODEV; + + pr_info("Photobit pb0100 sensor detected\n"); + + sd->gspca_dev.cam.cam_mode = pb0100_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); + + return 0; +} + +static int pb0100_start(struct sd *sd) +{ + int err, packet_size, max_packet_size; + struct usb_host_interface *alt; + struct usb_interface *intf; + struct cam *cam = &sd->gspca_dev.cam; + u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) + return -ENODEV; + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + + /* If we don't have enough bandwidth use a lower framerate */ + max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode]; + if (packet_size < max_packet_size) + stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); + else + stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1)); + + /* Setup sensor window */ + if (mode & PB0100_CROP_TO_VGA) { + stv06xx_write_sensor(sd, PB_RSTART, 30); + stv06xx_write_sensor(sd, PB_CSTART, 20); + stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1); + stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1); + } else { + stv06xx_write_sensor(sd, PB_RSTART, 8); + stv06xx_write_sensor(sd, PB_CSTART, 4); + stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1); + stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1); + } + + if (mode & PB0100_SUBSAMPLE) { + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */ + stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); + + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); + } else { + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); + stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); + /* larger -> slower */ + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); + } + + err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); + PDEBUG(D_STREAM, "Started stream, status: %d", err); + + return (err < 0) ? err : 0; +} + +static int pb0100_stop(struct sd *sd) +{ + int err; + + err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1); + + if (err < 0) + goto out; + + /* Set bit 1 to zero */ + err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); + + PDEBUG(D_STREAM, "Halting stream"); +out: + return (err < 0) ? err : 0; +} + +/* FIXME: Sort the init commands out and put them into tables, + this is only for getting the camera to work */ +/* FIXME: No error handling for now, + add this once the init has been converted to proper tables */ +static int pb0100_init(struct sd *sd) +{ + stv06xx_write_bridge(sd, STV_REG00, 1); + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0); + + /* Reset sensor */ + stv06xx_write_sensor(sd, PB_RESET, 1); + stv06xx_write_sensor(sd, PB_RESET, 0); + + /* Disable chip */ + stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); + + /* Gain stuff...*/ + stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6)); + stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12); + + /* Set up auto-exposure */ + /* ADC VREF_HI new setting for a transition + from the Expose1 to the Expose2 setting */ + stv06xx_write_sensor(sd, PB_R28, 12); + /* gain max for autoexposure */ + stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180); + /* gain min for autoexposure */ + stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12); + /* Maximum frame integration time (programmed into R8) + allowed for auto-exposure routine */ + stv06xx_write_sensor(sd, PB_R54, 3); + /* Minimum frame integration time (programmed into R8) + allowed for auto-exposure routine */ + stv06xx_write_sensor(sd, PB_R55, 0); + stv06xx_write_sensor(sd, PB_UPDATEINT, 1); + /* R15 Expose0 (maximum that auto-exposure may use) */ + stv06xx_write_sensor(sd, PB_R15, 800); + /* R17 Expose2 (minimum that auto-exposure may use) */ + stv06xx_write_sensor(sd, PB_R17, 10); + + stv06xx_write_sensor(sd, PB_EXPGAIN, 0); + + /* 0x14 */ + stv06xx_write_sensor(sd, PB_VOFFSET, 0); + /* 0x0D */ + stv06xx_write_sensor(sd, PB_ADCGAINH, 11); + /* Set black level (important!) */ + stv06xx_write_sensor(sd, PB_ADCGAINL, 0); + + /* ??? */ + stv06xx_write_bridge(sd, STV_REG00, 0x11); + stv06xx_write_bridge(sd, STV_REG03, 0x45); + stv06xx_write_bridge(sd, STV_REG04, 0x07); + + /* Scan/timing for the sensor */ + stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); + stv06xx_write_sensor(sd, PB_CFILLIN, 14); + stv06xx_write_sensor(sd, PB_VBL, 0); + stv06xx_write_sensor(sd, PB_FINTTIME, 0); + stv06xx_write_sensor(sd, PB_RINTTIME, 123); + + stv06xx_write_bridge(sd, STV_REG01, 0xc2); + stv06xx_write_bridge(sd, STV_REG02, 0xb0); + return 0; +} + +static int pb0100_dump(struct sd *sd) +{ + return 0; +} + +static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + struct pb0100_ctrls *ctrls = sd->sensor_priv; + + err = stv06xx_write_sensor(sd, PB_G1GAIN, val); + if (!err) + err = stv06xx_write_sensor(sd, PB_G2GAIN, val); + PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); + + if (!err) + err = pb0100_set_red_balance(gspca_dev, ctrls->red->val); + if (!err) + err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val); + + return err; +} + +static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + struct pb0100_ctrls *ctrls = sd->sensor_priv; + + val += ctrls->gain->val; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + + err = stv06xx_write_sensor(sd, PB_RGAIN, val); + PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err); + + return err; +} + +static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + struct pb0100_ctrls *ctrls = sd->sensor_priv; + + val += ctrls->gain->val; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + + err = stv06xx_write_sensor(sd, PB_BGAIN, val); + PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err); + + return err; +} + +static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + + err = stv06xx_write_sensor(sd, PB_RINTTIME, val); + PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); + + return err; +} + +static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + struct pb0100_ctrls *ctrls = sd->sensor_priv; + + if (val) { + if (ctrls->natural->val) + val = BIT(6)|BIT(4)|BIT(0); + else + val = BIT(4)|BIT(0); + } else + val = 0; + + err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); + PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", + val, ctrls->natural->val, err); + + return err; +} + +static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) +{ + int err, totalpixels, brightpixels, darkpixels; + struct sd *sd = (struct sd *) gspca_dev; + + /* Number of pixels counted by the sensor when subsampling the pixels. + * Slightly larger than the real value to avoid oscillation */ + totalpixels = gspca_dev->width * gspca_dev->height; + totalpixels = totalpixels/(8*8) + totalpixels/(64*64); + + brightpixels = (totalpixels * val) >> 8; + darkpixels = totalpixels - brightpixels; + err = stv06xx_write_sensor(sd, PB_R21, brightpixels); + if (!err) + err = stv06xx_write_sensor(sd, PB_R22, darkpixels); + + PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err); + + return err; +} diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h new file mode 100644 index 000000000000..5071e5353fd3 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_PB0100_H_ +#define STV06XX_PB0100_H_ + +#include "stv06xx_sensor.h" + +/* mode priv field flags */ +#define PB0100_CROP_TO_VGA 0x01 +#define PB0100_SUBSAMPLE 0x02 + +/* I2C Registers */ +#define PB_IDENT 0x00 /* Chip Version */ +#define PB_RSTART 0x01 /* Row Window Start */ +#define PB_CSTART 0x02 /* Column Window Start */ +#define PB_RWSIZE 0x03 /* Row Window Size */ +#define PB_CWSIZE 0x04 /* Column Window Size */ +#define PB_CFILLIN 0x05 /* Column Fill-In */ +#define PB_VBL 0x06 /* Vertical Blank Count */ +#define PB_CONTROL 0x07 /* Control Mode */ +#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */ +#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */ +#define PB_ROWSPEED 0x0a /* Row Speed Control */ +#define PB_ABORTFRAME 0x0b /* Abort Frame */ +#define PB_R12 0x0c /* Reserved */ +#define PB_RESET 0x0d /* Reset */ +#define PB_EXPGAIN 0x0e /* Exposure Gain Command */ +#define PB_R15 0x0f /* Expose0 */ +#define PB_R16 0x10 /* Expose1 */ +#define PB_R17 0x11 /* Expose2 */ +#define PB_R18 0x12 /* Low0_DAC */ +#define PB_R19 0x13 /* Low1_DAC */ +#define PB_R20 0x14 /* Low2_DAC */ +#define PB_R21 0x15 /* Threshold11 */ +#define PB_R22 0x16 /* Threshold0x */ +#define PB_UPDATEINT 0x17 /* Update Interval */ +#define PB_R24 0x18 /* High_DAC */ +#define PB_R25 0x19 /* Trans0H */ +#define PB_R26 0x1a /* Trans1L */ +#define PB_R27 0x1b /* Trans1H */ +#define PB_R28 0x1c /* Trans2L */ +#define PB_R29 0x1d /* Reserved */ +#define PB_R30 0x1e /* Reserved */ +#define PB_R31 0x1f /* Wait to Read */ +#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */ +#define PB_R33 0x21 /* IREF_VLN */ +#define PB_R34 0x22 /* IREF_VLP */ +#define PB_R35 0x23 /* IREF_VLN_INTEG */ +#define PB_R36 0x24 /* IREF_MASTER */ +#define PB_R37 0x25 /* IDACP */ +#define PB_R38 0x26 /* IDACN */ +#define PB_R39 0x27 /* DAC_Control_Reg */ +#define PB_R40 0x28 /* VCL */ +#define PB_R41 0x29 /* IREF_VLN_ADCIN */ +#define PB_R42 0x2a /* Reserved */ +#define PB_G1GAIN 0x2b /* Green 1 Gain */ +#define PB_BGAIN 0x2c /* Blue Gain */ +#define PB_RGAIN 0x2d /* Red Gain */ +#define PB_G2GAIN 0x2e /* Green 2 Gain */ +#define PB_R47 0x2f /* Dark Row Address */ +#define PB_R48 0x30 /* Dark Row Options */ +#define PB_R49 0x31 /* Reserved */ +#define PB_R50 0x32 /* Image Test Data */ +#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */ +#define PB_ADCMINGAIN 0x34 /* Minimum Gain */ +#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */ +#define PB_R54 0x36 /* Maximum Frame */ +#define PB_R55 0x37 /* Minimum Frame */ +#define PB_R56 0x38 /* Reserved */ +#define PB_VOFFSET 0x39 /* VOFFSET */ +#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */ +#define PB_ADCGAINH 0x3b /* VREF_HI */ +#define PB_ADCGAINL 0x3c /* VREF_LO */ +#define PB_R61 0x3d /* Reserved */ +#define PB_R62 0x3e /* Reserved */ +#define PB_R63 0x3f /* Reserved */ +#define PB_R64 0x40 /* Red/Blue Gain */ +#define PB_R65 0x41 /* Green 2/Green 1 Gain */ +#define PB_R66 0x42 /* VREF_HI/LO */ +#define PB_R67 0x43 /* Integration Time/Row Unit Count */ +#define PB_R240 0xf0 /* ADC Test */ +#define PB_R241 0xf1 /* Chip Enable */ +#define PB_R242 0xf2 /* Reserved */ + +static int pb0100_probe(struct sd *sd); +static int pb0100_start(struct sd *sd); +static int pb0100_init(struct sd *sd); +static int pb0100_init_controls(struct sd *sd); +static int pb0100_stop(struct sd *sd); +static int pb0100_dump(struct sd *sd); + +/* V4L2 controls supported by the driver */ +static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_pb0100 = { + .name = "PB-0100", + .i2c_flush = 1, + .i2c_addr = 0xba, + .i2c_len = 2, + + .min_packet_size = { 635, 847 }, + .max_packet_size = { 847, 923 }, + + .init = pb0100_init, + .init_controls = pb0100_init_controls, + .probe = pb0100_probe, + .start = pb0100_start, + .stop = pb0100_stop, + .dump = pb0100_dump, +}; + +#endif diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h new file mode 100644 index 000000000000..3a498c2495c6 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_SENSOR_H_ +#define STV06XX_SENSOR_H_ + +#include "stv06xx.h" + +#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020) + +extern const struct stv06xx_sensor stv06xx_sensor_vv6410; +extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; +extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; +extern const struct stv06xx_sensor stv06xx_sensor_pb0100; +extern const struct stv06xx_sensor stv06xx_sensor_st6422; + +struct stv06xx_sensor { + /* Defines the name of a sensor */ + char name[32]; + + /* Sensor i2c address */ + u8 i2c_addr; + + /* Flush value*/ + u8 i2c_flush; + + /* length of an i2c word */ + u8 i2c_len; + + /* Isoc packet size (per mode) */ + int min_packet_size[4]; + int max_packet_size[4]; + + /* Probes if the sensor is connected */ + int (*probe)(struct sd *sd); + + /* Performs a initialization sequence */ + int (*init)(struct sd *sd); + + /* Initializes the controls */ + int (*init_controls)(struct sd *sd); + + /* Reads a sensor register */ + int (*read_sensor)(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + + /* Writes to a sensor register */ + int (*write_sensor)(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + + /* Instructs the sensor to start streaming */ + int (*start)(struct sd *sd); + + /* Instructs the sensor to stop streaming */ + int (*stop)(struct sd *sd); + + /* Instructs the sensor to dump all its contents */ + int (*dump)(struct sd *sd); +}; + +#endif diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c new file mode 100644 index 000000000000..8a57990dfe0f --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c @@ -0,0 +1,285 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "stv06xx_st6422.h" + +static struct v4l2_pix_format st6422_mode[] = { + /* Note we actually get 124 lines of data, of which we skip the 4st + 4 as they are garbage */ + { + 162, + 120, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 162 * 120, + .bytesperline = 162, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + /* Note we actually get 248 lines of data, of which we skip the 4st + 4 as they are garbage, and we tell the app it only gets the + first 240 of the 244 lines it actually gets, so that it ignores + the last 4. */ + { + 324, + 240, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 324 * 244, + .bytesperline = 324, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, +}; + +/* V4L2 controls supported by the driver */ +static int setbrightness(struct sd *sd, s32 val); +static int setcontrast(struct sd *sd, s32 val); +static int setgain(struct sd *sd, u8 gain); +static int setexposure(struct sd *sd, s16 expo); + +static int st6422_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = setbrightness(sd, ctrl->val); + break; + case V4L2_CID_CONTRAST: + err = setcontrast(sd, ctrl->val); + break; + case V4L2_CID_GAIN: + err = setgain(sd, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = setexposure(sd, ctrl->val); + break; + } + + /* commit settings */ + if (err >= 0) + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + sd->gspca_dev.usb_err = err; + return err; +} + +static const struct v4l2_ctrl_ops st6422_ctrl_ops = { + .s_ctrl = st6422_s_ctrl, +}; + +static int st6422_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 31, 1, 3); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 11); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 1023, 1, 256); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 64); + + return hdl->error; +} + +static int st6422_probe(struct sd *sd) +{ + if (sd->bridge != BRIDGE_ST6422) + return -ENODEV; + + pr_info("st6422 sensor detected\n"); + + sd->gspca_dev.cam.cam_mode = st6422_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); + return 0; +} + +static int st6422_init(struct sd *sd) +{ + int err = 0, i; + + const u16 st6422_bridge_init[][2] = { + { STV_ISO_ENABLE, 0x00 }, /* disable capture */ + { 0x1436, 0x00 }, + { 0x1432, 0x03 }, /* 0x00-0x1F brightness */ + { 0x143a, 0xf9 }, /* 0x00-0x0F contrast */ + { 0x0509, 0x38 }, /* R */ + { 0x050a, 0x38 }, /* G */ + { 0x050b, 0x38 }, /* B */ + { 0x050c, 0x2a }, + { 0x050d, 0x01 }, + + + { 0x1431, 0x00 }, /* 0x00-0x07 ??? */ + { 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */ + { 0x1438, 0x18 }, /* 640x480 */ +/* 18 bayes */ +/* 10 compressed? */ + + { 0x1439, 0x00 }, +/* anti-noise? 0xa2 gives a perfect image */ + + { 0x143b, 0x05 }, + { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */ + + +/* shutter time 0x0000-0x03FF */ +/* low value give good picures on moving objects (but requires much light) */ +/* high value gives good picures in darkness (but tends to be overexposed) */ + { 0x143e, 0x01 }, + { 0x143d, 0x00 }, + + { 0x1442, 0xe2 }, +/* write: 1x1x xxxx */ +/* read: 1x1x xxxx */ +/* bit 5 == button pressed and hold if 0 */ +/* write 0xe2,0xea */ + +/* 0x144a */ +/* 0x00 init */ +/* bit 7 == button has been pressed, but not handled */ + +/* interrupt */ +/* if(urb->iso_frame_desc[i].status == 0x80) { */ +/* if(urb->iso_frame_desc[i].status == 0x88) { */ + + { 0x1500, 0xd0 }, + { 0x1500, 0xd0 }, + { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */ + + { 0x1501, 0xaf }, +/* high val-> light area gets darker */ +/* low val -> light area gets lighter */ + { 0x1502, 0xc2 }, +/* high val-> light area gets darker */ +/* low val -> light area gets lighter */ + { 0x1503, 0x45 }, +/* high val-> light area gets darker */ +/* low val -> light area gets lighter */ + { 0x1505, 0x02 }, +/* 2 : 324x248 80352 bytes */ +/* 7 : 248x162 40176 bytes */ +/* c+f: 162*124 20088 bytes */ + + { 0x150e, 0x8e }, + { 0x150f, 0x37 }, + { 0x15c0, 0x00 }, + { 0x15c3, 0x08 }, /* 0x04/0x14 ... test pictures ??? */ + + + { 0x143f, 0x01 }, /* commit settings */ + + }; + + for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) { + err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0], + st6422_bridge_init[i][1]); + } + + return err; +} + +static int setbrightness(struct sd *sd, s32 val) +{ + /* val goes from 0 -> 31 */ + return stv06xx_write_bridge(sd, 0x1432, val); +} + +static int setcontrast(struct sd *sd, s32 val) +{ + /* Val goes from 0 -> 15 */ + return stv06xx_write_bridge(sd, 0x143a, val | 0xf0); +} + +static int setgain(struct sd *sd, u8 gain) +{ + int err; + + /* Set red, green, blue, gain */ + err = stv06xx_write_bridge(sd, 0x0509, gain); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050a, gain); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050b, gain); + if (err < 0) + return err; + + /* 2 mystery writes */ + err = stv06xx_write_bridge(sd, 0x050c, 0x2a); + if (err < 0) + return err; + + return stv06xx_write_bridge(sd, 0x050d, 0x01); +} + +static int setexposure(struct sd *sd, s16 expo) +{ + int err; + + err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff); + if (err < 0) + return err; + + return stv06xx_write_bridge(sd, 0x143e, expo >> 8); +} + +static int st6422_start(struct sd *sd) +{ + int err; + struct cam *cam = &sd->gspca_dev.cam; + + if (cam->cam_mode[sd->gspca_dev.curr_mode].priv) + err = stv06xx_write_bridge(sd, 0x1505, 0x0f); + else + err = stv06xx_write_bridge(sd, 0x1505, 0x02); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "Halting stream"); + + return 0; +} diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h new file mode 100644 index 000000000000..8f20fbf30f33 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h @@ -0,0 +1,52 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef STV06XX_ST6422_H_ +#define STV06XX_ST6422_H_ + +#include "stv06xx_sensor.h" + +static int st6422_probe(struct sd *sd); +static int st6422_start(struct sd *sd); +static int st6422_init(struct sd *sd); +static int st6422_init_controls(struct sd *sd); +static int st6422_stop(struct sd *sd); + +const struct stv06xx_sensor stv06xx_sensor_st6422 = { + .name = "ST6422", + /* No known way to lower framerate in case of less bandwidth */ + .min_packet_size = { 300, 847 }, + .max_packet_size = { 300, 847 }, + .init = st6422_init, + .init_controls = st6422_init_controls, + .probe = st6422_probe, + .start = st6422_start, + .stop = st6422_stop, +}; + +#endif diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c new file mode 100644 index 000000000000..748e1421d6d8 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "stv06xx_vv6410.h" + +static struct v4l2_pix_format vv6410_mode[] = { + { + 356, + 292, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 356 * 292, + .bytesperline = 356, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + err = vv6410_set_hflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_VFLIP: + err = vv6410_set_vflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + err = vv6410_set_analog_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = vv6410_set_exposure(gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops vv6410_ctrl_ops = { + .s_ctrl = vv6410_s_ctrl, +}; + +static int vv6410_probe(struct sd *sd) +{ + u16 data; + int err; + + err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); + if (err < 0) + return -ENODEV; + + if (data != 0x19) + return -ENODEV; + + pr_info("vv6410 sensor detected\n"); + + sd->gspca_dev.cam.cam_mode = vv6410_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); + return 0; +} + +static int vv6410_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_GAIN, 0, 15, 1, 10); + return hdl->error; +} + +static int vv6410_init(struct sd *sd) +{ + int err = 0, i; + + for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) + stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); + + if (err < 0) + return err; + + err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, + ARRAY_SIZE(vv6410_sensor_init)); + return (err < 0) ? err : 0; +} + +static int vv6410_start(struct sd *sd) +{ + int err; + struct cam *cam = &sd->gspca_dev.cam; + u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + + if (priv & VV6410_SUBSAMPLE) { + PDEBUG(D_CONF, "Enabling subsampling"); + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); + stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); + + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); + } else { + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); + stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00); + + } + + /* Turn on LED */ + err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON); + if (err < 0) + return err; + + err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0); + if (err < 0) + return err; + + PDEBUG(D_STREAM, "Starting stream"); + + return 0; +} + +static int vv6410_stop(struct sd *sd) +{ + int err; + + /* Turn off LED */ + err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF); + if (err < 0) + return err; + + err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE); + if (err < 0) + return err; + + PDEBUG(D_STREAM, "Halting stream"); + + return (err < 0) ? err : 0; +} + +static int vv6410_dump(struct sd *sd) +{ + u8 i; + int err = 0; + + pr_info("Dumping all vv6410 sensor registers\n"); + for (i = 0; i < 0xff && !err; i++) { + u16 data; + err = stv06xx_read_sensor(sd, i, &data); + pr_info("Register 0x%x contained 0x%x\n", i, data); + } + return (err < 0) ? err : 0; +} + +static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u16 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); + if (err < 0) + return err; + + if (val) + i2c_data |= VV6410_HFLIP; + else + i2c_data &= ~VV6410_HFLIP; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); + + return (err < 0) ? err : 0; +} + +static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u16 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); + if (err < 0) + return err; + + if (val) + i2c_data |= VV6410_VFLIP; + else + i2c_data &= ~VV6410_VFLIP; + + PDEBUG(D_V4L2, "Set vertical flip to %d", val); + err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); + + return (err < 0) ? err : 0; +} + +static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_V4L2, "Set analog gain to %d", val); + err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); + + return (err < 0) ? err : 0; +} + +static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + unsigned int fine, coarse; + + val = (val * val >> 14) + val / 4; + + fine = val % VV6410_CIF_LINELENGTH; + coarse = min(512, val / VV6410_CIF_LINELENGTH); + + PDEBUG(D_V4L2, "Set coarse exposure to %d, fine expsure to %d", + coarse, fine); + + err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8); + if (err < 0) + goto out; + + err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff); + if (err < 0) + goto out; + + err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8); + if (err < 0) + goto out; + + err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff); + +out: + return err; +} diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h new file mode 100644 index 000000000000..53e67b40ca05 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_VV6410_H_ +#define STV06XX_VV6410_H_ + +#include "stv06xx_sensor.h" + +#define VV6410_COLS 416 +#define VV6410_ROWS 320 + +/* Status registers */ +/* Chip identification number including revision indicator */ +#define VV6410_DEVICEH 0x00 +#define VV6410_DEVICEL 0x01 + +/* User can determine whether timed I2C data + has been consumed by interrogating flag states */ +#define VV6410_STATUS0 0x02 + +/* Current line counter value */ +#define VV6410_LINECOUNTH 0x03 +#define VV6410_LINECOUNTL 0x04 + +/* End x coordinate of image size */ +#define VV6410_XENDH 0x05 +#define VV6410_XENDL 0x06 + +/* End y coordinate of image size */ +#define VV6410_YENDH 0x07 +#define VV6410_YENDL 0x08 + +/* This is the average pixel value returned from the + dark line offset cancellation algorithm */ +#define VV6410_DARKAVGH 0x09 +#define VV6410_DARKAVGL 0x0a + +/* This is the average pixel value returned from the + black line offset cancellation algorithm */ +#define VV6410_BLACKAVGH 0x0b +#define VV6410_BLACKAVGL 0x0c + +/* Flags to indicate whether the x or y image coordinates have been clipped */ +#define VV6410_STATUS1 0x0d + +/* Setup registers */ + +/* Low-power/sleep modes & video timing */ +#define VV6410_SETUP0 0x10 + +/* Various parameters */ +#define VV6410_SETUP1 0x11 + +/* Contains pixel counter reset value used by external sync */ +#define VV6410_SYNCVALUE 0x12 + +/* Frame grabbing modes (FST, LST and QCK) */ +#define VV6410_FGMODES 0x14 + +/* FST and QCK mapping modes. */ +#define VV6410_PINMAPPING 0x15 + +/* Data resolution */ +#define VV6410_DATAFORMAT 0x16 + +/* Output coding formats */ +#define VV6410_OPFORMAT 0x17 + +/* Various mode select bits */ +#define VV6410_MODESELECT 0x18 + +/* Exposure registers */ +/* Fine exposure. */ +#define VV6410_FINEH 0x20 +#define VV6410_FINEL 0x21 + +/* Coarse exposure */ +#define VV6410_COARSEH 0x22 +#define VV6410_COARSEL 0x23 + +/* Analog gain setting */ +#define VV6410_ANALOGGAIN 0x24 + +/* Clock division */ +#define VV6410_CLKDIV 0x25 + +/* Dark line offset cancellation value */ +#define VV6410_DARKOFFSETH 0x2c +#define VV6410_DARKOFFSETL 0x2d + +/* Dark line offset cancellation enable */ +#define VV6410_DARKOFFSETSETUP 0x2e + +/* Video timing registers */ +/* Line Length (Pixel Clocks) */ +#define VV6410_LINELENGTHH 0x52 +#define VV6410_LINELENGTHL 0x53 + +/* X-co-ordinate of top left corner of region of interest (x-offset) */ +#define VV6410_XOFFSETH 0x57 +#define VV6410_XOFFSETL 0x58 + +/* Y-coordinate of top left corner of region of interest (y-offset) */ +#define VV6410_YOFFSETH 0x59 +#define VV6410_YOFFSETL 0x5a + +/* Field length (Lines) */ +#define VV6410_FIELDLENGTHH 0x61 +#define VV6410_FIELDLENGTHL 0x62 + +/* System registers */ +/* Black offset cancellation default value */ +#define VV6410_BLACKOFFSETH 0x70 +#define VV6410_BLACKOFFSETL 0x71 + +/* Black offset cancellation setup */ +#define VV6410_BLACKOFFSETSETUP 0x72 + +/* Analog Control Register 0 */ +#define VV6410_CR0 0x75 + +/* Analog Control Register 1 */ +#define VV6410_CR1 0x76 + +/* ADC Setup Register */ +#define VV6410_AS0 0x77 + +/* Analog Test Register */ +#define VV6410_AT0 0x78 + +/* Audio Amplifier Setup Register */ +#define VV6410_AT1 0x79 + +#define VV6410_HFLIP (1 << 3) +#define VV6410_VFLIP (1 << 4) + +#define VV6410_LOW_POWER_MODE (1 << 0) +#define VV6410_SOFT_RESET (1 << 2) +#define VV6410_PAL_25_FPS (0 << 3) + +#define VV6410_CLK_DIV_2 (1 << 1) + +#define VV6410_FINE_EXPOSURE 320 +#define VV6410_COARSE_EXPOSURE 192 +#define VV6410_DEFAULT_GAIN 5 + +#define VV6410_SUBSAMPLE 0x01 +#define VV6410_CROP_TO_QVGA 0x02 + +#define VV6410_CIF_LINELENGTH 415 + +static int vv6410_probe(struct sd *sd); +static int vv6410_start(struct sd *sd); +static int vv6410_init(struct sd *sd); +static int vv6410_init_controls(struct sd *sd); +static int vv6410_stop(struct sd *sd); +static int vv6410_dump(struct sd *sd); + +/* V4L2 controls supported by the driver */ +static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); +static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_vv6410 = { + .name = "ST VV6410", + .i2c_flush = 5, + .i2c_addr = 0x20, + .i2c_len = 1, + /* FIXME (see if we can lower packet_size-s, needs testing, and also + adjusting framerate when the bandwidth gets lower) */ + .min_packet_size = { 1023 }, + .max_packet_size = { 1023 }, + .init = vv6410_init, + .init_controls = vv6410_init_controls, + .probe = vv6410_probe, + .start = vv6410_start, + .stop = vv6410_stop, + .dump = vv6410_dump, +}; + +/* If NULL, only single value to write, stored in len */ +struct stv_init { + u16 addr; + u8 data; +}; + +static const struct stv_init stv_bridge_init[] = { + /* This reg is written twice. Some kind of reset? */ + {STV_RESET, 0x80}, + {STV_RESET, 0x00}, + {STV_SCAN_RATE, 0x00}, + {STV_I2C_FLUSH, 0x04}, + {STV_REG00, 0x0b}, + {STV_REG01, 0xa7}, + {STV_REG02, 0xb7}, + {STV_REG03, 0x00}, + {STV_REG04, 0x00}, + {0x1536, 0x02}, + {0x1537, 0x00}, + {0x1538, 0x60}, + {0x1539, 0x01}, + {0x153a, 0x20}, + {0x153b, 0x01}, +}; + +static const u8 vv6410_sensor_init[][2] = { + /* Setup registers */ + {VV6410_SETUP0, VV6410_SOFT_RESET}, + {VV6410_SETUP0, VV6410_LOW_POWER_MODE}, + /* Use shuffled read-out mode */ + {VV6410_SETUP1, BIT(6)}, + /* All modes to 1, FST, Fast QCK, Free running QCK, Free running LST, FST will qualify visible pixels */ + {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)}, + {VV6410_PINMAPPING, 0x00}, + /* Pre-clock generator divide off */ + {VV6410_DATAFORMAT, BIT(7) | BIT(0)}, + + {VV6410_CLKDIV, VV6410_CLK_DIV_2}, + + /* System registers */ + /* Enable voltage doubler */ + {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, + {VV6410_AT0, 0x00}, + /* Power up audio, differential */ + {VV6410_AT1, BIT(4) | BIT(0)}, +}; + +#endif diff --git a/drivers/media/usb/gspca/sunplus.c b/drivers/media/usb/gspca/sunplus.c new file mode 100644 index 000000000000..9ccfcb1c6479 --- /dev/null +++ b/drivers/media/usb/gspca/sunplus.c @@ -0,0 +1,1085 @@ +/* + * Sunplus spca504(abc) spca533 spca536 library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "sunplus" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define QUALITY 85 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + bool autogain; + + u8 bridge; +#define BRIDGE_SPCA504 0 +#define BRIDGE_SPCA504B 1 +#define BRIDGE_SPCA504C 2 +#define BRIDGE_SPCA533 3 +#define BRIDGE_SPCA536 4 + u8 subtype; +#define AiptekMiniPenCam13 1 +#define LogitechClickSmart420 2 +#define LogitechClickSmart820 3 +#define MegapixV4 4 +#define MegaImageVI 5 + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +static const struct v4l2_pix_format custom_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 464, + .sizeimage = 464 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +static const struct v4l2_pix_format vga_mode2[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 4}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +#define SPCA50X_OFFSET_DATA 10 +#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3 +#define SPCA504_PCCAM600_OFFSET_COMPRESS 4 +#define SPCA504_PCCAM600_OFFSET_MODE 5 +#define SPCA504_PCCAM600_OFFSET_DATA 14 + /* Frame packet header offsets for the spca533 */ +#define SPCA533_OFFSET_DATA 16 +#define SPCA533_OFFSET_FRAMSEQ 15 +/* Frame packet header offsets for the spca536 */ +#define SPCA536_OFFSET_DATA 4 +#define SPCA536_OFFSET_FRAMSEQ 1 + +struct cmd { + u8 req; + u16 val; + u16 idx; +}; + +/* Initialisation data for the Creative PC-CAM 600 */ +static const struct cmd spca504_pccam600_init_data[] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0154, 0x0008}, + {0x30, 0x0004, 0x0006}, + {0x30, 0x0258, 0x0009}, + {0x30, 0x0004, 0x0000}, + {0x30, 0x0093, 0x0004}, + {0x30, 0x0066, 0x0005}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, +}; + +/* Creative PC-CAM 600 specific open data, sent before using the + * generic initialisation data from spca504_open_data. + */ +static const struct cmd spca504_pccam600_open_data[] = { + {0x00, 0x0001, 0x2501}, + {0x20, 0x0500, 0x0001}, /* snapshot mode */ + {0x00, 0x0003, 0x2880}, + {0x00, 0x0001, 0x2881}, +}; + +/* Initialisation data for the logitech clicksmart 420 */ +static const struct cmd spca504A_clicksmart420_init_data[] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0004, 0x000a}, + {0xb0, 0x0001, 0x0000}, + + {0xa1, 0x0080, 0x0001}, + {0x30, 0x0049, 0x0000}, + {0x30, 0x0060, 0x0005}, + {0x0c, 0x0004, 0x0000}, + {0x00, 0x0000, 0x0000}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, +}; + +/* clicksmart 420 open data ? */ +static const struct cmd spca504A_clicksmart420_open_data[] = { + {0x00, 0x0001, 0x2501}, + {0x20, 0x0502, 0x0000}, + {0x06, 0x0000, 0x0000}, + {0x00, 0x0004, 0x2880}, + {0x00, 0x0001, 0x2881}, + + {0xa0, 0x0000, 0x0503}, +}; + +static const u8 qtable_creative_pccam[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +/* FIXME: This Q-table is identical to the Creative PC-CAM one, + * except for one byte. Possibly a typo? + * NWG: 18/05/2003. + */ +static const u8 qtable_spca504_default[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e, + }, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +/* read bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + u8 req, + u16 index, + u16 len) +{ + int ret; + +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + pr_err("reg_r: buffer overflow\n"); + return; + } +#endif + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + len ? gspca_dev->usb_buf : NULL, len, + 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* write one byte */ +static void reg_w_1(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index, + u16 byte) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + gspca_dev->usb_buf[0] = byte; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_w_1 err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* write req / index / value */ +static void reg_w_riv(struct gspca_dev *gspca_dev, + u8 req, u16 index, u16 value) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + if (ret < 0) { + pr_err("reg_w_riv err %d\n", ret); + gspca_dev->usb_err = ret; + return; + } + PDEBUG(D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x", + req, index, value); +} + +static void write_vector(struct gspca_dev *gspca_dev, + const struct cmd *data, int ncmds) +{ + while (--ncmds >= 0) { + reg_w_riv(gspca_dev, data->req, data->idx, data->val); + data++; + } +} + +static void setup_qtable(struct gspca_dev *gspca_dev, + const u8 qtable[2][64]) +{ + int i; + + /* loop over y components */ + for (i = 0; i < 64; i++) + reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]); + + /* loop over c components */ + for (i = 0; i < 64; i++) + reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]); +} + +static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, + u8 req, u16 idx, u16 val) +{ + reg_w_riv(gspca_dev, req, idx, val); + reg_r(gspca_dev, 0x01, 0x0001, 1); + PDEBUG(D_FRAM, "before wait 0x%04x", gspca_dev->usb_buf[0]); + reg_w_riv(gspca_dev, req, idx, val); + + msleep(200); + reg_r(gspca_dev, 0x01, 0x0001, 1); + PDEBUG(D_FRAM, "after wait 0x%04x", gspca_dev->usb_buf[0]); +} + +#ifdef GSPCA_DEBUG +static void spca504_read_info(struct gspca_dev *gspca_dev) +{ + int i; + u8 info[6]; + + for (i = 0; i < 6; i++) { + reg_r(gspca_dev, 0, i, 1); + info[i] = gspca_dev->usb_buf[0]; + } + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); +} +#endif + +static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, + u8 req, + u16 idx, u16 val, u8 endcode, u8 count) +{ + u16 status; + + reg_w_riv(gspca_dev, req, idx, val); + reg_r(gspca_dev, 0x01, 0x0001, 1); + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_FRAM, "Status 0x%02x Need 0x%02x", + gspca_dev->usb_buf[0], endcode); + if (!count) + return; + count = 200; + while (--count > 0) { + msleep(10); + /* gsmart mini2 write a each wait setting 1 ms is enough */ +/* reg_w_riv(gspca_dev, req, idx, val); */ + reg_r(gspca_dev, 0x01, 0x0001, 1); + status = gspca_dev->usb_buf[0]; + if (status == endcode) { + PDEBUG(D_FRAM, "status 0x%04x after wait %d", + status, 200 - count); + break; + } + } +} + +static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev) +{ + int count = 10; + + while (--count > 0) { + reg_r(gspca_dev, 0x21, 0, 1); + if ((gspca_dev->usb_buf[0] & 0x01) == 0) + break; + msleep(10); + } +} + +static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) +{ + int count = 50; + + while (--count > 0) { + reg_r(gspca_dev, 0x21, 1, 1); + if (gspca_dev->usb_buf[0] != 0) { + reg_w_1(gspca_dev, 0x21, 0, 1, 0); + reg_r(gspca_dev, 0x21, 1, 1); + spca504B_PollingDataReady(gspca_dev); + break; + } + msleep(10); + } +} + +#ifdef GSPCA_DEBUG +static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) +{ + u8 *data; + + data = gspca_dev->usb_buf; + reg_r(gspca_dev, 0x20, 0, 5); + PDEBUG(D_STREAM, "FirmWare: %d %d %d %d %d", + data[0], data[1], data[2], data[3], data[4]); + reg_r(gspca_dev, 0x23, 0, 64); + reg_r(gspca_dev, 0x23, 1, 64); +} +#endif + +static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 Size; + + Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + switch (sd->bridge) { + case BRIDGE_SPCA533: + reg_w_riv(gspca_dev, 0x31, 0, 0); + spca504B_WaitCmdStatus(gspca_dev); + spca504B_PollingDataReady(gspca_dev); +#ifdef GSPCA_DEBUG + spca50x_GetFirmware(gspca_dev); +#endif + reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */ + reg_r(gspca_dev, 0x24, 8, 1); + + reg_w_1(gspca_dev, 0x25, 0, 4, Size); + reg_r(gspca_dev, 0x25, 4, 1); /* size */ + spca504B_PollingDataReady(gspca_dev); + + /* Init the cam width height with some values get on init ? */ + reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00); + spca504B_WaitCmdStatus(gspca_dev); + spca504B_PollingDataReady(gspca_dev); + break; + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA536: */ + reg_w_1(gspca_dev, 0x25, 0, 4, Size); + reg_r(gspca_dev, 0x25, 4, 1); /* size */ + reg_w_1(gspca_dev, 0x27, 0, 0, 6); + reg_r(gspca_dev, 0x27, 0, 1); /* type */ + spca504B_PollingDataReady(gspca_dev); + break; + case BRIDGE_SPCA504: + Size += 3; + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, + 0x08, Size, 0, + 0x80 | (Size & 0x0f), 1); + spca504A_acknowledged_command(gspca_dev, + 1, 3, 0, 0x9f, 0); + } else { + spca504_acknowledged_command(gspca_dev, 0x08, Size, 0); + } + break; + case BRIDGE_SPCA504C: + /* capture mode */ + reg_w_riv(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00); + reg_w_riv(gspca_dev, 0x20, 0x01, 0x0500 | (Size & 0x0f)); + break; + } +} + +static void spca504_wait_status(struct gspca_dev *gspca_dev) +{ + int cnt; + + cnt = 256; + while (--cnt > 0) { + /* With this we get the status, when return 0 it's all ok */ + reg_r(gspca_dev, 0x06, 0x00, 1); + if (gspca_dev->usb_buf[0] == 0) + return; + msleep(10); + } +} + +static void spca504B_setQtable(struct gspca_dev *gspca_dev) +{ + reg_w_1(gspca_dev, 0x26, 0, 0, 3); + reg_r(gspca_dev, 0x26, 0, 1); + spca504B_PollingDataReady(gspca_dev); +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7; + reg_w_riv(gspca_dev, 0x00, reg, val); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8; + reg_w_riv(gspca_dev, 0x00, reg, val); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae; + reg_w_riv(gspca_dev, 0x00, reg, val); +} + +static void init_ctl_reg(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int pollreg = 1; + + switch (sd->bridge) { + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + pollreg = 0; + /* fall thru */ + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ + reg_w_riv(gspca_dev, 0, 0x21ad, 0x00); /* hue */ + reg_w_riv(gspca_dev, 0, 0x21ac, 0x01); /* sat/hue */ + reg_w_riv(gspca_dev, 0, 0x21a3, 0x00); /* gamma */ + break; + case BRIDGE_SPCA536: + reg_w_riv(gspca_dev, 0, 0x20f5, 0x40); + reg_w_riv(gspca_dev, 0, 0x20f4, 0x01); + reg_w_riv(gspca_dev, 0, 0x2089, 0x00); + break; + } + if (pollreg) + spca504B_PollingDataReady(gspca_dev); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + sd->bridge = id->driver_info >> 8; + sd->subtype = id->driver_info; + + if (sd->subtype == AiptekMiniPenCam13) { + + /* try to get the firmware as some cam answer 2.0.1.2.2 + * and should be a spca504b then overwrite that setting */ + reg_r(gspca_dev, 0x20, 0, 1); + switch (gspca_dev->usb_buf[0]) { + case 1: + break; /* (right bridge/subtype) */ + case 2: + sd->bridge = BRIDGE_SPCA504B; + sd->subtype = 0; + break; + default: + return -ENODEV; + } + } + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA536: */ + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + break; + case BRIDGE_SPCA533: + cam->cam_mode = custom_mode; + if (sd->subtype == MegaImageVI) /* 320x240 only */ + cam->nmodes = ARRAY_SIZE(custom_mode) - 1; + else + cam->nmodes = ARRAY_SIZE(custom_mode); + break; + case BRIDGE_SPCA504C: + cam->cam_mode = vga_mode2; + cam->nmodes = ARRAY_SIZE(vga_mode2); + break; + } + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + case BRIDGE_SPCA504B: + reg_w_riv(gspca_dev, 0x1d, 0x00, 0); + reg_w_riv(gspca_dev, 0x00, 0x2306, 0x01); + reg_w_riv(gspca_dev, 0x00, 0x0d04, 0x00); + reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00); + reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13); + reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00); + /* fall thru */ + case BRIDGE_SPCA533: + spca504B_PollingDataReady(gspca_dev); +#ifdef GSPCA_DEBUG + spca50x_GetFirmware(gspca_dev); +#endif + break; + case BRIDGE_SPCA536: +#ifdef GSPCA_DEBUG + spca50x_GetFirmware(gspca_dev); +#endif + reg_r(gspca_dev, 0x00, 0x5002, 1); + reg_w_1(gspca_dev, 0x24, 0, 0, 0); + reg_r(gspca_dev, 0x24, 0, 1); + spca504B_PollingDataReady(gspca_dev); + reg_w_riv(gspca_dev, 0x34, 0, 0); + spca504B_WaitCmdStatus(gspca_dev); + break; + case BRIDGE_SPCA504C: /* pccam600 */ + PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)"); + reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000); + reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001); /* reset */ + spca504_wait_status(gspca_dev); + if (sd->subtype == LogitechClickSmart420) + write_vector(gspca_dev, + spca504A_clicksmart420_open_data, + ARRAY_SIZE(spca504A_clicksmart420_open_data)); + else + write_vector(gspca_dev, spca504_pccam600_open_data, + ARRAY_SIZE(spca504_pccam600_open_data)); + setup_qtable(gspca_dev, qtable_creative_pccam); + break; + default: +/* case BRIDGE_SPCA504: */ + PDEBUG(D_STREAM, "Opening SPCA504"); + if (sd->subtype == AiptekMiniPenCam13) { +#ifdef GSPCA_DEBUG + spca504_read_info(gspca_dev); +#endif + + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequential need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + /******************************/ + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); +/* reg_write (dev, 0, 0x2000, 0); */ +/* reg_write (dev, 0, 0x2883, 1); */ +/* spca504A_acknowledged_command (gspca_dev, 0x08, + 6, 0, 0x86, 1); */ +/* spca504A_acknowledged_command (gspca_dev, 0x24, + 0, 0, 0x9D, 1); */ + reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05); + /* L92 sno1t.txt */ + reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0, 0xff, 0); + } + /* setup qtable */ + reg_w_riv(gspca_dev, 0, 0x2000, 0); + reg_w_riv(gspca_dev, 0, 0x2883, 1); + setup_qtable(gspca_dev, qtable_spca504_default); + break; + } + return gspca_dev->usb_err; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int enable; + + /* create the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, QUALITY); + + if (sd->bridge == BRIDGE_SPCA504B) + spca504B_setQtable(gspca_dev); + spca504B_SetSizeType(gspca_dev); + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA536: */ + switch (sd->subtype) { + case MegapixV4: + case LogitechClickSmart820: + case MegaImageVI: + reg_w_riv(gspca_dev, 0xf0, 0, 0); + spca504B_WaitCmdStatus(gspca_dev); + reg_r(gspca_dev, 0xf0, 4, 0); + spca504B_WaitCmdStatus(gspca_dev); + break; + default: + reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00); + spca504B_WaitCmdStatus(gspca_dev); + spca504B_PollingDataReady(gspca_dev); + break; + } + break; + case BRIDGE_SPCA504: + if (sd->subtype == AiptekMiniPenCam13) { +#ifdef GSPCA_DEBUG + spca504_read_info(gspca_dev); +#endif + + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequential need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); +#ifdef GSPCA_DEBUG + spca504_read_info(gspca_dev); +#endif + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); + spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); + } + spca504B_SetSizeType(gspca_dev); + reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05); + /* L92 sno1t.txt */ + reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05); + break; + case BRIDGE_SPCA504C: + if (sd->subtype == LogitechClickSmart420) { + write_vector(gspca_dev, + spca504A_clicksmart420_init_data, + ARRAY_SIZE(spca504A_clicksmart420_init_data)); + } else { + write_vector(gspca_dev, spca504_pccam600_init_data, + ARRAY_SIZE(spca504_pccam600_init_data)); + } + enable = (sd->autogain ? 0x04 : 0x01); + reg_w_riv(gspca_dev, 0x0c, 0x0000, enable); + /* auto exposure */ + reg_w_riv(gspca_dev, 0xb0, 0x0000, enable); + /* auto whiteness */ + + /* set default exposure compensation and whiteness balance */ + reg_w_riv(gspca_dev, 0x30, 0x0001, 800); /* ~ 20 fps */ + reg_w_riv(gspca_dev, 0x30, 0x0002, 1600); + spca504B_SetSizeType(gspca_dev); + break; + } + init_ctl_reg(gspca_dev); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA536: */ +/* case BRIDGE_SPCA504B: */ + reg_w_riv(gspca_dev, 0x31, 0, 0); + spca504B_WaitCmdStatus(gspca_dev); + spca504B_PollingDataReady(gspca_dev); + break; + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + reg_w_riv(gspca_dev, 0x00, 0x2000, 0x0000); + + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ +/* spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 0x00, 0x00, 0x9d, 1); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0x00, 0xff, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); + reg_w_riv(gspca_dev, 0x01, 0x000f, 0x0000); + } + break; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, sof = 0; + static u8 ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + switch (sd->bridge) { + case BRIDGE_SPCA533: + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + sof = 1; + data += SPCA533_OFFSET_DATA; + len -= SPCA533_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + break; + case BRIDGE_SPCA536: + if (data[0] == 0xff) { + sof = 1; + data += SPCA536_OFFSET_DATA; + len -= SPCA536_OFFSET_DATA; + } else { + data += 2; + len -= 2; + } + break; + default: +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504B: */ + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + case BRIDGE_SPCA504C: + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA504_PCCAM600_OFFSET_DATA; + len -= SPCA504_PCCAM600_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + } + if (sof) { /* start of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); + + /* put the JPEG header in the new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + } + + /* add 0x00 after 0xff */ + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + i++; + } while (i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + sd->autogain = ctrl->val; + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 0x20); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 0x1a); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define BS(bridge, subtype) \ + .driver_info = (BRIDGE_ ## bridge << 8) \ + | (subtype) +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)}, + {USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)}, + {USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)}, + {USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)}, + {USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)}, + {USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)}, + {USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)}, + {USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)}, + {USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)}, + {USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)}, + {USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)}, + {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, + {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, + {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)}, + {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, + {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)}, + {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, + {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)}, + {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)}, + {USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)}, + {USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)}, + {USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)}, + {USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)}, + {USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)}, + {USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)}, + {USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)}, + {USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)}, + {USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)}, + {USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)}, + {USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)}, + {USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c new file mode 100644 index 000000000000..8bc6c3ceec2c --- /dev/null +++ b/drivers/media/usb/gspca/t613.c @@ -0,0 +1,1054 @@ +/* + * T613 subdriver + * + * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *Notes: * t613 + tas5130A + * * Focus to light do not balance well as in win. + * Quality in win is not good, but its kinda better. + * * Fix some "extraneous bytes", most of apps will show the image anyway + * * Gamma table, is there, but its really doing something? + * * 7~8 Fps, its ok, max on win its 10. + * Costantino Leandro + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "t613" + +#include +#include +#include "gspca.h" + +MODULE_AUTHOR("Leandro Costantino "); +MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); +MODULE_LICENSE("GPL"); + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_ctrl *freq; + struct { /* awb / color gains control cluster */ + struct v4l2_ctrl *awb; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *red_balance; + struct v4l2_ctrl *blue_balance; + }; + + u8 sensor; + u8 button_pressed; +}; +enum sensors { + SENSOR_OM6802, + SENSOR_OTHER, + SENSOR_TAS5130A, + SENSOR_LT168G, /* must verify if this is the actual model */ +}; + +static const struct v4l2_pix_format vga_mode_t16[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 4}, +#if 0 /* HDG: broken with my test cam, so lets disable it */ + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, +#endif + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, +#if 0 /* HDG: broken with my test cam, so lets disable it */ + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +#endif + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* sensor specific data */ +struct additional_sensor_data { + const u8 n3[6]; + const u8 *n4, n4sz; + const u8 reg80, reg8e; + const u8 nset8[6]; + const u8 data1[10]; + const u8 data2[9]; + const u8 data3[9]; + const u8 data5[6]; + const u8 stream[4]; +}; + +static const u8 n4_om6802[] = { + 0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, + 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, + 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, + 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, + 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, + 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, + 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, + 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, + 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46 +}; +static const u8 n4_other[] = { + 0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69, + 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68, + 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8, + 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8, + 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56, + 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5, + 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0, + 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00 +}; +static const u8 n4_tas5130a[] = { + 0x80, 0x3c, 0x81, 0x68, 0x83, 0xa0, 0x84, 0x20, + 0x8a, 0x68, 0x8b, 0x58, 0x8c, 0x88, 0x8e, 0xb4, + 0x8f, 0x24, 0xa1, 0xb1, 0xa2, 0x30, 0xa5, 0x10, + 0xa6, 0x4a, 0xae, 0x03, 0xb1, 0x44, 0xb2, 0x08, + 0xb7, 0x06, 0xb9, 0xe7, 0xbb, 0xc4, 0xbc, 0x4a, + 0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8, + 0xc6, 0xda +}; +static const u8 n4_lt168g[] = { + 0x66, 0x01, 0x7f, 0x00, 0x80, 0x7c, 0x81, 0x28, + 0x83, 0x44, 0x84, 0x20, 0x86, 0x20, 0x8a, 0x70, + 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xa0, 0x8e, 0xb3, + 0x8f, 0x24, 0xa1, 0xb0, 0xa2, 0x38, 0xa5, 0x20, + 0xa6, 0x4a, 0xa8, 0xe8, 0xaf, 0x38, 0xb0, 0x68, + 0xb1, 0x44, 0xb2, 0x88, 0xbb, 0x86, 0xbd, 0x40, + 0xbe, 0x26, 0xc1, 0x05, 0xc2, 0x88, 0xc5, 0xc0, + 0xda, 0x8e, 0xdb, 0xca, 0xdc, 0xa8, 0xdd, 0x8c, + 0xde, 0x44, 0xdf, 0x0c, 0xe9, 0x80 +}; + +static const struct additional_sensor_data sensor_data[] = { +[SENSOR_OM6802] = { + .n3 = + {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}, + .n4 = n4_om6802, + .n4sz = sizeof n4_om6802, + .reg80 = 0x3c, + .reg8e = 0x33, + .nset8 = {0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00}, + .data1 = + {0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06, + 0xb3, 0xfc}, + .data2 = + {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, + 0xff}, + .data3 = + {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, + 0xff}, + .data5 = /* this could be removed later */ + {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23}, + .stream = + {0x0b, 0x04, 0x0a, 0x78}, + }, +[SENSOR_OTHER] = { + .n3 = + {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}, + .n4 = n4_other, + .n4sz = sizeof n4_other, + .reg80 = 0xac, + .reg8e = 0xb8, + .nset8 = {0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00}, + .data1 = + {0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a, + 0xe8, 0xfc}, + .data2 = + {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, + 0xd9}, + .data3 = + {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, + 0xd9}, + .data5 = + {0x0c, 0x03, 0xab, 0x29, 0x81, 0x69}, + .stream = + {0x0b, 0x04, 0x0a, 0x00}, + }, +[SENSOR_TAS5130A] = { + .n3 = + {0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08}, + .n4 = n4_tas5130a, + .n4sz = sizeof n4_tas5130a, + .reg80 = 0x3c, + .reg8e = 0xb4, + .nset8 = {0xa8, 0xf0, 0xc6, 0xda, 0xc0, 0x00}, + .data1 = + {0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27, + 0xc8, 0xfc}, + .data2 = + {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, + 0xe0}, + .data3 = + {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, + 0xe0}, + .data5 = + {0x0c, 0x03, 0xab, 0x10, 0x81, 0x20}, + .stream = + {0x0b, 0x04, 0x0a, 0x40}, + }, +[SENSOR_LT168G] = { + .n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00}, + .n4 = n4_lt168g, + .n4sz = sizeof n4_lt168g, + .reg80 = 0x7c, + .reg8e = 0xb3, + .nset8 = {0xa8, 0xf0, 0xc6, 0xba, 0xc0, 0x00}, + .data1 = {0xc0, 0x38, 0x08, 0x10, 0xc0, 0x30, 0x10, 0x40, + 0xb0, 0xf4}, + .data2 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, + 0xff}, + .data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, + 0xff}, + .data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b}, + .stream = {0x0b, 0x04, 0x0a, 0x28}, + }, +}; + +#define MAX_EFFECTS 7 +static const u8 effects_table[MAX_EFFECTS][6] = { + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80}, /* Sepia */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02}, /* Croquis */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10}, /* Sun Effect */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */ +}; + +#define GAMMA_MAX (15) +static const u8 gamma_table[GAMMA_MAX+1][17] = { +/* gamma table from cam1690.ini */ + {0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21, /* 0 */ + 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb, + 0xff}, + {0x00, 0x01, 0x03, 0x08, 0x0e, 0x16, 0x21, 0x2d, /* 1 */ + 0x3c, 0x4d, 0x60, 0x75, 0x8d, 0xa6, 0xc2, 0xe1, + 0xff}, + {0x00, 0x01, 0x05, 0x0b, 0x12, 0x1c, 0x28, 0x35, /* 2 */ + 0x45, 0x56, 0x69, 0x7e, 0x95, 0xad, 0xc7, 0xe3, + 0xff}, + {0x00, 0x02, 0x07, 0x0f, 0x18, 0x24, 0x30, 0x3f, /* 3 */ + 0x4f, 0x61, 0x73, 0x88, 0x9d, 0xb4, 0xcd, 0xe6, + 0xff}, + {0x00, 0x04, 0x0b, 0x15, 0x20, 0x2d, 0x3b, 0x4a, /* 4 */ + 0x5b, 0x6c, 0x7f, 0x92, 0xa7, 0xbc, 0xd2, 0xe9, + 0xff}, + {0x00, 0x07, 0x11, 0x15, 0x20, 0x2d, 0x48, 0x58, /* 5 */ + 0x68, 0x79, 0x8b, 0x9d, 0xb0, 0xc4, 0xd7, 0xec, + 0xff}, + {0x00, 0x0c, 0x1a, 0x29, 0x38, 0x47, 0x57, 0x67, /* 6 */ + 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + 0xff}, + {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, /* 7 */ + 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0xff}, + {0x00, 0x15, 0x27, 0x38, 0x49, 0x59, 0x69, 0x79, /* 8 */ + 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe2, 0xf0, + 0xff}, + {0x00, 0x1c, 0x30, 0x43, 0x54, 0x65, 0x75, 0x84, /* 9 */ + 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd8, 0xe5, 0xf2, + 0xff}, + {0x00, 0x24, 0x3b, 0x4f, 0x60, 0x70, 0x80, 0x8e, /* 10 */ + 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xdc, 0xe8, 0xf3, + 0xff}, + {0x00, 0x2a, 0x3c, 0x5d, 0x6e, 0x7e, 0x8d, 0x9b, /* 11 */ + 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, + 0xff}, + {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */ + 0xb4, 0xbf, 0xc9, 0xd3, 0xdc, 0xe5, 0xee, 0xf6, + 0xff}, + {0x00, 0x54, 0x6f, 0x83, 0x93, 0xa0, 0xad, 0xb7, /* 13 */ + 0xc2, 0xcb, 0xd4, 0xdc, 0xe4, 0xeb, 0xf2, 0xf9, + 0xff}, + {0x00, 0x6e, 0x88, 0x9a, 0xa8, 0xb3, 0xbd, 0xc6, /* 14 */ + 0xcf, 0xd6, 0xdd, 0xe3, 0xe9, 0xef, 0xf4, 0xfa, + 0xff}, + {0x00, 0x93, 0xa8, 0xb7, 0xc1, 0xca, 0xd2, 0xd8, /* 15 */ + 0xde, 0xe3, 0xe8, 0xed, 0xf1, 0xf5, 0xf8, 0xfc, + 0xff} +}; + +static const u8 tas5130a_sensor_init[][8] = { + {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09}, + {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09}, + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, +}; + +static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07}; + +/* read 1 byte */ +static u8 reg_r(struct gspca_dev *gspca_dev, + u16 index) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, 1, 500); + return gspca_dev->usb_buf[0]; +} + +static void reg_w(struct gspca_dev *gspca_dev, + u16 index) +{ + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, + NULL, 0, 500); +} + +static void reg_w_buf(struct gspca_dev *gspca_dev, + const u8 *buffer, u16 len) +{ + if (len <= USB_BUF_SZ) { + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x01, 0, + gspca_dev->usb_buf, len, 500); + } else { + u8 *tmpbuf; + + tmpbuf = kmemdup(buffer, len, GFP_KERNEL); + if (!tmpbuf) { + pr_err("Out of memory\n"); + return; + } + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x01, 0, + tmpbuf, len, 500); + kfree(tmpbuf); + } +} + +/* write values to consecutive registers */ +static void reg_w_ixbuf(struct gspca_dev *gspca_dev, + u8 reg, + const u8 *buffer, u16 len) +{ + int i; + u8 *p, *tmpbuf; + + if (len * 2 <= USB_BUF_SZ) { + p = tmpbuf = gspca_dev->usb_buf; + } else { + p = tmpbuf = kmalloc(len * 2, GFP_KERNEL); + if (!tmpbuf) { + pr_err("Out of memory\n"); + return; + } + } + i = len; + while (--i >= 0) { + *p++ = reg++; + *p++ = *buffer++; + } + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x01, 0, + tmpbuf, len * 2, 500); + if (len * 2 > USB_BUF_SZ) + kfree(tmpbuf); +} + +static void om6802_sensor_init(struct gspca_dev *gspca_dev) +{ + int i; + const u8 *p; + u8 byte; + u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05}; + static const u8 sensor_init[] = { + 0xdf, 0x6d, + 0xdd, 0x18, + 0x5a, 0xe0, + 0x5c, 0x07, + 0x5d, 0xb0, + 0x5e, 0x1e, + 0x60, 0x71, + 0xef, 0x00, + 0xe9, 0x00, + 0xea, 0x00, + 0x90, 0x24, + 0x91, 0xb2, + 0x82, 0x32, + 0xfd, 0x41, + 0x00 /* table end */ + }; + + reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); + msleep(100); + i = 4; + while (--i > 0) { + byte = reg_r(gspca_dev, 0x0060); + if (!(byte & 0x01)) + break; + msleep(100); + } + byte = reg_r(gspca_dev, 0x0063); + if (byte != 0x17) { + pr_err("Bad sensor reset %02x\n", byte); + /* continue? */ + } + + p = sensor_init; + while (*p != 0) { + val[1] = *p++; + val[3] = *p++; + if (*p == 0) + reg_w(gspca_dev, 0x3c80); + reg_w_buf(gspca_dev, val, sizeof val); + i = 4; + while (--i >= 0) { + msleep(15); + byte = reg_r(gspca_dev, 0x60); + if (!(byte & 0x01)) + break; + } + } + msleep(15); + reg_w(gspca_dev, 0x3c80); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + + cam->cam_mode = vga_mode_t16; + cam->nmodes = ARRAY_SIZE(vga_mode_t16); + + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +{ + u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 }; + + if (brightness < 7) { + set6[1] = 0x26; + set6[3] = 0x70 - brightness * 0x10; + } else { + set6[3] = 0x00 + ((brightness - 7) * 0x10); + } + + reg_w_buf(gspca_dev, set6, sizeof set6); +} + +static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast) +{ + u16 reg_to_write; + + if (contrast < 7) + reg_to_write = 0x8ea9 - contrast * 0x200; + else + reg_to_write = 0x00a9 + (contrast - 7) * 0x200; + + reg_w(gspca_dev, reg_to_write); +} + +static void setcolors(struct gspca_dev *gspca_dev, s32 val) +{ + u16 reg_to_write; + + reg_to_write = 0x80bb + val * 0x100; /* was 0xc0 */ + reg_w(gspca_dev, reg_to_write); +} + +static void setgamma(struct gspca_dev *gspca_dev, s32 val) +{ + PDEBUG(D_CONF, "Gamma: %d", sd->gamma); + reg_w_ixbuf(gspca_dev, 0x90, + gamma_table[val], sizeof gamma_table[0]); +} + +static void setawb_n_RGB(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 all_gain_reg[8] = { + 0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00 }; + s32 red_gain, blue_gain, green_gain; + + green_gain = sd->gain->val; + + red_gain = green_gain + sd->red_balance->val; + if (red_gain > 0x40) + red_gain = 0x40; + else if (red_gain < 0x10) + red_gain = 0x10; + + blue_gain = green_gain + sd->blue_balance->val; + if (blue_gain > 0x40) + blue_gain = 0x40; + else if (blue_gain < 0x10) + blue_gain = 0x10; + + all_gain_reg[1] = red_gain; + all_gain_reg[3] = blue_gain; + all_gain_reg[5] = green_gain; + all_gain_reg[7] = sensor_data[sd->sensor].reg80; + if (!sd->awb->val) + all_gain_reg[7] &= ~0x04; /* AWB off */ + + reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + u16 reg_to_write; + + reg_to_write = 0x0aa6 + 0x1000 * val; + + reg_w(gspca_dev, reg_to_write); +} + +static void setfreq(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 reg66; + u8 freq[4] = { 0x66, 0x00, 0xa8, 0xe8 }; + + switch (sd->sensor) { + case SENSOR_LT168G: + if (val != 0) + freq[3] = 0xa8; + reg66 = 0x41; + break; + case SENSOR_OM6802: + reg66 = 0xca; + break; + default: + reg66 = 0x40; + break; + } + switch (val) { + case 0: /* no flicker */ + freq[3] = 0xf0; + break; + case 2: /* 60Hz */ + reg66 &= ~0x40; + break; + } + freq[1] = reg66; + + reg_w_buf(gspca_dev, freq, sizeof freq); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + /* some of this registers are not really neded, because + * they are overriden by setbrigthness, setcontrast, etc, + * but wont hurt anyway, and can help someone with similar webcam + * to see the initial parameters.*/ + struct sd *sd = (struct sd *) gspca_dev; + const struct additional_sensor_data *sensor; + int i; + u16 sensor_id; + u8 test_byte = 0; + + static const u8 read_indexs[] = + { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, + 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 }; + static const u8 n1[] = + {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; + static const u8 n2[] = + {0x08, 0x00}; + + sensor_id = (reg_r(gspca_dev, 0x06) << 8) + | reg_r(gspca_dev, 0x07); + switch (sensor_id & 0xff0f) { + case 0x0801: + PDEBUG(D_PROBE, "sensor tas5130a"); + sd->sensor = SENSOR_TAS5130A; + break; + case 0x0802: + PDEBUG(D_PROBE, "sensor lt168g"); + sd->sensor = SENSOR_LT168G; + break; + case 0x0803: + PDEBUG(D_PROBE, "sensor 'other'"); + sd->sensor = SENSOR_OTHER; + break; + case 0x0807: + PDEBUG(D_PROBE, "sensor om6802"); + sd->sensor = SENSOR_OM6802; + break; + default: + pr_err("unknown sensor %04x\n", sensor_id); + return -EINVAL; + } + + if (sd->sensor == SENSOR_OM6802) { + reg_w_buf(gspca_dev, n1, sizeof n1); + i = 5; + while (--i >= 0) { + reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); + test_byte = reg_r(gspca_dev, 0x0063); + msleep(100); + if (test_byte == 0x17) + break; /* OK */ + } + if (i < 0) { + pr_err("Bad sensor reset %02x\n", test_byte); + return -EIO; + } + reg_w_buf(gspca_dev, n2, sizeof n2); + } + + i = 0; + while (read_indexs[i] != 0x00) { + test_byte = reg_r(gspca_dev, read_indexs[i]); + PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", read_indexs[i], + test_byte); + i++; + } + + sensor = &sensor_data[sd->sensor]; + reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3); + reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz); + + if (sd->sensor == SENSOR_LT168G) { + test_byte = reg_r(gspca_dev, 0x80); + PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80, + test_byte); + reg_w(gspca_dev, 0x6c80); + } + + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); + reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); + reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); + + reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); + reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); + reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e); + reg_w(gspca_dev, (0x20 << 8) + 0x87); + reg_w(gspca_dev, (0x20 << 8) + 0x88); + reg_w(gspca_dev, (0x20 << 8) + 0x89); + + reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5); + reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + + if (sd->sensor == SENSOR_LT168G) { + test_byte = reg_r(gspca_dev, 0x80); + PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80, + test_byte); + reg_w(gspca_dev, 0x6c80); + } + + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); + reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); + reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); + + return 0; +} + +static void setmirror(struct gspca_dev *gspca_dev, s32 val) +{ + u8 hflipcmd[8] = + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}; + + if (val) + hflipcmd[3] = 0x01; + + reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd); +} + +static void seteffect(struct gspca_dev *gspca_dev, s32 val) +{ + int idx = 0; + + switch (val) { + case V4L2_COLORFX_NONE: + break; + case V4L2_COLORFX_BW: + idx = 2; + break; + case V4L2_COLORFX_SEPIA: + idx = 3; + break; + case V4L2_COLORFX_SKETCH: + idx = 4; + break; + case V4L2_COLORFX_NEGATIVE: + idx = 6; + break; + default: + break; + } + + reg_w_buf(gspca_dev, effects_table[idx], + sizeof effects_table[0]); + + if (val == V4L2_COLORFX_SKETCH) + reg_w(gspca_dev, 0x4aa6); + else + reg_w(gspca_dev, 0xfaa6); +} + +/* Is this really needed? + * i added some module parameters for test with some users */ +static void poll_sensor(struct gspca_dev *gspca_dev) +{ + static const u8 poll1[] = + {0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82, + 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34, + 0x74, 0x32, 0x75, 0x92, 0x76, 0x00, 0x09, 0x01, + 0x60, 0x14}; + static const u8 poll2[] = + {0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9, + 0x73, 0x02, 0x73, 0x02, 0x60, 0x14}; + static const u8 noise03[] = /* (some differences / ms-drv) */ + {0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f, + 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c, + 0xc2, 0x80, 0xc3, 0x10}; + + PDEBUG(D_STREAM, "[Sensor requires polling]"); + reg_w_buf(gspca_dev, poll1, sizeof poll1); + reg_w_buf(gspca_dev, poll2, sizeof poll2); + reg_w_buf(gspca_dev, noise03, sizeof noise03); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const struct additional_sensor_data *sensor; + int i, mode; + u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; + static const u8 t3[] = + { 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 }; + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + switch (mode) { + case 0: /* 640x480 (0x00) */ + break; + case 1: /* 352x288 */ + t2[1] = 0x40; + break; + case 2: /* 320x240 */ + t2[1] = 0x10; + break; + case 3: /* 176x144 */ + t2[1] = 0x50; + break; + default: +/* case 4: * 160x120 */ + t2[1] = 0x20; + break; + } + + switch (sd->sensor) { + case SENSOR_OM6802: + om6802_sensor_init(gspca_dev); + break; + case SENSOR_TAS5130A: + i = 0; + for (;;) { + reg_w_buf(gspca_dev, tas5130a_sensor_init[i], + sizeof tas5130a_sensor_init[0]); + if (i >= ARRAY_SIZE(tas5130a_sensor_init) - 1) + break; + i++; + } + reg_w(gspca_dev, 0x3c80); + /* just in case and to keep sync with logs (for mine) */ + reg_w_buf(gspca_dev, tas5130a_sensor_init[i], + sizeof tas5130a_sensor_init[0]); + reg_w(gspca_dev, 0x3c80); + break; + } + sensor = &sensor_data[sd->sensor]; + setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); + reg_r(gspca_dev, 0x0012); + reg_w_buf(gspca_dev, t2, sizeof t2); + reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3); + reg_w(gspca_dev, 0x0013); + msleep(15); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + + if (sd->sensor == SENSOR_OM6802) + poll_sensor(gspca_dev); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, + sizeof sensor_data[sd->sensor].stream); + reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, + sizeof sensor_data[sd->sensor].stream); + if (sd->sensor == SENSOR_OM6802) { + msleep(20); + reg_w(gspca_dev, 0x0309); + } +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + /* If the last button state is pressed, release it now! */ + if (sd->button_pressed) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + sd->button_pressed = 0; + } +#endif +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int pkt_type; + + if (data[0] == 0x5a) { +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + if (len > 20) { + u8 state = (data[20] & 0x80) ? 1 : 0; + if (sd->button_pressed != state) { + input_report_key(gspca_dev->input_dev, + KEY_CAMERA, state); + input_sync(gspca_dev->input_dev); + sd->button_pressed = state; + } + } +#endif + /* Control Packet, after this came the header again, + * but extra bytes came in the packet before this, + * sometimes an EOF arrives, sometimes not... */ + return; + } + data += 2; + len -= 2; + if (data[0] == 0xff && data[1] == 0xd8) + pkt_type = FIRST_PACKET; + else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) + pkt_type = LAST_PACKET; + else + pkt_type = INTER_PACKET; + gspca_frame_add(gspca_dev, pkt_type, data, len); +} + +static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + s32 red_gain, blue_gain, green_gain; + + gspca_dev->usb_err = 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + red_gain = reg_r(gspca_dev, 0x0087); + if (red_gain > 0x40) + red_gain = 0x40; + else if (red_gain < 0x10) + red_gain = 0x10; + + blue_gain = reg_r(gspca_dev, 0x0088); + if (blue_gain > 0x40) + blue_gain = 0x40; + else if (blue_gain < 0x10) + blue_gain = 0x10; + + green_gain = reg_r(gspca_dev, 0x0089); + if (green_gain > 0x40) + green_gain = 0x40; + else if (green_gain < 0x10) + green_gain = 0x10; + + sd->gain->val = green_gain; + sd->red_balance->val = red_gain - green_gain; + sd->blue_balance->val = blue_gain - green_gain; + break; + } + return 0; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAMMA: + setgamma(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + setmirror(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_BACKLIGHT_COMPENSATION: + reg_w(gspca_dev, ctrl->val ? 0xf48e : 0xb48e); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + setawb_n_RGB(gspca_dev); + break; + case V4L2_CID_COLORFX: + seteffect(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .g_volatile_ctrl = sd_g_volatile_ctrl, + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 12); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 14, 1, 8); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0x0d, 1, 7); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xf, 1, 5); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10); + /* Activate lowlight, some apps dont bring up the + backlight_compensation control) */ + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1); + if (sd->sensor == SENSOR_TAS5130A) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->awb = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0x10, 0x40, 1, 0x20); + sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, 0); + sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, 0); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 15, 1, 6); + v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_COLORFX, V4L2_COLORFX_SKETCH, + ~((1 << V4L2_COLORFX_NONE) | + (1 << V4L2_COLORFX_BW) | + (1 << V4L2_COLORFX_SEPIA) | + (1 << V4L2_COLORFX_SKETCH) | + (1 << V4L2_COLORFX_NEGATIVE)), + V4L2_COLORFX_NONE); + sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_auto_cluster(4, &sd->awb, 0, true); + + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .other_input = 1, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x17a1, 0x0128)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c new file mode 100644 index 000000000000..a6055246cb9d --- /dev/null +++ b/drivers/media/usb/gspca/topro.c @@ -0,0 +1,4969 @@ +/* + * Topro TP6800/6810 webcam driver. + * + * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr) + * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se) + * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "gspca.h" + +MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver"); +MODULE_AUTHOR("Jean-Francois Moine , " + "Anders Blomdell "); +MODULE_LICENSE("GPL"); + +static int force_sensor = -1; + +/* JPEG header */ +static const u8 jpeg_head[] = { + 0xff, 0xd8, /* jpeg */ + +/* quantization table quality 50% */ + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, +#define JPEG_QT0_OFFSET 7 + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, +1, +#define JPEG_QT1_OFFSET 72 + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + + /* Define Huffman table (thanks to Thomas Kaiser) */ + 0xff, 0xc4, 0x01, 0x5e, + 0x00, 0x00, 0x02, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, + 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21, + 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32, + 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52, + 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1, + 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2, + 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25, + 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, + 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, + 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02, + 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01, + 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13, + 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14, + 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1, + 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43, + 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82, + 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28, + 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ + 0x08, /* data precision */ +#define JPEG_HEIGHT_OFFSET 493 + 0x01, 0xe0, /* height */ + 0x02, 0x80, /* width */ + 0x03, /* component number */ + 0x01, + 0x21, /* samples Y = jpeg 422 */ + 0x00, /* quant Y */ + 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ + 0x03, 0x11, 0x01, + + 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ + 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +#define JPEG_HDR_SZ 521 +}; + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_ctrl *jpegqual; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *gamma; + struct v4l2_ctrl *blue; + struct v4l2_ctrl *red; + + u8 framerate; + u8 quality; /* webcam current JPEG quality (0..16) */ + s8 ag_cnt; /* autogain / start counter for tp6810 */ +#define AG_CNT_START 13 /* check gain every N frames */ + + u8 bridge; + u8 sensor; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +enum bridges { + BRIDGE_TP6800, + BRIDGE_TP6810, +}; + +enum sensors { + SENSOR_CX0342, + SENSOR_SOI763A, /* ~= ov7630 / ov7648 */ + NSENSORS +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG} +}; + +/* + * JPEG quality + * index: webcam compression + * value: JPEG quality in % + */ +static const u8 jpeg_q[17] = { + 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94 +}; + +#define BULK_OUT_SIZE 0x20 +#if BULK_OUT_SIZE > USB_BUF_SZ +#error "USB buffer too small" +#endif + +static const u8 rates[] = {30, 20, 15, 10, 7, 5}; +static const struct framerates framerates[] = { + { + .rates = rates, + .nrates = ARRAY_SIZE(rates) + }, + { + .rates = rates, + .nrates = ARRAY_SIZE(rates) + } +}; +static const u8 rates_6810[] = {30, 15, 10, 7, 5}; +static const struct framerates framerates_6810[] = { + { + .rates = rates_6810, + .nrates = ARRAY_SIZE(rates_6810) + }, + { + .rates = rates_6810, + .nrates = ARRAY_SIZE(rates_6810) + } +}; + +/* + * webcam quality in % + * the last value is the ultra fine quality + */ + +/* TP6800 register offsets */ +#define TP6800_R10_SIF_TYPE 0x10 +#define TP6800_R11_SIF_CONTROL 0x11 +#define TP6800_R12_SIF_ADDR_S 0x12 +#define TP6800_R13_SIF_TX_DATA 0x13 +#define TP6800_R14_SIF_RX_DATA 0x14 +#define TP6800_R15_GPIO_PU 0x15 +#define TP6800_R16_GPIO_PD 0x16 +#define TP6800_R17_GPIO_IO 0x17 +#define TP6800_R18_GPIO_DATA 0x18 +#define TP6800_R19_SIF_ADDR_S2 0x19 +#define TP6800_R1A_SIF_TX_DATA2 0x1a +#define TP6800_R1B_SIF_RX_DATA2 0x1b +#define TP6800_R21_ENDP_1_CTL 0x21 +#define TP6800_R2F_TIMING_CFG 0x2f +#define TP6800_R30_SENSOR_CFG 0x30 +#define TP6800_R31_PIXEL_START 0x31 +#define TP6800_R32_PIXEL_END_L 0x32 +#define TP6800_R33_PIXEL_END_H 0x33 +#define TP6800_R34_LINE_START 0x34 +#define TP6800_R35_LINE_END_L 0x35 +#define TP6800_R36_LINE_END_H 0x36 +#define TP6800_R37_FRONT_DARK_ST 0x37 +#define TP6800_R38_FRONT_DARK_END 0x38 +#define TP6800_R39_REAR_DARK_ST_L 0x39 +#define TP6800_R3A_REAR_DARK_ST_H 0x3a +#define TP6800_R3B_REAR_DARK_END_L 0x3b +#define TP6800_R3C_REAR_DARK_END_H 0x3c +#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d +#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e +#define TP6800_R3F_FRAME_RATE 0x3f +#define TP6800_R50 0x50 +#define TP6800_R51 0x51 +#define TP6800_R52 0x52 +#define TP6800_R53 0x53 +#define TP6800_R54_DARK_CFG 0x54 +#define TP6800_R55_GAMMA_R 0x55 +#define TP6800_R56_GAMMA_G 0x56 +#define TP6800_R57_GAMMA_B 0x57 +#define TP6800_R5C_EDGE_THRLD 0x5c +#define TP6800_R5D_DEMOSAIC_CFG 0x5d +#define TP6800_R78_FORMAT 0x78 +#define TP6800_R79_QUALITY 0x79 +#define TP6800_R7A_BLK_THRLD 0x7a + +/* CX0342 register offsets */ + +#define CX0342_SENSOR_ID 0x00 +#define CX0342_VERSION_NO 0x01 +#define CX0342_ORG_X_L 0x02 +#define CX0342_ORG_X_H 0x03 +#define CX0342_ORG_Y_L 0x04 +#define CX0342_ORG_Y_H 0x05 +#define CX0342_STOP_X_L 0x06 +#define CX0342_STOP_X_H 0x07 +#define CX0342_STOP_Y_L 0x08 +#define CX0342_STOP_Y_H 0x09 +#define CX0342_FRAME_WIDTH_L 0x0a +#define CX0342_FRAME_WIDTH_H 0x0b +#define CX0342_FRAME_HEIGH_L 0x0c +#define CX0342_FRAME_HEIGH_H 0x0d +#define CX0342_EXPO_LINE_L 0x10 +#define CX0342_EXPO_LINE_H 0x11 +#define CX0342_EXPO_CLK_L 0x12 +#define CX0342_EXPO_CLK_H 0x13 +#define CX0342_RAW_GRGAIN_L 0x14 +#define CX0342_RAW_GRGAIN_H 0x15 +#define CX0342_RAW_GBGAIN_L 0x16 +#define CX0342_RAW_GBGAIN_H 0x17 +#define CX0342_RAW_RGAIN_L 0x18 +#define CX0342_RAW_RGAIN_H 0x19 +#define CX0342_RAW_BGAIN_L 0x1a +#define CX0342_RAW_BGAIN_H 0x1b +#define CX0342_GLOBAL_GAIN 0x1c +#define CX0342_SYS_CTRL_0 0x20 +#define CX0342_SYS_CTRL_1 0x21 +#define CX0342_SYS_CTRL_2 0x22 +#define CX0342_BYPASS_MODE 0x23 +#define CX0342_SYS_CTRL_3 0x24 +#define CX0342_TIMING_EN 0x25 +#define CX0342_OUTPUT_CTRL 0x26 +#define CX0342_AUTO_ADC_CALIB 0x27 +#define CX0342_SYS_CTRL_4 0x28 +#define CX0342_ADCGN 0x30 +#define CX0342_SLPCR 0x31 +#define CX0342_SLPFN_LO 0x32 +#define CX0342_ADC_CTL 0x33 +#define CX0342_LVRST_BLBIAS 0x34 +#define CX0342_VTHSEL 0x35 +#define CX0342_RAMP_RIV 0x36 +#define CX0342_LDOSEL 0x37 +#define CX0342_CLOCK_GEN 0x40 +#define CX0342_SOFT_RESET 0x41 +#define CX0342_PLL 0x42 +#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43 +#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44 +#define CX0342_DR_ENH_PULSE_POS_L 0x45 +#define CX0342_DR_ENH_PULSE_POS_H 0x46 +#define CX0342_DR_ENH_PULSE_WIDTH 0x47 +#define CX0342_AS_CURRENT_CNT_L 0x48 +#define CX0342_AS_CURRENT_CNT_H 0x49 +#define CX0342_AS_PREVIOUS_CNT_L 0x4a +#define CX0342_AS_PREVIOUS_CNT_H 0x4b +#define CX0342_SPV_VALUE_L 0x4c +#define CX0342_SPV_VALUE_H 0x4d +#define CX0342_GPXLTHD_L 0x50 +#define CX0342_GPXLTHD_H 0x51 +#define CX0342_RBPXLTHD_L 0x52 +#define CX0342_RBPXLTHD_H 0x53 +#define CX0342_PLANETHD_L 0x54 +#define CX0342_PLANETHD_H 0x55 +#define CX0342_ROWDARK_TH 0x56 +#define CX0342_ROWDARK_TOL 0x57 +#define CX0342_RB_GAP_L 0x58 +#define CX0342_RB_GAP_H 0x59 +#define CX0342_G_GAP_L 0x5a +#define CX0342_G_GAP_H 0x5b +#define CX0342_AUTO_ROW_DARK 0x60 +#define CX0342_MANUAL_DARK_VALUE 0x61 +#define CX0342_GB_DARK_OFFSET 0x62 +#define CX0342_GR_DARK_OFFSET 0x63 +#define CX0342_RED_DARK_OFFSET 0x64 +#define CX0342_BLUE_DARK_OFFSET 0x65 +#define CX0342_DATA_SCALING_MULTI 0x66 +#define CX0342_AUTOD_Q_FRAME 0x67 +#define CX0342_AUTOD_ALLOW_VARI 0x68 +#define CX0342_AUTO_DARK_VALUE_L 0x69 +#define CX0342_AUTO_DARK_VALUE_H 0x6a +#define CX0342_IO_CTRL_0 0x70 +#define CX0342_IO_CTRL_1 0x71 +#define CX0342_IO_CTRL_2 0x72 +#define CX0342_IDLE_CTRL 0x73 +#define CX0342_TEST_MODE 0x74 +#define CX0342_FRAME_FIX_DATA_TEST 0x75 +#define CX0342_FRAME_CNT_TEST 0x76 +#define CX0342_RST_OVERFLOW_L 0x80 +#define CX0342_RST_OVERFLOW_H 0x81 +#define CX0342_RST_UNDERFLOW_L 0x82 +#define CX0342_RST_UNDERFLOW_H 0x83 +#define CX0342_DATA_OVERFLOW_L 0x84 +#define CX0342_DATA_OVERFLOW_H 0x85 +#define CX0342_DATA_UNDERFLOW_L 0x86 +#define CX0342_DATA_UNDERFLOW_H 0x87 +#define CX0342_CHANNEL_0_0_L_irst 0x90 +#define CX0342_CHANNEL_0_0_H_irst 0x91 +#define CX0342_CHANNEL_0_1_L_irst 0x92 +#define CX0342_CHANNEL_0_1_H_irst 0x93 +#define CX0342_CHANNEL_0_2_L_irst 0x94 +#define CX0342_CHANNEL_0_2_H_irst 0x95 +#define CX0342_CHANNEL_0_3_L_irst 0x96 +#define CX0342_CHANNEL_0_3_H_irst 0x97 +#define CX0342_CHANNEL_0_4_L_irst 0x98 +#define CX0342_CHANNEL_0_4_H_irst 0x99 +#define CX0342_CHANNEL_0_5_L_irst 0x9a +#define CX0342_CHANNEL_0_5_H_irst 0x9b +#define CX0342_CHANNEL_0_6_L_irst 0x9c +#define CX0342_CHANNEL_0_6_H_irst 0x9d +#define CX0342_CHANNEL_0_7_L_irst 0x9e +#define CX0342_CHANNEL_0_7_H_irst 0x9f +#define CX0342_CHANNEL_1_0_L_itx 0xa0 +#define CX0342_CHANNEL_1_0_H_itx 0xa1 +#define CX0342_CHANNEL_1_1_L_itx 0xa2 +#define CX0342_CHANNEL_1_1_H_itx 0xa3 +#define CX0342_CHANNEL_1_2_L_itx 0xa4 +#define CX0342_CHANNEL_1_2_H_itx 0xa5 +#define CX0342_CHANNEL_1_3_L_itx 0xa6 +#define CX0342_CHANNEL_1_3_H_itx 0xa7 +#define CX0342_CHANNEL_1_4_L_itx 0xa8 +#define CX0342_CHANNEL_1_4_H_itx 0xa9 +#define CX0342_CHANNEL_1_5_L_itx 0xaa +#define CX0342_CHANNEL_1_5_H_itx 0xab +#define CX0342_CHANNEL_1_6_L_itx 0xac +#define CX0342_CHANNEL_1_6_H_itx 0xad +#define CX0342_CHANNEL_1_7_L_itx 0xae +#define CX0342_CHANNEL_1_7_H_itx 0xaf +#define CX0342_CHANNEL_2_0_L_iwl 0xb0 +#define CX0342_CHANNEL_2_0_H_iwl 0xb1 +#define CX0342_CHANNEL_2_1_L_iwl 0xb2 +#define CX0342_CHANNEL_2_1_H_iwl 0xb3 +#define CX0342_CHANNEL_2_2_L_iwl 0xb4 +#define CX0342_CHANNEL_2_2_H_iwl 0xb5 +#define CX0342_CHANNEL_2_3_L_iwl 0xb6 +#define CX0342_CHANNEL_2_3_H_iwl 0xb7 +#define CX0342_CHANNEL_2_4_L_iwl 0xb8 +#define CX0342_CHANNEL_2_4_H_iwl 0xb9 +#define CX0342_CHANNEL_2_5_L_iwl 0xba +#define CX0342_CHANNEL_2_5_H_iwl 0xbb +#define CX0342_CHANNEL_2_6_L_iwl 0xbc +#define CX0342_CHANNEL_2_6_H_iwl 0xbd +#define CX0342_CHANNEL_2_7_L_iwl 0xbe +#define CX0342_CHANNEL_2_7_H_iwl 0xbf +#define CX0342_CHANNEL_3_0_L_ensp 0xc0 +#define CX0342_CHANNEL_3_0_H_ensp 0xc1 +#define CX0342_CHANNEL_3_1_L_ensp 0xc2 +#define CX0342_CHANNEL_3_1_H_ensp 0xc3 +#define CX0342_CHANNEL_3_2_L_ensp 0xc4 +#define CX0342_CHANNEL_3_2_H_ensp 0xc5 +#define CX0342_CHANNEL_3_3_L_ensp 0xc6 +#define CX0342_CHANNEL_3_3_H_ensp 0xc7 +#define CX0342_CHANNEL_3_4_L_ensp 0xc8 +#define CX0342_CHANNEL_3_4_H_ensp 0xc9 +#define CX0342_CHANNEL_3_5_L_ensp 0xca +#define CX0342_CHANNEL_3_5_H_ensp 0xcb +#define CX0342_CHANNEL_3_6_L_ensp 0xcc +#define CX0342_CHANNEL_3_6_H_ensp 0xcd +#define CX0342_CHANNEL_3_7_L_ensp 0xce +#define CX0342_CHANNEL_3_7_H_ensp 0xcf +#define CX0342_CHANNEL_4_0_L_sela 0xd0 +#define CX0342_CHANNEL_4_0_H_sela 0xd1 +#define CX0342_CHANNEL_4_1_L_sela 0xd2 +#define CX0342_CHANNEL_4_1_H_sela 0xd3 +#define CX0342_CHANNEL_5_0_L_intla 0xe0 +#define CX0342_CHANNEL_5_0_H_intla 0xe1 +#define CX0342_CHANNEL_5_1_L_intla 0xe2 +#define CX0342_CHANNEL_5_1_H_intla 0xe3 +#define CX0342_CHANNEL_5_2_L_intla 0xe4 +#define CX0342_CHANNEL_5_2_H_intla 0xe5 +#define CX0342_CHANNEL_5_3_L_intla 0xe6 +#define CX0342_CHANNEL_5_3_H_intla 0xe7 +#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0 +#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1 +#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2 +#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3 +#define CX0342_SENSOR_HEIGHT_L 0xfb +#define CX0342_SENSOR_HEIGHT_H 0xfc +#define CX0342_SENSOR_WIDTH_L 0xfd +#define CX0342_SENSOR_WIDTH_H 0xfe +#define CX0342_VSYNC_HSYNC_READ 0xff + +struct cmd { + u8 reg; + u8 val; +}; + +static const u8 DQT[17][130] = { + /* Define quantization table (thanks to Thomas Kaiser) */ + { /* Quality 0 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x01, + 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06, + 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + }, + { /* Quality 1 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x01, + 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d, + 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + }, + { /* Quality 2 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x01, + 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13, + 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + }, + { /* Quality 3 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, + 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x01, + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + }, + { /* Quality 4 */ + 0x00, + 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05, + 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x01, + 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20, + 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + }, + { /* Quality 5 */ + 0x00, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06, + 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x01, + 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27, + 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + }, + { /* Quality 6 */ + 0x00, + 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07, + 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x01, + 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d, + 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + }, + { /* Quality 7 */ + 0x00, + 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, + 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x01, + 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, + 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + }, + { /* Quality 8 */ + 0x00, + 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a, + 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x01, + 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41, + 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + }, + { /* Quality 9 */ + 0x00, + 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c, + 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x01, + 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e, + 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 10 */ + 0x00, + 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e, + 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x01, + 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b, + 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 11 */ + 0x00, + 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, + 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x01, + 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68, + 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 12 */ + 0x00, + 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14, + 0x14, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x01, + 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82, + 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 13 */ + 0x00, + 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, + 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x01, + 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c, + 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 14 */ + 0x00, + 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c, + 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x01, + 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6, + 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 15 */ + 0x00, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, + 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x01, + 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0, + 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 16-31 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x01, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + } +}; + +static const struct cmd tp6810_cx_init_common[] = { + {0x1c, 0x00}, + {TP6800_R10_SIF_TYPE, 0x00}, + {0x4e, 0x00}, + {0x4f, 0x00}, + {TP6800_R50, 0xff}, + {TP6800_R51, 0x03}, + {0x00, 0x07}, + {TP6800_R79_QUALITY, 0x03}, + {TP6800_R2F_TIMING_CFG, 0x37}, + {TP6800_R30_SENSOR_CFG, 0x10}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R52, 0x40}, + {TP6800_R53, 0x40}, + {TP6800_R54_DARK_CFG, 0x40}, + {TP6800_R30_SENSOR_CFG, 0x18}, + {0x4b, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x83}, + {TP6800_R79_QUALITY, 0x05}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {0x7c, 0x04}, + {0x25, 0x14}, + {0x26, 0x0f}, + {0x7b, 0x10}, +}; + +static const struct cmd tp6810_ov_init_common[] = { + {0x1c, 0x00}, + {TP6800_R10_SIF_TYPE, 0x00}, + {0x4e, 0x00}, + {0x4f, 0x00}, + {TP6800_R50, 0xff}, + {TP6800_R51, 0x03}, + {0x00, 0x07}, + {TP6800_R52, 0x40}, + {TP6800_R53, 0x40}, + {TP6800_R54_DARK_CFG, 0x40}, + {TP6800_R79_QUALITY, 0x03}, + {TP6800_R2F_TIMING_CFG, 0x17}, + {TP6800_R30_SENSOR_CFG, 0x18}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x86}, + {0x25, 0x18}, + {0x26, 0x0f}, + {0x7b, 0x90}, +}; + +static const struct cmd tp6810_bridge_start[] = { + {0x59, 0x88}, + {0x5a, 0x0f}, + {0x5b, 0x4e}, + {TP6800_R5C_EDGE_THRLD, 0x63}, + {TP6800_R5D_DEMOSAIC_CFG, 0x00}, + {0x03, 0x7f}, + {0x04, 0x80}, + {0x06, 0x00}, + {0x00, 0x00}, +}; + +static const struct cmd tp6810_late_start[] = { + {0x7d, 0x01}, + {0xb0, 0x04}, + {0xb1, 0x04}, + {0xb2, 0x04}, + {0xb3, 0x04}, + {0xb4, 0x04}, + {0xb5, 0x04}, + {0xb6, 0x08}, + {0xb7, 0x08}, + {0xb8, 0x04}, + {0xb9, 0x04}, + {0xba, 0x04}, + {0xbb, 0x04}, + {0xbc, 0x04}, + {0xbd, 0x08}, + {0xbe, 0x08}, + {0xbf, 0x08}, + {0xc0, 0x04}, + {0xc1, 0x04}, + {0xc2, 0x08}, + {0xc3, 0x08}, + {0xc4, 0x08}, + {0xc5, 0x08}, + {0xc6, 0x08}, + {0xc7, 0x13}, + {0xc8, 0x04}, + {0xc9, 0x08}, + {0xca, 0x08}, + {0xcb, 0x08}, + {0xcc, 0x08}, + {0xcd, 0x08}, + {0xce, 0x13}, + {0xcf, 0x13}, + {0xd0, 0x08}, + {0xd1, 0x08}, + {0xd2, 0x08}, + {0xd3, 0x08}, + {0xd4, 0x08}, + {0xd5, 0x13}, + {0xd6, 0x13}, + {0xd7, 0x13}, + {0xd8, 0x08}, + {0xd9, 0x08}, + {0xda, 0x08}, + {0xdb, 0x08}, + {0xdc, 0x13}, + {0xdd, 0x13}, + {0xde, 0x13}, + {0xdf, 0x13}, + {0xe0, 0x08}, + {0xe1, 0x08}, + {0xe2, 0x08}, + {0xe3, 0x13}, + {0xe4, 0x13}, + {0xe5, 0x13}, + {0xe6, 0x13}, + {0xe7, 0x13}, + {0xe8, 0x08}, + {0xe9, 0x08}, + {0xea, 0x13}, + {0xeb, 0x13}, + {0xec, 0x13}, + {0xed, 0x13}, + {0xee, 0x13}, + {0xef, 0x13}, + {0x7d, 0x02}, + + /* later after isoc start */ + {0x7d, 0x08}, + {0x7d, 0x00}, +}; + +static const struct cmd cx0342_timing_seq[] = { + {CX0342_CHANNEL_0_1_L_irst, 0x20}, + {CX0342_CHANNEL_0_2_L_irst, 0x24}, + {CX0342_CHANNEL_0_2_H_irst, 0x00}, + {CX0342_CHANNEL_0_3_L_irst, 0x2f}, + {CX0342_CHANNEL_0_3_H_irst, 0x00}, + {CX0342_CHANNEL_1_0_L_itx, 0x02}, + {CX0342_CHANNEL_1_0_H_itx, 0x00}, + {CX0342_CHANNEL_1_1_L_itx, 0x20}, + {CX0342_CHANNEL_1_1_H_itx, 0x00}, + {CX0342_CHANNEL_1_2_L_itx, 0xe4}, + {CX0342_CHANNEL_1_2_H_itx, 0x00}, + {CX0342_CHANNEL_1_3_L_itx, 0xee}, + {CX0342_CHANNEL_1_3_H_itx, 0x00}, + {CX0342_CHANNEL_2_0_L_iwl, 0x30}, + {CX0342_CHANNEL_2_0_H_iwl, 0x00}, + {CX0342_CHANNEL_3_0_L_ensp, 0x34}, + {CX0342_CHANNEL_3_1_L_ensp, 0xe2}, + {CX0342_CHANNEL_3_1_H_ensp, 0x00}, + {CX0342_CHANNEL_3_2_L_ensp, 0xf6}, + {CX0342_CHANNEL_3_2_H_ensp, 0x00}, + {CX0342_CHANNEL_3_3_L_ensp, 0xf4}, + {CX0342_CHANNEL_3_3_H_ensp, 0x02}, + {CX0342_CHANNEL_4_0_L_sela, 0x26}, + {CX0342_CHANNEL_4_0_H_sela, 0x00}, + {CX0342_CHANNEL_4_1_L_sela, 0xe2}, + {CX0342_CHANNEL_4_1_H_sela, 0x00}, + {CX0342_CHANNEL_5_0_L_intla, 0x26}, + {CX0342_CHANNEL_5_1_L_intla, 0x29}, + {CX0342_CHANNEL_5_2_L_intla, 0xf0}, + {CX0342_CHANNEL_5_2_H_intla, 0x00}, + {CX0342_CHANNEL_5_3_L_intla, 0xf3}, + {CX0342_CHANNEL_5_3_H_intla, 0x00}, + {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24}, + {CX0342_CHANNEL_7_1_L_cds_pos, 0x02}, + {CX0342_TIMING_EN, 0x01}, +}; + +/* define the JPEG header */ +static void jpeg_define(u8 *jpeg_hdr, + int height, + int width) +{ + memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); + jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width; +} + +/* set the JPEG quality for sensor soi763a */ +static void jpeg_set_qual(u8 *jpeg_hdr, + int quality) +{ + int i, sc; + + if (quality < 50) + sc = 5000 / quality; + else + sc = 200 - quality * 2; + for (i = 0; i < 64; i++) { + jpeg_hdr[JPEG_QT0_OFFSET + i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + jpeg_hdr[JPEG_QT1_OFFSET + i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x0e, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* the returned value is in gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, u8 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x0d, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w_buf(struct gspca_dev *gspca_dev, + const struct cmd *p, int l) +{ + do { + reg_w(gspca_dev, p->reg, p->val); + p++; + } while (--l > 0); +} + +static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); + reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index); + reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value); + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01); + if (sd->bridge == BRIDGE_TP6800) + return 0; + msleep(5); + reg_r(gspca_dev, TP6800_R11_SIF_CONTROL); + if (gspca_dev->usb_buf[0] == 0) + return 0; + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); + return -1; /* error */ +} + +static void i2c_w_buf(struct gspca_dev *gspca_dev, + const struct cmd *p, int l) +{ + do { + i2c_w(gspca_dev, p->reg, p->val); + p++; + } while (--l > 0); +} + +static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int v; + + reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index); + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02); + msleep(5); + reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA); + v = gspca_dev->usb_buf[0]; + if (sd->bridge == BRIDGE_TP6800) + return v; + if (len > 1) { + reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2); + v |= (gspca_dev->usb_buf[0] << 8); + } + reg_r(gspca_dev, TP6800_R11_SIF_CONTROL); + if (gspca_dev->usb_buf[0] == 0) + return v; + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); + return -1; +} + +static void bulk_w(struct gspca_dev *gspca_dev, + u8 tag, + const u8 *data, + int length) +{ + struct usb_device *dev = gspca_dev->dev; + int count, actual_count, ret; + + if (gspca_dev->usb_err < 0) + return; + for (;;) { + count = length > BULK_OUT_SIZE - 1 + ? BULK_OUT_SIZE - 1 : length; + gspca_dev->usb_buf[0] = tag; + memcpy(&gspca_dev->usb_buf[1], data, count); + ret = usb_bulk_msg(dev, + usb_sndbulkpipe(dev, 3), + gspca_dev->usb_buf, count + 1, + &actual_count, 500); + if (ret < 0) { + pr_err("bulk write error %d tag=%02x\n", + ret, tag); + gspca_dev->usb_err = ret; + return; + } + length -= count; + if (length <= 0) + break; + data += count; + } +} + +static int probe_6810(struct gspca_dev *gspca_dev) +{ + u8 gpio; + int ret; + + reg_r(gspca_dev, TP6800_R18_GPIO_DATA); + gpio = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */ + reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00); + if (i2c_w(gspca_dev, 0x00, 0x00) >= 0) + return SENSOR_SOI763A; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */ + if (i2c_w(gspca_dev, 0x00, 0x00) >= 0) + return -2; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */ + ret = i2c_r(gspca_dev, 0x00, 1); + if (ret > 0) + return -3; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */ + ret = i2c_r(gspca_dev, 0x00, 1); + if (ret > 0) + return -4; + + ret = i2c_r(gspca_dev, 0x01, 1); + if (ret > 0) + return -5; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */ + ret = i2c_r(gspca_dev, 0x00, 2); + if (ret > 0) + return -6; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */ + ret = i2c_r(gspca_dev, 0x36, 2); + if (ret > 0) + return -7; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */ + reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10); + if (i2c_w(gspca_dev, 0xff, 0x00) >= 0) + return -8; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */ + ret = i2c_r(gspca_dev, 0x00, 1); + if (ret > 0) + return SENSOR_CX0342; + return -9; +} + +static void cx0342_6810_init(struct gspca_dev *gspca_dev) +{ + static const struct cmd reg_init_1[] = { + {TP6800_R2F_TIMING_CFG, 0x2f}, + {0x25, 0x02}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x80}, + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R18_GPIO_DATA, 0xe1}, + {TP6800_R18_GPIO_DATA, 0xc1}, + {TP6800_R18_GPIO_DATA, 0xe1}, + {TP6800_R11_SIF_CONTROL, 0x00}, + }; + static const struct cmd reg_init_2[] = { + {TP6800_R78_FORMAT, 0x48}, + {TP6800_R11_SIF_CONTROL, 0x00}, + }; + static const struct cmd sensor_init[] = { + {CX0342_OUTPUT_CTRL, 0x07}, + {CX0342_BYPASS_MODE, 0x58}, + {CX0342_GPXLTHD_L, 0x28}, + {CX0342_RBPXLTHD_L, 0x28}, + {CX0342_PLANETHD_L, 0x50}, + {CX0342_PLANETHD_H, 0x03}, + {CX0342_RB_GAP_L, 0xff}, + {CX0342_RB_GAP_H, 0x07}, + {CX0342_G_GAP_L, 0xff}, + {CX0342_G_GAP_H, 0x07}, + {CX0342_RST_OVERFLOW_L, 0x5c}, + {CX0342_RST_OVERFLOW_H, 0x01}, + {CX0342_DATA_OVERFLOW_L, 0xfc}, + {CX0342_DATA_OVERFLOW_H, 0x03}, + {CX0342_DATA_UNDERFLOW_L, 0x00}, + {CX0342_DATA_UNDERFLOW_H, 0x00}, + {CX0342_SYS_CTRL_0, 0x40}, + {CX0342_GLOBAL_GAIN, 0x01}, + {CX0342_CLOCK_GEN, 0x00}, + {CX0342_SYS_CTRL_0, 0x02}, + {CX0342_IDLE_CTRL, 0x05}, + {CX0342_ADCGN, 0x00}, + {CX0342_ADC_CTL, 0x00}, + {CX0342_LVRST_BLBIAS, 0x01}, + {CX0342_VTHSEL, 0x0b}, + {CX0342_RAMP_RIV, 0x0b}, + {CX0342_LDOSEL, 0x07}, + {CX0342_SPV_VALUE_L, 0x40}, + {CX0342_SPV_VALUE_H, 0x02}, + + {CX0342_AUTO_ADC_CALIB, 0x81}, + {CX0342_TIMING_EN, 0x01}, + }; + + reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1)); + reg_w_buf(gspca_dev, tp6810_cx_init_common, + ARRAY_SIZE(tp6810_cx_init_common)); + reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2)); + + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */ + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); + i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq)); +} + +static void soi763a_6810_init(struct gspca_dev *gspca_dev) +{ + static const struct cmd reg_init_1[] = { + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R18_GPIO_DATA, 0xe1}, + {0x25, 0x02}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x80}, + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R18_GPIO_DATA, 0xc1}, + }; + static const struct cmd reg_init_2[] = { + {TP6800_R78_FORMAT, 0x54}, + }; + static const struct cmd sensor_init[] = { + {0x00, 0x00}, + {0x01, 0x80}, + {0x02, 0x80}, + {0x03, 0x90}, + {0x04, 0x20}, + {0x05, 0x20}, + {0x06, 0x80}, + {0x07, 0x00}, + {0x08, 0xff}, + {0x09, 0xff}, + {0x0a, 0x76}, /* 7630 = soi673a */ + {0x0b, 0x30}, + {0x0c, 0x20}, + {0x0d, 0x20}, + {0x0e, 0xff}, + {0x0f, 0xff}, + {0x10, 0x41}, + {0x15, 0x14}, + {0x11, 0x40}, + {0x12, 0x48}, + {0x13, 0x80}, + {0x14, 0x80}, + {0x16, 0x03}, + {0x28, 0xb0}, + {0x71, 0x20}, + {0x75, 0x8e}, + {0x17, 0x1b}, + {0x18, 0xbd}, + {0x19, 0x05}, + {0x1a, 0xf6}, + {0x1b, 0x04}, + {0x1c, 0x7f}, /* omnivision */ + {0x1d, 0xa2}, + {0x1e, 0x00}, + {0x1f, 0x00}, + {0x20, 0x45}, + {0x21, 0x80}, + {0x22, 0x80}, + {0x23, 0xee}, + {0x24, 0x50}, + {0x25, 0x7a}, + {0x26, 0xa0}, + {0x27, 0x9a}, + {0x29, 0x30}, + {0x2a, 0x80}, + {0x2b, 0x00}, + {0x2c, 0xac}, + {0x2d, 0x05}, + {0x2e, 0x80}, + {0x2f, 0x3c}, + {0x30, 0x22}, + {0x31, 0x00}, + {0x32, 0x86}, + {0x33, 0x08}, + {0x34, 0xff}, + {0x35, 0xff}, + {0x36, 0xff}, + {0x37, 0xff}, + {0x38, 0xff}, + {0x39, 0xff}, + {0x3a, 0xfe}, + {0x3b, 0xfe}, + {0x3c, 0xfe}, + {0x3d, 0xfe}, + {0x3e, 0xfe}, + {0x3f, 0x71}, + {0x40, 0xff}, + {0x41, 0xff}, + {0x42, 0xff}, + {0x43, 0xff}, + {0x44, 0xff}, + {0x45, 0xff}, + {0x46, 0xff}, + {0x47, 0xff}, + {0x48, 0xff}, + {0x49, 0xff}, + {0x4a, 0xfe}, + {0x4b, 0xff}, + {0x4c, 0x00}, + {0x4d, 0x00}, + {0x4e, 0xff}, + {0x4f, 0xff}, + {0x50, 0xff}, + {0x51, 0xff}, + {0x52, 0xff}, + {0x53, 0xff}, + {0x54, 0xff}, + {0x55, 0xff}, + {0x56, 0xff}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5a, 0xff}, + {0x5b, 0xfe}, + {0x5c, 0xff}, + {0x5d, 0x8f}, + {0x5e, 0xff}, + {0x5f, 0x8f}, + {0x60, 0xa2}, + {0x61, 0x4a}, + {0x62, 0xf3}, + {0x63, 0x75}, + {0x64, 0xf0}, + {0x65, 0x00}, + {0x66, 0x55}, + {0x67, 0x92}, + {0x68, 0xa0}, + {0x69, 0x4a}, + {0x6a, 0x22}, + {0x6b, 0x00}, + {0x6c, 0x33}, + {0x6d, 0x44}, + {0x6e, 0x22}, + {0x6f, 0x84}, + {0x70, 0x0b}, + {0x72, 0x10}, + {0x73, 0x50}, + {0x74, 0x21}, + {0x76, 0x00}, + {0x77, 0xa5}, + {0x78, 0x80}, + {0x79, 0x80}, + {0x7a, 0x80}, + {0x7b, 0xe2}, + {0x7c, 0x00}, + {0x7d, 0xf7}, + {0x7e, 0x00}, + {0x7f, 0x00}, + }; + + reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1)); + reg_w_buf(gspca_dev, tp6810_ov_init_common, + ARRAY_SIZE(tp6810_ov_init_common)); + reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2)); + + i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(10); + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); +} + +/* set the gain and exposure */ +static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain, + s32 blue, s32 red) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_CX0342) { + expo = (expo << 2) - 1; + i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8); + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H, + gain >> 8); + i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain); + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H, + gain >> 8); + i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain); + if (sd->sensor == SENSOR_CX0342) { + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, + blue >> 8); + i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue); + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, + red >> 8); + i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red); + } + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, + sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81); + return; + } + + /* soi763a */ + i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */ + expo); +/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */ + i2c_w(gspca_dev, 0x00, /* gain */ + gain); +} + +/* set the JPEG quantization tables */ +static void set_dqt(struct gspca_dev *gspca_dev, u8 q) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* update the jpeg quantization tables */ + PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q); + sd->quality = q; + if (q > 16) + q = 16; + if (sd->sensor == SENSOR_SOI763A) + jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]); + else + memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1], + DQT[q], sizeof DQT[0]); +} + +/* set the JPEG compression quality factor */ +static void setquality(struct gspca_dev *gspca_dev, s32 q) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (q != 16) + q = 15 - q; + + reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00); + reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04); + reg_w(gspca_dev, TP6800_R79_QUALITY, q); + + /* auto quality */ + if (q == 15 && sd->bridge == BRIDGE_TP6810) { + msleep(4); + reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19); + } +} + +static const u8 color_null[18] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 color_gain[NSENSORS][18] = { +[SENSOR_CX0342] = + {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */ + 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */ + 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */ +[SENSOR_SOI763A] = + {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */ + 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */ + 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */ +}; + +static void setgamma(struct gspca_dev *gspca_dev, s32 gamma) +{ + struct sd *sd = (struct sd *) gspca_dev; +#define NGAMMA 6 + static const u8 gamma_tb[NGAMMA][3][1024] = { + { /* gamma 0 - from tp6800 + soi763a */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, + 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, + 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, + 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, + 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, + 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, + 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, + 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, + 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, + 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, + 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, + 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, + 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, + 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, + 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, + 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, + 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, + 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, + 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, + 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, + 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, + 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, + 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, + 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, + 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, + 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, + 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, + 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, + 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, + 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, + 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, + 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, + 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, + 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, + 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, + 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, + 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, + 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, + 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, + 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} + }, + { /* gamma 1 - from tp6810 + soi763a */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a, + 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e, + 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27, + 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f, + 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35, + 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43, + 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49, + 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, + 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, + 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, + 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, + 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61, + 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, + 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, + 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84, + 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88, + 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, + 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, + 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, + 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, + 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab, + 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, + 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d, + 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15, + 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25, + 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c, + 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31, + 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, + 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43, + 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, + 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, + 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, + 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55, + 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59, + 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, + 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, + 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65, + 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, + 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, + 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, + 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, + 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78, + 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, + 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, + 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, + 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, + 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, + 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07, + 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e, + 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16, + 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23, + 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, + 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30, + 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35, + 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a, + 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, + 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43, + 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47, + 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b, + 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, + 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53, + 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, + 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, + 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, + 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, + 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, + 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, + 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, + 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, + 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, + 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, + 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, + 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77, + 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, + 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, + 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, + 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, + 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + }, + { /* gamma 2 */ + {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c, + 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22, + 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c, + 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, + 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b, + 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43, + 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49, + 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f, + 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55, + 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a, + 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, + 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, + 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, + 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, + 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, + 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, + 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, + 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, + 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, + 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89, + 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, + 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, + 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, + 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11, + 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a, + 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, + 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b, + 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33, + 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39, + 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f, + 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, + 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a, + 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, + 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, + 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58, + 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, + 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63, + 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, + 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, + 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, + 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, + 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, + 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84, + 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, + 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08, + 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e, + 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27, + 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f, + 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37, + 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c, + 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43, + 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, + 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, + 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, + 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, + 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, + 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, + 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, + 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, + 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, + 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, + 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, + 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, + 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, + 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, + 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, + 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, + 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} + }, + { /* gamma 3 - from tp6810 + cx0342 */ + {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15, + 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23, + 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f, + 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43, + 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53, + 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a, + 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61, + 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67, + 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, + 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73, + 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, + 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, + 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, + 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, + 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, + 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97, + 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, + 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, + 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, + 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, + 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, + 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, + 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, + 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10, + 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b, + 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f, + 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38, + 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, + 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, + 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, + 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, + 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58, + 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, + 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, + 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66, + 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, + 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, + 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, + 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, + 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, + 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, + 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98, + 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, + 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, + 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d, + 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, + 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49, + 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50, + 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58, + 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, + 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, + 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, + 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74, + 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79, + 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, + 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, + 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, + 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92, + 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0, + 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, + 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, + 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, + 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, + 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, + 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, + 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, + 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, + 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + }, + { /* gamma 4 - from tp6800 + soi763a */ + {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f, + 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d, + 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a, + 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45, + 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f, + 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58, + 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60, + 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67, + 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e, + 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74, + 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a, + 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80, + 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, + 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b, + 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91, + 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96, + 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, + 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, + 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc, + 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, + 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9, + 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, + 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, + 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, + 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, + 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15, + 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, + 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c, + 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, + 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, + 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c, + 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54, + 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a, + 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, + 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65, + 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, + 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, + 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, + 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, + 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, + 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, + 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, + 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b, + 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28, + 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, + 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, + 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50, + 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58, + 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, + 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65, + 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c, + 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, + 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, + 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, + 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, + 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, + 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, + 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, + 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, + 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, + 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} + }, + { /* gamma 5 */ + {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21, + 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, + 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, + 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b, + 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61, + 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66, + 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, + 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, + 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, + 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, + 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e, + 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82, + 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, + 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89, + 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d, + 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90, + 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0, + 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, + 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, + 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, + 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, + 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, + 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, + 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, + 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19, + 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, + 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45, + 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, + 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50, + 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54, + 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59, + 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d, + 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, + 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, + 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, + 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, + 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, + 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75, + 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78, + 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, + 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81, + 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, + 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, + 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, + 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, + 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, + 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, + 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, + 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e, + 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, + 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, + 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, + 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59, + 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f, + 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63, + 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, + 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, + 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, + 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, + 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, + 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, + 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83, + 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, + 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89, + 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, + 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, + 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92, + 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95, + 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, + 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, + 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9, + 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, + 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, + 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + }; + + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); + if (sd->bridge == BRIDGE_TP6810) + reg_w(gspca_dev, 0x02, 0x28); +/* msleep(50); */ + bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024); + bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024); + bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024); + if (sd->bridge == BRIDGE_TP6810) { + int i; + + reg_w(gspca_dev, 0x02, 0x2b); + reg_w(gspca_dev, 0x02, 0x28); + for (i = 0; i < 6; i++) + reg_w(gspca_dev, TP6800_R55_GAMMA_R, + gamma_tb[gamma][0][i]); + reg_w(gspca_dev, 0x02, 0x2b); + reg_w(gspca_dev, 0x02, 0x28); + for (i = 0; i < 6; i++) + reg_w(gspca_dev, TP6800_R56_GAMMA_G, + gamma_tb[gamma][1][i]); + reg_w(gspca_dev, 0x02, 0x2b); + reg_w(gspca_dev, 0x02, 0x28); + for (i = 0; i < 6; i++) + reg_w(gspca_dev, TP6800_R57_GAMMA_B, + gamma_tb[gamma][2][i]); + reg_w(gspca_dev, 0x02, 0x28); + } + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); +/* msleep(50); */ +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge == BRIDGE_TP6800) { + val |= 0x08; /* grid compensation enable */ + if (gspca_dev->width == 640) + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + else + val |= 0x04; /* scaling down enable */ + reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val); + } else { + val = (val << 5) | 0x08; + reg_w(gspca_dev, 0x59, val); + } +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->ag_cnt = val ? AG_CNT_START : -1; +} + +/* set the resolution for sensor cx0342 */ +static void set_resolution(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); + if (gspca_dev->width == 320) { + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06); + msleep(100); + i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); + msleep(100); + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ + reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d); + i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01); + } else { + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05); + msleep(100); + i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); + msleep(100); + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09); + i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00); + } + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01); + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342], + ARRAY_SIZE(color_gain[0])); + setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); + if (sd->sensor == SENSOR_SOI763A) + setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); +} + +/* convert the frame rate to a tp68x0 value */ +static int get_fr_idx(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + if (sd->bridge == BRIDGE_TP6800) { + for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) { + if (sd->framerate >= rates[i]) + break; + } + i = 6 - i; /* 1 = 5fps .. 6 = 30fps */ + + /* 640x480 * 30 fps does not work */ + if (i == 6 /* if 30 fps */ + && gspca_dev->width == 640) + i = 0x05; /* 15 fps */ + } else { + for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) { + if (sd->framerate >= rates_6810[i]) + break; + } + i = 7 - i; /* 3 = 5fps .. 7 = 30fps */ + + /* 640x480 * 30 fps does not work */ + if (i == 7 /* if 30 fps */ + && gspca_dev->width == 640) + i = 6; /* 15 fps */ + i |= 0x80; /* clock * 1 */ + } + return i; +} + +static void setframerate(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 fr_idx; + + fr_idx = get_fr_idx(gspca_dev); + + if (sd->bridge == BRIDGE_TP6810) { + reg_r(gspca_dev, 0x7b); + reg_w(gspca_dev, 0x7b, + sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90); + if (val >= 128) + fr_idx = 0xf0; /* lower frame rate */ + } + + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx); + + if (sd->sensor == SENSOR_CX0342) + i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); +} + +static void setrgain(struct gspca_dev *gspca_dev, s32 rgain) +{ + i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8); + i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain); + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80); +} + +static int sd_setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 val = gspca_dev->gain->val; + + if (sd->sensor == SENSOR_CX0342) { + s32 old = gspca_dev->gain->cur.val ? + gspca_dev->gain->cur.val : 1; + + sd->blue->val = sd->blue->val * val / old; + if (sd->blue->val > 4095) + sd->blue->val = 4095; + sd->red->val = sd->red->val * val / old; + if (sd->red->val > 4095) + sd->red->val = 4095; + } + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_CX0342) + setexposure(gspca_dev, gspca_dev->exposure->val, + gspca_dev->gain->val, + sd->blue->val, sd->red->val); + else + setexposure(gspca_dev, gspca_dev->exposure->val, + gspca_dev->gain->val, 0, 0); + } + return gspca_dev->usb_err; +} + +static void setbgain(struct gspca_dev *gspca_dev, s32 bgain) +{ + i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8); + i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain); + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->bridge = id->driver_info; + + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ? + framerates : framerates_6810; + + sd->framerate = 30; /* default: 30 fps */ + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd tp6800_preinit[] = { + {TP6800_R10_SIF_TYPE, 0x01}, /* sif */ + {TP6800_R11_SIF_CONTROL, 0x01}, + {TP6800_R15_GPIO_PU, 0x9f}, + {TP6800_R16_GPIO_PD, 0x9f}, + {TP6800_R17_GPIO_IO, 0x80}, + {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */ + }; + static const struct cmd tp6810_preinit[] = { + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R15_GPIO_PU, 0x6f}, + {TP6800_R16_GPIO_PD, 0x40}, + {TP6800_R17_GPIO_IO, 0x9f}, + {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */ + }; + + if (sd->bridge == BRIDGE_TP6800) + reg_w_buf(gspca_dev, tp6800_preinit, + ARRAY_SIZE(tp6800_preinit)); + else + reg_w_buf(gspca_dev, tp6810_preinit, + ARRAY_SIZE(tp6810_preinit)); + msleep(15); + reg_r(gspca_dev, TP6800_R18_GPIO_DATA); + PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]); +/* values: + * 0x80: snapshot button + * 0x40: LED + * 0x20: (bridge / sensor) reset for tp6810 ? + * 0x07: sensor type ? + */ + + /* guess the sensor type */ + if (force_sensor >= 0) { + sd->sensor = force_sensor; + } else { + if (sd->bridge == BRIDGE_TP6800) { +/*fixme: not sure this is working*/ + switch (gspca_dev->usb_buf[0] & 0x07) { + case 0: + sd->sensor = SENSOR_SOI763A; + break; + case 1: + sd->sensor = SENSOR_CX0342; + break; + } + } else { + int sensor; + + sensor = probe_6810(gspca_dev); + if (sensor < 0) { + pr_warn("Unknown sensor %d - forced to soi763a\n", + -sensor); + sensor = SENSOR_SOI763A; + } + sd->sensor = sensor; + } + } + if (sd->sensor == SENSOR_SOI763A) { + pr_info("Sensor soi763a\n"); + if (sd->bridge == BRIDGE_TP6810) { + soi763a_6810_init(gspca_dev); + } + } else { + pr_info("Sensor cx0342\n"); + if (sd->bridge == BRIDGE_TP6810) { + cx0342_6810_init(gspca_dev); + } + } + + set_dqt(gspca_dev, 0); + return 0; +} + +/* This function is called before choosing the alt setting */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd cx_sensor_init[] = { + {CX0342_AUTO_ADC_CALIB, 0x81}, + {CX0342_EXPO_LINE_L, 0x37}, + {CX0342_EXPO_LINE_H, 0x01}, + {CX0342_RAW_GRGAIN_L, 0x00}, + {CX0342_RAW_GBGAIN_L, 0x00}, + {CX0342_RAW_RGAIN_L, 0x00}, + {CX0342_RAW_BGAIN_L, 0x00}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd cx_bridge_init[] = { + {0x4d, 0x00}, + {0x4c, 0xff}, + {0x4e, 0xff}, + {0x4f, 0x00}, + }; + static const struct cmd ov_sensor_init[] = { + {0x10, 0x75}, /* exposure */ + {0x76, 0x03}, + {0x00, 0x00}, /* gain */ + }; + static const struct cmd ov_bridge_init[] = { + {0x7b, 0x90}, + {TP6800_R3F_FRAME_RATE, 0x87}, + }; + + if (sd->bridge == BRIDGE_TP6800) + return 0; + if (sd->sensor == SENSOR_CX0342) { + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87); + i2c_w_buf(gspca_dev, cx_sensor_init, + ARRAY_SIZE(cx_sensor_init)); + reg_w_buf(gspca_dev, cx_bridge_init, + ARRAY_SIZE(cx_bridge_init)); + bulk_w(gspca_dev, 0x03, color_null, sizeof color_null); + reg_w(gspca_dev, 0x59, 0x40); + } else { + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); + i2c_w_buf(gspca_dev, ov_sensor_init, + ARRAY_SIZE(ov_sensor_init)); + reg_r(gspca_dev, 0x7b); + reg_w_buf(gspca_dev, ov_bridge_init, + ARRAY_SIZE(ov_bridge_init)); + } + reg_w(gspca_dev, TP6800_R78_FORMAT, + gspca_dev->curr_mode ? 0x00 : 0x01); + return gspca_dev->usb_err; +} + +static void set_led(struct gspca_dev *gspca_dev, int on) +{ + u8 data; + + reg_r(gspca_dev, TP6800_R18_GPIO_DATA); + data = gspca_dev->usb_buf[0]; + if (on) + data &= ~0x40; + else + data |= 0x40; + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data); +} + +static void cx0342_6800_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd reg_init[] = { + /* fixme: is this useful? */ + {TP6800_R17_GPIO_IO, 0x9f}, + {TP6800_R16_GPIO_PD, 0x40}, + {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */ + {TP6800_R50, 0x00}, + {TP6800_R51, 0x00}, + {TP6800_R52, 0xff}, + {TP6800_R53, 0x03}, + {TP6800_R54_DARK_CFG, 0x07}, + {TP6800_R5C_EDGE_THRLD, 0x40}, + {TP6800_R7A_BLK_THRLD, 0x40}, + {TP6800_R2F_TIMING_CFG, 0x17}, + {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */ + {TP6800_R37_FRONT_DARK_ST, 0x00}, + {TP6800_R38_FRONT_DARK_END, 0x00}, + {TP6800_R39_REAR_DARK_ST_L, 0x00}, + {TP6800_R3A_REAR_DARK_ST_H, 0x00}, + {TP6800_R3B_REAR_DARK_END_L, 0x00}, + {TP6800_R3C_REAR_DARK_END_H, 0x00}, + {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00}, + {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00}, + {TP6800_R21_ENDP_1_CTL, 0x03}, + + {TP6800_R31_PIXEL_START, 0x0b}, + {TP6800_R32_PIXEL_END_L, 0x8a}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x0e}, + {TP6800_R35_LINE_END_L, 0xf4}, + {TP6800_R36_LINE_END_H, 0x01}, + {TP6800_R78_FORMAT, 0x00}, + {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */ + }; + static const struct cmd sensor_init[] = { + {CX0342_OUTPUT_CTRL, 0x07}, + {CX0342_BYPASS_MODE, 0x58}, + {CX0342_GPXLTHD_L, 0x16}, + {CX0342_RBPXLTHD_L, 0x16}, + {CX0342_PLANETHD_L, 0xc0}, + {CX0342_PLANETHD_H, 0x03}, + {CX0342_RB_GAP_L, 0xff}, + {CX0342_RB_GAP_H, 0x07}, + {CX0342_G_GAP_L, 0xff}, + {CX0342_G_GAP_H, 0x07}, + {CX0342_RST_OVERFLOW_L, 0x5c}, + {CX0342_RST_OVERFLOW_H, 0x01}, + {CX0342_DATA_OVERFLOW_L, 0xfc}, + {CX0342_DATA_OVERFLOW_H, 0x03}, + {CX0342_DATA_UNDERFLOW_L, 0x00}, + {CX0342_DATA_UNDERFLOW_H, 0x00}, + {CX0342_SYS_CTRL_0, 0x40}, + {CX0342_GLOBAL_GAIN, 0x01}, + {CX0342_CLOCK_GEN, 0x00}, + {CX0342_SYS_CTRL_0, 0x02}, + {CX0342_IDLE_CTRL, 0x05}, + {CX0342_ADCGN, 0x00}, + {CX0342_ADC_CTL, 0x00}, + {CX0342_LVRST_BLBIAS, 0x01}, + {CX0342_VTHSEL, 0x0b}, + {CX0342_RAMP_RIV, 0x0b}, + {CX0342_LDOSEL, 0x07}, + {CX0342_SPV_VALUE_L, 0x40}, + {CX0342_SPV_VALUE_H, 0x02}, + }; + + reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init)); + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); + i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq)); + reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10); + reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00); + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01); + if (sd->sensor == SENSOR_CX0342) + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), + v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + else + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); + set_led(gspca_dev, 1); + set_resolution(gspca_dev); +} + +static void cx0342_6810_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd sensor_init_2[] = { + {CX0342_EXPO_LINE_L, 0x6f}, + {CX0342_EXPO_LINE_H, 0x02}, + {CX0342_RAW_GRGAIN_L, 0x00}, + {CX0342_RAW_GBGAIN_L, 0x00}, + {CX0342_RAW_RGAIN_L, 0x00}, + {CX0342_RAW_BGAIN_L, 0x00}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd bridge_init_2[] = { + {0x4d, 0x00}, + {0x4c, 0xff}, + {0x4e, 0xff}, + {0x4f, 0x00}, + {TP6800_R7A_BLK_THRLD, 0x00}, + {TP6800_R79_QUALITY, 0x04}, + {TP6800_R79_QUALITY, 0x01}, + }; + static const struct cmd bridge_init_3[] = { + {TP6800_R31_PIXEL_START, 0x08}, + {TP6800_R32_PIXEL_END_L, 0x87}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x0e}, + {TP6800_R35_LINE_END_L, 0xf4}, + {TP6800_R36_LINE_END_H, 0x01}, + }; + static const struct cmd sensor_init_3[] = { + {CX0342_AUTO_ADC_CALIB, 0x81}, + {CX0342_EXPO_LINE_L, 0x6f}, + {CX0342_EXPO_LINE_H, 0x02}, + {CX0342_RAW_GRGAIN_L, 0x00}, + {CX0342_RAW_GBGAIN_L, 0x00}, + {CX0342_RAW_RGAIN_L, 0x00}, + {CX0342_RAW_BGAIN_L, 0x00}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd bridge_init_5[] = { + {0x4d, 0x00}, + {0x4c, 0xff}, + {0x4e, 0xff}, + {0x4f, 0x00}, + }; + static const struct cmd sensor_init_4[] = { + {CX0342_EXPO_LINE_L, 0xd3}, + {CX0342_EXPO_LINE_H, 0x01}, +/*fixme: gains, but 00..80 only*/ + {CX0342_RAW_GRGAIN_L, 0x40}, + {CX0342_RAW_GBGAIN_L, 0x40}, + {CX0342_RAW_RGAIN_L, 0x40}, + {CX0342_RAW_BGAIN_L, 0x40}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd sensor_init_5[] = { + {CX0342_IDLE_CTRL, 0x05}, + {CX0342_ADCGN, 0x00}, + {CX0342_ADC_CTL, 0x00}, + {CX0342_LVRST_BLBIAS, 0x01}, + {CX0342_VTHSEL, 0x0b}, + {CX0342_RAMP_RIV, 0x0b}, + {CX0342_LDOSEL, 0x07}, + {CX0342_SPV_VALUE_L, 0x40}, + {CX0342_SPV_VALUE_H, 0x02}, + {CX0342_AUTO_ADC_CALIB, 0x81}, + }; + + reg_w(gspca_dev, 0x22, gspca_dev->alt); + i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2)); + reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2)); + reg_w_buf(gspca_dev, tp6810_cx_init_common, + ARRAY_SIZE(tp6810_cx_init_common)); + reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3)); + if (gspca_dev->curr_mode) { + reg_w(gspca_dev, 0x4a, 0x7f); + reg_w(gspca_dev, 0x07, 0x05); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + } else { + reg_w(gspca_dev, 0x4a, 0xff); + reg_w(gspca_dev, 0x07, 0x85); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ + } + setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); + reg_w_buf(gspca_dev, tp6810_bridge_start, + ARRAY_SIZE(tp6810_bridge_start)); + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342], + ARRAY_SIZE(color_gain[0])); + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87); + i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3)); + reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5)); + i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4)); + reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5)); + i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5)); + + set_led(gspca_dev, 1); +/* setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */ +} + +static void soi763a_6800_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd reg_init[] = { + {TP6800_R79_QUALITY, 0x04}, + {TP6800_R79_QUALITY, 0x01}, + {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */ + + {TP6800_R50, 0x00}, + {TP6800_R51, 0x00}, + {TP6800_R52, 0xff}, + {TP6800_R53, 0x03}, + {TP6800_R54_DARK_CFG, 0x07}, + {TP6800_R5C_EDGE_THRLD, 0x40}, + + {TP6800_R79_QUALITY, 0x03}, + {TP6800_R7A_BLK_THRLD, 0x40}, + + {TP6800_R2F_TIMING_CFG, 0x46}, + {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */ + {TP6800_R37_FRONT_DARK_ST, 0x00}, + {TP6800_R38_FRONT_DARK_END, 0x00}, + {TP6800_R39_REAR_DARK_ST_L, 0x00}, + {TP6800_R3A_REAR_DARK_ST_H, 0x00}, + {TP6800_R3B_REAR_DARK_END_L, 0x00}, + {TP6800_R3C_REAR_DARK_END_H, 0x00}, + {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00}, + {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00}, + {TP6800_R21_ENDP_1_CTL, 0x03}, + + {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */ + {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */ + + {TP6800_R31_PIXEL_START, 0x1b}, + {TP6800_R32_PIXEL_END_L, 0x9a}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x0f}, + {TP6800_R35_LINE_END_L, 0xf4}, + {TP6800_R36_LINE_END_H, 0x01}, + {TP6800_R78_FORMAT, 0x01}, /* qvga */ + {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */ + {TP6800_R1A_SIF_TX_DATA2, 0x00}, + }; + static const struct cmd sensor_init[] = { + {0x12, 0x48}, /* mirror - RGB */ + {0x13, 0xa0}, /* clock - no AGC nor AEC */ + {0x03, 0xa4}, /* saturation */ + {0x04, 0x30}, /* hue */ + {0x05, 0x88}, /* contrast */ + {0x06, 0x60}, /* brightness */ + {0x10, 0x41}, /* AEC */ + {0x11, 0x40}, /* clock rate */ + {0x13, 0xa0}, + {0x14, 0x00}, /* 640x480 */ + {0x15, 0x14}, + {0x1f, 0x41}, + {0x20, 0x80}, + {0x23, 0xee}, + {0x24, 0x50}, + {0x25, 0x7a}, + {0x26, 0x00}, + {0x27, 0xe2}, + {0x28, 0xb0}, + {0x2a, 0x00}, + {0x2b, 0x00}, + {0x2d, 0x81}, + {0x2f, 0x9d}, + {0x60, 0x80}, + {0x61, 0x00}, + {0x62, 0x88}, + {0x63, 0x11}, + {0x64, 0x89}, + {0x65, 0x00}, + {0x67, 0x94}, + {0x68, 0x7a}, + {0x69, 0x0f}, + {0x6c, 0x80}, + {0x6d, 0x80}, + {0x6e, 0x80}, + {0x6f, 0xff}, + {0x71, 0x20}, + {0x74, 0x20}, + {0x75, 0x86}, + {0x77, 0xb5}, + {0x17, 0x18}, /* H href start */ + {0x18, 0xbf}, /* H href end */ + {0x19, 0x03}, /* V start */ + {0x1a, 0xf8}, /* V end */ + {0x01, 0x80}, /* blue gain */ + {0x02, 0x80}, /* red gain */ + }; + + reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init)); + + i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(10); + + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); + + reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10); + reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00); + + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); + + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A], + ARRAY_SIZE(color_gain[0])); + + set_led(gspca_dev, 1); + if (sd->sensor == SENSOR_CX0342) + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), + v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + else + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); + if (sd->sensor == SENSOR_SOI763A) + setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); + setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); +} + +static void soi763a_6810_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd bridge_init_2[] = { + {TP6800_R7A_BLK_THRLD, 0x00}, + {TP6800_R79_QUALITY, 0x04}, + {TP6800_R79_QUALITY, 0x01}, + }; + static const struct cmd bridge_init_3[] = { + {TP6800_R31_PIXEL_START, 0x20}, + {TP6800_R32_PIXEL_END_L, 0x9f}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x13}, + {TP6800_R35_LINE_END_L, 0xf8}, + {TP6800_R36_LINE_END_H, 0x01}, + }; + static const struct cmd bridge_init_6[] = { + {0x08, 0xff}, + {0x09, 0xff}, + {0x0a, 0x5f}, + {0x0b, 0x80}, + }; + + reg_w(gspca_dev, 0x22, gspca_dev->alt); + bulk_w(gspca_dev, 0x03, color_null, sizeof color_null); + reg_w(gspca_dev, 0x59, 0x40); + if (sd->sensor == SENSOR_CX0342) + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), + v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + else + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); + reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2)); + reg_w_buf(gspca_dev, tp6810_ov_init_common, + ARRAY_SIZE(tp6810_ov_init_common)); + reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3)); + if (gspca_dev->curr_mode) { + reg_w(gspca_dev, 0x4a, 0x7f); + reg_w(gspca_dev, 0x07, 0x05); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + } else { + reg_w(gspca_dev, 0x4a, 0xff); + reg_w(gspca_dev, 0x07, 0x85); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ + } + setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); + reg_w_buf(gspca_dev, tp6810_bridge_start, + ARRAY_SIZE(tp6810_bridge_start)); + + if (gspca_dev->curr_mode) { + reg_w(gspca_dev, 0x4f, 0x00); + reg_w(gspca_dev, 0x4e, 0x7c); + } + + reg_w(gspca_dev, 0x00, 0x00); + + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A], + ARRAY_SIZE(color_gain[0])); + set_led(gspca_dev, 1); + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0); + if (sd->sensor == SENSOR_CX0342) + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), + v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + else + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); + reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6)); +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width); + set_dqt(gspca_dev, sd->quality); + if (sd->bridge == BRIDGE_TP6800) { + if (sd->sensor == SENSOR_CX0342) + cx0342_6800_start(gspca_dev); + else + soi763a_6800_start(gspca_dev); + } else { + if (sd->sensor == SENSOR_CX0342) + cx0342_6810_start(gspca_dev); + else + soi763a_6810_start(gspca_dev); + reg_w_buf(gspca_dev, tp6810_late_start, + ARRAY_SIZE(tp6810_late_start)); + reg_w(gspca_dev, 0x80, 0x03); + reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e); + + if (sd->sensor == SENSOR_CX0342) + setexposure(gspca_dev, + v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), + v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + else + setexposure(gspca_dev, + v4l2_ctrl_g_ctrl(gspca_dev->exposure), + v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); + if (sd->sensor == SENSOR_SOI763A) + setquality(gspca_dev, + v4l2_ctrl_g_ctrl(sd->jpegqual)); + if (sd->bridge == BRIDGE_TP6810) + setautogain(gspca_dev, + v4l2_ctrl_g_ctrl(gspca_dev->autogain)); + } + + setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge == BRIDGE_TP6800) + reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03); + set_led(gspca_dev, 0); + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* the start of frame contains: + * ff d8 + * ff fe + * width / 16 + * height / 8 + * quality + */ + if (sd->bridge == BRIDGE_TP6810) { + if (*data != 0x5a) { +/*fixme: don't discard the whole frame..*/ + if (*data == 0xaa || *data == 0x00) + return; + if (*data > 0xc0) { + PDEBUG(D_FRAM, "bad frame"); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + } + data++; + len--; + if (*data == 0xff && data[1] == 0xd8) { +/*fixme: there may be information in the 4 high bits*/ + if ((data[6] & 0x0f) != sd->quality) + set_dqt(gspca_dev, data[6] & 0x0f); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 7, len - 7); + } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) { + gspca_frame_add(gspca_dev, LAST_PACKET, + data, len); + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, len); + } + return; + } + + switch (*data) { + case 0x55: + gspca_frame_add(gspca_dev, LAST_PACKET, data, 0); + + if (len < 8 + || data[1] != 0xff || data[2] != 0xd8 + || data[3] != 0xff || data[4] != 0xfe) { + + /* Have only seen this with corrupt frames */ + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + if (data[7] != sd->quality) + set_dqt(gspca_dev, data[7]); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 8, len - 8); + break; + case 0xaa: + gspca_dev->last_packet_type = DISCARD_PACKET; + break; + case 0xcc: + if (data[1] != 0xff || data[2] != 0xd8) + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 1, len - 1); + else + gspca_dev->last_packet_type = DISCARD_PACKET; + break; + } +} + +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret, alen; + int luma, expo; + + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt > 5) + return; + switch (sd->ag_cnt) { +/* case 5: */ + default: + reg_w(gspca_dev, 0x7d, 0x00); + break; + case 4: + reg_w(gspca_dev, 0x27, 0xb0); + break; + case 3: + reg_w(gspca_dev, 0x0c, 0x01); + break; + case 2: + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x02), + gspca_dev->usb_buf, + 32, + &alen, + 500); + if (ret < 0) { + pr_err("bulk err %d\n", ret); + break; + } + /* values not used (unknown) */ + break; + case 1: + reg_w(gspca_dev, 0x27, 0xd0); + break; + case 0: + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x02), + gspca_dev->usb_buf, + 32, + &alen, + 500); + if (ret < 0) { + pr_err("bulk err %d\n", ret); + break; + } + luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] + + (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] + + (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] + + (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] + + (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] + + (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] + + (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] + + (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28]) + / 8; + if (gspca_dev->width == 640) + luma /= 4; + reg_w(gspca_dev, 0x7d, 0x00); + + expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + ret = gspca_expo_autogain(gspca_dev, luma, + 60, /* desired luma */ + 6, /* dead zone */ + 2, /* gain knee */ + 70); /* expo knee */ + sd->ag_cnt = AG_CNT_START; + if (sd->bridge == BRIDGE_TP6810) { + int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + if ((expo >= 128 && new_expo < 128) + || (expo < 128 && new_expo >= 128)) + setframerate(gspca_dev, new_expo); + } + break; + } +} + +/* get stream parameters (framerate) */ +static void sd_get_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + int fr, i; + + cp->capability |= V4L2_CAP_TIMEPERFRAME; + tpf->numerator = 1; + i = get_fr_idx(gspca_dev); + if (i & 0x80) { + if (sd->bridge == BRIDGE_TP6800) + fr = rates[6 - (i & 0x07)]; + else + fr = rates_6810[7 - (i & 0x07)]; + } else { + fr = rates[6 - i]; + } + tpf->denominator = fr; +} + +/* set stream parameters (framerate) */ +static void sd_set_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + int fr, i; + + sd->framerate = tpf->denominator / tpf->numerator; + if (gspca_dev->streaming) + setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + + /* Return the actual framerate */ + i = get_fr_idx(gspca_dev); + if (i & 0x80) + fr = rates_6810[7 - (i & 0x07)]; + else + fr = rates[6 - i]; + tpf->numerator = 1; + tpf->denominator = fr; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor != SENSOR_SOI763A) + return -ENOTTY; + v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor != SENSOR_SOI763A) + return -ENOTTY; + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAMMA: + setgamma(gspca_dev, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + setbgain(gspca_dev, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + setrgain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + sd_setgain(gspca_dev); + break; + case V4L2_CID_AUTOGAIN: + if (ctrl->val) + break; + sd_setgain(gspca_dev); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + jpeg_set_qual(sd->jpeg_hdr, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e); + if (sd->sensor == SENSOR_CX0342) { + sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 4095, 1, 256); + sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256); + } + if (sd->sensor == SENSOR_SOI763A) + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 15, 1, 3); + else + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 4095, 1, 256); + sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 3, 1, 2); + sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAMMA, 0, NGAMMA - 1, 1, + (sd->sensor == SENSOR_SOI763A && + sd->bridge == BRIDGE_TP6800) ? 0 : 1); + if (sd->bridge == BRIDGE_TP6810) + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor == SENSOR_SOI763A) + sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + if (gspca_dev->autogain) + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + else + v4l2_ctrl_cluster(2, &gspca_dev->exposure); + return 0; +} + +static const struct sd_desc sd_desc = { + .name = KBUILD_MODNAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_dq_callback, + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, +}; + +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800}, + {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static int sd_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = KBUILD_MODNAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); + +module_param(force_sensor, int, 0644); +MODULE_PARM_DESC(force_sensor, + "Force sensor. 0: cx0342, 1: soi763a"); diff --git a/drivers/media/usb/gspca/tv8532.c b/drivers/media/usb/gspca/tv8532.c new file mode 100644 index 000000000000..8591324a53e1 --- /dev/null +++ b/drivers/media/usb/gspca/tv8532.c @@ -0,0 +1,378 @@ +/* + * Quickcam cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define MODULE_NAME "tv8532" + +#include "gspca.h" + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("TV8532 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + __u8 packet; +}; + +static const struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* TV-8532A (ICM532A) registers (LE) */ +#define R00_PART_CONTROL 0x00 +#define LATENT_CHANGE 0x80 +#define EXPO_CHANGE 0x04 +#define R01_TIMING_CONTROL_LOW 0x01 +#define CMD_EEprom_Open 0x30 +#define CMD_EEprom_Close 0x29 +#define R03_TABLE_ADDR 0x03 +#define R04_WTRAM_DATA_L 0x04 +#define R05_WTRAM_DATA_M 0x05 +#define R06_WTRAM_DATA_H 0x06 +#define R07_TABLE_LEN 0x07 +#define R08_RAM_WRITE_ACTION 0x08 +#define R0C_AD_WIDTHL 0x0c +#define R0D_AD_WIDTHH 0x0d +#define R0E_AD_HEIGHTL 0x0e +#define R0F_AD_HEIGHTH 0x0f +#define R10_AD_COL_BEGINL 0x10 +#define R11_AD_COL_BEGINH 0x11 +#define MIRROR 0x04 /* [10] */ +#define R14_AD_ROW_BEGINL 0x14 +#define R15_AD_ROWBEGINH 0x15 +#define R1C_AD_EXPOSE_TIMEL 0x1c +#define R20_GAIN_G1L 0x20 +#define R21_GAIN_G1H 0x21 +#define R22_GAIN_RL 0x22 +#define R23_GAIN_RH 0x23 +#define R24_GAIN_BL 0x24 +#define R25_GAIN_BH 0x25 +#define R26_GAIN_G2L 0x26 +#define R27_GAIN_G2H 0x27 +#define R28_QUANT 0x28 +#define R29_LINE 0x29 +#define R2C_POLARITY 0x2c +#define R2D_POINT 0x2d +#define R2E_POINTH 0x2e +#define R2F_POINTB 0x2f +#define R30_POINTBH 0x30 +#define R31_UPD 0x31 +#define R2A_HIGH_BUDGET 0x2a +#define R2B_LOW_BUDGET 0x2b +#define R34_VID 0x34 +#define R35_VIDH 0x35 +#define R36_PID 0x36 +#define R37_PIDH 0x37 +#define R39_Test1 0x39 /* GPIO */ +#define R3B_Test3 0x3b /* GPIO */ +#define R83_AD_IDH 0x83 +#define R91_AD_SLOPEREG 0x91 +#define R94_AD_BITCONTROL 0x94 + +static const u8 eeprom_data[][3] = { +/* dataH dataM dataL */ + {0x01, 0x00, 0x01}, + {0x01, 0x80, 0x11}, + {0x05, 0x00, 0x14}, + {0x05, 0x00, 0x1c}, + {0x0d, 0x00, 0x1e}, + {0x05, 0x00, 0x1f}, + {0x05, 0x05, 0x19}, + {0x05, 0x01, 0x1b}, + {0x05, 0x09, 0x1e}, + {0x0d, 0x89, 0x2e}, + {0x05, 0x89, 0x2f}, + {0x05, 0x0d, 0xd9}, + {0x05, 0x09, 0xf1}, +}; + + +/* write 1 byte */ +static void reg_w1(struct gspca_dev *gspca_dev, + __u16 index, __u8 value) +{ + gspca_dev->usb_buf[0] = value; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, 1, 500); +} + +/* write 2 bytes */ +static void reg_w2(struct gspca_dev *gspca_dev, + u16 index, u16 value) +{ + gspca_dev->usb_buf[0] = value; + gspca_dev->usb_buf[1] = value >> 8; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, 2, 500); +} + +static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) +{ + int i; + + reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Open); + for (i = 0; i < ARRAY_SIZE(eeprom_data); i++) { + reg_w1(gspca_dev, R03_TABLE_ADDR, i); + reg_w1(gspca_dev, R04_WTRAM_DATA_L, eeprom_data[i][2]); + reg_w1(gspca_dev, R05_WTRAM_DATA_M, eeprom_data[i][1]); + reg_w1(gspca_dev, R06_WTRAM_DATA_H, eeprom_data[i][0]); + reg_w1(gspca_dev, R08_RAM_WRITE_ACTION, 0); + } + reg_w1(gspca_dev, R07_TABLE_LEN, i); + reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + + return 0; +} + +static void tv_8532_setReg(struct gspca_dev *gspca_dev) +{ + reg_w1(gspca_dev, R3B_Test3, 0x0a); /* Test0Sel = 10 */ + /******************************************************/ + reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90); + reg_w1(gspca_dev, R0F_AD_HEIGHTH, 0x01); + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f); + reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44); + /* begin active line */ + reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00); + /* mirror and digital gain */ + reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a); + + reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02); + reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); + reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + /* = 0x84 */ +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + tv_8532WriteEEprom(gspca_dev); + + return 0; +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, val); + reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + /* 0x84 */ +} + +static void setgain(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w2(gspca_dev, R20_GAIN_G1L, val); + reg_w2(gspca_dev, R22_GAIN_RL, val); + reg_w2(gspca_dev, R24_GAIN_BL, val); + reg_w2(gspca_dev, R26_GAIN_G2L, val); +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); /* 0x20; 0x0c */ + reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); + + /************************************************/ + reg_w1(gspca_dev, R28_QUANT, 0x90); + /* 0x72 compressed mode 0x28 */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + /* 176x144 */ + reg_w1(gspca_dev, R29_LINE, 0x41); + /* CIF - 2 lines/packet */ + } else { + /* 352x288 */ + reg_w1(gspca_dev, R29_LINE, 0x81); + /* CIF - 2 lines/packet */ + } + /************************************************/ + reg_w1(gspca_dev, R2C_POLARITY, 0x10); /* slow clock */ + reg_w1(gspca_dev, R2D_POINT, 0x14); + reg_w1(gspca_dev, R2E_POINTH, 0x01); + reg_w1(gspca_dev, R2F_POINTB, 0x12); + reg_w1(gspca_dev, R30_POINTBH, 0x01); + + tv_8532_setReg(gspca_dev); + + /************************************************/ + reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ + msleep(200); + reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ + + gspca_dev->empty_packet = 0; /* check the empty packets */ + sd->packet = 0; /* ignore the first packets */ + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int packet_type0, packet_type1; + + packet_type0 = packet_type1 = INTER_PACKET; + if (gspca_dev->empty_packet) { + gspca_dev->empty_packet = 0; + sd->packet = gspca_dev->height / 2; + packet_type0 = FIRST_PACKET; + } else if (sd->packet == 0) + return; /* 2 more lines in 352x288 ! */ + sd->packet--; + if (sd->packet == 0) + packet_type1 = LAST_PACKET; + + /* each packet contains: + * - header 2 bytes + * - RGRG line + * - 4 bytes + * - GBGB line + * - 4 bytes + */ + gspca_frame_add(gspca_dev, packet_type0, + data + 2, gspca_dev->width); + gspca_frame_add(gspca_dev, packet_type1, + data + gspca_dev->width + 5, gspca_dev->width); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + setgain(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 2); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0x18f, 1, 0x18f); + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 0x7ff, 1, 0x100); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0920)}, + {USB_DEVICE(0x046d, 0x0921)}, + {USB_DEVICE(0x0545, 0x808b)}, + {USB_DEVICE(0x0545, 0x8333)}, + {USB_DEVICE(0x0923, 0x010f)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c new file mode 100644 index 000000000000..e50079503d96 --- /dev/null +++ b/drivers/media/usb/gspca/vc032x.c @@ -0,0 +1,3850 @@ +/* + * Z-star vc0321 library + * + * Copyright (C) 2009-2010 Jean-François Moine + * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl + * Copyright (C) 2006 Michel Xhaard + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "vc032x" + +#include "gspca.h" + +MODULE_AUTHOR("Jean-François Moine "); +MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct { /* hvflip cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + u8 image_offset; + + u8 bridge; + u8 sensor; + u8 flags; +#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */ +#define FL_HFLIP 0x02 /* mirrored by default */ +#define FL_VFLIP 0x04 /* vertical flipped by default */ +}; +enum bridges { + BRIDGE_VC0321, + BRIDGE_VC0323, +}; +enum sensors { + SENSOR_HV7131R, + SENSOR_MI0360, + SENSOR_MI1310_SOC, + SENSOR_MI1320, + SENSOR_MI1320_SOC, + SENSOR_OV7660, + SENSOR_OV7670, + SENSOR_PO1200, + SENSOR_PO3130NC, + SENSOR_POxxxx, + NSENSORS +}; + + +static const struct v4l2_pix_format vc0321_mode[] = { + {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format vc0323_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, + {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */ + .bytesperline = 1280, + .sizeimage = 1280 * 960 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, +}; +static const struct v4l2_pix_format bi_mode[] = { + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format svga_mode[] = { + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 1 / 4 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* OV7660/7670 registers */ +#define OV7660_REG_MVFP 0x1e +#define OV7660_MVFP_MIRROR 0x20 +#define OV7660_MVFP_VFLIP 0x10 + +static const u8 mi0360_matrix[9] = { + 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 +}; + +static const u8 mi0360_initVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */ + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xb8, 0x00, 0x13, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x14, 0x18, 0xcc}, + {0xb8, 0xb2, 0x0a, 0xcc}, + {0xb8, 0xb4, 0x0a, 0xcc}, + {0xb8, 0xb5, 0x0a, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x31, 0x00, 0x00, 0xbb}, + {0x09, 0x01, 0xc7, 0xbb}, + {0x34, 0x01, 0x00, 0xbb}, + {0x2b, 0x00, 0x28, 0xbb}, + {0x2c, 0x00, 0x30, 0xbb}, + {0x2d, 0x00, 0x30, 0xbb}, + {0x2e, 0x00, 0x28, 0xbb}, + {0x62, 0x04, 0x11, 0xbb}, + {0x03, 0x01, 0xe0, 0xbb}, + {0x2c, 0x00, 0x2c, 0xbb}, + {0x20, 0xd0, 0x00, 0xbb}, + {0x01, 0x00, 0x08, 0xbb}, + {0x06, 0x00, 0x10, 0xbb}, + {0x05, 0x00, 0x20, 0xbb}, + {0x20, 0x00, 0x00, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0x35, 0x00, 0x60, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const u8 mi0360_initQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x13, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x14, 0x18, 0xcc}, + {0xb8, 0xb2, 0x0a, 0xcc}, + {0xb8, 0xb4, 0x0a, 0xcc}, + {0xb8, 0xb5, 0x0a, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x31, 0x00, 0x00, 0xbb}, + {0x09, 0x01, 0xc7, 0xbb}, + {0x34, 0x01, 0x00, 0xbb}, + {0x2b, 0x00, 0x28, 0xbb}, + {0x2c, 0x00, 0x30, 0xbb}, + {0x2d, 0x00, 0x30, 0xbb}, + {0x2e, 0x00, 0x28, 0xbb}, + {0x62, 0x04, 0x11, 0xbb}, + {0x03, 0x01, 0xe0, 0xbb}, + {0x2c, 0x00, 0x2c, 0xbb}, + {0x20, 0xd0, 0x00, 0xbb}, + {0x01, 0x00, 0x08, 0xbb}, + {0x06, 0x00, 0x10, 0xbb}, + {0x05, 0x00, 0x20, 0xbb}, + {0x20, 0x00, 0x00, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, + {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, + {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, + {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0x35, 0x00, 0xef, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; + +static const u8 mi1310_socinitVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */ + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static const u8 mi1310_socinitQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static const u8 mi1310_soc_InitSXGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x0d, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x70, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x60, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x37, 0x01, 0x40, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, + {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x03, 0xcc}, + {0xb6, 0x04, 0xc0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x09, 0xcc}, + {0xb6, 0x17, 0x60, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0x00, 0x00, 0x80, 0xdd}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0x00, 0x00, 0x80, 0xdd}, + {0x06, 0x70, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5e, 0x6a, 0x53, 0xbb}, + {0x5f, 0x40, 0x2c, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x58, 0x00, 0x00, 0xbb}, + {0x53, 0x09, 0x03, 0xbb}, + {0x54, 0x31, 0x18, 0xbb}, + {0x55, 0x8b, 0x5f, 0xbb}, + {0x56, 0xc0, 0xa9, 0xbb}, + {0x57, 0xe0, 0xd2, 0xbb}, + {0xe1, 0x00, 0x00, 0xbb}, + {0xdc, 0x09, 0x03, 0xbb}, + {0xdd, 0x31, 0x18, 0xbb}, + {0xde, 0x8b, 0x5f, 0xbb}, + {0xdf, 0xc0, 0xa9, 0xbb}, + {0xe0, 0xe0, 0xd2, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {} +}; + +static const u8 mi1320_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const u8 mi1320_matrix[9] = { + 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 +}; +static const u8 mi1320_initVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */ + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd}, + {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb}, + {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb}, + {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb}, + {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb}, + {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb}, + {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb}, + {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb}, + {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb}, + {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb}, + {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb}, + {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb}, + {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb}, + {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb}, + {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb}, + {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb}, + {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb}, + {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb}, + {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb}, + {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb}, + {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, + {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */ + {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, + {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static const u8 mi1320_initQVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb}, + {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb}, + {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb}, + {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb}, + {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb}, + {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb}, + {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb}, + {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb}, + {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb}, + {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb}, + {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb}, + {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb}, + {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb}, + {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb}, + {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb}, + {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb}, + {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb}, + {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb}, + {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb}, + {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb}, + {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb}, + {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static const u8 mi1320_soc_InitVGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */ + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const u8 mi1320_soc_InitQVGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const u8 mi1320_soc_InitSXGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ + {0x00, 0x00, 0x20, 0xdd}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, + {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x64, 0x5e, 0x1c, 0xbb}, + {} +}; +static const u8 po3130_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const u8 po3130_matrix[9] = { + 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 +}; + +static const u8 po3130_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */ + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, +/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */ + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static const u8 po3130_rundata[][4] = { + {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, + {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, + {0x00, 0x44, 0x40, 0xaa}, +/* {0x00, 0xd5, 0x7c, 0xaa}, */ + {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa}, + {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa}, + {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa}, + {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa}, + {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa}, + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {} +}; + +static const u8 po3130_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, + {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static const u8 hv7131r_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const u8 hv7131r_matrix[9] = { + 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 +}; +static const u8 hv7131r_initVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x20, 0xdd}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x02, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */ + {0xb3, 0x00, 0x27, 0xcc}, + {0xbc, 0x00, 0x73, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x58, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x03, 0xaa}, + {0x00, 0x26, 0xa9, 0xaa}, + {0x00, 0x27, 0x80, 0xaa}, + {0x00, 0x30, 0x18, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; + +static const u8 hv7131r_initQVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x20, 0xdd}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x02, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, + {0xbc, 0x00, 0xd3, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x58, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x03, 0xaa}, + {0x00, 0x26, 0xa9, 0xaa}, + {0x00, 0x27, 0x80, 0xaa}, + {0x00, 0x30, 0x18, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, + {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, + {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, + {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; + +static const u8 ov7660_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const u8 ov7660_matrix[9] = { + 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 +}; +static const u8 ov7660_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, + {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ + {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, + {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, + {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, + {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + + {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, + {} +}; +static const u8 ov7660_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, +/* sizer */ + {0xbc, 0x00, 0xd3, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, +/* sizer filters */ + {0xbc, 0x02, 0x08, 0xcc}, + {0xbc, 0x03, 0x70, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xbc, 0x04, 0x08, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x3c, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x04, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, +/* */ + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, +/* */ + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, +/* */ + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, /* ff */ + {0x00, 0x29, 0x3c, 0xaa}, + {0xb3, 0x01, 0x45, 0xcc}, /* 45 */ + {} +}; + +static const u8 ov7660_50HZ[][4] = { + {0x00, 0x3b, 0x08, 0xaa}, + {0x00, 0x9d, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {} +}; + +static const u8 ov7660_60HZ[][4] = { + {0x00, 0x3b, 0x00, 0xaa}, + {0x00, 0x9e, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {} +}; + +static const u8 ov7660_NoFliker[][4] = { + {0x00, 0x13, 0x87, 0xaa}, + {} +}; + +static const u8 ov7670_InitVGA[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x16, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xbc, 0x00, 0x41, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, + {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, + {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, + {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, + {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, + {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, + {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, + {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, + {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, + {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, + {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, + {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, + {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, + {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, + {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, + {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, + {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, + {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ + {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, + {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, + {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, + {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, + {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, + {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, + {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, + {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, + {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, + {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, + {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, + {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, + {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, + {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, + {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, + {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, + {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, + {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, + {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, + {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, + {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, + {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, + {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, + {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, + {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, + {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, + {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, + {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, + {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, + {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, + {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, + {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, + {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, + {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, + {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, + {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, + {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, + {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, + {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, + {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, + {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, + {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, + {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, + {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, + {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, + {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, + {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, + {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, + {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, + {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, + {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, + {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa}, + {}, +}; + +static const u8 ov7670_InitQVGA[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x16, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, + {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, + {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, + {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, + {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, + {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, + {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, + {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, + {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, + {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, + {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, + {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, + {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, + {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, + {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, + {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, + {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, + {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ + {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, + {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, + {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, + {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, + {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, + {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, + {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, + {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, + {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, + {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, + {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, + {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, + {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, + {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, + {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, + {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, + {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, + {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, + {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, + {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, + {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, + {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, + {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, + {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, + {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, + {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, + {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, + {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, + {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, + {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, + {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, + {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, + {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, + {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, + {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, + {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, + {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, + {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, + {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, + {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, + {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, + {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, + {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, + {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, + {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, + {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, + {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, + {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, + {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, + {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, + {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, + {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa}, + {}, +}; + +/* PO1200 - values from usbvm326.inf and ms-win trace */ +static const u8 po1200_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const u8 po1200_matrix[9] = { + 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e +}; +static const u8 po1200_initVGA_data[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, /* reset? */ + {0xb0, 0x03, 0x19, 0xcc}, +/* {0x00, 0x00, 0x33, 0xdd}, */ + {0xb0, 0x04, 0x02, 0xcc}, + {0xb0, 0x02, 0x02, 0xcc}, + {0xb3, 0x5d, 0x00, 0xcc}, + {0xb3, 0x01, 0x01, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x02, 0xb2, 0xcc}, + {0xb3, 0x03, 0x18, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x02, 0xcc}, + {0xb3, 0x23, 0x58, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x03, 0xcc}, + {0xb3, 0x17, 0x1f, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb0, 0x54, 0x13, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x13, 0x02, 0xaa}, + {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */ + {0x00, 0x21, 0x00, 0xaa}, + {0x00, 0x25, 0x02, 0xaa}, + {0x00, 0x3c, 0x4f, 0xaa}, + {0x00, 0x3f, 0xe0, 0xaa}, + {0x00, 0x42, 0xff, 0xaa}, + {0x00, 0x45, 0x34, 0xaa}, + {0x00, 0x55, 0xfe, 0xaa}, + {0x00, 0x59, 0xd3, 0xaa}, + {0x00, 0x5e, 0x04, 0xaa}, + {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */ + {0x00, 0x62, 0x02, 0xaa}, + {0x00, 0xa7, 0x31, 0xaa}, + {0x00, 0xa9, 0x66, 0xaa}, + {0x00, 0xb0, 0x00, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb3, 0x11, 0xaa}, + {0x00, 0xb6, 0x26, 0xaa}, + {0x00, 0xb7, 0x20, 0xaa}, + {0x00, 0xba, 0x04, 0xaa}, + {0x00, 0x88, 0x42, 0xaa}, + {0x00, 0x89, 0x9a, 0xaa}, + {0x00, 0x8a, 0x88, 0xaa}, + {0x00, 0x8b, 0x8e, 0xaa}, + {0x00, 0x8c, 0x3e, 0xaa}, + {0x00, 0x8d, 0x90, 0xaa}, + {0x00, 0x8e, 0x87, 0xaa}, + {0x00, 0x8f, 0x96, 0xaa}, + {0x00, 0x90, 0x3d, 0xaa}, + {0x00, 0x64, 0x00, 0xaa}, + {0x00, 0x65, 0x10, 0xaa}, + {0x00, 0x66, 0x20, 0xaa}, + {0x00, 0x67, 0x2b, 0xaa}, + {0x00, 0x68, 0x36, 0xaa}, + {0x00, 0x69, 0x49, 0xaa}, + {0x00, 0x6a, 0x5a, 0xaa}, + {0x00, 0x6b, 0x7f, 0xaa}, + {0x00, 0x6c, 0x9b, 0xaa}, + {0x00, 0x6d, 0xba, 0xaa}, + {0x00, 0x6e, 0xd4, 0xaa}, + {0x00, 0x6f, 0xea, 0xaa}, + {0x00, 0x70, 0x00, 0xaa}, + {0x00, 0x71, 0x10, 0xaa}, + {0x00, 0x72, 0x20, 0xaa}, + {0x00, 0x73, 0x2b, 0xaa}, + {0x00, 0x74, 0x36, 0xaa}, + {0x00, 0x75, 0x49, 0xaa}, + {0x00, 0x76, 0x5a, 0xaa}, + {0x00, 0x77, 0x7f, 0xaa}, + {0x00, 0x78, 0x9b, 0xaa}, + {0x00, 0x79, 0xba, 0xaa}, + {0x00, 0x7a, 0xd4, 0xaa}, + {0x00, 0x7b, 0xea, 0xaa}, + {0x00, 0x7c, 0x00, 0xaa}, + {0x00, 0x7d, 0x10, 0xaa}, + {0x00, 0x7e, 0x20, 0xaa}, + {0x00, 0x7f, 0x2b, 0xaa}, + {0x00, 0x80, 0x36, 0xaa}, + {0x00, 0x81, 0x49, 0xaa}, + {0x00, 0x82, 0x5a, 0xaa}, + {0x00, 0x83, 0x7f, 0xaa}, + {0x00, 0x84, 0x9b, 0xaa}, + {0x00, 0x85, 0xba, 0xaa}, + {0x00, 0x86, 0xd4, 0xaa}, + {0x00, 0x87, 0xea, 0xaa}, + {0x00, 0x57, 0x2a, 0xaa}, + {0x00, 0x03, 0x01, 0xaa}, + {0x00, 0x04, 0x10, 0xaa}, + {0x00, 0x05, 0x10, 0xaa}, + {0x00, 0x06, 0x10, 0xaa}, + {0x00, 0x07, 0x10, 0xaa}, + {0x00, 0x08, 0x13, 0xaa}, + {0x00, 0x0a, 0x00, 0xaa}, + {0x00, 0x0b, 0x10, 0xaa}, + {0x00, 0x0c, 0x20, 0xaa}, + {0x00, 0x0d, 0x18, 0xaa}, + {0x00, 0x22, 0x01, 0xaa}, + {0x00, 0x23, 0x60, 0xaa}, + {0x00, 0x25, 0x08, 0xaa}, + {0x00, 0x26, 0x82, 0xaa}, + {0x00, 0x2e, 0x0f, 0xaa}, + {0x00, 0x2f, 0x1e, 0xaa}, + {0x00, 0x30, 0x2d, 0xaa}, + {0x00, 0x31, 0x3c, 0xaa}, + {0x00, 0x32, 0x4b, 0xaa}, + {0x00, 0x33, 0x5a, 0xaa}, + {0x00, 0x34, 0x69, 0xaa}, + {0x00, 0x35, 0x78, 0xaa}, + {0x00, 0x36, 0x87, 0xaa}, + {0x00, 0x37, 0x96, 0xaa}, + {0x00, 0x38, 0xa5, 0xaa}, + {0x00, 0x39, 0xb4, 0xaa}, + {0x00, 0x3a, 0xc3, 0xaa}, + {0x00, 0x3b, 0xd2, 0xaa}, + {0x00, 0x3c, 0xe1, 0xaa}, + {0x00, 0x3e, 0xff, 0xaa}, + {0x00, 0x3f, 0xff, 0xaa}, + {0x00, 0x40, 0xff, 0xaa}, + {0x00, 0x41, 0xff, 0xaa}, + {0x00, 0x42, 0xff, 0xaa}, + {0x00, 0x43, 0xff, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x20, 0xc4, 0xaa}, + {0x00, 0x13, 0x03, 0xaa}, + {0x00, 0x3c, 0x50, 0xaa}, + {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */ + {0x00, 0x51, 0x5b, 0xaa}, + {0x00, 0x52, 0x91, 0xaa}, + {0x00, 0x53, 0x4c, 0xaa}, + {0x00, 0x54, 0x50, 0xaa}, + {0x00, 0x56, 0x02, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x03, 0xcc}, + {0xb6, 0x02, 0x20, 0xcc}, + {0xb6, 0x05, 0x02, 0xcc}, + {0xb6, 0x04, 0x58, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x21, 0xcc}, + {0xb6, 0x18, 0x03, 0xcc}, + {0xb6, 0x17, 0xa9, 0xcc}, + {0xb6, 0x16, 0x80, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xb8, 0x06, 0x20, 0xcc}, + {0xb8, 0x07, 0x03, 0xcc}, + {0xb8, 0x08, 0x58, 0xcc}, + {0xb8, 0x09, 0x02, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xd9, 0x0f, 0xaa}, + {0x00, 0xda, 0xaa, 0xaa}, + {0x00, 0xd9, 0x10, 0xaa}, + {0x00, 0xda, 0xaa, 0xaa}, + {0x00, 0xd9, 0x11, 0xaa}, + {0x00, 0xda, 0x00, 0xaa}, + {0x00, 0xd9, 0x12, 0xaa}, + {0x00, 0xda, 0xff, 0xaa}, + {0x00, 0xd9, 0x13, 0xaa}, + {0x00, 0xda, 0xff, 0xaa}, + {0x00, 0xe8, 0x11, 0xaa}, + {0x00, 0xe9, 0x12, 0xaa}, + {0x00, 0xea, 0x5c, 0xaa}, + {0x00, 0xeb, 0xff, 0xaa}, + {0x00, 0xd8, 0x80, 0xaa}, + {0x00, 0xe6, 0x02, 0xaa}, + {0x00, 0xd6, 0x40, 0xaa}, + {0x00, 0xe3, 0x05, 0xaa}, + {0x00, 0xe0, 0x40, 0xaa}, + {0x00, 0xde, 0x03, 0xaa}, + {0x00, 0xdf, 0x03, 0xaa}, + {0x00, 0xdb, 0x02, 0xaa}, + {0x00, 0xdc, 0x00, 0xaa}, + {0x00, 0xdd, 0x03, 0xaa}, + {0x00, 0xe1, 0x08, 0xaa}, + {0x00, 0xe2, 0x01, 0xaa}, + {0x00, 0xd6, 0x40, 0xaa}, + {0x00, 0xe4, 0x40, 0xaa}, + {0x00, 0xa8, 0x8f, 0xaa}, + {0x00, 0xb4, 0x16, 0xaa}, + {0xb0, 0x02, 0x06, 0xcc}, + {0xb0, 0x18, 0x06, 0xcc}, + {0xb0, 0x19, 0x06, 0xcc}, + {0xb3, 0x5d, 0x18, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0x00, 0xb4, 0x0e, 0xaa}, + {0x00, 0xb5, 0x49, 0xaa}, + {0x00, 0xb6, 0x1c, 0xaa}, + {0x00, 0xb7, 0x96, 0xaa}, +/* end of usbvm326.inf - start of ms-win trace */ + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x3d, 0xcc}, +/*read b306*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x1a, 0x09, 0xaa}, + {0x00, 0x1b, 0x8a, 0xaa}, +/*read b827*/ + {0xb8, 0x27, 0x00, 0xcc}, + {0xb8, 0x26, 0x60, 0xcc}, + {0xb8, 0x26, 0x60, 0xcc}, +/*gamma - to do?*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xae, 0x84, 0xaa}, +/*gamma again*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x96, 0xa0, 0xaa}, +/*matrix*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x91, 0x35, 0xaa}, + {0x00, 0x92, 0x22, 0xaa}, +/*gamma*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x95, 0x85, 0xaa}, +/*matrix*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x4d, 0x20, 0xaa}, + {0xb8, 0x22, 0x40, 0xcc}, + {0xb8, 0x23, 0x40, 0xcc}, + {0xb8, 0x24, 0x40, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x00, 0x00, 0x64, 0xdd}, + {0x00, 0x03, 0x01, 0xaa}, +/*read 46*/ + {0x00, 0x46, 0x3c, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x16, 0x40, 0xaa}, + {0x00, 0x17, 0x40, 0xaa}, + {0x00, 0x18, 0x40, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, + {0x00, 0x03, 0x01, 0xaa}, + {0x00, 0x46, 0x3c, 0xaa}, + {0x00, 0x00, 0x18, 0xdd}, +/*read bfff*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xb4, 0x1c, 0xaa}, + {0x00, 0xb5, 0x92, 0xaa}, + {0x00, 0xb6, 0x39, 0xaa}, + {0x00, 0xb7, 0x24, 0xaa}, +/*write 89 0400 1415*/ + {} +}; + +static const u8 poxxxx_init_common[][4] = { + {0xb3, 0x00, 0x04, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x65, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x03, 0x09, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */ + {0xb3, 0x02, 0xb0, 0xcc}, + {0xb3, 0x03, 0x18, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, /* sensor height = 1024 */ + {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, /* sensor width = 1280 */ + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x2c, 0x03, 0xcc}, + {0xb3, 0x2d, 0x56, 0xcc}, + {0xb3, 0x2e, 0x02, 0xcc}, + {0xb3, 0x2f, 0x0a, 0xcc}, + {0xb3, 0x40, 0x00, 0xcc}, + {0xb3, 0x41, 0x34, 0xcc}, + {0xb3, 0x42, 0x01, 0xcc}, + {0xb3, 0x43, 0xe0, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb3, 0x4d, 0x00, 0xcc}, + {0x00, 0x0b, 0x2a, 0xaa}, + {0x00, 0x0e, 0x03, 0xaa}, + {0x00, 0x0f, 0xea, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, + {0x00, 0x31, 0x1f, 0xaa}, + {0x00, 0x33, 0x38, 0xaa}, + {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, + {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, + {0x00, 0x4d, 0x2e, 0xaa}, + {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, + {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x56, 0x0a, 0xaa}, + {0x00, 0x57, 0x07, 0xaa}, + {0x00, 0x58, 0x07, 0xaa}, + {0x00, 0x59, 0x04, 0xaa}, + {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x71, 0x04, 0xaa}, + {0x00, 0x72, 0x10, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, + {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, + {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, + {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x8b, 0x25, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x8d, 0x86, 0xaa}, + {0x00, 0x8e, 0x82, 0xaa}, + {0x00, 0x8f, 0x2d, 0xaa}, + {0x00, 0x90, 0x8b, 0xaa}, + {0x00, 0x91, 0x81, 0xaa}, + {0x00, 0x92, 0x81, 0xaa}, + {0x00, 0x93, 0x23, 0xaa}, + {0x00, 0xa3, 0x2a, 0xaa}, + {0x00, 0xa4, 0x03, 0xaa}, + {0x00, 0xa5, 0xea, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, + {0x00, 0xbe, 0x3b, 0xaa}, + {0x00, 0x4e, 0x40, 0xaa}, + {0x00, 0x06, 0x04, 0xaa}, + {0x00, 0x07, 0x03, 0xaa}, + {0x00, 0xcd, 0x18, 0xaa}, + {0x00, 0x28, 0x03, 0xaa}, + {0x00, 0x29, 0xef, 0xaa}, +/* reinit on alt 2 (qvga) or alt7 (vga) */ + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb8, 0x00, 0x01, 0xcc}, + + {0x00, 0x1d, 0x85, 0xaa}, + {0x00, 0x1e, 0xc6, 0xaa}, + {0x00, 0x00, 0x40, 0xdd}, + {0x00, 0x1d, 0x05, 0xaa}, + {} +}; +static const u8 poxxxx_gamma[][4] = { + {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */ + {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x0a, 0xaa}, + {0x00, 0x75, 0x16, 0xaa}, + {0x00, 0x76, 0x25, 0xaa}, + {0x00, 0x77, 0x34, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + + {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */ + {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x0a, 0xaa}, + {0x00, 0x75, 0x16, 0xaa}, + {0x00, 0x76, 0x25, 0xaa}, + {0x00, 0x77, 0x34, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + + {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */ + {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x0a, 0xaa}, + {0x00, 0x75, 0x16, 0xaa}, + {0x00, 0x76, 0x25, 0xaa}, + {0x00, 0x77, 0x34, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + {} +}; +static const u8 poxxxx_init_start_3[][4] = { + {0x00, 0xb8, 0x28, 0xaa}, + {0x00, 0xb9, 0x1e, 0xaa}, + {0x00, 0xb6, 0x14, 0xaa}, + {0x00, 0xb7, 0x0f, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, + {0x00, 0x5d, 0x18, 0xaa}, + {0x00, 0x5e, 0x24, 0xaa}, + {0x00, 0x5f, 0x24, 0xaa}, + {0x00, 0x86, 0x1a, 0xaa}, + {0x00, 0x60, 0x00, 0xaa}, + {0x00, 0x61, 0x1b, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x40, 0xaa}, + {0x00, 0x87, 0x1a, 0xaa}, + {0x00, 0x64, 0x00, 0xaa}, + {0x00, 0x65, 0x08, 0xaa}, + {0x00, 0x66, 0x10, 0xaa}, + {0x00, 0x67, 0x20, 0xaa}, + {0x00, 0x88, 0x10, 0xaa}, + {0x00, 0x68, 0x00, 0xaa}, + {0x00, 0x69, 0x08, 0xaa}, + {0x00, 0x6a, 0x0f, 0xaa}, + {0x00, 0x6b, 0x0f, 0xaa}, + {0x00, 0x89, 0x07, 0xaa}, + {0x00, 0xd5, 0x4c, 0xaa}, + {0x00, 0x0a, 0x00, 0xaa}, + {0x00, 0x0b, 0x2a, 0xaa}, + {0x00, 0x0e, 0x03, 0xaa}, + {0x00, 0x0f, 0xea, 0xaa}, + {0x00, 0xa2, 0x00, 0xaa}, + {0x00, 0xa3, 0x2a, 0xaa}, + {0x00, 0xa4, 0x03, 0xaa}, + {0x00, 0xa5, 0xea, 0xaa}, + {} +}; +static const u8 poxxxx_initVGA[][4] = { + {0x00, 0x20, 0x11, 0xaa}, + {0x00, 0x33, 0x38, 0xaa}, + {0x00, 0xbb, 0x0d, 0xaa}, + {0xb3, 0x22, 0x01, 0xcc}, /* change to 640x480 */ + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x02, 0xb0, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0x00, 0x04, 0x06, 0xaa}, + {0x00, 0x05, 0x3f, 0xaa}, + {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */ + {} +}; +static const u8 poxxxx_initQVGA[][4] = { + {0x00, 0x20, 0x33, 0xaa}, + {0x00, 0x33, 0x38, 0xaa}, + {0x00, 0xbb, 0x0d, 0xaa}, + {0xb3, 0x22, 0x00, 0xcc}, /* change to 320x240 */ + {0xb3, 0x23, 0xf0, 0xcc}, + {0xb3, 0x16, 0x01, 0xcc}, + {0xb3, 0x17, 0x3f, 0xcc}, + {0xb3, 0x02, 0xb0, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x5c, 0x00, 0xcc}, + {0x00, 0x04, 0x06, 0xaa}, + {0x00, 0x05, 0x3f, 0xaa}, + {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */ + {} +}; +static const u8 poxxxx_init_end_1[][4] = { + {0x00, 0x47, 0x25, 0xaa}, + {0x00, 0x48, 0x80, 0xaa}, + {0x00, 0x49, 0x1f, 0xaa}, + {0x00, 0x4a, 0x40, 0xaa}, + {0x00, 0x44, 0x40, 0xaa}, + {0x00, 0xab, 0x4a, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb2, 0x04, 0xaa}, + {0x00, 0xb3, 0x08, 0xaa}, + {0x00, 0xb4, 0x0b, 0xaa}, + {0x00, 0xb5, 0x0d, 0xaa}, + {} +}; +static const u8 poxxxx_init_end_2[][4] = { + {0x00, 0x1d, 0x85, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x1d, 0x05, 0xaa}, + {} +}; + +struct sensor_info { + s8 sensorId; + u8 I2cAdd; + u8 IdAdd; + u16 VpId; + u8 m1; + u8 m2; + u8 op; +}; + +/* probe values */ +static const struct sensor_info vc0321_probe_data[] = { +/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ +/* 0 OV9640 */ + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */ + {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, +/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/ + {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 3 MI1310 */ + {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 4 MI360 - tested in vc032x_probe_sensor */ +/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ +/* 5 7131R */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, +/* 6 OV7649 */ + {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, +/* 7 PAS302BCW */ + {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, +/* 8 OV7660 */ + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, +/* 9 PO3130NC - (tested in vc032x_probe_sensor) */ +/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */ +/* 10 PO1030KC */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 11 MI1310_SOC */ + {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +/* 12 OV9650 */ + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 13 S5K532 */ + {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, +/* 14 MI360_SOC - ??? */ +/* 15 PO1200N */ + {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, +/* 16 PO3030K */ + {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 17 PO2030 */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* ?? */ + {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, + {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01}, +}; +static const struct sensor_info vc0323_probe_data[] = { +/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ +/* 0 OV9640 */ + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */ + {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, +/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/ + {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 3 MI1310 */ + {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 4 MI360 - tested in vc032x_probe_sensor */ +/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ +/* 5 7131R */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, +/* 6 OV7649 */ + {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, +/* 7 PAS302BCW */ + {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, +/* 8 OV7660 */ + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, +/* 9 PO3130NC - (tested in vc032x_probe_sensor) */ +/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */ +/* 10 PO1030KC */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 11 MI1310_SOC */ + {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +/* 12 OV9650 */ + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 13 S5K532 */ + {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, +/* 14 MI360_SOC - ??? */ +/* 15 PO1200N */ + {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, +/* 16 ?? */ + {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01}, +/* 17 PO2030 */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* ?? */ + {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, + {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01}, +/*fixme: not in the ms-win probe - may be found before? */ + {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, +}; + +/* read 'len' bytes in gspca_dev->usb_buf */ +static void reg_r_i(struct gspca_dev *gspca_dev, + u16 req, + u16 index, + u16 len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, /* value */ + index, gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } +} +static void reg_r(struct gspca_dev *gspca_dev, + u16 req, + u16 index, + u16 len) +{ + reg_r_i(gspca_dev, req, index, len); +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + if (len == 1) + PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index, + gspca_dev->usb_buf[0]); + else + PDEBUG(D_USBI, "GET %02x 0001 %04x %*ph", + req, index, 3, gspca_dev->usb_buf); +#endif +} + +static void reg_w_i(struct gspca_dev *gspca_dev, + u16 req, + u16 value, + u16 index) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} +static void reg_w(struct gspca_dev *gspca_dev, + u16 req, + u16 value, + u16 index) +{ +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); +#endif + reg_w_i(gspca_dev, req, value, index); +} + +static u16 read_sensor_register(struct gspca_dev *gspca_dev, + u16 address) +{ + u8 ldata, mdata, hdata; + int retry = 50; + + reg_r(gspca_dev, 0xa1, 0xb33f, 1); + if (!(gspca_dev->usb_buf[0] & 0x02)) { + pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]); + return 0; + } + reg_w(gspca_dev, 0xa0, address, 0xb33a); + reg_w(gspca_dev, 0xa0, 0x02, 0xb339); + + do { + reg_r(gspca_dev, 0xa1, 0xb33b, 1); + if (gspca_dev->usb_buf[0] == 0x00) + break; + msleep(40); + } while (--retry >= 0); + + reg_r(gspca_dev, 0xa1, 0xb33e, 1); + ldata = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0xa1, 0xb33d, 1); + mdata = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0xa1, 0xb33c, 1); + hdata = gspca_dev->usb_buf[0]; + if (hdata != 0 && mdata != 0 && ldata != 0) + PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x", + hdata, mdata, ldata); + reg_r(gspca_dev, 0xa1, 0xb334, 1); + if (gspca_dev->usb_buf[0] == 0x02) + return (hdata << 8) + mdata; + return hdata; +} + +static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, n; + u16 value; + const struct sensor_info *ptsensor_info; + +/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/ + if (sd->flags & FL_SAMSUNG) { + reg_w(gspca_dev, 0xa0, 0x01, 0xb301); + reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); + /* select the back sensor */ + } + + reg_r(gspca_dev, 0xa1, 0xbfcf, 1); + PDEBUG(D_PROBE, "vc032%d check sensor header %02x", + sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]); + if (sd->bridge == BRIDGE_VC0321) { + ptsensor_info = vc0321_probe_data; + n = ARRAY_SIZE(vc0321_probe_data); + } else { + ptsensor_info = vc0323_probe_data; + n = ARRAY_SIZE(vc0323_probe_data); + } + for (i = 0; i < n; i++) { + reg_w(gspca_dev, 0xa0, 0x02, 0xb334); + reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300); + reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300); + reg_w(gspca_dev, 0xa0, 0x01, 0xb308); + reg_w(gspca_dev, 0xa0, 0x0c, 0xb309); + reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); + reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301); + value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd); + if (value == 0 && ptsensor_info->IdAdd == 0x82) + value = read_sensor_register(gspca_dev, 0x83); + if (value != 0) { + PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)", + value, i); + if (value == ptsensor_info->VpId) + return ptsensor_info->sensorId; + + switch (value) { + case 0x3130: + return SENSOR_PO3130NC; + case 0x7673: + return SENSOR_OV7670; + case 0x8243: + return SENSOR_MI0360; + } + } + ptsensor_info++; + } + return -1; +} + +static void i2c_write(struct gspca_dev *gspca_dev, + u8 reg, const u8 *val, + u8 size) /* 1 or 2 */ +{ + int retry; + +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + if (size == 1) + PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val); + else + PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]); +#endif + reg_r_i(gspca_dev, 0xa1, 0xb33f, 1); +/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/ + reg_w_i(gspca_dev, 0xa0, size, 0xb334); + reg_w_i(gspca_dev, 0xa0, reg, 0xb33a); + reg_w_i(gspca_dev, 0xa0, val[0], 0xb336); + if (size > 1) + reg_w_i(gspca_dev, 0xa0, val[1], 0xb337); + reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339); + retry = 4; + do { + reg_r_i(gspca_dev, 0xa1, 0xb33b, 1); + if (gspca_dev->usb_buf[0] == 0) + break; + msleep(20); + } while (--retry > 0); + if (retry <= 0) + pr_err("i2c_write timeout\n"); +} + +static void put_tab_to_reg(struct gspca_dev *gspca_dev, + const u8 *tab, u8 tabsize, u16 addr) +{ + int j; + u16 ad = addr; + + for (j = 0; j < tabsize; j++) + reg_w(gspca_dev, 0xa0, tab[j], ad++); +} + +static void usb_exchange(struct gspca_dev *gspca_dev, + const u8 data[][4]) +{ + int i = 0; + + for (;;) { + switch (data[i][3]) { + default: + return; + case 0xcc: /* normal write */ + reg_w(gspca_dev, 0xa0, data[i][2], + (data[i][0]) << 8 | data[i][1]); + break; + case 0xaa: /* i2c op */ + i2c_write(gspca_dev, data[i][1], &data[i][2], 1); + break; + case 0xbb: /* i2c op */ + i2c_write(gspca_dev, data[i][0], &data[i][1], 2); + break; + case 0xdd: + msleep(data[i][1] * 256 + data[i][2] + 10); + break; + } + i++; + } + /*not reached*/ +} + + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->bridge = id->driver_info >> 8; + sd->flags = id->driver_info & 0xff; + + if (id->idVendor == 0x046d && + (id->idProduct == 0x0892 || id->idProduct == 0x0896)) + sd->sensor = SENSOR_POxxxx; /* no probe */ + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int sensor; + /* number of packets per ISOC message */ + static u8 npkt[NSENSORS] = { + [SENSOR_HV7131R] = 64, + [SENSOR_MI0360] = 32, + [SENSOR_MI1310_SOC] = 32, + [SENSOR_MI1320] = 64, + [SENSOR_MI1320_SOC] = 128, + [SENSOR_OV7660] = 32, + [SENSOR_OV7670] = 64, + [SENSOR_PO1200] = 128, + [SENSOR_PO3130NC] = 128, + [SENSOR_POxxxx] = 128, + }; + + if (sd->sensor != SENSOR_POxxxx) + sensor = vc032x_probe_sensor(gspca_dev); + else + sensor = sd->sensor; + + switch (sensor) { + case -1: + pr_err("Unknown sensor...\n"); + return -EINVAL; + case SENSOR_HV7131R: + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + break; + case SENSOR_MI0360: + PDEBUG(D_PROBE, "Find Sensor MI0360"); + sd->bridge = BRIDGE_VC0323; + break; + case SENSOR_MI1310_SOC: + PDEBUG(D_PROBE, "Find Sensor MI1310_SOC"); + break; + case SENSOR_MI1320: + PDEBUG(D_PROBE, "Find Sensor MI1320"); + break; + case SENSOR_MI1320_SOC: + PDEBUG(D_PROBE, "Find Sensor MI1320_SOC"); + break; + case SENSOR_OV7660: + PDEBUG(D_PROBE, "Find Sensor OV7660"); + break; + case SENSOR_OV7670: + PDEBUG(D_PROBE, "Find Sensor OV7670"); + break; + case SENSOR_PO1200: + PDEBUG(D_PROBE, "Find Sensor PO1200"); + break; + case SENSOR_PO3130NC: + PDEBUG(D_PROBE, "Find Sensor PO3130NC"); + break; + case SENSOR_POxxxx: + PDEBUG(D_PROBE, "Sensor POxxxx"); + break; + } + sd->sensor = sensor; + + cam = &gspca_dev->cam; + if (sd->bridge == BRIDGE_VC0321) { + cam->cam_mode = vc0321_mode; + cam->nmodes = ARRAY_SIZE(vc0321_mode); + } else { + switch (sensor) { + case SENSOR_PO1200: + cam->cam_mode = svga_mode; + cam->nmodes = ARRAY_SIZE(svga_mode); + break; + case SENSOR_MI1310_SOC: + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode); + break; + case SENSOR_MI1320_SOC: + cam->cam_mode = bi_mode; + cam->nmodes = ARRAY_SIZE(bi_mode); + break; + case SENSOR_OV7670: + cam->cam_mode = bi_mode; + cam->nmodes = ARRAY_SIZE(bi_mode) - 1; + break; + default: + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; + break; + } + } + cam->npkt = npkt[sd->sensor]; + + if (sd->sensor == SENSOR_OV7670) + sd->flags |= FL_HFLIP | FL_VFLIP; + + if (sd->bridge == BRIDGE_VC0321) { + reg_r(gspca_dev, 0x8a, 0, 3); + reg_w(gspca_dev, 0x87, 0x00, 0x0f0f); + reg_r(gspca_dev, 0x8b, 0, 3); + reg_w(gspca_dev, 0x88, 0x00, 0x0202); + if (sd->sensor == SENSOR_POxxxx) { + reg_r(gspca_dev, 0xa1, 0xb300, 1); + if (gspca_dev->usb_buf[0] != 0) { + reg_w(gspca_dev, 0xa0, 0x26, 0xb300); + reg_w(gspca_dev, 0xa0, 0x04, 0xb300); + } + reg_w(gspca_dev, 0xa0, 0x00, 0xb300); + } + } + return gspca_dev->usb_err; +} + +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + u8 data; + + data = val; + if (data >= 0x80) + data &= 0x7f; + else + data = 0xff ^ data; + i2c_write(gspca_dev, 0x98, &data, 1); +} + +static void setcontrast(struct gspca_dev *gspca_dev, u8 val) +{ + i2c_write(gspca_dev, 0x99, &val, 1); +} + +static void setcolors(struct gspca_dev *gspca_dev, u8 val) +{ + u8 data; + + data = val - (val >> 3) - 1; + i2c_write(gspca_dev, 0x94, &data, 1); + i2c_write(gspca_dev, 0x95, &val, 1); +} + +static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data[2]; + + if (sd->flags & FL_HFLIP) + hflip = !hflip; + if (sd->flags & FL_VFLIP) + vflip = !vflip; + switch (sd->sensor) { + case SENSOR_MI1310_SOC: + case SENSOR_MI1320: + case SENSOR_MI1320_SOC: + data[0] = data[1] = 0; /* select page 0 */ + i2c_write(gspca_dev, 0xf0, data, 2); + data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01; + data[1] = 0x02 * hflip + | 0x01 * vflip; + i2c_write(gspca_dev, 0x20, data, 2); + break; + case SENSOR_OV7660: + case SENSOR_OV7670: + data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07; + data[0] |= OV7660_MVFP_MIRROR * hflip + | OV7660_MVFP_VFLIP * vflip; + i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1); + break; + case SENSOR_PO1200: + data[0] = 0; + i2c_write(gspca_dev, 0x03, data, 1); + data[0] = 0x80 * hflip + | 0x40 * vflip + | 0x06; + i2c_write(gspca_dev, 0x1e, data, 1); + break; + } +} + +static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const u8 (*ov7660_freq_tb[3])[4] = + {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; + + if (sd->sensor != SENSOR_OV7660) + return; + usb_exchange(gspca_dev, ov7660_freq_tb[val]); +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data; + + switch (sd->sensor) { + case SENSOR_PO1200: + data = 0; + i2c_write(gspca_dev, 0x03, &data, 1); + if (val < 0) + data = 0x6a; + else + data = 0xb5 + val * 3; + i2c_write(gspca_dev, 0x61, &data, 1); + break; + case SENSOR_POxxxx: + if (val < 0) + data = 0x7e; /* def = max */ + else + data = 0x60 + val * 0x0f; + i2c_write(gspca_dev, 0x59, &data, 1); + break; + } +} +static void setgain(struct gspca_dev *gspca_dev, u8 val) +{ + i2c_write(gspca_dev, 0x15, &val, 1); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + u8 data; + + data = val >> 8; + i2c_write(gspca_dev, 0x1a, &data, 1); + data = val; + i2c_write(gspca_dev, 0x1b, &data, 1); +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 val) +{ + static const u8 data[2] = {0x28, 0x3c}; + + i2c_write(gspca_dev, 0xd1, &data[val], 1); +} + +static void setgamma(struct gspca_dev *gspca_dev) +{ +/*fixme:to do */ + usb_exchange(gspca_dev, poxxxx_gamma); +} + +static void setbacklight(struct gspca_dev *gspca_dev, s32 val) +{ + u16 v; + u8 data; + + data = (val << 4) | 0x0f; + i2c_write(gspca_dev, 0xaa, &data, 1); + v = 613 + 12 * val; + data = v >> 8; + i2c_write(gspca_dev, 0xc4, &data, 1); + data = v; + i2c_write(gspca_dev, 0xc5, &data, 1); + v = 1093 - 12 * val; + data = v >> 8; + i2c_write(gspca_dev, 0xc6, &data, 1); + data = v; + i2c_write(gspca_dev, 0xc7, &data, 1); + v = 342 + 9 * val; + data = v >> 8; + i2c_write(gspca_dev, 0xc8, &data, 1); + data = v; + i2c_write(gspca_dev, 0xc9, &data, 1); + v = 702 - 9 * val; + data = v >> 8; + i2c_write(gspca_dev, 0xca, &data, 1); + data = v; + i2c_write(gspca_dev, 0xcb, &data, 1); +} + +static void setwb(struct gspca_dev *gspca_dev) +{ +/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/ + static const u8 data[2] = {0x00, 0x00}; + + i2c_write(gspca_dev, 0x16, &data[0], 1); + i2c_write(gspca_dev, 0x18, &data[1], 1); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const u8 (*init)[4]; + const u8 *GammaT = NULL; + const u8 *MatrixT = NULL; + int mode; + static const u8 (*mi1320_soc_init[])[4] = { + mi1320_soc_InitSXGA, + mi1320_soc_InitVGA, + mi1320_soc_InitQVGA, + }; + +/*fixme: back sensor only*/ + if (sd->flags & FL_SAMSUNG) { + reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); + reg_w(gspca_dev, 0xa9, 0x8348, 0x000e); + reg_w(gspca_dev, 0xa9, 0x0000, 0x001a); + } + + /* Assume start use the good resolution from gspca_dev->mode */ + if (sd->bridge == BRIDGE_VC0321) { + reg_w(gspca_dev, 0xa0, 0xff, 0xbfec); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfed); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfee); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfef); + sd->image_offset = 46; + } else { + if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat + == V4L2_PIX_FMT_JPEG) + sd->image_offset = 0; + else + sd->image_offset = 32; + } + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + switch (sd->sensor) { + case SENSOR_HV7131R: + GammaT = hv7131r_gamma; + MatrixT = hv7131r_matrix; + if (mode) + init = hv7131r_initQVGA_data; /* 320x240 */ + else + init = hv7131r_initVGA_data; /* 640x480 */ + break; + case SENSOR_OV7660: + GammaT = ov7660_gamma; + MatrixT = ov7660_matrix; + if (mode) + init = ov7660_initQVGA_data; /* 320x240 */ + else + init = ov7660_initVGA_data; /* 640x480 */ + break; + case SENSOR_MI0360: + GammaT = mi1320_gamma; + MatrixT = mi0360_matrix; + if (mode) + init = mi0360_initQVGA_JPG; /* 320x240 */ + else + init = mi0360_initVGA_JPG; /* 640x480 */ + break; + case SENSOR_MI1310_SOC: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + switch (mode) { + case 1: + init = mi1310_socinitQVGA_JPG; /* 320x240 */ + break; + case 0: + init = mi1310_socinitVGA_JPG; /* 640x480 */ + break; + default: + init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */ + break; + } + break; + case SENSOR_MI1320: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + if (mode) + init = mi1320_initQVGA_data; /* 320x240 */ + else + init = mi1320_initVGA_data; /* 640x480 */ + break; + case SENSOR_MI1320_SOC: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + init = mi1320_soc_init[mode]; + break; + case SENSOR_OV7670: + init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA; + break; + case SENSOR_PO3130NC: + GammaT = po3130_gamma; + MatrixT = po3130_matrix; + if (mode) + init = po3130_initQVGA_data; /* 320x240 */ + else + init = po3130_initVGA_data; /* 640x480 */ + usb_exchange(gspca_dev, init); + init = po3130_rundata; + break; + case SENSOR_PO1200: + GammaT = po1200_gamma; + MatrixT = po1200_matrix; + init = po1200_initVGA_data; + break; + default: +/* case SENSOR_POxxxx: */ + usb_exchange(gspca_dev, poxxxx_init_common); + setgamma(gspca_dev); + usb_exchange(gspca_dev, poxxxx_init_start_3); + if (mode) + init = poxxxx_initQVGA; + else + init = poxxxx_initVGA; + usb_exchange(gspca_dev, init); + reg_r(gspca_dev, 0x8c, 0x0000, 3); + reg_w(gspca_dev, 0xa0, + gspca_dev->usb_buf[2] & 1 ? 0 : 1, + 0xb35c); + msleep(300); +/*fixme: i2c read 04 and 05*/ + init = poxxxx_init_end_1; + break; + } + usb_exchange(gspca_dev, init); + if (GammaT && MatrixT) { + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c); + put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); + + switch (sd->sensor) { + case SENSOR_PO1200: + case SENSOR_HV7131R: + reg_w(gspca_dev, 0x89, 0x0400, 0x1415); + break; + case SENSOR_MI1310_SOC: + reg_w(gspca_dev, 0x89, 0x058c, 0x0000); + break; + } + msleep(100); + } + switch (sd->sensor) { + case SENSOR_OV7670: + reg_w(gspca_dev, 0x87, 0xffff, 0xffff); + reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1); + reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff); + break; + case SENSOR_POxxxx: + usb_exchange(gspca_dev, poxxxx_init_end_2); + setwb(gspca_dev); + msleep(80); /* led on */ + reg_w(gspca_dev, 0x89, 0xffff, 0xfdff); + break; + } + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_MI1310_SOC: + reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); + break; + case SENSOR_POxxxx: + return; + default: + if (!(sd->flags & FL_SAMSUNG)) + reg_w(gspca_dev, 0x89, 0xffff, 0xffff); + break; + } + reg_w(gspca_dev, 0xa0, 0x01, 0xb301); + reg_w(gspca_dev, 0xa0, 0x09, 0xb003); +} + +/* called on streamoff with alt 0 and on disconnect */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!gspca_dev->present) + return; +/*fixme: is this useful?*/ + if (sd->sensor == SENSOR_MI1310_SOC) + reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); + else if (!(sd->flags & FL_SAMSUNG)) + reg_w(gspca_dev, 0x89, 0xffff, 0xffff); + + if (sd->sensor == SENSOR_POxxxx) { + reg_w(gspca_dev, 0xa0, 0x26, 0xb300); + reg_w(gspca_dev, 0xa0, 0x04, 0xb300); + reg_w(gspca_dev, 0xa0, 0x00, 0xb300); + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso pkt length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] == 0xff && data[1] == 0xd8) { + PDEBUG(D_PACK, + "vc032x header packet found len %d", len); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + data += sd->image_offset; + len -= sd->image_offset; + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + return; + } + + /* The vc0321 sends some additional data after sending the complete + * frame, we ignore this. */ + if (sd->bridge == BRIDGE_VC0321) { + int size, l; + + l = gspca_dev->image_len; + size = gspca_dev->frsz; + if (len > size - l) + len = size - l; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + setgain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, ctrl->val); + break; + case V4L2_CID_BACKLIGHT_COMPENSATION: + setbacklight(gspca_dev, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + bool has_brightness = false; + bool has_contrast = false; + bool has_sat = false; + bool has_hvflip = false; + bool has_freq = false; + bool has_backlight = false; + bool has_exposure = false; + bool has_autogain = false; + bool has_gain = false; + bool has_sharpness = false; + + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MI0360: + case SENSOR_PO3130NC: + break; + case SENSOR_MI1310_SOC: + case SENSOR_MI1320: + case SENSOR_MI1320_SOC: + case SENSOR_OV7660: + has_hvflip = true; + break; + case SENSOR_OV7670: + has_hvflip = has_freq = true; + break; + case SENSOR_PO1200: + has_hvflip = has_sharpness = true; + break; + case SENSOR_POxxxx: + has_brightness = has_contrast = has_sat = has_backlight = + has_exposure = has_autogain = has_gain = + has_sharpness = true; + break; + } + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 8); + if (has_brightness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + if (has_contrast) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + if (has_sat) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 1, 127, 1, 63); + if (has_hvflip) { + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } + if (has_sharpness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, -1, 2, 1, -1); + if (has_freq) + v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + if (has_autogain) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (has_gain) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 78, 1, 0); + if (has_exposure) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 4095, 1, 450); + if (has_backlight) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + if (sd->hflip) + v4l2_ctrl_cluster(2, &sd->hflip); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .init_controls = sd_init_controls, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define BF(bridge, flags) \ + .driver_info = (BRIDGE_ ## bridge << 8) \ + | (flags) +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)}, + {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)}, + {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)}, + {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)}, + {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)}, + {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)}, + {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)}, + {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c new file mode 100644 index 000000000000..b1a64b912666 --- /dev/null +++ b/drivers/media/usb/gspca/vicam.c @@ -0,0 +1,365 @@ +/* + * gspca ViCam subdriver + * + * Copyright (C) 2011 Hans de Goede + * + * Based on the usbvideo vicam driver, which is: + * + * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), + * Christopher L Cheney (ccheney@cheney.cx), + * Pavel Machek (pavel@ucw.cz), + * John Tyner (jtyner@cs.ucr.edu), + * Monroe Williams (monroe@pobox.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "vicam" +#define HEADER_SIZE 64 + +#include +#include +#include +#include +#include "gspca.h" + +#define VICAM_FIRMWARE "vicam/firmware.fw" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(VICAM_FIRMWARE); + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; +}; + +/* The vicam sensor has a resolution of 512 x 244, with I believe square + pixels, but this is forced to a 4:3 ratio by optics. So it has + non square pixels :( */ +static struct v4l2_pix_format vicam_mode[] = { + { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 256, + .sizeimage = 256 * 122, + .colorspace = V4L2_COLORSPACE_SRGB,}, + /* 2 modes with somewhat more square pixels */ + { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 256, + .sizeimage = 256 * 200, + .colorspace = V4L2_COLORSPACE_SRGB,}, + { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 256, + .sizeimage = 256 * 240, + .colorspace = V4L2_COLORSPACE_SRGB,}, +#if 0 /* This mode has extremely non square pixels, testing use only */ + { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 512, + .sizeimage = 512 * 122, + .colorspace = V4L2_COLORSPACE_SRGB,}, +#endif + { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 512, + .sizeimage = 512 * 244, + .colorspace = V4L2_COLORSPACE_SRGB,}, +}; + +static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, + u16 value, u16 index, u8 *data, u16 len) +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, len, 1000); + if (ret < 0) + pr_err("control msg req %02X error %d\n", request, ret); + + return ret; +} + +static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) +{ + int ret; + + ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0); + if (ret < 0) + return ret; + + if (state) + ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0); + + return ret; +} + +/* + * request and read a block of data - see warning on vicam_command. + */ +static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) +{ + int ret, unscaled_height, act_len = 0; + u8 *req_data = gspca_dev->usb_buf; + s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + + memset(req_data, 0, 16); + req_data[0] = gain; + if (gspca_dev->width == 256) + req_data[1] |= 0x01; /* low nibble x-scale */ + if (gspca_dev->height <= 122) { + req_data[1] |= 0x10; /* high nibble y-scale */ + unscaled_height = gspca_dev->height * 2; + } else + unscaled_height = gspca_dev->height; + req_data[2] = 0x90; /* unknown, does not seem to do anything */ + if (unscaled_height <= 200) + req_data[3] = 0x06; /* vend? */ + else if (unscaled_height <= 242) /* Yes 242 not 240 */ + req_data[3] = 0x07; /* vend? */ + else /* Up to 244 lines with req_data[3] == 0x08 */ + req_data[3] = 0x08; /* vend? */ + + if (expo < 256) { + /* Frame rate maxed out, use partial frame expo time */ + req_data[4] = 255 - expo; + req_data[5] = 0x00; + req_data[6] = 0x00; + req_data[7] = 0x01; + } else { + /* Modify frame rate */ + req_data[4] = 0x00; + req_data[5] = 0x00; + req_data[6] = expo & 0xFF; + req_data[7] = expo >> 8; + } + req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */ + /* bytes 9-15 do not seem to affect exposure or image quality */ + + mutex_lock(&gspca_dev->usb_lock); + ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + return ret; + + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + data, size, &act_len, 10000); + /* successful, it returns 0, otherwise negative */ + if (ret < 0 || act_len != size) { + pr_err("bulk read fail (%d) len %d/%d\n", + ret, act_len, size); + return -EIO; + } + return 0; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface we take the gspca + * usb_lock when performing USB operations. In practice the only thing we need + * to protect against is the usb_set_interface call that gspca makes during + * stream_off as the camera doesn't provide any controls that the user could try + * to change. + */ +static void vicam_dostream(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + int ret, frame_sz; + u8 *buffer; + + frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + + HEADER_SIZE; + buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA); + if (!buffer) { + pr_err("Couldn't allocate USB buffer\n"); + goto exit; + } + + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + ret = vicam_read_frame(gspca_dev, buffer, frame_sz); + if (ret < 0) + break; + + /* Note the frame header contents seem to be completely + constant, they do not change with either image, or + settings. So we simply discard it. The frames have + a very similar 64 byte footer, which we don't even + bother reading from the cam */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + buffer + HEADER_SIZE, + frame_sz - HEADER_SIZE); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + } +exit: + kfree(buffer); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *sd = (struct sd *)gspca_dev; + + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk = 1; + cam->bulk_size = 64; + cam->cam_mode = vicam_mode; + cam->nmodes = ARRAY_SIZE(vicam_mode); + + INIT_WORK(&sd->work_struct, vicam_dostream); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int ret; + const struct ihex_binrec *rec; + const struct firmware *uninitialized_var(fw); + u8 *firmware_buf; + + ret = request_ihex_firmware(&fw, VICAM_FIRMWARE, + &gspca_dev->dev->dev); + if (ret) { + pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); + return ret; + } + + firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!firmware_buf) { + ret = -ENOMEM; + goto exit; + } + for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { + memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len)); + ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf, + be16_to_cpu(rec->len)); + if (ret < 0) + break; + } + + kfree(firmware_buf); +exit: + release_firmware(fw); + return ret; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + int ret; + + ret = vicam_set_camera_power(gspca_dev, 1); + if (ret < 0) + return ret; + + /* Start the workqueue function to do the streaming */ + sd->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(sd->work_thread, &sd->work_struct); + + return 0; +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *)gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for vicam_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); + + if (gspca_dev->dev) + vicam_set_camera_power(gspca_dev, 0); +} + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 2); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_EXPOSURE, 0, 2047, 1, 256); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_GAIN, 0, 255, 1, 200); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* Table of supported USB devices */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x04c1, 0x009d)}, + {USB_DEVICE(0x0602, 0x1001)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/w996Xcf.c b/drivers/media/usb/gspca/w996Xcf.c new file mode 100644 index 000000000000..9e3a909e0a00 --- /dev/null +++ b/drivers/media/usb/gspca/w996Xcf.c @@ -0,0 +1,567 @@ +/** + * + * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip. + * + * Copyright (C) 2009 Hans de Goede + * + * This module is adapted from the in kernel v4l1 w9968cf driver: + * + * Copyright (C) 2002-2004 by Luca Risolia + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Note this is not a stand alone driver, it gets included in ov519.c, this + is a bit of a hack, but it needs the driver code for a lot of different + ov sensors which is already present in ov519.c (the old v4l1 driver used + the ovchipcam framework). When we have the time we really should move + the sensor drivers to v4l2 sub drivers, and properly split of this + driver from ov519.c */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ + +#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET]) +#define UV_QUANTABLE (&sd->jpeg_hdr[JPEG_QT1_OFFSET]) + +static const struct v4l2_pix_format w9968cf_vga_mode[] = { + {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 160 * 2, + .sizeimage = 160 * 120 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 176 * 2, + .sizeimage = 176 * 144 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352 * 2, + .sizeimage = 352 * 288 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static void reg_w(struct sd *sd, u16 index, u16 value); + +/*-------------------------------------------------------------------------- + Write 64-bit data to the fast serial bus registers. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static void w9968cf_write_fsb(struct sd *sd, u16* data) +{ + struct usb_device *udev = sd->gspca_dev.dev; + u16 value; + int ret; + + if (sd->gspca_dev.usb_err < 0) + return; + + value = *data++; + memcpy(sd->gspca_dev.usb_buf, data, 6); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, 0x06, sd->gspca_dev.usb_buf, 6, 500); + if (ret < 0) { + pr_err("Write FSB registers failed (%d)\n", ret); + sd->gspca_dev.usb_err = ret; + } +} + +/*-------------------------------------------------------------------------- + Write data to the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static void w9968cf_write_sb(struct sd *sd, u16 value) +{ + int ret; + + if (sd->gspca_dev.usb_err < 0) + return; + + /* We don't use reg_w here, as that would cause all writes when + bitbanging i2c to be logged, making the logs impossible to read */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0x01, NULL, 0, 500); + + udelay(W9968CF_I2C_BUS_DELAY); + + if (ret < 0) { + pr_err("Write SB reg [01] %04x failed\n", value); + sd->gspca_dev.usb_err = ret; + } +} + +/*-------------------------------------------------------------------------- + Read data from the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_sb(struct sd *sd) +{ + int ret; + + if (sd->gspca_dev.usb_err < 0) + return -1; + + /* We don't use reg_r here, as the w9968cf is special and has 16 + bit registers instead of 8 bit */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0x01, sd->gspca_dev.usb_buf, 2, 500); + if (ret >= 0) { + ret = sd->gspca_dev.usb_buf[0] | + (sd->gspca_dev.usb_buf[1] << 8); + } else { + pr_err("Read SB reg [01] failed\n"); + sd->gspca_dev.usb_err = ret; + } + + udelay(W9968CF_I2C_BUS_DELAY); + + return ret; +} + +/*-------------------------------------------------------------------------- + Upload quantization tables for the JPEG compression. + This function is called by w9968cf_start_transfer(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static void w9968cf_upload_quantizationtables(struct sd *sd) +{ + u16 a, b; + int i, j; + + reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */ + + for (i = 0, j = 0; i < 32; i++, j += 2) { + a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j + 1]) << 8); + b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j + 1]) << 8); + reg_w(sd, 0x40 + i, a); + reg_w(sd, 0x60 + i, b); + } + reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */ +} + +/**************************************************************************** + * Low-level I2C I/O functions. * + * The adapter supports the following I2C transfer functions: * + * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * + * i2c_adap_read_byte_data() * + * i2c_adap_read_byte() * + ****************************************************************************/ + +static void w9968cf_smbus_start(struct sd *sd) +{ + w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ +} + +static void w9968cf_smbus_stop(struct sd *sd) +{ + w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ +} + +static void w9968cf_smbus_write_byte(struct sd *sd, u8 v) +{ + u8 bit; + int sda; + + for (bit = 0 ; bit < 8 ; bit++) { + sda = (v & 0x80) ? 2 : 0; + v <<= 1; + /* SDE=1, SDA=sda, SCL=0 */ + w9968cf_write_sb(sd, 0x10 | sda); + /* SDE=1, SDA=sda, SCL=1 */ + w9968cf_write_sb(sd, 0x11 | sda); + /* SDE=1, SDA=sda, SCL=0 */ + w9968cf_write_sb(sd, 0x10 | sda); + } +} + +static void w9968cf_smbus_read_byte(struct sd *sd, u8 *v) +{ + u8 bit; + + /* No need to ensure SDA is high as we are always called after + read_ack which ends with SDA high */ + *v = 0; + for (bit = 0 ; bit < 8 ; bit++) { + *v <<= 1; + /* SDE=1, SDA=1, SCL=1 */ + w9968cf_write_sb(sd, 0x0013); + *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0; + /* SDE=1, SDA=1, SCL=0 */ + w9968cf_write_sb(sd, 0x0012); + } +} + +static void w9968cf_smbus_write_nack(struct sd *sd) +{ + /* No need to ensure SDA is high as we are always called after + read_byte which ends with SDA high */ + w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ +} + +static void w9968cf_smbus_read_ack(struct sd *sd) +{ + int sda; + + /* Ensure SDA is high before raising clock to avoid a spurious stop */ + w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + sda = w9968cf_read_sb(sd); + w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + if (sda >= 0 && (sda & 0x08)) { + PDEBUG(D_USBI, "Did not receive i2c ACK"); + sd->gspca_dev.usb_err = -EIO; + } +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ +static void w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value) +{ + u16* data = (u16 *)sd->gspca_dev.usb_buf; + + data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0); + data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0; + data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0); + data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0; + data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0; + data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0); + data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0; + data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0; + data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0); + data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0; + + w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0); + data[0] |= (reg & 0x40) ? 0x0540 : 0x0; + data[0] |= (reg & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0); + data[1] |= (reg & 0x10) ? 0x0054 : 0x0; + data[1] |= (reg & 0x08) ? 0x1500 : 0x0; + data[1] |= (reg & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0); + data[2] |= (reg & 0x02) ? 0x0150 : 0x0; + data[2] |= (reg & 0x01) ? 0x5400 : 0x0; + data[3] = 0x001d; + + w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); + data[0] |= (value & 0x40) ? 0x0540 : 0x0; + data[0] |= (value & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); + data[1] |= (value & 0x10) ? 0x0054 : 0x0; + data[1] |= (value & 0x08) ? 0x1500 : 0x0; + data[1] |= (value & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); + data[2] |= (value & 0x02) ? 0x0150 : 0x0; + data[2] |= (value & 0x01) ? 0x5400 : 0x0; + data[3] = 0xfe1d; + + w9968cf_write_fsb(sd, data); + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ +static int w9968cf_i2c_r(struct sd *sd, u8 reg) +{ + int ret = 0; + u8 value; + + /* Fast serial bus data control disable */ + w9968cf_write_sb(sd, 0x0013); /* don't change ! */ + + w9968cf_smbus_start(sd); + w9968cf_smbus_write_byte(sd, sd->sensor_addr); + w9968cf_smbus_read_ack(sd); + w9968cf_smbus_write_byte(sd, reg); + w9968cf_smbus_read_ack(sd); + w9968cf_smbus_stop(sd); + w9968cf_smbus_start(sd); + w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1); + w9968cf_smbus_read_ack(sd); + w9968cf_smbus_read_byte(sd, &value); + /* signal we don't want to read anymore, the v4l1 driver used to + send an ack here which is very wrong! (and then fixed + the issues this gave by retrying reads) */ + w9968cf_smbus_write_nack(sd); + w9968cf_smbus_stop(sd); + + /* Fast serial bus data control re-enable */ + w9968cf_write_sb(sd, 0x0030); + + if (sd->gspca_dev.usb_err >= 0) { + ret = value; + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + } else + PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + + return ret; +} + +/*-------------------------------------------------------------------------- + Turn on the LED on some webcams. A beep should be heard too. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static void w9968cf_configure(struct sd *sd) +{ + reg_w(sd, 0x00, 0xff00); /* power-down */ + reg_w(sd, 0x00, 0xbf17); /* reset everything */ + reg_w(sd, 0x00, 0xbf10); /* normal operation */ + reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */ + reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */ + reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */ + reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */ + + sd->stopped = 1; +} + +static void w9968cf_init(struct sd *sd) +{ + unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2), + y0 = 0x0000, + u0 = y0 + hw_bufsize / 2, + v0 = u0 + hw_bufsize / 4, + y1 = v0 + hw_bufsize / 4, + u1 = y1 + hw_bufsize / 2, + v1 = u1 + hw_bufsize / 4; + + reg_w(sd, 0x00, 0xff00); /* power off */ + reg_w(sd, 0x00, 0xbf10); /* power on */ + + reg_w(sd, 0x03, 0x405d); /* DRAM timings */ + reg_w(sd, 0x04, 0x0030); /* SDRAM timings */ + + reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */ + reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */ + reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */ + reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */ + reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */ + reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */ + + reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */ + reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */ + reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */ + reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */ + reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */ + reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */ + + reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */ + reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */ + + reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */ + reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */ + + reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */ + reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/ + reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */ + reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */ +} + +static void w9968cf_set_crop_window(struct sd *sd) +{ + int start_cropx, start_cropy, x, y, fw, fh, cw, ch, + max_width, max_height; + + if (sd->sif) { + max_width = 352; + max_height = 288; + } else { + max_width = 640; + max_height = 480; + } + + if (sd->sensor == SEN_OV7620) { + /* + * Sigh, this is dependend on the clock / framerate changes + * made by the frequency control, sick. + * + * Note we cannot use v4l2_ctrl_g_ctrl here, as we get called + * from ov519.c:setfreq() with the ctrl lock held! + */ + if (sd->freq->val == 1) { + start_cropx = 277; + start_cropy = 37; + } else { + start_cropx = 105; + start_cropy = 37; + } + } else { + start_cropx = 320; + start_cropy = 35; + } + + /* Work around to avoid FP arithmetics */ + #define SC(x) ((x) << 10) + + /* Scaling factors */ + fw = SC(sd->gspca_dev.width) / max_width; + fh = SC(sd->gspca_dev.height) / max_height; + + cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width) / fh; + ch = (fw >= fh) ? SC(sd->gspca_dev.height) / fw : max_height; + + sd->sensor_width = max_width; + sd->sensor_height = max_height; + + x = (max_width - cw) / 2; + y = (max_height - ch) / 2; + + reg_w(sd, 0x10, start_cropx + x); + reg_w(sd, 0x11, start_cropy + y); + reg_w(sd, 0x12, start_cropx + x + cw); + reg_w(sd, 0x13, start_cropy + y + ch); +} + +static void w9968cf_mode_init_regs(struct sd *sd) +{ + int val, vs_polarity, hs_polarity; + + w9968cf_set_crop_window(sd); + + reg_w(sd, 0x14, sd->gspca_dev.width); + reg_w(sd, 0x15, sd->gspca_dev.height); + + /* JPEG width & height */ + reg_w(sd, 0x30, sd->gspca_dev.width); + reg_w(sd, 0x31, sd->gspca_dev.height); + + /* Y & UV frame buffer strides (in WORD) */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + reg_w(sd, 0x2c, sd->gspca_dev.width / 2); + reg_w(sd, 0x2d, sd->gspca_dev.width / 4); + } else + reg_w(sd, 0x2c, sd->gspca_dev.width); + + reg_w(sd, 0x00, 0xbf17); /* reset everything */ + reg_w(sd, 0x00, 0xbf10); /* normal operation */ + + /* Transfer size in WORDS (for UYVY format only) */ + val = sd->gspca_dev.width * sd->gspca_dev.height; + reg_w(sd, 0x3d, val & 0xffff); /* low bits */ + reg_w(sd, 0x3e, val >> 16); /* high bits */ + + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* We may get called multiple times (usb isoc bw negotiat.) */ + jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, + sd->gspca_dev.width, 0x22); /* JPEG 420 */ + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); + w9968cf_upload_quantizationtables(sd); + v4l2_ctrl_grab(sd->jpegqual, true); + } + + /* Video Capture Control Register */ + if (sd->sensor == SEN_OV7620) { + /* Seems to work around a bug in the image sensor */ + vs_polarity = 1; + hs_polarity = 1; + } else { + vs_polarity = 1; + hs_polarity = 0; + } + + val = (vs_polarity << 12) | (hs_polarity << 11); + + /* NOTE: We may not have enough memory to do double buffering while + doing compression (amount of memory differs per model cam). + So we use the second image buffer also as jpeg stream buffer + (see w9968cf_init), and disable double buffering. */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* val |= 0x0002; YUV422P */ + val |= 0x0003; /* YUV420P */ + } else + val |= 0x0080; /* Enable HW double buffering */ + + /* val |= 0x0020; enable clamping */ + /* val |= 0x0008; enable (1-2-1) filter */ + /* val |= 0x000c; enable (2-3-6-3-2) filter */ + + val |= 0x8000; /* capt. enable */ + + reg_w(sd, 0x16, val); + + sd->gspca_dev.empty_packet = 0; +} + +static void w9968cf_stop0(struct sd *sd) +{ + v4l2_ctrl_grab(sd->jpegqual, false); + reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ + reg_w(sd, 0x16, 0x0000); /* stop video capture */ +} + +/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF + for the next frame). This seems to simply not be true when operating + in JPEG mode, in this case there may be empty packets within the + frame. So in JPEG mode use the JPEG SOI marker to detect SOF. + + Note to make things even more interesting the w9968cf sends *PLANAR* jpeg, + to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS, + V-data, EOI. */ +static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + if (len >= 2 && + data[0] == 0xff && + data[1] == 0xd8) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* Strip the ff d8, our own header (which adds + huffman and quantization tables) already has this */ + len -= 2; + data += 2; + } + } else { + /* In UYVY mode an empty packet signals EOF */ + if (gspca_dev->empty_packet) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + NULL, 0); + gspca_dev->empty_packet = 0; + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} diff --git a/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c new file mode 100644 index 000000000000..13b8d395d210 --- /dev/null +++ b/drivers/media/usb/gspca/xirlink_cit.c @@ -0,0 +1,3145 @@ +/* + * USB IBM C-It Video Camera driver + * + * Supports Xirlink C-It Video Camera, IBM PC Camera, + * IBM NetCamera and Veo Stingray. + * + * Copyright (C) 2010 Hans de Goede + * + * This driver is based on earlier work of: + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "xirlink-cit" + +#include +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Xirlink C-IT"); +MODULE_LICENSE("GPL"); + +/* FIXME we should autodetect this */ +static int ibm_netcam_pro; +module_param(ibm_netcam_pro, int, 0); +MODULE_PARM_DESC(ibm_netcam_pro, + "Use IBM Netcamera Pro init sequences for Model 3 cams"); + +/* FIXME this should be handled through the V4L2 input selection API */ +static int rca_input; +module_param(rca_input, int, 0644); +MODULE_PARM_DESC(rca_input, + "Use rca input instead of ccd sensor on Model 3 cams"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_ctrl *lighting; + u8 model; +#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */ +#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */ +#define CIT_MODEL2 2 /* ibmcam driver */ +#define CIT_MODEL3 3 +#define CIT_MODEL4 4 +#define CIT_IBM_NETCAM_PRO 5 + u8 input_index; + u8 button_state; + u8 stop_on_control_change; + u8 sof_read; + u8 sof_len; +}; + +static void sd_stop0(struct gspca_dev *gspca_dev); + +static const struct v4l2_pix_format cif_yuv_mode[] = { + {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {352, 288, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; + +static const struct v4l2_pix_format vga_yuv_mode[] = { + {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {640, 480, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; + +static const struct v4l2_pix_format model0_mode[] = { + {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; + +static const struct v4l2_pix_format model2_mode[] = { + {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 2 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, + {352, 288, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 + 4, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; + +/* + * 01.01.08 - Added for RCA video in support -LO + * This struct is used to init the Model3 cam to use the RCA video in port + * instead of the CCD sensor. + */ +static const u16 rca_initdata[][3] = { + {0, 0x0000, 0x010c}, + {0, 0x0006, 0x012c}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {1, 0x0000, 0x0116}, + {0, 0x0064, 0x0116}, + {1, 0x0000, 0x0115}, + {0, 0x0003, 0x0115}, + {0, 0x0008, 0x0123}, + {0, 0x0000, 0x0117}, + {0, 0x0000, 0x0112}, + {0, 0x0080, 0x0100}, + {0, 0x0000, 0x0100}, + {1, 0x0000, 0x0116}, + {0, 0x0060, 0x0116}, + {0, 0x0002, 0x0112}, + {0, 0x0000, 0x0123}, + {0, 0x0001, 0x0117}, + {0, 0x0040, 0x0108}, + {0, 0x0019, 0x012c}, + {0, 0x0040, 0x0116}, + {0, 0x000a, 0x0115}, + {0, 0x000b, 0x0115}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {0, 0x0064, 0x0116}, + {0, 0x0000, 0x0115}, + {0, 0x0001, 0x0115}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00aa, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f2, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f8, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00fc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f9, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x003c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0027, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0019, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0021, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0006, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0045, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002a, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002b, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f4, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0004, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002d, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0053, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0000, 0x0101}, + {0, 0x00a0, 0x0103}, + {0, 0x0078, 0x0105}, + {0, 0x0000, 0x010a}, + {0, 0x0024, 0x010b}, + {0, 0x0028, 0x0119}, + {0, 0x0088, 0x011b}, + {0, 0x0002, 0x011d}, + {0, 0x0003, 0x011e}, + {0, 0x0000, 0x0129}, + {0, 0x00fc, 0x012b}, + {0, 0x0008, 0x0102}, + {0, 0x0000, 0x0104}, + {0, 0x0008, 0x011a}, + {0, 0x0028, 0x011c}, + {0, 0x0021, 0x012a}, + {0, 0x0000, 0x0118}, + {0, 0x0000, 0x0132}, + {0, 0x0000, 0x0109}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0031, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00dc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0032, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0020, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0030, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0008, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0003, 0x0111}, +}; + +/* TESTME the old ibmcam driver repeats certain commands to Model1 cameras, we + do the same for now (testing needed to see if this is really necessary) */ +static const int cit_model1_ntries = 5; +static const int cit_model1_ntries2 = 2; + +static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + struct usb_device *udev = gspca_dev->dev; + int err; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, index, NULL, 0, 1000); + if (err < 0) + pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", + index, value, err); + + return 0; +} + +static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose) +{ + struct usb_device *udev = gspca_dev->dev; + __u8 *buf = gspca_dev->usb_buf; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0x00, index, buf, 8, 1000); + if (res < 0) { + pr_err("Failed to read a register (index 0x%04X, error %d)\n", + index, res); + return res; + } + + if (verbose) + PDEBUG(D_PROBE, "Register %04x value: %02x", index, buf[0]); + + return 0; +} + +/* + * cit_send_FF_04_02() + * + * This procedure sends magic 3-command prefix to the camera. + * The purpose of this prefix is not known. + * + * History: + * 1/2/00 Created. + */ +static void cit_send_FF_04_02(struct gspca_dev *gspca_dev) +{ + cit_write_reg(gspca_dev, 0x00FF, 0x0127); + cit_write_reg(gspca_dev, 0x0004, 0x0124); + cit_write_reg(gspca_dev, 0x0002, 0x0124); +} + +static void cit_send_00_04_06(struct gspca_dev *gspca_dev) +{ + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0x0004, 0x0124); + cit_write_reg(gspca_dev, 0x0006, 0x0124); +} + +static void cit_send_x_00(struct gspca_dev *gspca_dev, unsigned short x) +{ + cit_write_reg(gspca_dev, x, 0x0127); + cit_write_reg(gspca_dev, 0x0000, 0x0124); +} + +static void cit_send_x_00_05(struct gspca_dev *gspca_dev, unsigned short x) +{ + cit_send_x_00(gspca_dev, x); + cit_write_reg(gspca_dev, 0x0005, 0x0124); +} + +static void cit_send_x_00_05_02(struct gspca_dev *gspca_dev, unsigned short x) +{ + cit_write_reg(gspca_dev, x, 0x0127); + cit_write_reg(gspca_dev, 0x0000, 0x0124); + cit_write_reg(gspca_dev, 0x0005, 0x0124); + cit_write_reg(gspca_dev, 0x0002, 0x0124); +} + +static void cit_send_x_01_00_05(struct gspca_dev *gspca_dev, u16 x) +{ + cit_write_reg(gspca_dev, x, 0x0127); + cit_write_reg(gspca_dev, 0x0001, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0124); + cit_write_reg(gspca_dev, 0x0005, 0x0124); +} + +static void cit_send_x_00_05_02_01(struct gspca_dev *gspca_dev, u16 x) +{ + cit_write_reg(gspca_dev, x, 0x0127); + cit_write_reg(gspca_dev, 0x0000, 0x0124); + cit_write_reg(gspca_dev, 0x0005, 0x0124); + cit_write_reg(gspca_dev, 0x0002, 0x0124); + cit_write_reg(gspca_dev, 0x0001, 0x0124); +} + +static void cit_send_x_00_05_02_08_01(struct gspca_dev *gspca_dev, u16 x) +{ + cit_write_reg(gspca_dev, x, 0x0127); + cit_write_reg(gspca_dev, 0x0000, 0x0124); + cit_write_reg(gspca_dev, 0x0005, 0x0124); + cit_write_reg(gspca_dev, 0x0002, 0x0124); + cit_write_reg(gspca_dev, 0x0008, 0x0124); + cit_write_reg(gspca_dev, 0x0001, 0x0124); +} + +static void cit_Packet_Format1(struct gspca_dev *gspca_dev, u16 fkey, u16 val) +{ + cit_send_x_01_00_05(gspca_dev, 0x0088); + cit_send_x_00_05(gspca_dev, fkey); + cit_send_x_00_05_02_08_01(gspca_dev, val); + cit_send_x_00_05(gspca_dev, 0x0088); + cit_send_x_00_05_02_01(gspca_dev, fkey); + cit_send_x_00_05(gspca_dev, 0x0089); + cit_send_x_00(gspca_dev, fkey); + cit_send_00_04_06(gspca_dev); + cit_read_reg(gspca_dev, 0x0126, 0); + cit_send_FF_04_02(gspca_dev); +} + +static void cit_PacketFormat2(struct gspca_dev *gspca_dev, u16 fkey, u16 val) +{ + cit_send_x_01_00_05(gspca_dev, 0x0088); + cit_send_x_00_05(gspca_dev, fkey); + cit_send_x_00_05_02(gspca_dev, val); +} + +static void cit_model2_Packet2(struct gspca_dev *gspca_dev) +{ + cit_write_reg(gspca_dev, 0x00ff, 0x012d); + cit_write_reg(gspca_dev, 0xfea3, 0x0124); +} + +static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) +{ + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x00ff, 0x012e); + cit_write_reg(gspca_dev, v1, 0x012f); + cit_write_reg(gspca_dev, 0x00ff, 0x0130); + cit_write_reg(gspca_dev, 0xc719, 0x0124); + cit_write_reg(gspca_dev, v2, 0x0127); + + cit_model2_Packet2(gspca_dev); +} + +/* + * cit_model3_Packet1() + * + * 00_0078_012d + * 00_0097_012f + * 00_d141_0124 + * 00_0096_0127 + * 00_fea8_0124 +*/ +static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) +{ + cit_write_reg(gspca_dev, 0x0078, 0x012d); + cit_write_reg(gspca_dev, v1, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, v2, 0x0127); + cit_write_reg(gspca_dev, 0xfea8, 0x0124); +} + +static void cit_model4_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) +{ + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, v1, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, v2, 0x0127); + cit_write_reg(gspca_dev, 0xfea8, 0x0124); +} + +static void cit_model4_BrightnessPacket(struct gspca_dev *gspca_dev, u16 val) +{ + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0026, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, val, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0038, 0x012d); + cit_write_reg(gspca_dev, 0x0004, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + sd->model = id->driver_info; + if (sd->model == CIT_MODEL3 && ibm_netcam_pro) + sd->model = CIT_IBM_NETCAM_PRO; + + cam = &gspca_dev->cam; + switch (sd->model) { + case CIT_MODEL0: + cam->cam_mode = model0_mode; + cam->nmodes = ARRAY_SIZE(model0_mode); + sd->sof_len = 4; + break; + case CIT_MODEL1: + cam->cam_mode = cif_yuv_mode; + cam->nmodes = ARRAY_SIZE(cif_yuv_mode); + sd->sof_len = 4; + break; + case CIT_MODEL2: + cam->cam_mode = model2_mode + 1; /* no 160x120 */ + cam->nmodes = 3; + break; + case CIT_MODEL3: + cam->cam_mode = vga_yuv_mode; + cam->nmodes = ARRAY_SIZE(vga_yuv_mode); + sd->stop_on_control_change = 1; + sd->sof_len = 4; + break; + case CIT_MODEL4: + cam->cam_mode = model2_mode; + cam->nmodes = ARRAY_SIZE(model2_mode); + break; + case CIT_IBM_NETCAM_PRO: + cam->cam_mode = vga_yuv_mode; + cam->nmodes = 2; /* no 640 x 480 */ + cam->input_flags = V4L2_IN_ST_VFLIP; + sd->stop_on_control_change = 1; + sd->sof_len = 4; + break; + } + + return 0; +} + +static int cit_init_model0(struct gspca_dev *gspca_dev) +{ + cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */ + cit_write_reg(gspca_dev, 0x0001, 0x0112); /* turn on autogain ? */ + cit_write_reg(gspca_dev, 0x0000, 0x0400); + cit_write_reg(gspca_dev, 0x0001, 0x0400); + cit_write_reg(gspca_dev, 0x0000, 0x0420); + cit_write_reg(gspca_dev, 0x0001, 0x0420); + cit_write_reg(gspca_dev, 0x000d, 0x0409); + cit_write_reg(gspca_dev, 0x0002, 0x040a); + cit_write_reg(gspca_dev, 0x0018, 0x0405); + cit_write_reg(gspca_dev, 0x0008, 0x0435); + cit_write_reg(gspca_dev, 0x0026, 0x040b); + cit_write_reg(gspca_dev, 0x0007, 0x0437); + cit_write_reg(gspca_dev, 0x0015, 0x042f); + cit_write_reg(gspca_dev, 0x002b, 0x0439); + cit_write_reg(gspca_dev, 0x0026, 0x043a); + cit_write_reg(gspca_dev, 0x0008, 0x0438); + cit_write_reg(gspca_dev, 0x001e, 0x042b); + cit_write_reg(gspca_dev, 0x0041, 0x042c); + + return 0; +} + +static int cit_init_ibm_netcam_pro(struct gspca_dev *gspca_dev) +{ + cit_read_reg(gspca_dev, 0x128, 1); + cit_write_reg(gspca_dev, 0x0003, 0x0133); + cit_write_reg(gspca_dev, 0x0000, 0x0117); + cit_write_reg(gspca_dev, 0x0008, 0x0123); + cit_write_reg(gspca_dev, 0x0000, 0x0100); + cit_read_reg(gspca_dev, 0x0116, 0); + cit_write_reg(gspca_dev, 0x0060, 0x0116); + cit_write_reg(gspca_dev, 0x0002, 0x0112); + cit_write_reg(gspca_dev, 0x0000, 0x0133); + cit_write_reg(gspca_dev, 0x0000, 0x0123); + cit_write_reg(gspca_dev, 0x0001, 0x0117); + cit_write_reg(gspca_dev, 0x0040, 0x0108); + cit_write_reg(gspca_dev, 0x0019, 0x012c); + cit_write_reg(gspca_dev, 0x0060, 0x0116); + cit_write_reg(gspca_dev, 0x0002, 0x0115); + cit_write_reg(gspca_dev, 0x000b, 0x0115); + + cit_write_reg(gspca_dev, 0x0078, 0x012d); + cit_write_reg(gspca_dev, 0x0001, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0079, 0x012d); + cit_write_reg(gspca_dev, 0x00ff, 0x0130); + cit_write_reg(gspca_dev, 0xcd41, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_read_reg(gspca_dev, 0x0126, 1); + + cit_model3_Packet1(gspca_dev, 0x0000, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0000, 0x0001); + cit_model3_Packet1(gspca_dev, 0x000b, 0x0000); + cit_model3_Packet1(gspca_dev, 0x000c, 0x0008); + cit_model3_Packet1(gspca_dev, 0x000d, 0x003a); + cit_model3_Packet1(gspca_dev, 0x000e, 0x0060); + cit_model3_Packet1(gspca_dev, 0x000f, 0x0060); + cit_model3_Packet1(gspca_dev, 0x0010, 0x0008); + cit_model3_Packet1(gspca_dev, 0x0011, 0x0004); + cit_model3_Packet1(gspca_dev, 0x0012, 0x0028); + cit_model3_Packet1(gspca_dev, 0x0013, 0x0002); + cit_model3_Packet1(gspca_dev, 0x0014, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0015, 0x00fb); + cit_model3_Packet1(gspca_dev, 0x0016, 0x0002); + cit_model3_Packet1(gspca_dev, 0x0017, 0x0037); + cit_model3_Packet1(gspca_dev, 0x0018, 0x0036); + cit_model3_Packet1(gspca_dev, 0x001e, 0x0000); + cit_model3_Packet1(gspca_dev, 0x001f, 0x0008); + cit_model3_Packet1(gspca_dev, 0x0020, 0x00c1); + cit_model3_Packet1(gspca_dev, 0x0021, 0x0034); + cit_model3_Packet1(gspca_dev, 0x0022, 0x0034); + cit_model3_Packet1(gspca_dev, 0x0025, 0x0002); + cit_model3_Packet1(gspca_dev, 0x0028, 0x0022); + cit_model3_Packet1(gspca_dev, 0x0029, 0x000a); + cit_model3_Packet1(gspca_dev, 0x002b, 0x0000); + cit_model3_Packet1(gspca_dev, 0x002c, 0x0000); + cit_model3_Packet1(gspca_dev, 0x002d, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x002e, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x002f, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x0030, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x0031, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x0032, 0x0007); + cit_model3_Packet1(gspca_dev, 0x0033, 0x0005); + cit_model3_Packet1(gspca_dev, 0x0037, 0x0040); + cit_model3_Packet1(gspca_dev, 0x0039, 0x0000); + cit_model3_Packet1(gspca_dev, 0x003a, 0x0000); + cit_model3_Packet1(gspca_dev, 0x003b, 0x0001); + cit_model3_Packet1(gspca_dev, 0x003c, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0040, 0x000c); + cit_model3_Packet1(gspca_dev, 0x0041, 0x00fb); + cit_model3_Packet1(gspca_dev, 0x0042, 0x0002); + cit_model3_Packet1(gspca_dev, 0x0043, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0045, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0046, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0047, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0048, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0049, 0x0000); + cit_model3_Packet1(gspca_dev, 0x004a, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x004b, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x004c, 0x00ff); + cit_model3_Packet1(gspca_dev, 0x004f, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0050, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0051, 0x0002); + cit_model3_Packet1(gspca_dev, 0x0055, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0056, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0057, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0058, 0x0002); + cit_model3_Packet1(gspca_dev, 0x0059, 0x0000); + cit_model3_Packet1(gspca_dev, 0x005c, 0x0016); + cit_model3_Packet1(gspca_dev, 0x005d, 0x0022); + cit_model3_Packet1(gspca_dev, 0x005e, 0x003c); + cit_model3_Packet1(gspca_dev, 0x005f, 0x0050); + cit_model3_Packet1(gspca_dev, 0x0060, 0x0044); + cit_model3_Packet1(gspca_dev, 0x0061, 0x0005); + cit_model3_Packet1(gspca_dev, 0x006a, 0x007e); + cit_model3_Packet1(gspca_dev, 0x006f, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0072, 0x001b); + cit_model3_Packet1(gspca_dev, 0x0073, 0x0005); + cit_model3_Packet1(gspca_dev, 0x0074, 0x000a); + cit_model3_Packet1(gspca_dev, 0x0075, 0x001b); + cit_model3_Packet1(gspca_dev, 0x0076, 0x002a); + cit_model3_Packet1(gspca_dev, 0x0077, 0x003c); + cit_model3_Packet1(gspca_dev, 0x0078, 0x0050); + cit_model3_Packet1(gspca_dev, 0x007b, 0x0000); + cit_model3_Packet1(gspca_dev, 0x007c, 0x0011); + cit_model3_Packet1(gspca_dev, 0x007d, 0x0024); + cit_model3_Packet1(gspca_dev, 0x007e, 0x0043); + cit_model3_Packet1(gspca_dev, 0x007f, 0x005a); + cit_model3_Packet1(gspca_dev, 0x0084, 0x0020); + cit_model3_Packet1(gspca_dev, 0x0085, 0x0033); + cit_model3_Packet1(gspca_dev, 0x0086, 0x000a); + cit_model3_Packet1(gspca_dev, 0x0087, 0x0030); + cit_model3_Packet1(gspca_dev, 0x0088, 0x0070); + cit_model3_Packet1(gspca_dev, 0x008b, 0x0008); + cit_model3_Packet1(gspca_dev, 0x008f, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0090, 0x0006); + cit_model3_Packet1(gspca_dev, 0x0091, 0x0028); + cit_model3_Packet1(gspca_dev, 0x0092, 0x005a); + cit_model3_Packet1(gspca_dev, 0x0093, 0x0082); + cit_model3_Packet1(gspca_dev, 0x0096, 0x0014); + cit_model3_Packet1(gspca_dev, 0x0097, 0x0020); + cit_model3_Packet1(gspca_dev, 0x0098, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00b0, 0x0046); + cit_model3_Packet1(gspca_dev, 0x00b1, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00b2, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00b3, 0x0004); + cit_model3_Packet1(gspca_dev, 0x00b4, 0x0007); + cit_model3_Packet1(gspca_dev, 0x00b6, 0x0002); + cit_model3_Packet1(gspca_dev, 0x00b7, 0x0004); + cit_model3_Packet1(gspca_dev, 0x00bb, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00bc, 0x0001); + cit_model3_Packet1(gspca_dev, 0x00bd, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00bf, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00c0, 0x00c8); + cit_model3_Packet1(gspca_dev, 0x00c1, 0x0014); + cit_model3_Packet1(gspca_dev, 0x00c2, 0x0001); + cit_model3_Packet1(gspca_dev, 0x00c3, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00c4, 0x0004); + cit_model3_Packet1(gspca_dev, 0x00cb, 0x00bf); + cit_model3_Packet1(gspca_dev, 0x00cc, 0x00bf); + cit_model3_Packet1(gspca_dev, 0x00cd, 0x00bf); + cit_model3_Packet1(gspca_dev, 0x00ce, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00cf, 0x0020); + cit_model3_Packet1(gspca_dev, 0x00d0, 0x0040); + cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf); + cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf); + cit_model3_Packet1(gspca_dev, 0x00d2, 0x00bf); + cit_model3_Packet1(gspca_dev, 0x00d3, 0x00bf); + cit_model3_Packet1(gspca_dev, 0x00ea, 0x0008); + cit_model3_Packet1(gspca_dev, 0x00eb, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00ec, 0x00e8); + cit_model3_Packet1(gspca_dev, 0x00ed, 0x0001); + cit_model3_Packet1(gspca_dev, 0x00ef, 0x0022); + cit_model3_Packet1(gspca_dev, 0x00f0, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00f2, 0x0028); + cit_model3_Packet1(gspca_dev, 0x00f4, 0x0002); + cit_model3_Packet1(gspca_dev, 0x00f5, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00fa, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00fb, 0x0001); + cit_model3_Packet1(gspca_dev, 0x00fc, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00fd, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00fe, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00ff, 0x0000); + + cit_model3_Packet1(gspca_dev, 0x00be, 0x0003); + cit_model3_Packet1(gspca_dev, 0x00c8, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00c9, 0x0020); + cit_model3_Packet1(gspca_dev, 0x00ca, 0x0040); + cit_model3_Packet1(gspca_dev, 0x0053, 0x0001); + cit_model3_Packet1(gspca_dev, 0x0082, 0x000e); + cit_model3_Packet1(gspca_dev, 0x0083, 0x0020); + cit_model3_Packet1(gspca_dev, 0x0034, 0x003c); + cit_model3_Packet1(gspca_dev, 0x006e, 0x0055); + cit_model3_Packet1(gspca_dev, 0x0062, 0x0005); + cit_model3_Packet1(gspca_dev, 0x0063, 0x0008); + cit_model3_Packet1(gspca_dev, 0x0066, 0x000a); + cit_model3_Packet1(gspca_dev, 0x0067, 0x0006); + cit_model3_Packet1(gspca_dev, 0x006b, 0x0010); + cit_model3_Packet1(gspca_dev, 0x005a, 0x0001); + cit_model3_Packet1(gspca_dev, 0x005b, 0x000a); + cit_model3_Packet1(gspca_dev, 0x0023, 0x0006); + cit_model3_Packet1(gspca_dev, 0x0026, 0x0004); + cit_model3_Packet1(gspca_dev, 0x0036, 0x0069); + cit_model3_Packet1(gspca_dev, 0x0038, 0x0064); + cit_model3_Packet1(gspca_dev, 0x003d, 0x0003); + cit_model3_Packet1(gspca_dev, 0x003e, 0x0001); + cit_model3_Packet1(gspca_dev, 0x00b8, 0x0014); + cit_model3_Packet1(gspca_dev, 0x00b9, 0x0014); + cit_model3_Packet1(gspca_dev, 0x00e6, 0x0004); + cit_model3_Packet1(gspca_dev, 0x00e8, 0x0001); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->model) { + case CIT_MODEL0: + cit_init_model0(gspca_dev); + sd_stop0(gspca_dev); + break; + case CIT_MODEL1: + case CIT_MODEL2: + case CIT_MODEL3: + case CIT_MODEL4: + break; /* All is done in sd_start */ + case CIT_IBM_NETCAM_PRO: + cit_init_ibm_netcam_pro(gspca_dev); + sd_stop0(gspca_dev); + break; + } + return 0; +} + +static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + switch (sd->model) { + case CIT_MODEL0: + case CIT_IBM_NETCAM_PRO: + /* No (known) brightness control for these */ + break; + case CIT_MODEL1: + /* Model 1: Brightness range 0 - 63 */ + cit_Packet_Format1(gspca_dev, 0x0031, val); + cit_Packet_Format1(gspca_dev, 0x0032, val); + cit_Packet_Format1(gspca_dev, 0x0033, val); + break; + case CIT_MODEL2: + /* Model 2: Brightness range 0x60 - 0xee */ + /* Scale 0 - 63 to 0x60 - 0xee */ + i = 0x60 + val * 2254 / 1000; + cit_model2_Packet1(gspca_dev, 0x001a, i); + break; + case CIT_MODEL3: + /* Model 3: Brightness range 'i' in [0x0C..0x3F] */ + i = val; + if (i < 0x0c) + i = 0x0c; + cit_model3_Packet1(gspca_dev, 0x0036, i); + break; + case CIT_MODEL4: + /* Model 4: Brightness range 'i' in [0x04..0xb4] */ + /* Scale 0 - 63 to 0x04 - 0xb4 */ + i = 0x04 + val * 2794 / 1000; + cit_model4_BrightnessPacket(gspca_dev, i); + break; + } + + return 0; +} + +static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->model) { + case CIT_MODEL0: { + int i; + /* gain 0-15, 0-20 -> 0-15 */ + i = val * 1000 / 1333; + cit_write_reg(gspca_dev, i, 0x0422); + /* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */ + i = val * 2000 / 1333; + cit_write_reg(gspca_dev, i, 0x0423); + /* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63 */ + i = val * 4000 / 1333; + cit_write_reg(gspca_dev, i, 0x0424); + /* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */ + i = val * 8000 / 1333; + cit_write_reg(gspca_dev, i, 0x0425); + break; + } + case CIT_MODEL2: + case CIT_MODEL4: + /* These models do not have this control. */ + break; + case CIT_MODEL1: + { + /* Scale 0 - 20 to 15 - 0 */ + int i, new_contrast = (20 - val) * 1000 / 1333; + for (i = 0; i < cit_model1_ntries; i++) { + cit_Packet_Format1(gspca_dev, 0x0014, new_contrast); + cit_send_FF_04_02(gspca_dev); + } + break; + } + case CIT_MODEL3: + { /* Preset hardware values */ + static const struct { + unsigned short cv1; + unsigned short cv2; + unsigned short cv3; + } cv[7] = { + { 0x05, 0x05, 0x0f }, /* Minimum */ + { 0x04, 0x04, 0x16 }, + { 0x02, 0x03, 0x16 }, + { 0x02, 0x08, 0x16 }, + { 0x01, 0x0c, 0x16 }, + { 0x01, 0x0e, 0x16 }, + { 0x01, 0x10, 0x16 } /* Maximum */ + }; + int i = val / 3; + cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1); + cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2); + cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3); + break; + } + case CIT_IBM_NETCAM_PRO: + cit_model3_Packet1(gspca_dev, 0x005b, val + 1); + break; + } + return 0; +} + +static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->model) { + case CIT_MODEL0: + case CIT_MODEL1: + case CIT_IBM_NETCAM_PRO: + /* No hue control for these models */ + break; + case CIT_MODEL2: + cit_model2_Packet1(gspca_dev, 0x0024, val); + /* cit_model2_Packet1(gspca_dev, 0x0020, sat); */ + break; + case CIT_MODEL3: { + /* Model 3: Brightness range 'i' in [0x05..0x37] */ + /* TESTME according to the ibmcam driver this does not work */ + if (0) { + /* Scale 0 - 127 to 0x05 - 0x37 */ + int i = 0x05 + val * 1000 / 2540; + cit_model3_Packet1(gspca_dev, 0x007e, i); + } + break; + } + case CIT_MODEL4: + /* HDG: taken from ibmcam, setting the color gains does not + * really belong here. + * + * I am not sure r/g/b_gain variables exactly control gain + * of those channels. Most likely they subtly change some + * very internal image processing settings in the camera. + * In any case, here is what they do, and feel free to tweak: + * + * r_gain: seriously affects red gain + * g_gain: seriously affects green gain + * b_gain: seriously affects blue gain + * hue: changes average color from violet (0) to red (0xFF) + */ + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x001e, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 160, 0x0127); /* Green gain */ + cit_write_reg(gspca_dev, 160, 0x012e); /* Red gain */ + cit_write_reg(gspca_dev, 160, 0x0130); /* Blue gain */ + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, val, 0x012d); /* Hue */ + cit_write_reg(gspca_dev, 0xf545, 0x0124); + break; + } + return 0; +} + +static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->model) { + case CIT_MODEL0: + case CIT_MODEL2: + case CIT_MODEL4: + case CIT_IBM_NETCAM_PRO: + /* These models do not have this control */ + break; + case CIT_MODEL1: { + int i; + const unsigned short sa[] = { + 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; + + for (i = 0; i < cit_model1_ntries; i++) + cit_PacketFormat2(gspca_dev, 0x0013, sa[val]); + break; + } + case CIT_MODEL3: + { /* + * "Use a table of magic numbers. + * This setting doesn't really change much. + * But that's how Windows does it." + */ + static const struct { + unsigned short sv1; + unsigned short sv2; + unsigned short sv3; + unsigned short sv4; + } sv[7] = { + { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */ + { 0x01, 0x04, 0x05, 0x14 }, + { 0x02, 0x04, 0x05, 0x14 }, + { 0x03, 0x04, 0x05, 0x14 }, + { 0x03, 0x05, 0x05, 0x14 }, + { 0x03, 0x06, 0x05, 0x14 }, + { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */ + }; + cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1); + cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2); + cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3); + cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4); + break; + } + } + return 0; +} + +/* + * cit_set_lighting() + * + * Camera model 1: + * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low. + * + * Camera model 2: + * We have 16 levels of lighting, 0 for bright light and up to 15 for + * low light. But values above 5 or so are useless because camera is + * not really capable to produce anything worth viewing at such light. + * This setting may be altered only in certain camera state. + * + * Low lighting forces slower FPS. + * + * History: + * 1/5/00 Created. + * 2/20/00 Added support for Model 2 cameras. + */ +static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->model) { + case CIT_MODEL0: + case CIT_MODEL2: + case CIT_MODEL3: + case CIT_MODEL4: + case CIT_IBM_NETCAM_PRO: + break; + case CIT_MODEL1: { + int i; + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x0027, val); + break; + } + } +} + +static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->model) { + case CIT_MODEL0: + if (val) + cit_write_reg(gspca_dev, 0x0020, 0x0115); + else + cit_write_reg(gspca_dev, 0x0040, 0x0115); + break; + case CIT_MODEL1: + case CIT_MODEL2: + case CIT_MODEL3: + case CIT_MODEL4: + case CIT_IBM_NETCAM_PRO: + break; + } +} + +static int cit_restart_stream(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->model) { + case CIT_MODEL0: + case CIT_MODEL1: + cit_write_reg(gspca_dev, 0x0001, 0x0114); + /* Fall through */ + case CIT_MODEL2: + case CIT_MODEL4: + cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); + break; + case CIT_MODEL3: + case CIT_IBM_NETCAM_PRO: + cit_write_reg(gspca_dev, 0x0001, 0x0114); + cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); + /* Clear button events from while we were not streaming */ + cit_write_reg(gspca_dev, 0x0001, 0x0113); + break; + } + + sd->sof_read = 0; + + return 0; +} + +static int cit_get_packet_size(struct gspca_dev *gspca_dev) +{ + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + alt = usb_altnum_to_altsetting(intf, gspca_dev->alt); + if (!alt) { + pr_err("Couldn't get altsetting\n"); + return -EIO; + } + + return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); +} + +/* Calculate the clockdiv giving us max fps given the available bandwidth */ +static int cit_get_clock_div(struct gspca_dev *gspca_dev) +{ + int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */ + int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 }; + int packet_size; + + packet_size = cit_get_packet_size(gspca_dev); + if (packet_size < 0) + return packet_size; + + while (clock_div > 3 && + 1000 * packet_size > + gspca_dev->width * gspca_dev->height * + fps[clock_div - 1] * 3 / 2) + clock_div--; + + PDEBUG(D_PROBE, + "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)", + packet_size, gspca_dev->width, gspca_dev->height, clock_div, + fps[clock_div]); + + return clock_div; +} + +static int cit_start_model0(struct gspca_dev *gspca_dev) +{ + const unsigned short compression = 0; /* 0=none, 7=best frame rate */ + int clock_div; + + clock_div = cit_get_clock_div(gspca_dev); + if (clock_div < 0) + return clock_div; + + cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */ + cit_write_reg(gspca_dev, 0x0003, 0x0438); + cit_write_reg(gspca_dev, 0x001e, 0x042b); + cit_write_reg(gspca_dev, 0x0041, 0x042c); + cit_write_reg(gspca_dev, 0x0008, 0x0436); + cit_write_reg(gspca_dev, 0x0024, 0x0403); + cit_write_reg(gspca_dev, 0x002c, 0x0404); + cit_write_reg(gspca_dev, 0x0002, 0x0426); + cit_write_reg(gspca_dev, 0x0014, 0x0427); + + switch (gspca_dev->width) { + case 160: /* 160x120 */ + cit_write_reg(gspca_dev, 0x0004, 0x010b); + cit_write_reg(gspca_dev, 0x0001, 0x010a); + cit_write_reg(gspca_dev, 0x0010, 0x0102); + cit_write_reg(gspca_dev, 0x00a0, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0104); + cit_write_reg(gspca_dev, 0x0078, 0x0105); + break; + + case 176: /* 176x144 */ + cit_write_reg(gspca_dev, 0x0006, 0x010b); + cit_write_reg(gspca_dev, 0x0000, 0x010a); + cit_write_reg(gspca_dev, 0x0005, 0x0102); + cit_write_reg(gspca_dev, 0x00b0, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0104); + cit_write_reg(gspca_dev, 0x0090, 0x0105); + break; + + case 320: /* 320x240 */ + cit_write_reg(gspca_dev, 0x0008, 0x010b); + cit_write_reg(gspca_dev, 0x0004, 0x010a); + cit_write_reg(gspca_dev, 0x0005, 0x0102); + cit_write_reg(gspca_dev, 0x00a0, 0x0103); + cit_write_reg(gspca_dev, 0x0010, 0x0104); + cit_write_reg(gspca_dev, 0x0078, 0x0105); + break; + } + + cit_write_reg(gspca_dev, compression, 0x0109); + cit_write_reg(gspca_dev, clock_div, 0x0111); + + return 0; +} + +static int cit_start_model1(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, clock_div; + + clock_div = cit_get_clock_div(gspca_dev); + if (clock_div < 0) + return clock_div; + + cit_read_reg(gspca_dev, 0x0128, 1); + cit_read_reg(gspca_dev, 0x0100, 0); + cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */ + cit_read_reg(gspca_dev, 0x0100, 0); + cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */ + cit_read_reg(gspca_dev, 0x0100, 0); + cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */ + cit_write_reg(gspca_dev, 0x01, 0x0108); + + cit_write_reg(gspca_dev, 0x03, 0x0112); + cit_read_reg(gspca_dev, 0x0115, 0); + cit_write_reg(gspca_dev, 0x06, 0x0115); + cit_read_reg(gspca_dev, 0x0116, 0); + cit_write_reg(gspca_dev, 0x44, 0x0116); + cit_read_reg(gspca_dev, 0x0116, 0); + cit_write_reg(gspca_dev, 0x40, 0x0116); + cit_read_reg(gspca_dev, 0x0115, 0); + cit_write_reg(gspca_dev, 0x0e, 0x0115); + cit_write_reg(gspca_dev, 0x19, 0x012c); + + cit_Packet_Format1(gspca_dev, 0x00, 0x1e); + cit_Packet_Format1(gspca_dev, 0x39, 0x0d); + cit_Packet_Format1(gspca_dev, 0x39, 0x09); + cit_Packet_Format1(gspca_dev, 0x3b, 0x00); + cit_Packet_Format1(gspca_dev, 0x28, 0x22); + cit_Packet_Format1(gspca_dev, 0x27, 0x00); + cit_Packet_Format1(gspca_dev, 0x2b, 0x1f); + cit_Packet_Format1(gspca_dev, 0x39, 0x08); + + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x2c, 0x00); + + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x30, 0x14); + + cit_PacketFormat2(gspca_dev, 0x39, 0x02); + cit_PacketFormat2(gspca_dev, 0x01, 0xe1); + cit_PacketFormat2(gspca_dev, 0x02, 0xcd); + cit_PacketFormat2(gspca_dev, 0x03, 0xcd); + cit_PacketFormat2(gspca_dev, 0x04, 0xfa); + cit_PacketFormat2(gspca_dev, 0x3f, 0xff); + cit_PacketFormat2(gspca_dev, 0x39, 0x00); + + cit_PacketFormat2(gspca_dev, 0x39, 0x02); + cit_PacketFormat2(gspca_dev, 0x0a, 0x37); + cit_PacketFormat2(gspca_dev, 0x0b, 0xb8); + cit_PacketFormat2(gspca_dev, 0x0c, 0xf3); + cit_PacketFormat2(gspca_dev, 0x0d, 0xe3); + cit_PacketFormat2(gspca_dev, 0x0e, 0x0d); + cit_PacketFormat2(gspca_dev, 0x0f, 0xf2); + cit_PacketFormat2(gspca_dev, 0x10, 0xd5); + cit_PacketFormat2(gspca_dev, 0x11, 0xba); + cit_PacketFormat2(gspca_dev, 0x12, 0x53); + cit_PacketFormat2(gspca_dev, 0x3f, 0xff); + cit_PacketFormat2(gspca_dev, 0x39, 0x00); + + cit_PacketFormat2(gspca_dev, 0x39, 0x02); + cit_PacketFormat2(gspca_dev, 0x16, 0x00); + cit_PacketFormat2(gspca_dev, 0x17, 0x28); + cit_PacketFormat2(gspca_dev, 0x18, 0x7d); + cit_PacketFormat2(gspca_dev, 0x19, 0xbe); + cit_PacketFormat2(gspca_dev, 0x3f, 0xff); + cit_PacketFormat2(gspca_dev, 0x39, 0x00); + + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x00, 0x18); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x13, 0x18); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x14, 0x06); + + /* TESTME These are handled through controls + KEEP until someone can test leaving this out is ok */ + if (0) { + /* This is default brightness */ + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x31, 0x37); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x32, 0x46); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x33, 0x55); + } + + cit_Packet_Format1(gspca_dev, 0x2e, 0x04); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x2d, 0x04); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x29, 0x80); + cit_Packet_Format1(gspca_dev, 0x2c, 0x01); + cit_Packet_Format1(gspca_dev, 0x30, 0x17); + cit_Packet_Format1(gspca_dev, 0x39, 0x08); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x34, 0x00); + + cit_write_reg(gspca_dev, 0x00, 0x0101); + cit_write_reg(gspca_dev, 0x00, 0x010a); + + switch (gspca_dev->width) { + case 128: /* 128x96 */ + cit_write_reg(gspca_dev, 0x80, 0x0103); + cit_write_reg(gspca_dev, 0x60, 0x0105); + cit_write_reg(gspca_dev, 0x0c, 0x010b); + cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x0b, 0x011d); + cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x00, 0x0129); + break; + case 176: /* 176x144 */ + cit_write_reg(gspca_dev, 0xb0, 0x0103); + cit_write_reg(gspca_dev, 0x8f, 0x0105); + cit_write_reg(gspca_dev, 0x06, 0x010b); + cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x0d, 0x011d); + cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x03, 0x0129); + break; + case 352: /* 352x288 */ + cit_write_reg(gspca_dev, 0xb0, 0x0103); + cit_write_reg(gspca_dev, 0x90, 0x0105); + cit_write_reg(gspca_dev, 0x02, 0x010b); + cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x05, 0x011d); + cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x00, 0x0129); + break; + } + + cit_write_reg(gspca_dev, 0xff, 0x012b); + + /* TESTME These are handled through controls + KEEP until someone can test leaving this out is ok */ + if (0) { + /* This is another brightness - don't know why */ + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x31, 0xc3); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x32, 0xd2); + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x33, 0xe1); + + /* Default contrast */ + for (i = 0; i < cit_model1_ntries; i++) + cit_Packet_Format1(gspca_dev, 0x14, 0x0a); + + /* Default sharpness */ + for (i = 0; i < cit_model1_ntries2; i++) + cit_PacketFormat2(gspca_dev, 0x13, 0x1a); + + /* Default lighting conditions */ + cit_Packet_Format1(gspca_dev, 0x0027, + v4l2_ctrl_g_ctrl(sd->lighting)); + } + + /* Assorted init */ + switch (gspca_dev->width) { + case 128: /* 128x96 */ + cit_Packet_Format1(gspca_dev, 0x2b, 0x1e); + cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x36, 0x0102); + cit_write_reg(gspca_dev, 0x1a, 0x0104); + cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x2b, 0x011c); + cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */ + break; + case 176: /* 176x144 */ + cit_Packet_Format1(gspca_dev, 0x2b, 0x1e); + cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x04, 0x0102); + cit_write_reg(gspca_dev, 0x02, 0x0104); + cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x2b, 0x011c); + cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */ + break; + case 352: /* 352x288 */ + cit_Packet_Format1(gspca_dev, 0x2b, 0x1f); + cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x08, 0x0102); + cit_write_reg(gspca_dev, 0x01, 0x0104); + cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */ + cit_write_reg(gspca_dev, 0x2f, 0x011c); + cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */ + break; + } + + cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */ + cit_write_reg(gspca_dev, clock_div, 0x0111); + + return 0; +} + +static int cit_start_model2(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int clock_div = 0; + + cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */ + cit_read_reg(gspca_dev, 0x0116, 0); + cit_write_reg(gspca_dev, 0x0060, 0x0116); + cit_write_reg(gspca_dev, 0x0002, 0x0112); + cit_write_reg(gspca_dev, 0x00bc, 0x012c); + cit_write_reg(gspca_dev, 0x0008, 0x012b); + cit_write_reg(gspca_dev, 0x0000, 0x0108); + cit_write_reg(gspca_dev, 0x0001, 0x0133); + cit_write_reg(gspca_dev, 0x0001, 0x0102); + switch (gspca_dev->width) { + case 176: /* 176x144 */ + cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ + cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ + cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */ + cit_write_reg(gspca_dev, 0x00b9, 0x010a); /* Unique to this mode */ + cit_write_reg(gspca_dev, 0x0038, 0x0119); /* Unique to this mode */ + /* TESTME HDG: this does not seem right + (it is 2 for all other resolutions) */ + sd->sof_len = 10; + break; + case 320: /* 320x240 */ + cit_write_reg(gspca_dev, 0x0028, 0x0103); /* Unique to this mode */ + cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ + cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */ + cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */ + cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ + sd->sof_len = 2; + break; + /* case VIDEOSIZE_352x240: */ + cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ + cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ + cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */ + cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */ + cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ + sd->sof_len = 2; + break; + case 352: /* 352x288 */ + cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ + cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ + cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */ + cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */ + cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ + sd->sof_len = 2; + break; + } + + cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */ + + switch (gspca_dev->width) { + case 176: /* 176x144 */ + cit_write_reg(gspca_dev, 0x0050, 0x0111); + cit_write_reg(gspca_dev, 0x00d0, 0x0111); + break; + case 320: /* 320x240 */ + case 352: /* 352x288 */ + cit_write_reg(gspca_dev, 0x0040, 0x0111); + cit_write_reg(gspca_dev, 0x00c0, 0x0111); + break; + } + cit_write_reg(gspca_dev, 0x009b, 0x010f); + cit_write_reg(gspca_dev, 0x00bb, 0x010f); + + /* + * Hardware settings, may affect CMOS sensor; not user controls! + * ------------------------------------------------------------- + * 0x0004: no effect + * 0x0006: hardware effect + * 0x0008: no effect + * 0x000a: stops video stream, probably important h/w setting + * 0x000c: changes color in hardware manner (not user setting) + * 0x0012: changes number of colors (does not affect speed) + * 0x002a: no effect + * 0x002c: hardware setting (related to scan lines) + * 0x002e: stops video stream, probably important h/w setting + */ + cit_model2_Packet1(gspca_dev, 0x000a, 0x005c); + cit_model2_Packet1(gspca_dev, 0x0004, 0x0000); + cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb); + cit_model2_Packet1(gspca_dev, 0x0008, 0x0000); + cit_model2_Packet1(gspca_dev, 0x000c, 0x0009); + cit_model2_Packet1(gspca_dev, 0x0012, 0x000a); + cit_model2_Packet1(gspca_dev, 0x002a, 0x0000); + cit_model2_Packet1(gspca_dev, 0x002c, 0x0000); + cit_model2_Packet1(gspca_dev, 0x002e, 0x0008); + + /* + * Function 0x0030 pops up all over the place. Apparently + * it is a hardware control register, with every bit assigned to + * do something. + */ + cit_model2_Packet1(gspca_dev, 0x0030, 0x0000); + + /* + * Magic control of CMOS sensor. Only lower values like + * 0-3 work, and picture shifts left or right. Don't change. + */ + switch (gspca_dev->width) { + case 176: /* 176x144 */ + cit_model2_Packet1(gspca_dev, 0x0014, 0x0002); + cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */ + cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */ + clock_div = 6; + break; + case 320: /* 320x240 */ + cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); + cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */ + cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */ + clock_div = 8; + break; + /* case VIDEOSIZE_352x240: */ + /* This mode doesn't work as Windows programs it; changed to work */ + cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */ + cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */ + cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ + clock_div = 10; + break; + case 352: /* 352x288 */ + cit_model2_Packet1(gspca_dev, 0x0014, 0x0003); + cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */ + cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */ + clock_div = 16; + break; + } + + /* TESTME These are handled through controls + KEEP until someone can test leaving this out is ok */ + if (0) + cit_model2_Packet1(gspca_dev, 0x001a, 0x005a); + + /* + * We have our own frame rate setting varying from 0 (slowest) to 6 + * (fastest). The camera model 2 allows frame rate in range [0..0x1F] + # where 0 is also the slowest setting. However for all practical + # reasons high settings make no sense because USB is not fast enough + # to support high FPS. Be aware that the picture datastream will be + # severely disrupted if you ask for frame rate faster than allowed + # for the video size - see below: + * + * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz): + * ----------------------------------------------------------------- + * 176x144: [6..31] + * 320x240: [8..31] + * 352x240: [10..31] + * 352x288: [16..31] I have to raise lower threshold for stability... + * + * As usual, slower FPS provides better sensitivity. + */ + cit_model2_Packet1(gspca_dev, 0x001c, clock_div); + + /* + * This setting does not visibly affect pictures; left it here + * because it was present in Windows USB data stream. This function + * does not allow arbitrary values and apparently is a bit mask, to + * be activated only at appropriate time. Don't change it randomly! + */ + switch (gspca_dev->width) { + case 176: /* 176x144 */ + cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2); + break; + case 320: /* 320x240 */ + cit_model2_Packet1(gspca_dev, 0x0026, 0x0044); + break; + /* case VIDEOSIZE_352x240: */ + cit_model2_Packet1(gspca_dev, 0x0026, 0x0046); + break; + case 352: /* 352x288 */ + cit_model2_Packet1(gspca_dev, 0x0026, 0x0048); + break; + } + + cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting)); + /* model2 cannot change the backlight compensation while streaming */ + v4l2_ctrl_grab(sd->lighting, true); + + /* color balance rg2 */ + cit_model2_Packet1(gspca_dev, 0x001e, 0x002f); + /* saturation */ + cit_model2_Packet1(gspca_dev, 0x0020, 0x0034); + /* color balance yb */ + cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0); + + /* Hardware control command */ + cit_model2_Packet1(gspca_dev, 0x0030, 0x0004); + + return 0; +} + +static int cit_start_model3(struct gspca_dev *gspca_dev) +{ + const unsigned short compression = 0; /* 0=none, 7=best frame rate */ + int i, clock_div = 0; + + /* HDG not in ibmcam driver, added to see if it helps with + auto-detecting between model3 and ibm netcamera pro */ + cit_read_reg(gspca_dev, 0x128, 1); + + cit_write_reg(gspca_dev, 0x0000, 0x0100); + cit_read_reg(gspca_dev, 0x0116, 0); + cit_write_reg(gspca_dev, 0x0060, 0x0116); + cit_write_reg(gspca_dev, 0x0002, 0x0112); + cit_write_reg(gspca_dev, 0x0000, 0x0123); + cit_write_reg(gspca_dev, 0x0001, 0x0117); + cit_write_reg(gspca_dev, 0x0040, 0x0108); + cit_write_reg(gspca_dev, 0x0019, 0x012c); + cit_write_reg(gspca_dev, 0x0060, 0x0116); + cit_write_reg(gspca_dev, 0x0002, 0x0115); + cit_write_reg(gspca_dev, 0x0003, 0x0115); + cit_read_reg(gspca_dev, 0x0115, 0); + cit_write_reg(gspca_dev, 0x000b, 0x0115); + + /* TESTME HDG not in ibmcam driver, added to see if it helps with + auto-detecting between model3 and ibm netcamera pro */ + if (0) { + cit_write_reg(gspca_dev, 0x0078, 0x012d); + cit_write_reg(gspca_dev, 0x0001, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0079, 0x012d); + cit_write_reg(gspca_dev, 0x00ff, 0x0130); + cit_write_reg(gspca_dev, 0xcd41, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_read_reg(gspca_dev, 0x0126, 1); + } + + cit_model3_Packet1(gspca_dev, 0x000a, 0x0040); + cit_model3_Packet1(gspca_dev, 0x000b, 0x00f6); + cit_model3_Packet1(gspca_dev, 0x000c, 0x0002); + cit_model3_Packet1(gspca_dev, 0x000d, 0x0020); + cit_model3_Packet1(gspca_dev, 0x000e, 0x0033); + cit_model3_Packet1(gspca_dev, 0x000f, 0x0007); + cit_model3_Packet1(gspca_dev, 0x0010, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0011, 0x0070); + cit_model3_Packet1(gspca_dev, 0x0012, 0x0030); + cit_model3_Packet1(gspca_dev, 0x0013, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0014, 0x0001); + cit_model3_Packet1(gspca_dev, 0x0015, 0x0001); + cit_model3_Packet1(gspca_dev, 0x0016, 0x0001); + cit_model3_Packet1(gspca_dev, 0x0017, 0x0001); + cit_model3_Packet1(gspca_dev, 0x0018, 0x0000); + cit_model3_Packet1(gspca_dev, 0x001e, 0x00c3); + cit_model3_Packet1(gspca_dev, 0x0020, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0028, 0x0010); + cit_model3_Packet1(gspca_dev, 0x0029, 0x0054); + cit_model3_Packet1(gspca_dev, 0x002a, 0x0013); + cit_model3_Packet1(gspca_dev, 0x002b, 0x0007); + cit_model3_Packet1(gspca_dev, 0x002d, 0x0028); + cit_model3_Packet1(gspca_dev, 0x002e, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0031, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0032, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0033, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0034, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0035, 0x0038); + cit_model3_Packet1(gspca_dev, 0x003a, 0x0001); + cit_model3_Packet1(gspca_dev, 0x003c, 0x001e); + cit_model3_Packet1(gspca_dev, 0x003f, 0x000a); + cit_model3_Packet1(gspca_dev, 0x0041, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0046, 0x003f); + cit_model3_Packet1(gspca_dev, 0x0047, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0050, 0x0005); + cit_model3_Packet1(gspca_dev, 0x0052, 0x001a); + cit_model3_Packet1(gspca_dev, 0x0053, 0x0003); + cit_model3_Packet1(gspca_dev, 0x005a, 0x006b); + cit_model3_Packet1(gspca_dev, 0x005d, 0x001e); + cit_model3_Packet1(gspca_dev, 0x005e, 0x0030); + cit_model3_Packet1(gspca_dev, 0x005f, 0x0041); + cit_model3_Packet1(gspca_dev, 0x0064, 0x0008); + cit_model3_Packet1(gspca_dev, 0x0065, 0x0015); + cit_model3_Packet1(gspca_dev, 0x0068, 0x000f); + cit_model3_Packet1(gspca_dev, 0x0079, 0x0000); + cit_model3_Packet1(gspca_dev, 0x007a, 0x0000); + cit_model3_Packet1(gspca_dev, 0x007c, 0x003f); + cit_model3_Packet1(gspca_dev, 0x0082, 0x000f); + cit_model3_Packet1(gspca_dev, 0x0085, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0099, 0x0000); + cit_model3_Packet1(gspca_dev, 0x009b, 0x0023); + cit_model3_Packet1(gspca_dev, 0x009c, 0x0022); + cit_model3_Packet1(gspca_dev, 0x009d, 0x0096); + cit_model3_Packet1(gspca_dev, 0x009e, 0x0096); + cit_model3_Packet1(gspca_dev, 0x009f, 0x000a); + + switch (gspca_dev->width) { + case 160: + cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ + cit_write_reg(gspca_dev, 0x0024, 0x010b); /* Differs everywhere */ + cit_write_reg(gspca_dev, 0x00a9, 0x0119); + cit_write_reg(gspca_dev, 0x0016, 0x011b); + cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */ + cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ + cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ + cit_write_reg(gspca_dev, 0x0018, 0x0102); + cit_write_reg(gspca_dev, 0x0004, 0x0104); + cit_write_reg(gspca_dev, 0x0004, 0x011a); + cit_write_reg(gspca_dev, 0x0028, 0x011c); + cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ + cit_write_reg(gspca_dev, 0x0000, 0x0118); + cit_write_reg(gspca_dev, 0x0000, 0x0132); + cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */ + cit_write_reg(gspca_dev, compression, 0x0109); + clock_div = 3; + break; + case 320: + cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ + cit_write_reg(gspca_dev, 0x0028, 0x010b); /* Differs everywhere */ + cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same */ + cit_write_reg(gspca_dev, 0x0000, 0x011e); + cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ + cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ + /* 4 commands from 160x120 skipped */ + cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ + cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */ + cit_write_reg(gspca_dev, compression, 0x0109); + cit_write_reg(gspca_dev, 0x00d9, 0x0119); + cit_write_reg(gspca_dev, 0x0006, 0x011b); + cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + cit_write_reg(gspca_dev, 0x0010, 0x0104); + cit_write_reg(gspca_dev, 0x0004, 0x011a); + cit_write_reg(gspca_dev, 0x003f, 0x011c); + cit_write_reg(gspca_dev, 0x001c, 0x0118); + cit_write_reg(gspca_dev, 0x0000, 0x0132); + clock_div = 5; + break; + case 640: + cit_write_reg(gspca_dev, 0x00f0, 0x0105); + cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ + cit_write_reg(gspca_dev, 0x0038, 0x010b); /* Differs everywhere */ + cit_write_reg(gspca_dev, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */ + cit_write_reg(gspca_dev, 0x0006, 0x011b); /* Same on 320x240, 640x480 */ + cit_write_reg(gspca_dev, 0x0004, 0x011d); /* NC */ + cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */ + cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ + cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ + cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + cit_write_reg(gspca_dev, 0x0016, 0x0104); /* NC */ + cit_write_reg(gspca_dev, 0x0004, 0x011a); /* Same on 320x240, 640x480 */ + cit_write_reg(gspca_dev, 0x003f, 0x011c); /* Same on 320x240, 640x480 */ + cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ + cit_write_reg(gspca_dev, 0x001c, 0x0118); /* Same on 320x240, 640x480 */ + cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */ + cit_write_reg(gspca_dev, compression, 0x0109); + cit_write_reg(gspca_dev, 0x0040, 0x0101); + cit_write_reg(gspca_dev, 0x0040, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0132); /* Same on 320x240, 640x480 */ + clock_div = 7; + break; + } + + cit_model3_Packet1(gspca_dev, 0x007e, 0x000e); /* Hue */ + cit_model3_Packet1(gspca_dev, 0x0036, 0x0011); /* Brightness */ + cit_model3_Packet1(gspca_dev, 0x0060, 0x0002); /* Sharpness */ + cit_model3_Packet1(gspca_dev, 0x0061, 0x0004); /* Sharpness */ + cit_model3_Packet1(gspca_dev, 0x0062, 0x0005); /* Sharpness */ + cit_model3_Packet1(gspca_dev, 0x0063, 0x0014); /* Sharpness */ + cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */ + cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */ + cit_model3_Packet1(gspca_dev, 0x0067, 0x0001); /* Contrast */ + cit_model3_Packet1(gspca_dev, 0x005b, 0x000c); /* Contrast */ + cit_model3_Packet1(gspca_dev, 0x005c, 0x0016); /* Contrast */ + cit_model3_Packet1(gspca_dev, 0x0098, 0x000b); + cit_model3_Packet1(gspca_dev, 0x002c, 0x0003); /* Was 1, broke 640x480 */ + cit_model3_Packet1(gspca_dev, 0x002f, 0x002a); + cit_model3_Packet1(gspca_dev, 0x0030, 0x0029); + cit_model3_Packet1(gspca_dev, 0x0037, 0x0002); + cit_model3_Packet1(gspca_dev, 0x0038, 0x0059); + cit_model3_Packet1(gspca_dev, 0x003d, 0x002e); + cit_model3_Packet1(gspca_dev, 0x003e, 0x0028); + cit_model3_Packet1(gspca_dev, 0x0078, 0x0005); + cit_model3_Packet1(gspca_dev, 0x007b, 0x0011); + cit_model3_Packet1(gspca_dev, 0x007d, 0x004b); + cit_model3_Packet1(gspca_dev, 0x007f, 0x0022); + cit_model3_Packet1(gspca_dev, 0x0080, 0x000c); + cit_model3_Packet1(gspca_dev, 0x0081, 0x000b); + cit_model3_Packet1(gspca_dev, 0x0083, 0x00fd); + cit_model3_Packet1(gspca_dev, 0x0086, 0x000b); + cit_model3_Packet1(gspca_dev, 0x0087, 0x000b); + cit_model3_Packet1(gspca_dev, 0x007e, 0x000e); + cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */ + cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */ + cit_model3_Packet1(gspca_dev, 0x0098, 0x000b); + + /* FIXME we should probably use cit_get_clock_div() here (in + combination with isoc negotiation using the programmable isoc size) + like with the IBM netcam pro). */ + cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */ + + switch (gspca_dev->width) { + case 160: + cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */ + cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */ + cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */ + cit_model3_Packet1(gspca_dev, 0x0040, 0x000a); + cit_model3_Packet1(gspca_dev, 0x0051, 0x000a); + break; + case 320: + cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */ + cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */ + cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */ + cit_model3_Packet1(gspca_dev, 0x0040, 0x0008); + cit_model3_Packet1(gspca_dev, 0x0051, 0x000b); + break; + case 640: + cit_model3_Packet1(gspca_dev, 0x001f, 0x0002); /* !Same */ + cit_model3_Packet1(gspca_dev, 0x0039, 0x003e); /* !Same */ + cit_model3_Packet1(gspca_dev, 0x0040, 0x0008); + cit_model3_Packet1(gspca_dev, 0x0051, 0x000a); + break; + } + +/* if (sd->input_index) { */ + if (rca_input) { + for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) { + if (rca_initdata[i][0]) + cit_read_reg(gspca_dev, rca_initdata[i][2], 0); + else + cit_write_reg(gspca_dev, rca_initdata[i][1], + rca_initdata[i][2]); + } + } + + return 0; +} + +static int cit_start_model4(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + cit_write_reg(gspca_dev, 0x0000, 0x0100); + cit_write_reg(gspca_dev, 0x00c0, 0x0111); + cit_write_reg(gspca_dev, 0x00bc, 0x012c); + cit_write_reg(gspca_dev, 0x0080, 0x012b); + cit_write_reg(gspca_dev, 0x0000, 0x0108); + cit_write_reg(gspca_dev, 0x0001, 0x0133); + cit_write_reg(gspca_dev, 0x009b, 0x010f); + cit_write_reg(gspca_dev, 0x00bb, 0x010f); + cit_model4_Packet1(gspca_dev, 0x0038, 0x0000); + cit_model4_Packet1(gspca_dev, 0x000a, 0x005c); + + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0004, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0x00fb, 0x012e); + cit_write_reg(gspca_dev, 0x0000, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012f); + cit_write_reg(gspca_dev, 0xd055, 0x0124); + cit_write_reg(gspca_dev, 0x000c, 0x0127); + cit_write_reg(gspca_dev, 0x0009, 0x012e); + cit_write_reg(gspca_dev, 0xaa28, 0x0124); + + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0012, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0008, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x002a, 0x012d); + cit_write_reg(gspca_dev, 0x0000, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_model4_Packet1(gspca_dev, 0x0034, 0x0000); + + switch (gspca_dev->width) { + case 128: /* 128x96 */ + cit_write_reg(gspca_dev, 0x0070, 0x0119); + cit_write_reg(gspca_dev, 0x00d0, 0x0111); + cit_write_reg(gspca_dev, 0x0039, 0x010a); + cit_write_reg(gspca_dev, 0x0001, 0x0102); + cit_write_reg(gspca_dev, 0x0028, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0104); + cit_write_reg(gspca_dev, 0x001e, 0x0105); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0016, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x000a, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0014, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012e); + cit_write_reg(gspca_dev, 0x001a, 0x0130); + cit_write_reg(gspca_dev, 0x8a0a, 0x0124); + cit_write_reg(gspca_dev, 0x005a, 0x012d); + cit_write_reg(gspca_dev, 0x9545, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x0127); + cit_write_reg(gspca_dev, 0x0018, 0x012e); + cit_write_reg(gspca_dev, 0x0043, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012f); + cit_write_reg(gspca_dev, 0xd055, 0x0124); + cit_write_reg(gspca_dev, 0x001c, 0x0127); + cit_write_reg(gspca_dev, 0x00eb, 0x012e); + cit_write_reg(gspca_dev, 0xaa28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0032, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0036, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x001e, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0017, 0x0127); + cit_write_reg(gspca_dev, 0x0013, 0x012e); + cit_write_reg(gspca_dev, 0x0031, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x0017, 0x012d); + cit_write_reg(gspca_dev, 0x0078, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0xfea8, 0x0124); + sd->sof_len = 2; + break; + case 160: /* 160x120 */ + cit_write_reg(gspca_dev, 0x0038, 0x0119); + cit_write_reg(gspca_dev, 0x00d0, 0x0111); + cit_write_reg(gspca_dev, 0x00b9, 0x010a); + cit_write_reg(gspca_dev, 0x0001, 0x0102); + cit_write_reg(gspca_dev, 0x0028, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0104); + cit_write_reg(gspca_dev, 0x001e, 0x0105); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0016, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x000b, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0014, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012e); + cit_write_reg(gspca_dev, 0x001a, 0x0130); + cit_write_reg(gspca_dev, 0x8a0a, 0x0124); + cit_write_reg(gspca_dev, 0x005a, 0x012d); + cit_write_reg(gspca_dev, 0x9545, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x0127); + cit_write_reg(gspca_dev, 0x0018, 0x012e); + cit_write_reg(gspca_dev, 0x0043, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012f); + cit_write_reg(gspca_dev, 0xd055, 0x0124); + cit_write_reg(gspca_dev, 0x001c, 0x0127); + cit_write_reg(gspca_dev, 0x00c7, 0x012e); + cit_write_reg(gspca_dev, 0xaa28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0032, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0025, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0036, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x001e, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0048, 0x0127); + cit_write_reg(gspca_dev, 0x0035, 0x012e); + cit_write_reg(gspca_dev, 0x00d0, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x0048, 0x012d); + cit_write_reg(gspca_dev, 0x0090, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x0001, 0x0127); + cit_write_reg(gspca_dev, 0xfea8, 0x0124); + sd->sof_len = 2; + break; + case 176: /* 176x144 */ + cit_write_reg(gspca_dev, 0x0038, 0x0119); + cit_write_reg(gspca_dev, 0x00d0, 0x0111); + cit_write_reg(gspca_dev, 0x00b9, 0x010a); + cit_write_reg(gspca_dev, 0x0001, 0x0102); + cit_write_reg(gspca_dev, 0x002c, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0104); + cit_write_reg(gspca_dev, 0x0024, 0x0105); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0016, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0007, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0014, 0x012d); + cit_write_reg(gspca_dev, 0x0001, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012e); + cit_write_reg(gspca_dev, 0x001a, 0x0130); + cit_write_reg(gspca_dev, 0x8a0a, 0x0124); + cit_write_reg(gspca_dev, 0x005e, 0x012d); + cit_write_reg(gspca_dev, 0x9545, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x0127); + cit_write_reg(gspca_dev, 0x0018, 0x012e); + cit_write_reg(gspca_dev, 0x0049, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012f); + cit_write_reg(gspca_dev, 0xd055, 0x0124); + cit_write_reg(gspca_dev, 0x001c, 0x0127); + cit_write_reg(gspca_dev, 0x00c7, 0x012e); + cit_write_reg(gspca_dev, 0xaa28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0032, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0028, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0036, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x001e, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0010, 0x0127); + cit_write_reg(gspca_dev, 0x0013, 0x012e); + cit_write_reg(gspca_dev, 0x002a, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x0010, 0x012d); + cit_write_reg(gspca_dev, 0x006d, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x0001, 0x0127); + cit_write_reg(gspca_dev, 0xfea8, 0x0124); + /* TESTME HDG: this does not seem right + (it is 2 for all other resolutions) */ + sd->sof_len = 10; + break; + case 320: /* 320x240 */ + cit_write_reg(gspca_dev, 0x0070, 0x0119); + cit_write_reg(gspca_dev, 0x00d0, 0x0111); + cit_write_reg(gspca_dev, 0x0039, 0x010a); + cit_write_reg(gspca_dev, 0x0001, 0x0102); + cit_write_reg(gspca_dev, 0x0028, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0104); + cit_write_reg(gspca_dev, 0x001e, 0x0105); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0016, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x000a, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0014, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012e); + cit_write_reg(gspca_dev, 0x001a, 0x0130); + cit_write_reg(gspca_dev, 0x8a0a, 0x0124); + cit_write_reg(gspca_dev, 0x005a, 0x012d); + cit_write_reg(gspca_dev, 0x9545, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x0127); + cit_write_reg(gspca_dev, 0x0018, 0x012e); + cit_write_reg(gspca_dev, 0x0043, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012f); + cit_write_reg(gspca_dev, 0xd055, 0x0124); + cit_write_reg(gspca_dev, 0x001c, 0x0127); + cit_write_reg(gspca_dev, 0x00eb, 0x012e); + cit_write_reg(gspca_dev, 0xaa28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0032, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0036, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x001e, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0017, 0x0127); + cit_write_reg(gspca_dev, 0x0013, 0x012e); + cit_write_reg(gspca_dev, 0x0031, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x0017, 0x012d); + cit_write_reg(gspca_dev, 0x0078, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0xfea8, 0x0124); + sd->sof_len = 2; + break; + case 352: /* 352x288 */ + cit_write_reg(gspca_dev, 0x0070, 0x0119); + cit_write_reg(gspca_dev, 0x00c0, 0x0111); + cit_write_reg(gspca_dev, 0x0039, 0x010a); + cit_write_reg(gspca_dev, 0x0001, 0x0102); + cit_write_reg(gspca_dev, 0x002c, 0x0103); + cit_write_reg(gspca_dev, 0x0000, 0x0104); + cit_write_reg(gspca_dev, 0x0024, 0x0105); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0016, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0006, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0014, 0x012d); + cit_write_reg(gspca_dev, 0x0002, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012e); + cit_write_reg(gspca_dev, 0x001a, 0x0130); + cit_write_reg(gspca_dev, 0x8a0a, 0x0124); + cit_write_reg(gspca_dev, 0x005e, 0x012d); + cit_write_reg(gspca_dev, 0x9545, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x0127); + cit_write_reg(gspca_dev, 0x0018, 0x012e); + cit_write_reg(gspca_dev, 0x0049, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012f); + cit_write_reg(gspca_dev, 0xd055, 0x0124); + cit_write_reg(gspca_dev, 0x001c, 0x0127); + cit_write_reg(gspca_dev, 0x00cf, 0x012e); + cit_write_reg(gspca_dev, 0xaa28, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x0032, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0x00aa, 0x0130); + cit_write_reg(gspca_dev, 0x82a8, 0x0124); + cit_write_reg(gspca_dev, 0x0036, 0x012d); + cit_write_reg(gspca_dev, 0x0008, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0xfffa, 0x0124); + cit_write_reg(gspca_dev, 0x00aa, 0x012d); + cit_write_reg(gspca_dev, 0x001e, 0x012f); + cit_write_reg(gspca_dev, 0xd141, 0x0124); + cit_write_reg(gspca_dev, 0x0010, 0x0127); + cit_write_reg(gspca_dev, 0x0013, 0x012e); + cit_write_reg(gspca_dev, 0x0025, 0x0130); + cit_write_reg(gspca_dev, 0x8a28, 0x0124); + cit_write_reg(gspca_dev, 0x0010, 0x012d); + cit_write_reg(gspca_dev, 0x0048, 0x012f); + cit_write_reg(gspca_dev, 0xd145, 0x0124); + cit_write_reg(gspca_dev, 0x0000, 0x0127); + cit_write_reg(gspca_dev, 0xfea8, 0x0124); + sd->sof_len = 2; + break; + } + + cit_model4_Packet1(gspca_dev, 0x0038, 0x0004); + + return 0; +} + +static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev) +{ + const unsigned short compression = 0; /* 0=none, 7=best frame rate */ + int i, clock_div; + + clock_div = cit_get_clock_div(gspca_dev); + if (clock_div < 0) + return clock_div; + + cit_write_reg(gspca_dev, 0x0003, 0x0133); + cit_write_reg(gspca_dev, 0x0000, 0x0117); + cit_write_reg(gspca_dev, 0x0008, 0x0123); + cit_write_reg(gspca_dev, 0x0000, 0x0100); + cit_write_reg(gspca_dev, 0x0060, 0x0116); + /* cit_write_reg(gspca_dev, 0x0002, 0x0112); see sd_stop0 */ + cit_write_reg(gspca_dev, 0x0000, 0x0133); + cit_write_reg(gspca_dev, 0x0000, 0x0123); + cit_write_reg(gspca_dev, 0x0001, 0x0117); + cit_write_reg(gspca_dev, 0x0040, 0x0108); + cit_write_reg(gspca_dev, 0x0019, 0x012c); + cit_write_reg(gspca_dev, 0x0060, 0x0116); + /* cit_write_reg(gspca_dev, 0x000b, 0x0115); see sd_stop0 */ + + cit_model3_Packet1(gspca_dev, 0x0049, 0x0000); + + cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x003a, 0x0102); /* Hstart */ + cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ + cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */ + cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ + cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ + cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ + + switch (gspca_dev->width) { + case 160: /* 160x120 */ + cit_write_reg(gspca_dev, 0x0024, 0x010b); + cit_write_reg(gspca_dev, 0x0089, 0x0119); + cit_write_reg(gspca_dev, 0x000a, 0x011b); + cit_write_reg(gspca_dev, 0x0003, 0x011e); + cit_write_reg(gspca_dev, 0x0007, 0x0104); + cit_write_reg(gspca_dev, 0x0009, 0x011a); + cit_write_reg(gspca_dev, 0x008b, 0x011c); + cit_write_reg(gspca_dev, 0x0008, 0x0118); + cit_write_reg(gspca_dev, 0x0000, 0x0132); + break; + case 320: /* 320x240 */ + cit_write_reg(gspca_dev, 0x0028, 0x010b); + cit_write_reg(gspca_dev, 0x00d9, 0x0119); + cit_write_reg(gspca_dev, 0x0006, 0x011b); + cit_write_reg(gspca_dev, 0x0000, 0x011e); + cit_write_reg(gspca_dev, 0x000e, 0x0104); + cit_write_reg(gspca_dev, 0x0004, 0x011a); + cit_write_reg(gspca_dev, 0x003f, 0x011c); + cit_write_reg(gspca_dev, 0x000c, 0x0118); + cit_write_reg(gspca_dev, 0x0000, 0x0132); + break; + } + + cit_model3_Packet1(gspca_dev, 0x0019, 0x0031); + cit_model3_Packet1(gspca_dev, 0x001a, 0x0003); + cit_model3_Packet1(gspca_dev, 0x001b, 0x0038); + cit_model3_Packet1(gspca_dev, 0x001c, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0024, 0x0001); + cit_model3_Packet1(gspca_dev, 0x0027, 0x0001); + cit_model3_Packet1(gspca_dev, 0x002a, 0x0004); + cit_model3_Packet1(gspca_dev, 0x0035, 0x000b); + cit_model3_Packet1(gspca_dev, 0x003f, 0x0001); + cit_model3_Packet1(gspca_dev, 0x0044, 0x0000); + cit_model3_Packet1(gspca_dev, 0x0054, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00c4, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00e7, 0x0001); + cit_model3_Packet1(gspca_dev, 0x00e9, 0x0001); + cit_model3_Packet1(gspca_dev, 0x00ee, 0x0000); + cit_model3_Packet1(gspca_dev, 0x00f3, 0x00c0); + + cit_write_reg(gspca_dev, compression, 0x0109); + cit_write_reg(gspca_dev, clock_div, 0x0111); + +/* if (sd->input_index) { */ + if (rca_input) { + for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) { + if (rca_initdata[i][0]) + cit_read_reg(gspca_dev, rca_initdata[i][2], 0); + else + cit_write_reg(gspca_dev, rca_initdata[i][1], + rca_initdata[i][2]); + } + } + + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int packet_size; + + packet_size = cit_get_packet_size(gspca_dev); + if (packet_size < 0) + return packet_size; + + switch (sd->model) { + case CIT_MODEL0: + cit_start_model0(gspca_dev); + break; + case CIT_MODEL1: + cit_start_model1(gspca_dev); + break; + case CIT_MODEL2: + cit_start_model2(gspca_dev); + break; + case CIT_MODEL3: + cit_start_model3(gspca_dev); + break; + case CIT_MODEL4: + cit_start_model4(gspca_dev); + break; + case CIT_IBM_NETCAM_PRO: + cit_start_ibm_netcam_pro(gspca_dev); + break; + } + + /* Program max isoc packet size */ + cit_write_reg(gspca_dev, packet_size >> 8, 0x0106); + cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107); + + cit_restart_stream(gspca_dev); + + return 0; +} + +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct usb_host_interface *alt; + int max_packet_size; + + switch (gspca_dev->width) { + case 160: + max_packet_size = 450; + break; + case 176: + max_packet_size = 600; + break; + default: + max_packet_size = 1022; + break; + } + + /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ + alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; + alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size); + + return 0; +} + +static int sd_isoc_nego(struct gspca_dev *gspca_dev) +{ + int ret, packet_size, min_packet_size; + struct usb_host_interface *alt; + + switch (gspca_dev->width) { + case 160: + min_packet_size = 200; + break; + case 176: + min_packet_size = 266; + break; + default: + min_packet_size = 400; + break; + } + + alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + if (packet_size <= min_packet_size) + return -EIO; + + packet_size -= 100; + if (packet_size < min_packet_size) + packet_size = min_packet_size; + alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size); + + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); + if (ret < 0) + pr_err("set alt 1 err %d\n", ret); + + return ret; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + cit_write_reg(gspca_dev, 0x0000, 0x010c); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* We cannot use gspca_dev->present here as that is not set when + sd_init gets called and we get called from sd_init */ + if (!gspca_dev->dev) + return; + + switch (sd->model) { + case CIT_MODEL0: + /* HDG windows does this, but it causes the cams autogain to + restart from a gain of 0, which does not look good when + changing resolutions. */ + /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */ + cit_write_reg(gspca_dev, 0x00c0, 0x0100); /* LED Off */ + break; + case CIT_MODEL1: + cit_send_FF_04_02(gspca_dev); + cit_read_reg(gspca_dev, 0x0100, 0); + cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */ + break; + case CIT_MODEL2: + v4l2_ctrl_grab(sd->lighting, false); + /* Fall through! */ + case CIT_MODEL4: + cit_model2_Packet1(gspca_dev, 0x0030, 0x0004); + + cit_write_reg(gspca_dev, 0x0080, 0x0100); /* LED Off */ + cit_write_reg(gspca_dev, 0x0020, 0x0111); + cit_write_reg(gspca_dev, 0x00a0, 0x0111); + + cit_model2_Packet1(gspca_dev, 0x0030, 0x0002); + + cit_write_reg(gspca_dev, 0x0020, 0x0111); + cit_write_reg(gspca_dev, 0x0000, 0x0112); + break; + case CIT_MODEL3: + cit_write_reg(gspca_dev, 0x0006, 0x012c); + cit_model3_Packet1(gspca_dev, 0x0046, 0x0000); + cit_read_reg(gspca_dev, 0x0116, 0); + cit_write_reg(gspca_dev, 0x0064, 0x0116); + cit_read_reg(gspca_dev, 0x0115, 0); + cit_write_reg(gspca_dev, 0x0003, 0x0115); + cit_write_reg(gspca_dev, 0x0008, 0x0123); + cit_write_reg(gspca_dev, 0x0000, 0x0117); + cit_write_reg(gspca_dev, 0x0000, 0x0112); + cit_write_reg(gspca_dev, 0x0080, 0x0100); + break; + case CIT_IBM_NETCAM_PRO: + cit_model3_Packet1(gspca_dev, 0x0049, 0x00ff); + cit_write_reg(gspca_dev, 0x0006, 0x012c); + cit_write_reg(gspca_dev, 0x0000, 0x0116); + /* HDG windows does this, but I cannot get the camera + to restart with this without redoing the entire init + sequence which makes switching modes really slow */ + /* cit_write_reg(gspca_dev, 0x0006, 0x0115); */ + cit_write_reg(gspca_dev, 0x0008, 0x0123); + cit_write_reg(gspca_dev, 0x0000, 0x0117); + cit_write_reg(gspca_dev, 0x0003, 0x0133); + cit_write_reg(gspca_dev, 0x0000, 0x0111); + /* HDG windows does this, but I get a green picture when + restarting the stream after this */ + /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */ + cit_write_reg(gspca_dev, 0x00c0, 0x0100); + break; + } + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + /* If the last button state is pressed, release it now! */ + if (sd->button_state) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + sd->button_state = 0; + } +#endif +} + +static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 byte3 = 0, byte4 = 0; + int i; + + switch (sd->model) { + case CIT_MODEL0: + case CIT_MODEL1: + case CIT_MODEL3: + case CIT_IBM_NETCAM_PRO: + switch (gspca_dev->width) { + case 160: /* 160x120 */ + byte3 = 0x02; + byte4 = 0x0a; + break; + case 176: /* 176x144 */ + byte3 = 0x02; + byte4 = 0x0e; + break; + case 320: /* 320x240 */ + byte3 = 0x02; + byte4 = 0x08; + break; + case 352: /* 352x288 */ + byte3 = 0x02; + byte4 = 0x00; + break; + case 640: + byte3 = 0x03; + byte4 = 0x08; + break; + } + + /* These have a different byte3 */ + if (sd->model <= CIT_MODEL1) + byte3 = 0x00; + + for (i = 0; i < len; i++) { + /* For this model the SOF always starts at offset 0 + so no need to search the entire frame */ + if (sd->model == CIT_MODEL0 && sd->sof_read != i) + break; + + switch (sd->sof_read) { + case 0: + if (data[i] == 0x00) + sd->sof_read++; + break; + case 1: + if (data[i] == 0xff) + sd->sof_read++; + else if (data[i] == 0x00) + sd->sof_read = 1; + else + sd->sof_read = 0; + break; + case 2: + if (data[i] == byte3) + sd->sof_read++; + else if (data[i] == 0x00) + sd->sof_read = 1; + else + sd->sof_read = 0; + break; + case 3: + if (data[i] == byte4) { + sd->sof_read = 0; + return data + i + (sd->sof_len - 3); + } + if (byte3 == 0x00 && data[i] == 0xff) + sd->sof_read = 2; + else if (data[i] == 0x00) + sd->sof_read = 1; + else + sd->sof_read = 0; + break; + } + } + break; + case CIT_MODEL2: + case CIT_MODEL4: + /* TESTME we need to find a longer sof signature to avoid + false positives */ + for (i = 0; i < len; i++) { + switch (sd->sof_read) { + case 0: + if (data[i] == 0x00) + sd->sof_read++; + break; + case 1: + sd->sof_read = 0; + if (data[i] == 0xff) { + if (i >= 4) + PDEBUG(D_FRAM, + "header found at offset: %d: %02x %02x 00 %02x %02x %02x\n", + i - 1, + data[i - 4], + data[i - 3], + data[i], + data[i + 1], + data[i + 2]); + else + PDEBUG(D_FRAM, + "header found at offset: %d: 00 %02x %02x %02x\n", + i - 1, + data[i], + data[i + 1], + data[i + 2]); + return data + i + (sd->sof_len - 1); + } + break; + } + } + break; + } + return NULL; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char *sof; + + sof = cit_find_sof(gspca_dev, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sd->sof_len) + n -= sd->sof_len; + else + n = 0; + gspca_frame_add(gspca_dev, LAST_PACKET, + data, n); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + len -= sof - data; + data = sof; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static void cit_check_button(struct gspca_dev *gspca_dev) +{ + int new_button_state; + struct sd *sd = (struct sd *)gspca_dev; + + switch (sd->model) { + case CIT_MODEL3: + case CIT_IBM_NETCAM_PRO: + break; + default: /* TEST ME unknown if this works on other models too */ + return; + } + + /* Read the button state */ + cit_read_reg(gspca_dev, 0x0113, 0); + new_button_state = !gspca_dev->usb_buf[0]; + + /* Tell the cam we've seen the button press, notice that this + is a nop (iow the cam keeps reporting pressed) until the + button is actually released. */ + if (new_button_state) + cit_write_reg(gspca_dev, 0x01, 0x0113); + + if (sd->button_state != new_button_state) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, + new_button_state); + input_sync(gspca_dev->input_dev); + sd->button_state = new_button_state; + } +} +#endif + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + if (sd->stop_on_control_change) + sd_stopN(gspca_dev); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cit_set_brightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + cit_set_contrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_HUE: + cit_set_hue(gspca_dev, ctrl->val); + break; + case V4L2_CID_HFLIP: + cit_set_hflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + cit_set_sharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_BACKLIGHT_COMPENSATION: + cit_set_lighting(gspca_dev, ctrl->val); + break; + } + if (sd->stop_on_control_change) + cit_restart_stream(gspca_dev); + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + bool has_brightness; + bool has_contrast; + bool has_hue; + bool has_sharpness; + bool has_lighting; + bool has_hflip; + + has_brightness = has_contrast = has_hue = + has_sharpness = has_hflip = has_lighting = false; + switch (sd->model) { + case CIT_MODEL0: + has_contrast = has_hflip = true; + break; + case CIT_MODEL1: + has_brightness = has_contrast = + has_sharpness = has_lighting = true; + break; + case CIT_MODEL2: + has_brightness = has_hue = has_lighting = true; + break; + case CIT_MODEL3: + has_brightness = has_contrast = has_sharpness = true; + break; + case CIT_MODEL4: + has_brightness = has_hue = true; + break; + case CIT_IBM_NETCAM_PRO: + has_brightness = has_hue = + has_sharpness = has_hflip = has_lighting = true; + break; + } + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 5); + if (has_brightness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); + if (has_contrast) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 20, 1, 10); + if (has_hue) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, 0, 127, 1, 63); + if (has_sharpness) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 6, 1, 3); + if (has_lighting) + sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1); + if (has_hflip) + v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .dq_callback = cit_check_button, + .other_input = 1, +#endif +}; + +static const struct sd_desc sd_desc_isoc_nego = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .isoc_init = sd_isoc_init, + .isoc_nego = sd_isoc_nego, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .dq_callback = cit_check_button, + .other_input = 1, +#endif +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + { USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 }, + { USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 }, + { USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 }, + { USB_DEVICE_VER(0x0545, 0x8080, 0x0301, 0x0301), .driver_info = CIT_MODEL3 }, + { USB_DEVICE_VER(0x0545, 0x8002, 0x030a, 0x030a), .driver_info = CIT_MODEL4 }, + { USB_DEVICE_VER(0x0545, 0x800c, 0x030a, 0x030a), .driver_info = CIT_MODEL2 }, + { USB_DEVICE_VER(0x0545, 0x800d, 0x030a, 0x030a), .driver_info = CIT_MODEL4 }, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + const struct sd_desc *desc = &sd_desc; + + switch (id->driver_info) { + case CIT_MODEL0: + case CIT_MODEL1: + if (intf->cur_altsetting->desc.bInterfaceNumber != 2) + return -ENODEV; + break; + case CIT_MODEL2: + case CIT_MODEL4: + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + break; + case CIT_MODEL3: + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + /* FIXME this likely applies to all model3 cams and probably + to other models too. */ + if (ibm_netcam_pro) + desc = &sd_desc_isoc_nego; + break; + } + + return gspca_dev_probe2(intf, id, desc, sizeof(struct sd), THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/zc3xx-reg.h b/drivers/media/usb/gspca/zc3xx-reg.h new file mode 100644 index 000000000000..a1bd94e8ce52 --- /dev/null +++ b/drivers/media/usb/gspca/zc3xx-reg.h @@ -0,0 +1,251 @@ +/* + * zc030x registers + * + * Copyright (c) 2008 Mauro Carvalho Chehab + * + * The register aliases used here came from this driver: + * http://zc0302.sourceforge.net/zc0302.php + * + * This code is placed under the terms of the GNU General Public License v2 + */ + +/* Define the register map */ +#define ZC3XX_R000_SYSTEMCONTROL 0x0000 +#define ZC3XX_R001_SYSTEMOPERATING 0x0001 + +/* Picture size */ +#define ZC3XX_R002_CLOCKSELECT 0x0002 +#define ZC3XX_R003_FRAMEWIDTHHIGH 0x0003 +#define ZC3XX_R004_FRAMEWIDTHLOW 0x0004 +#define ZC3XX_R005_FRAMEHEIGHTHIGH 0x0005 +#define ZC3XX_R006_FRAMEHEIGHTLOW 0x0006 + +/* JPEG control */ +#define ZC3XX_R008_CLOCKSETTING 0x0008 + +/* Test mode */ +#define ZC3XX_R00B_TESTMODECONTROL 0x000b + +/* Frame retreiving */ +#define ZC3XX_R00C_LASTACQTIME 0x000c +#define ZC3XX_R00D_MONITORRES 0x000d +#define ZC3XX_R00E_TIMESTAMPHIGH 0x000e +#define ZC3XX_R00F_TIMESTAMPLOW 0x000f +#define ZC3XX_R018_FRAMELOST 0x0018 +#define ZC3XX_R019_AUTOADJUSTFPS 0x0019 +#define ZC3XX_R01A_LASTFRAMESTATE 0x001a +#define ZC3XX_R025_DATACOUNTER 0x0025 + +/* Stream and sensor specific */ +#define ZC3XX_R010_CMOSSENSORSELECT 0x0010 +#define ZC3XX_R011_VIDEOSTATUS 0x0011 +#define ZC3XX_R012_VIDEOCONTROLFUNC 0x0012 + +/* Horizontal and vertical synchros */ +#define ZC3XX_R01D_HSYNC_0 0x001d +#define ZC3XX_R01E_HSYNC_1 0x001e +#define ZC3XX_R01F_HSYNC_2 0x001f +#define ZC3XX_R020_HSYNC_3 0x0020 + +/* Target picture size in byte */ +#define ZC3XX_R022_TARGETPICTSIZE_0 0x0022 +#define ZC3XX_R023_TARGETPICTSIZE_1 0x0023 +#define ZC3XX_R024_TARGETPICTSIZE_2 0x0024 + +/* Audio registers */ +#define ZC3XX_R030_AUDIOADC 0x0030 +#define ZC3XX_R031_AUDIOSTREAMSTATUS 0x0031 +#define ZC3XX_R032_AUDIOSTATUS 0x0032 + +/* Sensor interface */ +#define ZC3XX_R080_HBLANKHIGH 0x0080 +#define ZC3XX_R081_HBLANKLOW 0x0081 +#define ZC3XX_R082_RESETLEVELADDR 0x0082 +#define ZC3XX_R083_RGAINADDR 0x0083 +#define ZC3XX_R084_GGAINADDR 0x0084 +#define ZC3XX_R085_BGAINADDR 0x0085 +#define ZC3XX_R086_EXPTIMEHIGH 0x0086 +#define ZC3XX_R087_EXPTIMEMID 0x0087 +#define ZC3XX_R088_EXPTIMELOW 0x0088 +#define ZC3XX_R089_RESETBLACKHIGH 0x0089 +#define ZC3XX_R08A_RESETWHITEHIGH 0x008a +#define ZC3XX_R08B_I2CDEVICEADDR 0x008b +#define ZC3XX_R08C_I2CIDLEANDNACK 0x008c +#define ZC3XX_R08D_COMPABILITYMODE 0x008d +#define ZC3XX_R08E_COMPABILITYMODE2 0x008e + +/* I2C control */ +#define ZC3XX_R090_I2CCOMMAND 0x0090 +#define ZC3XX_R091_I2CSTATUS 0x0091 +#define ZC3XX_R092_I2CADDRESSSELECT 0x0092 +#define ZC3XX_R093_I2CSETVALUE 0x0093 +#define ZC3XX_R094_I2CWRITEACK 0x0094 +#define ZC3XX_R095_I2CREAD 0x0095 +#define ZC3XX_R096_I2CREADACK 0x0096 + +/* Window inside the sensor array */ +#define ZC3XX_R097_WINYSTARTHIGH 0x0097 +#define ZC3XX_R098_WINYSTARTLOW 0x0098 +#define ZC3XX_R099_WINXSTARTHIGH 0x0099 +#define ZC3XX_R09A_WINXSTARTLOW 0x009a +#define ZC3XX_R09B_WINHEIGHTHIGH 0x009b +#define ZC3XX_R09C_WINHEIGHTLOW 0x009c +#define ZC3XX_R09D_WINWIDTHHIGH 0x009d +#define ZC3XX_R09E_WINWIDTHLOW 0x009e +#define ZC3XX_R119_FIRSTYHIGH 0x0119 +#define ZC3XX_R11A_FIRSTYLOW 0x011a +#define ZC3XX_R11B_FIRSTXHIGH 0x011b +#define ZC3XX_R11C_FIRSTXLOW 0x011c + +/* Max sensor array size */ +#define ZC3XX_R09F_MAXXHIGH 0x009f +#define ZC3XX_R0A0_MAXXLOW 0x00a0 +#define ZC3XX_R0A1_MAXYHIGH 0x00a1 +#define ZC3XX_R0A2_MAXYLOW 0x00a2 +#define ZC3XX_R0A3_EXPOSURETIMEHIGH 0x00a3 +#define ZC3XX_R0A4_EXPOSURETIMELOW 0x00a4 +#define ZC3XX_R0A5_EXPOSUREGAIN 0x00a5 +#define ZC3XX_R0A6_EXPOSUREBLACKLVL 0x00a6 + +/* Other registers */ +#define ZC3XX_R100_OPERATIONMODE 0x0100 +#define ZC3XX_R101_SENSORCORRECTION 0x0101 + +/* Gains */ +#define ZC3XX_R116_RGAIN 0x0116 +#define ZC3XX_R117_GGAIN 0x0117 +#define ZC3XX_R118_BGAIN 0x0118 +#define ZC3XX_R11D_GLOBALGAIN 0x011d +#define ZC3XX_R1A8_DIGITALGAIN 0x01a8 +#define ZC3XX_R1A9_DIGITALLIMITDIFF 0x01a9 +#define ZC3XX_R1AA_DIGITALGAINSTEP 0x01aa + +/* Auto correction */ +#define ZC3XX_R180_AUTOCORRECTENABLE 0x0180 +#define ZC3XX_R181_WINXSTART 0x0181 +#define ZC3XX_R182_WINXWIDTH 0x0182 +#define ZC3XX_R183_WINXCENTER 0x0183 +#define ZC3XX_R184_WINYSTART 0x0184 +#define ZC3XX_R185_WINYWIDTH 0x0185 +#define ZC3XX_R186_WINYCENTER 0x0186 + +/* Gain range */ +#define ZC3XX_R187_MAXGAIN 0x0187 +#define ZC3XX_R188_MINGAIN 0x0188 + +/* Auto exposure and white balance */ +#define ZC3XX_R189_AWBSTATUS 0x0189 +#define ZC3XX_R18A_AWBFREEZE 0x018a +#define ZC3XX_R18B_AESTATUS 0x018b +#define ZC3XX_R18C_AEFREEZE 0x018c +#define ZC3XX_R18F_AEUNFREEZE 0x018f +#define ZC3XX_R190_EXPOSURELIMITHIGH 0x0190 +#define ZC3XX_R191_EXPOSURELIMITMID 0x0191 +#define ZC3XX_R192_EXPOSURELIMITLOW 0x0192 +#define ZC3XX_R195_ANTIFLICKERHIGH 0x0195 +#define ZC3XX_R196_ANTIFLICKERMID 0x0196 +#define ZC3XX_R197_ANTIFLICKERLOW 0x0197 + +/* What is this ? */ +#define ZC3XX_R18D_YTARGET 0x018d +#define ZC3XX_R18E_RESETLVL 0x018e + +/* Color */ +#define ZC3XX_R1A0_REDMEANAFTERAGC 0x01a0 +#define ZC3XX_R1A1_GREENMEANAFTERAGC 0x01a1 +#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2 +#define ZC3XX_R1A3_REDMEANAFTERAWB 0x01a3 +#define ZC3XX_R1A4_GREENMEANAFTERAWB 0x01a4 +#define ZC3XX_R1A5_BLUEMEANAFTERAWB 0x01a5 +#define ZC3XX_R1A6_YMEANAFTERAE 0x01a6 +#define ZC3XX_R1A7_CALCGLOBALMEAN 0x01a7 + +/* Matrixes */ + +/* Color matrix is like : + R' = R * RGB00 + G * RGB01 + B * RGB02 + RGB03 + G' = R * RGB10 + G * RGB11 + B * RGB22 + RGB13 + B' = R * RGB20 + G * RGB21 + B * RGB12 + RGB23 + */ +#define ZC3XX_R10A_RGB00 0x010a +#define ZC3XX_R10B_RGB01 0x010b +#define ZC3XX_R10C_RGB02 0x010c +#define ZC3XX_R113_RGB03 0x0113 +#define ZC3XX_R10D_RGB10 0x010d +#define ZC3XX_R10E_RGB11 0x010e +#define ZC3XX_R10F_RGB12 0x010f +#define ZC3XX_R114_RGB13 0x0114 +#define ZC3XX_R110_RGB20 0x0110 +#define ZC3XX_R111_RGB21 0x0111 +#define ZC3XX_R112_RGB22 0x0112 +#define ZC3XX_R115_RGB23 0x0115 + +/* Gamma matrix */ +#define ZC3XX_R120_GAMMA00 0x0120 +#define ZC3XX_R121_GAMMA01 0x0121 +#define ZC3XX_R122_GAMMA02 0x0122 +#define ZC3XX_R123_GAMMA03 0x0123 +#define ZC3XX_R124_GAMMA04 0x0124 +#define ZC3XX_R125_GAMMA05 0x0125 +#define ZC3XX_R126_GAMMA06 0x0126 +#define ZC3XX_R127_GAMMA07 0x0127 +#define ZC3XX_R128_GAMMA08 0x0128 +#define ZC3XX_R129_GAMMA09 0x0129 +#define ZC3XX_R12A_GAMMA0A 0x012a +#define ZC3XX_R12B_GAMMA0B 0x012b +#define ZC3XX_R12C_GAMMA0C 0x012c +#define ZC3XX_R12D_GAMMA0D 0x012d +#define ZC3XX_R12E_GAMMA0E 0x012e +#define ZC3XX_R12F_GAMMA0F 0x012f +#define ZC3XX_R130_GAMMA10 0x0130 +#define ZC3XX_R131_GAMMA11 0x0131 +#define ZC3XX_R132_GAMMA12 0x0132 +#define ZC3XX_R133_GAMMA13 0x0133 +#define ZC3XX_R134_GAMMA14 0x0134 +#define ZC3XX_R135_GAMMA15 0x0135 +#define ZC3XX_R136_GAMMA16 0x0136 +#define ZC3XX_R137_GAMMA17 0x0137 +#define ZC3XX_R138_GAMMA18 0x0138 +#define ZC3XX_R139_GAMMA19 0x0139 +#define ZC3XX_R13A_GAMMA1A 0x013a +#define ZC3XX_R13B_GAMMA1B 0x013b +#define ZC3XX_R13C_GAMMA1C 0x013c +#define ZC3XX_R13D_GAMMA1D 0x013d +#define ZC3XX_R13E_GAMMA1E 0x013e +#define ZC3XX_R13F_GAMMA1F 0x013f + +/* Luminance gamma */ +#define ZC3XX_R140_YGAMMA00 0x0140 +#define ZC3XX_R141_YGAMMA01 0x0141 +#define ZC3XX_R142_YGAMMA02 0x0142 +#define ZC3XX_R143_YGAMMA03 0x0143 +#define ZC3XX_R144_YGAMMA04 0x0144 +#define ZC3XX_R145_YGAMMA05 0x0145 +#define ZC3XX_R146_YGAMMA06 0x0146 +#define ZC3XX_R147_YGAMMA07 0x0147 +#define ZC3XX_R148_YGAMMA08 0x0148 +#define ZC3XX_R149_YGAMMA09 0x0149 +#define ZC3XX_R14A_YGAMMA0A 0x014a +#define ZC3XX_R14B_YGAMMA0B 0x014b +#define ZC3XX_R14C_YGAMMA0C 0x014c +#define ZC3XX_R14D_YGAMMA0D 0x014d +#define ZC3XX_R14E_YGAMMA0E 0x014e +#define ZC3XX_R14F_YGAMMA0F 0x014f +#define ZC3XX_R150_YGAMMA10 0x0150 +#define ZC3XX_R151_YGAMMA11 0x0151 + +#define ZC3XX_R1C5_SHARPNESSMODE 0x01c5 +#define ZC3XX_R1C6_SHARPNESS00 0x01c6 +#define ZC3XX_R1C7_SHARPNESS01 0x01c7 +#define ZC3XX_R1C8_SHARPNESS02 0x01c8 +#define ZC3XX_R1C9_SHARPNESS03 0x01c9 +#define ZC3XX_R1CA_SHARPNESS04 0x01ca +#define ZC3XX_R1CB_SHARPNESS05 0x01cb + +/* Dead pixels */ +#define ZC3XX_R250_DEADPIXELSMODE 0x0250 + +/* EEPROM */ +#define ZC3XX_R300_EEPROMCONFIG 0x0300 +#define ZC3XX_R301_EEPROMACCESS 0x0301 +#define ZC3XX_R302_EEPROMSTATUS 0x0302 diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c new file mode 100644 index 000000000000..f0bacee33ef9 --- /dev/null +++ b/drivers/media/usb/gspca/zc3xx.c @@ -0,0 +1,7024 @@ +/* + * Z-Star/Vimicro zc301/zc302p/vc30x driver + * + * Copyright (C) 2009-2012 Jean-Francois Moine + * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Jean-Francois Moine , " + "Serge A. Suchkov "); +MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); +MODULE_LICENSE("GPL"); + +static int force_sensor = -1; + +#define REG08_DEF 3 /* default JPEG compression (75%) */ +#include "zc3xx-reg.h" + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct { /* gamma/brightness/contrast control cluster */ + struct v4l2_ctrl *gamma; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + }; + struct { /* autogain/exposure control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + }; + struct v4l2_ctrl *plfreq; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *jpegqual; + + struct work_struct work; + struct workqueue_struct *work_thread; + + u8 reg08; /* webcam compression quality */ + + u8 bridge; + u8 sensor; /* Type of image sensor chip */ + u16 chip_revision; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; +enum bridges { + BRIDGE_ZC301, + BRIDGE_ZC303, +}; +enum sensors { + SENSOR_ADCM2700, + SENSOR_CS2102, + SENSOR_CS2102K, + SENSOR_GC0303, + SENSOR_GC0305, + SENSOR_HDCS2020, + SENSOR_HV7131B, + SENSOR_HV7131R, + SENSOR_ICM105A, + SENSOR_MC501CB, + SENSOR_MT9V111_1, /* (mi360soc) zc301 */ + SENSOR_MT9V111_3, /* (mi360soc) zc303 */ + SENSOR_OV7620, /* OV7648 - same values */ + SENSOR_OV7630C, + SENSOR_PAS106, + SENSOR_PAS202B, + SENSOR_PB0330, + SENSOR_PO2030, + SENSOR_TAS5130C, + SENSOR_MAX +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const struct v4l2_pix_format broken_vga_mode[] = { + {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 232 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 472 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* + * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest + * quality setting is not usable as USB 1 does not have enough bandwidth. + */ +static u8 jpeg_qual[] = {50, 75, 87, /* 94 */}; + +/* usb exchanges */ +struct usb_action { + u8 req; + u8 val; + u16 idx; +}; + +static const struct usb_action adcm2700_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */ + {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */ + {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */ + {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */ + {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */ + {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */ + {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */ + {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */ + {0xbb, 0x86, 0x0002}, /* 00,86,02,bb */ + {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */ + {0xbb, 0x86, 0x0802}, /* 08,86,02,bb */ + {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */ +/*mswin+*/ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, + {0xaa, 0xfe, 0x0002}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xaa, 0xb4, 0xcd37}, + {0xaa, 0xa4, 0x0004}, + {0xaa, 0xa8, 0x0007}, + {0xaa, 0xac, 0x0004}, +/*mswin-*/ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */ + {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {} +}; +static const struct usb_action adcm2700_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */ + {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */ + {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0050}, /* 00,00,50,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */ + {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */ + {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */ + {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */ + {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */ + {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */ + {0xbb, 0x86, 0x0002}, /* 00,88,02,bb */ + {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */ + {0xbb, 0x86, 0x0802}, /* 08,88,02,bb */ + {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */ + /*******/ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */ + {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {} +}; +static const struct usb_action adcm2700_50HZ[] = { + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x05, 0x8400}, /* 84,05,00,bb */ + {0xbb, 0xd0, 0xb007}, /* b0,d0,07,bb */ + {0xbb, 0xa0, 0xb80f}, /* b8,a0,0f,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {0xaa, 0x26, 0x00d0}, /* 00,26,d0,aa */ + {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */ + {} +}; +static const struct usb_action adcm2700_60HZ[] = { + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */ + {0xbb, 0x82, 0xb006}, /* b0,82,06,bb */ + {0xbb, 0x04, 0xb80d}, /* b8,04,0d,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {0xaa, 0x26, 0x0057}, /* 00,26,57,aa */ + {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */ + {} +}; +static const struct usb_action adcm2700_NoFliker[] = { + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */ + {0xbb, 0x05, 0xb000}, /* b0,05,00,bb */ + {0xbb, 0xa0, 0xb801}, /* b8,a0,01,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {} +}; +static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH}, + {0xa0, 0x21, ZC3XX_R081_HBLANKLOW}, + {0xa0, 0x30, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x31, ZC3XX_R084_GGAINADDR}, + {0xa0, 0x32, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x02, 0x0008}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x12, 0x0089}, + {0xaa, 0x13, 0x0000}, + {0xaa, 0x14, 0x00e9}, + {0xaa, 0x20, 0x0000}, + {0xaa, 0x22, 0x0000}, + {0xaa, 0x0b, 0x0004}, + {0xaa, 0x30, 0x0030}, + {0xaa, 0x31, 0x0030}, + {0xaa, 0x32, 0x0030}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x10, 0x01ae}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x00, 0x01ad}, + {} +}; + +static const struct usb_action cs2102_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH}, + {0xa0, 0x21, ZC3XX_R081_HBLANKLOW}, + {0xa0, 0x30, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x31, ZC3XX_R084_GGAINADDR}, + {0xa0, 0x32, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x02, 0x0008}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x11, 0x0001}, + {0xaa, 0x12, 0x0087}, + {0xaa, 0x13, 0x0001}, + {0xaa, 0x14, 0x00e7}, + {0xaa, 0x20, 0x0000}, + {0xaa, 0x22, 0x0000}, + {0xaa, 0x0b, 0x0004}, + {0xaa, 0x30, 0x0030}, + {0xaa, 0x31, 0x0030}, + {0xaa, 0x32, 0x0030}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x00, 0x01ad}, + {} +}; +static const struct usb_action cs2102_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0090}, + {0xaa, 0x21, 0x00dd}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x00c8}, + {0xaa, 0x21, 0x0068}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x0055}, + {0xaa, 0x25, 0x00cc}, + {0xaa, 0x21, 0x003f}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00aa}, + {0xaa, 0x25, 0x00e6}, + {0xaa, 0x21, 0x003f}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x55, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xcc, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x18, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x6a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x3f, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0000}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x0080}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; + +/* CS2102_KOCOM */ +static const struct usb_action cs2102K_InitialScale[] = { + {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x01, 0x01b1}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {} +}; + +static const struct usb_action cs2102K_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, +/*fixme: next sequence = i2c exchanges*/ + {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x01, 0x01b1}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, +/*fixme:what does the next sequence?*/ + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {} +}; + +static const struct usb_action gc0305_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */ + {0xaa, 0x13, 0x0002}, /* 00,13,02,aa */ + {0xaa, 0x15, 0x0003}, /* 00,15,03,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */ + {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ + {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */ + {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */ + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */ + {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */ + {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */ + {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */ + {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */ + {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */ + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */ + {0xaa, 0x17, 0x00e6}, /* 00,17,e6,aa */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */ + {0xaa, 0x19, 0x0086}, /* 00,19,86,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x85, ZC3XX_R18D_YTARGET}, /* 01,8d,85,cc */ + {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */ + {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */ + {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */ + {} +}; +static const struct usb_action gc0305_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */ + {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ + {0xaa, 0x15, 0x0001}, /* 00,15,01,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */ + {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ + {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */ + {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */ + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */ + {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */ + {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */ + {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */ + {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */ + {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */ + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */ + {0xaa, 0x17, 0x00e8}, /* 00,17,e8,aa */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */ + {0xaa, 0x19, 0x0088}, /* 00,19,88,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */ + {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */ + {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */ + {} +}; +static const struct usb_action gc0305_50HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */ + {0xaa, 0x84, 0x0038}, /* 00,84,38,aa */ /* win: 00,84,ec */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + /* win: 01,92,10 */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc */ + /* win: 01,97,ec */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ +/* {0xa0, 0x85, ZC3XX_R18D_YTARGET}, * 01,8d,85,cc * + * if 640x480 */ + {} +}; +static const struct usb_action gc0305_60HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x00ec}, /* 00,84,ec,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */ + {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0xec, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,ec,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */ + {} +}; + +static const struct usb_action gc0305_NoFliker[] = { + {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */ + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,00,cc */ + {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,48,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ + {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */ + {} +}; + +static const struct usb_action hdcs2020_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */ + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x1c, 0x0000}, + {0xaa, 0x0a, 0x0001}, + {0xaa, 0x0b, 0x0006}, + {0xaa, 0x0c, 0x007b}, + {0xaa, 0x0d, 0x00a7}, + {0xaa, 0x03, 0x00fb}, + {0xaa, 0x05, 0x0000}, + {0xaa, 0x06, 0x0003}, + {0xaa, 0x09, 0x0008}, + + {0xaa, 0x0f, 0x0018}, /* set sensor gain */ + {0xaa, 0x10, 0x0018}, + {0xaa, 0x11, 0x0018}, + {0xaa, 0x12, 0x0018}, + + {0xaa, 0x15, 0x004e}, + {0xaa, 0x1c, 0x0004}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + + {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xed, ZC3XX_R10B_RGB01}, + {0xa0, 0xed, ZC3XX_R10C_RGB02}, + {0xa0, 0xed, ZC3XX_R10D_RGB10}, + {0xa0, 0x66, ZC3XX_R10E_RGB11}, + {0xa0, 0xed, ZC3XX_R10F_RGB12}, + {0xa0, 0xed, ZC3XX_R110_RGB20}, + {0xa0, 0xed, ZC3XX_R111_RGB21}, + {0xa0, 0x66, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x13, 0x0031}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x0e, 0x0004}, + {0xaa, 0x19, 0x00cd}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 0x14 */ + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x41, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action hdcs2020_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x1c, 0x0000}, + {0xaa, 0x0a, 0x0001}, + {0xaa, 0x0b, 0x0006}, + {0xaa, 0x0c, 0x007a}, + {0xaa, 0x0d, 0x00a7}, + {0xaa, 0x03, 0x00fb}, + {0xaa, 0x05, 0x0000}, + {0xaa, 0x06, 0x0003}, + {0xaa, 0x09, 0x0008}, + {0xaa, 0x0f, 0x0018}, /* original setting */ + {0xaa, 0x10, 0x0018}, + {0xaa, 0x11, 0x0018}, + {0xaa, 0x12, 0x0018}, + {0xaa, 0x15, 0x004e}, + {0xaa, 0x1c, 0x0004}, + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xed, ZC3XX_R10B_RGB01}, + {0xa0, 0xed, ZC3XX_R10C_RGB02}, + {0xa0, 0xed, ZC3XX_R10D_RGB10}, + {0xa0, 0x66, ZC3XX_R10E_RGB11}, + {0xa0, 0xed, ZC3XX_R10F_RGB12}, + {0xa0, 0xed, ZC3XX_R110_RGB20}, + {0xa0, 0xed, ZC3XX_R111_RGB21}, + {0xa0, 0x66, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + /**** set exposure ***/ + {0xaa, 0x13, 0x0031}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x0e, 0x0004}, + {0xaa, 0x19, 0x00cd}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x41, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action hdcs2020_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */ + {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ + {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */ + {0xa0, 0x05, ZC3XX_R01D_HSYNC_0}, /* 00,1d,05,cc */ + {0xa0, 0x1a, ZC3XX_R01E_HSYNC_1}, /* 00,1e,1a,cc */ + {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */ + {} +}; +static const struct usb_action hdcs2020_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ + {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ + {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */ + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */ + {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, /* 00,1e,18,cc */ + {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */ + {} +}; +static const struct usb_action hdcs2020_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ + {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ + {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */ + {0xa0, 0x17, ZC3XX_R01E_HSYNC_1}, /* 00,1e,17,cc */ + {0xa0, 0x2a, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2a,cc */ + {} +}; + +static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x30, 0x002d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x13, 0x0001}, /* {0xaa, 0x13, 0x0000}, */ + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e8}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0086}, /* 00,17,88,aa */ + {0xaa, 0x31, 0x0038}, + {0xaa, 0x32, 0x0038}, + {0xaa, 0x33, 0x0038}, + {0xaa, 0x5b, 0x0001}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x02, ZC3XX_R188_MINGAIN}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0x02, 0x0090}, /* 00,02,80,aa */ + {} +}; + +static const struct usb_action hv7131b_Initial[] = { /* 640x480*/ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x30, 0x002d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x11, 0x0001}, + {0xaa, 0x13, 0x0000}, /* {0xaa, 0x13, 0x0001}; */ + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e6}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0086}, + {0xaa, 0x31, 0x0038}, + {0xaa, 0x32, 0x0038}, + {0xaa, 0x33, 0x0038}, + {0xaa, 0x5b, 0x0001}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x02, ZC3XX_R188_MINGAIN}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */ + {} +}; +static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ + {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */ + {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */ + {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ + {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */ + {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */ + {} +}; +static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ + {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */ + {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ + {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */ + {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */ + {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ + {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ + {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */ + {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */ + {} +}; +static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */ + {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */ + {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */ + {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */ + {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ + {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */ + {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */ + {} +}; +static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */ + {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ + {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ + {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ + {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */ + {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */ + {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ + {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */ + {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */ + {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */ + {} +}; +static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ + {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */ + {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ + {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ + {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */ + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */ + {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */ + {} +}; +static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ + {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ + {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ + {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ + {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */ + {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */ + {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */ + {} +}; + +/* from lPEPI264v.inf (hv7131b!) */ +static const struct usb_action hv7131r_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x000c}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x13, 0x0000}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e8}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0088}, + {0xaa, 0x30, 0x000b}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {} +}; +static const struct usb_action hv7131r_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x000c}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x13, 0x0000}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e6}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0086}, + {0xaa, 0x30, 0x000b}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {} +}; +static const struct usb_action hv7131r_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; + +static const struct usb_action icm105a_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH}, + {0xa0, 0x01, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH}, + {0xa0, 0x01, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x01, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x01, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xaa, 0x01, 0x0010}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0001}, + {0xaa, 0x04, 0x0011}, + {0xaa, 0x05, 0x00a0}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0002}, + {0xaa, 0x04, 0x0013}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0003}, + {0xaa, 0x04, 0x0015}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0004}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0005}, + {0xaa, 0x04, 0x0019}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0006}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0026}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0007}, + {0xaa, 0x04, 0x0019}, + {0xaa, 0x05, 0x0022}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0008}, + {0xaa, 0x04, 0x0021}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0009}, + {0xaa, 0x04, 0x0023}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000a}, + {0xaa, 0x04, 0x0025}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000b}, + {0xaa, 0x04, 0x00ec}, + {0xaa, 0x05, 0x002e}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000c}, + {0xaa, 0x04, 0x00fa}, + {0xaa, 0x05, 0x002a}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x07, 0x000d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x94, 0x0002}, + {0xaa, 0x90, 0x0000}, + {0xaa, 0x91, 0x001f}, + {0xaa, 0x10, 0x0064}, + {0xaa, 0x9b, 0x00f0}, + {0xaa, 0x9c, 0x0002}, + {0xaa, 0x14, 0x001a}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0xa8, 0x00c0}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf7, ZC3XX_R10B_RGB01}, + {0xa0, 0xf7, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x52, ZC3XX_R10E_RGB11}, + {0xa0, 0xf7, ZC3XX_R10F_RGB12}, + {0xa0, 0xf7, ZC3XX_R110_RGB20}, + {0xa0, 0xf7, ZC3XX_R111_RGB21}, + {0xa0, 0x52, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x0d, 0x0003}, + {0xaa, 0x0c, 0x008c}, + {0xaa, 0x0e, 0x0095}, + {0xaa, 0x0f, 0x0002}, + {0xaa, 0x1c, 0x0094}, + {0xaa, 0x1d, 0x0002}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0xc0, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; + +static const struct usb_action icm105a_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH}, + {0xa0, 0x02, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH}, + {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x02, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xaa, 0x01, 0x0010}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0001}, + {0xaa, 0x04, 0x0011}, + {0xaa, 0x05, 0x00a0}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0002}, + {0xaa, 0x04, 0x0013}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0003}, + {0xaa, 0x04, 0x0015}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0004}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0005}, + {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x19, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0006}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0026}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0007}, + {0xaa, 0x04, 0x0019}, + {0xaa, 0x05, 0x0022}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0008}, + {0xaa, 0x04, 0x0021}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0009}, + {0xaa, 0x04, 0x0023}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000a}, + {0xaa, 0x04, 0x0025}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000b}, + {0xaa, 0x04, 0x00ec}, + {0xaa, 0x05, 0x002e}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000c}, + {0xaa, 0x04, 0x00fa}, + {0xaa, 0x05, 0x002a}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x07, 0x000d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x94, 0x0002}, + {0xaa, 0x90, 0x0000}, + {0xaa, 0x91, 0x0010}, + {0xaa, 0x10, 0x0064}, + {0xaa, 0x9b, 0x00f0}, + {0xaa, 0x9c, 0x0002}, + {0xaa, 0x14, 0x001a}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0xa8, 0x0080}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf7, ZC3XX_R10B_RGB01}, + {0xa0, 0xf7, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x52, ZC3XX_R10E_RGB11}, + {0xa0, 0xf7, ZC3XX_R10F_RGB12}, + {0xa0, 0xf7, ZC3XX_R110_RGB20}, + {0xa0, 0xf7, ZC3XX_R111_RGB21}, + {0xa0, 0x52, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x0d, 0x0003}, + {0xaa, 0x0c, 0x0020}, + {0xaa, 0x0e, 0x000e}, + {0xaa, 0x0f, 0x0002}, + {0xaa, 0x1c, 0x000d}, + {0xaa, 0x1d, 0x0002}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action icm105a_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */ + {0xaa, 0x0e, 0x000e}, /* 00,0e,0e,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x000d}, /* 00,1c,0d,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,0d,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,1a,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4b,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ + {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d8,cc */ + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {} +}; +static const struct usb_action icm105a_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */ + {0xaa, 0x0e, 0x0095}, /* 00,0e,95,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0094}, /* 00,1c,94,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,94,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,84,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e3,cc */ + {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, /* 00,1e,ec,cc */ + {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f5,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ + {} +}; +static const struct usb_action icm105a_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ + {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0008}, /* 00,1c,08,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x08, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,08,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x41, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,41,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ + {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ + {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {} +}; +static const struct usb_action icm105a_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0e, 0x0086}, /* 00,0e,86,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0085}, /* 00,1c,85,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x85, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,85,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,08,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xc2, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c2,cc */ + {0xa0, 0xd6, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d6,cc */ + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ + {} +}; +static const struct usb_action icm105a_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ + {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0000}, /* 00,1c,00,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x00, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,00,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ + {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ + {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {} +}; +static const struct usb_action icm105a_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ + {0xaa, 0x0e, 0x0081}, /* 00,0e,81,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0080}, /* 00,1c,80,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x80, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,80,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ + {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ + {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ + {} +}; + +static const struct usb_action mc501cb_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */ + {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */ + {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */ + {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ + {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */ + {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ + {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */ + {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */ + {0xaa, 0x18, 0x00de}, /* 00,18,de,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x0086}, /* 00,1a,86,aa */ + {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */ + {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ + {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */ + {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */ + {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */ + {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */ + {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */ + {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */ + {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */ + {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */ + {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */ + {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */ + {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */ + {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */ + {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */ + {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */ + {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */ + {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ + {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ + {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ + {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */ + {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ + {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ + {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ + {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */ + {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */ + {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */ + {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */ + {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */ + {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */ + {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */ + {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */ + {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */ + {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */ + {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */ + {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */ + {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */ + {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */ + {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */ + {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */ + {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x1c, 0x0050}, /* 00,1C,50,aa */ + {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */ + {0xaa, 0x3b, 0x001d}, /* 00,3b,1D,aa */ + {0xaa, 0x3c, 0x004c}, /* 00,3c,4C,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3d,18,aa */ + {0xaa, 0x3e, 0x006a}, /* 00,3e,6A,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */ + {0xaa, 0x51, 0x0027}, /* 00,51,27,aa */ + {0xaa, 0x52, 0x0020}, /* 00,52,20,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */ + {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */ + {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */ + {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */ + {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */ + {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */ + + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ + {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ + {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ + {} +}; + +static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */ + {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */ + {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */ + {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ + {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */ + {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ + {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */ + {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */ + {0xaa, 0x18, 0x00d8}, /* 00,18,d8,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x0088}, /* 00,1a,88,aa */ + {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */ + {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ + {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */ + {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */ + {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */ + {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */ + {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */ + {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */ + {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */ + {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */ + {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */ + {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */ + {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */ + {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */ + {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */ + {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */ + {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */ + {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ + {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ + {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ + {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */ + {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ + {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ + {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ + {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */ + {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */ + {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */ + {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */ + {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */ + {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */ + {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */ + {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */ + {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */ + {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */ + {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */ + {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */ + {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */ + {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */ + {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */ + {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */ + {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x1c, 0x0050}, /* 00,1c,50,aa */ + {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */ + {0xaa, 0x3b, 0x003a}, /* 00,3b,3A,aa */ + {0xaa, 0x3c, 0x0098}, /* 00,3c,98,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3d,30,aa */ + {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */ + {0xaa, 0x51, 0x004e}, /* 00,51,4E,aa */ + {0xaa, 0x52, 0x0041}, /* 00,52,41,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */ + {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */ + {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */ + {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */ + {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */ + {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */ + {} +}; + +static const struct usb_action mc501cb_50HZ[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ + {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ + {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ + {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */ + {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */ + {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */ + {} +}; + +static const struct usb_action mc501cb_50HZScale[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ + {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */ + {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */ + {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */ + {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */ + {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */ + {} +}; + +static const struct usb_action mc501cb_60HZ[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ + {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ + {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ + {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ + {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ + {} +}; + +static const struct usb_action mc501cb_60HZScale[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ + {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ + {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ + {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ + {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ + {} +}; + +static const struct usb_action mc501cb_NoFliker[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ + {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ + {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ + {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ + {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ + {} +}; + +static const struct usb_action mc501cb_NoFlikerScale[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ + {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ + {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ + {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ + {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ + {} +}; + +/* from zs211.inf */ +static const struct usb_action ov7620_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */ + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */ + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */ + {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */ + {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */ + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */ + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */ + {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ + {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */ + {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x00f1}, /* 00,1a,f1,aa */ + {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */ + {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */ + {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */ + {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */ + {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */ + {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */ + {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */ + {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */ + {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */ + {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */ + {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */ + {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */ + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x40, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,40,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ + {} +}; +static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + /* mx change? */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */ + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */ + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */ + {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */ + {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */ + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */ + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xd6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d6,cc */ + /* OV7648 00,9c,d8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */ + {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ + {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */ + {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */ + {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */ + {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x00f2}, /* 00,1a,f2,aa */ + {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */ + {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */ + {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */ + {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */ + {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */ + {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */ + {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */ + {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */ + {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */ + {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */ + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,50,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ + {} +}; +static const struct usb_action ov7620_50HZ[] = { + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ + {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */ + {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ +/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc + * if mode0 (640x480) */ + {} +}; +static const struct usb_action ov7620_60HZ[] = { + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + /* (bug in zs211.inf) */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ + {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */ + {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ +/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc + * if mode0 (640x480) */ +/* ?? in gspca v1, it was + {0xa0, 0x00, 0x0039}, * 00,00,00,dd * + {0xa1, 0x01, 0x0037}, */ + {} +}; +static const struct usb_action ov7620_NoFliker[] = { + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + /* (bug in zs211.inf) */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ + {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */ + {0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */ +/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc + * if mode1 (320x240) */ +/* ?? was + {0xa0, 0x00, 0x0039}, * 00,00,00,dd * + {0xa1, 0x01, 0x0037}, */ + {} +}; + +static const struct usb_action ov7630c_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x12, 0x0080}, + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x12, 0x0069}, + {0xaa, 0x04, 0x0020}, + {0xaa, 0x06, 0x0050}, + {0xaa, 0x13, 0x0083}, + {0xaa, 0x14, 0x0000}, + {0xaa, 0x15, 0x0024}, + {0xaa, 0x17, 0x0018}, + {0xaa, 0x18, 0x00ba}, + {0xaa, 0x19, 0x0002}, + {0xaa, 0x1a, 0x00f6}, + {0xaa, 0x1b, 0x0002}, + {0xaa, 0x20, 0x00c2}, + {0xaa, 0x24, 0x0060}, + {0xaa, 0x25, 0x0040}, + {0xaa, 0x26, 0x0030}, + {0xaa, 0x27, 0x00ea}, + {0xaa, 0x28, 0x00a0}, + {0xaa, 0x21, 0x0000}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0096}, + {0xaa, 0x2d, 0x0094}, + {0xaa, 0x2f, 0x003d}, + {0xaa, 0x30, 0x0024}, + {0xaa, 0x60, 0x0000}, + {0xaa, 0x61, 0x0040}, + {0xaa, 0x68, 0x007c}, + {0xaa, 0x6f, 0x0015}, + {0xaa, 0x75, 0x0088}, + {0xaa, 0x77, 0x00b5}, + {0xaa, 0x01, 0x0060}, + {0xaa, 0x02, 0x0060}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x46, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R113_RGB03}, +/* 0x10, */ + {0xa1, 0x01, 0x0002}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x01, ZC3XX_R120_GAMMA00}, /* gamma 2 ?*/ + {0xa0, 0x0c, ZC3XX_R121_GAMMA01}, + {0xa0, 0x1f, ZC3XX_R122_GAMMA02}, + {0xa0, 0x3a, ZC3XX_R123_GAMMA03}, + {0xa0, 0x53, ZC3XX_R124_GAMMA04}, + {0xa0, 0x6d, ZC3XX_R125_GAMMA05}, + {0xa0, 0x85, ZC3XX_R126_GAMMA06}, + {0xa0, 0x9c, ZC3XX_R127_GAMMA07}, + {0xa0, 0xb0, ZC3XX_R128_GAMMA08}, + {0xa0, 0xc2, ZC3XX_R129_GAMMA09}, + {0xa0, 0xd1, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xde, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xe9, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf2, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xf9, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x05, ZC3XX_R130_GAMMA10}, + {0xa0, 0x0f, ZC3XX_R131_GAMMA11}, + {0xa0, 0x16, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1a, ZC3XX_R133_GAMMA13}, + {0xa0, 0x19, ZC3XX_R134_GAMMA14}, + {0xa0, 0x19, ZC3XX_R135_GAMMA15}, + {0xa0, 0x17, ZC3XX_R136_GAMMA16}, + {0xa0, 0x15, ZC3XX_R137_GAMMA17}, + {0xa0, 0x12, ZC3XX_R138_GAMMA18}, + {0xa0, 0x10, ZC3XX_R139_GAMMA19}, + {0xa0, 0x0e, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x0b, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x09, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x08, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x06, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x03, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x10, 0x001b}, + {0xaa, 0x76, 0x0002}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0000}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xb8, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x37, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x13, 0x0083}, /* 40 */ + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action ov7630c_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + + {0xaa, 0x12, 0x0080}, + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x12, 0x0069}, /* i2c */ + {0xaa, 0x04, 0x0020}, + {0xaa, 0x06, 0x0050}, + {0xaa, 0x13, 0x00c3}, + {0xaa, 0x14, 0x0000}, + {0xaa, 0x15, 0x0024}, + {0xaa, 0x19, 0x0003}, + {0xaa, 0x1a, 0x00f6}, + {0xaa, 0x1b, 0x0002}, + {0xaa, 0x20, 0x00c2}, + {0xaa, 0x24, 0x0060}, + {0xaa, 0x25, 0x0040}, + {0xaa, 0x26, 0x0030}, + {0xaa, 0x27, 0x00ea}, + {0xaa, 0x28, 0x00a0}, + {0xaa, 0x21, 0x0000}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0096}, + {0xaa, 0x2d, 0x0084}, + {0xaa, 0x2f, 0x003d}, + {0xaa, 0x30, 0x0024}, + {0xaa, 0x60, 0x0000}, + {0xaa, 0x61, 0x0040}, + {0xaa, 0x68, 0x007c}, + {0xaa, 0x6f, 0x0015}, + {0xaa, 0x75, 0x0088}, + {0xaa, 0x77, 0x00b5}, + {0xaa, 0x01, 0x0060}, + {0xaa, 0x02, 0x0060}, + {0xaa, 0x17, 0x0018}, + {0xaa, 0x18, 0x00ba}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x46, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R113_RGB03}, + + {0xa1, 0x01, 0x0002}, + {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xfe, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x4d, ZC3XX_R10E_RGB11}, + {0xa0, 0xfc, ZC3XX_R10F_RGB12}, + {0xa0, 0x00, ZC3XX_R110_RGB20}, + {0xa0, 0xf6, ZC3XX_R111_RGB21}, + {0xa0, 0x4a, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x16, ZC3XX_R120_GAMMA00}, /* gamma ~4 */ + {0xa0, 0x3a, ZC3XX_R121_GAMMA01}, + {0xa0, 0x5b, ZC3XX_R122_GAMMA02}, + {0xa0, 0x7c, ZC3XX_R123_GAMMA03}, + {0xa0, 0x94, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa9, ZC3XX_R125_GAMMA05}, + {0xa0, 0xbb, ZC3XX_R126_GAMMA06}, + {0xa0, 0xca, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd7, ZC3XX_R128_GAMMA08}, + {0xa0, 0xe1, ZC3XX_R129_GAMMA09}, + {0xa0, 0xea, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xf1, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf7, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xfc, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x20, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xfe, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x4d, ZC3XX_R10E_RGB11}, + {0xa0, 0xfc, ZC3XX_R10F_RGB12}, + {0xa0, 0x00, ZC3XX_R110_RGB20}, + {0xa0, 0xf6, ZC3XX_R111_RGB21}, + {0xa0, 0x4a, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x10, 0x000d}, + {0xaa, 0x76, 0x0002}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0000}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd8, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x1b, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x13, 0x00c3}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action pas106b_Initial_com[] = { +/* Sream and Sensor specific */ + {0xa1, 0x01, 0x0010}, /* CMOSSensorSelect */ +/* System */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */ +/* Picture size */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* ClockSelect */ + {0xa0, 0x03, 0x003a}, + {0xa0, 0x0c, 0x003b}, + {0xa0, 0x04, 0x0038}, + {} +}; + +static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, +/* Sream and Sensor specific */ + {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, +/* Picture size */ + {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW}, +/* System */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, +/* Sream and Sensor specific */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, +/* Sensor Interface */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, +/* Window inside sensor array */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, +/* Init the sensor */ + {0xaa, 0x02, 0x0004}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x09, 0x0005}, + {0xaa, 0x0a, 0x0002}, + {0xaa, 0x0b, 0x0002}, + {0xaa, 0x0c, 0x0005}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x0e, 0x0002}, + {0xaa, 0x14, 0x0081}, +/* Other registers */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, +/* Frame retreiving */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, +/* Gains */ + {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, +/* Unknown */ + {0xa0, 0x00, 0x01ad}, +/* Sharpness */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, +/* Other registers */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, +/* Other registers */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, + + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, +/* Auto correction */ + {0xa0, 0x03, ZC3XX_R181_WINXSTART}, + {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, + {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, + {0xa0, 0x03, ZC3XX_R184_WINYSTART}, + {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, + {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, +/* Auto exposure and white balance */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, +/* sensor on */ + {0xaa, 0x07, 0x00b1}, + {0xaa, 0x05, 0x0003}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x03, 0x003b}, +/* Gains */ + {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, +/* Auto correction */ + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, +/* Gains */ + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; + +static const struct usb_action pas106b_Initial[] = { /* 352x288 */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, +/* Sream and Sensor specific */ + {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, +/* Picture size */ + {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW}, +/* System */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, +/* Sream and Sensor specific */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, +/* Sensor Interface */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, +/* Window inside sensor array */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, +/* Init the sensor */ + {0xaa, 0x02, 0x0004}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x09, 0x0005}, + {0xaa, 0x0a, 0x0002}, + {0xaa, 0x0b, 0x0002}, + {0xaa, 0x0c, 0x0005}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x0e, 0x0002}, + {0xaa, 0x14, 0x0081}, +/* Other registers */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, +/* Frame retreiving */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, +/* Gains */ + {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, +/* Unknown */ + {0xa0, 0x00, 0x01ad}, +/* Sharpness */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, +/* Other registers */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, +/* Other registers */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, + + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, +/* Auto correction */ + {0xa0, 0x03, ZC3XX_R181_WINXSTART}, + {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, + {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, + {0xa0, 0x03, ZC3XX_R184_WINYSTART}, + {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, + {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + +/* Auto exposure and white balance */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, + + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, + + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, +/* sensor on */ + {0xaa, 0x07, 0x00b1}, + {0xaa, 0x05, 0x0003}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x03, 0x003b}, +/* Gains */ + {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, +/* Auto correction */ + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, +/* Gains */ + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + + {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */ + {0xa0, 0xff, ZC3XX_R018_FRAMELOST}, /* Frame adjust */ + {} +}; +static const struct usb_action pas106b_50HZ[] = { + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ + {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,54,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,87,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */ + {0xaa, 0x03, 0x0021}, /* 00,03,21,aa */ + {0xaa, 0x04, 0x000c}, /* 00,04,0c,aa */ + {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */ + {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */ + {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */ + {} +}; +static const struct usb_action pas106b_60HZ[] = { + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ + {0xa0, 0x2e, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,2e,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x71, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,71,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */ + {0xaa, 0x03, 0x001c}, /* 00,03,1c,aa */ + {0xaa, 0x04, 0x0004}, /* 00,04,04,aa */ + {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ + {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */ + {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */ + {} +}; +static const struct usb_action pas106b_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ + {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xaa, 0x03, 0x0013}, /* 00,03,13,aa */ + {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ + {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ + {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {} +}; + +/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */ +static const struct usb_action pas202b_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */ + {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ + {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ + {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ + {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ + {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ + {0xaa, 0x0c, 0x0006}, + {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ + {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ + {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ + {} +}; +static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */ + {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */ + {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */ + {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */ + {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ + {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ + {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ + {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ + {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ + {0xaa, 0x0c, 0x0006}, + {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ + {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ + {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ + {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH}, + {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW}, + {} +}; +static const struct usb_action pas202b_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x001b}, + {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */ + {0xaa, 0x04, 0x0008}, + {0xaa, 0x05, 0x001b}, + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ + {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0004}, + {0xaa, 0x21, 0x003d}, + {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */ + {0xaa, 0x04, 0x0010}, + {0xaa, 0x05, 0x003d}, + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */ + {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */ + {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */ + {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0004}, + {0xaa, 0x21, 0x0008}, + {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */ + {0xaa, 0x04, 0x0010}, + {0xaa, 0x05, 0x0008}, + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0006}, + {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0006}, + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0004}, + {0xaa, 0x21, 0x000c}, + {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ + {0xaa, 0x04, 0x0010}, + {0xaa, 0x05, 0x000c}, + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; + +/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */ +static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0001}, + {0xaa, 0x06, 0x0000}, + {0xaa, 0x08, 0x0483}, + {0xaa, 0x01, 0x0004}, + {0xaa, 0x08, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x5100}, + {0xaa, 0x35, 0x507f}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x007f}, + {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0001}, + {0xaa, 0x06, 0x0000}, + {0xaa, 0x08, 0x0483}, + {0xaa, 0x01, 0x0004}, + {0xaa, 0x08, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x5100}, + {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/ + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/ + {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action mt9v111_1_AE50HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0562}, + {0xbb, 0x01, 0x09aa}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_1_AE50HZScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0934}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_1_AE60HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x003d}, + {0xaa, 0x09, 0x016e}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_1_AE60HZScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0983}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_1_AENoFliker[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_1_AENoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0534}, + {0xbb, 0x02, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */ +static const struct usb_action mt9v111_3_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */ + {0xaa, 0x06, 0x0000}, /* operating mode control */ + {0xaa, 0x08, 0x0483}, /* output format control */ + /* H red first, V red or blue first, + * raw Bayer, auto flicker */ + {0xaa, 0x01, 0x0004}, /* select sensor core registers */ + {0xaa, 0x08, 0x0006}, /* row start */ + {0xaa, 0x02, 0x0011}, /* column start */ + {0xaa, 0x03, 0x01e5}, /* window height - 1 */ + {0xaa, 0x04, 0x0285}, /* window width - 1 */ + {0xaa, 0x07, 0x3002}, /* output control */ + {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */ + {0xaa, 0x35, 0x007f}, /* global gain */ + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x007f}, /* green1 gain */ + {0xaa, 0x2c, 0x007f}, /* blue gain */ + {0xaa, 0x2d, 0x007f}, /* red gain */ + {0xaa, 0x2e, 0x007f}, /* green2 gain */ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action mt9v111_3_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0001}, + {0xaa, 0x06, 0x0000}, + {0xaa, 0x08, 0x0483}, + {0xaa, 0x01, 0x0004}, + {0xaa, 0x08, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x35, 0x007f}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x007f}, + {0xaa, 0x2c, 0x007f}, + {0xaa, 0x2d, 0x007f}, + {0xaa, 0x2e, 0x007f}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action mt9v111_3_AE50HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, /* horizontal blanking */ + {0xaa, 0x09, 0x01ce}, /* shutter width */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AE50HZScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x01ce}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AE60HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x0083}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AE60HZScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x0083}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AENoFliker[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0034}, + {0xaa, 0x09, 0x0260}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AENoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0034}, + {0xaa, 0x09, 0x0260}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action pb0330_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ + {0xaa, 0x06, 0x0003}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x2f, 0xf7b0}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x34, 0x0100}, + {0xaa, 0x35, 0x0060}, + {0xaa, 0x3d, 0x068f}, + {0xaa, 0x40, 0x01e0}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */ + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ + {} +}; +static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x06, 0x0003}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x2f, 0xf7b0}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x34, 0x0100}, + {0xaa, 0x35, 0x0060}, + {0xaa, 0x3d, 0x068f}, + {0xaa, 0x40, 0x01e0}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ + {} +}; +static const struct usb_action pb0330_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x055c}, + {0xbb, 0x01, 0x09aa}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0566}, + {0xbb, 0x02, 0x09b2}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0974}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x02, 0x096c}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x02, 0x0940}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0980}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {} +}; + +/* from oem9.inf */ +static const struct usb_action po2030_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */ + {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */ + {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */ + {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */ + {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */ + {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */ + {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */ + {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xaa, 0x09, 0x00ce}, /* 00,09,ce,aa */ + {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */ + {0xaa, 0x0d, 0x0054}, /* 00,0d,54,aa */ + {0xaa, 0x0f, 0x00eb}, /* 00,0f,eb,aa */ + {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */ + {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */ + {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */ + {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */ + {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */ + {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */ + {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */ + {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */ + {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */ + {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */ + {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */ + {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */ + {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */ + {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */ + {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */ + {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */ + {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */ + {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */ + {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */ + {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */ + {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */ + {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */ + {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */ + {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */ + {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */ + {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */ + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */ + {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */ + {} +}; + +/* from oem9.inf */ +static const struct usb_action po2030_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */ + {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */ + {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */ + {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */ + {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */ + {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */ + {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */ + {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xaa, 0x09, 0x00cc}, /* 00,09,cc,aa */ + {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */ + {0xaa, 0x0d, 0x0058}, /* 00,0d,58,aa */ + {0xaa, 0x0f, 0x00ed}, /* 00,0f,ed,aa */ + {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */ + {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */ + {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */ + {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */ + {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */ + {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */ + {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */ + {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */ + {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */ + {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */ + {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */ + {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */ + {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */ + {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */ + {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */ + {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */ + {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */ + {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */ + {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */ + {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */ + {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */ + {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */ + {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */ + {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */ + {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */ + {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */ + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */ + {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */ + {} +}; + +static const struct usb_action po2030_50HZ[] = { + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */ + {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */ + {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */ + {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */ + {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */ + {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */ + {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */ + {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {} +}; + +static const struct usb_action po2030_60HZ[] = { + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */ + {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */ + {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */ + {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */ + {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */ + {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */ + /* win: 01,8d,80 */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {} +}; + +static const struct usb_action po2030_NoFliker[] = { + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */ + {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */ + {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */ + {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ + {} +}; + +static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + + {0xa0, 0x04, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x04, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN}, + {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, + {} +}; +static const struct usb_action tas5130c_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x05, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x05, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN}, + {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, + {} +}; +static const struct usb_action tas5130c_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */ + {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */ + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {} +}; +static const struct usb_action tas5130c_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */ + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */ + {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {} +}; +static const struct usb_action tas5130c_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {} +}; +static const struct usb_action tas5130c_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {} +}; +static const struct usb_action tas5130c_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {} +}; + +static const struct usb_action tas5130c_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {} +}; + +/* from usbvm305.inf 0ac8:305b 07/06/15 (3 - tas5130c) */ +static const struct usb_action gc0303_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ + {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc, + * 6<->8 */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc, + * 6<->8 */ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xaa, 0x01, 0x0000}, + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ + {0xaa, 0x1b, 0x0000}, + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */ + {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */ + {0xaa, 0x0a, 0x0002}, + {0xaa, 0x0b, 0x0000}, + {0xaa, 0x0c, 0x0002}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x0e, 0x0002}, + {0xaa, 0x0f, 0x0000}, + {0xaa, 0x10, 0x0002}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ + {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa, (e6 -> e8) */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ + {0xaa, 0x19, 0x0088}, /* 00,19,86,aa, */ + {0xaa, 0x20, 0x0020}, /* 00,20,20,aa, */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */ + {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */ + {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */ + {0xaa, 0x1b, 0x0000}, + {} +}; + +static const struct usb_action gc0303_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ + {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc, + * 8<->6 */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc, + * 8<->6 */ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xaa, 0x01, 0x0000}, + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ + {0xaa, 0x1b, 0x0000}, + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */ + {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */ + {0xaa, 0x0a, 0x0001}, + {0xaa, 0x0b, 0x0000}, + {0xaa, 0x0c, 0x0001}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x0e, 0x0001}, + {0xaa, 0x0f, 0x0000}, + {0xaa, 0x10, 0x0001}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ + {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ + {0xaa, 0x19, 0x0088}, /* 00,19,88,aa, */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */ + {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */ + {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */ + {0xaa, 0x1b, 0x0000}, + {} +}; +static const struct usb_action gc0303_50HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */ + {0xaa, 0x84, 0x0063}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */ + {0xa0, 0xa8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ + {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x7f, ZC3XX_R18D_YTARGET}, + {} +}; + +static const struct usb_action gc0303_50HZScale[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */ + {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x0d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */ + {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ + {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x7f, ZC3XX_R18D_YTARGET}, + {} +}; + +static const struct usb_action gc0303_60HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, + {0xaa, 0x84, 0x003b}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,05,cc, */ + {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,88,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x3b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3b,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, + {} +}; + +static const struct usb_action gc0303_60HZScale[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, + {0xaa, 0x84, 0x0076}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,1,0b,cc, */ + {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,2,10,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,5,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,6,00,cc, */ + {0xa0, 0x76, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,7,76,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,9,10,cc, */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,a,24,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,0,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, + {} +}; + +static const struct usb_action gc0303_NoFliker[] = { + {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,0,00,cc, */ + {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ + {} +}; + +static const struct usb_action gc0303_NoFlikerScale[] = { + {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ + {} +}; + +static u8 reg_r(struct gspca_dev *gspca_dev, + u16 index) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return 0; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0xa1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x01, /* value */ + index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + return 0; + } + return gspca_dev->usb_buf[0]; +} + +static void reg_w(struct gspca_dev *gspca_dev, + u8 value, + u16 index) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0xa0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); + if (ret < 0) { + pr_err("reg_w_i err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static u16 i2c_read(struct gspca_dev *gspca_dev, + u8 reg) +{ + u8 retbyte; + u16 retval; + + if (gspca_dev->usb_err < 0) + return 0; + reg_w(gspca_dev, reg, 0x0092); + reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */ + msleep(20); + retbyte = reg_r(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + pr_err("i2c_r status error %02x\n", retbyte); + retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */ + retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */ + return retval; +} + +static u8 i2c_write(struct gspca_dev *gspca_dev, + u8 reg, + u8 valL, + u8 valH) +{ + u8 retbyte; + + if (gspca_dev->usb_err < 0) + return 0; + reg_w(gspca_dev, reg, 0x92); + reg_w(gspca_dev, valL, 0x93); + reg_w(gspca_dev, valH, 0x94); + reg_w(gspca_dev, 0x01, 0x90); /* <- write command */ + msleep(1); + retbyte = reg_r(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + pr_err("i2c_w status error %02x\n", retbyte); + return retbyte; +} + +static void usb_exchange(struct gspca_dev *gspca_dev, + const struct usb_action *action) +{ + while (action->req) { + switch (action->req) { + case 0xa0: /* write register */ + reg_w(gspca_dev, action->val, action->idx); + break; + case 0xa1: /* read status */ + reg_r(gspca_dev, action->idx); + break; + case 0xaa: + i2c_write(gspca_dev, + action->val, /* reg */ + action->idx & 0xff, /* valL */ + action->idx >> 8); /* valH */ + break; + case 0xbb: + i2c_write(gspca_dev, + action->idx >> 8, /* reg */ + action->idx & 0xff, /* valL */ + action->val); /* valH */ + break; + default: +/* case 0xdd: * delay */ + msleep(action->idx); + break; + } + action++; + msleep(1); + } +} + +static void setmatrix(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + const u8 *matrix; + static const u8 adcm2700_matrix[9] = +/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */ +/*ms-win*/ + {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74}; + static const u8 gc0305_matrix[9] = + {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50}; + static const u8 ov7620_matrix[9] = + {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58}; + static const u8 pas202b_matrix[9] = + {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; + static const u8 po2030_matrix[9] = + {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; + static const u8 tas5130c_matrix[9] = + {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68}; + static const u8 gc0303_matrix[9] = + {0x6c, 0xea, 0xea, 0xea, 0x6c, 0xea, 0xea, 0xea, 0x6c}; + static const u8 *matrix_tb[SENSOR_MAX] = { + [SENSOR_ADCM2700] = adcm2700_matrix, + [SENSOR_CS2102] = ov7620_matrix, + [SENSOR_CS2102K] = NULL, + [SENSOR_GC0303] = gc0303_matrix, + [SENSOR_GC0305] = gc0305_matrix, + [SENSOR_HDCS2020] = NULL, + [SENSOR_HV7131B] = NULL, + [SENSOR_HV7131R] = po2030_matrix, + [SENSOR_ICM105A] = po2030_matrix, + [SENSOR_MC501CB] = NULL, + [SENSOR_MT9V111_1] = gc0305_matrix, + [SENSOR_MT9V111_3] = gc0305_matrix, + [SENSOR_OV7620] = ov7620_matrix, + [SENSOR_OV7630C] = NULL, + [SENSOR_PAS106] = NULL, + [SENSOR_PAS202B] = pas202b_matrix, + [SENSOR_PB0330] = gc0305_matrix, + [SENSOR_PO2030] = po2030_matrix, + [SENSOR_TAS5130C] = tas5130c_matrix, + }; + + matrix = matrix_tb[sd->sensor]; + if (matrix == NULL) + return; /* matrix already loaded */ + for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++) + reg_w(gspca_dev, matrix[i], 0x010a + i); +} + +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +{ + static const u8 sharpness_tb[][2] = { + {0x02, 0x03}, + {0x04, 0x07}, + {0x08, 0x0f}, + {0x10, 0x1e} + }; + + reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6); + reg_r(gspca_dev, 0x01c8); + reg_r(gspca_dev, 0x01c9); + reg_r(gspca_dev, 0x01ca); + reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb); +} + +static void setcontrast(struct gspca_dev *gspca_dev, + s32 gamma, s32 brightness, s32 contrast) +{ + const u8 *Tgamma; + int g, i, adj, gp1, gp2; + u8 gr[16]; + static const u8 delta_b[16] = /* delta for brightness */ + {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d, + 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18}; + static const u8 delta_c[16] = /* delta for contrast */ + {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06, + 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02}; + static const u8 gamma_tb[6][16] = { + {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f, + 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff}, + {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c, + 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff}, + {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac, + 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff}, + {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff}, + {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2, + 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff}, + {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3, + 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}, + }; + + Tgamma = gamma_tb[gamma - 1]; + + contrast -= 128; /* -128 / 127 */ + brightness -= 128; /* -128 / 92 */ + adj = 0; + gp1 = gp2 = 0; + for (i = 0; i < 16; i++) { + g = Tgamma[i] + delta_b[i] * brightness / 256 + - delta_c[i] * contrast / 256 - adj / 2; + if (g > 0xff) + g = 0xff; + else if (g < 0) + g = 0; + reg_w(gspca_dev, g, 0x0120 + i); /* gamma */ + if (contrast > 0) + adj--; + else if (contrast < 0) + adj++; + if (i > 1) + gr[i - 1] = (g - gp2) / 2; + else if (i != 0) + gr[0] = gp1 == 0 ? 0 : (g - gp1); + gp2 = gp1; + gp1 = g; + } + gr[15] = (0xff - gp2) / 2; + for (i = 0; i < 16; i++) + reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */ +} + +static s32 getexposure(struct gspca_dev *gspca_dev) +{ + return (i2c_read(gspca_dev, 0x25) << 9) + | (i2c_read(gspca_dev, 0x26) << 1) + | (i2c_read(gspca_dev, 0x27) >> 7); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + i2c_write(gspca_dev, 0x25, val >> 9, 0x00); + i2c_write(gspca_dev, 0x26, val >> 1, 0x00); + i2c_write(gspca_dev, 0x27, val << 7, 0x00); +} + +static void setquality(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]); + reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); +} + +/* Matches the sensor's internal frame rate to the lighting frequency. + * Valid frequencies are: + * 50Hz, for European and Asian lighting (default) + * 60Hz, for American lighting + * 0 = No Fliker (for outdoore usage) + */ +static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, mode; + const struct usb_action *zc3_freq; + static const struct usb_action *freq_tb[SENSOR_MAX][6] = { + [SENSOR_ADCM2700] = + {adcm2700_NoFliker, adcm2700_NoFliker, + adcm2700_50HZ, adcm2700_50HZ, + adcm2700_60HZ, adcm2700_60HZ}, + [SENSOR_CS2102] = + {cs2102_NoFliker, cs2102_NoFlikerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, + [SENSOR_CS2102K] = + {cs2102_NoFliker, cs2102_NoFlikerScale, + NULL, NULL, /* currently disabled */ + NULL, NULL}, + [SENSOR_GC0303] = + {gc0303_NoFliker, gc0303_NoFlikerScale, + gc0303_50HZ, gc0303_50HZScale, + gc0303_60HZ, gc0303_60HZScale}, + [SENSOR_GC0305] = + {gc0305_NoFliker, gc0305_NoFliker, + gc0305_50HZ, gc0305_50HZ, + gc0305_60HZ, gc0305_60HZ}, + [SENSOR_HDCS2020] = + {hdcs2020_NoFliker, hdcs2020_NoFliker, + hdcs2020_50HZ, hdcs2020_50HZ, + hdcs2020_60HZ, hdcs2020_60HZ}, + [SENSOR_HV7131B] = + {hv7131b_NoFliker, hv7131b_NoFlikerScale, + hv7131b_50HZ, hv7131b_50HZScale, + hv7131b_60HZ, hv7131b_60HZScale}, + [SENSOR_HV7131R] = + {hv7131r_NoFliker, hv7131r_NoFlikerScale, + hv7131r_50HZ, hv7131r_50HZScale, + hv7131r_60HZ, hv7131r_60HZScale}, + [SENSOR_ICM105A] = + {icm105a_NoFliker, icm105a_NoFlikerScale, + icm105a_50HZ, icm105a_50HZScale, + icm105a_60HZ, icm105a_60HZScale}, + [SENSOR_MC501CB] = + {mc501cb_NoFliker, mc501cb_NoFlikerScale, + mc501cb_50HZ, mc501cb_50HZScale, + mc501cb_60HZ, mc501cb_60HZScale}, + [SENSOR_MT9V111_1] = + {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale, + mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale, + mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale}, + [SENSOR_MT9V111_3] = + {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale, + mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale, + mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale}, + [SENSOR_OV7620] = + {ov7620_NoFliker, ov7620_NoFliker, + ov7620_50HZ, ov7620_50HZ, + ov7620_60HZ, ov7620_60HZ}, + [SENSOR_OV7630C] = + {NULL, NULL, + NULL, NULL, + NULL, NULL}, + [SENSOR_PAS106] = + {pas106b_NoFliker, pas106b_NoFliker, + pas106b_50HZ, pas106b_50HZ, + pas106b_60HZ, pas106b_60HZ}, + [SENSOR_PAS202B] = + {pas202b_NoFliker, pas202b_NoFlikerScale, + pas202b_50HZ, pas202b_50HZScale, + pas202b_60HZ, pas202b_60HZScale}, + [SENSOR_PB0330] = + {pb0330_NoFliker, pb0330_NoFlikerScale, + pb0330_50HZ, pb0330_50HZScale, + pb0330_60HZ, pb0330_60HZScale}, + [SENSOR_PO2030] = + {po2030_NoFliker, po2030_NoFliker, + po2030_50HZ, po2030_50HZ, + po2030_60HZ, po2030_60HZ}, + [SENSOR_TAS5130C] = + {tas5130c_NoFliker, tas5130c_NoFlikerScale, + tas5130c_50HZ, tas5130c_50HZScale, + tas5130c_60HZ, tas5130c_60HZScale}, + }; + + i = val * 2; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + if (mode) + i++; /* 320x240 */ + zc3_freq = freq_tb[sd->sensor][i]; + if (zc3_freq == NULL) + return; + usb_exchange(gspca_dev, zc3_freq); + switch (sd->sensor) { + case SENSOR_GC0305: + if (mode /* if 320x240 */ + && val == 1) /* and 50Hz */ + reg_w(gspca_dev, 0x85, 0x018d); + /* win: 0x80, 0x018d */ + break; + case SENSOR_OV7620: + if (!mode) { /* if 640x480 */ + if (val != 0) /* and filter */ + reg_w(gspca_dev, 0x40, 0x0002); + else + reg_w(gspca_dev, 0x44, 0x0002); + } + break; + case SENSOR_PAS202B: + reg_w(gspca_dev, 0x00, 0x01a7); + break; + } +} + +static void setautogain(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180); +} + +/* + * Update the transfer parameters. + * This function is executed from a work queue. + */ +static void transfer_update(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + int change, good; + u8 reg07, reg11; + + /* reg07 gets set to 0 by sd_start before starting us */ + reg07 = 0; + + good = 0; + for (;;) { + msleep(100); + + mutex_lock(&gspca_dev->usb_lock); +#ifdef CONFIG_PM + if (gspca_dev->frozen) + goto err; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) + goto err; + + /* Bit 0 of register 11 indicates FIFO overflow */ + gspca_dev->usb_err = 0; + reg11 = reg_r(gspca_dev, 0x0011); + if (gspca_dev->usb_err) + goto err; + + change = reg11 & 0x01; + if (change) { /* overflow */ + good = 0; + + if (reg07 == 0) /* Bit Rate Control not enabled? */ + reg07 = 0x32; /* Allow 98 bytes / unit */ + else if (reg07 > 2) + reg07 -= 2; /* Decrease allowed bytes / unit */ + else + change = 0; + } else { /* no overflow */ + good++; + if (good >= 10) { + good = 0; + if (reg07) { /* BRC enabled? */ + change = 1; + if (reg07 < 0x32) + reg07 += 2; + else + reg07 = 0; + } + } + } + if (change) { + gspca_dev->usb_err = 0; + reg_w(gspca_dev, reg07, 0x0007); + if (gspca_dev->usb_err) + goto err; + } + mutex_unlock(&gspca_dev->usb_lock); + } + return; +err: + mutex_unlock(&gspca_dev->usb_lock); +} + +static void send_unknown(struct gspca_dev *gspca_dev, int sensor) +{ + reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */ + switch (sensor) { + case SENSOR_PAS106: + reg_w(gspca_dev, 0x03, 0x003a); + reg_w(gspca_dev, 0x0c, 0x003b); + reg_w(gspca_dev, 0x08, 0x0038); + break; + case SENSOR_ADCM2700: + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_MT9V111_1: + case SENSOR_MT9V111_3: + case SENSOR_PB0330: + case SENSOR_PO2030: + reg_w(gspca_dev, 0x0d, 0x003a); + reg_w(gspca_dev, 0x02, 0x003b); + reg_w(gspca_dev, 0x00, 0x0038); + break; + case SENSOR_HV7131R: + case SENSOR_PAS202B: + reg_w(gspca_dev, 0x03, 0x003b); + reg_w(gspca_dev, 0x0c, 0x003a); + reg_w(gspca_dev, 0x0b, 0x0039); + if (sensor == SENSOR_PAS202B) + reg_w(gspca_dev, 0x0b, 0x0038); + break; + } +} + +/* start probe 2 wires */ +static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor) +{ + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, sensor, 0x0010); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); +/* msleep(2); */ +} + +static int sif_probe(struct gspca_dev *gspca_dev) +{ + u16 checkword; + + start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */ + reg_w(gspca_dev, 0x08, 0x008d); + msleep(150); + checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4) + | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4); + PDEBUG(D_PROBE, "probe sif 0x%04x", checkword); + if (checkword == 0x0007) { + send_unknown(gspca_dev, SENSOR_PAS106); + return 0x0f; /* PAS106 */ + } + return -1; +} + +static int vga_2wr_probe(struct gspca_dev *gspca_dev) +{ + u16 retword; + + start_2wr_probe(gspca_dev, 0x00); /* HV7131B */ + i2c_write(gspca_dev, 0x01, 0xaa, 0x00); + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) + return 0x00; /* HV7131B */ + + start_2wr_probe(gspca_dev, 0x04); /* CS2102 */ + i2c_write(gspca_dev, 0x01, 0xaa, 0x00); + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) + return 0x04; /* CS2102 */ + + start_2wr_probe(gspca_dev, 0x06); /* OmniVision */ + reg_w(gspca_dev, 0x08, 0x008d); + i2c_write(gspca_dev, 0x11, 0xaa, 0x00); + retword = i2c_read(gspca_dev, 0x11); + if (retword != 0) { + /* (should have returned 0xaa) --> Omnivision? */ + /* reg_r 0x10 -> 0x06 --> */ + goto ov_check; + } + + start_2wr_probe(gspca_dev, 0x08); /* HDCS2020 */ + i2c_write(gspca_dev, 0x1c, 0x00, 0x00); + i2c_write(gspca_dev, 0x15, 0xaa, 0x00); + retword = i2c_read(gspca_dev, 0x15); + if (retword != 0) + return 0x08; /* HDCS2020 */ + + start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */ + i2c_write(gspca_dev, 0x07, 0xaa, 0xaa); + retword = i2c_read(gspca_dev, 0x07); + if (retword != 0) + return 0x0a; /* PB0330 */ + retword = i2c_read(gspca_dev, 0x03); + if (retword != 0) + return 0x0a; /* PB0330 ?? */ + retword = i2c_read(gspca_dev, 0x04); + if (retword != 0) + return 0x0a; /* PB0330 ?? */ + + start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */ + i2c_write(gspca_dev, 0x01, 0x11, 0x00); + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) + return 0x0c; /* ICM105A */ + + start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */ + reg_w(gspca_dev, 0x08, 0x008d); + i2c_write(gspca_dev, 0x03, 0xaa, 0x00); + msleep(50); + retword = i2c_read(gspca_dev, 0x03); + if (retword != 0) { + send_unknown(gspca_dev, SENSOR_PAS202B); + return 0x0e; /* PAS202BCB */ + } + + start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */ + i2c_write(gspca_dev, 0x01, 0xaa, 0x00); + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) + return 0x02; /* TAS5130C */ +ov_check: + reg_r(gspca_dev, 0x0010); /* ?? */ + reg_r(gspca_dev, 0x0010); + + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */ + reg_w(gspca_dev, 0xa1, 0x008b); + reg_w(gspca_dev, 0x08, 0x008d); + msleep(500); + reg_w(gspca_dev, 0x01, 0x0012); + i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */ + retword = i2c_read(gspca_dev, 0x0a) << 8; + retword |= i2c_read(gspca_dev, 0x0b); + PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword); + switch (retword) { + case 0x7631: /* OV7630C */ + reg_w(gspca_dev, 0x06, 0x0010); + break; + case 0x7620: /* OV7620 */ + case 0x7648: /* OV7648 */ + break; + default: + return -1; /* not OmniVision */ + } + return retword; +} + +struct sensor_by_chipset_revision { + u16 revision; + u8 internal_sensor_id; +}; +static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { + {0xc000, 0x12}, /* TAS5130C */ + {0xc001, 0x13}, /* MT9V111 */ + {0xe001, 0x13}, + {0x8001, 0x13}, + {0x8000, 0x14}, /* CS2102K */ + {0x8400, 0x15}, /* MT9V111 */ + {0xe400, 0x15}, +}; + +static int vga_3wr_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u16 retword; + +/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ + reg_w(gspca_dev, 0x02, 0x0010); + reg_r(gspca_dev, 0x0010); + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x00, 0x0010); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x91, 0x008b); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); + retword = i2c_read(gspca_dev, 0x14); + if (retword != 0) + return 0x11; /* HV7131R */ + retword = i2c_read(gspca_dev, 0x15); + if (retword != 0) + return 0x11; /* HV7131R */ + retword = i2c_read(gspca_dev, 0x16); + if (retword != 0) + return 0x11; /* HV7131R */ + + reg_w(gspca_dev, 0x02, 0x0010); + retword = reg_r(gspca_dev, 0x000b) << 8; + retword |= reg_r(gspca_dev, 0x000a); + PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword); + reg_r(gspca_dev, 0x0010); + if ((retword & 0xff00) == 0x6400) + return 0x02; /* TAS5130C */ + for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { + if (chipset_revision_sensor[i].revision == retword) { + sd->chip_revision = retword; + send_unknown(gspca_dev, SENSOR_PB0330); + return chipset_revision_sensor[i].internal_sensor_id; + } + } + + reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */ + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0xdd, 0x008b); + reg_w(gspca_dev, 0x0a, 0x0010); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); + retword = i2c_read(gspca_dev, 0x00); + if (retword != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type 0a"); + return 0x0a; /* PB0330 */ + } + + /* probe gc0303 / gc0305 */ + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x98, 0x008b); + reg_w(gspca_dev, 0x01, 0x0010); + reg_w(gspca_dev, 0x03, 0x0012); + msleep(2); + reg_w(gspca_dev, 0x01, 0x0012); + retword = i2c_read(gspca_dev, 0x00); + if (retword != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword); + if (retword == 0x0011) /* gc0303 */ + return 0x0303; + if (retword == 0x0029) /* gc0305 */ + send_unknown(gspca_dev, SENSOR_GC0305); + return retword; + } + + reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */ + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0xa1, 0x008b); + reg_w(gspca_dev, 0x08, 0x008d); + reg_w(gspca_dev, 0x06, 0x0010); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); + if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */ + && i2c_read(gspca_dev, 0x1d) == 0x00a2) { + send_unknown(gspca_dev, SENSOR_OV7620); + return 0x06; /* OmniVision confirm ? */ + } + + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x00, 0x0002); + reg_w(gspca_dev, 0x01, 0x0010); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0xee, 0x008b); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); + retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */ + retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */ + PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword); + if (retword == 0x2030) { +#ifdef GSPCA_DEBUG + u8 retbyte; + + retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ + PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte); +#endif + send_unknown(gspca_dev, SENSOR_PO2030); + return retword; + } + + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x0a, 0x0010); + reg_w(gspca_dev, 0xd3, 0x008b); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); + reg_w(gspca_dev, 0xd3, 0x008b); + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword); + return 0x16; /* adcm2700 (6100/6200) */ + } + return -1; +} + +static int zcxx_probeSensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int sensor; + + switch (sd->sensor) { + case SENSOR_MC501CB: + return -1; /* don't probe */ + case SENSOR_GC0303: + /* may probe but with no write in reg 0x0010 */ + return -1; /* don't probe */ + case SENSOR_PAS106: + sensor = sif_probe(gspca_dev); + if (sensor >= 0) + return sensor; + break; + } + sensor = vga_2wr_probe(gspca_dev); + if (sensor >= 0) + return sensor; + return vga_3wr_probe(gspca_dev); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (id->idProduct == 0x301b) + sd->bridge = BRIDGE_ZC301; + else + sd->bridge = BRIDGE_ZC303; + + /* define some sensors from the vendor/product */ + sd->sensor = id->driver_info; + + sd->reg08 = REG08_DEF; + + INIT_WORK(&sd->work, transfer_update); + + return 0; +} + +static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + gspca_dev->usb_err = 0; + if (ctrl->val && sd->exposure && gspca_dev->streaming) + sd->exposure->val = getexposure(gspca_dev); + return gspca_dev->usb_err; + } + return -EINVAL; +} + +static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + int i, qual; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) { + qual = sd->reg08 >> 1; + + for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) { + if (ctrl->val <= jpeg_qual[i]) + break; + } + if (i > 0 && i == qual && ctrl->val < jpeg_qual[i]) + i--; + + /* With high quality settings we need max bandwidth */ + if (i >= 2 && gspca_dev->streaming && + !gspca_dev->cam.needs_full_bandwidth) + return -EBUSY; + + sd->reg08 = (i << 1) | 1; + ctrl->val = jpeg_qual[i]; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* gamma/brightness/contrast cluster */ + case V4L2_CID_GAMMA: + setcontrast(gspca_dev, sd->gamma->val, + sd->brightness->val, sd->contrast->val); + break; + /* autogain/exposure cluster */ + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev, ctrl->val); + if (!gspca_dev->usb_err && !ctrl->val && sd->exposure) + setexposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + setquality(gspca_dev); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops zcxx_ctrl_ops = { + .g_volatile_ctrl = zcxx_g_volatile_ctrl, + .s_ctrl = zcxx_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + static const u8 gamma[SENSOR_MAX] = { + [SENSOR_ADCM2700] = 4, + [SENSOR_CS2102] = 4, + [SENSOR_CS2102K] = 5, + [SENSOR_GC0303] = 3, + [SENSOR_GC0305] = 4, + [SENSOR_HDCS2020] = 4, + [SENSOR_HV7131B] = 4, + [SENSOR_HV7131R] = 4, + [SENSOR_ICM105A] = 4, + [SENSOR_MC501CB] = 4, + [SENSOR_MT9V111_1] = 4, + [SENSOR_MT9V111_3] = 4, + [SENSOR_OV7620] = 3, + [SENSOR_OV7630C] = 4, + [SENSOR_PAS106] = 4, + [SENSOR_PAS202B] = 4, + [SENSOR_PB0330] = 4, + [SENSOR_PO2030] = 4, + [SENSOR_TAS5130C] = 3, + }; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 8); + sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]); + if (sd->sensor == SENSOR_HV7131R) + sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927); + sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor != SENSOR_OV7630C) + sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 3, 1, + sd->sensor == SENSOR_PO2030 ? 0 : 2); + sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1, + jpeg_qual[REG08_DEF >> 1]); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(3, &sd->gamma); + if (sd->sensor == SENSOR_HV7131R) + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int sensor; + static const u8 mode_tb[SENSOR_MAX] = { + [SENSOR_ADCM2700] = 2, + [SENSOR_CS2102] = 1, + [SENSOR_CS2102K] = 1, + [SENSOR_GC0303] = 1, + [SENSOR_GC0305] = 1, + [SENSOR_HDCS2020] = 1, + [SENSOR_HV7131B] = 1, + [SENSOR_HV7131R] = 1, + [SENSOR_ICM105A] = 1, + [SENSOR_MC501CB] = 2, + [SENSOR_MT9V111_1] = 1, + [SENSOR_MT9V111_3] = 1, + [SENSOR_OV7620] = 2, + [SENSOR_OV7630C] = 1, + [SENSOR_PAS106] = 0, + [SENSOR_PAS202B] = 1, + [SENSOR_PB0330] = 1, + [SENSOR_PO2030] = 1, + [SENSOR_TAS5130C] = 1, + }; + + sensor = zcxx_probeSensor(gspca_dev); + if (sensor >= 0) + PDEBUG(D_PROBE, "probe sensor -> %04x", sensor); + if ((unsigned) force_sensor < SENSOR_MAX) { + sd->sensor = force_sensor; + PDEBUG(D_PROBE, "sensor forced to %d", force_sensor); + } else { + switch (sensor) { + case -1: + switch (sd->sensor) { + case SENSOR_MC501CB: + PDEBUG(D_PROBE, "Sensor MC501CB"); + break; + case SENSOR_GC0303: + PDEBUG(D_PROBE, "Sensor GC0303"); + break; + default: + pr_warn("Unknown sensor - set to TAS5130C\n"); + sd->sensor = SENSOR_TAS5130C; + } + break; + case 0: + /* check the sensor type */ + sensor = i2c_read(gspca_dev, 0x00); + PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor); + switch (sensor) { + case 0: /* hv7131b */ + case 1: /* hv7131e */ + PDEBUG(D_PROBE, "Find Sensor HV7131B"); + sd->sensor = SENSOR_HV7131B; + break; + default: +/* case 2: * hv7131r */ + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + sd->sensor = SENSOR_HV7131R; + break; + } + break; + case 0x02: + PDEBUG(D_PROBE, "Sensor TAS5130C"); + sd->sensor = SENSOR_TAS5130C; + break; + case 0x04: + PDEBUG(D_PROBE, "Find Sensor CS2102"); + sd->sensor = SENSOR_CS2102; + break; + case 0x08: + PDEBUG(D_PROBE, "Find Sensor HDCS2020"); + sd->sensor = SENSOR_HDCS2020; + break; + case 0x0a: + PDEBUG(D_PROBE, + "Find Sensor PB0330. Chip revision %x", + sd->chip_revision); + sd->sensor = SENSOR_PB0330; + break; + case 0x0c: + PDEBUG(D_PROBE, "Find Sensor ICM105A"); + sd->sensor = SENSOR_ICM105A; + break; + case 0x0e: + PDEBUG(D_PROBE, "Find Sensor PAS202B"); + sd->sensor = SENSOR_PAS202B; + break; + case 0x0f: + PDEBUG(D_PROBE, "Find Sensor PAS106"); + sd->sensor = SENSOR_PAS106; + break; + case 0x10: + case 0x12: + PDEBUG(D_PROBE, "Find Sensor TAS5130C"); + sd->sensor = SENSOR_TAS5130C; + break; + case 0x11: + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + sd->sensor = SENSOR_HV7131R; + break; + case 0x13: + case 0x15: + PDEBUG(D_PROBE, + "Sensor MT9V111. Chip revision %04x", + sd->chip_revision); + sd->sensor = sd->bridge == BRIDGE_ZC301 + ? SENSOR_MT9V111_1 + : SENSOR_MT9V111_3; + break; + case 0x14: + PDEBUG(D_PROBE, + "Find Sensor CS2102K?. Chip revision %x", + sd->chip_revision); + sd->sensor = SENSOR_CS2102K; + break; + case 0x16: + PDEBUG(D_PROBE, "Find Sensor ADCM2700"); + sd->sensor = SENSOR_ADCM2700; + break; + case 0x29: + PDEBUG(D_PROBE, "Find Sensor GC0305"); + sd->sensor = SENSOR_GC0305; + break; + case 0x0303: + PDEBUG(D_PROBE, "Sensor GC0303"); + sd->sensor = SENSOR_GC0303; + break; + case 0x2030: + PDEBUG(D_PROBE, "Find Sensor PO2030"); + sd->sensor = SENSOR_PO2030; + break; + case 0x7620: + PDEBUG(D_PROBE, "Find Sensor OV7620"); + sd->sensor = SENSOR_OV7620; + break; + case 0x7631: + PDEBUG(D_PROBE, "Find Sensor OV7630C"); + sd->sensor = SENSOR_OV7630C; + break; + case 0x7648: + PDEBUG(D_PROBE, "Find Sensor OV7648"); + sd->sensor = SENSOR_OV7620; /* same sensor (?) */ + break; + default: + pr_err("Unknown sensor %04x\n", sensor); + return -EINVAL; + } + } + if (sensor < 0x20) { + if (sensor == -1 || sensor == 0x10 || sensor == 0x12) + reg_w(gspca_dev, 0x02, 0x0010); + reg_r(gspca_dev, 0x0010); + } + + cam = &gspca_dev->cam; + switch (mode_tb[sd->sensor]) { + case 0: + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + break; + case 1: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + break; + default: +/* case 2: */ + cam->cam_mode = broken_vga_mode; + cam->nmodes = ARRAY_SIZE(broken_vga_mode); + break; + } + + /* switch off the led */ + reg_w(gspca_dev, 0x01, 0x0000); + return gspca_dev->usb_err; +} + +static int sd_pre_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0; + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode; + static const struct usb_action *init_tb[SENSOR_MAX][2] = { + [SENSOR_ADCM2700] = + {adcm2700_Initial, adcm2700_InitialScale}, + [SENSOR_CS2102] = + {cs2102_Initial, cs2102_InitialScale}, + [SENSOR_CS2102K] = + {cs2102K_Initial, cs2102K_InitialScale}, + [SENSOR_GC0303] = + {gc0303_Initial, gc0303_InitialScale}, + [SENSOR_GC0305] = + {gc0305_Initial, gc0305_InitialScale}, + [SENSOR_HDCS2020] = + {hdcs2020_Initial, hdcs2020_InitialScale}, + [SENSOR_HV7131B] = + {hv7131b_Initial, hv7131b_InitialScale}, + [SENSOR_HV7131R] = + {hv7131r_Initial, hv7131r_InitialScale}, + [SENSOR_ICM105A] = + {icm105a_Initial, icm105a_InitialScale}, + [SENSOR_MC501CB] = + {mc501cb_Initial, mc501cb_InitialScale}, + [SENSOR_MT9V111_1] = + {mt9v111_1_Initial, mt9v111_1_InitialScale}, + [SENSOR_MT9V111_3] = + {mt9v111_3_Initial, mt9v111_3_InitialScale}, + [SENSOR_OV7620] = + {ov7620_Initial, ov7620_InitialScale}, + [SENSOR_OV7630C] = + {ov7630c_Initial, ov7630c_InitialScale}, + [SENSOR_PAS106] = + {pas106b_Initial, pas106b_InitialScale}, + [SENSOR_PAS202B] = + {pas202b_Initial, pas202b_InitialScale}, + [SENSOR_PB0330] = + {pb0330_Initial, pb0330_InitialScale}, + [SENSOR_PO2030] = + {po2030_Initial, po2030_InitialScale}, + [SENSOR_TAS5130C] = + {tas5130c_Initial, tas5130c_InitialScale}, + }; + + /* create the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + switch (sd->sensor) { + case SENSOR_HV7131R: + zcxx_probeSensor(gspca_dev); + break; + case SENSOR_PAS106: + usb_exchange(gspca_dev, pas106b_Initial_com); + break; + } + usb_exchange(gspca_dev, init_tb[sd->sensor][mode]); + + switch (sd->sensor) { + case SENSOR_ADCM2700: + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_PO2030: + case SENSOR_TAS5130C: + case SENSOR_GC0303: +/* msleep(100); * ?? */ + reg_r(gspca_dev, 0x0002); /* --> 0x40 */ + reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(gspca_dev, 0x15, 0x01ae); + if (sd->sensor == SENSOR_TAS5130C) + break; + reg_w(gspca_dev, 0x0d, 0x003a); + reg_w(gspca_dev, 0x02, 0x003b); + reg_w(gspca_dev, 0x00, 0x0038); + break; + case SENSOR_HV7131R: + case SENSOR_PAS202B: + reg_w(gspca_dev, 0x03, 0x003b); + reg_w(gspca_dev, 0x0c, 0x003a); + reg_w(gspca_dev, 0x0b, 0x0039); + if (sd->sensor == SENSOR_HV7131R) + reg_w(gspca_dev, 0x50, ZC3XX_R11D_GLOBALGAIN); + break; + } + + setmatrix(gspca_dev); + switch (sd->sensor) { + case SENSOR_ADCM2700: + case SENSOR_OV7620: + reg_r(gspca_dev, 0x0008); + reg_w(gspca_dev, 0x00, 0x0008); + break; + case SENSOR_PAS202B: + case SENSOR_GC0305: + case SENSOR_HV7131R: + case SENSOR_TAS5130C: + reg_r(gspca_dev, 0x0008); + /* fall thru */ + case SENSOR_PO2030: + reg_w(gspca_dev, 0x03, 0x0008); + break; + } + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); + + /* set the gamma tables when not set */ + switch (sd->sensor) { + case SENSOR_CS2102K: /* gamma set in xxx_Initial */ + case SENSOR_HDCS2020: + case SENSOR_OV7630C: + break; + default: + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma), + v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast)); + break; + } + setmatrix(gspca_dev); /* one more time? */ + switch (sd->sensor) { + case SENSOR_OV7620: + case SENSOR_PAS202B: + reg_r(gspca_dev, 0x0180); /* from win */ + reg_w(gspca_dev, 0x00, 0x0180); + break; + } + setquality(gspca_dev); + /* Start with BRC disabled, transfer_update will enable it if needed */ + reg_w(gspca_dev, 0x00, 0x0007); + if (sd->plfreq) + setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq)); + + switch (sd->sensor) { + case SENSOR_ADCM2700: + reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(gspca_dev, 0x15, 0x01ae); + reg_w(gspca_dev, 0x02, 0x0180); + /* ms-win + */ + reg_w(gspca_dev, 0x40, 0x0117); + break; + case SENSOR_HV7131R: + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); + reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); + break; + case SENSOR_GC0305: + case SENSOR_TAS5130C: + reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(gspca_dev, 0x15, 0x01ae); + /* fall thru */ + case SENSOR_PAS202B: + case SENSOR_PO2030: +/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */ + reg_r(gspca_dev, 0x0180); + break; + case SENSOR_OV7620: + reg_w(gspca_dev, 0x09, 0x01ad); + reg_w(gspca_dev, 0x15, 0x01ae); + i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */ + i2c_write(gspca_dev, 0x13, 0xa3, 0x00); + /*fixme: returned value to send? */ + reg_w(gspca_dev, 0x40, 0x0117); + reg_r(gspca_dev, 0x0180); + break; + } + + setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); + + if (gspca_dev->usb_err < 0) + return gspca_dev->usb_err; + + /* Start the transfer parameters update thread */ + sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME); + queue_work(sd->work_thread, &sd->work); + + return 0; +} + +/* called on streamoff with alt 0 and on disconnect */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } + if (!gspca_dev->dev) + return; + send_unknown(gspca_dev, sd->sensor); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* check the JPEG end of frame */ + if (len >= 3 + && data[len - 3] == 0xff && data[len - 2] == 0xd9) { +/*fixme: what does the last byte mean?*/ + gspca_frame_add(gspca_dev, LAST_PACKET, + data, len - 1); + return; + } + + /* check the JPEG start of a frame */ + if (data[0] == 0xff && data[1] == 0xd8) { + /* put the JPEG header in the new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + + /* remove the webcam's header: + * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp + * - 'ss ss' is the frame sequence number (BE) + * - 'ww ww' and 'hh hh' are the window dimensions (BE) + * - 'pp pp' is the packet sequence number (BE) + */ + data += 18; + len -= 18; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + if (ret) + return ret; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrput packet length */ +{ + if (len == 8 && data[4] == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + } + + return 0; +} +#endif + +static const struct sd_desc sd_desc = { + .name = KBUILD_MODNAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_pre_start, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .int_pkt_scan = sd_int_pkt_scan, +#endif +}; + +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x03f0, 0x1b07)}, + {USB_DEVICE(0x041e, 0x041e)}, + {USB_DEVICE(0x041e, 0x4017)}, + {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x401e)}, + {USB_DEVICE(0x041e, 0x401f)}, + {USB_DEVICE(0x041e, 0x4022)}, + {USB_DEVICE(0x041e, 0x4029)}, + {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x4036)}, + {USB_DEVICE(0x041e, 0x403a)}, + {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_GC0303}, + {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_GC0303}, + {USB_DEVICE(0x0458, 0x7007)}, + {USB_DEVICE(0x0458, 0x700c)}, + {USB_DEVICE(0x0458, 0x700f)}, + {USB_DEVICE(0x0461, 0x0a00)}, + {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB}, + {USB_DEVICE(0x046d, 0x08a0)}, + {USB_DEVICE(0x046d, 0x08a1)}, + {USB_DEVICE(0x046d, 0x08a2)}, + {USB_DEVICE(0x046d, 0x08a3)}, + {USB_DEVICE(0x046d, 0x08a6)}, + {USB_DEVICE(0x046d, 0x08a7)}, + {USB_DEVICE(0x046d, 0x08a9)}, + {USB_DEVICE(0x046d, 0x08aa)}, + {USB_DEVICE(0x046d, 0x08ac)}, + {USB_DEVICE(0x046d, 0x08ad)}, + {USB_DEVICE(0x046d, 0x08ae)}, + {USB_DEVICE(0x046d, 0x08af)}, + {USB_DEVICE(0x046d, 0x08b9)}, + {USB_DEVICE(0x046d, 0x08d7)}, + {USB_DEVICE(0x046d, 0x08d8)}, + {USB_DEVICE(0x046d, 0x08d9)}, + {USB_DEVICE(0x046d, 0x08da)}, + {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB}, + {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x055f, 0xc005)}, + {USB_DEVICE(0x055f, 0xd003)}, + {USB_DEVICE(0x055f, 0xd004)}, + {USB_DEVICE(0x0698, 0x2003)}, + {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0ac8, 0x301b)}, + {USB_DEVICE(0x0ac8, 0x303b)}, + {USB_DEVICE(0x0ac8, 0x305b)}, + {USB_DEVICE(0x0ac8, 0x307b)}, + {USB_DEVICE(0x10fd, 0x0128)}, + {USB_DEVICE(0x10fd, 0x804d)}, + {USB_DEVICE(0x10fd, 0x8050)}, + {} /* end of entry */ +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +/* USB driver */ +static struct usb_driver sd_driver = { + .name = KBUILD_MODNAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); + +module_param(force_sensor, int, 0644); +MODULE_PARM_DESC(force_sensor, + "Force sensor. Only for experts!!!"); diff --git a/drivers/media/usb/hdpvr/Kconfig b/drivers/media/usb/hdpvr/Kconfig new file mode 100644 index 000000000000..de247f3c7d05 --- /dev/null +++ b/drivers/media/usb/hdpvr/Kconfig @@ -0,0 +1,10 @@ + +config VIDEO_HDPVR + tristate "Hauppauge HD PVR support" + depends on VIDEO_DEV + ---help--- + This is a video4linux driver for Hauppauge's HD PVR USB device. + + To compile this driver as a module, choose M here: the + module will be called hdpvr + diff --git a/drivers/media/usb/hdpvr/Makefile b/drivers/media/usb/hdpvr/Makefile new file mode 100644 index 000000000000..52f057f24e39 --- /dev/null +++ b/drivers/media/usb/hdpvr/Makefile @@ -0,0 +1,7 @@ +hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o + +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o + +ccflags-y += -Idrivers/media/video + +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/usb/hdpvr/hdpvr-control.c b/drivers/media/usb/hdpvr/hdpvr-control.c new file mode 100644 index 000000000000..ae8f229d1141 --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-control.c @@ -0,0 +1,200 @@ +/* + * Hauppauge HD PVR USB driver - video 4 linux 2 interface + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "hdpvr.h" + + +int hdpvr_config_call(struct hdpvr_device *dev, uint value, u8 valbuf) +{ + int ret; + char request_type = 0x38, snd_request = 0x01; + + mutex_lock(&dev->usbc_mutex); + dev->usbc_buf[0] = valbuf; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + snd_request, 0x00 | request_type, + value, CTRL_DEFAULT_INDEX, + dev->usbc_buf, 1, 10000); + + mutex_unlock(&dev->usbc_mutex); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "config call request for value 0x%x returned %d\n", value, + ret); + + return ret < 0 ? ret : 0; +} + +struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev) +{ + struct hdpvr_video_info *vidinf = NULL; +#ifdef HDPVR_DEBUG + char print_buf[15]; +#endif + int ret; + + vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL); + if (!vidinf) { + v4l2_err(&dev->v4l2_dev, "out of memory\n"); + goto err; + } + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + 0x81, 0x80 | 0x38, + 0x1400, 0x0003, + dev->usbc_buf, 5, + 1000); + if (ret == 5) { + vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; + vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2]; + vidinf->fps = dev->usbc_buf[4]; + } + +#ifdef HDPVR_DEBUG + if (hdpvr_debug & MSG_INFO) { + hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "get video info returned: %d, %s\n", ret, print_buf); + } +#endif + mutex_unlock(&dev->usbc_mutex); + + if (!vidinf->width || !vidinf->height || !vidinf->fps) { + kfree(vidinf); + vidinf = NULL; + } +err: + return vidinf; +} + +int get_input_lines_info(struct hdpvr_device *dev) +{ +#ifdef HDPVR_DEBUG + char print_buf[9]; +#endif + int ret, lines; + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + 0x81, 0x80 | 0x38, + 0x1800, 0x0003, + dev->usbc_buf, 3, + 1000); + +#ifdef HDPVR_DEBUG + if (hdpvr_debug & MSG_INFO) { + hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "get input lines info returned: %d, %s\n", ret, + print_buf); + } +#else + (void)ret; /* suppress compiler warning */ +#endif + lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; + mutex_unlock(&dev->usbc_mutex); + return lines; +} + + +int hdpvr_set_bitrate(struct hdpvr_device *dev) +{ + int ret; + + mutex_lock(&dev->usbc_mutex); + memset(dev->usbc_buf, 0, 4); + dev->usbc_buf[0] = dev->options.bitrate; + dev->usbc_buf[2] = dev->options.peak_bitrate; + + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, CTRL_BITRATE_VALUE, + CTRL_DEFAULT_INDEX, dev->usbc_buf, 4, 1000); + mutex_unlock(&dev->usbc_mutex); + + return ret; +} + +int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, + enum v4l2_mpeg_audio_encoding codec) +{ + int ret = 0; + + if (dev->flags & HDPVR_FLAG_AC3_CAP) { + mutex_lock(&dev->usbc_mutex); + memset(dev->usbc_buf, 0, 2); + dev->usbc_buf[0] = input; + if (codec == V4L2_MPEG_AUDIO_ENCODING_AAC) + dev->usbc_buf[1] = 0; + else if (codec == V4L2_MPEG_AUDIO_ENCODING_AC3) + dev->usbc_buf[1] = 1; + else { + mutex_unlock(&dev->usbc_mutex); + v4l2_err(&dev->v4l2_dev, "invalid audio codec %d\n", + codec); + ret = -EINVAL; + goto error; + } + + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, CTRL_AUDIO_INPUT_VALUE, + CTRL_DEFAULT_INDEX, dev->usbc_buf, 2, + 1000); + mutex_unlock(&dev->usbc_mutex); + if (ret == 2) + ret = 0; + } else + ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE, input); +error: + return ret; +} + +int hdpvr_set_options(struct hdpvr_device *dev) +{ + hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std); + + hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, + dev->options.video_input+1); + + hdpvr_set_audio(dev, dev->options.audio_input+1, + dev->options.audio_codec); + + hdpvr_set_bitrate(dev); + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + dev->options.bitrate_mode); + hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode); + + hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness); + hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast); + hdpvr_config_call(dev, CTRL_HUE, dev->options.hue); + hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation); + hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness); + + return 0; +} diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c new file mode 100644 index 000000000000..304f43ef59eb --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -0,0 +1,472 @@ +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * Copyright (C) 2008 John Poet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hdpvr.h" + +static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET}; +module_param_array(video_nr, int, NULL, 0); +MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)"); + +/* holds the number of currently registered devices */ +static atomic_t dev_nr = ATOMIC_INIT(-1); + +int hdpvr_debug; +module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(hdpvr_debug, "enable debugging output"); + +static uint default_video_input = HDPVR_VIDEO_INPUTS; +module_param(default_video_input, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / " + "1=S-Video / 2=Composite"); + +static uint default_audio_input = HDPVR_AUDIO_INPUTS; +module_param(default_audio_input, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / " + "1=RCA front / 2=S/PDIF"); + +static bool boost_audio; +module_param(boost_audio, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(boost_audio, "boost the audio signal"); + + +/* table of devices that work with this driver */ +static struct usb_device_id hdpvr_table[] = { + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID4) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, hdpvr_table); + + +void hdpvr_delete(struct hdpvr_device *dev) +{ + hdpvr_free_buffers(dev); + + if (dev->video_dev) + video_device_release(dev->video_dev); + + usb_put_dev(dev->udev); +} + +static void challenge(u8 *bytes) +{ + u64 *i64P, tmp64; + uint i, idx; + + for (idx = 0; idx < 32; ++idx) { + + if (idx & 0x3) + bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3]; + + switch (idx & 0x3) { + case 0x3: + bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5]; + bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9; + break; + case 0x1: + bytes[0] *= 8; + bytes[0] += 7*idx + 4; + bytes[6] += bytes[3] * 3; + break; + case 0x0: + bytes[3 - (idx >> 3)] = bytes[idx >> 2]; + bytes[5] += bytes[6] * 3; + for (i = 0; i < 3; i++) + bytes[3] *= bytes[3] + 1; + break; + case 0x2: + for (i = 0; i < 3; i++) + bytes[1] *= bytes[6] + 1; + for (i = 0; i < 3; i++) { + i64P = (u64 *)bytes; + tmp64 = le64_to_cpup(i64P); + tmp64 <<= bytes[7] & 0x0f; + *i64P += cpu_to_le64(tmp64); + } + break; + } + } +} + +/* try to init the device like the windows driver */ +static int device_authorization(struct hdpvr_device *dev) +{ + + int ret, retval = -ENOMEM; + char request_type = 0x38, rcv_request = 0x81; + char *response; +#ifdef HDPVR_DEBUG + size_t buf_size = 46; + char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); + if (!print_buf) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + return retval; + } +#endif + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + rcv_request, 0x80 | request_type, + 0x0400, 0x0003, + dev->usbc_buf, 46, + 10000); + if (ret != 46) { + v4l2_err(&dev->v4l2_dev, + "unexpected answer of status request, len %d\n", ret); + goto unlock; + } +#ifdef HDPVR_DEBUG + else { + hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf, + 5*buf_size+1, 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "Status request returned, len %d: %s\n", + ret, print_buf); + } +#endif + + dev->fw_ver = dev->usbc_buf[1]; + + v4l2_info(&dev->v4l2_dev, "firmware version 0x%x dated %s\n", + dev->fw_ver, &dev->usbc_buf[2]); + + if (dev->fw_ver > 0x15) { + dev->options.brightness = 0x80; + dev->options.contrast = 0x40; + dev->options.hue = 0xf; + dev->options.saturation = 0x40; + dev->options.sharpness = 0x80; + } + + switch (dev->fw_ver) { + case HDPVR_FIRMWARE_VERSION: + dev->flags &= ~HDPVR_FLAG_AC3_CAP; + break; + case HDPVR_FIRMWARE_VERSION_AC3: + case HDPVR_FIRMWARE_VERSION_0X12: + case HDPVR_FIRMWARE_VERSION_0X15: + dev->flags |= HDPVR_FLAG_AC3_CAP; + break; + default: + v4l2_info(&dev->v4l2_dev, "untested firmware, the driver might" + " not work.\n"); + if (dev->fw_ver >= HDPVR_FIRMWARE_VERSION_AC3) + dev->flags |= HDPVR_FLAG_AC3_CAP; + else + dev->flags &= ~HDPVR_FLAG_AC3_CAP; + } + + response = dev->usbc_buf+38; +#ifdef HDPVR_DEBUG + hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n", + print_buf); +#endif + challenge(response); +#ifdef HDPVR_DEBUG + hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n", + print_buf); +#endif + + msleep(100); + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd1, 0x00 | request_type, + 0x0000, 0x0000, + response, 8, + 10000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "magic request returned %d\n", ret); + + retval = ret != 8; +unlock: + mutex_unlock(&dev->usbc_mutex); + return retval; +} + +static int hdpvr_device_init(struct hdpvr_device *dev) +{ + int ret; + u8 *buf; + struct hdpvr_video_info *vidinf; + + if (device_authorization(dev)) + return -EACCES; + + /* default options for init */ + hdpvr_set_options(dev); + + /* set filter options */ + mutex_lock(&dev->usbc_mutex); + buf = dev->usbc_buf; + buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, + CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX, + buf, 4, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + mutex_unlock(&dev->usbc_mutex); + + vidinf = get_video_info(dev); + if (!vidinf) + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "no valid video signal or device init failed\n"); + else + kfree(vidinf); + + /* enable fan and bling leds */ + mutex_lock(&dev->usbc_mutex); + buf[0] = 0x1; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd4, 0x38, 0, 0, buf, 1, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + + /* boost analog audio */ + buf[0] = boost_audio; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd5, 0x38, 0, 0, buf, 1, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + mutex_unlock(&dev->usbc_mutex); + + dev->status = STATUS_IDLE; + return 0; +} + +static const struct hdpvr_options hdpvr_default_options = { + .video_std = HDPVR_60HZ, + .video_input = HDPVR_COMPONENT, + .audio_input = HDPVR_RCA_BACK, + .bitrate = 65, /* 6 mbps */ + .peak_bitrate = 90, /* 9 mbps */ + .bitrate_mode = HDPVR_CONSTANT, + .gop_mode = HDPVR_SIMPLE_IDR_GOP, + .audio_codec = V4L2_MPEG_AUDIO_ENCODING_AAC, + /* original picture controls for firmware version <= 0x15 */ + /* updated in device_authorization() for newer firmware */ + .brightness = 0x86, + .contrast = 0x80, + .hue = 0x80, + .saturation = 0x80, + .sharpness = 0x80, +}; + +static int hdpvr_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct hdpvr_device *dev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct i2c_client *client; + size_t buffer_size; + int i; + int retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_err(&interface->dev, "Out of memory\n"); + goto error; + } + + dev->workqueue = 0; + + /* register v4l2_device early so it can be used for printks */ + if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { + dev_err(&interface->dev, "v4l2_device_register failed\n"); + goto error; + } + + mutex_init(&dev->io_mutex); + mutex_init(&dev->i2c_mutex); + mutex_init(&dev->usbc_mutex); + dev->usbc_buf = kmalloc(64, GFP_KERNEL); + if (!dev->usbc_buf) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto error; + } + + init_waitqueue_head(&dev->wait_buffer); + init_waitqueue_head(&dev->wait_data); + + dev->workqueue = create_singlethread_workqueue("hdpvr_buffer"); + if (!dev->workqueue) + goto error; + + /* init video transfer queues */ + INIT_LIST_HEAD(&dev->free_buff_list); + INIT_LIST_HEAD(&dev->rec_buff_list); + + dev->options = hdpvr_default_options; + + if (default_video_input < HDPVR_VIDEO_INPUTS) + dev->options.video_input = default_video_input; + + if (default_audio_input < HDPVR_AUDIO_INPUTS) { + dev->options.audio_input = default_audio_input; + if (default_audio_input == HDPVR_SPDIF) + dev->options.audio_codec = + V4L2_MPEG_AUDIO_ENCODING_AC3; + } + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpointAddr && + usb_endpoint_is_bulk_in(endpoint)) { + /* USB interface description is buggy, reported max + * packet size is 512 bytes, windows driver uses 8192 */ + buffer_size = 8192; + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + } + + } + if (!dev->bulk_in_endpointAddr) { + v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n"); + goto error; + } + + /* init the device */ + if (hdpvr_device_init(dev)) { + v4l2_err(&dev->v4l2_dev, "device init failed\n"); + goto error; + } + + mutex_lock(&dev->io_mutex); + if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) { + mutex_unlock(&dev->io_mutex); + v4l2_err(&dev->v4l2_dev, + "allocating transfer buffers failed\n"); + goto error; + } + mutex_unlock(&dev->io_mutex); + + if (hdpvr_register_videodev(dev, &interface->dev, + video_nr[atomic_inc_return(&dev_nr)])) { + v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); + goto error; + } + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + retval = hdpvr_register_i2c_adapter(dev); + if (retval < 0) { + v4l2_err(&dev->v4l2_dev, "i2c adapter register failed\n"); + goto error; + } + + client = hdpvr_register_ir_rx_i2c(dev); + if (!client) { + v4l2_err(&dev->v4l2_dev, "i2c IR RX device register failed\n"); + goto reg_fail; + } + + client = hdpvr_register_ir_tx_i2c(dev); + if (!client) { + v4l2_err(&dev->v4l2_dev, "i2c IR TX device register failed\n"); + goto reg_fail; + } +#endif + + /* let the user know what node this device is now attached to */ + v4l2_info(&dev->v4l2_dev, "device now attached to %s\n", + video_device_node_name(dev->video_dev)); + return 0; + +reg_fail: +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_adapter(&dev->i2c_adapter); +#endif +error: + if (dev) { + /* Destroy single thread */ + if (dev->workqueue) + destroy_workqueue(dev->workqueue); + /* this frees allocated memory */ + hdpvr_delete(dev); + } + return retval; +} + +static void hdpvr_disconnect(struct usb_interface *interface) +{ + struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface)); + + v4l2_info(&dev->v4l2_dev, "device %s disconnected\n", + video_device_node_name(dev->video_dev)); + /* prevent more I/O from starting and stop any ongoing */ + mutex_lock(&dev->io_mutex); + dev->status = STATUS_DISCONNECTED; + wake_up_interruptible(&dev->wait_data); + wake_up_interruptible(&dev->wait_buffer); + mutex_unlock(&dev->io_mutex); + v4l2_device_disconnect(&dev->v4l2_dev); + msleep(100); + flush_workqueue(dev->workqueue); + mutex_lock(&dev->io_mutex); + hdpvr_cancel_queue(dev); + mutex_unlock(&dev->io_mutex); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_adapter(&dev->i2c_adapter); +#endif + video_unregister_device(dev->video_dev); + atomic_dec(&dev_nr); +} + + +static struct usb_driver hdpvr_usb_driver = { + .name = "hdpvr", + .probe = hdpvr_probe, + .disconnect = hdpvr_disconnect, + .id_table = hdpvr_table, +}; + +module_usb_driver(hdpvr_usb_driver); + +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2.1"); +MODULE_AUTHOR("Janne Grunau"); +MODULE_DESCRIPTION("Hauppauge HD PVR driver"); diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c new file mode 100644 index 000000000000..82e819fa91c0 --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -0,0 +1,231 @@ + +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * IR device registration code is + * Copyright (C) 2010 Andy Walls + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + +#include +#include +#include + +#include "hdpvr.h" + +#define CTRL_READ_REQUEST 0xb8 +#define CTRL_WRITE_REQUEST 0x38 + +#define REQTYPE_I2C_READ 0xb1 +#define REQTYPE_I2C_WRITE 0xb0 +#define REQTYPE_I2C_WRITE_STATT 0xd0 + +#define Z8F0811_IR_TX_I2C_ADDR 0x70 +#define Z8F0811_IR_RX_I2C_ADDR 0x71 + + +struct i2c_client *hdpvr_register_ir_tx_i2c(struct hdpvr_device *dev) +{ + struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data; + struct i2c_board_info hdpvr_ir_tx_i2c_board_info = { + I2C_BOARD_INFO("ir_tx_z8f0811_hdpvr", Z8F0811_IR_TX_I2C_ADDR), + }; + + init_data->name = "HD-PVR"; + hdpvr_ir_tx_i2c_board_info.platform_data = init_data; + + return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_tx_i2c_board_info); +} + +struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev) +{ + struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data; + struct i2c_board_info hdpvr_ir_rx_i2c_board_info = { + I2C_BOARD_INFO("ir_rx_z8f0811_hdpvr", Z8F0811_IR_RX_I2C_ADDR), + }; + + /* Our default information for ir-kbd-i2c.c to use */ + init_data->ir_codes = RC_MAP_HAUPPAUGE; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = RC_TYPE_RC5; + init_data->name = "HD-PVR"; + init_data->polling_interval = 405; /* ms, duplicated from Windows */ + hdpvr_ir_rx_i2c_board_info.platform_data = init_data; + + return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_rx_i2c_board_info); +} + +static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, + unsigned char addr, char *wdata, int wlen, + char *data, int len) +{ + int ret; + + if ((len > sizeof(dev->i2c_buf)) || (wlen > sizeof(dev->i2c_buf))) + return -EINVAL; + + if (wlen) { + memcpy(&dev->i2c_buf, wdata, wlen); + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, + (bus << 8) | addr, 0, &dev->i2c_buf, + wlen, 1000); + if (ret < 0) + return ret; + } + + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + REQTYPE_I2C_READ, CTRL_READ_REQUEST, + (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); + + if (ret == len) { + memcpy(data, &dev->i2c_buf, len); + ret = 0; + } else if (ret >= 0) + ret = -EIO; + + return ret; +} + +static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus, + unsigned char addr, char *data, int len) +{ + int ret; + + if (len > sizeof(dev->i2c_buf)) + return -EINVAL; + + memcpy(&dev->i2c_buf, data, len); + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, + (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); + + if (ret < 0) + return ret; + + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, + 0, 0, &dev->i2c_buf, 2, 1000); + + if ((ret == 2) && (dev->i2c_buf[1] == (len - 1))) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + return ret; +} + +static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, + int num) +{ + struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter); + int retval = 0, addr; + + if (num <= 0) + return 0; + + mutex_lock(&dev->i2c_mutex); + + addr = msgs[0].addr << 1; + + if (num == 1) { + if (msgs[0].flags & I2C_M_RD) + retval = hdpvr_i2c_read(dev, 1, addr, NULL, 0, + msgs[0].buf, msgs[0].len); + else + retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf, + msgs[0].len); + } else if (num == 2) { + if (msgs[0].addr != msgs[1].addr) { + v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer " + "with conflicting target addresses\n"); + retval = -EINVAL; + goto out; + } + + if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) { + v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with " + "r0=%d, r1=%d\n", msgs[0].flags & I2C_M_RD, + msgs[1].flags & I2C_M_RD); + retval = -EINVAL; + goto out; + } + + /* + * Write followed by atomic read is the only complex xfer that + * we actually support here. + */ + retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len, + msgs[1].buf, msgs[1].len); + } else { + v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num); + } + +out: + mutex_unlock(&dev->i2c_mutex); + + return retval ? retval : num; +} + +static u32 hdpvr_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm hdpvr_algo = { + .master_xfer = hdpvr_transfer, + .functionality = hdpvr_functionality, +}; + +static struct i2c_adapter hdpvr_i2c_adapter_template = { + .name = "Hauppage HD PVR I2C", + .owner = THIS_MODULE, + .algo = &hdpvr_algo, +}; + +static int hdpvr_activate_ir(struct hdpvr_device *dev) +{ + char buffer[2]; + + mutex_lock(&dev->i2c_mutex); + + hdpvr_i2c_read(dev, 0, 0x54, NULL, 0, buffer, 1); + + buffer[0] = 0; + buffer[1] = 0x8; + hdpvr_i2c_write(dev, 1, 0x54, buffer, 2); + + buffer[1] = 0x18; + hdpvr_i2c_write(dev, 1, 0x54, buffer, 2); + + mutex_unlock(&dev->i2c_mutex); + + return 0; +} + +int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) +{ + int retval = -ENOMEM; + + hdpvr_activate_ir(dev); + + memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template, + sizeof(struct i2c_adapter)); + dev->i2c_adapter.dev.parent = &dev->udev->dev; + + i2c_set_adapdata(&dev->i2c_adapter, dev); + + retval = i2c_add_adapter(&dev->i2c_adapter); + + return retval; +} + +#endif diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c new file mode 100644 index 000000000000..0e9e156bb2aa --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -0,0 +1,1289 @@ +/* + * Hauppauge HD PVR USB driver - video 4 linux 2 interface + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "hdpvr.h" + +#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */ + +#define print_buffer_status() { \ + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \ + "%s:%d buffer stat: %d free, %d proc\n", \ + __func__, __LINE__, \ + list_size(&dev->free_buff_list), \ + list_size(&dev->rec_buff_list)); } + +struct hdpvr_fh { + struct hdpvr_device *dev; +}; + +static uint list_size(struct list_head *list) +{ + struct list_head *tmp; + uint count = 0; + + list_for_each(tmp, list) { + count++; + } + + return count; +} + +/*=========================================================================*/ +/* urb callback */ +static void hdpvr_read_bulk_callback(struct urb *urb) +{ + struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context; + struct hdpvr_device *dev = buf->dev; + + /* marking buffer as received and wake waiting */ + buf->status = BUFSTAT_READY; + wake_up_interruptible(&dev->wait_data); +} + +/*=========================================================================*/ +/* bufffer bits */ + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_cancel_queue(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + + list_for_each_entry(buf, &dev->rec_buff_list, buff_list) { + usb_kill_urb(buf->urb); + buf->status = BUFSTAT_AVAILABLE; + } + + list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev); + + return 0; +} + +static int hdpvr_free_queue(struct list_head *q) +{ + struct list_head *tmp; + struct list_head *p; + struct hdpvr_buffer *buf; + struct urb *urb; + + for (p = q->next; p != q;) { + buf = list_entry(p, struct hdpvr_buffer, buff_list); + + urb = buf->urb; + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + tmp = p->next; + list_del(p); + kfree(buf); + p = tmp; + } + + return 0; +} + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_free_buffers(struct hdpvr_device *dev) +{ + hdpvr_cancel_queue(dev); + + hdpvr_free_queue(&dev->free_buff_list); + hdpvr_free_queue(&dev->rec_buff_list); + + return 0; +} + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) +{ + uint i; + int retval = -ENOMEM; + u8 *mem; + struct hdpvr_buffer *buf; + struct urb *urb; + + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "allocating %u buffers\n", count); + + for (i = 0; i < count; i++) { + + buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL); + if (!buf) { + v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n"); + goto exit; + } + buf->dev = dev; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); + goto exit_urb; + } + buf->urb = urb; + + mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL, + &urb->transfer_dma); + if (!mem) { + v4l2_err(&dev->v4l2_dev, + "cannot allocate usb transfer buffer\n"); + goto exit_urb_buffer; + } + + usb_fill_bulk_urb(buf->urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), + mem, dev->bulk_in_size, + hdpvr_read_bulk_callback, buf); + + buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + buf->status = BUFSTAT_AVAILABLE; + list_add_tail(&buf->buff_list, &dev->free_buff_list); + } + return 0; +exit_urb_buffer: + usb_free_urb(urb); +exit_urb: + kfree(buf); +exit: + hdpvr_free_buffers(dev); + return retval; +} + +static int hdpvr_submit_buffers(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + struct urb *urb; + int ret = 0, err_count = 0; + + mutex_lock(&dev->io_mutex); + + while (dev->status == STATUS_STREAMING && + !list_empty(&dev->free_buff_list)) { + + buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer, + buff_list); + if (buf->status != BUFSTAT_AVAILABLE) { + v4l2_err(&dev->v4l2_dev, + "buffer not marked as available\n"); + ret = -EFAULT; + goto err; + } + + urb = buf->urb; + urb->status = 0; + urb->actual_length = 0; + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "usb_submit_urb in %s returned %d\n", + __func__, ret); + if (++err_count > 2) + break; + continue; + } + buf->status = BUFSTAT_INPROGRESS; + list_move_tail(&buf->buff_list, &dev->rec_buff_list); + } +err: + print_buffer_status(); + mutex_unlock(&dev->io_mutex); + return ret; +} + +static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + + mutex_lock(&dev->io_mutex); + + if (list_empty(&dev->rec_buff_list)) { + mutex_unlock(&dev->io_mutex); + return NULL; + } + + buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer, + buff_list); + mutex_unlock(&dev->io_mutex); + + return buf; +} + +static void hdpvr_transmit_buffers(struct work_struct *work) +{ + struct hdpvr_device *dev = container_of(work, struct hdpvr_device, + worker); + + while (dev->status == STATUS_STREAMING) { + + if (hdpvr_submit_buffers(dev)) { + v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n"); + goto error; + } + if (wait_event_interruptible(dev->wait_buffer, + !list_empty(&dev->free_buff_list) || + dev->status != STATUS_STREAMING)) + goto error; + } + + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "transmit worker exited\n"); + return; +error: + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "transmit buffers errored\n"); + dev->status = STATUS_ERROR; +} + +/* function expects dev->io_mutex to be hold by caller */ +static int hdpvr_start_streaming(struct hdpvr_device *dev) +{ + int ret; + struct hdpvr_video_info *vidinf; + + if (dev->status == STATUS_STREAMING) + return 0; + else if (dev->status != STATUS_IDLE) + return -EAGAIN; + + vidinf = get_video_info(dev); + + if (vidinf) { + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "video signal: %dx%d@%dhz\n", vidinf->width, + vidinf->height, vidinf->fps); + kfree(vidinf); + + /* start streaming 2 request */ + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xb8, 0x38, 0x1, 0, NULL, 0, 8000); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "encoder start control request returned %d\n", ret); + + hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00); + + dev->status = STATUS_STREAMING; + + INIT_WORK(&dev->worker, hdpvr_transmit_buffers); + queue_work(dev->workqueue, &dev->worker); + + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "streaming started\n"); + + return 0; + } + msleep(250); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "no video signal at input %d\n", dev->options.video_input); + return -EAGAIN; +} + + +/* function expects dev->io_mutex to be hold by caller */ +static int hdpvr_stop_streaming(struct hdpvr_device *dev) +{ + int actual_length; + uint c = 0; + u8 *buf; + + if (dev->status == STATUS_IDLE) + return 0; + else if (dev->status != STATUS_STREAMING) + return -EAGAIN; + + buf = kmalloc(dev->bulk_in_size, GFP_KERNEL); + if (!buf) + v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer " + "for emptying the internal device buffer. " + "Next capture start will be slow\n"); + + dev->status = STATUS_SHUTTING_DOWN; + hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00); + mutex_unlock(&dev->io_mutex); + + wake_up_interruptible(&dev->wait_buffer); + msleep(50); + + flush_workqueue(dev->workqueue); + + mutex_lock(&dev->io_mutex); + /* kill the still outstanding urbs */ + hdpvr_cancel_queue(dev); + + /* emptying the device buffer beforeshutting it down */ + while (buf && ++c < 500 && + !usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), + buf, dev->bulk_in_size, &actual_length, + BULK_URB_TIMEOUT)) { + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "%2d: got %d bytes\n", c, actual_length); + } + kfree(buf); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "used %d urbs to empty device buffers\n", c-1); + msleep(10); + + dev->status = STATUS_IDLE; + + return 0; +} + + +/*=======================================================================*/ +/* + * video 4 linux 2 file operations + */ + +static int hdpvr_open(struct file *file) +{ + struct hdpvr_device *dev; + struct hdpvr_fh *fh; + int retval = -ENOMEM; + + dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file)); + if (!dev) { + pr_err("open failing with with ENODEV\n"); + retval = -ENODEV; + goto err; + } + + fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL); + if (!fh) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto err; + } + /* lock the device to allow correctly handling errors + * in resumption */ + mutex_lock(&dev->io_mutex); + dev->open_count++; + mutex_unlock(&dev->io_mutex); + + fh->dev = dev; + + /* save our object in the file's private structure */ + file->private_data = fh; + + retval = 0; +err: + return retval; +} + +static int hdpvr_release(struct file *file) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->io_mutex); + if (!(--dev->open_count) && dev->status == STATUS_STREAMING) + hdpvr_stop_streaming(dev); + + mutex_unlock(&dev->io_mutex); + + return 0; +} + +/* + * hdpvr_v4l2_read() + * will allocate buffers when called for the first time + */ +static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, + loff_t *pos) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + struct hdpvr_buffer *buf = NULL; + struct urb *urb; + unsigned int ret = 0; + int rem, cnt; + + if (*pos) + return -ESPIPE; + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->io_mutex); + if (dev->status == STATUS_IDLE) { + if (hdpvr_start_streaming(dev)) { + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "start_streaming failed\n"); + ret = -EIO; + msleep(200); + dev->status = STATUS_IDLE; + mutex_unlock(&dev->io_mutex); + goto err; + } + print_buffer_status(); + } + mutex_unlock(&dev->io_mutex); + + /* wait for the first buffer */ + if (!(file->f_flags & O_NONBLOCK)) { + if (wait_event_interruptible(dev->wait_data, + hdpvr_get_next_buffer(dev))) + return -ERESTARTSYS; + } + + buf = hdpvr_get_next_buffer(dev); + + while (count > 0 && buf) { + + if (buf->status != BUFSTAT_READY && + dev->status != STATUS_DISCONNECTED) { + /* return nonblocking */ + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto err; + } + + if (wait_event_interruptible(dev->wait_data, + buf->status == BUFSTAT_READY)) { + ret = -ERESTARTSYS; + goto err; + } + } + + if (buf->status != BUFSTAT_READY) + break; + + /* set remaining bytes to copy */ + urb = buf->urb; + rem = urb->actual_length - buf->pos; + cnt = rem > count ? count : rem; + + if (copy_to_user(buffer, urb->transfer_buffer + buf->pos, + cnt)) { + v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n"); + if (!ret) + ret = -EFAULT; + goto err; + } + + buf->pos += cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + + /* finished, take next buffer */ + if (buf->pos == urb->actual_length) { + mutex_lock(&dev->io_mutex); + buf->pos = 0; + buf->status = BUFSTAT_AVAILABLE; + + list_move_tail(&buf->buff_list, &dev->free_buff_list); + + print_buffer_status(); + + mutex_unlock(&dev->io_mutex); + + wake_up_interruptible(&dev->wait_buffer); + + buf = hdpvr_get_next_buffer(dev); + } + } +err: + if (!ret && !buf) + ret = -EAGAIN; + return ret; +} + +static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) +{ + struct hdpvr_buffer *buf = NULL; + struct hdpvr_fh *fh = filp->private_data; + struct hdpvr_device *dev = fh->dev; + unsigned int mask = 0; + + mutex_lock(&dev->io_mutex); + + if (!video_is_registered(dev->video_dev)) { + mutex_unlock(&dev->io_mutex); + return -EIO; + } + + if (dev->status == STATUS_IDLE) { + if (hdpvr_start_streaming(dev)) { + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "start_streaming failed\n"); + dev->status = STATUS_IDLE; + } + + print_buffer_status(); + } + mutex_unlock(&dev->io_mutex); + + buf = hdpvr_get_next_buffer(dev); + /* only wait if no data is available */ + if (!buf || buf->status != BUFSTAT_READY) { + poll_wait(filp, &dev->wait_data, wait); + buf = hdpvr_get_next_buffer(dev); + } + if (buf && buf->status == BUFSTAT_READY) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + + +static const struct v4l2_file_operations hdpvr_fops = { + .owner = THIS_MODULE, + .open = hdpvr_open, + .release = hdpvr_release, + .read = hdpvr_read, + .poll = hdpvr_poll, + .unlocked_ioctl = video_ioctl2, +}; + +/*=======================================================================*/ +/* + * V4L2 ioctl handling + */ + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct hdpvr_device *dev = video_drvdata(file); + + strcpy(cap->driver, "hdpvr"); + strcpy(cap->card, "Hauppauge HD PVR"); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_s_std(struct file *file, void *private_data, + v4l2_std_id *std) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + u8 std_type = 1; + + if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60)) + std_type = 0; + + return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type); +} + +static const char *iname[] = { + [HDPVR_COMPONENT] = "Component", + [HDPVR_SVIDEO] = "S-Video", + [HDPVR_COMPOSITE] = "Composite", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= HDPVR_VIDEO_INPUTS) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + + strncpy(i->name, iname[n], sizeof(i->name) - 1); + i->name[sizeof(i->name) - 1] = '\0'; + + i->audioset = 1<std = dev->video_dev->tvnorms; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *private_data, + unsigned int index) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + if (index >= HDPVR_VIDEO_INPUTS) + return -EINVAL; + + if (dev->status != STATUS_IDLE) + return -EAGAIN; + + retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1); + if (!retval) + dev->options.video_input = index; + + return retval; +} + +static int vidioc_g_input(struct file *file, void *private_data, + unsigned int *index) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + *index = dev->options.video_input; + return 0; +} + + +static const char *audio_iname[] = { + [HDPVR_RCA_FRONT] = "RCA front", + [HDPVR_RCA_BACK] = "RCA back", + [HDPVR_SPDIF] = "SPDIF", +}; + +static int vidioc_enumaudio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + unsigned int n; + + n = audio->index; + if (n >= HDPVR_AUDIO_INPUTS) + return -EINVAL; + + audio->capability = V4L2_AUDCAP_STEREO; + + strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1); + audio->name[sizeof(audio->name) - 1] = '\0'; + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *private_data, + struct v4l2_audio *audio) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + if (audio->index >= HDPVR_AUDIO_INPUTS) + return -EINVAL; + + if (dev->status != STATUS_IDLE) + return -EAGAIN; + + retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec); + if (!retval) + dev->options.audio_input = audio->index; + + return retval; +} + +static int vidioc_g_audio(struct file *file, void *private_data, + struct v4l2_audio *audio) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + audio->index = dev->options.audio_input; + audio->capability = V4L2_AUDCAP_STEREO; + strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name)); + audio->name[sizeof(audio->name) - 1] = '\0'; + return 0; +} + +static const s32 supported_v4l2_ctrls[] = { + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_SHARPNESS, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, +}; + +static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc, + int ac3, int fw_ver) +{ + int err; + + if (fw_ver > 0x15) { + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf); + case V4L2_CID_SHARPNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + } + } else { + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_SHARPNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + } + } + + switch (qc->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_AUDIO_ENCODING_AAC, + ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 + : V4L2_MPEG_AUDIO_ENCODING_AAC, + 1, V4L2_MPEG_AUDIO_ENCODING_AAC); + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); + +/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */ +/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000, + 6500000); + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000, + 9000000); + if (!err && opt->bitrate_mode == HDPVR_CONSTANT) + qc->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + default: + return -EINVAL; + } +} + +static int vidioc_queryctrl(struct file *file, void *private_data, + struct v4l2_queryctrl *qc) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, next; + u32 id = qc->id; + + memset(qc, 0, sizeof(*qc)); + + next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); + qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; + + for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) { + if (next) { + if (qc->id < supported_v4l2_ctrls[i]) + qc->id = supported_v4l2_ctrls[i]; + else + continue; + } + + if (qc->id == supported_v4l2_ctrls[i]) + return fill_queryctrl(&dev->options, qc, + dev->flags & HDPVR_FLAG_AC3_CAP, + dev->fw_ver); + + if (qc->id < supported_v4l2_ctrls[i]) + break; + } + + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *private_data, + struct v4l2_control *ctrl) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->options.brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dev->options.contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dev->options.saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dev->options.hue; + break; + case V4L2_CID_SHARPNESS: + ctrl->value = dev->options.sharpness; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *private_data, + struct v4l2_control *ctrl) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value); + if (!retval) + dev->options.brightness = ctrl->value; + break; + case V4L2_CID_CONTRAST: + retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value); + if (!retval) + dev->options.contrast = ctrl->value; + break; + case V4L2_CID_SATURATION: + retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value); + if (!retval) + dev->options.saturation = ctrl->value; + break; + case V4L2_CID_HUE: + retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value); + if (!retval) + dev->options.hue = ctrl->value; + break; + case V4L2_CID_SHARPNESS: + retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value); + if (!retval) + dev->options.sharpness = ctrl->value; + break; + default: + return -EINVAL; + } + + return retval; +} + + +static int hdpvr_get_ctrl(struct hdpvr_options *opt, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = opt->audio_codec; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT + ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR + : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = opt->bitrate * 100000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = opt->peak_bitrate * 100000; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_get_ctrl(&dev->options, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + + } + + return -EINVAL; +} + + +static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) +{ + int ret = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC || + (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3)) + ret = 0; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) + ret = 0; + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* if (ctrl->value == 0 || ctrl->value == 128) */ +/* ret = 0; */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR || + ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + ret = 0; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + { + uint bitrate = ctrl->value / 100000; + if (bitrate >= 10 && bitrate <= 135) + ret = 0; + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + { + uint peak_bitrate = ctrl->value / 100000; + if (peak_bitrate >= 10 && peak_bitrate <= 202) + ret = 0; + break; + } + case V4L2_CID_MPEG_STREAM_TYPE: + if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) + ret = 0; + break; + default: + return -EINVAL; + } + return ret; +} + +static int vidioc_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_try_ctrl(ctrl, + dev->flags & HDPVR_FLAG_AC3_CAP); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + } + + return -EINVAL; +} + + +static int hdpvr_set_ctrl(struct hdpvr_device *dev, + struct v4l2_ext_control *ctrl) +{ + struct hdpvr_options *opt = &dev->options; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (dev->flags & HDPVR_FLAG_AC3_CAP) { + opt->audio_codec = ctrl->value; + ret = hdpvr_set_audio(dev, opt->audio_input, + opt->audio_codec); + } + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */ +/* opt->gop_mode |= 0x2; */ +/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ +/* opt->gop_mode); */ +/* } */ +/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */ +/* opt->gop_mode &= ~0x2; */ +/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ +/* opt->gop_mode); */ +/* } */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR && + opt->bitrate_mode != HDPVR_CONSTANT) { + opt->bitrate_mode = HDPVR_CONSTANT; + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + opt->bitrate_mode); + } + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + opt->bitrate_mode == HDPVR_CONSTANT) { + opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE; + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + opt->bitrate_mode); + } + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: { + uint bitrate = ctrl->value / 100000; + + opt->bitrate = bitrate; + if (bitrate >= opt->peak_bitrate) + opt->peak_bitrate = bitrate+1; + + hdpvr_set_bitrate(dev); + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: { + uint peak_bitrate = ctrl->value / 100000; + + if (opt->bitrate_mode == HDPVR_CONSTANT) + break; + + if (opt->bitrate < peak_bitrate) { + opt->peak_bitrate = peak_bitrate; + hdpvr_set_bitrate(dev); + } else + ret = -EINVAL; + break; + } + case V4L2_CID_MPEG_STREAM_TYPE: + break; + default: + return -EINVAL; + } + return ret; +} + +static int vidioc_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_try_ctrl(ctrl, + dev->flags & HDPVR_FLAG_AC3_CAP); + if (err) { + ctrls->error_idx = i; + break; + } + err = hdpvr_set_ctrl(dev, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + + } + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data, + struct v4l2_fmtdesc *f) +{ + + if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32); + f->pixelformat = V4L2_PIX_FMT_MPEG; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data, + struct v4l2_format *f) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + struct hdpvr_video_info *vid_info; + + if (!dev) + return -ENODEV; + + vid_info = get_video_info(dev); + if (!vid_info) + return -EFAULT; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.width = vid_info->width; + f->fmt.pix.height = vid_info->height; + f->fmt.pix.sizeimage = dev->bulk_in_size; + f->fmt.pix.colorspace = 0; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.field = V4L2_FIELD_ANY; + + kfree(vid_info); + return 0; +} + +static int vidioc_encoder_cmd(struct file *filp, void *priv, + struct v4l2_encoder_cmd *a) +{ + struct hdpvr_fh *fh = filp->private_data; + struct hdpvr_device *dev = fh->dev; + int res; + + mutex_lock(&dev->io_mutex); + + memset(&a->raw, 0, sizeof(a->raw)); + switch (a->cmd) { + case V4L2_ENC_CMD_START: + a->flags = 0; + res = hdpvr_start_streaming(dev); + break; + case V4L2_ENC_CMD_STOP: + res = hdpvr_stop_streaming(dev); + break; + default: + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "Unsupported encoder cmd %d\n", a->cmd); + res = -EINVAL; + } + mutex_unlock(&dev->io_mutex); + return res; +} + +static int vidioc_try_encoder_cmd(struct file *filp, void *priv, + struct v4l2_encoder_cmd *a) +{ + switch (a->cmd) { + case V4L2_ENC_CMD_START: + case V4L2_ENC_CMD_STOP: + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, + .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_encoder_cmd = vidioc_encoder_cmd, + .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd, +}; + +static void hdpvr_device_release(struct video_device *vdev) +{ + struct hdpvr_device *dev = video_get_drvdata(vdev); + + hdpvr_delete(dev); + mutex_lock(&dev->io_mutex); + destroy_workqueue(dev->workqueue); + mutex_unlock(&dev->io_mutex); + + v4l2_device_unregister(&dev->v4l2_dev); + + /* deregister I2C adapter */ +#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE) + mutex_lock(&dev->i2c_mutex); + i2c_del_adapter(&dev->i2c_adapter); + mutex_unlock(&dev->i2c_mutex); +#endif /* CONFIG_I2C */ + + kfree(dev->usbc_buf); + kfree(dev); +} + +static const struct video_device hdpvr_video_template = { +/* .type = VFL_TYPE_GRABBER, */ +/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */ + .fops = &hdpvr_fops, + .release = hdpvr_device_release, + .ioctl_ops = &hdpvr_ioctl_ops, + .tvnorms = + V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B | + V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | + V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | + V4L2_STD_PAL_60, + .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M | + V4L2_STD_PAL_60, +}; + +int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, + int devnum) +{ + /* setup and register video device */ + dev->video_dev = video_device_alloc(); + if (!dev->video_dev) { + v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n"); + goto error; + } + + *(dev->video_dev) = hdpvr_video_template; + strcpy(dev->video_dev->name, "Hauppauge HD PVR"); + dev->video_dev->parent = parent; + video_set_drvdata(dev->video_dev, dev); + + if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) { + v4l2_err(&dev->v4l2_dev, "video_device registration failed\n"); + goto error; + } + + return 0; +error: + return -ENOMEM; +} diff --git a/drivers/media/usb/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h new file mode 100644 index 000000000000..fea3c6926997 --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr.h @@ -0,0 +1,317 @@ +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define HDPVR_MAX 8 +#define HDPVR_I2C_MAX_SIZE 128 + +/* Define these values to match your devices */ +#define HD_PVR_VENDOR_ID 0x2040 +#define HD_PVR_PRODUCT_ID 0x4900 +#define HD_PVR_PRODUCT_ID1 0x4901 +#define HD_PVR_PRODUCT_ID2 0x4902 +#define HD_PVR_PRODUCT_ID4 0x4903 +#define HD_PVR_PRODUCT_ID3 0x4982 + +#define UNSET (-1U) + +#define NUM_BUFFERS 64 + +#define HDPVR_FIRMWARE_VERSION 0x08 +#define HDPVR_FIRMWARE_VERSION_AC3 0x0d +#define HDPVR_FIRMWARE_VERSION_0X12 0x12 +#define HDPVR_FIRMWARE_VERSION_0X15 0x15 + +/* #define HDPVR_DEBUG */ + +extern int hdpvr_debug; + +#define MSG_INFO 1 +#define MSG_BUFFER 2 + +struct hdpvr_options { + u8 video_std; + u8 video_input; + u8 audio_input; + u8 bitrate; /* in 100kbps */ + u8 peak_bitrate; /* in 100kbps */ + u8 bitrate_mode; + u8 gop_mode; + enum v4l2_mpeg_audio_encoding audio_codec; + u8 brightness; + u8 contrast; + u8 hue; + u8 saturation; + u8 sharpness; +}; + +/* Structure to hold all of our device specific stuff */ +struct hdpvr_device { + /* the v4l device for this device */ + struct video_device *video_dev; + /* the usb device for this device */ + struct usb_device *udev; + /* v4l2-device unused */ + struct v4l2_device v4l2_dev; + + /* the max packet size of the bulk endpoint */ + size_t bulk_in_size; + /* the address of the bulk in endpoint */ + __u8 bulk_in_endpointAddr; + + /* holds the current device status */ + __u8 status; + /* count the number of openers */ + uint open_count; + + /* holds the cureent set options */ + struct hdpvr_options options; + + uint flags; + + /* synchronize I/O */ + struct mutex io_mutex; + /* available buffers */ + struct list_head free_buff_list; + /* in progress buffers */ + struct list_head rec_buff_list; + /* waitqueue for buffers */ + wait_queue_head_t wait_buffer; + /* waitqueue for data */ + wait_queue_head_t wait_data; + /**/ + struct workqueue_struct *workqueue; + /**/ + struct work_struct worker; + + /* I2C adapter */ + struct i2c_adapter i2c_adapter; + /* I2C lock */ + struct mutex i2c_mutex; + /* I2C message buffer space */ + char i2c_buf[HDPVR_I2C_MAX_SIZE]; + + /* For passing data to ir-kbd-i2c */ + struct IR_i2c_init_data ir_i2c_init_data; + + /* usb control transfer buffer and lock */ + struct mutex usbc_mutex; + u8 *usbc_buf; + u8 fw_ver; +}; + +static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct hdpvr_device, v4l2_dev); +} + + +/* buffer one bulk urb of data */ +struct hdpvr_buffer { + struct list_head buff_list; + + struct urb *urb; + + struct hdpvr_device *dev; + + uint pos; + + __u8 status; +}; + +/* */ + +struct hdpvr_video_info { + u16 width; + u16 height; + u8 fps; +}; + +enum { + STATUS_UNINITIALIZED = 0, + STATUS_IDLE, + STATUS_STARTING, + STATUS_SHUTTING_DOWN, + STATUS_STREAMING, + STATUS_ERROR, + STATUS_DISCONNECTED, +}; + +enum { + HDPVR_FLAG_AC3_CAP = 1, +}; + +enum { + BUFSTAT_UNINITIALIZED = 0, + BUFSTAT_AVAILABLE, + BUFSTAT_INPROGRESS, + BUFSTAT_READY, +}; + +#define CTRL_START_STREAMING_VALUE 0x0700 +#define CTRL_STOP_STREAMING_VALUE 0x0800 +#define CTRL_BITRATE_VALUE 0x1000 +#define CTRL_BITRATE_MODE_VALUE 0x1200 +#define CTRL_GOP_MODE_VALUE 0x1300 +#define CTRL_VIDEO_INPUT_VALUE 0x1500 +#define CTRL_VIDEO_STD_TYPE 0x1700 +#define CTRL_AUDIO_INPUT_VALUE 0x2500 +#define CTRL_BRIGHTNESS 0x2900 +#define CTRL_CONTRAST 0x2a00 +#define CTRL_HUE 0x2b00 +#define CTRL_SATURATION 0x2c00 +#define CTRL_SHARPNESS 0x2d00 +#define CTRL_LOW_PASS_FILTER_VALUE 0x3100 + +#define CTRL_DEFAULT_INDEX 0x0003 + + + /* :0 s 38 01 1000 0003 0004 4 = 0a00ca00 + * BITRATE SETTING + * 1st and 2nd byte (little endian): average bitrate in 100 000 bit/s + * min: 1 mbit/s, max: 13.5 mbit/s + * 3rd and 4th byte (little endian): peak bitrate in 100 000 bit/s + * min: average + 100kbit/s, + * max: 20.2 mbit/s + */ + + /* :0 s 38 01 1200 0003 0001 1 = 02 + * BIT RATE MODE + * constant = 1, variable (peak) = 2, variable (average) = 3 + */ + + /* :0 s 38 01 1300 0003 0001 1 = 03 + * GOP MODE (2 bit) + * low bit 0/1: advanced/simple GOP + * high bit 0/1: IDR(4/32/128) / no IDR (4/32/0) + */ + + /* :0 s 38 01 1700 0003 0001 1 = 00 + * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz + */ + + /* :0 s 38 01 3100 0003 0004 4 = 03030000 + * FILTER CONTROL + * 1st byte luma low pass filter strength, + * 2nd byte chroma low pass filter strength, + * 3rd byte MF enable chroma, min=0, max=1 + * 4th byte n + */ + + + /* :0 s 38 b9 0001 0000 0000 0 */ + + + +/* :0 s 38 d3 0000 0000 0001 1 = 00 */ +/* ret = usb_control_msg(dev->udev, */ +/* usb_sndctrlpipe(dev->udev, 0), */ +/* 0xd3, 0x38, */ +/* 0, 0, */ +/* "\0", 1, */ +/* 1000); */ + +/* info("control request returned %d", ret); */ +/* msleep(5000); */ + + + /* :0 s b8 81 1400 0003 0005 5 < + * :0 0 5 = d0024002 19 + * QUERY FRAME SIZE AND RATE + * 1st and 2nd byte (little endian): horizontal resolution + * 3rd and 4th byte (little endian): vertical resolution + * 5th byte: frame rate + */ + + /* :0 s b8 81 1800 0003 0003 3 < + * :0 0 3 = 030104 + * QUERY SIGNAL AND DETECTED LINES, maybe INPUT + */ + +enum hdpvr_video_std { + HDPVR_60HZ = 0, + HDPVR_50HZ, +}; + +enum hdpvr_video_input { + HDPVR_COMPONENT = 0, + HDPVR_SVIDEO, + HDPVR_COMPOSITE, + HDPVR_VIDEO_INPUTS +}; + +enum hdpvr_audio_inputs { + HDPVR_RCA_BACK = 0, + HDPVR_RCA_FRONT, + HDPVR_SPDIF, + HDPVR_AUDIO_INPUTS +}; + +enum hdpvr_bitrate_mode { + HDPVR_CONSTANT = 1, + HDPVR_VARIABLE_PEAK, + HDPVR_VARIABLE_AVERAGE, +}; + +enum hdpvr_gop_mode { + HDPVR_ADVANCED_IDR_GOP = 0, + HDPVR_SIMPLE_IDR_GOP, + HDPVR_ADVANCED_NOIDR_GOP, + HDPVR_SIMPLE_NOIDR_GOP, +}; + +void hdpvr_delete(struct hdpvr_device *dev); + +/*========================================================================*/ +/* hardware control functions */ +int hdpvr_set_options(struct hdpvr_device *dev); + +int hdpvr_set_bitrate(struct hdpvr_device *dev); + +int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, + enum v4l2_mpeg_audio_encoding codec); + +int hdpvr_config_call(struct hdpvr_device *dev, uint value, + unsigned char valbuf); + +struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev); + +/* :0 s b8 81 1800 0003 0003 3 < */ +/* :0 0 3 = 0301ff */ +int get_input_lines_info(struct hdpvr_device *dev); + + +/*========================================================================*/ +/* v4l2 registration */ +int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, + int devnumber); + +int hdpvr_cancel_queue(struct hdpvr_device *dev); + +/*========================================================================*/ +/* i2c adapter registration */ +int hdpvr_register_i2c_adapter(struct hdpvr_device *dev); + +struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev); +struct i2c_client *hdpvr_register_ir_tx_i2c(struct hdpvr_device *dev); + +/*========================================================================*/ +/* buffer management */ +int hdpvr_free_buffers(struct hdpvr_device *dev); +int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count); diff --git a/drivers/media/usb/pvrusb2/Kconfig b/drivers/media/usb/pvrusb2/Kconfig new file mode 100644 index 000000000000..25e412ecad2c --- /dev/null +++ b/drivers/media/usb/pvrusb2/Kconfig @@ -0,0 +1,65 @@ +config VIDEO_PVRUSB2 + tristate "Hauppauge WinTV-PVR USB2 support" + depends on VIDEO_V4L2 && I2C + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_CX2341X + select VIDEO_SAA711X + select VIDEO_CX25840 + select VIDEO_MSP3400 + select VIDEO_WM8775 + select VIDEO_CS53L32A + ---help--- + This is a video4linux driver for Conexant 23416 based + usb2 personal video recorder devices. + + To compile this driver as a module, choose M here: the + module will be called pvrusb2 + +config VIDEO_PVRUSB2_SYSFS + bool "pvrusb2 sysfs support (EXPERIMENTAL)" + default y + depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL + ---help--- + This option enables the operation of a sysfs based + interface for query and control of the pvrusb2 driver. + + This is not generally needed for v4l applications, + although certain applications are optimized to take + advantage of this feature. + + If you are in doubt, say Y. + + Note: This feature is experimental and subject to change. + +config VIDEO_PVRUSB2_DVB + bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)" + default y + depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select DVB_S5H1411 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + ---help--- + + This option enables a DVB interface for the pvrusb2 driver. + If your device does not support digital television, this + feature will have no affect on the driver's operation. + + If you are in doubt, say Y. + +config VIDEO_PVRUSB2_DEBUGIFC + bool "pvrusb2 debug interface" + depends on VIDEO_PVRUSB2_SYSFS + ---help--- + This option enables the inclusion of a debug interface + in the pvrusb2 driver, hosted through sysfs. + + You do not need to select this option unless you plan + on debugging the driver or performing a manual firmware + extraction. + + If you are in doubt, say N. diff --git a/drivers/media/usb/pvrusb2/Makefile b/drivers/media/usb/pvrusb2/Makefile new file mode 100644 index 000000000000..bc716db797e3 --- /dev/null +++ b/drivers/media/usb/pvrusb2/Makefile @@ -0,0 +1,22 @@ +obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o +obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o +obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o + +pvrusb2-objs := pvrusb2-i2c-core.o \ + pvrusb2-audio.o \ + pvrusb2-encoder.o pvrusb2-video-v4l.o \ + pvrusb2-eeprom.o \ + pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ + pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ + pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ + pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ + pvrusb2-cs53l32a.o \ + $(obj-pvrusb2-dvb-y) \ + $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) + +obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o + +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/tuners +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.c b/drivers/media/usb/pvrusb2/pvrusb2-audio.c new file mode 100644 index 000000000000..cc06d5e4adcc --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.c @@ -0,0 +1,96 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pvrusb2-audio.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include +#include +#include + + +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + +static const int routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT, + [PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), + [PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), + [PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), +}; + +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, +}; + +void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) +{ + if (hdw->input_dirty || hdw->force_dirty) { + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + u32 input; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + + if ((sp != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev msp3400 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; + } + sd->ops->audio->s_routing(sd, input, + MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); + } +} + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.h b/drivers/media/usb/pvrusb2/pvrusb2-audio.h new file mode 100644 index 000000000000..e3e63d750891 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.h @@ -0,0 +1,37 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_AUDIO_H +#define __PVRUSB2_AUDIO_H + +#include "pvrusb2-hdw-internal.h" +void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); +#endif /* __PVRUSB2_AUDIO_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.c b/drivers/media/usb/pvrusb2/pvrusb2-context.c new file mode 100644 index 000000000000..7c19ff72e6b3 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-context.c @@ -0,0 +1,431 @@ +/* + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pvrusb2-context.h" +#include "pvrusb2-io.h" +#include "pvrusb2-ioread.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-debug.h" +#include +#include +#include +#include +#include + +static struct pvr2_context *pvr2_context_exist_first; +static struct pvr2_context *pvr2_context_exist_last; +static struct pvr2_context *pvr2_context_notify_first; +static struct pvr2_context *pvr2_context_notify_last; +static DEFINE_MUTEX(pvr2_context_mutex); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data); +static int pvr2_context_cleanup_flag; +static int pvr2_context_cleaned_flag; +static struct task_struct *pvr2_context_thread_ptr; + + +static void pvr2_context_set_notify(struct pvr2_context *mp, int fl) +{ + int signal_flag = 0; + mutex_lock(&pvr2_context_mutex); + if (fl) { + if (!mp->notify_flag) { + signal_flag = (pvr2_context_notify_first == NULL); + mp->notify_prev = pvr2_context_notify_last; + mp->notify_next = NULL; + pvr2_context_notify_last = mp; + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp; + } else { + pvr2_context_notify_first = mp; + } + mp->notify_flag = !0; + } + } else { + if (mp->notify_flag) { + mp->notify_flag = 0; + if (mp->notify_next) { + mp->notify_next->notify_prev = mp->notify_prev; + } else { + pvr2_context_notify_last = mp->notify_prev; + } + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp->notify_next; + } else { + pvr2_context_notify_first = mp->notify_next; + } + } + } + mutex_unlock(&pvr2_context_mutex); + if (signal_flag) wake_up(&pvr2_context_sync_data); +} + + +static void pvr2_context_destroy(struct pvr2_context *mp) +{ + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); + if (mp->hdw) pvr2_hdw_destroy(mp->hdw); + pvr2_context_set_notify(mp, 0); + mutex_lock(&pvr2_context_mutex); + if (mp->exist_next) { + mp->exist_next->exist_prev = mp->exist_prev; + } else { + pvr2_context_exist_last = mp->exist_prev; + } + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp->exist_next; + } else { + pvr2_context_exist_first = mp->exist_next; + } + if (!pvr2_context_exist_first) { + /* Trigger wakeup on control thread in case it is waiting + for an exit condition. */ + wake_up(&pvr2_context_sync_data); + } + mutex_unlock(&pvr2_context_mutex); + kfree(mp); +} + + +static void pvr2_context_notify(struct pvr2_context *mp) +{ + pvr2_context_set_notify(mp,!0); +} + + +static void pvr2_context_check(struct pvr2_context *mp) +{ + struct pvr2_channel *ch1, *ch2; + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (notify)", mp); + if (!mp->initialized_flag && !mp->disconnect_flag) { + mp->initialized_flag = !0; + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (initialize)", mp); + /* Finish hardware initialization */ + if (pvr2_hdw_initialize(mp->hdw, + (void (*)(void *))pvr2_context_notify, + mp)) { + mp->video_stream.stream = + pvr2_hdw_get_video_stream(mp->hdw); + /* Trigger interface initialization. By doing this + here initialization runs in our own safe and + cozy thread context. */ + if (mp->setup_func) mp->setup_func(mp); + } else { + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (thread skipping setup)", + mp); + /* Even though initialization did not succeed, + we're still going to continue anyway. We need + to do this in order to await the expected + disconnect (which we will detect in the normal + course of operation). */ + } + } + + for (ch1 = mp->mc_first; ch1; ch1 = ch2) { + ch2 = ch1->mc_next; + if (ch1->check_func) ch1->check_func(ch1); + } + + if (mp->disconnect_flag && !mp->mc_first) { + /* Go away... */ + pvr2_context_destroy(mp); + return; + } +} + + +static int pvr2_context_shutok(void) +{ + return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL); +} + + +static int pvr2_context_thread_func(void *foo) +{ + struct pvr2_context *mp; + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start"); + + do { + while ((mp = pvr2_context_notify_first) != NULL) { + pvr2_context_set_notify(mp, 0); + pvr2_context_check(mp); + } + wait_event_interruptible( + pvr2_context_sync_data, + ((pvr2_context_notify_first != NULL) || + pvr2_context_shutok())); + } while (!pvr2_context_shutok()); + + pvr2_context_cleaned_flag = !0; + wake_up(&pvr2_context_cleanup_data); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up"); + + wait_event_interruptible( + pvr2_context_sync_data, + kthread_should_stop()); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end"); + + return 0; +} + + +int pvr2_context_global_init(void) +{ + pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func, + NULL, + "pvrusb2-context"); + return (pvr2_context_thread_ptr ? 0 : -ENOMEM); +} + + +void pvr2_context_global_done(void) +{ + pvr2_context_cleanup_flag = !0; + wake_up(&pvr2_context_sync_data); + wait_event_interruptible( + pvr2_context_cleanup_data, + pvr2_context_cleaned_flag); + kthread_stop(pvr2_context_thread_ptr); +} + + +struct pvr2_context *pvr2_context_create( + struct usb_interface *intf, + const struct usb_device_id *devid, + void (*setup_func)(struct pvr2_context *)) +{ + struct pvr2_context *mp = NULL; + mp = kzalloc(sizeof(*mp),GFP_KERNEL); + if (!mp) goto done; + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); + mp->setup_func = setup_func; + mutex_init(&mp->mutex); + mutex_lock(&pvr2_context_mutex); + mp->exist_prev = pvr2_context_exist_last; + mp->exist_next = NULL; + pvr2_context_exist_last = mp; + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp; + } else { + pvr2_context_exist_first = mp; + } + mutex_unlock(&pvr2_context_mutex); + mp->hdw = pvr2_hdw_create(intf,devid); + if (!mp->hdw) { + pvr2_context_destroy(mp); + mp = NULL; + goto done; + } + pvr2_context_set_notify(mp, !0); + done: + return mp; +} + + +static void pvr2_context_reset_input_limits(struct pvr2_context *mp) +{ + unsigned int tmsk,mmsk; + struct pvr2_channel *cp; + struct pvr2_hdw *hdw = mp->hdw; + mmsk = pvr2_hdw_get_input_available(hdw); + tmsk = mmsk; + for (cp = mp->mc_first; cp; cp = cp->mc_next) { + if (!cp->input_mask) continue; + tmsk &= cp->input_mask; + } + pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk); + pvr2_hdw_commit_ctl(hdw); +} + + +static void pvr2_context_enter(struct pvr2_context *mp) +{ + mutex_lock(&mp->mutex); +} + + +static void pvr2_context_exit(struct pvr2_context *mp) +{ + int destroy_flag = 0; + if (!(mp->mc_first || !mp->disconnect_flag)) { + destroy_flag = !0; + } + mutex_unlock(&mp->mutex); + if (destroy_flag) pvr2_context_notify(mp); +} + + +void pvr2_context_disconnect(struct pvr2_context *mp) +{ + pvr2_hdw_disconnect(mp->hdw); + mp->disconnect_flag = !0; + pvr2_context_notify(mp); +} + + +void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) +{ + pvr2_context_enter(mp); + cp->hdw = mp->hdw; + cp->mc_head = mp; + cp->mc_next = NULL; + cp->mc_prev = mp->mc_last; + if (mp->mc_last) { + mp->mc_last->mc_next = cp; + } else { + mp->mc_first = cp; + } + mp->mc_last = cp; + pvr2_context_exit(mp); +} + + +static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) +{ + if (!cp->stream) return; + pvr2_stream_kill(cp->stream->stream); + cp->stream->user = NULL; + cp->stream = NULL; +} + + +void pvr2_channel_done(struct pvr2_channel *cp) +{ + struct pvr2_context *mp = cp->mc_head; + pvr2_context_enter(mp); + cp->input_mask = 0; + pvr2_channel_disclaim_stream(cp); + pvr2_context_reset_input_limits(mp); + if (cp->mc_next) { + cp->mc_next->mc_prev = cp->mc_prev; + } else { + mp->mc_last = cp->mc_prev; + } + if (cp->mc_prev) { + cp->mc_prev->mc_next = cp->mc_next; + } else { + mp->mc_first = cp->mc_next; + } + cp->hdw = NULL; + pvr2_context_exit(mp); +} + + +int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk) +{ + unsigned int tmsk,mmsk; + int ret = 0; + struct pvr2_channel *p2; + struct pvr2_hdw *hdw = cp->hdw; + + mmsk = pvr2_hdw_get_input_available(hdw); + cmsk &= mmsk; + if (cmsk == cp->input_mask) { + /* No change; nothing to do */ + return 0; + } + + pvr2_context_enter(cp->mc_head); + do { + if (!cmsk) { + cp->input_mask = 0; + pvr2_context_reset_input_limits(cp->mc_head); + break; + } + tmsk = mmsk; + for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) { + if (p2 == cp) continue; + if (!p2->input_mask) continue; + tmsk &= p2->input_mask; + } + if (!(tmsk & cmsk)) { + ret = -EPERM; + break; + } + tmsk &= cmsk; + if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) { + /* Internal failure changing allowed list; probably + should not happen, but react if it does. */ + break; + } + cp->input_mask = cmsk; + pvr2_hdw_commit_ctl(hdw); + } while (0); + pvr2_context_exit(cp->mc_head); + return ret; +} + + +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) +{ + return cp->input_mask; +} + + +int pvr2_channel_claim_stream(struct pvr2_channel *cp, + struct pvr2_context_stream *sp) +{ + int code = 0; + pvr2_context_enter(cp->mc_head); do { + if (sp == cp->stream) break; + if (sp && sp->user) { + code = -EBUSY; + break; + } + pvr2_channel_disclaim_stream(cp); + if (!sp) break; + sp->user = cp; + cp->stream = sp; + } while (0); pvr2_context_exit(cp->mc_head); + return code; +} + + +// This is the marker for the real beginning of a legitimate mpeg2 stream. +static char stream_sync_key[] = { + 0x00, 0x00, 0x01, 0xba, +}; + +struct pvr2_ioread *pvr2_channel_create_mpeg_stream( + struct pvr2_context_stream *sp) +{ + struct pvr2_ioread *cp; + cp = pvr2_ioread_create(); + if (!cp) return NULL; + pvr2_ioread_setup(cp,sp->stream); + pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key)); + return cp; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.h b/drivers/media/usb/pvrusb2/pvrusb2-context.h new file mode 100644 index 000000000000..d657e53bbfa3 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-context.h @@ -0,0 +1,94 @@ +/* + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_CONTEXT_H +#define __PVRUSB2_CONTEXT_H + +#include +#include +#include + +struct pvr2_hdw; /* hardware interface - defined elsewhere */ +struct pvr2_stream; /* stream interface - defined elsewhere */ + +struct pvr2_context; /* All central state */ +struct pvr2_channel; /* One I/O pathway to a user */ +struct pvr2_context_stream; /* Wrapper for a stream */ +struct pvr2_ioread; /* Low level stream structure */ + +struct pvr2_context_stream { + struct pvr2_channel *user; + struct pvr2_stream *stream; +}; + +struct pvr2_context { + struct pvr2_channel *mc_first; + struct pvr2_channel *mc_last; + struct pvr2_context *exist_next; + struct pvr2_context *exist_prev; + struct pvr2_context *notify_next; + struct pvr2_context *notify_prev; + struct pvr2_hdw *hdw; + struct pvr2_context_stream video_stream; + struct mutex mutex; + int notify_flag; + int initialized_flag; + int disconnect_flag; + + /* Called after pvr2_context initialization is complete */ + void (*setup_func)(struct pvr2_context *); + +}; + +struct pvr2_channel { + struct pvr2_context *mc_head; + struct pvr2_channel *mc_next; + struct pvr2_channel *mc_prev; + struct pvr2_context_stream *stream; + struct pvr2_hdw *hdw; + unsigned int input_mask; + void (*check_func)(struct pvr2_channel *); +}; + +struct pvr2_context *pvr2_context_create(struct usb_interface *intf, + const struct usb_device_id *devid, + void (*setup_func)(struct pvr2_context *)); +void pvr2_context_disconnect(struct pvr2_context *); + +void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *); +void pvr2_channel_done(struct pvr2_channel *); +int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int); +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *); +int pvr2_channel_claim_stream(struct pvr2_channel *, + struct pvr2_context_stream *); +struct pvr2_ioread *pvr2_channel_create_mpeg_stream( + struct pvr2_context_stream *); + +int pvr2_context_global_init(void); +void pvr2_context_global_done(void); + +#endif /* __PVRUSB2_CONTEXT_H */ +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c new file mode 100644 index 000000000000..88320900dbd4 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c @@ -0,0 +1,95 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + v4l-dvb cs53l32a module. + +*/ + +#include "pvrusb2-cs53l32a.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include +#include +#include + +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + + +static const int routing_scheme1[] = { + [PVR2_CVAL_INPUT_TV] = 2, /* 1 or 2 seems to work here */ + [PVR2_CVAL_INPUT_RADIO] = 2, + [PVR2_CVAL_INPUT_COMPOSITE] = 0, + [PVR2_CVAL_INPUT_SVIDEO] = 0, +}; + +static const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, +}; + + +void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) +{ + if (hdw->input_dirty || hdw->force_dirty) { + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + u32 input; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", + hdw->input_val); + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev v4l2 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; + } + input = sp->def[hdw->input_val]; + sd->ops->audio->s_routing(sd, input, 0, 0); + } +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h new file mode 100644 index 000000000000..53ba548b72a7 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h @@ -0,0 +1,48 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_CS53L32A_H +#define __PVRUSB2_CS53L32A_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles device video processing. This interface is + used internally by the driver; higher level code should only + interact through the interface provided by pvrusb2-hdw.h. + +*/ + + +#include "pvrusb2-hdw-internal.h" +void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); + +#endif /* __PVRUSB2_AUDIO_CS53L32A_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c new file mode 100644 index 000000000000..7d5a7139a45a --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c @@ -0,0 +1,609 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pvrusb2-ctrl.h" +#include "pvrusb2-hdw-internal.h" +#include +#include +#include + + +static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val) +{ + if (cptr->info->check_value) { + if (!cptr->info->check_value(cptr,val)) return -ERANGE; + } else if (cptr->info->type == pvr2_ctl_enum) { + if (val < 0) return -ERANGE; + if (val >= cptr->info->def.type_enum.count) return -ERANGE; + } else { + int lim; + lim = cptr->info->def.type_int.min_value; + if (cptr->info->get_min_value) { + cptr->info->get_min_value(cptr,&lim); + } + if (val < lim) return -ERANGE; + lim = cptr->info->def.type_int.max_value; + if (cptr->info->get_max_value) { + cptr->info->get_max_value(cptr,&lim); + } + if (val > lim) return -ERANGE; + } + return 0; +} + + +/* Set the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) +{ + return pvr2_ctrl_set_mask_value(cptr,~0,val); +} + + +/* Set/clear specific bits of the given control. */ +int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) +{ + int ret = 0; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->set_value) { + if (cptr->info->type == pvr2_ctl_bitmask) { + mask &= cptr->info->def.type_bitmask.valid_bits; + } else if ((cptr->info->type == pvr2_ctl_int)|| + (cptr->info->type == pvr2_ctl_enum)) { + ret = pvr2_ctrl_range_check(cptr,val); + if (ret < 0) break; + } else if (cptr->info->type != pvr2_ctl_bool) { + break; + } + ret = cptr->info->set_value(cptr,mask,val); + } else { + ret = -EPERM; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr) +{ + int ret = 0; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = cptr->info->get_value(cptr,valptr); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's type */ +enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) +{ + if (!cptr) return pvr2_ctl_int; + return cptr->info->type; +} + + +/* Retrieve control's maximum value (int type) */ +int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->get_max_value) { + cptr->info->get_max_value(cptr,&ret); + } else if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->def.type_int.max_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's minimum value (int type) */ +int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->get_min_value) { + cptr->info->get_min_value(cptr,&ret); + } else if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->def.type_int.min_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's default value (any type) */ +int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr) +{ + int ret = 0; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->get_def_value) { + ret = cptr->info->get_def_value(cptr, valptr); + } else { + *valptr = cptr->info->default_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's enumeration count (enum only) */ +int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_enum) { + ret = cptr->info->def.type_enum.count; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's valid mask bits (bit mask only) */ +int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_bitmask) { + ret = cptr->info->def.type_bitmask.valid_bits; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve the control's name */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) +{ + if (!cptr) return NULL; + return cptr->info->name; +} + + +/* Retrieve the control's desc */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) +{ + if (!cptr) return NULL; + return cptr->info->desc; +} + + +/* Retrieve a control enumeration or bit mask value */ +int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, + char *bptr,unsigned int bmax, + unsigned int *blen) +{ + int ret = -EINVAL; + if (!cptr) return 0; + *blen = 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_enum) { + const char * const *names; + names = cptr->info->def.type_enum.value_names; + if (pvr2_ctrl_range_check(cptr,val) == 0) { + if (names[val]) { + *blen = scnprintf( + bptr,bmax,"%s", + names[val]); + } else { + *blen = 0; + } + ret = 0; + } + } else if (cptr->info->type == pvr2_ctl_bitmask) { + const char **names; + unsigned int idx; + int msk; + names = cptr->info->def.type_bitmask.bit_names; + val &= cptr->info->def.type_bitmask.valid_bits; + for (idx = 0, msk = 1; val; idx++, msk <<= 1) { + if (val & msk) { + *blen = scnprintf(bptr,bmax,"%s", + names[idx]); + ret = 0; + break; + } + } + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Return V4L ID for this control or zero if none */ +int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->v4l_id; +} + + +unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr) +{ + unsigned int flags = 0; + + if (cptr->info->get_v4lflags) { + flags = cptr->info->get_v4lflags(cptr); + } + + if (cptr->info->set_value) { + flags &= ~V4L2_CTRL_FLAG_READ_ONLY; + } else { + flags |= V4L2_CTRL_FLAG_READ_ONLY; + } + + return flags; +} + + +/* Return true if control is writable */ +int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->set_value != NULL; +} + + +/* Return true if control has custom symbolic representation */ +int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + if (!cptr->info->val_to_sym) return 0; + if (!cptr->info->sym_to_val) return 0; + return !0; +} + + +/* Convert a given mask/val to a custom symbolic value */ +int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + if (!cptr) return -EINVAL; + if (!cptr->info->val_to_sym) return -EINVAL; + return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len); +} + + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr, + const char *buf,unsigned int len, + int *maskptr,int *valptr) +{ + if (!cptr) return -EINVAL; + if (!cptr->info->sym_to_val) return -EINVAL; + return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr); +} + + +static unsigned int gen_bitmask_string(int msk,int val,int msk_only, + const char **names, + char *ptr,unsigned int len) +{ + unsigned int idx; + long sm,um; + int spcFl; + unsigned int uc,cnt; + const char *idStr; + + spcFl = 0; + uc = 0; + um = 0; + for (idx = 0, sm = 1; msk; idx++, sm <<= 1) { + if (sm & msk) { + msk &= ~sm; + idStr = names[idx]; + if (idStr) { + cnt = scnprintf(ptr,len,"%s%s%s", + (spcFl ? " " : ""), + (msk_only ? "" : + ((val & sm) ? "+" : "-")), + idStr); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else { + um |= sm; + } + } + } + if (um) { + if (msk_only) { + cnt = scnprintf(ptr,len,"%s0x%lx", + (spcFl ? " " : ""), + um); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else if (um & val) { + cnt = scnprintf(ptr,len,"%s+0x%lx", + (spcFl ? " " : ""), + um & val); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else if (um & ~val) { + cnt = scnprintf(ptr,len,"%s+0x%lx", + (spcFl ? " " : ""), + um & ~val); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } + } + return uc; +} + + +static const char *boolNames[] = { + "false", + "true", + "no", + "yes", +}; + + +static int parse_token(const char *ptr,unsigned int len, + int *valptr, + const char * const *names, unsigned int namecnt) +{ + char buf[33]; + unsigned int slen; + unsigned int idx; + int negfl; + char *p2; + *valptr = 0; + if (!names) namecnt = 0; + for (idx = 0; idx < namecnt; idx++) { + if (!names[idx]) continue; + slen = strlen(names[idx]); + if (slen != len) continue; + if (memcmp(names[idx],ptr,slen)) continue; + *valptr = idx; + return 0; + } + negfl = 0; + if ((*ptr == '-') || (*ptr == '+')) { + negfl = (*ptr == '-'); + ptr++; len--; + } + if (len >= sizeof(buf)) return -EINVAL; + memcpy(buf,ptr,len); + buf[len] = 0; + *valptr = simple_strtol(buf,&p2,0); + if (negfl) *valptr = -(*valptr); + if (*p2) return -EINVAL; + return 1; +} + + +static int parse_mtoken(const char *ptr,unsigned int len, + int *valptr, + const char **names,int valid_bits) +{ + char buf[33]; + unsigned int slen; + unsigned int idx; + char *p2; + int msk; + *valptr = 0; + for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) { + if (!(msk & valid_bits)) continue; + valid_bits &= ~msk; + if (!names[idx]) continue; + slen = strlen(names[idx]); + if (slen != len) continue; + if (memcmp(names[idx],ptr,slen)) continue; + *valptr = msk; + return 0; + } + if (len >= sizeof(buf)) return -EINVAL; + memcpy(buf,ptr,len); + buf[len] = 0; + *valptr = simple_strtol(buf,&p2,0); + if (*p2) return -EINVAL; + return 0; +} + + +static int parse_tlist(const char *ptr,unsigned int len, + int *maskptr,int *valptr, + const char **names,int valid_bits) +{ + unsigned int cnt; + int mask,val,kv,mode,ret; + mask = 0; + val = 0; + ret = 0; + while (len) { + cnt = 0; + while ((cnt < len) && + ((ptr[cnt] <= 32) || + (ptr[cnt] >= 127))) cnt++; + ptr += cnt; + len -= cnt; + mode = 0; + if ((*ptr == '-') || (*ptr == '+')) { + mode = (*ptr == '-') ? -1 : 1; + ptr++; + len--; + } + cnt = 0; + while (cnt < len) { + if (ptr[cnt] <= 32) break; + if (ptr[cnt] >= 127) break; + cnt++; + } + if (!cnt) break; + if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) { + ret = -EINVAL; + break; + } + ptr += cnt; + len -= cnt; + switch (mode) { + case 0: + mask = valid_bits; + val |= kv; + break; + case -1: + mask |= kv; + val &= ~kv; + break; + case 1: + mask |= kv; + val |= kv; + break; + default: + break; + } + } + *maskptr = mask; + *valptr = val; + return ret; +} + + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, + const char *ptr,unsigned int len, + int *maskptr,int *valptr) +{ + int ret = -EINVAL; + unsigned int cnt; + + *maskptr = 0; + *valptr = 0; + + cnt = 0; + while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++; + len -= cnt; ptr += cnt; + cnt = 0; + while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) || + (ptr[len-(cnt+1)] >= 127))) cnt++; + len -= cnt; + + if (!len) return -EINVAL; + + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = parse_token(ptr,len,valptr,NULL,0); + if (ret >= 0) { + ret = pvr2_ctrl_range_check(cptr,*valptr); + } + *maskptr = ~0; + } else if (cptr->info->type == pvr2_ctl_bool) { + ret = parse_token(ptr,len,valptr,boolNames, + ARRAY_SIZE(boolNames)); + if (ret == 1) { + *valptr = *valptr ? !0 : 0; + } else if (ret == 0) { + *valptr = (*valptr & 1) ? !0 : 0; + } + *maskptr = 1; + } else if (cptr->info->type == pvr2_ctl_enum) { + ret = parse_token( + ptr,len,valptr, + cptr->info->def.type_enum.value_names, + cptr->info->def.type_enum.count); + if (ret >= 0) { + ret = pvr2_ctrl_range_check(cptr,*valptr); + } + *maskptr = ~0; + } else if (cptr->info->type == pvr2_ctl_bitmask) { + ret = parse_tlist( + ptr,len,maskptr,valptr, + cptr->info->def.type_bitmask.bit_names, + cptr->info->def.type_bitmask.valid_bits); + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + int ret = -EINVAL; + + *len = 0; + if (cptr->info->type == pvr2_ctl_int) { + *len = scnprintf(buf,maxlen,"%d",val); + ret = 0; + } else if (cptr->info->type == pvr2_ctl_bool) { + *len = scnprintf(buf,maxlen,"%s",val ? "true" : "false"); + ret = 0; + } else if (cptr->info->type == pvr2_ctl_enum) { + const char * const *names; + names = cptr->info->def.type_enum.value_names; + if ((val >= 0) && + (val < cptr->info->def.type_enum.count)) { + if (names[val]) { + *len = scnprintf( + buf,maxlen,"%s", + names[val]); + } else { + *len = 0; + } + ret = 0; + } + } else if (cptr->info->type == pvr2_ctl_bitmask) { + *len = gen_bitmask_string( + val & mask & cptr->info->def.type_bitmask.valid_bits, + ~0,!0, + cptr->info->def.type_bitmask.bit_names, + buf,maxlen); + } + return ret; +} + + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + int ret; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val, + buf,maxlen,len); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h new file mode 100644 index 000000000000..794ff90121c7 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h @@ -0,0 +1,122 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_CTRL_H +#define __PVRUSB2_CTRL_H + +struct pvr2_ctrl; + +enum pvr2_ctl_type { + pvr2_ctl_int = 0, + pvr2_ctl_enum = 1, + pvr2_ctl_bitmask = 2, + pvr2_ctl_bool = 3, +}; + + +/* Set the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); + +/* Set/clear specific bits of the given control. */ +int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val); + +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr); + +/* Retrieve control's type */ +enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *); + +/* Retrieve control's maximum value (int type) */ +int pvr2_ctrl_get_max(struct pvr2_ctrl *); + +/* Retrieve control's minimum value (int type) */ +int pvr2_ctrl_get_min(struct pvr2_ctrl *); + +/* Retrieve control's default value (any type) */ +int pvr2_ctrl_get_def(struct pvr2_ctrl *, int *valptr); + +/* Retrieve control's enumeration count (enum only) */ +int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); + +/* Retrieve control's valid mask bits (bit mask only) */ +int pvr2_ctrl_get_mask(struct pvr2_ctrl *); + +/* Retrieve the control's name */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); + +/* Retrieve the control's desc */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); + +/* Retrieve a control enumeration or bit mask value */ +int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int, + unsigned int *); + +/* Return true if control is writable */ +int pvr2_ctrl_is_writable(struct pvr2_ctrl *); + +/* Return V4L flags value for control (or zero if there is no v4l control + actually under this control) */ +unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *); + +/* Return V4L ID for this control or zero if none */ +int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *); + +/* Return true if control has custom symbolic representation */ +int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *); + +/* Convert a given mask/val to a custom symbolic value */ +int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *, + const char *buf,unsigned int len, + int *maskptr,int *valptr); + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *, + const char *buf,unsigned int len, + int *maskptr,int *valptr); + +/* Convert a given mask/val to a symbolic value - must already be + inside of critical region. */ +int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +#endif /* __PVRUSB2_CTRL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c new file mode 100644 index 000000000000..c514d0b9ffdc --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -0,0 +1,166 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + cx2584x, in kernels 2.6.16 or newer. + +*/ + +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-video-v4l.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include +#include +#include +#include + + +struct routing_scheme_item { + int vid; + int aud; +}; + +struct routing_scheme { + const struct routing_scheme_item *def; + unsigned int cnt; +}; + +static const struct routing_scheme_item routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = { + .vid = CX25840_COMPOSITE7, + .aud = CX25840_AUDIO8, + }, + [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + .vid = CX25840_COMPOSITE3, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE3, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = CX25840_SVIDEO1, + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + +/* Specific to gotview device */ +static const struct routing_scheme_item routing_schemegv[] = { + [PVR2_CVAL_INPUT_TV] = { + .vid = CX25840_COMPOSITE2, + .aud = CX25840_AUDIO5, + }, + [PVR2_CVAL_INPUT_RADIO] = { + /* line-in is used for radio and composite. A GPIO is + used to switch between the two choices. */ + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4), + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +static const struct routing_scheme routing_defgv = { + .def = routing_schemegv, + .cnt = ARRAY_SIZE(routing_schemegv), +}; + +/* Specific to grabster av400 device */ +static const struct routing_scheme_item routing_schemeav400[] = { + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = (CX25840_SVIDEO_LUMA2|CX25840_SVIDEO_CHROMA4), + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +static const struct routing_scheme routing_defav400 = { + .def = routing_schemeav400, + .cnt = ARRAY_SIZE(routing_schemeav400), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, + [PVR2_ROUTING_SCHEME_AV400] = &routing_defav400, +}; + +void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) +{ + pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update..."); + if (hdw->input_dirty || hdw->force_dirty) { + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev cx2584x set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; + } + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; + pvr2_trace(PVR2_TRACE_CHIPS, + "subdev cx2584x set_input vid=0x%x aud=0x%x", + vid_input, aud_input); + sd->ops->video->s_routing(sd, (u32)vid_input, 0, 0); + sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0); + } +} + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h new file mode 100644 index 000000000000..e35c2322a08c --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -0,0 +1,52 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_CX2584X_V4L_H +#define __PVRUSB2_CX2584X_V4L_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles combined device audio & video processing. + This interface is used internally by the driver; higher level code + should only interact through the interface provided by + pvrusb2-hdw.h. + +*/ + + + +#include "pvrusb2-hdw-internal.h" + +void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); + + +#endif /* __PVRUSB2_CX2584X_V4L_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debug.h b/drivers/media/usb/pvrusb2/pvrusb2-debug.h new file mode 100644 index 000000000000..be79249f8628 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-debug.h @@ -0,0 +1,69 @@ +/* + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_DEBUG_H +#define __PVRUSB2_DEBUG_H + +extern int pvrusb2_debug; + +#define pvr2_trace(msk, fmt, arg...) do {if(msk & pvrusb2_debug) printk(KERN_INFO "pvrusb2: " fmt "\n", ##arg); } while (0) + +/* These are listed in *rough* order of decreasing usefulness and + increasing noise level. */ +#define PVR2_TRACE_INFO (1 << 0) /* Normal messages */ +#define PVR2_TRACE_ERROR_LEGS (1 << 1) /* error messages */ +#define PVR2_TRACE_TOLERANCE (1 << 2) /* track tolerance-affected errors */ +#define PVR2_TRACE_TRAP (1 << 3) /* Trap & report app misbehavior */ +#define PVR2_TRACE_STD (1 << 4) /* Log video standard stuff */ +#define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */ +#define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */ +#define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */ +#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */ +#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */ +#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */ +#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */ +#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ +#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */ +#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */ +#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */ +#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */ +#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */ +#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */ +#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */ +#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */ +#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */ +#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */ +#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */ +#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */ +#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */ +#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */ +#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */ +#define PVR2_TRACE_DVB_FEED (1 << 28) /* DVB transport feed debug */ + + +#endif /* __PVRUSB2_HDW_INTERNAL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c new file mode 100644 index 000000000000..4279ebb811a1 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c @@ -0,0 +1,335 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "pvrusb2-debugifc.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-debug.h" + +struct debugifc_mask_item { + const char *name; + unsigned long msk; +}; + + +static unsigned int debugifc_count_whitespace(const char *buf, + unsigned int count) +{ + unsigned int scnt; + char ch; + + for (scnt = 0; scnt < count; scnt++) { + ch = buf[scnt]; + if (ch == ' ') continue; + if (ch == '\t') continue; + if (ch == '\n') continue; + break; + } + return scnt; +} + + +static unsigned int debugifc_count_nonwhitespace(const char *buf, + unsigned int count) +{ + unsigned int scnt; + char ch; + + for (scnt = 0; scnt < count; scnt++) { + ch = buf[scnt]; + if (ch == ' ') break; + if (ch == '\t') break; + if (ch == '\n') break; + } + return scnt; +} + + +static unsigned int debugifc_isolate_word(const char *buf,unsigned int count, + const char **wstrPtr, + unsigned int *wlenPtr) +{ + const char *wptr; + unsigned int consume_cnt = 0; + unsigned int wlen; + unsigned int scnt; + + wptr = NULL; + wlen = 0; + scnt = debugifc_count_whitespace(buf,count); + consume_cnt += scnt; count -= scnt; buf += scnt; + if (!count) goto done; + + scnt = debugifc_count_nonwhitespace(buf,count); + if (!scnt) goto done; + wptr = buf; + wlen = scnt; + consume_cnt += scnt; count -= scnt; buf += scnt; + + done: + *wstrPtr = wptr; + *wlenPtr = wlen; + return consume_cnt; +} + + +static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, + u32 *num_ptr) +{ + u32 result = 0; + int radix = 10; + if ((count >= 2) && (buf[0] == '0') && + ((buf[1] == 'x') || (buf[1] == 'X'))) { + radix = 16; + count -= 2; + buf += 2; + } else if ((count >= 1) && (buf[0] == '0')) { + radix = 8; + } + + while (count--) { + int val = hex_to_bin(*buf++); + if (val < 0 || val >= radix) + return -EINVAL; + result *= radix; + result += val; + } + *num_ptr = result; + return 0; +} + + +static int debugifc_match_keyword(const char *buf,unsigned int count, + const char *keyword) +{ + unsigned int kl; + if (!keyword) return 0; + kl = strlen(keyword); + if (kl != count) return 0; + return !memcmp(buf,keyword,kl); +} + + +int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) +{ + int bcnt = 0; + int ccnt; + ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n", + pvr2_hdw_get_desc(hdw)); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + ccnt = scnprintf(buf,acnt,"Driver state info:\n"); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + ccnt = pvr2_hdw_state_report(hdw,buf,acnt); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + + return bcnt; +} + + +int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, + char *buf,unsigned int acnt) +{ + int bcnt = 0; + int ccnt; + int ret; + u32 gpio_dir,gpio_in,gpio_out; + struct pvr2_stream_stats stats; + struct pvr2_stream *sp; + + ret = pvr2_hdw_is_hsm(hdw); + ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", + (ret < 0 ? "FAIL" : (ret ? "high" : "full"))); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + + gpio_dir = 0; gpio_in = 0; gpio_out = 0; + pvr2_hdw_gpio_get_dir(hdw,&gpio_dir); + pvr2_hdw_gpio_get_out(hdw,&gpio_out); + pvr2_hdw_gpio_get_in(hdw,&gpio_in); + ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n", + gpio_dir,gpio_in,gpio_out); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + + ccnt = scnprintf(buf,acnt,"Streaming is %s\n", + pvr2_hdw_get_streaming(hdw) ? "on" : "off"); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + + + sp = pvr2_hdw_get_video_stream(hdw); + if (sp) { + pvr2_stream_get_stats(sp, &stats, 0); + ccnt = scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u\n", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + } + + return bcnt; +} + + +static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, + unsigned int count) +{ + const char *wptr; + unsigned int wlen; + unsigned int scnt; + + scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); + if (!scnt) return 0; + count -= scnt; buf += scnt; + if (!wptr) return 0; + + pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr); + if (debugifc_match_keyword(wptr,wlen,"reset")) { + scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); + if (!scnt) return -EINVAL; + count -= scnt; buf += scnt; + if (!wptr) return -EINVAL; + if (debugifc_match_keyword(wptr,wlen,"cpu")) { + pvr2_hdw_cpureset_assert(hdw,!0); + pvr2_hdw_cpureset_assert(hdw,0); + return 0; + } else if (debugifc_match_keyword(wptr,wlen,"bus")) { + pvr2_hdw_device_reset(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"soft")) { + return pvr2_hdw_cmd_powerup(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"deep")) { + return pvr2_hdw_cmd_deep_reset(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { + return pvr2_upload_firmware2(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { + return pvr2_hdw_cmd_decoder_reset(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"worker")) { + return pvr2_hdw_untrip(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) { + pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), + NULL, !0); + return 0; + } + return -EINVAL; + } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { + scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); + if (!scnt) return -EINVAL; + count -= scnt; buf += scnt; + if (!wptr) return -EINVAL; + if (debugifc_match_keyword(wptr,wlen,"fetch")) { + scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); + if (scnt && wptr) { + count -= scnt; buf += scnt; + if (debugifc_match_keyword(wptr, wlen, + "prom")) { + pvr2_hdw_cpufw_set_enabled(hdw, 2, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram8k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 0, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram16k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 1, !0); + } else { + return -EINVAL; + } + } + pvr2_hdw_cpufw_set_enabled(hdw,0,!0); + return 0; + } else if (debugifc_match_keyword(wptr,wlen,"done")) { + pvr2_hdw_cpufw_set_enabled(hdw,0,0); + return 0; + } else { + return -EINVAL; + } + } else if (debugifc_match_keyword(wptr,wlen,"gpio")) { + int dir_fl = 0; + int ret; + u32 msk,val; + scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); + if (!scnt) return -EINVAL; + count -= scnt; buf += scnt; + if (!wptr) return -EINVAL; + if (debugifc_match_keyword(wptr,wlen,"dir")) { + dir_fl = !0; + } else if (!debugifc_match_keyword(wptr,wlen,"out")) { + return -EINVAL; + } + scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); + if (!scnt) return -EINVAL; + count -= scnt; buf += scnt; + if (!wptr) return -EINVAL; + ret = debugifc_parse_unsigned_number(wptr,wlen,&msk); + if (ret) return ret; + scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); + if (wptr) { + ret = debugifc_parse_unsigned_number(wptr,wlen,&val); + if (ret) return ret; + } else { + val = msk; + msk = 0xffffffff; + } + if (dir_fl) { + ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val); + } else { + ret = pvr2_hdw_gpio_chg_out(hdw,msk,val); + } + return ret; + } + pvr2_trace(PVR2_TRACE_DEBUGIFC, + "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr); + return -EINVAL; +} + + +int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, + unsigned int count) +{ + unsigned int bcnt = 0; + int ret; + + while (count) { + for (bcnt = 0; bcnt < count; bcnt++) { + if (buf[bcnt] == '\n') break; + } + + ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt); + if (ret < 0) return ret; + if (bcnt < count) bcnt++; + buf += bcnt; + count -= bcnt; + } + + return 0; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h new file mode 100644 index 000000000000..2f8d46761cd0 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h @@ -0,0 +1,52 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_DEBUGIFC_H +#define __PVRUSB2_DEBUGIFC_H + +struct pvr2_hdw; + +/* Print general status of driver. This will also trigger a probe of + the USB link. Unlike print_info(), this one synchronizes with the + driver so the information should be self-consistent (but it will + hang if the driver is wedged). */ +int pvr2_debugifc_print_info(struct pvr2_hdw *, + char *buf_ptr, unsigned int buf_size); + +/* Non-intrusively print some useful debugging info from inside the + driver. This should work even if the driver appears to be + wedged. */ +int pvr2_debugifc_print_status(struct pvr2_hdw *, + char *buf_ptr,unsigned int buf_size); + +/* Parse a string command into a driver action. */ +int pvr2_debugifc_docmd(struct pvr2_hdw *, + const char *buf_ptr,unsigned int buf_size); + +#endif /* __PVRUSB2_DEBUGIFC_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c new file mode 100644 index 000000000000..adc501d3c287 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c @@ -0,0 +1,576 @@ +/* + * + * + * Copyright (C) 2007 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + +This source file should encompass ALL per-device type information for the +driver. To define a new device, add elements to the pvr2_device_table and +pvr2_device_desc structures. + +*/ + +#include "pvrusb2-devattr.h" +#include +#include +/* This is needed in order to pull in tuner type ids... */ +#include +#include +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-hdw-internal.h" +#include "lgdt330x.h" +#include "s5h1409.h" +#include "s5h1411.h" +#include "tda10048.h" +#include "tda18271.h" +#include "tda8290.h" +#include "tuner-simple.h" +#endif + + +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 29xxx */ + +static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_MSP3400 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, + { .module_id = PVR2_CLIENT_ID_DEMOD }, +}; + +#define PVR2_FIRMWARE_29xxx "v4l-pvrusb2-29xxx-01.fw" +static const char *pvr2_fw1_names_29xxx[] = { + PVR2_FIRMWARE_29xxx, +}; + +static const struct pvr2_device_desc pvr2_device_29xxx = { + .description = "WinTV PVR USB2 Model 29xxx", + .shortname = "29xxx", + .client_table.lst = pvr2_cli_29xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), + .fx2_firmware.lst = pvr2_fw1_names_29xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_29XXX, +}; + + + +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 24xxx */ + +static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, + { .module_id = PVR2_CLIENT_ID_WM8775 }, + { .module_id = PVR2_CLIENT_ID_DEMOD }, +}; + +#define PVR2_FIRMWARE_24xxx "v4l-pvrusb2-24xxx-01.fw" +static const char *pvr2_fw1_names_24xxx[] = { + PVR2_FIRMWARE_24xxx, +}; + +static const struct pvr2_device_desc pvr2_device_24xxx = { + .description = "WinTV PVR USB2 Model 24xxx", + .shortname = "24xxx", + .client_table.lst = pvr2_cli_24xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), + .fx2_firmware.lst = pvr2_fw1_names_24xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), + .flag_has_cx25840 = !0, + .flag_has_wm8775 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_24XXX, +}; + + + +/*------------------------------------------------------------------------*/ +/* GOTVIEW USB2.0 DVD2 */ + +static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, + { .module_id = PVR2_CLIENT_ID_DEMOD }, +}; + +static const struct pvr2_device_desc pvr2_device_gotview_2 = { + .description = "Gotview USB 2.0 DVD 2", + .shortname = "gv2", + .client_table.lst = pvr2_cli_gotview_2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), + .flag_has_cx25840 = !0, + .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, +}; + + + +/*------------------------------------------------------------------------*/ +/* GOTVIEW USB2.0 DVD Deluxe */ + +/* (same module list as gotview_2) */ + +static const struct pvr2_device_desc pvr2_device_gotview_2d = { + .description = "Gotview USB 2.0 DVD Deluxe", + .shortname = "gv2d", + .client_table.lst = pvr2_cli_gotview_2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), + .flag_has_cx25840 = !0, + .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, +}; + + + +/*------------------------------------------------------------------------*/ +/* Terratec Grabster AV400 */ + +static const struct pvr2_device_client_desc pvr2_cli_av400[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, +}; + +static const struct pvr2_device_desc pvr2_device_av400 = { + .description = "Terratec Grabster AV400", + .shortname = "av400", + .flag_is_experimental = 1, + .client_table.lst = pvr2_cli_av400, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_av400), + .flag_has_cx25840 = !0, + .flag_has_analogtuner = 0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400, +}; + + + +/*------------------------------------------------------------------------*/ +/* OnAir Creator */ + +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3303_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .clock_polarity_flip = 1, +}; + +static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF); + + return 0; +} + +static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = { + .frontend_attach = pvr2_lgdt3303_attach, + .tuner_attach = pvr2_lgh06xf_attach, +}; +#endif + +static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_CS53L32A }, + { .module_id = PVR2_CLIENT_ID_TUNER }, +}; + +static const struct pvr2_device_desc pvr2_device_onair_creator = { + .description = "OnAir Creator Hybrid USB tuner", + .shortname = "oac", + .client_table.lst = pvr2_cli_onair_creator, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator), + .default_tuner_type = TUNER_LG_TDVS_H06XF, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, + .default_std_mask = V4L2_STD_NTSC_M, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_creator_fe_props, +#endif +}; + + + +/*------------------------------------------------------------------------*/ +/* OnAir USB 2.0 */ + +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3302_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3302, +}; + +static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_PHILIPS_FCV1236D); + + return 0; +} + +static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { + .frontend_attach = pvr2_lgdt3302_attach, + .tuner_attach = pvr2_fcv1236d_attach, +}; +#endif + +static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_CS53L32A }, + { .module_id = PVR2_CLIENT_ID_TUNER }, +}; + +static const struct pvr2_device_desc pvr2_device_onair_usb2 = { + .description = "OnAir USB2 Hybrid USB tuner", + .shortname = "oa2", + .client_table.lst = pvr2_cli_onair_usb2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2), + .default_tuner_type = TUNER_PHILIPS_FCV1236D, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, + .default_std_mask = V4L2_STD_NTSC_M, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_usb2_fe_props, +#endif +}; + + + +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 73xxx */ + +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct tda10048_config hauppauge_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_50, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3800, + .dtv8_if_freq_khz = TDA10048_IF_4300, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + +static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config hauppauge_tda18271_dvb_config = { + .std_map = &hauppauge_tda18271_dvbt_std_map, + .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, +}; + +static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_dvb_config); + + return 0; +} + +static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = { + .frontend_attach = pvr2_tda10048_attach, + .tuner_attach = pvr2_73xxx_tda18271_8295_attach, +}; +#endif + +static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER, + .i2c_address_list = "\x42"}, +}; + +#define PVR2_FIRMWARE_73xxx "v4l-pvrusb2-73xxx-01.fw" +static const char *pvr2_fw1_names_73xxx[] = { + PVR2_FIRMWARE_73xxx, +}; + +static const struct pvr2_device_desc pvr2_device_73xxx = { + .description = "WinTV HVR-1900 Model 73xxx", + .shortname = "73xxx", + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), + .fx2_firmware.lst = pvr2_fw1_names_73xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx), + .flag_has_cx25840 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .flag_fx2_16kb = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_73xxx_dvb_props, +#endif +}; + + + +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 75xxx */ + +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct s5h1409_config pvr2_s5h1409_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_PARALLEL_OUTPUT, + .gpio = S5H1409_GPIO_OFF, + .qam_if = 4000, + .inversion = S5H1409_INVERSION_ON, + .status_mode = S5H1409_DEMODLOCKING, +}; + +static struct s5h1411_config pvr2_s5h1411_config = { + .output_mode = S5H1411_PARALLEL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .vsb_if = S5H1411_IF_44000, + .qam_if = S5H1411_IF_4000, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config hauppauge_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, +}; + +static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_config); + + return 0; +} + +static const struct pvr2_dvb_props pvr2_750xx_dvb_props = { + .frontend_attach = pvr2_s5h1409_attach, + .tuner_attach = pvr2_tda18271_8295_attach, +}; + +static const struct pvr2_dvb_props pvr2_751xx_dvb_props = { + .frontend_attach = pvr2_s5h1411_attach, + .tuner_attach = pvr2_tda18271_8295_attach, +}; +#endif + +#define PVR2_FIRMWARE_75xxx "v4l-pvrusb2-73xxx-01.fw" +static const char *pvr2_fw1_names_75xxx[] = { + PVR2_FIRMWARE_75xxx, +}; + +static const struct pvr2_device_desc pvr2_device_750xx = { + .description = "WinTV HVR-1950 Model 750xx", + .shortname = "750xx", + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), + .fx2_firmware.lst = pvr2_fw1_names_75xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), + .flag_has_cx25840 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .flag_fx2_16kb = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_750xx_dvb_props, +#endif +}; + +static const struct pvr2_device_desc pvr2_device_751xx = { + .description = "WinTV HVR-1950 Model 751xx", + .shortname = "751xx", + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), + .fx2_firmware.lst = pvr2_fw1_names_75xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), + .flag_has_cx25840 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .flag_fx2_16kb = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_751xx_dvb_props, +#endif +}; + + + +/*------------------------------------------------------------------------*/ + +struct usb_device_id pvr2_device_table[] = { + { USB_DEVICE(0x2040, 0x2900), + .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, + { USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */ + .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, + { USB_DEVICE(0x2040, 0x2400), + .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, + { USB_DEVICE(0x1164, 0x0622), + .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2}, + { USB_DEVICE(0x1164, 0x0602), + .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d}, + { USB_DEVICE(0x11ba, 0x1003), + .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator}, + { USB_DEVICE(0x11ba, 0x1001), + .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2}, + { USB_DEVICE(0x2040, 0x7300), + .driver_info = (kernel_ulong_t)&pvr2_device_73xxx}, + { USB_DEVICE(0x2040, 0x7500), + .driver_info = (kernel_ulong_t)&pvr2_device_750xx}, + { USB_DEVICE(0x2040, 0x7501), + .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, + { USB_DEVICE(0x0ccd, 0x0039), + .driver_info = (kernel_ulong_t)&pvr2_device_av400}, + { } +}; + +MODULE_DEVICE_TABLE(usb, pvr2_device_table); +MODULE_FIRMWARE(PVR2_FIRMWARE_29xxx); +MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx); +MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx); +MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx); + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h new file mode 100644 index 000000000000..273c8d4b3853 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h @@ -0,0 +1,199 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_DEVATTR_H +#define __PVRUSB2_DEVATTR_H + +#include +#include +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-dvb.h" +#endif + +/* + + This header defines structures used to describe attributes of a device. + +*/ + + +#define PVR2_CLIENT_ID_NULL 0 +#define PVR2_CLIENT_ID_MSP3400 1 +#define PVR2_CLIENT_ID_CX25840 2 +#define PVR2_CLIENT_ID_SAA7115 3 +#define PVR2_CLIENT_ID_TUNER 4 +#define PVR2_CLIENT_ID_CS53L32A 5 +#define PVR2_CLIENT_ID_WM8775 6 +#define PVR2_CLIENT_ID_DEMOD 7 + +struct pvr2_device_client_desc { + /* One ovr PVR2_CLIENT_ID_xxxx */ + unsigned char module_id; + + /* Null-terminated array of I2C addresses to try in order + initialize the module. It's safe to make this null terminated + since we're never going to encounter an i2c device with an + address of zero. If this is a null pointer or zero-length, + then no I2C addresses have been specified, in which case we'll + try some compiled in defaults for now. */ + unsigned char *i2c_address_list; +}; + +struct pvr2_device_client_table { + const struct pvr2_device_client_desc *lst; + unsigned char cnt; +}; + + +struct pvr2_string_table { + const char **lst; + unsigned int cnt; +}; + +#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 +#define PVR2_ROUTING_SCHEME_GOTVIEW 1 +#define PVR2_ROUTING_SCHEME_ONAIR 2 +#define PVR2_ROUTING_SCHEME_AV400 3 + +#define PVR2_DIGITAL_SCHEME_NONE 0 +#define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 +#define PVR2_DIGITAL_SCHEME_ONAIR 2 + +#define PVR2_LED_SCHEME_NONE 0 +#define PVR2_LED_SCHEME_HAUPPAUGE 1 + +#define PVR2_IR_SCHEME_NONE 0 +#define PVR2_IR_SCHEME_24XXX 1 /* FX2-controlled IR */ +#define PVR2_IR_SCHEME_ZILOG 2 /* HVR-1950 style (must be taken out of reset) */ +#define PVR2_IR_SCHEME_24XXX_MCE 3 /* 24xxx MCE device */ +#define PVR2_IR_SCHEME_29XXX 4 /* Original 29xxx device */ + +/* This describes a particular hardware type (except for the USB device ID + which must live in a separate structure due to environmental + constraints). See the top of pvrusb2-hdw.c for where this is + instantiated. */ +struct pvr2_device_desc { + /* Single line text description of hardware */ + const char *description; + + /* Single token identifier for hardware */ + const char *shortname; + + /* List of additional client modules we need to load */ + struct pvr2_string_table client_modules; + + /* List of defined client modules we need to load */ + struct pvr2_device_client_table client_table; + + /* List of FX2 firmware file names we should search; if empty then + FX2 firmware check / load is skipped and we assume the device + was initialized from internal ROM. */ + struct pvr2_string_table fx2_firmware; + +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* callback functions to handle attachment of digital tuner & demod */ + const struct pvr2_dvb_props *dvb_props; + +#endif + /* Initial standard bits to use for this device, if not zero. + Anything set here is also implied as an available standard. + Note: This is ignored if overridden on the module load line via + the video_std module option. */ + v4l2_std_id default_std_mask; + + /* V4L tuner type ID to use with this device (only used if the + driver could not discover the type any other way). */ + int default_tuner_type; + + /* Signal routing scheme used by device, contains one of + PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we + encounter them. This is an arbitrary integer scheme id; its + meaning is contained entirely within the driver and is + interpreted by logic which must send commands to the chip-level + drivers (search for things which touch this field). */ + unsigned char signal_routing_scheme; + + /* Indicates scheme for controlling device's LED (if any). The + driver will turn on the LED when streaming is underway. This + contains one of PVR2_LED_SCHEME_XXX. */ + unsigned char led_scheme; + + /* Control scheme to use if there is a digital tuner. This + contains one of PVR2_DIGITAL_SCHEME_XXX. This is an arbitrary + integer scheme id; its meaning is contained entirely within the + driver and is interpreted by logic which must control the + streaming pathway (search for things which touch this field). */ + unsigned char digital_control_scheme; + + /* If set, we don't bother trying to load cx23416 firmware. */ + unsigned int flag_skip_cx23416_firmware:1; + + /* If set, the encoder must be healthy in order for digital mode to + work (otherwise we assume that digital streaming will work even + if we fail to locate firmware for the encoder). If the device + doesn't support digital streaming then this flag has no + effect. */ + unsigned int flag_digital_requires_cx23416:1; + + /* Device has a hauppauge eeprom which we can interrogate. */ + unsigned int flag_has_hauppauge_rom:1; + + /* Device does not require a powerup command to be issued. */ + unsigned int flag_no_powerup:1; + + /* Device has a cx25840 - this enables special additional logic to + handle it. */ + unsigned int flag_has_cx25840:1; + + /* Device has a wm8775 - this enables special additional logic to + ensure that it is found. */ + unsigned int flag_has_wm8775:1; + + /* Indicate IR scheme of hardware. If not set, then it is assumed + that IR can work without any help from the driver. */ + unsigned int ir_scheme:3; + + /* These bits define which kinds of sources the device can handle. + Note: Digital tuner presence is inferred by the + digital_control_scheme enumeration. */ + unsigned int flag_has_fmradio:1; /* Has FM radio receiver */ + unsigned int flag_has_analogtuner:1; /* Has analog tuner */ + unsigned int flag_has_composite:1; /* Has composite input */ + unsigned int flag_has_svideo:1; /* Has s-video input */ + unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */ + + /* If this driver is considered experimental, i.e. not all aspects + are working correctly and/or it is untested, mark that fact + with this flag. */ + unsigned int flag_is_experimental:1; +}; + +extern struct usb_device_id pvr2_device_table[]; + +#endif /* __PVRUSB2_HDW_INTERNAL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-dvb.c b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c new file mode 100644 index 000000000000..8c95793433e7 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c @@ -0,0 +1,435 @@ +/* + * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "dvbdev.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-io.h" +#include "pvrusb2-dvb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) +{ + int ret; + unsigned int count; + struct pvr2_buffer *bp; + struct pvr2_stream *stream; + + pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started"); + set_freezable(); + + stream = adap->channel.stream->stream; + + for (;;) { + if (kthread_should_stop()) break; + + /* Not sure about this... */ + try_to_freeze(); + + bp = pvr2_stream_get_ready_buffer(stream); + if (bp != NULL) { + count = pvr2_buffer_get_count(bp); + if (count) { + dvb_dmx_swfilter( + &adap->demux, + adap->buffer_storage[ + pvr2_buffer_get_id(bp)], + count); + } else { + ret = pvr2_buffer_get_status(bp); + if (ret < 0) break; + } + ret = pvr2_buffer_queue(bp); + if (ret < 0) break; + + /* Since we know we did something to a buffer, + just go back and try again. No point in + blocking unless we really ran out of + buffers to process. */ + continue; + } + + + /* Wait until more buffers become available or we're + told not to wait any longer. */ + ret = wait_event_interruptible( + adap->buffer_wait_data, + (pvr2_stream_get_ready_count(stream) > 0) || + kthread_should_stop()); + if (ret < 0) break; + } + + /* If we get here and ret is < 0, then an error has occurred. + Probably would be a good idea to communicate that to DVB core... */ + + pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped"); + + return 0; +} + +static int pvr2_dvb_feed_thread(void *data) +{ + int stat = pvr2_dvb_feed_func(data); + /* from videobuf-dvb.c: */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return stat; +} + +static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap) +{ + wake_up(&adap->buffer_wait_data); +} + +static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap) +{ + unsigned int idx; + struct pvr2_stream *stream; + + if (adap->thread) { + kthread_stop(adap->thread); + adap->thread = NULL; + } + + if (adap->channel.stream) { + stream = adap->channel.stream->stream; + } else { + stream = NULL; + } + if (stream) { + pvr2_hdw_set_streaming(adap->channel.hdw, 0); + pvr2_stream_set_callback(stream, NULL, NULL); + pvr2_stream_kill(stream); + pvr2_stream_set_buffer_count(stream, 0); + pvr2_channel_claim_stream(&adap->channel, NULL); + } + + if (adap->stream_run) { + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + if (!(adap->buffer_storage[idx])) continue; + kfree(adap->buffer_storage[idx]); + adap->buffer_storage[idx] = NULL; + } + adap->stream_run = 0; + } +} + +static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_context *pvr = adap->channel.mc_head; + unsigned int idx; + int ret; + struct pvr2_buffer *bp; + struct pvr2_stream *stream = NULL; + + if (adap->stream_run) return -EIO; + + ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream); + /* somebody else already has the stream */ + if (ret < 0) return ret; + + stream = adap->channel.stream->stream; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE, + GFP_KERNEL); + if (!(adap->buffer_storage[idx])) return -ENOMEM; + } + + pvr2_stream_set_callback(pvr->video_stream.stream, + (pvr2_stream_callback) pvr2_dvb_notify, adap); + + ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT); + if (ret < 0) return ret; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + bp = pvr2_stream_get_buffer(stream, idx); + pvr2_buffer_set_buffer(bp, + adap->buffer_storage[idx], + PVR2_DVB_BUFFER_SIZE); + } + + ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1); + if (ret < 0) return ret; + + while ((bp = pvr2_stream_get_idle_buffer(stream)) != NULL) { + ret = pvr2_buffer_queue(bp); + if (ret < 0) return ret; + } + + adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb"); + + if (IS_ERR(adap->thread)) { + ret = PTR_ERR(adap->thread); + adap->thread = NULL; + return ret; + } + + adap->stream_run = !0; + + return 0; +} + +static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap) +{ + int ret = pvr2_dvb_stream_do_start(adap); + if (ret < 0) pvr2_dvb_stream_end(adap); + return ret; +} + +static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv; + int ret = 0; + + if (adap == NULL) return -ENODEV; + + mutex_lock(&adap->lock); + do { + if (onoff) { + if (!adap->feedcount) { + pvr2_trace(PVR2_TRACE_DVB_FEED, + "start feeding demux"); + ret = pvr2_dvb_stream_start(adap); + if (ret < 0) break; + } + (adap->feedcount)++; + } else if (adap->feedcount > 0) { + (adap->feedcount)--; + if (!adap->feedcount) { + pvr2_trace(PVR2_TRACE_DVB_FEED, + "stop feeding demux"); + pvr2_dvb_stream_end(adap); + } + } + } while (0); + mutex_unlock(&adap->lock); + + return ret; +} + +static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1); +} + +static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0); +} + +static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct pvr2_dvb_adapter *adap = fe->dvb->priv; + return pvr2_channel_limit_inputs( + &adap->channel, + (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0)); +} + +static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap) +{ + int ret; + + ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb", + THIS_MODULE/*&hdw->usb_dev->owner*/, + &adap->channel.hdw->usb_dev->dev, + adapter_nr); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "dvb_register_adapter failed: error %d", ret); + goto err; + } + adap->dvb_adap.priv = adap; + + adap->demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + adap->demux.priv = adap; + adap->demux.filternum = 256; + adap->demux.feednum = 256; + adap->demux.start_feed = pvr2_dvb_start_feed; + adap->demux.stop_feed = pvr2_dvb_stop_feed; + adap->demux.write_to_decoder = NULL; + + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "dvb_dmx_init failed: error %d", ret); + goto err_dmx; + } + + adap->dmxdev.filternum = adap->demux.filternum; + adap->dmxdev.demux = &adap->demux.dmx; + adap->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "dvb_dmxdev_init failed: error %d", ret); + goto err_dmx_dev; + } + + dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); + + return 0; + +err_dmx_dev: + dvb_dmx_release(&adap->demux); +err_dmx: + dvb_unregister_adapter(&adap->dvb_adap); +err: + return ret; +} + +static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) +{ + pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices"); + dvb_net_release(&adap->dvb_net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->dvb_adap); + return 0; +} + +static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_hdw *hdw = adap->channel.hdw; + const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; + int ret = 0; + + if (dvb_props == NULL) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!"); + return -EINVAL; + } + + ret = pvr2_channel_limit_inputs( + &adap->channel, + (1 << PVR2_CVAL_INPUT_DTV)); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "failed to grab control of dtv input (code=%d)", + ret); + return ret; + } + + if (dvb_props->frontend_attach == NULL) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "frontend_attach not defined!"); + ret = -EINVAL; + goto done; + } + + if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { + + if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "frontend registration failed!"); + dvb_frontend_detach(adap->fe); + adap->fe = NULL; + ret = -ENODEV; + goto done; + } + + if (dvb_props->tuner_attach) + dvb_props->tuner_attach(adap); + + if (adap->fe->ops.analog_ops.standby) + adap->fe->ops.analog_ops.standby(adap->fe); + + /* Ensure all frontends negotiate bus access */ + adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; + + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "no frontend was attached!"); + ret = -ENODEV; + return ret; + } + + done: + pvr2_channel_limit_inputs(&adap->channel, 0); + return ret; +} + +static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) +{ + if (adap->fe != NULL) { + dvb_unregister_frontend(adap->fe); + dvb_frontend_detach(adap->fe); + } + return 0; +} + +static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap) +{ + pvr2_dvb_stream_end(adap); + pvr2_dvb_frontend_exit(adap); + pvr2_dvb_adapter_exit(adap); + pvr2_channel_done(&adap->channel); + kfree(adap); +} + +static void pvr2_dvb_internal_check(struct pvr2_channel *chp) +{ + struct pvr2_dvb_adapter *adap; + adap = container_of(chp, struct pvr2_dvb_adapter, channel); + if (!adap->channel.mc_head->disconnect_flag) return; + pvr2_dvb_destroy(adap); +} + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr) +{ + int ret = 0; + struct pvr2_dvb_adapter *adap; + if (!pvr->hdw->hdw_desc->dvb_props) { + /* Device lacks a digital interface so don't set up + the DVB side of the driver either. For now. */ + return NULL; + } + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (!adap) return adap; + pvr2_channel_init(&adap->channel, pvr); + adap->channel.check_func = pvr2_dvb_internal_check; + init_waitqueue_head(&adap->buffer_wait_data); + mutex_init(&adap->lock); + ret = pvr2_dvb_adapter_init(adap); + if (ret < 0) goto fail1; + ret = pvr2_dvb_frontend_init(adap); + if (ret < 0) goto fail2; + return adap; + +fail2: + pvr2_dvb_adapter_exit(adap); +fail1: + pvr2_channel_done(&adap->channel); + return NULL; +} + diff --git a/drivers/media/usb/pvrusb2/pvrusb2-dvb.h b/drivers/media/usb/pvrusb2/pvrusb2-dvb.h new file mode 100644 index 000000000000..884ff916a352 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-dvb.h @@ -0,0 +1,41 @@ +#ifndef __PVRUSB2_DVB_H__ +#define __PVRUSB2_DVB_H__ + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" +#include "pvrusb2-context.h" + +#define PVR2_DVB_BUFFER_COUNT 32 +#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000) + +struct pvr2_dvb_adapter { + struct pvr2_channel channel; + + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct dvb_frontend *fe; + + int feedcount; + int max_feed_count; + + struct task_struct *thread; + struct mutex lock; + + unsigned int stream_run:1; + + wait_queue_head_t buffer_wait_data; + char *buffer_storage[PVR2_DVB_BUFFER_COUNT]; +}; + +struct pvr2_dvb_props { + int (*frontend_attach) (struct pvr2_dvb_adapter *); + int (*tuner_attach) (struct pvr2_dvb_adapter *); +}; + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr); + +#endif /* __PVRUSB2_DVB_H__ */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c new file mode 100644 index 000000000000..9515f3a68f8f --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c @@ -0,0 +1,164 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "pvrusb2-eeprom.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" + +#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) + + + +/* + + Read and analyze data in the eeprom. Use tveeprom to figure out + the packet structure, since this is another Hauppauge device and + internally it has a family resemblance to ivtv-type devices + +*/ + +#include + +/* We seem to only be interested in the last 128 bytes of the EEPROM */ +#define EEPROM_SIZE 128 + +/* Grab EEPROM contents, needed for direct method. */ +static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) +{ + struct i2c_msg msg[2]; + u8 *eeprom; + u8 iadd[2]; + u8 addr; + u16 eepromSize; + unsigned int offs; + int ret; + int mode16 = 0; + unsigned pcnt,tcnt; + eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL); + if (!eeprom) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to allocate memory" + " required to read eeprom"); + return NULL; + } + + trace_eeprom("Value for eeprom addr from controller was 0x%x", + hdw->eeprom_addr); + addr = hdw->eeprom_addr; + /* Seems that if the high bit is set, then the *real* eeprom + address is shifted right now bit position (noticed this in + newer PVR USB2 hardware) */ + if (addr & 0x80) addr >>= 1; + + /* FX2 documentation states that a 16bit-addressed eeprom is + expected if the I2C address is an odd number (yeah, this is + strange but it's what they do) */ + mode16 = (addr & 1); + eepromSize = (mode16 ? 4096 : 256); + trace_eeprom("Examining %d byte eeprom at location 0x%x" + " using %d bit addressing",eepromSize,addr, + mode16 ? 16 : 8); + + msg[0].addr = addr; + msg[0].flags = 0; + msg[0].len = mode16 ? 2 : 1; + msg[0].buf = iadd; + msg[1].addr = addr; + msg[1].flags = I2C_M_RD; + + /* We have to do the actual eeprom data fetch ourselves, because + (1) we're only fetching part of the eeprom, and (2) if we were + getting the whole thing our I2C driver can't grab it in one + pass - which is what tveeprom is otherwise going to attempt */ + memset(eeprom,0,EEPROM_SIZE); + for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) { + pcnt = 16; + if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt; + offs = tcnt + (eepromSize - EEPROM_SIZE); + if (mode16) { + iadd[0] = offs >> 8; + iadd[1] = offs; + } else { + iadd[0] = offs; + } + msg[1].len = pcnt; + msg[1].buf = eeprom+tcnt; + if ((ret = i2c_transfer(&hdw->i2c_adap, + msg,ARRAY_SIZE(msg))) != 2) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "eeprom fetch set offs err=%d",ret); + kfree(eeprom); + return NULL; + } + } + return eeprom; +} + + +/* Directly call eeprom analysis function within tveeprom. */ +int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) +{ + u8 *eeprom; + struct tveeprom tvdata; + + memset(&tvdata,0,sizeof(tvdata)); + + eeprom = pvr2_eeprom_fetch(hdw); + if (!eeprom) return -EINVAL; + + { + struct i2c_client fake_client; + /* Newer version expects a useless client interface */ + fake_client.addr = hdw->eeprom_addr; + fake_client.adapter = &hdw->i2c_adap; + tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom); + } + + trace_eeprom("eeprom assumed v4l tveeprom module"); + trace_eeprom("eeprom direct call results:"); + trace_eeprom("has_radio=%d",tvdata.has_radio); + trace_eeprom("tuner_type=%d",tvdata.tuner_type); + trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats); + trace_eeprom("audio_processor=%d",tvdata.audio_processor); + trace_eeprom("model=%d",tvdata.model); + trace_eeprom("revision=%d",tvdata.revision); + trace_eeprom("serial_number=%d",tvdata.serial_number); + trace_eeprom("rev_str=%s",tvdata.rev_str); + hdw->tuner_type = tvdata.tuner_type; + hdw->tuner_updated = !0; + hdw->serial_number = tvdata.serial_number; + hdw->std_mask_eeprom = tvdata.tuner_formats; + + kfree(eeprom); + + return 0; +} + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h new file mode 100644 index 000000000000..cca3216f94cc --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h @@ -0,0 +1,39 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_EEPROM_H +#define __PVRUSB2_EEPROM_H + +struct pvr2_hdw; + +int pvr2_eeprom_analyze(struct pvr2_hdw *); + +#endif /* __PVRUSB2_EEPROM_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c new file mode 100644 index 000000000000..e046fdaec5ae --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c @@ -0,0 +1,552 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include // for linux/firmware.h +#include +#include "pvrusb2-util.h" +#include "pvrusb2-encoder.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-fx2-cmd.h" + + + +/* Firmware mailbox flags - definitions found from ivtv */ +#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 +#define IVTV_MBOX_DRIVER_DONE 0x00000002 +#define IVTV_MBOX_DRIVER_BUSY 0x00000001 + +#define MBOX_BASE 0x44 + + +static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, + unsigned int offs, + const u32 *data, unsigned int dlen) +{ + unsigned int idx,addr; + unsigned int bAddr; + int ret; + unsigned int chunkCnt; + + /* + + Format: First byte must be 0x01. Remaining 32 bit words are + spread out into chunks of 7 bytes each, with the first 4 bytes + being the data word (little endian), and the next 3 bytes + being the address where that data word is to be written (big + endian). Repeat request for additional words, with offset + adjusted accordingly. + + */ + while (dlen) { + chunkCnt = 8; + if (chunkCnt > dlen) chunkCnt = dlen; + memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); + bAddr = 0; + hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; + for (idx = 0; idx < chunkCnt; idx++) { + addr = idx + offs; + hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); + hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); + hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); + PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); + bAddr += 7; + } + ret = pvr2_send_request(hdw, + hdw->cmd_buffer,1+(chunkCnt*7), + NULL,0); + if (ret) return ret; + data += chunkCnt; + dlen -= chunkCnt; + offs += chunkCnt; + } + + return 0; +} + + +static int pvr2_encoder_read_words(struct pvr2_hdw *hdw, + unsigned int offs, + u32 *data, unsigned int dlen) +{ + unsigned int idx; + int ret; + unsigned int chunkCnt; + + /* + + Format: First byte must be 0x02 (status check) or 0x28 (read + back block of 32 bit words). Next 6 bytes must be zero, + followed by a single byte of MBOX_BASE+offset for portion to + be read. Returned data is packed set of 32 bits words that + were read. + + */ + + while (dlen) { + chunkCnt = 16; + if (chunkCnt > dlen) chunkCnt = dlen; + if (chunkCnt < 16) chunkCnt = 1; + hdw->cmd_buffer[0] = + ((chunkCnt == 1) ? + FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); + hdw->cmd_buffer[1] = 0; + hdw->cmd_buffer[2] = 0; + hdw->cmd_buffer[3] = 0; + hdw->cmd_buffer[4] = 0; + hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); + hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); + hdw->cmd_buffer[7] = (offs & 0xffu); + ret = pvr2_send_request(hdw, + hdw->cmd_buffer,8, + hdw->cmd_buffer, + (chunkCnt == 1 ? 4 : 16 * 4)); + if (ret) return ret; + + for (idx = 0; idx < chunkCnt; idx++) { + data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); + } + data += chunkCnt; + dlen -= chunkCnt; + offs += chunkCnt; + } + + return 0; +} + + +/* This prototype is set up to be compatible with the + cx2341x_mbox_func prototype in cx2341x.h, which should be in + kernels 2.6.18 or later. We do this so that we can enable + cx2341x.ko to write to our encoder (by handing it a pointer to this + function). For earlier kernels this doesn't really matter. */ +static int pvr2_encoder_cmd(void *ctxt, + u32 cmd, + int arg_cnt_send, + int arg_cnt_recv, + u32 *argp) +{ + unsigned int poll_count; + unsigned int try_count = 0; + int retry_flag; + int ret = 0; + unsigned int idx; + /* These sizes look to be limited by the FX2 firmware implementation */ + u32 wrData[16]; + u32 rdData[16]; + struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; + + + /* + + The encoder seems to speak entirely using blocks 32 bit words. + In ivtv driver terms, this is a mailbox at MBOX_BASE which we + populate with data and watch what the hardware does with it. + The first word is a set of flags used to control the + transaction, the second word is the command to execute, the + third byte is zero (ivtv driver suggests that this is some + kind of return value), and the fourth byte is a specified + timeout (windows driver always uses 0x00060000 except for one + case when it is zero). All successive words are the argument + words for the command. + + First, write out the entire set of words, with the first word + being zero. + + Next, write out just the first word again, but set it to + IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which + probably means "go"). + + Next, read back the return count words. Check the first word, + which should have IVTV_MBOX_FIRMWARE_DONE set. If however + that bit is not set, then the command isn't done so repeat the + read until it is set. + + Finally, write out just the first word again, but set it to + 0x0 this time (which probably means "idle"). + + */ + + if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to write cx23416 command" + " - too many input arguments" + " (was given %u limit %lu)", + arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); + return -EINVAL; + } + + if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to write cx23416 command" + " - too many return arguments" + " (was given %u limit %lu)", + arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); + return -EINVAL; + } + + + LOCK_TAKE(hdw->ctl_lock); do { + + if (!hdw->state_encoder_ok) { + ret = -EIO; + break; + } + + retry_flag = 0; + try_count++; + ret = 0; + wrData[0] = 0; + wrData[1] = cmd; + wrData[2] = 0; + wrData[3] = 0x00060000; + for (idx = 0; idx < arg_cnt_send; idx++) { + wrData[idx+4] = argp[idx]; + } + for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { + wrData[idx+4] = 0; + } + + ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx); + if (ret) break; + wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; + ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); + if (ret) break; + poll_count = 0; + while (1) { + poll_count++; + ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData, + arg_cnt_recv+4); + if (ret) { + break; + } + if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { + break; + } + if (rdData[0] && (poll_count < 1000)) continue; + if (!rdData[0]) { + retry_flag = !0; + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Encoder timed out waiting for us" + "; arranging to retry"); + } else { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "***WARNING*** device's encoder" + " appears to be stuck" + " (status=0x%08x)",rdData[0]); + } + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Encoder command: 0x%02x",cmd); + for (idx = 4; idx < arg_cnt_send; idx++) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Encoder arg%d: 0x%08x", + idx-3,wrData[idx]); + } + ret = -EBUSY; + break; + } + if (retry_flag) { + if (try_count < 20) continue; + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Too many retries..."); + ret = -EBUSY; + } + if (ret) { + del_timer_sync(&hdw->encoder_run_timer); + hdw->state_encoder_ok = 0; + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + "state_encoder_ok", + (hdw->state_encoder_ok ? "true" : "false")); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + "state_encoder_runok", + (hdw->state_encoder_runok ? + "true" : "false")); + } + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Giving up on command." + " This is normally recovered via a firmware" + " reload and re-initialization; concern" + " is only warranted if this happens repeatedly" + " and rapidly."); + break; + } + wrData[0] = 0x7; + for (idx = 0; idx < arg_cnt_recv; idx++) { + argp[idx] = rdData[idx+4]; + } + + wrData[0] = 0x0; + ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); + if (ret) break; + + } while(0); LOCK_GIVE(hdw->ctl_lock); + + return ret; +} + + +static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, + int args, ...) +{ + va_list vl; + unsigned int idx; + u32 data[12]; + + if (args > ARRAY_SIZE(data)) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to write cx23416 command" + " - too many arguments" + " (was given %u limit %lu)", + args, (long unsigned) ARRAY_SIZE(data)); + return -EINVAL; + } + + va_start(vl, args); + for (idx = 0; idx < args; idx++) { + data[idx] = va_arg(vl, u32); + } + va_end(vl); + + return pvr2_encoder_cmd(hdw,cmd,args,0,data); +} + + +/* This implements some extra setup for the encoder that seems to be + specific to the PVR USB2 hardware. */ +static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) +{ + int ret = 0; + int encMisc3Arg = 0; + +#if 0 + /* This inexplicable bit happens in the Hauppauge windows + driver (for both 24xxx and 29xxx devices). However I + currently see no difference in behavior with or without + this stuff. Leave this here as a note of its existence, + but don't use it. */ + LOCK_TAKE(hdw->ctl_lock); do { + u32 dat[1]; + dat[0] = 0x80000640; + pvr2_encoder_write_words(hdw,0x01fe,dat,1); + pvr2_encoder_write_words(hdw,0x023e,dat,1); + } while(0); LOCK_GIVE(hdw->ctl_lock); +#endif + + /* Mike Isely 26-Jan-2006 The windows driver + sends the following list of ENC_MISC commands (for both + 24xxx and 29xxx devices). Meanings are not entirely clear, + however without the ENC_MISC(3,1) command then we risk + random perpetual video corruption whenever the video input + breaks up for a moment (like when switching channels). */ + + +#if 0 + /* This ENC_MISC(5,0) command seems to hurt 29xxx sync + performance on channel changes, but is not a problem on + 24xxx devices. */ + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); +#endif + + /* This ENC_MISC(3,encMisc3Arg) command is critical - without + it there will eventually be video corruption. Also, the + saa7115 case is strange - the Windows driver is passing 1 + regardless of device type but if we have 1 for saa7115 + devices the video turns sluggish. */ + if (hdw->hdw_desc->flag_has_cx25840) { + encMisc3Arg = 1; + } else { + encMisc3Arg = 0; + } + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, + encMisc3Arg,0,0); + + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); + +#if 0 + /* This ENC_MISC(4,1) command is poisonous, so it is commented + out. But I'm leaving it here anyway to document its + existence in the Windows driver. The effect of this + command is that apps displaying the stream become sluggish + with stuttering video. */ + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); +#endif + + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); + + /* prevent the PTSs from slowly drifting away in the generated + MPEG stream */ + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1); + + return ret; +} + +int pvr2_encoder_adjust(struct pvr2_hdw *hdw) +{ + int ret; + ret = cx2341x_update(hdw,pvr2_encoder_cmd, + (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), + &hdw->enc_ctl_state); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Error from cx2341x module code=%d",ret); + } else { + memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, + sizeof(struct cx2341x_mpeg_params)); + hdw->enc_cur_valid = !0; + } + return ret; +} + + +int pvr2_encoder_configure(struct pvr2_hdw *hdw) +{ + int ret; + int val; + pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure" + " (cx2341x module)"); + hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; + hdw->enc_ctl_state.width = hdw->res_hor_val; + hdw->enc_ctl_state.height = hdw->res_ver_val; + hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ? + 0 : 1); + + ret = 0; + + ret |= pvr2_encoder_prep_config(hdw); + + /* saa7115: 0xf0 */ + val = 0xf0; + if (hdw->hdw_desc->flag_has_cx25840) { + /* ivtv cx25840: 0x140 */ + val = 0x140; + } + + if (!ret) ret = pvr2_encoder_vcmd( + hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, + val, val); + + /* setup firmware to notify us about some events (don't know why...) */ + if (!ret) ret = pvr2_encoder_vcmd( + hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, + 0, 0, 0x10000000, 0xffffffff); + + if (!ret) ret = pvr2_encoder_vcmd( + hdw,CX2341X_ENC_SET_VBI_LINE, 5, + 0xffffffff,0,0,0,0); + + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to configure cx23416"); + return ret; + } + + ret = pvr2_encoder_adjust(hdw); + if (ret) return ret; + + ret = pvr2_encoder_vcmd( + hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); + + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to initialize cx23416 video input"); + return ret; + } + + return 0; +} + + +int pvr2_encoder_start(struct pvr2_hdw *hdw) +{ + int status; + + /* unmask some interrupts */ + pvr2_write_register(hdw, 0x0048, 0xbfffffff); + + pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, + hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); + + switch (hdw->active_stream_type) { + case pvr2_config_vbi: + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, + 0x01,0x14); + break; + case pvr2_config_mpeg: + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, + 0,0x13); + break; + default: /* Unhandled cases for now */ + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, + 0,0x13); + break; + } + return status; +} + +int pvr2_encoder_stop(struct pvr2_hdw *hdw) +{ + int status; + + /* mask all interrupts */ + pvr2_write_register(hdw, 0x0048, 0xffffffff); + + switch (hdw->active_stream_type) { + case pvr2_config_vbi: + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, + 0x01,0x01,0x14); + break; + case pvr2_config_mpeg: + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, + 0x01,0,0x13); + break; + default: /* Unhandled cases for now */ + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, + 0x01,0,0x13); + break; + } + + return status; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.h b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h new file mode 100644 index 000000000000..232fefbcd1ac --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h @@ -0,0 +1,42 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_ENCODER_H +#define __PVRUSB2_ENCODER_H + +struct pvr2_hdw; + +int pvr2_encoder_adjust(struct pvr2_hdw *); +int pvr2_encoder_configure(struct pvr2_hdw *); +int pvr2_encoder_start(struct pvr2_hdw *); +int pvr2_encoder_stop(struct pvr2_hdw *); + +#endif /* __PVRUSB2_ENCODER_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h new file mode 100644 index 000000000000..614755ea2ea3 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h @@ -0,0 +1,72 @@ +/* + * + * + * Copyright (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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _PVRUSB2_FX2_CMD_H_ +#define _PVRUSB2_FX2_CMD_H_ + +#define FX2CMD_MEM_WRITE_DWORD 0x01u +#define FX2CMD_MEM_READ_DWORD 0x02u + +#define FX2CMD_HCW_ZILOG_RESET 0x10u /* 1=reset 0=release */ + +#define FX2CMD_MEM_READ_64BYTES 0x28u + +#define FX2CMD_REG_WRITE 0x04u +#define FX2CMD_REG_READ 0x05u +#define FX2CMD_MEMSEL 0x06u + +#define FX2CMD_I2C_WRITE 0x08u +#define FX2CMD_I2C_READ 0x09u + +#define FX2CMD_GET_USB_SPEED 0x0bu + +#define FX2CMD_STREAMING_ON 0x36u +#define FX2CMD_STREAMING_OFF 0x37u + +#define FX2CMD_FWPOST1 0x52u + +#define FX2CMD_POWER_OFF 0xdcu +#define FX2CMD_POWER_ON 0xdeu + +#define FX2CMD_DEEP_RESET 0xddu + +#define FX2CMD_GET_EEPROM_ADDR 0xebu +#define FX2CMD_GET_IR_CODE 0xecu + +#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u +#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u +#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u + +#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u +#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u +#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u +#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u + +#endif /* _PVRUSB2_FX2_CMD_H_ */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h new file mode 100644 index 000000000000..036952f2a3cb --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h @@ -0,0 +1,406 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_HDW_INTERNAL_H +#define __PVRUSB2_HDW_INTERNAL_H + +/* + + This header sets up all the internal structures and definitions needed to + track and coordinate the driver's interaction with the hardware. ONLY + source files which actually implement part of that whole circus should be + including this header. Higher levels, like the external layers to the + various public APIs (V4L, sysfs, etc) should NOT ever include this + private, internal header. This means that pvrusb2-hdw, pvrusb2-encoder, + etc will include this, but pvrusb2-v4l should not. + +*/ + +#include +#include +#include +#include +#include "pvrusb2-hdw.h" +#include "pvrusb2-io.h" +#include +#include +#include +#include "pvrusb2-devattr.h" + +/* Legal values for PVR2_CID_HSM */ +#define PVR2_CVAL_HSM_FAIL 0 +#define PVR2_CVAL_HSM_FULL 1 +#define PVR2_CVAL_HSM_HIGH 2 + +#define PVR2_VID_ENDPOINT 0x84 +#define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */ +#define PVR2_VBI_ENDPOINT 0x88 + +#define PVR2_CTL_BUFFSIZE 64 + +#define FREQTABLE_SIZE 500 + +#define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0) +#define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0) + +typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); +typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); +typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int); +typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *); +typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val); +typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val, + char *,unsigned int,unsigned int *); +typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *, + const char *,unsigned int, + int *mskp,int *valp); +typedef unsigned int (*pvr2_ctlf_get_v4lflags)(struct pvr2_ctrl *); + +/* This structure describes a specific control. A table of these is set up + in pvrusb2-hdw.c. */ +struct pvr2_ctl_info { + /* Control's name suitable for use as an identifier */ + const char *name; + + /* Short description of control */ + const char *desc; + + /* Control's implementation */ + pvr2_ctlf_get_value get_value; /* Get its value */ + pvr2_ctlf_get_value get_def_value; /* Get its default value */ + pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */ + pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */ + pvr2_ctlf_set_value set_value; /* Set its value */ + pvr2_ctlf_check_value check_value; /* Check that value is valid */ + pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */ + pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */ + pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */ + pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */ + pvr2_ctlf_get_v4lflags get_v4lflags;/* Retrieve v4l flags */ + + /* Control's type (int, enum, bitmask) */ + enum pvr2_ctl_type type; + + /* Associated V4L control ID, if any */ + int v4l_id; + + /* Associated driver internal ID, if any */ + int internal_id; + + /* Don't implicitly initialize this control's value */ + int skip_init; + + /* Starting value for this control */ + int default_value; + + /* Type-specific control information */ + union { + struct { /* Integer control */ + long min_value; /* lower limit */ + long max_value; /* upper limit */ + } type_int; + struct { /* enumerated control */ + unsigned int count; /* enum value count */ + const char * const *value_names; /* symbol names */ + } type_enum; + struct { /* bitmask control */ + unsigned int valid_bits; /* bits in use */ + const char **bit_names; /* symbol name/bit */ + } type_bitmask; + } def; +}; + + +/* Same as pvr2_ctl_info, but includes storage for the control description */ +#define PVR2_CTLD_INFO_DESC_SIZE 32 +struct pvr2_ctld_info { + struct pvr2_ctl_info info; + char desc[PVR2_CTLD_INFO_DESC_SIZE]; +}; + +struct pvr2_ctrl { + const struct pvr2_ctl_info *info; + struct pvr2_hdw *hdw; +}; + + + +/* Disposition of firmware1 loading situation */ +#define FW1_STATE_UNKNOWN 0 +#define FW1_STATE_MISSING 1 +#define FW1_STATE_FAILED 2 +#define FW1_STATE_RELOAD 3 +#define FW1_STATE_OK 4 + +/* What state the device is in if it is a hybrid */ +#define PVR2_PATHWAY_UNKNOWN 0 +#define PVR2_PATHWAY_ANALOG 1 +#define PVR2_PATHWAY_DIGITAL 2 + +typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); +#define PVR2_I2C_FUNC_CNT 128 + +/* This structure contains all state data directly needed to + manipulate the hardware (as opposed to complying with a kernel + interface) */ +struct pvr2_hdw { + /* Underlying USB device handle */ + struct usb_device *usb_dev; + struct usb_interface *usb_intf; + + /* Our handle into the v4l2 sub-device architecture */ + struct v4l2_device v4l2_dev; + /* Device description, anything that must adjust behavior based on + device specific info will use information held here. */ + const struct pvr2_device_desc *hdw_desc; + + /* Kernel worker thread handling */ + struct workqueue_struct *workqueue; + struct work_struct workpoll; /* Update driver state */ + + /* Video spigot */ + struct pvr2_stream *vid_stream; + + /* Mutex for all hardware state control */ + struct mutex big_lock_mutex; + int big_lock_held; /* For debugging */ + + /* This is a simple string which identifies the instance of this + driver. It is unique within the set of existing devices, but + there is no attempt to keep the name consistent with the same + physical device each time. */ + char name[32]; + + /* This is a simple string which identifies the physical device + instance itself - if possible. (If not possible, then it is + based on the specific driver instance, similar to name above.) + The idea here is that userspace might hopefully be able to use + this recognize specific tuners. It will encode a serial number, + if available. */ + char identifier[32]; + + /* I2C stuff */ + struct i2c_adapter i2c_adap; + struct i2c_algorithm i2c_algo; + pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; + int i2c_cx25840_hack_state; + int i2c_linked; + + /* IR related */ + unsigned int ir_scheme_active; /* IR scheme as seen from the outside */ + struct IR_i2c_init_data ir_init_data; /* params passed to IR modules */ + + /* Frequency table */ + unsigned int freqTable[FREQTABLE_SIZE]; + unsigned int freqProgSlot; + + /* Stuff for handling low level control interaction with device */ + struct mutex ctl_lock_mutex; + int ctl_lock_held; /* For debugging */ + struct urb *ctl_write_urb; + struct urb *ctl_read_urb; + unsigned char *ctl_write_buffer; + unsigned char *ctl_read_buffer; + int ctl_write_pend_flag; + int ctl_read_pend_flag; + int ctl_timeout_flag; + struct completion ctl_done; + unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE]; + int cmd_debug_state; // Low level command debugging info + unsigned char cmd_debug_code; // + unsigned int cmd_debug_write_len; // + unsigned int cmd_debug_read_len; // + + /* Bits of state that describe what is going on with various parts + of the driver. */ + int state_pathway_ok; /* Pathway config is ok */ + int state_encoder_ok; /* Encoder is operational */ + int state_encoder_run; /* Encoder is running */ + int state_encoder_config; /* Encoder is configured */ + int state_encoder_waitok; /* Encoder pre-wait done */ + int state_encoder_runok; /* Encoder has run for >= .25 sec */ + int state_decoder_run; /* Decoder is running */ + int state_decoder_ready; /* Decoder is stabilized & streamable */ + int state_usbstream_run; /* FX2 is streaming */ + int state_decoder_quiescent; /* Decoder idle for minimal interval */ + int state_pipeline_config; /* Pipeline is configured */ + int state_pipeline_req; /* Somebody wants to stream */ + int state_pipeline_pause; /* Pipeline must be paused */ + int state_pipeline_idle; /* Pipeline not running */ + + /* This is the master state of the driver. It is the combined + result of other bits of state. Examining this will indicate the + overall state of the driver. Values here are one of + PVR2_STATE_xxxx */ + unsigned int master_state; + + /* True if device led is currently on */ + int led_on; + + /* True if states must be re-evaluated */ + int state_stale; + + void (*state_func)(void *); + void *state_data; + + /* Timer for measuring required decoder settling time before we're + allowed to fire it up again. */ + struct timer_list quiescent_timer; + + /* Timer for measuring decoder stabilization time, which is the + amount of time we need to let the decoder run before we can + trust its output (otherwise the encoder might see garbage and + then fail to start correctly). */ + struct timer_list decoder_stabilization_timer; + + /* Timer for measuring encoder pre-wait time */ + struct timer_list encoder_wait_timer; + + /* Timer for measuring encoder minimum run time */ + struct timer_list encoder_run_timer; + + /* Place to block while waiting for state changes */ + wait_queue_head_t state_wait_data; + + + int force_dirty; /* consider all controls dirty if true */ + int flag_ok; /* device in known good state */ + int flag_modulefail; /* true if at least one module failed to load */ + int flag_disconnected; /* flag_ok == 0 due to disconnect */ + int flag_init_ok; /* true if structure is fully initialized */ + int fw1_state; /* current situation with fw1 */ + int pathway_state; /* one of PVR2_PATHWAY_xxx */ + int flag_decoder_missed;/* We've noticed missing decoder */ + int flag_tripped; /* Indicates overall failure to start */ + + unsigned int decoder_client_id; + + // CPU firmware info (used to help find / save firmware data) + char *fw_buffer; + unsigned int fw_size; + int fw_cpu_flag; /* True if we are dealing with the CPU */ + + /* Tuner / frequency control stuff */ + unsigned int tuner_type; + int tuner_updated; + unsigned int freqValTelevision; /* Current freq for tv mode */ + unsigned int freqValRadio; /* Current freq for radio mode */ + unsigned int freqSlotTelevision; /* Current slot for tv mode */ + unsigned int freqSlotRadio; /* Current slot for radio mode */ + unsigned int freqSelector; /* 0=radio 1=television */ + int freqDirty; + + /* Current tuner info - this information is polled from the I2C bus */ + struct v4l2_tuner tuner_signal_info; + int tuner_signal_stale; + + /* Cropping capability info */ + struct v4l2_cropcap cropcap_info; + int cropcap_stale; + + /* Video standard handling */ + v4l2_std_id std_mask_eeprom; // Hardware supported selections + v4l2_std_id std_mask_avail; // Which standards we may select from + v4l2_std_id std_mask_cur; // Currently selected standard(s) + int std_enum_cur; // selected standard enumeration value + int std_dirty; // True if std_mask_cur has changed + struct pvr2_ctl_info std_info_enum; + struct pvr2_ctl_info std_info_avail; + struct pvr2_ctl_info std_info_cur; + struct pvr2_ctl_info std_info_detect; + + // Generated string names, one per actual V4L2 standard + const char *std_mask_ptrs[32]; + char std_mask_names[32][16]; + + int unit_number; /* ID for driver instance */ + unsigned long serial_number; /* ID for hardware itself */ + + char bus_info[32]; /* Bus location info */ + + /* Minor numbers used by v4l logic (yes, this is a hack, as there + should be no v4l junk here). Probably a better way to do this. */ + int v4l_minor_number_video; + int v4l_minor_number_vbi; + int v4l_minor_number_radio; + + /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */ + unsigned int input_avail_mask; + /* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */ + unsigned int input_allowed_mask; + + /* Location of eeprom or a negative number if none */ + int eeprom_addr; + + enum pvr2_config active_stream_type; + enum pvr2_config desired_stream_type; + + /* Control state needed for cx2341x module */ + struct cx2341x_mpeg_params enc_cur_state; + struct cx2341x_mpeg_params enc_ctl_state; + /* True if an encoder attribute has changed */ + int enc_stale; + /* True if an unsafe encoder attribute has changed */ + int enc_unsafe_stale; + /* True if enc_cur_state is valid */ + int enc_cur_valid; + + /* Control state */ +#define VCREATE_DATA(lab) int lab##_val; int lab##_dirty + VCREATE_DATA(brightness); + VCREATE_DATA(contrast); + VCREATE_DATA(saturation); + VCREATE_DATA(hue); + VCREATE_DATA(volume); + VCREATE_DATA(balance); + VCREATE_DATA(bass); + VCREATE_DATA(treble); + VCREATE_DATA(mute); + VCREATE_DATA(cropl); + VCREATE_DATA(cropt); + VCREATE_DATA(cropw); + VCREATE_DATA(croph); + VCREATE_DATA(input); + VCREATE_DATA(audiomode); + VCREATE_DATA(res_hor); + VCREATE_DATA(res_ver); + VCREATE_DATA(srate); +#undef VCREATE_DATA + + struct pvr2_ctld_info *mpeg_ctrl_info; + + struct pvr2_ctrl *controls; + unsigned int control_cnt; +}; + +/* This function gets the current frequency */ +unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); + +void pvr2_hdw_status_poll(struct pvr2_hdw *); + +#endif /* __PVRUSB2_HDW_INTERNAL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c new file mode 100644 index 000000000000..fb828ba1dbbe --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -0,0 +1,5202 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pvrusb2.h" +#include "pvrusb2-std.h" +#include "pvrusb2-util.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-i2c-core.h" +#include "pvrusb2-eeprom.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-encoder.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-fx2-cmd.h" +#include "pvrusb2-wm8775.h" +#include "pvrusb2-video-v4l.h" +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-cs53l32a.h" +#include "pvrusb2-audio.h" + +#define TV_MIN_FREQ 55250000L +#define TV_MAX_FREQ 850000000L + +/* This defines a minimum interval that the decoder must remain quiet + before we are allowed to start it running. */ +#define TIME_MSEC_DECODER_WAIT 50 + +/* This defines a minimum interval that the decoder must be allowed to run + before we can safely begin using its streaming output. */ +#define TIME_MSEC_DECODER_STABILIZATION_WAIT 300 + +/* This defines a minimum interval that the encoder must remain quiet + before we are allowed to configure it. */ +#define TIME_MSEC_ENCODER_WAIT 50 + +/* This defines the minimum interval that the encoder must successfully run + before we consider that the encoder has run at least once since its + firmware has been loaded. This measurement is in important for cases + where we can't do something until we know that the encoder has been run + at least once. */ +#define TIME_MSEC_ENCODER_OK 250 + +static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; +static DEFINE_MUTEX(pvr2_unit_mtx); + +static int ctlchg; +static int procreload; +static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; +static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; +static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; +static int init_pause_msec; + +module_param(ctlchg, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value"); +module_param(init_pause_msec, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay"); +module_param(procreload, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(procreload, + "Attempt init failure recovery with firmware reload"); +module_param_array(tuner, int, NULL, 0444); +MODULE_PARM_DESC(tuner,"specify installed tuner type"); +module_param_array(video_std, int, NULL, 0444); +MODULE_PARM_DESC(video_std,"specify initial video standard"); +module_param_array(tolerance, int, NULL, 0444); +MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); + +/* US Broadcast channel 3 (61.25 MHz), to help with testing */ +static int default_tv_freq = 61250000L; +/* 104.3 MHz, a usable FM station for my area */ +static int default_radio_freq = 104300000L; + +module_param_named(tv_freq, default_tv_freq, int, 0444); +MODULE_PARM_DESC(tv_freq, "specify initial television frequency"); +module_param_named(radio_freq, default_radio_freq, int, 0444); +MODULE_PARM_DESC(radio_freq, "specify initial radio frequency"); + +#define PVR2_CTL_WRITE_ENDPOINT 0x01 +#define PVR2_CTL_READ_ENDPOINT 0x81 + +#define PVR2_GPIO_IN 0x9008 +#define PVR2_GPIO_OUT 0x900c +#define PVR2_GPIO_DIR 0x9020 + +#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__) + +#define PVR2_FIRMWARE_ENDPOINT 0x02 + +/* size of a firmware chunk */ +#define FIRMWARE_CHUNK_SIZE 0x2000 + +typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *, + struct v4l2_subdev *); + +static const pvr2_subdev_update_func pvr2_module_update_functions[] = { + [PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update, + [PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update, + [PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update, + [PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update, + [PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update, +}; + +static const char *module_names[] = { + [PVR2_CLIENT_ID_MSP3400] = "msp3400", + [PVR2_CLIENT_ID_CX25840] = "cx25840", + [PVR2_CLIENT_ID_SAA7115] = "saa7115", + [PVR2_CLIENT_ID_TUNER] = "tuner", + [PVR2_CLIENT_ID_DEMOD] = "tuner", + [PVR2_CLIENT_ID_CS53L32A] = "cs53l32a", + [PVR2_CLIENT_ID_WM8775] = "wm8775", +}; + + +static const unsigned char *module_i2c_addresses[] = { + [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63", + [PVR2_CLIENT_ID_DEMOD] = "\x43", + [PVR2_CLIENT_ID_MSP3400] = "\x40", + [PVR2_CLIENT_ID_SAA7115] = "\x21", + [PVR2_CLIENT_ID_WM8775] = "\x1b", + [PVR2_CLIENT_ID_CX25840] = "\x44", + [PVR2_CLIENT_ID_CS53L32A] = "\x11", +}; + + +static const char *ir_scheme_names[] = { + [PVR2_IR_SCHEME_NONE] = "none", + [PVR2_IR_SCHEME_29XXX] = "29xxx", + [PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)", + [PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)", + [PVR2_IR_SCHEME_ZILOG] = "Zilog", +}; + + +/* Define the list of additional controls we'll dynamically construct based + on query of the cx2341x module. */ +struct pvr2_mpeg_ids { + const char *strid; + int id; +}; +static const struct pvr2_mpeg_ids mpeg_ids[] = { + { + .strid = "audio_layer", + .id = V4L2_CID_MPEG_AUDIO_ENCODING, + },{ + .strid = "audio_bitrate", + .id = V4L2_CID_MPEG_AUDIO_L2_BITRATE, + },{ + /* Already using audio_mode elsewhere :-( */ + .strid = "mpeg_audio_mode", + .id = V4L2_CID_MPEG_AUDIO_MODE, + },{ + .strid = "mpeg_audio_mode_extension", + .id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + },{ + .strid = "audio_emphasis", + .id = V4L2_CID_MPEG_AUDIO_EMPHASIS, + },{ + .strid = "audio_crc", + .id = V4L2_CID_MPEG_AUDIO_CRC, + },{ + .strid = "video_aspect", + .id = V4L2_CID_MPEG_VIDEO_ASPECT, + },{ + .strid = "video_b_frames", + .id = V4L2_CID_MPEG_VIDEO_B_FRAMES, + },{ + .strid = "video_gop_size", + .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE, + },{ + .strid = "video_gop_closure", + .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, + },{ + .strid = "video_bitrate_mode", + .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + },{ + .strid = "video_bitrate", + .id = V4L2_CID_MPEG_VIDEO_BITRATE, + },{ + .strid = "video_bitrate_peak", + .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + },{ + .strid = "video_temporal_decimation", + .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, + },{ + .strid = "stream_type", + .id = V4L2_CID_MPEG_STREAM_TYPE, + },{ + .strid = "video_spatial_filter_mode", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + },{ + .strid = "video_spatial_filter", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + },{ + .strid = "video_luma_spatial_filter_type", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + },{ + .strid = "video_chroma_spatial_filter_type", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + },{ + .strid = "video_temporal_filter_mode", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + },{ + .strid = "video_temporal_filter", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + },{ + .strid = "video_median_filter_type", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + },{ + .strid = "video_luma_median_filter_top", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + },{ + .strid = "video_luma_median_filter_bottom", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + },{ + .strid = "video_chroma_median_filter_top", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + },{ + .strid = "video_chroma_median_filter_bottom", + .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + } +}; +#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids) + + +static const char *control_values_srate[] = { + [V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100] = "44.1 kHz", + [V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000] = "48 kHz", + [V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000] = "32 kHz", +}; + + + +static const char *control_values_input[] = { + [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ + [PVR2_CVAL_INPUT_DTV] = "dtv", + [PVR2_CVAL_INPUT_RADIO] = "radio", + [PVR2_CVAL_INPUT_SVIDEO] = "s-video", + [PVR2_CVAL_INPUT_COMPOSITE] = "composite", +}; + + +static const char *control_values_audiomode[] = { + [V4L2_TUNER_MODE_MONO] = "Mono", + [V4L2_TUNER_MODE_STEREO] = "Stereo", + [V4L2_TUNER_MODE_LANG1] = "Lang1", + [V4L2_TUNER_MODE_LANG2] = "Lang2", + [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2", +}; + + +static const char *control_values_hsm[] = { + [PVR2_CVAL_HSM_FAIL] = "Fail", + [PVR2_CVAL_HSM_HIGH] = "High", + [PVR2_CVAL_HSM_FULL] = "Full", +}; + + +static const char *pvr2_state_names[] = { + [PVR2_STATE_NONE] = "none", + [PVR2_STATE_DEAD] = "dead", + [PVR2_STATE_COLD] = "cold", + [PVR2_STATE_WARM] = "warm", + [PVR2_STATE_ERROR] = "error", + [PVR2_STATE_READY] = "ready", + [PVR2_STATE_RUN] = "run", +}; + + +struct pvr2_fx2cmd_descdef { + unsigned char id; + unsigned char *desc; +}; + +static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { + {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, + {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, + {FX2CMD_HCW_ZILOG_RESET, "zilog IR reset control"}, + {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, + {FX2CMD_REG_WRITE, "write encoder register"}, + {FX2CMD_REG_READ, "read encoder register"}, + {FX2CMD_MEMSEL, "encoder memsel"}, + {FX2CMD_I2C_WRITE, "i2c write"}, + {FX2CMD_I2C_READ, "i2c read"}, + {FX2CMD_GET_USB_SPEED, "get USB speed"}, + {FX2CMD_STREAMING_ON, "stream on"}, + {FX2CMD_STREAMING_OFF, "stream off"}, + {FX2CMD_FWPOST1, "fwpost1"}, + {FX2CMD_POWER_OFF, "power off"}, + {FX2CMD_POWER_ON, "power on"}, + {FX2CMD_DEEP_RESET, "deep reset"}, + {FX2CMD_GET_EEPROM_ADDR, "get rom addr"}, + {FX2CMD_GET_IR_CODE, "get IR code"}, + {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"}, + {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"}, + {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"}, + {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"}, + {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"}, + {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"}, + {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"}, +}; + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); +static void pvr2_hdw_state_sched(struct pvr2_hdw *); +static int pvr2_hdw_state_eval(struct pvr2_hdw *); +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); +static void pvr2_hdw_worker_poll(struct work_struct *work); +static int pvr2_hdw_wait(struct pvr2_hdw *,int state); +static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); +static void pvr2_hdw_state_log_state(struct pvr2_hdw *); +static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); +static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw); +static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); +static void pvr2_hdw_quiescent_timeout(unsigned long); +static void pvr2_hdw_decoder_stabilization_timeout(unsigned long); +static void pvr2_hdw_encoder_wait_timeout(unsigned long); +static void pvr2_hdw_encoder_run_timeout(unsigned long); +static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); +static int pvr2_send_request_ex(struct pvr2_hdw *hdw, + unsigned int timeout,int probe_fl, + void *write_data,unsigned int write_len, + void *read_data,unsigned int read_len); +static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw); +static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw); + +static void trace_stbit(const char *name,int val) +{ + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + name,(val ? "true" : "false")); +} + +static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { + *vp = hdw->freqTable[hdw->freqProgSlot-1]; + } else { + *vp = 0; + } + return 0; +} + +static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + unsigned int slotId = hdw->freqProgSlot; + if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) { + hdw->freqTable[slotId-1] = v; + /* Handle side effects correctly - if we're tuned to this + slot, then forgot the slot id relation since the stored + frequency has been changed. */ + if (hdw->freqSelector) { + if (hdw->freqSlotRadio == slotId) { + hdw->freqSlotRadio = 0; + } + } else { + if (hdw->freqSlotTelevision == slotId) { + hdw->freqSlotTelevision = 0; + } + } + } + return 0; +} + +static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->freqProgSlot; + return 0; +} + +static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((v >= 0) && (v <= FREQTABLE_SIZE)) { + hdw->freqProgSlot = v; + } + return 0; +} + +static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) +{ + struct pvr2_hdw *hdw = cptr->hdw; + *vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision; + return 0; +} + +static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId) +{ + unsigned freq = 0; + struct pvr2_hdw *hdw = cptr->hdw; + if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0; + if (slotId > 0) { + freq = hdw->freqTable[slotId-1]; + if (!freq) return 0; + pvr2_hdw_set_cur_freq(hdw,freq); + } + if (hdw->freqSelector) { + hdw->freqSlotRadio = slotId; + } else { + hdw->freqSlotTelevision = slotId; + } + return 0; +} + +static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = pvr2_hdw_get_cur_freq(cptr->hdw); + return 0; +} + +static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->freqDirty != 0; +} + +static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->freqDirty = 0; +} + +static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) +{ + pvr2_hdw_set_cur_freq(cptr->hdw,v); + return 0; +} + +static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *left = cap->bounds.left; + return 0; +} + +static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *left = cap->bounds.left; + if (cap->bounds.width > cptr->hdw->cropw_val) { + *left += cap->bounds.width - cptr->hdw->cropw_val; + } + return 0; +} + +static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *top = cap->bounds.top; + return 0; +} + +static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *top = cap->bounds.top; + if (cap->bounds.height > cptr->hdw->croph_val) { + *top += cap->bounds.height - cptr->hdw->croph_val; + } + return 0; +} + +static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat, bleftend, cleft; + + stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + bleftend = cap->bounds.left+cap->bounds.width; + cleft = cptr->hdw->cropl_val; + + *width = cleft < bleftend ? bleftend-cleft : 0; + return 0; +} + +static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat, btopend, ctop; + + stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + btopend = cap->bounds.top+cap->bounds.height; + ctop = cptr->hdw->cropt_val; + + *height = ctop < btopend ? btopend-ctop : 0; + return 0; +} + +static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.left; + return 0; +} + +static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.top; + return 0; +} + +static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.width; + return 0; +} + +static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.height; + return 0; +} + +static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.left; + return 0; +} + +static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.top; + return 0; +} + +static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.width; + return 0; +} + +static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.height; + return 0; +} + +static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->pixelaspect.numerator; + return 0; +} + +static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->pixelaspect.denominator; + return 0; +} + +static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) +{ + /* Actual maximum depends on the video standard in effect. */ + if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) { + *vp = 480; + } else { + *vp = 576; + } + return 0; +} + +static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) +{ + /* Actual minimum depends on device digitizer type. */ + if (cptr->hdw->hdw_desc->flag_has_cx25840) { + *vp = 75; + } else { + *vp = 17; + } + return 0; +} + +static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->input_val; + return 0; +} + +static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) +{ + return ((1 << v) & cptr->hdw->input_allowed_mask) != 0; +} + +static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) +{ + return pvr2_hdw_set_input(cptr->hdw,v); +} + +static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->input_dirty != 0; +} + +static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr) +{ + cptr->hdw->input_dirty = 0; +} + + +static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) +{ + unsigned long fv; + struct pvr2_hdw *hdw = cptr->hdw; + if (hdw->tuner_signal_stale) { + pvr2_hdw_status_poll(hdw); + } + fv = hdw->tuner_signal_info.rangehigh; + if (!fv) { + /* Safety fallback */ + *vp = TV_MAX_FREQ; + return 0; + } + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; + } + *vp = fv; + return 0; +} + +static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp) +{ + unsigned long fv; + struct pvr2_hdw *hdw = cptr->hdw; + if (hdw->tuner_signal_stale) { + pvr2_hdw_status_poll(hdw); + } + fv = hdw->tuner_signal_info.rangelow; + if (!fv) { + /* Safety fallback */ + *vp = TV_MIN_FREQ; + return 0; + } + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; + } + *vp = fv; + return 0; +} + +static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->enc_stale != 0; +} + +static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->enc_stale = 0; + cptr->hdw->enc_unsafe_stale = 0; +} + +static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp) +{ + int ret; + struct v4l2_ext_controls cs; + struct v4l2_ext_control c1; + memset(&cs,0,sizeof(cs)); + memset(&c1,0,sizeof(c1)); + cs.controls = &c1; + cs.count = 1; + c1.id = cptr->info->v4l_id; + ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs, + VIDIOC_G_EXT_CTRLS); + if (ret) return ret; + *vp = c1.value; + return 0; +} + +static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v) +{ + int ret; + struct pvr2_hdw *hdw = cptr->hdw; + struct v4l2_ext_controls cs; + struct v4l2_ext_control c1; + memset(&cs,0,sizeof(cs)); + memset(&c1,0,sizeof(c1)); + cs.controls = &c1; + cs.count = 1; + c1.id = cptr->info->v4l_id; + c1.value = v; + ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, + hdw->state_encoder_run, &cs, + VIDIOC_S_EXT_CTRLS); + if (ret == -EBUSY) { + /* Oops. cx2341x is telling us it's not safe to change + this control while we're capturing. Make a note of this + fact so that the pipeline will be stopped the next time + controls are committed. Then go on ahead and store this + change anyway. */ + ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, + 0, &cs, + VIDIOC_S_EXT_CTRLS); + if (!ret) hdw->enc_unsafe_stale = !0; + } + if (ret) return ret; + hdw->enc_stale = !0; + return 0; +} + +static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr) +{ + struct v4l2_queryctrl qctrl; + struct pvr2_ctl_info *info; + qctrl.id = cptr->info->v4l_id; + cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl); + /* Strip out the const so we can adjust a function pointer. It's + OK to do this here because we know this is a dynamically created + control, so the underlying storage for the info pointer is (a) + private to us, and (b) not in read-only storage. Either we do + this or we significantly complicate the underlying control + implementation. */ + info = (struct pvr2_ctl_info *)(cptr->info); + if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) { + if (info->set_value) { + info->set_value = NULL; + } + } else { + if (!(info->set_value)) { + info->set_value = ctrl_cx2341x_set; + } + } + return qctrl.flags; +} + +static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->state_pipeline_req; + return 0; +} + +static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->master_state; + return 0; +} + +static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) +{ + int result = pvr2_hdw_is_hsm(cptr->hdw); + *vp = PVR2_CVAL_HSM_FULL; + if (result < 0) *vp = PVR2_CVAL_HSM_FAIL; + if (result) *vp = PVR2_CVAL_HSM_HIGH; + return 0; +} + +static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp) +{ + *vp = pvr2_hdw_get_detected_std(cptr->hdw); + return 0; +} + +static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->std_mask_avail; + return 0; +} + +static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + v4l2_std_id ns; + ns = hdw->std_mask_avail; + ns = (ns & ~m) | (v & m); + if (ns == hdw->std_mask_avail) return 0; + hdw->std_mask_avail = ns; + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; + return 0; +} + +static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val, + char *bufPtr,unsigned int bufSize, + unsigned int *len) +{ + *len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val); + return 0; +} + +static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr, + const char *bufPtr,unsigned int bufSize, + int *mskp,int *valp) +{ + int ret; + v4l2_std_id id; + ret = pvr2_std_str_to_id(&id,bufPtr,bufSize); + if (ret < 0) return ret; + if (mskp) *mskp = id; + if (valp) *valp = id; + return 0; +} + +static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->std_mask_cur; + return 0; +} + +static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + v4l2_std_id ns; + ns = hdw->std_mask_cur; + ns = (ns & ~m) | (v & m); + if (ns == hdw->std_mask_cur) return 0; + hdw->std_mask_cur = ns; + hdw->std_dirty = !0; + return 0; +} + +static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->std_dirty != 0; +} + +static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->std_dirty = 0; +} + +static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) +{ + struct pvr2_hdw *hdw = cptr->hdw; + pvr2_hdw_status_poll(hdw); + *vp = hdw->tuner_signal_info.signal; + return 0; +} + +static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) +{ + int val = 0; + unsigned int subchan; + struct pvr2_hdw *hdw = cptr->hdw; + pvr2_hdw_status_poll(hdw); + subchan = hdw->tuner_signal_info.rxsubchans; + if (subchan & V4L2_TUNER_SUB_MONO) { + val |= (1 << V4L2_TUNER_MODE_MONO); + } + if (subchan & V4L2_TUNER_SUB_STEREO) { + val |= (1 << V4L2_TUNER_MODE_STEREO); + } + if (subchan & V4L2_TUNER_SUB_LANG1) { + val |= (1 << V4L2_TUNER_MODE_LANG1); + } + if (subchan & V4L2_TUNER_SUB_LANG2) { + val |= (1 << V4L2_TUNER_MODE_LANG2); + } + *vp = val; + return 0; +} + + +#define DEFINT(vmin,vmax) \ + .type = pvr2_ctl_int, \ + .def.type_int.min_value = vmin, \ + .def.type_int.max_value = vmax + +#define DEFENUM(tab) \ + .type = pvr2_ctl_enum, \ + .def.type_enum.count = ARRAY_SIZE(tab), \ + .def.type_enum.value_names = tab + +#define DEFBOOL \ + .type = pvr2_ctl_bool + +#define DEFMASK(msk,tab) \ + .type = pvr2_ctl_bitmask, \ + .def.type_bitmask.valid_bits = msk, \ + .def.type_bitmask.bit_names = tab + +#define DEFREF(vname) \ + .set_value = ctrl_set_##vname, \ + .get_value = ctrl_get_##vname, \ + .is_dirty = ctrl_isdirty_##vname, \ + .clear_dirty = ctrl_cleardirty_##vname + + +#define VCREATE_FUNCS(vname) \ +static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \ +{*vp = cptr->hdw->vname##_val; return 0;} \ +static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \ +{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \ +static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \ +{return cptr->hdw->vname##_dirty != 0;} \ +static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \ +{cptr->hdw->vname##_dirty = 0;} + +VCREATE_FUNCS(brightness) +VCREATE_FUNCS(contrast) +VCREATE_FUNCS(saturation) +VCREATE_FUNCS(hue) +VCREATE_FUNCS(volume) +VCREATE_FUNCS(balance) +VCREATE_FUNCS(bass) +VCREATE_FUNCS(treble) +VCREATE_FUNCS(mute) +VCREATE_FUNCS(cropl) +VCREATE_FUNCS(cropt) +VCREATE_FUNCS(cropw) +VCREATE_FUNCS(croph) +VCREATE_FUNCS(audiomode) +VCREATE_FUNCS(res_hor) +VCREATE_FUNCS(res_ver) +VCREATE_FUNCS(srate) + +/* Table definition of all controls which can be manipulated */ +static const struct pvr2_ctl_info control_defs[] = { + { + .v4l_id = V4L2_CID_BRIGHTNESS, + .desc = "Brightness", + .name = "brightness", + .default_value = 128, + DEFREF(brightness), + DEFINT(0,255), + },{ + .v4l_id = V4L2_CID_CONTRAST, + .desc = "Contrast", + .name = "contrast", + .default_value = 68, + DEFREF(contrast), + DEFINT(0,127), + },{ + .v4l_id = V4L2_CID_SATURATION, + .desc = "Saturation", + .name = "saturation", + .default_value = 64, + DEFREF(saturation), + DEFINT(0,127), + },{ + .v4l_id = V4L2_CID_HUE, + .desc = "Hue", + .name = "hue", + .default_value = 0, + DEFREF(hue), + DEFINT(-128,127), + },{ + .v4l_id = V4L2_CID_AUDIO_VOLUME, + .desc = "Volume", + .name = "volume", + .default_value = 62000, + DEFREF(volume), + DEFINT(0,65535), + },{ + .v4l_id = V4L2_CID_AUDIO_BALANCE, + .desc = "Balance", + .name = "balance", + .default_value = 0, + DEFREF(balance), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_BASS, + .desc = "Bass", + .name = "bass", + .default_value = 0, + DEFREF(bass), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_TREBLE, + .desc = "Treble", + .name = "treble", + .default_value = 0, + DEFREF(treble), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_MUTE, + .desc = "Mute", + .name = "mute", + .default_value = 0, + DEFREF(mute), + DEFBOOL, + }, { + .desc = "Capture crop left margin", + .name = "crop_left", + .internal_id = PVR2_CID_CROPL, + .default_value = 0, + DEFREF(cropl), + DEFINT(-129, 340), + .get_min_value = ctrl_cropl_min_get, + .get_max_value = ctrl_cropl_max_get, + .get_def_value = ctrl_get_cropcapdl, + }, { + .desc = "Capture crop top margin", + .name = "crop_top", + .internal_id = PVR2_CID_CROPT, + .default_value = 0, + DEFREF(cropt), + DEFINT(-35, 544), + .get_min_value = ctrl_cropt_min_get, + .get_max_value = ctrl_cropt_max_get, + .get_def_value = ctrl_get_cropcapdt, + }, { + .desc = "Capture crop width", + .name = "crop_width", + .internal_id = PVR2_CID_CROPW, + .default_value = 720, + DEFREF(cropw), + DEFINT(0, 864), + .get_max_value = ctrl_cropw_max_get, + .get_def_value = ctrl_get_cropcapdw, + }, { + .desc = "Capture crop height", + .name = "crop_height", + .internal_id = PVR2_CID_CROPH, + .default_value = 480, + DEFREF(croph), + DEFINT(0, 576), + .get_max_value = ctrl_croph_max_get, + .get_def_value = ctrl_get_cropcapdh, + }, { + .desc = "Capture capability pixel aspect numerator", + .name = "cropcap_pixel_numerator", + .internal_id = PVR2_CID_CROPCAPPAN, + .get_value = ctrl_get_cropcappan, + }, { + .desc = "Capture capability pixel aspect denominator", + .name = "cropcap_pixel_denominator", + .internal_id = PVR2_CID_CROPCAPPAD, + .get_value = ctrl_get_cropcappad, + }, { + .desc = "Capture capability bounds top", + .name = "cropcap_bounds_top", + .internal_id = PVR2_CID_CROPCAPBT, + .get_value = ctrl_get_cropcapbt, + }, { + .desc = "Capture capability bounds left", + .name = "cropcap_bounds_left", + .internal_id = PVR2_CID_CROPCAPBL, + .get_value = ctrl_get_cropcapbl, + }, { + .desc = "Capture capability bounds width", + .name = "cropcap_bounds_width", + .internal_id = PVR2_CID_CROPCAPBW, + .get_value = ctrl_get_cropcapbw, + }, { + .desc = "Capture capability bounds height", + .name = "cropcap_bounds_height", + .internal_id = PVR2_CID_CROPCAPBH, + .get_value = ctrl_get_cropcapbh, + },{ + .desc = "Video Source", + .name = "input", + .internal_id = PVR2_CID_INPUT, + .default_value = PVR2_CVAL_INPUT_TV, + .check_value = ctrl_check_input, + DEFREF(input), + DEFENUM(control_values_input), + },{ + .desc = "Audio Mode", + .name = "audio_mode", + .internal_id = PVR2_CID_AUDIOMODE, + .default_value = V4L2_TUNER_MODE_STEREO, + DEFREF(audiomode), + DEFENUM(control_values_audiomode), + },{ + .desc = "Horizontal capture resolution", + .name = "resolution_hor", + .internal_id = PVR2_CID_HRES, + .default_value = 720, + DEFREF(res_hor), + DEFINT(19,720), + },{ + .desc = "Vertical capture resolution", + .name = "resolution_ver", + .internal_id = PVR2_CID_VRES, + .default_value = 480, + DEFREF(res_ver), + DEFINT(17,576), + /* Hook in check for video standard and adjust maximum + depending on the standard. */ + .get_max_value = ctrl_vres_max_get, + .get_min_value = ctrl_vres_min_get, + },{ + .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + .desc = "Audio Sampling Frequency", + .name = "srate", + DEFREF(srate), + DEFENUM(control_values_srate), + },{ + .desc = "Tuner Frequency (Hz)", + .name = "frequency", + .internal_id = PVR2_CID_FREQUENCY, + .default_value = 0, + .set_value = ctrl_freq_set, + .get_value = ctrl_freq_get, + .is_dirty = ctrl_freq_is_dirty, + .clear_dirty = ctrl_freq_clear_dirty, + DEFINT(0,0), + /* Hook in check for input value (tv/radio) and adjust + max/min values accordingly */ + .get_max_value = ctrl_freq_max_get, + .get_min_value = ctrl_freq_min_get, + },{ + .desc = "Channel", + .name = "channel", + .set_value = ctrl_channel_set, + .get_value = ctrl_channel_get, + DEFINT(0,FREQTABLE_SIZE), + },{ + .desc = "Channel Program Frequency", + .name = "freq_table_value", + .set_value = ctrl_channelfreq_set, + .get_value = ctrl_channelfreq_get, + DEFINT(0,0), + /* Hook in check for input value (tv/radio) and adjust + max/min values accordingly */ + .get_max_value = ctrl_freq_max_get, + .get_min_value = ctrl_freq_min_get, + },{ + .desc = "Channel Program ID", + .name = "freq_table_channel", + .set_value = ctrl_channelprog_set, + .get_value = ctrl_channelprog_get, + DEFINT(0,FREQTABLE_SIZE), + },{ + .desc = "Streaming Enabled", + .name = "streaming_enabled", + .get_value = ctrl_streamingenabled_get, + DEFBOOL, + },{ + .desc = "USB Speed", + .name = "usb_speed", + .get_value = ctrl_hsm_get, + DEFENUM(control_values_hsm), + },{ + .desc = "Master State", + .name = "master_state", + .get_value = ctrl_masterstate_get, + DEFENUM(pvr2_state_names), + },{ + .desc = "Signal Present", + .name = "signal_present", + .get_value = ctrl_signal_get, + DEFINT(0,65535), + },{ + .desc = "Audio Modes Present", + .name = "audio_modes_present", + .get_value = ctrl_audio_modes_present_get, + /* For this type we "borrow" the V4L2_TUNER_MODE enum from + v4l. Nothing outside of this module cares about this, + but I reuse it in order to also reuse the + control_values_audiomode string table. */ + DEFMASK(((1 << V4L2_TUNER_MODE_MONO)| + (1 << V4L2_TUNER_MODE_STEREO)| + (1 << V4L2_TUNER_MODE_LANG1)| + (1 << V4L2_TUNER_MODE_LANG2)), + control_values_audiomode), + },{ + .desc = "Video Standards Available Mask", + .name = "video_standard_mask_available", + .internal_id = PVR2_CID_STDAVAIL, + .skip_init = !0, + .get_value = ctrl_stdavail_get, + .set_value = ctrl_stdavail_set, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, + },{ + .desc = "Video Standards In Use Mask", + .name = "video_standard_mask_active", + .internal_id = PVR2_CID_STDCUR, + .skip_init = !0, + .get_value = ctrl_stdcur_get, + .set_value = ctrl_stdcur_set, + .is_dirty = ctrl_stdcur_is_dirty, + .clear_dirty = ctrl_stdcur_clear_dirty, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, + },{ + .desc = "Video Standards Detected Mask", + .name = "video_standard_mask_detected", + .internal_id = PVR2_CID_STDDETECT, + .skip_init = !0, + .get_value = ctrl_stddetect_get, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, + } +}; + +#define CTRLDEF_COUNT ARRAY_SIZE(control_defs) + + +const char *pvr2_config_get_name(enum pvr2_config cfg) +{ + switch (cfg) { + case pvr2_config_empty: return "empty"; + case pvr2_config_mpeg: return "mpeg"; + case pvr2_config_vbi: return "vbi"; + case pvr2_config_pcm: return "pcm"; + case pvr2_config_rawvideo: return "raw video"; + } + return ""; +} + + +struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw) +{ + return hdw->usb_dev; +} + + +unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw) +{ + return hdw->serial_number; +} + + +const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw) +{ + return hdw->bus_info; +} + + +const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw) +{ + return hdw->identifier; +} + + +unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) +{ + return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio; +} + +/* Set the currently tuned frequency and account for all possible + driver-core side effects of this action. */ +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) +{ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + if (hdw->freqSelector) { + /* Swing over to radio frequency selection */ + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } + if (hdw->freqValRadio != val) { + hdw->freqValRadio = val; + hdw->freqSlotRadio = 0; + hdw->freqDirty = !0; + } + } else { + if (!(hdw->freqSelector)) { + /* Swing over to television frequency selection */ + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + if (hdw->freqValTelevision != val) { + hdw->freqValTelevision = val; + hdw->freqSlotTelevision = 0; + hdw->freqDirty = !0; + } + } +} + +int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw) +{ + return hdw->unit_number; +} + + +/* Attempt to locate one of the given set of files. Messages are logged + appropriate to what has been found. The return value will be 0 or + greater on success (it will be the index of the file name found) and + fw_entry will be filled in. Otherwise a negative error is returned on + failure. If the return value is -ENOENT then no viable firmware file + could be located. */ +static int pvr2_locate_firmware(struct pvr2_hdw *hdw, + const struct firmware **fw_entry, + const char *fwtypename, + unsigned int fwcount, + const char *fwnames[]) +{ + unsigned int idx; + int ret = -EINVAL; + for (idx = 0; idx < fwcount; idx++) { + ret = request_firmware(fw_entry, + fwnames[idx], + &hdw->usb_dev->dev); + if (!ret) { + trace_firmware("Located %s firmware: %s;" + " uploading...", + fwtypename, + fwnames[idx]); + return idx; + } + if (ret == -ENOENT) continue; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "request_firmware fatal error with code=%d",ret); + return ret; + } + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "***WARNING***" + " Device %s firmware" + " seems to be missing.", + fwtypename); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Did you install the pvrusb2 firmware files" + " in their proper location?"); + if (fwcount == 1) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "request_firmware unable to locate %s file %s", + fwtypename,fwnames[0]); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "request_firmware unable to locate" + " one of the following %s files:", + fwtypename); + for (idx = 0; idx < fwcount; idx++) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "request_firmware: Failed to find %s", + fwnames[idx]); + } + } + return ret; +} + + +/* + * pvr2_upload_firmware1(). + * + * Send the 8051 firmware to the device. After the upload, arrange for + * device to re-enumerate. + * + * NOTE : the pointer to the firmware data given by request_firmware() + * is not suitable for an usb transaction. + * + */ +static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) +{ + const struct firmware *fw_entry = NULL; + void *fw_ptr; + unsigned int pipe; + unsigned int fwsize; + int ret; + u16 address; + + if (!hdw->hdw_desc->fx2_firmware.cnt) { + hdw->fw1_state = FW1_STATE_OK; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Connected device type defines" + " no firmware to upload; ignoring firmware"); + return -ENOTTY; + } + + hdw->fw1_state = FW1_STATE_FAILED; // default result + + trace_firmware("pvr2_upload_firmware1"); + + ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller", + hdw->hdw_desc->fx2_firmware.cnt, + hdw->hdw_desc->fx2_firmware.lst); + if (ret < 0) { + if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING; + return ret; + } + + usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); + + pipe = usb_sndctrlpipe(hdw->usb_dev, 0); + fwsize = fw_entry->size; + + if ((fwsize != 0x2000) && + (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) { + if (hdw->hdw_desc->flag_fx2_16kb) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192 or 16384, got %u)", + fwsize); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192, got %u)", + fwsize); + } + release_firmware(fw_entry); + return -ENOMEM; + } + + fw_ptr = kmalloc(0x800, GFP_KERNEL); + if (fw_ptr == NULL){ + release_firmware(fw_entry); + return -ENOMEM; + } + + /* We have to hold the CPU during firmware upload. */ + pvr2_hdw_cpureset_assert(hdw,1); + + /* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes + chunk. */ + + ret = 0; + for (address = 0; address < fwsize; address += 0x800) { + memcpy(fw_ptr, fw_entry->data + address, 0x800); + ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address, + 0, fw_ptr, 0x800, HZ); + } + + trace_firmware("Upload done, releasing device's CPU"); + + /* Now release the CPU. It will disconnect and reconnect later. */ + pvr2_hdw_cpureset_assert(hdw,0); + + kfree(fw_ptr); + release_firmware(fw_entry); + + trace_firmware("Upload done (%d bytes sent)",ret); + + /* We should have written fwsize bytes */ + if (ret == fwsize) { + hdw->fw1_state = FW1_STATE_RELOAD; + return 0; + } + + return -EIO; +} + + +/* + * pvr2_upload_firmware2() + * + * This uploads encoder firmware on endpoint 2. + * + */ + +int pvr2_upload_firmware2(struct pvr2_hdw *hdw) +{ + const struct firmware *fw_entry = NULL; + void *fw_ptr; + unsigned int pipe, fw_len, fw_done, bcnt, icnt; + int actual_length; + int ret = 0; + int fwidx; + static const char *fw_files[] = { + CX2341X_FIRM_ENC_FILENAME, + }; + + if (hdw->hdw_desc->flag_skip_cx23416_firmware) { + return 0; + } + + trace_firmware("pvr2_upload_firmware2"); + + ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder", + ARRAY_SIZE(fw_files), fw_files); + if (ret < 0) return ret; + fwidx = ret; + ret = 0; + /* Since we're about to completely reinitialize the encoder, + invalidate our cached copy of its configuration state. Next + time we configure the encoder, then we'll fully configure it. */ + hdw->enc_cur_valid = 0; + + /* Encoder is about to be reset so note that as far as we're + concerned now, the encoder has never been run. */ + del_timer_sync(&hdw->encoder_run_timer); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + } + + /* First prepare firmware loading */ + ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ + ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ + ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ + ret |= pvr2_hdw_cmd_deep_reset(hdw); + ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/ + ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/ + ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ + ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/ + ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/ + ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/ + ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/ + ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/ + ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/ + ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ + ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ + ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); + + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "firmware2 upload prep failed, ret=%d",ret); + release_firmware(fw_entry); + goto done; + } + + /* Now send firmware */ + + fw_len = fw_entry->size; + + if (fw_len % sizeof(u32)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "size of %s firmware" + " must be a multiple of %zu bytes", + fw_files[fwidx],sizeof(u32)); + release_firmware(fw_entry); + ret = -EINVAL; + goto done; + } + + fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); + if (fw_ptr == NULL){ + release_firmware(fw_entry); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "failed to allocate memory for firmware2 upload"); + ret = -ENOMEM; + goto done; + } + + pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); + + fw_done = 0; + for (fw_done = 0; fw_done < fw_len;) { + bcnt = fw_len - fw_done; + if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE; + memcpy(fw_ptr, fw_entry->data + fw_done, bcnt); + /* Usbsnoop log shows that we must swap bytes... */ + /* Some background info: The data being swapped here is a + firmware image destined for the mpeg encoder chip that + lives at the other end of a USB endpoint. The encoder + chip always talks in 32 bit chunks and its storage is + organized into 32 bit words. However from the file + system to the encoder chip everything is purely a byte + stream. The firmware file's contents are always 32 bit + swapped from what the encoder expects. Thus the need + always exists to swap the bytes regardless of the endian + type of the host processor and therefore swab32() makes + the most sense. */ + for (icnt = 0; icnt < bcnt/4 ; icnt++) + ((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]); + + ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt, + &actual_length, HZ); + ret |= (actual_length != bcnt); + if (ret) break; + fw_done += bcnt; + } + + trace_firmware("upload of %s : %i / %i ", + fw_files[fwidx],fw_done,fw_len); + + kfree(fw_ptr); + release_firmware(fw_entry); + + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "firmware2 upload transfer failure"); + goto done; + } + + /* Finish upload */ + + ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ + ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); + + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "firmware2 upload post-proc failure"); + } + + done: + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } + return ret; +} + + +static const char *pvr2_get_state_name(unsigned int st) +{ + if (st < ARRAY_SIZE(pvr2_state_names)) { + return pvr2_state_names[st]; + } + return "???"; +} + +static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) +{ + /* Even though we really only care about the video decoder chip at + this point, we'll broadcast stream on/off to all sub-devices + anyway, just in case somebody else wants to hear the + command... */ + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s", + (enablefl ? "on" : "off")); + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl); + v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl); + if (hdw->decoder_client_id) { + /* We get here if the encoder has been noticed. Otherwise + we'll issue a warning to the user (which should + normally never happen). */ + return 0; + } + if (!hdw->flag_decoder_missed) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: No decoder present"); + hdw->flag_decoder_missed = !0; + trace_stbit("flag_decoder_missed", + hdw->flag_decoder_missed); + } + return -EIO; +} + + +int pvr2_hdw_get_state(struct pvr2_hdw *hdw) +{ + return hdw->master_state; +} + + +static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw) +{ + if (!hdw->flag_tripped) return 0; + hdw->flag_tripped = 0; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Clearing driver error statuss"); + return !0; +} + + +int pvr2_hdw_untrip(struct pvr2_hdw *hdw) +{ + int fl; + LOCK_TAKE(hdw->big_lock); do { + fl = pvr2_hdw_untrip_unlocked(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); + if (fl) pvr2_hdw_state_sched(hdw); + return 0; +} + + + + +int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) +{ + return hdw->state_pipeline_req != 0; +} + + +int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag) +{ + int ret,st; + LOCK_TAKE(hdw->big_lock); do { + pvr2_hdw_untrip_unlocked(hdw); + if ((!enable_flag) != !(hdw->state_pipeline_req)) { + hdw->state_pipeline_req = enable_flag != 0; + pvr2_trace(PVR2_TRACE_START_STOP, + "/*--TRACE_STREAM--*/ %s", + enable_flag ? "enable" : "disable"); + } + pvr2_hdw_state_sched(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); + if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret; + if (enable_flag) { + while ((st = hdw->master_state) != PVR2_STATE_RUN) { + if (st != PVR2_STATE_READY) return -EIO; + if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret; + } + } + return 0; +} + + +int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config) +{ + int fl; + LOCK_TAKE(hdw->big_lock); + if ((fl = (hdw->desired_stream_type != config)) != 0) { + hdw->desired_stream_type = config; + hdw->state_pipeline_config = 0; + trace_stbit("state_pipeline_config", + hdw->state_pipeline_config); + pvr2_hdw_state_sched(hdw); + } + LOCK_GIVE(hdw->big_lock); + if (fl) return 0; + return pvr2_hdw_wait(hdw,0); +} + + +static int get_default_tuner_type(struct pvr2_hdw *hdw) +{ + int unit_number = hdw->unit_number; + int tp = -1; + if ((unit_number >= 0) && (unit_number < PVR_NUM)) { + tp = tuner[unit_number]; + } + if (tp < 0) return -EINVAL; + hdw->tuner_type = tp; + hdw->tuner_updated = !0; + return 0; +} + + +static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) +{ + int unit_number = hdw->unit_number; + int tp = 0; + if ((unit_number >= 0) && (unit_number < PVR_NUM)) { + tp = video_std[unit_number]; + if (tp) return tp; + } + return 0; +} + + +static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw) +{ + int unit_number = hdw->unit_number; + int tp = 0; + if ((unit_number >= 0) && (unit_number < PVR_NUM)) { + tp = tolerance[unit_number]; + } + return tp; +} + + +static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) +{ + /* Try a harmless request to fetch the eeprom's address over + endpoint 1. See what happens. Only the full FX2 image can + respond to this. If this probe fails then likely the FX2 + firmware needs be loaded. */ + int result; + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR; + result = pvr2_send_request_ex(hdw,HZ*1,!0, + hdw->cmd_buffer,1, + hdw->cmd_buffer,1); + if (result < 0) break; + } while(0); LOCK_GIVE(hdw->ctl_lock); + if (result) { + pvr2_trace(PVR2_TRACE_INIT, + "Probe of device endpoint 1 result status %d", + result); + } else { + pvr2_trace(PVR2_TRACE_INIT, + "Probe of device endpoint 1 succeeded"); + } + return result == 0; +} + +struct pvr2_std_hack { + v4l2_std_id pat; /* Pattern to match */ + v4l2_std_id msk; /* Which bits we care about */ + v4l2_std_id std; /* What additional standards or default to set */ +}; + +/* This data structure labels specific combinations of standards from + tveeprom that we'll try to recognize. If we recognize one, then assume + a specified default standard to use. This is here because tveeprom only + tells us about available standards not the intended default standard (if + any) for the device in question. We guess the default based on what has + been reported as available. Note that this is only for guessing a + default - which can always be overridden explicitly - and if the user + has otherwise named a default then that default will always be used in + place of this table. */ +static const struct pvr2_std_hack std_eeprom_maps[] = { + { /* PAL(B/G) */ + .pat = V4L2_STD_B|V4L2_STD_GH, + .std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G, + }, + { /* NTSC(M) */ + .pat = V4L2_STD_MN, + .std = V4L2_STD_NTSC_M, + }, + { /* PAL(I) */ + .pat = V4L2_STD_PAL_I, + .std = V4L2_STD_PAL_I, + }, + { /* SECAM(L/L') */ + .pat = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, + .std = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, + }, + { /* PAL(D/D1/K) */ + .pat = V4L2_STD_DK, + .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K, + }, +}; + +static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) +{ + char buf[40]; + unsigned int bcnt; + v4l2_std_id std1,std2,std3; + + std1 = get_default_standard(hdw); + std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask; + + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); + pvr2_trace(PVR2_TRACE_STD, + "Supported video standard(s) reported available" + " in hardware: %.*s", + bcnt,buf); + + hdw->std_mask_avail = hdw->std_mask_eeprom; + + std2 = (std1|std3) & ~hdw->std_mask_avail; + if (std2) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); + pvr2_trace(PVR2_TRACE_STD, + "Expanding supported video standards" + " to include: %.*s", + bcnt,buf); + hdw->std_mask_avail |= std2; + } + + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; + + if (std1) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); + pvr2_trace(PVR2_TRACE_STD, + "Initial video standard forced to %.*s", + bcnt,buf); + hdw->std_mask_cur = std1; + hdw->std_dirty = !0; + return; + } + if (std3) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3); + pvr2_trace(PVR2_TRACE_STD, + "Initial video standard" + " (determined by device type): %.*s",bcnt,buf); + hdw->std_mask_cur = std3; + hdw->std_dirty = !0; + return; + } + + { + unsigned int idx; + for (idx = 0; idx < ARRAY_SIZE(std_eeprom_maps); idx++) { + if (std_eeprom_maps[idx].msk ? + ((std_eeprom_maps[idx].pat ^ + hdw->std_mask_eeprom) & + std_eeprom_maps[idx].msk) : + (std_eeprom_maps[idx].pat != + hdw->std_mask_eeprom)) continue; + bcnt = pvr2_std_id_to_str(buf,sizeof(buf), + std_eeprom_maps[idx].std); + pvr2_trace(PVR2_TRACE_STD, + "Initial video standard guessed as %.*s", + bcnt,buf); + hdw->std_mask_cur = std_eeprom_maps[idx].std; + hdw->std_dirty = !0; + return; + } + } + +} + + +static unsigned int pvr2_copy_i2c_addr_list( + unsigned short *dst, const unsigned char *src, + unsigned int dst_max) +{ + unsigned int cnt = 0; + if (!src) return 0; + while (src[cnt] && (cnt + 1) < dst_max) { + dst[cnt] = src[cnt]; + cnt++; + } + dst[cnt] = I2C_CLIENT_END; + return cnt; +} + + +static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) +{ + /* + Mike Isely 19-Nov-2006 - This bit of nuttiness + for cx25840 causes that module to correctly set up its video + scaling. This is really a problem in the cx25840 module itself, + but we work around it here. The problem has not been seen in + ivtv because there VBI is supported and set up. We don't do VBI + here (at least not yet) and thus we never attempted to even set + it up. + */ + struct v4l2_format fmt; + if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) { + /* We're not using a cx25840 so don't enable the hack */ + return; + } + + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Executing cx25840 VBI hack", + hdw->decoder_client_id); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; + v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, + vbi, s_sliced_fmt, &fmt.fmt.sliced); +} + + +static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, + const struct pvr2_device_client_desc *cd) +{ + const char *fname; + unsigned char mid; + struct v4l2_subdev *sd; + unsigned int i2ccnt; + const unsigned char *p; + /* Arbitrary count - max # i2c addresses we will probe */ + unsigned short i2caddr[25]; + + mid = cd->module_id; + fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL; + if (!fname) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u for device %s has no name?" + " The driver might have a configuration problem.", + mid, + hdw->hdw_desc->description); + return -EINVAL; + } + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u (%s) for device %s being loaded...", + mid, fname, + hdw->hdw_desc->description); + + i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list, + ARRAY_SIZE(i2caddr)); + if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ? + module_i2c_addresses[mid] : NULL) != NULL)) { + /* Second chance: Try default i2c address list */ + i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p, + ARRAY_SIZE(i2caddr)); + if (i2ccnt) { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Using default i2c address list", + mid); + } + } + + if (!i2ccnt) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u (%s) for device %s:" + " No i2c addresses." + " The driver might have a configuration problem.", + mid, fname, hdw->hdw_desc->description); + return -EINVAL; + } + + if (i2ccnt == 1) { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Setting up with specified i2c address 0x%x", + mid, i2caddr[0]); + sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, + fname, i2caddr[0], NULL); + } else { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Setting up with address probe list", + mid); + sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, + fname, 0, i2caddr); + } + + if (!sd) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u (%s) for device %s failed to load." + " Possible missing sub-device kernel module or" + " initialization failure within module.", + mid, fname, hdw->hdw_desc->description); + return -EIO; + } + + /* Tag this sub-device instance with the module ID we know about. + In other places we'll use that tag to determine if the instance + requires special handling. */ + sd->grp_id = mid; + + pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname); + + + /* client-specific setup... */ + switch (mid) { + case PVR2_CLIENT_ID_CX25840: + case PVR2_CLIENT_ID_SAA7115: + hdw->decoder_client_id = mid; + break; + default: break; + } + + return 0; +} + + +static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw) +{ + unsigned int idx; + const struct pvr2_string_table *cm; + const struct pvr2_device_client_table *ct; + int okFl = !0; + + cm = &hdw->hdw_desc->client_modules; + for (idx = 0; idx < cm->cnt; idx++) { + request_module(cm->lst[idx]); + } + + ct = &hdw->hdw_desc->client_table; + for (idx = 0; idx < ct->cnt; idx++) { + if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0; + } + if (!okFl) { + hdw->flag_modulefail = !0; + pvr2_hdw_render_useless(hdw); + } +} + + +static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) +{ + int ret; + unsigned int idx; + struct pvr2_ctrl *cptr; + int reloadFl = 0; + if (hdw->hdw_desc->fx2_firmware.cnt) { + if (!reloadFl) { + reloadFl = + (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints + == 0); + if (reloadFl) { + pvr2_trace(PVR2_TRACE_INIT, + "USB endpoint config looks strange" + "; possibly firmware needs to be" + " loaded"); + } + } + if (!reloadFl) { + reloadFl = !pvr2_hdw_check_firmware(hdw); + if (reloadFl) { + pvr2_trace(PVR2_TRACE_INIT, + "Check for FX2 firmware failed" + "; possibly firmware needs to be" + " loaded"); + } + } + if (reloadFl) { + if (pvr2_upload_firmware1(hdw) != 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failure uploading firmware1"); + } + return; + } + } + hdw->fw1_state = FW1_STATE_OK; + + if (!pvr2_hdw_dev_ok(hdw)) return; + + hdw->force_dirty = !0; + + if (!hdw->hdw_desc->flag_no_powerup) { + pvr2_hdw_cmd_powerup(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + } + + /* Take the IR chip out of reset, if appropriate */ + if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) { + pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_ZILOG_RESET | + (1 << 8) | + ((0) << 16)); + } + + // This step MUST happen after the earlier powerup step. + pvr2_i2c_core_init(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + + pvr2_hdw_load_modules(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw); + + for (idx = 0; idx < CTRLDEF_COUNT; idx++) { + cptr = hdw->controls + idx; + if (cptr->info->skip_init) continue; + if (!cptr->info->set_value) continue; + cptr->info->set_value(cptr,~0,cptr->info->default_value); + } + + pvr2_hdw_cx25840_vbi_hack(hdw); + + /* Set up special default values for the television and radio + frequencies here. It's not really important what these defaults + are, but I set them to something usable in the Chicago area just + to make driver testing a little easier. */ + + hdw->freqValTelevision = default_tv_freq; + hdw->freqValRadio = default_radio_freq; + + // Do not use pvr2_reset_ctl_endpoints() here. It is not + // thread-safe against the normal pvr2_send_request() mechanism. + // (We should make it thread safe). + + if (hdw->hdw_desc->flag_has_hauppauge_rom) { + ret = pvr2_hdw_get_eeprom_addr(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Unable to determine location of eeprom," + " skipping"); + } else { + hdw->eeprom_addr = ret; + pvr2_eeprom_analyze(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + } + } else { + hdw->tuner_type = hdw->hdw_desc->default_tuner_type; + hdw->tuner_updated = !0; + hdw->std_mask_eeprom = V4L2_STD_ALL; + } + + if (hdw->serial_number) { + idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, + "sn-%lu", hdw->serial_number); + } else if (hdw->unit_number >= 0) { + idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, + "unit-%c", + hdw->unit_number + 'a'); + } else { + idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, + "unit-??"); + } + hdw->identifier[idx] = 0; + + pvr2_hdw_setup_std(hdw); + + if (!get_default_tuner_type(hdw)) { + pvr2_trace(PVR2_TRACE_INIT, + "pvr2_hdw_setup: Tuner type overridden to %d", + hdw->tuner_type); + } + + + if (!pvr2_hdw_dev_ok(hdw)) return; + + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } + + pvr2_hdw_commit_setup(hdw); + + hdw->vid_stream = pvr2_stream_create(); + if (!pvr2_hdw_dev_ok(hdw)) return; + pvr2_trace(PVR2_TRACE_INIT, + "pvr2_hdw_setup: video stream is %p",hdw->vid_stream); + if (hdw->vid_stream) { + idx = get_default_error_tolerance(hdw); + if (idx) { + pvr2_trace(PVR2_TRACE_INIT, + "pvr2_hdw_setup: video stream %p" + " setting tolerance %u", + hdw->vid_stream,idx); + } + pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev, + PVR2_VID_ENDPOINT,idx); + } + + if (!pvr2_hdw_dev_ok(hdw)) return; + + hdw->flag_init_ok = !0; + + pvr2_hdw_state_sched(hdw); +} + + +/* Set up the structure and attempt to put the device into a usable state. + This can be a time-consuming operation, which is why it is not done + internally as part of the create() step. */ +static void pvr2_hdw_setup(struct pvr2_hdw *hdw) +{ + pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw); + do { + pvr2_hdw_setup_low(hdw); + pvr2_trace(PVR2_TRACE_INIT, + "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d", + hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok); + if (pvr2_hdw_dev_ok(hdw)) { + if (hdw->flag_init_ok) { + pvr2_trace( + PVR2_TRACE_INFO, + "Device initialization" + " completed successfully."); + break; + } + if (hdw->fw1_state == FW1_STATE_RELOAD) { + pvr2_trace( + PVR2_TRACE_INFO, + "Device microcontroller firmware" + " (re)loaded; it should now reset" + " and reconnect."); + break; + } + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Device initialization was not successful."); + if (hdw->fw1_state == FW1_STATE_MISSING) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Giving up since device" + " microcontroller firmware" + " appears to be missing."); + break; + } + } + if (hdw->flag_modulefail) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "***WARNING*** pvrusb2 driver initialization" + " failed due to the failure of one or more" + " sub-device kernel modules."); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "You need to resolve the failing condition" + " before this driver can function. There" + " should be some earlier messages giving more" + " information about the problem."); + break; + } + if (procreload) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Attempting pvrusb2 recovery by reloading" + " primary firmware."); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "If this works, device should disconnect" + " and reconnect in a sane state."); + hdw->fw1_state = FW1_STATE_UNKNOWN; + pvr2_upload_firmware1(hdw); + } else { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "***WARNING*** pvrusb2 device hardware" + " appears to be jammed" + " and I can't clear it."); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "You might need to power cycle" + " the pvrusb2 device" + " in order to recover."); + } + } while (0); + pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw); +} + + +/* Perform second stage initialization. Set callback pointer first so that + we can avoid a possible initialization race (if the kernel thread runs + before the callback has been set). */ +int pvr2_hdw_initialize(struct pvr2_hdw *hdw, + void (*callback_func)(void *), + void *callback_data) +{ + LOCK_TAKE(hdw->big_lock); do { + if (hdw->flag_disconnected) { + /* Handle a race here: If we're already + disconnected by this point, then give up. If we + get past this then we'll remain connected for + the duration of initialization since the entire + initialization sequence is now protected by the + big_lock. */ + break; + } + hdw->state_data = callback_data; + hdw->state_func = callback_func; + pvr2_hdw_setup(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); + return hdw->flag_init_ok; +} + + +/* Create, set up, and return a structure for interacting with the + underlying hardware. */ +struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, + const struct usb_device_id *devid) +{ + unsigned int idx,cnt1,cnt2,m; + struct pvr2_hdw *hdw = NULL; + int valid_std_mask; + struct pvr2_ctrl *cptr; + struct usb_device *usb_dev; + const struct pvr2_device_desc *hdw_desc; + __u8 ifnum; + struct v4l2_queryctrl qctrl; + struct pvr2_ctl_info *ciptr; + + usb_dev = interface_to_usbdev(intf); + + hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info); + + if (hdw_desc == NULL) { + pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create:" + " No device description pointer," + " unable to continue."); + pvr2_trace(PVR2_TRACE_INIT, "If you have a new device type," + " please contact Mike Isely " + " to get it included in the driver\n"); + goto fail; + } + + hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); + pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", + hdw,hdw_desc->description); + pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s", + hdw_desc->description); + if (hdw_desc->flag_is_experimental) { + pvr2_trace(PVR2_TRACE_INFO, "**********"); + pvr2_trace(PVR2_TRACE_INFO, + "WARNING: Support for this device (%s) is" + " experimental.", hdw_desc->description); + pvr2_trace(PVR2_TRACE_INFO, + "Important functionality might not be" + " entirely working."); + pvr2_trace(PVR2_TRACE_INFO, + "Please consider contacting the driver author to" + " help with further stabilization of the driver."); + pvr2_trace(PVR2_TRACE_INFO, "**********"); + } + if (!hdw) goto fail; + + init_timer(&hdw->quiescent_timer); + hdw->quiescent_timer.data = (unsigned long)hdw; + hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout; + + init_timer(&hdw->decoder_stabilization_timer); + hdw->decoder_stabilization_timer.data = (unsigned long)hdw; + hdw->decoder_stabilization_timer.function = + pvr2_hdw_decoder_stabilization_timeout; + + init_timer(&hdw->encoder_wait_timer); + hdw->encoder_wait_timer.data = (unsigned long)hdw; + hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + + init_timer(&hdw->encoder_run_timer); + hdw->encoder_run_timer.data = (unsigned long)hdw; + hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout; + + hdw->master_state = PVR2_STATE_DEAD; + + init_waitqueue_head(&hdw->state_wait_data); + + hdw->tuner_signal_stale = !0; + cx2341x_fill_defaults(&hdw->enc_ctl_state); + + /* Calculate which inputs are OK */ + m = 0; + if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV; + if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) { + m |= 1 << PVR2_CVAL_INPUT_DTV; + } + if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO; + if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; + if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; + hdw->input_avail_mask = m; + hdw->input_allowed_mask = hdw->input_avail_mask; + + /* If not a hybrid device, pathway_state never changes. So + initialize it here to what it should forever be. */ + if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) { + hdw->pathway_state = PVR2_PATHWAY_ANALOG; + } else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) { + hdw->pathway_state = PVR2_PATHWAY_DIGITAL; + } + + hdw->control_cnt = CTRLDEF_COUNT; + hdw->control_cnt += MPEGDEF_COUNT; + hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, + GFP_KERNEL); + if (!hdw->controls) goto fail; + hdw->hdw_desc = hdw_desc; + hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme; + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + cptr->hdw = hdw; + } + for (idx = 0; idx < 32; idx++) { + hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx]; + } + for (idx = 0; idx < CTRLDEF_COUNT; idx++) { + cptr = hdw->controls + idx; + cptr->info = control_defs+idx; + } + + /* Ensure that default input choice is a valid one. */ + m = hdw->input_avail_mask; + if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + hdw->input_val = idx; + break; + } + + /* Define and configure additional controls from cx2341x module. */ + hdw->mpeg_ctrl_info = kcalloc(MPEGDEF_COUNT, + sizeof(*(hdw->mpeg_ctrl_info)), + GFP_KERNEL); + if (!hdw->mpeg_ctrl_info) goto fail; + for (idx = 0; idx < MPEGDEF_COUNT; idx++) { + cptr = hdw->controls + idx + CTRLDEF_COUNT; + ciptr = &(hdw->mpeg_ctrl_info[idx].info); + ciptr->desc = hdw->mpeg_ctrl_info[idx].desc; + ciptr->name = mpeg_ids[idx].strid; + ciptr->v4l_id = mpeg_ids[idx].id; + ciptr->skip_init = !0; + ciptr->get_value = ctrl_cx2341x_get; + ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags; + ciptr->is_dirty = ctrl_cx2341x_is_dirty; + if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty; + qctrl.id = ciptr->v4l_id; + cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl); + if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) { + ciptr->set_value = ctrl_cx2341x_set; + } + strncpy(hdw->mpeg_ctrl_info[idx].desc,qctrl.name, + PVR2_CTLD_INFO_DESC_SIZE); + hdw->mpeg_ctrl_info[idx].desc[PVR2_CTLD_INFO_DESC_SIZE-1] = 0; + ciptr->default_value = qctrl.default_value; + switch (qctrl.type) { + default: + case V4L2_CTRL_TYPE_INTEGER: + ciptr->type = pvr2_ctl_int; + ciptr->def.type_int.min_value = qctrl.minimum; + ciptr->def.type_int.max_value = qctrl.maximum; + break; + case V4L2_CTRL_TYPE_BOOLEAN: + ciptr->type = pvr2_ctl_bool; + break; + case V4L2_CTRL_TYPE_MENU: + ciptr->type = pvr2_ctl_enum; + ciptr->def.type_enum.value_names = + cx2341x_ctrl_get_menu(&hdw->enc_ctl_state, + ciptr->v4l_id); + for (cnt1 = 0; + ciptr->def.type_enum.value_names[cnt1] != NULL; + cnt1++) { } + ciptr->def.type_enum.count = cnt1; + break; + } + cptr->info = ciptr; + } + + // Initialize control data regarding video standard masks + valid_std_mask = pvr2_std_get_usable(); + for (idx = 0; idx < 32; idx++) { + if (!(valid_std_mask & (1 << idx))) continue; + cnt1 = pvr2_std_id_to_str( + hdw->std_mask_names[idx], + sizeof(hdw->std_mask_names[idx])-1, + 1 << idx); + hdw->std_mask_names[idx][cnt1] = 0; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL); + if (cptr) { + memcpy(&hdw->std_info_avail,cptr->info, + sizeof(hdw->std_info_avail)); + cptr->info = &hdw->std_info_avail; + hdw->std_info_avail.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_avail.def.type_bitmask.valid_bits = + valid_std_mask; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR); + if (cptr) { + memcpy(&hdw->std_info_cur,cptr->info, + sizeof(hdw->std_info_cur)); + cptr->info = &hdw->std_info_cur; + hdw->std_info_cur.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_cur.def.type_bitmask.valid_bits = + valid_std_mask; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT); + if (cptr) { + memcpy(&hdw->std_info_detect,cptr->info, + sizeof(hdw->std_info_detect)); + cptr->info = &hdw->std_info_detect; + hdw->std_info_detect.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_detect.def.type_bitmask.valid_bits = + valid_std_mask; + } + + hdw->cropcap_stale = !0; + hdw->eeprom_addr = -1; + hdw->unit_number = -1; + hdw->v4l_minor_number_video = -1; + hdw->v4l_minor_number_vbi = -1; + hdw->v4l_minor_number_radio = -1; + hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); + if (!hdw->ctl_write_buffer) goto fail; + hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); + if (!hdw->ctl_read_buffer) goto fail; + hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL); + if (!hdw->ctl_write_urb) goto fail; + hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL); + if (!hdw->ctl_read_urb) goto fail; + + if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Error registering with v4l core, giving up"); + goto fail; + } + mutex_lock(&pvr2_unit_mtx); do { + for (idx = 0; idx < PVR_NUM; idx++) { + if (unit_pointers[idx]) continue; + hdw->unit_number = idx; + unit_pointers[idx] = hdw; + break; + } + } while (0); mutex_unlock(&pvr2_unit_mtx); + + cnt1 = 0; + cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2"); + cnt1 += cnt2; + if (hdw->unit_number >= 0) { + cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c", + ('a' + hdw->unit_number)); + cnt1 += cnt2; + } + if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1; + hdw->name[cnt1] = 0; + + hdw->workqueue = create_singlethread_workqueue(hdw->name); + INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); + + pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", + hdw->unit_number,hdw->name); + + hdw->tuner_type = -1; + hdw->flag_ok = !0; + + hdw->usb_intf = intf; + hdw->usb_dev = usb_dev; + + usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info)); + + ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; + usb_set_interface(hdw->usb_dev,ifnum,0); + + mutex_init(&hdw->ctl_lock_mutex); + mutex_init(&hdw->big_lock_mutex); + + return hdw; + fail: + if (hdw) { + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->decoder_stabilization_timer); + del_timer_sync(&hdw->encoder_run_timer); + del_timer_sync(&hdw->encoder_wait_timer); + if (hdw->workqueue) { + flush_workqueue(hdw->workqueue); + destroy_workqueue(hdw->workqueue); + hdw->workqueue = NULL; + } + usb_free_urb(hdw->ctl_read_urb); + usb_free_urb(hdw->ctl_write_urb); + kfree(hdw->ctl_read_buffer); + kfree(hdw->ctl_write_buffer); + kfree(hdw->controls); + kfree(hdw->mpeg_ctrl_info); + kfree(hdw); + } + return NULL; +} + + +/* Remove _all_ associations between this driver and the underlying USB + layer. */ +static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw) +{ + if (hdw->flag_disconnected) return; + pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw); + if (hdw->ctl_read_urb) { + usb_kill_urb(hdw->ctl_read_urb); + usb_free_urb(hdw->ctl_read_urb); + hdw->ctl_read_urb = NULL; + } + if (hdw->ctl_write_urb) { + usb_kill_urb(hdw->ctl_write_urb); + usb_free_urb(hdw->ctl_write_urb); + hdw->ctl_write_urb = NULL; + } + if (hdw->ctl_read_buffer) { + kfree(hdw->ctl_read_buffer); + hdw->ctl_read_buffer = NULL; + } + if (hdw->ctl_write_buffer) { + kfree(hdw->ctl_write_buffer); + hdw->ctl_write_buffer = NULL; + } + hdw->flag_disconnected = !0; + /* If we don't do this, then there will be a dangling struct device + reference to our disappearing device persisting inside the V4L + core... */ + v4l2_device_disconnect(&hdw->v4l2_dev); + hdw->usb_dev = NULL; + hdw->usb_intf = NULL; + pvr2_hdw_render_useless(hdw); +} + + +/* Destroy hardware interaction structure */ +void pvr2_hdw_destroy(struct pvr2_hdw *hdw) +{ + if (!hdw) return; + pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); + if (hdw->workqueue) { + flush_workqueue(hdw->workqueue); + destroy_workqueue(hdw->workqueue); + hdw->workqueue = NULL; + } + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->decoder_stabilization_timer); + del_timer_sync(&hdw->encoder_run_timer); + del_timer_sync(&hdw->encoder_wait_timer); + if (hdw->fw_buffer) { + kfree(hdw->fw_buffer); + hdw->fw_buffer = NULL; + } + if (hdw->vid_stream) { + pvr2_stream_destroy(hdw->vid_stream); + hdw->vid_stream = NULL; + } + pvr2_i2c_core_done(hdw); + v4l2_device_unregister(&hdw->v4l2_dev); + pvr2_hdw_remove_usb_stuff(hdw); + mutex_lock(&pvr2_unit_mtx); do { + if ((hdw->unit_number >= 0) && + (hdw->unit_number < PVR_NUM) && + (unit_pointers[hdw->unit_number] == hdw)) { + unit_pointers[hdw->unit_number] = NULL; + } + } while (0); mutex_unlock(&pvr2_unit_mtx); + kfree(hdw->controls); + kfree(hdw->mpeg_ctrl_info); + kfree(hdw); +} + + +int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw) +{ + return (hdw && hdw->flag_ok); +} + + +/* Called when hardware has been unplugged */ +void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) +{ + pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw); + LOCK_TAKE(hdw->big_lock); + LOCK_TAKE(hdw->ctl_lock); + pvr2_hdw_remove_usb_stuff(hdw); + LOCK_GIVE(hdw->ctl_lock); + LOCK_GIVE(hdw->big_lock); +} + + +/* Get the number of defined controls */ +unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) +{ + return hdw->control_cnt; +} + + +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw, + unsigned int idx) +{ + if (idx >= hdw->control_cnt) return NULL; + return hdw->controls + idx; +} + + +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw, + unsigned int ctl_id) +{ + struct pvr2_ctrl *cptr; + unsigned int idx; + int i; + + /* This could be made a lot more efficient, but for now... */ + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + i = cptr->info->internal_id; + if (i && (i == ctl_id)) return cptr; + } + return NULL; +} + + +/* Given a V4L ID, retrieve the control structure associated with it. */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id) +{ + struct pvr2_ctrl *cptr; + unsigned int idx; + int i; + + /* This could be made a lot more efficient, but for now... */ + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + i = cptr->info->v4l_id; + if (i && (i == ctl_id)) return cptr; + } + return NULL; +} + + +/* Given a V4L ID for its immediate predecessor, retrieve the control + structure associated with it. */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw, + unsigned int ctl_id) +{ + struct pvr2_ctrl *cptr,*cp2; + unsigned int idx; + int i; + + /* This could be made a lot more efficient, but for now... */ + cp2 = NULL; + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + i = cptr->info->v4l_id; + if (!i) continue; + if (i <= ctl_id) continue; + if (cp2 && (cp2->info->v4l_id < i)) continue; + cp2 = cptr; + } + return cp2; + return NULL; +} + + +static const char *get_ctrl_typename(enum pvr2_ctl_type tp) +{ + switch (tp) { + case pvr2_ctl_int: return "integer"; + case pvr2_ctl_enum: return "enum"; + case pvr2_ctl_bool: return "boolean"; + case pvr2_ctl_bitmask: return "bitmask"; + } + return ""; +} + + +static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, + const char *name, int val) +{ + struct v4l2_control ctrl; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val); + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; + ctrl.value = val; + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl); +} + +#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \ + if ((hdw)->lab##_dirty || (hdw)->force_dirty) { \ + pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ + } + +v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw) +{ + v4l2_std_id std; + std = (v4l2_std_id)hdw->std_mask_avail; + v4l2_device_call_all(&hdw->v4l2_dev, 0, + video, querystd, &std); + return std; +} + +/* Execute whatever commands are required to update the state of all the + sub-devices so that they match our current control values. */ +static void pvr2_subdev_update(struct pvr2_hdw *hdw) +{ + struct v4l2_subdev *sd; + unsigned int id; + pvr2_subdev_update_func fp; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev update..."); + + if (hdw->tuner_updated || hdw->force_dirty) { + struct tuner_setup setup; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)", + hdw->tuner_type); + if (((int)(hdw->tuner_type)) >= 0) { + memset(&setup, 0, sizeof(setup)); + setup.addr = ADDR_UNSET; + setup.type = hdw->tuner_type; + setup.mode_mask = T_RADIO | T_ANALOG_TV; + v4l2_device_call_all(&hdw->v4l2_dev, 0, + tuner, s_type_addr, &setup); + } + } + + if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) { + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard"); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + v4l2_device_call_all(&hdw->v4l2_dev, 0, + tuner, s_radio); + } else { + v4l2_std_id vs; + vs = hdw->std_mask_cur; + v4l2_device_call_all(&hdw->v4l2_dev, 0, + core, s_std, vs); + pvr2_hdw_cx25840_vbi_hack(hdw); + } + hdw->tuner_signal_stale = !0; + hdw->cropcap_stale = !0; + } + + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble); + + if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) { + struct v4l2_tuner vt; + memset(&vt, 0, sizeof(vt)); + vt.type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + vt.audmode = hdw->audiomode_val; + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt); + } + + if (hdw->freqDirty || hdw->force_dirty) { + unsigned long fv; + struct v4l2_frequency freq; + fv = pvr2_hdw_get_cur_freq(hdw); + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv); + if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw); + memset(&freq, 0, sizeof(freq)); + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + /* ((fv * 1000) / 62500) */ + freq.frequency = (fv * 2) / 125; + } else { + freq.frequency = fv / 62500; + } + /* tuner-core currently doesn't seem to care about this, but + let's set it anyway for completeness. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + freq.type = V4L2_TUNER_RADIO; + } else { + freq.type = V4L2_TUNER_ANALOG_TV; + } + freq.tuner = 0; + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, + s_frequency, &freq); + } + + if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) { + struct v4l2_mbus_framefmt fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.width = hdw->res_hor_val; + fmt.height = hdw->res_ver_val; + fmt.code = V4L2_MBUS_FMT_FIXED; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)", + fmt.width, fmt.height); + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_mbus_fmt, &fmt); + } + + if (hdw->srate_dirty || hdw->force_dirty) { + u32 val; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d", + hdw->srate_val); + switch (hdw->srate_val) { + default: + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: + val = 48000; + break; + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: + val = 44100; + break; + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: + val = 32000; + break; + } + v4l2_device_call_all(&hdw->v4l2_dev, 0, + audio, s_clock_freq, val); + } + + /* Unable to set crop parameters; there is apparently no equivalent + for VIDIOC_S_CROP */ + + v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { + id = sd->grp_id; + if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue; + fp = pvr2_module_update_functions[id]; + if (!fp) continue; + (*fp)(hdw, sd); + } + + if (hdw->tuner_signal_stale || hdw->cropcap_stale) { + pvr2_hdw_status_poll(hdw); + } +} + + +/* Figure out if we need to commit control changes. If so, mark internal + state flags to indicate this fact and return true. Otherwise do nothing + else and return false. */ +static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw) +{ + unsigned int idx; + struct pvr2_ctrl *cptr; + int value; + int commit_flag = hdw->force_dirty; + char buf[100]; + unsigned int bcnt,ccnt; + + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + if (!cptr->info->is_dirty) continue; + if (!cptr->info->is_dirty(cptr)) continue; + commit_flag = !0; + + if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue; + bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ", + cptr->info->name); + value = 0; + cptr->info->get_value(cptr,&value); + pvr2_ctrl_value_to_sym_internal(cptr,~0,value, + buf+bcnt, + sizeof(buf)-bcnt,&ccnt); + bcnt += ccnt; + bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>", + get_ctrl_typename(cptr->info->type)); + pvr2_trace(PVR2_TRACE_CTL, + "/*--TRACE_COMMIT--*/ %.*s", + bcnt,buf); + } + + if (!commit_flag) { + /* Nothing has changed */ + return 0; + } + + hdw->state_pipeline_config = 0; + trace_stbit("state_pipeline_config",hdw->state_pipeline_config); + pvr2_hdw_state_sched(hdw); + + return !0; +} + + +/* Perform all operations needed to commit all control changes. This must + be performed in synchronization with the pipeline state and is thus + expected to be called as part of the driver's worker thread. Return + true if commit successful, otherwise return false to indicate that + commit isn't possible at this time. */ +static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) +{ + unsigned int idx; + struct pvr2_ctrl *cptr; + int disruptive_change; + + if (hdw->input_dirty && hdw->state_pathway_ok && + (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? + PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != + hdw->pathway_state)) { + /* Change of mode being asked for... */ + hdw->state_pathway_ok = 0; + trace_stbit("state_pathway_ok", hdw->state_pathway_ok); + } + if (!hdw->state_pathway_ok) { + /* Can't commit anything until pathway is ok. */ + return 0; + } + + /* Handle some required side effects when the video standard is + changed.... */ + if (hdw->std_dirty) { + int nvres; + int gop_size; + if (hdw->std_mask_cur & V4L2_STD_525_60) { + nvres = 480; + gop_size = 15; + } else { + nvres = 576; + gop_size = 12; + } + /* Rewrite the vertical resolution to be appropriate to the + video standard that has been selected. */ + if (nvres != hdw->res_ver_val) { + hdw->res_ver_val = nvres; + hdw->res_ver_dirty = !0; + } + /* Rewrite the GOP size to be appropriate to the video + standard that has been selected. */ + if (gop_size != hdw->enc_ctl_state.video_gop_size) { + struct v4l2_ext_controls cs; + struct v4l2_ext_control c1; + memset(&cs, 0, sizeof(cs)); + memset(&c1, 0, sizeof(c1)); + cs.controls = &c1; + cs.count = 1; + c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; + c1.value = gop_size; + cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs, + VIDIOC_S_EXT_CTRLS); + } + } + + /* The broadcast decoder can only scale down, so if + * res_*_dirty && crop window < output format ==> enlarge crop. + * + * The mpeg encoder receives fields of res_hor_val dots and + * res_ver_val halflines. Limits: hor<=720, ver<=576. + */ + if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) { + hdw->cropw_val = hdw->res_hor_val; + hdw->cropw_dirty = !0; + } else if (hdw->cropw_dirty) { + hdw->res_hor_dirty = !0; /* must rescale */ + hdw->res_hor_val = min(720, hdw->cropw_val); + } + if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) { + hdw->croph_val = hdw->res_ver_val; + hdw->croph_dirty = !0; + } else if (hdw->croph_dirty) { + int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576; + hdw->res_ver_dirty = !0; + hdw->res_ver_val = min(nvres, hdw->croph_val); + } + + /* If any of the below has changed, then we can't do the update + while the pipeline is running. Pipeline must be paused first + and decoder -> encoder connection be made quiescent before we + can proceed. */ + disruptive_change = + (hdw->std_dirty || + hdw->enc_unsafe_stale || + hdw->srate_dirty || + hdw->res_ver_dirty || + hdw->res_hor_dirty || + hdw->cropw_dirty || + hdw->croph_dirty || + hdw->input_dirty || + (hdw->active_stream_type != hdw->desired_stream_type)); + if (disruptive_change && !hdw->state_pipeline_idle) { + /* Pipeline is not idle; we can't proceed. Arrange to + cause pipeline to stop so that we can try this again + later.... */ + hdw->state_pipeline_pause = !0; + return 0; + } + + if (hdw->srate_dirty) { + /* Write new sample rate into control structure since + * the master copy is stale. We must track srate + * separate from the mpeg control structure because + * other logic also uses this value. */ + struct v4l2_ext_controls cs; + struct v4l2_ext_control c1; + memset(&cs,0,sizeof(cs)); + memset(&c1,0,sizeof(c1)); + cs.controls = &c1; + cs.count = 1; + c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ; + c1.value = hdw->srate_val; + cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS); + } + + if (hdw->active_stream_type != hdw->desired_stream_type) { + /* Handle any side effects of stream config here */ + hdw->active_stream_type = hdw->desired_stream_type; + } + + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + u32 b; + /* Handle GOTVIEW audio switching */ + pvr2_hdw_gpio_get_out(hdw,&b); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + /* Set GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0); + } else { + /* Clear GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0); + } + } + + /* Check and update state for all sub-devices. */ + pvr2_subdev_update(hdw); + + hdw->tuner_updated = 0; + hdw->force_dirty = 0; + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + if (!cptr->info->clear_dirty) continue; + cptr->info->clear_dirty(cptr); + } + + if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) && + hdw->state_encoder_run) { + /* If encoder isn't running or it can't be touched, then + this will get worked out later when we start the + encoder. */ + if (pvr2_encoder_adjust(hdw) < 0) return !0; + } + + hdw->state_pipeline_config = !0; + /* Hardware state may have changed in a way to cause the cropping + capabilities to have changed. So mark it stale, which will + cause a later re-fetch. */ + trace_stbit("state_pipeline_config",hdw->state_pipeline_config); + return !0; +} + + +int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) +{ + int fl; + LOCK_TAKE(hdw->big_lock); + fl = pvr2_hdw_commit_setup(hdw); + LOCK_GIVE(hdw->big_lock); + if (!fl) return 0; + return pvr2_hdw_wait(hdw,0); +} + + +static void pvr2_hdw_worker_poll(struct work_struct *work) +{ + int fl = 0; + struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll); + LOCK_TAKE(hdw->big_lock); do { + fl = pvr2_hdw_state_eval(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); + if (fl && hdw->state_func) { + hdw->state_func(hdw->state_data); + } +} + + +static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) +{ + return wait_event_interruptible( + hdw->state_wait_data, + (hdw->state_stale == 0) && + (!state || (hdw->master_state != state))); +} + + +/* Return name for this driver instance */ +const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) +{ + return hdw->name; +} + + +const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw) +{ + return hdw->hdw_desc->description; +} + + +const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw) +{ + return hdw->hdw_desc->shortname; +} + + +int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) +{ + int result; + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED; + result = pvr2_send_request(hdw, + hdw->cmd_buffer,1, + hdw->cmd_buffer,1); + if (result < 0) break; + result = (hdw->cmd_buffer[0] != 0); + } while(0); LOCK_GIVE(hdw->ctl_lock); + return result; +} + + +/* Execute poll of tuner status */ +void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) +{ + LOCK_TAKE(hdw->big_lock); do { + pvr2_hdw_status_poll(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); +} + + +static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw) +{ + if (!hdw->cropcap_stale) { + return 0; + } + pvr2_hdw_status_poll(hdw); + if (hdw->cropcap_stale) { + return -EIO; + } + return 0; +} + + +/* Return information about cropping capabilities */ +int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp) +{ + int stat = 0; + LOCK_TAKE(hdw->big_lock); + stat = pvr2_hdw_check_cropcap(hdw); + if (!stat) { + memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info)); + } + LOCK_GIVE(hdw->big_lock); + return stat; +} + + +/* Return information about the tuner */ +int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) +{ + LOCK_TAKE(hdw->big_lock); do { + if (hdw->tuner_signal_stale) { + pvr2_hdw_status_poll(hdw); + } + memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner)); + } while (0); LOCK_GIVE(hdw->big_lock); + return 0; +} + + +/* Get handle to video output stream */ +struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp) +{ + return hp->vid_stream; +} + + +void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) +{ + int nr = pvr2_hdw_get_unit_number(hdw); + LOCK_TAKE(hdw->big_lock); do { + printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status); + pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); + cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); + pvr2_hdw_state_log_state(hdw); + printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); + } while (0); LOCK_GIVE(hdw->big_lock); +} + + +/* Grab EEPROM contents, needed for direct method. */ +#define EEPROM_SIZE 8192 +#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) +static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw) +{ + struct i2c_msg msg[2]; + u8 *eeprom; + u8 iadd[2]; + u8 addr; + u16 eepromSize; + unsigned int offs; + int ret; + int mode16 = 0; + unsigned pcnt,tcnt; + eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL); + if (!eeprom) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to allocate memory" + " required to read eeprom"); + return NULL; + } + + trace_eeprom("Value for eeprom addr from controller was 0x%x", + hdw->eeprom_addr); + addr = hdw->eeprom_addr; + /* Seems that if the high bit is set, then the *real* eeprom + address is shifted right now bit position (noticed this in + newer PVR USB2 hardware) */ + if (addr & 0x80) addr >>= 1; + + /* FX2 documentation states that a 16bit-addressed eeprom is + expected if the I2C address is an odd number (yeah, this is + strange but it's what they do) */ + mode16 = (addr & 1); + eepromSize = (mode16 ? EEPROM_SIZE : 256); + trace_eeprom("Examining %d byte eeprom at location 0x%x" + " using %d bit addressing",eepromSize,addr, + mode16 ? 16 : 8); + + msg[0].addr = addr; + msg[0].flags = 0; + msg[0].len = mode16 ? 2 : 1; + msg[0].buf = iadd; + msg[1].addr = addr; + msg[1].flags = I2C_M_RD; + + /* We have to do the actual eeprom data fetch ourselves, because + (1) we're only fetching part of the eeprom, and (2) if we were + getting the whole thing our I2C driver can't grab it in one + pass - which is what tveeprom is otherwise going to attempt */ + memset(eeprom,0,EEPROM_SIZE); + for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) { + pcnt = 16; + if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt; + offs = tcnt + (eepromSize - EEPROM_SIZE); + if (mode16) { + iadd[0] = offs >> 8; + iadd[1] = offs; + } else { + iadd[0] = offs; + } + msg[1].len = pcnt; + msg[1].buf = eeprom+tcnt; + if ((ret = i2c_transfer(&hdw->i2c_adap, + msg,ARRAY_SIZE(msg))) != 2) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "eeprom fetch set offs err=%d",ret); + kfree(eeprom); + return NULL; + } + } + return eeprom; +} + + +void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, + int mode, + int enable_flag) +{ + int ret; + u16 address; + unsigned int pipe; + LOCK_TAKE(hdw->big_lock); do { + if ((hdw->fw_buffer == NULL) == !enable_flag) break; + + if (!enable_flag) { + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Cleaning up after CPU firmware fetch"); + kfree(hdw->fw_buffer); + hdw->fw_buffer = NULL; + hdw->fw_size = 0; + if (hdw->fw_cpu_flag) { + /* Now release the CPU. It will disconnect + and reconnect later. */ + pvr2_hdw_cpureset_assert(hdw,0); + } + break; + } + + hdw->fw_cpu_flag = (mode != 2); + if (hdw->fw_cpu_flag) { + hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000; + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Preparing to suck out CPU firmware" + " (size=%u)", hdw->fw_size); + hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL); + if (!hdw->fw_buffer) { + hdw->fw_size = 0; + break; + } + + /* We have to hold the CPU during firmware upload. */ + pvr2_hdw_cpureset_assert(hdw,1); + + /* download the firmware from address 0000-1fff in 2048 + (=0x800) bytes chunk. */ + + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Grabbing CPU firmware"); + pipe = usb_rcvctrlpipe(hdw->usb_dev, 0); + for(address = 0; address < hdw->fw_size; + address += 0x800) { + ret = usb_control_msg(hdw->usb_dev,pipe, + 0xa0,0xc0, + address,0, + hdw->fw_buffer+address, + 0x800,HZ); + if (ret < 0) break; + } + + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Done grabbing CPU firmware"); + } else { + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Sucking down EEPROM contents"); + hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw); + if (!hdw->fw_buffer) { + pvr2_trace(PVR2_TRACE_FIRMWARE, + "EEPROM content suck failed."); + break; + } + hdw->fw_size = EEPROM_SIZE; + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Done sucking down EEPROM contents"); + } + + } while (0); LOCK_GIVE(hdw->big_lock); +} + + +/* Return true if we're in a mode for retrieval CPU firmware */ +int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw) +{ + return hdw->fw_buffer != NULL; +} + + +int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs, + char *buf,unsigned int cnt) +{ + int ret = -EINVAL; + LOCK_TAKE(hdw->big_lock); do { + if (!buf) break; + if (!cnt) break; + + if (!hdw->fw_buffer) { + ret = -EIO; + break; + } + + if (offs >= hdw->fw_size) { + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Read firmware data offs=%d EOF", + offs); + ret = 0; + break; + } + + if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs; + + memcpy(buf,hdw->fw_buffer+offs,cnt); + + pvr2_trace(PVR2_TRACE_FIRMWARE, + "Read firmware data offs=%d cnt=%d", + offs,cnt); + ret = cnt; + } while (0); LOCK_GIVE(hdw->big_lock); + + return ret; +} + + +int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw, + enum pvr2_v4l_type index) +{ + switch (index) { + case pvr2_v4l_type_video: return hdw->v4l_minor_number_video; + case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi; + case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio; + default: return -1; + } +} + + +/* Store a v4l minor device number */ +void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw, + enum pvr2_v4l_type index,int v) +{ + switch (index) { + case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v; + case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v; + case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v; + default: break; + } +} + + +static void pvr2_ctl_write_complete(struct urb *urb) +{ + struct pvr2_hdw *hdw = urb->context; + hdw->ctl_write_pend_flag = 0; + if (hdw->ctl_read_pend_flag) return; + complete(&hdw->ctl_done); +} + + +static void pvr2_ctl_read_complete(struct urb *urb) +{ + struct pvr2_hdw *hdw = urb->context; + hdw->ctl_read_pend_flag = 0; + if (hdw->ctl_write_pend_flag) return; + complete(&hdw->ctl_done); +} + + +static void pvr2_ctl_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) { + hdw->ctl_timeout_flag = !0; + if (hdw->ctl_write_pend_flag) + usb_unlink_urb(hdw->ctl_write_urb); + if (hdw->ctl_read_pend_flag) + usb_unlink_urb(hdw->ctl_read_urb); + } +} + + +/* Issue a command and get a response from the device. This extended + version includes a probe flag (which if set means that device errors + should not be logged or treated as fatal) and a timeout in jiffies. + This can be used to non-lethally probe the health of endpoint 1. */ +static int pvr2_send_request_ex(struct pvr2_hdw *hdw, + unsigned int timeout,int probe_fl, + void *write_data,unsigned int write_len, + void *read_data,unsigned int read_len) +{ + unsigned int idx; + int status = 0; + struct timer_list timer; + if (!hdw->ctl_lock_held) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Attempted to execute control transfer" + " without lock!!"); + return -EDEADLK; + } + if (!hdw->flag_ok && !probe_fl) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Attempted to execute control transfer" + " when device not ok"); + return -EIO; + } + if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) { + if (!probe_fl) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Attempted to execute control transfer" + " when USB is disconnected"); + } + return -ENOTTY; + } + + /* Ensure that we have sane parameters */ + if (!write_data) write_len = 0; + if (!read_data) read_len = 0; + if (write_len > PVR2_CTL_BUFFSIZE) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Attempted to execute %d byte" + " control-write transfer (limit=%d)", + write_len,PVR2_CTL_BUFFSIZE); + return -EINVAL; + } + if (read_len > PVR2_CTL_BUFFSIZE) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Attempted to execute %d byte" + " control-read transfer (limit=%d)", + write_len,PVR2_CTL_BUFFSIZE); + return -EINVAL; + } + if ((!write_len) && (!read_len)) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Attempted to execute null control transfer?"); + return -EINVAL; + } + + + hdw->cmd_debug_state = 1; + if (write_len) { + hdw->cmd_debug_code = ((unsigned char *)write_data)[0]; + } else { + hdw->cmd_debug_code = 0; + } + hdw->cmd_debug_write_len = write_len; + hdw->cmd_debug_read_len = read_len; + + /* Initialize common stuff */ + init_completion(&hdw->ctl_done); + hdw->ctl_timeout_flag = 0; + hdw->ctl_write_pend_flag = 0; + hdw->ctl_read_pend_flag = 0; + init_timer(&timer); + timer.expires = jiffies + timeout; + timer.data = (unsigned long)hdw; + timer.function = pvr2_ctl_timeout; + + if (write_len) { + hdw->cmd_debug_state = 2; + /* Transfer write data to internal buffer */ + for (idx = 0; idx < write_len; idx++) { + hdw->ctl_write_buffer[idx] = + ((unsigned char *)write_data)[idx]; + } + /* Initiate a write request */ + usb_fill_bulk_urb(hdw->ctl_write_urb, + hdw->usb_dev, + usb_sndbulkpipe(hdw->usb_dev, + PVR2_CTL_WRITE_ENDPOINT), + hdw->ctl_write_buffer, + write_len, + pvr2_ctl_write_complete, + hdw); + hdw->ctl_write_urb->actual_length = 0; + hdw->ctl_write_pend_flag = !0; + status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL); + if (status < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to submit write-control" + " URB status=%d",status); + hdw->ctl_write_pend_flag = 0; + goto done; + } + } + + if (read_len) { + hdw->cmd_debug_state = 3; + memset(hdw->ctl_read_buffer,0x43,read_len); + /* Initiate a read request */ + usb_fill_bulk_urb(hdw->ctl_read_urb, + hdw->usb_dev, + usb_rcvbulkpipe(hdw->usb_dev, + PVR2_CTL_READ_ENDPOINT), + hdw->ctl_read_buffer, + read_len, + pvr2_ctl_read_complete, + hdw); + hdw->ctl_read_urb->actual_length = 0; + hdw->ctl_read_pend_flag = !0; + status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL); + if (status < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to submit read-control" + " URB status=%d",status); + hdw->ctl_read_pend_flag = 0; + goto done; + } + } + + /* Start timer */ + add_timer(&timer); + + /* Now wait for all I/O to complete */ + hdw->cmd_debug_state = 4; + while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) { + wait_for_completion(&hdw->ctl_done); + } + hdw->cmd_debug_state = 5; + + /* Stop timer */ + del_timer_sync(&timer); + + hdw->cmd_debug_state = 6; + status = 0; + + if (hdw->ctl_timeout_flag) { + status = -ETIMEDOUT; + if (!probe_fl) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Timed out control-write"); + } + goto done; + } + + if (write_len) { + /* Validate results of write request */ + if ((hdw->ctl_write_urb->status != 0) && + (hdw->ctl_write_urb->status != -ENOENT) && + (hdw->ctl_write_urb->status != -ESHUTDOWN) && + (hdw->ctl_write_urb->status != -ECONNRESET)) { + /* USB subsystem is reporting some kind of failure + on the write */ + status = hdw->ctl_write_urb->status; + if (!probe_fl) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "control-write URB failure," + " status=%d", + status); + } + goto done; + } + if (hdw->ctl_write_urb->actual_length < write_len) { + /* Failed to write enough data */ + status = -EIO; + if (!probe_fl) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "control-write URB short," + " expected=%d got=%d", + write_len, + hdw->ctl_write_urb->actual_length); + } + goto done; + } + } + if (read_len) { + /* Validate results of read request */ + if ((hdw->ctl_read_urb->status != 0) && + (hdw->ctl_read_urb->status != -ENOENT) && + (hdw->ctl_read_urb->status != -ESHUTDOWN) && + (hdw->ctl_read_urb->status != -ECONNRESET)) { + /* USB subsystem is reporting some kind of failure + on the read */ + status = hdw->ctl_read_urb->status; + if (!probe_fl) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "control-read URB failure," + " status=%d", + status); + } + goto done; + } + if (hdw->ctl_read_urb->actual_length < read_len) { + /* Failed to read enough data */ + status = -EIO; + if (!probe_fl) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "control-read URB short," + " expected=%d got=%d", + read_len, + hdw->ctl_read_urb->actual_length); + } + goto done; + } + /* Transfer retrieved data out from internal buffer */ + for (idx = 0; idx < read_len; idx++) { + ((unsigned char *)read_data)[idx] = + hdw->ctl_read_buffer[idx]; + } + } + + done: + + hdw->cmd_debug_state = 0; + if ((status < 0) && (!probe_fl)) { + pvr2_hdw_render_useless(hdw); + } + return status; +} + + +int pvr2_send_request(struct pvr2_hdw *hdw, + void *write_data,unsigned int write_len, + void *read_data,unsigned int read_len) +{ + return pvr2_send_request_ex(hdw,HZ*4,0, + write_data,write_len, + read_data,read_len); +} + + +static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode) +{ + int ret; + unsigned int cnt = 1; + unsigned int args = 0; + LOCK_TAKE(hdw->ctl_lock); + hdw->cmd_buffer[0] = cmdcode & 0xffu; + args = (cmdcode >> 8) & 0xffu; + args = (args > 2) ? 2 : args; + if (args) { + cnt += args; + hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu; + if (args > 1) { + hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu; + } + } + if (pvrusb2_debug & PVR2_TRACE_INIT) { + unsigned int idx; + unsigned int ccnt,bcnt; + char tbuf[50]; + cmdcode &= 0xffu; + bcnt = 0; + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + "Sending FX2 command 0x%x",cmdcode); + bcnt += ccnt; + for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) { + if (pvr2_fx2cmd_desc[idx].id == cmdcode) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " \"%s\"", + pvr2_fx2cmd_desc[idx].desc); + bcnt += ccnt; + break; + } + } + if (args) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " (%u",hdw->cmd_buffer[1]); + bcnt += ccnt; + if (args > 1) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ",%u",hdw->cmd_buffer[2]); + bcnt += ccnt; + } + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ")"); + bcnt += ccnt; + } + pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf); + } + ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0); + LOCK_GIVE(hdw->ctl_lock); + return ret; +} + + +int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) +{ + int ret; + + LOCK_TAKE(hdw->ctl_lock); + + hdw->cmd_buffer[0] = FX2CMD_REG_WRITE; /* write register prefix */ + PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data); + hdw->cmd_buffer[5] = 0; + hdw->cmd_buffer[6] = (reg >> 8) & 0xff; + hdw->cmd_buffer[7] = reg & 0xff; + + + ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0); + + LOCK_GIVE(hdw->ctl_lock); + + return ret; +} + + +static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data) +{ + int ret = 0; + + LOCK_TAKE(hdw->ctl_lock); + + hdw->cmd_buffer[0] = FX2CMD_REG_READ; /* read register prefix */ + hdw->cmd_buffer[1] = 0; + hdw->cmd_buffer[2] = 0; + hdw->cmd_buffer[3] = 0; + hdw->cmd_buffer[4] = 0; + hdw->cmd_buffer[5] = 0; + hdw->cmd_buffer[6] = (reg >> 8) & 0xff; + hdw->cmd_buffer[7] = reg & 0xff; + + ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4); + *data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0); + + LOCK_GIVE(hdw->ctl_lock); + + return ret; +} + + +void pvr2_hdw_render_useless(struct pvr2_hdw *hdw) +{ + if (!hdw->flag_ok) return; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Device being rendered inoperable"); + if (hdw->vid_stream) { + pvr2_stream_setup(hdw->vid_stream,NULL,0,0); + } + hdw->flag_ok = 0; + trace_stbit("flag_ok",hdw->flag_ok); + pvr2_hdw_state_sched(hdw); +} + + +void pvr2_hdw_device_reset(struct pvr2_hdw *hdw) +{ + int ret; + pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset..."); + ret = usb_lock_device_for_reset(hdw->usb_dev,NULL); + if (ret == 0) { + ret = usb_reset_device(hdw->usb_dev); + usb_unlock_device(hdw->usb_dev); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to lock USB device ret=%d",ret); + } + if (init_pause_msec) { + pvr2_trace(PVR2_TRACE_INFO, + "Waiting %u msec for hardware to settle", + init_pause_msec); + msleep(init_pause_msec); + } + +} + + +void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val) +{ + char *da; + unsigned int pipe; + int ret; + + if (!hdw->usb_dev) return; + + da = kmalloc(16, GFP_KERNEL); + + if (da == NULL) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Unable to allocate memory to control CPU reset"); + return; + } + + pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val); + + da[0] = val ? 0x01 : 0x00; + + /* Write the CPUCS register on the 8051. The lsb of the register + is the reset bit; a 1 asserts reset while a 0 clears it. */ + pipe = usb_sndctrlpipe(hdw->usb_dev, 0); + ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,HZ); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "cpureset_assert(%d) error=%d",val,ret); + pvr2_hdw_render_useless(hdw); + } + + kfree(da); +} + + +int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) +{ + return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET); +} + + +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) +{ + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON); +} + + +int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) +{ + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF); +} + + +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) +{ + pvr2_trace(PVR2_TRACE_INIT, + "Requesting decoder reset"); + if (hdw->decoder_client_id) { + v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, + core, reset, 0); + pvr2_hdw_cx25840_vbi_hack(hdw); + return 0; + } + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: nothing attached"); + return -ENOTTY; +} + + +static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) +{ + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_DEMOD_RESETIN | + (1 << 8) | + ((onoff ? 1 : 0) << 16)); +} + + +static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) +{ + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_POWER_ON : + FX2CMD_ONAIR_DTV_POWER_OFF)); +} + + +static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, + int onoff) +{ + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_STREAMING_ON : + FX2CMD_ONAIR_DTV_STREAMING_OFF)); +} + + +static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) +{ + int cmode; + /* Compare digital/analog desired setting with current setting. If + they don't match, fix it... */ + cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG); + if (cmode == hdw->pathway_state) { + /* They match; nothing to do */ + return; + } + + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl); + if (cmode == PVR2_PATHWAY_ANALOG) { + /* If moving to analog mode, also force the decoder + to reset. If no decoder is attached, then it's + ok to ignore this because if/when the decoder + attaches, it will reset itself at that time. */ + pvr2_hdw_cmd_decoder_reset(hdw); + } + break; + case PVR2_DIGITAL_SCHEME_ONAIR: + /* Supposedly we should always have the power on whether in + digital or analog mode. But for now do what appears to + work... */ + pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl); + break; + default: break; + } + + pvr2_hdw_untrip_unlocked(hdw); + hdw->pathway_state = cmode; +} + + +static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) +{ + /* change some GPIO data + * + * note: bit d7 of dir appears to control the LED, + * so we shut it off here. + * + */ + if (onoff) { + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481); + } else { + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401); + } + pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000); +} + + +typedef void (*led_method_func)(struct pvr2_hdw *,int); + +static led_method_func led_methods[] = { + [PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge, +}; + + +/* Toggle LED */ +static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) +{ + unsigned int scheme_id; + led_method_func fp; + + if ((!onoff) == (!hdw->led_on)) return; + + hdw->led_on = onoff != 0; + + scheme_id = hdw->hdw_desc->led_scheme; + if (scheme_id < ARRAY_SIZE(led_methods)) { + fp = led_methods[scheme_id]; + } else { + fp = NULL; + } + + if (fp) (*fp)(hdw,onoff); +} + + +/* Stop / start video stream transport */ +static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) +{ + int ret; + + /* If we're in analog mode, then just issue the usual analog + command. */ + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + /*Note: Not reached */ + } + + if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) { + /* Whoops, we don't know what mode we're in... */ + return -EINVAL; + } + + /* To get here we have to be in digital mode. The mechanism here + is unfortunately different for different vendors. So we switch + on the device's digital scheme attribute in order to figure out + what to do. */ + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF)); + case PVR2_DIGITAL_SCHEME_ONAIR: + ret = pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + if (ret) return ret; + return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl); + default: + return -EINVAL; + } +} + + +/* Evaluate whether or not state_pathway_ok can change */ +static int state_eval_pathway_ok(struct pvr2_hdw *hdw) +{ + if (hdw->state_pathway_ok) { + /* Nothing to do if pathway is already ok */ + return 0; + } + if (!hdw->state_pipeline_idle) { + /* Not allowed to change anything if pipeline is not idle */ + return 0; + } + pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV); + hdw->state_pathway_ok = !0; + trace_stbit("state_pathway_ok",hdw->state_pathway_ok); + return !0; +} + + +/* Evaluate whether or not state_encoder_ok can change */ +static int state_eval_encoder_ok(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_ok) return 0; + if (hdw->flag_tripped) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->state_encoder_config) return 0; + if (hdw->state_decoder_run) return 0; + if (hdw->state_usbstream_run) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) { + if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0; + } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) { + return 0; + } + + if (pvr2_upload_firmware2(hdw) < 0) { + hdw->flag_tripped = !0; + trace_stbit("flag_tripped",hdw->flag_tripped); + return !0; + } + hdw->state_encoder_ok = !0; + trace_stbit("state_encoder_ok",hdw->state_encoder_ok); + return !0; +} + + +/* Evaluate whether or not state_encoder_config can change */ +static int state_eval_encoder_config(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_config) { + if (hdw->state_encoder_ok) { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause) return 0; + } + hdw->state_encoder_config = 0; + hdw->state_encoder_waitok = 0; + trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); + /* paranoia - solve race if timer just completed */ + del_timer_sync(&hdw->encoder_wait_timer); + } else { + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_encoder_ok || + !hdw->state_pipeline_idle || + hdw->state_pipeline_pause || + !hdw->state_pipeline_req || + !hdw->state_pipeline_config) { + /* We must reset the enforced wait interval if + anything has happened that might have disturbed + the encoder. This should be a rare case. */ + if (timer_pending(&hdw->encoder_wait_timer)) { + del_timer_sync(&hdw->encoder_wait_timer); + } + if (hdw->state_encoder_waitok) { + /* Must clear the state - therefore we did + something to a state bit and must also + return true. */ + hdw->state_encoder_waitok = 0; + trace_stbit("state_encoder_waitok", + hdw->state_encoder_waitok); + return !0; + } + return 0; + } + if (!hdw->state_encoder_waitok) { + if (!timer_pending(&hdw->encoder_wait_timer)) { + /* waitok flag wasn't set and timer isn't + running. Check flag once more to avoid + a race then start the timer. This is + the point when we measure out a minimal + quiet interval before doing something to + the encoder. */ + if (!hdw->state_encoder_waitok) { + hdw->encoder_wait_timer.expires = + jiffies + + (HZ * TIME_MSEC_ENCODER_WAIT + / 1000); + add_timer(&hdw->encoder_wait_timer); + } + } + /* We can't continue until we know we have been + quiet for the interval measured by this + timer. */ + return 0; + } + pvr2_encoder_configure(hdw); + if (hdw->state_encoder_ok) hdw->state_encoder_config = !0; + } + trace_stbit("state_encoder_config",hdw->state_encoder_config); + return !0; +} + + +/* Return true if the encoder should not be running. */ +static int state_check_disable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Encoder isn't healthy at the moment, so stop it. */ + return !0; + } + if (!hdw->state_pathway_ok) { + /* Mode is not understood at the moment (i.e. it wants to + change), so encoder must be stopped. */ + return !0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (!hdw->state_decoder_run) { + /* We're in analog mode and the decoder is not + running; thus the encoder should be stopped as + well. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if (hdw->state_encoder_runok) { + /* This is a funny case. We're in digital mode so + really the encoder should be stopped. However + if it really is running, only kill it after + runok has been set. This gives a chance for the + onair quirk to function (encoder must run + briefly first, at least once, before onair + digital streaming can work). */ + return !0; + } + break; + default: + /* Unknown mode; so encoder should be stopped. */ + return !0; + } + + /* If we get here, we haven't found a reason to stop the + encoder. */ + return 0; +} + + +/* Return true if the encoder should be running. */ +static int state_check_enable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Don't run the encoder if it isn't healthy... */ + return 0; + } + if (!hdw->state_pathway_ok) { + /* Don't run the encoder if we don't (yet) know what mode + we need to be in... */ + return 0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (hdw->state_decoder_run && hdw->state_decoder_ready) { + /* In analog mode, if the decoder is running, then + run the encoder. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if ((hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) && + !hdw->state_encoder_runok) { + /* This is a quirk. OnAir hardware won't stream + digital until the encoder has been run at least + once, for a minimal period of time (empiricially + measured to be 1/4 second). So if we're on + OnAir hardware and the encoder has never been + run at all, then start the encoder. Normal + state machine logic in the driver will + automatically handle the remaining bits. */ + return !0; + } + break; + default: + /* For completeness (unknown mode; encoder won't run ever) */ + break; + } + /* If we get here, then we haven't found any reason to run the + encoder, so don't run it. */ + return 0; +} + + +/* Evaluate whether or not state_encoder_run can change */ +static int state_eval_encoder_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_run) { + if (!state_check_disable_encoder_run(hdw)) return 0; + if (hdw->state_encoder_ok) { + del_timer_sync(&hdw->encoder_run_timer); + if (pvr2_encoder_stop(hdw) < 0) return !0; + } + hdw->state_encoder_run = 0; + } else { + if (!state_check_enable_encoder_run(hdw)) return 0; + if (pvr2_encoder_start(hdw) < 0) return !0; + hdw->state_encoder_run = !0; + if (!hdw->state_encoder_runok) { + hdw->encoder_run_timer.expires = + jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000); + add_timer(&hdw->encoder_run_timer); + } + } + trace_stbit("state_encoder_run",hdw->state_encoder_run); + return !0; +} + + +/* Timeout function for quiescent timer. */ +static void pvr2_hdw_quiescent_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_decoder_quiescent = !0; + trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +/* Timeout function for decoder stabilization timer. */ +static void pvr2_hdw_decoder_stabilization_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_decoder_ready = !0; + trace_stbit("state_decoder_ready", hdw->state_decoder_ready); + hdw->state_stale = !0; + queue_work(hdw->workqueue, &hdw->workpoll); +} + + +/* Timeout function for encoder wait timer. */ +static void pvr2_hdw_encoder_wait_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_encoder_waitok = !0; + trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +/* Timeout function for encoder run timer. */ +static void pvr2_hdw_encoder_run_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + if (!hdw->state_encoder_runok) { + hdw->state_encoder_runok = !0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); + } +} + + +/* Evaluate whether or not state_decoder_run can change */ +static int state_eval_decoder_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_decoder_run) { + if (hdw->state_encoder_ok) { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) return 0; + } + if (!hdw->flag_decoder_missed) { + pvr2_decoder_enable(hdw,0); + } + hdw->state_decoder_quiescent = 0; + hdw->state_decoder_run = 0; + /* paranoia - solve race if timer(s) just completed */ + del_timer_sync(&hdw->quiescent_timer); + /* Kill the stabilization timer, in case we're killing the + encoder before the previous stabilization interval has + been properly timed. */ + del_timer_sync(&hdw->decoder_stabilization_timer); + hdw->state_decoder_ready = 0; + } else { + if (!hdw->state_decoder_quiescent) { + if (!timer_pending(&hdw->quiescent_timer)) { + /* We don't do something about the + quiescent timer until right here because + we also want to catch cases where the + decoder was already not running (like + after initialization) as opposed to + knowing that we had just stopped it. + The second flag check is here to cover a + race - the timer could have run and set + this flag just after the previous check + but before we did the pending check. */ + if (!hdw->state_decoder_quiescent) { + hdw->quiescent_timer.expires = + jiffies + + (HZ * TIME_MSEC_DECODER_WAIT + / 1000); + add_timer(&hdw->quiescent_timer); + } + } + /* Don't allow decoder to start again until it has + been quiesced first. This little detail should + hopefully further stabilize the encoder. */ + return 0; + } + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_pipeline_req || + hdw->state_pipeline_pause || + !hdw->state_pipeline_config || + !hdw->state_encoder_config || + !hdw->state_encoder_ok) return 0; + del_timer_sync(&hdw->quiescent_timer); + if (hdw->flag_decoder_missed) return 0; + if (pvr2_decoder_enable(hdw,!0) < 0) return 0; + hdw->state_decoder_quiescent = 0; + hdw->state_decoder_ready = 0; + hdw->state_decoder_run = !0; + if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) { + hdw->decoder_stabilization_timer.expires = + jiffies + + (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT / + 1000); + add_timer(&hdw->decoder_stabilization_timer); + } else { + hdw->state_decoder_ready = !0; + } + } + trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); + trace_stbit("state_decoder_run",hdw->state_decoder_run); + trace_stbit("state_decoder_ready", hdw->state_decoder_ready); + return !0; +} + + +/* Evaluate whether or not state_usbstream_run can change */ +static int state_eval_usbstream_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_usbstream_run) { + int fl = !0; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + fl = (hdw->state_encoder_ok && + hdw->state_encoder_run); + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + fl = hdw->state_encoder_ok; + } + if (fl && + hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) { + return 0; + } + pvr2_hdw_cmd_usbstream(hdw,0); + hdw->state_usbstream_run = 0; + } else { + if (!hdw->state_pipeline_req || + hdw->state_pipeline_pause || + !hdw->state_pathway_ok) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + if (!hdw->state_encoder_ok || + !hdw->state_encoder_run) return 0; + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + if (!hdw->state_encoder_ok) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) { + /* OnAir digital receivers won't stream + unless the analog encoder has run first. + Why? I have no idea. But don't even + try until we know the analog side is + known to have run. */ + if (!hdw->state_encoder_runok) return 0; + } + } + if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; + hdw->state_usbstream_run = !0; + } + trace_stbit("state_usbstream_run",hdw->state_usbstream_run); + return !0; +} + + +/* Attempt to configure pipeline, if needed */ +static int state_eval_pipeline_config(struct pvr2_hdw *hdw) +{ + if (hdw->state_pipeline_config || + hdw->state_pipeline_pause) return 0; + pvr2_hdw_commit_execute(hdw); + return !0; +} + + +/* Update pipeline idle and pipeline pause tracking states based on other + inputs. This must be called whenever the other relevant inputs have + changed. */ +static int state_update_pipeline_state(struct pvr2_hdw *hdw) +{ + unsigned int st; + int updatedFl = 0; + /* Update pipeline state */ + st = !(hdw->state_encoder_run || + hdw->state_decoder_run || + hdw->state_usbstream_run || + (!hdw->state_decoder_quiescent)); + if (!st != !hdw->state_pipeline_idle) { + hdw->state_pipeline_idle = st; + updatedFl = !0; + } + if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) { + hdw->state_pipeline_pause = 0; + updatedFl = !0; + } + return updatedFl; +} + + +typedef int (*state_eval_func)(struct pvr2_hdw *); + +/* Set of functions to be run to evaluate various states in the driver. */ +static const state_eval_func eval_funcs[] = { + state_eval_pathway_ok, + state_eval_pipeline_config, + state_eval_encoder_ok, + state_eval_encoder_config, + state_eval_decoder_run, + state_eval_encoder_run, + state_eval_usbstream_run, +}; + + +/* Process various states and return true if we did anything interesting. */ +static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) +{ + unsigned int i; + int state_updated = 0; + int check_flag; + + if (!hdw->state_stale) return 0; + if ((hdw->fw1_state != FW1_STATE_OK) || + !hdw->flag_ok) { + hdw->state_stale = 0; + return !0; + } + /* This loop is the heart of the entire driver. It keeps trying to + evaluate various bits of driver state until nothing changes for + one full iteration. Each "bit of state" tracks some global + aspect of the driver, e.g. whether decoder should run, if + pipeline is configured, usb streaming is on, etc. We separately + evaluate each of those questions based on other driver state to + arrive at the correct running configuration. */ + do { + check_flag = 0; + state_update_pipeline_state(hdw); + /* Iterate over each bit of state */ + for (i = 0; (iflag_ok; i++) { + if ((*eval_funcs[i])(hdw)) { + check_flag = !0; + state_updated = !0; + state_update_pipeline_state(hdw); + } + } + } while (check_flag && hdw->flag_ok); + hdw->state_stale = 0; + trace_stbit("state_stale",hdw->state_stale); + return state_updated; +} + + +static unsigned int print_input_mask(unsigned int msk, + char *buf,unsigned int acnt) +{ + unsigned int idx,ccnt; + unsigned int tcnt = 0; + for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) { + if (!((1 << idx) & msk)) continue; + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "%s%s", + (tcnt ? ", " : ""), + control_values_input[idx]); + tcnt += ccnt; + } + return tcnt; +} + + +static const char *pvr2_pathway_state_name(int id) +{ + switch (id) { + case PVR2_PATHWAY_ANALOG: return "analog"; + case PVR2_PATHWAY_DIGITAL: return "digital"; + default: return "unknown"; + } +} + + +static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, + char *buf,unsigned int acnt) +{ + switch (which) { + case 0: + return scnprintf( + buf,acnt, + "driver:%s%s%s%s%s ", + (hdw->flag_ok ? " " : " "), + (hdw->flag_init_ok ? " " : " "), + (hdw->flag_disconnected ? " " : + " "), + (hdw->flag_tripped ? " " : ""), + (hdw->flag_decoder_missed ? " " : ""), + pvr2_pathway_state_name(hdw->pathway_state)); + + case 1: + return scnprintf( + buf,acnt, + "pipeline:%s%s%s%s", + (hdw->state_pipeline_idle ? " " : ""), + (hdw->state_pipeline_config ? + " " : " "), + (hdw->state_pipeline_req ? " " : ""), + (hdw->state_pipeline_pause ? " " : "")); + case 2: + return scnprintf( + buf,acnt, + "worker:%s%s%s%s%s%s%s", + (hdw->state_decoder_run ? + (hdw->state_decoder_ready ? + "" : " ") : + (hdw->state_decoder_quiescent ? + "" : " ")), + (hdw->state_decoder_quiescent ? + " " : ""), + (hdw->state_encoder_ok ? + "" : " "), + (hdw->state_encoder_run ? + (hdw->state_encoder_runok ? + " " : + " ") : + (hdw->state_encoder_runok ? + " " : + " ")), + (hdw->state_encoder_config ? + " " : + (hdw->state_encoder_waitok ? + "" : " ")), + (hdw->state_usbstream_run ? + " " : " "), + (hdw->state_pathway_ok ? + " " : "")); + case 3: + return scnprintf( + buf,acnt, + "state: %s", + pvr2_get_state_name(hdw->master_state)); + case 4: { + unsigned int tcnt = 0; + unsigned int ccnt; + + ccnt = scnprintf(buf, + acnt, + "Hardware supported inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_avail_mask, + buf+tcnt, + acnt-tcnt); + if (hdw->input_avail_mask != hdw->input_allowed_mask) { + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "; allowed inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_allowed_mask, + buf+tcnt, + acnt-tcnt); + } + return tcnt; + } + case 5: { + struct pvr2_stream_stats stats; + if (!hdw->vid_stream) break; + pvr2_stream_get_stats(hdw->vid_stream, + &stats, + 0); + return scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + } + case 6: { + unsigned int id = hdw->ir_scheme_active; + return scnprintf(buf, acnt, "ir scheme: id=%d %s", id, + (id >= ARRAY_SIZE(ir_scheme_names) ? + "?" : ir_scheme_names[id])); + } + default: break; + } + return 0; +} + + +/* Generate report containing info about attached sub-devices and attached + i2c clients, including an indication of which attached i2c clients are + actually sub-devices. */ +static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw, + char *buf, unsigned int acnt) +{ + struct v4l2_subdev *sd; + unsigned int tcnt = 0; + unsigned int ccnt; + struct i2c_client *client; + const char *p; + unsigned int id; + + ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n"); + tcnt += ccnt; + v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { + id = sd->grp_id; + p = NULL; + if (id < ARRAY_SIZE(module_names)) p = module_names[id]; + if (p) { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, " %s:", p); + tcnt += ccnt; + } else { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " (unknown id=%u):", id); + tcnt += ccnt; + } + client = v4l2_get_subdevdata(sd); + if (client) { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " %s @ %02x\n", client->name, + client->addr); + tcnt += ccnt; + } else { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " no i2c client\n"); + tcnt += ccnt; + } + } + return tcnt; +} + + +unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, + char *buf,unsigned int acnt) +{ + unsigned int bcnt,ccnt,idx; + bcnt = 0; + LOCK_TAKE(hdw->big_lock); + for (idx = 0; ; idx++) { + ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt); + if (!ccnt) break; + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + if (!acnt) break; + buf[0] = '\n'; ccnt = 1; + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + } + ccnt = pvr2_hdw_report_clients(hdw, buf, acnt); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + LOCK_GIVE(hdw->big_lock); + return bcnt; +} + + +static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw) +{ + char buf[256]; + unsigned int idx, ccnt; + unsigned int lcnt, ucnt; + + for (idx = 0; ; idx++) { + ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf)); + if (!ccnt) break; + printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf); + } + ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf)); + ucnt = 0; + while (ucnt < ccnt) { + lcnt = 0; + while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) { + lcnt++; + } + printk(KERN_INFO "%s %.*s\n", hdw->name, lcnt, buf + ucnt); + ucnt += lcnt + 1; + } +} + + +/* Evaluate and update the driver's current state, taking various actions + as appropriate for the update. */ +static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) +{ + unsigned int st; + int state_updated = 0; + int callback_flag = 0; + int analog_mode; + + pvr2_trace(PVR2_TRACE_STBITS, + "Drive state check START"); + if (pvrusb2_debug & PVR2_TRACE_STBITS) { + pvr2_hdw_state_log_state(hdw); + } + + /* Process all state and get back over disposition */ + state_updated = pvr2_hdw_state_update(hdw); + + analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL); + + /* Update master state based upon all other states. */ + if (!hdw->flag_ok) { + st = PVR2_STATE_DEAD; + } else if (hdw->fw1_state != FW1_STATE_OK) { + st = PVR2_STATE_COLD; + } else if ((analog_mode || + hdw->hdw_desc->flag_digital_requires_cx23416) && + !hdw->state_encoder_ok) { + st = PVR2_STATE_WARM; + } else if (hdw->flag_tripped || + (analog_mode && hdw->flag_decoder_missed)) { + st = PVR2_STATE_ERROR; + } else if (hdw->state_usbstream_run && + (!analog_mode || + (hdw->state_encoder_run && hdw->state_decoder_run))) { + st = PVR2_STATE_RUN; + } else { + st = PVR2_STATE_READY; + } + if (hdw->master_state != st) { + pvr2_trace(PVR2_TRACE_STATE, + "Device state change from %s to %s", + pvr2_get_state_name(hdw->master_state), + pvr2_get_state_name(st)); + pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN); + hdw->master_state = st; + state_updated = !0; + callback_flag = !0; + } + if (state_updated) { + /* Trigger anyone waiting on any state changes here. */ + wake_up(&hdw->state_wait_data); + } + + if (pvrusb2_debug & PVR2_TRACE_STBITS) { + pvr2_hdw_state_log_state(hdw); + } + pvr2_trace(PVR2_TRACE_STBITS, + "Drive state check DONE callback=%d",callback_flag); + + return callback_flag; +} + + +/* Cause kernel thread to check / update driver state */ +static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) +{ + if (hdw->state_stale) return; + hdw->state_stale = !0; + trace_stbit("state_stale",hdw->state_stale); + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) +{ + return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp); +} + + +int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp) +{ + return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp); +} + + +int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp) +{ + return pvr2_read_register(hdw,PVR2_GPIO_IN,dp); +} + + +int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val) +{ + u32 cval,nval; + int ret; + if (~msk) { + ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval); + if (ret) return ret; + nval = (cval & ~msk) | (val & msk); + pvr2_trace(PVR2_TRACE_GPIO, + "GPIO direction changing 0x%x:0x%x" + " from 0x%x to 0x%x", + msk,val,cval,nval); + } else { + nval = val; + pvr2_trace(PVR2_TRACE_GPIO, + "GPIO direction changing to 0x%x",nval); + } + return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval); +} + + +int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) +{ + u32 cval,nval; + int ret; + if (~msk) { + ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval); + if (ret) return ret; + nval = (cval & ~msk) | (val & msk); + pvr2_trace(PVR2_TRACE_GPIO, + "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x", + msk,val,cval,nval); + } else { + nval = val; + pvr2_trace(PVR2_TRACE_GPIO, + "GPIO output changing to 0x%x",nval); + } + return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval); +} + + +void pvr2_hdw_status_poll(struct pvr2_hdw *hdw) +{ + struct v4l2_tuner *vtp = &hdw->tuner_signal_info; + memset(vtp, 0, sizeof(*vtp)); + vtp->type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + hdw->tuner_signal_stale = 0; + /* Note: There apparently is no replacement for VIDIOC_CROPCAP + using v4l2-subdev - therefore we can't support that AT ALL right + now. (Of course, no sub-drivers seem to implement it either. + But now it's a a chicken and egg problem...) */ + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp); + pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll" + " type=%u strength=%u audio=0x%x cap=0x%x" + " low=%u hi=%u", + vtp->type, + vtp->signal, vtp->rxsubchans, vtp->capability, + vtp->rangelow, vtp->rangehigh); + + /* We have to do this to avoid getting into constant polling if + there's nobody to answer a poll of cropcap info. */ + hdw->cropcap_stale = 0; +} + + +unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) +{ + return hdw->input_avail_mask; +} + + +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw) +{ + return hdw->input_allowed_mask; +} + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v) +{ + if (hdw->input_val != v) { + hdw->input_val = v; + hdw->input_dirty = !0; + } + + /* Handle side effects - if we switch to a mode that needs the RF + tuner, then select the right frequency choice as well and mark + it dirty. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || + (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + return 0; +} + + +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw, + unsigned int change_mask, + unsigned int change_val) +{ + int ret = 0; + unsigned int nv,m,idx; + LOCK_TAKE(hdw->big_lock); + do { + nv = hdw->input_allowed_mask & ~change_mask; + nv |= (change_val & change_mask); + nv &= hdw->input_avail_mask; + if (!nv) { + /* No legal modes left; return error instead. */ + ret = -EPERM; + break; + } + hdw->input_allowed_mask = nv; + if ((1 << hdw->input_val) & hdw->input_allowed_mask) { + /* Current mode is still in the allowed mask, so + we're done. */ + break; + } + /* Select and switch to a mode that is still in the allowed + mask */ + if (!hdw->input_allowed_mask) { + /* Nothing legal; give up */ + break; + } + m = hdw->input_allowed_mask; + for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + pvr2_hdw_set_input(hdw,idx); + break; + } + } while (0); + LOCK_GIVE(hdw->big_lock); + return ret; +} + + +/* Find I2C address of eeprom */ +static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) +{ + int result; + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR; + result = pvr2_send_request(hdw, + hdw->cmd_buffer,1, + hdw->cmd_buffer,1); + if (result < 0) break; + result = hdw->cmd_buffer[0]; + } while(0); LOCK_GIVE(hdw->ctl_lock); + return result; +} + + +int pvr2_hdw_register_access(struct pvr2_hdw *hdw, + struct v4l2_dbg_match *match, u64 reg_id, + int setFl, u64 *val_ptr) +{ +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct v4l2_dbg_register req; + int stat = 0; + int okFl = 0; + + if (!capable(CAP_SYS_ADMIN)) return -EPERM; + + req.match = *match; + req.reg = reg_id; + if (setFl) req.val = *val_ptr; + /* It would be nice to know if a sub-device answered the request */ + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, g_register, &req); + if (!setFl) *val_ptr = req.val; + if (okFl) { + return stat; + } + return -EINVAL; +#else + return -ENOSYS; +#endif +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h new file mode 100644 index 000000000000..8060fc666eeb --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h @@ -0,0 +1,360 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_HDW_H +#define __PVRUSB2_HDW_H + +#include +#include +#include "pvrusb2-io.h" +#include "pvrusb2-ctrl.h" + + +/* Private internal control ids, look these up with + pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ +#define PVR2_CID_STDCUR 2 +#define PVR2_CID_STDAVAIL 3 +#define PVR2_CID_INPUT 4 +#define PVR2_CID_AUDIOMODE 5 +#define PVR2_CID_FREQUENCY 6 +#define PVR2_CID_HRES 7 +#define PVR2_CID_VRES 8 +#define PVR2_CID_CROPL 9 +#define PVR2_CID_CROPT 10 +#define PVR2_CID_CROPW 11 +#define PVR2_CID_CROPH 12 +#define PVR2_CID_CROPCAPPAN 13 +#define PVR2_CID_CROPCAPPAD 14 +#define PVR2_CID_CROPCAPBL 15 +#define PVR2_CID_CROPCAPBT 16 +#define PVR2_CID_CROPCAPBW 17 +#define PVR2_CID_CROPCAPBH 18 +#define PVR2_CID_STDDETECT 19 + +/* Legal values for the INPUT state variable */ +#define PVR2_CVAL_INPUT_TV 0 +#define PVR2_CVAL_INPUT_DTV 1 +#define PVR2_CVAL_INPUT_COMPOSITE 2 +#define PVR2_CVAL_INPUT_SVIDEO 3 +#define PVR2_CVAL_INPUT_RADIO 4 + +enum pvr2_config { + pvr2_config_empty, /* No configuration */ + pvr2_config_mpeg, /* Encoded / compressed video */ + pvr2_config_vbi, /* Standard vbi info */ + pvr2_config_pcm, /* Audio raw pcm stream */ + pvr2_config_rawvideo, /* Video raw frames */ +}; + +enum pvr2_v4l_type { + pvr2_v4l_type_video, + pvr2_v4l_type_vbi, + pvr2_v4l_type_radio, +}; + +/* Major states that we can be in: + * + * DEAD - Device is in an unusable state and cannot be recovered. This + * can happen if we completely lose the ability to communicate with it + * (but it might still on the bus). In this state there's nothing we can + * do; it must be replugged in order to recover. + * + * COLD - Device is in an unusable state, needs microcontroller firmware. + * + * WARM - We can communicate with the device and the proper + * microcontroller firmware is running, but other device initialization is + * still needed (e.g. encoder firmware). + * + * ERROR - A problem prevents capture operation (e.g. encoder firmware + * missing). + * + * READY - Device is operational, but not streaming. + * + * RUN - Device is streaming. + * + */ +#define PVR2_STATE_NONE 0 +#define PVR2_STATE_DEAD 1 +#define PVR2_STATE_COLD 2 +#define PVR2_STATE_WARM 3 +#define PVR2_STATE_ERROR 4 +#define PVR2_STATE_READY 5 +#define PVR2_STATE_RUN 6 + +/* Translate configuration enum to a string label */ +const char *pvr2_config_get_name(enum pvr2_config); + +struct pvr2_hdw; + +/* Create and return a structure for interacting with the underlying + hardware */ +struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, + const struct usb_device_id *devid); + +/* Perform second stage initialization, passing in a notification callback + for when the master state changes. */ +int pvr2_hdw_initialize(struct pvr2_hdw *, + void (*callback_func)(void *), + void *callback_data); + +/* Destroy hardware interaction structure */ +void pvr2_hdw_destroy(struct pvr2_hdw *); + +/* Return true if in the ready (normal) state */ +int pvr2_hdw_dev_ok(struct pvr2_hdw *); + +/* Return small integer number [1..N] for logical instance number of this + device. This is useful for indexing array-valued module parameters. */ +int pvr2_hdw_get_unit_number(struct pvr2_hdw *); + +/* Get pointer to underlying USB device */ +struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *); + +/* Retrieve serial number of device */ +unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); + +/* Retrieve bus location info of device */ +const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *); + +/* Retrieve per-instance string identifier for this specific device */ +const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *); + +/* Called when hardware has been unplugged */ +void pvr2_hdw_disconnect(struct pvr2_hdw *); + +/* Get the number of defined controls */ +unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *); + +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int); + +/* Retrieve a control handle given its internal ID (if any) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int); + +/* Retrieve a control handle given its V4L ID (if any) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id); + +/* Retrieve a control handle given its immediate predecessor V4L ID (if any) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *, + unsigned int ctl_id); + +/* Commit all control changes made up to this point */ +int pvr2_hdw_commit_ctl(struct pvr2_hdw *); + +/* Return a bit mask of valid input selections for this device. Mask bits + * will be according to PVR_CVAL_INPUT_xxxx definitions. */ +unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *); + +/* Return a bit mask of allowed input selections for this device. Mask bits + * will be according to PVR_CVAL_INPUT_xxxx definitions. */ +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *); + +/* Change the set of allowed input selections for this device. Both + change_mask and change_valu are mask bits according to + PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate + which settings are being changed and the change_val parameter indicates + whether corresponding settings are being set or cleared. */ +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *, + unsigned int change_mask, + unsigned int change_val); + +/* Return name for this driver instance */ +const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); + +/* Mark tuner status stale so that it will be re-fetched */ +void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *); + +/* Return information about the tuner */ +int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); + +/* Return information about cropping capabilities */ +int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *); + +/* Query device and see if it thinks it is on a high-speed USB link */ +int pvr2_hdw_is_hsm(struct pvr2_hdw *); + +/* Return a string token representative of the hardware type */ +const char *pvr2_hdw_get_type(struct pvr2_hdw *); + +/* Return a single line description of the hardware type */ +const char *pvr2_hdw_get_desc(struct pvr2_hdw *); + +/* Turn streaming on/off */ +int pvr2_hdw_set_streaming(struct pvr2_hdw *,int); + +/* Find out if streaming is on */ +int pvr2_hdw_get_streaming(struct pvr2_hdw *); + +/* Retrieve driver overall state */ +int pvr2_hdw_get_state(struct pvr2_hdw *); + +/* Configure the type of stream to generate */ +int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); + +/* Get handle to video output stream */ +struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); + +/* Enable / disable retrieval of CPU firmware or prom contents. This must + be enabled before pvr2_hdw_cpufw_get() will function. Note that doing + this may prevent the device from running (and leaving this mode may + imply a device reset). */ +void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, + int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */ + int enable_flag); + +/* Return true if we're in a mode for retrieval CPU firmware */ +int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *); + +/* Retrieve a piece of the CPU's firmware at the given offset. Return + value is the number of bytes retrieved or zero if we're past the end or + an error otherwise (e.g. if firmware retrieval is not enabled). */ +int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs, + char *buf,unsigned int cnt); + +/* Retrieve a previously stored v4l minor device number */ +int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *,enum pvr2_v4l_type index); + +/* Store a v4l minor device number */ +void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, + enum pvr2_v4l_type index,int); + +/* Direct read/write access to chip's registers: + match - specify criteria to identify target chip (this is a v4l dbg struct) + reg_id - register number to access + setFl - true to set the register, false to read it + val_ptr - storage location for source / result. */ +int pvr2_hdw_register_access(struct pvr2_hdw *, + struct v4l2_dbg_match *match, u64 reg_id, + int setFl, u64 *val_ptr); + +/* The following entry points are all lower level things you normally don't + want to worry about. */ + +/* Issue a command and get a response from the device. LOTS of higher + level stuff is built on this. */ +int pvr2_send_request(struct pvr2_hdw *, + void *write_ptr,unsigned int write_len, + void *read_ptr,unsigned int read_len); + +/* Slightly higher level device communication functions. */ +int pvr2_write_register(struct pvr2_hdw *, u16, u32); + +/* Call if for any reason we can't talk to the hardware anymore - this will + cause the driver to stop flailing on the device. */ +void pvr2_hdw_render_useless(struct pvr2_hdw *); + +/* Set / clear 8051's reset bit */ +void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int); + +/* Execute a USB-commanded device reset */ +void pvr2_hdw_device_reset(struct pvr2_hdw *); + +/* Reset worker's error trapping circuit breaker */ +int pvr2_hdw_untrip(struct pvr2_hdw *); + +/* Execute hard reset command (after this point it's likely that the + encoder will have to be reconfigured). This also clears the "useless" + state. */ +int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); + +/* Execute simple reset command */ +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); + +/* suspend */ +int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *); + +/* Order decoder to reset */ +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); + +/* Direct manipulation of GPIO bits */ +int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *); +int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *); +int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *); +int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val); +int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val); + +/* This data structure is specifically for the next function... */ +struct pvr2_hdw_debug_info { + int big_lock_held; + int ctl_lock_held; + int flag_disconnected; + int flag_init_ok; + int flag_ok; + int fw1_state; + int flag_decoder_missed; + int flag_tripped; + int state_encoder_ok; + int state_encoder_run; + int state_decoder_run; + int state_decoder_ready; + int state_usbstream_run; + int state_decoder_quiescent; + int state_pipeline_config; + int state_pipeline_req; + int state_pipeline_pause; + int state_pipeline_idle; + int cmd_debug_state; + int cmd_debug_write_len; + int cmd_debug_read_len; + int cmd_debug_write_pend; + int cmd_debug_read_pend; + int cmd_debug_timeout; + int cmd_debug_rstatus; + int cmd_debug_wstatus; + unsigned char cmd_code; +}; + +/* Non-intrusively retrieve internal state info - this is useful for + diagnosing lockups. Note that this operation is completed without any + kind of locking and so it is not atomic and may yield inconsistent + results. This is *purely* a debugging aid. */ +void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *); + +/* Intrusively retrieve internal state info - this is useful for + diagnosing overall driver state. This operation synchronizes against + the overall driver mutex - so if there are locking problems this will + likely hang! This is *purely* a debugging aid. */ +void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *); + +/* Report out several lines of text that describes driver internal state. + Results are written into the passed-in buffer. */ +unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, + char *buf_ptr,unsigned int buf_size); + +/* Cause modules to log their state once */ +void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); + +/* Cause encoder firmware to be uploaded into the device. This is normally + done autonomously, but the interface is exported here because it is also + a debugging aid. */ +int pvr2_upload_firmware2(struct pvr2_hdw *hdw); + +#endif /* __PVRUSB2_HDW_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c new file mode 100644 index 000000000000..885ce11f222d --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -0,0 +1,698 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "pvrusb2-i2c-core.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-fx2-cmd.h" +#include "pvrusb2.h" + +#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) + +/* + + This module attempts to implement a compliant I2C adapter for the pvrusb2 + device. + +*/ + +static unsigned int i2c_scan; +module_param(i2c_scan, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); + +static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 }; +module_param_array(ir_mode, int, NULL, 0444); +MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR"); + +static int pvr2_disable_ir_video; +module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video, + int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(disable_autoload_ir_video, + "1=do not try to autoload ir_video IR receiver"); + +static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ + u8 i2c_addr, /* I2C address we're talking to */ + u8 *data, /* Data to write */ + u16 length) /* Size of data to write */ +{ + /* Return value - default 0 means success */ + int ret; + + + if (!data) length = 0; + if (length > (sizeof(hdw->cmd_buffer) - 3)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Killing an I2C write to %u that is too large" + " (desired=%u limit=%u)", + i2c_addr, + length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3)); + return -ENOTSUPP; + } + + LOCK_TAKE(hdw->ctl_lock); + + /* Clear the command buffer (likely to be paranoia) */ + memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); + + /* Set up command buffer for an I2C write */ + hdw->cmd_buffer[0] = FX2CMD_I2C_WRITE; /* write prefix */ + hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */ + hdw->cmd_buffer[2] = length; /* length of what follows */ + if (length) memcpy(hdw->cmd_buffer + 3, data, length); + + /* Do the operation */ + ret = pvr2_send_request(hdw, + hdw->cmd_buffer, + length + 3, + hdw->cmd_buffer, + 1); + if (!ret) { + if (hdw->cmd_buffer[0] != 8) { + ret = -EIO; + if (hdw->cmd_buffer[0] != 7) { + trace_i2c("unexpected status" + " from i2_write[%d]: %d", + i2c_addr,hdw->cmd_buffer[0]); + } + } + } + + LOCK_GIVE(hdw->ctl_lock); + + return ret; +} + +static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ + u8 i2c_addr, /* I2C address we're talking to */ + u8 *data, /* Data to write */ + u16 dlen, /* Size of data to write */ + u8 *res, /* Where to put data we read */ + u16 rlen) /* Amount of data to read */ +{ + /* Return value - default 0 means success */ + int ret; + + + if (!data) dlen = 0; + if (dlen > (sizeof(hdw->cmd_buffer) - 4)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Killing an I2C read to %u that has wlen too large" + " (desired=%u limit=%u)", + i2c_addr, + dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4)); + return -ENOTSUPP; + } + if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Killing an I2C read to %u that has rlen too large" + " (desired=%u limit=%u)", + i2c_addr, + rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1)); + return -ENOTSUPP; + } + + LOCK_TAKE(hdw->ctl_lock); + + /* Clear the command buffer (likely to be paranoia) */ + memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); + + /* Set up command buffer for an I2C write followed by a read */ + hdw->cmd_buffer[0] = FX2CMD_I2C_READ; /* read prefix */ + hdw->cmd_buffer[1] = dlen; /* arg length */ + hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one + more byte (status). */ + hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */ + if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen); + + /* Do the operation */ + ret = pvr2_send_request(hdw, + hdw->cmd_buffer, + 4 + dlen, + hdw->cmd_buffer, + rlen + 1); + if (!ret) { + if (hdw->cmd_buffer[0] != 8) { + ret = -EIO; + if (hdw->cmd_buffer[0] != 7) { + trace_i2c("unexpected status" + " from i2_read[%d]: %d", + i2c_addr,hdw->cmd_buffer[0]); + } + } + } + + /* Copy back the result */ + if (res && rlen) { + if (ret) { + /* Error, just blank out the return buffer */ + memset(res, 0, rlen); + } else { + memcpy(res, hdw->cmd_buffer + 1, rlen); + } + } + + LOCK_GIVE(hdw->ctl_lock); + + return ret; +} + +/* This is the common low level entry point for doing I2C operations to the + hardware. */ +static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, + u8 i2c_addr, + u8 *wdata, + u16 wlen, + u8 *rdata, + u16 rlen) +{ + if (!rdata) rlen = 0; + if (!wdata) wlen = 0; + if (rlen || !wlen) { + return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen); + } else { + return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen); + } +} + + +/* This is a special entry point for cases of I2C transaction attempts to + the IR receiver. The implementation here simulates the IR receiver by + issuing a command to the FX2 firmware and using that response to return + what the real I2C receiver would have returned. We use this for 24xxx + devices, where the IR receiver chip has been removed and replaced with + FX2 related logic. */ +static int i2c_24xxx_ir(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + u8 dat[4]; + unsigned int stat; + + if (!(rlen || wlen)) { + /* This is a probe attempt. Just let it succeed. */ + return 0; + } + + /* We don't understand this kind of transaction */ + if ((wlen != 0) || (rlen == 0)) return -EIO; + + if (rlen < 3) { + /* Mike Isely Appears to be a probe + attempt from lirc. Just fill in zeroes and return. If + we try instead to do the full transaction here, then bad + things seem to happen within the lirc driver module + (version 0.8.0-7 sources from Debian, when run under + vanilla 2.6.17.6 kernel) - and I don't have the patience + to chase it down. */ + if (rlen > 0) rdata[0] = 0; + if (rlen > 1) rdata[1] = 0; + return 0; + } + + /* Issue a command to the FX2 to read the IR receiver. */ + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = FX2CMD_GET_IR_CODE; + stat = pvr2_send_request(hdw, + hdw->cmd_buffer,1, + hdw->cmd_buffer,4); + dat[0] = hdw->cmd_buffer[0]; + dat[1] = hdw->cmd_buffer[1]; + dat[2] = hdw->cmd_buffer[2]; + dat[3] = hdw->cmd_buffer[3]; + } while (0); LOCK_GIVE(hdw->ctl_lock); + + /* Give up if that operation failed. */ + if (stat != 0) return stat; + + /* Mangle the results into something that looks like the real IR + receiver. */ + rdata[2] = 0xc1; + if (dat[0] != 1) { + /* No code received. */ + rdata[0] = 0; + rdata[1] = 0; + } else { + u16 val; + /* Mash the FX2 firmware-provided IR code into something + that the normal i2c chip-level driver expects. */ + val = dat[1]; + val <<= 8; + val |= dat[2]; + val >>= 1; + val &= ~0x0003; + val |= 0x8000; + rdata[0] = (val >> 8) & 0xffu; + rdata[1] = val & 0xffu; + } + + return 0; +} + +/* This is a special entry point that is entered if an I2C operation is + attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this + part doesn't work, but we know it is really there. So let's look for + the autodetect attempt and just return success if we see that. */ +static int i2c_hack_wm8775(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + if (!(rlen || wlen)) { + // This is a probe attempt. Just let it succeed. + return 0; + } + return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); +} + +/* This is an entry point designed to always fail any attempt to perform a + transfer. We use this to cause certain I2C addresses to not be + probed. */ +static int i2c_black_hole(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + return -EIO; +} + +/* This is a special entry point that is entered if an I2C operation is + attempted to a cx25840 chip on model 24xxx hardware. This chip can + sometimes wedge itself. Worse still, when this happens msp3400 can + falsely detect this part and then the system gets hosed up after msp3400 + gets confused and dies. What we want to do here is try to keep msp3400 + away and also try to notice if the chip is wedged and send a warning to + the system log. */ +static int i2c_hack_cx25840(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + int ret; + unsigned int subaddr; + u8 wbuf[2]; + int state = hdw->i2c_cx25840_hack_state; + + if (!(rlen || wlen)) { + // Probe attempt - always just succeed and don't bother the + // hardware (this helps to make the state machine further + // down somewhat easier). + return 0; + } + + if (state == 3) { + return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); + } + + /* We're looking for the exact pattern where the revision register + is being read. The cx25840 module will always look at the + revision register first. Any other pattern of access therefore + has to be a probe attempt from somebody else so we'll reject it. + Normally we could just let each client just probe the part + anyway, but when the cx25840 is wedged, msp3400 will get a false + positive and that just screws things up... */ + + if (wlen == 0) { + switch (state) { + case 1: subaddr = 0x0100; break; + case 2: subaddr = 0x0101; break; + default: goto fail; + } + } else if (wlen == 2) { + subaddr = (wdata[0] << 8) | wdata[1]; + switch (subaddr) { + case 0x0100: state = 1; break; + case 0x0101: state = 2; break; + default: goto fail; + } + } else { + goto fail; + } + if (!rlen) goto success; + state = 0; + if (rlen != 1) goto fail; + + /* If we get to here then we have a legitimate read for one of the + two revision bytes, so pass it through. */ + wbuf[0] = subaddr >> 8; + wbuf[1] = subaddr; + ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen); + + if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Detected a wedged cx25840 chip;" + " the device will not work."); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Try power cycling the pvrusb2 device."); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Disabling further access to the device" + " to prevent other foul-ups."); + // This blocks all further communication with the part. + hdw->i2c_func[0x44] = NULL; + pvr2_hdw_render_useless(hdw); + goto fail; + } + + /* Success! */ + pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK."); + state = 3; + + success: + hdw->i2c_cx25840_hack_state = state; + return 0; + + fail: + hdw->i2c_cx25840_hack_state = state; + return -EIO; +} + +/* This is a very, very limited I2C adapter implementation. We can only + support what we actually know will work on the device... */ +static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + int ret = -ENOTSUPP; + pvr2_i2c_func funcp = NULL; + struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); + + if (!num) { + ret = -EINVAL; + goto done; + } + if (msgs[0].addr < PVR2_I2C_FUNC_CNT) { + funcp = hdw->i2c_func[msgs[0].addr]; + } + if (!funcp) { + ret = -EIO; + goto done; + } + + if (num == 1) { + if (msgs[0].flags & I2C_M_RD) { + /* Simple read */ + u16 tcnt,bcnt,offs; + if (!msgs[0].len) { + /* Length == 0 read. This is a probe. */ + if (funcp(hdw,msgs[0].addr,NULL,0,NULL,0)) { + ret = -EIO; + goto done; + } + ret = 1; + goto done; + } + /* If the read is short enough we'll do the whole + thing atomically. Otherwise we have no choice + but to break apart the reads. */ + tcnt = msgs[0].len; + offs = 0; + while (tcnt) { + bcnt = tcnt; + if (bcnt > sizeof(hdw->cmd_buffer)-1) { + bcnt = sizeof(hdw->cmd_buffer)-1; + } + if (funcp(hdw,msgs[0].addr,NULL,0, + msgs[0].buf+offs,bcnt)) { + ret = -EIO; + goto done; + } + offs += bcnt; + tcnt -= bcnt; + } + ret = 1; + goto done; + } else { + /* Simple write */ + ret = 1; + if (funcp(hdw,msgs[0].addr, + msgs[0].buf,msgs[0].len,NULL,0)) { + ret = -EIO; + } + goto done; + } + } else if (num == 2) { + if (msgs[0].addr != msgs[1].addr) { + trace_i2c("i2c refusing 2 phase transfer with" + " conflicting target addresses"); + ret = -ENOTSUPP; + goto done; + } + if ((!((msgs[0].flags & I2C_M_RD))) && + (msgs[1].flags & I2C_M_RD)) { + u16 tcnt,bcnt,wcnt,offs; + /* Write followed by atomic read. If the read + portion is short enough we'll do the whole thing + atomically. Otherwise we have no choice but to + break apart the reads. */ + tcnt = msgs[1].len; + wcnt = msgs[0].len; + offs = 0; + while (tcnt || wcnt) { + bcnt = tcnt; + if (bcnt > sizeof(hdw->cmd_buffer)-1) { + bcnt = sizeof(hdw->cmd_buffer)-1; + } + if (funcp(hdw,msgs[0].addr, + msgs[0].buf,wcnt, + msgs[1].buf+offs,bcnt)) { + ret = -EIO; + goto done; + } + offs += bcnt; + tcnt -= bcnt; + wcnt = 0; + } + ret = 2; + goto done; + } else { + trace_i2c("i2c refusing complex transfer" + " read0=%d read1=%d", + (msgs[0].flags & I2C_M_RD), + (msgs[1].flags & I2C_M_RD)); + } + } else { + trace_i2c("i2c refusing %d phase transfer",num); + } + + done: + if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) { + unsigned int idx,offs,cnt; + for (idx = 0; idx < num; idx++) { + cnt = msgs[idx].len; + printk(KERN_INFO + "pvrusb2 i2c xfer %u/%u:" + " addr=0x%x len=%d %s", + idx+1,num, + msgs[idx].addr, + cnt, + (msgs[idx].flags & I2C_M_RD ? + "read" : "write")); + if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) { + if (cnt > 8) cnt = 8; + printk(" ["); + for (offs = 0; offs < (cnt>8?8:cnt); offs++) { + if (offs) printk(" "); + printk("%02x",msgs[idx].buf[offs]); + } + if (offs < cnt) printk(" ..."); + printk("]"); + } + if (idx+1 == num) { + printk(" result=%d",ret); + } + printk("\n"); + } + if (!num) { + printk(KERN_INFO + "pvrusb2 i2c xfer null transfer result=%d\n", + ret); + } + } + return ret; +} + +static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm pvr2_i2c_algo_template = { + .master_xfer = pvr2_i2c_xfer, + .functionality = pvr2_i2c_functionality, +}; + +static struct i2c_adapter pvr2_i2c_adap_template = { + .owner = THIS_MODULE, + .class = 0, +}; + + +/* Return true if device exists at given address */ +static int do_i2c_probe(struct pvr2_hdw *hdw, int addr) +{ + struct i2c_msg msg[1]; + int rc; + msg[0].addr = 0; + msg[0].flags = I2C_M_RD; + msg[0].len = 0; + msg[0].buf = NULL; + msg[0].addr = addr; + rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg)); + return rc == 1; +} + +static void do_i2c_scan(struct pvr2_hdw *hdw) +{ + int i; + printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name); + for (i = 0; i < 128; i++) { + if (do_i2c_probe(hdw, i)) { + printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n", + hdw->name, i); + } + } + printk(KERN_INFO "%s: i2c scan done.\n", hdw->name); +} + +static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) +{ + struct i2c_board_info info; + struct IR_i2c_init_data *init_data = &hdw->ir_init_data; + if (pvr2_disable_ir_video) { + pvr2_trace(PVR2_TRACE_INFO, + "Automatic binding of ir_video has been disabled."); + return; + } + memset(&info, 0, sizeof(struct i2c_board_info)); + switch (hdw->ir_scheme_active) { + case PVR2_IR_SCHEME_24XXX: /* FX2-controlled IR */ + case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */ + init_data->ir_codes = RC_MAP_HAUPPAUGE; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; + init_data->type = RC_TYPE_RC5; + init_data->name = hdw->hdw_desc->description; + init_data->polling_interval = 100; /* ms From ir-kbd-i2c */ + /* IR Receiver */ + info.addr = 0x18; + info.platform_data = init_data; + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", + info.type, info.addr); + i2c_new_device(&hdw->i2c_adap, &info); + break; + case PVR2_IR_SCHEME_ZILOG: /* HVR-1950 style */ + case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */ + init_data->ir_codes = RC_MAP_HAUPPAUGE; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = RC_TYPE_RC5; + init_data->name = hdw->hdw_desc->description; + /* IR Receiver */ + info.addr = 0x71; + info.platform_data = init_data; + strlcpy(info.type, "ir_rx_z8f0811_haup", I2C_NAME_SIZE); + pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", + info.type, info.addr); + i2c_new_device(&hdw->i2c_adap, &info); + /* IR Trasmitter */ + info.addr = 0x70; + info.platform_data = init_data; + strlcpy(info.type, "ir_tx_z8f0811_haup", I2C_NAME_SIZE); + pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", + info.type, info.addr); + i2c_new_device(&hdw->i2c_adap, &info); + break; + default: + /* The device either doesn't support I2C-based IR or we + don't know (yet) how to operate IR on the device. */ + break; + } +} + +void pvr2_i2c_core_init(struct pvr2_hdw *hdw) +{ + unsigned int idx; + + /* The default action for all possible I2C addresses is just to do + the transfer normally. */ + for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { + hdw->i2c_func[idx] = pvr2_i2c_basic_op; + } + + /* However, deal with various special cases for 24xxx hardware. */ + if (ir_mode[hdw->unit_number] == 0) { + printk(KERN_INFO "%s: IR disabled\n",hdw->name); + hdw->i2c_func[0x18] = i2c_black_hole; + } else if (ir_mode[hdw->unit_number] == 1) { + if (hdw->ir_scheme_active == PVR2_IR_SCHEME_24XXX) { + /* Set up translation so that our IR looks like a + 29xxx device */ + hdw->i2c_func[0x18] = i2c_24xxx_ir; + } + } + if (hdw->hdw_desc->flag_has_cx25840) { + hdw->i2c_func[0x44] = i2c_hack_cx25840; + } + if (hdw->hdw_desc->flag_has_wm8775) { + hdw->i2c_func[0x1b] = i2c_hack_wm8775; + } + + // Configure the adapter and set up everything else related to it. + memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); + memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); + strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); + hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; + hdw->i2c_adap.algo = &hdw->i2c_algo; + hdw->i2c_adap.algo_data = hdw; + hdw->i2c_linked = !0; + i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev); + i2c_add_adapter(&hdw->i2c_adap); + if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { + /* Probe for a different type of IR receiver on this + device. This is really the only way to differentiate + older 24xxx devices from 24xxx variants that include an + IR blaster. If the IR blaster is present, the IR + receiver is part of that chip and thus we must disable + the emulated IR receiver. */ + if (do_i2c_probe(hdw, 0x71)) { + pvr2_trace(PVR2_TRACE_INFO, + "Device has newer IR hardware;" + " disabling unneeded virtual IR device"); + hdw->i2c_func[0x18] = NULL; + /* Remember that this is a different device... */ + hdw->ir_scheme_active = PVR2_IR_SCHEME_24XXX_MCE; + } + } + if (i2c_scan) do_i2c_scan(hdw); + + pvr2_i2c_register_ir(hdw); +} + +void pvr2_i2c_core_done(struct pvr2_hdw *hdw) +{ + if (hdw->i2c_linked) { + i2c_del_adapter(&hdw->i2c_adap); + hdw->i2c_linked = 0; + } +} + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h new file mode 100644 index 000000000000..6a75769200bd --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h @@ -0,0 +1,40 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_I2C_CORE_H +#define __PVRUSB2_I2C_CORE_H + +struct pvr2_hdw; + +void pvr2_i2c_core_init(struct pvr2_hdw *); +void pvr2_i2c_core_done(struct pvr2_hdw *); + + +#endif /* __PVRUSB2_I2C_ADAPTER_H */ + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c new file mode 100644 index 000000000000..20b6ae0bb40d --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-io.c @@ -0,0 +1,695 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pvrusb2-io.h" +#include "pvrusb2-debug.h" +#include +#include +#include +#include + +static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state); + +#define BUFFER_SIG 0x47653271 + +// #define SANITY_CHECK_BUFFERS + + +#ifdef SANITY_CHECK_BUFFERS +#define BUFFER_CHECK(bp) do { \ + if ((bp)->signature != BUFFER_SIG) { \ + pvr2_trace(PVR2_TRACE_ERROR_LEGS, \ + "Buffer %p is bad at %s:%d", \ + (bp),__FILE__,__LINE__); \ + pvr2_buffer_describe(bp,"BadSig"); \ + BUG(); \ + } \ +} while (0) +#else +#define BUFFER_CHECK(bp) do {} while(0) +#endif + +struct pvr2_stream { + /* Buffers queued for reading */ + struct list_head queued_list; + unsigned int q_count; + unsigned int q_bcount; + /* Buffers with retrieved data */ + struct list_head ready_list; + unsigned int r_count; + unsigned int r_bcount; + /* Buffers available for use */ + struct list_head idle_list; + unsigned int i_count; + unsigned int i_bcount; + /* Pointers to all buffers */ + struct pvr2_buffer **buffers; + /* Array size of buffers */ + unsigned int buffer_slot_count; + /* Total buffers actually in circulation */ + unsigned int buffer_total_count; + /* Designed number of buffers to be in circulation */ + unsigned int buffer_target_count; + /* Executed when ready list become non-empty */ + pvr2_stream_callback callback_func; + void *callback_data; + /* Context for transfer endpoint */ + struct usb_device *dev; + int endpoint; + /* Overhead for mutex enforcement */ + spinlock_t list_lock; + struct mutex mutex; + /* Tracking state for tolerating errors */ + unsigned int fail_count; + unsigned int fail_tolerance; + + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; +}; + +struct pvr2_buffer { + int id; + int signature; + enum pvr2_buffer_state state; + void *ptr; /* Pointer to storage area */ + unsigned int max_count; /* Size of storage area */ + unsigned int used_count; /* Amount of valid data in storage area */ + int status; /* Transfer result status */ + struct pvr2_stream *stream; + struct list_head list_overhead; + struct urb *purb; +}; + +static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st) +{ + switch (st) { + case pvr2_buffer_state_none: return "none"; + case pvr2_buffer_state_idle: return "idle"; + case pvr2_buffer_state_queued: return "queued"; + case pvr2_buffer_state_ready: return "ready"; + } + return "unknown"; +} + +#ifdef SANITY_CHECK_BUFFERS +static void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg) +{ + pvr2_trace(PVR2_TRACE_INFO, + "buffer%s%s %p state=%s id=%d status=%d" + " stream=%p purb=%p sig=0x%x", + (msg ? " " : ""), + (msg ? msg : ""), + bp, + (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"), + (bp ? bp->id : 0), + (bp ? bp->status : 0), + (bp ? bp->stream : NULL), + (bp ? bp->purb : NULL), + (bp ? bp->signature : 0)); +} +#endif /* SANITY_CHECK_BUFFERS */ + +static void pvr2_buffer_remove(struct pvr2_buffer *bp) +{ + unsigned int *cnt; + unsigned int *bcnt; + unsigned int ccnt; + struct pvr2_stream *sp = bp->stream; + switch (bp->state) { + case pvr2_buffer_state_idle: + cnt = &sp->i_count; + bcnt = &sp->i_bcount; + ccnt = bp->max_count; + break; + case pvr2_buffer_state_queued: + cnt = &sp->q_count; + bcnt = &sp->q_bcount; + ccnt = bp->max_count; + break; + case pvr2_buffer_state_ready: + cnt = &sp->r_count; + bcnt = &sp->r_bcount; + ccnt = bp->used_count; + break; + default: + return; + } + list_del_init(&bp->list_overhead); + (*cnt)--; + (*bcnt) -= ccnt; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/" + " bufferPool %8s dec cap=%07d cnt=%02d", + pvr2_buffer_state_decode(bp->state),*bcnt,*cnt); + bp->state = pvr2_buffer_state_none; +} + +static void pvr2_buffer_set_none(struct pvr2_buffer *bp) +{ + unsigned long irq_flags; + struct pvr2_stream *sp; + BUFFER_CHECK(bp); + sp = bp->stream; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", + bp, + pvr2_buffer_state_decode(bp->state), + pvr2_buffer_state_decode(pvr2_buffer_state_none)); + spin_lock_irqsave(&sp->list_lock,irq_flags); + pvr2_buffer_remove(bp); + spin_unlock_irqrestore(&sp->list_lock,irq_flags); +} + +static int pvr2_buffer_set_ready(struct pvr2_buffer *bp) +{ + int fl; + unsigned long irq_flags; + struct pvr2_stream *sp; + BUFFER_CHECK(bp); + sp = bp->stream; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", + bp, + pvr2_buffer_state_decode(bp->state), + pvr2_buffer_state_decode(pvr2_buffer_state_ready)); + spin_lock_irqsave(&sp->list_lock,irq_flags); + fl = (sp->r_count == 0); + pvr2_buffer_remove(bp); + list_add_tail(&bp->list_overhead,&sp->ready_list); + bp->state = pvr2_buffer_state_ready; + (sp->r_count)++; + sp->r_bcount += bp->used_count; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/" + " bufferPool %8s inc cap=%07d cnt=%02d", + pvr2_buffer_state_decode(bp->state), + sp->r_bcount,sp->r_count); + spin_unlock_irqrestore(&sp->list_lock,irq_flags); + return fl; +} + +static void pvr2_buffer_set_idle(struct pvr2_buffer *bp) +{ + unsigned long irq_flags; + struct pvr2_stream *sp; + BUFFER_CHECK(bp); + sp = bp->stream; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", + bp, + pvr2_buffer_state_decode(bp->state), + pvr2_buffer_state_decode(pvr2_buffer_state_idle)); + spin_lock_irqsave(&sp->list_lock,irq_flags); + pvr2_buffer_remove(bp); + list_add_tail(&bp->list_overhead,&sp->idle_list); + bp->state = pvr2_buffer_state_idle; + (sp->i_count)++; + sp->i_bcount += bp->max_count; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/" + " bufferPool %8s inc cap=%07d cnt=%02d", + pvr2_buffer_state_decode(bp->state), + sp->i_bcount,sp->i_count); + spin_unlock_irqrestore(&sp->list_lock,irq_flags); +} + +static void pvr2_buffer_set_queued(struct pvr2_buffer *bp) +{ + unsigned long irq_flags; + struct pvr2_stream *sp; + BUFFER_CHECK(bp); + sp = bp->stream; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", + bp, + pvr2_buffer_state_decode(bp->state), + pvr2_buffer_state_decode(pvr2_buffer_state_queued)); + spin_lock_irqsave(&sp->list_lock,irq_flags); + pvr2_buffer_remove(bp); + list_add_tail(&bp->list_overhead,&sp->queued_list); + bp->state = pvr2_buffer_state_queued; + (sp->q_count)++; + sp->q_bcount += bp->max_count; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/" + " bufferPool %8s inc cap=%07d cnt=%02d", + pvr2_buffer_state_decode(bp->state), + sp->q_bcount,sp->q_count); + spin_unlock_irqrestore(&sp->list_lock,irq_flags); +} + +static void pvr2_buffer_wipe(struct pvr2_buffer *bp) +{ + if (bp->state == pvr2_buffer_state_queued) { + usb_kill_urb(bp->purb); + } +} + +static int pvr2_buffer_init(struct pvr2_buffer *bp, + struct pvr2_stream *sp, + unsigned int id) +{ + memset(bp,0,sizeof(*bp)); + bp->signature = BUFFER_SIG; + bp->id = id; + pvr2_trace(PVR2_TRACE_BUF_POOL, + "/*---TRACE_FLOW---*/ bufferInit %p stream=%p",bp,sp); + bp->stream = sp; + bp->state = pvr2_buffer_state_none; + INIT_LIST_HEAD(&bp->list_overhead); + bp->purb = usb_alloc_urb(0,GFP_KERNEL); + if (! bp->purb) return -ENOMEM; +#ifdef SANITY_CHECK_BUFFERS + pvr2_buffer_describe(bp,"create"); +#endif + return 0; +} + +static void pvr2_buffer_done(struct pvr2_buffer *bp) +{ +#ifdef SANITY_CHECK_BUFFERS + pvr2_buffer_describe(bp,"delete"); +#endif + pvr2_buffer_wipe(bp); + pvr2_buffer_set_none(bp); + bp->signature = 0; + bp->stream = NULL; + usb_free_urb(bp->purb); + pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/" + " bufferDone %p",bp); +} + +static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt) +{ + int ret; + unsigned int scnt; + + /* Allocate buffers pointer array in multiples of 32 entries */ + if (cnt == sp->buffer_total_count) return 0; + + pvr2_trace(PVR2_TRACE_BUF_POOL, + "/*---TRACE_FLOW---*/ poolResize " + " stream=%p cur=%d adj=%+d", + sp, + sp->buffer_total_count, + cnt-sp->buffer_total_count); + + scnt = cnt & ~0x1f; + if (cnt > scnt) scnt += 0x20; + + if (cnt > sp->buffer_total_count) { + if (scnt > sp->buffer_slot_count) { + struct pvr2_buffer **nb; + nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL); + if (!nb) return -ENOMEM; + if (sp->buffer_slot_count) { + memcpy(nb,sp->buffers, + sp->buffer_slot_count * sizeof(*nb)); + kfree(sp->buffers); + } + sp->buffers = nb; + sp->buffer_slot_count = scnt; + } + while (sp->buffer_total_count < cnt) { + struct pvr2_buffer *bp; + bp = kmalloc(sizeof(*bp),GFP_KERNEL); + if (!bp) return -ENOMEM; + ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count); + if (ret) { + kfree(bp); + return -ENOMEM; + } + sp->buffers[sp->buffer_total_count] = bp; + (sp->buffer_total_count)++; + pvr2_buffer_set_idle(bp); + } + } else { + while (sp->buffer_total_count > cnt) { + struct pvr2_buffer *bp; + bp = sp->buffers[sp->buffer_total_count - 1]; + /* Paranoia */ + sp->buffers[sp->buffer_total_count - 1] = NULL; + (sp->buffer_total_count)--; + pvr2_buffer_done(bp); + kfree(bp); + } + if (scnt < sp->buffer_slot_count) { + struct pvr2_buffer **nb = NULL; + if (scnt) { + nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL); + if (!nb) return -ENOMEM; + memcpy(nb,sp->buffers,scnt * sizeof(*nb)); + } + kfree(sp->buffers); + sp->buffers = nb; + sp->buffer_slot_count = scnt; + } + } + return 0; +} + +static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp) +{ + struct pvr2_buffer *bp; + unsigned int cnt; + + if (sp->buffer_total_count == sp->buffer_target_count) return 0; + + pvr2_trace(PVR2_TRACE_BUF_POOL, + "/*---TRACE_FLOW---*/" + " poolCheck stream=%p cur=%d tgt=%d", + sp,sp->buffer_total_count,sp->buffer_target_count); + + if (sp->buffer_total_count < sp->buffer_target_count) { + return pvr2_stream_buffer_count(sp,sp->buffer_target_count); + } + + cnt = 0; + while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) { + bp = sp->buffers[sp->buffer_total_count - (cnt + 1)]; + if (bp->state != pvr2_buffer_state_idle) break; + cnt++; + } + if (cnt) { + pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt); + } + + return 0; +} + +static void pvr2_stream_internal_flush(struct pvr2_stream *sp) +{ + struct list_head *lp; + struct pvr2_buffer *bp1; + while ((lp = sp->queued_list.next) != &sp->queued_list) { + bp1 = list_entry(lp,struct pvr2_buffer,list_overhead); + pvr2_buffer_wipe(bp1); + /* At this point, we should be guaranteed that no + completion callback may happen on this buffer. But it's + possible that it might have completed after we noticed + it but before we wiped it. So double check its status + here first. */ + if (bp1->state != pvr2_buffer_state_queued) continue; + pvr2_buffer_set_idle(bp1); + } + if (sp->buffer_total_count != sp->buffer_target_count) { + pvr2_stream_achieve_buffer_count(sp); + } +} + +static void pvr2_stream_init(struct pvr2_stream *sp) +{ + spin_lock_init(&sp->list_lock); + mutex_init(&sp->mutex); + INIT_LIST_HEAD(&sp->queued_list); + INIT_LIST_HEAD(&sp->ready_list); + INIT_LIST_HEAD(&sp->idle_list); +} + +static void pvr2_stream_done(struct pvr2_stream *sp) +{ + mutex_lock(&sp->mutex); do { + pvr2_stream_internal_flush(sp); + pvr2_stream_buffer_count(sp,0); + } while (0); mutex_unlock(&sp->mutex); +} + +static void buffer_complete(struct urb *urb) +{ + struct pvr2_buffer *bp = urb->context; + struct pvr2_stream *sp; + unsigned long irq_flags; + BUFFER_CHECK(bp); + sp = bp->stream; + bp->used_count = 0; + bp->status = 0; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d", + bp,urb->status,urb->actual_length); + spin_lock_irqsave(&sp->list_lock,irq_flags); + if ((!(urb->status)) || + (urb->status == -ENOENT) || + (urb->status == -ECONNRESET) || + (urb->status == -ESHUTDOWN)) { + (sp->buffers_processed)++; + sp->bytes_processed += urb->actual_length; + bp->used_count = urb->actual_length; + if (sp->fail_count) { + pvr2_trace(PVR2_TRACE_TOLERANCE, + "stream %p transfer ok" + " - fail count reset",sp); + sp->fail_count = 0; + } + } else if (sp->fail_count < sp->fail_tolerance) { + // We can tolerate this error, because we're below the + // threshold... + (sp->fail_count)++; + (sp->buffers_failed)++; + pvr2_trace(PVR2_TRACE_TOLERANCE, + "stream %p ignoring error %d" + " - fail count increased to %u", + sp,urb->status,sp->fail_count); + } else { + (sp->buffers_failed)++; + bp->status = urb->status; + } + spin_unlock_irqrestore(&sp->list_lock,irq_flags); + pvr2_buffer_set_ready(bp); + if (sp && sp->callback_func) { + sp->callback_func(sp->callback_data); + } +} + +struct pvr2_stream *pvr2_stream_create(void) +{ + struct pvr2_stream *sp; + sp = kzalloc(sizeof(*sp),GFP_KERNEL); + if (!sp) return sp; + pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp); + pvr2_stream_init(sp); + return sp; +} + +void pvr2_stream_destroy(struct pvr2_stream *sp) +{ + if (!sp) return; + pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp); + pvr2_stream_done(sp); + kfree(sp); +} + +void pvr2_stream_setup(struct pvr2_stream *sp, + struct usb_device *dev, + int endpoint, + unsigned int tolerance) +{ + mutex_lock(&sp->mutex); do { + pvr2_stream_internal_flush(sp); + sp->dev = dev; + sp->endpoint = endpoint; + sp->fail_tolerance = tolerance; + } while(0); mutex_unlock(&sp->mutex); +} + +void pvr2_stream_set_callback(struct pvr2_stream *sp, + pvr2_stream_callback func, + void *data) +{ + unsigned long irq_flags; + mutex_lock(&sp->mutex); do { + spin_lock_irqsave(&sp->list_lock,irq_flags); + sp->callback_data = data; + sp->callback_func = func; + spin_unlock_irqrestore(&sp->list_lock,irq_flags); + } while(0); mutex_unlock(&sp->mutex); +} + +void pvr2_stream_get_stats(struct pvr2_stream *sp, + struct pvr2_stream_stats *stats, + int zero_counts) +{ + unsigned long irq_flags; + spin_lock_irqsave(&sp->list_lock,irq_flags); + if (stats) { + stats->buffers_in_queue = sp->q_count; + stats->buffers_in_idle = sp->i_count; + stats->buffers_in_ready = sp->r_count; + stats->buffers_processed = sp->buffers_processed; + stats->buffers_failed = sp->buffers_failed; + stats->bytes_processed = sp->bytes_processed; + } + if (zero_counts) { + sp->buffers_processed = 0; + sp->buffers_failed = 0; + sp->bytes_processed = 0; + } + spin_unlock_irqrestore(&sp->list_lock,irq_flags); +} + +/* Query / set the nominal buffer count */ +int pvr2_stream_get_buffer_count(struct pvr2_stream *sp) +{ + return sp->buffer_target_count; +} + +int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt) +{ + int ret; + if (sp->buffer_target_count == cnt) return 0; + mutex_lock(&sp->mutex); do { + sp->buffer_target_count = cnt; + ret = pvr2_stream_achieve_buffer_count(sp); + } while(0); mutex_unlock(&sp->mutex); + return ret; +} + +struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp) +{ + struct list_head *lp = sp->idle_list.next; + if (lp == &sp->idle_list) return NULL; + return list_entry(lp,struct pvr2_buffer,list_overhead); +} + +struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp) +{ + struct list_head *lp = sp->ready_list.next; + if (lp == &sp->ready_list) return NULL; + return list_entry(lp,struct pvr2_buffer,list_overhead); +} + +struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id) +{ + if (id < 0) return NULL; + if (id >= sp->buffer_total_count) return NULL; + return sp->buffers[id]; +} + +int pvr2_stream_get_ready_count(struct pvr2_stream *sp) +{ + return sp->r_count; +} + +void pvr2_stream_kill(struct pvr2_stream *sp) +{ + struct pvr2_buffer *bp; + mutex_lock(&sp->mutex); do { + pvr2_stream_internal_flush(sp); + while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) { + pvr2_buffer_set_idle(bp); + } + if (sp->buffer_total_count != sp->buffer_target_count) { + pvr2_stream_achieve_buffer_count(sp); + } + } while(0); mutex_unlock(&sp->mutex); +} + +int pvr2_buffer_queue(struct pvr2_buffer *bp) +{ +#undef SEED_BUFFER +#ifdef SEED_BUFFER + unsigned int idx; + unsigned int val; +#endif + int ret = 0; + struct pvr2_stream *sp; + if (!bp) return -EINVAL; + sp = bp->stream; + mutex_lock(&sp->mutex); do { + pvr2_buffer_wipe(bp); + if (!sp->dev) { + ret = -EIO; + break; + } + pvr2_buffer_set_queued(bp); +#ifdef SEED_BUFFER + for (idx = 0; idx < (bp->max_count) / 4; idx++) { + val = bp->id << 24; + val |= idx; + ((unsigned int *)(bp->ptr))[idx] = val; + } +#endif + bp->status = -EINPROGRESS; + usb_fill_bulk_urb(bp->purb, // struct urb *urb + sp->dev, // struct usb_device *dev + // endpoint (below) + usb_rcvbulkpipe(sp->dev,sp->endpoint), + bp->ptr, // void *transfer_buffer + bp->max_count, // int buffer_length + buffer_complete, + bp); + usb_submit_urb(bp->purb,GFP_KERNEL); + } while(0); mutex_unlock(&sp->mutex); + return ret; +} + +int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt) +{ + int ret = 0; + unsigned long irq_flags; + struct pvr2_stream *sp; + if (!bp) return -EINVAL; + sp = bp->stream; + mutex_lock(&sp->mutex); do { + spin_lock_irqsave(&sp->list_lock,irq_flags); + if (bp->state != pvr2_buffer_state_idle) { + ret = -EPERM; + } else { + bp->ptr = ptr; + bp->stream->i_bcount -= bp->max_count; + bp->max_count = cnt; + bp->stream->i_bcount += bp->max_count; + pvr2_trace(PVR2_TRACE_BUF_FLOW, + "/*---TRACE_FLOW---*/ bufferPool " + " %8s cap cap=%07d cnt=%02d", + pvr2_buffer_state_decode( + pvr2_buffer_state_idle), + bp->stream->i_bcount,bp->stream->i_count); + } + spin_unlock_irqrestore(&sp->list_lock,irq_flags); + } while(0); mutex_unlock(&sp->mutex); + return ret; +} + +unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp) +{ + return bp->used_count; +} + +int pvr2_buffer_get_status(struct pvr2_buffer *bp) +{ + return bp->status; +} + +int pvr2_buffer_get_id(struct pvr2_buffer *bp) +{ + return bp->id; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.h b/drivers/media/usb/pvrusb2/pvrusb2-io.h new file mode 100644 index 000000000000..afb7e87c0394 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-io.h @@ -0,0 +1,102 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_IO_H +#define __PVRUSB2_IO_H + +#include +#include + +typedef void (*pvr2_stream_callback)(void *); + +enum pvr2_buffer_state { + pvr2_buffer_state_none = 0, // Not on any list + pvr2_buffer_state_idle = 1, // Buffer is ready to be used again + pvr2_buffer_state_queued = 2, // Buffer has been queued for filling + pvr2_buffer_state_ready = 3, // Buffer has data available +}; + +struct pvr2_stream; +struct pvr2_buffer; + +struct pvr2_stream_stats { + unsigned int buffers_in_queue; + unsigned int buffers_in_idle; + unsigned int buffers_in_ready; + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; +}; + +/* Initialize / tear down stream structure */ +struct pvr2_stream *pvr2_stream_create(void); +void pvr2_stream_destroy(struct pvr2_stream *); +void pvr2_stream_setup(struct pvr2_stream *, + struct usb_device *dev,int endpoint, + unsigned int tolerance); +void pvr2_stream_set_callback(struct pvr2_stream *, + pvr2_stream_callback func, + void *data); +void pvr2_stream_get_stats(struct pvr2_stream *, + struct pvr2_stream_stats *, + int zero_counts); + +/* Query / set the nominal buffer count */ +int pvr2_stream_get_buffer_count(struct pvr2_stream *); +int pvr2_stream_set_buffer_count(struct pvr2_stream *,unsigned int); + +/* Get a pointer to a buffer that is either idle, ready, or is specified + named. */ +struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *); +struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *); +struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id); + +/* Find out how many buffers are idle or ready */ +int pvr2_stream_get_ready_count(struct pvr2_stream *); + + +/* Kill all pending buffers and throw away any ready buffers as well */ +void pvr2_stream_kill(struct pvr2_stream *); + +/* Set up the actual storage for a buffer */ +int pvr2_buffer_set_buffer(struct pvr2_buffer *,void *ptr,unsigned int cnt); + +/* Find out size of data in the given ready buffer */ +unsigned int pvr2_buffer_get_count(struct pvr2_buffer *); + +/* Retrieve completion code for given ready buffer */ +int pvr2_buffer_get_status(struct pvr2_buffer *); + +/* Retrieve ID of given buffer */ +int pvr2_buffer_get_id(struct pvr2_buffer *); + +/* Start reading into given buffer (kill it if needed) */ +int pvr2_buffer_queue(struct pvr2_buffer *); + +#endif /* __PVRUSB2_IO_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c new file mode 100644 index 000000000000..bba6115c9ae8 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c @@ -0,0 +1,512 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pvrusb2-ioread.h" +#include "pvrusb2-debug.h" +#include +#include +#include +#include +#include +#include + +#define BUFFER_COUNT 32 +#define BUFFER_SIZE PAGE_ALIGN(0x4000) + +struct pvr2_ioread { + struct pvr2_stream *stream; + char *buffer_storage[BUFFER_COUNT]; + char *sync_key_ptr; + unsigned int sync_key_len; + unsigned int sync_buf_offs; + unsigned int sync_state; + unsigned int sync_trashed_count; + int enabled; // Streaming is on + int spigot_open; // OK to pass data to client + int stream_running; // Passing data to client now + + /* State relevant to current buffer being read */ + struct pvr2_buffer *c_buf; + char *c_data_ptr; + unsigned int c_data_len; + unsigned int c_data_offs; + struct mutex mutex; +}; + +static int pvr2_ioread_init(struct pvr2_ioread *cp) +{ + unsigned int idx; + + cp->stream = NULL; + mutex_init(&cp->mutex); + + for (idx = 0; idx < BUFFER_COUNT; idx++) { + cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL); + if (!(cp->buffer_storage[idx])) break; + } + + if (idx < BUFFER_COUNT) { + // An allocation appears to have failed + for (idx = 0; idx < BUFFER_COUNT; idx++) { + if (!(cp->buffer_storage[idx])) continue; + kfree(cp->buffer_storage[idx]); + } + return -ENOMEM; + } + return 0; +} + +static void pvr2_ioread_done(struct pvr2_ioread *cp) +{ + unsigned int idx; + + pvr2_ioread_setup(cp,NULL); + for (idx = 0; idx < BUFFER_COUNT; idx++) { + if (!(cp->buffer_storage[idx])) continue; + kfree(cp->buffer_storage[idx]); + } +} + +struct pvr2_ioread *pvr2_ioread_create(void) +{ + struct pvr2_ioread *cp; + cp = kzalloc(sizeof(*cp),GFP_KERNEL); + if (!cp) return NULL; + pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp); + if (pvr2_ioread_init(cp) < 0) { + kfree(cp); + return NULL; + } + return cp; +} + +void pvr2_ioread_destroy(struct pvr2_ioread *cp) +{ + if (!cp) return; + pvr2_ioread_done(cp); + pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp); + if (cp->sync_key_ptr) { + kfree(cp->sync_key_ptr); + cp->sync_key_ptr = NULL; + } + kfree(cp); +} + +void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp, + const char *sync_key_ptr, + unsigned int sync_key_len) +{ + if (!cp) return; + + if (!sync_key_ptr) sync_key_len = 0; + if ((sync_key_len == cp->sync_key_len) && + ((!sync_key_len) || + (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return; + + if (sync_key_len != cp->sync_key_len) { + if (cp->sync_key_ptr) { + kfree(cp->sync_key_ptr); + cp->sync_key_ptr = NULL; + } + cp->sync_key_len = 0; + if (sync_key_len) { + cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL); + if (cp->sync_key_ptr) { + cp->sync_key_len = sync_key_len; + } + } + } + if (!cp->sync_key_len) return; + memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len); +} + +static void pvr2_ioread_stop(struct pvr2_ioread *cp) +{ + if (!(cp->enabled)) return; + pvr2_trace(PVR2_TRACE_START_STOP, + "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp); + pvr2_stream_kill(cp->stream); + cp->c_buf = NULL; + cp->c_data_ptr = NULL; + cp->c_data_len = 0; + cp->c_data_offs = 0; + cp->enabled = 0; + cp->stream_running = 0; + cp->spigot_open = 0; + if (cp->sync_state) { + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/ sync_state <== 0"); + cp->sync_state = 0; + } +} + +static int pvr2_ioread_start(struct pvr2_ioread *cp) +{ + int stat; + struct pvr2_buffer *bp; + if (cp->enabled) return 0; + if (!(cp->stream)) return 0; + pvr2_trace(PVR2_TRACE_START_STOP, + "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp); + while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != NULL) { + stat = pvr2_buffer_queue(bp); + if (stat < 0) { + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/" + " pvr2_ioread_start id=%p" + " error=%d", + cp,stat); + pvr2_ioread_stop(cp); + return stat; + } + } + cp->enabled = !0; + cp->c_buf = NULL; + cp->c_data_ptr = NULL; + cp->c_data_len = 0; + cp->c_data_offs = 0; + cp->stream_running = 0; + if (cp->sync_key_len) { + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/ sync_state <== 1"); + cp->sync_state = 1; + cp->sync_trashed_count = 0; + cp->sync_buf_offs = 0; + } + cp->spigot_open = 0; + return 0; +} + +struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp) +{ + return cp->stream; +} + +int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) +{ + int ret; + unsigned int idx; + struct pvr2_buffer *bp; + + mutex_lock(&cp->mutex); do { + if (cp->stream) { + pvr2_trace(PVR2_TRACE_START_STOP, + "/*---TRACE_READ---*/" + " pvr2_ioread_setup (tear-down) id=%p",cp); + pvr2_ioread_stop(cp); + pvr2_stream_kill(cp->stream); + if (pvr2_stream_get_buffer_count(cp->stream)) { + pvr2_stream_set_buffer_count(cp->stream,0); + } + cp->stream = NULL; + } + if (sp) { + pvr2_trace(PVR2_TRACE_START_STOP, + "/*---TRACE_READ---*/" + " pvr2_ioread_setup (setup) id=%p",cp); + pvr2_stream_kill(sp); + ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); + if (ret < 0) { + mutex_unlock(&cp->mutex); + return ret; + } + for (idx = 0; idx < BUFFER_COUNT; idx++) { + bp = pvr2_stream_get_buffer(sp,idx); + pvr2_buffer_set_buffer(bp, + cp->buffer_storage[idx], + BUFFER_SIZE); + } + cp->stream = sp; + } + } while (0); mutex_unlock(&cp->mutex); + + return 0; +} + +int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl) +{ + int ret = 0; + if ((!fl) == (!(cp->enabled))) return ret; + + mutex_lock(&cp->mutex); do { + if (fl) { + ret = pvr2_ioread_start(cp); + } else { + pvr2_ioread_stop(cp); + } + } while (0); mutex_unlock(&cp->mutex); + return ret; +} + +static int pvr2_ioread_get_buffer(struct pvr2_ioread *cp) +{ + int stat; + + while (cp->c_data_len <= cp->c_data_offs) { + if (cp->c_buf) { + // Flush out current buffer first. + stat = pvr2_buffer_queue(cp->c_buf); + if (stat < 0) { + // Streaming error... + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/" + " pvr2_ioread_read id=%p" + " queue_error=%d", + cp,stat); + pvr2_ioread_stop(cp); + return 0; + } + cp->c_buf = NULL; + cp->c_data_ptr = NULL; + cp->c_data_len = 0; + cp->c_data_offs = 0; + } + // Now get a freshly filled buffer. + cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream); + if (!cp->c_buf) break; // Nothing ready; done. + cp->c_data_len = pvr2_buffer_get_count(cp->c_buf); + if (!cp->c_data_len) { + // Nothing transferred. Was there an error? + stat = pvr2_buffer_get_status(cp->c_buf); + if (stat < 0) { + // Streaming error... + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/" + " pvr2_ioread_read id=%p" + " buffer_error=%d", + cp,stat); + pvr2_ioread_stop(cp); + // Give up. + return 0; + } + // Start over... + continue; + } + cp->c_data_offs = 0; + cp->c_data_ptr = cp->buffer_storage[ + pvr2_buffer_get_id(cp->c_buf)]; + } + return !0; +} + +static void pvr2_ioread_filter(struct pvr2_ioread *cp) +{ + unsigned int idx; + if (!cp->enabled) return; + if (cp->sync_state != 1) return; + + // Search the stream for our synchronization key. This is made + // complicated by the fact that in order to be honest with + // ourselves here we must search across buffer boundaries... + mutex_lock(&cp->mutex); while (1) { + // Ensure we have a buffer + if (!pvr2_ioread_get_buffer(cp)) break; + if (!cp->c_data_len) break; + + // Now walk the buffer contents until we match the key or + // run out of buffer data. + for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) { + if (cp->sync_buf_offs >= cp->sync_key_len) break; + if (cp->c_data_ptr[idx] == + cp->sync_key_ptr[cp->sync_buf_offs]) { + // Found the next key byte + (cp->sync_buf_offs)++; + } else { + // Whoops, mismatched. Start key over... + cp->sync_buf_offs = 0; + } + } + + // Consume what we've walked through + cp->c_data_offs += idx; + cp->sync_trashed_count += idx; + + // If we've found the key, then update state and get out. + if (cp->sync_buf_offs >= cp->sync_key_len) { + cp->sync_trashed_count -= cp->sync_key_len; + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/" + " sync_state <== 2 (skipped %u bytes)", + cp->sync_trashed_count); + cp->sync_state = 2; + cp->sync_buf_offs = 0; + break; + } + + if (cp->c_data_offs < cp->c_data_len) { + // Sanity check - should NEVER get here + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "ERROR: pvr2_ioread filter sync problem" + " len=%u offs=%u", + cp->c_data_len,cp->c_data_offs); + // Get out so we don't get stuck in an infinite + // loop. + break; + } + + continue; // (for clarity) + } mutex_unlock(&cp->mutex); +} + +int pvr2_ioread_avail(struct pvr2_ioread *cp) +{ + int ret; + if (!(cp->enabled)) { + // Stream is not enabled; so this is an I/O error + return -EIO; + } + + if (cp->sync_state == 1) { + pvr2_ioread_filter(cp); + if (cp->sync_state == 1) return -EAGAIN; + } + + ret = 0; + if (cp->stream_running) { + if (!pvr2_stream_get_ready_count(cp->stream)) { + // No data available at all right now. + ret = -EAGAIN; + } + } else { + if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) { + // Haven't buffered up enough yet; try again later + ret = -EAGAIN; + } + } + + if ((!(cp->spigot_open)) != (!(ret == 0))) { + cp->spigot_open = (ret == 0); + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/ data is %s", + cp->spigot_open ? "available" : "pending"); + } + + return ret; +} + +int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) +{ + unsigned int copied_cnt; + unsigned int bcnt; + const char *src; + int stat; + int ret = 0; + unsigned int req_cnt = cnt; + + if (!cnt) { + pvr2_trace(PVR2_TRACE_TRAP, + "/*---TRACE_READ---*/ pvr2_ioread_read id=%p" + " ZERO Request? Returning zero.",cp); + return 0; + } + + stat = pvr2_ioread_avail(cp); + if (stat < 0) return stat; + + cp->stream_running = !0; + + mutex_lock(&cp->mutex); do { + + // Suck data out of the buffers and copy to the user + copied_cnt = 0; + if (!buf) cnt = 0; + while (1) { + if (!pvr2_ioread_get_buffer(cp)) { + ret = -EIO; + break; + } + + if (!cnt) break; + + if (cp->sync_state == 2) { + // We're repeating the sync key data into + // the stream. + src = cp->sync_key_ptr + cp->sync_buf_offs; + bcnt = cp->sync_key_len - cp->sync_buf_offs; + } else { + // Normal buffer copy + src = cp->c_data_ptr + cp->c_data_offs; + bcnt = cp->c_data_len - cp->c_data_offs; + } + + if (!bcnt) break; + + // Don't run past user's buffer + if (bcnt > cnt) bcnt = cnt; + + if (copy_to_user(buf,src,bcnt)) { + // User supplied a bad pointer? + // Give up - this *will* cause data + // to be lost. + ret = -EFAULT; + break; + } + cnt -= bcnt; + buf += bcnt; + copied_cnt += bcnt; + + if (cp->sync_state == 2) { + // Update offset inside sync key that we're + // repeating back out. + cp->sync_buf_offs += bcnt; + if (cp->sync_buf_offs >= cp->sync_key_len) { + // Consumed entire key; switch mode + // to normal. + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/" + " sync_state <== 0"); + cp->sync_state = 0; + } + } else { + // Update buffer offset. + cp->c_data_offs += bcnt; + } + } + + } while (0); mutex_unlock(&cp->mutex); + + if (!ret) { + if (copied_cnt) { + // If anything was copied, return that count + ret = copied_cnt; + } else { + // Nothing copied; suggest to caller that another + // attempt should be tried again later + ret = -EAGAIN; + } + } + + pvr2_trace(PVR2_TRACE_DATA_FLOW, + "/*---TRACE_READ---*/ pvr2_ioread_read" + " id=%p request=%d result=%d", + cp,req_cnt,ret); + return ret; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.h b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h new file mode 100644 index 000000000000..100e0780e1aa --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h @@ -0,0 +1,48 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_IOREAD_H +#define __PVRUSB2_IOREAD_H + +#include "pvrusb2-io.h" + +struct pvr2_ioread; + +struct pvr2_ioread *pvr2_ioread_create(void); +void pvr2_ioread_destroy(struct pvr2_ioread *); +int pvr2_ioread_setup(struct pvr2_ioread *,struct pvr2_stream *); +struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *); +void pvr2_ioread_set_sync_key(struct pvr2_ioread *, + const char *sync_key_ptr, + unsigned int sync_key_len); +int pvr2_ioread_set_enabled(struct pvr2_ioread *,int fl); +int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt); +int pvr2_ioread_avail(struct pvr2_ioread *); + +#endif /* __PVRUSB2_IOREAD_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-main.c b/drivers/media/usb/pvrusb2/pvrusb2-main.c new file mode 100644 index 000000000000..c1d9bb61cd77 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-main.c @@ -0,0 +1,182 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#include "pvrusb2-hdw.h" +#include "pvrusb2-devattr.h" +#include "pvrusb2-context.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-v4l2.h" +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS +#include "pvrusb2-sysfs.h" +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ + +#define DRIVER_AUTHOR "Mike Isely " +#define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner" +#define DRIVER_VERSION "V4L in-tree version" + +#define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \ + PVR2_TRACE_INFO| \ + PVR2_TRACE_STD| \ + PVR2_TRACE_TOLERANCE| \ + PVR2_TRACE_TRAP| \ + 0) + +int pvrusb2_debug = DEFAULT_DEBUG_MASK; + +module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug, "Debug trace mask"); + +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS +static struct pvr2_sysfs_class *class_ptr = NULL; +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ + +static void pvr_setup_attach(struct pvr2_context *pvr) +{ + /* Create association with v4l layer */ + pvr2_v4l2_create(pvr); +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* Create association with dvb layer */ + pvr2_dvb_create(pvr); +#endif +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS + pvr2_sysfs_create(pvr,class_ptr); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ +} + +static int pvr_probe(struct usb_interface *intf, + const struct usb_device_id *devid) +{ + struct pvr2_context *pvr; + + /* Create underlying hardware interface */ + pvr = pvr2_context_create(intf,devid,pvr_setup_attach); + if (!pvr) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to create hdw handler"); + return -ENOMEM; + } + + pvr2_trace(PVR2_TRACE_INIT,"pvr_probe(pvr=%p)",pvr); + + usb_set_intfdata(intf, pvr); + + return 0; +} + +/* + * pvr_disconnect() + * + */ +static void pvr_disconnect(struct usb_interface *intf) +{ + struct pvr2_context *pvr = usb_get_intfdata(intf); + + pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) BEGIN",pvr); + + usb_set_intfdata (intf, NULL); + pvr2_context_disconnect(pvr); + + pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) DONE",pvr); + +} + +static struct usb_driver pvr_driver = { + .name = "pvrusb2", + .id_table = pvr2_device_table, + .probe = pvr_probe, + .disconnect = pvr_disconnect +}; + +/* + * pvr_init() / pvr_exit() + * + * This code is run to initialize/exit the driver. + * + */ +static int __init pvr_init(void) +{ + int ret; + + pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); + + ret = pvr2_context_global_init(); + if (ret != 0) { + pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret); + return ret; + } + +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS + class_ptr = pvr2_sysfs_class_create(); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ + + ret = usb_register(&pvr_driver); + + if (ret == 0) + printk(KERN_INFO "pvrusb2: " DRIVER_VERSION ":" + DRIVER_DESC "\n"); + if (pvrusb2_debug) + printk(KERN_INFO "pvrusb2: Debug mask is %d (0x%x)\n", + pvrusb2_debug,pvrusb2_debug); + + pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete"); + + return ret; +} + +static void __exit pvr_exit(void) +{ + pvr2_trace(PVR2_TRACE_INIT,"pvr_exit"); + + usb_deregister(&pvr_driver); + + pvr2_context_global_done(); + +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS + pvr2_sysfs_class_destroy(class_ptr); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ + + pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete"); +} + +module_init(pvr_init); +module_exit(pvr_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.9.1"); + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c new file mode 100644 index 000000000000..453627b07833 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -0,0 +1,411 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pvrusb2-std.h" +#include "pvrusb2-debug.h" +#include +#include + +struct std_name { + const char *name; + v4l2_std_id id; +}; + + +#define CSTD_PAL \ + (V4L2_STD_PAL_B| \ + V4L2_STD_PAL_B1| \ + V4L2_STD_PAL_G| \ + V4L2_STD_PAL_H| \ + V4L2_STD_PAL_I| \ + V4L2_STD_PAL_D| \ + V4L2_STD_PAL_D1| \ + V4L2_STD_PAL_K| \ + V4L2_STD_PAL_M| \ + V4L2_STD_PAL_N| \ + V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_60) + +#define CSTD_NTSC \ + (V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP| \ + V4L2_STD_NTSC_M_KR| \ + V4L2_STD_NTSC_443) + +#define CSTD_ATSC \ + (V4L2_STD_ATSC_8_VSB| \ + V4L2_STD_ATSC_16_VSB) + +#define CSTD_SECAM \ + (V4L2_STD_SECAM_B| \ + V4L2_STD_SECAM_D| \ + V4L2_STD_SECAM_G| \ + V4L2_STD_SECAM_H| \ + V4L2_STD_SECAM_K| \ + V4L2_STD_SECAM_K1| \ + V4L2_STD_SECAM_L| \ + V4L2_STD_SECAM_LC) + +#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B) +#define TSTD_B1 (V4L2_STD_PAL_B1) +#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D) +#define TSTD_D1 (V4L2_STD_PAL_D1) +#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G) +#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H) +#define TSTD_I (V4L2_STD_PAL_I) +#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K) +#define TSTD_K1 (V4L2_STD_SECAM_K1) +#define TSTD_L (V4L2_STD_SECAM_L) +#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M) +#define TSTD_N (V4L2_STD_PAL_N) +#define TSTD_Nc (V4L2_STD_PAL_Nc) +#define TSTD_60 (V4L2_STD_PAL_60) + +#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_ATSC|CSTD_SECAM) + +/* Mapping of standard bits to color system */ +static const struct std_name std_groups[] = { + {"PAL",CSTD_PAL}, + {"NTSC",CSTD_NTSC}, + {"SECAM",CSTD_SECAM}, + {"ATSC",CSTD_ATSC}, +}; + +/* Mapping of standard bits to modulation system */ +static const struct std_name std_items[] = { + {"B",TSTD_B}, + {"B1",TSTD_B1}, + {"D",TSTD_D}, + {"D1",TSTD_D1}, + {"G",TSTD_G}, + {"H",TSTD_H}, + {"I",TSTD_I}, + {"K",TSTD_K}, + {"K1",TSTD_K1}, + {"L",TSTD_L}, + {"LC",V4L2_STD_SECAM_LC}, + {"M",TSTD_M}, + {"Mj",V4L2_STD_NTSC_M_JP}, + {"443",V4L2_STD_NTSC_443}, + {"Mk",V4L2_STD_NTSC_M_KR}, + {"N",TSTD_N}, + {"Nc",TSTD_Nc}, + {"60",TSTD_60}, + {"8VSB",V4L2_STD_ATSC_8_VSB}, + {"16VSB",V4L2_STD_ATSC_16_VSB}, +}; + + +// Search an array of std_name structures and return a pointer to the +// element with the matching name. +static const struct std_name *find_std_name(const struct std_name *arrPtr, + unsigned int arrSize, + const char *bufPtr, + unsigned int bufSize) +{ + unsigned int idx; + const struct std_name *p; + for (idx = 0; idx < arrSize; idx++) { + p = arrPtr + idx; + if (strlen(p->name) != bufSize) continue; + if (!memcmp(bufPtr,p->name,bufSize)) return p; + } + return NULL; +} + + +int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, + unsigned int bufSize) +{ + v4l2_std_id id = 0; + v4l2_std_id cmsk = 0; + v4l2_std_id t; + int mMode = 0; + unsigned int cnt; + char ch; + const struct std_name *sp; + + while (bufSize) { + if (!mMode) { + cnt = 0; + while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; + if (cnt >= bufSize) return 0; // No more characters + sp = find_std_name(std_groups, ARRAY_SIZE(std_groups), + bufPtr,cnt); + if (!sp) return 0; // Illegal color system name + cnt++; + bufPtr += cnt; + bufSize -= cnt; + mMode = !0; + cmsk = sp->id; + continue; + } + cnt = 0; + while (cnt < bufSize) { + ch = bufPtr[cnt]; + if (ch == ';') { + mMode = 0; + break; + } + if (ch == '/') break; + cnt++; + } + sp = find_std_name(std_items, ARRAY_SIZE(std_items), + bufPtr,cnt); + if (!sp) return 0; // Illegal modulation system ID + t = sp->id & cmsk; + if (!t) return 0; // Specific color + modulation system illegal + id |= t; + if (cnt < bufSize) cnt++; + bufPtr += cnt; + bufSize -= cnt; + } + + if (idPtr) *idPtr = id; + return !0; +} + + +unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, + v4l2_std_id id) +{ + unsigned int idx1,idx2; + const struct std_name *ip,*gp; + int gfl,cfl; + unsigned int c1,c2; + cfl = 0; + c1 = 0; + for (idx1 = 0; idx1 < ARRAY_SIZE(std_groups); idx1++) { + gp = std_groups + idx1; + gfl = 0; + for (idx2 = 0; idx2 < ARRAY_SIZE(std_items); idx2++) { + ip = std_items + idx2; + if (!(gp->id & ip->id & id)) continue; + if (!gfl) { + if (cfl) { + c2 = scnprintf(bufPtr,bufSize,";"); + c1 += c2; + bufSize -= c2; + bufPtr += c2; + } + cfl = !0; + c2 = scnprintf(bufPtr,bufSize, + "%s-",gp->name); + gfl = !0; + } else { + c2 = scnprintf(bufPtr,bufSize,"/"); + } + c1 += c2; + bufSize -= c2; + bufPtr += c2; + c2 = scnprintf(bufPtr,bufSize, + ip->name); + c1 += c2; + bufSize -= c2; + bufPtr += c2; + } + } + return c1; +} + + +// Template data for possible enumerated video standards. Here we group +// standards which share common frame rates and resolution. +static struct v4l2_standard generic_standards[] = { + { + .id = (TSTD_B|TSTD_B1| + TSTD_D|TSTD_D1| + TSTD_G| + TSTD_H| + TSTD_I| + TSTD_K|TSTD_K1| + TSTD_L| + V4L2_STD_SECAM_LC | + TSTD_N|TSTD_Nc), + .frameperiod = + { + .numerator = 1, + .denominator= 25 + }, + .framelines = 625, + .reserved = {0,0,0,0} + }, { + .id = (TSTD_M| + V4L2_STD_NTSC_M_JP| + V4L2_STD_NTSC_M_KR), + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + }, { // This is a total wild guess + .id = (TSTD_60), + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + }, { // This is total wild guess + .id = V4L2_STD_NTSC_443, + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + } +}; + +static struct v4l2_standard *match_std(v4l2_std_id id) +{ + unsigned int idx; + for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) { + if (generic_standards[idx].id & id) { + return generic_standards + idx; + } + } + return NULL; +} + +static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) +{ + struct v4l2_standard *template; + int idx; + unsigned int bcnt; + template = match_std(id); + if (!template) return 0; + idx = std->index; + memcpy(std,template,sizeof(*template)); + std->index = idx; + std->id = id; + bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); + std->name[bcnt] = 0; + pvr2_trace(PVR2_TRACE_STD,"Set up standard idx=%u name=%s", + std->index,std->name); + return !0; +} + +/* These are special cases of combined standards that we should enumerate + separately if the component pieces are present. */ +static v4l2_std_id std_mixes[] = { + V4L2_STD_PAL_B | V4L2_STD_PAL_G, + V4L2_STD_PAL_D | V4L2_STD_PAL_K, + V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, + V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, +}; + +struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, + v4l2_std_id id) +{ + unsigned int std_cnt = 0; + unsigned int idx,bcnt,idx2; + v4l2_std_id idmsk,cmsk,fmsk; + struct v4l2_standard *stddefs; + + if (pvrusb2_debug & PVR2_TRACE_STD) { + char buf[100]; + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); + pvr2_trace( + PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)", + (int)id,bcnt,buf); + } + + *countptr = 0; + std_cnt = 0; + fmsk = 0; + for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { + if (!(idmsk & cmsk)) continue; + cmsk &= ~idmsk; + if (match_std(idmsk)) { + std_cnt++; + continue; + } + fmsk |= idmsk; + } + + for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) { + if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; + } + + /* Don't complain about ATSC standard values */ + fmsk &= ~CSTD_ATSC; + + if (fmsk) { + char buf[100]; + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "WARNING:" + " Failed to classify the following standard(s): %.*s", + bcnt,buf); + } + + pvr2_trace(PVR2_TRACE_STD,"Setting up %u unique standard(s)", + std_cnt); + if (!std_cnt) return NULL; // paranoia + + stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt, + GFP_KERNEL); + if (!stddefs) + return NULL; + + for (idx = 0; idx < std_cnt; idx++) + stddefs[idx].index = idx; + + idx = 0; + + /* Enumerate potential special cases */ + for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt); + idx2++) { + if (!(id & std_mixes[idx2])) continue; + if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; + } + /* Now enumerate individual pieces */ + for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { + if (!(idmsk & cmsk)) continue; + cmsk &= ~idmsk; + if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; + idx++; + } + + *countptr = std_cnt; + return stddefs; +} + +v4l2_std_id pvr2_std_get_usable(void) +{ + return CSTD_ALL; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.h b/drivers/media/usb/pvrusb2/pvrusb2-std.h new file mode 100644 index 000000000000..a35c53d0b320 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.h @@ -0,0 +1,59 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_STD_H +#define __PVRUSB2_STD_H + +#include + +// Convert string describing one or more video standards into a mask of V4L +// standard bits. Return true if conversion succeeds otherwise return +// false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and +// C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are +// modulation schemes (e.g. "M", "B", "G", etc). +int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, + unsigned int bufSize); + +// Convert any arbitrary set of video standard bits into an unambiguous +// readable string. Return value is the number of bytes consumed in the +// buffer. The formatted string is of a form that can be parsed by our +// sibling std_std_to_id() function. +unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, + v4l2_std_id id); + +// Create an array of suitable v4l2_standard structures given a bit mask of +// video standards to support. The array is allocated from the heap, and +// the number of elements is returned in the first argument. +struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, + v4l2_std_id id); + +// Return mask of which video standard bits are valid +v4l2_std_id pvr2_std_get_usable(void); + +#endif /* __PVRUSB2_STD_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c new file mode 100644 index 000000000000..6ef1335b2858 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c @@ -0,0 +1,861 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "pvrusb2-sysfs.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-debug.h" +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC +#include "pvrusb2-debugifc.h" +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ + +#define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) + +struct pvr2_sysfs { + struct pvr2_channel channel; + struct device *class_dev; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC + struct pvr2_sysfs_debugifc *debugifc; +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ + struct pvr2_sysfs_ctl_item *item_first; + struct pvr2_sysfs_ctl_item *item_last; + struct device_attribute attr_v4l_minor_number; + struct device_attribute attr_v4l_radio_minor_number; + struct device_attribute attr_unit_number; + struct device_attribute attr_bus_info; + struct device_attribute attr_hdw_name; + struct device_attribute attr_hdw_desc; + int v4l_minor_number_created_ok; + int v4l_radio_minor_number_created_ok; + int unit_number_created_ok; + int bus_info_created_ok; + int hdw_name_created_ok; + int hdw_desc_created_ok; +}; + +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC +struct pvr2_sysfs_debugifc { + struct device_attribute attr_debugcmd; + struct device_attribute attr_debuginfo; + int debugcmd_created_ok; + int debuginfo_created_ok; +}; +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ + +struct pvr2_sysfs_ctl_item { + struct device_attribute attr_name; + struct device_attribute attr_type; + struct device_attribute attr_min; + struct device_attribute attr_max; + struct device_attribute attr_def; + struct device_attribute attr_enum; + struct device_attribute attr_bits; + struct device_attribute attr_val; + struct device_attribute attr_custom; + struct pvr2_ctrl *cptr; + int ctl_id; + struct pvr2_sysfs *chptr; + struct pvr2_sysfs_ctl_item *item_next; + struct attribute *attr_gen[8]; + struct attribute_group grp; + int created_ok; + char name[80]; +}; + +struct pvr2_sysfs_class { + struct class class; +}; + +static ssize_t show_name(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + const char *name; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name); + name = pvr2_ctrl_get_desc(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s", + cip->chptr, cip->ctl_id, name); + if (!name) return -EINVAL; + return scnprintf(buf, PAGE_SIZE, "%s\n", name); +} + +static ssize_t show_type(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + const char *name; + enum pvr2_ctl_type tp; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type); + tp = pvr2_ctrl_get_type(cip->cptr); + switch (tp) { + case pvr2_ctl_int: name = "integer"; break; + case pvr2_ctl_enum: name = "enum"; break; + case pvr2_ctl_bitmask: name = "bitmask"; break; + case pvr2_ctl_bool: name = "boolean"; break; + default: name = "?"; break; + } + pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s", + cip->chptr, cip->ctl_id, name); + if (!name) return -EINVAL; + return scnprintf(buf, PAGE_SIZE, "%s\n", name); +} + +static ssize_t show_min(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + long val; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min); + val = pvr2_ctrl_get_min(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld", + cip->chptr, cip->ctl_id, val); + return scnprintf(buf, PAGE_SIZE, "%ld\n", val); +} + +static ssize_t show_max(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + long val; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max); + val = pvr2_ctrl_get_max(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld", + cip->chptr, cip->ctl_id, val); + return scnprintf(buf, PAGE_SIZE, "%ld\n", val); +} + +static ssize_t show_def(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; + unsigned int cnt = 0; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def); + ret = pvr2_ctrl_get_def(cip->cptr, &val); + if (ret < 0) return ret; + ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %.*s (%d)", + cip->chptr, cip->ctl_id, cnt, buf, val); + buf[cnt] = '\n'; + return cnt + 1; +} + +static ssize_t show_val_norm(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; + unsigned int cnt = 0; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); + ret = pvr2_ctrl_get_value(cip->cptr, &val); + if (ret < 0) return ret; + ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", + cip->chptr, cip->ctl_id, cnt, buf, val); + buf[cnt] = '\n'; + return cnt+1; +} + +static ssize_t show_val_custom(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; + unsigned int cnt = 0; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); + ret = pvr2_ctrl_get_value(cip->cptr, &val); + if (ret < 0) return ret; + ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", + cip->chptr, cip->ctl_id, cnt, buf, val); + buf[cnt] = '\n'; + return cnt+1; +} + +static ssize_t show_enum(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + long val; + unsigned int bcnt, ccnt, ecnt; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum); + ecnt = pvr2_ctrl_get_cnt(cip->cptr); + bcnt = 0; + for (val = 0; val < ecnt; val++) { + pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt, + PAGE_SIZE - bcnt, &ccnt); + if (!ccnt) continue; + bcnt += ccnt; + if (bcnt >= PAGE_SIZE) break; + buf[bcnt] = '\n'; + bcnt++; + } + pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)", + cip->chptr, cip->ctl_id); + return bcnt; +} + +static ssize_t show_bits(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + int valid_bits, msk; + unsigned int bcnt, ccnt; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits); + valid_bits = pvr2_ctrl_get_mask(cip->cptr); + bcnt = 0; + for (msk = 1; valid_bits; msk <<= 1) { + if (!(msk & valid_bits)) continue; + valid_bits &= ~msk; + pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt, + PAGE_SIZE - bcnt, &ccnt); + bcnt += ccnt; + if (bcnt >= PAGE_SIZE) break; + buf[bcnt] = '\n'; + bcnt++; + } + pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)", + cip->chptr, cip->ctl_id); + return bcnt; +} + +static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl, + const char *buf,unsigned int count) +{ + int ret; + int mask,val; + if (customfl) { + ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count, + &mask, &val); + } else { + ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count, + &mask, &val); + } + if (ret < 0) return ret; + ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val); + pvr2_hdw_commit_ctl(cip->chptr->channel.hdw); + return ret; +} + +static ssize_t store_val_norm(struct device *class_dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pvr2_sysfs_ctl_item *cip; + int ret; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); + pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"", + cip->chptr, cip->ctl_id, (int)count, buf); + ret = store_val_any(cip, 0, buf, count); + if (!ret) ret = count; + return ret; +} + +static ssize_t store_val_custom(struct device *class_dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pvr2_sysfs_ctl_item *cip; + int ret; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); + pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"", + cip->chptr, cip->ctl_id, (int)count, buf); + ret = store_val_any(cip, 1, buf, count); + if (!ret) ret = count; + return ret; +} + +static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) +{ + struct pvr2_sysfs_ctl_item *cip; + struct pvr2_ctrl *cptr; + unsigned int cnt,acnt; + int ret; + + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); + if (!cptr) return; + + cip = kzalloc(sizeof(*cip),GFP_KERNEL); + if (!cip) return; + pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); + + cip->cptr = cptr; + cip->ctl_id = ctl_id; + + cip->chptr = sfp; + cip->item_next = NULL; + if (sfp->item_last) { + sfp->item_last->item_next = cip; + } else { + sfp->item_first = cip; + } + sfp->item_last = cip; + + sysfs_attr_init(&cip->attr_name.attr); + cip->attr_name.attr.name = "name"; + cip->attr_name.attr.mode = S_IRUGO; + cip->attr_name.show = show_name; + + sysfs_attr_init(&cip->attr_type.attr); + cip->attr_type.attr.name = "type"; + cip->attr_type.attr.mode = S_IRUGO; + cip->attr_type.show = show_type; + + sysfs_attr_init(&cip->attr_min.attr); + cip->attr_min.attr.name = "min_val"; + cip->attr_min.attr.mode = S_IRUGO; + cip->attr_min.show = show_min; + + sysfs_attr_init(&cip->attr_max.attr); + cip->attr_max.attr.name = "max_val"; + cip->attr_max.attr.mode = S_IRUGO; + cip->attr_max.show = show_max; + + sysfs_attr_init(&cip->attr_def.attr); + cip->attr_def.attr.name = "def_val"; + cip->attr_def.attr.mode = S_IRUGO; + cip->attr_def.show = show_def; + + sysfs_attr_init(&cip->attr_val.attr); + cip->attr_val.attr.name = "cur_val"; + cip->attr_val.attr.mode = S_IRUGO; + + sysfs_attr_init(&cip->attr_custom.attr); + cip->attr_custom.attr.name = "custom_val"; + cip->attr_custom.attr.mode = S_IRUGO; + + sysfs_attr_init(&cip->attr_enum.attr); + cip->attr_enum.attr.name = "enum_val"; + cip->attr_enum.attr.mode = S_IRUGO; + cip->attr_enum.show = show_enum; + + sysfs_attr_init(&cip->attr_bits.attr); + cip->attr_bits.attr.name = "bit_val"; + cip->attr_bits.attr.mode = S_IRUGO; + cip->attr_bits.show = show_bits; + + if (pvr2_ctrl_is_writable(cptr)) { + cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; + cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; + } + + acnt = 0; + cip->attr_gen[acnt++] = &cip->attr_name.attr; + cip->attr_gen[acnt++] = &cip->attr_type.attr; + cip->attr_gen[acnt++] = &cip->attr_val.attr; + cip->attr_gen[acnt++] = &cip->attr_def.attr; + cip->attr_val.show = show_val_norm; + cip->attr_val.store = store_val_norm; + if (pvr2_ctrl_has_custom_symbols(cptr)) { + cip->attr_gen[acnt++] = &cip->attr_custom.attr; + cip->attr_custom.show = show_val_custom; + cip->attr_custom.store = store_val_custom; + } + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: + // Control is an enumeration + cip->attr_gen[acnt++] = &cip->attr_enum.attr; + break; + case pvr2_ctl_int: + // Control is an integer + cip->attr_gen[acnt++] = &cip->attr_min.attr; + cip->attr_gen[acnt++] = &cip->attr_max.attr; + break; + case pvr2_ctl_bitmask: + // Control is an bitmask + cip->attr_gen[acnt++] = &cip->attr_bits.attr; + break; + default: break; + } + + cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", + pvr2_ctrl_get_name(cptr)); + cip->name[cnt] = 0; + cip->grp.name = cip->name; + cip->grp.attrs = cip->attr_gen; + + ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "sysfs_create_group error: %d", + ret); + return; + } + cip->created_ok = !0; +} + +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC +static ssize_t debuginfo_show(struct device *, struct device_attribute *, + char *); +static ssize_t debugcmd_show(struct device *, struct device_attribute *, + char *); +static ssize_t debugcmd_store(struct device *, struct device_attribute *, + const char *, size_t count); + +static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) +{ + struct pvr2_sysfs_debugifc *dip; + int ret; + + dip = kzalloc(sizeof(*dip),GFP_KERNEL); + if (!dip) return; + sysfs_attr_init(&dip->attr_debugcmd.attr); + dip->attr_debugcmd.attr.name = "debugcmd"; + dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; + dip->attr_debugcmd.show = debugcmd_show; + dip->attr_debugcmd.store = debugcmd_store; + sysfs_attr_init(&dip->attr_debuginfo.attr); + dip->attr_debuginfo.attr.name = "debuginfo"; + dip->attr_debuginfo.attr.mode = S_IRUGO; + dip->attr_debuginfo.show = debuginfo_show; + sfp->debugifc = dip; + ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + dip->debugcmd_created_ok = !0; + } + ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + dip->debuginfo_created_ok = !0; + } +} + + +static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) +{ + if (!sfp->debugifc) return; + if (sfp->debugifc->debuginfo_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->debugifc->attr_debuginfo); + } + if (sfp->debugifc->debugcmd_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->debugifc->attr_debugcmd); + } + kfree(sfp->debugifc); + sfp->debugifc = NULL; +} +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ + + +static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) +{ + unsigned int idx,cnt; + cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); + for (idx = 0; idx < cnt; idx++) { + pvr2_sysfs_add_control(sfp,idx); + } +} + + +static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp) +{ + struct pvr2_sysfs_ctl_item *cip1,*cip2; + for (cip1 = sfp->item_first; cip1; cip1 = cip2) { + cip2 = cip1->item_next; + if (cip1->created_ok) { + sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp); + } + pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1); + kfree(cip1); + } +} + + +static void pvr2_sysfs_class_release(struct class *class) +{ + struct pvr2_sysfs_class *clp; + clp = container_of(class,struct pvr2_sysfs_class,class); + pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp); + kfree(clp); +} + + +static void pvr2_sysfs_release(struct device *class_dev) +{ + pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev); + kfree(class_dev); +} + + +static void class_dev_destroy(struct pvr2_sysfs *sfp) +{ + struct device *dev; + if (!sfp->class_dev) return; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC + pvr2_sysfs_tear_down_debugifc(sfp); +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ + pvr2_sysfs_tear_down_controls(sfp); + if (sfp->hdw_desc_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_hdw_desc); + } + if (sfp->hdw_name_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_hdw_name); + } + if (sfp->bus_info_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_bus_info); + } + if (sfp->v4l_minor_number_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_v4l_minor_number); + } + if (sfp->v4l_radio_minor_number_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_v4l_radio_minor_number); + } + if (sfp->unit_number_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_unit_number); + } + pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev); + dev_set_drvdata(sfp->class_dev, NULL); + dev = sfp->class_dev->parent; + sfp->class_dev->parent = NULL; + put_device(dev); + device_unregister(sfp->class_dev); + sfp->class_dev = NULL; +} + + +static ssize_t v4l_minor_number_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%d\n", + pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, + pvr2_v4l_type_video)); +} + + +static ssize_t bus_info_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_bus_info(sfp->channel.hdw)); +} + + +static ssize_t hdw_name_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_type(sfp->channel.hdw)); +} + + +static ssize_t hdw_desc_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_desc(sfp->channel.hdw)); +} + + +static ssize_t v4l_radio_minor_number_show(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%d\n", + pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, + pvr2_v4l_type_radio)); +} + + +static ssize_t unit_number_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%d\n", + pvr2_hdw_get_unit_number(sfp->channel.hdw)); +} + + +static void class_dev_create(struct pvr2_sysfs *sfp, + struct pvr2_sysfs_class *class_ptr) +{ + struct usb_device *usb_dev; + struct device *class_dev; + int ret; + + usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); + if (!usb_dev) return; + class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL); + if (!class_dev) return; + + pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); + + class_dev->class = &class_ptr->class; + + dev_set_name(class_dev, "%s", + pvr2_hdw_get_device_identifier(sfp->channel.hdw)); + + class_dev->parent = get_device(&usb_dev->dev); + + sfp->class_dev = class_dev; + dev_set_drvdata(class_dev, sfp); + ret = device_register(class_dev); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_register failed"); + put_device(class_dev); + return; + } + + sysfs_attr_init(&sfp->attr_v4l_minor_number.attr); + sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number"; + sfp->attr_v4l_minor_number.attr.mode = S_IRUGO; + sfp->attr_v4l_minor_number.show = v4l_minor_number_show; + sfp->attr_v4l_minor_number.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_v4l_minor_number); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + sfp->v4l_minor_number_created_ok = !0; + } + + sysfs_attr_init(&sfp->attr_v4l_radio_minor_number.attr); + sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number"; + sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO; + sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show; + sfp->attr_v4l_radio_minor_number.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_v4l_radio_minor_number); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + sfp->v4l_radio_minor_number_created_ok = !0; + } + + sysfs_attr_init(&sfp->attr_unit_number.attr); + sfp->attr_unit_number.attr.name = "unit_number"; + sfp->attr_unit_number.attr.mode = S_IRUGO; + sfp->attr_unit_number.show = unit_number_show; + sfp->attr_unit_number.store = NULL; + ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + sfp->unit_number_created_ok = !0; + } + + sysfs_attr_init(&sfp->attr_bus_info.attr); + sfp->attr_bus_info.attr.name = "bus_info_str"; + sfp->attr_bus_info.attr.mode = S_IRUGO; + sfp->attr_bus_info.show = bus_info_show; + sfp->attr_bus_info.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_bus_info); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + sfp->bus_info_created_ok = !0; + } + + sysfs_attr_init(&sfp->attr_hdw_name.attr); + sfp->attr_hdw_name.attr.name = "device_hardware_type"; + sfp->attr_hdw_name.attr.mode = S_IRUGO; + sfp->attr_hdw_name.show = hdw_name_show; + sfp->attr_hdw_name.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_hdw_name); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + sfp->hdw_name_created_ok = !0; + } + + sysfs_attr_init(&sfp->attr_hdw_desc.attr); + sfp->attr_hdw_desc.attr.name = "device_hardware_description"; + sfp->attr_hdw_desc.attr.mode = S_IRUGO; + sfp->attr_hdw_desc.show = hdw_desc_show; + sfp->attr_hdw_desc.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_hdw_desc); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); + } else { + sfp->hdw_desc_created_ok = !0; + } + + pvr2_sysfs_add_controls(sfp); +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC + pvr2_sysfs_add_debugifc(sfp); +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ +} + + +static void pvr2_sysfs_internal_check(struct pvr2_channel *chp) +{ + struct pvr2_sysfs *sfp; + sfp = container_of(chp,struct pvr2_sysfs,channel); + if (!sfp->channel.mc_head->disconnect_flag) return; + pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp); + class_dev_destroy(sfp); + pvr2_channel_done(&sfp->channel); + kfree(sfp); +} + + +struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp, + struct pvr2_sysfs_class *class_ptr) +{ + struct pvr2_sysfs *sfp; + sfp = kzalloc(sizeof(*sfp),GFP_KERNEL); + if (!sfp) return sfp; + pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp); + pvr2_channel_init(&sfp->channel,mp); + sfp->channel.check_func = pvr2_sysfs_internal_check; + + class_dev_create(sfp,class_ptr); + return sfp; +} + + + +struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) +{ + struct pvr2_sysfs_class *clp; + clp = kzalloc(sizeof(*clp),GFP_KERNEL); + if (!clp) return clp; + pvr2_sysfs_trace("Creating and registering pvr2_sysfs_class id=%p", + clp); + clp->class.name = "pvrusb2"; + clp->class.class_release = pvr2_sysfs_class_release; + clp->class.dev_release = pvr2_sysfs_release; + if (class_register(&clp->class)) { + pvr2_sysfs_trace( + "Registration failed for pvr2_sysfs_class id=%p",clp); + kfree(clp); + clp = NULL; + } + return clp; +} + + +void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) +{ + pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp); + class_unregister(&clp->class); +} + + +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC +static ssize_t debuginfo_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + pvr2_hdw_trigger_module_log(sfp->channel.hdw); + return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE); +} + + +static ssize_t debugcmd_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE); +} + + +static ssize_t debugcmd_store(struct device *class_dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pvr2_sysfs *sfp; + int ret; + + sfp = dev_get_drvdata(class_dev); + if (!sfp) return -EINVAL; + + ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count); + if (ret < 0) return ret; + return count; +} +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h new file mode 100644 index 000000000000..6d875bfe7991 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h @@ -0,0 +1,46 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_SYSFS_H +#define __PVRUSB2_SYSFS_H + +#include +#include +#include "pvrusb2-context.h" + +struct pvr2_sysfs; +struct pvr2_sysfs_class; + +struct pvr2_sysfs_class *pvr2_sysfs_class_create(void); +void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *); + +struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *, + struct pvr2_sysfs_class *); + +#endif /* __PVRUSB2_SYSFS_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-util.h b/drivers/media/usb/pvrusb2/pvrusb2-util.h new file mode 100644 index 000000000000..92b75544ee2e --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-util.h @@ -0,0 +1,62 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_UTIL_H +#define __PVRUSB2_UTIL_H + +#define PVR2_DECOMPOSE_LE(t,i,d) \ + do { \ + (t)[i] = (d) & 0xff;\ + (t)[i+1] = ((d) >> 8) & 0xff;\ + (t)[i+2] = ((d) >> 16) & 0xff;\ + (t)[i+3] = ((d) >> 24) & 0xff;\ + } while(0) + +#define PVR2_DECOMPOSE_BE(t,i,d) \ + do { \ + (t)[i+3] = (d) & 0xff;\ + (t)[i+2] = ((d) >> 8) & 0xff;\ + (t)[i+1] = ((d) >> 16) & 0xff;\ + (t)[i] = ((d) >> 24) & 0xff;\ + } while(0) + +#define PVR2_COMPOSE_LE(t,i) \ + ((((u32)((t)[i+3])) << 24) | \ + (((u32)((t)[i+2])) << 16) | \ + (((u32)((t)[i+1])) << 8) | \ + ((u32)((t)[i]))) + +#define PVR2_COMPOSE_BE(t,i) \ + ((((u32)((t)[i])) << 24) | \ + (((u32)((t)[i+1])) << 16) | \ + (((u32)((t)[i+2])) << 8) | \ + ((u32)((t)[i+3]))) + + +#endif /* __PVRUSB2_UTIL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c new file mode 100644 index 000000000000..f344aed32a93 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -0,0 +1,1413 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "pvrusb2-context.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-v4l2.h" +#include "pvrusb2-ioread.h" +#include +#include +#include +#include +#include + +struct pvr2_v4l2_dev; +struct pvr2_v4l2_fh; +struct pvr2_v4l2; + +struct pvr2_v4l2_dev { + struct video_device devbase; /* MUST be first! */ + struct pvr2_v4l2 *v4lp; + struct pvr2_context_stream *stream; + /* Information about this device: */ + enum pvr2_config config; /* Expected stream format */ + int v4l_type; /* V4L defined type for this device node */ + enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */ +}; + +struct pvr2_v4l2_fh { + struct pvr2_channel channel; + struct pvr2_v4l2_dev *pdi; + enum v4l2_priority prio; + struct pvr2_ioread *rhp; + struct file *file; + struct pvr2_v4l2 *vhead; + struct pvr2_v4l2_fh *vnext; + struct pvr2_v4l2_fh *vprev; + wait_queue_head_t wait_data; + int fw_mode_flag; + /* Map contiguous ordinal value to input id */ + unsigned char *input_map; + unsigned int input_cnt; +}; + +struct pvr2_v4l2 { + struct pvr2_channel channel; + struct pvr2_v4l2_fh *vfirst; + struct pvr2_v4l2_fh *vlast; + + struct v4l2_prio_state prio; + + /* streams - Note that these must be separately, individually, + * allocated pointers. This is because the v4l core is going to + * manage their deletion - separately, individually... */ + struct pvr2_v4l2_dev *dev_video; + struct pvr2_v4l2_dev *dev_radio; +}; + +static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor"); +static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor"); +static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; +module_param_array(vbi_nr, int, NULL, 0444); +MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor"); + +static struct v4l2_capability pvr_capability ={ + .driver = "pvrusb2", + .card = "Hauppauge WinTV pvr-usb2", + .bus_info = "usb", + .version = LINUX_VERSION_CODE, + .capabilities = (V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | + V4L2_CAP_READWRITE), +}; + +static struct v4l2_fmtdesc pvr_fmtdesc [] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = V4L2_FMT_FLAG_COMPRESSED, + .description = "MPEG1/2", + // This should really be V4L2_PIX_FMT_MPEG, but xawtv + // breaks when I do that. + .pixelformat = 0, // V4L2_PIX_FMT_MPEG, + } +}; + +#define PVR_FORMAT_PIX 0 +#define PVR_FORMAT_VBI 1 + +static struct v4l2_format pvr_format [] = { + [PVR_FORMAT_PIX] = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt = { + .pix = { + .width = 720, + .height = 576, + // This should really be V4L2_PIX_FMT_MPEG, + // but xawtv breaks when I do that. + .pixelformat = 0, // V4L2_PIX_FMT_MPEG, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = 0, // doesn't make sense + // here + //FIXME : Don't know what to put here... + .sizeimage = (32*1024), + .colorspace = 0, // doesn't make sense here + .priv = 0 + } + } + }, + [PVR_FORMAT_VBI] = { + .type = V4L2_BUF_TYPE_VBI_CAPTURE, + .fmt = { + .vbi = { + .sampling_rate = 27000000, + .offset = 248, + .samples_per_line = 1443, + .sample_format = V4L2_PIX_FMT_GREY, + .start = { 0, 0 }, + .count = { 0, 0 }, + .flags = 0, + } + } + } +}; + + + +/* + * This is part of Video 4 Linux API. These procedures handle ioctl() calls. + */ +static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw), + sizeof(cap->bus_info)); + strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card)); + return 0; +} + +static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; + + *p = v4l2_prio_max(&vp->prio); + return 0; +} + +static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; + + return v4l2_prio_change(&vp->prio, &fh->prio, prio); +} + +static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val); + *std = val; + return ret; +} + +int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std); +} + +static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val); + *std = val; + return ret; +} + +static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + struct v4l2_input tmp; + unsigned int cnt; + int val; + + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + + memset(&tmp, 0, sizeof(tmp)); + tmp.index = vi->index; + if (vi->index >= fh->input_cnt) + return -EINVAL; + val = fh->input_map[vi->index]; + switch (val) { + case PVR2_CVAL_INPUT_TV: + case PVR2_CVAL_INPUT_DTV: + case PVR2_CVAL_INPUT_RADIO: + tmp.type = V4L2_INPUT_TYPE_TUNER; + break; + case PVR2_CVAL_INPUT_SVIDEO: + case PVR2_CVAL_INPUT_COMPOSITE: + tmp.type = V4L2_INPUT_TYPE_CAMERA; + break; + default: + return -EINVAL; + } + + cnt = 0; + pvr2_ctrl_get_valname(cptr, val, + tmp.name, sizeof(tmp.name) - 1, &cnt); + tmp.name[cnt] = 0; + + /* Don't bother with audioset, since this driver currently + always switches the audio whenever the video is + switched. */ + + /* Handling std is a tougher problem. It doesn't make + sense in cases where a device might be multi-standard. + We could just copy out the current value for the + standard, but it can change over time. For now just + leave it zero. */ + *vi = tmp; + return 0; +} + +static int pvr2_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int idx; + struct pvr2_ctrl *cptr; + int val; + int ret; + + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + val = 0; + ret = pvr2_ctrl_get_value(cptr, &val); + *i = 0; + for (idx = 0; idx < fh->input_cnt; idx++) { + if (fh->input_map[idx] == val) { + *i = idx; + break; + } + } + return ret; +} + +static int pvr2_s_input(struct file *file, void *priv, unsigned int inp) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (inp >= fh->input_cnt) + return -EINVAL; + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), + fh->input_map[inp]); +} + +static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: We are returning one "fake" input here + which could very well be called "whatever_we_like". + This is for apps that want to see an audio input + just to feel comfortable, as well as to test if + it can do stereo or sth. There is actually no guarantee + that the actual audio input cannot change behind the app's + back, but most applications should not mind that either. + + Hopefully, mplayer people will work with us on this (this + whole mess is to support mplayer pvr://), or Hans will come + up with a more standard way to say "we have inputs but we + don 't want you to change them independent of video" which + will sort this mess. + */ + + if (vin->index > 0) + return -EINVAL; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ + vin->index = 0; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout) +{ + if (vout->index) + return -EINVAL; + return 0; +} + +static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (vt->index != 0) + return -EINVAL; /* Only answer for the 1st tuner */ + + pvr2_hdw_execute_tuner_poll(hdw); + return pvr2_hdw_get_tuner_status(hdw, vt); +} + +static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (vt->index != 0) + return -EINVAL; + + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE), + vt->audmode); +} + +int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned long fv; + struct v4l2_tuner vt; + int cur_input; + struct pvr2_ctrl *ctrlp; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + ret = pvr2_ctrl_get_value(ctrlp, &cur_input); + if (ret != 0) + return ret; + if (vf->type == V4L2_TUNER_RADIO) { + if (cur_input != PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO); + } else { + if (cur_input == PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV); + } + fv = vf->frequency; + if (vt.capability & V4L2_TUNER_CAP_LOW) + fv = (fv * 125) / 2; + else + fv = fv * 62500; + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); +} + +static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int cur_input; + struct v4l2_tuner vt; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY), + &val); + if (ret != 0) + return ret; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), + &cur_input); + if (cur_input == PVR2_CVAL_INPUT_RADIO) + vf->type = V4L2_TUNER_RADIO; + else + vf->type = V4L2_TUNER_ANALOG_TV; + if (vt.capability & V4L2_TUNER_CAP_LOW) + val = (val * 2) / 125; + else + val /= 62500; + vf->frequency = val; + return 0; +} + +static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd) +{ + /* Only one format is supported : mpeg.*/ + if (fd->index != 0) + return -EINVAL; + + memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); + return 0; +} + +static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val; + + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES), + &val); + vf->fmt.pix.width = val; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES), + &val); + vf->fmt.pix.height = val; + return 0; +} + +static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int lmin, lmax, ldef; + struct pvr2_ctrl *hcp, *vcp; + int h = vf->fmt.pix.height; + int w = vf->fmt.pix.width; + + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + + lmin = pvr2_ctrl_get_min(hcp); + lmax = pvr2_ctrl_get_max(hcp); + pvr2_ctrl_get_def(hcp, &ldef); + if (w == -1) + w = ldef; + else if (w < lmin) + w = lmin; + else if (w > lmax) + w = lmax; + lmin = pvr2_ctrl_get_min(vcp); + lmax = pvr2_ctrl_get_max(vcp); + pvr2_ctrl_get_def(vcp, &ldef); + if (h == -1) + h = ldef; + else if (h < lmin) + h = lmin; + else if (h > lmax) + h = lmax; + + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], + sizeof(struct v4l2_format)); + vf->fmt.pix.width = w; + vf->fmt.pix.height = h; + return 0; +} + +static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *hcp, *vcp; + int ret = pvr2_try_fmt_vid_cap(file, fh, vf); + + if (ret) + return ret; + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + pvr2_ctrl_set_value(hcp, vf->fmt.pix.width); + pvr2_ctrl_set_value(vcp, vf->fmt.pix.height); + return 0; +} + +static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_v4l2_dev *pdi = fh->pdi; + int ret; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; + } + ret = pvr2_hdw_set_stream_type(hdw, pdi->config); + if (ret < 0) + return ret; + return pvr2_hdw_set_streaming(hdw, !0); +} + +static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; + } + return pvr2_hdw_set_streaming(hdw, 0); +} + +static int pvr2_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + int val; + + if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { + cptr = pvr2_hdw_get_ctrl_nextv4l( + hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); + if (cptr) + vc->id = pvr2_ctrl_get_v4lid(cptr); + } else { + cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id); + } + if (!cptr) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x not implemented here", + vc->id); + return -EINVAL; + } + + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x mapping name=%s (%s)", + vc->id, pvr2_ctrl_get_name(cptr), + pvr2_ctrl_get_desc(cptr)); + strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name)); + vc->flags = pvr2_ctrl_get_v4lflags(cptr); + pvr2_ctrl_get_def(cptr, &val); + vc->default_value = val; + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: + vc->type = V4L2_CTRL_TYPE_MENU; + vc->minimum = 0; + vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; + vc->step = 1; + break; + case pvr2_ctl_bool: + vc->type = V4L2_CTRL_TYPE_BOOLEAN; + vc->minimum = 0; + vc->maximum = 1; + vc->step = 1; + break; + case pvr2_ctl_int: + vc->type = V4L2_CTRL_TYPE_INTEGER; + vc->minimum = pvr2_ctrl_get_min(cptr); + vc->maximum = pvr2_ctrl_get_max(cptr); + vc->step = 1; + break; + default: + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x name=%s not mappable", + vc->id, pvr2_ctrl_get_name(cptr)); + return -EINVAL; + } + return 0; +} + +static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int cnt = 0; + int ret; + + ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id), + vm->index, + vm->name, sizeof(vm->name) - 1, + &cnt); + vm->name[cnt] = 0; + return ret; +} + +static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + &val); + vc->value = val; + return ret; +} + +static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + vc->value); +} + +static int pvr2_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int val; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val); + if (ret) { + ctls->error_idx = idx; + return ret; + } + /* Ensure that if read as a 64 bit value, the user + will still get a hopefully sane value */ + ctrl->value64 = 0; + ctrl->value = val; + } + return 0; +} + +static int pvr2_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), + ctrl->value); + if (ret) { + ctls->error_idx = idx; + return ret; + } + } + return 0; +} + +static int pvr2_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + struct pvr2_ctrl *pctl; + unsigned int idx; + + /* For the moment just validate that the requested control + actually exists. */ + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); + if (!pctl) { + ctls->error_idx = idx; + return -EINVAL; + } + } + return 0; +} + +static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_hdw_get_cropcap(hdw, cap); + cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ + return ret; +} + +static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); + if (ret != 0) + return -EINVAL; + crop->c.left = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); + if (ret != 0) + return -EINVAL; + crop->c.top = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); + if (ret != 0) + return -EINVAL; + crop->c.width = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); + if (ret != 0) + return -EINVAL; + crop->c.height = val; + return 0; +} + +static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), + crop->c.left); + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), + crop->c.top); + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), + crop->c.width); + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), + crop->c.height); + if (ret != 0) + return -EINVAL; + return 0; +} + +static int pvr2_log_status(struct file *file, void *priv) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + pvr2_hdw_trigger_module_log(hdw); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; + + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 0, &val); + req->val = val; + return ret; +} + +static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; + + val = req->val; + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 1, &val); + return ret; +} +#endif + +static const struct v4l2_ioctl_ops pvr2_ioctl_ops = { + .vidioc_querycap = pvr2_querycap, + .vidioc_g_priority = pvr2_g_priority, + .vidioc_s_priority = pvr2_s_priority, + .vidioc_s_audio = pvr2_s_audio, + .vidioc_g_audio = pvr2_g_audio, + .vidioc_enumaudio = pvr2_enumaudio, + .vidioc_enum_input = pvr2_enum_input, + .vidioc_cropcap = pvr2_cropcap, + .vidioc_s_crop = pvr2_s_crop, + .vidioc_g_crop = pvr2_g_crop, + .vidioc_g_input = pvr2_g_input, + .vidioc_s_input = pvr2_s_input, + .vidioc_g_frequency = pvr2_g_frequency, + .vidioc_s_frequency = pvr2_s_frequency, + .vidioc_s_tuner = pvr2_s_tuner, + .vidioc_g_tuner = pvr2_g_tuner, + .vidioc_g_std = pvr2_g_std, + .vidioc_s_std = pvr2_s_std, + .vidioc_querystd = pvr2_querystd, + .vidioc_log_status = pvr2_log_status, + .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap, + .vidioc_streamon = pvr2_streamon, + .vidioc_streamoff = pvr2_streamoff, + .vidioc_queryctrl = pvr2_queryctrl, + .vidioc_querymenu = pvr2_querymenu, + .vidioc_g_ctrl = pvr2_g_ctrl, + .vidioc_s_ctrl = pvr2_s_ctrl, + .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls, + .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls, + .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = pvr2_g_register, + .vidioc_s_register = pvr2_s_register, +#endif +}; + +static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) +{ + struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw; + enum pvr2_config cfg = dip->config; + char msg[80]; + unsigned int mcnt; + + /* Construct the unregistration message *before* we actually + perform the unregistration step. By doing it this way we don't + have to worry about potentially touching deleted resources. */ + mcnt = scnprintf(msg, sizeof(msg) - 1, + "pvrusb2: unregistered device %s [%s]", + video_device_node_name(&dip->devbase), + pvr2_config_get_name(cfg)); + msg[mcnt] = 0; + + pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1); + + /* Paranoia */ + dip->v4lp = NULL; + dip->stream = NULL; + + /* Actual deallocation happens later when all internal references + are gone. */ + video_unregister_device(&dip->devbase); + + printk(KERN_INFO "%s\n", msg); + +} + + +static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip) +{ + if (!dip) return; + if (!dip->devbase.parent) return; + dip->devbase.parent = NULL; + device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE); +} + + +static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) +{ + if (vp->dev_video) { + pvr2_v4l2_dev_destroy(vp->dev_video); + vp->dev_video = NULL; + } + if (vp->dev_radio) { + pvr2_v4l2_dev_destroy(vp->dev_radio); + vp->dev_radio = NULL; + } + + pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); + pvr2_channel_done(&vp->channel); + kfree(vp); +} + + +static void pvr2_video_device_release(struct video_device *vdev) +{ + struct pvr2_v4l2_dev *dev; + dev = container_of(vdev,struct pvr2_v4l2_dev,devbase); + kfree(dev); +} + + +static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) +{ + struct pvr2_v4l2 *vp; + vp = container_of(chp,struct pvr2_v4l2,channel); + if (!vp->channel.mc_head->disconnect_flag) return; + pvr2_v4l2_dev_disassociate_parent(vp->dev_video); + pvr2_v4l2_dev_disassociate_parent(vp->dev_radio); + if (vp->vfirst) return; + pvr2_v4l2_destroy_no_lock(vp); +} + + +static long pvr2_v4l2_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + long ret = -EINVAL; + + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); + + if (!pvr2_hdw_dev_ok(hdw)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "ioctl failed - bad or no context"); + return -EFAULT; + } + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + ret = v4l2_prio_check(&vp->prio, fh->prio); + if (ret) + return ret; + } + + ret = video_ioctl2(file, cmd, arg); + + pvr2_hdw_commit_ctl(hdw); + + if (ret < 0) { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); + } else { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld" + " command was:", ret); + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), + cmd); + } + } + } else { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", + ret, ret); + } + return ret; + +} + + +static int pvr2_v4l2_release(struct file *file) +{ + struct pvr2_v4l2_fh *fhp = file->private_data; + struct pvr2_v4l2 *vp = fhp->vhead; + struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw; + + pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); + + if (fhp->rhp) { + struct pvr2_stream *sp; + pvr2_hdw_set_streaming(hdw,0); + sp = pvr2_ioread_get_stream(fhp->rhp); + if (sp) pvr2_stream_set_callback(sp,NULL,NULL); + pvr2_ioread_destroy(fhp->rhp); + fhp->rhp = NULL; + } + + v4l2_prio_close(&vp->prio, fhp->prio); + file->private_data = NULL; + + if (fhp->vnext) { + fhp->vnext->vprev = fhp->vprev; + } else { + vp->vlast = fhp->vprev; + } + if (fhp->vprev) { + fhp->vprev->vnext = fhp->vnext; + } else { + vp->vfirst = fhp->vnext; + } + fhp->vnext = NULL; + fhp->vprev = NULL; + fhp->vhead = NULL; + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p",fhp); + if (fhp->input_map) { + kfree(fhp->input_map); + fhp->input_map = NULL; + } + kfree(fhp); + if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { + pvr2_v4l2_destroy_no_lock(vp); + } + return 0; +} + + +static int pvr2_v4l2_open(struct file *file) +{ + struct pvr2_v4l2_dev *dip; /* Our own context pointer */ + struct pvr2_v4l2_fh *fhp; + struct pvr2_v4l2 *vp; + struct pvr2_hdw *hdw; + unsigned int input_mask = 0; + unsigned int input_cnt,idx; + int ret = 0; + + dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); + + vp = dip->v4lp; + hdw = vp->channel.hdw; + + pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open"); + + if (!pvr2_hdw_dev_ok(hdw)) { + pvr2_trace(PVR2_TRACE_OPEN_CLOSE, + "pvr2_v4l2_open: hardware not ready"); + return -EIO; + } + + fhp = kzalloc(sizeof(*fhp),GFP_KERNEL); + if (!fhp) { + return -ENOMEM; + } + + init_waitqueue_head(&fhp->wait_data); + fhp->pdi = dip; + + pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); + pvr2_channel_init(&fhp->channel,vp->channel.mc_head); + + if (dip->v4l_type == VFL_TYPE_RADIO) { + /* Opening device as a radio, legal input selection subset + is just the radio. */ + input_mask = (1 << PVR2_CVAL_INPUT_RADIO); + } else { + /* Opening the main V4L device, legal input selection + subset includes all analog inputs. */ + input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) | + (1 << PVR2_CVAL_INPUT_TV) | + (1 << PVR2_CVAL_INPUT_COMPOSITE) | + (1 << PVR2_CVAL_INPUT_SVIDEO)); + } + ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask); + if (ret) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input mask error)", + fhp); + + kfree(fhp); + return ret; + } + + input_mask &= pvr2_hdw_get_input_available(hdw); + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (input_mask & (1 << idx)) input_cnt++; + } + fhp->input_cnt = input_cnt; + fhp->input_map = kzalloc(input_cnt,GFP_KERNEL); + if (!fhp->input_map) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input map failure)", + fhp); + kfree(fhp); + return -ENOMEM; + } + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (!(input_mask & (1 << idx))) continue; + fhp->input_map[input_cnt++] = idx; + } + + fhp->vnext = NULL; + fhp->vprev = vp->vlast; + if (vp->vlast) { + vp->vlast->vnext = fhp; + } else { + vp->vfirst = fhp; + } + vp->vlast = fhp; + fhp->vhead = vp; + + fhp->file = file; + file->private_data = fhp; + v4l2_prio_open(&vp->prio, &fhp->prio); + + fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); + + return 0; +} + + +static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp) +{ + wake_up(&fhp->wait_data); +} + +static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) +{ + int ret; + struct pvr2_stream *sp; + struct pvr2_hdw *hdw; + if (fh->rhp) return 0; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means that we're + not currently allowed to stream from this node. */ + return -EPERM; + } + + /* First read() attempt. Try to claim the stream and start + it... */ + if ((ret = pvr2_channel_claim_stream(&fh->channel, + fh->pdi->stream)) != 0) { + /* Someone else must already have it */ + return ret; + } + + fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream); + if (!fh->rhp) { + pvr2_channel_claim_stream(&fh->channel,NULL); + return -ENOMEM; + } + + hdw = fh->channel.mc_head->hdw; + sp = fh->pdi->stream->stream; + pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh); + pvr2_hdw_set_stream_type(hdw,fh->pdi->config); + if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret; + return pvr2_ioread_set_enabled(fh->rhp,!0); +} + + +static ssize_t pvr2_v4l2_read(struct file *file, + char __user *buff, size_t count, loff_t *ppos) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + int ret; + + if (fh->fw_mode_flag) { + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + char *tbuf; + int c1,c2; + int tcnt = 0; + unsigned int offs = *ppos; + + tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL); + if (!tbuf) return -ENOMEM; + + while (count) { + c1 = count; + if (c1 > PAGE_SIZE) c1 = PAGE_SIZE; + c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1); + if (c2 < 0) { + tcnt = c2; + break; + } + if (!c2) break; + if (copy_to_user(buff,tbuf,c2)) { + tcnt = -EFAULT; + break; + } + offs += c2; + tcnt += c2; + buff += c2; + count -= c2; + *ppos += c2; + } + kfree(tbuf); + return tcnt; + } + + if (!fh->rhp) { + ret = pvr2_v4l2_iosetup(fh); + if (ret) { + return ret; + } + } + + for (;;) { + ret = pvr2_ioread_read(fh->rhp,buff,count); + if (ret >= 0) break; + if (ret != -EAGAIN) break; + if (file->f_flags & O_NONBLOCK) break; + /* Doing blocking I/O. Wait here. */ + ret = wait_event_interruptible( + fh->wait_data, + pvr2_ioread_avail(fh->rhp) >= 0); + if (ret < 0) break; + } + + return ret; +} + + +static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait) +{ + unsigned int mask = 0; + struct pvr2_v4l2_fh *fh = file->private_data; + int ret; + + if (fh->fw_mode_flag) { + mask |= POLLIN | POLLRDNORM; + return mask; + } + + if (!fh->rhp) { + ret = pvr2_v4l2_iosetup(fh); + if (ret) return POLLERR; + } + + poll_wait(file,&fh->wait_data,wait); + + if (pvr2_ioread_avail(fh->rhp) >= 0) { + mask |= POLLIN | POLLRDNORM; + } + + return mask; +} + + +static const struct v4l2_file_operations vdev_fops = { + .owner = THIS_MODULE, + .open = pvr2_v4l2_open, + .release = pvr2_v4l2_release, + .read = pvr2_v4l2_read, + .ioctl = pvr2_v4l2_ioctl, + .poll = pvr2_v4l2_poll, +}; + + +static struct video_device vdev_template = { + .fops = &vdev_fops, +}; + + +static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, + struct pvr2_v4l2 *vp, + int v4l_type) +{ + struct usb_device *usbdev; + int mindevnum; + int unit_number; + struct pvr2_hdw *hdw; + int *nr_ptr = NULL; + dip->v4lp = vp; + + hdw = vp->channel.mc_head->hdw; + usbdev = pvr2_hdw_get_dev(hdw); + dip->v4l_type = v4l_type; + switch (v4l_type) { + case VFL_TYPE_GRABBER: + dip->stream = &vp->channel.mc_head->video_stream; + dip->config = pvr2_config_mpeg; + dip->minor_type = pvr2_v4l_type_video; + nr_ptr = video_nr; + if (!dip->stream) { + pr_err(KBUILD_MODNAME + ": Failed to set up pvrusb2 v4l video dev" + " due to missing stream instance\n"); + return; + } + break; + case VFL_TYPE_VBI: + dip->config = pvr2_config_vbi; + dip->minor_type = pvr2_v4l_type_vbi; + nr_ptr = vbi_nr; + break; + case VFL_TYPE_RADIO: + dip->stream = &vp->channel.mc_head->video_stream; + dip->config = pvr2_config_mpeg; + dip->minor_type = pvr2_v4l_type_radio; + nr_ptr = radio_nr; + break; + default: + /* Bail out (this should be impossible) */ + pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev" + " due to unrecognized config\n"); + return; + } + + memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); + dip->devbase.release = pvr2_video_device_release; + dip->devbase.ioctl_ops = &pvr2_ioctl_ops; + { + int val; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_STDAVAIL), &val); + dip->devbase.tvnorms = (v4l2_std_id)val; + } + + mindevnum = -1; + unit_number = pvr2_hdw_get_unit_number(hdw); + if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { + mindevnum = nr_ptr[unit_number]; + } + dip->devbase.parent = &usbdev->dev; + if ((video_register_device(&dip->devbase, + dip->v4l_type, mindevnum) < 0) && + (video_register_device(&dip->devbase, + dip->v4l_type, -1) < 0)) { + pr_err(KBUILD_MODNAME + ": Failed to register pvrusb2 v4l device\n"); + } + + printk(KERN_INFO "pvrusb2: registered device %s [%s]\n", + video_device_node_name(&dip->devbase), + pvr2_config_get_name(dip->config)); + + pvr2_hdw_v4l_store_minor_number(hdw, + dip->minor_type,dip->devbase.minor); +} + + +struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) +{ + struct pvr2_v4l2 *vp; + + vp = kzalloc(sizeof(*vp),GFP_KERNEL); + if (!vp) return vp; + pvr2_channel_init(&vp->channel,mnp); + pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); + + vp->channel.check_func = pvr2_v4l2_internal_check; + + /* register streams */ + vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); + if (!vp->dev_video) goto fail; + pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); + if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & + (1 << PVR2_CVAL_INPUT_RADIO)) { + vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); + if (!vp->dev_radio) goto fail; + pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); + } + + return vp; + fail: + pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp); + pvr2_v4l2_destroy_no_lock(vp); + return NULL; +} + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h new file mode 100644 index 000000000000..34c011a7b107 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h @@ -0,0 +1,39 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_V4L2_H +#define __PVRUSB2_V4L2_H + +#include "pvrusb2-context.h" + +struct pvr2_v4l2; + +struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *); + +#endif /* __PVRUSB2_V4L2_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c new file mode 100644 index 000000000000..2e205c99eb96 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c @@ -0,0 +1,114 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + saa711x support that is available in the v4l available starting + with linux 2.6.15. + +*/ + +#include "pvrusb2-video-v4l.h" + + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include +#include +#include +#include + +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + + +static const int routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, + /* In radio mode, we mute the video, but point at one + spot just to stay consistent */ + [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, +}; + +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + +static const int routing_scheme1[] = { + [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, + [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3, + [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */ +}; + +static const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, +}; + +void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) +{ + if (hdw->input_dirty || hdw->force_dirty) { + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + u32 input; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", + hdw->input_val); + + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev v4l2 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; + } + input = sp->def[hdw->input_val]; + sd->ops->video->s_routing(sd, input, 0, 0); + } +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h new file mode 100644 index 000000000000..3b0bd5db602b --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h @@ -0,0 +1,48 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_VIDEO_V4L_H +#define __PVRUSB2_VIDEO_V4L_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles device video processing. This interface is + used internally by the driver; higher level code should only + interact through the interface provided by pvrusb2-hdw.h. + +*/ + + +#include "pvrusb2-hdw-internal.h" +void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); + +#endif /* __PVRUSB2_VIDEO_V4L_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c new file mode 100644 index 000000000000..3ac8d751a5c0 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c @@ -0,0 +1,70 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + wm8775. + +*/ + +#include "pvrusb2-wm8775.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include +#include +#include + +void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) +{ + if (hdw->input_dirty || hdw->force_dirty) { + u32 input; + + switch (hdw->input_val) { + case PVR2_CVAL_INPUT_RADIO: + input = 1; + break; + default: + /* All other cases just use the second input */ + input = 2; + break; + } + pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775" + " set_input(val=%d route=0x%x)", + hdw->input_val, input); + + sd->ops->audio->s_routing(sd, input, 0, 0); + } +} + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h new file mode 100644 index 000000000000..0577bc7246fb --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h @@ -0,0 +1,52 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_WM8775_H +#define __PVRUSB2_WM8775_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which performs analog -> digital audio conversion for + external audio inputs. This interface is used internally by the + driver; higher level code should only interact through the + interface provided by pvrusb2-hdw.h. + +*/ + + + +#include "pvrusb2-hdw-internal.h" + +void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); + + +#endif /* __PVRUSB2_WM8775_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2.h b/drivers/media/usb/pvrusb2/pvrusb2.h new file mode 100644 index 000000000000..240de9b35661 --- /dev/null +++ b/drivers/media/usb/pvrusb2/pvrusb2.h @@ -0,0 +1,42 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_H +#define __PVRUSB2_H + +/* Maximum number of pvrusb2 instances we can track at once. You + might want to increase this - however the driver operation will not + be impaired if it is too small. Instead additional units just + won't have an ID assigned and it might not be possible to specify + module parameters for those extra units. */ +#define PVR_NUM 20 + +#endif /* __PVRUSB2_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/usb/pwc/Kconfig b/drivers/media/usb/pwc/Kconfig new file mode 100644 index 000000000000..d63d0a850035 --- /dev/null +++ b/drivers/media/usb/pwc/Kconfig @@ -0,0 +1,48 @@ +config USB_PWC + tristate "USB Philips Cameras" + depends on VIDEO_V4L2 + select VIDEOBUF2_VMALLOC + ---help--- + Say Y or M here if you want to use one of these Philips & OEM + webcams: + * Philips PCA645, PCA646 + * Philips PCVC675, PCVC680, PCVC690 + * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 + * Philips SPC900NC + * Askey VC010 + * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' + and 'Orbit'/'Sphere' + * Samsung MPC-C10, MPC-C30 + * Creative Webcam 5, Pro Ex + * SOTEC Afina Eye + * Visionite VCS-UC300, VCS-UM100 + + The PCA635, PCVC665 and PCVC720/20 are not supported by this driver + and never will be, but the 665 and 720/20 are supported by other + drivers. + + Some newer logitech webcams are not handled by this driver but by the + Usb Video Class driver (linux-uvc). + + The built-in microphone is enabled by selecting USB Audio support. + + To compile this driver as a module, choose M here: the + module will be called pwc. + +config USB_PWC_DEBUG + bool "USB Philips Cameras verbose debug" + depends on USB_PWC + help + Say Y here in order to have the pwc driver generate verbose debugging + messages. + A special module options 'trace' is used to control the verbosity. + +config USB_PWC_INPUT_EVDEV + bool "USB Philips Cameras input events device support" + default y + depends on USB_PWC && (USB_PWC=INPUT || INPUT=y) + ---help--- + This option makes USB Philips cameras register the snapshot button as + an input device to report button events. + + If you are in doubt, say Y. diff --git a/drivers/media/usb/pwc/Makefile b/drivers/media/usb/pwc/Makefile new file mode 100644 index 000000000000..f5c8ec261e87 --- /dev/null +++ b/drivers/media/usb/pwc/Makefile @@ -0,0 +1,4 @@ +pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o +pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o + +obj-$(CONFIG_USB_PWC) += pwc.o diff --git a/drivers/media/usb/pwc/philips.txt b/drivers/media/usb/pwc/philips.txt new file mode 100644 index 000000000000..d38dd791511e --- /dev/null +++ b/drivers/media/usb/pwc/philips.txt @@ -0,0 +1,236 @@ +This file contains some additional information for the Philips and OEM webcams. +E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19 +Site: http://www.smcc.demon.nl/webcam/ + +As of this moment, the following cameras are supported: + * Philips PCA645 + * Philips PCA646 + * Philips PCVC675 + * Philips PCVC680 + * Philips PCVC690 + * Philips PCVC720/40 + * Philips PCVC730 + * Philips PCVC740 + * Philips PCVC750 + * Askey VC010 + * Creative Labs Webcam 5 + * Creative Labs Webcam Pro Ex + * Logitech QuickCam 3000 Pro + * Logitech QuickCam 4000 Pro + * Logitech QuickCam Notebook Pro + * Logitech QuickCam Zoom + * Logitech QuickCam Orbit + * Logitech QuickCam Sphere + * Samsung MPC-C10 + * Samsung MPC-C30 + * Sotec Afina Eye + * AME CU-001 + * Visionite VCS-UM100 + * Visionite VCS-UC300 + +The main webpage for the Philips driver is at the address above. It contains +a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin +contains decompression routines that allow you to use higher image sizes and +framerates; in addition the webcam uses less bandwidth on the USB bus (handy +if you want to run more than 1 camera simultaneously). These routines fall +under a NDA, and may therefore not be distributed as source; however, its use +is completely optional. + +You can build this code either into your kernel, or as a module. I recommend +the latter, since it makes troubleshooting a lot easier. The built-in +microphone is supported through the USB Audio class. + +When you load the module you can set some default settings for the +camera; some programs depend on a particular image-size or -format and +don't know how to set it properly in the driver. The options are: + +size + Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or + 'vga', for an image size of resp. 128x96, 160x120, 176x144, + 320x240, 352x288 and 640x480 (of course, only for those cameras that + support these resolutions). + +fps + Specifies the desired framerate. Is an integer in the range of 4-30. + +fbufs + This parameter specifies the number of internal buffers to use for storing + frames from the cam. This will help if the process that reads images from + the cam is a bit slow or momentarily busy. However, on slow machines it + only introduces lag, so choose carefully. The default is 3, which is + reasonable. You can set it between 2 and 5. + +mbufs + This is an integer between 1 and 10. It will tell the module the number of + buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends. + The default is 2, which is adequate for most applications (double + buffering). + + Should you experience a lot of 'Dumping frame...' messages during + grabbing with a tool that uses mmap(), you might want to increase if. + However, it doesn't really buffer images, it just gives you a bit more + slack when your program is behind. But you need a multi-threaded or + forked program to really take advantage of these buffers. + + The absolute maximum is 10, but don't set it too high! Every buffer takes + up 460 KB of RAM, so unless you have a lot of memory setting this to + something more than 4 is an absolute waste. This memory is only + allocated during open(), so nothing is wasted when the camera is not in + use. + +power_save + When power_save is enabled (set to 1), the module will try to shut down + the cam on close() and re-activate on open(). This will save power and + turn off the LED. Not all cameras support this though (the 645 and 646 + don't have power saving at all), and some models don't work either (they + will shut down, but never wake up). Consider this experimental. By + default this option is disabled. + +compression (only useful with the plugin) + With this option you can control the compression factor that the camera + uses to squeeze the image through the USB bus. You can set the + parameter between 0 and 3: + 0 = prefer uncompressed images; if the requested mode is not available + in an uncompressed format, the driver will silently switch to low + compression. + 1 = low compression. + 2 = medium compression. + 3 = high compression. + + High compression takes less bandwidth of course, but it could also + introduce some unwanted artefacts. The default is 2, medium compression. + See the FAQ on the website for an overview of which modes require + compression. + + The compression parameter does not apply to the 645 and 646 cameras + and OEM models derived from those (only a few). Most cams honour this + parameter. + +leds + This settings takes 2 integers, that define the on/off time for the LED + (in milliseconds). One of the interesting things that you can do with + this is let the LED blink while the camera is in use. This: + + leds=500,500 + + will blink the LED once every second. But with: + + leds=0,0 + + the LED never goes on, making it suitable for silent surveillance. + + By default the camera's LED is on solid while in use, and turned off + when the camera is not used anymore. + + This parameter works only with the ToUCam range of cameras (720, 730, 740, + 750) and OEMs. For other cameras this command is silently ignored, and + the LED cannot be controlled. + + Finally: this parameters does not take effect UNTIL the first time you + open the camera device. Until then, the LED remains on. + +dev_hint + A long standing problem with USB devices is their dynamic nature: you + never know what device a camera gets assigned; it depends on module load + order, the hub configuration, the order in which devices are plugged in, + and the phase of the moon (i.e. it can be random). With this option you + can give the driver a hint as to what video device node (/dev/videoX) it + should use with a specific camera. This is also handy if you have two + cameras of the same model. + + A camera is specified by its type (the number from the camera model, + like PCA645, PCVC750VC, etc) and optionally the serial number (visible + in /proc/bus/usb/devices). A hint consists of a string with the following + format: + + [type[.serialnumber]:]node + + The square brackets mean that both the type and the serialnumber are + optional, but a serialnumber cannot be specified without a type (which + would be rather pointless). The serialnumber is separated from the type + by a '.'; the node number by a ':'. + + This somewhat cryptic syntax is best explained by a few examples: + + dev_hint=3,5 The first detected cam gets assigned + /dev/video3, the second /dev/video5. Any + other cameras will get the first free + available slot (see below). + + dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1, + and a PCVC680 /dev/video2. + + dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber + 0123 goes to /dev/video3, the same + camera model with the 4567 serial + gets /dev/video0. + + dev_hint=750:1,4,5,6 The PCVC750 camera will get /dev/video1, the + next 3 Philips cams will use /dev/video4 + through /dev/video6. + + Some points worth knowing: + - Serialnumbers are case sensitive and must be written full, including + leading zeroes (it's treated as a string). + - If a device node is already occupied, registration will fail and + the webcam is not available. + - You can have up to 64 video devices; be sure to make enough device + nodes in /dev if you want to spread the numbers. + After /dev/video9 comes /dev/video10 (not /dev/videoA). + - If a camera does not match any dev_hint, it will simply get assigned + the first available device node, just as it used to be. + +trace + In order to better detect problems, it is now possible to turn on a + 'trace' of some of the calls the module makes; it logs all items in your + kernel log at debug level. + + The trace variable is a bitmask; each bit represents a certain feature. + If you want to trace something, look up the bit value(s) in the table + below, add the values together and supply that to the trace variable. + + Value Value Description Default + (dec) (hex) + 1 0x1 Module initialization; this will log messages On + while loading and unloading the module + + 2 0x2 probe() and disconnect() traces On + + 4 0x4 Trace open() and close() calls Off + + 8 0x8 read(), mmap() and associated ioctl() calls Off + + 16 0x10 Memory allocation of buffers, etc. Off + + 32 0x20 Showing underflow, overflow and Dumping frame On + messages + + 64 0x40 Show viewport and image sizes Off + + 128 0x80 PWCX debugging Off + + For example, to trace the open() & read() functions, sum 8 + 4 = 12, + so you would supply trace=12 during insmod or modprobe. If + you want to turn the initialization and probing tracing off, set trace=0. + The default value for trace is 35 (0x23). + + + +Example: + + # modprobe pwc size=cif fps=15 power_save=1 + +The fbufs, mbufs and trace parameters are global and apply to all connected +cameras. Each camera has its own set of buffers. + +size and fps only specify defaults when you open() the device; this is to +accommodate some tools that don't set the size. You can change these +settings after open() with the Video4Linux ioctl() calls. The default of +defaults is QCIF size at 10 fps. + +The compression parameter is semiglobal; it sets the initial compression +preference for all camera's, but this parameter can be set per camera with +the VIDIOCPWCSCQUAL ioctl() call. + +All parameters are optional. + diff --git a/drivers/media/usb/pwc/pwc-ctrl.c b/drivers/media/usb/pwc/pwc-ctrl.c new file mode 100644 index 000000000000..1f506fde97d0 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-ctrl.c @@ -0,0 +1,553 @@ +/* Driver for Philips webcam + Functions that send various control messages to the webcam, including + video modes. + (C) 1999-2003 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + (C) 2011 Hans de Goede + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + Changes + 2001/08/03 Alvarado Added methods for changing white balance and + red/green gains + */ + +/* Control functions for the cam; brightness, contrast, video mode, etc. */ + +#ifdef __KERNEL__ +#include +#endif +#include + +#include "pwc.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" + +/* Selectors for status controls used only in this file */ +#define GET_STATUS_B00 0x0B00 +#define SENSOR_TYPE_FORMATTER1 0x0C00 +#define GET_STATUS_3000 0x3000 +#define READ_RAW_Y_MEAN_FORMATTER 0x3100 +#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 +#define MIRROR_IMAGE_FORMATTER 0x3300 +#define LED_FORMATTER 0x3400 +#define LOWLIGHT 0x3500 +#define GET_STATUS_3600 0x3600 +#define SENSOR_TYPE_FORMATTER2 0x3700 +#define GET_STATUS_3800 0x3800 +#define GET_STATUS_4000 0x4000 +#define GET_STATUS_4100 0x4100 /* Get */ +#define CTL_STATUS_4200 0x4200 /* [GS] 1 */ + +/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ +#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 + +static const char *size2name[PSZ_MAX] = +{ + "subQCIF", + "QSIF", + "QCIF", + "SIF", + "CIF", + "VGA", +}; + +/********/ + +/* Entries for the Nala (645/646) camera; the Nala doesn't have compression + preferences, so you either get compressed or non-compressed streams. + + An alternate value of 0 means this mode is not available at all. + */ + +#define PWC_FPS_MAX_NALA 8 + +struct Nala_table_entry { + char alternate; /* USB alternate setting */ + int compressed; /* Compressed yes/no */ + + unsigned char mode[3]; /* precomputed mode table */ +}; + +static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; + +static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = +{ +#include "pwc-nala.h" +}; + +/****************************************************************************/ + +static int recv_control_msg(struct pwc_device *pdev, + u8 request, u16 value, int recv_count) +{ + int rc; + + rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), + request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, pdev->vcinterface, + pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT); + if (rc < 0) + PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", + rc, request, value); + return rc; +} + +static inline int send_video_command(struct pwc_device *pdev, + int index, const unsigned char *buf, int buflen) +{ + int rc; + + memcpy(pdev->ctrl_buf, buf, buflen); + + rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), + SET_EP_STREAM_CTL, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VIDEO_OUTPUT_CONTROL_FORMATTER, index, + pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT); + if (rc >= 0) + memcpy(pdev->cmd_buf, buf, buflen); + else + PWC_ERROR("send_video_command error %d\n", rc); + + return rc; +} + +int send_control_msg(struct pwc_device *pdev, + u8 request, u16 value, void *buf, int buflen) +{ + return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, pdev->vcinterface, + buf, buflen, USB_CTRL_SET_TIMEOUT); +} + +static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression, int send_to_cam) +{ + int fps, ret = 0; + struct Nala_table_entry *pEntry; + int frames2frames[31] = + { /* closest match of framerate */ + 0, 0, 0, 0, 4, /* 0-4 */ + 5, 5, 7, 7, 10, /* 5-9 */ + 10, 10, 12, 12, 15, /* 10-14 */ + 15, 15, 15, 20, 20, /* 15-19 */ + 20, 20, 20, 24, 24, /* 20-24 */ + 24, 24, 24, 24, 24, /* 25-29 */ + 24 /* 30 */ + }; + int frames2table[31] = + { 0, 0, 0, 0, 0, /* 0-4 */ + 1, 1, 1, 2, 2, /* 5-9 */ + 3, 3, 4, 4, 4, /* 10-14 */ + 5, 5, 5, 5, 5, /* 15-19 */ + 6, 6, 6, 6, 7, /* 20-24 */ + 7, 7, 7, 7, 7, /* 25-29 */ + 7 /* 30 */ + }; + + if (size < 0 || size > PSZ_CIF) + return -EINVAL; + if (frames < 4) + frames = 4; + else if (frames > 25) + frames = 25; + frames = frames2frames[frames]; + fps = frames2table[frames]; + pEntry = &Nala_table[size][fps]; + if (pEntry->alternate == 0) + return -EINVAL; + + if (send_to_cam) + ret = send_video_command(pdev, pdev->vendpoint, + pEntry->mode, 3); + if (ret < 0) + return ret; + + if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) + pwc_dec1_init(pdev, pEntry->mode); + + /* Set various parameters */ + pdev->pixfmt = pixfmt; + pdev->vframes = frames; + pdev->valternate = pEntry->alternate; + pdev->width = pwc_image_sizes[size][0]; + pdev->height = pwc_image_sizes[size][1]; + pdev->frame_size = (pdev->width * pdev->height * 3) / 2; + if (pEntry->compressed) { + if (pdev->release < 5) { /* 4 fold compression */ + pdev->vbandlength = 528; + pdev->frame_size /= 4; + } + else { + pdev->vbandlength = 704; + pdev->frame_size /= 3; + } + } + else + pdev->vbandlength = 0; + + /* Let pwc-if.c:isoc_init know we don't support higher compression */ + *compression = 3; + + return 0; +} + + +static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression, int send_to_cam) +{ + const struct Timon_table_entry *pChoose; + int fps, ret = 0; + + if (size >= PSZ_MAX || *compression < 0 || *compression > 3) + return -EINVAL; + if (frames < 5) + frames = 5; + else if (size == PSZ_VGA && frames > 15) + frames = 15; + else if (frames > 30) + frames = 30; + fps = (frames / 5) - 1; + + /* Find a supported framerate with progressively higher compression */ + pChoose = NULL; + while (*compression <= 3) { + pChoose = &Timon_table[size][fps][*compression]; + if (pChoose->alternate != 0) + break; + (*compression)++; + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + if (send_to_cam) + ret = send_video_command(pdev, pdev->vendpoint, + pChoose->mode, 13); + if (ret < 0) + return ret; + + if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) + pwc_dec23_init(pdev, pChoose->mode); + + /* Set various parameters */ + pdev->pixfmt = pixfmt; + pdev->vframes = (fps + 1) * 5; + pdev->valternate = pChoose->alternate; + pdev->width = pwc_image_sizes[size][0]; + pdev->height = pwc_image_sizes[size][1]; + pdev->vbandlength = pChoose->bandlength; + if (pChoose->bandlength > 0) + pdev->frame_size = (pChoose->bandlength * pdev->height) / 4; + else + pdev->frame_size = (pdev->width * pdev->height * 12) / 8; + return 0; +} + + +static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression, int send_to_cam) +{ + const struct Kiara_table_entry *pChoose = NULL; + int fps, ret = 0; + + if (size >= PSZ_MAX || *compression < 0 || *compression > 3) + return -EINVAL; + if (frames < 5) + frames = 5; + else if (size == PSZ_VGA && frames > 15) + frames = 15; + else if (frames > 30) + frames = 30; + fps = (frames / 5) - 1; + + /* Find a supported framerate with progressively higher compression */ + while (*compression <= 3) { + pChoose = &Kiara_table[size][fps][*compression]; + if (pChoose->alternate != 0) + break; + (*compression)++; + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ + if (send_to_cam) + ret = send_video_command(pdev, 4, pChoose->mode, 12); + if (ret < 0) + return ret; + + if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) + pwc_dec23_init(pdev, pChoose->mode); + + /* All set and go */ + pdev->pixfmt = pixfmt; + pdev->vframes = (fps + 1) * 5; + pdev->valternate = pChoose->alternate; + pdev->width = pwc_image_sizes[size][0]; + pdev->height = pwc_image_sizes[size][1]; + pdev->vbandlength = pChoose->bandlength; + if (pdev->vbandlength > 0) + pdev->frame_size = (pdev->vbandlength * pdev->height) / 4; + else + pdev->frame_size = (pdev->width * pdev->height * 12) / 8; + PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n", + pdev->frame_size, pdev->vframes, size, pdev->vbandlength); + return 0; +} + +int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, + int pixfmt, int frames, int *compression, int send_to_cam) +{ + int ret, size; + + PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", + width, height, frames, pixfmt); + size = pwc_get_size(pdev, width, height); + PWC_TRACE("decode_size = %d.\n", size); + + if (DEVICE_USE_CODEC1(pdev->type)) { + ret = set_video_mode_Nala(pdev, size, pixfmt, frames, + compression, send_to_cam); + } else if (DEVICE_USE_CODEC3(pdev->type)) { + ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, + compression, send_to_cam); + } else { + ret = set_video_mode_Timon(pdev, size, pixfmt, frames, + compression, send_to_cam); + } + if (ret < 0) { + PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); + return ret; + } + pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; + PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height); + return 0; +} + +static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < PWC_FPS_MAX_NALA; i++) { + if (Nala_table[size][i].alternate) { + if (index--==0) return Nala_fps_vector[i]; + } + } + return 0; +} + +static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { + if (Kiara_table[size][i][3].alternate) { + if (index--==0) return Kiara_fps_vector[i]; + } + } + return 0; +} + +static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i=0; i < PWC_FPS_MAX_TIMON; i++) { + if (Timon_table[size][i][3].alternate) { + if (index--==0) return Timon_fps_vector[i]; + } + } + return 0; +} + +unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int ret; + + if (DEVICE_USE_CODEC1(pdev->type)) { + ret = pwc_get_fps_Nala(pdev, index, size); + + } else if (DEVICE_USE_CODEC3(pdev->type)) { + ret = pwc_get_fps_Kiara(pdev, index, size); + + } else { + ret = pwc_get_fps_Timon(pdev, index, size); + } + + return ret; +} + +int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) +{ + int ret; + + ret = recv_control_msg(pdev, request, value, 1); + if (ret < 0) + return ret; + + *data = pdev->ctrl_buf[0]; + return 0; +} + +int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) +{ + int ret; + + pdev->ctrl_buf[0] = data; + ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) +{ + int ret; + + ret = recv_control_msg(pdev, request, value, 1); + if (ret < 0) + return ret; + + *data = ((s8 *)pdev->ctrl_buf)[0]; + return 0; +} + +int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) +{ + int ret; + + ret = recv_control_msg(pdev, request, value, 2); + if (ret < 0) + return ret; + + *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; + return 0; +} + +int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) +{ + int ret; + + pdev->ctrl_buf[0] = data & 0xff; + pdev->ctrl_buf[1] = data >> 8; + ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2); + if (ret < 0) + return ret; + + return 0; +} + +int pwc_button_ctrl(struct pwc_device *pdev, u16 value) +{ + int ret; + + ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0); + if (ret < 0) + return ret; + + return 0; +} + +/* POWER */ +void pwc_camera_power(struct pwc_device *pdev, int power) +{ + int r; + + if (!pdev->power_save) + return; + + if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) + return; /* Not supported by Nala or Timon < release 6 */ + + if (power) + pdev->ctrl_buf[0] = 0x00; /* active */ + else + pdev->ctrl_buf[0] = 0xFF; /* power save */ + r = send_control_msg(pdev, SET_STATUS_CTL, + SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1); + if (r < 0) + PWC_ERROR("Failed to power %s camera (%d)\n", + power ? "on" : "off", r); +} + +int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) +{ + int r; + + if (pdev->type < 730) + return 0; + on_value /= 100; + off_value /= 100; + if (on_value < 0) + on_value = 0; + if (on_value > 0xff) + on_value = 0xff; + if (off_value < 0) + off_value = 0; + if (off_value > 0xff) + off_value = 0xff; + + pdev->ctrl_buf[0] = on_value; + pdev->ctrl_buf[1] = off_value; + + r = send_control_msg(pdev, + SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2); + if (r < 0) + PWC_ERROR("Failed to set LED on/off time (%d)\n", r); + + return r; +} + +#ifdef CONFIG_USB_PWC_DEBUG +int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) +{ + int ret = -1, request; + + if (pdev->type < 675) + request = SENSOR_TYPE_FORMATTER1; + else if (pdev->type < 730) + return -1; /* The Vesta series doesn't have this call */ + else + request = SENSOR_TYPE_FORMATTER2; + + ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1); + if (ret < 0) + return ret; + if (pdev->type < 675) + *sensor = pdev->ctrl_buf[0] | 0x100; + else + *sensor = pdev->ctrl_buf[0]; + return 0; +} +#endif diff --git a/drivers/media/usb/pwc/pwc-dec1.c b/drivers/media/usb/pwc/pwc-dec1.c new file mode 100644 index 000000000000..e899036aadf4 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-dec1.c @@ -0,0 +1,32 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 1 + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "pwc.h" + +void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd) +{ + struct pwc_dec1_private *pdec = &pdev->dec1; + + pdec->version = pdev->release; +} diff --git a/drivers/media/usb/pwc/pwc-dec1.h b/drivers/media/usb/pwc/pwc-dec1.h new file mode 100644 index 000000000000..c565ef8f52fb --- /dev/null +++ b/drivers/media/usb/pwc/pwc-dec1.h @@ -0,0 +1,39 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PWC_DEC1_H +#define PWC_DEC1_H + +#include + +struct pwc_device; + +struct pwc_dec1_private +{ + int version; +}; + +void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd); + +#endif diff --git a/drivers/media/usb/pwc/pwc-dec23.c b/drivers/media/usb/pwc/pwc-dec23.c new file mode 100644 index 000000000000..3792fedff951 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-dec23.c @@ -0,0 +1,691 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 2 et 3 + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "pwc-timon.h" +#include "pwc-kiara.h" +#include "pwc-dec23.h" + +#include +#include + +/* + * USE_LOOKUP_TABLE_TO_CLAMP + * 0: use a C version of this tests: { a<0?0:(a>255?255:a) } + * 1: use a faster lookup table for cpu with a big cache (intel) + */ +#define USE_LOOKUP_TABLE_TO_CLAMP 1 +/* + * UNROLL_LOOP_FOR_COPYING_BLOCK + * 0: use a loop for a smaller code (but little slower) + * 1: when unrolling the loop, gcc produces some faster code (perhaps only + * valid for intel processor class). Activating this option, automaticaly + * activate USE_LOOKUP_TABLE_TO_CLAMP + */ +#define UNROLL_LOOP_FOR_COPY 1 +#if UNROLL_LOOP_FOR_COPY +# undef USE_LOOKUP_TABLE_TO_CLAMP +# define USE_LOOKUP_TABLE_TO_CLAMP 1 +#endif + +static void build_subblock_pattern(struct pwc_dec23_private *pdec) +{ + static const unsigned int initial_values[12] = { + -0x526500, -0x221200, 0x221200, 0x526500, + -0x3de200, 0x3de200, + -0x6db480, -0x2d5d00, 0x2d5d00, 0x6db480, + -0x12c200, 0x12c200 + + }; + static const unsigned int values_derivated[12] = { + 0xa4ca, 0x4424, -0x4424, -0xa4ca, + 0x7bc4, -0x7bc4, + 0xdb69, 0x5aba, -0x5aba, -0xdb69, + 0x2584, -0x2584 + }; + unsigned int temp_values[12]; + int i, j; + + memcpy(temp_values, initial_values, sizeof(initial_values)); + for (i = 0; i < 256; i++) { + for (j = 0; j < 12; j++) { + pdec->table_subblock[i][j] = temp_values[j]; + temp_values[j] += values_derivated[j]; + } + } +} + +static void build_bit_powermask_table(struct pwc_dec23_private *pdec) +{ + unsigned char *p; + unsigned int bit, byte, mask, val; + unsigned int bitpower = 1; + + for (bit = 0; bit < 8; bit++) { + mask = bitpower - 1; + p = pdec->table_bitpowermask[bit]; + for (byte = 0; byte < 256; byte++) { + val = (byte & mask); + if (byte & bitpower) + val = -val; + *p++ = val; + } + bitpower<<=1; + } +} + + +static void build_table_color(const unsigned int romtable[16][8], + unsigned char p0004[16][1024], + unsigned char p8004[16][256]) +{ + int compression_mode, j, k, bit, pw; + unsigned char *p0, *p8; + const unsigned int *r; + + /* We have 16 compressions tables */ + for (compression_mode = 0; compression_mode < 16; compression_mode++) { + p0 = p0004[compression_mode]; + p8 = p8004[compression_mode]; + r = romtable[compression_mode]; + + for (j = 0; j < 8; j++, r++, p0 += 128) { + + for (k = 0; k < 16; k++) { + if (k == 0) + bit = 1; + else if (k >= 1 && k < 3) + bit = (r[0] >> 15) & 7; + else if (k >= 3 && k < 6) + bit = (r[0] >> 12) & 7; + else if (k >= 6 && k < 10) + bit = (r[0] >> 9) & 7; + else if (k >= 10 && k < 13) + bit = (r[0] >> 6) & 7; + else if (k >= 13 && k < 15) + bit = (r[0] >> 3) & 7; + else + bit = (r[0]) & 7; + if (k == 0) + *p8++ = 8; + else + *p8++ = j - bit; + *p8++ = bit; + + pw = 1 << bit; + p0[k + 0x00] = (1 * pw) + 0x80; + p0[k + 0x10] = (2 * pw) + 0x80; + p0[k + 0x20] = (3 * pw) + 0x80; + p0[k + 0x30] = (4 * pw) + 0x80; + p0[k + 0x40] = (-1 * pw) + 0x80; + p0[k + 0x50] = (-2 * pw) + 0x80; + p0[k + 0x60] = (-3 * pw) + 0x80; + p0[k + 0x70] = (-4 * pw) + 0x80; + } /* end of for (k=0; k<16; k++, p8++) */ + } /* end of for (j=0; j<8; j++ , table++) */ + } /* end of foreach compression_mode */ +} + +/* + * + */ +static void fill_table_dc00_d800(struct pwc_dec23_private *pdec) +{ +#define SCALEBITS 15 +#define ONE_HALF (1UL << (SCALEBITS - 1)) + int i; + unsigned int offset1 = ONE_HALF; + unsigned int offset2 = 0x0000; + + for (i=0; i<256; i++) { + pdec->table_dc00[i] = offset1 & ~(ONE_HALF); + pdec->table_d800[i] = offset2; + + offset1 += 0x7bc4; + offset2 += 0x7bc4; + } +} + +/* + * To decode the stream: + * if look_bits(2) == 0: # op == 2 in the lookup table + * skip_bits(2) + * end of the stream + * elif look_bits(3) == 7: # op == 1 in the lookup table + * skip_bits(3) + * yyyy = get_bits(4) + * xxxx = get_bits(8) + * else: # op == 0 in the lookup table + * skip_bits(x) + * + * For speedup processing, we build a lookup table and we takes the first 6 bits. + * + * struct { + * unsigned char op; // operation to execute + * unsigned char bits; // bits use to perform operation + * unsigned char offset1; // offset to add to access in the table_0004 % 16 + * unsigned char offset2; // offset to add to access in the table_0004 + * } + * + * How to build this table ? + * op == 2 when (i%4)==0 + * op == 1 when (i%8)==7 + * op == 0 otherwise + * + */ +static const unsigned char hash_table_ops[64*4] = { + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x01, 0x30, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x20, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x02, 0x10, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x60, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x40, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x40, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x01, 0x70, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x20, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x02, 0x50, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x60, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x40, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x40, + 0x01, 0x00, 0x00, 0x00 +}; + +/* + * + */ +static const unsigned int MulIdx[16][16] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,}, + {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,}, + {4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,}, + {6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6,}, + {4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4,}, + {1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2,}, + {0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3,}, + {0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3,}, + {1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2,}, + {7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7,}, + {4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4,}, + {7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7,}, + {1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2,}, + {1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2,}, + {10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10} +}; + +#if USE_LOOKUP_TABLE_TO_CLAMP +#define MAX_OUTER_CROP_VALUE (512) +static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; +#define CLAMP(x) (pwc_crop_table[MAX_OUTER_CROP_VALUE+(x)]) +#else +#define CLAMP(x) ((x)>255?255:((x)<0?0:x)) +#endif + + +/* If the type or the command change, we rebuild the lookup table */ +void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd) +{ + int flags, version, shift, i; + struct pwc_dec23_private *pdec = &pdev->dec23; + + mutex_init(&pdec->lock); + + if (pdec->last_cmd_valid && pdec->last_cmd == cmd[2]) + return; + + if (DEVICE_USE_CODEC3(pdev->type)) { + flags = cmd[2] & 0x18; + if (flags == 8) + pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */ + else if (flags == 0x10) + pdec->nbits = 8; + else + pdec->nbits = 6; + + version = cmd[2] >> 5; + build_table_color(KiaraRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); + build_table_color(KiaraRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); + + } else { + + flags = cmd[2] & 6; + if (flags == 2) + pdec->nbits = 7; + else if (flags == 4) + pdec->nbits = 8; + else + pdec->nbits = 6; + + version = cmd[2] >> 3; + build_table_color(TimonRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); + build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); + } + + /* Informations can be coded on a variable number of bits but never less than 8 */ + shift = 8 - pdec->nbits; + pdec->scalebits = SCALEBITS - shift; + pdec->nbitsmask = 0xFF >> shift; + + fill_table_dc00_d800(pdec); + build_subblock_pattern(pdec); + build_bit_powermask_table(pdec); + +#if USE_LOOKUP_TABLE_TO_CLAMP + /* Build the static table to clamp value [0-255] */ + for (i=0;ilast_cmd = cmd[2]; + pdec->last_cmd_valid = 1; +} + +/* + * Copy the 4x4 image block to Y plane buffer + */ +static void copy_image_block_Y(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + const int *c = src; + unsigned char *d = dst; + + *d++ = cm[c[0] >> scalebits]; + *d++ = cm[c[1] >> scalebits]; + *d++ = cm[c[2] >> scalebits]; + *d++ = cm[c[3] >> scalebits]; + + d = dst + bytes_per_line; + *d++ = cm[c[4] >> scalebits]; + *d++ = cm[c[5] >> scalebits]; + *d++ = cm[c[6] >> scalebits]; + *d++ = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line*2; + *d++ = cm[c[8] >> scalebits]; + *d++ = cm[c[9] >> scalebits]; + *d++ = cm[c[10] >> scalebits]; + *d++ = cm[c[11] >> scalebits]; + + d = dst + bytes_per_line*3; + *d++ = cm[c[12] >> scalebits]; + *d++ = cm[c[13] >> scalebits]; + *d++ = cm[c[14] >> scalebits]; + *d++ = cm[c[15] >> scalebits]; +#else + int i; + const int *c = src; + unsigned char *d = dst; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*2; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*3; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); +#endif +} + +/* + * Copy the 4x4 image block to a CrCb plane buffer + * + */ +static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + /* Unroll all loops */ + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + const int *c = src; + unsigned char *d = dst; + + *d++ = cm[c[0] >> scalebits]; + *d++ = cm[c[4] >> scalebits]; + *d++ = cm[c[1] >> scalebits]; + *d++ = cm[c[5] >> scalebits]; + *d++ = cm[c[2] >> scalebits]; + *d++ = cm[c[6] >> scalebits]; + *d++ = cm[c[3] >> scalebits]; + *d++ = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line; + *d++ = cm[c[12] >> scalebits]; + *d++ = cm[c[8] >> scalebits]; + *d++ = cm[c[13] >> scalebits]; + *d++ = cm[c[9] >> scalebits]; + *d++ = cm[c[14] >> scalebits]; + *d++ = cm[c[10] >> scalebits]; + *d++ = cm[c[15] >> scalebits]; + *d++ = cm[c[11] >> scalebits]; +#else + int i; + const int *c1 = src; + const int *c2 = src + 4; + unsigned char *d = dst; + + for (i = 0; i < 4; i++, c1++, c2++) { + *d++ = CLAMP((*c1) >> scalebits); + *d++ = CLAMP((*c2) >> scalebits); + } + c1 = src + 12; + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c1++, c2++) { + *d++ = CLAMP((*c1) >> scalebits); + *d++ = CLAMP((*c2) >> scalebits); + } +#endif +} + +/* + * To manage the stream, we keep bits in a 32 bits register. + * fill_nbits(n): fill the reservoir with at least n bits + * skip_bits(n): discard n bits from the reservoir + * get_bits(n): fill the reservoir, returns the first n bits and discard the + * bits from the reservoir. + * __get_nbits(n): faster version of get_bits(n), but asumes that the reservoir + * contains at least n bits. bits returned is discarded. + */ +#define fill_nbits(pdec, nbits_wanted) do { \ + while (pdec->nbits_in_reservoir<(nbits_wanted)) \ + { \ + pdec->reservoir |= (*(pdec->stream)++) << (pdec->nbits_in_reservoir); \ + pdec->nbits_in_reservoir += 8; \ + } \ +} while(0); + +#define skip_nbits(pdec, nbits_to_skip) do { \ + pdec->reservoir >>= (nbits_to_skip); \ + pdec->nbits_in_reservoir -= (nbits_to_skip); \ +} while(0); + +#define get_nbits(pdec, nbits_wanted, result) do { \ + fill_nbits(pdec, nbits_wanted); \ + result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ + skip_nbits(pdec, nbits_wanted); \ +} while(0); + +#define __get_nbits(pdec, nbits_wanted, result) do { \ + result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ + skip_nbits(pdec, nbits_wanted); \ +} while(0); + +#define look_nbits(pdec, nbits_wanted) \ + ((pdec->reservoir) & ((1U<<(nbits_wanted))-1)) + +/* + * Decode a 4x4 pixel block + */ +static void decode_block(struct pwc_dec23_private *pdec, + const unsigned char *ptable0004, + const unsigned char *ptable8004) +{ + unsigned int primary_color; + unsigned int channel_v, offset1, op; + int i; + + fill_nbits(pdec, 16); + __get_nbits(pdec, pdec->nbits, primary_color); + + if (look_nbits(pdec,2) == 0) { + skip_nbits(pdec, 2); + /* Very simple, the color is the same for all pixels of the square */ + for (i = 0; i < 16; i++) + pdec->temp_colors[i] = pdec->table_dc00[primary_color]; + + return; + } + + /* This block is encoded with small pattern */ + for (i = 0; i < 16; i++) + pdec->temp_colors[i] = pdec->table_d800[primary_color]; + + __get_nbits(pdec, 3, channel_v); + channel_v = ((channel_v & 1) << 2) | (channel_v & 2) | ((channel_v & 4) >> 2); + + ptable0004 += (channel_v * 128); + ptable8004 += (channel_v * 32); + + offset1 = 0; + do + { + unsigned int htable_idx, rows = 0; + const unsigned int *block; + + /* [ zzzz y x x ] + * xx == 00 :=> end of the block def, remove the two bits from the stream + * yxx == 111 + * yxx == any other value + * + */ + fill_nbits(pdec, 16); + htable_idx = look_nbits(pdec, 6); + op = hash_table_ops[htable_idx * 4]; + + if (op == 2) { + skip_nbits(pdec, 2); + + } else if (op == 1) { + /* 15bits [ xxxx xxxx yyyy 111 ] + * yyy => offset in the table8004 + * xxx => offset in the tabled004 (tree) + */ + unsigned int mask, shift; + unsigned int nbits, col1; + unsigned int yyyy; + + skip_nbits(pdec, 3); + /* offset1 += yyyy */ + __get_nbits(pdec, 4, yyyy); + offset1 += 1 + yyyy; + offset1 &= 0x0F; + nbits = ptable8004[offset1 * 2]; + + /* col1 = xxxx xxxx */ + __get_nbits(pdec, nbits+1, col1); + + /* Bit mask table */ + mask = pdec->table_bitpowermask[nbits][col1]; + shift = ptable8004[offset1 * 2 + 1]; + rows = ((mask << shift) + 0x80) & 0xFF; + + block = pdec->table_subblock[rows]; + for (i = 0; i < 16; i++) + pdec->temp_colors[i] += block[MulIdx[offset1][i]]; + + } else { + /* op == 0 + * offset1 is coded on 3 bits + */ + unsigned int shift; + + offset1 += hash_table_ops [htable_idx * 4 + 2]; + offset1 &= 0x0F; + + rows = ptable0004[offset1 + hash_table_ops [htable_idx * 4 + 3]]; + block = pdec->table_subblock[rows]; + for (i = 0; i < 16; i++) + pdec->temp_colors[i] += block[MulIdx[offset1][i]]; + + shift = hash_table_ops[htable_idx * 4 + 1]; + skip_nbits(pdec, shift); + } + + } while (op != 2); + +} + +static void DecompressBand23(struct pwc_dec23_private *pdec, + const unsigned char *rawyuv, + unsigned char *planar_y, + unsigned char *planar_u, + unsigned char *planar_v, + unsigned int compressed_image_width, + unsigned int real_image_width) +{ + int compression_index, nblocks; + const unsigned char *ptable0004; + const unsigned char *ptable8004; + + pdec->reservoir = 0; + pdec->nbits_in_reservoir = 0; + pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ + + get_nbits(pdec, 4, compression_index); + + /* pass 1: uncompress Y component */ + nblocks = compressed_image_width / 4; + + ptable0004 = pdec->table_0004_pass1[compression_index]; + ptable8004 = pdec->table_8004_pass1[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_Y(pdec->temp_colors, planar_y, real_image_width, pdec->scalebits); + planar_y += 4; + nblocks--; + } + + /* pass 2: uncompress UV component */ + nblocks = compressed_image_width / 8; + + ptable0004 = pdec->table_0004_pass2[compression_index]; + ptable8004 = pdec->table_8004_pass2[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_CrCb(pdec->temp_colors, planar_u, real_image_width/2, pdec->scalebits); + + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_CrCb(pdec->temp_colors, planar_v, real_image_width/2, pdec->scalebits); + + planar_v += 8; + planar_u += 8; + nblocks -= 2; + } + +} + +/** + * + * Uncompress a pwc23 buffer. + * + * src: raw data + * dst: image output + */ +void pwc_dec23_decompress(struct pwc_device *pdev, + const void *src, + void *dst) +{ + int bandlines_left, bytes_per_block; + struct pwc_dec23_private *pdec = &pdev->dec23; + + /* YUV420P image format */ + unsigned char *pout_planar_y; + unsigned char *pout_planar_u; + unsigned char *pout_planar_v; + unsigned int plane_size; + + mutex_lock(&pdec->lock); + + bandlines_left = pdev->height / 4; + bytes_per_block = pdev->width * 4; + plane_size = pdev->height * pdev->width; + + pout_planar_y = dst; + pout_planar_u = dst + plane_size; + pout_planar_v = dst + plane_size + plane_size / 4; + + while (bandlines_left--) { + DecompressBand23(pdec, src, + pout_planar_y, pout_planar_u, pout_planar_v, + pdev->width, pdev->width); + src += pdev->vbandlength; + pout_planar_y += bytes_per_block; + pout_planar_u += pdev->width; + pout_planar_v += pdev->width; + } + mutex_unlock(&pdec->lock); +} diff --git a/drivers/media/usb/pwc/pwc-dec23.h b/drivers/media/usb/pwc/pwc-dec23.h new file mode 100644 index 000000000000..c655b1c1e6a9 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-dec23.h @@ -0,0 +1,61 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PWC_DEC23_H +#define PWC_DEC23_H + +struct pwc_device; + +struct pwc_dec23_private +{ + struct mutex lock; + + unsigned char last_cmd, last_cmd_valid; + + unsigned int scalebits; + unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */ + + unsigned int reservoir; + unsigned int nbits_in_reservoir; + + const unsigned char *stream; + int temp_colors[16]; + + unsigned char table_0004_pass1[16][1024]; + unsigned char table_0004_pass2[16][1024]; + unsigned char table_8004_pass1[16][256]; + unsigned char table_8004_pass2[16][256]; + unsigned int table_subblock[256][12]; + + unsigned char table_bitpowermask[8][256]; + unsigned int table_d800[256]; + unsigned int table_dc00[256]; + +}; + +void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd); +void pwc_dec23_decompress(struct pwc_device *pdev, + const void *src, + void *dst); +#endif diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c new file mode 100644 index 000000000000..de7c7ba99ef4 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-if.c @@ -0,0 +1,1165 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + (C) 2011 Hans de Goede + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* + This code forms the interface between the USB layers and the Philips + specific stuff. Some adanved stuff of the driver falls under an + NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and + is thus not distributed in source form. The binary pwcx.o module + contains the code that falls under the NDA. + + In case you're wondering: 'pwc' stands for "Philips WebCam", but + I really didn't want to type 'philips_web_cam' every time (I'm lazy as + any Linux kernel hacker, but I don't like uncomprehensible abbreviations + without explanation). + + Oh yes, convention: to disctinguish between all the various pointers to + device-structures, I use these names for the pointer variables: + udev: struct usb_device * + vdev: struct video_device (member of pwc_dev) + pdev: struct pwc_devive * +*/ + +/* Contributors: + - Alvarado: adding whitebalance code + - Alistar Moire: QuickCam 3000 Pro device/product ID + - Tony Hoyle: Creative Labs Webcam 5 device/product ID + - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged + - Jk Fang: Sotec Afina Eye ID + - Xavier Roche: QuickCam Pro 4000 ID + - Jens Knudsen: QuickCam Zoom ID + - J. Debert: QuickCam for Notebooks ID + - Pham Thanh Nam: webcam snapshot button as an event input device +*/ + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_USB_PWC_INPUT_EVDEV +#include +#endif +#include +#include +#include /* simple_strtol() */ + +#include "pwc.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-dec23.h" +#include "pwc-dec1.h" + +/* Function prototypes and driver templates */ + +/* hotplug device table support */ +static const struct usb_device_id pwc_device_table [] = { + { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ + { USB_DEVICE(0x0471, 0x0303) }, + { USB_DEVICE(0x0471, 0x0304) }, + { USB_DEVICE(0x0471, 0x0307) }, + { USB_DEVICE(0x0471, 0x0308) }, + { USB_DEVICE(0x0471, 0x030C) }, + { USB_DEVICE(0x0471, 0x0310) }, + { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */ + { USB_DEVICE(0x0471, 0x0312) }, + { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ + { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */ + { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ + { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ + { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ + { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ + { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ + { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ + { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */ + { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ + { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ + { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */ + { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ + { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ + { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ + { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ + { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ + { USB_DEVICE(0x0d81, 0x1900) }, + { } +}; +MODULE_DEVICE_TABLE(usb, pwc_device_table); + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); +static void usb_pwc_disconnect(struct usb_interface *intf); +static void pwc_isoc_cleanup(struct pwc_device *pdev); + +static struct usb_driver pwc_driver = { + .name = "Philips webcam", /* name */ + .id_table = pwc_device_table, + .probe = usb_pwc_probe, /* probe() */ + .disconnect = usb_pwc_disconnect, /* disconnect() */ +}; + +#define MAX_DEV_HINTS 20 +#define MAX_ISOC_ERRORS 20 + +#ifdef CONFIG_USB_PWC_DEBUG + int pwc_trace = PWC_DEBUG_LEVEL; +#endif +static int power_save = -1; +static int leds[2] = { 100, 0 }; + +/***/ + +static const struct v4l2_file_operations pwc_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; +static struct video_device pwc_template = { + .name = "Philips Webcam", /* Filled in later */ + .release = video_device_release_empty, + .fops = &pwc_fops, + .ioctl_ops = &pwc_ioctl_ops, +}; + +/***************************************************************************/ +/* Private functions */ + +struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev) +{ + unsigned long flags = 0; + struct pwc_frame_buf *buf = NULL; + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); + if (list_empty(&pdev->queued_bufs)) + goto leave; + + buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); + return buf; +} + +static void pwc_snapshot_button(struct pwc_device *pdev, int down) +{ + if (down) { + PWC_TRACE("Snapshot button pressed.\n"); + } else { + PWC_TRACE("Snapshot button released.\n"); + } + +#ifdef CONFIG_USB_PWC_INPUT_EVDEV + if (pdev->button_dev) { + input_report_key(pdev->button_dev, KEY_CAMERA, down); + input_sync(pdev->button_dev); + } +#endif +} + +static void pwc_frame_complete(struct pwc_device *pdev) +{ + struct pwc_frame_buf *fbuf = pdev->fill_buf; + + /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus + frames on the USB wire after an exposure change. This conditition is + however detected in the cam and a bit is set in the header. + */ + if (pdev->type == 730) { + unsigned char *ptr = (unsigned char *)fbuf->data; + + if (ptr[1] == 1 && ptr[0] & 0x10) { + PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n"); + pdev->drop_frames += 2; + } + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + pwc_snapshot_button(pdev, ptr[0] & 0x01); + } + if ((ptr[0] ^ pdev->vmirror) & 0x02) { + if (ptr[0] & 0x02) + PWC_TRACE("Image is mirrored.\n"); + else + PWC_TRACE("Image is normal.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + /* Sometimes the trailer of the 730 is still sent as a 4 byte packet + after a short frame; this condition is filtered out specifically. A 4 byte + frame doesn't make sense anyway. + So we get either this sequence: + drop_bit set -> 4 byte frame -> short frame -> good frame + Or this one: + drop_bit set -> short frame -> good frame + So we drop either 3 or 2 frames in all! + */ + if (fbuf->filled == 4) + pdev->drop_frames++; + } else if (pdev->type == 740 || pdev->type == 720) { + unsigned char *ptr = (unsigned char *)fbuf->data; + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + pwc_snapshot_button(pdev, ptr[0] & 0x01); + } + pdev->vmirror = ptr[0] & 0x03; + } + + /* In case we were instructed to drop the frame, do so silently. */ + if (pdev->drop_frames > 0) { + pdev->drop_frames--; + } else { + /* Check for underflow first */ + if (fbuf->filled < pdev->frame_total_size) { + PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);" + " discarded.\n", fbuf->filled); + } else { + fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE; + fbuf->vb.v4l2_buf.sequence = pdev->vframe_count; + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + pdev->fill_buf = NULL; + pdev->vsync = 0; + } + } /* !drop_frames */ + pdev->vframe_count++; +} + +/* This gets called for the Isochronous pipe (video). This is done in + * interrupt time, so it has to be fast, not crash, and not stall. Neat. + */ +static void pwc_isoc_handler(struct urb *urb) +{ + struct pwc_device *pdev = (struct pwc_device *)urb->context; + int i, fst, flen; + unsigned char *iso_buf = NULL; + + if (urb->status == -ENOENT || urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN) { + PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); + return; + } + + if (pdev->fill_buf == NULL) + pdev->fill_buf = pwc_get_next_fill_buf(pdev); + + if (urb->status != 0) { + const char *errmsg; + + errmsg = "Unknown"; + switch(urb->status) { + case -ENOSR: errmsg = "Buffer error (overrun)"; break; + case -EPIPE: errmsg = "Stalled (device not responding)"; break; + case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; + case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; + case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; + case -ETIME: errmsg = "Device does not respond"; break; + } + PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n", + urb->status, errmsg); + /* Give up after a number of contiguous errors */ + if (++pdev->visoc_errors > MAX_ISOC_ERRORS) + { + PWC_ERROR("Too many ISOC errors, bailing out.\n"); + if (pdev->fill_buf) { + vb2_buffer_done(&pdev->fill_buf->vb, + VB2_BUF_STATE_ERROR); + pdev->fill_buf = NULL; + } + } + pdev->vsync = 0; /* Drop the current frame */ + goto handler_end; + } + + /* Reset ISOC error counter. We did get here, after all. */ + pdev->visoc_errors = 0; + + /* vsync: 0 = don't copy data + 1 = sync-hunt + 2 = synched + */ + /* Compact data */ + for (i = 0; i < urb->number_of_packets; i++) { + fst = urb->iso_frame_desc[i].status; + flen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (fst != 0) { + PWC_ERROR("Iso frame %d has error %d\n", i, fst); + continue; + } + if (flen > 0 && pdev->vsync) { + struct pwc_frame_buf *fbuf = pdev->fill_buf; + + if (pdev->vsync == 1) { + do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp); + pdev->vsync = 2; + } + + if (flen + fbuf->filled > pdev->frame_total_size) { + PWC_ERROR("Frame overflow (%d > %d)\n", + flen + fbuf->filled, + pdev->frame_total_size); + pdev->vsync = 0; /* Let's wait for an EOF */ + } else { + memcpy(fbuf->data + fbuf->filled, iso_buf, + flen); + fbuf->filled += flen; + } + } + if (flen < pdev->vlast_packet_size) { + /* Shorter packet... end of frame */ + if (pdev->vsync == 2) + pwc_frame_complete(pdev); + if (pdev->fill_buf == NULL) + pdev->fill_buf = pwc_get_next_fill_buf(pdev); + if (pdev->fill_buf) { + pdev->fill_buf->filled = 0; + pdev->vsync = 1; + } + } + pdev->vlast_packet_size = flen; + } + +handler_end: + i = usb_submit_urb(urb, GFP_ATOMIC); + if (i != 0) + PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); +} + +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ +static int pwc_isoc_init(struct pwc_device *pdev) +{ + struct usb_device *udev; + struct urb *urb; + int i, j, ret; + struct usb_interface *intf; + struct usb_host_interface *idesc = NULL; + int compression = 0; /* 0..3 = uncompressed..high */ + + pdev->vsync = 0; + pdev->vlast_packet_size = 0; + pdev->fill_buf = NULL; + pdev->vframe_count = 0; + pdev->visoc_errors = 0; + udev = pdev->udev; + +retry: + /* We first try with low compression and then retry with a higher + compression setting if there is not enough bandwidth. */ + ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, + pdev->vframes, &compression, 1); + + /* Get the current alternate interface, adjust packet size */ + intf = usb_ifnum_to_if(udev, 0); + if (intf) + idesc = usb_altnum_to_altsetting(intf, pdev->valternate); + if (!idesc) + return -EIO; + + /* Search video endpoint */ + pdev->vmax_packet_size = -1; + for (i = 0; i < idesc->desc.bNumEndpoints; i++) { + if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { + pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize); + break; + } + } + + if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { + PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n"); + return -ENFILE; /* Odd error, that should be noticeable */ + } + + /* Set alternate interface */ + PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate); + ret = usb_set_interface(pdev->udev, 0, pdev->valternate); + if (ret == -ENOSPC && compression < 3) { + compression++; + goto retry; + } + if (ret < 0) + return ret; + + /* Allocate and init Isochronuous urbs */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + PWC_ERROR("Failed to allocate urb %d\n", i); + pwc_isoc_cleanup(pdev); + return -ENOMEM; + } + pdev->urbs[i] = urb; + PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb); + + urb->interval = 1; // devik + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_buffer = usb_alloc_coherent(udev, + ISO_BUFFER_SIZE, + GFP_KERNEL, + &urb->transfer_dma); + if (urb->transfer_buffer == NULL) { + PWC_ERROR("Failed to allocate urb buffer %d\n", i); + pwc_isoc_cleanup(pdev); + return -ENOMEM; + } + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = pwc_isoc_handler; + urb->context = pdev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = pdev->vmax_packet_size; + } + } + + /* link */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL); + if (ret == -ENOSPC && compression < 3) { + compression++; + pwc_isoc_cleanup(pdev); + goto retry; + } + if (ret) { + PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); + pwc_isoc_cleanup(pdev); + return ret; + } + PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]); + } + + /* All is done... */ + PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); + return 0; +} + +static void pwc_iso_stop(struct pwc_device *pdev) +{ + int i; + + /* Unlinking ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (pdev->urbs[i]) { + PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]); + usb_kill_urb(pdev->urbs[i]); + } + } +} + +static void pwc_iso_free(struct pwc_device *pdev) +{ + int i; + + /* Freeing ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (pdev->urbs[i]) { + PWC_DEBUG_MEMORY("Freeing URB\n"); + if (pdev->urbs[i]->transfer_buffer) { + usb_free_coherent(pdev->udev, + pdev->urbs[i]->transfer_buffer_length, + pdev->urbs[i]->transfer_buffer, + pdev->urbs[i]->transfer_dma); + } + usb_free_urb(pdev->urbs[i]); + pdev->urbs[i] = NULL; + } + } +} + +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ +static void pwc_isoc_cleanup(struct pwc_device *pdev) +{ + PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); + + pwc_iso_stop(pdev); + pwc_iso_free(pdev); + usb_set_interface(pdev->udev, 0, 0); + + PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); +} + +/* Must be called with vb_queue_lock hold */ +static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); + while (!list_empty(&pdev->queued_bufs)) { + struct pwc_frame_buf *buf; + + buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, + list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); +} + +#ifdef CONFIG_USB_PWC_DEBUG +static const char *pwc_sensor_type_to_string(unsigned int sensor_type) +{ + switch(sensor_type) { + case 0x00: + return "Hyundai CMOS sensor"; + case 0x20: + return "Sony CCD sensor + TDA8787"; + case 0x2E: + return "Sony CCD sensor + Exas 98L59"; + case 0x2F: + return "Sony CCD sensor + ADI 9804"; + case 0x30: + return "Sharp CCD sensor + TDA8787"; + case 0x3E: + return "Sharp CCD sensor + Exas 98L59"; + case 0x3F: + return "Sharp CCD sensor + ADI 9804"; + case 0x40: + return "UPA 1021 sensor"; + case 0x100: + return "VGA sensor"; + case 0x101: + return "PAL MR sensor"; + default: + return "unknown type of sensor"; + } +} +#endif + +/***************************************************************************/ +/* Video4Linux functions */ + +static void pwc_video_release(struct v4l2_device *v) +{ + struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); + + v4l2_ctrl_handler_free(&pdev->ctrl_handler); + v4l2_device_unregister(&pdev->v4l2_dev); + kfree(pdev->ctrl_buf); + kfree(pdev); +} + +/***************************************************************************/ +/* Videobuf2 operations */ + +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + int size; + + if (*nbuffers < MIN_FRAMES) + *nbuffers = MIN_FRAMES; + else if (*nbuffers > MAX_FRAMES) + *nbuffers = MAX_FRAMES; + + *nplanes = 1; + + size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT); + sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] * + pwc_image_sizes[size][1] * 3 / 2); + + return 0; +} + +static int buffer_init(struct vb2_buffer *vb) +{ + struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + + /* need vmalloc since frame buffer > 128K */ + buf->data = vzalloc(PWC_FRAME_SIZE); + if (buf->data == NULL) + return -ENOMEM; + + return 0; +} + +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); + + /* Don't allow queing new buffers after device disconnection */ + if (!pdev->udev) + return -ENODEV; + + return 0; +} + +static int buffer_finish(struct vb2_buffer *vb) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); + struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + + /* + * Application has called dqbuf and is getting back a buffer we've + * filled, take the pwc data we've stored in buf->data and decompress + * it into a usable format, storing the result in the vb2_buffer + */ + return pwc_decompress(pdev, buf); +} + +static void buffer_cleanup(struct vb2_buffer *vb) +{ + struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + + vfree(buf->data); +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); + struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + unsigned long flags = 0; + + /* Check the device has not disconnected between prep and queuing */ + if (!pdev->udev) { + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &pdev->queued_bufs); + spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); +} + +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + int r; + + if (!pdev->udev) + return -ENODEV; + + if (mutex_lock_interruptible(&pdev->v4l2_lock)) + return -ERESTARTSYS; + /* Turn on camera and set LEDS on */ + pwc_camera_power(pdev, 1); + pwc_set_leds(pdev, leds[0], leds[1]); + + r = pwc_isoc_init(pdev); + if (r) { + /* If we failed turn camera and LEDS back off */ + pwc_set_leds(pdev, 0, 0); + pwc_camera_power(pdev, 0); + /* And cleanup any queued bufs!! */ + pwc_cleanup_queued_bufs(pdev); + } + mutex_unlock(&pdev->v4l2_lock); + + return r; +} + +static int stop_streaming(struct vb2_queue *vq) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + + if (mutex_lock_interruptible(&pdev->v4l2_lock)) + return -ERESTARTSYS; + if (pdev->udev) { + pwc_set_leds(pdev, 0, 0); + pwc_camera_power(pdev, 0); + pwc_isoc_cleanup(pdev); + } + + pwc_cleanup_queued_bufs(pdev); + mutex_unlock(&pdev->v4l2_lock); + + return 0; +} + +static struct vb2_ops pwc_vb_queue_ops = { + .queue_setup = queue_setup, + .buf_init = buffer_init, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_cleanup = buffer_cleanup, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/***************************************************************************/ +/* USB functions */ + +/* This function gets called when a new device is plugged in or the usb core + * is loaded. + */ + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct pwc_device *pdev = NULL; + int vendor_id, product_id, type_id; + int rc; + int features = 0; + int compression = 0; + int my_power_save = power_save; + char serial_number[30], *name; + + vendor_id = le16_to_cpu(udev->descriptor.idVendor); + product_id = le16_to_cpu(udev->descriptor.idProduct); + + /* Check if we can handle this device */ + PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n", + vendor_id, product_id, + intf->altsetting->desc.bInterfaceNumber); + + /* the interfaces are probed one by one. We are only interested in the + video interface (0) now. + Interface 1 is the Audio Control, and interface 2 Audio itself. + */ + if (intf->altsetting->desc.bInterfaceNumber > 0) + return -ENODEV; + + if (vendor_id == 0x0471) { + switch (product_id) { + case 0x0302: + PWC_INFO("Philips PCA645VC USB webcam detected.\n"); + name = "Philips 645 webcam"; + type_id = 645; + break; + case 0x0303: + PWC_INFO("Philips PCA646VC USB webcam detected.\n"); + name = "Philips 646 webcam"; + type_id = 646; + break; + case 0x0304: + PWC_INFO("Askey VC010 type 2 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 646; + break; + case 0x0307: + PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n"); + name = "Philips 675 webcam"; + type_id = 675; + break; + case 0x0308: + PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); + name = "Philips 680 webcam"; + type_id = 680; + break; + case 0x030C: + PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); + name = "Philips 690 webcam"; + type_id = 690; + break; + case 0x0310: + PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); + name = "Philips 730 webcam"; + type_id = 730; + break; + case 0x0311: + PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); + name = "Philips 740 webcam"; + type_id = 740; + break; + case 0x0312: + PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); + name = "Philips 750 webcam"; + type_id = 750; + break; + case 0x0313: + PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + name = "Philips 720K/40 webcam"; + type_id = 720; + break; + case 0x0329: + PWC_INFO("Philips SPC 900NC USB webcam detected.\n"); + name = "Philips SPC 900NC webcam"; + type_id = 740; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x069A) { + switch(product_id) { + case 0x0001: + PWC_INFO("Askey VC010 type 1 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 645; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x046d) { + switch(product_id) { + case 0x08b0: + PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n"); + name = "Logitech QuickCam Pro 3000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b1: + PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n"); + name = "Logitech QuickCam Notebook Pro"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b2: + PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n"); + name = "Logitech QuickCam Pro 4000"; + type_id = 740; /* CCD sensor */ + if (my_power_save == -1) + my_power_save = 1; + break; + case 0x08b3: + PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08B4: + PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + if (my_power_save == -1) + my_power_save = 1; + break; + case 0x08b5: + PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + name = "Logitech QuickCam Orbit"; + type_id = 740; /* CCD sensor */ + if (my_power_save == -1) + my_power_save = 1; + features |= FEATURE_MOTOR_PANTILT; + break; + case 0x08b6: + PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n"); + name = "Cisco VT Camera"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b7: + PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n"); + name = "Logitech ViewPort AV 100"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b8: /* Where this released? */ + PWC_INFO("Logitech QuickCam detected (reserved ID).\n"); + name = "Logitech QuickCam (res.)"; + type_id = 730; /* Assuming CMOS */ + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x055d) { + /* I don't know the difference between the C10 and the C30; + I suppose the difference is the sensor, but both cameras + work equally well with a type_id of 675 + */ + switch(product_id) { + case 0x9000: + PWC_INFO("Samsung MPC-C10 USB webcam detected.\n"); + name = "Samsung MPC-C10"; + type_id = 675; + break; + case 0x9001: + PWC_INFO("Samsung MPC-C30 USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 675; + break; + case 0x9002: + PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 740; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x041e) { + switch(product_id) { + case 0x400c: + PWC_INFO("Creative Labs Webcam 5 detected.\n"); + name = "Creative Labs Webcam 5"; + type_id = 730; + if (my_power_save == -1) + my_power_save = 1; + break; + case 0x4011: + PWC_INFO("Creative Labs Webcam Pro Ex detected.\n"); + name = "Creative Labs Webcam Pro Ex"; + type_id = 740; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x04cc) { + switch(product_id) { + case 0x8116: + PWC_INFO("Sotec Afina Eye USB webcam detected.\n"); + name = "Sotec Afina Eye"; + type_id = 730; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x06be) { + switch(product_id) { + case 0x8116: + /* This is essentially the same cam as the Sotec Afina Eye */ + PWC_INFO("AME Co. Afina Eye USB webcam detected.\n"); + name = "AME Co. Afina Eye"; + type_id = 750; + break; + default: + return -ENODEV; + break; + } + + } + else if (vendor_id == 0x0d81) { + switch(product_id) { + case 0x1900: + PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n"); + name = "Visionite VCS-UC300"; + type_id = 740; /* CCD sensor */ + break; + case 0x1910: + PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n"); + name = "Visionite VCS-UM100"; + type_id = 730; /* CMOS sensor */ + break; + default: + return -ENODEV; + break; + } + } + else + return -ENODEV; /* Not any of the know types; but the list keeps growing. */ + + if (my_power_save == -1) + my_power_save = 0; + + memset(serial_number, 0, 30); + usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); + PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number); + + if (udev->descriptor.bNumConfigurations > 1) + PWC_WARNING("Warning: more than 1 configuration available.\n"); + + /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ + pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); + if (pdev == NULL) { + PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); + return -ENOMEM; + } + pdev->type = type_id; + pdev->features = features; + pwc_construct(pdev); /* set min/max sizes correct */ + + mutex_init(&pdev->v4l2_lock); + mutex_init(&pdev->vb_queue_lock); + spin_lock_init(&pdev->queued_bufs_lock); + INIT_LIST_HEAD(&pdev->queued_bufs); + + pdev->udev = udev; + pdev->power_save = my_power_save; + + /* Init videobuf2 queue structure */ + memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue)); + pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + pdev->vb_queue.drv_priv = pdev; + pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf); + pdev->vb_queue.ops = &pwc_vb_queue_ops; + pdev->vb_queue.mem_ops = &vb2_vmalloc_memops; + vb2_queue_init(&pdev->vb_queue); + + /* Init video_device structure */ + memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); + strcpy(pdev->vdev.name, name); + pdev->vdev.queue = &pdev->vb_queue; + pdev->vdev.queue->lock = &pdev->vb_queue_lock; + set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags); + video_set_drvdata(&pdev->vdev, pdev); + + pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); + PWC_DEBUG_PROBE("Release: %04x\n", pdev->release); + + /* Allocate USB command buffers */ + pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL); + if (!pdev->ctrl_buf) { + PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); + rc = -ENOMEM; + goto err_free_mem; + } + +#ifdef CONFIG_USB_PWC_DEBUG + /* Query sensor type */ + if (pwc_get_cmos_sensor(pdev, &rc) >= 0) { + PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n", + pdev->vdev.name, + pwc_sensor_type_to_string(rc), rc); + } +#endif + + /* Set the leds off */ + pwc_set_leds(pdev, 0, 0); + + /* Setup intial videomode */ + rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, + V4L2_PIX_FMT_YUV420, 30, &compression, 1); + if (rc) + goto err_free_mem; + + /* Register controls (and read default values from camera */ + rc = pwc_init_controls(pdev); + if (rc) { + PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc); + goto err_free_mem; + } + + /* And powerdown the camera until streaming starts */ + pwc_camera_power(pdev, 0); + + /* Register the v4l2_device structure */ + pdev->v4l2_dev.release = pwc_video_release; + rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev); + if (rc) { + PWC_ERROR("Failed to register v4l2-device (%d).\n", rc); + goto err_free_controls; + } + + pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; + pdev->vdev.v4l2_dev = &pdev->v4l2_dev; + pdev->vdev.lock = &pdev->v4l2_lock; + + rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); + if (rc < 0) { + PWC_ERROR("Failed to register as video device (%d).\n", rc); + goto err_unregister_v4l2_dev; + } + PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev)); + +#ifdef CONFIG_USB_PWC_INPUT_EVDEV + /* register webcam snapshot button input device */ + pdev->button_dev = input_allocate_device(); + if (!pdev->button_dev) { + PWC_ERROR("Err, insufficient memory for webcam snapshot button device."); + rc = -ENOMEM; + goto err_video_unreg; + } + + usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys)); + strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys)); + + pdev->button_dev->name = "PWC snapshot button"; + pdev->button_dev->phys = pdev->button_phys; + usb_to_input_id(pdev->udev, &pdev->button_dev->id); + pdev->button_dev->dev.parent = &pdev->udev->dev; + pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY); + pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); + + rc = input_register_device(pdev->button_dev); + if (rc) { + input_free_device(pdev->button_dev); + pdev->button_dev = NULL; + goto err_video_unreg; + } +#endif + + return 0; + +err_video_unreg: + video_unregister_device(&pdev->vdev); +err_unregister_v4l2_dev: + v4l2_device_unregister(&pdev->v4l2_dev); +err_free_controls: + v4l2_ctrl_handler_free(&pdev->ctrl_handler); +err_free_mem: + kfree(pdev->ctrl_buf); + kfree(pdev); + return rc; +} + +/* The user yanked out the cable... */ +static void usb_pwc_disconnect(struct usb_interface *intf) +{ + struct v4l2_device *v = usb_get_intfdata(intf); + struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); + + mutex_lock(&pdev->vb_queue_lock); + mutex_lock(&pdev->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + if (pdev->vb_queue.streaming) + pwc_isoc_cleanup(pdev); + pdev->udev = NULL; + pwc_cleanup_queued_bufs(pdev); + + v4l2_device_disconnect(&pdev->v4l2_dev); + video_unregister_device(&pdev->vdev); + mutex_unlock(&pdev->v4l2_lock); + mutex_unlock(pdev->vb_queue.lock); + +#ifdef CONFIG_USB_PWC_INPUT_EVDEV + if (pdev->button_dev) + input_unregister_device(pdev->button_dev); +#endif + + v4l2_device_put(&pdev->v4l2_dev); +} + + +/* + * Initialization code & module stuff + */ + +static unsigned int leds_nargs; + +#ifdef CONFIG_USB_PWC_DEBUG +module_param_named(trace, pwc_trace, int, 0644); +#endif +module_param(power_save, int, 0644); +module_param_array(leds, int, &leds_nargs, 0444); + +#ifdef CONFIG_USB_PWC_DEBUG +MODULE_PARM_DESC(trace, "For debugging purposes"); +#endif +MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off"); +MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); + +MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); +MODULE_AUTHOR("Luc Saillard "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("pwcx"); +MODULE_VERSION( PWC_VERSION ); + +module_usb_driver(pwc_driver); diff --git a/drivers/media/usb/pwc/pwc-kiara.c b/drivers/media/usb/pwc/pwc-kiara.c new file mode 100644 index 000000000000..e5f4fd817125 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-kiara.c @@ -0,0 +1,892 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 730/740/750 (Kiara) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + + +#include "pwc-kiara.h" + +const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 }; + +const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 20 fps */ + { + {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, + {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, + {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, + {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, + {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, + }, + /* 30 fps */ + { + {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, + {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, + {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, + {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, + {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, + {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, + {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, + {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, + {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, + {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, + {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, + {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, + {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, + {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, + {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, + {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + + +/* + * Rom table for kiara chips + * + * 32 roms tables (one for each resolution ?) + * 2 tables per roms (one for each passes) (Y, and U&V) + * 128 bytes per passes + */ + +const unsigned int KiaraRomTable [8][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009292,0x00009493,0x000124db}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009292, + 0x00009492,0x00009493,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000126dc,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; + diff --git a/drivers/media/usb/pwc/pwc-kiara.h b/drivers/media/usb/pwc/pwc-kiara.h new file mode 100644 index 000000000000..8e02b7ac2139 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-kiara.h @@ -0,0 +1,48 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Entries for the Kiara (730/740/750) camera */ + +#ifndef PWC_KIARA_H +#define PWC_KIARA_H + +#include "pwc.h" + +#define PWC_FPS_MAX_KIARA 6 + +struct Kiara_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[12]; /* precomputed mode settings for cam */ +}; + +extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][PWC_FPS_MAX_KIARA][4]; +extern const unsigned int KiaraRomTable[8][2][16][8]; +extern const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA]; + +#endif + + diff --git a/drivers/media/usb/pwc/pwc-misc.c b/drivers/media/usb/pwc/pwc-misc.c new file mode 100644 index 000000000000..9be5adffa874 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-misc.c @@ -0,0 +1,93 @@ +/* Linux driver for Philips webcam + Various miscellaneous functions and tables. + (C) 1999-2003 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "pwc.h" + +const int pwc_image_sizes[PSZ_MAX][2] = +{ + { 128, 96 }, /* sqcif */ + { 160, 120 }, /* qsif */ + { 176, 144 }, /* qcif */ + { 320, 240 }, /* sif */ + { 352, 288 }, /* cif */ + { 640, 480 }, /* vga */ +}; + +/* x,y -> PSZ_ */ +int pwc_get_size(struct pwc_device *pdev, int width, int height) +{ + int i; + + /* Find the largest size supported by the camera that fits into the + requested size. */ + for (i = PSZ_MAX - 1; i >= 0; i--) { + if (!(pdev->image_mask & (1 << i))) + continue; + + if (pwc_image_sizes[i][0] <= width && + pwc_image_sizes[i][1] <= height) + return i; + } + + /* No mode found, return the smallest mode we have */ + for (i = 0; i < PSZ_MAX; i++) { + if (pdev->image_mask & (1 << i)) + return i; + } + + /* Never reached there always is atleast one supported mode */ + return 0; +} + +/* initialize variables depending on type and decompressor */ +void pwc_construct(struct pwc_device *pdev) +{ + if (DEVICE_USE_CODEC1(pdev->type)) { + + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; + pdev->vcinterface = 2; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + + } else if (DEVICE_USE_CODEC3(pdev->type)) { + + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + pdev->vcinterface = 3; + pdev->vendpoint = 5; + pdev->frame_header_size = TOUCAM_HEADER_SIZE; + pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; + + } else /* if (DEVICE_USE_CODEC2(pdev->type)) */ { + + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; + pdev->vcinterface = 3; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + } +} diff --git a/drivers/media/usb/pwc/pwc-nala.h b/drivers/media/usb/pwc/pwc-nala.h new file mode 100644 index 000000000000..168c73ef75d8 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-nala.h @@ -0,0 +1,66 @@ + /* SQCIF */ + { + {0, 0, {0x04, 0x01, 0x03}}, + {8, 0, {0x05, 0x01, 0x03}}, + {7, 0, {0x08, 0x01, 0x03}}, + {7, 0, {0x0A, 0x01, 0x03}}, + {6, 0, {0x0C, 0x01, 0x03}}, + {5, 0, {0x0F, 0x01, 0x03}}, + {4, 0, {0x14, 0x01, 0x03}}, + {3, 0, {0x18, 0x01, 0x03}}, + }, + /* QSIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* QCIF */ + { + {0, 0, {0x04, 0x01, 0x02}}, + {8, 0, {0x05, 0x01, 0x02}}, + {7, 0, {0x08, 0x01, 0x02}}, + {6, 0, {0x0A, 0x01, 0x02}}, + {5, 0, {0x0C, 0x01, 0x02}}, + {4, 0, {0x0F, 0x01, 0x02}}, + {1, 0, {0x14, 0x01, 0x02}}, + {1, 0, {0x18, 0x01, 0x02}}, + }, + /* SIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* CIF */ + { + {4, 0, {0x04, 0x01, 0x01}}, + {7, 1, {0x05, 0x03, 0x01}}, + {6, 1, {0x08, 0x03, 0x01}}, + {4, 1, {0x0A, 0x03, 0x01}}, + {3, 1, {0x0C, 0x03, 0x01}}, + {2, 1, {0x0F, 0x03, 0x01}}, + {0}, + {0}, + }, + /* VGA */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, diff --git a/drivers/media/usb/pwc/pwc-timon.c b/drivers/media/usb/pwc/pwc-timon.c new file mode 100644 index 000000000000..c56c174b161c --- /dev/null +++ b/drivers/media/usb/pwc/pwc-timon.c @@ -0,0 +1,1448 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#include "pwc-timon.h" + +const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON] = { 5, 10, 15, 20, 25, 30 }; + +const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + }, + /* 15 fps */ + { + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + }, + /* 25 fps */ + { + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + }, + /* 30 fps */ + { + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, + {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, + {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, + {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, + {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, + {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, + {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, + {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, + {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, + {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, + {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, + {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, + {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, + {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, + {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, + {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, + {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, + {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, + {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, + {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, + {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, + {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, + {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, + {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, + {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, + {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, + {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, + {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, + {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, + {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, + {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, + {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, + {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, + }, + /* 25 fps */ + { + {0, }, + {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, + {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, + {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, + {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, + {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, + {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, + {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, + {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, + {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + +/* + * 16 versions: + * 2 tables (one for Y, and one for U&V) + * 16 levels of details per tables + * 8 blocs + */ + +const unsigned int TimonRomTable [16][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000001,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000001,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000049,0x00000249,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009292, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x000124db,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001b724,0x0001b925,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 8 */ + { /* version 8, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 8, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 9 */ + { /* version 9, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 9, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 10 */ + { /* version 10, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 10, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x00009493,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 11 */ + { /* version 11, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 11, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 12 */ + { /* version 12, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 12, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 13 */ + { /* version 13, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 13, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 14 */ + { /* version 14, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 14, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 15 */ + { /* version 15, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 15, passes 1 */ + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000124db,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; diff --git a/drivers/media/usb/pwc/pwc-timon.h b/drivers/media/usb/pwc/pwc-timon.h new file mode 100644 index 000000000000..270c5b9010f6 --- /dev/null +++ b/drivers/media/usb/pwc/pwc-timon.h @@ -0,0 +1,63 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#ifndef PWC_TIMON_H +#define PWC_TIMON_H + +#include "pwc.h" + +#define PWC_FPS_MAX_TIMON 6 + +struct Timon_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[13]; /* precomputed mode settings for cam */ +}; + +extern const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4]; +extern const unsigned int TimonRomTable [16][2][16][8]; +extern const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON]; + +#endif + + diff --git a/drivers/media/usb/pwc/pwc-uncompress.c b/drivers/media/usb/pwc/pwc-uncompress.c new file mode 100644 index 000000000000..b65903fbcf0d --- /dev/null +++ b/drivers/media/usb/pwc/pwc-uncompress.c @@ -0,0 +1,107 @@ +/* Linux driver for Philips webcam + Decompression frontend. + (C) 1999-2003 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + vim: set ts=8: +*/ + +#include +#include + +#include "pwc.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" + +int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) +{ + int n, line, col; + void *yuv, *image; + u16 *src; + u16 *dsty, *dstu, *dstv; + + image = vb2_plane_vaddr(&fbuf->vb, 0); + + yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ + + /* Raw format; that's easy... */ + if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) + { + struct pwc_raw_frame *raw_frame = image; + raw_frame->type = cpu_to_le16(pdev->type); + raw_frame->vbandlength = cpu_to_le16(pdev->vbandlength); + /* cmd_buf is always 4 bytes, but sometimes, only the + * first 3 bytes is filled (Nala case). We can + * determine this using the type of the webcam */ + memcpy(raw_frame->cmd, pdev->cmd_buf, 4); + memcpy(raw_frame+1, yuv, pdev->frame_size); + vb2_set_plane_payload(&fbuf->vb, 0, + pdev->frame_size + sizeof(struct pwc_raw_frame)); + return 0; + } + + vb2_set_plane_payload(&fbuf->vb, 0, + pdev->width * pdev->height * 3 / 2); + + if (pdev->vbandlength == 0) { + /* Uncompressed mode. + * + * We do some byte shuffling here to go from the + * native format to YUV420P. + */ + src = (u16 *)yuv; + n = pdev->width * pdev->height; + dsty = (u16 *)(image); + dstu = (u16 *)(image + n); + dstv = (u16 *)(image + n + n / 4); + + for (line = 0; line < pdev->height; line++) { + for (col = 0; col < pdev->width; col += 4) { + *dsty++ = *src++; + *dsty++ = *src++; + if (line & 1) + *dstv++ = *src++; + else + *dstu++ = *src++; + } + } + + return 0; + } + + /* + * Compressed; + * the decompressor routines will write the data in planar format + * immediately. + */ + if (DEVICE_USE_CODEC1(pdev->type)) { + + /* TODO & FIXME */ + PWC_ERROR("This chipset is not supported for now\n"); + return -ENXIO; /* No such device or address: missing decompressor */ + + } else { + pwc_dec23_decompress(pdev, yuv, image); + } + return 0; +} diff --git a/drivers/media/usb/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c new file mode 100644 index 000000000000..545e9bbdeede --- /dev/null +++ b/drivers/media/usb/pwc/pwc-v4l.c @@ -0,0 +1,1053 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + (C) 2011 Hans de Goede + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc.h" + +#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl) + +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl); +static int pwc_s_ctrl(struct v4l2_ctrl *ctrl); + +static const struct v4l2_ctrl_ops pwc_ctrl_ops = { + .g_volatile_ctrl = pwc_g_volatile_ctrl, + .s_ctrl = pwc_s_ctrl, +}; + +enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto }; +enum { custom_autocontour, custom_contour, custom_noise_reduction, + custom_awb_speed, custom_awb_delay, + custom_save_user, custom_restore_user, custom_restore_factory }; + +const char * const pwc_auto_whitebal_qmenu[] = { + "Indoor (Incandescant Lighting) Mode", + "Outdoor (Sunlight) Mode", + "Indoor (Fluorescent Lighting) Mode", + "Manual Mode", + "Auto Mode", + NULL +}; + +static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = { + .ops = &pwc_ctrl_ops, + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_MENU, + .max = awb_auto, + .qmenu = pwc_auto_whitebal_qmenu, +}; + +static const struct v4l2_ctrl_config pwc_autocontour_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(autocontour), + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto contour", + .min = 0, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_contour_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(contour), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contour", + .flags = V4L2_CTRL_FLAG_SLIDER, + .min = 0, + .max = 63, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_backlight_cfg = { + .ops = &pwc_ctrl_ops, + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_flicker_cfg = { + .ops = &pwc_ctrl_ops, + .id = V4L2_CID_BAND_STOP_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(noise_reduction), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Dynamic Noise Reduction", + .min = 0, + .max = 3, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_save_user_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(save_user), + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Save User Settings", +}; + +static const struct v4l2_ctrl_config pwc_restore_user_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(restore_user), + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore User Settings", +}; + +static const struct v4l2_ctrl_config pwc_restore_factory_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(restore_factory), + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore Factory Settings", +}; + +static const struct v4l2_ctrl_config pwc_awb_speed_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(awb_speed), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto White Balance Speed", + .min = 1, + .max = 32, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_awb_delay_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(awb_delay), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto White Balance Delay", + .min = 0, + .max = 63, + .step = 1, +}; + +int pwc_init_controls(struct pwc_device *pdev) +{ + struct v4l2_ctrl_handler *hdl; + struct v4l2_ctrl_config cfg; + int r, def; + + hdl = &pdev->ctrl_handler; + r = v4l2_ctrl_handler_init(hdl, 20); + if (r) + return r; + + /* Brightness, contrast, saturation, gamma */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def); + if (r || def > 127) + def = 63; + pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, def); + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def); + if (r || def > 63) + def = 31; + pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, def); + + if (pdev->type >= 675) { + if (pdev->type < 730) + pdev->saturation_fmt = SATURATION_MODE_FORMATTER2; + else + pdev->saturation_fmt = SATURATION_MODE_FORMATTER1; + r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt, + &def); + if (r || def < -100 || def > 100) + def = 0; + pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_SATURATION, -100, 100, 1, def); + } + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def); + if (r || def > 31) + def = 15; + pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_GAMMA, 0, 31, 1, def); + + /* auto white balance, red gain, blue gain */ + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def); + if (r || def > awb_auto) + def = awb_auto; + cfg = pwc_auto_white_balance_cfg; + cfg.name = v4l2_ctrl_get_name(cfg.id); + cfg.def = def; + pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */ + if (!pdev->auto_white_balance) + return hdl->error; + + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + PRESET_MANUAL_RED_GAIN_FORMATTER, &def); + if (r) + def = 127; + pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 255, 1, def); + + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def); + if (r) + def = 127; + pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 255, 1, def); + + v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true); + + /* autogain, gain */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + /* Note a register value if 0 means auto gain is on */ + pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0); + if (!pdev->autogain) + return hdl->error; + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def); + if (r || def > 63) + def = 31; + pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_GAIN, 0, 63, 1, def); + + /* auto exposure, exposure */ + if (DEVICE_USE_CODEC2(pdev->type)) { + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER, + &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + /* + * def = 0 auto, def = ff manual + * menu idx 0 = auto, idx 1 = manual + */ + pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl, + &pwc_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + 1, 0, def != 0); + if (!pdev->exposure_auto) + return hdl->error; + + /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ + r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, &def); + if (r || def > 655) + def = 655; + pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 655, 1, def); + /* CODEC2: separate auto gain & auto exposure */ + v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true); + v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto, + V4L2_EXPOSURE_MANUAL, true); + } else if (DEVICE_USE_CODEC3(pdev->type)) { + /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ + r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, &def); + if (r || def > 255) + def = 255; + pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 255, 1, def); + /* CODEC3: both gain and exposure controlled by autogain */ + pdev->autogain_expo_cluster[0] = pdev->autogain; + pdev->autogain_expo_cluster[1] = pdev->gain; + pdev->autogain_expo_cluster[2] = pdev->exposure; + v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster, + 0, true); + } + + /* color / bw setting */ + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER, + &def); + if (r || (def != 0 && def != 0xff)) + def = 0xff; + /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */ + pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops, + V4L2_CID_COLORFX, 1, 0, def == 0); + + /* autocontour, contour */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + cfg = pwc_autocontour_cfg; + cfg.def = def == 0; + pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + if (!pdev->autocontour) + return hdl->error; + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def); + if (r || def > 63) + def = 31; + cfg = pwc_contour_cfg; + cfg.def = def; + pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false); + + /* backlight */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, + BACK_LIGHT_COMPENSATION_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + cfg = pwc_backlight_cfg; + cfg.name = v4l2_ctrl_get_name(cfg.id); + cfg.def = def == 0; + pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + /* flikker rediction */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, + FLICKERLESS_MODE_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + cfg = pwc_flicker_cfg; + cfg.name = v4l2_ctrl_get_name(cfg.id); + cfg.def = def == 0; + pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + /* Dynamic noise reduction */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, + DYNAMIC_NOISE_CONTROL_FORMATTER, &def); + if (r || def > 3) + def = 2; + cfg = pwc_noise_reduction_cfg; + cfg.def = def; + pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + /* Save / Restore User / Factory Settings */ + pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL); + pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg, + NULL); + if (pdev->restore_user) + pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE; + pdev->restore_factory = v4l2_ctrl_new_custom(hdl, + &pwc_restore_factory_cfg, + NULL); + if (pdev->restore_factory) + pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE; + + /* Auto White Balance speed & delay */ + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + AWB_CONTROL_SPEED_FORMATTER, &def); + if (r || def < 1 || def > 32) + def = 1; + cfg = pwc_awb_speed_cfg; + cfg.def = def; + pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + AWB_CONTROL_DELAY_FORMATTER, &def); + if (r || def > 63) + def = 0; + cfg = pwc_awb_delay_cfg; + cfg.def = def; + pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + if (!(pdev->features & FEATURE_MOTOR_PANTILT)) + return hdl->error; + + /* Motor pan / tilt / reset */ + pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0); + if (!pdev->motor_pan) + return hdl->error; + pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0); + pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_PAN_RESET, 0, 0, 0, 0); + pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_TILT_RESET, 0, 0, 0, 0); + v4l2_ctrl_cluster(4, &pdev->motor_pan); + + return hdl->error; +} + +static void pwc_vidioc_fill_fmt(struct v4l2_format *f, + int width, int height, u32 pixfmt) +{ + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->fmt.pix.width = width; + f->fmt.pix.height = height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = pixfmt; + f->fmt.pix.bytesperline = f->fmt.pix.width; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() " + "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", + f->fmt.pix.width, + f->fmt.pix.height, + f->fmt.pix.bytesperline, + f->fmt.pix.sizeimage, + (f->fmt.pix.pixelformat)&255, + (f->fmt.pix.pixelformat>>8)&255, + (f->fmt.pix.pixelformat>>16)&255, + (f->fmt.pix.pixelformat>>24)&255); +} + +/* ioctl(VIDIOC_TRY_FMT) */ +static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) +{ + int size; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUV420: + break; + case V4L2_PIX_FMT_PWC1: + if (DEVICE_USE_CODEC23(pdev->type)) { + PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n"); + return -EINVAL; + } + break; + case V4L2_PIX_FMT_PWC2: + if (DEVICE_USE_CODEC1(pdev->type)) { + PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n"); + return -EINVAL; + } + break; + default: + PWC_DEBUG_IOCTL("Unsupported pixel format\n"); + return -EINVAL; + + } + + size = pwc_get_size(pdev, f->fmt.pix.width, f->fmt.pix.height); + pwc_vidioc_fill_fmt(f, + pwc_image_sizes[size][0], + pwc_image_sizes[size][1], + f->fmt.pix.pixelformat); + + return 0; +} + +/* ioctl(VIDIOC_SET_FMT) */ + +static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct pwc_device *pdev = video_drvdata(file); + int ret, pixelformat, compression = 0; + + ret = pwc_vidioc_try_fmt(pdev, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&pdev->vb_queue)) + return -EBUSY; + + pixelformat = f->fmt.pix.pixelformat; + + PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " + "format=%c%c%c%c\n", + f->fmt.pix.width, f->fmt.pix.height, pdev->vframes, + (pixelformat)&255, + (pixelformat>>8)&255, + (pixelformat>>16)&255, + (pixelformat>>24)&255); + + ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height, + pixelformat, 30, &compression, 0); + + PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); + + pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); + return ret; +} + +static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct pwc_device *pdev = video_drvdata(file); + + strcpy(cap->driver, PWC_NAME); + strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card)); + usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index) /* Only one INPUT is supported */ + return -EINVAL; + + strlcpy(i->name, "Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int pwc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int pwc_s_input(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct pwc_device *pdev = + container_of(ctrl->handler, struct pwc_device, ctrl_handler); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + if (pdev->color_bal_valid && + (pdev->auto_white_balance->val != awb_auto || + time_before(jiffies, + pdev->last_color_bal_update + HZ / 4))) { + pdev->red_balance->val = pdev->last_red_balance; + pdev->blue_balance->val = pdev->last_blue_balance; + break; + } + ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_RED_GAIN_FORMATTER, + &pdev->red_balance->val); + if (ret) + break; + ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_BLUE_GAIN_FORMATTER, + &pdev->blue_balance->val); + if (ret) + break; + pdev->last_red_balance = pdev->red_balance->val; + pdev->last_blue_balance = pdev->blue_balance->val; + pdev->last_color_bal_update = jiffies; + pdev->color_bal_valid = true; + break; + case V4L2_CID_AUTOGAIN: + if (pdev->gain_valid && time_before(jiffies, + pdev->last_gain_update + HZ / 4)) { + pdev->gain->val = pdev->last_gain; + break; + } + ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_AGC_FORMATTER, &pdev->gain->val); + if (ret) + break; + pdev->last_gain = pdev->gain->val; + pdev->last_gain_update = jiffies; + pdev->gain_valid = true; + if (!DEVICE_USE_CODEC3(pdev->type)) + break; + /* Fall through for CODEC3 where autogain also controls expo */ + case V4L2_CID_EXPOSURE_AUTO: + if (pdev->exposure_valid && time_before(jiffies, + pdev->last_exposure_update + HZ / 4)) { + pdev->exposure->val = pdev->last_exposure; + break; + } + ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, + &pdev->exposure->val); + if (ret) + break; + pdev->last_exposure = pdev->exposure->val; + pdev->last_exposure_update = jiffies; + pdev->exposure_valid = true; + break; + default: + ret = -EINVAL; + } + + if (ret) + PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret); + + return ret; +} + +static int pwc_set_awb(struct pwc_device *pdev) +{ + int ret; + + if (pdev->auto_white_balance->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + WB_MODE_FORMATTER, + pdev->auto_white_balance->val); + if (ret) + return ret; + + if (pdev->auto_white_balance->val != awb_manual) + pdev->color_bal_valid = false; /* Force cache update */ + + /* + * If this is a preset, update our red / blue balance values + * so that events get generated for the new preset values + */ + if (pdev->auto_white_balance->val == awb_indoor || + pdev->auto_white_balance->val == awb_outdoor || + pdev->auto_white_balance->val == awb_fl) + pwc_g_volatile_ctrl(pdev->auto_white_balance); + } + if (pdev->auto_white_balance->val != awb_manual) + return 0; + + if (pdev->red_balance->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + PRESET_MANUAL_RED_GAIN_FORMATTER, + pdev->red_balance->val); + if (ret) + return ret; + } + + if (pdev->blue_balance->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + PRESET_MANUAL_BLUE_GAIN_FORMATTER, + pdev->blue_balance->val); + if (ret) + return ret; + } + return 0; +} + +/* For CODEC2 models which have separate autogain and auto exposure */ +static int pwc_set_autogain(struct pwc_device *pdev) +{ + int ret; + + if (pdev->autogain->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + AGC_MODE_FORMATTER, + pdev->autogain->val ? 0 : 0xff); + if (ret) + return ret; + + if (pdev->autogain->val) + pdev->gain_valid = false; /* Force cache update */ + } + + if (pdev->autogain->val) + return 0; + + if (pdev->gain->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + PRESET_AGC_FORMATTER, + pdev->gain->val); + if (ret) + return ret; + } + return 0; +} + +/* For CODEC2 models which have separate autogain and auto exposure */ +static int pwc_set_exposure_auto(struct pwc_device *pdev) +{ + int ret; + int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO; + + if (pdev->exposure_auto->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + SHUTTER_MODE_FORMATTER, + is_auto ? 0 : 0xff); + if (ret) + return ret; + + if (is_auto) + pdev->exposure_valid = false; /* Force cache update */ + } + + if (is_auto) + return 0; + + if (pdev->exposure->is_new) { + ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, + PRESET_SHUTTER_FORMATTER, + pdev->exposure->val); + if (ret) + return ret; + } + return 0; +} + +/* For CODEC3 models which have autogain controlling both gain and exposure */ +static int pwc_set_autogain_expo(struct pwc_device *pdev) +{ + int ret; + + if (pdev->autogain->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + AGC_MODE_FORMATTER, + pdev->autogain->val ? 0 : 0xff); + if (ret) + return ret; + + if (pdev->autogain->val) { + pdev->gain_valid = false; /* Force cache update */ + pdev->exposure_valid = false; /* Force cache update */ + } + } + + if (pdev->autogain->val) + return 0; + + if (pdev->gain->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + PRESET_AGC_FORMATTER, + pdev->gain->val); + if (ret) + return ret; + } + + if (pdev->exposure->is_new) { + ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, + PRESET_SHUTTER_FORMATTER, + pdev->exposure->val); + if (ret) + return ret; + } + return 0; +} + +static int pwc_set_motor(struct pwc_device *pdev) +{ + int ret; + + pdev->ctrl_buf[0] = 0; + if (pdev->motor_pan_reset->is_new) + pdev->ctrl_buf[0] |= 0x01; + if (pdev->motor_tilt_reset->is_new) + pdev->ctrl_buf[0] |= 0x02; + if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) { + ret = send_control_msg(pdev, SET_MPT_CTL, + PT_RESET_CONTROL_FORMATTER, + pdev->ctrl_buf, 1); + if (ret < 0) + return ret; + } + + memset(pdev->ctrl_buf, 0, 4); + if (pdev->motor_pan->is_new) { + pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF; + pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8); + } + if (pdev->motor_tilt->is_new) { + pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF; + pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8); + } + if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) { + ret = send_control_msg(pdev, SET_MPT_CTL, + PT_RELATIVE_CONTROL_FORMATTER, + pdev->ctrl_buf, 4); + if (ret < 0) + return ret; + } + + return 0; +} + +static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct pwc_device *pdev = + container_of(ctrl->handler, struct pwc_device, ctrl_handler); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + BRIGHTNESS_FORMATTER, ctrl->val); + break; + case V4L2_CID_CONTRAST: + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + CONTRAST_FORMATTER, ctrl->val); + break; + case V4L2_CID_SATURATION: + ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL, + pdev->saturation_fmt, ctrl->val); + break; + case V4L2_CID_GAMMA: + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + GAMMA_FORMATTER, ctrl->val); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = pwc_set_awb(pdev); + break; + case V4L2_CID_AUTOGAIN: + if (DEVICE_USE_CODEC2(pdev->type)) + ret = pwc_set_autogain(pdev); + else if (DEVICE_USE_CODEC3(pdev->type)) + ret = pwc_set_autogain_expo(pdev); + else + ret = -EINVAL; + break; + case V4L2_CID_EXPOSURE_AUTO: + if (DEVICE_USE_CODEC2(pdev->type)) + ret = pwc_set_exposure_auto(pdev); + else + ret = -EINVAL; + break; + case V4L2_CID_COLORFX: + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + COLOUR_MODE_FORMATTER, + ctrl->val ? 0 : 0xff); + break; + case PWC_CID_CUSTOM(autocontour): + if (pdev->autocontour->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + AUTO_CONTOUR_FORMATTER, + pdev->autocontour->val ? 0 : 0xff); + } + if (ret == 0 && pdev->contour->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + PRESET_CONTOUR_FORMATTER, + pdev->contour->val); + } + break; + case V4L2_CID_BACKLIGHT_COMPENSATION: + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + BACK_LIGHT_COMPENSATION_FORMATTER, + ctrl->val ? 0 : 0xff); + break; + case V4L2_CID_BAND_STOP_FILTER: + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + FLICKERLESS_MODE_FORMATTER, + ctrl->val ? 0 : 0xff); + break; + case PWC_CID_CUSTOM(noise_reduction): + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + DYNAMIC_NOISE_CONTROL_FORMATTER, + ctrl->val); + break; + case PWC_CID_CUSTOM(save_user): + ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER); + break; + case PWC_CID_CUSTOM(restore_user): + ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER); + break; + case PWC_CID_CUSTOM(restore_factory): + ret = pwc_button_ctrl(pdev, + RESTORE_FACTORY_DEFAULTS_FORMATTER); + break; + case PWC_CID_CUSTOM(awb_speed): + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + AWB_CONTROL_SPEED_FORMATTER, + ctrl->val); + break; + case PWC_CID_CUSTOM(awb_delay): + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + AWB_CONTROL_DELAY_FORMATTER, + ctrl->val); + break; + case V4L2_CID_PAN_RELATIVE: + ret = pwc_set_motor(pdev); + break; + default: + ret = -EINVAL; + } + + if (ret) + PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); + + return ret; +} + +static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct pwc_device *pdev = video_drvdata(file); + + /* We only support two format: the raw format, and YUV */ + switch (f->index) { + case 0: + /* RAW format */ + f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strlcpy(f->description, "Raw Philips Webcam", sizeof(f->description)); + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUV420; + strlcpy(f->description, "4:2:0, planar, Y-Cb-Cr", sizeof(f->description)); + break; + default: + return -EINVAL; + } + return 0; +} + +static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct pwc_device *pdev = video_drvdata(file); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", + pdev->width, pdev->height); + pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); + return 0; +} + +static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct pwc_device *pdev = video_drvdata(file); + + return pwc_vidioc_try_fmt(pdev, f); +} + +static int pwc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct pwc_device *pdev = video_drvdata(file); + unsigned int i = 0, index = fsize->index; + + if (fsize->pixel_format == V4L2_PIX_FMT_YUV420 || + (fsize->pixel_format == V4L2_PIX_FMT_PWC1 && + DEVICE_USE_CODEC1(pdev->type)) || + (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && + DEVICE_USE_CODEC23(pdev->type))) { + for (i = 0; i < PSZ_MAX; i++) { + if (!(pdev->image_mask & (1UL << i))) + continue; + if (!index--) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pwc_image_sizes[i][0]; + fsize->discrete.height = pwc_image_sizes[i][1]; + return 0; + } + } + } + return -EINVAL; +} + +static int pwc_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct pwc_device *pdev = video_drvdata(file); + int size = -1; + unsigned int i; + + for (i = 0; i < PSZ_MAX; i++) { + if (pwc_image_sizes[i][0] == fival->width && + pwc_image_sizes[i][1] == fival->height) { + size = i; + break; + } + } + + /* TODO: Support raw format */ + if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) + return -EINVAL; + + i = pwc_get_fps(pdev, fival->index, size); + if (!i) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + fival->discrete.denominator = i; + + return 0; +} + +static int pwc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct pwc_device *pdev = video_drvdata(file); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(parm, 0, sizeof(*parm)); + + parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm->parm.capture.readbuffers = MIN_FRAMES; + parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe.denominator = pdev->vframes; + parm->parm.capture.timeperframe.numerator = 1; + + return 0; +} + +static int pwc_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct pwc_device *pdev = video_drvdata(file); + int compression = 0; + int ret, fps; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* If timeperframe == 0, then reset the framerate to the nominal value. + We pick a high framerate here, and let pwc_set_video_mode() figure + out the best match. */ + if (parm->parm.capture.timeperframe.numerator == 0 || + parm->parm.capture.timeperframe.denominator == 0) + fps = 30; + else + fps = parm->parm.capture.timeperframe.denominator / + parm->parm.capture.timeperframe.numerator; + + if (vb2_is_busy(&pdev->vb_queue)) + return -EBUSY; + + ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, + fps, &compression, 0); + + pwc_g_parm(file, fh, parm); + + return ret; +} + +const struct v4l2_ioctl_ops pwc_ioctl_ops = { + .vidioc_querycap = pwc_querycap, + .vidioc_enum_input = pwc_enum_input, + .vidioc_g_input = pwc_g_input, + .vidioc_s_input = pwc_s_input, + .vidioc_enum_fmt_vid_cap = pwc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_enum_framesizes = pwc_enum_framesizes, + .vidioc_enum_frameintervals = pwc_enum_frameintervals, + .vidioc_g_parm = pwc_g_parm, + .vidioc_s_parm = pwc_s_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; diff --git a/drivers/media/usb/pwc/pwc.h b/drivers/media/usb/pwc/pwc.h new file mode 100644 index 000000000000..7a6a0d39c2c6 --- /dev/null +++ b/drivers/media/usb/pwc/pwc.h @@ -0,0 +1,393 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PWC_H +#define PWC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_USB_PWC_INPUT_EVDEV +#include +#endif +#include "pwc-dec1.h" +#include "pwc-dec23.h" + +/* Version block */ +#define PWC_VERSION "10.0.15" +#define PWC_NAME "pwc" +#define PFX PWC_NAME ": " + + +/* Trace certain actions in the driver */ +#define PWC_DEBUG_LEVEL_MODULE (1<<0) +#define PWC_DEBUG_LEVEL_PROBE (1<<1) +#define PWC_DEBUG_LEVEL_OPEN (1<<2) +#define PWC_DEBUG_LEVEL_READ (1<<3) +#define PWC_DEBUG_LEVEL_MEMORY (1<<4) +#define PWC_DEBUG_LEVEL_FLOW (1<<5) +#define PWC_DEBUG_LEVEL_SIZE (1<<6) +#define PWC_DEBUG_LEVEL_IOCTL (1<<7) +#define PWC_DEBUG_LEVEL_TRACE (1<<8) + +#define PWC_DEBUG_MODULE(fmt, args...) PWC_DEBUG(MODULE, fmt, ##args) +#define PWC_DEBUG_PROBE(fmt, args...) PWC_DEBUG(PROBE, fmt, ##args) +#define PWC_DEBUG_OPEN(fmt, args...) PWC_DEBUG(OPEN, fmt, ##args) +#define PWC_DEBUG_READ(fmt, args...) PWC_DEBUG(READ, fmt, ##args) +#define PWC_DEBUG_MEMORY(fmt, args...) PWC_DEBUG(MEMORY, fmt, ##args) +#define PWC_DEBUG_FLOW(fmt, args...) PWC_DEBUG(FLOW, fmt, ##args) +#define PWC_DEBUG_SIZE(fmt, args...) PWC_DEBUG(SIZE, fmt, ##args) +#define PWC_DEBUG_IOCTL(fmt, args...) PWC_DEBUG(IOCTL, fmt, ##args) +#define PWC_DEBUG_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) + + +#ifdef CONFIG_USB_PWC_DEBUG + +#define PWC_DEBUG_LEVEL (PWC_DEBUG_LEVEL_MODULE) + +#define PWC_DEBUG(level, fmt, args...) do {\ + if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \ + printk(KERN_DEBUG PFX fmt, ##args); \ + } while (0) + +#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) +#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) +#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) +#define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) + +#else /* if ! CONFIG_USB_PWC_DEBUG */ + +#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) +#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) +#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) +#define PWC_TRACE(fmt, args...) do { } while(0) +#define PWC_DEBUG(level, fmt, args...) do { } while(0) + +#define pwc_trace 0 + +#endif + +/* Defines for ToUCam cameras */ +#define TOUCAM_HEADER_SIZE 8 +#define TOUCAM_TRAILER_SIZE 4 + +#define FEATURE_MOTOR_PANTILT 0x0001 +#define FEATURE_CODEC1 0x0002 +#define FEATURE_CODEC2 0x0004 + +#define MAX_WIDTH 640 +#define MAX_HEIGHT 480 + +/* Ignore errors in the first N frames, to allow for startup delays */ +#define FRAME_LOWMARK 5 + +/* Size and number of buffers for the ISO pipe. */ +#define MAX_ISO_BUFS 3 +#define ISO_FRAMES_PER_DESC 10 +#define ISO_MAX_FRAME_SIZE 960 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + +/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ +#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) + +/* Absolute minimum and maximum number of buffers available for mmap() */ +#define MIN_FRAMES 2 +#define MAX_FRAMES 16 + +/* Some macros to quickly find the type of a webcam */ +#define DEVICE_USE_CODEC1(x) ((x)<675) +#define DEVICE_USE_CODEC2(x) ((x)>=675 && (x)<700) +#define DEVICE_USE_CODEC3(x) ((x)>=700) +#define DEVICE_USE_CODEC23(x) ((x)>=675) + +/* Request types: video */ +#define SET_LUM_CTL 0x01 +#define GET_LUM_CTL 0x02 +#define SET_CHROM_CTL 0x03 +#define GET_CHROM_CTL 0x04 +#define SET_STATUS_CTL 0x05 +#define GET_STATUS_CTL 0x06 +#define SET_EP_STREAM_CTL 0x07 +#define GET_EP_STREAM_CTL 0x08 +#define GET_XX_CTL 0x09 +#define SET_XX_CTL 0x0A +#define GET_XY_CTL 0x0B +#define SET_XY_CTL 0x0C +#define SET_MPT_CTL 0x0D +#define GET_MPT_CTL 0x0E + +/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ +#define AGC_MODE_FORMATTER 0x2000 +#define PRESET_AGC_FORMATTER 0x2100 +#define SHUTTER_MODE_FORMATTER 0x2200 +#define PRESET_SHUTTER_FORMATTER 0x2300 +#define PRESET_CONTOUR_FORMATTER 0x2400 +#define AUTO_CONTOUR_FORMATTER 0x2500 +#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 +#define CONTRAST_FORMATTER 0x2700 +#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 +#define FLICKERLESS_MODE_FORMATTER 0x2900 +#define AE_CONTROL_SPEED 0x2A00 +#define BRIGHTNESS_FORMATTER 0x2B00 +#define GAMMA_FORMATTER 0x2C00 + +/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ +#define WB_MODE_FORMATTER 0x1000 +#define AWB_CONTROL_SPEED_FORMATTER 0x1100 +#define AWB_CONTROL_DELAY_FORMATTER 0x1200 +#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 +#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 +#define COLOUR_MODE_FORMATTER 0x1500 +#define SATURATION_MODE_FORMATTER1 0x1600 +#define SATURATION_MODE_FORMATTER2 0x1700 + +/* Selectors for the Status controls [GS]ET_STATUS_CTL */ +#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 +#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 +#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 +#define READ_AGC_FORMATTER 0x0500 +#define READ_SHUTTER_FORMATTER 0x0600 +#define READ_RED_GAIN_FORMATTER 0x0700 +#define READ_BLUE_GAIN_FORMATTER 0x0800 + +/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ +#define PT_RELATIVE_CONTROL_FORMATTER 0x01 +#define PT_RESET_CONTROL_FORMATTER 0x02 +#define PT_STATUS_FORMATTER 0x03 + +/* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + +struct pwc_raw_frame { + __le16 type; /* type of the webcam */ + __le16 vbandlength; /* Size of 4 lines compressed (used by the + decompressor) */ + __u8 cmd[4]; /* the four byte of the command (in case of + nala, only the first 3 bytes is filled) */ + __u8 rawframe[0]; /* frame_size = H / 4 * vbandlength */ +} __packed; + +/* intermediate buffers with raw data from the USB cam */ +struct pwc_frame_buf +{ + struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + struct list_head list; + void *data; + int filled; /* number of bytes filled */ +}; + +struct pwc_device +{ + struct video_device vdev; + struct v4l2_device v4l2_dev; + + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + /* Pointer to our usb_device, will be NULL after unplug */ + struct usb_device *udev; /* Both mutexes most be hold when setting! */ + + /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ + int type; + int release; /* release number */ + int features; /* feature bits */ + + /*** Video data ***/ + int vendpoint; /* video isoc endpoint */ + int vcinterface; /* video control interface */ + int valternate; /* alternate interface needed */ + int vframes; /* frames-per-second */ + int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or _PWCX */ + int vframe_count; /* received frames */ + int vmax_packet_size; /* USB maxpacket size */ + int vlast_packet_size; /* for frame synchronisation */ + int visoc_errors; /* number of contiguous ISOC errors */ + int vbandlength; /* compressed band length; 0 is uncompressed */ + char vsync; /* used by isoc handler */ + char vmirror; /* for ToUCaM series */ + char power_save; /* Do powersaving for this cam */ + + unsigned char cmd_buf[13]; + unsigned char *ctrl_buf; + + struct urb *urbs[MAX_ISO_BUFS]; + + /* + * Frame currently being filled, this only gets touched by the + * isoc urb complete handler, and by stream start / stop since + * start / stop touch it before / after starting / killing the urbs + * no locking is needed around this + */ + struct pwc_frame_buf *fill_buf; + + int frame_header_size, frame_trailer_size; + int frame_size; + int frame_total_size; /* including header & trailer */ + int drop_frames; + + union { /* private data for decompression engine */ + struct pwc_dec1_private dec1; + struct pwc_dec23_private dec23; + }; + + /* + * We have an 'image' and a 'view', where 'image' is the fixed-size img + * as delivered by the camera, and 'view' is the size requested by the + * program. The camera image is centered in this viewport, laced with + * a gray or black border. view_min <= image <= view <= view_max; + */ + int image_mask; /* supported sizes */ + int width, height; /* current resolution */ + +#ifdef CONFIG_USB_PWC_INPUT_EVDEV + struct input_dev *button_dev; /* webcam snapshot button input */ + char button_phys[64]; +#endif + + /* controls */ + struct v4l2_ctrl_handler ctrl_handler; + u16 saturation_fmt; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *gamma; + struct { + /* awb / red-blue balance cluster */ + struct v4l2_ctrl *auto_white_balance; + struct v4l2_ctrl *red_balance; + struct v4l2_ctrl *blue_balance; + /* usb ctrl transfers are slow, so we cache things */ + int color_bal_valid; + unsigned long last_color_bal_update; /* In jiffies */ + s32 last_red_balance; + s32 last_blue_balance; + }; + struct { + /* autogain / gain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + int gain_valid; + unsigned long last_gain_update; /* In jiffies */ + s32 last_gain; + }; + struct { + /* exposure_auto / exposure cluster */ + struct v4l2_ctrl *exposure_auto; + struct v4l2_ctrl *exposure; + int exposure_valid; + unsigned long last_exposure_update; /* In jiffies */ + s32 last_exposure; + }; + struct v4l2_ctrl *colorfx; + struct { + /* autocontour/contour cluster */ + struct v4l2_ctrl *autocontour; + struct v4l2_ctrl *contour; + }; + struct v4l2_ctrl *backlight; + struct v4l2_ctrl *flicker; + struct v4l2_ctrl *noise_reduction; + struct v4l2_ctrl *save_user; + struct v4l2_ctrl *restore_user; + struct v4l2_ctrl *restore_factory; + struct v4l2_ctrl *awb_speed; + struct v4l2_ctrl *awb_delay; + struct { + /* motor control cluster */ + struct v4l2_ctrl *motor_pan; + struct v4l2_ctrl *motor_tilt; + struct v4l2_ctrl *motor_pan_reset; + struct v4l2_ctrl *motor_tilt_reset; + }; + /* CODEC3 models have both gain and exposure controlled by autogain */ + struct v4l2_ctrl *autogain_expo_cluster[3]; +}; + +/* Global variables */ +#ifdef CONFIG_USB_PWC_DEBUG +extern int pwc_trace; +#endif + +/** Functions in pwc-misc.c */ +/* sizes in pixels */ +extern const int pwc_image_sizes[PSZ_MAX][2]; + +int pwc_get_size(struct pwc_device *pdev, int width, int height); +void pwc_construct(struct pwc_device *pdev); + +/** Functions in pwc-ctrl.c */ +/* Request a certain video mode. Returns < 0 if not possible */ +extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, + int pixfmt, int frames, int *compression, int send_to_cam); +extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); +extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); +extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); +extern int send_control_msg(struct pwc_device *pdev, + u8 request, u16 value, void *buf, int buflen); + +/* Control get / set helpers */ +int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data); +int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data); +int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data); +#define pwc_set_s8_ctrl pwc_set_u8_ctrl +int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *dat); +int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data); +int pwc_button_ctrl(struct pwc_device *pdev, u16 value); +int pwc_init_controls(struct pwc_device *pdev); + +/* Power down or up the camera; not supported by all models */ +extern void pwc_camera_power(struct pwc_device *pdev, int power); + +extern const struct v4l2_ioctl_ops pwc_ioctl_ops; + +/** pwc-uncompress.c */ +/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ +int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf); + +#endif diff --git a/drivers/media/usb/sn9c102/Kconfig b/drivers/media/usb/sn9c102/Kconfig new file mode 100644 index 000000000000..6ebaf2940d06 --- /dev/null +++ b/drivers/media/usb/sn9c102/Kconfig @@ -0,0 +1,14 @@ +config USB_SN9C102 + tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" + depends on VIDEO_V4L2 + ---help--- + This driver is DEPRECATED please use the gspca sonixb and + sonixj modules instead. + + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called sn9c102. diff --git a/drivers/media/usb/sn9c102/Makefile b/drivers/media/usb/sn9c102/Makefile new file mode 100644 index 000000000000..7ecd5a90c7c9 --- /dev/null +++ b/drivers/media/usb/sn9c102/Makefile @@ -0,0 +1,15 @@ +sn9c102-objs := sn9c102_core.o \ + sn9c102_hv7131d.o \ + sn9c102_hv7131r.o \ + sn9c102_mi0343.o \ + sn9c102_mi0360.o \ + sn9c102_mt9v111.o \ + sn9c102_ov7630.o \ + sn9c102_ov7660.o \ + sn9c102_pas106b.o \ + sn9c102_pas202bcb.o \ + sn9c102_tas5110c1b.o \ + sn9c102_tas5110d.o \ + sn9c102_tas5130d1b.o + +obj-$(CONFIG_USB_SN9C102) += sn9c102.o diff --git a/drivers/media/usb/sn9c102/sn9c102.h b/drivers/media/usb/sn9c102/sn9c102.h new file mode 100644 index 000000000000..2bc153e869be --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102.h @@ -0,0 +1,211 @@ +/*************************************************************************** + * V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; 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 _SN9C102_H_ +#define _SN9C102_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102_config.h" +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +enum sn9c102_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct sn9c102_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum sn9c102_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum sn9c102_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum sn9c102_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum sn9c102_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +typedef char sn9c102_sof_header_t[62]; + +struct sn9c102_sof_t { + sn9c102_sof_header_t header; + u16 bytesread; +}; + +struct sn9c102_sysfs_attr { + u16 reg, i2c_reg; + sn9c102_sof_header_t frame_header; +}; + +struct sn9c102_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DEFINE_MUTEX(sn9c102_sysfs_lock); +static DECLARE_RWSEM(sn9c102_dev_lock); + +struct sn9c102_device { + struct video_device* v4ldev; + + enum sn9c102_bridge bridge; + struct sn9c102_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[SN9C102_URBS]; + void* transfer_buffer[SN9C102_URBS]; + u8* control_buffer; + + struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum sn9c102_io_method io; + enum sn9c102_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct sn9c102_sysfs_attr sysfs; + struct sn9c102_sof_t sof; + u16 reg[384]; + + struct sn9c102_module_param module_param; + + struct kref kref; + enum sn9c102_dev_state state; + u8 users; + + struct completion probe; + struct mutex open_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t wait_open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) +{ + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; +} + + +void +sn9c102_attach_sensor(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); +} + + +enum sn9c102_bridge +sn9c102_get_bridge(struct sn9c102_device* cam) +{ + return cam->bridge; +} + + +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam) +{ + return &cam->sensor; +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef SN9C102_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __func__, __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_printk_ioctl(name, cmd); \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("sn9c102: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", \ + __func__, __LINE__ , ## args); \ + } \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ + __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _SN9C102_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_config.h b/drivers/media/usb/sn9c102/sn9c102_config.h new file mode 100644 index 000000000000..0f4e0378b071 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_config.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; 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 _SN9C102_CONFIG_H_ +#define _SN9C102_CONFIG_H_ + +#include +#include + +#define SN9C102_DEBUG +#define SN9C102_DEBUG_LEVEL 2 +#define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 +#define SN9C102_FORCE_MUNMAP 0 +#define SN9C102_MAX_FRAMES 32 +#define SN9C102_URBS 2 +#define SN9C102_ISO_PACKETS 7 +#define SN9C102_ALTERNATE_SETTING 8 +#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) +#define SN9C102_CTRL_TIMEOUT 300 +#define SN9C102_FRAME_TIMEOUT 0 + +/*****************************************************************************/ + +static const u8 SN9C102_Y_QTABLE0[64] = { + 8, 5, 5, 8, 12, 20, 25, 30, + 6, 6, 7, 9, 13, 29, 30, 27, + 7, 6, 8, 12, 20, 28, 34, 28, + 7, 8, 11, 14, 25, 43, 40, 31, + 9, 11, 18, 28, 34, 54, 51, 38, + 12, 17, 27, 32, 40, 52, 56, 46, + 24, 32, 39, 43, 51, 60, 60, 50, + 36, 46, 47, 49, 56, 50, 51, 49 +}; + +static const u8 SN9C102_UV_QTABLE0[64] = { + 8, 9, 12, 23, 49, 49, 49, 49, + 9, 10, 13, 33, 49, 49, 49, 49, + 12, 13, 28, 49, 49, 49, 49, 49, + 23, 33, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49 +}; + +static const u8 SN9C102_Y_QTABLE1[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const u8 SN9C102_UV_QTABLE1[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c new file mode 100644 index 000000000000..19ea780b16ff --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -0,0 +1,3421 @@ +/*************************************************************************** + * V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * This program is free software; 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102.h" + +/*****************************************************************************/ + +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" +#define SN9C102_MODULE_ALIAS "sn9c1xx" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" +#define SN9C102_AUTHOR_EMAIL "" +#define SN9C102_MODULE_LICENSE "GPL" +#define SN9C102_MODULE_VERSION "1:1.48" + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, sn9c102_id_table); + +MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); +MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_ALIAS(SN9C102_MODULE_ALIAS); +MODULE_VERSION(SN9C102_MODULE_VERSION); +MODULE_LICENSE(SN9C102_MODULE_LICENSE); + +static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + " <-1|n[,...]>" + "\nSpecify V4L2 minor mode number." + "\n-1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES) + " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + " <0|1[,...]>" + "\nForce the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n0 = do not force memory unmapping" + "\n1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + " <0|n[,...]>" + "\nTimeout for a video frame in seconds before" + "\nreturning an I/O error; 0 for infinity." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." + "\n"); + +#ifdef SN9C102_DEBUG +static unsigned short debug = SN9C102_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + " " + "\nDebugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only." + "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." + "\n"); +#endif + +/* + Add the probe entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ +}; + +/*****************************************************************************/ + +static u32 +sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, + enum sn9c102_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > SN9C102_MAX_FRAMES) + count = SN9C102_MAX_FRAMES; + + if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) + imagesize += 589 + 2; /* length of JPEG header + EOI marker */ + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32_user(cam->nbuffers * + PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void sn9c102_release_buffers(struct sn9c102_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void sn9c102_empty_framequeues(struct sn9c102_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < SN9C102_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void sn9c102_requeue_outqueue(struct sn9c102_device* cam) +{ + struct sn9c102_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +/* + Write a sequence of count value/register pairs. Returns -1 after the first + failed write, or 0 for no errors. +*/ +int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2], + int count) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int i, res; + + for (i = 0; i < count; i++) { + u8 index = valreg[i][1]; + + /* + index is a u8, so it must be <256 and can't be out of range. + If we put in a check anyway, gcc annoys us with a warning + hat our check is useless. People get all uppity when they + see warnings in the kernel compile. + */ + + *buff = valreg[i][0]; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, + 0x41, index, 0, buff, 1, + SN9C102_CTRL_TIMEOUT); + + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, " + "index 0x%02X, error %d)", *buff, index, res); + return -1; + } + + cam->reg[index] = *buff; + } + + return 0; +} + + +int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + *buff = value; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, index " + "0x%02X, error %d)", value, index, res); + return -1; + } + + cam->reg[index] = value; + + return 0; +} + + +/* NOTE: with the SN9C10[123] reading some registers always returns 0 */ +int sn9c102_read_reg(struct sn9c102_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%02X, error %d)", + index, res); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) +{ + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + return cam->reg[index]; +} + + +static int +sn9c102_i2c_wait(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + int i, r; + + for (i = 1; i <= 5; i++) { + r = sn9c102_read_reg(cam, 0x08); + if (r < 0) + return -EIO; + if (r & 0x04) + return 0; + if (sensor->frequency & SN9C102_I2C_400KHZ) + udelay(5*16); + else + udelay(16*16); + } + return -EBUSY; +} + + +static int +sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + int r , err = 0; + + r = sn9c102_read_reg(cam, 0x08); + if (r < 0) + err += r; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { + if (!(r & 0x08)) + err += -1; + } else { + if (r & 0x08) + err += -1; + } + + return err ? -EIO : 0; +} + + +static int +sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; +} + + +int +sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 data0, + u8 data1, u8 n, u8 buffer[]) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int i = 0, err = 0, res; + + /* Write cycle */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; + data[1] = data0; /* I2C slave id */ + data[2] = data1; /* address */ + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* Read cycle - n bytes */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | + (n << 4) | 0x02; + data[1] = data0; + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* The first read byte will be placed in data[4] */ + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_detect_read_error(cam, sensor); + + PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, + data[4]); + + if (err) { + DBG(3, "I2C read failed for %s image sensor", sensor->name); + return -1; + } + + if (buffer) + for (i = 0; i < n && i < 5; i++) + buffer[n-i-1] = data[4-i]; + + return (int)data[4]; +} + + +int +sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 n, u8 data0, + u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle. It usually is address + value */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) + | ((n - 1) << 4); + data[1] = data0; + data[2] = data1; + data[3] = data2; + data[4] = data3; + data[5] = data4; + data[6] = data5; + data[7] = 0x17; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + err += sn9c102_i2c_detect_write_error(cam, sensor); + + if (err) + DBG(3, "I2C write failed for %s image sensor", sensor->name); + + PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " + "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", + n, data0, data1, data2, data3, data4, data5); + + return err ? -1 : 0; +} + + +int +sn9c102_i2c_try_read(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 address) +{ + return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, + address, 1, NULL); +} + + +static int sn9c102_i2c_try_write(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, + u8 address, u8 value) +{ + return sn9c102_i2c_try_raw_write(cam, sensor, 3, + sensor->i2c_slave_id, address, + value, 0, 0, 0); +} + + +int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) +{ + return sn9c102_i2c_try_read(cam, &cam->sensor, address); +} + + +int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) +{ + return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); +} + +/*****************************************************************************/ + +static size_t sn9c102_sof_length(struct sn9c102_device* cam) +{ + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + return 12; + case BRIDGE_SN9C103: + return 18; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + return 62; + } + + return 0; +} + + +static void* +sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + const char *m = mem; + size_t soflen = 0, i, j; + + soflen = sn9c102_sof_length(cam); + + for (i = 0; i < len; i++) { + size_t b; + + /* Read the variable part of the header */ + if (unlikely(cam->sof.bytesread >= sizeof(marker))) { + cam->sof.header[cam->sof.bytesread] = *(m+i); + if (++cam->sof.bytesread == soflen) { + cam->sof.bytesread = 0; + return mem + i; + } + continue; + } + + /* Search for the SOF marker (fixed part) in the header */ + for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { + if (unlikely(i+j == len)) + return NULL; + if (*(m+i+j) == marker[cam->sof.bytesread]) { + cam->sof.header[cam->sof.bytesread] = *(m+i+j); + if (++cam->sof.bytesread == sizeof(marker)) { + PDBGG("Bytes to analyze: %zd. SOF " + "starts at byte #%zd", len, i); + i += j+1; + break; + } + } else { + cam->sof.bytesread = 0; + break; + } + } + } + + return NULL; +} + + +static void* +sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + static const u8 eof_header[4][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}, + {0xc0, 0x00, 0x00, 0x00}, + }; + size_t i, j; + + /* The EOF header does not exist in compressed data */ + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + return NULL; + + /* + The EOF header might cross the packet boundary, but this is not a + problem, since the end of a frame is determined by checking its size + in the first place. + */ + for (i = 0; (len >= 4) && (i <= len - 4); i++) + for (j = 0; j < ARRAY_SIZE(eof_header); j++) + if (!memcmp(mem + i, eof_header[j], 4)) + return mem + i; + + return NULL; +} + + +static void +sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) +{ + static const u8 jpeg_header[589] = { + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, + 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, + 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, + 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, + 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, + 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, + 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, + 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, + 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, + 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, + 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, + 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, + 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, + 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, + 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, + 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, + 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, + 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, + 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, + 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, + 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, + 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, + 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, + 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, + 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 + }; + u8 *pos = f->bufmem; + + memcpy(pos, jpeg_header, sizeof(jpeg_header)); + *(pos + 6) = 0x00; + *(pos + 7 + 64) = 0x01; + if (cam->compression.quality == 0) { + memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); + } else if (cam->compression.quality == 1) { + memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); + } + *(pos + 564) = cam->sensor.pix_format.width & 0xFF; + *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; + *(pos + 562) = cam->sensor.pix_format.height & 0xFF; + *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; + *(pos + 567) = 0x21; + + f->buf.bytesused += sizeof(jpeg_header); +} + + +static void sn9c102_urb_complete(struct urb *urb) +{ + struct sn9c102_device* cam = urb->context; + struct sn9c102_frame_t** f; + size_t imagesize, soflen; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + cam->sof.bytesread = 0; + DBG(3, "Stream interrupted by application"); + wake_up(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + imagesize += 589; /* length of jpeg header */ + soflen = sn9c102_sof_length(cam); + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int img, len, status; + void *pos, *sof, *eof; + + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + cam->sof.bytesread = 0; + continue; + } + + PDBGG("Isochrnous frame: length %u, #%u i", len, i); + +redo: + sof = sn9c102_find_sof_header(cam, pos, len); + if (likely(!sof)) { + eof = sn9c102_find_eof_header(cam, pos, len); + if ((*f)->state == F_GRABBING) { +end_of_frame: + img = len; + + if (eof) + img = (eof > pos) ? eof - pos - 1 : 0; + + if ((*f)->buf.bytesused + img > imagesize) { + u32 b; + b = (*f)->buf.bytesused + img - + imagesize; + img = imagesize - (*f)->buf.bytesused; + PDBGG("Expected EOF not found: video " + "frame cut"); + if (eof) + DBG(3, "Exceeded limit: +%u " + "bytes", (unsigned)(b)); + } + + memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, + img); + + if ((*f)->buf.bytesused == 0) + do_gettimeofday(&(*f)->buf.timestamp); + + (*f)->buf.bytesused += img; + + if ((*f)->buf.bytesused == imagesize || + ((cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) && eof)) { + u32 b; + + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, + &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry( + cam->inqueue.next, + struct sn9c102_frame_t, + frame ); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + + memcpy(cam->sysfs.frame_header, + cam->sof.header, soflen); + + DBG(3, "Video frame captured: %lu " + "bytes", (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + } else if (eof) { + (*f)->state = F_ERROR; + DBG(3, "Not expected EOF after %lu " + "bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + } + + if (sof) /* (1) */ + goto start_of_frame; + + } else if (eof) { + DBG(3, "EOF without SOF"); + continue; + + } else { + PDBGG("Ignoring pointless isochronous frame"); + continue; + } + + } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { +start_of_frame: + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + len -= (sof - pos); + pos = sof; + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) + sn9c102_write_jpegheader(cam, (*f)); + DBG(3, "SOF detected: new video frame"); + if (len) + goto redo; + + } else if ((*f)->state == F_GRABBING) { + eof = sn9c102_find_eof_header(cam, pos, len); + if (eof && eof < sof) + goto end_of_frame; /* (1) */ + else { + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) { + if (sof - pos >= soflen) { + eof = sof - soflen; + } else { /* remove header */ + eof = pos; + (*f)->buf.bytesused -= + (soflen - (sof - pos)); + } + goto end_of_frame; + } else { + DBG(3, "SOF before expected EOF after " + "%lu bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + goto start_of_frame; + } + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int sn9c102_start_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + struct usb_host_interface* altsetting = usb_altnum_to_altsetting( + usb_ifnum_to_if(udev, 0), + SN9C102_ALTERNATE_SETTING); + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); + s8 i, j; + int err = 0; + + for (i = 0; i < SN9C102_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < SN9C102_URBS; i++) { + urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = SN9C102_ISO_PACKETS; + urb->complete = sn9c102_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < SN9C102_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + /* Enable video */ + if (!(cam->reg[0x01] & 0x04)) { + err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); + if (err) { + err = -EIO; + DBG(1, "I/O hardware error"); + goto free_urbs; + } + } + + err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + cam->sof.bytesread = 0; + + for (i = 0; i < SN9C102_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int sn9c102_stop_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = SN9C102_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int sn9c102_stream_interrupt(struct sn9c102_device* cam) +{ + cam->stream = STREAM_INTERRUPT; + wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + SN9C102_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (cam->stream != STREAM_OFF) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. " + "To use it, close and open %s again.", + video_device_node_name(cam->v4ldev)); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) +{ + char str[7]; + char* endp; + unsigned long val; + + if (len < 6) { + strncpy(str, buff, len); + str[len] = '\0'; + } else { + strncpy(str, buff, 6); + str[6] = '\0'; + } + + val = simple_strtoul(str, &endp, 0); + + *count = 0; + if (val <= 0xffff) + *count = (ssize_t)(endp - str); + if ((*count) && (len == *count+1) && (buff[*count] == '\n')) + *count += 1; + + return (u16)val; +} + +/* + NOTE 1: being inside one of the following methods implies that the v4l + device exists for sure (see kobjects and reference counters) + NOTE 2: buffers are PAGE_SIZE long +*/ + +static ssize_t sn9c102_show_reg(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.reg); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_reg(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou16(buf, len, &count); + if (index >= ARRAY_SIZE(cam->reg) || !count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.reg = index; + + DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_val(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd, value: %d", count, val); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_val(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + value = sn9c102_strtou16(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_write_reg(cam, value, cam->sysfs.reg); + if (err) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", + cam->sysfs.reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_reg(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou16(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.i2c_reg = index; + + DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_val(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd, value: %d", count, val); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + value = sn9c102_strtou16(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); + if (err) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", + cam->sysfs.i2c_reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_green(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + enum sn9c102_bridge bridge; + ssize_t res = 0; + u16 value; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + bridge = cam->bridge; + + mutex_unlock(&sn9c102_sysfs_lock); + + value = sn9c102_strtou16(buf, len, &count); + if (!count) + return -EINVAL; + + switch (bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + if (value > 0x0f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + break; + case BRIDGE_SN9C103: + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (value > 0x7f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + break; + } + + return res; +} + + +static ssize_t +sn9c102_store_blue(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + ssize_t res = 0; + u16 value; + ssize_t count; + + value = sn9c102_strtou16(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + + return res; +} + + +static ssize_t +sn9c102_store_red(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + ssize_t res = 0; + u16 value; + ssize_t count; + + value = sn9c102_strtou16(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + + return res; +} + + +static ssize_t sn9c102_show_frame_header(struct device* cd, + struct device_attribute *attr, + char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) + return -ENODEV; + + count = sizeof(cam->sysfs.frame_header); + memcpy(buf, cam->sysfs.frame_header, count); + + DBG(3, "Frame header, read bytes: %zd", count); + + return count; +} + + +static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg); +static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val); +static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); +static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_val, sn9c102_store_i2c_val); +static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); +static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); +static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); +static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); + + +static int sn9c102_create_sysfs(struct sn9c102_device* cam) +{ + struct device *dev = &(cam->v4ldev->dev); + int err = 0; + + if ((err = device_create_file(dev, &dev_attr_reg))) + goto err_out; + if ((err = device_create_file(dev, &dev_attr_val))) + goto err_reg; + if ((err = device_create_file(dev, &dev_attr_frame_header))) + goto err_val; + + if (cam->sensor.sysfs_ops) { + if ((err = device_create_file(dev, &dev_attr_i2c_reg))) + goto err_frame_header; + if ((err = device_create_file(dev, &dev_attr_i2c_val))) + goto err_i2c_reg; + } + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { + if ((err = device_create_file(dev, &dev_attr_green))) + goto err_i2c_val; + } else { + if ((err = device_create_file(dev, &dev_attr_blue))) + goto err_i2c_val; + if ((err = device_create_file(dev, &dev_attr_red))) + goto err_blue; + } + + return 0; + +err_blue: + device_remove_file(dev, &dev_attr_blue); +err_i2c_val: + if (cam->sensor.sysfs_ops) + device_remove_file(dev, &dev_attr_i2c_val); +err_i2c_reg: + if (cam->sensor.sysfs_ops) + device_remove_file(dev, &dev_attr_i2c_reg); +err_frame_header: + device_remove_file(dev, &dev_attr_frame_header); +err_val: + device_remove_file(dev, &dev_attr_val); +err_reg: + device_remove_file(dev, &dev_attr_reg); +err_out: + return err; +} +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + +/*****************************************************************************/ + +static int +sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + } + } else { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + } + } + + return err ? -EIO : 0; +} + + +static int +sn9c102_set_compression(struct sn9c102_device* cam, + struct v4l2_jpegcompression* compression) +{ + int i, err = 0; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (compression->quality == 0) + err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, + 0x17); + else if (compression->quality == 1) + err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, + 0x17); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (compression->quality == 0) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE1[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE1[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, + 0x18); + } else if (compression->quality == 1) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE1[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE1[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, + 0x18); + } + break; + } + + return err ? -EIO : 0; +} + + +static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) +{ + u8 r = 0; + int err = 0; + + if (scale == 1) + r = cam->reg[0x18] & 0xcf; + else if (scale == 2) { + r = cam->reg[0x18] & 0xcf; + r |= 0x10; + } else if (scale == 4) + r = cam->reg[0x18] | 0x20; + + err += sn9c102_write_reg(cam, r, 0x18); + if (err) + return -EIO; + + PDBGG("Scaling factor: %u", scale); + + return 0; +} + + +static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &cam->sensor; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), + v_start = (u8)(rect->top - s->cropcap.bounds.top), + h_size = (u8)(rect->width / 16), + v_size = (u8)(rect->height / 16); + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + err += sn9c102_write_reg(cam, h_size, 0x15); + err += sn9c102_write_reg(cam, v_size, 0x16); + if (err) + return -EIO; + + PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " + "%u %u %u %u", h_start, v_start, h_size, v_size); + + return 0; +} + + +static int sn9c102_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->open_mutex); + init_waitqueue_head(&cam->wait_open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); + err += sn9c102_set_crop(cam, rect); + if (err) + return err; + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + if (!(cam->state & DEV_INITIALIZED)) + if (cam->bridge == BRIDGE_SN9C101 || + cam->bridge == BRIDGE_SN9C102 || + cam->bridge == BRIDGE_SN9C103) { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; + cam->compression.quality = cam->reg[0x17] & 0x01 ? + 0 : 1; + } else { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; + cam->compression.quality = cam->reg[0x18] & 0x40 ? + 0 : 1; + err += sn9c102_set_compression(cam, &cam->compression); + } + else + err += sn9c102_set_compression(cam, &cam->compression); + err += sn9c102_set_pix_format(cam, &s->pix_format); + if (s->set_pix_format) + err += s->set_pix_format(cam, &s->pix_format); + if (err) + return err; + + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || + s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + DBG(3, "Compressed video format is active, quality %d", + cam->compression.quality); + else + DBG(3, "Uncompressed video format is active"); + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + +/*****************************************************************************/ + +static void sn9c102_release_resources(struct kref *kref) +{ + struct sn9c102_device *cam; + + mutex_lock(&sn9c102_sysfs_lock); + + cam = container_of(kref, struct sn9c102_device, kref); + + DBG(2, "V4L2 device %s deregistered", + video_device_node_name(cam->v4ldev)); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + usb_put_dev(cam->usbdev); + kfree(cam->control_buffer); + kfree(cam); + + mutex_unlock(&sn9c102_sysfs_lock); + +} + + +static int sn9c102_open(struct file *filp) +{ + struct sn9c102_device* cam; + int err = 0; + + /* + A read_trylock() in open() is the only safe way to prevent race + conditions with disconnect(), one close() and multiple (not + necessarily simultaneous) attempts to open(). For example, it + prevents from waiting for a second access, while the device + structure is being deallocated, after a possible disconnect() and + during a following close() holding the write lock: given that, after + this deallocation, no access will be possible anymore, using the + non-trylock version would have let open() gain the access to the + device structure improperly. + For this reason the lock must also not be per-device. + */ + if (!down_read_trylock(&sn9c102_dev_lock)) + return -ERESTARTSYS; + + cam = video_drvdata(filp); + + if (wait_for_completion_interruptible(&cam->probe)) { + up_read(&sn9c102_dev_lock); + return -ERESTARTSYS; + } + + kref_get(&cam->kref); + + /* + Make sure to isolate all the simultaneous opens. + */ + if (mutex_lock_interruptible(&cam->open_mutex)) { + kref_put(&cam->kref, sn9c102_release_resources); + up_read(&sn9c102_dev_lock); + return -ERESTARTSYS; + } + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + err = -ENODEV; + goto out; + } + + if (cam->users) { + DBG(2, "Device %s is already in use", + video_device_node_name(cam->v4ldev)); + DBG(3, "Simultaneous opens are not supported"); + /* + open() must follow the open flags and should block + eventually while the device is in use. + */ + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + DBG(2, "A blocking open() has been requested. Wait for the " + "device to be released..."); + up_read(&sn9c102_dev_lock); + /* + We will not release the "open_mutex" lock, so that only one + process can be in the wait queue below. This way the process + will be sleeping while holding the lock, without losing its + priority after any wake_up(). + */ + err = wait_event_interruptible_exclusive(cam->wait_open, + (cam->state & DEV_DISCONNECTED) + || !cam->users); + down_read(&sn9c102_dev_lock); + if (err) + goto out; + if (cam->state & DEV_DISCONNECTED) { + err = -ENODEV; + goto out; + } + } + + if (cam->state & DEV_MISCONFIGURED) { + err = sn9c102_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = sn9c102_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + sn9c102_empty_framequeues(cam); + + DBG(3, "Video device %s is open", video_device_node_name(cam->v4ldev)); + +out: + mutex_unlock(&cam->open_mutex); + if (err) + kref_put(&cam->kref, sn9c102_release_resources); + + up_read(&sn9c102_dev_lock); + return err; +} + + +static int sn9c102_release(struct file *filp) +{ + struct sn9c102_device* cam; + + down_write(&sn9c102_dev_lock); + + cam = video_drvdata(filp); + + sn9c102_stop_transfer(cam); + sn9c102_release_buffers(cam); + cam->users--; + wake_up_interruptible_nr(&cam->wait_open, 1); + + DBG(3, "Video device %s closed", video_device_node_name(cam->v4ldev)); + + kref_put(&cam->kref, sn9c102_release_resources); + + up_write(&sn9c102_dev_lock); + + return 0; +} + + +static ssize_t +sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct sn9c102_device *cam = video_drvdata(filp); + struct sn9c102_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose " + "the read method"); + mutex_unlock(&cam->fileop_mutex); + return -EBUSY; + } + + if (cam->io == IO_NONE) { + if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + sn9c102_empty_framequeues(cam); + sn9c102_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) { + mutex_unlock(&cam->fileop_mutex); + return err; + } + } else { + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + msecs_to_jiffies( + cam->module_param.frame_timeout * 1000 + ) + ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (cam->state & DEV_MISCONFIGURED) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + sn9c102_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return count; +} + + +static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) +{ + struct sn9c102_device *cam = video_drvdata(filp); + struct sn9c102_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!sn9c102_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + sn9c102_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void sn9c102_vm_open(struct vm_area_struct* vma) +{ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void sn9c102_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static const struct vm_operations_struct sn9c102_vm_ops = { + .open = sn9c102_vm_open, + .close = sn9c102_vm_close, +}; + + +static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct sn9c102_device *cam = video_drvdata(filp); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { + mutex_unlock(&cam->fileop_mutex); + return -EACCES; + } + + if (cam->io != IO_MMAP || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &sn9c102_vm_ops; + vma->vm_private_data = &cam->frame[i]; + sn9c102_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "sn9c102", + .version = LINUX_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; + i.capabilities = V4L2_IN_CAP_STD; + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id && ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + + return err; +} + + +static int +sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { + if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + } + if (i == ARRAY_SIZE(s->qctrl)) + return -EINVAL; + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + + return 0; +} + + +static int +sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_pix_format* pix_format = &(s->pix_format); + u8 scale; + const enum sn9c102_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EBUSY; + } + + /* Preserve R,G or B origin */ + rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; + rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; + + if (rect->width < 16) + rect->width = 16; + if (rect->height < 16) + rect->height = 16; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + + rect->width &= ~15L; + rect->height &= ~15L; + + if (SN9C102_PRESERVE_IMGSCALE) { + /* Calculate the actual scaling factor */ + u32 a, b; + a = rect->width * rect->height; + b = pix_format->width * pix_format->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } else + scale = 1; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + sn9c102_release_buffers(cam); + + err = sn9c102_set_crop(cam, rect); + if (s->set_crop) + err += s->set_crop(cam, rect); + err += sn9c102_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open %s again.", + video_device_node_name(cam->v4ldev)); + return -EIO; + } + + s->pix_format.width = rect->width/scale; + s->pix_format.height = rect->height/scale; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open %s again.", + video_device_node_name(cam->v4ldev)); + return -ENOMEM; + } + + if (cam->io == IO_READ) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_frmsizeenum frmsize; + + if (copy_from_user(&frmsize, arg, sizeof(frmsize))) + return -EFAULT; + + if (frmsize.index != 0) + return -EINVAL; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + } + + frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; + frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; + frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; + frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; + frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; + memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); + + if (copy_to_user(arg, &frmsize, sizeof(frmsize))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "bayer rgb"); + fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; + } else if (fmtd.index == 1) { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + strcpy(fmtd.description, "JPEG"); + fmtd.pixelformat = V4L2_PIX_FMT_JPEG; + break; + } + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || + pfmt->pixelformat == V4L2_PIX_FMT_JPEG) + ? 0 : (pfmt->width * pfmt->priv) / 8; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, + void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + u8 scale; + const enum sn9c102_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + { /* calculate the actual scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } + + rect.width = scale * pix->width; + rect.height = scale * pix->height; + + if (rect.width < 16) + rect.width = 16; + if (rect.height < 16) + rect.height = 16; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + + rect.width &= ~15L; + rect.height &= ~15L; + + { /* adjust the scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } + + pix->width = rect.width / scale; + pix->height = rect.height / scale; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat != V4L2_PIX_FMT_JPEG && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + break; + } + pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) + ? 0 : (pix->width * pix->priv) / 8; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. Unmap the " + "buffers first."); + return -EBUSY; + } + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + sn9c102_release_buffers(cam); + + err += sn9c102_set_pix_format(cam, pix); + err += sn9c102_set_crop(cam, &rect); + if (s->set_pix_format) + err += s->set_pix_format(cam, pix); + if (s->set_crop) + err += s->set_crop(cam, &rect); + err += sn9c102_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open %s again.", + video_device_node_name(cam->v4ldev)); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open %s again.", + video_device_node_name(cam->v4ldev)); + return -ENOMEM; + } + + if (cam->io == IO_READ) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum sn9c102_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality != 0 && jc.quality != 1) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + err += sn9c102_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware problems. " + "To use the camera, close and open %s again.", + video_device_node_name(cam->v4ldev)); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EBUSY; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " + "still mapped."); + return -EBUSY; + } + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + sn9c102_release_buffers(cam); + if (rb.count) + rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + sn9c102_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct sn9c102_frame_t *f; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) + return err; + } else { + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + return -EIO; + } + } + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (cam->state & DEV_MISCONFIGURED) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES) + sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int +sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + strcpy(audio.name, "Microphone"); + audio.capability = 0; + audio.mode = 0; + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + memset(&audio, 0, sizeof(audio)); + strcpy(audio.name, "Microphone"); + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + return 0; +} + + +static long sn9c102_ioctl_v4l2(struct file *filp, + unsigned int cmd, void __user *arg) +{ + struct sn9c102_device *cam = video_drvdata(filp); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return sn9c102_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return sn9c102_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return sn9c102_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return sn9c102_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return sn9c102_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return sn9c102_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL: + return sn9c102_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP: + return sn9c102_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return sn9c102_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return sn9c102_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FRAMESIZES: + return sn9c102_vidioc_enum_framesizes(cam, arg); + + case VIDIOC_ENUM_FMT: + return sn9c102_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return sn9c102_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return sn9c102_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return sn9c102_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return sn9c102_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return sn9c102_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return sn9c102_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return sn9c102_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return sn9c102_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return sn9c102_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return sn9c102_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM: + return sn9c102_vidioc_s_parm(cam, arg); + + case VIDIOC_ENUMAUDIO: + return sn9c102_vidioc_enumaudio(cam, arg); + + case VIDIOC_G_AUDIO: + return sn9c102_vidioc_g_audio(cam, arg); + + case VIDIOC_S_AUDIO: + return sn9c102_vidioc_s_audio(cam, arg); + + default: + return -ENOTTY; + + } +} + + +static long sn9c102_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct sn9c102_device *cam = video_drvdata(filp); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "sn9c102", cmd); + + err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + +/*****************************************************************************/ + +static const struct v4l2_file_operations sn9c102_fops = { + .owner = THIS_MODULE, + .open = sn9c102_open, + .release = sn9c102_release, + .unlocked_ioctl = sn9c102_ioctl, + .read = sn9c102_read, + .poll = sn9c102_poll, + .mmap = sn9c102_mmap, +}; + +/*****************************************************************************/ + +/* It exists a single interface only. We do not need to validate anything. */ +static int +sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct sn9c102_device* cam; + static unsigned int dev_nr; + unsigned int i; + int err = 0, r; + + if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { + DBG(1, "kzalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + r = sn9c102_read_reg(cam, 0x00); + if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { + DBG(1, "Sorry, this is not a SN9C1xx-based camera " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + err = -ENODEV; + goto fail; + } + + cam->bridge = id->driver_info; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + DBG(2, "SN9C10[12] PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C103: + DBG(2, "SN9C103 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C105: + DBG(2, "SN9C105 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C120: + DBG(2, "SN9C120 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + } + + for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) { + err = sn9c102_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) { + DBG(2, "%s image sensor detected", cam->sensor.name); + DBG(3, "Support for %s maintained by %s", + cam->sensor.name, cam->sensor.maintainer); + } else { + DBG(1, "No supported image sensor detected for this bridge"); + err = -ENODEV; + goto fail; + } + + if (!(cam->bridge & cam->sensor.supported_bridge)) { + DBG(1, "Bridge not supported"); + err = -ENODEV; + goto fail; + } + + if (sn9c102_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); + cam->v4ldev->fops = &sn9c102_fops; + cam->v4ldev->release = video_device_release; + cam->v4ldev->parent = &udev->dev; + + init_completion(&cam->probe); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + complete_all(&cam->probe); + goto fail; + } + + DBG(2, "V4L2 device registered as %s", + video_device_node_name(cam->v4ldev)); + + video_set_drvdata(cam->v4ldev, cam); + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + err = sn9c102_create_sysfs(cam); + if (!err) + DBG(2, "Optional device control through 'sysfs' " + "interface ready"); + else + DBG(2, "Failed to create optional 'sysfs' interface for " + "device controlling. Error #%d", err); +#else + DBG(2, "Optional device control through 'sysfs' interface disabled"); + DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " + "configuration option to enable it."); +#endif + + usb_set_intfdata(intf, cam); + kref_init(&cam->kref); + usb_get_dev(cam->usbdev); + + complete_all(&cam->probe); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void sn9c102_usb_disconnect(struct usb_interface* intf) +{ + struct sn9c102_device* cam; + + down_write(&sn9c102_dev_lock); + + cam = usb_get_intfdata(intf); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + if (cam->users) { + DBG(2, "Device %s is open! Deregistration and memory " + "deallocation are deferred.", + video_device_node_name(cam->v4ldev)); + cam->state |= DEV_MISCONFIGURED; + sn9c102_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up(&cam->wait_stream); + } else + cam->state |= DEV_DISCONNECTED; + + wake_up_interruptible_all(&cam->wait_open); + + kref_put(&cam->kref, sn9c102_release_resources); + + up_write(&sn9c102_dev_lock); +} + + +static struct usb_driver sn9c102_usb_driver = { + .name = "sn9c102", + .id_table = sn9c102_id_table, + .probe = sn9c102_usb_probe, + .disconnect = sn9c102_usb_disconnect, +}; + +module_usb_driver(sn9c102_usb_driver); diff --git a/drivers/media/usb/sn9c102/sn9c102_devtable.h b/drivers/media/usb/sn9c102/sn9c102_devtable.h new file mode 100644 index 000000000000..b3d2cc729657 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_devtable.h @@ -0,0 +1,147 @@ +/*************************************************************************** + * Table of device identifiers of the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; 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 _SN9C102_DEVTABLE_H_ +#define _SN9C102_DEVTABLE_H_ + +#include + +struct sn9c102_device; + +/* + Each SN9C1xx camera has proper PID/VID identifiers. + SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to + handle the video class interface. +*/ +#define SN9C102_USB_DEVICE(vend, prod, bridge) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = 0xff, \ + .driver_info = (bridge) + +static const struct usb_device_id sn9c102_id_table[] = { + /* SN9C101 and SN9C102 */ +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE + { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, +#endif + { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE + { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, +#endif + { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */ +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE + { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ + { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, +#endif + { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */ + /* SN9C103 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ + { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE +/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ + { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ + { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ + { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ +#endif + /* SN9C105 */ +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE + { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ + { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, + /* SN9C120 */ + { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ + { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ + { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, +#endif + { } +}; + +/* + Probing functions: on success, you must attach the sensor to the camera + by calling sn9c102_attach_sensor(). + To enable the I2C communication, you might need to perform a really basic + initialization of the SN9C1XX chip. + Functions must return 0 on success, the appropriate error otherwise. +*/ +extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); +extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam); +extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); +extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); + +#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_hv7131d.c b/drivers/media/usb/sn9c102/sn9c102_hv7131d.c new file mode 100644 index 000000000000..2dce5c908c8e --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_hv7131d.c @@ -0,0 +1,264 @@ +/*************************************************************************** + * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int hv7131d_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x60, 0x17}, + {0x0e, 0x18}, {0xf2, 0x19}); + + err += sn9c102_i2c_write(cam, 0x01, 0x04); + err += sn9c102_i2c_write(cam, 0x02, 0x00); + err += sn9c102_i2c_write(cam, 0x28, 0x00); + + return err; +} + + +static int hv7131d_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x26), + r2 = sn9c102_i2c_read(cam, 0x27); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 8) | (r2 & 0xff); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_RESET_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + ctrl->value &= 0x3f; + return 0; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131d_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); + err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_RESET_LEVEL: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + err += sn9c102_i2c_write(cam, 0x34, ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x42, 0x19); + else + err += sn9c102_write_reg(cam, 0xf2, 0x19); + + return err; +} + + +static const struct sn9c102_sensor hv7131d = { + .name = "HV7131D", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131d_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x0250, + .maximum = 0xffff, + .step = 0x0001, + .default_value = 0x0250, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1e, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_RESET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "reset level", + .minimum = 0x19, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x30, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "pixel bias voltage", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x02, + .flags = 0, + }, + }, + .get_ctrl = &hv7131d_get_ctrl, + .set_ctrl = &hv7131d_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131d_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131d_set_pix_format +}; + + +int sn9c102_probe_hv7131d(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + + r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); + r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); + if (err || r0 < 0 || r1 < 0) + return -EIO; + + if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131d); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_hv7131r.c b/drivers/media/usb/sn9c102/sn9c102_hv7131r.c new file mode 100644 index 000000000000..4295887ff609 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_hv7131r.c @@ -0,0 +1,363 @@ +/*************************************************************************** + * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int hv7131r_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, + {0x20, 0x05}, {0x20, 0x06}, + {0x03, 0x10}, {0x00, 0x14}, + {0x60, 0x17}, {0x0a, 0x18}, + {0xf0, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x44, 0x05}, {0x3e, 0x06}, + {0x1a, 0x07}, {0x03, 0x10}, + {0x08, 0x14}, {0xa3, 0x17}, + {0x4b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); + break; + default: + break; + } + + err += sn9c102_i2c_write(cam, 0x20, 0x00); + err += sn9c102_i2c_write(cam, 0x21, 0xd6); + err += sn9c102_i2c_write(cam, 0x25, 0x06); + + return err; +} + + +static int hv7131r_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLACK_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x08) ? 1 : 0; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131r_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, ctrl->value); + break; + case V4L2_CID_BLACK_LEVEL: + { + int r = sn9c102_i2c_read(cam, 0x01); + if (r < 0) + return -EIO; + err += sn9c102_i2c_write(cam, 0x01, + (ctrl->value<<3) | (r&0xf7)); + } + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131r_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131r_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa0, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } else { + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa5, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x24); + } else { + err += sn9c102_write_reg(cam, 0xa3, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + default: + break; + } + + return err; +} + + +static const struct sn9c102_sensor hv7131r = { + .name = "HV7131R", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131r_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x08, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1a, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x2f, + .flags = 0, + }, + { + .id = V4L2_CID_BLACK_LEVEL, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto black level compensation", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &hv7131r_get_ctrl, + .set_ctrl = &hv7131r_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131r_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131r_set_pix_format +}; + + +int sn9c102_probe_hv7131r(struct sn9c102_device* cam) +{ + int devid, err; + + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, + {0x34, 0x01}, {0x20, 0x17}, + {0x34, 0x01}, {0x46, 0x01}); + + devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); + if (err || devid < 0) + return -EIO; + + if (devid != 0x02) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131r); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_mi0343.c b/drivers/media/usb/sn9c102/sn9c102_mi0343.c new file mode 100644 index 000000000000..1f5b09bec89c --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_mi0343.c @@ -0,0 +1,352 @@ +/*************************************************************************** + * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int mi0343_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0343_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[2]; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, + data) < 0) + return -EIO; + ctrl->value = data[0]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, + data) < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x80 ? 1 : 0; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, + data) < 0) + return -EIO; + break; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, + data) < 0) + return -EIO; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, + data) < 0) + return -EIO; + break; + default: + return -EINVAL; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = data[1] | (data[0] << 8); + if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) + ctrl->value -= 0x10; + else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) + ctrl->value -= 0x60; + else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) + ctrl->value -= 0xe0; + } + + return 0; +} + + +static int mi0343_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u16 reg = 0; + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (ctrl->value <= (0x3f-0x10)) + reg = 0x10 + ctrl->value; + else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) + reg = 0x60 + (ctrl->value - (0x3f-0x10)); + else + reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); + break; + } + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x35, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2d, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2c, reg >> 8, reg & 0xff, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2b, reg >> 8, reg & 0xff, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2e, reg >> 8, reg & 0xff, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0343_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0343_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x03, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + } else { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0xa0, 0x19); + } + + return err; +} + + +static const struct sn9c102_sensor mi0343 = { + .name = "MI-0343", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0343_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &mi0343_get_ctrl, + .set_ctrl = &mi0343_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0343_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0343_set_pix_format +}; + + +int sn9c102_probe_mi0343(struct sn9c102_device* cam) +{ + u8 data[2]; + + if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17})) + return -EIO; + + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, + 2, data) < 0) + return -EIO; + + if (data[1] != 0x42 || data[0] != 0xe3) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0343); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_mi0360.c b/drivers/media/usb/sn9c102/sn9c102_mi0360.c new file mode 100644 index 000000000000..d973fc1973d9 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_mi0360.c @@ -0,0 +1,453 @@ +/*************************************************************************** + * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int mi0360_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x50, 0x05}, {0x20, 0x06}, + {0x10, 0x07}, {0x03, 0x10}, + {0x08, 0x14}, {0xa2, 0x17}, + {0x47, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); + break; + default: + break; + } + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0360_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[2]; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, + data) < 0) + return -EIO; + ctrl->value = data[0]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x80 ? 1 : 0; + return 0; + default: + return -EINVAL; + } + + return 0; +} + + +static int mi0360_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x35, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2c, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2d, 0x03, ctrl->value, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2b, 0x03, ctrl->value, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2e, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0360_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; + break; + default: + break; + } + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0360_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0x60, 0x19); + if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || + sn9c102_get_bridge(cam) == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, 0xa6, 0x17); + } else { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x02, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || + sn9c102_get_bridge(cam) == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, 0xa2, 0x17); + } + + return err; +} + + +static const struct sn9c102_sensor mi0360 = { + .name = "MI-0360", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0360_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x0f, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x32, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + }, + .get_ctrl = &mi0360_get_ctrl, + .set_ctrl = &mi0360_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0360_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0360_set_pix_format +}; + + +int sn9c102_probe_mi0360(struct sn9c102_device* cam) +{ + + u8 data[2]; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17})) + return -EIO; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17})) + return -EIO; + break; + default: + break; + } + + if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, + 2, data) < 0) + return -EIO; + + if (data[0] != 0x82 || data[1] != 0x43) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0360); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_mt9v111.c b/drivers/media/usb/sn9c102/sn9c102_mt9v111.c new file mode 100644 index 000000000000..95986eb492e4 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_mt9v111.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int mt9v111_init(struct sn9c102_device *cam) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x1f, 0x05}, {0x20, 0x06}, + {0x1f, 0x07}, {0x81, 0x08}, + {0x5c, 0x09}, {0x00, 0x0a}, + {0x00, 0x0b}, {0x00, 0x0c}, + {0x00, 0x0d}, {0x00, 0x0e}, + {0x00, 0x0f}, {0x03, 0x10}, + {0x00, 0x11}, {0x00, 0x12}, + {0x02, 0x13}, {0x14, 0x14}, + {0x28, 0x15}, {0x1e, 0x16}, + {0xe2, 0x17}, {0x06, 0x18}, + {0x00, 0x19}, {0x00, 0x1a}, + {0x00, 0x1b}, {0x08, 0x20}, + {0x39, 0x21}, {0x51, 0x22}, + {0x63, 0x23}, {0x73, 0x24}, + {0x82, 0x25}, {0x8f, 0x26}, + {0x9b, 0x27}, {0xa7, 0x28}, + {0xb1, 0x29}, {0xbc, 0x2a}, + {0xc6, 0x2b}, {0xcf, 0x2c}, + {0xd8, 0x2d}, {0xe1, 0x2e}, + {0xea, 0x2f}, {0xf2, 0x30}, + {0x13, 0x84}, {0x00, 0x85}, + {0x25, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xee, 0x8a}, {0x0f, 0x8b}, + {0xe5, 0x8c}, {0x0f, 0x8d}, + {0x2e, 0x8e}, {0x00, 0x8f}, + {0x30, 0x90}, {0x00, 0x91}, + {0xd4, 0x92}, {0x0f, 0x93}, + {0xfc, 0x94}, {0x0f, 0x95}, + {0x14, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x60, 0x99}, + {0x07, 0x9a}, {0x40, 0x9b}, + {0x20, 0x9c}, {0x00, 0x9d}, + {0x00, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x05, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3c, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x2d, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x60, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, + 0x04, 0x80, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, + 0x00, 0x04, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, + 0x00, 0x08, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02, + 0x00, 0x16, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe7, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x87, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x40, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x09, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07, + 0x30, 0x02, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12, + 0x00, 0xb0, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13, + 0x00, 0x7c, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, + 0x00, 0x04, 0, 0); + + return err; +} + +static int mt9v111_get_ctrl(struct sn9c102_device *cam, + struct v4l2_control *ctrl) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + u8 data[2]; + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x80 ? 1 : 0; + return 0; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + +static int mt9v111_set_ctrl(struct sn9c102_device *cam, + const struct v4l2_control *ctrl) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, + ctrl->value ? 0x80 : 0x00, + ctrl->value ? 0x80 : 0x00, 0, + 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + +static int mt9v111_set_crop(struct sn9c102_device *cam, + const struct v4l2_rect *rect) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + int err = 0; + u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + +static int mt9v111_set_pix_format(struct sn9c102_device *cam, + const struct v4l2_pix_format *pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xb4, 0x17); + } else { + err += sn9c102_write_reg(cam, 0xe2, 0x17); + } + + return err; +} + + +static const struct sn9c102_sensor mt9v111 = { + .name = "MT9V111", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5c, + .init = &mt9v111_init, + .qctrl = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + }, + .get_ctrl = &mt9v111_get_ctrl, + .set_ctrl = &mt9v111_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mt9v111_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mt9v111_set_pix_format +}; + + +int sn9c102_probe_mt9v111(struct sn9c102_device *cam) +{ + u8 data[2]; + int err = 0; + + err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x29, 0x01}, {0x42, 0x17}, + {0x62, 0x17}, {0x08, 0x01}); + err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4, + mt9v111.i2c_slave_id, 0x01, 0x00, + 0x04, 0, 0); + if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111, + mt9v111.i2c_slave_id, 0x36, 2, + data) < 0) + return -EIO; + + if (data[0] != 0x82 || data[1] != 0x3a) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mt9v111); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_ov7630.c b/drivers/media/usb/sn9c102/sn9c102_ov7630.c new file mode 100644 index 000000000000..803712c29f02 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_ov7630.c @@ -0,0 +1,626 @@ +/*************************************************************************** + * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2006-2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int ov7630_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, + {0x0f, 0x18}, {0x50, 0x19}); + + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + err += sn9c102_i2c_write(cam, 0x15, 0x35); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1c); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x06); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x20, 0x05}, + {0x20, 0x06}, {0x20, 0x07}, + {0x03, 0x10}, {0x0a, 0x14}, + {0x60, 0x17}, {0x0f, 0x18}, + {0x50, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x15, 0x34); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x03, 0x10}, + {0x0a, 0x14}, {0xe2, 0x17}, + {0x0b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x24, 0x21}, + {0x3b, 0x22}, {0x47, 0x23}, + {0x60, 0x24}, {0x71, 0x25}, + {0x80, 0x26}, {0x8f, 0x27}, + {0x9d, 0x28}, {0xaa, 0x29}, + {0xb8, 0x2a}, {0xc4, 0x2b}, + {0xd1, 0x2c}, {0xdd, 0x2d}, + {0xe8, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3f}, + {0xc7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xc7, 0x48}, {0x01, 0x49}, + {0xc7, 0x4a}, {0x01, 0x4b}, + {0xc7, 0x4c}, {0x01, 0x4d}, + {0x44, 0x4e}, {0x00, 0x4f}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xc7, 0x54}, {0x01, 0x55}, + {0xc7, 0x56}, {0x01, 0x57}, + {0xc7, 0x58}, {0x01, 0x59}, + {0x44, 0x5a}, {0x00, 0x5b}, + {0x44, 0x5c}, {0x00, 0x5d}, + {0x44, 0x5e}, {0x00, 0x5f}, + {0xc7, 0x60}, {0x01, 0x61}, + {0xc7, 0x62}, {0x01, 0x63}, + {0xc7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6a}, {0x00, 0x6b}, + {0xc7, 0x6c}, {0x01, 0x6d}, + {0xc7, 0x6e}, {0x01, 0x6f}, + {0xc7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xc7, 0x78}, {0x01, 0x79}, + {0xc7, 0x7a}, {0x01, 0x7b}, + {0xc7, 0x7c}, {0x01, 0x7d}, + {0x44, 0x7e}, {0x00, 0x7f}, + {0x17, 0x84}, {0x00, 0x85}, + {0x2e, 0x86}, {0x00, 0x87}, + {0x09, 0x88}, {0x00, 0x89}, + {0xe8, 0x8a}, {0x0f, 0x8b}, + {0xda, 0x8c}, {0x0f, 0x8d}, + {0x40, 0x8e}, {0x00, 0x8f}, + {0x37, 0x90}, {0x00, 0x91}, + {0xcf, 0x92}, {0x0f, 0x93}, + {0xfa, 0x94}, {0x0f, 0x95}, + {0x00, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x00, 0x9a}, {0x40, 0x9b}, + {0x20, 0x9c}, {0x00, 0x9d}, + {0x00, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x00, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3c, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x32, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x60, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x12, 0x48); + err += sn9c102_i2c_write(cam, 0x01, 0x80); + err += sn9c102_i2c_write(cam, 0x02, 0x80); + err += sn9c102_i2c_write(cam, 0x03, 0x80); + err += sn9c102_i2c_write(cam, 0x04, 0x10); + err += sn9c102_i2c_write(cam, 0x05, 0x20); + err += sn9c102_i2c_write(cam, 0x06, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + err += sn9c102_i2c_write(cam, 0x0c, 0x20); + err += sn9c102_i2c_write(cam, 0x0d, 0x20); + err += sn9c102_i2c_write(cam, 0x15, 0x80); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1b); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x05); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x21, 0x1b); + err += sn9c102_i2c_write(cam, 0x22, 0x00); + err += sn9c102_i2c_write(cam, 0x23, 0xde); + err += sn9c102_i2c_write(cam, 0x24, 0x10); + err += sn9c102_i2c_write(cam, 0x25, 0x8a); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0xca); + err += sn9c102_i2c_write(cam, 0x28, 0xa2); + err += sn9c102_i2c_write(cam, 0x29, 0x74); + err += sn9c102_i2c_write(cam, 0x2a, 0x88); + err += sn9c102_i2c_write(cam, 0x2b, 0x34); + err += sn9c102_i2c_write(cam, 0x2c, 0x88); + err += sn9c102_i2c_write(cam, 0x2e, 0x00); + err += sn9c102_i2c_write(cam, 0x2f, 0x00); + err += sn9c102_i2c_write(cam, 0x30, 0x00); + err += sn9c102_i2c_write(cam, 0x32, 0xc2); + err += sn9c102_i2c_write(cam, 0x33, 0x08); + err += sn9c102_i2c_write(cam, 0x4c, 0x40); + err += sn9c102_i2c_write(cam, 0x4d, 0xf3); + err += sn9c102_i2c_write(cam, 0x60, 0x05); + err += sn9c102_i2c_write(cam, 0x61, 0x40); + err += sn9c102_i2c_write(cam, 0x62, 0x12); + err += sn9c102_i2c_write(cam, 0x63, 0x57); + err += sn9c102_i2c_write(cam, 0x64, 0x73); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x66, 0x55); + err += sn9c102_i2c_write(cam, 0x67, 0x01); + err += sn9c102_i2c_write(cam, 0x68, 0xac); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x1f); + err += sn9c102_i2c_write(cam, 0x70, 0x01); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x72, 0x10); + err += sn9c102_i2c_write(cam, 0x73, 0x50); + err += sn9c102_i2c_write(cam, 0x74, 0x20); + err += sn9c102_i2c_write(cam, 0x76, 0x01); + err += sn9c102_i2c_write(cam, 0x77, 0xf3); + err += sn9c102_i2c_write(cam, 0x78, 0x90); + err += sn9c102_i2c_write(cam, 0x79, 0x98); + err += sn9c102_i2c_write(cam, 0x7a, 0x98); + err += sn9c102_i2c_write(cam, 0x7b, 0x00); + err += sn9c102_i2c_write(cam, 0x7c, 0x38); + err += sn9c102_i2c_write(cam, 0x7d, 0xff); + break; + default: + break; + } + + return err; +} + + +static int ov7630_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + break; + case V4L2_CID_RED_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + ctrl->value = sn9c102_pread_reg(cam, 0x05); + else + ctrl->value = sn9c102_pread_reg(cam, 0x07); + break; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + ctrl->value = sn9c102_pread_reg(cam, 0x07); + else + ctrl->value = sn9c102_pread_reg(cam, 0x05); + break; + break; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_DO_WHITE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_WHITENESS: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + case V4L2_CID_VFLIP: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x80) ? 1 : 0; + break; + case SN9C102_V4L2_CID_GAMMA: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; + break; + case SN9C102_V4L2_CID_BAND_FILTER: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + else + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + else + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_WHITENESS: + err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, ctrl->value | + (ctrl->value << 1)); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); + break; + case SN9C102_V4L2_CID_GAMMA: + err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); + break; + case SN9C102_V4L2_CID_BAND_FILTER: + err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; + break; + default: + break; + } + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7630_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) + err += sn9c102_write_reg(cam, 0x50, 0x19); + else + err += sn9c102_write_reg(cam, 0x20, 0x19); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xe5, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x04); + } else { + err += sn9c102_write_reg(cam, 0xe2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x02); + } + break; + default: + break; + } + + return err; +} + + +static const struct sn9c102_sensor ov7630 = { + .name = "OV7630", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 | + BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7630_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x60, + .flags = 0, + }, + { + .id = V4L2_CID_WHITENESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: red", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: blue", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BAND_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "band filter", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "rgb gamma", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &ov7630_get_ctrl, + .set_ctrl = &ov7630_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7630_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SN9C10X, + .priv = 8, + }, + .set_pix_format = &ov7630_set_pix_format +}; + + +int sn9c102_probe_ov7630(struct sn9c102_device* cam) +{ + int pid, ver, err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01}, + {0x28, 0x17}, {0x44, 0x02}); + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + if (err || pid < 0) /* try a different initialization */ + err += sn9c102_write_const_regs(cam, {0x01, 0x01}, + {0x00, 0x01}); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x29, 0x01}, {0x74, 0x02}, + {0x0e, 0x01}, {0x44, 0x01}); + break; + default: + break; + } + + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x31) + return -ENODEV; + sn9c102_attach_sensor(cam, &ov7630); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_ov7660.c b/drivers/media/usb/sn9c102/sn9c102_ov7660.c new file mode 100644 index 000000000000..7977795d342b --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_ov7660.c @@ -0,0 +1,538 @@ +/*************************************************************************** + * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int ov7660_init(struct sn9c102_device* cam) +{ + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x03, 0x10}, + {0x08, 0x14}, {0x20, 0x17}, + {0x8b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3f}, + {0xc7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xc7, 0x48}, {0x01, 0x49}, + {0xc7, 0x4a}, {0x01, 0x4b}, + {0xc7, 0x4c}, {0x01, 0x4d}, + {0x44, 0x4e}, {0x00, 0x4f}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xc7, 0x54}, {0x01, 0x55}, + {0xc7, 0x56}, {0x01, 0x57}, + {0xc7, 0x58}, {0x01, 0x59}, + {0x44, 0x5a}, {0x00, 0x5b}, + {0x44, 0x5c}, {0x00, 0x5d}, + {0x44, 0x5e}, {0x00, 0x5f}, + {0xc7, 0x60}, {0x01, 0x61}, + {0xc7, 0x62}, {0x01, 0x63}, + {0xc7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6a}, {0x00, 0x6b}, + {0xc7, 0x6c}, {0x01, 0x6d}, + {0xc7, 0x6e}, {0x01, 0x6f}, + {0xc7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xc7, 0x78}, {0x01, 0x79}, + {0xc7, 0x7a}, {0x01, 0x7b}, + {0xc7, 0x7c}, {0x01, 0x7d}, + {0x44, 0x7e}, {0x00, 0x7f}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xec, 0x8a}, {0x0f, 0x8b}, + {0xd8, 0x8c}, {0x0f, 0x8d}, + {0x3d, 0x8e}, {0x00, 0x8f}, + {0x3d, 0x90}, {0x00, 0x91}, + {0xcd, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0c, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9a}, {0x00, 0x9b}, + {0x04, 0x9c}, {0x00, 0x9d}, + {0x08, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x05, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3C, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x32, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x00, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x09); + err += sn9c102_i2c_write(cam, 0x00, 0x0A); + err += sn9c102_i2c_write(cam, 0x01, 0x80); + err += sn9c102_i2c_write(cam, 0x02, 0x80); + err += sn9c102_i2c_write(cam, 0x03, 0x00); + err += sn9c102_i2c_write(cam, 0x04, 0x00); + err += sn9c102_i2c_write(cam, 0x05, 0x08); + err += sn9c102_i2c_write(cam, 0x06, 0x0B); + err += sn9c102_i2c_write(cam, 0x07, 0x00); + err += sn9c102_i2c_write(cam, 0x08, 0x1C); + err += sn9c102_i2c_write(cam, 0x09, 0x01); + err += sn9c102_i2c_write(cam, 0x0A, 0x76); + err += sn9c102_i2c_write(cam, 0x0B, 0x60); + err += sn9c102_i2c_write(cam, 0x0C, 0x00); + err += sn9c102_i2c_write(cam, 0x0D, 0x08); + err += sn9c102_i2c_write(cam, 0x0E, 0x04); + err += sn9c102_i2c_write(cam, 0x0F, 0x6F); + err += sn9c102_i2c_write(cam, 0x10, 0x20); + err += sn9c102_i2c_write(cam, 0x11, 0x03); + err += sn9c102_i2c_write(cam, 0x12, 0x05); + err += sn9c102_i2c_write(cam, 0x13, 0xC7); + err += sn9c102_i2c_write(cam, 0x14, 0x2C); + err += sn9c102_i2c_write(cam, 0x15, 0x00); + err += sn9c102_i2c_write(cam, 0x16, 0x02); + err += sn9c102_i2c_write(cam, 0x17, 0x10); + err += sn9c102_i2c_write(cam, 0x18, 0x60); + err += sn9c102_i2c_write(cam, 0x19, 0x02); + err += sn9c102_i2c_write(cam, 0x1A, 0x7B); + err += sn9c102_i2c_write(cam, 0x1B, 0x02); + err += sn9c102_i2c_write(cam, 0x1C, 0x7F); + err += sn9c102_i2c_write(cam, 0x1D, 0xA2); + err += sn9c102_i2c_write(cam, 0x1E, 0x01); + err += sn9c102_i2c_write(cam, 0x1F, 0x0E); + err += sn9c102_i2c_write(cam, 0x20, 0x05); + err += sn9c102_i2c_write(cam, 0x21, 0x05); + err += sn9c102_i2c_write(cam, 0x22, 0x05); + err += sn9c102_i2c_write(cam, 0x23, 0x05); + err += sn9c102_i2c_write(cam, 0x24, 0x68); + err += sn9c102_i2c_write(cam, 0x25, 0x58); + err += sn9c102_i2c_write(cam, 0x26, 0xD4); + err += sn9c102_i2c_write(cam, 0x27, 0x80); + err += sn9c102_i2c_write(cam, 0x28, 0x80); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2A, 0x00); + err += sn9c102_i2c_write(cam, 0x2B, 0x00); + err += sn9c102_i2c_write(cam, 0x2C, 0x80); + err += sn9c102_i2c_write(cam, 0x2D, 0x00); + err += sn9c102_i2c_write(cam, 0x2E, 0x00); + err += sn9c102_i2c_write(cam, 0x2F, 0x0E); + err += sn9c102_i2c_write(cam, 0x30, 0x08); + err += sn9c102_i2c_write(cam, 0x31, 0x30); + err += sn9c102_i2c_write(cam, 0x32, 0xB4); + err += sn9c102_i2c_write(cam, 0x33, 0x00); + err += sn9c102_i2c_write(cam, 0x34, 0x07); + err += sn9c102_i2c_write(cam, 0x35, 0x84); + err += sn9c102_i2c_write(cam, 0x36, 0x00); + err += sn9c102_i2c_write(cam, 0x37, 0x0C); + err += sn9c102_i2c_write(cam, 0x38, 0x02); + err += sn9c102_i2c_write(cam, 0x39, 0x43); + err += sn9c102_i2c_write(cam, 0x3A, 0x00); + err += sn9c102_i2c_write(cam, 0x3B, 0x0A); + err += sn9c102_i2c_write(cam, 0x3C, 0x6C); + err += sn9c102_i2c_write(cam, 0x3D, 0x99); + err += sn9c102_i2c_write(cam, 0x3E, 0x0E); + err += sn9c102_i2c_write(cam, 0x3F, 0x41); + err += sn9c102_i2c_write(cam, 0x40, 0xC1); + err += sn9c102_i2c_write(cam, 0x41, 0x22); + err += sn9c102_i2c_write(cam, 0x42, 0x08); + err += sn9c102_i2c_write(cam, 0x43, 0xF0); + err += sn9c102_i2c_write(cam, 0x44, 0x10); + err += sn9c102_i2c_write(cam, 0x45, 0x78); + err += sn9c102_i2c_write(cam, 0x46, 0xA8); + err += sn9c102_i2c_write(cam, 0x47, 0x60); + err += sn9c102_i2c_write(cam, 0x48, 0x80); + err += sn9c102_i2c_write(cam, 0x49, 0x00); + err += sn9c102_i2c_write(cam, 0x4A, 0x00); + err += sn9c102_i2c_write(cam, 0x4B, 0x00); + err += sn9c102_i2c_write(cam, 0x4C, 0x00); + err += sn9c102_i2c_write(cam, 0x4D, 0x00); + err += sn9c102_i2c_write(cam, 0x4E, 0x00); + err += sn9c102_i2c_write(cam, 0x4F, 0x46); + err += sn9c102_i2c_write(cam, 0x50, 0x36); + err += sn9c102_i2c_write(cam, 0x51, 0x0F); + err += sn9c102_i2c_write(cam, 0x52, 0x17); + err += sn9c102_i2c_write(cam, 0x53, 0x7F); + err += sn9c102_i2c_write(cam, 0x54, 0x96); + err += sn9c102_i2c_write(cam, 0x55, 0x40); + err += sn9c102_i2c_write(cam, 0x56, 0x40); + err += sn9c102_i2c_write(cam, 0x57, 0x40); + err += sn9c102_i2c_write(cam, 0x58, 0x0F); + err += sn9c102_i2c_write(cam, 0x59, 0xBA); + err += sn9c102_i2c_write(cam, 0x5A, 0x9A); + err += sn9c102_i2c_write(cam, 0x5B, 0x22); + err += sn9c102_i2c_write(cam, 0x5C, 0xB9); + err += sn9c102_i2c_write(cam, 0x5D, 0x9B); + err += sn9c102_i2c_write(cam, 0x5E, 0x10); + err += sn9c102_i2c_write(cam, 0x5F, 0xF0); + err += sn9c102_i2c_write(cam, 0x60, 0x05); + err += sn9c102_i2c_write(cam, 0x61, 0x60); + err += sn9c102_i2c_write(cam, 0x62, 0x00); + err += sn9c102_i2c_write(cam, 0x63, 0x00); + err += sn9c102_i2c_write(cam, 0x64, 0x50); + err += sn9c102_i2c_write(cam, 0x65, 0x30); + err += sn9c102_i2c_write(cam, 0x66, 0x00); + err += sn9c102_i2c_write(cam, 0x67, 0x80); + err += sn9c102_i2c_write(cam, 0x68, 0x7A); + err += sn9c102_i2c_write(cam, 0x69, 0x90); + err += sn9c102_i2c_write(cam, 0x6A, 0x80); + err += sn9c102_i2c_write(cam, 0x6B, 0x0A); + err += sn9c102_i2c_write(cam, 0x6C, 0x30); + err += sn9c102_i2c_write(cam, 0x6D, 0x48); + err += sn9c102_i2c_write(cam, 0x6E, 0x80); + err += sn9c102_i2c_write(cam, 0x6F, 0x74); + err += sn9c102_i2c_write(cam, 0x70, 0x64); + err += sn9c102_i2c_write(cam, 0x71, 0x60); + err += sn9c102_i2c_write(cam, 0x72, 0x5C); + err += sn9c102_i2c_write(cam, 0x73, 0x58); + err += sn9c102_i2c_write(cam, 0x74, 0x54); + err += sn9c102_i2c_write(cam, 0x75, 0x4C); + err += sn9c102_i2c_write(cam, 0x76, 0x40); + err += sn9c102_i2c_write(cam, 0x77, 0x38); + err += sn9c102_i2c_write(cam, 0x78, 0x34); + err += sn9c102_i2c_write(cam, 0x79, 0x30); + err += sn9c102_i2c_write(cam, 0x7A, 0x2F); + err += sn9c102_i2c_write(cam, 0x7B, 0x2B); + err += sn9c102_i2c_write(cam, 0x7C, 0x03); + err += sn9c102_i2c_write(cam, 0x7D, 0x07); + err += sn9c102_i2c_write(cam, 0x7E, 0x17); + err += sn9c102_i2c_write(cam, 0x7F, 0x34); + err += sn9c102_i2c_write(cam, 0x80, 0x41); + err += sn9c102_i2c_write(cam, 0x81, 0x4D); + err += sn9c102_i2c_write(cam, 0x82, 0x58); + err += sn9c102_i2c_write(cam, 0x83, 0x63); + err += sn9c102_i2c_write(cam, 0x84, 0x6E); + err += sn9c102_i2c_write(cam, 0x85, 0x77); + err += sn9c102_i2c_write(cam, 0x86, 0x87); + err += sn9c102_i2c_write(cam, 0x87, 0x95); + err += sn9c102_i2c_write(cam, 0x88, 0xAF); + err += sn9c102_i2c_write(cam, 0x89, 0xC7); + err += sn9c102_i2c_write(cam, 0x8A, 0xDF); + err += sn9c102_i2c_write(cam, 0x8B, 0x99); + err += sn9c102_i2c_write(cam, 0x8C, 0x99); + err += sn9c102_i2c_write(cam, 0x8D, 0xCF); + err += sn9c102_i2c_write(cam, 0x8E, 0x20); + err += sn9c102_i2c_write(cam, 0x8F, 0x26); + err += sn9c102_i2c_write(cam, 0x90, 0x10); + err += sn9c102_i2c_write(cam, 0x91, 0x0C); + err += sn9c102_i2c_write(cam, 0x92, 0x25); + err += sn9c102_i2c_write(cam, 0x93, 0x00); + err += sn9c102_i2c_write(cam, 0x94, 0x50); + err += sn9c102_i2c_write(cam, 0x95, 0x50); + err += sn9c102_i2c_write(cam, 0x96, 0x00); + err += sn9c102_i2c_write(cam, 0x97, 0x01); + err += sn9c102_i2c_write(cam, 0x98, 0x10); + err += sn9c102_i2c_write(cam, 0x99, 0x40); + err += sn9c102_i2c_write(cam, 0x9A, 0x40); + err += sn9c102_i2c_write(cam, 0x9B, 0x20); + err += sn9c102_i2c_write(cam, 0x9C, 0x00); + err += sn9c102_i2c_write(cam, 0x9D, 0x99); + err += sn9c102_i2c_write(cam, 0x9E, 0x7F); + err += sn9c102_i2c_write(cam, 0x9F, 0x00); + err += sn9c102_i2c_write(cam, 0xA0, 0x00); + err += sn9c102_i2c_write(cam, 0xA1, 0x00); + + return err; +} + + +static int ov7660_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + break; + case V4L2_CID_DO_WHITE_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x02)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x04) ? 1 : 0; + break; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x05)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x06)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x07)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case SN9C102_V4L2_CID_BAND_FILTER: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x3b)) < 0) + return -EIO; + ctrl->value &= 0x08; + break; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x1f; + break; + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + break; + case SN9C102_V4L2_CID_BAND_FILTER: + err += sn9c102_i2c_write(cam, ctrl->value << 3, 0x3b); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, 0x60 + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, 0xc0 | + (ctrl->value * 0x07)); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7660_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int r0, err = 0; + + r0 = sn9c102_pread_reg(cam, 0x01); + + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + } else { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x0d); + } + + return err; +} + + +static const struct sn9c102_sensor ov7660 = { + .name = "OV7660", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7660_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x09, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x27, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "night mode", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BAND_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "band filter", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &ov7660_get_ctrl, + .set_ctrl = &ov7660_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7660_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, + .set_pix_format = &ov7660_set_pix_format +}; + + +int sn9c102_probe_ov7660(struct sn9c102_device* cam) +{ + int pid, ver, err; + + err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + + pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x60) + return -ENODEV; + + sn9c102_attach_sensor(cam, &ov7660); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_pas106b.c b/drivers/media/usb/sn9c102/sn9c102_pas106b.c new file mode 100644 index 000000000000..81cd969c1b7b --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_pas106b.c @@ -0,0 +1,302 @@ +/*************************************************************************** + * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int pas106b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x20, 0x19}, {0x09, 0x18}); + + err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x05, 0x5a); + err += sn9c102_i2c_write(cam, 0x06, 0x88); + err += sn9c102_i2c_write(cam, 0x07, 0x80); + err += sn9c102_i2c_write(cam, 0x10, 0x06); + err += sn9c102_i2c_write(cam, 0x11, 0x06); + err += sn9c102_i2c_write(cam, 0x12, 0x00); + err += sn9c102_i2c_write(cam, 0x14, 0x02); + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + msleep(400); + + return err; +} + + +static int pas106b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x03), + r2 = sn9c102_i2c_read(cam, 0x04); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 4) | (r2 & 0x0f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_CONTRAST: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x1f) << 1; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0xf8; + return 0; + default: + return -EINVAL; + } +} + + +static int pas106b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); + err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); + break; + case V4L2_CID_CONTRAST: + err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); + err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + return err ? -EIO : 0; +} + + +static int pas106b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int pas106b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2c, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static const struct sn9c102_sensor pas106b = { + .name = "PAS106B", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas106b_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x125, + .maximum = 0xfff, + .step = 0x001, + .default_value = 0x140, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0d, + .flags = 0, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "contrast", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x00, /* 0x00~0x03 have same effect */ + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3e, + .step = 0x02, + .default_value = 0x02, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + }, + .get_ctrl = &pas106b_get_ctrl, + .set_ctrl = &pas106b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &pas106b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, /* we use this field as 'bits per pixel' */ + }, + .set_pix_format = &pas106b_set_pix_format +}; + + +int sn9c102_probe_pas106b(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0; + unsigned int pid = 0; + + /* + Minimal initialization to enable the I2C communication + NOTE: do NOT change the values! + */ + if (sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* sensor power down */ + {0x00, 0x01}, /* sensor power on */ + {0x28, 0x17})) /* sensor clock at 24 MHz */ + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 11) | ((r1 & 0xf0) >> 4); + if (pid != 0x007) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas106b); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c b/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c new file mode 100644 index 000000000000..2e86fdc86989 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c @@ -0,0 +1,335 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * + * * + * * + * Support for SN9C103, DAC Magnitude, exposure and green gain controls * + * added by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int pas202bcb_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x30, 0x19}, {0x09, 0x18}); + break; + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x20, 0x05}, + {0x20, 0x06}, {0x20, 0x07}, + {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x30, 0x19}, {0x09, 0x18}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x10, 0x21}, + {0x20, 0x22}, {0x30, 0x23}, + {0x40, 0x24}, {0x50, 0x25}, + {0x60, 0x26}, {0x70, 0x27}, + {0x80, 0x28}, {0x90, 0x29}, + {0xa0, 0x2a}, {0xb0, 0x2b}, + {0xc0, 0x2c}, {0xd0, 0x2d}, + {0xe0, 0x2e}, {0xf0, 0x2f}, + {0xff, 0x30}); + break; + default: + break; + } + + err += sn9c102_i2c_write(cam, 0x02, 0x14); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x10, 0x08); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bcb_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x04), + r2 = sn9c102_i2c_read(cam, 0x05); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 6) | (r2 & 0x3f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x28, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static int pas202bcb_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); + err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static int pas202bcb_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; + break; + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; + break; + default: + break; + } + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static const struct sn9c102_sensor pas202bcb = { + .name = "PAS202BCB", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0b, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bcb_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &pas202bcb_set_pix_format +}; + + +int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* + * Minimal initialization to enable the I2C communication + * NOTE: do NOT change the values! + */ + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* power down */ + {0x40, 0x01}, /* power on */ + {0x28, 0x17});/* clock 24 MHz */ + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x01}, + {0x44, 0x02}, {0x29, 0x17}); + break; + default: + break; + } + + r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); + + if (err || r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_sensor.h b/drivers/media/usb/sn9c102/sn9c102_sensor.h new file mode 100644 index 000000000000..3679970dba2c --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_sensor.h @@ -0,0 +1,307 @@ +/*************************************************************************** + * API for image sensors connected to the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * This program is free software; 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 _SN9C102_SENSOR_H_ +#define _SN9C102_SENSOR_H_ + +#include +#include +#include +#include +#include +#include + +struct sn9c102_device; +struct sn9c102_sensor; + +/*****************************************************************************/ + +/* + OVERVIEW. + This is a small interface that allows you to add support for any CCD/CMOS + image sensors connected to the SN9C1XX bridges. The entire API is documented + below. In the most general case, to support a sensor there are three steps + you have to follow: + 1) define the main "sn9c102_sensor" structure by setting the basic fields; + 2) write a probing function to be called by the core module when the USB + camera is recognized, then add both the USB ids and the name of that + function to the two corresponding tables in sn9c102_devtable.h; + 3) implement the methods that you want/need (and fill the rest of the main + structure accordingly). + "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do + NOT need to touch the source code of the core module for the things to work + properly, unless you find bugs or flaws in it. Finally, do not forget to + read the V4L2 API for completeness. +*/ + +/*****************************************************************************/ + +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, + BRIDGE_SN9C105 = 0x08, + BRIDGE_SN9C120 = 0x10, +}; + +/* Return the bridge name */ +enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam); + +/* Return a pointer the sensor struct attached to the camera */ +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam); + +/* Identify a device */ +extern struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); + +/* Attach a probed sensor to the camera. */ +extern void +sn9c102_attach_sensor(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor); + +/* + Read/write routines: they always return -1 on error, 0 or the read value + otherwise. NOTE that a real read operation is not supported by the SN9C1XX + chip for some of its registers. To work around this problem, a pseudo-read + call is provided instead: it returns the last successfully written value + on the register (0 if it has never been written), the usual -1 on error. +*/ + +/* The "try" I2C I/O versions are used when probing the sensor */ +extern int sn9c102_i2c_try_read(struct sn9c102_device*, + const struct sn9c102_sensor*, u8 address); + +/* + These must be used if and only if the sensor doesn't implement the standard + I2C protocol. There are a number of good reasons why you must use the + single-byte versions of these functions: do not abuse. The first function + writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX + chip. The second one programs the registers 0x09 and 0x10 with data0 and + data1, and places the n bytes read from the sensor register table in the + buffer pointed by 'buffer'. Both the functions return -1 on error; the write + version returns 0 on success, while the read version returns the first read + byte. +*/ +extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 n, + u8 data0, u8 data1, u8 data2, u8 data3, + u8 data4, u8 data5); +extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, + u8 data0, u8 data1, u8 n, u8 buffer[]); + +/* To be used after the sensor struct has been attached to the camera struct */ +extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); +extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); + +/* I/O on registers in the bridge. Could be used by the sensor methods too */ +extern int sn9c102_read_reg(struct sn9c102_device*, u16 index); +extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); +extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); +extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], + int count); +/* + Write multiple registers with constant values. For example: + sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); + Register addresses must be < 256. +*/ +#define sn9c102_write_const_regs(sn9c102_device, data...) \ + ({ static const u8 _valreg[][2] = {data}; \ + sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) + +/*****************************************************************************/ + +enum sn9c102_i2c_sysfs_ops { + SN9C102_I2C_READ = 0x01, + SN9C102_I2C_WRITE = 0x02, +}; + +enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ + SN9C102_I2C_100KHZ = 0x01, + SN9C102_I2C_400KHZ = 0x02, +}; + +enum sn9c102_i2c_interface { + SN9C102_I2C_2WIRES, + SN9C102_I2C_3WIRES, +}; + +#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) + +struct sn9c102_sensor { + char name[32], /* sensor name */ + maintainer[64]; /* name of the maintainer */ + + enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ + + /* Supported operations through the 'sysfs' interface */ + enum sn9c102_i2c_sysfs_ops sysfs_ops; + + /* + These sensor capabilities must be provided if the SN9C1XX controller + needs to communicate through the sensor serial interface by using + at least one of the i2c functions available. + */ + enum sn9c102_i2c_frequency frequency; + enum sn9c102_i2c_interface interface; + + /* + This identifier must be provided if the image sensor implements + the standard I2C protocol. + */ + u8 i2c_slave_id; /* reg. 0x09 */ + + /* + NOTE: Where not noted,most of the functions below are not mandatory. + Set to null if you do not implement them. If implemented, + they must return 0 on success, the proper error otherwise. + */ + + int (*init)(struct sn9c102_device* cam); + /* + This function will be called after the sensor has been attached. + It should be used to initialize the sensor only, but may also + configure part of the SN9C1XX chip if necessary. You don't need to + setup picture settings like brightness, contrast, etc.. here, if + the corresponding controls are implemented (see below), since + they are adjusted in the core driver by calling the set_ctrl() + method after init(), where the arguments are the default values + specified in the v4l2_queryctrl list of supported controls; + Same suggestions apply for other settings, _if_ the corresponding + methods are present; if not, the initialization must configure the + sensor according to the default configuration structures below. + */ + + struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; + /* + Optional list of default controls, defined as indicated in the + V4L2 API. Menu type controls are not handled by this interface. + */ + + int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); + int (*set_ctrl)(struct sn9c102_device* cam, + const struct v4l2_control* ctrl); + /* + You must implement at least the set_ctrl method if you have defined + the list above. The returned value must follow the V4L2 + specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER + are not supported by this driver, so do not implement them. Also, + you don't have to check whether the passed values are out of bounds, + given that this is done by the core module. + */ + + struct v4l2_cropcap cropcap; + /* + Think the image sensor as a grid of R,G,B monochromatic pixels + disposed according to a particular Bayer pattern, which describes + the complete array of pixels, from (0,0) to (xmax, ymax). We will + use this coordinate system from now on. It is assumed the sensor + chip can be programmed to capture/transmit a subsection of that + array of pixels: we will call this subsection "active window". + It is not always true that the largest achievable active window can + cover the whole array of pixels. The V4L2 API defines another + area called "source rectangle", which, in turn, is a subrectangle of + the active window. The SN9C1XX chip is always programmed to read the + source rectangle. + The bounds of both the active window and the source rectangle are + specified in the cropcap substructures 'bounds' and 'defrect'. + By default, the source rectangle should cover the largest possible + area. Again, it is not always true that the largest source rectangle + can cover the entire active window, although it is a rare case for + the hardware we have. The bounds of the source rectangle _must_ be + multiple of 16 and must use the same coordinate system as indicated + before; their centers shall align initially. + If necessary, the sensor chip must be initialized during init() to + set the bounds of the active sensor window; however, by default, it + usually covers the largest achievable area (maxwidth x maxheight) + of pixels, so no particular initialization is needed, if you have + defined the correct default bounds in the structures. + See the V4L2 API for further details. + NOTE: once you have defined the bounds of the active window + (struct cropcap.bounds) you must not change them.anymore. + Only 'bounds' and 'defrect' fields are mandatory, other fields + will be ignored. + */ + + int (*set_crop)(struct sn9c102_device* cam, + const struct v4l2_rect* rect); + /* + To be called on VIDIOC_C_SETCROP. The core module always calls a + default routine which configures the appropriate SN9C1XX regs (also + scaling), but you may need to override/adjust specific stuff. + 'rect' contains width and height values that are multiple of 16: in + case you override the default function, you always have to program + the chip to match those values; on error return the corresponding + error code without rolling back. + NOTE: in case, you must program the SN9C1XX chip to get rid of + blank pixels or blank lines at the _start_ of each line or + frame after each HSYNC or VSYNC, so that the image starts with + real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, + V_SIZE you don't have to care about blank pixels or blank + lines at the end of each line or frame). + */ + + struct v4l2_pix_format pix_format; + /* + What you have to define here are: 1) initial 'width' and 'height' of + the target rectangle 2) the initial 'pixelformat', which can be + either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) + or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate + the number of bits per pixel for uncompressed video, 8 or 9 (despite + the current value of 'pixelformat'). + NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 + of cropcap.defrect.width and cropcap.defrect.height. I + suggest 1/1. + NOTE 2: The initial compression quality is defined by the first bit + of reg 0x17 during the initialization of the image sensor. + NOTE 3: as said above, you have to program the SN9C1XX chip to get + rid of any blank pixels, so that the output of the sensor + matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). + */ + + int (*set_pix_format)(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix); + /* + To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to + SN9C10X pixel format or viceversa. On error return the corresponding + error code without rolling back. + */ + + /* + Do NOT write to the data below, it's READ ONLY. It is used by the + core module to store successfully updated values of the above + settings, for rollbacks..etc..in case of errors during atomic I/O + */ + struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +/*****************************************************************************/ + +/* Private ioctl's for control settings supported by some image sensors */ +#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) +#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) +#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) +#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) +#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) +#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) +#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) + +#endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c new file mode 100644 index 000000000000..04cdfdde8564 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c @@ -0,0 +1,154 @@ +/*************************************************************************** + * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int tas5110c1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, + {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); + + err += sn9c102_i2c_write(cam, 0xc0, 0x80); + + return err; +} + + +static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5110c1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Don't change ! */ + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static const struct sn9c102_sensor tas5110c1b = { + .name = "TAS5110C1B", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5110c1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + }, + .set_ctrl = &tas5110c1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110c1b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110c1b_set_pix_format +}; + + +int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110c1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6001), }, + { USB_DEVICE(0x0c45, 0x6005), }, + { USB_DEVICE(0x0c45, 0x60ab), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5110c1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110c1b); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5110d.c b/drivers/media/usb/sn9c102/sn9c102_tas5110d.c new file mode 100644 index 000000000000..9372e6f9fcff --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_tas5110d.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int tas5110d_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); + + err += sn9c102_i2c_write(cam, 0x9a, 0xca); + + return err; +} + + +static int tas5110d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + + return err; +} + + +static int tas5110d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x3b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static const struct sn9c102_sensor tas5110d = { + .name = "TAS5110D", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x61, + .init = &tas5110d_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110d_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110d_set_pix_format +}; + + +int sn9c102_probe_tas5110d(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110d_id_table[] = { + { USB_DEVICE(0x0c45, 0x6007), }, + { } + }; + + if (!sn9c102_match_id(cam, tas5110d_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110d); + + return 0; +} diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c new file mode 100644 index 000000000000..a30bbc4389f5 --- /dev/null +++ b/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c @@ -0,0 +1,165 @@ +/*************************************************************************** + * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * This program is free software; 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 "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int tas5130d1b_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, + {0x04, 0x01}, {0x01, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x60, 0x17}, {0x07, 0x18}); + + return err; +} + + +static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5130d1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Do NOT change! */ + err += sn9c102_write_reg(cam, 0x1f, 0x1a); + err += sn9c102_write_reg(cam, 0x1a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x63, 0x19); + else + err += sn9c102_write_reg(cam, 0xf3, 0x19); + + return err; +} + + +static const struct sn9c102_sensor tas5130d1b = { + .name = "TAS5130D1B", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .set_ctrl = &tas5130d1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &tas5130d1b_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5130d1b_set_pix_format +}; + + +int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6024), }, + { USB_DEVICE(0x0c45, 0x6025), }, + { USB_DEVICE(0x0c45, 0x60aa), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5130d1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5130d1b); + + return 0; +} diff --git a/drivers/media/usb/stk1160/Kconfig b/drivers/media/usb/stk1160/Kconfig new file mode 100644 index 000000000000..1c3a1ec00237 --- /dev/null +++ b/drivers/media/usb/stk1160/Kconfig @@ -0,0 +1,20 @@ +config VIDEO_STK1160 + tristate "STK1160 USB video capture support" + depends on VIDEO_DEV && I2C + select VIDEOBUF2_VMALLOC + select VIDEO_SAA711X + + ---help--- + This is a video4linux driver for STK1160 based video capture devices. + + To compile this driver as a module, choose M here: the + module will be called stk1160 + +config VIDEO_STK1160_AC97 + bool "STK1160 AC97 codec support" + depends on VIDEO_STK1160 && SND + select SND_AC97_CODEC + + ---help--- + Enables AC97 codec support for stk1160 driver. +. diff --git a/drivers/media/usb/stk1160/Makefile b/drivers/media/usb/stk1160/Makefile new file mode 100644 index 000000000000..8a3c78482e73 --- /dev/null +++ b/drivers/media/usb/stk1160/Makefile @@ -0,0 +1,11 @@ +obj-stk1160-ac97-$(CONFIG_VIDEO_STK1160_AC97) := stk1160-ac97.o + +stk1160-y := stk1160-core.o \ + stk1160-v4l.o \ + stk1160-video.o \ + stk1160-i2c.o \ + $(obj-stk1160-ac97-y) + +obj-$(CONFIG_VIDEO_STK1160) += stk1160.o + +ccflags-y += -Idrivers/media/video diff --git a/drivers/media/usb/stk1160/stk1160-ac97.c b/drivers/media/usb/stk1160/stk1160-ac97.c new file mode 100644 index 000000000000..8d325f50c87b --- /dev/null +++ b/drivers/media/usb/stk1160/stk1160-ac97.c @@ -0,0 +1,153 @@ +/* + * STK1160 driver + * + * Copyright (C) 2012 Ezequiel Garcia + * + * + * Based on Easycap driver by R.M. Thomas + * Copyright (C) 2010 R.M. Thomas + * + * + * This program is free software; 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 "stk1160.h" +#include "stk1160-reg.h" + +static struct snd_ac97 *stk1160_ac97; + +static void stk1160_write_ac97(struct snd_ac97 *ac97, u16 reg, u16 value) +{ + struct stk1160 *dev = ac97->private_data; + + /* Set codec register address */ + stk1160_write_reg(dev, STK1160_AC97_ADDR, reg); + + /* Set codec command */ + stk1160_write_reg(dev, STK1160_AC97_CMD, value & 0xff); + stk1160_write_reg(dev, STK1160_AC97_CMD + 1, (value & 0xff00) >> 8); + + /* + * Set command write bit to initiate write operation. + * The bit will be cleared when transfer is done. + */ + stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8c); +} + +static u16 stk1160_read_ac97(struct snd_ac97 *ac97, u16 reg) +{ + struct stk1160 *dev = ac97->private_data; + u8 vall = 0; + u8 valh = 0; + + /* Set codec register address */ + stk1160_write_reg(dev, STK1160_AC97_ADDR, reg); + + /* + * Set command read bit to initiate read operation. + * The bit will be cleared when transfer is done. + */ + stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8b); + + /* Retrieve register value */ + stk1160_read_reg(dev, STK1160_AC97_CMD, &vall); + stk1160_read_reg(dev, STK1160_AC97_CMD + 1, &valh); + + return (valh << 8) | vall; +} + +static void stk1160_reset_ac97(struct snd_ac97 *ac97) +{ + struct stk1160 *dev = ac97->private_data; + /* Two-step reset AC97 interface and hardware codec */ + stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x94); + stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x88); + + /* Set 16-bit audio data and choose L&R channel*/ + stk1160_write_reg(dev, STK1160_AC97CTL_1 + 2, 0x01); +} + +static struct snd_ac97_bus_ops stk1160_ac97_ops = { + .read = stk1160_read_ac97, + .write = stk1160_write_ac97, + .reset = stk1160_reset_ac97, +}; + +int stk1160_ac97_register(struct stk1160 *dev) +{ + struct snd_card *card = NULL; + struct snd_ac97_bus *ac97_bus; + struct snd_ac97_template ac97_template; + int rc; + + /* + * Just want a card to access ac96 controls, + * the actual capture interface will be handled by snd-usb-audio + */ + rc = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (rc < 0) + return rc; + + snd_card_set_dev(card, dev->dev); + + /* TODO: I'm not sure where should I get these names :-( */ + snprintf(card->shortname, sizeof(card->shortname), + "stk1160-mixer"); + snprintf(card->longname, sizeof(card->longname), + "stk1160 ac97 codec mixer control"); + strncpy(card->driver, dev->dev->driver->name, sizeof(card->driver)); + + rc = snd_ac97_bus(card, 0, &stk1160_ac97_ops, NULL, &ac97_bus); + if (rc) + goto err; + + /* We must set private_data before calling snd_ac97_mixer */ + memset(&ac97_template, 0, sizeof(ac97_template)); + ac97_template.private_data = dev; + ac97_template.scaps = AC97_SCAP_SKIP_MODEM; + rc = snd_ac97_mixer(ac97_bus, &ac97_template, &stk1160_ac97); + if (rc) + goto err; + + dev->snd_card = card; + rc = snd_card_register(card); + if (rc) + goto err; + + return 0; + +err: + dev->snd_card = NULL; + if (card) + snd_card_free(card); + return rc; +} + +int stk1160_ac97_unregister(struct stk1160 *dev) +{ + struct snd_card *card = dev->snd_card; + + /* + * We need to check usb_device, + * because ac97 release attempts to communicate with codec + */ + if (card && dev->udev) + snd_card_free(card); + + return 0; +} diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c new file mode 100644 index 000000000000..74236fd3b7ec --- /dev/null +++ b/drivers/media/usb/stk1160/stk1160-core.c @@ -0,0 +1,432 @@ +/* + * STK1160 driver + * + * Copyright (C) 2012 Ezequiel Garcia + * + * + * Based on Easycap driver by R.M. Thomas + * Copyright (C) 2010 R.M. Thomas + * + * + * This program is free software; 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. + * + * TODO: + * + * 1. (Try to) detect if we must register ac97 mixer + * 2. Support stream at lower speed: lower frame rate or lower frame size. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "stk1160.h" +#include "stk1160-reg.h" + +static unsigned int input; +module_param(input, int, 0644); +MODULE_PARM_DESC(input, "Set default input"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ezequiel Garcia"); +MODULE_DESCRIPTION("STK1160 driver"); + +/* Devices supported by this driver */ +static struct usb_device_id stk1160_id_table[] = { + { USB_DEVICE(0x05e1, 0x0408) }, + { } +}; +MODULE_DEVICE_TABLE(usb, stk1160_id_table); + +/* saa7113 I2C address */ +static unsigned short saa7113_addrs[] = { + 0x4a >> 1, + I2C_CLIENT_END +}; + +/* + * Read/Write stk registers + */ +int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value) +{ + int ret; + int pipe = usb_rcvctrlpipe(dev->udev, 0); + + *value = 0; + ret = usb_control_msg(dev->udev, pipe, 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, value, sizeof(u8), HZ); + if (ret < 0) { + stk1160_err("read failed on reg 0x%x (%d)\n", + reg, ret); + return ret; + } + + return 0; +} + +int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value) +{ + int ret; + int pipe = usb_sndctrlpipe(dev->udev, 0); + + ret = usb_control_msg(dev->udev, pipe, 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, reg, NULL, 0, HZ); + if (ret < 0) { + stk1160_err("write failed on reg 0x%x (%d)\n", + reg, ret); + return ret; + } + + return 0; +} + +void stk1160_select_input(struct stk1160 *dev) +{ + static const u8 gctrl[] = { + 0x98, 0x90, 0x88, 0x80 + }; + + if (dev->ctl_input < ARRAY_SIZE(gctrl)) + stk1160_write_reg(dev, STK1160_GCTRL, gctrl[dev->ctl_input]); +} + +/* TODO: We should break this into pieces */ +static void stk1160_reg_reset(struct stk1160 *dev) +{ + int i; + + static const struct regval ctl[] = { + {STK1160_GCTRL+2, 0x0078}, + + {STK1160_RMCTL+1, 0x0000}, + {STK1160_RMCTL+3, 0x0002}, + + {STK1160_PLLSO, 0x0010}, + {STK1160_PLLSO+1, 0x0000}, + {STK1160_PLLSO+2, 0x0014}, + {STK1160_PLLSO+3, 0x000E}, + + {STK1160_PLLFD, 0x0046}, + + /* Timing generator setup */ + {STK1160_TIGEN, 0x0012}, + {STK1160_TICTL, 0x002D}, + {STK1160_TICTL+1, 0x0001}, + {STK1160_TICTL+2, 0x0000}, + {STK1160_TICTL+3, 0x0000}, + {STK1160_TIGEN, 0x0080}, + + {0xffff, 0xffff} + }; + + for (i = 0; ctl[i].reg != 0xffff; i++) + stk1160_write_reg(dev, ctl[i].reg, ctl[i].val); +} + +static void stk1160_release(struct v4l2_device *v4l2_dev) +{ + struct stk1160 *dev = container_of(v4l2_dev, struct stk1160, v4l2_dev); + + stk1160_info("releasing all resources\n"); + + stk1160_i2c_unregister(dev); + + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev->alt_max_pkt_size); + kfree(dev); +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +/* + * Scan usb interface and populate max_pkt_size array + * with information on each alternate setting. + * The array should be allocated by the caller. + */ +static int stk1160_scan_usb(struct usb_interface *intf, struct usb_device *udev, + unsigned int *max_pkt_size) +{ + int i, e, sizedescr, size, ifnum; + const struct usb_endpoint_descriptor *desc; + + bool has_video = false, has_audio = false; + const char *speed; + + ifnum = intf->altsetting[0].desc.bInterfaceNumber; + + /* Get endpoints */ + for (i = 0; i < intf->num_altsetting; i++) { + + for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) { + + /* This isn't clear enough, at least to me */ + desc = &intf->altsetting[i].endpoint[e].desc; + sizedescr = le16_to_cpu(desc->wMaxPacketSize); + size = sizedescr & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult(sizedescr); + + if (usb_endpoint_xfer_isoc(desc) && + usb_endpoint_dir_in(desc)) { + switch (desc->bEndpointAddress) { + case STK1160_EP_AUDIO: + has_audio = true; + break; + case STK1160_EP_VIDEO: + has_video = true; + max_pkt_size[i] = size; + break; + } + } + } + } + + /* Is this even possible? */ + if (!(has_audio || has_video)) { + dev_err(&udev->dev, "no audio or video endpoints found\n"); + return -ENODEV; + } + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + dev_info(&udev->dev, "New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n", + udev->manufacturer ? udev->manufacturer : "", + udev->product ? udev->product : "", + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + intf->altsetting->desc.bInterfaceNumber); + + /* This should never happen, since we rejected audio interfaces */ + if (has_audio) + dev_warn(&udev->dev, "audio interface %d found.\n\ + This is not implemented by this driver,\ + you should use snd-usb-audio instead\n", ifnum); + + if (has_video) + dev_info(&udev->dev, "video interface %d found\n", + ifnum); + + /* + * Make sure we have 480 Mbps of bandwidth, otherwise things like + * video stream wouldn't likely work, since 12 Mbps is generally + * not enough even for most streams. + */ + if (udev->speed != USB_SPEED_HIGH) + dev_warn(&udev->dev, "must be connected to a high-speed USB 2.0 port\n\ + You may not be able to stream video smoothly\n"); + + return 0; +} + +static int stk1160_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ifnum; + int rc = 0; + + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + struct usb_device *udev; + struct stk1160 *dev; + + ifnum = interface->altsetting[0].desc.bInterfaceNumber; + udev = interface_to_usbdev(interface); + + /* + * Since usb audio class is supported by snd-usb-audio, + * we reject audio interface. + */ + if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) + return -ENODEV; + + /* Alloc an array for all possible max_pkt_size */ + alt_max_pkt_size = kmalloc(sizeof(alt_max_pkt_size[0]) * + interface->num_altsetting, GFP_KERNEL); + if (alt_max_pkt_size == NULL) + return -ENOMEM; + + /* + * Scan usb posibilities and populate alt_max_pkt_size array. + * Also, check if device speed is fast enough. + */ + rc = stk1160_scan_usb(interface, udev, alt_max_pkt_size); + if (rc < 0) { + kfree(alt_max_pkt_size); + return rc; + } + + dev = kzalloc(sizeof(struct stk1160), GFP_KERNEL); + if (dev == NULL) { + kfree(alt_max_pkt_size); + return -ENOMEM; + } + + dev->alt_max_pkt_size = alt_max_pkt_size; + dev->udev = udev; + dev->num_alt = interface->num_altsetting; + dev->ctl_input = input; + + /* We save struct device for debug purposes only */ + dev->dev = &interface->dev; + + usb_set_intfdata(interface, dev); + + /* initialize videobuf2 stuff */ + rc = stk1160_vb2_setup(dev); + if (rc < 0) + goto free_err; + + /* + * There is no need to take any locks here in probe + * because we register the device node as the *last* thing. + */ + spin_lock_init(&dev->buf_lock); + mutex_init(&dev->v4l_lock); + mutex_init(&dev->vb_queue_lock); + + rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0); + if (rc) { + stk1160_err("v4l2_ctrl_handler_init failed (%d)\n", rc); + goto free_err; + } + + /* + * We obtain a v4l2_dev but defer + * registration of video device node as the last thing. + * There is no need to set the name if we give a device struct + */ + dev->v4l2_dev.release = stk1160_release; + dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; + rc = v4l2_device_register(dev->dev, &dev->v4l2_dev); + if (rc) { + stk1160_err("v4l2_device_register failed (%d)\n", rc); + goto free_ctrl; + } + + rc = stk1160_i2c_register(dev); + if (rc < 0) + goto unreg_v4l2; + + /* + * To the best of my knowledge stk1160 boards only have + * saa7113, but it doesn't hurt to support them all. + */ + dev->sd_saa7115 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "saa7115_auto", 0, saa7113_addrs); + + stk1160_info("driver ver %s successfully loaded\n", + STK1160_VERSION); + + /* i2c reset saa711x */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + 0, 0, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + + /* reset stk1160 to default values */ + stk1160_reg_reset(dev); + + /* select default input */ + stk1160_select_input(dev); + + stk1160_ac97_register(dev); + + rc = stk1160_video_register(dev); + if (rc < 0) + goto unreg_i2c; + + return 0; + +unreg_i2c: + stk1160_i2c_unregister(dev); +unreg_v4l2: + v4l2_device_unregister(&dev->v4l2_dev); +free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); +free_err: + kfree(alt_max_pkt_size); + kfree(dev); + + return rc; +} + +static void stk1160_disconnect(struct usb_interface *interface) +{ + struct stk1160 *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* + * Wait until all current v4l2 operation are finished + * then deallocate resources + */ + mutex_lock(&dev->vb_queue_lock); + mutex_lock(&dev->v4l_lock); + + /* Here is the only place where isoc get released */ + stk1160_uninit_isoc(dev); + + /* ac97 unregister needs to be done before usb_device is cleared */ + stk1160_ac97_unregister(dev); + + stk1160_clear_queue(dev); + + video_unregister_device(&dev->vdev); + v4l2_device_disconnect(&dev->v4l2_dev); + + /* This way current users can detect device is gone */ + dev->udev = NULL; + + mutex_unlock(&dev->v4l_lock); + mutex_unlock(&dev->vb_queue_lock); + + /* + * This calls stk1160_release if it's the last reference. + * therwise, release is posponed until there are no users left. + */ + v4l2_device_put(&dev->v4l2_dev); +} + +static struct usb_driver stk1160_usb_driver = { + .name = "stk1160", + .id_table = stk1160_id_table, + .probe = stk1160_probe, + .disconnect = stk1160_disconnect, +}; + +module_usb_driver(stk1160_usb_driver); diff --git a/drivers/media/usb/stk1160/stk1160-i2c.c b/drivers/media/usb/stk1160/stk1160-i2c.c new file mode 100644 index 000000000000..176ac937306b --- /dev/null +++ b/drivers/media/usb/stk1160/stk1160-i2c.c @@ -0,0 +1,294 @@ +/* + * STK1160 driver + * + * Copyright (C) 2012 Ezequiel Garcia + * + * + * Based on Easycap driver by R.M. Thomas + * Copyright (C) 2010 R.M. Thomas + * + * + * This program is free software; 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 "stk1160.h" +#include "stk1160-reg.h" + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define dprintk_i2c(fmt, args...) \ +do { \ + if (i2c_debug) \ + printk(KERN_DEBUG fmt, ##args); \ +} while (0) + +static int stk1160_i2c_busy_wait(struct stk1160 *dev, u8 wait_bit_mask) +{ + unsigned long end; + u8 flag; + + /* Wait until read/write finish bit is set */ + end = jiffies + msecs_to_jiffies(STK1160_I2C_TIMEOUT); + while (time_is_after_jiffies(end)) { + + stk1160_read_reg(dev, STK1160_SICTL+1, &flag); + /* read/write done? */ + if (flag & wait_bit_mask) + goto done; + + usleep_range(10 * USEC_PER_MSEC, 20 * USEC_PER_MSEC); + } + + return -ETIMEDOUT; + +done: + return 0; +} + +static int stk1160_i2c_write_reg(struct stk1160 *dev, u8 addr, + u8 reg, u8 value) +{ + int rc; + + /* Set serial device address */ + rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr); + if (rc < 0) + return rc; + + /* Set i2c device register sub-address */ + rc = stk1160_write_reg(dev, STK1160_SBUSW_WA, reg); + if (rc < 0) + return rc; + + /* Set i2c device register value */ + rc = stk1160_write_reg(dev, STK1160_SBUSW_WD, value); + if (rc < 0) + return rc; + + /* Start write now */ + rc = stk1160_write_reg(dev, STK1160_SICTL, 0x01); + if (rc < 0) + return rc; + + rc = stk1160_i2c_busy_wait(dev, 0x04); + if (rc < 0) + return rc; + + return 0; +} + +static int stk1160_i2c_read_reg(struct stk1160 *dev, u8 addr, + u8 reg, u8 *value) +{ + int rc; + + /* Set serial device address */ + rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr); + if (rc < 0) + return rc; + + /* Set i2c device register sub-address */ + rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, reg); + if (rc < 0) + return rc; + + /* Start read now */ + rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20); + if (rc < 0) + return rc; + + rc = stk1160_i2c_busy_wait(dev, 0x01); + if (rc < 0) + return rc; + + stk1160_read_reg(dev, STK1160_SBUSR_RD, value); + if (rc < 0) + return rc; + + return 0; +} + +/* + * stk1160_i2c_check_for_device() + * check if there is a i2c_device at the supplied address + */ +static int stk1160_i2c_check_for_device(struct stk1160 *dev, + unsigned char addr) +{ + int rc; + + /* Set serial device address */ + rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr); + if (rc < 0) + return rc; + + /* Set device sub-address, we'll chip version reg */ + rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, 0x00); + if (rc < 0) + return rc; + + /* Start read now */ + rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20); + if (rc < 0) + return rc; + + rc = stk1160_i2c_busy_wait(dev, 0x01); + if (rc < 0) + return -ENODEV; + + return 0; +} + +/* + * stk1160_i2c_xfer() + * the main i2c transfer function + */ +static int stk1160_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct stk1160 *dev = i2c_adap->algo_data; + int addr, rc, i; + + for (i = 0; i < num; i++) { + addr = msgs[i].addr << 1; + dprintk_i2c("%s: addr=%x", __func__, addr); + + if (!msgs[i].len) { + /* no len: check only for device presence */ + rc = stk1160_i2c_check_for_device(dev, addr); + if (rc < 0) { + dprintk_i2c(" no device\n"); + return rc; + } + + } else if (msgs[i].flags & I2C_M_RD) { + /* read request without preceding register selection */ + dprintk_i2c(" subaddr not selected"); + rc = -EOPNOTSUPP; + goto err; + + } else if (i + 1 < num && msgs[i].len <= 2 && + (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + + if (msgs[i].len != 1 || msgs[i + 1].len != 1) { + dprintk_i2c(" len not supported"); + rc = -EOPNOTSUPP; + goto err; + } + + dprintk_i2c(" subaddr=%x", msgs[i].buf[0]); + + rc = stk1160_i2c_read_reg(dev, addr, msgs[i].buf[0], + msgs[i + 1].buf); + + dprintk_i2c(" read=%x", *msgs[i + 1].buf); + + /* consumed two msgs, so we skip one of them */ + i++; + + } else { + if (msgs[i].len != 2) { + dprintk_i2c(" len not supported"); + rc = -EOPNOTSUPP; + goto err; + } + + dprintk_i2c(" subaddr=%x write=%x", + msgs[i].buf[0], msgs[i].buf[1]); + + rc = stk1160_i2c_write_reg(dev, addr, msgs[i].buf[0], + msgs[i].buf[1]); + } + + if (rc < 0) + goto err; + dprintk_i2c(" OK\n"); + } + + return num; +err: + dprintk_i2c(" ERROR: %d\n", rc); + return num; +} + +/* + * functionality(), what da heck is this? + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm algo = { + .master_xfer = stk1160_i2c_xfer, + .functionality = functionality, +}; + +static struct i2c_adapter adap_template = { + .owner = THIS_MODULE, + .name = "stk1160", + .algo = &algo, +}; + +static struct i2c_client client_template = { + .name = "stk1160 internal", +}; + +/* + * stk1160_i2c_register() + * register i2c bus + */ +int stk1160_i2c_register(struct stk1160 *dev) +{ + int rc; + + dev->i2c_adap = adap_template; + dev->i2c_adap.dev.parent = dev->dev; + strcpy(dev->i2c_adap.name, "stk1160"); + dev->i2c_adap.algo_data = dev; + + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + + rc = i2c_add_adapter(&dev->i2c_adap); + if (rc < 0) { + stk1160_err("cannot add i2c adapter (%d)\n", rc); + return rc; + } + + dev->i2c_client = client_template; + dev->i2c_client.adapter = &dev->i2c_adap; + + /* Set i2c clock divider device address */ + stk1160_write_reg(dev, STK1160_SICTL_CD, 0x0f); + + /* ??? */ + stk1160_write_reg(dev, STK1160_ASIC + 3, 0x00); + + return 0; +} + +/* + * stk1160_i2c_unregister() + * unregister i2c_bus + */ +int stk1160_i2c_unregister(struct stk1160 *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/drivers/media/usb/stk1160/stk1160-reg.h b/drivers/media/usb/stk1160/stk1160-reg.h new file mode 100644 index 000000000000..3e49da6e7edd --- /dev/null +++ b/drivers/media/usb/stk1160/stk1160-reg.h @@ -0,0 +1,93 @@ +/* + * STK1160 driver + * + * Copyright (C) 2012 Ezequiel Garcia + * + * + * Based on Easycap driver by R.M. Thomas + * Copyright (C) 2010 R.M. Thomas + * + * + * This program is free software; 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. + * + */ + +/* GPIO Control */ +#define STK1160_GCTRL 0x000 + +/* Remote Wakup Control */ +#define STK1160_RMCTL 0x00c + +/* + * Decoder Control Register: + * This byte controls capture start/stop + * with bit #7 (0x?? OR 0x80 to activate). + */ +#define STK1160_DCTRL 0x100 + +/* Capture Frame Start Position */ +#define STK116_CFSPO 0x110 +#define STK116_CFSPO_STX_L 0x110 +#define STK116_CFSPO_STX_H 0x111 +#define STK116_CFSPO_STY_L 0x112 +#define STK116_CFSPO_STY_H 0x113 + +/* Capture Frame End Position */ +#define STK116_CFEPO 0x114 +#define STK116_CFEPO_ENX_L 0x114 +#define STK116_CFEPO_ENX_H 0x115 +#define STK116_CFEPO_ENY_L 0x116 +#define STK116_CFEPO_ENY_H 0x117 + +/* Serial Interface Control */ +#define STK1160_SICTL 0x200 +#define STK1160_SICTL_CD 0x202 +#define STK1160_SICTL_SDA 0x203 + +/* Serial Bus Write */ +#define STK1160_SBUSW 0x204 +#define STK1160_SBUSW_WA 0x204 +#define STK1160_SBUSW_WD 0x205 + +/* Serial Bus Read */ +#define STK1160_SBUSR 0x208 +#define STK1160_SBUSR_RA 0x208 +#define STK1160_SBUSR_RD 0x209 + +/* Alternate Serial Inteface Control */ +#define STK1160_ASIC 0x2fc + +/* PLL Select Options */ +#define STK1160_PLLSO 0x018 + +/* PLL Frequency Divider */ +#define STK1160_PLLFD 0x01c + +/* Timing Generator */ +#define STK1160_TIGEN 0x300 + +/* Timing Control Parameter */ +#define STK1160_TICTL 0x350 + +/* AC97 Audio Control */ +#define STK1160_AC97CTL_0 0x500 +#define STK1160_AC97CTL_1 0x504 + +/* Use [0:6] bits of register 0x504 to set codec command address */ +#define STK1160_AC97_ADDR 0x504 +/* Use [16:31] bits of register 0x500 to set codec command data */ +#define STK1160_AC97_CMD 0x502 + +/* Audio I2S Interface */ +#define STK1160_I2SCTL 0x50c + +/* EEPROM Interface */ +#define STK1160_EEPROM_SZ 0x5f0 diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c new file mode 100644 index 000000000000..360bdbee4270 --- /dev/null +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -0,0 +1,738 @@ +/* + * STK1160 driver + * + * Copyright (C) 2012 Ezequiel Garcia + * + * + * Based on Easycap driver by R.M. Thomas + * Copyright (C) 2010 R.M. Thomas + * + * + * This program is free software; 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "stk1160.h" +#include "stk1160-reg.h" + +static unsigned int vidioc_debug; +module_param(vidioc_debug, int, 0644); +MODULE_PARM_DESC(vidioc_debug, "enable debug messages [vidioc]"); + +static bool keep_buffers; +module_param(keep_buffers, bool, 0644); +MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); + +/* supported video standards */ +static struct stk1160_fmt format[] = { + { + .name = "16 bpp YUY2, 4:2:2, packed", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + } +}; + +static void stk1160_set_std(struct stk1160 *dev) +{ + int i; + + static struct regval std525[] = { + + /* 720x480 */ + + /* Frame start */ + {STK116_CFSPO_STX_L, 0x0000}, + {STK116_CFSPO_STX_H, 0x0000}, + {STK116_CFSPO_STY_L, 0x0003}, + {STK116_CFSPO_STY_H, 0x0000}, + + /* Frame end */ + {STK116_CFEPO_ENX_L, 0x05a0}, + {STK116_CFEPO_ENX_H, 0x0005}, + {STK116_CFEPO_ENY_L, 0x00f3}, + {STK116_CFEPO_ENY_H, 0x0000}, + + {0xffff, 0xffff} + }; + + static struct regval std625[] = { + + /* 720x576 */ + + /* TODO: Each line of frame has some junk at the end */ + /* Frame start */ + {STK116_CFSPO, 0x0000}, + {STK116_CFSPO+1, 0x0000}, + {STK116_CFSPO+2, 0x0001}, + {STK116_CFSPO+3, 0x0000}, + + /* Frame end */ + {STK116_CFEPO, 0x05a0}, + {STK116_CFEPO+1, 0x0005}, + {STK116_CFEPO+2, 0x0121}, + {STK116_CFEPO+3, 0x0001}, + + {0xffff, 0xffff} + }; + + if (dev->norm & V4L2_STD_525_60) { + stk1160_dbg("registers to NTSC like standard\n"); + for (i = 0; std525[i].reg != 0xffff; i++) + stk1160_write_reg(dev, std525[i].reg, std525[i].val); + } else { + stk1160_dbg("registers to PAL like standard\n"); + for (i = 0; std625[i].reg != 0xffff; i++) + stk1160_write_reg(dev, std625[i].reg, std625[i].val); + } + +} + +/* + * Set a new alternate setting. + * Returns true is dev->max_pkt_size has changed, false otherwise. + */ +static bool stk1160_set_alternate(struct stk1160 *dev) +{ + int i, prev_alt = dev->alt; + unsigned int min_pkt_size; + bool new_pkt_size; + + /* + * If we don't set right alternate, + * then we will get a green screen with junk. + */ + min_pkt_size = STK1160_MIN_PKT_SIZE; + + for (i = 0; i < dev->num_alt; i++) { + /* stop when the selected alt setting offers enough bandwidth */ + if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + dev->alt = i; + break; + /* + * otherwise make sure that we end up with the maximum bandwidth + * because the min_pkt_size equation might be wrong... + */ + } else if (dev->alt_max_pkt_size[i] > + dev->alt_max_pkt_size[dev->alt]) + dev->alt = i; + } + + stk1160_info("setting alternate %d\n", dev->alt); + + if (dev->alt != prev_alt) { + stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + stk1160_dbg("setting alt %d with wMaxPacketSize=%u\n", + dev->alt, dev->alt_max_pkt_size[dev->alt]); + usb_set_interface(dev->udev, 0, dev->alt); + } + + new_pkt_size = dev->max_pkt_size != dev->alt_max_pkt_size[dev->alt]; + dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; + + return new_pkt_size; +} + +static int stk1160_start_streaming(struct stk1160 *dev) +{ + int i, rc; + bool new_pkt_size; + + /* Check device presence */ + if (!dev->udev) + return -ENODEV; + + if (mutex_lock_interruptible(&dev->v4l_lock)) + return -ERESTARTSYS; + /* + * For some reason it is mandatory to set alternate *first* + * and only *then* initialize isoc urbs. + * Someone please explain me why ;) + */ + new_pkt_size = stk1160_set_alternate(dev); + + /* + * We (re)allocate isoc urbs if: + * there is no allocated isoc urbs, OR + * a new dev->max_pkt_size is detected + */ + if (!dev->isoc_ctl.num_bufs || new_pkt_size) { + rc = stk1160_alloc_isoc(dev); + if (rc < 0) + return rc; + } + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL); + if (rc) { + stk1160_err("cannot submit urb[%d] (%d)\n", i, rc); + stk1160_uninit_isoc(dev); + return rc; + } + } + + /* Start saa711x */ + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + + /* Start stk1160 */ + stk1160_write_reg(dev, STK1160_DCTRL, 0xb3); + stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); + + stk1160_dbg("streaming started\n"); + + mutex_unlock(&dev->v4l_lock); + + return 0; +} + +/* Must be called with v4l_lock hold */ +static void stk1160_stop_hw(struct stk1160 *dev) +{ + /* If the device is not physically present, there is nothing to do */ + if (!dev->udev) + return; + + /* set alternate 0 */ + dev->alt = 0; + stk1160_info("setting alternate %d\n", dev->alt); + usb_set_interface(dev->udev, 0, 0); + + /* Stop stk1160 */ + stk1160_write_reg(dev, STK1160_DCTRL, 0x00); + stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); + + /* Stop saa711x */ + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); +} + +static int stk1160_stop_streaming(struct stk1160 *dev) +{ + if (mutex_lock_interruptible(&dev->v4l_lock)) + return -ERESTARTSYS; + + stk1160_cancel_isoc(dev); + + /* + * It is possible to keep buffers around using a module parameter. + * This is intended to avoid memory fragmentation. + */ + if (!keep_buffers) + stk1160_free_isoc(dev); + + stk1160_stop_hw(dev); + + stk1160_clear_queue(dev); + + stk1160_dbg("streaming stopped\n"); + + mutex_unlock(&dev->v4l_lock); + + return 0; +} + +static struct v4l2_file_operations stk1160_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +/* + * vidioc ioctls + */ +static int vidioc_querycap(struct file *file, + void *priv, struct v4l2_capability *cap) +{ + struct stk1160 *dev = video_drvdata(file); + + strcpy(cap->driver, "stk1160"); + strcpy(cap->card, "stk1160"); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; + + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct stk1160 *dev = video_drvdata(file); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.pixelformat = dev->fmt->fourcc; + f->fmt.pix.bytesperline = dev->width * 2; + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct stk1160 *dev = video_drvdata(file); + + if (f->fmt.pix.pixelformat != format[0].fourcc) { + stk1160_err("fourcc format 0x%08x invalid\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + /* + * User can't choose size at his own will, + * so we just return him the current size chosen + * at standard selection. + * TODO: Implement frame scaling? + */ + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.bytesperline = dev->width * 2; + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct stk1160 *dev = video_drvdata(file); + struct vb2_queue *q = &dev->vb_vidq; + int rc; + + if (vb2_is_busy(q)) + return -EBUSY; + + rc = vidioc_try_fmt_vid_cap(file, priv, f); + if (rc < 0) + return rc; + + /* We don't support any format changes */ + + return 0; +} + +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct stk1160 *dev = video_drvdata(file); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct stk1160 *dev = video_drvdata(file); + + *norm = dev->norm; + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct stk1160 *dev = video_drvdata(file); + struct vb2_queue *q = &dev->vb_vidq; + + if (vb2_is_busy(q)) + return -EBUSY; + + /* Check device presence */ + if (!dev->udev) + return -ENODEV; + + /* We need to set this now, before we call stk1160_set_std */ + dev->norm = *norm; + + /* This is taken from saa7115 video decoder */ + if (dev->norm & V4L2_STD_525_60) { + dev->width = 720; + dev->height = 480; + } else if (dev->norm & V4L2_STD_625_50) { + dev->width = 720; + dev->height = 576; + } else { + stk1160_err("invalid standard\n"); + return -EINVAL; + } + + stk1160_set_std(dev); + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, + dev->norm); + + return 0; +} + + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct stk1160 *dev = video_drvdata(file); + + if (i->index > STK1160_MAX_INPUT) + return -EINVAL; + + sprintf(i->name, "Composite%d", i->index); + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = dev->vdev.tvnorms; + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct stk1160 *dev = video_drvdata(file); + *i = dev->ctl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct stk1160 *dev = video_drvdata(file); + + if (vb2_is_busy(&dev->vb_vidq)) + return -EBUSY; + + if (i > STK1160_MAX_INPUT) + return -EINVAL; + + dev->ctl_input = i; + + stk1160_select_input(dev); + + return 0; +} + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + return 0; + default: + return -EINVAL; + } +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct stk1160 *dev = video_drvdata(file); + int rc; + u8 val; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_AC97: + /* TODO: Support me please :-( */ + return -EINVAL; + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + /* Match host */ + rc = stk1160_read_reg(dev, reg->reg, &val); + reg->val = val; + reg->size = 1; + + return rc; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct stk1160 *dev = video_drvdata(file); + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_AC97: + return -EINVAL; + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + /* Match host */ + return stk1160_write_reg(dev, reg->reg, cpu_to_le16(reg->val)); +} +#endif + +static const struct v4l2_ioctl_ops stk1160_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_querystd = vidioc_querystd, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + + /* vb2 takes care of these */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_chip_ident = vidioc_g_chip_ident, + +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +/********************************************************************/ + +/* + * Videobuf2 operations + */ +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct stk1160 *dev = vb2_get_drv_priv(vq); + unsigned long size; + + size = dev->width * dev->height * 2; + + /* + * Here we can change the number of buffers being requested. + * So, we set a minimum and a maximum like this: + */ + *nbuffers = clamp_t(unsigned int, *nbuffers, + STK1160_MIN_VIDEO_BUFFERS, STK1160_MAX_VIDEO_BUFFERS); + + /* This means a packed colorformat */ + *nplanes = 1; + + sizes[0] = size; + + stk1160_info("%s: buffer count %d, each %ld bytes\n", + __func__, *nbuffers, size); + + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + unsigned long flags; + struct stk1160 *dev = vb2_get_drv_priv(vb->vb2_queue); + struct stk1160_buffer *buf = + container_of(vb, struct stk1160_buffer, vb); + + spin_lock_irqsave(&dev->buf_lock, flags); + if (!dev->udev) { + /* + * If the device is disconnected return the buffer to userspace + * directly. The next QBUF call will fail with -ENODEV. + */ + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } else { + + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + buf->bytesused = 0; + buf->pos = 0; + + /* + * If buffer length is less from expected then we return + * the buffer to userspace directly. + */ + if (buf->length < dev->width * dev->height * 2) + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + else + list_add_tail(&buf->list, &dev->avail_bufs); + + } + spin_unlock_irqrestore(&dev->buf_lock, flags); +} + +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct stk1160 *dev = vb2_get_drv_priv(vq); + return stk1160_start_streaming(dev); +} + +/* abort streaming and wait for last buffer */ +static int stop_streaming(struct vb2_queue *vq) +{ + struct stk1160 *dev = vb2_get_drv_priv(vq); + return stk1160_stop_streaming(dev); +} + +static struct vb2_ops stk1160_video_qops = { + .queue_setup = queue_setup, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static struct video_device v4l_template = { + .name = "stk1160", + .tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50, + .fops = &stk1160_fops, + .ioctl_ops = &stk1160_ioctl_ops, + .release = video_device_release_empty, +}; + +/********************************************************************/ + +/* Must be called with both v4l_lock and vb_queue_lock hold */ +void stk1160_clear_queue(struct stk1160 *dev) +{ + struct stk1160_buffer *buf; + unsigned long flags; + + /* Release all active buffers */ + spin_lock_irqsave(&dev->buf_lock, flags); + while (!list_empty(&dev->avail_bufs)) { + buf = list_first_entry(&dev->avail_bufs, + struct stk1160_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + stk1160_info("buffer [%p/%d] aborted\n", + buf, buf->vb.v4l2_buf.index); + } + /* It's important to clear current buffer */ + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->buf_lock, flags); +} + +int stk1160_vb2_setup(struct stk1160 *dev) +{ + int rc; + struct vb2_queue *q; + + q = &dev->vb_vidq; + memset(q, 0, sizeof(dev->vb_vidq)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct stk1160_buffer); + q->ops = &stk1160_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + /* initialize video dma queue */ + INIT_LIST_HEAD(&dev->avail_bufs); + + return 0; +} + +int stk1160_video_register(struct stk1160 *dev) +{ + int rc; + + /* Initialize video_device with a template structure */ + dev->vdev = v4l_template; + dev->vdev.debug = vidioc_debug; + dev->vdev.queue = &dev->vb_vidq; + + /* + * Provide mutexes for v4l2 core and for videobuf2 queue. + * It will be used to protect *only* v4l2 ioctls. + */ + dev->vdev.lock = &dev->v4l_lock; + dev->vdev.queue->lock = &dev->vb_queue_lock; + + /* This will be used to set video_device parent */ + dev->vdev.v4l2_dev = &dev->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); + + /* NTSC is default */ + dev->norm = V4L2_STD_NTSC_M; + dev->width = 720; + dev->height = 480; + + /* set default format */ + dev->fmt = &format[0]; + stk1160_set_std(dev); + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, + dev->norm); + + video_set_drvdata(&dev->vdev, dev); + rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + if (rc < 0) { + stk1160_err("video_register_device failed (%d)\n", rc); + return rc; + } + + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(&dev->vdev)); + + return 0; +} diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c new file mode 100644 index 000000000000..378526981146 --- /dev/null +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -0,0 +1,518 @@ +/* + * STK1160 driver + * + * Copyright (C) 2012 Ezequiel Garcia + * + * + * Based on Easycap driver by R.M. Thomas + * Copyright (C) 2010 R.M. Thomas + * + * + * This program is free software; 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 "stk1160.h" + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +static inline void print_err_status(struct stk1160 *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + + if (packet < 0) + printk_ratelimited(KERN_WARNING "URB status %d [%s].\n", + status, errmsg); + else + printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s].\n", + packet, status, errmsg); +} + +static inline +struct stk1160_buffer *stk1160_next_buffer(struct stk1160 *dev) +{ + struct stk1160_buffer *buf = NULL; + unsigned long flags = 0; + + /* Current buffer must be NULL when this functions gets called */ + BUG_ON(dev->isoc_ctl.buf); + + spin_lock_irqsave(&dev->buf_lock, flags); + if (!list_empty(&dev->avail_bufs)) { + buf = list_first_entry(&dev->avail_bufs, + struct stk1160_buffer, list); + list_del(&buf->list); + } + spin_unlock_irqrestore(&dev->buf_lock, flags); + + return buf; +} + +static inline +void stk1160_buffer_done(struct stk1160 *dev) +{ + struct stk1160_buffer *buf = dev->isoc_ctl.buf; + + dev->field_count++; + + buf->vb.v4l2_buf.sequence = dev->field_count >> 1; + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + buf->vb.v4l2_buf.bytesused = buf->bytesused; + do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + + vb2_set_plane_payload(&buf->vb, 0, buf->bytesused); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + + dev->isoc_ctl.buf = NULL; +} + +static inline +void stk1160_copy_video(struct stk1160 *dev, u8 *src, int len) +{ + int linesdone, lineoff, lencopy; + int bytesperline = dev->width * 2; + struct stk1160_buffer *buf = dev->isoc_ctl.buf; + u8 *dst = buf->mem; + int remain; + + /* + * TODO: These stk1160_dbg are very spammy! + * We should 1) check why we are getting them + * and 2) add ratelimit. + * + * UPDATE: One of the reasons (the only one?) for getting these + * is incorrect standard (mismatch between expected and configured). + * So perhaps, we could add a counter for errors. When the counter + * reaches some value, we simply stop streaming. + */ + + len -= 4; + src += 4; + + remain = len; + + linesdone = buf->pos / bytesperline; + lineoff = buf->pos % bytesperline; /* offset in current line */ + + if (!buf->odd) + dst += bytesperline; + + /* Multiply linesdone by two, to take account of the other field */ + dst += linesdone * bytesperline * 2 + lineoff; + + /* Copy the remaining of current line */ + if (remain < (bytesperline - lineoff)) + lencopy = remain; + else + lencopy = bytesperline - lineoff; + + /* + * Check if we have enough space left in the buffer. + * In that case, we force loop exit after copy. + */ + if (lencopy > buf->bytesused - buf->length) { + lencopy = buf->bytesused - buf->length; + remain = lencopy; + } + + /* Check if the copy is done */ + if (lencopy == 0 || remain == 0) + return; + + /* Let the bug hunt begin! sanity checks! */ + if (lencopy < 0) { + stk1160_dbg("copy skipped: negative lencopy\n"); + return; + } + + if ((unsigned long)dst + lencopy > + (unsigned long)buf->mem + buf->length) { + printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n"); + return; + } + + memcpy(dst, src, lencopy); + + buf->bytesused += lencopy; + buf->pos += lencopy; + remain -= lencopy; + + /* Copy current field line by line, interlacing with the other field */ + while (remain > 0) { + + dst += lencopy + bytesperline; + src += lencopy; + + /* Copy one line at a time */ + if (remain < bytesperline) + lencopy = remain; + else + lencopy = bytesperline; + + /* + * Check if we have enough space left in the buffer. + * In that case, we force loop exit after copy. + */ + if (lencopy > buf->bytesused - buf->length) { + lencopy = buf->bytesused - buf->length; + remain = lencopy; + } + + /* Check if the copy is done */ + if (lencopy == 0 || remain == 0) + return; + + if (lencopy < 0) { + printk_ratelimited(KERN_WARNING "stk1160: negative lencopy detected\n"); + return; + } + + if ((unsigned long)dst + lencopy > + (unsigned long)buf->mem + buf->length) { + printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n"); + return; + } + + memcpy(dst, src, lencopy); + remain -= lencopy; + + buf->bytesused += lencopy; + buf->pos += lencopy; + } +} + +/* + * Controls the isoc copy of each urb packet + */ +static void stk1160_process_isoc(struct stk1160 *dev, struct urb *urb) +{ + int i, len, status; + u8 *p; + + if (!dev) { + stk1160_warn("%s called with null device\n", __func__); + return; + } + + if (urb->status < 0) { + /* Print status and drop current packet (or field?) */ + print_err_status(dev, -1, urb->status); + return; + } + + for (i = 0; i < urb->number_of_packets; i++) { + status = urb->iso_frame_desc[i].status; + if (status < 0) { + print_err_status(dev, i, status); + continue; + } + + /* Get packet actual length and pointer to data */ + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + len = urb->iso_frame_desc[i].actual_length; + + /* Empty packet */ + if (len <= 4) + continue; + + /* + * An 8-byte packet sequence means end of field. + * So if we don't have any packet, we start receiving one now + * and if we do have a packet, then we are done with it. + * + * These end of field packets are always 0xc0 or 0x80, + * but not always 8-byte long so we don't check packet length. + */ + if (p[0] == 0xc0) { + + /* + * If first byte is 0xc0 then we received + * second field, and frame has ended. + */ + if (dev->isoc_ctl.buf != NULL) + stk1160_buffer_done(dev); + + dev->isoc_ctl.buf = stk1160_next_buffer(dev); + if (dev->isoc_ctl.buf == NULL) + return; + } + + /* + * If we don't have a buffer here, then it means we + * haven't found the start mark sequence. + */ + if (dev->isoc_ctl.buf == NULL) + continue; + + if (p[0] == 0xc0 || p[0] == 0x80) { + + /* We set next packet parity and + * continue to get next one + */ + dev->isoc_ctl.buf->odd = *p & 0x40; + dev->isoc_ctl.buf->pos = 0; + continue; + } + + stk1160_copy_video(dev, p, len); + } +} + + +/* + * IRQ callback, called by URB callback + */ +static void stk1160_isoc_irq(struct urb *urb) +{ + int i, rc; + struct stk1160 *dev = urb->context; + + switch (urb->status) { + case 0: + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + /* TODO: check uvc driver: he frees the queue here */ + return; + default: + stk1160_err("urb error! status %d\n", urb->status); + return; + } + + stk1160_process_isoc(dev, urb); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc) + stk1160_err("urb re-submit failed (%d)\n", rc); +} + +/* + * Cancel urbs + * This function can't be called in atomic context + */ +void stk1160_cancel_isoc(struct stk1160 *dev) +{ + int i; + + /* + * This check is not necessary, but we add it + * to avoid a spurious debug message + */ + if (!dev->isoc_ctl.num_bufs) + return; + + stk1160_dbg("killing urbs...\n"); + + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + + /* + * To kill urbs we can't be in atomic context. + * We don't care for NULL pointer since + * usb_kill_urb allows it. + */ + usb_kill_urb(dev->isoc_ctl.urb[i]); + } + + stk1160_dbg("all urbs killed\n"); +} + +/* + * Releases urb and transfer buffers + * Obviusly, associated urb must be killed before releasing it. + */ +void stk1160_free_isoc(struct stk1160 *dev) +{ + struct urb *urb; + int i; + + stk1160_dbg("freeing urb buffers...\n"); + + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + + urb = dev->isoc_ctl.urb[i]; + if (urb) { + + if (dev->isoc_ctl.transfer_buffer[i]) { +#ifndef CONFIG_DMA_NONCOHERENT + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); +#else + kfree(dev->isoc_ctl.transfer_buffer[i]); +#endif + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; + + stk1160_dbg("all urb buffers freed\n"); +} + +/* + * Helper for cancelling and freeing urbs + * This function can't be called in atomic context + */ +void stk1160_uninit_isoc(struct stk1160 *dev) +{ + stk1160_cancel_isoc(dev); + stk1160_free_isoc(dev); +} + +/* + * Allocate URBs + */ +int stk1160_alloc_isoc(struct stk1160 *dev) +{ + struct urb *urb; + int i, j, k, sb_size, max_packets, num_bufs; + + /* + * It may be necessary to release isoc here, + * since isoc are only released on disconnection. + * (see new_pkt_size flag) + */ + if (dev->isoc_ctl.num_bufs) + stk1160_uninit_isoc(dev); + + stk1160_dbg("allocating urbs...\n"); + + num_bufs = STK1160_NUM_BUFS; + max_packets = STK1160_NUM_PACKETS; + sb_size = max_packets * dev->max_pkt_size; + + dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.max_pkt_size = dev->max_pkt_size; + dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + stk1160_err("out of memory for urb array\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + stk1160_err("out of memory for usb transfers\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + /* allocate urbs and transfer buffers */ + for (i = 0; i < num_bufs; i++) { + + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + stk1160_err("cannot alloc urb[%d]\n", i); + stk1160_uninit_isoc(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + +#ifndef CONFIG_DMA_NONCOHERENT + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); +#else + dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size, GFP_KERNEL); +#endif + if (!dev->isoc_ctl.transfer_buffer[i]) { + stk1160_err("cannot alloc %d bytes for tx buffer\n", + sb_size); + stk1160_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + /* + * FIXME: Where can I get the endpoint? + */ + urb->dev = dev->udev; + urb->pipe = usb_rcvisocpipe(dev->udev, STK1160_EP_VIDEO); + urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i]; + urb->transfer_buffer_length = sb_size; + urb->complete = stk1160_isoc_irq; + urb->context = dev; + urb->interval = 1; + urb->start_frame = 0; + urb->number_of_packets = max_packets; +#ifndef CONFIG_DMA_NONCOHERENT + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; +#else + urb->transfer_flags = URB_ISO_ASAP; +#endif + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->isoc_ctl.max_pkt_size; + k += dev->isoc_ctl.max_pkt_size; + } + } + + stk1160_dbg("urbs allocated\n"); + + /* At last we can say we have some buffers */ + dev->isoc_ctl.num_bufs = num_bufs; + + return 0; +} + diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h new file mode 100644 index 000000000000..3feba0033f98 --- /dev/null +++ b/drivers/media/usb/stk1160/stk1160.h @@ -0,0 +1,208 @@ +/* + * STK1160 driver + * + * Copyright (C) 2012 Ezequiel Garcia + * + * + * Based on Easycap driver by R.M. Thomas + * Copyright (C) 2010 R.M. Thomas + * + * + * This program is free software; 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 + +#define STK1160_VERSION "0.9.5" +#define STK1160_VERSION_NUM 0x000905 + +/* TODO: Decide on number of packets for each buffer */ +#define STK1160_NUM_PACKETS 64 + +/* Number of buffers for isoc transfers */ +#define STK1160_NUM_BUFS 16 /* TODO */ + +/* TODO: This endpoint address should be retrieved */ +#define STK1160_EP_VIDEO 0x82 +#define STK1160_EP_AUDIO 0x81 + +/* Max and min video buffers */ +#define STK1160_MIN_VIDEO_BUFFERS 8 +#define STK1160_MAX_VIDEO_BUFFERS 32 + +#define STK1160_MIN_PKT_SIZE 3072 + +#define STK1160_MAX_INPUT 3 + +#define STK1160_I2C_TIMEOUT 100 + +/* TODO: Print helpers + * I could use dev_xxx, pr_xxx, v4l2_xxx or printk. + * However, there isn't a solid consensus on which + * new drivers should use. + * + */ +#define DEBUG +#ifdef DEBUG +#define stk1160_dbg(fmt, args...) \ + printk(KERN_DEBUG "stk1160: " fmt, ## args) +#else +#define stk1160_dbg(fmt, args...) +#endif + +#define stk1160_info(fmt, args...) \ + pr_info("stk1160: " fmt, ## args) + +#define stk1160_warn(fmt, args...) \ + pr_warn("stk1160: " fmt, ## args) + +#define stk1160_err(fmt, args...) \ + pr_err("stk1160: " fmt, ## args) + +/* Buffer for one video frame */ +struct stk1160_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_buffer vb; + struct list_head list; + + void *mem; + unsigned int length; /* buffer length */ + unsigned int bytesused; /* bytes written */ + int odd; /* current oddity */ + + /* + * Since we interlace two fields per frame, + * this is different from bytesused. + */ + unsigned int pos; /* current pos inside buffer */ +}; + +struct stk1160_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* current buffer */ + struct stk1160_buffer *buf; +}; + +struct stk1160_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; +}; + +struct stk1160 { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_ctrl_handler ctrl_handler; + + struct device *dev; + struct usb_device *udev; + + /* saa7115 subdev */ + struct v4l2_subdev *sd_saa7115; + + /* isoc control struct */ + struct list_head avail_bufs; + + /* video capture */ + struct vb2_queue vb_vidq; + + /* max packet size of isoc transaction */ + int max_pkt_size; + /* array of wMaxPacketSize */ + unsigned int *alt_max_pkt_size; + /* alternate */ + int alt; + /* Number of alternative settings */ + int num_alt; + + struct stk1160_isoc_ctl isoc_ctl; + char urb_buf[255]; /* urb control msg buffer */ + + /* frame properties */ + int width; /* current frame width */ + int height; /* current frame height */ + unsigned int ctl_input; /* selected input */ + v4l2_std_id norm; /* current norm */ + struct stk1160_fmt *fmt; /* selected format */ + + unsigned int field_count; /* not sure ??? */ + enum v4l2_field field; /* also not sure :/ */ + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + struct mutex v4l_lock; + struct mutex vb_queue_lock; + spinlock_t buf_lock; + + struct file *fh_owner; /* filehandle ownership */ + + /* EXPERIMENTAL */ + struct snd_card *snd_card; +}; + +struct regval { + u16 reg; + u16 val; +}; + +/* Provided by stk1160-v4l.c */ +int stk1160_vb2_setup(struct stk1160 *dev); +int stk1160_video_register(struct stk1160 *dev); +void stk1160_video_unregister(struct stk1160 *dev); +void stk1160_clear_queue(struct stk1160 *dev); + +/* Provided by stk1160-video.c */ +int stk1160_alloc_isoc(struct stk1160 *dev); +void stk1160_free_isoc(struct stk1160 *dev); +void stk1160_cancel_isoc(struct stk1160 *dev); +void stk1160_uninit_isoc(struct stk1160 *dev); + +/* Provided by stk1160-i2c.c */ +int stk1160_i2c_register(struct stk1160 *dev); +int stk1160_i2c_unregister(struct stk1160 *dev); + +/* Provided by stk1160-core.c */ +int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value); +int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value); +int stk1160_write_regs_req(struct stk1160 *dev, u8 req, u16 reg, + char *buf, int len); +int stk1160_read_reg_req_len(struct stk1160 *dev, u8 req, u16 reg, + char *buf, int len); +void stk1160_select_input(struct stk1160 *dev); + +/* Provided by stk1160-ac97.c */ +#ifdef CONFIG_VIDEO_STK1160_AC97 +int stk1160_ac97_register(struct stk1160 *dev); +int stk1160_ac97_unregister(struct stk1160 *dev); +#else +static inline int stk1160_ac97_register(struct stk1160 *dev) { return 0; } +static inline int stk1160_ac97_unregister(struct stk1160 *dev) { return 0; } +#endif + diff --git a/drivers/media/usb/tlg2300/Kconfig b/drivers/media/usb/tlg2300/Kconfig new file mode 100644 index 000000000000..645d915267e6 --- /dev/null +++ b/drivers/media/usb/tlg2300/Kconfig @@ -0,0 +1,16 @@ +config VIDEO_TLG2300 + tristate "Telegent TLG2300 USB video capture support" + depends on VIDEO_DEV && I2C && SND && DVB_CORE + select VIDEO_TUNER + select VIDEO_TVEEPROM + depends on RC_CORE + select VIDEOBUF_VMALLOC + select SND_PCM + select VIDEOBUF_DVB + + ---help--- + This is a video4linux driver for Telegent tlg2300 based TV cards. + The driver supports V4L2, DVB-T and radio. + + To compile this driver as a module, choose M here: the + module will be called poseidon diff --git a/drivers/media/usb/tlg2300/Makefile b/drivers/media/usb/tlg2300/Makefile new file mode 100644 index 000000000000..4d660879999f --- /dev/null +++ b/drivers/media/usb/tlg2300/Makefile @@ -0,0 +1,9 @@ +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/tuners +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends + diff --git a/drivers/media/usb/tlg2300/pd-alsa.c b/drivers/media/usb/tlg2300/pd-alsa.c new file mode 100644 index 000000000000..9f8b7da56b67 --- /dev/null +++ b/drivers/media/usb/tlg2300/pd-alsa.c @@ -0,0 +1,332 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pd-common.h" +#include "vendorcmds.h" + +static void complete_handler_audio(struct urb *urb); +#define AUDIO_EP (0x83) +#define AUDIO_BUF_SIZE (512) +#define PERIOD_SIZE (1024 * 8) +#define PERIOD_MIN (4) +#define PERIOD_MAX PERIOD_MIN + +static struct snd_pcm_hardware snd_pd_hw_capture = { + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN, + .period_bytes_min = PERIOD_SIZE, + .period_bytes_max = PERIOD_SIZE, + .periods_min = PERIOD_MIN, + .periods_max = PERIOD_MAX, + /* + .buffer_bytes_max = 62720 * 8, + .period_bytes_min = 64, + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98 + */ +}; + +static int snd_pd_capture_open(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + struct snd_pcm_runtime *runtime = substream->runtime; + + if (!p) + return -ENODEV; + pa->users++; + pa->card_close = 0; + pa->capture_pcm_substream = substream; + runtime->private_data = p; + + runtime->hw = snd_pd_hw_capture; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + usb_autopm_get_interface(p->interface); + kref_get(&p->kref); + return 0; +} + +static int snd_pd_pcm_close(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + + pa->users--; + pa->card_close = 1; + usb_autopm_put_interface(p->interface); + kref_put(&p->kref, poseidon_delete); + return 0; +} + +static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int size; + + size = params_buffer_bytes(hw_params); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + else + runtime->dma_bytes = size; + return 0; +} + +static int audio_buf_free(struct poseidon *p) +{ + struct poseidon_audio *pa = &p->audio; + int i; + + for (i = 0; i < AUDIO_BUFS; i++) + if (pa->urb_array[i]) + usb_kill_urb(pa->urb_array[i]); + free_all_urb_generic(pa->urb_array, AUDIO_BUFS); + logpm(); + return 0; +} + +static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + + logpm(); + audio_buf_free(p); + return 0; +} + +static int snd_pd_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +#define AUDIO_TRAILER_SIZE (16) +static inline void handle_audio_data(struct urb *urb, int *period_elapsed) +{ + struct poseidon_audio *pa = urb->context; + struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime; + + int stride = runtime->frame_bits >> 3; + int len = urb->actual_length / stride; + unsigned char *cp = urb->transfer_buffer; + unsigned int oldptr = pa->rcv_position; + + if (urb->actual_length == AUDIO_BUF_SIZE - 4) + len -= (AUDIO_TRAILER_SIZE / stride); + + /* do the copy */ + if (oldptr + len >= runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - oldptr; + + memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); + memcpy(runtime->dma_area, (cp + cnt * stride), + (len * stride - cnt * stride)); + } else + memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); + + /* update the statas */ + snd_pcm_stream_lock(pa->capture_pcm_substream); + pa->rcv_position += len; + if (pa->rcv_position >= runtime->buffer_size) + pa->rcv_position -= runtime->buffer_size; + + pa->copied_position += (len); + if (pa->copied_position >= runtime->period_size) { + pa->copied_position -= runtime->period_size; + *period_elapsed = 1; + } + snd_pcm_stream_unlock(pa->capture_pcm_substream); +} + +static void complete_handler_audio(struct urb *urb) +{ + struct poseidon_audio *pa = urb->context; + struct snd_pcm_substream *substream = pa->capture_pcm_substream; + int period_elapsed = 0; + int ret; + + if (1 == pa->card_close || pa->capture_stream != STREAM_ON) + return; + + if (urb->status != 0) { + /*if (urb->status == -ESHUTDOWN)*/ + return; + } + + if (substream) { + if (urb->actual_length) { + handle_audio_data(urb, &period_elapsed); + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + } + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + log("audio urb failed (errcod = %i)", ret); + return; +} + +static int fire_audio_urb(struct poseidon *p) +{ + int i, ret = 0; + struct poseidon_audio *pa = &p->audio; + + alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS, + p->udev, AUDIO_EP, + AUDIO_BUF_SIZE, GFP_ATOMIC, + complete_handler_audio, pa); + + for (i = 0; i < AUDIO_BUFS; i++) { + ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL); + if (ret) + log("urb err : %d", ret); + } + log(); + return ret; +} + +static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + + if (debug_mode) + log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + if (pa->capture_stream == STREAM_ON) + return 0; + + pa->rcv_position = pa->copied_position = 0; + pa->capture_stream = STREAM_ON; + + if (in_hibernation(p)) + return 0; + fire_audio_urb(p); + return 0; + + case SNDRV_PCM_TRIGGER_SUSPEND: + pa->capture_stream = STREAM_SUSPEND; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + pa->capture_stream = STREAM_OFF; + return 0; + default: + return -EINVAL; + } +} + +static snd_pcm_uframes_t +snd_pd_capture_pointer(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + return pa->rcv_position; +} + +static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +static struct snd_pcm_ops pcm_capture_ops = { + .open = snd_pd_capture_open, + .close = snd_pd_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_pd_hw_capture_params, + .hw_free = snd_pd_hw_capture_free, + .prepare = snd_pd_prepare, + .trigger = snd_pd_capture_trigger, + .pointer = snd_pd_capture_pointer, + .page = snd_pcm_pd_get_page, +}; + +#ifdef CONFIG_PM +int pm_alsa_suspend(struct poseidon *p) +{ + logpm(p); + audio_buf_free(p); + return 0; +} + +int pm_alsa_resume(struct poseidon *p) +{ + logpm(p); + fire_audio_urb(p); + return 0; +} +#endif + +int poseidon_audio_init(struct poseidon *p) +{ + struct poseidon_audio *pa = &p->audio; + struct snd_card *card; + struct snd_pcm *pcm; + int ret; + + ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card); + if (ret != 0) + return ret; + + ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + pcm->info_flags = 0; + pcm->private_data = p; + strcpy(pcm->name, "poseidon audio capture"); + + strcpy(card->driver, "ALSA driver"); + strcpy(card->shortname, "poseidon Audio"); + strcpy(card->longname, "poseidon ALSA Audio"); + + if (snd_card_register(card)) { + snd_card_free(card); + return -ENOMEM; + } + pa->card = card; + return 0; +} + +int poseidon_audio_free(struct poseidon *p) +{ + struct poseidon_audio *pa = &p->audio; + + if (pa->card) + snd_card_free(pa->card); + return 0; +} diff --git a/drivers/media/usb/tlg2300/pd-common.h b/drivers/media/usb/tlg2300/pd-common.h new file mode 100644 index 000000000000..5dd73b7857d1 --- /dev/null +++ b/drivers/media/usb/tlg2300/pd-common.h @@ -0,0 +1,281 @@ +#ifndef PD_COMMON_H +#define PD_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dmxdev.h" + +#define SBUF_NUM 8 +#define MAX_BUFFER_NUM 6 +#define PK_PER_URB 32 +#define ISO_PKT_SIZE 3072 + +#define POSEIDON_STATE_NONE (0x0000) +#define POSEIDON_STATE_ANALOG (0x0001) +#define POSEIDON_STATE_FM (0x0002) +#define POSEIDON_STATE_DVBT (0x0004) +#define POSEIDON_STATE_VBI (0x0008) +#define POSEIDON_STATE_DISCONNECT (0x0080) + +#define PM_SUSPEND_DELAY 3 + +#define V4L_PAL_VBI_LINES 18 +#define V4L_NTSC_VBI_LINES 12 +#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2) +#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2) + +#define TUNER_FREQ_MIN (45000000) +#define TUNER_FREQ_MAX (862000000) + +struct vbi_data { + struct video_device *v_dev; + struct video_data *video; + struct front_face *front; + + unsigned int copied; + unsigned int vbi_size; /* the whole size of two fields */ + int users; +}; + +/* + * This is the running context of the video, it is useful for + * resume() + */ +struct running_context { + u32 freq; /* VIDIOC_S_FREQUENCY */ + int audio_idx; /* VIDIOC_S_TUNER */ + v4l2_std_id tvnormid; /* VIDIOC_S_STD */ + int sig_index; /* VIDIOC_S_INPUT */ + struct v4l2_pix_format pix; /* VIDIOC_S_FMT */ +}; + +struct video_data { + /* v4l2 video device */ + struct video_device *v_dev; + + /* the working context */ + struct running_context context; + + /* for data copy */ + int field_count; + + char *dst; + int lines_copied; + int prev_left; + + int lines_per_field; + int lines_size; + + /* for communication */ + u8 endpoint_addr; + struct urb *urb_array[SBUF_NUM]; + struct vbi_data *vbi; + struct poseidon *pd; + struct front_face *front; + + int is_streaming; + int users; + + /* for bubble handler */ + struct work_struct bubble_work; +}; + +enum pcm_stream_state { + STREAM_OFF, + STREAM_ON, + STREAM_SUSPEND, +}; + +#define AUDIO_BUFS (3) +#define CAPTURE_STREAM_EN 1 +struct poseidon_audio { + struct urb *urb_array[AUDIO_BUFS]; + unsigned int copied_position; + struct snd_pcm_substream *capture_pcm_substream; + + unsigned int rcv_position; + struct snd_card *card; + int card_close; + + int users; + int pm_state; + enum pcm_stream_state capture_stream; +}; + +struct radio_data { + __u32 fm_freq; + int users; + unsigned int is_radio_streaming; + int pre_emphasis; + struct video_device *fm_dev; +}; + +#define DVB_SBUF_NUM 4 +#define DVB_URB_BUF_SIZE 0x2000 +struct pd_dvb_adapter { + struct dvb_adapter dvb_adap; + struct dvb_frontend dvb_fe; + struct dmxdev dmxdev; + struct dvb_demux demux; + + atomic_t users; + atomic_t active_feed; + + /* data transfer */ + s32 is_streaming; + struct urb *urb_array[DVB_SBUF_NUM]; + struct poseidon *pd_device; + u8 ep_addr; + u8 reserved[3]; + + /* data for power resume*/ + struct dtv_frontend_properties fe_param; + + /* for channel scanning */ + int prev_freq; + int bandwidth; + unsigned long last_jiffies; +}; + +struct front_face { + /* use this field to distinguish VIDEO and VBI */ + enum v4l2_buf_type type; + + /* for host */ + struct videobuf_queue q; + + /* the bridge for host and device */ + struct videobuf_buffer *curr_frame; + + /* for device */ + spinlock_t queue_lock; + struct list_head active; + struct poseidon *pd; +}; + +struct poseidon { + struct list_head device_list; + + struct mutex lock; + struct kref kref; + + /* for V4L2 */ + struct v4l2_device v4l2_dev; + + /* hardware info */ + struct usb_device *udev; + struct usb_interface *interface; + int cur_transfer_mode; + + struct video_data video_data; /* video */ + struct vbi_data vbi_data; /* vbi */ + struct poseidon_audio audio; /* audio (alsa) */ + struct radio_data radio_data; /* FM */ + struct pd_dvb_adapter dvb_data; /* DVB */ + + u32 state; + struct file *file_for_stream; /* the active stream*/ + +#ifdef CONFIG_PM + int (*pm_suspend)(struct poseidon *); + int (*pm_resume)(struct poseidon *); + pm_message_t msg; + + struct work_struct pm_work; + u8 portnum; +#endif +}; + +struct poseidon_format { + char *name; + int fourcc; /* video4linux 2 */ + int depth; /* bit/pixel */ + int flags; +}; + +struct poseidon_tvnorm { + v4l2_std_id v4l2_id; + char name[12]; + u32 tlg_tvnorm; +}; + +/* video */ +int pd_video_init(struct poseidon *); +void pd_video_exit(struct poseidon *); +int stop_all_video_stream(struct poseidon *); + +/* alsa audio */ +int poseidon_audio_init(struct poseidon *); +int poseidon_audio_free(struct poseidon *); +#ifdef CONFIG_PM +int pm_alsa_suspend(struct poseidon *); +int pm_alsa_resume(struct poseidon *); +#endif + +/* dvb */ +int pd_dvb_usb_device_init(struct poseidon *); +void pd_dvb_usb_device_exit(struct poseidon *); +void pd_dvb_usb_device_cleanup(struct poseidon *); +int pd_dvb_get_adapter_num(struct pd_dvb_adapter *); +void dvb_stop_streaming(struct pd_dvb_adapter *); + +/* FM */ +int poseidon_fm_init(struct poseidon *); +int poseidon_fm_exit(struct poseidon *); +struct video_device *vdev_init(struct poseidon *, struct video_device *); + +/* vendor command ops */ +int send_set_req(struct poseidon*, u8, s32, s32*); +int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32); +s32 set_tuner_mode(struct poseidon*, unsigned char); + +/* bulk urb alloc/free */ +int alloc_bulk_urbs_generic(struct urb **urb_array, int num, + struct usb_device *udev, u8 ep_addr, + int buf_size, gfp_t gfp_flags, + usb_complete_t complete_fn, void *context); +void free_all_urb_generic(struct urb **urb_array, int num); + +/* misc */ +void poseidon_delete(struct kref *kref); +void destroy_video_device(struct video_device **v_dev); +extern int debug_mode; +void set_debug_mode(struct video_device *vfd, int debug_mode); + +#ifdef CONFIG_PM +#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE) +#else +#define in_hibernation(pd) (0) +#endif +#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt)) + +#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \ + __func__, __LINE__, ## __VA_ARGS__) + +/* for power management */ +#define logpm(pd) do {\ + if (debug_mode & 0x10)\ + log();\ + } while (0) + +#define logs(f) do { \ + if ((debug_mode & 0x4) && \ + (f)->type == V4L2_BUF_TYPE_VBI_CAPTURE) \ + log("type : VBI");\ + \ + if ((debug_mode & 0x8) && \ + (f)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) \ + log("type : VIDEO");\ + } while (0) +#endif diff --git a/drivers/media/usb/tlg2300/pd-dvb.c b/drivers/media/usb/tlg2300/pd-dvb.c new file mode 100644 index 000000000000..30fcb117e898 --- /dev/null +++ b/drivers/media/usb/tlg2300/pd-dvb.c @@ -0,0 +1,596 @@ +#include "pd-common.h" +#include +#include +#include +#include +#include + +#include "vendorcmds.h" +#include +#include + +static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb); + +static int dvb_bandwidth[][2] = { + { TLG_BW_8, 8000000 }, + { TLG_BW_7, 7000000 }, + { TLG_BW_6, 6000000 } +}; +static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth); + +static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb); +static int poseidon_check_mode_dvbt(struct poseidon *pd) +{ + s32 ret = 0, cmd_status = 0; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); + + ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE); + if (ret != 0) + return ret; + + ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T); + if (ret) + return ret; + + /* signal source */ + ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status); + if (ret|cmd_status) + return ret; + + return 0; +} + +/* acquire : + * 1 == open + * 0 == release + */ +static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb; + int ret = 0; + + if (!pd) + return -ENODEV; + + pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe); + if (acquire) { + mutex_lock(&pd->lock); + if (pd->state & POSEIDON_STATE_DISCONNECT) { + ret = -ENODEV; + goto open_out; + } + + if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) { + ret = -EBUSY; + goto open_out; + } + + usb_autopm_get_interface(pd->interface); + if (0 == pd->state) { + ret = poseidon_check_mode_dvbt(pd); + if (ret < 0) { + usb_autopm_put_interface(pd->interface); + goto open_out; + } + pd->state |= POSEIDON_STATE_DVBT; + pd_dvb->bandwidth = 0; + pd_dvb->prev_freq = 0; + } + atomic_inc(&pd_dvb->users); + kref_get(&pd->kref); +open_out: + mutex_unlock(&pd->lock); + } else { + dvb_stop_streaming(pd_dvb); + + if (atomic_dec_and_test(&pd_dvb->users)) { + mutex_lock(&pd->lock); + pd->state &= ~POSEIDON_STATE_DVBT; + mutex_unlock(&pd->lock); + } + kref_put(&pd->kref, poseidon_delete); + usb_autopm_put_interface(pd->interface); + } + return ret; +} + +#ifdef CONFIG_PM +static void poseidon_fe_release(struct dvb_frontend *fe) +{ + struct poseidon *pd = fe->demodulator_priv; + + pd->pm_suspend = NULL; + pd->pm_resume = NULL; +} +#else +#define poseidon_fe_release NULL +#endif + +static s32 poseidon_fe_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +/* + * return true if we can satisfy the conditions, else return false. + */ +static bool check_scan_ok(__u32 freq, int bandwidth, + struct pd_dvb_adapter *adapter) +{ + if (bandwidth < 0) + return false; + + if (adapter->prev_freq == freq + && adapter->bandwidth == bandwidth) { + long nl = jiffies - adapter->last_jiffies; + unsigned int msec ; + + msec = jiffies_to_msecs(abs(nl)); + return msec > 15000 ? true : false; + } + return true; +} + +/* + * Check if the firmware delays too long for an invalid frequency. + */ +static int fw_delay_overflow(struct pd_dvb_adapter *adapter) +{ + long nl = jiffies - adapter->last_jiffies; + unsigned int msec ; + + msec = jiffies_to_msecs(abs(nl)); + return msec > 800 ? true : false; +} + +static int poseidon_set_fe(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + s32 ret = 0, cmd_status = 0; + s32 i, bandwidth = -1; + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + if (in_hibernation(pd)) + return -EBUSY; + + mutex_lock(&pd->lock); + for (i = 0; i < dvb_bandwidth_length; i++) + if (fep->bandwidth_hz == dvb_bandwidth[i][1]) + bandwidth = dvb_bandwidth[i][0]; + + if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) { + ret = send_set_req(pd, TUNE_FREQ_SELECT, + fep->frequency / 1000, &cmd_status); + if (ret | cmd_status) { + log("error line"); + goto front_out; + } + + ret = send_set_req(pd, DVBT_BANDW_SEL, + bandwidth, &cmd_status); + if (ret | cmd_status) { + log("error line"); + goto front_out; + } + + ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + if (ret | cmd_status) { + log("error line"); + goto front_out; + } + + /* save the context for future */ + memcpy(&pd_dvb->fe_param, fep, sizeof(*fep)); + pd_dvb->bandwidth = bandwidth; + pd_dvb->prev_freq = fep->frequency; + pd_dvb->last_jiffies = jiffies; + } +front_out: + mutex_unlock(&pd->lock); + return ret; +} + +#ifdef CONFIG_PM +static int pm_dvb_suspend(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + dvb_stop_streaming(pd_dvb); + dvb_urb_cleanup(pd_dvb); + msleep(500); + return 0; +} + +static int pm_dvb_resume(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + poseidon_check_mode_dvbt(pd); + msleep(300); + poseidon_set_fe(&pd_dvb->dvb_fe); + + dvb_start_streaming(pd_dvb); + return 0; +} +#endif + +static s32 poseidon_fe_init(struct dvb_frontend *fe) +{ + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + +#ifdef CONFIG_PM + pd->pm_suspend = pm_dvb_suspend; + pd->pm_resume = pm_dvb_resume; +#endif + memset(&pd_dvb->fe_param, 0, + sizeof(struct dtv_frontend_properties)); + return 0; +} + +static int poseidon_get_fe(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + memcpy(fep, &pd_dvb->fe_param, sizeof(*fep)); + return 0; +} + +static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + struct poseidon *pd = fe->demodulator_priv; + s32 ret = -1, cmd_status; + struct tuner_dtv_sig_stat_s status = {}; + + if (in_hibernation(pd)) + return -EBUSY; + mutex_lock(&pd->lock); + + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, + &status, &cmd_status, sizeof(status)); + if (ret | cmd_status) { + log("get tuner status error"); + goto out; + } + + if (debug_mode) + log("P : %d, L %d, LB :%d", status.sig_present, + status.sig_locked, status.sig_lock_busy); + + if (status.sig_lock_busy) { + goto out; + } else if (status.sig_present || status.sig_locked) { + *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER + | FE_HAS_SYNC | FE_HAS_VITERBI; + } else { + if (fw_delay_overflow(&pd->dvb_data)) + *stat |= FE_TIMEDOUT; + } +out: + mutex_unlock(&pd->lock); + return ret; +} + +static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct poseidon *pd = fe->demodulator_priv; + struct tuner_ber_rate_s tlg_ber = {}; + s32 ret = -1, cmd_status; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_BER_RATE, 0, + &tlg_ber, &cmd_status, sizeof(tlg_ber)); + if (ret | cmd_status) + goto out; + *ber = tlg_ber.ber_rate; +out: + mutex_unlock(&pd->lock); + return ret; +} + +static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct poseidon *pd = fe->demodulator_priv; + struct tuner_dtv_sig_stat_s status = {}; + s32 ret = 0, cmd_status; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, + &status, &cmd_status, sizeof(status)); + if (ret | cmd_status) + goto out; + if ((status.sig_present || status.sig_locked) && !status.sig_strength) + *strength = 0xFFFF; + else + *strength = status.sig_strength; +out: + mutex_unlock(&pd->lock); + return ret; +} + +static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + return 0; +} + +static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + *unc = 0; + return 0; +} + +static struct dvb_frontend_ops poseidon_frontend_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Poseidon DVB-T", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 62500,/* FIXME */ + .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 = poseidon_fe_release, + + .init = poseidon_fe_init, + .sleep = poseidon_fe_sleep, + + .set_frontend = poseidon_set_fe, + .get_frontend = poseidon_get_fe, + .get_tune_settings = poseidon_fe_get_tune_settings, + + .read_status = poseidon_read_status, + .read_ber = poseidon_read_ber, + .read_signal_strength = poseidon_read_signal_strength, + .read_snr = poseidon_read_snr, + .read_ucblocks = poseidon_read_unc_blocks, + + .ts_bus_ctrl = poseidon_ts_bus_ctrl, +}; + +static void dvb_urb_irq(struct urb *urb) +{ + struct pd_dvb_adapter *pd_dvb = urb->context; + int len = urb->transfer_buffer_length; + struct dvb_demux *demux = &pd_dvb->demux; + s32 ret; + + if (!pd_dvb->is_streaming || urb->status) { + if (urb->status == -EPROTO) + goto resend; + return; + } + + if (urb->actual_length == len) + dvb_dmx_swfilter(demux, urb->transfer_buffer, len); + else if (urb->actual_length == len - 4) { + int offset; + u8 *buf = urb->transfer_buffer; + + /* + * The packet size is 512, + * last packet contains 456 bytes tsp data + */ + for (offset = 456; offset < len; offset += 512) { + if (!strncmp(buf + offset, "DVHS", 4)) { + dvb_dmx_swfilter(demux, buf, offset); + if (len > offset + 52 + 4) { + /*16 bytes trailer + 36 bytes padding */ + buf += offset + 52; + len -= offset + 52 + 4; + dvb_dmx_swfilter(demux, buf, len); + } + break; + } + } + } + +resend: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + log(" usb_submit_urb failed: error %d", ret); +} + +static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb) +{ + if (pd_dvb->urb_array[0]) + return 0; + + alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM, + pd_dvb->pd_device->udev, pd_dvb->ep_addr, + DVB_URB_BUF_SIZE, GFP_KERNEL, + dvb_urb_irq, pd_dvb); + return 0; +} + +static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb) +{ + free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM); +} + +static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb) +{ + struct poseidon *pd = pd_dvb->pd_device; + int ret = 0; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + mutex_lock(&pd->lock); + if (!pd_dvb->is_streaming) { + s32 i, cmd_status = 0; + /* + * Once upon a time, there was a difficult bug lying here. + * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + */ + + ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status); + if (ret | cmd_status) + goto out; + + ret = dvb_urb_init(pd_dvb); + if (ret < 0) + goto out; + + pd_dvb->is_streaming = 1; + for (i = 0; i < DVB_SBUF_NUM; i++) { + ret = usb_submit_urb(pd_dvb->urb_array[i], + GFP_KERNEL); + if (ret) { + log(" submit urb error %d", ret); + goto out; + } + } + } +out: + mutex_unlock(&pd->lock); + return ret; +} + +void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb) +{ + struct poseidon *pd = pd_dvb->pd_device; + + mutex_lock(&pd->lock); + if (pd_dvb->is_streaming) { + s32 i, ret, cmd_status = 0; + + pd_dvb->is_streaming = 0; + + for (i = 0; i < DVB_SBUF_NUM; i++) + if (pd_dvb->urb_array[i]) + usb_kill_urb(pd_dvb->urb_array[i]); + + ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, + &cmd_status); + if (ret | cmd_status) + log("error"); + } + mutex_unlock(&pd->lock); +} + +static int pd_start_feed(struct dvb_demux_feed *feed) +{ + struct pd_dvb_adapter *pd_dvb = feed->demux->priv; + int ret = 0; + + if (!pd_dvb) + return -1; + if (atomic_inc_return(&pd_dvb->active_feed) == 1) + ret = dvb_start_streaming(pd_dvb); + return ret; +} + +static int pd_stop_feed(struct dvb_demux_feed *feed) +{ + struct pd_dvb_adapter *pd_dvb = feed->demux->priv; + + if (!pd_dvb) + return -1; + if (atomic_dec_and_test(&pd_dvb->active_feed)) + dvb_stop_streaming(pd_dvb); + return 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +int pd_dvb_usb_device_init(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + struct dvb_demux *dvbdemux; + int ret = 0; + + pd_dvb->ep_addr = 0x82; + atomic_set(&pd_dvb->users, 0); + atomic_set(&pd_dvb->active_feed, 0); + pd_dvb->pd_device = pd; + + ret = dvb_register_adapter(&pd_dvb->dvb_adap, + "Poseidon dvbt adapter", + THIS_MODULE, + NULL /* for hibernation correctly*/, + adapter_nr); + if (ret < 0) + goto error1; + + /* register frontend */ + pd_dvb->dvb_fe.demodulator_priv = pd; + memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops, + sizeof(struct dvb_frontend_ops)); + ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe); + if (ret < 0) + goto error2; + + /* register demux device */ + dvbdemux = &pd_dvb->demux; + dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + dvbdemux->priv = pd_dvb; + dvbdemux->feednum = dvbdemux->filternum = 64; + dvbdemux->start_feed = pd_start_feed; + dvbdemux->stop_feed = pd_stop_feed; + dvbdemux->write_to_decoder = NULL; + + ret = dvb_dmx_init(dvbdemux); + if (ret < 0) + goto error3; + + pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum; + pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx; + pd_dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap); + if (ret < 0) + goto error3; + return 0; + +error3: + dvb_unregister_frontend(&pd_dvb->dvb_fe); +error2: + dvb_unregister_adapter(&pd_dvb->dvb_adap); +error1: + return ret; +} + +void pd_dvb_usb_device_exit(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + while (atomic_read(&pd_dvb->users) != 0 + || atomic_read(&pd_dvb->active_feed) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + dvb_dmxdev_release(&pd_dvb->dmxdev); + dvb_unregister_frontend(&pd_dvb->dvb_fe); + dvb_unregister_adapter(&pd_dvb->dvb_adap); + pd_dvb_usb_device_cleanup(pd); +} + +void pd_dvb_usb_device_cleanup(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + dvb_urb_cleanup(pd_dvb); +} + +int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb) +{ + return pd_dvb->dvb_adap.num; +} diff --git a/drivers/media/usb/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c new file mode 100644 index 000000000000..7b1f6ebd0e2c --- /dev/null +++ b/drivers/media/usb/tlg2300/pd-main.c @@ -0,0 +1,536 @@ +/* + * device driver for Telegent tlg2300 based TV cards + * + * Author : + * Kang Yong + * Zhang Xiaobing + * Huang Shijie or + * + * (c) 2009 Telegent Systems + * (c) 2010 Telegent Systems + * + * This program is free software; 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 +#include +#include +#include + +#include "vendorcmds.h" +#include "pd-common.h" + +#define VENDOR_ID 0x1B24 +#define PRODUCT_ID 0x4001 +static struct usb_device_id id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +int debug_mode; +module_param(debug_mode, int, 0644); +MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); + +#define TLG2300_FIRMWARE "tlg2300_firmware.bin" +static const char *firmware_name = TLG2300_FIRMWARE; +static struct usb_driver poseidon_driver; +static LIST_HEAD(pd_device_list); + +/* + * send set request to USB firmware. + */ +s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status) +{ + s32 ret; + s8 data[32] = {}; + u16 lower_16, upper_16; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + mdelay(30); + + if (param == 0) { + upper_16 = lower_16 = 0; + } else { + /* send 32 bit param as two 16 bit param,little endian */ + lower_16 = (unsigned short)(param & 0xffff); + upper_16 = (unsigned short)((param >> 16) & 0xffff); + } + ret = usb_control_msg(pd->udev, + usb_rcvctrlpipe(pd->udev, 0), + REQ_SET_CMD | cmdid, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + lower_16, + upper_16, + &data, + sizeof(*cmd_status), + USB_CTRL_GET_TIMEOUT); + + if (!ret) { + return -ENXIO; + } else { + /* 1st 4 bytes into cmd_status */ + memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status)); + } + return 0; +} + +/* + * send get request to Poseidon firmware. + */ +s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param, + void *buf, s32 *cmd_status, s32 datalen) +{ + s32 ret; + s8 data[128] = {}; + u16 lower_16, upper_16; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + mdelay(30); + if (param == 0) { + upper_16 = lower_16 = 0; + } else { + /*send 32 bit param as two 16 bit param, little endian */ + lower_16 = (unsigned short)(param & 0xffff); + upper_16 = (unsigned short)((param >> 16) & 0xffff); + } + ret = usb_control_msg(pd->udev, + usb_rcvctrlpipe(pd->udev, 0), + REQ_GET_CMD | cmdid, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + lower_16, + upper_16, + &data, + (datalen + sizeof(*cmd_status)), + USB_CTRL_GET_TIMEOUT); + + if (ret < 0) { + return -ENXIO; + } else { + /* 1st 4 bytes into cmd_status, remaining data into cmd_data */ + memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status)); + memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen); + } + return 0; +} + +static int pm_notifier_block(struct notifier_block *nb, + unsigned long event, void *dummy) +{ + struct poseidon *pd = NULL; + struct list_head *node, *next; + + switch (event) { + case PM_POST_HIBERNATION: + list_for_each_safe(node, next, &pd_device_list) { + struct usb_device *udev; + struct usb_interface *iface; + int rc = 0; + + pd = container_of(node, struct poseidon, device_list); + udev = pd->udev; + iface = pd->interface; + + /* It will cause the system to reload the firmware */ + rc = usb_lock_device_for_reset(udev, iface); + if (rc >= 0) { + usb_reset_device(udev); + usb_unlock_device(udev); + } + } + break; + default: + break; + } + log("event :%ld\n", event); + return 0; +} + +static struct notifier_block pm_notifer = { + .notifier_call = pm_notifier_block, +}; + +int set_tuner_mode(struct poseidon *pd, unsigned char mode) +{ + s32 ret, cmd_status; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status); + if (ret || cmd_status) + return -ENXIO; + return 0; +} + +void poseidon_delete(struct kref *kref) +{ + struct poseidon *pd = container_of(kref, struct poseidon, kref); + + if (!pd) + return; + list_del_init(&pd->device_list); + + pd_dvb_usb_device_cleanup(pd); + /* clean_audio_data(&pd->audio_data);*/ + + if (pd->udev) { + usb_put_dev(pd->udev); + pd->udev = NULL; + } + if (pd->interface) { + usb_put_intf(pd->interface); + pd->interface = NULL; + } + kfree(pd); + log(); +} + +static int firmware_download(struct usb_device *udev) +{ + int ret = 0, actual_length; + const struct firmware *fw = NULL; + void *fwbuf = NULL; + size_t fwlength = 0, offset; + size_t max_packet_size; + + ret = request_firmware(&fw, firmware_name, &udev->dev); + if (ret) { + log("download err : %d", ret); + return ret; + } + + fwlength = fw->size; + + fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL); + if (!fwbuf) { + ret = -ENOMEM; + goto out; + } + + max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize; + log("\t\t download size : %d", (int)max_packet_size); + + for (offset = 0; offset < fwlength; offset += max_packet_size) { + actual_length = 0; + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x01), /* ep 1 */ + fwbuf + offset, + min(max_packet_size, fwlength - offset), + &actual_length, + HZ * 10); + if (ret) + break; + } + kfree(fwbuf); +out: + release_firmware(fw); + return ret; +} + +static inline struct poseidon *get_pd(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +#ifdef CONFIG_PM +/* one-to-one map : poseidon{} <----> usb_device{}'s port */ +static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) +{ + pd->portnum = udev->portnum; +} + +static inline int get_autopm_ref(struct poseidon *pd) +{ + return pd->video_data.users + pd->vbi_data.users + pd->audio.users + + atomic_read(&pd->dvb_data.users) + pd->radio_data.users; +} + +/* fixup something for poseidon */ +static inline struct poseidon *fixup(struct poseidon *pd) +{ + int count; + + /* old udev and interface have gone, so put back reference . */ + count = get_autopm_ref(pd); + log("count : %d, ref count : %d", count, get_pm_count(pd)); + while (count--) + usb_autopm_put_interface(pd->interface); + /*usb_autopm_set_interface(pd->interface); */ + + usb_put_dev(pd->udev); + usb_put_intf(pd->interface); + log("event : %d\n", pd->msg.event); + return pd; +} + +static struct poseidon *find_old_poseidon(struct usb_device *udev) +{ + struct poseidon *pd; + + list_for_each_entry(pd, &pd_device_list, device_list) { + if (pd->portnum == udev->portnum && in_hibernation(pd)) + return fixup(pd); + } + return NULL; +} + +/* Is the card working now ? */ +static inline int is_working(struct poseidon *pd) +{ + return get_pm_count(pd) > 0; +} + +static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct poseidon *pd = get_pd(intf); + + if (!pd) + return 0; + if (!is_working(pd)) { + if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) { + pd->msg.event = PM_EVENT_AUTO_SUSPEND; + pd->pm_resume = NULL; /* a good guard */ + printk(KERN_DEBUG "\n\t+ TLG2300 auto suspend +\n\n"); + } + return 0; + } + pd->msg = msg; /* save it here */ + logpm(pd); + return pd->pm_suspend ? pd->pm_suspend(pd) : 0; +} + +static int poseidon_resume(struct usb_interface *intf) +{ + struct poseidon *pd = get_pd(intf); + + if (!pd) + return 0; + printk(KERN_DEBUG "\n\t ++ TLG2300 resume ++\n\n"); + + if (!is_working(pd)) { + if (PM_EVENT_AUTO_SUSPEND == pd->msg.event) + pd->msg = PMSG_ON; + return 0; + } + if (in_hibernation(pd)) { + logpm(pd); + return 0; + } + logpm(pd); + return pd->pm_resume ? pd->pm_resume(pd) : 0; +} + +static void hibernation_resume(struct work_struct *w) +{ + struct poseidon *pd = container_of(w, struct poseidon, pm_work); + int count; + + pd->msg.event = 0; /* clear it here */ + pd->state &= ~POSEIDON_STATE_DISCONNECT; + + /* set the new interface's reference */ + count = get_autopm_ref(pd); + while (count--) + usb_autopm_get_interface(pd->interface); + + /* resume the context */ + logpm(pd); + if (pd->pm_resume) + pd->pm_resume(pd); +} +#else /* CONFIG_PM is not enabled: */ +static inline struct poseidon *find_old_poseidon(struct usb_device *udev) +{ + return NULL; +} + +static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) +{ +} +#endif + +static int check_firmware(struct usb_device *udev, int *down_firmware) +{ + void *buf; + int ret; + struct cmd_firmware_vers_s *cmd_firm; + + buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + REQ_GET_CMD | GET_FW_ID, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + 0, + buf, + sizeof(*cmd_firm) + sizeof(u32), + USB_CTRL_GET_TIMEOUT); + kfree(buf); + + if (ret < 0) { + *down_firmware = 1; + return firmware_download(udev); + } + return 0; +} + +static int poseidon_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct poseidon *pd = NULL; + int ret = 0; + int new_one = 0; + + /* download firmware */ + check_firmware(udev, &ret); + if (ret) + return 0; + + /* Do I recovery from the hibernate ? */ + pd = find_old_poseidon(udev); + if (!pd) { + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + kref_init(&pd->kref); + set_map_flags(pd, udev); + new_one = 1; + } + + pd->udev = usb_get_dev(udev); + pd->interface = usb_get_intf(interface); + usb_set_intfdata(interface, pd); + + if (new_one) { + struct device *dev = &interface->dev; + + logpm(pd); + mutex_init(&pd->lock); + + /* register v4l2 device */ + snprintf(pd->v4l2_dev.name, sizeof(pd->v4l2_dev.name), "%s %s", + dev->driver->name, dev_name(dev)); + ret = v4l2_device_register(NULL, &pd->v4l2_dev); + + /* register devices in directory /dev */ + ret = pd_video_init(pd); + poseidon_audio_init(pd); + poseidon_fm_init(pd); + pd_dvb_usb_device_init(pd); + + INIT_LIST_HEAD(&pd->device_list); + list_add_tail(&pd->device_list, &pd_device_list); + } + + device_init_wakeup(&udev->dev, 1); +#ifdef CONFIG_PM + pm_runtime_set_autosuspend_delay(&pd->udev->dev, + 1000 * PM_SUSPEND_DELAY); + usb_enable_autosuspend(pd->udev); + + if (in_hibernation(pd)) { + INIT_WORK(&pd->pm_work, hibernation_resume); + schedule_work(&pd->pm_work); + } +#endif + return 0; +} + +static void poseidon_disconnect(struct usb_interface *interface) +{ + struct poseidon *pd = get_pd(interface); + + if (!pd) + return; + logpm(pd); + if (in_hibernation(pd)) + return; + + mutex_lock(&pd->lock); + pd->state |= POSEIDON_STATE_DISCONNECT; + mutex_unlock(&pd->lock); + + /* stop urb transferring */ + stop_all_video_stream(pd); + dvb_stop_streaming(&pd->dvb_data); + + /*unregister v4l2 device */ + v4l2_device_unregister(&pd->v4l2_dev); + + pd_dvb_usb_device_exit(pd); + poseidon_fm_exit(pd); + + poseidon_audio_free(pd); + pd_video_exit(pd); + + usb_set_intfdata(interface, NULL); + kref_put(&pd->kref, poseidon_delete); +} + +static struct usb_driver poseidon_driver = { + .name = "poseidon", + .probe = poseidon_probe, + .disconnect = poseidon_disconnect, + .id_table = id_table, +#ifdef CONFIG_PM + .suspend = poseidon_suspend, + .resume = poseidon_resume, +#endif + .supports_autosuspend = 1, +}; + +static int __init poseidon_init(void) +{ + int ret; + + ret = usb_register(&poseidon_driver); + if (ret) + return ret; + register_pm_notifier(&pm_notifer); + return ret; +} + +static void __exit poseidon_exit(void) +{ + log(); + unregister_pm_notifier(&pm_notifer); + usb_deregister(&poseidon_driver); +} + +module_init(poseidon_init); +module_exit(poseidon_exit); + +MODULE_AUTHOR("Telegent Systems"); +MODULE_DESCRIPTION("For tlg2300-based USB device "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.0.2"); +MODULE_FIRMWARE(TLG2300_FIRMWARE); diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c new file mode 100644 index 000000000000..4fad1dfb92cf --- /dev/null +++ b/drivers/media/usb/tlg2300/pd-radio.c @@ -0,0 +1,421 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pd-common.h" +#include "vendorcmds.h" + +static int set_frequency(struct poseidon *p, __u32 frequency); +static int poseidon_fm_close(struct file *filp); +static int poseidon_fm_open(struct file *filp); + +#define TUNER_FREQ_MIN_FM 76000000 +#define TUNER_FREQ_MAX_FM 108000000 + +#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1) +static int preemphasis[MAX_PREEMPHASIS] = { + TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */ + TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */ + TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */ +}; + +static int poseidon_check_mode_radio(struct poseidon *p) +{ + int ret; + u32 status; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/2); + ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE); + if (ret < 0) + goto out; + + ret = set_tuner_mode(p, TLG_MODE_FM_RADIO); + if (ret != 0) + goto out; + + ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status); + ret = send_set_req(p, TUNER_AUD_ANA_STD, + p->radio_data.pre_emphasis, &status); + ret |= send_set_req(p, TUNER_AUD_MODE, + TLG_TUNE_TVAUDIO_MODE_STEREO, &status); + ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL, + ATV_AUDIO_RATE_48K, &status); + ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status); +out: + return ret; +} + +#ifdef CONFIG_PM +static int pm_fm_suspend(struct poseidon *p) +{ + logpm(p); + pm_alsa_suspend(p); + usb_set_interface(p->udev, 0, 0); + msleep(300); + return 0; +} + +static int pm_fm_resume(struct poseidon *p) +{ + logpm(p); + poseidon_check_mode_radio(p); + set_frequency(p, p->radio_data.fm_freq); + pm_alsa_resume(p); + return 0; +} +#endif + +static int poseidon_fm_open(struct file *filp) +{ + struct video_device *vfd = video_devdata(filp); + struct poseidon *p = video_get_drvdata(vfd); + int ret = 0; + + if (!p) + return -1; + + mutex_lock(&p->lock); + if (p->state & POSEIDON_STATE_DISCONNECT) { + ret = -ENODEV; + goto out; + } + + if (p->state && !(p->state & POSEIDON_STATE_FM)) { + ret = -EBUSY; + goto out; + } + + usb_autopm_get_interface(p->interface); + if (0 == p->state) { + /* default pre-emphasis */ + if (p->radio_data.pre_emphasis == 0) + p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR; + set_debug_mode(vfd, debug_mode); + + ret = poseidon_check_mode_radio(p); + if (ret < 0) { + usb_autopm_put_interface(p->interface); + goto out; + } + p->state |= POSEIDON_STATE_FM; + } + p->radio_data.users++; + kref_get(&p->kref); + filp->private_data = p; +out: + mutex_unlock(&p->lock); + return ret; +} + +static int poseidon_fm_close(struct file *filp) +{ + struct poseidon *p = filp->private_data; + struct radio_data *fm = &p->radio_data; + uint32_t status; + + mutex_lock(&p->lock); + fm->users--; + if (0 == fm->users) + p->state &= ~POSEIDON_STATE_FM; + + if (fm->is_radio_streaming && filp == p->file_for_stream) { + fm->is_radio_streaming = 0; + send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status); + } + usb_autopm_put_interface(p->interface); + mutex_unlock(&p->lock); + + kref_put(&p->kref, poseidon_delete); + filp->private_data = NULL; + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct poseidon *p = file->private_data; + + strlcpy(v->driver, "tele-radio", sizeof(v->driver)); + strlcpy(v->card, "Telegent Poseidon", sizeof(v->card)); + usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info)); + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + return 0; +} + +static const struct v4l2_file_operations poseidon_fm_fops = { + .owner = THIS_MODULE, + .open = poseidon_fm_open, + .release = poseidon_fm_close, + .ioctl = video_ioctl2, +}; + +static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *vt) +{ + struct tuner_fm_sig_stat_s fm_stat = {}; + int ret, status, count = 5; + struct poseidon *p = file->private_data; + + if (vt->index != 0) + return -EINVAL; + + vt->type = V4L2_TUNER_RADIO; + vt->capability = V4L2_TUNER_CAP_STEREO; + vt->rangelow = TUNER_FREQ_MIN_FM / 62500; + vt->rangehigh = TUNER_FREQ_MAX_FM / 62500; + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + vt->audmode = V4L2_TUNER_MODE_STEREO; + vt->signal = 0; + vt->afc = 0; + + mutex_lock(&p->lock); + ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, + &fm_stat, &status, sizeof(fm_stat)); + + while (fm_stat.sig_lock_busy && count-- && !ret) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + + ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, + &fm_stat, &status, sizeof(fm_stat)); + } + mutex_unlock(&p->lock); + + if (ret || status) { + vt->signal = 0; + } else if ((fm_stat.sig_present || fm_stat.sig_locked) + && fm_stat.sig_strength == 0) { + vt->signal = 0xffff; + } else + vt->signal = (fm_stat.sig_strength * 255 / 10) << 8; + + return 0; +} + +static int fm_get_freq(struct file *file, void *priv, + struct v4l2_frequency *argp) +{ + struct poseidon *p = file->private_data; + + argp->frequency = p->radio_data.fm_freq; + return 0; +} + +static int set_frequency(struct poseidon *p, __u32 frequency) +{ + __u32 freq ; + int ret, status; + + mutex_lock(&p->lock); + + ret = send_set_req(p, TUNER_AUD_ANA_STD, + p->radio_data.pre_emphasis, &status); + + freq = (frequency * 125) * 500 / 1000;/* kHZ */ + if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) { + ret = -EINVAL; + goto error; + } + + ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status); + if (ret < 0) + goto error ; + ret = send_set_req(p, TAKE_REQUEST, 0, &status); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); + if (!p->radio_data.is_radio_streaming) { + ret = send_set_req(p, TAKE_REQUEST, 0, &status); + ret = send_set_req(p, PLAY_SERVICE, + TLG_TUNE_PLAY_SVC_START, &status); + p->radio_data.is_radio_streaming = 1; + } + p->radio_data.fm_freq = frequency; +error: + mutex_unlock(&p->lock); + return ret; +} + +static int fm_set_freq(struct file *file, void *priv, + struct v4l2_frequency *argp) +{ + struct poseidon *p = file->private_data; + + p->file_for_stream = file; +#ifdef CONFIG_PM + p->pm_suspend = pm_fm_suspend; + p->pm_resume = pm_fm_resume; +#endif + return set_frequency(p, argp->frequency); +} + +static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *arg) +{ + return 0; +} + +static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *ctrls) +{ + struct poseidon *p = file->private_data; + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) + continue; + + if (i < MAX_PREEMPHASIS) + ctrl->value = p->radio_data.pre_emphasis; + } + return 0; +} + +static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *ctrls) +{ + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) + continue; + + if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) { + struct poseidon *p = file->private_data; + int pre_emphasis = preemphasis[ctrl->value]; + u32 status; + + send_set_req(p, TUNER_AUD_ANA_STD, + pre_emphasis, &status); + p->radio_data.pre_emphasis = pre_emphasis; + } + } + return 0; +} + +static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + return 0; +} + +static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *ctrl) +{ + if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) + return -EINVAL; + + ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; + if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) { + /* return the next supported control */ + ctrl->id = V4L2_CID_TUNE_PREEMPHASIS; + v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED, + V4L2_PREEMPHASIS_75_uS, 1, + V4L2_PREEMPHASIS_50_uS); + ctrl->flags = V4L2_CTRL_FLAG_UPDATE; + return 0; + } + return -EINVAL; +} + +static int tlg_fm_vidioc_querymenu(struct file *file, void *fh, + struct v4l2_querymenu *qmenu) +{ + return v4l2_ctrl_query_menu(qmenu, NULL, NULL); +} + +static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + return vt->index > 0 ? -EINVAL : 0; +} +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va) +{ + return (va->index != 0) ? -EINVAL : 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + a->index = 0; + a->mode = 0; + a->capability = V4L2_AUDCAP_STEREO; + strcpy(a->name, "Radio"); + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, u32 i) +{ + return (i != 0) ? -EINVAL : 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, u32 *i) +{ + return (*i != 0) ? -EINVAL : 0; +} + +static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = tlg_fm_vidioc_queryctrl, + .vidioc_querymenu = tlg_fm_vidioc_querymenu, + .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl, + .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl, + .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl, + .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_tuner = tlg_fm_vidioc_g_tuner, + .vidioc_g_frequency = fm_get_freq, + .vidioc_s_frequency = fm_set_freq, +}; + +static struct video_device poseidon_fm_template = { + .name = "Telegent-Radio", + .fops = &poseidon_fm_fops, + .minor = -1, + .release = video_device_release, + .ioctl_ops = &poseidon_fm_ioctl_ops, +}; + +int poseidon_fm_init(struct poseidon *p) +{ + struct video_device *fm_dev; + + fm_dev = vdev_init(p, &poseidon_fm_template); + if (fm_dev == NULL) + return -1; + + if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) { + video_device_release(fm_dev); + return -1; + } + p->radio_data.fm_dev = fm_dev; + return 0; +} + +int poseidon_fm_exit(struct poseidon *p) +{ + destroy_video_device(&p->radio_data.fm_dev); + return 0; +} diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c new file mode 100644 index 000000000000..bfbf9e56b0a4 --- /dev/null +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -0,0 +1,1668 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pd-common.h" +#include "vendorcmds.h" + +#ifdef CONFIG_PM +static int pm_video_suspend(struct poseidon *pd); +static int pm_video_resume(struct poseidon *pd); +#endif +static void iso_bubble_handler(struct work_struct *w); + +static int usb_transfer_mode; +module_param(usb_transfer_mode, int, 0644); +MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous"); + +static const struct poseidon_format poseidon_formats[] = { + { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0}, + { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0}, +}; + +static const struct poseidon_tvnorm poseidon_tvnorms[] = { + { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D }, + { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B }, + { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G }, + { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H }, + { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I }, + { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M }, + { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO }, + { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO }, + { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M }, + { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J }, + { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B }, + { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D }, + { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G }, + { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H }, + { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K }, + { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 }, + { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L }, + { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 }, +}; +static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms); + +struct pd_audio_mode { + u32 tlg_audio_mode; + u32 v4l2_audio_sub; + u32 v4l2_audio_mode; +}; + +static const struct pd_audio_mode pd_audio_modes[] = { + { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO, + V4L2_TUNER_MODE_MONO }, + { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO, + V4L2_TUNER_MODE_STEREO }, + { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1, + V4L2_TUNER_MODE_LANG1 }, + { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2, + V4L2_TUNER_MODE_LANG2 }, + { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1, + V4L2_TUNER_MODE_LANG1_LANG2 } +}; +static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes); + +struct pd_input { + char *name; + uint32_t tlg_src; +}; + +static const struct pd_input pd_inputs[] = { + { "TV Antenna", TLG_SIG_SRC_ANTENNA }, + { "TV Cable", TLG_SIG_SRC_CABLE }, + { "TV SVideo", TLG_SIG_SRC_SVIDEO }, + { "TV Composite", TLG_SIG_SRC_COMPOSITE } +}; +static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs); + +struct poseidon_control { + struct v4l2_queryctrl v4l2_ctrl; + enum cmd_custom_param_id vc_id; +}; + +static struct poseidon_control controls[] = { + { + { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, + "brightness", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_BRIGHTNESS_CTRL + }, { + { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, + "contrast", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_CONTRAST_CTRL, + }, { + { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER, + "hue", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_HUE_CTRL, + }, { + { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER, + "saturation", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_SATURATION_CTRL, + }, +}; + +struct video_std_to_audio_std { + v4l2_std_id video_std; + int audio_std; +}; + +static const struct video_std_to_audio_std video_to_audio_map[] = { + /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64, + 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */ + { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D | + V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM }, + + /* country : { 1, 52, 54, 55, 886 } */ + {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC}, + + /* country : { 81 } */ + { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ }, + + /* other country : TLG_TUNE_ASTD_A2 */ +}; +static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map); + +static int get_audio_std(v4l2_std_id v4l2_std) +{ + int i = 0; + + for (; i < map_size; i++) { + if (v4l2_std & video_to_audio_map[i].video_std) + return video_to_audio_map[i].audio_std; + } + return TLG_TUNE_ASTD_A2; +} + +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct front_face *front = fh; + struct poseidon *p = front->pd; + + logs(front); + + strcpy(cap->driver, "tele-video"); + strcpy(cap->card, "Telegent Poseidon"); + usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; + return 0; +} + +/*====================================================================*/ +static void init_copy(struct video_data *video, bool index) +{ + struct front_face *front = video->front; + + video->field_count = index; + video->lines_copied = 0; + video->prev_left = 0 ; + video->dst = (char *)videobuf_to_vmalloc(front->curr_frame) + + index * video->lines_size; + video->vbi->copied = 0; /* set it here */ +} + +static bool get_frame(struct front_face *front, int *need_init) +{ + struct videobuf_buffer *vb = front->curr_frame; + + if (vb) + return true; + + spin_lock(&front->queue_lock); + if (!list_empty(&front->active)) { + vb = list_entry(front->active.next, + struct videobuf_buffer, queue); + if (need_init) + *need_init = 1; + front->curr_frame = vb; + list_del_init(&vb->queue); + } + spin_unlock(&front->queue_lock); + + return !!vb; +} + +/* check if the video's buffer is ready */ +static bool get_video_frame(struct front_face *front, struct video_data *video) +{ + int need_init = 0; + bool ret = true; + + ret = get_frame(front, &need_init); + if (ret && need_init) + init_copy(video, 0); + return ret; +} + +static void submit_frame(struct front_face *front) +{ + struct videobuf_buffer *vb = front->curr_frame; + + if (vb == NULL) + return; + + front->curr_frame = NULL; + vb->state = VIDEOBUF_DONE; + vb->field_count++; + do_gettimeofday(&vb->ts); + + wake_up(&vb->done); +} + +/* + * A frame is composed of two fields. If we receive all the two fields, + * call the submit_frame() to submit the whole frame to applications. + */ +static void end_field(struct video_data *video) +{ + /* logs(video->front); */ + if (1 == video->field_count) + submit_frame(video->front); + else + init_copy(video, 1); +} + +static void copy_video_data(struct video_data *video, char *src, + unsigned int count) +{ +#define copy_data(len) \ + do { \ + if (++video->lines_copied > video->lines_per_field) \ + goto overflow; \ + memcpy(video->dst, src, len);\ + video->dst += len + video->lines_size; \ + src += len; \ + count -= len; \ + } while (0) + + while (count && count >= video->lines_size) { + if (video->prev_left) { + copy_data(video->prev_left); + video->prev_left = 0; + continue; + } + copy_data(video->lines_size); + } + if (count && count < video->lines_size) { + memcpy(video->dst, src, count); + + video->prev_left = video->lines_size - count; + video->dst += count; + } + return; + +overflow: + end_field(video); +} + +static void check_trailer(struct video_data *video, char *src, int count) +{ + struct vbi_data *vbi = video->vbi; + int offset; /* trailer's offset */ + char *buf; + + offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2) + - (vbi->copied + video->lines_size * video->lines_copied); + if (video->prev_left) + offset -= (video->lines_size - video->prev_left); + + if (offset > count || offset <= 0) + goto short_package; + + buf = src + offset; + + /* trailer : (VFHS) + U32 + U32 + field_num */ + if (!strncmp(buf, "VFHS", 4)) { + int field_num = *((u32 *)(buf + 12)); + + if ((field_num & 1) ^ video->field_count) { + init_copy(video, video->field_count); + return; + } + copy_video_data(video, src, offset); + } +short_package: + end_field(video); +} + +/* ========== Check this more carefully! =========== */ +static inline void copy_vbi_data(struct vbi_data *vbi, + char *src, unsigned int count) +{ + struct front_face *front = vbi->front; + + if (front && get_frame(front, NULL)) { + char *buf = videobuf_to_vmalloc(front->curr_frame); + + if (vbi->video->field_count) + buf += (vbi->vbi_size / 2); + memcpy(buf + vbi->copied, src, count); + } + vbi->copied += count; +} + +/* + * Copy the normal data (VBI or VIDEO) without the trailer. + * VBI is not interlaced, while VIDEO is interlaced. + */ +static inline void copy_vbi_video_data(struct video_data *video, + char *src, unsigned int count) +{ + struct vbi_data *vbi = video->vbi; + unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied; + + if (vbi_delta >= count) { + copy_vbi_data(vbi, src, count); + } else { + if (vbi_delta) { + copy_vbi_data(vbi, src, vbi_delta); + + /* we receive the two fields of the VBI*/ + if (vbi->front && video->field_count) + submit_frame(vbi->front); + } + copy_video_data(video, src + vbi_delta, count - vbi_delta); + } +} + +static void urb_complete_bulk(struct urb *urb) +{ + struct front_face *front = urb->context; + struct video_data *video = &front->pd->video_data; + char *src = (char *)urb->transfer_buffer; + int count = urb->actual_length; + int ret = 0; + + if (!video->is_streaming || urb->status) { + if (urb->status == -EPROTO) + goto resend_it; + return; + } + if (!get_video_frame(front, video)) + goto resend_it; + + if (count == urb->transfer_buffer_length) + copy_vbi_video_data(video, src, count); + else + check_trailer(video, src, count); + +resend_it: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + log(" submit failed: error %d", ret); +} + +/************************* for ISO *********************/ +#define GET_SUCCESS (0) +#define GET_TRAILER (1) +#define GET_TOO_MUCH_BUBBLE (2) +#define GET_NONE (3) +static int get_chunk(int start, struct urb *urb, + int *head, int *tail, int *bubble_err) +{ + struct usb_iso_packet_descriptor *pkt = NULL; + int ret = GET_SUCCESS; + + for (*head = *tail = -1; start < urb->number_of_packets; start++) { + pkt = &urb->iso_frame_desc[start]; + + /* handle the bubble of the Hub */ + if (-EOVERFLOW == pkt->status) { + if (++*bubble_err > urb->number_of_packets / 3) + return GET_TOO_MUCH_BUBBLE; + continue; + } + + /* This is the gap */ + if (pkt->status || pkt->actual_length <= 0 + || pkt->actual_length > ISO_PKT_SIZE) { + if (*head != -1) + break; + continue; + } + + /* a good isochronous packet */ + if (pkt->actual_length == ISO_PKT_SIZE) { + if (*head == -1) + *head = start; + *tail = start; + continue; + } + + /* trailer is here */ + if (pkt->actual_length < ISO_PKT_SIZE) { + if (*head == -1) { + *head = start; + *tail = start; + return GET_TRAILER; + } + break; + } + } + + if (*head == -1 && *tail == -1) + ret = GET_NONE; + return ret; +} + +/* + * |__|------|___|-----|_______| + * ^ ^ + * | | + * gap gap + */ +static void urb_complete_iso(struct urb *urb) +{ + struct front_face *front = urb->context; + struct video_data *video = &front->pd->video_data; + int bubble_err = 0, head = 0, tail = 0; + char *src = (char *)urb->transfer_buffer; + int ret = 0; + + if (!video->is_streaming) + return; + + do { + if (!get_video_frame(front, video)) + goto out; + + switch (get_chunk(head, urb, &head, &tail, &bubble_err)) { + case GET_SUCCESS: + copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE), + (tail - head + 1) * ISO_PKT_SIZE); + break; + case GET_TRAILER: + check_trailer(video, src + (head * ISO_PKT_SIZE), + ISO_PKT_SIZE); + break; + case GET_NONE: + goto out; + case GET_TOO_MUCH_BUBBLE: + log("\t We got too much bubble"); + schedule_work(&video->bubble_work); + return; + } + } while (head = tail + 1, head < urb->number_of_packets); + +out: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + log("usb_submit_urb err : %d", ret); +} +/*============================= [ end ] =====================*/ + +static int prepare_iso_urb(struct video_data *video) +{ + struct usb_device *udev = video->pd->udev; + int i; + + if (video->urb_array[0]) + return 0; + + for (i = 0; i < SBUF_NUM; i++) { + struct urb *urb; + void *mem; + int j; + + urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL); + if (urb == NULL) + goto out; + + video->urb_array[i] = urb; + mem = usb_alloc_coherent(udev, + ISO_PKT_SIZE * PK_PER_URB, + GFP_KERNEL, + &urb->transfer_dma); + + urb->complete = urb_complete_iso; /* handler */ + urb->dev = udev; + urb->context = video->front; + urb->pipe = usb_rcvisocpipe(udev, + video->endpoint_addr); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->number_of_packets = PK_PER_URB; + urb->transfer_buffer = mem; + urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE; + + for (j = 0; j < PK_PER_URB; j++) { + urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j; + urb->iso_frame_desc[j].length = ISO_PKT_SIZE; + } + } + return 0; +out: + for (; i > 0; i--) + ; + return -ENOMEM; +} + +/* return the succeeded number of the allocation */ +int alloc_bulk_urbs_generic(struct urb **urb_array, int num, + struct usb_device *udev, u8 ep_addr, + int buf_size, gfp_t gfp_flags, + usb_complete_t complete_fn, void *context) +{ + int i = 0; + + for (; i < num; i++) { + void *mem; + struct urb *urb = usb_alloc_urb(0, gfp_flags); + if (urb == NULL) + return i; + + mem = usb_alloc_coherent(udev, buf_size, gfp_flags, + &urb->transfer_dma); + if (mem == NULL) { + usb_free_urb(urb); + return i; + } + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr), + mem, buf_size, complete_fn, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + urb_array[i] = urb; + } + return i; +} + +void free_all_urb_generic(struct urb **urb_array, int num) +{ + int i; + struct urb *urb; + + for (i = 0; i < num; i++) { + urb = urb_array[i]; + if (urb) { + usb_free_coherent(urb->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); + urb_array[i] = NULL; + } + } +} + +static int prepare_bulk_urb(struct video_data *video) +{ + if (video->urb_array[0]) + return 0; + + alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM, + video->pd->udev, video->endpoint_addr, + 0x2000, GFP_KERNEL, + urb_complete_bulk, video->front); + return 0; +} + +/* free the URBs */ +static void free_all_urb(struct video_data *video) +{ + free_all_urb_generic(video->urb_array, SBUF_NUM); +} + +static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + videobuf_vmalloc_free(vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct front_face *front = q->priv_data; + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &front->active); +} + +static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct front_face *front = q->priv_data; + int rc; + + switch (front->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (VIDEOBUF_NEEDS_INIT == vb->state) { + struct v4l2_pix_format *pix; + + pix = &front->pd->video_data.context.pix; + vb->size = pix->sizeimage; /* real frame size */ + vb->width = pix->width; + vb->height = pix->height; + rc = videobuf_iolock(q, vb, NULL); + if (rc < 0) + return rc; + } + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->size = front->pd->vbi_data.vbi_size; + rc = videobuf_iolock(q, vb, NULL); + if (rc < 0) + return rc; + } + break; + default: + return -EINVAL; + } + vb->field = field; + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +static int fire_all_urb(struct video_data *video) +{ + int i, ret; + + video->is_streaming = 1; + + for (i = 0; i < SBUF_NUM; i++) { + ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL); + if (ret) + log("(%d) failed: error %d", i, ret); + } + return ret; +} + +static int start_video_stream(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + s32 cmd_status; + + send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status); + + if (pd->cur_transfer_mode) { + prepare_iso_urb(video); + INIT_WORK(&video->bubble_work, iso_bubble_handler); + } else { + /* The bulk mode does not need a bubble handler */ + prepare_bulk_urb(video); + } + fire_all_urb(video); + return 0; +} + +static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct front_face *front = q->priv_data; + struct poseidon *pd = front->pd; + + switch (front->type) { + default: + return -EINVAL; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct video_data *video = &pd->video_data; + struct v4l2_pix_format *pix = &video->context.pix; + + *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */ + if (*count < 4) + *count = 4; + if (1) { + /* same in different altersetting */ + video->endpoint_addr = 0x82; + video->vbi = &pd->vbi_data; + video->vbi->video = video; + video->pd = pd; + video->lines_per_field = pix->height / 2; + video->lines_size = pix->width * 2; + video->front = front; + } + return start_video_stream(pd); + } + + case V4L2_BUF_TYPE_VBI_CAPTURE: { + struct vbi_data *vbi = &pd->vbi_data; + + *size = PAGE_ALIGN(vbi->vbi_size); + log("size : %d", *size); + if (*count == 0) + *count = 4; + } + break; + } + return 0; +} + +static struct videobuf_queue_ops pd_video_qops = { + .buf_setup = pd_buf_setup, + .buf_prepare = pd_buf_prepare, + .buf_queue = pd_buf_queue, + .buf_release = pd_buf_release, +}; + +static int vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (ARRAY_SIZE(poseidon_formats) <= f->index) + return -EINVAL; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = 0; + f->pixelformat = poseidon_formats[f->index].fourcc; + strcpy(f->description, poseidon_formats[f->index].name); + return 0; +} + +static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + + logs(front); + f->fmt.pix = pd->video_data.context.pix; + return 0; +} + +static int vidioc_try_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + return 0; +} + +/* + * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while + * Mplayer calls them in the reverse order. + */ +static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) +{ + struct video_data *video = &pd->video_data; + struct running_context *context = &video->context; + struct v4l2_pix_format *pix_def = &context->pix; + s32 ret = 0, cmd_status = 0, vid_resol; + + /* set the pixel format to firmware */ + if (pix->pixelformat == V4L2_PIX_FMT_RGB565) { + vid_resol = TLG_TUNER_VID_FORMAT_RGB_565; + } else { + pix->pixelformat = V4L2_PIX_FMT_YUYV; + vid_resol = TLG_TUNER_VID_FORMAT_YUV; + } + ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL, + vid_resol, &cmd_status); + + /* set the resolution to firmware */ + vid_resol = TLG_TUNE_VID_RES_720; + switch (pix->width) { + case 704: + vid_resol = TLG_TUNE_VID_RES_704; + break; + default: + pix->width = 720; + case 720: + break; + } + ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, + vid_resol, &cmd_status); + if (ret || cmd_status) + return -EBUSY; + + pix_def->pixelformat = pix->pixelformat; /* save it */ + pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; + + /* Compare with the default setting */ + if ((pix_def->width != pix->width) + || (pix_def->height != pix->height)) { + pix_def->width = pix->width; + pix_def->height = pix->height; + pix_def->bytesperline = pix->width * 2; + pix_def->sizeimage = pix->width * pix->height * 2; + } + *pix = *pix_def; + + return 0; +} + +static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + + logs(front); + /* stop VBI here */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + return -EINVAL; + + mutex_lock(&pd->lock); + if (pd->file_for_stream == NULL) + pd->file_for_stream = file; + else if (file != pd->file_for_stream) { + mutex_unlock(&pd->lock); + return -EINVAL; + } + + pd_vidioc_s_fmt(pd, &f->fmt.pix); + mutex_unlock(&pd->lock); + return 0; +} + +static int vidioc_g_fmt_vbi(struct file *file, void *fh, + struct v4l2_format *v4l2_f) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi; + + vbi_fmt->samples_per_line = 720 * 2; + vbi_fmt->sampling_rate = 6750000 * 4; + vbi_fmt->sample_format = V4L2_PIX_FMT_GREY; + vbi_fmt->offset = 64 * 4; /*FIXME: why offset */ + if (pd->video_data.context.tvnormid & V4L2_STD_525_60) { + vbi_fmt->start[0] = 10; + vbi_fmt->start[1] = 264; + vbi_fmt->count[0] = V4L_NTSC_VBI_LINES; + vbi_fmt->count[1] = V4L_NTSC_VBI_LINES; + } else { + vbi_fmt->start[0] = 6; + vbi_fmt->start[1] = 314; + vbi_fmt->count[0] = V4L_PAL_VBI_LINES; + vbi_fmt->count[1] = V4L_PAL_VBI_LINES; + } + vbi_fmt->flags = V4L2_VBI_UNSYNC; + logs(front); + return 0; +} + +static int set_std(struct poseidon *pd, v4l2_std_id *norm) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + struct running_context *context; + struct v4l2_pix_format *pix; + s32 i, ret = 0, cmd_status, param; + int height; + + for (i = 0; i < POSEIDON_TVNORMS; i++) { + if (*norm & poseidon_tvnorms[i].v4l2_id) { + param = poseidon_tvnorms[i].tlg_tvnorm; + log("name : %s", poseidon_tvnorms[i].name); + goto found; + } + } + return -EINVAL; +found: + mutex_lock(&pd->lock); + ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status); + if (ret || cmd_status) + goto out; + + /* Set vbi size and check the height of the frame */ + context = &video->context; + context->tvnormid = poseidon_tvnorms[i].v4l2_id; + if (context->tvnormid & V4L2_STD_525_60) { + vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE; + height = 480; + } else { + vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE; + height = 576; + } + + pix = &context->pix; + if (pix->height != height) { + pix->height = height; + pix->sizeimage = pix->width * pix->height * 2; + } + +out: + mutex_unlock(&pd->lock); + return ret; +} + +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct front_face *front = fh; + logs(front); + return set_std(front->pd, norm); +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in) +{ + struct front_face *front = fh; + + if (in->index < 0 || in->index >= POSEIDON_INPUTS) + return -EINVAL; + strcpy(in->name, pd_inputs[in->index].name); + in->type = V4L2_INPUT_TYPE_TUNER; + + /* + * the audio input index mixed with this video input, + * Poseidon only have one audio/video, set to "0" + */ + in->audioset = 0; + in->tuner = 0; + in->std = V4L2_STD_ALL; + in->status = 0; + logs(front); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct running_context *context = &pd->video_data.context; + + logs(front); + *i = context->sig_index; + return 0; +} + +/* We can support several inputs */ +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + s32 ret, cmd_status; + + if (i < 0 || i >= POSEIDON_INPUTS) + return -EINVAL; + ret = send_set_req(pd, SGNL_SRC_SEL, + pd_inputs[i].tlg_src, &cmd_status); + if (ret) + return ret; + + pd->video_data.context.sig_index = i; + return 0; +} + +static struct poseidon_control *check_control_id(__u32 id) +{ + struct poseidon_control *control = &controls[0]; + int array_size = ARRAY_SIZE(controls); + + for (; control < &controls[array_size]; control++) + if (control->v4l2_ctrl.id == id) + return control; + return NULL; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *a) +{ + struct poseidon_control *control = NULL; + + control = check_control_id(a->id); + if (!control) + return -EINVAL; + + *a = control->v4l2_ctrl; + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct poseidon_control *control = NULL; + struct tuner_custom_parameter_s tuner_param; + s32 ret = 0, cmd_status; + + control = check_control_id(ctrl->id); + if (!control) + return -EINVAL; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id, + &tuner_param, &cmd_status, sizeof(tuner_param)); + mutex_unlock(&pd->lock); + + if (ret || cmd_status) + return -1; + + ctrl->value = tuner_param.param_value; + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) +{ + struct tuner_custom_parameter_s param = {0}; + struct poseidon_control *control = NULL; + struct front_face *front = fh; + struct poseidon *pd = front->pd; + s32 ret = 0, cmd_status, params; + + control = check_control_id(a->id); + if (!control) + return -EINVAL; + + param.param_value = a->value; + param.param_id = control->vc_id; + params = *(s32 *)¶m; /* temp code */ + + mutex_lock(&pd->lock); + ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status); + ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + mutex_unlock(&pd->lock); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); + return ret; +} + +/* Audio ioctls */ +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + if (0 != a->index) + return -EINVAL; + a->capability = V4L2_AUDCAP_STEREO; + strcpy(a->name, "USB audio in"); + /*Poseidon have no AVL function.*/ + a->mode = 0; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + a->index = 0; + a->capability = V4L2_AUDCAP_STEREO; + strcpy(a->name, "USB audio in"); + a->mode = 0; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + return (0 == a->index) ? 0 : -EINVAL; +} + +/* Tuner ioctls */ +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct tuner_atv_sig_stat_s atv_stat; + s32 count = 5, ret, cmd_status; + int index; + + if (0 != tuner->index) + return -EINVAL; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, + &atv_stat, &cmd_status, sizeof(atv_stat)); + + while (atv_stat.sig_lock_busy && count-- && !ret) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, + &atv_stat, &cmd_status, sizeof(atv_stat)); + } + mutex_unlock(&pd->lock); + + if (debug_mode) + log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength); + + if (ret || cmd_status) + tuner->signal = 0; + else if (atv_stat.sig_present && !atv_stat.sig_strength) + tuner->signal = 0xFFFF; + else + tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8; + + strcpy(tuner->name, "Telegent Systems"); + tuner->type = V4L2_TUNER_ANALOG_TV; + tuner->rangelow = TUNER_FREQ_MIN / 62500; + tuner->rangehigh = TUNER_FREQ_MAX / 62500; + tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + index = pd->video_data.context.audio_idx; + tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub; + tuner->audmode = pd_audio_modes[index].v4l2_audio_mode; + tuner->afc = 0; + logs(front); + return 0; +} + +static int pd_vidioc_s_tuner(struct poseidon *pd, int index) +{ + s32 ret = 0, cmd_status, param, audiomode; + + mutex_lock(&pd->lock); + param = pd_audio_modes[index].tlg_audio_mode; + ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status); + audiomode = get_audio_std(pd->video_data.context.tvnormid); + ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, + &cmd_status); + if (!ret) + pd->video_data.context.audio_idx = index; + mutex_unlock(&pd->lock); + return ret; +} + +static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + int index; + + if (0 != a->index) + return -EINVAL; + logs(front); + for (index = 0; index < POSEIDON_AUDIOMODS; index++) + if (a->audmode == pd_audio_modes[index].v4l2_audio_mode) + return pd_vidioc_s_tuner(pd, index); + return -EINVAL; +} + +static int vidioc_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *freq) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct running_context *context = &pd->video_data.context; + + if (0 != freq->tuner) + return -EINVAL; + freq->frequency = context->freq; + freq->type = V4L2_TUNER_ANALOG_TV; + return 0; +} + +static int set_frequency(struct poseidon *pd, __u32 frequency) +{ + s32 ret = 0, param, cmd_status; + struct running_context *context = &pd->video_data.context; + + param = frequency * 62500 / 1000; + if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000) + return -EINVAL; + + mutex_lock(&pd->lock); + ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status); + ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + + msleep(250); /* wait for a while until the hardware is ready. */ + context->freq = frequency; + mutex_unlock(&pd->lock); + return ret; +} + +static int vidioc_s_frequency(struct file *file, void *fh, + struct v4l2_frequency *freq) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + + logs(front); +#ifdef CONFIG_PM + pd->pm_suspend = pm_video_suspend; + pd->pm_resume = pm_video_resume; +#endif + return set_frequency(pd, freq->frequency); +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *b) +{ + struct front_face *front = file->private_data; + logs(front); + return videobuf_reqbufs(&front->q, b); +} + +static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct front_face *front = file->private_data; + logs(front); + return videobuf_querybuf(&front->q, b); +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct front_face *front = file->private_data; + return videobuf_qbuf(&front->q, b); +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct front_face *front = file->private_data; + return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK); +} + +/* Just stop the URBs, do not free the URBs */ +static int usb_transfer_stop(struct video_data *video) +{ + if (video->is_streaming) { + int i; + s32 cmd_status; + struct poseidon *pd = video->pd; + + video->is_streaming = 0; + for (i = 0; i < SBUF_NUM; ++i) { + if (video->urb_array[i]) + usb_kill_urb(video->urb_array[i]); + } + + send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, + &cmd_status); + } + return 0; +} + +int stop_all_video_stream(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + + mutex_lock(&pd->lock); + if (video->is_streaming) { + struct front_face *front = video->front; + + /* stop the URBs */ + usb_transfer_stop(video); + free_all_urb(video); + + /* stop the host side of VIDEO */ + videobuf_stop(&front->q); + videobuf_mmap_free(&front->q); + + /* stop the host side of VBI */ + front = vbi->front; + if (front) { + videobuf_stop(&front->q); + videobuf_mmap_free(&front->q); + } + } + mutex_unlock(&pd->lock); + return 0; +} + +/* + * The bubbles can seriously damage the video's quality, + * though it occurs in very rare situation. + */ +static void iso_bubble_handler(struct work_struct *w) +{ + struct video_data *video; + struct poseidon *pd; + + video = container_of(w, struct video_data, bubble_work); + pd = video->pd; + + mutex_lock(&pd->lock); + usb_transfer_stop(video); + msleep(500); + start_video_stream(pd); + mutex_unlock(&pd->lock); +} + + +static int vidioc_streamon(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct front_face *front = fh; + + logs(front); + if (unlikely(type != front->type)) + return -EINVAL; + return videobuf_streamon(&front->q); +} + +static int vidioc_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct front_face *front = file->private_data; + + logs(front); + if (unlikely(type != front->type)) + return -EINVAL; + return videobuf_streamoff(&front->q); +} + +/* Set the firmware's default values : need altersetting */ +static int pd_video_checkmode(struct poseidon *pd) +{ + s32 ret = 0, cmd_status, audiomode; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/2); + + /* choose the altersetting */ + ret = usb_set_interface(pd->udev, 0, + (pd->cur_transfer_mode ? + ISO_3K_BULK_ALTERNATE_IFACE : + BULK_ALTERNATE_IFACE)); + if (ret < 0) + goto error; + + /* set default parameters for PAL-D , with the VBI enabled*/ + ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV); + ret |= send_set_req(pd, SGNL_SRC_SEL, + TLG_SIG_SRC_ANTENNA, &cmd_status); + ret |= send_set_req(pd, VIDEO_STD_SEL, + TLG_TUNE_VSTD_PAL_D, &cmd_status); + ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL, + TLG_TUNER_VID_FORMAT_YUV, &cmd_status); + ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, + TLG_TUNE_VID_RES_720, &cmd_status); + ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status); + ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */ + + /* set the audio */ + audiomode = get_audio_std(pd->video_data.context.tvnormid); + ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status); + ret |= send_set_req(pd, TUNER_AUD_MODE, + TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status); + ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL, + ATV_AUDIO_RATE_48K, &cmd_status); +error: + return ret; +} + +#ifdef CONFIG_PM +static int pm_video_suspend(struct poseidon *pd) +{ + /* stop audio */ + pm_alsa_suspend(pd); + + /* stop and free all the URBs */ + usb_transfer_stop(&pd->video_data); + free_all_urb(&pd->video_data); + + /* reset the interface */ + usb_set_interface(pd->udev, 0, 0); + msleep(300); + return 0; +} + +static int restore_v4l2_context(struct poseidon *pd, + struct running_context *context) +{ + struct front_face *front = pd->video_data.front; + + pd_video_checkmode(pd); + + set_std(pd, &context->tvnormid); + vidioc_s_input(NULL, front, context->sig_index); + pd_vidioc_s_tuner(pd, context->audio_idx); + pd_vidioc_s_fmt(pd, &context->pix); + set_frequency(pd, context->freq); + return 0; +} + +static int pm_video_resume(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + + /* resume the video */ + /* [1] restore the origin V4L2 parameters */ + restore_v4l2_context(pd, &video->context); + + /* [2] initiate video copy variables */ + if (video->front->curr_frame) + init_copy(video, 0); + + /* [3] fire urbs */ + start_video_stream(pd); + + /* resume the audio */ + pm_alsa_resume(pd); + return 0; +} +#endif + +void set_debug_mode(struct video_device *vfd, int debug_mode) +{ + vfd->debug = 0; + if (debug_mode & 0x1) + vfd->debug = V4L2_DEBUG_IOCTL; + if (debug_mode & 0x2) + vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; +} + +static void init_video_context(struct running_context *context) +{ + context->sig_index = 0; + context->audio_idx = 1; /* stereo */ + context->tvnormid = V4L2_STD_PAL_D; + context->pix = (struct v4l2_pix_format) { + .width = 720, + .height = 576, + .pixelformat = V4L2_PIX_FMT_YUYV, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = 720 * 2, + .sizeimage = 720 * 576 * 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .priv = 0 + }; +} + +static int pd_video_open(struct file *file) +{ + struct video_device *vfd = video_devdata(file); + struct poseidon *pd = video_get_drvdata(vfd); + struct front_face *front = NULL; + int ret = -ENOMEM; + + mutex_lock(&pd->lock); + usb_autopm_get_interface(pd->interface); + + if (vfd->vfl_type == VFL_TYPE_GRABBER + && !(pd->state & POSEIDON_STATE_ANALOG)) { + front = kzalloc(sizeof(struct front_face), GFP_KERNEL); + if (!front) + goto out; + + pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */ + init_video_context(&pd->video_data.context); + + ret = pd_video_checkmode(pd); + if (ret < 0) { + kfree(front); + ret = -1; + goto out; + } + + pd->state |= POSEIDON_STATE_ANALOG; + front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pd->video_data.users++; + set_debug_mode(vfd, debug_mode); + + videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, + NULL, &front->queue_lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED,/* video is interlacd */ + sizeof(struct videobuf_buffer),/*it's enough*/ + front, NULL); + } else if (vfd->vfl_type == VFL_TYPE_VBI + && !(pd->state & POSEIDON_STATE_VBI)) { + front = kzalloc(sizeof(struct front_face), GFP_KERNEL); + if (!front) + goto out; + + pd->state |= POSEIDON_STATE_VBI; + front->type = V4L2_BUF_TYPE_VBI_CAPTURE; + pd->vbi_data.front = front; + pd->vbi_data.users++; + + videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, + NULL, &front->queue_lock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_NONE, /* vbi is NONE mode */ + sizeof(struct videobuf_buffer), + front, NULL); + } else { + /* maybe add FM support here */ + log("other "); + ret = -EINVAL; + goto out; + } + + front->pd = pd; + front->curr_frame = NULL; + INIT_LIST_HEAD(&front->active); + spin_lock_init(&front->queue_lock); + + file->private_data = front; + kref_get(&pd->kref); + + mutex_unlock(&pd->lock); + return 0; +out: + usb_autopm_put_interface(pd->interface); + mutex_unlock(&pd->lock); + return ret; +} + +static int pd_video_release(struct file *file) +{ + struct front_face *front = file->private_data; + struct poseidon *pd = front->pd; + s32 cmd_status = 0; + + logs(front); + mutex_lock(&pd->lock); + + if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pd->state &= ~POSEIDON_STATE_ANALOG; + + /* stop the device, and free the URBs */ + usb_transfer_stop(&pd->video_data); + free_all_urb(&pd->video_data); + + /* stop the firmware */ + send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, + &cmd_status); + + pd->file_for_stream = NULL; + pd->video_data.users--; + } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + pd->state &= ~POSEIDON_STATE_VBI; + pd->vbi_data.front = NULL; + pd->vbi_data.users--; + } + videobuf_stop(&front->q); + videobuf_mmap_free(&front->q); + + usb_autopm_put_interface(pd->interface); + mutex_unlock(&pd->lock); + + kfree(front); + file->private_data = NULL; + kref_put(&pd->kref, poseidon_delete); + return 0; +} + +static int pd_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct front_face *front = file->private_data; + return videobuf_mmap_mapper(&front->q, vma); +} + +static unsigned int pd_video_poll(struct file *file, poll_table *table) +{ + struct front_face *front = file->private_data; + return videobuf_poll_stream(file, &front->q, table); +} + +static ssize_t pd_video_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct front_face *front = file->private_data; + return videobuf_read_stream(&front->q, buffer, count, ppos, + 0, file->f_flags & O_NONBLOCK); +} + +/* This struct works for both VIDEO and VBI */ +static const struct v4l2_file_operations pd_video_fops = { + .owner = THIS_MODULE, + .open = pd_video_open, + .release = pd_video_release, + .read = pd_video_read, + .poll = pd_video_poll, + .mmap = pd_video_mmap, + .ioctl = video_ioctl2, /* maybe changed in future */ +}; + +static const struct v4l2_ioctl_ops pd_video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + /* Video format */ + .vidioc_g_fmt_vid_cap = vidioc_g_fmt, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */ + .vidioc_try_fmt_vid_cap = vidioc_try_fmt, + + /* Input */ + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enum_input = vidioc_enum_input, + + /* Audio ioctls */ + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + + /* Tuner ioctls */ + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + + /* Buffer handlers */ + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + + /* Stream on/off */ + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + + /* Control handling */ + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + +static struct video_device pd_video_template = { + .name = "Telegent-Video", + .fops = &pd_video_fops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_ALL, + .ioctl_ops = &pd_video_ioctl_ops, +}; + +struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (vfd == NULL) + return NULL; + *vfd = *tmp; + vfd->minor = -1; + vfd->v4l2_dev = &pd->v4l2_dev; + /*vfd->parent = &(pd->udev->dev); */ + vfd->release = video_device_release; + video_set_drvdata(vfd, pd); + return vfd; +} + +void destroy_video_device(struct video_device **v_dev) +{ + struct video_device *dev = *v_dev; + + if (dev == NULL) + return; + + if (video_is_registered(dev)) + video_unregister_device(dev); + else + video_device_release(dev); + *v_dev = NULL; +} + +void pd_video_exit(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + + destroy_video_device(&video->v_dev); + destroy_video_device(&vbi->v_dev); + log(); +} + +int pd_video_init(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + int ret = -ENOMEM; + + video->v_dev = vdev_init(pd, &pd_video_template); + if (video->v_dev == NULL) + goto out; + + ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1); + if (ret != 0) + goto out; + + /* VBI uses the same template as video */ + vbi->v_dev = vdev_init(pd, &pd_video_template); + if (vbi->v_dev == NULL) { + ret = -ENOMEM; + goto out; + } + ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1); + if (ret != 0) + goto out; + log("register VIDEO/VBI devices"); + return 0; +out: + log("VIDEO/VBI devices register failed, : %d", ret); + pd_video_exit(pd); + return ret; +} + diff --git a/drivers/media/usb/tlg2300/vendorcmds.h b/drivers/media/usb/tlg2300/vendorcmds.h new file mode 100644 index 000000000000..ba6f4ae3b2c2 --- /dev/null +++ b/drivers/media/usb/tlg2300/vendorcmds.h @@ -0,0 +1,243 @@ +#ifndef VENDOR_CMD_H_ +#define VENDOR_CMD_H_ + +#define BULK_ALTERNATE_IFACE (2) +#define ISO_3K_BULK_ALTERNATE_IFACE (1) +#define REQ_SET_CMD (0X00) +#define REQ_GET_CMD (0X80) + +enum tlg__analog_audio_standard { + TLG_TUNE_ASTD_NONE = 0x00000000, + TLG_TUNE_ASTD_A2 = 0x00000001, + TLG_TUNE_ASTD_NICAM = 0x00000002, + TLG_TUNE_ASTD_EIAJ = 0x00000004, + TLG_TUNE_ASTD_BTSC = 0x00000008, + TLG_TUNE_ASTD_FM_US = 0x00000010, + TLG_TUNE_ASTD_FM_EUR = 0x00000020, + TLG_TUNE_ASTD_ALL = 0x0000003f +}; + +/* + * identifiers for Custom Parameter messages. + * @typedef cmd_custom_param_id_t + */ +enum cmd_custom_param_id { + CUST_PARM_ID_NONE = 0x00, + CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01, + CUST_PARM_ID_CONTRAST_CTRL = 0x02, + CUST_PARM_ID_HUE_CTRL = 0x03, + CUST_PARM_ID_SATURATION_CTRL = 0x04, + CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10, + CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11, + CUST_PARM_ID_MAX +}; + +struct tuner_custom_parameter_s { + uint16_t param_id; /* Parameter identifier */ + uint16_t param_value; /* Parameter value */ +}; + +struct tuner_ber_rate_s { + uint32_t ber_rate; /* BER sample rate in seconds */ +}; + +struct tuner_atv_sig_stat_s { + uint32_t sig_present; + uint32_t sig_locked; + uint32_t sig_lock_busy; + uint32_t sig_strength; /* milliDb */ + uint32_t tv_audio_chan; /* mono/stereo/sap*/ + uint32_t mvision_stat; /* macrovision status */ +}; + +struct tuner_dtv_sig_stat_s { + uint32_t sig_present; /* Boolean*/ + uint32_t sig_locked; /* Boolean */ + uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */ + uint32_t sig_strength; /* milliDb*/ +}; + +struct tuner_fm_sig_stat_s { + uint32_t sig_present; /* Boolean*/ + uint32_t sig_locked; /* Boolean */ + uint32_t sig_lock_busy; /* Boolean */ + uint32_t sig_stereo_mono;/* TBD*/ + uint32_t sig_strength; /* milliDb*/ +}; + +enum _tag_tlg_tune_srv_cmd { + TLG_TUNE_PLAY_SVC_START = 1, + TLG_TUNE_PLAY_SVC_STOP +}; + +enum _tag_tune_atv_audio_mode_caps { + TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001, + TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002, + TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/ + TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/ + TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040 +}; + + +enum _tag_tuner_atv_audio_rates { + ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/ + ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/ + ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/ + ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */ +}; + +enum _tag_tune_atv_vid_res_caps { + TLG_TUNE_VID_RES_NONE = 0x00000000, + TLG_TUNE_VID_RES_720 = 0x00000001, + TLG_TUNE_VID_RES_704 = 0x00000002, + TLG_TUNE_VID_RES_360 = 0x00000004 +}; + +enum _tag_tuner_analog_video_format { + TLG_TUNER_VID_FORMAT_YUV = 0x00000001, + TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002, + TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004, +}; + +enum tlg_ext_audio_support { + TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */ + TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/ +}; + +enum { + TLG_MODE_NONE = 0x00, /* No Mode specified*/ + TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/ + TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/ + TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/ + TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/ + TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/ +}; + +enum tlg_signal_sources_t { + TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */ + TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */ + TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/ + TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */ + TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */ +}; + +enum tuner_analog_video_standard { + TLG_TUNE_VSTD_NONE = 0x00000000, + TLG_TUNE_VSTD_NTSC_M = 0x00000001, + TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */ + TLG_TUNE_VSTD_PAL_B = 0x00000010, + TLG_TUNE_VSTD_PAL_D = 0x00000020, + TLG_TUNE_VSTD_PAL_G = 0x00000040, + TLG_TUNE_VSTD_PAL_H = 0x00000080, + TLG_TUNE_VSTD_PAL_I = 0x00000100, + TLG_TUNE_VSTD_PAL_M = 0x00000200, + TLG_TUNE_VSTD_PAL_N = 0x00000400, + TLG_TUNE_VSTD_SECAM_B = 0x00001000, + TLG_TUNE_VSTD_SECAM_D = 0x00002000, + TLG_TUNE_VSTD_SECAM_G = 0x00004000, + TLG_TUNE_VSTD_SECAM_H = 0x00008000, + TLG_TUNE_VSTD_SECAM_K = 0x00010000, + TLG_TUNE_VSTD_SECAM_K1 = 0x00020000, + TLG_TUNE_VSTD_SECAM_L = 0x00040000, + TLG_TUNE_VSTD_SECAM_L1 = 0x00080000, + TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000 +}; + +enum tlg_mode_caps { + TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */ + TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */ + TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/ + TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */ + TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */ +}; + +enum poseidon_vendor_cmds { + LAST_CMD_STAT = 0x00, + GET_CHIP_ID = 0x01, + GET_FW_ID = 0x02, + PRODUCT_CAPS = 0x03, + + TUNE_MODE_CAP_ATV = 0x10, + TUNE_MODE_CAP_ATVCOMP = 0X10, + TUNE_MODE_CAP_DVBT = 0x10, + TUNE_MODE_CAP_FM = 0x10, + TUNE_MODE_SELECT = 0x11, + TUNE_FREQ_SELECT = 0x12, + SGNL_SRC_SEL = 0x13, + + VIDEO_STD_SEL = 0x14, + VIDEO_STREAM_FMT_SEL = 0x15, + VIDEO_ROSOLU_AVAIL = 0x16, + VIDEO_ROSOLU_SEL = 0x17, + VIDEO_CONT_PROTECT = 0x20, + + VCR_TIMING_MODSEL = 0x21, + EXT_AUDIO_CAP = 0x22, + EXT_AUDIO_SEL = 0x23, + TEST_PATTERN_SEL = 0x24, + VBI_DATA_SEL = 0x25, + AUDIO_SAMPLE_RATE_CAP = 0x28, + AUDIO_SAMPLE_RATE_SEL = 0x29, + TUNER_AUD_MODE = 0x2a, + TUNER_AUD_MODE_AVAIL = 0x2b, + TUNER_AUD_ANA_STD = 0x2c, + TUNER_CUSTOM_PARAMETER = 0x2f, + + DVBT_TUNE_MODE_SEL = 0x30, + DVBT_BANDW_CAP = 0x31, + DVBT_BANDW_SEL = 0x32, + DVBT_GUARD_INTERV_CAP = 0x33, + DVBT_GUARD_INTERV_SEL = 0x34, + DVBT_MODULATION_CAP = 0x35, + DVBT_MODULATION_SEL = 0x36, + DVBT_INNER_FEC_RATE_CAP = 0x37, + DVBT_INNER_FEC_RATE_SEL = 0x38, + DVBT_TRANS_MODE_CAP = 0x39, + DVBT_TRANS_MODE_SEL = 0x3a, + DVBT_SEARCH_RANG = 0x3c, + + TUNER_SETUP_ANALOG = 0x40, + TUNER_SETUP_DIGITAL = 0x41, + TUNER_SETUP_FM_RADIO = 0x42, + TAKE_REQUEST = 0x43, /* Take effect of the command */ + PLAY_SERVICE = 0x44, /* Play start or Play stop */ + TUNER_STATUS = 0x45, + TUNE_PROP_DVBT = 0x46, + ERR_RATE_STATS = 0x47, + TUNER_BER_RATE = 0x48, + + SCAN_CAPS = 0x50, + SCAN_SETUP = 0x51, + SCAN_SERVICE = 0x52, + SCAN_STATS = 0x53, + + PID_SET = 0x58, + PID_UNSET = 0x59, + PID_LIST = 0x5a, + + IRD_CAP = 0x60, + IRD_MODE_SEL = 0x61, + IRD_SETUP = 0x62, + + PTM_MODE_CAP = 0x70, + PTM_MODE_SEL = 0x71, + PTM_SERVICE = 0x72, + TUNER_REG_SCRIPT = 0x73, + CMD_CHIP_RST = 0x74, +}; + +enum tlg_bw { + TLG_BW_5 = 5, + TLG_BW_6 = 6, + TLG_BW_7 = 7, + TLG_BW_8 = 8, + TLG_BW_12 = 12, + TLG_BW_15 = 15 +}; + +struct cmd_firmware_vers_s { + uint8_t fw_rev_major; + uint8_t fw_rev_minor; + uint16_t fw_patch; +}; +#endif /* VENDOR_CMD_H_ */ diff --git a/drivers/media/usb/tm6000/Kconfig b/drivers/media/usb/tm6000/Kconfig new file mode 100644 index 000000000000..a43b77abd931 --- /dev/null +++ b/drivers/media/usb/tm6000/Kconfig @@ -0,0 +1,33 @@ +config VIDEO_TM6000 + tristate "TV Master TM5600/6000/6010 driver" + depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB + select VIDEO_TUNER + select MEDIA_TUNER_XC2028 + select MEDIA_TUNER_XC5000 + select VIDEOBUF_VMALLOC + help + Support for TM5600/TM6000/TM6010 USB Device + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the usb bus, so you need + an external software decoder to watch TV on your computer. + + Say Y if you own such a device and want to use it. + +config VIDEO_TM6000_ALSA + tristate "TV Master TM5600/6000/6010 audio support" + depends on VIDEO_TM6000 && SND + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio for + TM5600/TM6000/TM6010 USB Devices. + + To compile this driver as a module, choose M here: the + module will be called tm6000-alsa. + +config VIDEO_TM6000_DVB + tristate "DVB Support for tm6000 based TV cards" + depends on VIDEO_TM6000 && DVB_CORE && USB + select DVB_ZL10353 + ---help--- + This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/media/usb/tm6000/Makefile b/drivers/media/usb/tm6000/Makefile new file mode 100644 index 000000000000..1feb8c9c816c --- /dev/null +++ b/drivers/media/usb/tm6000/Makefile @@ -0,0 +1,15 @@ +tm6000-y := tm6000-cards.o \ + tm6000-core.o \ + tm6000-i2c.o \ + tm6000-video.o \ + tm6000-stds.o \ + tm6000-input.o + +obj-$(CONFIG_VIDEO_TM6000) += tm6000.o +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/tuners +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c new file mode 100644 index 000000000000..bd07ec707956 --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-alsa.c @@ -0,0 +1,528 @@ +/* + * + * Support for audio capture for tm5600/6000/6010 + * (c) 2007-2008 Mauro Carvalho Chehab + * + * Based on cx88-alsa.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include "tm6000.h" +#include "tm6000-regs.h" + +#undef dprintk + +#define dprintk(level, fmt, arg...) do { \ + if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ + } while (0) + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ + +static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); + + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Trident,tm5600}," + "{{Trident,tm6000}," + "{{Trident,tm6010}"); +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific funtions + ****************************************************************************/ + +/* + * BOARD Specific: Sets audio DMA + */ + +static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Starting audio DMA\n"); + + /* Enables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40); + + tm6000_set_audio_bitrate(core, 48000); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Stopping audio DMA\n"); + + /* Disables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40); + + return 0; +} + +static void dsp_buffer_free(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + dprintk(2, "Freeing buffer\n"); + + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + substream->runtime->dma_bytes = 0; +} + +static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + dprintk(2, "Allocating buffer\n"); + + if (substream->runtime->dma_area) { + if (substream->runtime->dma_bytes > size) + return 0; + + dsp_buffer_free(substream); + } + + substream->runtime->dma_area = vmalloc(size); + if (!substream->runtime->dma_area) + return -ENOMEM; + + substream->runtime->dma_bytes = size; + + return 0; +} + + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 4096 + +static struct snd_pcm_hardware snd_tm6000_digital_hw = { + .info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 64, + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, + .buffer_bytes_max = 62720 * 8, +}; + +/* + * audio pcm capture open callback + */ +static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_tm6000_digital_hw; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_tm6000_close(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + + if (atomic_read(&core->stream_started) > 0) { + atomic_set(&core->stream_started, 0); + schedule_work(&core->wq_trigger); + } + + return 0; +} + +static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) +{ + struct snd_tm6000_card *chip = core->adev; + struct snd_pcm_substream *substream = chip->substream; + struct snd_pcm_runtime *runtime; + int period_elapsed = 0; + unsigned int stride, buf_pos; + int length; + + if (atomic_read(&core->stream_started) == 0) + return 0; + + if (!size || !substream) { + dprintk(1, "substream was NULL\n"); + return -EINVAL; + } + + runtime = substream->runtime; + if (!runtime || !runtime->dma_area) { + dprintk(1, "runtime was NULL\n"); + return -EINVAL; + } + + buf_pos = chip->buf_pos; + stride = runtime->frame_bits >> 3; + + if (stride == 0) { + dprintk(1, "stride is zero\n"); + return -EINVAL; + } + + length = size / stride; + if (length == 0) { + dprintk(1, "%s: length was zero\n", __func__); + return -EINVAL; + } + + dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, + runtime->dma_area, buf_pos, + (unsigned int)runtime->buffer_size, stride); + + if (buf_pos + length >= runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - buf_pos; + memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); + memcpy(runtime->dma_area, buf + cnt * stride, + length * stride - cnt * stride); + } else + memcpy(runtime->dma_area + buf_pos * stride, buf, + length * stride); + + snd_pcm_stream_lock(substream); + + chip->buf_pos += length; + if (chip->buf_pos >= runtime->buffer_size) + chip->buf_pos -= runtime->buffer_size; + + chip->period_pos += length; + if (chip->period_pos >= runtime->period_size) { + chip->period_pos -= runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock(substream); + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + + return 0; +} + +/* + * hw_params callback + */ +static int snd_tm6000_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int size, rc; + + size = params_period_bytes(hw_params) * params_periods(hw_params); + + rc = dsp_buffer_alloc(substream, size); + if (rc < 0) + return rc; + + return 0; +} + +/* + * hw free callback + */ +static int snd_tm6000_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + + if (atomic_read(&core->stream_started) > 0) { + atomic_set(&core->stream_started, 0); + schedule_work(&core->wq_trigger); + } + + dsp_buffer_free(substream); + return 0; +} + +/* + * prepare callback + */ +static int snd_tm6000_prepare(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + chip->buf_pos = 0; + chip->period_pos = 0; + + return 0; +} + + +/* + * trigger callback + */ +static void audio_trigger(struct work_struct *work) +{ + struct tm6000_core *core = container_of(work, struct tm6000_core, + wq_trigger); + struct snd_tm6000_card *chip = core->adev; + + if (atomic_read(&core->stream_started)) { + dprintk(1, "starting capture"); + _tm6000_start_audio_dma(chip); + } else { + dprintk(1, "stopping capture"); + _tm6000_stop_audio_dma(chip); + } +} + +static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + int err = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ + case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ + case SNDRV_PCM_TRIGGER_START: + atomic_set(&core->stream_started, 1); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ + case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ + case SNDRV_PCM_TRIGGER_STOP: + atomic_set(&core->stream_started, 0); + break; + default: + err = -EINVAL; + break; + } + schedule_work(&core->wq_trigger); + + return err; +} +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + return chip->buf_pos; +} + +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +/* + * operators + */ +static struct snd_pcm_ops snd_tm6000_pcm_ops = { + .open = snd_tm6000_pcm_open, + .close = snd_tm6000_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_tm6000_hw_params, + .hw_free = snd_tm6000_hw_free, + .prepare = snd_tm6000_prepare, + .trigger = snd_tm6000_card_trigger, + .pointer = snd_tm6000_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +/* + * create a PCM device + */ + +/* FIXME: Control interface - How to control volume/mute? */ + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * Alsa Constructor - Component probe + */ +static int tm6000_audio_init(struct tm6000_core *dev) +{ + struct snd_card *card; + struct snd_tm6000_card *chip; + int rc; + static int devnr; + char component[14]; + struct snd_pcm *pcm; + + if (!dev) + return 0; + + if (devnr >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[devnr]) + return -ENOENT; + + rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card); + if (rc < 0) { + snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); + return rc; + } + strcpy(card->driver, "tm6000-alsa"); + strcpy(card->shortname, "TM5600/60x0"); + sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", + dev->udev->bus->busnum, dev->udev->devnum); + + sprintf(component, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + snd_component_add(card, component); + snd_card_set_dev(card, &dev->udev->dev); + + chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); + if (!chip) { + rc = -ENOMEM; + goto error; + } + + chip->core = dev; + chip->card = card; + dev->adev = chip; + spin_lock_init(&chip->reg_lock); + + rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); + if (rc < 0) + goto error_chip; + + pcm->info_flags = 0; + pcm->private_data = chip; + strcpy(pcm->name, "Trident TM5600/60x0"); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); + + INIT_WORK(&dev->wq_trigger, audio_trigger); + rc = snd_card_register(card); + if (rc < 0) + goto error_chip; + + dprintk(1, "Registered audio driver for %s\n", card->longname); + + return 0; + +error_chip: + kfree(chip); + dev->adev = NULL; +error: + snd_card_free(card); + return rc; +} + +static int tm6000_audio_fini(struct tm6000_core *dev) +{ + struct snd_tm6000_card *chip = dev->adev; + + if (!dev) + return 0; + + if (!chip) + return 0; + + if (!chip->card) + return 0; + + snd_card_free(chip->card); + chip->card = NULL; + kfree(chip); + dev->adev = NULL; + + return 0; +} + +static struct tm6000_ops audio_ops = { + .type = TM6000_AUDIO, + .name = "TM6000 Audio Extension", + .init = tm6000_audio_init, + .fini = tm6000_audio_fini, + .fillbuf = tm6000_fillbuf, +}; + +static int __init tm6000_alsa_register(void) +{ + return tm6000_register_extension(&audio_ops); +} + +static void __exit tm6000_alsa_unregister(void) +{ + tm6000_unregister_extension(&audio_ops); +} + +module_init(tm6000_alsa_register); +module_exit(tm6000_alsa_unregister); diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c new file mode 100644 index 000000000000..034659b13174 --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-cards.c @@ -0,0 +1,1413 @@ +/* + * tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + +#include "tm6000.h" +#include "tm6000-regs.h" +#include "tuner-xc2028.h" +#include "xc5000.h" + +#define TM6000_BOARD_UNKNOWN 0 +#define TM5600_BOARD_GENERIC 1 +#define TM6000_BOARD_GENERIC 2 +#define TM6010_BOARD_GENERIC 3 +#define TM5600_BOARD_10MOONS_UT821 4 +#define TM5600_BOARD_10MOONS_UT330 5 +#define TM6000_BOARD_ADSTECH_DUAL_TV 6 +#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 +#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 +#define TM6010_BOARD_HAUPPAUGE_900H 9 +#define TM6010_BOARD_BEHOLD_WANDER 10 +#define TM6010_BOARD_BEHOLD_VOYAGER 11 +#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 +#define TM6010_BOARD_TWINHAN_TU501 13 +#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 +#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 +#define TM5600_BOARD_TERRATEC_GRABSTER 16 + +#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \ + (model == TM5600_BOARD_GENERIC) || \ + (model == TM6000_BOARD_GENERIC) || \ + (model == TM6010_BOARD_GENERIC)) + +#define TM6000_MAXBOARDS 16 +static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); + +static unsigned long tm6000_devused; + + +struct tm6000_board { + char *name; + char eename[16]; /* EEPROM name */ + unsigned eename_size; /* size of EEPROM name */ + unsigned eename_pos; /* Position where it appears at ROM */ + + struct tm6000_capabilities caps; + + enum tm6000_devtype type; /* variant of the chipset */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int demod_addr; /* demodulator address */ + + struct tm6000_gpio gpio; + + struct tm6000_input vinput[3]; + struct tm6000_input rinput; + + char *ir_codes; +}; + +static struct tm6000_board tm6000_boards[] = { + [TM6000_BOARD_UNKNOWN] = { + .name = "Unknown tm6000 video grabber", + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_GENERIC] = { + .name = "Generic tm5600 board", + .type = TM5600, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_GENERIC] = { + .name = "Generic tm6000 board", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_GENERIC] = { + .name = "Generic tm6010 board", + .type = TM6010, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT821] = { + .name = "10Moons UT 821", + .tuner_type = TUNER_XC2028, + .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b}, + .eename_size = 14, + .eename_pos = 0x14, + .type = TM5600, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT330] = { + .name = "10Moons UT 330", + .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_DUAL_TV] = { + .name = "ADSTECH Dual TV USB", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_tda9874 = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_FREECOM_AND_SIMILAR] = { + .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { + .name = "ADSTECH Mini Dual TV USB", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc8 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_HAUPPAUGE_900H] = { + .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", + .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 }, + .eename_size = 14, + .eename_pos = 0x42, + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .ir_codes = RC_MAP_HAUPPAUGE, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER] = { + .name = "Beholder Wander DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER] = { + .name = "Beholder Voyager TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = { + .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_SIF1, + }, + }, + [TM5600_BOARD_TERRATEC_GRABSTER] = { + .name = "Terratec Grabster AV 150/250 MX", + .type = TM5600, + .tuner_type = TUNER_ABSENT, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_TWINHAN_TU501] = { + .name = "Twinhan TU501(704D1)", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER_LITE] = { + .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { + .name = "Beholder Voyager Lite TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id tm6000_id_table[] = { + { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC }, + { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, + { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, + { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, + { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, + { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER }, + { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, + { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, + { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, + { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, + { } +}; +MODULE_DEVICE_TABLE(usb, tm6000_id_table); + +/* Control power led for show some activity */ +void tm6000_flash_led(struct tm6000_core *dev, u8 state) +{ + /* Power LED unconfigured */ + if (!dev->gpio.power_led) + return; + + /* ON Power LED */ + if (state) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + } + } + /* OFF Power LED */ + else { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + } + } +} + +/* Tuner callback to provide the proper gpio changes needed for xc5000 */ +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC5000) + return 0; + + switch (command) { + case XC5000_TUNER_RESET: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_xc5000_callback); + +/* Tuner callback to provide the proper gpio changes needed for xc2028 */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + switch (command) { + case XC2028_RESET_CLK: + tm6000_ir_wait(dev, 0); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, arg); + msleep(10); + rc = tm6000_i2c_reset(dev, 10); + break; + case XC2028_TUNER_RESET: + /* Reset codes during load firmware */ + switch (arg) { + case 0: + /* newer tuner can faster reset */ + switch (dev->model) { + case TM5600_BOARD_10MOONS_UT821: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x00); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + break; + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(75); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + break; + default: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(130); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(130); + break; + } + + tm6000_ir_wait(dev, 1); + break; + case 1: + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, 0x01); + msleep(10); + break; + case 2: + rc = tm6000_i2c_reset(dev, 100); + break; + } + break; + case XC2028_I2C_FLUSH: + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_tuner_callback); + +int tm6000_cards_setup(struct tm6000_core *dev) +{ + /* + * Board-specific initialization sequence. Handles all GPIO + * initialization sequences that are board-specific. + * Up to now, all found devices use GPIO1 and GPIO4 at the same way. + * Probably, they're all based on some reference device. Due to that, + * there's a common routine at the end to handle those GPIO's. Devices + * that use different pinups or init sequences can just return at + * the board-specific session. + */ + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + case TM6010_BOARD_GENERIC: + /* Turn xceive 3028 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + /* Turn zarlink zl10353 off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); + msleep(15); + /* ir ? */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); + msleep(15); + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); + msleep(15); + /* DVB led off (orange) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + break; + default: + break; + } + + /* + * Default initialization. Most of the devices seem to use GPIO1 + * and GPIO4.on the same way, so, this handles the common sequence + * used by most devices. + * If a device uses a different sequence or different GPIO pins for + * reset, just add the code at the board-specific part + */ + + if (dev->gpio.tuner_reset) { + int rc; + int i; + + for (i = 0; i < 2; i++) { + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + + msleep(10); /* Just to be conservative */ + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + } + } else { + printk(KERN_ERR "Tuner reset is not configured\n"); + return -1; + } + + msleep(50); + + return 0; +}; + +static void tm6000_config_tuner(struct tm6000_core *dev) +{ + struct tuner_setup tun_setup; + + /* Load tuner module */ + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", dev->tuner_addr, NULL); + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + tun_setup.mode_mask = 0; + if (dev->caps.has_tuner) + tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); + + switch (dev->tuner_type) { + case TUNER_XC2028: + tun_setup.tuner_callback = tm6000_tuner_callback; + break; + case TUNER_XC5000: + tun_setup.tuner_callback = tm6000_xc5000_callback; + break; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.demod = XC3028_FE_ZARLINK456; + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + ctl.max_len = 80; + ctl.fname = "xc3028L-v36.fw"; + break; + default: + if (dev->dev_type == TM6010) + ctl.fname = "xc3028-v27.fw"; + else + ctl.fname = "xc3028-v24.fw"; + } + + printk(KERN_INFO "Setting firmware parameters for xc2028\n"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc2028_cfg); + + } + break; + case TUNER_XC5000: + { + struct v4l2_priv_tun_config xc5000_cfg; + struct xc5000_config ctl = { + .i2c_address = dev->tuner_addr, + .if_khz = 4570, + .radio_input = XC5000_RADIO_FM1_MONO, + }; + + xc5000_cfg.tuner = TUNER_XC5000; + xc5000_cfg.priv = &ctl; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc5000_cfg); + } + break; + default: + printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n"); + break; + } +} + +static int fill_board_specific_data(struct tm6000_core *dev) +{ + int rc; + + dev->dev_type = tm6000_boards[dev->model].type; + dev->tuner_type = tm6000_boards[dev->model].tuner_type; + dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; + + dev->gpio = tm6000_boards[dev->model].gpio; + + dev->ir_codes = tm6000_boards[dev->model].ir_codes; + + dev->demod_addr = tm6000_boards[dev->model].demod_addr; + + dev->caps = tm6000_boards[dev->model].caps; + + dev->vinput[0] = tm6000_boards[dev->model].vinput[0]; + dev->vinput[1] = tm6000_boards[dev->model].vinput[1]; + dev->vinput[2] = tm6000_boards[dev->model].vinput[2]; + dev->rinput = tm6000_boards[dev->model].rinput; + + /* setup per-model quirks */ + switch (dev->model) { + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_HAUPPAUGE_900H: + dev->quirks |= TM6000_QUIRK_NO_USB_DELAY; + break; + + default: + break; + } + + /* initialize hardware */ + rc = tm6000_init(dev); + if (rc < 0) + return rc; + + return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); +} + + +static void use_alternative_detection_method(struct tm6000_core *dev) +{ + int i, model = -1; + + if (!dev->eedata_size) + return; + + for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) { + if (!tm6000_boards[i].eename_size) + continue; + if (dev->eedata_size < tm6000_boards[i].eename_pos + + tm6000_boards[i].eename_size) + continue; + + if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos], + tm6000_boards[i].eename, + tm6000_boards[i].eename_size)) { + model = i; + break; + } + } + if (model < 0) { + printk(KERN_INFO "Device has eeprom but is currently unknown\n"); + return; + } + + dev->model = model; + + printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n", + tm6000_boards[model].name, model); +} + +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct tm6000_core *dev = container_of(work, struct tm6000_core, + request_module_wk); + + request_module("tm6000-alsa"); + + if (dev->caps.has_dvb) + request_module("tm6000-dvb"); +} + +static void request_modules(struct tm6000_core *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} + +static void flush_request_modules(struct tm6000_core *dev) +{ + flush_work_sync(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#define flush_request_modules(dev) +#endif /* CONFIG_MODULES */ + +static int tm6000_init_dev(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + int rc = 0; + + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + + if (!is_generic(dev->model)) { + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + } else { + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + + use_alternative_detection_method(dev); + + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + } + + /* Default values for STD and resolutions */ + dev->width = 720; + dev->height = 480; + dev->norm = V4L2_STD_PAL_M; + + /* Configure tuner */ + tm6000_config_tuner(dev); + + /* Set video standard */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 3092; /* 193.25 MHz */ + dev->freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + if (dev->caps.has_tda9874) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", I2C_ADDR_TDA9874, NULL); + + /* register and initialize V4L2 */ + rc = tm6000_v4l2_register(dev); + if (rc < 0) + goto err; + + tm6000_add_into_devlist(dev); + tm6000_init_extension(dev); + + tm6000_ir_init(dev); + + request_modules(dev); + + mutex_unlock(&dev->lock); + return 0; + +err: + mutex_unlock(&dev->lock); + return rc; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +static void get_max_endpoint(struct usb_device *udev, + struct usb_host_interface *alt, + char *msgtype, + struct usb_host_endpoint *curr_e, + struct tm6000_endpoint *tm_ep) +{ + u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); + unsigned int size = tmp & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult(tmp); + + if (size > tm_ep->maxsize) { + tm_ep->endp = curr_e; + tm_ep->maxsize = size; + tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber; + tm_ep->bAlternateSetting = alt->desc.bAlternateSetting; + + printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", + msgtype, curr_e->desc.bEndpointAddress, + size); + } +} + +/* + * tm6000_usb_probe() + * checks for supported devices + */ +static int tm6000_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct tm6000_core *dev = NULL; + int i, rc = 0; + int nr = 0; + char *speed; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + + /* Selects the proper interface */ + rc = usb_set_interface(usbdev, 0, 1); + if (rc < 0) + goto err; + + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); + if (nr >= TM6000_MAXBOARDS) { + printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); + usb_put_dev(usbdev); + return -ENOMEM; + } + + /* Create and initialize dev struct */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "tm6000" ": out of memory!\n"); + usb_put_dev(usbdev); + return -ENOMEM; + } + spin_lock_init(&dev->slock); + mutex_init(&dev->usb_lock); + + /* Increment usage count */ + set_bit(nr, &tm6000_devused); + snprintf(dev->name, 29, "tm6000 #%d", nr); + + dev->model = id->driver_info; + if (card[nr] < ARRAY_SIZE(tm6000_boards)) + dev->model = card[nr]; + + dev->udev = usbdev; + dev->devno = nr; + + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + /* Get endpoints */ + for (i = 0; i < interface->num_altsetting; i++) { + int ep; + + for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { + struct usb_host_endpoint *e; + int dir_out; + + e = &interface->altsetting[i].endpoint[ep]; + + dir_out = ((e->desc.bEndpointAddress & + USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); + + printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n", + i, + interface->altsetting[i].desc.bInterfaceNumber, + interface->altsetting[i].desc.bInterfaceClass); + + switch (e->desc.bmAttributes) { + case USB_ENDPOINT_XFER_BULK: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk IN", e, + &dev->bulk_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk OUT", e, + &dev->bulk_out); + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC IN", e, + &dev->isoc_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC OUT", e, + &dev->isoc_out); + } + break; + case USB_ENDPOINT_XFER_INT: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT IN", e, + &dev->int_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT OUT", e, + &dev->int_out); + } + break; + } + } + } + + + printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", + speed, + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct), + interface->altsetting->desc.bInterfaceNumber); + +/* check if the the device has the iso in endpoint at the correct place */ + if (!dev->isoc_in.endp) { + printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); + rc = -ENODEV; + + goto err; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name); + + rc = tm6000_init_dev(dev); + if (rc < 0) + goto err; + + return 0; + +err: + printk(KERN_ERR "tm6000: Error %d while registering\n", rc); + + clear_bit(nr, &tm6000_devused); + usb_put_dev(usbdev); + + kfree(dev); + return rc; +} + +/* + * tm6000_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void tm6000_usb_disconnect(struct usb_interface *interface) +{ + struct tm6000_core *dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name); + + flush_request_modules(dev); + + tm6000_ir_fini(dev); + + if (dev->gpio.power_led) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + msleep(15); + break; + } + } + tm6000_v4l2_unregister(dev); + + tm6000_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + dev->state |= DEV_DISCONNECTED; + + usb_put_dev(dev->udev); + + tm6000_close_extension(dev); + tm6000_remove_from_devlist(dev); + + clear_bit(dev->devno, &tm6000_devused); + kfree(dev); +} + +static struct usb_driver tm6000_usb_driver = { + .name = "tm6000", + .probe = tm6000_usb_probe, + .disconnect = tm6000_usb_disconnect, + .id_table = tm6000_id_table, +}; + +module_usb_driver(tm6000_usb_driver); + +MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/tm6000/tm6000-core.c b/drivers/media/usb/tm6000/tm6000-core.c new file mode 100644 index 000000000000..22cc0116deb6 --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-core.c @@ -0,0 +1,935 @@ +/* + * tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab + * + * Copyright (C) 2007 Michel Ludwig + * - DVB-T support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 "tm6000.h" +#include "tm6000-regs.h" +#include +#include + +#define USB_TIMEOUT (5 * HZ) /* ms */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, + u16 value, u16 index, u8 *buf, u16 len) +{ + int ret, i; + unsigned int pipe; + u8 *data = NULL; + int delay = 5000; + + mutex_lock(&dev->usb_lock); + + if (len) + data = kzalloc(len, GFP_KERNEL); + + if (req_type & USB_DIR_IN) + pipe = usb_rcvctrlpipe(dev->udev, 0); + else { + pipe = usb_sndctrlpipe(dev->udev, 0); + memcpy(data, buf, len); + } + + if (tm6000_debug & V4L2_DEBUG_I2C) { + printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe); + + printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", + (req_type & USB_DIR_IN) ? " IN" : "OUT", + req_type, req, value&0xff, value>>8, index&0xff, + index>>8, len&0xff, len>>8); + + if (!(req_type & USB_DIR_IN)) { + printk(KERN_CONT ">>> "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, + data, len, USB_TIMEOUT); + + if (req_type & USB_DIR_IN) + memcpy(buf, data, len); + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (ret < 0) { + if (req_type & USB_DIR_IN) + printk(KERN_DEBUG "<<< (len=%d)\n", len); + + printk(KERN_CONT "%s: Error #%d\n", __func__, ret); + } else if (req_type & USB_DIR_IN) { + printk(KERN_CONT "<<< "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + kfree(data); + + if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY) + delay = 0; + + if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) { + unsigned int tsleep; + /* Calculate delay time, 14000us for 64 bytes */ + tsleep = (len * 200) + 200; + if (tsleep < delay) + tsleep = delay; + usleep_range(tsleep, tsleep + 1000); + } + else if (delay) + usleep_range(delay, delay + 1000); + + mutex_unlock(&dev->usb_lock); + return ret; +} + +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + return + tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg); + +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[1]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc < 0) + return rc; + + return *buf; +} +EXPORT_SYMBOL_GPL(tm6000_get_reg); + +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask) +{ + int rc; + u8 buf[1]; + u8 new_index; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, 0, buf, 1); + + if (rc < 0) + return rc; + + new_index = (buf[0] & ~mask) | (index & mask); + + if (new_index == buf[0]) + return 0; + + return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, new_index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); + +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[2]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 2); + + if (rc < 0) + return rc; + + return buf[1]|buf[0]<<8; +} + +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[4]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 4); + + if (rc < 0) + return rc; + + return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; +} + +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep) +{ + int rc; + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0); + if (rc < 0) + return rc; + + msleep(tsleep); + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1); + msleep(tsleep); + + return rc; +} + +void tm6000_set_fourcc_format(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + + val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc; + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val); + else + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1); + } else { + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + else + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); + } +} + +static void tm6000_set_vbi(struct tm6000_core *dev) +{ + /* + * FIXME: + * VBI lines and start/end are different between 60Hz and 50Hz + * So, it is very likely that we need to change the config to + * something that takes it into account, doing something different + * if (dev->norm & V4L2_STD_525_60) + */ + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); + tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); + tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); + tm6000_set_reg(dev, + TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); + tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); + tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); + tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); + tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); + tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } +} + +int tm6000_init_analog_mode(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + + if (dev->dev_type == TM6010) { + u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE; + + if (!dev->radio) + active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE; + + /* Enable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + active, 0x60); + /* Disable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x00, 0x40); + } else { + /* Enables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + + if (dev->scaler) + /* Disable Hfilter and Enable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); + else /* Enable Hfilter and disable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); + + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); + + /* AP Software reset */ + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + + tm6000_set_fourcc_format(dev); + + /* Disables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } + msleep(20); + + /* Tuner firmware can now be loaded */ + + /* + * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected + * for more than a few seconds. Not sure why, as this behavior does + * not happen on other devices with xc3028. So, I suspect that it + * is yet another bug at tm6000. After start sleeping, decoding + * doesn't start automatically. Instead, it requires some + * I2C commands to wake it up. As we want to have image at the + * beginning, we needed to add this hack. The better would be to + * discover some way to make tm6000 to wake up without this hack. + */ + f.frequency = dev->freq; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + msleep(100); + tm6000_set_standard(dev); + tm6000_set_vbi(dev); + tm6000_set_audio_bitrate(dev, 48000); + + /* switch dvb led off */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x01); + } + + return 0; +} + +int tm6000_init_digital_mode(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Disable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + 0x00, 0x60); + /* Enable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x40, 0x40); + /* all power down, but not the digital data port */ + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); + } else { + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); + + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); + msleep(50); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(100); + } + + /* switch dvb led on */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x00); + } + + return 0; +} +EXPORT_SYMBOL(tm6000_init_digital_mode); + +struct reg_init { + u8 req; + u8 reg; + u8 val; +}; + +/* The meaning of those initializations are unknown */ +static struct reg_init tm6000_init_tab[] = { + /* REG VALUE */ + { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, + { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, + { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, + { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, + { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, + { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, + { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, + { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, + { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ + { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ + { TM6010_REQ05_R18_IMASK7, 0x00 }, +}; + +static struct reg_init tm6010_init_tab[] = { + { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, + { TM6010_REQ07_RC4_HSTART0, 0xa0 }, + { TM6010_REQ07_RC6_HEND0, 0x40 }, + { TM6010_REQ07_RCA_VEND0, 0x31 }, + { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 }, + { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, + { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, + + { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, + { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, + { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, + { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, + { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, + { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, + { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, + { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, + { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + + { TM6010_REQ05_R18_IMASK7, 0x00 }, + + { TM6010_REQ07_RDC_IR_LEADER1, 0xaa }, + { TM6010_REQ07_RDD_IR_LEADER0, 0x30 }, + { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 }, + { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 }, + { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, + { TM6010_REQ07_RD8_IR, 0x0f }, + + /* set remote wakeup key:any key wakeup */ + { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, + { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff }, +}; + +int tm6000_init(struct tm6000_core *dev) +{ + int board, rc = 0, i, size; + struct reg_init *tab; + + /* Check board revision */ + board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); + if (board >= 0) { + switch (board & 0xff) { + case 0xf3: + printk(KERN_INFO "Found tm6000\n"); + if (dev->dev_type != TM6000) + dev->dev_type = TM6000; + break; + case 0xf4: + printk(KERN_INFO "Found tm6010\n"); + if (dev->dev_type != TM6010) + dev->dev_type = TM6010; + break; + default: + printk(KERN_INFO "Unknown board version = 0x%08x\n", board); + } + } else + printk(KERN_ERR "Error %i while retrieving board version\n", board); + + if (dev->dev_type == TM6010) { + tab = tm6010_init_tab; + size = ARRAY_SIZE(tm6010_init_tab); + } else { + tab = tm6000_init_tab; + size = ARRAY_SIZE(tm6000_init_tab); + } + + /* Load board's initialization table */ + for (i = 0; i < size; i++) { + rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting req %d, " + "reg %d to value %d\n", rc, + tab[i].req, tab[i].reg, tab[i].val); + return rc; + } + } + + msleep(5); /* Just to be conservative */ + + rc = tm6000_cards_setup(dev); + + return rc; +} + + +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) +{ + int val = 0; + u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + u8 areg_0a = 0x91; /* SIF 48KHz */ + + switch (bitrate) { + case 48000: + areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + areg_0a = 0x91; /* SIF 48KHz */ + dev->audio_bitrate = bitrate; + break; + case 32000: + areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ + areg_0a = 0x90; /* SIF 32KHz */ + dev->audio_bitrate = bitrate; + break; + default: + return -EINVAL; + } + + + /* enable I2S, if we use sif or external I2S device */ + if (dev->dev_type == TM6010) { + val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); + if (val < 0) + return val; + + val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0xf0); + if (val < 0) + return val; + } else { + val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_f0, 0xf0); + if (val < 0) + return val; + } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); + +int tm6000_set_audio_rinput(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Audio crossbar setting, default SIF1 */ + u8 areg_f0; + u8 areg_07 = 0x10; + + switch (dev->rinput.amux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + areg_f0 = 0x03; + areg_07 = 0x30; + break; + case TM6000_AMUX_ADC1: + areg_f0 = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_f0 = 0x08; + break; + case TM6000_AMUX_I2S: + areg_f0 = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input dosn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input crossbar */ + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + areg_07, 0xf0); + } else { + u8 areg_eb; + /* Audio setting, default LINE1 */ + switch (dev->rinput.amux) { + case TM6000_AMUX_ADC1: + areg_eb = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_eb = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input dosn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input */ + tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_eb, 0x0f); + } + return 0; +} + +static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x08; + + tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); +} + +static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x20; + + if (dev->dev_type == TM6010) { + tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, + mute_reg, 0x20); + } else { + tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, + mute_reg, 0x20); + } +} + +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) +{ + enum tm6000_mux mux; + + if (dev->radio) + mux = dev->rinput.amux; + else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_mute_sif(dev, mute); + else { + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + return -EINVAL; + } + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_mute_adc(dev, mute); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = vol & 0x0F; + + if (vol < 0) + vol_reg |= 0x40; + + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); +} + +static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = (vol + 0x10) & 0x1f; + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); + } else { + tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); + tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); + } +} + +void tm6000_set_volume(struct tm6000_core *dev, int vol) +{ + enum tm6000_mux mux; + + if (dev->radio) { + mux = dev->rinput.amux; + vol += 8; /* Offset to 0 dB */ + } else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_volume_sif(dev, vol); + else + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_volume_adc(dev, vol); + break; + default: + break; + } +} + +static LIST_HEAD(tm6000_devlist); +static DEFINE_MUTEX(tm6000_devlist_mutex); + +/* + * tm6000_realease_resource() + */ + +void tm6000_remove_from_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +void tm6000_add_into_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&dev->devlist, &tm6000_devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +/* + * Extension interface + */ + +static LIST_HEAD(tm6000_extension_devlist); + +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size) +{ + struct tm6000_ops *ops = NULL; + + /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ + + if (!list_empty(&tm6000_extension_devlist)) { + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fillbuf && ops->type == type) + ops->fillbuf(dev, buf, size); + } + } + + return 0; +} + +int tm6000_register_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&ops->next, &tm6000_extension_devlist); + list_for_each_entry(dev, &tm6000_devlist, devlist) { + ops->init(dev); + printk(KERN_INFO "%s: Initialized (%s) extension\n", + dev->name, ops->name); + } + mutex_unlock(&tm6000_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(tm6000_register_extension); + +void tm6000_unregister_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(dev, &tm6000_devlist, devlist) + ops->fini(dev); + + printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&tm6000_devlist_mutex); +} +EXPORT_SYMBOL(tm6000_unregister_extension); + +void tm6000_init_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + if (!list_empty(&tm6000_extension_devlist)) { + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + } + mutex_unlock(&tm6000_devlist_mutex); +} + +void tm6000_close_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + if (!list_empty(&tm6000_extension_devlist)) { + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + } + mutex_unlock(&tm6000_devlist_mutex); +} diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c new file mode 100644 index 000000000000..e1f3f66e1e63 --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -0,0 +1,467 @@ +/* + * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2007 Michel Ludwig + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 "tm6000.h" +#include "tm6000-regs.h" + +#include "zl10353.h" + +#include + +#include "tuner-xc2028.h" +#include "xc5000.h" + +MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); + +MODULE_SUPPORTED_DEVICE("{{Trident, tm5600}," + "{{Trident, tm6000}," + "{{Trident, tm6010}"); + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug message"); + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, 1, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, 1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static void tm6000_urb_received(struct urb *urb) +{ + int ret; + struct tm6000_core *dev = urb->context; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + default: + print_err_status(dev, 0, urb->status); + } + + if (urb->actual_length > 0) + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + + if (dev->dvb->streams > 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %s\n", __func__); + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + } +} + +static int tm6000_start_stream(struct tm6000_core *dev) +{ + int ret; + unsigned int pipe, size; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got start stream request %s\n", __func__); + + if (dev->mode != TM6000_MODE_DIGITAL) { + tm6000_init_digital_mode(dev); + dev->mode = TM6000_MODE_DIGITAL; + } + + dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (dvb->bulk_urb == NULL) { + printk(KERN_ERR "tm6000: couldn't allocate urb\n"); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + size = size * 15; /* 512 x 8 or 12 or 15 */ + + dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); + if (dvb->bulk_urb->transfer_buffer == NULL) { + usb_free_urb(dvb->bulk_urb); + printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n"); + return -ENOMEM; + } + + usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, + dvb->bulk_urb->transfer_buffer, + size, + tm6000_urb_received, dev); + + ret = usb_clear_halt(dev->udev, pipe); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", + ret, __func__); + return ret; + } else + printk(KERN_ERR "tm6000: pipe resetted\n"); + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC); + +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ + if (ret) { + printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", + ret); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + return ret; + } + + return 0; +} + +static void tm6000_stop_stream(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb) { + printk(KERN_INFO "urb killing\n"); + usb_kill_urb(dvb->bulk_urb); + printk(KERN_INFO "urb buffer free\n"); + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + } +} + +static int tm6000_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + printk(KERN_INFO "tm6000: got start feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + if (dvb->streams == 0) { + dvb->streams = 1; +/* mutex_init(&tm6000_dev->streming_mutex); */ + tm6000_start_stream(dev); + } else + ++(dvb->streams); + mutex_unlock(&dvb->mutex); + + return 0; +} + +static int tm6000_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + + printk(KERN_INFO "stream %#x\n", dvb->streams); + --(dvb->streams); + if (dvb->streams == 0) { + printk(KERN_INFO "stop stream\n"); + tm6000_stop_stream(dev); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + } + mutex_unlock(&dvb->mutex); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + + return 0; +} + +static int tm6000_dvb_attach_frontend(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dev->caps.has_zl10353) { + struct zl10353_config config = { + .demod_address = dev->demod_addr, + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45700, + .disable_i2c_gate_ctrl = 1, + }; + + dvb->frontend = dvb_attach(zl10353_attach, &config, + &dev->i2c_adap); + } else { + printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); + return -1; + } + + return (!dvb->frontend) ? -1 : 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int register_dvb(struct tm6000_core *dev) +{ + int ret = -1; + struct tm6000_dvb *dvb = dev->dvb; + + mutex_init(&dvb->mutex); + + dvb->streams = 0; + + /* attach the frontend */ + ret = tm6000_dvb_attach_frontend(dev); + if (ret < 0) { + printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); + goto err; + } + + ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", + THIS_MODULE, &dev->udev->dev, adapter_nr); + dvb->adapter.priv = dev; + + if (dvb->frontend) { + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_tuner_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register " + "frontend (xc3028)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC2028/3028 asked to be " + "attached to frontend!\n"); + break; + } + case TUNER_XC5000: { + struct xc5000_config cfg = { + .i2c_address = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_xc5000_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register " + "frontend (xc5000)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC5000 asked to be " + "attached to frontend!\n"); + break; + } + } + } else + printk(KERN_ERR "tm6000: no frontend found\n"); + + dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 8; + dvb->demux.feednum = 8; + dvb->demux.start_feed = tm6000_start_feed; + dvb->demux.stop_feed = tm6000_stop_feed; + dvb->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&dvb->demux); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret); + goto frontend_err; + } + + dvb->dmxdev.filternum = dev->dvb->demux.filternum; + dvb->dmxdev.demux = &dev->dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); + goto dvb_dmx_err; + } + + return 0; + +dvb_dmx_err: + dvb_dmx_release(&dvb->demux); +frontend_err: + if (dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } +adapter_err: + dvb_unregister_adapter(&dvb->adapter); +err: + return ret; +} + +static void unregister_dvb(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb != NULL) { + struct urb *bulk_urb = dvb->bulk_urb; + + kfree(bulk_urb->transfer_buffer); + bulk_urb->transfer_buffer = NULL; + usb_unlink_urb(bulk_urb); + usb_free_urb(bulk_urb); + } + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + if (dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } + + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_adapter(&dvb->adapter); + mutex_destroy(&dvb->mutex); +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ +} + +static int dvb_init(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb; + int rc; + + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->udev->speed == USB_SPEED_FULL) { + printk(KERN_INFO "This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)\n"); + return 0; + } + + dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); + if (!dvb) { + printk(KERN_INFO "Cannot allocate memory\n"); + return -ENOMEM; + } + + dev->dvb = dvb; + + rc = register_dvb(dev); + if (rc < 0) { + kfree(dvb); + dev->dvb = NULL; + return 0; + } + + return 0; +} + +static int dvb_fini(struct tm6000_core *dev) +{ + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->dvb) { + unregister_dvb(dev); + kfree(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct tm6000_ops dvb_ops = { + .type = TM6000_DVB, + .name = "TM6000 dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init tm6000_dvb_register(void) +{ + return tm6000_register_extension(&dvb_ops); +} + +static void __exit tm6000_dvb_unregister(void) +{ + tm6000_unregister_extension(&dvb_ops); +} + +module_init(tm6000_dvb_register); +module_exit(tm6000_dvb_unregister); diff --git a/drivers/media/usb/tm6000/tm6000-i2c.c b/drivers/media/usb/tm6000/tm6000-i2c.c new file mode 100644 index 000000000000..c7e23e3dd75e --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-i2c.c @@ -0,0 +1,334 @@ +/* + * tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab + * + * Copyright (C) 2007 Michel Ludwig + * - Fix SMBus Read Byte command + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 "tm6000.h" +#include "tm6000-regs.h" +#include +#include +#include "tuner-xc2028.h" + + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__, ##args); } while (0) + +static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 80; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | reg << 8, 0, buf, len); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + /* release mutex */ + return rc; +} + +/* Generic read - doesn't work fine with 16bit registers */ +static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + u8 b[2]; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 64; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { + /* + * Workaround an I2C bug when reading from zl10353 + */ + reg -= 1; + len += 1; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); + + *buf = b[1]; + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); + } + + /* release mutex */ + return rc; +} + +/* + * read from a 16bit register + * for example xc2028, xc3028 or xc3028L + */ +static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, + __u16 reg, char *buf, int len) +{ + int rc; + unsigned char ureg; + + if (!buf || len != 2) + return -1; + + /* capture mutex */ + if (dev->dev_type == TM6010) { + ureg = reg & 0xFF; + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | (reg & 0xFF00), 0, &ureg, 1); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, + reg, 0, buf, len); + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, + addr, reg, buf, len); + } + + /* release mutex */ + return rc; +} + +static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct tm6000_core *dev = i2c_adap->algo_data; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + for (i = 0; i < num; i++) { + addr = (msgs[i].addr << 1) & 0xff; + i2c_dprintk(2, "%s %s addr=0x%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read request without preceding register selection */ + /* + * The TM6000 only supports a read transaction + * immediately after a 1 or 2 byte write to select + * a register. We cannot fulfil this request. + */ + i2c_dprintk(2, " read without preceding write not" + " supported"); + rc = -EOPNOTSUPP; + goto err; + } else if (i + 1 < num && msgs[i].len <= 2 && + (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* 1 or 2 byte write followed by a read */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + i2c_dprintk(2, "; joined to read %s len=%d:", + i == num - 2 ? "stop" : "nonstop", + msgs[i + 1].len); + + if (msgs[i].len == 2) { + rc = tm6000_i2c_recv_regs16(dev, addr, + msgs[i].buf[0] << 8 | msgs[i].buf[1], + msgs[i + 1].buf, msgs[i + 1].len); + } else { + rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], + msgs[i + 1].buf, msgs[i + 1].len); + } + + i++; + + if (addr == dev->tuner_addr << 1) { + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + } + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + } else { + /* write bytes */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], + msgs[i].buf + 1, msgs[i].len - 1); + } + if (i2c_debug >= 2) + printk(KERN_CONT "\n"); + if (rc < 0) + goto err; + } + + return num; +err: + i2c_dprintk(2, " ERROR: %i\n", rc); + return rc; +} + +static int tm6000_i2c_eeprom(struct tm6000_core *dev) +{ + int i, rc; + unsigned char *p = dev->eedata; + unsigned char bytes[17]; + + dev->i2c_client.addr = 0xa0 >> 1; + dev->eedata_size = 0; + + bytes[16] = '\0'; + for (i = 0; i < sizeof(dev->eedata); ) { + *p = i; + rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); + if (rc < 1) { + if (p == dev->eedata) + goto noeeprom; + else { + printk(KERN_WARNING + "%s: i2c eeprom read error (err=%d)\n", + dev->name, rc); + } + return -EINVAL; + } + dev->eedata_size++; + p++; + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); + printk(KERN_CONT " %02x", dev->eedata[i]); + if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) + bytes[i%16] = dev->eedata[i]; + else + bytes[i%16] = '.'; + + i++; + + if (0 == (i % 16)) { + bytes[16] = '\0'; + printk(KERN_CONT " %s\n", bytes); + } + } + if (0 != (i%16)) { + bytes[i%16] = '\0'; + for (i %= 16; i < 16; i++) + printk(KERN_CONT " "); + printk(KERN_CONT " %s\n", bytes); + } + + return 0; + +noeeprom: + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name, rc); + return -EINVAL; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm tm6000_algo = { + .master_xfer = tm6000_i2c_xfer, + .functionality = functionality, +}; + +/* ----------------------------------------------------------- */ + +/* + * tm6000_i2c_register() + * register i2c bus + */ +int tm6000_i2c_register(struct tm6000_core *dev) +{ + int rc; + + dev->i2c_adap.owner = THIS_MODULE; + dev->i2c_adap.algo = &tm6000_algo; + dev->i2c_adap.dev.parent = &dev->udev->dev; + strlcpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + rc = i2c_add_adapter(&dev->i2c_adap); + if (rc) + return rc; + + dev->i2c_client.adapter = &dev->i2c_adap; + strlcpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); + tm6000_i2c_eeprom(dev); + + return 0; +} + +/* + * tm6000_i2c_unregister() + * unregister i2c_bus + */ +int tm6000_i2c_unregister(struct tm6000_core *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c new file mode 100644 index 000000000000..e80b7e190471 --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -0,0 +1,498 @@ +/* + * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2010 Stefan Ringel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "debug message level"); + +static unsigned int enable_ir = 1; +module_param(enable_ir, int, 0644); +MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); + +static unsigned int ir_clock_mhz = 12; +module_param(ir_clock_mhz, int, 0644); +MODULE_PARM_DESC(enable_ir, "ir clock, in MHz"); + +#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */ +#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */ + +#undef dprintk + +#define dprintk(level, fmt, arg...) do {\ + if (ir_debug >= level) \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } while (0) + +struct tm6000_ir_poll_result { + u16 rc_data; +}; + +struct tm6000_IR { + struct tm6000_core *dev; + struct rc_dev *rc; + char name[32]; + char phys[32]; + + /* poll expernal decoder */ + int polling; + struct delayed_work work; + u8 wait:1; + u8 pwled:2; + u8 submit_urb:1; + u16 key_addr; + struct urb *int_urb; + + /* IR device properties */ + u64 rc_type; +}; + +void tm6000_ir_wait(struct tm6000_core *dev, u8 state) +{ + struct tm6000_IR *ir = dev->ir; + + if (!dev->ir) + return; + + dprintk(2, "%s: %i\n",__func__, ir->wait); + + if (state) + ir->wait = 1; + else + ir->wait = 0; +} + +static int tm6000_ir_config(struct tm6000_IR *ir) +{ + struct tm6000_core *dev = ir->dev; + u32 pulse = 0, leader = 0; + + dprintk(2, "%s\n",__func__); + + /* + * The IR decoder supports RC-5 or NEC, with a configurable timing. + * The timing configuration there is not that accurate, as it uses + * approximate values. The NEC spec mentions a 562.5 unit period, + * and RC-5 uses a 888.8 period. + * Currently, driver assumes a clock provided by a 12 MHz XTAL, but + * a modprobe parameter can adjust it. + * Adjustments are required for other timings. + * It seems that the 900ms timing for NEC is used to detect a RC-5 + * IR, in order to discard such decoding + */ + + switch (ir->rc_type) { + case RC_TYPE_NEC: + leader = 900; /* ms */ + pulse = 700; /* ms - the actual value would be 562 */ + break; + default: + case RC_TYPE_RC5: + leader = 900; /* ms - from the NEC decoding */ + pulse = 1780; /* ms - The actual value would be 1776 */ + break; + } + + pulse = ir_clock_mhz * pulse; + leader = ir_clock_mhz * leader; + if (ir->rc_type == RC_TYPE_NEC) + leader = leader | 0x8000; + + dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n", + __func__, + (ir->rc_type == RC_TYPE_NEC) ? "NEC" : "RC-5", + ir_clock_mhz, leader, pulse); + + /* Remote WAKEUP = enable, normal mode, from IR decoder output */ + tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); + + /* Enable IR reception on non-busrt mode */ + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f); + + /* IR_WKUP_SEL = Low byte in decoded IR data */ + tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff); + /* IR_WKU_ADD code */ + tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff); + + tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8); + tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader); + + tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8); + tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse); + + if (!ir->polling) + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + else + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); + msleep(10); + + /* Shows that IR is working via the LED */ + tm6000_flash_led(dev, 0); + msleep(100); + tm6000_flash_led(dev, 1); + ir->pwled = 1; + + return 0; +} + +static void tm6000_ir_urb_received(struct urb *urb) +{ + struct tm6000_core *dev = urb->context; + struct tm6000_IR *ir = dev->ir; + struct tm6000_ir_poll_result poll_result; + char *buf; + + dprintk(2, "%s\n",__func__); + if (urb->status < 0 || urb->actual_length <= 0) { + printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n", + urb->status, urb->actual_length); + ir->submit_urb = 1; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + return; + } + buf = urb->transfer_buffer; + + if (ir_debug) + print_hex_dump(KERN_DEBUG, "tm6000: IR data: ", + DUMP_PREFIX_OFFSET,16, 1, + buf, urb->actual_length, false); + + poll_result.rc_data = buf[0]; + if (urb->actual_length > 1) + poll_result.rc_data |= buf[1] << 8; + + dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); + rc_keydown(ir->rc, poll_result.rc_data, 0); + + usb_submit_urb(urb, GFP_ATOMIC); + /* + * Flash the led. We can't do it here, as it is running on IRQ context. + * So, use the scheduler to do it, in a few ms. + */ + ir->pwled = 2; + schedule_delayed_work(&ir->work, msecs_to_jiffies(10)); +} + +static void tm6000_ir_handle_key(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + struct tm6000_core *dev = ir->dev; + struct tm6000_ir_poll_result poll_result; + int rc; + u8 buf[2]; + + if (ir->wait) + return; + + dprintk(3, "%s\n",__func__); + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_02_GET_IR_CODE, 0, 0, buf, 2); + if (rc < 0) + return; + + if (rc > 1) + poll_result.rc_data = buf[0] | buf[1] << 8; + else + poll_result.rc_data = buf[0]; + + /* Check if something was read */ + if ((poll_result.rc_data & 0xff) == 0xff) { + if (!ir->pwled) { + tm6000_flash_led(dev, 1); + ir->pwled = 1; + } + return; + } + + dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); + rc_keydown(ir->rc, poll_result.rc_data, 0); + tm6000_flash_led(dev, 0); + ir->pwled = 0; + + /* Re-schedule polling */ + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + +static void tm6000_ir_int_work(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + struct tm6000_core *dev = ir->dev; + int rc; + + dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb, + ir->pwled); + + if (ir->submit_urb) { + dprintk(3, "Resubmit urb\n"); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + + rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC); + if (rc < 0) { + printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n", + rc); + /* Retry in 100 ms */ + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + return; + } + ir->submit_urb = 0; + } + + /* Led is enabled only if USB submit doesn't fail */ + if (ir->pwled == 2) { + tm6000_flash_led(dev, 0); + ir->pwled = 0; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY)); + } else if (!ir->pwled) { + tm6000_flash_led(dev, 1); + ir->pwled = 1; + } +} + +static int tm6000_ir_start(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + dprintk(2, "%s\n",__func__); + + schedule_delayed_work(&ir->work, 0); + + return 0; +} + +static void tm6000_ir_stop(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + dprintk(2, "%s\n",__func__); + + cancel_delayed_work_sync(&ir->work); +} + +static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type) +{ + struct tm6000_IR *ir = rc->priv; + + if (!ir) + return 0; + + dprintk(2, "%s\n",__func__); + + if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC)) + ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff); + + ir->rc_type = rc_type; + + tm6000_ir_config(ir); + /* TODO */ + return 0; +} + +static int __tm6000_ir_int_start(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + struct tm6000_core *dev = ir->dev; + int pipe, size; + int err = -ENOMEM; + + if (!ir) + return -ENODEV; + + dprintk(2, "%s\n",__func__); + + ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!ir->int_urb) + return -ENOMEM; + + pipe = usb_rcvintpipe(dev->udev, + dev->int_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + dprintk(1, "IR max size: %d\n", size); + + ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC); + if (ir->int_urb->transfer_buffer == NULL) { + usb_free_urb(ir->int_urb); + return err; + } + dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval); + + usb_fill_int_urb(ir->int_urb, dev->udev, pipe, + ir->int_urb->transfer_buffer, size, + tm6000_ir_urb_received, dev, + dev->int_in.endp->desc.bInterval); + + ir->submit_urb = 1; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + + return 0; +} + +static void __tm6000_ir_int_stop(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + if (!ir || !ir->int_urb) + return; + + dprintk(2, "%s\n",__func__); + + usb_kill_urb(ir->int_urb); + kfree(ir->int_urb->transfer_buffer); + usb_free_urb(ir->int_urb); + ir->int_urb = NULL; +} + +int tm6000_ir_int_start(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + if (!ir) + return 0; + + return __tm6000_ir_int_start(ir->rc); +} + +void tm6000_ir_int_stop(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + if (!ir || !ir->rc) + return; + + __tm6000_ir_int_stop(ir->rc); +} + +int tm6000_ir_init(struct tm6000_core *dev) +{ + struct tm6000_IR *ir; + struct rc_dev *rc; + int err = -ENOMEM; + + if (!enable_ir) + return -ENODEV; + + if (!dev->caps.has_remote) + return 0; + + if (!dev->ir_codes) + return 0; + + ir = kzalloc(sizeof(*ir), GFP_ATOMIC); + rc = rc_allocate_device(); + if (!ir || !rc) + goto out; + + dprintk(2, "%s\n", __func__); + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + ir->rc = rc; + + /* input setup */ + rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC; + /* Neded, in order to support NEC remotes with 24 or 32 bits */ + rc->scanmask = 0xffff; + rc->priv = ir; + rc->change_protocol = tm6000_ir_change_protocol; + if (dev->int_in.endp) { + rc->open = __tm6000_ir_int_start; + rc->close = __tm6000_ir_int_stop; + INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work); + } else { + rc->open = tm6000_ir_start; + rc->close = tm6000_ir_stop; + ir->polling = 50; + INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key); + } + rc->driver_type = RC_DRIVER_SCANCODE; + + snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN); + + rc->input_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_USB; + rc->input_id.version = 1; + rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + rc->map_name = dev->ir_codes; + rc->driver_name = "tm6000"; + rc->dev.parent = &dev->udev->dev; + + /* ir register */ + err = rc_register_device(rc); + if (err) + goto out; + + return 0; + +out: + dev->ir = NULL; + rc_free_device(rc); + kfree(ir); + return err; +} + +int tm6000_ir_fini(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + /* skip detach on non attached board */ + + if (!ir) + return 0; + + dprintk(2, "%s\n",__func__); + + if (!ir->polling) + __tm6000_ir_int_stop(ir->rc); + + tm6000_ir_stop(ir->rc); + + /* Turn off the led */ + tm6000_flash_led(dev, 0); + ir->pwled = 0; + + rc_unregister_device(ir->rc); + + kfree(ir); + dev->ir = NULL; + + return 0; +} diff --git a/drivers/media/usb/tm6000/tm6000-regs.h b/drivers/media/usb/tm6000/tm6000-regs.h new file mode 100644 index 000000000000..a38c251ed57b --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-regs.h @@ -0,0 +1,600 @@ +/* + * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 TV Master TM5600/TM6000/TM6010 Request codes + */ +#define REQ_00_SET_IR_VALUE 0 +#define REQ_01_SET_WAKEUP_IRCODE 1 +#define REQ_02_GET_IR_CODE 2 +#define REQ_03_SET_GET_MCU_PIN 3 +#define REQ_04_EN_DISABLE_MCU_INT 4 +#define REQ_05_SET_GET_USBREG 5 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_06_SET_GET_USBREG_BIT 6 +#define REQ_07_SET_GET_AVREG 7 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_08_SET_GET_AVREG_BIT 8 +#define REQ_09_SET_GET_TUNER_FQ 9 +#define REQ_10_SET_TUNER_SYSTEM 10 +#define REQ_11_SET_EEPROM_ADDR 11 +#define REQ_12_SET_GET_EEPROMBYTE 12 +#define REQ_13_GET_EEPROM_SEQREAD 13 +#define REQ_14_SET_GET_I2C_WR2_RDN 14 +#define REQ_15_SET_GET_I2CBYTE 15 + /* Write: Subaddr, Slave Addr, value, 0 */ + /* Read : Subaddr, Slave Addr, value, 1 */ +#define REQ_16_SET_GET_I2C_WR1_RDN 16 + /* Subaddr, Slave Addr, 0, length */ +#define REQ_17_SET_GET_I2CFP 17 + /* Write: Slave Addr, register, value */ + /* Read : Slave Addr, register, 2, data */ +#define REQ_20_DATA_TRANSFER 20 +#define REQ_30_I2C_WRITE 30 +#define REQ_31_I2C_READ 31 +#define REQ_35_AFTEK_TUNER_READ 35 +#define REQ_40_GET_VERSION 40 +#define REQ_50_SET_START 50 +#define REQ_51_SET_STOP 51 +#define REQ_52_TRANSMIT_DATA 52 +#define REQ_53_SPI_INITIAL 53 +#define REQ_54_SPI_SETSTART 54 +#define REQ_55_SPI_INOUTDATA 55 +#define REQ_56_SPI_SETSTOP 56 + +/* + * Define TV Master TM5600/TM6000/TM6010 GPIO lines + */ + +#define TM6000_GPIO_CLK 0x101 +#define TM6000_GPIO_DATA 0x100 + +#define TM6000_GPIO_1 0x102 +#define TM6000_GPIO_2 0x103 +#define TM6000_GPIO_3 0x104 +#define TM6000_GPIO_4 0x300 +#define TM6000_GPIO_5 0x301 +#define TM6000_GPIO_6 0x304 +#define TM6000_GPIO_7 0x305 + +/* tm6010 defines GPIO with different values */ +#define TM6010_GPIO_0 0x0102 +#define TM6010_GPIO_1 0x0103 +#define TM6010_GPIO_2 0x0104 +#define TM6010_GPIO_3 0x0105 +#define TM6010_GPIO_4 0x0106 +#define TM6010_GPIO_5 0x0107 +#define TM6010_GPIO_6 0x0300 +#define TM6010_GPIO_7 0x0301 +#define TM6010_GPIO_9 0x0305 +/* + * Define TV Master TM5600/TM6000/TM6010 URB message codes and length + */ + +enum { + TM6000_URB_MSG_VIDEO = 1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; + +/* Define specific TM6000 Video decoder registers */ +#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 +#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 +#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda +#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb +#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc +#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd +#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde +#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf +#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 +#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 +#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 +#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 +#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 +#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 +#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 +#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 +#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 +#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 +#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea +#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb +#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec +#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed +#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee +#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef +#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd +#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe + +/* Define TM6000/TM6010 Video decoder registers */ +#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 +#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 +#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02 +#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03 +#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04 +#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05 +#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06 +#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07 +#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08 +#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09 +#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a +#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b +#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c +#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d +#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f +#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10 +#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11 +#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12 +#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13 +#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14 +#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15 +#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16 +#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17 +#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18 +#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19 +#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a +#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b +#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c +#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d +#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e +#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f +#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20 +#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21 +#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22 +#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23 +#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24 +#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25 +#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26 +#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27 +#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28 +#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29 +#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a +#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b +#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c +#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d +#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e +#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f +#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30 +#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31 +#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32 +#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33 +#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34 +#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35 +#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36 +#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37 +#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38 +#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39 +#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a +#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b +#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c +#define TM6010_REQ07_R3F_RESET 0x07, 0x3f +#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40 +#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41 +#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42 +#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43 +#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44 +#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45 +#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46 +#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47 +#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48 +#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49 +#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a +#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b +#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c +#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d +#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e +#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f +#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50 +#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51 +#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52 +#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53 +#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54 +#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55 +#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56 +#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57 +#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58 +#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59 +#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a +#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b +#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c +#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d +#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e +#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f +#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60 +#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61 +#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62 +#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63 +#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64 +#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65 +#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66 +#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67 +#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68 +#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70 +#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71 +#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72 +#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73 +#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74 +#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75 +#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76 +#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77 +#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78 +#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79 +#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a +#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b +#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c +#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d +#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f +#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80 +#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82 +#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83 +#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84 +#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85 +#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86 +#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87 +#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a +#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b +#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d +#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e +#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f + +/* Define TM6000/TM6010 Miscellaneous registers */ +#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0 +#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1 +#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2 +#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3 +#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4 +#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5 +#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6 +#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7 +#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8 +#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 +#define TM6010_REQ07_RCA_VEND0 0x07, 0xca +#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc +#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5) +#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6) +#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 +#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 +#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2 +#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3 +#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4 +#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 +#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 +#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR 0x07, 0xd8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD9_IR_BSIZE 0x07, 0xd9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDA_IR_WAKEUP_SEL 0x07, 0xda +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDB_IR_WAKEUP_ADD 0x07, 0xdb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDC_IR_LEADER1 0x07, 0xdc +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDD_IR_LEADER0 0x07, 0xdd +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDE_IR_PULSE_CNT1 0x07, 0xde +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDF_IR_PULSE_CNT0 0x07, 0xdf +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF7_BIST 0x07, 0xf7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe +#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff + +/* Define TM6000/TM6010 USB registers */ +#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00 +#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01 +#define TM6010_REQ05_R02_TEST 0x05, 0x02 +#define TM6010_REQ05_R04_SOFN0 0x05, 0x04 +#define TM6010_REQ05_R05_SOFN1 0x05, 0x05 +#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06 +#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07 +#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08 +#define TM6010_REQ05_R09_VCTL 0x05, 0x09 +#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a +#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b +#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c +#define TM6010_REQ05_R10_GMASK 0x05, 0x10 +#define TM6010_REQ05_R11_IMASK0 0x05, 0x11 +#define TM6010_REQ05_R12_IMASK1 0x05, 0x12 +#define TM6010_REQ05_R13_IMASK2 0x05, 0x13 +#define TM6010_REQ05_R14_IMASK3 0x05, 0x14 +#define TM6010_REQ05_R15_IMASK4 0x05, 0x15 +#define TM6010_REQ05_R16_IMASK5 0x05, 0x16 +#define TM6010_REQ05_R17_IMASK6 0x05, 0x17 +#define TM6010_REQ05_R18_IMASK7 0x05, 0x18 +#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19 +#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a +#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c +#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d +#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20 +#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21 +#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22 +#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23 +#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24 +#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25 +#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26 +#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27 +#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28 +#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29 +#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a +#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b +#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c +#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d +#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e +#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f +#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30 +#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31 +#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32 +#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33 +#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34 +#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35 +#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36 +#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37 +#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38 +#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39 +#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a +#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b +#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c +#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d +#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e +#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41 +#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42 +#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43 +#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44 +#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45 +#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46 +#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47 +#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48 +#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49 +#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a +#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b +#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c +#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d +#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e +#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f +#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50 +#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51 +#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53 +#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55 +#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57 +#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59 +#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a +#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b +#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c +#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d +#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61 +#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62 +#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63 +#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64 +#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65 +#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66 +#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67 +#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68 +#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69 +#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a +#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b +#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c +#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d +#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e +#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f +#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70 +#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b +#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d +#define TM6010_REQ05_R80_FIFO0 0x05, 0x80 +#define TM6010_REQ05_R81_FIFO1 0x05, 0x81 +#define TM6010_REQ05_R82_FIFO2 0x05, 0x82 +#define TM6010_REQ05_R83_FIFO3 0x05, 0x83 +#define TM6010_REQ05_R84_FIFO4 0x05, 0x84 +#define TM6010_REQ05_R85_FIFO5 0x05, 0x85 +#define TM6010_REQ05_R86_FIFO6 0x05, 0x86 +#define TM6010_REQ05_R87_FIFO7 0x05, 0x87 +#define TM6010_REQ05_R88_FIFO8 0x05, 0x88 +#define TM6010_REQ05_R89_FIFO9 0x05, 0x89 +#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a +#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b +#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c +#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d +#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e +#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f +#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90 +#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91 +#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92 +#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93 +#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94 +#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95 +#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96 +#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97 +#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98 +#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99 +#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a +#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b +#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c +#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d +#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e +#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f +#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0 +#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1 +#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2 +#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3 +#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4 +#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5 +#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6 +#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7 +#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8 +#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9 +#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa +#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab +#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac +#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad +#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae +#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf +#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0 +#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1 +#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2 +#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3 +#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4 +#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5 +#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6 +#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7 +#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8 +#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9 +#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba +#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb +#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc +#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd +#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe +#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf +#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0 +#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4 +#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8 +#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc +#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0 +#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4 +#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8 +#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc +#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0 +#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4 +#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8 +#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec +#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0 +#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4 +#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 +#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc + +/* Define TM6010 Audio decoder registers */ +/* This core available only in TM6010 */ +#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 +#define TM6010_REQ08_R01_A_INIT 0x08, 0x01 +#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 +#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03 +#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04 +#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05 +#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06 +#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07 +#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08 +#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09 +#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a +#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b +#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c +#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d +#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e +#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f +#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10 +#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11 +#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12 +#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13 +#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14 +#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15 +#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16 +#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17 +#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18 +#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19 +#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a +#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b +#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e +#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f +#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20 +#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21 +#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22 +#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23 +#define TM6010_REQ08_R24_A_SER 0x08, 0x24 +#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25 +#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26 +#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 +#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 + +/* Define TM6010 Video ADC registers */ +#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 +#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 +#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 +#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3 +#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4 +#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5 +#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6 +#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7 +#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8 +#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9 +#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea +#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb +#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec +#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed + +/* Define TM6010 Audio ADC registers */ +#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 +#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 +#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 +#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3 diff --git a/drivers/media/usb/tm6000/tm6000-stds.c b/drivers/media/usb/tm6000/tm6000-stds.c new file mode 100644 index 000000000000..5e28d6a2412f --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-stds.c @@ -0,0 +1,638 @@ +/* + * tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2007 Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int tm6010_a_mode; +module_param(tm6010_a_mode, int, 0644); +MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode"); + +struct tm6000_reg_settings { + unsigned char req; + unsigned char reg; + unsigned char value; +}; + + +struct tm6000_std_settings { + v4l2_std_id id; + struct tm6000_reg_settings *common; +}; + +static struct tm6000_reg_settings composite_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings composite_stds[] = { + { .id = V4L2_STD_PAL_M, .common = composite_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, }, + { .id = V4L2_STD_PAL, .common = composite_pal, }, + { .id = V4L2_STD_SECAM, .common = composite_secam, }, + { .id = V4L2_STD_NTSC, .common = composite_ntsc, }, +}; + +static struct tm6000_reg_settings svideo_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings svideo_stds[] = { + { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, }, + { .id = V4L2_STD_PAL, .common = svideo_pal, }, + { .id = V4L2_STD_SECAM, .common = svideo_secam, }, + { .id = V4L2_STD_NTSC, .common = svideo_ntsc, }, +}; + +static int tm6000_set_audio_std(struct tm6000_core *dev) +{ + uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ + uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ + uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */ + + if (dev->radio) { + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); + /* set mono or stereo */ + if (dev->amode == V4L2_TUNER_MODE_MONO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); + else if (dev->amode == V4L2_TUNER_MODE_STEREO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff); + return 0; + } + + /* + * STD/MN shouldn't be affected by tm6010_a_mode, as there's just one + * audio standard for each V4L2_STD type. + */ + if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_KR) { + areg_05 |= 0x04; + } else if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_JP) { + areg_05 |= 0x43; + } else if (dev->norm & V4L2_STD_MN) { + areg_05 |= 0x22; + } else switch (tm6010_a_mode) { + /* auto */ + case 0: + if ((dev->norm & V4L2_STD_SECAM) == V4L2_STD_SECAM_L) + areg_05 |= 0x00; + else /* Other PAL/SECAM standards */ + areg_05 |= 0x10; + break; + /* A2 */ + case 1: + if (dev->norm & V4L2_STD_DK) + areg_05 = 0x09; + else + areg_05 = 0x05; + break; + /* NICAM */ + case 2: + if (dev->norm & V4L2_STD_DK) { + areg_05 = 0x06; + } else if (dev->norm & V4L2_STD_PAL_I) { + areg_05 = 0x08; + } else if (dev->norm & V4L2_STD_SECAM_L) { + areg_05 = 0x0a; + areg_02 = 0x02; + } else { + areg_05 = 0x07; + } + break; + /* other */ + case 3: + if (dev->norm & V4L2_STD_DK) { + areg_05 = 0x0b; + } else { + areg_05 = 0x02; + } + break; + } + + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06); + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08); + tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); + tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14); + tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); + tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32); + tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64); + tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + + return 0; +} + +void tm6000_get_std_res(struct tm6000_core *dev) +{ + /* Currently, those are the only supported resoltions */ + if (dev->norm & V4L2_STD_525_60) + dev->height = 480; + else + dev->height = 576; + + dev->width = 720; +} + +static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set) +{ + int i, rc; + + /* Load board's initialization table */ + for (i = 0; set[i].req; i++) { + rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting " + "req %d, reg %d to value %d\n", + rc, set[i].req, set[i].reg, set[i].value); + return rc; + } + } + + return 0; +} + +int tm6000_set_standard(struct tm6000_core *dev) +{ + struct tm6000_input *input; + int i, rc = 0; + u8 reg_07_fe = 0x8a; + u8 reg_08_f1 = 0xfc; + u8 reg_08_e2 = 0xf0; + u8 reg_08_e6 = 0x0f; + + tm6000_get_std_res(dev); + + if (!dev->radio) + input = &dev->vinput[dev->input]; + else + input = &dev->rinput; + + if (dev->dev_type == TM6010) { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8); + reg_08_e6 = 0x00; + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x00, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x10, 0xf0); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x08, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x10, 0xf0); + break; + case TM6000_AMUX_SIF1: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x30, 0xf0); + break; + case TM6000_AMUX_SIF2: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x30, 0xf0); + break; + default: + break; + } + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe); + } else { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f); + break; + default: + break; + } + } + if (input->type == TM6000_INPUT_SVIDEO) { + for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { + if (dev->norm & svideo_stds[i].id) { + rc = tm6000_load_std(dev, svideo_stds[i].common); + goto ret; + } + } + return -EINVAL; + } else { + for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { + if (dev->norm & composite_stds[i].id) { + rc = tm6000_load_std(dev, composite_stds[i].common); + goto ret; + } + } + return -EINVAL; + } + +ret: + if (rc < 0) + return rc; + + if ((dev->dev_type == TM6010) && + ((input->amux == TM6000_AMUX_SIF1) || + (input->amux == TM6000_AMUX_SIF2))) + tm6000_set_audio_std(dev); + + msleep(40); + + return 0; +} diff --git a/drivers/media/usb/tm6000/tm6000-usb-isoc.h b/drivers/media/usb/tm6000/tm6000-usb-isoc.h new file mode 100644 index 000000000000..99d15a55aa03 --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-usb-isoc.h @@ -0,0 +1,50 @@ +/* + * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + +#define TM6000_URB_MSG_LEN 180 + +struct usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int vfield, field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct tm6000_buffer *buf; +}; diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c new file mode 100644 index 000000000000..45ed59cb2d04 --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -0,0 +1,1852 @@ +/* + * tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab + * + * Copyright (C) 2007 Michel Ludwig + * - Fixed module load/unload + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "tm6000-regs.h" +#include "tm6000.h" + +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +/* Limits minimum and default number of buffers */ +#define TM6000_MIN_BUF 4 +#define TM6000_DEF_BUF 8 + +#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ + +/* Debug level */ +int tm6000_debug; +EXPORT_SYMBOL_GPL(tm6000_debug); + +static const struct v4l2_queryctrl no_ctrl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +/* supported controls */ +static struct v4l2_queryctrl tm6000_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 54, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 119, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 112, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 0x1, + .default_value = 0, + .flags = 0, + }, + /* --- audio --- */ + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = -15, + .maximum = 15, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + +static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl); +static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; + +static struct tm6000_fmt format[] = { + { + .name = "4:2:2, packed, YVY2", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + }, { + .name = "A/V + VBI mux packet", + .fourcc = V4L2_PIX_FMT_TM6000, + .depth = 16, + } +}; + +static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) +{ + unsigned int i; + + for (i = 0; i < CTRLS; i++) + if (tm6000_qctrl[i].id == id) + return tm6000_qctrl+i; + return NULL; +} + +/* ------------------------------------------------------------------ + * DMA and thread functions + * ------------------------------------------------------------------ + */ + +#define norm_maxw(a) 720 +#define norm_maxh(a) 576 + +#define norm_minw(a) norm_maxw(a) +#define norm_minh(a) norm_maxh(a) + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer **buf) +{ + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + + if (list_empty(&dma_q->active)) { + dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); + *buf = NULL; + return; + } + + *buf = list_entry(dma_q->active.next, + struct tm6000_buffer, vb.queue); +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct tm6000_core *dev, + struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer *buf) +{ + /* Advice that buffer was filled */ + dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_streams(u8 *data, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + u8 *ptr = data, *endp = data+len; + unsigned long header = 0; + int rc = 0; + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; + struct tm6000_buffer *vbuf = NULL; + char *voutp = NULL; + unsigned int linewidth; + + if (!dev->radio) { + /* get video buffer */ + get_next_buf(dma_q, &vbuf); + + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + + if (!voutp) + return 0; + } + + for (ptr = data; ptr < endp;) { + if (!dev->isoc_ctl.cmd) { + /* Header */ + if (dev->isoc_ctl.tmp_buf_len > 0) { + /* from last urb or packet */ + header = dev->isoc_ctl.tmp_buf; + if (4 - dev->isoc_ctl.tmp_buf_len > 0) { + memcpy((u8 *)&header + + dev->isoc_ctl.tmp_buf_len, + ptr, + 4 - dev->isoc_ctl.tmp_buf_len); + ptr += 4 - dev->isoc_ctl.tmp_buf_len; + } + dev->isoc_ctl.tmp_buf_len = 0; + } else { + if (ptr + 3 >= endp) { + /* have incomplete header */ + dev->isoc_ctl.tmp_buf_len = endp - ptr; + memcpy(&dev->isoc_ctl.tmp_buf, ptr, + dev->isoc_ctl.tmp_buf_len); + return rc; + } + /* Seek for sync */ + for (; ptr < endp - 3; ptr++) { + if (*(ptr + 3) == 0x47) + break; + } + /* Get message header */ + header = *(unsigned long *)ptr; + ptr += 4; + } + + /* split the header fields */ + size = ((header & 0x7e) << 1); + if (size > 0) + size -= 4; + block = (header >> 7) & 0xf; + field = (header >> 11) & 0x1; + line = (header >> 12) & 0x1ff; + cmd = (header >> 21) & 0x7; + /* Validates haeder fields */ + if (size > TM6000_URB_MSG_LEN) + size = TM6000_URB_MSG_LEN; + pktsize = TM6000_URB_MSG_LEN; + /* + * calculate position in buffer and change the buffer + */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + if (!dev->radio) { + if ((dev->isoc_ctl.vfield != field) && + (field == 1)) { + /* + * Announces that a new buffer + * were filled + */ + buffer_filled(dev, dma_q, vbuf); + dprintk(dev, V4L2_DEBUG_ISOC, + "new buffer filled\n"); + get_next_buf(dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + if (!voutp) + return rc; + memset(voutp, 0, vbuf->vb.size); + } + linewidth = vbuf->vb.width << 1; + pos = ((line << 1) - field - 1) * + linewidth + block * TM6000_URB_MSG_LEN; + /* Don't allow to write out of the buffer */ + if (pos + size > vbuf->vb.size) + cmd = TM6000_URB_MSG_ERR; + dev->isoc_ctl.vfield = field; + } + break; + case TM6000_URB_MSG_VBI: + break; + case TM6000_URB_MSG_AUDIO: + case TM6000_URB_MSG_PTS: + size = pktsize; /* Size is always 180 bytes */ + break; + } + } else { + /* Continue the last copy */ + cmd = dev->isoc_ctl.cmd; + size = dev->isoc_ctl.size; + pos = dev->isoc_ctl.pos; + pktsize = dev->isoc_ctl.pktsize; + field = dev->isoc_ctl.field; + } + cpysize = (endp - ptr > size) ? size : endp - ptr; + if (cpysize) { + /* copy data in different buffers */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + if (vbuf) + memcpy(&voutp[pos], ptr, cpysize); + break; + case TM6000_URB_MSG_AUDIO: { + int i; + for (i = 0; i < cpysize; i += 2) + swab16s((u16 *)(ptr + i)); + + tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize); + break; + } + case TM6000_URB_MSG_VBI: + /* Need some code to copy vbi buffer */ + break; + case TM6000_URB_MSG_PTS: { + /* Need some code to copy pts */ + u32 pts; + pts = *(u32 *)ptr; + dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x", + field, pts); + break; + } + } + } + if (ptr + pktsize > endp) { + /* + * End of URB packet, but cmd processing is not + * complete. Preserve the state for a next packet + */ + dev->isoc_ctl.pos = pos + cpysize; + dev->isoc_ctl.size = size - cpysize; + dev->isoc_ctl.cmd = cmd; + dev->isoc_ctl.field = field; + dev->isoc_ctl.pktsize = pktsize - (endp - ptr); + ptr += endp - ptr; + } else { + dev->isoc_ctl.cmd = 0; + ptr += pktsize; + } + } + return 0; +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_multiplexed(u8 *ptr, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + unsigned int pos = dev->isoc_ctl.pos, cpysize; + int rc = 1; + struct tm6000_buffer *buf; + char *outp = NULL; + + get_next_buf(dma_q, &buf); + if (buf) + outp = videobuf_to_vmalloc(&buf->vb); + + if (!outp) + return 0; + + while (len > 0) { + cpysize = min(len, buf->vb.size-pos); + memcpy(&outp[pos], ptr, cpysize); + pos += cpysize; + ptr += cpysize; + len -= cpysize; + if (pos >= buf->vb.size) { + pos = 0; + /* Announces that a new buffer were filled */ + buffer_filled(dev, dma_q, buf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); + get_next_buf(dma_q, &buf); + if (!buf) + break; + outp = videobuf_to_vmalloc(&(buf->vb)); + if (!outp) + return rc; + pos = 0; + } + } + + dev->isoc_ctl.pos = pos; + return rc; +} + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + + +/* + * Controls the isoc copy of each urb packet + */ +static inline int tm6000_isoc_copy(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i, len = 0, rc = 1, status; + char *p; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + continue; + } + + len = urb->iso_frame_desc[i].actual_length; + + if (len > 0) { + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (!urb->iso_frame_desc[i].status) { + if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) { + rc = copy_multiplexed(p, len, urb); + if (rc <= 0) + return rc; + } else { + copy_streams(p, len, urb); + } + } + } + } + return rc; +} + +/* ------------------------------------------------------------------ + * URB control + * ------------------------------------------------------------------ + */ + +/* + * IRQ callback, called by URB callback + */ +static void tm6000_irq_callback(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + tm6000_err("urb completion error %d.\n", urb->status); + break; + } + + spin_lock(&dev->slock); + tm6000_isoc_copy(urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) + tm6000_err("urb resubmit failed (error=%i)\n", + urb->status); +} + +/* + * Stop and Deallocate URBs + */ +static void tm6000_uninit_isoc(struct tm6000_core *dev) +{ + struct urb *urb; + int i; + + dev->isoc_ctl.buf = NULL; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; +} + +/* + * Allocate URBs and start IRQ + */ +static int tm6000_prepare_isoc(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; + struct urb *urb; + + /* De-allocates all pending stuff */ + tm6000_uninit_isoc(dev); + /* Stop interrupt USB pipe */ + tm6000_ir_int_stop(dev); + + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, + dev->isoc_in.bAlternateSetting); + + /* Start interrupt USB pipe */ + tm6000_ir_int_start(dev); + + pipe = usb_rcvisocpipe(dev->udev, + dev->isoc_in.endp->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + + if (size > dev->isoc_in.maxsize) + size = dev->isoc_in.maxsize; + + dev->isoc_ctl.max_pkt_size = size; + + max_packets = TM6000_MAX_ISO_PACKETS; + sb_size = max_packets * size; + + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + tm6000_err("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + tm6000_err("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets" + " (%d bytes) of %d bytes each to handle %u size\n", + max_packets, num_bufs, sb_size, + dev->isoc_in.maxsize, size); + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + tm6000_err("cannot alloc isoc_ctl.urb %i\n", i); + tm6000_uninit_isoc(dev); + usb_free_urb(urb); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + tm6000_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + tm6000_irq_callback, dma_q); + urb->interval = dev->isoc_in.endp->desc.bInterval; + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = size * j; + urb->iso_frame_desc[j].length = size; + } + } + + return 0; +} + +static int tm6000_start_thread(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i; + + dma_q->frame = 0; + dma_q->ini_jiffies = jiffies; + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + tm6000_err("submit of urb %i failed (error=%i)\n", i, + rc); + tm6000_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* ------------------------------------------------------------------ + * Videobuf operations + * ------------------------------------------------------------------ + */ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct tm6000_fh *fh = vq->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = TM6000_DEF_BUF; + + if (*count < TM6000_MIN_BUF) + *count = TM6000_MIN_BUF; + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + unsigned long flags; + + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_core *dev = fh->dev; + int rc = 0; + + BUG_ON(NULL == fh->fmt); + + + /* FIXME: It assumes depth=2 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->vb.state = VIDEOBUF_NEEDS_INIT; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + } + + if (!dev->isoc_ctl.num_bufs) { + rc = tm6000_prepare_isoc(dev); + if (rc < 0) + goto fail; + + rc = tm6000_start_thread(dev); + if (rc < 0) + goto fail; + + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + struct tm6000_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops tm6000_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + * IOCTL handling + * ------------------------------------------------------------------ + */ + +static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read) + return true; + + return false; +} + +static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh) + return true; + + return false; +} + +static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh, + bool is_res_read) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read == is_res_read) + return true; + + /* is it free? */ + if (dev->resources) + return false; + + /* grab it */ + dev->resources = fh; + dev->is_res_read = is_res_read; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); + return true; +} + +static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources != fh) + return; + + dev->resources = NULL; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); +} + +/* ------------------------------------------------------------------ + * IOCTL vidioc handling + * ------------------------------------------------------------------ + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + + strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); + strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE; + + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return format+i; + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct tm6000_fmt *fmt; + enum v4l2_field field; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" + " invalid.\n", f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_SEQ_TB; + else if (V4L2_FIELD_INTERLACED != field) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); + return -EINVAL; + } + + tm6000_get_std_res(dev); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + + f->fmt.pix.width &= ~0x01; + + f->fmt.pix.field = field; + + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +/*FIXME: This seems to be generic enough to be at videodev2 */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int ret = vidioc_try_fmt_vid_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + + dev->fourcc = f->fmt.pix.pixelformat; + + tm6000_set_fourcc_format(dev); + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + if (!res_get(dev, fh, false)) + return -EBUSY; + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (i != fh->type) + return -EINVAL; + + videobuf_streamoff(&fh->vb_vidq); + res_free(dev, fh); + + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + int rc = 0; + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + dev->norm = *norm; + rc = tm6000_init_analog_mode(dev); + + fh->width = dev->width; + fh->height = dev->height; + + if (rc < 0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + return 0; +} + +static const char *iname[] = { + [TM6000_INPUT_TV] = "Television", + [TM6000_INPUT_COMPOSITE1] = "Composite 1", + [TM6000_INPUT_COMPOSITE2] = "Composite 2", + [TM6000_INPUT_SVIDEO] = "S-Video", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= 3) + return -EINVAL; + + if (!dev->vinput[n].type) + return -EINVAL; + + i->index = n; + + if (dev->vinput[n].type == TM6000_INPUT_TV) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; + + strcpy(i->name, iname[dev->vinput[n].type]); + + i->std = TM6000_STD; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + *i = dev->input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int rc = 0; + + if (i >= 3) + return -EINVAL; + if (!dev->vinput[i].type) + return -EINVAL; + + dev->input = i; + + rc = vidioc_s_std(file, priv, &dev->vfd->current_norm); + + return rc; +} + +/* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + if (qc->id && qc->id == tm6000_qctrl[i].id) { + memcpy(qc, &(tm6000_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int val; + + /* FIXME: Probably, those won't work! Maybe we need shadow regs */ + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0); + break; + case V4L2_CID_BRIGHTNESS: + val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0); + return 0; + case V4L2_CID_SATURATION: + val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0); + return 0; + case V4L2_CID_HUE: + val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); + return 0; + case V4L2_CID_AUDIO_MUTE: + val = dev->ctl_mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + val = dev->ctl_volume; + return 0; + default: + return -EINVAL; + } + + if (val < 0) + return val; + + ctrl->value = val; + + return 0; +} +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + u8 val = ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); + return 0; + case V4L2_CID_BRIGHTNESS: + tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); + return 0; + case V4L2_CID_SATURATION: + tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); + return 0; + case V4L2_CID_HUE: + tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); + return 0; + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = val; + tm6000_tvaudio_set_mute(dev, val); + return 0; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = val; + tm6000_set_volume(dev, val); + return 0; + } + return -EINVAL; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + t->audmode = dev->amode; + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + dev->amode = t->audmode; + dprintk(dev, 3, "audio mode: %x\n", t->audmode); + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->freq; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; + if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + return -EINVAL; + + dev->freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + + return 0; +} + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + strcpy(cap->driver, "tm6000"); + strlcpy(cap->card, dev->name, sizeof(dev->name)); + sprintf(cap->bus_info, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + cap->version = dev->dev_type; + cap->capabilities = V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | + V4L2_CAP_RADIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (i->index != 0) + return -EINVAL; + + if (!dev->rinput.type) + return -EINVAL; + + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_input(struct file *filp, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (dev->input != 5) + return -EINVAL; + + *i = dev->input - 5; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + memset(a, 0, sizeof(*a)); + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *filp, void *priv, unsigned int i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (i) + return -EINVAL; + + if (!dev->rinput.type) + return -EINVAL; + + dev->input = i + 5; + + return 0; +} + +static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + const struct v4l2_queryctrl *ctrl; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + ctrl = ctrl_by_id(c->id); + *c = *ctrl; + } else + *c = no_ctrl; + + return 0; +} + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int __tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct tm6000_core *dev = video_drvdata(file); + struct tm6000_fh *fh; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int i, rc; + int radio = 0; + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", + video_device_node_name(vdev)); + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + } + + /* If more than one user, mutex should be added */ + dev->users++; + + dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n", + video_device_node_name(vdev), v4l2_type_names[type], + dev->users); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + dev->users--; + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->radio = radio; + dev->radio = radio; + fh->type = type; + dev->fourcc = format[0].fourcc; + + fh->fmt = format_by_fourcc(dev->fourcc); + + tm6000_get_std_res(dev); + + fh->width = dev->width; + fh->height = dev->height; + + dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, " + "dev->vidq=0x%08lx\n", + (unsigned long)fh, (unsigned long)dev, + (unsigned long)&dev->vidq); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "queued=%d\n", list_empty(&dev->vidq.queued)); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "active=%d\n", list_empty(&dev->vidq.active)); + + /* initialize hardware on analog mode */ + rc = tm6000_init_analog_mode(dev); + if (rc < 0) + return rc; + + if (dev->mode != TM6000_MODE_ANALOG) { + /* Put all controls at a sane state */ + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + qctl_regs[i] = tm6000_qctrl[i].default_value; + + dev->mode = TM6000_MODE_ANALOG; + } + + if (!fh->radio) { + videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct tm6000_buffer), fh, &dev->lock); + } else { + dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); + dev->input = 5; + tm6000_set_audio_rinput(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + tm6000_prepare_isoc(dev); + tm6000_start_thread(dev); + } + + return 0; +} + +static int tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + int res; + + mutex_lock(vdev->lock); + res = __tm6000_open(file); + mutex_unlock(vdev->lock); + return res; +} + +static ssize_t +tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int res; + + if (!res_get(fh->dev, fh, true)) + return -EBUSY; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + res = videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, + file->f_flags & O_NONBLOCK); + mutex_unlock(&dev->lock); + return res; + } + return 0; +} + +static unsigned int +__tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_buffer *buf; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + if (!!is_res_streaming(fh->dev, fh)) + return POLLERR; + + if (!is_res_read(fh->dev, fh)) { + /* streaming capture */ + if (list_empty(&fh->vb_vidq.stream)) + return POLLERR; + buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); + } else { + /* read() capture */ + return videobuf_poll_stream(file, &fh->vb_vidq, wait); + } + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + return 0; +} + +static unsigned int tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + unsigned int res; + + mutex_lock(&dev->lock); + res = __tm6000_poll(file, wait); + mutex_unlock(&dev->lock); + return res; +} + +static int tm6000_release(struct file *file) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n", + video_device_node_name(vdev), dev->users); + + mutex_lock(&dev->lock); + dev->users--; + + res_free(dev, fh); + + if (!dev->users) { + tm6000_uninit_isoc(dev); + + /* Stop interrupt USB pipe */ + tm6000_ir_int_stop(dev); + + usb_reset_configuration(dev->udev); + + if (dev->int_in.endp) + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, 2); + else + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, 0); + + /* Start interrupt USB pipe */ + tm6000_ir_int_start(dev); + + if (!fh->radio) + videobuf_mmap_free(&fh->vb_vidq); + } + + kfree(fh); + mutex_unlock(&dev->lock); + + return 0; +} + +static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + int res; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + res = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + return res; +} + +static struct v4l2_file_operations tm6000_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .read = tm6000_read, + .poll = tm6000_poll, + .mmap = tm6000_mmap, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +}; + +static struct video_device tm6000_template = { + .name = "tm6000", + .fops = &tm6000_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release, + .tvnorms = TM6000_STD, + .current_norm = V4L2_STD_NTSC_M, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_s_std = radio_s_std, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_input = radio_g_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +}; + +static struct video_device tm6000_radio_template = { + .name = "tm6000", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + +/* ----------------------------------------------------------------- + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + +static struct video_device *vdev_init(struct tm6000_core *dev, + const struct video_device + *template, const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = tm6000_debug; + vfd->lock = &dev->lock; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + video_set_drvdata(vfd, dev); + return vfd; +} + +int tm6000_v4l2_register(struct tm6000_core *dev) +{ + int ret = -1; + + dev->vfd = vdev_init(dev, &tm6000_template, "video"); + + if (!dev->vfd) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + return -ENOMEM; + } + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); + + if (ret < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + return ret; + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->vfd)); + + if (dev->caps.has_radio) { + dev->radio_dev = vdev_init(dev, &tm6000_radio_template, + "radio"); + if (!dev->radio_dev) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr); + if (ret < 0) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->radio_dev)); + } + + printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); + return ret; +} + +int tm6000_v4l2_unregister(struct tm6000_core *dev) +{ + video_unregister_device(dev->vfd); + + if (dev->radio_dev) { + if (video_is_registered(dev->radio_dev)) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + + return 0; +} + +int tm6000_v4l2_exit(void) +{ + return 0; +} + +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "Allow changing video device number"); + +module_param_named(debug, tm6000_debug, int, 0444); +MODULE_PARM_DESC(debug, "activates debug info"); + +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); + diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h new file mode 100644 index 000000000000..6df418658c9c --- /dev/null +++ b/drivers/media/usb/tm6000/tm6000.h @@ -0,0 +1,399 @@ +/* + * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab + * + * Copyright (C) 2007 Michel Ludwig + * - DVB-T support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 "tm6000-usb-isoc.h" +#include +#include +#include + +#include +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dmxdev.h" + +/* Inputs */ +enum tm6000_itype { + TM6000_INPUT_TV = 1, + TM6000_INPUT_COMPOSITE1, + TM6000_INPUT_COMPOSITE2, + TM6000_INPUT_SVIDEO, + TM6000_INPUT_DVB, + TM6000_INPUT_RADIO, +}; + +enum tm6000_mux { + TM6000_VMUX_VIDEO_A = 1, + TM6000_VMUX_VIDEO_B, + TM6000_VMUX_VIDEO_AB, + TM6000_AMUX_ADC1, + TM6000_AMUX_ADC2, + TM6000_AMUX_SIF1, + TM6000_AMUX_SIF2, + TM6000_AMUX_I2S, +}; + +enum tm6000_devtype { + TM6000 = 0, + TM5600, + TM6010, +}; + +struct tm6000_input { + enum tm6000_itype type; + enum tm6000_mux vmux; + enum tm6000_mux amux; + unsigned int v_gpio; + unsigned int a_gpio; +}; + +/* ------------------------------------------------------------------ + * Basic structures + * ------------------------------------------------------------------ + */ + +struct tm6000_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; +}; + +/* buffer for one video frame */ +struct tm6000_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct tm6000_fmt *fmt; +}; + +struct tm6000_dmaqueue { + struct list_head active; + struct list_head queued; + + /* thread for generating video stream*/ + struct task_struct *kthread; + wait_queue_head_t wq; + /* Counters to control fps rate */ + int frame; + int ini_jiffies; +}; + +/* device states */ +enum tm6000_core_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +/* io methods */ +enum tm6000_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum tm6000_mode { + TM6000_MODE_UNKNOWN = 0, + TM6000_MODE_ANALOG, + TM6000_MODE_DIGITAL, +}; + +struct tm6000_gpio { + int tuner_reset; + int tuner_on; + int demod_reset; + int demod_on; + int power_led; + int dvb_led; + int ir; +}; + +struct tm6000_capabilities { + unsigned int has_tuner:1; + unsigned int has_tda9874:1; + unsigned int has_dvb:1; + unsigned int has_zl10353:1; + unsigned int has_eeprom:1; + unsigned int has_remote:1; + unsigned int has_radio:1; +}; + +struct tm6000_dvb { + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dvb_frontend *frontend; + struct dmxdev dmxdev; + unsigned int streams; + struct urb *bulk_urb; + struct mutex mutex; +}; + +struct snd_tm6000_card { + struct snd_card *card; + spinlock_t reg_lock; + struct tm6000_core *core; + struct snd_pcm_substream *substream; + + /* temporary data for buffer fill processing */ + unsigned buf_pos; + unsigned period_pos; +}; + +struct tm6000_endpoint { + struct usb_host_endpoint *endp; + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + unsigned maxsize; +}; + +#define TM6000_QUIRK_NO_USB_DELAY (1 << 0) + +struct tm6000_core { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + enum tm6000_devtype dev_type; /* type of device */ + unsigned char eedata[256]; /* Eeprom data */ + unsigned eedata_size; /* Size of the eeprom info */ + + v4l2_std_id norm; /* Current norm */ + int width, height; /* Selected resolution */ + + enum tm6000_core_state state; + + /* Device Capabilities*/ + struct tm6000_capabilities caps; + + /* Used to load alsa/dvb */ + struct work_struct request_module_wk; + + /* Tuner configuration */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + struct tm6000_gpio gpio; + + char *ir_codes; + + __u8 radio; + + /* Demodulator configuration */ + int demod_addr; /* demodulator address */ + + int audio_bitrate; + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + + /* extension */ + struct list_head devlist; + + /* video for linux */ + int users; + + /* various device info */ + struct tm6000_fh *resources; /* Points to fh that is streaming */ + bool is_res_read; + + struct video_device *vfd; + struct video_device *radio_dev; + struct tm6000_dmaqueue vidq; + struct v4l2_device v4l2_dev; + + int input; + struct tm6000_input vinput[3]; /* video input */ + struct tm6000_input rinput; /* radio input */ + + int freq; + unsigned int fourcc; + + enum tm6000_mode mode; + + int ctl_mute; /* audio */ + int ctl_volume; + int amode; + + /* DVB-T support */ + struct tm6000_dvb *dvb; + + /* audio support */ + struct snd_tm6000_card *adev; + struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ + atomic_t stream_started; /* stream should be running if true */ + + struct tm6000_IR *ir; + + /* locks */ + struct mutex lock; + struct mutex usb_lock; + + /* usb transfer */ + struct usb_device *udev; /* the usb device */ + + struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; + struct tm6000_endpoint int_in, int_out; + + /* scaler!=0 if scaler is active*/ + int scaler; + + /* Isoc control struct */ + struct usb_isoc_ctl isoc_ctl; + + spinlock_t slock; + + unsigned long quirks; +}; + +enum tm6000_ops_type { + TM6000_AUDIO = 0x10, + TM6000_DVB = 0x20, +}; + +struct tm6000_ops { + struct list_head next; + char *name; + enum tm6000_ops_type type; + int (*init)(struct tm6000_core *); + int (*fini)(struct tm6000_core *); + int (*fillbuf)(struct tm6000_core *, char *buf, int size); +}; + +struct tm6000_fh { + struct tm6000_core *dev; + unsigned int radio; + + /* video capture */ + struct tm6000_fmt *fmt; + unsigned int width, height; + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +#define TM6000_STD (V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM) + +/* In tm6000-cards.c */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg); +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); +int tm6000_cards_setup(struct tm6000_core *dev); +void tm6000_flash_led(struct tm6000_core *dev, u8 state); + +/* In tm6000-core.c */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req, + u16 value, u16 index, u8 *buf, u16 len); +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask); +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); +int tm6000_init(struct tm6000_core *dev); +int tm6000_reset(struct tm6000_core *dev); + +int tm6000_init_analog_mode(struct tm6000_core *dev); +int tm6000_init_digital_mode(struct tm6000_core *dev); +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); +int tm6000_set_audio_rinput(struct tm6000_core *dev); +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); +void tm6000_set_volume(struct tm6000_core *dev, int vol); + +int tm6000_v4l2_register(struct tm6000_core *dev); +int tm6000_v4l2_unregister(struct tm6000_core *dev); +int tm6000_v4l2_exit(void); +void tm6000_set_fourcc_format(struct tm6000_core *dev); + +void tm6000_remove_from_devlist(struct tm6000_core *dev); +void tm6000_add_into_devlist(struct tm6000_core *dev); +int tm6000_register_extension(struct tm6000_ops *ops); +void tm6000_unregister_extension(struct tm6000_ops *ops); +void tm6000_init_extension(struct tm6000_core *dev); +void tm6000_close_extension(struct tm6000_core *dev); +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size); + + +/* In tm6000-stds.c */ +void tm6000_get_std_res(struct tm6000_core *dev); +int tm6000_set_standard(struct tm6000_core *dev); + +/* In tm6000-i2c.c */ +int tm6000_i2c_register(struct tm6000_core *dev); +int tm6000_i2c_unregister(struct tm6000_core *dev); + +/* In tm6000-queue.c */ + +int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); + +int tm6000_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int tm6000_vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b); +int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); +ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, + loff_t *f_pos); +unsigned int tm6000_v4l2_poll(struct file *file, + struct poll_table_struct *wait); +int tm6000_queue_init(struct tm6000_core *dev); + +/* In tm6000-alsa.c */ +/*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/ + +/* In tm6000-input.c */ +int tm6000_ir_init(struct tm6000_core *dev); +int tm6000_ir_fini(struct tm6000_core *dev); +void tm6000_ir_wait(struct tm6000_core *dev, u8 state); +int tm6000_ir_int_start(struct tm6000_core *dev); +void tm6000_ir_int_stop(struct tm6000_core *dev); + +/* Debug stuff */ + +extern int tm6000_debug; + +#define dprintk(dev, level, fmt, arg...) do {\ + if (tm6000_debug & level) \ + printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ + dev->name, __func__ , ##arg); } while (0) + +#define V4L2_DEBUG_REG 0x0004 +#define V4L2_DEBUG_I2C 0x0008 +#define V4L2_DEBUG_QUEUE 0x0010 +#define V4L2_DEBUG_ISOC 0x0020 +#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ +#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ + +#define tm6000_err(fmt, arg...) do {\ + printk(KERN_ERR "tm6000 %s :"fmt, \ + __func__ , ##arg); } while (0) diff --git a/drivers/media/usb/usbvision/Kconfig b/drivers/media/usb/usbvision/Kconfig new file mode 100644 index 000000000000..fc24ef05b3f3 --- /dev/null +++ b/drivers/media/usb/usbvision/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_USBVISION + tristate "USB video devices based on Nogatech NT1003/1004/1005" + depends on I2C && VIDEO_V4L2 + select VIDEO_TUNER + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO + ---help--- + There are more than 50 different USB video devices based on + NT1003/1004/1005 USB Bridges. This driver enables using those + devices. + + To compile this driver as a module, choose M here: the + module will be called usbvision. diff --git a/drivers/media/usb/usbvision/Makefile b/drivers/media/usb/usbvision/Makefile new file mode 100644 index 000000000000..d55c6bd97a35 --- /dev/null +++ b/drivers/media/usb/usbvision/Makefile @@ -0,0 +1,6 @@ +usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o + +obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o + +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/usb/usbvision/usbvision-cards.c b/drivers/media/usb/usbvision/usbvision-cards.c new file mode 100644 index 000000000000..3103d0d020e8 --- /dev/null +++ b/drivers/media/usb/usbvision/usbvision-cards.c @@ -0,0 +1,1133 @@ +/* + * usbvision-cards.c + * usbvision cards definition file + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * This program is free software; 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 "usbvision.h" +#include "usbvision-cards.h" + +/* Supported Devices: A table for usbvision.c*/ +struct usbvision_device_data_st usbvision_device_data[] = { + [XANBOO] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 4, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "Xanboo", + }, + [BELKIN_VIDEOBUS_II] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Belkin USB VideoBus II Adapter", + }, + [BELKIN_VIDEOBUS] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "Belkin Components USB VideoBus", + }, + [BELKIN_USB_VIDEOBUS_II] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Belkin USB VideoBus II", + }, + [ECHOFX_INTERVIEW_LITE] = { + .interface = 0, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "echoFX InterView Lite", + }, + [USBGEAR_USBG_V1] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "USBGear USBG-V1 resp. HAMA USB", + }, + [D_LINK_V100] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 4, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "D-Link V100", + }, + [X10_USB_CAMERA] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "X10 USB Camera", + }, + [HPG_WINTV_LIVE_PAL_BG] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Live (PAL B/G)", + }, + [HPG_WINTV_LIVE_PRO_NTSC_MN] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Live Pro (NTSC M/N)", + }, + [ZORAN_PMD_NOGATECH] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 2, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan", + }, + [NOGATECH_USB_TV_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = 20, + .model_string = "Nogatech USB-TV (NTSC) FM", + }, + [PNY_USB_TV_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = 20, + .model_string = "PNY USB-TV (NTSC) FM", + }, + [PV_PLAYTV_USB_PRO_PAL_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "PixelView PlayTv-USB PRO (PAL) FM", + }, + [ZT_721] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "ZTV ZT-721 2.4GHz USB A/V Receiver", + }, + [HPG_WINTV_NTSC_MN] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = 20, + .model_string = "Hauppauge WinTV USB (NTSC M/N)", + }, + [HPG_WINTV_PAL_BG] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL B/G)", + }, + [HPG_WINTV_PAL_I] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL I)", + }, + [HPG_WINTV_PAL_SECAM_L] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0x80, + .y_offset = 0x16, + .model_string = "Hauppauge WinTV USB (PAL/SECAM L)", + }, + [HPG_WINTV_PAL_D_K] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL D/K)", + }, + [HPG_WINTV_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (NTSC FM)", + }, + [HPG_WINTV_PAL_BG_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL B/G FM)", + }, + [HPG_WINTV_PAL_I_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL I FM)", + }, + [HPG_WINTV_PAL_D_K_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL D/K FM)", + }, + [HPG_WINTV_PRO_NTSC_MN] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N)", + }, + [HPG_WINTV_PRO_NTSC_MN_V2] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V2", + }, + [HPG_WINTV_PRO_PAL] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)", + }, + [HPG_WINTV_PRO_NTSC_MN_V3] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V3", + }, + [HPG_WINTV_PRO_PAL_BG] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G)", + }, + [HPG_WINTV_PRO_PAL_I] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL I)", + }, + [HPG_WINTV_PRO_PAL_SECAM_L] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM L)", + }, + [HPG_WINTV_PRO_PAL_D_K] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL D/K)", + }, + [HPG_WINTV_PRO_PAL_SECAM] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)", + }, + [HPG_WINTV_PRO_PAL_SECAM_V2] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2", + }, + [HPG_WINTV_PRO_PAL_BG_V2] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_ALPS_TSBE1_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G) V2", + }, + [HPG_WINTV_PRO_PAL_BG_D_K] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_ALPS_TSBE1_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G,D/K)", + }, + [HPG_WINTV_PRO_PAL_I_D_K] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL I,D/K)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM)", + }, + [HPG_WINTV_PRO_PAL_BG_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_I_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL I FM)", + }, + [HPG_WINTV_PRO_PAL_D_K_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL D/K FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM_V2] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2", + }, + [CAMTEL_TVB330] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 5, + .y_offset = 5, + .model_string = "Camtel Technology USB TV Genie Pro FM Model TVB330", + }, + [DIGITAL_VIDEO_CREATOR_I] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Digital Video Creator I", + }, + [GLOBAL_VILLAGE_GV_007_NTSC] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 82, + .y_offset = 20, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Global Village GV-007 (NTSC)", + }, + [DAZZLE_DVC_50_REV_1_NTSC] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)", + }, + [DAZZLE_DVC_80_REV_1_PAL] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)", + }, + [DAZZLE_DVC_90_REV_1_SECAM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)", + }, + [ESKAPE_LABS_MYTV2GO] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Eskape Labs MyTV2Go", + }, + [PINNA_PCTV_USB_PAL] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 0, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4066FY5_PAL_I, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (PAL)", + }, + [PINNA_PCTV_USB_SECAM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (SECAM)", + }, + [PINNA_PCTV_USB_PAL_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 128, + .y_offset = 23, + .model_string = "Pinnacle Studio PCTV USB (PAL) FM", + }, + [MIRO_PCTV_USB] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Miro PCTV USB", + }, + [PINNA_PCTV_USB_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (NTSC) FM", + }, + [PINNA_PCTV_USB_NTSC_FM_V3] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V3", + }, + [PINNA_PCTV_USB_PAL_FM_V2] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4009FR5_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio PCTV USB (PAL) FM V2", + }, + [PINNA_PCTV_USB_NTSC_FM_V2] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4039FR5_NTSC, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V2", + }, + [PINNA_PCTV_USB_PAL_FM_V3] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4009FR5_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio PCTV USB (PAL) FM V3", + }, + [PINNA_LINX_VD_IN_CAB_NTSC] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio Linx Video input cable (NTSC)", + }, + [PINNA_LINX_VD_IN_CAB_PAL] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio Linx Video input cable (PAL)", + }, + [PINNA_PCTV_BUNGEE_PAL_FM] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4009FR5_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle PCTV Bungee USB (PAL) FM", + }, + [HPG_WINTV] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTv-USB", + }, + [MICROCAM_NTSC] = { + .interface = -1, + .codec = CODEC_WEBCAM, + .video_channels = 1, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 0, + .tuner = 0, + .tuner_type = 0, + .x_offset = 71, + .y_offset = 15, + .model_string = "Nogatech USB MicroCam NTSC (NV3000N)", + }, + [MICROCAM_PAL] = { + .interface = -1, + .codec = CODEC_WEBCAM, + .video_channels = 1, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 0, + .tuner = 0, + .tuner_type = 0, + .x_offset = 71, + .y_offset = 18, + .model_string = "Nogatech USB MicroCam PAL (NV3001P)", + }, +}; +const int usbvision_device_data_size = ARRAY_SIZE(usbvision_device_data); + +/* Supported Devices */ + +struct usb_device_id usbvision_table[] = { + { USB_DEVICE(0x0a6f, 0x0400), .driver_info = XANBOO }, + { USB_DEVICE(0x050d, 0x0106), .driver_info = BELKIN_VIDEOBUS_II }, + { USB_DEVICE(0x050d, 0x0207), .driver_info = BELKIN_VIDEOBUS }, + { USB_DEVICE(0x050d, 0x0208), .driver_info = BELKIN_USB_VIDEOBUS_II }, + { USB_DEVICE(0x0571, 0x0002), .driver_info = ECHOFX_INTERVIEW_LITE }, + { USB_DEVICE(0x0573, 0x0003), .driver_info = USBGEAR_USBG_V1 }, + { USB_DEVICE(0x0573, 0x0400), .driver_info = D_LINK_V100 }, + { USB_DEVICE(0x0573, 0x2000), .driver_info = X10_USB_CAMERA }, + { USB_DEVICE(0x0573, 0x2d00), .driver_info = HPG_WINTV_LIVE_PAL_BG }, + { USB_DEVICE(0x0573, 0x2d01), .driver_info = HPG_WINTV_LIVE_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x2101), .driver_info = ZORAN_PMD_NOGATECH }, + { USB_DEVICE(0x0573, 0x3000), .driver_info = MICROCAM_NTSC }, + { USB_DEVICE(0x0573, 0x3001), .driver_info = MICROCAM_PAL }, + { USB_DEVICE(0x0573, 0x4100), .driver_info = NOGATECH_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4110), .driver_info = PNY_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4450), .driver_info = PV_PLAYTV_USB_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4550), .driver_info = ZT_721 }, + { USB_DEVICE(0x0573, 0x4d00), .driver_info = HPG_WINTV_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d01), .driver_info = HPG_WINTV_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d02), .driver_info = HPG_WINTV_PAL_I }, + { USB_DEVICE(0x0573, 0x4d03), .driver_info = HPG_WINTV_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d04), .driver_info = HPG_WINTV_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d10), .driver_info = HPG_WINTV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4d11), .driver_info = HPG_WINTV_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d12), .driver_info = HPG_WINTV_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d14), .driver_info = HPG_WINTV_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d2a), .driver_info = HPG_WINTV_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d2b), .driver_info = HPG_WINTV_PRO_NTSC_MN_V2 }, + { USB_DEVICE(0x0573, 0x4d2c), .driver_info = HPG_WINTV_PRO_PAL }, + { USB_DEVICE(0x0573, 0x4d20), .driver_info = HPG_WINTV_PRO_NTSC_MN_V3 }, + { USB_DEVICE(0x0573, 0x4d21), .driver_info = HPG_WINTV_PRO_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d22), .driver_info = HPG_WINTV_PRO_PAL_I }, + { USB_DEVICE(0x0573, 0x4d23), .driver_info = HPG_WINTV_PRO_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d24), .driver_info = HPG_WINTV_PRO_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d25), .driver_info = HPG_WINTV_PRO_PAL_SECAM }, + { USB_DEVICE(0x0573, 0x4d26), .driver_info = HPG_WINTV_PRO_PAL_SECAM_V2 }, + { USB_DEVICE(0x0573, 0x4d27), .driver_info = HPG_WINTV_PRO_PAL_BG_V2 }, + { USB_DEVICE(0x0573, 0x4d28), .driver_info = HPG_WINTV_PRO_PAL_BG_D_K }, + { USB_DEVICE(0x0573, 0x4d29), .driver_info = HPG_WINTV_PRO_PAL_I_D_K }, + { USB_DEVICE(0x0573, 0x4d30), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM }, + { USB_DEVICE(0x0573, 0x4d31), .driver_info = HPG_WINTV_PRO_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d32), .driver_info = HPG_WINTV_PRO_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d34), .driver_info = HPG_WINTV_PRO_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d35), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d36), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d37), .driver_info = HPG_WINTV_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d38), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM_V2 }, + { USB_DEVICE(0x0768, 0x0006), .driver_info = CAMTEL_TVB330 }, + { USB_DEVICE(0x07d0, 0x0001), .driver_info = DIGITAL_VIDEO_CREATOR_I }, + { USB_DEVICE(0x07d0, 0x0002), .driver_info = GLOBAL_VILLAGE_GV_007_NTSC }, + { USB_DEVICE(0x07d0, 0x0003), .driver_info = DAZZLE_DVC_50_REV_1_NTSC }, + { USB_DEVICE(0x07d0, 0x0004), .driver_info = DAZZLE_DVC_80_REV_1_PAL }, + { USB_DEVICE(0x07d0, 0x0005), .driver_info = DAZZLE_DVC_90_REV_1_SECAM }, + { USB_DEVICE(0x07f8, 0x9104), .driver_info = ESKAPE_LABS_MYTV2GO }, + { USB_DEVICE(0x2304, 0x010d), .driver_info = PINNA_PCTV_USB_PAL }, + { USB_DEVICE(0x2304, 0x0109), .driver_info = PINNA_PCTV_USB_SECAM }, + { USB_DEVICE(0x2304, 0x0110), .driver_info = PINNA_PCTV_USB_PAL_FM }, + { USB_DEVICE(0x2304, 0x0111), .driver_info = MIRO_PCTV_USB }, + { USB_DEVICE(0x2304, 0x0112), .driver_info = PINNA_PCTV_USB_NTSC_FM }, + { USB_DEVICE(0x2304, 0x0113), .driver_info = PINNA_PCTV_USB_NTSC_FM_V3 }, + { USB_DEVICE(0x2304, 0x0210), .driver_info = PINNA_PCTV_USB_PAL_FM_V2 }, + { USB_DEVICE(0x2304, 0x0212), .driver_info = PINNA_PCTV_USB_NTSC_FM_V2 }, + { USB_DEVICE(0x2304, 0x0214), .driver_info = PINNA_PCTV_USB_PAL_FM_V3 }, + { USB_DEVICE(0x2304, 0x0300), .driver_info = PINNA_LINX_VD_IN_CAB_NTSC }, + { USB_DEVICE(0x2304, 0x0301), .driver_info = PINNA_LINX_VD_IN_CAB_PAL }, + { USB_DEVICE(0x2304, 0x0419), .driver_info = PINNA_PCTV_BUNGEE_PAL_FM }, + { USB_DEVICE(0x2400, 0x4200), .driver_info = HPG_WINTV }, + { }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(usb, usbvision_table); diff --git a/drivers/media/usb/usbvision/usbvision-cards.h b/drivers/media/usb/usbvision/usbvision-cards.h new file mode 100644 index 000000000000..a51cc1185cce --- /dev/null +++ b/drivers/media/usb/usbvision/usbvision-cards.h @@ -0,0 +1,69 @@ +#define XANBOO 0 +#define BELKIN_VIDEOBUS_II 1 +#define BELKIN_VIDEOBUS 2 +#define BELKIN_USB_VIDEOBUS_II 3 +#define ECHOFX_INTERVIEW_LITE 4 +#define USBGEAR_USBG_V1 5 +#define D_LINK_V100 6 +#define X10_USB_CAMERA 7 +#define HPG_WINTV_LIVE_PAL_BG 8 +#define HPG_WINTV_LIVE_PRO_NTSC_MN 9 +#define ZORAN_PMD_NOGATECH 10 +#define NOGATECH_USB_TV_NTSC_FM 11 +#define PNY_USB_TV_NTSC_FM 12 +#define PV_PLAYTV_USB_PRO_PAL_FM 13 +#define ZT_721 14 +#define HPG_WINTV_NTSC_MN 15 +#define HPG_WINTV_PAL_BG 16 +#define HPG_WINTV_PAL_I 17 +#define HPG_WINTV_PAL_SECAM_L 18 +#define HPG_WINTV_PAL_D_K 19 +#define HPG_WINTV_NTSC_FM 20 +#define HPG_WINTV_PAL_BG_FM 21 +#define HPG_WINTV_PAL_I_FM 22 +#define HPG_WINTV_PAL_D_K_FM 23 +#define HPG_WINTV_PRO_NTSC_MN 24 +#define HPG_WINTV_PRO_NTSC_MN_V2 25 +#define HPG_WINTV_PRO_PAL 26 +#define HPG_WINTV_PRO_NTSC_MN_V3 27 +#define HPG_WINTV_PRO_PAL_BG 28 +#define HPG_WINTV_PRO_PAL_I 29 +#define HPG_WINTV_PRO_PAL_SECAM_L 30 +#define HPG_WINTV_PRO_PAL_D_K 31 +#define HPG_WINTV_PRO_PAL_SECAM 32 +#define HPG_WINTV_PRO_PAL_SECAM_V2 33 +#define HPG_WINTV_PRO_PAL_BG_V2 34 +#define HPG_WINTV_PRO_PAL_BG_D_K 35 +#define HPG_WINTV_PRO_PAL_I_D_K 36 +#define HPG_WINTV_PRO_NTSC_MN_FM 37 +#define HPG_WINTV_PRO_PAL_BG_FM 38 +#define HPG_WINTV_PRO_PAL_I_FM 39 +#define HPG_WINTV_PRO_PAL_D_K_FM 40 +#define HPG_WINTV_PRO_TEMIC_PAL_FM 41 +#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42 +#define HPG_WINTV_PRO_PAL_FM 43 +#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44 +#define CAMTEL_TVB330 45 +#define DIGITAL_VIDEO_CREATOR_I 46 +#define GLOBAL_VILLAGE_GV_007_NTSC 47 +#define DAZZLE_DVC_50_REV_1_NTSC 48 +#define DAZZLE_DVC_80_REV_1_PAL 49 +#define DAZZLE_DVC_90_REV_1_SECAM 50 +#define ESKAPE_LABS_MYTV2GO 51 +#define PINNA_PCTV_USB_PAL 52 +#define PINNA_PCTV_USB_SECAM 53 +#define PINNA_PCTV_USB_PAL_FM 54 +#define MIRO_PCTV_USB 55 +#define PINNA_PCTV_USB_NTSC_FM 56 +#define PINNA_PCTV_USB_PAL_FM_V2 57 +#define PINNA_PCTV_USB_NTSC_FM_V2 58 +#define PINNA_PCTV_USB_PAL_FM_V3 59 +#define PINNA_LINX_VD_IN_CAB_NTSC 60 +#define PINNA_LINX_VD_IN_CAB_PAL 61 +#define PINNA_PCTV_BUNGEE_PAL_FM 62 +#define HPG_WINTV 63 +#define PINNA_PCTV_USB_NTSC_FM_V3 64 +#define MICROCAM_NTSC 65 +#define MICROCAM_PAL 66 + +extern const int usbvision_device_data_size; diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c new file mode 100644 index 000000000000..c9b2042f8bdf --- /dev/null +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -0,0 +1,2518 @@ +/* + * usbvision-core.c - driver for NT100x USB video capture devices + * + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * Dwaine Garden + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * This program is free software; 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 +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "usbvision.h" + +static unsigned int core_debug; +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); + +static int adjust_compression = 1; /* Set the compression to be adaptive */ +module_param(adjust_compression, int, 0444); +MODULE_PARM_DESC(adjust_compression, " Set the ADPCM compression for the device. Default: 1 (On)"); + +/* To help people with Black and White output with using s-video input. + * Some cables and input device are wired differently. */ +static int switch_svideo_input; +module_param(switch_svideo_input, int, 0444); +MODULE_PARM_DESC(switch_svideo_input, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)"); + +static unsigned int adjust_x_offset = -1; +module_param(adjust_x_offset, int, 0644); +MODULE_PARM_DESC(adjust_x_offset, "adjust X offset display [core]"); + +static unsigned int adjust_y_offset = -1; +module_param(adjust_y_offset, int, 0644); +MODULE_PARM_DESC(adjust_y_offset, "adjust Y offset display [core]"); + + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ + + +#ifdef USBVISION_DEBUG + #define PDEBUG(level, fmt, args...) { \ + if (core_debug & (level)) \ + printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ + __func__, __LINE__ , ## args); \ + } +#else + #define PDEBUG(level, fmt, args...) do {} while (0) +#endif + +#define DBG_HEADER (1 << 0) +#define DBG_IRQ (1 << 1) +#define DBG_ISOC (1 << 2) +#define DBG_PARSE (1 << 3) +#define DBG_SCRATCH (1 << 4) +#define DBG_FUNC (1 << 5) + +static const int max_imgwidth = MAX_FRAME_WIDTH; +static const int max_imgheight = MAX_FRAME_HEIGHT; +static const int min_imgwidth = MIN_FRAME_WIDTH; +static const int min_imgheight = MIN_FRAME_HEIGHT; + +/* The value of 'scratch_buf_size' affects quality of the picture + * in many ways. Shorter buffers may cause loss of data when client + * is too slow. Larger buffers are memory-consuming and take longer + * to work with. This setting can be adjusted, but the default value + * should be OK for most desktop users. + */ +#define DEFAULT_SCRATCH_BUF_SIZE (0x20000) /* 128kB memory scratch buffer */ +static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE; + +/* Function prototypes */ +static int usbvision_request_intra(struct usb_usbvision *usbvision); +static int usbvision_unrequest_intra(struct usb_usbvision *usbvision); +static int usbvision_adjust_compression(struct usb_usbvision *usbvision); +static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision); + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +/* + * Here we want the physical address of the memory. + * This is used when initializing the contents of the area. + */ + +static void *usbvision_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void usbvision_rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + size = PAGE_ALIGN(size); + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vfree(mem); +} + + +#if ENABLE_HEXDUMP +static void usbvision_hexdump(const unsigned char *data, int len) +{ + char tmp[80]; + int i, k; + + for (i = k = 0; len > 0; i++, len--) { + if (i > 0 && (i % 16 == 0)) { + printk("%s\n", tmp); + k = 0; + } + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk(KERN_CONT "%s\n", tmp); +} +#endif + +/******************************** + * scratch ring buffer handling + ********************************/ +static int scratch_len(struct usb_usbvision *usbvision) /* This returns the amount of data actually in the buffer */ +{ + int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr; + + if (len < 0) + len += scratch_buf_size; + PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len); + + return len; +} + + +/* This returns the free space left in the buffer */ +static int scratch_free(struct usb_usbvision *usbvision) +{ + int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr; + if (free <= 0) + free += scratch_buf_size; + if (free) { + free -= 1; /* at least one byte in the buffer must */ + /* left blank, otherwise there is no chance to differ between full and empty */ + } + PDEBUG(DBG_SCRATCH, "return %d\n", free); + + return free; +} + + +/* This puts data into the buffer */ +static int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, + int len) +{ + int len_part; + + if (usbvision->scratch_write_ptr + len < scratch_buf_size) { + memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len); + usbvision->scratch_write_ptr += len; + } else { + len_part = scratch_buf_size - usbvision->scratch_write_ptr; + memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part); + if (len == len_part) { + usbvision->scratch_write_ptr = 0; /* just set write_ptr to zero */ + } else { + memcpy(usbvision->scratch, data + len_part, len - len_part); + usbvision->scratch_write_ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr); + + return len; +} + +/* This marks the write_ptr as position of new frame header */ +static void scratch_mark_header(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr); + + usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] = + usbvision->scratch_write_ptr; + usbvision->scratch_headermarker_write_ptr += 1; + usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER; +} + +/* This gets data from the buffer at the given "ptr" position */ +static int scratch_get_extra(struct usb_usbvision *usbvision, + unsigned char *data, int *ptr, int len) +{ + int len_part; + + if (*ptr + len < scratch_buf_size) { + memcpy(data, usbvision->scratch + *ptr, len); + *ptr += len; + } else { + len_part = scratch_buf_size - *ptr; + memcpy(data, usbvision->scratch + *ptr, len_part); + if (len == len_part) { + *ptr = 0; /* just set the y_ptr to zero */ + } else { + memcpy(data + len_part, usbvision->scratch, len - len_part); + *ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr); + + return len; +} + + +/* This sets the scratch extra read pointer */ +static void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr, + int len) +{ + *ptr = (usbvision->scratch_read_ptr + len) % scratch_buf_size; + + PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); +} + + +/* This increments the scratch extra read pointer */ +static void scratch_inc_extra_ptr(int *ptr, int len) +{ + *ptr = (*ptr + len) % scratch_buf_size; + + PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); +} + + +/* This gets data from the buffer */ +static int scratch_get(struct usb_usbvision *usbvision, unsigned char *data, + int len) +{ + int len_part; + + if (usbvision->scratch_read_ptr + len < scratch_buf_size) { + memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len); + usbvision->scratch_read_ptr += len; + } else { + len_part = scratch_buf_size - usbvision->scratch_read_ptr; + memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part); + if (len == len_part) { + usbvision->scratch_read_ptr = 0; /* just set the read_ptr to zero */ + } else { + memcpy(data + len_part, usbvision->scratch, len - len_part); + usbvision->scratch_read_ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr); + + return len; +} + + +/* This sets read pointer to next header and returns it */ +static int scratch_get_header(struct usb_usbvision *usbvision, + struct usbvision_frame_header *header) +{ + int err_code = 0; + + PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr); + + while (usbvision->scratch_headermarker_write_ptr - + usbvision->scratch_headermarker_read_ptr != 0) { + usbvision->scratch_read_ptr = + usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr]; + usbvision->scratch_headermarker_read_ptr += 1; + usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER; + scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH); + if ((header->magic_1 == USBVISION_MAGIC_1) + && (header->magic_2 == USBVISION_MAGIC_2) + && (header->header_length == USBVISION_HEADER_LENGTH)) { + err_code = USBVISION_HEADER_LENGTH; + header->frame_width = header->frame_width_lo + (header->frame_width_hi << 8); + header->frame_height = header->frame_height_lo + (header->frame_height_hi << 8); + break; + } + } + + return err_code; +} + + +/* This removes len bytes of old data from the buffer */ +static void scratch_rm_old(struct usb_usbvision *usbvision, int len) +{ + usbvision->scratch_read_ptr += len; + usbvision->scratch_read_ptr %= scratch_buf_size; + PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr); +} + + +/* This resets the buffer - kills all data in it too */ +static void scratch_reset(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_SCRATCH, "\n"); + + usbvision->scratch_read_ptr = 0; + usbvision->scratch_write_ptr = 0; + usbvision->scratch_headermarker_read_ptr = 0; + usbvision->scratch_headermarker_write_ptr = 0; + usbvision->isocstate = isoc_state_no_frame; +} + +int usbvision_scratch_alloc(struct usb_usbvision *usbvision) +{ + usbvision->scratch = vmalloc_32(scratch_buf_size); + scratch_reset(usbvision); + if (usbvision->scratch == NULL) { + dev_err(&usbvision->dev->dev, + "%s: unable to allocate %d bytes for scratch\n", + __func__, scratch_buf_size); + return -ENOMEM; + } + return 0; +} + +void usbvision_scratch_free(struct usb_usbvision *usbvision) +{ + vfree(usbvision->scratch); + usbvision->scratch = NULL; +} + +/* + * usbvision_decompress_alloc() + * + * allocates intermediate buffer for decompression + */ +int usbvision_decompress_alloc(struct usb_usbvision *usbvision) +{ + int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; + + usbvision->intra_frame_buffer = vmalloc_32(IFB_size); + if (usbvision->intra_frame_buffer == NULL) { + dev_err(&usbvision->dev->dev, + "%s: unable to allocate %d for compr. frame buffer\n", + __func__, IFB_size); + return -ENOMEM; + } + return 0; +} + +/* + * usbvision_decompress_free() + * + * frees intermediate buffer for decompression + */ +void usbvision_decompress_free(struct usb_usbvision *usbvision) +{ + vfree(usbvision->intra_frame_buffer); + usbvision->intra_frame_buffer = NULL; + +} + +/************************************************************ + * Here comes the data parsing stuff that is run as interrupt + ************************************************************/ +/* + * usbvision_find_header() + * + * Locate one of supported header markers in the scratch buffer. + */ +static enum parse_state usbvision_find_header(struct usb_usbvision *usbvision) +{ + struct usbvision_frame *frame; + int found_header = 0; + + frame = usbvision->cur_frame; + + while (scratch_get_header(usbvision, &frame->isoc_header) == USBVISION_HEADER_LENGTH) { + /* found header in scratch */ + PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u", + frame->isoc_header.magic_2, + frame->isoc_header.magic_1, + frame->isoc_header.header_length, + frame->isoc_header.frame_num, + frame->isoc_header.frame_phase, + frame->isoc_header.frame_latency, + frame->isoc_header.data_format, + frame->isoc_header.format_param, + frame->isoc_header.frame_width, + frame->isoc_header.frame_height); + + if (usbvision->request_intra) { + if (frame->isoc_header.format_param & 0x80) { + found_header = 1; + usbvision->last_isoc_frame_num = -1; /* do not check for lost frames this time */ + usbvision_unrequest_intra(usbvision); + break; + } + } else { + found_header = 1; + break; + } + } + + if (found_header) { + frame->frmwidth = frame->isoc_header.frame_width * usbvision->stretch_width; + frame->frmheight = frame->isoc_header.frame_height * usbvision->stretch_height; + frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth) >> 3; + } else { /* no header found */ + PDEBUG(DBG_HEADER, "skipping scratch data, no header"); + scratch_reset(usbvision); + return parse_state_end_parse; + } + + /* found header */ + if (frame->isoc_header.data_format == ISOC_MODE_COMPRESS) { + /* check isoc_header.frame_num for lost frames */ + if (usbvision->last_isoc_frame_num >= 0) { + if (((usbvision->last_isoc_frame_num + 1) % 32) != frame->isoc_header.frame_num) { + /* unexpected frame drop: need to request new intra frame */ + PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isoc_header.frame_num); + usbvision_request_intra(usbvision); + return parse_state_next_frame; + } + } + usbvision->last_isoc_frame_num = frame->isoc_header.frame_num; + } + usbvision->header_count++; + frame->scanstate = scan_state_lines; + frame->curline = 0; + + return parse_state_continue; +} + +static enum parse_state usbvision_parse_lines_422(struct usb_usbvision *usbvision, + long *pcopylen) +{ + volatile struct usbvision_frame *frame; + unsigned char *f; + int len; + int i; + unsigned char yuyv[4] = { 180, 128, 10, 128 }; /* YUV components */ + unsigned char rv, gv, bv; /* RGB components */ + int clipmask_index, bytes_per_pixel; + int stretch_bytes, clipmask_add; + + frame = usbvision->cur_frame; + f = frame->data + (frame->v4l2_linesize * frame->curline); + + /* Make sure there's enough data for the entire line */ + len = (frame->isoc_header.frame_width * 2) + 5; + if (scratch_len(usbvision) < len) { + PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len); + return parse_state_out; + } + + if ((frame->curline + 1) >= frame->frmheight) + return parse_state_next_frame; + + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; + clipmask_index = frame->curline * MAX_FRAME_WIDTH; + clipmask_add = usbvision->stretch_width; + + for (i = 0; i < frame->frmwidth; i += (2 * usbvision->stretch_width)) { + scratch_get(usbvision, &yuyv[0], 4); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = yuyv[0]; /* Y */ + *f++ = yuyv[3]; /* U */ + } else { + YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x07 & (gv >> 3)) | + (0xF8 & bv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = rv; + *f++ = gv; + *f++ = bv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = rv; + *f++ = gv; + *f++ = bv; + f++; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x03 & (gv >> 3)) | + (0x7C & (bv << 2)); + break; + } + } + clipmask_index += clipmask_add; + f += stretch_bytes; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = yuyv[2]; /* Y */ + *f++ = yuyv[1]; /* V */ + } else { + YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x07 & (gv >> 3)) | + (0xF8 & bv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = rv; + *f++ = gv; + *f++ = bv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = rv; + *f++ = gv; + *f++ = bv; + f++; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x03 & (gv >> 3)) | + (0x7C & (bv << 2)); + break; + } + } + clipmask_index += clipmask_add; + f += stretch_bytes; + } + + frame->curline += usbvision->stretch_height; + *pcopylen += frame->v4l2_linesize * usbvision->stretch_height; + + if (frame->curline >= frame->frmheight) + return parse_state_next_frame; + return parse_state_continue; +} + +/* The decompression routine */ +static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char *compressed, + unsigned char *decompressed, int *start_pos, + int *block_typestart_pos, int len) +{ + int rest_pixel, idx, pos, extra_pos, block_len, block_type_pos, block_type_len; + unsigned char block_byte, block_code, block_type, block_type_byte, integrator; + + integrator = 0; + pos = *start_pos; + block_type_pos = *block_typestart_pos; + extra_pos = pos; + block_len = 0; + block_byte = 0; + block_code = 0; + block_type = 0; + block_type_byte = 0; + block_type_len = 0; + rest_pixel = len; + + for (idx = 0; idx < len; idx++) { + if (block_len == 0) { + if (block_type_len == 0) { + block_type_byte = compressed[block_type_pos]; + block_type_pos++; + block_type_len = 4; + } + block_type = (block_type_byte & 0xC0) >> 6; + + /* statistic: */ + usbvision->compr_block_types[block_type]++; + + pos = extra_pos; + if (block_type == 0) { + if (rest_pixel >= 24) { + idx += 23; + rest_pixel -= 24; + integrator = decompressed[idx]; + } else { + idx += rest_pixel - 1; + rest_pixel = 0; + } + } else { + block_code = compressed[pos]; + pos++; + if (rest_pixel >= 24) + block_len = 24; + else + block_len = rest_pixel; + rest_pixel -= block_len; + extra_pos = pos + (block_len / 4); + } + block_type_byte <<= 2; + block_type_len -= 1; + } + if (block_len > 0) { + if ((block_len % 4) == 0) { + block_byte = compressed[pos]; + pos++; + } + if (block_type == 1) /* inter Block */ + integrator = decompressed[idx]; + switch (block_byte & 0xC0) { + case 0x03 << 6: + integrator += compressed[extra_pos]; + extra_pos++; + break; + case 0x02 << 6: + integrator += block_code; + break; + case 0x00: + integrator -= block_code; + break; + } + decompressed[idx] = integrator; + block_byte <<= 2; + block_len -= 1; + } + } + *start_pos = extra_pos; + *block_typestart_pos = block_type_pos; + return idx; +} + + +/* + * usbvision_parse_compress() + * + * Parse compressed frame from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + */ +static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision, + long *pcopylen) +{ +#define USBVISION_STRIP_MAGIC 0x5A +#define USBVISION_STRIP_LEN_MAX 400 +#define USBVISION_STRIP_HEADER_LEN 3 + + struct usbvision_frame *frame; + unsigned char *f, *u = NULL, *v = NULL; + unsigned char strip_data[USBVISION_STRIP_LEN_MAX]; + unsigned char strip_header[USBVISION_STRIP_HEADER_LEN]; + int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos; + int clipmask_index; + int image_size; + unsigned char rv, gv, bv; + static unsigned char *Y, *U, *V; + + frame = usbvision->cur_frame; + image_size = frame->frmwidth * frame->frmheight; + if ((frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || + (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420)) { /* this is a planar format */ + /* ... v4l2_linesize not used here. */ + f = frame->data + (frame->width * frame->curline); + } else + f = frame->data + (frame->v4l2_linesize * frame->curline); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { /* initialise u and v pointers */ + /* get base of u and b planes add halfoffset */ + u = frame->data + + image_size + + (frame->frmwidth >> 1) * frame->curline; + v = u + (image_size >> 1); + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { + v = frame->data + image_size + ((frame->curline * (frame->width)) >> 2); + u = v + (image_size >> 2); + } + + if (frame->curline == 0) + usbvision_adjust_compression(usbvision); + + if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) + return parse_state_out; + + /* get strip header without changing the scratch_read_ptr */ + scratch_set_extra_ptr(usbvision, &strip_ptr, 0); + scratch_get_extra(usbvision, &strip_header[0], &strip_ptr, + USBVISION_STRIP_HEADER_LEN); + + if (strip_header[0] != USBVISION_STRIP_MAGIC) { + /* wrong strip magic */ + usbvision->strip_magic_errors++; + return parse_state_next_frame; + } + + if (frame->curline != (int)strip_header[2]) { + /* line number mismatch error */ + usbvision->strip_line_number_errors++; + } + + strip_len = 2 * (unsigned int)strip_header[1]; + if (strip_len > USBVISION_STRIP_LEN_MAX) { + /* strip overrun */ + /* I think this never happens */ + usbvision_request_intra(usbvision); + } + + if (scratch_len(usbvision) < strip_len) { + /* there is not enough data for the strip */ + return parse_state_out; + } + + if (usbvision->intra_frame_buffer) { + Y = usbvision->intra_frame_buffer + frame->frmwidth * frame->curline; + U = usbvision->intra_frame_buffer + image_size + (frame->frmwidth / 2) * (frame->curline / 2); + V = usbvision->intra_frame_buffer + image_size / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2); + } else { + return parse_state_next_frame; + } + + clipmask_index = frame->curline * MAX_FRAME_WIDTH; + + scratch_get(usbvision, strip_data, strip_len); + + idx_end = frame->frmwidth; + block_type_pos = USBVISION_STRIP_HEADER_LEN; + startblock_pos = block_type_pos + (idx_end - 1) / 96 + (idx_end / 2 - 1) / 96 + 2; + block_pos = startblock_pos; + + usbvision->block_pos = block_pos; + + usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); + if (strip_len > usbvision->max_strip_len) + usbvision->max_strip_len = strip_len; + + if (frame->curline % 2) + usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); + else + usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); + + if (block_pos > usbvision->comprblock_pos) + usbvision->comprblock_pos = block_pos; + if (block_pos > strip_len) + usbvision->strip_len_errors++; + + for (idx = 0; idx < idx_end; idx++) { + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = Y[idx]; + *f++ = idx & 0x01 ? U[idx / 2] : V[idx / 2]; + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) { + *f++ = Y[idx]; + if (idx & 0x01) + *u++ = U[idx >> 1]; + else + *v++ = V[idx >> 1]; + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { + *f++ = Y[idx]; + if (!((idx & 0x01) | (frame->curline & 0x01))) { + /* only need do this for 1 in 4 pixels */ + /* intraframe buffer is YUV420 format */ + *u++ = U[idx >> 1]; + *v++ = V[idx >> 1]; + } + } else { + YUV_TO_RGB_BY_THE_BOOK(Y[idx], U[idx / 2], V[idx / 2], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_GREY: + *f++ = Y[idx]; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x03 & (gv >> 3)) | + (0x7C & (bv << 2)); + break; + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x07 & (gv >> 3)) | + (0xF8 & bv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = rv; + *f++ = gv; + *f++ = bv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = rv; + *f++ = gv; + *f++ = bv; + f++; + break; + } + } + clipmask_index++; + } + /* Deal with non-integer no. of bytes for YUV420P */ + if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420) + *pcopylen += frame->v4l2_linesize; + else + *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1; + + frame->curline += 1; + + if (frame->curline >= frame->frmheight) + return parse_state_next_frame; + return parse_state_continue; + +} + + +/* + * usbvision_parse_lines_420() + * + * Parse two lines from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + */ +static enum parse_state usbvision_parse_lines_420(struct usb_usbvision *usbvision, + long *pcopylen) +{ + struct usbvision_frame *frame; + unsigned char *f_even = NULL, *f_odd = NULL; + unsigned int pixel_per_line, block; + int pixel, block_split; + int y_ptr, u_ptr, v_ptr, y_odd_offset; + const int y_block_size = 128; + const int uv_block_size = 64; + const int sub_block_size = 32; + const int y_step[] = { 0, 0, 0, 2 }, y_step_size = 4; + const int uv_step[] = { 0, 0, 0, 4 }, uv_step_size = 4; + unsigned char y[2], u, v; /* YUV components */ + int y_, u_, v_, vb, uvg, ur; + int r_, g_, b_; /* RGB components */ + unsigned char g; + int clipmask_even_index, clipmask_odd_index, bytes_per_pixel; + int clipmask_add, stretch_bytes; + + frame = usbvision->cur_frame; + f_even = frame->data + (frame->v4l2_linesize * frame->curline); + f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height; + + /* Make sure there's enough data for the entire line */ + /* In this mode usbvision transfer 3 bytes for every 2 pixels */ + /* I need two lines to decode the color */ + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; + clipmask_even_index = frame->curline * MAX_FRAME_WIDTH; + clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH; + clipmask_add = usbvision->stretch_width; + pixel_per_line = frame->isoc_header.frame_width; + + if (scratch_len(usbvision) < (int)pixel_per_line * 3) { + /* printk(KERN_DEBUG "out of data, need %d\n", len); */ + return parse_state_out; + } + + if ((frame->curline + 1) >= frame->frmheight) + return parse_state_next_frame; + + block_split = (pixel_per_line%y_block_size) ? 1 : 0; /* are some blocks splitted into different lines? */ + + y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size) + + block_split * uv_block_size; + + scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset); + scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size); + scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset + + (4 - block_split) * sub_block_size); + + for (block = 0; block < (pixel_per_line / sub_block_size); block++) { + for (pixel = 0; pixel < sub_block_size; pixel += 2) { + scratch_get(usbvision, &y[0], 2); + scratch_get_extra(usbvision, &u, &u_ptr, 1); + scratch_get_extra(usbvision, &v, &v_ptr, 1); + + /* I don't use the YUV_TO_RGB macro for better performance */ + v_ = v - 128; + u_ = u - 128; + vb = 132252 * v_; + uvg = -53281 * u_ - 25625 * v_; + ur = 104595 * u_; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_even++ = y[0]; + *f_even++ = v; + } else { + y_ = 76284 * (y[0] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_even++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + f_even++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_even_index += clipmask_add; + f_even += stretch_bytes; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_even++ = y[1]; + *f_even++ = u; + } else { + y_ = 76284 * (y[1] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_even++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + f_even++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_even_index += clipmask_add; + f_even += stretch_bytes; + + scratch_get_extra(usbvision, &y[0], &y_ptr, 2); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_odd++ = y[0]; + *f_odd++ = v; + } else { + y_ = 76284 * (y[0] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_odd++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + f_odd++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_odd_index += clipmask_add; + f_odd += stretch_bytes; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_odd++ = y[1]; + *f_odd++ = u; + } else { + y_ = 76284 * (y[1] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_odd++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + f_odd++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_odd_index += clipmask_add; + f_odd += stretch_bytes; + } + + scratch_rm_old(usbvision, y_step[block % y_step_size] * sub_block_size); + scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size] + * sub_block_size); + scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size] + * sub_block_size); + scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size] + * sub_block_size); + } + + scratch_rm_old(usbvision, pixel_per_line * 3 / 2 + + block_split * sub_block_size); + + frame->curline += 2 * usbvision->stretch_height; + *pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height; + + if (frame->curline >= frame->frmheight) + return parse_state_next_frame; + return parse_state_continue; +} + +/* + * usbvision_parse_data() + * + * Generic routine to parse the scratch buffer. It employs either + * usbvision_find_header() or usbvision_parse_lines() to do most + * of work. + * + */ +static void usbvision_parse_data(struct usb_usbvision *usbvision) +{ + struct usbvision_frame *frame; + enum parse_state newstate; + long copylen = 0; + unsigned long lock_flags; + + frame = usbvision->cur_frame; + + PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision)); + + while (1) { + newstate = parse_state_out; + if (scratch_len(usbvision)) { + if (frame->scanstate == scan_state_scanning) { + newstate = usbvision_find_header(usbvision); + } else if (frame->scanstate == scan_state_lines) { + if (usbvision->isoc_mode == ISOC_MODE_YUV420) + newstate = usbvision_parse_lines_420(usbvision, ©len); + else if (usbvision->isoc_mode == ISOC_MODE_YUV422) + newstate = usbvision_parse_lines_422(usbvision, ©len); + else if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) + newstate = usbvision_parse_compress(usbvision, ©len); + } + } + if (newstate == parse_state_continue) + continue; + if ((newstate == parse_state_next_frame) || (newstate == parse_state_out)) + break; + return; /* parse_state_end_parse */ + } + + if (newstate == parse_state_next_frame) { + frame->grabstate = frame_state_done; + do_gettimeofday(&(frame->timestamp)); + frame->sequence = usbvision->frame_num; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_move_tail(&(frame->frame), &usbvision->outqueue); + usbvision->cur_frame = NULL; + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + usbvision->frame_num++; + + /* This will cause the process to request another frame. */ + if (waitqueue_active(&usbvision->wait_frame)) { + PDEBUG(DBG_PARSE, "Wake up !"); + wake_up_interruptible(&usbvision->wait_frame); + } + } else { + frame->grabstate = frame_state_grabbing; + } + + /* Update the frame's uncompressed length. */ + frame->scanlength += copylen; +} + + +/* + * Make all of the blocks of data contiguous + */ +static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, + struct urb *urb) +{ + unsigned char *packet_data; + int i, totlen = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + int packet_len = urb->iso_frame_desc[i].actual_length; + int packet_stat = urb->iso_frame_desc[i].status; + + packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (packet_stat) { /* packet_stat != 0 ????????????? */ + PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat); + usbvision->isoc_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (packet_len < 0) { + PDEBUG(DBG_ISOC, "error packet [%d]", i); + usbvision->isoc_skip_count++; + continue; + } else if (packet_len == 0) { /* Frame end ????? */ + PDEBUG(DBG_ISOC, "null packet [%d]", i); + usbvision->isocstate = isoc_state_no_frame; + usbvision->isoc_skip_count++; + continue; + } else if (packet_len > usbvision->isoc_packet_size) { + PDEBUG(DBG_ISOC, "packet[%d] > isoc_packet_size", i); + usbvision->isoc_skip_count++; + continue; + } + + PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len); + + if (usbvision->isocstate == isoc_state_no_frame) { /* new frame begins */ + usbvision->isocstate = isoc_state_in_frame; + scratch_mark_header(usbvision); + usbvision_measure_bandwidth(usbvision); + PDEBUG(DBG_ISOC, "packet with header"); + } + + /* + * If usbvision continues to feed us with data but there is no + * consumption (if, for example, V4L client fell asleep) we + * may overflow the buffer. We have to move old data over to + * free room for new data. This is bad for old data. If we + * just drop new data then it's bad for new data... choose + * your favorite evil here. + */ + if (scratch_free(usbvision) < packet_len) { + usbvision->scratch_ovf_count++; + PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d", + scratch_len(usbvision), packet_len); + scratch_rm_old(usbvision, packet_len - scratch_free(usbvision)); + } + + /* Now we know that there is enough room in scratch buffer */ + scratch_put(usbvision, packet_data, packet_len); + totlen += packet_len; + usbvision->isoc_data_count += packet_len; + usbvision->isoc_packet_count++; + } +#if ENABLE_HEXDUMP + if (totlen > 0) { + static int foo; + + if (foo < 1) { + printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen); + usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen); + ++foo; + } + } +#endif + return totlen; +} + +static void usbvision_isoc_irq(struct urb *urb) +{ + int err_code = 0; + int len; + struct usb_usbvision *usbvision = urb->context; + int i; + unsigned long start_time = jiffies; + struct usbvision_frame **f; + + /* We don't want to do anything if we are about to be removed! */ + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return; + + /* any urb with wrong status is ignored without acknowledgement */ + if (urb->status == -ENOENT) + return; + + f = &usbvision->cur_frame; + + /* Manage streaming interruption */ + if (usbvision->streaming == stream_interrupt) { + usbvision->streaming = stream_idle; + if ((*f)) { + (*f)->grabstate = frame_state_ready; + (*f)->scanstate = scan_state_scanning; + } + PDEBUG(DBG_IRQ, "stream interrupted"); + wake_up_interruptible(&usbvision->wait_stream); + } + + /* Copy the data received into our scratch buffer */ + len = usbvision_compress_isochronous(usbvision, urb); + + usbvision->isoc_urb_count++; + usbvision->urb_length = len; + + if (usbvision->streaming == stream_on) { + /* If we collected enough data let's parse! */ + if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH && + !list_empty(&(usbvision->inqueue))) { + if (!(*f)) { + (*f) = list_entry(usbvision->inqueue.next, + struct usbvision_frame, + frame); + } + usbvision_parse_data(usbvision); + } else { + /* If we don't have a frame + we're current working on, complain */ + PDEBUG(DBG_IRQ, + "received data, but no one needs it"); + scratch_reset(usbvision); + } + } else { + PDEBUG(DBG_IRQ, "received data, but no one needs it"); + scratch_reset(usbvision); + } + + usbvision->time_in_irq += jiffies - start_time; + + for (i = 0; i < USBVISION_URB_FRAMES; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = 0; + urb->dev = usbvision->dev; + err_code = usb_submit_urb(urb, GFP_ATOMIC); + + if (err_code) { + dev_err(&usbvision->dev->dev, + "%s: usb_submit_urb failed: error %d\n", + __func__, err_code); + } + + return; +} + +/*************************************/ +/* Low level usbvision access functions */ +/*************************************/ + +/* + * usbvision_read_reg() + * + * return < 0 -> Error + * >= 0 -> Data + */ + +int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) +{ + int err_code = 0; + unsigned char buffer[1]; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -1; + + err_code = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0, (__u16) reg, buffer, 1, HZ); + + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s: failed: error %d\n", __func__, err_code); + return err_code; + } + return buffer[0]; +} + +/* + * usbvision_write_reg() + * + * return 1 -> Reg written + * 0 -> usbvision is not yet ready + * -1 -> Something went wrong + */ + +int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, + unsigned char value) +{ + int err_code = 0; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ); + + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s: failed: error %d\n", __func__, err_code); + } + return err_code; +} + + +static void usbvision_ctrl_urb_complete(struct urb *urb) +{ + struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context; + + PDEBUG(DBG_IRQ, ""); + usbvision->ctrl_urb_busy = 0; + if (waitqueue_active(&usbvision->ctrl_urb_wq)) + wake_up_interruptible(&usbvision->ctrl_urb_wq); +} + + +static int usbvision_write_reg_irq(struct usb_usbvision *usbvision, int address, + unsigned char *data, int len) +{ + int err_code = 0; + + PDEBUG(DBG_IRQ, ""); + if (len > 8) + return -EFAULT; + if (usbvision->ctrl_urb_busy) + return -EBUSY; + usbvision->ctrl_urb_busy = 1; + + usbvision->ctrl_urb_setup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + usbvision->ctrl_urb_setup.bRequest = USBVISION_OP_CODE; + usbvision->ctrl_urb_setup.wValue = 0; + usbvision->ctrl_urb_setup.wIndex = cpu_to_le16(address); + usbvision->ctrl_urb_setup.wLength = cpu_to_le16(len); + usb_fill_control_urb(usbvision->ctrl_urb, usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + (unsigned char *)&usbvision->ctrl_urb_setup, + (void *)usbvision->ctrl_urb_buffer, len, + usbvision_ctrl_urb_complete, + (void *)usbvision); + + memcpy(usbvision->ctrl_urb_buffer, data, len); + + err_code = usb_submit_urb(usbvision->ctrl_urb, GFP_ATOMIC); + if (err_code < 0) { + /* error in usb_submit_urb() */ + usbvision->ctrl_urb_busy = 0; + } + PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, err_code); + return err_code; +} + + +static int usbvision_init_compression(struct usb_usbvision *usbvision) +{ + int err_code = 0; + + usbvision->last_isoc_frame_num = -1; + usbvision->isoc_data_count = 0; + usbvision->isoc_packet_count = 0; + usbvision->isoc_skip_count = 0; + usbvision->compr_level = 50; + usbvision->last_compr_level = -1; + usbvision->isoc_urb_count = 0; + usbvision->request_intra = 1; + usbvision->isoc_measure_bandwidth_count = 0; + + return err_code; +} + +/* this function measures the used bandwidth since last call + * return: 0 : no error + * sets used_bandwidth to 1-100 : 1-100% of full bandwidth resp. to isoc_packet_size + */ +static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision) +{ + int err_code = 0; + + if (usbvision->isoc_measure_bandwidth_count < 2) { /* this gives an average bandwidth of 3 frames */ + usbvision->isoc_measure_bandwidth_count++; + return err_code; + } + if ((usbvision->isoc_packet_size > 0) && (usbvision->isoc_packet_count > 0)) { + usbvision->used_bandwidth = usbvision->isoc_data_count / + (usbvision->isoc_packet_count + usbvision->isoc_skip_count) * + 100 / usbvision->isoc_packet_size; + } + usbvision->isoc_measure_bandwidth_count = 0; + usbvision->isoc_data_count = 0; + usbvision->isoc_packet_count = 0; + usbvision->isoc_skip_count = 0; + return err_code; +} + +static int usbvision_adjust_compression(struct usb_usbvision *usbvision) +{ + int err_code = 0; + unsigned char buffer[6]; + + PDEBUG(DBG_IRQ, ""); + if ((adjust_compression) && (usbvision->used_bandwidth > 0)) { + usbvision->compr_level += (usbvision->used_bandwidth - 90) / 2; + RESTRICT_TO_RANGE(usbvision->compr_level, 0, 100); + if (usbvision->compr_level != usbvision->last_compr_level) { + int distortion; + + if (usbvision->bridge_type == BRIDGE_NT1004 || usbvision->bridge_type == BRIDGE_NT1005) { + buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM Threshold 1 */ + buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM Threshold 2 */ + distortion = 7 + 248 * usbvision->compr_level / 100; + buffer[2] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (inter) */ + buffer[3] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (intra) */ + distortion = 1 + 42 * usbvision->compr_level / 100; + buffer[4] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (inter) */ + buffer[5] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (intra) */ + } else { /* BRIDGE_NT1003 */ + buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM threshold 1 */ + buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM threshold 2 */ + distortion = 2 + 253 * usbvision->compr_level / 100; + buffer[2] = (unsigned char)(distortion & 0xFF); /* distortion threshold bit0-7 */ + buffer[3] = 0; /* (unsigned char)((distortion >> 8) & 0x0F); distortion threshold bit 8-11 */ + distortion = 0 + 43 * usbvision->compr_level / 100; + buffer[4] = (unsigned char)(distortion & 0xFF); /* maximum distortion bit0-7 */ + buffer[5] = 0; /* (unsigned char)((distortion >> 8) & 0x01); maximum distortion bit 8 */ + } + err_code = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6); + if (err_code == 0) { + PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0], + buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); + usbvision->last_compr_level = usbvision->compr_level; + } + } + } + return err_code; +} + +static int usbvision_request_intra(struct usb_usbvision *usbvision) +{ + int err_code = 0; + unsigned char buffer[1]; + + PDEBUG(DBG_IRQ, ""); + usbvision->request_intra = 1; + buffer[0] = 1; + usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); + return err_code; +} + +static int usbvision_unrequest_intra(struct usb_usbvision *usbvision) +{ + int err_code = 0; + unsigned char buffer[1]; + + PDEBUG(DBG_IRQ, ""); + usbvision->request_intra = 0; + buffer[0] = 0; + usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); + return err_code; +} + +/******************************* + * usbvision utility functions + *******************************/ + +int usbvision_power_off(struct usb_usbvision *usbvision) +{ + int err_code = 0; + + PDEBUG(DBG_FUNC, ""); + + err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); + if (err_code == 1) + usbvision->power = 0; + PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code != 1) ? "ERROR" : "power is off", err_code); + return err_code; +} + +/* configure webcam image sensor using the serial port */ +static int usbvision_init_webcam(struct usb_usbvision *usbvision) +{ + int rc; + int i; + static char init_values[38][3] = { + { 0x04, 0x12, 0x08 }, { 0x05, 0xff, 0xc8 }, { 0x06, 0x18, 0x07 }, { 0x07, 0x90, 0x00 }, + { 0x09, 0x00, 0x00 }, { 0x0a, 0x00, 0x00 }, { 0x0b, 0x08, 0x00 }, { 0x0d, 0xcc, 0xcc }, + { 0x0e, 0x13, 0x14 }, { 0x10, 0x9b, 0x83 }, { 0x11, 0x5a, 0x3f }, { 0x12, 0xe4, 0x73 }, + { 0x13, 0x88, 0x84 }, { 0x14, 0x89, 0x80 }, { 0x15, 0x00, 0x20 }, { 0x16, 0x00, 0x00 }, + { 0x17, 0xff, 0xa0 }, { 0x18, 0x6b, 0x20 }, { 0x19, 0x22, 0x40 }, { 0x1a, 0x10, 0x07 }, + { 0x1b, 0x00, 0x47 }, { 0x1c, 0x03, 0xe0 }, { 0x1d, 0x00, 0x00 }, { 0x1e, 0x00, 0x00 }, + { 0x1f, 0x00, 0x00 }, { 0x20, 0x00, 0x00 }, { 0x21, 0x00, 0x00 }, { 0x22, 0x00, 0x00 }, + { 0x23, 0x00, 0x00 }, { 0x24, 0x00, 0x00 }, { 0x25, 0x00, 0x00 }, { 0x26, 0x00, 0x00 }, + { 0x27, 0x00, 0x00 }, { 0x28, 0x00, 0x00 }, { 0x29, 0x00, 0x00 }, { 0x08, 0x80, 0x60 }, + { 0x0f, 0x2d, 0x24 }, { 0x0c, 0x80, 0x80 } + }; + char value[3]; + + /* the only difference between PAL and NTSC init_values */ + if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_NTSC) + init_values[4][1] = 0x34; + + for (i = 0; i < sizeof(init_values) / 3; i++) { + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); + memcpy(value, init_values[i], 3); + rc = usb_control_msg(usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_SER_DAT1, value, + 3, HZ); + if (rc < 0) + return rc; + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SIO); + /* write 3 bytes to the serial port using SIO mode */ + usbvision_write_reg(usbvision, USBVISION_SER_CONT, 3 | 0x10); + usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); + usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_IO_2); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_DAT_IO); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT | USBVISION_DAT_IO); + } + + return 0; +} + +/* + * usbvision_set_video_format() + * + */ +static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format) +{ + static const char proc[] = "usbvision_set_video_format"; + int rc; + unsigned char value[2]; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + PDEBUG(DBG_FUNC, "isoc_mode %#02x", format); + + if ((format != ISOC_MODE_YUV422) + && (format != ISOC_MODE_YUV420) + && (format != ISOC_MODE_COMPRESS)) { + printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420", + format); + format = ISOC_MODE_YUV420; + } + value[0] = 0x0A; /* TODO: See the effect of the filter */ + value[1] = format; /* Sets the VO_MODE register which follows FILT_CONT */ + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_FILT_CONT, value, 2, HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + } + usbvision->isoc_mode = format; + return rc; +} + +/* + * usbvision_set_output() + * + */ + +int usbvision_set_output(struct usb_usbvision *usbvision, int width, + int height) +{ + int err_code = 0; + int usb_width, usb_height; + unsigned int frame_rate = 0, frame_drop = 0; + unsigned char value[4]; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + if (width > MAX_USB_WIDTH) { + usb_width = width / 2; + usbvision->stretch_width = 2; + } else { + usb_width = width; + usbvision->stretch_width = 1; + } + + if (height > MAX_USB_HEIGHT) { + usb_height = height / 2; + usbvision->stretch_height = 2; + } else { + usb_height = height; + usbvision->stretch_height = 1; + } + + RESTRICT_TO_RANGE(usb_width, MIN_FRAME_WIDTH, MAX_USB_WIDTH); + usb_width &= ~(MIN_FRAME_WIDTH-1); + RESTRICT_TO_RANGE(usb_height, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT); + usb_height &= ~(1); + + PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d", + usb_width, usb_height, width, height, + usbvision->stretch_width, usbvision->stretch_height); + + /* I'll not rewrite the same values */ + if ((usb_width != usbvision->curwidth) || (usb_height != usbvision->curheight)) { + value[0] = usb_width & 0xff; /* LSB */ + value[1] = (usb_width >> 8) & 0x03; /* MSB */ + value[2] = usb_height & 0xff; /* LSB */ + value[3] = (usb_height >> 8) & 0x03; /* MSB */ + + err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ); + + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s failed: error %d\n", __func__, err_code); + return err_code; + } + usbvision->curwidth = usbvision->stretch_width * usb_width; + usbvision->curheight = usbvision->stretch_height * usb_height; + } + + if (usbvision->isoc_mode == ISOC_MODE_YUV422) + frame_rate = (usbvision->isoc_packet_size * 1000) / (usb_width * usb_height * 2); + else if (usbvision->isoc_mode == ISOC_MODE_YUV420) + frame_rate = (usbvision->isoc_packet_size * 1000) / ((usb_width * usb_height * 12) / 8); + else + frame_rate = FRAMERATE_MAX; + + if (usbvision->tvnorm_id & V4L2_STD_625_50) + frame_drop = frame_rate * 32 / 25 - 1; + else if (usbvision->tvnorm_id & V4L2_STD_525_60) + frame_drop = frame_rate * 32 / 30 - 1; + + RESTRICT_TO_RANGE(frame_drop, FRAMERATE_MIN, FRAMERATE_MAX); + + PDEBUG(DBG_FUNC, "frame_rate %d fps, frame_drop %d", frame_rate, frame_drop); + + frame_drop = FRAMERATE_MAX; /* We can allow the maximum here, because dropping is controlled */ + + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_PAL) + frame_drop = 25; + else + frame_drop = 30; + } + + /* frame_drop = 7; => frame_phase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ... + => frame_skip = 4; + => frame_rate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25; + + frame_drop = 9; => frame_phase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ... + => frame_skip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ... + => frame_rate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125; + */ + err_code = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frame_drop); + return err_code; +} + + +/* + * usbvision_frames_alloc + * allocate the required frames + */ +int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames) +{ + int i; + + /* needs to be page aligned cause the buffers can be mapped individually! */ + usbvision->max_frame_size = PAGE_ALIGN(usbvision->curwidth * + usbvision->curheight * + usbvision->palette.bytes_per_pixel); + + /* Try to do my best to allocate the frames the user want in the remaining memory */ + usbvision->num_frames = number_of_frames; + while (usbvision->num_frames > 0) { + usbvision->fbuf_size = usbvision->num_frames * usbvision->max_frame_size; + usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size); + if (usbvision->fbuf) + break; + usbvision->num_frames--; + } + + spin_lock_init(&usbvision->queue_lock); + init_waitqueue_head(&usbvision->wait_frame); + init_waitqueue_head(&usbvision->wait_stream); + + /* Allocate all buffers */ + for (i = 0; i < usbvision->num_frames; i++) { + usbvision->frame[i].index = i; + usbvision->frame[i].grabstate = frame_state_unused; + usbvision->frame[i].data = usbvision->fbuf + + i * usbvision->max_frame_size; + /* + * Set default sizes for read operation. + */ + usbvision->stretch_width = 1; + usbvision->stretch_height = 1; + usbvision->frame[i].width = usbvision->curwidth; + usbvision->frame[i].height = usbvision->curheight; + usbvision->frame[i].bytes_read = 0; + } + PDEBUG(DBG_FUNC, "allocated %d frames (%d bytes per frame)", + usbvision->num_frames, usbvision->max_frame_size); + return usbvision->num_frames; +} + +/* + * usbvision_frames_free + * frees memory allocated for the frames + */ +void usbvision_frames_free(struct usb_usbvision *usbvision) +{ + /* Have to free all that memory */ + PDEBUG(DBG_FUNC, "free %d frames", usbvision->num_frames); + + if (usbvision->fbuf != NULL) { + usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); + usbvision->fbuf = NULL; + + usbvision->num_frames = 0; + } +} +/* + * usbvision_empty_framequeues() + * prepare queues for incoming and outgoing frames + */ +void usbvision_empty_framequeues(struct usb_usbvision *usbvision) +{ + u32 i; + + INIT_LIST_HEAD(&(usbvision->inqueue)); + INIT_LIST_HEAD(&(usbvision->outqueue)); + + for (i = 0; i < USBVISION_NUMFRAMES; i++) { + usbvision->frame[i].grabstate = frame_state_unused; + usbvision->frame[i].bytes_read = 0; + } +} + +/* + * usbvision_stream_interrupt() + * stops streaming + */ +int usbvision_stream_interrupt(struct usb_usbvision *usbvision) +{ + int ret = 0; + + /* stop reading from the device */ + + usbvision->streaming = stream_interrupt; + ret = wait_event_timeout(usbvision->wait_stream, + (usbvision->streaming == stream_idle), + msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES)); + return ret; +} + +/* + * usbvision_set_compress_params() + * + */ + +static int usbvision_set_compress_params(struct usb_usbvision *usbvision) +{ + static const char proc[] = "usbvision_set_compresion_params: "; + int rc; + unsigned char value[6]; + + value[0] = 0x0F; /* Intra-Compression cycle */ + value[1] = 0x01; /* Reg.45 one line per strip */ + value[2] = 0x00; /* Reg.46 Force intra mode on all new frames */ + value[3] = 0x00; /* Reg.47 FORCE_UP <- 0 normal operation (not force) */ + value[4] = 0xA2; /* Reg.48 BUF_THR I'm not sure if this does something in not compressed mode. */ + value[5] = 0x00; /* Reg.49 DVI_YUV This has nothing to do with compression */ + + /* catched values for NT1004 */ + /* value[0] = 0xFF; Never apply intra mode automatically */ + /* value[1] = 0xF1; Use full frame height for virtual strip width; One line per strip */ + /* value[2] = 0x01; Force intra mode on all new frames */ + /* value[3] = 0x00; Strip size 400 Bytes; do not force up */ + /* value[4] = 0xA2; */ + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_INTRA_CYC, value, 5, HZ); + + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + return rc; + } + + if (usbvision->bridge_type == BRIDGE_NT1004) { + value[0] = 20; /* PCM Threshold 1 */ + value[1] = 12; /* PCM Threshold 2 */ + value[2] = 255; /* Distortion Threshold inter */ + value[3] = 255; /* Distortion Threshold intra */ + value[4] = 43; /* Max Distortion inter */ + value[5] = 43; /* Max Distortion intra */ + } else { + value[0] = 20; /* PCM Threshold 1 */ + value[1] = 12; /* PCM Threshold 2 */ + value[2] = 255; /* Distortion Threshold d7-d0 */ + value[3] = 0; /* Distortion Threshold d11-d8 */ + value[4] = 43; /* Max Distortion d7-d0 */ + value[5] = 0; /* Max Distortion d8 */ + } + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_PCM_THR1, value, 6, HZ); + + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + } + return rc; +} + + +/* + * usbvision_set_input() + * + * Set the input (saa711x, ...) size x y and other misc input params + * I've no idea if this parameters are right + * + */ +int usbvision_set_input(struct usb_usbvision *usbvision) +{ + static const char proc[] = "usbvision_set_input: "; + int rc; + unsigned char value[8]; + unsigned char dvi_yuv_value; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + /* Set input format expected from decoder*/ + if (usbvision_device_data[usbvision->dev_model].vin_reg1_override) { + value[0] = usbvision_device_data[usbvision->dev_model].vin_reg1; + } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) { + /* SAA7113 uses 8 bit output */ + value[0] = USBVISION_8_422_SYNC; + } else { + /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses + * as that is how saa7111 is configured */ + value[0] = USBVISION_16_422_SYNC; + /* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/ + } + + rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]); + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + return rc; + } + + + if (usbvision->tvnorm_id & V4L2_STD_PAL) { + value[0] = 0xC0; + value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */ + value[2] = 0x20; + value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */ + value[4] = 0x60; + value[5] = 0x00; /* 0x0060 -> 96 Input video h offset */ + value[6] = 0x16; + value[7] = 0x00; /* 0x0016 -> 22 Input video v offset */ + } else if (usbvision->tvnorm_id & V4L2_STD_SECAM) { + value[0] = 0xC0; + value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */ + value[2] = 0x20; + value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */ + value[4] = 0x01; + value[5] = 0x00; /* 0x0001 -> 01 Input video h offset */ + value[6] = 0x01; + value[7] = 0x00; /* 0x0001 -> 01 Input video v offset */ + } else { /* V4L2_STD_NTSC */ + value[0] = 0xD0; + value[1] = 0x02; /* 0x02D0 -> 720 Input video line length */ + value[2] = 0xF0; + value[3] = 0x00; /* 0x00F0 -> 240 Input video number of lines */ + value[4] = 0x50; + value[5] = 0x00; /* 0x0050 -> 80 Input video h offset */ + value[6] = 0x10; + value[7] = 0x00; /* 0x0010 -> 16 Input video v offset */ + } + + /* webcam is only 480 pixels wide, both PAL and NTSC version */ + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + value[0] = 0xe0; + value[1] = 0x01; /* 0x01E0 -> 480 Input video line length */ + } + + if (usbvision_device_data[usbvision->dev_model].x_offset >= 0) { + value[4] = usbvision_device_data[usbvision->dev_model].x_offset & 0xff; + value[5] = (usbvision_device_data[usbvision->dev_model].x_offset & 0x0300) >> 8; + } + + if (adjust_x_offset != -1) { + value[4] = adjust_x_offset & 0xff; + value[5] = (adjust_x_offset & 0x0300) >> 8; + } + + if (usbvision_device_data[usbvision->dev_model].y_offset >= 0) { + value[6] = usbvision_device_data[usbvision->dev_model].y_offset & 0xff; + value[7] = (usbvision_device_data[usbvision->dev_model].y_offset & 0x0300) >> 8; + } + + if (adjust_y_offset != -1) { + value[6] = adjust_y_offset & 0xff; + value[7] = (adjust_y_offset & 0x0300) >> 8; + } + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, /* USBVISION specific code */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_LXSIZE_I, value, 8, HZ); + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + return rc; + } + + + dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */ + + if (usbvision_device_data[usbvision->dev_model].dvi_yuv_override) { + dvi_yuv_value = usbvision_device_data[usbvision->dev_model].dvi_yuv; + } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) { + /* This changes as the fine sync control changes. Further investigation necessary */ + dvi_yuv_value = 0x06; + } + + return usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value); +} + + +/* + * usbvision_set_dram_settings() + * + * Set the buffer address needed by the usbvision dram to operate + * This values has been taken with usbsnoop. + * + */ + +static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) +{ + int rc; + unsigned char value[8]; + + if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) { + value[0] = 0x42; + value[1] = 0x71; + value[2] = 0xff; + value[3] = 0x00; + value[4] = 0x98; + value[5] = 0xe0; + value[6] = 0x71; + value[7] = 0xff; + /* UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte) */ + /* FDL: 0x00000-0x0E099 = 57498 Words */ + /* VDW: 0x0E3FF-0x3FFFF */ + } else { + value[0] = 0x42; + value[1] = 0x00; + value[2] = 0xff; + value[3] = 0x00; + value[4] = 0x00; + value[5] = 0x00; + value[6] = 0x00; + value[7] = 0xff; + } + /* These are the values of the address of the video buffer, + * they have to be loaded into the USBVISION_DRM_PRM1-8 + * + * Start address of video output buffer for read: drm_prm1-2 -> 0x00000 + * End address of video output buffer for read: drm_prm1-3 -> 0x1ffff + * Start address of video frame delay buffer: drm_prm1-4 -> 0x20000 + * Only used in compressed mode + * End address of video frame delay buffer: drm_prm1-5-6 -> 0x3ffff + * Only used in compressed mode + * Start address of video output buffer for write: drm_prm1-7 -> 0x00000 + * End address of video output buffer for write: drm_prm1-8 -> 0x1ffff + */ + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, /* USBVISION specific code */ + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_DRM_PRM1, value, 8, HZ); + + if (rc < 0) { + dev_err(&usbvision->dev->dev, "%s: ERROR=%d\n", __func__, rc); + return rc; + } + + /* Restart the video buffer logic */ + rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR | + USBVISION_RES_FDL | USBVISION_RES_VDW); + if (rc < 0) + return rc; + rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00); + + return rc; +} + +/* + * () + * + * Power on the device, enables suspend-resume logic + * & reset the isoc End-Point + * + */ + +int usbvision_power_on(struct usb_usbvision *usbvision) +{ + int err_code = 0; + + PDEBUG(DBG_FUNC, ""); + + usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_RES2); + + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + usbvision_write_reg(usbvision, USBVISION_VIN_REG1, + USBVISION_16_422_SYNC | USBVISION_HVALID_PO); + usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_NOHVALID | USBVISION_KEEP_BLANK); + } + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID); + mdelay(10); + err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2); + if (err_code == 1) + usbvision->power = 1; + PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code < 0) ? "ERROR" : "power is on", err_code); + return err_code; +} + + +/* + * usbvision timer stuff + */ + +/* to call usbvision_power_off from task queue */ +static void call_usbvision_power_off(struct work_struct *work) +{ + struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, power_off_work); + + PDEBUG(DBG_FUNC, ""); + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return; + + if (usbvision->user == 0) { + usbvision_i2c_unregister(usbvision); + + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + mutex_unlock(&usbvision->v4l2_lock); +} + +static void usbvision_power_off_timer(unsigned long data) +{ + struct usb_usbvision *usbvision = (void *)data; + + PDEBUG(DBG_FUNC, ""); + del_timer(&usbvision->power_off_timer); + INIT_WORK(&usbvision->power_off_work, call_usbvision_power_off); + (void) schedule_work(&usbvision->power_off_work); +} + +void usbvision_init_power_off_timer(struct usb_usbvision *usbvision) +{ + init_timer(&usbvision->power_off_timer); + usbvision->power_off_timer.data = (long)usbvision; + usbvision->power_off_timer.function = usbvision_power_off_timer; +} + +void usbvision_set_power_off_timer(struct usb_usbvision *usbvision) +{ + mod_timer(&usbvision->power_off_timer, jiffies + USBVISION_POWEROFF_TIME); +} + +void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision) +{ + if (timer_pending(&usbvision->power_off_timer)) + del_timer(&usbvision->power_off_timer); +} + +/* + * usbvision_begin_streaming() + * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no + * idea about the rest + */ +int usbvision_begin_streaming(struct usb_usbvision *usbvision) +{ + if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) + usbvision_init_compression(usbvision); + return usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_NOHVALID | usbvision->vin_reg2_preset); +} + +/* + * usbvision_restart_isoc() + * Not sure yet if touching here PWR_REG make loose the config + */ + +int usbvision_restart_isoc(struct usb_usbvision *usbvision) +{ + int ret; + + ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID); + if (ret < 0) + return ret; + ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID | + USBVISION_RES2); + if (ret < 0) + return ret; + ret = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_KEEP_BLANK | USBVISION_NOHVALID | + usbvision->vin_reg2_preset); + if (ret < 0) + return ret; + + /* TODO: schedule timeout */ + while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) & 0x01) != 1) + ; + + return 0; +} + +int usbvision_audio_off(struct usb_usbvision *usbvision) +{ + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) { + printk(KERN_ERR "usbvision_audio_off: can't write reg\n"); + return -1; + } + usbvision->audio_mute = 0; + usbvision->audio_channel = USBVISION_AUDIO_MUTE; + return 0; +} + +int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel) +{ + if (!usbvision->audio_mute) { + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, audio_channel) < 0) { + printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n"); + return -1; + } + } + usbvision->audio_channel = audio_channel; + return 0; +} + +int usbvision_setup(struct usb_usbvision *usbvision, int format) +{ + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) + usbvision_init_webcam(usbvision); + usbvision_set_video_format(usbvision, format); + usbvision_set_dram_settings(usbvision); + usbvision_set_compress_params(usbvision); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT); + usbvision_restart_isoc(usbvision); + + /* cosas del PCM */ + return USBVISION_IS_OPERATIONAL(usbvision); +} + +int usbvision_set_alternate(struct usb_usbvision *dev) +{ + int err_code, prev_alt = dev->iface_alt; + int i; + + dev->iface_alt = 0; + for (i = 0; i < dev->num_alt; i++) + if (dev->alt_max_pkt_size[i] > dev->alt_max_pkt_size[dev->iface_alt]) + dev->iface_alt = i; + + if (dev->iface_alt != prev_alt) { + dev->isoc_packet_size = dev->alt_max_pkt_size[dev->iface_alt]; + PDEBUG(DBG_FUNC, "setting alternate %d with max_packet_size=%u", + dev->iface_alt, dev->isoc_packet_size); + err_code = usb_set_interface(dev->dev, dev->iface, dev->iface_alt); + if (err_code < 0) { + dev_err(&dev->dev->dev, + "cannot change alternate number to %d (error=%i)\n", + dev->iface_alt, err_code); + return err_code; + } + } + + PDEBUG(DBG_ISOC, "ISO Packet Length:%d", dev->isoc_packet_size); + + return 0; +} + +/* + * usbvision_init_isoc() + * + */ +int usbvision_init_isoc(struct usb_usbvision *usbvision) +{ + struct usb_device *dev = usbvision->dev; + int buf_idx, err_code, reg_value; + int sb_size; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + usbvision->cur_frame = NULL; + scratch_reset(usbvision); + + /* Alternate interface 1 is is the biggest frame size */ + err_code = usbvision_set_alternate(usbvision); + if (err_code < 0) { + usbvision->last_error = err_code; + return -EBUSY; + } + sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size; + + reg_value = (16 - usbvision_read_reg(usbvision, + USBVISION_ALTER_REG)) & 0x0F; + + usbvision->usb_bandwidth = reg_value >> 1; + PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", + usbvision->usb_bandwidth); + + + + /* We double buffer the Iso lists */ + + for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { + int j, k; + struct urb *urb; + + urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); + if (urb == NULL) { + dev_err(&usbvision->dev->dev, + "%s: usb_alloc_urb() failed\n", __func__); + return -ENOMEM; + } + usbvision->sbuf[buf_idx].urb = urb; + usbvision->sbuf[buf_idx].data = + usb_alloc_coherent(usbvision->dev, + sb_size, + GFP_KERNEL, + &urb->transfer_dma); + urb->dev = dev; + urb->context = usbvision; + urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->interval = 1; + urb->transfer_buffer = usbvision->sbuf[buf_idx].data; + urb->complete = usbvision_isoc_irq; + urb->number_of_packets = USBVISION_URB_FRAMES; + urb->transfer_buffer_length = + usbvision->isoc_packet_size * USBVISION_URB_FRAMES; + for (j = k = 0; j < USBVISION_URB_FRAMES; j++, + k += usbvision->isoc_packet_size) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + usbvision->isoc_packet_size; + } + } + + /* Submit all URBs */ + for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { + err_code = usb_submit_urb(usbvision->sbuf[buf_idx].urb, + GFP_KERNEL); + if (err_code) { + dev_err(&usbvision->dev->dev, + "%s: usb_submit_urb(%d) failed: error %d\n", + __func__, buf_idx, err_code); + } + } + + usbvision->streaming = stream_idle; + PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", + __func__, + usbvision->video_endp); + return 0; +} + +/* + * usbvision_stop_isoc() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + */ +void usbvision_stop_isoc(struct usb_usbvision *usbvision) +{ + int buf_idx, err_code, reg_value; + int sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size; + + if ((usbvision->streaming == stream_off) || (usbvision->dev == NULL)) + return; + + /* Unschedule all of the iso td's */ + for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { + usb_kill_urb(usbvision->sbuf[buf_idx].urb); + if (usbvision->sbuf[buf_idx].data) { + usb_free_coherent(usbvision->dev, + sb_size, + usbvision->sbuf[buf_idx].data, + usbvision->sbuf[buf_idx].urb->transfer_dma); + } + usb_free_urb(usbvision->sbuf[buf_idx].urb); + usbvision->sbuf[buf_idx].urb = NULL; + } + + PDEBUG(DBG_ISOC, "%s: streaming=stream_off\n", __func__); + usbvision->streaming = stream_off; + + if (!usbvision->remove_pending) { + /* Set packet size to 0 */ + usbvision->iface_alt = 0; + err_code = usb_set_interface(usbvision->dev, usbvision->iface, + usbvision->iface_alt); + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s: usb_set_interface() failed: error %d\n", + __func__, err_code); + usbvision->last_error = err_code; + } + reg_value = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; + usbvision->isoc_packet_size = + (reg_value == 0) ? 0 : (reg_value * 64) - 1; + PDEBUG(DBG_ISOC, "ISO Packet Length:%d", + usbvision->isoc_packet_size); + + usbvision->usb_bandwidth = reg_value >> 1; + PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", + usbvision->usb_bandwidth); + } +} + +int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) +{ + /* inputs #0 and #3 are constant for every SAA711x. */ + /* inputs #1 and #2 are variable for SAA7111 and SAA7113 */ + int mode[4] = { SAA7115_COMPOSITE0, 0, 0, SAA7115_COMPOSITE3 }; + int audio[] = { 1, 0, 0, 0 }; + /* channel 0 is TV with audiochannel 1 (tuner mono) */ + /* channel 1 is Composite with audio channel 0 (line in) */ + /* channel 2 is S-Video with audio channel 0 (line in) */ + /* channel 3 is additional video inputs to the device with audio channel 0 (line in) */ + + RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs); + usbvision->ctl_input = channel; + + /* set the new channel */ + /* Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video */ + /* Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red */ + + switch (usbvision_device_data[usbvision->dev_model].codec) { + case CODEC_SAA7113: + mode[1] = SAA7115_COMPOSITE2; + if (switch_svideo_input) { + /* To handle problems with S-Video Input for + * some devices. Use switch_svideo_input + * parameter when loading the module.*/ + mode[2] = SAA7115_COMPOSITE1; + } else { + mode[2] = SAA7115_SVIDEO1; + } + break; + case CODEC_SAA7111: + default: + /* modes for saa7111 */ + mode[1] = SAA7115_COMPOSITE1; + mode[2] = SAA7115_SVIDEO1; + break; + } + call_all(usbvision, video, s_routing, mode[channel], 0, 0); + usbvision_set_audio(usbvision, audio[channel]); + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c new file mode 100644 index 000000000000..89fec029e924 --- /dev/null +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -0,0 +1,456 @@ +/* + * usbvision_i2c.c + * i2c algorithm for USB-I2C Bridges + * + * Copyright (c) 1999-2007 Joerg Heckenbach + * Dwaine Garden + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * This program is free software; 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 +#include "usbvision.h" + +#define DBG_I2C (1 << 0) + +static int i2c_debug; + +module_param(i2c_debug, int, 0644); /* debug_i2c_usb mode of the device driver */ +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define PDEBUG(level, fmt, args...) { \ + if (i2c_debug & (level)) \ + printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ + __func__, __LINE__ , ## args); \ + } + +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len); +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len); + +static inline int try_write_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct usb_usbvision *usbvision; + int i, ret = -1; + char buf[4]; + + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); + buf[0] = 0x00; + for (i = 0; i <= retries; i++) { + ret = (usbvision_i2c_write(usbvision, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5); + if (i == retries) /* no success */ + break; + udelay(10); + } + if (i) { + PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C, "Maybe there's no device at this address"); + } + return ret; +} + +static inline int try_read_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct usb_usbvision *usbvision; + int i, ret = -1; + char buf[4]; + + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); + for (i = 0; i <= retries; i++) { + ret = (usbvision_i2c_read(usbvision, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5); + if (i == retries) /* no success */ + break; + udelay(10); + } + if (i) { + PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C, "Maybe there's no device at this address"); + } + return ret; +} + +static inline int usb_find_address(struct i2c_adapter *i2c_adap, + struct i2c_msg *msg, int retries, + unsigned char *add) +{ + unsigned short flags = msg->flags; + + unsigned char addr; + int ret; + + addr = (msg->addr << 1); + if (flags & I2C_M_RD) + addr |= 1; + + add[0] = addr; + if (flags & I2C_M_RD) + ret = try_read_address(i2c_adap, addr, retries); + else + ret = try_write_address(i2c_adap, addr, retries); + + if (ret != 1) + return -EREMOTEIO; + + return 0; +} + +static int +usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct usb_usbvision *usbvision; + int i, ret; + unsigned char addr = 0; + + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); + if (ret != 0) { + PDEBUG(DBG_I2C, "got NAK from device, message #%d", i); + return (ret < 0) ? ret : -EREMOTEIO; + } + + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer */ + ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) + return (ret < 0) ? ret : -EREMOTEIO; + } else { + /* write bytes from buffer */ + ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) + return (ret < 0) ? ret : -EREMOTEIO; + } + } + return num; +} + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm usbvision_algo = { + .master_xfer = usbvision_i2c_xfer, + .smbus_xfer = NULL, + .functionality = functionality, +}; + + +/* ----------------------------------------------------------------------- */ +/* usbvision specific I2C functions */ +/* ----------------------------------------------------------------------- */ +static struct i2c_adapter i2c_adap_template; + +int usbvision_i2c_register(struct usb_usbvision *usbvision) +{ + static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; + + if (usbvision->registered_i2c) + return 0; + + memcpy(&usbvision->i2c_adap, &i2c_adap_template, + sizeof(struct i2c_adapter)); + + sprintf(usbvision->i2c_adap.name, "%s-%d-%s", i2c_adap_template.name, + usbvision->dev->bus->busnum, usbvision->dev->devpath); + PDEBUG(DBG_I2C, "Adaptername: %s", usbvision->i2c_adap.name); + usbvision->i2c_adap.dev.parent = &usbvision->dev->dev; + + i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev); + + if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { + printk(KERN_ERR "usbvision_i2c_register: can't write reg\n"); + return -EBUSY; + } + + PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); + PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); + + /* register new adapter to i2c module... */ + + usbvision->i2c_adap.algo = &usbvision_algo; + + usbvision->i2c_adap.timeout = 100; /* default values, should */ + usbvision->i2c_adap.retries = 3; /* be replaced by defines */ + + i2c_add_adapter(&usbvision->i2c_adap); + + PDEBUG(DBG_I2C, "i2c bus for %s registered", usbvision->i2c_adap.name); + + /* Request the load of the i2c modules we need */ + switch (usbvision_device_data[usbvision->dev_model].codec) { + case CODEC_SAA7113: + case CODEC_SAA7111: + /* Without this delay the detection of the saa711x is + hit-and-miss. */ + mdelay(10); + v4l2_i2c_new_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, + "saa7115_auto", 0, saa711x_addrs); + break; + } + if (usbvision_device_data[usbvision->dev_model].tuner == 1) { + struct v4l2_subdev *sd; + enum v4l2_i2c_tuner_type type; + struct tuner_setup tun_setup; + + sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, + "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + /* depending on whether we found a demod or not, select + the tuner type. */ + type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + + sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, + "tuner", 0, v4l2_i2c_tuner_addrs(type)); + + if (sd == NULL) + return -ENODEV; + if (usbvision->tuner_type != -1) { + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = usbvision->tuner_type; + tun_setup.addr = v4l2_i2c_subdev_addr(sd); + call_all(usbvision, tuner, s_type_addr, &tun_setup); + } + } + usbvision->registered_i2c = 1; + + return 0; +} + +int usbvision_i2c_unregister(struct usb_usbvision *usbvision) +{ + if (!usbvision->registered_i2c) + return 0; + + i2c_del_adapter(&(usbvision->i2c_adap)); + usbvision->registered_i2c = 0; + + PDEBUG(DBG_I2C, "i2c bus for %s unregistered", usbvision->i2c_adap.name); + + return 0; +} + +static int +usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, + char *buf, short len) +{ + int rc, retries; + + for (retries = 5;;) { + rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr); + if (rc < 0) + return rc; + + /* Initiate byte read cycle */ + /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */ + /* d3 0=Wr 1=Rd */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x18); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + /* USBVISION_SER_CONT -> d4 == 0 busy */ + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + /* USBVISION_SER_CONT -> d5 == 1 Not ack */ + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + if (rc < 0) + return rc; + + if (--retries < 0) + return -1; + } + + switch (len) { + case 4: + buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4); + case 3: + buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3); + case 2: + buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2); + case 1: + buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1); + break; + default: + printk(KERN_ERR + "usbvision_i2c_read_max4: buffer length > 4\n"); + } + + if (i2c_debug & DBG_I2C) { + int idx; + + for (idx = 0; idx < len; idx++) + PDEBUG(DBG_I2C, "read %x from address %x", (unsigned char)buf[idx], addr); + } + return len; +} + + +static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, + unsigned char addr, const char *buf, + short len) +{ + int rc, retries; + int i; + unsigned char value[6]; + unsigned char ser_cont; + + ser_cont = (len & 0x07) | 0x10; + + value[0] = addr; + value[1] = ser_cont; + for (i = 0; i < len; i++) + value[i + 2] = buf[i]; + + for (retries = 5;;) { + rc = usb_control_msg(usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_SER_ADRS, value, + len + 2, HZ); + + if (rc < 0) + return rc; + + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x10); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + + if (--retries < 0) + return -1; + + } + + if (i2c_debug & DBG_I2C) { + int idx; + + for (idx = 0; idx < len; idx++) + PDEBUG(DBG_I2C, "wrote %x at address %x", (unsigned char)buf[idx], addr); + } + return len; +} + +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len) +{ + char *buf_ptr = buf; + int retval; + int wrcount = 0; + int count; + int max_len = 4; + + while (len > 0) { + count = (len > max_len) ? max_len : len; + retval = usbvision_i2c_write_max4(usbvision, addr, buf_ptr, count); + if (retval > 0) { + len -= count; + buf_ptr += count; + wrcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return wrcount; +} + +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len) +{ + char temp[4]; + int retval, i; + int rdcount = 0; + int count; + + while (len > 0) { + count = (len > 3) ? 4 : len; + retval = usbvision_i2c_read_max4(usbvision, addr, temp, count); + if (retval > 0) { + for (i = 0; i < len; i++) + buf[rdcount + i] = temp[i]; + len -= count; + rdcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return rdcount; +} + +static struct i2c_adapter i2c_adap_template = { + .owner = THIS_MODULE, + .name = "usbvision", +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c new file mode 100644 index 000000000000..8a4317979a43 --- /dev/null +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -0,0 +1,1720 @@ +/* + * USB USBVISION Video device driver 0.9.10 + * + * + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * + * This program is free software; 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. + * + * Let's call the version 0.... until compression decoding is completely + * implemented. + * + * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach. + * It was based on USB CPiA driver written by Peter Pregler, + * Scott J. Bertin and Johannes Erdfelt + * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler & + * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink + * Updates to driver completed by Dwaine P. Garden + * + * + * TODO: + * - use submit_urb for all setup packets + * - Fix memory settings for nt1004. It is 4 times as big as the + * nt1003 memory. + * - Add audio on endpoint 3 for nt1004 chip. + * Seems impossible, needs a codec interface. Which one? + * - Clean up the driver. + * - optimization for performance. + * - Add Videotext capability (VBI). Working on it..... + * - Check audio for other devices + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "usbvision.h" +#include "usbvision-cards.h" + +#define DRIVER_AUTHOR \ + "Joerg Heckenbach , " \ + "Dwaine Garden " +#define DRIVER_NAME "usbvision" +#define DRIVER_ALIAS "USBVision" +#define DRIVER_DESC "USBVision USB Video Device Driver for Linux" +#define DRIVER_LICENSE "GPL" +#define USBVISION_VERSION_STRING "0.9.11" + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ + + +#ifdef USBVISION_DEBUG + #define PDEBUG(level, fmt, args...) { \ + if (video_debug & (level)) \ + printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ + __func__, __LINE__ , ## args); \ + } +#else + #define PDEBUG(level, fmt, args...) do {} while (0) +#endif + +#define DBG_IO (1 << 1) +#define DBG_PROBE (1 << 2) +#define DBG_MMAP (1 << 3) + +/* String operations */ +#define rmspace(str) while (*str == ' ') str++; +#define goto2next(str) while (*str != ' ') str++; while (*str == ' ') str++; + + +/* sequential number of usbvision device */ +static int usbvision_nr; + +static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { + { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, + { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, + { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, + { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, + { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, /* 1.5 ! */ + { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } +}; + +/* Function prototypes */ +static void usbvision_release(struct usb_usbvision *usbvision); + +/* Default initialization of device driver parameters */ +/* Set the default format for ISOC endpoint */ +static int isoc_mode = ISOC_MODE_COMPRESS; +/* Set the default Debug Mode of the device driver */ +static int video_debug; +/* Set the default device to power on at startup */ +static int power_on_at_open = 1; +/* Sequential Number of Video Device */ +static int video_nr = -1; +/* Sequential Number of Radio Device */ +static int radio_nr = -1; + +/* Grab parameters for the device driver */ + +/* Showing parameters under SYSFS */ +module_param(isoc_mode, int, 0444); +module_param(video_debug, int, 0444); +module_param(power_on_at_open, int, 0444); +module_param(video_nr, int, 0444); +module_param(radio_nr, int, 0444); + +MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); +MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); +MODULE_PARM_DESC(power_on_at_open, " Set the default device to power on when device is opened. Default: 1 (On)"); +MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); + + +/* Misc stuff */ +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); +MODULE_VERSION(USBVISION_VERSION_STRING); +MODULE_ALIAS(DRIVER_ALIAS); + + +/*****************************************************************************/ +/* SYSFS Code - Copied from the stv680.c usb module. */ +/* Device information is located at /sys/class/video4linux/video0 */ +/* Device parameters information is located at /sys/module/usbvision */ +/* Device USB Information is located at */ +/* /sys/bus/usb/drivers/USBVision Video Grabber */ +/*****************************************************************************/ + +#define YES_NO(x) ((x) ? "Yes" : "No") + +static inline struct usb_usbvision *cd_to_usbvision(struct device *cd) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + return video_get_drvdata(vdev); +} + +static ssize_t show_version(struct device *cd, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", USBVISION_VERSION_STRING); +} +static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t show_model(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", + usbvision_device_data[usbvision->dev_model].model_string); +} +static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); + +static ssize_t show_hue(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_HUE; + ctrl.value = 0; + if (usbvision->user) + call_all(usbvision, core, g_ctrl, &ctrl); + return sprintf(buf, "%d\n", ctrl.value); +} +static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); + +static ssize_t show_contrast(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_CONTRAST; + ctrl.value = 0; + if (usbvision->user) + call_all(usbvision, core, g_ctrl, &ctrl); + return sprintf(buf, "%d\n", ctrl.value); +} +static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); + +static ssize_t show_brightness(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_BRIGHTNESS; + ctrl.value = 0; + if (usbvision->user) + call_all(usbvision, core, g_ctrl, &ctrl); + return sprintf(buf, "%d\n", ctrl.value); +} +static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); + +static ssize_t show_saturation(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_SATURATION; + ctrl.value = 0; + if (usbvision->user) + call_all(usbvision, core, g_ctrl, &ctrl); + return sprintf(buf, "%d\n", ctrl.value); +} +static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); + +static ssize_t show_streaming(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", + YES_NO(usbvision->streaming == stream_on ? 1 : 0)); +} +static DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); + +static ssize_t show_compression(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", + YES_NO(usbvision->isoc_mode == ISOC_MODE_COMPRESS)); +} +static DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); + +static ssize_t show_device_bridge(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = + container_of(cd, struct video_device, dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->bridge_type); +} +static DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); + +static void usbvision_create_sysfs(struct video_device *vdev) +{ + int res; + + if (!vdev) + return; + do { + res = device_create_file(&vdev->dev, &dev_attr_version); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_model); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_hue); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_contrast); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_brightness); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_saturation); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_streaming); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_compression); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_bridge); + if (res >= 0) + return; + } while (0); + + dev_err(&vdev->dev, "%s error: %d\n", __func__, res); +} + +static void usbvision_remove_sysfs(struct video_device *vdev) +{ + if (vdev) { + device_remove_file(&vdev->dev, &dev_attr_version); + device_remove_file(&vdev->dev, &dev_attr_model); + device_remove_file(&vdev->dev, &dev_attr_hue); + device_remove_file(&vdev->dev, &dev_attr_contrast); + device_remove_file(&vdev->dev, &dev_attr_brightness); + device_remove_file(&vdev->dev, &dev_attr_saturation); + device_remove_file(&vdev->dev, &dev_attr_streaming); + device_remove_file(&vdev->dev, &dev_attr_compression); + device_remove_file(&vdev->dev, &dev_attr_bridge); + } +} + +/* + * usbvision_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'usbvision->user'). The procedure + * then allocates buffers needed for video processing. + * + */ +static int usbvision_v4l2_open(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code = 0; + + PDEBUG(DBG_IO, "open"); + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + usbvision_reset_power_off_timer(usbvision); + + if (usbvision->user) + err_code = -EBUSY; + else { + /* Allocate memory for the scratch ring buffer */ + err_code = usbvision_scratch_alloc(usbvision); + if (isoc_mode == ISOC_MODE_COMPRESS) { + /* Allocate intermediate decompression buffers + only if needed */ + err_code = usbvision_decompress_alloc(usbvision); + } + if (err_code) { + /* Deallocate all buffers if trouble */ + usbvision_scratch_free(usbvision); + usbvision_decompress_free(usbvision); + } + } + + /* If so far no errors then we shall start the camera */ + if (!err_code) { + if (usbvision->power == 0) { + usbvision_power_on(usbvision); + usbvision_i2c_register(usbvision); + } + + /* Send init sequence only once, it's large! */ + if (!usbvision->initialized) { + int setup_ok = 0; + setup_ok = usbvision_setup(usbvision, isoc_mode); + if (setup_ok) + usbvision->initialized = 1; + else + err_code = -EBUSY; + } + + if (!err_code) { + usbvision_begin_streaming(usbvision); + err_code = usbvision_init_isoc(usbvision); + /* device must be initialized before isoc transfer */ + usbvision_muxsel(usbvision, 0); + usbvision->user++; + } else { + if (power_on_at_open) { + usbvision_i2c_unregister(usbvision); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + } + } + + /* prepare queues */ + usbvision_empty_framequeues(usbvision); + mutex_unlock(&usbvision->v4l2_lock); + + PDEBUG(DBG_IO, "success"); + return err_code; +} + +/* + * usbvision_v4l2_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvision_v4l2_open(). + * + */ +static int usbvision_v4l2_close(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + PDEBUG(DBG_IO, "close"); + + mutex_lock(&usbvision->v4l2_lock); + usbvision_audio_off(usbvision); + usbvision_restart_isoc(usbvision); + usbvision_stop_isoc(usbvision); + + usbvision_decompress_free(usbvision); + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + usbvision_scratch_free(usbvision); + + usbvision->user--; + + if (power_on_at_open) { + /* power off in a little while + to avoid off/on every close/open short sequences */ + usbvision_set_power_off_timer(usbvision); + usbvision->initialized = 0; + } + + if (usbvision->remove_pending) { + printk(KERN_INFO "%s: Final disconnect\n", __func__); + usbvision_release(usbvision); + } + mutex_unlock(&usbvision->v4l2_lock); + + PDEBUG(DBG_IO, "success"); + return 0; +} + + +/* + * usbvision_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + */ +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + /* NT100x has a 8-bit register space */ + err_code = usbvision_read_reg(usbvision, reg->reg&0xff); + if (err_code < 0) { + dev_err(&usbvision->vdev->dev, + "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n", + __func__, err_code); + return err_code; + } + reg->val = err_code; + reg->size = 1; + return 0; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + /* NT100x has a 8-bit register space */ + err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val); + if (err_code < 0) { + dev_err(&usbvision->vdev->dev, + "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n", + __func__, err_code); + return err_code; + } + return 0; +} +#endif + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *vc) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); + strlcpy(vc->card, + usbvision_device_data[usbvision->dev_model].model_string, + sizeof(vc->card)); + usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); + vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *vi) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int chan; + + if (vi->index >= usbvision->video_inputs) + return -EINVAL; + if (usbvision->have_tuner) + chan = vi->index; + else + chan = vi->index + 1; /* skip Television string*/ + + /* Determine the requested input characteristics + specific for each usbvision card model */ + switch (chan) { + case 0: + if (usbvision_device_data[usbvision->dev_model].video_channels == 4) { + strcpy(vi->name, "White Video Input"); + } else { + strcpy(vi->name, "Television"); + vi->type = V4L2_INPUT_TYPE_TUNER; + vi->audioset = 1; + vi->tuner = chan; + vi->std = USBVISION_NORMS; + } + break; + case 1: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->dev_model].video_channels == 4) + strcpy(vi->name, "Green Video Input"); + else + strcpy(vi->name, "Composite Video Input"); + vi->std = V4L2_STD_PAL; + break; + case 2: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->dev_model].video_channels == 4) + strcpy(vi->name, "Yellow Video Input"); + else + strcpy(vi->name, "S-Video Input"); + vi->std = V4L2_STD_PAL; + break; + case 3: + vi->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(vi->name, "Red Video Input"); + vi->std = V4L2_STD_PAL; + break; + } + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + *input = usbvision->ctl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int input) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (input >= usbvision->video_inputs) + return -EINVAL; + + usbvision_muxsel(usbvision, input); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, + usbvision->curwidth, + usbvision->curheight); + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + usbvision->tvnorm_id = *id; + + call_all(usbvision, core, s_std, usbvision->tvnorm_id); + /* propagate the change to the decoder */ + usbvision_muxsel(usbvision, usbvision->ctl_input); + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *vt) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (!usbvision->have_tuner || vt->index) /* Only tuner 0 */ + return -EINVAL; + if (usbvision->radio) { + strcpy(vt->name, "Radio"); + vt->type = V4L2_TUNER_RADIO; + } else { + strcpy(vt->name, "Television"); + } + /* Let clients fill in the remainder of this struct */ + call_all(usbvision, tuner, g_tuner, vt); + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *vt) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + /* Only no or one tuner for now */ + if (!usbvision->have_tuner || vt->index) + return -EINVAL; + /* let clients handle this */ + call_all(usbvision, tuner, s_tuner, vt); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + freq->tuner = 0; /* Only one tuner */ + if (usbvision->radio) + freq->type = V4L2_TUNER_RADIO; + else + freq->type = V4L2_TUNER_ANALOG_TV; + freq->frequency = usbvision->freq; + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + /* Only no or one tuner for now */ + if (!usbvision->have_tuner || freq->tuner) + return -EINVAL; + + usbvision->freq = freq->frequency; + call_all(usbvision, tuner, s_frequency, freq); + + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (usbvision->radio) + strcpy(a->name, "Radio"); + else + strcpy(a->name, "TV"); + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *fh, + struct v4l2_audio *a) +{ + if (a->index) + return -EINVAL; + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *ctrl) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + call_all(usbvision, core, queryctrl, ctrl); + + if (!ctrl->type) + return -EINVAL; + + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + call_all(usbvision, core, g_ctrl, ctrl); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + call_all(usbvision, core, s_ctrl, ctrl); + return 0; +} + +static int vidioc_reqbufs(struct file *file, + void *priv, struct v4l2_requestbuffers *vr) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int ret; + + RESTRICT_TO_RANGE(vr->count, 1, USBVISION_NUMFRAMES); + + /* Check input validity: + the user must do a VIDEO CAPTURE and MMAP method. */ + if (vr->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (usbvision->streaming == stream_on) { + ret = usbvision_stream_interrupt(usbvision); + if (ret) + return ret; + } + + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + vr->count = usbvision_frames_alloc(usbvision, vr->count); + + usbvision->cur_frame = NULL; + + return 0; +} + +static int vidioc_querybuf(struct file *file, + void *priv, struct v4l2_buffer *vb) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + struct usbvision_frame *frame; + + /* FIXME : must control + that buffers are mapped (VIDIOC_REQBUFS has been called) */ + if (vb->index >= usbvision->num_frames) + return -EINVAL; + /* Updating the corresponding frame state */ + vb->flags = 0; + frame = &usbvision->frame[vb->index]; + if (frame->grabstate >= frame_state_ready) + vb->flags |= V4L2_BUF_FLAG_QUEUED; + if (frame->grabstate >= frame_state_done) + vb->flags |= V4L2_BUF_FLAG_DONE; + if (frame->grabstate == frame_state_unused) + vb->flags |= V4L2_BUF_FLAG_MAPPED; + vb->memory = V4L2_MEMORY_MMAP; + + vb->m.offset = vb->index * PAGE_ALIGN(usbvision->max_frame_size); + + vb->memory = V4L2_MEMORY_MMAP; + vb->field = V4L2_FIELD_NONE; + vb->length = usbvision->curwidth * + usbvision->curheight * + usbvision->palette.bytes_per_pixel; + vb->timestamp = usbvision->frame[vb->index].timestamp; + vb->sequence = usbvision->frame[vb->index].sequence; + return 0; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *vb) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + struct usbvision_frame *frame; + unsigned long lock_flags; + + /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */ + if (vb->index >= usbvision->num_frames) + return -EINVAL; + + frame = &usbvision->frame[vb->index]; + + if (frame->grabstate != frame_state_unused) + return -EAGAIN; + + /* Mark it as ready and enqueue frame */ + frame->grabstate = frame_state_ready; + frame->scanstate = scan_state_scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + + vb->flags &= ~V4L2_BUF_FLAG_DONE; + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int ret; + struct usbvision_frame *f; + unsigned long lock_flags; + + if (list_empty(&(usbvision->outqueue))) { + if (usbvision->streaming == stream_idle) + return -EINVAL; + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; + } + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + f = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + f->grabstate = frame_state_unused; + + vb->memory = V4L2_MEMORY_MMAP; + vb->flags = V4L2_BUF_FLAG_MAPPED | + V4L2_BUF_FLAG_QUEUED | + V4L2_BUF_FLAG_DONE; + vb->index = f->index; + vb->sequence = f->sequence; + vb->timestamp = f->timestamp; + vb->field = V4L2_FIELD_NONE; + vb->bytesused = f->scanlength; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + usbvision->streaming = stream_on; + call_all(usbvision, video, s_stream, 1); + + return 0; +} + +static int vidioc_streamoff(struct file *file, + void *priv, enum v4l2_buf_type type) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (usbvision->streaming == stream_on) { + usbvision_stream_interrupt(usbvision); + /* Stop all video streamings */ + call_all(usbvision, video, s_stream, 0); + } + usbvision_empty_framequeues(usbvision); + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *vfd) +{ + if (vfd->index >= USBVISION_SUPPORTED_PALETTES - 1) + return -EINVAL; + strcpy(vfd->description, usbvision_v4l2_format[vfd->index].desc); + vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + vf->fmt.pix.width = usbvision->curwidth; + vf->fmt.pix.height = usbvision->curheight; + vf->fmt.pix.pixelformat = usbvision->palette.format; + vf->fmt.pix.bytesperline = + usbvision->curwidth * usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * usbvision->curheight; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int format_idx; + + /* Find requested format in available ones */ + for (format_idx = 0; format_idx < USBVISION_SUPPORTED_PALETTES; format_idx++) { + if (vf->fmt.pix.pixelformat == + usbvision_v4l2_format[format_idx].format) { + usbvision->palette = usbvision_v4l2_format[format_idx]; + break; + } + } + /* robustness */ + if (format_idx == USBVISION_SUPPORTED_PALETTES) + return -EINVAL; + RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + + vf->fmt.pix.bytesperline = vf->fmt.pix.width* + usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, vf); + if (ret) + return ret; + + /* stop io in case it is already in progress */ + if (usbvision->streaming == stream_on) { + ret = usbvision_stream_interrupt(usbvision); + if (ret) + return ret; + } + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + + usbvision->cur_frame = NULL; + + /* by now we are committed to the new data... */ + usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); + + return 0; +} + +static ssize_t usbvision_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int noblock = file->f_flags & O_NONBLOCK; + unsigned long lock_flags; + int ret, i; + struct usbvision_frame *frame; + + PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __func__, + (unsigned long)count, noblock); + + if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) + return -EFAULT; + + /* This entry point is compatible with the mmap routines + so that a user can do either VIDIOC_QBUF/VIDIOC_DQBUF + to get frames or call read on the device. */ + if (!usbvision->num_frames) { + /* First, allocate some frames to work with + if this has not been done with VIDIOC_REQBUF */ + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + usbvision_frames_alloc(usbvision, USBVISION_NUMFRAMES); + } + + if (usbvision->streaming != stream_on) { + /* no stream is running, make it running ! */ + usbvision->streaming = stream_on; + call_all(usbvision, video, s_stream, 1); + } + + /* Then, enqueue as many frames as possible + (like a user of VIDIOC_QBUF would do) */ + for (i = 0; i < usbvision->num_frames; i++) { + frame = &usbvision->frame[i]; + if (frame->grabstate == frame_state_unused) { + /* Mark it as ready and enqueue frame */ + frame->grabstate = frame_state_ready; + frame->scanstate = scan_state_scanning; + /* Accumulated in usbvision_parse_data() */ + frame->scanlength = 0; + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&frame->frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, + lock_flags); + } + } + + /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */ + if (list_empty(&(usbvision->outqueue))) { + if (noblock) + return -EAGAIN; + + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; + } + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + frame = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + /* An error returns an empty frame */ + if (frame->grabstate == frame_state_error) { + frame->bytes_read = 0; + return 0; + } + + PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", + __func__, + frame->index, frame->bytes_read, frame->scanlength); + + /* copy bytes to user space; we allow for partials reads */ + if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) + count = frame->scanlength - frame->bytes_read; + + if (copy_to_user(buf, frame->data + frame->bytes_read, count)) + return -EFAULT; + + frame->bytes_read += count; + PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", + __func__, + (unsigned long)count, frame->bytes_read); + + /* For now, forget the frame if it has not been read in one shot. */ +/* if (frame->bytes_read >= frame->scanlength) {*/ /* All data has been read */ + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + frame->grabstate = frame_state_unused; +/* } */ + + return count; +} + +static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int res; + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + res = usbvision_read(file, buf, count, ppos); + mutex_unlock(&usbvision->v4l2_lock); + return res; +} + +static int usbvision_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + struct usb_usbvision *usbvision = video_drvdata(file); + + PDEBUG(DBG_MMAP, "mmap"); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + if (!(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(usbvision->max_frame_size)) { + return -EINVAL; + } + + for (i = 0; i < usbvision->num_frames; i++) { + if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) == + vma->vm_pgoff) + break; + } + if (i == usbvision->num_frames) { + PDEBUG(DBG_MMAP, + "mmap: user supplied mapping address is out of range"); + return -EINVAL; + } + + /* VM_IO is eventually going to replace PageReserved altogether */ + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + + pos = usbvision->frame[i].data; + while (size > 0) { + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed"); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return 0; +} + +static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int res; + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + res = usbvision_mmap(file, vma); + mutex_unlock(&usbvision->v4l2_lock); + return res; +} + +/* + * Here comes the stuff for radio on usbvision based devices + * + */ +static int usbvision_radio_open(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code = 0; + + PDEBUG(DBG_IO, "%s:", __func__); + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + if (usbvision->user) { + dev_err(&usbvision->rdev->dev, + "%s: Someone tried to open an already opened USBVision Radio!\n", + __func__); + err_code = -EBUSY; + } else { + if (power_on_at_open) { + usbvision_reset_power_off_timer(usbvision); + if (usbvision->power == 0) { + usbvision_power_on(usbvision); + usbvision_i2c_register(usbvision); + } + } + + /* Alternate interface 1 is is the biggest frame size */ + err_code = usbvision_set_alternate(usbvision); + if (err_code < 0) { + usbvision->last_error = err_code; + err_code = -EBUSY; + goto out; + } + + /* If so far no errors then we shall start the radio */ + usbvision->radio = 1; + call_all(usbvision, tuner, s_radio); + usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); + usbvision->user++; + } + + if (err_code) { + if (power_on_at_open) { + usbvision_i2c_unregister(usbvision); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + } +out: + mutex_unlock(&usbvision->v4l2_lock); + return err_code; +} + + +static int usbvision_radio_close(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code = 0; + + PDEBUG(DBG_IO, ""); + + mutex_lock(&usbvision->v4l2_lock); + /* Set packet size to 0 */ + usbvision->iface_alt = 0; + err_code = usb_set_interface(usbvision->dev, usbvision->iface, + usbvision->iface_alt); + + usbvision_audio_off(usbvision); + usbvision->radio = 0; + usbvision->user--; + + if (power_on_at_open) { + usbvision_set_power_off_timer(usbvision); + usbvision->initialized = 0; + } + + if (usbvision->remove_pending) { + printk(KERN_INFO "%s: Final disconnect\n", __func__); + usbvision_release(usbvision); + } + + mutex_unlock(&usbvision->v4l2_lock); + PDEBUG(DBG_IO, "success"); + return err_code; +} + +/* Video registration stuff */ + +/* Video template */ +static const struct v4l2_file_operations usbvision_fops = { + .owner = THIS_MODULE, + .open = usbvision_v4l2_open, + .release = usbvision_v4l2_close, + .read = usbvision_v4l2_read, + .mmap = usbvision_v4l2_mmap, + .unlocked_ioctl = video_ioctl2, +/* .poll = video_poll, */ +}; + +static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device usbvision_video_template = { + .fops = &usbvision_fops, + .ioctl_ops = &usbvision_ioctl_ops, + .name = "usbvision-video", + .release = video_device_release, + .tvnorms = USBVISION_NORMS, + .current_norm = V4L2_STD_PAL +}; + + +/* Radio template */ +static const struct v4l2_file_operations usbvision_radio_fops = { + .owner = THIS_MODULE, + .open = usbvision_radio_open, + .release = usbvision_radio_close, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +}; + +static struct video_device usbvision_radio_template = { + .fops = &usbvision_radio_fops, + .name = "usbvision-radio", + .release = video_device_release, + .ioctl_ops = &usbvision_radio_ioctl_ops, + + .tvnorms = USBVISION_NORMS, + .current_norm = V4L2_STD_PAL +}; + + +static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, + struct video_device *vdev_template, + char *name) +{ + struct usb_device *usb_dev = usbvision->dev; + struct video_device *vdev; + + if (usb_dev == NULL) { + dev_err(&usbvision->dev->dev, + "%s: usbvision->dev is not set\n", __func__); + return NULL; + } + + vdev = video_device_alloc(); + if (NULL == vdev) + return NULL; + *vdev = *vdev_template; + vdev->lock = &usbvision->v4l2_lock; + vdev->v4l2_dev = &usbvision->v4l2_dev; + snprintf(vdev->name, sizeof(vdev->name), "%s", name); + video_set_drvdata(vdev, usbvision); + return vdev; +} + +/* unregister video4linux devices */ +static void usbvision_unregister_video(struct usb_usbvision *usbvision) +{ + /* Radio Device: */ + if (usbvision->rdev) { + PDEBUG(DBG_PROBE, "unregister %s [v4l2]", + video_device_node_name(usbvision->rdev)); + if (video_is_registered(usbvision->rdev)) + video_unregister_device(usbvision->rdev); + else + video_device_release(usbvision->rdev); + usbvision->rdev = NULL; + } + + /* Video Device: */ + if (usbvision->vdev) { + PDEBUG(DBG_PROBE, "unregister %s [v4l2]", + video_device_node_name(usbvision->vdev)); + if (video_is_registered(usbvision->vdev)) + video_unregister_device(usbvision->vdev); + else + video_device_release(usbvision->vdev); + usbvision->vdev = NULL; + } +} + +/* register video4linux devices */ +static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) +{ + /* Video Device: */ + usbvision->vdev = usbvision_vdev_init(usbvision, + &usbvision_video_template, + "USBVision Video"); + if (usbvision->vdev == NULL) + goto err_exit; + if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) + goto err_exit; + printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", + usbvision->nr, video_device_node_name(usbvision->vdev)); + + /* Radio Device: */ + if (usbvision_device_data[usbvision->dev_model].radio) { + /* usbvision has radio */ + usbvision->rdev = usbvision_vdev_init(usbvision, + &usbvision_radio_template, + "USBVision Radio"); + if (usbvision->rdev == NULL) + goto err_exit; + if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0) + goto err_exit; + printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n", + usbvision->nr, video_device_node_name(usbvision->rdev)); + } + /* all done */ + return 0; + + err_exit: + dev_err(&usbvision->dev->dev, + "USBVision[%d]: video_register_device() failed\n", + usbvision->nr); + usbvision_unregister_video(usbvision); + return -1; +} + +/* + * usbvision_alloc() + * + * This code allocates the struct usb_usbvision. + * It is filled with default values. + * + * Returns NULL on error, a pointer to usb_usbvision else. + * + */ +static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, + struct usb_interface *intf) +{ + struct usb_usbvision *usbvision; + + usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL); + if (usbvision == NULL) + return NULL; + + usbvision->dev = dev; + if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev)) + goto err_free; + + mutex_init(&usbvision->v4l2_lock); + + /* prepare control urb for control messages during interrupts */ + usbvision->ctrl_urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); + if (usbvision->ctrl_urb == NULL) + goto err_unreg; + init_waitqueue_head(&usbvision->ctrl_urb_wq); + + usbvision_init_power_off_timer(usbvision); + + return usbvision; + +err_unreg: + v4l2_device_unregister(&usbvision->v4l2_dev); +err_free: + kfree(usbvision); + return NULL; +} + +/* + * usbvision_release() + * + * This code does final release of struct usb_usbvision. This happens + * after the device is disconnected -and- all clients closed their files. + * + */ +static void usbvision_release(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_PROBE, ""); + + usbvision_reset_power_off_timer(usbvision); + + usbvision->initialized = 0; + + usbvision_remove_sysfs(usbvision->vdev); + usbvision_unregister_video(usbvision); + + usb_free_urb(usbvision->ctrl_urb); + + v4l2_device_unregister(&usbvision->v4l2_dev); + kfree(usbvision); + + PDEBUG(DBG_PROBE, "success"); +} + + +/*********************** usb interface **********************************/ + +static void usbvision_configure_video(struct usb_usbvision *usbvision) +{ + int model; + + if (usbvision == NULL) + return; + + model = usbvision->dev_model; + usbvision->palette = usbvision_v4l2_format[2]; /* V4L2_PIX_FMT_RGB24; */ + + if (usbvision_device_data[usbvision->dev_model].vin_reg2_override) { + usbvision->vin_reg2_preset = + usbvision_device_data[usbvision->dev_model].vin_reg2; + } else { + usbvision->vin_reg2_preset = 0; + } + + usbvision->tvnorm_id = usbvision_device_data[model].video_norm; + + usbvision->video_inputs = usbvision_device_data[model].video_channels; + usbvision->ctl_input = 0; + + /* This should be here to make i2c clients to be able to register */ + /* first switch off audio */ + if (usbvision_device_data[model].audio_channels > 0) + usbvision_audio_off(usbvision); + if (!power_on_at_open) { + /* and then power up the noisy tuner */ + usbvision_power_on(usbvision); + usbvision_i2c_register(usbvision); + } +} + +/* + * usbvision_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like USBVISION video device + * + */ +static int __devinit usbvision_probe(struct usb_interface *intf, + const struct usb_device_id *devid) +{ + struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf)); + struct usb_interface *uif; + __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; + const struct usb_host_interface *interface; + struct usb_usbvision *usbvision = NULL; + const struct usb_endpoint_descriptor *endpoint; + int model, i; + + PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", + dev->descriptor.idVendor, + dev->descriptor.idProduct, ifnum); + + model = devid->driver_info; + if (model < 0 || model >= usbvision_device_data_size) { + PDEBUG(DBG_PROBE, "model out of bounds %d", model); + return -ENODEV; + } + printk(KERN_INFO "%s: %s found\n", __func__, + usbvision_device_data[model].model_string); + + if (usbvision_device_data[model].interface >= 0) + interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0]; + else + interface = &dev->actconfig->interface[ifnum]->altsetting[0]; + endpoint = &interface->endpoint[1].desc; + if (!usb_endpoint_xfer_isoc(endpoint)) { + dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n", + __func__, ifnum); + dev_err(&intf->dev, "%s: Endpoint attributes %d", + __func__, endpoint->bmAttributes); + return -ENODEV; + } + if (usb_endpoint_dir_out(endpoint)) { + dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n", + __func__, ifnum); + return -ENODEV; + } + + usbvision = usbvision_alloc(dev, intf); + if (usbvision == NULL) { + dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__); + return -ENOMEM; + } + + if (dev->descriptor.bNumConfigurations > 1) + usbvision->bridge_type = BRIDGE_NT1004; + else if (model == DAZZLE_DVC_90_REV_1_SECAM) + usbvision->bridge_type = BRIDGE_NT1005; + else + usbvision->bridge_type = BRIDGE_NT1003; + PDEBUG(DBG_PROBE, "bridge_type %d", usbvision->bridge_type); + + /* compute alternate max packet sizes */ + uif = dev->actconfig->interface[0]; + + usbvision->num_alt = uif->num_altsetting; + PDEBUG(DBG_PROBE, "Alternate settings: %i", usbvision->num_alt); + usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL); + if (usbvision->alt_max_pkt_size == NULL) { + dev_err(&intf->dev, "usbvision: out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < usbvision->num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. + wMaxPacketSize); + usbvision->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + PDEBUG(DBG_PROBE, "Alternate setting %i, max size= %i", i, + usbvision->alt_max_pkt_size[i]); + } + + + usbvision->nr = usbvision_nr++; + + usbvision->have_tuner = usbvision_device_data[model].tuner; + if (usbvision->have_tuner) + usbvision->tuner_type = usbvision_device_data[model].tuner_type; + + usbvision->dev_model = model; + usbvision->remove_pending = 0; + usbvision->iface = ifnum; + usbvision->iface_alt = 0; + usbvision->video_endp = endpoint->bEndpointAddress; + usbvision->isoc_packet_size = 0; + usbvision->usb_bandwidth = 0; + usbvision->user = 0; + usbvision->streaming = stream_off; + usbvision_configure_video(usbvision); + usbvision_register_video(usbvision); + + usbvision_create_sysfs(usbvision->vdev); + + PDEBUG(DBG_PROBE, "success"); + return 0; +} + + +/* + * usbvision_disconnect() + * + * This procedure stops all driver activity, deallocates interface-private + * structure (pointed by 'ptr') and after that driver should be removable + * with no ill consequences. + * + */ +static void __devexit usbvision_disconnect(struct usb_interface *intf) +{ + struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf)); + + PDEBUG(DBG_PROBE, ""); + + if (usbvision == NULL) { + pr_err("%s: usb_get_intfdata() failed\n", __func__); + return; + } + + mutex_lock(&usbvision->v4l2_lock); + + /* At this time we ask to cancel outstanding URBs */ + usbvision_stop_isoc(usbvision); + + v4l2_device_disconnect(&usbvision->v4l2_dev); + + if (usbvision->power) { + usbvision_i2c_unregister(usbvision); + usbvision_power_off(usbvision); + } + usbvision->remove_pending = 1; /* Now all ISO data will be ignored */ + + usb_put_dev(usbvision->dev); + usbvision->dev = NULL; /* USB device is no more */ + + mutex_unlock(&usbvision->v4l2_lock); + + if (usbvision->user) { + printk(KERN_INFO "%s: In use, disconnect pending\n", + __func__); + wake_up_interruptible(&usbvision->wait_frame); + wake_up_interruptible(&usbvision->wait_stream); + } else { + usbvision_release(usbvision); + } + + PDEBUG(DBG_PROBE, "success"); +} + +static struct usb_driver usbvision_driver = { + .name = "usbvision", + .id_table = usbvision_table, + .probe = usbvision_probe, + .disconnect = __devexit_p(usbvision_disconnect), +}; + +/* + * usbvision_init() + * + * This code is run to initialize the driver. + * + */ +static int __init usbvision_init(void) +{ + int err_code; + + PDEBUG(DBG_PROBE, ""); + + PDEBUG(DBG_IO, "IO debugging is enabled [video]"); + PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]"); + PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]"); + + /* disable planar mode support unless compression enabled */ + if (isoc_mode != ISOC_MODE_COMPRESS) { + /* FIXME : not the right way to set supported flag */ + usbvision_v4l2_format[6].supported = 0; /* V4L2_PIX_FMT_YVU420 */ + usbvision_v4l2_format[7].supported = 0; /* V4L2_PIX_FMT_YUV422P */ + } + + err_code = usb_register(&usbvision_driver); + + if (err_code == 0) { + printk(KERN_INFO DRIVER_DESC " : " USBVISION_VERSION_STRING "\n"); + PDEBUG(DBG_PROBE, "success"); + } + return err_code; +} + +static void __exit usbvision_exit(void) +{ + PDEBUG(DBG_PROBE, ""); + + usb_deregister(&usbvision_driver); + PDEBUG(DBG_PROBE, "success"); +} + +module_init(usbvision_init); +module_exit(usbvision_exit); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h new file mode 100644 index 000000000000..43cf61fe4943 --- /dev/null +++ b/drivers/media/usb/usbvision/usbvision.h @@ -0,0 +1,535 @@ +/* + * USBVISION.H + * usbvision header file + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * Dwaine Garden + * + * + * Report problems to v4l MailingList: linux-media@vger.kernel.org + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * v4l2 conversion by Thierry Merle + * + * This program is free software; 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 __LINUX_USBVISION_H +#define __LINUX_USBVISION_H + +#include +#include +#include +#include +#include +#include +#include + +#define USBVISION_DEBUG /* Turn on debug messages */ + +#define USBVISION_PWR_REG 0x00 + #define USBVISION_SSPND_EN (1 << 1) + #define USBVISION_RES2 (1 << 2) + #define USBVISION_PWR_VID (1 << 5) + #define USBVISION_E2_EN (1 << 7) +#define USBVISION_CONFIG_REG 0x01 +#define USBVISION_ADRS_REG 0x02 +#define USBVISION_ALTER_REG 0x03 +#define USBVISION_FORCE_ALTER_REG 0x04 +#define USBVISION_STATUS_REG 0x05 +#define USBVISION_IOPIN_REG 0x06 + #define USBVISION_IO_1 (1 << 0) + #define USBVISION_IO_2 (1 << 1) + #define USBVISION_AUDIO_IN 0 + #define USBVISION_AUDIO_TV 1 + #define USBVISION_AUDIO_RADIO 2 + #define USBVISION_AUDIO_MUTE 3 +#define USBVISION_SER_MODE 0x07 + #define USBVISION_CLK_OUT (1 << 0) + #define USBVISION_DAT_IO (1 << 1) + #define USBVISION_SENS_OUT (1 << 2) + #define USBVISION_SER_MODE_SOFT (0 << 4) + #define USBVISION_SER_MODE_SIO (1 << 4) +#define USBVISION_SER_ADRS 0x08 +#define USBVISION_SER_CONT 0x09 +#define USBVISION_SER_DAT1 0x0A +#define USBVISION_SER_DAT2 0x0B +#define USBVISION_SER_DAT3 0x0C +#define USBVISION_SER_DAT4 0x0D +#define USBVISION_EE_DATA 0x0E +#define USBVISION_EE_LSBAD 0x0F +#define USBVISION_EE_CONT 0x10 +#define USBVISION_DRM_CONT 0x12 + #define USBVISION_REF (1 << 0) + #define USBVISION_RES_UR (1 << 2) + #define USBVISION_RES_FDL (1 << 3) + #define USBVISION_RES_VDW (1 << 4) +#define USBVISION_DRM_PRM1 0x13 +#define USBVISION_DRM_PRM2 0x14 +#define USBVISION_DRM_PRM3 0x15 +#define USBVISION_DRM_PRM4 0x16 +#define USBVISION_DRM_PRM5 0x17 +#define USBVISION_DRM_PRM6 0x18 +#define USBVISION_DRM_PRM7 0x19 +#define USBVISION_DRM_PRM8 0x1A +#define USBVISION_VIN_REG1 0x1B + #define USBVISION_8_422_SYNC 0x01 + #define USBVISION_16_422_SYNC 0x02 + #define USBVISION_VSNC_POL (1 << 3) + #define USBVISION_HSNC_POL (1 << 4) + #define USBVISION_FID_POL (1 << 5) + #define USBVISION_HVALID_PO (1 << 6) + #define USBVISION_VCLK_POL (1 << 7) +#define USBVISION_VIN_REG2 0x1C + #define USBVISION_AUTO_FID (1 << 0) + #define USBVISION_NONE_INTER (1 << 1) + #define USBVISION_NOHVALID (1 << 2) + #define USBVISION_UV_ID (1 << 3) + #define USBVISION_FIX_2C (1 << 4) + #define USBVISION_SEND_FID (1 << 5) + #define USBVISION_KEEP_BLANK (1 << 7) +#define USBVISION_LXSIZE_I 0x1D +#define USBVISION_MXSIZE_I 0x1E +#define USBVISION_LYSIZE_I 0x1F +#define USBVISION_MYSIZE_I 0x20 +#define USBVISION_LX_OFFST 0x21 +#define USBVISION_MX_OFFST 0x22 +#define USBVISION_LY_OFFST 0x23 +#define USBVISION_MY_OFFST 0x24 +#define USBVISION_FRM_RATE 0x25 +#define USBVISION_LXSIZE_O 0x26 +#define USBVISION_MXSIZE_O 0x27 +#define USBVISION_LYSIZE_O 0x28 +#define USBVISION_MYSIZE_O 0x29 +#define USBVISION_FILT_CONT 0x2A +#define USBVISION_VO_MODE 0x2B +#define USBVISION_INTRA_CYC 0x2C +#define USBVISION_STRIP_SZ 0x2D +#define USBVISION_FORCE_INTRA 0x2E +#define USBVISION_FORCE_UP 0x2F +#define USBVISION_BUF_THR 0x30 +#define USBVISION_DVI_YUV 0x31 +#define USBVISION_AUDIO_CONT 0x32 +#define USBVISION_AUD_PK_LEN 0x33 +#define USBVISION_BLK_PK_LEN 0x34 +#define USBVISION_PCM_THR1 0x38 +#define USBVISION_PCM_THR2 0x39 +#define USBVISION_DIST_THR_L 0x3A +#define USBVISION_DIST_THR_H 0x3B +#define USBVISION_MAX_DIST_L 0x3C +#define USBVISION_MAX_DIST_H 0x3D +#define USBVISION_OP_CODE 0x33 + +#define MAX_BYTES_PER_PIXEL 4 + +#define MIN_FRAME_WIDTH 64 +#define MAX_USB_WIDTH 320 /* 384 */ +#define MAX_FRAME_WIDTH 320 /* 384 */ /* streching sometimes causes crashes*/ + +#define MIN_FRAME_HEIGHT 48 +#define MAX_USB_HEIGHT 240 /* 288 */ +#define MAX_FRAME_HEIGHT 240 /* 288 */ /* Streching sometimes causes crashes*/ + +#define MAX_FRAME_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL) +#define USBVISION_CLIPMASK_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) /* bytesize of clipmask */ + +#define USBVISION_URB_FRAMES 32 + +#define USBVISION_NUM_HEADERMARKER 20 +#define USBVISION_NUMFRAMES 3 /* Maximum number of frames an application can get */ +#define USBVISION_NUMSBUF 2 /* Dimensioning the USB S buffering */ + +#define USBVISION_POWEROFF_TIME (3 * HZ) /* 3 seconds */ + + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 31 + +enum { + ISOC_MODE_YUV422 = 0x03, + ISOC_MODE_YUV420 = 0x14, + ISOC_MODE_COMPRESS = 0x60, +}; + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v, mi, ma) \ + { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my, mu, mv, mr, mg, mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc = mm_y * 76284; \ + mm_b = (mm_yc + 132252 * mm_v) >> 16; \ + mm_g = (mm_yc - 53281 * mm_u - 25625 * mm_v) >> 16; \ + mm_r = (mm_yc + 104595 * mm_u) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +/* Debugging aid */ +#define USBVISION_SAY_AND_WAIT(what) { \ + wait_queue_head_t wq; \ + init_waitqueue_head(&wq); \ + printk(KERN_INFO "Say: %s\n", what); \ + interruptible_sleep_on_timeout(&wq, HZ * 3); \ +} + +/* + * This macro checks if usbvision is still operational. The 'usbvision' + * pointer must be valid, usbvision->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define USBVISION_IS_OPERATIONAL(udevice) (\ + (udevice != NULL) && \ + ((udevice)->dev != NULL) && \ + ((udevice)->last_error == 0) && \ + (!(udevice)->remove_pending)) + +#define I2C_USB_ADAP_MAX 16 + +#define USBVISION_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_M) + +/* ----------------------------------------------------------------- */ +/* usbvision video structures */ +/* ----------------------------------------------------------------- */ +enum scan_state { + scan_state_scanning, /* Scanning for header */ + scan_state_lines /* Parsing lines */ +}; + +/* Completion states of the data parser */ +enum parse_state { + parse_state_continue, /* Just parse next item */ + parse_state_next_frame, /* Frame done, send it to V4L */ + parse_state_out, /* Not enough data for frame */ + parse_state_end_parse /* End parsing */ +}; + +enum frame_state { + frame_state_unused, /* Unused (no MCAPTURE) */ + frame_state_ready, /* Ready to start grabbing */ + frame_state_grabbing, /* In the process of being grabbed into */ + frame_state_done, /* Finished grabbing, but not been synced yet */ + frame_state_done_hold, /* Are syncing or reading */ + frame_state_error, /* Something bad happened while processing */ +}; + +/* stream states */ +enum stream_state { + stream_off, /* Driver streaming is completely OFF */ + stream_idle, /* Driver streaming is ready to be put ON by the application */ + stream_interrupt, /* Driver streaming must be interrupted */ + stream_on, /* Driver streaming is put ON by the application */ +}; + +enum isoc_state { + isoc_state_in_frame, /* Isoc packet is member of frame */ + isoc_state_no_frame, /* Isoc packet is not member of any frame */ +}; + +struct usb_device; + +struct usbvision_sbuf { + char *data; + struct urb *urb; +}; + +#define USBVISION_MAGIC_1 0x55 +#define USBVISION_MAGIC_2 0xAA +#define USBVISION_HEADER_LENGTH 0x0c +#define USBVISION_SAA7111_ADDR 0x48 +#define USBVISION_SAA7113_ADDR 0x4a +#define USBVISION_IIC_LRACK 0x20 +#define USBVISION_IIC_LRNACK 0x30 +#define USBVISION_FRAME_FORMAT_PARAM_INTRA (1<<7) + +struct usbvision_v4l2_format_st { + int supported; + int bytes_per_pixel; + int depth; + int format; + char *desc; +}; +#define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format) + +struct usbvision_frame_header { + unsigned char magic_1; /* 0 magic */ + unsigned char magic_2; /* 1 magic */ + unsigned char header_length; /* 2 */ + unsigned char frame_num; /* 3 */ + unsigned char frame_phase; /* 4 */ + unsigned char frame_latency; /* 5 */ + unsigned char data_format; /* 6 */ + unsigned char format_param; /* 7 */ + unsigned char frame_width_lo; /* 8 */ + unsigned char frame_width_hi; /* 9 */ + unsigned char frame_height_lo; /* 10 */ + unsigned char frame_height_hi; /* 11 */ + __u16 frame_width; /* 8 - 9 after endian correction*/ + __u16 frame_height; /* 10 - 11 after endian correction*/ +}; + +struct usbvision_frame { + char *data; /* Frame buffer */ + struct usbvision_frame_header isoc_header; /* Header from stream */ + + int width; /* Width application is expecting */ + int height; /* Height */ + int index; /* Frame index */ + int frmwidth; /* Width the frame actually is */ + int frmheight; /* Height */ + + volatile int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + struct list_head frame; + + int curline; /* Line of frame we're working on */ + + long scanlength; /* uncompressed, raw data length of frame */ + long bytes_read; /* amount of scanlength that has been read from data */ + struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/ + int v4l2_linesize; /* bytes for one videoline*/ + struct timeval timestamp; + int sequence; /* How many video frames we send to user */ +}; + +#define CODEC_SAA7113 7113 +#define CODEC_SAA7111 7111 +#define CODEC_WEBCAM 3000 +#define BRIDGE_NT1003 1003 +#define BRIDGE_NT1004 1004 +#define BRIDGE_NT1005 1005 + +struct usbvision_device_data_st { + __u64 video_norm; + const char *model_string; + int interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ + __u16 codec; + unsigned video_channels:3; + unsigned audio_channels:2; + unsigned radio:1; + unsigned vbi:1; + unsigned tuner:1; + unsigned vin_reg1_override:1; /* Override default value with */ + unsigned vin_reg2_override:1; /* vin_reg1, vin_reg2, etc. */ + unsigned dvi_yuv_override:1; + __u8 vin_reg1; + __u8 vin_reg2; + __u8 dvi_yuv; + __u8 tuner_type; + __s16 x_offset; + __s16 y_offset; +}; + +/* Declared on usbvision-cards.c */ +extern struct usbvision_device_data_st usbvision_device_data[]; +extern struct usb_device_id usbvision_table[]; + +struct usb_usbvision { + struct v4l2_device v4l2_dev; + struct video_device *vdev; /* Video Device */ + struct video_device *rdev; /* Radio Device */ + + /* i2c Declaration Section*/ + struct i2c_adapter i2c_adap; + int registered_i2c; + + struct urb *ctrl_urb; + unsigned char ctrl_urb_buffer[8]; + int ctrl_urb_busy; + struct usb_ctrlrequest ctrl_urb_setup; + wait_queue_head_t ctrl_urb_wq; /* Processes waiting */ + + /* configuration part */ + int have_tuner; + int tuner_type; + int bridge_type; /* NT1003, NT1004, NT1005 */ + int radio; + int video_inputs; /* # of inputs */ + unsigned long freq; + int audio_mute; + int audio_channel; + int isoc_mode; /* format of video data for the usb isoc-transfer */ + unsigned int nr; /* Number of the device */ + + /* Device structure */ + struct usb_device *dev; + /* usb transfer */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of max_packet_size */ + unsigned char iface; /* Video interface number */ + unsigned char iface_alt; /* Alt settings */ + unsigned char vin_reg2_preset; + struct mutex v4l2_lock; + struct timer_list power_off_timer; + struct work_struct power_off_work; + int power; /* is the device powered on? */ + int user; /* user count for exclusive use */ + int initialized; /* Had we already sent init sequence? */ + int dev_model; /* What type of USBVISION device we got? */ + enum stream_state streaming; /* Are we streaming Isochronous? */ + int last_error; /* What calamity struck us? */ + int curwidth; /* width of the frame the device is currently set to*/ + int curheight; /* height of the frame the device is currently set to*/ + int stretch_width; /* stretch-factor for frame width (from usb to screen)*/ + int stretch_height; /* stretch-factor for frame height (from usb to screen)*/ + char *fbuf; /* Videodev buffer area for mmap*/ + int max_frame_size; /* Bytes in one video frame */ + int fbuf_size; /* Videodev buffer size */ + spinlock_t queue_lock; /* spinlock for protecting mods on inqueue and outqueue */ + struct list_head inqueue, outqueue; /* queued frame list and ready to dequeue frame list */ + wait_queue_head_t wait_frame; /* Processes waiting */ + wait_queue_head_t wait_stream; /* Processes waiting */ + struct usbvision_frame *cur_frame; /* pointer to current frame, set by usbvision_find_header */ + struct usbvision_frame frame[USBVISION_NUMFRAMES]; /* frame buffer */ + int num_frames; /* number of frames allocated */ + struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; /* S buffering */ + volatile int remove_pending; /* If set then about to exit */ + + /* Scratch space from the Isochronous Pipe.*/ + unsigned char *scratch; + int scratch_read_ptr; + int scratch_write_ptr; + int scratch_headermarker[USBVISION_NUM_HEADERMARKER]; + int scratch_headermarker_read_ptr; + int scratch_headermarker_write_ptr; + enum isoc_state isocstate; + struct usbvision_v4l2_format_st palette; + + struct v4l2_capability vcap; /* Video capabilities */ + unsigned int ctl_input; /* selected input */ + v4l2_std_id tvnorm_id; /* selected tv norm */ + unsigned char video_endp; /* 0x82 for USBVISION devices based */ + + /* Decompression stuff: */ + unsigned char *intra_frame_buffer; /* Buffer for reference frame */ + int block_pos; /* for test only */ + int request_intra; /* 0 = normal; 1 = intra frame is requested; */ + int last_isoc_frame_num; /* check for lost isoc frames */ + int isoc_packet_size; /* need to calculate used_bandwidth */ + int used_bandwidth; /* used bandwidth 0-100%, need to set compr_level */ + int compr_level; /* How strong (100) or weak (0) is compression */ + int last_compr_level; /* How strong (100) or weak (0) was compression */ + int usb_bandwidth; /* Mbit/s */ + + /* Statistics that can be overlayed on the screen */ + unsigned long isoc_urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long isoc_data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long scratch_ovf_count; /* How many times we overflowed scratch */ + unsigned long isoc_skip_count; /* How many empty ISO packets received */ + unsigned long isoc_err_count; /* How many bad ISO packets received */ + unsigned long isoc_packet_count; /* How many packets we totally got */ + unsigned long time_in_irq; /* How long do we need for interrupt */ + int isoc_measure_bandwidth_count; + int frame_num; /* How many video frames we send to user */ + int max_strip_len; /* How big is the biggest strip */ + int comprblock_pos; + int strip_len_errors; /* How many times was block_pos greater than strip_len */ + int strip_magic_errors; + int strip_line_number_errors; + int compr_block_types[4]; +}; + +static inline struct usb_usbvision *to_usbvision(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct usb_usbvision, v4l2_dev); +} + +#define call_all(usbvision, o, f, args...) \ + v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args) + +/* --------------------------------------------------------------- */ +/* defined in usbvision-i2c.c */ +/* i2c-algo-usb declaration */ +/* --------------------------------------------------------------- */ + +/* ----------------------------------------------------------------------- */ +/* usbvision specific I2C functions */ +/* ----------------------------------------------------------------------- */ +int usbvision_i2c_register(struct usb_usbvision *usbvision); +int usbvision_i2c_unregister(struct usb_usbvision *usbvision); + +/* defined in usbvision-core.c */ +int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); +int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, + unsigned char value); + +int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames); +void usbvision_frames_free(struct usb_usbvision *usbvision); +int usbvision_scratch_alloc(struct usb_usbvision *usbvision); +void usbvision_scratch_free(struct usb_usbvision *usbvision); +int usbvision_decompress_alloc(struct usb_usbvision *usbvision); +void usbvision_decompress_free(struct usb_usbvision *usbvision); + +int usbvision_setup(struct usb_usbvision *usbvision, int format); +int usbvision_init_isoc(struct usb_usbvision *usbvision); +int usbvision_restart_isoc(struct usb_usbvision *usbvision); +void usbvision_stop_isoc(struct usb_usbvision *usbvision); +int usbvision_set_alternate(struct usb_usbvision *dev); + +int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel); +int usbvision_audio_off(struct usb_usbvision *usbvision); + +int usbvision_begin_streaming(struct usb_usbvision *usbvision); +void usbvision_empty_framequeues(struct usb_usbvision *dev); +int usbvision_stream_interrupt(struct usb_usbvision *dev); + +int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); +int usbvision_set_input(struct usb_usbvision *usbvision); +int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); + +void usbvision_init_power_off_timer(struct usb_usbvision *usbvision); +void usbvision_set_power_off_timer(struct usb_usbvision *usbvision); +void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision); +int usbvision_power_off(struct usb_usbvision *usbvision); +int usbvision_power_on(struct usb_usbvision *usbvision); + +#endif /* __LINUX_USBVISION_H */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/usb/uvc/Kconfig b/drivers/media/usb/uvc/Kconfig new file mode 100644 index 000000000000..541c9f1e4c6a --- /dev/null +++ b/drivers/media/usb/uvc/Kconfig @@ -0,0 +1,19 @@ +config USB_VIDEO_CLASS + tristate "USB Video Class (UVC)" + select VIDEOBUF2_VMALLOC + ---help--- + Support for the USB Video Class (UVC). Currently only video + input devices, such as webcams, are supported. + + For more information see: + +config USB_VIDEO_CLASS_INPUT_EVDEV + bool "UVC input events device support" + default y + depends on USB_VIDEO_CLASS + depends on USB_VIDEO_CLASS=INPUT || INPUT=y + ---help--- + This option makes USB Video Class devices register an input device + to report button events. + + If you are in doubt, say Y. diff --git a/drivers/media/usb/uvc/Makefile b/drivers/media/usb/uvc/Makefile new file mode 100644 index 000000000000..c26d12fdb8f4 --- /dev/null +++ b/drivers/media/usb/uvc/Makefile @@ -0,0 +1,6 @@ +uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ + uvc_status.o uvc_isight.o uvc_debugfs.o +ifeq ($(CONFIG_MEDIA_CONTROLLER),y) +uvcvideo-objs += uvc_entity.o +endif +obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c new file mode 100644 index 000000000000..f7061a5ef1d2 --- /dev/null +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -0,0 +1,2165 @@ +/* + * uvc_ctrl.c -- USB Video Class driver - Controls + * + * Copyright (C) 2005-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uvcvideo.h" + +#define UVC_CTRL_DATA_CURRENT 0 +#define UVC_CTRL_DATA_BACKUP 1 +#define UVC_CTRL_DATA_MIN 2 +#define UVC_CTRL_DATA_MAX 3 +#define UVC_CTRL_DATA_RES 4 +#define UVC_CTRL_DATA_DEF 5 +#define UVC_CTRL_DATA_LAST 6 + +/* ------------------------------------------------------------------------ + * Controls + */ + +static struct uvc_control_info uvc_ctrls[] = { + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_BRIGHTNESS_CONTROL, + .index = 0, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_CONTRAST_CONTROL, + .index = 1, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_HUE_CONTROL, + .index = 2, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_SATURATION_CONTROL, + .index = 3, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_SHARPNESS_CONTROL, + .index = 4, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_GAMMA_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .index = 6, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, + .index = 7, + .size = 4, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, + .index = 8, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_GAIN_CONTROL, + .index = 9, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .index = 10, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_HUE_AUTO_CONTROL, + .index = 11, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .index = 12, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .index = 13, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL, + .index = 14, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, + .index = 15, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL, + .index = 16, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_CUR, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL, + .index = 17, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_CUR, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_SCANNING_MODE_CONTROL, + .index = 0, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_AE_MODE_CONTROL, + .index = 1, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_AE_PRIORITY_CONTROL, + .index = 2, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .index = 3, + .size = 4, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL, + .index = 4, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_FOCUS_RELATIVE_CONTROL, + .index = 6, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, + .index = 7, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_RELATIVE_CONTROL, + .index = 8, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, + .index = 9, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, + .index = 10, + .size = 3, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .index = 11, + .size = 8, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, + .index = 12, + .size = 4, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_ROLL_ABSOLUTE_CONTROL, + .index = 13, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR + | UVC_CTRL_FLAG_GET_RANGE + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_ROLL_RELATIVE_CONTROL, + .index = 14, + .size = 2, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_FOCUS_AUTO_CONTROL, + .index = 17, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PRIVACY_CONTROL, + .index = 18, + .size = 1, + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_RESTORE + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, +}; + +static struct uvc_menu_info power_line_frequency_controls[] = { + { 0, "Disabled" }, + { 1, "50 Hz" }, + { 2, "60 Hz" }, +}; + +static struct uvc_menu_info exposure_auto_controls[] = { + { 2, "Auto Mode" }, + { 1, "Manual Mode" }, + { 4, "Shutter Priority Mode" }, + { 8, "Aperture Priority Mode" }, +}; + +static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) +{ + __s8 zoom = (__s8)data[0]; + + switch (query) { + case UVC_GET_CUR: + return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); + + case UVC_GET_MIN: + case UVC_GET_MAX: + case UVC_GET_RES: + case UVC_GET_DEF: + default: + return data[2]; + } +} + +static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) +{ + data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; + data[2] = min((int)abs(value), 0xff); +} + +static struct uvc_control_mapping uvc_ctrl_mappings[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_BRIGHTNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_CONTRAST_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_HUE, + .name = "Hue", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_HUE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_HUE_AUTO, + .master_manual = 0, + }, + { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_SATURATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_SHARPNESS, + .name = "Sharpness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_SHARPNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAMMA, + .name = "Gamma", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_GAMMA_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .name = "Backlight Compensation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAIN, + .name = "Gain", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_GAIN_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .name = "Power Line Frequency", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_count = ARRAY_SIZE(power_line_frequency_controls), + }, + { + .id = V4L2_CID_HUE_AUTO, + .name = "Hue, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_HUE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_HUE, }, + }, + { + .id = V4L2_CID_EXPOSURE_AUTO, + .name = "Exposure, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_AE_MODE_CONTROL, + .size = 4, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_BITMASK, + .menu_info = exposure_auto_controls, + .menu_count = ARRAY_SIZE(exposure_auto_controls), + .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, }, + }, + { + .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, + .name = "Exposure, Auto Priority", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_AE_PRIORITY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_EXPOSURE_ABSOLUTE, + .name = "Exposure (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_EXPOSURE_AUTO, + .master_manual = V4L2_EXPOSURE_MANUAL, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Temperature, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, }, + }, + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .name = "White Balance Temperature", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Component, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_BLUE_BALANCE, + V4L2_CID_RED_BALANCE }, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .name = "White Balance Blue Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .name = "White Balance Red Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, + }, + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .name = "Focus (absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_FOCUS_AUTO, + .master_manual = 0, + }, + { + .id = V4L2_CID_FOCUS_AUTO, + .name = "Focus, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_FOCUS_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_FOCUS_ABSOLUTE, }, + }, + { + .id = V4L2_CID_IRIS_ABSOLUTE, + .name = "Iris, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_IRIS_RELATIVE, + .name = "Iris, Relative", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_RELATIVE_CONTROL, + .size = 8, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_ZOOM_ABSOLUTE, + .name = "Zoom, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_ZOOM_CONTINUOUS, + .name = "Zoom, Continuous", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, + .size = 0, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .get = uvc_ctrl_get_zoom, + .set = uvc_ctrl_set_zoom, + }, + { + .id = V4L2_CID_PAN_ABSOLUTE, + .name = "Pan (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_ABSOLUTE, + .name = "Tilt (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 32, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_PRIVACY, + .name = "Privacy", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PRIVACY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) +{ + return ctrl->uvc_data + id * ctrl->info.size; +} + +static inline int uvc_test_bit(const __u8 *data, int bit) +{ + return (data[bit >> 3] >> (bit & 7)) & 1; +} + +static inline void uvc_clear_bit(__u8 *data, int bit) +{ + data[bit >> 3] &= ~(1 << (bit & 7)); +} + +/* Extract the bit string specified by mapping->offset and mapping->size + * from the little-endian data stored at 'data' and return the result as + * a signed 32bit integer. Sign extension will be performed if the mapping + * references a signed data type. + */ +static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) +{ + int bits = mapping->size; + int offset = mapping->offset; + __s32 value = 0; + __u8 mask; + + data += offset / 8; + offset &= 7; + mask = ((1LL << bits) - 1) << offset; + + for (; bits > 0; data++) { + __u8 byte = *data & mask; + value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); + bits -= 8 - (offset > 0 ? offset : 0); + offset -= 8; + mask = (1 << bits) - 1; + } + + /* Sign-extend the value if needed. */ + if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) + value |= -(value & (1 << (mapping->size - 1))); + + return value; +} + +/* Set the bit string specified by mapping->offset and mapping->size + * in the little-endian data stored at 'data' to the value 'value'. + */ +static void uvc_set_le_value(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) +{ + int bits = mapping->size; + int offset = mapping->offset; + __u8 mask; + + /* According to the v4l2 spec, writing any value to a button control + * should result in the action belonging to the button control being + * triggered. UVC devices however want to see a 1 written -> override + * value. + */ + if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON) + value = -1; + + data += offset / 8; + offset &= 7; + + for (; bits > 0; data++) { + mask = ((1LL << bits) - 1) << offset; + *data = (*data & ~mask) | ((value << offset) & mask); + value >>= offset ? offset : 8; + bits -= 8 - offset; + offset = 0; + } +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; +static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; +static const __u8 uvc_media_transport_input_guid[16] = + UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; + +static int uvc_entity_match_guid(const struct uvc_entity *entity, + const __u8 guid[16]) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case UVC_ITT_CAMERA: + return memcmp(uvc_camera_guid, guid, 16) == 0; + + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; + + case UVC_VC_PROCESSING_UNIT: + return memcmp(uvc_processing_guid, guid, 16) == 0; + + case UVC_VC_EXTENSION_UNIT: + return memcmp(entity->extension.guidExtensionCode, + guid, 16) == 0; + + default: + return 0; + } +} + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, + struct uvc_control_mapping **mapping, struct uvc_control **control, + int next) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *map; + unsigned int i; + + if (entity == NULL) + return; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (!ctrl->initialized) + continue; + + list_for_each_entry(map, &ctrl->info.mappings, list) { + if ((map->id == v4l2_id) && !next) { + *control = ctrl; + *mapping = map; + return; + } + + if ((*mapping == NULL || (*mapping)->id > map->id) && + (map->id > v4l2_id) && next) { + *control = ctrl; + *mapping = map; + } + } + } +} + +static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, + __u32 v4l2_id, struct uvc_control_mapping **mapping) +{ + struct uvc_control *ctrl = NULL; + struct uvc_entity *entity; + int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL; + + *mapping = NULL; + + /* Mask the query flags. */ + v4l2_id &= V4L2_CTRL_ID_MASK; + + /* Find the control. */ + list_for_each_entry(entity, &chain->entities, chain) { + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + } + + if (ctrl == NULL && !next) + uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", + v4l2_id); + + return ctrl; +} + +static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, + struct uvc_control *ctrl) +{ + int ret; + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF), + ctrl->info.size); + if (ret < 0) + return ret; + } + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN), + ctrl->info.size); + if (ret < 0) + return ret; + } + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX), + ctrl->info.size); + if (ret < 0) + return ret; + } + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), + ctrl->info.size); + if (ret < 0) { + if (UVC_ENTITY_TYPE(ctrl->entity) != + UVC_VC_EXTENSION_UNIT) + return ret; + + /* GET_RES is mandatory for XU controls, but some + * cameras still choke on it. Ignore errors and set the + * resolution value to zero. + */ + uvc_warn_once(chain->dev, UVC_WARN_XU_GET_RES, + "UVC non compliance - GET_RES failed on " + "an XU control. Enabling workaround.\n"); + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0, + ctrl->info.size); + } + } + + ctrl->cached = 1; + return 0; +} + +static int __uvc_ctrl_get(struct uvc_video_chain *chain, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 *value) +{ + struct uvc_menu_info *menu; + unsigned int i; + int ret; + + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + return -EINVAL; + + if (!ctrl->loaded) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + if (ret < 0) + return ret; + + ctrl->loaded = 1; + } + + *value = mapping->get(mapping, UVC_GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == *value) { + *value = i; + break; + } + } + } + + return 0; +} + +static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control_mapping *master_map = NULL; + struct uvc_control *master_ctrl = NULL; + struct uvc_menu_info *menu; + unsigned int i; + + memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); + v4l2_ctrl->id = mapping->id; + v4l2_ctrl->type = mapping->v4l2_type; + strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); + v4l2_ctrl->flags = 0; + + if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (mapping->master_id) + __uvc_find_control(ctrl->entity, mapping->master_id, + &master_map, &master_ctrl, 0); + if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) { + s32 val; + int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); + if (ret < 0) + return ret; + + if (val != mapping->master_manual) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } + + if (!ctrl->cached) { + int ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + return ret; + } + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { + v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF)); + } + + switch (mapping->v4l2_type) { + case V4L2_CTRL_TYPE_MENU: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = mapping->menu_count - 1; + v4l2_ctrl->step = 1; + + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == v4l2_ctrl->default_value) { + v4l2_ctrl->default_value = i; + break; + } + } + + return 0; + + case V4L2_CTRL_TYPE_BOOLEAN: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = 1; + v4l2_ctrl->step = 1; + return 0; + + case V4L2_CTRL_TYPE_BUTTON: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = 0; + v4l2_ctrl->step = 0; + return 0; + + default: + break; + } + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) + v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) + v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + + return 0; +} + +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + int ret; + + ret = mutex_lock_interruptible(&chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl); +done: + mutex_unlock(&chain->ctrl_mutex); + return ret; +} + +/* + * Mapping V4L2 controls to UVC controls can be straighforward if done well. + * Most of the UVC controls exist in V4L2, and can be mapped directly. Some + * must be grouped (for instance the Red Balance, Blue Balance and Do White + * Balance V4L2 controls use the White Balance Component UVC control) or + * otherwise translated. The approach we take here is to use a translation + * table for the controls that can be mapped directly, and handle the others + * manually. + */ +int uvc_query_v4l2_menu(struct uvc_video_chain *chain, + struct v4l2_querymenu *query_menu) +{ + struct uvc_menu_info *menu_info; + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + u32 index = query_menu->index; + u32 id = query_menu->id; + int ret; + + memset(query_menu, 0, sizeof(*query_menu)); + query_menu->id = id; + query_menu->index = index; + + ret = mutex_lock_interruptible(&chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(chain, query_menu->id, &mapping); + if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) { + ret = -EINVAL; + goto done; + } + + if (query_menu->index >= mapping->menu_count) { + ret = -EINVAL; + goto done; + } + + menu_info = &mapping->menu_info[query_menu->index]; + + if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && + (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { + s32 bitmap; + + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + goto done; + } + + bitmap = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + if (!(bitmap & menu_info->value)) { + ret = -EINVAL; + goto done; + } + } + + strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name); + +done: + mutex_unlock(&chain->ctrl_mutex); + return ret; +} + +/* -------------------------------------------------------------------------- + * Ctrl event handling + */ + +static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, + struct v4l2_event *ev, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_queryctrl v4l2_ctrl; + + __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); + + memset(ev->reserved, 0, sizeof(ev->reserved)); + ev->type = V4L2_EVENT_CTRL; + ev->id = v4l2_ctrl.id; + ev->u.ctrl.value = value; + ev->u.ctrl.changes = changes; + ev->u.ctrl.type = v4l2_ctrl.type; + ev->u.ctrl.flags = v4l2_ctrl.flags; + ev->u.ctrl.minimum = v4l2_ctrl.minimum; + ev->u.ctrl.maximum = v4l2_ctrl.maximum; + ev->u.ctrl.step = v4l2_ctrl.step; + ev->u.ctrl.default_value = v4l2_ctrl.default_value; +} + +static void uvc_ctrl_send_event(struct uvc_fh *handle, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_subscribed_event *sev; + struct v4l2_event ev; + + if (list_empty(&mapping->ev_subs)) + return; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + + list_for_each_entry(sev, &mapping->ev_subs, node) { + if (sev->fh && (sev->fh != &handle->vfh || + (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || + (changes & V4L2_EVENT_CTRL_CH_FLAGS))) + v4l2_event_queue_fh(sev->fh, &ev); + } +} + +static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, + struct uvc_control *master, u32 slave_id, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping = NULL; + struct uvc_control *ctrl = NULL; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + unsigned int i; + s32 val = 0; + + /* + * We can skip sending an event for the slave if the slave + * is being modified in the same transaction. + */ + for (i = 0; i < xctrls_count; i++) { + if (xctrls[i].id == slave_id) + return; + } + + __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); + if (ctrl == NULL) + return; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); +} + +static void uvc_ctrl_send_events(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + u32 changes = V4L2_EVENT_CTRL_CH_VALUE; + unsigned int i; + unsigned int j; + + for (i = 0; i < xctrls_count; ++i) { + ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + + for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { + if (!mapping->slave_ids[j]) + break; + uvc_ctrl_send_slave_event(handle, ctrl, + mapping->slave_ids[j], + xctrls, xctrls_count); + } + + /* + * If the master is being modified in the same transaction + * flags may change too. + */ + if (mapping->master_id) { + for (j = 0; j < xctrls_count; j++) { + if (xctrls[j].id == mapping->master_id) { + changes |= V4L2_EVENT_CTRL_CH_FLAGS; + break; + } + } + } + + uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, + changes); + } +} + +static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + int ret; + + ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(handle->chain, sev->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + list_add_tail(&sev->node, &mapping->ev_subs); + if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) { + struct v4l2_event ev; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + s32 val = 0; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, + changes); + /* Mark the queue as active, allowing this initial + event to be accepted. */ + sev->elems = elems; + v4l2_event_queue_fh(sev->fh, &ev); + } + +done: + mutex_unlock(&handle->chain->ctrl_mutex); + return ret; +} + +static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + + mutex_lock(&handle->chain->ctrl_mutex); + list_del(&sev->node); + mutex_unlock(&handle->chain->ctrl_mutex); +} + +const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = { + .add = uvc_ctrl_add_event, + .del = uvc_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; + +/* -------------------------------------------------------------------------- + * Control transactions + * + * To make extended set operations as atomic as the hardware allows, controls + * are handled using begin/commit/rollback operations. + * + * At the beginning of a set request, uvc_ctrl_begin should be called to + * initialize the request. This function acquires the control lock. + * + * When setting a control, the new value is stored in the control data field + * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for + * later processing. If the UVC and V4L2 control sizes differ, the current + * value is loaded from the hardware before storing the new value in the data + * field. + * + * After processing all controls in the transaction, uvc_ctrl_commit or + * uvc_ctrl_rollback must be called to apply the pending changes to the + * hardware or revert them. When applying changes, all controls marked as + * dirty will be modified in the UVC device, and the dirty flag will be + * cleared. When reverting controls, the control data field + * UVC_CTRL_DATA_CURRENT is reverted to its previous value + * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the + * control lock. + */ +int uvc_ctrl_begin(struct uvc_video_chain *chain) +{ + return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0; +} + +static int uvc_ctrl_commit_entity(struct uvc_device *dev, + struct uvc_entity *entity, int rollback) +{ + struct uvc_control *ctrl; + unsigned int i; + int ret; + + if (entity == NULL) + return 0; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (!ctrl->initialized) + continue; + + /* Reset the loaded flag for auto-update controls that were + * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent + * uvc_ctrl_get from using the cached value, and for write-only + * controls to prevent uvc_ctrl_set from setting bits not + * explicitly set by the user. + */ + if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE || + !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) + ctrl->loaded = 0; + + if (!ctrl->dirty) + continue; + + if (!rollback) + ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id, + dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + else + ret = 0; + + if (rollback || ret < 0) + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + ctrl->info.size); + + ctrl->dirty = 0; + + if (ret < 0) + return ret; + } + + return 0; +} + +int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) +{ + struct uvc_video_chain *chain = handle->chain; + struct uvc_entity *entity; + int ret = 0; + + /* Find the control. */ + list_for_each_entry(entity, &chain->entities, chain) { + ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); + if (ret < 0) + goto done; + } + + if (!rollback) + uvc_ctrl_send_events(handle, xctrls, xctrls_count); +done: + mutex_unlock(&chain->ctrl_mutex); + return ret; +} + +int uvc_ctrl_get(struct uvc_video_chain *chain, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + + ctrl = uvc_find_control(chain, xctrl->id, &mapping); + if (ctrl == NULL) + return -EINVAL; + + return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); +} + +int uvc_ctrl_set(struct uvc_video_chain *chain, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + s32 value; + u32 step; + s32 min; + s32 max; + int ret; + + ctrl = uvc_find_control(chain, xctrl->id, &mapping); + if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0) + return -EINVAL; + + /* Clamp out of range values. */ + switch (mapping->v4l2_type) { + case V4L2_CTRL_TYPE_INTEGER: + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + return ret; + } + + min = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); + max = mapping->get(mapping, UVC_GET_MAX, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + step = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + if (step == 0) + step = 1; + + xctrl->value = min + (xctrl->value - min + step/2) / step * step; + xctrl->value = clamp(xctrl->value, min, max); + value = xctrl->value; + break; + + case V4L2_CTRL_TYPE_BOOLEAN: + xctrl->value = clamp(xctrl->value, 0, 1); + value = xctrl->value; + break; + + case V4L2_CTRL_TYPE_MENU: + if (xctrl->value < 0 || xctrl->value >= mapping->menu_count) + return -ERANGE; + value = mapping->menu_info[xctrl->value].value; + + /* Valid menu indices are reported by the GET_RES request for + * UVC controls that support it. + */ + if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && + (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + return ret; + } + + step = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + if (!(step & value)) + return -ERANGE; + } + + break; + + default: + value = xctrl->value; + break; + } + + /* If the mapping doesn't span the whole UVC control, the current value + * needs to be loaded from the device to perform the read-modify-write + * operation. + */ + if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) { + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) { + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + 0, ctrl->info.size); + } else { + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + ctrl->entity->id, chain->dev->intfnum, + ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + if (ret < 0) + return ret; + } + + ctrl->loaded = 1; + } + + /* Backup the current value in case we need to rollback later. */ + if (!ctrl->dirty) { + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + } + + mapping->set(mapping, value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + + ctrl->dirty = 1; + ctrl->modified = 1; + return 0; +} + +/* -------------------------------------------------------------------------- + * Dynamic controls + */ + +static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev, + const struct uvc_control *ctrl, struct uvc_control_info *info) +{ + struct uvc_ctrl_fixup { + struct usb_device_id id; + u8 entity; + u8 selector; + u8 flags; + }; + + static const struct uvc_ctrl_fixup fixups[] = { + { { USB_DEVICE(0x046d, 0x08c2) }, 9, 1, + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE }, + { { USB_DEVICE(0x046d, 0x08cc) }, 9, 1, + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE }, + { { USB_DEVICE(0x046d, 0x0994) }, 9, 1, + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE }, + }; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fixups); ++i) { + if (!usb_match_one_id(dev->intf, &fixups[i].id)) + continue; + + if (fixups[i].entity == ctrl->entity->id && + fixups[i].selector == info->selector) { + info->flags = fixups[i].flags; + return; + } + } +} + +/* + * Query control information (size and flags) for XU controls. + */ +static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, + const struct uvc_control *ctrl, struct uvc_control_info *info) +{ + u8 *data; + int ret; + + data = kmalloc(2, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + memcpy(info->entity, ctrl->entity->extension.guidExtensionCode, + sizeof(info->entity)); + info->index = ctrl->index; + info->selector = ctrl->index + 1; + + /* Query and verify the control length (GET_LEN) */ + ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, + info->selector, data, 2); + if (ret < 0) { + uvc_trace(UVC_TRACE_CONTROL, + "GET_LEN failed on control %pUl/%u (%d).\n", + info->entity, info->selector, ret); + goto done; + } + + info->size = le16_to_cpup((__le16 *)data); + + /* Query the control information (GET_INFO) */ + ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, + info->selector, data, 1); + if (ret < 0) { + uvc_trace(UVC_TRACE_CONTROL, + "GET_INFO failed on control %pUl/%u (%d).\n", + info->entity, info->selector, ret); + goto done; + } + + info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF + | (data[0] & UVC_CONTROL_CAP_GET ? + UVC_CTRL_FLAG_GET_CUR : 0) + | (data[0] & UVC_CONTROL_CAP_SET ? + UVC_CTRL_FLAG_SET_CUR : 0) + | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? + UVC_CTRL_FLAG_AUTO_UPDATE : 0); + + uvc_ctrl_fixup_xu_info(dev, ctrl, info); + + uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " + "flags { get %u set %u auto %u }.\n", + info->entity, info->selector, info->size, + (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0, + (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0, + (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0); + +done: + kfree(data); + return ret; +} + +static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, + const struct uvc_control_info *info); + +static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, + struct uvc_control *ctrl) +{ + struct uvc_control_info info; + int ret; + + if (ctrl->initialized) + return 0; + + ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info); + if (ret < 0) + return ret; + + ret = uvc_ctrl_add_info(dev, ctrl, &info); + if (ret < 0) + uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control " + "%pUl/%u on device %s entity %u\n", info.entity, + info.selector, dev->udev->devpath, ctrl->entity->id); + + return ret; +} + +int uvc_xu_ctrl_query(struct uvc_video_chain *chain, + struct uvc_xu_control_query *xqry) +{ + struct uvc_entity *entity; + struct uvc_control *ctrl; + unsigned int i, found = 0; + __u32 reqflags; + __u16 size; + __u8 *data = NULL; + int ret; + + /* Find the extension unit. */ + list_for_each_entry(entity, &chain->entities, chain) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && + entity->id == xqry->unit) + break; + } + + if (entity->id != xqry->unit) { + uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", + xqry->unit); + return -ENOENT; + } + + /* Find the control and perform delayed initialization if needed. */ + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->index == xqry->selector - 1) { + found = 1; + break; + } + } + + if (!found) { + uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", + entity->extension.guidExtensionCode, xqry->selector); + return -ENOENT; + } + + if (mutex_lock_interruptible(&chain->ctrl_mutex)) + return -ERESTARTSYS; + + ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl); + if (ret < 0) { + ret = -ENOENT; + goto done; + } + + /* Validate the required buffer size and flags for the request */ + reqflags = 0; + size = ctrl->info.size; + + switch (xqry->query) { + case UVC_GET_CUR: + reqflags = UVC_CTRL_FLAG_GET_CUR; + break; + case UVC_GET_MIN: + reqflags = UVC_CTRL_FLAG_GET_MIN; + break; + case UVC_GET_MAX: + reqflags = UVC_CTRL_FLAG_GET_MAX; + break; + case UVC_GET_DEF: + reqflags = UVC_CTRL_FLAG_GET_DEF; + break; + case UVC_GET_RES: + reqflags = UVC_CTRL_FLAG_GET_RES; + break; + case UVC_SET_CUR: + reqflags = UVC_CTRL_FLAG_SET_CUR; + break; + case UVC_GET_LEN: + size = 2; + break; + case UVC_GET_INFO: + size = 1; + break; + default: + ret = -EINVAL; + goto done; + } + + if (size != xqry->size) { + ret = -ENOBUFS; + goto done; + } + + if (reqflags && !(ctrl->info.flags & reqflags)) { + ret = -EBADRQC; + goto done; + } + + data = kmalloc(size, GFP_KERNEL); + if (data == NULL) { + ret = -ENOMEM; + goto done; + } + + if (xqry->query == UVC_SET_CUR && + copy_from_user(data, xqry->data, size)) { + ret = -EFAULT; + goto done; + } + + ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit, + chain->dev->intfnum, xqry->selector, data, size); + if (ret < 0) + goto done; + + if (xqry->query != UVC_SET_CUR && + copy_to_user(xqry->data, data, size)) + ret = -EFAULT; +done: + kfree(data); + mutex_unlock(&chain->ctrl_mutex); + return ret; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Restore control values after resume, skipping controls that haven't been + * changed. + * + * TODO + * - Don't restore modified controls that are back to their default value. + * - Handle restore order (Auto-Exposure Mode should be restored before + * Exposure Time). + */ +int uvc_ctrl_resume_device(struct uvc_device *dev) +{ + struct uvc_control *ctrl; + struct uvc_entity *entity; + unsigned int i; + int ret; + + /* Walk the entities list and restore controls when possible. */ + list_for_each_entry(entity, &dev->entities, list) { + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + + if (!ctrl->initialized || !ctrl->modified || + (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0) + continue; + + printk(KERN_INFO "restoring control %pUl/%u/%u\n", + ctrl->info.entity, ctrl->info.index, + ctrl->info.selector); + ctrl->dirty = 1; + } + + ret = uvc_ctrl_commit_entity(dev, entity, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Control and mapping handling + */ + +/* + * Add control information to a given control. + */ +static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, + const struct uvc_control_info *info) +{ + int ret = 0; + + memcpy(&ctrl->info, info, sizeof(*info)); + INIT_LIST_HEAD(&ctrl->info.mappings); + + /* Allocate an array to save control values (cur, def, max, etc.) */ + ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + 1, + GFP_KERNEL); + if (ctrl->uvc_data == NULL) { + ret = -ENOMEM; + goto done; + } + + ctrl->initialized = 1; + + uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " + "entity %u\n", ctrl->info.entity, ctrl->info.selector, + dev->udev->devpath, ctrl->entity->id); + +done: + if (ret < 0) + kfree(ctrl->uvc_data); + return ret; +} + +/* + * Add a control mapping to a given control. + */ +static int __uvc_ctrl_add_mapping(struct uvc_device *dev, + struct uvc_control *ctrl, const struct uvc_control_mapping *mapping) +{ + struct uvc_control_mapping *map; + unsigned int size; + + /* Most mappings come from static kernel data and need to be duplicated. + * Mappings that come from userspace will be unnecessarily duplicated, + * this could be optimized. + */ + map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&map->ev_subs); + + size = sizeof(*mapping->menu_info) * mapping->menu_count; + map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); + if (map->menu_info == NULL) { + kfree(map); + return -ENOMEM; + } + + if (map->get == NULL) + map->get = uvc_get_le_value; + if (map->set == NULL) + map->set = uvc_set_le_value; + + list_add_tail(&map->list, &ctrl->info.mappings); + uvc_trace(UVC_TRACE_CONTROL, + "Adding mapping '%s' to control %pUl/%u.\n", + map->name, ctrl->info.entity, ctrl->info.selector); + + return 0; +} + +int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, + const struct uvc_control_mapping *mapping) +{ + struct uvc_device *dev = chain->dev; + struct uvc_control_mapping *map; + struct uvc_entity *entity; + struct uvc_control *ctrl; + int found = 0; + int ret; + + if (mapping->id & ~V4L2_CTRL_ID_MASK) { + uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control " + "id 0x%08x is invalid.\n", mapping->name, + mapping->id); + return -EINVAL; + } + + /* Search for the matching (GUID/CS) control on the current chain */ + list_for_each_entry(entity, &chain->entities, chain) { + unsigned int i; + + if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT || + !uvc_entity_match_guid(entity, mapping->entity)) + continue; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->index == mapping->selector - 1) { + found = 1; + break; + } + } + + if (found) + break; + } + if (!found) + return -ENOENT; + + if (mutex_lock_interruptible(&chain->ctrl_mutex)) + return -ERESTARTSYS; + + /* Perform delayed initialization of XU controls */ + ret = uvc_ctrl_init_xu_ctrl(dev, ctrl); + if (ret < 0) { + ret = -ENOENT; + goto done; + } + + list_for_each_entry(map, &ctrl->info.mappings, list) { + if (mapping->id == map->id) { + uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', " + "control id 0x%08x already exists.\n", + mapping->name, mapping->id); + ret = -EEXIST; + goto done; + } + } + + /* Prevent excess memory consumption */ + if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) { + atomic_dec(&dev->nmappings); + uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', maximum " + "mappings count (%u) exceeded.\n", mapping->name, + UVC_MAX_CONTROL_MAPPINGS); + ret = -ENOMEM; + goto done; + } + + ret = __uvc_ctrl_add_mapping(dev, ctrl, mapping); + if (ret < 0) + atomic_dec(&dev->nmappings); + +done: + mutex_unlock(&chain->ctrl_mutex); + return ret; +} + +/* + * Prune an entity of its bogus controls using a blacklist. Bogus controls + * are currently the ones that crash the camera or unconditionally return an + * error when queried. + */ +static void uvc_ctrl_prune_entity(struct uvc_device *dev, + struct uvc_entity *entity) +{ + struct uvc_ctrl_blacklist { + struct usb_device_id id; + u8 index; + }; + + static const struct uvc_ctrl_blacklist processing_blacklist[] = { + { { USB_DEVICE(0x13d3, 0x509b) }, 9 }, /* Gain */ + { { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */ + { { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */ + }; + static const struct uvc_ctrl_blacklist camera_blacklist[] = { + { { USB_DEVICE(0x06f8, 0x3005) }, 9 }, /* Zoom, Absolute */ + }; + + const struct uvc_ctrl_blacklist *blacklist; + unsigned int size; + unsigned int count; + unsigned int i; + u8 *controls; + + switch (UVC_ENTITY_TYPE(entity)) { + case UVC_VC_PROCESSING_UNIT: + blacklist = processing_blacklist; + count = ARRAY_SIZE(processing_blacklist); + controls = entity->processing.bmControls; + size = entity->processing.bControlSize; + break; + + case UVC_ITT_CAMERA: + blacklist = camera_blacklist; + count = ARRAY_SIZE(camera_blacklist); + controls = entity->camera.bmControls; + size = entity->camera.bControlSize; + break; + + default: + return; + } + + for (i = 0; i < count; ++i) { + if (!usb_match_one_id(dev->intf, &blacklist[i].id)) + continue; + + if (blacklist[i].index >= 8 * size || + !uvc_test_bit(controls, blacklist[i].index)) + continue; + + uvc_trace(UVC_TRACE_CONTROL, "%u/%u control is black listed, " + "removing it.\n", entity->id, blacklist[i].index); + + uvc_clear_bit(controls, blacklist[i].index); + } +} + +/* + * Add control information and hardcoded stock control mappings to the given + * device. + */ +static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl) +{ + const struct uvc_control_info *info = uvc_ctrls; + const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls); + const struct uvc_control_mapping *mapping = uvc_ctrl_mappings; + const struct uvc_control_mapping *mend = + mapping + ARRAY_SIZE(uvc_ctrl_mappings); + + /* XU controls initialization requires querying the device for control + * information. As some buggy UVC devices will crash when queried + * repeatedly in a tight loop, delay XU controls initialization until + * first use. + */ + if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) + return; + + for (; info < iend; ++info) { + if (uvc_entity_match_guid(ctrl->entity, info->entity) && + ctrl->index == info->index) { + uvc_ctrl_add_info(dev, ctrl, info); + break; + } + } + + if (!ctrl->initialized) + return; + + for (; mapping < mend; ++mapping) { + if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && + ctrl->info.selector == mapping->selector) + __uvc_ctrl_add_mapping(dev, ctrl, mapping); + } +} + +/* + * Initialize device controls. + */ +int uvc_ctrl_init_device(struct uvc_device *dev) +{ + struct uvc_entity *entity; + unsigned int i; + + /* Walk the entities list and instantiate controls */ + list_for_each_entry(entity, &dev->entities, list) { + struct uvc_control *ctrl; + unsigned int bControlSize = 0, ncontrols; + __u8 *bmControls = NULL; + + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { + bmControls = entity->extension.bmControls; + bControlSize = entity->extension.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) { + bmControls = entity->processing.bmControls; + bControlSize = entity->processing.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) { + bmControls = entity->camera.bmControls; + bControlSize = entity->camera.bControlSize; + } + + /* Remove bogus/blacklisted controls */ + uvc_ctrl_prune_entity(dev, entity); + + /* Count supported controls and allocate the controls array */ + ncontrols = memweight(bmControls, bControlSize); + if (ncontrols == 0) + continue; + + entity->controls = kcalloc(ncontrols, sizeof(*ctrl), + GFP_KERNEL); + if (entity->controls == NULL) + return -ENOMEM; + entity->ncontrols = ncontrols; + + /* Initialize all supported controls */ + ctrl = entity->controls; + for (i = 0; i < bControlSize * 8; ++i) { + if (uvc_test_bit(bmControls, i) == 0) + continue; + + ctrl->entity = entity; + ctrl->index = i; + + uvc_ctrl_init_ctrl(dev, ctrl); + ctrl++; + } + } + + return 0; +} + +/* + * Cleanup device controls. + */ +static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev, + struct uvc_control *ctrl) +{ + struct uvc_control_mapping *mapping, *nm; + + list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) { + list_del(&mapping->list); + kfree(mapping->menu_info); + kfree(mapping); + } +} + +void uvc_ctrl_cleanup_device(struct uvc_device *dev) +{ + struct uvc_entity *entity; + unsigned int i; + + /* Free controls and control mappings for all entities. */ + list_for_each_entry(entity, &dev->entities, list) { + for (i = 0; i < entity->ncontrols; ++i) { + struct uvc_control *ctrl = &entity->controls[i]; + + if (!ctrl->initialized) + continue; + + uvc_ctrl_cleanup_mappings(dev, ctrl); + kfree(ctrl->uvc_data); + } + + kfree(entity->controls); + } +} diff --git a/drivers/media/usb/uvc/uvc_debugfs.c b/drivers/media/usb/uvc/uvc_debugfs.c new file mode 100644 index 000000000000..14561a5abb79 --- /dev/null +++ b/drivers/media/usb/uvc/uvc_debugfs.c @@ -0,0 +1,136 @@ +/* + * uvc_debugfs.c -- USB Video Class driver - Debugging support + * + * Copyright (C) 2011 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include + +#include "uvcvideo.h" + +/* ----------------------------------------------------------------------------- + * Statistics + */ + +#define UVC_DEBUGFS_BUF_SIZE 1024 + +struct uvc_debugfs_buffer { + size_t count; + char data[UVC_DEBUGFS_BUF_SIZE]; +}; + +static int uvc_debugfs_stats_open(struct inode *inode, struct file *file) +{ + struct uvc_streaming *stream = inode->i_private; + struct uvc_debugfs_buffer *buf; + + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data)); + + file->private_data = buf; + return 0; +} + +static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf, + size_t nbytes, loff_t *ppos) +{ + struct uvc_debugfs_buffer *buf = file->private_data; + + return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data, + buf->count); +} + +static int uvc_debugfs_stats_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations uvc_debugfs_stats_fops = { + .owner = THIS_MODULE, + .open = uvc_debugfs_stats_open, + .llseek = no_llseek, + .read = uvc_debugfs_stats_read, + .release = uvc_debugfs_stats_release, +}; + +/* ----------------------------------------------------------------------------- + * Global and stream initialization/cleanup + */ + +static struct dentry *uvc_debugfs_root_dir; + +int uvc_debugfs_init_stream(struct uvc_streaming *stream) +{ + struct usb_device *udev = stream->dev->udev; + struct dentry *dent; + char dir_name[32]; + + if (uvc_debugfs_root_dir == NULL) + return -ENODEV; + + sprintf(dir_name, "%u-%u", udev->bus->busnum, udev->devnum); + + dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir); + if (IS_ERR_OR_NULL(dent)) { + uvc_printk(KERN_INFO, "Unable to create debugfs %s " + "directory.\n", dir_name); + return -ENODEV; + } + + stream->debugfs_dir = dent; + + dent = debugfs_create_file("stats", 0444, stream->debugfs_dir, + stream, &uvc_debugfs_stats_fops); + if (IS_ERR_OR_NULL(dent)) { + uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n"); + uvc_debugfs_cleanup_stream(stream); + return -ENODEV; + } + + return 0; +} + +void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream) +{ + if (stream->debugfs_dir == NULL) + return; + + debugfs_remove_recursive(stream->debugfs_dir); + stream->debugfs_dir = NULL; +} + +int uvc_debugfs_init(void) +{ + struct dentry *dir; + + dir = debugfs_create_dir("uvcvideo", usb_debug_root); + if (IS_ERR_OR_NULL(dir)) { + uvc_printk(KERN_INFO, "Unable to create debugfs directory\n"); + return -ENODATA; + } + + uvc_debugfs_root_dir = dir; + return 0; +} + +void uvc_debugfs_cleanup(void) +{ + if (uvc_debugfs_root_dir != NULL) + debugfs_remove_recursive(uvc_debugfs_root_dir); +} diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c new file mode 100644 index 000000000000..45d7aa162d9d --- /dev/null +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -0,0 +1,2472 @@ +/* + * uvc_driver.c -- USB Video Class driver + * + * Copyright (C) 2005-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +/* + * This driver aims to support video input and ouput devices compliant with the + * 'USB Video Class' specification. + * + * The driver doesn't support the deprecated v4l1 interface. It implements the + * mmap capture method only, and doesn't do any image format conversion in + * software. If your user-space application doesn't support YUYV or MJPEG, fix + * it :-). Please note that the MJPEG data have been stripped from their + * Huffman tables (DHT marker), you will need to add it back if your JPEG + * codec can't handle MJPEG data. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "uvcvideo.h" + +#define DRIVER_AUTHOR "Laurent Pinchart " \ + "" +#define DRIVER_DESC "USB Video Class driver" + +unsigned int uvc_clock_param = CLOCK_MONOTONIC; +unsigned int uvc_no_drop_param; +static unsigned int uvc_quirks_param = -1; +unsigned int uvc_trace_param; +unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; + +/* ------------------------------------------------------------------------ + * Video formats + */ + +static struct uvc_format_desc uvc_fmts[] = { + { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { + .name = "YUV 4:2:0 (NV12)", + .guid = UVC_GUID_FORMAT_NV12, + .fcc = V4L2_PIX_FMT_NV12, + }, + { + .name = "MJPEG", + .guid = UVC_GUID_FORMAT_MJPEG, + .fcc = V4L2_PIX_FMT_MJPEG, + }, + { + .name = "YVU 4:2:0 (YV12)", + .guid = UVC_GUID_FORMAT_YV12, + .fcc = V4L2_PIX_FMT_YVU420, + }, + { + .name = "YUV 4:2:0 (I420)", + .guid = UVC_GUID_FORMAT_I420, + .fcc = V4L2_PIX_FMT_YUV420, + }, + { + .name = "YUV 4:2:0 (M420)", + .guid = UVC_GUID_FORMAT_M420, + .fcc = V4L2_PIX_FMT_M420, + }, + { + .name = "YUV 4:2:2 (UYVY)", + .guid = UVC_GUID_FORMAT_UYVY, + .fcc = V4L2_PIX_FMT_UYVY, + }, + { + .name = "Greyscale 8-bit (Y800)", + .guid = UVC_GUID_FORMAT_Y800, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "Greyscale 8-bit (Y8 )", + .guid = UVC_GUID_FORMAT_Y8, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "Greyscale 10-bit (Y10 )", + .guid = UVC_GUID_FORMAT_Y10, + .fcc = V4L2_PIX_FMT_Y10, + }, + { + .name = "Greyscale 12-bit (Y12 )", + .guid = UVC_GUID_FORMAT_Y12, + .fcc = V4L2_PIX_FMT_Y12, + }, + { + .name = "Greyscale 16-bit (Y16 )", + .guid = UVC_GUID_FORMAT_Y16, + .fcc = V4L2_PIX_FMT_Y16, + }, + { + .name = "RGB Bayer", + .guid = UVC_GUID_FORMAT_BY8, + .fcc = V4L2_PIX_FMT_SBGGR8, + }, + { + .name = "RGB565", + .guid = UVC_GUID_FORMAT_RGBP, + .fcc = V4L2_PIX_FMT_RGB565, + }, + { + .name = "H.264", + .guid = UVC_GUID_FORMAT_H264, + .fcc = V4L2_PIX_FMT_H264, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, + __u8 epaddr) +{ + struct usb_host_endpoint *ep; + unsigned int i; + + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + ep = &alts->endpoint[i]; + if (ep->desc.bEndpointAddress == epaddr) + return ep; + } + + return NULL; +} + +static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) +{ + unsigned int len = ARRAY_SIZE(uvc_fmts); + unsigned int i; + + for (i = 0; i < len; ++i) { + if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) + return &uvc_fmts[i]; + } + + return NULL; +} + +static __u32 uvc_colorspace(const __u8 primaries) +{ + static const __u8 colorprimaries[] = { + 0, + V4L2_COLORSPACE_SRGB, + V4L2_COLORSPACE_470_SYSTEM_M, + V4L2_COLORSPACE_470_SYSTEM_BG, + V4L2_COLORSPACE_SMPTE170M, + V4L2_COLORSPACE_SMPTE240M, + }; + + if (primaries < ARRAY_SIZE(colorprimaries)) + return colorprimaries[primaries]; + + return 0; +} + +/* Simplify a fraction using a simple continued fraction decomposition. The + * idea here is to convert fractions such as 333333/10000000 to 1/30 using + * 32 bit arithmetic only. The algorithm is not perfect and relies upon two + * arbitrary parameters to remove non-significative terms from the simple + * continued fraction decomposition. Using 8 and 333 for n_terms and threshold + * respectively seems to give nice results. + */ +void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold) +{ + uint32_t *an; + uint32_t x, y, r; + unsigned int i, n; + + an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); + if (an == NULL) + return; + + /* Convert the fraction to a simple continued fraction. See + * http://mathforum.org/dr.math/faq/faq.fractions.html + * Stop if the current term is bigger than or equal to the given + * threshold. + */ + x = *numerator; + y = *denominator; + + for (n = 0; n < n_terms && y != 0; ++n) { + an[n] = x / y; + if (an[n] >= threshold) { + if (n < 2) + n++; + break; + } + + r = x - an[n] * y; + x = y; + y = r; + } + + /* Expand the simple continued fraction back to an integer fraction. */ + x = 0; + y = 1; + + for (i = n; i > 0; --i) { + r = y; + y = an[i-1] * y + x; + x = r; + } + + *numerator = y; + *denominator = x; + kfree(an); +} + +/* Convert a fraction to a frame interval in 100ns multiples. The idea here is + * to compute numerator / denominator * 10000000 using 32 bit fixed point + * arithmetic only. + */ +uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) +{ + uint32_t multiplier; + + /* Saturate the result if the operation would overflow. */ + if (denominator == 0 || + numerator/denominator >= ((uint32_t)-1)/10000000) + return (uint32_t)-1; + + /* Divide both the denominator and the multiplier by two until + * numerator * multiplier doesn't overflow. If anyone knows a better + * algorithm please let me know. + */ + multiplier = 10000000; + while (numerator > ((uint32_t)-1)/multiplier) { + multiplier /= 2; + denominator /= 2; + } + + return denominator ? numerator * multiplier / denominator : 0; +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) +{ + struct uvc_entity *entity; + + list_for_each_entry(entity, &dev->entities, list) { + if (entity->id == id) + return entity; + } + + return NULL; +} + +static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, + int id, struct uvc_entity *entity) +{ + unsigned int i; + + if (entity == NULL) + entity = list_entry(&dev->entities, struct uvc_entity, list); + + list_for_each_entry_continue(entity, &dev->entities, list) { + for (i = 0; i < entity->bNrInPins; ++i) + if (entity->baSourceID[i] == id) + return entity; + } + + return NULL; +} + +static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) +{ + struct uvc_streaming *stream; + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->header.bTerminalLink == id) + return stream; + } + + return NULL; +} + +/* ------------------------------------------------------------------------ + * Descriptors parsing + */ + +static int uvc_parse_format(struct uvc_device *dev, + struct uvc_streaming *streaming, struct uvc_format *format, + __u32 **intervals, unsigned char *buffer, int buflen) +{ + struct usb_interface *intf = streaming->intf; + struct usb_host_interface *alts = intf->cur_altsetting; + struct uvc_format_desc *fmtdesc; + struct uvc_frame *frame; + const unsigned char *start = buffer; + unsigned int interval; + unsigned int i, n; + __u8 ftype; + + format->type = buffer[2]; + format->index = buffer[3]; + + switch (buffer[2]) { + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_FRAME_BASED: + n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; + if (buflen < n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Find the format descriptor from its GUID. */ + fmtdesc = uvc_format_by_guid(&buffer[5]); + + if (fmtdesc != NULL) { + strlcpy(format->name, fmtdesc->name, + sizeof format->name); + format->fcc = fmtdesc->fcc; + } else { + uvc_printk(KERN_INFO, "Unknown video format %pUl\n", + &buffer[5]); + snprintf(format->name, sizeof(format->name), "%pUl\n", + &buffer[5]); + format->fcc = 0; + } + + format->bpp = buffer[21]; + if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { + ftype = UVC_VS_FRAME_UNCOMPRESSED; + } else { + ftype = UVC_VS_FRAME_FRAME_BASED; + if (buffer[27]) + format->flags = UVC_FMT_FLAG_COMPRESSED; + } + break; + + case UVC_VS_FORMAT_MJPEG: + if (buflen < 11) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + strlcpy(format->name, "MJPEG", sizeof format->name); + format->fcc = V4L2_PIX_FMT_MJPEG; + format->flags = UVC_FMT_FLAG_COMPRESSED; + format->bpp = 0; + ftype = UVC_VS_FRAME_MJPEG; + break; + + case UVC_VS_FORMAT_DV: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + switch (buffer[8] & 0x7f) { + case 0: + strlcpy(format->name, "SD-DV", sizeof format->name); + break; + case 1: + strlcpy(format->name, "SDL-DV", sizeof format->name); + break; + case 2: + strlcpy(format->name, "HD-DV", sizeof format->name); + break; + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d: unknown DV format %u\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, buffer[8]); + return -EINVAL; + } + + strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", + sizeof format->name); + + format->fcc = V4L2_PIX_FMT_DV; + format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; + format->bpp = 0; + ftype = 0; + + /* Create a dummy frame descriptor. */ + frame = &format->frame[0]; + memset(&format->frame[0], 0, sizeof format->frame[0]); + frame->bFrameIntervalType = 1; + frame->dwDefaultFrameInterval = 1; + frame->dwFrameInterval = *intervals; + *(*intervals)++ = 1; + format->nframes = 1; + break; + + case UVC_VS_FORMAT_MPEG2TS: + case UVC_VS_FORMAT_STREAM_BASED: + /* Not supported yet. */ + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d unsupported format %u\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[2]); + return -EINVAL; + } + + uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); + + buflen -= buffer[0]; + buffer += buffer[0]; + + /* Parse the frame descriptors. Only uncompressed, MJPEG and frame + * based formats have frame descriptors. + */ + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == ftype) { + frame = &format->frame[format->nframes]; + if (ftype != UVC_VS_FRAME_FRAME_BASED) + n = buflen > 25 ? buffer[25] : 0; + else + n = buflen > 21 ? buffer[21] : 0; + + n = n ? n : 3; + + if (buflen < 26 + 4*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FRAME error\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + frame->bFrameIndex = buffer[3]; + frame->bmCapabilities = buffer[4]; + frame->wWidth = get_unaligned_le16(&buffer[5]); + frame->wHeight = get_unaligned_le16(&buffer[7]); + frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); + frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); + if (ftype != UVC_VS_FRAME_FRAME_BASED) { + frame->dwMaxVideoFrameBufferSize = + get_unaligned_le32(&buffer[17]); + frame->dwDefaultFrameInterval = + get_unaligned_le32(&buffer[21]); + frame->bFrameIntervalType = buffer[25]; + } else { + frame->dwMaxVideoFrameBufferSize = 0; + frame->dwDefaultFrameInterval = + get_unaligned_le32(&buffer[17]); + frame->bFrameIntervalType = buffer[21]; + } + frame->dwFrameInterval = *intervals; + + /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize + * completely. Observed behaviours range from setting the + * value to 1.1x the actual frame size to hardwiring the + * 16 low bits to 0. This results in a higher than necessary + * memory usage as well as a wrong image size information. For + * uncompressed formats this can be fixed by computing the + * value from the frame size. + */ + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) + frame->dwMaxVideoFrameBufferSize = format->bpp + * frame->wWidth * frame->wHeight / 8; + + /* Some bogus devices report dwMinFrameInterval equal to + * dwMaxFrameInterval and have dwFrameIntervalStep set to + * zero. Setting all null intervals to 1 fixes the problem and + * some other divisions by zero that could happen. + */ + for (i = 0; i < n; ++i) { + interval = get_unaligned_le32(&buffer[26+4*i]); + *(*intervals)++ = interval ? interval : 1; + } + + /* Make sure that the default frame interval stays between + * the boundaries. + */ + n -= frame->bFrameIntervalType ? 1 : 2; + frame->dwDefaultFrameInterval = + min(frame->dwFrameInterval[n], + max(frame->dwFrameInterval[0], + frame->dwDefaultFrameInterval)); + + if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { + frame->bFrameIntervalType = 1; + frame->dwFrameInterval[0] = + frame->dwDefaultFrameInterval; + } + + uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000/frame->dwDefaultFrameInterval, + (100000000/frame->dwDefaultFrameInterval)%10); + + format->nframes++; + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_COLORFORMAT) { + if (buflen < 6) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d COLORFORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + format->colorspace = uvc_colorspace(buffer[3]); + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + return buffer - start; +} + +static int uvc_parse_streaming(struct uvc_device *dev, + struct usb_interface *intf) +{ + struct uvc_streaming *streaming = NULL; + struct uvc_format *format; + struct uvc_frame *frame; + struct usb_host_interface *alts = &intf->altsetting[0]; + unsigned char *_buffer, *buffer = alts->extra; + int _buflen, buflen = alts->extralen; + unsigned int nformats = 0, nframes = 0, nintervals = 0; + unsigned int size, i, n, p; + __u32 *interval; + __u16 psize; + int ret = -EINVAL; + + if (intf->cur_altsetting->desc.bInterfaceSubClass + != UVC_SC_VIDEOSTREAMING) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " + "video streaming interface\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " + "claimed\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + streaming = kzalloc(sizeof *streaming, GFP_KERNEL); + if (streaming == NULL) { + usb_driver_release_interface(&uvc_driver.driver, intf); + return -EINVAL; + } + + mutex_init(&streaming->mutex); + streaming->dev = dev; + streaming->intf = usb_get_intf(intf); + streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + + /* The Pico iMage webcam has its class-specific interface descriptors + * after the endpoint descriptors. + */ + if (buflen == 0) { + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + struct usb_host_endpoint *ep = &alts->endpoint[i]; + + if (ep->extralen == 0) + continue; + + if (ep->extralen > 2 && + ep->extra[1] == USB_DT_CS_INTERFACE) { + uvc_trace(UVC_TRACE_DESCR, "trying extra data " + "from endpoint %u.\n", i); + buffer = alts->endpoint[i].extra; + buflen = alts->endpoint[i].extralen; + break; + } + } + } + + /* Skip the standard interface descriptors. */ + while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen <= 2) { + uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " + "interface descriptors found.\n"); + goto error; + } + + /* Parse the header descriptor. */ + switch (buffer[2]) { + case UVC_VS_OUTPUT_HEADER: + streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + size = 9; + break; + + case UVC_VS_INPUT_HEADER: + streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + size = 13; + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d HEADER descriptor not found.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + goto error; + } + + p = buflen >= 4 ? buffer[3] : 0; + n = buflen >= size ? buffer[size-1] : 0; + + if (buflen < size + p*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d HEADER descriptor is invalid.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } + + streaming->header.bNumFormats = p; + streaming->header.bEndpointAddress = buffer[6]; + if (buffer[2] == UVC_VS_INPUT_HEADER) { + streaming->header.bmInfo = buffer[7]; + streaming->header.bTerminalLink = buffer[8]; + streaming->header.bStillCaptureMethod = buffer[9]; + streaming->header.bTriggerSupport = buffer[10]; + streaming->header.bTriggerUsage = buffer[11]; + } else { + streaming->header.bTerminalLink = buffer[7]; + } + streaming->header.bControlSize = n; + + streaming->header.bmaControls = kmemdup(&buffer[size], p * n, + GFP_KERNEL); + if (streaming->header.bmaControls == NULL) { + ret = -ENOMEM; + goto error; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + + _buffer = buffer; + _buflen = buflen; + + /* Count the format and frame descriptors. */ + while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { + switch (_buffer[2]) { + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_FRAME_BASED: + nformats++; + break; + + case UVC_VS_FORMAT_DV: + /* DV format has no frame descriptor. We will create a + * dummy frame descriptor with a dummy frame interval. + */ + nformats++; + nframes++; + nintervals++; + break; + + case UVC_VS_FORMAT_MPEG2TS: + case UVC_VS_FORMAT_STREAM_BASED: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FORMAT %u is not supported.\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, _buffer[2]); + break; + + case UVC_VS_FRAME_UNCOMPRESSED: + case UVC_VS_FRAME_MJPEG: + nframes++; + if (_buflen > 25) + nintervals += _buffer[25] ? _buffer[25] : 3; + break; + + case UVC_VS_FRAME_FRAME_BASED: + nframes++; + if (_buflen > 21) + nintervals += _buffer[21] ? _buffer[21] : 3; + break; + } + + _buflen -= _buffer[0]; + _buffer += _buffer[0]; + } + + if (nformats == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has no supported formats defined.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } + + size = nformats * sizeof *format + nframes * sizeof *frame + + nintervals * sizeof *interval; + format = kzalloc(size, GFP_KERNEL); + if (format == NULL) { + ret = -ENOMEM; + goto error; + } + + frame = (struct uvc_frame *)&format[nformats]; + interval = (__u32 *)&frame[nframes]; + + streaming->format = format; + streaming->nformats = nformats; + + /* Parse the format descriptors. */ + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { + switch (buffer[2]) { + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_DV: + case UVC_VS_FORMAT_FRAME_BASED: + format->frame = frame; + ret = uvc_parse_format(dev, streaming, format, + &interval, buffer, buflen); + if (ret < 0) + goto error; + + frame += format->nframes; + format++; + + buflen -= ret; + buffer += ret; + continue; + + default: + break; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen) + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has %u bytes of trailing descriptor garbage.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); + + /* Parse the alternate settings to find the maximum bandwidth. */ + for (i = 0; i < intf->num_altsetting; ++i) { + struct usb_host_endpoint *ep; + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + streaming->header.bEndpointAddress); + if (ep == NULL) + continue; + + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (psize > streaming->maxpsize) + streaming->maxpsize = psize; + } + + list_add_tail(&streaming->list, &dev->streams); + return 0; + +error: + usb_driver_release_interface(&uvc_driver.driver, intf); + usb_put_intf(intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + return ret; +} + +static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, + unsigned int num_pads, unsigned int extra_size) +{ + struct uvc_entity *entity; + unsigned int num_inputs; + unsigned int size; + unsigned int i; + + extra_size = ALIGN(extra_size, sizeof(*entity->pads)); + num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; + size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads + + num_inputs; + entity = kzalloc(size, GFP_KERNEL); + if (entity == NULL) + return NULL; + + entity->id = id; + entity->type = type; + + entity->num_links = 0; + entity->num_pads = num_pads; + entity->pads = ((void *)(entity + 1)) + extra_size; + + for (i = 0; i < num_inputs; ++i) + entity->pads[i].flags = MEDIA_PAD_FL_SINK; + if (!UVC_ENTITY_IS_OTERM(entity)) + entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; + + entity->bNrInPins = num_inputs; + entity->baSourceID = (__u8 *)(&entity->pads[num_pads]); + + return entity; +} + +/* Parse vendor-specific extensions. */ +static int uvc_parse_vendor_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + struct uvc_entity *unit; + unsigned int n, p; + int handled = 0; + + switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { + case 0x046d: /* Logitech */ + if (buffer[1] != 0x41 || buffer[2] != 0x01) + break; + + /* Logitech implements several vendor specific functions + * through vendor specific extension units (LXU). + * + * The LXU descriptors are similar to XU descriptors + * (see "USB Device Video Class for Video Devices", section + * 3.7.2.6 "Extension Unit Descriptor") with the following + * differences: + * + * ---------------------------------------------------------- + * 0 bLength 1 Number + * Size of this descriptor, in bytes: 24+p+n*2 + * ---------------------------------------------------------- + * 23+p+n bmControlsType N Bitmap + * Individual bits in the set are defined: + * 0: Absolute + * 1: Relative + * + * This bitset is mapped exactly the same as bmControls. + * ---------------------------------------------------------- + * 23+p+n*2 bReserved 1 Boolean + * ---------------------------------------------------------- + * 24+p+n*2 iExtension 1 Index + * Index of a string descriptor that describes this + * extension unit. + * ---------------------------------------------------------- + */ + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 25 + p ? buffer[22+p] : 0; + + if (buflen < 25 + p + 2*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + break; + } + + unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], + p + 1, 2*n); + if (unit == NULL) + return -ENOMEM; + + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + memcpy(unit->baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); + unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) + + n; + memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); + + if (buffer[24+p+2*n] != 0) + usb_string(udev, buffer[24+p+2*n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + handled = 1; + break; + } + + return handled; +} + +static int uvc_parse_standard_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct uvc_entity *unit, *term; + struct usb_interface *intf; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned int i, n, p, len; + __u16 type; + + switch (buffer[2]) { + case UVC_VC_HEADER: + n = buflen >= 12 ? buffer[11] : 0; + + if (buflen < 12 || buflen < 12 + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d HEADER error\n", udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + dev->uvc_version = get_unaligned_le16(&buffer[3]); + dev->clock_frequency = get_unaligned_le32(&buffer[7]); + + /* Parse all USB Video Streaming interfaces. */ + for (i = 0; i < n; ++i) { + intf = usb_ifnum_to_if(udev, buffer[12+i]); + if (intf == NULL) { + uvc_trace(UVC_TRACE_DESCR, "device %d " + "interface %d doesn't exists\n", + udev->devnum, i); + continue; + } + + uvc_parse_streaming(dev, intf); + } + break; + + case UVC_VC_INPUT_TERMINAL: + if (buflen < 8) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = get_unaligned_le16(&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, + buffer[3], type); + return 0; + } + + n = 0; + p = 0; + len = 8; + + if (type == UVC_ITT_CAMERA) { + n = buflen >= 15 ? buffer[14] : 0; + len = 15; + + } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { + n = buflen >= 9 ? buffer[8] : 0; + p = buflen >= 10 + n ? buffer[9+n] : 0; + len = 10; + } + + if (buflen < len + n + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], + 1, n + p); + if (term == NULL) + return -ENOMEM; + + if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { + term->camera.bControlSize = n; + term->camera.bmControls = (__u8 *)term + sizeof *term; + term->camera.wObjectiveFocalLengthMin = + get_unaligned_le16(&buffer[8]); + term->camera.wObjectiveFocalLengthMax = + get_unaligned_le16(&buffer[10]); + term->camera.wOcularFocalLength = + get_unaligned_le16(&buffer[12]); + memcpy(term->camera.bmControls, &buffer[15], n); + } else if (UVC_ENTITY_TYPE(term) == + UVC_ITT_MEDIA_TRANSPORT_INPUT) { + term->media.bControlSize = n; + term->media.bmControls = (__u8 *)term + sizeof *term; + term->media.bTransportModeSize = p; + term->media.bmTransportModes = (__u8 *)term + + sizeof *term + n; + memcpy(term->media.bmControls, &buffer[9], n); + memcpy(term->media.bmTransportModes, &buffer[10+n], p); + } + + if (buffer[7] != 0) + usb_string(udev, buffer[7], term->name, + sizeof term->name); + else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) + sprintf(term->name, "Camera %u", buffer[3]); + else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) + sprintf(term->name, "Media %u", buffer[3]); + else + sprintf(term->name, "Input %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case UVC_VC_OUTPUT_TERMINAL: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = get_unaligned_le16(&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, buffer[3], type); + return 0; + } + + term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], + 1, 0); + if (term == NULL) + return -ENOMEM; + + memcpy(term->baSourceID, &buffer[7], 1); + + if (buffer[8] != 0) + usb_string(udev, buffer[8], term->name, + sizeof term->name); + else + sprintf(term->name, "Output %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case UVC_VC_SELECTOR_UNIT: + p = buflen >= 5 ? buffer[4] : 0; + + if (buflen < 5 || buflen < 6 + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d SELECTOR_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); + if (unit == NULL) + return -ENOMEM; + + memcpy(unit->baSourceID, &buffer[5], p); + + if (buffer[5+p] != 0) + usb_string(udev, buffer[5+p], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Selector %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case UVC_VC_PROCESSING_UNIT: + n = buflen >= 8 ? buffer[7] : 0; + p = dev->uvc_version >= 0x0110 ? 10 : 9; + + if (buflen < p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d PROCESSING_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); + if (unit == NULL) + return -ENOMEM; + + memcpy(unit->baSourceID, &buffer[4], 1); + unit->processing.wMaxMultiplier = + get_unaligned_le16(&buffer[5]); + unit->processing.bControlSize = buffer[7]; + unit->processing.bmControls = (__u8 *)unit + sizeof *unit; + memcpy(unit->processing.bmControls, &buffer[8], n); + if (dev->uvc_version >= 0x0110) + unit->processing.bmVideoStandards = buffer[9+n]; + + if (buffer[8+n] != 0) + usb_string(udev, buffer[8+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Processing %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case UVC_VC_EXTENSION_UNIT: + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 24 + p ? buffer[22+p] : 0; + + if (buflen < 24 + p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); + if (unit == NULL) + return -ENOMEM; + + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + memcpy(unit->baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit; + memcpy(unit->extension.bmControls, &buffer[23+p], n); + + if (buffer[23+p+n] != 0) + usb_string(udev, buffer[23+p+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " + "descriptor (%u)\n", buffer[2]); + break; + } + + return 0; +} + +static int uvc_parse_control(struct uvc_device *dev) +{ + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned char *buffer = alts->extra; + int buflen = alts->extralen; + int ret; + + /* Parse the default alternate setting only, as the UVC specification + * defines a single alternate setting, the default alternate setting + * zero. + */ + + while (buflen > 2) { + if (uvc_parse_vendor_control(dev, buffer, buflen) || + buffer[1] != USB_DT_CS_INTERFACE) + goto next_descriptor; + + if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) + return ret; + +next_descriptor: + buflen -= buffer[0]; + buffer += buffer[0]; + } + + /* Check if the optional status endpoint is present. Built-in iSight + * webcams have an interrupt endpoint but spit proprietary data that + * don't conform to the UVC status endpoint messages. Don't try to + * handle the interrupt endpoint for those cameras. + */ + if (alts->desc.bNumEndpoints == 1 && + !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { + struct usb_host_endpoint *ep = &alts->endpoint[0]; + struct usb_endpoint_descriptor *desc = &ep->desc; + + if (usb_endpoint_is_int_in(desc) && + le16_to_cpu(desc->wMaxPacketSize) >= 8 && + desc->bInterval != 0) { + uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " + "(addr %02x).\n", desc->bEndpointAddress); + dev->int_ep = ep; + } + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * UVC device scan + */ + +/* + * Scan the UVC descriptors to locate a chain starting at an Output Terminal + * and containing the following units: + * + * - one or more Output Terminals (USB Streaming or Display) + * - zero or one Processing Unit + * - zero, one or more single-input Selector Units + * - zero or one multiple-input Selector Units, provided all inputs are + * connected to input terminals + * - zero, one or mode single-input Extension Units + * - one or more Input Terminals (Camera, External or USB Streaming) + * + * The terminal and units must match on of the following structures: + * + * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) + * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... + * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) + * + * +---------+ +---------+ -> OTT_*(0) + * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... + * +---------+ +---------+ -> OTT_*(n) + * + * The Processing Unit and Extension Units can be in any order. Additional + * Extension Units connected to the main chain as single-unit branches are + * also supported. Single-input Selector Units are ignored. + */ +static int uvc_scan_chain_entity(struct uvc_video_chain *chain, + struct uvc_entity *entity) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case UVC_VC_EXTENSION_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- XU %d", entity->id); + + if (entity->bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " + "than 1 input pin.\n", entity->id); + return -1; + } + + break; + + case UVC_VC_PROCESSING_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- PU %d", entity->id); + + if (chain->processing != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple " + "Processing Units in chain.\n"); + return -1; + } + + chain->processing = entity; + break; + + case UVC_VC_SELECTOR_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- SU %d", entity->id); + + /* Single-input selector units are ignored. */ + if (entity->bNrInPins == 1) + break; + + if (chain->selector != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " + "Units in chain.\n"); + return -1; + } + + chain->selector = entity; + break; + + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + + break; + + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); + + break; + + case UVC_TT_STREAMING: + if (UVC_ENTITY_IS_ITERM(entity)) { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + } else { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); + } + + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " + "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); + return -1; + } + + list_add_tail(&entity->chain, &chain->entities); + return 0; +} + +static int uvc_scan_chain_forward(struct uvc_video_chain *chain, + struct uvc_entity *entity, struct uvc_entity *prev) +{ + struct uvc_entity *forward; + int found; + + /* Forward scan */ + forward = NULL; + found = 0; + + while (1) { + forward = uvc_entity_by_reference(chain->dev, entity->id, + forward); + if (forward == NULL) + break; + if (forward == prev) + continue; + + switch (UVC_ENTITY_TYPE(forward)) { + case UVC_VC_EXTENSION_UNIT: + if (forward->bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " + "has more than 1 input pin.\n", + entity->id); + return -EINVAL; + } + + list_add_tail(&forward->chain, &chain->entities); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); + + printk(" XU %d", forward->id); + found = 1; + } + break; + + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + if (UVC_ENTITY_IS_ITERM(forward)) { + uvc_trace(UVC_TRACE_DESCR, "Unsupported input " + "terminal %u.\n", forward->id); + return -EINVAL; + } + + list_add_tail(&forward->chain, &chain->entities); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); + + printk(" OT %d", forward->id); + found = 1; + } + break; + } + } + if (found) + printk(")"); + + return 0; +} + +static int uvc_scan_chain_backward(struct uvc_video_chain *chain, + struct uvc_entity **_entity) +{ + struct uvc_entity *entity = *_entity; + struct uvc_entity *term; + int id = -EINVAL, i; + + switch (UVC_ENTITY_TYPE(entity)) { + case UVC_VC_EXTENSION_UNIT: + case UVC_VC_PROCESSING_UNIT: + id = entity->baSourceID[0]; + break; + + case UVC_VC_SELECTOR_UNIT: + /* Single-input selector units are ignored. */ + if (entity->bNrInPins == 1) { + id = entity->baSourceID[0]; + break; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT"); + + chain->selector = entity; + for (i = 0; i < entity->bNrInPins; ++i) { + id = entity->baSourceID[i]; + term = uvc_entity_by_id(chain->dev, id); + if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { + uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " + "input %d isn't connected to an " + "input terminal\n", entity->id, i); + return -1; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" %d", term->id); + + list_add_tail(&term->chain, &chain->entities); + uvc_scan_chain_forward(chain, term, entity); + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk("\n"); + + id = 0; + break; + + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; + break; + } + + if (id <= 0) { + *_entity = NULL; + return id; + } + + entity = uvc_entity_by_id(chain->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -EINVAL; + } + + *_entity = entity; + return 0; +} + +static int uvc_scan_chain(struct uvc_video_chain *chain, + struct uvc_entity *term) +{ + struct uvc_entity *entity, *prev; + + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); + + entity = term; + prev = NULL; + + while (entity != NULL) { + /* Entity must not be part of an existing chain */ + if (entity->chain.next || entity->chain.prev) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "entity %d already in chain.\n", entity->id); + return -EINVAL; + } + + /* Process entity */ + if (uvc_scan_chain_entity(chain, entity) < 0) + return -EINVAL; + + /* Forward scan */ + if (uvc_scan_chain_forward(chain, entity, prev) < 0) + return -EINVAL; + + /* Backward scan */ + prev = entity; + if (uvc_scan_chain_backward(chain, &entity) < 0) + return -EINVAL; + } + + return 0; +} + +static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, + char *buffer) +{ + struct uvc_entity *term; + unsigned int nterms = 0; + char *p = buffer; + + list_for_each_entry(term, terms, chain) { + if (!UVC_ENTITY_IS_TERM(term) || + UVC_TERM_DIRECTION(term) != dir) + continue; + + if (nterms) + p += sprintf(p, ","); + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; + } + p += sprintf(p, "%u", term->id); + } + + return p - buffer; +} + +static const char *uvc_print_chain(struct uvc_video_chain *chain) +{ + static char buffer[43]; + char *p = buffer; + + p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); + p += sprintf(p, " -> "); + uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); + + return buffer; +} + +/* + * Scan the device for video chains and register video devices. + * + * Chains are scanned starting at their output terminals and walked backwards. + */ +static int uvc_scan_device(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + struct uvc_entity *term; + + list_for_each_entry(term, &dev->entities, list) { + if (!UVC_ENTITY_IS_OTERM(term)) + continue; + + /* If the terminal is already included in a chain, skip it. + * This can happen for chains that have multiple output + * terminals, where all output terminals beside the first one + * will be inserted in the chain in forward scans. + */ + if (term->chain.next || term->chain.prev) + continue; + + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&chain->entities); + mutex_init(&chain->ctrl_mutex); + chain->dev = dev; + + if (uvc_scan_chain(chain, term) < 0) { + kfree(chain); + continue; + } + + uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", + uvc_print_chain(chain)); + + list_add_tail(&chain->list, &dev->chains); + } + + if (list_empty(&dev->chains)) { + uvc_printk(KERN_INFO, "No valid video chain found.\n"); + return -1; + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * Video device registration and unregistration + */ + +/* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +static void uvc_delete(struct uvc_device *dev) +{ + struct list_head *p, *n; + + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + if (dev->vdev.dev) + v4l2_device_unregister(&dev->vdev); +#ifdef CONFIG_MEDIA_CONTROLLER + if (media_devnode_is_registered(&dev->mdev.devnode)) + media_device_unregister(&dev->mdev); +#endif + + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); +#ifdef CONFIG_MEDIA_CONTROLLER + uvc_mc_cleanup_entity(entity); +#endif + if (entity->vdev) { + video_device_release(entity->vdev); + entity->vdev = NULL; + } + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streams) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static void uvc_release(struct video_device *vdev) +{ + struct uvc_streaming *stream = video_get_drvdata(vdev); + struct uvc_device *dev = stream->dev; + + /* Decrement the registered streams count and delete the device when it + * reaches zero. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); +} + +/* + * Unregister the video devices. + */ +static void uvc_unregister_video(struct uvc_device *dev) +{ + struct uvc_streaming *stream; + + /* Unregistering all video devices might result in uvc_delete() being + * called from inside the loop if there's no open file handle. To avoid + * that, increment the stream count before iterating over the streams + * and decrement it when done. + */ + atomic_inc(&dev->nstreams); + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->vdev == NULL) + continue; + + video_unregister_device(stream->vdev); + stream->vdev = NULL; + + uvc_debugfs_cleanup_stream(stream); + } + + /* Decrement the stream count and call uvc_delete explicitly if there + * are no stream left. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); +} + +static int uvc_register_video(struct uvc_device *dev, + struct uvc_streaming *stream) +{ + struct video_device *vdev; + int ret; + + /* Initialize the streaming interface with default streaming + * parameters. + */ + ret = uvc_video_init(stream); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to initialize the device " + "(%d).\n", ret); + return ret; + } + + uvc_debugfs_init_stream(stream); + + /* Register the device with V4L. */ + vdev = video_device_alloc(); + if (vdev == NULL) { + uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n", + ret); + return -ENOMEM; + } + + /* We already hold a reference to dev->udev. The video device will be + * unregistered before the reference is released, so we don't need to + * get another one. + */ + vdev->v4l2_dev = &dev->vdev; + vdev->fops = &uvc_fops; + vdev->release = uvc_release; + strlcpy(vdev->name, dev->name, sizeof vdev->name); + + /* Set the driver data before calling video_register_device, otherwise + * uvc_v4l2_open might race us. + */ + stream->vdev = vdev; + video_set_drvdata(vdev, stream); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to register video device (%d).\n", + ret); + stream->vdev = NULL; + video_device_release(vdev); + return ret; + } + + atomic_inc(&dev->nstreams); + return 0; +} + +/* + * Register all video devices in all chains. + */ +static int uvc_register_terms(struct uvc_device *dev, + struct uvc_video_chain *chain) +{ + struct uvc_streaming *stream; + struct uvc_entity *term; + int ret; + + list_for_each_entry(term, &chain->entities, chain) { + if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) + continue; + + stream = uvc_stream_by_id(dev, term->id); + if (stream == NULL) { + uvc_printk(KERN_INFO, "No streaming interface found " + "for terminal %u.", term->id); + continue; + } + + stream->chain = chain; + ret = uvc_register_video(dev, stream); + if (ret < 0) + return ret; + + term->vdev = stream->vdev; + } + + return 0; +} + +static int uvc_register_chains(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + int ret; + + list_for_each_entry(chain, &dev->chains, list) { + ret = uvc_register_terms(dev, chain); + if (ret < 0) + return ret; + +#ifdef CONFIG_MEDIA_CONTROLLER + ret = uvc_mc_register_entities(chain); + if (ret < 0) { + uvc_printk(KERN_INFO, "Failed to register entites " + "(%d).\n", ret); + } +#endif + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * USB probe, disconnect, suspend and resume + */ + +static int uvc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct uvc_device *dev; + int ret; + + if (id->idVendor && id->idProduct) + uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " + "(%04x:%04x)\n", udev->devpath, id->idVendor, + id->idProduct); + else + uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", + udev->devpath); + + /* Allocate memory for the device and initialize it. */ + if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->entities); + INIT_LIST_HEAD(&dev->chains); + INIT_LIST_HEAD(&dev->streams); + atomic_set(&dev->nstreams, 0); + atomic_set(&dev->users, 0); + atomic_set(&dev->nmappings, 0); + + dev->udev = usb_get_dev(udev); + dev->intf = usb_get_intf(intf); + dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + dev->quirks = (uvc_quirks_param == -1) + ? id->driver_info : uvc_quirks_param; + + if (udev->product != NULL) + strlcpy(dev->name, udev->product, sizeof dev->name); + else + snprintf(dev->name, sizeof dev->name, + "UVC Camera (%04x:%04x)", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + /* Parse the Video Class control descriptor. */ + if (uvc_parse_control(dev) < 0) { + uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " + "descriptors.\n"); + goto error; + } + + uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", + dev->uvc_version >> 8, dev->uvc_version & 0xff, + udev->product ? udev->product : "", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + if (dev->quirks != id->driver_info) { + uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " + "parameter for testing purpose.\n", dev->quirks); + uvc_printk(KERN_INFO, "Please report required quirks to the " + "linux-uvc-devel mailing list.\n"); + } + + /* Register the media and V4L2 devices. */ +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &intf->dev; + strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); + if (udev->serial) + strlcpy(dev->mdev.serial, udev->serial, + sizeof(dev->mdev.serial)); + strcpy(dev->mdev.bus_info, udev->devpath); + dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); + dev->mdev.driver_version = LINUX_VERSION_CODE; + if (media_device_register(&dev->mdev) < 0) + goto error; + + dev->vdev.mdev = &dev->mdev; +#endif + if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) + goto error; + + /* Initialize controls. */ + if (uvc_ctrl_init_device(dev) < 0) + goto error; + + /* Scan the device for video chains. */ + if (uvc_scan_device(dev) < 0) + goto error; + + /* Register video device nodes. */ + if (uvc_register_chains(dev) < 0) + goto error; + + /* Save our data pointer in the interface data. */ + usb_set_intfdata(intf, dev); + + /* Initialize the interrupt URB. */ + if ((ret = uvc_status_init(dev)) < 0) { + uvc_printk(KERN_INFO, "Unable to initialize the status " + "endpoint (%d), status interrupt will not be " + "supported.\n", ret); + } + + uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); + usb_enable_autosuspend(udev); + return 0; + +error: + uvc_unregister_video(dev); + return -ENODEV; +} + +static void uvc_disconnect(struct usb_interface *intf) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + + /* Set the USB interface data to NULL. This can be done outside the + * lock, as there's no other reader. + */ + usb_set_intfdata(intf, NULL); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOSTREAMING) + return; + + dev->state |= UVC_DEV_DISCONNECTED; + + uvc_unregister_video(dev); +} + +static int uvc_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; + + uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + /* Controls are cached on the fly so they don't need to be saved. */ + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOCONTROL) + return uvc_status_suspend(dev); + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_suspend(stream); + } + + uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; +} + +static int __uvc_resume(struct usb_interface *intf, int reset) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; + + uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOCONTROL) { + if (reset) { + int ret = uvc_ctrl_resume_device(dev); + + if (ret < 0) + return ret; + } + + return uvc_status_resume(dev); + } + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_resume(stream, reset); + } + + uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; +} + +static int uvc_resume(struct usb_interface *intf) +{ + return __uvc_resume(intf, 0); +} + +static int uvc_reset_resume(struct usb_interface *intf) +{ + return __uvc_resume(intf, 1); +} + +/* ------------------------------------------------------------------------ + * Module parameters + */ + +static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) +{ + if (uvc_clock_param == CLOCK_MONOTONIC) + return sprintf(buffer, "CLOCK_MONOTONIC"); + else + return sprintf(buffer, "CLOCK_REALTIME"); +} + +static int uvc_clock_param_set(const char *val, struct kernel_param *kp) +{ + if (strncasecmp(val, "clock_", strlen("clock_")) == 0) + val += strlen("clock_"); + + if (strcasecmp(val, "monotonic") == 0) + uvc_clock_param = CLOCK_MONOTONIC; + else if (strcasecmp(val, "realtime") == 0) + uvc_clock_param = CLOCK_REALTIME; + else + return -EINVAL; + + return 0; +} + +module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, + &uvc_clock_param, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); +module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); +module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(quirks, "Forced device quirks"); +module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); +module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); + +/* ------------------------------------------------------------------------ + * Driver initialization and cleanup + */ + +/* + * The Logitech cameras listed below have their interface class set to + * VENDOR_SPEC because they don't announce themselves as UVC devices, even + * though they are compliant. + */ +static struct usb_device_id uvc_ids[] = { + /* LogiLink Wireless Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0416, + .idProduct = 0xa91a, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Genius eFace 2025 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0458, + .idProduct = 0x706e, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam NX-6000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x00f8, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam VX-7000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x0723, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Logitech Quickcam Fusion */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c1, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Orbit MP */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro for Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro 5000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c5, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Dell Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c6, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Cisco VT Camera II */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c7, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Chicony CNF7129 (Asus EEE 100HE) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x04f2, + .idProduct = 0xb071, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE }, + /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x058f, + .idProduct = 0x3820, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Dell XPS m1530 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05a9, + .idProduct = 0x2640, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, + /* Apple Built-In iSight */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05ac, + .idProduct = 0x8501, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_BUILTIN_ISIGHT }, + /* Foxlink ("HP Webcam" on HP Mini 5103) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05c8, + .idProduct = 0x0403, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* Genesys Logic USB 2.0 PC Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05e3, + .idProduct = 0x0505, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Hercules Classic Silver */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x06f8, + .idProduct = 0x300c, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro Vega */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x332d, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x3410, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro Venus - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x3420, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* Ophir Optronics - SPCAM 620U */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0bd3, + .idProduct = 0x0555, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* MT6227 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0e8d, + .idProduct = 0x0004, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_PROBE_DEF }, + /* IMC Networks (Medion Akoya) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x13d3, + .idProduct = 0x5103, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* JMicron USB2.0 XGA WebCam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x152d, + .idProduct = 0x0310, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Syntek (HP Spartan) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5212, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Samsung Q310) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5931, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Packard Bell EasyNote MX52 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a12, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Asus F9SG) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a31, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Asus U3S) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a33, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (JAOtech Smart Terminal) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a34, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Miricle 307K */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x17dc, + .idProduct = 0x0202, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Lenovo Thinkpad SL400/SL500 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x17ef, + .idProduct = 0x480b, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Aveo Technology USB 2.0 Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1871, + .idProduct = 0x0306, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Ecamm Pico iMage */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18cd, + .idProduct = 0xcafe, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Manta MM-353 Plako */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3188, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* FSC WebCam V30S */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3288, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Arkmicro unbranded */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3290, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, + /* The Imaging Source USB CCD cameras */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x199e, + .idProduct = 0x8102, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Bodelin ProScopeHR */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_DEV_HI + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19ab, + .idProduct = 0x1000, + .bcdDevice_hi = 0x0126, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* MSI StarCam 370i */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1b3b, + .idProduct = 0x2951, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* SiGma Micro USB Web Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1c4f, + .idProduct = 0x3000, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, + /* Generic USB Video Class */ + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, + {} +}; + +MODULE_DEVICE_TABLE(usb, uvc_ids); + +struct uvc_driver uvc_driver = { + .driver = { + .name = "uvcvideo", + .probe = uvc_probe, + .disconnect = uvc_disconnect, + .suspend = uvc_suspend, + .resume = uvc_resume, + .reset_resume = uvc_reset_resume, + .id_table = uvc_ids, + .supports_autosuspend = 1, + }, +}; + +static int __init uvc_init(void) +{ + int ret; + + uvc_debugfs_init(); + + ret = usb_register(&uvc_driver.driver); + if (ret < 0) { + uvc_debugfs_cleanup(); + return ret; + } + + printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); + return 0; +} + +static void __exit uvc_cleanup(void) +{ + usb_deregister(&uvc_driver.driver); + uvc_debugfs_cleanup(); +} + +module_init(uvc_init); +module_exit(uvc_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c new file mode 100644 index 000000000000..29e239911d0e --- /dev/null +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -0,0 +1,126 @@ +/* + * uvc_entity.c -- USB Video Class driver + * + * Copyright (C) 2005-2011 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include + +#include + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * Video subdevices registration and unregistration + */ + +static int uvc_mc_register_entity(struct uvc_video_chain *chain, + struct uvc_entity *entity) +{ + const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; + struct media_entity *sink; + unsigned int i; + int ret; + + sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) + ? (entity->vdev ? &entity->vdev->entity : NULL) + : &entity->subdev.entity; + if (sink == NULL) + return 0; + + for (i = 0; i < entity->num_pads; ++i) { + struct media_entity *source; + struct uvc_entity *remote; + u8 remote_pad; + + if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK)) + continue; + + remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]); + if (remote == NULL) + return -EINVAL; + + source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) + ? (remote->vdev ? &remote->vdev->entity : NULL) + : &remote->subdev.entity; + if (source == NULL) + continue; + + remote_pad = remote->num_pads - 1; + ret = media_entity_create_link(source, remote_pad, + sink, i, flags); + if (ret < 0) + return ret; + } + + if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) + return 0; + + return v4l2_device_register_subdev(&chain->dev->vdev, &entity->subdev); +} + +static struct v4l2_subdev_ops uvc_subdev_ops = { +}; + +void uvc_mc_cleanup_entity(struct uvc_entity *entity) +{ + if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) + media_entity_cleanup(&entity->subdev.entity); + else if (entity->vdev != NULL) + media_entity_cleanup(&entity->vdev->entity); +} + +static int uvc_mc_init_entity(struct uvc_entity *entity) +{ + int ret; + + if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { + v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops); + strlcpy(entity->subdev.name, entity->name, + sizeof(entity->subdev.name)); + + ret = media_entity_init(&entity->subdev.entity, + entity->num_pads, entity->pads, 0); + } else if (entity->vdev != NULL) { + ret = media_entity_init(&entity->vdev->entity, + entity->num_pads, entity->pads, 0); + } else + ret = 0; + + return ret; +} + +int uvc_mc_register_entities(struct uvc_video_chain *chain) +{ + struct uvc_entity *entity; + int ret; + + list_for_each_entry(entity, &chain->entities, chain) { + ret = uvc_mc_init_entity(entity); + if (ret < 0) { + uvc_printk(KERN_INFO, "Failed to initialize entity for " + "entity %u\n", entity->id); + return ret; + } + } + + list_for_each_entry(entity, &chain->entities, chain) { + ret = uvc_mc_register_entity(chain, entity); + if (ret < 0) { + uvc_printk(KERN_INFO, "Failed to register entity for " + "entity %u\n", entity->id); + return ret; + } + } + + return 0; +} diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c new file mode 100644 index 000000000000..8510e7259e76 --- /dev/null +++ b/drivers/media/usb/uvc/uvc_isight.c @@ -0,0 +1,137 @@ +/* + * uvc_isight.c -- USB Video Class driver - iSight support + * + * Copyright (C) 2006-2007 + * Ivan N. Zlatev + * Copyright (C) 2008-2009 + * Laurent Pinchart + * + * This program is free software; 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. + * + */ + +#include +#include +#include + +#include "uvcvideo.h" + +/* Built-in iSight webcams implements most of UVC 1.0 except a + * different packet format. Instead of sending a header at the + * beginning of each isochronous transfer payload, the webcam sends a + * single header per image (on its own in a packet), followed by + * packets containing data only. + * + * Offset Size (bytes) Description + * ------------------------------------------------------------------ + * 0x00 1 Header length + * 0x01 1 Flags (UVC-compliant) + * 0x02 4 Always equal to '11223344' + * 0x06 8 Always equal to 'deadbeefdeadface' + * 0x0e 16 Unknown + * + * The header can be prefixed by an optional, unknown-purpose byte. + */ + +static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, + const __u8 *data, unsigned int len) +{ + static const __u8 hdr[] = { + 0x11, 0x22, 0x33, 0x44, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xfa, 0xce + }; + + unsigned int maxlen, nbytes; + __u8 *mem; + int is_header = 0; + + if (buf == NULL) + return 0; + + if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) || + (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) { + uvc_trace(UVC_TRACE_FRAME, "iSight header found\n"); + is_header = 1; + } + + /* Synchronize to the input stream by waiting for a header packet. */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + if (!is_header) { + uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of " + "sync).\n"); + return 0; + } + + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. + */ + if (is_header && buf->bytesused != 0) { + buf->state = UVC_BUF_STATE_DONE; + return -EAGAIN; + } + + /* Copy the video data to the buffer. Skip header packets, as they + * contain no data. + */ + if (!is_header) { + maxlen = buf->length - buf->bytesused; + mem = buf->mem + buf->bytesused; + nbytes = min(len, maxlen); + memcpy(mem, data, nbytes); + buf->bytesused += nbytes; + + if (len > maxlen || buf->bytesused == buf->length) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete " + "(overflow).\n"); + buf->state = UVC_BUF_STATE_DONE; + } + } + + return 0; +} + +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) +{ + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", + urb->iso_frame_desc[i].status); + } + + /* Decode the payload packet. + * uvc_video_decode is entered twice when a frame transition + * has been detected because the end of frame can only be + * reliably detected when the first packet of the new frame + * is processed. The first pass detects the transition and + * closes the previous frame's buffer, the second pass + * processes the data of the first payload of the new frame. + */ + do { + ret = isight_decode(&stream->queue, buf, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + + if (buf == NULL) + break; + + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&stream->queue, + buf); + } while (ret == -EAGAIN); + } +} diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c new file mode 100644 index 000000000000..9288fbd5001b --- /dev/null +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -0,0 +1,359 @@ +/* + * uvc_queue.c -- USB Video Class driver - Buffers management + * + * Copyright (C) 2005-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * Video buffers queue management. + * + * Video queues is initialized by uvc_queue_init(). The function performs + * basic initialization of the uvc_video_queue struct and never fails. + * + * Video buffers are managed by videobuf2. The driver uses a mutex to protect + * the videobuf2 queue operations by serializing calls to videobuf2 and a + * spinlock to protect the IRQ queue that holds the buffers to be processed by + * the driver. + */ + +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations + */ + +static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + struct uvc_streaming *stream = + container_of(queue, struct uvc_streaming, queue); + + if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) + *nbuffers = UVC_MAX_VIDEO_BUFFERS; + + *nplanes = 1; + + sizes[0] = stream->ctrl.dwMaxVideoFrameSize; + + return 0; +} + +static int uvc_buffer_prepare(struct vb2_buffer *vb) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); + return -EINVAL; + } + + if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED)) + return -ENODEV; + + buf->state = UVC_BUF_STATE_QUEUED; + buf->error = 0; + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + buf->bytesused = 0; + else + buf->bytesused = vb2_get_plane_payload(vb, 0); + + return 0; +} + +static void uvc_buffer_queue(struct vb2_buffer *vb) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + unsigned long flags; + + spin_lock_irqsave(&queue->irqlock, flags); + if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { + list_add_tail(&buf->queue, &queue->irqqueue); + } else { + /* If the device is disconnected return the buffer to userspace + * directly. The next QBUF call will fail with -ENODEV. + */ + buf->state = UVC_BUF_STATE_ERROR; + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + } + + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +static int uvc_buffer_finish(struct vb2_buffer *vb) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_streaming *stream = + container_of(queue, struct uvc_streaming, queue); + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + + uvc_video_clock_update(stream, &vb->v4l2_buf, buf); + return 0; +} + +static struct vb2_ops uvc_queue_qops = { + .queue_setup = uvc_queue_setup, + .buf_prepare = uvc_buffer_prepare, + .buf_queue = uvc_buffer_queue, + .buf_finish = uvc_buffer_finish, +}; + +void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, + int drop_corrupted) +{ + queue->queue.type = type; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.drv_priv = queue; + queue->queue.buf_struct_size = sizeof(struct uvc_buffer); + queue->queue.ops = &uvc_queue_qops; + queue->queue.mem_ops = &vb2_vmalloc_memops; + vb2_queue_init(&queue->queue); + + mutex_init(&queue->mutex); + spin_lock_init(&queue->irqlock); + INIT_LIST_HEAD(&queue->irqqueue); + queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 queue operations + */ + +int uvc_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_reqbufs(&queue->queue, rb); + mutex_unlock(&queue->mutex); + + return ret ? ret : rb->count; +} + +void uvc_free_buffers(struct uvc_video_queue *queue) +{ + mutex_lock(&queue->mutex); + vb2_queue_release(&queue->queue); + mutex_unlock(&queue->mutex); +} + +int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_querybuf(&queue->queue, buf); + mutex_unlock(&queue->mutex); + + return ret; +} + +int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_qbuf(&queue->queue, buf); + mutex_unlock(&queue->mutex); + + return ret; +} + +int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf, + int nonblocking) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_dqbuf(&queue->queue, buf, nonblocking); + mutex_unlock(&queue->mutex); + + return ret; +} + +int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_mmap(&queue->queue, vma); + mutex_unlock(&queue->mutex); + + return ret; +} + +#ifndef CONFIG_MMU +unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) +{ + unsigned long ret; + + mutex_lock(&queue->mutex); + ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); + mutex_unlock(&queue->mutex); + return ret; +} +#endif + +unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, + poll_table *wait) +{ + unsigned int ret; + + mutex_lock(&queue->mutex); + ret = vb2_poll(&queue->queue, file, wait); + mutex_unlock(&queue->mutex); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * + */ + +/* + * Check if buffers have been allocated. + */ +int uvc_queue_allocated(struct uvc_video_queue *queue) +{ + int allocated; + + mutex_lock(&queue->mutex); + allocated = vb2_is_busy(&queue->queue); + mutex_unlock(&queue->mutex); + + return allocated; +} + +/* + * Enable or disable the video buffers queue. + * + * The queue must be enabled before starting video acquisition and must be + * disabled after stopping it. This ensures that the video buffers queue + * state can be properly initialized before buffers are accessed from the + * interrupt handler. + * + * Enabling the video queue returns -EBUSY if the queue is already enabled. + * + * Disabling the video queue cancels the queue and removes all buffers from + * the main queue. + * + * This function can't be called from interrupt context. Use + * uvc_queue_cancel() instead. + */ +int uvc_queue_enable(struct uvc_video_queue *queue, int enable) +{ + unsigned long flags; + int ret; + + mutex_lock(&queue->mutex); + if (enable) { + ret = vb2_streamon(&queue->queue, queue->queue.type); + if (ret < 0) + goto done; + + queue->buf_used = 0; + } else { + ret = vb2_streamoff(&queue->queue, queue->queue.type); + if (ret < 0) + goto done; + + spin_lock_irqsave(&queue->irqlock, flags); + INIT_LIST_HEAD(&queue->irqqueue); + spin_unlock_irqrestore(&queue->irqlock, flags); + } + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Cancel the video buffers queue. + * + * Cancelling the queue marks all buffers on the irq queue as erroneous, + * wakes them up and removes them from the queue. + * + * If the disconnect parameter is set, further calls to uvc_queue_buffer will + * fail with -ENODEV. + * + * This function acquires the irq spinlock and can be called from interrupt + * context. + */ +void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) +{ + struct uvc_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&queue->irqlock, flags); + while (!list_empty(&queue->irqqueue)) { + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + list_del(&buf->queue); + buf->state = UVC_BUF_STATE_ERROR; + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + } + /* This must be protected by the irqlock spinlock to avoid race + * conditions between uvc_buffer_queue and the disconnection event that + * could result in an interruptible wait in uvc_dequeue_buffer. Do not + * blindly replace this logic by checking for the UVC_QUEUE_DISCONNECTED + * state outside the queue code. + */ + if (disconnect) + queue->flags |= UVC_QUEUE_DISCONNECTED; + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) +{ + struct uvc_buffer *nextbuf; + unsigned long flags; + + if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) { + buf->error = 0; + buf->state = UVC_BUF_STATE_QUEUED; + vb2_set_plane_payload(&buf->buf, 0, 0); + return buf; + } + + spin_lock_irqsave(&queue->irqlock, flags); + list_del(&buf->queue); + if (!list_empty(&queue->irqqueue)) + nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + else + nextbuf = NULL; + spin_unlock_irqrestore(&queue->irqlock, flags); + + buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; + vb2_set_plane_payload(&buf->buf, 0, buf->bytesused); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); + + return nextbuf; +} diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c new file mode 100644 index 000000000000..b7492775e6ae --- /dev/null +++ b/drivers/media/usb/uvc/uvc_status.c @@ -0,0 +1,237 @@ +/* + * uvc_status.c -- USB Video Class driver - Status endpoint + * + * Copyright (C) 2005-2009 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include + +#include "uvcvideo.h" + +/* -------------------------------------------------------------------------- + * Input device + */ +#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV +static int uvc_input_init(struct uvc_device *dev) +{ + struct input_dev *input; + int ret; + + input = input_allocate_device(); + if (input == NULL) + return -ENOMEM; + + usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys)); + strlcat(dev->input_phys, "/button", sizeof(dev->input_phys)); + + input->name = dev->name; + input->phys = dev->input_phys; + usb_to_input_id(dev->udev, &input->id); + input->dev.parent = &dev->intf->dev; + + __set_bit(EV_KEY, input->evbit); + __set_bit(KEY_CAMERA, input->keybit); + + if ((ret = input_register_device(input)) < 0) + goto error; + + dev->input = input; + return 0; + +error: + input_free_device(input); + return ret; +} + +static void uvc_input_cleanup(struct uvc_device *dev) +{ + if (dev->input) + input_unregister_device(dev->input); +} + +static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, + int value) +{ + if (dev->input) { + input_report_key(dev->input, code, value); + input_sync(dev->input); + } +} + +#else +#define uvc_input_init(dev) +#define uvc_input_cleanup(dev) +#define uvc_input_report_key(dev, code, value) +#endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */ + +/* -------------------------------------------------------------------------- + * Status interrupt endpoint + */ +static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) +{ + if (len < 3) { + uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " + "received.\n"); + return; + } + + if (data[2] == 0) { + if (len < 4) + return; + uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", + data[1], data[3] ? "pressed" : "released", len); + uvc_input_report_key(dev, KEY_CAMERA, data[3]); + } else { + uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " + "len %d.\n", data[1], data[2], data[3], len); + } +} + +static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) +{ + char *attrs[3] = { "value", "info", "failure" }; + + if (len < 6 || data[2] != 0 || data[4] > 2) { + uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " + "received.\n"); + return; + } + + uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", + data[1], data[3], attrs[data[4]], len); +} + +static void uvc_status_complete(struct urb *urb) +{ + struct uvc_device *dev = urb->context; + int len, ret; + + switch (urb->status) { + case 0: + break; + + case -ENOENT: /* usb_kill_urb() called. */ + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + case -EPROTO: /* Device is disconnected (reported by some + * host controller). */ + return; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " + "completion handler.\n", urb->status); + return; + } + + len = urb->actual_length; + if (len > 0) { + switch (dev->status[0] & 0x0f) { + case UVC_STATUS_TYPE_CONTROL: + uvc_event_control(dev, dev->status, len); + break; + + case UVC_STATUS_TYPE_STREAMING: + uvc_event_streaming(dev, dev->status, len); + break; + + default: + uvc_trace(UVC_TRACE_STATUS, "Unknown status event " + "type %u.\n", dev->status[0]); + break; + } + } + + /* Resubmit the URB. */ + urb->interval = dev->int_ep->desc.bInterval; + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", + ret); + } +} + +int uvc_status_init(struct uvc_device *dev) +{ + struct usb_host_endpoint *ep = dev->int_ep; + unsigned int pipe; + int interval; + + if (ep == NULL) + return 0; + + uvc_input_init(dev); + + dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL); + if (dev->status == NULL) + return -ENOMEM; + + dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (dev->int_urb == NULL) { + kfree(dev->status); + return -ENOMEM; + } + + pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); + + /* For high-speed interrupt endpoints, the bInterval value is used as + * an exponent of two. Some developers forgot about it. + */ + interval = ep->desc.bInterval; + if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH && + (dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) + interval = fls(interval) - 1; + + usb_fill_int_urb(dev->int_urb, dev->udev, pipe, + dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete, + dev, interval); + + return 0; +} + +void uvc_status_cleanup(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); + usb_free_urb(dev->int_urb); + kfree(dev->status); + uvc_input_cleanup(dev); +} + +int uvc_status_start(struct uvc_device *dev) +{ + if (dev->int_urb == NULL) + return 0; + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} + +void uvc_status_stop(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); +} + +int uvc_status_suspend(struct uvc_device *dev) +{ + if (atomic_read(&dev->users)) + usb_kill_urb(dev->int_urb); + + return 0; +} + +int uvc_status_resume(struct uvc_device *dev) +{ + if (dev->int_urb == NULL || atomic_read(&dev->users) == 0) + return 0; + + return usb_submit_urb(dev->int_urb, GFP_NOIO); +} + diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c new file mode 100644 index 000000000000..f00db3060e0e --- /dev/null +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -0,0 +1,1317 @@ +/* + * uvc_v4l2.c -- USB Video Class driver - V4L2 API + * + * Copyright (C) 2005-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * UVC ioctls + */ +static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, + struct uvc_xu_control_mapping *xmap) +{ + struct uvc_control_mapping *map; + unsigned int size; + int ret; + + map = kzalloc(sizeof *map, GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->id = xmap->id; + memcpy(map->name, xmap->name, sizeof map->name); + memcpy(map->entity, xmap->entity, sizeof map->entity); + map->selector = xmap->selector; + map->size = xmap->size; + map->offset = xmap->offset; + map->v4l2_type = xmap->v4l2_type; + map->data_type = xmap->data_type; + + switch (xmap->v4l2_type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + break; + + case V4L2_CTRL_TYPE_MENU: + /* Prevent excessive memory consumption, as well as integer + * overflows. + */ + if (xmap->menu_count == 0 || + xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) { + ret = -EINVAL; + goto done; + } + + size = xmap->menu_count * sizeof(*map->menu_info); + map->menu_info = kmalloc(size, GFP_KERNEL); + if (map->menu_info == NULL) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user(map->menu_info, xmap->menu_info, size)) { + ret = -EFAULT; + goto done; + } + + map->menu_count = xmap->menu_count; + break; + + default: + uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type " + "%u.\n", xmap->v4l2_type); + ret = -ENOTTY; + goto done; + } + + ret = uvc_ctrl_add_mapping(chain, map); + +done: + kfree(map->menu_info); + kfree(map); + + return ret; +} + +/* ------------------------------------------------------------------------ + * V4L2 interface + */ + +/* + * Find the frame interval closest to the requested frame interval for the + * given frame format and size. This should be done by the device as part of + * the Video Probe and Commit negotiation, but some hardware don't implement + * that feature. + */ +static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) +{ + unsigned int i; + + if (frame->bFrameIntervalType) { + __u32 best = -1, dist; + + for (i = 0; i < frame->bFrameIntervalType; ++i) { + dist = interval > frame->dwFrameInterval[i] + ? interval - frame->dwFrameInterval[i] + : frame->dwFrameInterval[i] - interval; + + if (dist > best) + break; + + best = dist; + } + + interval = frame->dwFrameInterval[i-1]; + } else { + const __u32 min = frame->dwFrameInterval[0]; + const __u32 max = frame->dwFrameInterval[1]; + const __u32 step = frame->dwFrameInterval[2]; + + interval = min + (interval - min + step/2) / step * step; + if (interval > max) + interval = max; + } + + return interval; +} + +static int uvc_v4l2_try_format(struct uvc_streaming *stream, + struct v4l2_format *fmt, struct uvc_streaming_control *probe, + struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) +{ + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + __u16 rw, rh; + unsigned int d, maxd; + unsigned int i; + __u32 interval; + int ret = 0; + __u8 *fcc; + + if (fmt->type != stream->type) + return -EINVAL; + + fcc = (__u8 *)&fmt->fmt.pix.pixelformat; + uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); + + /* Check if the hardware supports the requested format. */ + for (i = 0; i < stream->nformats; ++i) { + format = &stream->format[i]; + if (format->fcc == fmt->fmt.pix.pixelformat) + break; + } + + if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n", + fmt->fmt.pix.pixelformat); + return -EINVAL; + } + + /* Find the closest image size. The distance between image sizes is + * the size in pixels of the non-overlapping regions between the + * requested size and the frame-specified size. + */ + rw = fmt->fmt.pix.width; + rh = fmt->fmt.pix.height; + maxd = (unsigned int)-1; + + for (i = 0; i < format->nframes; ++i) { + __u16 w = format->frame[i].wWidth; + __u16 h = format->frame[i].wHeight; + + d = min(w, rw) * min(h, rh); + d = w*h + rw*rh - 2*d; + if (d < maxd) { + maxd = d; + frame = &format->frame[i]; + } + + if (maxd == 0) + break; + } + + if (frame == NULL) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", + fmt->fmt.pix.width, fmt->fmt.pix.height); + return -EINVAL; + } + + /* Use the default frame interval. */ + interval = frame->dwDefaultFrameInterval; + uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " + "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, + (100000000/interval)%10); + + /* Set the format index, frame index and frame interval. */ + memset(probe, 0, sizeof *probe); + probe->bmHint = 1; /* dwFrameInterval */ + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); + /* Some webcams stall the probe control set request when the + * dwMaxVideoFrameSize field is set to zero. The UVC specification + * clearly states that the field is read-only from the host, so this + * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by + * the webcam to work around the problem. + * + * The workaround could probably be enabled for all webcams, so the + * quirk can be removed if needed. It's currently useful to detect + * webcam bugs and fix them before they hit the market (providing + * developers test their webcams with the Linux driver as well as with + * the Windows driver). + */ + mutex_lock(&stream->mutex); + if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) + probe->dwMaxVideoFrameSize = + stream->ctrl.dwMaxVideoFrameSize; + + /* Probe the device. */ + ret = uvc_probe_video(stream, probe); + mutex_unlock(&stream->mutex); + if (ret < 0) + goto done; + + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + if (uvc_format != NULL) + *uvc_format = format; + if (uvc_frame != NULL) + *uvc_frame = frame; + +done: + return ret; +} + +static int uvc_v4l2_get_format(struct uvc_streaming *stream, + struct v4l2_format *fmt) +{ + struct uvc_format *format; + struct uvc_frame *frame; + int ret = 0; + + if (fmt->type != stream->type) + return -EINVAL; + + mutex_lock(&stream->mutex); + format = stream->cur_format; + frame = stream->cur_frame; + + if (format == NULL || frame == NULL) { + ret = -EINVAL; + goto done; + } + + fmt->fmt.pix.pixelformat = format->fcc; + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + +done: + mutex_unlock(&stream->mutex); + return ret; +} + +static int uvc_v4l2_set_format(struct uvc_streaming *stream, + struct v4l2_format *fmt) +{ + struct uvc_streaming_control probe; + struct uvc_format *format; + struct uvc_frame *frame; + int ret; + + if (fmt->type != stream->type) + return -EINVAL; + + ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame); + if (ret < 0) + return ret; + + mutex_lock(&stream->mutex); + + if (uvc_queue_allocated(&stream->queue)) { + ret = -EBUSY; + goto done; + } + + memcpy(&stream->ctrl, &probe, sizeof probe); + stream->cur_format = format; + stream->cur_frame = frame; + +done: + mutex_unlock(&stream->mutex); + return ret; +} + +static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, + struct v4l2_streamparm *parm) +{ + uint32_t numerator, denominator; + + if (parm->type != stream->type) + return -EINVAL; + + mutex_lock(&stream->mutex); + numerator = stream->ctrl.dwFrameInterval; + mutex_unlock(&stream->mutex); + + denominator = 10000000; + uvc_simplify_fraction(&numerator, &denominator, 8, 333); + + memset(parm, 0, sizeof *parm); + parm->type = stream->type; + + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.capturemode = 0; + parm->parm.capture.timeperframe.numerator = numerator; + parm->parm.capture.timeperframe.denominator = denominator; + parm->parm.capture.extendedmode = 0; + parm->parm.capture.readbuffers = 0; + } else { + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.output.outputmode = 0; + parm->parm.output.timeperframe.numerator = numerator; + parm->parm.output.timeperframe.denominator = denominator; + } + + return 0; +} + +static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, + struct v4l2_streamparm *parm) +{ + struct uvc_streaming_control probe; + struct v4l2_fract timeperframe; + uint32_t interval; + int ret; + + if (parm->type != stream->type) + return -EINVAL; + + if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + timeperframe = parm->parm.capture.timeperframe; + else + timeperframe = parm->parm.output.timeperframe; + + interval = uvc_fraction_to_interval(timeperframe.numerator, + timeperframe.denominator); + uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", + timeperframe.numerator, timeperframe.denominator, interval); + + mutex_lock(&stream->mutex); + + if (uvc_queue_streaming(&stream->queue)) { + mutex_unlock(&stream->mutex); + return -EBUSY; + } + + memcpy(&probe, &stream->ctrl, sizeof probe); + probe.dwFrameInterval = + uvc_try_frame_interval(stream->cur_frame, interval); + + /* Probe the device with the new settings. */ + ret = uvc_probe_video(stream, &probe); + if (ret < 0) { + mutex_unlock(&stream->mutex); + return ret; + } + + memcpy(&stream->ctrl, &probe, sizeof probe); + mutex_unlock(&stream->mutex); + + /* Return the actual frame period. */ + timeperframe.numerator = probe.dwFrameInterval; + timeperframe.denominator = 10000000; + uvc_simplify_fraction(&timeperframe.numerator, + &timeperframe.denominator, 8, 333); + + if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + parm->parm.capture.timeperframe = timeperframe; + else + parm->parm.output.timeperframe = timeperframe; + + return 0; +} + +/* ------------------------------------------------------------------------ + * Privilege management + */ + +/* + * Privilege management is the multiple-open implementation basis. The current + * implementation is completely transparent for the end-user and doesn't + * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls. + * Those ioctls enable finer control on the device (by making possible for a + * user to request exclusive access to a device), but are not mature yet. + * Switching to the V4L2 priority mechanism might be considered in the future + * if this situation changes. + * + * Each open instance of a UVC device can either be in a privileged or + * unprivileged state. Only a single instance can be in a privileged state at + * a given time. Trying to perform an operation that requires privileges will + * automatically acquire the required privileges if possible, or return -EBUSY + * otherwise. Privileges are dismissed when closing the instance or when + * freeing the video buffers using VIDIOC_REQBUFS. + * + * Operations that require privileges are: + * + * - VIDIOC_S_INPUT + * - VIDIOC_S_PARM + * - VIDIOC_S_FMT + * - VIDIOC_REQBUFS + */ +static int uvc_acquire_privileges(struct uvc_fh *handle) +{ + /* Always succeed if the handle is already privileged. */ + if (handle->state == UVC_HANDLE_ACTIVE) + return 0; + + /* Check if the device already has a privileged handle. */ + if (atomic_inc_return(&handle->stream->active) != 1) { + atomic_dec(&handle->stream->active); + return -EBUSY; + } + + handle->state = UVC_HANDLE_ACTIVE; + return 0; +} + +static void uvc_dismiss_privileges(struct uvc_fh *handle) +{ + if (handle->state == UVC_HANDLE_ACTIVE) + atomic_dec(&handle->stream->active); + + handle->state = UVC_HANDLE_PASSIVE; +} + +static int uvc_has_privileges(struct uvc_fh *handle) +{ + return handle->state == UVC_HANDLE_ACTIVE; +} + +/* ------------------------------------------------------------------------ + * V4L2 file operations + */ + +static int uvc_v4l2_open(struct file *file) +{ + struct uvc_streaming *stream; + struct uvc_fh *handle; + int ret = 0; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); + stream = video_drvdata(file); + + if (stream->dev->state & UVC_DEV_DISCONNECTED) + return -ENODEV; + + ret = usb_autopm_get_interface(stream->dev->intf); + if (ret < 0) + return ret; + + /* Create the device handle. */ + handle = kzalloc(sizeof *handle, GFP_KERNEL); + if (handle == NULL) { + usb_autopm_put_interface(stream->dev->intf); + return -ENOMEM; + } + + if (atomic_inc_return(&stream->dev->users) == 1) { + ret = uvc_status_start(stream->dev); + if (ret < 0) { + usb_autopm_put_interface(stream->dev->intf); + atomic_dec(&stream->dev->users); + kfree(handle); + return ret; + } + } + + v4l2_fh_init(&handle->vfh, stream->vdev); + v4l2_fh_add(&handle->vfh); + handle->chain = stream->chain; + handle->stream = stream; + handle->state = UVC_HANDLE_PASSIVE; + file->private_data = handle; + + return 0; +} + +static int uvc_v4l2_release(struct file *file) +{ + struct uvc_fh *handle = file->private_data; + struct uvc_streaming *stream = handle->stream; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); + + /* Only free resources if this is a privileged handle. */ + if (uvc_has_privileges(handle)) { + uvc_video_enable(stream, 0); + uvc_free_buffers(&stream->queue); + } + + /* Release the file handle. */ + uvc_dismiss_privileges(handle); + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); + kfree(handle); + file->private_data = NULL; + + if (atomic_dec_return(&stream->dev->users) == 0) + uvc_status_stop(stream->dev); + + usb_autopm_put_interface(stream->dev->intf); + return 0; +} + +static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_fh *handle = file->private_data; + struct uvc_video_chain *chain = handle->chain; + struct uvc_streaming *stream = handle->stream; + long ret = 0; + + switch (cmd) { + /* Query capabilities */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap, 0, sizeof *cap); + strlcpy(cap->driver, "uvcvideo", sizeof cap->driver); + strlcpy(cap->card, vdev->name, sizeof cap->card); + usb_make_path(stream->dev->udev, + cap->bus_info, sizeof(cap->bus_info)); + cap->version = LINUX_VERSION_CODE; + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; + break; + } + + /* Get, Set & Query control */ + case VIDIOC_QUERYCTRL: + return uvc_query_v4l2_ctrl(chain, arg); + + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; + + ret = uvc_ctrl_get(chain, &xctrl); + uvc_ctrl_rollback(handle); + if (ret >= 0) + ctrl->value = xctrl.value; + break; + } + + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + xctrl.value = ctrl->value; + + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; + + ret = uvc_ctrl_set(chain, &xctrl); + if (ret < 0) { + uvc_ctrl_rollback(handle); + return ret; + } + ret = uvc_ctrl_commit(handle, &xctrl, 1); + if (ret == 0) + ctrl->value = xctrl.value; + break; + } + + case VIDIOC_QUERYMENU: + return uvc_query_v4l2_menu(chain, arg); + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; + + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_get(chain, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(handle); + ctrls->error_idx = i; + return ret; + } + } + ctrls->error_idx = 0; + ret = uvc_ctrl_rollback(handle); + break; + } + + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; + + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_set(chain, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(handle); + ctrls->error_idx = i; + return ret; + } + } + + ctrls->error_idx = 0; + + if (cmd == VIDIOC_S_EXT_CTRLS) + ret = uvc_ctrl_commit(handle, + ctrls->controls, ctrls->count); + else + ret = uvc_ctrl_rollback(handle); + break; + } + + /* Get, Set & Enum input */ + case VIDIOC_ENUMINPUT: + { + const struct uvc_entity *selector = chain->selector; + struct v4l2_input *input = arg; + struct uvc_entity *iterm = NULL; + u32 index = input->index; + int pin = 0; + + if (selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (index != 0) + return -EINVAL; + list_for_each_entry(iterm, &chain->entities, chain) { + if (UVC_ENTITY_IS_ITERM(iterm)) + break; + } + pin = iterm->id; + } else if (index < selector->bNrInPins) { + pin = selector->baSourceID[index]; + list_for_each_entry(iterm, &chain->entities, chain) { + if (!UVC_ENTITY_IS_ITERM(iterm)) + continue; + if (iterm->id == pin) + break; + } + } + + if (iterm == NULL || iterm->id != pin) + return -EINVAL; + + memset(input, 0, sizeof *input); + input->index = index; + strlcpy(input->name, iterm->name, sizeof input->name); + if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA) + input->type = V4L2_INPUT_TYPE_CAMERA; + break; + } + + case VIDIOC_G_INPUT: + { + u8 input; + + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + *(int *)arg = 0; + break; + } + + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + chain->selector->id, chain->dev->intfnum, + UVC_SU_INPUT_SELECT_CONTROL, &input, 1); + if (ret < 0) + return ret; + + *(int *)arg = input - 1; + break; + } + + case VIDIOC_S_INPUT: + { + u32 input = *(u32 *)arg + 1; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (input != 1) + return -EINVAL; + break; + } + + if (input == 0 || input > chain->selector->bNrInPins) + return -EINVAL; + + return uvc_query_ctrl(chain->dev, UVC_SET_CUR, + chain->selector->id, chain->dev->intfnum, + UVC_SU_INPUT_SELECT_CONTROL, &input, 1); + } + + /* Try, Get, Set & Enum format */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *fmt = arg; + struct uvc_format *format; + enum v4l2_buf_type type = fmt->type; + __u32 index = fmt->index; + + if (fmt->type != stream->type || + fmt->index >= stream->nformats) + return -EINVAL; + + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + + format = &stream->format[fmt->index]; + fmt->flags = 0; + if (format->flags & UVC_FMT_FLAG_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + strlcpy(fmt->description, format->name, + sizeof fmt->description); + fmt->description[sizeof fmt->description - 1] = 0; + fmt->pixelformat = format->fcc; + break; + } + + case VIDIOC_TRY_FMT: + { + struct uvc_streaming_control probe; + + return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); + } + + case VIDIOC_S_FMT: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_format(stream, arg); + + case VIDIOC_G_FMT: + return uvc_v4l2_get_format(stream, arg); + + /* Frame size enumeration */ + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *fsize = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame; + int i; + + /* Look for the given pixel format */ + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == + fsize->pixel_format) { + format = &stream->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + if (fsize->index >= format->nframes) + return -EINVAL; + + frame = &format->frame[fsize->index]; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = frame->wWidth; + fsize->discrete.height = frame->wHeight; + break; + } + + /* Frame interval enumeration */ + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *fival = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + int i; + + /* Look for the given pixel format and frame size */ + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == + fival->pixel_format) { + format = &stream->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + for (i = 0; i < format->nframes; i++) { + if (format->frame[i].wWidth == fival->width && + format->frame[i].wHeight == fival->height) { + frame = &format->frame[i]; + break; + } + } + if (frame == NULL) + return -EINVAL; + + if (frame->bFrameIntervalType) { + if (fival->index >= frame->bFrameIntervalType) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = + frame->dwFrameInterval[fival->index]; + fival->discrete.denominator = 10000000; + uvc_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + } else { + fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + fival->stepwise.min.numerator = + frame->dwFrameInterval[0]; + fival->stepwise.min.denominator = 10000000; + fival->stepwise.max.numerator = + frame->dwFrameInterval[1]; + fival->stepwise.max.denominator = 10000000; + fival->stepwise.step.numerator = + frame->dwFrameInterval[2]; + fival->stepwise.step.denominator = 10000000; + uvc_simplify_fraction(&fival->stepwise.min.numerator, + &fival->stepwise.min.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.max.numerator, + &fival->stepwise.max.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.step.numerator, + &fival->stepwise.step.denominator, 8, 333); + } + break; + } + + /* Get & Set streaming parameters */ + case VIDIOC_G_PARM: + return uvc_v4l2_get_streamparm(stream, arg); + + case VIDIOC_S_PARM: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_streamparm(stream, arg); + + /* Cropping and scaling */ + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *ccap = arg; + + if (ccap->type != stream->type) + return -EINVAL; + + ccap->bounds.left = 0; + ccap->bounds.top = 0; + + mutex_lock(&stream->mutex); + ccap->bounds.width = stream->cur_frame->wWidth; + ccap->bounds.height = stream->cur_frame->wHeight; + mutex_unlock(&stream->mutex); + + ccap->defrect = ccap->bounds; + + ccap->pixelaspect.numerator = 1; + ccap->pixelaspect.denominator = 1; + break; + } + + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + return -EINVAL; + + /* Buffers & streaming */ + case VIDIOC_REQBUFS: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + mutex_lock(&stream->mutex); + ret = uvc_alloc_buffers(&stream->queue, arg); + mutex_unlock(&stream->mutex); + if (ret < 0) + return ret; + + if (ret == 0) + uvc_dismiss_privileges(handle); + + ret = 0; + break; + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_query_buffer(&stream->queue, buf); + } + + case VIDIOC_QBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_queue_buffer(&stream->queue, arg); + + case VIDIOC_DQBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_dequeue_buffer(&stream->queue, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + { + int *type = arg; + + if (*type != stream->type) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + mutex_lock(&stream->mutex); + ret = uvc_video_enable(stream, 1); + mutex_unlock(&stream->mutex); + if (ret < 0) + return ret; + break; + } + + case VIDIOC_STREAMOFF: + { + int *type = arg; + + if (*type != stream->type) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_video_enable(stream, 0); + } + + case VIDIOC_SUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_event_subscribe(&handle->vfh, sub, 0, + &uvc_ctrl_sub_ev_ops); + default: + return -EINVAL; + } + } + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_event_unsubscribe(&handle->vfh, arg); + + case VIDIOC_DQEVENT: + return v4l2_event_dequeue(&handle->vfh, arg, + file->f_flags & O_NONBLOCK); + + /* Analog video standards make no sense for digital cameras. */ + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + + case VIDIOC_OVERLAY: + + case VIDIOC_ENUMAUDIO: + case VIDIOC_ENUMAUDOUT: + + case VIDIOC_ENUMOUTPUT: + uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); + return -EINVAL; + + case UVCIOC_CTRL_MAP: + return uvc_ioctl_ctrl_map(chain, arg); + + case UVCIOC_CTRL_QUERY: + return uvc_xu_ctrl_query(chain, arg); + + default: + uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); + return -ENOTTY; + } + + return ret; +} + +static long uvc_v4l2_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + if (uvc_trace_param & UVC_TRACE_IOCTL) { + uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); + v4l_printk_ioctl(NULL, cmd); + printk(")\n"); + } + + return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); +} + +#ifdef CONFIG_COMPAT +struct uvc_xu_control_mapping32 { + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + __u32 v4l2_type; + __u32 data_type; + + compat_caddr_t menu_info; + __u32 menu_count; + + __u32 reserved[4]; +}; + +static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, + const struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) || + __get_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + memset(kp->reserved, 0, sizeof(kp->reserved)); + + if (kp->menu_count == 0) { + kp->menu_info = NULL; + return 0; + } + + if (__get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus)); + if (kmenus == NULL) + return -EFAULT; + kp->menu_info = kmenus; + + if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, + struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus = kp->menu_info; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) || + __put_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + if (__clear_user(up->reserved, sizeof(up->reserved))) + return -EFAULT; + + if (kp->menu_count == 0) + return 0; + + if (get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + + if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +struct uvc_xu_control_query32 { + __u8 unit; + __u8 selector; + __u8 query; + __u16 size; + compat_caddr_t data; +}; + +static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp, + const struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) { + kp->data = NULL; + return 0; + } + + if (__get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + kdata = compat_alloc_user_space(kp->size); + if (kdata == NULL) + return -EFAULT; + kp->data = kdata; + + if (copy_in_user(kdata, udata, kp->size)) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, + struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata = kp->data; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) + return 0; + + if (get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + if (copy_in_user(udata, kdata, kp->size)) + return -EFAULT; + + return 0; +} + +#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) +#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) + +static long uvc_v4l2_compat_ioctl32(struct file *file, + unsigned int cmd, unsigned long arg) +{ + union { + struct uvc_xu_control_mapping xmap; + struct uvc_xu_control_query xqry; + } karg; + void __user *up = compat_ptr(arg); + mm_segment_t old_fs; + long ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP32: + cmd = UVCIOC_CTRL_MAP; + ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY32: + cmd = UVCIOC_CTRL_QUERY; + ret = uvc_v4l2_get_xu_query(&karg.xqry, up); + break; + + default: + return -ENOIOCTLCMD; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if (ret < 0) + return ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP: + ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY: + ret = uvc_v4l2_put_xu_query(&karg.xqry, up); + break; + } + + return ret; +} +#endif + +static ssize_t uvc_v4l2_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); + return -EINVAL; +} + +static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct uvc_fh *handle = file->private_data; + struct uvc_streaming *stream = handle->stream; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); + + return uvc_queue_mmap(&stream->queue, vma); +} + +static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) +{ + struct uvc_fh *handle = file->private_data; + struct uvc_streaming *stream = handle->stream; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); + + return uvc_queue_poll(&stream->queue, file, wait); +} + +#ifndef CONFIG_MMU +static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct uvc_fh *handle = file->private_data; + struct uvc_streaming *stream = handle->stream; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n"); + + return uvc_queue_get_unmapped_area(&stream->queue, pgoff); +} +#endif + +const struct v4l2_file_operations uvc_fops = { + .owner = THIS_MODULE, + .open = uvc_v4l2_open, + .release = uvc_v4l2_release, + .unlocked_ioctl = uvc_v4l2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = uvc_v4l2_compat_ioctl32, +#endif + .read = uvc_v4l2_read, + .mmap = uvc_v4l2_mmap, + .poll = uvc_v4l2_poll, +#ifndef CONFIG_MMU + .get_unmapped_area = uvc_v4l2_get_unmapped_area, +#endif +}; + diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c new file mode 100644 index 000000000000..1c15b4227bdb --- /dev/null +++ b/drivers/media/usb/uvc/uvc_video.c @@ -0,0 +1,1879 @@ +/* + * uvc_video.c -- USB Video Class driver - Video handling + * + * Copyright (C) 2005-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size, + int timeout) +{ + __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + unsigned int pipe; + + pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) + : usb_sndctrlpipe(dev->udev, 0); + type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; + + return usb_control_msg(dev->udev, pipe, query, type, cs << 8, + unit << 8 | intfnum, data, size, timeout); +} + +static const char *uvc_query_name(__u8 query) +{ + switch (query) { + case UVC_SET_CUR: + return "SET_CUR"; + case UVC_GET_CUR: + return "GET_CUR"; + case UVC_GET_MIN: + return "GET_MIN"; + case UVC_GET_MAX: + return "GET_MAX"; + case UVC_GET_RES: + return "GET_RES"; + case UVC_GET_LEN: + return "GET_LEN"; + case UVC_GET_INFO: + return "GET_INFO"; + case UVC_GET_DEF: + return "GET_DEF"; + default: + return ""; + } +} + +int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size) +{ + int ret; + + ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, + UVC_CTRL_CONTROL_TIMEOUT); + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on " + "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, + unit, ret, size); + return -EIO; + } + + return 0; +} + +static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, + struct uvc_streaming_control *ctrl) +{ + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + unsigned int i; + + for (i = 0; i < stream->nformats; ++i) { + if (stream->format[i].index == ctrl->bFormatIndex) { + format = &stream->format[i]; + break; + } + } + + if (format == NULL) + return; + + for (i = 0; i < format->nframes; ++i) { + if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { + frame = &format->frame[i]; + break; + } + } + + if (frame == NULL) + return; + + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || + (ctrl->dwMaxVideoFrameSize == 0 && + stream->dev->uvc_version < 0x0110)) + ctrl->dwMaxVideoFrameSize = + frame->dwMaxVideoFrameBufferSize; + + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) && + stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && + stream->intf->num_altsetting > 1) { + u32 interval; + u32 bandwidth; + + interval = (ctrl->dwFrameInterval > 100000) + ? ctrl->dwFrameInterval + : frame->dwFrameInterval[0]; + + /* Compute a bandwidth estimation by multiplying the frame + * size by the number of video frames per second, divide the + * result by the number of USB frames (or micro-frames for + * high-speed devices) per second and add the UVC header size + * (assumed to be 12 bytes long). + */ + bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; + bandwidth *= 10000000 / interval + 1; + bandwidth /= 1000; + if (stream->dev->udev->speed == USB_SPEED_HIGH) + bandwidth /= 8; + bandwidth += 12; + + /* The bandwidth estimate is too low for many cameras. Don't use + * maximum packet sizes lower than 1024 bytes to try and work + * around the problem. According to measurements done on two + * different camera models, the value is high enough to get most + * resolutions working while not preventing two simultaneous + * VGA streams at 15 fps. + */ + bandwidth = max_t(u32, bandwidth, 1024); + + ctrl->dwMaxPayloadTransferSize = bandwidth; + } +} + +static int uvc_get_video_ctrl(struct uvc_streaming *stream, + struct uvc_streaming_control *ctrl, int probe, __u8 query) +{ + __u8 *data; + __u16 size; + int ret; + + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; + if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && + query == UVC_GET_DEF) + return -EIO; + + data = kmalloc(size, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, + probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, + size, uvc_timeout_param); + + if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { + /* Some cameras, mostly based on Bison Electronics chipsets, + * answer a GET_MIN or GET_MAX request with the wCompQuality + * field only. + */ + uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non " + "compliance - GET_MIN/MAX(PROBE) incorrectly " + "supported. Enabling workaround.\n"); + memset(ctrl, 0, sizeof *ctrl); + ctrl->wCompQuality = le16_to_cpup((__le16 *)data); + ret = 0; + goto out; + } else if (query == UVC_GET_DEF && probe == 1 && ret != size) { + /* Many cameras don't support the GET_DEF request on their + * video probe control. Warn once and return, the caller will + * fall back to GET_CUR. + */ + uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non " + "compliance - GET_DEF(PROBE) not supported. " + "Enabling workaround.\n"); + ret = -EIO; + goto out; + } else if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : " + "%d (exp. %u).\n", query, probe ? "probe" : "commit", + ret, size); + ret = -EIO; + goto out; + } + + ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); + ctrl->bFormatIndex = data[2]; + ctrl->bFrameIndex = data[3]; + ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]); + ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]); + ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]); + ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); + ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); + ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); + ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]); + ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]); + + if (size == 34) { + ctrl->dwClockFrequency = get_unaligned_le32(&data[26]); + ctrl->bmFramingInfo = data[30]; + ctrl->bPreferedVersion = data[31]; + ctrl->bMinVersion = data[32]; + ctrl->bMaxVersion = data[33]; + } else { + ctrl->dwClockFrequency = stream->dev->clock_frequency; + ctrl->bmFramingInfo = 0; + ctrl->bPreferedVersion = 0; + ctrl->bMinVersion = 0; + ctrl->bMaxVersion = 0; + } + + /* Some broken devices return null or wrong dwMaxVideoFrameSize and + * dwMaxPayloadTransferSize fields. Try to get the value from the + * format and frame descriptors. + */ + uvc_fixup_video_ctrl(stream, ctrl); + ret = 0; + +out: + kfree(data); + return ret; +} + +static int uvc_set_video_ctrl(struct uvc_streaming *stream, + struct uvc_streaming_control *ctrl, int probe) +{ + __u8 *data; + __u16 size; + int ret; + + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; + data = kzalloc(size, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint); + data[2] = ctrl->bFormatIndex; + data[3] = ctrl->bFrameIndex; + *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval); + *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate); + *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate); + *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality); + *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize); + *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay); + put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]); + put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]); + + if (size == 34) { + put_unaligned_le32(ctrl->dwClockFrequency, &data[26]); + data[30] = ctrl->bmFramingInfo; + data[31] = ctrl->bPreferedVersion; + data[32] = ctrl->bMinVersion; + data[33] = ctrl->bMaxVersion; + } + + ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, + probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, + size, uvc_timeout_param); + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to set UVC %s control : " + "%d (exp. %u).\n", probe ? "probe" : "commit", + ret, size); + ret = -EIO; + } + + kfree(data); + return ret; +} + +int uvc_probe_video(struct uvc_streaming *stream, + struct uvc_streaming_control *probe) +{ + struct uvc_streaming_control probe_min, probe_max; + __u16 bandwidth; + unsigned int i; + int ret; + + /* Perform probing. The device should adjust the requested values + * according to its capabilities. However, some devices, namely the + * first generation UVC Logitech webcams, don't implement the Video + * Probe control properly, and just return the needed bandwidth. For + * that reason, if the needed bandwidth exceeds the maximum available + * bandwidth, try to lower the quality. + */ + ret = uvc_set_video_ctrl(stream, probe, 1); + if (ret < 0) + goto done; + + /* Get the minimum and maximum values for compression settings. */ + if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { + ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN); + if (ret < 0) + goto done; + ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX); + if (ret < 0) + goto done; + + probe->wCompQuality = probe_max.wCompQuality; + } + + for (i = 0; i < 2; ++i) { + ret = uvc_set_video_ctrl(stream, probe, 1); + if (ret < 0) + goto done; + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); + if (ret < 0) + goto done; + + if (stream->intf->num_altsetting == 1) + break; + + bandwidth = probe->dwMaxPayloadTransferSize; + if (bandwidth <= stream->maxpsize) + break; + + if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { + ret = -ENOSPC; + goto done; + } + + /* TODO: negotiate compression parameters */ + probe->wKeyFrameRate = probe_min.wKeyFrameRate; + probe->wPFrameRate = probe_min.wPFrameRate; + probe->wCompQuality = probe_max.wCompQuality; + probe->wCompWindowSize = probe_min.wCompWindowSize; + } + +done: + return ret; +} + +static int uvc_commit_video(struct uvc_streaming *stream, + struct uvc_streaming_control *probe) +{ + return uvc_set_video_ctrl(stream, probe, 0); +} + +/* ----------------------------------------------------------------------------- + * Clocks and timestamps + */ + +static void +uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, + const __u8 *data, int len) +{ + struct uvc_clock_sample *sample; + unsigned int header_size; + bool has_pts = false; + bool has_scr = false; + unsigned long flags; + struct timespec ts; + u16 host_sof; + u16 dev_sof; + + switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { + case UVC_STREAM_PTS | UVC_STREAM_SCR: + header_size = 12; + has_pts = true; + has_scr = true; + break; + case UVC_STREAM_PTS: + header_size = 6; + has_pts = true; + break; + case UVC_STREAM_SCR: + header_size = 8; + has_scr = true; + break; + default: + header_size = 2; + break; + } + + /* Check for invalid headers. */ + if (len < header_size) + return; + + /* Extract the timestamps: + * + * - store the frame PTS in the buffer structure + * - if the SCR field is present, retrieve the host SOF counter and + * kernel timestamps and store them with the SCR STC and SOF fields + * in the ring buffer + */ + if (has_pts && buf != NULL) + buf->pts = get_unaligned_le32(&data[2]); + + if (!has_scr) + return; + + /* To limit the amount of data, drop SCRs with an SOF identical to the + * previous one. + */ + dev_sof = get_unaligned_le16(&data[header_size - 2]); + if (dev_sof == stream->clock.last_sof) + return; + + stream->clock.last_sof = dev_sof; + + host_sof = usb_get_current_frame_number(stream->dev->udev); + ktime_get_ts(&ts); + + /* The UVC specification allows device implementations that can't obtain + * the USB frame number to keep their own frame counters as long as they + * match the size and frequency of the frame number associated with USB + * SOF tokens. The SOF values sent by such devices differ from the USB + * SOF tokens by a fixed offset that needs to be estimated and accounted + * for to make timestamp recovery as accurate as possible. + * + * The offset is estimated the first time a device SOF value is received + * as the difference between the host and device SOF values. As the two + * SOF values can differ slightly due to transmission delays, consider + * that the offset is null if the difference is not higher than 10 ms + * (negative differences can not happen and are thus considered as an + * offset). The video commit control wDelay field should be used to + * compute a dynamic threshold instead of using a fixed 10 ms value, but + * devices don't report reliable wDelay values. + * + * See uvc_video_clock_host_sof() for an explanation regarding why only + * the 8 LSBs of the delta are kept. + */ + if (stream->clock.sof_offset == (u16)-1) { + u16 delta_sof = (host_sof - dev_sof) & 255; + if (delta_sof >= 10) + stream->clock.sof_offset = delta_sof; + else + stream->clock.sof_offset = 0; + } + + dev_sof = (dev_sof + stream->clock.sof_offset) & 2047; + + spin_lock_irqsave(&stream->clock.lock, flags); + + sample = &stream->clock.samples[stream->clock.head]; + sample->dev_stc = get_unaligned_le32(&data[header_size - 6]); + sample->dev_sof = dev_sof; + sample->host_sof = host_sof; + sample->host_ts = ts; + + /* Update the sliding window head and count. */ + stream->clock.head = (stream->clock.head + 1) % stream->clock.size; + + if (stream->clock.count < stream->clock.size) + stream->clock.count++; + + spin_unlock_irqrestore(&stream->clock.lock, flags); +} + +static void uvc_video_clock_reset(struct uvc_streaming *stream) +{ + struct uvc_clock *clock = &stream->clock; + + clock->head = 0; + clock->count = 0; + clock->last_sof = -1; + clock->sof_offset = -1; +} + +static int uvc_video_clock_init(struct uvc_streaming *stream) +{ + struct uvc_clock *clock = &stream->clock; + + spin_lock_init(&clock->lock); + clock->size = 32; + + clock->samples = kmalloc(clock->size * sizeof(*clock->samples), + GFP_KERNEL); + if (clock->samples == NULL) + return -ENOMEM; + + uvc_video_clock_reset(stream); + + return 0; +} + +static void uvc_video_clock_cleanup(struct uvc_streaming *stream) +{ + kfree(stream->clock.samples); + stream->clock.samples = NULL; +} + +/* + * uvc_video_clock_host_sof - Return the host SOF value for a clock sample + * + * Host SOF counters reported by usb_get_current_frame_number() usually don't + * cover the whole 11-bits SOF range (0-2047) but are limited to the HCI frame + * schedule window. They can be limited to 8, 9 or 10 bits depending on the host + * controller and its configuration. + * + * We thus need to recover the SOF value corresponding to the host frame number. + * As the device and host frame numbers are sampled in a short interval, the + * difference between their values should be equal to a small delta plus an + * integer multiple of 256 caused by the host frame number limited precision. + * + * To obtain the recovered host SOF value, compute the small delta by masking + * the high bits of the host frame counter and device SOF difference and add it + * to the device SOF value. + */ +static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample) +{ + /* The delta value can be negative. */ + s8 delta_sof; + + delta_sof = (sample->host_sof - sample->dev_sof) & 255; + + return (sample->dev_sof + delta_sof) & 2047; +} + +/* + * uvc_video_clock_update - Update the buffer timestamp + * + * This function converts the buffer PTS timestamp to the host clock domain by + * going through the USB SOF clock domain and stores the result in the V4L2 + * buffer timestamp field. + * + * The relationship between the device clock and the host clock isn't known. + * However, the device and the host share the common USB SOF clock which can be + * used to recover that relationship. + * + * The relationship between the device clock and the USB SOF clock is considered + * to be linear over the clock samples sliding window and is given by + * + * SOF = m * PTS + p + * + * Several methods to compute the slope (m) and intercept (p) can be used. As + * the clock drift should be small compared to the sliding window size, we + * assume that the line that goes through the points at both ends of the window + * is a good approximation. Naming those points P1 and P2, we get + * + * SOF = (SOF2 - SOF1) / (STC2 - STC1) * PTS + * + (SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) + * + * or + * + * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1) + * + * to avoid loosing precision in the division. Similarly, the host timestamp is + * computed with + * + * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) + * + * SOF values are coded on 11 bits by USB. We extend their precision with 16 + * decimal bits, leading to a 11.16 coding. + * + * TODO: To avoid surprises with device clock values, PTS/STC timestamps should + * be normalized using the nominal device clock frequency reported through the + * UVC descriptors. + * + * Both the PTS/STC and SOF counters roll over, after a fixed but device + * specific amount of time for PTS/STC and after 2048ms for SOF. As long as the + * sliding window size is smaller than the rollover period, differences computed + * on unsigned integers will produce the correct result. However, the p term in + * the linear relations will be miscomputed. + * + * To fix the issue, we subtract a constant from the PTS and STC values to bring + * PTS to half the 32 bit STC range. The sliding window STC values then fit into + * the 32 bit range without any rollover. + * + * Similarly, we add 2048 to the device SOF values to make sure that the SOF + * computed by (1) will never be smaller than 0. This offset is then compensated + * by adding 2048 to the SOF values used in (2). However, this doesn't prevent + * rollovers between (1) and (2): the SOF value computed by (1) can be slightly + * lower than 4096, and the host SOF counters can have rolled over to 2048. This + * case is handled by subtracting 2048 from the SOF value if it exceeds the host + * SOF value at the end of the sliding window. + * + * Finally we subtract a constant from the host timestamps to bring the first + * timestamp of the sliding window to 1s. + */ +void uvc_video_clock_update(struct uvc_streaming *stream, + struct v4l2_buffer *v4l2_buf, + struct uvc_buffer *buf) +{ + struct uvc_clock *clock = &stream->clock; + struct uvc_clock_sample *first; + struct uvc_clock_sample *last; + unsigned long flags; + struct timespec ts; + u32 delta_stc; + u32 y1, y2; + u32 x1, x2; + u32 mean; + u32 sof; + u32 div; + u32 rem; + u64 y; + + spin_lock_irqsave(&clock->lock, flags); + + if (clock->count < clock->size) + goto done; + + first = &clock->samples[clock->head]; + last = &clock->samples[(clock->head - 1) % clock->size]; + + /* First step, PTS to SOF conversion. */ + delta_stc = buf->pts - (1UL << 31); + x1 = first->dev_stc - delta_stc; + x2 = last->dev_stc - delta_stc; + if (x1 == x2) + goto done; + + y1 = (first->dev_sof + 2048) << 16; + y2 = (last->dev_sof + 2048) << 16; + if (y2 < y1) + y2 += 2048 << 16; + + y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 + - (u64)y2 * (u64)x1; + y = div_u64(y, x2 - x1); + + sof = y; + + uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu " + "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", + stream->dev->name, buf->pts, + y >> 16, div_u64((y & 0xffff) * 1000000, 65536), + sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), + x1, x2, y1, y2, clock->sof_offset); + + /* Second step, SOF to host clock conversion. */ + x1 = (uvc_video_clock_host_sof(first) + 2048) << 16; + x2 = (uvc_video_clock_host_sof(last) + 2048) << 16; + if (x2 < x1) + x2 += 2048 << 16; + if (x1 == x2) + goto done; + + ts = timespec_sub(last->host_ts, first->host_ts); + y1 = NSEC_PER_SEC; + y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec; + + /* Interpolated and host SOF timestamps can wrap around at slightly + * different times. Handle this by adding or removing 2048 to or from + * the computed SOF value to keep it close to the SOF samples mean + * value. + */ + mean = (x1 + x2) / 2; + if (mean - (1024 << 16) > sof) + sof += 2048 << 16; + else if (sof > mean + (1024 << 16)) + sof -= 2048 << 16; + + y = (u64)(y2 - y1) * (u64)sof + (u64)y1 * (u64)x2 + - (u64)y2 * (u64)x1; + y = div_u64(y, x2 - x1); + + div = div_u64_rem(y, NSEC_PER_SEC, &rem); + ts.tv_sec = first->host_ts.tv_sec - 1 + div; + ts.tv_nsec = first->host_ts.tv_nsec + rem; + if (ts.tv_nsec >= NSEC_PER_SEC) { + ts.tv_sec++; + ts.tv_nsec -= NSEC_PER_SEC; + } + + uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu " + "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", + stream->dev->name, + sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), + y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC, + v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec, + x1, first->host_sof, first->dev_sof, + x2, last->host_sof, last->dev_sof, y1, y2); + + /* Update the V4L2 buffer. */ + v4l2_buf->timestamp.tv_sec = ts.tv_sec; + v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + +done: + spin_unlock_irqrestore(&stream->clock.lock, flags); +} + +/* ------------------------------------------------------------------------ + * Stream statistics + */ + +static void uvc_video_stats_decode(struct uvc_streaming *stream, + const __u8 *data, int len) +{ + unsigned int header_size; + bool has_pts = false; + bool has_scr = false; + u16 uninitialized_var(scr_sof); + u32 uninitialized_var(scr_stc); + u32 uninitialized_var(pts); + + if (stream->stats.stream.nb_frames == 0 && + stream->stats.frame.nb_packets == 0) + ktime_get_ts(&stream->stats.stream.start_ts); + + switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { + case UVC_STREAM_PTS | UVC_STREAM_SCR: + header_size = 12; + has_pts = true; + has_scr = true; + break; + case UVC_STREAM_PTS: + header_size = 6; + has_pts = true; + break; + case UVC_STREAM_SCR: + header_size = 8; + has_scr = true; + break; + default: + header_size = 2; + break; + } + + /* Check for invalid headers. */ + if (len < header_size || data[0] < header_size) { + stream->stats.frame.nb_invalid++; + return; + } + + /* Extract the timestamps. */ + if (has_pts) + pts = get_unaligned_le32(&data[2]); + + if (has_scr) { + scr_stc = get_unaligned_le32(&data[header_size - 6]); + scr_sof = get_unaligned_le16(&data[header_size - 2]); + } + + /* Is PTS constant through the whole frame ? */ + if (has_pts && stream->stats.frame.nb_pts) { + if (stream->stats.frame.pts != pts) { + stream->stats.frame.nb_pts_diffs++; + stream->stats.frame.last_pts_diff = + stream->stats.frame.nb_packets; + } + } + + if (has_pts) { + stream->stats.frame.nb_pts++; + stream->stats.frame.pts = pts; + } + + /* Do all frames have a PTS in their first non-empty packet, or before + * their first empty packet ? + */ + if (stream->stats.frame.size == 0) { + if (len > header_size) + stream->stats.frame.has_initial_pts = has_pts; + if (len == header_size && has_pts) + stream->stats.frame.has_early_pts = true; + } + + /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */ + if (has_scr && stream->stats.frame.nb_scr) { + if (stream->stats.frame.scr_stc != scr_stc) + stream->stats.frame.nb_scr_diffs++; + } + + if (has_scr) { + /* Expand the SOF counter to 32 bits and store its value. */ + if (stream->stats.stream.nb_frames > 0 || + stream->stats.frame.nb_scr > 0) + stream->stats.stream.scr_sof_count += + (scr_sof - stream->stats.stream.scr_sof) % 2048; + stream->stats.stream.scr_sof = scr_sof; + + stream->stats.frame.nb_scr++; + stream->stats.frame.scr_stc = scr_stc; + stream->stats.frame.scr_sof = scr_sof; + + if (scr_sof < stream->stats.stream.min_sof) + stream->stats.stream.min_sof = scr_sof; + if (scr_sof > stream->stats.stream.max_sof) + stream->stats.stream.max_sof = scr_sof; + } + + /* Record the first non-empty packet number. */ + if (stream->stats.frame.size == 0 && len > header_size) + stream->stats.frame.first_data = stream->stats.frame.nb_packets; + + /* Update the frame size. */ + stream->stats.frame.size += len - header_size; + + /* Update the packets counters. */ + stream->stats.frame.nb_packets++; + if (len > header_size) + stream->stats.frame.nb_empty++; + + if (data[1] & UVC_STREAM_ERR) + stream->stats.frame.nb_errors++; +} + +static void uvc_video_stats_update(struct uvc_streaming *stream) +{ + struct uvc_stats_frame *frame = &stream->stats.frame; + + uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, " + "%u/%u/%u pts (%searly %sinitial), %u/%u scr, " + "last pts/stc/sof %u/%u/%u\n", + stream->sequence, frame->first_data, + frame->nb_packets - frame->nb_empty, frame->nb_packets, + frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts, + frame->has_early_pts ? "" : "!", + frame->has_initial_pts ? "" : "!", + frame->nb_scr_diffs, frame->nb_scr, + frame->pts, frame->scr_stc, frame->scr_sof); + + stream->stats.stream.nb_frames++; + stream->stats.stream.nb_packets += stream->stats.frame.nb_packets; + stream->stats.stream.nb_empty += stream->stats.frame.nb_empty; + stream->stats.stream.nb_errors += stream->stats.frame.nb_errors; + stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid; + + if (frame->has_early_pts) + stream->stats.stream.nb_pts_early++; + if (frame->has_initial_pts) + stream->stats.stream.nb_pts_initial++; + if (frame->last_pts_diff <= frame->first_data) + stream->stats.stream.nb_pts_constant++; + if (frame->nb_scr >= frame->nb_packets - frame->nb_empty) + stream->stats.stream.nb_scr_count_ok++; + if (frame->nb_scr_diffs + 1 == frame->nb_scr) + stream->stats.stream.nb_scr_diffs_ok++; + + memset(&stream->stats.frame, 0, sizeof(stream->stats.frame)); +} + +size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, + size_t size) +{ + unsigned int scr_sof_freq; + unsigned int duration; + struct timespec ts; + size_t count = 0; + + ts.tv_sec = stream->stats.stream.stop_ts.tv_sec + - stream->stats.stream.start_ts.tv_sec; + ts.tv_nsec = stream->stats.stream.stop_ts.tv_nsec + - stream->stats.stream.start_ts.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_sec--; + ts.tv_nsec += 1000000000; + } + + /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF + * frequency this will not overflow before more than 1h. + */ + duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + if (duration != 0) + scr_sof_freq = stream->stats.stream.scr_sof_count * 1000 + / duration; + else + scr_sof_freq = 0; + + count += scnprintf(buf + count, size - count, + "frames: %u\npackets: %u\nempty: %u\n" + "errors: %u\ninvalid: %u\n", + stream->stats.stream.nb_frames, + stream->stats.stream.nb_packets, + stream->stats.stream.nb_empty, + stream->stats.stream.nb_errors, + stream->stats.stream.nb_invalid); + count += scnprintf(buf + count, size - count, + "pts: %u early, %u initial, %u ok\n", + stream->stats.stream.nb_pts_early, + stream->stats.stream.nb_pts_initial, + stream->stats.stream.nb_pts_constant); + count += scnprintf(buf + count, size - count, + "scr: %u count ok, %u diff ok\n", + stream->stats.stream.nb_scr_count_ok, + stream->stats.stream.nb_scr_diffs_ok); + count += scnprintf(buf + count, size - count, + "sof: %u <= sof <= %u, freq %u.%03u kHz\n", + stream->stats.stream.min_sof, + stream->stats.stream.max_sof, + scr_sof_freq / 1000, scr_sof_freq % 1000); + + return count; +} + +static void uvc_video_stats_start(struct uvc_streaming *stream) +{ + memset(&stream->stats, 0, sizeof(stream->stats)); + stream->stats.stream.min_sof = 2048; +} + +static void uvc_video_stats_stop(struct uvc_streaming *stream) +{ + ktime_get_ts(&stream->stats.stream.stop_ts); +} + +/* ------------------------------------------------------------------------ + * Video codecs + */ + +/* Video payload decoding is handled by uvc_video_decode_start(), + * uvc_video_decode_data() and uvc_video_decode_end(). + * + * uvc_video_decode_start is called with URB data at the start of a bulk or + * isochronous payload. It processes header data and returns the header size + * in bytes if successful. If an error occurs, it returns a negative error + * code. The following error codes have special meanings. + * + * - EAGAIN informs the caller that the current video buffer should be marked + * as done, and that the function should be called again with the same data + * and a new video buffer. This is used when end of frame conditions can be + * reliably detected at the beginning of the next frame only. + * + * If an error other than -EAGAIN is returned, the caller will drop the current + * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be + * made until the next payload. -ENODATA can be used to drop the current + * payload if no other error code is appropriate. + * + * uvc_video_decode_data is called for every URB with URB data. It copies the + * data to the video buffer. + * + * uvc_video_decode_end is called with header data at the end of a bulk or + * isochronous payload. It performs any additional header data processing and + * returns 0 or a negative error code if an error occurred. As header data have + * already been processed by uvc_video_decode_start, this functions isn't + * required to perform sanity checks a second time. + * + * For isochronous transfers where a payload is always transferred in a single + * URB, the three functions will be called in a row. + * + * To let the decoder process header data and update its internal state even + * when no video buffer is available, uvc_video_decode_start must be prepared + * to be called with a NULL buf parameter. uvc_video_decode_data and + * uvc_video_decode_end will never be called with a NULL buffer. + */ +static int uvc_video_decode_start(struct uvc_streaming *stream, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + __u8 fid; + + /* Sanity checks: + * - packet must be at least 2 bytes long + * - bHeaderLength value must be at least 2 bytes (see above) + * - bHeaderLength value can't be larger than the packet size. + */ + if (len < 2 || data[0] < 2 || data[0] > len) { + stream->stats.frame.nb_invalid++; + return -EINVAL; + } + + fid = data[1] & UVC_STREAM_FID; + + /* Increase the sequence number regardless of any buffer states, so + * that discontinuous sequence numbers always indicate lost frames. + */ + if (stream->last_fid != fid) { + stream->sequence++; + if (stream->sequence) + uvc_video_stats_update(stream); + } + + uvc_video_clock_decode(stream, buf, data, len); + uvc_video_stats_decode(stream, data, len); + + /* Store the payload FID bit and return immediately when the buffer is + * NULL. + */ + if (buf == NULL) { + stream->last_fid = fid; + return -ENODATA; + } + + /* Mark the buffer as bad if the error bit is set. */ + if (data[1] & UVC_STREAM_ERR) { + uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit " + "set).\n"); + buf->error = 1; + } + + /* Synchronize to the input stream by waiting for the FID bit to be + * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. + * stream->last_fid is initialized to -1, so the first isochronous + * frame will always be in sync. + * + * If the device doesn't toggle the FID bit, invert stream->last_fid + * when the EOF bit is set to force synchronisation on the next packet. + */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + struct timespec ts; + + if (fid == stream->last_fid) { + uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " + "sync).\n"); + if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && + (data[1] & UVC_STREAM_EOF)) + stream->last_fid ^= UVC_STREAM_FID; + return -ENODATA; + } + + if (uvc_clock_param == CLOCK_MONOTONIC) + ktime_get_ts(&ts); + else + ktime_get_real_ts(&ts); + + buf->buf.v4l2_buf.sequence = stream->sequence; + buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec; + buf->buf.v4l2_buf.timestamp.tv_usec = + ts.tv_nsec / NSEC_PER_USEC; + + /* TODO: Handle PTS and SCR. */ + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * stream->last_fid is initialized to -1, so the first isochronous + * frame will never trigger an end of frame detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting end of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != stream->last_fid && buf->bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " + "toggled).\n"); + buf->state = UVC_BUF_STATE_READY; + return -EAGAIN; + } + + stream->last_fid = fid; + + return data[0]; +} + +static void uvc_video_decode_data(struct uvc_streaming *stream, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + unsigned int maxlen, nbytes; + void *mem; + + if (len <= 0) + return; + + /* Copy the video data to the buffer. */ + maxlen = buf->length - buf->bytesused; + mem = buf->mem + buf->bytesused; + nbytes = min((unsigned int)len, maxlen); + memcpy(mem, data, nbytes); + buf->bytesused += nbytes; + + /* Complete the current frame if the buffer size was exceeded. */ + if (len > maxlen) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n"); + buf->state = UVC_BUF_STATE_READY; + } +} + +static void uvc_video_decode_end(struct uvc_streaming *stream, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + /* Mark the buffer as done if the EOF marker is set. */ + if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); + if (data[0] == len) + uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); + buf->state = UVC_BUF_STATE_READY; + if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) + stream->last_fid ^= UVC_STREAM_FID; + } +} + +/* Video payload encoding is handled by uvc_video_encode_header() and + * uvc_video_encode_data(). Only bulk transfers are currently supported. + * + * uvc_video_encode_header is called at the start of a payload. It adds header + * data to the transfer buffer and returns the header size. As the only known + * UVC output device transfers a whole frame in a single payload, the EOF bit + * is always set in the header. + * + * uvc_video_encode_data is called for every URB and copies the data from the + * video buffer to the transfer buffer. + */ +static int uvc_video_encode_header(struct uvc_streaming *stream, + struct uvc_buffer *buf, __u8 *data, int len) +{ + data[0] = 2; /* Header length */ + data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF + | (stream->last_fid & UVC_STREAM_FID); + return 2; +} + +static int uvc_video_encode_data(struct uvc_streaming *stream, + struct uvc_buffer *buf, __u8 *data, int len) +{ + struct uvc_video_queue *queue = &stream->queue; + unsigned int nbytes; + void *mem; + + /* Copy video data to the URB buffer. */ + mem = buf->mem + queue->buf_used; + nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); + nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size, + nbytes); + memcpy(data, mem, nbytes); + + queue->buf_used += nbytes; + + return nbytes; +} + +/* ------------------------------------------------------------------------ + * URB handling + */ + +/* + * Completion handler for video URBs. + */ +static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) +{ + u8 *mem; + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", urb->iso_frame_desc[i].status); + /* Mark the buffer as faulty. */ + if (buf != NULL) + buf->error = 1; + continue; + } + + /* Decode the payload header. */ + mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + do { + ret = uvc_video_decode_start(stream, buf, mem, + urb->iso_frame_desc[i].actual_length); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&stream->queue, + buf); + } while (ret == -EAGAIN); + + if (ret < 0) + continue; + + /* Decode the payload data. */ + uvc_video_decode_data(stream, buf, mem + ret, + urb->iso_frame_desc[i].actual_length - ret); + + /* Process the header again. */ + uvc_video_decode_end(stream, buf, mem, + urb->iso_frame_desc[i].actual_length); + + if (buf->state == UVC_BUF_STATE_READY) { + if (buf->length != buf->bytesused && + !(stream->cur_format->flags & + UVC_FMT_FLAG_COMPRESSED)) + buf->error = 1; + + buf = uvc_queue_next_buffer(&stream->queue, buf); + } + } +} + +static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) +{ + u8 *mem; + int len, ret; + + /* + * Ignore ZLPs if they're not part of a frame, otherwise process them + * to trigger the end of payload detection. + */ + if (urb->actual_length == 0 && stream->bulk.header_size == 0) + return; + + mem = urb->transfer_buffer; + len = urb->actual_length; + stream->bulk.payload_size += len; + + /* If the URB is the first of its payload, decode and save the + * header. + */ + if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { + do { + ret = uvc_video_decode_start(stream, buf, mem, len); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&stream->queue, + buf); + } while (ret == -EAGAIN); + + /* If an error occurred skip the rest of the payload. */ + if (ret < 0 || buf == NULL) { + stream->bulk.skip_payload = 1; + } else { + memcpy(stream->bulk.header, mem, ret); + stream->bulk.header_size = ret; + + mem += ret; + len -= ret; + } + } + + /* The buffer queue might have been cancelled while a bulk transfer + * was in progress, so we can reach here with buf equal to NULL. Make + * sure buf is never dereferenced if NULL. + */ + + /* Process video data. */ + if (!stream->bulk.skip_payload && buf != NULL) + uvc_video_decode_data(stream, buf, mem, len); + + /* Detect the payload end by a URB smaller than the maximum size (or + * a payload size equal to the maximum) and process the header again. + */ + if (urb->actual_length < urb->transfer_buffer_length || + stream->bulk.payload_size >= stream->bulk.max_payload_size) { + if (!stream->bulk.skip_payload && buf != NULL) { + uvc_video_decode_end(stream, buf, stream->bulk.header, + stream->bulk.payload_size); + if (buf->state == UVC_BUF_STATE_READY) + buf = uvc_queue_next_buffer(&stream->queue, + buf); + } + + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; + } +} + +static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) +{ + u8 *mem = urb->transfer_buffer; + int len = stream->urb_size, ret; + + if (buf == NULL) { + urb->transfer_buffer_length = 0; + return; + } + + /* If the URB is the first of its payload, add the header. */ + if (stream->bulk.header_size == 0) { + ret = uvc_video_encode_header(stream, buf, mem, len); + stream->bulk.header_size = ret; + stream->bulk.payload_size += ret; + mem += ret; + len -= ret; + } + + /* Process video data. */ + ret = uvc_video_encode_data(stream, buf, mem, len); + + stream->bulk.payload_size += ret; + len -= ret; + + if (buf->bytesused == stream->queue.buf_used || + stream->bulk.payload_size == stream->bulk.max_payload_size) { + if (buf->bytesused == stream->queue.buf_used) { + stream->queue.buf_used = 0; + buf->state = UVC_BUF_STATE_READY; + buf->buf.v4l2_buf.sequence = ++stream->sequence; + uvc_queue_next_buffer(&stream->queue, buf); + stream->last_fid ^= UVC_STREAM_FID; + } + + stream->bulk.header_size = 0; + stream->bulk.payload_size = 0; + } + + urb->transfer_buffer_length = stream->urb_size - len; +} + +static void uvc_video_complete(struct urb *urb) +{ + struct uvc_streaming *stream = urb->context; + struct uvc_video_queue *queue = &stream->queue; + struct uvc_buffer *buf = NULL; + unsigned long flags; + int ret; + + switch (urb->status) { + case 0: + break; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " + "completion handler.\n", urb->status); + + case -ENOENT: /* usb_kill_urb() called. */ + if (stream->frozen) + return; + + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); + return; + } + + spin_lock_irqsave(&queue->irqlock, flags); + if (!list_empty(&queue->irqqueue)) + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + spin_unlock_irqrestore(&queue->irqlock, flags); + + stream->decode(urb, stream, buf); + + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", + ret); + } +} + +/* + * Free transfer buffers. + */ +static void uvc_free_urb_buffers(struct uvc_streaming *stream) +{ + unsigned int i; + + for (i = 0; i < UVC_URBS; ++i) { + if (stream->urb_buffer[i]) { +#ifndef CONFIG_DMA_NONCOHERENT + usb_free_coherent(stream->dev->udev, stream->urb_size, + stream->urb_buffer[i], stream->urb_dma[i]); +#else + kfree(stream->urb_buffer[i]); +#endif + stream->urb_buffer[i] = NULL; + } + } + + stream->urb_size = 0; +} + +/* + * Allocate transfer buffers. This function can be called with buffers + * already allocated when resuming from suspend, in which case it will + * return without touching the buffers. + * + * Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the + * system is too low on memory try successively smaller numbers of packets + * until allocation succeeds. + * + * Return the number of allocated packets on success or 0 when out of memory. + */ +static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, + unsigned int size, unsigned int psize, gfp_t gfp_flags) +{ + unsigned int npackets; + unsigned int i; + + /* Buffers are already allocated, bail out. */ + if (stream->urb_size) + return stream->urb_size / psize; + + /* Compute the number of packets. Bulk endpoints might transfer UVC + * payloads across multiple URBs. + */ + npackets = DIV_ROUND_UP(size, psize); + if (npackets > UVC_MAX_PACKETS) + npackets = UVC_MAX_PACKETS; + + /* Retry allocations until one succeed. */ + for (; npackets > 1; npackets /= 2) { + for (i = 0; i < UVC_URBS; ++i) { + stream->urb_size = psize * npackets; +#ifndef CONFIG_DMA_NONCOHERENT + stream->urb_buffer[i] = usb_alloc_coherent( + stream->dev->udev, stream->urb_size, + gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); +#else + stream->urb_buffer[i] = + kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN); +#endif + if (!stream->urb_buffer[i]) { + uvc_free_urb_buffers(stream); + break; + } + } + + if (i == UVC_URBS) { + uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " + "of %ux%u bytes each.\n", UVC_URBS, npackets, + psize); + return npackets; + } + } + + uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes " + "per packet).\n", psize); + return 0; +} + +/* + * Uninitialize isochronous/bulk URBs and free transfer buffers. + */ +static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) +{ + struct urb *urb; + unsigned int i; + + uvc_video_stats_stop(stream); + + for (i = 0; i < UVC_URBS; ++i) { + urb = stream->urb[i]; + if (urb == NULL) + continue; + + usb_kill_urb(urb); + usb_free_urb(urb); + stream->urb[i] = NULL; + } + + if (free_buffers) + uvc_free_urb_buffers(stream); +} + +/* + * Compute the maximum number of bytes per interval for an endpoint. + */ +static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev, + struct usb_host_endpoint *ep) +{ + u16 psize; + + switch (dev->speed) { + case USB_SPEED_SUPER: + return ep->ss_ep_comp.wBytesPerInterval; + case USB_SPEED_HIGH: + psize = usb_endpoint_maxp(&ep->desc); + return (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + default: + psize = usb_endpoint_maxp(&ep->desc); + return psize & 0x07ff; + } +} + +/* + * Initialize isochronous URBs and allocate transfer buffers. The packet size + * is given by the endpoint. + */ +static int uvc_init_video_isoc(struct uvc_streaming *stream, + struct usb_host_endpoint *ep, gfp_t gfp_flags) +{ + struct urb *urb; + unsigned int npackets, i, j; + u16 psize; + u32 size; + + psize = uvc_endpoint_max_bpi(stream->dev->udev, ep); + size = stream->ctrl.dwMaxVideoFrameSize; + + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); + if (npackets == 0) + return -ENOMEM; + + size = npackets * psize; + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(npackets, gfp_flags); + if (urb == NULL) { + uvc_uninit_video(stream, 1); + return -ENOMEM; + } + + urb->dev = stream->dev->udev; + urb->context = stream; + urb->pipe = usb_rcvisocpipe(stream->dev->udev, + ep->desc.bEndpointAddress); +#ifndef CONFIG_DMA_NONCOHERENT + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_dma = stream->urb_dma[i]; +#else + urb->transfer_flags = URB_ISO_ASAP; +#endif + urb->interval = ep->desc.bInterval; + urb->transfer_buffer = stream->urb_buffer[i]; + urb->complete = uvc_video_complete; + urb->number_of_packets = npackets; + urb->transfer_buffer_length = size; + + for (j = 0; j < npackets; ++j) { + urb->iso_frame_desc[j].offset = j * psize; + urb->iso_frame_desc[j].length = psize; + } + + stream->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize bulk URBs and allocate transfer buffers. The packet size is + * given by the endpoint. + */ +static int uvc_init_video_bulk(struct uvc_streaming *stream, + struct usb_host_endpoint *ep, gfp_t gfp_flags) +{ + struct urb *urb; + unsigned int npackets, pipe, i; + u16 psize; + u32 size; + + psize = usb_endpoint_maxp(&ep->desc) & 0x7ff; + size = stream->ctrl.dwMaxPayloadTransferSize; + stream->bulk.max_payload_size = size; + + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); + if (npackets == 0) + return -ENOMEM; + + size = npackets * psize; + + if (usb_endpoint_dir_in(&ep->desc)) + pipe = usb_rcvbulkpipe(stream->dev->udev, + ep->desc.bEndpointAddress); + else + pipe = usb_sndbulkpipe(stream->dev->udev, + ep->desc.bEndpointAddress); + + if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + size = 0; + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(0, gfp_flags); + if (urb == NULL) { + uvc_uninit_video(stream, 1); + return -ENOMEM; + } + + usb_fill_bulk_urb(urb, stream->dev->udev, pipe, + stream->urb_buffer[i], size, uvc_video_complete, + stream); +#ifndef CONFIG_DMA_NONCOHERENT + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_dma = stream->urb_dma[i]; +#endif + + stream->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize isochronous/bulk URBs and allocate transfer buffers. + */ +static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) +{ + struct usb_interface *intf = stream->intf; + struct usb_host_endpoint *ep; + unsigned int i; + int ret; + + stream->sequence = -1; + stream->last_fid = -1; + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; + + uvc_video_stats_start(stream); + + if (intf->num_altsetting > 1) { + struct usb_host_endpoint *best_ep = NULL; + unsigned int best_psize = UINT_MAX; + unsigned int bandwidth; + unsigned int uninitialized_var(altsetting); + int intfnum = stream->intfnum; + + /* Isochronous endpoint, select the alternate setting. */ + bandwidth = stream->ctrl.dwMaxPayloadTransferSize; + + if (bandwidth == 0) { + uvc_trace(UVC_TRACE_VIDEO, "Device requested null " + "bandwidth, defaulting to lowest.\n"); + bandwidth = 1; + } else { + uvc_trace(UVC_TRACE_VIDEO, "Device requested %u " + "B/frame bandwidth.\n", bandwidth); + } + + for (i = 0; i < intf->num_altsetting; ++i) { + struct usb_host_interface *alts; + unsigned int psize; + + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + stream->header.bEndpointAddress); + if (ep == NULL) + continue; + + /* Check if the bandwidth is high enough. */ + psize = uvc_endpoint_max_bpi(stream->dev->udev, ep); + if (psize >= bandwidth && psize <= best_psize) { + altsetting = alts->desc.bAlternateSetting; + best_psize = psize; + best_ep = ep; + } + } + + if (best_ep == NULL) { + uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " + "for requested bandwidth.\n"); + return -EIO; + } + + uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u " + "(%u B/frame bandwidth).\n", altsetting, best_psize); + + ret = usb_set_interface(stream->dev->udev, intfnum, altsetting); + if (ret < 0) + return ret; + + ret = uvc_init_video_isoc(stream, best_ep, gfp_flags); + } else { + /* Bulk endpoint, proceed to URB initialization. */ + ep = uvc_find_endpoint(&intf->altsetting[0], + stream->header.bEndpointAddress); + if (ep == NULL) + return -EIO; + + ret = uvc_init_video_bulk(stream, ep, gfp_flags); + } + + if (ret < 0) + return ret; + + /* Submit the URBs. */ + for (i = 0; i < UVC_URBS; ++i) { + ret = usb_submit_urb(stream->urb[i], gfp_flags); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to submit URB %u " + "(%d).\n", i, ret); + uvc_uninit_video(stream, 1); + return ret; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Stop streaming without disabling the video queue. + * + * To let userspace applications resume without trouble, we must not touch the + * video buffers in any way. We mark the device as frozen to make sure the URB + * completion handler won't try to cancel the queue when we kill the URBs. + */ +int uvc_video_suspend(struct uvc_streaming *stream) +{ + if (!uvc_queue_streaming(&stream->queue)) + return 0; + + stream->frozen = 1; + uvc_uninit_video(stream, 0); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + return 0; +} + +/* + * Reconfigure the video interface and restart streaming if it was enabled + * before suspend. + * + * If an error occurs, disable the video queue. This will wake all pending + * buffers, making sure userspace applications are notified of the problem + * instead of waiting forever. + */ +int uvc_video_resume(struct uvc_streaming *stream, int reset) +{ + int ret; + + /* If the bus has been reset on resume, set the alternate setting to 0. + * This should be the default value, but some devices crash or otherwise + * misbehave if they don't receive a SET_INTERFACE request before any + * other video control request. + */ + if (reset) + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + + stream->frozen = 0; + + uvc_video_clock_reset(stream); + + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) { + uvc_queue_enable(&stream->queue, 0); + return ret; + } + + if (!uvc_queue_streaming(&stream->queue)) + return 0; + + ret = uvc_init_video(stream, GFP_NOIO); + if (ret < 0) + uvc_queue_enable(&stream->queue, 0); + + return ret; +} + +/* ------------------------------------------------------------------------ + * Video device + */ + +/* + * Initialize the UVC video device by switching to alternate setting 0 and + * retrieve the default format. + * + * Some cameras (namely the Fuji Finepix) set the format and frame + * indexes to zero. The UVC standard doesn't clearly make this a spec + * violation, so try to silently fix the values if possible. + * + * This function is called before registering the device with V4L. + */ +int uvc_video_init(struct uvc_streaming *stream) +{ + struct uvc_streaming_control *probe = &stream->ctrl; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + unsigned int i; + int ret; + + if (stream->nformats == 0) { + uvc_printk(KERN_INFO, "No supported video formats found.\n"); + return -EINVAL; + } + + atomic_set(&stream->active, 0); + + /* Initialize the video buffers queue. */ + uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); + + /* Alternate setting 0 should be the default, yet the XBox Live Vision + * Cam (and possibly other devices) crash or otherwise misbehave if + * they don't receive a SET_INTERFACE request before any other video + * control request. + */ + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + + /* Set the streaming probe control with default streaming parameters + * retrieved from the device. Webcams that don't suport GET_DEF + * requests on the probe control will just keep their current streaming + * parameters. + */ + if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) + uvc_set_video_ctrl(stream, probe, 1); + + /* Initialize the streaming parameters with the probe control current + * value. This makes sure SET_CUR requests on the streaming commit + * control will always use values retrieved from a successful GET_CUR + * request on the probe control, as required by the UVC specification. + */ + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); + if (ret < 0) + return ret; + + /* Check if the default format descriptor exists. Use the first + * available format otherwise. + */ + for (i = stream->nformats; i > 0; --i) { + format = &stream->format[i-1]; + if (format->index == probe->bFormatIndex) + break; + } + + if (format->nframes == 0) { + uvc_printk(KERN_INFO, "No frame descriptor found for the " + "default format.\n"); + return -EINVAL; + } + + /* Zero bFrameIndex might be correct. Stream-based formats (including + * MPEG-2 TS and DV) do not support frames but have a dummy frame + * descriptor with bFrameIndex set to zero. If the default frame + * descriptor is not found, use the first available frame. + */ + for (i = format->nframes; i > 0; --i) { + frame = &format->frame[i-1]; + if (frame->bFrameIndex == probe->bFrameIndex) + break; + } + + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + + stream->cur_format = format; + stream->cur_frame = frame; + + /* Select the video decoding function */ + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + stream->decode = uvc_video_decode_isight; + else if (stream->intf->num_altsetting > 1) + stream->decode = uvc_video_decode_isoc; + else + stream->decode = uvc_video_decode_bulk; + } else { + if (stream->intf->num_altsetting == 1) + stream->decode = uvc_video_encode_bulk; + else { + uvc_printk(KERN_INFO, "Isochronous endpoints are not " + "supported for video output devices.\n"); + return -EINVAL; + } + } + + return 0; +} + +/* + * Enable or disable the video stream. + */ +int uvc_video_enable(struct uvc_streaming *stream, int enable) +{ + int ret; + + if (!enable) { + uvc_uninit_video(stream, 1); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + uvc_queue_enable(&stream->queue, 0); + uvc_video_clock_cleanup(stream); + return 0; + } + + ret = uvc_video_clock_init(stream); + if (ret < 0) + return ret; + + ret = uvc_queue_enable(&stream->queue, 1); + if (ret < 0) + goto error_queue; + + /* Commit the streaming parameters. */ + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) + goto error_commit; + + ret = uvc_init_video(stream, GFP_KERNEL); + if (ret < 0) + goto error_video; + + return 0; + +error_video: + usb_set_interface(stream->dev->udev, stream->intfnum, 0); +error_commit: + uvc_queue_enable(&stream->queue, 0); +error_queue: + uvc_video_clock_cleanup(stream); + + return ret; +} diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h new file mode 100644 index 000000000000..3764040475bb --- /dev/null +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -0,0 +1,718 @@ +#ifndef _USB_VIDEO_H_ +#define _USB_VIDEO_H_ + +#ifndef __KERNEL__ +#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead." +#endif /* __KERNEL__ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* -------------------------------------------------------------------------- + * UVC constants + */ + +#define UVC_TERM_INPUT 0x0000 +#define UVC_TERM_OUTPUT 0x8000 +#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000) + +#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) +#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) +#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) +#define UVC_ENTITY_IS_ITERM(entity) \ + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_INPUT) +#define UVC_ENTITY_IS_OTERM(entity) \ + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_OUTPUT) + + +/* ------------------------------------------------------------------------ + * GUIDs + */ +#define UVC_GUID_UVC_CAMERA \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} +#define UVC_GUID_UVC_OUTPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} +#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03} +#define UVC_GUID_UVC_PROCESSING \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01} +#define UVC_GUID_UVC_SELECTOR \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} + +#define UVC_GUID_FORMAT_MJPEG \ + { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YUY2 \ + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YUY2_ISIGHT \ + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_NV12 \ + { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YV12 \ + { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_I420 \ + { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_UYVY \ + { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y800 \ + { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y8 \ + { 'Y', '8', ' ', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y10 \ + { 'Y', '1', '0', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y12 \ + { 'Y', '1', '2', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y16 \ + { 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_BY8 \ + { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_RGBP \ + { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_M420 \ + { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + +#define UVC_GUID_FORMAT_H264 \ + { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + +/* ------------------------------------------------------------------------ + * Driver specific constants. + */ + +#define DRIVER_VERSION "1.1.1" + +/* Number of isochronous URBs. */ +#define UVC_URBS 5 +/* Maximum number of packets per URB. */ +#define UVC_MAX_PACKETS 32 +/* Maximum number of video buffers. */ +#define UVC_MAX_VIDEO_BUFFERS 32 +/* Maximum status buffer size in bytes of interrupt URB. */ +#define UVC_MAX_STATUS_SIZE 16 + +#define UVC_CTRL_CONTROL_TIMEOUT 300 +#define UVC_CTRL_STREAMING_TIMEOUT 5000 + +/* Maximum allowed number of control mappings per device */ +#define UVC_MAX_CONTROL_MAPPINGS 1024 +#define UVC_MAX_CONTROL_MENU_ENTRIES 32 + +/* Devices quirks */ +#define UVC_QUIRK_STATUS_INTERVAL 0x00000001 +#define UVC_QUIRK_PROBE_MINMAX 0x00000002 +#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004 +#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 +#define UVC_QUIRK_STREAM_NO_FID 0x00000010 +#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 +#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 +#define UVC_QUIRK_PROBE_DEF 0x00000100 +#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200 + +/* Format flags */ +#define UVC_FMT_FLAG_COMPRESSED 0x00000001 +#define UVC_FMT_FLAG_STREAM 0x00000002 + +/* ------------------------------------------------------------------------ + * Structures. + */ + +struct uvc_device; + +/* TODO: Put the most frequently accessed fields at the beginning of + * structures to maximize cache efficiency. + */ +struct uvc_control_info { + struct list_head mappings; + + __u8 entity[16]; + __u8 index; /* Bit index in bmControls */ + __u8 selector; + + __u16 size; + __u32 flags; +}; + +struct uvc_control_mapping { + struct list_head list; + struct list_head ev_subs; + + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + __u32 data_type; + + struct uvc_menu_info *menu_info; + __u32 menu_count; + + __u32 master_id; + __s32 master_manual; + __u32 slave_ids[2]; + + __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, + const __u8 *data); + void (*set) (struct uvc_control_mapping *mapping, __s32 value, + __u8 *data); +}; + +struct uvc_control { + struct uvc_entity *entity; + struct uvc_control_info info; + + __u8 index; /* Used to match the uvc_control entry with a + uvc_control_info. */ + __u8 dirty:1, + loaded:1, + modified:1, + cached:1, + initialized:1; + + __u8 *uvc_data; +}; + +struct uvc_format_desc { + char *name; + __u8 guid[16]; + __u32 fcc; +}; + +/* The term 'entity' refers to both UVC units and UVC terminals. + * + * The type field is either the terminal type (wTerminalType in the terminal + * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor). + * As the bDescriptorSubtype field is one byte long, the type value will + * always have a null MSB for units. All terminal types defined by the UVC + * specification have a non-null MSB, so it is safe to use the MSB to + * differentiate between units and terminals as long as the descriptor parsing + * code makes sure terminal types have a non-null MSB. + * + * For terminals, the type's most significant bit stores the terminal + * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should + * always be accessed with the UVC_ENTITY_* macros and never directly. + */ + +struct uvc_entity { + struct list_head list; /* Entity as part of a UVC device. */ + struct list_head chain; /* Entity as part of a video device + * chain. */ + __u8 id; + __u16 type; + char name[64]; + + /* Media controller-related fields. */ + struct video_device *vdev; + struct v4l2_subdev subdev; + unsigned int num_pads; + unsigned int num_links; + struct media_pad *pads; + + union { + struct { + __u16 wObjectiveFocalLengthMin; + __u16 wObjectiveFocalLengthMax; + __u16 wOcularFocalLength; + __u8 bControlSize; + __u8 *bmControls; + } camera; + + struct { + __u8 bControlSize; + __u8 *bmControls; + __u8 bTransportModeSize; + __u8 *bmTransportModes; + } media; + + struct { + } output; + + struct { + __u16 wMaxMultiplier; + __u8 bControlSize; + __u8 *bmControls; + __u8 bmVideoStandards; + } processing; + + struct { + } selector; + + struct { + __u8 guidExtensionCode[16]; + __u8 bNumControls; + __u8 bControlSize; + __u8 *bmControls; + __u8 *bmControlsType; + } extension; + }; + + __u8 bNrInPins; + __u8 *baSourceID; + + unsigned int ncontrols; + struct uvc_control *controls; +}; + +struct uvc_frame { + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwMaxVideoFrameBufferSize; + __u8 bFrameIntervalType; + __u32 dwDefaultFrameInterval; + __u32 *dwFrameInterval; +}; + +struct uvc_format { + __u8 type; + __u8 index; + __u8 bpp; + __u8 colorspace; + __u32 fcc; + __u32 flags; + + char name[32]; + + unsigned int nframes; + struct uvc_frame *frame; +}; + +struct uvc_streaming_header { + __u8 bNumFormats; + __u8 bEndpointAddress; + __u8 bTerminalLink; + __u8 bControlSize; + __u8 *bmaControls; + /* The following fields are used by input headers only. */ + __u8 bmInfo; + __u8 bStillCaptureMethod; + __u8 bTriggerSupport; + __u8 bTriggerUsage; +}; + +enum uvc_buffer_state { + UVC_BUF_STATE_IDLE = 0, + UVC_BUF_STATE_QUEUED = 1, + UVC_BUF_STATE_ACTIVE = 2, + UVC_BUF_STATE_READY = 3, + UVC_BUF_STATE_DONE = 4, + UVC_BUF_STATE_ERROR = 5, +}; + +struct uvc_buffer { + struct vb2_buffer buf; + struct list_head queue; + + enum uvc_buffer_state state; + unsigned int error; + + void *mem; + unsigned int length; + unsigned int bytesused; + + u32 pts; +}; + +#define UVC_QUEUE_DISCONNECTED (1 << 0) +#define UVC_QUEUE_DROP_CORRUPTED (1 << 1) + +struct uvc_video_queue { + struct vb2_queue queue; + struct mutex mutex; /* Protects queue */ + + unsigned int flags; + unsigned int buf_used; + + spinlock_t irqlock; /* Protects irqqueue */ + struct list_head irqqueue; +}; + +struct uvc_video_chain { + struct uvc_device *dev; + struct list_head list; + + struct list_head entities; /* All entities */ + struct uvc_entity *processing; /* Processing unit */ + struct uvc_entity *selector; /* Selector unit */ + + struct mutex ctrl_mutex; /* Protects ctrl.info */ +}; + +struct uvc_stats_frame { + unsigned int size; /* Number of bytes captured */ + unsigned int first_data; /* Index of the first non-empty packet */ + + unsigned int nb_packets; /* Number of packets */ + unsigned int nb_empty; /* Number of empty packets */ + unsigned int nb_invalid; /* Number of packets with an invalid header */ + unsigned int nb_errors; /* Number of packets with the error bit set */ + + unsigned int nb_pts; /* Number of packets with a PTS timestamp */ + unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */ + unsigned int last_pts_diff; /* Index of the last PTS difference */ + bool has_initial_pts; /* Whether the first non-empty packet has a PTS */ + bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */ + u32 pts; /* PTS of the last packet */ + + unsigned int nb_scr; /* Number of packets with a SCR timestamp */ + unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */ + u16 scr_sof; /* SCR.SOF of the last packet */ + u32 scr_stc; /* SCR.STC of the last packet */ +}; + +struct uvc_stats_stream { + struct timespec start_ts; /* Stream start timestamp */ + struct timespec stop_ts; /* Stream stop timestamp */ + + unsigned int nb_frames; /* Number of frames */ + + unsigned int nb_packets; /* Number of packets */ + unsigned int nb_empty; /* Number of empty packets */ + unsigned int nb_invalid; /* Number of packets with an invalid header */ + unsigned int nb_errors; /* Number of packets with the error bit set */ + + unsigned int nb_pts_constant; /* Number of frames with constant PTS */ + unsigned int nb_pts_early; /* Number of frames with early PTS */ + unsigned int nb_pts_initial; /* Number of frames with initial PTS */ + + unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */ + unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */ + unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */ + unsigned int scr_sof; /* STC.SOF of the last packet */ + unsigned int min_sof; /* Minimum STC.SOF value */ + unsigned int max_sof; /* Maximum STC.SOF value */ +}; + +struct uvc_streaming { + struct list_head list; + struct uvc_device *dev; + struct video_device *vdev; + struct uvc_video_chain *chain; + atomic_t active; + + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; + + struct uvc_streaming_header header; + enum v4l2_buf_type type; + + unsigned int nformats; + struct uvc_format *format; + + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + /* Protect access to ctrl, cur_format, cur_frame and hardware video + * probe control. + */ + struct mutex mutex; + + /* Buffers queue. */ + unsigned int frozen : 1; + struct uvc_video_queue queue; + void (*decode) (struct urb *urb, struct uvc_streaming *video, + struct uvc_buffer *buf); + + /* Context data used by the bulk completion handler. */ + struct { + __u8 header[256]; + unsigned int header_size; + int skip_payload; + __u32 payload_size; + __u32 max_payload_size; + } bulk; + + struct urb *urb[UVC_URBS]; + char *urb_buffer[UVC_URBS]; + dma_addr_t urb_dma[UVC_URBS]; + unsigned int urb_size; + + __u32 sequence; + __u8 last_fid; + + /* debugfs */ + struct dentry *debugfs_dir; + struct { + struct uvc_stats_frame frame; + struct uvc_stats_stream stream; + } stats; + + /* Timestamps support. */ + struct uvc_clock { + struct uvc_clock_sample { + u32 dev_stc; + u16 dev_sof; + struct timespec host_ts; + u16 host_sof; + } *samples; + + unsigned int head; + unsigned int count; + unsigned int size; + + u16 last_sof; + u16 sof_offset; + + spinlock_t lock; + } clock; +}; + +enum uvc_device_state { + UVC_DEV_DISCONNECTED = 1, +}; + +struct uvc_device { + struct usb_device *udev; + struct usb_interface *intf; + unsigned long warnings; + __u32 quirks; + int intfnum; + char name[32]; + + enum uvc_device_state state; + atomic_t users; + atomic_t nmappings; + + /* Video control interface */ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif + struct v4l2_device vdev; + __u16 uvc_version; + __u32 clock_frequency; + + struct list_head entities; + struct list_head chains; + + /* Video Streaming interfaces */ + struct list_head streams; + atomic_t nstreams; + + /* Status Interrupt Endpoint */ + struct usb_host_endpoint *int_ep; + struct urb *int_urb; + __u8 *status; + struct input_dev *input; + char input_phys[64]; +}; + +enum uvc_handle_state { + UVC_HANDLE_PASSIVE = 0, + UVC_HANDLE_ACTIVE = 1, +}; + +struct uvc_fh { + struct v4l2_fh vfh; + struct uvc_video_chain *chain; + struct uvc_streaming *stream; + enum uvc_handle_state state; +}; + +struct uvc_driver { + struct usb_driver driver; +}; + +/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + +#define UVC_TRACE_PROBE (1 << 0) +#define UVC_TRACE_DESCR (1 << 1) +#define UVC_TRACE_CONTROL (1 << 2) +#define UVC_TRACE_FORMAT (1 << 3) +#define UVC_TRACE_CAPTURE (1 << 4) +#define UVC_TRACE_CALLS (1 << 5) +#define UVC_TRACE_IOCTL (1 << 6) +#define UVC_TRACE_FRAME (1 << 7) +#define UVC_TRACE_SUSPEND (1 << 8) +#define UVC_TRACE_STATUS (1 << 9) +#define UVC_TRACE_VIDEO (1 << 10) +#define UVC_TRACE_STATS (1 << 11) +#define UVC_TRACE_CLOCK (1 << 12) + +#define UVC_WARN_MINMAX 0 +#define UVC_WARN_PROBE_DEF 1 +#define UVC_WARN_XU_GET_RES 2 + +extern unsigned int uvc_clock_param; +extern unsigned int uvc_no_drop_param; +extern unsigned int uvc_trace_param; +extern unsigned int uvc_timeout_param; + +#define uvc_trace(flag, msg...) \ + do { \ + if (uvc_trace_param & flag) \ + printk(KERN_DEBUG "uvcvideo: " msg); \ + } while (0) + +#define uvc_warn_once(dev, warn, msg...) \ + do { \ + if (!test_and_set_bit(warn, &dev->warnings)) \ + printk(KERN_INFO "uvcvideo: " msg); \ + } while (0) + +#define uvc_printk(level, msg...) \ + printk(level "uvcvideo: " msg) + +/* -------------------------------------------------------------------------- + * Internal functions. + */ + +/* Core driver */ +extern struct uvc_driver uvc_driver; + +extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); + +/* Video buffers queue management. */ +extern void uvc_queue_init(struct uvc_video_queue *queue, + enum v4l2_buf_type type, int drop_corrupted); +extern int uvc_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb); +extern void uvc_free_buffers(struct uvc_video_queue *queue); +extern int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking); +extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable); +extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); +extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); +extern int uvc_queue_mmap(struct uvc_video_queue *queue, + struct vm_area_struct *vma); +extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait); +#ifndef CONFIG_MMU +extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff); +#endif +extern int uvc_queue_allocated(struct uvc_video_queue *queue); +static inline int uvc_queue_streaming(struct uvc_video_queue *queue) +{ + return vb2_is_streaming(&queue->queue); +} + +/* V4L2 interface */ +extern const struct v4l2_file_operations uvc_fops; + +/* Media controller */ +extern int uvc_mc_register_entities(struct uvc_video_chain *chain); +extern void uvc_mc_cleanup_entity(struct uvc_entity *entity); + +/* Video */ +extern int uvc_video_init(struct uvc_streaming *stream); +extern int uvc_video_suspend(struct uvc_streaming *stream); +extern int uvc_video_resume(struct uvc_streaming *stream, int reset); +extern int uvc_video_enable(struct uvc_streaming *stream, int enable); +extern int uvc_probe_video(struct uvc_streaming *stream, + struct uvc_streaming_control *probe); +extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size); +void uvc_video_clock_update(struct uvc_streaming *stream, + struct v4l2_buffer *v4l2_buf, + struct uvc_buffer *buf); + +/* Status */ +extern int uvc_status_init(struct uvc_device *dev); +extern void uvc_status_cleanup(struct uvc_device *dev); +extern int uvc_status_start(struct uvc_device *dev); +extern void uvc_status_stop(struct uvc_device *dev); +extern int uvc_status_suspend(struct uvc_device *dev); +extern int uvc_status_resume(struct uvc_device *dev); + +/* Controls */ +extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; + +extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct v4l2_queryctrl *v4l2_ctrl); +extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, + struct v4l2_querymenu *query_menu); + +extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, + const struct uvc_control_mapping *mapping); +extern int uvc_ctrl_init_device(struct uvc_device *dev); +extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); +extern int uvc_ctrl_resume_device(struct uvc_device *dev); + +extern int uvc_ctrl_begin(struct uvc_video_chain *chain); +extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count); +static inline int uvc_ctrl_commit(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) +{ + return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count); +} +static inline int uvc_ctrl_rollback(struct uvc_fh *handle) +{ + return __uvc_ctrl_commit(handle, 1, NULL, 0); +} + +extern int uvc_ctrl_get(struct uvc_video_chain *chain, + struct v4l2_ext_control *xctrl); +extern int uvc_ctrl_set(struct uvc_video_chain *chain, + struct v4l2_ext_control *xctrl); + +extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, + struct uvc_xu_control_query *xqry); + +/* Utility functions */ +extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold); +extern uint32_t uvc_fraction_to_interval(uint32_t numerator, + uint32_t denominator); +extern struct usb_host_endpoint *uvc_find_endpoint( + struct usb_host_interface *alts, __u8 epaddr); + +/* Quirks support */ +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf); + +/* debugfs and statistics */ +int uvc_debugfs_init(void); +void uvc_debugfs_cleanup(void); +int uvc_debugfs_init_stream(struct uvc_streaming *stream); +void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream); + +size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, + size_t size); + +#endif diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 068e8daa6b7b..097b17ced172 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -616,16 +616,6 @@ menuconfig V4L_USB_DRIVERS if V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT - comment "Webcam devices" - -source "drivers/media/video/uvc/Kconfig" - -source "drivers/media/video/gspca/Kconfig" - -source "drivers/media/video/pwc/Kconfig" - -source "drivers/media/video/cpia2/Kconfig" - config USB_ZR364XX tristate "USB ZR364XX Camera support" depends on VIDEO_V4L2 @@ -662,40 +652,9 @@ config USB_S2255 Say Y here if you want support for the Sensoray 2255 USB device. This driver can be compiled as a module, called s2255drv. -source "drivers/media/video/sn9c102/Kconfig" endif # V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT -if V4L_USB_DRIVERS - - comment "Webcam and/or TV USB devices" - -source "drivers/media/video/em28xx/Kconfig" - -endif - -if V4L_USB_DRIVERS && MEDIA_ANALOG_TV_SUPPORT - - comment "TV USB devices" - -source "drivers/media/video/au0828/Kconfig" - -source "drivers/media/video/pvrusb2/Kconfig" - -source "drivers/media/video/hdpvr/Kconfig" - -source "drivers/media/video/tlg2300/Kconfig" - -source "drivers/media/video/cx231xx/Kconfig" - -source "drivers/media/video/tm6000/Kconfig" - -source "drivers/media/video/usbvision/Kconfig" - -source "drivers/media/video/stk1160/Kconfig" - -endif # V4L_USB_DRIVERS - # # PCI drivers configuration - No devices here are for webcams # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9dff3e2ec613..a22a2580ce30 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -99,20 +99,12 @@ obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ -obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ -obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/ -obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ -obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ -obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ -obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ -obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o -obj-$(CONFIG_VIDEO_STK1160) += stk1160/ obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o @@ -130,11 +122,6 @@ obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o -obj-$(CONFIG_USB_SN9C102) += sn9c102/ -obj-$(CONFIG_USB_PWC) += pwc/ -obj-$(CONFIG_USB_GSPCA) += gspca/ - -obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ obj-$(CONFIG_USB_S2255) += s2255drv.o @@ -179,9 +166,6 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o -obj-$(CONFIG_VIDEO_AU0828) += au0828/ - -obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig deleted file mode 100644 index 23f7fd22f0eb..000000000000 --- a/drivers/media/video/au0828/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ - -config VIDEO_AU0828 - tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 - depends on DVB_CAPTURE_DRIVERS - select I2C_ALGOBIT - select VIDEO_TVEEPROM - select VIDEOBUF_VMALLOC - select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE - select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - ---help--- - This is a video4linux driver for Auvitek's USB device. - - To compile this driver as a module, choose M here: the - module will be called au0828 diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile deleted file mode 100644 index 98cc20cc0ffb..000000000000 --- a/drivers/media/video/au0828/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o - -obj-$(CONFIG_VIDEO_AU0828) += au0828.o - -ccflags-y += -Idrivers/media/tuners -ccflags-y += -Idrivers/media/dvb-core -ccflags-y += -Idrivers/media/dvb-frontends - -ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c deleted file mode 100644 index 448361c6a13e..000000000000 --- a/drivers/media/video/au0828/au0828-cards.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Driver for the Auvitek USB bridge - * - * 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. - */ - -#include "au0828.h" -#include "au0828-cards.h" -#include "au8522.h" -#include "media/tuner.h" -#include "media/v4l2-common.h" - -void hvr950q_cs5340_audio(void *priv, int enable) -{ - /* Because the HVR-950q shares an i2s bus between the cs5340 and the - au8522, we need to hold cs5340 in reset when using the au8522 */ - struct au0828_dev *dev = priv; - if (enable == 1) - au0828_set(dev, REG_000, 0x10); - else - au0828_clear(dev, REG_000, 0x10); -} - -struct au0828_board au0828_boards[] = { - [AU0828_BOARD_UNKNOWN] = { - .name = "Unknown board", - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, - }, - [AU0828_BOARD_HAUPPAUGE_HVR850] = { - .name = "Hauppauge HVR850", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0x61, - .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, - .input = { - { - .type = AU0828_VMUX_TELEVISION, - .vmux = AU8522_COMPOSITE_CH4_SIF, - .amux = AU8522_AUDIO_SIF, - }, - { - .type = AU0828_VMUX_COMPOSITE, - .vmux = AU8522_COMPOSITE_CH1, - .amux = AU8522_AUDIO_NONE, - .audio_setup = hvr950q_cs5340_audio, - }, - { - .type = AU0828_VMUX_SVIDEO, - .vmux = AU8522_SVIDEO_CH13, - .amux = AU8522_AUDIO_NONE, - .audio_setup = hvr950q_cs5340_audio, - }, - }, - }, - [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { - .name = "Hauppauge HVR950Q", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0x61, - /* The au0828 hardware i2c implementation does not properly - support the xc5000's i2c clock stretching. So we need to - lower the clock frequency enough where the 15us clock - stretch fits inside of a normal clock cycle, or else the - au0828 fails to set the STOP bit. A 30 KHz clock puts the - clock pulse width at 18us */ - .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, - .input = { - { - .type = AU0828_VMUX_TELEVISION, - .vmux = AU8522_COMPOSITE_CH4_SIF, - .amux = AU8522_AUDIO_SIF, - }, - { - .type = AU0828_VMUX_COMPOSITE, - .vmux = AU8522_COMPOSITE_CH1, - .amux = AU8522_AUDIO_NONE, - .audio_setup = hvr950q_cs5340_audio, - }, - { - .type = AU0828_VMUX_SVIDEO, - .vmux = AU8522_SVIDEO_CH13, - .amux = AU8522_AUDIO_NONE, - .audio_setup = hvr950q_cs5340_audio, - }, - }, - }, - [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { - .name = "Hauppauge HVR950Q rev xxF8", - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, - .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, - }, - [AU0828_BOARD_DVICO_FUSIONHDTV7] = { - .name = "DViCO FusionHDTV USB", - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, - .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, - }, - [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { - .name = "Hauppauge Woodbury", - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, - .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, - }, -}; - -/* Tuner callback function for au0828 boards. Currently only needed - * for HVR1500Q, which has an xc5000 tuner. - */ -int au0828_tuner_callback(void *priv, int component, int command, int arg) -{ - struct au0828_dev *dev = priv; - - dprintk(1, "%s()\n", __func__); - - switch (dev->boardnr) { - case AU0828_BOARD_HAUPPAUGE_HVR850: - case AU0828_BOARD_HAUPPAUGE_HVR950Q: - case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: - case AU0828_BOARD_DVICO_FUSIONHDTV7: - if (command == 0) { - /* Tuner Reset Command from xc5000 */ - /* Drive the tuner into reset and out */ - au0828_clear(dev, REG_001, 2); - mdelay(10); - au0828_set(dev, REG_001, 2); - mdelay(10); - return 0; - } else { - printk(KERN_ERR - "%s(): Unknown command.\n", __func__); - return -EINVAL; - } - break; - } - - return 0; /* Should never be here */ -} - -static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) -{ - struct tveeprom tv; - - tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); - dev->board.tuner_type = tv.tuner_type; - - /* Make sure we support the board model */ - switch (tv.model) { - case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ - case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ - case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ - case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ - case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ - case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ - case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ - case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ - case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ - case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ - case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ - case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ - break; - default: - printk(KERN_WARNING "%s: warning: " - "unknown hauppauge model #%d\n", __func__, tv.model); - break; - } - - printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", - __func__, tv.model); -} - -void au0828_card_setup(struct au0828_dev *dev) -{ - static u8 eeprom[256]; - struct tuner_setup tun_setup; - struct v4l2_subdev *sd; - unsigned int mode_mask = T_ANALOG_TV; - - dprintk(1, "%s()\n", __func__); - - memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); - - if (dev->i2c_rc == 0) { - dev->i2c_client.addr = 0xa0 >> 1; - tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); - } - - switch (dev->boardnr) { - case AU0828_BOARD_HAUPPAUGE_HVR850: - case AU0828_BOARD_HAUPPAUGE_HVR950Q: - case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: - case AU0828_BOARD_HAUPPAUGE_WOODBURY: - if (dev->i2c_rc == 0) - hauppauge_eeprom(dev, eeprom+0xa0); - break; - } - - if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { - /* Load the analog demodulator driver (note this would need to - be abstracted out if we ever need to support a different - demod) */ - sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "au8522", 0x8e >> 1, NULL); - if (sd == NULL) - printk(KERN_ERR "analog subdev registration failed\n"); - } - - /* Setup tuners */ - if (dev->board.tuner_type != TUNER_ABSENT) { - /* Load the tuner module, which does the attach */ - sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", dev->board.tuner_addr, NULL); - if (sd == NULL) - printk(KERN_ERR "tuner subdev registration fail\n"); - - tun_setup.mode_mask = mode_mask; - tun_setup.type = dev->board.tuner_type; - tun_setup.addr = dev->board.tuner_addr; - tun_setup.tuner_callback = au0828_tuner_callback; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, - &tun_setup); - } -} - -/* - * The bridge has between 8 and 12 gpios. - * Regs 1 and 0 deal with output enables. - * Regs 3 and 2 deal with direction. - */ -void au0828_gpio_setup(struct au0828_dev *dev) -{ - dprintk(1, "%s()\n", __func__); - - switch (dev->boardnr) { - case AU0828_BOARD_HAUPPAUGE_HVR850: - case AU0828_BOARD_HAUPPAUGE_HVR950Q: - case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: - case AU0828_BOARD_HAUPPAUGE_WOODBURY: - /* GPIO's - * 4 - CS5340 - * 5 - AU8522 Demodulator - * 6 - eeprom W/P - * 7 - power supply - * 9 - XC5000 Tuner - */ - - /* Into reset */ - au0828_write(dev, REG_003, 0x02); - au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); - au0828_write(dev, REG_001, 0x0); - au0828_write(dev, REG_000, 0x0); - msleep(100); - - /* Out of reset (leave the cs5340 in reset until needed) */ - au0828_write(dev, REG_003, 0x02); - au0828_write(dev, REG_001, 0x02); - au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); - au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20); - - msleep(250); - break; - case AU0828_BOARD_DVICO_FUSIONHDTV7: - /* GPIO's - * 6 - ? - * 8 - AU8522 Demodulator - * 9 - XC5000 Tuner - */ - - /* Into reset */ - au0828_write(dev, REG_003, 0x02); - au0828_write(dev, REG_002, 0xa0); - au0828_write(dev, REG_001, 0x0); - au0828_write(dev, REG_000, 0x0); - msleep(100); - - /* Out of reset */ - au0828_write(dev, REG_003, 0x02); - au0828_write(dev, REG_002, 0xa0); - au0828_write(dev, REG_001, 0x02); - au0828_write(dev, REG_000, 0xa0); - msleep(250); - break; - } -} - -/* table of devices that work with this driver */ -struct usb_device_id au0828_usb_id_table[] = { - { USB_DEVICE(0x2040, 0x7200), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x7240), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, - { USB_DEVICE(0x0fe9, 0xd620), - .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, - { USB_DEVICE(0x2040, 0x7210), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x7217), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x721b), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x721e), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x721f), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x7280), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x0fd9, 0x0008), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x7201), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, - { USB_DEVICE(0x2040, 0x7211), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, - { USB_DEVICE(0x2040, 0x7281), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, - { USB_DEVICE(0x05e1, 0x0480), - .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, - { USB_DEVICE(0x2040, 0x8200), - .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, - { USB_DEVICE(0x2040, 0x7260), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { USB_DEVICE(0x2040, 0x7213), - .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, - { }, -}; - -MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); diff --git a/drivers/media/video/au0828/au0828-cards.h b/drivers/media/video/au0828/au0828-cards.h deleted file mode 100644 index 48a1882c2b6b..000000000000 --- a/drivers/media/video/au0828/au0828-cards.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Driver for the Auvitek USB bridge - * - * 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. - */ - -#define AU0828_BOARD_UNKNOWN 0 -#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1 -#define AU0828_BOARD_HAUPPAUGE_HVR850 2 -#define AU0828_BOARD_DVICO_FUSIONHDTV7 3 -#define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4 -#define AU0828_BOARD_HAUPPAUGE_WOODBURY 5 diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c deleted file mode 100644 index 745a80a798c8..000000000000 --- a/drivers/media/video/au0828/au0828-core.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Driver for the Auvitek USB bridge - * - * 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. - */ - -#include -#include -#include -#include -#include - -#include "au0828.h" - -/* - * 1 = General debug messages - * 2 = USB handling - * 4 = I2C related - * 8 = Bridge related - */ -int au0828_debug; -module_param_named(debug, au0828_debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages"); - -static unsigned int disable_usb_speed_check; -module_param(disable_usb_speed_check, int, 0444); -MODULE_PARM_DESC(disable_usb_speed_check, - "override min bandwidth requirement of 480M bps"); - -#define _AU0828_BULKPIPE 0x03 -#define _BULKPIPESIZE 0xffff - -static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, - u16 index); -static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, - u16 index, unsigned char *cp, u16 size); - -/* USB Direction */ -#define CMD_REQUEST_IN 0x00 -#define CMD_REQUEST_OUT 0x01 - -u32 au0828_readreg(struct au0828_dev *dev, u16 reg) -{ - u8 result = 0; - - recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1); - dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result); - - return result; -} - -u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) -{ - dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); - return send_control_msg(dev, CMD_REQUEST_OUT, val, reg); -} - -static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, - u16 index) -{ - int status = -ENODEV; - - if (dev->usbdev) { - - /* cp must be memory that has been allocated by kmalloc */ - status = usb_control_msg(dev->usbdev, - usb_sndctrlpipe(dev->usbdev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, - value, index, NULL, 0, 1000); - - status = min(status, 0); - - if (status < 0) { - printk(KERN_ERR "%s() Failed sending control message, error %d.\n", - __func__, status); - } - - } - - return status; -} - -static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, - u16 index, unsigned char *cp, u16 size) -{ - int status = -ENODEV; - mutex_lock(&dev->mutex); - if (dev->usbdev) { - status = usb_control_msg(dev->usbdev, - usb_rcvctrlpipe(dev->usbdev, 0), - request, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, - dev->ctrlmsg, size, 1000); - - status = min(status, 0); - - if (status < 0) { - printk(KERN_ERR "%s() Failed receiving control message, error %d.\n", - __func__, status); - } - - /* the host controller requires heap allocated memory, which - is why we didn't just pass "cp" into usb_control_msg */ - memcpy(cp, dev->ctrlmsg, size); - } - mutex_unlock(&dev->mutex); - return status; -} - -static void au0828_usb_disconnect(struct usb_interface *interface) -{ - struct au0828_dev *dev = usb_get_intfdata(interface); - - dprintk(1, "%s()\n", __func__); - - /* Digital TV */ - au0828_dvb_unregister(dev); - - if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) - au0828_analog_unregister(dev); - - /* I2C */ - au0828_i2c_unregister(dev); - - v4l2_device_unregister(&dev->v4l2_dev); - - usb_set_intfdata(interface, NULL); - - mutex_lock(&dev->mutex); - dev->usbdev = NULL; - mutex_unlock(&dev->mutex); - - kfree(dev); - -} - -static int au0828_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int ifnum, retval; - struct au0828_dev *dev; - struct usb_device *usbdev = interface_to_usbdev(interface); - - ifnum = interface->altsetting->desc.bInterfaceNumber; - - if (ifnum != 0) - return -ENODEV; - - dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__, - le16_to_cpu(usbdev->descriptor.idVendor), - le16_to_cpu(usbdev->descriptor.idProduct), - ifnum); - - /* - * Make sure we have 480 Mbps of bandwidth, otherwise things like - * video stream wouldn't likely work, since 12 Mbps is generally - * not enough even for most Digital TV streams. - */ - if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { - printk(KERN_ERR "au0828: Device initialization failed.\n"); - printk(KERN_ERR "au0828: Device must be connected to a " - "high-speed USB 2.0 port.\n"); - return -ENODEV; - } - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - printk(KERN_ERR "%s() Unable to allocate memory\n", __func__); - return -ENOMEM; - } - - mutex_init(&dev->lock); - mutex_lock(&dev->lock); - mutex_init(&dev->mutex); - mutex_init(&dev->dvb.lock); - dev->usbdev = usbdev; - dev->boardnr = id->driver_info; - - /* Create the v4l2_device */ - retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); - if (retval) { - printk(KERN_ERR "%s() v4l2_device_register failed\n", - __func__); - mutex_unlock(&dev->lock); - kfree(dev); - return -EIO; - } - - /* Power Up the bridge */ - au0828_write(dev, REG_600, 1 << 4); - - /* Bring up the GPIO's and supporting devices */ - au0828_gpio_setup(dev); - - /* I2C */ - au0828_i2c_register(dev); - - /* Setup */ - au0828_card_setup(dev); - - /* Analog TV */ - if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) - au0828_analog_register(dev, interface); - - /* Digital TV */ - au0828_dvb_register(dev); - - /* Store the pointer to the au0828_dev so it can be accessed in - au0828_usb_disconnect */ - usb_set_intfdata(interface, dev); - - printk(KERN_INFO "Registered device AU0828 [%s]\n", - dev->board.name == NULL ? "Unset" : dev->board.name); - - mutex_unlock(&dev->lock); - - return 0; -} - -static struct usb_driver au0828_usb_driver = { - .name = DRIVER_NAME, - .probe = au0828_usb_probe, - .disconnect = au0828_usb_disconnect, - .id_table = au0828_usb_id_table, -}; - -static int __init au0828_init(void) -{ - int ret; - - if (au0828_debug & 1) - printk(KERN_INFO "%s() Debugging is enabled\n", __func__); - - if (au0828_debug & 2) - printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__); - - if (au0828_debug & 4) - printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__); - - if (au0828_debug & 8) - printk(KERN_INFO "%s() Bridge Debugging is enabled\n", - __func__); - - printk(KERN_INFO "au0828 driver loaded\n"); - - ret = usb_register(&au0828_usb_driver); - if (ret) - printk(KERN_ERR "usb_register failed, error = %d\n", ret); - - return ret; -} - -static void __exit au0828_exit(void) -{ - usb_deregister(&au0828_usb_driver); -} - -module_init(au0828_init); -module_exit(au0828_exit); - -MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); -MODULE_AUTHOR("Steven Toth "); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.2"); diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c deleted file mode 100644 index b328f6550d0b..000000000000 --- a/drivers/media/video/au0828/au0828-dvb.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Driver for the Auvitek USB bridge - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "au0828.h" -#include "au8522.h" -#include "xc5000.h" -#include "mxl5007t.h" -#include "tda18271.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define _AU0828_BULKPIPE 0x83 -#define _BULKPIPESIZE 0xe522 - -static u8 hauppauge_hvr950q_led_states[] = { - 0x00, /* off */ - 0x02, /* yellow */ - 0x04, /* green */ -}; - -static struct au8522_led_config hauppauge_hvr950q_led_cfg = { - .gpio_output = 0x00e0, - .gpio_output_enable = 0x6006, - .gpio_output_disable = 0x0660, - - .gpio_leds = 0x00e2, - .led_states = hauppauge_hvr950q_led_states, - .num_led_states = sizeof(hauppauge_hvr950q_led_states), - - .vsb8_strong = 20 /* dB */ * 10, - .qam64_strong = 25 /* dB */ * 10, - .qam256_strong = 32 /* dB */ * 10, -}; - -static struct au8522_config hauppauge_hvr950q_config = { - .demod_address = 0x8e >> 1, - .status_mode = AU8522_DEMODLOCKING, - .qam_if = AU8522_IF_6MHZ, - .vsb_if = AU8522_IF_6MHZ, - .led_cfg = &hauppauge_hvr950q_led_cfg, -}; - -static struct au8522_config fusionhdtv7usb_config = { - .demod_address = 0x8e >> 1, - .status_mode = AU8522_DEMODLOCKING, - .qam_if = AU8522_IF_6MHZ, - .vsb_if = AU8522_IF_6MHZ, -}; - -static struct au8522_config hauppauge_woodbury_config = { - .demod_address = 0x8e >> 1, - .status_mode = AU8522_DEMODLOCKING, - .qam_if = AU8522_IF_4MHZ, - .vsb_if = AU8522_IF_3_25MHZ, -}; - -static struct xc5000_config hauppauge_xc5000a_config = { - .i2c_address = 0x61, - .if_khz = 6000, - .chip_id = XC5000A, -}; - -static struct xc5000_config hauppauge_xc5000c_config = { - .i2c_address = 0x61, - .if_khz = 6000, - .chip_id = XC5000C, -}; - -static struct mxl5007t_config mxl5007t_hvr950q_config = { - .xtal_freq_hz = MxL_XTAL_24_MHZ, - .if_freq_hz = MxL_IF_6_MHZ, -}; - -static struct tda18271_config hauppauge_woodbury_tunerconfig = { - .gate = TDA18271_GATE_DIGITAL, -}; - -static void au0828_restart_dvb_streaming(struct work_struct *work); - -/*-------------------------------------------------------------------*/ -static void urb_completion(struct urb *purb) -{ - struct au0828_dev *dev = purb->context; - int ptype = usb_pipetype(purb->pipe); - unsigned char *ptr; - - dprintk(2, "%s()\n", __func__); - - if (!dev) - return; - - if (dev->urb_streaming == 0) - return; - - if (ptype != PIPE_BULK) { - printk(KERN_ERR "%s() Unsupported URB type %d\n", - __func__, ptype); - return; - } - - /* See if the stream is corrupted (to work around a hardware - bug where the stream gets misaligned */ - ptr = purb->transfer_buffer; - if (purb->actual_length > 0 && ptr[0] != 0x47) { - dprintk(1, "Need to restart streaming %02x len=%d!\n", - ptr[0], purb->actual_length); - schedule_work(&dev->restart_streaming); - return; - } - - /* Feed the transport payload into the kernel demux */ - dvb_dmx_swfilter_packets(&dev->dvb.demux, - purb->transfer_buffer, purb->actual_length / 188); - - /* Clean the buffer before we requeue */ - memset(purb->transfer_buffer, 0, URB_BUFSIZE); - - /* Requeue URB */ - usb_submit_urb(purb, GFP_ATOMIC); -} - -static int stop_urb_transfer(struct au0828_dev *dev) -{ - int i; - - dprintk(2, "%s()\n", __func__); - - dev->urb_streaming = 0; - for (i = 0; i < URB_COUNT; i++) { - usb_kill_urb(dev->urbs[i]); - kfree(dev->urbs[i]->transfer_buffer); - usb_free_urb(dev->urbs[i]); - } - - return 0; -} - -static int start_urb_transfer(struct au0828_dev *dev) -{ - struct urb *purb; - int i, ret = -ENOMEM; - - dprintk(2, "%s()\n", __func__); - - if (dev->urb_streaming) { - dprintk(2, "%s: bulk xfer already running!\n", __func__); - return 0; - } - - for (i = 0; i < URB_COUNT; i++) { - - dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->urbs[i]) - goto err; - - purb = dev->urbs[i]; - - purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL); - if (!purb->transfer_buffer) { - usb_free_urb(purb); - dev->urbs[i] = NULL; - goto err; - } - - purb->status = -EINPROGRESS; - usb_fill_bulk_urb(purb, - dev->usbdev, - usb_rcvbulkpipe(dev->usbdev, - _AU0828_BULKPIPE), - purb->transfer_buffer, - URB_BUFSIZE, - urb_completion, - dev); - - } - - for (i = 0; i < URB_COUNT; i++) { - ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC); - if (ret != 0) { - stop_urb_transfer(dev); - printk(KERN_ERR "%s: failed urb submission, " - "err = %d\n", __func__, ret); - return ret; - } - } - - dev->urb_streaming = 1; - ret = 0; - -err: - return ret; -} - -static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct au0828_dev *dev = (struct au0828_dev *) demux->priv; - struct au0828_dvb *dvb = &dev->dvb; - int ret = 0; - - dprintk(1, "%s()\n", __func__); - - if (!demux->dmx.frontend) - return -EINVAL; - - if (dvb) { - mutex_lock(&dvb->lock); - if (dvb->feeding++ == 0) { - /* Start transport */ - au0828_write(dev, 0x608, 0x90); - au0828_write(dev, 0x609, 0x72); - au0828_write(dev, 0x60a, 0x71); - au0828_write(dev, 0x60b, 0x01); - ret = start_urb_transfer(dev); - } - mutex_unlock(&dvb->lock); - } - - return ret; -} - -static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct au0828_dev *dev = (struct au0828_dev *) demux->priv; - struct au0828_dvb *dvb = &dev->dvb; - int ret = 0; - - dprintk(1, "%s()\n", __func__); - - if (dvb) { - mutex_lock(&dvb->lock); - if (--dvb->feeding == 0) { - /* Stop transport */ - ret = stop_urb_transfer(dev); - au0828_write(dev, 0x60b, 0x00); - } - mutex_unlock(&dvb->lock); - } - - return ret; -} - -static void au0828_restart_dvb_streaming(struct work_struct *work) -{ - struct au0828_dev *dev = container_of(work, struct au0828_dev, - restart_streaming); - struct au0828_dvb *dvb = &dev->dvb; - int ret; - - if (dev->urb_streaming == 0) - return; - - dprintk(1, "Restarting streaming...!\n"); - - mutex_lock(&dvb->lock); - - /* Stop transport */ - ret = stop_urb_transfer(dev); - au0828_write(dev, 0x608, 0x00); - au0828_write(dev, 0x609, 0x00); - au0828_write(dev, 0x60a, 0x00); - au0828_write(dev, 0x60b, 0x00); - - /* Start transport */ - au0828_write(dev, 0x608, 0x90); - au0828_write(dev, 0x609, 0x72); - au0828_write(dev, 0x60a, 0x71); - au0828_write(dev, 0x60b, 0x01); - ret = start_urb_transfer(dev); - - mutex_unlock(&dvb->lock); -} - -static int dvb_register(struct au0828_dev *dev) -{ - struct au0828_dvb *dvb = &dev->dvb; - int result; - - dprintk(1, "%s()\n", __func__); - - INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming); - - /* register adapter */ - result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, - &dev->usbdev->dev, adapter_nr); - if (result < 0) { - printk(KERN_ERR "%s: dvb_register_adapter failed " - "(errno = %d)\n", DRIVER_NAME, result); - goto fail_adapter; - } - dvb->adapter.priv = dev; - - /* register frontend */ - result = dvb_register_frontend(&dvb->adapter, dvb->frontend); - if (result < 0) { - printk(KERN_ERR "%s: dvb_register_frontend failed " - "(errno = %d)\n", DRIVER_NAME, result); - goto fail_frontend; - } - - /* register demux stuff */ - dvb->demux.dmx.capabilities = - DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - dvb->demux.priv = dev; - dvb->demux.filternum = 256; - dvb->demux.feednum = 256; - dvb->demux.start_feed = au0828_dvb_start_feed; - dvb->demux.stop_feed = au0828_dvb_stop_feed; - result = dvb_dmx_init(&dvb->demux); - if (result < 0) { - printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", - DRIVER_NAME, result); - goto fail_dmx; - } - - dvb->dmxdev.filternum = 256; - dvb->dmxdev.demux = &dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); - if (result < 0) { - printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", - DRIVER_NAME, result); - goto fail_dmxdev; - } - - dvb->fe_hw.source = DMX_FRONTEND_0; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_ERR "%s: add_frontend failed " - "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); - goto fail_fe_hw; - } - - dvb->fe_mem.source = DMX_MEMORY_FE; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); - if (result < 0) { - printk(KERN_ERR "%s: add_frontend failed " - "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); - goto fail_fe_mem; - } - - result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", - DRIVER_NAME, result); - goto fail_fe_conn; - } - - /* register network adapter */ - dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); - return 0; - -fail_fe_conn: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); -fail_fe_mem: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); -fail_fe_hw: - dvb_dmxdev_release(&dvb->dmxdev); -fail_dmxdev: - dvb_dmx_release(&dvb->demux); -fail_dmx: - dvb_unregister_frontend(dvb->frontend); -fail_frontend: - dvb_frontend_detach(dvb->frontend); - dvb_unregister_adapter(&dvb->adapter); -fail_adapter: - return result; -} - -void au0828_dvb_unregister(struct au0828_dev *dev) -{ - struct au0828_dvb *dvb = &dev->dvb; - - dprintk(1, "%s()\n", __func__); - - if (dvb->frontend == NULL) - return; - - dvb_net_release(&dvb->net); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); - dvb_dmxdev_release(&dvb->dmxdev); - dvb_dmx_release(&dvb->demux); - dvb_unregister_frontend(dvb->frontend); - dvb_frontend_detach(dvb->frontend); - dvb_unregister_adapter(&dvb->adapter); -} - -/* All the DVB attach calls go here, this function get's modified - * for each new card. No other function in this file needs - * to change. - */ -int au0828_dvb_register(struct au0828_dev *dev) -{ - struct au0828_dvb *dvb = &dev->dvb; - int ret; - - dprintk(1, "%s()\n", __func__); - - /* init frontend */ - switch (dev->boardnr) { - case AU0828_BOARD_HAUPPAUGE_HVR850: - case AU0828_BOARD_HAUPPAUGE_HVR950Q: - dvb->frontend = dvb_attach(au8522_attach, - &hauppauge_hvr950q_config, - &dev->i2c_adap); - if (dvb->frontend != NULL) - switch (dev->board.tuner_type) { - default: - case TUNER_XC5000: - dvb_attach(xc5000_attach, dvb->frontend, - &dev->i2c_adap, - &hauppauge_xc5000a_config); - break; - case TUNER_XC5000C: - dvb_attach(xc5000_attach, dvb->frontend, - &dev->i2c_adap, - &hauppauge_xc5000c_config); - break; - } - break; - case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: - dvb->frontend = dvb_attach(au8522_attach, - &hauppauge_hvr950q_config, - &dev->i2c_adap); - if (dvb->frontend != NULL) - dvb_attach(mxl5007t_attach, dvb->frontend, - &dev->i2c_adap, 0x60, - &mxl5007t_hvr950q_config); - break; - case AU0828_BOARD_HAUPPAUGE_WOODBURY: - dvb->frontend = dvb_attach(au8522_attach, - &hauppauge_woodbury_config, - &dev->i2c_adap); - if (dvb->frontend != NULL) - dvb_attach(tda18271_attach, dvb->frontend, - 0x60, &dev->i2c_adap, - &hauppauge_woodbury_tunerconfig); - break; - case AU0828_BOARD_DVICO_FUSIONHDTV7: - dvb->frontend = dvb_attach(au8522_attach, - &fusionhdtv7usb_config, - &dev->i2c_adap); - if (dvb->frontend != NULL) { - dvb_attach(xc5000_attach, dvb->frontend, - &dev->i2c_adap, - &hauppauge_xc5000a_config); - } - break; - default: - printk(KERN_WARNING "The frontend of your DVB/ATSC card " - "isn't supported yet\n"); - break; - } - if (NULL == dvb->frontend) { - printk(KERN_ERR "%s() Frontend initialization failed\n", - __func__); - return -1; - } - /* define general-purpose callback pointer */ - dvb->frontend->callback = au0828_tuner_callback; - - /* register everything */ - ret = dvb_register(dev); - if (ret < 0) { - if (dvb->frontend->ops.release) - dvb->frontend->ops.release(dvb->frontend); - return ret; - } - - return 0; -} diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c deleted file mode 100644 index 4ded17fe1957..000000000000 --- a/drivers/media/video/au0828/au0828-i2c.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Driver for the Auvitek AU0828 USB bridge - * - * 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. - */ - -#include -#include -#include -#include -#include - -#include "au0828.h" -#include "media/tuner.h" -#include - -static int i2c_scan; -module_param(i2c_scan, int, 0444); -MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); - -#define I2C_WAIT_DELAY 25 -#define I2C_WAIT_RETRY 1000 - -static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap) -{ - struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, AU0828_I2C_STATUS_201) & - AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1; -} - -static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap) -{ - struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, AU0828_I2C_STATUS_201) & - AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1; -} - -static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap) -{ - int count; - - for (count = 0; count < I2C_WAIT_RETRY; count++) { - if (!i2c_slave_did_read_ack(i2c_adap)) - break; - udelay(I2C_WAIT_DELAY); - } - - if (I2C_WAIT_RETRY == count) - return 0; - - return 1; -} - -static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap) -{ - struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, AU0828_I2C_STATUS_201) & - AU0828_I2C_STATUS_READ_DONE ? 0 : 1; -} - -static int i2c_wait_read_done(struct i2c_adapter *i2c_adap) -{ - int count; - - for (count = 0; count < I2C_WAIT_RETRY; count++) { - if (!i2c_is_read_busy(i2c_adap)) - break; - udelay(I2C_WAIT_DELAY); - } - - if (I2C_WAIT_RETRY == count) - return 0; - - return 1; -} - -static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap) -{ - struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, AU0828_I2C_STATUS_201) & - AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0; -} - -static int i2c_wait_write_done(struct i2c_adapter *i2c_adap) -{ - int count; - - for (count = 0; count < I2C_WAIT_RETRY; count++) { - if (i2c_is_write_done(i2c_adap)) - break; - udelay(I2C_WAIT_DELAY); - } - - if (I2C_WAIT_RETRY == count) - return 0; - - return 1; -} - -static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) -{ - struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, AU0828_I2C_STATUS_201) & - AU0828_I2C_STATUS_BUSY ? 1 : 0; -} - -static int i2c_wait_done(struct i2c_adapter *i2c_adap) -{ - int count; - - for (count = 0; count < I2C_WAIT_RETRY; count++) { - if (!i2c_is_busy(i2c_adap)) - break; - udelay(I2C_WAIT_DELAY); - } - - if (I2C_WAIT_RETRY == count) - return 0; - - return 1; -} - -/* FIXME: Implement join handling correctly */ -static int i2c_sendbytes(struct i2c_adapter *i2c_adap, - const struct i2c_msg *msg, int joined_rlen) -{ - int i, strobe = 0; - struct au0828_dev *dev = i2c_adap->algo_data; - - dprintk(4, "%s()\n", __func__); - - au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); - - /* Set the I2C clock */ - if (((dev->board.tuner_type == TUNER_XC5000) || - (dev->board.tuner_type == TUNER_XC5000C)) && - (dev->board.tuner_addr == msg->addr) && - (msg->len == 64)) { - /* Hack to speed up firmware load. The xc5000 lets us do up - to 400 KHz when in firmware download mode */ - au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, - AU0828_I2C_CLK_250KHZ); - } else { - /* Use the i2c clock speed in the board configuration */ - au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, - dev->board.i2c_clk_divider); - } - - /* Hardware needs 8 bit addresses */ - au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); - - dprintk(4, "SEND: %02x\n", msg->addr); - - /* Deal with i2c_scan */ - if (msg->len == 0) { - /* The analog tuner detection code makes use of the SMBUS_QUICK - message (which involves a zero length i2c write). To avoid - checking the status register when we didn't strobe out any - actual bytes to the bus, just do a read check. This is - consistent with how I saw i2c device checking done in the - USB trace of the Windows driver */ - au0828_write(dev, AU0828_I2C_TRIGGER_200, - AU0828_I2C_TRIGGER_READ); - - if (!i2c_wait_done(i2c_adap)) - return -EIO; - - if (i2c_wait_read_ack(i2c_adap)) - return -EIO; - - return 0; - } - - for (i = 0; i < msg->len;) { - - dprintk(4, " %02x\n", msg->buf[i]); - - au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]); - - strobe++; - i++; - - if ((strobe >= 4) || (i >= msg->len)) { - - /* Strobe the byte into the bus */ - if (i < msg->len) - au0828_write(dev, AU0828_I2C_TRIGGER_200, - AU0828_I2C_TRIGGER_WRITE | - AU0828_I2C_TRIGGER_HOLD); - else - au0828_write(dev, AU0828_I2C_TRIGGER_200, - AU0828_I2C_TRIGGER_WRITE); - - /* Reset strobe trigger */ - strobe = 0; - - if (!i2c_wait_write_done(i2c_adap)) - return -EIO; - - } - - } - if (!i2c_wait_done(i2c_adap)) - return -EIO; - - dprintk(4, "\n"); - - return msg->len; -} - -/* FIXME: Implement join handling correctly */ -static int i2c_readbytes(struct i2c_adapter *i2c_adap, - const struct i2c_msg *msg, int joined) -{ - struct au0828_dev *dev = i2c_adap->algo_data; - int i; - - dprintk(4, "%s()\n", __func__); - - au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); - - /* Set the I2C clock */ - au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, - dev->board.i2c_clk_divider); - - /* Hardware needs 8 bit addresses */ - au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); - - dprintk(4, " RECV:\n"); - - /* Deal with i2c_scan */ - if (msg->len == 0) { - au0828_write(dev, AU0828_I2C_TRIGGER_200, - AU0828_I2C_TRIGGER_READ); - - if (i2c_wait_read_ack(i2c_adap)) - return -EIO; - return 0; - } - - for (i = 0; i < msg->len;) { - - i++; - - if (i < msg->len) - au0828_write(dev, AU0828_I2C_TRIGGER_200, - AU0828_I2C_TRIGGER_READ | - AU0828_I2C_TRIGGER_HOLD); - else - au0828_write(dev, AU0828_I2C_TRIGGER_200, - AU0828_I2C_TRIGGER_READ); - - if (!i2c_wait_read_done(i2c_adap)) - return -EIO; - - msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) & - 0xff; - - dprintk(4, " %02x\n", msg->buf[i-1]); - } - if (!i2c_wait_done(i2c_adap)) - return -EIO; - - dprintk(4, "\n"); - - return msg->len; -} - -static int i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg *msgs, int num) -{ - int i, retval = 0; - - dprintk(4, "%s(num = %d)\n", __func__, num); - - for (i = 0; i < num; i++) { - dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n", - __func__, num, msgs[i].addr, msgs[i].len); - if (msgs[i].flags & I2C_M_RD) { - /* read */ - retval = i2c_readbytes(i2c_adap, &msgs[i], 0); - } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && - msgs[i].addr == msgs[i + 1].addr) { - /* write then read from same address */ - retval = i2c_sendbytes(i2c_adap, &msgs[i], - msgs[i + 1].len); - if (retval < 0) - goto err; - i++; - retval = i2c_readbytes(i2c_adap, &msgs[i], 1); - } else { - /* write */ - retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); - } - if (retval < 0) - goto err; - } - return num; - -err: - return retval; -} - -static u32 au0828_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; -} - -static struct i2c_algorithm au0828_i2c_algo_template = { - .master_xfer = i2c_xfer, - .functionality = au0828_functionality, -}; - -/* ----------------------------------------------------------------------- */ - -static struct i2c_adapter au0828_i2c_adap_template = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .algo = &au0828_i2c_algo_template, -}; - -static struct i2c_client au0828_i2c_client_template = { - .name = "au0828 internal", -}; - -static char *i2c_devs[128] = { - [0x8e >> 1] = "au8522", - [0xa0 >> 1] = "eeprom", - [0xc2 >> 1] = "tuner/xc5000", -}; - -static void do_i2c_scan(char *name, struct i2c_client *c) -{ - unsigned char buf; - int i, rc; - - for (i = 0; i < 128; i++) { - c->addr = i; - rc = i2c_master_recv(c, &buf, 0); - if (rc < 0) - continue; - printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", - name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); - } -} - -/* init + register i2c adapter */ -int au0828_i2c_register(struct au0828_dev *dev) -{ - dprintk(1, "%s()\n", __func__); - - memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, - sizeof(dev->i2c_adap)); - memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, - sizeof(dev->i2c_algo)); - memcpy(&dev->i2c_client, &au0828_i2c_client_template, - sizeof(dev->i2c_client)); - - dev->i2c_adap.dev.parent = &dev->usbdev->dev; - - strlcpy(dev->i2c_adap.name, DRIVER_NAME, - sizeof(dev->i2c_adap.name)); - - dev->i2c_adap.algo = &dev->i2c_algo; - dev->i2c_adap.algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); - i2c_add_adapter(&dev->i2c_adap); - - dev->i2c_client.adapter = &dev->i2c_adap; - - if (0 == dev->i2c_rc) { - printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME); - if (i2c_scan) - do_i2c_scan(DRIVER_NAME, &dev->i2c_client); - } else - printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME); - - return dev->i2c_rc; -} - -int au0828_i2c_unregister(struct au0828_dev *dev) -{ - i2c_del_adapter(&dev->i2c_adap); - return 0; -} - diff --git a/drivers/media/video/au0828/au0828-reg.h b/drivers/media/video/au0828/au0828-reg.h deleted file mode 100644 index 2140f4cfb645..000000000000 --- a/drivers/media/video/au0828/au0828-reg.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Driver for the Auvitek USB bridge - * - * 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. - */ - -/* We'll start to rename these registers once we have a better - * understanding of their meaning. - */ -#define REG_000 0x000 -#define REG_001 0x001 -#define REG_002 0x002 -#define REG_003 0x003 - -#define AU0828_SENSORCTRL_100 0x100 -#define AU0828_SENSORCTRL_VBI_103 0x103 - -/* I2C registers */ -#define AU0828_I2C_TRIGGER_200 0x200 -#define AU0828_I2C_STATUS_201 0x201 -#define AU0828_I2C_CLK_DIVIDER_202 0x202 -#define AU0828_I2C_DEST_ADDR_203 0x203 -#define AU0828_I2C_WRITE_FIFO_205 0x205 -#define AU0828_I2C_READ_FIFO_209 0x209 -#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff - -/* Audio registers */ -#define AU0828_AUDIOCTRL_50C 0x50C - -#define REG_600 0x600 - -/*********************************************************************/ -/* Here are constants for values associated with the above registers */ - -/* I2C Trigger (Reg 0x200) */ -#define AU0828_I2C_TRIGGER_WRITE 0x01 -#define AU0828_I2C_TRIGGER_READ 0x20 -#define AU0828_I2C_TRIGGER_HOLD 0x40 - -/* I2C Status (Reg 0x201) */ -#define AU0828_I2C_STATUS_READ_DONE 0x01 -#define AU0828_I2C_STATUS_NO_READ_ACK 0x02 -#define AU0828_I2C_STATUS_WRITE_DONE 0x04 -#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08 -#define AU0828_I2C_STATUS_BUSY 0x10 - -/* I2C Clock Divider (Reg 0x202) */ -#define AU0828_I2C_CLK_250KHZ 0x07 -#define AU0828_I2C_CLK_100KHZ 0x14 -#define AU0828_I2C_CLK_30KHZ 0x40 -#define AU0828_I2C_CLK_20KHZ 0x60 diff --git a/drivers/media/video/au0828/au0828-vbi.c b/drivers/media/video/au0828/au0828-vbi.c deleted file mode 100644 index 63f593070ee8..000000000000 --- a/drivers/media/video/au0828/au0828-vbi.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - au0828-vbi.c - VBI driver for au0828 - - Copyright (C) 2010 Devin Heitmueller - - This work was sponsored by GetWellNetwork 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., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - */ - -#include -#include -#include -#include - -#include "au0828.h" - -static unsigned int vbibufs = 5; -module_param(vbibufs, int, 0644); -MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); - -/* ------------------------------------------------------------------ */ - -static void -free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) -{ - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vbi_buf == buf) - dev->isoc_ctl.vbi_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct au0828_fh *fh = q->priv_data; - struct au0828_dev *dev = fh->dev; - - *size = dev->vbi_width * dev->vbi_height * 2; - - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; - return 0; -} - -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct au0828_fh *fh = q->priv_data; - struct au0828_dev *dev = fh->dev; - struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); - int rc = 0; - - buf->vb.size = dev->vbi_width * dev->vbi_height * 2; - - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - buf->vb.width = dev->vbi_width; - buf->vb.height = dev->vbi_height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(q, buf); - return rc; -} - -static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct au0828_buffer *buf = container_of(vb, - struct au0828_buffer, - vb); - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; - struct au0828_dmaqueue *vbiq = &dev->vbiq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vbiq->active); -} - -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); - free_buffer(q, buf); -} - -struct videobuf_queue_ops au0828_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, -}; diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c deleted file mode 100644 index fa0fa9ae91c7..000000000000 --- a/drivers/media/video/au0828/au0828-video.c +++ /dev/null @@ -1,2034 +0,0 @@ -/* - * Auvitek AU0828 USB Bridge (Analog video support) - * - * Copyright (C) 2009 Devin Heitmueller - * Copyright (C) 2005-2008 Auvitek International, Ltd. - * - * This program is free software; 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. - */ - -/* Developer Notes: - * - * VBI support is not yet working - * The hardware scaler supported is unimplemented - * AC97 audio support is unimplemented (only i2s audio mode) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "au0828.h" -#include "au0828-reg.h" - -static DEFINE_MUTEX(au0828_sysfs_lock); - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static unsigned int isoc_debug; -module_param(isoc_debug, int, 0644); -MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); - -#define au0828_isocdbg(fmt, arg...) \ -do {\ - if (isoc_debug) { \ - printk(KERN_INFO "au0828 %s :"fmt, \ - __func__ , ##arg); \ - } \ - } while (0) - -static inline void print_err_status(struct au0828_dev *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - au0828_isocdbg("URB status %d [%s].\n", status, errmsg); - } else { - au0828_isocdbg("URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -static int check_dev(struct au0828_dev *dev) -{ - if (dev->dev_state & DEV_DISCONNECTED) { - printk(KERN_INFO "v4l2 ioctl: device not present\n"); - return -ENODEV; - } - - if (dev->dev_state & DEV_MISCONFIGURED) { - printk(KERN_INFO "v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); - return -EIO; - } - return 0; -} - -/* - * IRQ callback, called by URB callback - */ -static void au0828_irq_callback(struct urb *urb) -{ - struct au0828_dmaqueue *dma_q = urb->context; - struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); - unsigned long flags = 0; - int i; - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - au0828_isocdbg("au0828_irq_callback called: status kill\n"); - return; - default: /* unknown error */ - au0828_isocdbg("urb completition error %d.\n", urb->status); - break; - } - - /* Copy data from URB */ - spin_lock_irqsave(&dev->slock, flags); - dev->isoc_ctl.isoc_copy(dev, urb); - spin_unlock_irqrestore(&dev->slock, flags); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) { - au0828_isocdbg("urb resubmit failed (error=%i)\n", - urb->status); - } -} - -/* - * Stop and Deallocate URBs - */ -void au0828_uninit_isoc(struct au0828_dev *dev) -{ - struct urb *urb; - int i; - - au0828_isocdbg("au0828: called au0828_uninit_isoc\n"); - - dev->isoc_ctl.nfields = -1; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; - if (urb) { - if (!irqs_disabled()) - usb_kill_urb(urb); - else - usb_unlink_urb(urb); - - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->usbdev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } - usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; - } - dev->isoc_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - dev->isoc_ctl.num_bufs = 0; -} - -/* - * Allocate URBs and start IRQ - */ -int au0828_init_isoc(struct au0828_dev *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb)) -{ - struct au0828_dmaqueue *dma_q = &dev->vidq; - int i; - int sb_size, pipe; - struct urb *urb; - int j, k; - int rc; - - au0828_isocdbg("au0828: called au0828_prepare_isoc\n"); - - /* De-allocates all pending stuff */ - au0828_uninit_isoc(dev); - - dev->isoc_ctl.isoc_copy = isoc_copy; - dev->isoc_ctl.num_bufs = num_bufs; - - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - au0828_isocdbg("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { - au0828_isocdbg("cannot allocate memory for usb transfer\n"); - kfree(dev->isoc_ctl.urb); - return -ENOMEM; - } - - dev->isoc_ctl.max_pkt_size = max_pkt_size; - dev->isoc_ctl.buf = NULL; - - sb_size = max_packets * dev->isoc_ctl.max_pkt_size; - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) { - au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i); - au0828_uninit_isoc(dev); - return -ENOMEM; - } - dev->isoc_ctl.urb[i] = urb; - - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - printk("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - au0828_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); - - pipe = usb_rcvisocpipe(dev->usbdev, - dev->isoc_in_endpointaddr), - - usb_fill_int_urb(urb, dev->usbdev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, - au0828_irq_callback, dma_q, 1); - - urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - k = 0; - for (j = 0; j < max_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - dev->isoc_ctl.max_pkt_size; - k += dev->isoc_ctl.max_pkt_size; - } - } - - init_waitqueue_head(&dma_q->wq); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); - if (rc) { - au0828_isocdbg("submit of urb %i failed (error=%i)\n", - i, rc); - au0828_uninit_isoc(dev); - return rc; - } - } - - return 0; -} - -/* - * Announces that a buffer were filled and request the next - */ -static inline void buffer_filled(struct au0828_dev *dev, - struct au0828_dmaqueue *dma_q, - struct au0828_buffer *buf) -{ - /* Advice that buffer was filled */ - au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->isoc_ctl.buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -static inline void vbi_buffer_filled(struct au0828_dev *dev, - struct au0828_dmaqueue *dma_q, - struct au0828_buffer *buf) -{ - /* Advice that buffer was filled */ - au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->isoc_ctl.vbi_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -/* - * Identify the buffer header type and properly handles - */ -static void au0828_copy_video(struct au0828_dev *dev, - struct au0828_dmaqueue *dma_q, - struct au0828_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) -{ - void *fieldstart, *startwrite, *startread; - int linesdone, currlinedone, offset, lencopy, remain; - int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */ - - if (len == 0) - return; - - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; - - startread = p; - remain = len; - - /* Interlaces frame */ - if (buf->top_field) - fieldstart = outp; - else - fieldstart = outp + bytesperline; - - linesdone = dma_q->pos / bytesperline; - currlinedone = dma_q->pos % bytesperline; - offset = linesdone * bytesperline * 2 + currlinedone; - startwrite = fieldstart + offset; - lencopy = bytesperline - currlinedone; - lencopy = lencopy > remain ? remain : lencopy; - - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { - au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; - lencopy = remain; - } - if (lencopy <= 0) - return; - memcpy(startwrite, startread, lencopy); - - remain -= lencopy; - - while (remain > 0) { - startwrite += lencopy + bytesperline; - startread += lencopy; - if (bytesperline > remain) - lencopy = remain; - else - lencopy = bytesperline; - - if ((char *)startwrite + lencopy > (char *)outp + - buf->vb.size) { - au0828_isocdbg("Overflow %zi bytes past buf end (2)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - - (char *)startwrite; - } - if (lencopy <= 0) - break; - - memcpy(startwrite, startread, lencopy); - - remain -= lencopy; - } - - if (offset > 1440) { - /* We have enough data to check for greenscreen */ - if (outp[0] < 0x60 && outp[1440] < 0x60) - dev->greenscreen_detected = 1; - } - - dma_q->pos += len; -} - -/* - * video-buf generic routine to get the next available buffer - */ -static inline void get_next_buf(struct au0828_dmaqueue *dma_q, - struct au0828_buffer **buf) -{ - struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); - - if (list_empty(&dma_q->active)) { - au0828_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); - dev->isoc_ctl.buf = *buf; - - return; -} - -static void au0828_copy_vbi(struct au0828_dev *dev, - struct au0828_dmaqueue *dma_q, - struct au0828_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) -{ - unsigned char *startwrite, *startread; - int bytesperline; - int i, j = 0; - - if (dev == NULL) { - au0828_isocdbg("dev is null\n"); - return; - } - - if (dma_q == NULL) { - au0828_isocdbg("dma_q is null\n"); - return; - } - if (buf == NULL) - return; - if (p == NULL) { - au0828_isocdbg("p is null\n"); - return; - } - if (outp == NULL) { - au0828_isocdbg("outp is null\n"); - return; - } - - bytesperline = dev->vbi_width; - - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; - - startread = p; - startwrite = outp + (dma_q->pos / 2); - - /* Make sure the bottom field populates the second half of the frame */ - if (buf->top_field == 0) - startwrite += bytesperline * dev->vbi_height; - - for (i = 0; i < len; i += 2) - startwrite[j++] = startread[i+1]; - - dma_q->pos += len; -} - - -/* - * video-buf generic routine to get the next available VBI buffer - */ -static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q, - struct au0828_buffer **buf) -{ - struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq); - char *outp; - - if (list_empty(&dma_q->active)) { - au0828_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vbi_buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0x00, (*buf)->vb.size); - - dev->isoc_ctl.vbi_buf = *buf; - - return; -} - -/* - * Controls the isoc copy of each urb packet - */ -static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) -{ - struct au0828_buffer *buf; - struct au0828_buffer *vbi_buf; - struct au0828_dmaqueue *dma_q = urb->context; - struct au0828_dmaqueue *vbi_dma_q = &dev->vbiq; - unsigned char *outp = NULL; - unsigned char *vbioutp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - unsigned char fbyte; - unsigned int vbi_field_size; - unsigned int remain, lencopy; - - if (!dev) - return 0; - - if ((dev->dev_state & DEV_DISCONNECTED) || - (dev->dev_state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - buf = dev->isoc_ctl.buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - vbi_buf = dev->isoc_ctl.vbi_buf; - if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - if (urb->iso_frame_desc[i].actual_length <= 0) - continue; - - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - au0828_isocdbg("packet bigger than packet size"); - continue; - } - - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - fbyte = p[0]; - len = urb->iso_frame_desc[i].actual_length - 4; - p += 4; - - if (fbyte & 0x80) { - len -= 4; - p += 4; - au0828_isocdbg("Video frame %s\n", - (fbyte & 0x40) ? "odd" : "even"); - if (fbyte & 0x40) { - /* VBI */ - if (vbi_buf != NULL) - vbi_buffer_filled(dev, - vbi_dma_q, - vbi_buf); - vbi_get_next_buf(vbi_dma_q, &vbi_buf); - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = videobuf_to_vmalloc( - &vbi_buf->vb); - - /* Video */ - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - - /* As long as isoc traffic is arriving, keep - resetting the timer */ - if (dev->vid_timeout_running) - mod_timer(&dev->vid_timeout, - jiffies + (HZ / 10)); - if (dev->vbi_timeout_running) - mod_timer(&dev->vbi_timeout, - jiffies + (HZ / 10)); - } - - if (buf != NULL) { - if (fbyte & 0x40) - buf->top_field = 1; - else - buf->top_field = 0; - } - - if (vbi_buf != NULL) { - if (fbyte & 0x40) - vbi_buf->top_field = 1; - else - vbi_buf->top_field = 0; - } - - dev->vbi_read = 0; - vbi_dma_q->pos = 0; - dma_q->pos = 0; - } - - vbi_field_size = dev->vbi_width * dev->vbi_height * 2; - if (dev->vbi_read < vbi_field_size) { - remain = vbi_field_size - dev->vbi_read; - if (len < remain) - lencopy = len; - else - lencopy = remain; - - if (vbi_buf != NULL) - au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, len); - - len -= lencopy; - p += lencopy; - dev->vbi_read += lencopy; - } - - if (dev->vbi_read >= vbi_field_size && buf != NULL) - au0828_copy_video(dev, dma_q, buf, p, outp, len); - } - return rc; -} - -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct au0828_fh *fh = vq->priv_data; - *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; - - if (0 == *count) - *count = AU0828_DEF_BUF; - - if (*count < AU0828_MIN_BUF) - *count = AU0828_MIN_BUF; - return 0; -} - -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) -{ - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct au0828_fh *fh = vq->priv_data; - struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); - struct au0828_dev *dev = fh->dev; - int rc = 0, urb_init = 0; - - buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; - - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) { - printk(KERN_INFO "videobuf_iolock failed\n"); - goto fail; - } - } - - if (!dev->isoc_ctl.num_bufs) - urb_init = 1; - - if (urb_init) { - rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB, - AU0828_MAX_ISO_BUFS, dev->max_pkt_size, - au0828_isoc_copy); - if (rc < 0) { - printk(KERN_INFO "au0828_init_isoc failed\n"); - goto fail; - } - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(vq, buf); - return rc; -} - -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct au0828_buffer *buf = container_of(vb, - struct au0828_buffer, - vb); - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; - struct au0828_dmaqueue *vidq = &dev->vidq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct au0828_buffer *buf = container_of(vb, - struct au0828_buffer, - vb); - - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops au0828_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* ------------------------------------------------------------------ - V4L2 interface - ------------------------------------------------------------------*/ - -static int au0828_i2s_init(struct au0828_dev *dev) -{ - /* Enable i2s mode */ - au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01); - return 0; -} - -/* - * Auvitek au0828 analog stream enable - * Please set interface0 to AS5 before enable the stream - */ -int au0828_analog_stream_enable(struct au0828_dev *d) -{ - dprintk(1, "au0828_analog_stream_enable called\n"); - au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00); - au0828_writereg(d, 0x106, 0x00); - /* set x position */ - au0828_writereg(d, 0x110, 0x00); - au0828_writereg(d, 0x111, 0x00); - au0828_writereg(d, 0x114, 0xa0); - au0828_writereg(d, 0x115, 0x05); - /* set y position */ - au0828_writereg(d, 0x112, 0x00); - au0828_writereg(d, 0x113, 0x00); - au0828_writereg(d, 0x116, 0xf2); - au0828_writereg(d, 0x117, 0x00); - au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3); - - return 0; -} - -int au0828_analog_stream_disable(struct au0828_dev *d) -{ - dprintk(1, "au0828_analog_stream_disable called\n"); - au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0); - return 0; -} - -void au0828_analog_stream_reset(struct au0828_dev *dev) -{ - dprintk(1, "au0828_analog_stream_reset called\n"); - au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0); - mdelay(30); - au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3); -} - -/* - * Some operations needs to stop current streaming - */ -static int au0828_stream_interrupt(struct au0828_dev *dev) -{ - int ret = 0; - - dev->stream_state = STREAM_INTERRUPT; - if (dev->dev_state == DEV_DISCONNECTED) - return -ENODEV; - else if (ret) { - dev->dev_state = DEV_MISCONFIGURED; - dprintk(1, "%s device is misconfigured!\n", __func__); - return ret; - } - return 0; -} - -/* - * au0828_release_resources - * unregister v4l2 devices - */ -void au0828_analog_unregister(struct au0828_dev *dev) -{ - dprintk(1, "au0828_release_resources called\n"); - mutex_lock(&au0828_sysfs_lock); - - if (dev->vdev) - video_unregister_device(dev->vdev); - if (dev->vbi_dev) - video_unregister_device(dev->vbi_dev); - - mutex_unlock(&au0828_sysfs_lock); -} - - -/* Usage lock check functions */ -static int res_get(struct au0828_fh *fh, unsigned int bit) -{ - struct au0828_dev *dev = fh->dev; - - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - if (dev->resources & bit) { - /* no, someone else uses it */ - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - dprintk(1, "res: get %d\n", bit); - - return 1; -} - -static int res_check(struct au0828_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct au0828_dev *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct au0828_fh *fh, unsigned int bits) -{ - struct au0828_dev *dev = fh->dev; - - BUG_ON((fh->resources & bits) != bits); - - fh->resources &= ~bits; - dev->resources &= ~bits; - dprintk(1, "res: put %d\n", bits); -} - -static int get_ressource(struct au0828_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return AU0828_RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return AU0828_RESOURCE_VBI; - default: - BUG(); - return 0; - } -} - -/* This function ensures that video frames continue to be delivered even if - the ITU-656 input isn't receiving any data (thereby preventing applications - such as tvtime from hanging) */ -void au0828_vid_buffer_timeout(unsigned long data) -{ - struct au0828_dev *dev = (struct au0828_dev *) data; - struct au0828_dmaqueue *dma_q = &dev->vidq; - struct au0828_buffer *buf; - unsigned char *vid_data; - unsigned long flags = 0; - - spin_lock_irqsave(&dev->slock, flags); - - buf = dev->isoc_ctl.buf; - if (buf != NULL) { - vid_data = videobuf_to_vmalloc(&buf->vb); - memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */ - buffer_filled(dev, dma_q, buf); - } - get_next_buf(dma_q, &buf); - - if (dev->vid_timeout_running == 1) - mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); - - spin_unlock_irqrestore(&dev->slock, flags); -} - -void au0828_vbi_buffer_timeout(unsigned long data) -{ - struct au0828_dev *dev = (struct au0828_dev *) data; - struct au0828_dmaqueue *dma_q = &dev->vbiq; - struct au0828_buffer *buf; - unsigned char *vbi_data; - unsigned long flags = 0; - - spin_lock_irqsave(&dev->slock, flags); - - buf = dev->isoc_ctl.vbi_buf; - if (buf != NULL) { - vbi_data = videobuf_to_vmalloc(&buf->vb); - memset(vbi_data, 0x00, buf->vb.size); - vbi_buffer_filled(dev, dma_q, buf); - } - vbi_get_next_buf(dma_q, &buf); - - if (dev->vbi_timeout_running == 1) - mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); - spin_unlock_irqrestore(&dev->slock, flags); -} - - -static int au0828_v4l2_open(struct file *filp) -{ - int ret = 0; - struct video_device *vdev = video_devdata(filp); - struct au0828_dev *dev = video_drvdata(filp); - struct au0828_fh *fh; - int type; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - default: - return -EINVAL; - } - - fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL); - if (NULL == fh) { - dprintk(1, "Failed allocate au0828_fh struct!\n"); - return -ENOMEM; - } - - fh->type = type; - fh->dev = dev; - filp->private_data = fh; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { - /* set au0828 interface0 to AS5 here again */ - ret = usb_set_interface(dev->usbdev, 0, 5); - if (ret < 0) { - printk(KERN_INFO "Au0828 can't set alternate to 5!\n"); - return -EBUSY; - } - dev->width = NTSC_STD_W; - dev->height = NTSC_STD_H; - dev->frame_size = dev->width * dev->height * 2; - dev->field_size = dev->width * dev->height; - dev->bytesperline = dev->width * 2; - - au0828_analog_stream_enable(dev); - au0828_analog_stream_reset(dev); - - /* If we were doing ac97 instead of i2s, it would go here...*/ - au0828_i2s_init(dev); - - dev->stream_state = STREAM_OFF; - dev->dev_state |= DEV_INITIALIZED; - } - - dev->users++; - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct au0828_buffer), fh, - &dev->lock); - - /* VBI Setup */ - dev->vbi_width = 720; - dev->vbi_height = 1; - videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct au0828_buffer), fh, - &dev->lock); - return ret; -} - -static int au0828_v4l2_close(struct file *filp) -{ - int ret; - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; - - if (res_check(fh, AU0828_RESOURCE_VIDEO)) { - /* Cancel timeout thread in case they didn't call streamoff */ - dev->vid_timeout_running = 0; - del_timer_sync(&dev->vid_timeout); - - videobuf_stop(&fh->vb_vidq); - res_free(fh, AU0828_RESOURCE_VIDEO); - } - - if (res_check(fh, AU0828_RESOURCE_VBI)) { - /* Cancel timeout thread in case they didn't call streamoff */ - dev->vbi_timeout_running = 0; - del_timer_sync(&dev->vbi_timeout); - - videobuf_stop(&fh->vb_vbiq); - res_free(fh, AU0828_RESOURCE_VBI); - } - - if (dev->users == 1) { - if (dev->dev_state & DEV_DISCONNECTED) { - au0828_analog_unregister(dev); - kfree(dev); - return 0; - } - - au0828_analog_stream_disable(dev); - - au0828_uninit_isoc(dev); - - /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); - - /* When close the device, set the usb intf0 into alt0 to free - USB bandwidth */ - ret = usb_set_interface(dev->usbdev, 0, 0); - if (ret < 0) - printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); - } - - videobuf_mmap_free(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vbiq); - kfree(fh); - dev->users--; - wake_up_interruptible_nr(&dev->open, 1); - return 0; -} - -static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, - size_t count, loff_t *pos) -{ - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_locked(dev, AU0828_RESOURCE_VIDEO)) - return -EBUSY; - - return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, AU0828_RESOURCE_VBI)) - return -EBUSY; - - if (dev->vbi_timeout_running == 0) { - /* Handle case where caller tries to read without - calling streamon first */ - dev->vbi_timeout_running = 1; - mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); - } - - return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - - return 0; -} - -static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, AU0828_RESOURCE_VIDEO)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, AU0828_RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } else { - return POLLERR; - } -} - -static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); - - return rc; -} - -static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, - struct v4l2_format *format) -{ - int ret; - int width = format->fmt.pix.width; - int height = format->fmt.pix.height; - - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* If they are demanding a format other than the one we support, - bail out (tvtime asks for UYVY and then retries with YUYV) */ - if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY) - return -EINVAL; - - /* format->fmt.pix.width only support 720 and height 480 */ - if (width != 720) - width = 720; - if (height != 480) - height = 480; - - format->fmt.pix.width = width; - format->fmt.pix.height = height; - format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - format->fmt.pix.bytesperline = width * 2; - format->fmt.pix.sizeimage = width * height * 2; - format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - format->fmt.pix.field = V4L2_FIELD_INTERLACED; - - if (cmd == VIDIOC_TRY_FMT) - return 0; - - /* maybe set new image format, driver current only support 720*480 */ - dev->width = width; - dev->height = height; - dev->frame_size = width * height * 2; - dev->field_size = width * height; - dev->bytesperline = width * 2; - - if (dev->stream_state == STREAM_ON) { - dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n"); - ret = au0828_stream_interrupt(dev); - if (ret != 0) { - dprintk(1, "error interrupting video stream!\n"); - return ret; - } - } - - /* set au0828 interface0 to AS5 here again */ - ret = usb_set_interface(dev->usbdev, 0, 5); - if (ret < 0) { - printk(KERN_INFO "Au0828 can't set alt setting to 5!\n"); - return -EBUSY; - } - - au0828_analog_stream_enable(dev); - - return 0; -} - - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); - if (qc->type) - return 0; - else - return -EINVAL; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - strlcpy(cap->driver, "au0828", sizeof(cap->driver)); - strlcpy(cap->card, dev->board.name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); - - /*set the device capabilities */ - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_TUNER; - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strcpy(f->description, "Packed YUV2"); - - f->flags = 0; - f->pixelformat = V4L2_PIX_FMT_UYVY; - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - f->fmt.pix.bytesperline = dev->bytesperline; - f->fmt.pix.sizeimage = dev->frame_size; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */ - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - return au0828_set_format(dev, VIDIOC_TRY_FMT, f); -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - printk(KERN_INFO "%s queue busy\n", __func__); - rc = -EBUSY; - goto out; - } - - rc = au0828_set_format(dev, VIDIOC_S_FMT, f); -out: - return rc; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); - - /* FIXME: when we support something other than NTSC, we are going to - have to make the au0828 bridge adjust the size of its capture - buffer, which is currently hardcoded at 720x480 */ - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm); - dev->std_set_in_tuner_core = 1; - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); - - return 0; -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - unsigned int tmp; - - static const char *inames[] = { - [AU0828_VMUX_UNDEFINED] = "Undefined", - [AU0828_VMUX_COMPOSITE] = "Composite", - [AU0828_VMUX_SVIDEO] = "S-Video", - [AU0828_VMUX_CABLE] = "Cable TV", - [AU0828_VMUX_TELEVISION] = "Television", - [AU0828_VMUX_DVB] = "DVB", - [AU0828_VMUX_DEBUG] = "tv debug" - }; - - tmp = input->index; - - if (tmp >= AU0828_MAX_INPUT) - return -EINVAL; - if (AUVI_INPUT(tmp).type == 0) - return -EINVAL; - - input->index = tmp; - strcpy(input->name, inames[AUVI_INPUT(tmp).type]); - if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) || - (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE)) - input->type |= V4L2_INPUT_TYPE_TUNER; - else - input->type |= V4L2_INPUT_TYPE_CAMERA; - - input->std = dev->vdev->tvnorms; - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - *i = dev->ctrl_input; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int index) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int i; - - dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, - index); - if (index >= AU0828_MAX_INPUT) - return -EINVAL; - if (AUVI_INPUT(index).type == 0) - return -EINVAL; - dev->ctrl_input = index; - - switch (AUVI_INPUT(index).type) { - case AU0828_VMUX_SVIDEO: - dev->input_type = AU0828_VMUX_SVIDEO; - break; - case AU0828_VMUX_COMPOSITE: - dev->input_type = AU0828_VMUX_COMPOSITE; - break; - case AU0828_VMUX_TELEVISION: - dev->input_type = AU0828_VMUX_TELEVISION; - break; - default: - dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n", - AUVI_INPUT(index).type); - break; - } - - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, - AUVI_INPUT(index).vmux, 0, 0); - - for (i = 0; i < AU0828_MAX_INPUT; i++) { - int enable = 0; - if (AUVI_INPUT(i).audio_setup == NULL) - continue; - - if (i == index) - enable = 1; - else - enable = 0; - if (enable) { - (AUVI_INPUT(i).audio_setup)(dev, enable); - } else { - /* Make sure we leave it turned on if some - other input is routed to this callback */ - if ((AUVI_INPUT(i).audio_setup) != - ((AUVI_INPUT(index).audio_setup))) { - (AUVI_INPUT(i).audio_setup)(dev, enable); - } - } - } - - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, - AUVI_INPUT(index).amux, 0, 0); - return 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - unsigned int index = a->index; - - if (a->index > 1) - return -EINVAL; - - index = dev->ctrl_ainput; - if (index == 0) - strcpy(a->name, "Television"); - else - strcpy(a->name, "Line in"); - - a->capability = V4L2_AUDCAP_STEREO; - a->index = index; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - if (a->index != dev->ctrl_ainput) - return -EINVAL; - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - return 0; - -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - if (t->index != 0) - return -EINVAL; - - strcpy(t->name, "Auvitek tuner"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - if (t->index != 0) - return -EINVAL; - - t->type = V4L2_TUNER_ANALOG_TV; - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); - - dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal, - t->afc); - - return 0; - -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - freq->type = V4L2_TUNER_ANALOG_TV; - freq->frequency = dev->ctrl_freq; - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - if (freq->tuner != 0) - return -EINVAL; - if (freq->type != V4L2_TUNER_ANALOG_TV) - return -EINVAL; - - dev->ctrl_freq = freq->frequency; - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1); - - if (dev->std_set_in_tuner_core == 0) { - /* If we've never sent the standard in tuner core, do so now. We - don't do this at device probe because we don't want to incur - the cost of a firmware load */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, - dev->vdev->tvnorms); - dev->std_set_in_tuner_core = 1; - } - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq); - - if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl) - dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0); - - au0828_analog_stream_reset(dev); - - return 0; -} - - -/* RAW VBI ioctls */ - -static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - format->fmt.vbi.samples_per_line = dev->vbi_width; - format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 0; - format->fmt.vbi.flags = 0; - format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; - - format->fmt.vbi.count[0] = dev->vbi_height; - format->fmt.vbi.count[1] = dev->vbi_height; - format->fmt.vbi.start[0] = 21; - format->fmt.vbi.start[1] = 284; - - return 0; -} - -static int vidioc_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - - if (v4l2_chip_match_host(&chip->match)) { - chip->ident = V4L2_IDENT_AU0828; - return 0; - } - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); - if (chip->ident == V4L2_IDENT_NONE) - return -EINVAL; - - return 0; -} - -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cc) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - cc->bounds.left = 0; - cc->bounds.top = 0; - cc->bounds.width = dev->width; - cc->bounds.height = dev->height; - - cc->defrect = cc->bounds; - - cc->pixelaspect.numerator = 54; - cc->pixelaspect.denominator = 59; - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc = -EINVAL; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (unlikely(type != fh->type)) - return -EINVAL; - - dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (unlikely(!res_get(fh, get_ressource(fh)))) - return -EBUSY; - - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - au0828_analog_stream_enable(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); - } - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - rc = videobuf_streamon(&fh->vb_vidq); - dev->vid_timeout_running = 1; - mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - rc = videobuf_streamon(&fh->vb_vbiq); - dev->vbi_timeout_running = 1; - mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); - } - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - int i; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev->vid_timeout_running = 0; - del_timer_sync(&dev->vid_timeout); - - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); - rc = au0828_stream_interrupt(dev); - if (rc != 0) - return rc; - - for (i = 0; i < AU0828_MAX_INPUT; i++) { - if (AUVI_INPUT(i).audio_setup == NULL) - continue; - (AUVI_INPUT(i).audio_setup)(dev, 0); - } - - if (res_check(fh, AU0828_RESOURCE_VIDEO)) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, AU0828_RESOURCE_VIDEO); - } - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - dev->vbi_timeout_running = 0; - del_timer_sync(&dev->vbi_timeout); - - if (res_check(fh, AU0828_RESOURCE_VBI)) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, AU0828_RESOURCE_VBI); - } - } - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } - - reg->val = au0828_read(dev, reg->reg); - return 0; -} - -static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } - return au0828_writereg(dev, reg->reg, reg->val); -} -#endif - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_reqbufs(&fh->vb_vidq, rb); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_reqbufs(&fh->vb_vbiq, rb); - - return rc; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_querybuf(&fh->vb_vidq, b); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_querybuf(&fh->vb_vbiq, b); - - return rc; -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_qbuf(&fh->vb_vidq, b); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_qbuf(&fh->vb_vbiq, b); - - return rc; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - /* Workaround for a bug in the au0828 hardware design that sometimes - results in the colorspace being inverted */ - if (dev->greenscreen_detected == 1) { - dprintk(1, "Detected green frame. Resetting stream...\n"); - au0828_analog_stream_reset(dev); - dev->greenscreen_detected = 0; - } - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK); - - return rc; -} - -static struct v4l2_file_operations au0828_v4l_fops = { - .owner = THIS_MODULE, - .open = au0828_v4l2_open, - .release = au0828_v4l2_close, - .read = au0828_v4l2_read, - .poll = au0828_v4l2_poll, - .mmap = au0828_v4l2_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_s_std = vidioc_s_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, -#endif - .vidioc_g_chip_ident = vidioc_g_chip_ident, -}; - -static const struct video_device au0828_video_template = { - .fops = &au0828_v4l_fops, - .release = video_device_release, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = V4L2_STD_NTSC_M, - .current_norm = V4L2_STD_NTSC_M, -}; - -/**************************************************************************/ - -int au0828_analog_register(struct au0828_dev *dev, - struct usb_interface *interface) -{ - int retval = -ENOMEM; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i, ret; - - dprintk(1, "au0828_analog_register called!\n"); - - /* set au0828 usb interface0 to as5 */ - retval = usb_set_interface(dev->usbdev, - interface->cur_altsetting->desc.bInterfaceNumber, 5); - if (retval != 0) { - printk(KERN_INFO "Failure setting usb interface0 to as5\n"); - return retval; - } - - /* Figure out which endpoint has the isoc interface */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - endpoint = &iface_desc->endpoint[i].desc; - if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - == USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_ISOC)) { - - /* we find our isoc in endpoint */ - u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize); - dev->max_pkt_size = (tmp & 0x07ff) * - (((tmp & 0x1800) >> 11) + 1); - dev->isoc_in_endpointaddr = endpoint->bEndpointAddress; - } - } - if (!(dev->isoc_in_endpointaddr)) { - printk(KERN_INFO "Could not locate isoc endpoint\n"); - kfree(dev); - return -ENODEV; - } - - init_waitqueue_head(&dev->open); - spin_lock_init(&dev->slock); - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); - INIT_LIST_HEAD(&dev->vbiq.active); - INIT_LIST_HEAD(&dev->vbiq.queued); - - dev->vid_timeout.function = au0828_vid_buffer_timeout; - dev->vid_timeout.data = (unsigned long) dev; - init_timer(&dev->vid_timeout); - - dev->vbi_timeout.function = au0828_vbi_buffer_timeout; - dev->vbi_timeout.data = (unsigned long) dev; - init_timer(&dev->vbi_timeout); - - dev->width = NTSC_STD_W; - dev->height = NTSC_STD_H; - dev->field_size = dev->width * dev->height; - dev->frame_size = dev->field_size << 1; - dev->bytesperline = dev->width << 1; - dev->ctrl_ainput = 0; - - /* allocate and fill v4l2 video struct */ - dev->vdev = video_device_alloc(); - if (NULL == dev->vdev) { - dprintk(1, "Can't allocate video_device.\n"); - return -ENOMEM; - } - - /* allocate the VBI struct */ - dev->vbi_dev = video_device_alloc(); - if (NULL == dev->vbi_dev) { - dprintk(1, "Can't allocate vbi_device.\n"); - ret = -ENOMEM; - goto err_vdev; - } - - /* Fill the video capture device struct */ - *dev->vdev = au0828_video_template; - dev->vdev->parent = &dev->usbdev->dev; - dev->vdev->lock = &dev->lock; - strcpy(dev->vdev->name, "au0828a video"); - - /* Setup the VBI device */ - *dev->vbi_dev = au0828_video_template; - dev->vbi_dev->parent = &dev->usbdev->dev; - dev->vbi_dev->lock = &dev->lock; - strcpy(dev->vbi_dev->name, "au0828a vbi"); - - /* Register the v4l2 device */ - video_set_drvdata(dev->vdev, dev); - retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1); - if (retval != 0) { - dprintk(1, "unable to register video device (error = %d).\n", - retval); - ret = -ENODEV; - goto err_vbi_dev; - } - - /* Register the vbi device */ - video_set_drvdata(dev->vbi_dev, dev); - retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1); - if (retval != 0) { - dprintk(1, "unable to register vbi device (error = %d).\n", - retval); - ret = -ENODEV; - goto err_vbi_dev; - } - - dprintk(1, "%s completed!\n", __func__); - - return 0; - -err_vbi_dev: - video_device_release(dev->vbi_dev); -err_vdev: - video_device_release(dev->vdev); - return ret; -} - diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h deleted file mode 100644 index 66a56ef7bbe4..000000000000 --- a/drivers/media/video/au0828/au0828.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Driver for the Auvitek AU0828 USB bridge - * - * 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. - */ - -#include -#include -#include -#include - -/* Analog */ -#include -#include -#include - -/* DVB */ -#include "demux.h" -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dvb_net.h" -#include "dvbdev.h" - -#include "au0828-reg.h" -#include "au0828-cards.h" - -#define DRIVER_NAME "au0828" -#define URB_COUNT 16 -#define URB_BUFSIZE (0xe522) - -/* Analog constants */ -#define NTSC_STD_W 720 -#define NTSC_STD_H 480 - -#define AU0828_INTERLACED_DEFAULT 1 -#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0) - -/* Defination for AU0828 USB transfer */ -#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */ -#define AU0828_ISO_PACKETS_PER_URB 128 - -#define AU0828_MIN_BUF 4 -#define AU0828_DEF_BUF 8 - -#define AU0828_MAX_INPUT 4 - -/* au0828 resource types (used for res_get/res_lock etc */ -#define AU0828_RESOURCE_VIDEO 0x01 -#define AU0828_RESOURCE_VBI 0x02 - -enum au0828_itype { - AU0828_VMUX_UNDEFINED = 0, - AU0828_VMUX_COMPOSITE, - AU0828_VMUX_SVIDEO, - AU0828_VMUX_CABLE, - AU0828_VMUX_TELEVISION, - AU0828_VMUX_DVB, - AU0828_VMUX_DEBUG -}; - -struct au0828_input { - enum au0828_itype type; - unsigned int vmux; - unsigned int amux; - void (*audio_setup) (void *priv, int enable); -}; - -struct au0828_board { - char *name; - unsigned int tuner_type; - unsigned char tuner_addr; - unsigned char i2c_clk_divider; - struct au0828_input input[AU0828_MAX_INPUT]; - -}; - -struct au0828_dvb { - struct mutex lock; - struct dvb_adapter adapter; - struct dvb_frontend *frontend; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dmx_frontend fe_hw; - struct dmx_frontend fe_mem; - struct dvb_net net; - int feeding; -}; - -enum au0828_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON -}; - -#define AUVI_INPUT(nr) (dev->board.input[nr]) - -/* device state */ -enum au0828_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04 -}; - -struct au0828_fh { - struct au0828_dev *dev; - unsigned int resources; - - struct videobuf_queue vb_vidq; - struct videobuf_queue vb_vbiq; - enum v4l2_buf_type type; -}; - -struct au0828_usb_isoc_ctl { - /* max packet size of isoc transaction */ - int max_pkt_size; - - /* number of allocated urbs */ - int num_bufs; - - /* urb for isoc transfers */ - struct urb **urb; - - /* transfer buffers for isoc transfer */ - char **transfer_buffer; - - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - - /* Stores already requested buffers */ - struct au0828_buffer *buf; - struct au0828_buffer *vbi_buf; - - /* Stores the number of received fields */ - int nfields; - - /* isoc urb callback */ - int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb); - -}; - -/* buffer for one video frame */ -struct au0828_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - struct list_head frame; - int top_field; - int receiving; -}; - -struct au0828_dmaqueue { - struct list_head active; - struct list_head queued; - - wait_queue_head_t wq; - - /* Counters to control buffer fill */ - int pos; -}; - -struct au0828_dev { - struct mutex mutex; - struct usb_device *usbdev; - int boardnr; - struct au0828_board board; - u8 ctrlmsg[64]; - - /* I2C */ - struct i2c_adapter i2c_adap; - struct i2c_algorithm i2c_algo; - struct i2c_client i2c_client; - u32 i2c_rc; - - /* Digital */ - struct au0828_dvb dvb; - struct work_struct restart_streaming; - - /* Analog */ - struct v4l2_device v4l2_dev; - int users; - unsigned int resources; /* resources in use */ - struct video_device *vdev; - struct video_device *vbi_dev; - struct timer_list vid_timeout; - int vid_timeout_running; - struct timer_list vbi_timeout; - int vbi_timeout_running; - int width; - int height; - int vbi_width; - int vbi_height; - u32 vbi_read; - u32 field_size; - u32 frame_size; - u32 bytesperline; - int type; - u8 ctrl_ainput; - __u8 isoc_in_endpointaddr; - u8 isoc_init_ok; - int greenscreen_detected; - unsigned int frame_count; - int ctrl_freq; - int input_type; - int std_set_in_tuner_core; - unsigned int ctrl_input; - enum au0828_dev_state dev_state; - enum au0828_stream_state stream_state; - wait_queue_head_t open; - - struct mutex lock; - - /* Isoc control struct */ - struct au0828_dmaqueue vidq; - struct au0828_dmaqueue vbiq; - struct au0828_usb_isoc_ctl isoc_ctl; - spinlock_t slock; - - /* usb transfer */ - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */ - char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc - transfer */ - - /* USB / URB Related */ - int urb_streaming; - struct urb *urbs[URB_COUNT]; -}; - -/* ----------------------------------------------------------- */ -#define au0828_read(dev, reg) au0828_readreg(dev, reg) -#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value) -#define au0828_andor(dev, reg, mask, value) \ - au0828_writereg(dev, reg, \ - (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask))) - -#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit)) -#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0) - -/* ----------------------------------------------------------- */ -/* au0828-core.c */ -extern u32 au0828_read(struct au0828_dev *dev, u16 reg); -extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val); -extern int au0828_debug; - -/* ----------------------------------------------------------- */ -/* au0828-cards.c */ -extern struct au0828_board au0828_boards[]; -extern struct usb_device_id au0828_usb_id_table[]; -extern void au0828_gpio_setup(struct au0828_dev *dev); -extern int au0828_tuner_callback(void *priv, int component, - int command, int arg); -extern void au0828_card_setup(struct au0828_dev *dev); - -/* ----------------------------------------------------------- */ -/* au0828-i2c.c */ -extern int au0828_i2c_register(struct au0828_dev *dev); -extern int au0828_i2c_unregister(struct au0828_dev *dev); - -/* ----------------------------------------------------------- */ -/* au0828-video.c */ -int au0828_analog_register(struct au0828_dev *dev, - struct usb_interface *interface); -int au0828_analog_stream_disable(struct au0828_dev *d); -void au0828_analog_unregister(struct au0828_dev *dev); - -/* ----------------------------------------------------------- */ -/* au0828-dvb.c */ -extern int au0828_dvb_register(struct au0828_dev *dev); -extern void au0828_dvb_unregister(struct au0828_dev *dev); - -/* au0828-vbi.c */ -extern struct videobuf_queue_ops au0828_vbi_qops; - -#define dprintk(level, fmt, arg...)\ - do { if (au0828_debug & level)\ - printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ - } while (0) diff --git a/drivers/media/video/cpia2/Kconfig b/drivers/media/video/cpia2/Kconfig deleted file mode 100644 index 66e9283f5993..000000000000 --- a/drivers/media/video/cpia2/Kconfig +++ /dev/null @@ -1,9 +0,0 @@ -config VIDEO_CPIA2 - tristate "CPiA2 Video For Linux" - depends on VIDEO_DEV && USB && VIDEO_V4L2 - ---help--- - This is the video4linux driver for cameras based on Vision's CPiA2 - (Colour Processor Interface ASIC), such as the Digital Blue QX5 - Microscope. If you have one of these cameras, say Y here - - This driver is also available as a module (cpia2). diff --git a/drivers/media/video/cpia2/Makefile b/drivers/media/video/cpia2/Makefile deleted file mode 100644 index 828cf1b1df86..000000000000 --- a/drivers/media/video/cpia2/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -cpia2-objs := cpia2_v4l.o cpia2_usb.o cpia2_core.o - -obj-$(CONFIG_VIDEO_CPIA2) += cpia2.o diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h deleted file mode 100644 index cdef677d57ec..000000000000 --- a/drivers/media/video/cpia2/cpia2.h +++ /dev/null @@ -1,487 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2.h - * - * Copyright 2001, STMicrolectronics, Inc. - * - * Contact: steve.miller@st.com - * - * Description: - * This is a USB driver for CPiA2 based video cameras. - * - * This driver is modelled on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - * This program is free software; 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 __CPIA2_H__ -#define __CPIA2_H__ - -#include -#include -#include -#include -#include -#include - -#include "cpia2_registers.h" - -/* define for verbose debug output */ -//#define _CPIA2_DEBUG_ - -/*** - * Image defines - ***/ - -/* Misc constants */ -#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */ - -/* USB Transfer mode */ -#define XFER_ISOC 0 -#define XFER_BULK 1 - -/* USB Alternates */ -#define USBIF_CMDONLY 0 -#define USBIF_BULK 1 -#define USBIF_ISO_1 2 /* 128 bytes/ms */ -#define USBIF_ISO_2 3 /* 384 bytes/ms */ -#define USBIF_ISO_3 4 /* 640 bytes/ms */ -#define USBIF_ISO_4 5 /* 768 bytes/ms */ -#define USBIF_ISO_5 6 /* 896 bytes/ms */ -#define USBIF_ISO_6 7 /* 1023 bytes/ms */ - -/* Flicker Modes */ -#define NEVER_FLICKER 0 -#define FLICKER_60 60 -#define FLICKER_50 50 - -/* Debug flags */ -#define DEBUG_NONE 0 -#define DEBUG_REG 0x00000001 -#define DEBUG_DUMP_PATCH 0x00000002 -#define DEBUG_DUMP_REGS 0x00000004 - -/*** - * Video frame sizes - ***/ -enum { - VIDEOSIZE_VGA = 0, /* 640x480 */ - VIDEOSIZE_CIF, /* 352x288 */ - VIDEOSIZE_QVGA, /* 320x240 */ - VIDEOSIZE_QCIF, /* 176x144 */ - VIDEOSIZE_288_216, - VIDEOSIZE_256_192, - VIDEOSIZE_224_168, - VIDEOSIZE_192_144, -}; - -#define STV_IMAGE_CIF_ROWS 288 -#define STV_IMAGE_CIF_COLS 352 - -#define STV_IMAGE_QCIF_ROWS 144 -#define STV_IMAGE_QCIF_COLS 176 - -#define STV_IMAGE_VGA_ROWS 480 -#define STV_IMAGE_VGA_COLS 640 - -#define STV_IMAGE_QVGA_ROWS 240 -#define STV_IMAGE_QVGA_COLS 320 - -#define JPEG_MARKER_COM (1<<6) /* Comment segment */ - -/*** - * Enums - ***/ -/* Sensor types available with cpia2 asics */ -enum sensors { - CPIA2_SENSOR_410, - CPIA2_SENSOR_500 -}; - -/* Asic types available in the CPiA2 architecture */ -#define CPIA2_ASIC_672 0x67 - -/* Device types (stv672, stv676, etc) */ -#define DEVICE_STV_672 0x0001 -#define DEVICE_STV_676 0x0002 - -enum frame_status { - FRAME_EMPTY, - FRAME_READING, /* In the process of being grabbed into */ - FRAME_READY, /* Ready to be read */ - FRAME_ERROR, -}; - -/*** - * Register access (for USB request byte) - ***/ -enum { - CAMERAACCESS_SYSTEM = 0, - CAMERAACCESS_VC, - CAMERAACCESS_VP, - CAMERAACCESS_IDATA -}; - -#define CAMERAACCESS_TYPE_BLOCK 0x00 -#define CAMERAACCESS_TYPE_RANDOM 0x04 -#define CAMERAACCESS_TYPE_MASK 0x08 -#define CAMERAACCESS_TYPE_REPEAT 0x0C - -#define TRANSFER_READ 0 -#define TRANSFER_WRITE 1 - -#define DEFAULT_ALT USBIF_ISO_6 -#define DEFAULT_BRIGHTNESS 0x46 -#define DEFAULT_CONTRAST 0x93 -#define DEFAULT_SATURATION 0x7f - -/* Power state */ -#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER -#define LO_POWER_MODE CPIA2_SYSTEM_CONTROL_LOW_POWER - - -/******** - * Commands - *******/ -enum { - CPIA2_CMD_NONE = 0, - CPIA2_CMD_GET_VERSION, - CPIA2_CMD_GET_PNP_ID, - CPIA2_CMD_GET_ASIC_TYPE, - CPIA2_CMD_GET_SENSOR, - CPIA2_CMD_GET_VP_DEVICE, - CPIA2_CMD_GET_VP_BRIGHTNESS, - CPIA2_CMD_SET_VP_BRIGHTNESS, - CPIA2_CMD_GET_CONTRAST, - CPIA2_CMD_SET_CONTRAST, - CPIA2_CMD_GET_VP_SATURATION, - CPIA2_CMD_SET_VP_SATURATION, - CPIA2_CMD_GET_VP_GPIO_DIRECTION, - CPIA2_CMD_SET_VP_GPIO_DIRECTION, - CPIA2_CMD_GET_VP_GPIO_DATA, - CPIA2_CMD_SET_VP_GPIO_DATA, - CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, - CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - CPIA2_CMD_GET_VC_MP_GPIO_DATA, - CPIA2_CMD_SET_VC_MP_GPIO_DATA, - CPIA2_CMD_ENABLE_PACKET_CTRL, - CPIA2_CMD_GET_FLICKER_MODES, - CPIA2_CMD_SET_FLICKER_MODES, - CPIA2_CMD_RESET_FIFO, /* clear fifo and enable stream block */ - CPIA2_CMD_SET_HI_POWER, - CPIA2_CMD_SET_LOW_POWER, - CPIA2_CMD_CLEAR_V2W_ERR, - CPIA2_CMD_SET_USER_MODE, - CPIA2_CMD_GET_USER_MODE, - CPIA2_CMD_FRAMERATE_REQ, - CPIA2_CMD_SET_COMPRESSION_STATE, - CPIA2_CMD_GET_WAKEUP, - CPIA2_CMD_SET_WAKEUP, - CPIA2_CMD_GET_PW_CONTROL, - CPIA2_CMD_SET_PW_CONTROL, - CPIA2_CMD_GET_SYSTEM_CTRL, - CPIA2_CMD_SET_SYSTEM_CTRL, - CPIA2_CMD_GET_VP_SYSTEM_STATE, - CPIA2_CMD_GET_VP_SYSTEM_CTRL, - CPIA2_CMD_SET_VP_SYSTEM_CTRL, - CPIA2_CMD_GET_VP_EXP_MODES, - CPIA2_CMD_SET_VP_EXP_MODES, - CPIA2_CMD_GET_DEVICE_CONFIG, - CPIA2_CMD_SET_DEVICE_CONFIG, - CPIA2_CMD_SET_SERIAL_ADDR, - CPIA2_CMD_SET_SENSOR_CR1, - CPIA2_CMD_GET_VC_CONTROL, - CPIA2_CMD_SET_VC_CONTROL, - CPIA2_CMD_SET_TARGET_KB, - CPIA2_CMD_SET_DEF_JPEG_OPT, - CPIA2_CMD_REHASH_VP4, - CPIA2_CMD_GET_USER_EFFECTS, - CPIA2_CMD_SET_USER_EFFECTS -}; - -enum user_cmd { - COMMAND_NONE = 0x00000001, - COMMAND_SET_FPS = 0x00000002, - COMMAND_SET_COLOR_PARAMS = 0x00000004, - COMMAND_GET_COLOR_PARAMS = 0x00000008, - COMMAND_SET_FORMAT = 0x00000010, /* size, etc */ - COMMAND_SET_FLICKER = 0x00000020 -}; - -/*** - * Some defines specific to the 676 chip - ***/ -#define CAMACC_CIF 0x01 -#define CAMACC_VGA 0x02 -#define CAMACC_QCIF 0x04 -#define CAMACC_QVGA 0x08 - - -struct cpia2_register { - u8 index; - u8 value; -}; - -struct cpia2_reg_mask { - u8 index; - u8 and_mask; - u8 or_mask; - u8 fill; -}; - -struct cpia2_command { - u32 command; - u8 req_mode; /* (Block or random) | registerBank */ - u8 reg_count; - u8 direction; - u8 start; - union reg_types { - struct cpia2_register registers[32]; - struct cpia2_reg_mask masks[16]; - u8 block_data[64]; - u8 *patch_data; /* points to function defined block */ - } buffer; -}; - -struct camera_params { - struct { - u8 firmware_revision_hi; /* For system register set (bank 0) */ - u8 firmware_revision_lo; - u8 asic_id; /* Video Compressor set (bank 1) */ - u8 asic_rev; - u8 vp_device_hi; /* Video Processor set (bank 2) */ - u8 vp_device_lo; - u8 sensor_flags; - u8 sensor_rev; - } version; - - struct { - u32 device_type; /* enumerated from vendor/product ids. - * Currently, either STV_672 or STV_676 */ - u16 vendor; - u16 product; - u16 device_revision; - } pnp_id; - - struct { - u8 brightness; /* CPIA2_VP_EXPOSURE_TARGET */ - u8 contrast; /* Note: this is CPIA2_VP_YRANGE */ - u8 saturation; /* CPIA2_VP_SATURATION */ - } color_params; - - struct { - u8 cam_register; - u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ - } flicker_control; - - struct { - u8 jpeg_options; - u8 creep_period; - u8 user_squeeze; - u8 inhibit_htables; - } compression; - - struct { - u8 ohsize; /* output image size */ - u8 ovsize; - u8 hcrop; /* cropping start_pos/4 */ - u8 vcrop; - u8 hphase; /* scaling registers */ - u8 vphase; - u8 hispan; - u8 vispan; - u8 hicrop; - u8 vicrop; - u8 hifraction; - u8 vifraction; - } image_size; - - struct { - int width; /* actual window width */ - int height; /* actual window height */ - } roi; - - struct { - u8 video_mode; - u8 frame_rate; - u8 video_size; /* Not a register, just a convenience for cropped sizes */ - u8 gpio_direction; - u8 gpio_data; - u8 system_ctrl; - u8 system_state; - u8 lowlight_boost; /* Bool: 0 = off, 1 = on */ - u8 device_config; - u8 exposure_modes; - u8 user_effects; - } vp_params; - - struct { - u8 pw_control; - u8 wakeup; - u8 vc_control; - u8 vc_mp_direction; - u8 vc_mp_data; - u8 quality; - } vc_params; - - struct { - u8 power_mode; - u8 system_ctrl; - u8 stream_mode; /* This is the current alternate for usb drivers */ - u8 allow_corrupt; - } camera_state; -}; - -#define NUM_SBUF 2 - -struct cpia2_sbuf { - char *data; - struct urb *urb; -}; - -struct framebuf { - struct timeval timestamp; - unsigned long seq; - int num; - int length; - int max_length; - volatile enum frame_status status; - u8 *data; - struct framebuf *next; -}; - -struct camera_data { - /* locks */ - struct v4l2_device v4l2_dev; - struct mutex v4l2_lock; /* serialize file operations */ - struct v4l2_ctrl_handler hdl; - struct { - /* Lights control cluster */ - struct v4l2_ctrl *top_light; - struct v4l2_ctrl *bottom_light; - }; - struct v4l2_ctrl *usb_alt; - - /* camera status */ - int first_image_seen; - enum sensors sensor_type; - u8 flush; - struct v4l2_fh *stream_fh; - u8 mmapped; - int streaming; /* 0 = no, 1 = yes */ - int xfer_mode; /* XFER_BULK or XFER_ISOC */ - struct camera_params params; /* camera settings */ - - /* v4l */ - int video_size; /* VIDEO_SIZE_ */ - struct video_device vdev; /* v4l videodev */ - u32 width; - u32 height; /* Its size */ - __u32 pixelformat; /* Format fourcc */ - - /* USB */ - struct usb_device *dev; - unsigned char iface; - unsigned int cur_alt; - unsigned int old_alt; - struct cpia2_sbuf sbuf[NUM_SBUF]; /* Double buffering */ - - wait_queue_head_t wq_stream; - - /* Buffering */ - u32 frame_size; - int num_frames; - unsigned long frame_count; - u8 *frame_buffer; /* frame buffer data */ - struct framebuf *buffers; - struct framebuf * volatile curbuff; - struct framebuf *workbuff; - - /* MJPEG Extension */ - int APPn; /* Number of APP segment to be written, must be 0..15 */ - int APP_len; /* Length of data in JPEG APPn segment */ - char APP_data[60]; /* Data in the JPEG APPn segment. */ - - int COM_len; /* Length of data in JPEG COM segment */ - char COM_data[60]; /* Data in JPEG COM segment */ -}; - -/* v4l */ -int cpia2_register_camera(struct camera_data *cam); -void cpia2_unregister_camera(struct camera_data *cam); -void cpia2_camera_release(struct v4l2_device *v4l2_dev); - -/* core */ -int cpia2_reset_camera(struct camera_data *cam); -int cpia2_set_low_power(struct camera_data *cam); -void cpia2_dbg_dump_registers(struct camera_data *cam); -int cpia2_match_video_size(int width, int height); -void cpia2_set_camera_state(struct camera_data *cam); -void cpia2_save_camera_state(struct camera_data *cam); -void cpia2_set_color_params(struct camera_data *cam); -void cpia2_set_brightness(struct camera_data *cam, unsigned char value); -void cpia2_set_contrast(struct camera_data *cam, unsigned char value); -void cpia2_set_saturation(struct camera_data *cam, unsigned char value); -int cpia2_set_flicker_mode(struct camera_data *cam, int mode); -void cpia2_set_format(struct camera_data *cam); -int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); -int cpia2_do_command(struct camera_data *cam, - unsigned int command, - unsigned char direction, unsigned char param); -struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); -int cpia2_init_camera(struct camera_data *cam); -int cpia2_allocate_buffers(struct camera_data *cam); -void cpia2_free_buffers(struct camera_data *cam); -long cpia2_read(struct camera_data *cam, - char __user *buf, unsigned long count, int noblock); -unsigned int cpia2_poll(struct camera_data *cam, - struct file *filp, poll_table *wait); -int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); -void cpia2_set_property_flip(struct camera_data *cam, int prop_val); -void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); -int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); -int cpia2_set_fps(struct camera_data *cam, int framerate); - -/* usb */ -int cpia2_usb_init(void); -void cpia2_usb_cleanup(void); -int cpia2_usb_transfer_cmd(struct camera_data *cam, void *registers, - u8 request, u8 start, u8 count, u8 direction); -int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate); -int cpia2_usb_stream_stop(struct camera_data *cam); -int cpia2_usb_stream_pause(struct camera_data *cam); -int cpia2_usb_stream_resume(struct camera_data *cam); -int cpia2_usb_change_streaming_alternate(struct camera_data *cam, - unsigned int alt); - - -/* ----------------------- debug functions ---------------------- */ -#ifdef _CPIA2_DEBUG_ -#define ALOG(lev, fmt, args...) printk(lev "%s:%d %s(): " fmt, __FILE__, __LINE__, __func__, ## args) -#define LOG(fmt, args...) ALOG(KERN_INFO, fmt, ## args) -#define ERR(fmt, args...) ALOG(KERN_ERR, fmt, ## args) -#define DBG(fmt, args...) ALOG(KERN_DEBUG, fmt, ## args) -#else -#define ALOG(fmt,args...) printk(fmt,##args) -#define LOG(fmt,args...) ALOG(KERN_INFO "cpia2: "fmt,##args) -#define ERR(fmt,args...) ALOG(KERN_ERR "cpia2: "fmt,##args) -#define DBG(fmn,args...) do {} while(0) -#endif -/* No function or lineno, for shorter lines */ -#define KINFO(fmt, args...) printk(KERN_INFO fmt,##args) - -#endif diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c deleted file mode 100644 index 187012ce444b..000000000000 --- a/drivers/media/video/cpia2/cpia2_core.c +++ /dev/null @@ -1,2417 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2_core.c - * - * Copyright 2001, STMicrolectronics, Inc. - * Contact: steve.miller@st.com - * - * Description: - * This is a USB driver for CPia2 based video cameras. - * The infrastructure of this driver is based on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - * This program is free software; 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. - * - * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox - * - ****************************************************************************/ - -#include "cpia2.h" - -#include -#include -#include -#include -#include - -#define FIRMWARE "cpia2/stv0672_vp4.bin" -MODULE_FIRMWARE(FIRMWARE); - -/* #define _CPIA2_DEBUG_ */ - -#ifdef _CPIA2_DEBUG_ - -static const char *block_name[] = { - "System", - "VC", - "VP", - "IDATA" -}; -#endif - -static unsigned int debugs_on; /* default 0 - DEBUG_REG */ - - -/****************************************************************************** - * - * Forward Declarations - * - *****************************************************************************/ -static int apply_vp_patch(struct camera_data *cam); -static int set_default_user_mode(struct camera_data *cam); -static int set_vw_size(struct camera_data *cam, int size); -static int configure_sensor(struct camera_data *cam, - int reqwidth, int reqheight); -static int config_sensor_410(struct camera_data *cam, - int reqwidth, int reqheight); -static int config_sensor_500(struct camera_data *cam, - int reqwidth, int reqheight); -static int set_all_properties(struct camera_data *cam); -static void wake_system(struct camera_data *cam); -static void set_lowlight_boost(struct camera_data *cam); -static void reset_camera_struct(struct camera_data *cam); -static int cpia2_set_high_power(struct camera_data *cam); - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the - * area and marking the pages as reserved. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long kva, ret; - - kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); - kva |= adr & (PAGE_SIZE-1); /* restore the offset */ - ret = __pa(kva); - return ret; -} - -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - /* Round it off to PAGE_SIZE */ - size = PAGE_ALIGN(size); - - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - - while ((long)size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - size = PAGE_ALIGN(size); - - adr = (unsigned long) mem; - while ((long)size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -/****************************************************************************** - * - * cpia2_do_command - * - * Send an arbitrary command to the camera. For commands that read from - * the camera, copy the buffers into the proper param structures. - *****************************************************************************/ -int cpia2_do_command(struct camera_data *cam, - u32 command, u8 direction, u8 param) -{ - int retval = 0; - struct cpia2_command cmd; - unsigned int device = cam->params.pnp_id.device_type; - - cmd.command = command; - cmd.reg_count = 2; /* default */ - cmd.direction = direction; - - /*** - * Set up the command. - ***/ - switch (command) { - case CPIA2_CMD_GET_VERSION: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.start = CPIA2_SYSTEM_DEVICE_HI; - break; - case CPIA2_CMD_GET_PNP_ID: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 8; - cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI; - break; - case CPIA2_CMD_GET_ASIC_TYPE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.start = CPIA2_VC_ASIC_ID; - break; - case CPIA2_CMD_GET_SENSOR: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.start = CPIA2_VP_SENSOR_FLAGS; - break; - case CPIA2_CMD_GET_VP_DEVICE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.start = CPIA2_VP_DEVICEH; - break; - case CPIA2_CMD_SET_VP_BRIGHTNESS: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VP_BRIGHTNESS: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_EXPOSURE_TARGET; - else - cmd.start = CPIA2_VP5_EXPOSURE_TARGET; - break; - case CPIA2_CMD_SET_CONTRAST: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_CONTRAST: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_YRANGE; - break; - case CPIA2_CMD_SET_VP_SATURATION: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VP_SATURATION: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP_SATURATION; - else - cmd.start = CPIA2_VP5_MCUVSATURATION; - break; - case CPIA2_CMD_SET_VP_GPIO_DATA: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VP_GPIO_DATA: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_GPIO_DATA; - break; - case CPIA2_CMD_SET_VP_GPIO_DIRECTION: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VP_GPIO_DIRECTION: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_GPIO_DIRECTION; - break; - case CPIA2_CMD_SET_VC_MP_GPIO_DATA: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VC_MP_GPIO_DATA: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_MP_DATA; - break; - case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_MP_DIR; - break; - case CPIA2_CMD_ENABLE_PACKET_CTRL: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL; - cmd.reg_count = 1; - cmd.buffer.block_data[0] = param; - break; - case CPIA2_CMD_SET_FLICKER_MODES: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_FLICKER_MODES: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_FLICKER_MODES; - break; - case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */ - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.reg_count = 2; - cmd.start = 0; - cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; - cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | - CPIA2_VC_ST_CTRL_EOF_DETECT | - CPIA2_VC_ST_CTRL_FIFO_ENABLE; - break; - case CPIA2_CMD_SET_HI_POWER: - cmd.req_mode = - CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; - cmd.reg_count = 2; - cmd.buffer.registers[0].index = - CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.registers[1].index = - CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; - cmd.buffer.registers[1].value = - CPIA2_SYSTEM_CONTROL_HIGH_POWER; - break; - case CPIA2_CMD_SET_LOW_POWER: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.block_data[0] = 0; - break; - case CPIA2_CMD_CLEAR_V2W_ERR: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; - break; - case CPIA2_CMD_SET_USER_MODE: /* Then fall through */ - cmd.buffer.block_data[0] = param; - case CPIA2_CMD_GET_USER_MODE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_USER_MODE; - else - cmd.start = CPIA2_VP5_USER_MODE; - break; - case CPIA2_CMD_FRAMERATE_REQ: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_FRAMERATE_REQUEST; - else - cmd.start = CPIA2_VP5_FRAMERATE_REQUEST; - cmd.buffer.block_data[0] = param; - break; - case CPIA2_CMD_SET_WAKEUP: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_WAKEUP: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_WAKEUP; - break; - case CPIA2_CMD_SET_PW_CONTROL: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_PW_CONTROL: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_PW_CTRL; - break; - case CPIA2_CMD_GET_VP_SYSTEM_STATE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_SYSTEMSTATE; - break; - case CPIA2_CMD_SET_SYSTEM_CTRL: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_SYSTEM_CTRL: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; - break; - case CPIA2_CMD_SET_VP_SYSTEM_CTRL: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VP_SYSTEM_CTRL: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_SYSTEMCTRL; - break; - case CPIA2_CMD_SET_VP_EXP_MODES: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VP_EXP_MODES: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_EXPOSURE_MODES; - break; - case CPIA2_CMD_SET_DEVICE_CONFIG: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_DEVICE_CONFIG: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_DEVICE_CONFIG; - break; - case CPIA2_CMD_SET_SERIAL_ADDR: - cmd.buffer.block_data[0] = param; - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR; - break; - case CPIA2_CMD_SET_SENSOR_CR1: - cmd.buffer.block_data[0] = param; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_SENSOR_CR1; - break; - case CPIA2_CMD_SET_VC_CONTROL: - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_VC_CONTROL: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_VC_CTRL; - break; - case CPIA2_CMD_SET_TARGET_KB: - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB; - cmd.buffer.registers[0].value = param; - break; - case CPIA2_CMD_SET_DEF_JPEG_OPT: - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.reg_count = 4; - cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT; - cmd.buffer.registers[0].value = - CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE; - cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE; - cmd.buffer.registers[1].value = 20; - cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD; - cmd.buffer.registers[2].value = 2; - cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT; - cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT; - break; - case CPIA2_CMD_REHASH_VP4: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_REHASH_VALUES; - cmd.buffer.block_data[0] = param; - break; - case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as - this register can also affect - flicker modes */ - cmd.buffer.block_data[0] = param; /* Then fall through */ - case CPIA2_CMD_GET_USER_EFFECTS: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_USER_EFFECTS; - else - cmd.start = CPIA2_VP5_USER_EFFECTS; - break; - default: - LOG("DoCommand received invalid command\n"); - return -EINVAL; - } - - retval = cpia2_send_command(cam, &cmd); - if (retval) { - return retval; - } - - /*** - * Now copy any results from a read into the appropriate param struct. - ***/ - switch (command) { - case CPIA2_CMD_GET_VERSION: - cam->params.version.firmware_revision_hi = - cmd.buffer.block_data[0]; - cam->params.version.firmware_revision_lo = - cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_PNP_ID: - cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) | - cmd.buffer.block_data[1]; - cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) | - cmd.buffer.block_data[3]; - cam->params.pnp_id.device_revision = - (cmd.buffer.block_data[4] << 8) | - cmd.buffer.block_data[5]; - if (cam->params.pnp_id.vendor == 0x553) { - if (cam->params.pnp_id.product == 0x100) { - cam->params.pnp_id.device_type = DEVICE_STV_672; - } else if (cam->params.pnp_id.product == 0x140 || - cam->params.pnp_id.product == 0x151) { - cam->params.pnp_id.device_type = DEVICE_STV_676; - } - } - break; - case CPIA2_CMD_GET_ASIC_TYPE: - cam->params.version.asic_id = cmd.buffer.block_data[0]; - cam->params.version.asic_rev = cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_SENSOR: - cam->params.version.sensor_flags = cmd.buffer.block_data[0]; - cam->params.version.sensor_rev = cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_VP_DEVICE: - cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; - cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_VP_GPIO_DATA: - cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_GPIO_DIRECTION: - cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: - cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VC_MP_GPIO_DATA: - cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_FLICKER_MODES: - cam->params.flicker_control.cam_register = - cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_WAKEUP: - cam->params.vc_params.wakeup = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_PW_CONTROL: - cam->params.vc_params.pw_control = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_SYSTEM_CTRL: - cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_SYSTEM_STATE: - cam->params.vp_params.system_state = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_SYSTEM_CTRL: - cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_EXP_MODES: - cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_DEVICE_CONFIG: - cam->params.vp_params.device_config = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VC_CONTROL: - cam->params.vc_params.vc_control = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_USER_MODE: - cam->params.vp_params.video_mode = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_USER_EFFECTS: - cam->params.vp_params.user_effects = cmd.buffer.block_data[0]; - break; - default: - break; - } - return retval; -} - -/****************************************************************************** - * - * cpia2_send_command - * - *****************************************************************************/ - -#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read") -#define BINDEX(cmd) (cmd->req_mode & 0x03) - -int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) -{ - u8 count; - u8 start; - u8 *buffer; - int retval; - - switch (cmd->req_mode & 0x0c) { - case CAMERAACCESS_TYPE_RANDOM: - count = cmd->reg_count * sizeof(struct cpia2_register); - start = 0; - buffer = (u8 *) & cmd->buffer; - if (debugs_on & DEBUG_REG) - DBG("%s Random: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - case CAMERAACCESS_TYPE_BLOCK: - count = cmd->reg_count; - start = cmd->start; - buffer = cmd->buffer.block_data; - if (debugs_on & DEBUG_REG) - DBG("%s Block: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - case CAMERAACCESS_TYPE_MASK: - count = cmd->reg_count * sizeof(struct cpia2_reg_mask); - start = 0; - buffer = (u8 *) & cmd->buffer; - if (debugs_on & DEBUG_REG) - DBG("%s Mask: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */ - count = cmd->reg_count; - start = cmd->start; - buffer = cmd->buffer.block_data; - if (debugs_on & DEBUG_REG) - DBG("%s Repeat: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - default: - LOG("%s: invalid request mode\n",__func__); - return -EINVAL; - } - - retval = cpia2_usb_transfer_cmd(cam, - buffer, - cmd->req_mode, - start, count, cmd->direction); -#ifdef _CPIA2_DEBUG_ - if (debugs_on & DEBUG_REG) { - int i; - for (i = 0; i < cmd->reg_count; i++) { - if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK) - KINFO("%s Block: [0x%02X] = 0x%02X\n", - DIR(cmd), start + i, buffer[i]); - if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM) - KINFO("%s Random: [0x%02X] = 0x%02X\n", - DIR(cmd), cmd->buffer.registers[i].index, - cmd->buffer.registers[i].value); - } - } -#endif - - return retval; -}; - -/************* - * Functions to implement camera functionality - *************/ -/****************************************************************************** - * - * cpia2_get_version_info - * - *****************************************************************************/ -static void cpia2_get_version_info(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0); -} - -/****************************************************************************** - * - * cpia2_reset_camera - * - * Called at least during the open process, sets up initial params. - *****************************************************************************/ -int cpia2_reset_camera(struct camera_data *cam) -{ - u8 tmp_reg; - int retval = 0; - int target_kb; - int i; - struct cpia2_command cmd; - - /*** - * VC setup - ***/ - retval = configure_sensor(cam, - cam->params.roi.width, - cam->params.roi.height); - if (retval < 0) { - ERR("Couldn't configure sensor, error=%d\n", retval); - return retval; - } - - /* Clear FIFO and route/enable stream block */ - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.direction = TRANSFER_WRITE; - cmd.reg_count = 2; - cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; - cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | - CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE; - - cpia2_send_command(cam, &cmd); - - cpia2_set_high_power(cam); - - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - /* Enable button notification */ - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; - cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL; - cmd.buffer.registers[0].value = - CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX; - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - } - - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - retval = apply_vp_patch(cam); - - /* wait for vp to go to sleep */ - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - /*** - * If this is a 676, apply VP5 fixes before we start streaming - ***/ - if (cam->params.pnp_id.device_type == DEVICE_STV_676) { - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; - - /* The following writes improve the picture */ - cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL; - cmd.buffer.registers[0].value = 0; /* reduce from the default - * rec 601 pedestal of 16 */ - cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE; - cmd.buffer.registers[1].value = 0x92; /* increase from 100% to - * (256/256 - 31) to fill - * available range */ - cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING; - cmd.buffer.registers[2].value = 0xFF; /* Increase from the - * default rec 601 ceiling - * of 240 */ - cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION; - cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec - * 601 100% level (128) - * to 145-192 */ - cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP; - cmd.buffer.registers[4].value = 0x80; /* Inhibit the - * anti-flicker */ - - /* The following 4 writes are a fix to allow QVGA to work at 30 fps */ - cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H; - cmd.buffer.registers[5].value = 0x01; - cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L; - cmd.buffer.registers[6].value = 0xE3; - cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA; - cmd.buffer.registers[7].value = 0x02; - cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA; - cmd.buffer.registers[8].value = 0xFC; - - cmd.direction = TRANSFER_WRITE; - cmd.reg_count = 9; - - cpia2_send_command(cam, &cmd); - } - - /* Activate all settings and start the data stream */ - /* Set user mode */ - set_default_user_mode(cam); - - /* Give VP time to wake up */ - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - set_all_properties(cam); - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); - DBG("After SetAllProperties(cam), user mode is 0x%0X\n", - cam->params.vp_params.video_mode); - - /*** - * Set audio regulator off. This and the code to set the compresison - * state are too complex to form a CPIA2_CMD_, and seem to be somewhat - * intertwined. This stuff came straight from the windows driver. - ***/ - /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */ - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); - tmp_reg = cam->params.vp_params.system_ctrl; - cmd.buffer.registers[0].value = tmp_reg & - (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF)); - - cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); - cmd.buffer.registers[1].value = cam->params.vp_params.device_config | - CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE; - cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL; - cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG; - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; - cmd.reg_count = 2; - cmd.direction = TRANSFER_WRITE; - cmd.start = 0; - cpia2_send_command(cam, &cmd); - - /* Set the correct I2C address in the CPiA-2 system register */ - cpia2_do_command(cam, - CPIA2_CMD_SET_SERIAL_ADDR, - TRANSFER_WRITE, - CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR); - - /* Now have sensor access - set bit to turn the audio regulator off */ - cpia2_do_command(cam, - CPIA2_CMD_SET_SENSOR_CR1, - TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR); - - /* Set the correct I2C address in the CPiA-2 system register */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - cpia2_do_command(cam, - CPIA2_CMD_SET_SERIAL_ADDR, - TRANSFER_WRITE, - CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88 - else - cpia2_do_command(cam, - CPIA2_CMD_SET_SERIAL_ADDR, - TRANSFER_WRITE, - CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a - - /* increase signal drive strength */ - if (cam->params.pnp_id.device_type == DEVICE_STV_676) - cpia2_do_command(cam, - CPIA2_CMD_SET_VP_EXP_MODES, - TRANSFER_WRITE, - CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP); - - /* Start autoexposure */ - cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); - cmd.buffer.registers[0].value = cam->params.vp_params.device_config & - (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF); - - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); - cmd.buffer.registers[1].value = - cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL; - - cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG; - cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL; - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; - cmd.reg_count = 2; - cmd.direction = TRANSFER_WRITE; - - cpia2_send_command(cam, &cmd); - - /* Set compression state */ - cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0); - if (cam->params.compression.inhibit_htables) { - tmp_reg = cam->params.vc_params.vc_control | - CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; - } else { - tmp_reg = cam->params.vc_params.vc_control & - ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; - } - cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); - - /* Set target size (kb) on vc - This is a heuristic based on the quality parameter and the raw - framesize in kB divided by 16 (the compression factor when the - quality is 100%) */ - target_kb = (cam->width * cam->height * 2 / 16384) * - cam->params.vc_params.quality / 100; - if (target_kb < 1) - target_kb = 1; - cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, - TRANSFER_WRITE, target_kb); - - /* Wiggle VC Reset */ - /*** - * First read and wait a bit. - ***/ - for (i = 0; i < 50; i++) { - cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL, - TRANSFER_READ, 0); - } - - tmp_reg = cam->params.vc_params.pw_control; - tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N; - - cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); - - tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N; - cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); - - cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0); - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); - DBG("After VC RESET, user mode is 0x%0X\n", - cam->params.vp_params.video_mode); - - return retval; -} - -/****************************************************************************** - * - * cpia2_set_high_power - * - *****************************************************************************/ -static int cpia2_set_high_power(struct camera_data *cam) -{ - int i; - for (i = 0; i <= 50; i++) { - /* Read system status */ - cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0); - - /* If there is an error, clear it */ - if(cam->params.camera_state.system_ctrl & - CPIA2_SYSTEM_CONTROL_V2W_ERR) - cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR, - TRANSFER_WRITE, 0); - - /* Try to set high power mode */ - cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, - TRANSFER_WRITE, 1); - - /* Try to read something in VP to check if everything is awake */ - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE, - TRANSFER_READ, 0); - if (cam->params.vp_params.system_state & - CPIA2_VP_SYSTEMSTATE_HK_ALIVE) { - break; - } else if (i == 50) { - cam->params.camera_state.power_mode = LO_POWER_MODE; - ERR("Camera did not wake up\n"); - return -EIO; - } - } - - DBG("System now in high power state\n"); - cam->params.camera_state.power_mode = HI_POWER_MODE; - return 0; -} - -/****************************************************************************** - * - * cpia2_set_low_power - * - *****************************************************************************/ -int cpia2_set_low_power(struct camera_data *cam) -{ - cam->params.camera_state.power_mode = LO_POWER_MODE; - cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0); - return 0; -} - -/****************************************************************************** - * - * apply_vp_patch - * - *****************************************************************************/ -static int cpia2_send_onebyte_command(struct camera_data *cam, - struct cpia2_command *cmd, - u8 start, u8 datum) -{ - cmd->buffer.block_data[0] = datum; - cmd->start = start; - cmd->reg_count = 1; - return cpia2_send_command(cam, cmd); -} - -static int apply_vp_patch(struct camera_data *cam) -{ - const struct firmware *fw; - const char fw_name[] = FIRMWARE; - int i, ret; - struct cpia2_command cmd; - - ret = request_firmware(&fw, fw_name, &cam->dev->dev); - if (ret) { - printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n", - fw_name); - return ret; - } - - cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP; - cmd.direction = TRANSFER_WRITE; - - /* First send the start address... */ - cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ - cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ - - /* ... followed by the data payload */ - for (i = 2; i < fw->size; i += 64) { - cmd.start = 0x0C; /* Data */ - cmd.reg_count = min_t(int, 64, fw->size - i); - memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count); - cpia2_send_command(cam, &cmd); - } - - /* Next send the start address... */ - cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ - cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ - - /* ... followed by the 'goto' command */ - cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1); - - release_firmware(fw); - return 0; -} - -/****************************************************************************** - * - * set_default_user_mode - * - *****************************************************************************/ -static int set_default_user_mode(struct camera_data *cam) -{ - unsigned char user_mode; - unsigned char frame_rate; - int width = cam->params.roi.width; - int height = cam->params.roi.height; - - switch (cam->params.version.sensor_flags) { - case CPIA2_VP_SENSOR_FLAGS_404: - case CPIA2_VP_SENSOR_FLAGS_407: - case CPIA2_VP_SENSOR_FLAGS_409: - case CPIA2_VP_SENSOR_FLAGS_410: - if ((width > STV_IMAGE_QCIF_COLS) - || (height > STV_IMAGE_QCIF_ROWS)) { - user_mode = CPIA2_VP_USER_MODE_CIF; - } else { - user_mode = CPIA2_VP_USER_MODE_QCIFDS; - } - frame_rate = CPIA2_VP_FRAMERATE_30; - break; - case CPIA2_VP_SENSOR_FLAGS_500: - if ((width > STV_IMAGE_CIF_COLS) - || (height > STV_IMAGE_CIF_ROWS)) { - user_mode = CPIA2_VP_USER_MODE_VGA; - } else { - user_mode = CPIA2_VP_USER_MODE_QVGADS; - } - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - frame_rate = CPIA2_VP_FRAMERATE_15; - else - frame_rate = CPIA2_VP_FRAMERATE_30; - break; - default: - LOG("%s: Invalid sensor flag value 0x%0X\n",__func__, - cam->params.version.sensor_flags); - return -EINVAL; - } - - DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n", - cam->params.version.sensor_flags, user_mode, frame_rate); - cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE, - user_mode); - if(cam->params.vp_params.frame_rate > 0 && - frame_rate > cam->params.vp_params.frame_rate) - frame_rate = cam->params.vp_params.frame_rate; - - cpia2_set_fps(cam, frame_rate); - -// if (cam->params.pnp_id.device_type == DEVICE_STV_676) -// cpia2_do_command(cam, -// CPIA2_CMD_SET_VP_SYSTEM_CTRL, -// TRANSFER_WRITE, -// CPIA2_VP_SYSTEMCTRL_HK_CONTROL | -// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL); - - return 0; -} - -/****************************************************************************** - * - * cpia2_match_video_size - * - * return the best match, where 'best' is as always - * the largest that is not bigger than what is requested. - *****************************************************************************/ -int cpia2_match_video_size(int width, int height) -{ - if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS) - return VIDEOSIZE_VGA; - - if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS) - return VIDEOSIZE_CIF; - - if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS) - return VIDEOSIZE_QVGA; - - if (width >= 288 && height >= 216) - return VIDEOSIZE_288_216; - - if (width >= 256 && height >= 192) - return VIDEOSIZE_256_192; - - if (width >= 224 && height >= 168) - return VIDEOSIZE_224_168; - - if (width >= 192 && height >= 144) - return VIDEOSIZE_192_144; - - if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS) - return VIDEOSIZE_QCIF; - - return -1; -} - -/****************************************************************************** - * - * SetVideoSize - * - *****************************************************************************/ -static int set_vw_size(struct camera_data *cam, int size) -{ - int retval = 0; - - cam->params.vp_params.video_size = size; - - switch (size) { - case VIDEOSIZE_VGA: - DBG("Setting size to VGA\n"); - cam->params.roi.width = STV_IMAGE_VGA_COLS; - cam->params.roi.height = STV_IMAGE_VGA_ROWS; - cam->width = STV_IMAGE_VGA_COLS; - cam->height = STV_IMAGE_VGA_ROWS; - break; - case VIDEOSIZE_CIF: - DBG("Setting size to CIF\n"); - cam->params.roi.width = STV_IMAGE_CIF_COLS; - cam->params.roi.height = STV_IMAGE_CIF_ROWS; - cam->width = STV_IMAGE_CIF_COLS; - cam->height = STV_IMAGE_CIF_ROWS; - break; - case VIDEOSIZE_QVGA: - DBG("Setting size to QVGA\n"); - cam->params.roi.width = STV_IMAGE_QVGA_COLS; - cam->params.roi.height = STV_IMAGE_QVGA_ROWS; - cam->width = STV_IMAGE_QVGA_COLS; - cam->height = STV_IMAGE_QVGA_ROWS; - break; - case VIDEOSIZE_288_216: - cam->params.roi.width = 288; - cam->params.roi.height = 216; - cam->width = 288; - cam->height = 216; - break; - case VIDEOSIZE_256_192: - cam->width = 256; - cam->height = 192; - cam->params.roi.width = 256; - cam->params.roi.height = 192; - break; - case VIDEOSIZE_224_168: - cam->width = 224; - cam->height = 168; - cam->params.roi.width = 224; - cam->params.roi.height = 168; - break; - case VIDEOSIZE_192_144: - cam->width = 192; - cam->height = 144; - cam->params.roi.width = 192; - cam->params.roi.height = 144; - break; - case VIDEOSIZE_QCIF: - DBG("Setting size to QCIF\n"); - cam->params.roi.width = STV_IMAGE_QCIF_COLS; - cam->params.roi.height = STV_IMAGE_QCIF_ROWS; - cam->width = STV_IMAGE_QCIF_COLS; - cam->height = STV_IMAGE_QCIF_ROWS; - break; - default: - retval = -EINVAL; - } - return retval; -} - -/****************************************************************************** - * - * configure_sensor - * - *****************************************************************************/ -static int configure_sensor(struct camera_data *cam, - int req_width, int req_height) -{ - int retval; - - switch (cam->params.version.sensor_flags) { - case CPIA2_VP_SENSOR_FLAGS_404: - case CPIA2_VP_SENSOR_FLAGS_407: - case CPIA2_VP_SENSOR_FLAGS_409: - case CPIA2_VP_SENSOR_FLAGS_410: - retval = config_sensor_410(cam, req_width, req_height); - break; - case CPIA2_VP_SENSOR_FLAGS_500: - retval = config_sensor_500(cam, req_width, req_height); - break; - default: - return -EINVAL; - } - - return retval; -} - -/****************************************************************************** - * - * config_sensor_410 - * - *****************************************************************************/ -static int config_sensor_410(struct camera_data *cam, - int req_width, int req_height) -{ - struct cpia2_command cmd; - int i = 0; - int image_size; - int image_type; - int width = req_width; - int height = req_height; - - /*** - * Make sure size doesn't exceed CIF. - ***/ - if (width > STV_IMAGE_CIF_COLS) - width = STV_IMAGE_CIF_COLS; - if (height > STV_IMAGE_CIF_ROWS) - height = STV_IMAGE_CIF_ROWS; - - image_size = cpia2_match_video_size(width, height); - - DBG("Config 410: width = %d, height = %d\n", width, height); - DBG("Image size returned is %d\n", image_size); - if (image_size >= 0) { - set_vw_size(cam, image_size); - width = cam->params.roi.width; - height = cam->params.roi.height; - - DBG("After set_vw_size(), width = %d, height = %d\n", - width, height); - if (width <= 176 && height <= 144) { - DBG("image type = VIDEOSIZE_QCIF\n"); - image_type = VIDEOSIZE_QCIF; - } - else if (width <= 320 && height <= 240) { - DBG("image type = VIDEOSIZE_QVGA\n"); - image_type = VIDEOSIZE_QVGA; - } - else { - DBG("image type = VIDEOSIZE_CIF\n"); - image_type = VIDEOSIZE_CIF; - } - } else { - ERR("ConfigSensor410 failed\n"); - return -EINVAL; - } - - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.direction = TRANSFER_WRITE; - - /* VC Format */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; - if (image_type == VIDEOSIZE_CIF) { - cmd.buffer.registers[i++].value = - (u8) (CPIA2_VC_VC_FORMAT_UFIRST | - CPIA2_VC_VC_FORMAT_SHORTLINE); - } else { - cmd.buffer.registers[i++].value = - (u8) CPIA2_VC_VC_FORMAT_UFIRST; - } - - /* VC Clocks */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; - if (image_type == VIDEOSIZE_QCIF) { - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - cmd.buffer.registers[i++].value= - (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_672_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV2); - DBG("VC_Clocks (0xc4) should be B\n"); - } - else { - cmd.buffer.registers[i++].value= - (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_CLOCKS_LOGDIV2); - } - } else { - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - cmd.buffer.registers[i++].value = - (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_CLOCKS_LOGDIV0); - } - else { - cmd.buffer.registers[i++].value = - (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_676_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV0); - } - } - DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value); - - /* Input reqWidth from VC */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = - (u8) (STV_IMAGE_QCIF_COLS / 4); - else - cmd.buffer.registers[i++].value = - (u8) (STV_IMAGE_CIF_COLS / 4); - - /* Timings */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 208; - else - cmd.buffer.registers[i++].value = (u8) 160; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 160; - else - cmd.buffer.registers[i++].value = (u8) 64; - - /* Output Image Size */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; - cmd.buffer.registers[i++].value = cam->params.roi.width / 4; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; - cmd.buffer.registers[i++].value = cam->params.roi.height / 4; - - /* Cropping */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); - else - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); - else - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); - - /* Scaling registers (defaults) */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; - cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; - cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ - - cmd.reg_count = i; - - cpia2_send_command(cam, &cmd); - - return i; -} - - -/****************************************************************************** - * - * config_sensor_500(cam) - * - *****************************************************************************/ -static int config_sensor_500(struct camera_data *cam, - int req_width, int req_height) -{ - struct cpia2_command cmd; - int i = 0; - int image_size = VIDEOSIZE_CIF; - int image_type = VIDEOSIZE_VGA; - int width = req_width; - int height = req_height; - unsigned int device = cam->params.pnp_id.device_type; - - image_size = cpia2_match_video_size(width, height); - - if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS) - image_type = VIDEOSIZE_VGA; - else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS) - image_type = VIDEOSIZE_CIF; - else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS) - image_type = VIDEOSIZE_QVGA; - else - image_type = VIDEOSIZE_QCIF; - - if (image_size >= 0) { - set_vw_size(cam, image_size); - width = cam->params.roi.width; - height = cam->params.roi.height; - } else { - ERR("ConfigSensor500 failed\n"); - return -EINVAL; - } - - DBG("image_size = %d, width = %d, height = %d, type = %d\n", - image_size, width, height, image_type); - - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.direction = TRANSFER_WRITE; - i = 0; - - /* VC Format */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; - cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING; - i++; - - /* VC Clocks */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; - if (device == DEVICE_STV_672) { - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i].value = - (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1; - else - cmd.buffer.registers[i].value = - (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV3); - } else { - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i].value = - (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0; - else - cmd.buffer.registers[i].value = - (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV2); - } - i++; - - DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value); - - /* Input width from VP */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i].value = - (u8) (STV_IMAGE_VGA_COLS / 4); - else - cmd.buffer.registers[i].value = - (u8) (STV_IMAGE_QVGA_COLS / 4); - i++; - DBG("Input width = %d\n", cmd.buffer.registers[i-1].value); - - /* Timings */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 2; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 250; - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = (u8) 125; - else - cmd.buffer.registers[i++].value = (u8) 160; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 2; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 12; - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = (u8) 64; - else - cmd.buffer.registers[i++].value = (u8) 6; - - /* Output Image Size */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4; - else - cmd.buffer.registers[i++].value = width / 4; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4; - else - cmd.buffer.registers[i++].value = height / 4; - - /* Cropping */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2); - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2); - else if (image_type == VIDEOSIZE_CIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); - else /*if (image_type == VIDEOSIZE_QCIF)*/ - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2); - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2); - else if (image_type == VIDEOSIZE_CIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); - else /*if (image_type == VIDEOSIZE_QCIF)*/ - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); - - /* Scaling registers (defaults) */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 36; - else - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 32; - else - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 26; - else - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 21; - else - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */ - else - cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */ - else - cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ - - cmd.reg_count = i; - - cpia2_send_command(cam, &cmd); - - return i; -} - - -/****************************************************************************** - * - * setallproperties - * - * This sets all user changeable properties to the values in cam->params. - *****************************************************************************/ -static int set_all_properties(struct camera_data *cam) -{ - /** - * Don't set target_kb here, it will be set later. - * framerate and user_mode were already set (set_default_user_mode). - **/ - - cpia2_usb_change_streaming_alternate(cam, - cam->params.camera_state.stream_mode); - - cpia2_do_command(cam, - CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - TRANSFER_WRITE, cam->params.vp_params.gpio_direction); - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, - cam->params.vp_params.gpio_data); - - v4l2_ctrl_handler_setup(&cam->hdl); - - wake_system(cam); - - set_lowlight_boost(cam); - - return 0; -} - -/****************************************************************************** - * - * cpia2_save_camera_state - * - *****************************************************************************/ -void cpia2_save_camera_state(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, - 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0); - /* Don't get framerate or target_kb. Trust the values we already have */ -} - - -/****************************************************************************** - * - * cpia2_set_flicker_mode - * - *****************************************************************************/ -int cpia2_set_flicker_mode(struct camera_data *cam, int mode) -{ - unsigned char cam_reg; - int err = 0; - - if(cam->params.pnp_id.device_type != DEVICE_STV_672) - return -EINVAL; - - /* Set the appropriate bits in FLICKER_MODES, preserving the rest */ - if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, - TRANSFER_READ, 0))) - return err; - cam_reg = cam->params.flicker_control.cam_register; - - switch(mode) { - case NEVER_FLICKER: - cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; - cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; - break; - case FLICKER_60: - cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; - cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; - break; - case FLICKER_50: - cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; - cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ; - break; - default: - return -EINVAL; - } - - if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES, - TRANSFER_WRITE, cam_reg))) - return err; - - /* Set the appropriate bits in EXP_MODES, preserving the rest */ - if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES, - TRANSFER_READ, 0))) - return err; - cam_reg = cam->params.vp_params.exposure_modes; - - if (mode == NEVER_FLICKER) { - cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; - } else { - cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; - } - - if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES, - TRANSFER_WRITE, cam_reg))) - return err; - - if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, - TRANSFER_WRITE, 1))) - return err; - - switch(mode) { - case NEVER_FLICKER: - case FLICKER_60: - case FLICKER_50: - cam->params.flicker_control.flicker_mode_req = mode; - break; - default: - err = -EINVAL; - } - - return err; -} - -/****************************************************************************** - * - * cpia2_set_property_flip - * - *****************************************************************************/ -void cpia2_set_property_flip(struct camera_data *cam, int prop_val) -{ - unsigned char cam_reg; - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); - cam_reg = cam->params.vp_params.user_effects; - - if (prop_val) - { - cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP; - } - else - { - cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; - } - cam->params.vp_params.user_effects = cam_reg; - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam_reg); -} - -/****************************************************************************** - * - * cpia2_set_property_mirror - * - *****************************************************************************/ -void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) -{ - unsigned char cam_reg; - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); - cam_reg = cam->params.vp_params.user_effects; - - if (prop_val) - { - cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR; - } - else - { - cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; - } - cam->params.vp_params.user_effects = cam_reg; - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam_reg); -} - -/****************************************************************************** - * - * cpia2_set_gpio - * - *****************************************************************************/ -int cpia2_set_gpio(struct camera_data *cam, unsigned char setting) -{ - int ret; - - /* Set the microport direction (register 0x90, should be defined - * already) to 1 (user output), and set the microport data (0x91) to - * the value in the ioctl argument. - */ - - ret = cpia2_do_command(cam, - CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - CPIA2_VC_MP_DIR_OUTPUT, - 255); - if (ret < 0) - return ret; - cam->params.vp_params.gpio_direction = 255; - - ret = cpia2_do_command(cam, - CPIA2_CMD_SET_VC_MP_GPIO_DATA, - CPIA2_VC_MP_DIR_OUTPUT, - setting); - if (ret < 0) - return ret; - cam->params.vp_params.gpio_data = setting; - - return 0; -} - -/****************************************************************************** - * - * cpia2_set_fps - * - *****************************************************************************/ -int cpia2_set_fps(struct camera_data *cam, int framerate) -{ - int retval; - - switch(framerate) { - case CPIA2_VP_FRAMERATE_30: - case CPIA2_VP_FRAMERATE_25: - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags == - CPIA2_VP_SENSOR_FLAGS_500) { - return -EINVAL; - } - /* Fall through */ - case CPIA2_VP_FRAMERATE_15: - case CPIA2_VP_FRAMERATE_12_5: - case CPIA2_VP_FRAMERATE_7_5: - case CPIA2_VP_FRAMERATE_6_25: - break; - default: - return -EINVAL; - } - - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && - framerate == CPIA2_VP_FRAMERATE_15) - framerate = 0; /* Work around bug in VP4 */ - - retval = cpia2_do_command(cam, - CPIA2_CMD_FRAMERATE_REQ, - TRANSFER_WRITE, - framerate); - - if(retval == 0) - cam->params.vp_params.frame_rate = framerate; - - return retval; -} - -/****************************************************************************** - * - * cpia2_set_brightness - * - *****************************************************************************/ -void cpia2_set_brightness(struct camera_data *cam, unsigned char value) -{ - /*** - * Don't let the register be set to zero - bug in VP4 - flash of full - * brightness - ***/ - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) - value++; - DBG("Setting brightness to %d (0x%0x)\n", value, value); - cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); -} - -/****************************************************************************** - * - * cpia2_set_contrast - * - *****************************************************************************/ -void cpia2_set_contrast(struct camera_data *cam, unsigned char value) -{ - DBG("Setting contrast to %d (0x%0x)\n", value, value); - cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); -} - -/****************************************************************************** - * - * cpia2_set_saturation - * - *****************************************************************************/ -void cpia2_set_saturation(struct camera_data *cam, unsigned char value) -{ - DBG("Setting saturation to %d (0x%0x)\n", value, value); - cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); -} - -/****************************************************************************** - * - * wake_system - * - *****************************************************************************/ -static void wake_system(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0); -} - -/****************************************************************************** - * - * set_lowlight_boost - * - * Valid for STV500 sensor only - *****************************************************************************/ -static void set_lowlight_boost(struct camera_data *cam) -{ - struct cpia2_command cmd; - - if (cam->params.pnp_id.device_type != DEVICE_STV_672 || - cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500) - return; - - cmd.direction = TRANSFER_WRITE; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 3; - cmd.start = CPIA2_VP_RAM_ADDR_H; - - cmd.buffer.block_data[0] = 0; /* High byte of address to write to */ - cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */ - cmd.buffer.block_data[2] = 0; /* High byte of data to write */ - - cpia2_send_command(cam, &cmd); - - if (cam->params.vp_params.lowlight_boost) { - cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */ - } else { - cmd.buffer.block_data[0] = 0x06; - } - cmd.start = CPIA2_VP_RAM_DATA; - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - - /* Rehash the VP4 values */ - cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1); -} - -/****************************************************************************** - * - * cpia2_set_format - * - * Assumes that new size is already set in param struct. - *****************************************************************************/ -void cpia2_set_format(struct camera_data *cam) -{ - cam->flush = true; - - cpia2_usb_stream_pause(cam); - - /* reset camera to new size */ - cpia2_set_low_power(cam); - cpia2_reset_camera(cam); - cam->flush = false; - - cpia2_dbg_dump_registers(cam); - - cpia2_usb_stream_resume(cam); -} - -/****************************************************************************** - * - * cpia2_dbg_dump_registers - * - *****************************************************************************/ -void cpia2_dbg_dump_registers(struct camera_data *cam) -{ -#ifdef _CPIA2_DEBUG_ - struct cpia2_command cmd; - - if (!(debugs_on & DEBUG_DUMP_REGS)) - return; - - cmd.direction = TRANSFER_READ; - - /* Start with bank 0 (SYSTEM) */ - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 3; - cmd.start = 0; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "System Device Hi = 0x%X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "System Device Lo = 0x%X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "System_system control = 0x%X\n", - cmd.buffer.block_data[2]); - - /* Bank 1 (VC) */ - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 4; - cmd.start = 0x80; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "ASIC_ID = 0x%X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "ASIC_REV = 0x%X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "PW_CONTRL = 0x%X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "WAKEUP = 0x%X\n", - cmd.buffer.block_data[3]); - - cmd.start = 0xA0; /* ST_CTRL */ - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "Stream ctrl = 0x%X\n", - cmd.buffer.block_data[0]); - - cmd.start = 0xA4; /* Stream status */ - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "Stream status = 0x%X\n", - cmd.buffer.block_data[0]); - - cmd.start = 0xA8; /* USB status */ - cmd.reg_count = 3; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "USB_CTRL = 0x%X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "USB_STRM = 0x%X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "USB_STATUS = 0x%X\n", - cmd.buffer.block_data[2]); - - cmd.start = 0xAF; /* USB settings */ - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "USB settings = 0x%X\n", - cmd.buffer.block_data[0]); - - cmd.start = 0xC0; /* VC stuff */ - cmd.reg_count = 26; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VC Control = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VC Format = 0x%0X\n", - cmd.buffer.block_data[3]); - printk(KERN_DEBUG "VC Clocks = 0x%0X\n", - cmd.buffer.block_data[4]); - printk(KERN_DEBUG "VC IHSize = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n", - cmd.buffer.block_data[7]); - printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n", - cmd.buffer.block_data[8]); - printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n", - cmd.buffer.block_data[9]); - printk(KERN_DEBUG "VC OHSize = 0x%0X\n", - cmd.buffer.block_data[10]); - printk(KERN_DEBUG "VC OVSize = 0x%0X\n", - cmd.buffer.block_data[11]); - printk(KERN_DEBUG "VC HCrop = 0x%0X\n", - cmd.buffer.block_data[12]); - printk(KERN_DEBUG "VC VCrop = 0x%0X\n", - cmd.buffer.block_data[13]); - printk(KERN_DEBUG "VC HPhase = 0x%0X\n", - cmd.buffer.block_data[14]); - printk(KERN_DEBUG "VC VPhase = 0x%0X\n", - cmd.buffer.block_data[15]); - printk(KERN_DEBUG "VC HIspan = 0x%0X\n", - cmd.buffer.block_data[16]); - printk(KERN_DEBUG "VC VIspan = 0x%0X\n", - cmd.buffer.block_data[17]); - printk(KERN_DEBUG "VC HiCrop = 0x%0X\n", - cmd.buffer.block_data[18]); - printk(KERN_DEBUG "VC ViCrop = 0x%0X\n", - cmd.buffer.block_data[19]); - printk(KERN_DEBUG "VC HiFract = 0x%0X\n", - cmd.buffer.block_data[20]); - printk(KERN_DEBUG "VC ViFract = 0x%0X\n", - cmd.buffer.block_data[21]); - printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n", - cmd.buffer.block_data[22]); - printk(KERN_DEBUG "VC Creep Per = 0x%0X\n", - cmd.buffer.block_data[23]); - printk(KERN_DEBUG "VC User Sq. = 0x%0X\n", - cmd.buffer.block_data[24]); - printk(KERN_DEBUG "VC Target KB = 0x%0X\n", - cmd.buffer.block_data[25]); - - /*** VP ***/ - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 14; - cmd.start = 0; - cpia2_send_command(cam, &cmd); - - printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP Sys State = 0x%0X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n", - cmd.buffer.block_data[3]); - printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VP Dev Config = 0x%0X\n", - cmd.buffer.block_data[7]); - printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n", - cmd.buffer.block_data[8]); - printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n", - cmd.buffer.block_data[9]); - printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n", - cmd.buffer.block_data[10]); - printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n", - cmd.buffer.block_data[11]); - printk(KERN_DEBUG "VP RAM Data = 0x%0X\n", - cmd.buffer.block_data[12]); - printk(KERN_DEBUG "Do Call = 0x%0X\n", - cmd.buffer.block_data[13]); - - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - cmd.reg_count = 9; - cmd.start = 0x0E; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "VP Framerate = 0x%0X\n", - cmd.buffer.block_data[3]); - printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", - cmd.buffer.block_data[4]); - printk(KERN_DEBUG "VP White Bal = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VP WB thresh = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n", - cmd.buffer.block_data[7]); - printk(KERN_DEBUG "VP Exp Target = 0x%0X\n", - cmd.buffer.block_data[8]); - - cmd.reg_count = 1; - cmd.start = 0x1B; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n", - cmd.buffer.block_data[0]); - } else { - cmd.reg_count = 8 ; - cmd.start = 0x0E; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VP Framerate = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", - cmd.buffer.block_data[7]); - - cmd.reg_count = 1; - cmd.start = CPIA2_VP5_EXPOSURE_TARGET; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n", - cmd.buffer.block_data[0]); - - cmd.reg_count = 4; - cmd.start = 0x3A; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n", - cmd.buffer.block_data[3]); - } -#endif -} - -/****************************************************************************** - * - * reset_camera_struct - * - * Sets all values to the defaults - *****************************************************************************/ -static void reset_camera_struct(struct camera_data *cam) -{ - /*** - * The following parameter values are the defaults from the register map. - ***/ - cam->params.vp_params.lowlight_boost = 0; - - /* FlickerModes */ - cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; - - /* jpeg params */ - cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; - cam->params.compression.creep_period = 2; - cam->params.compression.user_squeeze = 20; - cam->params.compression.inhibit_htables = false; - - /* gpio params */ - cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */ - cam->params.vp_params.gpio_data = 0; - - /* Target kb params */ - cam->params.vc_params.quality = 100; - - /*** - * Set Sensor FPS as fast as possible. - ***/ - if(cam->params.pnp_id.device_type == DEVICE_STV_672) { - if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) - cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15; - else - cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; - } else { - cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; - } - - /*** - * Set default video mode as large as possible : - * for vga sensor set to vga, for cif sensor set to CIF. - ***/ - if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) { - cam->sensor_type = CPIA2_SENSOR_500; - cam->video_size = VIDEOSIZE_VGA; - cam->params.roi.width = STV_IMAGE_VGA_COLS; - cam->params.roi.height = STV_IMAGE_VGA_ROWS; - } else { - cam->sensor_type = CPIA2_SENSOR_410; - cam->video_size = VIDEOSIZE_CIF; - cam->params.roi.width = STV_IMAGE_CIF_COLS; - cam->params.roi.height = STV_IMAGE_CIF_ROWS; - } - - cam->width = cam->params.roi.width; - cam->height = cam->params.roi.height; -} - -/****************************************************************************** - * - * cpia2_init_camera_struct - * - * Initializes camera struct, does not call reset to fill in defaults. - *****************************************************************************/ -struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) -{ - struct camera_data *cam; - - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - - if (!cam) { - ERR("couldn't kmalloc cpia2 struct\n"); - return NULL; - } - - cam->v4l2_dev.release = cpia2_camera_release; - if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { - v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); - kfree(cam); - return NULL; - } - - mutex_init(&cam->v4l2_lock); - init_waitqueue_head(&cam->wq_stream); - - return cam; -} - -/****************************************************************************** - * - * cpia2_init_camera - * - * Initializes camera. - *****************************************************************************/ -int cpia2_init_camera(struct camera_data *cam) -{ - DBG("Start\n"); - - cam->mmapped = false; - - /* Get sensor and asic types before reset. */ - cpia2_set_high_power(cam); - cpia2_get_version_info(cam); - if (cam->params.version.asic_id != CPIA2_ASIC_672) { - ERR("Device IO error (asicID has incorrect value of 0x%X\n", - cam->params.version.asic_id); - return -ENODEV; - } - - /* Set GPIO direction and data to a safe state. */ - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - TRANSFER_WRITE, 0); - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, - TRANSFER_WRITE, 0); - - /* resetting struct requires version info for sensor and asic types */ - reset_camera_struct(cam); - - cpia2_set_low_power(cam); - - DBG("End\n"); - - return 0; -} - -/****************************************************************************** - * - * cpia2_allocate_buffers - * - *****************************************************************************/ -int cpia2_allocate_buffers(struct camera_data *cam) -{ - int i; - - if(!cam->buffers) { - u32 size = cam->num_frames*sizeof(struct framebuf); - cam->buffers = kmalloc(size, GFP_KERNEL); - if(!cam->buffers) { - ERR("couldn't kmalloc frame buffer structures\n"); - return -ENOMEM; - } - } - - if(!cam->frame_buffer) { - cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames); - if (!cam->frame_buffer) { - ERR("couldn't vmalloc frame buffer data area\n"); - kfree(cam->buffers); - cam->buffers = NULL; - return -ENOMEM; - } - } - - for(i=0; inum_frames-1; ++i) { - cam->buffers[i].next = &cam->buffers[i+1]; - cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; - cam->buffers[i].status = FRAME_EMPTY; - cam->buffers[i].length = 0; - cam->buffers[i].max_length = 0; - cam->buffers[i].num = i; - } - cam->buffers[i].next = cam->buffers; - cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; - cam->buffers[i].status = FRAME_EMPTY; - cam->buffers[i].length = 0; - cam->buffers[i].max_length = 0; - cam->buffers[i].num = i; - cam->curbuff = cam->buffers; - cam->workbuff = cam->curbuff->next; - DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff, - cam->workbuff); - return 0; -} - -/****************************************************************************** - * - * cpia2_free_buffers - * - *****************************************************************************/ -void cpia2_free_buffers(struct camera_data *cam) -{ - if(cam->buffers) { - kfree(cam->buffers); - cam->buffers = NULL; - } - if(cam->frame_buffer) { - rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames); - cam->frame_buffer = NULL; - } -} - -/****************************************************************************** - * - * cpia2_read - * - *****************************************************************************/ -long cpia2_read(struct camera_data *cam, - char __user *buf, unsigned long count, int noblock) -{ - struct framebuf *frame; - - if (!count) - return 0; - - if (!buf) { - ERR("%s: buffer NULL\n",__func__); - return -EINVAL; - } - - if (!cam) { - ERR("%s: Internal error, camera_data NULL!\n",__func__); - return -EINVAL; - } - - if (!cam->streaming) { - /* Start streaming */ - cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - } - - /* Copy cam->curbuff in case it changes while we're processing */ - frame = cam->curbuff; - if (noblock && frame->status != FRAME_READY) { - return -EAGAIN; - } - - if (frame->status != FRAME_READY) { - mutex_unlock(&cam->v4l2_lock); - wait_event_interruptible(cam->wq_stream, - !video_is_registered(&cam->vdev) || - (frame = cam->curbuff)->status == FRAME_READY); - mutex_lock(&cam->v4l2_lock); - if (signal_pending(current)) - return -ERESTARTSYS; - if (!video_is_registered(&cam->vdev)) - return 0; - } - - /* copy data to user space */ - if (frame->length > count) - return -EFAULT; - if (copy_to_user(buf, frame->data, frame->length)) - return -EFAULT; - - count = frame->length; - - frame->status = FRAME_EMPTY; - - return count; -} - -/****************************************************************************** - * - * cpia2_poll - * - *****************************************************************************/ -unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, - poll_table *wait) -{ - unsigned int status = v4l2_ctrl_poll(filp, wait); - - if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) && - !cam->streaming) { - /* Start streaming */ - cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - } - - poll_wait(filp, &cam->wq_stream, wait); - - if (cam->curbuff->status == FRAME_READY) - status |= POLLIN | POLLRDNORM; - - return status; -} - -/****************************************************************************** - * - * cpia2_remap_buffer - * - *****************************************************************************/ -int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) -{ - const char *adr = (const char *)vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long start = (unsigned long) adr; - unsigned long page, pos; - - DBG("mmap offset:%ld size:%ld\n", start_offset, size); - - if (!video_is_registered(&cam->vdev)) - return -ENODEV; - - if (size > cam->frame_size*cam->num_frames || - (start_offset % cam->frame_size) != 0 || - (start_offset+size > cam->frame_size*cam->num_frames)) - return -EINVAL; - - pos = ((unsigned long) (cam->frame_buffer)) + start_offset; - while (size > 0) { - page = kvirt_to_pa(pos); - if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - cam->mmapped = true; - return 0; -} diff --git a/drivers/media/video/cpia2/cpia2_registers.h b/drivers/media/video/cpia2/cpia2_registers.h deleted file mode 100644 index 3bbec514a967..000000000000 --- a/drivers/media/video/cpia2/cpia2_registers.h +++ /dev/null @@ -1,476 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2registers.h - * - * Copyright 2001, STMicrolectronics, Inc. - * - * Description: - * Definitions for the CPia2 register set - * - * This program is free software; 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 CPIA2_REGISTER_HEADER -#define CPIA2_REGISTER_HEADER - -/*** - * System register set (Bank 0) - ***/ -#define CPIA2_SYSTEM_DEVICE_HI 0x00 -#define CPIA2_SYSTEM_DEVICE_LO 0x01 - -#define CPIA2_SYSTEM_SYSTEM_CONTROL 0x02 -#define CPIA2_SYSTEM_CONTROL_LOW_POWER 0x00 -#define CPIA2_SYSTEM_CONTROL_HIGH_POWER 0x01 -#define CPIA2_SYSTEM_CONTROL_SUSPEND 0x02 -#define CPIA2_SYSTEM_CONTROL_V2W_ERR 0x10 -#define CPIA2_SYSTEM_CONTROL_RB_ERR 0x10 -#define CPIA2_SYSTEM_CONTROL_CLEAR_ERR 0x80 - -#define CPIA2_SYSTEM_INT_PACKET_CTRL 0x04 -#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX 0x01 -#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_EOF 0x02 -#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_INT1 0x04 - -#define CPIA2_SYSTEM_CACHE_CTRL 0x05 -#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_RESET 0x01 -#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_FLUSH 0x02 - -#define CPIA2_SYSTEM_SERIAL_CTRL 0x06 -#define CPIA2_SYSTEM_SERIAL_CTRL_NULL_CMD 0x00 -#define CPIA2_SYSTEM_SERIAL_CTRL_START_CMD 0x01 -#define CPIA2_SYSTEM_SERIAL_CTRL_STOP_CMD 0x02 -#define CPIA2_SYSTEM_SERIAL_CTRL_WRITE_CMD 0x03 -#define CPIA2_SYSTEM_SERIAL_CTRL_READ_ACK_CMD 0x04 -#define CPIA2_SYSTEM_SERIAL_CTRL_READ_NACK_CMD 0x05 - -#define CPIA2_SYSTEM_SERIAL_DATA 0x07 - -#define CPIA2_SYSTEM_VP_SERIAL_ADDR 0x08 - -/*** - * I2C addresses for various devices in CPiA2 - ***/ -#define CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR 0x20 -#define CPIA2_SYSTEM_VP_SERIAL_ADDR_VP 0x88 -#define CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP 0x8A - -#define CPIA2_SYSTEM_SPARE_REG1 0x09 -#define CPIA2_SYSTEM_SPARE_REG2 0x0A -#define CPIA2_SYSTEM_SPARE_REG3 0x0B - -#define CPIA2_SYSTEM_MC_PORT_0 0x0C -#define CPIA2_SYSTEM_MC_PORT_1 0x0D -#define CPIA2_SYSTEM_MC_PORT_2 0x0E -#define CPIA2_SYSTEM_MC_PORT_3 0x0F - -#define CPIA2_SYSTEM_STATUS_PKT 0x20 -#define CPIA2_SYSTEM_STATUS_PKT_END 0x27 - -#define CPIA2_SYSTEM_DESCRIP_VID_HI 0x30 -#define CPIA2_SYSTEM_DESCRIP_VID_LO 0x31 -#define CPIA2_SYSTEM_DESCRIP_PID_HI 0x32 -#define CPIA2_SYSTEM_DESCRIP_PID_LO 0x33 - -#define CPIA2_SYSTEM_FW_VERSION_HI 0x34 -#define CPIA2_SYSTEM_FW_VERSION_LO 0x35 - -#define CPIA2_SYSTEM_CACHE_START_INDEX 0x80 -#define CPIA2_SYSTEM_CACHE_MAX_WRITES 0x10 - -/*** - * VC register set (Bank 1) - ***/ -#define CPIA2_VC_ASIC_ID 0x80 - -#define CPIA2_VC_ASIC_REV 0x81 - -#define CPIA2_VC_PW_CTRL 0x82 -#define CPIA2_VC_PW_CTRL_COLDSTART 0x01 -#define CPIA2_VC_PW_CTRL_CP_CLK_EN 0x02 -#define CPIA2_VC_PW_CTRL_VP_RESET_N 0x04 -#define CPIA2_VC_PW_CTRL_VC_CLK_EN 0x08 -#define CPIA2_VC_PW_CTRL_VC_RESET_N 0x10 -#define CPIA2_VC_PW_CTRL_GOTO_SUSPEND 0x20 -#define CPIA2_VC_PW_CTRL_UDC_SUSPEND 0x40 -#define CPIA2_VC_PW_CTRL_PWR_DOWN 0x80 - -#define CPIA2_VC_WAKEUP 0x83 -#define CPIA2_VC_WAKEUP_SW_ENABLE 0x01 -#define CPIA2_VC_WAKEUP_XX_ENABLE 0x02 -#define CPIA2_VC_WAKEUP_SW_ATWAKEUP 0x04 -#define CPIA2_VC_WAKEUP_XX_ATWAKEUP 0x08 - -#define CPIA2_VC_CLOCK_CTRL 0x84 -#define CPIA2_VC_CLOCK_CTRL_TESTUP72 0x01 - -#define CPIA2_VC_INT_ENABLE 0x88 -#define CPIA2_VC_INT_ENABLE_XX_IE 0x01 -#define CPIA2_VC_INT_ENABLE_SW_IE 0x02 -#define CPIA2_VC_INT_ENABLE_VC_IE 0x04 -#define CPIA2_VC_INT_ENABLE_USBDATA_IE 0x08 -#define CPIA2_VC_INT_ENABLE_USBSETUP_IE 0x10 -#define CPIA2_VC_INT_ENABLE_USBCFG_IE 0x20 - -#define CPIA2_VC_INT_FLAG 0x89 -#define CPIA2_VC_INT_ENABLE_XX_FLAG 0x01 -#define CPIA2_VC_INT_ENABLE_SW_FLAG 0x02 -#define CPIA2_VC_INT_ENABLE_VC_FLAG 0x04 -#define CPIA2_VC_INT_ENABLE_USBDATA_FLAG 0x08 -#define CPIA2_VC_INT_ENABLE_USBSETUP_FLAG 0x10 -#define CPIA2_VC_INT_ENABLE_USBCFG_FLAG 0x20 -#define CPIA2_VC_INT_ENABLE_SET_RESET_BIT 0x80 - -#define CPIA2_VC_INT_STATE 0x8A -#define CPIA2_VC_INT_STATE_XX_STATE 0x01 -#define CPIA2_VC_INT_STATE_SW_STATE 0x02 - -#define CPIA2_VC_MP_DIR 0x90 -#define CPIA2_VC_MP_DIR_INPUT 0x00 -#define CPIA2_VC_MP_DIR_OUTPUT 0x01 - -#define CPIA2_VC_MP_DATA 0x91 - -#define CPIA2_VC_DP_CTRL 0x98 -#define CPIA2_VC_DP_CTRL_MODE_0 0x00 -#define CPIA2_VC_DP_CTRL_MODE_A 0x01 -#define CPIA2_VC_DP_CTRL_MODE_B 0x02 -#define CPIA2_VC_DP_CTRL_MODE_C 0x03 -#define CPIA2_VC_DP_CTRL_FAKE_FST 0x04 - -#define CPIA2_VC_AD_CTRL 0x99 -#define CPIA2_VC_AD_CTRL_SRC_0 0x00 -#define CPIA2_VC_AD_CTRL_SRC_DIGI_A 0x01 -#define CPIA2_VC_AD_CTRL_SRC_REG 0x02 -#define CPIA2_VC_AD_CTRL_DST_USB 0x00 -#define CPIA2_VC_AD_CTRL_DST_REG 0x04 - -#define CPIA2_VC_AD_TEST_IN 0x9B - -#define CPIA2_VC_AD_TEST_OUT 0x9C - -#define CPIA2_VC_AD_STATUS 0x9D -#define CPIA2_VC_AD_STATUS_EMPTY 0x01 -#define CPIA2_VC_AD_STATUS_FULL 0x02 - -#define CPIA2_VC_DP_DATA 0x9E - -#define CPIA2_VC_ST_CTRL 0xA0 -#define CPIA2_VC_ST_CTRL_SRC_VC 0x00 -#define CPIA2_VC_ST_CTRL_SRC_DP 0x01 -#define CPIA2_VC_ST_CTRL_SRC_REG 0x02 - -#define CPIA2_VC_ST_CTRL_RAW_SELECT 0x04 - -#define CPIA2_VC_ST_CTRL_DST_USB 0x00 -#define CPIA2_VC_ST_CTRL_DST_DP 0x08 -#define CPIA2_VC_ST_CTRL_DST_REG 0x10 - -#define CPIA2_VC_ST_CTRL_FIFO_ENABLE 0x20 -#define CPIA2_VC_ST_CTRL_EOF_DETECT 0x40 - -#define CPIA2_VC_ST_TEST 0xA1 -#define CPIA2_VC_ST_TEST_MODE_MANUAL 0x00 -#define CPIA2_VC_ST_TEST_MODE_INCREMENT 0x02 - -#define CPIA2_VC_ST_TEST_AUTO_FILL 0x08 - -#define CPIA2_VC_ST_TEST_REPEAT_FIFO 0x10 - -#define CPIA2_VC_ST_TEST_IN 0xA2 - -#define CPIA2_VC_ST_TEST_OUT 0xA3 - -#define CPIA2_VC_ST_STATUS 0xA4 -#define CPIA2_VC_ST_STATUS_EMPTY 0x01 -#define CPIA2_VC_ST_STATUS_FULL 0x02 - -#define CPIA2_VC_ST_FRAME_DETECT_1 0xA5 - -#define CPIA2_VC_ST_FRAME_DETECT_2 0xA6 - -#define CPIA2_VC_USB_CTRL 0xA8 -#define CPIA2_VC_USB_CTRL_CMD_STALLED 0x01 -#define CPIA2_VC_USB_CTRL_CMD_READY 0x02 -#define CPIA2_VC_USB_CTRL_CMD_STATUS 0x04 -#define CPIA2_VC_USB_CTRL_CMD_STATUS_DIR 0x08 -#define CPIA2_VC_USB_CTRL_CMD_NO_CLASH 0x10 -#define CPIA2_VC_USB_CTRL_CMD_MICRO_ACCESS 0x80 - -#define CPIA2_VC_USB_STRM 0xA9 -#define CPIA2_VC_USB_STRM_ISO_ENABLE 0x01 -#define CPIA2_VC_USB_STRM_BLK_ENABLE 0x02 -#define CPIA2_VC_USB_STRM_INT_ENABLE 0x04 -#define CPIA2_VC_USB_STRM_AUD_ENABLE 0x08 - -#define CPIA2_VC_USB_STATUS 0xAA -#define CPIA2_VC_USB_STATUS_CMD_IN_PROGRESS 0x01 -#define CPIA2_VC_USB_STATUS_CMD_STATUS_STALL 0x02 -#define CPIA2_VC_USB_STATUS_CMD_HANDSHAKE 0x04 -#define CPIA2_VC_USB_STATUS_CMD_OVERRIDE 0x08 -#define CPIA2_VC_USB_STATUS_CMD_FIFO_BUSY 0x10 -#define CPIA2_VC_USB_STATUS_BULK_REPEAT_TXN 0x20 -#define CPIA2_VC_USB_STATUS_CONFIG_DONE 0x40 -#define CPIA2_VC_USB_STATUS_USB_SUSPEND 0x80 - -#define CPIA2_VC_USB_CMDW 0xAB - -#define CPIA2_VC_USB_DATARW 0xAC - -#define CPIA2_VC_USB_INFO 0xAD - -#define CPIA2_VC_USB_CONFIG 0xAE - -#define CPIA2_VC_USB_SETTINGS 0xAF -#define CPIA2_VC_USB_SETTINGS_CONFIG_MASK 0x03 -#define CPIA2_VC_USB_SETTINGS_INTERFACE_MASK 0x0C -#define CPIA2_VC_USB_SETTINGS_ALTERNATE_MASK 0x70 - -#define CPIA2_VC_USB_ISOLIM 0xB0 - -#define CPIA2_VC_USB_ISOFAILS 0xB1 - -#define CPIA2_VC_USB_ISOMAXPKTHI 0xB2 - -#define CPIA2_VC_USB_ISOMAXPKTLO 0xB3 - -#define CPIA2_VC_V2W_CTRL 0xB8 -#define CPIA2_VC_V2W_SELECT 0x01 - -#define CPIA2_VC_V2W_SCL 0xB9 - -#define CPIA2_VC_V2W_SDA 0xBA - -#define CPIA2_VC_VC_CTRL 0xC0 -#define CPIA2_VC_VC_CTRL_RUN 0x01 -#define CPIA2_VC_VC_CTRL_SINGLESHOT 0x02 -#define CPIA2_VC_VC_CTRL_IDLING 0x04 -#define CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES 0x10 -#define CPIA2_VC_VC_CTRL_INHIBIT_Q_TABLES 0x20 -#define CPIA2_VC_VC_CTRL_INHIBIT_PRIVATE 0x40 - -#define CPIA2_VC_VC_RESTART_IVAL_HI 0xC1 - -#define CPIA2_VC_VC_RESTART_IVAL_LO 0xC2 - -#define CPIA2_VC_VC_FORMAT 0xC3 -#define CPIA2_VC_VC_FORMAT_UFIRST 0x01 -#define CPIA2_VC_VC_FORMAT_MONO 0x02 -#define CPIA2_VC_VC_FORMAT_DECIMATING 0x04 -#define CPIA2_VC_VC_FORMAT_SHORTLINE 0x08 -#define CPIA2_VC_VC_FORMAT_SELFTEST 0x10 - -#define CPIA2_VC_VC_CLOCKS 0xC4 -#define CPIA2_VC_VC_CLOCKS_CLKDIV_MASK 0x03 -#define CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 0x04 -#define CPIA2_VC_VC_672_CLOCKS_SCALING 0x08 -#define CPIA2_VC_VC_CLOCKS_LOGDIV0 0x00 -#define CPIA2_VC_VC_CLOCKS_LOGDIV1 0x01 -#define CPIA2_VC_VC_CLOCKS_LOGDIV2 0x02 -#define CPIA2_VC_VC_CLOCKS_LOGDIV3 0x03 -#define CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 0x08 -#define CPIA2_VC_VC_676_CLOCKS_SCALING 0x10 - -#define CPIA2_VC_VC_IHSIZE_LO 0xC5 - -#define CPIA2_VC_VC_XLIM_HI 0xC6 - -#define CPIA2_VC_VC_XLIM_LO 0xC7 - -#define CPIA2_VC_VC_YLIM_HI 0xC8 - -#define CPIA2_VC_VC_YLIM_LO 0xC9 - -#define CPIA2_VC_VC_OHSIZE 0xCA - -#define CPIA2_VC_VC_OVSIZE 0xCB - -#define CPIA2_VC_VC_HCROP 0xCC - -#define CPIA2_VC_VC_VCROP 0xCD - -#define CPIA2_VC_VC_HPHASE 0xCE - -#define CPIA2_VC_VC_VPHASE 0xCF - -#define CPIA2_VC_VC_HISPAN 0xD0 - -#define CPIA2_VC_VC_VISPAN 0xD1 - -#define CPIA2_VC_VC_HICROP 0xD2 - -#define CPIA2_VC_VC_VICROP 0xD3 - -#define CPIA2_VC_VC_HFRACT 0xD4 -#define CPIA2_VC_VC_HFRACT_DEN_MASK 0x0F -#define CPIA2_VC_VC_HFRACT_NUM_MASK 0xF0 - -#define CPIA2_VC_VC_VFRACT 0xD5 -#define CPIA2_VC_VC_VFRACT_DEN_MASK 0x0F -#define CPIA2_VC_VC_VFRACT_NUM_MASK 0xF0 - -#define CPIA2_VC_VC_JPEG_OPT 0xD6 -#define CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE 0x01 -#define CPIA2_VC_VC_JPEG_OPT_NO_DC_AUTO_SQUEEZE 0x02 -#define CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE 0x04 -#define CPIA2_VC_VC_JPEG_OPT_DEFAULT (CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE|\ - CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE) - - -#define CPIA2_VC_VC_CREEP_PERIOD 0xD7 -#define CPIA2_VC_VC_USER_SQUEEZE 0xD8 -#define CPIA2_VC_VC_TARGET_KB 0xD9 - -#define CPIA2_VC_VC_AUTO_SQUEEZE 0xE6 - - -/*** - * VP register set (Bank 2) - ***/ -#define CPIA2_VP_DEVICEH 0 -#define CPIA2_VP_DEVICEL 1 - -#define CPIA2_VP_SYSTEMSTATE 0x02 -#define CPIA2_VP_SYSTEMSTATE_HK_ALIVE 0x01 - -#define CPIA2_VP_SYSTEMCTRL 0x03 -#define CPIA2_VP_SYSTEMCTRL_REQ_CLEAR_ERROR 0x80 -#define CPIA2_VP_SYSTEMCTRL_POWER_DOWN_PLL 0x20 -#define CPIA2_VP_SYSTEMCTRL_REQ_SUSPEND_STATE 0x10 -#define CPIA2_VP_SYSTEMCTRL_REQ_SERIAL_WAKEUP 0x08 -#define CPIA2_VP_SYSTEMCTRL_REQ_AUTOLOAD 0x04 -#define CPIA2_VP_SYSTEMCTRL_HK_CONTROL 0x02 -#define CPIA2_VP_SYSTEMCTRL_POWER_CONTROL 0x01 - -#define CPIA2_VP_SENSOR_FLAGS 0x05 -#define CPIA2_VP_SENSOR_FLAGS_404 0x01 -#define CPIA2_VP_SENSOR_FLAGS_407 0x02 -#define CPIA2_VP_SENSOR_FLAGS_409 0x04 -#define CPIA2_VP_SENSOR_FLAGS_410 0x08 -#define CPIA2_VP_SENSOR_FLAGS_500 0x10 - -#define CPIA2_VP_SENSOR_REV 0x06 - -#define CPIA2_VP_DEVICE_CONFIG 0x07 -#define CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE 0x01 - -#define CPIA2_VP_GPIO_DIRECTION 0x08 -#define CPIA2_VP_GPIO_READ 0xFF -#define CPIA2_VP_GPIO_WRITE 0x00 - -#define CPIA2_VP_GPIO_DATA 0x09 - -#define CPIA2_VP_RAM_ADDR_H 0x0A -#define CPIA2_VP_RAM_ADDR_L 0x0B -#define CPIA2_VP_RAM_DATA 0x0C - -#define CPIA2_VP_PATCH_REV 0x0F - -#define CPIA2_VP4_USER_MODE 0x10 -#define CPIA2_VP5_USER_MODE 0x13 -#define CPIA2_VP_USER_MODE_CIF 0x01 -#define CPIA2_VP_USER_MODE_QCIFDS 0x02 -#define CPIA2_VP_USER_MODE_QCIFPTC 0x04 -#define CPIA2_VP_USER_MODE_QVGADS 0x08 -#define CPIA2_VP_USER_MODE_QVGAPTC 0x10 -#define CPIA2_VP_USER_MODE_VGA 0x20 - -#define CPIA2_VP4_FRAMERATE_REQUEST 0x11 -#define CPIA2_VP5_FRAMERATE_REQUEST 0x14 -#define CPIA2_VP_FRAMERATE_60 0x80 -#define CPIA2_VP_FRAMERATE_50 0x40 -#define CPIA2_VP_FRAMERATE_30 0x20 -#define CPIA2_VP_FRAMERATE_25 0x10 -#define CPIA2_VP_FRAMERATE_15 0x08 -#define CPIA2_VP_FRAMERATE_12_5 0x04 -#define CPIA2_VP_FRAMERATE_7_5 0x02 -#define CPIA2_VP_FRAMERATE_6_25 0x01 - -#define CPIA2_VP4_USER_EFFECTS 0x12 -#define CPIA2_VP5_USER_EFFECTS 0x15 -#define CPIA2_VP_USER_EFFECTS_COLBARS 0x01 -#define CPIA2_VP_USER_EFFECTS_COLBARS_GRAD 0x02 -#define CPIA2_VP_USER_EFFECTS_MIRROR 0x04 -#define CPIA2_VP_USER_EFFECTS_FLIP 0x40 // VP5 only - -/* NOTE: CPIA2_VP_EXPOSURE_MODES shares the same register as VP5 User - * Effects */ -#define CPIA2_VP_EXPOSURE_MODES 0x15 -#define CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER 0x20 -#define CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP 0x10 - -#define CPIA2_VP4_EXPOSURE_TARGET 0x16 // VP4 -#define CPIA2_VP5_EXPOSURE_TARGET 0x20 // VP5 - -#define CPIA2_VP_FLICKER_MODES 0x1B -#define CPIA2_VP_FLICKER_MODES_50HZ 0x80 -#define CPIA2_VP_FLICKER_MODES_CUSTOM_FLT_FFREQ 0x40 -#define CPIA2_VP_FLICKER_MODES_NEVER_FLICKER 0x20 -#define CPIA2_VP_FLICKER_MODES_INHIBIT_RUB 0x10 -#define CPIA2_VP_FLICKER_MODES_ADJUST_LINE_FREQ 0x08 -#define CPIA2_VP_FLICKER_MODES_CUSTOM_INT_FFREQ 0x04 - -#define CPIA2_VP_UMISC 0x1D -#define CPIA2_VP_UMISC_FORCE_MONO 0x80 -#define CPIA2_VP_UMISC_FORCE_ID_MASK 0x40 -#define CPIA2_VP_UMISC_INHIBIT_AUTO_FGS 0x20 -#define CPIA2_VP_UMISC_INHIBIT_AUTO_DIMS 0x08 -#define CPIA2_VP_UMISC_OPT_FOR_SENSOR_DS 0x04 -#define CPIA2_VP_UMISC_INHIBIT_AUTO_MODE_INT 0x02 - -#define CPIA2_VP5_ANTIFLKRSETUP 0x22 //34 - -#define CPIA2_VP_INTERPOLATION 0x24 -#define CPIA2_VP_INTERPOLATION_EVEN_FIRST 0x40 -#define CPIA2_VP_INTERPOLATION_HJOG 0x20 -#define CPIA2_VP_INTERPOLATION_VJOG 0x10 - -#define CPIA2_VP_GAMMA 0x25 -#define CPIA2_VP_DEFAULT_GAMMA 0x10 - -#define CPIA2_VP_YRANGE 0x26 - -#define CPIA2_VP_SATURATION 0x27 - -#define CPIA2_VP5_MYBLACK_LEVEL 0x3A //58 -#define CPIA2_VP5_MCYRANGE 0x3B //59 -#define CPIA2_VP5_MYCEILING 0x3C //60 -#define CPIA2_VP5_MCUVSATURATION 0x3D //61 - - -#define CPIA2_VP_REHASH_VALUES 0x60 - - -/*** - * Common sensor registers - ***/ -#define CPIA2_SENSOR_DEVICE_H 0x00 -#define CPIA2_SENSOR_DEVICE_L 0x01 - -#define CPIA2_SENSOR_DATA_FORMAT 0x16 -#define CPIA2_SENSOR_DATA_FORMAT_HMIRROR 0x08 -#define CPIA2_SENSOR_DATA_FORMAT_VMIRROR 0x10 - -#define CPIA2_SENSOR_CR1 0x76 -#define CPIA2_SENSOR_CR1_STAND_BY 0x01 -#define CPIA2_SENSOR_CR1_DOWN_RAMP_GEN 0x02 -#define CPIA2_SENSOR_CR1_DOWN_COLUMN_ADC 0x04 -#define CPIA2_SENSOR_CR1_DOWN_CAB_REGULATOR 0x08 -#define CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR 0x10 -#define CPIA2_SENSOR_CR1_DOWN_VRT_AMP 0x20 -#define CPIA2_SENSOR_CR1_DOWN_BAND_GAP 0x40 - -#endif diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c deleted file mode 100644 index 95b5d6e7cdc4..000000000000 --- a/drivers/media/video/cpia2/cpia2_usb.c +++ /dev/null @@ -1,955 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2_usb.c - * - * Copyright 2001, STMicrolectronics, Inc. - * Contact: steve.miller@st.com - * - * Description: - * This is a USB driver for CPia2 based video cameras. - * The infrastructure of this driver is based on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - * This program is free software; 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. - * - * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox - ****************************************************************************/ - -#include -#include -#include -#include - -#include "cpia2.h" - -static int frame_sizes[] = { - 0, // USBIF_CMDONLY - 0, // USBIF_BULK - 128, // USBIF_ISO_1 - 384, // USBIF_ISO_2 - 640, // USBIF_ISO_3 - 768, // USBIF_ISO_4 - 896, // USBIF_ISO_5 - 1023, // USBIF_ISO_6 -}; - -#define FRAMES_PER_DESC 10 -#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt] - -static void process_frame(struct camera_data *cam); -static void cpia2_usb_complete(struct urb *urb); -static int cpia2_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void cpia2_usb_disconnect(struct usb_interface *intf); -static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); -static int cpia2_usb_resume(struct usb_interface *intf); - -static void free_sbufs(struct camera_data *cam); -static void add_APPn(struct camera_data *cam); -static void add_COM(struct camera_data *cam); -static int submit_urbs(struct camera_data *cam); -static int set_alternate(struct camera_data *cam, unsigned int alt); -static int configure_transfer_mode(struct camera_data *cam, unsigned int alt); - -static struct usb_device_id cpia2_id_table[] = { - {USB_DEVICE(0x0553, 0x0100)}, - {USB_DEVICE(0x0553, 0x0140)}, - {USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */ - {} /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, cpia2_id_table); - -static struct usb_driver cpia2_driver = { - .name = "cpia2", - .probe = cpia2_usb_probe, - .disconnect = cpia2_usb_disconnect, - .suspend = cpia2_usb_suspend, - .resume = cpia2_usb_resume, - .reset_resume = cpia2_usb_resume, - .id_table = cpia2_id_table -}; - - -/****************************************************************************** - * - * process_frame - * - *****************************************************************************/ -static void process_frame(struct camera_data *cam) -{ - static int frame_count; - - unsigned char *inbuff = cam->workbuff->data; - - DBG("Processing frame #%d, current:%d\n", - cam->workbuff->num, cam->curbuff->num); - - if(cam->workbuff->length > cam->workbuff->max_length) - cam->workbuff->max_length = cam->workbuff->length; - - if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) { - frame_count++; - } else { - cam->workbuff->status = FRAME_ERROR; - DBG("Start of frame not found\n"); - return; - } - - /*** - * Now the output buffer should have a JPEG image in it. - ***/ - if(!cam->first_image_seen) { - /* Always skip the first image after streaming - * starts. It is almost certainly corrupt. */ - cam->first_image_seen = 1; - cam->workbuff->status = FRAME_EMPTY; - return; - } - if (cam->workbuff->length > 3) { - if(cam->mmapped && - cam->workbuff->length < cam->workbuff->max_length) { - /* No junk in the buffers */ - memset(cam->workbuff->data+cam->workbuff->length, - 0, cam->workbuff->max_length- - cam->workbuff->length); - } - cam->workbuff->max_length = cam->workbuff->length; - cam->workbuff->status = FRAME_READY; - - if(!cam->mmapped && cam->num_frames > 2) { - /* During normal reading, the most recent - * frame will be read. If the current frame - * hasn't started reading yet, it will never - * be read, so mark it empty. If the buffer is - * mmapped, or we have few buffers, we need to - * wait for the user to free the buffer. - * - * NOTE: This is not entirely foolproof with 3 - * buffers, but it would take an EXTREMELY - * overloaded system to cause problems (possible - * image data corruption). Basically, it would - * need to take more time to execute cpia2_read - * than it would for the camera to send - * cam->num_frames-2 frames before problems - * could occur. - */ - cam->curbuff->status = FRAME_EMPTY; - } - cam->curbuff = cam->workbuff; - cam->workbuff = cam->workbuff->next; - DBG("Changed buffers, work:%d, current:%d\n", - cam->workbuff->num, cam->curbuff->num); - return; - } else { - DBG("Not enough data for an image.\n"); - } - - cam->workbuff->status = FRAME_ERROR; - return; -} - -/****************************************************************************** - * - * add_APPn - * - * Adds a user specified APPn record - *****************************************************************************/ -static void add_APPn(struct camera_data *cam) -{ - if(cam->APP_len > 0) { - cam->workbuff->data[cam->workbuff->length++] = 0xFF; - cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn; - cam->workbuff->data[cam->workbuff->length++] = 0; - cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2; - memcpy(cam->workbuff->data+cam->workbuff->length, - cam->APP_data, cam->APP_len); - cam->workbuff->length += cam->APP_len; - } -} - -/****************************************************************************** - * - * add_COM - * - * Adds a user specified COM record - *****************************************************************************/ -static void add_COM(struct camera_data *cam) -{ - if(cam->COM_len > 0) { - cam->workbuff->data[cam->workbuff->length++] = 0xFF; - cam->workbuff->data[cam->workbuff->length++] = 0xFE; - cam->workbuff->data[cam->workbuff->length++] = 0; - cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2; - memcpy(cam->workbuff->data+cam->workbuff->length, - cam->COM_data, cam->COM_len); - cam->workbuff->length += cam->COM_len; - } -} - -/****************************************************************************** - * - * cpia2_usb_complete - * - * callback when incoming packet is received - *****************************************************************************/ -static void cpia2_usb_complete(struct urb *urb) -{ - int i; - unsigned char *cdata; - static int frame_ready = false; - struct camera_data *cam = (struct camera_data *) urb->context; - - if (urb->status!=0) { - if (!(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) - { - DBG("urb->status = %d!\n", urb->status); - } - DBG("Stopping streaming\n"); - return; - } - - if (!cam->streaming || !video_is_registered(&cam->vdev)) { - LOG("Will now stop the streaming: streaming = %d, present=%d\n", - cam->streaming, video_is_registered(&cam->vdev)); - return; - } - - /*** - * Packet collater - ***/ - //DBG("Collating %d packets\n", urb->number_of_packets); - for (i = 0; i < urb->number_of_packets; i++) { - u16 checksum, iso_checksum; - int j; - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - if(cam->workbuff->status == FRAME_READY) { - struct framebuf *ptr; - /* Try to find an available buffer */ - DBG("workbuff full, searching\n"); - for (ptr = cam->workbuff->next; - ptr != cam->workbuff; - ptr = ptr->next) - { - if (ptr->status == FRAME_EMPTY) { - ptr->status = FRAME_READING; - ptr->length = 0; - break; - } - } - if (ptr == cam->workbuff) - break; /* No READING or EMPTY buffers left */ - - cam->workbuff = ptr; - } - - if (cam->workbuff->status == FRAME_EMPTY || - cam->workbuff->status == FRAME_ERROR) { - cam->workbuff->status = FRAME_READING; - cam->workbuff->length = 0; - } - - //DBG(" Packet %d length = %d, status = %d\n", i, n, st); - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (st) { - LOG("cpia2 data error: [%d] len=%d, status = %d\n", - i, n, st); - if(!ALLOW_CORRUPT) - cam->workbuff->status = FRAME_ERROR; - continue; - } - - if(n<=2) - continue; - - checksum = 0; - for(j=0; jworkbuff->status = FRAME_ERROR; - continue; - } - } - n -= 2; - - if(cam->workbuff->status != FRAME_READING) { - if((0xFF == cdata[0] && 0xD8 == cdata[1]) || - (0xD8 == cdata[0] && 0xFF == cdata[1] && - 0 != cdata[2])) { - /* frame is skipped, but increment total - * frame count anyway */ - cam->frame_count++; - } - DBG("workbuff not reading, status=%d\n", - cam->workbuff->status); - continue; - } - - if (cam->frame_size < cam->workbuff->length + n) { - ERR("buffer overflow! length: %d, n: %d\n", - cam->workbuff->length, n); - cam->workbuff->status = FRAME_ERROR; - if(cam->workbuff->length > cam->workbuff->max_length) - cam->workbuff->max_length = - cam->workbuff->length; - continue; - } - - if (cam->workbuff->length == 0) { - int data_offset; - if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) { - data_offset = 1; - } else if((0xFF == cdata[0]) && (0xD8 == cdata[1]) - && (0xFF == cdata[2])) { - data_offset = 2; - } else { - DBG("Ignoring packet, not beginning!\n"); - continue; - } - DBG("Start of frame pattern found\n"); - do_gettimeofday(&cam->workbuff->timestamp); - cam->workbuff->seq = cam->frame_count++; - cam->workbuff->data[0] = 0xFF; - cam->workbuff->data[1] = 0xD8; - cam->workbuff->length = 2; - add_APPn(cam); - add_COM(cam); - memcpy(cam->workbuff->data+cam->workbuff->length, - cdata+data_offset, n-data_offset); - cam->workbuff->length += n-data_offset; - } else if (cam->workbuff->length > 0) { - memcpy(cam->workbuff->data + cam->workbuff->length, - cdata, n); - cam->workbuff->length += n; - } - - if ((cam->workbuff->length >= 3) && - (cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) && - (cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) && - (cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) { - frame_ready = true; - cam->workbuff->data[cam->workbuff->length - 1] = 0; - cam->workbuff->length -= 1; - } else if ((cam->workbuff->length >= 2) && - (cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) && - (cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) { - frame_ready = true; - } - - if (frame_ready) { - DBG("Workbuff image size = %d\n",cam->workbuff->length); - process_frame(cam); - - frame_ready = false; - - if (waitqueue_active(&cam->wq_stream)) - wake_up_interruptible(&cam->wq_stream); - } - } - - if(cam->streaming) { - /* resubmit */ - urb->dev = cam->dev; - if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - ERR("%s: usb_submit_urb ret %d!\n", __func__, i); - } -} - -/****************************************************************************** - * - * configure_transfer_mode - * - *****************************************************************************/ -static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) -{ - static unsigned char iso_regs[8][4] = { - {0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00}, - {0xB9, 0x00, 0x00, 0x7E}, - {0xB9, 0x00, 0x01, 0x7E}, - {0xB9, 0x00, 0x02, 0x7E}, - {0xB9, 0x00, 0x02, 0xFE}, - {0xB9, 0x00, 0x03, 0x7E}, - {0xB9, 0x00, 0x03, 0xFD} - }; - struct cpia2_command cmd; - unsigned char reg; - - if (!video_is_registered(&cam->vdev)) - return -ENODEV; - - /*** - * Write the isoc registers according to the alternate selected - ***/ - cmd.direction = TRANSFER_WRITE; - cmd.buffer.block_data[0] = iso_regs[alt][0]; - cmd.buffer.block_data[1] = iso_regs[alt][1]; - cmd.buffer.block_data[2] = iso_regs[alt][2]; - cmd.buffer.block_data[3] = iso_regs[alt][3]; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.start = CPIA2_VC_USB_ISOLIM; - cmd.reg_count = 4; - cpia2_send_command(cam, &cmd); - - /*** - * Enable relevant streams before starting polling. - * First read USB Stream Config Register. - ***/ - cmd.direction = TRANSFER_READ; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.start = CPIA2_VC_USB_STRM; - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - reg = cmd.buffer.block_data[0]; - - /* Clear iso, bulk, and int */ - reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE | - CPIA2_VC_USB_STRM_ISO_ENABLE | - CPIA2_VC_USB_STRM_INT_ENABLE); - - if (alt == USBIF_BULK) { - DBG("Enabling bulk xfer\n"); - reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */ - cam->xfer_mode = XFER_BULK; - } else if (alt >= USBIF_ISO_1) { - DBG("Enabling ISOC xfer\n"); - reg |= CPIA2_VC_USB_STRM_ISO_ENABLE; - cam->xfer_mode = XFER_ISOC; - } - - cmd.buffer.block_data[0] = reg; - cmd.direction = TRANSFER_WRITE; - cmd.start = CPIA2_VC_USB_STRM; - cmd.reg_count = 1; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cpia2_send_command(cam, &cmd); - - return 0; -} - -/****************************************************************************** - * - * cpia2_usb_change_streaming_alternate - * - *****************************************************************************/ -int cpia2_usb_change_streaming_alternate(struct camera_data *cam, - unsigned int alt) -{ - int ret = 0; - - if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6) - return -EINVAL; - - if(alt == cam->params.camera_state.stream_mode) - return 0; - - cpia2_usb_stream_pause(cam); - - configure_transfer_mode(cam, alt); - - cam->params.camera_state.stream_mode = alt; - - /* Reset the camera to prevent image quality degradation */ - cpia2_reset_camera(cam); - - cpia2_usb_stream_resume(cam); - - return ret; -} - -/****************************************************************************** - * - * set_alternate - * - *****************************************************************************/ -static int set_alternate(struct camera_data *cam, unsigned int alt) -{ - int ret = 0; - - if(alt == cam->cur_alt) - return 0; - - if (cam->cur_alt != USBIF_CMDONLY) { - DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY); - ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY); - if (ret != 0) - return ret; - } - if (alt != USBIF_CMDONLY) { - DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt); - ret = usb_set_interface(cam->dev, cam->iface, alt); - if (ret != 0) - return ret; - } - - cam->old_alt = cam->cur_alt; - cam->cur_alt = alt; - - return ret; -} - -/****************************************************************************** - * - * free_sbufs - * - * Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL - * are assumed to be allocated. Non-NULL .urb members are also assumed to be - * submitted (and must therefore be killed before they are freed). - *****************************************************************************/ -static void free_sbufs(struct camera_data *cam) -{ - int i; - - for (i = 0; i < NUM_SBUF; i++) { - if(cam->sbuf[i].urb) { - usb_kill_urb(cam->sbuf[i].urb); - usb_free_urb(cam->sbuf[i].urb); - cam->sbuf[i].urb = NULL; - } - if(cam->sbuf[i].data) { - kfree(cam->sbuf[i].data); - cam->sbuf[i].data = NULL; - } - } -} - -/******* -* Convenience functions -*******/ -/**************************************************************************** - * - * write_packet - * - ***************************************************************************/ -static int write_packet(struct usb_device *udev, - u8 request, u8 * registers, u16 start, size_t size) -{ - if (!registers || size <= 0) - return -EINVAL; - - return usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - start, /* value */ - 0, /* index */ - registers, /* buffer */ - size, - HZ); -} - -/**************************************************************************** - * - * read_packet - * - ***************************************************************************/ -static int read_packet(struct usb_device *udev, - u8 request, u8 * registers, u16 start, size_t size) -{ - if (!registers || size <= 0) - return -EINVAL; - - return usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - request, - USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE, - start, /* value */ - 0, /* index */ - registers, /* buffer */ - size, - HZ); -} - -/****************************************************************************** - * - * cpia2_usb_transfer_cmd - * - *****************************************************************************/ -int cpia2_usb_transfer_cmd(struct camera_data *cam, - void *registers, - u8 request, u8 start, u8 count, u8 direction) -{ - int err = 0; - struct usb_device *udev = cam->dev; - - if (!udev) { - ERR("%s: Internal driver error: udev is NULL\n", __func__); - return -EINVAL; - } - - if (!registers) { - ERR("%s: Internal driver error: register array is NULL\n", __func__); - return -EINVAL; - } - - if (direction == TRANSFER_READ) { - err = read_packet(udev, request, (u8 *)registers, start, count); - if (err > 0) - err = 0; - } else if (direction == TRANSFER_WRITE) { - err =write_packet(udev, request, (u8 *)registers, start, count); - if (err < 0) { - LOG("Control message failed, err val = %d\n", err); - LOG("Message: request = 0x%0X, start = 0x%0X\n", - request, start); - LOG("Message: count = %d, register[0] = 0x%0X\n", - count, ((unsigned char *) registers)[0]); - } else - err=0; - } else { - LOG("Unexpected first byte of direction: %d\n", - direction); - return -EINVAL; - } - - if(err != 0) - LOG("Unexpected error: %d\n", err); - return err; -} - - -/****************************************************************************** - * - * submit_urbs - * - *****************************************************************************/ -static int submit_urbs(struct camera_data *cam) -{ - struct urb *urb; - int fx, err, i, j; - - for(i=0; isbuf[i].data) - continue; - cam->sbuf[i].data = - kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!cam->sbuf[i].data) { - while (--i >= 0) { - kfree(cam->sbuf[i].data); - cam->sbuf[i].data = NULL; - } - return -ENOMEM; - } - } - - /* We double buffer the Isoc lists, and also know the polling - * interval is every frame (1 == (1 << (bInterval -1))). - */ - for(i=0; isbuf[i].urb) { - continue; - } - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - ERR("%s: usb_alloc_urb error!\n", __func__); - for (j = 0; j < i; j++) - usb_free_urb(cam->sbuf[j].urb); - return -ENOMEM; - } - - cam->sbuf[i].urb = urb; - urb->dev = cam->dev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = cam->sbuf[i].data; - urb->complete = cpia2_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = - FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = - FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - } - - - /* Queue the ISO urbs, and resubmit in the completion handler */ - for(i=0; isbuf[i].urb, GFP_KERNEL); - if (err) { - ERR("usb_submit_urb[%d]() = %d\n", i, err); - return err; - } - } - - return 0; -} - -/****************************************************************************** - * - * cpia2_usb_stream_start - * - *****************************************************************************/ -int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate) -{ - int ret; - int old_alt; - - if(cam->streaming) - return 0; - - if (cam->flush) { - int i; - DBG("Flushing buffers\n"); - for(i=0; inum_frames; ++i) { - cam->buffers[i].status = FRAME_EMPTY; - cam->buffers[i].length = 0; - } - cam->curbuff = &cam->buffers[0]; - cam->workbuff = cam->curbuff->next; - cam->flush = false; - } - - old_alt = cam->params.camera_state.stream_mode; - cam->params.camera_state.stream_mode = 0; - ret = cpia2_usb_change_streaming_alternate(cam, alternate); - if (ret < 0) { - int ret2; - ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret); - cam->params.camera_state.stream_mode = old_alt; - ret2 = set_alternate(cam, USBIF_CMDONLY); - if (ret2 < 0) { - ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already " - "failed. Then tried to call " - "set_alternate(USBIF_CMDONLY) = %d.\n", - alternate, ret, ret2); - } - } else { - cam->frame_count = 0; - cam->streaming = 1; - ret = cpia2_usb_stream_resume(cam); - } - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_stream_pause - * - *****************************************************************************/ -int cpia2_usb_stream_pause(struct camera_data *cam) -{ - int ret = 0; - if(cam->streaming) { - free_sbufs(cam); - ret = set_alternate(cam, USBIF_CMDONLY); - } - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_stream_resume - * - *****************************************************************************/ -int cpia2_usb_stream_resume(struct camera_data *cam) -{ - int ret = 0; - if(cam->streaming) { - cam->first_image_seen = 0; - ret = set_alternate(cam, cam->params.camera_state.stream_mode); - if(ret == 0) { - /* for some reason the user effects need to be set - again when starting streaming. */ - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam->params.vp_params.user_effects); - ret = submit_urbs(cam); - } - } - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_stream_stop - * - *****************************************************************************/ -int cpia2_usb_stream_stop(struct camera_data *cam) -{ - int ret; - - ret = cpia2_usb_stream_pause(cam); - cam->streaming = 0; - configure_transfer_mode(cam, 0); - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_probe - * - * Probe and initialize. - *****************************************************************************/ -static int cpia2_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_interface_descriptor *interface; - struct camera_data *cam; - int ret; - - /* A multi-config CPiA2 camera? */ - if (udev->descriptor.bNumConfigurations != 1) - return -ENODEV; - interface = &intf->cur_altsetting->desc; - - /* If we get to this point, we found a CPiA2 camera */ - LOG("CPiA2 USB camera found\n"); - - cam = cpia2_init_camera_struct(intf); - if (cam == NULL) - return -ENOMEM; - - cam->dev = udev; - cam->iface = interface->bInterfaceNumber; - - ret = set_alternate(cam, USBIF_CMDONLY); - if (ret < 0) { - ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret); - kfree(cam); - return ret; - } - - - if((ret = cpia2_init_camera(cam)) < 0) { - ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); - kfree(cam); - return ret; - } - LOG(" CPiA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmware_revision_hi, - cam->params.version.firmware_revision_lo, - cam->params.version.asic_id, - cam->params.version.asic_rev); - LOG(" CPiA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnp_id.vendor, - cam->params.pnp_id.product, - cam->params.pnp_id.device_revision); - LOG(" SensorID: %d.(version %d)\n", - cam->params.version.sensor_flags, - cam->params.version.sensor_rev); - - usb_set_intfdata(intf, cam); - - ret = cpia2_register_camera(cam); - if (ret < 0) { - ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); - kfree(cam); - return ret; - } - - return 0; -} - -/****************************************************************************** - * - * cpia2_disconnect - * - *****************************************************************************/ -static void cpia2_usb_disconnect(struct usb_interface *intf) -{ - struct camera_data *cam = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - - DBG("Stopping stream\n"); - cpia2_usb_stream_stop(cam); - - mutex_lock(&cam->v4l2_lock); - DBG("Unregistering camera\n"); - cpia2_unregister_camera(cam); - v4l2_device_disconnect(&cam->v4l2_dev); - mutex_unlock(&cam->v4l2_lock); - v4l2_device_put(&cam->v4l2_dev); - - if(cam->buffers) { - DBG("Wakeup waiting processes\n"); - cam->curbuff->status = FRAME_READY; - cam->curbuff->length = 0; - if (waitqueue_active(&cam->wq_stream)) - wake_up_interruptible(&cam->wq_stream); - } - - DBG("Releasing interface\n"); - usb_driver_release_interface(&cpia2_driver, intf); - - LOG("CPiA2 camera disconnected.\n"); -} - -static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct camera_data *cam = usb_get_intfdata(intf); - - mutex_lock(&cam->v4l2_lock); - if (cam->streaming) { - cpia2_usb_stream_stop(cam); - cam->streaming = 1; - } - mutex_unlock(&cam->v4l2_lock); - - dev_info(&intf->dev, "going into suspend..\n"); - return 0; -} - -/* Resume device - start device. */ -static int cpia2_usb_resume(struct usb_interface *intf) -{ - struct camera_data *cam = usb_get_intfdata(intf); - - mutex_lock(&cam->v4l2_lock); - v4l2_ctrl_handler_setup(&cam->hdl); - if (cam->streaming) { - cam->streaming = 0; - cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - } - mutex_unlock(&cam->v4l2_lock); - - dev_info(&intf->dev, "coming out of suspend..\n"); - return 0; -} - -/****************************************************************************** - * - * usb_cpia2_init - * - *****************************************************************************/ -int cpia2_usb_init(void) -{ - return usb_register(&cpia2_driver); -} - -/****************************************************************************** - * - * usb_cpia_cleanup - * - *****************************************************************************/ -void cpia2_usb_cleanup(void) -{ - schedule_timeout(2 * HZ); - usb_deregister(&cpia2_driver); -} diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c deleted file mode 100644 index 5ca6f44b4c63..000000000000 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ /dev/null @@ -1,1267 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2_v4l.c - * - * Copyright 2001, STMicrolectronics, Inc. - * Contact: steve.miller@st.com - * Copyright 2001,2005, Scott J. Bertin - * - * Description: - * This is a USB driver for CPia2 based video cameras. - * The infrastructure of this driver is based on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - * This program is free software; 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. - * - * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox - ****************************************************************************/ - -#define CPIA_VERSION "3.0.1" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia2.h" - -static int video_nr = -1; -module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); - -static int buffer_size = 68 * 1024; -module_param(buffer_size, int, 0); -MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); - -static int num_buffers = 3; -module_param(num_buffers, int, 0); -MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-" - __stringify(VIDEO_MAX_FRAME) ", default 3)"); - -static int alternate = DEFAULT_ALT; -module_param(alternate, int, 0); -MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" - __stringify(USBIF_ISO_6) ", default " - __stringify(DEFAULT_ALT) ")"); - -static int flicker_mode; -module_param(flicker_mode, int, 0); -MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " - __stringify(60) ", default 0)"); - -MODULE_AUTHOR("Steve Miller (STMicroelectronics) "); -MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); -MODULE_SUPPORTED_DEVICE("video"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(CPIA_VERSION); - -#define ABOUT "V4L-Driver for Vision CPiA2 based cameras" -#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) - -/****************************************************************************** - * - * cpia2_open - * - *****************************************************************************/ -static int cpia2_open(struct file *file) -{ - struct camera_data *cam = video_drvdata(file); - int retval; - - if (mutex_lock_interruptible(&cam->v4l2_lock)) - return -ERESTARTSYS; - retval = v4l2_fh_open(file); - if (retval) - goto open_unlock; - - if (v4l2_fh_is_singular_file(file)) { - if (cpia2_allocate_buffers(cam)) { - v4l2_fh_release(file); - retval = -ENOMEM; - goto open_unlock; - } - - /* reset the camera */ - if (cpia2_reset_camera(cam) < 0) { - v4l2_fh_release(file); - retval = -EIO; - goto open_unlock; - } - - cam->APP_len = 0; - cam->COM_len = 0; - } - - cpia2_dbg_dump_registers(cam); -open_unlock: - mutex_unlock(&cam->v4l2_lock); - return retval; -} - -/****************************************************************************** - * - * cpia2_close - * - *****************************************************************************/ -static int cpia2_close(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); - - mutex_lock(&cam->v4l2_lock); - if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { - cpia2_usb_stream_stop(cam); - - /* save camera state for later open */ - cpia2_save_camera_state(cam); - - cpia2_set_low_power(cam); - cpia2_free_buffers(cam); - } - - if (cam->stream_fh == file->private_data) { - cam->stream_fh = NULL; - cam->mmapped = 0; - } - mutex_unlock(&cam->v4l2_lock); - return v4l2_fh_release(file); -} - -/****************************************************************************** - * - * cpia2_v4l_read - * - *****************************************************************************/ -static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, - loff_t *off) -{ - struct camera_data *cam = video_drvdata(file); - int noblock = file->f_flags&O_NONBLOCK; - ssize_t ret; - - if(!cam) - return -EINVAL; - - if (mutex_lock_interruptible(&cam->v4l2_lock)) - return -ERESTARTSYS; - ret = cpia2_read(cam, buf, count, noblock); - mutex_unlock(&cam->v4l2_lock); - return ret; -} - - -/****************************************************************************** - * - * cpia2_v4l_poll - * - *****************************************************************************/ -static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) -{ - struct camera_data *cam = video_drvdata(filp); - unsigned int res; - - mutex_lock(&cam->v4l2_lock); - res = cpia2_poll(cam, filp, wait); - mutex_unlock(&cam->v4l2_lock); - return res; -} - - -static int sync(struct camera_data *cam, int frame_nr) -{ - struct framebuf *frame = &cam->buffers[frame_nr]; - - while (1) { - if (frame->status == FRAME_READY) - return 0; - - if (!cam->streaming) { - frame->status = FRAME_READY; - frame->length = 0; - return 0; - } - - mutex_unlock(&cam->v4l2_lock); - wait_event_interruptible(cam->wq_stream, - !cam->streaming || - frame->status == FRAME_READY); - mutex_lock(&cam->v4l2_lock); - if (signal_pending(current)) - return -ERESTARTSYS; - if (!video_is_registered(&cam->vdev)) - return -ENOTTY; - } -} - -/****************************************************************************** - * - * ioctl_querycap - * - * V4L2 device capabilities - * - *****************************************************************************/ - -static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc) -{ - struct camera_data *cam = video_drvdata(file); - - strcpy(vc->driver, "cpia2"); - - if (cam->params.pnp_id.product == 0x151) - strcpy(vc->card, "QX5 Microscope"); - else - strcpy(vc->card, "CPiA2 Camera"); - switch (cam->params.pnp_id.device_type) { - case DEVICE_STV_672: - strcat(vc->card, " (672/"); - break; - case DEVICE_STV_676: - strcat(vc->card, " (676/"); - break; - default: - strcat(vc->card, " (XXX/"); - break; - } - switch (cam->params.version.sensor_flags) { - case CPIA2_VP_SENSOR_FLAGS_404: - strcat(vc->card, "404)"); - break; - case CPIA2_VP_SENSOR_FLAGS_407: - strcat(vc->card, "407)"); - break; - case CPIA2_VP_SENSOR_FLAGS_409: - strcat(vc->card, "409)"); - break; - case CPIA2_VP_SENSOR_FLAGS_410: - strcat(vc->card, "410)"); - break; - case CPIA2_VP_SENSOR_FLAGS_500: - strcat(vc->card, "500)"); - break; - default: - strcat(vc->card, "XXX)"); - break; - } - - if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0) - memset(vc->bus_info,0, sizeof(vc->bus_info)); - - vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - vc->capabilities = vc->device_caps | - V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -/****************************************************************************** - * - * ioctl_input - * - * V4L2 input get/set/enumerate - * - *****************************************************************************/ - -static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - if (i->index) - return -EINVAL; - strcpy(i->name, "Camera"); - i->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - -static int cpia2_g_input(struct file *file, void *fh, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int cpia2_s_input(struct file *file, void *fh, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -/****************************************************************************** - * - * ioctl_enum_fmt - * - * V4L2 format enumerate - * - *****************************************************************************/ - -static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - int index = f->index; - - if (index < 0 || index > 1) - return -EINVAL; - - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - switch(index) { - case 0: - strcpy(f->description, "MJPEG"); - f->pixelformat = V4L2_PIX_FMT_MJPEG; - break; - case 1: - strcpy(f->description, "JPEG"); - f->pixelformat = V4L2_PIX_FMT_JPEG; - break; - default: - return -EINVAL; - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_try_fmt - * - * V4L2 format try - * - *****************************************************************************/ - -static int cpia2_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct camera_data *cam = video_drvdata(file); - - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - return -EINVAL; - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = cam->frame_size; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - - switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) { - case VIDEOSIZE_VGA: - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - break; - case VIDEOSIZE_CIF: - f->fmt.pix.width = 352; - f->fmt.pix.height = 288; - break; - case VIDEOSIZE_QVGA: - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - break; - case VIDEOSIZE_288_216: - f->fmt.pix.width = 288; - f->fmt.pix.height = 216; - break; - case VIDEOSIZE_256_192: - f->fmt.pix.width = 256; - f->fmt.pix.height = 192; - break; - case VIDEOSIZE_224_168: - f->fmt.pix.width = 224; - f->fmt.pix.height = 168; - break; - case VIDEOSIZE_192_144: - f->fmt.pix.width = 192; - f->fmt.pix.height = 144; - break; - case VIDEOSIZE_QCIF: - default: - f->fmt.pix.width = 176; - f->fmt.pix.height = 144; - break; - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_set_fmt - * - * V4L2 format set - * - *****************************************************************************/ - -static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, - struct v4l2_format *f) -{ - struct camera_data *cam = video_drvdata(file); - int err, frame; - - err = cpia2_try_fmt_vid_cap(file, _fh, f); - if(err != 0) - return err; - - cam->pixelformat = f->fmt.pix.pixelformat; - - /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle - * the missing Huffman table properly. */ - cam->params.compression.inhibit_htables = 0; - /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/ - - /* we set the video window to something smaller or equal to what - * is requested by the user??? - */ - DBG("Requested width = %d, height = %d\n", - f->fmt.pix.width, f->fmt.pix.height); - if (f->fmt.pix.width != cam->width || - f->fmt.pix.height != cam->height) { - cam->width = f->fmt.pix.width; - cam->height = f->fmt.pix.height; - cam->params.roi.width = f->fmt.pix.width; - cam->params.roi.height = f->fmt.pix.height; - cpia2_set_format(cam); - } - - for (frame = 0; frame < cam->num_frames; ++frame) { - if (cam->buffers[frame].status == FRAME_READING) - if ((err = sync(cam, frame)) < 0) - return err; - - cam->buffers[frame].status = FRAME_EMPTY; - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_get_fmt - * - * V4L2 format get - * - *****************************************************************************/ - -static int cpia2_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct camera_data *cam = video_drvdata(file); - - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; - f->fmt.pix.pixelformat = cam->pixelformat; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = cam->frame_size; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - - return 0; -} - -/****************************************************************************** - * - * ioctl_cropcap - * - * V4L2 query cropping capabilities - * NOTE: cropping is currently disabled - * - *****************************************************************************/ - -static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c) -{ - struct camera_data *cam = video_drvdata(file); - - if (c->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - c->bounds.left = 0; - c->bounds.top = 0; - c->bounds.width = cam->width; - c->bounds.height = cam->height; - c->defrect.left = 0; - c->defrect.top = 0; - c->defrect.width = cam->width; - c->defrect.height = cam->height; - c->pixelaspect.numerator = 1; - c->pixelaspect.denominator = 1; - - return 0; -} - -struct framerate_info { - int value; - struct v4l2_fract period; -}; - -static const struct framerate_info framerate_controls[] = { - { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, - { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, - { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, - { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, - { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, - { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, -}; - -static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) -{ - struct camera_data *cam = video_drvdata(file); - struct v4l2_captureparm *cap = &p->parm.capture; - int i; - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - cap->capability = V4L2_CAP_TIMEPERFRAME; - cap->readbuffers = cam->num_frames; - for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) - if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { - cap->timeperframe = framerate_controls[i].period; - break; - } - return 0; -} - -static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) -{ - struct camera_data *cam = video_drvdata(file); - struct v4l2_captureparm *cap = &p->parm.capture; - struct v4l2_fract tpf = cap->timeperframe; - int max = ARRAY_SIZE(framerate_controls) - 1; - int ret; - int i; - - ret = cpia2_g_parm(file, fh, p); - if (ret || !tpf.denominator || !tpf.numerator) - return ret; - - /* Maximum 15 fps for this model */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) - max -= 2; - for (i = 0; i <= max; i++) { - struct v4l2_fract f1 = tpf; - struct v4l2_fract f2 = framerate_controls[i].period; - - f1.numerator *= f2.denominator; - f2.numerator *= f1.denominator; - if (f1.numerator >= f2.numerator) - break; - } - if (i > max) - i = max; - cap->timeperframe = framerate_controls[i].period; - return cpia2_set_fps(cam, framerate_controls[i].value); -} - -static const struct { - u32 width; - u32 height; -} cpia2_framesizes[] = { - { 640, 480 }, - { 352, 288 }, - { 320, 240 }, - { 288, 216 }, - { 256, 192 }, - { 224, 168 }, - { 192, 144 }, - { 176, 144 }, -}; - -static int cpia2_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - - if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && - fsize->pixel_format != V4L2_PIX_FMT_JPEG) - return -EINVAL; - if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) - return -EINVAL; - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = cpia2_framesizes[fsize->index].width; - fsize->discrete.height = cpia2_framesizes[fsize->index].height; - - return 0; -} - -static int cpia2_enum_frameintervals(struct file *file, void *fh, - struct v4l2_frmivalenum *fival) -{ - struct camera_data *cam = video_drvdata(file); - int max = ARRAY_SIZE(framerate_controls) - 1; - int i; - - if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && - fival->pixel_format != V4L2_PIX_FMT_JPEG) - return -EINVAL; - - /* Maximum 15 fps for this model */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) - max -= 2; - if (fival->index > max) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) - if (fival->width == cpia2_framesizes[i].width && - fival->height == cpia2_framesizes[i].height) - break; - if (i == ARRAY_SIZE(cpia2_framesizes)) - return -EINVAL; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = framerate_controls[fival->index].period; - return 0; -} - -/****************************************************************************** - * - * ioctl_s_ctrl - * - * V4L2 set the value of a control variable - * - *****************************************************************************/ - -static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct camera_data *cam = - container_of(ctrl->handler, struct camera_data, hdl); - static const int flicker_table[] = { - NEVER_FLICKER, - FLICKER_50, - FLICKER_60, - }; - - DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - cpia2_set_brightness(cam, ctrl->val); - break; - case V4L2_CID_CONTRAST: - cpia2_set_contrast(cam, ctrl->val); - break; - case V4L2_CID_SATURATION: - cpia2_set_saturation(cam, ctrl->val); - break; - case V4L2_CID_HFLIP: - cpia2_set_property_mirror(cam, ctrl->val); - break; - case V4L2_CID_VFLIP: - cpia2_set_property_flip(cam, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); - case V4L2_CID_ILLUMINATORS_1: - return cpia2_set_gpio(cam, (cam->top_light->val << 6) | - (cam->bottom_light->val << 7)); - case V4L2_CID_JPEG_ACTIVE_MARKER: - cam->params.compression.inhibit_htables = - !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - cam->params.vc_params.quality = ctrl->val; - break; - case CPIA2_CID_USB_ALT: - cam->params.camera_state.stream_mode = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_g_jpegcomp - * - * V4L2 get the JPEG compression parameters - * - *****************************************************************************/ - -static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) -{ - struct camera_data *cam = video_drvdata(file); - - memset(parms, 0, sizeof(*parms)); - - parms->quality = 80; // TODO: Can this be made meaningful? - - parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; - if(!cam->params.compression.inhibit_htables) { - parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT; - } - - parms->APPn = cam->APPn; - parms->APP_len = cam->APP_len; - if(cam->APP_len > 0) { - memcpy(parms->APP_data, cam->APP_data, cam->APP_len); - parms->jpeg_markers |= V4L2_JPEG_MARKER_APP; - } - - parms->COM_len = cam->COM_len; - if(cam->COM_len > 0) { - memcpy(parms->COM_data, cam->COM_data, cam->COM_len); - parms->jpeg_markers |= JPEG_MARKER_COM; - } - - DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n", - parms->APP_len, parms->COM_len); - - return 0; -} - -/****************************************************************************** - * - * ioctl_s_jpegcomp - * - * V4L2 set the JPEG compression parameters - * NOTE: quality and some jpeg_markers are ignored. - * - *****************************************************************************/ - -static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) -{ - struct camera_data *cam = video_drvdata(file); - - DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n", - parms->APP_len, parms->COM_len); - - cam->params.compression.inhibit_htables = - !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); - parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | - V4L2_JPEG_MARKER_DHT; - - if(parms->APP_len != 0) { - if(parms->APP_len > 0 && - parms->APP_len <= sizeof(cam->APP_data) && - parms->APPn >= 0 && parms->APPn <= 15) { - cam->APPn = parms->APPn; - cam->APP_len = parms->APP_len; - memcpy(cam->APP_data, parms->APP_data, parms->APP_len); - } else { - LOG("Bad APPn Params n=%d len=%d\n", - parms->APPn, parms->APP_len); - return -EINVAL; - } - } else { - cam->APP_len = 0; - } - - if(parms->COM_len != 0) { - if(parms->COM_len > 0 && - parms->COM_len <= sizeof(cam->COM_data)) { - cam->COM_len = parms->COM_len; - memcpy(cam->COM_data, parms->COM_data, parms->COM_len); - } else { - LOG("Bad COM_len=%d\n", parms->COM_len); - return -EINVAL; - } - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_reqbufs - * - * V4L2 Initiate memory mapping. - * NOTE: The user's request is ignored. For now the buffers are fixed. - * - *****************************************************************************/ - -static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) -{ - struct camera_data *cam = video_drvdata(file); - - if(req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - req->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames); - req->count = cam->num_frames; - memset(&req->reserved, 0, sizeof(req->reserved)); - - return 0; -} - -/****************************************************************************** - * - * ioctl_querybuf - * - * V4L2 Query memory buffer status. - * - *****************************************************************************/ - -static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct camera_data *cam = video_drvdata(file); - - if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->index > cam->num_frames) - return -EINVAL; - - buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; - buf->length = cam->frame_size; - - buf->memory = V4L2_MEMORY_MMAP; - - if(cam->mmapped) - buf->flags = V4L2_BUF_FLAG_MAPPED; - else - buf->flags = 0; - - switch (cam->buffers[buf->index].status) { - case FRAME_EMPTY: - case FRAME_ERROR: - case FRAME_READING: - buf->bytesused = 0; - buf->flags = V4L2_BUF_FLAG_QUEUED; - break; - case FRAME_READY: - buf->bytesused = cam->buffers[buf->index].length; - buf->timestamp = cam->buffers[buf->index].timestamp; - buf->sequence = cam->buffers[buf->index].seq; - buf->flags = V4L2_BUF_FLAG_DONE; - break; - } - - DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n", - buf->index, buf->m.offset, buf->flags, buf->sequence, - buf->bytesused); - - return 0; -} - -/****************************************************************************** - * - * ioctl_qbuf - * - * V4L2 User is freeing buffer - * - *****************************************************************************/ - -static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct camera_data *cam = video_drvdata(file); - - if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->memory != V4L2_MEMORY_MMAP || - buf->index > cam->num_frames) - return -EINVAL; - - DBG("QBUF #%d\n", buf->index); - - if(cam->buffers[buf->index].status == FRAME_READY) - cam->buffers[buf->index].status = FRAME_EMPTY; - - return 0; -} - -/****************************************************************************** - * - * find_earliest_filled_buffer - * - * Helper for ioctl_dqbuf. Find the next ready buffer. - * - *****************************************************************************/ - -static int find_earliest_filled_buffer(struct camera_data *cam) -{ - int i; - int found = -1; - for (i=0; inum_frames; i++) { - if(cam->buffers[i].status == FRAME_READY) { - if(found < 0) { - found = i; - } else { - /* find which buffer is earlier */ - struct timeval *tv1, *tv2; - tv1 = &cam->buffers[i].timestamp; - tv2 = &cam->buffers[found].timestamp; - if(tv1->tv_sec < tv2->tv_sec || - (tv1->tv_sec == tv2->tv_sec && - tv1->tv_usec < tv2->tv_usec)) - found = i; - } - } - } - return found; -} - -/****************************************************************************** - * - * ioctl_dqbuf - * - * V4L2 User is asking for a filled buffer. - * - *****************************************************************************/ - -static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct camera_data *cam = video_drvdata(file); - int frame; - - if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - frame = find_earliest_filled_buffer(cam); - - if(frame < 0 && file->f_flags&O_NONBLOCK) - return -EAGAIN; - - if(frame < 0) { - /* Wait for a frame to become available */ - struct framebuf *cb=cam->curbuff; - mutex_unlock(&cam->v4l2_lock); - wait_event_interruptible(cam->wq_stream, - !video_is_registered(&cam->vdev) || - (cb=cam->curbuff)->status == FRAME_READY); - mutex_lock(&cam->v4l2_lock); - if (signal_pending(current)) - return -ERESTARTSYS; - if (!video_is_registered(&cam->vdev)) - return -ENOTTY; - frame = cb->num; - } - - - buf->index = frame; - buf->bytesused = cam->buffers[buf->index].length; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; - buf->field = V4L2_FIELD_NONE; - buf->timestamp = cam->buffers[buf->index].timestamp; - buf->sequence = cam->buffers[buf->index].seq; - buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; - buf->length = cam->frame_size; - buf->reserved2 = 0; - buf->reserved = 0; - memset(&buf->timecode, 0, sizeof(buf->timecode)); - - DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index, - cam->buffers[buf->index].status, buf->sequence, buf->bytesused); - - return 0; -} - -static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) -{ - struct camera_data *cam = video_drvdata(file); - int ret = -EINVAL; - - DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); - if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (!cam->streaming) { - ret = cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - if (!ret) - v4l2_ctrl_grab(cam->usb_alt, true); - } - return ret; -} - -static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) -{ - struct camera_data *cam = video_drvdata(file); - int ret = -EINVAL; - - DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); - if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->streaming) { - ret = cpia2_usb_stream_stop(cam); - if (!ret) - v4l2_ctrl_grab(cam->usb_alt, false); - } - return ret; -} - -/****************************************************************************** - * - * cpia2_mmap - * - *****************************************************************************/ -static int cpia2_mmap(struct file *file, struct vm_area_struct *area) -{ - struct camera_data *cam = video_drvdata(file); - int retval; - - if (mutex_lock_interruptible(&cam->v4l2_lock)) - return -ERESTARTSYS; - retval = cpia2_remap_buffer(cam, area); - - if(!retval) - cam->stream_fh = file->private_data; - mutex_unlock(&cam->v4l2_lock); - return retval; -} - -/****************************************************************************** - * - * reset_camera_struct_v4l - * - * Sets all values to the defaults - *****************************************************************************/ -static void reset_camera_struct_v4l(struct camera_data *cam) -{ - cam->width = cam->params.roi.width; - cam->height = cam->params.roi.height; - - cam->frame_size = buffer_size; - cam->num_frames = num_buffers; - - /* Flicker modes */ - cam->params.flicker_control.flicker_mode_req = flicker_mode; - - /* stream modes */ - cam->params.camera_state.stream_mode = alternate; - - cam->pixelformat = V4L2_PIX_FMT_JPEG; -} - -static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { - .vidioc_querycap = cpia2_querycap, - .vidioc_enum_input = cpia2_enum_input, - .vidioc_g_input = cpia2_g_input, - .vidioc_s_input = cpia2_s_input, - .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, - .vidioc_g_jpegcomp = cpia2_g_jpegcomp, - .vidioc_s_jpegcomp = cpia2_s_jpegcomp, - .vidioc_cropcap = cpia2_cropcap, - .vidioc_reqbufs = cpia2_reqbufs, - .vidioc_querybuf = cpia2_querybuf, - .vidioc_qbuf = cpia2_qbuf, - .vidioc_dqbuf = cpia2_dqbuf, - .vidioc_streamon = cpia2_streamon, - .vidioc_streamoff = cpia2_streamoff, - .vidioc_s_parm = cpia2_s_parm, - .vidioc_g_parm = cpia2_g_parm, - .vidioc_enum_framesizes = cpia2_enum_framesizes, - .vidioc_enum_frameintervals = cpia2_enum_frameintervals, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/*** - * The v4l video device structure initialized for this device - ***/ -static const struct v4l2_file_operations cpia2_fops = { - .owner = THIS_MODULE, - .open = cpia2_open, - .release = cpia2_close, - .read = cpia2_v4l_read, - .poll = cpia2_v4l_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = cpia2_mmap, -}; - -static struct video_device cpia2_template = { - /* I could not find any place for the old .initialize initializer?? */ - .name = "CPiA2 Camera", - .fops = &cpia2_fops, - .ioctl_ops = &cpia2_ioctl_ops, - .release = video_device_release_empty, -}; - -void cpia2_camera_release(struct v4l2_device *v4l2_dev) -{ - struct camera_data *cam = - container_of(v4l2_dev, struct camera_data, v4l2_dev); - - v4l2_ctrl_handler_free(&cam->hdl); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); -} - -static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { - .s_ctrl = cpia2_s_ctrl, -}; - -/****************************************************************************** - * - * cpia2_register_camera - * - *****************************************************************************/ -int cpia2_register_camera(struct camera_data *cam) -{ - struct v4l2_ctrl_handler *hdl = &cam->hdl; - struct v4l2_ctrl_config cpia2_usb_alt = { - .ops = &cpia2_ctrl_ops, - .id = CPIA2_CID_USB_ALT, - .name = "USB Alternate", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = USBIF_ISO_1, - .max = USBIF_ISO_6, - .step = 1, - }; - int ret; - - v4l2_ctrl_handler_init(hdl, 12); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_BRIGHTNESS, - cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, - 255, 1, DEFAULT_BRIGHTNESS); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_JPEG_ACTIVE_MARKER, 0, - V4L2_JPEG_ACTIVE_MARKER_DHT, 0, - V4L2_JPEG_ACTIVE_MARKER_DHT); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, - 100, 1, 100); - cpia2_usb_alt.def = alternate; - cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); - /* VP5 Only */ - if (cam->params.pnp_id.device_type != DEVICE_STV_672) - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - /* Flicker control only valid for 672 */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); - /* Light control only valid for the QX5 Microscope */ - if (cam->params.pnp_id.product == 0x151) { - cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); - cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); - v4l2_ctrl_cluster(2, &cam->top_light); - } - - if (hdl->error) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - cam->vdev = cpia2_template; - video_set_drvdata(&cam->vdev, cam); - cam->vdev.lock = &cam->v4l2_lock; - cam->vdev.ctrl_handler = hdl; - cam->vdev.v4l2_dev = &cam->v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); - - reset_camera_struct_v4l(cam); - - /* register v4l device */ - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - ERR("video_register_device failed\n"); - return -ENODEV; - } - - return 0; -} - -/****************************************************************************** - * - * cpia2_unregister_camera - * - *****************************************************************************/ -void cpia2_unregister_camera(struct camera_data *cam) -{ - video_unregister_device(&cam->vdev); -} - -/****************************************************************************** - * - * check_parameters - * - * Make sure that all user-supplied parameters are sensible - *****************************************************************************/ -static void __init check_parameters(void) -{ - if(buffer_size < PAGE_SIZE) { - buffer_size = PAGE_SIZE; - LOG("buffer_size too small, setting to %d\n", buffer_size); - } else if(buffer_size > 1024*1024) { - /* arbitrary upper limiit */ - buffer_size = 1024*1024; - LOG("buffer_size ridiculously large, setting to %d\n", - buffer_size); - } else { - buffer_size += PAGE_SIZE-1; - buffer_size &= ~(PAGE_SIZE-1); - } - - if(num_buffers < 1) { - num_buffers = 1; - LOG("num_buffers too small, setting to %d\n", num_buffers); - } else if(num_buffers > VIDEO_MAX_FRAME) { - num_buffers = VIDEO_MAX_FRAME; - LOG("num_buffers too large, setting to %d\n", num_buffers); - } - - if(alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) { - alternate = DEFAULT_ALT; - LOG("alternate specified is invalid, using %d\n", alternate); - } - - if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { - flicker_mode = 0; - LOG("Flicker mode specified is invalid, using %d\n", - flicker_mode); - } - - DBG("Using %d buffers, each %d bytes, alternate=%d\n", - num_buffers, buffer_size, alternate); -} - -/************ Module Stuff ***************/ - - -/****************************************************************************** - * - * cpia2_init/module_init - * - *****************************************************************************/ -static int __init cpia2_init(void) -{ - LOG("%s v%s\n", - ABOUT, CPIA_VERSION); - check_parameters(); - cpia2_usb_init(); - return 0; -} - - -/****************************************************************************** - * - * cpia2_exit/module_exit - * - *****************************************************************************/ -static void __exit cpia2_exit(void) -{ - cpia2_usb_cleanup(); - schedule_timeout(2 * HZ); -} - -module_init(cpia2_init); -module_exit(cpia2_exit); diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig deleted file mode 100644 index 446f692aabb7..000000000000 --- a/drivers/media/video/cx231xx/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -config VIDEO_CX231XX - tristate "Conexant cx231xx USB video capture support" - depends on VIDEO_DEV && I2C - select VIDEO_TUNER - select VIDEO_TVEEPROM - depends on RC_CORE - select VIDEOBUF_VMALLOC - select VIDEO_CX25840 - select VIDEO_CX2341X - - ---help--- - This is a video4linux driver for Conexant 231xx USB based TV cards. - - To compile this driver as a module, choose M here: the - module will be called cx231xx - -config VIDEO_CX231XX_RC - bool "Conexant cx231xx Remote Controller additional support" - depends on RC_CORE - depends on VIDEO_CX231XX - default y - ---help--- - cx231xx hardware has a builtin RX/TX support. However, a few - designs opted to not use it, but, instead, some other hardware. - This module enables the usage of those other hardware, like the - ones used with ISDB-T boards. - - On most cases, all you need for IR is mceusb module. - -config VIDEO_CX231XX_ALSA - tristate "Conexant Cx231xx ALSA audio module" - depends on VIDEO_CX231XX && SND - select SND_PCM - - ---help--- - This is an ALSA driver for Cx231xx USB based TV cards. - - To compile this driver as a module, choose M here: the - module will be called cx231xx-alsa - -config VIDEO_CX231XX_DVB - tristate "DVB/ATSC Support for Cx231xx based TV cards" - depends on VIDEO_CX231XX && DVB_CORE && DVB_CAPTURE_DRIVERS - select VIDEOBUF_DVB - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select DVB_MB86A20S if !DVB_FE_CUSTOMISE - - ---help--- - This adds support for DVB cards based on the - Conexant cx231xx chips. diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile deleted file mode 100644 index 1d40fce77601..000000000000 --- a/drivers/media/video/cx231xx/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o -cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o -cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.o - -cx231xx-alsa-objs := cx231xx-audio.o - -obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o -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/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/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c deleted file mode 100644 index b024e5197a75..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ /dev/null @@ -1,2197 +0,0 @@ -/* - * - * Support for a cx23417 mpeg encoder via cx231xx host port. - * - * (c) 2004 Jelle Foks - * (c) 2004 Gerd Knorr - * (c) 2008 Steven Toth - * - CX23885/7/8 support - * - * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cx231xx.h" -/*#include "cx23885-ioctl.h"*/ - -#define CX231xx_FIRM_IMAGE_SIZE 376836 -#define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" - -/* for polaris ITVC */ -#define ITVC_WRITE_DIR 0x03FDFC00 -#define ITVC_READ_DIR 0x0001FC00 - -#define MCI_MEMORY_DATA_BYTE0 0x00 -#define MCI_MEMORY_DATA_BYTE1 0x08 -#define MCI_MEMORY_DATA_BYTE2 0x10 -#define MCI_MEMORY_DATA_BYTE3 0x18 - -#define MCI_MEMORY_ADDRESS_BYTE2 0x20 -#define MCI_MEMORY_ADDRESS_BYTE1 0x28 -#define MCI_MEMORY_ADDRESS_BYTE0 0x30 - -#define MCI_REGISTER_DATA_BYTE0 0x40 -#define MCI_REGISTER_DATA_BYTE1 0x48 -#define MCI_REGISTER_DATA_BYTE2 0x50 -#define MCI_REGISTER_DATA_BYTE3 0x58 - -#define MCI_REGISTER_ADDRESS_BYTE0 0x60 -#define MCI_REGISTER_ADDRESS_BYTE1 0x68 - -#define MCI_REGISTER_MODE 0x70 - -/* Read and write modes for polaris ITVC */ -#define MCI_MODE_REGISTER_READ 0x000 -#define MCI_MODE_REGISTER_WRITE 0x100 -#define MCI_MODE_MEMORY_READ 0x000 -#define MCI_MODE_MEMORY_WRITE 0x4000 - -static unsigned int mpegbufs = 8; -module_param(mpegbufs, int, 0644); -MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32"); -static unsigned int mpeglines = 128; -module_param(mpeglines, int, 0644); -MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32"); -static unsigned int mpeglinesize = 512; -module_param(mpeglinesize, int, 0644); -MODULE_PARM_DESC(mpeglinesize, - "number of bytes in each line of an MPEG buffer, range 512-1024"); - -static unsigned int v4l_debug = 1; -module_param(v4l_debug, int, 0644); -MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); -struct cx231xx_dmaqueue *dma_qq; -#define dprintk(level, fmt, arg...)\ - do { if (v4l_debug >= level) \ - printk(KERN_INFO "%s: " fmt, \ - (dev) ? dev->name : "cx231xx[?]", ## arg); \ - } while (0) - -static struct cx231xx_tvnorm cx231xx_tvnorms[] = { - { - .name = "NTSC-M", - .id = V4L2_STD_NTSC_M, - }, { - .name = "NTSC-JP", - .id = V4L2_STD_NTSC_M_JP, - }, { - .name = "PAL-BG", - .id = V4L2_STD_PAL_BG, - }, { - .name = "PAL-DK", - .id = V4L2_STD_PAL_DK, - }, { - .name = "PAL-I", - .id = V4L2_STD_PAL_I, - }, { - .name = "PAL-M", - .id = V4L2_STD_PAL_M, - }, { - .name = "PAL-N", - .id = V4L2_STD_PAL_N, - }, { - .name = "PAL-Nc", - .id = V4L2_STD_PAL_Nc, - }, { - .name = "PAL-60", - .id = V4L2_STD_PAL_60, - }, { - .name = "SECAM-L", - .id = V4L2_STD_SECAM_L, - }, { - .name = "SECAM-DK", - .id = V4L2_STD_SECAM_DK, - } -}; - -/* ------------------------------------------------------------------ */ -enum cx231xx_capture_type { - CX231xx_MPEG_CAPTURE, - CX231xx_RAW_CAPTURE, - CX231xx_RAW_PASSTHRU_CAPTURE -}; -enum cx231xx_capture_bits { - CX231xx_RAW_BITS_NONE = 0x00, - CX231xx_RAW_BITS_YUV_CAPTURE = 0x01, - CX231xx_RAW_BITS_PCM_CAPTURE = 0x02, - CX231xx_RAW_BITS_VBI_CAPTURE = 0x04, - CX231xx_RAW_BITS_PASSTHRU_CAPTURE = 0x08, - CX231xx_RAW_BITS_TO_HOST_CAPTURE = 0x10 -}; -enum cx231xx_capture_end { - CX231xx_END_AT_GOP, /* stop at the end of gop, generate irq */ - CX231xx_END_NOW, /* stop immediately, no irq */ -}; -enum cx231xx_framerate { - CX231xx_FRAMERATE_NTSC_30, /* NTSC: 30fps */ - CX231xx_FRAMERATE_PAL_25 /* PAL: 25fps */ -}; -enum cx231xx_stream_port { - CX231xx_OUTPUT_PORT_MEMORY, - CX231xx_OUTPUT_PORT_STREAMING, - CX231xx_OUTPUT_PORT_SERIAL -}; -enum cx231xx_data_xfer_status { - CX231xx_MORE_BUFFERS_FOLLOW, - CX231xx_LAST_BUFFER, -}; -enum cx231xx_picture_mask { - CX231xx_PICTURE_MASK_NONE, - CX231xx_PICTURE_MASK_I_FRAMES, - CX231xx_PICTURE_MASK_I_P_FRAMES = 0x3, - CX231xx_PICTURE_MASK_ALL_FRAMES = 0x7, -}; -enum cx231xx_vbi_mode_bits { - CX231xx_VBI_BITS_SLICED, - CX231xx_VBI_BITS_RAW, -}; -enum cx231xx_vbi_insertion_bits { - CX231xx_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, - CX231xx_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, - CX231xx_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, - CX231xx_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, - CX231xx_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, -}; -enum cx231xx_dma_unit { - CX231xx_DMA_BYTES, - CX231xx_DMA_FRAMES, -}; -enum cx231xx_dma_transfer_status_bits { - CX231xx_DMA_TRANSFER_BITS_DONE = 0x01, - CX231xx_DMA_TRANSFER_BITS_ERROR = 0x04, - CX231xx_DMA_TRANSFER_BITS_LL_ERROR = 0x10, -}; -enum cx231xx_pause { - CX231xx_PAUSE_ENCODING, - CX231xx_RESUME_ENCODING, -}; -enum cx231xx_copyright { - CX231xx_COPYRIGHT_OFF, - CX231xx_COPYRIGHT_ON, -}; -enum cx231xx_notification_type { - CX231xx_NOTIFICATION_REFRESH, -}; -enum cx231xx_notification_status { - CX231xx_NOTIFICATION_OFF, - CX231xx_NOTIFICATION_ON, -}; -enum cx231xx_notification_mailbox { - CX231xx_NOTIFICATION_NO_MAILBOX = -1, -}; -enum cx231xx_field1_lines { - CX231xx_FIELD1_SAA7114 = 0x00EF, /* 239 */ - CX231xx_FIELD1_SAA7115 = 0x00F0, /* 240 */ - CX231xx_FIELD1_MICRONAS = 0x0105, /* 261 */ -}; -enum cx231xx_field2_lines { - CX231xx_FIELD2_SAA7114 = 0x00EF, /* 239 */ - CX231xx_FIELD2_SAA7115 = 0x00F0, /* 240 */ - CX231xx_FIELD2_MICRONAS = 0x0106, /* 262 */ -}; -enum cx231xx_custom_data_type { - CX231xx_CUSTOM_EXTENSION_USR_DATA, - CX231xx_CUSTOM_PRIVATE_PACKET, -}; -enum cx231xx_mute { - CX231xx_UNMUTE, - CX231xx_MUTE, -}; -enum cx231xx_mute_video_mask { - CX231xx_MUTE_VIDEO_V_MASK = 0x0000FF00, - CX231xx_MUTE_VIDEO_U_MASK = 0x00FF0000, - CX231xx_MUTE_VIDEO_Y_MASK = 0xFF000000, -}; -enum cx231xx_mute_video_shift { - CX231xx_MUTE_VIDEO_V_SHIFT = 8, - CX231xx_MUTE_VIDEO_U_SHIFT = 16, - CX231xx_MUTE_VIDEO_Y_SHIFT = 24, -}; - -/* defines below are from ivtv-driver.h */ -#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF - -/* Firmware API commands */ -#define IVTV_API_STD_TIMEOUT 500 - -/* Registers */ -/* IVTV_REG_OFFSET */ -#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) -#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) -#define IVTV_REG_SPU (0x9050) -#define IVTV_REG_HW_BLOCKS (0x9054) -#define IVTV_REG_VPU (0x9058) -#define IVTV_REG_APU (0xA064) - -/* - * Bit definitions for MC417_RWD and MC417_OEN registers - * - * bits 31-16 - *+-----------+ - *| Reserved | - *|+-----------+ - *| bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 - *|+-------+-------+-------+-------+-------+-------+-------+-------+ - *|| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0| - *|+-------+-------+-------+-------+-------+-------+-------+-------+ - *| bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 - *|+-------+-------+-------+-------+-------+-------+-------+-------+ - *||MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0| - *|+-------+-------+-------+-------+-------+-------+-------+-------+ - */ -#define MC417_MIWR 0x8000 -#define MC417_MIRD 0x4000 -#define MC417_MICS 0x2000 -#define MC417_MIRDY 0x1000 -#define MC417_MIADDR 0x0F00 -#define MC417_MIDATA 0x00FF - - -/* Bit definitions for MC417_CTL register **** - *bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0 - *+--------+-------------+--------+--------------+------------+ - *|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN| - *+--------+-------------+--------+--------------+------------+ - */ -#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030) -#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006) -#define MC417_UART_GPIO_EN 0x00000001 - -/* Values for speed control */ -#define MC417_SPD_CTL_SLOW 0x1 -#define MC417_SPD_CTL_MEDIUM 0x0 -#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */ - -/* Values for GPIO select */ -#define MC417_GPIO_SEL_GPIO3 0x3 -#define MC417_GPIO_SEL_GPIO2 0x2 -#define MC417_GPIO_SEL_GPIO1 0x1 -#define MC417_GPIO_SEL_GPIO0 0x0 - - -#define CX23417_GPIO_MASK 0xFC0003FF -static int setITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 value) -{ - int status = 0; - u32 _gpio_direction = 0; - - _gpio_direction = _gpio_direction & CX23417_GPIO_MASK; - _gpio_direction = _gpio_direction|gpio_direction; - status = cx231xx_send_gpio_cmd(dev, _gpio_direction, - (u8 *)&value, 4, 0, 0); - return status; -} -static int getITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 *pValue) -{ - int status = 0; - u32 _gpio_direction = 0; - - _gpio_direction = _gpio_direction & CX23417_GPIO_MASK; - _gpio_direction = _gpio_direction|gpio_direction; - - status = cx231xx_send_gpio_cmd(dev, _gpio_direction, - (u8 *)pValue, 4, 0, 1); - return status; -} - -static int waitForMciComplete(struct cx231xx *dev) -{ - u32 gpio; - u32 gpio_driection = 0; - u8 count = 0; - getITVCReg(dev, gpio_driection, &gpio); - - while (!(gpio&0x020000)) { - msleep(10); - - getITVCReg(dev, gpio_driection, &gpio); - - if (count++ > 100) { - dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio); - return -1; - } - } - return 0; -} - -static int mc417_register_write(struct cx231xx *dev, u16 address, u32 value) -{ - u32 temp; - int status = 0; - - temp = 0x82|MCI_REGISTER_DATA_BYTE0|((value&0x000000FF)<<8); - temp = temp<<10; - status = setITVCReg(dev, ITVC_WRITE_DIR, temp); - if (status < 0) - return status; - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write data byte 1;*/ - temp = 0x82|MCI_REGISTER_DATA_BYTE1|(value&0x0000FF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write data byte 2;*/ - temp = 0x82|MCI_REGISTER_DATA_BYTE2|((value&0x00FF0000)>>8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write data byte 3;*/ - temp = 0x82|MCI_REGISTER_DATA_BYTE3|((value&0xFF000000)>>16); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write address byte 0;*/ - temp = 0x82|MCI_REGISTER_ADDRESS_BYTE0|((address&0x000000FF)<<8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write address byte 1;*/ - temp = 0x82|MCI_REGISTER_ADDRESS_BYTE1|(address&0x0000FF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*Write that the mode is write.*/ - temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_WRITE; - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - return waitForMciComplete(dev); -} - -static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value) -{ - /*write address byte 0;*/ - u32 temp; - u32 return_value = 0; - int ret = 0; - - temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x00FF) << 8); - temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write address byte 1;*/ - temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0xFF00); - temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write that the mode is read;*/ - temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_READ; - temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*wait for the MIRDY line to be asserted , - signalling that the read is done;*/ - ret = waitForMciComplete(dev); - - /*switch the DATA- GPIO to input mode;*/ - - /*Read data byte 0;*/ - temp = (0x82 | MCI_REGISTER_DATA_BYTE0) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81 | MCI_REGISTER_DATA_BYTE0) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp & 0x03FC0000) >> 18); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); - - /* Read data byte 1;*/ - temp = (0x82 | MCI_REGISTER_DATA_BYTE1) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81 | MCI_REGISTER_DATA_BYTE1) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - - return_value |= ((temp & 0x03FC0000) >> 10); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); - - /*Read data byte 2;*/ - temp = (0x82 | MCI_REGISTER_DATA_BYTE2) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81 | MCI_REGISTER_DATA_BYTE2) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp & 0x03FC0000) >> 2); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); - - /*Read data byte 3;*/ - temp = (0x82 | MCI_REGISTER_DATA_BYTE3) << 10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81 | MCI_REGISTER_DATA_BYTE3) << 10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp & 0x03FC0000) << 6); - setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10)); - - *value = return_value; - - - return ret; -} - -static int mc417_memory_write(struct cx231xx *dev, u32 address, u32 value) -{ - /*write data byte 0;*/ - - u32 temp; - int ret = 0; - - temp = 0x82 | MCI_MEMORY_DATA_BYTE0|((value & 0x000000FF) << 8); - temp = temp << 10; - ret = setITVCReg(dev, ITVC_WRITE_DIR, temp); - if (ret < 0) - return ret; - temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write data byte 1;*/ - temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00); - temp = temp << 10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp | ((0x05) << 10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write data byte 2;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write data byte 3;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /* write address byte 2;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | - ((address & 0x003F0000)>>8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /* write address byte 1;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /* write address byte 0;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*wait for MIRDY line;*/ - waitForMciComplete(dev); - - return 0; -} - -static int mc417_memory_read(struct cx231xx *dev, u32 address, u32 *value) -{ - u32 temp = 0; - u32 return_value = 0; - int ret = 0; - - /*write address byte 2;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ | - ((address & 0x003F0000)>>8); - temp = temp<<10; - ret = setITVCReg(dev, ITVC_WRITE_DIR, temp); - if (ret < 0) - return ret; - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write address byte 1*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*write address byte 0*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF)<<8); - temp = temp<<10; - setITVCReg(dev, ITVC_WRITE_DIR, temp); - temp = temp|((0x05)<<10); - setITVCReg(dev, ITVC_WRITE_DIR, temp); - - /*Wait for MIRDY line*/ - ret = waitForMciComplete(dev); - - - /*Read data byte 3;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE3)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE3)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)<<6); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); - - /*Read data byte 2;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE2)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE2)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)>>2); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); - - /* Read data byte 1;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE1)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE1)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)>>10); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); - - /*Read data byte 0;*/ - temp = (0x82|MCI_MEMORY_DATA_BYTE0)<<10; - setITVCReg(dev, ITVC_READ_DIR, temp); - temp = ((0x81|MCI_MEMORY_DATA_BYTE0)<<10); - setITVCReg(dev, ITVC_READ_DIR, temp); - getITVCReg(dev, ITVC_READ_DIR, &temp); - return_value |= ((temp&0x03FC0000)>>18); - setITVCReg(dev, ITVC_READ_DIR, (0x87<<10)); - - *value = return_value; - return ret; -} - -/* ------------------------------------------------------------------ */ - -/* MPEG encoder API */ -static char *cmd_to_str(int cmd) -{ - switch (cmd) { - case CX2341X_ENC_PING_FW: - return "PING_FW"; - case CX2341X_ENC_START_CAPTURE: - return "START_CAPTURE"; - case CX2341X_ENC_STOP_CAPTURE: - return "STOP_CAPTURE"; - case CX2341X_ENC_SET_AUDIO_ID: - return "SET_AUDIO_ID"; - case CX2341X_ENC_SET_VIDEO_ID: - return "SET_VIDEO_ID"; - case CX2341X_ENC_SET_PCR_ID: - return "SET_PCR_PID"; - case CX2341X_ENC_SET_FRAME_RATE: - return "SET_FRAME_RATE"; - case CX2341X_ENC_SET_FRAME_SIZE: - return "SET_FRAME_SIZE"; - case CX2341X_ENC_SET_BIT_RATE: - return "SET_BIT_RATE"; - case CX2341X_ENC_SET_GOP_PROPERTIES: - return "SET_GOP_PROPERTIES"; - case CX2341X_ENC_SET_ASPECT_RATIO: - return "SET_ASPECT_RATIO"; - case CX2341X_ENC_SET_DNR_FILTER_MODE: - return "SET_DNR_FILTER_PROPS"; - case CX2341X_ENC_SET_DNR_FILTER_PROPS: - return "SET_DNR_FILTER_PROPS"; - case CX2341X_ENC_SET_CORING_LEVELS: - return "SET_CORING_LEVELS"; - case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: - return "SET_SPATIAL_FILTER_TYPE"; - case CX2341X_ENC_SET_VBI_LINE: - return "SET_VBI_LINE"; - case CX2341X_ENC_SET_STREAM_TYPE: - return "SET_STREAM_TYPE"; - case CX2341X_ENC_SET_OUTPUT_PORT: - return "SET_OUTPUT_PORT"; - case CX2341X_ENC_SET_AUDIO_PROPERTIES: - return "SET_AUDIO_PROPERTIES"; - case CX2341X_ENC_HALT_FW: - return "HALT_FW"; - case CX2341X_ENC_GET_VERSION: - return "GET_VERSION"; - case CX2341X_ENC_SET_GOP_CLOSURE: - return "SET_GOP_CLOSURE"; - case CX2341X_ENC_GET_SEQ_END: - return "GET_SEQ_END"; - case CX2341X_ENC_SET_PGM_INDEX_INFO: - return "SET_PGM_INDEX_INFO"; - case CX2341X_ENC_SET_VBI_CONFIG: - return "SET_VBI_CONFIG"; - case CX2341X_ENC_SET_DMA_BLOCK_SIZE: - return "SET_DMA_BLOCK_SIZE"; - case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10: - return "GET_PREV_DMA_INFO_MB_10"; - case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9: - return "GET_PREV_DMA_INFO_MB_9"; - case CX2341X_ENC_SCHED_DMA_TO_HOST: - return "SCHED_DMA_TO_HOST"; - case CX2341X_ENC_INITIALIZE_INPUT: - return "INITIALIZE_INPUT"; - case CX2341X_ENC_SET_FRAME_DROP_RATE: - return "SET_FRAME_DROP_RATE"; - case CX2341X_ENC_PAUSE_ENCODER: - return "PAUSE_ENCODER"; - case CX2341X_ENC_REFRESH_INPUT: - return "REFRESH_INPUT"; - case CX2341X_ENC_SET_COPYRIGHT: - return "SET_COPYRIGHT"; - case CX2341X_ENC_SET_EVENT_NOTIFICATION: - return "SET_EVENT_NOTIFICATION"; - case CX2341X_ENC_SET_NUM_VSYNC_LINES: - return "SET_NUM_VSYNC_LINES"; - case CX2341X_ENC_SET_PLACEHOLDER: - return "SET_PLACEHOLDER"; - case CX2341X_ENC_MUTE_VIDEO: - return "MUTE_VIDEO"; - case CX2341X_ENC_MUTE_AUDIO: - return "MUTE_AUDIO"; - case CX2341X_ENC_MISC: - return "MISC"; - default: - return "UNKNOWN"; - } -} - -static int cx231xx_mbox_func(void *priv, - u32 command, - int in, - int out, - u32 data[CX2341X_MBOX_MAX_DATA]) -{ - struct cx231xx *dev = priv; - unsigned long timeout; - u32 value, flag, retval = 0; - int i; - - dprintk(3, "%s: command(0x%X) = %s\n", __func__, command, - cmd_to_str(command)); - - /* this may not be 100% safe if we can't read any memory location - without side effects */ - mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value); - if (value != 0x12345678) { - dprintk(3, - "Firmware and/or mailbox pointer not initialized " - "or corrupted, signature = 0x%x, cmd = %s\n", value, - cmd_to_str(command)); - return -1; - } - - /* This read looks at 32 bits, but flag is only 8 bits. - * Seems we also bail if CMD or TIMEOUT bytes are set??? - */ - mc417_memory_read(dev, dev->cx23417_mailbox, &flag); - if (flag) { - dprintk(3, "ERROR: Mailbox appears to be in use " - "(%x), cmd = %s\n", flag, cmd_to_str(command)); - return -1; - } - - flag |= 1; /* tell 'em we're working on it */ - mc417_memory_write(dev, dev->cx23417_mailbox, flag); - - /* write command + args + fill remaining with zeros */ - /* command code */ - mc417_memory_write(dev, dev->cx23417_mailbox + 1, command); - mc417_memory_write(dev, dev->cx23417_mailbox + 3, - IVTV_API_STD_TIMEOUT); /* timeout */ - for (i = 0; i < in; i++) { - mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]); - dprintk(3, "API Input %d = %d\n", i, data[i]); - } - for (; i < CX2341X_MBOX_MAX_DATA; i++) - mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0); - - flag |= 3; /* tell 'em we're done writing */ - mc417_memory_write(dev, dev->cx23417_mailbox, flag); - - /* wait for firmware to handle the API command */ - timeout = jiffies + msecs_to_jiffies(10); - for (;;) { - mc417_memory_read(dev, dev->cx23417_mailbox, &flag); - if (0 != (flag & 4)) - break; - if (time_after(jiffies, timeout)) { - dprintk(3, "ERROR: API Mailbox timeout\n"); - return -1; - } - udelay(10); - } - - /* read output values */ - for (i = 0; i < out; i++) { - mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i); - dprintk(3, "API Output %d = %d\n", i, data[i]); - } - - mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval); - dprintk(3, "API result = %d\n", retval); - - flag = 0; - mc417_memory_write(dev, dev->cx23417_mailbox, flag); - - return retval; -} - -/* We don't need to call the API often, so using just one - * mailbox will probably suffice - */ -static int cx231xx_api_cmd(struct cx231xx *dev, - u32 command, - u32 inputcnt, - u32 outputcnt, - ...) -{ - u32 data[CX2341X_MBOX_MAX_DATA]; - va_list vargs; - int i, err; - - dprintk(3, "%s() cmds = 0x%08x\n", __func__, command); - - va_start(vargs, outputcnt); - for (i = 0; i < inputcnt; i++) - data[i] = va_arg(vargs, int); - - err = cx231xx_mbox_func(dev, command, inputcnt, outputcnt, data); - for (i = 0; i < outputcnt; i++) { - int *vptr = va_arg(vargs, int *); - *vptr = data[i]; - } - va_end(vargs); - - return err; -} - -static int cx231xx_find_mailbox(struct cx231xx *dev) -{ - u32 signature[4] = { - 0x12345678, 0x34567812, 0x56781234, 0x78123456 - }; - int signaturecnt = 0; - u32 value; - int i; - int ret = 0; - - dprintk(2, "%s()\n", __func__); - - for (i = 0; i < 0x100; i++) {/*CX231xx_FIRM_IMAGE_SIZE*/ - ret = mc417_memory_read(dev, i, &value); - if (ret < 0) - return ret; - if (value == signature[signaturecnt]) - signaturecnt++; - else - signaturecnt = 0; - if (4 == signaturecnt) { - dprintk(1, "Mailbox signature found at 0x%x\n", i+1); - return i+1; - } - } - dprintk(3, "Mailbox signature values not found!\n"); - return -1; -} - -static void mciWriteMemoryToGPIO(struct cx231xx *dev, u32 address, u32 value, - u32 *p_fw_image) -{ - - u32 temp = 0; - int i = 0; - - temp = 0x82|MCI_MEMORY_DATA_BYTE0|((value&0x000000FF)<<8); - temp = temp<<10; - *p_fw_image = temp; - p_fw_image++; - temp = temp|((0x05)<<10); - *p_fw_image = temp; - p_fw_image++; - - /*write data byte 1;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE1|(value&0x0000FF00); - temp = temp<<10; - *p_fw_image = temp; - p_fw_image++; - temp = temp|((0x05)<<10); - *p_fw_image = temp; - p_fw_image++; - - /*write data byte 2;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8); - temp = temp<<10; - *p_fw_image = temp; - p_fw_image++; - temp = temp|((0x05)<<10); - *p_fw_image = temp; - p_fw_image++; - - /*write data byte 3;*/ - temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16); - temp = temp<<10; - *p_fw_image = temp; - p_fw_image++; - temp = temp|((0x05)<<10); - *p_fw_image = temp; - p_fw_image++; - - /* write address byte 2;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE | - ((address & 0x003F0000)>>8); - temp = temp<<10; - *p_fw_image = temp; - p_fw_image++; - temp = temp|((0x05)<<10); - *p_fw_image = temp; - p_fw_image++; - - /* write address byte 1;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00); - temp = temp<<10; - *p_fw_image = temp; - p_fw_image++; - temp = temp|((0x05)<<10); - *p_fw_image = temp; - p_fw_image++; - - /* write address byte 0;*/ - temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8); - temp = temp<<10; - *p_fw_image = temp; - p_fw_image++; - temp = temp|((0x05)<<10); - *p_fw_image = temp; - p_fw_image++; - - for (i = 0; i < 6; i++) { - *p_fw_image = 0xFFFFFFFF; - p_fw_image++; - } -} - - -static int cx231xx_load_firmware(struct cx231xx *dev) -{ - static const unsigned char magic[8] = { - 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa - }; - const struct firmware *firmware; - int i, retval = 0; - u32 value = 0; - u32 gpio_output = 0; - /*u32 checksum = 0;*/ - /*u32 *dataptr;*/ - u32 transfer_size = 0; - u32 fw_data = 0; - u32 address = 0; - /*u32 current_fw[800];*/ - u32 *p_current_fw, *p_fw; - u32 *p_fw_data; - int frame = 0; - u16 _buffer_size = 4096; - u8 *p_buffer; - - p_current_fw = vmalloc(1884180 * 4); - p_fw = p_current_fw; - if (p_current_fw == NULL) { - dprintk(2, "FAIL!!!\n"); - return -1; - } - - p_buffer = vmalloc(4096); - if (p_buffer == NULL) { - dprintk(2, "FAIL!!!\n"); - return -1; - } - - dprintk(2, "%s()\n", __func__); - - /* Save GPIO settings before reset of APU */ - retval |= mc417_memory_read(dev, 0x9020, &gpio_output); - retval |= mc417_memory_read(dev, 0x900C, &value); - - retval = mc417_register_write(dev, - IVTV_REG_VPU, 0xFFFFFFED); - retval |= mc417_register_write(dev, - IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); - retval |= mc417_register_write(dev, - IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800); - retval |= mc417_register_write(dev, - IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); - retval |= mc417_register_write(dev, - IVTV_REG_APU, 0); - - if (retval != 0) { - printk(KERN_ERR "%s: Error with mc417_register_write\n", - __func__); - return -1; - } - - retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME, - &dev->udev->dev); - - if (retval != 0) { - printk(KERN_ERR - "ERROR: Hotplug firmware request failed (%s).\n", - CX231xx_FIRM_IMAGE_NAME); - printk(KERN_ERR "Please fix your hotplug setup, the board will " - "not work without firmware loaded!\n"); - return -1; - } - - if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) { - printk(KERN_ERR "ERROR: Firmware size mismatch " - "(have %zd, expected %d)\n", - firmware->size, CX231xx_FIRM_IMAGE_SIZE); - release_firmware(firmware); - return -1; - } - - if (0 != memcmp(firmware->data, magic, 8)) { - printk(KERN_ERR - "ERROR: Firmware magic mismatch, wrong file?\n"); - release_firmware(firmware); - return -1; - } - - initGPIO(dev); - - /* transfer to the chip */ - dprintk(2, "Loading firmware to GPIO...\n"); - p_fw_data = (u32 *)firmware->data; - dprintk(2, "firmware->size=%zd\n", firmware->size); - for (transfer_size = 0; transfer_size < firmware->size; - transfer_size += 4) { - fw_data = *p_fw_data; - - mciWriteMemoryToGPIO(dev, address, fw_data, p_current_fw); - address = address + 1; - p_current_fw += 20; - p_fw_data += 1; - } - - /*download the firmware by ep5-out*/ - - for (frame = 0; frame < (int)(CX231xx_FIRM_IMAGE_SIZE*20/_buffer_size); - frame++) { - for (i = 0; i < _buffer_size; i++) { - *(p_buffer + i) = (u8)(*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x000000FF); - i++; - *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x0000FF00) >> 8); - i++; - *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x00FF0000) >> 16); - i++; - *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0xFF000000) >> 24); - } - cx231xx_ep5_bulkout(dev, p_buffer, _buffer_size); - } - - p_current_fw = p_fw; - vfree(p_current_fw); - p_current_fw = NULL; - uninitGPIO(dev); - release_firmware(firmware); - dprintk(1, "Firmware upload successful.\n"); - - retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, - IVTV_CMD_HW_BLOCKS_RST); - if (retval < 0) { - printk(KERN_ERR "%s: Error with mc417_register_write\n", - __func__); - return retval; - } - /* F/W power up disturbs the GPIOs, restore state */ - retval |= mc417_register_write(dev, 0x9020, gpio_output); - retval |= mc417_register_write(dev, 0x900C, value); - - retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); - retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8); - - if (retval < 0) { - printk(KERN_ERR "%s: Error with mc417_register_write\n", - __func__); - return retval; - } - return 0; -} - -static void cx231xx_417_check_encoder(struct cx231xx *dev) -{ - u32 status, seq; - - status = 0; - seq = 0; - cx231xx_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq); - dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq); -} - -static void cx231xx_codec_settings(struct cx231xx *dev) -{ - dprintk(1, "%s()\n", __func__); - - /* assign frame size */ - cx231xx_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, - dev->ts1.height, dev->ts1.width); - - dev->mpeg_params.width = dev->ts1.width; - dev->mpeg_params.height = dev->ts1.height; - - cx2341x_update(dev, cx231xx_mbox_func, NULL, &dev->mpeg_params); - - cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); - cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); -} - -static int cx231xx_initialize_codec(struct cx231xx *dev) -{ - int version; - int retval; - u32 i; - u32 val = 0; - - dprintk(1, "%s()\n", __func__); - cx231xx_disable656(dev); - retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ - if (retval < 0) { - dprintk(2, "%s() PING OK\n", __func__); - retval = cx231xx_load_firmware(dev); - if (retval < 0) { - printk(KERN_ERR "%s() f/w load failed\n", __func__); - return retval; - } - retval = cx231xx_find_mailbox(dev); - if (retval < 0) { - printk(KERN_ERR "%s() mailbox < 0, error\n", - __func__); - return -1; - } - dev->cx23417_mailbox = retval; - retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); - if (retval < 0) { - printk(KERN_ERR - "ERROR: cx23417 firmware ping failed!\n"); - return -1; - } - retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, - &version); - if (retval < 0) { - printk(KERN_ERR "ERROR: cx23417 firmware get encoder :" - "version failed!\n"); - return -1; - } - dprintk(1, "cx23417 firmware version is 0x%08x\n", version); - msleep(200); - } - - for (i = 0; i < 1; i++) { - retval = mc417_register_read(dev, 0x20f8, &val); - dprintk(3, "***before enable656() VIM Capture Lines =%d ***\n", - val); - if (retval < 0) - return retval; - } - - cx231xx_enable656(dev); - /* stop mpeg capture */ - cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, - 3, 0, 1, 3, 4); - - cx231xx_codec_settings(dev); - msleep(60); - -/* cx231xx_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, - CX231xx_FIELD1_SAA7115, CX231xx_FIELD2_SAA7115); - cx231xx_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, - CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0); -*/ - -#if 0 - /* TODO */ - u32 data[7]; - - /* Setup to capture VBI */ - data[0] = 0x0001BD00; - data[1] = 1; /* frames per interrupt */ - data[2] = 4; /* total bufs */ - data[3] = 0x91559155; /* start codes */ - data[4] = 0x206080C0; /* stop codes */ - data[5] = 6; /* lines */ - data[6] = 64; /* BPL */ - - cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], - data[2], data[3], data[4], data[5], data[6]); - - for (i = 2; i <= 24; i++) { - int valid; - - valid = ((i >= 19) && (i <= 21)); - cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i, - valid, 0 , 0, 0); - cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, - i | 0x80000000, valid, 0, 0, 0); - } -#endif -/* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE); - msleep(60); -*/ - /* initialize the video input */ - retval = cx231xx_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); - if (retval < 0) - return retval; - msleep(60); - - /* Enable VIP style pixel invalidation so we work with scaled mode */ - mc417_memory_write(dev, 2120, 0x00000080); - - /* start capturing to the host interface */ - retval = cx231xx_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, - CX231xx_MPEG_CAPTURE, CX231xx_RAW_BITS_NONE); - if (retval < 0) - return retval; - msleep(10); - - for (i = 0; i < 1; i++) { - mc417_register_read(dev, 0x20f8, &val); - dprintk(3, "***VIM Capture Lines =%d ***\n", val); - } - - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int bb_buf_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) -{ - struct cx231xx_fh *fh = q->priv_data; - - fh->dev->ts1.ts_packet_size = mpeglinesize; - fh->dev->ts1.ts_packet_count = mpeglines; - - *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; - *count = mpegbufs; - - return 0; -} -static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - unsigned long flags = 0; - - if (in_interrupt()) - BUG(); - - spin_lock_irqsave(&dev->video_mode.slock, flags); - if (dev->USE_ISO) { - if (dev->video_mode.isoc_ctl.buf == buf) - dev->video_mode.isoc_ctl.buf = NULL; - } else { - if (dev->video_mode.bulk_ctl.buf == buf) - dev->video_mode.bulk_ctl.buf = NULL; - } - spin_unlock_irqrestore(&dev->video_mode.slock, flags); - videobuf_waiton(vq, &buf->vb, 0, 0); - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb, - struct cx231xx_dmaqueue *dma_q) -{ - void *vbuf; - struct cx231xx_buffer *buf; - u32 tail_data = 0; - char *p_data; - - if (dma_q->mpeg_buffer_done == 0) { - if (list_empty(&dma_q->active)) - return; - - buf = list_entry(dma_q->active.next, - struct cx231xx_buffer, vb.queue); - dev->video_mode.isoc_ctl.buf = buf; - dma_q->mpeg_buffer_done = 1; - } - /* Fill buffer */ - buf = dev->video_mode.isoc_ctl.buf; - vbuf = videobuf_to_vmalloc(&buf->vb); - - if ((dma_q->mpeg_buffer_completed+len) < - mpeglines*mpeglinesize) { - if (dma_q->add_ps_package_head == - CX231XX_NEED_ADD_PS_PACKAGE_HEAD) { - memcpy(vbuf+dma_q->mpeg_buffer_completed, - dma_q->ps_head, 3); - dma_q->mpeg_buffer_completed = - dma_q->mpeg_buffer_completed + 3; - dma_q->add_ps_package_head = - CX231XX_NONEED_PS_PACKAGE_HEAD; - } - memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len); - dma_q->mpeg_buffer_completed = - dma_q->mpeg_buffer_completed + len; - } else { - dma_q->mpeg_buffer_done = 0; - - tail_data = - mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed; - memcpy(vbuf+dma_q->mpeg_buffer_completed, - data, tail_data); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - dma_q->mpeg_buffer_completed = 0; - - if (len - tail_data > 0) { - p_data = data + tail_data; - dma_q->left_data_count = len - tail_data; - memcpy(dma_q->p_left_data, - p_data, len - tail_data); - } - - } - - return; -} - -static void buffer_filled(char *data, int len, struct urb *urb, - struct cx231xx_dmaqueue *dma_q) -{ - void *vbuf; - struct cx231xx_buffer *buf; - - if (list_empty(&dma_q->active)) - return; - - - buf = list_entry(dma_q->active.next, - struct cx231xx_buffer, vb.queue); - - - /* Fill buffer */ - vbuf = videobuf_to_vmalloc(&buf->vb); - memcpy(vbuf, data, len); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - - return; -} -static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) -{ - struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *p_buffer; - u32 buffer_size = 0; - u32 i = 0; - - for (i = 0; i < urb->number_of_packets; i++) { - if (dma_q->left_data_count > 0) { - buffer_copy(dev, dma_q->p_left_data, - dma_q->left_data_count, urb, dma_q); - dma_q->mpeg_buffer_completed = dma_q->left_data_count; - dma_q->left_data_count = 0; - } - - p_buffer = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - buffer_size = urb->iso_frame_desc[i].actual_length; - - if (buffer_size > 0) - buffer_copy(dev, p_buffer, buffer_size, urb, dma_q); - } - - return 0; -} -static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) -{ - - /*char *outp;*/ - /*struct cx231xx_buffer *buf;*/ - struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *p_buffer, *buffer; - u32 buffer_size = 0; - - p_buffer = urb->transfer_buffer; - buffer_size = urb->actual_length; - - buffer = kmalloc(buffer_size, GFP_ATOMIC); - - memcpy(buffer, dma_q->ps_head, 3); - memcpy(buffer+3, p_buffer, buffer_size-3); - memcpy(dma_q->ps_head, p_buffer+buffer_size-3, 3); - - p_buffer = buffer; - buffer_filled(p_buffer, buffer_size, urb, dma_q); - - kfree(buffer); - return 0; -} - -static int bb_buf_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct cx231xx_fh *fh = q->priv_data; - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - int rc = 0, urb_init = 0; - int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; - - dma_qq = &dev->video_mode.vidq; - - if (0 != buf->vb.baddr && buf->vb.bsize < size) - return -EINVAL; - buf->vb.width = fh->dev->ts1.ts_packet_size; - buf->vb.height = fh->dev->ts1.ts_packet_count; - buf->vb.size = size; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - if (dev->USE_ISO) { - if (!dev->video_mode.isoc_ctl.num_bufs) - urb_init = 1; - } else { - if (!dev->video_mode.bulk_ctl.num_bufs) - urb_init = 1; - } - /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n", - urb_init, dev->video_mode.max_pkt_size);*/ - dev->mode_tv = 1; - - if (urb_init) { - rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - rc = cx231xx_unmute_audio(dev); - if (dev->USE_ISO) { - cx231xx_set_alt_setting(dev, INDEX_TS1, 4); - rc = cx231xx_init_isoc(dev, mpeglines, - mpegbufs, - dev->ts1_mode.max_pkt_size, - cx231xx_isoc_copy); - } else { - cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - rc = cx231xx_init_bulk(dev, mpeglines, - mpegbufs, - dev->ts1_mode.max_pkt_size, - cx231xx_bulk_copy); - } - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(q, buf); - return rc; -} - -static void bb_buf_queue(struct videobuf_queue *q, - struct videobuf_buffer *vb) -{ - struct cx231xx_fh *fh = q->priv_data; - - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); - -} - -static void bb_buf_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) -{ - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - /*struct cx231xx_fh *fh = q->priv_data;*/ - /*struct cx231xx *dev = (struct cx231xx *)fh->dev;*/ - - free_buffer(q, buf); -} - -static struct videobuf_queue_ops cx231xx_qops = { - .buf_setup = bb_buf_setup, - .buf_prepare = bb_buf_prepare, - .buf_queue = bb_buf_queue, - .buf_release = bb_buf_release, -}; - -/* ------------------------------------------------------------------ */ - -static const u32 *ctrl_classes[] = { - cx2341x_mpeg_ctrls, - NULL -}; - -static int cx231xx_queryctrl(struct cx231xx *dev, - struct v4l2_queryctrl *qctrl) -{ - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; - - /* MPEG V4L2 controls */ - if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - - return 0; -} - -static int cx231xx_querymenu(struct cx231xx *dev, - struct v4l2_querymenu *qmenu) -{ - struct v4l2_queryctrl qctrl; - - qctrl.id = qmenu->id; - cx231xx_queryctrl(dev, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id)); -} - -static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - *norm = dev->encodernorm.id; - return 0; -} -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++) - if (*id & cx231xx_tvnorms[i].id) - break; - if (i == ARRAY_SIZE(cx231xx_tvnorms)) - return -EINVAL; - dev->encodernorm = cx231xx_tvnorms[i]; - - if (dev->encodernorm.id & 0xb000) { - dprintk(3, "encodernorm set to NTSC\n"); - dev->norm = V4L2_STD_NTSC; - dev->ts1.height = 480; - dev->mpeg_params.is_50hz = 0; - } else { - dprintk(3, "encodernorm set to PAL\n"); - dev->norm = V4L2_STD_PAL_B; - dev->ts1.height = 576; - dev->mpeg_params.is_50hz = 1; - } - call_all(dev, core, s_std, dev->norm); - /* do mode control overrides */ - cx231xx_do_mode_ctrl_overrides(dev); - - dprintk(3, "exit vidioc_s_std() i=0x%x\n", i); - return 0; -} -static int vidioc_g_audio(struct file *file, void *fh, - struct v4l2_audio *a) -{ - struct v4l2_audio *vin = a; - - int ret = -EINVAL; - if (vin->index > 0) - return ret; - strncpy(vin->name, "VideoGrabber Audio", 14); - vin->capability = V4L2_AUDCAP_STEREO; -return 0; -} -static int vidioc_enumaudio(struct file *file, void *fh, - struct v4l2_audio *a) -{ - struct v4l2_audio *vin = a; - - int ret = -EINVAL; - - if (vin->index > 0) - return ret; - strncpy(vin->name, "VideoGrabber Audio", 14); - vin->capability = V4L2_AUDCAP_STEREO; - - -return 0; -} -static const char *iname[] = { - [CX231XX_VMUX_COMPOSITE1] = "Composite1", - [CX231XX_VMUX_SVIDEO] = "S-Video", - [CX231XX_VMUX_TELEVISION] = "Television", - [CX231XX_VMUX_CABLE] = "Cable TV", - [CX231XX_VMUX_DVB] = "DVB", - [CX231XX_VMUX_DEBUG] = "for debug only", -}; -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - struct cx231xx_input *input; - int n; - dprintk(3, "enter vidioc_enum_input()i->index=%d\n", i->index); - - if (i->index >= 4) - return -EINVAL; - - - input = &cx231xx_boards[dev->model].input[i->index]; - - if (input->type == 0) - return -EINVAL; - - /* FIXME - * strcpy(i->name, input->name); */ - - n = i->index; - strcpy(i->name, iname[INPUT(n)->type]); - - if (input->type == CX231XX_VMUX_TELEVISION || - input->type == CX231XX_VMUX_CABLE) - i->type = V4L2_INPUT_TYPE_TUNER; - else - i->type = V4L2_INPUT_TYPE_CAMERA; - - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - dprintk(3, "enter vidioc_s_input() i=%d\n", i); - - mutex_lock(&dev->lock); - - video_mux(dev, i); - - mutex_unlock(&dev->lock); - - if (i >= 4) - return -EINVAL; - dev->input = i; - dprintk(3, "exit vidioc_s_input()\n"); - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - - - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_s_ctrl()\n"); - /* Update the A/V core */ - call_all(dev, core, s_ctrl, ctl); - dprintk(3, "exit vidioc_s_ctrl()\n"); - return 0; -} -static struct v4l2_capability pvr_capability = { - .driver = "cx231xx", - .card = "VideoGrabber", - .bus_info = "usb", - .version = 1, - .capabilities = (V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE), -}; -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - - - - memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - - if (f->index != 0) - return -EINVAL; - - strlcpy(f->description, "MPEG", sizeof(f->description)); - f->pixelformat = V4L2_PIX_FMT_MPEG; - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_g_fmt_vid_cap()\n"); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; - f->fmt.pix.colorspace = 0; - f->fmt.pix.width = dev->ts1.width; - f->fmt.pix.height = dev->ts1.height; - f->fmt.pix.field = fh->vidq.field; - dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->vidq.field); - dprintk(3, "exit vidioc_g_fmt_vid_cap()\n"); - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_try_fmt_vid_cap()\n"); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; - f->fmt.pix.colorspace = 0; - dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->vidq.field); - dprintk(3, "exit vidioc_try_fmt_vid_cap()\n"); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - - return 0; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_reqbufs(&fh->vidq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_querybuf(&fh->vidq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_qbuf(&fh->vidq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - - return videobuf_dqbuf(&fh->vidq, b, file->f_flags & O_NONBLOCK); -} - - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct cx231xx_fh *fh = file->private_data; - - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_streamon()\n"); - cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - if (dev->USE_ISO) - cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, - CX231XX_NUM_BUFS, - dev->video_mode.max_pkt_size, - cx231xx_isoc_copy); - else { - cx231xx_init_bulk(dev, 320, - 5, - dev->ts1_mode.max_pkt_size, - cx231xx_bulk_copy); - } - dprintk(3, "exit vidioc_streamon()\n"); - return videobuf_streamon(&fh->vidq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_streamoff(&fh->vidq); -} - -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_g_ext_ctrls()\n"); - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - dprintk(3, "exit vidioc_g_ext_ctrls()\n"); - return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS); -} - -static int vidioc_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - dprintk(3, "enter vidioc_s_ext_ctrls()\n"); - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - - p = dev->mpeg_params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); - if (err == 0) { - err = cx2341x_update(dev, cx231xx_mbox_func, - &dev->mpeg_params, &p); - dev->mpeg_params = p; - } - - return err; - - -return 0; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - dprintk(3, "enter vidioc_try_ext_ctrls()\n"); - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - - p = dev->mpeg_params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); - dprintk(3, "exit vidioc_try_ext_ctrls() err=%d\n", err); - return err; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", dev->name); - dprintk(3, - "%s/2: ============ START LOG STATUS ============\n", - dev->name); - call_all(dev, core, log_status); - cx2341x_log_status(&dev->mpeg_params, name); - dprintk(3, - "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *a) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_querymenu()\n"); - dprintk(3, "exit vidioc_querymenu()\n"); - return cx231xx_querymenu(dev, a); -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - dprintk(3, "enter vidioc_queryctrl()\n"); - dprintk(3, "exit vidioc_queryctrl()\n"); - return cx231xx_queryctrl(dev, c); -} - -static int mpeg_open(struct file *file) -{ - int minor = video_devdata(file)->minor; - struct cx231xx *h, *dev = NULL; - /*struct list_head *list;*/ - struct cx231xx_fh *fh; - /*u32 value = 0;*/ - - dprintk(2, "%s()\n", __func__); - - list_for_each_entry(h, &cx231xx_devlist, devlist) { - if (h->v4l_device->minor == minor) - dev = h; - } - - if (dev == NULL) - return -ENODEV; - - mutex_lock(&dev->lock); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - mutex_unlock(&dev->lock); - return -ENOMEM; - } - - file->private_data = fh; - fh->dev = dev; - - - videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops, - NULL, &dev->video_mode.slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, - sizeof(struct cx231xx_buffer), fh, NULL); -/* - videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops, - &dev->udev->dev, &dev->ts1.slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx231xx_buffer), - fh, NULL); -*/ - - - cx231xx_set_alt_setting(dev, INDEX_VANC, 1); - cx231xx_set_gpio_value(dev, 2, 0); - - cx231xx_initialize_codec(dev); - - mutex_unlock(&dev->lock); - cx231xx_start_TS1(dev); - - return 0; -} - -static int mpeg_release(struct file *file) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - dprintk(3, "mpeg_release()! dev=0x%p\n", dev); - - if (!dev) { - dprintk(3, "abort!!!\n"); - return 0; - } - - mutex_lock(&dev->lock); - - cx231xx_stop_TS1(dev); - - /* do this before setting alternate! */ - if (dev->USE_ISO) - cx231xx_uninit_isoc(dev); - else - cx231xx_uninit_bulk(dev); - cx231xx_set_mode(dev, CX231XX_SUSPEND); - - cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - CX231xx_END_NOW, CX231xx_MPEG_CAPTURE, - CX231xx_RAW_BITS_NONE); - - /* FIXME: Review this crap */ - /* Shut device down on last close */ - if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { - if (atomic_dec_return(&dev->v4l_reader_count) == 0) { - /* stop mpeg capture */ - - msleep(500); - cx231xx_417_check_encoder(dev); - - } - } - - if (fh->vidq.streaming) - videobuf_streamoff(&fh->vidq); - if (fh->vidq.reading) - videobuf_read_stop(&fh->vidq); - - videobuf_mmap_free(&fh->vidq); - file->private_data = NULL; - kfree(fh); - mutex_unlock(&dev->lock); - return 0; -} - -static ssize_t mpeg_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - - /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ - /* Start mpeg encoder on first read. */ - if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { - if (atomic_inc_return(&dev->v4l_reader_count) == 1) { - if (cx231xx_initialize_codec(dev) < 0) - return -EINVAL; - } - } - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static unsigned int mpeg_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx231xx_fh *fh = file->private_data; - /*struct cx231xx *dev = fh->dev;*/ - - /*dprintk(2, "%s\n", __func__);*/ - - return videobuf_poll_stream(file, &fh->vidq, wait); -} - -static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - dprintk(2, "%s()\n", __func__); - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - -static struct v4l2_file_operations mpeg_fops = { - .owner = THIS_MODULE, - .open = mpeg_open, - .release = mpeg_release, - .read = mpeg_read, - .poll = mpeg_poll, - .mmap = mpeg_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { - .vidioc_s_std = vidioc_s_std, - .vidioc_g_std = vidioc_g_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_enumaudio = vidioc_enumaudio, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, - .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_log_status = vidioc_log_status, - .vidioc_querymenu = vidioc_querymenu, - .vidioc_queryctrl = vidioc_queryctrl, -/* .vidioc_g_chip_ident = cx231xx_g_chip_ident,*/ -#ifdef CONFIG_VIDEO_ADV_DEBUG -/* .vidioc_g_register = cx231xx_g_register,*/ -/* .vidioc_s_register = cx231xx_s_register,*/ -#endif -}; - -static struct video_device cx231xx_mpeg_template = { - .name = "cx231xx", - .fops = &mpeg_fops, - .ioctl_ops = &mpeg_ioctl_ops, - .minor = -1, - .tvnorms = CX231xx_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; - -void cx231xx_417_unregister(struct cx231xx *dev) -{ - dprintk(1, "%s()\n", __func__); - dprintk(3, "%s()\n", __func__); - - if (dev->v4l_device) { - if (-1 != dev->v4l_device->minor) - video_unregister_device(dev->v4l_device); - else - video_device_release(dev->v4l_device); - dev->v4l_device = NULL; - } -} - -static struct video_device *cx231xx_video_dev_alloc( - struct cx231xx *dev, - struct usb_device *usbdev, - struct video_device *template, - char *type) -{ - struct video_device *vfd; - - dprintk(1, "%s()\n", __func__); - vfd = video_device_alloc(); - if (NULL == vfd) - return NULL; - *vfd = *template; - vfd->minor = -1; - snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, - type, cx231xx_boards[dev->model].name); - - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; - - return vfd; - -} - -int cx231xx_417_register(struct cx231xx *dev) -{ - /* FIXME: Port1 hardcoded here */ - int err = -ENODEV; - struct cx231xx_tsport *tsport = &dev->ts1; - - dprintk(1, "%s()\n", __func__); - - /* Set default TV standard */ - dev->encodernorm = cx231xx_tvnorms[0]; - - if (dev->encodernorm.id & V4L2_STD_525_60) - tsport->height = 480; - else - tsport->height = 576; - - tsport->width = 720; - cx2341x_fill_defaults(&dev->mpeg_params); - dev->norm = V4L2_STD_NTSC; - - dev->mpeg_params.port = CX2341X_PORT_SERIAL; - - /* Allocate and initialize V4L video device */ - dev->v4l_device = cx231xx_video_dev_alloc(dev, - dev->udev, &cx231xx_mpeg_template, "mpeg"); - err = video_register_device(dev->v4l_device, - VFL_TYPE_GRABBER, -1); - if (err < 0) { - dprintk(3, "%s: can't register mpeg device\n", dev->name); - return err; - } - - dprintk(3, "%s: registered device video%d [mpeg]\n", - dev->name, dev->v4l_device->num); - - return 0; -} - -MODULE_FIRMWARE(CX231xx_FIRM_IMAGE_NAME); diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c deleted file mode 100644 index b4c99c7270cf..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-audio.c +++ /dev/null @@ -1,780 +0,0 @@ -/* - * Conexant Cx231xx audio extension - * - * Copyright (C) 2008 - * Based on em28xx driver - * - * - * This program is free software; 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include "cx231xx.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); - -#define dprintk(fmt, arg...) do { \ - if (debug) \ - printk(KERN_INFO "cx231xx-audio %s: " fmt, \ - __func__, ##arg); \ - } while (0) - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; - -static int cx231xx_isoc_audio_deinit(struct cx231xx *dev) -{ - int i; - - dprintk("Stopping isoc\n"); - - for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { - if (dev->adev.urb[i]) { - if (!irqs_disabled()) - usb_kill_urb(dev->adev.urb[i]); - else - usb_unlink_urb(dev->adev.urb[i]); - - usb_free_urb(dev->adev.urb[i]); - dev->adev.urb[i] = NULL; - - kfree(dev->adev.transfer_buffer[i]); - dev->adev.transfer_buffer[i] = NULL; - } - } - - return 0; -} - -static int cx231xx_bulk_audio_deinit(struct cx231xx *dev) -{ - int i; - - dprintk("Stopping bulk\n"); - - for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { - if (dev->adev.urb[i]) { - if (!irqs_disabled()) - usb_kill_urb(dev->adev.urb[i]); - else - usb_unlink_urb(dev->adev.urb[i]); - - usb_free_urb(dev->adev.urb[i]); - dev->adev.urb[i] = NULL; - - kfree(dev->adev.transfer_buffer[i]); - dev->adev.transfer_buffer[i] = NULL; - } - } - - return 0; -} - -static void cx231xx_audio_isocirq(struct urb *urb) -{ - struct cx231xx *dev = urb->context; - int i; - unsigned int oldptr; - int period_elapsed = 0; - int status; - unsigned char *cp; - unsigned int stride; - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; - - if (dev->state & DEV_DISCONNECTED) - return; - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - dprintk("urb completition error %d.\n", urb->status); - break; - } - - if (atomic_read(&dev->stream_started) == 0) - return; - - if (dev->adev.capture_pcm_substream) { - substream = dev->adev.capture_pcm_substream; - runtime = substream->runtime; - stride = runtime->frame_bits >> 3; - - for (i = 0; i < urb->number_of_packets; i++) { - int length = urb->iso_frame_desc[i].actual_length / - stride; - cp = (unsigned char *)urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - - if (!length) - continue; - - oldptr = dev->adev.hwptr_done_capture; - if (oldptr + length >= runtime->buffer_size) { - unsigned int cnt; - - cnt = runtime->buffer_size - oldptr; - memcpy(runtime->dma_area + oldptr * stride, cp, - cnt * stride); - memcpy(runtime->dma_area, cp + cnt * stride, - length * stride - cnt * stride); - } else { - memcpy(runtime->dma_area + oldptr * stride, cp, - length * stride); - } - - snd_pcm_stream_lock(substream); - - dev->adev.hwptr_done_capture += length; - if (dev->adev.hwptr_done_capture >= - runtime->buffer_size) - dev->adev.hwptr_done_capture -= - runtime->buffer_size; - - dev->adev.capture_transfer_done += length; - if (dev->adev.capture_transfer_done >= - runtime->period_size) { - dev->adev.capture_transfer_done -= - runtime->period_size; - period_elapsed = 1; - } - snd_pcm_stream_unlock(substream); - } - if (period_elapsed) - snd_pcm_period_elapsed(substream); - } - urb->status = 0; - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status < 0) { - cx231xx_errdev("resubmit of audio urb failed (error=%i)\n", - status); - } - return; -} - -static void cx231xx_audio_bulkirq(struct urb *urb) -{ - struct cx231xx *dev = urb->context; - unsigned int oldptr; - int period_elapsed = 0; - int status; - unsigned char *cp; - unsigned int stride; - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; - - if (dev->state & DEV_DISCONNECTED) - return; - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - dprintk("urb completition error %d.\n", urb->status); - break; - } - - if (atomic_read(&dev->stream_started) == 0) - return; - - if (dev->adev.capture_pcm_substream) { - substream = dev->adev.capture_pcm_substream; - runtime = substream->runtime; - stride = runtime->frame_bits >> 3; - - if (1) { - int length = urb->actual_length / - stride; - cp = (unsigned char *)urb->transfer_buffer; - - oldptr = dev->adev.hwptr_done_capture; - if (oldptr + length >= runtime->buffer_size) { - unsigned int cnt; - - cnt = runtime->buffer_size - oldptr; - memcpy(runtime->dma_area + oldptr * stride, cp, - cnt * stride); - memcpy(runtime->dma_area, cp + cnt * stride, - length * stride - cnt * stride); - } else { - memcpy(runtime->dma_area + oldptr * stride, cp, - length * stride); - } - - snd_pcm_stream_lock(substream); - - dev->adev.hwptr_done_capture += length; - if (dev->adev.hwptr_done_capture >= - runtime->buffer_size) - dev->adev.hwptr_done_capture -= - runtime->buffer_size; - - dev->adev.capture_transfer_done += length; - if (dev->adev.capture_transfer_done >= - runtime->period_size) { - dev->adev.capture_transfer_done -= - runtime->period_size; - period_elapsed = 1; - } - snd_pcm_stream_unlock(substream); - } - if (period_elapsed) - snd_pcm_period_elapsed(substream); - } - urb->status = 0; - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status < 0) { - cx231xx_errdev("resubmit of audio urb failed (error=%i)\n", - status); - } - return; -} - -static int cx231xx_init_audio_isoc(struct cx231xx *dev) -{ - int i, errCode; - int sb_size; - - cx231xx_info("%s: Starting ISO AUDIO transfers\n", __func__); - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; - - for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { - struct urb *urb; - int j, k; - - dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); - if (!dev->adev.transfer_buffer[i]) - return -ENOMEM; - - memset(dev->adev.transfer_buffer[i], 0x80, sb_size); - urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (!urb) { - cx231xx_errdev("usb_alloc_urb failed!\n"); - for (j = 0; j < i; j++) { - usb_free_urb(dev->adev.urb[j]); - kfree(dev->adev.transfer_buffer[j]); - } - return -ENOMEM; - } - - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvisocpipe(dev->udev, - dev->adev.end_point_addr); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->adev.transfer_buffer[i]; - urb->interval = 1; - urb->complete = cx231xx_audio_isocirq; - urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS; - urb->transfer_buffer_length = sb_size; - - for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS; - j++, k += dev->adev.max_pkt_size) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = dev->adev.max_pkt_size; - } - dev->adev.urb[i] = urb; - } - - for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { - errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); - if (errCode < 0) { - cx231xx_isoc_audio_deinit(dev); - return errCode; - } - } - - return errCode; -} - -static int cx231xx_init_audio_bulk(struct cx231xx *dev) -{ - int i, errCode; - int sb_size; - - cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__); - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; - - for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { - struct urb *urb; - int j; - - dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); - if (!dev->adev.transfer_buffer[i]) - return -ENOMEM; - - memset(dev->adev.transfer_buffer[i], 0x80, sb_size); - urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (!urb) { - cx231xx_errdev("usb_alloc_urb failed!\n"); - for (j = 0; j < i; j++) { - usb_free_urb(dev->adev.urb[j]); - kfree(dev->adev.transfer_buffer[j]); - } - return -ENOMEM; - } - - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvbulkpipe(dev->udev, - dev->adev.end_point_addr); - urb->transfer_flags = 0; - urb->transfer_buffer = dev->adev.transfer_buffer[i]; - urb->complete = cx231xx_audio_bulkirq; - urb->transfer_buffer_length = sb_size; - - dev->adev.urb[i] = urb; - - } - - for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { - errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); - if (errCode < 0) { - cx231xx_bulk_audio_deinit(dev); - return errCode; - } - } - - return errCode; -} - -static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, - size_t size) -{ - struct snd_pcm_runtime *runtime = subs->runtime; - - dprintk("Allocating vbuffer\n"); - if (runtime->dma_area) { - if (runtime->dma_bytes > size) - return 0; - - vfree(runtime->dma_area); - } - runtime->dma_area = vmalloc(size); - if (!runtime->dma_area) - return -ENOMEM; - - runtime->dma_bytes = size; - - return 0; -} - -static struct snd_pcm_hardware snd_cx231xx_hw_capture = { - .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID, - - .formats = SNDRV_PCM_FMTBIT_S16_LE, - - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, - - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ - .period_bytes_min = 64, /* 12544/2, */ - .period_bytes_max = 12544, - .periods_min = 2, - .periods_max = 98, /* 12544, */ -}; - -static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream) -{ - struct cx231xx *dev = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int ret = 0; - - dprintk("opening device and trying to acquire exclusive lock\n"); - - if (!dev) { - cx231xx_errdev("BUG: cx231xx can't find device struct." - " Can't proceed with open\n"); - return -ENODEV; - } - - if (dev->state & DEV_DISCONNECTED) { - cx231xx_errdev("Can't open. the device was removed.\n"); - return -ENODEV; - } - - /* Sets volume, mute, etc */ - dev->mute = 0; - - /* set alternate setting for audio interface */ - /* 1 - 48000 samples per sec */ - mutex_lock(&dev->lock); - if (dev->USE_ISO) - ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1); - else - ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); - mutex_unlock(&dev->lock); - if (ret < 0) { - cx231xx_errdev("failed to set alternate setting !\n"); - - return ret; - } - - runtime->hw = snd_cx231xx_hw_capture; - - mutex_lock(&dev->lock); - /* inform hardware to start streaming */ - ret = cx231xx_capture_start(dev, 1, Audio); - - dev->adev.users++; - mutex_unlock(&dev->lock); - - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - dev->adev.capture_pcm_substream = substream; - runtime->private_data = dev; - - return 0; -} - -static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) -{ - int ret; - struct cx231xx *dev = snd_pcm_substream_chip(substream); - - dprintk("closing device\n"); - - /* inform hardware to stop streaming */ - mutex_lock(&dev->lock); - ret = cx231xx_capture_start(dev, 0, Audio); - - /* set alternate setting for audio interface */ - /* 1 - 48000 samples per sec */ - ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); - if (ret < 0) { - cx231xx_errdev("failed to set alternate setting !\n"); - - mutex_unlock(&dev->lock); - return ret; - } - - dev->mute = 1; - dev->adev.users--; - mutex_unlock(&dev->lock); - - if (dev->adev.users == 0 && dev->adev.shutdown == 1) { - dprintk("audio users: %d\n", dev->adev.users); - dprintk("disabling audio stream!\n"); - dev->adev.shutdown = 0; - dprintk("released lock\n"); - if (atomic_read(&dev->stream_started) > 0) { - atomic_set(&dev->stream_started, 0); - schedule_work(&dev->wq_trigger); - } - } - return 0; -} - -static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - - dprintk("Setting capture parameters\n"); - - ret = snd_pcm_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); -#if 0 - /* TODO: set up cx231xx audio chip to deliver the correct audio format, - current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono */ - unsigned int channels, rate, format; - - format = params_format(hw_params); - rate = params_rate(hw_params); - channels = params_channels(hw_params); -#endif - - return ret; -} - -static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream) -{ - struct cx231xx *dev = snd_pcm_substream_chip(substream); - - dprintk("Stop capture, if needed\n"); - - if (atomic_read(&dev->stream_started) > 0) { - atomic_set(&dev->stream_started, 0); - schedule_work(&dev->wq_trigger); - } - - return 0; -} - -static int snd_cx231xx_prepare(struct snd_pcm_substream *substream) -{ - struct cx231xx *dev = snd_pcm_substream_chip(substream); - - dev->adev.hwptr_done_capture = 0; - dev->adev.capture_transfer_done = 0; - - return 0; -} - -static void audio_trigger(struct work_struct *work) -{ - struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger); - - if (atomic_read(&dev->stream_started)) { - dprintk("starting capture"); - if (is_fw_load(dev) == 0) - cx25840_call(dev, core, load_fw); - if (dev->USE_ISO) - cx231xx_init_audio_isoc(dev); - else - cx231xx_init_audio_bulk(dev); - } else { - dprintk("stopping capture"); - cx231xx_isoc_audio_deinit(dev); - } -} - -static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct cx231xx *dev = snd_pcm_substream_chip(substream); - int retval = 0; - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - spin_lock(&dev->adev.slock); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - atomic_set(&dev->stream_started, 1); - break; - case SNDRV_PCM_TRIGGER_STOP: - atomic_set(&dev->stream_started, 0); - break; - default: - retval = -EINVAL; - break; - } - spin_unlock(&dev->adev.slock); - - schedule_work(&dev->wq_trigger); - - return retval; -} - -static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream - *substream) -{ - struct cx231xx *dev; - unsigned long flags; - snd_pcm_uframes_t hwptr_done; - - dev = snd_pcm_substream_chip(substream); - - spin_lock_irqsave(&dev->adev.slock, flags); - hwptr_done = dev->adev.hwptr_done_capture; - spin_unlock_irqrestore(&dev->adev.slock, flags); - - return hwptr_done; -} - -static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, - unsigned long offset) -{ - void *pageptr = subs->runtime->dma_area + offset; - - return vmalloc_to_page(pageptr); -} - -static struct snd_pcm_ops snd_cx231xx_pcm_capture = { - .open = snd_cx231xx_capture_open, - .close = snd_cx231xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_cx231xx_hw_capture_params, - .hw_free = snd_cx231xx_hw_capture_free, - .prepare = snd_cx231xx_prepare, - .trigger = snd_cx231xx_capture_trigger, - .pointer = snd_cx231xx_capture_pointer, - .page = snd_pcm_get_vmalloc_page, -}; - -static int cx231xx_audio_init(struct cx231xx *dev) -{ - struct cx231xx_audio *adev = &dev->adev; - struct snd_pcm *pcm; - struct snd_card *card; - static int devnr; - int err; - struct usb_interface *uif; - int i, isoc_pipe = 0; - - if (dev->has_alsa_audio != 1) { - /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module or - doesn't have analog audio support at all) */ - return 0; - } - - cx231xx_info("cx231xx-audio.c: probing for cx231xx " - "non standard usbaudio\n"); - - err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE, - 0, &card); - if (err < 0) - return err; - - spin_lock_init(&adev->slock); - err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); - if (err < 0) { - snd_card_free(card); - return err; - } - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_cx231xx_pcm_capture); - pcm->info_flags = 0; - pcm->private_data = dev; - strcpy(pcm->name, "Conexant cx231xx Capture"); - snd_card_set_dev(card, &dev->udev->dev); - strcpy(card->driver, "Cx231xx-Audio"); - strcpy(card->shortname, "Cx231xx Audio"); - strcpy(card->longname, "Conexant cx231xx Audio"); - - INIT_WORK(&dev->wq_trigger, audio_trigger); - - err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } - adev->sndcard = card; - adev->udev = dev->udev; - - /* compute alternate max packet sizes for Audio */ - uif = - dev->udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0].interface_info. - audio_index + 1]; - - adev->end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress); - - adev->num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - adev->end_point_addr, adev->num_alt); - adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL); - - if (adev->alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - return -ENOMEM; - } - - for (i = 0; i < adev->num_alt; i++) { - u16 tmp = - le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. - wMaxPacketSize); - adev->alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - adev->alt_max_pkt_size[i]); - } - - return 0; -} - -static int cx231xx_audio_fini(struct cx231xx *dev) -{ - if (dev == NULL) - return 0; - - if (dev->has_alsa_audio != 1) { - /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module or - doesn't have analog audio support at all) */ - return 0; - } - - if (dev->adev.sndcard) { - snd_card_free(dev->adev.sndcard); - kfree(dev->adev.alt_max_pkt_size); - dev->adev.sndcard = NULL; - } - - return 0; -} - -static struct cx231xx_ops audio_ops = { - .id = CX231XX_AUDIO, - .name = "Cx231xx Audio Extension", - .init = cx231xx_audio_init, - .fini = cx231xx_audio_fini, -}; - -static int __init cx231xx_alsa_register(void) -{ - return cx231xx_register_extension(&audio_ops); -} - -static void __exit cx231xx_alsa_unregister(void) -{ - cx231xx_unregister_extension(&audio_ops); -} - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Srinivasa Deevi "); -MODULE_DESCRIPTION("Cx231xx Audio driver"); - -module_init(cx231xx_alsa_register); -module_exit(cx231xx_alsa_unregister); diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c deleted file mode 100644 index 447148eff958..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ /dev/null @@ -1,3087 +0,0 @@ -/* - cx231xx_avcore.c - driver for Conexant Cx23100/101/102 - USB video capture devices - - Copyright (C) 2008 - - This program contains the specific code to control the avdecoder chip and - other related usb control functions for cx231xx based chipset. - - This program is free software; 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 -#include - -#include -#include -#include - -#include "cx231xx.h" -#include "cx231xx-dif.h" - -#define TUNER_MODE_FM_RADIO 0 -/****************************************************************************** - -: BLOCK ARRANGEMENT :- - I2S block ----------------------| - [I2S audio] | - | - Analog Front End --> Direct IF -|-> Cx25840 --> Audio - [video & audio] | [Audio] - | - |-> Cx25840 --> Video - [Video] - -*******************************************************************************/ -/****************************************************************************** - * VERVE REGISTER * - * * - ******************************************************************************/ -static int verve_write_byte(struct cx231xx *dev, u8 saddr, u8 data) -{ - return cx231xx_write_i2c_data(dev, VERVE_I2C_ADDRESS, - saddr, 1, data, 1); -} - -static int verve_read_byte(struct cx231xx *dev, u8 saddr, u8 *data) -{ - int status; - u32 temp = 0; - - status = cx231xx_read_i2c_data(dev, VERVE_I2C_ADDRESS, - saddr, 1, &temp, 1); - *data = (u8) temp; - return status; -} -void initGPIO(struct cx231xx *dev) -{ - u32 _gpio_direction = 0; - u32 value = 0; - u8 val = 0; - - _gpio_direction = _gpio_direction & 0xFC0003FF; - _gpio_direction = _gpio_direction | 0x03FDFC00; - cx231xx_send_gpio_cmd(dev, _gpio_direction, (u8 *)&value, 4, 0, 0); - - verve_read_byte(dev, 0x07, &val); - cx231xx_info(" verve_read_byte address0x07=0x%x\n", val); - verve_write_byte(dev, 0x07, 0xF4); - verve_read_byte(dev, 0x07, &val); - cx231xx_info(" verve_read_byte address0x07=0x%x\n", val); - - cx231xx_capture_start(dev, 1, Vbi); - - cx231xx_mode_register(dev, EP_MODE_SET, 0x0500FE00); - cx231xx_mode_register(dev, GBULK_BIT_EN, 0xFFFDFFFF); - -} -void uninitGPIO(struct cx231xx *dev) -{ - u8 value[4] = { 0, 0, 0, 0 }; - - cx231xx_capture_start(dev, 0, Vbi); - verve_write_byte(dev, 0x07, 0x14); - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - 0x68, value, 4); -} - -/****************************************************************************** - * A F E - B L O C K C O N T R O L functions * - * [ANALOG FRONT END] * - ******************************************************************************/ -static int afe_write_byte(struct cx231xx *dev, u16 saddr, u8 data) -{ - return cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS, - saddr, 2, data, 1); -} - -static int afe_read_byte(struct cx231xx *dev, u16 saddr, u8 *data) -{ - int status; - u32 temp = 0; - - status = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, - saddr, 2, &temp, 1); - *data = (u8) temp; - return status; -} - -int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count) -{ - int status = 0; - u8 temp = 0; - u8 afe_power_status = 0; - int i = 0; - - /* super block initialize */ - temp = (u8) (ref_count & 0xff); - status = afe_write_byte(dev, SUP_BLK_TUNE2, temp); - if (status < 0) - return status; - - status = afe_read_byte(dev, SUP_BLK_TUNE2, &afe_power_status); - if (status < 0) - return status; - - temp = (u8) ((ref_count & 0x300) >> 8); - temp |= 0x40; - status = afe_write_byte(dev, SUP_BLK_TUNE1, temp); - if (status < 0) - return status; - - status = afe_write_byte(dev, SUP_BLK_PLL2, 0x0f); - if (status < 0) - return status; - - /* enable pll */ - while (afe_power_status != 0x18) { - status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18); - if (status < 0) { - cx231xx_info( - ": Init Super Block failed in send cmd\n"); - break; - } - - status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status); - afe_power_status &= 0xff; - if (status < 0) { - cx231xx_info( - ": Init Super Block failed in receive cmd\n"); - break; - } - i++; - if (i == 10) { - cx231xx_info( - ": Init Super Block force break in loop !!!!\n"); - status = -1; - break; - } - } - - if (status < 0) - return status; - - /* start tuning filter */ - status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x40); - if (status < 0) - return status; - - msleep(5); - - /* exit tuning */ - status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x00); - - return status; -} - -int cx231xx_afe_init_channels(struct cx231xx *dev) -{ - int status = 0; - - /* power up all 3 channels, clear pd_buffer */ - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, 0x00); - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, 0x00); - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, 0x00); - - /* Enable quantizer calibration */ - status = afe_write_byte(dev, ADC_COM_QUANT, 0x02); - - /* channel initialize, force modulator (fb) reset */ - status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x17); - status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x17); - status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x17); - - /* start quantilizer calibration */ - status = afe_write_byte(dev, ADC_CAL_ATEST_CH1, 0x10); - status = afe_write_byte(dev, ADC_CAL_ATEST_CH2, 0x10); - status = afe_write_byte(dev, ADC_CAL_ATEST_CH3, 0x10); - msleep(5); - - /* exit modulator (fb) reset */ - status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x07); - status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x07); - status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x07); - - /* enable the pre_clamp in each channel for single-ended input */ - status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH1, 0xf0); - status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH2, 0xf0); - status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, 0xf0); - - /* use diode instead of resistor, so set term_en to 0, res_en to 0 */ - status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, - ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00); - status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, - ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00); - status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, - ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00); - - /* dynamic element matching off */ - status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH1, 0x03); - status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH2, 0x03); - status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, 0x03); - - return status; -} - -int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev) -{ - u8 c_value = 0; - int status = 0; - - status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH2, &c_value); - c_value &= (~(0x50)); - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, c_value); - - return status; -} - -/* - The Analog Front End in Cx231xx has 3 channels. These - channels are used to share between different inputs - like tuner, s-video and composite inputs. - - channel 1 ----- pin 1 to pin4(in reg is 1-4) - channel 2 ----- pin 5 to pin8(in reg is 5-8) - channel 3 ----- pin 9 to pin 12(in reg is 9-11) -*/ -int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux) -{ - u8 ch1_setting = (u8) input_mux; - u8 ch2_setting = (u8) (input_mux >> 8); - u8 ch3_setting = (u8) (input_mux >> 16); - int status = 0; - u8 value = 0; - - if (ch1_setting != 0) { - status = afe_read_byte(dev, ADC_INPUT_CH1, &value); - value &= ~INPUT_SEL_MASK; - value |= (ch1_setting - 1) << 4; - value &= 0xff; - status = afe_write_byte(dev, ADC_INPUT_CH1, value); - } - - if (ch2_setting != 0) { - status = afe_read_byte(dev, ADC_INPUT_CH2, &value); - value &= ~INPUT_SEL_MASK; - value |= (ch2_setting - 1) << 4; - value &= 0xff; - status = afe_write_byte(dev, ADC_INPUT_CH2, value); - } - - /* For ch3_setting, the value to put in the register is - 7 less than the input number */ - if (ch3_setting != 0) { - status = afe_read_byte(dev, ADC_INPUT_CH3, &value); - value &= ~INPUT_SEL_MASK; - value |= (ch3_setting - 1) << 4; - value &= 0xff; - status = afe_write_byte(dev, ADC_INPUT_CH3, value); - } - - return status; -} - -int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode) -{ - int status = 0; - - /* - * FIXME: We need to implement the AFE code for LOW IF and for HI IF. - * Currently, only baseband works. - */ - - switch (mode) { - case AFE_MODE_LOW_IF: - cx231xx_Setup_AFE_for_LowIF(dev); - break; - case AFE_MODE_BASEBAND: - status = cx231xx_afe_setup_AFE_for_baseband(dev); - break; - case AFE_MODE_EU_HI_IF: - /* SetupAFEforEuHiIF(); */ - break; - case AFE_MODE_US_HI_IF: - /* SetupAFEforUsHiIF(); */ - break; - case AFE_MODE_JAPAN_HI_IF: - /* SetupAFEforJapanHiIF(); */ - break; - } - - if ((mode != dev->afe_mode) && - (dev->video_input == CX231XX_VMUX_TELEVISION)) - status = cx231xx_afe_adjust_ref_count(dev, - CX231XX_VMUX_TELEVISION); - - dev->afe_mode = mode; - - return status; -} - -int cx231xx_afe_update_power_control(struct cx231xx *dev, - enum AV_MODE avmode) -{ - u8 afe_power_status = 0; - int status = 0; - - switch (dev->model) { - case CX231XX_BOARD_CNXT_CARRAERA: - case CX231XX_BOARD_CNXT_RDE_250: - case CX231XX_BOARD_CNXT_SHELBY: - case CX231XX_BOARD_CNXT_RDU_250: - case CX231XX_BOARD_CNXT_RDE_253S: - case CX231XX_BOARD_CNXT_RDU_253S: - case CX231XX_BOARD_CNXT_VIDEO_GRABBER: - case CX231XX_BOARD_HAUPPAUGE_EXETER: - case CX231XX_BOARD_HAUPPAUGE_USBLIVE2: - case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: - if (avmode == POLARIS_AVMODE_ANALOGT_TV) { - while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL)) { - status = afe_write_byte(dev, SUP_BLK_PWRDN, - FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL); - status |= afe_read_byte(dev, SUP_BLK_PWRDN, - &afe_power_status); - if (status < 0) - break; - } - - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, - 0x00); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, - 0x00); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, - 0x00); - } else if (avmode == POLARIS_AVMODE_DIGITAL) { - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, - 0x70); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, - 0x70); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, - 0x70); - - status |= afe_read_byte(dev, SUP_BLK_PWRDN, - &afe_power_status); - afe_power_status |= FLD_PWRDN_PD_BANDGAP | - FLD_PWRDN_PD_BIAS | - FLD_PWRDN_PD_TUNECK; - status |= afe_write_byte(dev, SUP_BLK_PWRDN, - afe_power_status); - } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { - while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL)) { - status = afe_write_byte(dev, SUP_BLK_PWRDN, - FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL); - status |= afe_read_byte(dev, SUP_BLK_PWRDN, - &afe_power_status); - if (status < 0) - break; - } - - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, - 0x00); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, - 0x00); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, - 0x00); - } else { - cx231xx_info("Invalid AV mode input\n"); - status = -1; - } - break; - default: - if (avmode == POLARIS_AVMODE_ANALOGT_TV) { - while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL)) { - status = afe_write_byte(dev, SUP_BLK_PWRDN, - FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL); - status |= afe_read_byte(dev, SUP_BLK_PWRDN, - &afe_power_status); - if (status < 0) - break; - } - - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, - 0x40); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, - 0x40); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, - 0x00); - } else if (avmode == POLARIS_AVMODE_DIGITAL) { - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, - 0x70); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, - 0x70); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, - 0x70); - - status |= afe_read_byte(dev, SUP_BLK_PWRDN, - &afe_power_status); - afe_power_status |= FLD_PWRDN_PD_BANDGAP | - FLD_PWRDN_PD_BIAS | - FLD_PWRDN_PD_TUNECK; - status |= afe_write_byte(dev, SUP_BLK_PWRDN, - afe_power_status); - } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { - while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL)) { - status = afe_write_byte(dev, SUP_BLK_PWRDN, - FLD_PWRDN_TUNING_BIAS | - FLD_PWRDN_ENABLE_PLL); - status |= afe_read_byte(dev, SUP_BLK_PWRDN, - &afe_power_status); - if (status < 0) - break; - } - - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, - 0x00); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, - 0x00); - status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, - 0x40); - } else { - cx231xx_info("Invalid AV mode input\n"); - status = -1; - } - } /* switch */ - - return status; -} - -int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input) -{ - u8 input_mode = 0; - u8 ntf_mode = 0; - int status = 0; - - dev->video_input = video_input; - - if (video_input == CX231XX_VMUX_TELEVISION) { - status = afe_read_byte(dev, ADC_INPUT_CH3, &input_mode); - status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, - &ntf_mode); - } else { - status = afe_read_byte(dev, ADC_INPUT_CH1, &input_mode); - status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH1, - &ntf_mode); - } - - input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1); - - switch (input_mode) { - case SINGLE_ENDED: - dev->afe_ref_count = 0x23C; - break; - case LOW_IF: - dev->afe_ref_count = 0x24C; - break; - case EU_IF: - dev->afe_ref_count = 0x258; - break; - case US_IF: - dev->afe_ref_count = 0x260; - break; - default: - break; - } - - status = cx231xx_afe_init_super_block(dev, dev->afe_ref_count); - - return status; -} - -/****************************************************************************** - * V I D E O / A U D I O D E C O D E R C O N T R O L functions * - ******************************************************************************/ -static int vid_blk_write_byte(struct cx231xx *dev, u16 saddr, u8 data) -{ - return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS, - saddr, 2, data, 1); -} - -static int vid_blk_read_byte(struct cx231xx *dev, u16 saddr, u8 *data) -{ - int status; - u32 temp = 0; - - status = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, - saddr, 2, &temp, 1); - *data = (u8) temp; - return status; -} - -static int vid_blk_write_word(struct cx231xx *dev, u16 saddr, u32 data) -{ - return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS, - saddr, 2, data, 4); -} - -static int vid_blk_read_word(struct cx231xx *dev, u16 saddr, u32 *data) -{ - return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, - saddr, 2, data, 4); -} -int cx231xx_check_fw(struct cx231xx *dev) -{ - u8 temp = 0; - int status = 0; - status = vid_blk_read_byte(dev, DL_CTL_ADDRESS_LOW, &temp); - if (status < 0) - return status; - else - return temp; - -} - -int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input) -{ - int status = 0; - - switch (INPUT(input)->type) { - case CX231XX_VMUX_COMPOSITE1: - case CX231XX_VMUX_SVIDEO: - if ((dev->current_pcb_config.type == USB_BUS_POWER) && - (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) { - /* External AV */ - status = cx231xx_set_power_mode(dev, - POLARIS_AVMODE_ENXTERNAL_AV); - if (status < 0) { - cx231xx_errdev("%s: set_power_mode : Failed to" - " set Power - errCode [%d]!\n", - __func__, status); - return status; - } - } - status = cx231xx_set_decoder_video_input(dev, - INPUT(input)->type, - INPUT(input)->vmux); - break; - case CX231XX_VMUX_TELEVISION: - case CX231XX_VMUX_CABLE: - if ((dev->current_pcb_config.type == USB_BUS_POWER) && - (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) { - /* Tuner */ - status = cx231xx_set_power_mode(dev, - POLARIS_AVMODE_ANALOGT_TV); - if (status < 0) { - cx231xx_errdev("%s: set_power_mode:Failed" - " to set Power - errCode [%d]!\n", - __func__, status); - return status; - } - } - if (dev->tuner_type == TUNER_NXP_TDA18271) - status = cx231xx_set_decoder_video_input(dev, - CX231XX_VMUX_TELEVISION, - INPUT(input)->vmux); - else - status = cx231xx_set_decoder_video_input(dev, - CX231XX_VMUX_COMPOSITE1, - INPUT(input)->vmux); - - break; - default: - cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n", - __func__, INPUT(input)->type); - break; - } - - /* save the selection */ - dev->video_input = input; - - return status; -} - -int cx231xx_set_decoder_video_input(struct cx231xx *dev, - u8 pin_type, u8 input) -{ - int status = 0; - u32 value = 0; - - if (pin_type != dev->video_input) { - status = cx231xx_afe_adjust_ref_count(dev, pin_type); - if (status < 0) { - cx231xx_errdev("%s: adjust_ref_count :Failed to set" - "AFE input mux - errCode [%d]!\n", - __func__, status); - return status; - } - } - - /* call afe block to set video inputs */ - status = cx231xx_afe_set_input_mux(dev, input); - if (status < 0) { - cx231xx_errdev("%s: set_input_mux :Failed to set" - " AFE input mux - errCode [%d]!\n", - __func__, status); - return status; - } - - switch (pin_type) { - case CX231XX_VMUX_COMPOSITE1: - status = vid_blk_read_word(dev, AFE_CTRL, &value); - value |= (0 << 13) | (1 << 4); - value &= ~(1 << 5); - - /* set [24:23] [22:15] to 0 */ - value &= (~(0x1ff8000)); - /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ - value |= 0x1000000; - status = vid_blk_write_word(dev, AFE_CTRL, value); - - status = vid_blk_read_word(dev, OUT_CTRL1, &value); - value |= (1 << 7); - status = vid_blk_write_word(dev, OUT_CTRL1, value); - - /* Set output mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - OUT_CTRL1, - FLD_OUT_MODE, - dev->board.output_mode); - - /* Tell DIF object to go to baseband mode */ - status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); - if (status < 0) { - cx231xx_errdev("%s: cx231xx_dif set to By pass" - " mode- errCode [%d]!\n", - __func__, status); - return status; - } - - /* Read the DFE_CTRL1 register */ - status = vid_blk_read_word(dev, DFE_CTRL1, &value); - - /* enable the VBI_GATE_EN */ - value |= FLD_VBI_GATE_EN; - - /* Enable the auto-VGA enable */ - value |= FLD_VGA_AUTO_EN; - - /* Write it back */ - status = vid_blk_write_word(dev, DFE_CTRL1, value); - - /* Disable auto config of registers */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, FLD_ACFG_DIS, - cx231xx_set_field(FLD_ACFG_DIS, 1)); - - /* Set CVBS input mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, FLD_INPUT_MODE, - cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0)); - break; - case CX231XX_VMUX_SVIDEO: - /* Disable the use of DIF */ - - status = vid_blk_read_word(dev, AFE_CTRL, &value); - - /* set [24:23] [22:15] to 0 */ - value &= (~(0x1ff8000)); - /* set FUNC_MODE[24:23] = 2 - IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */ - value |= 0x1000010; - status = vid_blk_write_word(dev, AFE_CTRL, value); - - /* Tell DIF object to go to baseband mode */ - status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); - if (status < 0) { - cx231xx_errdev("%s: cx231xx_dif set to By pass" - " mode- errCode [%d]!\n", - __func__, status); - return status; - } - - /* Read the DFE_CTRL1 register */ - status = vid_blk_read_word(dev, DFE_CTRL1, &value); - - /* enable the VBI_GATE_EN */ - value |= FLD_VBI_GATE_EN; - - /* Enable the auto-VGA enable */ - value |= FLD_VGA_AUTO_EN; - - /* Write it back */ - status = vid_blk_write_word(dev, DFE_CTRL1, value); - - /* Disable auto config of registers */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, FLD_ACFG_DIS, - cx231xx_set_field(FLD_ACFG_DIS, 1)); - - /* Set YC input mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, - FLD_INPUT_MODE, - cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1)); - - /* Chroma to ADC2 */ - status = vid_blk_read_word(dev, AFE_CTRL, &value); - value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */ - - /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8) - This sets them to use video - rather than audio. Only one of the two will be in use. */ - value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3); - - status = vid_blk_write_word(dev, AFE_CTRL, value); - - status = cx231xx_afe_set_mode(dev, AFE_MODE_BASEBAND); - break; - case CX231XX_VMUX_TELEVISION: - case CX231XX_VMUX_CABLE: - default: - /* TODO: Test if this is also needed for xc2028/xc3028 */ - if (dev->board.tuner_type == TUNER_XC5000) { - /* Disable the use of DIF */ - - status = vid_blk_read_word(dev, AFE_CTRL, &value); - value |= (0 << 13) | (1 << 4); - value &= ~(1 << 5); - - /* set [24:23] [22:15] to 0 */ - value &= (~(0x1FF8000)); - /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ - value |= 0x1000000; - status = vid_blk_write_word(dev, AFE_CTRL, value); - - status = vid_blk_read_word(dev, OUT_CTRL1, &value); - value |= (1 << 7); - status = vid_blk_write_word(dev, OUT_CTRL1, value); - - /* Set output mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - OUT_CTRL1, FLD_OUT_MODE, - dev->board.output_mode); - - /* Tell DIF object to go to baseband mode */ - status = cx231xx_dif_set_standard(dev, - DIF_USE_BASEBAND); - if (status < 0) { - cx231xx_errdev("%s: cx231xx_dif set to By pass" - " mode- errCode [%d]!\n", - __func__, status); - return status; - } - - /* Read the DFE_CTRL1 register */ - status = vid_blk_read_word(dev, DFE_CTRL1, &value); - - /* enable the VBI_GATE_EN */ - value |= FLD_VBI_GATE_EN; - - /* Enable the auto-VGA enable */ - value |= FLD_VGA_AUTO_EN; - - /* Write it back */ - status = vid_blk_write_word(dev, DFE_CTRL1, value); - - /* Disable auto config of registers */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, FLD_ACFG_DIS, - cx231xx_set_field(FLD_ACFG_DIS, 1)); - - /* Set CVBS input mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, FLD_INPUT_MODE, - cx231xx_set_field(FLD_INPUT_MODE, - INPUT_MODE_CVBS_0)); - } else { - /* Enable the DIF for the tuner */ - - /* Reinitialize the DIF */ - status = cx231xx_dif_set_standard(dev, dev->norm); - if (status < 0) { - cx231xx_errdev("%s: cx231xx_dif set to By pass" - " mode- errCode [%d]!\n", - __func__, status); - return status; - } - - /* Make sure bypass is cleared */ - status = vid_blk_read_word(dev, DIF_MISC_CTRL, &value); - - /* Clear the bypass bit */ - value &= ~FLD_DIF_DIF_BYPASS; - - /* Enable the use of the DIF block */ - status = vid_blk_write_word(dev, DIF_MISC_CTRL, value); - - /* Read the DFE_CTRL1 register */ - status = vid_blk_read_word(dev, DFE_CTRL1, &value); - - /* Disable the VBI_GATE_EN */ - value &= ~FLD_VBI_GATE_EN; - - /* Enable the auto-VGA enable, AGC, and - set the skip count to 2 */ - value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000; - - /* Write it back */ - status = vid_blk_write_word(dev, DFE_CTRL1, value); - - /* Wait until AGC locks up */ - msleep(1); - - /* Disable the auto-VGA enable AGC */ - value &= ~(FLD_VGA_AUTO_EN); - - /* Write it back */ - status = vid_blk_write_word(dev, DFE_CTRL1, value); - - /* Enable Polaris B0 AGC output */ - status = vid_blk_read_word(dev, PIN_CTRL, &value); - value |= (FLD_OEF_AGC_RF) | - (FLD_OEF_AGC_IFVGA) | - (FLD_OEF_AGC_IF); - status = vid_blk_write_word(dev, PIN_CTRL, value); - - /* Set output mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - OUT_CTRL1, FLD_OUT_MODE, - dev->board.output_mode); - - /* Disable auto config of registers */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, FLD_ACFG_DIS, - cx231xx_set_field(FLD_ACFG_DIS, 1)); - - /* Set CVBS input mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - MODE_CTRL, FLD_INPUT_MODE, - cx231xx_set_field(FLD_INPUT_MODE, - INPUT_MODE_CVBS_0)); - - /* Set some bits in AFE_CTRL so that channel 2 or 3 - * is ready to receive audio */ - /* Clear clamp for channels 2 and 3 (bit 16-17) */ - /* Clear droop comp (bit 19-20) */ - /* Set VGA_SEL (for audio control) (bit 7-8) */ - status = vid_blk_read_word(dev, AFE_CTRL, &value); - - /*Set Func mode:01-DIF 10-baseband 11-YUV*/ - value &= (~(FLD_FUNC_MODE)); - value |= 0x800000; - - value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2; - - status = vid_blk_write_word(dev, AFE_CTRL, value); - - if (dev->tuner_type == TUNER_NXP_TDA18271) { - status = vid_blk_read_word(dev, PIN_CTRL, - &value); - status = vid_blk_write_word(dev, PIN_CTRL, - (value & 0xFFFFFFEF)); - } - - break; - - } - break; - } - - /* Set raw VBI mode */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - OUT_CTRL1, FLD_VBIHACTRAW_EN, - cx231xx_set_field(FLD_VBIHACTRAW_EN, 1)); - - status = vid_blk_read_word(dev, OUT_CTRL1, &value); - if (value & 0x02) { - value |= (1 << 19); - status = vid_blk_write_word(dev, OUT_CTRL1, value); - } - - return status; -} - -void cx231xx_enable656(struct cx231xx *dev) -{ - u8 temp = 0; - /*enable TS1 data[0:7] as output to export 656*/ - - vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); - - /*enable TS1 clock as output to export 656*/ - - vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); - temp = temp|0x04; - - vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); -} -EXPORT_SYMBOL_GPL(cx231xx_enable656); - -void cx231xx_disable656(struct cx231xx *dev) -{ - u8 temp = 0; - - vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); - - vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); - temp = temp&0xFB; - - vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); -} -EXPORT_SYMBOL_GPL(cx231xx_disable656); - -/* - * Handle any video-mode specific overrides that are different - * on a per video standards basis after touching the MODE_CTRL - * register which resets many values for autodetect - */ -int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev) -{ - int status = 0; - - cx231xx_info("do_mode_ctrl_overrides : 0x%x\n", - (unsigned int)dev->norm); - - /* Change the DFE_CTRL3 bp_percent to fix flagging */ - status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280); - - if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) { - cx231xx_info("do_mode_ctrl_overrides NTSC\n"); - - /* Move the close caption lines out of active video, - adjust the active video start point */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_VBLANK_CNT, 0x18); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_VACTIVE_CNT, - 0x1E7000); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_V656BLANK_CNT, - 0x1C000000); - - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - HORIZ_TIM_CTRL, - FLD_HBLANK_CNT, - cx231xx_set_field - (FLD_HBLANK_CNT, 0x79)); - - } else if (dev->norm & V4L2_STD_SECAM) { - cx231xx_info("do_mode_ctrl_overrides SECAM\n"); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_VBLANK_CNT, 0x20); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_VACTIVE_CNT, - cx231xx_set_field - (FLD_VACTIVE_CNT, - 0x244)); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_V656BLANK_CNT, - cx231xx_set_field - (FLD_V656BLANK_CNT, - 0x24)); - /* Adjust the active video horizontal start point */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - HORIZ_TIM_CTRL, - FLD_HBLANK_CNT, - cx231xx_set_field - (FLD_HBLANK_CNT, 0x85)); - } else { - cx231xx_info("do_mode_ctrl_overrides PAL\n"); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_VBLANK_CNT, 0x20); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_VACTIVE_CNT, - cx231xx_set_field - (FLD_VACTIVE_CNT, - 0x244)); - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - VERT_TIM_CTRL, - FLD_V656BLANK_CNT, - cx231xx_set_field - (FLD_V656BLANK_CNT, - 0x24)); - /* Adjust the active video horizontal start point */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - HORIZ_TIM_CTRL, - FLD_HBLANK_CNT, - cx231xx_set_field - (FLD_HBLANK_CNT, 0x85)); - - } - - return status; -} - -int cx231xx_unmute_audio(struct cx231xx *dev) -{ - return vid_blk_write_byte(dev, PATH1_VOL_CTL, 0x24); -} -EXPORT_SYMBOL_GPL(cx231xx_unmute_audio); - -int stopAudioFirmware(struct cx231xx *dev) -{ - return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x03); -} - -int restartAudioFirmware(struct cx231xx *dev) -{ - return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x13); -} - -int cx231xx_set_audio_input(struct cx231xx *dev, u8 input) -{ - int status = 0; - enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE; - - switch (INPUT(input)->amux) { - case CX231XX_AMUX_VIDEO: - ainput = AUDIO_INPUT_TUNER_TV; - break; - case CX231XX_AMUX_LINE_IN: - status = cx231xx_i2s_blk_set_audio_input(dev, input); - ainput = AUDIO_INPUT_LINE; - break; - default: - break; - } - - status = cx231xx_set_audio_decoder_input(dev, ainput); - - return status; -} - -int cx231xx_set_audio_decoder_input(struct cx231xx *dev, - enum AUDIO_INPUT audio_input) -{ - u32 dwval; - int status; - u8 gen_ctrl; - u32 value = 0; - - /* Put it in soft reset */ - status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl); - gen_ctrl |= 1; - status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl); - - switch (audio_input) { - case AUDIO_INPUT_LINE: - /* setup AUD_IO control from Merlin paralle output */ - value = cx231xx_set_field(FLD_AUD_CHAN1_SRC, - AUD_CHAN_SRC_PARALLEL); - status = vid_blk_write_word(dev, AUD_IO_CTRL, value); - - /* setup input to Merlin, SRC2 connect to AC97 - bypass upsample-by-2, slave mode, sony mode, left justify - adr 091c, dat 01000000 */ - status = vid_blk_read_word(dev, AC97_CTL, &dwval); - - status = vid_blk_write_word(dev, AC97_CTL, - (dwval | FLD_AC97_UP2X_BYPASS)); - - /* select the parallel1 and SRC3 */ - status = vid_blk_write_word(dev, BAND_OUT_SEL, - cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) | - cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) | - cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0)); - - /* unmute all, AC97 in, independence mode - adr 08d0, data 0x00063073 */ - status = vid_blk_write_word(dev, DL_CTL, 0x3000001); - status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073); - - /* set AVC maximum threshold, adr 08d4, dat ffff0024 */ - status = vid_blk_read_word(dev, PATH1_VOL_CTL, &dwval); - status = vid_blk_write_word(dev, PATH1_VOL_CTL, - (dwval | FLD_PATH1_AVC_THRESHOLD)); - - /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */ - status = vid_blk_read_word(dev, PATH1_SC_CTL, &dwval); - status = vid_blk_write_word(dev, PATH1_SC_CTL, - (dwval | FLD_PATH1_SC_THRESHOLD)); - break; - - case AUDIO_INPUT_TUNER_TV: - default: - status = stopAudioFirmware(dev); - /* Setup SRC sources and clocks */ - status = vid_blk_write_word(dev, BAND_OUT_SEL, - cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) | - cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) | - cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) | - cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) | - cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) | - cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) | - cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) | - cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) | - cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) | - cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) | - cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) | - cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) | - cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01)); - - /* Setup the AUD_IO control */ - status = vid_blk_write_word(dev, AUD_IO_CTRL, - cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) | - cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) | - cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) | - cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) | - cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03)); - - status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870); - - /* setAudioStandard(_audio_standard); */ - status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870); - - status = restartAudioFirmware(dev); - - switch (dev->board.tuner_type) { - case TUNER_XC5000: - /* SIF passthrough at 28.6363 MHz sample rate */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - CHIP_CTRL, - FLD_SIF_EN, - cx231xx_set_field(FLD_SIF_EN, 1)); - break; - case TUNER_NXP_TDA18271: - /* Normal mode: SIF passthrough at 14.32 MHz */ - status = cx231xx_read_modify_write_i2c_dword(dev, - VID_BLK_I2C_ADDRESS, - CHIP_CTRL, - FLD_SIF_EN, - cx231xx_set_field(FLD_SIF_EN, 0)); - break; - default: - /* This is just a casual suggestion to people adding - new boards in case they use a tuner type we don't - currently know about */ - printk(KERN_INFO "Unknown tuner type configuring SIF"); - break; - } - break; - - case AUDIO_INPUT_TUNER_FM: - /* use SIF for FM radio - setupFM(); - setAudioStandard(_audio_standard); - */ - break; - - case AUDIO_INPUT_MUTE: - status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F011012); - break; - } - - /* Take it out of soft reset */ - status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl); - gen_ctrl &= ~1; - status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl); - - return status; -} - -/****************************************************************************** - * C H I P Specific C O N T R O L functions * - ******************************************************************************/ -int cx231xx_init_ctrl_pin_status(struct cx231xx *dev) -{ - u32 value; - int status = 0; - - status = vid_blk_read_word(dev, PIN_CTRL, &value); - value |= (~dev->board.ctl_pin_status_mask); - status = vid_blk_write_word(dev, PIN_CTRL, value); - - return status; -} - -int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, - u8 analog_or_digital) -{ - int status = 0; - - /* first set the direction to output */ - status = cx231xx_set_gpio_direction(dev, - dev->board. - agc_analog_digital_select_gpio, 1); - - /* 0 - demod ; 1 - Analog mode */ - status = cx231xx_set_gpio_value(dev, - dev->board.agc_analog_digital_select_gpio, - analog_or_digital); - - return status; -} - -int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3) -{ - u8 value[4] = { 0, 0, 0, 0 }; - int status = 0; - bool current_is_port_3; - - if (dev->board.dont_use_port_3) - is_port_3 = false; - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, - PWR_CTL_EN, value, 4); - if (status < 0) - return status; - - current_is_port_3 = value[0] & I2C_DEMOD_EN ? true : false; - - /* Just return, if already using the right port */ - if (current_is_port_3 == is_port_3) - return 0; - - if (is_port_3) - value[0] |= I2C_DEMOD_EN; - else - value[0] &= ~I2C_DEMOD_EN; - - cx231xx_info("Changing the i2c master port to %d\n", - is_port_3 ? 3 : 1); - - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - - return status; - -} -EXPORT_SYMBOL_GPL(cx231xx_enable_i2c_port_3); - -void update_HH_register_after_set_DIF(struct cx231xx *dev) -{ -/* - u8 status = 0; - u32 value = 0; - - vid_blk_write_word(dev, PIN_CTRL, 0xA0FFF82F); - vid_blk_write_word(dev, DIF_MISC_CTRL, 0x0A203F11); - vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0x1BEFBF06); - - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); - vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390); - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); -*/ -} - -void cx231xx_dump_HH_reg(struct cx231xx *dev) -{ - u32 value = 0; - u16 i = 0; - - value = 0x45005390; - vid_blk_write_word(dev, 0x104, value); - - for (i = 0x100; i < 0x140; i++) { - vid_blk_read_word(dev, i, &value); - cx231xx_info("reg0x%x=0x%x\n", i, value); - i = i+3; - } - - for (i = 0x300; i < 0x400; i++) { - vid_blk_read_word(dev, i, &value); - cx231xx_info("reg0x%x=0x%x\n", i, value); - i = i+3; - } - - for (i = 0x400; i < 0x440; i++) { - vid_blk_read_word(dev, i, &value); - cx231xx_info("reg0x%x=0x%x\n", i, value); - i = i+3; - } - - vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); - cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); - vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390); - vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); - cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); -} - -void cx231xx_dump_SC_reg(struct cx231xx *dev) -{ - u8 value[4] = { 0, 0, 0, 0 }; - cx231xx_info("cx231xx_dump_SC_reg!\n"); - - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0], - value[1], value[2], value[3]); - - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0], - value[1], value[2], value[3]); - - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0], - value[1], value[2], value[3]); - - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0], - value[1], value[2], value[3]); - - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0], - value[1], value[2], value[3]); - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, - value, 4); - cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0], - value[1], value[2], value[3]); - - -} - -void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) - -{ - u8 value = 0; - - afe_read_byte(dev, ADC_STATUS2_CH3, &value); - value = (value & 0xFE)|0x01; - afe_write_byte(dev, ADC_STATUS2_CH3, value); - - afe_read_byte(dev, ADC_STATUS2_CH3, &value); - value = (value & 0xFE)|0x00; - afe_write_byte(dev, ADC_STATUS2_CH3, value); - - -/* - config colibri to lo-if mode - - FIXME: ntf_mode = 2'b00 by default. But set 0x1 would reduce - the diff IF input by half, - - for low-if agc defect -*/ - - afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); - value = (value & 0xFC)|0x00; - afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); - - afe_read_byte(dev, ADC_INPUT_CH3, &value); - value = (value & 0xF9)|0x02; - afe_write_byte(dev, ADC_INPUT_CH3, value); - - afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); - value = (value & 0xFB)|0x04; - afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); - - afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); - value = (value & 0xFC)|0x03; - afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); - - afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); - value = (value & 0xFB)|0x04; - afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); - - afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); - value = (value & 0xF8)|0x06; - afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - - afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); - value = (value & 0x8F)|0x40; - afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - - afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); - value = (value & 0xDF)|0x20; - afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); -} - -void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, - u8 spectral_invert, u32 mode) -{ - u32 colibri_carrier_offset = 0; - u32 func_mode = 0x01; /* Device has a DIF if this function is called */ - u32 standard = 0; - u8 value[4] = { 0, 0, 0, 0 }; - - cx231xx_info("Enter cx231xx_set_Colibri_For_LowIF()\n"); - value[0] = (u8) 0x6F; - value[1] = (u8) 0x6F; - value[2] = (u8) 0x6F; - value[3] = (u8) 0x6F; - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - - /*Set colibri for low IF*/ - cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); - - /* Set C2HH for low IF operation.*/ - standard = dev->norm; - cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, - func_mode, standard); - - /* Get colibri offsets.*/ - colibri_carrier_offset = cx231xx_Get_Colibri_CarrierOffset(mode, - standard); - - cx231xx_info("colibri_carrier_offset=%d, standard=0x%x\n", - colibri_carrier_offset, standard); - - /* Set the band Pass filter for DIF*/ - cx231xx_set_DIF_bandpass(dev, (if_freq+colibri_carrier_offset), - spectral_invert, mode); -} - -u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd) -{ - u32 colibri_carrier_offset = 0; - - if (mode == TUNER_MODE_FM_RADIO) { - colibri_carrier_offset = 1100000; - } else if (standerd & (V4L2_STD_MN | V4L2_STD_NTSC_M_JP)) { - colibri_carrier_offset = 4832000; /*4.83MHz */ - } else if (standerd & (V4L2_STD_PAL_B | V4L2_STD_PAL_G)) { - colibri_carrier_offset = 2700000; /*2.70MHz */ - } else if (standerd & (V4L2_STD_PAL_D | V4L2_STD_PAL_I - | V4L2_STD_SECAM)) { - colibri_carrier_offset = 2100000; /*2.10MHz */ - } - - return colibri_carrier_offset; -} - -void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, - u8 spectral_invert, u32 mode) -{ - unsigned long pll_freq_word; - u32 dif_misc_ctrl_value = 0; - u64 pll_freq_u64 = 0; - u32 i = 0; - - cx231xx_info("if_freq=%d;spectral_invert=0x%x;mode=0x%x\n", - if_freq, spectral_invert, mode); - - - if (mode == TUNER_MODE_FM_RADIO) { - pll_freq_word = 0x905A1CAC; - vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); - - } else /*KSPROPERTY_TUNER_MODE_TV*/{ - /* Calculate the PLL frequency word based on the adjusted if_freq*/ - pll_freq_word = if_freq; - pll_freq_u64 = (u64)pll_freq_word << 28L; - do_div(pll_freq_u64, 50000000); - pll_freq_word = (u32)pll_freq_u64; - /*pll_freq_word = 0x3463497;*/ - vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); - - if (spectral_invert) { - if_freq -= 400000; - /* Enable Spectral Invert*/ - vid_blk_read_word(dev, DIF_MISC_CTRL, - &dif_misc_ctrl_value); - dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000; - vid_blk_write_word(dev, DIF_MISC_CTRL, - dif_misc_ctrl_value); - } else { - if_freq += 400000; - /* Disable Spectral Invert*/ - vid_blk_read_word(dev, DIF_MISC_CTRL, - &dif_misc_ctrl_value); - dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF; - vid_blk_write_word(dev, DIF_MISC_CTRL, - dif_misc_ctrl_value); - } - - if_freq = (if_freq/100000)*100000; - - if (if_freq < 3000000) - if_freq = 3000000; - - if (if_freq > 16000000) - if_freq = 16000000; - } - - cx231xx_info("Enter IF=%zd\n", - ARRAY_SIZE(Dif_set_array)); - for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) { - if (Dif_set_array[i].if_freq == if_freq) { - vid_blk_write_word(dev, - Dif_set_array[i].register_address, Dif_set_array[i].value); - } - } -} - -/****************************************************************************** - * D I F - B L O C K C O N T R O L functions * - ******************************************************************************/ -int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, - u32 function_mode, u32 standard) -{ - int status = 0; - - - if (mode == V4L2_TUNER_RADIO) { - /* C2HH */ - /* lo if big signal */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); - /* FUNC_MODE = DIF */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode); - /* IF_MODE */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF); - /* no inv */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); - } else if (standard != DIF_USE_BASEBAND) { - if (standard & V4L2_STD_MN) { - /* lo if big signal */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); - /* FUNC_MODE = DIF */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 23, 24, - function_mode); - /* IF_MODE */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb); - /* no inv */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); - /* 0x124, AUD_CHAN1_SRC = 0x3 */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AUD_IO_CTRL, 0, 31, 0x00000003); - } else if ((standard == V4L2_STD_PAL_I) | - (standard & V4L2_STD_PAL_D) | - (standard & V4L2_STD_SECAM)) { - /* C2HH setup */ - /* lo if big signal */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); - /* FUNC_MODE = DIF */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 23, 24, - function_mode); - /* IF_MODE */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF); - /* no inv */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); - } else { - /* default PAL BG */ - /* C2HH setup */ - /* lo if big signal */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); - /* FUNC_MODE = DIF */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 23, 24, - function_mode); - /* IF_MODE */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE); - /* no inv */ - status = cx231xx_reg_mask_write(dev, - VID_BLK_I2C_ADDRESS, 32, - AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); - } - } - - return status; -} - -int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard) -{ - int status = 0; - u32 dif_misc_ctrl_value = 0; - u32 func_mode = 0; - - cx231xx_info("%s: setStandard to %x\n", __func__, standard); - - status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); - if (standard != DIF_USE_BASEBAND) - dev->norm = standard; - - switch (dev->model) { - case CX231XX_BOARD_CNXT_CARRAERA: - case CX231XX_BOARD_CNXT_RDE_250: - case CX231XX_BOARD_CNXT_SHELBY: - case CX231XX_BOARD_CNXT_RDU_250: - case CX231XX_BOARD_CNXT_VIDEO_GRABBER: - case CX231XX_BOARD_HAUPPAUGE_EXETER: - func_mode = 0x03; - break; - case CX231XX_BOARD_CNXT_RDE_253S: - case CX231XX_BOARD_CNXT_RDU_253S: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: - func_mode = 0x01; - break; - default: - func_mode = 0x01; - } - - status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, - func_mode, standard); - - if (standard == DIF_USE_BASEBAND) { /* base band */ - /* There is a different SRC_PHASE_INC value - for baseband vs. DIF */ - status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0xDF7DF83); - status = vid_blk_read_word(dev, DIF_MISC_CTRL, - &dif_misc_ctrl_value); - dif_misc_ctrl_value |= FLD_DIF_DIF_BYPASS; - status = vid_blk_write_word(dev, DIF_MISC_CTRL, - dif_misc_ctrl_value); - } else if (standard & V4L2_STD_PAL_D) { - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL, 0, 31, 0x6503bc0c); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL1, 0, 31, 0xbd038c85); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL2, 0, 31, 0x1db4640a); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL3, 0, 31, 0x00008800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_REF, 0, 31, 0x444C1380); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_INT_CURRENT, 0, 31, - 0x26001700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_RF_CURRENT, 0, 31, - 0x00002660); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VIDEO_AGC_CTRL, 0, 31, - 0x72500800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VID_AUD_OVERRIDE, 0, 31, - 0x27000100); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AV_SEP_CTRL, 0, 31, 0x3F3934EA); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_COMP_FLT_CTRL, 0, 31, - 0x00000000); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_PHASE_INC, 0, 31, - 0x1befbf06); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_GAIN_CONTROL, 0, 31, - 0x000035e8); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_RPT_VARIANCE, 0, 31, 0x00000000); - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value |= 0x3a023F11; - } else if (standard & V4L2_STD_PAL_I) { - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL, 0, 31, 0x6503bc0c); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL1, 0, 31, 0xbd038c85); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL2, 0, 31, 0x1db4640a); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL3, 0, 31, 0x00008800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_REF, 0, 31, 0x444C1380); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_INT_CURRENT, 0, 31, - 0x26001700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_RF_CURRENT, 0, 31, - 0x00002660); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VIDEO_AGC_CTRL, 0, 31, - 0x72500800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VID_AUD_OVERRIDE, 0, 31, - 0x27000100); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AV_SEP_CTRL, 0, 31, 0x5F39A934); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_COMP_FLT_CTRL, 0, 31, - 0x00000000); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_PHASE_INC, 0, 31, - 0x1befbf06); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_GAIN_CONTROL, 0, 31, - 0x000035e8); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_RPT_VARIANCE, 0, 31, 0x00000000); - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value |= 0x3a033F11; - } else if (standard & V4L2_STD_PAL_M) { - /* improved Low Frequency Phase Noise */ - status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C); - status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85); - status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a); - status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); - status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380); - status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, - 0x26001700); - status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, - 0x00002660); - status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, - 0x72500800); - status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, - 0x27000100); - status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x012c405d); - status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, - 0x009f50c1); - status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, - 0x1befbf06); - status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, - 0x000035e8); - status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB, - 0x00000000); - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value |= 0x3A0A3F10; - } else if (standard & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { - /* improved Low Frequency Phase Noise */ - status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C); - status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85); - status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a); - status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); - status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380); - status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, - 0x26001700); - status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, - 0x00002660); - status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, - 0x72500800); - status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, - 0x27000100); - status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, - 0x012c405d); - status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, - 0x009f50c1); - status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, - 0x1befbf06); - status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, - 0x000035e8); - status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB, - 0x00000000); - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value = 0x3A093F10; - } else if (standard & - (V4L2_STD_SECAM_B | V4L2_STD_SECAM_D | V4L2_STD_SECAM_G | - V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)) { - - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL, 0, 31, 0x6503bc0c); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL1, 0, 31, 0xbd038c85); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL2, 0, 31, 0x1db4640a); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL3, 0, 31, 0x00008800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_REF, 0, 31, 0x888C0380); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_INT_CURRENT, 0, 31, - 0x26001700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_RF_CURRENT, 0, 31, - 0x00002660); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VID_AUD_OVERRIDE, 0, 31, - 0x27000100); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_COMP_FLT_CTRL, 0, 31, - 0x00000000); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_PHASE_INC, 0, 31, - 0x1befbf06); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_GAIN_CONTROL, 0, 31, - 0x000035e8); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_RPT_VARIANCE, 0, 31, 0x00000000); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VIDEO_AGC_CTRL, 0, 31, - 0xf4000000); - - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value |= 0x3a023F11; - } else if (standard & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) { - /* Is it SECAM_L1? */ - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL, 0, 31, 0x6503bc0c); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL1, 0, 31, 0xbd038c85); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL2, 0, 31, 0x1db4640a); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL3, 0, 31, 0x00008800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_REF, 0, 31, 0x888C0380); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_INT_CURRENT, 0, 31, - 0x26001700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_RF_CURRENT, 0, 31, - 0x00002660); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VID_AUD_OVERRIDE, 0, 31, - 0x27000100); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_COMP_FLT_CTRL, 0, 31, - 0x00000000); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_PHASE_INC, 0, 31, - 0x1befbf06); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_GAIN_CONTROL, 0, 31, - 0x000035e8); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_RPT_VARIANCE, 0, 31, 0x00000000); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VIDEO_AGC_CTRL, 0, 31, - 0xf2560000); - - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value |= 0x3a023F11; - - } else if (standard & V4L2_STD_NTSC_M) { - /* V4L2_STD_NTSC_M (75 IRE Setup) Or - V4L2_STD_NTSC_M_JP (Japan, 0 IRE Setup) */ - - /* For NTSC the centre frequency of video coming out of - sidewinder is around 7.1MHz or 3.6MHz depending on the - spectral inversion. so for a non spectrally inverted channel - the pll freq word is 0x03420c49 - */ - - status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0x6503BC0C); - status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xBD038C85); - status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1DB4640A); - status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); - status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C0380); - status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, - 0x26001700); - status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, - 0x00002660); - status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, - 0x04000800); - status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, - 0x27000100); - status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x01296e1f); - - status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, - 0x009f50c1); - status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, - 0x1befbf06); - status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, - 0x000035e8); - - status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600); - status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT, - 0xC2262600); - status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600); - - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value |= 0x3a003F10; - } else { - /* default PAL BG */ - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL, 0, 31, 0x6503bc0c); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL1, 0, 31, 0xbd038c85); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL2, 0, 31, 0x1db4640a); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_PLL_CTRL3, 0, 31, 0x00008800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_REF, 0, 31, 0x444C1380); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_IF_INT_CURRENT, 0, 31, - 0x26001700); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AGC_RF_CURRENT, 0, 31, - 0x00002660); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VIDEO_AGC_CTRL, 0, 31, - 0x72500800); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_VID_AUD_OVERRIDE, 0, 31, - 0x27000100); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_AV_SEP_CTRL, 0, 31, 0x3F3530EC); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_COMP_FLT_CTRL, 0, 31, - 0x00A653A8); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_PHASE_INC, 0, 31, - 0x1befbf06); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_SRC_GAIN_CONTROL, 0, 31, - 0x000035e8); - status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, - DIF_RPT_VARIANCE, 0, 31, 0x00000000); - /* Save the Spec Inversion value */ - dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; - dif_misc_ctrl_value |= 0x3a013F11; - } - - /* The AGC values should be the same for all standards, - AUD_SRC_SEL[19] should always be disabled */ - dif_misc_ctrl_value &= ~FLD_DIF_AUD_SRC_SEL; - - /* It is still possible to get Set Standard calls even when we - are in FM mode. - This is done to override the value for FM. */ - if (dev->active_mode == V4L2_TUNER_RADIO) - dif_misc_ctrl_value = 0x7a080000; - - /* Write the calculated value for misc ontrol register */ - status = vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); - - return status; -} - -int cx231xx_tuner_pre_channel_change(struct cx231xx *dev) -{ - int status = 0; - u32 dwval; - - /* Set the RF and IF k_agc values to 3 */ - status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval); - dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); - dwval |= 0x33000000; - - status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval); - - return status; -} - -int cx231xx_tuner_post_channel_change(struct cx231xx *dev) -{ - int status = 0; - u32 dwval; - cx231xx_info("cx231xx_tuner_post_channel_change dev->tuner_type =0%d\n", - dev->tuner_type); - /* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for - * SECAM L/B/D standards */ - status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval); - dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); - - if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B | - V4L2_STD_SECAM_D)) { - if (dev->tuner_type == TUNER_NXP_TDA18271) { - dwval &= ~FLD_DIF_IF_REF; - dwval |= 0x88000300; - } else - dwval |= 0x88000000; - } else { - if (dev->tuner_type == TUNER_NXP_TDA18271) { - dwval &= ~FLD_DIF_IF_REF; - dwval |= 0xCC000300; - } else - dwval |= 0x44000000; - } - - status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval); - - return status; -} - -/****************************************************************************** - * I 2 S - B L O C K C O N T R O L functions * - ******************************************************************************/ -int cx231xx_i2s_blk_initialize(struct cx231xx *dev) -{ - int status = 0; - u32 value; - - status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL1, 1, &value, 1); - /* enables clock to delta-sigma and decimation filter */ - value |= 0x80; - status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL1, 1, value, 1); - /* power up all channel */ - status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL2, 1, 0x00, 1); - - return status; -} - -int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev, - enum AV_MODE avmode) -{ - int status = 0; - u32 value = 0; - - if (avmode != POLARIS_AVMODE_ENXTERNAL_AV) { - status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL2, 1, &value, 1); - value |= 0xfe; - status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL2, 1, value, 1); - } else { - status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL2, 1, 0x00, 1); - } - - return status; -} - -/* set i2s_blk for audio input types */ -int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input) -{ - int status = 0; - - switch (audio_input) { - case CX231XX_AMUX_LINE_IN: - status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL2, 1, 0x00, 1); - status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - CH_PWR_CTRL1, 1, 0x80, 1); - break; - case CX231XX_AMUX_VIDEO: - default: - break; - } - - dev->ctl_ainput = audio_input; - - return status; -} - -/****************************************************************************** - * P O W E R C O N T R O L functions * - ******************************************************************************/ -int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode) -{ - u8 value[4] = { 0, 0, 0, 0 }; - u32 tmp = 0; - int status = 0; - - if (dev->power_mode != mode) - dev->power_mode = mode; - else { - cx231xx_info(" setPowerMode::mode = %d, No Change req.\n", - mode); - return 0; - } - - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, - 4); - if (status < 0) - return status; - - tmp = *((u32 *) value); - - switch (mode) { - case POLARIS_AVMODE_ENXTERNAL_AV: - - tmp &= (~PWR_MODE_MASK); - - tmp |= PWR_AV_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - - tmp |= PWR_ISO_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, - value, 4); - msleep(PWR_SLEEP_INTERVAL); - - tmp |= POLARIS_AVMODE_ENXTERNAL_AV; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - - /* reset state of xceive tuner */ - dev->xc_fw_load_done = 0; - break; - - case POLARIS_AVMODE_ANALOGT_TV: - - tmp |= PWR_DEMOD_EN; - tmp |= (I2C_DEMOD_EN); - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - - if (!(tmp & PWR_TUNER_EN)) { - tmp |= (PWR_TUNER_EN); - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - - if (!(tmp & PWR_AV_EN)) { - tmp |= PWR_AV_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - if (!(tmp & PWR_ISO_EN)) { - tmp |= PWR_ISO_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - - if (!(tmp & POLARIS_AVMODE_ANALOGT_TV)) { - tmp |= POLARIS_AVMODE_ANALOGT_TV; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - - if (dev->board.tuner_type != TUNER_ABSENT) { - /* Enable tuner */ - cx231xx_enable_i2c_port_3(dev, true); - - /* reset the Tuner */ - if (dev->board.tuner_gpio) - cx231xx_gpio_set(dev, dev->board.tuner_gpio); - - if (dev->cx231xx_reset_analog_tuner) - dev->cx231xx_reset_analog_tuner(dev); - } - - break; - - case POLARIS_AVMODE_DIGITAL: - if (!(tmp & PWR_TUNER_EN)) { - tmp |= (PWR_TUNER_EN); - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - if (!(tmp & PWR_AV_EN)) { - tmp |= PWR_AV_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - if (!(tmp & PWR_ISO_EN)) { - tmp |= PWR_ISO_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - - tmp &= (~PWR_AV_MODE); - tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - - if (!(tmp & PWR_DEMOD_EN)) { - tmp |= PWR_DEMOD_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - - if (dev->board.tuner_type != TUNER_ABSENT) { - /* - * Enable tuner - * Hauppauge Exeter seems to need to do something different! - */ - if (dev->model == CX231XX_BOARD_HAUPPAUGE_EXETER) - cx231xx_enable_i2c_port_3(dev, false); - else - cx231xx_enable_i2c_port_3(dev, true); - - /* reset the Tuner */ - if (dev->board.tuner_gpio) - cx231xx_gpio_set(dev, dev->board.tuner_gpio); - - if (dev->cx231xx_reset_analog_tuner) - dev->cx231xx_reset_analog_tuner(dev); - } - break; - - default: - break; - } - - msleep(PWR_SLEEP_INTERVAL); - - /* For power saving, only enable Pwr_resetout_n - when digital TV is selected. */ - if (mode == POLARIS_AVMODE_DIGITAL) { - tmp |= PWR_RESETOUT_EN; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(PWR_SLEEP_INTERVAL); - } - - /* update power control for afe */ - status = cx231xx_afe_update_power_control(dev, mode); - - /* update power control for i2s_blk */ - status = cx231xx_i2s_blk_update_power_control(dev, mode); - - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, - 4); - - return status; -} - -int cx231xx_power_suspend(struct cx231xx *dev) -{ - u8 value[4] = { 0, 0, 0, 0 }; - u32 tmp = 0; - int status = 0; - - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, - value, 4); - if (status > 0) - return status; - - tmp = *((u32 *) value); - tmp &= (~PWR_MODE_MASK); - - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, - value, 4); - - return status; -} - -/****************************************************************************** - * S T R E A M C O N T R O L functions * - ******************************************************************************/ -int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask) -{ - u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; - u32 tmp = 0; - int status = 0; - - cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, - value, 4); - if (status < 0) - return status; - - tmp = *((u32 *) value); - tmp |= ep_mask; - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, - value, 4); - - return status; -} - -int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask) -{ - u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; - u32 tmp = 0; - int status = 0; - - cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask); - status = - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4); - if (status < 0) - return status; - - tmp = *((u32 *) value); - tmp &= (~ep_mask); - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, - value, 4); - - return status; -} - -int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) -{ - int status = 0; - u32 value = 0; - u8 val[4] = { 0, 0, 0, 0 }; - - if (dev->udev->speed == USB_SPEED_HIGH) { - switch (media_type) { - case Audio: - cx231xx_info("%s: Audio enter HANC\n", __func__); - status = - cx231xx_mode_register(dev, TS_MODE_REG, 0x9300); - break; - - case Vbi: - cx231xx_info("%s: set vanc registers\n", __func__); - status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300); - break; - - case Sliced_cc: - cx231xx_info("%s: set hanc registers\n", __func__); - status = - cx231xx_mode_register(dev, TS_MODE_REG, 0x1300); - break; - - case Raw_Video: - cx231xx_info("%s: set video registers\n", __func__); - status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); - break; - - case TS1_serial_mode: - cx231xx_info("%s: set ts1 registers", __func__); - - if (dev->board.has_417) { - cx231xx_info(" MPEG\n"); - value &= 0xFFFFFFFC; - value |= 0x3; - - status = cx231xx_mode_register(dev, TS_MODE_REG, value); - - val[0] = 0x04; - val[1] = 0xA3; - val[2] = 0x3B; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); - - val[0] = 0x00; - val[1] = 0x08; - val[2] = 0x00; - val[3] = 0x08; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_LENGTH_REG, val, 4); - - } else { - cx231xx_info(" BDA\n"); - status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); - status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x010); - } - break; - - case TS1_parallel_mode: - cx231xx_info("%s: set ts1 parallel mode registers\n", - __func__); - status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); - status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400); - break; - } - } else { - status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); - } - - return status; -} - -int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) -{ - int rc = -1; - u32 ep_mask = -1; - struct pcb_config *pcb_config; - - /* get EP for media type */ - pcb_config = (struct pcb_config *)&dev->current_pcb_config; - - if (pcb_config->config_num) { - switch (media_type) { - case Raw_Video: - ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ - break; - case Audio: - ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ - break; - case Vbi: - ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ - break; - case Sliced_cc: - ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ - break; - case TS1_serial_mode: - case TS1_parallel_mode: - ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ - break; - case TS2: - ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ - break; - } - } - - if (start) { - rc = cx231xx_initialize_stream_xfer(dev, media_type); - - if (rc < 0) - return rc; - - /* enable video capture */ - if (ep_mask > 0) - rc = cx231xx_start_stream(dev, ep_mask); - } else { - /* disable video capture */ - if (ep_mask > 0) - rc = cx231xx_stop_stream(dev, ep_mask); - } - - if (dev->mode == CX231XX_ANALOG_MODE) - ;/* do any in Analog mode */ - else - ;/* do any in digital mode */ - - return rc; -} -EXPORT_SYMBOL_GPL(cx231xx_capture_start); - -/***************************************************************************** -* G P I O B I T control functions * -******************************************************************************/ -int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val) -{ - int status = 0; - - status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0); - - return status; -} - -int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val) -{ - int status = 0; - - status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1); - - return status; -} - -/* -* cx231xx_set_gpio_direction -* Sets the direction of the GPIO pin to input or output -* -* Parameters : -* pin_number : The GPIO Pin number to program the direction for -* from 0 to 31 -* pin_value : The Direction of the GPIO Pin under reference. -* 0 = Input direction -* 1 = Output direction -*/ -int cx231xx_set_gpio_direction(struct cx231xx *dev, - int pin_number, int pin_value) -{ - int status = 0; - u32 value = 0; - - /* Check for valid pin_number - if 32 , bail out */ - if (pin_number >= 32) - return -EINVAL; - - /* input */ - if (pin_value == 0) - value = dev->gpio_dir & (~(1 << pin_number)); /* clear */ - else - value = dev->gpio_dir | (1 << pin_number); - - status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val); - - /* cache the value for future */ - dev->gpio_dir = value; - - return status; -} - -/* -* cx231xx_set_gpio_value -* Sets the value of the GPIO pin to Logic high or low. The Pin under -* reference should ALREADY BE SET IN OUTPUT MODE !!!!!!!!! -* -* Parameters : -* pin_number : The GPIO Pin number to program the direction for -* pin_value : The value of the GPIO Pin under reference. -* 0 = set it to 0 -* 1 = set it to 1 -*/ -int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value) -{ - int status = 0; - u32 value = 0; - - /* Check for valid pin_number - if 0xFF , bail out */ - if (pin_number >= 32) - return -EINVAL; - - /* first do a sanity check - if the Pin is not output, make it output */ - if ((dev->gpio_dir & (1 << pin_number)) == 0x00) { - /* It was in input mode */ - value = dev->gpio_dir | (1 << pin_number); - dev->gpio_dir = value; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *) &dev->gpio_val); - value = 0; - } - - if (pin_value == 0) - value = dev->gpio_val & (~(1 << pin_number)); - else - value = dev->gpio_val | (1 << pin_number); - - /* store the value */ - dev->gpio_val = value; - - /* toggle bit0 of GP_IO */ - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - return status; -} - -/***************************************************************************** -* G P I O I2C related functions * -******************************************************************************/ -int cx231xx_gpio_i2c_start(struct cx231xx *dev) -{ - int status = 0; - - /* set SCL to output 1 ; set SDA to output 1 */ - dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; - dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; - - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - if (status < 0) - return -EINVAL; - - /* set SCL to output 1; set SDA to output 0 */ - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - if (status < 0) - return -EINVAL; - - /* set SCL to output 0; set SDA to output 0 */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - if (status < 0) - return -EINVAL; - - return status; -} - -int cx231xx_gpio_i2c_end(struct cx231xx *dev) -{ - int status = 0; - - /* set SCL to output 0; set SDA to output 0 */ - dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; - dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; - - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - if (status < 0) - return -EINVAL; - - /* set SCL to output 1; set SDA to output 0 */ - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - if (status < 0) - return -EINVAL; - - /* set SCL to input ,release SCL cable control - set SDA to input ,release SDA cable control */ - dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); - dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); - - status = - cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - if (status < 0) - return -EINVAL; - - return status; -} - -int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data) -{ - int status = 0; - u8 i; - - /* set SCL to output ; set SDA to output */ - dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; - dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; - - for (i = 0; i < 8; i++) { - if (((data << i) & 0x80) == 0) { - /* set SCL to output 0; set SDA to output 0 */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - - /* set SCL to output 1; set SDA to output 0 */ - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - - /* set SCL to output 0; set SDA to output 0 */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - } else { - /* set SCL to output 0; set SDA to output 1 */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - - /* set SCL to output 1; set SDA to output 1 */ - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - - /* set SCL to output 0; set SDA to output 1 */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - } - } - return status; -} - -int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf) -{ - u8 value = 0; - int status = 0; - u32 gpio_logic_value = 0; - u8 i; - - /* read byte */ - for (i = 0; i < 8; i++) { /* send write I2c addr */ - - /* set SCL to output 0; set SDA to input */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - - /* set SCL to output 1; set SDA to input */ - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - - /* get SDA data bit */ - gpio_logic_value = dev->gpio_val; - status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0) - value |= (1 << (8 - i - 1)); - - dev->gpio_val = gpio_logic_value; - } - - /* set SCL to output 0,finish the read latest SCL signal. - !!!set SDA to input, never to modify SDA direction at - the same times */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - /* store the value */ - *buf = value & 0xff; - - return status; -} - -int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) -{ - int status = 0; - u32 gpio_logic_value = 0; - int nCnt = 10; - int nInit = nCnt; - - /* clock stretch; set SCL to input; set SDA to input; - get SCL value till SCL = 1 */ - dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); - dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); - - gpio_logic_value = dev->gpio_val; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - do { - msleep(2); - status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, - (u8 *)&dev->gpio_val); - nCnt--; - } while (((dev->gpio_val & - (1 << dev->board.tuner_scl_gpio)) == 0) && - (nCnt > 0)); - - if (nCnt == 0) - cx231xx_info("No ACK after %d msec -GPIO I2C failed!", - nInit * 10); - - /* - * readAck - * through clock stretch, slave has given a SCL signal, - * so the SDA data can be directly read. - */ - status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) { - dev->gpio_val = gpio_logic_value; - dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - status = 0; - } else { - dev->gpio_val = gpio_logic_value; - dev->gpio_val |= (1 << dev->board.tuner_sda_gpio); - } - - /* read SDA end, set the SCL to output 0, after this operation, - SDA direction can be changed. */ - dev->gpio_val = gpio_logic_value; - dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio); - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - return status; -} - -int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev) -{ - int status = 0; - - /* set SDA to ouput */ - dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - /* set SCL = 0 (output); set SDA = 0 (output) */ - dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - /* set SCL = 1 (output); set SDA = 0 (output) */ - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - /* set SCL = 0 (output); set SDA = 0 (output) */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - /* set SDA to input,and then the slave will read data from SDA. */ - dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - return status; -} - -int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev) -{ - int status = 0; - - /* set scl to output ; set sda to input */ - dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; - dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - /* set scl to output 0; set sda to input */ - dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - /* set scl to output 1; set sda to input */ - dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; - status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); - - return status; -} - -/***************************************************************************** -* G P I O I2C related functions * -******************************************************************************/ -/* cx231xx_gpio_i2c_read - * Function to read data from gpio based I2C interface - */ -int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) -{ - int status = 0; - int i = 0; - - /* get the lock */ - mutex_lock(&dev->gpio_i2c_lock); - - /* start */ - status = cx231xx_gpio_i2c_start(dev); - - /* write dev_addr */ - status = cx231xx_gpio_i2c_write_byte(dev, (dev_addr << 1) + 1); - - /* readAck */ - status = cx231xx_gpio_i2c_read_ack(dev); - - /* read data */ - for (i = 0; i < len; i++) { - /* read data */ - buf[i] = 0; - status = cx231xx_gpio_i2c_read_byte(dev, &buf[i]); - - if ((i + 1) != len) { - /* only do write ack if we more length */ - status = cx231xx_gpio_i2c_write_ack(dev); - } - } - - /* write NAK - inform reads are complete */ - status = cx231xx_gpio_i2c_write_nak(dev); - - /* write end */ - status = cx231xx_gpio_i2c_end(dev); - - /* release the lock */ - mutex_unlock(&dev->gpio_i2c_lock); - - return status; -} - -/* cx231xx_gpio_i2c_write - * Function to write data to gpio based I2C interface - */ -int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) -{ - int i = 0; - - /* get the lock */ - mutex_lock(&dev->gpio_i2c_lock); - - /* start */ - cx231xx_gpio_i2c_start(dev); - - /* write dev_addr */ - cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); - - /* read Ack */ - cx231xx_gpio_i2c_read_ack(dev); - - for (i = 0; i < len; i++) { - /* Write data */ - cx231xx_gpio_i2c_write_byte(dev, buf[i]); - - /* read Ack */ - cx231xx_gpio_i2c_read_ack(dev); - } - - /* write End */ - cx231xx_gpio_i2c_end(dev); - - /* release the lock */ - mutex_unlock(&dev->gpio_i2c_lock); - - return 0; -} diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c deleted file mode 100644 index 02d4d36735d3..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ /dev/null @@ -1,1370 +0,0 @@ -/* - cx231xx-cards.c - driver for Conexant Cx23100/101/102 - USB video capture devices - - Copyright (C) 2008 - Based on em28xx driver - - This program is free software; 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 -#include - -#include -#include "dvb-usb-ids.h" -#include "xc5000.h" -#include "tda18271.h" - -#include "cx231xx.h" - -static int tuner = -1; -module_param(tuner, int, 0444); -MODULE_PARM_DESC(tuner, "tuner type"); - -static int transfer_mode = 1; -module_param(transfer_mode, int, 0444); -MODULE_PARM_DESC(transfer_mode, "transfer mode (1-ISO or 0-BULK)"); - -static unsigned int disable_ir; -module_param(disable_ir, int, 0444); -MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); - -/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */ -static unsigned long cx231xx_devused; - -/* - * Reset sequences for analog/digital modes - */ - -static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = { - {0x03, 0x01, 10}, - {0x03, 0x00, 30}, - {0x03, 0x01, 10}, - {-1, -1, -1}, -}; - -/* - * Board definitions - */ -struct cx231xx_board cx231xx_boards[] = { - [CX231XX_BOARD_UNKNOWN] = { - .name = "Unknown CX231xx video grabber", - .tuner_type = TUNER_ABSENT, - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } - }, - }, - [CX231XX_BOARD_CNXT_CARRAERA] = { - .name = "Conexant Hybrid TV - CARRAERA", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0x61, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .demod_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x02, - .norm = V4L2_STD_PAL, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } - }, - }, - [CX231XX_BOARD_CNXT_SHELBY] = { - .name = "Conexant Hybrid TV - SHELBY", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0x61, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .demod_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x32, - .norm = V4L2_STD_NTSC, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } - }, - }, - [CX231XX_BOARD_CNXT_RDE_253S] = { - .name = "Conexant Hybrid TV - RDE253S", - .tuner_type = TUNER_NXP_TDA18271, - .tuner_addr = 0x60, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x1c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .demod_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x02, - .norm = V4L2_STD_PAL, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } - }, - }, - - [CX231XX_BOARD_CNXT_RDU_253S] = { - .name = "Conexant Hybrid TV - RDU253S", - .tuner_type = TUNER_NXP_TDA18271, - .tuner_addr = 0x60, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x1c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .demod_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x02, - .norm = V4L2_STD_PAL, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } - }, - }, - [CX231XX_BOARD_CNXT_VIDEO_GRABBER] = { - .name = "Conexant VIDEO GRABBER", - .tuner_type = TUNER_ABSENT, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x1c, - .gpio_pin_status_mask = 0x4001000, - .norm = V4L2_STD_PAL, - .no_alt_vanc = 1, - .external_av = 1, - .has_417 = 1, - - .input = {{ - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } - }, - }, - [CX231XX_BOARD_CNXT_RDE_250] = { - .name = "Conexant Hybrid TV - rde 250", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0x61, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .demod_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x02, - .norm = V4L2_STD_PAL, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - } - }, - }, - [CX231XX_BOARD_CNXT_RDU_250] = { - .name = "Conexant Hybrid TV - RDU 250", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0x61, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .demod_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x32, - .norm = V4L2_STD_NTSC, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - } - }, - }, - [CX231XX_BOARD_HAUPPAUGE_EXETER] = { - .name = "Hauppauge EXETER", - .tuner_type = TUNER_NXP_TDA18271, - .tuner_addr = 0x60, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .demod_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x0e, - .norm = V4L2_STD_NTSC, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } }, - }, - [CX231XX_BOARD_HAUPPAUGE_USBLIVE2] = { - .name = "Hauppauge USB Live 2", - .tuner_type = TUNER_ABSENT, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .norm = V4L2_STD_NTSC, - .no_alt_vanc = 1, - .external_av = 1, - .dont_use_port_3 = 1, - .input = {{ - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } }, - }, - [CX231XX_BOARD_KWORLD_UB430_USB_HYBRID] = { - .name = "Kworld UB430 USB Hybrid", - .tuner_type = TUNER_NXP_TDA18271, - .tuner_addr = 0x60, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x11, /* According with PV cxPolaris.inf file */ - .tuner_sif_gpio = -1, - .tuner_scl_gpio = -1, - .tuner_sda_gpio = -1, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 2, - .demod_i2c_master = 1, - .ir_i2c_master = 2, - .has_dvb = 1, - .demod_addr = 0x10, - .norm = V4L2_STD_PAL_M, - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } }, - }, - [CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = { - .name = "Pixelview PlayTV USB Hybrid", - .tuner_type = TUNER_NXP_TDA18271, - .tuner_addr = 0x60, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */ - .tuner_sif_gpio = -1, - .tuner_scl_gpio = -1, - .tuner_sda_gpio = -1, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 2, - .demod_i2c_master = 1, - .ir_i2c_master = 2, - .rc_map_name = RC_MAP_PIXELVIEW_002T, - .has_dvb = 1, - .demod_addr = 0x10, - .norm = V4L2_STD_PAL_M, - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } }, - }, - [CX231XX_BOARD_PV_XCAPTURE_USB] = { - .name = "Pixelview Xcapture USB", - .tuner_type = TUNER_ABSENT, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .norm = V4L2_STD_NTSC, - .no_alt_vanc = 1, - .external_av = 1, - .dont_use_port_3 = 1, - - .input = {{ - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } - }, - }, - - [CX231XX_BOARD_ICONBIT_U100] = { - .name = "Iconbit Analog Stick U100 FM", - .tuner_type = TUNER_ABSENT, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .demod_xfer_mode = 0, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x1C, - .gpio_pin_status_mask = 0x4001000, - - .input = {{ - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } }, - }, - [CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL] = { - .name = "Hauppauge WinTV USB2 FM (PAL)", - .tuner_type = TUNER_NXP_TDA18271, - .tuner_addr = 0x60, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .norm = V4L2_STD_PAL, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } }, - }, - [CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC] = { - .name = "Hauppauge WinTV USB2 FM (NTSC)", - .tuner_type = TUNER_NXP_TDA18271, - .tuner_addr = 0x60, - .tuner_gpio = RDE250_XCV_TUNER, - .tuner_sif_gpio = 0x05, - .tuner_scl_gpio = 0x1a, - .tuner_sda_gpio = 0x1b, - .decoder = CX231XX_AVDECODER, - .output_mode = OUT_MODE_VIP11, - .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x0c, - .gpio_pin_status_mask = 0x4001000, - .tuner_i2c_master = 1, - .norm = V4L2_STD_NTSC, - - .input = {{ - .type = CX231XX_VMUX_TELEVISION, - .vmux = CX231XX_VIN_3_1, - .amux = CX231XX_AMUX_VIDEO, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_COMPOSITE1, - .vmux = CX231XX_VIN_2_1, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - }, { - .type = CX231XX_VMUX_SVIDEO, - .vmux = CX231XX_VIN_1_1 | - (CX231XX_VIN_1_2 << 8) | - CX25840_SVIDEO_ON, - .amux = CX231XX_AMUX_LINE_IN, - .gpio = NULL, - } }, - }, -}; -const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); - -/* table of devices that work with this driver */ -struct usb_device_id cx231xx_id_table[] = { - {USB_DEVICE(0x0572, 0x5A3C), - .driver_info = CX231XX_BOARD_UNKNOWN}, - {USB_DEVICE(0x0572, 0x58A2), - .driver_info = CX231XX_BOARD_CNXT_CARRAERA}, - {USB_DEVICE(0x0572, 0x58A1), - .driver_info = CX231XX_BOARD_CNXT_SHELBY}, - {USB_DEVICE(0x0572, 0x58A4), - .driver_info = CX231XX_BOARD_CNXT_RDE_253S}, - {USB_DEVICE(0x0572, 0x58A5), - .driver_info = CX231XX_BOARD_CNXT_RDU_253S}, - {USB_DEVICE(0x0572, 0x58A6), - .driver_info = CX231XX_BOARD_CNXT_VIDEO_GRABBER}, - {USB_DEVICE(0x0572, 0x589E), - .driver_info = CX231XX_BOARD_CNXT_RDE_250}, - {USB_DEVICE(0x0572, 0x58A0), - .driver_info = CX231XX_BOARD_CNXT_RDU_250}, - {USB_DEVICE(0x2040, 0xb110), - .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL}, - {USB_DEVICE(0x2040, 0xb111), - .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC}, - {USB_DEVICE(0x2040, 0xb120), - .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, - {USB_DEVICE(0x2040, 0xb140), - .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, - {USB_DEVICE(0x2040, 0xc200), - .driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2}, - {USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001), - .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID}, - {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014), - .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB}, - {USB_DEVICE(0x1b80, 0xe424), - .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID}, - {USB_DEVICE(0x1f4d, 0x0237), - .driver_info = CX231XX_BOARD_ICONBIT_U100}, - {}, -}; - -MODULE_DEVICE_TABLE(usb, cx231xx_id_table); - -/* cx231xx_tuner_callback - * will be used to reset XC5000 tuner using GPIO pin - */ - -int cx231xx_tuner_callback(void *ptr, int component, int command, int arg) -{ - int rc = 0; - struct cx231xx *dev = ptr; - - if (dev->tuner_type == TUNER_XC5000) { - if (command == XC5000_TUNER_RESET) { - cx231xx_info - ("Tuner CB: RESET: cmd %d : tuner type %d \n", - command, dev->tuner_type); - cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, - 1); - msleep(10); - cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, - 0); - msleep(330); - cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, - 1); - msleep(10); - } - } else if (dev->tuner_type == TUNER_NXP_TDA18271) { - switch (command) { - case TDA18271_CALLBACK_CMD_AGC_ENABLE: - if (dev->model == CX231XX_BOARD_PV_PLAYTV_USB_HYBRID) - rc = cx231xx_set_agc_analog_digital_mux_select(dev, arg); - break; - default: - rc = -EINVAL; - break; - } - } - return rc; -} -EXPORT_SYMBOL_GPL(cx231xx_tuner_callback); - -void cx231xx_reset_out(struct cx231xx *dev) -{ - cx231xx_set_gpio_value(dev, CX23417_RESET, 1); - msleep(200); - cx231xx_set_gpio_value(dev, CX23417_RESET, 0); - msleep(200); - cx231xx_set_gpio_value(dev, CX23417_RESET, 1); -} -void cx231xx_enable_OSC(struct cx231xx *dev) -{ - cx231xx_set_gpio_value(dev, CX23417_OSC_EN, 1); -} -void cx231xx_sleep_s5h1432(struct cx231xx *dev) -{ - cx231xx_set_gpio_value(dev, SLEEP_S5H1432, 0); -} - -static inline void cx231xx_set_model(struct cx231xx *dev) -{ - memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board)); -} - -/* Since cx231xx_pre_card_setup() requires a proper dev->model, - * this won't work for boards with generic PCI IDs - */ -void cx231xx_pre_card_setup(struct cx231xx *dev) -{ - - cx231xx_set_model(dev); - - cx231xx_info("Identified as %s (card=%d)\n", - dev->board.name, dev->model); - - /* set the direction for GPIO pins */ - if (dev->board.tuner_gpio) { - cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1); - cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1); - } - if (dev->board.tuner_sif_gpio >= 0) - cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1); - - /* request some modules if any required */ - - /* set the mode to Analog mode initially */ - cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); - - /* Unlock device */ - /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */ - -} - -static void cx231xx_config_tuner(struct cx231xx *dev) -{ - struct tuner_setup tun_setup; - struct v4l2_frequency f; - - if (dev->tuner_type == TUNER_ABSENT) - return; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - tun_setup.tuner_callback = cx231xx_tuner_callback; - - tuner_call(dev, tuner, s_type_addr, &tun_setup); - -#if 0 - if (tun_setup.type == TUNER_XC5000) { - static struct xc2028_ctrl ctrl = { - .fname = XC5000_DEFAULT_FIRMWARE, - .max_len = 64, - .demod = 0; - }; - struct v4l2_priv_tun_config cfg = { - .tuner = dev->tuner_type, - .priv = &ctrl, - }; - tuner_call(dev, tuner, s_config, &cfg); - } -#endif - /* configure tuner */ - f.tuner = 0; - f.type = V4L2_TUNER_ANALOG_TV; - f.frequency = 9076; /* just a magic number */ - dev->ctl_freq = f.frequency; - call_all(dev, tuner, s_frequency, &f); - -} - -void cx231xx_card_setup(struct cx231xx *dev) -{ - - cx231xx_set_model(dev); - - dev->tuner_type = cx231xx_boards[dev->model].tuner_type; - if (cx231xx_boards[dev->model].tuner_addr) - dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr; - - /* request some modules */ - if (dev->board.decoder == CX231XX_AVDECODER) { - dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_bus[0].i2c_adap, - "cx25840", 0x88 >> 1, NULL); - if (dev->sd_cx25840 == NULL) - cx231xx_info("cx25840 subdev registration failure\n"); - cx25840_call(dev, core, load_fw); - - } - - /* Initialize the tuner */ - if (dev->board.tuner_type != TUNER_ABSENT) { - dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, - "tuner", - dev->tuner_addr, NULL); - if (dev->sd_tuner == NULL) - cx231xx_info("tuner subdev registration failure\n"); - else - cx231xx_config_tuner(dev); - } -} - -/* - * cx231xx_config() - * inits registers with sane defaults - */ -int cx231xx_config(struct cx231xx *dev) -{ - /* TBD need to add cx231xx specific code */ - dev->mute = 1; /* maybe not the right place... */ - dev->volume = 0x1f; - - return 0; -} - -/* - * cx231xx_config_i2c() - * configure i2c attached devices - */ -void cx231xx_config_i2c(struct cx231xx *dev) -{ - /* u32 input = INPUT(dev->video_input)->vmux; */ - - call_all(dev, video, s_stream, 1); -} - -/* - * cx231xx_realease_resources() - * unregisters the v4l2,i2c and usb devices - * called when the device gets disconected or at module unload -*/ -void cx231xx_release_resources(struct cx231xx *dev) -{ - cx231xx_release_analog_resources(dev); - - cx231xx_remove_from_devlist(dev); - - cx231xx_ir_exit(dev); - - /* Release I2C buses */ - cx231xx_dev_uninit(dev); - - /* delete v4l2 device */ - v4l2_device_unregister(&dev->v4l2_dev); - - usb_put_dev(dev->udev); - - /* Mark device as unused */ - clear_bit(dev->devno, &cx231xx_devused); - - kfree(dev->video_mode.alt_max_pkt_size); - kfree(dev->vbi_mode.alt_max_pkt_size); - kfree(dev->sliced_cc_mode.alt_max_pkt_size); - kfree(dev->ts1_mode.alt_max_pkt_size); - kfree(dev); -} - -/* - * cx231xx_init_dev() - * allocates and inits the device structs, registers i2c bus and v4l device - */ -static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev, - int minor) -{ - int retval = -ENOMEM; - int errCode; - unsigned int maxh, maxw; - - dev->udev = udev; - mutex_init(&dev->lock); - mutex_init(&dev->ctrl_urb_lock); - mutex_init(&dev->gpio_i2c_lock); - mutex_init(&dev->i2c_lock); - - spin_lock_init(&dev->video_mode.slock); - spin_lock_init(&dev->vbi_mode.slock); - spin_lock_init(&dev->sliced_cc_mode.slock); - - init_waitqueue_head(&dev->open); - init_waitqueue_head(&dev->wait_frame); - init_waitqueue_head(&dev->wait_stream); - - dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg; - dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg; - dev->cx231xx_send_usb_command = cx231xx_send_usb_command; - dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read; - dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write; - - /* Query cx231xx to find what pcb config it is related to */ - initialize_cx231xx(dev); - - /*To workaround error number=-71 on EP0 for VideoGrabber, - need set alt here.*/ - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER || - dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) { - cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3); - cx231xx_set_alt_setting(dev, INDEX_VANC, 1); - } - /* Cx231xx pre card setup */ - cx231xx_pre_card_setup(dev); - - errCode = cx231xx_config(dev); - if (errCode) { - cx231xx_errdev("error configuring device\n"); - return -ENOMEM; - } - - /* set default norm */ - dev->norm = dev->board.norm; - - /* register i2c bus */ - errCode = cx231xx_dev_init(dev); - if (errCode < 0) { - cx231xx_dev_uninit(dev); - cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* Do board specific init */ - cx231xx_card_setup(dev); - - /* configure the device */ - cx231xx_config_i2c(dev); - - maxw = norm_maxw(dev); - maxh = norm_maxh(dev); - - /* set default image size */ - dev->width = maxw; - dev->height = maxh; - dev->interlaced = 0; - dev->video_input = 0; - - errCode = cx231xx_config(dev); - if (errCode < 0) { - cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->video_mode.vidq.active); - INIT_LIST_HEAD(&dev->video_mode.vidq.queued); - - /* init vbi dma queues */ - INIT_LIST_HEAD(&dev->vbi_mode.vidq.active); - INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued); - - /* Reset other chips required if they are tied up with GPIO pins */ - cx231xx_add_into_devlist(dev); - - if (dev->board.has_417) { - printk(KERN_INFO "attach 417 %d\n", dev->model); - if (cx231xx_417_register(dev) < 0) { - printk(KERN_ERR - "%s() Failed to register 417 on VID_B\n", - __func__); - } - } - - retval = cx231xx_register_analog_devices(dev); - if (retval < 0) { - cx231xx_release_resources(dev); - return retval; - } - - cx231xx_ir_init(dev); - - cx231xx_init_extension(dev); - - return 0; -} - -#if defined(CONFIG_MODULES) && defined(MODULE) -static void request_module_async(struct work_struct *work) -{ - struct cx231xx *dev = container_of(work, - struct cx231xx, request_module_wk); - - if (dev->has_alsa_audio) - request_module("cx231xx-alsa"); - - if (dev->board.has_dvb) - request_module("cx231xx-dvb"); - -} - -static void request_modules(struct cx231xx *dev) -{ - INIT_WORK(&dev->request_module_wk, request_module_async); - schedule_work(&dev->request_module_wk); -} - -static void flush_request_modules(struct cx231xx *dev) -{ - flush_work_sync(&dev->request_module_wk); -} -#else -#define request_modules(dev) -#define flush_request_modules(dev) -#endif /* CONFIG_MODULES */ - -/* - * cx231xx_usb_probe() - * checks for supported devices - */ -static int cx231xx_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *udev; - struct usb_interface *uif; - struct cx231xx *dev = NULL; - int retval = -ENODEV; - int nr = 0, ifnum; - int i, isoc_pipe = 0; - char *speed; - struct usb_interface_assoc_descriptor *assoc_desc; - - udev = usb_get_dev(interface_to_usbdev(interface)); - ifnum = interface->altsetting[0].desc.bInterfaceNumber; - - /* - * Interface number 0 - IR interface (handled by mceusb driver) - * Interface number 1 - AV interface (handled by this driver) - */ - if (ifnum != 1) - return -ENODEV; - - /* Check to see next free device and mark as used */ - do { - nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS); - if (nr >= CX231XX_MAXBOARDS) { - /* No free device slots */ - cx231xx_err(DRIVER_NAME ": Supports only %i devices.\n", - CX231XX_MAXBOARDS); - return -ENOMEM; - } - } while (test_and_set_bit(nr, &cx231xx_devused)); - - /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - cx231xx_err(DRIVER_NAME ": out of memory!\n"); - clear_bit(nr, &cx231xx_devused); - return -ENOMEM; - } - - snprintf(dev->name, 29, "cx231xx #%d", nr); - dev->devno = nr; - dev->model = id->driver_info; - dev->video_mode.alt = -1; - - dev->interface_count++; - /* reset gpio dir and value */ - dev->gpio_dir = 0; - dev->gpio_val = 0; - dev->xc_fw_load_done = 0; - dev->has_alsa_audio = 1; - dev->power_mode = -1; - atomic_set(&dev->devlist_count, 0); - - /* 0 - vbi ; 1 -sliced cc mode */ - dev->vbi_or_sliced_cc_mode = 0; - - /* get maximum no.of IAD interfaces */ - assoc_desc = udev->actconfig->intf_assoc[0]; - dev->max_iad_interface_count = assoc_desc->bInterfaceCount; - - /* init CIR module TBD */ - - /*mode_tv: digital=1 or analog=0*/ - dev->mode_tv = 0; - - dev->USE_ISO = transfer_mode; - - switch (udev->speed) { - case USB_SPEED_LOW: - speed = "1.5"; - break; - case USB_SPEED_UNKNOWN: - case USB_SPEED_FULL: - speed = "12"; - break; - case USB_SPEED_HIGH: - speed = "480"; - break; - default: - speed = "unknown"; - } - - cx231xx_info("New device %s %s @ %s Mbps " - "(%04x:%04x) with %d interfaces\n", - udev->manufacturer ? udev->manufacturer : "", - udev->product ? udev->product : "", - speed, - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - dev->max_iad_interface_count); - - /* increment interface count */ - dev->interface_count++; - - /* get device number */ - nr = dev->devno; - - assoc_desc = udev->actconfig->intf_assoc[0]; - if (assoc_desc->bFirstInterface != ifnum) { - cx231xx_err(DRIVER_NAME ": Not found " - "matching IAD interface\n"); - clear_bit(dev->devno, &cx231xx_devused); - kfree(dev); - dev = NULL; - return -ENODEV; - } - - cx231xx_info("registering interface %d\n", ifnum); - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - /* - * AV device initialization - only done at the last interface - */ - - /* Create v4l2 device */ - retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); - if (retval) { - cx231xx_errdev("v4l2_device_register failed\n"); - clear_bit(dev->devno, &cx231xx_devused); - kfree(dev); - dev = NULL; - return -EIO; - } - /* allocate device struct */ - retval = cx231xx_init_dev(dev, udev, nr); - if (retval) { - clear_bit(dev->devno, &cx231xx_devused); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); - dev = NULL; - usb_set_intfdata(interface, NULL); - - return retval; - } - - /* compute alternate max packet sizes for video */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0].interface_info.video_index + 1]; - - dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0]. - endpoint[isoc_pipe].desc.bEndpointAddress); - - dev->video_mode.num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - dev->video_mode.end_point_addr, - dev->video_mode.num_alt); - dev->video_mode.alt_max_pkt_size = - kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL); - - if (dev->video_mode.alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - clear_bit(dev->devno, &cx231xx_devused); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); - dev = NULL; - return -ENOMEM; - } - - for (i = 0; i < dev->video_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. - desc.wMaxPacketSize); - dev->video_mode.alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - dev->video_mode.alt_max_pkt_size[i]); - } - - /* compute alternate max packet sizes for vbi */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0].interface_info. - vanc_index + 1]; - - dev->vbi_mode.end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress); - - dev->vbi_mode.num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - dev->vbi_mode.end_point_addr, - dev->vbi_mode.num_alt); - dev->vbi_mode.alt_max_pkt_size = - kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL); - - if (dev->vbi_mode.alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - clear_bit(dev->devno, &cx231xx_devused); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); - dev = NULL; - return -ENOMEM; - } - - for (i = 0; i < dev->vbi_mode.num_alt; i++) { - u16 tmp = - le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. - desc.wMaxPacketSize); - dev->vbi_mode.alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - dev->vbi_mode.alt_max_pkt_size[i]); - } - - /* compute alternate max packet sizes for sliced CC */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0].interface_info. - hanc_index + 1]; - - dev->sliced_cc_mode.end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress); - - dev->sliced_cc_mode.num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - dev->sliced_cc_mode.end_point_addr, - dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size = - kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL); - - if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - clear_bit(dev->devno, &cx231xx_devused); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); - dev = NULL; - return -ENOMEM; - } - - for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. - desc.wMaxPacketSize); - dev->sliced_cc_mode.alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - dev->sliced_cc_mode.alt_max_pkt_size[i]); - } - - if (dev->current_pcb_config.ts1_source != 0xff) { - /* compute alternate max packet sizes for TS1 */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0]. - interface_info. - ts1_index + 1]; - - dev->ts1_mode.end_point_addr = - le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe]. - desc.bEndpointAddress); - - dev->ts1_mode.num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - dev->ts1_mode.end_point_addr, - dev->ts1_mode.num_alt); - dev->ts1_mode.alt_max_pkt_size = - kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL); - - if (dev->ts1_mode.alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - clear_bit(dev->devno, &cx231xx_devused); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); - dev = NULL; - return -ENOMEM; - } - - for (i = 0; i < dev->ts1_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i]. - endpoint[isoc_pipe].desc. - wMaxPacketSize); - dev->ts1_mode.alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - dev->ts1_mode.alt_max_pkt_size[i]); - } - } - - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) { - cx231xx_enable_OSC(dev); - cx231xx_reset_out(dev); - cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3); - } - - if (dev->model == CX231XX_BOARD_CNXT_RDE_253S) - cx231xx_sleep_s5h1432(dev); - - /* load other modules required */ - request_modules(dev); - - return 0; -} - -/* - * cx231xx_usb_disconnect() - * called when the device gets diconencted - * video device will be unregistered on v4l2_close in case it is still open - */ -static void cx231xx_usb_disconnect(struct usb_interface *interface) -{ - struct cx231xx *dev; - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - if (!dev) - return; - - if (!dev->udev) - return; - - dev->state |= DEV_DISCONNECTED; - - flush_request_modules(dev); - - /* wait until all current v4l2 io is finished then deallocate - resources */ - mutex_lock(&dev->lock); - - wake_up_interruptible_all(&dev->open); - - if (dev->users) { - cx231xx_warn - ("device %s is open! Deregistration and memory " - "deallocation are deferred on close.\n", - video_device_node_name(dev->vdev)); - - /* Even having users, it is safe to remove the RC i2c driver */ - cx231xx_ir_exit(dev); - - if (dev->USE_ISO) - cx231xx_uninit_isoc(dev); - else - cx231xx_uninit_bulk(dev); - wake_up_interruptible(&dev->wait_frame); - wake_up_interruptible(&dev->wait_stream); - } else { - } - - cx231xx_close_extension(dev); - - mutex_unlock(&dev->lock); - - if (!dev->users) - cx231xx_release_resources(dev); -} - -static struct usb_driver cx231xx_usb_driver = { - .name = "cx231xx", - .probe = cx231xx_usb_probe, - .disconnect = cx231xx_usb_disconnect, - .id_table = cx231xx_id_table, -}; - -module_usb_driver(cx231xx_usb_driver); diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/video/cx231xx/cx231xx-conf-reg.h deleted file mode 100644 index 25593f212abf..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-conf-reg.h +++ /dev/null @@ -1,495 +0,0 @@ -/* - cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB - video capture devices - - Copyright (C) 2008 - - This program is free software; 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 _POLARIS_REG_H_ -#define _POLARIS_REG_H_ - -#define BOARD_CFG_STAT 0x0 -#define TS_MODE_REG 0x4 -#define TS1_CFG_REG 0x8 -#define TS1_LENGTH_REG 0xc -#define TS2_CFG_REG 0x10 -#define TS2_LENGTH_REG 0x14 -#define EP_MODE_SET 0x18 -#define CIR_PWR_PTN1 0x1c -#define CIR_PWR_PTN2 0x20 -#define CIR_PWR_PTN3 0x24 -#define CIR_PWR_MASK0 0x28 -#define CIR_PWR_MASK1 0x2c -#define CIR_PWR_MASK2 0x30 -#define CIR_GAIN 0x34 -#define CIR_CAR_REG 0x38 -#define CIR_OT_CFG1 0x40 -#define CIR_OT_CFG2 0x44 -#define GBULK_BIT_EN 0x68 -#define PWR_CTL_EN 0x74 - -/* Polaris Endpoints capture mask for register EP_MODE_SET */ -#define ENABLE_EP1 0x01 /* Bit[0]=1 */ -#define ENABLE_EP2 0x02 /* Bit[1]=1 */ -#define ENABLE_EP3 0x04 /* Bit[2]=1 */ -#define ENABLE_EP4 0x08 /* Bit[3]=1 */ -#define ENABLE_EP5 0x10 /* Bit[4]=1 */ -#define ENABLE_EP6 0x20 /* Bit[5]=1 */ - -/* Bit definition for register PWR_CTL_EN */ -#define PWR_MODE_MASK 0x17f -#define PWR_AV_EN 0x08 /* bit3 */ -#define PWR_ISO_EN 0x40 /* bit6 */ -#define PWR_AV_MODE 0x30 /* bit4,5 */ -#define PWR_TUNER_EN 0x04 /* bit2 */ -#define PWR_DEMOD_EN 0x02 /* bit1 */ -#define I2C_DEMOD_EN 0x01 /* bit0 */ -#define PWR_RESETOUT_EN 0x100 /* bit8 */ - -enum AV_MODE{ - POLARIS_AVMODE_DEFAULT = 0, - POLARIS_AVMODE_DIGITAL = 0x10, - POLARIS_AVMODE_ANALOGT_TV = 0x20, - POLARIS_AVMODE_ENXTERNAL_AV = 0x30, - -}; - -/* Colibri Registers */ - -#define SINGLE_ENDED 0x0 -#define LOW_IF 0x4 -#define EU_IF 0x9 -#define US_IF 0xa - -#define SUP_BLK_TUNE1 0x00 -#define SUP_BLK_TUNE2 0x01 -#define SUP_BLK_TUNE3 0x02 -#define SUP_BLK_XTAL 0x03 -#define SUP_BLK_PLL1 0x04 -#define SUP_BLK_PLL2 0x05 -#define SUP_BLK_PLL3 0x06 -#define SUP_BLK_REF 0x07 -#define SUP_BLK_PWRDN 0x08 -#define SUP_BLK_TESTPAD 0x09 -#define ADC_COM_INT5_STAB_REF 0x0a -#define ADC_COM_QUANT 0x0b -#define ADC_COM_BIAS1 0x0c -#define ADC_COM_BIAS2 0x0d -#define ADC_COM_BIAS3 0x0e -#define TESTBUS_CTRL 0x12 - -#define FLD_PWRDN_TUNING_BIAS 0x10 -#define FLD_PWRDN_ENABLE_PLL 0x08 -#define FLD_PWRDN_PD_BANDGAP 0x04 -#define FLD_PWRDN_PD_BIAS 0x02 -#define FLD_PWRDN_PD_TUNECK 0x01 - - -#define ADC_STATUS_CH1 0x20 -#define ADC_STATUS_CH2 0x40 -#define ADC_STATUS_CH3 0x60 - -#define ADC_STATUS2_CH1 0x21 -#define ADC_STATUS2_CH2 0x41 -#define ADC_STATUS2_CH3 0x61 - -#define ADC_CAL_ATEST_CH1 0x22 -#define ADC_CAL_ATEST_CH2 0x42 -#define ADC_CAL_ATEST_CH3 0x62 - -#define ADC_PWRDN_CLAMP_CH1 0x23 -#define ADC_PWRDN_CLAMP_CH2 0x43 -#define ADC_PWRDN_CLAMP_CH3 0x63 - -#define ADC_CTRL_DAC23_CH1 0x24 -#define ADC_CTRL_DAC23_CH2 0x44 -#define ADC_CTRL_DAC23_CH3 0x64 - -#define ADC_CTRL_DAC1_CH1 0x25 -#define ADC_CTRL_DAC1_CH2 0x45 -#define ADC_CTRL_DAC1_CH3 0x65 - -#define ADC_DCSERVO_DEM_CH1 0x26 -#define ADC_DCSERVO_DEM_CH2 0x46 -#define ADC_DCSERVO_DEM_CH3 0x66 - -#define ADC_FB_FRCRST_CH1 0x27 -#define ADC_FB_FRCRST_CH2 0x47 -#define ADC_FB_FRCRST_CH3 0x67 - -#define ADC_INPUT_CH1 0x28 -#define ADC_INPUT_CH2 0x48 -#define ADC_INPUT_CH3 0x68 -#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */ - -#define ADC_NTF_PRECLMP_EN_CH1 0x29 -#define ADC_NTF_PRECLMP_EN_CH2 0x49 -#define ADC_NTF_PRECLMP_EN_CH3 0x69 - -#define ADC_QGAIN_RES_TRM_CH1 0x2a -#define ADC_QGAIN_RES_TRM_CH2 0x4a -#define ADC_QGAIN_RES_TRM_CH3 0x6a - -#define ADC_SOC_PRECLMP_TERM_CH1 0x2b -#define ADC_SOC_PRECLMP_TERM_CH2 0x4b -#define ADC_SOC_PRECLMP_TERM_CH3 0x6b - -#define TESTBUS_CTRL_CH1 0x32 -#define TESTBUS_CTRL_CH2 0x52 -#define TESTBUS_CTRL_CH3 0x72 - -/****************************************************************************** - * DIF registers * - ******************************************************************************/ -#define DIRECT_IF_REVB_BASE 0x00300 - -/*****************************************************************************/ -#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000) -/*****************************************************************************/ -#define FLD_DIF_PLL_LOCK 0x80000000 -/* Reserved [30:29] */ -#define FLD_DIF_PLL_FREE_RUN 0x10000000 -#define FLD_DIF_PLL_FREQ 0x0fffffff - -/*****************************************************************************/ -#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004) -/*****************************************************************************/ -#define FLD_DIF_KD_PD 0xff000000 -/* Reserved [23:20] */ -#define FLD_DIF_KDS_PD 0x000f0000 -#define FLD_DIF_KI_PD 0x0000ff00 -/* Reserved [7:4] */ -#define FLD_DIF_KIS_PD 0x0000000f - -/*****************************************************************************/ -#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008) -/*****************************************************************************/ -#define FLD_DIF_KD_FD 0xff000000 -/* Reserved [23:20] */ -#define FLD_DIF_KDS_FD 0x000f0000 -#define FLD_DIF_KI_FD 0x0000ff00 -#define FLD_DIF_SIG_PROP_SZ 0x000000f0 -#define FLD_DIF_KIS_FD 0x0000000f - -/*****************************************************************************/ -#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c) -/*****************************************************************************/ -#define FLD_DIF_PLL_AGC_REF 0xfff00000 -#define FLD_DIF_PLL_AGC_KI 0x000f0000 -/* Reserved [15] */ -#define FLD_DIF_FREQ_LIMIT 0x00007000 -#define FLD_DIF_K_FD 0x00000f00 -#define FLD_DIF_DOWNSMPL_FD 0x000000ff - -/*****************************************************************************/ -#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010) -/*****************************************************************************/ -/* Reserved [31:16] */ -#define FLD_DIF_PLL_AGC_EN 0x00008000 -/* Reserved [14:12] */ -#define FLD_DIF_PLL_MAN_GAIN 0x00000fff - -/*****************************************************************************/ -#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014) -/*****************************************************************************/ -#define FLD_DIF_K_AGC_RF 0xf0000000 -#define FLD_DIF_K_AGC_IF 0x0f000000 -#define FLD_DIF_K_AGC_INT 0x00f00000 -/* Reserved [19:12] */ -#define FLD_DIF_IF_REF 0x00000fff - -/*****************************************************************************/ -#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018) -/*****************************************************************************/ -#define FLD_DIF_IF_MAX 0xff000000 -#define FLD_DIF_IF_MIN 0x00ff0000 -#define FLD_DIF_IF_AGC 0x0000ffff - -/*****************************************************************************/ -#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c) -/*****************************************************************************/ -#define FLD_DIF_INT_MAX 0xff000000 -#define FLD_DIF_INT_MIN 0x00ff0000 -#define FLD_DIF_INT_AGC 0x0000ffff - -/*****************************************************************************/ -#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020) -/*****************************************************************************/ -#define FLD_DIF_RF_MAX 0xff000000 -#define FLD_DIF_RF_MIN 0x00ff0000 -#define FLD_DIF_RF_AGC 0x0000ffff - -/*****************************************************************************/ -#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024) -/*****************************************************************************/ -#define FLD_DIF_IF_AGC_IN 0xffff0000 -#define FLD_DIF_INT_AGC_IN 0x0000ffff - -/*****************************************************************************/ -#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028) -/*****************************************************************************/ -/* Reserved [31:16] */ -#define FLD_DIF_RF_AGC_IN 0x0000ffff - -/*****************************************************************************/ -#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c) -/*****************************************************************************/ -#define FLD_DIF_AFD 0xc0000000 -#define FLD_DIF_K_VID_AGC 0x30000000 -#define FLD_DIF_LINE_LENGTH 0x0fff0000 -#define FLD_DIF_AGC_GAIN 0x0000ffff - -/*****************************************************************************/ -#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030) -/*****************************************************************************/ -#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000 -/* Reserved [30:30] */ -#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000 -/* Reserved [23:17] */ -#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000 -#define FLD_DIF_VID_MAN_GAIN 0x0000ffff - -/*****************************************************************************/ -#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034) -/*****************************************************************************/ -#define FLD_DIF_LPF_FREQ 0xc0000000 -#define FLD_DIF_AV_PHASE_INC 0x3f000000 -#define FLD_DIF_AUDIO_FREQ 0x00ffffff - -/*****************************************************************************/ -#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038) -/*****************************************************************************/ -/* Reserved [31:24] */ -#define FLD_DIF_IIR23_R2 0x00ff0000 -#define FLD_DIF_IIR23_R1 0x0000ff00 -#define FLD_DIF_IIR1_R1 0x000000ff - -/*****************************************************************************/ -#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c) -/*****************************************************************************/ -#define FLD_DIF_DIF_BYPASS 0x80000000 -#define FLD_DIF_FM_NYQ_GAIN 0x40000000 -#define FLD_DIF_RF_AGC_ENA 0x20000000 -#define FLD_DIF_INT_AGC_ENA 0x10000000 -#define FLD_DIF_IF_AGC_ENA 0x08000000 -#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000 -#define FLD_DIF_VIDEO_AGC_ENA 0x02000000 -#define FLD_DIF_RF_AGC_INV 0x01000000 -#define FLD_DIF_INT_AGC_INV 0x00800000 -#define FLD_DIF_IF_AGC_INV 0x00400000 -#define FLD_DIF_SPEC_INV 0x00200000 -#define FLD_DIF_AUD_FULL_BW 0x00100000 -#define FLD_DIF_AUD_SRC_SEL 0x00080000 -/* Reserved [18] */ -#define FLD_DIF_IF_FREQ 0x00030000 -/* Reserved [15:14] */ -#define FLD_DIF_TIP_OFFSET 0x00003f00 -/* Reserved [7:5] */ -#define FLD_DIF_DITHER_ENA 0x00000010 -/* Reserved [3:1] */ -#define FLD_DIF_RF_IF_LOCK 0x00000001 - -/*****************************************************************************/ -#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040) -/*****************************************************************************/ -/* Reserved [31:29] */ -#define FLD_DIF_PHASE_INC 0x1fffffff - -/*****************************************************************************/ -#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044) -/*****************************************************************************/ -/* Reserved [31:16] */ -#define FLD_DIF_SRC_KI 0x0000ff00 -#define FLD_DIF_SRC_KD 0x000000ff - -/*****************************************************************************/ -#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048) -/*****************************************************************************/ -/* Reserved [31:19] */ -#define FLD_DIF_BPF_COEFF_0 0x00070000 -/* Reserved [15:4] */ -#define FLD_DIF_BPF_COEFF_1 0x0000000f - -/*****************************************************************************/ -#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c) -/*****************************************************************************/ -/* Reserved [31:22] */ -#define FLD_DIF_BPF_COEFF_2 0x003f0000 -/* Reserved [15:7] */ -#define FLD_DIF_BPF_COEFF_3 0x0000007f - -/*****************************************************************************/ -#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050) -/*****************************************************************************/ -/* Reserved [31:24] */ -#define FLD_DIF_BPF_COEFF_4 0x00ff0000 -/* Reserved [15:8] */ -#define FLD_DIF_BPF_COEFF_5 0x000000ff - -/*****************************************************************************/ -#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054) -/*****************************************************************************/ -/* Reserved [31:25] */ -#define FLD_DIF_BPF_COEFF_6 0x01ff0000 -/* Reserved [15:9] */ -#define FLD_DIF_BPF_COEFF_7 0x000001ff - -/*****************************************************************************/ -#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058) -/*****************************************************************************/ -/* Reserved [31:26] */ -#define FLD_DIF_BPF_COEFF_8 0x03ff0000 -/* Reserved [15:10] */ -#define FLD_DIF_BPF_COEFF_9 0x000003ff - -/*****************************************************************************/ -#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c) -/*****************************************************************************/ -/* Reserved [31:27] */ -#define FLD_DIF_BPF_COEFF_10 0x07ff0000 -/* Reserved [15:11] */ -#define FLD_DIF_BPF_COEFF_11 0x000007ff - -/*****************************************************************************/ -#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060) -/*****************************************************************************/ -/* Reserved [31:27] */ -#define FLD_DIF_BPF_COEFF_12 0x07ff0000 -/* Reserved [15:12] */ -#define FLD_DIF_BPF_COEFF_13 0x00000fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064) -/*****************************************************************************/ -/* Reserved [31:28] */ -#define FLD_DIF_BPF_COEFF_14 0x0fff0000 -/* Reserved [15:12] */ -#define FLD_DIF_BPF_COEFF_15 0x00000fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068) -/*****************************************************************************/ -/* Reserved [31:29] */ -#define FLD_DIF_BPF_COEFF_16 0x1fff0000 -/* Reserved [15:13] */ -#define FLD_DIF_BPF_COEFF_17 0x00001fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c) -/*****************************************************************************/ -/* Reserved [31:29] */ -#define FLD_DIF_BPF_COEFF_18 0x1fff0000 -/* Reserved [15:13] */ -#define FLD_DIF_BPF_COEFF_19 0x00001fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070) -/*****************************************************************************/ -/* Reserved [31:29] */ -#define FLD_DIF_BPF_COEFF_20 0x1fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_21 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_22 0x3fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_23 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_24 0x3fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_25 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_26 0x3fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_27 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_28 0x3fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_29 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_30 0x3fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_31 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_32 0x3fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_33 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_34 0x3fff0000 -/* Reserved [15:14] */ -#define FLD_DIF_BPF_COEFF_35 0x00003fff - -/*****************************************************************************/ -#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090) -/*****************************************************************************/ -/* Reserved [31:30] */ -#define FLD_DIF_BPF_COEFF_36 0x3fff0000 -/* Reserved [15:0] */ - -/*****************************************************************************/ -#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094) -/*****************************************************************************/ -/* Reserved [31:20] */ -#define FLD_DIF_RPT_VARIANCE 0x000fffff - -/*****************************************************************************/ -#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098) -/*****************************************************************************/ -/* Reserved [31:8] */ -#define FLD_DIF_DIF_SOFT_RST 0x00000080 -#define FLD_DIF_DIF_REG_RST_MSK 0x00000040 -#define FLD_DIF_AGC_RST_MSK 0x00000020 -#define FLD_DIF_CMP_RST_MSK 0x00000010 -#define FLD_DIF_AVS_RST_MSK 0x00000008 -#define FLD_DIF_NYQ_RST_MSK 0x00000004 -#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002 -#define FLD_DIF_PLL_RST_MSK 0x00000001 - -/*****************************************************************************/ -#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c) -/*****************************************************************************/ -/* Reserved [31:25] */ -#define FLD_DIF_CTL_IP 0x01ffffff - -#endif diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c deleted file mode 100644 index 05358d486135..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ /dev/null @@ -1,1736 +0,0 @@ -/* - cx231xx-core.c - driver for Conexant Cx23100/101/102 - USB video capture devices - - Copyright (C) 2008 - Based on em28xx driver - - This program is free software; 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 "cx231xx.h" -#include "cx231xx-reg.h" - -/* #define ENABLE_DEBUG_ISOC_FRAMES */ - -static unsigned int core_debug; -module_param(core_debug, int, 0644); -MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); - -#define cx231xx_coredbg(fmt, arg...) do {\ - if (core_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -static unsigned int reg_debug; -module_param(reg_debug, int, 0644); -MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); - -static int alt = CX231XX_PINOUT; -module_param(alt, int, 0644); -MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); - -#define cx231xx_isocdbg(fmt, arg...) do {\ - if (core_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -/***************************************************************** -* Device control list functions * -******************************************************************/ - -LIST_HEAD(cx231xx_devlist); -static DEFINE_MUTEX(cx231xx_devlist_mutex); - -/* - * cx231xx_realease_resources() - * unregisters the v4l2,i2c and usb devices - * called when the device gets disconected or at module unload -*/ -void cx231xx_remove_from_devlist(struct cx231xx *dev) -{ - if (dev == NULL) - return; - if (dev->udev == NULL) - return; - - if (atomic_read(&dev->devlist_count) > 0) { - mutex_lock(&cx231xx_devlist_mutex); - list_del(&dev->devlist); - atomic_dec(&dev->devlist_count); - mutex_unlock(&cx231xx_devlist_mutex); - } -}; - -void cx231xx_add_into_devlist(struct cx231xx *dev) -{ - mutex_lock(&cx231xx_devlist_mutex); - list_add_tail(&dev->devlist, &cx231xx_devlist); - atomic_inc(&dev->devlist_count); - mutex_unlock(&cx231xx_devlist_mutex); -}; - -static LIST_HEAD(cx231xx_extension_devlist); - -int cx231xx_register_extension(struct cx231xx_ops *ops) -{ - struct cx231xx *dev = NULL; - - mutex_lock(&cx231xx_devlist_mutex); - list_add_tail(&ops->next, &cx231xx_extension_devlist); - list_for_each_entry(dev, &cx231xx_devlist, devlist) - ops->init(dev); - - printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name); - mutex_unlock(&cx231xx_devlist_mutex); - return 0; -} -EXPORT_SYMBOL(cx231xx_register_extension); - -void cx231xx_unregister_extension(struct cx231xx_ops *ops) -{ - struct cx231xx *dev = NULL; - - mutex_lock(&cx231xx_devlist_mutex); - list_for_each_entry(dev, &cx231xx_devlist, devlist) - ops->fini(dev); - - - printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name); - list_del(&ops->next); - mutex_unlock(&cx231xx_devlist_mutex); -} -EXPORT_SYMBOL(cx231xx_unregister_extension); - -void cx231xx_init_extension(struct cx231xx *dev) -{ - struct cx231xx_ops *ops = NULL; - - mutex_lock(&cx231xx_devlist_mutex); - if (!list_empty(&cx231xx_extension_devlist)) { - list_for_each_entry(ops, &cx231xx_extension_devlist, next) { - if (ops->init) - ops->init(dev); - } - } - mutex_unlock(&cx231xx_devlist_mutex); -} - -void cx231xx_close_extension(struct cx231xx *dev) -{ - struct cx231xx_ops *ops = NULL; - - mutex_lock(&cx231xx_devlist_mutex); - if (!list_empty(&cx231xx_extension_devlist)) { - list_for_each_entry(ops, &cx231xx_extension_devlist, next) { - if (ops->fini) - ops->fini(dev); - } - } - mutex_unlock(&cx231xx_devlist_mutex); -} - -/**************************************************************** -* U S B related functions * -*****************************************************************/ -int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, - struct cx231xx_i2c_xfer_data *req_data) -{ - int status = 0; - struct cx231xx *dev = i2c_bus->dev; - struct VENDOR_REQUEST_IN ven_req; - - u8 saddr_len = 0; - u8 _i2c_period = 0; - u8 _i2c_nostop = 0; - u8 _i2c_reserve = 0; - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - /* Get the I2C period, nostop and reserve parameters */ - _i2c_period = i2c_bus->i2c_period; - _i2c_nostop = i2c_bus->i2c_nostop; - _i2c_reserve = i2c_bus->i2c_reserve; - - saddr_len = req_data->saddr_len; - - /* Set wValue */ - if (saddr_len == 1) /* need check saddr_len == 0 */ - ven_req.wValue = - req_data-> - dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | - _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; - else - ven_req.wValue = - req_data-> - dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | - _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; - - /* set channel number */ - if (req_data->direction & I2C_M_RD) { - /* channel number, for read,spec required channel_num +4 */ - ven_req.bRequest = i2c_bus->nr + 4; - } else - ven_req.bRequest = i2c_bus->nr; /* channel number, */ - - /* set index value */ - switch (saddr_len) { - case 0: - ven_req.wIndex = 0; /* need check */ - break; - case 1: - ven_req.wIndex = (req_data->saddr_dat & 0xff); - break; - case 2: - ven_req.wIndex = req_data->saddr_dat; - break; - } - - /* set wLength value */ - ven_req.wLength = req_data->buf_size; - - /* set bData value */ - ven_req.bData = 0; - - /* set the direction */ - if (req_data->direction) { - ven_req.direction = USB_DIR_IN; - memset(req_data->p_buffer, 0x00, ven_req.wLength); - } else - ven_req.direction = USB_DIR_OUT; - - /* set the buffer for read / write */ - ven_req.pBuff = req_data->p_buffer; - - - /* call common vendor command request */ - status = cx231xx_send_vendor_cmd(dev, &ven_req); - if (status < 0) { - cx231xx_info - ("UsbInterface::sendCommand, failed with status -%d\n", - status); - } - - return status; -} -EXPORT_SYMBOL_GPL(cx231xx_send_usb_command); - -/* - * Sends/Receives URB control messages, assuring to use a kalloced buffer - * for all operations (dev->urb_buf), to avoid using stacked buffers, as - * they aren't safe for usage with USB, due to DMA restrictions. - * Also implements the debug code for control URB's. - */ -static int __usb_control_msg(struct cx231xx *dev, unsigned int pipe, - __u8 request, __u8 requesttype, __u16 value, __u16 index, - void *data, __u16 size, int timeout) -{ - int rc, i; - - if (reg_debug) { - printk(KERN_DEBUG "%s: (pipe 0x%08x): " - "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", - dev->name, - pipe, - (requesttype & USB_DIR_IN) ? "IN" : "OUT", - requesttype, - request, - value & 0xff, value >> 8, - index & 0xff, index >> 8, - size & 0xff, size >> 8); - if (!(requesttype & USB_DIR_IN)) { - printk(KERN_CONT ">>>"); - for (i = 0; i < size; i++) - printk(KERN_CONT " %02x", - ((unsigned char *)data)[i]); - } - } - - /* Do the real call to usb_control_msg */ - mutex_lock(&dev->ctrl_urb_lock); - if (!(requesttype & USB_DIR_IN) && size) - memcpy(dev->urb_buf, data, size); - rc = usb_control_msg(dev->udev, pipe, request, requesttype, value, - index, dev->urb_buf, size, timeout); - if ((requesttype & USB_DIR_IN) && size) - memcpy(data, dev->urb_buf, size); - mutex_unlock(&dev->ctrl_urb_lock); - - if (reg_debug) { - if (unlikely(rc < 0)) { - printk(KERN_CONT "FAILED!\n"); - return rc; - } - - if ((requesttype & USB_DIR_IN)) { - printk(KERN_CONT "<<<"); - for (i = 0; i < size; i++) - printk(KERN_CONT " %02x", - ((unsigned char *)data)[i]); - } - printk(KERN_CONT "\n"); - } - - return rc; -} - - -/* - * cx231xx_read_ctrl_reg() - * reads data from the usb device specifying bRequest and wValue - */ -int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, - char *buf, int len) -{ - u8 val = 0; - int ret; - int pipe = usb_rcvctrlpipe(dev->udev, 0); - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - if (len > URB_MAX_CTRL_SIZE) - return -EINVAL; - - switch (len) { - case 1: - val = ENABLE_ONE_BYTE; - break; - case 2: - val = ENABLE_TWE_BYTE; - break; - case 3: - val = ENABLE_THREE_BYTE; - break; - case 4: - val = ENABLE_FOUR_BYTE; - break; - default: - val = 0xFF; /* invalid option */ - } - - if (val == 0xFF) - return -EINVAL; - - ret = __usb_control_msg(dev, pipe, req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - val, reg, buf, len, HZ); - return ret; -} - -int cx231xx_send_vendor_cmd(struct cx231xx *dev, - struct VENDOR_REQUEST_IN *ven_req) -{ - int ret; - int pipe = 0; - int unsend_size = 0; - u8 *pdata; - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - if ((ven_req->wLength > URB_MAX_CTRL_SIZE)) - return -EINVAL; - - if (ven_req->direction) - pipe = usb_rcvctrlpipe(dev->udev, 0); - else - pipe = usb_sndctrlpipe(dev->udev, 0); - - /* - * If the cx23102 read more than 4 bytes with i2c bus, - * need chop to 4 byte per request - */ - if ((ven_req->wLength > 4) && ((ven_req->bRequest == 0x4) || - (ven_req->bRequest == 0x5) || - (ven_req->bRequest == 0x6))) { - unsend_size = 0; - pdata = ven_req->pBuff; - - - unsend_size = ven_req->wLength; - - /* the first package */ - ven_req->wValue = ven_req->wValue & 0xFFFB; - ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x2; - ret = __usb_control_msg(dev, pipe, ven_req->bRequest, - ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - ven_req->wValue, ven_req->wIndex, pdata, - 0x0004, HZ); - unsend_size = unsend_size - 4; - - /* the middle package */ - ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x42; - while (unsend_size - 4 > 0) { - pdata = pdata + 4; - ret = __usb_control_msg(dev, pipe, - ven_req->bRequest, - ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - ven_req->wValue, ven_req->wIndex, pdata, - 0x0004, HZ); - unsend_size = unsend_size - 4; - } - - /* the last package */ - ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x40; - pdata = pdata + 4; - ret = __usb_control_msg(dev, pipe, ven_req->bRequest, - ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - ven_req->wValue, ven_req->wIndex, pdata, - unsend_size, HZ); - } else { - ret = __usb_control_msg(dev, pipe, ven_req->bRequest, - ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - ven_req->wValue, ven_req->wIndex, - ven_req->pBuff, ven_req->wLength, HZ); - } - - return ret; -} - -/* - * cx231xx_write_ctrl_reg() - * sends data to the usb device, specifying bRequest - */ -int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf, - int len) -{ - u8 val = 0; - int ret; - int pipe = usb_sndctrlpipe(dev->udev, 0); - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) - return -EINVAL; - - switch (len) { - case 1: - val = ENABLE_ONE_BYTE; - break; - case 2: - val = ENABLE_TWE_BYTE; - break; - case 3: - val = ENABLE_THREE_BYTE; - break; - case 4: - val = ENABLE_FOUR_BYTE; - break; - default: - val = 0xFF; /* invalid option */ - } - - if (val == 0xFF) - return -EINVAL; - - if (reg_debug) { - int byte; - - cx231xx_isocdbg("(pipe 0x%08x): " - "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", - pipe, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - req, 0, val, reg & 0xff, - reg >> 8, len & 0xff, len >> 8); - - for (byte = 0; byte < len; byte++) - cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]); - cx231xx_isocdbg("\n"); - } - - ret = __usb_control_msg(dev, pipe, req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - val, reg, buf, len, HZ); - - return ret; -} - -/**************************************************************** -* USB Alternate Setting functions * -*****************************************************************/ - -int cx231xx_set_video_alternate(struct cx231xx *dev) -{ - int errCode, prev_alt = dev->video_mode.alt; - unsigned int min_pkt_size = dev->width * 2 + 4; - u32 usb_interface_index = 0; - - /* When image size is bigger than a certain value, - the frame size should be increased, otherwise, only - green screen will be received. - */ - if (dev->width * 2 * dev->height > 720 * 240 * 2) - min_pkt_size *= 2; - - if (dev->width > 360) { - /* resolutions: 720,704,640 */ - dev->video_mode.alt = 3; - } else if (dev->width > 180) { - /* resolutions: 360,352,320,240 */ - dev->video_mode.alt = 2; - } else if (dev->width > 0) { - /* resolutions: 180,176,160,128,88 */ - dev->video_mode.alt = 1; - } else { - /* Change to alt0 BULK to release USB bandwidth */ - dev->video_mode.alt = 0; - } - - if (dev->USE_ISO == 0) - dev->video_mode.alt = 0; - - cx231xx_coredbg("dev->video_mode.alt= %d\n", dev->video_mode.alt); - - /* Get the correct video interface Index */ - usb_interface_index = - dev->current_pcb_config.hs_config_info[0].interface_info. - video_index + 1; - - if (dev->video_mode.alt != prev_alt) { - cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->video_mode.alt); - - if (dev->video_mode.alt_max_pkt_size != NULL) - dev->video_mode.max_pkt_size = - dev->video_mode.alt_max_pkt_size[dev->video_mode.alt]; - cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", - dev->video_mode.alt, - dev->video_mode.max_pkt_size); - errCode = - usb_set_interface(dev->udev, usb_interface_index, - dev->video_mode.alt); - if (errCode < 0) { - cx231xx_errdev - ("cannot change alt number to %d (error=%i)\n", - dev->video_mode.alt, errCode); - return errCode; - } - } - return 0; -} - -int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt) -{ - int status = 0; - u32 usb_interface_index = 0; - u32 max_pkt_size = 0; - - switch (index) { - case INDEX_TS1: - usb_interface_index = - dev->current_pcb_config.hs_config_info[0].interface_info. - ts1_index + 1; - dev->ts1_mode.alt = alt; - if (dev->ts1_mode.alt_max_pkt_size != NULL) - max_pkt_size = dev->ts1_mode.max_pkt_size = - dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt]; - break; - case INDEX_TS2: - usb_interface_index = - dev->current_pcb_config.hs_config_info[0].interface_info. - ts2_index + 1; - break; - case INDEX_AUDIO: - usb_interface_index = - dev->current_pcb_config.hs_config_info[0].interface_info. - audio_index + 1; - dev->adev.alt = alt; - if (dev->adev.alt_max_pkt_size != NULL) - max_pkt_size = dev->adev.max_pkt_size = - dev->adev.alt_max_pkt_size[dev->adev.alt]; - break; - case INDEX_VIDEO: - usb_interface_index = - dev->current_pcb_config.hs_config_info[0].interface_info. - video_index + 1; - dev->video_mode.alt = alt; - if (dev->video_mode.alt_max_pkt_size != NULL) - max_pkt_size = dev->video_mode.max_pkt_size = - dev->video_mode.alt_max_pkt_size[dev->video_mode. - alt]; - break; - case INDEX_VANC: - if (dev->board.no_alt_vanc) - return 0; - usb_interface_index = - dev->current_pcb_config.hs_config_info[0].interface_info. - vanc_index + 1; - dev->vbi_mode.alt = alt; - if (dev->vbi_mode.alt_max_pkt_size != NULL) - max_pkt_size = dev->vbi_mode.max_pkt_size = - dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt]; - break; - case INDEX_HANC: - usb_interface_index = - dev->current_pcb_config.hs_config_info[0].interface_info. - hanc_index + 1; - dev->sliced_cc_mode.alt = alt; - if (dev->sliced_cc_mode.alt_max_pkt_size != NULL) - max_pkt_size = dev->sliced_cc_mode.max_pkt_size = - dev->sliced_cc_mode.alt_max_pkt_size[dev-> - sliced_cc_mode. - alt]; - break; - default: - break; - } - - if (alt > 0 && max_pkt_size == 0) { - cx231xx_errdev - ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n", - usb_interface_index, alt); - /*To workaround error number=-71 on EP0 for videograbber, - need add following codes.*/ - if (dev->board.no_alt_vanc) - return -1; - } - - cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u," - "Interface = %d\n", alt, max_pkt_size, - usb_interface_index); - - if (usb_interface_index > 0) { - status = usb_set_interface(dev->udev, usb_interface_index, alt); - if (status < 0) { - cx231xx_errdev - ("can't change interface %d alt no. to %d (err=%i)\n", - usb_interface_index, alt, status); - return status; - } - } - - return status; -} -EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting); - -int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio) -{ - int rc = 0; - - if (!gpio) - return rc; - - /* Send GPIO reset sequences specified at board entry */ - while (gpio->sleep >= 0) { - rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val); - if (rc < 0) - return rc; - - if (gpio->sleep > 0) - msleep(gpio->sleep); - - gpio++; - } - return rc; -} - -int cx231xx_demod_reset(struct cx231xx *dev) -{ - - u8 status = 0; - u8 value[4] = { 0, 0, 0, 0 }; - - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, - value, 4); - - cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, - value[0], value[1], value[2], value[3]); - - cx231xx_coredbg("Enter cx231xx_demod_reset()\n"); - - value[1] = (u8) 0x3; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(10); - - value[1] = (u8) 0x0; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(10); - - value[1] = (u8) 0x3; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(10); - - - - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, - value, 4); - - cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, - value[0], value[1], value[2], value[3]); - - return status; -} -EXPORT_SYMBOL_GPL(cx231xx_demod_reset); -int is_fw_load(struct cx231xx *dev) -{ - return cx231xx_check_fw(dev); -} -EXPORT_SYMBOL_GPL(is_fw_load); - -int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) -{ - int errCode = 0; - - if (dev->mode == set_mode) - return 0; - - if (set_mode == CX231XX_SUSPEND) { - /* Set the chip in power saving mode */ - dev->mode = set_mode; - } - - /* Resource is locked */ - if (dev->mode != CX231XX_SUSPEND) - return -EINVAL; - - dev->mode = set_mode; - - if (dev->mode == CX231XX_DIGITAL_MODE)/* Set Digital power mode */ { - /* set AGC mode to Digital */ - switch (dev->model) { - case CX231XX_BOARD_CNXT_CARRAERA: - case CX231XX_BOARD_CNXT_RDE_250: - case CX231XX_BOARD_CNXT_SHELBY: - case CX231XX_BOARD_CNXT_RDU_250: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); - break; - case CX231XX_BOARD_CNXT_RDE_253S: - case CX231XX_BOARD_CNXT_RDU_253S: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); - break; - case CX231XX_BOARD_HAUPPAUGE_EXETER: - errCode = cx231xx_set_power_mode(dev, - POLARIS_AVMODE_DIGITAL); - break; - default: - break; - } - } else/* Set Analog Power mode */ { - /* set AGC mode to Analog */ - switch (dev->model) { - case CX231XX_BOARD_CNXT_CARRAERA: - case CX231XX_BOARD_CNXT_RDE_250: - case CX231XX_BOARD_CNXT_SHELBY: - case CX231XX_BOARD_CNXT_RDU_250: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); - break; - case CX231XX_BOARD_CNXT_RDE_253S: - case CX231XX_BOARD_CNXT_RDU_253S: - case CX231XX_BOARD_HAUPPAUGE_EXETER: - case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); - break; - default: - break; - } - } - - return errCode ? -EINVAL : 0; -} -EXPORT_SYMBOL_GPL(cx231xx_set_mode); - -int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size) -{ - int errCode = 0; - int actlen, ret = -ENOMEM; - u32 *buffer; - - buffer = kzalloc(4096, GFP_KERNEL); - if (buffer == NULL) { - cx231xx_info("out of mem\n"); - return -ENOMEM; - } - memcpy(&buffer[0], firmware, 4096); - - ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5), - buffer, 4096, &actlen, 2000); - - if (ret) - cx231xx_info("bulk message failed: %d (%d/%d)", ret, - size, actlen); - else { - errCode = actlen != size ? -1 : 0; - } - kfree(buffer); - return errCode; -} - -/***************************************************************** -* URB Streaming functions * -******************************************************************/ - -/* - * IRQ callback, called by URB callback - */ -static void cx231xx_isoc_irq_callback(struct urb *urb) -{ - struct cx231xx_dmaqueue *dma_q = urb->context; - struct cx231xx_video_mode *vmode = - container_of(dma_q, struct cx231xx_video_mode, vidq); - struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - int i; - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - cx231xx_isocdbg("urb completition error %d.\n", urb->status); - break; - } - - /* Copy data from URB */ - spin_lock(&dev->video_mode.slock); - dev->video_mode.isoc_ctl.isoc_copy(dev, urb); - spin_unlock(&dev->video_mode.slock); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) { - cx231xx_isocdbg("urb resubmit failed (error=%i)\n", - urb->status); - } -} -/***************************************************************** -* URB Streaming functions * -******************************************************************/ - -/* - * IRQ callback, called by URB callback - */ -static void cx231xx_bulk_irq_callback(struct urb *urb) -{ - struct cx231xx_dmaqueue *dma_q = urb->context; - struct cx231xx_video_mode *vmode = - container_of(dma_q, struct cx231xx_video_mode, vidq); - struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - cx231xx_isocdbg("urb completition error %d.\n", urb->status); - break; - } - - /* Copy data from URB */ - spin_lock(&dev->video_mode.slock); - dev->video_mode.bulk_ctl.bulk_copy(dev, urb); - spin_unlock(&dev->video_mode.slock); - - /* Reset urb buffers */ - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) { - cx231xx_isocdbg("urb resubmit failed (error=%i)\n", - urb->status); - } -} -/* - * Stop and Deallocate URBs - */ -void cx231xx_uninit_isoc(struct cx231xx *dev) -{ - struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; - struct urb *urb; - int i; - - cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n"); - - dev->video_mode.isoc_ctl.nfields = -1; - for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { - urb = dev->video_mode.isoc_ctl.urb[i]; - if (urb) { - if (!irqs_disabled()) - usb_kill_urb(urb); - else - usb_unlink_urb(urb); - - if (dev->video_mode.isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->video_mode.isoc_ctl. - transfer_buffer[i], - urb->transfer_dma); - } - usb_free_urb(urb); - dev->video_mode.isoc_ctl.urb[i] = NULL; - } - dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->video_mode.isoc_ctl.urb); - kfree(dev->video_mode.isoc_ctl.transfer_buffer); - kfree(dma_q->p_left_data); - - dev->video_mode.isoc_ctl.urb = NULL; - dev->video_mode.isoc_ctl.transfer_buffer = NULL; - dev->video_mode.isoc_ctl.num_bufs = 0; - dma_q->p_left_data = NULL; - - if (dev->mode_tv == 0) - cx231xx_capture_start(dev, 0, Raw_Video); - else - cx231xx_capture_start(dev, 0, TS1_serial_mode); - - -} -EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc); - -/* - * Stop and Deallocate URBs - */ -void cx231xx_uninit_bulk(struct cx231xx *dev) -{ - struct urb *urb; - int i; - - cx231xx_isocdbg("cx231xx: called cx231xx_uninit_bulk\n"); - - dev->video_mode.bulk_ctl.nfields = -1; - for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) { - urb = dev->video_mode.bulk_ctl.urb[i]; - if (urb) { - if (!irqs_disabled()) - usb_kill_urb(urb); - else - usb_unlink_urb(urb); - - if (dev->video_mode.bulk_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->video_mode.isoc_ctl. - transfer_buffer[i], - urb->transfer_dma); - } - usb_free_urb(urb); - dev->video_mode.bulk_ctl.urb[i] = NULL; - } - dev->video_mode.bulk_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->video_mode.bulk_ctl.urb); - kfree(dev->video_mode.bulk_ctl.transfer_buffer); - - dev->video_mode.bulk_ctl.urb = NULL; - dev->video_mode.bulk_ctl.transfer_buffer = NULL; - dev->video_mode.bulk_ctl.num_bufs = 0; - - if (dev->mode_tv == 0) - cx231xx_capture_start(dev, 0, Raw_Video); - else - cx231xx_capture_start(dev, 0, TS1_serial_mode); - - -} -EXPORT_SYMBOL_GPL(cx231xx_uninit_bulk); - -/* - * Allocate URBs and start IRQ - */ -int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct cx231xx *dev, struct urb *urb)) -{ - struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; - int i; - int sb_size, pipe; - struct urb *urb; - int j, k; - int rc; - - /* De-allocates all pending stuff */ - cx231xx_uninit_isoc(dev); - - dma_q->p_left_data = kzalloc(4096, GFP_KERNEL); - if (dma_q->p_left_data == NULL) { - cx231xx_info("out of mem\n"); - return -ENOMEM; - } - - - - dev->video_mode.isoc_ctl.isoc_copy = isoc_copy; - dev->video_mode.isoc_ctl.num_bufs = num_bufs; - dma_q->pos = 0; - dma_q->is_partial_line = 0; - dma_q->last_sav = 0; - dma_q->current_field = -1; - dma_q->field1_done = 0; - dma_q->lines_per_field = dev->height / 2; - dma_q->bytes_left_in_line = dev->width << 1; - dma_q->lines_completed = 0; - dma_q->mpeg_buffer_done = 0; - dma_q->left_data_count = 0; - dma_q->mpeg_buffer_completed = 0; - dma_q->add_ps_package_head = CX231XX_NEED_ADD_PS_PACKAGE_HEAD; - dma_q->ps_head[0] = 0x00; - dma_q->ps_head[1] = 0x00; - dma_q->ps_head[2] = 0x01; - dma_q->ps_head[3] = 0xBA; - for (i = 0; i < 8; i++) - dma_q->partial_buf[i] = 0; - - dev->video_mode.isoc_ctl.urb = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); - if (!dev->video_mode.isoc_ctl.urb) { - cx231xx_errdev("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - dev->video_mode.isoc_ctl.transfer_buffer = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); - if (!dev->video_mode.isoc_ctl.transfer_buffer) { - cx231xx_errdev("cannot allocate memory for usbtransfer\n"); - kfree(dev->video_mode.isoc_ctl.urb); - return -ENOMEM; - } - - dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size; - dev->video_mode.isoc_ctl.buf = NULL; - - sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size; - - if (dev->mode_tv == 1) - dev->video_mode.end_point_addr = 0x81; - else - dev->video_mode.end_point_addr = 0x84; - - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) { - cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i); - cx231xx_uninit_isoc(dev); - return -ENOMEM; - } - dev->video_mode.isoc_ctl.urb[i] = urb; - - dev->video_mode.isoc_ctl.transfer_buffer[i] = - usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, - &urb->transfer_dma); - if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) { - cx231xx_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - cx231xx_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size); - - pipe = - usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr); - - usb_fill_int_urb(urb, dev->udev, pipe, - dev->video_mode.isoc_ctl.transfer_buffer[i], - sb_size, cx231xx_isoc_irq_callback, dma_q, 1); - - urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - k = 0; - for (j = 0; j < max_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - dev->video_mode.isoc_ctl.max_pkt_size; - k += dev->video_mode.isoc_ctl.max_pkt_size; - } - } - - init_waitqueue_head(&dma_q->wq); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i], - GFP_ATOMIC); - if (rc) { - cx231xx_err("submit of urb %i failed (error=%i)\n", i, - rc); - cx231xx_uninit_isoc(dev); - return rc; - } - } - - if (dev->mode_tv == 0) - cx231xx_capture_start(dev, 1, Raw_Video); - else - cx231xx_capture_start(dev, 1, TS1_serial_mode); - - return 0; -} -EXPORT_SYMBOL_GPL(cx231xx_init_isoc); - -/* - * Allocate URBs and start IRQ - */ -int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*bulk_copy) (struct cx231xx *dev, struct urb *urb)) -{ - struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; - int i; - int sb_size, pipe; - struct urb *urb; - int rc; - - dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; - - cx231xx_coredbg("Setting Video mux to %d\n", dev->video_input); - - video_mux(dev, dev->video_input); - - /* De-allocates all pending stuff */ - cx231xx_uninit_bulk(dev); - - dev->video_mode.bulk_ctl.bulk_copy = bulk_copy; - dev->video_mode.bulk_ctl.num_bufs = num_bufs; - dma_q->pos = 0; - dma_q->is_partial_line = 0; - dma_q->last_sav = 0; - dma_q->current_field = -1; - dma_q->field1_done = 0; - dma_q->lines_per_field = dev->height / 2; - dma_q->bytes_left_in_line = dev->width << 1; - dma_q->lines_completed = 0; - dma_q->mpeg_buffer_done = 0; - dma_q->left_data_count = 0; - dma_q->mpeg_buffer_completed = 0; - dma_q->ps_head[0] = 0x00; - dma_q->ps_head[1] = 0x00; - dma_q->ps_head[2] = 0x01; - dma_q->ps_head[3] = 0xBA; - for (i = 0; i < 8; i++) - dma_q->partial_buf[i] = 0; - - dev->video_mode.bulk_ctl.urb = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); - if (!dev->video_mode.bulk_ctl.urb) { - cx231xx_errdev("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - dev->video_mode.bulk_ctl.transfer_buffer = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); - if (!dev->video_mode.bulk_ctl.transfer_buffer) { - cx231xx_errdev("cannot allocate memory for usbtransfer\n"); - kfree(dev->video_mode.bulk_ctl.urb); - return -ENOMEM; - } - - dev->video_mode.bulk_ctl.max_pkt_size = max_pkt_size; - dev->video_mode.bulk_ctl.buf = NULL; - - sb_size = max_packets * dev->video_mode.bulk_ctl.max_pkt_size; - - if (dev->mode_tv == 1) - dev->video_mode.end_point_addr = 0x81; - else - dev->video_mode.end_point_addr = 0x84; - - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) { - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i); - cx231xx_uninit_bulk(dev); - return -ENOMEM; - } - dev->video_mode.bulk_ctl.urb[i] = urb; - urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - - dev->video_mode.bulk_ctl.transfer_buffer[i] = - usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, - &urb->transfer_dma); - if (!dev->video_mode.bulk_ctl.transfer_buffer[i]) { - cx231xx_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - cx231xx_uninit_bulk(dev); - return -ENOMEM; - } - memset(dev->video_mode.bulk_ctl.transfer_buffer[i], 0, sb_size); - - pipe = usb_rcvbulkpipe(dev->udev, - dev->video_mode.end_point_addr); - usb_fill_bulk_urb(urb, dev->udev, pipe, - dev->video_mode.bulk_ctl.transfer_buffer[i], - sb_size, cx231xx_bulk_irq_callback, dma_q); - } - - init_waitqueue_head(&dma_q->wq); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->video_mode.bulk_ctl.urb[i], - GFP_ATOMIC); - if (rc) { - cx231xx_err("submit of urb %i failed (error=%i)\n", i, - rc); - cx231xx_uninit_bulk(dev); - return rc; - } - } - - if (dev->mode_tv == 0) - cx231xx_capture_start(dev, 1, Raw_Video); - else - cx231xx_capture_start(dev, 1, TS1_serial_mode); - - return 0; -} -EXPORT_SYMBOL_GPL(cx231xx_init_bulk); -void cx231xx_stop_TS1(struct cx231xx *dev) -{ - u8 val[4] = { 0, 0, 0, 0 }; - - val[0] = 0x00; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); - - val[0] = 0x00; - val[1] = 0x70; - val[2] = 0x04; - val[3] = 0x00; - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); -} -/* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */ -void cx231xx_start_TS1(struct cx231xx *dev) -{ - u8 val[4] = { 0, 0, 0, 0 }; - - val[0] = 0x03; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); - - val[0] = 0x04; - val[1] = 0xA3; - val[2] = 0x3B; - val[3] = 0x00; - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); -} -/* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */ -/***************************************************************** -* Device Init/UnInit functions * -******************************************************************/ -int cx231xx_dev_init(struct cx231xx *dev) -{ - int errCode = 0; - - /* Initialize I2C bus */ - - /* External Master 1 Bus */ - dev->i2c_bus[0].nr = 0; - dev->i2c_bus[0].dev = dev; - dev->i2c_bus[0].i2c_period = I2C_SPEED_100K; /* 100 KHz */ - dev->i2c_bus[0].i2c_nostop = 0; - dev->i2c_bus[0].i2c_reserve = 0; - - /* External Master 2 Bus */ - dev->i2c_bus[1].nr = 1; - dev->i2c_bus[1].dev = dev; - dev->i2c_bus[1].i2c_period = I2C_SPEED_100K; /* 100 KHz */ - dev->i2c_bus[1].i2c_nostop = 0; - dev->i2c_bus[1].i2c_reserve = 0; - - /* Internal Master 3 Bus */ - dev->i2c_bus[2].nr = 2; - dev->i2c_bus[2].dev = dev; - dev->i2c_bus[2].i2c_period = I2C_SPEED_100K; /* 100kHz */ - dev->i2c_bus[2].i2c_nostop = 0; - dev->i2c_bus[2].i2c_reserve = 0; - - /* register I2C buses */ - cx231xx_i2c_register(&dev->i2c_bus[0]); - cx231xx_i2c_register(&dev->i2c_bus[1]); - cx231xx_i2c_register(&dev->i2c_bus[2]); - - /* init hardware */ - /* Note : with out calling set power mode function, - afe can not be set up correctly */ - if (dev->board.external_av) { - errCode = cx231xx_set_power_mode(dev, - POLARIS_AVMODE_ENXTERNAL_AV); - if (errCode < 0) { - cx231xx_errdev - ("%s: Failed to set Power - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - } else { - errCode = cx231xx_set_power_mode(dev, - POLARIS_AVMODE_ANALOGT_TV); - if (errCode < 0) { - cx231xx_errdev - ("%s: Failed to set Power - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - } - - /* reset the Tuner, if it is a Xceive tuner */ - if ((dev->board.tuner_type == TUNER_XC5000) || - (dev->board.tuner_type == TUNER_XC2028)) - cx231xx_gpio_set(dev, dev->board.tuner_gpio); - - /* initialize Colibri block */ - errCode = cx231xx_afe_init_super_block(dev, 0x23c); - if (errCode < 0) { - cx231xx_errdev - ("%s: cx231xx_afe init super block - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - errCode = cx231xx_afe_init_channels(dev); - if (errCode < 0) { - cx231xx_errdev - ("%s: cx231xx_afe init channels - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* Set DIF in By pass mode */ - errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); - if (errCode < 0) { - cx231xx_errdev - ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* I2S block related functions */ - errCode = cx231xx_i2s_blk_initialize(dev); - if (errCode < 0) { - cx231xx_errdev - ("%s: cx231xx_i2s block initialize - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* init control pins */ - errCode = cx231xx_init_ctrl_pin_status(dev); - if (errCode < 0) { - cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* set AGC mode to Analog */ - switch (dev->model) { - case CX231XX_BOARD_CNXT_CARRAERA: - case CX231XX_BOARD_CNXT_RDE_250: - case CX231XX_BOARD_CNXT_SHELBY: - case CX231XX_BOARD_CNXT_RDU_250: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); - break; - case CX231XX_BOARD_CNXT_RDE_253S: - case CX231XX_BOARD_CNXT_RDU_253S: - case CX231XX_BOARD_HAUPPAUGE_EXETER: - case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: - case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); - break; - default: - break; - } - if (errCode < 0) { - cx231xx_errdev - ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* set all alternate settings to zero initially */ - cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); - cx231xx_set_alt_setting(dev, INDEX_VANC, 0); - cx231xx_set_alt_setting(dev, INDEX_HANC, 0); - if (dev->board.has_dvb) - cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - - /* set the I2C master port to 3 on channel 1 */ - errCode = cx231xx_enable_i2c_port_3(dev, true); - - return errCode; -} -EXPORT_SYMBOL_GPL(cx231xx_dev_init); - -void cx231xx_dev_uninit(struct cx231xx *dev) -{ - /* Un Initialize I2C bus */ - cx231xx_i2c_unregister(&dev->i2c_bus[2]); - cx231xx_i2c_unregister(&dev->i2c_bus[1]); - cx231xx_i2c_unregister(&dev->i2c_bus[0]); -} -EXPORT_SYMBOL_GPL(cx231xx_dev_uninit); - -/***************************************************************** -* G P I O related functions * -******************************************************************/ -int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, - u8 len, u8 request, u8 direction) -{ - int status = 0; - struct VENDOR_REQUEST_IN ven_req; - - /* Set wValue */ - ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff); - - /* set request */ - if (!request) { - if (direction) - ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */ - else - ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */ - } else { - if (direction) - ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */ - else - ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */ - } - - /* set index value */ - ven_req.wIndex = (u16) (gpio_bit & 0xffff); - - /* set wLength value */ - ven_req.wLength = len; - - /* set bData value */ - ven_req.bData = 0; - - /* set the buffer for read / write */ - ven_req.pBuff = gpio_val; - - /* set the direction */ - if (direction) { - ven_req.direction = USB_DIR_IN; - memset(ven_req.pBuff, 0x00, ven_req.wLength); - } else - ven_req.direction = USB_DIR_OUT; - - - /* call common vendor command request */ - status = cx231xx_send_vendor_cmd(dev, &ven_req); - if (status < 0) { - cx231xx_info - ("UsbInterface::sendCommand, failed with status -%d\n", - status); - } - - return status; -} -EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd); - -/***************************************************************** - * C O N T R O L - Register R E A D / W R I T E functions * - *****************************************************************/ -int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode) -{ - u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; - u32 tmp = 0; - int status = 0; - - status = - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4); - if (status < 0) - return status; - - tmp = *((u32 *) value); - tmp |= mode; - - value[0] = (u8) tmp; - value[1] = (u8) (tmp >> 8); - value[2] = (u8) (tmp >> 16); - value[3] = (u8) (tmp >> 24); - - status = - cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4); - - return status; -} - -/***************************************************************** - * I 2 C Internal C O N T R O L functions * - *****************************************************************/ -int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, - u8 saddr_len, u32 *data, u8 data_len, int master) -{ - int status = 0; - struct cx231xx_i2c_xfer_data req_data; - u8 value[64] = "0"; - - if (saddr_len == 0) - saddr = 0; - else if (saddr_len == 1) - saddr &= 0xff; - - /* prepare xfer_data struct */ - req_data.dev_addr = dev_addr >> 1; - req_data.direction = I2C_M_RD; - req_data.saddr_len = saddr_len; - req_data.saddr_dat = saddr; - req_data.buf_size = data_len; - req_data.p_buffer = (u8 *) value; - - /* usb send command */ - if (master == 0) - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], - &req_data); - else if (master == 1) - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1], - &req_data); - else if (master == 2) - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2], - &req_data); - - if (status >= 0) { - /* Copy the data read back to main buffer */ - if (data_len == 1) - *data = value[0]; - else if (data_len == 4) - *data = - value[0] | value[1] << 8 | value[2] << 16 | value[3] - << 24; - else if (data_len > 4) - *data = value[saddr]; - } - - return status; -} - -int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, - u8 saddr_len, u32 data, u8 data_len, int master) -{ - int status = 0; - u8 value[4] = { 0, 0, 0, 0 }; - struct cx231xx_i2c_xfer_data req_data; - - value[0] = (u8) data; - value[1] = (u8) (data >> 8); - value[2] = (u8) (data >> 16); - value[3] = (u8) (data >> 24); - - if (saddr_len == 0) - saddr = 0; - else if (saddr_len == 1) - saddr &= 0xff; - - /* prepare xfer_data struct */ - req_data.dev_addr = dev_addr >> 1; - req_data.direction = 0; - req_data.saddr_len = saddr_len; - req_data.saddr_dat = saddr; - req_data.buf_size = data_len; - req_data.p_buffer = value; - - /* usb send command */ - if (master == 0) - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], - &req_data); - else if (master == 1) - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1], - &req_data); - else if (master == 2) - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2], - &req_data); - - return status; -} - -int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, - u8 saddr_len, u32 *data, u8 data_len) -{ - int status = 0; - struct cx231xx_i2c_xfer_data req_data; - u8 value[4] = { 0, 0, 0, 0 }; - - if (saddr_len == 0) - saddr = 0; - else if (saddr_len == 1) - saddr &= 0xff; - - /* prepare xfer_data struct */ - req_data.dev_addr = dev_addr >> 1; - req_data.direction = I2C_M_RD; - req_data.saddr_len = saddr_len; - req_data.saddr_dat = saddr; - req_data.buf_size = data_len; - req_data.p_buffer = (u8 *) value; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); - - if (status >= 0) { - /* Copy the data read back to main buffer */ - if (data_len == 1) - *data = value[0]; - else - *data = - value[0] | value[1] << 8 | value[2] << 16 | value[3] - << 24; - } - - return status; -} - -int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, - u8 saddr_len, u32 data, u8 data_len) -{ - int status = 0; - u8 value[4] = { 0, 0, 0, 0 }; - struct cx231xx_i2c_xfer_data req_data; - - value[0] = (u8) data; - value[1] = (u8) (data >> 8); - value[2] = (u8) (data >> 16); - value[3] = (u8) (data >> 24); - - if (saddr_len == 0) - saddr = 0; - else if (saddr_len == 1) - saddr &= 0xff; - - /* prepare xfer_data struct */ - req_data.dev_addr = dev_addr >> 1; - req_data.direction = 0; - req_data.saddr_len = saddr_len; - req_data.saddr_dat = saddr; - req_data.buf_size = data_len; - req_data.p_buffer = value; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); - - return status; -} - -int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, - u16 register_address, u8 bit_start, u8 bit_end, - u32 value) -{ - int status = 0; - u32 tmp; - u32 mask = 0; - int i; - - if (bit_start > (size - 1) || bit_end > (size - 1)) - return -1; - - if (size == 8) { - status = - cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, - &tmp, 1); - } else { - status = - cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, - &tmp, 4); - } - - if (status < 0) - return status; - - mask = 1 << bit_end; - for (i = bit_end; i > bit_start && i > 0; i--) - mask = mask + (1 << (i - 1)); - - value <<= bit_start; - - if (size == 8) { - tmp &= ~mask; - tmp |= value; - tmp &= 0xff; - status = - cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, - tmp, 1); - } else { - tmp &= ~mask; - tmp |= value; - status = - cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, - tmp, 4); - } - - return status; -} - -int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, - u16 saddr, u32 mask, u32 value) -{ - u32 temp; - int status = 0; - - status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4); - - if (status < 0) - return status; - - temp &= ~mask; - temp |= value; - - status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4); - - return status; -} - -u32 cx231xx_set_field(u32 field_mask, u32 data) -{ - u32 temp; - - for (temp = field_mask; (temp & 1) == 0; temp >>= 1) - data <<= 1; - - return data; -} diff --git a/drivers/media/video/cx231xx/cx231xx-dif.h b/drivers/media/video/cx231xx/cx231xx-dif.h deleted file mode 100644 index 2b63c2f6d3b0..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-dif.h +++ /dev/null @@ -1,3178 +0,0 @@ -/* - * cx231xx-dif.h - driver for Conexant Cx23100/101/102 USB video capture devices - * - * Copyright {C} 2009 - * - * This program is free software, 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 _CX231XX_DIF_H -#define _CX231XX_DIF_H - -#include "cx231xx-reg.h" - -struct dif_settings{ - u32 if_freq; - u32 register_address; - u32 value; -}; - -static struct dif_settings Dif_set_array[] = { - -/*case 3000000:*/ -/* BEGIN - DIF BPF register values from 30_quant.dat*/ -{3000000, DIF_BPF_COEFF01, 0x00000002}, -{3000000, DIF_BPF_COEFF23, 0x00080012}, -{3000000, DIF_BPF_COEFF45, 0x001e0024}, -{3000000, DIF_BPF_COEFF67, 0x001bfff8}, -{3000000, DIF_BPF_COEFF89, 0xffb4ff50}, -{3000000, DIF_BPF_COEFF1011, 0xfed8fe68}, -{3000000, DIF_BPF_COEFF1213, 0xfe24fe34}, -{3000000, DIF_BPF_COEFF1415, 0xfebaffc7}, -{3000000, DIF_BPF_COEFF1617, 0x014d031f}, -{3000000, DIF_BPF_COEFF1819, 0x04f0065d}, -{3000000, DIF_BPF_COEFF2021, 0x07010688}, -{3000000, DIF_BPF_COEFF2223, 0x04c901d6}, -{3000000, DIF_BPF_COEFF2425, 0xfe00f9d3}, -{3000000, DIF_BPF_COEFF2627, 0xf600f342}, -{3000000, DIF_BPF_COEFF2829, 0xf235f337}, -{3000000, DIF_BPF_COEFF3031, 0xf64efb22}, -{3000000, DIF_BPF_COEFF3233, 0x0105070f}, -{3000000, DIF_BPF_COEFF3435, 0x0c460fce}, -{3000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 30_quant.dat*/ - - -/*case 3100000:*/ -/* BEGIN - DIF BPF register values from 31_quant.dat*/ -{3100000, DIF_BPF_COEFF01, 0x00000001}, -{3100000, DIF_BPF_COEFF23, 0x00070012}, -{3100000, DIF_BPF_COEFF45, 0x00220032}, -{3100000, DIF_BPF_COEFF67, 0x00370026}, -{3100000, DIF_BPF_COEFF89, 0xfff0ff91}, -{3100000, DIF_BPF_COEFF1011, 0xff0efe7c}, -{3100000, DIF_BPF_COEFF1213, 0xfe01fdcc}, -{3100000, DIF_BPF_COEFF1415, 0xfe0afedb}, -{3100000, DIF_BPF_COEFF1617, 0x00440224}, -{3100000, DIF_BPF_COEFF1819, 0x0434060c}, -{3100000, DIF_BPF_COEFF2021, 0x0738074e}, -{3100000, DIF_BPF_COEFF2223, 0x06090361}, -{3100000, DIF_BPF_COEFF2425, 0xff99fb39}, -{3100000, DIF_BPF_COEFF2627, 0xf6fef3b6}, -{3100000, DIF_BPF_COEFF2829, 0xf21af2a5}, -{3100000, DIF_BPF_COEFF3031, 0xf573fa33}, -{3100000, DIF_BPF_COEFF3233, 0x0034067d}, -{3100000, DIF_BPF_COEFF3435, 0x0bfb0fb9}, -{3100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 31_quant.dat*/ - - -/*case 3200000:*/ -/* BEGIN - DIF BPF register values from 32_quant.dat*/ -{3200000, DIF_BPF_COEFF01, 0x00000000}, -{3200000, DIF_BPF_COEFF23, 0x0004000e}, -{3200000, DIF_BPF_COEFF45, 0x00200038}, -{3200000, DIF_BPF_COEFF67, 0x004c004f}, -{3200000, DIF_BPF_COEFF89, 0x002fffdf}, -{3200000, DIF_BPF_COEFF1011, 0xff5cfeb6}, -{3200000, DIF_BPF_COEFF1213, 0xfe0dfd92}, -{3200000, DIF_BPF_COEFF1415, 0xfd7ffe03}, -{3200000, DIF_BPF_COEFF1617, 0xff36010a}, -{3200000, DIF_BPF_COEFF1819, 0x03410575}, -{3200000, DIF_BPF_COEFF2021, 0x072607d2}, -{3200000, DIF_BPF_COEFF2223, 0x071804d5}, -{3200000, DIF_BPF_COEFF2425, 0x0134fcb7}, -{3200000, DIF_BPF_COEFF2627, 0xf81ff451}, -{3200000, DIF_BPF_COEFF2829, 0xf223f22e}, -{3200000, DIF_BPF_COEFF3031, 0xf4a7f94b}, -{3200000, DIF_BPF_COEFF3233, 0xff6405e8}, -{3200000, DIF_BPF_COEFF3435, 0x0bae0fa4}, -{3200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 32_quant.dat*/ - - -/*case 3300000:*/ -/* BEGIN - DIF BPF register values from 33_quant.dat*/ -{3300000, DIF_BPF_COEFF01, 0x0000ffff}, -{3300000, DIF_BPF_COEFF23, 0x00000008}, -{3300000, DIF_BPF_COEFF45, 0x001a0036}, -{3300000, DIF_BPF_COEFF67, 0x0056006d}, -{3300000, DIF_BPF_COEFF89, 0x00670030}, -{3300000, DIF_BPF_COEFF1011, 0xffbdff10}, -{3300000, DIF_BPF_COEFF1213, 0xfe46fd8d}, -{3300000, DIF_BPF_COEFF1415, 0xfd25fd4f}, -{3300000, DIF_BPF_COEFF1617, 0xfe35ffe0}, -{3300000, DIF_BPF_COEFF1819, 0x0224049f}, -{3300000, DIF_BPF_COEFF2021, 0x06c9080e}, -{3300000, DIF_BPF_COEFF2223, 0x07ef0627}, -{3300000, DIF_BPF_COEFF2425, 0x02c9fe45}, -{3300000, DIF_BPF_COEFF2627, 0xf961f513}, -{3300000, DIF_BPF_COEFF2829, 0xf250f1d2}, -{3300000, DIF_BPF_COEFF3031, 0xf3ecf869}, -{3300000, DIF_BPF_COEFF3233, 0xfe930552}, -{3300000, DIF_BPF_COEFF3435, 0x0b5f0f8f}, -{3300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 33_quant.dat*/ - - -/*case 3400000:*/ -/* BEGIN - DIF BPF register values from 34_quant.dat*/ -{3400000, DIF_BPF_COEFF01, 0xfffffffe}, -{3400000, DIF_BPF_COEFF23, 0xfffd0001}, -{3400000, DIF_BPF_COEFF45, 0x000f002c}, -{3400000, DIF_BPF_COEFF67, 0x0054007d}, -{3400000, DIF_BPF_COEFF89, 0x0093007c}, -{3400000, DIF_BPF_COEFF1011, 0x0024ff82}, -{3400000, DIF_BPF_COEFF1213, 0xfea6fdbb}, -{3400000, DIF_BPF_COEFF1415, 0xfd03fcca}, -{3400000, DIF_BPF_COEFF1617, 0xfd51feb9}, -{3400000, DIF_BPF_COEFF1819, 0x00eb0392}, -{3400000, DIF_BPF_COEFF2021, 0x06270802}, -{3400000, DIF_BPF_COEFF2223, 0x08880750}, -{3400000, DIF_BPF_COEFF2425, 0x044dffdb}, -{3400000, DIF_BPF_COEFF2627, 0xfabdf5f8}, -{3400000, DIF_BPF_COEFF2829, 0xf2a0f193}, -{3400000, DIF_BPF_COEFF3031, 0xf342f78f}, -{3400000, DIF_BPF_COEFF3233, 0xfdc404b9}, -{3400000, DIF_BPF_COEFF3435, 0x0b0e0f78}, -{3400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 34_quant.dat*/ - - -/*case 3500000:*/ -/* BEGIN - DIF BPF register values from 35_quant.dat*/ -{3500000, DIF_BPF_COEFF01, 0xfffffffd}, -{3500000, DIF_BPF_COEFF23, 0xfffafff9}, -{3500000, DIF_BPF_COEFF45, 0x0002001b}, -{3500000, DIF_BPF_COEFF67, 0x0046007d}, -{3500000, DIF_BPF_COEFF89, 0x00ad00ba}, -{3500000, DIF_BPF_COEFF1011, 0x00870000}, -{3500000, DIF_BPF_COEFF1213, 0xff26fe1a}, -{3500000, DIF_BPF_COEFF1415, 0xfd1bfc7e}, -{3500000, DIF_BPF_COEFF1617, 0xfc99fda4}, -{3500000, DIF_BPF_COEFF1819, 0xffa5025c}, -{3500000, DIF_BPF_COEFF2021, 0x054507ad}, -{3500000, DIF_BPF_COEFF2223, 0x08dd0847}, -{3500000, DIF_BPF_COEFF2425, 0x05b80172}, -{3500000, DIF_BPF_COEFF2627, 0xfc2ef6ff}, -{3500000, DIF_BPF_COEFF2829, 0xf313f170}, -{3500000, DIF_BPF_COEFF3031, 0xf2abf6bd}, -{3500000, DIF_BPF_COEFF3233, 0xfcf6041f}, -{3500000, DIF_BPF_COEFF3435, 0x0abc0f61}, -{3500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 35_quant.dat*/ - - -/*case 3600000:*/ -/* BEGIN - DIF BPF register values from 36_quant.dat*/ -{3600000, DIF_BPF_COEFF01, 0xfffffffd}, -{3600000, DIF_BPF_COEFF23, 0xfff8fff3}, -{3600000, DIF_BPF_COEFF45, 0xfff50006}, -{3600000, DIF_BPF_COEFF67, 0x002f006c}, -{3600000, DIF_BPF_COEFF89, 0x00b200e3}, -{3600000, DIF_BPF_COEFF1011, 0x00dc007e}, -{3600000, DIF_BPF_COEFF1213, 0xffb9fea0}, -{3600000, DIF_BPF_COEFF1415, 0xfd6bfc71}, -{3600000, DIF_BPF_COEFF1617, 0xfc17fcb1}, -{3600000, DIF_BPF_COEFF1819, 0xfe65010b}, -{3600000, DIF_BPF_COEFF2021, 0x042d0713}, -{3600000, DIF_BPF_COEFF2223, 0x08ec0906}, -{3600000, DIF_BPF_COEFF2425, 0x07020302}, -{3600000, DIF_BPF_COEFF2627, 0xfdaff823}, -{3600000, DIF_BPF_COEFF2829, 0xf3a7f16a}, -{3600000, DIF_BPF_COEFF3031, 0xf228f5f5}, -{3600000, DIF_BPF_COEFF3233, 0xfc2a0384}, -{3600000, DIF_BPF_COEFF3435, 0x0a670f4a}, -{3600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 36_quant.dat*/ - - -/*case 3700000:*/ -/* BEGIN - DIF BPF register values from 37_quant.dat*/ -{3700000, DIF_BPF_COEFF01, 0x0000fffd}, -{3700000, DIF_BPF_COEFF23, 0xfff7ffef}, -{3700000, DIF_BPF_COEFF45, 0xffe9fff1}, -{3700000, DIF_BPF_COEFF67, 0x0010004d}, -{3700000, DIF_BPF_COEFF89, 0x00a100f2}, -{3700000, DIF_BPF_COEFF1011, 0x011a00f0}, -{3700000, DIF_BPF_COEFF1213, 0x0053ff44}, -{3700000, DIF_BPF_COEFF1415, 0xfdedfca2}, -{3700000, DIF_BPF_COEFF1617, 0xfbd3fbef}, -{3700000, DIF_BPF_COEFF1819, 0xfd39ffae}, -{3700000, DIF_BPF_COEFF2021, 0x02ea0638}, -{3700000, DIF_BPF_COEFF2223, 0x08b50987}, -{3700000, DIF_BPF_COEFF2425, 0x08230483}, -{3700000, DIF_BPF_COEFF2627, 0xff39f960}, -{3700000, DIF_BPF_COEFF2829, 0xf45bf180}, -{3700000, DIF_BPF_COEFF3031, 0xf1b8f537}, -{3700000, DIF_BPF_COEFF3233, 0xfb6102e7}, -{3700000, DIF_BPF_COEFF3435, 0x0a110f32}, -{3700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 37_quant.dat*/ - - -/*case 3800000:*/ -/* BEGIN - DIF BPF register values from 38_quant.dat*/ -{3800000, DIF_BPF_COEFF01, 0x0000fffe}, -{3800000, DIF_BPF_COEFF23, 0xfff9ffee}, -{3800000, DIF_BPF_COEFF45, 0xffe1ffdd}, -{3800000, DIF_BPF_COEFF67, 0xfff00024}, -{3800000, DIF_BPF_COEFF89, 0x007c00e5}, -{3800000, DIF_BPF_COEFF1011, 0x013a014a}, -{3800000, DIF_BPF_COEFF1213, 0x00e6fff8}, -{3800000, DIF_BPF_COEFF1415, 0xfe98fd0f}, -{3800000, DIF_BPF_COEFF1617, 0xfbd3fb67}, -{3800000, DIF_BPF_COEFF1819, 0xfc32fe54}, -{3800000, DIF_BPF_COEFF2021, 0x01880525}, -{3800000, DIF_BPF_COEFF2223, 0x083909c7}, -{3800000, DIF_BPF_COEFF2425, 0x091505ee}, -{3800000, DIF_BPF_COEFF2627, 0x00c7fab3}, -{3800000, DIF_BPF_COEFF2829, 0xf52df1b4}, -{3800000, DIF_BPF_COEFF3031, 0xf15df484}, -{3800000, DIF_BPF_COEFF3233, 0xfa9b0249}, -{3800000, DIF_BPF_COEFF3435, 0x09ba0f19}, -{3800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 38_quant.dat*/ - - -/*case 3900000:*/ -/* BEGIN - DIF BPF register values from 39_quant.dat*/ -{3900000, DIF_BPF_COEFF01, 0x00000000}, -{3900000, DIF_BPF_COEFF23, 0xfffbfff0}, -{3900000, DIF_BPF_COEFF45, 0xffdeffcf}, -{3900000, DIF_BPF_COEFF67, 0xffd1fff6}, -{3900000, DIF_BPF_COEFF89, 0x004800be}, -{3900000, DIF_BPF_COEFF1011, 0x01390184}, -{3900000, DIF_BPF_COEFF1213, 0x016300ac}, -{3900000, DIF_BPF_COEFF1415, 0xff5efdb1}, -{3900000, DIF_BPF_COEFF1617, 0xfc17fb23}, -{3900000, DIF_BPF_COEFF1819, 0xfb5cfd0d}, -{3900000, DIF_BPF_COEFF2021, 0x001703e4}, -{3900000, DIF_BPF_COEFF2223, 0x077b09c4}, -{3900000, DIF_BPF_COEFF2425, 0x09d2073c}, -{3900000, DIF_BPF_COEFF2627, 0x0251fc18}, -{3900000, DIF_BPF_COEFF2829, 0xf61cf203}, -{3900000, DIF_BPF_COEFF3031, 0xf118f3dc}, -{3900000, DIF_BPF_COEFF3233, 0xf9d801aa}, -{3900000, DIF_BPF_COEFF3435, 0x09600eff}, -{3900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 39_quant.dat*/ - - -/*case 4000000:*/ -/* BEGIN - DIF BPF register values from 40_quant.dat*/ -{4000000, DIF_BPF_COEFF01, 0x00000001}, -{4000000, DIF_BPF_COEFF23, 0xfffefff4}, -{4000000, DIF_BPF_COEFF45, 0xffe1ffc8}, -{4000000, DIF_BPF_COEFF67, 0xffbaffca}, -{4000000, DIF_BPF_COEFF89, 0x000b0082}, -{4000000, DIF_BPF_COEFF1011, 0x01170198}, -{4000000, DIF_BPF_COEFF1213, 0x01c10152}, -{4000000, DIF_BPF_COEFF1415, 0x0030fe7b}, -{4000000, DIF_BPF_COEFF1617, 0xfc99fb24}, -{4000000, DIF_BPF_COEFF1819, 0xfac3fbe9}, -{4000000, DIF_BPF_COEFF2021, 0xfea5027f}, -{4000000, DIF_BPF_COEFF2223, 0x0683097f}, -{4000000, DIF_BPF_COEFF2425, 0x0a560867}, -{4000000, DIF_BPF_COEFF2627, 0x03d2fd89}, -{4000000, DIF_BPF_COEFF2829, 0xf723f26f}, -{4000000, DIF_BPF_COEFF3031, 0xf0e8f341}, -{4000000, DIF_BPF_COEFF3233, 0xf919010a}, -{4000000, DIF_BPF_COEFF3435, 0x09060ee5}, -{4000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 40_quant.dat*/ - - -/*case 4100000:*/ -/* BEGIN - DIF BPF register values from 41_quant.dat*/ -{4100000, DIF_BPF_COEFF01, 0x00010002}, -{4100000, DIF_BPF_COEFF23, 0x0002fffb}, -{4100000, DIF_BPF_COEFF45, 0xffe8ffca}, -{4100000, DIF_BPF_COEFF67, 0xffacffa4}, -{4100000, DIF_BPF_COEFF89, 0xffcd0036}, -{4100000, DIF_BPF_COEFF1011, 0x00d70184}, -{4100000, DIF_BPF_COEFF1213, 0x01f601dc}, -{4100000, DIF_BPF_COEFF1415, 0x00ffff60}, -{4100000, DIF_BPF_COEFF1617, 0xfd51fb6d}, -{4100000, DIF_BPF_COEFF1819, 0xfa6efaf5}, -{4100000, DIF_BPF_COEFF2021, 0xfd410103}, -{4100000, DIF_BPF_COEFF2223, 0x055708f9}, -{4100000, DIF_BPF_COEFF2425, 0x0a9e0969}, -{4100000, DIF_BPF_COEFF2627, 0x0543ff02}, -{4100000, DIF_BPF_COEFF2829, 0xf842f2f5}, -{4100000, DIF_BPF_COEFF3031, 0xf0cef2b2}, -{4100000, DIF_BPF_COEFF3233, 0xf85e006b}, -{4100000, DIF_BPF_COEFF3435, 0x08aa0ecb}, -{4100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 41_quant.dat*/ - - -/*case 4200000:*/ -/* BEGIN - DIF BPF register values from 42_quant.dat*/ -{4200000, DIF_BPF_COEFF01, 0x00010003}, -{4200000, DIF_BPF_COEFF23, 0x00050003}, -{4200000, DIF_BPF_COEFF45, 0xfff3ffd3}, -{4200000, DIF_BPF_COEFF67, 0xffaaff8b}, -{4200000, DIF_BPF_COEFF89, 0xff95ffe5}, -{4200000, DIF_BPF_COEFF1011, 0x0080014a}, -{4200000, DIF_BPF_COEFF1213, 0x01fe023f}, -{4200000, DIF_BPF_COEFF1415, 0x01ba0050}, -{4200000, DIF_BPF_COEFF1617, 0xfe35fbf8}, -{4200000, DIF_BPF_COEFF1819, 0xfa62fa3b}, -{4200000, DIF_BPF_COEFF2021, 0xfbf9ff7e}, -{4200000, DIF_BPF_COEFF2223, 0x04010836}, -{4200000, DIF_BPF_COEFF2425, 0x0aa90a3d}, -{4200000, DIF_BPF_COEFF2627, 0x069f007f}, -{4200000, DIF_BPF_COEFF2829, 0xf975f395}, -{4200000, DIF_BPF_COEFF3031, 0xf0cbf231}, -{4200000, DIF_BPF_COEFF3233, 0xf7a9ffcb}, -{4200000, DIF_BPF_COEFF3435, 0x084c0eaf}, -{4200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 42_quant.dat*/ - - -/*case 4300000:*/ -/* BEGIN - DIF BPF register values from 43_quant.dat*/ -{4300000, DIF_BPF_COEFF01, 0x00010003}, -{4300000, DIF_BPF_COEFF23, 0x0008000a}, -{4300000, DIF_BPF_COEFF45, 0x0000ffe4}, -{4300000, DIF_BPF_COEFF67, 0xffb4ff81}, -{4300000, DIF_BPF_COEFF89, 0xff6aff96}, -{4300000, DIF_BPF_COEFF1011, 0x001c00f0}, -{4300000, DIF_BPF_COEFF1213, 0x01d70271}, -{4300000, DIF_BPF_COEFF1415, 0x0254013b}, -{4300000, DIF_BPF_COEFF1617, 0xff36fcbd}, -{4300000, DIF_BPF_COEFF1819, 0xfa9ff9c5}, -{4300000, DIF_BPF_COEFF2021, 0xfadbfdfe}, -{4300000, DIF_BPF_COEFF2223, 0x028c073b}, -{4300000, DIF_BPF_COEFF2425, 0x0a750adf}, -{4300000, DIF_BPF_COEFF2627, 0x07e101fa}, -{4300000, DIF_BPF_COEFF2829, 0xfab8f44e}, -{4300000, DIF_BPF_COEFF3031, 0xf0ddf1be}, -{4300000, DIF_BPF_COEFF3233, 0xf6f9ff2b}, -{4300000, DIF_BPF_COEFF3435, 0x07ed0e94}, -{4300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 43_quant.dat*/ - - -/*case 4400000:*/ -/* BEGIN - DIF BPF register values from 44_quant.dat*/ -{4400000, DIF_BPF_COEFF01, 0x00000003}, -{4400000, DIF_BPF_COEFF23, 0x0009000f}, -{4400000, DIF_BPF_COEFF45, 0x000efff8}, -{4400000, DIF_BPF_COEFF67, 0xffc9ff87}, -{4400000, DIF_BPF_COEFF89, 0xff52ff54}, -{4400000, DIF_BPF_COEFF1011, 0xffb5007e}, -{4400000, DIF_BPF_COEFF1213, 0x01860270}, -{4400000, DIF_BPF_COEFF1415, 0x02c00210}, -{4400000, DIF_BPF_COEFF1617, 0x0044fdb2}, -{4400000, DIF_BPF_COEFF1819, 0xfb22f997}, -{4400000, DIF_BPF_COEFF2021, 0xf9f2fc90}, -{4400000, DIF_BPF_COEFF2223, 0x0102060f}, -{4400000, DIF_BPF_COEFF2425, 0x0a050b4c}, -{4400000, DIF_BPF_COEFF2627, 0x0902036e}, -{4400000, DIF_BPF_COEFF2829, 0xfc0af51e}, -{4400000, DIF_BPF_COEFF3031, 0xf106f15a}, -{4400000, DIF_BPF_COEFF3233, 0xf64efe8b}, -{4400000, DIF_BPF_COEFF3435, 0x078d0e77}, -{4400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 44_quant.dat*/ - - -/*case 4500000:*/ -/* BEGIN - DIF BPF register values from 45_quant.dat*/ -{4500000, DIF_BPF_COEFF01, 0x00000002}, -{4500000, DIF_BPF_COEFF23, 0x00080012}, -{4500000, DIF_BPF_COEFF45, 0x0019000e}, -{4500000, DIF_BPF_COEFF67, 0xffe5ff9e}, -{4500000, DIF_BPF_COEFF89, 0xff4fff25}, -{4500000, DIF_BPF_COEFF1011, 0xff560000}, -{4500000, DIF_BPF_COEFF1213, 0x0112023b}, -{4500000, DIF_BPF_COEFF1415, 0x02f702c0}, -{4500000, DIF_BPF_COEFF1617, 0x014dfec8}, -{4500000, DIF_BPF_COEFF1819, 0xfbe5f9b3}, -{4500000, DIF_BPF_COEFF2021, 0xf947fb41}, -{4500000, DIF_BPF_COEFF2223, 0xff7004b9}, -{4500000, DIF_BPF_COEFF2425, 0x095a0b81}, -{4500000, DIF_BPF_COEFF2627, 0x0a0004d8}, -{4500000, DIF_BPF_COEFF2829, 0xfd65f603}, -{4500000, DIF_BPF_COEFF3031, 0xf144f104}, -{4500000, DIF_BPF_COEFF3233, 0xf5aafdec}, -{4500000, DIF_BPF_COEFF3435, 0x072b0e5a}, -{4500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 45_quant.dat*/ - - -/*case 4600000:*/ -/* BEGIN - DIF BPF register values from 46_quant.dat*/ -{4600000, DIF_BPF_COEFF01, 0x00000001}, -{4600000, DIF_BPF_COEFF23, 0x00060012}, -{4600000, DIF_BPF_COEFF45, 0x00200022}, -{4600000, DIF_BPF_COEFF67, 0x0005ffc1}, -{4600000, DIF_BPF_COEFF89, 0xff61ff10}, -{4600000, DIF_BPF_COEFF1011, 0xff09ff82}, -{4600000, DIF_BPF_COEFF1213, 0x008601d7}, -{4600000, DIF_BPF_COEFF1415, 0x02f50340}, -{4600000, DIF_BPF_COEFF1617, 0x0241fff0}, -{4600000, DIF_BPF_COEFF1819, 0xfcddfa19}, -{4600000, DIF_BPF_COEFF2021, 0xf8e2fa1e}, -{4600000, DIF_BPF_COEFF2223, 0xfde30343}, -{4600000, DIF_BPF_COEFF2425, 0x08790b7f}, -{4600000, DIF_BPF_COEFF2627, 0x0ad50631}, -{4600000, DIF_BPF_COEFF2829, 0xfec7f6fc}, -{4600000, DIF_BPF_COEFF3031, 0xf198f0bd}, -{4600000, DIF_BPF_COEFF3233, 0xf50dfd4e}, -{4600000, DIF_BPF_COEFF3435, 0x06c90e3d}, -{4600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 46_quant.dat*/ - - -/*case 4700000:*/ -/* BEGIN - DIF BPF register values from 47_quant.dat*/ -{4700000, DIF_BPF_COEFF01, 0x0000ffff}, -{4700000, DIF_BPF_COEFF23, 0x0003000f}, -{4700000, DIF_BPF_COEFF45, 0x00220030}, -{4700000, DIF_BPF_COEFF67, 0x0025ffed}, -{4700000, DIF_BPF_COEFF89, 0xff87ff15}, -{4700000, DIF_BPF_COEFF1011, 0xfed6ff10}, -{4700000, DIF_BPF_COEFF1213, 0xffed014c}, -{4700000, DIF_BPF_COEFF1415, 0x02b90386}, -{4700000, DIF_BPF_COEFF1617, 0x03110119}, -{4700000, DIF_BPF_COEFF1819, 0xfdfefac4}, -{4700000, DIF_BPF_COEFF2021, 0xf8c6f92f}, -{4700000, DIF_BPF_COEFF2223, 0xfc6701b7}, -{4700000, DIF_BPF_COEFF2425, 0x07670b44}, -{4700000, DIF_BPF_COEFF2627, 0x0b7e0776}, -{4700000, DIF_BPF_COEFF2829, 0x002df807}, -{4700000, DIF_BPF_COEFF3031, 0xf200f086}, -{4700000, DIF_BPF_COEFF3233, 0xf477fcb1}, -{4700000, DIF_BPF_COEFF3435, 0x06650e1e}, -{4700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 47_quant.dat*/ - - -/*case 4800000:*/ -/* BEGIN - DIF BPF register values from 48_quant.dat*/ -{4800000, DIF_BPF_COEFF01, 0xfffffffe}, -{4800000, DIF_BPF_COEFF23, 0xffff0009}, -{4800000, DIF_BPF_COEFF45, 0x001e0038}, -{4800000, DIF_BPF_COEFF67, 0x003f001b}, -{4800000, DIF_BPF_COEFF89, 0xffbcff36}, -{4800000, DIF_BPF_COEFF1011, 0xfec2feb6}, -{4800000, DIF_BPF_COEFF1213, 0xff5600a5}, -{4800000, DIF_BPF_COEFF1415, 0x0248038d}, -{4800000, DIF_BPF_COEFF1617, 0x03b00232}, -{4800000, DIF_BPF_COEFF1819, 0xff39fbab}, -{4800000, DIF_BPF_COEFF2021, 0xf8f4f87f}, -{4800000, DIF_BPF_COEFF2223, 0xfb060020}, -{4800000, DIF_BPF_COEFF2425, 0x062a0ad2}, -{4800000, DIF_BPF_COEFF2627, 0x0bf908a3}, -{4800000, DIF_BPF_COEFF2829, 0x0192f922}, -{4800000, DIF_BPF_COEFF3031, 0xf27df05e}, -{4800000, DIF_BPF_COEFF3233, 0xf3e8fc14}, -{4800000, DIF_BPF_COEFF3435, 0x06000e00}, -{4800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 48_quant.dat*/ - - -/*case 4900000:*/ -/* BEGIN - DIF BPF register values from 49_quant.dat*/ -{4900000, DIF_BPF_COEFF01, 0xfffffffd}, -{4900000, DIF_BPF_COEFF23, 0xfffc0002}, -{4900000, DIF_BPF_COEFF45, 0x00160037}, -{4900000, DIF_BPF_COEFF67, 0x00510046}, -{4900000, DIF_BPF_COEFF89, 0xfff9ff6d}, -{4900000, DIF_BPF_COEFF1011, 0xfed0fe7c}, -{4900000, DIF_BPF_COEFF1213, 0xfecefff0}, -{4900000, DIF_BPF_COEFF1415, 0x01aa0356}, -{4900000, DIF_BPF_COEFF1617, 0x0413032b}, -{4900000, DIF_BPF_COEFF1819, 0x007ffcc5}, -{4900000, DIF_BPF_COEFF2021, 0xf96cf812}, -{4900000, DIF_BPF_COEFF2223, 0xf9cefe87}, -{4900000, DIF_BPF_COEFF2425, 0x04c90a2c}, -{4900000, DIF_BPF_COEFF2627, 0x0c4309b4}, -{4900000, DIF_BPF_COEFF2829, 0x02f3fa4a}, -{4900000, DIF_BPF_COEFF3031, 0xf30ef046}, -{4900000, DIF_BPF_COEFF3233, 0xf361fb7a}, -{4900000, DIF_BPF_COEFF3435, 0x059b0de0}, -{4900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 49_quant.dat*/ - - -/*case 5000000:*/ -/* BEGIN - DIF BPF register values from 50_quant.dat*/ -{5000000, DIF_BPF_COEFF01, 0xfffffffd}, -{5000000, DIF_BPF_COEFF23, 0xfff9fffa}, -{5000000, DIF_BPF_COEFF45, 0x000a002d}, -{5000000, DIF_BPF_COEFF67, 0x00570067}, -{5000000, DIF_BPF_COEFF89, 0x0037ffb5}, -{5000000, DIF_BPF_COEFF1011, 0xfefffe68}, -{5000000, DIF_BPF_COEFF1213, 0xfe62ff3d}, -{5000000, DIF_BPF_COEFF1415, 0x00ec02e3}, -{5000000, DIF_BPF_COEFF1617, 0x043503f6}, -{5000000, DIF_BPF_COEFF1819, 0x01befe05}, -{5000000, DIF_BPF_COEFF2021, 0xfa27f7ee}, -{5000000, DIF_BPF_COEFF2223, 0xf8c6fcf8}, -{5000000, DIF_BPF_COEFF2425, 0x034c0954}, -{5000000, DIF_BPF_COEFF2627, 0x0c5c0aa4}, -{5000000, DIF_BPF_COEFF2829, 0x044cfb7e}, -{5000000, DIF_BPF_COEFF3031, 0xf3b1f03f}, -{5000000, DIF_BPF_COEFF3233, 0xf2e2fae1}, -{5000000, DIF_BPF_COEFF3435, 0x05340dc0}, -{5000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 50_quant.dat*/ - - -/*case 5100000:*/ -/* BEGIN - DIF BPF register values from 51_quant.dat*/ -{5100000, DIF_BPF_COEFF01, 0x0000fffd}, -{5100000, DIF_BPF_COEFF23, 0xfff8fff4}, -{5100000, DIF_BPF_COEFF45, 0xfffd001e}, -{5100000, DIF_BPF_COEFF67, 0x0051007b}, -{5100000, DIF_BPF_COEFF89, 0x006e0006}, -{5100000, DIF_BPF_COEFF1011, 0xff48fe7c}, -{5100000, DIF_BPF_COEFF1213, 0xfe1bfe9a}, -{5100000, DIF_BPF_COEFF1415, 0x001d023e}, -{5100000, DIF_BPF_COEFF1617, 0x04130488}, -{5100000, DIF_BPF_COEFF1819, 0x02e6ff5b}, -{5100000, DIF_BPF_COEFF2021, 0xfb1ef812}, -{5100000, DIF_BPF_COEFF2223, 0xf7f7fb7f}, -{5100000, DIF_BPF_COEFF2425, 0x01bc084e}, -{5100000, DIF_BPF_COEFF2627, 0x0c430b72}, -{5100000, DIF_BPF_COEFF2829, 0x059afcba}, -{5100000, DIF_BPF_COEFF3031, 0xf467f046}, -{5100000, DIF_BPF_COEFF3233, 0xf26cfa4a}, -{5100000, DIF_BPF_COEFF3435, 0x04cd0da0}, -{5100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 51_quant.dat*/ - - -/*case 5200000:*/ -/* BEGIN - DIF BPF register values from 52_quant.dat*/ -{5200000, DIF_BPF_COEFF01, 0x0000fffe}, -{5200000, DIF_BPF_COEFF23, 0xfff8ffef}, -{5200000, DIF_BPF_COEFF45, 0xfff00009}, -{5200000, DIF_BPF_COEFF67, 0x003f007f}, -{5200000, DIF_BPF_COEFF89, 0x00980056}, -{5200000, DIF_BPF_COEFF1011, 0xffa5feb6}, -{5200000, DIF_BPF_COEFF1213, 0xfe00fe15}, -{5200000, DIF_BPF_COEFF1415, 0xff4b0170}, -{5200000, DIF_BPF_COEFF1617, 0x03b004d7}, -{5200000, DIF_BPF_COEFF1819, 0x03e800b9}, -{5200000, DIF_BPF_COEFF2021, 0xfc48f87f}, -{5200000, DIF_BPF_COEFF2223, 0xf768fa23}, -{5200000, DIF_BPF_COEFF2425, 0x0022071f}, -{5200000, DIF_BPF_COEFF2627, 0x0bf90c1b}, -{5200000, DIF_BPF_COEFF2829, 0x06dafdfd}, -{5200000, DIF_BPF_COEFF3031, 0xf52df05e}, -{5200000, DIF_BPF_COEFF3233, 0xf1fef9b5}, -{5200000, DIF_BPF_COEFF3435, 0x04640d7f}, -{5200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 52_quant.dat*/ - - -/*case 5300000:*/ -/* BEGIN - DIF BPF register values from 53_quant.dat*/ -{5300000, DIF_BPF_COEFF01, 0x0000ffff}, -{5300000, DIF_BPF_COEFF23, 0xfff9ffee}, -{5300000, DIF_BPF_COEFF45, 0xffe6fff3}, -{5300000, DIF_BPF_COEFF67, 0x00250072}, -{5300000, DIF_BPF_COEFF89, 0x00af009c}, -{5300000, DIF_BPF_COEFF1011, 0x000cff10}, -{5300000, DIF_BPF_COEFF1213, 0xfe13fdb8}, -{5300000, DIF_BPF_COEFF1415, 0xfe870089}, -{5300000, DIF_BPF_COEFF1617, 0x031104e1}, -{5300000, DIF_BPF_COEFF1819, 0x04b8020f}, -{5300000, DIF_BPF_COEFF2021, 0xfd98f92f}, -{5300000, DIF_BPF_COEFF2223, 0xf71df8f0}, -{5300000, DIF_BPF_COEFF2425, 0xfe8805ce}, -{5300000, DIF_BPF_COEFF2627, 0x0b7e0c9c}, -{5300000, DIF_BPF_COEFF2829, 0x0808ff44}, -{5300000, DIF_BPF_COEFF3031, 0xf603f086}, -{5300000, DIF_BPF_COEFF3233, 0xf19af922}, -{5300000, DIF_BPF_COEFF3435, 0x03fb0d5e}, -{5300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 53_quant.dat*/ - - -/*case 5400000:*/ -/* BEGIN - DIF BPF register values from 54_quant.dat*/ -{5400000, DIF_BPF_COEFF01, 0x00000001}, -{5400000, DIF_BPF_COEFF23, 0xfffcffef}, -{5400000, DIF_BPF_COEFF45, 0xffe0ffe0}, -{5400000, DIF_BPF_COEFF67, 0x00050056}, -{5400000, DIF_BPF_COEFF89, 0x00b000d1}, -{5400000, DIF_BPF_COEFF1011, 0x0071ff82}, -{5400000, DIF_BPF_COEFF1213, 0xfe53fd8c}, -{5400000, DIF_BPF_COEFF1415, 0xfddfff99}, -{5400000, DIF_BPF_COEFF1617, 0x024104a3}, -{5400000, DIF_BPF_COEFF1819, 0x054a034d}, -{5400000, DIF_BPF_COEFF2021, 0xff01fa1e}, -{5400000, DIF_BPF_COEFF2223, 0xf717f7ed}, -{5400000, DIF_BPF_COEFF2425, 0xfcf50461}, -{5400000, DIF_BPF_COEFF2627, 0x0ad50cf4}, -{5400000, DIF_BPF_COEFF2829, 0x0921008d}, -{5400000, DIF_BPF_COEFF3031, 0xf6e7f0bd}, -{5400000, DIF_BPF_COEFF3233, 0xf13ff891}, -{5400000, DIF_BPF_COEFF3435, 0x03920d3b}, -{5400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 54_quant.dat*/ - - -/*case 5500000:*/ -/* BEGIN - DIF BPF register values from 55_quant.dat*/ -{5500000, DIF_BPF_COEFF01, 0x00010002}, -{5500000, DIF_BPF_COEFF23, 0xfffffff3}, -{5500000, DIF_BPF_COEFF45, 0xffdeffd1}, -{5500000, DIF_BPF_COEFF67, 0xffe5002f}, -{5500000, DIF_BPF_COEFF89, 0x009c00ed}, -{5500000, DIF_BPF_COEFF1011, 0x00cb0000}, -{5500000, DIF_BPF_COEFF1213, 0xfebafd94}, -{5500000, DIF_BPF_COEFF1415, 0xfd61feb0}, -{5500000, DIF_BPF_COEFF1617, 0x014d0422}, -{5500000, DIF_BPF_COEFF1819, 0x05970464}, -{5500000, DIF_BPF_COEFF2021, 0x0074fb41}, -{5500000, DIF_BPF_COEFF2223, 0xf759f721}, -{5500000, DIF_BPF_COEFF2425, 0xfb7502de}, -{5500000, DIF_BPF_COEFF2627, 0x0a000d21}, -{5500000, DIF_BPF_COEFF2829, 0x0a2201d4}, -{5500000, DIF_BPF_COEFF3031, 0xf7d9f104}, -{5500000, DIF_BPF_COEFF3233, 0xf0edf804}, -{5500000, DIF_BPF_COEFF3435, 0x03280d19}, -{5500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 55_quant.dat*/ - - -/*case 5600000:*/ -/* BEGIN - DIF BPF register values from 56_quant.dat*/ -{5600000, DIF_BPF_COEFF01, 0x00010003}, -{5600000, DIF_BPF_COEFF23, 0x0003fffa}, -{5600000, DIF_BPF_COEFF45, 0xffe3ffc9}, -{5600000, DIF_BPF_COEFF67, 0xffc90002}, -{5600000, DIF_BPF_COEFF89, 0x007500ef}, -{5600000, DIF_BPF_COEFF1011, 0x010e007e}, -{5600000, DIF_BPF_COEFF1213, 0xff3dfdcf}, -{5600000, DIF_BPF_COEFF1415, 0xfd16fddd}, -{5600000, DIF_BPF_COEFF1617, 0x00440365}, -{5600000, DIF_BPF_COEFF1819, 0x059b0548}, -{5600000, DIF_BPF_COEFF2021, 0x01e3fc90}, -{5600000, DIF_BPF_COEFF2223, 0xf7dff691}, -{5600000, DIF_BPF_COEFF2425, 0xfa0f014d}, -{5600000, DIF_BPF_COEFF2627, 0x09020d23}, -{5600000, DIF_BPF_COEFF2829, 0x0b0a0318}, -{5600000, DIF_BPF_COEFF3031, 0xf8d7f15a}, -{5600000, DIF_BPF_COEFF3233, 0xf0a5f779}, -{5600000, DIF_BPF_COEFF3435, 0x02bd0cf6}, -{5600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 56_quant.dat*/ - - -/*case 5700000:*/ -/* BEGIN - DIF BPF register values from 57_quant.dat*/ -{5700000, DIF_BPF_COEFF01, 0x00010003}, -{5700000, DIF_BPF_COEFF23, 0x00060001}, -{5700000, DIF_BPF_COEFF45, 0xffecffc9}, -{5700000, DIF_BPF_COEFF67, 0xffb4ffd4}, -{5700000, DIF_BPF_COEFF89, 0x004000d5}, -{5700000, DIF_BPF_COEFF1011, 0x013600f0}, -{5700000, DIF_BPF_COEFF1213, 0xffd3fe39}, -{5700000, DIF_BPF_COEFF1415, 0xfd04fd31}, -{5700000, DIF_BPF_COEFF1617, 0xff360277}, -{5700000, DIF_BPF_COEFF1819, 0x055605ef}, -{5700000, DIF_BPF_COEFF2021, 0x033efdfe}, -{5700000, DIF_BPF_COEFF2223, 0xf8a5f642}, -{5700000, DIF_BPF_COEFF2425, 0xf8cbffb6}, -{5700000, DIF_BPF_COEFF2627, 0x07e10cfb}, -{5700000, DIF_BPF_COEFF2829, 0x0bd50456}, -{5700000, DIF_BPF_COEFF3031, 0xf9dff1be}, -{5700000, DIF_BPF_COEFF3233, 0xf067f6f2}, -{5700000, DIF_BPF_COEFF3435, 0x02520cd2}, -{5700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 57_quant.dat*/ - - -/*case 5800000:*/ -/* BEGIN - DIF BPF register values from 58_quant.dat*/ -{5800000, DIF_BPF_COEFF01, 0x00000003}, -{5800000, DIF_BPF_COEFF23, 0x00080009}, -{5800000, DIF_BPF_COEFF45, 0xfff8ffd2}, -{5800000, DIF_BPF_COEFF67, 0xffaaffac}, -{5800000, DIF_BPF_COEFF89, 0x000200a3}, -{5800000, DIF_BPF_COEFF1011, 0x013c014a}, -{5800000, DIF_BPF_COEFF1213, 0x006dfec9}, -{5800000, DIF_BPF_COEFF1415, 0xfd2bfcb7}, -{5800000, DIF_BPF_COEFF1617, 0xfe350165}, -{5800000, DIF_BPF_COEFF1819, 0x04cb0651}, -{5800000, DIF_BPF_COEFF2021, 0x0477ff7e}, -{5800000, DIF_BPF_COEFF2223, 0xf9a5f635}, -{5800000, DIF_BPF_COEFF2425, 0xf7b1fe20}, -{5800000, DIF_BPF_COEFF2627, 0x069f0ca8}, -{5800000, DIF_BPF_COEFF2829, 0x0c81058b}, -{5800000, DIF_BPF_COEFF3031, 0xfaf0f231}, -{5800000, DIF_BPF_COEFF3233, 0xf033f66d}, -{5800000, DIF_BPF_COEFF3435, 0x01e60cae}, -{5800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 58_quant.dat*/ - - -/*case 5900000:*/ -/* BEGIN - DIF BPF register values from 59_quant.dat*/ -{5900000, DIF_BPF_COEFF01, 0x00000002}, -{5900000, DIF_BPF_COEFF23, 0x0009000e}, -{5900000, DIF_BPF_COEFF45, 0x0005ffe1}, -{5900000, DIF_BPF_COEFF67, 0xffacff90}, -{5900000, DIF_BPF_COEFF89, 0xffc5005f}, -{5900000, DIF_BPF_COEFF1011, 0x01210184}, -{5900000, DIF_BPF_COEFF1213, 0x00fcff72}, -{5900000, DIF_BPF_COEFF1415, 0xfd8afc77}, -{5900000, DIF_BPF_COEFF1617, 0xfd51003f}, -{5900000, DIF_BPF_COEFF1819, 0x04020669}, -{5900000, DIF_BPF_COEFF2021, 0x05830103}, -{5900000, DIF_BPF_COEFF2223, 0xfad7f66b}, -{5900000, DIF_BPF_COEFF2425, 0xf6c8fc93}, -{5900000, DIF_BPF_COEFF2627, 0x05430c2b}, -{5900000, DIF_BPF_COEFF2829, 0x0d0d06b5}, -{5900000, DIF_BPF_COEFF3031, 0xfc08f2b2}, -{5900000, DIF_BPF_COEFF3233, 0xf00af5ec}, -{5900000, DIF_BPF_COEFF3435, 0x017b0c89}, -{5900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 59_quant.dat*/ - - -/*case 6000000:*/ -/* BEGIN - DIF BPF register values from 60_quant.dat*/ -{6000000, DIF_BPF_COEFF01, 0x00000001}, -{6000000, DIF_BPF_COEFF23, 0x00070012}, -{6000000, DIF_BPF_COEFF45, 0x0012fff5}, -{6000000, DIF_BPF_COEFF67, 0xffbaff82}, -{6000000, DIF_BPF_COEFF89, 0xff8e000f}, -{6000000, DIF_BPF_COEFF1011, 0x00e80198}, -{6000000, DIF_BPF_COEFF1213, 0x01750028}, -{6000000, DIF_BPF_COEFF1415, 0xfe18fc75}, -{6000000, DIF_BPF_COEFF1617, 0xfc99ff15}, -{6000000, DIF_BPF_COEFF1819, 0x03050636}, -{6000000, DIF_BPF_COEFF2021, 0x0656027f}, -{6000000, DIF_BPF_COEFF2223, 0xfc32f6e2}, -{6000000, DIF_BPF_COEFF2425, 0xf614fb17}, -{6000000, DIF_BPF_COEFF2627, 0x03d20b87}, -{6000000, DIF_BPF_COEFF2829, 0x0d7707d2}, -{6000000, DIF_BPF_COEFF3031, 0xfd26f341}, -{6000000, DIF_BPF_COEFF3233, 0xefeaf56f}, -{6000000, DIF_BPF_COEFF3435, 0x010f0c64}, -{6000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 60_quant.dat*/ - - -/*case 6100000:*/ -/* BEGIN - DIF BPF register values from 61_quant.dat*/ -{6100000, DIF_BPF_COEFF01, 0xffff0000}, -{6100000, DIF_BPF_COEFF23, 0x00050012}, -{6100000, DIF_BPF_COEFF45, 0x001c000b}, -{6100000, DIF_BPF_COEFF67, 0xffd1ff84}, -{6100000, DIF_BPF_COEFF89, 0xff66ffbe}, -{6100000, DIF_BPF_COEFF1011, 0x00960184}, -{6100000, DIF_BPF_COEFF1213, 0x01cd00da}, -{6100000, DIF_BPF_COEFF1415, 0xfeccfcb2}, -{6100000, DIF_BPF_COEFF1617, 0xfc17fdf9}, -{6100000, DIF_BPF_COEFF1819, 0x01e005bc}, -{6100000, DIF_BPF_COEFF2021, 0x06e703e4}, -{6100000, DIF_BPF_COEFF2223, 0xfdabf798}, -{6100000, DIF_BPF_COEFF2425, 0xf599f9b3}, -{6100000, DIF_BPF_COEFF2627, 0x02510abd}, -{6100000, DIF_BPF_COEFF2829, 0x0dbf08df}, -{6100000, DIF_BPF_COEFF3031, 0xfe48f3dc}, -{6100000, DIF_BPF_COEFF3233, 0xefd5f4f6}, -{6100000, DIF_BPF_COEFF3435, 0x00a20c3e}, -{6100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 61_quant.dat*/ - - -/*case 6200000:*/ -/* BEGIN - DIF BPF register values from 62_quant.dat*/ -{6200000, DIF_BPF_COEFF01, 0xfffffffe}, -{6200000, DIF_BPF_COEFF23, 0x0002000f}, -{6200000, DIF_BPF_COEFF45, 0x0021001f}, -{6200000, DIF_BPF_COEFF67, 0xfff0ff97}, -{6200000, DIF_BPF_COEFF89, 0xff50ff74}, -{6200000, DIF_BPF_COEFF1011, 0x0034014a}, -{6200000, DIF_BPF_COEFF1213, 0x01fa0179}, -{6200000, DIF_BPF_COEFF1415, 0xff97fd2a}, -{6200000, DIF_BPF_COEFF1617, 0xfbd3fcfa}, -{6200000, DIF_BPF_COEFF1819, 0x00a304fe}, -{6200000, DIF_BPF_COEFF2021, 0x07310525}, -{6200000, DIF_BPF_COEFF2223, 0xff37f886}, -{6200000, DIF_BPF_COEFF2425, 0xf55cf86e}, -{6200000, DIF_BPF_COEFF2627, 0x00c709d0}, -{6200000, DIF_BPF_COEFF2829, 0x0de209db}, -{6200000, DIF_BPF_COEFF3031, 0xff6df484}, -{6200000, DIF_BPF_COEFF3233, 0xefcbf481}, -{6200000, DIF_BPF_COEFF3435, 0x00360c18}, -{6200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 62_quant.dat*/ - - -/*case 6300000:*/ -/* BEGIN - DIF BPF register values from 63_quant.dat*/ -{6300000, DIF_BPF_COEFF01, 0xfffffffd}, -{6300000, DIF_BPF_COEFF23, 0xfffe000a}, -{6300000, DIF_BPF_COEFF45, 0x0021002f}, -{6300000, DIF_BPF_COEFF67, 0x0010ffb8}, -{6300000, DIF_BPF_COEFF89, 0xff50ff3b}, -{6300000, DIF_BPF_COEFF1011, 0xffcc00f0}, -{6300000, DIF_BPF_COEFF1213, 0x01fa01fa}, -{6300000, DIF_BPF_COEFF1415, 0x0069fdd4}, -{6300000, DIF_BPF_COEFF1617, 0xfbd3fc26}, -{6300000, DIF_BPF_COEFF1819, 0xff5d0407}, -{6300000, DIF_BPF_COEFF2021, 0x07310638}, -{6300000, DIF_BPF_COEFF2223, 0x00c9f9a8}, -{6300000, DIF_BPF_COEFF2425, 0xf55cf74e}, -{6300000, DIF_BPF_COEFF2627, 0xff3908c3}, -{6300000, DIF_BPF_COEFF2829, 0x0de20ac3}, -{6300000, DIF_BPF_COEFF3031, 0x0093f537}, -{6300000, DIF_BPF_COEFF3233, 0xefcbf410}, -{6300000, DIF_BPF_COEFF3435, 0xffca0bf2}, -{6300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 63_quant.dat*/ - - -/*case 6400000:*/ -/* BEGIN - DIF BPF register values from 64_quant.dat*/ -{6400000, DIF_BPF_COEFF01, 0xfffffffd}, -{6400000, DIF_BPF_COEFF23, 0xfffb0003}, -{6400000, DIF_BPF_COEFF45, 0x001c0037}, -{6400000, DIF_BPF_COEFF67, 0x002fffe2}, -{6400000, DIF_BPF_COEFF89, 0xff66ff17}, -{6400000, DIF_BPF_COEFF1011, 0xff6a007e}, -{6400000, DIF_BPF_COEFF1213, 0x01cd0251}, -{6400000, DIF_BPF_COEFF1415, 0x0134fea5}, -{6400000, DIF_BPF_COEFF1617, 0xfc17fb8b}, -{6400000, DIF_BPF_COEFF1819, 0xfe2002e0}, -{6400000, DIF_BPF_COEFF2021, 0x06e70713}, -{6400000, DIF_BPF_COEFF2223, 0x0255faf5}, -{6400000, DIF_BPF_COEFF2425, 0xf599f658}, -{6400000, DIF_BPF_COEFF2627, 0xfdaf0799}, -{6400000, DIF_BPF_COEFF2829, 0x0dbf0b96}, -{6400000, DIF_BPF_COEFF3031, 0x01b8f5f5}, -{6400000, DIF_BPF_COEFF3233, 0xefd5f3a3}, -{6400000, DIF_BPF_COEFF3435, 0xff5e0bca}, -{6400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 64_quant.dat*/ - - -/*case 6500000:*/ -/* BEGIN - DIF BPF register values from 65_quant.dat*/ -{6500000, DIF_BPF_COEFF01, 0x0000fffd}, -{6500000, DIF_BPF_COEFF23, 0xfff9fffb}, -{6500000, DIF_BPF_COEFF45, 0x00120037}, -{6500000, DIF_BPF_COEFF67, 0x00460010}, -{6500000, DIF_BPF_COEFF89, 0xff8eff0f}, -{6500000, DIF_BPF_COEFF1011, 0xff180000}, -{6500000, DIF_BPF_COEFF1213, 0x01750276}, -{6500000, DIF_BPF_COEFF1415, 0x01e8ff8d}, -{6500000, DIF_BPF_COEFF1617, 0xfc99fb31}, -{6500000, DIF_BPF_COEFF1819, 0xfcfb0198}, -{6500000, DIF_BPF_COEFF2021, 0x065607ad}, -{6500000, DIF_BPF_COEFF2223, 0x03cefc64}, -{6500000, DIF_BPF_COEFF2425, 0xf614f592}, -{6500000, DIF_BPF_COEFF2627, 0xfc2e0656}, -{6500000, DIF_BPF_COEFF2829, 0x0d770c52}, -{6500000, DIF_BPF_COEFF3031, 0x02daf6bd}, -{6500000, DIF_BPF_COEFF3233, 0xefeaf33b}, -{6500000, DIF_BPF_COEFF3435, 0xfef10ba3}, -{6500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 65_quant.dat*/ - - -/*case 6600000:*/ -/* BEGIN - DIF BPF register values from 66_quant.dat*/ -{6600000, DIF_BPF_COEFF01, 0x0000fffe}, -{6600000, DIF_BPF_COEFF23, 0xfff7fff5}, -{6600000, DIF_BPF_COEFF45, 0x0005002f}, -{6600000, DIF_BPF_COEFF67, 0x0054003c}, -{6600000, DIF_BPF_COEFF89, 0xffc5ff22}, -{6600000, DIF_BPF_COEFF1011, 0xfedfff82}, -{6600000, DIF_BPF_COEFF1213, 0x00fc0267}, -{6600000, DIF_BPF_COEFF1415, 0x0276007e}, -{6600000, DIF_BPF_COEFF1617, 0xfd51fb1c}, -{6600000, DIF_BPF_COEFF1819, 0xfbfe003e}, -{6600000, DIF_BPF_COEFF2021, 0x05830802}, -{6600000, DIF_BPF_COEFF2223, 0x0529fdec}, -{6600000, DIF_BPF_COEFF2425, 0xf6c8f4fe}, -{6600000, DIF_BPF_COEFF2627, 0xfabd04ff}, -{6600000, DIF_BPF_COEFF2829, 0x0d0d0cf6}, -{6600000, DIF_BPF_COEFF3031, 0x03f8f78f}, -{6600000, DIF_BPF_COEFF3233, 0xf00af2d7}, -{6600000, DIF_BPF_COEFF3435, 0xfe850b7b}, -{6600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 66_quant.dat*/ - - -/*case 6700000:*/ -/* BEGIN - DIF BPF register values from 67_quant.dat*/ -{6700000, DIF_BPF_COEFF01, 0x0000ffff}, -{6700000, DIF_BPF_COEFF23, 0xfff8fff0}, -{6700000, DIF_BPF_COEFF45, 0xfff80020}, -{6700000, DIF_BPF_COEFF67, 0x00560060}, -{6700000, DIF_BPF_COEFF89, 0x0002ff4e}, -{6700000, DIF_BPF_COEFF1011, 0xfec4ff10}, -{6700000, DIF_BPF_COEFF1213, 0x006d0225}, -{6700000, DIF_BPF_COEFF1415, 0x02d50166}, -{6700000, DIF_BPF_COEFF1617, 0xfe35fb4e}, -{6700000, DIF_BPF_COEFF1819, 0xfb35fee1}, -{6700000, DIF_BPF_COEFF2021, 0x0477080e}, -{6700000, DIF_BPF_COEFF2223, 0x065bff82}, -{6700000, DIF_BPF_COEFF2425, 0xf7b1f4a0}, -{6700000, DIF_BPF_COEFF2627, 0xf9610397}, -{6700000, DIF_BPF_COEFF2829, 0x0c810d80}, -{6700000, DIF_BPF_COEFF3031, 0x0510f869}, -{6700000, DIF_BPF_COEFF3233, 0xf033f278}, -{6700000, DIF_BPF_COEFF3435, 0xfe1a0b52}, -{6700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 67_quant.dat*/ - - -/*case 6800000:*/ -/* BEGIN - DIF BPF register values from 68_quant.dat*/ -{6800000, DIF_BPF_COEFF01, 0x00010000}, -{6800000, DIF_BPF_COEFF23, 0xfffaffee}, -{6800000, DIF_BPF_COEFF45, 0xffec000c}, -{6800000, DIF_BPF_COEFF67, 0x004c0078}, -{6800000, DIF_BPF_COEFF89, 0x0040ff8e}, -{6800000, DIF_BPF_COEFF1011, 0xfecafeb6}, -{6800000, DIF_BPF_COEFF1213, 0xffd301b6}, -{6800000, DIF_BPF_COEFF1415, 0x02fc0235}, -{6800000, DIF_BPF_COEFF1617, 0xff36fbc5}, -{6800000, DIF_BPF_COEFF1819, 0xfaaafd90}, -{6800000, DIF_BPF_COEFF2021, 0x033e07d2}, -{6800000, DIF_BPF_COEFF2223, 0x075b011b}, -{6800000, DIF_BPF_COEFF2425, 0xf8cbf47a}, -{6800000, DIF_BPF_COEFF2627, 0xf81f0224}, -{6800000, DIF_BPF_COEFF2829, 0x0bd50def}, -{6800000, DIF_BPF_COEFF3031, 0x0621f94b}, -{6800000, DIF_BPF_COEFF3233, 0xf067f21e}, -{6800000, DIF_BPF_COEFF3435, 0xfdae0b29}, -{6800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 68_quant.dat*/ - - -/*case 6900000:*/ -/* BEGIN - DIF BPF register values from 69_quant.dat*/ -{6900000, DIF_BPF_COEFF01, 0x00010001}, -{6900000, DIF_BPF_COEFF23, 0xfffdffef}, -{6900000, DIF_BPF_COEFF45, 0xffe3fff6}, -{6900000, DIF_BPF_COEFF67, 0x0037007f}, -{6900000, DIF_BPF_COEFF89, 0x0075ffdc}, -{6900000, DIF_BPF_COEFF1011, 0xfef2fe7c}, -{6900000, DIF_BPF_COEFF1213, 0xff3d0122}, -{6900000, DIF_BPF_COEFF1415, 0x02ea02dd}, -{6900000, DIF_BPF_COEFF1617, 0x0044fc79}, -{6900000, DIF_BPF_COEFF1819, 0xfa65fc5d}, -{6900000, DIF_BPF_COEFF2021, 0x01e3074e}, -{6900000, DIF_BPF_COEFF2223, 0x082102ad}, -{6900000, DIF_BPF_COEFF2425, 0xfa0ff48c}, -{6900000, DIF_BPF_COEFF2627, 0xf6fe00a9}, -{6900000, DIF_BPF_COEFF2829, 0x0b0a0e43}, -{6900000, DIF_BPF_COEFF3031, 0x0729fa33}, -{6900000, DIF_BPF_COEFF3233, 0xf0a5f1c9}, -{6900000, DIF_BPF_COEFF3435, 0xfd430b00}, -{6900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 69_quant.dat*/ - - -/*case 7000000:*/ -/* BEGIN - DIF BPF register values from 70_quant.dat*/ -{7000000, DIF_BPF_COEFF01, 0x00010002}, -{7000000, DIF_BPF_COEFF23, 0x0001fff3}, -{7000000, DIF_BPF_COEFF45, 0xffdeffe2}, -{7000000, DIF_BPF_COEFF67, 0x001b0076}, -{7000000, DIF_BPF_COEFF89, 0x009c002d}, -{7000000, DIF_BPF_COEFF1011, 0xff35fe68}, -{7000000, DIF_BPF_COEFF1213, 0xfeba0076}, -{7000000, DIF_BPF_COEFF1415, 0x029f0352}, -{7000000, DIF_BPF_COEFF1617, 0x014dfd60}, -{7000000, DIF_BPF_COEFF1819, 0xfa69fb53}, -{7000000, DIF_BPF_COEFF2021, 0x00740688}, -{7000000, DIF_BPF_COEFF2223, 0x08a7042d}, -{7000000, DIF_BPF_COEFF2425, 0xfb75f4d6}, -{7000000, DIF_BPF_COEFF2627, 0xf600ff2d}, -{7000000, DIF_BPF_COEFF2829, 0x0a220e7a}, -{7000000, DIF_BPF_COEFF3031, 0x0827fb22}, -{7000000, DIF_BPF_COEFF3233, 0xf0edf17a}, -{7000000, DIF_BPF_COEFF3435, 0xfcd80ad6}, -{7000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 70_quant.dat*/ - - -/*case 7100000:*/ -/* BEGIN - DIF BPF register values from 71_quant.dat*/ -{7100000, DIF_BPF_COEFF01, 0x00000003}, -{7100000, DIF_BPF_COEFF23, 0x0004fff9}, -{7100000, DIF_BPF_COEFF45, 0xffe0ffd2}, -{7100000, DIF_BPF_COEFF67, 0xfffb005e}, -{7100000, DIF_BPF_COEFF89, 0x00b0007a}, -{7100000, DIF_BPF_COEFF1011, 0xff8ffe7c}, -{7100000, DIF_BPF_COEFF1213, 0xfe53ffc1}, -{7100000, DIF_BPF_COEFF1415, 0x0221038c}, -{7100000, DIF_BPF_COEFF1617, 0x0241fe6e}, -{7100000, DIF_BPF_COEFF1819, 0xfab6fa80}, -{7100000, DIF_BPF_COEFF2021, 0xff010587}, -{7100000, DIF_BPF_COEFF2223, 0x08e90590}, -{7100000, DIF_BPF_COEFF2425, 0xfcf5f556}, -{7100000, DIF_BPF_COEFF2627, 0xf52bfdb3}, -{7100000, DIF_BPF_COEFF2829, 0x09210e95}, -{7100000, DIF_BPF_COEFF3031, 0x0919fc15}, -{7100000, DIF_BPF_COEFF3233, 0xf13ff12f}, -{7100000, DIF_BPF_COEFF3435, 0xfc6e0aab}, -{7100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 71_quant.dat*/ - - -/*case 7200000:*/ -/* BEGIN - DIF BPF register values from 72_quant.dat*/ -{7200000, DIF_BPF_COEFF01, 0x00000003}, -{7200000, DIF_BPF_COEFF23, 0x00070000}, -{7200000, DIF_BPF_COEFF45, 0xffe6ffc9}, -{7200000, DIF_BPF_COEFF67, 0xffdb0039}, -{7200000, DIF_BPF_COEFF89, 0x00af00b8}, -{7200000, DIF_BPF_COEFF1011, 0xfff4feb6}, -{7200000, DIF_BPF_COEFF1213, 0xfe13ff10}, -{7200000, DIF_BPF_COEFF1415, 0x01790388}, -{7200000, DIF_BPF_COEFF1617, 0x0311ff92}, -{7200000, DIF_BPF_COEFF1819, 0xfb48f9ed}, -{7200000, DIF_BPF_COEFF2021, 0xfd980453}, -{7200000, DIF_BPF_COEFF2223, 0x08e306cd}, -{7200000, DIF_BPF_COEFF2425, 0xfe88f60a}, -{7200000, DIF_BPF_COEFF2627, 0xf482fc40}, -{7200000, DIF_BPF_COEFF2829, 0x08080e93}, -{7200000, DIF_BPF_COEFF3031, 0x09fdfd0c}, -{7200000, DIF_BPF_COEFF3233, 0xf19af0ea}, -{7200000, DIF_BPF_COEFF3435, 0xfc050a81}, -{7200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 72_quant.dat*/ - - -/*case 7300000:*/ -/* BEGIN - DIF BPF register values from 73_quant.dat*/ -{7300000, DIF_BPF_COEFF01, 0x00000002}, -{7300000, DIF_BPF_COEFF23, 0x00080008}, -{7300000, DIF_BPF_COEFF45, 0xfff0ffc9}, -{7300000, DIF_BPF_COEFF67, 0xffc1000d}, -{7300000, DIF_BPF_COEFF89, 0x009800e2}, -{7300000, DIF_BPF_COEFF1011, 0x005bff10}, -{7300000, DIF_BPF_COEFF1213, 0xfe00fe74}, -{7300000, DIF_BPF_COEFF1415, 0x00b50345}, -{7300000, DIF_BPF_COEFF1617, 0x03b000bc}, -{7300000, DIF_BPF_COEFF1819, 0xfc18f9a1}, -{7300000, DIF_BPF_COEFF2021, 0xfc4802f9}, -{7300000, DIF_BPF_COEFF2223, 0x089807dc}, -{7300000, DIF_BPF_COEFF2425, 0x0022f6f0}, -{7300000, DIF_BPF_COEFF2627, 0xf407fada}, -{7300000, DIF_BPF_COEFF2829, 0x06da0e74}, -{7300000, DIF_BPF_COEFF3031, 0x0ad3fe06}, -{7300000, DIF_BPF_COEFF3233, 0xf1fef0ab}, -{7300000, DIF_BPF_COEFF3435, 0xfb9c0a55}, -{7300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 73_quant.dat*/ - - -/*case 7400000:*/ -/* BEGIN - DIF BPF register values from 74_quant.dat*/ -{7400000, DIF_BPF_COEFF01, 0x00000001}, -{7400000, DIF_BPF_COEFF23, 0x0008000e}, -{7400000, DIF_BPF_COEFF45, 0xfffdffd0}, -{7400000, DIF_BPF_COEFF67, 0xffafffdf}, -{7400000, DIF_BPF_COEFF89, 0x006e00f2}, -{7400000, DIF_BPF_COEFF1011, 0x00b8ff82}, -{7400000, DIF_BPF_COEFF1213, 0xfe1bfdf8}, -{7400000, DIF_BPF_COEFF1415, 0xffe302c8}, -{7400000, DIF_BPF_COEFF1617, 0x041301dc}, -{7400000, DIF_BPF_COEFF1819, 0xfd1af99e}, -{7400000, DIF_BPF_COEFF2021, 0xfb1e0183}, -{7400000, DIF_BPF_COEFF2223, 0x080908b5}, -{7400000, DIF_BPF_COEFF2425, 0x01bcf801}, -{7400000, DIF_BPF_COEFF2627, 0xf3bdf985}, -{7400000, DIF_BPF_COEFF2829, 0x059a0e38}, -{7400000, DIF_BPF_COEFF3031, 0x0b99ff03}, -{7400000, DIF_BPF_COEFF3233, 0xf26cf071}, -{7400000, DIF_BPF_COEFF3435, 0xfb330a2a}, -{7400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 74_quant.dat*/ - - -/*case 7500000:*/ -/* BEGIN - DIF BPF register values from 75_quant.dat*/ -{7500000, DIF_BPF_COEFF01, 0xffff0000}, -{7500000, DIF_BPF_COEFF23, 0x00070011}, -{7500000, DIF_BPF_COEFF45, 0x000affdf}, -{7500000, DIF_BPF_COEFF67, 0xffa9ffb5}, -{7500000, DIF_BPF_COEFF89, 0x003700e6}, -{7500000, DIF_BPF_COEFF1011, 0x01010000}, -{7500000, DIF_BPF_COEFF1213, 0xfe62fda8}, -{7500000, DIF_BPF_COEFF1415, 0xff140219}, -{7500000, DIF_BPF_COEFF1617, 0x043502e1}, -{7500000, DIF_BPF_COEFF1819, 0xfe42f9e6}, -{7500000, DIF_BPF_COEFF2021, 0xfa270000}, -{7500000, DIF_BPF_COEFF2223, 0x073a0953}, -{7500000, DIF_BPF_COEFF2425, 0x034cf939}, -{7500000, DIF_BPF_COEFF2627, 0xf3a4f845}, -{7500000, DIF_BPF_COEFF2829, 0x044c0de1}, -{7500000, DIF_BPF_COEFF3031, 0x0c4f0000}, -{7500000, DIF_BPF_COEFF3233, 0xf2e2f03c}, -{7500000, DIF_BPF_COEFF3435, 0xfacc09fe}, -{7500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 75_quant.dat*/ - - -/*case 7600000:*/ -/* BEGIN - DIF BPF register values from 76_quant.dat*/ -{7600000, DIF_BPF_COEFF01, 0xffffffff}, -{7600000, DIF_BPF_COEFF23, 0x00040012}, -{7600000, DIF_BPF_COEFF45, 0x0016fff3}, -{7600000, DIF_BPF_COEFF67, 0xffafff95}, -{7600000, DIF_BPF_COEFF89, 0xfff900c0}, -{7600000, DIF_BPF_COEFF1011, 0x0130007e}, -{7600000, DIF_BPF_COEFF1213, 0xfecefd89}, -{7600000, DIF_BPF_COEFF1415, 0xfe560146}, -{7600000, DIF_BPF_COEFF1617, 0x041303bc}, -{7600000, DIF_BPF_COEFF1819, 0xff81fa76}, -{7600000, DIF_BPF_COEFF2021, 0xf96cfe7d}, -{7600000, DIF_BPF_COEFF2223, 0x063209b1}, -{7600000, DIF_BPF_COEFF2425, 0x04c9fa93}, -{7600000, DIF_BPF_COEFF2627, 0xf3bdf71e}, -{7600000, DIF_BPF_COEFF2829, 0x02f30d6e}, -{7600000, DIF_BPF_COEFF3031, 0x0cf200fd}, -{7600000, DIF_BPF_COEFF3233, 0xf361f00e}, -{7600000, DIF_BPF_COEFF3435, 0xfa6509d1}, -{7600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 76_quant.dat*/ - - -/*case 7700000:*/ -/* BEGIN - DIF BPF register values from 77_quant.dat*/ -{7700000, DIF_BPF_COEFF01, 0xfffffffe}, -{7700000, DIF_BPF_COEFF23, 0x00010010}, -{7700000, DIF_BPF_COEFF45, 0x001e0008}, -{7700000, DIF_BPF_COEFF67, 0xffc1ff84}, -{7700000, DIF_BPF_COEFF89, 0xffbc0084}, -{7700000, DIF_BPF_COEFF1011, 0x013e00f0}, -{7700000, DIF_BPF_COEFF1213, 0xff56fd9f}, -{7700000, DIF_BPF_COEFF1415, 0xfdb8005c}, -{7700000, DIF_BPF_COEFF1617, 0x03b00460}, -{7700000, DIF_BPF_COEFF1819, 0x00c7fb45}, -{7700000, DIF_BPF_COEFF2021, 0xf8f4fd07}, -{7700000, DIF_BPF_COEFF2223, 0x04fa09ce}, -{7700000, DIF_BPF_COEFF2425, 0x062afc07}, -{7700000, DIF_BPF_COEFF2627, 0xf407f614}, -{7700000, DIF_BPF_COEFF2829, 0x01920ce0}, -{7700000, DIF_BPF_COEFF3031, 0x0d8301fa}, -{7700000, DIF_BPF_COEFF3233, 0xf3e8efe5}, -{7700000, DIF_BPF_COEFF3435, 0xfa0009a4}, -{7700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 77_quant.dat*/ - - -/*case 7800000:*/ -/* BEGIN - DIF BPF register values from 78_quant.dat*/ -{7800000, DIF_BPF_COEFF01, 0x0000fffd}, -{7800000, DIF_BPF_COEFF23, 0xfffd000b}, -{7800000, DIF_BPF_COEFF45, 0x0022001d}, -{7800000, DIF_BPF_COEFF67, 0xffdbff82}, -{7800000, DIF_BPF_COEFF89, 0xff870039}, -{7800000, DIF_BPF_COEFF1011, 0x012a014a}, -{7800000, DIF_BPF_COEFF1213, 0xffedfde7}, -{7800000, DIF_BPF_COEFF1415, 0xfd47ff6b}, -{7800000, DIF_BPF_COEFF1617, 0x031104c6}, -{7800000, DIF_BPF_COEFF1819, 0x0202fc4c}, -{7800000, DIF_BPF_COEFF2021, 0xf8c6fbad}, -{7800000, DIF_BPF_COEFF2223, 0x039909a7}, -{7800000, DIF_BPF_COEFF2425, 0x0767fd8e}, -{7800000, DIF_BPF_COEFF2627, 0xf482f52b}, -{7800000, DIF_BPF_COEFF2829, 0x002d0c39}, -{7800000, DIF_BPF_COEFF3031, 0x0e0002f4}, -{7800000, DIF_BPF_COEFF3233, 0xf477efc2}, -{7800000, DIF_BPF_COEFF3435, 0xf99b0977}, -{7800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 78_quant.dat*/ - - -/*case 7900000:*/ -/* BEGIN - DIF BPF register values from 79_quant.dat*/ -{7900000, DIF_BPF_COEFF01, 0x0000fffd}, -{7900000, DIF_BPF_COEFF23, 0xfffa0004}, -{7900000, DIF_BPF_COEFF45, 0x0020002d}, -{7900000, DIF_BPF_COEFF67, 0xfffbff91}, -{7900000, DIF_BPF_COEFF89, 0xff61ffe8}, -{7900000, DIF_BPF_COEFF1011, 0x00f70184}, -{7900000, DIF_BPF_COEFF1213, 0x0086fe5c}, -{7900000, DIF_BPF_COEFF1415, 0xfd0bfe85}, -{7900000, DIF_BPF_COEFF1617, 0x024104e5}, -{7900000, DIF_BPF_COEFF1819, 0x0323fd7d}, -{7900000, DIF_BPF_COEFF2021, 0xf8e2fa79}, -{7900000, DIF_BPF_COEFF2223, 0x021d093f}, -{7900000, DIF_BPF_COEFF2425, 0x0879ff22}, -{7900000, DIF_BPF_COEFF2627, 0xf52bf465}, -{7900000, DIF_BPF_COEFF2829, 0xfec70b79}, -{7900000, DIF_BPF_COEFF3031, 0x0e6803eb}, -{7900000, DIF_BPF_COEFF3233, 0xf50defa5}, -{7900000, DIF_BPF_COEFF3435, 0xf937094a}, -{7900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 79_quant.dat*/ - - -/*case 8000000:*/ -/* BEGIN - DIF BPF register values from 80_quant.dat*/ -{8000000, DIF_BPF_COEFF01, 0x0000fffe}, -{8000000, DIF_BPF_COEFF23, 0xfff8fffd}, -{8000000, DIF_BPF_COEFF45, 0x00190036}, -{8000000, DIF_BPF_COEFF67, 0x001bffaf}, -{8000000, DIF_BPF_COEFF89, 0xff4fff99}, -{8000000, DIF_BPF_COEFF1011, 0x00aa0198}, -{8000000, DIF_BPF_COEFF1213, 0x0112fef3}, -{8000000, DIF_BPF_COEFF1415, 0xfd09fdb9}, -{8000000, DIF_BPF_COEFF1617, 0x014d04be}, -{8000000, DIF_BPF_COEFF1819, 0x041bfecc}, -{8000000, DIF_BPF_COEFF2021, 0xf947f978}, -{8000000, DIF_BPF_COEFF2223, 0x00900897}, -{8000000, DIF_BPF_COEFF2425, 0x095a00b9}, -{8000000, DIF_BPF_COEFF2627, 0xf600f3c5}, -{8000000, DIF_BPF_COEFF2829, 0xfd650aa3}, -{8000000, DIF_BPF_COEFF3031, 0x0ebc04de}, -{8000000, DIF_BPF_COEFF3233, 0xf5aaef8e}, -{8000000, DIF_BPF_COEFF3435, 0xf8d5091c}, -{8000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 80_quant.dat*/ - - -/*case 8100000:*/ -/* BEGIN - DIF BPF register values from 81_quant.dat*/ -{8100000, DIF_BPF_COEFF01, 0x0000ffff}, -{8100000, DIF_BPF_COEFF23, 0xfff7fff6}, -{8100000, DIF_BPF_COEFF45, 0x000e0038}, -{8100000, DIF_BPF_COEFF67, 0x0037ffd7}, -{8100000, DIF_BPF_COEFF89, 0xff52ff56}, -{8100000, DIF_BPF_COEFF1011, 0x004b0184}, -{8100000, DIF_BPF_COEFF1213, 0x0186ffa1}, -{8100000, DIF_BPF_COEFF1415, 0xfd40fd16}, -{8100000, DIF_BPF_COEFF1617, 0x00440452}, -{8100000, DIF_BPF_COEFF1819, 0x04de0029}, -{8100000, DIF_BPF_COEFF2021, 0xf9f2f8b2}, -{8100000, DIF_BPF_COEFF2223, 0xfefe07b5}, -{8100000, DIF_BPF_COEFF2425, 0x0a05024d}, -{8100000, DIF_BPF_COEFF2627, 0xf6fef34d}, -{8100000, DIF_BPF_COEFF2829, 0xfc0a09b8}, -{8100000, DIF_BPF_COEFF3031, 0x0efa05cd}, -{8100000, DIF_BPF_COEFF3233, 0xf64eef7d}, -{8100000, DIF_BPF_COEFF3435, 0xf87308ed}, -{8100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 81_quant.dat*/ - - -/*case 8200000:*/ -/* BEGIN - DIF BPF register values from 82_quant.dat*/ -{8200000, DIF_BPF_COEFF01, 0x00010000}, -{8200000, DIF_BPF_COEFF23, 0xfff8fff0}, -{8200000, DIF_BPF_COEFF45, 0x00000031}, -{8200000, DIF_BPF_COEFF67, 0x004c0005}, -{8200000, DIF_BPF_COEFF89, 0xff6aff27}, -{8200000, DIF_BPF_COEFF1011, 0xffe4014a}, -{8200000, DIF_BPF_COEFF1213, 0x01d70057}, -{8200000, DIF_BPF_COEFF1415, 0xfdacfca6}, -{8200000, DIF_BPF_COEFF1617, 0xff3603a7}, -{8200000, DIF_BPF_COEFF1819, 0x05610184}, -{8200000, DIF_BPF_COEFF2021, 0xfadbf82e}, -{8200000, DIF_BPF_COEFF2223, 0xfd74069f}, -{8200000, DIF_BPF_COEFF2425, 0x0a7503d6}, -{8200000, DIF_BPF_COEFF2627, 0xf81ff2ff}, -{8200000, DIF_BPF_COEFF2829, 0xfab808b9}, -{8200000, DIF_BPF_COEFF3031, 0x0f2306b5}, -{8200000, DIF_BPF_COEFF3233, 0xf6f9ef72}, -{8200000, DIF_BPF_COEFF3435, 0xf81308bf}, -{8200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 82_quant.dat*/ - - -/*case 8300000:*/ -/* BEGIN - DIF BPF register values from 83_quant.dat*/ -{8300000, DIF_BPF_COEFF01, 0x00010001}, -{8300000, DIF_BPF_COEFF23, 0xfffbffee}, -{8300000, DIF_BPF_COEFF45, 0xfff30022}, -{8300000, DIF_BPF_COEFF67, 0x00560032}, -{8300000, DIF_BPF_COEFF89, 0xff95ff10}, -{8300000, DIF_BPF_COEFF1011, 0xff8000f0}, -{8300000, DIF_BPF_COEFF1213, 0x01fe0106}, -{8300000, DIF_BPF_COEFF1415, 0xfe46fc71}, -{8300000, DIF_BPF_COEFF1617, 0xfe3502c7}, -{8300000, DIF_BPF_COEFF1819, 0x059e02ce}, -{8300000, DIF_BPF_COEFF2021, 0xfbf9f7f2}, -{8300000, DIF_BPF_COEFF2223, 0xfbff055b}, -{8300000, DIF_BPF_COEFF2425, 0x0aa9054c}, -{8300000, DIF_BPF_COEFF2627, 0xf961f2db}, -{8300000, DIF_BPF_COEFF2829, 0xf97507aa}, -{8300000, DIF_BPF_COEFF3031, 0x0f350797}, -{8300000, DIF_BPF_COEFF3233, 0xf7a9ef6d}, -{8300000, DIF_BPF_COEFF3435, 0xf7b40890}, -{8300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 83_quant.dat*/ - - -/*case 8400000:*/ -/* BEGIN - DIF BPF register values from 84_quant.dat*/ -{8400000, DIF_BPF_COEFF01, 0x00010002}, -{8400000, DIF_BPF_COEFF23, 0xfffeffee}, -{8400000, DIF_BPF_COEFF45, 0xffe8000f}, -{8400000, DIF_BPF_COEFF67, 0x00540058}, -{8400000, DIF_BPF_COEFF89, 0xffcdff14}, -{8400000, DIF_BPF_COEFF1011, 0xff29007e}, -{8400000, DIF_BPF_COEFF1213, 0x01f6019e}, -{8400000, DIF_BPF_COEFF1415, 0xff01fc7c}, -{8400000, DIF_BPF_COEFF1617, 0xfd5101bf}, -{8400000, DIF_BPF_COEFF1819, 0x059203f6}, -{8400000, DIF_BPF_COEFF2021, 0xfd41f7fe}, -{8400000, DIF_BPF_COEFF2223, 0xfaa903f3}, -{8400000, DIF_BPF_COEFF2425, 0x0a9e06a9}, -{8400000, DIF_BPF_COEFF2627, 0xfabdf2e2}, -{8400000, DIF_BPF_COEFF2829, 0xf842068b}, -{8400000, DIF_BPF_COEFF3031, 0x0f320871}, -{8400000, DIF_BPF_COEFF3233, 0xf85eef6e}, -{8400000, DIF_BPF_COEFF3435, 0xf7560860}, -{8400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 84_quant.dat*/ - - -/*case 8500000:*/ -/* BEGIN - DIF BPF register values from 85_quant.dat*/ -{8500000, DIF_BPF_COEFF01, 0x00000003}, -{8500000, DIF_BPF_COEFF23, 0x0002fff2}, -{8500000, DIF_BPF_COEFF45, 0xffe1fff9}, -{8500000, DIF_BPF_COEFF67, 0x00460073}, -{8500000, DIF_BPF_COEFF89, 0x000bff34}, -{8500000, DIF_BPF_COEFF1011, 0xfee90000}, -{8500000, DIF_BPF_COEFF1213, 0x01c10215}, -{8500000, DIF_BPF_COEFF1415, 0xffd0fcc5}, -{8500000, DIF_BPF_COEFF1617, 0xfc99009d}, -{8500000, DIF_BPF_COEFF1819, 0x053d04f1}, -{8500000, DIF_BPF_COEFF2021, 0xfea5f853}, -{8500000, DIF_BPF_COEFF2223, 0xf97d0270}, -{8500000, DIF_BPF_COEFF2425, 0x0a5607e4}, -{8500000, DIF_BPF_COEFF2627, 0xfc2ef314}, -{8500000, DIF_BPF_COEFF2829, 0xf723055f}, -{8500000, DIF_BPF_COEFF3031, 0x0f180943}, -{8500000, DIF_BPF_COEFF3233, 0xf919ef75}, -{8500000, DIF_BPF_COEFF3435, 0xf6fa0830}, -{8500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 85_quant.dat*/ - - -/*case 8600000:*/ -/* BEGIN - DIF BPF register values from 86_quant.dat*/ -{8600000, DIF_BPF_COEFF01, 0x00000003}, -{8600000, DIF_BPF_COEFF23, 0x0005fff8}, -{8600000, DIF_BPF_COEFF45, 0xffdeffe4}, -{8600000, DIF_BPF_COEFF67, 0x002f007f}, -{8600000, DIF_BPF_COEFF89, 0x0048ff6b}, -{8600000, DIF_BPF_COEFF1011, 0xfec7ff82}, -{8600000, DIF_BPF_COEFF1213, 0x0163025f}, -{8600000, DIF_BPF_COEFF1415, 0x00a2fd47}, -{8600000, DIF_BPF_COEFF1617, 0xfc17ff73}, -{8600000, DIF_BPF_COEFF1819, 0x04a405b2}, -{8600000, DIF_BPF_COEFF2021, 0x0017f8ed}, -{8600000, DIF_BPF_COEFF2223, 0xf88500dc}, -{8600000, DIF_BPF_COEFF2425, 0x09d208f9}, -{8600000, DIF_BPF_COEFF2627, 0xfdaff370}, -{8600000, DIF_BPF_COEFF2829, 0xf61c0429}, -{8600000, DIF_BPF_COEFF3031, 0x0ee80a0b}, -{8600000, DIF_BPF_COEFF3233, 0xf9d8ef82}, -{8600000, DIF_BPF_COEFF3435, 0xf6a00800}, -{8600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 86_quant.dat*/ - - -/*case 8700000:*/ -/* BEGIN - DIF BPF register values from 87_quant.dat*/ -{8700000, DIF_BPF_COEFF01, 0x00000003}, -{8700000, DIF_BPF_COEFF23, 0x0007ffff}, -{8700000, DIF_BPF_COEFF45, 0xffe1ffd4}, -{8700000, DIF_BPF_COEFF67, 0x0010007a}, -{8700000, DIF_BPF_COEFF89, 0x007cffb2}, -{8700000, DIF_BPF_COEFF1011, 0xfec6ff10}, -{8700000, DIF_BPF_COEFF1213, 0x00e60277}, -{8700000, DIF_BPF_COEFF1415, 0x0168fdf9}, -{8700000, DIF_BPF_COEFF1617, 0xfbd3fe50}, -{8700000, DIF_BPF_COEFF1819, 0x03ce0631}, -{8700000, DIF_BPF_COEFF2021, 0x0188f9c8}, -{8700000, DIF_BPF_COEFF2223, 0xf7c7ff43}, -{8700000, DIF_BPF_COEFF2425, 0x091509e3}, -{8700000, DIF_BPF_COEFF2627, 0xff39f3f6}, -{8700000, DIF_BPF_COEFF2829, 0xf52d02ea}, -{8700000, DIF_BPF_COEFF3031, 0x0ea30ac9}, -{8700000, DIF_BPF_COEFF3233, 0xfa9bef95}, -{8700000, DIF_BPF_COEFF3435, 0xf64607d0}, -{8700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 87_quant.dat*/ - - -/*case 8800000:*/ -/* BEGIN - DIF BPF register values from 88_quant.dat*/ -{8800000, DIF_BPF_COEFF01, 0x00000002}, -{8800000, DIF_BPF_COEFF23, 0x00090007}, -{8800000, DIF_BPF_COEFF45, 0xffe9ffca}, -{8800000, DIF_BPF_COEFF67, 0xfff00065}, -{8800000, DIF_BPF_COEFF89, 0x00a10003}, -{8800000, DIF_BPF_COEFF1011, 0xfee6feb6}, -{8800000, DIF_BPF_COEFF1213, 0x0053025b}, -{8800000, DIF_BPF_COEFF1415, 0x0213fed0}, -{8800000, DIF_BPF_COEFF1617, 0xfbd3fd46}, -{8800000, DIF_BPF_COEFF1819, 0x02c70668}, -{8800000, DIF_BPF_COEFF2021, 0x02eafadb}, -{8800000, DIF_BPF_COEFF2223, 0xf74bfdae}, -{8800000, DIF_BPF_COEFF2425, 0x08230a9c}, -{8800000, DIF_BPF_COEFF2627, 0x00c7f4a3}, -{8800000, DIF_BPF_COEFF2829, 0xf45b01a6}, -{8800000, DIF_BPF_COEFF3031, 0x0e480b7c}, -{8800000, DIF_BPF_COEFF3233, 0xfb61efae}, -{8800000, DIF_BPF_COEFF3435, 0xf5ef079f}, -{8800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 88_quant.dat*/ - - -/*case 8900000:*/ -/* BEGIN - DIF BPF register values from 89_quant.dat*/ -{8900000, DIF_BPF_COEFF01, 0xffff0000}, -{8900000, DIF_BPF_COEFF23, 0x0008000d}, -{8900000, DIF_BPF_COEFF45, 0xfff5ffc8}, -{8900000, DIF_BPF_COEFF67, 0xffd10043}, -{8900000, DIF_BPF_COEFF89, 0x00b20053}, -{8900000, DIF_BPF_COEFF1011, 0xff24fe7c}, -{8900000, DIF_BPF_COEFF1213, 0xffb9020c}, -{8900000, DIF_BPF_COEFF1415, 0x0295ffbb}, -{8900000, DIF_BPF_COEFF1617, 0xfc17fc64}, -{8900000, DIF_BPF_COEFF1819, 0x019b0654}, -{8900000, DIF_BPF_COEFF2021, 0x042dfc1c}, -{8900000, DIF_BPF_COEFF2223, 0xf714fc2a}, -{8900000, DIF_BPF_COEFF2425, 0x07020b21}, -{8900000, DIF_BPF_COEFF2627, 0x0251f575}, -{8900000, DIF_BPF_COEFF2829, 0xf3a7005e}, -{8900000, DIF_BPF_COEFF3031, 0x0dd80c24}, -{8900000, DIF_BPF_COEFF3233, 0xfc2aefcd}, -{8900000, DIF_BPF_COEFF3435, 0xf599076e}, -{8900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 89_quant.dat*/ - - -/*case 9000000:*/ -/* BEGIN - DIF BPF register values from 90_quant.dat*/ -{9000000, DIF_BPF_COEFF01, 0xffffffff}, -{9000000, DIF_BPF_COEFF23, 0x00060011}, -{9000000, DIF_BPF_COEFF45, 0x0002ffcf}, -{9000000, DIF_BPF_COEFF67, 0xffba0018}, -{9000000, DIF_BPF_COEFF89, 0x00ad009a}, -{9000000, DIF_BPF_COEFF1011, 0xff79fe68}, -{9000000, DIF_BPF_COEFF1213, 0xff260192}, -{9000000, DIF_BPF_COEFF1415, 0x02e500ab}, -{9000000, DIF_BPF_COEFF1617, 0xfc99fbb6}, -{9000000, DIF_BPF_COEFF1819, 0x005b05f7}, -{9000000, DIF_BPF_COEFF2021, 0x0545fd81}, -{9000000, DIF_BPF_COEFF2223, 0xf723fabf}, -{9000000, DIF_BPF_COEFF2425, 0x05b80b70}, -{9000000, DIF_BPF_COEFF2627, 0x03d2f669}, -{9000000, DIF_BPF_COEFF2829, 0xf313ff15}, -{9000000, DIF_BPF_COEFF3031, 0x0d550cbf}, -{9000000, DIF_BPF_COEFF3233, 0xfcf6eff2}, -{9000000, DIF_BPF_COEFF3435, 0xf544073d}, -{9000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 90_quant.dat*/ - - -/*case 9100000:*/ -/* BEGIN - DIF BPF register values from 91_quant.dat*/ -{9100000, DIF_BPF_COEFF01, 0xfffffffe}, -{9100000, DIF_BPF_COEFF23, 0x00030012}, -{9100000, DIF_BPF_COEFF45, 0x000fffdd}, -{9100000, DIF_BPF_COEFF67, 0xffacffea}, -{9100000, DIF_BPF_COEFF89, 0x009300cf}, -{9100000, DIF_BPF_COEFF1011, 0xffdcfe7c}, -{9100000, DIF_BPF_COEFF1213, 0xfea600f7}, -{9100000, DIF_BPF_COEFF1415, 0x02fd0190}, -{9100000, DIF_BPF_COEFF1617, 0xfd51fb46}, -{9100000, DIF_BPF_COEFF1819, 0xff150554}, -{9100000, DIF_BPF_COEFF2021, 0x0627fefd}, -{9100000, DIF_BPF_COEFF2223, 0xf778f978}, -{9100000, DIF_BPF_COEFF2425, 0x044d0b87}, -{9100000, DIF_BPF_COEFF2627, 0x0543f77d}, -{9100000, DIF_BPF_COEFF2829, 0xf2a0fdcf}, -{9100000, DIF_BPF_COEFF3031, 0x0cbe0d4e}, -{9100000, DIF_BPF_COEFF3233, 0xfdc4f01d}, -{9100000, DIF_BPF_COEFF3435, 0xf4f2070b}, -{9100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 91_quant.dat*/ - - -/*case 9200000:*/ -/* BEGIN - DIF BPF register values from 92_quant.dat*/ -{9200000, DIF_BPF_COEFF01, 0x0000fffd}, -{9200000, DIF_BPF_COEFF23, 0x00000010}, -{9200000, DIF_BPF_COEFF45, 0x001afff0}, -{9200000, DIF_BPF_COEFF67, 0xffaaffbf}, -{9200000, DIF_BPF_COEFF89, 0x006700ed}, -{9200000, DIF_BPF_COEFF1011, 0x0043feb6}, -{9200000, DIF_BPF_COEFF1213, 0xfe460047}, -{9200000, DIF_BPF_COEFF1415, 0x02db0258}, -{9200000, DIF_BPF_COEFF1617, 0xfe35fb1b}, -{9200000, DIF_BPF_COEFF1819, 0xfddc0473}, -{9200000, DIF_BPF_COEFF2021, 0x06c90082}, -{9200000, DIF_BPF_COEFF2223, 0xf811f85e}, -{9200000, DIF_BPF_COEFF2425, 0x02c90b66}, -{9200000, DIF_BPF_COEFF2627, 0x069ff8ad}, -{9200000, DIF_BPF_COEFF2829, 0xf250fc8d}, -{9200000, DIF_BPF_COEFF3031, 0x0c140dcf}, -{9200000, DIF_BPF_COEFF3233, 0xfe93f04d}, -{9200000, DIF_BPF_COEFF3435, 0xf4a106d9}, -{9200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 92_quant.dat*/ - - -/*case 9300000:*/ -/* BEGIN - DIF BPF register values from 93_quant.dat*/ -{9300000, DIF_BPF_COEFF01, 0x0000fffd}, -{9300000, DIF_BPF_COEFF23, 0xfffc000c}, -{9300000, DIF_BPF_COEFF45, 0x00200006}, -{9300000, DIF_BPF_COEFF67, 0xffb4ff9c}, -{9300000, DIF_BPF_COEFF89, 0x002f00ef}, -{9300000, DIF_BPF_COEFF1011, 0x00a4ff10}, -{9300000, DIF_BPF_COEFF1213, 0xfe0dff92}, -{9300000, DIF_BPF_COEFF1415, 0x028102f7}, -{9300000, DIF_BPF_COEFF1617, 0xff36fb37}, -{9300000, DIF_BPF_COEFF1819, 0xfcbf035e}, -{9300000, DIF_BPF_COEFF2021, 0x07260202}, -{9300000, DIF_BPF_COEFF2223, 0xf8e8f778}, -{9300000, DIF_BPF_COEFF2425, 0x01340b0d}, -{9300000, DIF_BPF_COEFF2627, 0x07e1f9f4}, -{9300000, DIF_BPF_COEFF2829, 0xf223fb51}, -{9300000, DIF_BPF_COEFF3031, 0x0b590e42}, -{9300000, DIF_BPF_COEFF3233, 0xff64f083}, -{9300000, DIF_BPF_COEFF3435, 0xf45206a7}, -{9300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 93_quant.dat*/ - - -/*case 9400000:*/ -/* BEGIN - DIF BPF register values from 94_quant.dat*/ -{9400000, DIF_BPF_COEFF01, 0x0000fffd}, -{9400000, DIF_BPF_COEFF23, 0xfff90005}, -{9400000, DIF_BPF_COEFF45, 0x0022001a}, -{9400000, DIF_BPF_COEFF67, 0xffc9ff86}, -{9400000, DIF_BPF_COEFF89, 0xfff000d7}, -{9400000, DIF_BPF_COEFF1011, 0x00f2ff82}, -{9400000, DIF_BPF_COEFF1213, 0xfe01fee5}, -{9400000, DIF_BPF_COEFF1415, 0x01f60362}, -{9400000, DIF_BPF_COEFF1617, 0x0044fb99}, -{9400000, DIF_BPF_COEFF1819, 0xfbcc0222}, -{9400000, DIF_BPF_COEFF2021, 0x07380370}, -{9400000, DIF_BPF_COEFF2223, 0xf9f7f6cc}, -{9400000, DIF_BPF_COEFF2425, 0xff990a7e}, -{9400000, DIF_BPF_COEFF2627, 0x0902fb50}, -{9400000, DIF_BPF_COEFF2829, 0xf21afa1f}, -{9400000, DIF_BPF_COEFF3031, 0x0a8d0ea6}, -{9400000, DIF_BPF_COEFF3233, 0x0034f0bf}, -{9400000, DIF_BPF_COEFF3435, 0xf4050675}, -{9400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 94_quant.dat*/ - - -/*case 9500000:*/ -/* BEGIN - DIF BPF register values from 95_quant.dat*/ -{9500000, DIF_BPF_COEFF01, 0x0000fffe}, -{9500000, DIF_BPF_COEFF23, 0xfff8fffe}, -{9500000, DIF_BPF_COEFF45, 0x001e002b}, -{9500000, DIF_BPF_COEFF67, 0xffe5ff81}, -{9500000, DIF_BPF_COEFF89, 0xffb400a5}, -{9500000, DIF_BPF_COEFF1011, 0x01280000}, -{9500000, DIF_BPF_COEFF1213, 0xfe24fe50}, -{9500000, DIF_BPF_COEFF1415, 0x01460390}, -{9500000, DIF_BPF_COEFF1617, 0x014dfc3a}, -{9500000, DIF_BPF_COEFF1819, 0xfb1000ce}, -{9500000, DIF_BPF_COEFF2021, 0x070104bf}, -{9500000, DIF_BPF_COEFF2223, 0xfb37f65f}, -{9500000, DIF_BPF_COEFF2425, 0xfe0009bc}, -{9500000, DIF_BPF_COEFF2627, 0x0a00fcbb}, -{9500000, DIF_BPF_COEFF2829, 0xf235f8f8}, -{9500000, DIF_BPF_COEFF3031, 0x09b20efc}, -{9500000, DIF_BPF_COEFF3233, 0x0105f101}, -{9500000, DIF_BPF_COEFF3435, 0xf3ba0642}, -{9500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 95_quant.dat*/ - - -/*case 9600000:*/ -/* BEGIN - DIF BPF register values from 96_quant.dat*/ -{9600000, DIF_BPF_COEFF01, 0x0001ffff}, -{9600000, DIF_BPF_COEFF23, 0xfff8fff7}, -{9600000, DIF_BPF_COEFF45, 0x00150036}, -{9600000, DIF_BPF_COEFF67, 0x0005ff8c}, -{9600000, DIF_BPF_COEFF89, 0xff810061}, -{9600000, DIF_BPF_COEFF1011, 0x013d007e}, -{9600000, DIF_BPF_COEFF1213, 0xfe71fddf}, -{9600000, DIF_BPF_COEFF1415, 0x007c0380}, -{9600000, DIF_BPF_COEFF1617, 0x0241fd13}, -{9600000, DIF_BPF_COEFF1819, 0xfa94ff70}, -{9600000, DIF_BPF_COEFF2021, 0x068005e2}, -{9600000, DIF_BPF_COEFF2223, 0xfc9bf633}, -{9600000, DIF_BPF_COEFF2425, 0xfc7308ca}, -{9600000, DIF_BPF_COEFF2627, 0x0ad5fe30}, -{9600000, DIF_BPF_COEFF2829, 0xf274f7e0}, -{9600000, DIF_BPF_COEFF3031, 0x08c90f43}, -{9600000, DIF_BPF_COEFF3233, 0x01d4f147}, -{9600000, DIF_BPF_COEFF3435, 0xf371060f}, -{9600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 96_quant.dat*/ - - -/*case 9700000:*/ -/* BEGIN - DIF BPF register values from 97_quant.dat*/ -{9700000, DIF_BPF_COEFF01, 0x00010001}, -{9700000, DIF_BPF_COEFF23, 0xfff9fff1}, -{9700000, DIF_BPF_COEFF45, 0x00090038}, -{9700000, DIF_BPF_COEFF67, 0x0025ffa7}, -{9700000, DIF_BPF_COEFF89, 0xff5e0012}, -{9700000, DIF_BPF_COEFF1011, 0x013200f0}, -{9700000, DIF_BPF_COEFF1213, 0xfee3fd9b}, -{9700000, DIF_BPF_COEFF1415, 0xffaa0331}, -{9700000, DIF_BPF_COEFF1617, 0x0311fe15}, -{9700000, DIF_BPF_COEFF1819, 0xfa60fe18}, -{9700000, DIF_BPF_COEFF2021, 0x05bd06d1}, -{9700000, DIF_BPF_COEFF2223, 0xfe1bf64a}, -{9700000, DIF_BPF_COEFF2425, 0xfafa07ae}, -{9700000, DIF_BPF_COEFF2627, 0x0b7effab}, -{9700000, DIF_BPF_COEFF2829, 0xf2d5f6d7}, -{9700000, DIF_BPF_COEFF3031, 0x07d30f7a}, -{9700000, DIF_BPF_COEFF3233, 0x02a3f194}, -{9700000, DIF_BPF_COEFF3435, 0xf32905dc}, -{9700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 97_quant.dat*/ - - -/*case 9800000:*/ -/* BEGIN - DIF BPF register values from 98_quant.dat*/ -{9800000, DIF_BPF_COEFF01, 0x00010002}, -{9800000, DIF_BPF_COEFF23, 0xfffcffee}, -{9800000, DIF_BPF_COEFF45, 0xfffb0032}, -{9800000, DIF_BPF_COEFF67, 0x003fffcd}, -{9800000, DIF_BPF_COEFF89, 0xff4effc1}, -{9800000, DIF_BPF_COEFF1011, 0x0106014a}, -{9800000, DIF_BPF_COEFF1213, 0xff6efd8a}, -{9800000, DIF_BPF_COEFF1415, 0xfedd02aa}, -{9800000, DIF_BPF_COEFF1617, 0x03b0ff34}, -{9800000, DIF_BPF_COEFF1819, 0xfa74fcd7}, -{9800000, DIF_BPF_COEFF2021, 0x04bf0781}, -{9800000, DIF_BPF_COEFF2223, 0xffaaf6a3}, -{9800000, DIF_BPF_COEFF2425, 0xf99e066b}, -{9800000, DIF_BPF_COEFF2627, 0x0bf90128}, -{9800000, DIF_BPF_COEFF2829, 0xf359f5e1}, -{9800000, DIF_BPF_COEFF3031, 0x06d20fa2}, -{9800000, DIF_BPF_COEFF3233, 0x0370f1e5}, -{9800000, DIF_BPF_COEFF3435, 0xf2e405a8}, -{9800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 98_quant.dat*/ - - -/*case 9900000:*/ -/* BEGIN - DIF BPF register values from 99_quant.dat*/ -{9900000, DIF_BPF_COEFF01, 0x00000003}, -{9900000, DIF_BPF_COEFF23, 0xffffffee}, -{9900000, DIF_BPF_COEFF45, 0xffef0024}, -{9900000, DIF_BPF_COEFF67, 0x0051fffa}, -{9900000, DIF_BPF_COEFF89, 0xff54ff77}, -{9900000, DIF_BPF_COEFF1011, 0x00be0184}, -{9900000, DIF_BPF_COEFF1213, 0x0006fdad}, -{9900000, DIF_BPF_COEFF1415, 0xfe2701f3}, -{9900000, DIF_BPF_COEFF1617, 0x0413005e}, -{9900000, DIF_BPF_COEFF1819, 0xfad1fbba}, -{9900000, DIF_BPF_COEFF2021, 0x039007ee}, -{9900000, DIF_BPF_COEFF2223, 0x013bf73d}, -{9900000, DIF_BPF_COEFF2425, 0xf868050a}, -{9900000, DIF_BPF_COEFF2627, 0x0c4302a1}, -{9900000, DIF_BPF_COEFF2829, 0xf3fdf4fe}, -{9900000, DIF_BPF_COEFF3031, 0x05c70fba}, -{9900000, DIF_BPF_COEFF3233, 0x043bf23c}, -{9900000, DIF_BPF_COEFF3435, 0xf2a10575}, -{9900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 99_quant.dat*/ - - -/*case 10000000:*/ -/* BEGIN - DIF BPF register values from 100_quant.dat*/ -{10000000, DIF_BPF_COEFF01, 0x00000003}, -{10000000, DIF_BPF_COEFF23, 0x0003fff1}, -{10000000, DIF_BPF_COEFF45, 0xffe50011}, -{10000000, DIF_BPF_COEFF67, 0x00570027}, -{10000000, DIF_BPF_COEFF89, 0xff70ff3c}, -{10000000, DIF_BPF_COEFF1011, 0x00620198}, -{10000000, DIF_BPF_COEFF1213, 0x009efe01}, -{10000000, DIF_BPF_COEFF1415, 0xfd95011a}, -{10000000, DIF_BPF_COEFF1617, 0x04350183}, -{10000000, DIF_BPF_COEFF1819, 0xfb71fad0}, -{10000000, DIF_BPF_COEFF2021, 0x023c0812}, -{10000000, DIF_BPF_COEFF2223, 0x02c3f811}, -{10000000, DIF_BPF_COEFF2425, 0xf75e0390}, -{10000000, DIF_BPF_COEFF2627, 0x0c5c0411}, -{10000000, DIF_BPF_COEFF2829, 0xf4c1f432}, -{10000000, DIF_BPF_COEFF3031, 0x04b30fc1}, -{10000000, DIF_BPF_COEFF3233, 0x0503f297}, -{10000000, DIF_BPF_COEFF3435, 0xf2610541}, -{10000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 100_quant.dat*/ - - -/*case 10100000:*/ -/* BEGIN - DIF BPF register values from 101_quant.dat*/ -{10100000, DIF_BPF_COEFF01, 0x00000003}, -{10100000, DIF_BPF_COEFF23, 0x0006fff7}, -{10100000, DIF_BPF_COEFF45, 0xffdffffc}, -{10100000, DIF_BPF_COEFF67, 0x00510050}, -{10100000, DIF_BPF_COEFF89, 0xff9dff18}, -{10100000, DIF_BPF_COEFF1011, 0xfffc0184}, -{10100000, DIF_BPF_COEFF1213, 0x0128fe80}, -{10100000, DIF_BPF_COEFF1415, 0xfd32002e}, -{10100000, DIF_BPF_COEFF1617, 0x04130292}, -{10100000, DIF_BPF_COEFF1819, 0xfc4dfa21}, -{10100000, DIF_BPF_COEFF2021, 0x00d107ee}, -{10100000, DIF_BPF_COEFF2223, 0x0435f91c}, -{10100000, DIF_BPF_COEFF2425, 0xf6850205}, -{10100000, DIF_BPF_COEFF2627, 0x0c430573}, -{10100000, DIF_BPF_COEFF2829, 0xf5a1f37d}, -{10100000, DIF_BPF_COEFF3031, 0x03990fba}, -{10100000, DIF_BPF_COEFF3233, 0x05c7f2f8}, -{10100000, DIF_BPF_COEFF3435, 0xf222050d}, -{10100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 101_quant.dat*/ - - -/*case 10200000:*/ -/* BEGIN - DIF BPF register values from 102_quant.dat*/ -{10200000, DIF_BPF_COEFF01, 0x00000002}, -{10200000, DIF_BPF_COEFF23, 0x0008fffe}, -{10200000, DIF_BPF_COEFF45, 0xffdfffe7}, -{10200000, DIF_BPF_COEFF67, 0x003f006e}, -{10200000, DIF_BPF_COEFF89, 0xffd6ff0f}, -{10200000, DIF_BPF_COEFF1011, 0xff96014a}, -{10200000, DIF_BPF_COEFF1213, 0x0197ff1f}, -{10200000, DIF_BPF_COEFF1415, 0xfd05ff3e}, -{10200000, DIF_BPF_COEFF1617, 0x03b0037c}, -{10200000, DIF_BPF_COEFF1819, 0xfd59f9b7}, -{10200000, DIF_BPF_COEFF2021, 0xff5d0781}, -{10200000, DIF_BPF_COEFF2223, 0x0585fa56}, -{10200000, DIF_BPF_COEFF2425, 0xf5e4006f}, -{10200000, DIF_BPF_COEFF2627, 0x0bf906c4}, -{10200000, DIF_BPF_COEFF2829, 0xf69df2e0}, -{10200000, DIF_BPF_COEFF3031, 0x02790fa2}, -{10200000, DIF_BPF_COEFF3233, 0x0688f35d}, -{10200000, DIF_BPF_COEFF3435, 0xf1e604d8}, -{10200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 102_quant.dat*/ - - -/*case 10300000:*/ -/* BEGIN - DIF BPF register values from 103_quant.dat*/ -{10300000, DIF_BPF_COEFF01, 0xffff0001}, -{10300000, DIF_BPF_COEFF23, 0x00090005}, -{10300000, DIF_BPF_COEFF45, 0xffe4ffd6}, -{10300000, DIF_BPF_COEFF67, 0x0025007e}, -{10300000, DIF_BPF_COEFF89, 0x0014ff20}, -{10300000, DIF_BPF_COEFF1011, 0xff3c00f0}, -{10300000, DIF_BPF_COEFF1213, 0x01e1ffd0}, -{10300000, DIF_BPF_COEFF1415, 0xfd12fe5c}, -{10300000, DIF_BPF_COEFF1617, 0x03110433}, -{10300000, DIF_BPF_COEFF1819, 0xfe88f996}, -{10300000, DIF_BPF_COEFF2021, 0xfdf106d1}, -{10300000, DIF_BPF_COEFF2223, 0x06aafbb7}, -{10300000, DIF_BPF_COEFF2425, 0xf57efed8}, -{10300000, DIF_BPF_COEFF2627, 0x0b7e07ff}, -{10300000, DIF_BPF_COEFF2829, 0xf7b0f25e}, -{10300000, DIF_BPF_COEFF3031, 0x01560f7a}, -{10300000, DIF_BPF_COEFF3233, 0x0745f3c7}, -{10300000, DIF_BPF_COEFF3435, 0xf1ac04a4}, -{10300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 103_quant.dat*/ - - -/*case 10400000:*/ -/* BEGIN - DIF BPF register values from 104_quant.dat*/ -{10400000, DIF_BPF_COEFF01, 0xffffffff}, -{10400000, DIF_BPF_COEFF23, 0x0008000c}, -{10400000, DIF_BPF_COEFF45, 0xffedffcb}, -{10400000, DIF_BPF_COEFF67, 0x0005007d}, -{10400000, DIF_BPF_COEFF89, 0x0050ff4c}, -{10400000, DIF_BPF_COEFF1011, 0xfef6007e}, -{10400000, DIF_BPF_COEFF1213, 0x01ff0086}, -{10400000, DIF_BPF_COEFF1415, 0xfd58fd97}, -{10400000, DIF_BPF_COEFF1617, 0x024104ad}, -{10400000, DIF_BPF_COEFF1819, 0xffcaf9c0}, -{10400000, DIF_BPF_COEFF2021, 0xfc9905e2}, -{10400000, DIF_BPF_COEFF2223, 0x079afd35}, -{10400000, DIF_BPF_COEFF2425, 0xf555fd46}, -{10400000, DIF_BPF_COEFF2627, 0x0ad50920}, -{10400000, DIF_BPF_COEFF2829, 0xf8d9f1f6}, -{10400000, DIF_BPF_COEFF3031, 0x00310f43}, -{10400000, DIF_BPF_COEFF3233, 0x07fdf435}, -{10400000, DIF_BPF_COEFF3435, 0xf174046f}, -{10400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 104_quant.dat*/ - - -/*case 10500000:*/ -/* BEGIN - DIF BPF register values from 105_quant.dat*/ -{10500000, DIF_BPF_COEFF01, 0xfffffffe}, -{10500000, DIF_BPF_COEFF23, 0x00050011}, -{10500000, DIF_BPF_COEFF45, 0xfffaffc8}, -{10500000, DIF_BPF_COEFF67, 0xffe5006b}, -{10500000, DIF_BPF_COEFF89, 0x0082ff8c}, -{10500000, DIF_BPF_COEFF1011, 0xfecc0000}, -{10500000, DIF_BPF_COEFF1213, 0x01f00130}, -{10500000, DIF_BPF_COEFF1415, 0xfdd2fcfc}, -{10500000, DIF_BPF_COEFF1617, 0x014d04e3}, -{10500000, DIF_BPF_COEFF1819, 0x010efa32}, -{10500000, DIF_BPF_COEFF2021, 0xfb6404bf}, -{10500000, DIF_BPF_COEFF2223, 0x084efec5}, -{10500000, DIF_BPF_COEFF2425, 0xf569fbc2}, -{10500000, DIF_BPF_COEFF2627, 0x0a000a23}, -{10500000, DIF_BPF_COEFF2829, 0xfa15f1ab}, -{10500000, DIF_BPF_COEFF3031, 0xff0b0efc}, -{10500000, DIF_BPF_COEFF3233, 0x08b0f4a7}, -{10500000, DIF_BPF_COEFF3435, 0xf13f043a}, -{10500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 105_quant.dat*/ - - -/*case 10600000:*/ -/* BEGIN - DIF BPF register values from 106_quant.dat*/ -{10600000, DIF_BPF_COEFF01, 0x0000fffd}, -{10600000, DIF_BPF_COEFF23, 0x00020012}, -{10600000, DIF_BPF_COEFF45, 0x0007ffcd}, -{10600000, DIF_BPF_COEFF67, 0xffc9004c}, -{10600000, DIF_BPF_COEFF89, 0x00a4ffd9}, -{10600000, DIF_BPF_COEFF1011, 0xfec3ff82}, -{10600000, DIF_BPF_COEFF1213, 0x01b401c1}, -{10600000, DIF_BPF_COEFF1415, 0xfe76fc97}, -{10600000, DIF_BPF_COEFF1617, 0x004404d2}, -{10600000, DIF_BPF_COEFF1819, 0x0245fae8}, -{10600000, DIF_BPF_COEFF2021, 0xfa5f0370}, -{10600000, DIF_BPF_COEFF2223, 0x08c1005f}, -{10600000, DIF_BPF_COEFF2425, 0xf5bcfa52}, -{10600000, DIF_BPF_COEFF2627, 0x09020b04}, -{10600000, DIF_BPF_COEFF2829, 0xfb60f17b}, -{10600000, DIF_BPF_COEFF3031, 0xfde70ea6}, -{10600000, DIF_BPF_COEFF3233, 0x095df51e}, -{10600000, DIF_BPF_COEFF3435, 0xf10c0405}, -{10600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 106_quant.dat*/ - - -/*case 10700000:*/ -/* BEGIN - DIF BPF register values from 107_quant.dat*/ -{10700000, DIF_BPF_COEFF01, 0x0000fffd}, -{10700000, DIF_BPF_COEFF23, 0xffff0011}, -{10700000, DIF_BPF_COEFF45, 0x0014ffdb}, -{10700000, DIF_BPF_COEFF67, 0xffb40023}, -{10700000, DIF_BPF_COEFF89, 0x00b2002a}, -{10700000, DIF_BPF_COEFF1011, 0xfedbff10}, -{10700000, DIF_BPF_COEFF1213, 0x0150022d}, -{10700000, DIF_BPF_COEFF1415, 0xff38fc6f}, -{10700000, DIF_BPF_COEFF1617, 0xff36047b}, -{10700000, DIF_BPF_COEFF1819, 0x035efbda}, -{10700000, DIF_BPF_COEFF2021, 0xf9940202}, -{10700000, DIF_BPF_COEFF2223, 0x08ee01f5}, -{10700000, DIF_BPF_COEFF2425, 0xf649f8fe}, -{10700000, DIF_BPF_COEFF2627, 0x07e10bc2}, -{10700000, DIF_BPF_COEFF2829, 0xfcb6f169}, -{10700000, DIF_BPF_COEFF3031, 0xfcc60e42}, -{10700000, DIF_BPF_COEFF3233, 0x0a04f599}, -{10700000, DIF_BPF_COEFF3435, 0xf0db03d0}, -{10700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 107_quant.dat*/ - - -/*case 10800000:*/ -/* BEGIN - DIF BPF register values from 108_quant.dat*/ -{10800000, DIF_BPF_COEFF01, 0x0000fffd}, -{10800000, DIF_BPF_COEFF23, 0xfffb000d}, -{10800000, DIF_BPF_COEFF45, 0x001dffed}, -{10800000, DIF_BPF_COEFF67, 0xffaafff5}, -{10800000, DIF_BPF_COEFF89, 0x00aa0077}, -{10800000, DIF_BPF_COEFF1011, 0xff13feb6}, -{10800000, DIF_BPF_COEFF1213, 0x00ce026b}, -{10800000, DIF_BPF_COEFF1415, 0x000afc85}, -{10800000, DIF_BPF_COEFF1617, 0xfe3503e3}, -{10800000, DIF_BPF_COEFF1819, 0x044cfcfb}, -{10800000, DIF_BPF_COEFF2021, 0xf90c0082}, -{10800000, DIF_BPF_COEFF2223, 0x08d5037f}, -{10800000, DIF_BPF_COEFF2425, 0xf710f7cc}, -{10800000, DIF_BPF_COEFF2627, 0x069f0c59}, -{10800000, DIF_BPF_COEFF2829, 0xfe16f173}, -{10800000, DIF_BPF_COEFF3031, 0xfbaa0dcf}, -{10800000, DIF_BPF_COEFF3233, 0x0aa5f617}, -{10800000, DIF_BPF_COEFF3435, 0xf0ad039b}, -{10800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 108_quant.dat*/ - - -/*case 10900000:*/ -/* BEGIN - DIF BPF register values from 109_quant.dat*/ -{10900000, DIF_BPF_COEFF01, 0x0000fffe}, -{10900000, DIF_BPF_COEFF23, 0xfff90006}, -{10900000, DIF_BPF_COEFF45, 0x00210003}, -{10900000, DIF_BPF_COEFF67, 0xffacffc8}, -{10900000, DIF_BPF_COEFF89, 0x008e00b6}, -{10900000, DIF_BPF_COEFF1011, 0xff63fe7c}, -{10900000, DIF_BPF_COEFF1213, 0x003a0275}, -{10900000, DIF_BPF_COEFF1415, 0x00dafcda}, -{10900000, DIF_BPF_COEFF1617, 0xfd510313}, -{10900000, DIF_BPF_COEFF1819, 0x0501fe40}, -{10900000, DIF_BPF_COEFF2021, 0xf8cbfefd}, -{10900000, DIF_BPF_COEFF2223, 0x087604f0}, -{10900000, DIF_BPF_COEFF2425, 0xf80af6c2}, -{10900000, DIF_BPF_COEFF2627, 0x05430cc8}, -{10900000, DIF_BPF_COEFF2829, 0xff7af19a}, -{10900000, DIF_BPF_COEFF3031, 0xfa940d4e}, -{10900000, DIF_BPF_COEFF3233, 0x0b3ff699}, -{10900000, DIF_BPF_COEFF3435, 0xf0810365}, -{10900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 109_quant.dat*/ - - -/*case 11000000:*/ -/* BEGIN - DIF BPF register values from 110_quant.dat*/ -{11000000, DIF_BPF_COEFF01, 0x0001ffff}, -{11000000, DIF_BPF_COEFF23, 0xfff8ffff}, -{11000000, DIF_BPF_COEFF45, 0x00210018}, -{11000000, DIF_BPF_COEFF67, 0xffbaffa3}, -{11000000, DIF_BPF_COEFF89, 0x006000e1}, -{11000000, DIF_BPF_COEFF1011, 0xffc4fe68}, -{11000000, DIF_BPF_COEFF1213, 0xffa0024b}, -{11000000, DIF_BPF_COEFF1415, 0x019afd66}, -{11000000, DIF_BPF_COEFF1617, 0xfc990216}, -{11000000, DIF_BPF_COEFF1819, 0x0575ff99}, -{11000000, DIF_BPF_COEFF2021, 0xf8d4fd81}, -{11000000, DIF_BPF_COEFF2223, 0x07d40640}, -{11000000, DIF_BPF_COEFF2425, 0xf932f5e6}, -{11000000, DIF_BPF_COEFF2627, 0x03d20d0d}, -{11000000, DIF_BPF_COEFF2829, 0x00dff1de}, -{11000000, DIF_BPF_COEFF3031, 0xf9860cbf}, -{11000000, DIF_BPF_COEFF3233, 0x0bd1f71e}, -{11000000, DIF_BPF_COEFF3435, 0xf058032f}, -{11000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 110_quant.dat*/ - - -/*case 11100000:*/ -/* BEGIN - DIF BPF register values from 111_quant.dat*/ -{11100000, DIF_BPF_COEFF01, 0x00010000}, -{11100000, DIF_BPF_COEFF23, 0xfff8fff8}, -{11100000, DIF_BPF_COEFF45, 0x001b0029}, -{11100000, DIF_BPF_COEFF67, 0xffd1ff8a}, -{11100000, DIF_BPF_COEFF89, 0x002600f2}, -{11100000, DIF_BPF_COEFF1011, 0x002cfe7c}, -{11100000, DIF_BPF_COEFF1213, 0xff0f01f0}, -{11100000, DIF_BPF_COEFF1415, 0x023bfe20}, -{11100000, DIF_BPF_COEFF1617, 0xfc1700fa}, -{11100000, DIF_BPF_COEFF1819, 0x05a200f7}, -{11100000, DIF_BPF_COEFF2021, 0xf927fc1c}, -{11100000, DIF_BPF_COEFF2223, 0x06f40765}, -{11100000, DIF_BPF_COEFF2425, 0xfa82f53b}, -{11100000, DIF_BPF_COEFF2627, 0x02510d27}, -{11100000, DIF_BPF_COEFF2829, 0x0243f23d}, -{11100000, DIF_BPF_COEFF3031, 0xf8810c24}, -{11100000, DIF_BPF_COEFF3233, 0x0c5cf7a7}, -{11100000, DIF_BPF_COEFF3435, 0xf03102fa}, -{11100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 111_quant.dat*/ - - -/*case 11200000:*/ -/* BEGIN - DIF BPF register values from 112_quant.dat*/ -{11200000, DIF_BPF_COEFF01, 0x00010002}, -{11200000, DIF_BPF_COEFF23, 0xfffafff2}, -{11200000, DIF_BPF_COEFF45, 0x00110035}, -{11200000, DIF_BPF_COEFF67, 0xfff0ff81}, -{11200000, DIF_BPF_COEFF89, 0xffe700e7}, -{11200000, DIF_BPF_COEFF1011, 0x008ffeb6}, -{11200000, DIF_BPF_COEFF1213, 0xfe94016d}, -{11200000, DIF_BPF_COEFF1415, 0x02b0fefb}, -{11200000, DIF_BPF_COEFF1617, 0xfbd3ffd1}, -{11200000, DIF_BPF_COEFF1819, 0x05850249}, -{11200000, DIF_BPF_COEFF2021, 0xf9c1fadb}, -{11200000, DIF_BPF_COEFF2223, 0x05de0858}, -{11200000, DIF_BPF_COEFF2425, 0xfbf2f4c4}, -{11200000, DIF_BPF_COEFF2627, 0x00c70d17}, -{11200000, DIF_BPF_COEFF2829, 0x03a0f2b8}, -{11200000, DIF_BPF_COEFF3031, 0xf7870b7c}, -{11200000, DIF_BPF_COEFF3233, 0x0cdff833}, -{11200000, DIF_BPF_COEFF3435, 0xf00d02c4}, -{11200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 112_quant.dat*/ - - -/*case 11300000:*/ -/* BEGIN - DIF BPF register values from 113_quant.dat*/ -{11300000, DIF_BPF_COEFF01, 0x00000003}, -{11300000, DIF_BPF_COEFF23, 0xfffdffee}, -{11300000, DIF_BPF_COEFF45, 0x00040038}, -{11300000, DIF_BPF_COEFF67, 0x0010ff88}, -{11300000, DIF_BPF_COEFF89, 0xffac00c2}, -{11300000, DIF_BPF_COEFF1011, 0x00e2ff10}, -{11300000, DIF_BPF_COEFF1213, 0xfe3900cb}, -{11300000, DIF_BPF_COEFF1415, 0x02f1ffe9}, -{11300000, DIF_BPF_COEFF1617, 0xfbd3feaa}, -{11300000, DIF_BPF_COEFF1819, 0x05210381}, -{11300000, DIF_BPF_COEFF2021, 0xfa9cf9c8}, -{11300000, DIF_BPF_COEFF2223, 0x04990912}, -{11300000, DIF_BPF_COEFF2425, 0xfd7af484}, -{11300000, DIF_BPF_COEFF2627, 0xff390cdb}, -{11300000, DIF_BPF_COEFF2829, 0x04f4f34d}, -{11300000, DIF_BPF_COEFF3031, 0xf69a0ac9}, -{11300000, DIF_BPF_COEFF3233, 0x0d5af8c1}, -{11300000, DIF_BPF_COEFF3435, 0xefec028e}, -{11300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 113_quant.dat*/ - - -/*case 11400000:*/ -/* BEGIN - DIF BPF register values from 114_quant.dat*/ -{11400000, DIF_BPF_COEFF01, 0x00000003}, -{11400000, DIF_BPF_COEFF23, 0x0000ffee}, -{11400000, DIF_BPF_COEFF45, 0xfff60033}, -{11400000, DIF_BPF_COEFF67, 0x002fff9f}, -{11400000, DIF_BPF_COEFF89, 0xff7b0087}, -{11400000, DIF_BPF_COEFF1011, 0x011eff82}, -{11400000, DIF_BPF_COEFF1213, 0xfe080018}, -{11400000, DIF_BPF_COEFF1415, 0x02f900d8}, -{11400000, DIF_BPF_COEFF1617, 0xfc17fd96}, -{11400000, DIF_BPF_COEFF1819, 0x04790490}, -{11400000, DIF_BPF_COEFF2021, 0xfbadf8ed}, -{11400000, DIF_BPF_COEFF2223, 0x032f098e}, -{11400000, DIF_BPF_COEFF2425, 0xff10f47d}, -{11400000, DIF_BPF_COEFF2627, 0xfdaf0c75}, -{11400000, DIF_BPF_COEFF2829, 0x063cf3fc}, -{11400000, DIF_BPF_COEFF3031, 0xf5ba0a0b}, -{11400000, DIF_BPF_COEFF3233, 0x0dccf952}, -{11400000, DIF_BPF_COEFF3435, 0xefcd0258}, -{11400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 114_quant.dat*/ - - -/*case 11500000:*/ -/* BEGIN - DIF BPF register values from 115_quant.dat*/ -{11500000, DIF_BPF_COEFF01, 0x00000003}, -{11500000, DIF_BPF_COEFF23, 0x0004fff1}, -{11500000, DIF_BPF_COEFF45, 0xffea0026}, -{11500000, DIF_BPF_COEFF67, 0x0046ffc3}, -{11500000, DIF_BPF_COEFF89, 0xff5a003c}, -{11500000, DIF_BPF_COEFF1011, 0x013b0000}, -{11500000, DIF_BPF_COEFF1213, 0xfe04ff63}, -{11500000, DIF_BPF_COEFF1415, 0x02c801b8}, -{11500000, DIF_BPF_COEFF1617, 0xfc99fca6}, -{11500000, DIF_BPF_COEFF1819, 0x0397056a}, -{11500000, DIF_BPF_COEFF2021, 0xfcecf853}, -{11500000, DIF_BPF_COEFF2223, 0x01ad09c9}, -{11500000, DIF_BPF_COEFF2425, 0x00acf4ad}, -{11500000, DIF_BPF_COEFF2627, 0xfc2e0be7}, -{11500000, DIF_BPF_COEFF2829, 0x0773f4c2}, -{11500000, DIF_BPF_COEFF3031, 0xf4e90943}, -{11500000, DIF_BPF_COEFF3233, 0x0e35f9e6}, -{11500000, DIF_BPF_COEFF3435, 0xefb10221}, -{11500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 115_quant.dat*/ - - -/*case 11600000:*/ -/* BEGIN - DIF BPF register values from 116_quant.dat*/ -{11600000, DIF_BPF_COEFF01, 0x00000002}, -{11600000, DIF_BPF_COEFF23, 0x0007fff6}, -{11600000, DIF_BPF_COEFF45, 0xffe20014}, -{11600000, DIF_BPF_COEFF67, 0x0054ffee}, -{11600000, DIF_BPF_COEFF89, 0xff4effeb}, -{11600000, DIF_BPF_COEFF1011, 0x0137007e}, -{11600000, DIF_BPF_COEFF1213, 0xfe2efebb}, -{11600000, DIF_BPF_COEFF1415, 0x0260027a}, -{11600000, DIF_BPF_COEFF1617, 0xfd51fbe6}, -{11600000, DIF_BPF_COEFF1819, 0x02870605}, -{11600000, DIF_BPF_COEFF2021, 0xfe4af7fe}, -{11600000, DIF_BPF_COEFF2223, 0x001d09c1}, -{11600000, DIF_BPF_COEFF2425, 0x0243f515}, -{11600000, DIF_BPF_COEFF2627, 0xfabd0b32}, -{11600000, DIF_BPF_COEFF2829, 0x0897f59e}, -{11600000, DIF_BPF_COEFF3031, 0xf4280871}, -{11600000, DIF_BPF_COEFF3233, 0x0e95fa7c}, -{11600000, DIF_BPF_COEFF3435, 0xef9701eb}, -{11600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 116_quant.dat*/ - - -/*case 11700000:*/ -/* BEGIN - DIF BPF register values from 117_quant.dat*/ -{11700000, DIF_BPF_COEFF01, 0xffff0001}, -{11700000, DIF_BPF_COEFF23, 0x0008fffd}, -{11700000, DIF_BPF_COEFF45, 0xffdeffff}, -{11700000, DIF_BPF_COEFF67, 0x0056001d}, -{11700000, DIF_BPF_COEFF89, 0xff57ff9c}, -{11700000, DIF_BPF_COEFF1011, 0x011300f0}, -{11700000, DIF_BPF_COEFF1213, 0xfe82fe2e}, -{11700000, DIF_BPF_COEFF1415, 0x01ca0310}, -{11700000, DIF_BPF_COEFF1617, 0xfe35fb62}, -{11700000, DIF_BPF_COEFF1819, 0x0155065a}, -{11700000, DIF_BPF_COEFF2021, 0xffbaf7f2}, -{11700000, DIF_BPF_COEFF2223, 0xfe8c0977}, -{11700000, DIF_BPF_COEFF2425, 0x03cef5b2}, -{11700000, DIF_BPF_COEFF2627, 0xf9610a58}, -{11700000, DIF_BPF_COEFF2829, 0x09a5f68f}, -{11700000, DIF_BPF_COEFF3031, 0xf3790797}, -{11700000, DIF_BPF_COEFF3233, 0x0eebfb14}, -{11700000, DIF_BPF_COEFF3435, 0xef8001b5}, -{11700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 117_quant.dat*/ - - -/*case 11800000:*/ -/* BEGIN - DIF BPF register values from 118_quant.dat*/ -{11800000, DIF_BPF_COEFF01, 0xffff0000}, -{11800000, DIF_BPF_COEFF23, 0x00080004}, -{11800000, DIF_BPF_COEFF45, 0xffe0ffe9}, -{11800000, DIF_BPF_COEFF67, 0x004c0047}, -{11800000, DIF_BPF_COEFF89, 0xff75ff58}, -{11800000, DIF_BPF_COEFF1011, 0x00d1014a}, -{11800000, DIF_BPF_COEFF1213, 0xfef9fdc8}, -{11800000, DIF_BPF_COEFF1415, 0x0111036f}, -{11800000, DIF_BPF_COEFF1617, 0xff36fb21}, -{11800000, DIF_BPF_COEFF1819, 0x00120665}, -{11800000, DIF_BPF_COEFF2021, 0x012df82e}, -{11800000, DIF_BPF_COEFF2223, 0xfd0708ec}, -{11800000, DIF_BPF_COEFF2425, 0x0542f682}, -{11800000, DIF_BPF_COEFF2627, 0xf81f095c}, -{11800000, DIF_BPF_COEFF2829, 0x0a9af792}, -{11800000, DIF_BPF_COEFF3031, 0xf2db06b5}, -{11800000, DIF_BPF_COEFF3233, 0x0f38fbad}, -{11800000, DIF_BPF_COEFF3435, 0xef6c017e}, -{11800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 118_quant.dat*/ - - -/*case 11900000:*/ -/* BEGIN - DIF BPF register values from 119_quant.dat*/ -{11900000, DIF_BPF_COEFF01, 0xffffffff}, -{11900000, DIF_BPF_COEFF23, 0x0007000b}, -{11900000, DIF_BPF_COEFF45, 0xffe7ffd8}, -{11900000, DIF_BPF_COEFF67, 0x00370068}, -{11900000, DIF_BPF_COEFF89, 0xffa4ff28}, -{11900000, DIF_BPF_COEFF1011, 0x00790184}, -{11900000, DIF_BPF_COEFF1213, 0xff87fd91}, -{11900000, DIF_BPF_COEFF1415, 0x00430392}, -{11900000, DIF_BPF_COEFF1617, 0x0044fb26}, -{11900000, DIF_BPF_COEFF1819, 0xfece0626}, -{11900000, DIF_BPF_COEFF2021, 0x0294f8b2}, -{11900000, DIF_BPF_COEFF2223, 0xfb990825}, -{11900000, DIF_BPF_COEFF2425, 0x0698f77f}, -{11900000, DIF_BPF_COEFF2627, 0xf6fe0842}, -{11900000, DIF_BPF_COEFF2829, 0x0b73f8a7}, -{11900000, DIF_BPF_COEFF3031, 0xf25105cd}, -{11900000, DIF_BPF_COEFF3233, 0x0f7bfc48}, -{11900000, DIF_BPF_COEFF3435, 0xef5a0148}, -{11900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 119_quant.dat*/ - - -/*case 12000000:*/ -/* BEGIN - DIF BPF register values from 120_quant.dat*/ -{12000000, DIF_BPF_COEFF01, 0x0000fffe}, -{12000000, DIF_BPF_COEFF23, 0x00050010}, -{12000000, DIF_BPF_COEFF45, 0xfff2ffcc}, -{12000000, DIF_BPF_COEFF67, 0x001b007b}, -{12000000, DIF_BPF_COEFF89, 0xffdfff10}, -{12000000, DIF_BPF_COEFF1011, 0x00140198}, -{12000000, DIF_BPF_COEFF1213, 0x0020fd8e}, -{12000000, DIF_BPF_COEFF1415, 0xff710375}, -{12000000, DIF_BPF_COEFF1617, 0x014dfb73}, -{12000000, DIF_BPF_COEFF1819, 0xfd9a059f}, -{12000000, DIF_BPF_COEFF2021, 0x03e0f978}, -{12000000, DIF_BPF_COEFF2223, 0xfa4e0726}, -{12000000, DIF_BPF_COEFF2425, 0x07c8f8a7}, -{12000000, DIF_BPF_COEFF2627, 0xf600070c}, -{12000000, DIF_BPF_COEFF2829, 0x0c2ff9c9}, -{12000000, DIF_BPF_COEFF3031, 0xf1db04de}, -{12000000, DIF_BPF_COEFF3233, 0x0fb4fce5}, -{12000000, DIF_BPF_COEFF3435, 0xef4b0111}, -{12000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 120_quant.dat*/ - - -/*case 12100000:*/ -/* BEGIN - DIF BPF register values from 121_quant.dat*/ -{12100000, DIF_BPF_COEFF01, 0x0000fffd}, -{12100000, DIF_BPF_COEFF23, 0x00010012}, -{12100000, DIF_BPF_COEFF45, 0xffffffc8}, -{12100000, DIF_BPF_COEFF67, 0xfffb007e}, -{12100000, DIF_BPF_COEFF89, 0x001dff14}, -{12100000, DIF_BPF_COEFF1011, 0xffad0184}, -{12100000, DIF_BPF_COEFF1213, 0x00b7fdbe}, -{12100000, DIF_BPF_COEFF1415, 0xfea9031b}, -{12100000, DIF_BPF_COEFF1617, 0x0241fc01}, -{12100000, DIF_BPF_COEFF1819, 0xfc8504d6}, -{12100000, DIF_BPF_COEFF2021, 0x0504fa79}, -{12100000, DIF_BPF_COEFF2223, 0xf93005f6}, -{12100000, DIF_BPF_COEFF2425, 0x08caf9f2}, -{12100000, DIF_BPF_COEFF2627, 0xf52b05c0}, -{12100000, DIF_BPF_COEFF2829, 0x0ccbfaf9}, -{12100000, DIF_BPF_COEFF3031, 0xf17903eb}, -{12100000, DIF_BPF_COEFF3233, 0x0fe3fd83}, -{12100000, DIF_BPF_COEFF3435, 0xef3f00db}, -{12100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 121_quant.dat*/ - - -/*case 12200000:*/ -/* BEGIN - DIF BPF register values from 122_quant.dat*/ -{12200000, DIF_BPF_COEFF01, 0x0000fffd}, -{12200000, DIF_BPF_COEFF23, 0xfffe0011}, -{12200000, DIF_BPF_COEFF45, 0x000cffcc}, -{12200000, DIF_BPF_COEFF67, 0xffdb0071}, -{12200000, DIF_BPF_COEFF89, 0x0058ff32}, -{12200000, DIF_BPF_COEFF1011, 0xff4f014a}, -{12200000, DIF_BPF_COEFF1213, 0x013cfe1f}, -{12200000, DIF_BPF_COEFF1415, 0xfdfb028a}, -{12200000, DIF_BPF_COEFF1617, 0x0311fcc9}, -{12200000, DIF_BPF_COEFF1819, 0xfb9d03d6}, -{12200000, DIF_BPF_COEFF2021, 0x05f4fbad}, -{12200000, DIF_BPF_COEFF2223, 0xf848049d}, -{12200000, DIF_BPF_COEFF2425, 0x0999fb5b}, -{12200000, DIF_BPF_COEFF2627, 0xf4820461}, -{12200000, DIF_BPF_COEFF2829, 0x0d46fc32}, -{12200000, DIF_BPF_COEFF3031, 0xf12d02f4}, -{12200000, DIF_BPF_COEFF3233, 0x1007fe21}, -{12200000, DIF_BPF_COEFF3435, 0xef3600a4}, -{12200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 122_quant.dat*/ - - -/*case 12300000:*/ -/* BEGIN - DIF BPF register values from 123_quant.dat*/ -{12300000, DIF_BPF_COEFF01, 0x0000fffe}, -{12300000, DIF_BPF_COEFF23, 0xfffa000e}, -{12300000, DIF_BPF_COEFF45, 0x0017ffd9}, -{12300000, DIF_BPF_COEFF67, 0xffc10055}, -{12300000, DIF_BPF_COEFF89, 0x0088ff68}, -{12300000, DIF_BPF_COEFF1011, 0xff0400f0}, -{12300000, DIF_BPF_COEFF1213, 0x01a6fea7}, -{12300000, DIF_BPF_COEFF1415, 0xfd7501cc}, -{12300000, DIF_BPF_COEFF1617, 0x03b0fdc0}, -{12300000, DIF_BPF_COEFF1819, 0xfaef02a8}, -{12300000, DIF_BPF_COEFF2021, 0x06a7fd07}, -{12300000, DIF_BPF_COEFF2223, 0xf79d0326}, -{12300000, DIF_BPF_COEFF2425, 0x0a31fcda}, -{12300000, DIF_BPF_COEFF2627, 0xf40702f3}, -{12300000, DIF_BPF_COEFF2829, 0x0d9ffd72}, -{12300000, DIF_BPF_COEFF3031, 0xf0f601fa}, -{12300000, DIF_BPF_COEFF3233, 0x1021fec0}, -{12300000, DIF_BPF_COEFF3435, 0xef2f006d}, -{12300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 123_quant.dat*/ - - -/*case 12400000:*/ -/* BEGIN - DIF BPF register values from 124_quant.dat*/ -{12400000, DIF_BPF_COEFF01, 0x0001ffff}, -{12400000, DIF_BPF_COEFF23, 0xfff80007}, -{12400000, DIF_BPF_COEFF45, 0x001fffeb}, -{12400000, DIF_BPF_COEFF67, 0xffaf002d}, -{12400000, DIF_BPF_COEFF89, 0x00a8ffb0}, -{12400000, DIF_BPF_COEFF1011, 0xfed3007e}, -{12400000, DIF_BPF_COEFF1213, 0x01e9ff4c}, -{12400000, DIF_BPF_COEFF1415, 0xfd2000ee}, -{12400000, DIF_BPF_COEFF1617, 0x0413fed8}, -{12400000, DIF_BPF_COEFF1819, 0xfa82015c}, -{12400000, DIF_BPF_COEFF2021, 0x0715fe7d}, -{12400000, DIF_BPF_COEFF2223, 0xf7340198}, -{12400000, DIF_BPF_COEFF2425, 0x0a8dfe69}, -{12400000, DIF_BPF_COEFF2627, 0xf3bd017c}, -{12400000, DIF_BPF_COEFF2829, 0x0dd5feb8}, -{12400000, DIF_BPF_COEFF3031, 0xf0d500fd}, -{12400000, DIF_BPF_COEFF3233, 0x1031ff60}, -{12400000, DIF_BPF_COEFF3435, 0xef2b0037}, -{12400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 124_quant.dat*/ - - -/*case 12500000:*/ -/* BEGIN - DIF BPF register values from 125_quant.dat*/ -{12500000, DIF_BPF_COEFF01, 0x00010000}, -{12500000, DIF_BPF_COEFF23, 0xfff70000}, -{12500000, DIF_BPF_COEFF45, 0x00220000}, -{12500000, DIF_BPF_COEFF67, 0xffa90000}, -{12500000, DIF_BPF_COEFF89, 0x00b30000}, -{12500000, DIF_BPF_COEFF1011, 0xfec20000}, -{12500000, DIF_BPF_COEFF1213, 0x02000000}, -{12500000, DIF_BPF_COEFF1415, 0xfd030000}, -{12500000, DIF_BPF_COEFF1617, 0x04350000}, -{12500000, DIF_BPF_COEFF1819, 0xfa5e0000}, -{12500000, DIF_BPF_COEFF2021, 0x073b0000}, -{12500000, DIF_BPF_COEFF2223, 0xf7110000}, -{12500000, DIF_BPF_COEFF2425, 0x0aac0000}, -{12500000, DIF_BPF_COEFF2627, 0xf3a40000}, -{12500000, DIF_BPF_COEFF2829, 0x0de70000}, -{12500000, DIF_BPF_COEFF3031, 0xf0c90000}, -{12500000, DIF_BPF_COEFF3233, 0x10360000}, -{12500000, DIF_BPF_COEFF3435, 0xef290000}, -{12500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 125_quant.dat*/ - - -/*case 12600000:*/ -/* BEGIN - DIF BPF register values from 126_quant.dat*/ -{12600000, DIF_BPF_COEFF01, 0x00010001}, -{12600000, DIF_BPF_COEFF23, 0xfff8fff9}, -{12600000, DIF_BPF_COEFF45, 0x001f0015}, -{12600000, DIF_BPF_COEFF67, 0xffafffd3}, -{12600000, DIF_BPF_COEFF89, 0x00a80050}, -{12600000, DIF_BPF_COEFF1011, 0xfed3ff82}, -{12600000, DIF_BPF_COEFF1213, 0x01e900b4}, -{12600000, DIF_BPF_COEFF1415, 0xfd20ff12}, -{12600000, DIF_BPF_COEFF1617, 0x04130128}, -{12600000, DIF_BPF_COEFF1819, 0xfa82fea4}, -{12600000, DIF_BPF_COEFF2021, 0x07150183}, -{12600000, DIF_BPF_COEFF2223, 0xf734fe68}, -{12600000, DIF_BPF_COEFF2425, 0x0a8d0197}, -{12600000, DIF_BPF_COEFF2627, 0xf3bdfe84}, -{12600000, DIF_BPF_COEFF2829, 0x0dd50148}, -{12600000, DIF_BPF_COEFF3031, 0xf0d5ff03}, -{12600000, DIF_BPF_COEFF3233, 0x103100a0}, -{12600000, DIF_BPF_COEFF3435, 0xef2bffc9}, -{12600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 126_quant.dat*/ - - -/*case 12700000:*/ -/* BEGIN - DIF BPF register values from 127_quant.dat*/ -{12700000, DIF_BPF_COEFF01, 0x00000002}, -{12700000, DIF_BPF_COEFF23, 0xfffafff2}, -{12700000, DIF_BPF_COEFF45, 0x00170027}, -{12700000, DIF_BPF_COEFF67, 0xffc1ffab}, -{12700000, DIF_BPF_COEFF89, 0x00880098}, -{12700000, DIF_BPF_COEFF1011, 0xff04ff10}, -{12700000, DIF_BPF_COEFF1213, 0x01a60159}, -{12700000, DIF_BPF_COEFF1415, 0xfd75fe34}, -{12700000, DIF_BPF_COEFF1617, 0x03b00240}, -{12700000, DIF_BPF_COEFF1819, 0xfaeffd58}, -{12700000, DIF_BPF_COEFF2021, 0x06a702f9}, -{12700000, DIF_BPF_COEFF2223, 0xf79dfcda}, -{12700000, DIF_BPF_COEFF2425, 0x0a310326}, -{12700000, DIF_BPF_COEFF2627, 0xf407fd0d}, -{12700000, DIF_BPF_COEFF2829, 0x0d9f028e}, -{12700000, DIF_BPF_COEFF3031, 0xf0f6fe06}, -{12700000, DIF_BPF_COEFF3233, 0x10210140}, -{12700000, DIF_BPF_COEFF3435, 0xef2fff93}, -{12700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 127_quant.dat*/ - - -/*case 12800000:*/ -/* BEGIN - DIF BPF register values from 128_quant.dat*/ -{12800000, DIF_BPF_COEFF01, 0x00000003}, -{12800000, DIF_BPF_COEFF23, 0xfffeffef}, -{12800000, DIF_BPF_COEFF45, 0x000c0034}, -{12800000, DIF_BPF_COEFF67, 0xffdbff8f}, -{12800000, DIF_BPF_COEFF89, 0x005800ce}, -{12800000, DIF_BPF_COEFF1011, 0xff4ffeb6}, -{12800000, DIF_BPF_COEFF1213, 0x013c01e1}, -{12800000, DIF_BPF_COEFF1415, 0xfdfbfd76}, -{12800000, DIF_BPF_COEFF1617, 0x03110337}, -{12800000, DIF_BPF_COEFF1819, 0xfb9dfc2a}, -{12800000, DIF_BPF_COEFF2021, 0x05f40453}, -{12800000, DIF_BPF_COEFF2223, 0xf848fb63}, -{12800000, DIF_BPF_COEFF2425, 0x099904a5}, -{12800000, DIF_BPF_COEFF2627, 0xf482fb9f}, -{12800000, DIF_BPF_COEFF2829, 0x0d4603ce}, -{12800000, DIF_BPF_COEFF3031, 0xf12dfd0c}, -{12800000, DIF_BPF_COEFF3233, 0x100701df}, -{12800000, DIF_BPF_COEFF3435, 0xef36ff5c}, -{12800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 128_quant.dat*/ - - -/*case 12900000:*/ -/* BEGIN - DIF BPF register values from 129_quant.dat*/ -{12900000, DIF_BPF_COEFF01, 0x00000003}, -{12900000, DIF_BPF_COEFF23, 0x0001ffee}, -{12900000, DIF_BPF_COEFF45, 0xffff0038}, -{12900000, DIF_BPF_COEFF67, 0xfffbff82}, -{12900000, DIF_BPF_COEFF89, 0x001d00ec}, -{12900000, DIF_BPF_COEFF1011, 0xffadfe7c}, -{12900000, DIF_BPF_COEFF1213, 0x00b70242}, -{12900000, DIF_BPF_COEFF1415, 0xfea9fce5}, -{12900000, DIF_BPF_COEFF1617, 0x024103ff}, -{12900000, DIF_BPF_COEFF1819, 0xfc85fb2a}, -{12900000, DIF_BPF_COEFF2021, 0x05040587}, -{12900000, DIF_BPF_COEFF2223, 0xf930fa0a}, -{12900000, DIF_BPF_COEFF2425, 0x08ca060e}, -{12900000, DIF_BPF_COEFF2627, 0xf52bfa40}, -{12900000, DIF_BPF_COEFF2829, 0x0ccb0507}, -{12900000, DIF_BPF_COEFF3031, 0xf179fc15}, -{12900000, DIF_BPF_COEFF3233, 0x0fe3027d}, -{12900000, DIF_BPF_COEFF3435, 0xef3fff25}, -{12900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 129_quant.dat*/ - - -/*case 113000000:*/ -/* BEGIN - DIF BPF register values from 130_quant.dat*/ -{13000000, DIF_BPF_COEFF01, 0x00000002}, -{13000000, DIF_BPF_COEFF23, 0x0005fff0}, -{13000000, DIF_BPF_COEFF45, 0xfff20034}, -{13000000, DIF_BPF_COEFF67, 0x001bff85}, -{13000000, DIF_BPF_COEFF89, 0xffdf00f0}, -{13000000, DIF_BPF_COEFF1011, 0x0014fe68}, -{13000000, DIF_BPF_COEFF1213, 0x00200272}, -{13000000, DIF_BPF_COEFF1415, 0xff71fc8b}, -{13000000, DIF_BPF_COEFF1617, 0x014d048d}, -{13000000, DIF_BPF_COEFF1819, 0xfd9afa61}, -{13000000, DIF_BPF_COEFF2021, 0x03e00688}, -{13000000, DIF_BPF_COEFF2223, 0xfa4ef8da}, -{13000000, DIF_BPF_COEFF2425, 0x07c80759}, -{13000000, DIF_BPF_COEFF2627, 0xf600f8f4}, -{13000000, DIF_BPF_COEFF2829, 0x0c2f0637}, -{13000000, DIF_BPF_COEFF3031, 0xf1dbfb22}, -{13000000, DIF_BPF_COEFF3233, 0x0fb4031b}, -{13000000, DIF_BPF_COEFF3435, 0xef4bfeef}, -{13000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 130_quant.dat*/ - - -/*case 13100000:*/ -/* BEGIN - DIF BPF register values from 131_quant.dat*/ -{13100000, DIF_BPF_COEFF01, 0xffff0001}, -{13100000, DIF_BPF_COEFF23, 0x0007fff5}, -{13100000, DIF_BPF_COEFF45, 0xffe70028}, -{13100000, DIF_BPF_COEFF67, 0x0037ff98}, -{13100000, DIF_BPF_COEFF89, 0xffa400d8}, -{13100000, DIF_BPF_COEFF1011, 0x0079fe7c}, -{13100000, DIF_BPF_COEFF1213, 0xff87026f}, -{13100000, DIF_BPF_COEFF1415, 0x0043fc6e}, -{13100000, DIF_BPF_COEFF1617, 0x004404da}, -{13100000, DIF_BPF_COEFF1819, 0xfecef9da}, -{13100000, DIF_BPF_COEFF2021, 0x0294074e}, -{13100000, DIF_BPF_COEFF2223, 0xfb99f7db}, -{13100000, DIF_BPF_COEFF2425, 0x06980881}, -{13100000, DIF_BPF_COEFF2627, 0xf6fef7be}, -{13100000, DIF_BPF_COEFF2829, 0x0b730759}, -{13100000, DIF_BPF_COEFF3031, 0xf251fa33}, -{13100000, DIF_BPF_COEFF3233, 0x0f7b03b8}, -{13100000, DIF_BPF_COEFF3435, 0xef5afeb8}, -{13100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 131_quant.dat*/ - - -/*case 13200000:*/ -/* BEGIN - DIF BPF register values from 132_quant.dat*/ -{13200000, DIF_BPF_COEFF01, 0xffff0000}, -{13200000, DIF_BPF_COEFF23, 0x0008fffc}, -{13200000, DIF_BPF_COEFF45, 0xffe00017}, -{13200000, DIF_BPF_COEFF67, 0x004cffb9}, -{13200000, DIF_BPF_COEFF89, 0xff7500a8}, -{13200000, DIF_BPF_COEFF1011, 0x00d1feb6}, -{13200000, DIF_BPF_COEFF1213, 0xfef90238}, -{13200000, DIF_BPF_COEFF1415, 0x0111fc91}, -{13200000, DIF_BPF_COEFF1617, 0xff3604df}, -{13200000, DIF_BPF_COEFF1819, 0x0012f99b}, -{13200000, DIF_BPF_COEFF2021, 0x012d07d2}, -{13200000, DIF_BPF_COEFF2223, 0xfd07f714}, -{13200000, DIF_BPF_COEFF2425, 0x0542097e}, -{13200000, DIF_BPF_COEFF2627, 0xf81ff6a4}, -{13200000, DIF_BPF_COEFF2829, 0x0a9a086e}, -{13200000, DIF_BPF_COEFF3031, 0xf2dbf94b}, -{13200000, DIF_BPF_COEFF3233, 0x0f380453}, -{13200000, DIF_BPF_COEFF3435, 0xef6cfe82}, -{13200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 132_quant.dat*/ - - -/*case 13300000:*/ -/* BEGIN - DIF BPF register values from 133_quant.dat*/ -{13300000, DIF_BPF_COEFF01, 0xffffffff}, -{13300000, DIF_BPF_COEFF23, 0x00080003}, -{13300000, DIF_BPF_COEFF45, 0xffde0001}, -{13300000, DIF_BPF_COEFF67, 0x0056ffe3}, -{13300000, DIF_BPF_COEFF89, 0xff570064}, -{13300000, DIF_BPF_COEFF1011, 0x0113ff10}, -{13300000, DIF_BPF_COEFF1213, 0xfe8201d2}, -{13300000, DIF_BPF_COEFF1415, 0x01cafcf0}, -{13300000, DIF_BPF_COEFF1617, 0xfe35049e}, -{13300000, DIF_BPF_COEFF1819, 0x0155f9a6}, -{13300000, DIF_BPF_COEFF2021, 0xffba080e}, -{13300000, DIF_BPF_COEFF2223, 0xfe8cf689}, -{13300000, DIF_BPF_COEFF2425, 0x03ce0a4e}, -{13300000, DIF_BPF_COEFF2627, 0xf961f5a8}, -{13300000, DIF_BPF_COEFF2829, 0x09a50971}, -{13300000, DIF_BPF_COEFF3031, 0xf379f869}, -{13300000, DIF_BPF_COEFF3233, 0x0eeb04ec}, -{13300000, DIF_BPF_COEFF3435, 0xef80fe4b}, -{13300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 133_quant.dat*/ - - -/*case 13400000:*/ -/* BEGIN - DIF BPF register values from 134_quant.dat*/ -{13400000, DIF_BPF_COEFF01, 0x0000fffe}, -{13400000, DIF_BPF_COEFF23, 0x0007000a}, -{13400000, DIF_BPF_COEFF45, 0xffe2ffec}, -{13400000, DIF_BPF_COEFF67, 0x00540012}, -{13400000, DIF_BPF_COEFF89, 0xff4e0015}, -{13400000, DIF_BPF_COEFF1011, 0x0137ff82}, -{13400000, DIF_BPF_COEFF1213, 0xfe2e0145}, -{13400000, DIF_BPF_COEFF1415, 0x0260fd86}, -{13400000, DIF_BPF_COEFF1617, 0xfd51041a}, -{13400000, DIF_BPF_COEFF1819, 0x0287f9fb}, -{13400000, DIF_BPF_COEFF2021, 0xfe4a0802}, -{13400000, DIF_BPF_COEFF2223, 0x001df63f}, -{13400000, DIF_BPF_COEFF2425, 0x02430aeb}, -{13400000, DIF_BPF_COEFF2627, 0xfabdf4ce}, -{13400000, DIF_BPF_COEFF2829, 0x08970a62}, -{13400000, DIF_BPF_COEFF3031, 0xf428f78f}, -{13400000, DIF_BPF_COEFF3233, 0x0e950584}, -{13400000, DIF_BPF_COEFF3435, 0xef97fe15}, -{13400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 134_quant.dat*/ - - -/*case 13500000:*/ -/* BEGIN - DIF BPF register values from 135_quant.dat*/ -{13500000, DIF_BPF_COEFF01, 0x0000fffd}, -{13500000, DIF_BPF_COEFF23, 0x0004000f}, -{13500000, DIF_BPF_COEFF45, 0xffeaffda}, -{13500000, DIF_BPF_COEFF67, 0x0046003d}, -{13500000, DIF_BPF_COEFF89, 0xff5affc4}, -{13500000, DIF_BPF_COEFF1011, 0x013b0000}, -{13500000, DIF_BPF_COEFF1213, 0xfe04009d}, -{13500000, DIF_BPF_COEFF1415, 0x02c8fe48}, -{13500000, DIF_BPF_COEFF1617, 0xfc99035a}, -{13500000, DIF_BPF_COEFF1819, 0x0397fa96}, -{13500000, DIF_BPF_COEFF2021, 0xfcec07ad}, -{13500000, DIF_BPF_COEFF2223, 0x01adf637}, -{13500000, DIF_BPF_COEFF2425, 0x00ac0b53}, -{13500000, DIF_BPF_COEFF2627, 0xfc2ef419}, -{13500000, DIF_BPF_COEFF2829, 0x07730b3e}, -{13500000, DIF_BPF_COEFF3031, 0xf4e9f6bd}, -{13500000, DIF_BPF_COEFF3233, 0x0e35061a}, -{13500000, DIF_BPF_COEFF3435, 0xefb1fddf}, -{13500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 135_quant.dat*/ - - -/*case 13600000:*/ -/* BEGIN - DIF BPF register values from 136_quant.dat*/ -{13600000, DIF_BPF_COEFF01, 0x0000fffd}, -{13600000, DIF_BPF_COEFF23, 0x00000012}, -{13600000, DIF_BPF_COEFF45, 0xfff6ffcd}, -{13600000, DIF_BPF_COEFF67, 0x002f0061}, -{13600000, DIF_BPF_COEFF89, 0xff7bff79}, -{13600000, DIF_BPF_COEFF1011, 0x011e007e}, -{13600000, DIF_BPF_COEFF1213, 0xfe08ffe8}, -{13600000, DIF_BPF_COEFF1415, 0x02f9ff28}, -{13600000, DIF_BPF_COEFF1617, 0xfc17026a}, -{13600000, DIF_BPF_COEFF1819, 0x0479fb70}, -{13600000, DIF_BPF_COEFF2021, 0xfbad0713}, -{13600000, DIF_BPF_COEFF2223, 0x032ff672}, -{13600000, DIF_BPF_COEFF2425, 0xff100b83}, -{13600000, DIF_BPF_COEFF2627, 0xfdaff38b}, -{13600000, DIF_BPF_COEFF2829, 0x063c0c04}, -{13600000, DIF_BPF_COEFF3031, 0xf5baf5f5}, -{13600000, DIF_BPF_COEFF3233, 0x0dcc06ae}, -{13600000, DIF_BPF_COEFF3435, 0xefcdfda8}, -{13600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 136_quant.dat*/ - - -/*case 13700000:*/ -/* BEGIN - DIF BPF register values from 137_quant.dat*/ -{13700000, DIF_BPF_COEFF01, 0x0000fffd}, -{13700000, DIF_BPF_COEFF23, 0xfffd0012}, -{13700000, DIF_BPF_COEFF45, 0x0004ffc8}, -{13700000, DIF_BPF_COEFF67, 0x00100078}, -{13700000, DIF_BPF_COEFF89, 0xffacff3e}, -{13700000, DIF_BPF_COEFF1011, 0x00e200f0}, -{13700000, DIF_BPF_COEFF1213, 0xfe39ff35}, -{13700000, DIF_BPF_COEFF1415, 0x02f10017}, -{13700000, DIF_BPF_COEFF1617, 0xfbd30156}, -{13700000, DIF_BPF_COEFF1819, 0x0521fc7f}, -{13700000, DIF_BPF_COEFF2021, 0xfa9c0638}, -{13700000, DIF_BPF_COEFF2223, 0x0499f6ee}, -{13700000, DIF_BPF_COEFF2425, 0xfd7a0b7c}, -{13700000, DIF_BPF_COEFF2627, 0xff39f325}, -{13700000, DIF_BPF_COEFF2829, 0x04f40cb3}, -{13700000, DIF_BPF_COEFF3031, 0xf69af537}, -{13700000, DIF_BPF_COEFF3233, 0x0d5a073f}, -{13700000, DIF_BPF_COEFF3435, 0xefecfd72}, -{13700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 137_quant.dat*/ - - -/*case 13800000:*/ -/* BEGIN - DIF BPF register values from 138_quant.dat*/ -{13800000, DIF_BPF_COEFF01, 0x0001fffe}, -{13800000, DIF_BPF_COEFF23, 0xfffa000e}, -{13800000, DIF_BPF_COEFF45, 0x0011ffcb}, -{13800000, DIF_BPF_COEFF67, 0xfff0007f}, -{13800000, DIF_BPF_COEFF89, 0xffe7ff19}, -{13800000, DIF_BPF_COEFF1011, 0x008f014a}, -{13800000, DIF_BPF_COEFF1213, 0xfe94fe93}, -{13800000, DIF_BPF_COEFF1415, 0x02b00105}, -{13800000, DIF_BPF_COEFF1617, 0xfbd3002f}, -{13800000, DIF_BPF_COEFF1819, 0x0585fdb7}, -{13800000, DIF_BPF_COEFF2021, 0xf9c10525}, -{13800000, DIF_BPF_COEFF2223, 0x05def7a8}, -{13800000, DIF_BPF_COEFF2425, 0xfbf20b3c}, -{13800000, DIF_BPF_COEFF2627, 0x00c7f2e9}, -{13800000, DIF_BPF_COEFF2829, 0x03a00d48}, -{13800000, DIF_BPF_COEFF3031, 0xf787f484}, -{13800000, DIF_BPF_COEFF3233, 0x0cdf07cd}, -{13800000, DIF_BPF_COEFF3435, 0xf00dfd3c}, -{13800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 138_quant.dat*/ - - -/*case 13900000:*/ -/* BEGIN - DIF BPF register values from 139_quant.dat*/ -{13900000, DIF_BPF_COEFF01, 0x00010000}, -{13900000, DIF_BPF_COEFF23, 0xfff80008}, -{13900000, DIF_BPF_COEFF45, 0x001bffd7}, -{13900000, DIF_BPF_COEFF67, 0xffd10076}, -{13900000, DIF_BPF_COEFF89, 0x0026ff0e}, -{13900000, DIF_BPF_COEFF1011, 0x002c0184}, -{13900000, DIF_BPF_COEFF1213, 0xff0ffe10}, -{13900000, DIF_BPF_COEFF1415, 0x023b01e0}, -{13900000, DIF_BPF_COEFF1617, 0xfc17ff06}, -{13900000, DIF_BPF_COEFF1819, 0x05a2ff09}, -{13900000, DIF_BPF_COEFF2021, 0xf92703e4}, -{13900000, DIF_BPF_COEFF2223, 0x06f4f89b}, -{13900000, DIF_BPF_COEFF2425, 0xfa820ac5}, -{13900000, DIF_BPF_COEFF2627, 0x0251f2d9}, -{13900000, DIF_BPF_COEFF2829, 0x02430dc3}, -{13900000, DIF_BPF_COEFF3031, 0xf881f3dc}, -{13900000, DIF_BPF_COEFF3233, 0x0c5c0859}, -{13900000, DIF_BPF_COEFF3435, 0xf031fd06}, -{13900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 139_quant.dat*/ - - -/*case 14000000:*/ -/* BEGIN - DIF BPF register values from 140_quant.dat*/ -{14000000, DIF_BPF_COEFF01, 0x00010001}, -{14000000, DIF_BPF_COEFF23, 0xfff80001}, -{14000000, DIF_BPF_COEFF45, 0x0021ffe8}, -{14000000, DIF_BPF_COEFF67, 0xffba005d}, -{14000000, DIF_BPF_COEFF89, 0x0060ff1f}, -{14000000, DIF_BPF_COEFF1011, 0xffc40198}, -{14000000, DIF_BPF_COEFF1213, 0xffa0fdb5}, -{14000000, DIF_BPF_COEFF1415, 0x019a029a}, -{14000000, DIF_BPF_COEFF1617, 0xfc99fdea}, -{14000000, DIF_BPF_COEFF1819, 0x05750067}, -{14000000, DIF_BPF_COEFF2021, 0xf8d4027f}, -{14000000, DIF_BPF_COEFF2223, 0x07d4f9c0}, -{14000000, DIF_BPF_COEFF2425, 0xf9320a1a}, -{14000000, DIF_BPF_COEFF2627, 0x03d2f2f3}, -{14000000, DIF_BPF_COEFF2829, 0x00df0e22}, -{14000000, DIF_BPF_COEFF3031, 0xf986f341}, -{14000000, DIF_BPF_COEFF3233, 0x0bd108e2}, -{14000000, DIF_BPF_COEFF3435, 0xf058fcd1}, -{14000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 140_quant.dat*/ - - -/*case 14100000:*/ -/* BEGIN - DIF BPF register values from 141_quant.dat*/ -{14100000, DIF_BPF_COEFF01, 0x00000002}, -{14100000, DIF_BPF_COEFF23, 0xfff9fffa}, -{14100000, DIF_BPF_COEFF45, 0x0021fffd}, -{14100000, DIF_BPF_COEFF67, 0xffac0038}, -{14100000, DIF_BPF_COEFF89, 0x008eff4a}, -{14100000, DIF_BPF_COEFF1011, 0xff630184}, -{14100000, DIF_BPF_COEFF1213, 0x003afd8b}, -{14100000, DIF_BPF_COEFF1415, 0x00da0326}, -{14100000, DIF_BPF_COEFF1617, 0xfd51fced}, -{14100000, DIF_BPF_COEFF1819, 0x050101c0}, -{14100000, DIF_BPF_COEFF2021, 0xf8cb0103}, -{14100000, DIF_BPF_COEFF2223, 0x0876fb10}, -{14100000, DIF_BPF_COEFF2425, 0xf80a093e}, -{14100000, DIF_BPF_COEFF2627, 0x0543f338}, -{14100000, DIF_BPF_COEFF2829, 0xff7a0e66}, -{14100000, DIF_BPF_COEFF3031, 0xfa94f2b2}, -{14100000, DIF_BPF_COEFF3233, 0x0b3f0967}, -{14100000, DIF_BPF_COEFF3435, 0xf081fc9b}, -{14100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 141_quant.dat*/ - - -/*case 14200000:*/ -/* BEGIN - DIF BPF register values from 142_quant.dat*/ -{14200000, DIF_BPF_COEFF01, 0x00000003}, -{14200000, DIF_BPF_COEFF23, 0xfffbfff3}, -{14200000, DIF_BPF_COEFF45, 0x001d0013}, -{14200000, DIF_BPF_COEFF67, 0xffaa000b}, -{14200000, DIF_BPF_COEFF89, 0x00aaff89}, -{14200000, DIF_BPF_COEFF1011, 0xff13014a}, -{14200000, DIF_BPF_COEFF1213, 0x00cefd95}, -{14200000, DIF_BPF_COEFF1415, 0x000a037b}, -{14200000, DIF_BPF_COEFF1617, 0xfe35fc1d}, -{14200000, DIF_BPF_COEFF1819, 0x044c0305}, -{14200000, DIF_BPF_COEFF2021, 0xf90cff7e}, -{14200000, DIF_BPF_COEFF2223, 0x08d5fc81}, -{14200000, DIF_BPF_COEFF2425, 0xf7100834}, -{14200000, DIF_BPF_COEFF2627, 0x069ff3a7}, -{14200000, DIF_BPF_COEFF2829, 0xfe160e8d}, -{14200000, DIF_BPF_COEFF3031, 0xfbaaf231}, -{14200000, DIF_BPF_COEFF3233, 0x0aa509e9}, -{14200000, DIF_BPF_COEFF3435, 0xf0adfc65}, -{14200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 142_quant.dat*/ - - -/*case 14300000:*/ -/* BEGIN - DIF BPF register values from 143_quant.dat*/ -{14300000, DIF_BPF_COEFF01, 0x00000003}, -{14300000, DIF_BPF_COEFF23, 0xffffffef}, -{14300000, DIF_BPF_COEFF45, 0x00140025}, -{14300000, DIF_BPF_COEFF67, 0xffb4ffdd}, -{14300000, DIF_BPF_COEFF89, 0x00b2ffd6}, -{14300000, DIF_BPF_COEFF1011, 0xfedb00f0}, -{14300000, DIF_BPF_COEFF1213, 0x0150fdd3}, -{14300000, DIF_BPF_COEFF1415, 0xff380391}, -{14300000, DIF_BPF_COEFF1617, 0xff36fb85}, -{14300000, DIF_BPF_COEFF1819, 0x035e0426}, -{14300000, DIF_BPF_COEFF2021, 0xf994fdfe}, -{14300000, DIF_BPF_COEFF2223, 0x08eefe0b}, -{14300000, DIF_BPF_COEFF2425, 0xf6490702}, -{14300000, DIF_BPF_COEFF2627, 0x07e1f43e}, -{14300000, DIF_BPF_COEFF2829, 0xfcb60e97}, -{14300000, DIF_BPF_COEFF3031, 0xfcc6f1be}, -{14300000, DIF_BPF_COEFF3233, 0x0a040a67}, -{14300000, DIF_BPF_COEFF3435, 0xf0dbfc30}, -{14300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 143_quant.dat*/ - - -/*case 14400000:*/ -/* BEGIN - DIF BPF register values from 144_quant.dat*/ -{14400000, DIF_BPF_COEFF01, 0x00000003}, -{14400000, DIF_BPF_COEFF23, 0x0002ffee}, -{14400000, DIF_BPF_COEFF45, 0x00070033}, -{14400000, DIF_BPF_COEFF67, 0xffc9ffb4}, -{14400000, DIF_BPF_COEFF89, 0x00a40027}, -{14400000, DIF_BPF_COEFF1011, 0xfec3007e}, -{14400000, DIF_BPF_COEFF1213, 0x01b4fe3f}, -{14400000, DIF_BPF_COEFF1415, 0xfe760369}, -{14400000, DIF_BPF_COEFF1617, 0x0044fb2e}, -{14400000, DIF_BPF_COEFF1819, 0x02450518}, -{14400000, DIF_BPF_COEFF2021, 0xfa5ffc90}, -{14400000, DIF_BPF_COEFF2223, 0x08c1ffa1}, -{14400000, DIF_BPF_COEFF2425, 0xf5bc05ae}, -{14400000, DIF_BPF_COEFF2627, 0x0902f4fc}, -{14400000, DIF_BPF_COEFF2829, 0xfb600e85}, -{14400000, DIF_BPF_COEFF3031, 0xfde7f15a}, -{14400000, DIF_BPF_COEFF3233, 0x095d0ae2}, -{14400000, DIF_BPF_COEFF3435, 0xf10cfbfb}, -{14400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 144_quant.dat*/ - - -/*case 14500000:*/ -/* BEGIN - DIF BPF register values from 145_quant.dat*/ -{14500000, DIF_BPF_COEFF01, 0xffff0002}, -{14500000, DIF_BPF_COEFF23, 0x0005ffef}, -{14500000, DIF_BPF_COEFF45, 0xfffa0038}, -{14500000, DIF_BPF_COEFF67, 0xffe5ff95}, -{14500000, DIF_BPF_COEFF89, 0x00820074}, -{14500000, DIF_BPF_COEFF1011, 0xfecc0000}, -{14500000, DIF_BPF_COEFF1213, 0x01f0fed0}, -{14500000, DIF_BPF_COEFF1415, 0xfdd20304}, -{14500000, DIF_BPF_COEFF1617, 0x014dfb1d}, -{14500000, DIF_BPF_COEFF1819, 0x010e05ce}, -{14500000, DIF_BPF_COEFF2021, 0xfb64fb41}, -{14500000, DIF_BPF_COEFF2223, 0x084e013b}, -{14500000, DIF_BPF_COEFF2425, 0xf569043e}, -{14500000, DIF_BPF_COEFF2627, 0x0a00f5dd}, -{14500000, DIF_BPF_COEFF2829, 0xfa150e55}, -{14500000, DIF_BPF_COEFF3031, 0xff0bf104}, -{14500000, DIF_BPF_COEFF3233, 0x08b00b59}, -{14500000, DIF_BPF_COEFF3435, 0xf13ffbc6}, -{14500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 145_quant.dat*/ - - -/*case 14600000:*/ -/* BEGIN - DIF BPF register values from 146_quant.dat*/ -{14600000, DIF_BPF_COEFF01, 0xffff0001}, -{14600000, DIF_BPF_COEFF23, 0x0008fff4}, -{14600000, DIF_BPF_COEFF45, 0xffed0035}, -{14600000, DIF_BPF_COEFF67, 0x0005ff83}, -{14600000, DIF_BPF_COEFF89, 0x005000b4}, -{14600000, DIF_BPF_COEFF1011, 0xfef6ff82}, -{14600000, DIF_BPF_COEFF1213, 0x01ffff7a}, -{14600000, DIF_BPF_COEFF1415, 0xfd580269}, -{14600000, DIF_BPF_COEFF1617, 0x0241fb53}, -{14600000, DIF_BPF_COEFF1819, 0xffca0640}, -{14600000, DIF_BPF_COEFF2021, 0xfc99fa1e}, -{14600000, DIF_BPF_COEFF2223, 0x079a02cb}, -{14600000, DIF_BPF_COEFF2425, 0xf55502ba}, -{14600000, DIF_BPF_COEFF2627, 0x0ad5f6e0}, -{14600000, DIF_BPF_COEFF2829, 0xf8d90e0a}, -{14600000, DIF_BPF_COEFF3031, 0x0031f0bd}, -{14600000, DIF_BPF_COEFF3233, 0x07fd0bcb}, -{14600000, DIF_BPF_COEFF3435, 0xf174fb91}, -{14600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 146_quant.dat*/ - - -/*case 14700000:*/ -/* BEGIN - DIF BPF register values from 147_quant.dat*/ -{14700000, DIF_BPF_COEFF01, 0xffffffff}, -{14700000, DIF_BPF_COEFF23, 0x0009fffb}, -{14700000, DIF_BPF_COEFF45, 0xffe4002a}, -{14700000, DIF_BPF_COEFF67, 0x0025ff82}, -{14700000, DIF_BPF_COEFF89, 0x001400e0}, -{14700000, DIF_BPF_COEFF1011, 0xff3cff10}, -{14700000, DIF_BPF_COEFF1213, 0x01e10030}, -{14700000, DIF_BPF_COEFF1415, 0xfd1201a4}, -{14700000, DIF_BPF_COEFF1617, 0x0311fbcd}, -{14700000, DIF_BPF_COEFF1819, 0xfe88066a}, -{14700000, DIF_BPF_COEFF2021, 0xfdf1f92f}, -{14700000, DIF_BPF_COEFF2223, 0x06aa0449}, -{14700000, DIF_BPF_COEFF2425, 0xf57e0128}, -{14700000, DIF_BPF_COEFF2627, 0x0b7ef801}, -{14700000, DIF_BPF_COEFF2829, 0xf7b00da2}, -{14700000, DIF_BPF_COEFF3031, 0x0156f086}, -{14700000, DIF_BPF_COEFF3233, 0x07450c39}, -{14700000, DIF_BPF_COEFF3435, 0xf1acfb5c}, -{14700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 147_quant.dat*/ - - -/*case 14800000:*/ -/* BEGIN - DIF BPF register values from 148_quant.dat*/ -{14800000, DIF_BPF_COEFF01, 0x0000fffe}, -{14800000, DIF_BPF_COEFF23, 0x00080002}, -{14800000, DIF_BPF_COEFF45, 0xffdf0019}, -{14800000, DIF_BPF_COEFF67, 0x003fff92}, -{14800000, DIF_BPF_COEFF89, 0xffd600f1}, -{14800000, DIF_BPF_COEFF1011, 0xff96feb6}, -{14800000, DIF_BPF_COEFF1213, 0x019700e1}, -{14800000, DIF_BPF_COEFF1415, 0xfd0500c2}, -{14800000, DIF_BPF_COEFF1617, 0x03b0fc84}, -{14800000, DIF_BPF_COEFF1819, 0xfd590649}, -{14800000, DIF_BPF_COEFF2021, 0xff5df87f}, -{14800000, DIF_BPF_COEFF2223, 0x058505aa}, -{14800000, DIF_BPF_COEFF2425, 0xf5e4ff91}, -{14800000, DIF_BPF_COEFF2627, 0x0bf9f93c}, -{14800000, DIF_BPF_COEFF2829, 0xf69d0d20}, -{14800000, DIF_BPF_COEFF3031, 0x0279f05e}, -{14800000, DIF_BPF_COEFF3233, 0x06880ca3}, -{14800000, DIF_BPF_COEFF3435, 0xf1e6fb28}, -{14800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 148_quant.dat*/ - - -/*case 14900000:*/ -/* BEGIN - DIF BPF register values from 149_quant.dat*/ -{14900000, DIF_BPF_COEFF01, 0x0000fffd}, -{14900000, DIF_BPF_COEFF23, 0x00060009}, -{14900000, DIF_BPF_COEFF45, 0xffdf0004}, -{14900000, DIF_BPF_COEFF67, 0x0051ffb0}, -{14900000, DIF_BPF_COEFF89, 0xff9d00e8}, -{14900000, DIF_BPF_COEFF1011, 0xfffcfe7c}, -{14900000, DIF_BPF_COEFF1213, 0x01280180}, -{14900000, DIF_BPF_COEFF1415, 0xfd32ffd2}, -{14900000, DIF_BPF_COEFF1617, 0x0413fd6e}, -{14900000, DIF_BPF_COEFF1819, 0xfc4d05df}, -{14900000, DIF_BPF_COEFF2021, 0x00d1f812}, -{14900000, DIF_BPF_COEFF2223, 0x043506e4}, -{14900000, DIF_BPF_COEFF2425, 0xf685fdfb}, -{14900000, DIF_BPF_COEFF2627, 0x0c43fa8d}, -{14900000, DIF_BPF_COEFF2829, 0xf5a10c83}, -{14900000, DIF_BPF_COEFF3031, 0x0399f046}, -{14900000, DIF_BPF_COEFF3233, 0x05c70d08}, -{14900000, DIF_BPF_COEFF3435, 0xf222faf3}, -{14900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 149_quant.dat*/ - - -/*case 15000000:*/ -/* BEGIN - DIF BPF register values from 150_quant.dat*/ -{15000000, DIF_BPF_COEFF01, 0x0000fffd}, -{15000000, DIF_BPF_COEFF23, 0x0003000f}, -{15000000, DIF_BPF_COEFF45, 0xffe5ffef}, -{15000000, DIF_BPF_COEFF67, 0x0057ffd9}, -{15000000, DIF_BPF_COEFF89, 0xff7000c4}, -{15000000, DIF_BPF_COEFF1011, 0x0062fe68}, -{15000000, DIF_BPF_COEFF1213, 0x009e01ff}, -{15000000, DIF_BPF_COEFF1415, 0xfd95fee6}, -{15000000, DIF_BPF_COEFF1617, 0x0435fe7d}, -{15000000, DIF_BPF_COEFF1819, 0xfb710530}, -{15000000, DIF_BPF_COEFF2021, 0x023cf7ee}, -{15000000, DIF_BPF_COEFF2223, 0x02c307ef}, -{15000000, DIF_BPF_COEFF2425, 0xf75efc70}, -{15000000, DIF_BPF_COEFF2627, 0x0c5cfbef}, -{15000000, DIF_BPF_COEFF2829, 0xf4c10bce}, -{15000000, DIF_BPF_COEFF3031, 0x04b3f03f}, -{15000000, DIF_BPF_COEFF3233, 0x05030d69}, -{15000000, DIF_BPF_COEFF3435, 0xf261fabf}, -{15000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 150_quant.dat*/ - - -/*case 15100000:*/ -/* BEGIN - DIF BPF register values from 151_quant.dat*/ -{15100000, DIF_BPF_COEFF01, 0x0000fffd}, -{15100000, DIF_BPF_COEFF23, 0xffff0012}, -{15100000, DIF_BPF_COEFF45, 0xffefffdc}, -{15100000, DIF_BPF_COEFF67, 0x00510006}, -{15100000, DIF_BPF_COEFF89, 0xff540089}, -{15100000, DIF_BPF_COEFF1011, 0x00befe7c}, -{15100000, DIF_BPF_COEFF1213, 0x00060253}, -{15100000, DIF_BPF_COEFF1415, 0xfe27fe0d}, -{15100000, DIF_BPF_COEFF1617, 0x0413ffa2}, -{15100000, DIF_BPF_COEFF1819, 0xfad10446}, -{15100000, DIF_BPF_COEFF2021, 0x0390f812}, -{15100000, DIF_BPF_COEFF2223, 0x013b08c3}, -{15100000, DIF_BPF_COEFF2425, 0xf868faf6}, -{15100000, DIF_BPF_COEFF2627, 0x0c43fd5f}, -{15100000, DIF_BPF_COEFF2829, 0xf3fd0b02}, -{15100000, DIF_BPF_COEFF3031, 0x05c7f046}, -{15100000, DIF_BPF_COEFF3233, 0x043b0dc4}, -{15100000, DIF_BPF_COEFF3435, 0xf2a1fa8b}, -{15100000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 151_quant.dat*/ - - -/*case 15200000:*/ -/* BEGIN - DIF BPF register values from 152_quant.dat*/ -{15200000, DIF_BPF_COEFF01, 0x0001fffe}, -{15200000, DIF_BPF_COEFF23, 0xfffc0012}, -{15200000, DIF_BPF_COEFF45, 0xfffbffce}, -{15200000, DIF_BPF_COEFF67, 0x003f0033}, -{15200000, DIF_BPF_COEFF89, 0xff4e003f}, -{15200000, DIF_BPF_COEFF1011, 0x0106feb6}, -{15200000, DIF_BPF_COEFF1213, 0xff6e0276}, -{15200000, DIF_BPF_COEFF1415, 0xfeddfd56}, -{15200000, DIF_BPF_COEFF1617, 0x03b000cc}, -{15200000, DIF_BPF_COEFF1819, 0xfa740329}, -{15200000, DIF_BPF_COEFF2021, 0x04bff87f}, -{15200000, DIF_BPF_COEFF2223, 0xffaa095d}, -{15200000, DIF_BPF_COEFF2425, 0xf99ef995}, -{15200000, DIF_BPF_COEFF2627, 0x0bf9fed8}, -{15200000, DIF_BPF_COEFF2829, 0xf3590a1f}, -{15200000, DIF_BPF_COEFF3031, 0x06d2f05e}, -{15200000, DIF_BPF_COEFF3233, 0x03700e1b}, -{15200000, DIF_BPF_COEFF3435, 0xf2e4fa58}, -{15200000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 152_quant.dat*/ - - -/*case 115300000:*/ -/* BEGIN - DIF BPF register values from 153_quant.dat*/ -{15300000, DIF_BPF_COEFF01, 0x0001ffff}, -{15300000, DIF_BPF_COEFF23, 0xfff9000f}, -{15300000, DIF_BPF_COEFF45, 0x0009ffc8}, -{15300000, DIF_BPF_COEFF67, 0x00250059}, -{15300000, DIF_BPF_COEFF89, 0xff5effee}, -{15300000, DIF_BPF_COEFF1011, 0x0132ff10}, -{15300000, DIF_BPF_COEFF1213, 0xfee30265}, -{15300000, DIF_BPF_COEFF1415, 0xffaafccf}, -{15300000, DIF_BPF_COEFF1617, 0x031101eb}, -{15300000, DIF_BPF_COEFF1819, 0xfa6001e8}, -{15300000, DIF_BPF_COEFF2021, 0x05bdf92f}, -{15300000, DIF_BPF_COEFF2223, 0xfe1b09b6}, -{15300000, DIF_BPF_COEFF2425, 0xfafaf852}, -{15300000, DIF_BPF_COEFF2627, 0x0b7e0055}, -{15300000, DIF_BPF_COEFF2829, 0xf2d50929}, -{15300000, DIF_BPF_COEFF3031, 0x07d3f086}, -{15300000, DIF_BPF_COEFF3233, 0x02a30e6c}, -{15300000, DIF_BPF_COEFF3435, 0xf329fa24}, -{15300000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 153_quant.dat*/ - - -/*case 115400000:*/ -/* BEGIN - DIF BPF register values from 154_quant.dat*/ -{15400000, DIF_BPF_COEFF01, 0x00010001}, -{15400000, DIF_BPF_COEFF23, 0xfff80009}, -{15400000, DIF_BPF_COEFF45, 0x0015ffca}, -{15400000, DIF_BPF_COEFF67, 0x00050074}, -{15400000, DIF_BPF_COEFF89, 0xff81ff9f}, -{15400000, DIF_BPF_COEFF1011, 0x013dff82}, -{15400000, DIF_BPF_COEFF1213, 0xfe710221}, -{15400000, DIF_BPF_COEFF1415, 0x007cfc80}, -{15400000, DIF_BPF_COEFF1617, 0x024102ed}, -{15400000, DIF_BPF_COEFF1819, 0xfa940090}, -{15400000, DIF_BPF_COEFF2021, 0x0680fa1e}, -{15400000, DIF_BPF_COEFF2223, 0xfc9b09cd}, -{15400000, DIF_BPF_COEFF2425, 0xfc73f736}, -{15400000, DIF_BPF_COEFF2627, 0x0ad501d0}, -{15400000, DIF_BPF_COEFF2829, 0xf2740820}, -{15400000, DIF_BPF_COEFF3031, 0x08c9f0bd}, -{15400000, DIF_BPF_COEFF3233, 0x01d40eb9}, -{15400000, DIF_BPF_COEFF3435, 0xf371f9f1}, -{15400000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 154_quant.dat*/ - - -/*case 115500000:*/ -/* BEGIN - DIF BPF register values from 155_quant.dat*/ -{15500000, DIF_BPF_COEFF01, 0x00000002}, -{15500000, DIF_BPF_COEFF23, 0xfff80002}, -{15500000, DIF_BPF_COEFF45, 0x001effd5}, -{15500000, DIF_BPF_COEFF67, 0xffe5007f}, -{15500000, DIF_BPF_COEFF89, 0xffb4ff5b}, -{15500000, DIF_BPF_COEFF1011, 0x01280000}, -{15500000, DIF_BPF_COEFF1213, 0xfe2401b0}, -{15500000, DIF_BPF_COEFF1415, 0x0146fc70}, -{15500000, DIF_BPF_COEFF1617, 0x014d03c6}, -{15500000, DIF_BPF_COEFF1819, 0xfb10ff32}, -{15500000, DIF_BPF_COEFF2021, 0x0701fb41}, -{15500000, DIF_BPF_COEFF2223, 0xfb3709a1}, -{15500000, DIF_BPF_COEFF2425, 0xfe00f644}, -{15500000, DIF_BPF_COEFF2627, 0x0a000345}, -{15500000, DIF_BPF_COEFF2829, 0xf2350708}, -{15500000, DIF_BPF_COEFF3031, 0x09b2f104}, -{15500000, DIF_BPF_COEFF3233, 0x01050eff}, -{15500000, DIF_BPF_COEFF3435, 0xf3baf9be}, -{15500000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 155_quant.dat*/ - - -/*case 115600000:*/ -/* BEGIN - DIF BPF register values from 156_quant.dat*/ -{15600000, DIF_BPF_COEFF01, 0x00000003}, -{15600000, DIF_BPF_COEFF23, 0xfff9fffb}, -{15600000, DIF_BPF_COEFF45, 0x0022ffe6}, -{15600000, DIF_BPF_COEFF67, 0xffc9007a}, -{15600000, DIF_BPF_COEFF89, 0xfff0ff29}, -{15600000, DIF_BPF_COEFF1011, 0x00f2007e}, -{15600000, DIF_BPF_COEFF1213, 0xfe01011b}, -{15600000, DIF_BPF_COEFF1415, 0x01f6fc9e}, -{15600000, DIF_BPF_COEFF1617, 0x00440467}, -{15600000, DIF_BPF_COEFF1819, 0xfbccfdde}, -{15600000, DIF_BPF_COEFF2021, 0x0738fc90}, -{15600000, DIF_BPF_COEFF2223, 0xf9f70934}, -{15600000, DIF_BPF_COEFF2425, 0xff99f582}, -{15600000, DIF_BPF_COEFF2627, 0x090204b0}, -{15600000, DIF_BPF_COEFF2829, 0xf21a05e1}, -{15600000, DIF_BPF_COEFF3031, 0x0a8df15a}, -{15600000, DIF_BPF_COEFF3233, 0x00340f41}, -{15600000, DIF_BPF_COEFF3435, 0xf405f98b}, -{15600000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 156_quant.dat*/ - - -/*case 115700000:*/ -/* BEGIN - DIF BPF register values from 157_quant.dat*/ -{15700000, DIF_BPF_COEFF01, 0x00000003}, -{15700000, DIF_BPF_COEFF23, 0xfffcfff4}, -{15700000, DIF_BPF_COEFF45, 0x0020fffa}, -{15700000, DIF_BPF_COEFF67, 0xffb40064}, -{15700000, DIF_BPF_COEFF89, 0x002fff11}, -{15700000, DIF_BPF_COEFF1011, 0x00a400f0}, -{15700000, DIF_BPF_COEFF1213, 0xfe0d006e}, -{15700000, DIF_BPF_COEFF1415, 0x0281fd09}, -{15700000, DIF_BPF_COEFF1617, 0xff3604c9}, -{15700000, DIF_BPF_COEFF1819, 0xfcbffca2}, -{15700000, DIF_BPF_COEFF2021, 0x0726fdfe}, -{15700000, DIF_BPF_COEFF2223, 0xf8e80888}, -{15700000, DIF_BPF_COEFF2425, 0x0134f4f3}, -{15700000, DIF_BPF_COEFF2627, 0x07e1060c}, -{15700000, DIF_BPF_COEFF2829, 0xf22304af}, -{15700000, DIF_BPF_COEFF3031, 0x0b59f1be}, -{15700000, DIF_BPF_COEFF3233, 0xff640f7d}, -{15700000, DIF_BPF_COEFF3435, 0xf452f959}, -{15700000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 157_quant.dat*/ - - -/*case 115800000:*/ -/* BEGIN - DIF BPF register values from 158_quant.dat*/ -{15800000, DIF_BPF_COEFF01, 0x00000003}, -{15800000, DIF_BPF_COEFF23, 0x0000fff0}, -{15800000, DIF_BPF_COEFF45, 0x001a0010}, -{15800000, DIF_BPF_COEFF67, 0xffaa0041}, -{15800000, DIF_BPF_COEFF89, 0x0067ff13}, -{15800000, DIF_BPF_COEFF1011, 0x0043014a}, -{15800000, DIF_BPF_COEFF1213, 0xfe46ffb9}, -{15800000, DIF_BPF_COEFF1415, 0x02dbfda8}, -{15800000, DIF_BPF_COEFF1617, 0xfe3504e5}, -{15800000, DIF_BPF_COEFF1819, 0xfddcfb8d}, -{15800000, DIF_BPF_COEFF2021, 0x06c9ff7e}, -{15800000, DIF_BPF_COEFF2223, 0xf81107a2}, -{15800000, DIF_BPF_COEFF2425, 0x02c9f49a}, -{15800000, DIF_BPF_COEFF2627, 0x069f0753}, -{15800000, DIF_BPF_COEFF2829, 0xf2500373}, -{15800000, DIF_BPF_COEFF3031, 0x0c14f231}, -{15800000, DIF_BPF_COEFF3233, 0xfe930fb3}, -{15800000, DIF_BPF_COEFF3435, 0xf4a1f927}, -{15800000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 158_quant.dat*/ - - -/*case 115900000:*/ -/* BEGIN - DIF BPF register values from 159_quant.dat*/ -{15900000, DIF_BPF_COEFF01, 0xffff0002}, -{15900000, DIF_BPF_COEFF23, 0x0003ffee}, -{15900000, DIF_BPF_COEFF45, 0x000f0023}, -{15900000, DIF_BPF_COEFF67, 0xffac0016}, -{15900000, DIF_BPF_COEFF89, 0x0093ff31}, -{15900000, DIF_BPF_COEFF1011, 0xffdc0184}, -{15900000, DIF_BPF_COEFF1213, 0xfea6ff09}, -{15900000, DIF_BPF_COEFF1415, 0x02fdfe70}, -{15900000, DIF_BPF_COEFF1617, 0xfd5104ba}, -{15900000, DIF_BPF_COEFF1819, 0xff15faac}, -{15900000, DIF_BPF_COEFF2021, 0x06270103}, -{15900000, DIF_BPF_COEFF2223, 0xf7780688}, -{15900000, DIF_BPF_COEFF2425, 0x044df479}, -{15900000, DIF_BPF_COEFF2627, 0x05430883}, -{15900000, DIF_BPF_COEFF2829, 0xf2a00231}, -{15900000, DIF_BPF_COEFF3031, 0x0cbef2b2}, -{15900000, DIF_BPF_COEFF3233, 0xfdc40fe3}, -{15900000, DIF_BPF_COEFF3435, 0xf4f2f8f5}, -{15900000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 159_quant.dat*/ - - -/*case 116000000:*/ -/* BEGIN - DIF BPF register values from 160_quant.dat*/ -{16000000, DIF_BPF_COEFF01, 0xffff0001}, -{16000000, DIF_BPF_COEFF23, 0x0006ffef}, -{16000000, DIF_BPF_COEFF45, 0x00020031}, -{16000000, DIF_BPF_COEFF67, 0xffbaffe8}, -{16000000, DIF_BPF_COEFF89, 0x00adff66}, -{16000000, DIF_BPF_COEFF1011, 0xff790198}, -{16000000, DIF_BPF_COEFF1213, 0xff26fe6e}, -{16000000, DIF_BPF_COEFF1415, 0x02e5ff55}, -{16000000, DIF_BPF_COEFF1617, 0xfc99044a}, -{16000000, DIF_BPF_COEFF1819, 0x005bfa09}, -{16000000, DIF_BPF_COEFF2021, 0x0545027f}, -{16000000, DIF_BPF_COEFF2223, 0xf7230541}, -{16000000, DIF_BPF_COEFF2425, 0x05b8f490}, -{16000000, DIF_BPF_COEFF2627, 0x03d20997}, -{16000000, DIF_BPF_COEFF2829, 0xf31300eb}, -{16000000, DIF_BPF_COEFF3031, 0x0d55f341}, -{16000000, DIF_BPF_COEFF3233, 0xfcf6100e}, -{16000000, DIF_BPF_COEFF3435, 0xf544f8c3}, -{16000000, DIF_BPF_COEFF36, 0x110d0000}, -/* END - DIF BPF register values from 160_quant.dat*/ -}; - -#endif diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c deleted file mode 100644 index 7c4e360ba9bc..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-dvb.c +++ /dev/null @@ -1,796 +0,0 @@ -/* - DVB device driver for cx231xx - - Copyright (C) 2008 - Based on em28xx driver - - This program is free software; 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 "cx231xx.h" -#include -#include - -#include "xc5000.h" -#include "s5h1432.h" -#include "tda18271.h" -#include "s5h1411.h" -#include "lgdt3305.h" -#include "mb86a20s.h" - -MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); -MODULE_AUTHOR("Srinivasa Deevi "); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define dprintk(level, fmt, arg...) do { \ -if (debug >= level) \ - printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ -} while (0) - -#define CX231XX_DVB_NUM_BUFS 5 -#define CX231XX_DVB_MAX_PACKETSIZE 564 -#define CX231XX_DVB_MAX_PACKETS 64 - -struct cx231xx_dvb { - struct dvb_frontend *frontend; - - /* feed count management */ - struct mutex lock; - int nfeeds; - - /* general boilerplate stuff */ - struct dvb_adapter adapter; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dmx_frontend fe_hw; - struct dmx_frontend fe_mem; - struct dvb_net net; -}; - -static struct s5h1432_config dvico_s5h1432_config = { - .output_mode = S5H1432_SERIAL_OUTPUT, - .gpio = S5H1432_GPIO_ON, - .qam_if = S5H1432_IF_4000, - .vsb_if = S5H1432_IF_4000, - .inversion = S5H1432_INVERSION_OFF, - .status_mode = S5H1432_DEMODLOCKING, - .mpeg_timing = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, -}; - -static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = { - .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x37, }, - .dvbt_7 = { .if_freq = 4000, .agc_mode = 3, .std = 5, - .if_lvl = 1, .rfagc_top = 0x37, }, - .dvbt_8 = { .if_freq = 4000, .agc_mode = 3, .std = 6, - .if_lvl = 1, .rfagc_top = 0x37, }, -}; - -static struct tda18271_std_map mb86a20s_tda18271_config = { - .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, - .if_lvl = 7, .rfagc_top = 0x37, }, -}; - -static struct tda18271_config cnxt_rde253s_tunerconfig = { - .std_map = &cnxt_rde253s_tda18271_std_map, - .gate = TDA18271_GATE_ANALOG, -}; - -static struct s5h1411_config tda18271_s5h1411_config = { - .output_mode = S5H1411_SERIAL_OUTPUT, - .gpio = S5H1411_GPIO_OFF, - .vsb_if = S5H1411_IF_3250, - .qam_if = S5H1411_IF_4000, - .inversion = S5H1411_INVERSION_ON, - .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, -}; -static struct s5h1411_config xc5000_s5h1411_config = { - .output_mode = S5H1411_SERIAL_OUTPUT, - .gpio = S5H1411_GPIO_OFF, - .vsb_if = S5H1411_IF_3250, - .qam_if = S5H1411_IF_3250, - .inversion = S5H1411_INVERSION_OFF, - .status_mode = S5H1411_DEMODLOCKING, - .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, -}; - -static struct lgdt3305_config hcw_lgdt3305_config = { - .i2c_addr = 0x0e, - .mpeg_mode = LGDT3305_MPEG_SERIAL, - .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, - .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, - .deny_i2c_rptr = 1, - .spectral_inversion = 1, - .qam_if_khz = 4000, - .vsb_if_khz = 3250, -}; - -static struct tda18271_std_map hauppauge_tda18271_std_map = { - .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x58, }, - .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, - .if_lvl = 1, .rfagc_top = 0x58, }, -}; - -static struct tda18271_config hcw_tda18271_config = { - .std_map = &hauppauge_tda18271_std_map, - .gate = TDA18271_GATE_DIGITAL, -}; - -static const struct mb86a20s_config pv_mb86a20s_config = { - .demod_address = 0x10, - .is_serial = true, -}; - -static struct tda18271_config pv_tda18271_config = { - .std_map = &mb86a20s_tda18271_config, - .gate = TDA18271_GATE_DIGITAL, - .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, -}; - -static inline void print_err_status(struct cx231xx *dev, int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - dprintk(1, "URB status %d [%s].\n", status, errmsg); - } else { - dprintk(1, "URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb) -{ - int i; - - if (!dev) - return 0; - - if (dev->state & DEV_DISCONNECTED) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - dvb_dmx_swfilter(&dev->dvb->demux, - urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); - } - - return 0; -} - -static inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb) -{ - if (!dev) - return 0; - - if (dev->state & DEV_DISCONNECTED) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - /* Feed the transport payload into the kernel demux */ - dvb_dmx_swfilter(&dev->dvb->demux, - urb->transfer_buffer, urb->actual_length); - - return 0; -} - -static int start_streaming(struct cx231xx_dvb *dvb) -{ - int rc; - struct cx231xx *dev = dvb->adapter.priv; - - if (dev->USE_ISO) { - cx231xx_info("DVB transfer mode is ISO.\n"); - mutex_lock(&dev->i2c_lock); - cx231xx_enable_i2c_port_3(dev, false); - cx231xx_set_alt_setting(dev, INDEX_TS1, 4); - cx231xx_enable_i2c_port_3(dev, true); - mutex_unlock(&dev->i2c_lock); - rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - if (rc < 0) - return rc; - dev->mode_tv = 1; - return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS, - CX231XX_DVB_NUM_BUFS, - dev->ts1_mode.max_pkt_size, - dvb_isoc_copy); - } else { - cx231xx_info("DVB transfer mode is BULK.\n"); - cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - if (rc < 0) - return rc; - dev->mode_tv = 1; - return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS, - CX231XX_DVB_NUM_BUFS, - dev->ts1_mode.max_pkt_size, - dvb_bulk_copy); - } - -} - -static int stop_streaming(struct cx231xx_dvb *dvb) -{ - struct cx231xx *dev = dvb->adapter.priv; - - if (dev->USE_ISO) - cx231xx_uninit_isoc(dev); - else - cx231xx_uninit_bulk(dev); - - cx231xx_set_mode(dev, CX231XX_SUSPEND); - - return 0; -} - -static int start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct cx231xx_dvb *dvb = demux->priv; - int rc, ret; - - if (!demux->dmx.frontend) - return -EINVAL; - - mutex_lock(&dvb->lock); - dvb->nfeeds++; - rc = dvb->nfeeds; - - if (dvb->nfeeds == 1) { - ret = start_streaming(dvb); - if (ret < 0) - rc = ret; - } - - mutex_unlock(&dvb->lock); - return rc; -} - -static int stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct cx231xx_dvb *dvb = demux->priv; - int err = 0; - - mutex_lock(&dvb->lock); - dvb->nfeeds--; - - if (0 == dvb->nfeeds) - err = stop_streaming(dvb); - - mutex_unlock(&dvb->lock); - return err; -} - -/* ------------------------------------------------------------------ */ -static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) -{ - struct cx231xx *dev = fe->dvb->priv; - - if (acquire) - return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - else - return cx231xx_set_mode(dev, CX231XX_SUSPEND); -} - -/* ------------------------------------------------------------------ */ - -static struct xc5000_config cnxt_rde250_tunerconfig = { - .i2c_address = 0x61, - .if_khz = 4000, -}; -static struct xc5000_config cnxt_rdu250_tunerconfig = { - .i2c_address = 0x61, - .if_khz = 3250, -}; - -/* ------------------------------------------------------------------ */ -#if 0 -static int attach_xc5000(u8 addr, struct cx231xx *dev) -{ - - struct dvb_frontend *fe; - struct xc5000_config cfg; - - memset(&cfg, 0, sizeof(cfg)); - cfg.i2c_adap = &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap; - cfg.i2c_addr = addr; - - if (!dev->dvb->frontend) { - printk(KERN_ERR "%s/2: dvb frontend not attached. " - "Can't attach xc5000\n", dev->name); - return -EINVAL; - } - - fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg); - if (!fe) { - printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name); - dvb_frontend_detach(dev->dvb->frontend); - dev->dvb->frontend = NULL; - return -EINVAL; - } - - printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name); - - return 0; -} -#endif - -int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) -{ - int status = 0; - - if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { - - struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; - - if (dops->set_analog_params != NULL) { - struct analog_parameters params; - - params.frequency = freq; - params.std = dev->norm; - params.mode = 0; /* 0- Air; 1 - cable */ - /*params.audmode = ; */ - - /* Set the analog parameters to set the frequency */ - dops->set_analog_params(dev->dvb->frontend, ¶ms); - } - - } - - return status; -} - -int cx231xx_reset_analog_tuner(struct cx231xx *dev) -{ - int status = 0; - - if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { - - struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; - - if (dops->init != NULL && !dev->xc_fw_load_done) { - - cx231xx_info("Reloading firmware for XC5000\n"); - status = dops->init(dev->dvb->frontend); - if (status == 0) { - dev->xc_fw_load_done = 1; - cx231xx_info - ("XC5000 firmware download completed\n"); - } else { - dev->xc_fw_load_done = 0; - cx231xx_info - ("XC5000 firmware download failed !!!\n"); - } - } - - } - - return status; -} - -/* ------------------------------------------------------------------ */ - -static int register_dvb(struct cx231xx_dvb *dvb, - struct module *module, - struct cx231xx *dev, struct device *device) -{ - int result; - - mutex_init(&dvb->lock); - - /* register adapter */ - result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, - adapter_nr); - if (result < 0) { - printk(KERN_WARNING - "%s: dvb_register_adapter failed (errno = %d)\n", - dev->name, result); - goto fail_adapter; - } - - /* Ensure all frontends negotiate bus access */ - dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; - - dvb->adapter.priv = dev; - - /* register frontend */ - result = dvb_register_frontend(&dvb->adapter, dvb->frontend); - if (result < 0) { - printk(KERN_WARNING - "%s: dvb_register_frontend failed (errno = %d)\n", - dev->name, result); - goto fail_frontend; - } - - /* register demux stuff */ - dvb->demux.dmx.capabilities = - DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - dvb->demux.priv = dvb; - dvb->demux.filternum = 256; - dvb->demux.feednum = 256; - dvb->demux.start_feed = start_feed; - dvb->demux.stop_feed = stop_feed; - - result = dvb_dmx_init(&dvb->demux); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dev->name, result); - goto fail_dmx; - } - - dvb->dmxdev.filternum = 256; - dvb->dmxdev.demux = &dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", - dev->name, result); - goto fail_dmxdev; - } - - dvb->fe_hw.source = DMX_FRONTEND_0; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING - "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", - dev->name, result); - goto fail_fe_hw; - } - - dvb->fe_mem.source = DMX_MEMORY_FE; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); - if (result < 0) { - printk(KERN_WARNING - "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", - dev->name, result); - goto fail_fe_mem; - } - - result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING - "%s: connect_frontend failed (errno = %d)\n", dev->name, - result); - goto fail_fe_conn; - } - - /* register network adapter */ - dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); - return 0; - -fail_fe_conn: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); -fail_fe_mem: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); -fail_fe_hw: - dvb_dmxdev_release(&dvb->dmxdev); -fail_dmxdev: - dvb_dmx_release(&dvb->demux); -fail_dmx: - dvb_unregister_frontend(dvb->frontend); -fail_frontend: - dvb_frontend_detach(dvb->frontend); - dvb_unregister_adapter(&dvb->adapter); -fail_adapter: - return result; -} - -static void unregister_dvb(struct cx231xx_dvb *dvb) -{ - dvb_net_release(&dvb->net); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); - dvb_dmxdev_release(&dvb->dmxdev); - dvb_dmx_release(&dvb->demux); - dvb_unregister_frontend(dvb->frontend); - dvb_frontend_detach(dvb->frontend); - dvb_unregister_adapter(&dvb->adapter); -} - -static int dvb_init(struct cx231xx *dev) -{ - int result = 0; - struct cx231xx_dvb *dvb; - - if (!dev->board.has_dvb) { - /* This device does not support the extension */ - return 0; - } - - dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL); - - if (dvb == NULL) { - printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n"); - return -ENOMEM; - } - dev->dvb = dvb; - dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq; - dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner; - - mutex_lock(&dev->lock); - cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - cx231xx_demod_reset(dev); - /* init frontend */ - switch (dev->model) { - case CX231XX_BOARD_CNXT_CARRAERA: - case CX231XX_BOARD_CNXT_RDE_250: - - dev->dvb->frontend = dvb_attach(s5h1432_attach, - &dvico_s5h1432_config, - &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); - - if (dev->dvb->frontend == NULL) { - printk(DRIVER_NAME - ": Failed to attach s5h1432 front end\n"); - result = -EINVAL; - goto out_free; - } - - /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; - - if (!dvb_attach(xc5000_attach, dev->dvb->frontend, - &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, - &cnxt_rde250_tunerconfig)) { - result = -EINVAL; - goto out_free; - } - - break; - case CX231XX_BOARD_CNXT_SHELBY: - case CX231XX_BOARD_CNXT_RDU_250: - - dev->dvb->frontend = dvb_attach(s5h1411_attach, - &xc5000_s5h1411_config, - &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); - - if (dev->dvb->frontend == NULL) { - printk(DRIVER_NAME - ": Failed to attach s5h1411 front end\n"); - result = -EINVAL; - goto out_free; - } - - /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; - - if (!dvb_attach(xc5000_attach, dev->dvb->frontend, - &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, - &cnxt_rdu250_tunerconfig)) { - result = -EINVAL; - goto out_free; - } - break; - case CX231XX_BOARD_CNXT_RDE_253S: - - dev->dvb->frontend = dvb_attach(s5h1432_attach, - &dvico_s5h1432_config, - &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); - - if (dev->dvb->frontend == NULL) { - printk(DRIVER_NAME - ": Failed to attach s5h1432 front end\n"); - result = -EINVAL; - goto out_free; - } - - /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; - - if (!dvb_attach(tda18271_attach, dev->dvb->frontend, - 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, - &cnxt_rde253s_tunerconfig)) { - result = -EINVAL; - goto out_free; - } - break; - case CX231XX_BOARD_CNXT_RDU_253S: - - dev->dvb->frontend = dvb_attach(s5h1411_attach, - &tda18271_s5h1411_config, - &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); - - if (dev->dvb->frontend == NULL) { - printk(DRIVER_NAME - ": Failed to attach s5h1411 front end\n"); - result = -EINVAL; - goto out_free; - } - - /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; - - if (!dvb_attach(tda18271_attach, dev->dvb->frontend, - 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, - &cnxt_rde253s_tunerconfig)) { - result = -EINVAL; - goto out_free; - } - break; - case CX231XX_BOARD_HAUPPAUGE_EXETER: - - printk(KERN_INFO "%s: looking for tuner / demod on i2c bus: %d\n", - __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap)); - - dev->dvb->frontend = dvb_attach(lgdt3305_attach, - &hcw_lgdt3305_config, - &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap); - - if (dev->dvb->frontend == NULL) { - printk(DRIVER_NAME - ": Failed to attach LG3305 front end\n"); - result = -EINVAL; - goto out_free; - } - - /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; - - dvb_attach(tda18271_attach, dev->dvb->frontend, - 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, - &hcw_tda18271_config); - break; - - case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: - case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: - - printk(KERN_INFO "%s: looking for demod on i2c bus: %d\n", - __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap)); - - dev->dvb->frontend = dvb_attach(mb86a20s_attach, - &pv_mb86a20s_config, - &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap); - - if (dev->dvb->frontend == NULL) { - printk(DRIVER_NAME - ": Failed to attach mb86a20s demod\n"); - result = -EINVAL; - goto out_free; - } - - /* define general-purpose callback pointer */ - dvb->frontend->callback = cx231xx_tuner_callback; - - dvb_attach(tda18271_attach, dev->dvb->frontend, - 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, - &pv_tda18271_config); - break; - - default: - printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" - " isn't supported yet\n", dev->name); - break; - } - if (NULL == dvb->frontend) { - printk(KERN_ERR - "%s/2: frontend initialization failed\n", dev->name); - result = -EINVAL; - goto out_free; - } - - /* register everything */ - result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); - - if (result < 0) - goto out_free; - - - printk(KERN_INFO "Successfully loaded cx231xx-dvb\n"); - -ret: - cx231xx_set_mode(dev, CX231XX_SUSPEND); - mutex_unlock(&dev->lock); - return result; - -out_free: - kfree(dvb); - dev->dvb = NULL; - goto ret; -} - -static int dvb_fini(struct cx231xx *dev) -{ - if (!dev->board.has_dvb) { - /* This device does not support the extension */ - return 0; - } - - if (dev->dvb) { - unregister_dvb(dev->dvb); - dev->dvb = NULL; - } - - return 0; -} - -static struct cx231xx_ops dvb_ops = { - .id = CX231XX_DVB, - .name = "Cx231xx dvb Extension", - .init = dvb_init, - .fini = dvb_fini, -}; - -static int __init cx231xx_dvb_register(void) -{ - return cx231xx_register_extension(&dvb_ops); -} - -static void __exit cx231xx_dvb_unregister(void) -{ - cx231xx_unregister_extension(&dvb_ops); -} - -module_init(cx231xx_dvb_register); -module_exit(cx231xx_dvb_unregister); diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c deleted file mode 100644 index 781feed406f7..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-i2c.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices - - Copyright (C) 2008 - Based on em28xx driver - Based on Cx23885 driver - - This program is free software; 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 "cx231xx.h" - -/* ----------------------------------------------------------- */ - -static unsigned int i2c_scan; -module_param(i2c_scan, int, 0444); -MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); - -static unsigned int i2c_debug; -module_param(i2c_debug, int, 0644); -MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define dprintk1(lvl, fmt, args...) \ -do { \ - if (i2c_debug >= lvl) { \ - printk(fmt, ##args); \ - } \ -} while (0) - -#define dprintk2(lvl, fmt, args...) \ -do { \ - if (i2c_debug >= lvl) { \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __func__ , ##args); \ - } \ -} while (0) - -static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus, - const struct i2c_msg *msg, int tuner_type) -{ - if (bus->nr != dev->board.tuner_i2c_master) - return false; - - if (msg->addr != dev->board.tuner_addr) - return false; - - if (dev->tuner_type != tuner_type) - return false; - - return true; -} - -/* - * cx231xx_i2c_send_bytes() - */ -int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap, - const struct i2c_msg *msg) -{ - struct cx231xx_i2c *bus = i2c_adap->algo_data; - struct cx231xx *dev = bus->dev; - struct cx231xx_i2c_xfer_data req_data; - int status = 0; - u16 size = 0; - u8 loop = 0; - u8 saddr_len = 1; - u8 *buf_ptr = NULL; - u16 saddr = 0; - u8 need_gpio = 0; - - if (is_tuner(dev, bus, msg, TUNER_XC5000)) { - size = msg->len; - - if (size == 2) { /* register write sub addr */ - /* Just writing sub address will cause problem - * to XC5000. So ignore the request */ - return 0; - } else if (size == 4) { /* register write with sub addr */ - if (msg->len >= 2) - saddr = msg->buf[0] << 8 | msg->buf[1]; - else if (msg->len == 1) - saddr = msg->buf[0]; - - switch (saddr) { - case 0x0000: /* start tuner calibration mode */ - need_gpio = 1; - /* FW Loading is done */ - dev->xc_fw_load_done = 1; - break; - case 0x000D: /* Set signal source */ - case 0x0001: /* Set TV standard - Video */ - case 0x0002: /* Set TV standard - Audio */ - case 0x0003: /* Set RF Frequency */ - need_gpio = 1; - break; - default: - if (dev->xc_fw_load_done) - need_gpio = 1; - break; - } - - if (need_gpio) { - dprintk1(1, - "GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n", - msg->addr, msg->len, saddr); - - return dev->cx231xx_gpio_i2c_write(dev, - msg->addr, - msg->buf, - msg->len); - } - } - - /* special case for Xc5000 tuner case */ - saddr_len = 1; - - /* adjust the length to correct length */ - size -= saddr_len; - buf_ptr = (u8 *) (msg->buf + 1); - - do { - /* prepare xfer_data struct */ - req_data.dev_addr = msg->addr; - req_data.direction = msg->flags; - req_data.saddr_len = saddr_len; - req_data.saddr_dat = msg->buf[0]; - req_data.buf_size = size > 16 ? 16 : size; - req_data.p_buffer = (u8 *) (buf_ptr + loop * 16); - - bus->i2c_nostop = (size > 16) ? 1 : 0; - bus->i2c_reserve = (loop == 0) ? 0 : 1; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(bus, &req_data); - loop++; - - if (size >= 16) - size -= 16; - else - size = 0; - - } while (size > 0); - - bus->i2c_nostop = 0; - bus->i2c_reserve = 0; - - } else { /* regular case */ - - /* prepare xfer_data struct */ - req_data.dev_addr = msg->addr; - req_data.direction = msg->flags; - req_data.saddr_len = 0; - req_data.saddr_dat = 0; - req_data.buf_size = msg->len; - req_data.p_buffer = msg->buf; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(bus, &req_data); - } - - return status < 0 ? status : 0; -} - -/* - * cx231xx_i2c_recv_bytes() - * read a byte from the i2c device - */ -static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap, - const struct i2c_msg *msg) -{ - struct cx231xx_i2c *bus = i2c_adap->algo_data; - struct cx231xx *dev = bus->dev; - struct cx231xx_i2c_xfer_data req_data; - int status = 0; - u16 saddr = 0; - u8 need_gpio = 0; - - if (is_tuner(dev, bus, msg, TUNER_XC5000)) { - if (msg->len == 2) - saddr = msg->buf[0] << 8 | msg->buf[1]; - else if (msg->len == 1) - saddr = msg->buf[0]; - - if (dev->xc_fw_load_done) { - - switch (saddr) { - case 0x0009: /* BUSY check */ - dprintk1(1, - "GPIO R E A D: Special case BUSY check \n"); - /*Try read BUSY register, just set it to zero*/ - msg->buf[0] = 0; - if (msg->len == 2) - msg->buf[1] = 0; - return 0; - case 0x0004: /* read Lock status */ - need_gpio = 1; - break; - - } - - if (need_gpio) { - /* this is a special case to handle Xceive tuner - clock stretch issue with gpio based I2C */ - - dprintk1(1, - "GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n", - msg->addr, msg->len, - msg->buf[0] << 8 | msg->buf[1]); - - status = - dev->cx231xx_gpio_i2c_write(dev, msg->addr, - msg->buf, - msg->len); - status = - dev->cx231xx_gpio_i2c_read(dev, msg->addr, - msg->buf, - msg->len); - return status; - } - } - - /* prepare xfer_data struct */ - req_data.dev_addr = msg->addr; - req_data.direction = msg->flags; - req_data.saddr_len = msg->len; - req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1]; - req_data.buf_size = msg->len; - req_data.p_buffer = msg->buf; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(bus, &req_data); - - } else { - - /* prepare xfer_data struct */ - req_data.dev_addr = msg->addr; - req_data.direction = msg->flags; - req_data.saddr_len = 0; - req_data.saddr_dat = 0; - req_data.buf_size = msg->len; - req_data.p_buffer = msg->buf; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(bus, &req_data); - } - - return status < 0 ? status : 0; -} - -/* - * cx231xx_i2c_recv_bytes_with_saddr() - * read a byte from the i2c device - */ -static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap, - const struct i2c_msg *msg1, - const struct i2c_msg *msg2) -{ - struct cx231xx_i2c *bus = i2c_adap->algo_data; - struct cx231xx *dev = bus->dev; - struct cx231xx_i2c_xfer_data req_data; - int status = 0; - u16 saddr = 0; - u8 need_gpio = 0; - - if (msg1->len == 2) - saddr = msg1->buf[0] << 8 | msg1->buf[1]; - else if (msg1->len == 1) - saddr = msg1->buf[0]; - - if (is_tuner(dev, bus, msg2, TUNER_XC5000)) { - if ((msg2->len < 16)) { - - dprintk1(1, - "i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n", - msg2->addr, msg2->len, saddr, msg1->len); - - switch (saddr) { - case 0x0008: /* read FW load status */ - need_gpio = 1; - break; - case 0x0004: /* read Lock status */ - need_gpio = 1; - break; - } - - if (need_gpio) { - status = - dev->cx231xx_gpio_i2c_write(dev, msg1->addr, - msg1->buf, - msg1->len); - status = - dev->cx231xx_gpio_i2c_read(dev, msg2->addr, - msg2->buf, - msg2->len); - return status; - } - } - } - - /* prepare xfer_data struct */ - req_data.dev_addr = msg2->addr; - req_data.direction = msg2->flags; - req_data.saddr_len = msg1->len; - req_data.saddr_dat = saddr; - req_data.buf_size = msg2->len; - req_data.p_buffer = msg2->buf; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(bus, &req_data); - - return status < 0 ? status : 0; -} - -/* - * cx231xx_i2c_check_for_device() - * check if there is a i2c_device at the supplied address - */ -static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap, - const struct i2c_msg *msg) -{ - struct cx231xx_i2c *bus = i2c_adap->algo_data; - struct cx231xx *dev = bus->dev; - struct cx231xx_i2c_xfer_data req_data; - int status = 0; - - /* prepare xfer_data struct */ - req_data.dev_addr = msg->addr; - req_data.direction = msg->flags; - req_data.saddr_len = 0; - req_data.saddr_dat = 0; - req_data.buf_size = 0; - req_data.p_buffer = NULL; - - /* usb send command */ - status = dev->cx231xx_send_usb_command(bus, &req_data); - - return status < 0 ? status : 0; -} - -/* - * cx231xx_i2c_xfer() - * the main i2c transfer function - */ -static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) -{ - struct cx231xx_i2c *bus = i2c_adap->algo_data; - struct cx231xx *dev = bus->dev; - int addr, rc, i, byte; - - if (num <= 0) - return 0; - mutex_lock(&dev->i2c_lock); - for (i = 0; i < num; i++) { - - addr = msgs[i].addr >> 1; - - dprintk2(2, "%s %s addr=%x len=%d:", - (msgs[i].flags & I2C_M_RD) ? "read" : "write", - i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); - if (!msgs[i].len) { - /* no len: check only for device presence */ - rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]); - if (rc < 0) { - dprintk2(2, " no device\n"); - mutex_unlock(&dev->i2c_lock); - return rc; - } - - } else if (msgs[i].flags & I2C_M_RD) { - /* read bytes */ - rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]); - if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } - } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && - msgs[i].addr == msgs[i + 1].addr - && (msgs[i].len <= 2) && (bus->nr < 3)) { - /* read bytes */ - rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap, - &msgs[i], - &msgs[i + 1]); - if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } - i++; - } else { - /* write bytes */ - if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } - rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]); - } - if (rc < 0) - goto err; - if (i2c_debug >= 2) - printk("\n"); - } - mutex_unlock(&dev->i2c_lock); - return num; -err: - dprintk2(2, " ERROR: %i\n", rc); - mutex_unlock(&dev->i2c_lock); - return rc; -} - -/* ----------------------------------------------------------- */ - -/* - * functionality() - */ -static u32 functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; -} - -static struct i2c_algorithm cx231xx_algo = { - .master_xfer = cx231xx_i2c_xfer, - .functionality = functionality, -}; - -static struct i2c_adapter cx231xx_adap_template = { - .owner = THIS_MODULE, - .name = "cx231xx", - .algo = &cx231xx_algo, -}; - -static struct i2c_client cx231xx_client_template = { - .name = "cx231xx internal", -}; - -/* ----------------------------------------------------------- */ - -/* - * i2c_devs - * incomplete list of known devices - */ -static char *i2c_devs[128] = { - [0x60 >> 1] = "colibri", - [0x88 >> 1] = "hammerhead", - [0x8e >> 1] = "CIR", - [0x32 >> 1] = "GeminiIII", - [0x02 >> 1] = "Aquarius", - [0xa0 >> 1] = "eeprom", - [0xc0 >> 1] = "tuner", - [0xc2 >> 1] = "tuner", -}; - -/* - * cx231xx_do_i2c_scan() - * check i2c address range for devices - */ -void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c) -{ - unsigned char buf; - int i, rc; - - cx231xx_info(": Checking for I2C devices ..\n"); - for (i = 0; i < 128; i++) { - c->addr = i; - rc = i2c_master_recv(c, &buf, 0); - if (rc < 0) - continue; - cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n", - dev->name, i << 1, - i2c_devs[i] ? i2c_devs[i] : "???"); - } - cx231xx_info(": Completed Checking for I2C devices.\n"); -} - -/* - * cx231xx_i2c_register() - * register i2c bus - */ -int cx231xx_i2c_register(struct cx231xx_i2c *bus) -{ - struct cx231xx *dev = bus->dev; - - BUG_ON(!dev->cx231xx_send_usb_command); - - bus->i2c_adap = cx231xx_adap_template; - bus->i2c_client = cx231xx_client_template; - bus->i2c_adap.dev.parent = &dev->udev->dev; - - strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name)); - - bus->i2c_adap.algo_data = bus; - i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); - i2c_add_adapter(&bus->i2c_adap); - - bus->i2c_client.adapter = &bus->i2c_adap; - - if (0 == bus->i2c_rc) { - if (i2c_scan) - cx231xx_do_i2c_scan(dev, &bus->i2c_client); - } else - cx231xx_warn("%s: i2c bus %d register FAILED\n", - dev->name, bus->nr); - - return bus->i2c_rc; -} - -/* - * cx231xx_i2c_unregister() - * unregister i2c_bus - */ -int cx231xx_i2c_unregister(struct cx231xx_i2c *bus) -{ - i2c_del_adapter(&bus->i2c_adap); - return 0; -} diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c deleted file mode 100644 index 96176e9db5a2..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * cx231xx IR glue driver - * - * Copyright (C) 2010 Mauro Carvalho Chehab - * - * Polaris (cx231xx) has its support for IR's with a design close to MCE. - * however, a few designs are using an external I2C chip for IR, instead - * of using the one provided by the chip. - * This driver provides support for those extra devices - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include "cx231xx.h" -#include -#include - -#define MODULE_NAME "cx231xx-input" - -static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) -{ - int rc; - u8 cmd, scancode; - - dev_dbg(&ir->rc->input_dev->dev, "%s\n", __func__); - - /* poll IR chip */ - rc = i2c_master_recv(ir->c, &cmd, 1); - if (rc < 0) - return rc; - if (rc != 1) - return -EIO; - - /* it seems that 0xFE indicates that a button is still hold - down, while 0xff indicates that no button is hold - down. 0xfe sequences are sometimes interrupted by 0xFF */ - - if (cmd == 0xff) - return 0; - - scancode = - ((cmd & 0x01) ? 0x80 : 0) | - ((cmd & 0x02) ? 0x40 : 0) | - ((cmd & 0x04) ? 0x20 : 0) | - ((cmd & 0x08) ? 0x10 : 0) | - ((cmd & 0x10) ? 0x08 : 0) | - ((cmd & 0x20) ? 0x04 : 0) | - ((cmd & 0x40) ? 0x02 : 0) | - ((cmd & 0x80) ? 0x01 : 0); - - dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n", - cmd, scancode); - - *ir_key = scancode; - *ir_raw = scancode; - return 1; -} - -int cx231xx_ir_init(struct cx231xx *dev) -{ - struct i2c_board_info info; - u8 ir_i2c_bus; - - dev_dbg(&dev->udev->dev, "%s\n", __func__); - - /* Only initialize if a rc keycode map is defined */ - if (!cx231xx_boards[dev->model].rc_map_name) - return -ENODEV; - - request_module("ir-kbd-i2c"); - - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - dev->init_data.rc_dev = rc_allocate_device(); - if (!dev->init_data.rc_dev) - return -ENOMEM; - - dev->init_data.name = cx231xx_boards[dev->model].name; - - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - info.platform_data = &dev->init_data; - - /* - * Board-dependent values - * - * For now, there's just one type of hardware design using - * an i2c device. - */ - dev->init_data.get_key = get_key_isdbt; - dev->init_data.ir_codes = cx231xx_boards[dev->model].rc_map_name; - /* The i2c micro-controller only outputs the cmd part of NEC protocol */ - dev->init_data.rc_dev->scanmask = 0xff; - dev->init_data.rc_dev->driver_name = "cx231xx"; - dev->init_data.type = RC_TYPE_NEC; - info.addr = 0x30; - - /* Load and bind ir-kbd-i2c */ - ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master; - dev_dbg(&dev->udev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n", - ir_i2c_bus, info.addr); - dev->ir_i2c_client = i2c_new_device(&dev->i2c_bus[ir_i2c_bus].i2c_adap, &info); - - return 0; -} - -void cx231xx_ir_exit(struct cx231xx *dev) -{ - if (dev->ir_i2c_client) - i2c_unregister_device(dev->ir_i2c_client); - dev->ir_i2c_client = NULL; -} diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c deleted file mode 100644 index 7473c33e823e..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c +++ /dev/null @@ -1,795 +0,0 @@ -/* - cx231xx-pcb-config.c - driver for Conexant - Cx23100/101/102 USB video capture devices - - Copyright (C) 2008 - - This program is free software; 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 "cx231xx.h" -#include "cx231xx-conf-reg.h" - -static unsigned int pcb_debug; -module_param(pcb_debug, int, 0644); -MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]"); - -/******************************************************************************/ - -struct pcb_config cx231xx_Scenario[] = { - { - INDEX_SELFPOWER_DIGITAL_ONLY, /* index */ - USB_SELF_POWER, /* power_type */ - 0, /* speed , not decide yet */ - MOD_DIGITAL, /* mode */ - SOURCE_TS_BDA, /* ts1_source, digital tv only */ - NOT_SUPPORTED, /* ts2_source */ - NOT_SUPPORTED, /* analog source */ - - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - NOT_SUPPORTED, /* AUDIO */ - NOT_SUPPORTED, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - , - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - /* full-speed config */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - NOT_SUPPORTED, /* AUDIO */ - NOT_SUPPORTED, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - - { - INDEX_SELFPOWER_DUAL_DIGITAL, /* index */ - USB_SELF_POWER, /* power_type */ - 0, /* speed , not decide yet */ - MOD_DIGITAL, /* mode */ - SOURCE_TS_BDA, /* ts1_source, digital tv only */ - 0, /* ts2_source,need update from register */ - NOT_SUPPORTED, /* analog source */ - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - 2, /* TS2 index */ - NOT_SUPPORTED, /* AUDIO */ - NOT_SUPPORTED, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - /* full-speed */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - 2, /* TS2 index */ - NOT_SUPPORTED, /* AUDIO */ - NOT_SUPPORTED, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - - { - INDEX_SELFPOWER_ANALOG_ONLY, /* index */ - USB_SELF_POWER, /* power_type */ - 0, /* speed , not decide yet */ - MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */ - NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */ - NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ - 0, /* analog source, need update */ - - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - NOT_SUPPORTED, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 1, /* AUDIO */ - 2, /* VIDEO */ - 3, /* VANC */ - 4, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - /* full-speed */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - NOT_SUPPORTED, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 1, /* AUDIO */ - 2, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - - { - INDEX_SELFPOWER_DUAL, /* index */ - USB_SELF_POWER, /* power_type */ - 0, /* speed , not decide yet */ - /* mode ,analog tv and digital path */ - MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, - 0, /* ts1_source,will update in register */ - NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ - 0, /* analog source need update */ - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 2, /* AUDIO */ - 3, /* VIDEO */ - 4, /* VANC */ - 5, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - /* full-speed */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 2, /* AUDIO */ - 3, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - - { - INDEX_SELFPOWER_TRIPLE, /* index */ - USB_SELF_POWER, /* power_type */ - 0, /* speed , not decide yet */ - /* mode ,analog tv and digital path */ - MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, - 0, /* ts1_source, update in register */ - 0, /* ts2_source,update in register */ - 0, /* analog source, need update */ - - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - 2, /* TS2 index */ - 3, /* AUDIO */ - 4, /* VIDEO */ - 5, /* VANC */ - 6, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - /* full-speed */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - 2, /* TS2 index */ - 3, /* AUDIO */ - 4, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - - { - INDEX_SELFPOWER_COMPRESSOR, /* index */ - USB_SELF_POWER, /* power_type */ - 0, /* speed , not decide yet */ - /* mode ,analog tv AND DIGITAL path */ - MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, - NOT_SUPPORTED, /* ts1_source, disable */ - SOURCE_TS_BDA, /* ts2_source */ - 0, /* analog source,need update */ - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - NOT_SUPPORTED, /* ts1 index */ - 1, /* TS2 index */ - 2, /* AUDIO */ - 3, /* VIDEO */ - 4, /* VANC */ - 5, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - /* full-speed */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - NOT_SUPPORTED, /* ts1 index */ - 1, /* TS2 index */ - 2, /* AUDIO */ - 3, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - - { - INDEX_BUSPOWER_DIGITAL_ONLY, /* index */ - USB_BUS_POWER, /* power_type */ - 0, /* speed , not decide yet */ - MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */ - SOURCE_TS_BDA, /* ts1_source, disable */ - NOT_SUPPORTED, /* ts2_source */ - NOT_SUPPORTED, /* analog source */ - - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index = 2 */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - NOT_SUPPORTED, /* AUDIO */ - NOT_SUPPORTED, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - /* full-speed */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index = 2 */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - NOT_SUPPORTED, /* AUDIO */ - NOT_SUPPORTED, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - { - INDEX_BUSPOWER_ANALOG_ONLY, /* index */ - USB_BUS_POWER, /* power_type */ - 0, /* speed , not decide yet */ - MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */ - NOT_SUPPORTED, /* ts1_source, disable */ - NOT_SUPPORTED, /* ts2_source */ - SOURCE_ANALOG, /* analog source--analog */ - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - NOT_SUPPORTED, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 1, /* AUDIO */ - 2, /* VIDEO */ - 3, /* VANC */ - 4, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - { /* full-speed */ - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - NOT_SUPPORTED, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 1, /* AUDIO */ - 2, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - { - INDEX_BUSPOWER_DIF_ONLY, /* index */ - USB_BUS_POWER, /* power_type */ - 0, /* speed , not decide yet */ - /* mode ,analog tv AND DIGITAL path */ - MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL, - SOURCE_TS_BDA, /* ts1_source, disable */ - NOT_SUPPORTED, /* ts2_source */ - SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */ - 0, /* digital_index */ - 0, /* analog index */ - 0, /* dif_index */ - 0, /* external_index */ - 1, /* only one configuration */ - { - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 2, /* AUDIO */ - 3, /* VIDEO */ - 4, /* VANC */ - 5, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - , - { /* full speed */ - { - 0, /* config index */ - { - 0, /* interrupt ep index */ - 1, /* ts1 index */ - NOT_SUPPORTED, /* TS2 index */ - 2, /* AUDIO */ - 3, /* VIDEO */ - NOT_SUPPORTED, /* VANC */ - NOT_SUPPORTED, /* HANC */ - NOT_SUPPORTED /* ir_index */ - } - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - , - {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, - NOT_SUPPORTED} - } - } - } - , - -}; - -/*****************************************************************/ - -u32 initialize_cx231xx(struct cx231xx *dev) -{ - u32 config_info = 0; - struct pcb_config *p_pcb_info; - u8 usb_speed = 1; /* from register,1--HS, 0--FS */ - u8 data[4] = { 0, 0, 0, 0 }; - u32 ts1_source = 0; - u32 ts2_source = 0; - u32 analog_source = 0; - u8 _current_scenario_idx = 0xff; - - ts1_source = SOURCE_TS_BDA; - ts2_source = SOURCE_TS_BDA; - - /* read board config register to find out which - pcb config it is related to */ - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4); - - config_info = *((u32 *) data); - usb_speed = (u8) (config_info & 0x1); - - /* Verify this device belongs to Bus power or Self power device */ - if (config_info & BUS_POWER) { /* bus-power */ - switch (config_info & BUSPOWER_MASK) { - case TS1_PORT | BUS_POWER: - cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed = - usb_speed; - p_pcb_info = - &cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY]; - _current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY; - break; - case AVDEC_ENABLE | BUS_POWER: - cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed = - usb_speed; - p_pcb_info = - &cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY]; - _current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY; - break; - case AVDEC_ENABLE | BUS_POWER | TS1_PORT: - cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed = - usb_speed; - p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY]; - _current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY; - break; - default: - cx231xx_info("bad config in buspower!!!!\n"); - cx231xx_info("config_info=%x\n", - (config_info & BUSPOWER_MASK)); - return 1; - } - } else { /* self-power */ - - switch (config_info & SELFPOWER_MASK) { - case TS1_PORT | SELF_POWER: - cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed = - usb_speed; - p_pcb_info = - &cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY]; - _current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY; - break; - case TS1_TS2_PORT | SELF_POWER: - cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed = - usb_speed; - cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]. - ts2_source = ts2_source; - p_pcb_info = - &cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]; - _current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL; - break; - case AVDEC_ENABLE | SELF_POWER: - cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed = - usb_speed; - cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]. - analog_source = analog_source; - p_pcb_info = - &cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]; - _current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY; - break; - case AVDEC_ENABLE | TS1_PORT | SELF_POWER: - cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed = - usb_speed; - cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source = - ts1_source; - cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source = - analog_source; - p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL]; - _current_scenario_idx = INDEX_SELFPOWER_DUAL; - break; - case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER: - cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed = - usb_speed; - cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source = - ts1_source; - cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source = - ts2_source; - cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source = - analog_source; - p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE]; - _current_scenario_idx = INDEX_SELFPOWER_TRIPLE; - break; - case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER: - cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed = - usb_speed; - cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]. - analog_source = analog_source; - p_pcb_info = - &cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]; - _current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR; - break; - default: - cx231xx_info("bad senario!!!!!\n"); - cx231xx_info("config_info=%x\n", - (config_info & SELFPOWER_MASK)); - return 1; - } - } - - dev->current_scenario_idx = _current_scenario_idx; - - memcpy(&dev->current_pcb_config, p_pcb_info, - sizeof(struct pcb_config)); - - if (pcb_debug) { - cx231xx_info("SC(0x00) register = 0x%x\n", config_info); - cx231xx_info("scenario %d\n", - (dev->current_pcb_config.index) + 1); - cx231xx_info("type=%x\n", dev->current_pcb_config.type); - cx231xx_info("mode=%x\n", dev->current_pcb_config.mode); - cx231xx_info("speed=%x\n", dev->current_pcb_config.speed); - cx231xx_info("ts1_source=%x\n", - dev->current_pcb_config.ts1_source); - cx231xx_info("ts2_source=%x\n", - dev->current_pcb_config.ts2_source); - cx231xx_info("analog_source=%x\n", - dev->current_pcb_config.analog_source); - } - - return 0; -} diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h deleted file mode 100644 index f5e46e89f3ab..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - cx231xx-pcb-cfg.h - driver for Conexant - Cx23100/101/102 USB video capture devices - - Copyright (C) 2008 - - This program is free software; 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 _PCB_CONFIG_H_ -#define _PCB_CONFIG_H_ - -#include -#include - -/*************************************************************************** - * Class Information * -***************************************************************************/ -#define CLASS_DEFAULT 0xFF - -enum VENDOR_REQUEST_TYPE { - /* Set/Get I2C */ - VRT_SET_I2C0 = 0x0, - VRT_SET_I2C1 = 0x1, - VRT_SET_I2C2 = 0x2, - VRT_GET_I2C0 = 0x4, - VRT_GET_I2C1 = 0x5, - VRT_GET_I2C2 = 0x6, - - /* Set/Get GPIO */ - VRT_SET_GPIO = 0x8, - VRT_GET_GPIO = 0x9, - - /* Set/Get GPIE */ - VRT_SET_GPIE = 0xA, - VRT_GET_GPIE = 0xB, - - /* Set/Get Register Control/Status */ - VRT_SET_REGISTER = 0xC, - VRT_GET_REGISTER = 0xD, - - /* Get Extended Compat ID Descriptor */ - VRT_GET_EXTCID_DESC = 0xFF, -}; - -enum BYTE_ENABLE_MASK { - ENABLE_ONE_BYTE = 0x1, - ENABLE_TWE_BYTE = 0x3, - ENABLE_THREE_BYTE = 0x7, - ENABLE_FOUR_BYTE = 0xF, -}; - -#define SPEED_MASK 0x1 -enum USB_SPEED{ - FULL_SPEED = 0x0, /* 0: full speed */ - HIGH_SPEED = 0x1 /* 1: high speed */ -}; - -enum _true_false{ - FALSE = 0, - TRUE = 1 -}; - -#define TS_MASK 0x6 -enum TS_PORT{ - NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid, - only offers Analog TV or Video */ - TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode : - Digital or External Analog/Compressed source) */ - TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs - (Dual inputs from Digital and/or - External Analog/Compressed sources) */ - TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector - to external clock */ - TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output, - TS2 Input (from Compressor) */ -}; - -#define EAVP_MASK 0x8 -enum EAV_PRESENT{ - NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs - (no need for i2s blcok), - Analog Tuner must be present */ - EXTERNAL_AV = 0x8 /* 1: External A/V inputs - present (requires i2s blk) */ -}; - -#define ATM_MASK 0x30 -enum AT_MODE{ - DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */ - BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite & - Sound-IF Signals present */ - NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */ -}; - -#define PWR_SEL_MASK 0x40 -enum POWE_TYPE{ - SELF_POWER = 0x0, /* 0: self power */ - BUS_POWER = 0x40 /* 1: bus power */ -}; - -enum USB_POWE_TYPE{ - USB_SELF_POWER = 0, - USB_BUS_POWER -}; - -#define BO_0_MASK 0x80 -enum AVDEC_STATUS{ - AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */ - AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */ -}; - -#define BO_1_MASK 0x100 - -#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */ -#define SELFPOWER_MASK 0x86 - -/***************************************************************************/ -#define NOT_DECIDE_YET 0xFE -#define NOT_SUPPORTED 0xFF - -/*************************************************************************** - * for mod field use * -***************************************************************************/ -#define MOD_DIGITAL 0x1 -#define MOD_ANALOG 0x2 -#define MOD_DIF 0x4 -#define MOD_EXTERNAL 0x8 -#define CAP_ALL_MOD 0x0f - -/*************************************************************************** - * source define * -***************************************************************************/ -#define SOURCE_DIGITAL 0x1 -#define SOURCE_ANALOG 0x2 -#define SOURCE_DIF 0x4 -#define SOURCE_EXTERNAL 0x8 -#define SOURCE_TS_BDA 0x10 -#define SOURCE_TS_ENCODE 0x20 -#define SOURCE_TS_EXTERNAL 0x40 - -/*************************************************************************** - * interface information define * -***************************************************************************/ -struct INTERFACE_INFO { - u8 interrupt_index; - u8 ts1_index; - u8 ts2_index; - u8 audio_index; - u8 video_index; - u8 vanc_index; /* VBI */ - u8 hanc_index; /* Sliced CC */ - u8 ir_index; -}; - -enum INDEX_INTERFACE_INFO{ - INDEX_INTERRUPT = 0x0, - INDEX_TS1, - INDEX_TS2, - INDEX_AUDIO, - INDEX_VIDEO, - INDEX_VANC, - INDEX_HANC, - INDEX_IR, -}; - -/*************************************************************************** - * configuration information define * -***************************************************************************/ -struct CONFIG_INFO { - u8 config_index; - struct INTERFACE_INFO interface_info; -}; - -struct pcb_config { - u8 index; - u8 type; /* bus power or self power, - self power--0, bus_power--1 */ - u8 speed; /* usb speed, 2.0--1, 1.1--0 */ - u8 mode; /* digital , anlog, dif or external A/V */ - u32 ts1_source; /* three source -- BDA,External,encode */ - u32 ts2_source; - u32 analog_source; - u8 digital_index; /* bus-power used */ - u8 analog_index; /* bus-power used */ - u8 dif_index; /* bus-power used */ - u8 external_index; /* bus-power used */ - u8 config_num; /* current config num, 0,1,2, - for self-power, always 0 */ - struct CONFIG_INFO hs_config_info[3]; - struct CONFIG_INFO fs_config_info[3]; -}; - -enum INDEX_PCB_CONFIG{ - INDEX_SELFPOWER_DIGITAL_ONLY = 0x0, - INDEX_SELFPOWER_DUAL_DIGITAL, - INDEX_SELFPOWER_ANALOG_ONLY, - INDEX_SELFPOWER_DUAL, - INDEX_SELFPOWER_TRIPLE, - INDEX_SELFPOWER_COMPRESSOR, - INDEX_BUSPOWER_DIGITAL_ONLY, - INDEX_BUSPOWER_ANALOG_ONLY, - INDEX_BUSPOWER_DIF_ONLY, - INDEX_BUSPOWER_EXTERNAL_ONLY, - INDEX_BUSPOWER_EXTERNAL_ANALOG, - INDEX_BUSPOWER_EXTERNAL_DIF, - INDEX_BUSPOWER_EXTERNAL_DIGITAL, - INDEX_BUSPOWER_DIGITAL_ANALOG, - INDEX_BUSPOWER_DIGITAL_DIF, - INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL, - INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL, -}; - -/***************************************************************************/ -struct cx231xx; - -u32 initialize_cx231xx(struct cx231xx *p_dev); - -#endif diff --git a/drivers/media/video/cx231xx/cx231xx-reg.h b/drivers/media/video/cx231xx/cx231xx-reg.h deleted file mode 100644 index 750c5d37d569..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-reg.h +++ /dev/null @@ -1,1564 +0,0 @@ -/* - cx231xx-reg.h - driver for Conexant Cx23100/101/102 - USB video capture devices - - Copyright (C) 2008 - - This program is free software; 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 _CX231XX_REG_H -#define _CX231XX_REG_H - -/***************************************************************************** - * VBI codes * -*****************************************************************************/ - -#define SAV_ACTIVE_VIDEO_FIELD1 0x80 -#define EAV_ACTIVE_VIDEO_FIELD1 0x90 - -#define SAV_ACTIVE_VIDEO_FIELD2 0xc0 -#define EAV_ACTIVE_VIDEO_FIELD2 0xd0 - -#define SAV_VBLANK_FIELD1 0xa0 -#define EAV_VBLANK_FIELD1 0xb0 - -#define SAV_VBLANK_FIELD2 0xe0 -#define EAV_VBLANK_FIELD2 0xf0 - -#define SAV_VBI_FIELD1 0x20 -#define EAV_VBI_FIELD1 0x30 - -#define SAV_VBI_FIELD2 0x60 -#define EAV_VBI_FIELD2 0x70 - -/*****************************************************************************/ -/* Audio ADC Registers */ -#define CH_PWR_CTRL1 0x0000000e -#define CH_PWR_CTRL2 0x0000000f -/*****************************************************************************/ - -#define HOST_REG1 0x000 -#define FLD_FORCE_CHIP_SEL 0x80 -#define FLD_AUTO_INC_DIS 0x20 -#define FLD_PREFETCH_EN 0x10 -/* Reserved [2:3] */ -#define FLD_DIGITAL_PWR_DN 0x02 -#define FLD_SLEEP 0x01 - -/*****************************************************************************/ -#define HOST_REG2 0x001 - -/*****************************************************************************/ -#define HOST_REG3 0x002 - -/*****************************************************************************/ -/* added for polaris */ -#define GPIO_PIN_CTL0 0x3 -#define GPIO_PIN_CTL1 0x4 -#define GPIO_PIN_CTL2 0x5 -#define GPIO_PIN_CTL3 0x6 -#define TS1_PIN_CTL0 0x7 -#define TS1_PIN_CTL1 0x8 -/*****************************************************************************/ - -#define FLD_CLK_IN_EN 0x80 -#define FLD_XTAL_CTRL 0x70 -#define FLD_BB_CLK_MODE 0x0C -#define FLD_REF_DIV_PLL 0x02 -#define FLD_REF_SEL_PLL1 0x01 - -/*****************************************************************************/ -#define CHIP_CTRL 0x100 -/* Reserved [27] */ -/* Reserved [31:21] */ -#define FLD_CHIP_ACFG_DIS 0x00100000 -/* Reserved [19] */ -#define FLD_DUAL_MODE_ADC2 0x00040000 -#define FLD_SIF_EN 0x00020000 -#define FLD_SOFT_RST 0x00010000 -#define FLD_DEVICE_ID 0x0000ffff - -/*****************************************************************************/ -#define AFE_CTRL 0x104 -#define AFE_CTRL_C2HH_SRC_CTRL 0x104 -#define FLD_DIF_OUT_SEL 0xc0000000 -#define FLD_AUX_PLL_CLK_ALT_SEL 0x3c000000 -#define FLD_UV_ORDER_MODE 0x02000000 -#define FLD_FUNC_MODE 0x01800000 -#define FLD_ROT1_PHASE_CTL 0x007f8000 -#define FLD_AUD_IN_SEL 0x00004000 -#define FLD_LUMA_IN_SEL 0x00002000 -#define FLD_CHROMA_IN_SEL 0x00001000 -/* reserve [11:10] */ -#define FLD_INV_SPEC_DIS 0x00000200 -#define FLD_VGA_SEL_CH3 0x00000100 -#define FLD_VGA_SEL_CH2 0x00000080 -#define FLD_VGA_SEL_CH1 0x00000040 -#define FLD_DCR_BYP_CH1 0x00000020 -#define FLD_DCR_BYP_CH2 0x00000010 -#define FLD_DCR_BYP_CH3 0x00000008 -#define FLD_EN_12DB_CH3 0x00000004 -#define FLD_EN_12DB_CH2 0x00000002 -#define FLD_EN_12DB_CH1 0x00000001 - -/* redefine in Cx231xx */ -/*****************************************************************************/ -#define DC_CTRL1 0x108 -/* reserve [31:30] */ -#define FLD_CLAMP_LVL_CH1 0x3fff8000 -#define FLD_CLAMP_LVL_CH2 0x00007fff -/*****************************************************************************/ - -/*****************************************************************************/ -#define DC_CTRL2 0x10c -/* reserve [31:28] */ -#define FLD_CLAMP_LVL_CH3 0x00fffe00 -#define FLD_CLAMP_WIND_LENTH 0x000001e0 -#define FLD_C2HH_SAT_MIN 0x0000001e -#define FLD_FLT_BYP_SEL 0x00000001 -/*****************************************************************************/ - -/*****************************************************************************/ -#define DC_CTRL3 0x110 -/* reserve [31:16] */ -#define FLD_ERR_GAIN_CTL 0x00070000 -#define FLD_LPF_MIN 0x0000ffff -/*****************************************************************************/ - -/*****************************************************************************/ -#define DC_CTRL4 0x114 -/* reserve [31:31] */ -#define FLD_INTG_CH1 0x7fffffff -/*****************************************************************************/ - -/*****************************************************************************/ -#define DC_CTRL5 0x118 -/* reserve [31:31] */ -#define FLD_INTG_CH2 0x7fffffff -/*****************************************************************************/ - -/*****************************************************************************/ -#define DC_CTRL6 0x11c -/* reserve [31:31] */ -#define FLD_INTG_CH3 0x7fffffff -/*****************************************************************************/ - -/*****************************************************************************/ -#define PIN_CTRL 0x120 -#define FLD_OEF_AGC_RF 0x00000001 -#define FLD_OEF_AGC_IFVGA 0x00000002 -#define FLD_OEF_AGC_IF 0x00000004 -#define FLD_REG_BO_PUD 0x80000000 -#define FLD_IR_IRQ_STAT 0x40000000 -#define FLD_AUD_IRQ_STAT 0x20000000 -#define FLD_VID_IRQ_STAT 0x10000000 -/* Reserved [27:26] */ -#define FLD_IRQ_N_OUT_EN 0x02000000 -#define FLD_IRQ_N_POLAR 0x01000000 -/* Reserved [23:6] */ -#define FLD_OE_AUX_PLL_CLK 0x00000020 -#define FLD_OE_I2S_BCLK 0x00000010 -#define FLD_OE_I2S_WCLK 0x00000008 -#define FLD_OE_AGC_IF 0x00000004 -#define FLD_OE_AGC_IFVGA 0x00000002 -#define FLD_OE_AGC_RF 0x00000001 - -/*****************************************************************************/ -#define AUD_IO_CTRL 0x124 -/* Reserved [31:8] */ -#define FLD_I2S_PORT_DIR 0x00000080 -#define FLD_I2S_OUT_SRC 0x00000040 -#define FLD_AUD_CHAN3_SRC 0x00000030 -#define FLD_AUD_CHAN2_SRC 0x0000000c -#define FLD_AUD_CHAN1_SRC 0x00000003 - -/*****************************************************************************/ -#define AUD_LOCK1 0x128 -#define FLD_AUD_LOCK_KI_SHIFT 0xc0000000 -#define FLD_AUD_LOCK_KD_SHIFT 0x30000000 -/* Reserved [27:25] */ -#define FLD_EN_AV_LOCK 0x01000000 -#define FLD_VID_COUNT 0x00ffffff - -/*****************************************************************************/ -#define AUD_LOCK2 0x12c -#define FLD_AUD_LOCK_KI_MULT 0xf0000000 -#define FLD_AUD_LOCK_KD_MULT 0x0F000000 -/* Reserved [23:22] */ -#define FLD_AUD_LOCK_FREQ_SHIFT 0x00300000 -#define FLD_AUD_COUNT 0x000fffff - -/*****************************************************************************/ -#define AFE_DIAG_CTRL1 0x134 -/* Reserved [31:16] */ -#define FLD_CUV_DLY_LENGTH 0x0000ff00 -#define FLD_YC_DLY_LENGTH 0x000000ff - -/*****************************************************************************/ -/* Poalris redefine */ -#define AFE_DIAG_CTRL3 0x138 -/* Reserved [31:26] */ -#define FLD_AUD_DUAL_FLAG_POL 0x02000000 -#define FLD_VID_DUAL_FLAG_POL 0x01000000 -/* Reserved [23:23] */ -#define FLD_COL_CLAMP_DIS_CH1 0x00400000 -#define FLD_COL_CLAMP_DIS_CH2 0x00200000 -#define FLD_COL_CLAMP_DIS_CH3 0x00100000 - -#define TEST_CTRL1 0x144 -/* Reserved [31:29] */ -#define FLD_LBIST_EN 0x10000000 -/* Reserved [27:10] */ -#define FLD_FI_BIST_INTR_R 0x0000200 -#define FLD_FI_BIST_INTR_L 0x0000100 -#define FLD_BIST_FAIL_AUD_PLL 0x0000080 -#define FLD_BIST_INTR_AUD_PLL 0x0000040 -#define FLD_BIST_FAIL_VID_PLL 0x0000020 -#define FLD_BIST_INTR_VID_PLL 0x0000010 -/* Reserved [3:1] */ -#define FLD_CIR_TEST_DIS 0x00000001 - -/*****************************************************************************/ -#define TEST_CTRL2 0x148 -#define FLD_TSXCLK_POL_CTL 0x80000000 -#define FLD_ISO_CTL_SEL 0x40000000 -#define FLD_ISO_CTL_EN 0x20000000 -#define FLD_BIST_DEBUGZ 0x10000000 -#define FLD_AUD_BIST_TEST_H 0x0f000000 -/* Reserved [23:22] */ -#define FLD_FLTRN_BIST_TEST_H 0x00020000 -#define FLD_VID_BIST_TEST_H 0x00010000 -/* Reserved [19:17] */ -#define FLD_BIST_TEST_H 0x00010000 -/* Reserved [15:13] */ -#define FLD_TAB_EN 0x00001000 -/* Reserved [11:0] */ - -/*****************************************************************************/ -#define BIST_STAT 0x14c -#define FLD_AUD_BIST_FAIL_H 0xfff00000 -#define FLD_FLTRN_BIST_FAIL_H 0x00180000 -#define FLD_VID_BIST_FAIL_H 0x00070000 -#define FLD_AUD_BIST_TST_DONE 0x0000fff0 -#define FLD_FLTRN_BIST_TST_DONE 0x00000008 -#define FLD_VID_BIST_TST_DONE 0x00000007 - -/*****************************************************************************/ -/* DirectIF registers definition have been moved to DIF_reg.h */ -/*****************************************************************************/ -#define MODE_CTRL 0x400 -#define FLD_AFD_PAL60_DIS 0x20000000 -#define FLD_AFD_FORCE_SECAM 0x10000000 -#define FLD_AFD_FORCE_PALNC 0x08000000 -#define FLD_AFD_FORCE_PAL 0x04000000 -#define FLD_AFD_PALM_SEL 0x03000000 -#define FLD_CKILL_MODE 0x00300000 -#define FLD_COMB_NOTCH_MODE 0x00c00000 /* bit[19:18] */ -#define FLD_CLR_LOCK_STAT 0x00020000 -#define FLD_FAST_LOCK_MD 0x00010000 -#define FLD_WCEN 0x00008000 -#define FLD_CAGCEN 0x00004000 -#define FLD_CKILLEN 0x00002000 -#define FLD_AUTO_SC_LOCK 0x00001000 -#define FLD_MAN_SC_FAST_LOCK 0x00000800 -#define FLD_INPUT_MODE 0x00000600 -#define FLD_AFD_ACQUIRE 0x00000100 -#define FLD_AFD_NTSC_SEL 0x00000080 -#define FLD_AFD_PAL_SEL 0x00000040 -#define FLD_ACFG_DIS 0x00000020 -#define FLD_SQ_PIXEL 0x00000010 -#define FLD_VID_FMT_SEL 0x0000000f - -/*****************************************************************************/ -#define OUT_CTRL1 0x404 -#define FLD_POLAR 0x7f000000 -/* Reserved [23] */ -#define FLD_RND_MODE 0x00600000 -#define FLD_VIPCLAMP_EN 0x00100000 -#define FLD_VIPBLANK_EN 0x00080000 -#define FLD_VIP_OPT_AL 0x00040000 -#define FLD_IDID0_SOURCE 0x00020000 -#define FLD_DCMODE 0x00010000 -#define FLD_CLK_GATING 0x0000c000 -#define FLD_CLK_INVERT 0x00002000 -#define FLD_HSFMT 0x00001000 -#define FLD_VALIDFMT 0x00000800 -#define FLD_ACTFMT 0x00000400 -#define FLD_SWAPRAW 0x00000200 -#define FLD_CLAMPRAW_EN 0x00000100 -#define FLD_BLUE_FIELD_EN 0x00000080 -#define FLD_BLUE_FIELD_ACT 0x00000040 -#define FLD_TASKBIT_VAL 0x00000020 -#define FLD_ANC_DATA_EN 0x00000010 -#define FLD_VBIHACTRAW_EN 0x00000008 -#define FLD_MODE10B 0x00000004 -#define FLD_OUT_MODE 0x00000003 - -/*****************************************************************************/ -#define OUT_CTRL2 0x408 -#define FLD_AUD_GRP 0xc0000000 -#define FLD_SAMPLE_RATE 0x30000000 -#define FLD_AUD_ANC_EN 0x08000000 -#define FLD_EN_C 0x04000000 -#define FLD_EN_B 0x02000000 -#define FLD_EN_A 0x01000000 -/* Reserved [23:20] */ -#define FLD_IDID1_LSB 0x000c0000 -#define FLD_IDID0_LSB 0x00030000 -#define FLD_IDID1_MSB 0x0000ff00 -#define FLD_IDID0_MSB 0x000000ff - -/*****************************************************************************/ -#define GEN_STAT 0x40c -#define FLD_VCR_DETECT 0x00800000 -#define FLD_SPECIAL_PLAY_N 0x00400000 -#define FLD_VPRES 0x00200000 -#define FLD_AGC_LOCK 0x00100000 -#define FLD_CSC_LOCK 0x00080000 -#define FLD_VLOCK 0x00040000 -#define FLD_SRC_LOCK 0x00020000 -#define FLD_HLOCK 0x00010000 -#define FLD_VSYNC_N 0x00008000 -#define FLD_SRC_FIFO_UFLOW 0x00004000 -#define FLD_SRC_FIFO_OFLOW 0x00002000 -#define FLD_FIELD 0x00001000 -#define FLD_AFD_FMT_STAT 0x00000f00 -#define FLD_MV_TYPE2_PAIR 0x00000080 -#define FLD_MV_T3CS 0x00000040 -#define FLD_MV_CS 0x00000020 -#define FLD_MV_PSP 0x00000010 -/* Reserved [3] */ -#define FLD_MV_CDAT 0x00000003 - -/*****************************************************************************/ -#define INT_STAT_MASK 0x410 -#define FLD_COMB_3D_FIFO_MSK 0x80000000 -#define FLD_WSS_DAT_AVAIL_MSK 0x40000000 -#define FLD_GS2_DAT_AVAIL_MSK 0x20000000 -#define FLD_GS1_DAT_AVAIL_MSK 0x10000000 -#define FLD_CC_DAT_AVAIL_MSK 0x08000000 -#define FLD_VPRES_CHANGE_MSK 0x04000000 -#define FLD_MV_CHANGE_MSK 0x02000000 -#define FLD_END_VBI_EVEN_MSK 0x01000000 -#define FLD_END_VBI_ODD_MSK 0x00800000 -#define FLD_FMT_CHANGE_MSK 0x00400000 -#define FLD_VSYNC_TRAIL_MSK 0x00200000 -#define FLD_HLOCK_CHANGE_MSK 0x00100000 -#define FLD_VLOCK_CHANGE_MSK 0x00080000 -#define FLD_CSC_LOCK_CHANGE_MSK 0x00040000 -#define FLD_SRC_FIFO_UFLOW_MSK 0x00020000 -#define FLD_SRC_FIFO_OFLOW_MSK 0x00010000 -#define FLD_COMB_3D_FIFO_STAT 0x00008000 -#define FLD_WSS_DAT_AVAIL_STAT 0x00004000 -#define FLD_GS2_DAT_AVAIL_STAT 0x00002000 -#define FLD_GS1_DAT_AVAIL_STAT 0x00001000 -#define FLD_CC_DAT_AVAIL_STAT 0x00000800 -#define FLD_VPRES_CHANGE_STAT 0x00000400 -#define FLD_MV_CHANGE_STAT 0x00000200 -#define FLD_END_VBI_EVEN_STAT 0x00000100 -#define FLD_END_VBI_ODD_STAT 0x00000080 -#define FLD_FMT_CHANGE_STAT 0x00000040 -#define FLD_VSYNC_TRAIL_STAT 0x00000020 -#define FLD_HLOCK_CHANGE_STAT 0x00000010 -#define FLD_VLOCK_CHANGE_STAT 0x00000008 -#define FLD_CSC_LOCK_CHANGE_STAT 0x00000004 -#define FLD_SRC_FIFO_UFLOW_STAT 0x00000002 -#define FLD_SRC_FIFO_OFLOW_STAT 0x00000001 - -/*****************************************************************************/ -#define LUMA_CTRL 0x414 -#define BRIGHTNESS_CTRL_BYTE 0x414 -#define CONTRAST_CTRL_BYTE 0x415 -#define LUMA_CTRL_BYTE_3 0x416 -#define FLD_LUMA_CORE_SEL 0x00c00000 -#define FLD_RANGE 0x00300000 -/* Reserved [19] */ -#define FLD_PEAK_EN 0x00040000 -#define FLD_PEAK_SEL 0x00030000 -#define FLD_CNTRST 0x0000ff00 -#define FLD_BRITE 0x000000ff - -/*****************************************************************************/ -#define HSCALE_CTRL 0x418 -#define FLD_HFILT 0x03000000 -#define FLD_HSCALE 0x00ffffff - -/*****************************************************************************/ -#define VSCALE_CTRL 0x41c -#define FLD_LINE_AVG_DIS 0x01000000 -/* Reserved [23:20] */ -#define FLD_VS_INTRLACE 0x00080000 -#define FLD_VFILT 0x00070000 -/* Reserved [15:13] */ -#define FLD_VSCALE 0x00001fff - -/*****************************************************************************/ -#define CHROMA_CTRL 0x420 -#define USAT_CTRL_BYTE 0x420 -#define VSAT_CTRL_BYTE 0x421 -#define HUE_CTRL_BYTE 0x422 -#define FLD_C_LPF_EN 0x20000000 -#define FLD_CHR_DELAY 0x1c000000 -#define FLD_C_CORE_SEL 0x03000000 -#define FLD_HUE 0x00ff0000 -#define FLD_VSAT 0x0000ff00 -#define FLD_USAT 0x000000ff - -/*****************************************************************************/ -#define VBI_LINE_CTRL1 0x424 -#define FLD_VBI_MD_LINE4 0xff000000 -#define FLD_VBI_MD_LINE3 0x00ff0000 -#define FLD_VBI_MD_LINE2 0x0000ff00 -#define FLD_VBI_MD_LINE1 0x000000ff - -/*****************************************************************************/ -#define VBI_LINE_CTRL2 0x428 -#define FLD_VBI_MD_LINE8 0xff000000 -#define FLD_VBI_MD_LINE7 0x00ff0000 -#define FLD_VBI_MD_LINE6 0x0000ff00 -#define FLD_VBI_MD_LINE5 0x000000ff - -/*****************************************************************************/ -#define VBI_LINE_CTRL3 0x42c -#define FLD_VBI_MD_LINE12 0xff000000 -#define FLD_VBI_MD_LINE11 0x00ff0000 -#define FLD_VBI_MD_LINE10 0x0000ff00 -#define FLD_VBI_MD_LINE9 0x000000ff - -/*****************************************************************************/ -#define VBI_LINE_CTRL4 0x430 -#define FLD_VBI_MD_LINE16 0xff000000 -#define FLD_VBI_MD_LINE15 0x00ff0000 -#define FLD_VBI_MD_LINE14 0x0000ff00 -#define FLD_VBI_MD_LINE13 0x000000ff - -/*****************************************************************************/ -#define VBI_LINE_CTRL5 0x434 -#define FLD_VBI_MD_LINE17 0x000000ff - -/*****************************************************************************/ -#define VBI_FC_CFG 0x438 -#define FLD_FC_ALT2 0xff000000 -#define FLD_FC_ALT1 0x00ff0000 -#define FLD_FC_ALT2_TYPE 0x0000f000 -#define FLD_FC_ALT1_TYPE 0x00000f00 -/* Reserved [7:1] */ -#define FLD_FC_SEARCH_MODE 0x00000001 - -/*****************************************************************************/ -#define VBI_MISC_CFG1 0x43c -#define FLD_TTX_PKTADRU 0xfff00000 -#define FLD_TTX_PKTADRL 0x000fff00 -/* Reserved [7:6] */ -#define FLD_MOJI_PACK_DIS 0x00000020 -#define FLD_VPS_DEC_DIS 0x00000010 -#define FLD_CRI_MARG_SCALE 0x0000000c -#define FLD_EDGE_RESYNC_EN 0x00000002 -#define FLD_ADAPT_SLICE_DIS 0x00000001 - -/*****************************************************************************/ -#define VBI_MISC_CFG2 0x440 -#define FLD_HAMMING_TYPE 0x0f000000 -/* Reserved [23:20] */ -#define FLD_WSS_FIFO_RST 0x00080000 -#define FLD_GS2_FIFO_RST 0x00040000 -#define FLD_GS1_FIFO_RST 0x00020000 -#define FLD_CC_FIFO_RST 0x00010000 -/* Reserved [15:12] */ -#define FLD_VBI3_SDID 0x00000f00 -#define FLD_VBI2_SDID 0x000000f0 -#define FLD_VBI1_SDID 0x0000000f - -/*****************************************************************************/ -#define VBI_PAY1 0x444 -#define FLD_GS1_FIFO_DAT 0xFF000000 -#define FLD_GS1_STAT 0x00FF0000 -#define FLD_CC_FIFO_DAT 0x0000FF00 -#define FLD_CC_STAT 0x000000FF - -/*****************************************************************************/ -#define VBI_PAY2 0x448 -#define FLD_WSS_FIFO_DAT 0xff000000 -#define FLD_WSS_STAT 0x00ff0000 -#define FLD_GS2_FIFO_DAT 0x0000ff00 -#define FLD_GS2_STAT 0x000000ff - -/*****************************************************************************/ -#define VBI_CUST1_CFG1 0x44c -/* Reserved [31] */ -#define FLD_VBI1_CRIWIN 0x7f000000 -#define FLD_VBI1_SLICE_DIST 0x00f00000 -#define FLD_VBI1_BITINC 0x000fff00 -#define FLD_VBI1_HDELAY 0x000000ff - -/*****************************************************************************/ -#define VBI_CUST1_CFG2 0x450 -#define FLD_VBI1_FC_LENGTH 0x1f000000 -#define FLD_VBI1_FRAME_CODE 0x00ffffff - -/*****************************************************************************/ -#define VBI_CUST1_CFG3 0x454 -#define FLD_VBI1_HAM_EN 0x80000000 -#define FLD_VBI1_FIFO_MODE 0x70000000 -#define FLD_VBI1_FORMAT_TYPE 0x0f000000 -#define FLD_VBI1_PAYLD_LENGTH 0x00ff0000 -#define FLD_VBI1_CRI_LENGTH 0x0000f000 -#define FLD_VBI1_CRI_MARGIN 0x00000f00 -#define FLD_VBI1_CRI_TIME 0x000000ff - -/*****************************************************************************/ -#define VBI_CUST2_CFG1 0x458 -/* Reserved [31] */ -#define FLD_VBI2_CRIWIN 0x7f000000 -#define FLD_VBI2_SLICE_DIST 0x00f00000 -#define FLD_VBI2_BITINC 0x000fff00 -#define FLD_VBI2_HDELAY 0x000000ff - -/*****************************************************************************/ -#define VBI_CUST2_CFG2 0x45c -#define FLD_VBI2_FC_LENGTH 0x1f000000 -#define FLD_VBI2_FRAME_CODE 0x00ffffff - -/*****************************************************************************/ -#define VBI_CUST2_CFG3 0x460 -#define FLD_VBI2_HAM_EN 0x80000000 -#define FLD_VBI2_FIFO_MODE 0x70000000 -#define FLD_VBI2_FORMAT_TYPE 0x0f000000 -#define FLD_VBI2_PAYLD_LENGTH 0x00ff0000 -#define FLD_VBI2_CRI_LENGTH 0x0000f000 -#define FLD_VBI2_CRI_MARGIN 0x00000f00 -#define FLD_VBI2_CRI_TIME 0x000000ff - -/*****************************************************************************/ -#define VBI_CUST3_CFG1 0x464 -/* Reserved [31] */ -#define FLD_VBI3_CRIWIN 0x7f000000 -#define FLD_VBI3_SLICE_DIST 0x00f00000 -#define FLD_VBI3_BITINC 0x000fff00 -#define FLD_VBI3_HDELAY 0x000000ff - -/*****************************************************************************/ -#define VBI_CUST3_CFG2 0x468 -#define FLD_VBI3_FC_LENGTH 0x1f000000 -#define FLD_VBI3_FRAME_CODE 0x00ffffff - -/*****************************************************************************/ -#define VBI_CUST3_CFG3 0x46c -#define FLD_VBI3_HAM_EN 0x80000000 -#define FLD_VBI3_FIFO_MODE 0x70000000 -#define FLD_VBI3_FORMAT_TYPE 0x0f000000 -#define FLD_VBI3_PAYLD_LENGTH 0x00ff0000 -#define FLD_VBI3_CRI_LENGTH 0x0000f000 -#define FLD_VBI3_CRI_MARGIN 0x00000f00 -#define FLD_VBI3_CRI_TIME 0x000000ff - -/*****************************************************************************/ -#define HORIZ_TIM_CTRL 0x470 -#define FLD_BGDEL_CNT 0xff000000 -/* Reserved [23:22] */ -#define FLD_HACTIVE_CNT 0x003ff000 -/* Reserved [11:10] */ -#define FLD_HBLANK_CNT 0x000003ff - -/*****************************************************************************/ -#define VERT_TIM_CTRL 0x474 -#define FLD_V656BLANK_CNT 0xff000000 -/* Reserved [23:22] */ -#define FLD_VACTIVE_CNT 0x003ff000 -/* Reserved [11:10] */ -#define FLD_VBLANK_CNT 0x000003ff - -/*****************************************************************************/ -#define SRC_COMB_CFG 0x478 -#define FLD_CCOMB_2LN_CHECK 0x80000000 -#define FLD_CCOMB_3LN_EN 0x40000000 -#define FLD_CCOMB_2LN_EN 0x20000000 -#define FLD_CCOMB_3D_EN 0x10000000 -/* Reserved [27] */ -#define FLD_LCOMB_3LN_EN 0x04000000 -#define FLD_LCOMB_2LN_EN 0x02000000 -#define FLD_LCOMB_3D_EN 0x01000000 -#define FLD_LUMA_LPF_SEL 0x00c00000 -#define FLD_UV_LPF_SEL 0x00300000 -#define FLD_BLEND_SLOPE 0x000f0000 -#define FLD_CCOMB_REDUCE_EN 0x00008000 -/* Reserved [14:10] */ -#define FLD_SRC_DECIM_RATIO 0x000003ff - -/*****************************************************************************/ -#define CHROMA_VBIOFF_CFG 0x47c -#define FLD_VBI_VOFFSET 0x1f000000 -/* Reserved [23:20] */ -#define FLD_SC_STEP 0x000fffff - -/*****************************************************************************/ -#define FIELD_COUNT 0x480 -#define FLD_FIELD_COUNT_FLD 0x000003ff - -/*****************************************************************************/ -#define MISC_TIM_CTRL 0x484 -#define FLD_DEBOUNCE_COUNT 0xc0000000 -#define FLD_VT_LINE_CNT_HYST 0x30000000 -/* Reserved [27] */ -#define FLD_AFD_STAT 0x07ff0000 -#define FLD_VPRES_VERT_EN 0x00008000 -/* Reserved [14:12] */ -#define FLD_HR32 0x00000800 -#define FLD_TDALGN 0x00000400 -#define FLD_TDFIELD 0x00000200 -/* Reserved [8:6] */ -#define FLD_TEMPDEC 0x0000003f - -/*****************************************************************************/ -#define DFE_CTRL1 0x488 -#define FLD_CLAMP_AUTO_EN 0x80000000 -#define FLD_AGC_AUTO_EN 0x40000000 -#define FLD_VGA_CRUSH_EN 0x20000000 -#define FLD_VGA_AUTO_EN 0x10000000 -#define FLD_VBI_GATE_EN 0x08000000 -#define FLD_CLAMP_LEVEL 0x07000000 -/* Reserved [23:22] */ -#define FLD_CLAMP_SKIP_CNT 0x00300000 -#define FLD_AGC_GAIN 0x000fff00 -/* Reserved [7:6] */ -#define FLD_VGA_GAIN 0x0000003f - -/*****************************************************************************/ -#define DFE_CTRL2 0x48c -#define FLD_VGA_ACQUIRE_RANGE 0x00ff0000 -#define FLD_VGA_TRACK_RANGE 0x0000ff00 -#define FLD_VGA_SYNC 0x000000ff - -/*****************************************************************************/ -#define DFE_CTRL3 0x490 -#define FLD_BP_PERCENT 0xff000000 -#define FLD_DFT_THRESHOLD 0x00ff0000 -/* Reserved [15:12] */ -#define FLD_SYNC_WIDTH_SEL 0x00000600 -#define FLD_BP_LOOP_GAIN 0x00000300 -#define FLD_SYNC_LOOP_GAIN 0x000000c0 -/* Reserved [5:4] */ -#define FLD_AGC_LOOP_GAIN 0x0000000c -#define FLD_DCC_LOOP_GAIN 0x00000003 - -/*****************************************************************************/ -#define PLL_CTRL 0x494 -#define FLD_PLL_KD 0xff000000 -#define FLD_PLL_KI 0x00ff0000 -#define FLD_PLL_MAX_OFFSET 0x0000ffff - -/*****************************************************************************/ -#define HTL_CTRL 0x498 -/* Reserved [31:24] */ -#define FLD_AUTO_LOCK_SPD 0x00080000 -#define FLD_MAN_FAST_LOCK 0x00040000 -#define FLD_HTL_15K_EN 0x00020000 -#define FLD_HTL_500K_EN 0x00010000 -#define FLD_HTL_KD 0x0000ff00 -#define FLD_HTL_KI 0x000000ff - -/*****************************************************************************/ -#define COMB_CTRL 0x49c -#define FLD_COMB_PHASE_LIMIT 0xff000000 -#define FLD_CCOMB_ERR_LIMIT 0x00ff0000 -#define FLD_LUMA_THRESHOLD 0x0000ff00 -#define FLD_LCOMB_ERR_LIMIT 0x000000ff - -/*****************************************************************************/ -#define CRUSH_CTRL 0x4a0 -#define FLD_WTW_EN 0x00400000 -#define FLD_CRUSH_FREQ 0x00200000 -#define FLD_MAJ_SEL_EN 0x00100000 -#define FLD_MAJ_SEL 0x000c0000 -/* Reserved [17:15] */ -#define FLD_SYNC_TIP_REDUCE 0x00007e00 -/* Reserved [8:6] */ -#define FLD_SYNC_TIP_INC 0x0000003f - -/*****************************************************************************/ -#define SOFT_RST_CTRL 0x4a4 -#define FLD_VD_SOFT_RST 0x00008000 -/* Reserved [14:12] */ -#define FLD_REG_RST_MSK 0x00000800 -#define FLD_VOF_RST_MSK 0x00000400 -#define FLD_MVDET_RST_MSK 0x00000200 -#define FLD_VBI_RST_MSK 0x00000100 -#define FLD_SCALE_RST_MSK 0x00000080 -#define FLD_CHROMA_RST_MSK 0x00000040 -#define FLD_LUMA_RST_MSK 0x00000020 -#define FLD_VTG_RST_MSK 0x00000010 -#define FLD_YCSEP_RST_MSK 0x00000008 -#define FLD_SRC_RST_MSK 0x00000004 -#define FLD_DFE_RST_MSK 0x00000002 -/* Reserved [0] */ - -/*****************************************************************************/ -#define MV_DT_CTRL1 0x4a8 -/* Reserved [31:29] */ -#define FLD_PSP_STOP_LINE 0x1f000000 -/* Reserved [23:21] */ -#define FLD_PSP_STRT_LINE 0x001f0000 -/* Reserved [15] */ -#define FLD_PSP_LLIMW 0x00007f00 -/* Reserved [7] */ -#define FLD_PSP_ULIMW 0x0000007f - -/*****************************************************************************/ -#define MV_DT_CTRL2 0x4aC -#define FLD_CS_STOPWIN 0xff000000 -#define FLD_CS_STRTWIN 0x00ff0000 -#define FLD_CS_WIDTH 0x0000ff00 -#define FLD_PSP_SPEC_VAL 0x000000ff - -/*****************************************************************************/ -#define MV_DT_CTRL3 0x4B0 -#define FLD_AUTO_RATE_DIS 0x80000000 -#define FLD_HLOCK_DIS 0x40000000 -#define FLD_SEL_FIELD_CNT 0x20000000 -#define FLD_CS_TYPE2_SEL 0x10000000 -#define FLD_CS_LINE_THRSH_SEL 0x08000000 -#define FLD_CS_ATHRESH_SEL 0x04000000 -#define FLD_PSP_SPEC_SEL 0x02000000 -#define FLD_PSP_LINES_SEL 0x01000000 -#define FLD_FIELD_CNT 0x00f00000 -#define FLD_CS_TYPE2_CNT 0x000fc000 -#define FLD_CS_LINE_CNT 0x00003f00 -#define FLD_CS_ATHRESH_LEV 0x000000ff - -/*****************************************************************************/ -#define CHIP_VERSION 0x4b4 -/* Cx231xx redefine */ -#define VERSION 0x4b4 -#define FLD_REV_ID 0x000000ff - -/*****************************************************************************/ -#define MISC_DIAG_CTRL 0x4b8 -/* Reserved [31:24] */ -#define FLD_SC_CONVERGE_THRESH 0x00ff0000 -#define FLD_CCOMB_ERR_LIMIT_3D 0x0000ff00 -#define FLD_LCOMB_ERR_LIMIT_3D 0x000000ff - -/*****************************************************************************/ -#define VBI_PASS_CTRL 0x4bc -#define FLD_VBI_PASS_MD 0x00200000 -#define FLD_VBI_SETUP_DIS 0x00100000 -#define FLD_PASS_LINE_CTRL 0x000fffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define VCR_DET_CTRL 0x4c0 -#define FLD_EN_FIELD_PHASE_DET 0x80000000 -#define FLD_EN_HEAD_SW_DET 0x40000000 -#define FLD_FIELD_PHASE_LENGTH 0x01ff0000 -/* Reserved [29:25] */ -#define FLD_FIELD_PHASE_DELAY 0x0000ff00 -#define FLD_FIELD_PHASE_LIMIT 0x000000f0 -#define FLD_HEAD_SW_DET_LIMIT 0x0000000f - -/*****************************************************************************/ -#define DL_CTL 0x800 -#define DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */ -#define DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */ -#define DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */ -#define DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */ -/* Reserved [31:5] */ -#define FLD_START_8051 0x10000000 -#define FLD_DL_ENABLE 0x08000000 -#define FLD_DL_AUTO_INC 0x04000000 -#define FLD_DL_MAP 0x03000000 - -/*****************************************************************************/ -#define STD_DET_STATUS 0x804 -#define FLD_SPARE_STATUS1 0xff000000 -#define FLD_SPARE_STATUS0 0x00ff0000 -#define FLD_MOD_DET_STATUS1 0x0000ff00 -#define FLD_MOD_DET_STATUS0 0x000000ff - -/*****************************************************************************/ -#define AUD_BUILD_NUM 0x806 -#define AUD_VER_NUM 0x807 -#define STD_DET_CTL 0x808 -#define STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */ -#define STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */ -#define FLD_SPARE_CTL0 0xff000000 -#define FLD_DIS_DBX 0x00800000 -#define FLD_DIS_BTSC 0x00400000 -#define FLD_DIS_NICAM_A2 0x00200000 -#define FLD_VIDEO_PRESENT 0x00100000 -#define FLD_DW8051_VIDEO_FORMAT 0x000f0000 -#define FLD_PREF_DEC_MODE 0x0000ff00 -#define FLD_AUD_CONFIG 0x000000ff - -/*****************************************************************************/ -#define DW8051_INT 0x80c -#define FLD_VIDEO_PRESENT_CHANGE 0x80000000 -#define FLD_VIDEO_CHANGE 0x40000000 -#define FLD_RDS_READY 0x20000000 -#define FLD_AC97_INT 0x10000000 -#define FLD_NICAM_BIT_ERROR_TOO_HIGH 0x08000000 -#define FLD_NICAM_LOCK 0x04000000 -#define FLD_NICAM_UNLOCK 0x02000000 -#define FLD_DFT4_TH_CMP 0x01000000 -/* Reserved [23:22] */ -#define FLD_LOCK_IND_INT 0x00200000 -#define FLD_DFT3_TH_CMP 0x00100000 -#define FLD_DFT2_TH_CMP 0x00080000 -#define FLD_DFT1_TH_CMP 0x00040000 -#define FLD_FM2_DFT_TH_CMP 0x00020000 -#define FLD_FM1_DFT_TH_CMP 0x00010000 -#define FLD_VIDEO_PRESENT_EN 0x00008000 -#define FLD_VIDEO_CHANGE_EN 0x00004000 -#define FLD_RDS_READY_EN 0x00002000 -#define FLD_AC97_INT_EN 0x00001000 -#define FLD_NICAM_BIT_ERROR_TOO_HIGH_EN 0x00000800 -#define FLD_NICAM_LOCK_EN 0x00000400 -#define FLD_NICAM_UNLOCK_EN 0x00000200 -#define FLD_DFT4_TH_CMP_EN 0x00000100 -/* Reserved [7] */ -#define FLD_DW8051_INT6_CTL1 0x00000040 -#define FLD_DW8051_INT5_CTL1 0x00000020 -#define FLD_DW8051_INT4_CTL1 0x00000010 -#define FLD_DW8051_INT3_CTL1 0x00000008 -#define FLD_DW8051_INT2_CTL1 0x00000004 -#define FLD_DW8051_INT1_CTL1 0x00000002 -#define FLD_DW8051_INT0_CTL1 0x00000001 - -/*****************************************************************************/ -#define GENERAL_CTL 0x810 -#define FLD_RDS_INT 0x80000000 -#define FLD_NBER_INT 0x40000000 -#define FLD_NLL_INT 0x20000000 -#define FLD_IFL_INT 0x10000000 -#define FLD_FDL_INT 0x08000000 -#define FLD_AFC_INT 0x04000000 -#define FLD_AMC_INT 0x02000000 -#define FLD_AC97_INT_CTL 0x01000000 -#define FLD_RDS_INT_DIS 0x00800000 -#define FLD_NBER_INT_DIS 0x00400000 -#define FLD_NLL_INT_DIS 0x00200000 -#define FLD_IFL_INT_DIS 0x00100000 -#define FLD_FDL_INT_DIS 0x00080000 -#define FLD_FC_INT_DIS 0x00040000 -#define FLD_AMC_INT_DIS 0x00020000 -#define FLD_AC97_INT_DIS 0x00010000 -#define FLD_REV_NUM 0x0000ff00 -/* Reserved [7:5] */ -#define FLD_DBX_SOFT_RESET_REG 0x00000010 -#define FLD_AD_SOFT_RESET_REG 0x00000008 -#define FLD_SRC_SOFT_RESET_REG 0x00000004 -#define FLD_CDMOD_SOFT_RESET 0x00000002 -#define FLD_8051_SOFT_RESET 0x00000001 - -/*****************************************************************************/ -#define AAGC_CTL 0x814 -#define FLD_AFE_12DB_EN 0x80000000 -#define FLD_AAGC_DEFAULT_EN 0x40000000 -#define FLD_AAGC_DEFAULT 0x3f000000 -/* Reserved [23] */ -#define FLD_AAGC_GAIN 0x00600000 -#define FLD_AAGC_TH 0x001f0000 -/* Reserved [15:14] */ -#define FLD_AAGC_HYST2 0x00003f00 -/* Reserved [7:6] */ -#define FLD_AAGC_HYST1 0x0000003f - -/*****************************************************************************/ -#define IF_SRC_CTL 0x818 -#define FLD_DBX_BYPASS 0x80000000 -/* Reserved [30:25] */ -#define FLD_IF_SRC_MODE 0x01000000 -/* Reserved [23:18] */ -#define FLD_IF_SRC_PHASE_INC 0x0001ffff - -/*****************************************************************************/ -#define ANALOG_DEMOD_CTL 0x81c -#define FLD_ROT1_PHACC_PROG 0xffff0000 -/* Reserved [15] */ -#define FLD_FM1_DELAY_FIX 0x00007000 -#define FLD_PDF4_SHIFT 0x00000c00 -#define FLD_PDF3_SHIFT 0x00000300 -#define FLD_PDF2_SHIFT 0x000000c0 -#define FLD_PDF1_SHIFT 0x00000030 -#define FLD_FMBYPASS_MODE2 0x00000008 -#define FLD_FMBYPASS_MODE1 0x00000004 -#define FLD_NICAM_MODE 0x00000002 -#define FLD_BTSC_FMRADIO_MODE 0x00000001 - -/*****************************************************************************/ -#define ROT_FREQ_CTL 0x820 -#define FLD_ROT3_PHACC_PROG 0xffff0000 -#define FLD_ROT2_PHACC_PROG 0x0000ffff - -/*****************************************************************************/ -#define FM_CTL 0x824 -#define FLD_FM2_DC_FB_SHIFT 0xf0000000 -#define FLD_FM2_DC_INT_SHIFT 0x0f000000 -#define FLD_FM2_AFC_RESET 0x00800000 -#define FLD_FM2_DC_PASS_IN 0x00400000 -#define FLD_FM2_DAGC_SHIFT 0x00380000 -#define FLD_FM2_CORDIC_SHIFT 0x00070000 -#define FLD_FM1_DC_FB_SHIFT 0x0000f000 -#define FLD_FM1_DC_INT_SHIFT 0x00000f00 -#define FLD_FM1_AFC_RESET 0x00000080 -#define FLD_FM1_DC_PASS_IN 0x00000040 -#define FLD_FM1_DAGC_SHIFT 0x00000038 -#define FLD_FM1_CORDIC_SHIFT 0x00000007 - -/*****************************************************************************/ -#define LPF_PDF_CTL 0x828 -/* Reserved [31:30] */ -#define FLD_LPF32_SHIFT1 0x30000000 -#define FLD_LPF32_SHIFT2 0x0c000000 -#define FLD_LPF160_SHIFTA 0x03000000 -#define FLD_LPF160_SHIFTB 0x00c00000 -#define FLD_LPF160_SHIFTC 0x00300000 -#define FLD_LPF32_COEF_SEL2 0x000c0000 -#define FLD_LPF32_COEF_SEL1 0x00030000 -#define FLD_LPF160_COEF_SELC 0x0000c000 -#define FLD_LPF160_COEF_SELB 0x00003000 -#define FLD_LPF160_COEF_SELA 0x00000c00 -#define FLD_LPF160_IN_EN_REG 0x00000300 -#define FLD_PDF4_PDF_SEL 0x000000c0 -#define FLD_PDF3_PDF_SEL 0x00000030 -#define FLD_PDF2_PDF_SEL 0x0000000c -#define FLD_PDF1_PDF_SEL 0x00000003 - -/*****************************************************************************/ -#define DFT1_CTL1 0x82c -#define FLD_DFT1_DWELL 0xffff0000 -#define FLD_DFT1_FREQ 0x0000ffff - -/*****************************************************************************/ -#define DFT1_CTL2 0x830 -#define FLD_DFT1_THRESHOLD 0xffffff00 -#define FLD_DFT1_CMP_CTL 0x00000080 -#define FLD_DFT1_AVG 0x00000070 -/* Reserved [3:1] */ -#define FLD_DFT1_START 0x00000001 - -/*****************************************************************************/ -#define DFT1_STATUS 0x834 -#define FLD_DFT1_DONE 0x80000000 -#define FLD_DFT1_TH_CMP_STAT 0x40000000 -#define FLD_DFT1_RESULT 0x3fffffff - -/*****************************************************************************/ -#define DFT2_CTL1 0x838 -#define FLD_DFT2_DWELL 0xffff0000 -#define FLD_DFT2_FREQ 0x0000ffff - -/*****************************************************************************/ -#define DFT2_CTL2 0x83C -#define FLD_DFT2_THRESHOLD 0xffffff00 -#define FLD_DFT2_CMP_CTL 0x00000080 -#define FLD_DFT2_AVG 0x00000070 -/* Reserved [3:1] */ -#define FLD_DFT2_START 0x00000001 - -/*****************************************************************************/ -#define DFT2_STATUS 0x840 -#define FLD_DFT2_DONE 0x80000000 -#define FLD_DFT2_TH_CMP_STAT 0x40000000 -#define FLD_DFT2_RESULT 0x3fffffff - -/*****************************************************************************/ -#define DFT3_CTL1 0x844 -#define FLD_DFT3_DWELL 0xffff0000 -#define FLD_DFT3_FREQ 0x0000ffff - -/*****************************************************************************/ -#define DFT3_CTL2 0x848 -#define FLD_DFT3_THRESHOLD 0xffffff00 -#define FLD_DFT3_CMP_CTL 0x00000080 -#define FLD_DFT3_AVG 0x00000070 -/* Reserved [3:1] */ -#define FLD_DFT3_START 0x00000001 - -/*****************************************************************************/ -#define DFT3_STATUS 0x84c -#define FLD_DFT3_DONE 0x80000000 -#define FLD_DFT3_TH_CMP_STAT 0x40000000 -#define FLD_DFT3_RESULT 0x3fffffff - -/*****************************************************************************/ -#define DFT4_CTL1 0x850 -#define FLD_DFT4_DWELL 0xffff0000 -#define FLD_DFT4_FREQ 0x0000ffff - -/*****************************************************************************/ -#define DFT4_CTL2 0x854 -#define FLD_DFT4_THRESHOLD 0xffffff00 -#define FLD_DFT4_CMP_CTL 0x00000080 -#define FLD_DFT4_AVG 0x00000070 -/* Reserved [3:1] */ -#define FLD_DFT4_START 0x00000001 - -/*****************************************************************************/ -#define DFT4_STATUS 0x858 -#define FLD_DFT4_DONE 0x80000000 -#define FLD_DFT4_TH_CMP_STAT 0x40000000 -#define FLD_DFT4_RESULT 0x3fffffff - -/*****************************************************************************/ -#define AM_MTS_DET 0x85c -#define FLD_AM_MTS_MODE 0x80000000 -/* Reserved [30:26] */ -#define FLD_AM_SUB 0x02000000 -#define FLD_AM_GAIN_EN 0x01000000 -/* Reserved [23:16] */ -#define FLD_AMMTS_GAIN_SCALE 0x0000e000 -#define FLD_MTS_PDF_SHIFT 0x00001800 -#define FLD_AM_REG_GAIN 0x00000700 -#define FLD_AGC_REF 0x000000ff - -/*****************************************************************************/ -#define ANALOG_MUX_CTL 0x860 -/* Reserved [31:29] */ -#define FLD_MUX21_SEL 0x10000000 -#define FLD_MUX20_SEL 0x08000000 -#define FLD_MUX19_SEL 0x04000000 -#define FLD_MUX18_SEL 0x02000000 -#define FLD_MUX17_SEL 0x01000000 -#define FLD_MUX16_SEL 0x00800000 -#define FLD_MUX15_SEL 0x00400000 -#define FLD_MUX14_SEL 0x00300000 -#define FLD_MUX13_SEL 0x000C0000 -#define FLD_MUX12_SEL 0x00020000 -#define FLD_MUX11_SEL 0x00018000 -#define FLD_MUX10_SEL 0x00004000 -#define FLD_MUX9_SEL 0x00002000 -#define FLD_MUX8_SEL 0x00001000 -#define FLD_MUX7_SEL 0x00000800 -#define FLD_MUX6_SEL 0x00000600 -#define FLD_MUX5_SEL 0x00000100 -#define FLD_MUX4_SEL 0x000000c0 -#define FLD_MUX3_SEL 0x00000030 -#define FLD_MUX2_SEL 0x0000000c -#define FLD_MUX1_SEL 0x00000003 - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DPLL_CTRL1 0x864 -#define DIG_PLL_CTL1 0x864 - -#define FLD_PLL_STATUS 0x07000000 -#define FLD_BANDWIDTH_SELECT 0x00030000 -#define FLD_PLL_SHIFT_REG 0x00007000 -#define FLD_PHASE_SHIFT 0x000007ff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DPLL_CTRL2 0x868 -#define DIG_PLL_CTL2 0x868 -#define FLD_PLL_UNLOCK_THR 0xff000000 -#define FLD_PLL_LOCK_THR 0x00ff0000 -/* Reserved [15:8] */ -#define FLD_AM_PDF_SEL2 0x000000c0 -#define FLD_AM_PDF_SEL1 0x00000030 -#define FLD_DPLL_FSM_CTRL 0x0000000c -/* Reserved [1] */ -#define FLD_PLL_PILOT_DET 0x00000001 - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DPLL_CTRL3 0x86c -#define DIG_PLL_CTL3 0x86c -#define FLD_DISABLE_LOOP 0x01000000 -#define FLD_A1_DS1_SEL 0x000c0000 -#define FLD_A1_DS2_SEL 0x00030000 -#define FLD_A1_KI 0x0000ff00 -#define FLD_A1_KD 0x000000ff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DPLL_CTRL4 0x870 -#define DIG_PLL_CTL4 0x870 -#define FLD_A2_DS1_SEL 0x000c0000 -#define FLD_A2_DS2_SEL 0x00030000 -#define FLD_A2_KI 0x0000ff00 -#define FLD_A2_KD 0x000000ff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DPLL_CTRL5 0x874 -#define DIG_PLL_CTL5 0x874 -#define FLD_TRK_DS1_SEL 0x000c0000 -#define FLD_TRK_DS2_SEL 0x00030000 -#define FLD_TRK_KI 0x0000ff00 -#define FLD_TRK_KD 0x000000ff - -/*****************************************************************************/ -#define DEEMPH_GAIN_CTL 0x878 -#define FLD_DEEMPH2_GAIN 0xFFFF0000 -#define FLD_DEEMPH1_GAIN 0x0000FFFF - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DEEMPH_COEFF1 0x87c -#define DEEMPH_COEF1 0x87c -#define FLD_DEEMPH_B0 0xffff0000 -#define FLD_DEEMPH_A0 0x0000ffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DEEMPH_COEFF2 0x880 -#define DEEMPH_COEF2 0x880 -#define FLD_DEEMPH_B1 0xFFFF0000 -#define FLD_DEEMPH_A1 0x0000FFFF - -/*****************************************************************************/ -#define DBX1_CTL1 0x884 -#define FLD_DBX1_WBE_GAIN 0xffff0000 -#define FLD_DBX1_IN_GAIN 0x0000ffff - -/*****************************************************************************/ -#define DBX1_CTL2 0x888 -#define FLD_DBX1_SE_BYPASS 0xffff0000 -#define FLD_DBX1_SE_GAIN 0x0000ffff - -/*****************************************************************************/ -#define DBX1_RMS_SE 0x88C -#define FLD_DBX1_RMS_WBE 0xffff0000 -#define FLD_DBX1_RMS_SE_FLD 0x0000ffff - -/*****************************************************************************/ -#define DBX2_CTL1 0x890 -#define FLD_DBX2_WBE_GAIN 0xffff0000 -#define FLD_DBX2_IN_GAIN 0x0000ffff - -/*****************************************************************************/ -#define DBX2_CTL2 0x894 -#define FLD_DBX2_SE_BYPASS 0xffff0000 -#define FLD_DBX2_SE_GAIN 0x0000ffff - -/*****************************************************************************/ -#define DBX2_RMS_SE 0x898 -#define FLD_DBX2_RMS_WBE 0xffff0000 -#define FLD_DBX2_RMS_SE_FLD 0x0000ffff - -/*****************************************************************************/ -#define AM_FM_DIFF 0x89c -/* Reserved [31] */ -#define FLD_FM_DIFF_OUT 0x7fff0000 -/* Reserved [15] */ -#define FLD_AM_DIFF_OUT 0x00007fff - -/*****************************************************************************/ -#define NICAM_FAW 0x8a0 -#define FLD_FAWDETWINEND 0xFc000000 -#define FLD_FAWDETWINSTR 0x03ff0000 -/* Reserved [15:12] */ -#define FLD_FAWDETTHRSHLD3 0x00000f00 -#define FLD_FAWDETTHRSHLD2 0x000000f0 -#define FLD_FAWDETTHRSHLD1 0x0000000f - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DEEMPH_GAIN 0x8a4 -#define NICAM_DEEMPHGAIN 0x8a4 -/* Reserved [31:18] */ -#define FLD_DEEMPHGAIN 0x0003ffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DEEMPH_NUMER1 0x8a8 -#define NICAM_DEEMPHNUMER1 0x8a8 -/* Reserved [31:18] */ -#define FLD_DEEMPHNUMER1 0x0003ffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DEEMPH_NUMER2 0x8ac -#define NICAM_DEEMPHNUMER2 0x8ac -/* Reserved [31:18] */ -#define FLD_DEEMPHNUMER2 0x0003ffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DEEMPH_DENOM1 0x8b0 -#define NICAM_DEEMPHDENOM1 0x8b0 -/* Reserved [31:18] */ -#define FLD_DEEMPHDENOM1 0x0003ffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define DEEMPH_DENOM2 0x8b4 -#define NICAM_DEEMPHDENOM2 0x8b4 -/* Reserved [31:18] */ -#define FLD_DEEMPHDENOM2 0x0003ffff - -/*****************************************************************************/ -#define NICAM_ERRLOG_CTL1 0x8B8 -/* Reserved [31:28] */ -#define FLD_ERRINTRPTTHSHLD1 0x0fff0000 -/* Reserved [15:12] */ -#define FLD_ERRLOGPERIOD 0x00000fff - -/*****************************************************************************/ -#define NICAM_ERRLOG_CTL2 0x8bc -/* Reserved [31:28] */ -#define FLD_ERRINTRPTTHSHLD3 0x0fff0000 -/* Reserved [15:12] */ -#define FLD_ERRINTRPTTHSHLD2 0x00000fff - -/*****************************************************************************/ -#define NICAM_ERRLOG_STS1 0x8c0 -/* Reserved [31:28] */ -#define FLD_ERRLOG2 0x0fff0000 -/* Reserved [15:12] */ -#define FLD_ERRLOG1 0x00000fff - -/*****************************************************************************/ -#define NICAM_ERRLOG_STS2 0x8c4 -/* Reserved [31:12] */ -#define FLD_ERRLOG3 0x00000fff - -/*****************************************************************************/ -#define NICAM_STATUS 0x8c8 -/* Reserved [31:20] */ -#define FLD_NICAM_CIB 0x000c0000 -#define FLD_NICAM_LOCK_STAT 0x00020000 -#define FLD_NICAM_MUTE 0x00010000 -#define FLD_NICAMADDIT_DATA 0x0000ffe0 -#define FLD_NICAMCNTRL 0x0000001f - -/*****************************************************************************/ -#define DEMATRIX_CTL 0x8cc -#define FLD_AC97_IN_SHIFT 0xf0000000 -#define FLD_I2S_IN_SHIFT 0x0f000000 -#define FLD_DEMATRIX_SEL_CTL 0x00ff0000 -/* Reserved [15:11] */ -#define FLD_DMTRX_BYPASS 0x00000400 -#define FLD_DEMATRIX_MODE 0x00000300 -/* Reserved [7:6] */ -#define FLD_PH_DBX_SEL 0x00000020 -#define FLD_PH_CH_SEL 0x00000010 -#define FLD_PHASE_FIX 0x0000000f - -/*****************************************************************************/ -#define PATH1_CTL1 0x8d0 -/* Reserved [31:29] */ -#define FLD_PATH1_MUTE_CTL 0x1f000000 -/* Reserved [23:22] */ -#define FLD_PATH1_AVC_CG 0x00300000 -#define FLD_PATH1_AVC_RT 0x000f0000 -#define FLD_PATH1_AVC_AT 0x0000f000 -#define FLD_PATH1_AVC_STEREO 0x00000800 -#define FLD_PATH1_AVC_CR 0x00000700 -#define FLD_PATH1_AVC_RMS_CON 0x000000f0 -#define FLD_PATH1_SEL_CTL 0x0000000f - -/*****************************************************************************/ -#define PATH1_VOL_CTL 0x8d4 -#define FLD_PATH1_AVC_THRESHOLD 0x7fff0000 -#define FLD_PATH1_BAL_LEFT 0x00008000 -#define FLD_PATH1_BAL_LEVEL 0x00007f00 -#define FLD_PATH1_VOLUME 0x000000ff - -/*****************************************************************************/ -#define PATH1_EQ_CTL 0x8d8 -/* Reserved [31:30] */ -#define FLD_PATH1_EQ_TREBLE_VOL 0x3f000000 -/* Reserved [23:22] */ -#define FLD_PATH1_EQ_MID_VOL 0x003f0000 -/* Reserved [15:14] */ -#define FLD_PATH1_EQ_BASS_VOL 0x00003f00 -/* Reserved [7:1] */ -#define FLD_PATH1_EQ_BAND_SEL 0x00000001 - -/*****************************************************************************/ -#define PATH1_SC_CTL 0x8dc -#define FLD_PATH1_SC_THRESHOLD 0x7fff0000 -#define FLD_PATH1_SC_RT 0x0000f000 -#define FLD_PATH1_SC_AT 0x00000f00 -#define FLD_PATH1_SC_STEREO 0x00000080 -#define FLD_PATH1_SC_CR 0x00000070 -#define FLD_PATH1_SC_RMS_CON 0x0000000f - -/*****************************************************************************/ -#define PATH2_CTL1 0x8e0 -/* Reserved [31:26] */ -#define FLD_PATH2_MUTE_CTL 0x03000000 -/* Reserved [23:22] */ -#define FLD_PATH2_AVC_CG 0x00300000 -#define FLD_PATH2_AVC_RT 0x000f0000 -#define FLD_PATH2_AVC_AT 0x0000f000 -#define FLD_PATH2_AVC_STEREO 0x00000800 -#define FLD_PATH2_AVC_CR 0x00000700 -#define FLD_PATH2_AVC_RMS_CON 0x000000f0 -#define FLD_PATH2_SEL_CTL 0x0000000f - -/*****************************************************************************/ -#define PATH2_VOL_CTL 0x8e4 -#define FLD_PATH2_AVC_THRESHOLD 0xffff0000 -#define FLD_PATH2_BAL_LEFT 0x00008000 -#define FLD_PATH2_BAL_LEVEL 0x00007f00 -#define FLD_PATH2_VOLUME 0x000000ff - -/*****************************************************************************/ -#define PATH2_EQ_CTL 0x8e8 -/* Reserved [31:30] */ -#define FLD_PATH2_EQ_TREBLE_VOL 0x3f000000 -/* Reserved [23:22] */ -#define FLD_PATH2_EQ_MID_VOL 0x003f0000 -/* Reserved [15:14] */ -#define FLD_PATH2_EQ_BASS_VOL 0x00003f00 -/* Reserved [7:1] */ -#define FLD_PATH2_EQ_BAND_SEL 0x00000001 - -/*****************************************************************************/ -#define PATH2_SC_CTL 0x8eC -#define FLD_PATH2_SC_THRESHOLD 0xffff0000 -#define FLD_PATH2_SC_RT 0x0000f000 -#define FLD_PATH2_SC_AT 0x00000f00 -#define FLD_PATH2_SC_STEREO 0x00000080 -#define FLD_PATH2_SC_CR 0x00000070 -#define FLD_PATH2_SC_RMS_CON 0x0000000f - -/*****************************************************************************/ -#define SRC_CTL 0x8f0 -#define FLD_SRC_STATUS 0xffffff00 -#define FLD_FIFO_LF_EN 0x000000fc -#define FLD_BYPASS_LI 0x00000002 -#define FLD_BYPASS_PF 0x00000001 - -/*****************************************************************************/ -#define SRC_LF_COEF 0x8f4 -#define FLD_LOOP_FILTER_COEF2 0xffff0000 -#define FLD_LOOP_FILTER_COEF1 0x0000ffff - -/*****************************************************************************/ -#define SRC1_CTL 0x8f8 -/* Reserved [31:28] */ -#define FLD_SRC1_FIFO_RD_TH 0x0f000000 -/* Reserved [23:18] */ -#define FLD_SRC1_PHASE_INC 0x0003ffff - -/*****************************************************************************/ -#define SRC2_CTL 0x8fc -/* Reserved [31:28] */ -#define FLD_SRC2_FIFO_RD_TH 0x0f000000 -/* Reserved [23:18] */ -#define FLD_SRC2_PHASE_INC 0x0003ffff - -/*****************************************************************************/ -#define SRC3_CTL 0x900 -/* Reserved [31:28] */ -#define FLD_SRC3_FIFO_RD_TH 0x0f000000 -/* Reserved [23:18] */ -#define FLD_SRC3_PHASE_INC 0x0003ffff - -/*****************************************************************************/ -#define SRC4_CTL 0x904 -/* Reserved [31:28] */ -#define FLD_SRC4_FIFO_RD_TH 0x0f000000 -/* Reserved [23:18] */ -#define FLD_SRC4_PHASE_INC 0x0003ffff - -/*****************************************************************************/ -#define SRC5_CTL 0x908 -/* Reserved [31:28] */ -#define FLD_SRC5_FIFO_RD_TH 0x0f000000 -/* Reserved [23:18] */ -#define FLD_SRC5_PHASE_INC 0x0003ffff - -/*****************************************************************************/ -#define SRC6_CTL 0x90c -/* Reserved [31:28] */ -#define FLD_SRC6_FIFO_RD_TH 0x0f000000 -/* Reserved [23:18] */ -#define FLD_SRC6_PHASE_INC 0x0003ffff - -/*****************************************************************************/ -#define BAND_OUT_SEL 0x910 -#define FLD_SRC6_IN_SEL 0xc0000000 -#define FLD_SRC6_CLK_SEL 0x30000000 -#define FLD_SRC5_IN_SEL 0x0c000000 -#define FLD_SRC5_CLK_SEL 0x03000000 -#define FLD_SRC4_IN_SEL 0x00c00000 -#define FLD_SRC4_CLK_SEL 0x00300000 -#define FLD_SRC3_IN_SEL 0x000c0000 -#define FLD_SRC3_CLK_SEL 0x00030000 -#define FLD_BASEBAND_BYPASS_CTL 0x0000ff00 -#define FLD_AC97_SRC_SEL 0x000000c0 -#define FLD_I2S_SRC_SEL 0x00000030 -#define FLD_PARALLEL2_SRC_SEL 0x0000000c -#define FLD_PARALLEL1_SRC_SEL 0x00000003 - -/*****************************************************************************/ -#define I2S_IN_CTL 0x914 -/* Reserved [31:11] */ -#define FLD_I2S_UP2X_BW20K 0x00000400 -#define FLD_I2S_UP2X_BYPASS 0x00000200 -#define FLD_I2S_IN_MASTER_MODE 0x00000100 -#define FLD_I2S_IN_SONY_MODE 0x00000080 -#define FLD_I2S_IN_RIGHT_JUST 0x00000040 -#define FLD_I2S_IN_WS_SEL 0x00000020 -#define FLD_I2S_IN_BCN_DEL 0x0000001f - -/*****************************************************************************/ -#define I2S_OUT_CTL 0x918 -/* Reserved [31:17] */ -#define FLD_I2S_OUT_SOFT_RESET_EN 0x00010000 -/* Reserved [15:9] */ -#define FLD_I2S_OUT_MASTER_MODE 0x00000100 -#define FLD_I2S_OUT_SONY_MODE 0x00000080 -#define FLD_I2S_OUT_RIGHT_JUST 0x00000040 -#define FLD_I2S_OUT_WS_SEL 0x00000020 -#define FLD_I2S_OUT_BCN_DEL 0x0000001f - -/*****************************************************************************/ -#define AC97_CTL 0x91c -/* Reserved [31:26] */ -#define FLD_AC97_UP2X_BW20K 0x02000000 -#define FLD_AC97_UP2X_BYPASS 0x01000000 -/* Reserved [23:17] */ -#define FLD_AC97_RST_ACL 0x00010000 -/* Reserved [15:9] */ -#define FLD_AC97_WAKE_UP_SYNC 0x00000100 -/* Reserved [7:1] */ -#define FLD_AC97_SHUTDOWN 0x00000001 - -/* Cx231xx redefine */ -#define QPSK_IAGC_CTL1 0x94c -#define QPSK_IAGC_CTL2 0x950 -#define QPSK_FEPR_FREQ 0x954 -#define QPSK_BTL_CTL1 0x958 -#define QPSK_BTL_CTL2 0x95c -#define QPSK_CTL_CTL1 0x960 -#define QPSK_CTL_CTL2 0x964 -#define QPSK_MF_FAGC_CTL 0x968 -#define QPSK_EQ_CTL 0x96c -#define QPSK_LOCK_CTL 0x970 - -/*****************************************************************************/ -#define FM1_DFT_CTL 0x9a8 -#define FLD_FM1_DFT_THRESHOLD 0xffff0000 -/* Reserved [15:8] */ -#define FLD_FM1_DFT_CMP_CTL 0x00000080 -#define FLD_FM1_DFT_AVG 0x00000070 -/* Reserved [3:1] */ -#define FLD_FM1_DFT_START 0x00000001 - -/*****************************************************************************/ -#define FM1_DFT_STATUS 0x9ac -#define FLD_FM1_DFT_DONE 0x80000000 -/* Reserved [30:19] */ -#define FLD_FM_DFT_TH_CMP 0x00040000 -#define FLD_FM1_DFT 0x0003ffff - -/*****************************************************************************/ -#define FM2_DFT_CTL 0x9b0 -#define FLD_FM2_DFT_THRESHOLD 0xffff0000 -/* Reserved [15:8] */ -#define FLD_FM2_DFT_CMP_CTL 0x00000080 -#define FLD_FM2_DFT_AVG 0x00000070 -/* Reserved [3:1] */ -#define FLD_FM2_DFT_START 0x00000001 - -/*****************************************************************************/ -#define FM2_DFT_STATUS 0x9b4 -#define FLD_FM2_DFT_DONE 0x80000000 -/* Reserved [30:19] */ -#define FLD_FM2_DFT_TH_CMP_STAT 0x00040000 -#define FLD_FM2_DFT 0x0003ffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define AAGC_STATUS_REG 0x9b8 -#define AAGC_STATUS 0x9b8 -/* Reserved [31:27] */ -#define FLD_FM2_DAGC_OUT 0x07000000 -/* Reserved [23:19] */ -#define FLD_FM1_DAGC_OUT 0x00070000 -/* Reserved [15:6] */ -#define FLD_AFE_VGA_OUT 0x0000003f - -/*****************************************************************************/ -#define MTS_GAIN_STATUS 0x9bc -/* Reserved [31:14] */ -#define FLD_MTS_GAIN 0x00003fff - -#define RDS_OUT 0x9c0 -#define FLD_RDS_Q 0xffff0000 -#define FLD_RDS_I 0x0000ffff - -/*****************************************************************************/ -#define AUTOCONFIG_REG 0x9c4 -/* Reserved [31:4] */ -#define FLD_AUTOCONFIG_MODE 0x0000000f - -#define FM_AFC 0x9c8 -#define FLD_FM2_AFC 0xffff0000 -#define FLD_FM1_AFC 0x0000ffff - -/*****************************************************************************/ -/* Cx231xx redefine */ -#define NEW_SPARE 0x9cc -#define NEW_SPARE_REG 0x9cc - -/*****************************************************************************/ -#define DBX_ADJ 0x9d0 -/* Reserved [31:28] */ -#define FLD_DBX2_ADJ 0x0fff0000 -/* Reserved [15:12] */ -#define FLD_DBX1_ADJ 0x00000fff - -#define VID_FMT_AUTO 0 -#define VID_FMT_NTSC_M 1 -#define VID_FMT_NTSC_J 2 -#define VID_FMT_NTSC_443 3 -#define VID_FMT_PAL_BDGHI 4 -#define VID_FMT_PAL_M 5 -#define VID_FMT_PAL_N 6 -#define VID_FMT_PAL_NC 7 -#define VID_FMT_PAL_60 8 -#define VID_FMT_SECAM 12 -#define VID_FMT_SECAM_60 13 - -#define INPUT_MODE_CVBS_0 0 /* INPUT_MODE_VALUE(0) */ -#define INPUT_MODE_YC_1 1 /* INPUT_MODE_VALUE(1) */ -#define INPUT_MODE_YC2_2 2 /* INPUT_MODE_VALUE(2) */ -#define INPUT_MODE_YUV_3 3 /* INPUT_MODE_VALUE(3) */ - -#define LUMA_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ -#define LUMA_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ -#define LUMA_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ - -#define UV_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ -#define UV_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ -#define UV_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ - -#define TWO_TAP_FILT 0 -#define THREE_TAP_FILT 1 -#define FOUR_TAP_FILT 2 -#define FIVE_TAP_FILT 3 - -#define AUD_CHAN_SRC_PARALLEL 0 -#define AUD_CHAN_SRC_I2S_INPUT 1 -#define AUD_CHAN_SRC_FLATIRON 2 -#define AUD_CHAN_SRC_PARALLEL3 3 - -#define OUT_MODE_601 0 -#define OUT_MODE_656 1 -#define OUT_MODE_VIP11 2 -#define OUT_MODE_VIP20 3 - -#define PHASE_INC_49MHZ 0x0df22 -#define PHASE_INC_56MHZ 0x0fa5b -#define PHASE_INC_28MHZ 0x010000 - -#endif diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c deleted file mode 100644 index ac7db52f404f..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-vbi.c +++ /dev/null @@ -1,710 +0,0 @@ -/* - cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices - - Copyright (C) 2008 - Based on cx88 driver - - This program is free software; 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 -#include - -#include -#include -#include -#include -#include - -#include "cx231xx.h" -#include "cx231xx-vbi.h" - -static inline void print_err_status(struct cx231xx *dev, int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status, - errmsg); - } else { - cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -/* - * Controls the isoc copy of each urb packet - */ -static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) -{ - struct cx231xx_dmaqueue *dma_q = urb->context; - int rc = 1; - unsigned char *p_buffer; - u32 bytes_parsed = 0, buffer_size = 0; - u8 sav_eav = 0; - - if (!dev) - return 0; - - if (dev->state & DEV_DISCONNECTED) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - /* get buffer pointer and length */ - p_buffer = urb->transfer_buffer; - buffer_size = urb->actual_length; - - if (buffer_size > 0) { - bytes_parsed = 0; - - if (dma_q->is_partial_line) { - /* Handle the case where we were working on a partial - line */ - sav_eav = dma_q->last_sav; - } else { - /* Check for a SAV/EAV overlapping the - buffer boundary */ - - sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer, - dma_q->partial_buf, - &bytes_parsed); - } - - sav_eav &= 0xF0; - /* Get the first line if we have some portion of an SAV/EAV from - the last buffer or a partial line */ - if (sav_eav) { - bytes_parsed += cx231xx_get_vbi_line(dev, dma_q, - sav_eav, /* SAV/EAV */ - p_buffer + bytes_parsed, /* p_buffer */ - buffer_size - bytes_parsed); /* buffer size */ - } - - /* Now parse data that is completely in this buffer */ - dma_q->is_partial_line = 0; - - while (bytes_parsed < buffer_size) { - u32 bytes_used = 0; - - sav_eav = cx231xx_find_next_SAV_EAV( - p_buffer + bytes_parsed, /* p_buffer */ - buffer_size - bytes_parsed, /* buffer size */ - &bytes_used); /* bytes used to get SAV/EAV */ - - bytes_parsed += bytes_used; - - sav_eav &= 0xF0; - if (sav_eav && (bytes_parsed < buffer_size)) { - bytes_parsed += cx231xx_get_vbi_line(dev, - dma_q, sav_eav, /* SAV/EAV */ - p_buffer+bytes_parsed, /* p_buffer */ - buffer_size-bytes_parsed);/*buf size*/ - } - } - - /* Save the last four bytes of the buffer so we can - check the buffer boundary condition next time */ - memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); - bytes_parsed = 0; - } - - return rc; -} - -/* ------------------------------------------------------------------ - Vbi buf operations - ------------------------------------------------------------------*/ - -static int -vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - u32 height = 0; - - height = ((dev->norm & V4L2_STD_625_50) ? - PAL_VBI_LINES : NTSC_VBI_LINES); - - *size = (dev->width * height * 2 * 2); - if (0 == *count) - *count = CX231XX_DEF_VBI_BUF; - - if (*count < CX231XX_MIN_BUF) - *count = CX231XX_MIN_BUF; - - return 0; -} - -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->vbi_mode.slock, flags); - if (dev->vbi_mode.bulk_ctl.buf == buf) - dev->vbi_mode.bulk_ctl.buf = NULL; - spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - int rc = 0, urb_init = 0; - u32 height = 0; - - height = ((dev->norm & V4L2_STD_625_50) ? - PAL_VBI_LINES : NTSC_VBI_LINES); - buf->vb.size = ((dev->width << 1) * height * 2); - - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - buf->vb.width = dev->width; - buf->vb.height = height; - buf->vb.field = field; - buf->vb.field = V4L2_FIELD_SEQ_TB; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - if (!dev->vbi_mode.bulk_ctl.num_bufs) - urb_init = 1; - - if (urb_init) { - rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS, - CX231XX_NUM_VBI_BUFS, - dev->vbi_mode.alt_max_pkt_size[0], - cx231xx_isoc_vbi_copy); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(vq, buf); - return rc; -} - -static void -vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); - -} - -static void vbi_buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - - - free_buffer(vq, buf); -} - -struct videobuf_queue_ops cx231xx_vbi_qops = { - .buf_setup = vbi_buffer_setup, - .buf_prepare = vbi_buffer_prepare, - .buf_queue = vbi_buffer_queue, - .buf_release = vbi_buffer_release, -}; - -/* ------------------------------------------------------------------ - URB control - ------------------------------------------------------------------*/ - -/* - * IRQ callback, called by URB callback - */ -static void cx231xx_irq_vbi_callback(struct urb *urb) -{ - struct cx231xx_dmaqueue *dma_q = urb->context; - struct cx231xx_video_mode *vmode = - container_of(dma_q, struct cx231xx_video_mode, vidq); - struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - cx231xx_err(DRIVER_NAME "urb completition error %d.\n", - urb->status); - break; - } - - /* Copy data from URB */ - spin_lock(&dev->vbi_mode.slock); - dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); - spin_unlock(&dev->vbi_mode.slock); - - /* Reset status */ - urb->status = 0; - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) { - cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n", - urb->status); - } -} - -/* - * Stop and Deallocate URBs - */ -void cx231xx_uninit_vbi_isoc(struct cx231xx *dev) -{ - struct urb *urb; - int i; - - cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n"); - - dev->vbi_mode.bulk_ctl.nfields = -1; - for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { - urb = dev->vbi_mode.bulk_ctl.urb[i]; - if (urb) { - if (!irqs_disabled()) - usb_kill_urb(urb); - else - usb_unlink_urb(urb); - - if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) { - - kfree(dev->vbi_mode.bulk_ctl. - transfer_buffer[i]); - dev->vbi_mode.bulk_ctl.transfer_buffer[i] = - NULL; - } - usb_free_urb(urb); - dev->vbi_mode.bulk_ctl.urb[i] = NULL; - } - dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->vbi_mode.bulk_ctl.urb); - kfree(dev->vbi_mode.bulk_ctl.transfer_buffer); - - dev->vbi_mode.bulk_ctl.urb = NULL; - dev->vbi_mode.bulk_ctl.transfer_buffer = NULL; - dev->vbi_mode.bulk_ctl.num_bufs = 0; - - cx231xx_capture_start(dev, 0, Vbi); -} -EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc); - -/* - * Allocate URBs and start IRQ - */ -int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*bulk_copy) (struct cx231xx *dev, - struct urb *urb)) -{ - struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq; - int i; - int sb_size, pipe; - struct urb *urb; - int rc; - - cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n"); - - /* De-allocates all pending stuff */ - cx231xx_uninit_vbi_isoc(dev); - - /* clear if any halt */ - usb_clear_halt(dev->udev, - usb_rcvbulkpipe(dev->udev, - dev->vbi_mode.end_point_addr)); - - dev->vbi_mode.bulk_ctl.bulk_copy = bulk_copy; - dev->vbi_mode.bulk_ctl.num_bufs = num_bufs; - dma_q->pos = 0; - dma_q->is_partial_line = 0; - dma_q->last_sav = 0; - dma_q->current_field = -1; - dma_q->bytes_left_in_line = dev->width << 1; - dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ? - PAL_VBI_LINES : NTSC_VBI_LINES); - dma_q->lines_completed = 0; - for (i = 0; i < 8; i++) - dma_q->partial_buf[i] = 0; - - dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs, - GFP_KERNEL); - if (!dev->vbi_mode.bulk_ctl.urb) { - cx231xx_errdev("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - dev->vbi_mode.bulk_ctl.transfer_buffer = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); - if (!dev->vbi_mode.bulk_ctl.transfer_buffer) { - cx231xx_errdev("cannot allocate memory for usbtransfer\n"); - kfree(dev->vbi_mode.bulk_ctl.urb); - return -ENOMEM; - } - - dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size; - dev->vbi_mode.bulk_ctl.buf = NULL; - - sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size; - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - cx231xx_err(DRIVER_NAME - ": cannot alloc bulk_ctl.urb %i\n", i); - cx231xx_uninit_vbi_isoc(dev); - return -ENOMEM; - } - dev->vbi_mode.bulk_ctl.urb[i] = urb; - urb->transfer_flags = 0; - - dev->vbi_mode.bulk_ctl.transfer_buffer[i] = - kzalloc(sb_size, GFP_KERNEL); - if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) { - cx231xx_err(DRIVER_NAME - ": unable to allocate %i bytes for transfer" - " buffer %i%s\n", sb_size, i, - in_interrupt() ? " while in int" : ""); - cx231xx_uninit_vbi_isoc(dev); - return -ENOMEM; - } - - pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr); - usb_fill_bulk_urb(urb, dev->udev, pipe, - dev->vbi_mode.bulk_ctl.transfer_buffer[i], - sb_size, cx231xx_irq_vbi_callback, dma_q); - } - - init_waitqueue_head(&dma_q->wq); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC); - if (rc) { - cx231xx_err(DRIVER_NAME - ": submit of urb %i failed (error=%i)\n", i, - rc); - cx231xx_uninit_vbi_isoc(dev); - return rc; - } - } - - cx231xx_capture_start(dev, 1, Vbi); - - return 0; -} -EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc); - -u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 sav_eav, u8 *p_buffer, u32 buffer_size) -{ - u32 bytes_copied = 0; - int current_field = -1; - - switch (sav_eav) { - - case SAV_VBI_FIELD1: - current_field = 1; - break; - - case SAV_VBI_FIELD2: - current_field = 2; - break; - default: - break; - } - - if (current_field < 0) - return bytes_copied; - - dma_q->last_sav = sav_eav; - - bytes_copied = - cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size, - current_field); - - return bytes_copied; -} - -/* - * Announces that a buffer were filled and request the next - */ -static inline void vbi_buffer_filled(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q, - struct cx231xx_buffer *buf) -{ - /* Advice that buffer was filled */ - /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */ - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->vbi_mode.bulk_ctl.buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 *p_line, u32 length, int field_number) -{ - u32 bytes_to_copy; - struct cx231xx_buffer *buf; - u32 _line_size = dev->width * 2; - - if (dma_q->current_field == -1) { - /* Just starting up */ - cx231xx_reset_vbi_buffer(dev, dma_q); - } - - if (dma_q->current_field != field_number) - dma_q->lines_completed = 0; - - /* get the buffer pointer */ - buf = dev->vbi_mode.bulk_ctl.buf; - - /* Remember the field number for next time */ - dma_q->current_field = field_number; - - bytes_to_copy = dma_q->bytes_left_in_line; - if (bytes_to_copy > length) - bytes_to_copy = length; - - if (dma_q->lines_completed >= dma_q->lines_per_field) { - dma_q->bytes_left_in_line -= bytes_to_copy; - dma_q->is_partial_line = - (dma_q->bytes_left_in_line == 0) ? 0 : 1; - return 0; - } - - dma_q->is_partial_line = 1; - - /* If we don't have a buffer, just return the number of bytes we would - have copied if we had a buffer. */ - if (!buf) { - dma_q->bytes_left_in_line -= bytes_to_copy; - dma_q->is_partial_line = - (dma_q->bytes_left_in_line == 0) ? 0 : 1; - return bytes_to_copy; - } - - /* copy the data to video buffer */ - cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy); - - dma_q->pos += bytes_to_copy; - dma_q->bytes_left_in_line -= bytes_to_copy; - - if (dma_q->bytes_left_in_line == 0) { - - dma_q->bytes_left_in_line = _line_size; - dma_q->lines_completed++; - dma_q->is_partial_line = 0; - - if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) { - - vbi_buffer_filled(dev, dma_q, buf); - - dma_q->pos = 0; - dma_q->lines_completed = 0; - cx231xx_reset_vbi_buffer(dev, dma_q); - } - } - - return bytes_to_copy; -} - -/* - * video-buf generic routine to get the next available buffer - */ -static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, - struct cx231xx_buffer **buf) -{ - struct cx231xx_video_mode *vmode = - container_of(dma_q, struct cx231xx_video_mode, vidq); - struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); - char *outp; - - if (list_empty(&dma_q->active)) { - cx231xx_err(DRIVER_NAME ": No active queue to serve\n"); - dev->vbi_mode.bulk_ctl.buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); - - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); - - dev->vbi_mode.bulk_ctl.buf = *buf; - - return; -} - -void cx231xx_reset_vbi_buffer(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q) -{ - struct cx231xx_buffer *buf; - - buf = dev->vbi_mode.bulk_ctl.buf; - - if (buf == NULL) { - /* first try to get the buffer */ - get_next_vbi_buf(dma_q, &buf); - - dma_q->pos = 0; - dma_q->current_field = -1; - } - - dma_q->bytes_left_in_line = dev->width << 1; - dma_q->lines_completed = 0; -} - -int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 *p_buffer, u32 bytes_to_copy) -{ - u8 *p_out_buffer = NULL; - u32 current_line_bytes_copied = 0; - struct cx231xx_buffer *buf; - u32 _line_size = dev->width << 1; - void *startwrite; - int offset, lencopy; - - buf = dev->vbi_mode.bulk_ctl.buf; - - if (buf == NULL) - return -EINVAL; - - p_out_buffer = videobuf_to_vmalloc(&buf->vb); - - if (dma_q->bytes_left_in_line != _line_size) { - current_line_bytes_copied = - _line_size - dma_q->bytes_left_in_line; - } - - offset = (dma_q->lines_completed * _line_size) + - current_line_bytes_copied; - - if (dma_q->current_field == 2) { - /* Populate the second half of the frame */ - offset += (dev->width * 2 * dma_q->lines_per_field); - } - - /* prepare destination address */ - startwrite = p_out_buffer + offset; - - lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? - bytes_to_copy : dma_q->bytes_left_in_line; - - memcpy(startwrite, p_buffer, lencopy); - - return 0; -} - -u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q) -{ - u32 height = 0; - - height = ((dev->norm & V4L2_STD_625_50) ? - PAL_VBI_LINES : NTSC_VBI_LINES); - if (dma_q->lines_completed == height && dma_q->current_field == 2) - return 1; - else - return 0; -} diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.h b/drivers/media/video/cx231xx/cx231xx-vbi.h deleted file mode 100644 index 16c7d20a22a4..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-vbi.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices - - Copyright (C) 2008 - Based on cx88 driver - - This program is free software; 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 _CX231XX_VBI_H -#define _CX231XX_VBI_H - -extern struct videobuf_queue_ops cx231xx_vbi_qops; - -#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ -#define NTSC_VBI_END_LINE 21 -#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1) - -#define PAL_VBI_START_LINE 6 -#define PAL_VBI_END_LINE 23 -#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1) - -#define VBI_STRIDE 1440 -#define VBI_SAMPLES_PER_LINE 1440 - -#define CX231XX_NUM_VBI_PACKETS 4 -#define CX231XX_NUM_VBI_BUFS 5 - -/* stream functions */ -int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*bulk_copy) (struct cx231xx *dev, - struct urb *urb)); - -void cx231xx_uninit_vbi_isoc(struct cx231xx *dev); - -/* vbi data copy functions */ -u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 sav_eav, u8 *p_buffer, u32 buffer_size); - -u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 *p_line, u32 length, int field_number); - -void cx231xx_reset_vbi_buffer(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q); - -int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 *p_buffer, u32 bytes_to_copy); - -u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q); - -#endif diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c deleted file mode 100644 index 790b28d7f764..000000000000 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ /dev/null @@ -1,2670 +0,0 @@ -/* - cx231xx-video.c - driver for Conexant Cx23100/101/102 - USB video capture devices - - Copyright (C) 2008 - Based on em28xx driver - Based on cx23885 driver - Based on cx88 driver - - This program is free software; 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 -#include - -#include -#include -#include -#include -#include - -#include "dvb_frontend.h" - -#include "cx231xx.h" -#include "cx231xx-vbi.h" - -#define CX231XX_VERSION "0.0.2" - -#define DRIVER_AUTHOR "Srinivasa Deevi " -#define DRIVER_DESC "Conexant cx231xx based USB video device driver" - -#define cx231xx_videodbg(fmt, arg...) do {\ - if (video_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -static unsigned int isoc_debug; -module_param(isoc_debug, int, 0644); -MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); - -#define cx231xx_isocdbg(fmt, arg...) \ -do {\ - if (isoc_debug) { \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); \ - } \ - } while (0) - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(CX231XX_VERSION); - -static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; - -module_param_array(card, int, NULL, 0444); -module_param_array(video_nr, int, NULL, 0444); -module_param_array(vbi_nr, int, NULL, 0444); -module_param_array(radio_nr, int, NULL, 0444); - -MODULE_PARM_DESC(card, "card type"); -MODULE_PARM_DESC(video_nr, "video device numbers"); -MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); -MODULE_PARM_DESC(radio_nr, "radio device numbers"); - -static unsigned int video_debug; -module_param(video_debug, int, 0644); -MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); - -/* supported video standards */ -static struct cx231xx_fmt format[] = { - { - .name = "16bpp YUY2, 4:2:2, packed", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .reg = 0, - }, -}; - -/* supported controls */ -/* Common to all boards */ - -/* ------------------------------------------------------------------- */ - -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -static struct cx231xx_ctrl cx231xx_ctls[] = { - /* --- video --- */ - { - .v = { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0x00, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 128, - .reg = LUMA_CTRL, - .mask = 0x00ff, - .shift = 0, - }, { - .v = { - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x3f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, - .reg = LUMA_CTRL, - .mask = 0xff00, - .shift = 8, - }, { - .v = { - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 128, - .reg = CHROMA_CTRL, - .mask = 0xff0000, - .shift = 16, - }, { - /* strictly, this only describes only U saturation. - * V saturation is handled specially through code. - */ - .v = { - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, - .reg = CHROMA_CTRL, - .mask = 0x00ff, - .shift = 0, - }, { - /* --- audio --- */ - .v = { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .default_value = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, - .reg = PATH1_CTL1, - .mask = (0x1f << 24), - .shift = 24, - }, { - .v = { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 0x3f, - .step = 1, - .default_value = 0x3f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .reg = PATH1_VOL_CTL, - .mask = 0xff, - .shift = 0, - } -}; -static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls); - -static const u32 cx231xx_user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, -#if 0 - V4L2_CID_AUDIO_BALANCE, -#endif - V4L2_CID_AUDIO_MUTE, - 0 -}; - -static const u32 *ctrl_classes[] = { - cx231xx_user_ctrls, - NULL -}; - -/* ------------------------------------------------------------------ - Video buffer and parser functions - ------------------------------------------------------------------*/ - -/* - * Announces that a buffer were filled and request the next - */ -static inline void buffer_filled(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q, - struct cx231xx_buffer *buf) -{ - /* Advice that buffer was filled */ - cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - if (dev->USE_ISO) - dev->video_mode.isoc_ctl.buf = NULL; - else - dev->video_mode.bulk_ctl.buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -static inline void print_err_status(struct cx231xx *dev, int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg); - } else { - cx231xx_isocdbg("URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -/* - * video-buf generic routine to get the next available buffer - */ -static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, - struct cx231xx_buffer **buf) -{ - struct cx231xx_video_mode *vmode = - container_of(dma_q, struct cx231xx_video_mode, vidq); - struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - - char *outp; - - if (list_empty(&dma_q->active)) { - cx231xx_isocdbg("No active queue to serve\n"); - if (dev->USE_ISO) - dev->video_mode.isoc_ctl.buf = NULL; - else - dev->video_mode.bulk_ctl.buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); - - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); - - if (dev->USE_ISO) - dev->video_mode.isoc_ctl.buf = *buf; - else - dev->video_mode.bulk_ctl.buf = *buf; - - return; -} - -/* - * Controls the isoc copy of each urb packet - */ -static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) -{ - struct cx231xx_dmaqueue *dma_q = urb->context; - int i, rc = 1; - unsigned char *p_buffer; - u32 bytes_parsed = 0, buffer_size = 0; - u8 sav_eav = 0; - - if (!dev) - return 0; - - if (dev->state & DEV_DISCONNECTED) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* cx231xx_isocdbg("packet %d is empty",i); - spammy */ - continue; - } - if (urb->iso_frame_desc[i].actual_length > - dev->video_mode.max_pkt_size) { - cx231xx_isocdbg("packet bigger than packet size"); - continue; - } - - /* get buffer pointer and length */ - p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - buffer_size = urb->iso_frame_desc[i].actual_length; - bytes_parsed = 0; - - if (dma_q->is_partial_line) { - /* Handle the case of a partial line */ - sav_eav = dma_q->last_sav; - } else { - /* Check for a SAV/EAV overlapping - the buffer boundary */ - sav_eav = - cx231xx_find_boundary_SAV_EAV(p_buffer, - dma_q->partial_buf, - &bytes_parsed); - } - - sav_eav &= 0xF0; - /* Get the first line if we have some portion of an SAV/EAV from - the last buffer or a partial line */ - if (sav_eav) { - bytes_parsed += cx231xx_get_video_line(dev, dma_q, - sav_eav, /* SAV/EAV */ - p_buffer + bytes_parsed, /* p_buffer */ - buffer_size - bytes_parsed);/* buf size */ - } - - /* Now parse data that is completely in this buffer */ - /* dma_q->is_partial_line = 0; */ - - while (bytes_parsed < buffer_size) { - u32 bytes_used = 0; - - sav_eav = cx231xx_find_next_SAV_EAV( - p_buffer + bytes_parsed, /* p_buffer */ - buffer_size - bytes_parsed, /* buf size */ - &bytes_used);/* bytes used to get SAV/EAV */ - - bytes_parsed += bytes_used; - - sav_eav &= 0xF0; - if (sav_eav && (bytes_parsed < buffer_size)) { - bytes_parsed += cx231xx_get_video_line(dev, - dma_q, sav_eav, /* SAV/EAV */ - p_buffer + bytes_parsed,/* p_buffer */ - buffer_size - bytes_parsed);/*buf size*/ - } - } - - /* Save the last four bytes of the buffer so we can check the - buffer boundary condition next time */ - memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); - bytes_parsed = 0; - - } - return rc; -} - -static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) -{ - struct cx231xx_dmaqueue *dma_q = urb->context; - int rc = 1; - unsigned char *p_buffer; - u32 bytes_parsed = 0, buffer_size = 0; - u8 sav_eav = 0; - - if (!dev) - return 0; - - if (dev->state & DEV_DISCONNECTED) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - if (1) { - - /* get buffer pointer and length */ - p_buffer = urb->transfer_buffer; - buffer_size = urb->actual_length; - bytes_parsed = 0; - - if (dma_q->is_partial_line) { - /* Handle the case of a partial line */ - sav_eav = dma_q->last_sav; - } else { - /* Check for a SAV/EAV overlapping - the buffer boundary */ - sav_eav = - cx231xx_find_boundary_SAV_EAV(p_buffer, - dma_q->partial_buf, - &bytes_parsed); - } - - sav_eav &= 0xF0; - /* Get the first line if we have some portion of an SAV/EAV from - the last buffer or a partial line */ - if (sav_eav) { - bytes_parsed += cx231xx_get_video_line(dev, dma_q, - sav_eav, /* SAV/EAV */ - p_buffer + bytes_parsed, /* p_buffer */ - buffer_size - bytes_parsed);/* buf size */ - } - - /* Now parse data that is completely in this buffer */ - /* dma_q->is_partial_line = 0; */ - - while (bytes_parsed < buffer_size) { - u32 bytes_used = 0; - - sav_eav = cx231xx_find_next_SAV_EAV( - p_buffer + bytes_parsed, /* p_buffer */ - buffer_size - bytes_parsed, /* buf size */ - &bytes_used);/* bytes used to get SAV/EAV */ - - bytes_parsed += bytes_used; - - sav_eav &= 0xF0; - if (sav_eav && (bytes_parsed < buffer_size)) { - bytes_parsed += cx231xx_get_video_line(dev, - dma_q, sav_eav, /* SAV/EAV */ - p_buffer + bytes_parsed,/* p_buffer */ - buffer_size - bytes_parsed);/*buf size*/ - } - } - - /* Save the last four bytes of the buffer so we can check the - buffer boundary condition next time */ - memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); - bytes_parsed = 0; - - } - return rc; -} - - -u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, - u32 *p_bytes_used) -{ - u32 bytes_used; - u8 boundary_bytes[8]; - u8 sav_eav = 0; - - *p_bytes_used = 0; - - /* Create an array of the last 4 bytes of the last buffer and the first - 4 bytes of the current buffer. */ - - memcpy(boundary_bytes, partial_buf, 4); - memcpy(boundary_bytes + 4, p_buffer, 4); - - /* Check for the SAV/EAV in the boundary buffer */ - sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8, - &bytes_used); - - if (sav_eav) { - /* found a boundary SAV/EAV. Updates the bytes used to reflect - only those used in the new buffer */ - *p_bytes_used = bytes_used - 4; - } - - return sav_eav; -} - -u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used) -{ - u32 i; - u8 sav_eav = 0; - - /* - * Don't search if the buffer size is less than 4. It causes a page - * fault since buffer_size - 4 evaluates to a large number in that - * case. - */ - if (buffer_size < 4) { - *p_bytes_used = buffer_size; - return 0; - } - - for (i = 0; i < (buffer_size - 3); i++) { - - if ((p_buffer[i] == 0xFF) && - (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) { - - *p_bytes_used = i + 4; - sav_eav = p_buffer[i + 3]; - return sav_eav; - } - } - - *p_bytes_used = buffer_size; - return 0; -} - -u32 cx231xx_get_video_line(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q, u8 sav_eav, - u8 *p_buffer, u32 buffer_size) -{ - u32 bytes_copied = 0; - int current_field = -1; - - switch (sav_eav) { - case SAV_ACTIVE_VIDEO_FIELD1: - /* looking for skipped line which occurred in PAL 720x480 mode. - In this case, there will be no active data contained - between the SAV and EAV */ - if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && - (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && - ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || - (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || - (p_buffer[3] == EAV_VBLANK_FIELD1) || - (p_buffer[3] == EAV_VBLANK_FIELD2))) - return bytes_copied; - current_field = 1; - break; - - case SAV_ACTIVE_VIDEO_FIELD2: - /* looking for skipped line which occurred in PAL 720x480 mode. - In this case, there will be no active data contained between - the SAV and EAV */ - if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && - (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && - ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || - (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || - (p_buffer[3] == EAV_VBLANK_FIELD1) || - (p_buffer[3] == EAV_VBLANK_FIELD2))) - return bytes_copied; - current_field = 2; - break; - } - - dma_q->last_sav = sav_eav; - - bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer, - buffer_size, current_field); - - return bytes_copied; -} - -u32 cx231xx_copy_video_line(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q, u8 *p_line, - u32 length, int field_number) -{ - u32 bytes_to_copy; - struct cx231xx_buffer *buf; - u32 _line_size = dev->width * 2; - - if (dma_q->current_field != field_number) - cx231xx_reset_video_buffer(dev, dma_q); - - /* get the buffer pointer */ - if (dev->USE_ISO) - buf = dev->video_mode.isoc_ctl.buf; - else - buf = dev->video_mode.bulk_ctl.buf; - - /* Remember the field number for next time */ - dma_q->current_field = field_number; - - bytes_to_copy = dma_q->bytes_left_in_line; - if (bytes_to_copy > length) - bytes_to_copy = length; - - if (dma_q->lines_completed >= dma_q->lines_per_field) { - dma_q->bytes_left_in_line -= bytes_to_copy; - dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ? - 0 : 1; - return 0; - } - - dma_q->is_partial_line = 1; - - /* If we don't have a buffer, just return the number of bytes we would - have copied if we had a buffer. */ - if (!buf) { - dma_q->bytes_left_in_line -= bytes_to_copy; - dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) - ? 0 : 1; - return bytes_to_copy; - } - - /* copy the data to video buffer */ - cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy); - - dma_q->pos += bytes_to_copy; - dma_q->bytes_left_in_line -= bytes_to_copy; - - if (dma_q->bytes_left_in_line == 0) { - dma_q->bytes_left_in_line = _line_size; - dma_q->lines_completed++; - dma_q->is_partial_line = 0; - - if (cx231xx_is_buffer_done(dev, dma_q) && buf) { - buffer_filled(dev, dma_q, buf); - - dma_q->pos = 0; - buf = NULL; - dma_q->lines_completed = 0; - } - } - - return bytes_to_copy; -} - -void cx231xx_reset_video_buffer(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q) -{ - struct cx231xx_buffer *buf; - - /* handle the switch from field 1 to field 2 */ - if (dma_q->current_field == 1) { - if (dma_q->lines_completed >= dma_q->lines_per_field) - dma_q->field1_done = 1; - else - dma_q->field1_done = 0; - } - - if (dev->USE_ISO) - buf = dev->video_mode.isoc_ctl.buf; - else - buf = dev->video_mode.bulk_ctl.buf; - - if (buf == NULL) { - /* first try to get the buffer */ - get_next_buf(dma_q, &buf); - - dma_q->pos = 0; - dma_q->field1_done = 0; - dma_q->current_field = -1; - } - - /* reset the counters */ - dma_q->bytes_left_in_line = dev->width << 1; - dma_q->lines_completed = 0; -} - -int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 *p_buffer, u32 bytes_to_copy) -{ - u8 *p_out_buffer = NULL; - u32 current_line_bytes_copied = 0; - struct cx231xx_buffer *buf; - u32 _line_size = dev->width << 1; - void *startwrite; - int offset, lencopy; - - if (dev->USE_ISO) - buf = dev->video_mode.isoc_ctl.buf; - else - buf = dev->video_mode.bulk_ctl.buf; - - if (buf == NULL) - return -1; - - p_out_buffer = videobuf_to_vmalloc(&buf->vb); - - current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line; - - /* Offset field 2 one line from the top of the buffer */ - offset = (dma_q->current_field == 1) ? 0 : _line_size; - - /* Offset for field 2 */ - startwrite = p_out_buffer + offset; - - /* lines already completed in the current field */ - startwrite += (dma_q->lines_completed * _line_size * 2); - - /* bytes already completed in the current line */ - startwrite += current_line_bytes_copied; - - lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? - bytes_to_copy : dma_q->bytes_left_in_line; - - if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size)) - return 0; - - /* The below copies the UYVY data straight into video buffer */ - cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy); - - return 0; -} - -void cx231xx_swab(u16 *from, u16 *to, u16 len) -{ - u16 i; - - if (len <= 0) - return; - - for (i = 0; i < len / 2; i++) - to[i] = (from[i] << 8) | (from[i] >> 8); -} - -u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q) -{ - u8 buffer_complete = 0; - - /* Dual field stream */ - buffer_complete = ((dma_q->current_field == 2) && - (dma_q->lines_completed >= dma_q->lines_per_field) && - dma_q->field1_done); - - return buffer_complete; -} - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3; - if (0 == *count) - *count = CX231XX_DEF_BUF; - - if (*count < CX231XX_MIN_BUF) - *count = CX231XX_MIN_BUF; - - return 0; -} - -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - unsigned long flags = 0; - - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->video_mode.slock, flags); - if (dev->USE_ISO) { - if (dev->video_mode.isoc_ctl.buf == buf) - dev->video_mode.isoc_ctl.buf = NULL; - } else { - if (dev->video_mode.bulk_ctl.buf == buf) - dev->video_mode.bulk_ctl.buf = NULL; - } - spin_unlock_irqrestore(&dev->video_mode.slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - int rc = 0, urb_init = 0; - - /* The only currently supported format is 16 bits/pixel */ - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth - + 7) >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - if (dev->USE_ISO) { - if (!dev->video_mode.isoc_ctl.num_bufs) - urb_init = 1; - } else { - if (!dev->video_mode.bulk_ctl.num_bufs) - urb_init = 1; - } - /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n", - urb_init, dev->video_mode.max_pkt_size);*/ - if (urb_init) { - dev->mode_tv = 0; - if (dev->USE_ISO) - rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, - CX231XX_NUM_BUFS, - dev->video_mode.max_pkt_size, - cx231xx_isoc_copy); - else - rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS, - CX231XX_NUM_BUFS, - dev->video_mode.max_pkt_size, - cx231xx_bulk_copy); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(vq, buf); - return rc; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); - -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = (struct cx231xx *)fh->dev; - - cx231xx_isocdbg("cx231xx: called buffer_release\n"); - - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops cx231xx_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/********************* v4l2 interface **************************************/ - -void video_mux(struct cx231xx *dev, int index) -{ - dev->video_input = index; - dev->ctl_ainput = INPUT(index)->amux; - - cx231xx_set_video_input_mux(dev, index); - - cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0); - - cx231xx_set_audio_input(dev, dev->ctl_ainput); - - cx231xx_info("video_mux : %d\n", index); - - /* do mode control overrides if required */ - cx231xx_do_mode_ctrl_overrides(dev); -} - -/* Usage lock check functions */ -static int res_get(struct cx231xx_fh *fh) -{ - struct cx231xx *dev = fh->dev; - int rc = 0; - - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (dev->stream_on) - return -EBUSY; - dev->stream_on = 1; - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (dev->vbi_stream_on) - return -EBUSY; - dev->vbi_stream_on = 1; - } else - return -EINVAL; - - fh->stream_on = 1; - - return rc; -} - -static int res_check(struct cx231xx_fh *fh) -{ - return fh->stream_on; -} - -static void res_free(struct cx231xx_fh *fh) -{ - struct cx231xx *dev = fh->dev; - - fh->stream_on = 0; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - dev->stream_on = 0; - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - dev->vbi_stream_on = 0; -} - -static int check_dev(struct cx231xx *dev) -{ - if (dev->state & DEV_DISCONNECTED) { - cx231xx_errdev("v4l2 ioctl: device not present\n"); - return -ENODEV; - } - return 0; -} - -/* ------------------------------------------------------------------ - IOCTL vidioc handling - ------------------------------------------------------------------*/ - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = dev->format->fourcc; - f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - - return 0; -} - -static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(format); i++) - if (format[i].fourcc == fourcc) - return &format[i]; - - return NULL; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - unsigned int width = f->fmt.pix.width; - unsigned int height = f->fmt.pix.height; - unsigned int maxw = norm_maxw(dev); - unsigned int maxh = norm_maxh(dev); - struct cx231xx_fmt *fmt; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (!fmt) { - cx231xx_videodbg("Fourcc format (%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - /* width must even because of the YUYV format - height must be even because of interlacing */ - v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); - - f->fmt.pix.width = width; - f->fmt.pix.height = height; - f->fmt.pix.pixelformat = fmt->fourcc; - f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - struct cx231xx_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - vidioc_try_fmt_vid_cap(file, priv, f); - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (!fmt) - return -EINVAL; - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - cx231xx_errdev("%s queue busy\n", __func__); - return -EBUSY; - } - - if (dev->stream_on && !fh->stream_on) { - cx231xx_errdev("%s device in use by another fh\n", __func__); - return -EBUSY; - } - - /* set new image size */ - dev->width = f->fmt.pix.width; - dev->height = f->fmt.pix.height; - dev->format = fmt; - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); - call_all(dev, video, s_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); - - return rc; -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - *id = dev->norm; - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - struct v4l2_mbus_framefmt mbus_fmt; - struct v4l2_format f; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm); - - dev->norm = *norm; - - /* Adjusts width/height, if needed */ - f.fmt.pix.width = dev->width; - f.fmt.pix.height = dev->height; - vidioc_try_fmt_vid_cap(file, priv, &f); - - call_all(dev, core, s_std, dev->norm); - - /* We need to reset basic properties in the decoder related to - resolution (since a standard change effects things like the number - of lines in VACT, etc) */ - v4l2_fill_mbus_format(&mbus_fmt, &f.fmt.pix, V4L2_MBUS_FMT_FIXED); - call_all(dev, video, s_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(&f.fmt.pix, &mbus_fmt); - - /* set new image size */ - dev->width = f.fmt.pix.width; - dev->height = f.fmt.pix.height; - - /* do mode control overrides */ - cx231xx_do_mode_ctrl_overrides(dev); - - return 0; -} - -static const char *iname[] = { - [CX231XX_VMUX_COMPOSITE1] = "Composite1", - [CX231XX_VMUX_SVIDEO] = "S-Video", - [CX231XX_VMUX_TELEVISION] = "Television", - [CX231XX_VMUX_CABLE] = "Cable TV", - [CX231XX_VMUX_DVB] = "DVB", - [CX231XX_VMUX_DEBUG] = "for debug only", -}; - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - u32 gen_stat; - unsigned int ret, n; - - n = i->index; - if (n >= MAX_CX231XX_INPUT) - return -EINVAL; - if (0 == INPUT(n)->type) - return -EINVAL; - - i->index = n; - i->type = V4L2_INPUT_TYPE_CAMERA; - - strcpy(i->name, iname[INPUT(n)->type]); - - if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) || - (CX231XX_VMUX_CABLE == INPUT(n)->type)) - i->type = V4L2_INPUT_TYPE_TUNER; - - i->std = dev->vdev->tvnorms; - - /* If they are asking about the active input, read signal status */ - if (n == dev->video_input) { - ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, - GEN_STAT, 2, &gen_stat, 4); - if (ret > 0) { - if ((gen_stat & FLD_VPRES) == 0x00) - i->status |= V4L2_IN_ST_NO_SIGNAL; - if ((gen_stat & FLD_HLOCK) == 0x00) - i->status |= V4L2_IN_ST_NO_H_LOCK; - } - } - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - *i = dev->video_input; - - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - dev->mode_tv = 0; - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (i >= MAX_CX231XX_INPUT) - return -EINVAL; - if (0 == INPUT(i)->type) - return -EINVAL; - - video_mux(dev, i); - - if (INPUT(i)->type == CX231XX_VMUX_TELEVISION || - INPUT(i)->type == CX231XX_VMUX_CABLE) { - /* There's a tuner, so reset the standard and put it on the - last known frequency (since it was probably powered down - until now */ - call_all(dev, core, s_std, dev->norm); - } - - return 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - switch (a->index) { - case CX231XX_AMUX_VIDEO: - strcpy(a->name, "Television"); - break; - case CX231XX_AMUX_LINE_IN: - strcpy(a->name, "Line In"); - break; - default: - return -EINVAL; - } - - a->index = dev->ctl_ainput; - a->capability = V4L2_AUDCAP_STEREO; - - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int status = 0; - - /* Doesn't allow manual routing */ - if (a->index != dev->ctl_ainput) - return -EINVAL; - - dev->ctl_ainput = INPUT(a->index)->amux; - status = cx231xx_set_audio_input(dev, dev->ctl_ainput); - - return status; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int id = qc->id; - int i; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - qc->id = v4l2_ctrl_next(ctrl_classes, qc->id); - if (unlikely(qc->id == 0)) - return -EINVAL; - - memset(qc, 0, sizeof(*qc)); - - qc->id = id; - - if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1) - return -EINVAL; - - for (i = 0; i < CX231XX_CTLS; i++) - if (cx231xx_ctls[i].v.id == qc->id) - break; - - if (i == CX231XX_CTLS) { - *qc = no_ctl; - return 0; - } - *qc = cx231xx_ctls[i].v; - - call_all(dev, core, queryctrl, qc); - - if (qc->type) - return 0; - else - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - call_all(dev, core, g_ctrl, ctrl); - return rc; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - call_all(dev, core, s_ctrl, ctrl); - return rc; -} - -static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (0 != t->index) - return -EINVAL; - - strcpy(t->name, "Tuner"); - - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; - t->signal = 0xffff; /* LOCKED */ - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (0 != t->index) - return -EINVAL; -#if 0 - call_all(dev, tuner, s_tuner, t); -#endif - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = dev->ctl_freq; - - call_all(dev, tuner, g_frequency, f); - - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - u32 if_frequency = 5400000; - - cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n", - f->frequency, f->type); - /*cx231xx_info("f->type: 1-radio 2-analogTV 3-digitalTV\n");*/ - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (0 != f->tuner) - return -EINVAL; - - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; - - /* set pre channel change settings in DIF first */ - rc = cx231xx_tuner_pre_channel_change(dev); - - dev->ctl_freq = f->frequency; - call_all(dev, tuner, s_frequency, f); - - /* set post channel change settings in DIF first */ - rc = cx231xx_tuner_post_channel_change(dev); - - if (dev->tuner_type == TUNER_NXP_TDA18271) { - if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443)) - if_frequency = 5400000; /*5.4MHz */ - else if (dev->norm & V4L2_STD_B) - if_frequency = 6000000; /*6.0MHz */ - else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK)) - if_frequency = 6900000; /*6.9MHz */ - else if (dev->norm & V4L2_STD_GH) - if_frequency = 7100000; /*7.1MHz */ - else if (dev->norm & V4L2_STD_PAL_I) - if_frequency = 7250000; /*7.25MHz */ - else if (dev->norm & V4L2_STD_SECAM_L) - if_frequency = 6900000; /*6.9MHz */ - else if (dev->norm & V4L2_STD_SECAM_LC) - if_frequency = 1250000; /*1.25MHz */ - - cx231xx_info("if_frequency is set to %d\n", if_frequency); - cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1); - - update_HH_register_after_set_DIF(dev); - } - - cx231xx_info("Set New FREQUENCY to %d\n", f->frequency); - - return rc; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG - -/* - -R, --list-registers=type=, - chip=[,min=,max=] - dump registers from to [VIDIOC_DBG_G_REGISTER] - -r, --set-register=type=, - chip=,reg=,val= - set the register [VIDIOC_DBG_S_REGISTER] - - if type == host, then is the hosts chip ID (default 0) - if type == i2cdrv (default), then is the I2C driver name or ID - if type == i2caddr, then is the 7-bit I2C address -*/ - -static int vidioc_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int ret = 0; - u8 value[4] = { 0, 0, 0, 0 }; - u32 data = 0; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_HOST: - switch (reg->match.addr) { - case 0: /* Cx231xx - internal registers */ - ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, - (u16)reg->reg, value, 4); - reg->val = value[0] | value[1] << 8 | - value[2] << 16 | value[3] << 24; - break; - case 1: /* AFE - read byte */ - ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, - (u16)reg->reg, 2, &data, 1); - reg->val = le32_to_cpu(data & 0xff); - break; - case 14: /* AFE - read dword */ - ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, - (u16)reg->reg, 2, &data, 4); - reg->val = le32_to_cpu(data); - break; - case 2: /* Video Block - read byte */ - ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, &data, 1); - reg->val = le32_to_cpu(data & 0xff); - break; - case 24: /* Video Block - read dword */ - ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, &data, 4); - reg->val = le32_to_cpu(data); - break; - case 3: /* I2S block - read byte */ - ret = cx231xx_read_i2c_data(dev, - I2S_BLK_DEVICE_ADDRESS, - (u16)reg->reg, 1, - &data, 1); - reg->val = le32_to_cpu(data & 0xff); - break; - case 34: /* I2S Block - read dword */ - ret = - cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, - (u16)reg->reg, 1, &data, 4); - reg->val = le32_to_cpu(data); - break; - } - return ret < 0 ? ret : 0; - - case V4L2_CHIP_MATCH_I2C_DRIVER: - call_all(dev, core, g_register, reg); - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR:/*for register debug*/ - switch (reg->match.addr) { - case 0: /* Cx231xx - internal registers */ - ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, - (u16)reg->reg, value, 4); - reg->val = value[0] | value[1] << 8 | - value[2] << 16 | value[3] << 24; - - break; - case 0x600:/* AFE - read byte */ - ret = cx231xx_read_i2c_master(dev, AFE_DEVICE_ADDRESS, - (u16)reg->reg, 2, - &data, 1 , 0); - reg->val = le32_to_cpu(data & 0xff); - break; - - case 0x880:/* Video Block - read byte */ - if (reg->reg < 0x0b) { - ret = cx231xx_read_i2c_master(dev, - VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, - &data, 1 , 0); - reg->val = le32_to_cpu(data & 0xff); - } else { - ret = cx231xx_read_i2c_master(dev, - VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, - &data, 4 , 0); - reg->val = le32_to_cpu(data); - } - break; - case 0x980: - ret = cx231xx_read_i2c_master(dev, - I2S_BLK_DEVICE_ADDRESS, - (u16)reg->reg, 1, - &data, 1 , 0); - reg->val = le32_to_cpu(data & 0xff); - break; - case 0x400: - ret = - cx231xx_read_i2c_master(dev, 0x40, - (u16)reg->reg, 1, - &data, 1 , 0); - reg->val = le32_to_cpu(data & 0xff); - break; - case 0xc01: - ret = - cx231xx_read_i2c_master(dev, 0xc0, - (u16)reg->reg, 2, - &data, 38, 1); - reg->val = le32_to_cpu(data); - break; - case 0x022: - ret = - cx231xx_read_i2c_master(dev, 0x02, - (u16)reg->reg, 1, - &data, 1, 2); - reg->val = le32_to_cpu(data & 0xff); - break; - case 0x322: - ret = cx231xx_read_i2c_master(dev, - 0x32, - (u16)reg->reg, 1, - &data, 4 , 2); - reg->val = le32_to_cpu(data); - break; - case 0x342: - ret = cx231xx_read_i2c_master(dev, - 0x34, - (u16)reg->reg, 1, - &data, 4 , 2); - reg->val = le32_to_cpu(data); - break; - - default: - cx231xx_info("no match device address!!\n"); - break; - } - return ret < 0 ? ret : 0; - /*return -EINVAL;*/ - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } - - call_all(dev, core, g_register, reg); - - return ret; -} - -static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int ret = 0; - __le64 buf; - u32 value; - u8 data[4] = { 0, 0, 0, 0 }; - - buf = cpu_to_le64(reg->val); - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_HOST: - { - value = (u32) buf & 0xffffffff; - - switch (reg->match.addr) { - case 0: /* cx231xx internal registers */ - data[0] = (u8) value; - data[1] = (u8) (value >> 8); - data[2] = (u8) (value >> 16); - data[3] = (u8) (value >> 24); - ret = cx231xx_write_ctrl_reg(dev, - VRT_SET_REGISTER, - (u16)reg->reg, data, - 4); - break; - case 1: /* AFE - read byte */ - ret = cx231xx_write_i2c_data(dev, - AFE_DEVICE_ADDRESS, - (u16)reg->reg, 2, - value, 1); - break; - case 14: /* AFE - read dword */ - ret = cx231xx_write_i2c_data(dev, - AFE_DEVICE_ADDRESS, - (u16)reg->reg, 2, - value, 4); - break; - case 2: /* Video Block - read byte */ - ret = - cx231xx_write_i2c_data(dev, - VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, - value, 1); - break; - case 24: /* Video Block - read dword */ - ret = - cx231xx_write_i2c_data(dev, - VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, - value, 4); - break; - case 3: /* I2S block - read byte */ - ret = - cx231xx_write_i2c_data(dev, - I2S_BLK_DEVICE_ADDRESS, - (u16)reg->reg, 1, - value, 1); - break; - case 34: /* I2S block - read dword */ - ret = - cx231xx_write_i2c_data(dev, - I2S_BLK_DEVICE_ADDRESS, - (u16)reg->reg, 1, - value, 4); - break; - } - } - return ret < 0 ? ret : 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - { - value = (u32) buf & 0xffffffff; - - switch (reg->match.addr) { - case 0:/*cx231xx internal registers*/ - data[0] = (u8) value; - data[1] = (u8) (value >> 8); - data[2] = (u8) (value >> 16); - data[3] = (u8) (value >> 24); - ret = cx231xx_write_ctrl_reg(dev, - VRT_SET_REGISTER, - (u16)reg->reg, data, - 4); - break; - case 0x600:/* AFE - read byte */ - ret = cx231xx_write_i2c_master(dev, - AFE_DEVICE_ADDRESS, - (u16)reg->reg, 2, - value, 1 , 0); - break; - - case 0x880:/* Video Block - read byte */ - if (reg->reg < 0x0b) - cx231xx_write_i2c_master(dev, - VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, - value, 1, 0); - else - cx231xx_write_i2c_master(dev, - VID_BLK_I2C_ADDRESS, - (u16)reg->reg, 2, - value, 4, 0); - break; - case 0x980: - ret = - cx231xx_write_i2c_master(dev, - I2S_BLK_DEVICE_ADDRESS, - (u16)reg->reg, 1, - value, 1, 0); - break; - case 0x400: - ret = - cx231xx_write_i2c_master(dev, - 0x40, - (u16)reg->reg, 1, - value, 1, 0); - break; - case 0xc01: - ret = - cx231xx_write_i2c_master(dev, - 0xc0, - (u16)reg->reg, 1, - value, 1, 1); - break; - - case 0x022: - ret = - cx231xx_write_i2c_master(dev, - 0x02, - (u16)reg->reg, 1, - value, 1, 2); - case 0x322: - ret = - cx231xx_write_i2c_master(dev, - 0x32, - (u16)reg->reg, 1, - value, 4, 2); - break; - - case 0x342: - ret = - cx231xx_write_i2c_master(dev, - 0x34, - (u16)reg->reg, 1, - value, 4, 2); - break; - default: - cx231xx_info("no match device address, " - "the value is %x\n", reg->match.addr); - break; - - } - - } - default: - break; - } - - call_all(dev, core, s_register, reg); - - return ret; -} -#endif - -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cc) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - cc->bounds.left = 0; - cc->bounds.top = 0; - cc->bounds.width = dev->width; - cc->bounds.height = dev->height; - cc->defrect = cc->bounds; - cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ - cc->pixelaspect.denominator = 59; - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - rc = res_get(fh); - - if (likely(rc >= 0)) - rc = videobuf_streamon(&fh->vb_vidq); - - call_all(dev, video, s_stream, 1); - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - cx25840_call(dev, video, s_stream, 0); - - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); - strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_VBI_CAPTURE | -#if 0 - V4L2_CAP_SLICED_VBI_CAPTURE | -#endif - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (unlikely(f->index >= ARRAY_SIZE(format))) - return -EINVAL; - - strlcpy(f->description, format[f->index].name, sizeof(f->description)); - f->pixelformat = format[f->index].fourcc; - - return 0; -} - -/* Sliced VBI ioctls */ -static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - f->fmt.sliced.service_set = 0; - - call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - rc = -EINVAL; - - return rc; -} - -static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - return -EINVAL; - - return 0; -} - -/* RAW VBI ioctls */ - -static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - f->fmt.vbi.sampling_rate = 6750000 * 4; - f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = 0; - f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? - PAL_VBI_START_LINE : NTSC_VBI_START_LINE; - f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? - PAL_VBI_LINES : NTSC_VBI_LINES; - f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? - PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; - f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; - - return 0; - -} - -static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - if (dev->vbi_stream_on && !fh->stream_on) { - cx231xx_errdev("%s device in use by another fh\n", __func__); - return -EBUSY; - } - - f->type = V4L2_BUF_TYPE_VBI_CAPTURE; - f->fmt.vbi.sampling_rate = 6750000 * 4; - f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = 0; - f->fmt.vbi.flags = 0; - f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? - PAL_VBI_START_LINE : NTSC_VBI_START_LINE; - f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? - PAL_VBI_LINES : NTSC_VBI_LINES; - f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? - PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; - f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; - - return 0; - -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_reqbufs(&fh->vb_vidq, rb); -} - -static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_querybuf(&fh->vb_vidq, b); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_qbuf(&fh->vb_vidq, b); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); -} - -/* ----------------------------------------------------------- */ -/* RADIO ESPECIFIC IOCTLS */ -/* ----------------------------------------------------------- */ - -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; - - strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); - strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - -static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) -{ - struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; - - if (unlikely(t->index > 0)) - return -EINVAL; - - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; - - call_all(dev, tuner, s_tuner, t); - - return 0; -} - -static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "Radio"); - return 0; -} - -static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) -{ - struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; - - if (0 != t->index) - return -EINVAL; - - call_all(dev, tuner, s_tuner, t); - - return 0; -} - -static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *file, void *fh, unsigned int i) -{ - return 0; -} - -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - int i; - - if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { - for (i = 0; i < CX231XX_CTLS; i++) { - if (cx231xx_ctls[i].v.id == c->id) - break; - } - if (i == CX231XX_CTLS) - return -EINVAL; - *c = cx231xx_ctls[i].v; - } else - *c = no_ctl; - return 0; -} - -/* - * cx231xx_v4l2_open() - * inits the device and starts isoc transfer - */ -static int cx231xx_v4l2_open(struct file *filp) -{ - int errCode = 0, radio = 0; - struct video_device *vdev = video_devdata(filp); - struct cx231xx *dev = video_drvdata(filp); - struct cx231xx_fh *fh; - enum v4l2_buf_type fh_type = 0; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - case VFL_TYPE_RADIO: - radio = 1; - break; - } - - cx231xx_videodbg("open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[fh_type], - dev->users); - -#if 0 - errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); - if (errCode < 0) { - cx231xx_errdev - ("Device locked on digital mode. Can't open analog\n"); - return -EBUSY; - } -#endif - - fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL); - if (!fh) { - cx231xx_errdev("cx231xx-video.c: Out of memory?!\n"); - return -ENOMEM; - } - if (mutex_lock_interruptible(&dev->lock)) { - kfree(fh); - return -ERESTARTSYS; - } - fh->dev = dev; - fh->radio = radio; - fh->type = fh_type; - filp->private_data = fh; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { - dev->width = norm_maxw(dev); - dev->height = norm_maxh(dev); - - /* Power up in Analog TV mode */ - if (dev->board.external_av) - cx231xx_set_power_mode(dev, - POLARIS_AVMODE_ENXTERNAL_AV); - else - cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV); - -#if 0 - cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); -#endif - - /* set video alternate setting */ - cx231xx_set_video_alternate(dev); - - /* Needed, since GPIO might have disabled power of - some i2c device */ - cx231xx_config_i2c(dev); - - /* device needs to be initialized before isoc transfer */ - dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; - - } - if (fh->radio) { - cx231xx_videodbg("video_open: setting radio device\n"); - - /* cx231xx_start_radio(dev); */ - - call_all(dev, tuner, s_radio); - } - - dev->users++; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops, - NULL, &dev->video_mode.slock, - fh->type, V4L2_FIELD_INTERLACED, - sizeof(struct cx231xx_buffer), - fh, &dev->lock); - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* Set the required alternate setting VBI interface works in - Bulk mode only */ - cx231xx_set_alt_setting(dev, INDEX_VANC, 0); - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops, - NULL, &dev->vbi_mode.slock, - fh->type, V4L2_FIELD_SEQ_TB, - sizeof(struct cx231xx_buffer), - fh, &dev->lock); - } - mutex_unlock(&dev->lock); - - return errCode; -} - -/* - * cx231xx_realease_resources() - * unregisters the v4l2,i2c and usb devices - * called when the device gets disconected or at module unload -*/ -void cx231xx_release_analog_resources(struct cx231xx *dev) -{ - - /*FIXME: I2C IR should be disconnected */ - - if (dev->radio_dev) { - if (video_is_registered(dev->radio_dev)) - video_unregister_device(dev->radio_dev); - else - video_device_release(dev->radio_dev); - dev->radio_dev = NULL; - } - if (dev->vbi_dev) { - cx231xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vbi_dev)); - if (video_is_registered(dev->vbi_dev)) - video_unregister_device(dev->vbi_dev); - else - video_device_release(dev->vbi_dev); - dev->vbi_dev = NULL; - } - if (dev->vdev) { - cx231xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vdev)); - - if (dev->board.has_417) - cx231xx_417_unregister(dev); - - if (video_is_registered(dev->vdev)) - video_unregister_device(dev->vdev); - else - video_device_release(dev->vdev); - dev->vdev = NULL; - } -} - -/* - * cx231xx_close() - * stops streaming and deallocates all resources allocated by the v4l2 - * calls and ioctls - */ -static int cx231xx_close(struct file *filp) -{ - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - - cx231xx_videodbg("users=%d\n", dev->users); - - cx231xx_videodbg("users=%d\n", dev->users); - if (res_check(fh)) - res_free(fh); - - /* - * To workaround error number=-71 on EP0 for VideoGrabber, - * need exclude following. - * FIXME: It is probably safe to remove most of these, as we're - * now avoiding the alternate setting for INDEX_VANC - */ - if (!dev->board.no_alt_vanc) - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - /* the device is already disconnect, - free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { - if (atomic_read(&dev->devlist_count) > 0) { - cx231xx_release_resources(dev); - fh->dev = NULL; - return 0; - } - return 0; - } - - /* do this before setting alternate! */ - cx231xx_uninit_vbi_isoc(dev); - - /* set alternate 0 */ - if (!dev->vbi_or_sliced_cc_mode) - cx231xx_set_alt_setting(dev, INDEX_VANC, 0); - else - cx231xx_set_alt_setting(dev, INDEX_HANC, 0); - - kfree(fh); - dev->users--; - wake_up_interruptible_nr(&dev->open, 1); - return 0; - } - - dev->users--; - if (!dev->users) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - /* the device is already disconnect, - free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { - cx231xx_release_resources(dev); - fh->dev = NULL; - return 0; - } - - /* Save some power by putting tuner to sleep */ - call_all(dev, core, s_power, 0); - - /* do this before setting alternate! */ - if (dev->USE_ISO) - cx231xx_uninit_isoc(dev); - else - cx231xx_uninit_bulk(dev); - cx231xx_set_mode(dev, CX231XX_SUSPEND); - - /* set alternate 0 */ - cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); - } - kfree(fh); - wake_up_interruptible_nr(&dev->open, 1); - return 0; -} - -static int cx231xx_v4l2_close(struct file *filp) -{ - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - int rc; - - mutex_lock(&dev->lock); - rc = cx231xx_close(filp); - mutex_unlock(&dev->lock); - return rc; -} - -/* - * cx231xx_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t -cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || - (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) { - rc = res_get(fh); - - if (unlikely(rc < 0)) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - mutex_unlock(&dev->lock); - return rc; - } - return 0; -} - -/* - * cx231xx_v4l2_poll() - * will allocate buffers when called for the first time - */ -static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - rc = res_get(fh); - - if (unlikely(rc < 0)) - return POLLERR; - - if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) || - (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) { - unsigned int res; - - mutex_lock(&dev->lock); - res = videobuf_poll_stream(filp, &fh->vb_vidq, wait); - mutex_unlock(&dev->lock); - return res; - } - return POLLERR; -} - -/* - * cx231xx_v4l2_mmap() - */ -static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - rc = res_get(fh); - - if (unlikely(rc < 0)) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - mutex_unlock(&dev->lock); - - cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - - (unsigned long)vma->vm_start, rc); - - return rc; -} - -static const struct v4l2_file_operations cx231xx_v4l_fops = { - .owner = THIS_MODULE, - .open = cx231xx_v4l2_open, - .release = cx231xx_v4l2_close, - .read = cx231xx_v4l2_read, - .poll = cx231xx_v4l2_poll, - .mmap = cx231xx_v4l2_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, - .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_s_std = vidioc_s_std, - .vidioc_g_std = vidioc_g_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, -#endif -}; - -static struct video_device cx231xx_vbi_template; - -static const struct video_device cx231xx_video_template = { - .fops = &cx231xx_v4l_fops, - .release = video_device_release, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_PAL, -}; - -static const struct v4l2_file_operations radio_fops = { - .owner = THIS_MODULE, - .open = cx231xx_v4l2_open, - .release = cx231xx_v4l2_close, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, - .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, - .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, -#endif -}; - -static struct video_device cx231xx_radio_template = { - .name = "cx231xx-radio", - .fops = &radio_fops, - .ioctl_ops = &radio_ioctl_ops, -}; - -/******************************** usb interface ******************************/ - -static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, - const struct video_device - *template, const char *type_name) -{ - struct video_device *vfd; - - vfd = video_device_alloc(); - if (NULL == vfd) - return NULL; - - *vfd = *template; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; - vfd->debug = video_debug; - vfd->lock = &dev->lock; - - snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); - - video_set_drvdata(vfd, dev); - return vfd; -} - -int cx231xx_register_analog_devices(struct cx231xx *dev) -{ - int ret; - - cx231xx_info("%s: v4l2 driver version %s\n", - dev->name, CX231XX_VERSION); - - /* set default norm */ - /*dev->norm = cx231xx_video_template.current_norm; */ - dev->width = norm_maxw(dev); - dev->height = norm_maxh(dev); - dev->interlaced = 0; - - /* Analog specific initialization */ - dev->format = &format[0]; - - /* Set the initial input */ - video_mux(dev, dev->video_input); - - /* Audio defaults */ - dev->mute = 1; - dev->volume = 0x1f; - - /* enable vbi capturing */ - /* write code here... */ - - /* allocate and fill video video_device struct */ - dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video"); - if (!dev->vdev) { - cx231xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; - } - - /* register v4l2 video video_device */ - ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, - video_nr[dev->devno]); - if (ret) { - cx231xx_errdev("unable to register video device (error=%i).\n", - ret); - return ret; - } - - cx231xx_info("%s/0: registered device %s [v4l2]\n", - dev->name, video_device_node_name(dev->vdev)); - - /* Initialize VBI template */ - memcpy(&cx231xx_vbi_template, &cx231xx_video_template, - sizeof(cx231xx_vbi_template)); - strcpy(cx231xx_vbi_template.name, "cx231xx-vbi"); - - /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi"); - - /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]); - if (ret < 0) { - cx231xx_errdev("unable to register vbi device\n"); - return ret; - } - - cx231xx_info("%s/0: registered device %s\n", - dev->name, video_device_node_name(dev->vbi_dev)); - - if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) { - dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template, - "radio"); - if (!dev->radio_dev) { - cx231xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; - } - ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, - radio_nr[dev->devno]); - if (ret < 0) { - cx231xx_errdev("can't register radio device\n"); - return ret; - } - cx231xx_info("Registered radio device as %s\n", - video_device_node_name(dev->radio_dev)); - } - - cx231xx_info("V4L2 device registered as %s and %s\n", - video_device_node_name(dev->vdev), - video_device_node_name(dev->vbi_dev)); - - return 0; -} diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h deleted file mode 100644 index a89d020de948..000000000000 --- a/drivers/media/video/cx231xx/cx231xx.h +++ /dev/null @@ -1,1012 +0,0 @@ -/* - cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices - - Copyright (C) 2008 - Based on em28xx driver - - This program is free software; 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 _CX231XX_H -#define _CX231XX_H - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "cx231xx-reg.h" -#include "cx231xx-pcb-cfg.h" -#include "cx231xx-conf-reg.h" - -#define DRIVER_NAME "cx231xx" -#define PWR_SLEEP_INTERVAL 10 - -/* I2C addresses for control block in Cx231xx */ -#define AFE_DEVICE_ADDRESS 0x60 -#define I2S_BLK_DEVICE_ADDRESS 0x98 -#define VID_BLK_I2C_ADDRESS 0x88 -#define VERVE_I2C_ADDRESS 0x40 -#define DIF_USE_BASEBAND 0xFFFFFFFF - -/* Boards supported by driver */ -#define CX231XX_BOARD_UNKNOWN 0 -#define CX231XX_BOARD_CNXT_CARRAERA 1 -#define CX231XX_BOARD_CNXT_SHELBY 2 -#define CX231XX_BOARD_CNXT_RDE_253S 3 -#define CX231XX_BOARD_CNXT_RDU_253S 4 -#define CX231XX_BOARD_CNXT_VIDEO_GRABBER 5 -#define CX231XX_BOARD_CNXT_RDE_250 6 -#define CX231XX_BOARD_CNXT_RDU_250 7 -#define CX231XX_BOARD_HAUPPAUGE_EXETER 8 -#define CX231XX_BOARD_HAUPPAUGE_USBLIVE2 9 -#define CX231XX_BOARD_PV_PLAYTV_USB_HYBRID 10 -#define CX231XX_BOARD_PV_XCAPTURE_USB 11 -#define CX231XX_BOARD_KWORLD_UB430_USB_HYBRID 12 -#define CX231XX_BOARD_ICONBIT_U100 13 -#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14 -#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15 - -/* Limits minimum and default number of buffers */ -#define CX231XX_MIN_BUF 4 -#define CX231XX_DEF_BUF 12 -#define CX231XX_DEF_VBI_BUF 6 - -#define VBI_LINE_COUNT 17 -#define VBI_LINE_LENGTH 1440 - -/*Limits the max URB message size */ -#define URB_MAX_CTRL_SIZE 80 - -/* Params for validated field */ -#define CX231XX_BOARD_NOT_VALIDATED 1 -#define CX231XX_BOARD_VALIDATED 0 - -/* maximum number of cx231xx boards */ -#define CX231XX_MAXBOARDS 8 - -/* maximum number of frames that can be queued */ -#define CX231XX_NUM_FRAMES 5 - -/* number of buffers for isoc transfers */ -#define CX231XX_NUM_BUFS 8 - -/* number of packets for each buffer - windows requests only 40 packets .. so we better do the same - this is what I found out for all alternate numbers there! - */ -#define CX231XX_NUM_PACKETS 40 - -/* default alternate; 0 means choose the best */ -#define CX231XX_PINOUT 0 - -#define CX231XX_INTERLACED_DEFAULT 1 - -/* time to wait when stopping the isoc transfer */ -#define CX231XX_URB_TIMEOUT \ - msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS) - -#define CX231xx_NORMS (\ - V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \ - V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ - V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \ - V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK) - -#define SLEEP_S5H1432 30 -#define CX23417_OSC_EN 8 -#define CX23417_RESET 9 - -struct cx23417_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ - int depth; - int flags; - u32 cxformat; -}; -enum cx231xx_mode { - CX231XX_SUSPEND, - CX231XX_ANALOG_MODE, - CX231XX_DIGITAL_MODE, -}; - -enum cx231xx_std_mode { - CX231XX_TV_AIR = 0, - CX231XX_TV_CABLE -}; - -enum cx231xx_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -struct cx231xx; - -struct cx231xx_isoc_ctl { - /* max packet size of isoc transaction */ - int max_pkt_size; - - /* number of allocated urbs */ - int num_bufs; - - /* urb for isoc transfers */ - struct urb **urb; - - /* transfer buffers for isoc transfer */ - char **transfer_buffer; - - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - - /* Stores already requested buffers */ - struct cx231xx_buffer *buf; - - /* Stores the number of received fields */ - int nfields; - - /* isoc urb callback */ - int (*isoc_copy) (struct cx231xx *dev, struct urb *urb); -}; - -struct cx231xx_bulk_ctl { - /* max packet size of bulk transaction */ - int max_pkt_size; - - /* number of allocated urbs */ - int num_bufs; - - /* urb for bulk transfers */ - struct urb **urb; - - /* transfer buffers for bulk transfer */ - char **transfer_buffer; - - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - - /* Stores already requested buffers */ - struct cx231xx_buffer *buf; - - /* Stores the number of received fields */ - int nfields; - - /* bulk urb callback */ - int (*bulk_copy) (struct cx231xx *dev, struct urb *urb); -}; - -struct cx231xx_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ - int depth; - int reg; -}; - -/* buffer for one video frame */ -struct cx231xx_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - struct list_head frame; - int top_field; - int receiving; -}; - -enum ps_package_head { - CX231XX_NEED_ADD_PS_PACKAGE_HEAD = 0, - CX231XX_NONEED_PS_PACKAGE_HEAD -}; - -struct cx231xx_dmaqueue { - struct list_head active; - struct list_head queued; - - wait_queue_head_t wq; - - /* Counters to control buffer fill */ - int pos; - u8 is_partial_line; - u8 partial_buf[8]; - u8 last_sav; - int current_field; - u32 bytes_left_in_line; - u32 lines_completed; - u8 field1_done; - u32 lines_per_field; - - /*Mpeg2 control buffer*/ - u8 *p_left_data; - u32 left_data_count; - u8 mpeg_buffer_done; - u32 mpeg_buffer_completed; - enum ps_package_head add_ps_package_head; - char ps_head[10]; -}; - -/* inputs */ - -#define MAX_CX231XX_INPUT 4 - -enum cx231xx_itype { - CX231XX_VMUX_COMPOSITE1 = 1, - CX231XX_VMUX_SVIDEO, - CX231XX_VMUX_TELEVISION, - CX231XX_VMUX_CABLE, - CX231XX_RADIO, - CX231XX_VMUX_DVB, - CX231XX_VMUX_DEBUG -}; - -enum cx231xx_v_input { - CX231XX_VIN_1_1 = 0x1, - CX231XX_VIN_2_1, - CX231XX_VIN_3_1, - CX231XX_VIN_4_1, - CX231XX_VIN_1_2 = 0x01, - CX231XX_VIN_2_2, - CX231XX_VIN_3_2, - CX231XX_VIN_1_3 = 0x1, - CX231XX_VIN_2_3, - CX231XX_VIN_3_3, -}; - -/* cx231xx has two audio inputs: tuner and line in */ -enum cx231xx_amux { - /* This is the only entry for cx231xx tuner input */ - CX231XX_AMUX_VIDEO, /* cx231xx tuner */ - CX231XX_AMUX_LINE_IN, /* Line In */ -}; - -struct cx231xx_reg_seq { - unsigned char bit; - unsigned char val; - int sleep; -}; - -struct cx231xx_input { - enum cx231xx_itype type; - unsigned int vmux; - enum cx231xx_amux amux; - struct cx231xx_reg_seq *gpio; -}; - -#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr]) - -enum cx231xx_decoder { - CX231XX_NODECODER, - CX231XX_AVDECODER -}; - -enum CX231XX_I2C_MASTER_PORT { - I2C_0 = 0, - I2C_1 = 1, - I2C_2 = 2, - I2C_3 = 3 -}; - -struct cx231xx_board { - char *name; - int vchannels; - int tuner_type; - int tuner_addr; - v4l2_std_id norm; /* tv norm */ - - /* demod related */ - int demod_addr; - u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */ - - /* GPIO Pins */ - struct cx231xx_reg_seq *dvb_gpio; - struct cx231xx_reg_seq *suspend_gpio; - struct cx231xx_reg_seq *tuner_gpio; - /* Negative means don't use it */ - s8 tuner_sif_gpio; - s8 tuner_scl_gpio; - s8 tuner_sda_gpio; - - /* PIN ctrl */ - u32 ctl_pin_status_mask; - u8 agc_analog_digital_select_gpio; - u32 gpio_pin_status_mask; - - /* i2c masters */ - u8 tuner_i2c_master; - u8 demod_i2c_master; - u8 ir_i2c_master; - - /* for devices with I2C chips for IR */ - char *rc_map_name; - - unsigned int max_range_640_480:1; - unsigned int has_dvb:1; - unsigned int has_417:1; - unsigned int valid:1; - unsigned int no_alt_vanc:1; - unsigned int external_av:1; - unsigned int dont_use_port_3:1; - - unsigned char xclk, i2c_speed; - - enum cx231xx_decoder decoder; - int output_mode; - - struct cx231xx_input input[MAX_CX231XX_INPUT]; - struct cx231xx_input radio; - struct rc_map *ir_codes; -}; - -/* device states */ -enum cx231xx_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, -}; - -enum AFE_MODE { - AFE_MODE_LOW_IF, - AFE_MODE_BASEBAND, - AFE_MODE_EU_HI_IF, - AFE_MODE_US_HI_IF, - AFE_MODE_JAPAN_HI_IF -}; - -enum AUDIO_INPUT { - AUDIO_INPUT_MUTE, - AUDIO_INPUT_LINE, - AUDIO_INPUT_TUNER_TV, - AUDIO_INPUT_SPDIF, - AUDIO_INPUT_TUNER_FM -}; - -#define CX231XX_AUDIO_BUFS 5 -#define CX231XX_NUM_AUDIO_PACKETS 16 -#define CX231XX_ISO_NUM_AUDIO_PACKETS 64 - -/* cx231xx extensions */ -#define CX231XX_AUDIO 0x10 -#define CX231XX_DVB 0x20 - -struct cx231xx_audio { - char name[50]; - char *transfer_buffer[CX231XX_AUDIO_BUFS]; - struct urb *urb[CX231XX_AUDIO_BUFS]; - struct usb_device *udev; - unsigned int capture_transfer_done; - struct snd_pcm_substream *capture_pcm_substream; - - unsigned int hwptr_done_capture; - struct snd_card *sndcard; - - int users, shutdown; - /* locks */ - spinlock_t slock; - - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - u16 end_point_addr; -}; - -struct cx231xx; - -struct cx231xx_fh { - struct cx231xx *dev; - unsigned int stream_on:1; /* Locks streams */ - int radio; - - struct videobuf_queue vb_vidq; - - enum v4l2_buf_type type; - - - -/*following is copyed from cx23885.h*/ - u32 resources; - - /* video overlay */ - struct v4l2_window win; - struct v4l2_clip *clips; - unsigned int nclips; - - /* video capture */ - struct cx23417_fmt *fmt; - unsigned int width, height; - - /* vbi capture */ - struct videobuf_queue vidq; - struct videobuf_queue vbiq; - - /* MPEG Encoder specifics ONLY */ - - atomic_t v4l_reading; -}; - -/*****************************************************************/ -/* set/get i2c */ -/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */ -#define I2C_SPEED_1M 0x0 -#define I2C_SPEED_400K 0x1 -#define I2C_SPEED_100K 0x2 -#define I2C_SPEED_5M 0x3 - -/* 0-- STOP transaction */ -#define I2C_STOP 0x0 -/* 1-- do not transmit STOP at end of transaction */ -#define I2C_NOSTOP 0x1 -/* 1--allow slave to insert clock wait states */ -#define I2C_SYNC 0x1 - -struct cx231xx_i2c { - struct cx231xx *dev; - - int nr; - - /* i2c i/o */ - struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; - u32 i2c_rc; - - /* different settings for each bus */ - u8 i2c_period; - u8 i2c_nostop; - u8 i2c_reserve; -}; - -struct cx231xx_i2c_xfer_data { - u8 dev_addr; - u8 direction; /* 1 - IN, 0 - OUT */ - u8 saddr_len; /* sub address len */ - u16 saddr_dat; /* sub addr data */ - u8 buf_size; /* buffer size */ - u8 *p_buffer; /* pointer to the buffer */ -}; - -struct VENDOR_REQUEST_IN { - u8 bRequest; - u16 wValue; - u16 wIndex; - u16 wLength; - u8 direction; - u8 bData; - u8 *pBuff; -}; - -struct cx231xx_tvnorm { - char *name; - v4l2_std_id id; - u32 cxiformat; - u32 cxoformat; -}; - -struct cx231xx_ctrl { - struct v4l2_queryctrl v; - u32 off; - u32 reg; - u32 mask; - u32 shift; -}; - -enum TRANSFER_TYPE { - Raw_Video = 0, - Audio, - Vbi, /* VANC */ - Sliced_cc, /* HANC */ - TS1_serial_mode, - TS2, - TS1_parallel_mode -} ; - -struct cx231xx_video_mode { - /* Isoc control struct */ - struct cx231xx_dmaqueue vidq; - struct cx231xx_isoc_ctl isoc_ctl; - struct cx231xx_bulk_ctl bulk_ctl; - /* locks */ - spinlock_t slock; - - /* usb transfer */ - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - u16 end_point_addr; -}; -/* -struct cx23885_dmaqueue { - struct list_head active; - struct list_head queued; - struct timer_list timeout; - struct btcx_riscmem stopper; - u32 count; -}; -*/ -struct cx231xx_tsport { - struct cx231xx *dev; - - int nr; - int sram_chno; - - struct videobuf_dvb_frontends frontends; - - /* dma queues */ - - u32 ts_packet_size; - u32 ts_packet_count; - - int width; - int height; - - /* locks */ - spinlock_t slock; - - /* registers */ - u32 reg_gpcnt; - u32 reg_gpcnt_ctl; - u32 reg_dma_ctl; - u32 reg_lngth; - u32 reg_hw_sop_ctrl; - u32 reg_gen_ctrl; - u32 reg_bd_pkt_status; - u32 reg_sop_status; - u32 reg_fifo_ovfl_stat; - u32 reg_vld_misc; - u32 reg_ts_clk_en; - u32 reg_ts_int_msk; - u32 reg_ts_int_stat; - u32 reg_src_sel; - - /* Default register vals */ - int pci_irqmask; - u32 dma_ctl_val; - u32 ts_int_msk_val; - u32 gen_ctrl_val; - u32 ts_clk_en_val; - u32 src_sel_val; - u32 vld_misc_val; - u32 hw_sop_ctrl_val; - - /* Allow a single tsport to have multiple frontends */ - u32 num_frontends; - void *port_priv; -}; - -/* main device struct */ -struct cx231xx { - /* generic device properties */ - char name[30]; /* name (including minor) of the device */ - int model; /* index in the device_data struct */ - int devno; /* marks the number of this device */ - - struct cx231xx_board board; - - /* For I2C IR support */ - struct IR_i2c_init_data init_data; - struct i2c_client *ir_i2c_client; - - unsigned int stream_on:1; /* Locks streams */ - unsigned int vbi_stream_on:1; /* Locks streams for VBI */ - unsigned int has_audio_class:1; - unsigned int has_alsa_audio:1; - - struct cx231xx_fmt *format; - - struct v4l2_device v4l2_dev; - struct v4l2_subdev *sd_cx25840; - struct v4l2_subdev *sd_tuner; - - struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ - atomic_t stream_started; /* stream should be running if true */ - - struct list_head devlist; - - int tuner_type; /* type of the tuner */ - int tuner_addr; /* tuner address */ - - /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ - struct cx231xx_i2c i2c_bus[3]; - unsigned int xc_fw_load_done:1; - /* locks */ - struct mutex gpio_i2c_lock; - struct mutex i2c_lock; - - /* video for linux */ - int users; /* user count for exclusive use */ - struct video_device *vdev; /* video for linux device struct */ - v4l2_std_id norm; /* selected tv norm */ - int ctl_freq; /* selected frequency */ - unsigned int ctl_ainput; /* selected audio input */ - int mute; - int volume; - - /* frame properties */ - int width; /* current frame width */ - int height; /* current frame height */ - int interlaced; /* 1=interlace fileds, 0=just top fileds */ - - struct cx231xx_audio adev; - - /* states */ - enum cx231xx_dev_state state; - - struct work_struct request_module_wk; - - /* locks */ - struct mutex lock; - struct mutex ctrl_urb_lock; /* protects urb_buf */ - struct list_head inqueue, outqueue; - wait_queue_head_t open, wait_frame, wait_stream; - struct video_device *vbi_dev; - struct video_device *radio_dev; - - unsigned char eedata[256]; - - struct cx231xx_video_mode video_mode; - struct cx231xx_video_mode vbi_mode; - struct cx231xx_video_mode sliced_cc_mode; - struct cx231xx_video_mode ts1_mode; - - atomic_t devlist_count; - - struct usb_device *udev; /* the usb device */ - char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ - - /* helper funcs that call usb_control_msg */ - int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, - char *buf, int len); - int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, - char *buf, int len); - int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus, - struct cx231xx_i2c_xfer_data *req_data); - int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr, - u8 *buf, u8 len); - int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr, - u8 *buf, u8 len); - - int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq); - int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev); - - enum cx231xx_mode mode; - - struct cx231xx_dvb *dvb; - - /* Cx231xx supported PCB config's */ - struct pcb_config current_pcb_config; - u8 current_scenario_idx; - u8 interface_count; - u8 max_iad_interface_count; - - /* GPIO related register direction and values */ - u32 gpio_dir; - u32 gpio_val; - - /* Power Modes */ - int power_mode; - - /* afe parameters */ - enum AFE_MODE afe_mode; - u32 afe_ref_count; - - /* video related parameters */ - u32 video_input; - u32 active_mode; - u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */ - enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */ - - /*mode: digital=1 or analog=0*/ - u8 mode_tv; - - u8 USE_ISO; - struct cx231xx_tvnorm encodernorm; - struct cx231xx_tsport ts1, ts2; - struct cx2341x_mpeg_params mpeg_params; - struct video_device *v4l_device; - atomic_t v4l_reader_count; - u32 freq; - unsigned int input; - u32 cx23417_mailbox; - u32 __iomem *lmmio; - u8 __iomem *bmmio; -}; - -extern struct list_head cx231xx_devlist; - -#define cx25840_call(cx231xx, o, f, args...) \ - v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args) -#define tuner_call(cx231xx, o, f, args...) \ - v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args) -#define call_all(dev, o, f, args...) \ - v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) - -struct cx231xx_ops { - struct list_head next; - char *name; - int id; - int (*init) (struct cx231xx *); - int (*fini) (struct cx231xx *); -}; - -/* call back functions in dvb module */ -int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq); -int cx231xx_reset_analog_tuner(struct cx231xx *dev); - -/* Provided by cx231xx-i2c.c */ -void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c); -int cx231xx_i2c_register(struct cx231xx_i2c *bus); -int cx231xx_i2c_unregister(struct cx231xx_i2c *bus); - -/* Internal block control functions */ -int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, - u8 saddr_len, u32 *data, u8 data_len, int master); -int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr, - u8 saddr_len, u32 data, u8 data_len, int master); -int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, - u16 saddr, u8 saddr_len, u32 *data, u8 data_len); -int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, - u16 saddr, u8 saddr_len, u32 data, u8 data_len); -int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, - u16 register_address, u8 bit_start, u8 bit_end, - u32 value); -int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, - u16 saddr, u32 mask, u32 value); -u32 cx231xx_set_field(u32 field_mask, u32 data); - -/*verve r/w*/ -void initGPIO(struct cx231xx *dev); -void uninitGPIO(struct cx231xx *dev); -/* afe related functions */ -int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count); -int cx231xx_afe_init_channels(struct cx231xx *dev); -int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev); -int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux); -int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode); -int cx231xx_afe_update_power_control(struct cx231xx *dev, - enum AV_MODE avmode); -int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input); - -/* i2s block related functions */ -int cx231xx_i2s_blk_initialize(struct cx231xx *dev); -int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev, - enum AV_MODE avmode); -int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input); - -/* DIF related functions */ -int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, - u32 function_mode, u32 standard); -void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, - u8 spectral_invert, u32 mode); -u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd); -void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, - u8 spectral_invert, u32 mode); -void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev); -void reset_s5h1432_demod(struct cx231xx *dev); -void cx231xx_dump_HH_reg(struct cx231xx *dev); -void update_HH_register_after_set_DIF(struct cx231xx *dev); -void cx231xx_dump_SC_reg(struct cx231xx *dev); - - - -int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard); -int cx231xx_tuner_pre_channel_change(struct cx231xx *dev); -int cx231xx_tuner_post_channel_change(struct cx231xx *dev); - -/* video parser functions */ -u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, - u32 *p_bytes_used); -u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, - u32 *p_bytes_used); -int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 *p_buffer, u32 bytes_to_copy); -void cx231xx_reset_video_buffer(struct cx231xx *dev, - struct cx231xx_dmaqueue *dma_q); -u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q); -u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 *p_line, u32 length, int field_number); -u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, - u8 sav_eav, u8 *p_buffer, u32 buffer_size); -void cx231xx_swab(u16 *from, u16 *to, u16 len); - -/* Provided by cx231xx-core.c */ - -u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count); -void cx231xx_queue_unusedframes(struct cx231xx *dev); -void cx231xx_release_buffers(struct cx231xx *dev); - -/* read from control pipe */ -int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, - char *buf, int len); - -/* write to control pipe */ -int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, - char *buf, int len); -int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode); - -int cx231xx_send_vendor_cmd(struct cx231xx *dev, - struct VENDOR_REQUEST_IN *ven_req); -int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, - struct cx231xx_i2c_xfer_data *req_data); - -/* Gpio related functions */ -int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, - u8 len, u8 request, u8 direction); -int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); -int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); -int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value); -int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number, - int pin_value); - -int cx231xx_gpio_i2c_start(struct cx231xx *dev); -int cx231xx_gpio_i2c_end(struct cx231xx *dev); -int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data); -int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf); -int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev); -int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev); -int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev); - -int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); -int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); - -/* audio related functions */ -int cx231xx_set_audio_decoder_input(struct cx231xx *dev, - enum AUDIO_INPUT audio_input); - -int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type); -int cx231xx_set_video_alternate(struct cx231xx *dev); -int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt); -int is_fw_load(struct cx231xx *dev); -int cx231xx_check_fw(struct cx231xx *dev); -int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct cx231xx *dev, - struct urb *urb)); -int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*bulk_copy) (struct cx231xx *dev, - struct urb *urb)); -void cx231xx_stop_TS1(struct cx231xx *dev); -void cx231xx_start_TS1(struct cx231xx *dev); -void cx231xx_uninit_isoc(struct cx231xx *dev); -void cx231xx_uninit_bulk(struct cx231xx *dev); -int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode); -int cx231xx_unmute_audio(struct cx231xx *dev); -int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size); -void cx231xx_disable656(struct cx231xx *dev); -void cx231xx_enable656(struct cx231xx *dev); -int cx231xx_demod_reset(struct cx231xx *dev); -int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio); - -/* Device list functions */ -void cx231xx_release_resources(struct cx231xx *dev); -void cx231xx_release_analog_resources(struct cx231xx *dev); -int cx231xx_register_analog_devices(struct cx231xx *dev); -void cx231xx_remove_from_devlist(struct cx231xx *dev); -void cx231xx_add_into_devlist(struct cx231xx *dev); -void cx231xx_init_extension(struct cx231xx *dev); -void cx231xx_close_extension(struct cx231xx *dev); - -/* hardware init functions */ -int cx231xx_dev_init(struct cx231xx *dev); -void cx231xx_dev_uninit(struct cx231xx *dev); -void cx231xx_config_i2c(struct cx231xx *dev); -int cx231xx_config(struct cx231xx *dev); - -/* Stream control functions */ -int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask); -int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask); - -int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type); - -/* Power control functions */ -int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode); -int cx231xx_power_suspend(struct cx231xx *dev); - -/* chip specific control functions */ -int cx231xx_init_ctrl_pin_status(struct cx231xx *dev); -int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, - u8 analog_or_digital); -int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3); - -/* video audio decoder related functions */ -void video_mux(struct cx231xx *dev, int index); -int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input); -int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input); -int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev); -int cx231xx_set_audio_input(struct cx231xx *dev, u8 input); - -/* Provided by cx231xx-video.c */ -int cx231xx_register_extension(struct cx231xx_ops *dev); -void cx231xx_unregister_extension(struct cx231xx_ops *dev); -void cx231xx_init_extension(struct cx231xx *dev); -void cx231xx_close_extension(struct cx231xx *dev); - -/* Provided by cx231xx-cards.c */ -extern void cx231xx_pre_card_setup(struct cx231xx *dev); -extern void cx231xx_card_setup(struct cx231xx *dev); -extern struct cx231xx_board cx231xx_boards[]; -extern struct usb_device_id cx231xx_id_table[]; -extern const unsigned int cx231xx_bcount; -int cx231xx_tuner_callback(void *ptr, int component, int command, int arg); - -/* cx23885-417.c */ -extern int cx231xx_417_register(struct cx231xx *dev); -extern void cx231xx_417_unregister(struct cx231xx *dev); - -/* cx23885-input.c */ - -#if defined(CONFIG_VIDEO_CX231XX_RC) -int cx231xx_ir_init(struct cx231xx *dev); -void cx231xx_ir_exit(struct cx231xx *dev); -#else -#define cx231xx_ir_init(dev) (0) -#define cx231xx_ir_exit(dev) (0) -#endif - - -/* printk macros */ - -#define cx231xx_err(fmt, arg...) do {\ - printk(KERN_ERR fmt , ##arg); } while (0) - -#define cx231xx_errdev(fmt, arg...) do {\ - printk(KERN_ERR "%s: "fmt,\ - dev->name , ##arg); } while (0) - -#define cx231xx_info(fmt, arg...) do {\ - printk(KERN_INFO "%s: "fmt,\ - dev->name , ##arg); } while (0) -#define cx231xx_warn(fmt, arg...) do {\ - printk(KERN_WARNING "%s: "fmt,\ - dev->name , ##arg); } while (0) - -static inline unsigned int norm_maxw(struct cx231xx *dev) -{ - if (dev->board.max_range_640_480) - return 640; - else - return 720; -} - -static inline unsigned int norm_maxh(struct cx231xx *dev) -{ - if (dev->board.max_range_640_480) - return 480; - else - return (dev->norm & V4L2_STD_625_50) ? 576 : 480; -} -#endif diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig deleted file mode 100644 index 928ef0d0429f..000000000000 --- a/drivers/media/video/em28xx/Kconfig +++ /dev/null @@ -1,58 +0,0 @@ -config VIDEO_EM28XX - tristate "Empia EM28xx USB video capture support" - depends on VIDEO_DEV && I2C - select VIDEO_TUNER - select VIDEO_TVEEPROM - select VIDEOBUF_VMALLOC - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO - - ---help--- - This is a video4linux driver for Empia 28xx based TV cards. - - To compile this driver as a module, choose M here: the - module will be called em28xx - -config VIDEO_EM28XX_ALSA - depends on VIDEO_EM28XX && SND - select SND_PCM - tristate "Empia EM28xx ALSA audio module" - ---help--- - This is an ALSA driver for some Empia 28xx based TV cards. - - This is not required for em2800/em2820/em2821 boards. However, - newer em28xx devices uses Vendor Class for audio, instead of - implementing the USB Audio Class. For those chips, this module - will enable digital audio. - - To compile this driver as a module, choose M here: the - module will be called em28xx-alsa - -config VIDEO_EM28XX_DVB - tristate "DVB/ATSC Support for em28xx based TV cards" - depends on VIDEO_EM28XX && DVB_CORE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_S921 if !DVB_FE_CUSTOMISE - select DVB_DRXD if !DVB_FE_CUSTOMISE - select DVB_CXD2820R if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE - select DVB_TDA10071 if !DVB_FE_CUSTOMISE - select DVB_A8293 if !DVB_FE_CUSTOMISE - select VIDEOBUF_DVB - ---help--- - This adds support for DVB cards based on the - Empiatech em28xx chips. - -config VIDEO_EM28XX_RC - tristate "EM28XX Remote Controller support" - depends on RC_CORE - depends on VIDEO_EM28XX - depends on !(RC_CORE=m && VIDEO_EM28XX=y) - default VIDEO_EM28XX - ---help--- - Enables Remote Controller support on em28xx driver. diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile deleted file mode 100644 index 65c7c29e4161..000000000000 --- a/drivers/media/video/em28xx/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o -em28xx-y += em28xx-core.o em28xx-vbi.o - -em28xx-alsa-objs := em28xx-audio.o -em28xx-rc-objs := em28xx-input.o - -obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o -obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o -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/tuners -ccflags-y += -Idrivers/media/dvb-core -ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c deleted file mode 100644 index 2fdb66ee44ab..000000000000 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ /dev/null @@ -1,751 +0,0 @@ -/* - * Empiatech em28x1 audio extension - * - * Copyright (C) 2006 Markus Rechberger - * - * Copyright (C) 2007-2011 Mauro Carvalho Chehab - * - Port to work with the in-kernel driver - * - Cleanups, fixes, alsa-controls, etc. - * - * This driver is based on my previous au600 usb pstn audio driver - * and inherits all the copyrights - * - * This program is free software; 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "em28xx.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); - -#define dprintk(fmt, arg...) do { \ - if (debug) \ - printk(KERN_INFO "em28xx-audio %s: " fmt, \ - __func__, ##arg); \ - } while (0) - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; - -static int em28xx_deinit_isoc_audio(struct em28xx *dev) -{ - int i; - - dprintk("Stopping isoc\n"); - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - if (!irqs_disabled()) - usb_kill_urb(dev->adev.urb[i]); - else - usb_unlink_urb(dev->adev.urb[i]); - - usb_free_urb(dev->adev.urb[i]); - dev->adev.urb[i] = NULL; - - kfree(dev->adev.transfer_buffer[i]); - dev->adev.transfer_buffer[i] = NULL; - } - - return 0; -} - -static void em28xx_audio_isocirq(struct urb *urb) -{ - struct em28xx *dev = urb->context; - int i; - unsigned int oldptr; - int period_elapsed = 0; - int status; - unsigned char *cp; - unsigned int stride; - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - dprintk("urb completition error %d.\n", urb->status); - break; - } - - if (atomic_read(&dev->stream_started) == 0) - return; - - if (dev->adev.capture_pcm_substream) { - substream = dev->adev.capture_pcm_substream; - runtime = substream->runtime; - stride = runtime->frame_bits >> 3; - - for (i = 0; i < urb->number_of_packets; i++) { - int length = - urb->iso_frame_desc[i].actual_length / stride; - cp = (unsigned char *)urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - - if (!length) - continue; - - oldptr = dev->adev.hwptr_done_capture; - if (oldptr + length >= runtime->buffer_size) { - unsigned int cnt = - runtime->buffer_size - oldptr; - memcpy(runtime->dma_area + oldptr * stride, cp, - cnt * stride); - memcpy(runtime->dma_area, cp + cnt * stride, - length * stride - cnt * stride); - } else { - memcpy(runtime->dma_area + oldptr * stride, cp, - length * stride); - } - - snd_pcm_stream_lock(substream); - - dev->adev.hwptr_done_capture += length; - if (dev->adev.hwptr_done_capture >= - runtime->buffer_size) - dev->adev.hwptr_done_capture -= - runtime->buffer_size; - - dev->adev.capture_transfer_done += length; - if (dev->adev.capture_transfer_done >= - runtime->period_size) { - dev->adev.capture_transfer_done -= - runtime->period_size; - period_elapsed = 1; - } - - snd_pcm_stream_unlock(substream); - } - if (period_elapsed) - snd_pcm_period_elapsed(substream); - } - urb->status = 0; - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status < 0) { - em28xx_errdev("resubmit of audio urb failed (error=%i)\n", - status); - } - return; -} - -static int em28xx_init_audio_isoc(struct em28xx *dev) -{ - int i, errCode; - const int sb_size = EM28XX_NUM_AUDIO_PACKETS * - EM28XX_AUDIO_MAX_PACKET_SIZE; - - dprintk("Starting isoc transfers\n"); - - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - struct urb *urb; - int j, k; - - dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); - if (!dev->adev.transfer_buffer[i]) - return -ENOMEM; - - memset(dev->adev.transfer_buffer[i], 0x80, sb_size); - urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (!urb) { - em28xx_errdev("usb_alloc_urb failed!\n"); - for (j = 0; j < i; j++) { - usb_free_urb(dev->adev.urb[j]); - kfree(dev->adev.transfer_buffer[j]); - } - return -ENOMEM; - } - - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->adev.transfer_buffer[i]; - urb->interval = 1; - urb->complete = em28xx_audio_isocirq; - urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; - urb->transfer_buffer_length = sb_size; - - for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; - j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - EM28XX_AUDIO_MAX_PACKET_SIZE; - } - dev->adev.urb[i] = urb; - } - - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); - if (errCode) { - em28xx_errdev("submit of audio urb failed\n"); - em28xx_deinit_isoc_audio(dev); - atomic_set(&dev->stream_started, 0); - return errCode; - } - - } - - return 0; -} - -static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, - size_t size) -{ - struct snd_pcm_runtime *runtime = subs->runtime; - - dprintk("Allocating vbuffer\n"); - if (runtime->dma_area) { - if (runtime->dma_bytes > size) - return 0; - - vfree(runtime->dma_area); - } - runtime->dma_area = vmalloc(size); - if (!runtime->dma_area) - return -ENOMEM; - - runtime->dma_bytes = size; - - return 0; -} - -static struct snd_pcm_hardware snd_em28xx_hw_capture = { - .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_MMAP_VALID, - - .formats = SNDRV_PCM_FMTBIT_S16_LE, - - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, - - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ - .period_bytes_min = 64, /* 12544/2, */ - .period_bytes_max = 12544, - .periods_min = 2, - .periods_max = 98, /* 12544, */ -}; - -static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) -{ - struct em28xx *dev = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int ret = 0; - - dprintk("opening device and trying to acquire exclusive lock\n"); - - if (!dev) { - em28xx_err("BUG: em28xx can't find device struct." - " Can't proceed with open\n"); - return -ENODEV; - } - - runtime->hw = snd_em28xx_hw_capture; - if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) { - if (dev->audio_ifnum) - dev->alt = 1; - else - dev->alt = 7; - - dprintk("changing alternate number on interface %d to %d\n", - dev->audio_ifnum, dev->alt); - usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt); - - /* Sets volume, mute, etc */ - dev->mute = 0; - mutex_lock(&dev->lock); - ret = em28xx_audio_analog_set(dev); - if (ret < 0) - goto err; - - dev->adev.users++; - mutex_unlock(&dev->lock); - } - - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - dev->adev.capture_pcm_substream = substream; - - return 0; -err: - mutex_unlock(&dev->lock); - - em28xx_err("Error while configuring em28xx mixer\n"); - return ret; -} - -static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) -{ - struct em28xx *dev = snd_pcm_substream_chip(substream); - - dprintk("closing device\n"); - - dev->mute = 1; - mutex_lock(&dev->lock); - dev->adev.users--; - if (atomic_read(&dev->stream_started) > 0) { - atomic_set(&dev->stream_started, 0); - schedule_work(&dev->wq_trigger); - } - - em28xx_audio_analog_set(dev); - if (substream->runtime->dma_area) { - dprintk("freeing\n"); - vfree(substream->runtime->dma_area); - substream->runtime->dma_area = NULL; - } - mutex_unlock(&dev->lock); - - return 0; -} - -static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - - dprintk("Setting capture parameters\n"); - - ret = snd_pcm_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) - return ret; -#if 0 - /* TODO: set up em28xx audio chip to deliver the correct audio format, - current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono */ - unsigned int channels, rate, format; - - format = params_format(hw_params); - rate = params_rate(hw_params); - channels = params_channels(hw_params); -#endif - - return 0; -} - -static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) -{ - struct em28xx *dev = snd_pcm_substream_chip(substream); - - dprintk("Stop capture, if needed\n"); - - if (atomic_read(&dev->stream_started) > 0) { - atomic_set(&dev->stream_started, 0); - schedule_work(&dev->wq_trigger); - } - - return 0; -} - -static int snd_em28xx_prepare(struct snd_pcm_substream *substream) -{ - struct em28xx *dev = snd_pcm_substream_chip(substream); - - dev->adev.hwptr_done_capture = 0; - dev->adev.capture_transfer_done = 0; - - return 0; -} - -static void audio_trigger(struct work_struct *work) -{ - struct em28xx *dev = container_of(work, struct em28xx, wq_trigger); - - if (atomic_read(&dev->stream_started)) { - dprintk("starting capture"); - em28xx_init_audio_isoc(dev); - } else { - dprintk("stopping capture"); - em28xx_deinit_isoc_audio(dev); - } -} - -static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct em28xx *dev = snd_pcm_substream_chip(substream); - int retval = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ - case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ - case SNDRV_PCM_TRIGGER_START: - atomic_set(&dev->stream_started, 1); - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ - case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ - case SNDRV_PCM_TRIGGER_STOP: - atomic_set(&dev->stream_started, 0); - break; - default: - retval = -EINVAL; - } - schedule_work(&dev->wq_trigger); - return retval; -} - -static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream - *substream) -{ - unsigned long flags; - struct em28xx *dev; - snd_pcm_uframes_t hwptr_done; - - dev = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&dev->adev.slock, flags); - hwptr_done = dev->adev.hwptr_done_capture; - spin_unlock_irqrestore(&dev->adev.slock, flags); - - return hwptr_done; -} - -static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, - unsigned long offset) -{ - void *pageptr = subs->runtime->dma_area + offset; - - return vmalloc_to_page(pageptr); -} - -/* - * AC97 volume control support - */ -static int em28xx_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 2; - info->value.integer.min = 0; - info->value.integer.max = 0x1f; - - return 0; -} - -static int em28xx_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct em28xx *dev = snd_kcontrol_chip(kcontrol); - u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) | - (0x1f - (value->value.integer.value[1] & 0x1f)) << 8; - int rc; - - mutex_lock(&dev->lock); - rc = em28xx_read_ac97(dev, kcontrol->private_value); - if (rc < 0) - goto err; - - val |= rc & 0x8000; /* Preserve the mute flag */ - - rc = em28xx_write_ac97(dev, kcontrol->private_value, val); - if (rc < 0) - goto err; - - dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n", - (val & 0x8000) ? "muted " : "", - 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), - val, (int)kcontrol->private_value); - -err: - mutex_unlock(&dev->lock); - return rc; -} - -static int em28xx_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct em28xx *dev = snd_kcontrol_chip(kcontrol); - int val; - - mutex_lock(&dev->lock); - val = em28xx_read_ac97(dev, kcontrol->private_value); - mutex_unlock(&dev->lock); - if (val < 0) - return val; - - dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n", - (val & 0x8000) ? "muted " : "", - 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), - val, (int)kcontrol->private_value); - - value->value.integer.value[0] = 0x1f - (val & 0x1f); - value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f); - - return 0; -} - -static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct em28xx *dev = snd_kcontrol_chip(kcontrol); - u16 val = value->value.integer.value[0]; - int rc; - - mutex_lock(&dev->lock); - rc = em28xx_read_ac97(dev, kcontrol->private_value); - if (rc < 0) - goto err; - - if (val) - rc &= 0x1f1f; - else - rc |= 0x8000; - - rc = em28xx_write_ac97(dev, kcontrol->private_value, rc); - if (rc < 0) - goto err; - - dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n", - (val & 0x8000) ? "muted " : "", - 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), - val, (int)kcontrol->private_value); - -err: - mutex_unlock(&dev->lock); - return rc; -} - -static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct em28xx *dev = snd_kcontrol_chip(kcontrol); - int val; - - mutex_lock(&dev->lock); - val = em28xx_read_ac97(dev, kcontrol->private_value); - mutex_unlock(&dev->lock); - if (val < 0) - return val; - - if (val & 0x8000) - value->value.integer.value[0] = 0; - else - value->value.integer.value[0] = 1; - - dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n", - (val & 0x8000) ? "muted " : "", - 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f), - val, (int)kcontrol->private_value); - - return 0; -} - -static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0); - -static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev, - char *name, int id) -{ - int err; - char ctl_name[44]; - struct snd_kcontrol *kctl; - struct snd_kcontrol_new tmp; - - memset (&tmp, 0, sizeof(tmp)); - tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - tmp.private_value = id, - tmp.name = ctl_name, - - /* Add Mute Control */ - sprintf(ctl_name, "%s Switch", name); - tmp.get = em28xx_vol_get_mute; - tmp.put = em28xx_vol_put_mute; - tmp.info = snd_ctl_boolean_mono_info; - kctl = snd_ctl_new1(&tmp, dev); - err = snd_ctl_add(card, kctl); - if (err < 0) - return err; - dprintk("Added control %s for ac97 volume control 0x%04x\n", - ctl_name, id); - - memset (&tmp, 0, sizeof(tmp)); - tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - tmp.private_value = id, - tmp.name = ctl_name, - - /* Add Volume Control */ - sprintf(ctl_name, "%s Volume", name); - tmp.get = em28xx_vol_get; - tmp.put = em28xx_vol_put; - tmp.info = em28xx_vol_info; - tmp.tlv.p = em28xx_db_scale, - kctl = snd_ctl_new1(&tmp, dev); - err = snd_ctl_add(card, kctl); - if (err < 0) - return err; - dprintk("Added control %s for ac97 volume control 0x%04x\n", - ctl_name, id); - - return 0; -} - -/* - * register/unregister code and data - */ -static struct snd_pcm_ops snd_em28xx_pcm_capture = { - .open = snd_em28xx_capture_open, - .close = snd_em28xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_em28xx_hw_capture_params, - .hw_free = snd_em28xx_hw_capture_free, - .prepare = snd_em28xx_prepare, - .trigger = snd_em28xx_capture_trigger, - .pointer = snd_em28xx_capture_pointer, - .page = snd_pcm_get_vmalloc_page, -}; - -static int em28xx_audio_init(struct em28xx *dev) -{ - struct em28xx_audio *adev = &dev->adev; - struct snd_pcm *pcm; - struct snd_card *card; - static int devnr; - int err; - - if (!dev->has_alsa_audio || dev->audio_ifnum < 0) { - /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module or - doesn't have analog audio support at all) */ - return 0; - } - - printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n"); - printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " - "Rechberger\n"); - printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n"); - - err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, - &card); - if (err < 0) - return err; - - spin_lock_init(&adev->slock); - err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); - if (err < 0) { - snd_card_free(card); - return err; - } - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); - pcm->info_flags = 0; - pcm->private_data = dev; - strcpy(pcm->name, "Empia 28xx Capture"); - - snd_card_set_dev(card, &dev->udev->dev); - strcpy(card->driver, "Em28xx-Audio"); - strcpy(card->shortname, "Em28xx Audio"); - strcpy(card->longname, "Empia Em28xx Audio"); - - INIT_WORK(&dev->wq_trigger, audio_trigger); - - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - em28xx_cvol_new(card, dev, "Video", AC97_VIDEO); - em28xx_cvol_new(card, dev, "Line In", AC97_LINE); - em28xx_cvol_new(card, dev, "Phone", AC97_PHONE); - em28xx_cvol_new(card, dev, "Microphone", AC97_MIC); - em28xx_cvol_new(card, dev, "CD", AC97_CD); - em28xx_cvol_new(card, dev, "AUX", AC97_AUX); - em28xx_cvol_new(card, dev, "PCM", AC97_PCM); - - em28xx_cvol_new(card, dev, "Master", AC97_MASTER); - em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE); - em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO); - em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER); - em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); - } - - err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } - adev->sndcard = card; - adev->udev = dev->udev; - - return 0; -} - -static int em28xx_audio_fini(struct em28xx *dev) -{ - if (dev == NULL) - return 0; - - if (dev->has_alsa_audio != 1) { - /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module or - doesn't have analog audio support at all) */ - return 0; - } - - if (dev->adev.sndcard) { - snd_card_free(dev->adev.sndcard); - dev->adev.sndcard = NULL; - } - - return 0; -} - -static struct em28xx_ops audio_ops = { - .id = EM28XX_AUDIO, - .name = "Em28xx Audio Extension", - .init = em28xx_audio_init, - .fini = em28xx_audio_fini, -}; - -static int __init em28xx_alsa_register(void) -{ - return em28xx_register_extension(&audio_ops); -} - -static void __exit em28xx_alsa_unregister(void) -{ - em28xx_unregister_extension(&audio_ops); -} - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Markus Rechberger "); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_DESCRIPTION("Em28xx Audio driver"); - -module_init(em28xx_alsa_register); -module_exit(em28xx_alsa_unregister); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c deleted file mode 100644 index ca62b9981380..000000000000 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ /dev/null @@ -1,3417 +0,0 @@ -/* - em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB - video capture devices - - Copyright (C) 2005 Ludovico Cavedon - Markus Rechberger - Mauro Carvalho Chehab - Sascha Sommer - - This program is free software; 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 -#include -#include -#include -#include -#include -#include -#include - -#include "em28xx.h" - -#define DRIVER_NAME "em28xx" - -static int tuner = -1; -module_param(tuner, int, 0444); -MODULE_PARM_DESC(tuner, "tuner type"); - -static unsigned int disable_ir; -module_param(disable_ir, int, 0444); -MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); - -static unsigned int disable_usb_speed_check; -module_param(disable_usb_speed_check, int, 0444); -MODULE_PARM_DESC(disable_usb_speed_check, - "override min bandwidth requirement of 480M bps"); - -static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -module_param_array(card, int, NULL, 0444); -MODULE_PARM_DESC(card, "card type"); - -/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ -static unsigned long em28xx_devused; - -struct em28xx_hash_table { - unsigned long hash; - unsigned int model; - unsigned int tuner; -}; - -static void em28xx_pre_card_setup(struct em28xx *dev); - -/* - * Reset sequences for analog/digital modes - */ - -/* Reset for the most [analog] boards */ -static struct em28xx_reg_seq default_analog[] = { - {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Reset for the most [digital] boards */ -static struct em28xx_reg_seq default_digital[] = { - {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Board Hauppauge WinTV HVR 900 analog */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { - {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, - {0x05, 0xff, 0x10, 10}, - { -1, -1, -1, -1}, -}; - -/* Board Hauppauge WinTV HVR 900 digital */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { - {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x04, 0x0f, 10}, - {EM2880_R04_GPO, 0x0c, 0x0f, 10}, - { -1, -1, -1, -1}, -}; - -/* Board Hauppauge WinTV HVR 900 (R2) digital */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = { - {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x0c, 0x0f, 10}, - { -1, -1, -1, -1}, -}; - -/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ -static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { - {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ - -/* Board - EM2870 Kworld 355u - Analog - No input analog */ - -/* Board - EM2882 Kworld 315U digital */ -static struct em28xx_reg_seq em2882_kworld_315u_digital[] = { - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, - {EM2880_R04_GPO, 0x04, 0xff, 10}, - {EM2880_R04_GPO, 0x0c, 0xff, 10}, - {EM28XX_R08_GPIO, 0x7e, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = { - {EM2880_R04_GPO, 0x08, 0xff, 10}, - {EM2880_R04_GPO, 0x0c, 0xff, 10}, - {EM2880_R04_GPO, 0x08, 0xff, 10}, - {EM2880_R04_GPO, 0x0c, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq kworld_330u_analog[] = { - {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x00, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq kworld_330u_digital[] = { - {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x08, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -/* Evga inDtube - GPIO0 - Enable digital power (s5h1409) - low to enable - GPIO1 - Enable analog power (tvp5150/emp202) - low to enable - GPIO4 - xc3028 reset - GOP3 - s5h1409 reset - */ -static struct em28xx_reg_seq evga_indtube_analog[] = { - {EM28XX_R08_GPIO, 0x79, 0xff, 60}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq evga_indtube_digital[] = { - {EM28XX_R08_GPIO, 0x7a, 0xff, 1}, - {EM2880_R04_GPO, 0x04, 0xff, 10}, - {EM2880_R04_GPO, 0x0c, 0xff, 1}, - { -1, -1, -1, -1}, -}; - -/* - * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map: - * EM_GPIO_0 - currently unknown - * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on) - * EM_GPIO_2 - currently unknown - * EM_GPIO_3 - currently unknown - * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset) - * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset) - * EM_GPIO_6 - currently unknown - * EM_GPIO_7 - currently unknown - */ -static struct em28xx_reg_seq kworld_a340_digital[] = { - {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Pinnacle Hybrid Pro eb1a:2881 */ -static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { - {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { - {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */ - {EM2880_R04_GPO, 0x0c, 0xff, 1}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = { - {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x00, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = { - {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x08, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -/* eb1a:2868 Reddo DVB-C USB TV Box - GPIO4 - CU1216L NIM - Other GPIOs seems to be don't care. */ -static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { - {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, - {EM28XX_R08_GPIO, 0xde, 0xff, 10}, - {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - {EM28XX_R08_GPIO, 0x7f, 0xff, 10}, - {EM28XX_R08_GPIO, 0x6f, 0xff, 10}, - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - {-1, -1, -1, -1}, -}; - -/* Callback for the most boards */ -static struct em28xx_reg_seq default_tuner_gpio[] = { - {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, - {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, - {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Mute/unmute */ -static struct em28xx_reg_seq compro_unmute_tv_gpio[] = { - {EM28XX_R08_GPIO, 5, 7, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq compro_unmute_svid_gpio[] = { - {EM28XX_R08_GPIO, 4, 7, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq compro_mute_gpio[] = { - {EM28XX_R08_GPIO, 6, 7, 10}, - { -1, -1, -1, -1}, -}; - -/* Terratec AV350 */ -static struct em28xx_reg_seq terratec_av350_mute_gpio[] = { - {EM28XX_R08_GPIO, 0xff, 0x7f, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = { - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq silvercrest_reg_seq[] = { - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - {EM28XX_R08_GPIO, 0x01, 0xf7, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq vc211a_enable[] = { - {EM28XX_R08_GPIO, 0xff, 0x07, 10}, - {EM28XX_R08_GPIO, 0xff, 0x0f, 10}, - {EM28XX_R08_GPIO, 0xff, 0x0b, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq dikom_dk300_digital[] = { - {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x08, 0xff, 10}, - { -1, -1, -1, -1}, -}; - - -/* Reset for the most [digital] boards */ -static struct em28xx_reg_seq leadership_digital[] = { - {EM2874_R80_GPIO, 0x70, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq leadership_reset[] = { - {EM2874_R80_GPIO, 0xf0, 0xff, 10}, - {EM2874_R80_GPIO, 0xb0, 0xff, 10}, - {EM2874_R80_GPIO, 0xf0, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -/* 2013:024f PCTV nanoStick T2 290e - * GPIO_6 - demod reset - * GPIO_7 - LED - */ -static struct em28xx_reg_seq pctv_290e[] = { - {EM2874_R80_GPIO, 0x00, 0xff, 80}, - {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */ - {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */ - {-1, -1, -1, -1}, -}; - -#if 0 -static struct em28xx_reg_seq terratec_h5_gpio[] = { - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - {EM2874_R80_GPIO, 0xf6, 0xff, 100}, - {EM2874_R80_GPIO, 0xf2, 0xff, 50}, - {EM2874_R80_GPIO, 0xf6, 0xff, 50}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq terratec_h5_digital[] = { - {EM2874_R80_GPIO, 0xf6, 0xff, 10}, - {EM2874_R80_GPIO, 0xe6, 0xff, 100}, - {EM2874_R80_GPIO, 0xa6, 0xff, 10}, - { -1, -1, -1, -1}, -}; -#endif - -/* 2013:024f PCTV DVB-S2 Stick 460e - * GPIO_0 - POWER_ON - * GPIO_1 - BOOST - * GPIO_2 - VUV_LNB (red LED) - * GPIO_3 - EXT_12V - * GPIO_4 - INT_DEM (DEMOD GPIO_0) - * GPIO_5 - INT_LNB - * GPIO_6 - RESET_DEM - * GPIO_7 - LED (green LED) - */ -static struct em28xx_reg_seq pctv_460e[] = { - {EM2874_R80_GPIO, 0x01, 0xff, 50}, - {0x0d, 0xff, 0xff, 50}, - {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */ - {0x0d, 0x42, 0xff, 50}, - {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */ - { -1, -1, -1, -1}, -}; - -#if 0 -static struct em28xx_reg_seq hauppauge_930c_gpio[] = { - {EM2874_R80_GPIO, 0x6f, 0xff, 10}, - {EM2874_R80_GPIO, 0x4f, 0xff, 10}, /* xc5000 reset */ - {EM2874_R80_GPIO, 0x6f, 0xff, 10}, - {EM2874_R80_GPIO, 0x4f, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -static struct em28xx_reg_seq hauppauge_930c_digital[] = { - {EM2874_R80_GPIO, 0xf6, 0xff, 10}, - {EM2874_R80_GPIO, 0xe6, 0xff, 100}, - {EM2874_R80_GPIO, 0xa6, 0xff, 10}, - { -1, -1, -1, -1}, -}; -#endif - -/* 1b80:e425 MaxMedia UB425-TC - * GPIO_6 - demod reset, 0=active - * GPIO_7 - LED, 0=active - */ -static struct em28xx_reg_seq maxmedia_ub425_tc[] = { - {EM2874_R80_GPIO, 0x83, 0xff, 100}, - {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */ - {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */ - {-1, -1, -1, -1}, -}; - -/* 2304:0242 PCTV QuatroStick (510e) - * GPIO_2: decoder reset, 0=active - * GPIO_4: decoder suspend, 0=active - * GPIO_6: demod reset, 0=active - * GPIO_7: LED, 1=active - */ -static struct em28xx_reg_seq pctv_510e[] = { - {EM2874_R80_GPIO, 0x10, 0xff, 100}, - {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ - {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ - { -1, -1, -1, -1}, -}; - -/* 2013:0251 PCTV QuatroStick nano (520e) - * GPIO_2: decoder reset, 0=active - * GPIO_4: decoder suspend, 0=active - * GPIO_6: demod reset, 0=active - * GPIO_7: LED, 1=active - */ -static struct em28xx_reg_seq pctv_520e[] = { - {EM2874_R80_GPIO, 0x10, 0xff, 100}, - {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ - {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ - {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */ - { -1, -1, -1, -1}, -}; - -/* - * Board definitions - */ -struct em28xx_board em28xx_boards[] = { - [EM2750_BOARD_UNKNOWN] = { - .name = "EM2710/EM2750/EM2751 webcam grabber", - .xclk = EM28XX_XCLK_FREQUENCY_20MHZ, - .tuner_type = TUNER_ABSENT, - .is_webcam = 1, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = silvercrest_reg_seq, - } }, - }, - [EM2800_BOARD_UNKNOWN] = { - .name = "Unknown EM2800 video grabber", - .is_em2800 = 1, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .tuner_type = TUNER_ABSENT, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_UNKNOWN] = { - .name = "Unknown EM2750/28xx video grabber", - .tuner_type = TUNER_ABSENT, - .is_webcam = 1, /* To enable sensor probe */ - }, - [EM2750_BOARD_DLCW_130] = { - /* Beijing Huaqi Information Digital Technology Co., Ltd */ - .name = "Huaqi DLCW-130", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, - .tuner_type = TUNER_ABSENT, - .is_webcam = 1, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, - .amux = EM28XX_AMUX_VIDEO, - } }, - }, - [EM2820_BOARD_KWORLD_PVRTV2800RF] = { - .name = "Kworld PVR TV 2800 RF", - .tuner_type = TUNER_TEMIC_PAL, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_GADMEI_TVR200] = { - .name = "Gadmei TVR200", - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_TERRATEC_CINERGY_250] = { - .name = "Terratec Cinergy 250 USB", - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .has_ir_i2c = 1, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_PINNACLE_USB_2] = { - .name = "Pinnacle PCTV USB 2", - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .has_ir_i2c = 1, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = { - .name = "Hauppauge WinTV USB 2", - .tuner_type = TUNER_PHILIPS_FM1236_MK3, - .tda9887_conf = TDA9887_PRESENT | - TDA9887_PORT1_ACTIVE | - TDA9887_PORT2_ACTIVE, - .decoder = EM28XX_TVP5150, - .has_msp34xx = 1, - .has_ir_i2c = 1, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = MSP_INPUT_DEFAULT, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, - MSP_DSP_IN_SCART, MSP_DSP_IN_SCART), - } }, - }, - [EM2820_BOARD_DLINK_USB_TV] = { - .name = "D-Link DUB-T210 TV Tuner", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_HERCULES_SMART_TV_USB2] = { - .name = "Hercules Smart TV USB 2.0", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = { - .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_GADMEI_UTV310] = { - .name = "Gadmei UTV310", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_TNF_5335MF, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = { - .name = "Leadtek Winfast USB II Deluxe", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .has_ir_i2c = 1, - .tvaudio_addr = 0x58, - .tda9887_conf = TDA9887_PRESENT | - TDA9887_PORT2_ACTIVE | - TDA9887_QSS, - .decoder = EM28XX_SAA711X, - .adecoder = EM28XX_TVAUDIO, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE4, - .amux = EM28XX_AMUX_AUX, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE5, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - .radio = { - .type = EM28XX_RADIO, - .amux = EM28XX_AMUX_AUX, - } - }, - [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { - .name = "Videology 20K14XUSB USB2.0", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, - .is_webcam = 1, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, - .amux = EM28XX_AMUX_VIDEO, - } }, - }, - [EM2820_BOARD_SILVERCREST_WEBCAM] = { - .name = "Silvercrest Webcam 1.3mpix", - .tuner_type = TUNER_ABSENT, - .is_webcam = 1, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = silvercrest_reg_seq, - } }, - }, - [EM2821_BOARD_SUPERCOMP_USB_2] = { - .name = "Supercomp USB 2.0 TV", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_PHILIPS_FM1236_MK3, - .tda9887_conf = TDA9887_PRESENT | - TDA9887_PORT1_ACTIVE | - TDA9887_PORT2_ACTIVE, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2821_BOARD_USBGEAR_VD204] = { - .name = "Usbgear VD204v9", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* Capture only device */ - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2860_BOARD_NETGMBH_CAM] = { - /* Beijing Huaqi Information Digital Technology Co., Ltd */ - .name = "NetGMBH Cam", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, - .is_webcam = 1, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, - .amux = EM28XX_AMUX_VIDEO, - } }, - }, - [EM2860_BOARD_TYPHOON_DVD_MAKER] = { - .name = "Typhoon DVD Maker", - .decoder = EM28XX_SAA711X, - .tuner_type = TUNER_ABSENT, /* Capture only device */ - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2860_BOARD_GADMEI_UTV330] = { - .name = "Gadmei UTV330", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_TNF_5335MF, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2861_BOARD_GADMEI_UTV330PLUS] = { - .name = "Gadmei UTV330+", - .tuner_type = TUNER_TNF_5335MF, - .tda9887_conf = TDA9887_PRESENT, - .ir_codes = RC_MAP_GADMEI_RM008Z, - .decoder = EM28XX_SAA711X, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2860_BOARD_TERRATEC_HYBRID_XS] = { - .name = "Terratec Cinergy A Hybrid XS", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2861_BOARD_KWORLD_PVRTV_300U] = { - .name = "KWorld PVRTV 300U", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { - .name = "Yakumo MovieMixer", - .tuner_type = TUNER_ABSENT, /* Capture only device */ - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = { - .name = "EM2860/TVP5150 Reference Design", - .tuner_type = TUNER_ABSENT, /* Capture only device */ - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2861_BOARD_PLEXTOR_PX_TV100U] = { - .name = "Plextor ConvertX PX-TV100U", - .tuner_type = TUNER_TNF_5335MF, - .xclk = EM28XX_XCLK_I2S_MSB_TIMING | - EM28XX_XCLK_FREQUENCY_12MHZ, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_TVP5150, - .has_msp34xx = 1, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = pinnacle_hybrid_pro_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = pinnacle_hybrid_pro_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = pinnacle_hybrid_pro_analog, - } }, - }, - - /* Those boards with em2870 are DVB Only*/ - - [EM2870_BOARD_TERRATEC_XS] = { - .name = "Terratec Cinergy T XS", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - }, - [EM2870_BOARD_TERRATEC_XS_MT2060] = { - .name = "Terratec Cinergy T XS (MT2060)", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* MT2060 */ - }, - [EM2870_BOARD_KWORLD_350U] = { - .name = "Kworld 350 U DVB-T", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - }, - [EM2870_BOARD_KWORLD_355U] = { - .name = "Kworld 355 U DVB-T", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, - .tuner_gpio = default_tuner_gpio, - .has_dvb = 1, - .dvb_gpio = default_digital, - }, - [EM2870_BOARD_PINNACLE_PCTV_DVB] = { - .name = "Pinnacle PCTV DVB-T", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* MT2060 */ - /* djh - I have serious doubts this is right... */ - .xclk = EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_10MHZ, - }, - [EM2870_BOARD_COMPRO_VIDEOMATE] = { - .name = "Compro, VideoMate U3", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* MT2060 */ - }, - - [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = { - .name = "Terratec Hybrid XS Secam", - .has_msp34xx = 1, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .has_dvb = 1, - .dvb_gpio = terratec_cinergy_USB_XS_FR_digital, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = terratec_cinergy_USB_XS_FR_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = terratec_cinergy_USB_XS_FR_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = terratec_cinergy_USB_XS_FR_analog, - } }, - }, - [EM2884_BOARD_TERRATEC_H5] = { - .name = "Terratec Cinergy H5", - .has_dvb = 1, -#if 0 - .tuner_type = TUNER_PHILIPS_TDA8290, - .tuner_addr = 0x41, - .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */ - .tuner_gpio = terratec_h5_gpio, -#else - .tuner_type = TUNER_ABSENT, -#endif - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_400_KHZ, - }, - [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = { - .name = "Hauppauge WinTV HVR 930C", - .has_dvb = 1, -#if 0 /* FIXME: Add analog support */ - .tuner_type = TUNER_XC5000, - .tuner_addr = 0x41, - .dvb_gpio = hauppauge_930c_digital, - .tuner_gpio = hauppauge_930c_gpio, -#else - .tuner_type = TUNER_ABSENT, -#endif - .ir_codes = RC_MAP_HAUPPAUGE, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_400_KHZ, - }, - [EM2884_BOARD_CINERGY_HTC_STICK] = { - .name = "Terratec Cinergy HTC Stick", - .has_dvb = 1, - .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, - .tuner_type = TUNER_ABSENT, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_400_KHZ, - }, - [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { - .name = "Hauppauge WinTV HVR 900", - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_HAUPPAUGE, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = { - .name = "Hauppauge WinTV HVR 900 (R2)", - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900R2_digital, - .ir_codes = RC_MAP_HAUPPAUGE, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = { - .name = "Hauppauge WinTV HVR 850", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_HAUPPAUGE, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { - .name = "Hauppauge WinTV HVR 950", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_HAUPPAUGE, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { - .name = "Pinnacle PCTV HD Pro Stick", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { - .name = "AMD ATI TV Wonder HD 600", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_ATI_TV_WONDER_HD_600, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2880_BOARD_TERRATEC_HYBRID_XS] = { - .name = "Terratec Hybrid XS", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .has_dvb = 1, - .dvb_gpio = default_digital, - .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - } }, - }, - /* maybe there's a reason behind it why Terratec sells the Hybrid XS - as Prodigy XS with a different PID, let's keep it separated for now - maybe we'll need it lateron */ - [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { - .name = "Terratec Prodigy XS", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2820_BOARD_MSI_VOX_USB_2] = { - .name = "MSI VOX USB 2.0", - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .tda9887_conf = TDA9887_PRESENT | - TDA9887_PORT1_ACTIVE | - TDA9887_PORT2_ACTIVE, - .max_range_640_480 = 1, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE4, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2800_BOARD_TERRATEC_CINERGY_200] = { - .name = "Terratec Cinergy 200 USB", - .is_em2800 = 1, - .has_ir_i2c = 1, - .tuner_type = TUNER_LG_TALN, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2800_BOARD_GRABBEEX_USB2800] = { - .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", - .is_em2800 = 1, - .decoder = EM28XX_SAA711X, - .tuner_type = TUNER_ABSENT, /* capture only board */ - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2800_BOARD_VC211A] = { - .name = "Actionmaster/LinXcel/Digitus VC211A", - .is_em2800 = 1, - .tuner_type = TUNER_ABSENT, /* Capture-only board */ - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = vc211a_enable, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = vc211a_enable, - } }, - }, - [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { - .name = "Leadtek Winfast USB II", - .is_em2800 = 1, - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2800_BOARD_KWORLD_USB2800] = { - .name = "Kworld USB2800", - .is_em2800 = 1, - .tuner_type = TUNER_PHILIPS_FCV1236D, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker " - "/ Kworld DVD Maker 2 / Plextor ConvertX PX-AV100U", - .tuner_type = TUNER_ABSENT, /* capture only board */ - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2800_BOARD_VGEAR_POCKETTV] = { - .name = "V-Gear PocketTV", - .is_em2800 = 1, - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = { - .name = "Pixelview PlayTV Box 4 USB 2.0", - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_YMEC_TVF_5533MF, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - .aout = EM28XX_AOUT_MONO | /* I2S */ - EM28XX_AOUT_MASTER, /* Line out pin */ - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { - .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0", - .has_snapshot_button = 1, - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_YMEC_TVF_5533MF, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - .aout = EM28XX_AOUT_MONO | /* I2S */ - EM28XX_AOUT_MASTER, /* Line out pin */ - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = { - .name = "EM2860/SAA711X Reference Design", - .has_snapshot_button = 1, - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - } }, - }, - - [EM2874_BOARD_LEADERSHIP_ISDBT] = { - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ, - .xclk = EM28XX_XCLK_FREQUENCY_10MHZ, - .name = "EM2874 Leadership ISDBT", - .tuner_type = TUNER_ABSENT, - .tuner_gpio = leadership_reset, - .dvb_gpio = leadership_digital, - .has_dvb = 1, - }, - - [EM2880_BOARD_MSI_DIGIVOX_AD] = { - .name = "MSI DigiVox A/D", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = em2880_msi_digivox_ad_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = em2880_msi_digivox_ad_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = em2880_msi_digivox_ad_analog, - } }, - }, - [EM2880_BOARD_MSI_DIGIVOX_AD_II] = { - .name = "MSI DigiVox A/D II", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = em2880_msi_digivox_ad_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = em2880_msi_digivox_ad_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = em2880_msi_digivox_ad_analog, - } }, - }, - [EM2880_BOARD_KWORLD_DVB_305U] = { - .name = "KWorld DVB-T 305U", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2880_BOARD_KWORLD_DVB_310U] = { - .name = "KWorld DVB-T 310U", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .has_dvb = 1, - .dvb_gpio = default_digital, - .mts_firmware = 1, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - }, { /* S-video has not been tested yet */ - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - } }, - }, - [EM2882_BOARD_KWORLD_ATSC_315U] = { - .name = "KWorld ATSC 315U HDTV TV Box", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_THOMSON_DTT761X, - .tuner_gpio = em2882_kworld_315u_tuner_gpio, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA711X, - .has_dvb = 1, - .dvb_gpio = em2882_kworld_315u_digital, - .ir_codes = RC_MAP_KWORLD_315U, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, - /* Analog mode - still not ready */ - /*.input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, - .gpio = em2882_kworld_315u_analog, - .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = em2882_kworld_315u_analog1, - .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = em2882_kworld_315u_analog1, - .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, - } }, */ - }, - [EM2880_BOARD_EMPIRE_DUAL_TV] = { - .name = "Empire dual TV", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .has_dvb = 1, - .dvb_gpio = default_digital, - .mts_firmware = 1, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - } }, - }, - [EM2881_BOARD_DNT_DA2_HYBRID] = { - .name = "DNT DA2 Hybrid", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, - } }, - }, - [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { - .name = "Pinnacle Hybrid Pro", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .has_dvb = 1, - .dvb_gpio = pinnacle_hybrid_pro_digital, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = pinnacle_hybrid_pro_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = pinnacle_hybrid_pro_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = pinnacle_hybrid_pro_analog, - } }, - }, - [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = { - .name = "Pinnacle Hybrid Pro (330e)", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900R2_digital, - .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2882_BOARD_KWORLD_VS_DVBT] = { - .name = "Kworld VS-DVB-T 323UR", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = kworld_330u_digital, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ - .ir_codes = RC_MAP_KWORLD_315U, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2882_BOARD_TERRATEC_HYBRID_XS] = { - .name = "Terratec Cinnergy Hybrid T USB XS (em2882)", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .mts_firmware = 1, - .decoder = EM28XX_TVP5150, - .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = hauppauge_wintv_hvr_900_analog, - } }, - }, - [EM2882_BOARD_DIKOM_DK300] = { - .name = "Dikom DK300", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = dikom_dk300_digital, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, - } }, - }, - [EM2883_BOARD_KWORLD_HYBRID_330U] = { - .name = "Kworld PlusTV HD Hybrid 330", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = kworld_330u_digital, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_EEPROM_ON_BOARD | - EM28XX_I2C_EEPROM_KEY_VALID, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = kworld_330u_analog, - .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = kworld_330u_analog, - .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = kworld_330u_analog, - } }, - }, - [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { - .name = "Compro VideoMate ForYou/Stereo", - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .tvaudio_addr = 0xb0, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_TVP5150, - .adecoder = EM28XX_TVAUDIO, - .mute_gpio = compro_mute_gpio, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = compro_unmute_tv_gpio, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = compro_unmute_svid_gpio, - } }, - }, - [EM2860_BOARD_KAIOMY_TVNPC_U2] = { - .name = "Kaiomy TVnPC U2", - .vchannels = 3, - .tuner_type = TUNER_XC2028, - .tuner_addr = 0x61, - .mts_firmware = 1, - .decoder = EM28XX_TVP5150, - .tuner_gpio = default_tuner_gpio, - .ir_codes = RC_MAP_KAIOMY, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - } }, - .radio = { - .type = EM28XX_RADIO, - .amux = EM28XX_AMUX_LINE_IN, - } - }, - [EM2860_BOARD_EASYCAP] = { - .name = "Easy Cap Capture DC-60", - .vchannels = 2, - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2820_BOARD_IODATA_GVMVP_SZ] = { - .name = "IO-DATA GV-MVP/SZ", - .tuner_type = TUNER_PHILIPS_FM1236_MK3, - .tuner_gpio = default_tuner_gpio, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_TVP5150, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - }, { /* Composite has not been tested yet */ - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_VIDEO, - }, { /* S-video has not been tested yet */ - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_VIDEO, - } }, - }, - [EM2860_BOARD_TERRATEC_GRABBY] = { - .name = "Terratec Grabby", - .vchannels = 2, - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA711X, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - [EM2860_BOARD_TERRATEC_AV350] = { - .name = "Terratec AV350", - .vchannels = 2, - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_TVP5150, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, - .mute_gpio = terratec_av350_mute_gpio, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AUDIO_SRC_LINE, - .gpio = terratec_av350_unmute_gpio, - - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AUDIO_SRC_LINE, - .gpio = terratec_av350_unmute_gpio, - } }, - }, - - [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = { - .name = "Elgato Video Capture", - .decoder = EM28XX_SAA711X, - .tuner_type = TUNER_ABSENT, /* Capture only device */ - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - - [EM2882_BOARD_EVGA_INDTUBE] = { - .name = "Evga inDtube", - .tuner_type = TUNER_XC2028, - .tuner_gpio = default_tuner_gpio, - .decoder = EM28XX_TVP5150, - .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ - .mts_firmware = 1, - .has_dvb = 1, - .dvb_gpio = evga_indtube_digital, - .ir_codes = RC_MAP_EVGA_INDTUBE, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = TVP5150_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO, - .gpio = evga_indtube_analog, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = evga_indtube_analog, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_LINE_IN, - .gpio = evga_indtube_analog, - } }, - }, - /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 + - Infineon TUA6034) */ - [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = { - .name = "Reddo DVB-C USB TV Box", - .tuner_type = TUNER_ABSENT, - .tuner_gpio = reddo_dvb_c_usb_box, - .has_dvb = 1, - }, - /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold - * initially as the KWorld PlusTV 340U, then as the UB435-Q. - * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */ - [EM2870_BOARD_KWORLD_A340] = { - .name = "KWorld PlusTV 340U or UB435-Q (ATSC)", - .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */ - .has_dvb = 1, - .dvb_gpio = kworld_a340_digital, - .tuner_gpio = default_tuner_gpio, - }, - /* 2013:024f PCTV nanoStick T2 290e. - * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */ - [EM28174_BOARD_PCTV_290E] = { - .name = "PCTV nanoStick T2 290e", - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, - .tuner_type = TUNER_ABSENT, - .tuner_gpio = pctv_290e, - .has_dvb = 1, - .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - }, - /* 2013:024f PCTV DVB-S2 Stick 460e - * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */ - [EM28174_BOARD_PCTV_460E] = { - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, - .name = "PCTV DVB-S2 Stick (460e)", - .tuner_type = TUNER_ABSENT, - .tuner_gpio = pctv_460e, - .has_dvb = 1, - .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - }, - /* eb1a:5006 Honestech VIDBOX NW03 - * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */ - [EM2860_BOARD_HT_VIDBOX_NW03] = { - .name = "Honestech Vidbox NW03", - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs confirming */ - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, - /* 1b80:e425 MaxMedia UB425-TC - * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */ - [EM2874_BOARD_MAXMEDIA_UB425_TC] = { - .name = "MaxMedia UB425-TC", - .tuner_type = TUNER_ABSENT, - .tuner_gpio = maxmedia_ub425_tc, - .has_dvb = 1, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_400_KHZ, - }, - /* 2304:0242 PCTV QuatroStick (510e) - * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ - [EM2884_BOARD_PCTV_510E] = { - .name = "PCTV QuatroStick (510e)", - .tuner_type = TUNER_ABSENT, - .tuner_gpio = pctv_510e, - .has_dvb = 1, - .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_400_KHZ, - }, - /* 2013:0251 PCTV QuatroStick nano (520e) - * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ - [EM2884_BOARD_PCTV_520E] = { - .name = "PCTV QuatroStick nano (520e)", - .tuner_type = TUNER_ABSENT, - .tuner_gpio = pctv_520e, - .has_dvb = 1, - .ir_codes = RC_MAP_PINNACLE_PCTV_HD, - .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_400_KHZ, - }, -}; -const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); - -/* table of devices that work with this driver */ -struct usb_device_id em28xx_id_table[] = { - { USB_DEVICE(0xeb1a, 0x2750), - .driver_info = EM2750_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2751), - .driver_info = EM2750_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2800), - .driver_info = EM2800_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2710), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2820), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2821), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2860), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2861), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2862), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2863), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2870), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2881), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2883), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2868), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2875), - .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0xe300), - .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, - { USB_DEVICE(0xeb1a, 0xe303), - .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 }, - { USB_DEVICE(0xeb1a, 0xe305), - .driver_info = EM2880_BOARD_KWORLD_DVB_305U }, - { USB_DEVICE(0xeb1a, 0xe310), - .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD }, - { USB_DEVICE(0xeb1a, 0xa313), - .driver_info = EM2882_BOARD_KWORLD_ATSC_315U }, - { USB_DEVICE(0xeb1a, 0xa316), - .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U }, - { USB_DEVICE(0xeb1a, 0xe320), - .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II }, - { USB_DEVICE(0xeb1a, 0xe323), - .driver_info = EM2882_BOARD_KWORLD_VS_DVBT }, - { USB_DEVICE(0xeb1a, 0xe350), - .driver_info = EM2870_BOARD_KWORLD_350U }, - { USB_DEVICE(0xeb1a, 0xe355), - .driver_info = EM2870_BOARD_KWORLD_355U }, - { USB_DEVICE(0xeb1a, 0x2801), - .driver_info = EM2800_BOARD_GRABBEEX_USB2800 }, - { USB_DEVICE(0xeb1a, 0xe357), - .driver_info = EM2870_BOARD_KWORLD_355U }, - { USB_DEVICE(0xeb1a, 0xe359), - .driver_info = EM2870_BOARD_KWORLD_355U }, - { USB_DEVICE(0x1b80, 0xe302), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ - { USB_DEVICE(0x1b80, 0xe304), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */ - { USB_DEVICE(0x0ccd, 0x0036), - .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, - { USB_DEVICE(0x0ccd, 0x004c), - .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR }, - { USB_DEVICE(0x0ccd, 0x004f), - .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS }, - { USB_DEVICE(0x0ccd, 0x005e), - .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS }, - { USB_DEVICE(0x0ccd, 0x0042), - .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS }, - { USB_DEVICE(0x0ccd, 0x0043), - .driver_info = EM2870_BOARD_TERRATEC_XS }, - { USB_DEVICE(0x0ccd, 0x008e), /* Cinergy HTC USB XS Rev. 1 */ - .driver_info = EM2884_BOARD_TERRATEC_H5 }, - { USB_DEVICE(0x0ccd, 0x00ac), /* Cinergy HTC USB XS Rev. 2 */ - .driver_info = EM2884_BOARD_TERRATEC_H5 }, - { USB_DEVICE(0x0ccd, 0x10a2), /* H5 Rev. 1 */ - .driver_info = EM2884_BOARD_TERRATEC_H5 }, - { USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */ - .driver_info = EM2884_BOARD_TERRATEC_H5 }, - { USB_DEVICE(0x0ccd, 0x0084), - .driver_info = EM2860_BOARD_TERRATEC_AV350 }, - { USB_DEVICE(0x0ccd, 0x0096), - .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, - { USB_DEVICE(0x0ccd, 0x10AF), - .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, - { USB_DEVICE(0x0ccd, 0x00b2), - .driver_info = EM2884_BOARD_CINERGY_HTC_STICK }, - { USB_DEVICE(0x0fd9, 0x0033), - .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE}, - { USB_DEVICE(0x185b, 0x2870), - .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE }, - { USB_DEVICE(0x185b, 0x2041), - .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU }, - { USB_DEVICE(0x2040, 0x4200), - .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 }, - { USB_DEVICE(0x2040, 0x4201), - .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 }, - { USB_DEVICE(0x2040, 0x6500), - .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 }, - { USB_DEVICE(0x2040, 0x6502), - .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 }, - { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */ - .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, - { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */ - .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, - { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */ - .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, - { USB_DEVICE(0x2040, 0x651f), - .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, - { USB_DEVICE(0x0438, 0xb002), - .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, - { USB_DEVICE(0x2001, 0xf112), - .driver_info = EM2820_BOARD_DLINK_USB_TV }, - { USB_DEVICE(0x2304, 0x0207), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, - { USB_DEVICE(0x2304, 0x0208), - .driver_info = EM2820_BOARD_PINNACLE_USB_2 }, - { USB_DEVICE(0x2304, 0x021a), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, - { USB_DEVICE(0x2304, 0x0226), - .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E }, - { USB_DEVICE(0x2304, 0x0227), - .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO }, - { USB_DEVICE(0x0413, 0x6023), - .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII }, - { USB_DEVICE(0x093b, 0xa003), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, - { USB_DEVICE(0x093b, 0xa005), - .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, - { USB_DEVICE(0x04bb, 0x0515), - .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, - { USB_DEVICE(0xeb1a, 0x50a6), - .driver_info = EM2860_BOARD_GADMEI_UTV330 }, - { USB_DEVICE(0x1b80, 0xa340), - .driver_info = EM2870_BOARD_KWORLD_A340 }, - { USB_DEVICE(0x2013, 0x024f), - .driver_info = EM28174_BOARD_PCTV_290E }, - { USB_DEVICE(0x2013, 0x024c), - .driver_info = EM28174_BOARD_PCTV_460E }, - { USB_DEVICE(0x2040, 0x1605), - .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C }, - { USB_DEVICE(0xeb1a, 0x5006), - .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 }, - { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */ - .driver_info = EM2860_BOARD_EASYCAP }, - { USB_DEVICE(0x1b80, 0xe425), - .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC }, - { USB_DEVICE(0x2304, 0x0242), - .driver_info = EM2884_BOARD_PCTV_510E }, - { USB_DEVICE(0x2013, 0x0251), - .driver_info = EM2884_BOARD_PCTV_520E }, - { }, -}; -MODULE_DEVICE_TABLE(usb, em28xx_id_table); - -/* - * EEPROM hash table for devices with generic USB IDs - */ -static struct em28xx_hash_table em28xx_eeprom_hash[] = { - /* P/N: SA 60002070465 Tuner: TVF7533-MF */ - {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, - {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, - {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, - {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, - {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, - {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, - {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, - {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028}, -}; - -/* I2C devicelist hash table for devices with generic USB IDs */ -static struct em28xx_hash_table em28xx_i2c_hash[] = { - {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, - {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, - {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, - {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT}, - {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, - {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, - {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT}, -}; - -/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ -static unsigned short saa711x_addrs[] = { - 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ - 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ - I2C_CLIENT_END }; - -static unsigned short tvp5150_addrs[] = { - 0xb8 >> 1, - 0xba >> 1, - I2C_CLIENT_END -}; - -static unsigned short msp3400_addrs[] = { - 0x80 >> 1, - 0x88 >> 1, - I2C_CLIENT_END -}; - -int em28xx_tuner_callback(void *ptr, int component, int command, int arg) -{ - int rc = 0; - struct em28xx *dev = ptr; - - if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000) - return 0; - - if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET) - return 0; - - rc = em28xx_gpio_set(dev, dev->board.tuner_gpio); - - return rc; -} -EXPORT_SYMBOL_GPL(em28xx_tuner_callback); - -static inline void em28xx_set_model(struct em28xx *dev) -{ - memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board)); - - /* Those are the default values for the majority of boards - Use those values if not specified otherwise at boards entry - */ - if (!dev->board.xclk) - dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ; - - if (!dev->board.i2c_speed) - dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ; -} - - -/* FIXME: Should be replaced by a proper mt9m111 driver */ -static int em28xx_initialize_mt9m111(struct em28xx *dev) -{ - int i; - unsigned char regs[][3] = { - { 0x0d, 0x00, 0x01, }, /* reset and use defaults */ - { 0x0d, 0x00, 0x00, }, - { 0x0a, 0x00, 0x21, }, - { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */ - }; - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, ®s[i][0], 3); - - return 0; -} - - -/* FIXME: Should be replaced by a proper mt9m001 driver */ -static int em28xx_initialize_mt9m001(struct em28xx *dev) -{ - int i; - unsigned char regs[][3] = { - { 0x0d, 0x00, 0x01, }, - { 0x0d, 0x00, 0x00, }, - { 0x04, 0x05, 0x00, }, /* hres = 1280 */ - { 0x03, 0x04, 0x00, }, /* vres = 1024 */ - { 0x20, 0x11, 0x00, }, - { 0x06, 0x00, 0x10, }, - { 0x2b, 0x00, 0x24, }, - { 0x2e, 0x00, 0x24, }, - { 0x35, 0x00, 0x24, }, - { 0x2d, 0x00, 0x20, }, - { 0x2c, 0x00, 0x20, }, - { 0x09, 0x0a, 0xd4, }, - { 0x35, 0x00, 0x57, }, - }; - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, ®s[i][0], 3); - - return 0; -} - -/* HINT method: webcam I2C chips - * - * This method works for webcams with Micron sensors - */ -static int em28xx_hint_sensor(struct em28xx *dev) -{ - int rc; - char *sensor_name; - unsigned char cmd; - __be16 version_be; - u16 version; - - /* Micron sensor detection */ - dev->i2c_client.addr = 0xba >> 1; - cmd = 0; - i2c_master_send(&dev->i2c_client, &cmd, 1); - rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2); - if (rc != 2) - return -EINVAL; - - version = be16_to_cpu(version_be); - switch (version) { - case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */ - case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */ - dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; - em28xx_set_model(dev); - - sensor_name = "mt9v011"; - dev->em28xx_sensor = EM28XX_MT9V011; - dev->sensor_xres = 640; - dev->sensor_yres = 480; - /* - * FIXME: mt9v011 uses I2S speed as xtal clk - at least with - * the Silvercrest cam I have here for testing - for higher - * resolutions, a high clock cause horizontal artifacts, so we - * need to use a lower xclk frequency. - * Yet, it would be possible to adjust xclk depending on the - * desired resolution, since this affects directly the - * frame rate. - */ - dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; - dev->sensor_xtal = 4300000; - - /* probably means GRGB 16 bit bayer */ - dev->vinmode = 0x0d; - dev->vinctl = 0x00; - - break; - - case 0x143a: /* MT9M111 as found in the ECS G200 */ - dev->model = EM2750_BOARD_UNKNOWN; - em28xx_set_model(dev); - - sensor_name = "mt9m111"; - dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ; - dev->em28xx_sensor = EM28XX_MT9M111; - em28xx_initialize_mt9m111(dev); - dev->sensor_xres = 640; - dev->sensor_yres = 512; - - dev->vinmode = 0x0a; - dev->vinctl = 0x00; - - break; - - case 0x8431: - dev->model = EM2750_BOARD_UNKNOWN; - em28xx_set_model(dev); - - sensor_name = "mt9m001"; - dev->em28xx_sensor = EM28XX_MT9M001; - em28xx_initialize_mt9m001(dev); - dev->sensor_xres = 1280; - dev->sensor_yres = 1024; - - /* probably means BGGR 16 bit bayer */ - dev->vinmode = 0x0c; - dev->vinctl = 0x00; - - break; - default: - printk("Unknown Micron Sensor 0x%04x\n", version); - return -EINVAL; - } - - /* Setup webcam defaults */ - em28xx_pre_card_setup(dev); - - em28xx_errdev("Sensor is %s, using model %s entry.\n", - sensor_name, em28xx_boards[dev->model].name); - - return 0; -} - -/* Since em28xx_pre_card_setup() requires a proper dev->model, - * this won't work for boards with generic PCI IDs - */ -static void em28xx_pre_card_setup(struct em28xx *dev) -{ - /* Set the initial XCLK and I2C clock values based on the board - definition */ - em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); - if (!dev->board.is_em2800) - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); - msleep(50); - - /* request some modules */ - switch (dev->model) { - case EM2861_BOARD_PLEXTOR_PX_TV100U: - /* Sets the msp34xx I2S speed */ - dev->i2s_speed = 2048000; - break; - case EM2861_BOARD_KWORLD_PVRTV_300U: - case EM2880_BOARD_KWORLD_DVB_305U: - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d); - msleep(10); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d); - msleep(10); - break; - case EM2870_BOARD_COMPRO_VIDEOMATE: - /* TODO: someone can do some cleanup here... - not everything's needed */ - em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); - msleep(10); - em28xx_write_reg(dev, EM2880_R04_GPO, 0x01); - msleep(10); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); - mdelay(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); - mdelay(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc); - mdelay(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); - mdelay(70); - break; - case EM2870_BOARD_TERRATEC_XS_MT2060: - /* this device needs some gpio writes to get the DVB-T - demod work */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); - mdelay(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); - mdelay(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); - mdelay(70); - break; - case EM2870_BOARD_PINNACLE_PCTV_DVB: - /* this device needs some gpio writes to get the - DVB-T demod work */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); - mdelay(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); - mdelay(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); - mdelay(70); - break; - case EM2820_BOARD_GADMEI_UTV310: - case EM2820_BOARD_MSI_VOX_USB_2: - /* enables audio for that devices */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); - break; - - case EM2882_BOARD_KWORLD_ATSC_315U: - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); - msleep(10); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); - msleep(10); - em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); - msleep(10); - em28xx_write_reg(dev, EM2880_R04_GPO, 0x08); - msleep(10); - break; - - case EM2860_BOARD_KAIOMY_TVNPC_U2: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - em28xx_write_regs(dev, 0x0d, "\x42", 1); - em28xx_write_regs(dev, 0x08, "\xfd", 1); - msleep(10); - em28xx_write_regs(dev, 0x08, "\xff", 1); - msleep(10); - em28xx_write_regs(dev, 0x08, "\x7f", 1); - msleep(10); - em28xx_write_regs(dev, 0x08, "\x6b", 1); - - break; - case EM2860_BOARD_EASYCAP: - em28xx_write_regs(dev, 0x08, "\xf8", 1); - break; - - case EM2820_BOARD_IODATA_GVMVP_SZ: - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); - msleep(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); - msleep(10); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); - msleep(70); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); - msleep(70); - break; - } - - em28xx_gpio_set(dev, dev->board.tuner_gpio); - em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - - /* Unlock device */ - em28xx_set_mode(dev, EM28XX_SUSPEND); -} - -static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) -{ - memset(ctl, 0, sizeof(*ctl)); - - ctl->fname = XC2028_DEFAULT_FIRMWARE; - ctl->max_len = 64; - ctl->mts = em28xx_boards[dev->model].mts_firmware; - - switch (dev->model) { - case EM2880_BOARD_EMPIRE_DUAL_TV: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2882_BOARD_TERRATEC_HYBRID_XS: - ctl->demod = XC3028_FE_ZARLINK456; - break; - case EM2880_BOARD_TERRATEC_HYBRID_XS: - case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: - case EM2881_BOARD_PINNACLE_HYBRID_PRO: - ctl->demod = XC3028_FE_ZARLINK456; - break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: - ctl->demod = XC3028_FE_DEFAULT; - break; - case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - ctl->demod = XC3028_FE_DEFAULT; - ctl->fname = XC3028L_DEFAULT_FIRMWARE; - break; - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: - case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: - /* FIXME: Better to specify the needed IF */ - ctl->demod = XC3028_FE_DEFAULT; - break; - case EM2883_BOARD_KWORLD_HYBRID_330U: - case EM2882_BOARD_DIKOM_DK300: - case EM2882_BOARD_KWORLD_VS_DVBT: - ctl->demod = XC3028_FE_CHINA; - ctl->fname = XC2028_DEFAULT_FIRMWARE; - break; - case EM2882_BOARD_EVGA_INDTUBE: - ctl->demod = XC3028_FE_CHINA; - ctl->fname = XC3028L_DEFAULT_FIRMWARE; - break; - default: - ctl->demod = XC3028_FE_OREN538; - } -} - -static void em28xx_tuner_setup(struct em28xx *dev) -{ - struct tuner_setup tun_setup; - struct v4l2_frequency f; - - if (dev->tuner_type == TUNER_ABSENT) - return; - - memset(&tun_setup, 0, sizeof(tun_setup)); - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.tuner_callback = em28xx_tuner_callback; - - if (dev->board.radio.type) { - tun_setup.type = dev->board.radio.type; - tun_setup.addr = dev->board.radio_addr; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); - } - - if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); - } - - if (dev->tda9887_conf) { - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg); - } - - if (dev->tuner_type == TUNER_XC2028) { - struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; - - memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); - memset(&ctl, 0, sizeof(ctl)); - - em28xx_setup_xc3028(dev, &ctl); - - xc2028_cfg.tuner = TUNER_XC2028; - xc2028_cfg.priv = &ctl; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg); - } - - /* configure tuner */ - f.tuner = 0; - f.type = V4L2_TUNER_ANALOG_TV; - f.frequency = 9076; /* just a magic number */ - dev->ctl_freq = f.frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); -} - -static int em28xx_hint_board(struct em28xx *dev) -{ - int i; - - /* HINT method: EEPROM - * - * This method works only for boards with eeprom. - * Uses a hash of all eeprom bytes. The hash should be - * unique for a vendor/tuner pair. - * There are a high chance that tuners for different - * video standards produce different hashes. - */ - for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) { - if (dev->hash == em28xx_eeprom_hash[i].hash) { - dev->model = em28xx_eeprom_hash[i].model; - dev->tuner_type = em28xx_eeprom_hash[i].tuner; - - em28xx_errdev("Your board has no unique USB ID.\n"); - em28xx_errdev("A hint were successfully done, " - "based on eeprom hash.\n"); - em28xx_errdev("This method is not 100%% failproof.\n"); - em28xx_errdev("If the board were missdetected, " - "please email this log to:\n"); - em28xx_errdev("\tV4L Mailing List " - " \n"); - em28xx_errdev("Board detected as %s\n", - em28xx_boards[dev->model].name); - - return 0; - } - } - - /* HINT method: I2C attached devices - * - * This method works for all boards. - * Uses a hash of i2c scanned devices. - * Devices with the same i2c attached chips will - * be considered equal. - * This method is less precise than the eeprom one. - */ - - /* user did not request i2c scanning => do it now */ - if (!dev->i2c_hash) - em28xx_do_i2c_scan(dev); - - for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) { - if (dev->i2c_hash == em28xx_i2c_hash[i].hash) { - dev->model = em28xx_i2c_hash[i].model; - dev->tuner_type = em28xx_i2c_hash[i].tuner; - em28xx_errdev("Your board has no unique USB ID.\n"); - em28xx_errdev("A hint were successfully done, " - "based on i2c devicelist hash.\n"); - em28xx_errdev("This method is not 100%% failproof.\n"); - em28xx_errdev("If the board were missdetected, " - "please email this log to:\n"); - em28xx_errdev("\tV4L Mailing List " - " \n"); - em28xx_errdev("Board detected as %s\n", - em28xx_boards[dev->model].name); - - return 0; - } - } - - em28xx_errdev("Your board has no unique USB ID and thus need a " - "hint to be detected.\n"); - em28xx_errdev("You may try to use card= insmod option to " - "workaround that.\n"); - em28xx_errdev("Please send an email with this log to:\n"); - em28xx_errdev("\tV4L Mailing List \n"); - em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash); - em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash); - - em28xx_errdev("Here is a list of valid choices for the card=" - " insmod option:\n"); - for (i = 0; i < em28xx_bcount; i++) { - em28xx_errdev(" card=%d -> %s\n", - i, em28xx_boards[i].name); - } - return -1; -} - -static void em28xx_card_setup(struct em28xx *dev) -{ - /* - * If the device can be a webcam, seek for a sensor. - * If sensor is not found, then it isn't a webcam. - */ - if (dev->board.is_webcam) { - if (em28xx_hint_sensor(dev) < 0) - dev->board.is_webcam = 0; - else - dev->progressive = 1; - } - - if (!dev->board.is_webcam) { - switch (dev->model) { - case EM2820_BOARD_UNKNOWN: - case EM2800_BOARD_UNKNOWN: - /* - * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD. - * - * This occurs because they share identical USB vendor and - * product IDs. - * - * What we do here is look up the EEPROM hash of the K-WORLD - * and if it is found then we decide that we do not have - * a DIGIVOX and reset the device to the K-WORLD instead. - * - * This solution is only valid if they do not share eeprom - * hash identities which has not been determined as yet. - */ - if (em28xx_hint_board(dev) < 0) - em28xx_errdev("Board not discovered\n"); - else { - em28xx_set_model(dev); - em28xx_pre_card_setup(dev); - } - break; - default: - em28xx_set_model(dev); - } - } - - em28xx_info("Identified as %s (card=%d)\n", - dev->board.name, dev->model); - - dev->tuner_type = em28xx_boards[dev->model].tuner_type; - if (em28xx_boards[dev->model].tuner_addr) - dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; - - if (em28xx_boards[dev->model].tda9887_conf) - dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; - - /* request some modules */ - switch (dev->model) { - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: - { - struct tveeprom tv; -#if defined(CONFIG_MODULES) && defined(MODULE) - request_module("tveeprom"); -#endif - /* Call first TVeeprom */ - - dev->i2c_client.addr = 0xa0 >> 1; - tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata); - - dev->tuner_type = tv.tuner_type; - - if (tv.audio_processor == V4L2_IDENT_MSPX4XX) { - dev->i2s_speed = 2048000; - dev->board.has_msp34xx = 1; - } - break; - } - case EM2882_BOARD_KWORLD_ATSC_315U: - em28xx_write_reg(dev, 0x0d, 0x42); - msleep(10); - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); - msleep(10); - break; - case EM2820_BOARD_KWORLD_PVRTV2800RF: - /* GPIO enables sound on KWORLD PVR TV 2800RF */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9); - break; - case EM2820_BOARD_UNKNOWN: - case EM2800_BOARD_UNKNOWN: - /* - * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD. - * - * This occurs because they share identical USB vendor and - * product IDs. - * - * What we do here is look up the EEPROM hash of the K-WORLD - * and if it is found then we decide that we do not have - * a DIGIVOX and reset the device to the K-WORLD instead. - * - * This solution is only valid if they do not share eeprom - * hash identities which has not been determined as yet. - */ - case EM2880_BOARD_MSI_DIGIVOX_AD: - if (!em28xx_hint_board(dev)) - em28xx_set_model(dev); - - /* In cases where we had to use a board hint, the call to - em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, - so make the call now so the analog GPIOs are set properly - before probing the i2c bus. */ - em28xx_gpio_set(dev, dev->board.tuner_gpio); - em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - break; - -/* - * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR. - * - * This occurs because they share identical USB vendor and - * product IDs. - * - * What we do here is look up the EEPROM hash of the Dikom - * and if it is found then we decide that we do not have - * a Kworld and reset the device to the Dikom instead. - * - * This solution is only valid if they do not share eeprom - * hash identities which has not been determined as yet. - */ - case EM2882_BOARD_KWORLD_VS_DVBT: - if (!em28xx_hint_board(dev)) - em28xx_set_model(dev); - - /* In cases where we had to use a board hint, the call to - em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, - so make the call now so the analog GPIOs are set properly - before probing the i2c bus. */ - em28xx_gpio_set(dev, dev->board.tuner_gpio); - em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - break; - } - - if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { - em28xx_errdev("\n\n"); - em28xx_errdev("The support for this board weren't " - "valid yet.\n"); - em28xx_errdev("Please send a report of having this working\n"); - em28xx_errdev("not to V4L mailing list (and/or to other " - "addresses)\n\n"); - } - - /* Allow override tuner type by a module parameter */ - if (tuner >= 0) - dev->tuner_type = tuner; - - /* request some modules */ - if (dev->board.has_msp34xx) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "msp3400", 0, msp3400_addrs); - - if (dev->board.decoder == EM28XX_SAA711X) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "saa7115_auto", 0, saa711x_addrs); - - if (dev->board.decoder == EM28XX_TVP5150) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvp5150", 0, tvp5150_addrs); - - if (dev->em28xx_sensor == EM28XX_MT9V011) { - struct mt9v011_platform_data pdata; - struct i2c_board_info mt9v011_info = { - .type = "mt9v011", - .addr = 0xba >> 1, - .platform_data = &pdata, - }; - - pdata.xtal = dev->sensor_xtal; - v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap, - &mt9v011_info, NULL); - } - - - if (dev->board.adecoder == EM28XX_TVAUDIO) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvaudio", dev->board.tvaudio_addr, NULL); - - if (dev->board.tuner_type != TUNER_ABSENT) { - int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); - - if (dev->board.radio.type) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", dev->board.radio_addr, NULL); - - if (has_demod) - v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "tuner", - 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); - if (dev->tuner_addr == 0) { - enum v4l2_i2c_tuner_type type = - has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; - struct v4l2_subdev *sd; - - sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "tuner", - 0, v4l2_i2c_tuner_addrs(type)); - - if (sd) - dev->tuner_addr = v4l2_i2c_subdev_addr(sd); - } else { - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", dev->tuner_addr, NULL); - } - } - - em28xx_tuner_setup(dev); -} - - -#if defined(CONFIG_MODULES) && defined(MODULE) -static void request_module_async(struct work_struct *work) -{ - struct em28xx *dev = container_of(work, - struct em28xx, request_module_wk); - - if (dev->has_audio_class) - request_module("snd-usb-audio"); - else if (dev->has_alsa_audio) - request_module("em28xx-alsa"); - - if (dev->board.has_dvb) - request_module("em28xx-dvb"); - if (dev->board.ir_codes && !disable_ir) - request_module("em28xx-rc"); -} - -static void request_modules(struct em28xx *dev) -{ - INIT_WORK(&dev->request_module_wk, request_module_async); - schedule_work(&dev->request_module_wk); -} - -static void flush_request_modules(struct em28xx *dev) -{ - flush_work_sync(&dev->request_module_wk); -} -#else -#define request_modules(dev) -#define flush_request_modules(dev) -#endif /* CONFIG_MODULES */ - -/* - * em28xx_release_resources() - * unregisters the v4l2,i2c and usb devices - * called when the device gets disconnected or at module unload -*/ -void em28xx_release_resources(struct em28xx *dev) -{ - /*FIXME: I2C IR should be disconnected */ - - em28xx_release_analog_resources(dev); - - em28xx_i2c_unregister(dev); - - v4l2_device_unregister(&dev->v4l2_dev); - - usb_put_dev(dev->udev); - - /* Mark device as unused */ - clear_bit(dev->devno, &em28xx_devused); -}; - -/* - * em28xx_init_dev() - * allocates and inits the device structs, registers i2c bus and v4l device - */ -static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, - struct usb_interface *interface, - int minor) -{ - int retval; - - dev->udev = udev; - mutex_init(&dev->ctrl_urb_lock); - spin_lock_init(&dev->slock); - - dev->em28xx_write_regs = em28xx_write_regs; - dev->em28xx_read_reg = em28xx_read_reg; - dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; - dev->em28xx_write_regs_req = em28xx_write_regs_req; - dev->em28xx_read_reg_req = em28xx_read_reg_req; - dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; - - em28xx_set_model(dev); - - /* Set the default GPO/GPIO for legacy devices */ - dev->reg_gpo_num = EM2880_R04_GPO; - dev->reg_gpio_num = EM28XX_R08_GPIO; - - dev->wait_after_write = 5; - - /* Based on the Chip ID, set the device configuration */ - retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); - if (retval > 0) { - dev->chip_id = retval; - - switch (dev->chip_id) { - case CHIP_ID_EM2800: - em28xx_info("chip ID is em2800\n"); - break; - case CHIP_ID_EM2710: - em28xx_info("chip ID is em2710\n"); - break; - case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); - break; - case CHIP_ID_EM2820: - em28xx_info("chip ID is em2820 (or em2710)\n"); - break; - case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); - break; - case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); - break; - case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); - dev->reg_gpio_num = EM2874_R80_GPIO; - dev->wait_after_write = 0; - break; - case CHIP_ID_EM28174: - em28xx_info("chip ID is em28174\n"); - dev->reg_gpio_num = EM2874_R80_GPIO; - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2884: - em28xx_info("chip ID is em2884\n"); - dev->reg_gpio_num = EM2874_R80_GPIO; - dev->wait_after_write = 0; - break; - default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); - } - } - - if (dev->is_audio_only) { - retval = em28xx_audio_setup(dev); - if (retval) - return -ENODEV; - em28xx_init_extension(dev); - - return 0; - } - - /* Prepopulate cached GPO register content */ - retval = em28xx_read_reg(dev, dev->reg_gpo_num); - if (retval >= 0) - dev->reg_gpo = retval; - - em28xx_pre_card_setup(dev); - - if (!dev->board.is_em2800) { - /* Resets I2C speed */ - retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); - if (retval < 0) { - em28xx_errdev("%s: em28xx_write_reg failed!" - " retval [%d]\n", - __func__, retval); - return retval; - } - } - - retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); - if (retval < 0) { - em28xx_errdev("Call to v4l2_device_register() failed!\n"); - return retval; - } - - /* register i2c bus */ - retval = em28xx_i2c_register(dev); - if (retval < 0) { - em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n", - __func__, retval); - goto unregister_dev; - } - - /* - * Default format, used for tvp5150 or saa711x output formats - */ - dev->vinmode = 0x10; - dev->vinctl = EM28XX_VINCTRL_INTERLACED | - EM28XX_VINCTRL_CCIR656_ENABLE; - - /* Do board specific init and eeprom reading */ - em28xx_card_setup(dev); - - /* Configure audio */ - retval = em28xx_audio_setup(dev); - if (retval < 0) { - em28xx_errdev("%s: Error while setting audio - error [%d]!\n", - __func__, retval); - goto fail; - } - - /* wake i2c devices */ - em28xx_wake_i2c(dev); - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vbiq.active); - - if (dev->board.has_msp34xx) { - /* Send a reset to other chips via gpio */ - retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); - if (retval < 0) { - em28xx_errdev("%s: em28xx_write_reg - " - "msp34xx(1) failed! error [%d]\n", - __func__, retval); - goto fail; - } - msleep(3); - - retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); - if (retval < 0) { - em28xx_errdev("%s: em28xx_write_reg - " - "msp34xx(2) failed! error [%d]\n", - __func__, retval); - goto fail; - } - msleep(3); - } - - retval = em28xx_register_analog_devices(dev); - if (retval < 0) { - goto fail; - } - - /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); - - return 0; - -fail: - em28xx_i2c_unregister(dev); - -unregister_dev: - v4l2_device_unregister(&dev->v4l2_dev); - - return retval; -} - -/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) - -/* - * em28xx_usb_probe() - * checks for supported devices - */ -static int em28xx_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *udev; - struct em28xx *dev = NULL; - int retval; - bool has_audio = false, has_video = false, has_dvb = false; - int i, nr; - const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; - char *speed; - - udev = usb_get_dev(interface_to_usbdev(interface)); - - /* Check to see next free device and mark as used */ - do { - nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); - if (nr >= EM28XX_MAXBOARDS) { - /* No free device slots */ - printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", - EM28XX_MAXBOARDS); - retval = -ENOMEM; - goto err_no_slot; - } - } while (test_and_set_bit(nr, &em28xx_devused)); - - /* Don't register audio interfaces */ - if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - em28xx_err(DRIVER_NAME " audio device (%04x:%04x): " - "interface %i, class %i\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - ifnum, - interface->altsetting[0].desc.bInterfaceClass); - - retval = -ENODEV; - goto err; - } - - /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - em28xx_err(DRIVER_NAME ": out of memory!\n"); - retval = -ENOMEM; - goto err; - } - - /* compute alternate max packet sizes */ - dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) * - interface->num_altsetting, GFP_KERNEL); - if (dev->alt_max_pkt_size == NULL) { - em28xx_errdev("out of memory!\n"); - kfree(dev); - retval = -ENOMEM; - goto err; - } - - /* Get endpoints */ - for (i = 0; i < interface->num_altsetting; i++) { - int ep; - - for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { - const struct usb_endpoint_descriptor *e; - int sizedescr, size; - - e = &interface->altsetting[i].endpoint[ep].desc; - - sizedescr = le16_to_cpu(e->wMaxPacketSize); - size = sizedescr & 0x7ff; - - if (udev->speed == USB_SPEED_HIGH) - size = size * hb_mult(sizedescr); - - if (usb_endpoint_xfer_isoc(e) && - usb_endpoint_dir_in(e)) { - switch (e->bEndpointAddress) { - case EM28XX_EP_AUDIO: - has_audio = true; - break; - case EM28XX_EP_ANALOG: - has_video = true; - dev->alt_max_pkt_size[i] = size; - break; - case EM28XX_EP_DIGITAL: - has_dvb = true; - if (size > dev->dvb_max_pkt_size) { - dev->dvb_max_pkt_size = size; - dev->dvb_alt = i; - } - break; - } - } - } - } - - if (!(has_audio || has_video || has_dvb)) { - retval = -ENODEV; - goto err_free; - } - - switch (udev->speed) { - case USB_SPEED_LOW: - speed = "1.5"; - break; - case USB_SPEED_UNKNOWN: - case USB_SPEED_FULL: - speed = "12"; - break; - case USB_SPEED_HIGH: - speed = "480"; - break; - default: - speed = "unknown"; - } - - printk(KERN_INFO DRIVER_NAME - ": New device %s %s @ %s Mbps " - "(%04x:%04x, interface %d, class %d)\n", - udev->manufacturer ? udev->manufacturer : "", - udev->product ? udev->product : "", - speed, - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - ifnum, - interface->altsetting->desc.bInterfaceNumber); - - if (has_audio) - printk(KERN_INFO DRIVER_NAME - ": Audio Vendor Class interface %i found\n", - ifnum); - if (has_video) - printk(KERN_INFO DRIVER_NAME - ": Video interface %i found\n", - ifnum); - if (has_dvb) - printk(KERN_INFO DRIVER_NAME - ": DVB interface %i found\n", - ifnum); - - /* - * Make sure we have 480 Mbps of bandwidth, otherwise things like - * video stream wouldn't likely work, since 12 Mbps is generally - * not enough even for most Digital TV streams. - */ - if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { - printk(DRIVER_NAME ": Device initialization failed.\n"); - printk(DRIVER_NAME ": Device must be connected to a high-speed" - " USB 2.0 port.\n"); - retval = -ENODEV; - goto err_free; - } - - snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); - dev->devno = nr; - dev->model = id->driver_info; - dev->alt = -1; - dev->is_audio_only = has_audio && !(has_video || has_dvb); - dev->has_alsa_audio = has_audio; - dev->audio_ifnum = ifnum; - - /* Checks if audio is provided by some interface */ - for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { - struct usb_interface *uif = udev->config->interface[i]; - if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - dev->has_audio_class = 1; - break; - } - } - - dev->num_alt = interface->num_altsetting; - - if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) - dev->model = card[nr]; - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - /* allocate device struct */ - mutex_init(&dev->lock); - mutex_lock(&dev->lock); - retval = em28xx_init_dev(dev, udev, interface, nr); - if (retval) { - goto unlock_and_free; - } - - if (has_dvb) { - /* pre-allocate DVB isoc transfer buffers */ - retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, - EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size); - if (retval) { - goto unlock_and_free; - } - } - - request_modules(dev); - - /* Should be the last thing to do, to avoid newer udev's to - open the device before fully initializing it - */ - mutex_unlock(&dev->lock); - - /* - * These extensions can be modules. If the modules are already - * loaded then we can initialise the device now, otherwise we - * will initialise it when the modules load instead. - */ - em28xx_init_extension(dev); - - return 0; - -unlock_and_free: - mutex_unlock(&dev->lock); - -err_free: - kfree(dev->alt_max_pkt_size); - kfree(dev); - -err: - clear_bit(nr, &em28xx_devused); - -err_no_slot: - usb_put_dev(udev); - return retval; -} - -/* - * em28xx_usb_disconnect() - * called when the device gets disconnected - * video device will be unregistered on v4l2_close in case it is still open - */ -static void em28xx_usb_disconnect(struct usb_interface *interface) -{ - struct em28xx *dev; - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - if (!dev) - return; - - if (dev->is_audio_only) { - mutex_lock(&dev->lock); - em28xx_close_extension(dev); - mutex_unlock(&dev->lock); - return; - } - - em28xx_info("disconnecting %s\n", dev->vdev->name); - - flush_request_modules(dev); - - /* wait until all current v4l2 io is finished then deallocate - resources */ - mutex_lock(&dev->lock); - - v4l2_device_disconnect(&dev->v4l2_dev); - - if (dev->users) { - em28xx_warn - ("device %s is open! Deregistration and memory " - "deallocation are deferred on close.\n", - video_device_node_name(dev->vdev)); - - dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev, dev->mode); - dev->state |= DEV_DISCONNECTED; - } else { - dev->state |= DEV_DISCONNECTED; - em28xx_release_resources(dev); - } - - /* free DVB isoc buffers */ - em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); - - mutex_unlock(&dev->lock); - - em28xx_close_extension(dev); - - if (!dev->users) { - kfree(dev->alt_max_pkt_size); - kfree(dev); - } -} - -static struct usb_driver em28xx_usb_driver = { - .name = "em28xx", - .probe = em28xx_usb_probe, - .disconnect = em28xx_usb_disconnect, - .id_table = em28xx_id_table, -}; - -module_usb_driver(em28xx_usb_driver); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c deleted file mode 100644 index bed07a6c33f8..000000000000 --- a/drivers/media/video/em28xx/em28xx-core.c +++ /dev/null @@ -1,1260 +0,0 @@ -/* - em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices - - Copyright (C) 2005 Ludovico Cavedon - Markus Rechberger - Mauro Carvalho Chehab - Sascha Sommer - - This program is free software; 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 "em28xx.h" - -/* #define ENABLE_DEBUG_ISOC_FRAMES */ - -static unsigned int core_debug; -module_param(core_debug, int, 0644); -MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); - -#define em28xx_coredbg(fmt, arg...) do {\ - if (core_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -static unsigned int reg_debug; -module_param(reg_debug, int, 0644); -MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); - -#define em28xx_regdbg(fmt, arg...) do {\ - if (reg_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -static int alt; -module_param(alt, int, 0644); -MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); - -static unsigned int disable_vbi; -module_param(disable_vbi, int, 0644); -MODULE_PARM_DESC(disable_vbi, "disable vbi support"); - -/* FIXME */ -#define em28xx_isocdbg(fmt, arg...) do {\ - if (core_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -/* - * em28xx_read_reg_req() - * reads data from the usb device specifying bRequest - */ -int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, - char *buf, int len) -{ - int ret; - int pipe = usb_rcvctrlpipe(dev->udev, 0); - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - if (len > URB_MAX_CTRL_SIZE) - return -EINVAL; - - if (reg_debug) { - printk(KERN_DEBUG "(pipe 0x%08x): " - "IN: %02x %02x %02x %02x %02x %02x %02x %02x ", - pipe, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - req, 0, 0, - reg & 0xff, reg >> 8, - len & 0xff, len >> 8); - } - - mutex_lock(&dev->ctrl_urb_lock); - ret = usb_control_msg(dev->udev, pipe, req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, reg, dev->urb_buf, len, HZ); - if (ret < 0) { - if (reg_debug) - printk(" failed!\n"); - mutex_unlock(&dev->ctrl_urb_lock); - return ret; - } - - if (len) - memcpy(buf, dev->urb_buf, len); - - mutex_unlock(&dev->ctrl_urb_lock); - - if (reg_debug) { - int byte; - - printk("<<<"); - for (byte = 0; byte < len; byte++) - printk(" %02x", (unsigned char)buf[byte]); - printk("\n"); - } - - return ret; -} - -/* - * em28xx_read_reg_req() - * reads data from the usb device specifying bRequest - */ -int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg) -{ - int ret; - u8 val; - - ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1); - if (ret < 0) - return ret; - - return val; -} - -int em28xx_read_reg(struct em28xx *dev, u16 reg) -{ - return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg); -} -EXPORT_SYMBOL_GPL(em28xx_read_reg); - -/* - * em28xx_write_regs_req() - * sends data to the usb device, specifying bRequest - */ -int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, - int len) -{ - int ret; - int pipe = usb_sndctrlpipe(dev->udev, 0); - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - - if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) - return -EINVAL; - - if (reg_debug) { - int byte; - - printk(KERN_DEBUG "(pipe 0x%08x): " - "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", - pipe, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - req, 0, 0, - reg & 0xff, reg >> 8, - len & 0xff, len >> 8); - - for (byte = 0; byte < len; byte++) - printk(" %02x", (unsigned char)buf[byte]); - printk("\n"); - } - - mutex_lock(&dev->ctrl_urb_lock); - memcpy(dev->urb_buf, buf, len); - ret = usb_control_msg(dev->udev, pipe, req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, reg, dev->urb_buf, len, HZ); - mutex_unlock(&dev->ctrl_urb_lock); - - if (dev->wait_after_write) - msleep(dev->wait_after_write); - - return ret; -} - -int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) -{ - int rc; - - rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); - - /* Stores GPO/GPIO values at the cache, if changed - Only write values should be stored, since input on a GPIO - register will return the input bits. - Not sure what happens on reading GPO register. - */ - if (rc >= 0) { - if (reg == dev->reg_gpo_num) - dev->reg_gpo = buf[0]; - else if (reg == dev->reg_gpio_num) - dev->reg_gpio = buf[0]; - } - - return rc; -} -EXPORT_SYMBOL_GPL(em28xx_write_regs); - -/* Write a single register */ -int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) -{ - return em28xx_write_regs(dev, reg, &val, 1); -} -EXPORT_SYMBOL_GPL(em28xx_write_reg); - -/* - * em28xx_write_reg_bits() - * sets only some bits (specified by bitmask) of a register, by first reading - * the actual value - */ -int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, - u8 bitmask) -{ - int oldval; - u8 newval; - - /* Uses cache for gpo/gpio registers */ - if (reg == dev->reg_gpo_num) - oldval = dev->reg_gpo; - else if (reg == dev->reg_gpio_num) - oldval = dev->reg_gpio; - else - oldval = em28xx_read_reg(dev, reg); - - if (oldval < 0) - return oldval; - - newval = (((u8) oldval) & ~bitmask) | (val & bitmask); - - return em28xx_write_regs(dev, reg, &newval, 1); -} -EXPORT_SYMBOL_GPL(em28xx_write_reg_bits); - -/* - * em28xx_is_ac97_ready() - * Checks if ac97 is ready - */ -static int em28xx_is_ac97_ready(struct em28xx *dev) -{ - int ret, i; - - /* Wait up to 50 ms for AC97 command to complete */ - for (i = 0; i < 10; i++, msleep(5)) { - ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); - if (ret < 0) - return ret; - - if (!(ret & 0x01)) - return 0; - } - - em28xx_warn("AC97 command still being executed: not handled properly!\n"); - return -EBUSY; -} - -/* - * em28xx_read_ac97() - * write a 16 bit value to the specified AC97 address (LSB first!) - */ -int em28xx_read_ac97(struct em28xx *dev, u8 reg) -{ - int ret; - u8 addr = (reg & 0x7f) | 0x80; - u16 val; - - ret = em28xx_is_ac97_ready(dev); - if (ret < 0) - return ret; - - ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); - if (ret < 0) - return ret; - - ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB, - (u8 *)&val, sizeof(val)); - - if (ret < 0) - return ret; - return le16_to_cpu(val); -} -EXPORT_SYMBOL_GPL(em28xx_read_ac97); - -/* - * em28xx_write_ac97() - * write a 16 bit value to the specified AC97 address (LSB first!) - */ -int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) -{ - int ret; - u8 addr = reg & 0x7f; - __le16 value; - - value = cpu_to_le16(val); - - ret = em28xx_is_ac97_ready(dev); - if (ret < 0) - return ret; - - ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2); - if (ret < 0) - return ret; - - ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); - if (ret < 0) - return ret; - - return 0; -} -EXPORT_SYMBOL_GPL(em28xx_write_ac97); - -struct em28xx_vol_itable { - enum em28xx_amux mux; - u8 reg; -}; - -static struct em28xx_vol_itable inputs[] = { - { EM28XX_AMUX_VIDEO, AC97_VIDEO }, - { EM28XX_AMUX_LINE_IN, AC97_LINE }, - { EM28XX_AMUX_PHONE, AC97_PHONE }, - { EM28XX_AMUX_MIC, AC97_MIC }, - { EM28XX_AMUX_CD, AC97_CD }, - { EM28XX_AMUX_AUX, AC97_AUX }, - { EM28XX_AMUX_PCM_OUT, AC97_PCM }, -}; - -static int set_ac97_input(struct em28xx *dev) -{ - int ret, i; - enum em28xx_amux amux = dev->ctl_ainput; - - /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that - em28xx should point to LINE IN, while AC97 should use VIDEO - */ - if (amux == EM28XX_AMUX_VIDEO2) - amux = EM28XX_AMUX_VIDEO; - - /* Mute all entres but the one that were selected */ - for (i = 0; i < ARRAY_SIZE(inputs); i++) { - if (amux == inputs[i].mux) - ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808); - else - ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000); - - if (ret < 0) - em28xx_warn("couldn't setup AC97 register %d\n", - inputs[i].reg); - } - return 0; -} - -static int em28xx_set_audio_source(struct em28xx *dev) -{ - int ret; - u8 input; - - if (dev->board.is_em2800) { - if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) - input = EM2800_AUDIO_SRC_TUNER; - else - input = EM2800_AUDIO_SRC_LINE; - - ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1); - if (ret < 0) - return ret; - } - - if (dev->board.has_msp34xx) - input = EM28XX_AUDIO_SRC_TUNER; - else { - switch (dev->ctl_ainput) { - case EM28XX_AMUX_VIDEO: - input = EM28XX_AUDIO_SRC_TUNER; - break; - default: - input = EM28XX_AUDIO_SRC_LINE; - break; - } - } - - if (dev->board.mute_gpio && dev->mute) - em28xx_gpio_set(dev, dev->board.mute_gpio); - else - em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); - - ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0); - if (ret < 0) - return ret; - msleep(5); - - switch (dev->audio_mode.ac97) { - case EM28XX_NO_AC97: - break; - default: - ret = set_ac97_input(dev); - } - - return ret; -} - -struct em28xx_vol_otable { - enum em28xx_aout mux; - u8 reg; -}; - -static const struct em28xx_vol_otable outputs[] = { - { EM28XX_AOUT_MASTER, AC97_MASTER }, - { EM28XX_AOUT_LINE, AC97_HEADPHONE }, - { EM28XX_AOUT_MONO, AC97_MASTER_MONO }, - { EM28XX_AOUT_LFE, AC97_CENTER_LFE_MASTER }, - { EM28XX_AOUT_SURR, AC97_SURROUND_MASTER }, -}; - -int em28xx_audio_analog_set(struct em28xx *dev) -{ - int ret, i; - u8 xclk; - - if (!dev->audio_mode.has_audio) - return 0; - - /* It is assumed that all devices use master volume for output. - It would be possible to use also line output. - */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - /* Mute all outputs */ - for (i = 0; i < ARRAY_SIZE(outputs); i++) { - ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000); - if (ret < 0) - em28xx_warn("couldn't setup AC97 register %d\n", - outputs[i].reg); - } - } - - xclk = dev->board.xclk & 0x7f; - if (!dev->mute) - xclk |= EM28XX_XCLK_AUDIO_UNMUTE; - - ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk); - if (ret < 0) - return ret; - msleep(10); - - /* Selects the proper audio input */ - ret = em28xx_set_audio_source(dev); - - /* Sets volume */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - int vol; - - em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200); - em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031); - em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80); - - /* LSB: left channel - both channels with the same level */ - vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8); - - /* Mute device, if needed */ - if (dev->mute) - vol |= 0x8000; - - /* Sets volume */ - for (i = 0; i < ARRAY_SIZE(outputs); i++) { - if (dev->ctl_aoutput & outputs[i].mux) - ret = em28xx_write_ac97(dev, outputs[i].reg, - vol); - if (ret < 0) - em28xx_warn("couldn't setup AC97 register %d\n", - outputs[i].reg); - } - - if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) { - int sel = ac97_return_record_select(dev->ctl_aoutput); - - /* Use the same input for both left and right - channels */ - sel |= (sel << 8); - - em28xx_write_ac97(dev, AC97_REC_SEL, sel); - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); - -int em28xx_audio_setup(struct em28xx *dev) -{ - int vid1, vid2, feat, cfg; - u32 vid; - - if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874 - || dev->chip_id == CHIP_ID_EM28174) { - /* Digital only device - don't load any alsa module */ - dev->audio_mode.has_audio = false; - dev->has_audio_class = false; - dev->has_alsa_audio = false; - return 0; - } - - dev->audio_mode.has_audio = true; - - /* See how this device is configured */ - cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); - em28xx_info("Config register raw data: 0x%02x\n", cfg); - if (cfg < 0) { - /* Register read error? */ - cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */ - } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) { - /* The device doesn't have vendor audio at all */ - dev->has_alsa_audio = false; - dev->audio_mode.has_audio = false; - return 0; - } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == - EM28XX_CHIPCFG_I2S_3_SAMPRATES) { - em28xx_info("I2S Audio (3 sample rates)\n"); - dev->audio_mode.i2s_3rates = 1; - } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == - EM28XX_CHIPCFG_I2S_5_SAMPRATES) { - em28xx_info("I2S Audio (5 sample rates)\n"); - dev->audio_mode.i2s_5rates = 1; - } - - if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) { - /* Skip the code that does AC97 vendor detection */ - dev->audio_mode.ac97 = EM28XX_NO_AC97; - goto init_audio; - } - - dev->audio_mode.ac97 = EM28XX_AC97_OTHER; - - vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); - if (vid1 < 0) { - /* - * Device likely doesn't support AC97 - * Note: (some) em2800 devices without eeprom reports 0x91 on - * CHIPCFG register, even not having an AC97 chip - */ - em28xx_warn("AC97 chip type couldn't be determined\n"); - dev->audio_mode.ac97 = EM28XX_NO_AC97; - dev->has_alsa_audio = false; - dev->audio_mode.has_audio = false; - goto init_audio; - } - - vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2); - if (vid2 < 0) - goto init_audio; - - vid = vid1 << 16 | vid2; - - dev->audio_mode.ac97_vendor_id = vid; - em28xx_warn("AC97 vendor ID = 0x%08x\n", vid); - - feat = em28xx_read_ac97(dev, AC97_RESET); - if (feat < 0) - goto init_audio; - - dev->audio_mode.ac97_feat = feat; - em28xx_warn("AC97 features = 0x%04x\n", feat); - - /* Try to identify what audio processor we have */ - if (((vid == 0xffffffff) || (vid == 0x83847650)) && (feat == 0x6a90)) - dev->audio_mode.ac97 = EM28XX_AC97_EM202; - else if ((vid >> 8) == 0x838476) - dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL; - -init_audio: - /* Reports detected AC97 processor */ - switch (dev->audio_mode.ac97) { - case EM28XX_NO_AC97: - em28xx_info("No AC97 audio processor\n"); - break; - case EM28XX_AC97_EM202: - em28xx_info("Empia 202 AC97 audio processor detected\n"); - break; - case EM28XX_AC97_SIGMATEL: - em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n", - dev->audio_mode.ac97_vendor_id & 0xff); - break; - case EM28XX_AC97_OTHER: - em28xx_warn("Unknown AC97 audio processor detected!\n"); - break; - default: - break; - } - - return em28xx_audio_analog_set(dev); -} -EXPORT_SYMBOL_GPL(em28xx_audio_setup); - -int em28xx_colorlevels_set_default(struct em28xx *dev) -{ - em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */ - em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */ - em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */ - em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00); - em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00); - em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00); - - em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20); - em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20); - em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); - em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); - em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); - em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); - return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); -} - -int em28xx_capture_start(struct em28xx *dev, int start) -{ - int rc; - - if (dev->chip_id == CHIP_ID_EM2874 || - dev->chip_id == CHIP_ID_EM2884 || - dev->chip_id == CHIP_ID_EM28174) { - /* The Transport Stream Enable Register moved in em2874 */ - if (!start) { - rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - 0x00, - EM2874_TS1_CAPTURE_ENABLE); - return rc; - } - - /* Enable Transport Stream */ - rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - EM2874_TS1_CAPTURE_ENABLE, - EM2874_TS1_CAPTURE_ENABLE); - return rc; - } - - - /* FIXME: which is the best order? */ - /* video registers are sampled by VREF */ - rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, - start ? 0x10 : 0x00, 0x10); - if (rc < 0) - return rc; - - if (!start) { - /* disable video capture */ - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); - return rc; - } - - if (dev->board.is_webcam) - rc = em28xx_write_reg(dev, 0x13, 0x0c); - - /* enable video capture */ - rc = em28xx_write_reg(dev, 0x48, 0x00); - - if (dev->mode == EM28XX_ANALOG_MODE) - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); - else - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); - - msleep(6); - - return rc; -} - -int em28xx_vbi_supported(struct em28xx *dev) -{ - /* Modprobe option to manually disable */ - if (disable_vbi == 1) - return 0; - - if (dev->chip_id == CHIP_ID_EM2860 || - dev->chip_id == CHIP_ID_EM2883) - return 1; - - /* Version of em28xx that does not support VBI */ - return 0; -} - -int em28xx_set_outfmt(struct em28xx *dev) -{ - int ret; - u8 vinctrl; - - ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, - dev->format->reg | 0x20, 0xff); - if (ret < 0) - return ret; - - ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); - if (ret < 0) - return ret; - - vinctrl = dev->vinctl; - if (em28xx_vbi_supported(dev) == 1) { - vinctrl |= EM28XX_VINCTRL_VBI_RAW; - em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); - em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4); - em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height); - if (dev->norm & V4L2_STD_525_60) { - /* NTSC */ - em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); - } else if (dev->norm & V4L2_STD_625_50) { - /* PAL */ - em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); - } - } - - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); -} - -static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, - u8 ymin, u8 ymax) -{ - em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", - xmin, ymin, xmax, ymax); - - em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); - em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); - em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); - return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); -} - -static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, - u16 width, u16 height) -{ - u8 cwidth = width; - u8 cheight = height; - u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01); - - em28xx_coredbg("em28xx Area Set: (%d,%d)\n", - (width | (overflow & 2) << 7), - (height | (overflow & 1) << 8)); - - em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); - em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); - em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); - em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); - return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); -} - -static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) -{ - u8 mode; - /* the em2800 scaler only supports scaling down to 50% */ - - if (dev->board.is_em2800) { - mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); - } else { - u8 buf[2]; - - buf[0] = h; - buf[1] = h >> 8; - em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); - - buf[0] = v; - buf[1] = v >> 8; - em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); - /* it seems that both H and V scalers must be active - to work correctly */ - mode = (h || v) ? 0x30 : 0x00; - } - return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); -} - -/* FIXME: this only function read values from dev */ -int em28xx_resolution_set(struct em28xx *dev) -{ - int width, height; - width = norm_maxw(dev); - height = norm_maxh(dev); - - /* Properly setup VBI */ - dev->vbi_width = 720; - if (dev->norm & V4L2_STD_525_60) - dev->vbi_height = 12; - else - dev->vbi_height = 18; - - em28xx_set_outfmt(dev); - - em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - - /* If we don't set the start position to 2 in VBI mode, we end up - with line 20/21 being YUYV encoded instead of being in 8-bit - greyscale. The core of the issue is that line 21 (and line 23 for - PAL WSS) are inside of active video region, and as a result they - get the pixelformatting associated with that area. So by cropping - it out, we end up with the same format as the rest of the VBI - region */ - if (em28xx_vbi_supported(dev) == 1) - em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2); - else - em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); - - return em28xx_scaler_set(dev, dev->hscale, dev->vscale); -} - -int em28xx_set_alternate(struct em28xx *dev) -{ - int errCode, prev_alt = dev->alt; - int i; - unsigned int min_pkt_size = dev->width * 2 + 4; - - /* - * alt = 0 is used only for control messages, so, only values - * greater than 0 can be used for streaming. - */ - if (alt && alt < dev->num_alt) { - em28xx_coredbg("alternate forced to %d\n", dev->alt); - dev->alt = alt; - goto set_alt; - } - - /* When image size is bigger than a certain value, - the frame size should be increased, otherwise, only - green screen will be received. - */ - if (dev->width * 2 * dev->height > 720 * 240 * 2) - min_pkt_size *= 2; - - for (i = 0; i < dev->num_alt; i++) { - /* stop when the selected alt setting offers enough bandwidth */ - if (dev->alt_max_pkt_size[i] >= min_pkt_size) { - dev->alt = i; - break; - /* otherwise make sure that we end up with the maximum bandwidth - because the min_pkt_size equation might be wrong... - */ - } else if (dev->alt_max_pkt_size[i] > - dev->alt_max_pkt_size[dev->alt]) - dev->alt = i; - } - -set_alt: - if (dev->alt != prev_alt) { - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; - em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", - dev->alt, dev->max_pkt_size); - errCode = usb_set_interface(dev->udev, 0, dev->alt); - if (errCode < 0) { - em28xx_errdev("cannot change alternate number to %d (error=%i)\n", - dev->alt, errCode); - return errCode; - } - } - return 0; -} - -int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) -{ - int rc = 0; - - if (!gpio) - return rc; - - if (dev->mode != EM28XX_SUSPEND) { - em28xx_write_reg(dev, 0x48, 0x00); - if (dev->mode == EM28XX_ANALOG_MODE) - em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); - else - em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); - msleep(6); - } - - /* Send GPIO reset sequences specified at board entry */ - while (gpio->sleep >= 0) { - if (gpio->reg >= 0) { - rc = em28xx_write_reg_bits(dev, - gpio->reg, - gpio->val, - gpio->mask); - if (rc < 0) - return rc; - } - if (gpio->sleep > 0) - msleep(gpio->sleep); - - gpio++; - } - return rc; -} -EXPORT_SYMBOL_GPL(em28xx_gpio_set); - -int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) -{ - if (dev->mode == set_mode) - return 0; - - if (set_mode == EM28XX_SUSPEND) { - dev->mode = set_mode; - - /* FIXME: add suspend support for ac97 */ - - return em28xx_gpio_set(dev, dev->board.suspend_gpio); - } - - dev->mode = set_mode; - - if (dev->mode == EM28XX_DIGITAL_MODE) - return em28xx_gpio_set(dev, dev->board.dvb_gpio); - else - return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); -} -EXPORT_SYMBOL_GPL(em28xx_set_mode); - -/* ------------------------------------------------------------------ - URB control - ------------------------------------------------------------------*/ - -/* - * IRQ callback, called by URB callback - */ -static void em28xx_irq_callback(struct urb *urb) -{ - struct em28xx *dev = urb->context; - int i; - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - em28xx_isocdbg("urb completition error %d.\n", urb->status); - break; - } - - /* Copy data from URB */ - spin_lock(&dev->slock); - dev->isoc_ctl.isoc_copy(dev, urb); - spin_unlock(&dev->slock); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) { - em28xx_isocdbg("urb resubmit failed (error=%i)\n", - urb->status); - } -} - -/* - * Stop and Deallocate URBs - */ -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) -{ - struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs; - int i; - - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); - - if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; - else - isoc_bufs = &dev->isoc_ctl.analog_bufs; - - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = isoc_bufs->urb[i]; - if (urb) { - if (!irqs_disabled()) - usb_kill_urb(urb); - else - usb_unlink_urb(urb); - - if (isoc_bufs->transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - isoc_bufs->transfer_buffer[i], - urb->transfer_dma); - } - usb_free_urb(urb); - isoc_bufs->urb[i] = NULL; - } - isoc_bufs->transfer_buffer[i] = NULL; - } - - kfree(isoc_bufs->urb); - kfree(isoc_bufs->transfer_buffer); - - isoc_bufs->urb = NULL; - isoc_bufs->transfer_buffer = NULL; - isoc_bufs->num_bufs = 0; - - em28xx_capture_start(dev, 0); -} -EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); - -/* - * Stop URBs - */ -void em28xx_stop_urbs(struct em28xx *dev) -{ - int i; - struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; - - em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); - - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = isoc_bufs->urb[i]; - if (urb) { - if (!irqs_disabled()) - usb_kill_urb(urb); - else - usb_unlink_urb(urb); - } - } - - em28xx_capture_start(dev, 0); -} -EXPORT_SYMBOL_GPL(em28xx_stop_urbs); - -/* - * Allocate URBs - */ -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size) -{ - struct em28xx_usb_isoc_bufs *isoc_bufs; - int i; - int sb_size, pipe; - struct urb *urb; - int j, k; - - em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); - - if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; - else - isoc_bufs = &dev->isoc_ctl.analog_bufs; - - /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev, mode); - - isoc_bufs->num_bufs = num_bufs; - - isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->urb) { - em28xx_errdev("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!isoc_bufs->transfer_buffer) { - em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(isoc_bufs->urb); - return -ENOMEM; - } - - isoc_bufs->max_pkt_size = max_pkt_size; - isoc_bufs->num_packets = max_packets; - dev->isoc_ctl.vid_buf = NULL; - dev->isoc_ctl.vbi_buf = NULL; - - sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; - - /* allocate urbs and transfer buffers */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); - if (!urb) { - em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev, mode); - return -ENOMEM; - } - isoc_bufs->urb[i] = urb; - - isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!isoc_bufs->transfer_buffer[i]) { - em28xx_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev, mode); - return -ENOMEM; - } - memset(isoc_bufs->transfer_buffer[i], 0, sb_size); - - /* FIXME: this is a hack - should be - 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' - should also be using 'desc.bInterval' - */ - pipe = usb_rcvisocpipe(dev->udev, - mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); - - usb_fill_int_urb(urb, dev->udev, pipe, - isoc_bufs->transfer_buffer[i], sb_size, - em28xx_irq_callback, dev, 1); - - urb->number_of_packets = isoc_bufs->num_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - k = 0; - for (j = 0; j < isoc_bufs->num_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - isoc_bufs->max_pkt_size; - k += isoc_bufs->max_pkt_size; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); - -/* - * Allocate URBs and start IRQ - */ -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) -{ - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - struct em28xx_usb_isoc_bufs *isoc_bufs; - int i; - int rc; - int alloc; - - em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); - - dev->isoc_ctl.isoc_copy = isoc_copy; - - if (mode == EM28XX_DIGITAL_MODE) { - isoc_bufs = &dev->isoc_ctl.digital_bufs; - /* no need to free/alloc isoc buffers in digital mode */ - alloc = 0; - } else { - isoc_bufs = &dev->isoc_ctl.analog_bufs; - alloc = 1; - } - - if (alloc) { - rc = em28xx_alloc_isoc(dev, mode, max_packets, - num_bufs, max_pkt_size); - if (rc) - return rc; - } - - init_waitqueue_head(&dma_q->wq); - init_waitqueue_head(&vbi_dma_q->wq); - - em28xx_capture_start(dev, 1); - - /* submit urbs and enables IRQ */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); - if (rc) { - em28xx_err("submit of urb %i failed (error=%i)\n", i, - rc); - em28xx_uninit_isoc(dev, mode); - return rc; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(em28xx_init_isoc); - -/* - * em28xx_wake_i2c() - * configure i2c attached devices - */ -void em28xx_wake_i2c(struct em28xx *dev) -{ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, - INPUT(dev->ctl_input)->vmux, 0, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); -} - -/* - * Device control list - */ - -static LIST_HEAD(em28xx_devlist); -static DEFINE_MUTEX(em28xx_devlist_mutex); - -/* - * Extension interface - */ - -static LIST_HEAD(em28xx_extension_devlist); - -int em28xx_register_extension(struct em28xx_ops *ops) -{ - struct em28xx *dev = NULL; - - mutex_lock(&em28xx_devlist_mutex); - list_add_tail(&ops->next, &em28xx_extension_devlist); - list_for_each_entry(dev, &em28xx_devlist, devlist) { - ops->init(dev); - } - mutex_unlock(&em28xx_devlist_mutex); - printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); - return 0; -} -EXPORT_SYMBOL(em28xx_register_extension); - -void em28xx_unregister_extension(struct em28xx_ops *ops) -{ - struct em28xx *dev = NULL; - - mutex_lock(&em28xx_devlist_mutex); - list_for_each_entry(dev, &em28xx_devlist, devlist) { - ops->fini(dev); - } - list_del(&ops->next); - mutex_unlock(&em28xx_devlist_mutex); - printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); -} -EXPORT_SYMBOL(em28xx_unregister_extension); - -void em28xx_init_extension(struct em28xx *dev) -{ - const struct em28xx_ops *ops = NULL; - - mutex_lock(&em28xx_devlist_mutex); - list_add_tail(&dev->devlist, &em28xx_devlist); - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->init) - ops->init(dev); - } - mutex_unlock(&em28xx_devlist_mutex); -} - -void em28xx_close_extension(struct em28xx *dev) -{ - const struct em28xx_ops *ops = NULL; - - mutex_lock(&em28xx_devlist_mutex); - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->fini) - ops->fini(dev); - } - list_del(&dev->devlist); - mutex_unlock(&em28xx_devlist_mutex); -} diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c deleted file mode 100644 index a16531fa937a..000000000000 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ /dev/null @@ -1,1197 +0,0 @@ -/* - DVB device driver for em28xx - - (c) 2008-2011 Mauro Carvalho Chehab - - (c) 2008 Devin Heitmueller - - Fixes for the driver to properly work with HVR-950 - - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick - - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600 - - (c) 2008 Aidan Thornton - - Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: - (c) 2004, 2005 Chris Pascoe - (c) 2004 Gerd Knorr [SuSE Labs] - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License. - */ - -#include -#include -#include - -#include "em28xx.h" -#include -#include -#include -#include "tuner-simple.h" - -#include "lgdt330x.h" -#include "lgdt3305.h" -#include "zl10353.h" -#include "s5h1409.h" -#include "mt352.h" -#include "mt352_priv.h" /* FIXME */ -#include "tda1002x.h" -#include "tda18271.h" -#include "s921.h" -#include "drxd.h" -#include "cxd2820r.h" -#include "tda18271c2dd.h" -#include "drxk.h" -#include "tda10071.h" -#include "a8293.h" -#include "qt1010.h" - -MODULE_DESCRIPTION("driver for em28xx based DVB cards"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define dprintk(level, fmt, arg...) do { \ -if (debug >= level) \ - printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ -} while (0) - -struct em28xx_dvb { - struct dvb_frontend *fe[2]; - - /* feed count management */ - struct mutex lock; - int nfeeds; - - /* general boilerplate stuff */ - struct dvb_adapter adapter; - struct dvb_demux demux; - struct dmxdev dmxdev; - struct dmx_frontend fe_hw; - struct dmx_frontend fe_mem; - struct dvb_net net; - - /* Due to DRX-K - probably need changes */ - int (*gate_ctrl)(struct dvb_frontend *, int); - struct semaphore pll_mutex; - bool dont_attach_fe1; -}; - - -static inline void print_err_status(struct em28xx *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - dprintk(1, "URB status %d [%s].\n", status, errmsg); - } else { - dprintk(1, "URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) -{ - int i; - - if (!dev) - return 0; - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); - } - - return 0; -} - -static int em28xx_start_streaming(struct em28xx_dvb *dvb) -{ - int rc; - struct em28xx *dev = dvb->adapter.priv; - int max_dvb_packet_size; - - usb_set_interface(dev->udev, 0, dev->dvb_alt); - rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); - if (rc < 0) - return rc; - - max_dvb_packet_size = dev->dvb_max_pkt_size; - if (max_dvb_packet_size < 0) - return max_dvb_packet_size; - dprintk(1, "Using %d buffers each with %d x %d bytes\n", - EM28XX_DVB_NUM_BUFS, - EM28XX_DVB_MAX_PACKETS, - max_dvb_packet_size); - - return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, - max_dvb_packet_size, em28xx_dvb_isoc_copy); -} - -static int em28xx_stop_streaming(struct em28xx_dvb *dvb) -{ - struct em28xx *dev = dvb->adapter.priv; - - em28xx_stop_urbs(dev); - - em28xx_set_mode(dev, EM28XX_SUSPEND); - - return 0; -} - -static int em28xx_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct em28xx_dvb *dvb = demux->priv; - int rc, ret; - - if (!demux->dmx.frontend) - return -EINVAL; - - mutex_lock(&dvb->lock); - dvb->nfeeds++; - rc = dvb->nfeeds; - - if (dvb->nfeeds == 1) { - ret = em28xx_start_streaming(dvb); - if (ret < 0) - rc = ret; - } - - mutex_unlock(&dvb->lock); - return rc; -} - -static int em28xx_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct em28xx_dvb *dvb = demux->priv; - int err = 0; - - mutex_lock(&dvb->lock); - dvb->nfeeds--; - - if (0 == dvb->nfeeds) - err = em28xx_stop_streaming(dvb); - - mutex_unlock(&dvb->lock); - return err; -} - - - -/* ------------------------------------------------------------------ */ -static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) -{ - struct em28xx *dev = fe->dvb->priv; - - if (acquire) - return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); - else - return em28xx_set_mode(dev, EM28XX_SUSPEND); -} - -/* ------------------------------------------------------------------ */ - -static struct lgdt330x_config em2880_lgdt3303_dev = { - .demod_address = 0x0e, - .demod_chip = LGDT3303, -}; - -static struct lgdt3305_config em2870_lgdt3304_dev = { - .i2c_addr = 0x0e, - .demod_chip = LGDT3304, - .spectral_inversion = 1, - .deny_i2c_rptr = 1, - .mpeg_mode = LGDT3305_MPEG_PARALLEL, - .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, - .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, - .vsb_if_khz = 3250, - .qam_if_khz = 4000, -}; - -static struct s921_config sharp_isdbt = { - .demod_address = 0x30 >> 1 -}; - -static struct zl10353_config em28xx_zl10353_with_xc3028 = { - .demod_address = (0x1e >> 1), - .no_tuner = 1, - .parallel_ts = 1, - .if2 = 45600, -}; - -static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { - .demod_address = 0x32 >> 1, - .output_mode = S5H1409_PARALLEL_OUTPUT, - .gpio = S5H1409_GPIO_OFF, - .inversion = S5H1409_INVERSION_OFF, - .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK -}; - -static struct tda18271_std_map kworld_a340_std_map = { - .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0, - .if_lvl = 1, .rfagc_top = 0x37, }, - .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1, - .if_lvl = 1, .rfagc_top = 0x37, }, -}; - -static struct tda18271_config kworld_a340_config = { - .std_map = &kworld_a340_std_map, -}; - -static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { - .demod_address = (0x1e >> 1), - .no_tuner = 1, - .disable_i2c_gate_ctrl = 1, - .parallel_ts = 1, - .if2 = 45600, -}; - -static struct drxd_config em28xx_drxd = { - .demod_address = 0x70, - .demod_revision = 0xa2, - .pll_type = DRXD_PLL_NONE, - .clock = 12000, - .insert_rs_byte = 1, - .IF = 42800000, - .disable_i2c_gate_ctrl = 1, -}; - -static struct drxk_config terratec_h5_drxk = { - .adr = 0x29, - .single_master = 1, - .no_i2c_bridge = 1, - .microcode_name = "dvb-usb-terratec-h5-drxk.fw", - .qam_demod_parameter_count = 2, -}; - -static struct drxk_config hauppauge_930c_drxk = { - .adr = 0x29, - .single_master = 1, - .no_i2c_bridge = 1, - .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw", - .chunk_size = 56, - .qam_demod_parameter_count = 2, -}; - -struct drxk_config terratec_htc_stick_drxk = { - .adr = 0x29, - .single_master = 1, - .no_i2c_bridge = 1, - .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw", - .chunk_size = 54, - .qam_demod_parameter_count = 2, - /* Required for the antenna_gpio to disable LNA. */ - .antenna_dvbt = true, - /* The windows driver uses the same. This will disable LNA. */ - .antenna_gpio = 0x6, -}; - -static struct drxk_config maxmedia_ub425_tc_drxk = { - .adr = 0x29, - .single_master = 1, - .no_i2c_bridge = 1, -}; - -static struct drxk_config pctv_520e_drxk = { - .adr = 0x29, - .single_master = 1, - .microcode_name = "dvb-demod-drxk-pctv.fw", - .qam_demod_parameter_count = 2, - .chunk_size = 58, - .antenna_dvbt = true, /* disable LNA */ - .antenna_gpio = (1 << 2), /* disable LNA */ -}; - -static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct em28xx_dvb *dvb = fe->sec_priv; - int status; - - if (!dvb) - return -EINVAL; - - if (enable) { - down(&dvb->pll_mutex); - status = dvb->gate_ctrl(fe, 1); - } else { - status = dvb->gate_ctrl(fe, 0); - up(&dvb->pll_mutex); - } - return status; -} - -static void hauppauge_hvr930c_init(struct em28xx *dev) -{ - int i; - - struct em28xx_reg_seq hauppauge_hvr930c_init[] = { - {EM2874_R80_GPIO, 0xff, 0xff, 0x65}, - {EM2874_R80_GPIO, 0xfb, 0xff, 0x32}, - {EM2874_R80_GPIO, 0xff, 0xff, 0xb8}, - { -1, -1, -1, -1}, - }; - struct em28xx_reg_seq hauppauge_hvr930c_end[] = { - {EM2874_R80_GPIO, 0xef, 0xff, 0x01}, - {EM2874_R80_GPIO, 0xaf, 0xff, 0x65}, - {EM2874_R80_GPIO, 0xef, 0xff, 0x76}, - {EM2874_R80_GPIO, 0xef, 0xff, 0x01}, - {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b}, - {EM2874_R80_GPIO, 0xef, 0xff, 0x40}, - - {EM2874_R80_GPIO, 0xcf, 0xff, 0x65}, - {EM2874_R80_GPIO, 0xef, 0xff, 0x65}, - {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b}, - {EM2874_R80_GPIO, 0xef, 0xff, 0x65}, - - { -1, -1, -1, -1}, - }; - - struct { - unsigned char r[4]; - int len; - } regs[] = { - {{ 0x06, 0x02, 0x00, 0x31 }, 4}, - {{ 0x01, 0x02 }, 2}, - {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, - {{ 0x01, 0x00 }, 2}, - {{ 0x01, 0x00, 0xff, 0xaf }, 4}, - {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, - {{ 0x01, 0x00 }, 2}, - {{ 0x01, 0x00, 0x73, 0xaf }, 4}, - {{ 0x04, 0x00 }, 2}, - {{ 0x00, 0x04 }, 2}, - {{ 0x00, 0x04, 0x00, 0x0a }, 4}, - {{ 0x04, 0x14 }, 2}, - {{ 0x04, 0x14, 0x00, 0x00 }, 4}, - }; - - em28xx_gpio_set(dev, hauppauge_hvr930c_init); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - msleep(10); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); - msleep(10); - - dev->i2c_client.addr = 0x82 >> 1; - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); - em28xx_gpio_set(dev, hauppauge_hvr930c_end); - - msleep(100); - - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); - msleep(30); - - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); - msleep(10); - -} - -static void terratec_h5_init(struct em28xx *dev) -{ - int i; - struct em28xx_reg_seq terratec_h5_init[] = { - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - {EM2874_R80_GPIO, 0xf6, 0xff, 100}, - {EM2874_R80_GPIO, 0xf2, 0xff, 50}, - {EM2874_R80_GPIO, 0xf6, 0xff, 100}, - { -1, -1, -1, -1}, - }; - struct em28xx_reg_seq terratec_h5_end[] = { - {EM2874_R80_GPIO, 0xe6, 0xff, 100}, - {EM2874_R80_GPIO, 0xa6, 0xff, 50}, - {EM2874_R80_GPIO, 0xe6, 0xff, 100}, - { -1, -1, -1, -1}, - }; - struct { - unsigned char r[4]; - int len; - } regs[] = { - {{ 0x06, 0x02, 0x00, 0x31 }, 4}, - {{ 0x01, 0x02 }, 2}, - {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, - {{ 0x01, 0x00 }, 2}, - {{ 0x01, 0x00, 0xff, 0xaf }, 4}, - {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, - {{ 0x01, 0x00 }, 2}, - {{ 0x01, 0x00, 0x73, 0xaf }, 4}, - {{ 0x04, 0x00 }, 2}, - {{ 0x00, 0x04 }, 2}, - {{ 0x00, 0x04, 0x00, 0x0a }, 4}, - {{ 0x04, 0x14 }, 2}, - {{ 0x04, 0x14, 0x00, 0x00 }, 4}, - }; - - em28xx_gpio_set(dev, terratec_h5_init); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - msleep(10); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); - msleep(10); - - dev->i2c_client.addr = 0x82 >> 1; - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); - em28xx_gpio_set(dev, terratec_h5_end); -}; - -static void terratec_htc_stick_init(struct em28xx *dev) -{ - int i; - - /* - * GPIO configuration: - * 0xff: unknown (does not affect DVB-T). - * 0xf6: DRX-K (demodulator). - * 0xe6: unknown (does not affect DVB-T). - * 0xb6: unknown (does not affect DVB-T). - */ - struct em28xx_reg_seq terratec_htc_stick_init[] = { - {EM28XX_R08_GPIO, 0xff, 0xff, 10}, - {EM2874_R80_GPIO, 0xf6, 0xff, 100}, - {EM2874_R80_GPIO, 0xe6, 0xff, 50}, - {EM2874_R80_GPIO, 0xf6, 0xff, 100}, - { -1, -1, -1, -1}, - }; - struct em28xx_reg_seq terratec_htc_stick_end[] = { - {EM2874_R80_GPIO, 0xb6, 0xff, 100}, - {EM2874_R80_GPIO, 0xf6, 0xff, 50}, - { -1, -1, -1, -1}, - }; - - /* Init the analog decoder? */ - struct { - unsigned char r[4]; - int len; - } regs[] = { - {{ 0x06, 0x02, 0x00, 0x31 }, 4}, - {{ 0x01, 0x02 }, 2}, - {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, - {{ 0x01, 0x00 }, 2}, - {{ 0x01, 0x00, 0xff, 0xaf }, 4}, - }; - - em28xx_gpio_set(dev, terratec_htc_stick_init); - - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); - msleep(10); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); - msleep(10); - - dev->i2c_client.addr = 0x82 >> 1; - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); - - em28xx_gpio_set(dev, terratec_htc_stick_end); -}; - -static void pctv_520e_init(struct em28xx *dev) -{ - /* - * Init AVF4910B analog decoder. Looks like I2C traffic to - * digital demodulator and tuner are routed via AVF4910B. - */ - int i; - struct { - unsigned char r[4]; - int len; - } regs[] = { - {{ 0x06, 0x02, 0x00, 0x31 }, 4}, - {{ 0x01, 0x02 }, 2}, - {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, - {{ 0x01, 0x00 }, 2}, - {{ 0x01, 0x00, 0xff, 0xaf }, 4}, - {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, - {{ 0x01, 0x00 }, 2}, - {{ 0x01, 0x00, 0x73, 0xaf }, 4}, - }; - - dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */ - - for (i = 0; i < ARRAY_SIZE(regs); i++) - i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); -}; - -static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) -{ - /* Values extracted from a USB trace of the Terratec Windows driver */ - static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c }; - static u8 reset[] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 }; - static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 }; - static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d }; - static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; - static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 }; - static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 }; - static u8 tuner_go[] = { TUNER_GO, 0x01}; - - mt352_write(fe, clock_config, sizeof(clock_config)); - udelay(200); - mt352_write(fe, reset, sizeof(reset)); - mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); - mt352_write(fe, agc_cfg, sizeof(agc_cfg)); - mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg)); - mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg)); - mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); - mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg)); - mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg)); - mt352_write(fe, tuner_go, sizeof(tuner_go)); - return 0; -} - -static struct mt352_config terratec_xs_mt352_cfg = { - .demod_address = (0x1e >> 1), - .no_tuner = 1, - .if2 = 45600, - .demod_init = em28xx_mt352_terratec_xs_init, -}; - -static struct tda10023_config em28xx_tda10023_config = { - .demod_address = 0x0c, - .invert = 1, -}; - -static struct cxd2820r_config em28xx_cxd2820r_config = { - .i2c_address = (0xd8 >> 1), - .ts_mode = CXD2820R_TS_SERIAL, - - /* enable LNA for DVB-T, DVB-T2 and DVB-C */ - .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, - .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, - .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, -}; - -static struct tda18271_config em28xx_cxd2820r_tda18271_config = { - .output_opt = TDA18271_OUTPUT_LT_OFF, - .gate = TDA18271_GATE_DIGITAL, -}; - -static const struct tda10071_config em28xx_tda10071_config = { - .i2c_address = 0x55, /* (0xaa >> 1) */ - .i2c_wr_max = 64, - .ts_mode = TDA10071_TS_SERIAL, - .spec_inv = 0, - .xtal = 40444000, /* 40.444 MHz */ - .pll_multiplier = 20, -}; - -static const struct a8293_config em28xx_a8293_config = { - .i2c_addr = 0x08, /* (0x10 >> 1) */ -}; - -static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = { - .demod_address = (0x1e >> 1), - .disable_i2c_gate_ctrl = 1, - .no_tuner = 1, - .parallel_ts = 1, -}; -static struct qt1010_config em28xx_qt1010_config = { - .i2c_address = 0x62 - -}; - -/* ------------------------------------------------------------------ */ - -static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) -{ - struct dvb_frontend *fe; - struct xc2028_config cfg; - - memset(&cfg, 0, sizeof(cfg)); - cfg.i2c_adap = &dev->i2c_adap; - cfg.i2c_addr = addr; - - if (!dev->dvb->fe[0]) { - em28xx_errdev("/2: dvb frontend not attached. " - "Can't attach xc3028\n"); - return -EINVAL; - } - - fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg); - if (!fe) { - em28xx_errdev("/2: xc3028 attach failed\n"); - dvb_frontend_detach(dev->dvb->fe[0]); - dev->dvb->fe[0] = NULL; - return -EINVAL; - } - - em28xx_info("%s/2: xc3028 attached\n", dev->name); - - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module, - struct em28xx *dev, struct device *device) -{ - int result; - - mutex_init(&dvb->lock); - - /* register adapter */ - result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, - adapter_nr); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", - dev->name, result); - goto fail_adapter; - } - - /* Ensure all frontends negotiate bus access */ - dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; - if (dvb->fe[1]) - dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; - - dvb->adapter.priv = dev; - - /* register frontend */ - result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", - dev->name, result); - goto fail_frontend0; - } - - /* register 2nd frontend */ - if (dvb->fe[1]) { - result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]); - if (result < 0) { - printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n", - dev->name, result); - goto fail_frontend1; - } - } - - /* register demux stuff */ - dvb->demux.dmx.capabilities = - DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - dvb->demux.priv = dvb; - dvb->demux.filternum = 256; - dvb->demux.feednum = 256; - dvb->demux.start_feed = em28xx_start_feed; - dvb->demux.stop_feed = em28xx_stop_feed; - - result = dvb_dmx_init(&dvb->demux); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dev->name, result); - goto fail_dmx; - } - - dvb->dmxdev.filternum = 256; - dvb->dmxdev.demux = &dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", - dev->name, result); - goto fail_dmxdev; - } - - dvb->fe_hw.source = DMX_FRONTEND_0; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", - dev->name, result); - goto fail_fe_hw; - } - - dvb->fe_mem.source = DMX_MEMORY_FE; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); - if (result < 0) { - printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", - dev->name, result); - goto fail_fe_mem; - } - - result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", - dev->name, result); - goto fail_fe_conn; - } - - /* register network adapter */ - dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); - return 0; - -fail_fe_conn: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); -fail_fe_mem: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); -fail_fe_hw: - dvb_dmxdev_release(&dvb->dmxdev); -fail_dmxdev: - dvb_dmx_release(&dvb->demux); -fail_dmx: - if (dvb->fe[1]) - dvb_unregister_frontend(dvb->fe[1]); - dvb_unregister_frontend(dvb->fe[0]); -fail_frontend1: - if (dvb->fe[1]) - dvb_frontend_detach(dvb->fe[1]); -fail_frontend0: - dvb_frontend_detach(dvb->fe[0]); - dvb_unregister_adapter(&dvb->adapter); -fail_adapter: - return result; -} - -static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) -{ - dvb_net_release(&dvb->net); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); - dvb_dmxdev_release(&dvb->dmxdev); - dvb_dmx_release(&dvb->demux); - if (dvb->fe[1]) - dvb_unregister_frontend(dvb->fe[1]); - dvb_unregister_frontend(dvb->fe[0]); - if (dvb->fe[1] && !dvb->dont_attach_fe1) - dvb_frontend_detach(dvb->fe[1]); - dvb_frontend_detach(dvb->fe[0]); - dvb_unregister_adapter(&dvb->adapter); -} - -static int em28xx_dvb_init(struct em28xx *dev) -{ - int result = 0, mfe_shared = 0; - struct em28xx_dvb *dvb; - - if (!dev->board.has_dvb) { - /* This device does not support the extension */ - printk(KERN_INFO "em28xx_dvb: This device does not support the extension\n"); - return 0; - } - - dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); - - if (dvb == NULL) { - em28xx_info("em28xx_dvb: memory allocation failed\n"); - return -ENOMEM; - } - dev->dvb = dvb; - dvb->fe[0] = dvb->fe[1] = NULL; - - mutex_lock(&dev->lock); - em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); - /* init frontend */ - switch (dev->model) { - case EM2874_BOARD_LEADERSHIP_ISDBT: - dvb->fe[0] = dvb_attach(s921_attach, - &sharp_isdbt, &dev->i2c_adap); - - if (!dvb->fe[0]) { - result = -EINVAL; - goto out_free; - } - - break; - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: - case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: - case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - dvb->fe[0] = dvb_attach(lgdt330x_attach, - &em2880_lgdt3303_dev, - &dev->i2c_adap); - if (em28xx_attach_xc3028(0x61, dev) < 0) { - result = -EINVAL; - goto out_free; - } - break; - case EM2880_BOARD_KWORLD_DVB_310U: - dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_with_xc3028, - &dev->i2c_adap); - if (em28xx_attach_xc3028(0x61, dev) < 0) { - result = -EINVAL; - goto out_free; - } - break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2882_BOARD_TERRATEC_HYBRID_XS: - case EM2880_BOARD_EMPIRE_DUAL_TV: - dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_xc3028_no_i2c_gate, - &dev->i2c_adap); - if (em28xx_attach_xc3028(0x61, dev) < 0) { - result = -EINVAL; - goto out_free; - } - break; - case EM2880_BOARD_TERRATEC_HYBRID_XS: - case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: - case EM2881_BOARD_PINNACLE_HYBRID_PRO: - case EM2882_BOARD_DIKOM_DK300: - case EM2882_BOARD_KWORLD_VS_DVBT: - dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_xc3028_no_i2c_gate, - &dev->i2c_adap); - if (dvb->fe[0] == NULL) { - /* This board could have either a zl10353 or a mt352. - If the chip id isn't for zl10353, try mt352 */ - dvb->fe[0] = dvb_attach(mt352_attach, - &terratec_xs_mt352_cfg, - &dev->i2c_adap); - } - - if (em28xx_attach_xc3028(0x61, dev) < 0) { - result = -EINVAL; - goto out_free; - } - break; - case EM2870_BOARD_KWORLD_355U: - dvb->fe[0] = dvb_attach(zl10353_attach, - &em28xx_zl10353_no_i2c_gate_dev, - &dev->i2c_adap); - if (dvb->fe[0] != NULL) - dvb_attach(qt1010_attach, dvb->fe[0], - &dev->i2c_adap, &em28xx_qt1010_config); - break; - case EM2883_BOARD_KWORLD_HYBRID_330U: - case EM2882_BOARD_EVGA_INDTUBE: - dvb->fe[0] = dvb_attach(s5h1409_attach, - &em28xx_s5h1409_with_xc3028, - &dev->i2c_adap); - if (em28xx_attach_xc3028(0x61, dev) < 0) { - result = -EINVAL; - goto out_free; - } - break; - case EM2882_BOARD_KWORLD_ATSC_315U: - dvb->fe[0] = dvb_attach(lgdt330x_attach, - &em2880_lgdt3303_dev, - &dev->i2c_adap); - if (dvb->fe[0] != NULL) { - if (!dvb_attach(simple_tuner_attach, dvb->fe[0], - &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) { - result = -EINVAL; - goto out_free; - } - } - break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: - dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL, - &dev->i2c_adap, &dev->udev->dev); - if (em28xx_attach_xc3028(0x61, dev) < 0) { - result = -EINVAL; - goto out_free; - } - break; - case EM2870_BOARD_REDDO_DVB_C_USB_BOX: - /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ - dvb->fe[0] = dvb_attach(tda10023_attach, - &em28xx_tda10023_config, - &dev->i2c_adap, 0x48); - if (dvb->fe[0]) { - if (!dvb_attach(simple_tuner_attach, dvb->fe[0], - &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) { - result = -EINVAL; - goto out_free; - } - } - break; - case EM2870_BOARD_KWORLD_A340: - dvb->fe[0] = dvb_attach(lgdt3305_attach, - &em2870_lgdt3304_dev, - &dev->i2c_adap); - if (dvb->fe[0] != NULL) - dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, &kworld_a340_config); - break; - case EM28174_BOARD_PCTV_290E: - dvb->fe[0] = dvb_attach(cxd2820r_attach, - &em28xx_cxd2820r_config, - &dev->i2c_adap); - if (dvb->fe[0]) { - /* FE 0 attach tuner */ - if (!dvb_attach(tda18271_attach, - dvb->fe[0], - 0x60, - &dev->i2c_adap, - &em28xx_cxd2820r_tda18271_config)) { - - dvb_frontend_detach(dvb->fe[0]); - result = -EINVAL; - goto out_free; - } - } - break; - case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: - { - struct xc5000_config cfg; - hauppauge_hvr930c_init(dev); - - dvb->fe[0] = dvb_attach(drxk_attach, - &hauppauge_930c_drxk, &dev->i2c_adap); - if (!dvb->fe[0]) { - result = -EINVAL; - goto out_free; - } - /* FIXME: do we need a pll semaphore? */ - dvb->fe[0]->sec_priv = dvb; - sema_init(&dvb->pll_mutex, 1); - dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl; - dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; - - /* Attach xc5000 */ - memset(&cfg, 0, sizeof(cfg)); - cfg.i2c_address = 0x61; - cfg.if_khz = 4000; - - if (dvb->fe[0]->ops.i2c_gate_ctrl) - dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); - if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap, - &cfg)) { - result = -EINVAL; - goto out_free; - } - if (dvb->fe[0]->ops.i2c_gate_ctrl) - dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); - - break; - } - case EM2884_BOARD_TERRATEC_H5: - terratec_h5_init(dev); - - dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap); - if (!dvb->fe[0]) { - result = -EINVAL; - goto out_free; - } - /* FIXME: do we need a pll semaphore? */ - dvb->fe[0]->sec_priv = dvb; - sema_init(&dvb->pll_mutex, 1); - dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl; - dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; - - /* Attach tda18271 to DVB-C frontend */ - if (dvb->fe[0]->ops.i2c_gate_ctrl) - dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); - if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) { - result = -EINVAL; - goto out_free; - } - if (dvb->fe[0]->ops.i2c_gate_ctrl) - dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); - - break; - case EM28174_BOARD_PCTV_460E: - /* attach demod */ - dvb->fe[0] = dvb_attach(tda10071_attach, - &em28xx_tda10071_config, &dev->i2c_adap); - - /* attach SEC */ - if (dvb->fe[0]) - dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap, - &em28xx_a8293_config); - break; - case EM2874_BOARD_MAXMEDIA_UB425_TC: - /* attach demodulator */ - dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk, - &dev->i2c_adap); - - if (dvb->fe[0]) { - /* disable I2C-gate */ - dvb->fe[0]->ops.i2c_gate_ctrl = NULL; - - /* attach tuner */ - if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], - &dev->i2c_adap, 0x60)) { - dvb_frontend_detach(dvb->fe[0]); - result = -EINVAL; - goto out_free; - } - } - - /* TODO: we need drx-3913k firmware in order to support DVB-T */ - em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \ - "driver version\n"); - - break; - case EM2884_BOARD_PCTV_510E: - case EM2884_BOARD_PCTV_520E: - pctv_520e_init(dev); - - /* attach demodulator */ - dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk, - &dev->i2c_adap); - - if (dvb->fe[0]) { - /* attach tuner */ - if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, - &em28xx_cxd2820r_tda18271_config)) { - dvb_frontend_detach(dvb->fe[0]); - result = -EINVAL; - goto out_free; - } - } - break; - case EM2884_BOARD_CINERGY_HTC_STICK: - terratec_htc_stick_init(dev); - - /* attach demodulator */ - dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk, - &dev->i2c_adap); - if (!dvb->fe[0]) { - result = -EINVAL; - goto out_free; - } - - /* Attach the demodulator. */ - if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap, - &em28xx_cxd2820r_tda18271_config)) { - result = -EINVAL; - goto out_free; - } - break; - default: - em28xx_errdev("/2: The frontend of your DVB/ATSC card" - " isn't supported yet\n"); - break; - } - if (NULL == dvb->fe[0]) { - em28xx_errdev("/2: frontend initialization failed\n"); - result = -EINVAL; - goto out_free; - } - /* define general-purpose callback pointer */ - dvb->fe[0]->callback = em28xx_tuner_callback; - if (dvb->fe[1]) - dvb->fe[1]->callback = em28xx_tuner_callback; - - /* register everything */ - result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); - - if (result < 0) - goto out_free; - - /* MFE lock */ - dvb->adapter.mfe_shared = mfe_shared; - - em28xx_info("Successfully loaded em28xx-dvb\n"); -ret: - em28xx_set_mode(dev, EM28XX_SUSPEND); - mutex_unlock(&dev->lock); - return result; - -out_free: - kfree(dvb); - dev->dvb = NULL; - goto ret; -} - -static inline void prevent_sleep(struct dvb_frontend_ops *ops) -{ - ops->set_voltage = NULL; - ops->sleep = NULL; - ops->tuner_ops.sleep = NULL; -} - -static int em28xx_dvb_fini(struct em28xx *dev) -{ - if (!dev->board.has_dvb) { - /* This device does not support the extension */ - return 0; - } - - if (dev->dvb) { - struct em28xx_dvb *dvb = dev->dvb; - - if (dev->state & DEV_DISCONNECTED) { - /* We cannot tell the device to sleep - * once it has been unplugged. */ - if (dvb->fe[0]) - prevent_sleep(&dvb->fe[0]->ops); - if (dvb->fe[1]) - prevent_sleep(&dvb->fe[1]->ops); - } - - em28xx_unregister_dvb(dvb); - kfree(dvb); - dev->dvb = NULL; - } - - return 0; -} - -static struct em28xx_ops dvb_ops = { - .id = EM28XX_DVB, - .name = "Em28xx dvb Extension", - .init = em28xx_dvb_init, - .fini = em28xx_dvb_fini, -}; - -static int __init em28xx_dvb_register(void) -{ - return em28xx_register_extension(&dvb_ops); -} - -static void __exit em28xx_dvb_unregister(void) -{ - em28xx_unregister_extension(&dvb_ops); -} - -module_init(em28xx_dvb_register); -module_exit(em28xx_dvb_unregister); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c deleted file mode 100644 index 1683bd9d51ee..000000000000 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices - - Copyright (C) 2005 Ludovico Cavedon - Markus Rechberger - Mauro Carvalho Chehab - Sascha Sommer - - This program is free software; 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 "em28xx.h" -#include "tuner-xc2028.h" -#include -#include - -/* ----------------------------------------------------------- */ - -static unsigned int i2c_scan; -module_param(i2c_scan, int, 0444); -MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); - -static unsigned int i2c_debug; -module_param(i2c_debug, int, 0644); -MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define dprintk2(lvl, fmt, args...) \ -do { \ - if (i2c_debug >= lvl) { \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __func__ , ##args); \ - } \ -} while (0) - -/* - * em2800_i2c_send_max4() - * send up to 4 bytes to the i2c device - */ -static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, - char *buf, int len) -{ - int ret; - int write_timeout; - unsigned char b2[6]; - BUG_ON(len < 1 || len > 4); - b2[5] = 0x80 + len - 1; - b2[4] = addr; - b2[3] = buf[0]; - if (len > 1) - b2[2] = buf[1]; - if (len > 2) - b2[1] = buf[2]; - if (len > 3) - b2[0] = buf[3]; - - ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); - if (ret != 2 + len) { - em28xx_warn("writing to i2c device failed (error=%i)\n", ret); - return -EIO; - } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { - ret = dev->em28xx_read_reg(dev, 0x05); - if (ret == 0x80 + len - 1) - return len; - msleep(5); - } - em28xx_warn("i2c write timed out\n"); - return -EIO; -} - -/* - * em2800_i2c_send_bytes() - */ -static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len) -{ - char *bufPtr = buf; - int ret; - int wrcount = 0; - int count; - int maxLen = 4; - struct em28xx *dev = (struct em28xx *)data; - while (len > 0) { - count = (len > maxLen) ? maxLen : len; - ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); - if (ret > 0) { - len -= count; - bufPtr += count; - wrcount += count; - } else - return (ret < 0) ? ret : -EFAULT; - } - return wrcount; -} - -/* - * em2800_i2c_check_for_device() - * check if there is a i2c_device at the supplied address - */ -static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) -{ - char msg; - int ret; - int write_timeout; - msg = addr; - ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1); - if (ret < 0) { - em28xx_warn("setting i2c device address failed (error=%i)\n", - ret); - return ret; - } - msg = 0x84; - ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1); - if (ret < 0) { - em28xx_warn("preparing i2c read failed (error=%i)\n", ret); - return ret; - } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { - unsigned reg = dev->em28xx_read_reg(dev, 0x5); - - if (reg == 0x94) - return -ENODEV; - else if (reg == 0x84) - return 0; - msleep(5); - } - return -ENODEV; -} - -/* - * em2800_i2c_recv_bytes() - * read from the i2c device - */ -static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) -{ - int ret; - /* check for the device and set i2c read address */ - ret = em2800_i2c_check_for_device(dev, addr); - if (ret) { - em28xx_warn - ("preparing read at i2c address 0x%x failed (error=%i)\n", - addr, ret); - return ret; - } - ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len); - if (ret < 0) { - em28xx_warn("reading from i2c device at 0x%x failed (error=%i)", - addr, ret); - return ret; - } - return ret; -} - -/* - * em28xx_i2c_send_bytes() - */ -static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len, int stop) -{ - int wrcount = 0; - struct em28xx *dev = (struct em28xx *)data; - int write_timeout, ret; - - wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); - - /* Seems to be required after a write */ - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { - ret = dev->em28xx_read_reg(dev, 0x05); - if (!ret) - break; - msleep(5); - } - - return wrcount; -} - -/* - * em28xx_i2c_recv_bytes() - * read a byte from the i2c device - */ -static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) -{ - int ret; - ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); - if (ret < 0) { - em28xx_warn("reading i2c device failed (error=%i)\n", ret); - return ret; - } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return ret; -} - -/* - * em28xx_i2c_check_for_device() - * check if there is a i2c_device at the supplied address - */ -static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr) -{ - int ret; - - ret = dev->em28xx_read_reg_req(dev, 2, addr); - if (ret < 0) { - em28xx_warn("reading from i2c device failed (error=%i)\n", ret); - return ret; - } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return 0; -} - -/* - * em28xx_i2c_xfer() - * the main i2c transfer function - */ -static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) -{ - struct em28xx *dev = i2c_adap->algo_data; - int addr, rc, i, byte; - - if (num <= 0) - return 0; - for (i = 0; i < num; i++) { - addr = msgs[i].addr << 1; - dprintk2(2, "%s %s addr=%x len=%d:", - (msgs[i].flags & I2C_M_RD) ? "read" : "write", - i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); - if (!msgs[i].len) { /* no len: check only for device presence */ - if (dev->board.is_em2800) - rc = em2800_i2c_check_for_device(dev, addr); - else - rc = em28xx_i2c_check_for_device(dev, addr); - if (rc < 0) { - dprintk2(2, " no device\n"); - return rc; - } - - } else if (msgs[i].flags & I2C_M_RD) { - /* read bytes */ - if (dev->board.is_em2800) - rc = em2800_i2c_recv_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - else - rc = em28xx_i2c_recv_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } - } else { - /* write bytes */ - if (i2c_debug >= 2) { - for (byte = 0; byte < msgs[i].len; byte++) - printk(" %02x", msgs[i].buf[byte]); - } - if (dev->board.is_em2800) - rc = em2800_i2c_send_bytes(dev, addr, - msgs[i].buf, - msgs[i].len); - else - rc = em28xx_i2c_send_bytes(dev, addr, - msgs[i].buf, - msgs[i].len, - i == num - 1); - } - if (rc < 0) - goto err; - if (i2c_debug >= 2) - printk("\n"); - } - - return num; -err: - dprintk2(2, " ERROR: %i\n", rc); - return rc; -} - -/* based on linux/sunrpc/svcauth.h and linux/hash.h - * The original hash function returns a different value, if arch is x86_64 - * or i386. - */ -static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits) -{ - unsigned long hash = 0; - unsigned long l = 0; - int len = 0; - unsigned char c; - do { - if (len == length) { - c = (char)len; - len = -1; - } else - c = *buf++; - l = (l << 8) | c; - len++; - if ((len & (32 / 8 - 1)) == 0) - hash = ((hash^l) * 0x9e370001UL); - } while (len); - - return (hash >> (32 - bits)) & 0xffffffffUL; -} - -static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) -{ - unsigned char buf, *p = eedata; - struct em28xx_eeprom *em_eeprom = (void *)eedata; - int i, err, size = len, block; - - if (dev->chip_id == CHIP_ID_EM2874 || - dev->chip_id == CHIP_ID_EM28174 || - dev->chip_id == CHIP_ID_EM2884) { - /* Empia switched to a 16-bit addressable eeprom in newer - devices. While we could certainly write a routine to read - the eeprom, there is nothing of use in there that cannot be - accessed through registers, and there is the risk that we - could corrupt the eeprom (since a 16-bit read call is - interpreted as a write call by 8-bit eeproms). - */ - return 0; - } - - dev->i2c_client.addr = 0xa0 >> 1; - - /* Check if board has eeprom */ - err = i2c_master_recv(&dev->i2c_client, &buf, 0); - if (err < 0) { - em28xx_errdev("board has no eeprom\n"); - memset(eedata, 0, len); - return -ENODEV; - } - - buf = 0; - - err = i2c_master_send(&dev->i2c_client, &buf, 1); - if (err != 1) { - printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", - dev->name, err); - return err; - } - while (size > 0) { - if (size > 16) - block = 16; - else - block = size; - - if (block != - (err = i2c_master_recv(&dev->i2c_client, p, block))) { - printk(KERN_WARNING - "%s: i2c eeprom read error (err=%d)\n", - dev->name, err); - return err; - } - size -= block; - p += block; - } - for (i = 0; i < len; i++) { - if (0 == (i % 16)) - printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); - printk(" %02x", eedata[i]); - if (15 == (i % 16)) - printk("\n"); - } - - if (em_eeprom->id == 0x9567eb1a) - dev->hash = em28xx_hash_mem(eedata, len, 32); - - printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n", - dev->name, em_eeprom->id, dev->hash); - - printk(KERN_INFO "%s: EEPROM info:\n", dev->name); - - switch (em_eeprom->chip_conf >> 4 & 0x3) { - case 0: - printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name); - break; - case 1: - printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n", - dev->name); - break; - case 2: - printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", - dev->name); - break; - case 3: - printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", - dev->name); - break; - } - - if (em_eeprom->chip_conf & 1 << 3) - printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name); - - if (em_eeprom->chip_conf & 1 << 2) - printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name); - - switch (em_eeprom->chip_conf & 0x3) { - case 0: - printk(KERN_INFO "%s:\t500mA max power\n", dev->name); - break; - case 1: - printk(KERN_INFO "%s:\t400mA max power\n", dev->name); - break; - case 2: - printk(KERN_INFO "%s:\t300mA max power\n", dev->name); - break; - case 3: - printk(KERN_INFO "%s:\t200mA max power\n", dev->name); - break; - } - printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", - dev->name, - em_eeprom->string_idx_table, - em_eeprom->string1, - em_eeprom->string2, - em_eeprom->string3); - - return 0; -} - -/* ----------------------------------------------------------- */ - -/* - * functionality() - */ -static u32 functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static struct i2c_algorithm em28xx_algo = { - .master_xfer = em28xx_i2c_xfer, - .functionality = functionality, -}; - -static struct i2c_adapter em28xx_adap_template = { - .owner = THIS_MODULE, - .name = "em28xx", - .algo = &em28xx_algo, -}; - -static struct i2c_client em28xx_client_template = { - .name = "em28xx internal", -}; - -/* ----------------------------------------------------------- */ - -/* - * i2c_devs - * incomplete list of known devices - */ -static char *i2c_devs[128] = { - [0x4a >> 1] = "saa7113h", - [0x52 >> 1] = "drxk", - [0x60 >> 1] = "remote IR sensor", - [0x8e >> 1] = "remote IR sensor", - [0x86 >> 1] = "tda9887", - [0x80 >> 1] = "msp34xx", - [0x88 >> 1] = "msp34xx", - [0xa0 >> 1] = "eeprom", - [0xb0 >> 1] = "tda9874", - [0xb8 >> 1] = "tvp5150a", - [0xba >> 1] = "webcam sensor or tvp5150a", - [0xc0 >> 1] = "tuner (analog)", - [0xc2 >> 1] = "tuner (analog)", - [0xc4 >> 1] = "tuner (analog)", - [0xc6 >> 1] = "tuner (analog)", -}; - -/* - * do_i2c_scan() - * check i2c address range for devices - */ -void em28xx_do_i2c_scan(struct em28xx *dev) -{ - u8 i2c_devicelist[128]; - unsigned char buf; - int i, rc; - - memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist)); - - for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { - dev->i2c_client.addr = i; - rc = i2c_master_recv(&dev->i2c_client, &buf, 0); - if (rc < 0) - continue; - i2c_devicelist[i] = i; - printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n", - dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); - } - - dev->i2c_hash = em28xx_hash_mem(i2c_devicelist, - ARRAY_SIZE(i2c_devicelist), 32); -} - -/* - * em28xx_i2c_register() - * register i2c bus - */ -int em28xx_i2c_register(struct em28xx *dev) -{ - int retval; - - BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg); - BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req); - dev->i2c_adap = em28xx_adap_template; - dev->i2c_adap.dev.parent = &dev->udev->dev; - strcpy(dev->i2c_adap.name, dev->name); - dev->i2c_adap.algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); - - retval = i2c_add_adapter(&dev->i2c_adap); - if (retval < 0) { - em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n", - __func__, retval); - return retval; - } - - dev->i2c_client = em28xx_client_template; - dev->i2c_client.adapter = &dev->i2c_adap; - - retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); - if ((retval < 0) && (retval != -ENODEV)) { - em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", - __func__, retval); - - return retval; - } - - if (i2c_scan) - em28xx_do_i2c_scan(dev); - - return 0; -} - -/* - * em28xx_i2c_unregister() - * unregister i2c_bus - */ -int em28xx_i2c_unregister(struct em28xx *dev) -{ - i2c_del_adapter(&dev->i2c_adap); - return 0; -} diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c deleted file mode 100644 index 97d36b4f19db..000000000000 --- a/drivers/media/video/em28xx/em28xx-input.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - handle em28xx IR remotes via linux kernel input layer. - - Copyright (C) 2005 Ludovico Cavedon - Markus Rechberger - Mauro Carvalho Chehab - Sascha Sommer - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include - -#include "em28xx.h" - -#define EM28XX_SNAPSHOT_KEY KEY_CAMERA -#define EM28XX_SBUTTON_QUERY_INTERVAL 500 -#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20 - -static unsigned int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); - -#define MODULE_NAME "em28xx" - -#define i2cdprintk(fmt, arg...) \ - if (ir_debug) { \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ - } - -#define dprintk(fmt, arg...) \ - if (ir_debug) { \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ - } - -/********************************************************** - Polling structure used by em28xx IR's - **********************************************************/ - -struct em28xx_ir_poll_result { - unsigned int toggle_bit:1; - unsigned int read_count:7; - u8 rc_address; - u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ -}; - -struct em28xx_IR { - struct em28xx *dev; - struct rc_dev *rc; - char name[32]; - char phys[32]; - - /* poll external decoder */ - int polling; - struct delayed_work work; - unsigned int full_code:1; - unsigned int last_readcount; - - int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); -}; - -/********************************************************** - I2C IR based get keycodes - should be used with ir-kbd-i2c - **********************************************************/ - -static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - unsigned char b; - - /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { - i2cdprintk("read error\n"); - return -EIO; - } - - /* it seems that 0xFE indicates that a button is still hold - down, while 0xff indicates that no button is hold - down. 0xfe sequences are sometimes interrupted by 0xFF */ - - i2cdprintk("key %02x\n", b); - - if (b == 0xff) - return 0; - - if (b == 0xfe) - /* keep old data */ - return 1; - - *ir_key = b; - *ir_raw = b; - return 1; -} - -static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - unsigned char buf[2]; - u16 code; - int size; - - /* poll IR chip */ - size = i2c_master_recv(ir->c, buf, sizeof(buf)); - - if (size != 2) - return -EIO; - - /* Does eliminate repeated parity code */ - if (buf[1] == 0xff) - return 0; - - ir->old = buf[1]; - - /* - * Rearranges bits to the right order. - * The bit order were determined experimentally by using - * The original Hauppauge Grey IR and another RC5 that uses addr=0x08 - * The RC5 code has 14 bits, but we've experimentally determined - * the meaning for only 11 bits. - * So, the code translation is not complete. Yet, it is enough to - * work with the provided RC5 IR. - */ - code = - ((buf[0] & 0x01) ? 0x0020 : 0) | /* 0010 0000 */ - ((buf[0] & 0x02) ? 0x0010 : 0) | /* 0001 0000 */ - ((buf[0] & 0x04) ? 0x0008 : 0) | /* 0000 1000 */ - ((buf[0] & 0x08) ? 0x0004 : 0) | /* 0000 0100 */ - ((buf[0] & 0x10) ? 0x0002 : 0) | /* 0000 0010 */ - ((buf[0] & 0x20) ? 0x0001 : 0) | /* 0000 0001 */ - ((buf[1] & 0x08) ? 0x1000 : 0) | /* 0001 0000 */ - ((buf[1] & 0x10) ? 0x0800 : 0) | /* 0000 1000 */ - ((buf[1] & 0x20) ? 0x0400 : 0) | /* 0000 0100 */ - ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */ - ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */ - - i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x%02x)\n", - code, buf[1], buf[0]); - - /* return key */ - *ir_key = code; - *ir_raw = code; - return 1; -} - -static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) -{ - unsigned char buf[3]; - - /* poll IR chip */ - - if (3 != i2c_master_recv(ir->c, buf, 3)) { - i2cdprintk("read error\n"); - return -EIO; - } - - i2cdprintk("key %02x\n", buf[2]&0x3f); - if (buf[0] != 0x00) - return 0; - - *ir_key = buf[2]&0x3f; - *ir_raw = buf[2]&0x3f; - - return 1; -} - -static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) -{ - unsigned char subaddr, keydetect, key; - - struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1}, - - { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; - - subaddr = 0x10; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - i2cdprintk("read error\n"); - return -EIO; - } - if (keydetect == 0x00) - return 0; - - subaddr = 0x00; - msg[1].buf = &key; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - i2cdprintk("read error\n"); - return -EIO; - } - if (key == 0x00) - return 0; - - *ir_key = key; - *ir_raw = key; - return 1; -} - -/********************************************************** - Poll based get keycode functions - **********************************************************/ - -/* This is for the em2860/em2880 */ -static int default_polling_getkey(struct em28xx_IR *ir, - struct em28xx_ir_poll_result *poll_result) -{ - struct em28xx *dev = ir->dev; - int rc; - u8 msg[3] = { 0, 0, 0 }; - - /* Read key toggle, brand, and key code - on registers 0x45, 0x46 and 0x47 - */ - rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR, - msg, sizeof(msg)); - if (rc < 0) - return rc; - - /* Infrared toggle (Reg 0x45[7]) */ - poll_result->toggle_bit = (msg[0] >> 7); - - /* Infrared read count (Reg 0x45[6:0] */ - poll_result->read_count = (msg[0] & 0x7f); - - /* Remote Control Address (Reg 0x46) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x47) */ - poll_result->rc_data[0] = msg[2]; - - return 0; -} - -static int em2874_polling_getkey(struct em28xx_IR *ir, - struct em28xx_ir_poll_result *poll_result) -{ - struct em28xx *dev = ir->dev; - int rc; - u8 msg[5] = { 0, 0, 0, 0, 0 }; - - /* Read key toggle, brand, and key code - on registers 0x51-55 - */ - rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR, - msg, sizeof(msg)); - if (rc < 0) - return rc; - - /* Infrared toggle (Reg 0x51[7]) */ - poll_result->toggle_bit = (msg[0] >> 7); - - /* Infrared read count (Reg 0x51[6:0] */ - poll_result->read_count = (msg[0] & 0x7f); - - /* Remote Control Address (Reg 0x52) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x53-55) */ - poll_result->rc_data[0] = msg[2]; - poll_result->rc_data[1] = msg[3]; - poll_result->rc_data[2] = msg[4]; - - return 0; -} - -/********************************************************** - Polling code for em28xx - **********************************************************/ - -static void em28xx_ir_handle_key(struct em28xx_IR *ir) -{ - int result; - struct em28xx_ir_poll_result poll_result; - - /* read the registers containing the IR status */ - result = ir->get_key(ir, &poll_result); - if (unlikely(result < 0)) { - dprintk("ir->get_key() failed %d\n", result); - return; - } - - if (unlikely(poll_result.read_count != ir->last_readcount)) { - dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, - poll_result.toggle_bit, poll_result.read_count, - poll_result.rc_address, poll_result.rc_data[0]); - if (ir->full_code) - rc_keydown(ir->rc, - poll_result.rc_address << 8 | - poll_result.rc_data[0], - poll_result.toggle_bit); - else - rc_keydown(ir->rc, - poll_result.rc_data[0], - poll_result.toggle_bit); - - if (ir->dev->chip_id == CHIP_ID_EM2874 || - ir->dev->chip_id == CHIP_ID_EM2884) - /* The em2874 clears the readcount field every time the - register is read. The em2860/2880 datasheet says that it - is supposed to clear the readcount, but it doesn't. So with - the em2874, we are looking for a non-zero read count as - opposed to a readcount that is incrementing */ - ir->last_readcount = 0; - else - ir->last_readcount = poll_result.read_count; - } -} - -static void em28xx_ir_work(struct work_struct *work) -{ - struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); - - em28xx_ir_handle_key(ir); - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); -} - -static int em28xx_ir_start(struct rc_dev *rc) -{ - struct em28xx_IR *ir = rc->priv; - - INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); - schedule_delayed_work(&ir->work, 0); - - return 0; -} - -static void em28xx_ir_stop(struct rc_dev *rc) -{ - struct em28xx_IR *ir = rc->priv; - - cancel_delayed_work_sync(&ir->work); -} - -static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) -{ - int rc = 0; - struct em28xx_IR *ir = rc_dev->priv; - struct em28xx *dev = ir->dev; - u8 ir_config = EM2874_IR_RC5; - - /* Adjust xclk based o IR table for RC5/NEC tables */ - - if (rc_type == RC_TYPE_RC5) { - dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; - ir->full_code = 1; - } else if (rc_type == RC_TYPE_NEC) { - dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; - ir_config = EM2874_IR_NEC; - ir->full_code = 1; - } else if (rc_type != RC_TYPE_UNKNOWN) - rc = -EINVAL; - - em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, - EM28XX_XCLK_IR_RC5_MODE); - - /* Setup the proper handler based on the chip */ - switch (dev->chip_id) { - case CHIP_ID_EM2860: - case CHIP_ID_EM2883: - ir->get_key = default_polling_getkey; - break; - case CHIP_ID_EM2884: - case CHIP_ID_EM2874: - case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); - break; - default: - printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", - dev->chip_id); - rc = -EINVAL; - } - - return rc; -} - -static void em28xx_register_i2c_ir(struct em28xx *dev) -{ - /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ - /* at address 0x18, so if that address is needed for another board in */ - /* the future, please put it after 0x1f. */ - struct i2c_board_info info; - const unsigned short addr_list[] = { - 0x1f, 0x30, 0x47, I2C_CLIENT_END - }; - - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; - break; - } - - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); -} - -/********************************************************** - Handle Webcam snapshot button - **********************************************************/ - -static void em28xx_query_sbutton(struct work_struct *work) -{ - /* Poll the register and see if the button is depressed */ - struct em28xx *dev = - container_of(work, struct em28xx, sbutton_query_work.work); - int ret; - - ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); - - if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { - u8 cleared; - /* Button is depressed, clear the register */ - cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; - em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); - - /* Not emulate the keypress */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 1); - /* Now unpress the key */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 0); - } - - /* Schedule next poll */ - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); -} - -static void em28xx_register_snapshot_button(struct em28xx *dev) -{ - struct input_dev *input_dev; - int err; - - em28xx_info("Registering snapshot button...\n"); - input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); - return; - } - - usb_make_path(dev->udev, dev->snapshot_button_path, - sizeof(dev->snapshot_button_path)); - strlcat(dev->snapshot_button_path, "/sbutton", - sizeof(dev->snapshot_button_path)); - INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); - - input_dev->name = "em28xx snapshot button"; - input_dev->phys = dev->snapshot_button_path; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); - input_dev->keycodesize = 0; - input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - input_dev->id.version = 1; - input_dev->dev.parent = &dev->udev->dev; - - err = input_register_device(input_dev); - if (err) { - em28xx_errdev("input_register_device failed\n"); - input_free_device(input_dev); - return; - } - - dev->sbutton_input_dev = input_dev; - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); - return; - -} - -static void em28xx_deregister_snapshot_button(struct em28xx *dev) -{ - if (dev->sbutton_input_dev != NULL) { - em28xx_info("Deregistering snapshot button\n"); - cancel_delayed_work_sync(&dev->sbutton_query_work); - input_unregister_device(dev->sbutton_input_dev); - dev->sbutton_input_dev = NULL; - } - return; -} - -static int em28xx_ir_init(struct em28xx *dev) -{ - struct em28xx_IR *ir; - struct rc_dev *rc; - int err = -ENOMEM; - - if (dev->board.ir_codes == NULL) { - /* No remote control support */ - em28xx_warn("Remote control support is not available for " - "this card.\n"); - return 0; - } - - ir = kzalloc(sizeof(*ir), GFP_KERNEL); - rc = rc_allocate_device(); - if (!ir || !rc) - goto err_out_free; - - /* record handles to ourself */ - ir->dev = dev; - dev->ir = ir; - ir->rc = rc; - - /* - * em2874 supports more protocols. For now, let's just announce - * the two protocols that were already tested - */ - rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC; - rc->priv = ir; - rc->change_protocol = em28xx_ir_change_protocol; - rc->open = em28xx_ir_start; - rc->close = em28xx_ir_stop; - - /* By default, keep protocol field untouched */ - err = em28xx_ir_change_protocol(rc, RC_TYPE_UNKNOWN); - if (err) - goto err_out_free; - - /* This is how often we ask the chip for IR information */ - ir->polling = 100; /* ms */ - - /* init input device */ - snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", - dev->name); - - usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); - strlcat(ir->phys, "/input0", sizeof(ir->phys)); - - rc->input_name = ir->name; - rc->input_phys = ir->phys; - rc->input_id.bustype = BUS_USB; - rc->input_id.version = 1; - rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - rc->dev.parent = &dev->udev->dev; - rc->map_name = dev->board.ir_codes; - rc->driver_name = MODULE_NAME; - - /* all done */ - err = rc_register_device(rc); - if (err) - goto err_out_stop; - - em28xx_register_i2c_ir(dev); - -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); - - return 0; - - err_out_stop: - dev->ir = NULL; - err_out_free: - rc_free_device(rc); - kfree(ir); - return err; -} - -static int em28xx_ir_fini(struct em28xx *dev) -{ - struct em28xx_IR *ir = dev->ir; - - em28xx_deregister_snapshot_button(dev); - - /* skip detach on non attached boards */ - if (!ir) - return 0; - - if (ir->rc) - rc_unregister_device(ir->rc); - - /* done */ - kfree(ir); - dev->ir = NULL; - return 0; -} - -static struct em28xx_ops rc_ops = { - .id = EM28XX_RC, - .name = "Em28xx Input Extension", - .init = em28xx_ir_init, - .fini = em28xx_ir_fini, -}; - -static int __init em28xx_rc_register(void) -{ - return em28xx_register_extension(&rc_ops); -} - -static void __exit em28xx_rc_unregister(void) -{ - em28xx_unregister_extension(&rc_ops); -} - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_DESCRIPTION("Em28xx Input driver"); - -module_init(em28xx_rc_register); -module_exit(em28xx_rc_unregister); diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h deleted file mode 100644 index 6ff368297f6e..000000000000 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ /dev/null @@ -1,226 +0,0 @@ -#define EM_GPIO_0 (1 << 0) -#define EM_GPIO_1 (1 << 1) -#define EM_GPIO_2 (1 << 2) -#define EM_GPIO_3 (1 << 3) -#define EM_GPIO_4 (1 << 4) -#define EM_GPIO_5 (1 << 5) -#define EM_GPIO_6 (1 << 6) -#define EM_GPIO_7 (1 << 7) - -#define EM_GPO_0 (1 << 0) -#define EM_GPO_1 (1 << 1) -#define EM_GPO_2 (1 << 2) -#define EM_GPO_3 (1 << 3) - -/* em28xx endpoints */ -#define EM28XX_EP_ANALOG 0x82 -#define EM28XX_EP_AUDIO 0x83 -#define EM28XX_EP_DIGITAL 0x84 - -/* em2800 registers */ -#define EM2800_R08_AUDIOSRC 0x08 - -/* em28xx registers */ - -#define EM28XX_R00_CHIPCFG 0x00 - -/* em28xx Chip Configuration 0x00 */ -#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80 -#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40 -#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30 -#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20 -#define EM28XX_CHIPCFG_AC97 0x10 -#define EM28XX_CHIPCFG_AUDIOMASK 0x30 - -#define EM28XX_R01_CHIPCFG2 0x01 - -/* em28xx Chip Configuration 2 0x01 */ -#define EM28XX_CHIPCFG2_TS_PRESENT 0x10 -#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_MASK 0x0c /* bits 3-2 */ -#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_1MF 0x00 -#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_2MF 0x04 -#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_4MF 0x08 -#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_8MF 0x0c -#define EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK 0x03 /* bits 0-1 */ -#define EM28XX_CHIPCFG2_TS_PACKETSIZE_188 0x00 -#define EM28XX_CHIPCFG2_TS_PACKETSIZE_376 0x01 -#define EM28XX_CHIPCFG2_TS_PACKETSIZE_564 0x02 -#define EM28XX_CHIPCFG2_TS_PACKETSIZE_752 0x03 - - - /* GPIO/GPO registers */ -#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ -#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ - -#define EM28XX_R06_I2C_CLK 0x06 - -/* em28xx I2C Clock Register (0x06) */ -#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80 -#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40 -#define EM28XX_I2C_EEPROM_ON_BOARD 0x08 -#define EM28XX_I2C_EEPROM_KEY_VALID 0x04 -#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */ -#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */ -#define EM28XX_I2C_FREQ_25_KHZ 0x02 -#define EM28XX_I2C_FREQ_400_KHZ 0x01 -#define EM28XX_I2C_FREQ_100_KHZ 0x00 - - -#define EM28XX_R0A_CHIPID 0x0a -#define EM28XX_R0C_USBSUSP 0x0c /* */ - -#define EM28XX_R0E_AUDIOSRC 0x0e -#define EM28XX_R0F_XCLK 0x0f - -/* em28xx XCLK Register (0x0f) */ -#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */ -#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */ -#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */ -#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10 -#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */ -#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01 -#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02 -#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03 -#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04 -#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05 -#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06 -#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07 -#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08 -#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09 -#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a -#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b - -#define EM28XX_R10_VINMODE 0x10 - -#define EM28XX_R11_VINCTRL 0x11 - -/* em28xx Video Input Control Register 0x11 */ -#define EM28XX_VINCTRL_VBI_SLICED 0x80 -#define EM28XX_VINCTRL_VBI_RAW 0x40 -#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */ -#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10 -#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */ -#define EM28XX_VINCTRL_FID_ON_HREF 0x04 -#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02 -#define EM28XX_VINCTRL_INTERLACED 0x01 - -#define EM28XX_R12_VINENABLE 0x12 /* */ - -#define EM28XX_R14_GAMMA 0x14 -#define EM28XX_R15_RGAIN 0x15 -#define EM28XX_R16_GGAIN 0x16 -#define EM28XX_R17_BGAIN 0x17 -#define EM28XX_R18_ROFFSET 0x18 -#define EM28XX_R19_GOFFSET 0x19 -#define EM28XX_R1A_BOFFSET 0x1a - -#define EM28XX_R1B_OFLOW 0x1b -#define EM28XX_R1C_HSTART 0x1c -#define EM28XX_R1D_VSTART 0x1d -#define EM28XX_R1E_CWIDTH 0x1e -#define EM28XX_R1F_CHEIGHT 0x1f - -#define EM28XX_R20_YGAIN 0x20 -#define EM28XX_R21_YOFFSET 0x21 -#define EM28XX_R22_UVGAIN 0x22 -#define EM28XX_R23_UOFFSET 0x23 -#define EM28XX_R24_VOFFSET 0x24 -#define EM28XX_R25_SHARPNESS 0x25 - -#define EM28XX_R26_COMPR 0x26 -#define EM28XX_R27_OUTFMT 0x27 - -/* em28xx Output Format Register (0x27) */ -#define EM28XX_OUTFMT_RGB_8_RGRG 0x00 -#define EM28XX_OUTFMT_RGB_8_GRGR 0x01 -#define EM28XX_OUTFMT_RGB_8_GBGB 0x02 -#define EM28XX_OUTFMT_RGB_8_BGBG 0x03 -#define EM28XX_OUTFMT_RGB_16_656 0x04 -#define EM28XX_OUTFMT_RGB_8_BAYER 0x08 /* Pattern in Reg 0x10[1-0] */ -#define EM28XX_OUTFMT_YUV211 0x10 -#define EM28XX_OUTFMT_YUV422_Y0UY1V 0x14 -#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15 -#define EM28XX_OUTFMT_YUV411 0x18 - - -#define EM28XX_R28_XMIN 0x28 -#define EM28XX_R29_XMAX 0x29 -#define EM28XX_R2A_YMIN 0x2a -#define EM28XX_R2B_YMAX 0x2b - -#define EM28XX_R30_HSCALELOW 0x30 -#define EM28XX_R31_HSCALEHIGH 0x31 -#define EM28XX_R32_VSCALELOW 0x32 -#define EM28XX_R33_VSCALEHIGH 0x33 -#define EM28XX_R34_VBI_START_H 0x34 -#define EM28XX_R35_VBI_START_V 0x35 -#define EM28XX_R36_VBI_WIDTH 0x36 -#define EM28XX_R37_VBI_HEIGHT 0x37 - -#define EM28XX_R40_AC97LSB 0x40 -#define EM28XX_R41_AC97MSB 0x41 -#define EM28XX_R42_AC97ADDR 0x42 -#define EM28XX_R43_AC97BUSY 0x43 - -#define EM28XX_R45_IR 0x45 - /* 0x45 bit 7 - parity bit - bits 6-0 - count - 0x46 IR brand - 0x47 IR data - */ - -/* em2874 registers */ -#define EM2874_R50_IR_CONFIG 0x50 -#define EM2874_R51_IR 0x51 -#define EM2874_R5F_TS_ENABLE 0x5f -#define EM2874_R80_GPIO 0x80 - -/* em2874 IR config register (0x50) */ -#define EM2874_IR_NEC 0x00 -#define EM2874_IR_RC5 0x04 -#define EM2874_IR_RC6_MODE_0 0x08 -#define EM2874_IR_RC6_MODE_6A 0x0b - -/* em2874 Transport Stream Enable Register (0x5f) */ -#define EM2874_TS1_CAPTURE_ENABLE (1 << 0) -#define EM2874_TS1_FILTER_ENABLE (1 << 1) -#define EM2874_TS1_NULL_DISCARD (1 << 2) -#define EM2874_TS2_CAPTURE_ENABLE (1 << 4) -#define EM2874_TS2_FILTER_ENABLE (1 << 5) -#define EM2874_TS2_NULL_DISCARD (1 << 6) - -/* register settings */ -#define EM2800_AUDIO_SRC_TUNER 0x0d -#define EM2800_AUDIO_SRC_LINE 0x0c -#define EM28XX_AUDIO_SRC_TUNER 0xc0 -#define EM28XX_AUDIO_SRC_LINE 0x80 - -/* FIXME: Need to be populated with the other chip ID's */ -enum em28xx_chip_id { - CHIP_ID_EM2800 = 7, - CHIP_ID_EM2710 = 17, - CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ - CHIP_ID_EM2840 = 20, - CHIP_ID_EM2750 = 33, - CHIP_ID_EM2860 = 34, - CHIP_ID_EM2870 = 35, - CHIP_ID_EM2883 = 36, - CHIP_ID_EM2874 = 65, - CHIP_ID_EM2884 = 68, - CHIP_ID_EM28174 = 113, -}; - -/* - * Registers used by em202 - */ - -/* EMP202 vendor registers */ -#define EM202_EXT_MODEM_CTRL 0x3e -#define EM202_GPIO_CONF 0x4c -#define EM202_GPIO_POLARITY 0x4e -#define EM202_GPIO_STICKY 0x50 -#define EM202_GPIO_MASK 0x52 -#define EM202_GPIO_STATUS 0x54 -#define EM202_SPDIF_OUT_SEL 0x6a -#define EM202_ANTIPOP 0x72 -#define EM202_EAPD_GPIO_ACCESS 0x74 diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c deleted file mode 100644 index 2b4c9cba2d67..000000000000 --- a/drivers/media/video/em28xx/em28xx-vbi.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - em28xx-vbi.c - VBI driver for em28xx - - Copyright (C) 2009 Devin Heitmueller - - This work was sponsored by EyeMagnet Limited. - - This program is free software; 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 -#include -#include -#include - -#include "em28xx.h" - -static unsigned int vbibufs = 5; -module_param(vbibufs, int, 0644); -MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); - -static unsigned int vbi_debug; -module_param(vbi_debug, int, 0644); -MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); - -#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) - -/* ------------------------------------------------------------------ */ - -static void -free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) -{ - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vbi_buf == buf) - dev->isoc_ctl.vbi_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; - - *size = dev->vbi_width * dev->vbi_height * 2; - - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; - return 0; -} - -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - int rc = 0; - - buf->vb.size = dev->vbi_width * dev->vbi_height * 2; - - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - buf->vb.width = dev->vbi_width; - buf->vb.height = dev->vbi_height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(q, buf); - return rc; -} - -static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vbiq = &dev->vbiq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vbiq->active); -} - -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - free_buffer(q, buf); -} - -struct videobuf_queue_ops em28xx_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, -}; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c deleted file mode 100644 index ecb23df7f16e..000000000000 --- a/drivers/media/video/em28xx/em28xx-video.c +++ /dev/null @@ -1,2624 +0,0 @@ -/* - em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB - video capture devices - - Copyright (C) 2005 Ludovico Cavedon - Markus Rechberger - Mauro Carvalho Chehab - Sascha Sommer - - Some parts based on SN9C10x PC Camera Controllers GPL driver made - by Luca Risolia - - This program is free software; 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 -#include - -#include "em28xx.h" -#include -#include -#include -#include -#include - -#define DRIVER_AUTHOR "Ludovico Cavedon , " \ - "Markus Rechberger , " \ - "Mauro Carvalho Chehab , " \ - "Sascha Sommer " - -#define DRIVER_DESC "Empia em28xx based USB video device driver" - -#define EM28XX_VERSION "0.1.3" - -#define em28xx_videodbg(fmt, arg...) do {\ - if (video_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -static unsigned int isoc_debug; -module_param(isoc_debug, int, 0644); -MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); - -#define em28xx_isocdbg(fmt, arg...) \ -do {\ - if (isoc_debug) { \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); \ - } \ - } while (0) - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(EM28XX_VERSION); - -static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; - -module_param_array(video_nr, int, NULL, 0444); -module_param_array(vbi_nr, int, NULL, 0444); -module_param_array(radio_nr, int, NULL, 0444); -MODULE_PARM_DESC(video_nr, "video device numbers"); -MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); -MODULE_PARM_DESC(radio_nr, "radio device numbers"); - -static unsigned int video_debug; -module_param(video_debug, int, 0644); -MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); - -/* supported video standards */ -static struct em28xx_fmt format[] = { - { - .name = "16 bpp YUY2, 4:2:2, packed", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .reg = EM28XX_OUTFMT_YUV422_Y0UY1V, - }, { - .name = "16 bpp RGB 565, LE", - .fourcc = V4L2_PIX_FMT_RGB565, - .depth = 16, - .reg = EM28XX_OUTFMT_RGB_16_656, - }, { - .name = "8 bpp Bayer BGBG..GRGR", - .fourcc = V4L2_PIX_FMT_SBGGR8, - .depth = 8, - .reg = EM28XX_OUTFMT_RGB_8_BGBG, - }, { - .name = "8 bpp Bayer GRGR..BGBG", - .fourcc = V4L2_PIX_FMT_SGRBG8, - .depth = 8, - .reg = EM28XX_OUTFMT_RGB_8_GRGR, - }, { - .name = "8 bpp Bayer GBGB..RGRG", - .fourcc = V4L2_PIX_FMT_SGBRG8, - .depth = 8, - .reg = EM28XX_OUTFMT_RGB_8_GBGB, - }, { - .name = "12 bpp YUV411", - .fourcc = V4L2_PIX_FMT_YUV411P, - .depth = 12, - .reg = EM28XX_OUTFMT_YUV411, - }, -}; - -/* supported controls */ -/* Common to all boards */ -static struct v4l2_queryctrl ac97_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0x0, - .maximum = 0x1f, - .step = 0x1, - .default_value = 0x1f, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - } -}; - -/* ------------------------------------------------------------------ - DMA and thread functions - ------------------------------------------------------------------*/ - -/* - * Announces that a buffer were filled and request the next - */ -static inline void buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) -{ - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->isoc_ctl.vid_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -static inline void vbi_buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) -{ - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - dev->isoc_ctl.vbi_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -/* - * Identify the buffer header type and properly handles - */ -static void em28xx_copy_video(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) -{ - void *fieldstart, *startwrite, *startread; - int linesdone, currlinedone, offset, lencopy, remain; - int bytesperline = dev->width << 1; - - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; - - startread = p; - remain = len; - - if (dev->progressive) - fieldstart = outp; - else { - /* Interlaces two half frames */ - if (buf->top_field) - fieldstart = outp; - else - fieldstart = outp + bytesperline; - } - - linesdone = dma_q->pos / bytesperline; - currlinedone = dma_q->pos % bytesperline; - - if (dev->progressive) - offset = linesdone * bytesperline + currlinedone; - else - offset = linesdone * bytesperline * 2 + currlinedone; - - startwrite = fieldstart + offset; - lencopy = bytesperline - currlinedone; - lencopy = lencopy > remain ? remain : lencopy; - - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; - lencopy = remain; - } - if (lencopy <= 0) - return; - memcpy(startwrite, startread, lencopy); - - remain -= lencopy; - - while (remain > 0) { - startwrite += lencopy + bytesperline; - startread += lencopy; - if (bytesperline > remain) - lencopy = remain; - else - lencopy = bytesperline; - - if ((char *)startwrite + lencopy > (char *)outp + - buf->vb.size) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end" - "(2)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - - (char *)startwrite; - } - if (lencopy <= 0) - break; - - memcpy(startwrite, startread, lencopy); - - remain -= lencopy; - } - - dma_q->pos += len; -} - -static void em28xx_copy_vbi(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) -{ - void *startwrite, *startread; - int offset; - int bytesperline; - - if (dev == NULL) { - em28xx_isocdbg("dev is null\n"); - return; - } - bytesperline = dev->vbi_width; - - if (dma_q == NULL) { - em28xx_isocdbg("dma_q is null\n"); - return; - } - if (buf == NULL) { - return; - } - if (p == NULL) { - em28xx_isocdbg("p is null\n"); - return; - } - if (outp == NULL) { - em28xx_isocdbg("outp is null\n"); - return; - } - - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; - - startread = p; - - startwrite = outp + dma_q->pos; - offset = dma_q->pos; - - /* Make sure the bottom field populates the second half of the frame */ - if (buf->top_field == 0) { - startwrite += bytesperline * dev->vbi_height; - offset += bytesperline * dev->vbi_height; - } - - memcpy(startwrite, startread, len); - dma_q->pos += len; -} - -static inline void print_err_status(struct em28xx *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - em28xx_isocdbg("URB status %d [%s].\n", status, errmsg); - } else { - em28xx_isocdbg("URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -/* - * video-buf generic routine to get the next available buffer - */ -static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); - char *outp; - - if (list_empty(&dma_q->active)) { - em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vid_buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); - - dev->isoc_ctl.vid_buf = *buf; - - return; -} - -/* - * video-buf generic routine to get the next available VBI buffer - */ -static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); - char *outp; - - if (list_empty(&dma_q->active)) { - em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vbi_buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0x00, (*buf)->vb.size); - - dev->isoc_ctl.vbi_buf = *buf; - - return; -} - -/* - * Controls the isoc copy of each urb packet - */ -static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) -{ - struct em28xx_buffer *buf; - struct em28xx_dmaqueue *dma_q = &dev->vidq; - unsigned char *outp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - - if (!dev) - return 0; - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - buf = dev->isoc_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - len = urb->iso_frame_desc[i].actual_length - 4; - - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; - } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* FIXME: incomplete buffer checks where removed to make - logic simpler. Impacts of those changes should be evaluated - */ - if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { - em28xx_isocdbg("VBI HEADER!!!\n"); - /* FIXME: Should add vbi copy */ - continue; - } - if (p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - - if (dev->progressive || !(p[2] & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - - if (buf != NULL) { - if (p[2] & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } - - dma_q->pos = 0; - } - if (buf != NULL) { - if (p[0] != 0x88 && p[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - len += 4; - } else { - p += 4; - } - em28xx_copy_video(dev, dma_q, buf, p, outp, len); - } - } - return rc; -} - -/* Version of isoc handler that takes into account a mixture of video and - VBI data */ -static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) -{ - struct em28xx_buffer *buf, *vbi_buf; - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - unsigned char *outp = NULL; - unsigned char *vbioutp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - int vbi_size; - - if (!dev) - return 0; - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } - - buf = dev->isoc_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - vbi_buf = dev->isoc_ctl.vbi_buf; - if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); - - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - len = urb->iso_frame_desc[i].actual_length; - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; - } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* capture type 0 = vbi start - capture type 1 = video start - capture type 2 = video in progress */ - if (p[0] == 0x33 && p[1] == 0x95) { - dev->capture_type = 0; - dev->vbi_read = 0; - em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->cur_field = p[2]; - p += 4; - len -= 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len -= 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - p += 4; - len -= 4; - } - - vbi_size = dev->vbi_width * dev->vbi_height; - - if (dev->capture_type == 0) { - if (dev->vbi_read >= vbi_size) { - /* We've already read all the VBI data, so - treat the rest as video */ - em28xx_isocdbg("dev->vbi_read > vbi_size\n"); - } else if ((dev->vbi_read + len) < vbi_size) { - /* This entire frame is VBI data */ - if (dev->vbi_read == 0 && - (!(dev->cur_field & 1))) { - /* Brand new frame */ - if (vbi_buf != NULL) - vbi_buffer_filled(dev, - vbi_dma_q, - vbi_buf); - vbi_get_next_buf(vbi_dma_q, &vbi_buf); - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = videobuf_to_vmalloc( - &vbi_buf->vb); - } - - if (dev->vbi_read == 0) { - vbi_dma_q->pos = 0; - if (vbi_buf != NULL) { - if (dev->cur_field & 1) - vbi_buf->top_field = 0; - else - vbi_buf->top_field = 1; - } - } - - dev->vbi_read += len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, len); - } else { - /* Some of this frame is VBI data and some is - video data */ - int vbi_data_len = vbi_size - dev->vbi_read; - dev->vbi_read += vbi_data_len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, vbi_data_len); - dev->capture_type = 1; - p += vbi_data_len; - len -= vbi_data_len; - } - } - - if (dev->capture_type == 1) { - dev->capture_type = 2; - if (dev->progressive || !(dev->cur_field & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - if (buf != NULL) { - if (dev->cur_field & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } - - dma_q->pos = 0; - } - - if (buf != NULL && dev->capture_type == 2) { - if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - p += 4; - len -= 4; - } - if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, len=%i, %s\n", - p[2], len, (p[2] & 1) ? - "odd" : "even"); - p += 4; - len -= 4; - } - - if (len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, - len); - } - } - return rc; -} - - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) -{ - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct v4l2_frequency f; - - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) - >> 3; - - if (0 == *count) - *count = EM28XX_DEF_BUF; - - if (*count < EM28XX_MIN_BUF) - *count = EM28XX_MIN_BUF; - - /* Ask tuner to go to analog or radio mode */ - memset(&f, 0, sizeof(f)); - f.frequency = dev->ctl_freq; - f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); - - return 0; -} - -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) -{ - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vid_buf == buf) - dev->isoc_ctl.vid_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct em28xx_fh *fh = vq->priv_data; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - struct em28xx *dev = fh->dev; - int rc = 0, urb_init = 0; - - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth - + 7) >> 3; - - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - if (!dev->isoc_ctl.analog_bufs.num_bufs) - urb_init = 1; - - if (urb_init) { - if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy_vbi); - else - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(vq, buf); - return rc; -} - -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vidq = &dev->vidq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); - -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = (struct em28xx *)fh->dev; - - em28xx_isocdbg("em28xx: called buffer_release\n"); - - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops em28xx_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/********************* v4l2 interface **************************************/ - -static void video_mux(struct em28xx *dev, int index) -{ - dev->ctl_input = index; - dev->ctl_ainput = INPUT(index)->amux; - dev->ctl_aoutput = INPUT(index)->aout; - - if (!dev->ctl_aoutput) - dev->ctl_aoutput = EM28XX_AOUT_MASTER; - - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, - INPUT(index)->vmux, 0, 0); - - if (dev->board.has_msp34xx) { - if (dev->i2s_speed) { - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, - s_i2s_clock_freq, dev->i2s_speed); - } - /* Note: this is msp3400 specific */ - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, - dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); - } - - if (dev->board.adecoder != EM28XX_NOADECODER) { - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, - dev->ctl_ainput, dev->ctl_aoutput, 0); - } - - em28xx_audio_analog_set(dev); -} - -/* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh, unsigned int bit) -{ - struct em28xx *dev = fh->dev; - - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - if (dev->resources & bit) { - /* no, someone else uses it */ - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - em28xx_videodbg("res: get %d\n", bit); - return 1; -} - -static int res_check(struct em28xx_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct em28xx *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct em28xx_fh *fh, unsigned int bits) -{ - struct em28xx *dev = fh->dev; - - BUG_ON((fh->resources & bits) != bits); - - fh->resources &= ~bits; - dev->resources &= ~bits; - em28xx_videodbg("res: put %d\n", bits); -} - -static int get_ressource(struct em28xx_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return EM28XX_RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return EM28XX_RESOURCE_VBI; - default: - BUG(); - return 0; - } -} - -/* - * ac97_queryctrl() - * return the ac97 supported controls - */ -static int ac97_queryctrl(struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - /* Control is not ac97 related */ - return 1; -} - -/* - * ac97_get_ctrl() - * return the current values for ac97 mute and volume - */ -static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->mute; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = dev->volume; - return 0; - default: - /* Control is not ac97 related */ - return 1; - } -} - -/* - * ac97_set_ctrl() - * set values for ac97 mute and volume - */ -static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) - if (ctrl->id == ac97_qctrl[i].id) - goto handle; - - /* Announce that hasn't handle it */ - return 1; - -handle: - if (ctrl->value < ac97_qctrl[i].minimum || - ctrl->value > ac97_qctrl[i].maximum) - return -ERANGE; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; - break; - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - break; - } - - return em28xx_audio_analog_set(dev); -} - -static int check_dev(struct em28xx *dev) -{ - if (dev->state & DEV_DISCONNECTED) { - em28xx_errdev("v4l2 ioctl: device not present\n"); - return -ENODEV; - } - - if (dev->state & DEV_MISCONFIGURED) { - em28xx_errdev("v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); - return -EIO; - } - return 0; -} - -static void get_scale(struct em28xx *dev, - unsigned int width, unsigned int height, - unsigned int *hscale, unsigned int *vscale) -{ - unsigned int maxw = norm_maxw(dev); - unsigned int maxh = norm_maxh(dev); - - *hscale = (((unsigned long)maxw) << 12) / width - 4096L; - if (*hscale >= 0x4000) - *hscale = 0x3fff; - - *vscale = (((unsigned long)maxh) << 12) / height - 4096L; - if (*vscale >= 0x4000) - *vscale = 0x3fff; -} - -/* ------------------------------------------------------------------ - IOCTL vidioc handling - ------------------------------------------------------------------*/ - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = dev->format->fourcc; - f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ - if (dev->progressive) - f->fmt.pix.field = V4L2_FIELD_NONE; - else - f->fmt.pix.field = dev->interlaced ? - V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; - return 0; -} - -static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(format); i++) - if (format[i].fourcc == fourcc) - return &format[i]; - - return NULL; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - unsigned int width = f->fmt.pix.width; - unsigned int height = f->fmt.pix.height; - unsigned int maxw = norm_maxw(dev); - unsigned int maxh = norm_maxh(dev); - unsigned int hscale, vscale; - struct em28xx_fmt *fmt; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (!fmt) { - em28xx_videodbg("Fourcc format (%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - if (dev->board.is_em2800) { - /* the em2800 can only scale down to 50% */ - height = height > (3 * maxh / 4) ? maxh : maxh / 2; - width = width > (3 * maxw / 4) ? maxw : maxw / 2; - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ - if (width == maxw && height == maxh) - width /= 2; - } else { - /* width must even because of the YUYV format - height must be even because of interlacing */ - v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, - 1, 0); - } - - get_scale(dev, width, height, &hscale, &vscale); - - width = (((unsigned long)maxw) << 12) / (hscale + 4096L); - height = (((unsigned long)maxh) << 12) / (vscale + 4096L); - - f->fmt.pix.width = width; - f->fmt.pix.height = height; - f->fmt.pix.pixelformat = fmt->fourcc; - f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - if (dev->progressive) - f->fmt.pix.field = V4L2_FIELD_NONE; - else - f->fmt.pix.field = dev->interlaced ? - V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; - - return 0; -} - -static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, - unsigned width, unsigned height) -{ - struct em28xx_fmt *fmt; - - fmt = format_by_fourcc(fourcc); - if (!fmt) - return -EINVAL; - - dev->format = fmt; - dev->width = width; - dev->height = height; - - /* set new image size */ - get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); - - em28xx_set_alternate(dev); - em28xx_resolution_set(dev); - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - vidioc_try_fmt_vid_cap(file, priv, f); - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - em28xx_errdev("%s queue busy\n", __func__); - return -EBUSY; - } - - return em28xx_set_video_format(dev, f->fmt.pix.pixelformat, - f->fmt.pix.width, f->fmt.pix.height); -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - *norm = dev->norm; - - return 0; -} - -static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); - - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - struct v4l2_format f; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - dev->norm = *norm; - - /* Adjusts width/height, if needed */ - f.fmt.pix.width = dev->width; - f.fmt.pix.height = dev->height; - vidioc_try_fmt_vid_cap(file, priv, &f); - - /* set new image size */ - dev->width = f.fmt.pix.width; - dev->height = f.fmt.pix.height; - get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); - - em28xx_resolution_set(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); - - return 0; -} - -static int vidioc_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *p) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc = 0; - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (dev->board.is_webcam) - rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, - video, g_parm, p); - else - v4l2_video_std_frame_period(dev->norm, - &p->parm.capture.timeperframe); - - return rc; -} - -static int vidioc_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *p) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - if (!dev->board.is_webcam) - return -EINVAL; - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); -} - -static const char *iname[] = { - [EM28XX_VMUX_COMPOSITE1] = "Composite1", - [EM28XX_VMUX_COMPOSITE2] = "Composite2", - [EM28XX_VMUX_COMPOSITE3] = "Composite3", - [EM28XX_VMUX_COMPOSITE4] = "Composite4", - [EM28XX_VMUX_SVIDEO] = "S-Video", - [EM28XX_VMUX_TELEVISION] = "Television", - [EM28XX_VMUX_CABLE] = "Cable TV", - [EM28XX_VMUX_DVB] = "DVB", - [EM28XX_VMUX_DEBUG] = "for debug only", -}; - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - unsigned int n; - - n = i->index; - if (n >= MAX_EM28XX_INPUT) - return -EINVAL; - if (0 == INPUT(n)->type) - return -EINVAL; - - i->index = n; - i->type = V4L2_INPUT_TYPE_CAMERA; - - strcpy(i->name, iname[INPUT(n)->type]); - - if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) || - (EM28XX_VMUX_CABLE == INPUT(n)->type)) - i->type = V4L2_INPUT_TYPE_TUNER; - - i->std = dev->vdev->tvnorms; - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - *i = dev->ctl_input; - - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (i >= MAX_EM28XX_INPUT) - return -EINVAL; - if (0 == INPUT(i)->type) - return -EINVAL; - - video_mux(dev, i); - return 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - if (!dev->audio_mode.has_audio) - return -EINVAL; - - switch (a->index) { - case EM28XX_AMUX_VIDEO: - strcpy(a->name, "Television"); - break; - case EM28XX_AMUX_LINE_IN: - strcpy(a->name, "Line In"); - break; - case EM28XX_AMUX_VIDEO2: - strcpy(a->name, "Television alt"); - break; - case EM28XX_AMUX_PHONE: - strcpy(a->name, "Phone"); - break; - case EM28XX_AMUX_MIC: - strcpy(a->name, "Mic"); - break; - case EM28XX_AMUX_CD: - strcpy(a->name, "CD"); - break; - case EM28XX_AMUX_AUX: - strcpy(a->name, "Aux"); - break; - case EM28XX_AMUX_PCM_OUT: - strcpy(a->name, "PCM"); - break; - default: - return -EINVAL; - } - - a->index = dev->ctl_ainput; - a->capability = V4L2_AUDCAP_STEREO; - - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - - if (!dev->audio_mode.has_audio) - return -EINVAL; - - if (a->index >= MAX_EM28XX_INPUT) - return -EINVAL; - if (0 == INPUT(a->index)->type) - return -EINVAL; - - dev->ctl_ainput = INPUT(a->index)->amux; - dev->ctl_aoutput = INPUT(a->index)->aout; - - if (!dev->ctl_aoutput) - dev->ctl_aoutput = EM28XX_AOUT_MASTER; - - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int id = qc->id; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - memset(qc, 0, sizeof(*qc)); - - qc->id = id; - - /* enumerate AC97 controls */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - rc = ac97_queryctrl(qc); - if (!rc) - return 0; - } - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); - - if (qc->type) - return 0; - else - return -EINVAL; -} - -/* - * FIXME: This is an indirect way to check if a control exists at a - * subdev. Instead of that hack, maybe the better would be to change all - * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported. - */ -static int check_subdev_ctrl(struct em28xx *dev, int id) -{ - struct v4l2_queryctrl qc; - - memset(&qc, 0, sizeof(qc)); - qc.id = id; - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc); - - if (qc.type) - return 0; - else - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - rc = 0; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_get_ctrl(dev, ctrl); - else - rc = 1; - - /* It were not an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - if (check_subdev_ctrl(dev, ctrl->id)) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } - - return rc; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_set_ctrl(dev, ctrl); - else - rc = 1; - - /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - rc = check_subdev_ctrl(dev, ctrl->id); - if (!rc) - v4l2_device_call_all(&dev->v4l2_dev, 0, - core, s_ctrl, ctrl); - /* - * In the case of non-AC97 volume controls, we still need - * to do some setups at em28xx, in order to mute/unmute - * and to adjust audio volume. However, the value ranges - * should be checked by the corresponding V4L subdriver. - */ - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; - rc = em28xx_audio_analog_set(dev); - break; - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - rc = em28xx_audio_analog_set(dev); - } - } - return (rc < 0) ? rc : 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (0 != t->index) - return -EINVAL; - - strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (0 != t->index) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = dev->ctl_freq; - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (0 != f->tuner) - return -EINVAL; - - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; - - dev->ctl_freq = f->frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int em28xx_reg_len(int reg) -{ - switch (reg) { - case EM28XX_R40_AC97LSB: - case EM28XX_R30_HSCALELOW: - case EM28XX_R32_VSCALELOW: - return 2; - default: - return 1; - } -} - -static int vidioc_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); - - return 0; -} - - -static int vidioc_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int ret; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: - ret = em28xx_read_ac97(dev, reg->reg); - if (ret < 0) - return ret; - - reg->val = ret; - reg->size = 1; - return 0; - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - /* TODO: is this correct? */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } - - /* Match host */ - reg->size = em28xx_reg_len(reg->reg); - if (reg->size == 1) { - ret = em28xx_read_reg(dev, reg->reg); - - if (ret < 0) - return ret; - - reg->val = ret; - } else { - __le16 val = 0; - ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, - reg->reg, (char *)&val, 2); - if (ret < 0) - return ret; - - reg->val = le16_to_cpu(val); - } - - return 0; -} - -static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - __le16 buf; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: - return em28xx_write_ac97(dev, reg->reg, reg->val); - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - /* TODO: is this correct? */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } - - /* Match host */ - buf = cpu_to_le16(reg->val); - - return em28xx_write_regs(dev, reg->reg, (char *)&buf, - em28xx_reg_len(reg->reg)); -} -#endif - - -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - cc->bounds.left = 0; - cc->bounds.top = 0; - cc->bounds.width = dev->width; - cc->bounds.height = dev->height; - cc->defrect = cc->bounds; - cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ - cc->pixelaspect.denominator = 59; - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc = -EINVAL; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (unlikely(type != fh->type)) - return -EINVAL; - - em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (unlikely(!res_get(fh, get_ressource(fh)))) - return -EBUSY; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_streamon(&fh->vb_vidq); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_streamon(&fh->vb_vbiq); - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } - } - - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); - strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = - V4L2_CAP_SLICED_VBI_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - - if (dev->vbi_dev) - cap->capabilities |= V4L2_CAP_VBI_CAPTURE; - - if (dev->audio_mode.has_audio) - cap->capabilities |= V4L2_CAP_AUDIO; - - if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (unlikely(f->index >= ARRAY_SIZE(format))) - return -EINVAL; - - strlcpy(f->description, format[f->index].name, sizeof(f->description)); - f->pixelformat = format[f->index].fourcc; - - return 0; -} - -static int vidioc_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - struct em28xx_fmt *fmt; - unsigned int maxw = norm_maxw(dev); - unsigned int maxh = norm_maxh(dev); - - fmt = format_by_fourcc(fsize->pixel_format); - if (!fmt) { - em28xx_videodbg("Fourcc format (%08x) invalid.\n", - fsize->pixel_format); - return -EINVAL; - } - - if (dev->board.is_em2800) { - if (fsize->index > 1) - return -EINVAL; - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = maxw / (1 + fsize->index); - fsize->discrete.height = maxh / (1 + fsize->index); - return 0; - } - - if (fsize->index != 0) - return -EINVAL; - - /* Report a continuous range */ - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = 48; - fsize->stepwise.min_height = 32; - fsize->stepwise.max_width = maxw; - fsize->stepwise.max_height = maxh; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - return 0; -} - -/* Sliced VBI ioctls */ -static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - f->fmt.sliced.service_set = 0; - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - rc = -EINVAL; - - return rc; -} - -static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - return -EINVAL; - - return 0; -} - -/* RAW VBI ioctls */ - -static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - format->fmt.vbi.samples_per_line = dev->vbi_width; - format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 0; - format->fmt.vbi.flags = 0; - format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; - format->fmt.vbi.count[0] = dev->vbi_height; - format->fmt.vbi.count[1] = dev->vbi_height; - - /* Varies by video standard (NTSC, PAL, etc.) */ - if (dev->norm & V4L2_STD_525_60) { - /* NTSC */ - format->fmt.vbi.start[0] = 10; - format->fmt.vbi.start[1] = 273; - } else if (dev->norm & V4L2_STD_625_50) { - /* PAL */ - format->fmt.vbi.start[0] = 6; - format->fmt.vbi.start[1] = 318; - } - - return 0; -} - -static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - format->fmt.vbi.samples_per_line = dev->vbi_width; - format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 0; - format->fmt.vbi.flags = 0; - format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; - format->fmt.vbi.count[0] = dev->vbi_height; - format->fmt.vbi.count[1] = dev->vbi_height; - - /* Varies by video standard (NTSC, PAL, etc.) */ - if (dev->norm & V4L2_STD_525_60) { - /* NTSC */ - format->fmt.vbi.start[0] = 10; - format->fmt.vbi.start[1] = 273; - } else if (dev->norm & V4L2_STD_625_50) { - /* PAL */ - format->fmt.vbi.start[0] = 6; - format->fmt.vbi.start[1] = 318; - } - - return 0; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_reqbufs(&fh->vb_vidq, rb); - else - return videobuf_reqbufs(&fh->vb_vbiq, rb); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_querybuf(&fh->vb_vidq, b); - else { - /* FIXME: I'm not sure yet whether this is a bug in zvbi or - the videobuf framework, but we probably shouldn't be - returning a buffer larger than that which was asked for. - At a minimum, it causes a crash in zvbi since it does - a memcpy based on the source buffer length */ - int result = videobuf_querybuf(&fh->vb_vbiq, b); - b->length = dev->vbi_width * dev->vbi_height * 2; - - return result; - } -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_qbuf(&fh->vb_vidq, b); - else - return videobuf_qbuf(&fh->vb_vbiq, b); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & - O_NONBLOCK); - else - return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & - O_NONBLOCK); -} - -/* ----------------------------------------------------------- */ -/* RADIO ESPECIFIC IOCTLS */ -/* ----------------------------------------------------------- */ - -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; - - strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); - strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - -static int radio_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; - - if (unlikely(t->index > 0)) - return -EINVAL; - - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - - return 0; -} - -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "Radio"); - return 0; -} - -static int radio_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; - - if (0 != t->index) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - - return 0; -} - -static int radio_s_audio(struct file *file, void *fh, - struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *file, void *fh, unsigned int i) -{ - return 0; -} - -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - if (qc->id < V4L2_CID_BASE || - qc->id >= V4L2_CID_LASTP1) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - return -EINVAL; -} - -/* - * em28xx_v4l2_open() - * inits the device and starts isoc transfer - */ -static int em28xx_v4l2_open(struct file *filp) -{ - int errCode = 0, radio = 0; - struct video_device *vdev = video_devdata(filp); - struct em28xx *dev = video_drvdata(filp); - enum v4l2_buf_type fh_type = 0; - struct em28xx_fh *fh; - enum v4l2_field field; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - case VFL_TYPE_RADIO: - radio = 1; - break; - } - - em28xx_videodbg("open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[fh_type], - dev->users); - - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); - if (!fh) { - em28xx_errdev("em28xx-video.c: Out of memory?!\n"); - mutex_unlock(&dev->lock); - return -ENOMEM; - } - fh->dev = dev; - fh->radio = radio; - fh->type = fh_type; - filp->private_data = fh; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { - em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - em28xx_set_alternate(dev); - em28xx_resolution_set(dev); - - /* Needed, since GPIO might have disabled power of - some i2c device - */ - em28xx_wake_i2c(dev); - - } - if (fh->radio) { - em28xx_videodbg("video_open: setting radio device\n"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); - } - - dev->users++; - - if (dev->progressive) - field = V4L2_FIELD_NONE; - else - field = V4L2_FIELD_INTERLACED; - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, field, - sizeof(struct em28xx_buffer), fh, &dev->lock); - - videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct em28xx_buffer), fh, &dev->lock); - mutex_unlock(&dev->lock); - - return errCode; -} - -/* - * em28xx_realease_resources() - * unregisters the v4l2,i2c and usb devices - * called when the device gets disconected or at module unload -*/ -void em28xx_release_analog_resources(struct em28xx *dev) -{ - - /*FIXME: I2C IR should be disconnected */ - - if (dev->radio_dev) { - if (video_is_registered(dev->radio_dev)) - video_unregister_device(dev->radio_dev); - else - video_device_release(dev->radio_dev); - dev->radio_dev = NULL; - } - if (dev->vbi_dev) { - em28xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vbi_dev)); - if (video_is_registered(dev->vbi_dev)) - video_unregister_device(dev->vbi_dev); - else - video_device_release(dev->vbi_dev); - dev->vbi_dev = NULL; - } - if (dev->vdev) { - em28xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vdev)); - if (video_is_registered(dev->vdev)) - video_unregister_device(dev->vdev); - else - video_device_release(dev->vdev); - dev->vdev = NULL; - } -} - -/* - * em28xx_v4l2_close() - * stops streaming and deallocates all resources allocated by the v4l2 - * calls and ioctls - */ -static int em28xx_v4l2_close(struct file *filp) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int errCode; - - em28xx_videodbg("users=%d\n", dev->users); - - mutex_lock(&dev->lock); - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_stop(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_stop(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } - - if (dev->users == 1) { - /* the device is already disconnect, - free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { - em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size); - kfree(dev); - kfree(fh); - mutex_unlock(&dev->lock); - return 0; - } - - /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); - - /* do this before setting alternate! */ - em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); - em28xx_set_mode(dev, EM28XX_SUSPEND); - - /* set alternate 0 */ - dev->alt = 0; - em28xx_videodbg("setting alternate 0\n"); - errCode = usb_set_interface(dev->udev, 0, 0); - if (errCode < 0) { - em28xx_errdev("cannot change alternate number to " - "0 (error=%i)\n", errCode); - } - } - - videobuf_mmap_free(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vbiq); - kfree(fh); - dev->users--; - mutex_unlock(&dev->lock); - return 0; -} - -/* - * em28xx_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t -em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - /* FIXME: read() is not prepared to allow changing the video - resolution while streaming. Seems a bug at em28xx_set_fmt - */ - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - mutex_unlock(&dev->lock); - - return rc; -} - -/* - * em28xx_poll() - * will allocate buffers when called for the first time - */ -static unsigned int em28xx_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } else { - return POLLERR; - } -} - -static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - unsigned int res; - - mutex_lock(&dev->lock); - res = em28xx_poll(filp, wait); - mutex_unlock(&dev->lock); - return res; -} - -/* - * em28xx_v4l2_mmap() - */ -static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); - mutex_unlock(&dev->lock); - - em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, - rc); - - return rc; -} - -static const struct v4l2_file_operations em28xx_v4l_fops = { - .owner = THIS_MODULE, - .open = em28xx_v4l2_open, - .release = em28xx_v4l2_close, - .read = em28xx_v4l2_read, - .poll = em28xx_v4l2_poll, - .mmap = em28xx_v4l2_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_g_std = vidioc_g_std, - .vidioc_querystd = vidioc_querystd, - .vidioc_s_std = vidioc_s_std, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_s_parm = vidioc_s_parm, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, - .vidioc_g_chip_ident = vidioc_g_chip_ident, -#endif -}; - -static const struct video_device em28xx_video_template = { - .fops = &em28xx_v4l_fops, - .release = video_device_release, - .ioctl_ops = &video_ioctl_ops, - - .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_PAL, -}; - -static const struct v4l2_file_operations radio_fops = { - .owner = THIS_MODULE, - .open = em28xx_v4l2_open, - .release = em28xx_v4l2_close, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, - .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, - .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, -#endif -}; - -static struct video_device em28xx_radio_template = { - .name = "em28xx-radio", - .fops = &radio_fops, - .ioctl_ops = &radio_ioctl_ops, -}; - -/******************************** usb interface ******************************/ - - - -static struct video_device *em28xx_vdev_init(struct em28xx *dev, - const struct video_device *template, - const char *type_name) -{ - struct video_device *vfd; - - vfd = video_device_alloc(); - if (NULL == vfd) - return NULL; - - *vfd = *template; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; - vfd->debug = video_debug; - vfd->lock = &dev->lock; - - snprintf(vfd->name, sizeof(vfd->name), "%s %s", - dev->name, type_name); - - video_set_drvdata(vfd, dev); - return vfd; -} - -int em28xx_register_analog_devices(struct em28xx *dev) -{ - u8 val; - int ret; - unsigned int maxw; - - printk(KERN_INFO "%s: v4l2 driver version %s\n", - dev->name, EM28XX_VERSION); - - /* set default norm */ - dev->norm = em28xx_video_template.current_norm; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); - dev->interlaced = EM28XX_INTERLACED_DEFAULT; - - /* Analog specific initialization */ - dev->format = &format[0]; - - maxw = norm_maxw(dev); - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ - if (dev->board.is_em2800) - maxw /= 2; - - em28xx_set_video_format(dev, format[0].fourcc, - maxw, norm_maxh(dev)); - - video_mux(dev, 0); - - /* Audio defaults */ - dev->mute = 1; - dev->volume = 0x1f; - -/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ - val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - (EM28XX_XCLK_AUDIO_UNMUTE | val)); - - em28xx_set_outfmt(dev); - em28xx_colorlevels_set_default(dev); - em28xx_compression_disable(dev); - - /* allocate and fill video video_device struct */ - dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); - if (!dev->vdev) { - em28xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; - } - - /* register v4l2 video video_device */ - ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, - video_nr[dev->devno]); - if (ret) { - em28xx_errdev("unable to register video device (error=%i).\n", - ret); - return ret; - } - - /* Allocate and fill vbi video_device struct */ - if (em28xx_vbi_supported(dev) == 1) { - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, - "vbi"); - - /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]); - if (ret < 0) { - em28xx_errdev("unable to register vbi device\n"); - return ret; - } - } - - if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { - dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, - "radio"); - if (!dev->radio_dev) { - em28xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; - } - ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, - radio_nr[dev->devno]); - if (ret < 0) { - em28xx_errdev("can't register radio device\n"); - return ret; - } - em28xx_info("Registered radio device as %s\n", - video_device_node_name(dev->radio_dev)); - } - - em28xx_info("V4L2 video device registered as %s\n", - video_device_node_name(dev->vdev)); - - if (dev->vbi_dev) - em28xx_info("V4L2 VBI device registered as %s\n", - video_device_node_name(dev->vbi_dev)); - - return 0; -} diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h deleted file mode 100644 index 8757523e6863..000000000000 --- a/drivers/media/video/em28xx/em28xx.h +++ /dev/null @@ -1,809 +0,0 @@ -/* - em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices - - Copyright (C) 2005 Markus Rechberger - Ludovico Cavedon - Mauro Carvalho Chehab - - Based on the em2800 driver from Sascha Sommer - - This program is free software; 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 _EM28XX_H -#define _EM28XX_H - -#include -#include -#include -#include - -#include -#include -#include -#include -#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) -#include -#endif -#include "tuner-xc2028.h" -#include "xc5000.h" -#include "em28xx-reg.h" - -/* Boards supported by driver */ -#define EM2800_BOARD_UNKNOWN 0 -#define EM2820_BOARD_UNKNOWN 1 -#define EM2820_BOARD_TERRATEC_CINERGY_250 2 -#define EM2820_BOARD_PINNACLE_USB_2 3 -#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 -#define EM2820_BOARD_MSI_VOX_USB_2 5 -#define EM2800_BOARD_TERRATEC_CINERGY_200 6 -#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 -#define EM2800_BOARD_KWORLD_USB2800 8 -#define EM2820_BOARD_PINNACLE_DVC_90 9 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 -#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 -#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 -#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 -#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 -#define EM2800_BOARD_VGEAR_POCKETTV 15 -#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16 -#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18 -#define EM2860_BOARD_SAA711X_REFERENCE_DESIGN 19 -#define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20 -#define EM2800_BOARD_GRABBEEX_USB2800 21 -#define EM2750_BOARD_UNKNOWN 22 -#define EM2750_BOARD_DLCW_130 23 -#define EM2820_BOARD_DLINK_USB_TV 24 -#define EM2820_BOARD_GADMEI_UTV310 25 -#define EM2820_BOARD_HERCULES_SMART_TV_USB2 26 -#define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27 -#define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28 -#define EM2860_BOARD_TVP5150_REFERENCE_DESIGN 29 -#define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 -#define EM2821_BOARD_USBGEAR_VD204 31 -#define EM2821_BOARD_SUPERCOMP_USB_2 32 -#define EM2860_BOARD_ELGATO_VIDEO_CAPTURE 33 -#define EM2860_BOARD_TERRATEC_HYBRID_XS 34 -#define EM2860_BOARD_TYPHOON_DVD_MAKER 35 -#define EM2860_BOARD_NETGMBH_CAM 36 -#define EM2860_BOARD_GADMEI_UTV330 37 -#define EM2861_BOARD_YAKUMO_MOVIE_MIXER 38 -#define EM2861_BOARD_KWORLD_PVRTV_300U 39 -#define EM2861_BOARD_PLEXTOR_PX_TV100U 40 -#define EM2870_BOARD_KWORLD_350U 41 -#define EM2870_BOARD_KWORLD_355U 42 -#define EM2870_BOARD_TERRATEC_XS 43 -#define EM2870_BOARD_TERRATEC_XS_MT2060 44 -#define EM2870_BOARD_PINNACLE_PCTV_DVB 45 -#define EM2870_BOARD_COMPRO_VIDEOMATE 46 -#define EM2880_BOARD_KWORLD_DVB_305U 47 -#define EM2880_BOARD_KWORLD_DVB_310U 48 -#define EM2880_BOARD_MSI_DIGIVOX_AD 49 -#define EM2880_BOARD_MSI_DIGIVOX_AD_II 50 -#define EM2880_BOARD_TERRATEC_HYBRID_XS_FR 51 -#define EM2881_BOARD_DNT_DA2_HYBRID 52 -#define EM2881_BOARD_PINNACLE_HYBRID_PRO 53 -#define EM2882_BOARD_KWORLD_VS_DVBT 54 -#define EM2882_BOARD_TERRATEC_HYBRID_XS 55 -#define EM2882_BOARD_PINNACLE_HYBRID_PRO_330E 56 -#define EM2883_BOARD_KWORLD_HYBRID_330U 57 -#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 -#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 -#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 -#define EM2820_BOARD_GADMEI_TVR200 62 -#define EM2860_BOARD_KAIOMY_TVNPC_U2 63 -#define EM2860_BOARD_EASYCAP 64 -#define EM2820_BOARD_IODATA_GVMVP_SZ 65 -#define EM2880_BOARD_EMPIRE_DUAL_TV 66 -#define EM2860_BOARD_TERRATEC_GRABBY 67 -#define EM2860_BOARD_TERRATEC_AV350 68 -#define EM2882_BOARD_KWORLD_ATSC_315U 69 -#define EM2882_BOARD_EVGA_INDTUBE 70 -#define EM2820_BOARD_SILVERCREST_WEBCAM 71 -#define EM2861_BOARD_GADMEI_UTV330PLUS 72 -#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 -#define EM2800_BOARD_VC211A 74 -#define EM2882_BOARD_DIKOM_DK300 75 -#define EM2870_BOARD_KWORLD_A340 76 -#define EM2874_BOARD_LEADERSHIP_ISDBT 77 -#define EM28174_BOARD_PCTV_290E 78 -#define EM2884_BOARD_TERRATEC_H5 79 -#define EM28174_BOARD_PCTV_460E 80 -#define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81 -#define EM2884_BOARD_CINERGY_HTC_STICK 82 -#define EM2860_BOARD_HT_VIDBOX_NW03 83 -#define EM2874_BOARD_MAXMEDIA_UB425_TC 84 -#define EM2884_BOARD_PCTV_510E 85 -#define EM2884_BOARD_PCTV_520E 86 - -/* Limits minimum and default number of buffers */ -#define EM28XX_MIN_BUF 4 -#define EM28XX_DEF_BUF 8 - -/*Limits the max URB message size */ -#define URB_MAX_CTRL_SIZE 80 - -/* Params for validated field */ -#define EM28XX_BOARD_NOT_VALIDATED 1 -#define EM28XX_BOARD_VALIDATED 0 - -/* Params for em28xx_cmd() audio */ -#define EM28XX_START_AUDIO 1 -#define EM28XX_STOP_AUDIO 0 - -/* maximum number of em28xx boards */ -#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ - -/* maximum number of frames that can be queued */ -#define EM28XX_NUM_FRAMES 5 -/* number of frames that get used for v4l2_read() */ -#define EM28XX_NUM_READ_FRAMES 2 - -/* number of buffers for isoc transfers */ -#define EM28XX_NUM_BUFS 5 -#define EM28XX_DVB_NUM_BUFS 5 - -/* number of packets for each buffer - windows requests only 64 packets .. so we better do the same - this is what I found out for all alternate numbers there! - */ -#define EM28XX_NUM_PACKETS 64 -#define EM28XX_DVB_MAX_PACKETS 64 - -#define EM28XX_INTERLACED_DEFAULT 1 - -/* -#define (use usbview if you want to get the other alternate number infos) -#define -#define alternate number 2 -#define Endpoint Address: 82 - Direction: in - Attribute: 1 - Type: Isoc - Max Packet Size: 1448 - Interval: 125us - - alternate number 7 - - Endpoint Address: 82 - Direction: in - Attribute: 1 - Type: Isoc - Max Packet Size: 3072 - Interval: 125us -*/ - -/* time to wait when stopping the isoc transfer */ -#define EM28XX_URB_TIMEOUT \ - msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) - -/* time in msecs to wait for i2c writes to finish */ -#define EM2800_I2C_WRITE_TIMEOUT 20 - -enum em28xx_mode { - EM28XX_SUSPEND, - EM28XX_ANALOG_MODE, - EM28XX_DIGITAL_MODE, -}; - - -struct em28xx; - -struct em28xx_usb_isoc_bufs { - /* max packet size of isoc transaction */ - int max_pkt_size; - - /* number of packets in each buffer */ - int num_packets; - - /* number of allocated urbs */ - int num_bufs; - - /* urb for isoc transfers */ - struct urb **urb; - - /* transfer buffers for isoc transfer */ - char **transfer_buffer; -}; - -struct em28xx_usb_isoc_ctl { - /* isoc transfer buffers for analog mode */ - struct em28xx_usb_isoc_bufs analog_bufs; - - /* isoc transfer buffers for digital mode */ - struct em28xx_usb_isoc_bufs digital_bufs; - - /* Stores already requested buffers */ - struct em28xx_buffer *vid_buf; - struct em28xx_buffer *vbi_buf; - - /* isoc urb callback */ - int (*isoc_copy) (struct em28xx *dev, struct urb *urb); - -}; - -/* Struct to enumberate video formats */ -struct em28xx_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ - int depth; - int reg; -}; - -/* buffer for one video frame */ -struct em28xx_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - struct list_head frame; - int top_field; -}; - -struct em28xx_dmaqueue { - struct list_head active; - - wait_queue_head_t wq; - - /* Counters to control buffer fill */ - int pos; -}; - -/* inputs */ - -#define MAX_EM28XX_INPUT 4 -enum enum28xx_itype { - EM28XX_VMUX_COMPOSITE1 = 1, - EM28XX_VMUX_COMPOSITE2, - EM28XX_VMUX_COMPOSITE3, - EM28XX_VMUX_COMPOSITE4, - EM28XX_VMUX_SVIDEO, - EM28XX_VMUX_TELEVISION, - EM28XX_VMUX_CABLE, - EM28XX_VMUX_DVB, - EM28XX_VMUX_DEBUG, - EM28XX_RADIO, -}; - -enum em28xx_ac97_mode { - EM28XX_NO_AC97 = 0, - EM28XX_AC97_EM202, - EM28XX_AC97_SIGMATEL, - EM28XX_AC97_OTHER, -}; - -struct em28xx_audio_mode { - enum em28xx_ac97_mode ac97; - - u16 ac97_feat; - u32 ac97_vendor_id; - - unsigned int has_audio:1; - - unsigned int i2s_3rates:1; - unsigned int i2s_5rates:1; -}; - -/* em28xx has two audio inputs: tuner and line in. - However, on most devices, an auxiliary AC97 codec device is used. - The AC97 device may have several different inputs and outputs, - depending on their model. So, it is possible to use AC97 mixer to - address more than two different entries. - */ -enum em28xx_amux { - /* This is the only entry for em28xx tuner input */ - EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */ - - EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */ - - /* Some less-common mixer setups */ - EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */ - EM28XX_AMUX_PHONE, - EM28XX_AMUX_MIC, - EM28XX_AMUX_CD, - EM28XX_AMUX_AUX, - EM28XX_AMUX_PCM_OUT, -}; - -enum em28xx_aout { - /* AC97 outputs */ - EM28XX_AOUT_MASTER = 1 << 0, - EM28XX_AOUT_LINE = 1 << 1, - EM28XX_AOUT_MONO = 1 << 2, - EM28XX_AOUT_LFE = 1 << 3, - EM28XX_AOUT_SURR = 1 << 4, - - /* PCM IN Mixer - used by AC97_RECORD_SELECT register */ - EM28XX_AOUT_PCM_IN = 1 << 7, - - /* Bits 10-8 are used to indicate the PCM IN record select */ - EM28XX_AOUT_PCM_MIC_PCM = 0 << 8, - EM28XX_AOUT_PCM_CD = 1 << 8, - EM28XX_AOUT_PCM_VIDEO = 2 << 8, - EM28XX_AOUT_PCM_AUX = 3 << 8, - EM28XX_AOUT_PCM_LINE = 4 << 8, - EM28XX_AOUT_PCM_STEREO = 5 << 8, - EM28XX_AOUT_PCM_MONO = 6 << 8, - EM28XX_AOUT_PCM_PHONE = 7 << 8, -}; - -static inline int ac97_return_record_select(int a_out) -{ - return (a_out & 0x700) >> 8; -} - -struct em28xx_reg_seq { - int reg; - unsigned char val, mask; - int sleep; -}; - -struct em28xx_input { - enum enum28xx_itype type; - unsigned int vmux; - enum em28xx_amux amux; - enum em28xx_aout aout; - struct em28xx_reg_seq *gpio; -}; - -#define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) - -enum em28xx_decoder { - EM28XX_NODECODER = 0, - EM28XX_TVP5150, - EM28XX_SAA711X, -}; - -enum em28xx_sensor { - EM28XX_NOSENSOR = 0, - EM28XX_MT9V011, - EM28XX_MT9M001, - EM28XX_MT9M111, -}; - -enum em28xx_adecoder { - EM28XX_NOADECODER = 0, - EM28XX_TVAUDIO, -}; - -struct em28xx_board { - char *name; - int vchannels; - int tuner_type; - int tuner_addr; - - /* i2c flags */ - unsigned int tda9887_conf; - - /* GPIO sequences */ - struct em28xx_reg_seq *dvb_gpio; - struct em28xx_reg_seq *suspend_gpio; - struct em28xx_reg_seq *tuner_gpio; - struct em28xx_reg_seq *mute_gpio; - - unsigned int is_em2800:1; - unsigned int has_msp34xx:1; - unsigned int mts_firmware:1; - unsigned int max_range_640_480:1; - unsigned int has_dvb:1; - unsigned int has_snapshot_button:1; - unsigned int is_webcam:1; - unsigned int valid:1; - unsigned int has_ir_i2c:1; - - unsigned char xclk, i2c_speed; - unsigned char radio_addr; - unsigned short tvaudio_addr; - - enum em28xx_decoder decoder; - enum em28xx_adecoder adecoder; - - struct em28xx_input input[MAX_EM28XX_INPUT]; - struct em28xx_input radio; - char *ir_codes; -}; - -struct em28xx_eeprom { - u32 id; /* 0x9567eb1a */ - u16 vendor_ID; - u16 product_ID; - - u16 chip_conf; - - u16 board_conf; - - u16 string1, string2, string3; - - u8 string_idx_table; -}; - -/* device states */ -enum em28xx_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -#define EM28XX_AUDIO_BUFS 5 -#define EM28XX_NUM_AUDIO_PACKETS 64 -#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ -#define EM28XX_CAPTURE_STREAM_EN 1 - -/* em28xx extensions */ -#define EM28XX_AUDIO 0x10 -#define EM28XX_DVB 0x20 -#define EM28XX_RC 0x30 - -/* em28xx resource types (used for res_get/res_lock etc */ -#define EM28XX_RESOURCE_VIDEO 0x01 -#define EM28XX_RESOURCE_VBI 0x02 - -struct em28xx_audio { - char name[50]; - char *transfer_buffer[EM28XX_AUDIO_BUFS]; - struct urb *urb[EM28XX_AUDIO_BUFS]; - struct usb_device *udev; - unsigned int capture_transfer_done; - struct snd_pcm_substream *capture_pcm_substream; - - unsigned int hwptr_done_capture; - struct snd_card *sndcard; - - int users; - spinlock_t slock; -}; - -struct em28xx; - -struct em28xx_fh { - struct em28xx *dev; - int radio; - unsigned int resources; - - struct videobuf_queue vb_vidq; - struct videobuf_queue vb_vbiq; - - enum v4l2_buf_type type; -}; - -/* main device struct */ -struct em28xx { - /* generic device properties */ - char name[30]; /* name (including minor) of the device */ - int model; /* index in the device_data struct */ - int devno; /* marks the number of this device */ - enum em28xx_chip_id chip_id; - - int audio_ifnum; - - struct v4l2_device v4l2_dev; - struct em28xx_board board; - - /* Webcam specific fields */ - enum em28xx_sensor em28xx_sensor; - int sensor_xres, sensor_yres; - int sensor_xtal; - - /* Allows progressive (e. g. non-interlaced) mode */ - int progressive; - - /* Vinmode/Vinctl used at the driver */ - int vinmode, vinctl; - - unsigned int has_audio_class:1; - unsigned int has_alsa_audio:1; - unsigned int is_audio_only:1; - - /* Controls audio streaming */ - struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ - atomic_t stream_started; /* stream should be running if true */ - - struct em28xx_fmt *format; - - struct em28xx_IR *ir; - - /* Some older em28xx chips needs a waiting time after writing */ - unsigned int wait_after_write; - - struct list_head devlist; - - u32 i2s_speed; /* I2S speed for audio digital stream */ - - struct em28xx_audio_mode audio_mode; - - int tuner_type; /* type of the tuner */ - int tuner_addr; /* tuner address */ - int tda9887_conf; - /* i2c i/o */ - struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; - /* video for linux */ - int users; /* user count for exclusive use */ - struct video_device *vdev; /* video for linux device struct */ - v4l2_std_id norm; /* selected tv norm */ - int ctl_freq; /* selected frequency */ - unsigned int ctl_input; /* selected input */ - unsigned int ctl_ainput;/* selected audio input */ - unsigned int ctl_aoutput;/* selected audio output */ - int mute; - int volume; - /* frame properties */ - int width; /* current frame width */ - int height; /* current frame height */ - unsigned hscale; /* horizontal scale factor (see datasheet) */ - unsigned vscale; /* vertical scale factor (see datasheet) */ - int interlaced; /* 1=interlace fileds, 0=just top fileds */ - unsigned int video_bytesread; /* Number of bytes read */ - - unsigned long hash; /* eeprom hash - for boards with generic ID */ - unsigned long i2c_hash; /* i2c devicelist hash - - for boards with generic ID */ - - struct em28xx_audio adev; - - /* states */ - enum em28xx_dev_state state; - - /* vbi related state tracking */ - int capture_type; - int vbi_read; - unsigned char cur_field; - unsigned int vbi_width; - unsigned int vbi_height; /* lines per field */ - - struct work_struct request_module_wk; - - /* locks */ - struct mutex lock; - struct mutex ctrl_urb_lock; /* protects urb_buf */ - /* spinlock_t queue_lock; */ - struct list_head inqueue, outqueue; - struct video_device *vbi_dev; - struct video_device *radio_dev; - - /* resources in use */ - unsigned int resources; - - unsigned char eedata[256]; - - /* Isoc control struct */ - struct em28xx_dmaqueue vidq; - struct em28xx_dmaqueue vbiq; - struct em28xx_usb_isoc_ctl isoc_ctl; - spinlock_t slock; - - /* usb transfer */ - struct usb_device *udev; /* the usb device */ - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - int dvb_alt; /* alternate for DVB */ - unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ - char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ - - /* helper funcs that call usb_control_msg */ - int (*em28xx_write_regs) (struct em28xx *dev, u16 reg, - char *buf, int len); - int (*em28xx_read_reg) (struct em28xx *dev, u16 reg); - int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg, - char *buf, int len); - int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg, - char *buf, int len); - int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg); - - enum em28xx_mode mode; - - /* register numbers for GPO/GPIO registers */ - u16 reg_gpo_num, reg_gpio_num; - - /* Caches GPO and GPIO registers */ - unsigned char reg_gpo, reg_gpio; - - /* Snapshot button */ - char snapshot_button_path[30]; /* path of the input dev */ - struct input_dev *sbutton_input_dev; - struct delayed_work sbutton_query_work; - - struct em28xx_dvb *dvb; - - /* I2C keyboard data */ - struct IR_i2c_init_data init_data; -}; - -struct em28xx_ops { - struct list_head next; - char *name; - int id; - int (*init)(struct em28xx *); - int (*fini)(struct em28xx *); -}; - -/* Provided by em28xx-i2c.c */ -void em28xx_do_i2c_scan(struct em28xx *dev); -int em28xx_i2c_register(struct em28xx *dev); -int em28xx_i2c_unregister(struct em28xx *dev); - -/* Provided by em28xx-core.c */ - -u32 em28xx_request_buffers(struct em28xx *dev, u32 count); -void em28xx_queue_unusedframes(struct em28xx *dev); -void em28xx_release_buffers(struct em28xx *dev); - -int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, - char *buf, int len); -int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg); -int em28xx_read_reg(struct em28xx *dev, u16 reg); -int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, - int len); -int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); -int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val); -int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, - u8 bitmask); - -int em28xx_read_ac97(struct em28xx *dev, u8 reg); -int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); - -int em28xx_audio_analog_set(struct em28xx *dev); -int em28xx_audio_setup(struct em28xx *dev); - -int em28xx_colorlevels_set_default(struct em28xx *dev); -int em28xx_capture_start(struct em28xx *dev, int start); -int em28xx_vbi_supported(struct em28xx *dev); -int em28xx_set_outfmt(struct em28xx *dev); -int em28xx_resolution_set(struct em28xx *dev); -int em28xx_set_alternate(struct em28xx *dev); -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size); -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); -void em28xx_stop_urbs(struct em28xx *dev); -int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); -int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); -int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); -void em28xx_wake_i2c(struct em28xx *dev); -int em28xx_register_extension(struct em28xx_ops *dev); -void em28xx_unregister_extension(struct em28xx_ops *dev); -void em28xx_init_extension(struct em28xx *dev); -void em28xx_close_extension(struct em28xx *dev); - -/* Provided by em28xx-video.c */ -int em28xx_register_analog_devices(struct em28xx *dev); -void em28xx_release_analog_resources(struct em28xx *dev); - -/* Provided by em28xx-cards.c */ -extern int em2800_variant_detect(struct usb_device *udev, int model); -extern struct em28xx_board em28xx_boards[]; -extern struct usb_device_id em28xx_id_table[]; -extern const unsigned int em28xx_bcount; -int em28xx_tuner_callback(void *ptr, int component, int command, int arg); -void em28xx_release_resources(struct em28xx *dev); - -/* Provided by em28xx-vbi.c */ -extern struct videobuf_queue_ops em28xx_vbi_qops; - -/* printk macros */ - -#define em28xx_err(fmt, arg...) do {\ - printk(KERN_ERR fmt , ##arg); } while (0) - -#define em28xx_errdev(fmt, arg...) do {\ - printk(KERN_ERR "%s: "fmt,\ - dev->name , ##arg); } while (0) - -#define em28xx_info(fmt, arg...) do {\ - printk(KERN_INFO "%s: "fmt,\ - dev->name , ##arg); } while (0) -#define em28xx_warn(fmt, arg...) do {\ - printk(KERN_WARNING "%s: "fmt,\ - dev->name , ##arg); } while (0) - -static inline int em28xx_compression_disable(struct em28xx *dev) -{ - /* side effect of disabling scaler and mixer */ - return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00); -} - -static inline int em28xx_contrast_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f; -} - -static inline int em28xx_brightness_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R21_YOFFSET); -} - -static inline int em28xx_saturation_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f; -} - -static inline int em28xx_u_balance_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R23_UOFFSET); -} - -static inline int em28xx_v_balance_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R24_VOFFSET); -} - -static inline int em28xx_gamma_get(struct em28xx *dev) -{ - return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f; -} - -static inline int em28xx_contrast_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1); -} - -static inline int em28xx_brightness_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1); -} - -static inline int em28xx_saturation_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1); -} - -static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1); -} - -static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1); -} - -static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) -{ - u8 tmp = (u8) val; - return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1); -} - -/*FIXME: maxw should be dependent of alt mode */ -static inline unsigned int norm_maxw(struct em28xx *dev) -{ - if (dev->board.is_webcam) - return dev->sensor_xres; - - if (dev->board.max_range_640_480) - return 640; - - return 720; -} - -static inline unsigned int norm_maxh(struct em28xx *dev) -{ - if (dev->board.is_webcam) - return dev->sensor_yres; - - if (dev->board.max_range_640_480) - return 480; - - return (dev->norm & V4L2_STD_625_50) ? 576 : 480; -} -#endif diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig deleted file mode 100644 index dfe268bfa4f8..000000000000 --- a/drivers/media/video/gspca/Kconfig +++ /dev/null @@ -1,425 +0,0 @@ -menuconfig USB_GSPCA - tristate "GSPCA based webcams" - depends on VIDEO_V4L2 - default m - ---help--- - Say Y here if you want to enable selecting webcams based - on the GSPCA framework. - - See for more info. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as modules, choose M here: the - module will be called gspca_main. - - -if USB_GSPCA && VIDEO_V4L2 - -source "drivers/media/video/gspca/m5602/Kconfig" -source "drivers/media/video/gspca/stv06xx/Kconfig" -source "drivers/media/video/gspca/gl860/Kconfig" - -config USB_GSPCA_BENQ - tristate "Benq USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for the Benq DC E300 camera. - - To compile this driver as a module, choose M here: the - module will be called gspca_benq. - -config USB_GSPCA_CONEX - tristate "Conexant Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the Conexant chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_conex. - -config USB_GSPCA_CPIA1 - tristate "cpia CPiA (version 1) Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for USB cameras based on the cpia - CPiA chip. Note that you need atleast version 0.6.4 of libv4l for - applications to understand the videoformat generated by this driver. - - To compile this driver as a module, choose M here: the - module will be called gspca_cpia1. - -config USB_GSPCA_ETOMS - tristate "Etoms USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the Etoms chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_etoms. - -config USB_GSPCA_FINEPIX - tristate "Fujifilm FinePix USB V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the FinePix chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_finepix. - -config USB_GSPCA_JEILINJ - tristate "Jeilin JPEG USB V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on this Jeilin chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_jeilinj. - -config USB_GSPCA_JL2005BCD - tristate "JL2005B/C/D USB V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based the - JL2005B, JL2005C, or JL2005D chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_jl2005bcd. - -config USB_GSPCA_KINECT - tristate "Kinect sensor device USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for the Microsoft Kinect sensor device. - - To compile this driver as a module, choose M here: the - module will be called gspca_kinect. - -config USB_GSPCA_KONICA - tristate "Konica USB Camera V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the Konica chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_konica. - -config USB_GSPCA_MARS - tristate "Mars USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the Mars chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_mars. - -config USB_GSPCA_MR97310A - tristate "Mars-Semi MR97310A USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the MR97310A chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_mr97310a. - -config USB_GSPCA_NW80X - tristate "Divio based (NW80x) USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the NW80x chips. - - To compile this driver as a module, choose M here: the - module will be called gspca_nw80x. - -config USB_GSPCA_OV519 - tristate "OV51x / OVFX2 / W996xCF USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on one of these: - OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF - - To compile this driver as a module, choose M here: the - module will be called gspca_ov519. - -config USB_GSPCA_OV534 - tristate "OV534 OV772x USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the OV534 chip - and sensor OV772x (e.g. Sony Playstation EYE) - - To compile this driver as a module, choose M here: the - module will be called gspca_ov534. - -config USB_GSPCA_OV534_9 - tristate "OV534 OV965x USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the OV534 chip - and sensor OV965x (e.g. Hercules Dualpix) - - To compile this driver as a module, choose M here: the - module will be called gspca_ov534_9. - -config USB_GSPCA_PAC207 - tristate "Pixart PAC207 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the PAC207 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_pac207. - -config USB_GSPCA_PAC7302 - tristate "Pixart PAC7302 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the PAC7302 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_pac7302. - -config USB_GSPCA_PAC7311 - tristate "Pixart PAC7311 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the PAC7311 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_pac7311. - -config USB_GSPCA_SE401 - tristate "SE401 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the - Endpoints (formerly known as AOX) se401 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_se401. - -config USB_GSPCA_SN9C2028 - tristate "SONIX Dual-Mode USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want streaming support for Sonix SN9C2028 cameras. - These are supported as stillcams in libgphoto2/camlibs/sonix. - - To compile this driver as a module, choose M here: the - module will be called gspca_sn9c2028. - -config USB_GSPCA_SN9C20X - tristate "SN9C20X USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the - sn9c20x chips (SN9C201 and SN9C202). - - To compile this driver as a module, choose M here: the - module will be called gspca_sn9c20x. - -config USB_GSPCA_SONIXB - tristate "SONIX Bayer USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the Sonix - chips with Bayer format (SN9C101, SN9C102 and SN9C103). - - To compile this driver as a module, choose M here: the - module will be called gspca_sonixb. - -config USB_GSPCA_SONIXJ - tristate "SONIX JPEG USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the Sonix - chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110). - - To compile this driver as a module, choose M here: the - module will be called gspca_sonixj - -config USB_GSPCA_SPCA500 - tristate "SPCA500 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA500 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca500. - -config USB_GSPCA_SPCA501 - tristate "SPCA501 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA501 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca501. - -config USB_GSPCA_SPCA505 - tristate "SPCA505 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA505 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca505. - -config USB_GSPCA_SPCA506 - tristate "SPCA506 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA506 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca506. - -config USB_GSPCA_SPCA508 - tristate "SPCA508 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA508 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca508. - -config USB_GSPCA_SPCA561 - tristate "SPCA561 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA561 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca561. - -config USB_GSPCA_SPCA1528 - tristate "SPCA1528 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA1528 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca1528. - -config USB_GSPCA_SQ905 - tristate "SQ Technologies SQ905 based USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SQ905 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_sq905. - -config USB_GSPCA_SQ905C - tristate "SQ Technologies SQ905C based USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SQ905C chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_sq905c. - -config USB_GSPCA_SQ930X - tristate "SQ Technologies SQ930X based USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SQ930X chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_sq930x. - -config USB_GSPCA_STK014 - tristate "Syntek DV4000 (STK014) USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the STK014 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_stk014. - -config USB_GSPCA_STV0680 - tristate "STV0680 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the STV0680 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_stv0680. - -config USB_GSPCA_SUNPLUS - tristate "SUNPLUS USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the Sunplus - SPCA504(abc) SPCA533 SPCA536 chips. - - To compile this driver as a module, choose M here: the - module will be called gspca_sunplus. - -config USB_GSPCA_T613 - tristate "T613 (JPEG Compliance) USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the T613 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_t613. - -config USB_GSPCA_TOPRO - tristate "TOPRO USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the - TP6800 and TP6810 Topro chips. - - To compile this driver as a module, choose M here: the - module will be called gspca_topro. - -config USB_GSPCA_TV8532 - tristate "TV8532 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the TV8531 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_tv8532. - -config USB_GSPCA_VC032X - tristate "VC032X USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the VC032X chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_vc032x. - -config USB_GSPCA_VICAM - tristate "ViCam USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for the 3com homeconnect camera - (vicam). - - To compile this driver as a module, choose M here: the - module will be called gspca_vicam. - -config USB_GSPCA_XIRLINK_CIT - tristate "Xirlink C-It USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for Xirlink C-It bases cameras. - - To compile this driver as a module, choose M here: the - module will be called gspca_xirlink_cit. - -config USB_GSPCA_ZC3XX - tristate "ZC3XX USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the ZC3XX chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_zc3xx. - -endif diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile deleted file mode 100644 index c901da0bd657..000000000000 --- a/drivers/media/video/gspca/Makefile +++ /dev/null @@ -1,93 +0,0 @@ -obj-$(CONFIG_USB_GSPCA) += gspca_main.o -obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o -obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o -obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o -obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o -obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o -obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o -obj-$(CONFIG_USB_GSPCA_JL2005BCD) += gspca_jl2005bcd.o -obj-$(CONFIG_USB_GSPCA_KINECT) += gspca_kinect.o -obj-$(CONFIG_USB_GSPCA_KONICA) += gspca_konica.o -obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o -obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o -obj-$(CONFIG_USB_GSPCA_NW80X) += gspca_nw80x.o -obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o -obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o -obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o -obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o -obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o -obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o -obj-$(CONFIG_USB_GSPCA_SE401) += gspca_se401.o -obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o -obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o -obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o -obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o -obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o -obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o -obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o -obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o -obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o -obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o -obj-$(CONFIG_USB_GSPCA_SPCA1528) += gspca_spca1528.o -obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o -obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o -obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o -obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o -obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o -obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o -obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o -obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o -obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o -obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o -obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o -obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o -obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o - -gspca_main-objs := gspca.o autogain_functions.o -gspca_benq-objs := benq.o -gspca_conex-objs := conex.o -gspca_cpia1-objs := cpia1.o -gspca_etoms-objs := etoms.o -gspca_finepix-objs := finepix.o -gspca_jeilinj-objs := jeilinj.o -gspca_jl2005bcd-objs := jl2005bcd.o -gspca_kinect-objs := kinect.o -gspca_konica-objs := konica.o -gspca_mars-objs := mars.o -gspca_mr97310a-objs := mr97310a.o -gspca_nw80x-objs := nw80x.o -gspca_ov519-objs := ov519.o -gspca_ov534-objs := ov534.o -gspca_ov534_9-objs := ov534_9.o -gspca_pac207-objs := pac207.o -gspca_pac7302-objs := pac7302.o -gspca_pac7311-objs := pac7311.o -gspca_se401-objs := se401.o -gspca_sn9c2028-objs := sn9c2028.o -gspca_sn9c20x-objs := sn9c20x.o -gspca_sonixb-objs := sonixb.o -gspca_sonixj-objs := sonixj.o -gspca_spca500-objs := spca500.o -gspca_spca501-objs := spca501.o -gspca_spca505-objs := spca505.o -gspca_spca506-objs := spca506.o -gspca_spca508-objs := spca508.o -gspca_spca561-objs := spca561.o -gspca_spca1528-objs := spca1528.o -gspca_sq905-objs := sq905.o -gspca_sq905c-objs := sq905c.o -gspca_sq930x-objs := sq930x.o -gspca_stk014-objs := stk014.o -gspca_stv0680-objs := stv0680.o -gspca_sunplus-objs := sunplus.o -gspca_t613-objs := t613.o -gspca_topro-objs := topro.o -gspca_tv8532-objs := tv8532.o -gspca_vc032x-objs := vc032x.o -gspca_vicam-objs := vicam.o -gspca_xirlink_cit-objs := xirlink_cit.o -gspca_zc3xx-objs := zc3xx.o - -obj-$(CONFIG_USB_M5602) += m5602/ -obj-$(CONFIG_USB_STV06XX) += stv06xx/ -obj-$(CONFIG_USB_GL860) += gl860/ diff --git a/drivers/media/video/gspca/autogain_functions.c b/drivers/media/video/gspca/autogain_functions.c deleted file mode 100644 index 67db674bb044..000000000000 --- a/drivers/media/video/gspca/autogain_functions.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Functions for auto gain. - * - * Copyright (C) 2010-2012 Hans de Goede - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "gspca.h" - -/* auto gain and exposure algorithm based on the knee algorithm described here: - http://ytse.tricolour.net/docs/LowLightOptimization.html - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -int gspca_expo_autogain( - struct gspca_dev *gspca_dev, - int avg_lum, - int desired_avg_lum, - int deadzone, - int gain_knee, - int exposure_knee) -{ - s32 gain, orig_gain, exposure, orig_exposure; - int i, steps, retval = 0; - - if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) - return 0; - - orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); - orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = abs(desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - for (i = 0; i < steps; i++) { - if (avg_lum > desired_avg_lum) { - if (gain > gain_knee) - gain--; - else if (exposure > exposure_knee) - exposure--; - else if (gain > gspca_dev->gain->default_value) - gain--; - else if (exposure > gspca_dev->exposure->minimum) - exposure--; - else if (gain > gspca_dev->gain->minimum) - gain--; - else - break; - } else { - if (gain < gspca_dev->gain->default_value) - gain++; - else if (exposure < exposure_knee) - exposure++; - else if (gain < gain_knee) - gain++; - else if (exposure < gspca_dev->exposure->maximum) - exposure++; - else if (gain < gspca_dev->gain->maximum) - gain++; - else - break; - } - } - - if (gain != orig_gain) { - v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); - retval = 1; - } - if (exposure != orig_exposure) { - v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); - retval = 1; - } - - if (retval) - PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", - gain, exposure); - return retval; -} -EXPORT_SYMBOL(gspca_expo_autogain); - -/* Autogain + exposure algorithm for cameras with a coarse exposure control - (usually this means we can only control the clockdiv to change exposure) - As changing the clockdiv so that the fps drops from 30 to 15 fps for - example, will lead to a huge exposure change (it effectively doubles), - this algorithm normally tries to only adjust the gain (between 40 and - 80 %) and if that does not help, only then changes exposure. This leads - to a much more stable image then using the knee algorithm which at - certain points of the knee graph will only try to adjust exposure, - which leads to oscilating as one exposure step is huge. - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -int gspca_coarse_grained_expo_autogain( - struct gspca_dev *gspca_dev, - int avg_lum, - int desired_avg_lum, - int deadzone) -{ - s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure; - int steps, retval = 0; - - if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) - return 0; - - orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); - orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - - gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / - 5 * 2 + gspca_dev->gain->minimum; - gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / - 5 * 4 + gspca_dev->gain->minimum; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = (desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - if ((gain + steps) > gain_high && - exposure < gspca_dev->exposure->maximum) { - gain = gain_high; - gspca_dev->exp_too_low_cnt++; - gspca_dev->exp_too_high_cnt = 0; - } else if ((gain + steps) < gain_low && - exposure > gspca_dev->exposure->minimum) { - gain = gain_low; - gspca_dev->exp_too_high_cnt++; - gspca_dev->exp_too_low_cnt = 0; - } else { - gain += steps; - if (gain > gspca_dev->gain->maximum) - gain = gspca_dev->gain->maximum; - else if (gain < gspca_dev->gain->minimum) - gain = gspca_dev->gain->minimum; - gspca_dev->exp_too_high_cnt = 0; - gspca_dev->exp_too_low_cnt = 0; - } - - if (gspca_dev->exp_too_high_cnt > 3) { - exposure--; - gspca_dev->exp_too_high_cnt = 0; - } else if (gspca_dev->exp_too_low_cnt > 3) { - exposure++; - gspca_dev->exp_too_low_cnt = 0; - } - - if (gain != orig_gain) { - v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); - retval = 1; - } - if (exposure != orig_exposure) { - v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); - retval = 1; - } - - if (retval) - PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", - gain, exposure); - return retval; -} -EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain); diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/video/gspca/autogain_functions.h deleted file mode 100644 index d625eafe63eb..000000000000 --- a/drivers/media/video/gspca/autogain_functions.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Functions for auto gain. - * - * Copyright (C) 2010-2011 Hans de Goede - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef WANT_REGULAR_AUTOGAIN -/* auto gain and exposure algorithm based on the knee algorithm described here: - http://ytse.tricolour.net/docs/LowLightOptimization.html - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -static inline int auto_gain_n_exposure( - struct gspca_dev *gspca_dev, - int avg_lum, - int desired_avg_lum, - int deadzone, - int gain_knee, - int exposure_knee) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, steps, gain, orig_gain, exposure, orig_exposure; - int retval = 0; - - orig_gain = gain = sd->ctrls[GAIN].val; - orig_exposure = exposure = sd->ctrls[EXPOSURE].val; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = abs(desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - for (i = 0; i < steps; i++) { - if (avg_lum > desired_avg_lum) { - if (gain > gain_knee) - gain--; - else if (exposure > exposure_knee) - exposure--; - else if (gain > sd->ctrls[GAIN].def) - gain--; - else if (exposure > sd->ctrls[EXPOSURE].min) - exposure--; - else if (gain > sd->ctrls[GAIN].min) - gain--; - else - break; - } else { - if (gain < sd->ctrls[GAIN].def) - gain++; - else if (exposure < exposure_knee) - exposure++; - else if (gain < gain_knee) - gain++; - else if (exposure < sd->ctrls[EXPOSURE].max) - exposure++; - else if (gain < sd->ctrls[GAIN].max) - gain++; - else - break; - } - } - - if (gain != orig_gain) { - sd->ctrls[GAIN].val = gain; - setgain(gspca_dev); - retval = 1; - } - if (exposure != orig_exposure) { - sd->ctrls[EXPOSURE].val = exposure; - setexposure(gspca_dev); - retval = 1; - } - - if (retval) - PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", - gain, exposure); - return retval; -} -#endif - -#ifdef WANT_COARSE_EXPO_AUTOGAIN -/* Autogain + exposure algorithm for cameras with a coarse exposure control - (usually this means we can only control the clockdiv to change exposure) - As changing the clockdiv so that the fps drops from 30 to 15 fps for - example, will lead to a huge exposure change (it effectively doubles), - this algorithm normally tries to only adjust the gain (between 40 and - 80 %) and if that does not help, only then changes exposure. This leads - to a much more stable image then using the knee algorithm which at - certain points of the knee graph will only try to adjust exposure, - which leads to oscilating as one exposure step is huge. - - Note this assumes that the sd struct for the cam in question has - exp_too_low_cnt and exp_too_high_cnt int members for use by this function. - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -static inline int coarse_grained_expo_autogain( - struct gspca_dev *gspca_dev, - int avg_lum, - int desired_avg_lum, - int deadzone) -{ - struct sd *sd = (struct sd *) gspca_dev; - int steps, gain, orig_gain, exposure, orig_exposure; - int gain_low, gain_high; - int retval = 0; - - orig_gain = gain = sd->ctrls[GAIN].val; - orig_exposure = exposure = sd->ctrls[EXPOSURE].val; - - gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2; - gain_low += sd->ctrls[GAIN].min; - gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4; - gain_high += sd->ctrls[GAIN].min; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = (desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - if ((gain + steps) > gain_high && - exposure < sd->ctrls[EXPOSURE].max) { - gain = gain_high; - sd->exp_too_low_cnt++; - sd->exp_too_high_cnt = 0; - } else if ((gain + steps) < gain_low && - exposure > sd->ctrls[EXPOSURE].min) { - gain = gain_low; - sd->exp_too_high_cnt++; - sd->exp_too_low_cnt = 0; - } else { - gain += steps; - if (gain > sd->ctrls[GAIN].max) - gain = sd->ctrls[GAIN].max; - else if (gain < sd->ctrls[GAIN].min) - gain = sd->ctrls[GAIN].min; - sd->exp_too_high_cnt = 0; - sd->exp_too_low_cnt = 0; - } - - if (sd->exp_too_high_cnt > 3) { - exposure--; - sd->exp_too_high_cnt = 0; - } else if (sd->exp_too_low_cnt > 3) { - exposure++; - sd->exp_too_low_cnt = 0; - } - - if (gain != orig_gain) { - sd->ctrls[GAIN].val = gain; - setgain(gspca_dev); - retval = 1; - } - if (exposure != orig_exposure) { - sd->ctrls[EXPOSURE].val = exposure; - setexposure(gspca_dev); - retval = 1; - } - - if (retval) - PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", - gain, exposure); - return retval; -} -#endif diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c deleted file mode 100644 index 352f32190e68..000000000000 --- a/drivers/media/video/gspca/benq.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Benq DC E300 subdriver - * - * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "benq" - -#include "gspca.h" - -MODULE_AUTHOR("Jean-Francois Moine "); -MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, -}; - -static void sd_isoc_irq(struct urb *urb); - -/* -- write a register -- */ -static void reg_w(struct gspca_dev *gspca_dev, - u16 value, u16 index) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x02, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - NULL, - 0, - 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - gspca_dev->cam.cam_mode = vga_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->cam.no_urb_create = 1; - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct urb *urb; - int i, n; - - /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */ -#if MAX_NURBS < 4 -#error "Not enough URBs in the gspca table" -#endif -#define SD_PKT_SZ 64 -#define SD_NPKT 32 - for (n = 0; n < 4; n++) { - urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); - if (!urb) { - pr_err("usb_alloc_urb failed\n"); - return -ENOMEM; - } - gspca_dev->urb[n] = urb; - urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, - SD_PKT_SZ * SD_NPKT, - GFP_KERNEL, - &urb->transfer_dma); - - if (urb->transfer_buffer == NULL) { - pr_err("usb_alloc_coherent failed\n"); - return -ENOMEM; - } - urb->dev = gspca_dev->dev; - urb->context = gspca_dev; - urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT; - urb->pipe = usb_rcvisocpipe(gspca_dev->dev, - n & 1 ? 0x82 : 0x83); - urb->transfer_flags = URB_ISO_ASAP - | URB_NO_TRANSFER_DMA_MAP; - urb->interval = 1; - urb->complete = sd_isoc_irq; - urb->number_of_packets = SD_NPKT; - for (i = 0; i < SD_NPKT; i++) { - urb->iso_frame_desc[i].length = SD_PKT_SZ; - urb->iso_frame_desc[i].offset = SD_PKT_SZ * i; - } - } - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct usb_interface *intf; - - reg_w(gspca_dev, 0x003c, 0x0003); - reg_w(gspca_dev, 0x003c, 0x0004); - reg_w(gspca_dev, 0x003c, 0x0005); - reg_w(gspca_dev, 0x003c, 0x0006); - reg_w(gspca_dev, 0x003c, 0x0007); - - intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - usb_set_interface(gspca_dev->dev, gspca_dev->iface, - intf->num_altsetting - 1); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - /* unused */ -} - -/* reception of an URB */ -static void sd_isoc_irq(struct urb *urb) -{ - struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - struct urb *urb0; - u8 *data; - int i, st; - - PDEBUG(D_PACK, "sd isoc irq"); - if (!gspca_dev->streaming) - return; - if (urb->status != 0) { - if (urb->status == -ESHUTDOWN) - return; /* disconnection */ -#ifdef CONFIG_PM - if (gspca_dev->frozen) - return; -#endif - pr_err("urb status: %d\n", urb->status); - return; - } - - /* if this is a control URN (ep 0x83), wait */ - if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2]) - return; - - /* scan both received URBs */ - if (urb == gspca_dev->urb[1]) - urb0 = gspca_dev->urb[0]; - else - urb0 = gspca_dev->urb[2]; - for (i = 0; i < urb->number_of_packets; i++) { - - /* check the packet status and length */ - if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ - || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) { - PDEBUG(D_ERR, "ISOC bad lengths %d / %d", - urb0->iso_frame_desc[i].actual_length, - urb->iso_frame_desc[i].actual_length); - gspca_dev->last_packet_type = DISCARD_PACKET; - continue; - } - st = urb0->iso_frame_desc[i].status; - if (st == 0) - st = urb->iso_frame_desc[i].status; - if (st) { - pr_err("ISOC data error: [%d] status=%d\n", - i, st); - gspca_dev->last_packet_type = DISCARD_PACKET; - continue; - } - - /* - * The images are received in URBs of different endpoints - * (0x83 and 0x82). - * Image pieces in URBs of ep 0x83 are continuated in URBs of - * ep 0x82 of the same index. - * The packets in the URBs of endpoint 0x83 start with: - * - 80 ba/bb 00 00 = start of image followed by 'ff d8' - * - 04 ba/bb oo oo = image piece - * where 'oo oo' is the image offset - (not cheked) - * - (other -> bad frame) - * The images are JPEG encoded with full header and - * normal ff escape. - * The end of image ('ff d9') may occur in any URB. - * (not cheked) - */ - data = (u8 *) urb0->transfer_buffer - + urb0->iso_frame_desc[i].offset; - if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) { - - /* new image */ - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, - data + 4, SD_PKT_SZ - 4); - } else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) { - gspca_frame_add(gspca_dev, INTER_PACKET, - data + 4, SD_PKT_SZ - 4); - } else { - gspca_dev->last_packet_type = DISCARD_PACKET; - continue; - } - data = (u8 *) urb->transfer_buffer - + urb->iso_frame_desc[i].offset; - gspca_frame_add(gspca_dev, INTER_PACKET, - data, SD_PKT_SZ); - } - - /* resubmit the URBs */ - st = usb_submit_urb(urb0, GFP_ATOMIC); - if (st < 0) - pr_err("usb_submit_urb(0) ret %d\n", st); - st = usb_submit_urb(urb, GFP_ATOMIC); - if (st < 0) - pr_err("usb_submit_urb() ret %d\n", st); -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x04a5, 0x3035)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c deleted file mode 100644 index c9052f20435e..000000000000 --- a/drivers/media/video/gspca/conex.c +++ /dev/null @@ -1,966 +0,0 @@ -/* - * Connexant Cx11646 library - * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "conex" - -#include "gspca.h" -#define CONEX_CAM 1 /* special JPEG header */ -#include "jpeg.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver"); -MODULE_LICENSE("GPL"); - -#define QUALITY 50 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *sat; - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -static const struct v4l2_pix_format vga_mode[] = { - {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 3}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -/* the read bytes are found in gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 index, - __u16 len) -{ - struct usb_device *dev = gspca_dev->dev; - -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); - return; - } -#endif - usb_control_msg(dev, - usb_rcvctrlpipe(dev, 0), - 0, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - index, gspca_dev->usb_buf, len, - 500); - PDEBUG(D_USBI, "reg read [%02x] -> %02x ..", - index, gspca_dev->usb_buf[0]); -} - -/* the bytes to write are in gspca_dev->usb_buf */ -static void reg_w_val(struct gspca_dev *gspca_dev, - __u16 index, - __u8 val) -{ - struct usb_device *dev = gspca_dev->dev; - - gspca_dev->usb_buf[0] = val; - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - index, gspca_dev->usb_buf, 1, 500); -} - -static void reg_w(struct gspca_dev *gspca_dev, - __u16 index, - const __u8 *buffer, - __u16 len) -{ - struct usb_device *dev = gspca_dev->dev; - -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - pr_err("reg_w: buffer overflow\n"); - return; - } - PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); -#endif - memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - index, gspca_dev->usb_buf, len, 500); -} - -static const __u8 cx_sensor_init[][4] = { - {0x88, 0x11, 0x01, 0x01}, - {0x88, 0x12, 0x70, 0x01}, - {0x88, 0x0f, 0x00, 0x01}, - {0x88, 0x05, 0x01, 0x01}, - {} -}; - -static const __u8 cx11646_fw1[][3] = { - {0x00, 0x02, 0x00}, - {0x01, 0x43, 0x00}, - {0x02, 0xA7, 0x00}, - {0x03, 0x8B, 0x01}, - {0x04, 0xE9, 0x02}, - {0x05, 0x08, 0x04}, - {0x06, 0x08, 0x05}, - {0x07, 0x07, 0x06}, - {0x08, 0xE7, 0x06}, - {0x09, 0xC6, 0x07}, - {0x0A, 0x86, 0x08}, - {0x0B, 0x46, 0x09}, - {0x0C, 0x05, 0x0A}, - {0x0D, 0xA5, 0x0A}, - {0x0E, 0x45, 0x0B}, - {0x0F, 0xE5, 0x0B}, - {0x10, 0x85, 0x0C}, - {0x11, 0x25, 0x0D}, - {0x12, 0xC4, 0x0D}, - {0x13, 0x45, 0x0E}, - {0x14, 0xE4, 0x0E}, - {0x15, 0x64, 0x0F}, - {0x16, 0xE4, 0x0F}, - {0x17, 0x64, 0x10}, - {0x18, 0xE4, 0x10}, - {0x19, 0x64, 0x11}, - {0x1A, 0xE4, 0x11}, - {0x1B, 0x64, 0x12}, - {0x1C, 0xE3, 0x12}, - {0x1D, 0x44, 0x13}, - {0x1E, 0xC3, 0x13}, - {0x1F, 0x24, 0x14}, - {0x20, 0xA3, 0x14}, - {0x21, 0x04, 0x15}, - {0x22, 0x83, 0x15}, - {0x23, 0xE3, 0x15}, - {0x24, 0x43, 0x16}, - {0x25, 0xA4, 0x16}, - {0x26, 0x23, 0x17}, - {0x27, 0x83, 0x17}, - {0x28, 0xE3, 0x17}, - {0x29, 0x43, 0x18}, - {0x2A, 0xA3, 0x18}, - {0x2B, 0x03, 0x19}, - {0x2C, 0x63, 0x19}, - {0x2D, 0xC3, 0x19}, - {0x2E, 0x22, 0x1A}, - {0x2F, 0x63, 0x1A}, - {0x30, 0xC3, 0x1A}, - {0x31, 0x23, 0x1B}, - {0x32, 0x83, 0x1B}, - {0x33, 0xE2, 0x1B}, - {0x34, 0x23, 0x1C}, - {0x35, 0x83, 0x1C}, - {0x36, 0xE2, 0x1C}, - {0x37, 0x23, 0x1D}, - {0x38, 0x83, 0x1D}, - {0x39, 0xE2, 0x1D}, - {0x3A, 0x23, 0x1E}, - {0x3B, 0x82, 0x1E}, - {0x3C, 0xC3, 0x1E}, - {0x3D, 0x22, 0x1F}, - {0x3E, 0x63, 0x1F}, - {0x3F, 0xC1, 0x1F}, - {} -}; -static void cx11646_fw(struct gspca_dev*gspca_dev) -{ - int i = 0; - - reg_w_val(gspca_dev, 0x006a, 0x02); - while (cx11646_fw1[i][1]) { - reg_w(gspca_dev, 0x006b, cx11646_fw1[i], 3); - i++; - } - reg_w_val(gspca_dev, 0x006a, 0x00); -} - -static const __u8 cxsensor[] = { - 0x88, 0x12, 0x70, 0x01, - 0x88, 0x0d, 0x02, 0x01, - 0x88, 0x0f, 0x00, 0x01, - 0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, /* 3 */ - 0x88, 0x02, 0x10, 0x01, - 0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, /* 5 */ - 0x88, 0x0B, 0x00, 0x01, - 0x88, 0x0A, 0x0A, 0x01, - 0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, /* 8 */ - 0x88, 0x05, 0x01, 0x01, - 0xA1, 0x18, 0x00, 0x01, - 0x00 -}; - -static const __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff }; -static const __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff }; -static const __u8 reg10[] = { 0xb1, 0xb1 }; -static const __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e }; /* 640 */ -static const __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f }; - /* 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 */ -static const __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 }; - /* 320{0x04,0x0c,0x05,0x0f}; //320 */ -static const __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 }; /* 176 */ -static const __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }; - -static void cx_sensor(struct gspca_dev*gspca_dev) -{ - int i = 0; - int length; - const __u8 *ptsensor = cxsensor; - - reg_w(gspca_dev, 0x0020, reg20, 8); - reg_w(gspca_dev, 0x0028, reg28, 8); - reg_w(gspca_dev, 0x0010, reg10, 2); - reg_w_val(gspca_dev, 0x0092, 0x03); - - switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 0: - reg_w(gspca_dev, 0x0071, reg71a, 4); - break; - case 1: - reg_w(gspca_dev, 0x0071, reg71b, 4); - break; - default: -/* case 2: */ - reg_w(gspca_dev, 0x0071, reg71c, 4); - break; - case 3: - reg_w(gspca_dev, 0x0071, reg71d, 4); - break; - } - reg_w(gspca_dev, 0x007b, reg7b, 6); - reg_w_val(gspca_dev, 0x00f8, 0x00); - reg_w(gspca_dev, 0x0010, reg10, 2); - reg_w_val(gspca_dev, 0x0098, 0x41); - for (i = 0; i < 11; i++) { - if (i == 3 || i == 5 || i == 8) - length = 8; - else - length = 4; - reg_w(gspca_dev, 0x00e5, ptsensor, length); - if (length == 4) - reg_r(gspca_dev, 0x00e8, 1); - else - reg_r(gspca_dev, 0x00e8, length); - ptsensor += length; - } - reg_r(gspca_dev, 0x00e7, 8); -} - -static const __u8 cx_inits_176[] = { - 0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, /* 176x144 */ - 0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03, - 0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30, - 0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF, - 0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02, - 0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -static const __u8 cx_inits_320[] = { - 0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01, - 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01, - 0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81, - 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, - 0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02, - 0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -static const __u8 cx_inits_352[] = { - 0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03, - 0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b, - 0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25, - 0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00, - 0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, - 0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02, - 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -static const __u8 cx_inits_640[] = { - 0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01, - 0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81, - 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, - 0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02, - 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static void cx11646_initsize(struct gspca_dev *gspca_dev) -{ - const __u8 *cxinit; - static const __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 }; - static const __u8 reg17[] = - { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 }; - - switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 0: - cxinit = cx_inits_640; - break; - case 1: - cxinit = cx_inits_352; - break; - default: -/* case 2: */ - cxinit = cx_inits_320; - break; - case 3: - cxinit = cx_inits_176; - break; - } - reg_w_val(gspca_dev, 0x009a, 0x01); - reg_w_val(gspca_dev, 0x0010, 0x10); - reg_w(gspca_dev, 0x0012, reg12, 5); - reg_w(gspca_dev, 0x0017, reg17, 8); - reg_w_val(gspca_dev, 0x00c0, 0x00); - reg_w_val(gspca_dev, 0x00c1, 0x04); - reg_w_val(gspca_dev, 0x00c2, 0x04); - - reg_w(gspca_dev, 0x0061, cxinit, 8); - cxinit += 8; - reg_w(gspca_dev, 0x00ca, cxinit, 8); - cxinit += 8; - reg_w(gspca_dev, 0x00d2, cxinit, 8); - cxinit += 8; - reg_w(gspca_dev, 0x00da, cxinit, 6); - cxinit += 8; - reg_w(gspca_dev, 0x0041, cxinit, 8); - cxinit += 8; - reg_w(gspca_dev, 0x0049, cxinit, 8); - cxinit += 8; - reg_w(gspca_dev, 0x0051, cxinit, 2); - - reg_r(gspca_dev, 0x0010, 1); -} - -static const __u8 cx_jpeg_init[][8] = { - {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x15}, /* 1 */ - {0x0f, 0x10, 0x12, 0x10, 0x0d, 0x15, 0x12, 0x11}, - {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22}, - {0x20, 0x1d, 0x1d, 0x20, 0x41, 0x2e, 0x31, 0x26}, - {0x35, 0x4d, 0x43, 0x51, 0x4f, 0x4b, 0x43, 0x4a}, - {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73}, - {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D}, - {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0}, - {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01}, - {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12}, - {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35}, - {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31}, - {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43}, - {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A}, - {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73}, - {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95}, - {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83}, - {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05}, - {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, - {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, - {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01}, - {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, - {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00}, - {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05}, - {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01}, - {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21}, - {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22}, - {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23}, - {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24}, - {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17}, - {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29}, - {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A}, - {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A}, - {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A}, - {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A}, - {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A}, - {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A}, - {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99}, - {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8}, - {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, - {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6}, - {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5}, - {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3}, - {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1}, - {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9}, - {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04}, - {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01}, - {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04}, - {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07}, - {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14}, - {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33}, - {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16}, - {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19}, - {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36}, - {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46}, - {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56}, - {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66}, - {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76}, - {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85}, - {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94}, - {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3}, - {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2}, - {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA}, - {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9}, - {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8}, - {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7}, - {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6}, - {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F}, - {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22}, - {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11}, - {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00}, - {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08}, - {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00}, - {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA}, - {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02}, - {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00} /* 79 */ -}; - - -static const __u8 cxjpeg_640[][8] = { - {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x10}, /* 1 */ - {0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d}, - {0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a}, - {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d}, - {0x28, 0x3a, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38}, - {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57}, - {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F}, - {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79}, - {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01}, - {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E}, - {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28}, - {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25}, - {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33}, - {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44}, - {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57}, - {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71}, - {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63}, - {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00}, - {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, - {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, - {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, - {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF}, - {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80}, - {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, - {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, - {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, - {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ -}; -static const __u8 cxjpeg_352[][8] = { - {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, - {0x09, 0x09, 0x0b, 0x09, 0x08, 0x0D, 0x0b, 0x0a}, - {0x0b, 0x0e, 0x0d, 0x0d, 0x0f, 0x13, 0x1f, 0x14}, - {0x13, 0x11, 0x11, 0x13, 0x26, 0x1b, 0x1d, 0x17}, - {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, - {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, - {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, - {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, - {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, - {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, - {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, - {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, - {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, - {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, - {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, - {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, - {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, - {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00}, - {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, - {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, - {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, - {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF}, - {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60}, - {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, - {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, - {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, - {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -}; -static const __u8 cxjpeg_320[][8] = { - {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x05}, - {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04}, - {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08}, - {0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09}, - {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0f, 0x11}, - {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A}, - {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D}, - {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24}, - {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01}, - {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04}, - {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C}, - {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B}, - {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F}, - {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14}, - {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A}, - {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22}, - {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E}, - {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00}, - {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, - {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, - {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, - {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF}, - {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40}, - {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, - {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, - {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, - {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ -}; -static const __u8 cxjpeg_176[][8] = { - {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, - {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, - {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, - {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, - {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, - {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, - {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, - {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, - {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, - {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, - {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, - {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, - {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, - {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, - {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, - {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, - {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, - {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00}, - {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, - {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, - {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, - {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF}, - {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0}, - {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, - {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, - {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, - {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -}; -/* 640 take with the zcx30x part */ -static const __u8 cxjpeg_qtable[][8] = { - {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08}, - {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07}, - {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a}, - {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f}, - {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c}, - {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c}, - {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30}, - {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d}, - {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01}, - {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a}, - {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32}, - {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, - {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, - {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, - {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, - {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, - {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, - {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 18 */ -}; - - -static void cx11646_jpegInit(struct gspca_dev*gspca_dev) -{ - int i; - int length; - - reg_w_val(gspca_dev, 0x00c0, 0x01); - reg_w_val(gspca_dev, 0x00c3, 0x00); - reg_w_val(gspca_dev, 0x00c0, 0x00); - reg_r(gspca_dev, 0x0001, 1); - length = 8; - for (i = 0; i < 79; i++) { - if (i == 78) - length = 6; - reg_w(gspca_dev, 0x0008, cx_jpeg_init[i], length); - } - reg_r(gspca_dev, 0x0002, 1); - reg_w_val(gspca_dev, 0x0055, 0x14); -} - -static const __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 }; -static const __u8 regE5_8[] = - { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; -static const __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 }; -static const __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 }; -static const __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 }; -static const __u8 reg51[] = { 0x77, 0x03 }; -#define reg70 0x03 - -static void cx11646_jpeg(struct gspca_dev*gspca_dev) -{ - int i; - int length; - __u8 Reg55; - int retry; - - reg_w_val(gspca_dev, 0x00c0, 0x01); - reg_w_val(gspca_dev, 0x00c3, 0x00); - reg_w_val(gspca_dev, 0x00c0, 0x00); - reg_r(gspca_dev, 0x0001, 1); - length = 8; - switch (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv) { - case 0: - for (i = 0; i < 27; i++) { - if (i == 26) - length = 2; - reg_w(gspca_dev, 0x0008, cxjpeg_640[i], length); - } - Reg55 = 0x28; - break; - case 1: - for (i = 0; i < 27; i++) { - if (i == 26) - length = 2; - reg_w(gspca_dev, 0x0008, cxjpeg_352[i], length); - } - Reg55 = 0x16; - break; - default: -/* case 2: */ - for (i = 0; i < 27; i++) { - if (i == 26) - length = 2; - reg_w(gspca_dev, 0x0008, cxjpeg_320[i], length); - } - Reg55 = 0x14; - break; - case 3: - for (i = 0; i < 27; i++) { - if (i == 26) - length = 2; - reg_w(gspca_dev, 0x0008, cxjpeg_176[i], length); - } - Reg55 = 0x0B; - break; - } - - reg_r(gspca_dev, 0x0002, 1); - reg_w_val(gspca_dev, 0x0055, Reg55); - reg_r(gspca_dev, 0x0002, 1); - reg_w(gspca_dev, 0x0010, reg10, 2); - reg_w_val(gspca_dev, 0x0054, 0x02); - reg_w_val(gspca_dev, 0x0054, 0x01); - reg_w_val(gspca_dev, 0x0000, 0x94); - reg_w_val(gspca_dev, 0x0053, 0xc0); - reg_w_val(gspca_dev, 0x00fc, 0xe1); - reg_w_val(gspca_dev, 0x0000, 0x00); - /* wait for completion */ - retry = 50; - do { - reg_r(gspca_dev, 0x0002, 1); - /* 0x07 until 0x00 */ - if (gspca_dev->usb_buf[0] == 0x00) - break; - reg_w_val(gspca_dev, 0x0053, 0x00); - } while (--retry); - if (retry == 0) - PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); - /* send the qtable now */ - reg_r(gspca_dev, 0x0001, 1); /* -> 0x18 */ - length = 8; - for (i = 0; i < 18; i++) { - if (i == 17) - length = 2; - reg_w(gspca_dev, 0x0008, cxjpeg_qtable[i], length); - - } - reg_r(gspca_dev, 0x0002, 1); /* 0x00 */ - reg_r(gspca_dev, 0x0053, 1); /* 0x00 */ - reg_w_val(gspca_dev, 0x0054, 0x02); - reg_w_val(gspca_dev, 0x0054, 0x01); - reg_w_val(gspca_dev, 0x0000, 0x94); - reg_w_val(gspca_dev, 0x0053, 0xc0); - - reg_r(gspca_dev, 0x0038, 1); /* 0x40 */ - reg_r(gspca_dev, 0x0038, 1); /* 0x40 */ - reg_r(gspca_dev, 0x001f, 1); /* 0x38 */ - reg_w(gspca_dev, 0x0012, reg12, 5); - reg_w(gspca_dev, 0x00e5, regE5_8, 8); - reg_r(gspca_dev, 0x00e8, 8); - reg_w(gspca_dev, 0x00e5, regE5a, 4); - reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ - reg_w_val(gspca_dev, 0x009a, 0x01); - reg_w(gspca_dev, 0x00e5, regE5b, 4); - reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ - reg_w(gspca_dev, 0x00e5, regE5c, 4); - reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ - - reg_w(gspca_dev, 0x0051, reg51, 2); - reg_w(gspca_dev, 0x0010, reg10, 2); - reg_w_val(gspca_dev, 0x0070, reg70); -} - -static void cx11646_init1(struct gspca_dev *gspca_dev) -{ - int i = 0; - - reg_w_val(gspca_dev, 0x0010, 0x00); - reg_w_val(gspca_dev, 0x0053, 0x00); - reg_w_val(gspca_dev, 0x0052, 0x00); - reg_w_val(gspca_dev, 0x009b, 0x2f); - reg_w_val(gspca_dev, 0x009c, 0x10); - reg_r(gspca_dev, 0x0098, 1); - reg_w_val(gspca_dev, 0x0098, 0x40); - reg_r(gspca_dev, 0x0099, 1); - reg_w_val(gspca_dev, 0x0099, 0x07); - reg_w_val(gspca_dev, 0x0039, 0x40); - reg_w_val(gspca_dev, 0x003c, 0xff); - reg_w_val(gspca_dev, 0x003f, 0x1f); - reg_w_val(gspca_dev, 0x003d, 0x40); -/* reg_w_val(gspca_dev, 0x003d, 0x60); */ - reg_r(gspca_dev, 0x0099, 1); /* ->0x07 */ - - while (cx_sensor_init[i][0]) { - reg_w_val(gspca_dev, 0x00e5, cx_sensor_init[i][0]); - reg_r(gspca_dev, 0x00e8, 1); /* -> 0x00 */ - if (i == 1) { - reg_w_val(gspca_dev, 0x00ed, 0x01); - reg_r(gspca_dev, 0x00ed, 1); /* -> 0x01 */ - } - i++; - } - reg_w_val(gspca_dev, 0x00c3, 0x00); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam; - - cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - cx11646_init1(gspca_dev); - cx11646_initsize(gspca_dev); - cx11646_fw(gspca_dev); - cx_sensor(gspca_dev); - cx11646_jpegInit(gspca_dev); - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* create the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x22); /* JPEG 411 */ - jpeg_set_qual(sd->jpeg_hdr, QUALITY); - - cx11646_initsize(gspca_dev); - cx11646_fw(gspca_dev); - cx_sensor(gspca_dev); - cx11646_jpeg(gspca_dev); - return 0; -} - -/* called on streamoff with alt 0 and on disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - int retry = 50; - - if (!gspca_dev->present) - return; - reg_w_val(gspca_dev, 0x0000, 0x00); - reg_r(gspca_dev, 0x0002, 1); - reg_w_val(gspca_dev, 0x0053, 0x00); - - while (retry--) { -/* reg_r(gspca_dev, 0x0002, 1);*/ - reg_r(gspca_dev, 0x0053, 1); - if (gspca_dev->usb_buf[0] == 0) - break; - } - reg_w_val(gspca_dev, 0x0000, 0x00); - reg_r(gspca_dev, 0x0002, 1); - - reg_w_val(gspca_dev, 0x0010, 0x00); - reg_r(gspca_dev, 0x0033, 1); - reg_w_val(gspca_dev, 0x00fc, 0xe0); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (data[0] == 0xff && data[1] == 0xd8) { - - /* start of frame */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - - /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - data += 2; - len -= 2; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val, s32 sat) -{ - __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; - __u8 reg51c[2]; - - regE5cbx[2] = val; - reg_w(gspca_dev, 0x00e5, regE5cbx, 8); - reg_r(gspca_dev, 0x00e8, 8); - reg_w(gspca_dev, 0x00e5, regE5c, 4); - reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ - - reg51c[0] = 0x77; - reg51c[1] = sat; - reg_w(gspca_dev, 0x0051, reg51c, 2); - reg_w(gspca_dev, 0x0010, reg10, 2); - reg_w_val(gspca_dev, 0x0070, reg70); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val, s32 sat) -{ - __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */ -/* __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01}; * LSB */ - __u8 reg51c[2]; - - regE5acx[2] = val; - reg_w(gspca_dev, 0x00e5, regE5acx, 4); - reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ - reg51c[0] = 0x77; - reg51c[1] = sat; - reg_w(gspca_dev, 0x0051, reg51c, 2); - reg_w(gspca_dev, 0x0010, reg10, 2); - reg_w_val(gspca_dev, 0x0070, reg70); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val, sd->sat->cur.val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val, sd->sat->cur.val); - break; - case V4L2_CID_SATURATION: - setbrightness(gspca_dev, sd->brightness->cur.val, ctrl->val); - setcontrast(gspca_dev, sd->contrast->cur.val, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 3); - sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 0xd4); - sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0x0a, 0x1f, 1, 0x0c); - sd->sat = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 7, 1, 3); - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0572, 0x0041)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c deleted file mode 100644 index 2499a881d9a3..000000000000 --- a/drivers/media/video/gspca/cpia1.c +++ /dev/null @@ -1,1905 +0,0 @@ -/* - * cpia CPiA (1) gspca driver - * - * Copyright (C) 2010-2011 Hans de Goede - * - * This module is adapted from the in kernel v4l1 cpia driver which is : - * - * (C) Copyright 1999-2000 Peter Pregler - * (C) Copyright 1999-2000 Scott J. Bertin - * (C) Copyright 1999-2000 Johannes Erdfelt - * (C) Copyright 2000 STMicroelectronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "cpia1" - -#include -#include "gspca.h" - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("Vision CPiA"); -MODULE_LICENSE("GPL"); - -/* constant value's */ -#define MAGIC_0 0x19 -#define MAGIC_1 0x68 -#define DATA_IN 0xc0 -#define DATA_OUT 0x40 -#define VIDEOSIZE_QCIF 0 /* 176x144 */ -#define VIDEOSIZE_CIF 1 /* 352x288 */ -#define SUBSAMPLE_420 0 -#define SUBSAMPLE_422 1 -#define YUVORDER_YUYV 0 -#define YUVORDER_UYVY 1 -#define NOT_COMPRESSED 0 -#define COMPRESSED 1 -#define NO_DECIMATION 0 -#define DECIMATION_ENAB 1 -#define EOI 0xff /* End Of Image */ -#define EOL 0xfd /* End Of Line */ -#define FRAME_HEADER_SIZE 64 - -/* Image grab modes */ -#define CPIA_GRAB_SINGLE 0 -#define CPIA_GRAB_CONTINEOUS 1 - -/* Compression parameters */ -#define CPIA_COMPRESSION_NONE 0 -#define CPIA_COMPRESSION_AUTO 1 -#define CPIA_COMPRESSION_MANUAL 2 -#define CPIA_COMPRESSION_TARGET_QUALITY 0 -#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 - -/* Return offsets for GetCameraState */ -#define SYSTEMSTATE 0 -#define GRABSTATE 1 -#define STREAMSTATE 2 -#define FATALERROR 3 -#define CMDERROR 4 -#define DEBUGFLAGS 5 -#define VPSTATUS 6 -#define ERRORCODE 7 - -/* SystemState */ -#define UNINITIALISED_STATE 0 -#define PASS_THROUGH_STATE 1 -#define LO_POWER_STATE 2 -#define HI_POWER_STATE 3 -#define WARM_BOOT_STATE 4 - -/* GrabState */ -#define GRAB_IDLE 0 -#define GRAB_ACTIVE 1 -#define GRAB_DONE 2 - -/* StreamState */ -#define STREAM_NOT_READY 0 -#define STREAM_READY 1 -#define STREAM_OPEN 2 -#define STREAM_PAUSED 3 -#define STREAM_FINISHED 4 - -/* Fatal Error, CmdError, and DebugFlags */ -#define CPIA_FLAG 1 -#define SYSTEM_FLAG 2 -#define INT_CTRL_FLAG 4 -#define PROCESS_FLAG 8 -#define COM_FLAG 16 -#define VP_CTRL_FLAG 32 -#define CAPTURE_FLAG 64 -#define DEBUG_FLAG 128 - -/* VPStatus */ -#define VP_STATE_OK 0x00 - -#define VP_STATE_FAILED_VIDEOINIT 0x01 -#define VP_STATE_FAILED_AECACBINIT 0x02 -#define VP_STATE_AEC_MAX 0x04 -#define VP_STATE_ACB_BMAX 0x08 - -#define VP_STATE_ACB_RMIN 0x10 -#define VP_STATE_ACB_GMIN 0x20 -#define VP_STATE_ACB_RMAX 0x40 -#define VP_STATE_ACB_GMAX 0x80 - -/* default (minimum) compensation values */ -#define COMP_RED 220 -#define COMP_GREEN1 214 -#define COMP_GREEN2 COMP_GREEN1 -#define COMP_BLUE 230 - -/* exposure status */ -#define EXPOSURE_VERY_LIGHT 0 -#define EXPOSURE_LIGHT 1 -#define EXPOSURE_NORMAL 2 -#define EXPOSURE_DARK 3 -#define EXPOSURE_VERY_DARK 4 - -#define CPIA_MODULE_CPIA (0 << 5) -#define CPIA_MODULE_SYSTEM (1 << 5) -#define CPIA_MODULE_VP_CTRL (5 << 5) -#define CPIA_MODULE_CAPTURE (6 << 5) -#define CPIA_MODULE_DEBUG (7 << 5) - -#define INPUT (DATA_IN << 8) -#define OUTPUT (DATA_OUT << 8) - -#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) -#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) -#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) -#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) -#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) -#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) -#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) -#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) - -#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) -#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) -#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) -#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) -#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) -#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) -#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) -#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) -#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) -#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) -#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) -#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) -#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) - -#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) -#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) -#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) -#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) -#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) -#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) -#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) -#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) -#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) -#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) -#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) -#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) -#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) -#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) -#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) -#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) -#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) - -#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) -#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) -#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) -#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) -#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) -#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) -#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) -#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) -#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) -#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) -#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) -#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) -#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) -#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) -#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) - -#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) -#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) -#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) -#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) -#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) -#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) -#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) -#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) - -#define ROUND_UP_EXP_FOR_FLICKER 15 - -/* Constants for automatic frame rate adjustment */ -#define MAX_EXP 302 -#define MAX_EXP_102 255 -#define LOW_EXP 140 -#define VERY_LOW_EXP 70 -#define TC 94 -#define EXP_ACC_DARK 50 -#define EXP_ACC_LIGHT 90 -#define HIGH_COMP_102 160 -#define MAX_COMP 239 -#define DARK_TIME 3 -#define LIGHT_TIME 3 - -#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \ - sd->params.version.firmwareRevision == (y)) - -#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000) -#define BRIGHTNESS_DEF 50 -#define CONTRAST_DEF 48 -#define SATURATION_DEF 50 -#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ -#define ILLUMINATORS_1_DEF 0 -#define ILLUMINATORS_2_DEF 0 -#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY - -/* Developer's Guide Table 5 p 3-34 - * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ -static u8 flicker_jumps[2][2][4] = -{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, - { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } -}; - -struct cam_params { - struct { - u8 firmwareVersion; - u8 firmwareRevision; - u8 vcVersion; - u8 vcRevision; - } version; - struct { - u16 vendor; - u16 product; - u16 deviceRevision; - } pnpID; - struct { - u8 vpVersion; - u8 vpRevision; - u16 cameraHeadID; - } vpVersion; - struct { - u8 systemState; - u8 grabState; - u8 streamState; - u8 fatalError; - u8 cmdError; - u8 debugFlags; - u8 vpStatus; - u8 errorCode; - } status; - struct { - u8 brightness; - u8 contrast; - u8 saturation; - } colourParams; - struct { - u8 gainMode; - u8 expMode; - u8 compMode; - u8 centreWeight; - u8 gain; - u8 fineExp; - u8 coarseExpLo; - u8 coarseExpHi; - u8 redComp; - u8 green1Comp; - u8 green2Comp; - u8 blueComp; - } exposure; - struct { - u8 balanceMode; - u8 redGain; - u8 greenGain; - u8 blueGain; - } colourBalance; - struct { - u8 divisor; - u8 baserate; - } sensorFps; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } apcor; - struct { - u8 disabled; - u8 flickerMode; - u8 coarseJump; - u8 allowableOverExposure; - } flickerControl; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } vlOffset; - struct { - u8 mode; - u8 decimation; - } compression; - struct { - u8 frTargeting; - u8 targetFR; - u8 targetQ; - } compressionTarget; - struct { - u8 yThreshold; - u8 uvThreshold; - } yuvThreshold; - struct { - u8 hysteresis; - u8 threshMax; - u8 smallStep; - u8 largeStep; - u8 decimationHysteresis; - u8 frDiffStepThresh; - u8 qDiffStepThresh; - u8 decimationThreshMod; - } compressionParams; - struct { - u8 videoSize; /* CIF/QCIF */ - u8 subSample; - u8 yuvOrder; - } format; - struct { /* Intel QX3 specific data */ - u8 qx3_detected; /* a QX3 is present */ - u8 toplight; /* top light lit , R/W */ - u8 bottomlight; /* bottom light lit, R/W */ - u8 button; /* snapshot button pressed (R/O) */ - u8 cradled; /* microscope is in cradle (R/O) */ - } qx3; - struct { - u8 colStart; /* skip first 8*colStart pixels */ - u8 colEnd; /* finish at 8*colEnd pixels */ - u8 rowStart; /* skip first 4*rowStart lines */ - u8 rowEnd; /* finish at 4*rowEnd lines */ - } roi; - u8 ecpTiming; - u8 streamStartLine; -}; - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct cam_params params; /* camera settings */ - - atomic_t cam_exposure; - atomic_t fps; - int exposure_count; - u8 exposure_status; - struct v4l2_ctrl *freq; - u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */ - u8 first_frame; -}; - -static const struct v4l2_pix_format mode[] = { - {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, - /* The sizeimage is trial and error, as with low framerates - the camera will pad out usb frames, making the image - data larger then strictly necessary */ - .bytesperline = 160, - .sizeimage = 65536, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, - .bytesperline = 172, - .sizeimage = 65536, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 262144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 262144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/********************************************************************** - * - * General functions - * - **********************************************************************/ - -static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command) -{ - u8 requesttype; - unsigned int pipe; - int ret, databytes = command[6] | (command[7] << 8); - /* Sometimes we see spurious EPIPE errors */ - int retries = 3; - - if (command[0] == DATA_IN) { - pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); - requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE; - } else if (command[0] == DATA_OUT) { - pipe = usb_sndctrlpipe(gspca_dev->dev, 0); - requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE; - } else { - PDEBUG(D_ERR, "Unexpected first byte of command: %x", - command[0]); - return -EINVAL; - } - -retry: - ret = usb_control_msg(gspca_dev->dev, pipe, - command[1], - requesttype, - command[2] | (command[3] << 8), - command[4] | (command[5] << 8), - gspca_dev->usb_buf, databytes, 1000); - - if (ret < 0) - pr_err("usb_control_msg %02x, error %d\n", command[1], ret); - - if (ret == -EPIPE && retries > 0) { - retries--; - goto retry; - } - - return (ret < 0) ? ret : 0; -} - -/* send an arbitrary command to the camera */ -static int do_command(struct gspca_dev *gspca_dev, u16 command, - u8 a, u8 b, u8 c, u8 d) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret, datasize; - u8 cmd[8]; - - switch (command) { - case CPIA_COMMAND_GetCPIAVersion: - case CPIA_COMMAND_GetPnPID: - case CPIA_COMMAND_GetCameraStatus: - case CPIA_COMMAND_GetVPVersion: - case CPIA_COMMAND_GetColourParams: - case CPIA_COMMAND_GetColourBalance: - case CPIA_COMMAND_GetExposure: - datasize = 8; - break; - case CPIA_COMMAND_ReadMCPorts: - case CPIA_COMMAND_ReadVCRegs: - datasize = 4; - break; - default: - datasize = 0; - break; - } - - cmd[0] = command >> 8; - cmd[1] = command & 0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = datasize; - cmd[7] = 0; - - ret = cpia_usb_transferCmd(gspca_dev, cmd); - if (ret) - return ret; - - switch (command) { - case CPIA_COMMAND_GetCPIAVersion: - sd->params.version.firmwareVersion = gspca_dev->usb_buf[0]; - sd->params.version.firmwareRevision = gspca_dev->usb_buf[1]; - sd->params.version.vcVersion = gspca_dev->usb_buf[2]; - sd->params.version.vcRevision = gspca_dev->usb_buf[3]; - break; - case CPIA_COMMAND_GetPnPID: - sd->params.pnpID.vendor = - gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8); - sd->params.pnpID.product = - gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8); - sd->params.pnpID.deviceRevision = - gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8); - break; - case CPIA_COMMAND_GetCameraStatus: - sd->params.status.systemState = gspca_dev->usb_buf[0]; - sd->params.status.grabState = gspca_dev->usb_buf[1]; - sd->params.status.streamState = gspca_dev->usb_buf[2]; - sd->params.status.fatalError = gspca_dev->usb_buf[3]; - sd->params.status.cmdError = gspca_dev->usb_buf[4]; - sd->params.status.debugFlags = gspca_dev->usb_buf[5]; - sd->params.status.vpStatus = gspca_dev->usb_buf[6]; - sd->params.status.errorCode = gspca_dev->usb_buf[7]; - break; - case CPIA_COMMAND_GetVPVersion: - sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0]; - sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1]; - sd->params.vpVersion.cameraHeadID = - gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8); - break; - case CPIA_COMMAND_GetColourParams: - sd->params.colourParams.brightness = gspca_dev->usb_buf[0]; - sd->params.colourParams.contrast = gspca_dev->usb_buf[1]; - sd->params.colourParams.saturation = gspca_dev->usb_buf[2]; - break; - case CPIA_COMMAND_GetColourBalance: - sd->params.colourBalance.redGain = gspca_dev->usb_buf[0]; - sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1]; - sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2]; - break; - case CPIA_COMMAND_GetExposure: - sd->params.exposure.gain = gspca_dev->usb_buf[0]; - sd->params.exposure.fineExp = gspca_dev->usb_buf[1]; - sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2]; - sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3]; - sd->params.exposure.redComp = gspca_dev->usb_buf[4]; - sd->params.exposure.green1Comp = gspca_dev->usb_buf[5]; - sd->params.exposure.green2Comp = gspca_dev->usb_buf[6]; - sd->params.exposure.blueComp = gspca_dev->usb_buf[7]; - break; - - case CPIA_COMMAND_ReadMCPorts: - /* test button press */ - a = ((gspca_dev->usb_buf[1] & 0x02) == 0); - if (a != sd->params.qx3.button) { -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - input_report_key(gspca_dev->input_dev, KEY_CAMERA, a); - input_sync(gspca_dev->input_dev); -#endif - sd->params.qx3.button = a; - } - if (sd->params.qx3.button) { - /* button pressed - unlock the latch */ - do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, - 3, 0xdf, 0xdf, 0); - do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, - 3, 0xff, 0xff, 0); - } - - /* test whether microscope is cradled */ - sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0); - break; - } - - return 0; -} - -/* send a command to the camera with an additional data transaction */ -static int do_command_extended(struct gspca_dev *gspca_dev, u16 command, - u8 a, u8 b, u8 c, u8 d, - u8 e, u8 f, u8 g, u8 h, - u8 i, u8 j, u8 k, u8 l) -{ - u8 cmd[8]; - - cmd[0] = command >> 8; - cmd[1] = command & 0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = 8; - cmd[7] = 0; - gspca_dev->usb_buf[0] = e; - gspca_dev->usb_buf[1] = f; - gspca_dev->usb_buf[2] = g; - gspca_dev->usb_buf[3] = h; - gspca_dev->usb_buf[4] = i; - gspca_dev->usb_buf[5] = j; - gspca_dev->usb_buf[6] = k; - gspca_dev->usb_buf[7] = l; - - return cpia_usb_transferCmd(gspca_dev, cmd); -} - -/* find_over_exposure - * Finds a suitable value of OverExposure for use with SetFlickerCtrl - * Some calculation is required because this value changes with the brightness - * set with SetColourParameters - * - * Parameters: Brightness - last brightness value set with SetColourParameters - * - * Returns: OverExposure value to use with SetFlickerCtrl - */ -#define FLICKER_MAX_EXPOSURE 250 -#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 -#define FLICKER_BRIGHTNESS_CONSTANT 59 -static int find_over_exposure(int brightness) -{ - int MaxAllowableOverExposure, OverExposure; - - MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - - FLICKER_BRIGHTNESS_CONSTANT; - - if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) - OverExposure = MaxAllowableOverExposure; - else - OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; - - return OverExposure; -} -#undef FLICKER_MAX_EXPOSURE -#undef FLICKER_ALLOWABLE_OVER_EXPOSURE -#undef FLICKER_BRIGHTNESS_CONSTANT - -/* initialise cam_data structure */ -static void reset_camera_params(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam_params *params = &sd->params; - - /* The following parameter values are the defaults from - * "Software Developer's Guide for CPiA Cameras". Any changes - * to the defaults are noted in comments. */ - params->colourParams.brightness = BRIGHTNESS_DEF; - params->colourParams.contrast = CONTRAST_DEF; - params->colourParams.saturation = SATURATION_DEF; - params->exposure.gainMode = 4; - params->exposure.expMode = 2; /* AEC */ - params->exposure.compMode = 1; - params->exposure.centreWeight = 1; - params->exposure.gain = 0; - params->exposure.fineExp = 0; - params->exposure.coarseExpLo = 185; - params->exposure.coarseExpHi = 0; - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - params->colourBalance.balanceMode = 2; /* ACB */ - params->colourBalance.redGain = 32; - params->colourBalance.greenGain = 6; - params->colourBalance.blueGain = 92; - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x16; - params->apcor.gain4 = 0x24; - params->apcor.gain8 = 0x34; - params->vlOffset.gain1 = 20; - params->vlOffset.gain2 = 24; - params->vlOffset.gain4 = 26; - params->vlOffset.gain8 = 26; - params->compressionParams.hysteresis = 3; - params->compressionParams.threshMax = 11; - params->compressionParams.smallStep = 1; - params->compressionParams.largeStep = 3; - params->compressionParams.decimationHysteresis = 2; - params->compressionParams.frDiffStepThresh = 5; - params->compressionParams.qDiffStepThresh = 3; - params->compressionParams.decimationThreshMod = 2; - /* End of default values from Software Developer's Guide */ - - /* Set Sensor FPS to 15fps. This seems better than 30fps - * for indoor lighting. */ - params->sensorFps.divisor = 1; - params->sensorFps.baserate = 1; - - params->flickerControl.flickerMode = 0; - params->flickerControl.disabled = 1; - params->flickerControl.coarseJump = - flicker_jumps[sd->mainsFreq] - [params->sensorFps.baserate] - [params->sensorFps.divisor]; - params->flickerControl.allowableOverExposure = - find_over_exposure(params->colourParams.brightness); - - params->yuvThreshold.yThreshold = 6; /* From windows driver */ - params->yuvThreshold.uvThreshold = 6; /* From windows driver */ - - params->format.subSample = SUBSAMPLE_420; - params->format.yuvOrder = YUVORDER_YUYV; - - params->compression.mode = CPIA_COMPRESSION_AUTO; - params->compression.decimation = NO_DECIMATION; - - params->compressionTarget.frTargeting = COMP_TARGET_DEF; - params->compressionTarget.targetFR = 15; /* From windows driver */ - params->compressionTarget.targetQ = 5; /* From windows driver */ - - params->qx3.qx3_detected = 0; - params->qx3.toplight = 0; - params->qx3.bottomlight = 0; - params->qx3.button = 0; - params->qx3.cradled = 0; -} - -static void printstatus(struct cam_params *params) -{ - PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x", - params->status.systemState, params->status.grabState, - params->status.streamState, params->status.fatalError, - params->status.cmdError, params->status.debugFlags, - params->status.vpStatus, params->status.errorCode); -} - -static int goto_low_power(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0); - if (ret) - return ret; - - ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - if (ret) - return ret; - - if (sd->params.status.systemState != LO_POWER_STATE) { - if (sd->params.status.systemState != WARM_BOOT_STATE) { - PDEBUG(D_ERR, - "unexpected state after lo power cmd: %02x", - sd->params.status.systemState); - printstatus(&sd->params); - } - return -EIO; - } - - PDEBUG(D_CONF, "camera now in LOW power state"); - return 0; -} - -static int goto_high_power(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0); - if (ret) - return ret; - - msleep_interruptible(40); /* windows driver does it too */ - - if (signal_pending(current)) - return -EINTR; - - do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - if (ret) - return ret; - - if (sd->params.status.systemState != HI_POWER_STATE) { - PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x", - sd->params.status.systemState); - printstatus(&sd->params); - return -EIO; - } - - PDEBUG(D_CONF, "camera now in HIGH power state"); - return 0; -} - -static int get_version_information(struct gspca_dev *gspca_dev) -{ - int ret; - - /* GetCPIAVersion */ - ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); - if (ret) - return ret; - - /* GetPnPID */ - return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); -} - -static int save_camera_state(struct gspca_dev *gspca_dev) -{ - int ret; - - ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - if (ret) - return ret; - - return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); -} - -static int command_setformat(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat, - sd->params.format.videoSize, - sd->params.format.subSample, - sd->params.format.yuvOrder, 0); - if (ret) - return ret; - - return do_command(gspca_dev, CPIA_COMMAND_SetROI, - sd->params.roi.colStart, sd->params.roi.colEnd, - sd->params.roi.rowStart, sd->params.roi.rowEnd); -} - -static int command_setcolourparams(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - return do_command(gspca_dev, CPIA_COMMAND_SetColourParams, - sd->params.colourParams.brightness, - sd->params.colourParams.contrast, - sd->params.colourParams.saturation, 0); -} - -static int command_setapcor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - return do_command(gspca_dev, CPIA_COMMAND_SetApcor, - sd->params.apcor.gain1, - sd->params.apcor.gain2, - sd->params.apcor.gain4, - sd->params.apcor.gain8); -} - -static int command_setvloffset(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset, - sd->params.vlOffset.gain1, - sd->params.vlOffset.gain2, - sd->params.vlOffset.gain4, - sd->params.vlOffset.gain8); -} - -static int command_setexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure, - sd->params.exposure.gainMode, - 1, - sd->params.exposure.compMode, - sd->params.exposure.centreWeight, - sd->params.exposure.gain, - sd->params.exposure.fineExp, - sd->params.exposure.coarseExpLo, - sd->params.exposure.coarseExpHi, - sd->params.exposure.redComp, - sd->params.exposure.green1Comp, - sd->params.exposure.green2Comp, - sd->params.exposure.blueComp); - if (ret) - return ret; - - if (sd->params.exposure.expMode != 1) { - ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure, - 0, - sd->params.exposure.expMode, - 0, 0, - sd->params.exposure.gain, - sd->params.exposure.fineExp, - sd->params.exposure.coarseExpLo, - sd->params.exposure.coarseExpHi, - 0, 0, 0, 0); - } - - return ret; -} - -static int command_setcolourbalance(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->params.colourBalance.balanceMode == 1) { - int ret; - - ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, - 1, - sd->params.colourBalance.redGain, - sd->params.colourBalance.greenGain, - sd->params.colourBalance.blueGain); - if (ret) - return ret; - - return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - if (sd->params.colourBalance.balanceMode == 2) { - return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, - 2, 0, 0, 0); - } - if (sd->params.colourBalance.balanceMode == 3) { - return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - - return -EINVAL; -} - -static int command_setcompressiontarget(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget, - sd->params.compressionTarget.frTargeting, - sd->params.compressionTarget.targetFR, - sd->params.compressionTarget.targetQ, 0); -} - -static int command_setyuvtresh(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh, - sd->params.yuvThreshold.yThreshold, - sd->params.yuvThreshold.uvThreshold, 0, 0); -} - -static int command_setcompressionparams(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command_extended(gspca_dev, - CPIA_COMMAND_SetCompressionParams, - 0, 0, 0, 0, - sd->params.compressionParams.hysteresis, - sd->params.compressionParams.threshMax, - sd->params.compressionParams.smallStep, - sd->params.compressionParams.largeStep, - sd->params.compressionParams.decimationHysteresis, - sd->params.compressionParams.frDiffStepThresh, - sd->params.compressionParams.qDiffStepThresh, - sd->params.compressionParams.decimationThreshMod); -} - -static int command_setcompression(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command(gspca_dev, CPIA_COMMAND_SetCompression, - sd->params.compression.mode, - sd->params.compression.decimation, 0, 0); -} - -static int command_setsensorfps(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS, - sd->params.sensorFps.divisor, - sd->params.sensorFps.baserate, 0, 0); -} - -static int command_setflickerctrl(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl, - sd->params.flickerControl.flickerMode, - sd->params.flickerControl.coarseJump, - sd->params.flickerControl.allowableOverExposure, - 0); -} - -static int command_setecptiming(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming, - sd->params.ecpTiming, 0, 0, 0); -} - -static int command_pause(struct gspca_dev *gspca_dev) -{ - return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); -} - -static int command_resume(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap, - 0, sd->params.streamStartLine, 0, 0); -} - -static int command_setlights(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret, p1, p2; - - p1 = (sd->params.qx3.bottomlight == 0) << 1; - p2 = (sd->params.qx3.toplight == 0) << 3; - - ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg, - 0x90, 0x8f, 0x50, 0); - if (ret) - return ret; - - return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0, - p1 | p2 | 0xe0, 0); -} - -static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply) -{ - /* Everything in here is from the Windows driver */ -/* define for compgain calculation */ -#if 0 -#define COMPGAIN(base, curexp, newexp) \ - (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)((float)curexp * (float)(u8)(curcomp + 128) / \ - (float)(u8)(basecomp - 128)) -#else - /* equivalent functions without floating point math */ -#define COMPGAIN(base, curexp, newexp) \ - (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp))) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) -#endif - - struct sd *sd = (struct sd *) gspca_dev; - int currentexp = sd->params.exposure.coarseExpLo + - sd->params.exposure.coarseExpHi * 256; - int ret, startexp; - - if (on) { - int cj = sd->params.flickerControl.coarseJump; - sd->params.flickerControl.flickerMode = 1; - sd->params.flickerControl.disabled = 0; - if (sd->params.exposure.expMode != 2) { - sd->params.exposure.expMode = 2; - sd->exposure_status = EXPOSURE_NORMAL; - } - currentexp = currentexp << sd->params.exposure.gain; - sd->params.exposure.gain = 0; - /* round down current exposure to nearest value */ - startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; - if (startexp < 1) - startexp = 1; - startexp = (startexp * cj) - 1; - if (FIRMWARE_VERSION(1, 2)) - while (startexp > MAX_EXP_102) - startexp -= cj; - else - while (startexp > MAX_EXP) - startexp -= cj; - sd->params.exposure.coarseExpLo = startexp & 0xff; - sd->params.exposure.coarseExpHi = startexp >> 8; - if (currentexp > startexp) { - if (currentexp > (2 * startexp)) - currentexp = 2 * startexp; - sd->params.exposure.redComp = - COMPGAIN(COMP_RED, currentexp, startexp); - sd->params.exposure.green1Comp = - COMPGAIN(COMP_GREEN1, currentexp, startexp); - sd->params.exposure.green2Comp = - COMPGAIN(COMP_GREEN2, currentexp, startexp); - sd->params.exposure.blueComp = - COMPGAIN(COMP_BLUE, currentexp, startexp); - } else { - sd->params.exposure.redComp = COMP_RED; - sd->params.exposure.green1Comp = COMP_GREEN1; - sd->params.exposure.green2Comp = COMP_GREEN2; - sd->params.exposure.blueComp = COMP_BLUE; - } - if (FIRMWARE_VERSION(1, 2)) - sd->params.exposure.compMode = 0; - else - sd->params.exposure.compMode = 1; - - sd->params.apcor.gain1 = 0x18; - sd->params.apcor.gain2 = 0x18; - sd->params.apcor.gain4 = 0x16; - sd->params.apcor.gain8 = 0x14; - } else { - sd->params.flickerControl.flickerMode = 0; - sd->params.flickerControl.disabled = 1; - /* Average equivalent coarse for each comp channel */ - startexp = EXP_FROM_COMP(COMP_RED, - sd->params.exposure.redComp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN1, - sd->params.exposure.green1Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN2, - sd->params.exposure.green2Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_BLUE, - sd->params.exposure.blueComp, currentexp); - startexp = startexp >> 2; - while (startexp > MAX_EXP && sd->params.exposure.gain < - sd->params.exposure.gainMode - 1) { - startexp = startexp >> 1; - ++sd->params.exposure.gain; - } - if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102) - startexp = MAX_EXP_102; - if (startexp > MAX_EXP) - startexp = MAX_EXP; - sd->params.exposure.coarseExpLo = startexp & 0xff; - sd->params.exposure.coarseExpHi = startexp >> 8; - sd->params.exposure.redComp = COMP_RED; - sd->params.exposure.green1Comp = COMP_GREEN1; - sd->params.exposure.green2Comp = COMP_GREEN2; - sd->params.exposure.blueComp = COMP_BLUE; - sd->params.exposure.compMode = 1; - sd->params.apcor.gain1 = 0x18; - sd->params.apcor.gain2 = 0x16; - sd->params.apcor.gain4 = 0x24; - sd->params.apcor.gain8 = 0x34; - } - sd->params.vlOffset.gain1 = 20; - sd->params.vlOffset.gain2 = 24; - sd->params.vlOffset.gain4 = 26; - sd->params.vlOffset.gain8 = 26; - - if (apply) { - ret = command_setexposure(gspca_dev); - if (ret) - return ret; - - ret = command_setapcor(gspca_dev); - if (ret) - return ret; - - ret = command_setvloffset(gspca_dev); - if (ret) - return ret; - - ret = command_setflickerctrl(gspca_dev); - if (ret) - return ret; - } - - return 0; -#undef EXP_FROM_COMP -#undef COMPGAIN -} - -/* monitor the exposure and adjust the sensor frame rate if needed */ -static void monitor_exposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 exp_acc, bcomp, cmd[8]; - int ret, light_exp, dark_exp, very_dark_exp; - int old_exposure, new_exposure, framerate; - int setfps = 0, setexp = 0, setflicker = 0; - - /* get necessary stats and register settings from camera */ - /* do_command can't handle this, so do it ourselves */ - cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8; - cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff; - cmd[2] = 30; - cmd[3] = 4; - cmd[4] = 9; - cmd[5] = 8; - cmd[6] = 8; - cmd[7] = 0; - ret = cpia_usb_transferCmd(gspca_dev, cmd); - if (ret) { - pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret); - return; - } - exp_acc = gspca_dev->usb_buf[0]; - bcomp = gspca_dev->usb_buf[1]; - - light_exp = sd->params.colourParams.brightness + - TC - 50 + EXP_ACC_LIGHT; - if (light_exp > 255) - light_exp = 255; - dark_exp = sd->params.colourParams.brightness + - TC - 50 - EXP_ACC_DARK; - if (dark_exp < 0) - dark_exp = 0; - very_dark_exp = dark_exp / 2; - - old_exposure = sd->params.exposure.coarseExpHi * 256 + - sd->params.exposure.coarseExpLo; - - if (!sd->params.flickerControl.disabled) { - /* Flicker control on */ - int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP : - HIGH_COMP_102; - bcomp += 128; /* decode */ - if (bcomp >= max_comp && exp_acc < dark_exp) { - /* dark */ - if (exp_acc < very_dark_exp) { - /* very dark */ - if (sd->exposure_status == EXPOSURE_VERY_DARK) - ++sd->exposure_count; - else { - sd->exposure_status = - EXPOSURE_VERY_DARK; - sd->exposure_count = 1; - } - } else { - /* just dark */ - if (sd->exposure_status == EXPOSURE_DARK) - ++sd->exposure_count; - else { - sd->exposure_status = EXPOSURE_DARK; - sd->exposure_count = 1; - } - } - } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if (old_exposure <= VERY_LOW_EXP) { - /* very light */ - if (sd->exposure_status == EXPOSURE_VERY_LIGHT) - ++sd->exposure_count; - else { - sd->exposure_status = - EXPOSURE_VERY_LIGHT; - sd->exposure_count = 1; - } - } else { - /* just light */ - if (sd->exposure_status == EXPOSURE_LIGHT) - ++sd->exposure_count; - else { - sd->exposure_status = EXPOSURE_LIGHT; - sd->exposure_count = 1; - } - } - } else { - /* not dark or light */ - sd->exposure_status = EXPOSURE_NORMAL; - } - } else { - /* Flicker control off */ - if (old_exposure >= MAX_EXP && exp_acc < dark_exp) { - /* dark */ - if (exp_acc < very_dark_exp) { - /* very dark */ - if (sd->exposure_status == EXPOSURE_VERY_DARK) - ++sd->exposure_count; - else { - sd->exposure_status = - EXPOSURE_VERY_DARK; - sd->exposure_count = 1; - } - } else { - /* just dark */ - if (sd->exposure_status == EXPOSURE_DARK) - ++sd->exposure_count; - else { - sd->exposure_status = EXPOSURE_DARK; - sd->exposure_count = 1; - } - } - } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if (old_exposure <= VERY_LOW_EXP) { - /* very light */ - if (sd->exposure_status == EXPOSURE_VERY_LIGHT) - ++sd->exposure_count; - else { - sd->exposure_status = - EXPOSURE_VERY_LIGHT; - sd->exposure_count = 1; - } - } else { - /* just light */ - if (sd->exposure_status == EXPOSURE_LIGHT) - ++sd->exposure_count; - else { - sd->exposure_status = EXPOSURE_LIGHT; - sd->exposure_count = 1; - } - } - } else { - /* not dark or light */ - sd->exposure_status = EXPOSURE_NORMAL; - } - } - - framerate = atomic_read(&sd->fps); - if (framerate > 30 || framerate < 1) - framerate = 1; - - if (!sd->params.flickerControl.disabled) { - /* Flicker control on */ - if ((sd->exposure_status == EXPOSURE_VERY_DARK || - sd->exposure_status == EXPOSURE_DARK) && - sd->exposure_count >= DARK_TIME * framerate && - sd->params.sensorFps.divisor < 2) { - - /* dark for too long */ - ++sd->params.sensorFps.divisor; - setfps = 1; - - sd->params.flickerControl.coarseJump = - flicker_jumps[sd->mainsFreq] - [sd->params.sensorFps.baserate] - [sd->params.sensorFps.divisor]; - setflicker = 1; - - new_exposure = sd->params.flickerControl.coarseJump-1; - while (new_exposure < old_exposure / 2) - new_exposure += - sd->params.flickerControl.coarseJump; - sd->params.exposure.coarseExpLo = new_exposure & 0xff; - sd->params.exposure.coarseExpHi = new_exposure >> 8; - setexp = 1; - sd->exposure_status = EXPOSURE_NORMAL; - PDEBUG(D_CONF, "Automatically decreasing sensor_fps"); - - } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT || - sd->exposure_status == EXPOSURE_LIGHT) && - sd->exposure_count >= LIGHT_TIME * framerate && - sd->params.sensorFps.divisor > 0) { - - /* light for too long */ - int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 : - MAX_EXP; - --sd->params.sensorFps.divisor; - setfps = 1; - - sd->params.flickerControl.coarseJump = - flicker_jumps[sd->mainsFreq] - [sd->params.sensorFps.baserate] - [sd->params.sensorFps.divisor]; - setflicker = 1; - - new_exposure = sd->params.flickerControl.coarseJump-1; - while (new_exposure < 2 * old_exposure && - new_exposure + - sd->params.flickerControl.coarseJump < max_exp) - new_exposure += - sd->params.flickerControl.coarseJump; - sd->params.exposure.coarseExpLo = new_exposure & 0xff; - sd->params.exposure.coarseExpHi = new_exposure >> 8; - setexp = 1; - sd->exposure_status = EXPOSURE_NORMAL; - PDEBUG(D_CONF, "Automatically increasing sensor_fps"); - } - } else { - /* Flicker control off */ - if ((sd->exposure_status == EXPOSURE_VERY_DARK || - sd->exposure_status == EXPOSURE_DARK) && - sd->exposure_count >= DARK_TIME * framerate && - sd->params.sensorFps.divisor < 2) { - - /* dark for too long */ - ++sd->params.sensorFps.divisor; - setfps = 1; - - if (sd->params.exposure.gain > 0) { - --sd->params.exposure.gain; - setexp = 1; - } - sd->exposure_status = EXPOSURE_NORMAL; - PDEBUG(D_CONF, "Automatically decreasing sensor_fps"); - - } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT || - sd->exposure_status == EXPOSURE_LIGHT) && - sd->exposure_count >= LIGHT_TIME * framerate && - sd->params.sensorFps.divisor > 0) { - - /* light for too long */ - --sd->params.sensorFps.divisor; - setfps = 1; - - if (sd->params.exposure.gain < - sd->params.exposure.gainMode - 1) { - ++sd->params.exposure.gain; - setexp = 1; - } - sd->exposure_status = EXPOSURE_NORMAL; - PDEBUG(D_CONF, "Automatically increasing sensor_fps"); - } - } - - if (setexp) - command_setexposure(gspca_dev); - - if (setfps) - command_setsensorfps(gspca_dev); - - if (setflicker) - command_setflickerctrl(gspca_dev); -} - -/*-----------------------------------------------------------------*/ -/* if flicker is switched off, this function switches it back on.It checks, - however, that conditions are suitable before restarting it. - This should only be called for firmware version 1.2. - - It also adjust the colour balance when an exposure step is detected - as - long as flicker is running -*/ -static void restart_flicker(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int cam_exposure, old_exp; - - if (!FIRMWARE_VERSION(1, 2)) - return; - - cam_exposure = atomic_read(&sd->cam_exposure); - - if (sd->params.flickerControl.flickerMode == 0 || - cam_exposure == 0) - return; - - old_exp = sd->params.exposure.coarseExpLo + - sd->params.exposure.coarseExpHi*256; - /* - see how far away camera exposure is from a valid - flicker exposure value - */ - cam_exposure %= sd->params.flickerControl.coarseJump; - if (!sd->params.flickerControl.disabled && - cam_exposure <= sd->params.flickerControl.coarseJump - 3) { - /* Flicker control auto-disabled */ - sd->params.flickerControl.disabled = 1; - } - - if (sd->params.flickerControl.disabled && - old_exp > sd->params.flickerControl.coarseJump + - ROUND_UP_EXP_FOR_FLICKER) { - /* exposure is now high enough to switch - flicker control back on */ - set_flicker(gspca_dev, 1, 1); - } -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ; - reset_camera_params(gspca_dev); - - PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)", - id->idVendor, id->idProduct); - - cam = &gspca_dev->cam; - cam->cam_mode = mode; - cam->nmodes = ARRAY_SIZE(mode); - - goto_low_power(gspca_dev); - /* Check the firmware version. */ - sd->params.version.firmwareVersion = 0; - get_version_information(gspca_dev); - if (sd->params.version.firmwareVersion != 1) { - PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)", - sd->params.version.firmwareVersion); - return -ENODEV; - } - - /* A bug in firmware 1-02 limits gainMode to 2 */ - if (sd->params.version.firmwareRevision <= 2 && - sd->params.exposure.gainMode > 2) { - sd->params.exposure.gainMode = 2; - } - - /* set QX3 detected flag */ - sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 && - sd->params.pnpID.product == 0x0001); - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int priv, ret; - - /* Start the camera in low power mode */ - if (goto_low_power(gspca_dev)) { - if (sd->params.status.systemState != WARM_BOOT_STATE) { - PDEBUG(D_ERR, "unexpected systemstate: %02x", - sd->params.status.systemState); - printstatus(&sd->params); - return -ENODEV; - } - - /* FIXME: this is just dirty trial and error */ - ret = goto_high_power(gspca_dev); - if (ret) - return ret; - - ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame, - 0, 0, 0, 0); - if (ret) - return ret; - - ret = goto_low_power(gspca_dev); - if (ret) - return ret; - } - - /* procedure described in developer's guide p3-28 */ - - /* Check the firmware version. */ - sd->params.version.firmwareVersion = 0; - get_version_information(gspca_dev); - - /* The fatal error checking should be done after - * the camera powers up (developer's guide p 3-38) */ - - /* Set streamState before transition to high power to avoid bug - * in firmware 1-02 */ - ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus, - STREAMSTATE, 0, STREAM_NOT_READY, 0); - if (ret) - return ret; - - /* GotoHiPower */ - ret = goto_high_power(gspca_dev); - if (ret) - return ret; - - /* Check the camera status */ - ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - if (ret) - return ret; - - if (sd->params.status.fatalError) { - PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x", - sd->params.status.fatalError, - sd->params.status.vpStatus); - return -EIO; - } - - /* VPVersion can't be retrieved before the camera is in HiPower, - * so get it here instead of in get_version_information. */ - ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); - if (ret) - return ret; - - /* Determine video mode settings */ - sd->params.streamStartLine = 120; - - priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - if (priv & 0x01) { /* crop */ - sd->params.roi.colStart = 2; - sd->params.roi.rowStart = 6; - } else { - sd->params.roi.colStart = 0; - sd->params.roi.rowStart = 0; - } - - if (priv & 0x02) { /* quarter */ - sd->params.format.videoSize = VIDEOSIZE_QCIF; - sd->params.roi.colStart /= 2; - sd->params.roi.rowStart /= 2; - sd->params.streamStartLine /= 2; - } else - sd->params.format.videoSize = VIDEOSIZE_CIF; - - sd->params.roi.colEnd = sd->params.roi.colStart + - (gspca_dev->width >> 3); - sd->params.roi.rowEnd = sd->params.roi.rowStart + - (gspca_dev->height >> 2); - - /* And now set the camera to a known state */ - ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_CONTINEOUS, 0, 0, 0); - if (ret) - return ret; - /* We start with compression disabled, as we need one uncompressed - frame to handle later compressed frames */ - ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression, - CPIA_COMPRESSION_NONE, - NO_DECIMATION, 0, 0); - if (ret) - return ret; - ret = command_setcompressiontarget(gspca_dev); - if (ret) - return ret; - ret = command_setcolourparams(gspca_dev); - if (ret) - return ret; - ret = command_setformat(gspca_dev); - if (ret) - return ret; - ret = command_setyuvtresh(gspca_dev); - if (ret) - return ret; - ret = command_setecptiming(gspca_dev); - if (ret) - return ret; - ret = command_setcompressionparams(gspca_dev); - if (ret) - return ret; - ret = command_setexposure(gspca_dev); - if (ret) - return ret; - ret = command_setcolourbalance(gspca_dev); - if (ret) - return ret; - ret = command_setsensorfps(gspca_dev); - if (ret) - return ret; - ret = command_setapcor(gspca_dev); - if (ret) - return ret; - ret = command_setflickerctrl(gspca_dev); - if (ret) - return ret; - ret = command_setvloffset(gspca_dev); - if (ret) - return ret; - - /* Start stream */ - ret = command_resume(gspca_dev); - if (ret) - return ret; - - /* Wait 6 frames before turning compression on for the sensor to get - all settings and AEC/ACB to settle */ - sd->first_frame = 6; - sd->exposure_status = EXPOSURE_NORMAL; - sd->exposure_count = 0; - atomic_set(&sd->cam_exposure, 0); - atomic_set(&sd->fps, 0); - - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - command_pause(gspca_dev); - - /* save camera state for later open (developers guide ch 3.5.3) */ - save_camera_state(gspca_dev); - - /* GotoLoPower */ - goto_low_power(gspca_dev); - - /* Update the camera status */ - do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - /* If the last button state is pressed, release it now! */ - if (sd->params.qx3.button) { - /* The camera latch will hold the pressed state until we reset - the latch, so we do not reset sd->params.qx3.button now, to - avoid a false keypress being reported the next sd_start */ - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - } -#endif -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - /* Start / Stop the camera to make sure we are talking to - a supported camera, and to get some information from it - to print. */ - ret = sd_start(gspca_dev); - if (ret) - return ret; - - /* Ensure the QX3 illuminators' states are restored upon resume, - or disable the illuminator controls, if this isn't a QX3 */ - if (sd->params.qx3.qx3_detected) - command_setlights(gspca_dev); - - sd_stopN(gspca_dev); - - PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)", - sd->params.version.firmwareVersion, - sd->params.version.firmwareRevision, - sd->params.version.vcVersion, - sd->params.version.vcRevision); - PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x", - sd->params.pnpID.vendor, sd->params.pnpID.product, - sd->params.pnpID.deviceRevision); - PDEBUG(D_PROBE, "VP-Version: %d.%d %04x", - sd->params.vpVersion.vpVersion, - sd->params.vpVersion.vpRevision, - sd->params.vpVersion.cameraHeadID); - - return 0; -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, - int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* Check for SOF */ - if (len >= 64 && - data[0] == MAGIC_0 && data[1] == MAGIC_1 && - data[16] == sd->params.format.videoSize && - data[17] == sd->params.format.subSample && - data[18] == sd->params.format.yuvOrder && - data[24] == sd->params.roi.colStart && - data[25] == sd->params.roi.colEnd && - data[26] == sd->params.roi.rowStart && - data[27] == sd->params.roi.rowEnd) { - u8 *image; - - atomic_set(&sd->cam_exposure, data[39] * 2); - atomic_set(&sd->fps, data[41]); - - /* Check for proper EOF for last frame */ - image = gspca_dev->image; - if (image != NULL && - gspca_dev->image_len > 4 && - image[gspca_dev->image_len - 4] == 0xff && - image[gspca_dev->image_len - 3] == 0xff && - image[gspca_dev->image_len - 2] == 0xff && - image[gspca_dev->image_len - 1] == 0xff) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - return; - } - - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static void sd_dq_callback(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* Set the normal compression settings once we have captured a - few uncompressed frames (and AEC has hopefully settled) */ - if (sd->first_frame) { - sd->first_frame--; - if (sd->first_frame == 0) - command_setcompression(gspca_dev); - } - - /* Switch flicker control back on if it got turned off */ - restart_flicker(gspca_dev); - - /* If AEC is enabled, monitor the exposure and - adjust the sensor frame rate if needed */ - if (sd->params.exposure.expMode == 2) - monitor_exposure(gspca_dev); - - /* Update our knowledge of the camera state */ - do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - sd->params.colourParams.brightness = ctrl->val; - sd->params.flickerControl.allowableOverExposure = - find_over_exposure(sd->params.colourParams.brightness); - gspca_dev->usb_err = command_setcolourparams(gspca_dev); - if (!gspca_dev->usb_err) - gspca_dev->usb_err = command_setflickerctrl(gspca_dev); - break; - case V4L2_CID_CONTRAST: - sd->params.colourParams.contrast = ctrl->val; - gspca_dev->usb_err = command_setcolourparams(gspca_dev); - break; - case V4L2_CID_SATURATION: - sd->params.colourParams.saturation = ctrl->val; - gspca_dev->usb_err = command_setcolourparams(gspca_dev); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ; - sd->params.flickerControl.coarseJump = - flicker_jumps[sd->mainsFreq] - [sd->params.sensorFps.baserate] - [sd->params.sensorFps.divisor]; - - gspca_dev->usb_err = set_flicker(gspca_dev, - ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, - gspca_dev->streaming); - break; - case V4L2_CID_ILLUMINATORS_1: - sd->params.qx3.bottomlight = ctrl->val; - gspca_dev->usb_err = command_setlights(gspca_dev); - break; - case V4L2_CID_ILLUMINATORS_2: - sd->params.qx3.toplight = ctrl->val; - gspca_dev->usb_err = command_setlights(gspca_dev); - break; - case CPIA1_CID_COMP_TARGET: - sd->params.compressionTarget.frTargeting = ctrl->val; - gspca_dev->usb_err = command_setcompressiontarget(gspca_dev); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - static const char * const comp_target_menu[] = { - "Quality", - "Framerate", - NULL - }; - static const struct v4l2_ctrl_config comp_target = { - .ops = &sd_ctrl_ops, - .id = CPIA1_CID_COMP_TARGET, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Compression Target", - .qmenu = comp_target_menu, - .max = 1, - .def = COMP_TARGET_DEF, - }; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 7); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF); - sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, - FREQ_DEF); - if (sd->params.qx3.qx3_detected) { - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_ILLUMINATORS_1, 0, 1, 1, - ILLUMINATORS_1_DEF); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_ILLUMINATORS_2, 0, 1, 1, - ILLUMINATORS_2_DEF); - } - v4l2_ctrl_new_custom(hdl, &comp_target, NULL); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .dq_callback = sd_dq_callback, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .other_input = 1, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0553, 0x0002)}, - {USB_DEVICE(0x0813, 0x0001)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c deleted file mode 100644 index 38f68e11c3a2..000000000000 --- a/drivers/media/video/gspca/etoms.c +++ /dev/null @@ -1,799 +0,0 @@ -/* - * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004) - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "etoms" - -#include "gspca.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("Etoms USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - unsigned char autogain; - - char sensor; -#define SENSOR_PAS106 0 -#define SENSOR_TAS5130CXX 1 - signed char ag_cnt; -#define AG_CNT_START 13 -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, -/* {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, */ -}; - -static const struct v4l2_pix_format sif_mode[] = { - {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -#define ETOMS_ALT_SIZE_1000 12 - -#define ET_GPIO_DIR_CTRL 0x04 /* Control IO bit[0..5] (0 in 1 out) */ -#define ET_GPIO_OUT 0x05 /* Only IO data */ -#define ET_GPIO_IN 0x06 /* Read Only IO data */ -#define ET_RESET_ALL 0x03 -#define ET_ClCK 0x01 -#define ET_CTRL 0x02 /* enable i2c OutClck Powerdown mode */ - -#define ET_COMP 0x12 /* Compression register */ -#define ET_MAXQt 0x13 -#define ET_MINQt 0x14 -#define ET_COMP_VAL0 0x02 -#define ET_COMP_VAL1 0x03 - -#define ET_REG1d 0x1d -#define ET_REG1e 0x1e -#define ET_REG1f 0x1f -#define ET_REG20 0x20 -#define ET_REG21 0x21 -#define ET_REG22 0x22 -#define ET_REG23 0x23 -#define ET_REG24 0x24 -#define ET_REG25 0x25 -/* base registers for luma calculation */ -#define ET_LUMA_CENTER 0x39 - -#define ET_G_RED 0x4d -#define ET_G_GREEN1 0x4e -#define ET_G_BLUE 0x4f -#define ET_G_GREEN2 0x50 -#define ET_G_GR_H 0x51 -#define ET_G_GB_H 0x52 - -#define ET_O_RED 0x34 -#define ET_O_GREEN1 0x35 -#define ET_O_BLUE 0x36 -#define ET_O_GREEN2 0x37 - -#define ET_SYNCHRO 0x68 -#define ET_STARTX 0x69 -#define ET_STARTY 0x6a -#define ET_WIDTH_LOW 0x6b -#define ET_HEIGTH_LOW 0x6c -#define ET_W_H_HEIGTH 0x6d - -#define ET_REG6e 0x6e /* OBW */ -#define ET_REG6f 0x6f /* OBW */ -#define ET_REG70 0x70 /* OBW_AWB */ -#define ET_REG71 0x71 /* OBW_AWB */ -#define ET_REG72 0x72 /* OBW_AWB */ -#define ET_REG73 0x73 /* Clkdelay ns */ -#define ET_REG74 0x74 /* test pattern */ -#define ET_REG75 0x75 /* test pattern */ - -#define ET_I2C_CLK 0x8c -#define ET_PXL_CLK 0x60 - -#define ET_I2C_BASE 0x89 -#define ET_I2C_COUNT 0x8a -#define ET_I2C_PREFETCH 0x8b -#define ET_I2C_REG 0x88 -#define ET_I2C_DATA7 0x87 -#define ET_I2C_DATA6 0x86 -#define ET_I2C_DATA5 0x85 -#define ET_I2C_DATA4 0x84 -#define ET_I2C_DATA3 0x83 -#define ET_I2C_DATA2 0x82 -#define ET_I2C_DATA1 0x81 -#define ET_I2C_DATA0 0x80 - -#define PAS106_REG2 0x02 /* pxlClk = systemClk/(reg2) */ -#define PAS106_REG3 0x03 /* line/frame H [11..4] */ -#define PAS106_REG4 0x04 /* line/frame L [3..0] */ -#define PAS106_REG5 0x05 /* exposure time line offset(default 5) */ -#define PAS106_REG6 0x06 /* exposure time pixel offset(default 6) */ -#define PAS106_REG7 0x07 /* signbit Dac (default 0) */ -#define PAS106_REG9 0x09 -#define PAS106_REG0e 0x0e /* global gain [4..0](default 0x0e) */ -#define PAS106_REG13 0x13 /* end i2c write */ - -static const __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; - -static const __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d }; - -static const __u8 I2c3[] = { 0x12, 0x05 }; - -static const __u8 I2c4[] = { 0x41, 0x08 }; - -/* read 'len' bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 index, - __u16 len) -{ - struct usb_device *dev = gspca_dev->dev; - -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); - return; - } -#endif - usb_control_msg(dev, - usb_rcvctrlpipe(dev, 0), - 0, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0, - index, gspca_dev->usb_buf, len, 500); - PDEBUG(D_USBI, "reg read [%02x] -> %02x ..", - index, gspca_dev->usb_buf[0]); -} - -static void reg_w_val(struct gspca_dev *gspca_dev, - __u16 index, - __u8 val) -{ - struct usb_device *dev = gspca_dev->dev; - - gspca_dev->usb_buf[0] = val; - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0, - index, gspca_dev->usb_buf, 1, 500); -} - -static void reg_w(struct gspca_dev *gspca_dev, - __u16 index, - const __u8 *buffer, - __u16 len) -{ - struct usb_device *dev = gspca_dev->dev; - -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - pr_err("reg_w: buffer overflow\n"); - return; - } - PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); -#endif - memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0, index, gspca_dev->usb_buf, len, 500); -} - -static int i2c_w(struct gspca_dev *gspca_dev, - __u8 reg, - const __u8 *buffer, - int len, __u8 mode) -{ - /* buffer should be [D0..D7] */ - __u8 ptchcount; - - /* set the base address */ - reg_w_val(gspca_dev, ET_I2C_BASE, 0x40); - /* sensor base for the pas106 */ - /* set count and prefetch */ - ptchcount = ((len & 0x07) << 4) | (mode & 0x03); - reg_w_val(gspca_dev, ET_I2C_COUNT, ptchcount); - /* set the register base */ - reg_w_val(gspca_dev, ET_I2C_REG, reg); - while (--len >= 0) - reg_w_val(gspca_dev, ET_I2C_DATA0 + len, buffer[len]); - return 0; -} - -static int i2c_r(struct gspca_dev *gspca_dev, - __u8 reg) -{ - /* set the base address */ - reg_w_val(gspca_dev, ET_I2C_BASE, 0x40); - /* sensor base for the pas106 */ - /* set count and prefetch (cnd: 4 bits - mode: 4 bits) */ - reg_w_val(gspca_dev, ET_I2C_COUNT, 0x11); - reg_w_val(gspca_dev, ET_I2C_REG, reg); /* set the register base */ - reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x02); /* prefetch */ - reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x00); - reg_r(gspca_dev, ET_I2C_DATA0, 1); /* read one byte */ - return 0; -} - -static int Et_WaitStatus(struct gspca_dev *gspca_dev) -{ - int retry = 10; - - while (retry--) { - reg_r(gspca_dev, ET_ClCK, 1); - if (gspca_dev->usb_buf[0] != 0) - return 1; - } - return 0; -} - -static int et_video(struct gspca_dev *gspca_dev, - int on) -{ - int ret; - - reg_w_val(gspca_dev, ET_GPIO_OUT, - on ? 0x10 /* startvideo - set Bit5 */ - : 0); /* stopvideo */ - ret = Et_WaitStatus(gspca_dev); - if (ret != 0) - PDEBUG(D_ERR, "timeout video on/off"); - return ret; -} - -static void Et_init2(struct gspca_dev *gspca_dev) -{ - __u8 value; - static const __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 }; - - PDEBUG(D_STREAM, "Open Init2 ET"); - reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 0x2f); - reg_w_val(gspca_dev, ET_GPIO_OUT, 0x10); - reg_r(gspca_dev, ET_GPIO_IN, 1); - reg_w_val(gspca_dev, ET_ClCK, 0x14); /* 0x14 // 0x16 enabled pattern */ - reg_w_val(gspca_dev, ET_CTRL, 0x1b); - - /* compression et subsampling */ - if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) - value = ET_COMP_VAL1; /* 320 */ - else - value = ET_COMP_VAL0; /* 640 */ - reg_w_val(gspca_dev, ET_COMP, value); - reg_w_val(gspca_dev, ET_MAXQt, 0x1f); - reg_w_val(gspca_dev, ET_MINQt, 0x04); - /* undocumented registers */ - reg_w_val(gspca_dev, ET_REG1d, 0xff); - reg_w_val(gspca_dev, ET_REG1e, 0xff); - reg_w_val(gspca_dev, ET_REG1f, 0xff); - reg_w_val(gspca_dev, ET_REG20, 0x35); - reg_w_val(gspca_dev, ET_REG21, 0x01); - reg_w_val(gspca_dev, ET_REG22, 0x00); - reg_w_val(gspca_dev, ET_REG23, 0xff); - reg_w_val(gspca_dev, ET_REG24, 0xff); - reg_w_val(gspca_dev, ET_REG25, 0x0f); - /* colors setting */ - reg_w_val(gspca_dev, 0x30, 0x11); /* 0x30 */ - reg_w_val(gspca_dev, 0x31, 0x40); - reg_w_val(gspca_dev, 0x32, 0x00); - reg_w_val(gspca_dev, ET_O_RED, 0x00); /* 0x34 */ - reg_w_val(gspca_dev, ET_O_GREEN1, 0x00); - reg_w_val(gspca_dev, ET_O_BLUE, 0x00); - reg_w_val(gspca_dev, ET_O_GREEN2, 0x00); - /*************/ - reg_w_val(gspca_dev, ET_G_RED, 0x80); /* 0x4d */ - reg_w_val(gspca_dev, ET_G_GREEN1, 0x80); - reg_w_val(gspca_dev, ET_G_BLUE, 0x80); - reg_w_val(gspca_dev, ET_G_GREEN2, 0x80); - reg_w_val(gspca_dev, ET_G_GR_H, 0x00); - reg_w_val(gspca_dev, ET_G_GB_H, 0x00); /* 0x52 */ - /* Window control registers */ - reg_w_val(gspca_dev, 0x61, 0x80); /* use cmc_out */ - reg_w_val(gspca_dev, 0x62, 0x02); - reg_w_val(gspca_dev, 0x63, 0x03); - reg_w_val(gspca_dev, 0x64, 0x14); - reg_w_val(gspca_dev, 0x65, 0x0e); - reg_w_val(gspca_dev, 0x66, 0x02); - reg_w_val(gspca_dev, 0x67, 0x02); - - /**************************************/ - reg_w_val(gspca_dev, ET_SYNCHRO, 0x8f); /* 0x68 */ - reg_w_val(gspca_dev, ET_STARTX, 0x69); /* 0x6a //0x69 */ - reg_w_val(gspca_dev, ET_STARTY, 0x0d); /* 0x0d //0x0c */ - reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x80); - reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0xe0); - reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x60); /* 6d */ - reg_w_val(gspca_dev, ET_REG6e, 0x86); - reg_w_val(gspca_dev, ET_REG6f, 0x01); - reg_w_val(gspca_dev, ET_REG70, 0x26); - reg_w_val(gspca_dev, ET_REG71, 0x7a); - reg_w_val(gspca_dev, ET_REG72, 0x01); - /* Clock Pattern registers ***************** */ - reg_w_val(gspca_dev, ET_REG73, 0x00); - reg_w_val(gspca_dev, ET_REG74, 0x18); /* 0x28 */ - reg_w_val(gspca_dev, ET_REG75, 0x0f); /* 0x01 */ - /**********************************************/ - reg_w_val(gspca_dev, 0x8a, 0x20); - reg_w_val(gspca_dev, 0x8d, 0x0f); - reg_w_val(gspca_dev, 0x8e, 0x08); - /**************************************/ - reg_w_val(gspca_dev, 0x03, 0x08); - reg_w_val(gspca_dev, ET_PXL_CLK, 0x03); - reg_w_val(gspca_dev, 0x81, 0xff); - reg_w_val(gspca_dev, 0x80, 0x00); - reg_w_val(gspca_dev, 0x81, 0xff); - reg_w_val(gspca_dev, 0x80, 0x20); - reg_w_val(gspca_dev, 0x03, 0x01); - reg_w_val(gspca_dev, 0x03, 0x00); - reg_w_val(gspca_dev, 0x03, 0x08); - /********************************************/ - -/* reg_r(gspca_dev, ET_I2C_BASE, 1); - always 0x40 as the pas106 ??? */ - /* set the sensor */ - if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) - value = 0x04; /* 320 */ - else /* 640 */ - value = 0x1e; /* 0x17 * setting PixelClock - * 0x03 mean 24/(3+1) = 6 Mhz - * 0x05 -> 24/(5+1) = 4 Mhz - * 0x0b -> 24/(11+1) = 2 Mhz - * 0x17 -> 24/(23+1) = 1 Mhz - */ - reg_w_val(gspca_dev, ET_PXL_CLK, value); - /* now set by fifo the FormatLine setting */ - reg_w(gspca_dev, 0x62, FormLine, 6); - - /* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */ - reg_w_val(gspca_dev, 0x81, 0x47); /* 0x47; */ - reg_w_val(gspca_dev, 0x80, 0x40); /* 0x40; */ - /* Pedro change */ - /* Brightness change Brith+ decrease value */ - /* Brigth- increase value */ - /* original value = 0x70; */ - reg_w_val(gspca_dev, 0x81, 0x30); /* 0x20; - set brightness */ - reg_w_val(gspca_dev, 0x80, 0x20); /* 0x20; */ -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - int i; - - for (i = 0; i < 4; i++) - reg_w_val(gspca_dev, ET_O_RED + i, val); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; - - memset(RGBG, val, sizeof(RGBG) - 2); - reg_w(gspca_dev, ET_G_RED, RGBG, 6); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d }; - __u8 i2cflags = 0x01; - /* __u8 green = 0; */ - - I2cc[3] = val; /* red */ - I2cc[0] = 15 - val; /* blue */ - /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */ - /* I2cc[1] = I2cc[2] = green; */ - if (sd->sensor == SENSOR_PAS106) { - i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3); - i2c_w(gspca_dev, PAS106_REG9, I2cc, sizeof I2cc, 1); - } -/* PDEBUG(D_CONF , "Etoms red %d blue %d green %d", - I2cc[3], I2cc[0], green); */ -} - -static s32 getcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAS106) { -/* i2c_r(gspca_dev, PAS106_REG9); * blue */ - i2c_r(gspca_dev, PAS106_REG9 + 3); /* red */ - return gspca_dev->usb_buf[0] & 0x0f; - } - return 0; -} - -static void setautogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->autogain) - sd->ag_cnt = AG_CNT_START; - else - sd->ag_cnt = -1; -} - -static void Et_init1(struct gspca_dev *gspca_dev) -{ - __u8 value; -/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0x22, 0xac, 0x00, 0x01, 0x00}; */ - __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 }; - /* try 1/120 0x6d 0xcd 0x40 */ -/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0xfe, 0xfe, 0xc0, 0x01, 0x00}; - * 1/60000 hmm ?? */ - - PDEBUG(D_STREAM, "Open Init1 ET"); - reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 7); - reg_r(gspca_dev, ET_GPIO_IN, 1); - reg_w_val(gspca_dev, ET_RESET_ALL, 1); - reg_w_val(gspca_dev, ET_RESET_ALL, 0); - reg_w_val(gspca_dev, ET_ClCK, 0x10); - reg_w_val(gspca_dev, ET_CTRL, 0x19); - /* compression et subsampling */ - if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) - value = ET_COMP_VAL1; - else - value = ET_COMP_VAL0; - PDEBUG(D_STREAM, "Open mode %d Compression %d", - gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv, - value); - reg_w_val(gspca_dev, ET_COMP, value); - reg_w_val(gspca_dev, ET_MAXQt, 0x1d); - reg_w_val(gspca_dev, ET_MINQt, 0x02); - /* undocumented registers */ - reg_w_val(gspca_dev, ET_REG1d, 0xff); - reg_w_val(gspca_dev, ET_REG1e, 0xff); - reg_w_val(gspca_dev, ET_REG1f, 0xff); - reg_w_val(gspca_dev, ET_REG20, 0x35); - reg_w_val(gspca_dev, ET_REG21, 0x01); - reg_w_val(gspca_dev, ET_REG22, 0x00); - reg_w_val(gspca_dev, ET_REG23, 0xf7); - reg_w_val(gspca_dev, ET_REG24, 0xff); - reg_w_val(gspca_dev, ET_REG25, 0x07); - /* colors setting */ - reg_w_val(gspca_dev, ET_G_RED, 0x80); - reg_w_val(gspca_dev, ET_G_GREEN1, 0x80); - reg_w_val(gspca_dev, ET_G_BLUE, 0x80); - reg_w_val(gspca_dev, ET_G_GREEN2, 0x80); - reg_w_val(gspca_dev, ET_G_GR_H, 0x00); - reg_w_val(gspca_dev, ET_G_GB_H, 0x00); - /* Window control registers */ - reg_w_val(gspca_dev, ET_SYNCHRO, 0xf0); - reg_w_val(gspca_dev, ET_STARTX, 0x56); /* 0x56 */ - reg_w_val(gspca_dev, ET_STARTY, 0x05); /* 0x04 */ - reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x60); - reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0x20); - reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x50); - reg_w_val(gspca_dev, ET_REG6e, 0x86); - reg_w_val(gspca_dev, ET_REG6f, 0x01); - reg_w_val(gspca_dev, ET_REG70, 0x86); - reg_w_val(gspca_dev, ET_REG71, 0x14); - reg_w_val(gspca_dev, ET_REG72, 0x00); - /* Clock Pattern registers */ - reg_w_val(gspca_dev, ET_REG73, 0x00); - reg_w_val(gspca_dev, ET_REG74, 0x00); - reg_w_val(gspca_dev, ET_REG75, 0x0a); - reg_w_val(gspca_dev, ET_I2C_CLK, 0x04); - reg_w_val(gspca_dev, ET_PXL_CLK, 0x01); - /* set the sensor */ - if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - I2c0[0] = 0x06; - i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1); - i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1); - value = 0x06; - i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1); - i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1); - /* value = 0x1f; */ - value = 0x04; - i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1); - } else { - I2c0[0] = 0x0a; - - i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1); - i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1); - value = 0x0a; - i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1); - i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1); - value = 0x04; - /* value = 0x10; */ - i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1); - /* bit 2 enable bit 1:2 select 0 1 2 3 - value = 0x07; * curve 0 * - i2c_w(gspca_dev, PAS106_REG0f, &value, 1, 1); - */ - } - -/* value = 0x01; */ -/* value = 0x22; */ -/* i2c_w(gspca_dev, PAS106_REG5, &value, 1, 1); */ - /* magnetude and sign bit for DAC */ - i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1); - /* now set by fifo the whole colors setting */ - reg_w(gspca_dev, ET_G_RED, GainRGBG, 6); - setcolors(gspca_dev, getcolors(gspca_dev)); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - sd->sensor = id->driver_info; - if (sd->sensor == SENSOR_PAS106) { - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); - } else { - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - } - sd->ag_cnt = -1; - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAS106) - Et_init1(gspca_dev); - else - Et_init2(gspca_dev); - reg_w_val(gspca_dev, ET_RESET_ALL, 0x08); - et_video(gspca_dev, 0); /* video off */ - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAS106) - Et_init1(gspca_dev); - else - Et_init2(gspca_dev); - - setautogain(gspca_dev); - - reg_w_val(gspca_dev, ET_RESET_ALL, 0x08); - et_video(gspca_dev, 1); /* video on */ - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - et_video(gspca_dev, 0); /* video off */ -} - -static __u8 Et_getgainG(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAS106) { - i2c_r(gspca_dev, PAS106_REG0e); - PDEBUG(D_CONF, "Etoms gain G %d", gspca_dev->usb_buf[0]); - return gspca_dev->usb_buf[0]; - } - return 0x1f; -} - -static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAS106) { - __u8 i2cflags = 0x01; - - i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3); - i2c_w(gspca_dev, PAS106_REG0e, &gain, 1, 1); - } -} - -#define BLIMIT(bright) \ - (u8)((bright > 0x1f) ? 0x1f : ((bright < 4) ? 3 : bright)) -#define LIMIT(color) \ - (u8)((color > 0xff) ? 0xff : ((color < 0) ? 0 : color)) - -static void do_autogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 luma; - __u8 luma_mean = 128; - __u8 luma_delta = 20; - __u8 spring = 4; - int Gbright; - __u8 r, g, b; - - if (sd->ag_cnt < 0) - return; - if (--sd->ag_cnt >= 0) - return; - sd->ag_cnt = AG_CNT_START; - - Gbright = Et_getgainG(gspca_dev); - reg_r(gspca_dev, ET_LUMA_CENTER, 4); - g = (gspca_dev->usb_buf[0] + gspca_dev->usb_buf[3]) >> 1; - r = gspca_dev->usb_buf[1]; - b = gspca_dev->usb_buf[2]; - r = ((r << 8) - (r << 4) - (r << 3)) >> 10; - b = ((b << 7) >> 10); - g = ((g << 9) + (g << 7) + (g << 5)) >> 10; - luma = LIMIT(r + g + b); - PDEBUG(D_FRAM, "Etoms luma G %d", luma); - if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) { - Gbright += (luma_mean - luma) >> spring; - Gbright = BLIMIT(Gbright); - PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright); - Et_setgainG(gspca_dev, (__u8) Gbright); - } -} - -#undef BLIMIT -#undef LIMIT - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - int seqframe; - - seqframe = data[0] & 0x3f; - len = (int) (((data[0] & 0xc0) << 2) | data[1]); - if (seqframe == 0x3f) { - PDEBUG(D_FRAM, - "header packet found datalength %d !!", len); - PDEBUG(D_FRAM, "G %d R %d G %d B %d", - data[2], data[3], data[4], data[5]); - data += 30; - /* don't change datalength as the chips provided it */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - return; - } - if (len) { - data += 8; - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - } else { /* Drop Packet */ - gspca_dev->last_packet_type = DISCARD_PACKET; - } -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - sd->autogain = ctrl->val; - setautogain(gspca_dev); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 1, 127, 1, 63); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 127); - if (sd->sensor == SENSOR_PAS106) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 15, 1, 7); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .dq_callback = do_autogain, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106}, -#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE - {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX}, -#endif - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c deleted file mode 100644 index c8f2201cc35a..000000000000 --- a/drivers/media/video/gspca/finepix.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Fujifilm Finepix subdriver - * - * Copyright (C) 2008 Frank Zago - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "finepix" - -#include "gspca.h" - -MODULE_AUTHOR("Frank Zago "); -MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver"); -MODULE_LICENSE("GPL"); - -/* Default timeout, in ms */ -#define FPIX_TIMEOUT 250 - -/* Maximum transfer size to use. The windows driver reads by chunks of - * 0x2000 bytes, so do the same. Note: reading more seems to work - * too. */ -#define FPIX_MAX_TRANSFER 0x2000 - -/* Structure to hold all of our device specific stuff */ -struct usb_fpix { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct work_struct work_struct; - struct workqueue_struct *work_thread; -}; - -/* Delay after which claim the next frame. If the delay is too small, - * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms - * will fail every 4 or 5 frames, but 30ms is perfect. On the A210, - * 30ms is bad while 35ms is perfect. */ -#define NEXT_FRAME_DELAY 35 - -/* These cameras only support 320x200. */ -static const struct v4l2_pix_format fpix_mode[1] = { - { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0} -}; - -/* send a command to the webcam */ -static int command(struct gspca_dev *gspca_dev, - int order) /* 0: reset, 1: frame request */ -{ - static u8 order_values[2][12] = { - {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */ - {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */ - }; - - memcpy(gspca_dev->usb_buf, order_values[order], 12); - return usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_OUT | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, - 12, FPIX_TIMEOUT); -} - -/* workqueue */ -static void dostream(struct work_struct *work) -{ - struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct); - struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct urb *urb = gspca_dev->urb[0]; - u8 *data = urb->transfer_buffer; - int ret = 0; - int len; - - /* synchronize with the main driver */ - mutex_lock(&gspca_dev->usb_lock); - mutex_unlock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, "dostream started"); - - /* loop reading a frame */ -again: - while (gspca_dev->dev && gspca_dev->streaming) { -#ifdef CONFIG_PM - if (gspca_dev->frozen) - break; -#endif - - /* request a frame */ - mutex_lock(&gspca_dev->usb_lock); - ret = command(gspca_dev, 1); - mutex_unlock(&gspca_dev->usb_lock); - if (ret < 0) - break; -#ifdef CONFIG_PM - if (gspca_dev->frozen) - break; -#endif - if (!gspca_dev->dev || !gspca_dev->streaming) - break; - - /* the frame comes in parts */ - for (;;) { - ret = usb_bulk_msg(gspca_dev->dev, - urb->pipe, - data, - FPIX_MAX_TRANSFER, - &len, FPIX_TIMEOUT); - if (ret < 0) { - /* Most of the time we get a timeout - * error. Just restart. */ - goto again; - } -#ifdef CONFIG_PM - if (gspca_dev->frozen) - goto out; -#endif - if (!gspca_dev->dev || !gspca_dev->streaming) - goto out; - if (len < FPIX_MAX_TRANSFER || - (data[len - 2] == 0xff && - data[len - 1] == 0xd9)) { - - /* If the result is less than what was asked - * for, then it's the end of the - * frame. Sometimes the jpeg is not complete, - * but there's nothing we can do. We also end - * here if the the jpeg ends right at the end - * of the frame. */ - gspca_frame_add(gspca_dev, LAST_PACKET, - data, len); - break; - } - - /* got a partial image */ - gspca_frame_add(gspca_dev, - gspca_dev->last_packet_type - == LAST_PACKET - ? FIRST_PACKET : INTER_PACKET, - data, len); - } - - /* We must wait before trying reading the next - * frame. If we don't, or if the delay is too short, - * the camera will disconnect. */ - msleep(NEXT_FRAME_DELAY); - } - -out: - PDEBUG(D_STREAM, "dostream stopped"); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - - cam->cam_mode = fpix_mode; - cam->nmodes = 1; - cam->bulk = 1; - cam->bulk_size = FPIX_MAX_TRANSFER; - - INIT_WORK(&dev->work_struct, dostream); - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -/* start the camera */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - int ret, len; - - /* Init the device */ - ret = command(gspca_dev, 0); - if (ret < 0) { - pr_err("init failed %d\n", ret); - return ret; - } - - /* Read the result of the command. Ignore the result, for it - * varies with the device. */ - ret = usb_bulk_msg(gspca_dev->dev, - gspca_dev->urb[0]->pipe, - gspca_dev->urb[0]->transfer_buffer, - FPIX_MAX_TRANSFER, &len, - FPIX_TIMEOUT); - if (ret < 0) { - pr_err("usb_bulk_msg failed %d\n", ret); - return ret; - } - - /* Request a frame, but don't read it */ - ret = command(gspca_dev, 1); - if (ret < 0) { - pr_err("frame request failed %d\n", ret); - return ret; - } - - /* Again, reset bulk in endpoint */ - usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); - - /* Start the workqueue function to do the streaming */ - dev->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(dev->work_thread, &dev->work_struct); - - return 0; -} - -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - /* wait for the work queue to terminate */ - mutex_unlock(&gspca_dev->usb_lock); - destroy_workqueue(dev->work_thread); - mutex_lock(&gspca_dev->usb_lock); - dev->work_thread = NULL; -} - -/* Table of supported USB devices */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x04cb, 0x0104)}, - {USB_DEVICE(0x04cb, 0x0109)}, - {USB_DEVICE(0x04cb, 0x010b)}, - {USB_DEVICE(0x04cb, 0x010f)}, - {USB_DEVICE(0x04cb, 0x0111)}, - {USB_DEVICE(0x04cb, 0x0113)}, - {USB_DEVICE(0x04cb, 0x0115)}, - {USB_DEVICE(0x04cb, 0x0117)}, - {USB_DEVICE(0x04cb, 0x0119)}, - {USB_DEVICE(0x04cb, 0x011b)}, - {USB_DEVICE(0x04cb, 0x011d)}, - {USB_DEVICE(0x04cb, 0x0121)}, - {USB_DEVICE(0x04cb, 0x0123)}, - {USB_DEVICE(0x04cb, 0x0125)}, - {USB_DEVICE(0x04cb, 0x0127)}, - {USB_DEVICE(0x04cb, 0x0129)}, - {USB_DEVICE(0x04cb, 0x012b)}, - {USB_DEVICE(0x04cb, 0x012d)}, - {USB_DEVICE(0x04cb, 0x012f)}, - {USB_DEVICE(0x04cb, 0x0131)}, - {USB_DEVICE(0x04cb, 0x013b)}, - {USB_DEVICE(0x04cb, 0x013d)}, - {USB_DEVICE(0x04cb, 0x013f)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stop0 = sd_stop0, -}; - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, - &sd_desc, - sizeof(struct usb_fpix), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/gl860/Kconfig b/drivers/media/video/gspca/gl860/Kconfig deleted file mode 100644 index 22772f53ec7b..000000000000 --- a/drivers/media/video/gspca/gl860/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config USB_GL860 - tristate "GL860 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the GL860 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_gl860. diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile deleted file mode 100644 index 773ea3426561..000000000000 --- a/drivers/media/video/gspca/gl860/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -obj-$(CONFIG_USB_GL860) += gspca_gl860.o - -gspca_gl860-objs := gl860.o \ - gl860-mi1320.o \ - gl860-ov2640.o \ - gl860-ov9655.o \ - gl860-mi2020.o - -ccflags-y += -I$(srctree)/drivers/media/video/gspca - diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c deleted file mode 100644 index b57160e04866..000000000000 --- a/drivers/media/video/gspca/gl860/gl860-mi1320.c +++ /dev/null @@ -1,536 +0,0 @@ -/* Subdriver for the GL860 chip with the MI1320 sensor - * Author Olivier LORIN from own logs - * - * This program is free software; 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 - * 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 . - */ - -/* Sensor : MI1320 */ - -#include "gl860.h" - -static struct validx tbl_common[] = { - {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1}, - {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, - {0xffff, 0xffff}, - {0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1}, - {0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1}, - {0xba70, 0x0006}, {0xba0e, 0x00f1}, - {0xffff, 0xffff}, - {0xba74, 0x0006}, {0xba0e, 0x00f1}, - {0xffff, 0xffff}, - {0x0061, 0x0000}, {0x0068, 0x000d}, -}; - -static struct validx tbl_init_at_startup[] = { - {0x0000, 0x0000}, {0x0010, 0x0010}, - {35, 0xffff}, - {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006}, - {0x006a, 0x000d}, -}; - -static struct validx tbl_sensor_settings_common[] = { - {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000}, - {0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006}, -}; -static struct validx tbl_sensor_settings_1280[] = { - {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, - {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, -}; -static struct validx tbl_sensor_settings_800[] = { - {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, - {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, -}; -static struct validx tbl_sensor_settings_640[] = { - {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, - {0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1}, - {0xba20, 0x0065}, {0xba00, 0x00f1}, -}; -static struct validx tbl_post_unset_alt[] = { - {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, - {0x0061, 0x0000}, {0x0068, 0x000d}, -}; - -static u8 *tbl_1280[] = { - "\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" - "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" - "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" - "\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08" - , - "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" - "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" - "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" - , - "\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1" -}; - -static u8 *tbl_800[] = { - "\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" - "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" - "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" - "\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08" - , - "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" - "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" - "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" - , - "\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59" -}; - -static u8 *tbl_640[] = { - "\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c" - "\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01" - "\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04" - "\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09" - , - "\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6" - "\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c" - "\xd2\x00\xf1\x00\xcb\x00\xf1\x01" - , - "\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1" -}; - -static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d}; -static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70}; -static s32 tbl_backlight[] = {0x0e, 0x06, 0x02}; - -static s32 tbl_cntr1[] = { - 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0}; -static s32 tbl_cntr2[] = { - 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10}; - -static u8 dat_wbalNL[] = - "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10" - "\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20" - "\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00"; - -static u8 dat_wbalLL[] = - "\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40" - "\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00" - "\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00"; - -static u8 dat_wbalBL[] = - "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae" - "\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20" - "\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00"; - -static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00}; - -static u8 dat_common00[] = - "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42" - "\xd8\x04\x58\x00\x04\x02"; -static u8 dat_common01[] = - "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d" - "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0"; -static u8 dat_common02[] = - "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e" - "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00" - "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff"; -static u8 dat_common03[] = - "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda" - "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c" - "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c"; -static u8 dat_common04[] = - "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43"; -static u8 dat_common05[] = - "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68" - "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82" - "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b"; -static u8 dat_common06[] = - "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06" - "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4" - "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f"; -static u8 dat_common07[] = - "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72" - "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03" - "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea" - "\xe1\xff\xf1\x00"; -static u8 dat_common08[] = - "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7" - "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06" - "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a"; -static u8 dat_common09[] = - "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03" - "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa" - "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14"; -static u8 dat_common10[] = - "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00" - "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f" - "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01" - "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10"; -static u8 dat_common11[] = - "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10" - "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00" - "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81"; - -static int mi1320_init_at_startup(struct gspca_dev *gspca_dev); -static int mi1320_configure_alt(struct gspca_dev *gspca_dev); -static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev); -static int mi1320_init_post_alt(struct gspca_dev *gspca_dev); -static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev); -static int mi1320_sensor_settings(struct gspca_dev *gspca_dev); -static int mi1320_camera_settings(struct gspca_dev *gspca_dev); -/*==========================================================================*/ - -void mi1320_init_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vcur.backlight = 0; - sd->vcur.brightness = 0; - sd->vcur.sharpness = 6; - sd->vcur.contrast = 10; - sd->vcur.gamma = 20; - sd->vcur.hue = 0; - sd->vcur.saturation = 6; - sd->vcur.whitebal = 0; - sd->vcur.mirror = 0; - sd->vcur.flip = 0; - sd->vcur.AC50Hz = 1; - - sd->vmax.backlight = 2; - sd->vmax.brightness = 8; - sd->vmax.sharpness = 7; - sd->vmax.contrast = 0; /* 10 but not working with this driver */ - sd->vmax.gamma = 40; - sd->vmax.hue = 5 + 1; - sd->vmax.saturation = 8; - sd->vmax.whitebal = 2; - sd->vmax.mirror = 1; - sd->vmax.flip = 1; - sd->vmax.AC50Hz = 1; - - sd->dev_camera_settings = mi1320_camera_settings; - sd->dev_init_at_startup = mi1320_init_at_startup; - sd->dev_configure_alt = mi1320_configure_alt; - sd->dev_init_pre_alt = mi1320_init_pre_alt; - sd->dev_post_unset_alt = mi1320_post_unset_alt; -} - -/*==========================================================================*/ - -static void common(struct gspca_dev *gspca_dev) -{ - s32 n; /* reserved for FETCH functions */ - - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01); - n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06); - keep_on_fetching_validx(gspca_dev, tbl_common, - ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10); - keep_on_fetching_validx(gspca_dev, tbl_common, - ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11); - keep_on_fetching_validx(gspca_dev, tbl_common, - ARRAY_SIZE(tbl_common), n); -} - -static int mi1320_init_at_startup(struct gspca_dev *gspca_dev) -{ - fetch_validx(gspca_dev, tbl_init_at_startup, - ARRAY_SIZE(tbl_init_at_startup)); - - common(gspca_dev); - -/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ - - return 0; -} - -static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->mirrorMask = 0; - - sd->vold.backlight = -1; - sd->vold.brightness = -1; - sd->vold.sharpness = -1; - sd->vold.contrast = -1; - sd->vold.saturation = -1; - sd->vold.gamma = -1; - sd->vold.hue = -1; - sd->vold.whitebal = -1; - sd->vold.mirror = -1; - sd->vold.flip = -1; - sd->vold.AC50Hz = -1; - - common(gspca_dev); - - mi1320_sensor_settings(gspca_dev); - - mi1320_init_post_alt(gspca_dev); - - return 0; -} - -static int mi1320_init_post_alt(struct gspca_dev *gspca_dev) -{ - mi1320_camera_settings(gspca_dev); - - return 0; -} - -static int mi1320_sensor_settings(struct gspca_dev *gspca_dev) -{ - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - - fetch_validx(gspca_dev, tbl_sensor_settings_common, - ARRAY_SIZE(tbl_sensor_settings_common)); - - switch (reso) { - case IMAGE_1280: - fetch_validx(gspca_dev, tbl_sensor_settings_1280, - ARRAY_SIZE(tbl_sensor_settings_1280)); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_1280[0]); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_1280[1]); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_1280[2]); - break; - - case IMAGE_800: - fetch_validx(gspca_dev, tbl_sensor_settings_800, - ARRAY_SIZE(tbl_sensor_settings_800)); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_800[0]); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_800[1]); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_800[2]); - break; - - default: - fetch_validx(gspca_dev, tbl_sensor_settings_640, - ARRAY_SIZE(tbl_sensor_settings_640)); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 60, tbl_640[0]); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_640[1]); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_640[2]); - break; - } - return 0; -} - -static int mi1320_configure_alt(struct gspca_dev *gspca_dev) -{ - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - switch (reso) { - case IMAGE_640: - gspca_dev->alt = 3 + 1; - break; - - case IMAGE_800: - case IMAGE_1280: - gspca_dev->alt = 1 + 1; - break; - } - return 0; -} - -static int mi1320_camera_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - s32 backlight = sd->vcur.backlight; - s32 bright = sd->vcur.brightness; - s32 sharp = sd->vcur.sharpness; - s32 cntr = sd->vcur.contrast; - s32 gam = sd->vcur.gamma; - s32 hue = sd->vcur.hue; - s32 sat = sd->vcur.saturation; - s32 wbal = sd->vcur.whitebal; - s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); - s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); - s32 freq = (sd->vcur.AC50Hz > 0); - s32 i; - - if (freq != sd->vold.AC50Hz) { - sd->vold.AC50Hz = freq; - - freq = 2 * (freq == 0); - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba02, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x005b, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01 + freq, 0x00f1, 0, NULL); - } - - if (wbal != sd->vold.whitebal) { - sd->vold.whitebal = wbal; - if (wbal < 0 || wbal > sd->vmax.whitebal) - wbal = 0; - - for (i = 0; i < 2; i++) { - if (wbal == 0) { /* Normal light */ - ctrl_out(gspca_dev, 0x40, 1, - 0x0010, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, - 0x0003, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, - 0x0042, 0x00c2, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, - 0xba00, 0x0200, 48, dat_wbalNL); - } - - if (wbal == 1) { /* Low light */ - ctrl_out(gspca_dev, 0x40, 1, - 0x0010, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, - 0x0004, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, - 0x0043, 0x00c2, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, - 0xba00, 0x0200, 48, dat_wbalLL); - } - - if (wbal == 2) { /* Back light */ - ctrl_out(gspca_dev, 0x40, 1, - 0x0010, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, - 0x0003, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, - 0x0042, 0x00c2, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, - 0xba00, 0x0200, 44, dat_wbalBL); - } - } - } - - if (bright != sd->vold.brightness) { - sd->vold.brightness = bright; - if (bright < 0 || bright > sd->vmax.brightness) - bright = 0; - - bright = tbl_bright[bright]; - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x0034, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x00f1, 0, NULL); - } - - if (sat != sd->vold.saturation) { - sd->vold.saturation = sat; - if (sat < 0 || sat > sd->vmax.saturation) - sat = 0; - - sat = tbl_sat[sat]; - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0025, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sat, 0x00f1, 0, NULL); - } - - if (sharp != sd->vold.sharpness) { - sd->vold.sharpness = sharp; - if (sharp < 0 || sharp > sd->vmax.sharpness) - sharp = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0005, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sharp, 0x00f1, 0, NULL); - } - - if (hue != sd->vold.hue) { - /* 0=normal 1=NB 2="sepia" 3=negative 4=other 5=other2 */ - if (hue < 0 || hue > sd->vmax.hue) - hue = 0; - if (hue == sd->vmax.hue) - sd->swapRB = 1; - else - sd->swapRB = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, - 0, NULL); - } - - if (backlight != sd->vold.backlight) { - sd->vold.backlight = backlight; - if (backlight < 0 || backlight > sd->vmax.backlight) - backlight = 0; - - backlight = tbl_backlight[backlight]; - for (i = 0; i < 2; i++) { - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba74, 0x0006, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba80 + backlight, 0x00f1, - 0, NULL); - } - } - - if (hue != sd->vold.hue) { - sd->vold.hue = hue; - - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, - 0, NULL); - } - - if (mirror != sd->vold.mirror || flip != sd->vold.flip) { - u8 dat_hvflip2[4] = {0x20, 0x01, 0xf1, 0x00}; - sd->vold.mirror = mirror; - sd->vold.flip = flip; - - dat_hvflip2[3] = flip + 2 * mirror; - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip1); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip2); - } - - if (gam != sd->vold.gamma) { - sd->vold.gamma = gam; - if (gam < 0 || gam > sd->vmax.gamma) - gam = 0; - - gam = 2 * gam; - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba04 , 0x003b, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba02 + gam, 0x00f1, 0, NULL); - } - - if (cntr != sd->vold.contrast) { - sd->vold.contrast = cntr; - if (cntr < 0 || cntr > sd->vmax.contrast) - cntr = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr1[cntr], 0x0035, - 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr2[cntr], 0x00f1, - 0, NULL); - } - - return 0; -} - -static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev) -{ - ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); - - fetch_validx(gspca_dev, tbl_post_unset_alt, - ARRAY_SIZE(tbl_post_unset_alt)); -} diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c deleted file mode 100644 index 2edda6b7d653..000000000000 --- a/drivers/media/video/gspca/gl860/gl860-mi2020.c +++ /dev/null @@ -1,733 +0,0 @@ -/* Subdriver for the GL860 chip with the MI2020 sensor - * Author Olivier LORIN, from logs by Iceman/Soro2005 + Fret_saw/Hulkie/Tricid - * with the help of Kytrix/BUGabundo/Blazercist. - * Driver achieved thanks to a webcam gift by Kytrix. - * - * This program is free software; 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 - * 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 . - */ - -/* Sensor : MI2020 */ - -#include "gl860.h" - -static u8 dat_wbal1[] = {0x8c, 0xa2, 0x0c}; - -static u8 dat_bright1[] = {0x8c, 0xa2, 0x06}; -static u8 dat_bright3[] = {0x8c, 0xa1, 0x02}; -static u8 dat_bright4[] = {0x90, 0x00, 0x0f}; -static u8 dat_bright5[] = {0x8c, 0xa1, 0x03}; -static u8 dat_bright6[] = {0x90, 0x00, 0x05}; - -static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19}; -static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b}; -static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03}; -static u8 dat_hvflip6[] = {0x90, 0x00, 0x06}; - -static struct idxdata tbl_middle_hvflip_low[] = { - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, -}; - -static struct idxdata tbl_middle_hvflip_big[] = { - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {102, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, -}; - -static struct idxdata tbl_end_hvflip[] = { - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {6, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {6, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {6, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, -}; - -static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; - -static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; -static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; - -static struct validx tbl_init_at_startup[] = { - {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, - {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, - {53, 0xffff}, - {0x0040, 0x0000}, {0x0063, 0x0006}, -}; - -static struct validx tbl_common_0B[] = { - {0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, - {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, - {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, -}; - -static struct idxdata tbl_common_3B[] = { - {0x33, "\x86\x25\x01"}, {0x33, "\x86\x25\x00"}, - {2, "\xff\xff\xff"}, - {0x30, "\x1a\x0a\xcc"}, {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, - {6, "\xff\xff\xff"}, /* 12 */ - {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, - {2, "\xff\xff\xff"}, /* - */ - {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"}, - {0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"}, - {0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"}, - {0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"}, - {0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"}, - {0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"}, - {0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"}, - {0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, - {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, - {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, - {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, - {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, - {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, - {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, - {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, - {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, - {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, - {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, - {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, - {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, - {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, - {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, - {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, - {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, - {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, - {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, - {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, - {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, - {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, - {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, - {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, - {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, - {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, - {0x33, "\x78\x00\x00"}, - {2, "\xff\xff\xff"}, - {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"}, - {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"}, - {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"}, - {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"}, - {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, {0x33, "\x8c\xa4\x04"}, - {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, - {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, {0x33, "\x90\x00\x04"}, - {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, - {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x25"}, - {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, - {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x47"}, - {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x02\x84"}, - {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, {0x33, "\x8c\x27\x07"}, - {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, {0x33, "\x90\x04\xb0"}, - {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x0f"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, {0x33, "\x90\x04\xbd"}, - {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, {0x33, "\x8c\x27\x15"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, - {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, {0x33, "\x8c\x27\x1b"}, - {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, {0x33, "\x90\x01\x02"}, - {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, {0x33, "\x8c\x27\x21"}, - {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, {0x33, "\x90\x02\x85"}, - {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x27"}, - {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, {0x33, "\x90\x20\x20"}, - {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, {0x33, "\x8c\x27\x2d"}, - {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, {0x33, "\x90\x00\x04"}, - {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x33"}, - {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, {0x33, "\x90\x06\x4b"}, - {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x39"}, - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, - {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x41"}, - {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, {0x33, "\x90\x04\xed"}, - {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x51"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, {0x33, "\x90\x03\x20"}, - {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x57"}, - {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x63"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, {0x33, "\x90\x04\xb0"}, - {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\xa4\x08"}, - {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, - {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, - {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa1"}, - {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, {0x33, "\x8c\x24\x15"}, - {0x33, "\x90\x00\x6a"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\x80"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {3, "\xff\xff\xff"}, -}; - -static struct idxdata tbl_init_post_alt_low1[] = { - {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, - {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, - {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, - {0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"}, - {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"}, - {0x33, "\x90\x00\x9b"}, -}; - -static struct idxdata tbl_init_post_alt_low2[] = { - {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, - {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, -}; - -static struct idxdata tbl_init_post_alt_low3[] = { - {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, - {2, "\xff\xff\xff"}, - {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, - {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, -}; - -static struct idxdata tbl_init_post_alt_big[] = { - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, - {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, - {2, "\xff\xff\xff"}, - {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"}, - {0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"}, - {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"}, - {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, - {0x33, "\x8c\x27\x97"}, {0x33, "\x90\x01\x00"}, - {51, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {51, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, - {51, "\xff\xff\xff"}, -}; - -static struct idxdata tbl_init_post_alt_3B[] = { - {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, - {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, - {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, - {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, - {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, - {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, - {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, - {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, - {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, - {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, - {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, - {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, - {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, - {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, - {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, - {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, - {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, - {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, - {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, - {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, - {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, - {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, - {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, - {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, - {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, - {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, -}; - -static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81"; -static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21"; -static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01"; -static u8 *dat_1600 = "\xd0\x02\xd1\x20\xd2\xaf\xd3\x02\xd4\x30\xd5\x41"; - -static int mi2020_init_at_startup(struct gspca_dev *gspca_dev); -static int mi2020_configure_alt(struct gspca_dev *gspca_dev); -static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev); -static int mi2020_init_post_alt(struct gspca_dev *gspca_dev); -static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev); -static int mi2020_camera_settings(struct gspca_dev *gspca_dev); -/*==========================================================================*/ - -void mi2020_init_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vcur.backlight = 0; - sd->vcur.brightness = 70; - sd->vcur.sharpness = 20; - sd->vcur.contrast = 0; - sd->vcur.gamma = 0; - sd->vcur.hue = 0; - sd->vcur.saturation = 60; - sd->vcur.whitebal = 0; /* 50, not done by hardware */ - sd->vcur.mirror = 0; - sd->vcur.flip = 0; - sd->vcur.AC50Hz = 1; - - sd->vmax.backlight = 64; - sd->vmax.brightness = 128; - sd->vmax.sharpness = 40; - sd->vmax.contrast = 3; - sd->vmax.gamma = 2; - sd->vmax.hue = 0 + 1; /* 200, not done by hardware */ - sd->vmax.saturation = 0; /* 100, not done by hardware */ - sd->vmax.whitebal = 2; /* 100, not done by hardware */ - sd->vmax.mirror = 1; - sd->vmax.flip = 1; - sd->vmax.AC50Hz = 1; - - sd->dev_camera_settings = mi2020_camera_settings; - sd->dev_init_at_startup = mi2020_init_at_startup; - sd->dev_configure_alt = mi2020_configure_alt; - sd->dev_init_pre_alt = mi2020_init_pre_alt; - sd->dev_post_unset_alt = mi2020_post_unset_alt; -} - -/*==========================================================================*/ - -static void common(struct gspca_dev *gspca_dev) -{ - fetch_validx(gspca_dev, tbl_common_0B, ARRAY_SIZE(tbl_common_0B)); - fetch_idxdata(gspca_dev, tbl_common_3B, ARRAY_SIZE(tbl_common_3B)); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); -} - -static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) -{ - u8 c; - - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); - - fetch_validx(gspca_dev, tbl_init_at_startup, - ARRAY_SIZE(tbl_init_at_startup)); - - ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); - ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &c); - - common(gspca_dev); - - msleep(61); -/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ -/* msleep(36); */ - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); - - return 0; -} - -static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->mirrorMask = 0; - sd->vold.hue = -1; - - /* These controls need to be reset */ - sd->vold.brightness = -1; - sd->vold.sharpness = -1; - - /* If not different from default, they do not need to be set */ - sd->vold.contrast = 0; - sd->vold.gamma = 0; - sd->vold.backlight = 0; - - mi2020_init_post_alt(gspca_dev); - - return 0; -} - -static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); - s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); - s32 freq = (sd->vcur.AC50Hz > 0); - s32 wbal = sd->vcur.whitebal; - - u8 dat_freq2[] = {0x90, 0x00, 0x80}; - u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; - u8 dat_multi2[] = {0x90, 0x00, 0x00}; - u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; - u8 dat_multi4[] = {0x90, 0x00, 0x00}; - u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; - u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; - u8 dat_wbal2[] = {0x90, 0x00, 0x00}; - u8 c; - - sd->nbIm = -1; - - dat_freq2[2] = freq ? 0xc0 : 0x80; - dat_multi1[2] = 0x9d; - dat_multi3[2] = dat_multi1[2] + 1; - if (wbal == 0) { - dat_multi4[2] = dat_multi2[2] = 0; - dat_wbal2[2] = 0x17; - } else if (wbal == 1) { - dat_multi4[2] = dat_multi2[2] = 0; - dat_wbal2[2] = 0x35; - } else if (wbal == 2) { - dat_multi4[2] = dat_multi2[2] = 0x20; - dat_wbal2[2] = 0x17; - } - dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); - dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); - - msleep(200); - ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - msleep(2); - - common(gspca_dev); - - msleep(142); - ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); - - switch (reso) { - case IMAGE_640: - case IMAGE_800: - if (reso != IMAGE_800) - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - 12, dat_640); - else - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - 12, dat_800); - - fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, - ARRAY_SIZE(tbl_init_post_alt_low1)); - - if (reso == IMAGE_800) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low2, - ARRAY_SIZE(tbl_init_post_alt_low2)); - - fetch_idxdata(gspca_dev, tbl_init_post_alt_low3, - ARRAY_SIZE(tbl_init_post_alt_low3)); - - ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(120); - break; - - case IMAGE_1280: - case IMAGE_1600: - if (reso == IMAGE_1280) { - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - 12, dat_1280); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x8c\x27\x07"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x90\x05\x04"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x8c\x27\x09"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x90\x04\x02"); - } else { - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - 12, dat_1600); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x8c\x27\x07"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x90\x06\x40"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x8c\x27\x09"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, "\x90\x04\xb0"); - } - - fetch_idxdata(gspca_dev, tbl_init_post_alt_big, - ARRAY_SIZE(tbl_init_post_alt_big)); - - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(1850); - } - - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(40); - - /* AC power frequency */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); - msleep(33); - /* light source */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - msleep(7); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); - - fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, - ARRAY_SIZE(tbl_init_post_alt_3B)); - - /* hvflip */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); - msleep(250); - - if (reso == IMAGE_640 || reso == IMAGE_800) - fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, - ARRAY_SIZE(tbl_middle_hvflip_low)); - else - fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, - ARRAY_SIZE(tbl_middle_hvflip_big)); - - fetch_idxdata(gspca_dev, tbl_end_hvflip, - ARRAY_SIZE(tbl_end_hvflip)); - - sd->nbIm = 0; - - sd->vold.mirror = mirror; - sd->vold.flip = flip; - sd->vold.AC50Hz = freq; - sd->vold.whitebal = wbal; - - mi2020_camera_settings(gspca_dev); - - return 0; -} - -static int mi2020_configure_alt(struct gspca_dev *gspca_dev) -{ - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - switch (reso) { - case IMAGE_640: - gspca_dev->alt = 3 + 1; - break; - - case IMAGE_800: - case IMAGE_1280: - case IMAGE_1600: - gspca_dev->alt = 1 + 1; - break; - } - return 0; -} - -static int mi2020_camera_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - s32 backlight = sd->vcur.backlight; - s32 bright = sd->vcur.brightness; - s32 sharp = sd->vcur.sharpness; - s32 cntr = sd->vcur.contrast; - s32 gam = sd->vcur.gamma; - s32 hue = (sd->vcur.hue > 0); - s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); - s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); - s32 freq = (sd->vcur.AC50Hz > 0); - s32 wbal = sd->vcur.whitebal; - - u8 dat_sharp[] = {0x6c, 0x00, 0x08}; - u8 dat_bright2[] = {0x90, 0x00, 0x00}; - u8 dat_freq2[] = {0x90, 0x00, 0x80}; - u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; - u8 dat_multi2[] = {0x90, 0x00, 0x00}; - u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; - u8 dat_multi4[] = {0x90, 0x00, 0x00}; - u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; - u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; - u8 dat_wbal2[] = {0x90, 0x00, 0x00}; - - /* Less than 4 images received -> too early to set the settings */ - if (sd->nbIm < 4) { - sd->waitSet = 1; - return 0; - } - sd->waitSet = 0; - - if (freq != sd->vold.AC50Hz) { - sd->vold.AC50Hz = freq; - - dat_freq2[2] = freq ? 0xc0 : 0x80; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); - msleep(20); - } - - if (wbal != sd->vold.whitebal) { - sd->vold.whitebal = wbal; - if (wbal < 0 || wbal > sd->vmax.whitebal) - wbal = 0; - - dat_multi1[2] = 0x9d; - dat_multi3[2] = dat_multi1[2] + 1; - if (wbal == 0) { - dat_multi4[2] = dat_multi2[2] = 0; - dat_wbal2[2] = 0x17; - } else if (wbal == 1) { - dat_multi4[2] = dat_multi2[2] = 0; - dat_wbal2[2] = 0x35; - } else if (wbal == 2) { - dat_multi4[2] = dat_multi2[2] = 0x20; - dat_wbal2[2] = 0x17; - } - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - } - - if (mirror != sd->vold.mirror || flip != sd->vold.flip) { - sd->vold.mirror = mirror; - sd->vold.flip = flip; - - dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); - dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); - - fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, - ARRAY_SIZE(tbl_init_post_alt_3B)); - - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); - msleep(40); - - if (reso == IMAGE_640 || reso == IMAGE_800) - fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, - ARRAY_SIZE(tbl_middle_hvflip_low)); - else - fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, - ARRAY_SIZE(tbl_middle_hvflip_big)); - - fetch_idxdata(gspca_dev, tbl_end_hvflip, - ARRAY_SIZE(tbl_end_hvflip)); - } - - if (bright != sd->vold.brightness) { - sd->vold.brightness = bright; - if (bright < 0 || bright > sd->vmax.brightness) - bright = 0; - - dat_bright2[2] = bright; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); - } - - if (cntr != sd->vold.contrast || gam != sd->vold.gamma) { - sd->vold.contrast = cntr; - if (cntr < 0 || cntr > sd->vmax.contrast) - cntr = 0; - sd->vold.gamma = gam; - if (gam < 0 || gam > sd->vmax.gamma) - gam = 0; - - dat_multi1[2] = 0x6d; - dat_multi3[2] = dat_multi1[2] + 1; - if (cntr == 0) - cntr = 4; - dat_multi4[2] = dat_multi2[2] = cntr * 0x10 + 2 - gam; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - } - - if (backlight != sd->vold.backlight) { - sd->vold.backlight = backlight; - if (backlight < 0 || backlight > sd->vmax.backlight) - backlight = 0; - - dat_multi1[2] = 0x9d; - dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = backlight; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - } - - if (sharp != sd->vold.sharpness) { - sd->vold.sharpness = sharp; - if (sharp < 0 || sharp > sd->vmax.sharpness) - sharp = 0; - - dat_sharp[1] = sharp; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0032, 3, dat_sharp); - } - - if (hue != sd->vold.hue) { - sd->swapRB = hue; - sd->vold.hue = hue; - } - - return 0; -} - -static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev) -{ - ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); - msleep(40); - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); -} diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c deleted file mode 100644 index 768cac5cd72b..000000000000 --- a/drivers/media/video/gspca/gl860/gl860-ov2640.c +++ /dev/null @@ -1,489 +0,0 @@ -/* Subdriver for the GL860 chip with the OV2640 sensor - * Author Olivier LORIN, from Malmostoso's logs - * - * This program is free software; 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 - * 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 . - */ - -/* Sensor : OV2640 */ - -#include "gl860.h" - -static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01"; - -static u8 c61[] = {0x61}; /* expected */ -static u8 c51[] = {0x51}; /* expected */ -static u8 c50[] = {0x50}; /* expected */ -static u8 c28[] = {0x28}; /* expected */ -static u8 ca8[] = {0xa8}; /* expected */ - -static u8 dat_post[] = - "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01"; - -static u8 dat_640[] = "\xd0\x01\xd1\x08\xd2\xe0\xd3\x02\xd4\x10\xd5\x81"; -static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21"; -static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01"; -static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41"; - -static struct validx tbl_init_at_startup[] = { - {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, - {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, - {0x0050, 0x0000}, {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0061, 0x0006}, - {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, - {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, - {0x0041, 0x0000}, {0x0061, 0x0000}, -}; - -static struct validx tbl_common[] = { - {0x6000, 0x00ff}, {0x60ff, 0x002c}, {0x60df, 0x002e}, {0x6001, 0x00ff}, - {0x6080, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, - {0x6035, 0x003c}, {0x6000, 0x0011}, {0x6028, 0x0004}, {0x60e5, 0x0013}, - {0x6088, 0x0014}, {0x600c, 0x002c}, {0x6078, 0x0033}, {0x60f7, 0x003b}, - {0x6000, 0x003e}, {0x6011, 0x0043}, {0x6010, 0x0016}, {0x6082, 0x0039}, - {0x6088, 0x0035}, {0x600a, 0x0022}, {0x6040, 0x0037}, {0x6000, 0x0023}, - {0x60a0, 0x0034}, {0x601a, 0x0036}, {0x6002, 0x0006}, {0x60c0, 0x0007}, - {0x60b7, 0x000d}, {0x6001, 0x000e}, {0x6000, 0x004c}, {0x6081, 0x004a}, - {0x6099, 0x0021}, {0x6002, 0x0009}, {0x603e, 0x0024}, {0x6034, 0x0025}, - {0x6081, 0x0026}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, - {0x6000, 0x005c}, {0x6000, 0x0063}, {0x6000, 0x007c}, {0x6070, 0x0061}, - {0x6080, 0x0062}, {0x6080, 0x0020}, {0x6030, 0x0028}, {0x6000, 0x006c}, - {0x6000, 0x006e}, {0x6002, 0x0070}, {0x6094, 0x0071}, {0x60c1, 0x0073}, - {0x6034, 0x003d}, {0x6057, 0x005a}, {0x60bb, 0x004f}, {0x609c, 0x0050}, - {0x6080, 0x006d}, {0x6002, 0x0039}, {0x6033, 0x003a}, {0x60f1, 0x003b}, - {0x6031, 0x003c}, {0x6000, 0x00ff}, {0x6014, 0x00e0}, {0x60ff, 0x0076}, - {0x60a0, 0x0033}, {0x6020, 0x0042}, {0x6018, 0x0043}, {0x6000, 0x004c}, - {0x60d0, 0x0087}, {0x600f, 0x0088}, {0x6003, 0x00d7}, {0x6010, 0x00d9}, - {0x6005, 0x00da}, {0x6082, 0x00d3}, {0x60c0, 0x00f9}, {0x6006, 0x0044}, - {0x6007, 0x00d1}, {0x6002, 0x00d2}, {0x6000, 0x00d2}, {0x6011, 0x00d8}, - {0x6008, 0x00c8}, {0x6080, 0x00c9}, {0x6008, 0x007c}, {0x6020, 0x007d}, - {0x6020, 0x007d}, {0x6000, 0x0090}, {0x600e, 0x0091}, {0x601a, 0x0091}, - {0x6031, 0x0091}, {0x605a, 0x0091}, {0x6069, 0x0091}, {0x6075, 0x0091}, - {0x607e, 0x0091}, {0x6088, 0x0091}, {0x608f, 0x0091}, {0x6096, 0x0091}, - {0x60a3, 0x0091}, {0x60af, 0x0091}, {0x60c4, 0x0091}, {0x60d7, 0x0091}, - {0x60e8, 0x0091}, {0x6020, 0x0091}, {0x6000, 0x0092}, {0x6006, 0x0093}, - {0x60e3, 0x0093}, {0x6005, 0x0093}, {0x6005, 0x0093}, {0x6000, 0x0093}, - {0x6004, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, - {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, - {0x6000, 0x0096}, {0x6008, 0x0097}, {0x6019, 0x0097}, {0x6002, 0x0097}, - {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, {0x6028, 0x0097}, - {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6098, 0x0097}, {0x6080, 0x0097}, - {0x6000, 0x0097}, {0x6000, 0x0097}, {0x60ed, 0x00c3}, {0x609a, 0x00c4}, - {0x6000, 0x00a4}, {0x6011, 0x00c5}, {0x6051, 0x00c6}, {0x6010, 0x00c7}, - {0x6066, 0x00b6}, {0x60a5, 0x00b8}, {0x6064, 0x00b7}, {0x607c, 0x00b9}, - {0x60af, 0x00b3}, {0x6097, 0x00b4}, {0x60ff, 0x00b5}, {0x60c5, 0x00b0}, - {0x6094, 0x00b1}, {0x600f, 0x00b2}, {0x605c, 0x00c4}, {0x6000, 0x00a8}, - {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x601d, 0x0086}, {0x6000, 0x0050}, - {0x6090, 0x0051}, {0x6018, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054}, - {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6090, 0x005a}, {0x6018, 0x005b}, - {0x6005, 0x005c}, {0x60ed, 0x00c3}, {0x6000, 0x007f}, {0x6005, 0x00da}, - {0x601f, 0x00e5}, {0x6067, 0x00e1}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, - {0x6000, 0x0005}, {0x6001, 0x00ff}, {0x6000, 0x0000}, {0x6000, 0x0045}, - {0x6000, 0x0010}, -}; - -static struct validx tbl_sensor_settings_common1[] = { - {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, - {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, - {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000}, - {50, 0xffff}, - {0x0061, 0x0000}, - {0xffff, 0xffff}, - {0x6000, 0x00ff}, {0x6000, 0x007c}, {0x6007, 0x007d}, - {30, 0xffff}, - {0x0040, 0x0000}, -}; - -static struct validx tbl_sensor_settings_common2[] = { - {0x6001, 0x00ff}, {0x6038, 0x000c}, - {10, 0xffff}, - {0x6000, 0x0011}, -}; - -static struct validx tbl_640[] = { - {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, - {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, - {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, - {0x6075, 0x0018}, {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, - {0x60bb, 0x004f}, {0x6057, 0x005a}, {0x609c, 0x0050}, {0x6080, 0x006d}, - {0x6092, 0x0026}, {0x60ff, 0x0020}, {0x6000, 0x0027}, {0x6000, 0x00ff}, - {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, {0x603d, 0x0086}, - {0x6089, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053}, - {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x60a0, 0x005a}, - {0x6078, 0x005b}, {0x6000, 0x005c}, {0x6004, 0x00d3}, {0x6000, 0x00e0}, - {0x60ff, 0x00dd}, {0x60a1, 0x005a}, -}; - -static struct validx tbl_800[] = { - {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, - {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, - {0x6001, 0x00ff}, {0x6040, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, - {0x6043, 0x0018}, {0x6000, 0x0019}, {0x604b, 0x001a}, {0x6009, 0x0032}, - {0x60ca, 0x004f}, {0x60a8, 0x0050}, {0x6000, 0x006d}, {0x6038, 0x003d}, - {0x60c8, 0x0035}, {0x6000, 0x0022}, {0x6092, 0x0026}, {0x60ff, 0x0020}, - {0x6000, 0x0027}, {0x6000, 0x00ff}, {0x6064, 0x00c0}, {0x604b, 0x00c1}, - {0x6000, 0x008c}, {0x601d, 0x0086}, {0x6082, 0x00d3}, {0x6000, 0x00e0}, - {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018}, -}; - -static struct validx tbl_big1[] = { - {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, - {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, - {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018}, - {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, {0x60bb, 0x004f}, - {0x609c, 0x0050}, {0x6057, 0x005a}, {0x6080, 0x006d}, {0x6043, 0x000f}, - {0x608f, 0x0003}, {0x6005, 0x007c}, {0x6081, 0x0026}, {0x6000, 0x00ff}, - {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, -}; - -static struct validx tbl_big2[] = { - {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, - {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, - {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3}, - {0x6000, 0x008e}, -}; - -static struct validx tbl_big3[] = { - {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd}, - {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, - {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7}, - {0x6000, 0x0092}, {0x6006, 0x0093}, {0x60e3, 0x0093}, {0x6005, 0x0093}, - {0x6005, 0x0093}, {0x60ed, 0x00c3}, {0x6000, 0x00a4}, {0x60d0, 0x0087}, - {0x6003, 0x0096}, {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, - {0x6028, 0x0097}, {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6001, 0x00ff}, - {0x6043, 0x000f}, {0x608f, 0x0003}, {0x6000, 0x002d}, {0x6000, 0x002e}, - {0x600a, 0x0022}, {0x6002, 0x0070}, {0x6008, 0x0014}, {0x6048, 0x0014}, - {0x6000, 0x00ff}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, -}; - -static struct validx tbl_post_unset_alt[] = { - {0x006a, 0x000d}, {0x6001, 0x00ff}, {0x6081, 0x0026}, {0x6000, 0x0000}, - {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6068, 0x000d}, - {50, 0xffff}, - {0x0021, 0x0000}, -}; - -static int ov2640_init_at_startup(struct gspca_dev *gspca_dev); -static int ov2640_configure_alt(struct gspca_dev *gspca_dev); -static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev); -static int ov2640_init_post_alt(struct gspca_dev *gspca_dev); -static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev); -static int ov2640_camera_settings(struct gspca_dev *gspca_dev); -/*==========================================================================*/ - -void ov2640_init_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vcur.backlight = 32; - sd->vcur.brightness = 0; - sd->vcur.sharpness = 6; - sd->vcur.contrast = 0; - sd->vcur.gamma = 32; - sd->vcur.hue = 0; - sd->vcur.saturation = 128; - sd->vcur.whitebal = 64; - sd->vcur.mirror = 0; - sd->vcur.flip = 0; - - sd->vmax.backlight = 64; - sd->vmax.brightness = 255; - sd->vmax.sharpness = 31; - sd->vmax.contrast = 255; - sd->vmax.gamma = 64; - sd->vmax.hue = 254 + 2; - sd->vmax.saturation = 255; - sd->vmax.whitebal = 128; - sd->vmax.mirror = 1; - sd->vmax.flip = 1; - sd->vmax.AC50Hz = 0; - - sd->dev_camera_settings = ov2640_camera_settings; - sd->dev_init_at_startup = ov2640_init_at_startup; - sd->dev_configure_alt = ov2640_configure_alt; - sd->dev_init_pre_alt = ov2640_init_pre_alt; - sd->dev_post_unset_alt = ov2640_post_unset_alt; -} - -/*==========================================================================*/ - -static void common(struct gspca_dev *gspca_dev) -{ - fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); -} - -static int ov2640_init_at_startup(struct gspca_dev *gspca_dev) -{ - fetch_validx(gspca_dev, tbl_init_at_startup, - ARRAY_SIZE(tbl_init_at_startup)); - - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_init1); - - common(gspca_dev); - - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61); - - ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL); - - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51); - - ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL); -/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ - - return 0; -} - -static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->mirrorMask = 0; - - sd->vold.backlight = -1; - sd->vold.brightness = -1; - sd->vold.sharpness = -1; - sd->vold.contrast = -1; - sd->vold.saturation = -1; - sd->vold.gamma = -1; - sd->vold.hue = -1; - sd->vold.whitebal = -1; - sd->vold.mirror = -1; - sd->vold.flip = -1; - - ov2640_init_post_alt(gspca_dev); - - return 0; -} - -static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) -{ - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH functions */ - - ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - - n = fetch_validx(gspca_dev, tbl_sensor_settings_common1, - ARRAY_SIZE(tbl_sensor_settings_common1)); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post); - common(gspca_dev); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1, - ARRAY_SIZE(tbl_sensor_settings_common1), n); - - switch (reso) { - case IMAGE_640: - n = fetch_validx(gspca_dev, tbl_640, ARRAY_SIZE(tbl_640)); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_640); - break; - - case IMAGE_800: - n = fetch_validx(gspca_dev, tbl_800, ARRAY_SIZE(tbl_800)); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800); - break; - - case IMAGE_1600: - case IMAGE_1280: - n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1)); - - if (reso == IMAGE_1280) { - n = fetch_validx(gspca_dev, tbl_big2, - ARRAY_SIZE(tbl_big2)); - } else { - ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL); - } - - n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3)); - - if (reso == IMAGE_1280) { - ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - 12, dat_1280); - } else { - ctrl_out(gspca_dev, 0x40, 1, 0x6020, 0x008c, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6076, 0x0018, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - 12, dat_1600); - } - break; - } - - n = fetch_validx(gspca_dev, tbl_sensor_settings_common2, - ARRAY_SIZE(tbl_sensor_settings_common2)); - - ov2640_camera_settings(gspca_dev); - - return 0; -} - -static int ov2640_configure_alt(struct gspca_dev *gspca_dev) -{ - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - switch (reso) { - case IMAGE_640: - gspca_dev->alt = 3 + 1; - break; - - case IMAGE_800: - case IMAGE_1280: - case IMAGE_1600: - gspca_dev->alt = 1 + 1; - break; - } - return 0; -} - -static int ov2640_camera_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - s32 backlight = sd->vcur.backlight; - s32 bright = sd->vcur.brightness; - s32 sharp = sd->vcur.sharpness; - s32 gam = sd->vcur.gamma; - s32 cntr = sd->vcur.contrast; - s32 sat = sd->vcur.saturation; - s32 hue = sd->vcur.hue; - s32 wbal = sd->vcur.whitebal; - s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0); - s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0); - - if (backlight != sd->vold.backlight) { - /* No sd->vold.backlight=backlight; (to be done again later) */ - if (backlight < 0 || backlight > sd->vmax.backlight) - backlight = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, - 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, - 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, - 0, NULL); - } - - if (bright != sd->vold.brightness) { - sd->vold.brightness = bright; - if (bright < 0 || bright > sd->vmax.brightness) - bright = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6009 , 0x007c, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000 + bright, 0x007d, 0, NULL); - } - - if (wbal != sd->vold.whitebal) { - sd->vold.whitebal = wbal; - if (wbal < 0 || wbal > sd->vmax.whitebal) - wbal = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6003 , 0x007c, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000 + wbal, 0x007d, 0, NULL); - } - - if (cntr != sd->vold.contrast) { - sd->vold.contrast = cntr; - if (cntr < 0 || cntr > sd->vmax.contrast) - cntr = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6007 , 0x007c, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000 + cntr, 0x007d, 0, NULL); - } - - if (sat != sd->vold.saturation) { - sd->vold.saturation = sat; - if (sat < 0 || sat > sd->vmax.saturation) - sat = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x007c, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000 + sat, 0x007d, 0, NULL); - } - - if (sharp != sd->vold.sharpness) { - sd->vold.sharpness = sharp; - if (sharp < 0 || sharp > sd->vmax.sharpness) - sharp = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x0092, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x60c0 + sharp, 0x0093, 0, NULL); - } - - if (hue != sd->vold.hue) { - sd->vold.hue = hue; - if (hue < 0 || hue > sd->vmax.hue) - hue = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d, - 0, NULL); - if (hue >= 255) - sd->swapRB = 1; - else - sd->swapRB = 0; - } - - if (gam != sd->vold.gamma) { - sd->vold.gamma = gam; - if (gam < 0 || gam > sd->vmax.gamma) - gam = 0; - - ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6008 , 0x007c, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL); - } - - if (mirror != sd->vold.mirror || flip != sd->vold.flip) { - sd->vold.mirror = mirror; - sd->vold.flip = flip; - - mirror = 0x80 * mirror; - ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); - ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL); - - flip = 0x50 * flip + mirror; - ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); - ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL); - - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); - } - - if (backlight != sd->vold.backlight) { - sd->vold.backlight = backlight; - - ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, - 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, - 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, - 0, NULL); - } - - return 0; -} - -static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev) -{ - ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); - msleep(20); - fetch_validx(gspca_dev, tbl_post_unset_alt, - ARRAY_SIZE(tbl_post_unset_alt)); -} diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c deleted file mode 100644 index 5ae9619d72a5..000000000000 --- a/drivers/media/video/gspca/gl860/gl860-ov9655.c +++ /dev/null @@ -1,336 +0,0 @@ -/* Subdriver for the GL860 chip with the OV9655 sensor - * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt - * on dsd's weblog - * - * This program is free software; 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 - * 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 . - */ - -/* Sensor : OV9655 */ - -#include "gl860.h" - -static struct validx tbl_init_at_startup[] = { - {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, - {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, - - {0x0040, 0x0000}, -}; - -static struct validx tbl_commmon[] = { - {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, - {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, - {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000}, - {0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000}, -}; - -static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12}; - -static u8 *tbl_640[] = { - "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" - , - "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61" - "\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00" - "\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" - "\x29\x15\x2a\x00\x2b\x00\x2c\x08" - , - "\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00" - "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" - "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7" - "\x4d\xe7\x4e\xe7" - , - "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" - "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" - "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04" - "\x6d\x55\x6e\x00\x6f\x9d" - , - "\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02" - "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" - "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" - "\x8a\x23\x8c\x8d\x90\x7c\x91\x7b" - , - "\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70" - "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" - "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" - , - "\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01" - "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" - , - "\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80" -}; - -static u8 *tbl_1280[] = { - "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" - , - "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61" - "\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb" - "\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" - "\x29\x15\x2a\x00\x2b\x00\x2c\x08" - , - "\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00" - "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" - "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8" - "\x4d\xe8\x4e\xe8" - , - "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" - "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" - "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04" - "\x6d\x55\x6e\x00\x6f\x9d" - , - "\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02" - "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" - "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" - "\x8a\x23\x8c\x0d\x90\x90\x91\x90" - , - "\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70" - "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" - "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" - , - "\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01" - "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" - , - "\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00" -}; - -static u8 c04[] = {0x04}; -static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; -static u8 dat_post2[] = "\x10\x10\xc1\x02"; -static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; -static u8 dat_post4[] = "\x10\x02\xc1\x06"; -static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; -static u8 dat_post6[] = "\x10\x10\xc1\x05"; -static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; -static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; - -static struct validx tbl_init_post_alt[] = { - {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff}, - {0x6003, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6001, 0x00ff}, - {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6012, 0x0003}, - {0xffff, 0xffff}, - {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6012, 0x0003}, - {0xffff, 0xffff}, - {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6000, 0x801e}, - {0xffff, 0xffff}, - {0x6004, 0x001e}, {0x6012, 0x0003}, -}; - -static int ov9655_init_at_startup(struct gspca_dev *gspca_dev); -static int ov9655_configure_alt(struct gspca_dev *gspca_dev); -static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev); -static int ov9655_init_post_alt(struct gspca_dev *gspca_dev); -static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev); -static int ov9655_camera_settings(struct gspca_dev *gspca_dev); -/*==========================================================================*/ - -void ov9655_init_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vcur.backlight = 0; - sd->vcur.brightness = 128; - sd->vcur.sharpness = 0; - sd->vcur.contrast = 0; - sd->vcur.gamma = 0; - sd->vcur.hue = 0; - sd->vcur.saturation = 0; - sd->vcur.whitebal = 0; - - sd->vmax.backlight = 0; - sd->vmax.brightness = 255; - sd->vmax.sharpness = 0; - sd->vmax.contrast = 0; - sd->vmax.gamma = 0; - sd->vmax.hue = 0 + 1; - sd->vmax.saturation = 0; - sd->vmax.whitebal = 0; - sd->vmax.mirror = 0; - sd->vmax.flip = 0; - sd->vmax.AC50Hz = 0; - - sd->dev_camera_settings = ov9655_camera_settings; - sd->dev_init_at_startup = ov9655_init_at_startup; - sd->dev_configure_alt = ov9655_configure_alt; - sd->dev_init_pre_alt = ov9655_init_pre_alt; - sd->dev_post_unset_alt = ov9655_post_unset_alt; -} - -/*==========================================================================*/ - -static int ov9655_init_at_startup(struct gspca_dev *gspca_dev) -{ - fetch_validx(gspca_dev, tbl_init_at_startup, - ARRAY_SIZE(tbl_init_at_startup)); - fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); -/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/ - - return 0; -} - -static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vold.brightness = -1; - sd->vold.hue = -1; - - fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); - - ov9655_init_post_alt(gspca_dev); - - return 0; -} - -static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) -{ - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH functions */ - s32 i; - u8 **tbl; - - ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - - tbl = (reso == IMAGE_640) ? tbl_640 : tbl_1280; - - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - tbl_length[0], tbl[0]); - for (i = 1; i < 7; i++) - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, - tbl_length[i], tbl[i]); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, - tbl_length[7], tbl[7]); - - n = fetch_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt)); - - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); - keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, - ARRAY_SIZE(tbl_init_post_alt), n); - - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); - - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3); - - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5); - - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7); - - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8); - - ov9655_camera_settings(gspca_dev); - - return 0; -} - -static int ov9655_configure_alt(struct gspca_dev *gspca_dev) -{ - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - switch (reso) { - case IMAGE_640: - gspca_dev->alt = 1 + 1; - break; - - default: - gspca_dev->alt = 1 + 1; - break; - } - return 0; -} - -static int ov9655_camera_settings(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70"; - - s32 bright = sd->vcur.brightness; - s32 hue = sd->vcur.hue; - - if (bright != sd->vold.brightness) { - sd->vold.brightness = bright; - if (bright < 0 || bright > sd->vmax.brightness) - bright = 0; - - dat_bright[3] = bright; - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_bright); - } - - if (hue != sd->vold.hue) { - sd->vold.hue = hue; - sd->swapRB = (hue != 0); - } - - return 0; -} - -static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev) -{ - ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0061, 0x0000, 0, NULL); -} diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c deleted file mode 100644 index ced3b71f14e5..000000000000 --- a/drivers/media/video/gspca/gl860/gl860.c +++ /dev/null @@ -1,725 +0,0 @@ -/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip - * Subdriver core - * - * 2009/09/24 Olivier Lorin - * GSPCA by Jean-Francois Moine - * Thanks BUGabundo and Malmostoso for your amazing help! - * - * This program is free software; 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 - * 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 . - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "gspca.h" -#include "gl860.h" - -MODULE_AUTHOR("Olivier Lorin "); -MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver"); -MODULE_LICENSE("GPL"); - -/*======================== static function declarations ====================*/ - -static void (*dev_init_settings)(struct gspca_dev *gspca_dev); - -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id); -static int sd_init(struct gspca_dev *gspca_dev); -static int sd_isoc_init(struct gspca_dev *gspca_dev); -static int sd_start(struct gspca_dev *gspca_dev); -static void sd_stop0(struct gspca_dev *gspca_dev); -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, int len); -static void sd_callback(struct gspca_dev *gspca_dev); - -static int gl860_guess_sensor(struct gspca_dev *gspca_dev, - u16 vendor_id, u16 product_id); - -/*============================ driver options ==============================*/ - -static s32 AC50Hz = 0xff; -module_param(AC50Hz, int, 0644); -MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); - -static char sensor[7]; -module_param_string(sensor, sensor, sizeof(sensor), 0644); -MODULE_PARM_DESC(sensor, - " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')"); - -/*============================ webcam controls =============================*/ - -/* Functions to get and set a control value */ -#define SD_SETGET(thename) \ -static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ -{\ - struct sd *sd = (struct sd *) gspca_dev;\ -\ - sd->vcur.thename = val;\ - if (gspca_dev->streaming)\ - sd->waitSet = 1;\ - return 0;\ -} \ -static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ -{\ - struct sd *sd = (struct sd *) gspca_dev;\ -\ - *val = sd->vcur.thename;\ - return 0;\ -} - -SD_SETGET(mirror) -SD_SETGET(flip) -SD_SETGET(AC50Hz) -SD_SETGET(backlight) -SD_SETGET(brightness) -SD_SETGET(gamma) -SD_SETGET(hue) -SD_SETGET(saturation) -SD_SETGET(sharpness) -SD_SETGET(whitebal) -SD_SETGET(contrast) - -#define GL860_NCTRLS 11 - -/* control table */ -static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; -static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; -static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; -static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; - -#define SET_MY_CTRL(theid, \ - thetype, thelabel, thename) \ - if (sd->vmax.thename != 0) {\ - sd_ctrls[nCtrls].qctrl.id = theid;\ - sd_ctrls[nCtrls].qctrl.type = thetype;\ - strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\ - sd_ctrls[nCtrls].qctrl.minimum = 0;\ - sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\ - sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\ - sd_ctrls[nCtrls].qctrl.step = \ - (sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\ - sd_ctrls[nCtrls].set = sd_set_##thename;\ - sd_ctrls[nCtrls].get = sd_get_##thename;\ - nCtrls++;\ - } - -static int gl860_build_control_table(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct ctrl *sd_ctrls; - int nCtrls = 0; - - if (_MI1320_) - sd_ctrls = sd_ctrls_mi1320; - else if (_MI2020_) - sd_ctrls = sd_ctrls_mi2020; - else if (_OV2640_) - sd_ctrls = sd_ctrls_ov2640; - else if (_OV9655_) - sd_ctrls = sd_ctrls_ov9655; - else - return 0; - - memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl)); - - SET_MY_CTRL(V4L2_CID_BRIGHTNESS, - V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness) - SET_MY_CTRL(V4L2_CID_SHARPNESS, - V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness) - SET_MY_CTRL(V4L2_CID_CONTRAST, - V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast) - SET_MY_CTRL(V4L2_CID_GAMMA, - V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma) - SET_MY_CTRL(V4L2_CID_HUE, - V4L2_CTRL_TYPE_INTEGER, "Palette", hue) - SET_MY_CTRL(V4L2_CID_SATURATION, - V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation) - SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE, - V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal) - SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION, - V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight) - - SET_MY_CTRL(V4L2_CID_HFLIP, - V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror) - SET_MY_CTRL(V4L2_CID_VFLIP, - V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) - SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz) - - return nCtrls; -} - -/*==================== sud-driver structure initialisation =================*/ - -static const struct sd_desc sd_desc_mi1320 = { - .name = MODULE_NAME, - .ctrls = sd_ctrls_mi1320, - .nctrls = GL860_NCTRLS, - .config = sd_config, - .init = sd_init, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_callback, -}; - -static const struct sd_desc sd_desc_mi2020 = { - .name = MODULE_NAME, - .ctrls = sd_ctrls_mi2020, - .nctrls = GL860_NCTRLS, - .config = sd_config, - .init = sd_init, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_callback, -}; - -static const struct sd_desc sd_desc_ov2640 = { - .name = MODULE_NAME, - .ctrls = sd_ctrls_ov2640, - .nctrls = GL860_NCTRLS, - .config = sd_config, - .init = sd_init, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_callback, -}; - -static const struct sd_desc sd_desc_ov9655 = { - .name = MODULE_NAME, - .ctrls = sd_ctrls_ov9655, - .nctrls = GL860_NCTRLS, - .config = sd_config, - .init = sd_init, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_callback, -}; - -/*=========================== sub-driver image sizes =======================*/ - -static struct v4l2_pix_format mi2020_mode[] = { - { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, - { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 800, - .sizeimage = 800 * 598, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - }, - {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 1024, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2 - }, - {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 1600, - .sizeimage = 1600 * 1198, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3 - }, -}; - -static struct v4l2_pix_format ov2640_mode[] = { - { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, - { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 800, - .sizeimage = 800 * 600, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - }, - {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 960, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2 - }, - {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 1600, - .sizeimage = 1600 * 1200, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3 - }, -}; - -static struct v4l2_pix_format mi1320_mode[] = { - { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, - { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 800, - .sizeimage = 800 * 600, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - }, - {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 960, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2 - }, -}; - -static struct v4l2_pix_format ov9655_mode[] = { - { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, - {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 960, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - }, -}; - -/*========================= sud-driver functions ===========================*/ - -/* This function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - u16 vendor_id, product_id; - - /* Get USB VendorID and ProductID */ - vendor_id = id->idVendor; - product_id = id->idProduct; - - sd->nbRightUp = 1; - sd->nbIm = -1; - - sd->sensor = 0xff; - if (strcmp(sensor, "MI1320") == 0) - sd->sensor = ID_MI1320; - else if (strcmp(sensor, "OV2640") == 0) - sd->sensor = ID_OV2640; - else if (strcmp(sensor, "OV9655") == 0) - sd->sensor = ID_OV9655; - else if (strcmp(sensor, "MI2020") == 0) - sd->sensor = ID_MI2020; - - /* Get sensor and set the suitable init/start/../stop functions */ - if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) - return -1; - - cam = &gspca_dev->cam; - - switch (sd->sensor) { - case ID_MI1320: - gspca_dev->sd_desc = &sd_desc_mi1320; - cam->cam_mode = mi1320_mode; - cam->nmodes = ARRAY_SIZE(mi1320_mode); - dev_init_settings = mi1320_init_settings; - break; - - case ID_MI2020: - gspca_dev->sd_desc = &sd_desc_mi2020; - cam->cam_mode = mi2020_mode; - cam->nmodes = ARRAY_SIZE(mi2020_mode); - dev_init_settings = mi2020_init_settings; - break; - - case ID_OV2640: - gspca_dev->sd_desc = &sd_desc_ov2640; - cam->cam_mode = ov2640_mode; - cam->nmodes = ARRAY_SIZE(ov2640_mode); - dev_init_settings = ov2640_init_settings; - break; - - case ID_OV9655: - gspca_dev->sd_desc = &sd_desc_ov9655; - cam->cam_mode = ov9655_mode; - cam->nmodes = ARRAY_SIZE(ov9655_mode); - dev_init_settings = ov9655_init_settings; - break; - } - - dev_init_settings(gspca_dev); - if (AC50Hz != 0xff) - ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; - gl860_build_control_table(gspca_dev); - - return 0; -} - -/* This function is called at probe time after sd_config */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return sd->dev_init_at_startup(gspca_dev); -} - -/* This function is called before to choose the alt setting */ -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return sd->dev_configure_alt(gspca_dev); -} - -/* This function is called to start the webcam */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - return sd->dev_init_pre_alt(gspca_dev); -} - -/* This function is called to stop the webcam */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!sd->gspca_dev.present) - return; - - return sd->dev_post_unset_alt(gspca_dev); -} - -/* This function is called when an image is being received */ -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - static s32 nSkipped; - - s32 mode = (s32) gspca_dev->curr_mode; - s32 nToSkip = - sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); - - /* Test only against 0202h, so endianess does not matter */ - switch (*(s16 *) data) { - case 0x0202: /* End of frame, start a new one */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - nSkipped = 0; - if (sd->nbIm >= 0 && sd->nbIm < 10) - sd->nbIm++; - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - break; - - default: - data += 2; - len -= 2; - if (nSkipped + len <= nToSkip) - nSkipped += len; - else { - if (nSkipped < nToSkip && nSkipped + len > nToSkip) { - data += nToSkip - nSkipped; - len -= nToSkip - nSkipped; - nSkipped = nToSkip + 1; - } - gspca_frame_add(gspca_dev, - INTER_PACKET, data, len); - } - break; - } -} - -/* This function is called when an image has been read */ -/* This function is used to monitor webcam orientation */ -static void sd_callback(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!_OV9655_) { - u8 state; - u8 upsideDown; - - /* Probe sensor orientation */ - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state); - - /* C8/40 means upside-down (looking backwards) */ - /* D8/50 means right-up (looking onwards) */ - upsideDown = (state == 0xc8 || state == 0x40); - - if (upsideDown && sd->nbRightUp > -4) { - if (sd->nbRightUp > 0) - sd->nbRightUp = 0; - if (sd->nbRightUp == -3) { - sd->mirrorMask = 1; - sd->waitSet = 1; - } - sd->nbRightUp--; - } - if (!upsideDown && sd->nbRightUp < 4) { - if (sd->nbRightUp < 0) - sd->nbRightUp = 0; - if (sd->nbRightUp == 3) { - sd->mirrorMask = 0; - sd->waitSet = 1; - } - sd->nbRightUp++; - } - } - - if (sd->waitSet) - sd->dev_camera_settings(gspca_dev); -} - -/*=================== USB driver structure initialisation ==================*/ - -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x05e3, 0x0503)}, - {USB_DEVICE(0x05e3, 0xf191)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, - &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); -} - -static void sd_disconnect(struct usb_interface *intf) -{ - gspca_disconnect(intf); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = sd_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -/*====================== Init and Exit module functions ====================*/ - -module_usb_driver(sd_driver); - -/*==========================================================================*/ - -int gl860_RTx(struct gspca_dev *gspca_dev, - unsigned char pref, u32 req, u16 val, u16 index, - s32 len, void *pdata) -{ - struct usb_device *udev = gspca_dev->dev; - s32 r = 0; - - if (pref == 0x40) { /* Send */ - if (len > 0) { - memcpy(gspca_dev->usb_buf, pdata, len); - r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - req, pref, val, index, - gspca_dev->usb_buf, - len, 400 + 200 * (len > 1)); - } else { - r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - req, pref, val, index, NULL, len, 400); - } - } else { /* Receive */ - if (len > 0) { - r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - req, pref, val, index, - gspca_dev->usb_buf, - len, 400 + 200 * (len > 1)); - memcpy(pdata, gspca_dev->usb_buf, len); - } else { - r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - req, pref, val, index, NULL, len, 400); - } - } - - if (r < 0) - pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n", - r, pref, req, val, index, len); - else if (len > 1 && r < len) - PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); - - msleep(1); - - return r; -} - -int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len) -{ - int n; - - for (n = 0; n < len; n++) { - if (tbl[n].idx != 0xffff) - ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, - tbl[n].idx, 0, NULL); - else if (tbl[n].val == 0xffff) - break; - else - msleep(tbl[n].val); - } - return n; -} - -int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, - int len, int n) -{ - while (++n < len) { - if (tbl[n].idx != 0xffff) - ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx, - 0, NULL); - else if (tbl[n].val == 0xffff) - break; - else - msleep(tbl[n].val); - } - return n; -} - -void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len) -{ - int n; - - for (n = 0; n < len; n++) { - if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0) - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx, - 3, tbl[n].data); - else - msleep(tbl[n].idx); - } -} - -static int gl860_guess_sensor(struct gspca_dev *gspca_dev, - u16 vendor_id, u16 product_id) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 probe, nb26, nb96, nOV, ntry; - - if (product_id == 0xf191) - sd->sensor = ID_MI1320; - - if (sd->sensor == 0xff) { - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); - - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); - msleep(56); - - PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX"); - nOV = 0; - for (ntry = 0; ntry < 4; ntry++) { - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); - msleep(10); - ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); - PDEBUG(D_PROBE, "probe=0x%02x", probe); - if (probe == 0xff) - nOV++; - } - - if (nOV) { - PDEBUG(D_PROBE, "0xff -> OVXXXX"); - PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655"); - - nb26 = nb96 = 0; - for (ntry = 0; ntry < 4; ntry++) { - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, - 0, NULL); - msleep(3); - ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, - 0, NULL); - msleep(10); - - /* Wait for 26(OV2640) or 96(OV9655) */ - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, - 1, &probe); - - if (probe == 0x26 || probe == 0x40) { - PDEBUG(D_PROBE, - "probe=0x%02x -> OV2640", - probe); - sd->sensor = ID_OV2640; - nb26 += 4; - break; - } - if (probe == 0x96 || probe == 0x55) { - PDEBUG(D_PROBE, - "probe=0x%02x -> OV9655", - probe); - sd->sensor = ID_OV9655; - nb96 += 4; - break; - } - PDEBUG(D_PROBE, "probe=0x%02x", probe); - if (probe == 0x00) - nb26++; - if (probe == 0xff) - nb96++; - msleep(3); - } - if (nb26 < 4 && nb96 < 4) - return -1; - } else { - PDEBUG(D_PROBE, "Not any 0xff -> MI2020"); - sd->sensor = ID_MI2020; - } - } - - if (_MI1320_) { - PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); - } else if (_MI2020_) { - PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); - } else if (_OV9655_) { - PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); - } else if (_OV2640_) { - PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)"); - } else { - PDEBUG(D_PROBE, "***** Unknown sensor *****"); - return -1; - } - - return 0; -} diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h deleted file mode 100644 index 0330a0293b9c..000000000000 --- a/drivers/media/video/gspca/gl860/gl860.h +++ /dev/null @@ -1,105 +0,0 @@ -/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip - * Subdriver declarations - * - * 2009/10/14 Olivier LORIN - * - * This program is free software; 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 - * 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 GL860_DEV_H -#define GL860_DEV_H - -#include "gspca.h" - -#define MODULE_NAME "gspca_gl860" -#define DRIVER_VERSION "0.9d10" - -#define ctrl_in gl860_RTx -#define ctrl_out gl860_RTx - -#define ID_MI1320 1 -#define ID_OV2640 2 -#define ID_OV9655 4 -#define ID_MI2020 8 - -#define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320) -#define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020) -#define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640) -#define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655) - -#define IMAGE_640 0 -#define IMAGE_800 1 -#define IMAGE_1280 2 -#define IMAGE_1600 3 - -struct sd_gl860 { - u16 backlight; - u16 brightness; - u16 sharpness; - u16 contrast; - u16 gamma; - u16 hue; - u16 saturation; - u16 whitebal; - u8 mirror; - u8 flip; - u8 AC50Hz; -}; - -/* Specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct sd_gl860 vcur; - struct sd_gl860 vold; - struct sd_gl860 vmax; - - int (*dev_configure_alt) (struct gspca_dev *); - int (*dev_init_at_startup)(struct gspca_dev *); - int (*dev_init_pre_alt) (struct gspca_dev *); - void (*dev_post_unset_alt) (struct gspca_dev *); - int (*dev_camera_settings)(struct gspca_dev *); - - u8 swapRB; - u8 mirrorMask; - u8 sensor; - s32 nbIm; - s32 nbRightUp; - u8 waitSet; -}; - -struct validx { - u16 val; - u16 idx; -}; - -struct idxdata { - u8 idx; - u8 data[3]; -}; - -int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len); -int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, - int len, int n); -void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len); - -int gl860_RTx(struct gspca_dev *gspca_dev, - unsigned char pref, u32 req, u16 val, u16 index, - s32 len, void *pdata); - -void mi1320_init_settings(struct gspca_dev *); -void ov2640_init_settings(struct gspca_dev *); -void ov9655_init_settings(struct gspca_dev *); -void mi2020_init_settings(struct gspca_dev *); - -#endif diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c deleted file mode 100644 index d4e8343f5b10..000000000000 --- a/drivers/media/video/gspca/gspca.c +++ /dev/null @@ -1,2456 +0,0 @@ -/* - * Main USB camera driver - * - * Copyright (C) 2008-2011 Jean-François Moine - * - * Camera button input handling by Márton Németh - * Copyright (C) 2009-2010 Márton Németh - * - * This program is free software; 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 - -#define GSPCA_VERSION "2.14.0" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gspca.h" - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -#include -#include -#endif - -/* global values */ -#define DEF_NURBS 3 /* default number of URBs */ -#if DEF_NURBS > MAX_NURBS -#error "DEF_NURBS too big" -#endif - -MODULE_AUTHOR("Jean-François Moine "); -MODULE_DESCRIPTION("GSPCA USB Camera Driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(GSPCA_VERSION); - -#ifdef GSPCA_DEBUG -int gspca_debug = D_ERR | D_PROBE; -EXPORT_SYMBOL(gspca_debug); - -static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) -{ - if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') { - PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d", - txt, - pixfmt & 0xff, - (pixfmt >> 8) & 0xff, - (pixfmt >> 16) & 0xff, - pixfmt >> 24, - w, h); - } else { - PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d", - txt, - pixfmt, - w, h); - } -} -#else -#define PDEBUG_MODE(txt, pixfmt, w, h) -#endif - -/* specific memory types - !! should be different from V4L2_MEMORY_xxx */ -#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ -#define GSPCA_MEMORY_READ 7 - -#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) - -/* - * VMA operations. - */ -static void gspca_vm_open(struct vm_area_struct *vma) -{ - struct gspca_frame *frame = vma->vm_private_data; - - frame->vma_use_count++; - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED; -} - -static void gspca_vm_close(struct vm_area_struct *vma) -{ - struct gspca_frame *frame = vma->vm_private_data; - - if (--frame->vma_use_count <= 0) - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; -} - -static const struct vm_operations_struct gspca_vm_ops = { - .open = gspca_vm_open, - .close = gspca_vm_close, -}; - -/* - * Input and interrupt endpoint handling functions - */ -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static void int_irq(struct urb *urb) -{ - struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - int ret; - - ret = urb->status; - switch (ret) { - case 0: - if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev, - urb->transfer_buffer, urb->actual_length) < 0) { - PDEBUG(D_ERR, "Unknown packet received"); - } - break; - - case -ENOENT: - case -ECONNRESET: - case -ENODEV: - case -ESHUTDOWN: - /* Stop is requested either by software or hardware is gone, - * keep the ret value non-zero and don't resubmit later. - */ - break; - - default: - PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status); - urb->status = 0; - ret = 0; - } - - if (ret == 0) { - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) - pr_err("Resubmit URB failed with error %i\n", ret); - } -} - -static int gspca_input_connect(struct gspca_dev *dev) -{ - struct input_dev *input_dev; - int err = 0; - - dev->input_dev = NULL; - if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) { - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; - - usb_make_path(dev->dev, dev->phys, sizeof(dev->phys)); - strlcat(dev->phys, "/input0", sizeof(dev->phys)); - - input_dev->name = dev->sd_desc->name; - input_dev->phys = dev->phys; - - usb_to_input_id(dev->dev, &input_dev->id); - - input_dev->evbit[0] = BIT_MASK(EV_KEY); - input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); - input_dev->dev.parent = &dev->dev->dev; - - err = input_register_device(input_dev); - if (err) { - pr_err("Input device registration failed with error %i\n", - err); - input_dev->dev.parent = NULL; - input_free_device(input_dev); - } else { - dev->input_dev = input_dev; - } - } - - return err; -} - -static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, - struct usb_endpoint_descriptor *ep) -{ - unsigned int buffer_len; - int interval; - struct urb *urb; - struct usb_device *dev; - void *buffer = NULL; - int ret = -EINVAL; - - buffer_len = le16_to_cpu(ep->wMaxPacketSize); - interval = ep->bInterval; - PDEBUG(D_CONF, "found int in endpoint: 0x%x, " - "buffer_len=%u, interval=%u", - ep->bEndpointAddress, buffer_len, interval); - - dev = gspca_dev->dev; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - ret = -ENOMEM; - goto error; - } - - buffer = usb_alloc_coherent(dev, buffer_len, - GFP_KERNEL, &urb->transfer_dma); - if (!buffer) { - ret = -ENOMEM; - goto error_buffer; - } - usb_fill_int_urb(urb, dev, - usb_rcvintpipe(dev, ep->bEndpointAddress), - buffer, buffer_len, - int_irq, (void *)gspca_dev, interval); - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - ret = usb_submit_urb(urb, GFP_KERNEL); - if (ret < 0) { - PDEBUG(D_ERR, "submit int URB failed with error %i", ret); - goto error_submit; - } - gspca_dev->int_urb = urb; - return ret; - -error_submit: - usb_free_coherent(dev, - urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); -error_buffer: - usb_free_urb(urb); -error: - return ret; -} - -static void gspca_input_create_urb(struct gspca_dev *gspca_dev) -{ - struct usb_interface *intf; - struct usb_host_interface *intf_desc; - struct usb_endpoint_descriptor *ep; - int i; - - if (gspca_dev->sd_desc->int_pkt_scan) { - intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - intf_desc = intf->cur_altsetting; - for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { - ep = &intf_desc->endpoint[i].desc; - if (usb_endpoint_dir_in(ep) && - usb_endpoint_xfer_int(ep)) { - - alloc_and_submit_int_urb(gspca_dev, ep); - break; - } - } - } -} - -static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) -{ - struct urb *urb; - - urb = gspca_dev->int_urb; - if (urb) { - gspca_dev->int_urb = NULL; - usb_kill_urb(urb); - usb_free_coherent(gspca_dev->dev, - urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); - usb_free_urb(urb); - } -} -#else -static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) -{ -} - -static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev) -{ -} - -static inline int gspca_input_connect(struct gspca_dev *dev) -{ - return 0; -} -#endif - -/* - * fill a video frame from an URB and resubmit - */ -static void fill_frame(struct gspca_dev *gspca_dev, - struct urb *urb) -{ - u8 *data; /* address of data in the iso message */ - int i, len, st; - cam_pkt_op pkt_scan; - - if (urb->status != 0) { - if (urb->status == -ESHUTDOWN) - return; /* disconnection */ -#ifdef CONFIG_PM - if (gspca_dev->frozen) - return; -#endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - urb->status = 0; - goto resubmit; - } - pkt_scan = gspca_dev->sd_desc->pkt_scan; - for (i = 0; i < urb->number_of_packets; i++) { - len = urb->iso_frame_desc[i].actual_length; - - /* check the packet status and length */ - st = urb->iso_frame_desc[i].status; - if (st) { - pr_err("ISOC data error: [%d] len=%d, status=%d\n", - i, len, st); - gspca_dev->last_packet_type = DISCARD_PACKET; - continue; - } - if (len == 0) { - if (gspca_dev->empty_packet == 0) - gspca_dev->empty_packet = 1; - continue; - } - - /* let the packet be analyzed by the subdriver */ - PDEBUG(D_PACK, "packet [%d] o:%d l:%d", - i, urb->iso_frame_desc[i].offset, len); - data = (u8 *) urb->transfer_buffer - + urb->iso_frame_desc[i].offset; - pkt_scan(gspca_dev, data, len); - } - -resubmit: - /* resubmit the URB */ - st = usb_submit_urb(urb, GFP_ATOMIC); - if (st < 0) - pr_err("usb_submit_urb() ret %d\n", st); -} - -/* - * ISOC message interrupt from the USB device - * - * Analyse each packet and call the subdriver for copy to the frame buffer. - */ -static void isoc_irq(struct urb *urb) -{ - struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - - PDEBUG(D_PACK, "isoc irq"); - if (!gspca_dev->streaming) - return; - fill_frame(gspca_dev, urb); -} - -/* - * bulk message interrupt from the USB device - */ -static void bulk_irq(struct urb *urb) -{ - struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - int st; - - PDEBUG(D_PACK, "bulk irq"); - if (!gspca_dev->streaming) - return; - switch (urb->status) { - case 0: - break; - case -ESHUTDOWN: - return; /* disconnection */ - default: -#ifdef CONFIG_PM - if (gspca_dev->frozen) - return; -#endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - urb->status = 0; - goto resubmit; - } - - PDEBUG(D_PACK, "packet l:%d", urb->actual_length); - gspca_dev->sd_desc->pkt_scan(gspca_dev, - urb->transfer_buffer, - urb->actual_length); - -resubmit: - /* resubmit the URB */ - if (gspca_dev->cam.bulk_nurbs != 0) { - st = usb_submit_urb(urb, GFP_ATOMIC); - if (st < 0) - pr_err("usb_submit_urb() ret %d\n", st); - } -} - -/* - * add data to the current frame - * - * This function is called by the subdrivers at interrupt level. - * - * To build a frame, these ones must add - * - one FIRST_PACKET - * - 0 or many INTER_PACKETs - * - one LAST_PACKET - * DISCARD_PACKET invalidates the whole frame. - */ -void gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - const u8 *data, - int len) -{ - struct gspca_frame *frame; - int i, j; - - PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); - - if (packet_type == FIRST_PACKET) { - i = atomic_read(&gspca_dev->fr_i); - - /* if there are no queued buffer, discard the whole frame */ - if (i == atomic_read(&gspca_dev->fr_q)) { - gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence++; - return; - } - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get()); - frame->v4l2_buf.sequence = gspca_dev->sequence++; - gspca_dev->image = frame->data; - gspca_dev->image_len = 0; - } else { - switch (gspca_dev->last_packet_type) { - case DISCARD_PACKET: - if (packet_type == LAST_PACKET) { - gspca_dev->last_packet_type = packet_type; - gspca_dev->image = NULL; - gspca_dev->image_len = 0; - } - return; - case LAST_PACKET: - return; - } - } - - /* append the packet to the frame buffer */ - if (len > 0) { - if (gspca_dev->image_len + len > gspca_dev->frsz) { - PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d", - gspca_dev->image_len + len, - gspca_dev->frsz); - packet_type = DISCARD_PACKET; - } else { -/* !! image is NULL only when last pkt is LAST or DISCARD - if (gspca_dev->image == NULL) { - pr_err("gspca_frame_add() image == NULL\n"); - return; - } - */ - memcpy(gspca_dev->image + gspca_dev->image_len, - data, len); - gspca_dev->image_len += len; - } - } - gspca_dev->last_packet_type = packet_type; - - /* if last packet, invalidate packet concatenation until - * next first packet, wake up the application and advance - * in the queue */ - if (packet_type == LAST_PACKET) { - i = atomic_read(&gspca_dev->fr_i); - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - frame->v4l2_buf.bytesused = gspca_dev->image_len; - frame->v4l2_buf.flags = (frame->v4l2_buf.flags - | V4L2_BUF_FLAG_DONE) - & ~V4L2_BUF_FLAG_QUEUED; - i = (i + 1) % GSPCA_MAX_FRAMES; - atomic_set(&gspca_dev->fr_i, i); - wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ - PDEBUG(D_FRAM, "frame complete len:%d", - frame->v4l2_buf.bytesused); - gspca_dev->image = NULL; - gspca_dev->image_len = 0; - } -} -EXPORT_SYMBOL(gspca_frame_add); - -static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory, unsigned int count) -{ - struct gspca_frame *frame; - unsigned int frsz; - int i; - - i = gspca_dev->curr_mode; - frsz = gspca_dev->cam.cam_mode[i].sizeimage; - PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); - frsz = PAGE_ALIGN(frsz); - if (count >= GSPCA_MAX_FRAMES) - count = GSPCA_MAX_FRAMES - 1; - gspca_dev->frbuf = vmalloc_32(frsz * count); - if (!gspca_dev->frbuf) { - pr_err("frame alloc failed\n"); - return -ENOMEM; - } - gspca_dev->capt_file = file; - gspca_dev->memory = memory; - gspca_dev->frsz = frsz; - gspca_dev->nframes = count; - for (i = 0; i < count; i++) { - frame = &gspca_dev->frame[i]; - frame->v4l2_buf.index = i; - frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - frame->v4l2_buf.flags = 0; - frame->v4l2_buf.field = V4L2_FIELD_NONE; - frame->v4l2_buf.length = frsz; - frame->v4l2_buf.memory = memory; - frame->v4l2_buf.sequence = 0; - frame->data = gspca_dev->frbuf + i * frsz; - frame->v4l2_buf.m.offset = i * frsz; - } - atomic_set(&gspca_dev->fr_q, 0); - atomic_set(&gspca_dev->fr_i, 0); - gspca_dev->fr_o = 0; - return 0; -} - -static void frame_free(struct gspca_dev *gspca_dev) -{ - int i; - - PDEBUG(D_STREAM, "frame free"); - if (gspca_dev->frbuf != NULL) { - vfree(gspca_dev->frbuf); - gspca_dev->frbuf = NULL; - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].data = NULL; - } - gspca_dev->nframes = 0; - gspca_dev->frsz = 0; - gspca_dev->capt_file = NULL; - gspca_dev->memory = GSPCA_MEMORY_NO; -} - -static void destroy_urbs(struct gspca_dev *gspca_dev) -{ - struct urb *urb; - unsigned int i; - - PDEBUG(D_STREAM, "kill transfer"); - for (i = 0; i < MAX_NURBS; i++) { - urb = gspca_dev->urb[i]; - if (urb == NULL) - break; - - gspca_dev->urb[i] = NULL; - usb_kill_urb(urb); - if (urb->transfer_buffer != NULL) - usb_free_coherent(gspca_dev->dev, - urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); - usb_free_urb(urb); - } -} - -static int gspca_set_alt0(struct gspca_dev *gspca_dev) -{ - int ret; - - if (gspca_dev->alt == 0) - return 0; - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); - if (ret < 0) - pr_err("set alt 0 err %d\n", ret); - return ret; -} - -/* Note: both the queue and the usb locks should be held when calling this */ -static void gspca_stream_off(struct gspca_dev *gspca_dev) -{ - gspca_dev->streaming = 0; - gspca_dev->usb_err = 0; - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - if (gspca_dev->sd_desc->stop0) - gspca_dev->sd_desc->stop0(gspca_dev); - PDEBUG(D_STREAM, "stream off OK"); -} - -/* - * look for an input transfer endpoint in an alternate setting - */ -static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, - int xfer) -{ - struct usb_host_endpoint *ep; - int i, attr; - - for (i = 0; i < alt->desc.bNumEndpoints; i++) { - ep = &alt->endpoint[i]; - attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - if (attr == xfer - && ep->desc.wMaxPacketSize != 0 - && usb_endpoint_dir_in(&ep->desc)) - return ep; - } - return NULL; -} - -/* compute the minimum bandwidth for the current transfer */ -static u32 which_bandwidth(struct gspca_dev *gspca_dev) -{ - u32 bandwidth; - int i; - - /* get the (max) image size */ - i = gspca_dev->curr_mode; - bandwidth = gspca_dev->cam.cam_mode[i].sizeimage; - - /* if the image is compressed, estimate its mean size */ - if (!gspca_dev->cam.needs_full_bandwidth && - bandwidth < gspca_dev->cam.cam_mode[i].width * - gspca_dev->cam.cam_mode[i].height) - bandwidth = bandwidth * 3 / 8; /* 0.375 */ - - /* estimate the frame rate */ - if (gspca_dev->sd_desc->get_streamparm) { - struct v4l2_streamparm parm; - - gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm); - bandwidth *= parm.parm.capture.timeperframe.denominator; - bandwidth /= parm.parm.capture.timeperframe.numerator; - } else { - - /* don't hope more than 15 fps with USB 1.1 and - * image resolution >= 640x480 */ - if (gspca_dev->width >= 640 - && gspca_dev->dev->speed == USB_SPEED_FULL) - bandwidth *= 15; /* 15 fps */ - else - bandwidth *= 30; /* 30 fps */ - } - - PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth); - return bandwidth; -} - -/* endpoint table */ -#define MAX_ALT 16 -struct ep_tb_s { - u32 alt; - u32 bandwidth; -}; - -/* - * build the table of the endpoints - * and compute the minimum bandwidth for the image transfer - */ -static int build_isoc_ep_tb(struct gspca_dev *gspca_dev, - struct usb_interface *intf, - struct ep_tb_s *ep_tb) -{ - struct usb_host_endpoint *ep; - int i, j, nbalt, psize, found; - u32 bandwidth, last_bw; - - nbalt = intf->num_altsetting; - if (nbalt > MAX_ALT) - nbalt = MAX_ALT; /* fixme: should warn */ - - /* build the endpoint table */ - i = 0; - last_bw = 0; - for (;;) { - ep_tb->bandwidth = 2000 * 2000 * 120; - found = 0; - for (j = 0; j < nbalt; j++) { - ep = alt_xfer(&intf->altsetting[j], - USB_ENDPOINT_XFER_ISOC); - if (ep == NULL) - continue; - if (ep->desc.bInterval == 0) { - pr_err("alt %d iso endp with 0 interval\n", j); - continue; - } - psize = le16_to_cpu(ep->desc.wMaxPacketSize); - psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - bandwidth = psize * 1000; - if (gspca_dev->dev->speed == USB_SPEED_HIGH - || gspca_dev->dev->speed == USB_SPEED_SUPER) - bandwidth *= 8; - bandwidth /= 1 << (ep->desc.bInterval - 1); - if (bandwidth <= last_bw) - continue; - if (bandwidth < ep_tb->bandwidth) { - ep_tb->bandwidth = bandwidth; - ep_tb->alt = j; - found = 1; - } - } - if (!found) - break; - PDEBUG(D_STREAM, "alt %d bandwidth %d", - ep_tb->alt, ep_tb->bandwidth); - last_bw = ep_tb->bandwidth; - i++; - ep_tb++; - } - - /* - * If the camera: - * has a usb audio class interface (a built in usb mic); and - * is a usb 1 full speed device; and - * uses the max full speed iso bandwidth; and - * and has more than 1 alt setting - * then skip the highest alt setting to spare bandwidth for the mic - */ - if (gspca_dev->audio && - gspca_dev->dev->speed == USB_SPEED_FULL && - last_bw >= 1000000 && - i > 1) { - PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt"); - i--; - ep_tb--; - } - - /* get the requested bandwidth and start at the highest atlsetting */ - bandwidth = which_bandwidth(gspca_dev); - ep_tb--; - while (i > 1) { - ep_tb--; - if (ep_tb->bandwidth < bandwidth) - break; - i--; - } - return i; -} - -/* - * create the URBs for image transfer - */ -static int create_urbs(struct gspca_dev *gspca_dev, - struct usb_host_endpoint *ep) -{ - struct urb *urb; - int n, nurbs, i, psize, npkt, bsize; - - /* calculate the packet size and the number of packets */ - psize = le16_to_cpu(ep->desc.wMaxPacketSize); - - if (!gspca_dev->cam.bulk) { /* isoc */ - - /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ - if (gspca_dev->pkt_size == 0) - psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - else - psize = gspca_dev->pkt_size; - npkt = gspca_dev->cam.npkt; - if (npkt == 0) - npkt = 32; /* default value */ - bsize = psize * npkt; - PDEBUG(D_STREAM, - "isoc %d pkts size %d = bsize:%d", - npkt, psize, bsize); - nurbs = DEF_NURBS; - } else { /* bulk */ - npkt = 0; - bsize = gspca_dev->cam.bulk_size; - if (bsize == 0) - bsize = psize; - PDEBUG(D_STREAM, "bulk bsize:%d", bsize); - if (gspca_dev->cam.bulk_nurbs != 0) - nurbs = gspca_dev->cam.bulk_nurbs; - else - nurbs = 1; - } - - for (n = 0; n < nurbs; n++) { - urb = usb_alloc_urb(npkt, GFP_KERNEL); - if (!urb) { - pr_err("usb_alloc_urb failed\n"); - return -ENOMEM; - } - gspca_dev->urb[n] = urb; - urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, - bsize, - GFP_KERNEL, - &urb->transfer_dma); - - if (urb->transfer_buffer == NULL) { - pr_err("usb_alloc_coherent failed\n"); - return -ENOMEM; - } - urb->dev = gspca_dev->dev; - urb->context = gspca_dev; - urb->transfer_buffer_length = bsize; - if (npkt != 0) { /* ISOC */ - urb->pipe = usb_rcvisocpipe(gspca_dev->dev, - ep->desc.bEndpointAddress); - urb->transfer_flags = URB_ISO_ASAP - | URB_NO_TRANSFER_DMA_MAP; - urb->interval = 1 << (ep->desc.bInterval - 1); - urb->complete = isoc_irq; - urb->number_of_packets = npkt; - for (i = 0; i < npkt; i++) { - urb->iso_frame_desc[i].length = psize; - urb->iso_frame_desc[i].offset = psize * i; - } - } else { /* bulk */ - urb->pipe = usb_rcvbulkpipe(gspca_dev->dev, - ep->desc.bEndpointAddress); - urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - urb->complete = bulk_irq; - } - } - return 0; -} - -/* - * start the USB transfer - */ -static int gspca_init_transfer(struct gspca_dev *gspca_dev) -{ - struct usb_interface *intf; - struct usb_host_endpoint *ep; - struct urb *urb; - struct ep_tb_s ep_tb[MAX_ALT]; - int n, ret, xfer, alt, alt_idx; - - /* reset the streaming variables */ - gspca_dev->image = NULL; - gspca_dev->image_len = 0; - gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence = 0; - - gspca_dev->usb_err = 0; - - /* do the specific subdriver stuff before endpoint selection */ - intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0; - if (gspca_dev->sd_desc->isoc_init) { - ret = gspca_dev->sd_desc->isoc_init(gspca_dev); - if (ret < 0) - return ret; - } - xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK - : USB_ENDPOINT_XFER_ISOC; - - /* if bulk or the subdriver forced an altsetting, get the endpoint */ - if (gspca_dev->alt != 0) { - gspca_dev->alt--; /* (previous version compatibility) */ - ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); - if (ep == NULL) { - pr_err("bad altsetting %d\n", gspca_dev->alt); - return -EIO; - } - ep_tb[0].alt = gspca_dev->alt; - alt_idx = 1; - } else { - - /* else, compute the minimum bandwidth - * and build the endpoint table */ - alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); - if (alt_idx <= 0) { - pr_err("no transfer endpoint found\n"); - return -EIO; - } - } - - /* set the highest alternate setting and - * loop until urb submit succeeds */ - gspca_input_destroy_urb(gspca_dev); - - gspca_dev->alt = ep_tb[--alt_idx].alt; - alt = -1; - for (;;) { - if (alt != gspca_dev->alt) { - alt = gspca_dev->alt; - if (intf->num_altsetting > 1) { - ret = usb_set_interface(gspca_dev->dev, - gspca_dev->iface, - alt); - if (ret < 0) { - if (ret == -ENOSPC) - goto retry; /*fixme: ugly*/ - pr_err("set alt %d err %d\n", alt, ret); - goto out; - } - } - } - if (!gspca_dev->cam.no_urb_create) { - PDEBUG(D_STREAM, "init transfer alt %d", alt); - ret = create_urbs(gspca_dev, - alt_xfer(&intf->altsetting[alt], xfer)); - if (ret < 0) { - destroy_urbs(gspca_dev); - goto out; - } - } - - /* clear the bulk endpoint */ - if (gspca_dev->cam.bulk) - usb_clear_halt(gspca_dev->dev, - gspca_dev->urb[0]->pipe); - - /* start the cam */ - ret = gspca_dev->sd_desc->start(gspca_dev); - if (ret < 0) { - destroy_urbs(gspca_dev); - goto out; - } - gspca_dev->streaming = 1; - v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); - - /* some bulk transfers are started by the subdriver */ - if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) - break; - - /* submit the URBs */ - for (n = 0; n < MAX_NURBS; n++) { - urb = gspca_dev->urb[n]; - if (urb == NULL) - break; - ret = usb_submit_urb(urb, GFP_KERNEL); - if (ret < 0) - break; - } - if (ret >= 0) - break; /* transfer is started */ - - /* something when wrong - * stop the webcam and free the transfer resources */ - gspca_stream_off(gspca_dev); - if (ret != -ENOSPC) { - pr_err("usb_submit_urb alt %d err %d\n", - gspca_dev->alt, ret); - goto out; - } - - /* the bandwidth is not wide enough - * negotiate or try a lower alternate setting */ -retry: - PDEBUG(D_ERR|D_STREAM, - "alt %d - bandwidth not wide enough - trying again", - alt); - msleep(20); /* wait for kill complete */ - if (gspca_dev->sd_desc->isoc_nego) { - ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); - if (ret < 0) - goto out; - } else { - if (alt_idx <= 0) { - pr_err("no transfer endpoint found\n"); - ret = -EIO; - goto out; - } - gspca_dev->alt = ep_tb[--alt_idx].alt; - } - } -out: - gspca_input_create_urb(gspca_dev); - return ret; -} - -static void gspca_set_default_mode(struct gspca_dev *gspca_dev) -{ - struct gspca_ctrl *ctrl; - int i; - - i = gspca_dev->cam.nmodes - 1; /* take the highest mode */ - gspca_dev->curr_mode = i; - gspca_dev->width = gspca_dev->cam.cam_mode[i].width; - gspca_dev->height = gspca_dev->cam.cam_mode[i].height; - gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat; - - /* set the current control values to their default values - * which may have changed in sd_init() */ - /* does nothing if ctrl_handler == NULL */ - v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); - ctrl = gspca_dev->cam.ctrls; - if (ctrl != NULL) { - for (i = 0; - i < gspca_dev->sd_desc->nctrls; - i++, ctrl++) - ctrl->val = ctrl->def; - } -} - -static int wxh_to_mode(struct gspca_dev *gspca_dev, - int width, int height) -{ - int i; - - for (i = gspca_dev->cam.nmodes; --i > 0; ) { - if (width >= gspca_dev->cam.cam_mode[i].width - && height >= gspca_dev->cam.cam_mode[i].height) - break; - } - return i; -} - -/* - * search a mode with the right pixel format - */ -static int gspca_get_mode(struct gspca_dev *gspca_dev, - int mode, - int pixfmt) -{ - int modeU, modeD; - - modeU = modeD = mode; - while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) { - if (--modeD >= 0) { - if (gspca_dev->cam.cam_mode[modeD].pixelformat - == pixfmt) - return modeD; - } - if (++modeU < gspca_dev->cam.nmodes) { - if (gspca_dev->cam.cam_mode[modeU].pixelformat - == pixfmt) - return modeU; - } - } - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - gspca_dev->usb_err = 0; - return gspca_dev->sd_desc->get_register(gspca_dev, reg); -} - -static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - gspca_dev->usb_err = 0; - return gspca_dev->sd_desc->set_register(gspca_dev, reg); -} -#endif - -static int vidioc_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - gspca_dev->usb_err = 0; - return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *fmtdesc) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i, j, index; - __u32 fmt_tb[8]; - - /* give an index to each format */ - index = 0; - j = 0; - for (i = gspca_dev->cam.nmodes; --i >= 0; ) { - fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat; - j = 0; - for (;;) { - if (fmt_tb[j] == fmt_tb[index]) - break; - j++; - } - if (j == index) { - if (fmtdesc->index == index) - break; /* new format */ - index++; - if (index >= ARRAY_SIZE(fmt_tb)) - return -EINVAL; - } - } - if (i < 0) - return -EINVAL; /* no more format */ - - fmtdesc->pixelformat = fmt_tb[index]; - if (gspca_dev->cam.cam_mode[i].sizeimage < - gspca_dev->cam.cam_mode[i].width * - gspca_dev->cam.cam_mode[i].height) - fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; - fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; - fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; - fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff; - fmtdesc->description[3] = fmtdesc->pixelformat >> 24; - fmtdesc->description[4] = '\0'; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int mode; - - mode = gspca_dev->curr_mode; - fmt->fmt.pix = gspca_dev->cam.cam_mode[mode]; - /* some drivers use priv internally, zero it before giving it to - userspace */ - fmt->fmt.pix.priv = 0; - return 0; -} - -static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, - struct v4l2_format *fmt) -{ - int w, h, mode, mode2; - - w = fmt->fmt.pix.width; - h = fmt->fmt.pix.height; - -#ifdef GSPCA_DEBUG - if (gspca_debug & D_CONF) - PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h); -#endif - /* search the closest mode for width and height */ - mode = wxh_to_mode(gspca_dev, w, h); - - /* OK if right palette */ - if (gspca_dev->cam.cam_mode[mode].pixelformat - != fmt->fmt.pix.pixelformat) { - - /* else, search the closest mode with the same pixel format */ - mode2 = gspca_get_mode(gspca_dev, mode, - fmt->fmt.pix.pixelformat); - if (mode2 >= 0) - mode = mode2; -/* else - ; * no chance, return this mode */ - } - fmt->fmt.pix = gspca_dev->cam.cam_mode[mode]; - /* some drivers use priv internally, zero it before giving it to - userspace */ - fmt->fmt.pix.priv = 0; - return mode; /* used when s_fmt */ -} - -static int vidioc_try_fmt_vid_cap(struct file *file, - void *priv, - struct v4l2_format *fmt) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - ret = try_fmt_vid_cap(gspca_dev, fmt); - if (ret < 0) - return ret; - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - ret = try_fmt_vid_cap(gspca_dev, fmt); - if (ret < 0) - goto out; - - if (gspca_dev->nframes != 0 - && fmt->fmt.pix.sizeimage > gspca_dev->frsz) { - ret = -EINVAL; - goto out; - } - - if (ret == gspca_dev->curr_mode) { - ret = 0; - goto out; /* same mode */ - } - - if (gspca_dev->streaming) { - ret = -EBUSY; - goto out; - } - gspca_dev->width = fmt->fmt.pix.width; - gspca_dev->height = fmt->fmt.pix.height; - gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; - gspca_dev->curr_mode = ret; - - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -static int vidioc_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i; - __u32 index = 0; - - for (i = 0; i < gspca_dev->cam.nmodes; i++) { - if (fsize->pixel_format != - gspca_dev->cam.cam_mode[i].pixelformat) - continue; - - if (fsize->index == index) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = - gspca_dev->cam.cam_mode[i].width; - fsize->discrete.height = - gspca_dev->cam.cam_mode[i].height; - return 0; - } - index++; - } - - return -EINVAL; -} - -static int vidioc_enum_frameintervals(struct file *filp, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct gspca_dev *gspca_dev = video_drvdata(filp); - int mode = wxh_to_mode(gspca_dev, fival->width, fival->height); - __u32 i; - - if (gspca_dev->cam.mode_framerates == NULL || - gspca_dev->cam.mode_framerates[mode].nrates == 0) - return -EINVAL; - - if (fival->pixel_format != - gspca_dev->cam.cam_mode[mode].pixelformat) - return -EINVAL; - - for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) { - if (fival->index == i) { - fival->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fival->discrete.numerator = 1; - fival->discrete.denominator = - gspca_dev->cam.mode_framerates[mode].rates[i]; - return 0; - } - } - - return -EINVAL; -} - -static void gspca_release(struct v4l2_device *v4l2_device) -{ - struct gspca_dev *gspca_dev = - container_of(v4l2_device, struct gspca_dev, v4l2_dev); - - v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); - v4l2_device_unregister(&gspca_dev->v4l2_dev); - kfree(gspca_dev->usb_buf); - kfree(gspca_dev); -} - -static int dev_open(struct file *file) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - PDEBUG(D_STREAM, "[%s] open", current->comm); - - /* protect the subdriver against rmmod */ - if (!try_module_get(gspca_dev->module)) - return -ENODEV; - -#ifdef GSPCA_DEBUG - /* activate the v4l2 debug */ - if (gspca_debug & D_V4L2) - gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL - | V4L2_DEBUG_IOCTL_ARG; - else - gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL - | V4L2_DEBUG_IOCTL_ARG); -#endif - return v4l2_fh_open(file); -} - -static int dev_close(struct file *file) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - PDEBUG(D_STREAM, "[%s] close", current->comm); - - /* Needed for gspca_stream_off, always lock before queue_lock! */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { - mutex_unlock(&gspca_dev->usb_lock); - return -ERESTARTSYS; - } - - /* if the file did the capture, free the streaming resources */ - if (gspca_dev->capt_file == file) { - if (gspca_dev->streaming) - gspca_stream_off(gspca_dev); - frame_free(gspca_dev); - } - module_put(gspca_dev->module); - mutex_unlock(&gspca_dev->queue_lock); - mutex_unlock(&gspca_dev->usb_lock); - - PDEBUG(D_STREAM, "close done"); - - return v4l2_fh_release(file); -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - strlcpy((char *) cap->driver, gspca_dev->sd_desc->name, - sizeof cap->driver); - if (gspca_dev->dev->product != NULL) { - strlcpy((char *) cap->card, gspca_dev->dev->product, - sizeof cap->card); - } else { - snprintf((char *) cap->card, sizeof cap->card, - "USB Camera (%04x:%04x)", - le16_to_cpu(gspca_dev->dev->descriptor.idVendor), - le16_to_cpu(gspca_dev->dev->descriptor.idProduct)); - } - usb_make_path(gspca_dev->dev, (char *) cap->bus_info, - sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_STREAMING - | V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int get_ctrl(struct gspca_dev *gspca_dev, - int id) -{ - const struct ctrl *ctrls; - int i; - - for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; - i < gspca_dev->sd_desc->nctrls; - i++, ctrls++) { - if (gspca_dev->ctrl_dis & (1 << i)) - continue; - if (id == ctrls->qctrl.id) - return i; - } - return -1; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *q_ctrl) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - const struct ctrl *ctrls; - struct gspca_ctrl *gspca_ctrl; - int i, idx; - u32 id; - - id = q_ctrl->id; - if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { - id &= V4L2_CTRL_ID_MASK; - id++; - idx = -1; - for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (gspca_dev->ctrl_dis & (1 << i)) - continue; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) - continue; - if (idx >= 0 - && gspca_dev->sd_desc->ctrls[i].qctrl.id - > gspca_dev->sd_desc->ctrls[idx].qctrl.id) - continue; - idx = i; - } - } else { - idx = get_ctrl(gspca_dev, id); - } - if (idx < 0) - return -EINVAL; - ctrls = &gspca_dev->sd_desc->ctrls[idx]; - memcpy(q_ctrl, &ctrls->qctrl, sizeof *q_ctrl); - if (gspca_dev->cam.ctrls != NULL) { - gspca_ctrl = &gspca_dev->cam.ctrls[idx]; - q_ctrl->default_value = gspca_ctrl->def; - q_ctrl->minimum = gspca_ctrl->min; - q_ctrl->maximum = gspca_ctrl->max; - } - if (gspca_dev->ctrl_inac & (1 << idx)) - q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - const struct ctrl *ctrls; - struct gspca_ctrl *gspca_ctrl; - int idx; - - idx = get_ctrl(gspca_dev, ctrl->id); - if (idx < 0) - return -EINVAL; - if (gspca_dev->ctrl_inac & (1 << idx)) - return -EINVAL; - ctrls = &gspca_dev->sd_desc->ctrls[idx]; - if (gspca_dev->cam.ctrls != NULL) { - gspca_ctrl = &gspca_dev->cam.ctrls[idx]; - if (ctrl->value < gspca_ctrl->min - || ctrl->value > gspca_ctrl->max) - return -ERANGE; - } else { - gspca_ctrl = NULL; - if (ctrl->value < ctrls->qctrl.minimum - || ctrl->value > ctrls->qctrl.maximum) - return -ERANGE; - } - PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); - gspca_dev->usb_err = 0; - if (ctrls->set != NULL) - return ctrls->set(gspca_dev, ctrl->value); - if (gspca_ctrl != NULL) { - gspca_ctrl->val = ctrl->value; - if (ctrls->set_control != NULL - && gspca_dev->streaming) - ctrls->set_control(gspca_dev); - } - return gspca_dev->usb_err; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - const struct ctrl *ctrls; - int idx; - - idx = get_ctrl(gspca_dev, ctrl->id); - if (idx < 0) - return -EINVAL; - ctrls = &gspca_dev->sd_desc->ctrls[idx]; - - gspca_dev->usb_err = 0; - if (ctrls->get != NULL) - return ctrls->get(gspca_dev, &ctrl->value); - if (gspca_dev->cam.ctrls != NULL) - ctrl->value = gspca_dev->cam.ctrls[idx].val; - return 0; -} - -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - if (!gspca_dev->sd_desc->querymenu) - return -ENOTTY; - return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu); -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - if (input->index != 0) - return -EINVAL; - input->type = V4L2_INPUT_TYPE_CAMERA; - input->status = gspca_dev->cam.input_flags; - strlcpy(input->name, gspca_dev->sd_desc->name, - sizeof input->name); - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - return (0); -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i, ret = 0, streaming; - - i = rb->memory; /* (avoid compilation warning) */ - switch (i) { - case GSPCA_MEMORY_READ: /* (internal call) */ - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_USERPTR: - break; - default: - return -EINVAL; - } - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - if (gspca_dev->memory != GSPCA_MEMORY_NO - && gspca_dev->memory != GSPCA_MEMORY_READ - && gspca_dev->memory != rb->memory) { - ret = -EBUSY; - goto out; - } - - /* only one file may do the capture */ - if (gspca_dev->capt_file != NULL - && gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - /* if allocated, the buffers must not be mapped */ - for (i = 0; i < gspca_dev->nframes; i++) { - if (gspca_dev->frame[i].vma_use_count) { - ret = -EBUSY; - goto out; - } - } - - /* stop streaming */ - streaming = gspca_dev->streaming; - if (streaming) { - gspca_stream_off(gspca_dev); - - /* Don't restart the stream when switching from read - * to mmap mode */ - if (gspca_dev->memory == GSPCA_MEMORY_READ) - streaming = 0; - } - - /* free the previous allocated buffers, if any */ - if (gspca_dev->nframes != 0) - frame_free(gspca_dev); - if (rb->count == 0) /* unrequest */ - goto out; - ret = frame_alloc(gspca_dev, file, rb->memory, rb->count); - if (ret == 0) { - rb->count = gspca_dev->nframes; - if (streaming) - ret = gspca_init_transfer(gspca_dev); - } -out: - mutex_unlock(&gspca_dev->queue_lock); - PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count); - return ret; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - - if (v4l2_buf->index < 0 - || v4l2_buf->index >= gspca_dev->nframes) - return -EINVAL; - - frame = &gspca_dev->frame[v4l2_buf->index]; - memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - /* check the capture file */ - if (gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - if (gspca_dev->nframes == 0 - || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { - ret = -EINVAL; - goto out; - } - if (!gspca_dev->streaming) { - ret = gspca_init_transfer(gspca_dev); - if (ret < 0) - goto out; - } -#ifdef GSPCA_DEBUG - if (gspca_debug & D_STREAM) { - PDEBUG_MODE("stream on OK", - gspca_dev->pixfmt, - gspca_dev->width, - gspca_dev->height); - } -#endif - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i, ret; - - if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - if (!gspca_dev->streaming) { - ret = 0; - goto out; - } - - /* check the capture file */ - if (gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - /* stop streaming */ - gspca_stream_off(gspca_dev); - /* In case another thread is waiting in dqbuf */ - wake_up_interruptible(&gspca_dev->wq); - - /* empty the transfer queues */ - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; - atomic_set(&gspca_dev->fr_q, 0); - atomic_set(&gspca_dev->fr_i, 0); - gspca_dev->fr_o = 0; - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -static int vidioc_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jpegcomp) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - gspca_dev->usb_err = 0; - return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); -} - -static int vidioc_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jpegcomp) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - gspca_dev->usb_err = 0; - return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); -} - -static int vidioc_g_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parm) -{ - struct gspca_dev *gspca_dev = video_drvdata(filp); - - parm->parm.capture.readbuffers = gspca_dev->nbufread; - - if (gspca_dev->sd_desc->get_streamparm) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); - return gspca_dev->usb_err; - } - return 0; -} - -static int vidioc_s_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parm) -{ - struct gspca_dev *gspca_dev = video_drvdata(filp); - int n; - - n = parm->parm.capture.readbuffers; - if (n == 0 || n >= GSPCA_MAX_FRAMES) - parm->parm.capture.readbuffers = gspca_dev->nbufread; - else - gspca_dev->nbufread = n; - - if (gspca_dev->sd_desc->set_streamparm) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); - return gspca_dev->usb_err; - } - - return 0; -} - -static int dev_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - struct page *page; - unsigned long addr, start, size; - int i, ret; - - start = vma->vm_start; - size = vma->vm_end - vma->vm_start; - PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - if (gspca_dev->capt_file != file) { - ret = -EINVAL; - goto out; - } - - frame = NULL; - for (i = 0; i < gspca_dev->nframes; ++i) { - if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { - PDEBUG(D_STREAM, "mmap bad memory type"); - break; - } - if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) - == vma->vm_pgoff) { - frame = &gspca_dev->frame[i]; - break; - } - } - if (frame == NULL) { - PDEBUG(D_STREAM, "mmap no frame buffer found"); - ret = -EINVAL; - goto out; - } - if (size != frame->v4l2_buf.length) { - PDEBUG(D_STREAM, "mmap bad size"); - ret = -EINVAL; - goto out; - } - - /* - * - VM_IO marks the area as being a mmaped region for I/O to a - * device. It also prevents the region from being core dumped. - */ - vma->vm_flags |= VM_IO; - - addr = (unsigned long) frame->data; - while (size > 0) { - page = vmalloc_to_page((void *) addr); - ret = vm_insert_page(vma, start, page); - if (ret < 0) - goto out; - start += PAGE_SIZE; - addr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &gspca_vm_ops; - vma->vm_private_data = frame; - gspca_vm_open(vma); - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory) -{ - if (!gspca_dev->present) - return -ENODEV; - if (gspca_dev->capt_file != file || gspca_dev->memory != memory || - !gspca_dev->streaming) - return -EINVAL; - - /* check if a frame is ready */ - return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); -} - -static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory) -{ - int ret; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - ret = frame_ready_nolock(gspca_dev, file, memory); - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -/* - * dequeue a video buffer - * - * If nonblock_ing is false, block until a buffer is available. - */ -static int vidioc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - int i, j, ret; - - PDEBUG(D_FRAM, "dqbuf"); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - for (;;) { - ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); - if (ret < 0) - goto out; - if (ret > 0) - break; - - mutex_unlock(&gspca_dev->queue_lock); - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - /* wait till a frame is ready */ - ret = wait_event_interruptible_timeout(gspca_dev->wq, - frame_ready(gspca_dev, file, v4l2_buf->memory), - msecs_to_jiffies(3000)); - if (ret < 0) - return ret; - if (ret == 0) - return -EIO; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - } - - i = gspca_dev->fr_o; - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - - gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; - - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; - memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); - PDEBUG(D_FRAM, "dqbuf %d", j); - ret = 0; - - if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { - if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, - frame->data, - frame->v4l2_buf.bytesused)) { - PDEBUG(D_ERR|D_STREAM, - "dqbuf cp to user failed"); - ret = -EFAULT; - } - } -out: - mutex_unlock(&gspca_dev->queue_lock); - - if (ret == 0 && gspca_dev->sd_desc->dq_callback) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; - if (gspca_dev->present) - gspca_dev->sd_desc->dq_callback(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } - - return ret; -} - -/* - * queue a video buffer - * - * Attempting to queue a buffer that has already been - * queued will return -EINVAL. - */ -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - int i, index, ret; - - PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - index = v4l2_buf->index; - if ((unsigned) index >= gspca_dev->nframes) { - PDEBUG(D_FRAM, - "qbuf idx %d >= %d", index, gspca_dev->nframes); - ret = -EINVAL; - goto out; - } - if (v4l2_buf->memory != gspca_dev->memory) { - PDEBUG(D_FRAM, "qbuf bad memory type"); - ret = -EINVAL; - goto out; - } - - frame = &gspca_dev->frame[index]; - if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) { - PDEBUG(D_FRAM, "qbuf bad state"); - ret = -EINVAL; - goto out; - } - - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; - - if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { - frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; - frame->v4l2_buf.length = v4l2_buf->length; - } - - /* put the buffer in the 'queued' queue */ - i = atomic_read(&gspca_dev->fr_q); - gspca_dev->fr_queue[i] = index; - atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); - - v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; - v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -/* - * allocate the resources for read() - */ -static int read_alloc(struct gspca_dev *gspca_dev, - struct file *file) -{ - struct v4l2_buffer v4l2_buf; - int i, ret; - - PDEBUG(D_STREAM, "read alloc"); - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (gspca_dev->nframes == 0) { - struct v4l2_requestbuffers rb; - - memset(&rb, 0, sizeof rb); - rb.count = gspca_dev->nbufread; - rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rb.memory = GSPCA_MEMORY_READ; - ret = vidioc_reqbufs(file, gspca_dev, &rb); - if (ret != 0) { - PDEBUG(D_STREAM, "read reqbuf err %d", ret); - goto out; - } - memset(&v4l2_buf, 0, sizeof v4l2_buf); - v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buf.memory = GSPCA_MEMORY_READ; - for (i = 0; i < gspca_dev->nbufread; i++) { - v4l2_buf.index = i; - ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - PDEBUG(D_STREAM, "read qbuf err: %d", ret); - goto out; - } - } - } - - /* start streaming */ - ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (ret != 0) - PDEBUG(D_STREAM, "read streamon err %d", ret); -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; -} - -static unsigned int dev_poll(struct file *file, poll_table *wait) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - unsigned long req_events = poll_requested_events(wait); - int ret = 0; - - PDEBUG(D_FRAM, "poll"); - - if (req_events & POLLPRI) - ret |= v4l2_ctrl_poll(file, wait); - - if (req_events & (POLLIN | POLLRDNORM)) { - /* if reqbufs is not done, the user would use read() */ - if (gspca_dev->memory == GSPCA_MEMORY_NO) { - if (read_alloc(gspca_dev, file) != 0) { - ret |= POLLERR; - goto out; - } - } - - poll_wait(file, &gspca_dev->wq, wait); - - /* check if an image has been received */ - if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { - ret |= POLLERR; - goto out; - } - if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) - ret |= POLLIN | POLLRDNORM; - mutex_unlock(&gspca_dev->queue_lock); - } - -out: - if (!gspca_dev->present) - ret |= POLLHUP; - - return ret; -} - -static ssize_t dev_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - struct v4l2_buffer v4l2_buf; - struct timeval timestamp; - int n, ret, ret2; - - PDEBUG(D_FRAM, "read (%zd)", count); - if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ - ret = read_alloc(gspca_dev, file); - if (ret != 0) - return ret; - } - - /* get a frame */ - timestamp = ktime_to_timeval(ktime_get()); - timestamp.tv_sec--; - n = 2; - for (;;) { - memset(&v4l2_buf, 0, sizeof v4l2_buf); - v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buf.memory = GSPCA_MEMORY_READ; - ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - PDEBUG(D_STREAM, "read dqbuf err %d", ret); - return ret; - } - - /* if the process slept for more than 1 second, - * get a newer frame */ - frame = &gspca_dev->frame[v4l2_buf.index]; - if (--n < 0) - break; /* avoid infinite loop */ - if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec) - break; - ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - PDEBUG(D_STREAM, "read qbuf err %d", ret); - return ret; - } - } - - /* copy the frame */ - if (count > frame->v4l2_buf.bytesused) - count = frame->v4l2_buf.bytesused; - ret = copy_to_user(data, frame->data, count); - if (ret != 0) { - PDEBUG(D_ERR|D_STREAM, - "read cp to user lack %d / %zd", ret, count); - ret = -EFAULT; - goto out; - } - ret = count; -out: - /* in each case, requeue the buffer */ - ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret2 != 0) - return ret2; - return ret; -} - -static struct v4l2_file_operations dev_fops = { - .owner = THIS_MODULE, - .open = dev_open, - .release = dev_close, - .read = dev_read, - .mmap = dev_mmap, - .unlocked_ioctl = video_ioctl2, - .poll = dev_poll, -}; - -static const struct v4l2_ioctl_ops dev_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_streamon = vidioc_streamon, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_querymenu = vidioc_querymenu, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_jpegcomp = vidioc_g_jpegcomp, - .vidioc_s_jpegcomp = vidioc_s_jpegcomp, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_s_parm = vidioc_s_parm, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - .vidioc_enum_frameintervals = vidioc_enum_frameintervals, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, -#endif - .vidioc_g_chip_ident = vidioc_g_chip_ident, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device gspca_template = { - .name = "gspca main driver", - .fops = &dev_fops, - .ioctl_ops = &dev_ioctl_ops, - .release = video_device_release_empty, /* We use v4l2_dev.release */ -}; - -/* initialize the controls */ -static void ctrls_init(struct gspca_dev *gspca_dev) -{ - struct gspca_ctrl *ctrl; - int i; - - for (i = 0, ctrl = gspca_dev->cam.ctrls; - i < gspca_dev->sd_desc->nctrls; - i++, ctrl++) { - ctrl->def = gspca_dev->sd_desc->ctrls[i].qctrl.default_value; - ctrl->val = ctrl->def; - ctrl->min = gspca_dev->sd_desc->ctrls[i].qctrl.minimum; - ctrl->max = gspca_dev->sd_desc->ctrls[i].qctrl.maximum; - } -} - -/* - * probe and create a new gspca device - * - * This function must be called by the sub-driver when it is - * called for probing a new device. - */ -int gspca_dev_probe2(struct usb_interface *intf, - const struct usb_device_id *id, - const struct sd_desc *sd_desc, - int dev_size, - struct module *module) -{ - struct gspca_dev *gspca_dev; - struct usb_device *dev = interface_to_usbdev(intf); - int ret; - - pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n", - sd_desc->name, id->idVendor, id->idProduct); - - /* create the device */ - if (dev_size < sizeof *gspca_dev) - dev_size = sizeof *gspca_dev; - gspca_dev = kzalloc(dev_size, GFP_KERNEL); - if (!gspca_dev) { - pr_err("couldn't kzalloc gspca struct\n"); - return -ENOMEM; - } - gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); - if (!gspca_dev->usb_buf) { - pr_err("out of memory\n"); - ret = -ENOMEM; - goto out; - } - gspca_dev->dev = dev; - gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber; - - /* check if any audio device */ - if (dev->actconfig->desc.bNumInterfaces != 1) { - int i; - struct usb_interface *intf2; - - for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { - intf2 = dev->actconfig->interface[i]; - if (intf2 != NULL - && intf2->altsetting != NULL - && intf2->altsetting->desc.bInterfaceClass == - USB_CLASS_AUDIO) { - gspca_dev->audio = 1; - break; - } - } - } - - gspca_dev->v4l2_dev.release = gspca_release; - ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); - if (ret) - goto out; - gspca_dev->sd_desc = sd_desc; - gspca_dev->nbufread = 2; - gspca_dev->empty_packet = -1; /* don't check the empty packets */ - gspca_dev->vdev = gspca_template; - gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; - video_set_drvdata(&gspca_dev->vdev, gspca_dev); - set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags); - gspca_dev->module = module; - gspca_dev->present = 1; - - mutex_init(&gspca_dev->usb_lock); - gspca_dev->vdev.lock = &gspca_dev->usb_lock; - mutex_init(&gspca_dev->queue_lock); - init_waitqueue_head(&gspca_dev->wq); - - /* configure the subdriver and initialize the USB device */ - ret = sd_desc->config(gspca_dev, id); - if (ret < 0) - goto out; - if (gspca_dev->cam.ctrls != NULL) - ctrls_init(gspca_dev); - ret = sd_desc->init(gspca_dev); - if (ret < 0) - goto out; - if (sd_desc->init_controls) - ret = sd_desc->init_controls(gspca_dev); - if (ret < 0) - goto out; - gspca_set_default_mode(gspca_dev); - - ret = gspca_input_connect(gspca_dev); - if (ret) - goto out; - - /* - * Don't take usb_lock for these ioctls. This improves latency if - * usb_lock is taken for a long time, e.g. when changing a control - * value, and a new frame is ready to be dequeued. - */ - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); - if (!gspca_dev->sd_desc->get_chip_ident) - v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT); -#ifdef CONFIG_VIDEO_ADV_DEBUG - if (!gspca_dev->sd_desc->get_chip_ident || - !gspca_dev->sd_desc->get_register) - v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER); - if (!gspca_dev->sd_desc->get_chip_ident || - !gspca_dev->sd_desc->set_register) - v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER); -#endif - if (!gspca_dev->sd_desc->get_jcomp) - v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP); - if (!gspca_dev->sd_desc->set_jcomp) - v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP); - - /* init video stuff */ - ret = video_register_device(&gspca_dev->vdev, - VFL_TYPE_GRABBER, - -1); - if (ret < 0) { - pr_err("video_register_device err %d\n", ret); - goto out; - } - - usb_set_intfdata(intf, gspca_dev); - PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev)); - - gspca_input_create_urb(gspca_dev); - - return 0; -out: -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - if (gspca_dev->input_dev) - input_unregister_device(gspca_dev->input_dev); -#endif - v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); - kfree(gspca_dev->usb_buf); - kfree(gspca_dev); - return ret; -} -EXPORT_SYMBOL(gspca_dev_probe2); - -/* same function as the previous one, but check the interface */ -int gspca_dev_probe(struct usb_interface *intf, - const struct usb_device_id *id, - const struct sd_desc *sd_desc, - int dev_size, - struct module *module) -{ - struct usb_device *dev = interface_to_usbdev(intf); - - /* we don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) { - pr_err("%04x:%04x too many config\n", - id->idVendor, id->idProduct); - return -ENODEV; - } - - /* the USB video interface must be the first one */ - if (dev->actconfig->desc.bNumInterfaces != 1 - && intf->cur_altsetting->desc.bInterfaceNumber != 0) - return -ENODEV; - - return gspca_dev_probe2(intf, id, sd_desc, dev_size, module); -} -EXPORT_SYMBOL(gspca_dev_probe); - -/* - * USB disconnection - * - * This function must be called by the sub-driver - * when the device disconnects, after the specific resources are freed. - */ -void gspca_disconnect(struct usb_interface *intf) -{ - struct gspca_dev *gspca_dev = usb_get_intfdata(intf); -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - struct input_dev *input_dev; -#endif - - PDEBUG(D_PROBE, "%s disconnect", - video_device_node_name(&gspca_dev->vdev)); - - mutex_lock(&gspca_dev->usb_lock); - - usb_set_intfdata(intf, NULL); - gspca_dev->dev = NULL; - gspca_dev->present = 0; - destroy_urbs(gspca_dev); - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - gspca_input_destroy_urb(gspca_dev); - input_dev = gspca_dev->input_dev; - if (input_dev) { - gspca_dev->input_dev = NULL; - input_unregister_device(input_dev); - } -#endif - /* Free subdriver's streaming resources / stop sd workqueue(s) */ - if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) - gspca_dev->sd_desc->stop0(gspca_dev); - gspca_dev->streaming = 0; - wake_up_interruptible(&gspca_dev->wq); - - v4l2_device_disconnect(&gspca_dev->v4l2_dev); - video_unregister_device(&gspca_dev->vdev); - - mutex_unlock(&gspca_dev->usb_lock); - - /* (this will call gspca_release() immediately or on last close) */ - v4l2_device_put(&gspca_dev->v4l2_dev); -} -EXPORT_SYMBOL(gspca_disconnect); - -#ifdef CONFIG_PM -int gspca_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - - if (!gspca_dev->streaming) - return 0; - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->frozen = 1; /* avoid urb error messages */ - gspca_dev->usb_err = 0; - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - if (gspca_dev->sd_desc->stop0) - gspca_dev->sd_desc->stop0(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - return 0; -} -EXPORT_SYMBOL(gspca_suspend); - -int gspca_resume(struct usb_interface *intf) -{ - struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - int streaming, ret = 0; - - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->frozen = 0; - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->init(gspca_dev); - gspca_input_create_urb(gspca_dev); - /* - * Most subdrivers send all ctrl values on sd_start and thus - * only write to the device registers on s_ctrl when streaming -> - * Clear streaming to avoid setting all ctrls twice. - */ - streaming = gspca_dev->streaming; - gspca_dev->streaming = 0; - if (streaming) - ret = gspca_init_transfer(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - return ret; -} -EXPORT_SYMBOL(gspca_resume); -#endif - -/* -- module insert / remove -- */ -static int __init gspca_init(void) -{ - pr_info("v" GSPCA_VERSION " registered\n"); - return 0; -} -static void __exit gspca_exit(void) -{ -} - -module_init(gspca_init); -module_exit(gspca_exit); - -#ifdef GSPCA_DEBUG -module_param_named(debug, gspca_debug, int, 0644); -MODULE_PARM_DESC(debug, - "Debug (bit) 0x01:error 0x02:probe 0x04:config" - " 0x08:stream 0x10:frame 0x20:packet" - " 0x0100: v4l2"); -#endif diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h deleted file mode 100644 index dc688c7f5e48..000000000000 --- a/drivers/media/video/gspca/gspca.h +++ /dev/null @@ -1,259 +0,0 @@ -#ifndef GSPCAV2_H -#define GSPCAV2_H - -#include -#include -#include -#include -#include -#include -#include -#include - -/* compilation option */ -/*#define GSPCA_DEBUG 1*/ - -#ifdef GSPCA_DEBUG -/* GSPCA our debug messages */ -extern int gspca_debug; -#define PDEBUG(level, fmt, ...) \ -do { \ - if (gspca_debug & (level)) \ - pr_info(fmt, ##__VA_ARGS__); \ -} while (0) - -#define D_ERR 0x01 -#define D_PROBE 0x02 -#define D_CONF 0x04 -#define D_STREAM 0x08 -#define D_FRAM 0x10 -#define D_PACK 0x20 -#define D_USBI 0x00 -#define D_USBO 0x00 -#define D_V4L2 0x0100 -#else -#define PDEBUG(level, fmt, ...) -#endif - -#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ -/* image transfers */ -#define MAX_NURBS 4 /* max number of URBs */ - - -/* used to list framerates supported by a camera mode (resolution) */ -struct framerates { - const u8 *rates; - int nrates; -}; - -/* control definition */ -struct gspca_ctrl { - s16 val; /* current value */ - s16 def; /* default value */ - s16 min, max; /* minimum and maximum values */ -}; - -/* device information - set at probe time */ -struct cam { - const struct v4l2_pix_format *cam_mode; /* size nmodes */ - const struct framerates *mode_framerates; /* must have size nmodes, - * just like cam_mode */ - struct gspca_ctrl *ctrls; /* control table - size nctrls */ - /* may be NULL */ - u32 bulk_size; /* buffer size when image transfer by bulk */ - u32 input_flags; /* value for ENUM_INPUT status flags */ - u8 nmodes; /* size of cam_mode */ - u8 no_urb_create; /* don't create transfer URBs */ - u8 bulk_nurbs; /* number of URBs in bulk mode - * - cannot be > MAX_NURBS - * - when 0 and bulk_size != 0 means - * 1 URB and submit done by subdriver */ - u8 bulk; /* image transfer by 0:isoc / 1:bulk */ - u8 npkt; /* number of packets in an ISOC message - * 0 is the default value: 32 packets */ - u8 needs_full_bandwidth;/* Set this flag to notify the bandwidth calc. - * code that the cam fills all image buffers to - * the max, even when using compression. */ -}; - -struct gspca_dev; -struct gspca_frame; - -/* subdriver operations */ -typedef int (*cam_op) (struct gspca_dev *); -typedef void (*cam_v_op) (struct gspca_dev *); -typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); -typedef int (*cam_jpg_op) (struct gspca_dev *, - struct v4l2_jpegcompression *); -typedef int (*cam_reg_op) (struct gspca_dev *, - struct v4l2_dbg_register *); -typedef int (*cam_ident_op) (struct gspca_dev *, - struct v4l2_dbg_chip_ident *); -typedef void (*cam_streamparm_op) (struct gspca_dev *, - struct v4l2_streamparm *); -typedef int (*cam_qmnu_op) (struct gspca_dev *, - struct v4l2_querymenu *); -typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, - u8 *data, - int len); -typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev, - u8 *data, - int len); - -struct ctrl { - struct v4l2_queryctrl qctrl; - int (*set)(struct gspca_dev *, __s32); - int (*get)(struct gspca_dev *, __s32 *); - cam_v_op set_control; -}; - -/* subdriver description */ -struct sd_desc { -/* information */ - const char *name; /* sub-driver name */ -/* controls */ - const struct ctrl *ctrls; /* static control definition */ - int nctrls; -/* mandatory operations */ - cam_cf_op config; /* called on probe */ - cam_op init; /* called on probe and resume */ - cam_op init_controls; /* called on probe */ - cam_op start; /* called on stream on after URBs creation */ - cam_pkt_op pkt_scan; -/* optional operations */ - cam_op isoc_init; /* called on stream on before getting the EP */ - cam_op isoc_nego; /* called when URB submit failed with NOSPC */ - cam_v_op stopN; /* called on stream off - main alt */ - cam_v_op stop0; /* called on stream off & disconnect - alt 0 */ - cam_v_op dq_callback; /* called when a frame has been dequeued */ - cam_jpg_op get_jcomp; - cam_jpg_op set_jcomp; - cam_qmnu_op querymenu; - cam_streamparm_op get_streamparm; - cam_streamparm_op set_streamparm; -#ifdef CONFIG_VIDEO_ADV_DEBUG - cam_reg_op set_register; - cam_reg_op get_register; -#endif - cam_ident_op get_chip_ident; -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - cam_int_pkt_op int_pkt_scan; - /* other_input makes the gspca core create gspca_dev->input even when - int_pkt_scan is NULL, for cams with non interrupt driven buttons */ - u8 other_input; -#endif -}; - -/* packet types when moving from iso buf to frame buf */ -enum gspca_packet_type { - DISCARD_PACKET, - FIRST_PACKET, - INTER_PACKET, - LAST_PACKET -}; - -struct gspca_frame { - __u8 *data; /* frame buffer */ - int vma_use_count; - struct v4l2_buffer v4l2_buf; -}; - -struct gspca_dev { - struct video_device vdev; /* !! must be the first item */ - struct module *module; /* subdriver handling the device */ - struct v4l2_device v4l2_dev; - struct usb_device *dev; - struct file *capt_file; /* file doing video capture */ - /* protected by queue_lock */ -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - struct input_dev *input_dev; - char phys[64]; /* physical device path */ -#endif - - struct cam cam; /* device information */ - const struct sd_desc *sd_desc; /* subdriver description */ - unsigned ctrl_dis; /* disabled controls (bit map) */ - unsigned ctrl_inac; /* inactive controls (bit map) */ - struct v4l2_ctrl_handler ctrl_handler; - - /* autogain and exposure or gain control cluster, these are global as - the autogain/exposure functions in autogain_functions.c use them */ - struct { - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; - int exp_too_low_cnt, exp_too_high_cnt; - }; - -#define USB_BUF_SZ 64 - __u8 *usb_buf; /* buffer for USB exchanges */ - struct urb *urb[MAX_NURBS]; -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - struct urb *int_urb; -#endif - - __u8 *frbuf; /* buffer for nframes */ - struct gspca_frame frame[GSPCA_MAX_FRAMES]; - u8 *image; /* image beeing filled */ - __u32 frsz; /* frame size */ - u32 image_len; /* current length of image */ - atomic_t fr_q; /* next frame to queue */ - atomic_t fr_i; /* frame being filled */ - signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ - char nframes; /* number of frames */ - u8 fr_o; /* next frame to dequeue */ - __u8 last_packet_type; - __s8 empty_packet; /* if (-1) don't check empty packets */ - __u8 streaming; /* protected by both mutexes (*) */ - - __u8 curr_mode; /* current camera mode */ - __u32 pixfmt; /* current mode parameters */ - __u16 width; - __u16 height; - __u32 sequence; /* frame sequence number */ - - wait_queue_head_t wq; /* wait queue */ - struct mutex usb_lock; /* usb exchange protection */ - struct mutex queue_lock; /* ISOC queue protection */ - int usb_err; /* USB error - protected by usb_lock */ - u16 pkt_size; /* ISOC packet size */ -#ifdef CONFIG_PM - char frozen; /* suspend - resume */ -#endif - char present; /* device connected */ - char nbufread; /* number of buffers for read() */ - char memory; /* memory type (V4L2_MEMORY_xxx) */ - __u8 iface; /* USB interface number */ - __u8 alt; /* USB alternate setting */ - u8 audio; /* presence of audio device */ - - /* (*) These variables are proteced by both usb_lock and queue_lock, - that is any code setting them is holding *both*, which means that - any code getting them needs to hold at least one of them */ -}; - -int gspca_dev_probe(struct usb_interface *intf, - const struct usb_device_id *id, - const struct sd_desc *sd_desc, - int dev_size, - struct module *module); -int gspca_dev_probe2(struct usb_interface *intf, - const struct usb_device_id *id, - const struct sd_desc *sd_desc, - int dev_size, - struct module *module); -void gspca_disconnect(struct usb_interface *intf); -void gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - const u8 *data, - int len); -#ifdef CONFIG_PM -int gspca_suspend(struct usb_interface *intf, pm_message_t message); -int gspca_resume(struct usb_interface *intf); -#endif -int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum, - int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); -int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, - int avg_lum, int desired_avg_lum, int deadzone); - -#endif /* GSPCAV2_H */ diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c deleted file mode 100644 index 26b99310d628..000000000000 --- a/drivers/media/video/gspca/jeilinj.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Jeilinj subdriver - * - * Supports some Jeilin dual-mode cameras which use bulk transport and - * download raw JPEG data. - * - * Copyright (C) 2009 Theodore Kilgore - * - * Sportscam DV15 support and control settings are - * Copyright (C) 2011 Patrice Chotard - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "jeilinj" - -#include -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Theodore Kilgore "); -MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* Default timeouts, in ms */ -#define JEILINJ_CMD_TIMEOUT 500 -#define JEILINJ_CMD_DELAY 160 -#define JEILINJ_DATA_TIMEOUT 1000 - -/* Maximum transfer size to use. */ -#define JEILINJ_MAX_TRANSFER 0x200 -#define FRAME_HEADER_LEN 0x10 -#define FRAME_START 0xFFFFFFFF - -enum { - SAKAR_57379, - SPORTSCAM_DV15, -}; - -#define CAMQUALITY_MIN 0 /* highest cam quality */ -#define CAMQUALITY_MAX 97 /* lowest cam quality */ - -/* Structure to hold all of our device specific stuff */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - int blocks_left; - const struct v4l2_pix_format *cap_mode; - struct v4l2_ctrl *freq; - struct v4l2_ctrl *jpegqual; - /* Driver stuff */ - u8 type; - u8 quality; /* image quality */ -#define QUALITY_MIN 35 -#define QUALITY_MAX 85 -#define QUALITY_DEF 85 - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -struct jlj_command { - unsigned char instruction[2]; - unsigned char ack_wanted; - unsigned char delay; -}; - -/* AFAICT these cameras will only do 320x240. */ -static struct v4l2_pix_format jlj_mode[] = { - { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, - { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0} -}; - -/* - * cam uses endpoint 0x03 to send commands, 0x84 for read commands, - * and 0x82 for bulk transfer. - */ - -/* All commands are two bytes only */ -static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) -{ - int retval; - - if (gspca_dev->usb_err < 0) - return; - memcpy(gspca_dev->usb_buf, command, 2); - retval = usb_bulk_msg(gspca_dev->dev, - usb_sndbulkpipe(gspca_dev->dev, 3), - gspca_dev->usb_buf, 2, NULL, 500); - if (retval < 0) { - pr_err("command write [%02x] error %d\n", - gspca_dev->usb_buf[0], retval); - gspca_dev->usb_err = retval; - } -} - -/* Responses are one byte only */ -static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) -{ - int retval; - - if (gspca_dev->usb_err < 0) - return; - retval = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x84), - gspca_dev->usb_buf, 1, NULL, 500); - response = gspca_dev->usb_buf[0]; - if (retval < 0) { - pr_err("read command [%02x] error %d\n", - gspca_dev->usb_buf[0], retval); - gspca_dev->usb_err = retval; - } -} - -static void setfreq(struct gspca_dev *gspca_dev, s32 val) -{ - u8 freq_commands[][2] = { - {0x71, 0x80}, - {0x70, 0x07} - }; - - freq_commands[0][1] |= val >> 1; - - jlj_write2(gspca_dev, freq_commands[0]); - jlj_write2(gspca_dev, freq_commands[1]); -} - -static void setcamquality(struct gspca_dev *gspca_dev, s32 val) -{ - u8 quality_commands[][2] = { - {0x71, 0x1E}, - {0x70, 0x06} - }; - u8 camquality; - - /* adapt camera quality from jpeg quality */ - camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX) - / (QUALITY_MAX - QUALITY_MIN); - quality_commands[0][1] += camquality; - - jlj_write2(gspca_dev, quality_commands[0]); - jlj_write2(gspca_dev, quality_commands[1]); -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 val) -{ - u8 autogain_commands[][2] = { - {0x94, 0x02}, - {0xcf, 0x00} - }; - - autogain_commands[1][1] = val << 4; - - jlj_write2(gspca_dev, autogain_commands[0]); - jlj_write2(gspca_dev, autogain_commands[1]); -} - -static void setred(struct gspca_dev *gspca_dev, s32 val) -{ - u8 setred_commands[][2] = { - {0x94, 0x02}, - {0xe6, 0x00} - }; - - setred_commands[1][1] = val; - - jlj_write2(gspca_dev, setred_commands[0]); - jlj_write2(gspca_dev, setred_commands[1]); -} - -static void setgreen(struct gspca_dev *gspca_dev, s32 val) -{ - u8 setgreen_commands[][2] = { - {0x94, 0x02}, - {0xe7, 0x00} - }; - - setgreen_commands[1][1] = val; - - jlj_write2(gspca_dev, setgreen_commands[0]); - jlj_write2(gspca_dev, setgreen_commands[1]); -} - -static void setblue(struct gspca_dev *gspca_dev, s32 val) -{ - u8 setblue_commands[][2] = { - {0x94, 0x02}, - {0xe9, 0x00} - }; - - setblue_commands[1][1] = val; - - jlj_write2(gspca_dev, setblue_commands[0]); - jlj_write2(gspca_dev, setblue_commands[1]); -} - -static int jlj_start(struct gspca_dev *gspca_dev) -{ - int i; - int start_commands_size; - u8 response = 0xff; - struct sd *sd = (struct sd *) gspca_dev; - struct jlj_command start_commands[] = { - {{0x71, 0x81}, 0, 0}, - {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY}, - {{0x95, 0x70}, 1, 0}, - {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0}, - {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY}, - {{0x95, 0x70}, 1, 0}, - {{0x71, 0x00}, 0, 0}, /* start streaming ??*/ - {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY}, - {{0x95, 0x70}, 1, 0}, -#define SPORTSCAM_DV15_CMD_SIZE 9 - {{0x94, 0x02}, 0, 0}, - {{0xde, 0x24}, 0, 0}, - {{0x94, 0x02}, 0, 0}, - {{0xdd, 0xf0}, 0, 0}, - {{0x94, 0x02}, 0, 0}, - {{0xe3, 0x2c}, 0, 0}, - {{0x94, 0x02}, 0, 0}, - {{0xe4, 0x00}, 0, 0}, - {{0x94, 0x02}, 0, 0}, - {{0xe5, 0x00}, 0, 0}, - {{0x94, 0x02}, 0, 0}, - {{0xe6, 0x2c}, 0, 0}, - {{0x94, 0x03}, 0, 0}, - {{0xaa, 0x00}, 0, 0} - }; - - sd->blocks_left = 0; - /* Under Windows, USB spy shows that only the 9 first start - * commands are used for SPORTSCAM_DV15 webcam - */ - if (sd->type == SPORTSCAM_DV15) - start_commands_size = SPORTSCAM_DV15_CMD_SIZE; - else - start_commands_size = ARRAY_SIZE(start_commands); - - for (i = 0; i < start_commands_size; i++) { - jlj_write2(gspca_dev, start_commands[i].instruction); - if (start_commands[i].delay) - msleep(start_commands[i].delay); - if (start_commands[i].ack_wanted) - jlj_read1(gspca_dev, response); - } - setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); - msleep(2); - setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); - if (gspca_dev->usb_err < 0) - PDEBUG(D_ERR, "Start streaming command failed"); - return gspca_dev->usb_err; -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - int packet_type; - u32 header_marker; - - PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0", - len, JEILINJ_MAX_TRANSFER); - if (len != JEILINJ_MAX_TRANSFER) { - PDEBUG(D_PACK, "bad length"); - goto discard; - } - /* check if it's start of frame */ - header_marker = ((u32 *)data)[0]; - if (header_marker == FRAME_START) { - sd->blocks_left = data[0x0a] - 1; - PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left); - /* Start a new frame, and add the JPEG header, first thing */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - /* Toss line 0 of data block 0, keep the rest. */ - gspca_frame_add(gspca_dev, INTER_PACKET, - data + FRAME_HEADER_LEN, - JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); - } else if (sd->blocks_left > 0) { - PDEBUG(D_STREAM, "%d blocks remaining for frame", - sd->blocks_left); - sd->blocks_left -= 1; - if (sd->blocks_left == 0) - packet_type = LAST_PACKET; - else - packet_type = INTER_PACKET; - gspca_frame_add(gspca_dev, packet_type, - data, JEILINJ_MAX_TRANSFER); - } else - goto discard; - return; -discard: - /* Discard data until a new frame starts. */ - gspca_dev->last_packet_type = DISCARD_PACKET; -} - -/* This function is called at probe time just before sd_init */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam = &gspca_dev->cam; - struct sd *dev = (struct sd *) gspca_dev; - - dev->type = id->driver_info; - dev->quality = QUALITY_DEF; - - cam->cam_mode = jlj_mode; - cam->nmodes = ARRAY_SIZE(jlj_mode); - cam->bulk = 1; - cam->bulk_nurbs = 1; - cam->bulk_size = JEILINJ_MAX_TRANSFER; - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - int i; - u8 *buf; - static u8 stop_commands[][2] = { - {0x71, 0x00}, - {0x70, 0x09}, - {0x71, 0x80}, - {0x70, 0x05} - }; - - for (;;) { - /* get the image remaining blocks */ - usb_bulk_msg(gspca_dev->dev, - gspca_dev->urb[0]->pipe, - gspca_dev->urb[0]->transfer_buffer, - JEILINJ_MAX_TRANSFER, NULL, - JEILINJ_DATA_TIMEOUT); - - /* search for 0xff 0xd9 (EOF for JPEG) */ - i = 0; - buf = gspca_dev->urb[0]->transfer_buffer; - while ((i < (JEILINJ_MAX_TRANSFER - 1)) && - ((buf[i] != 0xff) || (buf[i+1] != 0xd9))) - i++; - - if (i != (JEILINJ_MAX_TRANSFER - 1)) - /* last remaining block found */ - break; - } - - for (i = 0; i < ARRAY_SIZE(stop_commands); i++) - jlj_write2(gspca_dev, stop_commands[i]); -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return gspca_dev->usb_err; -} - -/* Set up for getting frames. */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *dev = (struct sd *) gspca_dev; - - /* create the JPEG header */ - jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x21); /* JPEG 422 */ - jpeg_set_qual(dev->jpeg_hdr, dev->quality); - PDEBUG(D_STREAM, "Start streaming at %dx%d", - gspca_dev->height, gspca_dev->width); - jlj_start(gspca_dev); - return gspca_dev->usb_err; -} - -/* Table of supported USB devices */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379}, - {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_POWER_LINE_FREQUENCY: - setfreq(gspca_dev, ctrl->val); - break; - case V4L2_CID_RED_BALANCE: - setred(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - setgreen(gspca_dev, ctrl->val); - break; - case V4L2_CID_BLUE_BALANCE: - setblue(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - setautogain(gspca_dev, ctrl->val); - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - jpeg_set_qual(sd->jpeg_hdr, ctrl->val); - setcamquality(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - static const struct v4l2_ctrl_config custom_autogain = { - .ops = &sd_ctrl_ops, - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Automatic Gain (and Exposure)", - .max = 3, - .step = 1, - .def = 0, - }; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 6); - sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ); - v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 3, 1, 2); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 3, 1, 2); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2); - sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - - v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); - return 0; -} - -static int sd_get_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - - memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); - jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT - | V4L2_JPEG_MARKER_DQT; - return 0; -} - - -/* sub-driver description */ -static const struct sd_desc sd_desc_sakar_57379 = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* sub-driver description */ -static const struct sd_desc sd_desc_sportscam_dv15 = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .get_jcomp = sd_get_jcomp, - .set_jcomp = sd_set_jcomp, -}; - -static const struct sd_desc *sd_desc[2] = { - &sd_desc_sakar_57379, - &sd_desc_sportscam_dv15 -}; - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, - sd_desc[id->driver_info], - sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c deleted file mode 100644 index cf9d9fca5b84..000000000000 --- a/drivers/media/video/gspca/jl2005bcd.c +++ /dev/null @@ -1,557 +0,0 @@ -/* - * Jeilin JL2005B/C/D library - * - * Copyright (C) 2011 Theodore Kilgore - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define MODULE_NAME "jl2005bcd" - -#include -#include -#include "gspca.h" - - -MODULE_AUTHOR("Theodore Kilgore "); -MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* Default timeouts, in ms */ -#define JL2005C_CMD_TIMEOUT 500 -#define JL2005C_DATA_TIMEOUT 1000 - -/* Maximum transfer size to use. */ -#define JL2005C_MAX_TRANSFER 0x200 -#define FRAME_HEADER_LEN 16 - - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char firmware_id[6]; - const struct v4l2_pix_format *cap_mode; - /* Driver stuff */ - struct work_struct work_struct; - struct workqueue_struct *work_thread; - u8 frame_brightness; - int block_size; /* block size of camera */ - int vga; /* 1 if vga cam, 0 if cif cam */ -}; - - -/* Camera has two resolution settings. What they are depends on model. */ -static const struct v4l2_pix_format cif_mode[] = { - {176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, - {352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, - {640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/* - * cam uses endpoint 0x03 to send commands, 0x84 for read commands, - * and 0x82 for bulk data transfer. - */ - -/* All commands are two bytes only */ -static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command) -{ - int retval; - - memcpy(gspca_dev->usb_buf, command, 2); - retval = usb_bulk_msg(gspca_dev->dev, - usb_sndbulkpipe(gspca_dev->dev, 3), - gspca_dev->usb_buf, 2, NULL, 500); - if (retval < 0) - pr_err("command write [%02x] error %d\n", - gspca_dev->usb_buf[0], retval); - return retval; -} - -/* Response to a command is one byte in usb_buf[0], only if requested. */ -static int jl2005c_read1(struct gspca_dev *gspca_dev) -{ - int retval; - - retval = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x84), - gspca_dev->usb_buf, 1, NULL, 500); - if (retval < 0) - pr_err("read command [0x%02x] error %d\n", - gspca_dev->usb_buf[0], retval); - return retval; -} - -/* Response appears in gspca_dev->usb_buf[0] */ -static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg) -{ - int retval; - - static u8 instruction[2] = {0x95, 0x00}; - /* put register to read in byte 1 */ - instruction[1] = reg; - /* Send the read request */ - retval = jl2005c_write2(gspca_dev, instruction); - if (retval < 0) - return retval; - retval = jl2005c_read1(gspca_dev); - - return retval; -} - -static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev) -{ - int i; - int retval; - int frame_brightness = 0; - - static u8 instruction[2] = {0x7f, 0x01}; - - retval = jl2005c_write2(gspca_dev, instruction); - if (retval < 0) - return retval; - - i = 0; - while (i < 20 && !frame_brightness) { - /* If we tried 20 times, give up. */ - retval = jl2005c_read_reg(gspca_dev, 0x7e); - if (retval < 0) - return retval; - frame_brightness = gspca_dev->usb_buf[0]; - retval = jl2005c_read_reg(gspca_dev, 0x7d); - if (retval < 0) - return retval; - i++; - } - PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]); - return retval; -} - -static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg, - unsigned char value) -{ - int retval; - u8 instruction[2]; - - instruction[0] = reg; - instruction[1] = value; - - retval = jl2005c_write2(gspca_dev, instruction); - if (retval < 0) - return retval; - - return retval; -} - -static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - int i = 0; - int retval = -1; - unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f}; - - PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id"); - /* Read the first ID byte once for warmup */ - retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]); - PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]); - if (retval < 0) - return retval; - /* Now actually get the ID string */ - for (i = 0; i < 6; i++) { - retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]); - if (retval < 0) - return retval; - sd->firmware_id[i] = gspca_dev->usb_buf[0]; - } - PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x", - sd->firmware_id[0], - sd->firmware_id[1], - sd->firmware_id[2], - sd->firmware_id[3], - sd->firmware_id[4], - sd->firmware_id[5]); - return 0; -} - -static int jl2005c_stream_start_vga_lg - (struct gspca_dev *gspca_dev) -{ - int i; - int retval = -1; - static u8 instruction[][2] = { - {0x05, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x18}, - {0x02, 0x00}, - {0x01, 0x00}, - {0x04, 0x52}, - }; - - for (i = 0; i < ARRAY_SIZE(instruction); i++) { - msleep(60); - retval = jl2005c_write2(gspca_dev, instruction[i]); - if (retval < 0) - return retval; - } - msleep(60); - return retval; -} - -static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev) -{ - int i; - int retval = -1; - static u8 instruction[][2] = { - {0x06, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x1a}, - {0x02, 0x00}, - {0x01, 0x00}, - {0x04, 0x52}, - }; - - for (i = 0; i < ARRAY_SIZE(instruction); i++) { - msleep(60); - retval = jl2005c_write2(gspca_dev, instruction[i]); - if (retval < 0) - return retval; - } - msleep(60); - return retval; -} - -static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev) -{ - int i; - int retval = -1; - static u8 instruction[][2] = { - {0x05, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x30}, - {0x02, 0x00}, - {0x01, 0x00}, - {0x04, 0x42}, - }; - - for (i = 0; i < ARRAY_SIZE(instruction); i++) { - msleep(60); - retval = jl2005c_write2(gspca_dev, instruction[i]); - if (retval < 0) - return retval; - } - msleep(60); - return retval; -} - -static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev) -{ - int i; - int retval = -1; - static u8 instruction[][2] = { - {0x06, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x32}, - {0x02, 0x00}, - {0x01, 0x00}, - {0x04, 0x42}, - }; - - for (i = 0; i < ARRAY_SIZE(instruction); i++) { - msleep(60); - retval = jl2005c_write2(gspca_dev, instruction[i]); - if (retval < 0) - return retval; - } - msleep(60); - return retval; -} - - -static int jl2005c_stop(struct gspca_dev *gspca_dev) -{ - int retval; - - retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00); - return retval; -} - -/* This function is called as a workqueue function and runs whenever the camera - * is streaming data. Because it is a workqueue function it is allowed to sleep - * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface the gspca usb_lock is - * used when performing the one USB control operation inside the workqueue, - * which tells the camera to close the stream. In practice the only thing - * which needs to be protected against is the usb_set_interface call that - * gspca makes during stream_off. Otherwise the camera doesn't provide any - * controls that the user could try to change. - */ -static void jl2005c_dostream(struct work_struct *work) -{ - struct sd *dev = container_of(work, struct sd, work_struct); - struct gspca_dev *gspca_dev = &dev->gspca_dev; - int bytes_left = 0; /* bytes remaining in current frame. */ - int data_len; /* size to use for the next read. */ - int header_read = 0; - unsigned char header_sig[2] = {0x4a, 0x4c}; - int act_len; - int packet_type; - int ret; - u8 *buffer; - - buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); - if (!buffer) { - pr_err("Couldn't allocate USB buffer\n"); - goto quit_stream; - } - - while (gspca_dev->dev && gspca_dev->streaming) { -#ifdef CONFIG_PM - if (gspca_dev->frozen) - break; -#endif - /* Check if this is a new frame. If so, start the frame first */ - if (!header_read) { - mutex_lock(&gspca_dev->usb_lock); - ret = jl2005c_start_new_frame(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - if (ret < 0) - goto quit_stream; - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x82), - buffer, JL2005C_MAX_TRANSFER, &act_len, - JL2005C_DATA_TIMEOUT); - PDEBUG(D_PACK, - "Got %d bytes out of %d for header", - act_len, JL2005C_MAX_TRANSFER); - if (ret < 0 || act_len < JL2005C_MAX_TRANSFER) - goto quit_stream; - /* Check whether we actually got the first blodk */ - if (memcmp(header_sig, buffer, 2) != 0) { - pr_err("First block is not the first block\n"); - goto quit_stream; - } - /* total size to fetch is byte 7, times blocksize - * of which we already got act_len */ - bytes_left = buffer[0x07] * dev->block_size - act_len; - PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left); - /* We keep the header. It has other information, too.*/ - packet_type = FIRST_PACKET; - gspca_frame_add(gspca_dev, packet_type, - buffer, act_len); - header_read = 1; - } - while (bytes_left > 0 && gspca_dev->dev) { - data_len = bytes_left > JL2005C_MAX_TRANSFER ? - JL2005C_MAX_TRANSFER : bytes_left; - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x82), - buffer, data_len, &act_len, - JL2005C_DATA_TIMEOUT); - if (ret < 0 || act_len < data_len) - goto quit_stream; - PDEBUG(D_PACK, - "Got %d bytes out of %d for frame", - data_len, bytes_left); - bytes_left -= data_len; - if (bytes_left == 0) { - packet_type = LAST_PACKET; - header_read = 0; - } else - packet_type = INTER_PACKET; - gspca_frame_add(gspca_dev, packet_type, - buffer, data_len); - } - } -quit_stream: - if (gspca_dev->dev) { - mutex_lock(&gspca_dev->usb_lock); - jl2005c_stop(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } - kfree(buffer); -} - - - - -/* This function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam; - struct sd *sd = (struct sd *) gspca_dev; - - cam = &gspca_dev->cam; - /* We don't use the buffer gspca allocates so make it small. */ - cam->bulk_size = 64; - cam->bulk = 1; - /* For the rest, the camera needs to be detected */ - jl2005c_get_firmware_id(gspca_dev); - /* Here are some known firmware IDs - * First some JL2005B cameras - * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2} Sakar KidzCam - * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2} No-name JL2005B - * JL2005C cameras - * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8} Argus DC-1512 - * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8} ICarly - * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4} Jazz - * - * Based upon this scanty evidence, we can detect a CIF camera by - * testing byte 0 for 0x4x. - */ - if ((sd->firmware_id[0] & 0xf0) == 0x40) { - cam->cam_mode = cif_mode; - cam->nmodes = ARRAY_SIZE(cif_mode); - sd->block_size = 0x80; - } else { - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - sd->block_size = 0x200; - } - - INIT_WORK(&sd->work_struct, jl2005c_dostream); - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - - struct sd *sd = (struct sd *) gspca_dev; - sd->cap_mode = gspca_dev->cam.cam_mode; - - switch (gspca_dev->width) { - case 640: - PDEBUG(D_STREAM, "Start streaming at vga resolution"); - jl2005c_stream_start_vga_lg(gspca_dev); - break; - case 320: - PDEBUG(D_STREAM, "Start streaming at qvga resolution"); - jl2005c_stream_start_vga_small(gspca_dev); - break; - case 352: - PDEBUG(D_STREAM, "Start streaming at cif resolution"); - jl2005c_stream_start_cif_lg(gspca_dev); - break; - case 176: - PDEBUG(D_STREAM, "Start streaming at qcif resolution"); - jl2005c_stream_start_cif_small(gspca_dev); - break; - default: - pr_err("Unknown resolution specified\n"); - return -1; - } - - /* Start the workqueue function to do the streaming */ - sd->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(sd->work_thread, &sd->work_struct); - - return 0; -} - -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *dev = (struct sd *) gspca_dev; - - /* wait for the work queue to terminate */ - mutex_unlock(&gspca_dev->usb_lock); - /* This waits for sq905c_dostream to finish */ - destroy_workqueue(dev->work_thread); - dev->work_thread = NULL; - mutex_lock(&gspca_dev->usb_lock); -} - - - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stop0 = sd_stop0, -}; - -/* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x0979, 0x0227)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -/* -- module insert / remove -- */ -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h deleted file mode 100644 index ab54910418b4..000000000000 --- a/drivers/media/video/gspca/jpeg.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef JPEG_H -#define JPEG_H 1 -/* - * Insert a JPEG header at start of frame - * - * This module is used by the gspca subdrivers. - * A special case is done for Conexant webcams. - * - * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* - * generation options - * CONEX_CAM Conexant if present - */ - -/* JPEG header */ -static const u8 jpeg_head[] = { - 0xff, 0xd8, /* jpeg */ - -/* quantization table quality 50% */ - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, -#define JPEG_QT0_OFFSET 7 - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, -1, -#define JPEG_QT1_OFFSET 72 - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - -/* huffman table */ - 0xff, 0xc4, 0x01, 0xa2, - 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, - 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, - 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, - 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, - 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, - 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, - 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, - 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, - 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, - 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, - 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, - 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, - 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, - 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, - 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, - 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, - 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, - 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, - 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, - 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, - 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, - 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, - 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, - 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, - 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, -#ifdef CONEX_CAM -/* the Conexant frames start with SOF0 */ -#define JPEG_HDR_SZ 556 -#else - 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ - 0x08, /* data precision */ -#define JPEG_HEIGHT_OFFSET 561 - 0x01, 0xe0, /* height */ - 0x02, 0x80, /* width */ - 0x03, /* component number */ - 0x01, - 0x21, /* samples Y */ - 0x00, /* quant Y */ - 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ - 0x03, 0x11, 0x01, - - 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ - 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -#define JPEG_HDR_SZ 589 -#endif -}; - -/* define the JPEG header */ -static void jpeg_define(u8 *jpeg_hdr, - int height, - int width, - int samplesY) -{ - memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); -#ifndef CONEX_CAM - jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY; -#endif -} - -/* set the JPEG quality */ -static void jpeg_set_qual(u8 *jpeg_hdr, - int quality) -{ - int i, sc; - - if (quality < 50) - sc = 5000 / quality; - else - sc = 200 - quality * 2; - for (i = 0; i < 64; i++) { - jpeg_hdr[JPEG_QT0_OFFSET + i] = - (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; - jpeg_hdr[JPEG_QT1_OFFSET + i] = - (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; - } -} -#endif diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c deleted file mode 100644 index 40ad6687ee5d..000000000000 --- a/drivers/media/video/gspca/kinect.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * kinect sensor device camera, gspca driver - * - * Copyright (C) 2011 Antonio Ospite - * - * Based on the OpenKinect project and libfreenect - * http://openkinect.org/wiki/Init_Analysis - * - * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect - * sensor device which I tested the driver on. - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "kinect" - -#include "gspca.h" - -#define CTRL_TIMEOUT 500 - -MODULE_AUTHOR("Antonio Ospite "); -MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); -MODULE_LICENSE("GPL"); - -struct pkt_hdr { - uint8_t magic[2]; - uint8_t pad; - uint8_t flag; - uint8_t unk1; - uint8_t seq; - uint8_t unk2; - uint8_t unk3; - uint32_t timestamp; -}; - -struct cam_hdr { - uint8_t magic[2]; - uint16_t len; - uint16_t cmd; - uint16_t tag; -}; - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - uint16_t cam_tag; /* a sequence number for packets */ - uint8_t stream_flag; /* to identify different stream types */ - uint8_t obuf[0x400]; /* output buffer for control commands */ - uint8_t ibuf[0x200]; /* input buffer for control commands */ -}; - -#define MODE_640x480 0x0001 -#define MODE_640x488 0x0002 -#define MODE_1280x1024 0x0004 - -#define FORMAT_BAYER 0x0010 -#define FORMAT_UYVY 0x0020 -#define FORMAT_Y10B 0x0040 - -#define FPS_HIGH 0x0100 - -static const struct v4l2_pix_format video_camera_mode[] = { - {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH}, - {640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = MODE_640x480 | FORMAT_UYVY}, - {1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 1024, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = MODE_1280x1024 | FORMAT_BAYER}, - {640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, - .bytesperline = 640 * 10 / 8, - .sizeimage = 640 * 488 * 10 / 8, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH}, - {1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, - .bytesperline = 1280 * 10 / 8, - .sizeimage = 1280 * 1024 * 10 / 8, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = MODE_1280x1024 | FORMAT_Y10B}, -}; - -static int kinect_write(struct usb_device *udev, uint8_t *data, - uint16_t wLength) -{ - return usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - 0x00, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, data, wLength, CTRL_TIMEOUT); -} - -static int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength) -{ - return usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, data, wLength, CTRL_TIMEOUT); -} - -static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, - unsigned int cmd_len, void *replybuf, unsigned int reply_len) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *udev = gspca_dev->dev; - int res, actual_len; - uint8_t *obuf = sd->obuf; - uint8_t *ibuf = sd->ibuf; - struct cam_hdr *chdr = (void *)obuf; - struct cam_hdr *rhdr = (void *)ibuf; - - if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) { - pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len); - return -1; - } - - chdr->magic[0] = 0x47; - chdr->magic[1] = 0x4d; - chdr->cmd = cpu_to_le16(cmd); - chdr->tag = cpu_to_le16(sd->cam_tag); - chdr->len = cpu_to_le16(cmd_len / 2); - - memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len); - - res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr)); - PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd, - sd->cam_tag, cmd_len, res); - if (res < 0) { - pr_err("send_cmd: Output control transfer failed (%d)\n", res); - return res; - } - - do { - actual_len = kinect_read(udev, ibuf, 0x200); - } while (actual_len == 0); - PDEBUG(D_USBO, "Control reply: %d", res); - if (actual_len < sizeof(*rhdr)) { - pr_err("send_cmd: Input control transfer failed (%d)\n", res); - return res; - } - actual_len -= sizeof(*rhdr); - - if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) { - pr_err("send_cmd: Bad magic %02x %02x\n", - rhdr->magic[0], rhdr->magic[1]); - return -1; - } - if (rhdr->cmd != chdr->cmd) { - pr_err("send_cmd: Bad cmd %02x != %02x\n", - rhdr->cmd, chdr->cmd); - return -1; - } - if (rhdr->tag != chdr->tag) { - pr_err("send_cmd: Bad tag %04x != %04x\n", - rhdr->tag, chdr->tag); - return -1; - } - if (cpu_to_le16(rhdr->len) != (actual_len/2)) { - pr_err("send_cmd: Bad len %04x != %04x\n", - cpu_to_le16(rhdr->len), (int)(actual_len/2)); - return -1; - } - - if (actual_len > reply_len) { - pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n", - reply_len, actual_len); - memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len); - } else { - memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len); - } - - sd->cam_tag++; - - return actual_len; -} - -static int write_register(struct gspca_dev *gspca_dev, uint16_t reg, - uint16_t data) -{ - uint16_t reply[2]; - uint16_t cmd[2]; - int res; - - cmd[0] = cpu_to_le16(reg); - cmd[1] = cpu_to_le16(data); - - PDEBUG(D_USBO, "Write Reg 0x%04x <= 0x%02x", reg, data); - res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4); - if (res < 0) - return res; - if (res != 2) { - pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n", - res, reply[0], reply[1]); - } - return 0; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - sd->cam_tag = 0; - - /* Only video stream is supported for now, - * which has stream flag = 0x80 */ - sd->stream_flag = 0x80; - - cam = &gspca_dev->cam; - - cam->cam_mode = video_camera_mode; - cam->nmodes = ARRAY_SIZE(video_camera_mode); - -#if 0 - /* Setting those values is not needed for video stream */ - cam->npkt = 15; - gspca_dev->pkt_size = 960 * 2; -#endif - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - PDEBUG(D_PROBE, "Kinect Camera device."); - - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - int mode; - uint8_t fmt_reg, fmt_val; - uint8_t res_reg, res_val; - uint8_t fps_reg, fps_val; - uint8_t mode_val; - - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - - if (mode & FORMAT_Y10B) { - fmt_reg = 0x19; - res_reg = 0x1a; - fps_reg = 0x1b; - mode_val = 0x03; - } else { - fmt_reg = 0x0c; - res_reg = 0x0d; - fps_reg = 0x0e; - mode_val = 0x01; - } - - /* format */ - if (mode & FORMAT_UYVY) - fmt_val = 0x05; - else - fmt_val = 0x00; - - if (mode & MODE_1280x1024) - res_val = 0x02; - else - res_val = 0x01; - - if (mode & FPS_HIGH) - fps_val = 0x1e; - else - fps_val = 0x0f; - - - /* turn off IR-reset function */ - write_register(gspca_dev, 0x105, 0x00); - - /* Reset video stream */ - write_register(gspca_dev, 0x05, 0x00); - - /* Due to some ridiculous condition in the firmware, we have to start - * and stop the depth stream before the camera will hand us 1280x1024 - * IR. This is a stupid workaround, but we've yet to find a better - * solution. - * - * Thanks to Drew Fisher for figuring this out. - */ - if (mode & (FORMAT_Y10B | MODE_1280x1024)) { - write_register(gspca_dev, 0x13, 0x01); - write_register(gspca_dev, 0x14, 0x1e); - write_register(gspca_dev, 0x06, 0x02); - write_register(gspca_dev, 0x06, 0x00); - } - - write_register(gspca_dev, fmt_reg, fmt_val); - write_register(gspca_dev, res_reg, res_val); - write_register(gspca_dev, fps_reg, fps_val); - - /* Start video stream */ - write_register(gspca_dev, 0x05, mode_val); - - /* disable Hflip */ - write_register(gspca_dev, 0x47, 0x00); - - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - /* reset video stream */ - write_register(gspca_dev, 0x05, 0x00); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - - struct pkt_hdr *hdr = (void *)__data; - uint8_t *data = __data + sizeof(*hdr); - int datalen = len - sizeof(*hdr); - - uint8_t sof = sd->stream_flag | 1; - uint8_t mof = sd->stream_flag | 2; - uint8_t eof = sd->stream_flag | 5; - - if (len < 12) - return; - - if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') { - pr_warn("[Stream %02x] Invalid magic %02x%02x\n", - sd->stream_flag, hdr->magic[0], hdr->magic[1]); - return; - } - - if (hdr->flag == sof) - gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen); - - else if (hdr->flag == mof) - gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen); - - else if (hdr->flag == eof) - gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen); - - else - pr_warn("Packet type not recognized...\n"); -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - /* - .get_streamparm = sd_get_streamparm, - .set_streamparm = sd_set_streamparm, - */ -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x045e, 0x02ae)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c deleted file mode 100644 index bbf91e07e38b..000000000000 --- a/drivers/media/video/gspca/konica.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Driver for USB webcams based on Konica chipset. This - * chipset is used in Intel YC76 camera. - * - * Copyright (C) 2010 Hans de Goede - * - * Based on the usbvideo v4l1 konicawc driver which is: - * - * Copyright (C) 2002 Simon Evans - * - * The code for making gspca work with a webcam with 2 isoc endpoints was - * taken from the benq gspca subdriver which is: - * - * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "konica" - -#include -#include "gspca.h" - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("Konica chipset USB Camera Driver"); -MODULE_LICENSE("GPL"); - -#define WHITEBAL_REG 0x01 -#define BRIGHTNESS_REG 0x02 -#define SHARPNESS_REG 0x03 -#define CONTRAST_REG 0x04 -#define SATURATION_REG 0x05 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct urb *last_data_urb; - u8 snapshot_pressed; -}; - - -/* .priv is what goes to register 8 for this mode, known working values: - 0x00 -> 176x144, cropped - 0x01 -> 176x144, cropped - 0x02 -> 176x144, cropped - 0x03 -> 176x144, cropped - 0x04 -> 176x144, binned - 0x05 -> 320x240 - 0x06 -> 320x240 - 0x07 -> 160x120, cropped - 0x08 -> 160x120, cropped - 0x09 -> 160x120, binned (note has 136 lines) - 0x0a -> 160x120, binned (note has 136 lines) - 0x0b -> 160x120, cropped -*/ -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 136 * 3 / 2 + 960, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0x0a}, - {176, 144, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 2 + 960, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0x04}, - {320, 240, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 2 + 960, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0x05}, -}; - -static void sd_isoc_irq(struct urb *urb); - -static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x02, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - NULL, - 0, - 1000); - if (ret < 0) { - pr_err("reg_w err writing %02x to %02x: %d\n", - value, index, ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x03, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - gspca_dev->usb_buf, - 2, - 1000); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void konica_stream_on(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 1, 0x0b); -} - -static void konica_stream_off(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 0, 0x0b); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - gspca_dev->cam.cam_mode = vga_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->cam.no_urb_create = 1; - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - int i; - - /* - * The konica needs a freaking large time to "boot" (approx 6.5 sec.), - * and does not want to be bothered while doing so :| - * Register 0x10 counts from 1 - 3, with 3 being "ready" - */ - msleep(6000); - for (i = 0; i < 20; i++) { - reg_r(gspca_dev, 0, 0x10); - if (gspca_dev->usb_buf[0] == 3) - break; - msleep(100); - } - reg_w(gspca_dev, 0, 0x0d); - - return gspca_dev->usb_err; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct urb *urb; - int i, n, packet_size; - struct usb_host_interface *alt; - struct usb_interface *intf; - - intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); - alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); - if (!alt) { - pr_err("Couldn't get altsetting\n"); - return -EIO; - } - - packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - - n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - reg_w(gspca_dev, n, 0x08); - - konica_stream_on(gspca_dev); - - if (gspca_dev->usb_err) - return gspca_dev->usb_err; - - /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */ -#if MAX_NURBS < 4 -#error "Not enough URBs in the gspca table" -#endif -#define SD_NPKT 32 - for (n = 0; n < 4; n++) { - i = n & 1 ? 0 : 1; - packet_size = - le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize); - urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); - if (!urb) { - pr_err("usb_alloc_urb failed\n"); - return -ENOMEM; - } - gspca_dev->urb[n] = urb; - urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, - packet_size * SD_NPKT, - GFP_KERNEL, - &urb->transfer_dma); - if (urb->transfer_buffer == NULL) { - pr_err("usb_buffer_alloc failed\n"); - return -ENOMEM; - } - - urb->dev = gspca_dev->dev; - urb->context = gspca_dev; - urb->transfer_buffer_length = packet_size * SD_NPKT; - urb->pipe = usb_rcvisocpipe(gspca_dev->dev, - n & 1 ? 0x81 : 0x82); - urb->transfer_flags = URB_ISO_ASAP - | URB_NO_TRANSFER_DMA_MAP; - urb->interval = 1; - urb->complete = sd_isoc_irq; - urb->number_of_packets = SD_NPKT; - for (i = 0; i < SD_NPKT; i++) { - urb->iso_frame_desc[i].length = packet_size; - urb->iso_frame_desc[i].offset = packet_size * i; - } - } - - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - konica_stream_off(gspca_dev); -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - /* Don't keep the button in the pressed state "forever" if it was - pressed when streaming is stopped */ - if (sd->snapshot_pressed) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - sd->snapshot_pressed = 0; - } -#endif -} - -/* reception of an URB */ -static void sd_isoc_irq(struct urb *urb) -{ - struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - struct sd *sd = (struct sd *) gspca_dev; - struct urb *data_urb, *status_urb; - u8 *data; - int i, st; - - PDEBUG(D_PACK, "sd isoc irq"); - if (!gspca_dev->streaming) - return; - - if (urb->status != 0) { - if (urb->status == -ESHUTDOWN) - return; /* disconnection */ -#ifdef CONFIG_PM - if (gspca_dev->frozen) - return; -#endif - PDEBUG(D_ERR, "urb status: %d", urb->status); - st = usb_submit_urb(urb, GFP_ATOMIC); - if (st < 0) - pr_err("resubmit urb error %d\n", st); - return; - } - - /* if this is a data URB (ep 0x82), wait */ - if (urb->transfer_buffer_length > 32) { - sd->last_data_urb = urb; - return; - } - - status_urb = urb; - data_urb = sd->last_data_urb; - sd->last_data_urb = NULL; - - if (!data_urb || data_urb->start_frame != status_urb->start_frame) { - PDEBUG(D_ERR|D_PACK, "lost sync on frames"); - goto resubmit; - } - - if (data_urb->number_of_packets != status_urb->number_of_packets) { - PDEBUG(D_ERR|D_PACK, - "no packets does not match, data: %d, status: %d", - data_urb->number_of_packets, - status_urb->number_of_packets); - goto resubmit; - } - - for (i = 0; i < status_urb->number_of_packets; i++) { - if (data_urb->iso_frame_desc[i].status || - status_urb->iso_frame_desc[i].status) { - PDEBUG(D_ERR|D_PACK, - "pkt %d data-status %d, status-status %d", i, - data_urb->iso_frame_desc[i].status, - status_urb->iso_frame_desc[i].status); - gspca_dev->last_packet_type = DISCARD_PACKET; - continue; - } - - if (status_urb->iso_frame_desc[i].actual_length != 1) { - PDEBUG(D_ERR|D_PACK, - "bad status packet length %d", - status_urb->iso_frame_desc[i].actual_length); - gspca_dev->last_packet_type = DISCARD_PACKET; - continue; - } - - st = *((u8 *)status_urb->transfer_buffer - + status_urb->iso_frame_desc[i].offset); - - data = (u8 *)data_urb->transfer_buffer - + data_urb->iso_frame_desc[i].offset; - - /* st: 0x80-0xff: frame start with frame number (ie 0-7f) - * otherwise: - * bit 0 0: keep packet - * 1: drop packet (padding data) - * - * bit 4 0 button not clicked - * 1 button clicked - * button is used to `take a picture' (in software) - */ - if (st & 0x80) { - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - } else { -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - u8 button_state = st & 0x40 ? 1 : 0; - if (sd->snapshot_pressed != button_state) { - input_report_key(gspca_dev->input_dev, - KEY_CAMERA, - button_state); - input_sync(gspca_dev->input_dev); - sd->snapshot_pressed = button_state; - } -#endif - if (st & 0x01) - continue; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, - data_urb->iso_frame_desc[i].actual_length); - } - -resubmit: - if (data_urb) { - st = usb_submit_urb(data_urb, GFP_ATOMIC); - if (st < 0) - PDEBUG(D_ERR|D_PACK, - "usb_submit_urb(data_urb) ret %d", st); - } - st = usb_submit_urb(status_urb, GFP_ATOMIC); - if (st < 0) - pr_err("usb_submit_urb(status_urb) ret %d\n", st); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, BRIGHTNESS_REG); - konica_stream_on(gspca_dev); - break; - case V4L2_CID_CONTRAST: - konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, CONTRAST_REG); - konica_stream_on(gspca_dev); - break; - case V4L2_CID_SATURATION: - konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, SATURATION_REG); - konica_stream_on(gspca_dev); - break; - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: - konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, WHITEBAL_REG); - konica_stream_on(gspca_dev); - break; - case V4L2_CID_SHARPNESS: - konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, SHARPNESS_REG); - konica_stream_on(gspca_dev); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 9, 1, 4); - /* Needs to be verified */ - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 9, 1, 4); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 9, 1, 4); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_WHITE_BALANCE_TEMPERATURE, - 0, 33, 1, 25); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 9, 1, 4); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .other_input = 1, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x04c8, 0x0720)}, /* Intel YC 76 */ - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/m5602/Kconfig b/drivers/media/video/gspca/m5602/Kconfig deleted file mode 100644 index 5a69016ed75f..000000000000 --- a/drivers/media/video/gspca/m5602/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -config USB_M5602 - tristate "ALi USB m5602 Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the - ALi m5602 connected to various image sensors. - - See for more info. - - To compile this driver as a module, choose M here: the - module will be called gspca_m5602. diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile deleted file mode 100644 index 575b75bacb62..000000000000 --- a/drivers/media/video/gspca/m5602/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -obj-$(CONFIG_USB_M5602) += gspca_m5602.o - -gspca_m5602-objs := m5602_core.o \ - m5602_ov9650.o \ - m5602_ov7660.o \ - m5602_mt9m111.o \ - m5602_po1030.o \ - m5602_s5k83a.o \ - m5602_s5k4aa.o - -ccflags-y += -I$(srctree)/drivers/media/video/gspca diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h deleted file mode 100644 index 51af3ee3ab85..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_bridge.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * USB Driver for ALi m5602 based webcams - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_BRIDGE_H_ -#define M5602_BRIDGE_H_ - -#include -#include "gspca.h" - -#define MODULE_NAME "ALi m5602" - -/*****************************************************************************/ - -#define M5602_XB_SENSOR_TYPE 0x00 -#define M5602_XB_SENSOR_CTRL 0x01 -#define M5602_XB_LINE_OF_FRAME_H 0x02 -#define M5602_XB_LINE_OF_FRAME_L 0x03 -#define M5602_XB_PIX_OF_LINE_H 0x04 -#define M5602_XB_PIX_OF_LINE_L 0x05 -#define M5602_XB_VSYNC_PARA 0x06 -#define M5602_XB_HSYNC_PARA 0x07 -#define M5602_XB_TEST_MODE_1 0x08 -#define M5602_XB_TEST_MODE_2 0x09 -#define M5602_XB_SIG_INI 0x0a -#define M5602_XB_DS_PARA 0x0e -#define M5602_XB_TRIG_PARA 0x0f -#define M5602_XB_CLK_PD 0x10 -#define M5602_XB_MCU_CLK_CTRL 0x12 -#define M5602_XB_MCU_CLK_DIV 0x13 -#define M5602_XB_SEN_CLK_CTRL 0x14 -#define M5602_XB_SEN_CLK_DIV 0x15 -#define M5602_XB_AUD_CLK_CTRL 0x16 -#define M5602_XB_AUD_CLK_DIV 0x17 -#define M5602_OB_AC_LINK_STATE 0x22 -#define M5602_OB_PCM_SLOT_INDEX 0x24 -#define M5602_OB_GPIO_SLOT_INDEX 0x25 -#define M5602_OB_ACRX_STATUS_ADDRESS_H 0x28 -#define M5602_OB_ACRX_STATUS_DATA_L 0x29 -#define M5602_OB_ACRX_STATUS_DATA_H 0x2a -#define M5602_OB_ACTX_COMMAND_ADDRESS 0x31 -#define M5602_OB_ACRX_COMMAND_DATA_L 0x32 -#define M5602_OB_ACTX_COMMAND_DATA_H 0X33 -#define M5602_XB_DEVCTR1 0x41 -#define M5602_XB_EPSETR0 0x42 -#define M5602_XB_EPAFCTR 0x47 -#define M5602_XB_EPBFCTR 0x49 -#define M5602_XB_EPEFCTR 0x4f -#define M5602_XB_TEST_REG 0x53 -#define M5602_XB_ALT2SIZE 0x54 -#define M5602_XB_ALT3SIZE 0x55 -#define M5602_XB_OBSFRAME 0x56 -#define M5602_XB_PWR_CTL 0x59 -#define M5602_XB_ADC_CTRL 0x60 -#define M5602_XB_ADC_DATA 0x61 -#define M5602_XB_MISC_CTRL 0x62 -#define M5602_XB_SNAPSHOT 0x63 -#define M5602_XB_SCRATCH_1 0x64 -#define M5602_XB_SCRATCH_2 0x65 -#define M5602_XB_SCRATCH_3 0x66 -#define M5602_XB_SCRATCH_4 0x67 -#define M5602_XB_I2C_CTRL 0x68 -#define M5602_XB_I2C_CLK_DIV 0x69 -#define M5602_XB_I2C_DEV_ADDR 0x6a -#define M5602_XB_I2C_REG_ADDR 0x6b -#define M5602_XB_I2C_DATA 0x6c -#define M5602_XB_I2C_STATUS 0x6d -#define M5602_XB_GPIO_DAT_H 0x70 -#define M5602_XB_GPIO_DAT_L 0x71 -#define M5602_XB_GPIO_DIR_H 0x72 -#define M5602_XB_GPIO_DIR_L 0x73 -#define M5602_XB_GPIO_EN_H 0x74 -#define M5602_XB_GPIO_EN_L 0x75 -#define M5602_XB_GPIO_DAT 0x76 -#define M5602_XB_GPIO_DIR 0x77 -#define M5602_XB_SEN_CLK_CONTROL 0x80 -#define M5602_XB_SEN_CLK_DIVISION 0x81 -#define M5602_XB_CPR_CLK_CONTROL 0x82 -#define M5602_XB_CPR_CLK_DIVISION 0x83 -#define M5602_XB_MCU_CLK_CONTROL 0x84 -#define M5602_XB_MCU_CLK_DIVISION 0x85 -#define M5602_XB_DCT_CLK_CONTROL 0x86 -#define M5602_XB_DCT_CLK_DIVISION 0x87 -#define M5602_XB_EC_CLK_CONTROL 0x88 -#define M5602_XB_EC_CLK_DIVISION 0x89 -#define M5602_XB_LBUF_CLK_CONTROL 0x8a -#define M5602_XB_LBUF_CLK_DIVISION 0x8b - -#define I2C_BUSY 0x80 - -/*****************************************************************************/ - -/* Driver info */ -#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project" -#define DRIVER_DESC "ALi m5602 webcam driver" - -#define M5602_ISOC_ENDPOINT_ADDR 0x81 -#define M5602_INTR_ENDPOINT_ADDR 0x82 - -#define M5602_URB_MSG_TIMEOUT 5000 - -/*****************************************************************************/ - -/* A skeleton used for sending messages to the m5602 bridge */ -static const unsigned char bridge_urb_skeleton[] = { - 0x13, 0x00, 0x81, 0x00 -}; - -/* A skeleton used for sending messages to the sensor */ -static const unsigned char sensor_urb_skeleton[] = { - 0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06, - 0x23, M5602_XB_MISC_CTRL, 0x81, 0x80, - 0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00, - 0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00, - 0x13, M5602_XB_I2C_DATA, 0x81, 0x00, - 0x13, M5602_XB_I2C_CTRL, 0x81, 0x11 -}; - -struct sd { - struct gspca_dev gspca_dev; - - /* A pointer to the currently connected sensor */ - const struct m5602_sensor *sensor; - - struct sd_desc *desc; - - /* Sensor private data */ - void *sensor_priv; - - /* The current frame's id, used to detect frame boundaries */ - u8 frame_id; - - /* The current frame count */ - u32 frame_count; -}; - -int m5602_read_bridge( - struct sd *sd, const u8 address, u8 *i2c_data); - -int m5602_write_bridge( - struct sd *sd, const u8 address, const u8 i2c_data); - -int m5602_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - -int m5602_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - -#endif diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c deleted file mode 100644 index ed22638978ce..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ /dev/null @@ -1,424 +0,0 @@ - /* - * USB Driver for ALi m5602 based webcams - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "m5602_ov9650.h" -#include "m5602_ov7660.h" -#include "m5602_mt9m111.h" -#include "m5602_po1030.h" -#include "m5602_s5k83a.h" -#include "m5602_s5k4aa.h" - -/* Kernel module parameters */ -int force_sensor; -static bool dump_bridge; -bool dump_sensor; - -static const struct usb_device_id m5602_table[] = { - {USB_DEVICE(0x0402, 0x5602)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, m5602_table); - -/* Reads a byte from the m5602 */ -int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data) -{ - int err; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x04, 0xc0, 0x14, - 0x8100 + address, buf, - 1, M5602_URB_MSG_TIMEOUT); - *i2c_data = buf[0]; - - PDEBUG(D_CONF, "Reading bridge register 0x%x containing 0x%x", - address, *i2c_data); - - /* usb_control_msg(...) returns the number of bytes sent upon success, - mask that and return zero instead*/ - return (err < 0) ? err : 0; -} - -/* Writes a byte to the m5602 */ -int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data) -{ - int err; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - PDEBUG(D_CONF, "Writing bridge register 0x%x with 0x%x", - address, i2c_data); - - memcpy(buf, bridge_urb_skeleton, - sizeof(bridge_urb_skeleton)); - buf[1] = address; - buf[3] = i2c_data; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x19, - 0x0000, buf, - 4, M5602_URB_MSG_TIMEOUT); - - /* usb_control_msg(...) returns the number of bytes sent upon success, - mask that and return zero instead */ - return (err < 0) ? err : 0; -} - -static int m5602_wait_for_i2c(struct sd *sd) -{ - int err; - u8 data; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, &data); - } while ((data & I2C_BUSY) && !err); - return err; -} - -int m5602_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - - if (!len || len > sd->sensor->i2c_regW) - return -EINVAL; - - err = m5602_wait_for_i2c(sd); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, - sd->sensor->i2c_slave_id); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); - if (err < 0) - return err; - - /* Sensors with registers that are of only - one byte width are differently read */ - - /* FIXME: This works with the ov9650, but has issues with the po1030 */ - if (sd->sensor->i2c_regW == 1) { - err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 1); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); - } else { - err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); - } - - for (i = 0; (i < len) && !err; i++) { - err = m5602_wait_for_i2c(sd); - if (err < 0) - return err; - - err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); - - PDEBUG(D_CONF, "Reading sensor register " - "0x%x containing 0x%x ", address, *i2c_data); - } - return err; -} - -int m5602_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - u8 *p; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - /* No sensor with a data width larger than 16 bits has yet been seen */ - if (len > sd->sensor->i2c_regW || !len) - return -EINVAL; - - memcpy(buf, sensor_urb_skeleton, - sizeof(sensor_urb_skeleton)); - - buf[11] = sd->sensor->i2c_slave_id; - buf[15] = address; - - /* Special case larger sensor writes */ - p = buf + 16; - - /* Copy a four byte write sequence for each byte to be written to */ - for (i = 0; i < len; i++) { - memcpy(p, sensor_urb_skeleton + 16, 4); - p[3] = i2c_data[i]; - p += 4; - PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", - address, i2c_data[i]); - } - - /* Copy the tailer */ - memcpy(p, sensor_urb_skeleton + 20, 4); - - /* Set the total length */ - p[3] = 0x10 + len; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x19, - 0x0000, buf, - 20 + len * 4, M5602_URB_MSG_TIMEOUT); - - return (err < 0) ? err : 0; -} - -/* Dump all the registers of the m5602 bridge, - unfortunately this breaks the camera until it's power cycled */ -static void m5602_dump_bridge(struct sd *sd) -{ - int i; - for (i = 0; i < 0x80; i++) { - unsigned char val = 0; - m5602_read_bridge(sd, i, &val); - pr_info("ALi m5602 address 0x%x contains 0x%x\n", i, val); - } - pr_info("Warning: The ALi m5602 webcam probably won't work until it's power cycled\n"); -} - -static int m5602_probe_sensor(struct sd *sd) -{ - /* Try the po1030 */ - sd->sensor = &po1030; - if (!sd->sensor->probe(sd)) - return 0; - - /* Try the mt9m111 sensor */ - sd->sensor = &mt9m111; - if (!sd->sensor->probe(sd)) - return 0; - - /* Try the s5k4aa */ - sd->sensor = &s5k4aa; - if (!sd->sensor->probe(sd)) - return 0; - - /* Try the ov9650 */ - sd->sensor = &ov9650; - if (!sd->sensor->probe(sd)) - return 0; - - /* Try the ov7660 */ - sd->sensor = &ov7660; - if (!sd->sensor->probe(sd)) - return 0; - - /* Try the s5k83a */ - sd->sensor = &s5k83a; - if (!sd->sensor->probe(sd)) - return 0; - - /* More sensor probe function goes here */ - pr_info("Failed to find a sensor\n"); - sd->sensor = NULL; - return -ENODEV; -} - -static int m5602_configure(struct gspca_dev *gspca_dev, - const struct usb_device_id *id); - -static int m5602_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int err; - - PDEBUG(D_CONF, "Initializing ALi m5602 webcam"); - /* Run the init sequence */ - err = sd->sensor->init(sd); - - return err; -} - -static int m5602_start_transfer(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 *buf = sd->gspca_dev.usb_buf; - int err; - - /* Send start command to the camera */ - const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01}; - - if (sd->sensor->start) - sd->sensor->start(sd); - - memcpy(buf, buffer, sizeof(buffer)); - err = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x04, 0x40, 0x19, 0x0000, buf, - sizeof(buffer), M5602_URB_MSG_TIMEOUT); - - PDEBUG(D_STREAM, "Transfer started"); - return (err < 0) ? err : 0; -} - -static void m5602_urb_complete(struct gspca_dev *gspca_dev, - u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (len < 6) { - PDEBUG(D_PACK, "Packet is less than 6 bytes"); - return; - } - - /* Frame delimiter: ff xx xx xx ff ff */ - if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff && - data[2] != sd->frame_id) { - PDEBUG(D_FRAM, "Frame delimiter detected"); - sd->frame_id = data[2]; - - /* Remove the extra fluff appended on each header */ - data += 6; - len -= 6; - - /* Complete the last frame (if any) */ - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - sd->frame_count++; - - /* Create a new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - - PDEBUG(D_FRAM, "Starting new frame %d", - sd->frame_count); - - } else { - int cur_frame_len; - - cur_frame_len = gspca_dev->image_len; - /* Remove urb header */ - data += 4; - len -= 4; - - if (cur_frame_len + len <= gspca_dev->frsz) { - PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes", - sd->frame_count, len); - - gspca_frame_add(gspca_dev, INTER_PACKET, - data, len); - } else { - /* Add the remaining data up to frame size */ - gspca_frame_add(gspca_dev, INTER_PACKET, data, - gspca_dev->frsz - cur_frame_len); - } - } -} - -static void m5602_stop_transfer(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* Run the sensor specific end transfer sequence */ - if (sd->sensor->stop) - sd->sensor->stop(sd); -} - -/* sub-driver description, the ctrl and nctrl is filled at probe time */ -static struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = m5602_configure, - .init = m5602_init, - .start = m5602_start_transfer, - .stopN = m5602_stop_transfer, - .pkt_scan = m5602_urb_complete -}; - -/* this function is called at probe time */ -static int m5602_configure(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - int err; - - cam = &gspca_dev->cam; - sd->desc = &sd_desc; - - if (dump_bridge) - m5602_dump_bridge(sd); - - /* Probe sensor */ - err = m5602_probe_sensor(sd); - if (err) - goto fail; - - return 0; - -fail: - PDEBUG(D_ERR, "ALi m5602 webcam failed"); - cam->cam_mode = NULL; - cam->nmodes = 0; - - return err; -} - -static int m5602_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static void m5602_disconnect(struct usb_interface *intf) -{ - struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor->disconnect) - sd->sensor->disconnect(sd); - - gspca_disconnect(intf); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = m5602_table, - .probe = m5602_probe, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif - .disconnect = m5602_disconnect -}; - -module_usb_driver(sd_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -module_param(force_sensor, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(force_sensor, - "forces detection of a sensor, " - "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, " - "4 = MT9M111, 5 = PO1030, 6 = OV7660"); - -module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); - -module_param(dump_sensor, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers " - "at startup providing a sensor is found"); diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c deleted file mode 100644 index 6268aa24ec5d..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ /dev/null @@ -1,647 +0,0 @@ -/* - * Driver for the mt9m111 sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "m5602_mt9m111.h" - -static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); - -static struct v4l2_pix_format mt9m111_modes[] = { - { - 640, - 480, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = 640 * 480, - .bytesperline = 640, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } -}; - -static const struct ctrl mt9m111_ctrls[] = { -#define VFLIP_IDX 0 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = mt9m111_set_vflip, - .get = mt9m111_get_vflip - }, -#define HFLIP_IDX 1 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = mt9m111_set_hflip, - .get = mt9m111_get_hflip - }, -#define GAIN_IDX 2 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0, - .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, - .step = 1, - .default_value = MT9M111_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_gain, - .get = mt9m111_get_gain - }, -#define AUTO_WHITE_BALANCE_IDX 3 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = mt9m111_set_auto_white_balance, - .get = mt9m111_get_auto_white_balance - }, -#define GREEN_BALANCE_IDX 4 - { - { - .id = M5602_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7ff, - .step = 0x1, - .default_value = MT9M111_GREEN_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_green_balance, - .get = mt9m111_get_green_balance - }, -#define BLUE_BALANCE_IDX 5 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7ff, - .step = 0x1, - .default_value = MT9M111_BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_blue_balance, - .get = mt9m111_get_blue_balance - }, -#define RED_BALANCE_IDX 5 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7ff, - .step = 0x1, - .default_value = MT9M111_RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_red_balance, - .get = mt9m111_get_red_balance - }, -}; - -static void mt9m111_dump_registers(struct sd *sd); - -int mt9m111_probe(struct sd *sd) -{ - u8 data[2] = {0x00, 0x00}; - int i; - s32 *sensor_settings; - - if (force_sensor) { - if (force_sensor == MT9M111_SENSOR) { - pr_info("Forcing a %s sensor\n", mt9m111.name); - goto sensor_found; - } - /* If we want to force another sensor, don't try to probe this - * one */ - return -ENODEV; - } - - PDEBUG(D_PROBE, "Probing for a mt9m111 sensor"); - - /* Do the preinit */ - for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) { - if (preinit_mt9m111[i][0] == BRIDGE) { - m5602_write_bridge(sd, - preinit_mt9m111[i][1], - preinit_mt9m111[i][2]); - } else { - data[0] = preinit_mt9m111[i][2]; - data[1] = preinit_mt9m111[i][3]; - m5602_write_sensor(sd, - preinit_mt9m111[i][1], data, 2); - } - } - - if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2)) - return -ENODEV; - - if ((data[0] == 0x14) && (data[1] == 0x3a)) { - pr_info("Detected a mt9m111 sensor\n"); - goto sensor_found; - } - - return -ENODEV; - -sensor_found: - sensor_settings = kmalloc(ARRAY_SIZE(mt9m111_ctrls) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - - sd->gspca_dev.cam.cam_mode = mt9m111_modes; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes); - sd->desc->ctrls = mt9m111_ctrls; - sd->desc->nctrls = ARRAY_SIZE(mt9m111_ctrls); - - for (i = 0; i < ARRAY_SIZE(mt9m111_ctrls); i++) - sensor_settings[i] = mt9m111_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - - return 0; -} - -int mt9m111_init(struct sd *sd) -{ - int i, err = 0; - s32 *sensor_settings = sd->sensor_priv; - - /* Init the sensor */ - for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) { - u8 data[2]; - - if (init_mt9m111[i][0] == BRIDGE) { - err = m5602_write_bridge(sd, - init_mt9m111[i][1], - init_mt9m111[i][2]); - } else { - data[0] = init_mt9m111[i][2]; - data[1] = init_mt9m111[i][3]; - err = m5602_write_sensor(sd, - init_mt9m111[i][1], data, 2); - } - } - - if (dump_sensor) - mt9m111_dump_registers(sd); - - err = mt9m111_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; - - err = mt9m111_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = mt9m111_set_green_balance(&sd->gspca_dev, - sensor_settings[GREEN_BALANCE_IDX]); - if (err < 0) - return err; - - err = mt9m111_set_blue_balance(&sd->gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); - if (err < 0) - return err; - - err = mt9m111_set_red_balance(&sd->gspca_dev, - sensor_settings[RED_BALANCE_IDX]); - if (err < 0) - return err; - - return mt9m111_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); -} - -int mt9m111_start(struct sd *sd) -{ - int i, err = 0; - u8 data[2]; - struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; - - int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1; - int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; - - for (i = 0; i < ARRAY_SIZE(start_mt9m111) && !err; i++) { - if (start_mt9m111[i][0] == BRIDGE) { - err = m5602_write_bridge(sd, - start_mt9m111[i][1], - start_mt9m111[i][2]); - } else { - data[0] = start_mt9m111[i][2]; - data[1] = start_mt9m111[i][3]; - err = m5602_write_sensor(sd, - start_mt9m111[i][1], data, 2); - } - } - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); - if (err < 0) - return err; - - for (i = 0; i < 2 && !err; i++) - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); - if (err < 0) - return err; - - for (i = 0; i < 2 && !err; i++) - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, - (width >> 8) & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, width & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); - if (err < 0) - return err; - - switch (width) { - case 640: - PDEBUG(D_V4L2, "Configuring camera for VGA mode"); - data[0] = MT9M111_RMB_OVER_SIZED; - data[1] = MT9M111_RMB_ROW_SKIP_2X | - MT9M111_RMB_COLUMN_SKIP_2X | - (sensor_settings[VFLIP_IDX] << 0) | - (sensor_settings[HFLIP_IDX] << 1); - - err = m5602_write_sensor(sd, - MT9M111_SC_R_MODE_CONTEXT_B, data, 2); - break; - - case 320: - PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); - data[0] = MT9M111_RMB_OVER_SIZED; - data[1] = MT9M111_RMB_ROW_SKIP_4X | - MT9M111_RMB_COLUMN_SKIP_4X | - (sensor_settings[VFLIP_IDX] << 0) | - (sensor_settings[HFLIP_IDX] << 1); - err = m5602_write_sensor(sd, - MT9M111_SC_R_MODE_CONTEXT_B, data, 2); - break; - } - return err; -} - -void mt9m111_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 data[2] = {0x00, 0x00}; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - - sensor_settings[VFLIP_IDX] = val; - - /* The mt9m111 is flipped by default */ - val = !val; - - /* Set the correct page map */ - err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); - if (err < 0) - return err; - - data[1] = (data[1] & 0xfe) | val; - err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, - data, 2); - return err; -} - -static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return 0; -} - -static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 data[2] = {0x00, 0x00}; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - - sensor_settings[HFLIP_IDX] = val; - - /* The mt9m111 is flipped by default */ - val = !val; - - /* Set the correct page map */ - err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); - if (err < 0) - return err; - - data[1] = (data[1] & 0xfd) | ((val << 1) & 0x02); - err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, - data, 2); - return err; -} - -static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); - - return 0; -} - -static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - int err; - u8 data[2]; - - err = m5602_read_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2); - if (err < 0) - return err; - - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val & 0x01; - data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1)); - - err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2); - - PDEBUG(D_V4L2, "Set auto white balance %d", val); - return err; -} - -static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) { - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read auto white balance %d", *val); - return 0; -} - -static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err, tmp; - u8 data[2] = {0x00, 0x00}; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[GAIN_IDX] = val; - - /* Set the correct page map */ - err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); - if (err < 0) - return err; - - if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2) - return -EINVAL; - - if ((val >= INITIAL_MAX_GAIN * 2 * 2) && - (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2)) - tmp = (1 << 10) | (val << 9) | - (val << 8) | (val / 8); - else if ((val >= INITIAL_MAX_GAIN * 2) && - (val < INITIAL_MAX_GAIN * 2 * 2)) - tmp = (1 << 9) | (1 << 8) | (val / 4); - else if ((val >= INITIAL_MAX_GAIN) && - (val < INITIAL_MAX_GAIN * 2)) - tmp = (1 << 8) | (val / 2); - else - tmp = val; - - data[1] = (tmp & 0xff); - data[0] = (tmp & 0xff00) >> 8; - PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp, - data[1], data[0]); - - err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN, - data, 2); - - return err; -} - -static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 data[2]; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[GREEN_BALANCE_IDX] = val; - data[1] = (val & 0xff); - data[0] = (val & 0xff00) >> 8; - - PDEBUG(D_V4L2, "Set green balance %d", val); - err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN, - data, 2); - if (err < 0) - return err; - - return m5602_write_sensor(sd, MT9M111_SC_GREEN_2_GAIN, - data, 2); -} - -static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GREEN_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read green balance %d", *val); - return 0; -} - -static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - u8 data[2]; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[BLUE_BALANCE_IDX] = val; - data[1] = (val & 0xff); - data[0] = (val & 0xff00) >> 8; - - PDEBUG(D_V4L2, "Set blue balance %d", val); - - return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN, - data, 2); -} - -static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read blue balance %d", *val); - return 0; -} - -static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - u8 data[2]; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[RED_BALANCE_IDX] = val; - data[1] = (val & 0xff); - data[0] = (val & 0xff00) >> 8; - - PDEBUG(D_V4L2, "Set red balance %d", val); - - return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN, - data, 2); -} - -static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read red balance %d", *val); - return 0; -} - -static void mt9m111_dump_registers(struct sd *sd) -{ - u8 address, value[2] = {0x00, 0x00}; - - pr_info("Dumping the mt9m111 register state\n"); - - pr_info("Dumping the mt9m111 sensor core registers\n"); - value[1] = MT9M111_SENSOR_CORE; - m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); - for (address = 0; address < 0xff; address++) { - m5602_read_sensor(sd, address, value, 2); - pr_info("register 0x%x contains 0x%x%x\n", - address, value[0], value[1]); - } - - pr_info("Dumping the mt9m111 color pipeline registers\n"); - value[1] = MT9M111_COLORPIPE; - m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); - for (address = 0; address < 0xff; address++) { - m5602_read_sensor(sd, address, value, 2); - pr_info("register 0x%x contains 0x%x%x\n", - address, value[0], value[1]); - } - - pr_info("Dumping the mt9m111 camera control registers\n"); - value[1] = MT9M111_CAMERA_CONTROL; - m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); - for (address = 0; address < 0xff; address++) { - m5602_read_sensor(sd, address, value, 2); - pr_info("register 0x%x contains 0x%x%x\n", - address, value[0], value[1]); - } - - pr_info("mt9m111 register state dump complete\n"); -} diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h deleted file mode 100644 index 8c672b5c8c6a..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.h +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Driver for the mt9m111 sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * Some defines taken from the mt9m111 sensor driver - * Copyright (C) 2008, Robert Jarzmik - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_MT9M111_H_ -#define M5602_MT9M111_H_ - -#include "m5602_sensor.h" - -/*****************************************************************************/ - -#define MT9M111_SC_CHIPVER 0x00 -#define MT9M111_SC_ROWSTART 0x01 -#define MT9M111_SC_COLSTART 0x02 -#define MT9M111_SC_WINDOW_HEIGHT 0x03 -#define MT9M111_SC_WINDOW_WIDTH 0x04 -#define MT9M111_SC_HBLANK_CONTEXT_B 0x05 -#define MT9M111_SC_VBLANK_CONTEXT_B 0x06 -#define MT9M111_SC_HBLANK_CONTEXT_A 0x07 -#define MT9M111_SC_VBLANK_CONTEXT_A 0x08 -#define MT9M111_SC_SHUTTER_WIDTH 0x09 -#define MT9M111_SC_ROW_SPEED 0x0a -#define MT9M111_SC_EXTRA_DELAY 0x0b -#define MT9M111_SC_SHUTTER_DELAY 0x0c -#define MT9M111_SC_RESET 0x0d -#define MT9M111_SC_R_MODE_CONTEXT_B 0x20 -#define MT9M111_SC_R_MODE_CONTEXT_A 0x21 -#define MT9M111_SC_FLASH_CONTROL 0x23 -#define MT9M111_SC_GREEN_1_GAIN 0x2b -#define MT9M111_SC_BLUE_GAIN 0x2c -#define MT9M111_SC_RED_GAIN 0x2d -#define MT9M111_SC_GREEN_2_GAIN 0x2e -#define MT9M111_SC_GLOBAL_GAIN 0x2f - -#define MT9M111_CONTEXT_CONTROL 0xc8 -#define MT9M111_PAGE_MAP 0xf0 -#define MT9M111_BYTEWISE_ADDRESS 0xf1 - -#define MT9M111_CP_OPERATING_MODE_CTL 0x06 -#define MT9M111_CP_LUMA_OFFSET 0x34 -#define MT9M111_CP_LUMA_CLIP 0x35 -#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a -#define MT9M111_CP_LENS_CORRECTION_1 0x3b -#define MT9M111_CP_DEFECT_CORR_CONTEXT_A 0x4c -#define MT9M111_CP_DEFECT_CORR_CONTEXT_B 0x4d -#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b -#define MT9M111_CP_GLOBAL_CLK_CONTROL 0xb3 - -#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18 0x65 -#define MT9M111_CC_AWB_PARAMETER_7 0x28 - -#define MT9M111_SENSOR_CORE 0x00 -#define MT9M111_COLORPIPE 0x01 -#define MT9M111_CAMERA_CONTROL 0x02 - -#define MT9M111_RESET (1 << 0) -#define MT9M111_RESTART (1 << 1) -#define MT9M111_ANALOG_STANDBY (1 << 2) -#define MT9M111_CHIP_ENABLE (1 << 3) -#define MT9M111_CHIP_DISABLE (0 << 3) -#define MT9M111_OUTPUT_DISABLE (1 << 4) -#define MT9M111_SHOW_BAD_FRAMES (1 << 0) -#define MT9M111_RESTART_BAD_FRAMES (1 << 1) -#define MT9M111_SYNCHRONIZE_CHANGES (1 << 7) - -#define MT9M111_RMB_OVER_SIZED (1 << 0) -#define MT9M111_RMB_MIRROR_ROWS (1 << 0) -#define MT9M111_RMB_MIRROR_COLS (1 << 1) -#define MT9M111_RMB_ROW_SKIP_2X (1 << 2) -#define MT9M111_RMB_COLUMN_SKIP_2X (1 << 3) -#define MT9M111_RMB_ROW_SKIP_4X (1 << 4) -#define MT9M111_RMB_COLUMN_SKIP_4X (1 << 5) - -#define MT9M111_COLOR_MATRIX_BYPASS (1 << 4) -#define MT9M111_SEL_CONTEXT_B (1 << 3) - -#define MT9M111_TRISTATE_PIN_IN_STANDBY (1 << 1) -#define MT9M111_SOC_SOFT_STANDBY (1 << 0) - -#define MT9M111_2D_DEFECT_CORRECTION_ENABLE (1 << 0) - -#define INITIAL_MAX_GAIN 64 -#define MT9M111_DEFAULT_GAIN 283 -#define MT9M111_GREEN_GAIN_DEFAULT 0x20 -#define MT9M111_BLUE_GAIN_DEFAULT 0x20 -#define MT9M111_RED_GAIN_DEFAULT 0x20 - -/*****************************************************************************/ - -/* Kernel module parameters */ -extern int force_sensor; -extern bool dump_sensor; - -int mt9m111_probe(struct sd *sd); -int mt9m111_init(struct sd *sd); -int mt9m111_start(struct sd *sd); -void mt9m111_disconnect(struct sd *sd); - -static const struct m5602_sensor mt9m111 = { - .name = "MT9M111", - - .i2c_slave_id = 0xba, - .i2c_regW = 2, - - .probe = mt9m111_probe, - .init = mt9m111_init, - .disconnect = mt9m111_disconnect, - .start = mt9m111_start, -}; - -static const unsigned char preinit_mt9m111[][4] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, - MT9M111_RESET | - MT9M111_RESTART | - MT9M111_ANALOG_STANDBY | - MT9M111_CHIP_DISABLE, - MT9M111_SHOW_BAD_FRAMES | - MT9M111_RESTART_BAD_FRAMES | - MT9M111_SYNCHRONIZE_CHANGES}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00} -}; - -static const unsigned char init_mt9m111[][4] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, - - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, - MT9M111_CP_OPERATING_MODE_CTL}, - {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, - MT9M111_2D_DEFECT_CORRECTION_ENABLE}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, - MT9M111_2D_DEFECT_CORRECTION_ENABLE}, - {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, - {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, - {SENSOR, 0xcd, 0x00, 0x0e}, - {SENSOR, 0xd0, 0x00, 0x40}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, - {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, 0x33, 0x03, 0x49}, - {SENSOR, 0x34, 0xc0, 0x19}, - {SENSOR, 0x3f, 0x20, 0x20}, - {SENSOR, 0x40, 0x20, 0x20}, - {SENSOR, 0x5a, 0xc0, 0x0a}, - {SENSOR, 0x70, 0x7b, 0x0a}, - {SENSOR, 0x71, 0xff, 0x00}, - {SENSOR, 0x72, 0x19, 0x0e}, - {SENSOR, 0x73, 0x18, 0x0f}, - {SENSOR, 0x74, 0x57, 0x32}, - {SENSOR, 0x75, 0x56, 0x34}, - {SENSOR, 0x76, 0x73, 0x35}, - {SENSOR, 0x77, 0x30, 0x12}, - {SENSOR, 0x78, 0x79, 0x02}, - {SENSOR, 0x79, 0x75, 0x06}, - {SENSOR, 0x7a, 0x77, 0x0a}, - {SENSOR, 0x7b, 0x78, 0x09}, - {SENSOR, 0x7c, 0x7d, 0x06}, - {SENSOR, 0x7d, 0x31, 0x10}, - {SENSOR, 0x7e, 0x00, 0x7e}, - {SENSOR, 0x80, 0x59, 0x04}, - {SENSOR, 0x81, 0x59, 0x04}, - {SENSOR, 0x82, 0x57, 0x0a}, - {SENSOR, 0x83, 0x58, 0x0b}, - {SENSOR, 0x84, 0x47, 0x0c}, - {SENSOR, 0x85, 0x48, 0x0e}, - {SENSOR, 0x86, 0x5b, 0x02}, - {SENSOR, 0x87, 0x00, 0x5c}, - {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B}, - {SENSOR, 0x60, 0x00, 0x80}, - {SENSOR, 0x61, 0x00, 0x00}, - {SENSOR, 0x62, 0x00, 0x00}, - {SENSOR, 0x63, 0x00, 0x00}, - {SENSOR, 0x64, 0x00, 0x00}, - - {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */ - {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */ - {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */ - {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */ - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */ - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */ - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */ - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */ - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */ - {SENSOR, 0x30, 0x04, 0x00}, - /* Set number of blank rows chosen to 400 */ - {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90}, -}; - -static const unsigned char start_mt9m111[][4] = { - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, -}; -#endif diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c deleted file mode 100644 index 9a14835c128f..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_ov7660.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Driver for the ov7660 sensor - * - * Copyright (C) 2009 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "m5602_ov7660.h" - -static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl ov7660_ctrls[] = { -#define GAIN_IDX 1 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = OV7660_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov7660_set_gain, - .get = ov7660_get_gain - }, -#define BLUE_BALANCE_IDX 2 -#define RED_BALANCE_IDX 3 -#define AUTO_WHITE_BALANCE_IDX 4 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov7660_set_auto_white_balance, - .get = ov7660_get_auto_white_balance - }, -#define AUTO_GAIN_CTRL_IDX 5 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto gain control", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov7660_set_auto_gain, - .get = ov7660_get_auto_gain - }, -#define AUTO_EXPOSURE_IDX 6 - { - { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov7660_set_auto_exposure, - .get = ov7660_get_auto_exposure - }, -#define HFLIP_IDX 7 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov7660_set_hflip, - .get = ov7660_get_hflip - }, -#define VFLIP_IDX 8 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov7660_set_vflip, - .get = ov7660_get_vflip - }, - -}; - -static struct v4l2_pix_format ov7660_modes[] = { - { - 640, - 480, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 640 * 480, - .bytesperline = 640, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } -}; - -static void ov7660_dump_registers(struct sd *sd); - -int ov7660_probe(struct sd *sd) -{ - int err = 0, i; - u8 prod_id = 0, ver_id = 0; - - s32 *sensor_settings; - - if (force_sensor) { - if (force_sensor == OV7660_SENSOR) { - pr_info("Forcing an %s sensor\n", ov7660.name); - goto sensor_found; - } - /* If we want to force another sensor, - don't try to probe this one */ - return -ENODEV; - } - - /* Do the preinit */ - for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) { - u8 data[2]; - - if (preinit_ov7660[i][0] == BRIDGE) { - err = m5602_write_bridge(sd, - preinit_ov7660[i][1], - preinit_ov7660[i][2]); - } else { - data[0] = preinit_ov7660[i][2]; - err = m5602_write_sensor(sd, - preinit_ov7660[i][1], data, 1); - } - } - if (err < 0) - return err; - - if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1)) - return -ENODEV; - - if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1)) - return -ENODEV; - - pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id); - - if ((prod_id == 0x76) && (ver_id == 0x60)) { - pr_info("Detected a ov7660 sensor\n"); - goto sensor_found; - } - return -ENODEV; - -sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - - sd->gspca_dev.cam.cam_mode = ov7660_modes; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes); - sd->desc->ctrls = ov7660_ctrls; - sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls); - - for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++) - sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - - return 0; -} - -int ov7660_init(struct sd *sd) -{ - int i, err = 0; - s32 *sensor_settings = sd->sensor_priv; - - /* Init the sensor */ - for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) { - u8 data[2]; - - if (init_ov7660[i][0] == BRIDGE) { - err = m5602_write_bridge(sd, - init_ov7660[i][1], - init_ov7660[i][2]); - } else { - data[0] = init_ov7660[i][2]; - err = m5602_write_sensor(sd, - init_ov7660[i][1], data, 1); - } - } - - if (dump_sensor) - ov7660_dump_registers(sd); - - err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = ov7660_set_auto_white_balance(&sd->gspca_dev, - sensor_settings[AUTO_WHITE_BALANCE_IDX]); - if (err < 0) - return err; - - err = ov7660_set_auto_gain(&sd->gspca_dev, - sensor_settings[AUTO_GAIN_CTRL_IDX]); - if (err < 0) - return err; - - err = ov7660_set_auto_exposure(&sd->gspca_dev, - sensor_settings[AUTO_EXPOSURE_IDX]); - if (err < 0) - return err; - err = ov7660_set_hflip(&sd->gspca_dev, - sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = ov7660_set_vflip(&sd->gspca_dev, - sensor_settings[VFLIP_IDX]); - - return err; -} - -int ov7660_start(struct sd *sd) -{ - return 0; -} - -int ov7660_stop(struct sd *sd) -{ - return 0; -} - -void ov7660_disconnect(struct sd *sd) -{ - ov7660_stop(sd); - - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); - return 0; -} - -static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Setting gain to %d", val); - - sensor_settings[GAIN_IDX] = val; - - err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1); - return err; -} - - -static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - return 0; -} - -static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set auto white balance to %d", val); - - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; - err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); - err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); - - return err; -} - -static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; - PDEBUG(D_V4L2, "Read auto gain control %d", *val); - return 0; -} - -static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set auto gain control to %d", val); - - sensor_settings[AUTO_GAIN_CTRL_IDX] = val; - err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); - - return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); -} - -static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read auto exposure control %d", *val); - return 0; -} - -static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, - __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set auto exposure control to %d", val); - - sensor_settings[AUTO_EXPOSURE_IDX] = val; - err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); - - return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); -} - -static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return 0; -} - -static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - - sensor_settings[HFLIP_IDX] = val; - - i2c_data = ((val & 0x01) << 5) | - (sensor_settings[VFLIP_IDX] << 4); - - err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); - - return err; -} - -static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - sensor_settings[VFLIP_IDX] = val; - - i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); - err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); - if (err < 0) - return err; - - /* When vflip is toggled we need to readjust the bridge hsync/vsync */ - if (gspca_dev->streaming) - err = ov7660_start(sd); - - return err; -} - -static void ov7660_dump_registers(struct sd *sd) -{ - int address; - pr_info("Dumping the ov7660 register state\n"); - for (address = 0; address < 0xa9; address++) { - u8 value; - m5602_read_sensor(sd, address, &value, 1); - pr_info("register 0x%x contains 0x%x\n", address, value); - } - - pr_info("ov7660 register state dump complete\n"); - - pr_info("Probing for which registers that are read/write\n"); - for (address = 0; address < 0xff; address++) { - u8 old_value, ctrl_value; - u8 test_value[2] = {0xff, 0xff}; - - m5602_read_sensor(sd, address, &old_value, 1); - m5602_write_sensor(sd, address, test_value, 1); - m5602_read_sensor(sd, address, &ctrl_value, 1); - - if (ctrl_value == test_value[0]) - pr_info("register 0x%x is writeable\n", address); - else - pr_info("register 0x%x is read only\n", address); - - /* Restore original value */ - m5602_write_sensor(sd, address, &old_value, 1); - } -} diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h deleted file mode 100644 index 2b6a13b508f7..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_ov7660.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Driver for the ov7660 sensor - * - * Copyright (C) 2009 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_OV7660_H_ -#define M5602_OV7660_H_ - -#include "m5602_sensor.h" - -#define OV7660_GAIN 0x00 -#define OV7660_BLUE_GAIN 0x01 -#define OV7660_RED_GAIN 0x02 -#define OV7660_VREF 0x03 -#define OV7660_COM1 0x04 -#define OV7660_BAVE 0x05 -#define OV7660_GEAVE 0x06 -#define OV7660_AECHH 0x07 -#define OV7660_RAVE 0x08 -#define OV7660_COM2 0x09 -#define OV7660_PID 0x0a -#define OV7660_VER 0x0b -#define OV7660_COM3 0x0c -#define OV7660_COM4 0x0d -#define OV7660_COM5 0x0e -#define OV7660_COM6 0x0f -#define OV7660_AECH 0x10 -#define OV7660_CLKRC 0x11 -#define OV7660_COM7 0x12 -#define OV7660_COM8 0x13 -#define OV7660_COM9 0x14 -#define OV7660_COM10 0x15 -#define OV7660_RSVD16 0x16 -#define OV7660_HSTART 0x17 -#define OV7660_HSTOP 0x18 -#define OV7660_VSTART 0x19 -#define OV7660_VSTOP 0x1a -#define OV7660_PSHFT 0x1b -#define OV7660_MIDH 0x1c -#define OV7660_MIDL 0x1d -#define OV7660_MVFP 0x1e -#define OV7660_LAEC 0x1f -#define OV7660_BOS 0x20 -#define OV7660_GBOS 0x21 -#define OV7660_GROS 0x22 -#define OV7660_ROS 0x23 -#define OV7660_AEW 0x24 -#define OV7660_AEB 0x25 -#define OV7660_VPT 0x26 -#define OV7660_BBIAS 0x27 -#define OV7660_GbBIAS 0x28 -#define OV7660_RSVD29 0x29 -#define OV7660_RBIAS 0x2c -#define OV7660_HREF 0x32 -#define OV7660_ADC 0x37 -#define OV7660_OFON 0x39 -#define OV7660_TSLB 0x3a -#define OV7660_COM12 0x3c -#define OV7660_COM13 0x3d -#define OV7660_LCC1 0x62 -#define OV7660_LCC2 0x63 -#define OV7660_LCC3 0x64 -#define OV7660_LCC4 0x65 -#define OV7660_LCC5 0x66 -#define OV7660_HV 0x69 -#define OV7660_RSVDA1 0xa1 - -#define OV7660_DEFAULT_GAIN 0x0e -#define OV7660_DEFAULT_RED_GAIN 0x80 -#define OV7660_DEFAULT_BLUE_GAIN 0x80 -#define OV7660_DEFAULT_SATURATION 0x00 -#define OV7660_DEFAULT_EXPOSURE 0x20 - -/* Kernel module parameters */ -extern int force_sensor; -extern bool dump_sensor; - -int ov7660_probe(struct sd *sd); -int ov7660_init(struct sd *sd); -int ov7660_start(struct sd *sd); -int ov7660_stop(struct sd *sd); -void ov7660_disconnect(struct sd *sd); - -static const struct m5602_sensor ov7660 = { - .name = "ov7660", - .i2c_slave_id = 0x42, - .i2c_regW = 1, - .probe = ov7660_probe, - .init = ov7660_init, - .start = ov7660_start, - .stop = ov7660_stop, - .disconnect = ov7660_disconnect, -}; - -static const unsigned char preinit_ov7660[][4] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - - {SENSOR, OV7660_OFON, 0x0c}, - {SENSOR, OV7660_COM2, 0x11}, - {SENSOR, OV7660_COM7, 0x05}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00} -}; - -static const unsigned char init_ov7660[][4] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, - {SENSOR, OV7660_COM7, 0x80}, - {SENSOR, OV7660_CLKRC, 0x80}, - {SENSOR, OV7660_COM9, 0x4c}, - {SENSOR, OV7660_OFON, 0x43}, - {SENSOR, OV7660_COM12, 0x28}, - {SENSOR, OV7660_COM8, 0x00}, - {SENSOR, OV7660_COM10, 0x40}, - {SENSOR, OV7660_HSTART, 0x0c}, - {SENSOR, OV7660_HSTOP, 0x61}, - {SENSOR, OV7660_HREF, 0xa4}, - {SENSOR, OV7660_PSHFT, 0x0b}, - {SENSOR, OV7660_VSTART, 0x01}, - {SENSOR, OV7660_VSTOP, 0x7a}, - {SENSOR, OV7660_VSTOP, 0x00}, - {SENSOR, OV7660_COM7, 0x05}, - {SENSOR, OV7660_COM6, 0x42}, - {SENSOR, OV7660_BBIAS, 0x94}, - {SENSOR, OV7660_GbBIAS, 0x94}, - {SENSOR, OV7660_RSVD29, 0x94}, - {SENSOR, OV7660_RBIAS, 0x94}, - {SENSOR, OV7660_COM1, 0x00}, - {SENSOR, OV7660_AECH, 0x00}, - {SENSOR, OV7660_AECHH, 0x00}, - {SENSOR, OV7660_ADC, 0x05}, - {SENSOR, OV7660_COM13, 0x00}, - {SENSOR, OV7660_RSVDA1, 0x23}, - {SENSOR, OV7660_TSLB, 0x0d}, - {SENSOR, OV7660_HV, 0x80}, - {SENSOR, OV7660_LCC1, 0x00}, - {SENSOR, OV7660_LCC2, 0x00}, - {SENSOR, OV7660_LCC3, 0x10}, - {SENSOR, OV7660_LCC4, 0x40}, - {SENSOR, OV7660_LCC5, 0x01}, - - {SENSOR, OV7660_AECH, 0x20}, - {SENSOR, OV7660_COM1, 0x00}, - {SENSOR, OV7660_OFON, 0x0c}, - {SENSOR, OV7660_COM2, 0x11}, - {SENSOR, OV7660_COM7, 0x05}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, - {SENSOR, OV7660_AECH, 0x5f}, - {SENSOR, OV7660_COM1, 0x03}, - {SENSOR, OV7660_OFON, 0x0c}, - {SENSOR, OV7660_COM2, 0x11}, - {SENSOR, OV7660_COM7, 0x05}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, - {BRIDGE, M5602_XB_SIG_INI, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x08}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x27}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0xa7}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, -}; -#endif diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c deleted file mode 100644 index 2114a8b90ec9..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.c +++ /dev/null @@ -1,881 +0,0 @@ -/* - * Driver for the ov9650 sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "m5602_ov9650.h" - -static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); -static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); - -/* Vertically and horizontally flips the image if matched, needed for machines - where the sensor is mounted upside down */ -static - const - struct dmi_system_id ov9650_flip_dmi_table[] = { - { - .ident = "ASUS A6Ja", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6J") - } - }, - { - .ident = "ASUS A6JC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") - } - }, - { - .ident = "ASUS A6K", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6K") - } - }, - { - .ident = "ASUS A6Kt", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") - } - }, - { - .ident = "ASUS A6VA", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VA") - } - }, - { - - .ident = "ASUS A6VC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") - } - }, - { - .ident = "ASUS A6VM", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") - } - }, - { - .ident = "ASUS A7V", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A7V") - } - }, - { - .ident = "Alienware Aurora m9700", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700") - } - }, - {} -}; - -static const struct ctrl ov9650_ctrls[] = { -#define EXPOSURE_IDX 0 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x1ff, - .step = 0x4, - .default_value = EXPOSURE_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_exposure, - .get = ov9650_get_exposure - }, -#define GAIN_IDX 1 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0x3ff, - .step = 0x1, - .default_value = GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_gain, - .get = ov9650_get_gain - }, -#define RED_BALANCE_IDX 2 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_red_balance, - .get = ov9650_get_red_balance - }, -#define BLUE_BALANCE_IDX 3 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_blue_balance, - .get = ov9650_get_blue_balance - }, -#define HFLIP_IDX 4 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_hflip, - .get = ov9650_get_hflip - }, -#define VFLIP_IDX 5 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_vflip, - .get = ov9650_get_vflip - }, -#define AUTO_WHITE_BALANCE_IDX 6 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov9650_set_auto_white_balance, - .get = ov9650_get_auto_white_balance - }, -#define AUTO_GAIN_CTRL_IDX 7 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto gain control", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov9650_set_auto_gain, - .get = ov9650_get_auto_gain - }, -#define AUTO_EXPOSURE_IDX 8 - { - { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = ov9650_set_auto_exposure, - .get = ov9650_get_auto_exposure - } - -}; - -static struct v4l2_pix_format ov9650_modes[] = { - { - 176, - 144, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 176 * 144, - .bytesperline = 176, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 9 - }, { - 320, - 240, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 320 * 240, - .bytesperline = 320, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 8 - }, { - 352, - 288, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 352 * 288, - .bytesperline = 352, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 9 - }, { - 640, - 480, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 640 * 480, - .bytesperline = 640, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 9 - } -}; - -static void ov9650_dump_registers(struct sd *sd); - -int ov9650_probe(struct sd *sd) -{ - int err = 0; - u8 prod_id = 0, ver_id = 0, i; - s32 *sensor_settings; - - if (force_sensor) { - if (force_sensor == OV9650_SENSOR) { - pr_info("Forcing an %s sensor\n", ov9650.name); - goto sensor_found; - } - /* If we want to force another sensor, - don't try to probe this one */ - return -ENODEV; - } - - PDEBUG(D_PROBE, "Probing for an ov9650 sensor"); - - /* Run the pre-init before probing the sensor */ - for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) { - u8 data = preinit_ov9650[i][2]; - if (preinit_ov9650[i][0] == SENSOR) - err = m5602_write_sensor(sd, - preinit_ov9650[i][1], &data, 1); - else - err = m5602_write_bridge(sd, - preinit_ov9650[i][1], data); - } - - if (err < 0) - return err; - - if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1)) - return -ENODEV; - - if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1)) - return -ENODEV; - - if ((prod_id == 0x96) && (ver_id == 0x52)) { - pr_info("Detected an ov9650 sensor\n"); - goto sensor_found; - } - return -ENODEV; - -sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(ov9650_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - - sd->gspca_dev.cam.cam_mode = ov9650_modes; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes); - sd->desc->ctrls = ov9650_ctrls; - sd->desc->nctrls = ARRAY_SIZE(ov9650_ctrls); - - for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++) - sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - return 0; -} - -int ov9650_init(struct sd *sd) -{ - int i, err = 0; - u8 data; - s32 *sensor_settings = sd->sensor_priv; - - if (dump_sensor) - ov9650_dump_registers(sd); - - for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) { - data = init_ov9650[i][2]; - if (init_ov9650[i][0] == SENSOR) - err = m5602_write_sensor(sd, init_ov9650[i][1], - &data, 1); - else - err = m5602_write_bridge(sd, init_ov9650[i][1], data); - } - - err = ov9650_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = ov9650_set_red_balance(&sd->gspca_dev, - sensor_settings[RED_BALANCE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_blue_balance(&sd->gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = ov9650_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; - - err = ov9650_set_auto_exposure(&sd->gspca_dev, - sensor_settings[AUTO_EXPOSURE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_auto_white_balance(&sd->gspca_dev, - sensor_settings[AUTO_WHITE_BALANCE_IDX]); - if (err < 0) - return err; - - err = ov9650_set_auto_gain(&sd->gspca_dev, - sensor_settings[AUTO_GAIN_CTRL_IDX]); - return err; -} - -int ov9650_start(struct sd *sd) -{ - u8 data; - int i, err = 0; - struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; - - int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; - int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; - int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; - int hor_offs = OV9650_LEFT_OFFSET; - - if ((!dmi_check_system(ov9650_flip_dmi_table) && - sensor_settings[VFLIP_IDX]) || - (dmi_check_system(ov9650_flip_dmi_table) && - !sensor_settings[VFLIP_IDX])) - ver_offs--; - - if (width <= 320) - hor_offs /= 2; - - /* Synthesize the vsync/hsync setup */ - for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) { - if (res_init_ov9650[i][0] == BRIDGE) - err = m5602_write_bridge(sd, res_init_ov9650[i][1], - res_init_ov9650[i][2]); - else if (res_init_ov9650[i][0] == SENSOR) { - data = res_init_ov9650[i][2]; - err = m5602_write_sensor(sd, - res_init_ov9650[i][1], &data, 1); - } - } - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, - ((ver_offs >> 8) & 0xff)); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); - if (err < 0) - return err; - - for (i = 0; i < 2 && !err; i++) - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, - (hor_offs >> 8) & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, - ((width + hor_offs) >> 8) & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, - ((width + hor_offs) & 0xff)); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); - if (err < 0) - return err; - - switch (width) { - case 640: - PDEBUG(D_V4L2, "Configuring camera for VGA mode"); - - data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT; - err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); - break; - - case 352: - PDEBUG(D_V4L2, "Configuring camera for CIF mode"); - - data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT; - err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); - break; - - case 320: - PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); - - data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT; - err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); - break; - - case 176: - PDEBUG(D_V4L2, "Configuring camera for QCIF mode"); - - data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT; - err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); - break; - } - return err; -} - -int ov9650_stop(struct sd *sd) -{ - u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X; - return m5602_write_sensor(sd, OV9650_COM2, &data, 1); -} - -void ov9650_disconnect(struct sd *sd) -{ - ov9650_stop(sd); - - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read exposure %d", *val); - return 0; -} - -static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - PDEBUG(D_V4L2, "Set exposure to %d", val); - - sensor_settings[EXPOSURE_IDX] = val; - /* The 6 MSBs */ - i2c_data = (val >> 10) & 0x3f; - err = m5602_write_sensor(sd, OV9650_AECHM, - &i2c_data, 1); - if (err < 0) - return err; - - /* The 8 middle bits */ - i2c_data = (val >> 2) & 0xff; - err = m5602_write_sensor(sd, OV9650_AECH, - &i2c_data, 1); - if (err < 0) - return err; - - /* The 2 LSBs */ - i2c_data = val & 0x03; - err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1); - return err; -} - -static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); - return 0; -} - -static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Setting gain to %d", val); - - sensor_settings[GAIN_IDX] = val; - - /* The 2 MSB */ - /* Read the OV9650_VREF register first to avoid - corrupting the VREF high and low bits */ - err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); - if (err < 0) - return err; - - /* Mask away all uninteresting bits */ - i2c_data = ((val & 0x0300) >> 2) | - (i2c_data & 0x3f); - err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); - if (err < 0) - return err; - - /* The 8 LSBs */ - i2c_data = val & 0xff; - err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); - return err; -} - -static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read red gain %d", *val); - return 0; -} - -static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set red gain to %d", val); - - sensor_settings[RED_BALANCE_IDX] = val; - - i2c_data = val & 0xff; - err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); - return err; -} - -static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read blue gain %d", *val); - - return 0; -} - -static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set blue gain to %d", val); - - sensor_settings[BLUE_BALANCE_IDX] = val; - - i2c_data = val & 0xff; - err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); - return err; -} - -static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return 0; -} - -static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - - sensor_settings[HFLIP_IDX] = val; - - if (!dmi_check_system(ov9650_flip_dmi_table)) - i2c_data = ((val & 0x01) << 5) | - (sensor_settings[VFLIP_IDX] << 4); - else - i2c_data = ((val & 0x01) << 5) | - (!sensor_settings[VFLIP_IDX] << 4); - - err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); - - return err; -} - -static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - sensor_settings[VFLIP_IDX] = val; - - if (dmi_check_system(ov9650_flip_dmi_table)) - val = !val; - - i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); - err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); - if (err < 0) - return err; - - /* When vflip is toggled we need to readjust the bridge hsync/vsync */ - if (gspca_dev->streaming) - err = ov9650_start(sd); - - return err; -} - -static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read auto exposure control %d", *val); - return 0; -} - -static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, - __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set auto exposure control to %d", val); - - sensor_settings[AUTO_EXPOSURE_IDX] = val; - err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); - - return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); -} - -static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - return 0; -} - -static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set auto white balance to %d", val); - - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; - err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); - err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); - - return err; -} - -static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; - PDEBUG(D_V4L2, "Read auto gain control %d", *val); - return 0; -} - -static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - PDEBUG(D_V4L2, "Set auto gain control to %d", val); - - sensor_settings[AUTO_GAIN_CTRL_IDX] = val; - err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); - - return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); -} - -static void ov9650_dump_registers(struct sd *sd) -{ - int address; - pr_info("Dumping the ov9650 register state\n"); - for (address = 0; address < 0xa9; address++) { - u8 value; - m5602_read_sensor(sd, address, &value, 1); - pr_info("register 0x%x contains 0x%x\n", address, value); - } - - pr_info("ov9650 register state dump complete\n"); - - pr_info("Probing for which registers that are read/write\n"); - for (address = 0; address < 0xff; address++) { - u8 old_value, ctrl_value; - u8 test_value[2] = {0xff, 0xff}; - - m5602_read_sensor(sd, address, &old_value, 1); - m5602_write_sensor(sd, address, test_value, 1); - m5602_read_sensor(sd, address, &ctrl_value, 1); - - if (ctrl_value == test_value[0]) - pr_info("register 0x%x is writeable\n", address); - else - pr_info("register 0x%x is read only\n", address); - - /* Restore original value */ - m5602_write_sensor(sd, address, &old_value, 1); - } -} diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h deleted file mode 100644 index f7aa5bf68983..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Driver for the ov9650 sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_OV9650_H_ -#define M5602_OV9650_H_ - -#include -#include "m5602_sensor.h" - -/*****************************************************************************/ - -#define OV9650_GAIN 0x00 -#define OV9650_BLUE 0x01 -#define OV9650_RED 0x02 -#define OV9650_VREF 0x03 -#define OV9650_COM1 0x04 -#define OV9650_BAVE 0x05 -#define OV9650_GEAVE 0x06 -#define OV9650_RSVD7 0x07 -#define OV9650_COM2 0x09 -#define OV9650_PID 0x0a -#define OV9650_VER 0x0b -#define OV9650_COM3 0x0c -#define OV9650_COM4 0x0d -#define OV9650_COM5 0x0e -#define OV9650_COM6 0x0f -#define OV9650_AECH 0x10 -#define OV9650_CLKRC 0x11 -#define OV9650_COM7 0x12 -#define OV9650_COM8 0x13 -#define OV9650_COM9 0x14 -#define OV9650_COM10 0x15 -#define OV9650_RSVD16 0x16 -#define OV9650_HSTART 0x17 -#define OV9650_HSTOP 0x18 -#define OV9650_VSTRT 0x19 -#define OV9650_VSTOP 0x1a -#define OV9650_PSHFT 0x1b -#define OV9650_MVFP 0x1e -#define OV9650_AEW 0x24 -#define OV9650_AEB 0x25 -#define OV9650_VPT 0x26 -#define OV9650_BBIAS 0x27 -#define OV9650_GbBIAS 0x28 -#define OV9650_Gr_COM 0x29 -#define OV9650_RBIAS 0x2c -#define OV9650_HREF 0x32 -#define OV9650_CHLF 0x33 -#define OV9650_ARBLM 0x34 -#define OV9650_RSVD35 0x35 -#define OV9650_RSVD36 0x36 -#define OV9650_ADC 0x37 -#define OV9650_ACOM38 0x38 -#define OV9650_OFON 0x39 -#define OV9650_TSLB 0x3a -#define OV9650_COM12 0x3c -#define OV9650_COM13 0x3d -#define OV9650_COM15 0x40 -#define OV9650_COM16 0x41 -#define OV9650_LCC1 0x62 -#define OV9650_LCC2 0x63 -#define OV9650_LCC3 0x64 -#define OV9650_LCC4 0x65 -#define OV9650_LCC5 0x66 -#define OV9650_HV 0x69 -#define OV9650_DBLV 0x6b -#define OV9650_COM21 0x8b -#define OV9650_COM22 0x8c -#define OV9650_COM24 0x8e -#define OV9650_DBLC1 0x8f -#define OV9650_RSVD94 0x94 -#define OV9650_RSVD95 0x95 -#define OV9650_RSVD96 0x96 -#define OV9650_LCCFB 0x9d -#define OV9650_LCCFR 0x9e -#define OV9650_AECHM 0xa1 -#define OV9650_COM26 0xa5 -#define OV9650_ACOMA8 0xa8 -#define OV9650_ACOMA9 0xa9 - -#define OV9650_REGISTER_RESET (1 << 7) -#define OV9650_VGA_SELECT (1 << 6) -#define OV9650_CIF_SELECT (1 << 5) -#define OV9650_QVGA_SELECT (1 << 4) -#define OV9650_QCIF_SELECT (1 << 3) -#define OV9650_RGB_SELECT (1 << 2) -#define OV9650_RAW_RGB_SELECT (1 << 0) - -#define OV9650_FAST_AGC_AEC (1 << 7) -#define OV9650_AEC_UNLIM_STEP_SIZE (1 << 6) -#define OV9650_BANDING (1 << 5) -#define OV9650_AGC_EN (1 << 2) -#define OV9650_AWB_EN (1 << 1) -#define OV9650_AEC_EN (1 << 0) - -#define OV9650_VARIOPIXEL (1 << 2) -#define OV9650_SYSTEM_CLK_SEL (1 << 7) -#define OV9650_SLAM_MODE (1 << 4) - -#define OV9650_QVGA_VARIOPIXEL (1 << 7) - -#define OV9650_VFLIP (1 << 4) -#define OV9650_HFLIP (1 << 5) - -#define OV9650_SOFT_SLEEP (1 << 4) -#define OV9650_OUTPUT_DRIVE_2X (1 << 0) - -#define OV9650_DENOISE_ENABLE (1 << 5) -#define OV9650_WHITE_PIXEL_ENABLE (1 << 1) -#define OV9650_WHITE_PIXEL_OPTION (1 << 0) - -#define OV9650_LEFT_OFFSET 0x62 - -#define GAIN_DEFAULT 0x14 -#define RED_GAIN_DEFAULT 0x70 -#define BLUE_GAIN_DEFAULT 0x20 -#define EXPOSURE_DEFAULT 0x1ff - -/*****************************************************************************/ - -/* Kernel module parameters */ -extern int force_sensor; -extern bool dump_sensor; - -int ov9650_probe(struct sd *sd); -int ov9650_init(struct sd *sd); -int ov9650_start(struct sd *sd); -int ov9650_stop(struct sd *sd); -void ov9650_disconnect(struct sd *sd); - -static const struct m5602_sensor ov9650 = { - .name = "OV9650", - .i2c_slave_id = 0x60, - .i2c_regW = 1, - .probe = ov9650_probe, - .init = ov9650_init, - .start = ov9650_start, - .stop = ov9650_stop, - .disconnect = ov9650_disconnect, -}; - -static const unsigned char preinit_ov9650[][3] = { - /* [INITCAM] */ - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, - /* Reset chip */ - {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, - /* Enable double clock */ - {SENSOR, OV9650_CLKRC, 0x80}, - /* Do something out of spec with the power */ - {SENSOR, OV9650_OFON, 0x40} -}; - -static const unsigned char init_ov9650[][3] = { - /* [INITCAM] */ - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, - - /* Reset chip */ - {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, - /* One extra reset is needed in order to make the sensor behave - properly when resuming from ram, could be a timing issue */ - {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, - - /* Enable double clock */ - {SENSOR, OV9650_CLKRC, 0x80}, - /* Do something out of spec with the power */ - {SENSOR, OV9650_OFON, 0x40}, - - /* Set fast AGC/AEC algorithm with unlimited step size */ - {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | - OV9650_AEC_UNLIM_STEP_SIZE}, - - {SENSOR, OV9650_CHLF, 0x10}, - {SENSOR, OV9650_ARBLM, 0xbf}, - {SENSOR, OV9650_ACOM38, 0x81}, - /* Turn off color matrix coefficient double option */ - {SENSOR, OV9650_COM16, 0x00}, - /* Enable color matrix for RGB/YUV, Delay Y channel, - set output Y/UV delay to 1 */ - {SENSOR, OV9650_COM13, 0x19}, - /* Enable digital BLC, Set output mode to U Y V Y */ - {SENSOR, OV9650_TSLB, 0x0c}, - /* Limit the AGC/AEC stable upper region */ - {SENSOR, OV9650_COM24, 0x00}, - /* Enable HREF and some out of spec things */ - {SENSOR, OV9650_COM12, 0x73}, - /* Set all DBLC offset signs to positive and - do some out of spec stuff */ - {SENSOR, OV9650_DBLC1, 0xdf}, - {SENSOR, OV9650_COM21, 0x06}, - {SENSOR, OV9650_RSVD35, 0x91}, - /* Necessary, no camera stream without it */ - {SENSOR, OV9650_RSVD16, 0x06}, - {SENSOR, OV9650_RSVD94, 0x99}, - {SENSOR, OV9650_RSVD95, 0x99}, - {SENSOR, OV9650_RSVD96, 0x04}, - /* Enable full range output */ - {SENSOR, OV9650_COM15, 0x0}, - /* Enable HREF at optical black, enable ADBLC bias, - enable ADBLC, reset timings at format change */ - {SENSOR, OV9650_COM6, 0x4b}, - /* Subtract 32 from the B channel bias */ - {SENSOR, OV9650_BBIAS, 0xa0}, - /* Subtract 32 from the Gb channel bias */ - {SENSOR, OV9650_GbBIAS, 0xa0}, - /* Do not bypass the analog BLC and to some out of spec stuff */ - {SENSOR, OV9650_Gr_COM, 0x00}, - /* Subtract 32 from the R channel bias */ - {SENSOR, OV9650_RBIAS, 0xa0}, - /* Subtract 32 from the R channel bias */ - {SENSOR, OV9650_RBIAS, 0x0}, - {SENSOR, OV9650_COM26, 0x80}, - {SENSOR, OV9650_ACOMA9, 0x98}, - /* Set the AGC/AEC stable region upper limit */ - {SENSOR, OV9650_AEW, 0x68}, - /* Set the AGC/AEC stable region lower limit */ - {SENSOR, OV9650_AEB, 0x5c}, - /* Set the high and low limit nibbles to 3 */ - {SENSOR, OV9650_VPT, 0xc3}, - /* Set the Automatic Gain Ceiling (AGC) to 128x, - drop VSYNC at frame drop, - limit exposure timing, - drop frame when the AEC step is larger than the exposure gap */ - {SENSOR, OV9650_COM9, 0x6e}, - /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync) - and set PWDN to SLVS (slave mode vertical sync) */ - {SENSOR, OV9650_COM10, 0x42}, - /* Set horizontal column start high to default value */ - {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */ - /* Set horizontal column end */ - {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */ - /* Complementing register to the two writes above */ - {SENSOR, OV9650_HREF, 0xb2}, - /* Set vertical row start high bits */ - {SENSOR, OV9650_VSTRT, 0x02}, - /* Set vertical row end low bits */ - {SENSOR, OV9650_VSTOP, 0x7e}, - /* Set complementing vertical frame control */ - {SENSOR, OV9650_VREF, 0x10}, - {SENSOR, OV9650_ADC, 0x04}, - {SENSOR, OV9650_HV, 0x40}, - - /* Enable denoise, and white-pixel erase */ - {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE | - OV9650_WHITE_PIXEL_ENABLE | - OV9650_WHITE_PIXEL_OPTION}, - - /* Enable VARIOPIXEL */ - {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, - {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL}, - - /* Put the sensor in soft sleep mode */ - {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X}, -}; - -static const unsigned char res_init_ov9650[][3] = { - {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X}, - - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, - {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01} -}; -#endif diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c deleted file mode 100644 index b8771698cbcb..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_po1030.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * Driver for the po1030 sensor - * - * Copyright (c) 2008 Erik Andrén - * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (c) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "m5602_po1030.h" - -static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val); -static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val); -static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, - __s32 val); -static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, - __s32 *val); - -static struct v4l2_pix_format po1030_modes[] = { - { - 640, - 480, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = 640 * 480, - .bytesperline = 640, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2 - } -}; - -static const struct ctrl po1030_ctrls[] = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0x4f, - .step = 0x1, - .default_value = PO1030_GLOBAL_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_gain, - .get = po1030_get_gain - }, -#define EXPOSURE_IDX 1 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x02ff, - .step = 0x1, - .default_value = PO1030_EXPOSURE_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_exposure, - .get = po1030_get_exposure - }, -#define RED_BALANCE_IDX 2 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_red_balance, - .get = po1030_get_red_balance - }, -#define BLUE_BALANCE_IDX 3 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_blue_balance, - .get = po1030_get_blue_balance - }, -#define HFLIP_IDX 4 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_hflip, - .get = po1030_get_hflip - }, -#define VFLIP_IDX 5 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_vflip, - .get = po1030_get_vflip - }, -#define AUTO_WHITE_BALANCE_IDX 6 - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_auto_white_balance, - .get = po1030_get_auto_white_balance - }, -#define AUTO_EXPOSURE_IDX 7 - { - { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_auto_exposure, - .get = po1030_get_auto_exposure - }, -#define GREEN_BALANCE_IDX 8 - { - { - .id = M5602_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_GREEN_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_green_balance, - .get = po1030_get_green_balance - }, -}; - -static void po1030_dump_registers(struct sd *sd); - -int po1030_probe(struct sd *sd) -{ - u8 dev_id_h = 0, i; - s32 *sensor_settings; - - if (force_sensor) { - if (force_sensor == PO1030_SENSOR) { - pr_info("Forcing a %s sensor\n", po1030.name); - goto sensor_found; - } - /* If we want to force another sensor, don't try to probe this - * one */ - return -ENODEV; - } - - PDEBUG(D_PROBE, "Probing for a po1030 sensor"); - - /* Run the pre-init to actually probe the unit */ - for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { - u8 data = preinit_po1030[i][2]; - if (preinit_po1030[i][0] == SENSOR) - m5602_write_sensor(sd, - preinit_po1030[i][1], &data, 1); - else - m5602_write_bridge(sd, preinit_po1030[i][1], data); - } - - if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1)) - return -ENODEV; - - if (dev_id_h == 0x30) { - pr_info("Detected a po1030 sensor\n"); - goto sensor_found; - } - return -ENODEV; - -sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(po1030_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - - sd->gspca_dev.cam.cam_mode = po1030_modes; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes); - sd->desc->ctrls = po1030_ctrls; - sd->desc->nctrls = ARRAY_SIZE(po1030_ctrls); - - for (i = 0; i < ARRAY_SIZE(po1030_ctrls); i++) - sensor_settings[i] = po1030_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - - return 0; -} - -int po1030_init(struct sd *sd) -{ - s32 *sensor_settings = sd->sensor_priv; - int i, err = 0; - - /* Init the sensor */ - for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) { - u8 data[2] = {0x00, 0x00}; - - switch (init_po1030[i][0]) { - case BRIDGE: - err = m5602_write_bridge(sd, - init_po1030[i][1], - init_po1030[i][2]); - break; - - case SENSOR: - data[0] = init_po1030[i][2]; - err = m5602_write_sensor(sd, - init_po1030[i][1], data, 1); - break; - - default: - pr_info("Invalid stream command, exiting init\n"); - return -EINVAL; - } - } - if (err < 0) - return err; - - if (dump_sensor) - po1030_dump_registers(sd); - - err = po1030_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = po1030_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = po1030_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = po1030_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; - - err = po1030_set_red_balance(&sd->gspca_dev, - sensor_settings[RED_BALANCE_IDX]); - if (err < 0) - return err; - - err = po1030_set_blue_balance(&sd->gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); - if (err < 0) - return err; - - err = po1030_set_green_balance(&sd->gspca_dev, - sensor_settings[GREEN_BALANCE_IDX]); - if (err < 0) - return err; - - err = po1030_set_auto_white_balance(&sd->gspca_dev, - sensor_settings[AUTO_WHITE_BALANCE_IDX]); - if (err < 0) - return err; - - err = po1030_set_auto_exposure(&sd->gspca_dev, - sensor_settings[AUTO_EXPOSURE_IDX]); - return err; -} - -int po1030_start(struct sd *sd) -{ - struct cam *cam = &sd->gspca_dev.cam; - int i, err = 0; - int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; - int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; - int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; - u8 data; - - switch (width) { - case 320: - data = PO1030_SUBSAMPLING; - err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); - if (err < 0) - return err; - - data = ((width + 3) >> 8) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); - if (err < 0) - return err; - - data = (width + 3) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); - if (err < 0) - return err; - - data = ((height + 1) >> 8) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); - if (err < 0) - return err; - - data = (height + 1) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); - - height += 6; - width -= 1; - break; - - case 640: - data = 0; - err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); - if (err < 0) - return err; - - data = ((width + 7) >> 8) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); - if (err < 0) - return err; - - data = (width + 7) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); - if (err < 0) - return err; - - data = ((height + 3) >> 8) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); - if (err < 0) - return err; - - data = (height + 3) & 0xff; - err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); - - height += 12; - width -= 2; - break; - } - err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, - ((ver_offs >> 8) & 0xff)); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); - if (err < 0) - return err; - - for (i = 0; i < 2 && !err; i++) - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); - if (err < 0) - return err; - - for (i = 0; i < 2 && !err; i++) - err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); - - for (i = 0; i < 2 && !err; i++) - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); - - for (i = 0; i < 2 && !err; i++) - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff)); - if (err < 0) - return err; - - err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); - return err; -} - -static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Exposure read as %d", *val); - return 0; -} - -static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[EXPOSURE_IDX] = val; - PDEBUG(D_V4L2, "Set exposure to %d", val & 0xffff); - - i2c_data = ((val & 0xff00) >> 8); - PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x", - i2c_data); - - err = m5602_write_sensor(sd, PO1030_INTEGLINES_H, - &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = (val & 0xff); - PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x", - i2c_data); - err = m5602_write_sensor(sd, PO1030_INTEGLINES_M, - &i2c_data, 1); - - return err; -} - -static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read global gain %d", *val); - return 0; -} - -static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[GAIN_IDX] = val; - - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set global gain to %d", i2c_data); - err = m5602_write_sensor(sd, PO1030_GLOBALGAIN, - &i2c_data, 1); - return err; -} - -static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read hflip %d", *val); - - return 0; -} - -static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[HFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set hflip %d", val); - err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7); - - err = m5602_write_sensor(sd, PO1030_CONTROL2, - &i2c_data, 1); - - return err; -} - -static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vflip %d", *val); - - return 0; -} - -static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[VFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set vflip %d", val); - err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); - if (err < 0) - return err; - - i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6); - - err = m5602_write_sensor(sd, PO1030_CONTROL2, - &i2c_data, 1); - - return err; -} - -static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read red gain %d", *val); - return 0; -} - -static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[RED_BALANCE_IDX] = val; - - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set red gain to %d", i2c_data); - err = m5602_write_sensor(sd, PO1030_RED_GAIN, - &i2c_data, 1); - return err; -} - -static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read blue gain %d", *val); - - return 0; -} - -static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[BLUE_BALANCE_IDX] = val; - - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data); - err = m5602_write_sensor(sd, PO1030_BLUE_GAIN, - &i2c_data, 1); - - return err; -} - -static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GREEN_BALANCE_IDX]; - PDEBUG(D_V4L2, "Read green gain %d", *val); - - return 0; -} - -static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[GREEN_BALANCE_IDX] = val; - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set green gain to %d", i2c_data); - - err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN, - &i2c_data, 1); - if (err < 0) - return err; - - return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN, - &i2c_data, 1); -} - -static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; - PDEBUG(D_V4L2, "Auto white balancing is %d", *val); - - return 0; -} - -static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, - __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; - - err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); - if (err < 0) - return err; - - PDEBUG(D_V4L2, "Set auto white balance to %d", val); - i2c_data = (i2c_data & 0xfe) | (val & 0x01); - err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); - return err; -} - -static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, - __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTO_EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Auto exposure is %d", *val); - return 0; -} - -static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, - __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 i2c_data; - int err; - - sensor_settings[AUTO_EXPOSURE_IDX] = val; - err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); - if (err < 0) - return err; - - PDEBUG(D_V4L2, "Set auto exposure to %d", val); - i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1); - return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); -} - -void po1030_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static void po1030_dump_registers(struct sd *sd) -{ - int address; - u8 value = 0; - - pr_info("Dumping the po1030 sensor core registers\n"); - for (address = 0; address < 0x7f; address++) { - m5602_read_sensor(sd, address, &value, 1); - pr_info("register 0x%x contains 0x%x\n", address, value); - } - - pr_info("po1030 register state dump complete\n"); - - pr_info("Probing for which registers that are read/write\n"); - for (address = 0; address < 0xff; address++) { - u8 old_value, ctrl_value; - u8 test_value[2] = {0xff, 0xff}; - - m5602_read_sensor(sd, address, &old_value, 1); - m5602_write_sensor(sd, address, test_value, 1); - m5602_read_sensor(sd, address, &ctrl_value, 1); - - if (ctrl_value == test_value[0]) - pr_info("register 0x%x is writeable\n", address); - else - pr_info("register 0x%x is read only\n", address); - - /* Restore original value */ - m5602_write_sensor(sd, address, &old_value, 1); - } -} diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h deleted file mode 100644 index 81a2bcb88fe3..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_po1030.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Driver for the po1030 sensor. - * - * Copyright (c) 2008 Erik Andrén - * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (c) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * Register defines taken from Pascal Stangs Procyon Armlib - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_PO1030_H_ -#define M5602_PO1030_H_ - -#include "m5602_sensor.h" - -/*****************************************************************************/ - -#define PO1030_DEVID_H 0x00 -#define PO1030_DEVID_L 0x01 -#define PO1030_FRAMEWIDTH_H 0x04 -#define PO1030_FRAMEWIDTH_L 0x05 -#define PO1030_FRAMEHEIGHT_H 0x06 -#define PO1030_FRAMEHEIGHT_L 0x07 -#define PO1030_WINDOWX_H 0x08 -#define PO1030_WINDOWX_L 0x09 -#define PO1030_WINDOWY_H 0x0a -#define PO1030_WINDOWY_L 0x0b -#define PO1030_WINDOWWIDTH_H 0x0c -#define PO1030_WINDOWWIDTH_L 0x0d -#define PO1030_WINDOWHEIGHT_H 0x0e -#define PO1030_WINDOWHEIGHT_L 0x0f - -#define PO1030_GLOBALIBIAS 0x12 -#define PO1030_PIXELIBIAS 0x13 - -#define PO1030_GLOBALGAIN 0x15 -#define PO1030_RED_GAIN 0x16 -#define PO1030_GREEN_1_GAIN 0x17 -#define PO1030_BLUE_GAIN 0x18 -#define PO1030_GREEN_2_GAIN 0x19 - -#define PO1030_INTEGLINES_H 0x1a -#define PO1030_INTEGLINES_M 0x1b -#define PO1030_INTEGLINES_L 0x1c - -#define PO1030_CONTROL1 0x1d -#define PO1030_CONTROL2 0x1e -#define PO1030_CONTROL3 0x1f -#define PO1030_CONTROL4 0x20 - -#define PO1030_PERIOD50_H 0x23 -#define PO1030_PERIOD50_L 0x24 -#define PO1030_PERIOD60_H 0x25 -#define PO1030_PERIOD60_L 0x26 -#define PO1030_REGCLK167 0x27 -#define PO1030_FLICKER_DELTA50 0x28 -#define PO1030_FLICKERDELTA60 0x29 - -#define PO1030_ADCOFFSET 0x2c - -/* Gamma Correction Coeffs */ -#define PO1030_GC0 0x2d -#define PO1030_GC1 0x2e -#define PO1030_GC2 0x2f -#define PO1030_GC3 0x30 -#define PO1030_GC4 0x31 -#define PO1030_GC5 0x32 -#define PO1030_GC6 0x33 -#define PO1030_GC7 0x34 - -/* Color Transform Matrix */ -#define PO1030_CT0 0x35 -#define PO1030_CT1 0x36 -#define PO1030_CT2 0x37 -#define PO1030_CT3 0x38 -#define PO1030_CT4 0x39 -#define PO1030_CT5 0x3a -#define PO1030_CT6 0x3b -#define PO1030_CT7 0x3c -#define PO1030_CT8 0x3d - -#define PO1030_AUTOCTRL1 0x3e -#define PO1030_AUTOCTRL2 0x3f - -#define PO1030_YTARGET 0x40 -#define PO1030_GLOBALGAINMIN 0x41 -#define PO1030_GLOBALGAINMAX 0x42 - -#define PO1030_AWB_RED_TUNING 0x47 -#define PO1030_AWB_BLUE_TUNING 0x48 - -/* Output format control */ -#define PO1030_OUTFORMCTRL1 0x5a -#define PO1030_OUTFORMCTRL2 0x5b -#define PO1030_OUTFORMCTRL3 0x5c -#define PO1030_OUTFORMCTRL4 0x5d -#define PO1030_OUTFORMCTRL5 0x5e - -#define PO1030_EDGE_ENH_OFF 0x5f -#define PO1030_EGA 0x60 - -#define PO1030_Cb_U_GAIN 0x63 -#define PO1030_Cr_V_GAIN 0x64 - -#define PO1030_YCONTRAST 0x74 -#define PO1030_YSATURATION 0x75 - -#define PO1030_HFLIP (1 << 7) -#define PO1030_VFLIP (1 << 6) - -#define PO1030_HREF_ENABLE (1 << 6) - -#define PO1030_RAW_RGB_BAYER 0x4 - -#define PO1030_FRAME_EQUAL (1 << 3) -#define PO1030_AUTO_SUBSAMPLING (1 << 4) - -#define PO1030_WEIGHT_WIN_2X (1 << 3) - -#define PO1030_SHUTTER_MODE (1 << 6) -#define PO1030_AUTO_SUBSAMPLING (1 << 4) -#define PO1030_FRAME_EQUAL (1 << 3) - -#define PO1030_SENSOR_RESET (1 << 5) - -#define PO1030_SUBSAMPLING (1 << 6) - -/*****************************************************************************/ - -#define PO1030_GLOBAL_GAIN_DEFAULT 0x12 -#define PO1030_EXPOSURE_DEFAULT 0x0085 -#define PO1030_BLUE_GAIN_DEFAULT 0x36 -#define PO1030_RED_GAIN_DEFAULT 0x36 -#define PO1030_GREEN_GAIN_DEFAULT 0x40 - -/*****************************************************************************/ - -/* Kernel module parameters */ -extern int force_sensor; -extern bool dump_sensor; - -int po1030_probe(struct sd *sd); -int po1030_init(struct sd *sd); -int po1030_start(struct sd *sd); -void po1030_disconnect(struct sd *sd); - -static const struct m5602_sensor po1030 = { - .name = "PO1030", - - .i2c_slave_id = 0xdc, - .i2c_regW = 1, - - .probe = po1030_probe, - .init = po1030_init, - .start = po1030_start, - .disconnect = po1030_disconnect, -}; - -static const unsigned char preinit_po1030[][3] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, - - {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00} -}; - -static const unsigned char init_po1030[][3] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - - {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - - {SENSOR, PO1030_AUTOCTRL2, 0x04}, - - {SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER}, - {SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X}, - - {SENSOR, PO1030_CONTROL2, 0x03}, - {SENSOR, 0x21, 0x90}, - {SENSOR, PO1030_YTARGET, 0x60}, - {SENSOR, 0x59, 0x13}, - {SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE}, - {SENSOR, PO1030_EDGE_ENH_OFF, 0x00}, - {SENSOR, PO1030_EGA, 0x80}, - {SENSOR, 0x78, 0x14}, - {SENSOR, 0x6f, 0x01}, - {SENSOR, PO1030_GLOBALGAINMAX, 0x14}, - {SENSOR, PO1030_Cb_U_GAIN, 0x38}, - {SENSOR, PO1030_Cr_V_GAIN, 0x38}, - {SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE | - PO1030_AUTO_SUBSAMPLING | - PO1030_FRAME_EQUAL}, - {SENSOR, PO1030_GC0, 0x10}, - {SENSOR, PO1030_GC1, 0x20}, - {SENSOR, PO1030_GC2, 0x40}, - {SENSOR, PO1030_GC3, 0x60}, - {SENSOR, PO1030_GC4, 0x80}, - {SENSOR, PO1030_GC5, 0xa0}, - {SENSOR, PO1030_GC6, 0xc0}, - {SENSOR, PO1030_GC7, 0xff}, - - /* Set the width to 751 */ - {SENSOR, PO1030_FRAMEWIDTH_H, 0x02}, - {SENSOR, PO1030_FRAMEWIDTH_L, 0xef}, - - /* Set the height to 540 */ - {SENSOR, PO1030_FRAMEHEIGHT_H, 0x02}, - {SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c}, - - /* Set the x window to 1 */ - {SENSOR, PO1030_WINDOWX_H, 0x00}, - {SENSOR, PO1030_WINDOWX_L, 0x01}, - - /* Set the y window to 1 */ - {SENSOR, PO1030_WINDOWY_H, 0x00}, - {SENSOR, PO1030_WINDOWY_L, 0x01}, - - /* with a very low lighted environment increase the exposure but - * decrease the FPS (Frame Per Second) */ - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, -}; -#endif diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c deleted file mode 100644 index cc8ec3f7e8dc..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Driver for the s5k4aa sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "m5602_s5k4aa.h" - -static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val); -static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val); - -static - const - struct dmi_system_id s5k4aa_vflip_dmi_table[] = { - { - .ident = "BRUNEINIT", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"), - DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"), - DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001") - } - }, { - .ident = "Fujitsu-Siemens Amilo Xa 2528", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528") - } - }, { - .ident = "Fujitsu-Siemens Amilo Xi 2428", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428") - } - }, { - .ident = "Fujitsu-Siemens Amilo Xi 2528", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528") - } - }, { - .ident = "Fujitsu-Siemens Amilo Xi 2550", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") - } - }, { - .ident = "Fujitsu-Siemens Amilo Pa 2548", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548") - } - }, { - .ident = "MSI GX700", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), - DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), - DMI_MATCH(DMI_BIOS_DATE, "12/02/2008") - } - }, { - .ident = "MSI GX700", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), - DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), - DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") - } - }, { - .ident = "MSI GX700", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), - DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), - DMI_MATCH(DMI_BIOS_DATE, "07/19/2007") - } - }, { - .ident = "MSI GX700/GX705/EX700", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), - DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700") - } - }, { - .ident = "MSI L735", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), - DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X") - } - }, { - .ident = "Lenovo Y300", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"), - DMI_MATCH(DMI_PRODUCT_NAME, "Y300") - } - }, - { } -}; - -static struct v4l2_pix_format s5k4aa_modes[] = { - { - 640, - 480, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 640 * 480, - .bytesperline = 640, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, - { - 1280, - 1024, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 1280 * 1024, - .bytesperline = 1280, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } -}; - -static const struct ctrl s5k4aa_ctrls[] = { -#define VFLIP_IDX 0 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k4aa_set_vflip, - .get = s5k4aa_get_vflip - }, -#define HFLIP_IDX 1 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k4aa_set_hflip, - .get = s5k4aa_get_hflip - }, -#define GAIN_IDX 2 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = S5K4AA_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k4aa_set_gain, - .get = s5k4aa_get_gain - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 13, - .maximum = 0xfff, - .step = 1, - .default_value = 0x100, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k4aa_set_exposure, - .get = s5k4aa_get_exposure - }, -#define NOISE_SUPP_IDX 4 - { - { - .id = V4L2_CID_PRIVATE_BASE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Noise suppression (smoothing)", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - .set = s5k4aa_set_noise, - .get = s5k4aa_get_noise - }, -#define BRIGHTNESS_IDX 5 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0x1f, - .step = 1, - .default_value = S5K4AA_DEFAULT_BRIGHTNESS, - }, - .set = s5k4aa_set_brightness, - .get = s5k4aa_get_brightness - }, - -}; - -static void s5k4aa_dump_registers(struct sd *sd); - -int s5k4aa_probe(struct sd *sd) -{ - u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75}; - int i, err = 0; - s32 *sensor_settings; - - if (force_sensor) { - if (force_sensor == S5K4AA_SENSOR) { - pr_info("Forcing a %s sensor\n", s5k4aa.name); - goto sensor_found; - } - /* If we want to force another sensor, don't try to probe this - * one */ - return -ENODEV; - } - - PDEBUG(D_PROBE, "Probing for a s5k4aa sensor"); - - /* Preinit the sensor */ - for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) { - u8 data[2] = {0x00, 0x00}; - - switch (preinit_s5k4aa[i][0]) { - case BRIDGE: - err = m5602_write_bridge(sd, - preinit_s5k4aa[i][1], - preinit_s5k4aa[i][2]); - break; - - case SENSOR: - data[0] = preinit_s5k4aa[i][2]; - err = m5602_write_sensor(sd, - preinit_s5k4aa[i][1], - data, 1); - break; - - case SENSOR_LONG: - data[0] = preinit_s5k4aa[i][2]; - data[1] = preinit_s5k4aa[i][3]; - err = m5602_write_sensor(sd, - preinit_s5k4aa[i][1], - data, 2); - break; - default: - pr_info("Invalid stream command, exiting init\n"); - return -EINVAL; - } - } - - /* Test some registers, but we don't know their exact meaning yet */ - if (m5602_read_sensor(sd, 0x00, prod_id, 2)) - return -ENODEV; - if (m5602_read_sensor(sd, 0x02, prod_id+2, 2)) - return -ENODEV; - if (m5602_read_sensor(sd, 0x04, prod_id+4, 2)) - return -ENODEV; - - if (memcmp(prod_id, expected_prod_id, sizeof(prod_id))) - return -ENODEV; - else - pr_info("Detected a s5k4aa sensor\n"); - -sensor_found: - sensor_settings = kmalloc( - ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - - sd->gspca_dev.cam.cam_mode = s5k4aa_modes; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes); - sd->desc->ctrls = s5k4aa_ctrls; - sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls); - - for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++) - sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - - return 0; -} - -int s5k4aa_start(struct sd *sd) -{ - int i, err = 0; - u8 data[2]; - struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; - - switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) { - case 1280: - PDEBUG(D_V4L2, "Configuring camera for SXGA mode"); - - for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) { - switch (SXGA_s5k4aa[i][0]) { - case BRIDGE: - err = m5602_write_bridge(sd, - SXGA_s5k4aa[i][1], - SXGA_s5k4aa[i][2]); - break; - - case SENSOR: - data[0] = SXGA_s5k4aa[i][2]; - err = m5602_write_sensor(sd, - SXGA_s5k4aa[i][1], - data, 1); - break; - - case SENSOR_LONG: - data[0] = SXGA_s5k4aa[i][2]; - data[1] = SXGA_s5k4aa[i][3]; - err = m5602_write_sensor(sd, - SXGA_s5k4aa[i][1], - data, 2); - break; - - default: - pr_err("Invalid stream command, exiting init\n"); - return -EINVAL; - } - } - err = s5k4aa_set_noise(&sd->gspca_dev, 0); - if (err < 0) - return err; - break; - - case 640: - PDEBUG(D_V4L2, "Configuring camera for VGA mode"); - - for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) { - switch (VGA_s5k4aa[i][0]) { - case BRIDGE: - err = m5602_write_bridge(sd, - VGA_s5k4aa[i][1], - VGA_s5k4aa[i][2]); - break; - - case SENSOR: - data[0] = VGA_s5k4aa[i][2]; - err = m5602_write_sensor(sd, - VGA_s5k4aa[i][1], - data, 1); - break; - - case SENSOR_LONG: - data[0] = VGA_s5k4aa[i][2]; - data[1] = VGA_s5k4aa[i][3]; - err = m5602_write_sensor(sd, - VGA_s5k4aa[i][1], - data, 2); - break; - - default: - pr_err("Invalid stream command, exiting init\n"); - return -EINVAL; - } - } - err = s5k4aa_set_noise(&sd->gspca_dev, 1); - if (err < 0) - return err; - break; - } - if (err < 0) - return err; - - err = s5k4aa_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_brightness(&sd->gspca_dev, - sensor_settings[BRIGHTNESS_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]); - if (err < 0) - return err; - - err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - if (err < 0) - return err; - - return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); -} - -int s5k4aa_init(struct sd *sd) -{ - int i, err = 0; - - for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) { - u8 data[2] = {0x00, 0x00}; - - switch (init_s5k4aa[i][0]) { - case BRIDGE: - err = m5602_write_bridge(sd, - init_s5k4aa[i][1], - init_s5k4aa[i][2]); - break; - - case SENSOR: - data[0] = init_s5k4aa[i][2]; - err = m5602_write_sensor(sd, - init_s5k4aa[i][1], data, 1); - break; - - case SENSOR_LONG: - data[0] = init_s5k4aa[i][2]; - data[1] = init_s5k4aa[i][3]; - err = m5602_write_sensor(sd, - init_s5k4aa[i][1], data, 2); - break; - default: - pr_info("Invalid stream command, exiting init\n"); - return -EINVAL; - } - } - - if (dump_sensor) - s5k4aa_dump_registers(sd); - - return err; -} - -static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - PDEBUG(D_V4L2, "Read exposure %d", *val); - - return 0; -} - -static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - sensor_settings[EXPOSURE_IDX] = val; - PDEBUG(D_V4L2, "Set exposure to %d", val); - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - return err; - data = (val >> 8) & 0xff; - err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); - if (err < 0) - return err; - data = val & 0xff; - err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); - - return err; -} - -static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - -static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - sensor_settings[VFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; - - if (dmi_check_system(s5k4aa_vflip_dmi_table)) - val = !val; - - data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7)); - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - if (err < 0) - return err; - if (val) - data &= 0xfe; - else - data |= 0x01; - err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - return err; -} - -static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return 0; -} - -static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - sensor_settings[HFLIP_IDX] = val; - - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; - - if (dmi_check_system(s5k4aa_vflip_dmi_table)) - val = !val; - - data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; - - err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - if (err < 0) - return err; - if (val) - data &= 0xfe; - else - data |= 0x01; - err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - return err; -} - -static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - PDEBUG(D_V4L2, "Read gain %d", *val); - return 0; -} - -static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - sensor_settings[GAIN_IDX] = val; - - PDEBUG(D_V4L2, "Set gain to %d", val); - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - return err; - - data = val & 0xff; - err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1); - - return err; -} - -static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BRIGHTNESS_IDX]; - PDEBUG(D_V4L2, "Read brightness %d", *val); - return 0; -} - -static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - sensor_settings[BRIGHTNESS_IDX] = val; - - PDEBUG(D_V4L2, "Set brightness to %d", val); - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - return err; - - data = val & 0xff; - return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1); -} - -static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[NOISE_SUPP_IDX]; - PDEBUG(D_V4L2, "Read noise %d", *val); - return 0; -} - -static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - sensor_settings[NOISE_SUPP_IDX] = val; - - PDEBUG(D_V4L2, "Set noise to %d", val); - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - return err; - - data = val & 0x01; - return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1); -} - -void s5k4aa_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static void s5k4aa_dump_registers(struct sd *sd) -{ - int address; - u8 page, old_page; - m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); - for (page = 0; page < 16; page++) { - m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); - pr_info("Dumping the s5k4aa register state for page 0x%x\n", - page); - for (address = 0; address <= 0xff; address++) { - u8 value = 0; - m5602_read_sensor(sd, address, &value, 1); - pr_info("register 0x%x contains 0x%x\n", - address, value); - } - } - pr_info("s5k4aa register state dump complete\n"); - - for (page = 0; page < 16; page++) { - m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); - pr_info("Probing for which registers that are read/write for page 0x%x\n", - page); - for (address = 0; address <= 0xff; address++) { - u8 old_value, ctrl_value, test_value = 0xff; - - m5602_read_sensor(sd, address, &old_value, 1); - m5602_write_sensor(sd, address, &test_value, 1); - m5602_read_sensor(sd, address, &ctrl_value, 1); - - if (ctrl_value == test_value) - pr_info("register 0x%x is writeable\n", - address); - else - pr_info("register 0x%x is read only\n", - address); - - /* Restore original value */ - m5602_write_sensor(sd, address, &old_value, 1); - } - } - pr_info("Read/write register probing complete\n"); - m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); -} diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h deleted file mode 100644 index 8e0035e731c7..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Driver for the s5k4aa sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_S5K4AA_H_ -#define M5602_S5K4AA_H_ - -#include - -#include "m5602_sensor.h" - -/*****************************************************************************/ - -#define S5K4AA_PAGE_MAP 0xec - -#define S5K4AA_PAGE_MAP_0 0x00 -#define S5K4AA_PAGE_MAP_1 0x01 -#define S5K4AA_PAGE_MAP_2 0x02 - -/* Sensor register definitions for page 0x02 */ -#define S5K4AA_READ_MODE 0x03 -#define S5K4AA_ROWSTART_HI 0x04 -#define S5K4AA_ROWSTART_LO 0x05 -#define S5K4AA_COLSTART_HI 0x06 -#define S5K4AA_COLSTART_LO 0x07 -#define S5K4AA_WINDOW_HEIGHT_HI 0x08 -#define S5K4AA_WINDOW_HEIGHT_LO 0x09 -#define S5K4AA_WINDOW_WIDTH_HI 0x0a -#define S5K4AA_WINDOW_WIDTH_LO 0x0b -#define S5K4AA_GLOBAL_GAIN__ 0x0f -/* sync lost, if too low, reduces frame rate if too high */ -#define S5K4AA_H_BLANK_HI__ 0x1d -#define S5K4AA_H_BLANK_LO__ 0x1e -#define S5K4AA_EXPOSURE_HI 0x17 -#define S5K4AA_EXPOSURE_LO 0x18 -#define S5K4AA_BRIGHTNESS 0x1f /* (digital?) gain : 5 bits */ -#define S5K4AA_GAIN 0x20 /* (analogue?) gain : 7 bits */ -#define S5K4AA_NOISE_SUPP 0x37 - -#define S5K4AA_RM_ROW_SKIP_4X 0x08 -#define S5K4AA_RM_ROW_SKIP_2X 0x04 -#define S5K4AA_RM_COL_SKIP_4X 0x02 -#define S5K4AA_RM_COL_SKIP_2X 0x01 -#define S5K4AA_RM_H_FLIP 0x40 -#define S5K4AA_RM_V_FLIP 0x80 - -#define S5K4AA_DEFAULT_GAIN 0x5f -#define S5K4AA_DEFAULT_BRIGHTNESS 0x10 - -/*****************************************************************************/ - -/* Kernel module parameters */ -extern int force_sensor; -extern bool dump_sensor; - -int s5k4aa_probe(struct sd *sd); -int s5k4aa_init(struct sd *sd); -int s5k4aa_start(struct sd *sd); -void s5k4aa_disconnect(struct sd *sd); - -static const struct m5602_sensor s5k4aa = { - .name = "S5K4AA", - .i2c_slave_id = 0x5a, - .i2c_regW = 2, - - .probe = s5k4aa_probe, - .init = s5k4aa_init, - .start = s5k4aa_start, - .disconnect = s5k4aa_disconnect, -}; - -static const unsigned char preinit_s5k4aa[][4] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, - - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, - - {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00} -}; - -static const unsigned char init_s5k4aa[][4] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, - - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, - - {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00}, - {SENSOR, 0x36, 0x01, 0x00}, - {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}, - {SENSOR, 0x7b, 0xff, 0x00}, - {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, - {SENSOR, 0x0c, 0x05, 0x00}, - {SENSOR, 0x02, 0x0e, 0x00}, - {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00}, - {SENSOR, 0x37, 0x00, 0x00}, -}; - -static const unsigned char VGA_s5k4aa[][4] = { - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */ - - {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, - {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X - | S5K4AA_RM_COL_SKIP_2X, 0x00}, - /* 0x37 : Fix image stability when light is too bright and improves - * image quality in 640x480, but worsens it in 1280x1024 */ - {SENSOR, 0x37, 0x01, 0x00}, - /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */ - {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00}, - {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00}, - /* window_height_hi, window_height_lo : 960 = 0x03c0 */ - {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00}, - {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00}, - /* window_width_hi, window_width_lo : 1280 = 0x0500 */ - {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, - {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00}, - {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00}, - {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */ - {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00}, - {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00}, - {SENSOR, 0x11, 0x04, 0x00}, - {SENSOR, 0x12, 0xc3, 0x00}, - {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, - {SENSOR, 0x02, 0x0e, 0x00}, -}; - -static const unsigned char SXGA_s5k4aa[][4] = { - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - /* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - /* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */ - - {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, - {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00}, - {SENSOR, 0x37, 0x01, 0x00}, - {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00}, - {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00}, - {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00}, - {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00}, - {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, - {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00}, - {SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00}, - {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, - {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00}, - {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00}, - {SENSOR, 0x11, 0x04, 0x00}, - {SENSOR, 0x12, 0xc3, 0x00}, - {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, - {SENSOR, 0x02, 0x0e, 0x00}, -}; -#endif diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c deleted file mode 100644 index 1de743a02b02..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Driver for the s5k83a sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include "m5602_s5k83a.h" - -static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val); - -static struct v4l2_pix_format s5k83a_modes[] = { - { - 640, - 480, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 640 * 480, - .bytesperline = 640, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } -}; - -static const struct ctrl s5k83a_ctrls[] = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = S5K83A_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_gain, - .get = s5k83a_get_gain - - }, -#define BRIGHTNESS_IDX 1 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "brightness", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = S5K83A_DEFAULT_BRIGHTNESS, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_brightness, - .get = s5k83a_get_brightness, - }, -#define EXPOSURE_IDX 2 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = S5K83A_MAXIMUM_EXPOSURE, - .step = 0x01, - .default_value = S5K83A_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_exposure, - .get = s5k83a_get_exposure - }, -#define HFLIP_IDX 3 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k83a_set_hflip, - .get = s5k83a_get_hflip - }, -#define VFLIP_IDX 4 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k83a_set_vflip, - .get = s5k83a_get_vflip - } -}; - -static void s5k83a_dump_registers(struct sd *sd); -static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data); -static int s5k83a_set_led_indication(struct sd *sd, u8 val); -static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, - __s32 vflip, __s32 hflip); - -int s5k83a_probe(struct sd *sd) -{ - struct s5k83a_priv *sens_priv; - u8 prod_id = 0, ver_id = 0; - int i, err = 0; - - if (force_sensor) { - if (force_sensor == S5K83A_SENSOR) { - pr_info("Forcing a %s sensor\n", s5k83a.name); - goto sensor_found; - } - /* If we want to force another sensor, don't try to probe this - * one */ - return -ENODEV; - } - - PDEBUG(D_PROBE, "Probing for a s5k83a sensor"); - - /* Preinit the sensor */ - for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) { - u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]}; - if (preinit_s5k83a[i][0] == SENSOR) - err = m5602_write_sensor(sd, preinit_s5k83a[i][1], - data, 2); - else - err = m5602_write_bridge(sd, preinit_s5k83a[i][1], - data[0]); - } - - /* We don't know what register (if any) that contain the product id - * Just pick the first addresses that seem to produce the same results - * on multiple machines */ - if (m5602_read_sensor(sd, 0x00, &prod_id, 1)) - return -ENODEV; - - if (m5602_read_sensor(sd, 0x01, &ver_id, 1)) - return -ENODEV; - - if ((prod_id == 0xff) || (ver_id == 0xff)) - return -ENODEV; - else - pr_info("Detected a s5k83a sensor\n"); - -sensor_found: - sens_priv = kmalloc( - sizeof(struct s5k83a_priv), GFP_KERNEL); - if (!sens_priv) - return -ENOMEM; - - sens_priv->settings = - kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); - if (!sens_priv->settings) { - kfree(sens_priv); - return -ENOMEM; - } - - sd->gspca_dev.cam.cam_mode = s5k83a_modes; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); - sd->desc->ctrls = s5k83a_ctrls; - sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls); - - /* null the pointer! thread is't running now */ - sens_priv->rotation_thread = NULL; - - for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++) - sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value; - - sd->sensor_priv = sens_priv; - return 0; -} - -int s5k83a_init(struct sd *sd) -{ - int i, err = 0; - s32 *sensor_settings = - ((struct s5k83a_priv *) sd->sensor_priv)->settings; - - for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) { - u8 data[2] = {0x00, 0x00}; - - switch (init_s5k83a[i][0]) { - case BRIDGE: - err = m5602_write_bridge(sd, - init_s5k83a[i][1], - init_s5k83a[i][2]); - break; - - case SENSOR: - data[0] = init_s5k83a[i][2]; - err = m5602_write_sensor(sd, - init_s5k83a[i][1], data, 1); - break; - - case SENSOR_LONG: - data[0] = init_s5k83a[i][2]; - data[1] = init_s5k83a[i][3]; - err = m5602_write_sensor(sd, - init_s5k83a[i][1], data, 2); - break; - default: - pr_info("Invalid stream command, exiting init\n"); - return -EINVAL; - } - } - - if (dump_sensor) - s5k83a_dump_registers(sd); - - err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - if (err < 0) - return err; - - err = s5k83a_set_brightness(&sd->gspca_dev, - sensor_settings[BRIGHTNESS_IDX]); - if (err < 0) - return err; - - err = s5k83a_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); - if (err < 0) - return err; - - err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); - - return err; -} - -static int rotation_thread_function(void *data) -{ - struct sd *sd = (struct sd *) data; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - u8 reg, previous_rotation = 0; - __s32 vflip, hflip; - - set_current_state(TASK_INTERRUPTIBLE); - while (!schedule_timeout(100)) { - if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock)) - break; - - s5k83a_get_rotation(sd, ®); - if (previous_rotation != reg) { - previous_rotation = reg; - pr_info("Camera was flipped\n"); - - s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); - s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); - - if (reg) { - vflip = !vflip; - hflip = !hflip; - } - s5k83a_set_flip_real((struct gspca_dev *) sd, - vflip, hflip); - } - - mutex_unlock(&sd->gspca_dev.usb_lock); - set_current_state(TASK_INTERRUPTIBLE); - } - - /* return to "front" flip */ - if (previous_rotation) { - s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); - s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); - s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip); - } - - sens_priv->rotation_thread = NULL; - return 0; -} - -int s5k83a_start(struct sd *sd) -{ - int i, err = 0; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - /* Create another thread, polling the GPIO ports of the camera to check - if it got rotated. This is how the windows driver does it so we have - to assume that there is no better way of accomplishing this */ - sens_priv->rotation_thread = kthread_create(rotation_thread_function, - sd, "rotation thread"); - wake_up_process(sens_priv->rotation_thread); - - /* Preinit the sensor */ - for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) { - u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]}; - if (start_s5k83a[i][0] == SENSOR) - err = m5602_write_sensor(sd, start_s5k83a[i][1], - data, 2); - else - err = m5602_write_bridge(sd, start_s5k83a[i][1], - data[0]); - } - if (err < 0) - return err; - - return s5k83a_set_led_indication(sd, 1); -} - -int s5k83a_stop(struct sd *sd) -{ - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - if (sens_priv->rotation_thread) - kthread_stop(sens_priv->rotation_thread); - - return s5k83a_set_led_indication(sd, 0); -} - -void s5k83a_disconnect(struct sd *sd) -{ - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - s5k83a_stop(sd); - - sd->sensor = NULL; - kfree(sens_priv->settings); - kfree(sens_priv); -} - -static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[GAIN_IDX]; - return 0; -} - -static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 data[2]; - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[GAIN_IDX] = val; - - data[0] = 0x00; - data[1] = 0x20; - err = m5602_write_sensor(sd, 0x14, data, 2); - if (err < 0) - return err; - - data[0] = 0x01; - data[1] = 0x00; - err = m5602_write_sensor(sd, 0x0d, data, 2); - if (err < 0) - return err; - - /* FIXME: This is not sane, we need to figure out the composition - of these registers */ - data[0] = val >> 3; /* gain, high 5 bits */ - data[1] = val >> 1; /* gain, high 7 bits */ - err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2); - - return err; -} - -static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[BRIGHTNESS_IDX]; - return 0; -} - -static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 data[1]; - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[BRIGHTNESS_IDX] = val; - data[0] = val; - err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1); - return err; -} - -static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[EXPOSURE_IDX]; - return 0; -} - -static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 data[2]; - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[EXPOSURE_IDX] = val; - data[0] = 0; - data[1] = val; - err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2); - return err; -} - -static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[VFLIP_IDX]; - return 0; -} - -static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, - __s32 vflip, __s32 hflip) -{ - int err; - u8 data[1]; - struct sd *sd = (struct sd *) gspca_dev; - - data[0] = 0x05; - err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); - if (err < 0) - return err; - - /* six bit is vflip, seven is hflip */ - data[0] = S5K83A_FLIP_MASK; - data[0] = (vflip) ? data[0] | 0x40 : data[0]; - data[0] = (hflip) ? data[0] | 0x80 : data[0]; - - err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); - if (err < 0) - return err; - - data[0] = (vflip) ? 0x0b : 0x0a; - err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1); - if (err < 0) - return err; - - data[0] = (hflip) ? 0x0a : 0x0b; - err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); - return err; -} - -static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 reg; - __s32 hflip; - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[VFLIP_IDX] = val; - - s5k83a_get_hflip(gspca_dev, &hflip); - - err = s5k83a_get_rotation(sd, ®); - if (err < 0) - return err; - if (reg) { - val = !val; - hflip = !hflip; - } - - err = s5k83a_set_flip_real(gspca_dev, val, hflip); - return err; -} - -static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - *val = sens_priv->settings[HFLIP_IDX]; - return 0; -} - -static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u8 reg; - __s32 vflip; - struct sd *sd = (struct sd *) gspca_dev; - struct s5k83a_priv *sens_priv = sd->sensor_priv; - - sens_priv->settings[HFLIP_IDX] = val; - - s5k83a_get_vflip(gspca_dev, &vflip); - - err = s5k83a_get_rotation(sd, ®); - if (err < 0) - return err; - if (reg) { - val = !val; - vflip = !vflip; - } - - err = s5k83a_set_flip_real(gspca_dev, vflip, val); - return err; -} - -static int s5k83a_set_led_indication(struct sd *sd, u8 val) -{ - int err = 0; - u8 data[1]; - - err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data); - if (err < 0) - return err; - - if (val) - data[0] = data[0] | S5K83A_GPIO_LED_MASK; - else - data[0] = data[0] & ~S5K83A_GPIO_LED_MASK; - - err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]); - - return err; -} - -/* Get camera rotation on Acer notebooks */ -static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data) -{ - int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data); - *reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1; - return err; -} - -static void s5k83a_dump_registers(struct sd *sd) -{ - int address; - u8 page, old_page; - m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); - - for (page = 0; page < 16; page++) { - m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); - pr_info("Dumping the s5k83a register state for page 0x%x\n", - page); - for (address = 0; address <= 0xff; address++) { - u8 val = 0; - m5602_read_sensor(sd, address, &val, 1); - pr_info("register 0x%x contains 0x%x\n", address, val); - } - } - pr_info("s5k83a register state dump complete\n"); - - for (page = 0; page < 16; page++) { - m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); - pr_info("Probing for which registers that are read/write for page 0x%x\n", - page); - for (address = 0; address <= 0xff; address++) { - u8 old_val, ctrl_val, test_val = 0xff; - - m5602_read_sensor(sd, address, &old_val, 1); - m5602_write_sensor(sd, address, &test_val, 1); - m5602_read_sensor(sd, address, &ctrl_val, 1); - - if (ctrl_val == test_val) - pr_info("register 0x%x is writeable\n", - address); - else - pr_info("register 0x%x is read only\n", - address); - - /* Restore original val */ - m5602_write_sensor(sd, address, &old_val, 1); - } - } - pr_info("Read/write register probing complete\n"); - m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); -} diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h deleted file mode 100644 index 79952247b534..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Driver for the s5k83a sensor - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_S5K83A_H_ -#define M5602_S5K83A_H_ - -#include "m5602_sensor.h" - -#define S5K83A_FLIP 0x01 -#define S5K83A_HFLIP_TUNE 0x03 -#define S5K83A_VFLIP_TUNE 0x05 -#define S5K83A_BRIGHTNESS 0x0a -#define S5K83A_EXPOSURE 0x18 -#define S5K83A_GAIN 0x1b -#define S5K83A_PAGE_MAP 0xec - -#define S5K83A_DEFAULT_GAIN 0x71 -#define S5K83A_DEFAULT_BRIGHTNESS 0x7e -#define S5K83A_DEFAULT_EXPOSURE 0x00 -#define S5K83A_MAXIMUM_EXPOSURE 0x3c -#define S5K83A_FLIP_MASK 0x10 -#define S5K83A_GPIO_LED_MASK 0x10 -#define S5K83A_GPIO_ROTATION_MASK 0x40 - -/*****************************************************************************/ - -/* Kernel module parameters */ -extern int force_sensor; -extern bool dump_sensor; - -int s5k83a_probe(struct sd *sd); -int s5k83a_init(struct sd *sd); -int s5k83a_start(struct sd *sd); -int s5k83a_stop(struct sd *sd); -void s5k83a_disconnect(struct sd *sd); - -static const struct m5602_sensor s5k83a = { - .name = "S5K83A", - .probe = s5k83a_probe, - .init = s5k83a_init, - .start = s5k83a_start, - .stop = s5k83a_stop, - .disconnect = s5k83a_disconnect, - .i2c_slave_id = 0x5a, - .i2c_regW = 2, -}; - -struct s5k83a_priv { - /* We use another thread periodically - probing the orientation of the camera */ - struct task_struct *rotation_thread; - s32 *settings; -}; - -static const unsigned char preinit_s5k83a[][4] = { - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, - - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, -}; - -/* This could probably be considerably shortened. - I don't have the hardware to experiment with it, patches welcome -*/ -static const unsigned char init_s5k83a[][4] = { - /* The following sequence is useless after a clean boot - but is necessary after resume from suspend */ - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, - - {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, - {SENSOR, 0xaf, 0x01, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}, - {SENSOR, 0x7b, 0xff, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR, 0x01, 0x50, 0x00}, - {SENSOR, 0x12, 0x20, 0x00}, - {SENSOR, 0x17, 0x40, 0x00}, - {SENSOR, 0x1c, 0x00, 0x00}, - {SENSOR, 0x02, 0x70, 0x00}, - {SENSOR, 0x03, 0x0b, 0x00}, - {SENSOR, 0x04, 0xf0, 0x00}, - {SENSOR, 0x05, 0x0b, 0x00}, - {SENSOR, 0x06, 0x71, 0x00}, - {SENSOR, 0x07, 0xe8, 0x00}, /* 488 */ - {SENSOR, 0x08, 0x02, 0x00}, - {SENSOR, 0x09, 0x88, 0x00}, /* 648 */ - {SENSOR, 0x14, 0x00, 0x00}, - {SENSOR, 0x15, 0x20, 0x00}, /* 32 */ - {SENSOR, 0x19, 0x00, 0x00}, - {SENSOR, 0x1a, 0x98, 0x00}, /* 152 */ - {SENSOR, 0x0f, 0x02, 0x00}, - {SENSOR, 0x10, 0xe5, 0x00}, /* 741 */ - /* normal colors - (this is value after boot, but after tries can be different) */ - {SENSOR, 0x00, 0x06, 0x00}, -}; - -static const unsigned char start_s5k83a[][4] = { - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */ - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, -}; -#endif diff --git a/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/video/gspca/m5602/m5602_sensor.h deleted file mode 100644 index edff4f1f586f..000000000000 --- a/drivers/media/video/gspca/m5602/m5602_sensor.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * USB Driver for ALi m5602 based webcams - * - * Copyright (C) 2008 Erik Andrén - * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. - * Copyright (C) 2005 m5603x Linux Driver Project - * - * Portions of code to USB interface and ALi driver software, - * Copyright (c) 2006 Willem Duinker - * v4l2 interface modeled after the V4L2 driver - * for SN9C10x PC Camera Controllers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#ifndef M5602_SENSOR_H_ -#define M5602_SENSOR_H_ - -#include "m5602_bridge.h" - -#define M5602_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 0) -#define M5602_V4L2_CID_NOISE_SUPPRESION (V4L2_CID_PRIVATE_BASE + 1) - -/* Enumerates all supported sensors */ -enum sensors { - OV9650_SENSOR = 1, - S5K83A_SENSOR = 2, - S5K4AA_SENSOR = 3, - MT9M111_SENSOR = 4, - PO1030_SENSOR = 5, - OV7660_SENSOR = 6, -}; - -/* Enumerates all possible instruction types */ -enum instruction { - BRIDGE, - SENSOR, - SENSOR_LONG -}; - -struct m5602_sensor { - /* Defines the name of a sensor */ - char name[32]; - - /* What i2c address the sensor is connected to */ - u8 i2c_slave_id; - - /* Width of each i2c register (in bytes) */ - u8 i2c_regW; - - /* Probes if the sensor is connected */ - int (*probe)(struct sd *sd); - - /* Performs a initialization sequence */ - int (*init)(struct sd *sd); - - /* Executed when the camera starts to send data */ - int (*start)(struct sd *sd); - - /* Executed when the camera ends to send data */ - int (*stop)(struct sd *sd); - - /* Executed when the device is disconnected */ - void (*disconnect)(struct sd *sd); -}; - -#endif diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c deleted file mode 100644 index ff2c5abf115b..000000000000 --- a/drivers/media/video/gspca/mars.c +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Mars-Semi MR97311A library - * Copyright (C) 2005 - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "mars" - -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); -MODULE_LICENSE("GPL"); - -#define QUALITY 50 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *sharpness; - struct v4l2_ctrl *gamma; - struct { /* illuminator control cluster */ - struct v4l2_ctrl *illum_top; - struct v4l2_ctrl *illum_bottom; - }; - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -/* V4L2 controls supported by the driver */ -static void setbrightness(struct gspca_dev *gspca_dev, s32 val); -static void setcolors(struct gspca_dev *gspca_dev, s32 val); -static void setgamma(struct gspca_dev *gspca_dev, s32 val); -static void setsharpness(struct gspca_dev *gspca_dev, s32 val); - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, -}; - -static const __u8 mi_data[0x20] = { -/* 01 02 03 04 05 06 07 08 */ - 0x48, 0x22, 0x01, 0x47, 0x10, 0x00, 0x00, 0x00, -/* 09 0a 0b 0c 0d 0e 0f 10 */ - 0x00, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, -/* 11 12 13 14 15 16 17 18 */ - 0x30, 0x00, 0x04, 0x00, 0x06, 0x01, 0xe2, 0x02, -/* 19 1a 1b 1c 1d 1e 1f 20 */ - 0x82, 0x00, 0x20, 0x17, 0x80, 0x08, 0x0c, 0x00 -}; - -/* write bytes from gspca_dev->usb_buf */ -static void reg_w(struct gspca_dev *gspca_dev, - int len) -{ - int alen, ret; - - if (gspca_dev->usb_err < 0) - return; - - ret = usb_bulk_msg(gspca_dev->dev, - usb_sndbulkpipe(gspca_dev->dev, 4), - gspca_dev->usb_buf, - len, - &alen, - 500); /* timeout in milliseconds */ - if (ret < 0) { - pr_err("reg write [%02x] error %d\n", - gspca_dev->usb_buf[0], ret); - gspca_dev->usb_err = ret; - } -} - -static void mi_w(struct gspca_dev *gspca_dev, - u8 addr, - u8 value) -{ - gspca_dev->usb_buf[0] = 0x1f; - gspca_dev->usb_buf[1] = 0; /* control byte */ - gspca_dev->usb_buf[2] = addr; - gspca_dev->usb_buf[3] = value; - - reg_w(gspca_dev, 4); -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - gspca_dev->usb_buf[0] = 0x61; - gspca_dev->usb_buf[1] = val; - reg_w(gspca_dev, 2); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - gspca_dev->usb_buf[0] = 0x5f; - gspca_dev->usb_buf[1] = val << 3; - gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04; - reg_w(gspca_dev, 3); -} - -static void setgamma(struct gspca_dev *gspca_dev, s32 val) -{ - gspca_dev->usb_buf[0] = 0x06; - gspca_dev->usb_buf[1] = val * 0x40; - reg_w(gspca_dev, 2); -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - gspca_dev->usb_buf[0] = 0x67; - gspca_dev->usb_buf[1] = val * 4 + 3; - reg_w(gspca_dev, 2); -} - -static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom) -{ - /* both are off if not streaming */ - gspca_dev->usb_buf[0] = 0x22; - if (top) - gspca_dev->usb_buf[1] = 0x76; - else if (bottom) - gspca_dev->usb_buf[1] = 0x7a; - else - gspca_dev->usb_buf[1] = 0x7e; - reg_w(gspca_dev, 2); -} - -static int mars_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (ctrl->id == V4L2_CID_ILLUMINATORS_1) { - /* only one can be on at a time */ - if (ctrl->is_new && ctrl->val) - sd->illum_bottom->val = 0; - if (sd->illum_bottom->is_new && sd->illum_bottom->val) - sd->illum_top->val = 0; - } - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAMMA: - setgamma(gspca_dev, ctrl->val); - break; - case V4L2_CID_ILLUMINATORS_1: - setilluminators(gspca_dev, sd->illum_top->val, - sd->illum_bottom->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - default: - return -EINVAL; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops mars_ctrl_ops = { - .s_ctrl = mars_s_ctrl, -}; - -/* this function is called at probe time */ -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 6); - sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 30, 1, 15); - sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 200); - sd->gamma = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, - V4L2_CID_GAMMA, 0, 3, 1, 1); - sd->sharpness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 2, 1, 1); - sd->illum_top = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, - V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); - sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE; - sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, - V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); - sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE; - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_cluster(2, &sd->illum_top); - return 0; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam; - - cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 *data; - int i; - - /* create the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x21); /* JPEG 422 */ - jpeg_set_qual(sd->jpeg_hdr, QUALITY); - - data = gspca_dev->usb_buf; - - data[0] = 0x01; /* address */ - data[1] = 0x01; - reg_w(gspca_dev, 2); - - /* - Initialize the MR97113 chip register - */ - data[0] = 0x00; /* address */ - data[1] = 0x0c | 0x01; /* reg 0 */ - data[2] = 0x01; /* reg 1 */ - data[3] = gspca_dev->width / 8; /* h_size , reg 2 */ - data[4] = gspca_dev->height / 8; /* v_size , reg 3 */ - data[5] = 0x30; /* reg 4, MI, PAS5101 : - * 0x30 for 24mhz , 0x28 for 12mhz */ - data[6] = 0x02; /* reg 5, H start - was 0x04 */ - data[7] = v4l2_ctrl_g_ctrl(sd->gamma) * 0x40; /* reg 0x06: gamma */ - data[8] = 0x01; /* reg 7, V start - was 0x03 */ -/* if (h_size == 320 ) */ -/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ -/* else */ - data[9] = 0x52; /* reg 8, 24MHz, no scale down */ -/*jfm: from win trace*/ - data[10] = 0x18; - - reg_w(gspca_dev, 11); - - data[0] = 0x23; /* address */ - data[1] = 0x09; /* reg 35, append frame header */ - - reg_w(gspca_dev, 2); - - data[0] = 0x3c; /* address */ -/* if (gspca_dev->width == 1280) */ -/* data[1] = 200; * reg 60, pc-cam frame size - * (unit: 4KB) 800KB */ -/* else */ - data[1] = 50; /* 50 reg 60, pc-cam frame size - * (unit: 4KB) 200KB */ - reg_w(gspca_dev, 2); - - /* auto dark-gain */ - data[0] = 0x5e; /* address */ - data[1] = 0; /* reg 94, Y Gain (auto) */ -/*jfm: from win trace*/ - /* reg 0x5f/0x60 (LE) = saturation */ - /* h (60): xxxx x100 - * l (5f): xxxx x000 */ - data[2] = v4l2_ctrl_g_ctrl(sd->saturation) << 3; - data[3] = ((v4l2_ctrl_g_ctrl(sd->saturation) >> 2) & 0xf8) | 0x04; - data[4] = v4l2_ctrl_g_ctrl(sd->brightness); /* reg 0x61 = brightness */ - data[5] = 0x00; - - reg_w(gspca_dev, 6); - - data[0] = 0x67; -/*jfm: from win trace*/ - data[1] = v4l2_ctrl_g_ctrl(sd->sharpness) * 4 + 3; - data[2] = 0x14; - reg_w(gspca_dev, 3); - - data[0] = 0x69; - data[1] = 0x2f; - data[2] = 0x28; - data[3] = 0x42; - reg_w(gspca_dev, 4); - - data[0] = 0x63; - data[1] = 0x07; - reg_w(gspca_dev, 2); -/*jfm: win trace - many writes here to reg 0x64*/ - - /* initialize the MI sensor */ - for (i = 0; i < sizeof mi_data; i++) - mi_w(gspca_dev, i + 1, mi_data[i]); - - data[0] = 0x00; - data[1] = 0x4d; /* ISOC transferring enable... */ - reg_w(gspca_dev, 2); - - setilluminators(gspca_dev, v4l2_ctrl_g_ctrl(sd->illum_top), - v4l2_ctrl_g_ctrl(sd->illum_bottom)); - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (v4l2_ctrl_g_ctrl(sd->illum_top) || - v4l2_ctrl_g_ctrl(sd->illum_bottom)) { - setilluminators(gspca_dev, false, false); - msleep(20); - } - - gspca_dev->usb_buf[0] = 1; - gspca_dev->usb_buf[1] = 0; - reg_w(gspca_dev, 2); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - int p; - - if (len < 6) { -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - } - for (p = 0; p < len - 6; p++) { - if (data[0 + p] == 0xff - && data[1 + p] == 0xff - && data[2 + p] == 0x00 - && data[3 + p] == 0xff - && data[4 + p] == 0x96) { - if (data[5 + p] == 0x64 - || data[5 + p] == 0x65 - || data[5 + p] == 0x66 - || data[5 + p] == 0x67) { - PDEBUG(D_PACK, "sof offset: %d len: %d", - p, len); - gspca_frame_add(gspca_dev, LAST_PACKET, - data, p); - - /* put the JPEG header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - data += p + 16; - len -= p + 16; - break; - } - } - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x093a, 0x050f)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c deleted file mode 100644 index 8f4714df5990..000000000000 --- a/drivers/media/video/gspca/mr97310a.c +++ /dev/null @@ -1,1091 +0,0 @@ -/* - * Mars MR97310A library - * - * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is - * Copyright (C) 2009 Kyle Guinn - * - * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ - * and for the routines for detecting and classifying these various cameras, - * is Copyright (C) 2009 Theodore Kilgore - * - * Support for the control settings for the CIF cameras is - * Copyright (C) 2009 Hans de Goede and - * Thomas Kaiser - * - * Support for the control settings for the VGA cameras is - * Copyright (C) 2009 Theodore Kilgore - * - * Several previously unsupported cameras are owned and have been tested by - * Hans de Goede and - * Thomas Kaiser and - * Theodore Kilgore and - * Edmond Rodriguez and - * Aurelien Jacobs - * - * The MR97311A support in gspca/mars.c has been helpful in understanding some - * of the registers in these cameras. - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "mr97310a" - -#include "gspca.h" - -#define CAM_TYPE_CIF 0 -#define CAM_TYPE_VGA 1 - -#define MR97310A_BRIGHTNESS_DEFAULT 0 - -#define MR97310A_EXPOSURE_MIN 0 -#define MR97310A_EXPOSURE_MAX 4095 -#define MR97310A_EXPOSURE_DEFAULT 1000 - -#define MR97310A_GAIN_MIN 0 -#define MR97310A_GAIN_MAX 31 -#define MR97310A_GAIN_DEFAULT 25 - -#define MR97310A_CONTRAST_MIN 0 -#define MR97310A_CONTRAST_MAX 31 -#define MR97310A_CONTRAST_DEFAULT 23 - -#define MR97310A_CS_GAIN_MIN 0 -#define MR97310A_CS_GAIN_MAX 0x7ff -#define MR97310A_CS_GAIN_DEFAULT 0x110 - -#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000) -#define MR97310A_MIN_CLOCKDIV_MIN 3 -#define MR97310A_MIN_CLOCKDIV_MAX 8 -#define MR97310A_MIN_CLOCKDIV_DEFAULT 3 - -MODULE_AUTHOR("Kyle Guinn ," - "Theodore Kilgore "); -MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* global parameters */ -static int force_sensor_type = -1; -module_param(force_sensor_type, int, 0644); -MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct { /* exposure/min_clockdiv control cluster */ - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *min_clockdiv; - }; - u8 sof_read; - u8 cam_type; /* 0 is CIF and 1 is VGA */ - u8 sensor_type; /* We use 0 and 1 here, too. */ - u8 do_lcd_stop; - u8 adj_colors; -}; - -struct sensor_w_data { - u8 reg; - u8 flags; - u8 data[16]; - int len; -}; - -static void sd_stopN(struct gspca_dev *gspca_dev); - -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 4}, - {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/* the bytes to write are in gspca_dev->usb_buf */ -static int mr_write(struct gspca_dev *gspca_dev, int len) -{ - int rc; - - rc = usb_bulk_msg(gspca_dev->dev, - usb_sndbulkpipe(gspca_dev->dev, 4), - gspca_dev->usb_buf, len, NULL, 500); - if (rc < 0) - pr_err("reg write [%02x] error %d\n", - gspca_dev->usb_buf[0], rc); - return rc; -} - -/* the bytes are read into gspca_dev->usb_buf */ -static int mr_read(struct gspca_dev *gspca_dev, int len) -{ - int rc; - - rc = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 3), - gspca_dev->usb_buf, len, NULL, 500); - if (rc < 0) - pr_err("reg read [%02x] error %d\n", - gspca_dev->usb_buf[0], rc); - return rc; -} - -static int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags, - const u8 *data, int len) -{ - gspca_dev->usb_buf[0] = 0x1f; - gspca_dev->usb_buf[1] = flags; - gspca_dev->usb_buf[2] = reg; - memcpy(gspca_dev->usb_buf + 3, data, len); - - return mr_write(gspca_dev, len + 3); -} - -static int sensor_write_regs(struct gspca_dev *gspca_dev, - const struct sensor_w_data *data, int len) -{ - int i, rc; - - for (i = 0; i < len; i++) { - rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags, - data[i].data, data[i].len); - if (rc < 0) - return rc; - } - - return 0; -} - -static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 buf, confirm_reg; - int rc; - - buf = data; - if (sd->cam_type == CAM_TYPE_CIF) { - rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); - confirm_reg = sd->sensor_type ? 0x13 : 0x11; - } else { - rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1); - confirm_reg = 0x11; - } - if (rc < 0) - return rc; - - buf = 0x01; - rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); - if (rc < 0) - return rc; - - return 0; -} - -static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose) -{ - int err_code; - - gspca_dev->usb_buf[0] = reg; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; - - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; - - if (verbose) - PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg, - gspca_dev->usb_buf[0], - gspca_dev->usb_buf[1], - gspca_dev->usb_buf[2]); - - return 0; -} - -static int zero_the_pointer(struct gspca_dev *gspca_dev) -{ - __u8 *data = gspca_dev->usb_buf; - int err_code; - u8 status = 0; - int tries = 0; - - err_code = cam_get_response16(gspca_dev, 0x21, 0); - if (err_code < 0) - return err_code; - - data[0] = 0x19; - data[1] = 0x51; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - err_code = cam_get_response16(gspca_dev, 0x21, 0); - if (err_code < 0) - return err_code; - - data[0] = 0x19; - data[1] = 0xba; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - err_code = cam_get_response16(gspca_dev, 0x21, 0); - if (err_code < 0) - return err_code; - - data[0] = 0x19; - data[1] = 0x00; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - err_code = cam_get_response16(gspca_dev, 0x21, 0); - if (err_code < 0) - return err_code; - - data[0] = 0x19; - data[1] = 0x00; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - while (status != 0x0a && tries < 256) { - err_code = cam_get_response16(gspca_dev, 0x21, 0); - status = data[0]; - tries++; - if (err_code < 0) - return err_code; - } - if (status != 0x0a) - PDEBUG(D_ERR, "status is %02x", status); - - tries = 0; - while (tries < 4) { - data[0] = 0x19; - data[1] = 0x00; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - err_code = cam_get_response16(gspca_dev, 0x21, 0); - status = data[0]; - tries++; - if (err_code < 0) - return err_code; - } - - data[0] = 0x19; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; - - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; - - return 0; -} - -static int stream_start(struct gspca_dev *gspca_dev) -{ - gspca_dev->usb_buf[0] = 0x01; - gspca_dev->usb_buf[1] = 0x01; - return mr_write(gspca_dev, 2); -} - -static void stream_stop(struct gspca_dev *gspca_dev) -{ - gspca_dev->usb_buf[0] = 0x01; - gspca_dev->usb_buf[1] = 0x00; - if (mr_write(gspca_dev, 2) < 0) - PDEBUG(D_ERR, "Stream Stop failed"); -} - -static void lcd_stop(struct gspca_dev *gspca_dev) -{ - gspca_dev->usb_buf[0] = 0x19; - gspca_dev->usb_buf[1] = 0x54; - if (mr_write(gspca_dev, 2) < 0) - PDEBUG(D_ERR, "LCD Stop failed"); -} - -static int isoc_enable(struct gspca_dev *gspca_dev) -{ - gspca_dev->usb_buf[0] = 0x00; - gspca_dev->usb_buf[1] = 0x4d; /* ISOC transferring enable... */ - return mr_write(gspca_dev, 2); -} - -/* This function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - int err_code; - - cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - sd->do_lcd_stop = 0; - - /* Several of the supported CIF cameras share the same USB ID but - * require different initializations and different control settings. - * The same is true of the VGA cameras. Therefore, we are forced - * to start the initialization process in order to determine which - * camera is present. Some of the supported cameras require the - * memory pointer to be set to 0 as the very first item of business - * or else they will not stream. So we do that immediately. - */ - err_code = zero_the_pointer(gspca_dev); - if (err_code < 0) - return err_code; - - err_code = stream_start(gspca_dev); - if (err_code < 0) - return err_code; - - /* Now, the query for sensor type. */ - err_code = cam_get_response16(gspca_dev, 0x07, 1); - if (err_code < 0) - return err_code; - - if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { - sd->cam_type = CAM_TYPE_CIF; - cam->nmodes--; - /* - * All but one of the known CIF cameras share the same USB ID, - * but two different init routines are in use, and the control - * settings are different, too. We need to detect which camera - * of the two known varieties is connected! - * - * A list of known CIF cameras follows. They all report either - * 0200 for type 0 or 0300 for type 1. - * If you have another to report, please do - * - * Name sd->sensor_type reported by - * - * Sakar 56379 Spy-shot 0 T. Kilgore - * Innovage 0 T. Kilgore - * Vivitar Mini 0 H. De Goede - * Vivitar Mini 0 E. Rodriguez - * Vivitar Mini 1 T. Kilgore - * Elta-Media 8212dc 1 T. Kaiser - * Philips dig. keych. 1 T. Kilgore - * Trust Spyc@m 100 1 A. Jacobs - */ - switch (gspca_dev->usb_buf[0]) { - case 2: - sd->sensor_type = 0; - break; - case 3: - sd->sensor_type = 1; - break; - default: - pr_err("Unknown CIF Sensor id : %02x\n", - gspca_dev->usb_buf[1]); - return -ENODEV; - } - PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d", - sd->sensor_type); - } else { - sd->cam_type = CAM_TYPE_VGA; - - /* - * Here is a table of the responses to the query for sensor - * type, from the known MR97310A VGA cameras. Six different - * cameras of which five share the same USB ID. - * - * Name gspca_dev->usb_buf[] sd->sensor_type - * sd->do_lcd_stop - * Aiptek Pencam VGA+ 0300 0 1 - * ION digital 0300 0 1 - * Argus DC-1620 0450 1 0 - * Argus QuickClix 0420 1 1 - * Sakar 77379 Digital 0350 0 1 - * Sakar 1638x CyberPix 0120 0 2 - * - * Based upon these results, we assume default settings - * and then correct as necessary, as follows. - * - */ - - sd->sensor_type = 1; - sd->do_lcd_stop = 0; - sd->adj_colors = 0; - if (gspca_dev->usb_buf[0] == 0x01) { - sd->sensor_type = 2; - } else if ((gspca_dev->usb_buf[0] != 0x03) && - (gspca_dev->usb_buf[0] != 0x04)) { - pr_err("Unknown VGA Sensor id Byte 0: %02x\n", - gspca_dev->usb_buf[0]); - pr_err("Defaults assumed, may not work\n"); - pr_err("Please report this\n"); - } - /* Sakar Digital color needs to be adjusted. */ - if ((gspca_dev->usb_buf[0] == 0x03) && - (gspca_dev->usb_buf[1] == 0x50)) - sd->adj_colors = 1; - if (gspca_dev->usb_buf[0] == 0x04) { - sd->do_lcd_stop = 1; - switch (gspca_dev->usb_buf[1]) { - case 0x50: - sd->sensor_type = 0; - PDEBUG(D_PROBE, "sensor_type corrected to 0"); - break; - case 0x20: - /* Nothing to do here. */ - break; - default: - pr_err("Unknown VGA Sensor id Byte 1: %02x\n", - gspca_dev->usb_buf[1]); - pr_err("Defaults assumed, may not work\n"); - pr_err("Please report this\n"); - } - } - PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d", - sd->sensor_type); - } - /* Stop streaming as we've started it only to probe the sensor type. */ - sd_stopN(gspca_dev); - - if (force_sensor_type != -1) { - sd->sensor_type = !!force_sensor_type; - PDEBUG(D_PROBE, "Forcing sensor type to: %d", - sd->sensor_type); - } - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -static int start_cif_cam(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 *data = gspca_dev->usb_buf; - int err_code; - static const __u8 startup_string[] = { - 0x00, - 0x0d, - 0x01, - 0x00, /* Hsize/8 for 352 or 320 */ - 0x00, /* Vsize/4 for 288 or 240 */ - 0x13, /* or 0xbb, depends on sensor */ - 0x00, /* Hstart, depends on res. */ - 0x00, /* reserved ? */ - 0x00, /* Vstart, depends on res. and sensor */ - 0x50, /* 0x54 to get 176 or 160 */ - 0xc0 - }; - - /* Note: Some of the above descriptions guessed from MR97113A driver */ - - memcpy(data, startup_string, 11); - if (sd->sensor_type) - data[5] = 0xbb; - - switch (gspca_dev->width) { - case 160: - data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */ - /* fall thru */ - case 320: - default: - data[3] = 0x28; /* reg 2, H size/8 */ - data[4] = 0x3c; /* reg 3, V size/4 */ - data[6] = 0x14; /* reg 5, H start */ - data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */ - break; - case 176: - data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */ - /* fall thru */ - case 352: - data[3] = 0x2c; /* reg 2, H size/8 */ - data[4] = 0x48; /* reg 3, V size/4 */ - data[6] = 0x06; /* reg 5, H start */ - data[8] = 0x06 - sd->sensor_type; /* reg 7, V start */ - break; - } - err_code = mr_write(gspca_dev, 11); - if (err_code < 0) - return err_code; - - if (!sd->sensor_type) { - static const struct sensor_w_data cif_sensor0_init_data[] = { - {0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01, - 0x0f, 0x14, 0x0f, 0x10}, 8}, - {0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5}, - {0x12, 0x00, {0x07}, 1}, - {0x1f, 0x00, {0x06}, 1}, - {0x27, 0x00, {0x04}, 1}, - {0x29, 0x00, {0x0c}, 1}, - {0x40, 0x00, {0x40, 0x00, 0x04}, 3}, - {0x50, 0x00, {0x60}, 1}, - {0x60, 0x00, {0x06}, 1}, - {0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6}, - {0x72, 0x00, {0x1e, 0x56}, 2}, - {0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02, - 0x31, 0x80, 0x00}, 9}, - {0x11, 0x00, {0x01}, 1}, - {0, 0, {0}, 0} - }; - err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data, - ARRAY_SIZE(cif_sensor0_init_data)); - } else { /* sd->sensor_type = 1 */ - static const struct sensor_w_data cif_sensor1_init_data[] = { - /* Reg 3,4, 7,8 get set by the controls */ - {0x02, 0x00, {0x10}, 1}, - {0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */ - {0x06, 0x01, {0x00}, 1}, - {0x09, 0x02, {0x0e}, 1}, - {0x0a, 0x02, {0x05}, 1}, - {0x0b, 0x02, {0x05}, 1}, - {0x0c, 0x02, {0x0f}, 1}, - {0x0d, 0x02, {0x07}, 1}, - {0x0e, 0x02, {0x0c}, 1}, - {0x0f, 0x00, {0x00}, 1}, - {0x10, 0x00, {0x06}, 1}, - {0x11, 0x00, {0x07}, 1}, - {0x12, 0x00, {0x00}, 1}, - {0x13, 0x00, {0x01}, 1}, - {0, 0, {0}, 0} - }; - /* Without this command the cam won't work with USB-UHCI */ - gspca_dev->usb_buf[0] = 0x0a; - gspca_dev->usb_buf[1] = 0x00; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, - ARRAY_SIZE(cif_sensor1_init_data)); - } - return err_code; -} - -static int start_vga_cam(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 *data = gspca_dev->usb_buf; - int err_code; - static const __u8 startup_string[] = - {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, - 0x00, 0x50, 0xc0}; - /* What some of these mean is explained in start_cif_cam(), above */ - - memcpy(data, startup_string, 11); - if (!sd->sensor_type) { - data[5] = 0x00; - data[10] = 0x91; - } - if (sd->sensor_type == 2) { - data[5] = 0x00; - data[10] = 0x18; - } - - switch (gspca_dev->width) { - case 160: - data[9] |= 0x0c; /* reg 8, 4:1 scale down */ - /* fall thru */ - case 320: - data[9] |= 0x04; /* reg 8, 2:1 scale down */ - /* fall thru */ - case 640: - default: - data[3] = 0x50; /* reg 2, H size/8 */ - data[4] = 0x78; /* reg 3, V size/4 */ - data[6] = 0x04; /* reg 5, H start */ - data[8] = 0x03; /* reg 7, V start */ - if (sd->sensor_type == 2) { - data[6] = 2; - data[8] = 1; - } - if (sd->do_lcd_stop) - data[8] = 0x04; /* Bayer tile shifted */ - break; - - case 176: - data[9] |= 0x04; /* reg 8, 2:1 scale down */ - /* fall thru */ - case 352: - data[3] = 0x2c; /* reg 2, H size */ - data[4] = 0x48; /* reg 3, V size */ - data[6] = 0x94; /* reg 5, H start */ - data[8] = 0x63; /* reg 7, V start */ - if (sd->do_lcd_stop) - data[8] = 0x64; /* Bayer tile shifted */ - break; - } - - err_code = mr_write(gspca_dev, 11); - if (err_code < 0) - return err_code; - - if (!sd->sensor_type) { - static const struct sensor_w_data vga_sensor0_init_data[] = { - {0x01, 0x00, {0x0c, 0x00, 0x04}, 3}, - {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4}, - {0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4}, - {0x25, 0x00, {0x03, 0xa9, 0x80}, 3}, - {0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4}, - {0, 0, {0}, 0} - }; - err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, - ARRAY_SIZE(vga_sensor0_init_data)); - } else if (sd->sensor_type == 1) { - static const struct sensor_w_data color_adj[] = { - {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, - /* adjusted blue, green, red gain correct - too much blue from the Sakar Digital */ - 0x05, 0x01, 0x04}, 8} - }; - - static const struct sensor_w_data color_no_adj[] = { - {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, - /* default blue, green, red gain settings */ - 0x07, 0x00, 0x01}, 8} - }; - - static const struct sensor_w_data vga_sensor1_init_data[] = { - {0x11, 0x04, {0x01}, 1}, - {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, - /* These settings may be better for some cameras */ - /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */ - 0x00, 0x0a}, 7}, - {0x11, 0x04, {0x01}, 1}, - {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, - {0x11, 0x04, {0x01}, 1}, - {0, 0, {0}, 0} - }; - - if (sd->adj_colors) - err_code = sensor_write_regs(gspca_dev, color_adj, - ARRAY_SIZE(color_adj)); - else - err_code = sensor_write_regs(gspca_dev, color_no_adj, - ARRAY_SIZE(color_no_adj)); - - if (err_code < 0) - return err_code; - - err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, - ARRAY_SIZE(vga_sensor1_init_data)); - } else { /* sensor type == 2 */ - static const struct sensor_w_data vga_sensor2_init_data[] = { - - {0x01, 0x00, {0x48}, 1}, - {0x02, 0x00, {0x22}, 1}, - /* Reg 3 msb and 4 is lsb of the exposure setting*/ - {0x05, 0x00, {0x10}, 1}, - {0x06, 0x00, {0x00}, 1}, - {0x07, 0x00, {0x00}, 1}, - {0x08, 0x00, {0x00}, 1}, - {0x09, 0x00, {0x00}, 1}, - /* The following are used in the gain control - * which is BTW completely borked in the OEM driver - * The values for each color go from 0 to 0x7ff - *{0x0a, 0x00, {0x01}, 1}, green1 gain msb - *{0x0b, 0x00, {0x10}, 1}, green1 gain lsb - *{0x0c, 0x00, {0x01}, 1}, red gain msb - *{0x0d, 0x00, {0x10}, 1}, red gain lsb - *{0x0e, 0x00, {0x01}, 1}, blue gain msb - *{0x0f, 0x00, {0x10}, 1}, blue gain lsb - *{0x10, 0x00, {0x01}, 1}, green2 gain msb - *{0x11, 0x00, {0x10}, 1}, green2 gain lsb - */ - {0x12, 0x00, {0x00}, 1}, - {0x13, 0x00, {0x04}, 1}, /* weird effect on colors */ - {0x14, 0x00, {0x00}, 1}, - {0x15, 0x00, {0x06}, 1}, - {0x16, 0x00, {0x01}, 1}, - {0x17, 0x00, {0xe2}, 1}, /* vertical alignment */ - {0x18, 0x00, {0x02}, 1}, - {0x19, 0x00, {0x82}, 1}, /* don't mess with */ - {0x1a, 0x00, {0x00}, 1}, - {0x1b, 0x00, {0x20}, 1}, - /* {0x1c, 0x00, {0x17}, 1}, contrast control */ - {0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */ - {0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */ - {0x1f, 0x00, {0x0c}, 1}, - {0x20, 0x00, {0x00}, 1}, - {0, 0, {0}, 0} - }; - err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data, - ARRAY_SIZE(vga_sensor2_init_data)); - } - return err_code; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int err_code; - - sd->sof_read = 0; - - /* Some of the VGA cameras require the memory pointer - * to be set to 0 again. We have been forced to start the - * stream in sd_config() to detect the hardware, and closed it. - * Thus, we need here to do a completely fresh and clean start. */ - err_code = zero_the_pointer(gspca_dev); - if (err_code < 0) - return err_code; - - err_code = stream_start(gspca_dev); - if (err_code < 0) - return err_code; - - if (sd->cam_type == CAM_TYPE_CIF) { - err_code = start_cif_cam(gspca_dev); - } else { - err_code = start_vga_cam(gspca_dev); - } - if (err_code < 0) - return err_code; - - return isoc_enable(gspca_dev); -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - stream_stop(gspca_dev); - /* Not all the cams need this, but even if not, probably a good idea */ - zero_the_pointer(gspca_dev); - if (sd->do_lcd_stop) - lcd_stop(gspca_dev); -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */ - u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */ - static const u8 quick_clix_table[] = - /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ - { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15}; - if (sd->cam_type == CAM_TYPE_VGA) { - sign_reg += 4; - value_reg += 4; - } - - /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */ - if (val > 0) { - sensor_write1(gspca_dev, sign_reg, 0x00); - } else { - sensor_write1(gspca_dev, sign_reg, 0x01); - val = 257 - val; - } - /* Use lookup table for funky Argus QuickClix brightness */ - if (sd->do_lcd_stop) - val = quick_clix_table[val]; - - sensor_write1(gspca_dev, value_reg, val); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) -{ - struct sd *sd = (struct sd *) gspca_dev; - int exposure = MR97310A_EXPOSURE_DEFAULT; - u8 buf[2]; - - if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { - /* This cam does not like exposure settings < 300, - so scale 0 - 4095 to 300 - 4095 */ - exposure = (expo * 9267) / 10000 + 300; - sensor_write1(gspca_dev, 3, exposure >> 4); - sensor_write1(gspca_dev, 4, exposure & 0x0f); - } else if (sd->sensor_type == 2) { - exposure = expo; - exposure >>= 3; - sensor_write1(gspca_dev, 3, exposure >> 8); - sensor_write1(gspca_dev, 4, exposure & 0xff); - } else { - /* We have both a clock divider and an exposure register. - We first calculate the clock divider, as that determines - the maximum exposure and then we calculate the exposure - register setting (which goes from 0 - 511). - - Note our 0 - 4095 exposure is mapped to 0 - 511 - milliseconds exposure time */ - u8 clockdiv = (60 * expo + 7999) / 8000; - - /* Limit framerate to not exceed usb bandwidth */ - if (clockdiv < min_clockdiv && gspca_dev->width >= 320) - clockdiv = min_clockdiv; - else if (clockdiv < 2) - clockdiv = 2; - - if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4) - clockdiv = 4; - - /* Frame exposure time in ms = 1000 * clockdiv / 60 -> - exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ - exposure = (60 * 511 * expo) / (8000 * clockdiv); - if (exposure > 511) - exposure = 511; - - /* exposure register value is reversed! */ - exposure = 511 - exposure; - - buf[0] = exposure & 0xff; - buf[1] = exposure >> 8; - sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2); - sensor_write1(gspca_dev, 0x02, clockdiv); - } -} - -static void setgain(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 gainreg; - - if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) - sensor_write1(gspca_dev, 0x0e, val); - else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2) - for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) { - sensor_write1(gspca_dev, gainreg, val >> 8); - sensor_write1(gspca_dev, gainreg + 1, val & 0xff); - } - else - sensor_write1(gspca_dev, 0x10, val); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - sensor_write1(gspca_dev, 0x1c, val); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, sd->exposure->val, - sd->min_clockdiv ? sd->min_clockdiv->val : 0); - break; - case V4L2_CID_GAIN: - setgain(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - static const struct v4l2_ctrl_config clockdiv = { - .ops = &sd_ctrl_ops, - .id = MR97310A_CID_CLOCKDIV, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Minimum Clock Divider", - .min = MR97310A_MIN_CLOCKDIV_MIN, - .max = MR97310A_MIN_CLOCKDIV_MAX, - .step = 1, - .def = MR97310A_MIN_CLOCKDIV_DEFAULT, - }; - bool has_brightness = false; - bool has_argus_brightness = false; - bool has_contrast = false; - bool has_gain = false; - bool has_cs_gain = false; - bool has_exposure = false; - bool has_clockdiv = false; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - - /* Setup controls depending on camera type */ - if (sd->cam_type == CAM_TYPE_CIF) { - /* No brightness for sensor_type 0 */ - if (sd->sensor_type == 0) - has_exposure = has_gain = has_clockdiv = true; - else - has_exposure = has_gain = has_brightness = true; - } else { - /* All controls need to be disabled if VGA sensor_type is 0 */ - if (sd->sensor_type == 0) - ; /* no controls! */ - else if (sd->sensor_type == 2) - has_exposure = has_cs_gain = has_contrast = true; - else if (sd->do_lcd_stop) - has_exposure = has_gain = has_argus_brightness = - has_clockdiv = true; - else - has_exposure = has_gain = has_brightness = - has_clockdiv = true; - } - - /* Separate brightness control description for Argus QuickClix as it has - * different limits from the other mr97310a cameras, and separate gain - * control for Sakar CyberPix camera. */ - /* - * This control is disabled for CIF type 1 and VGA type 0 cameras. - * It does not quite act linearly for the Argus QuickClix camera, - * but it does control brightness. The values are 0 - 15 only, and - * the table above makes them act consecutively. - */ - if (has_brightness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, -254, 255, 1, - MR97310A_BRIGHTNESS_DEFAULT); - else if (has_argus_brightness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 15, 1, - MR97310A_BRIGHTNESS_DEFAULT); - if (has_contrast) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN, - MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT); - if (has_gain) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX, - 1, MR97310A_GAIN_DEFAULT); - else if (has_cs_gain) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN, - MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX, - 1, MR97310A_CS_GAIN_DEFAULT); - if (has_exposure) - sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN, - MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT); - if (has_clockdiv) - sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - if (has_exposure && has_clockdiv) - v4l2_ctrl_cluster(2, &sd->exposure); - return 0; -} - -/* Include pac common sof detection functions */ -#include "pac_common.h" - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - unsigned char *sof; - - sof = pac_find_sof(&sd->sof_read, data, len); - if (sof) { - int n; - - /* finish decoding current frame */ - n = sof - data; - if (n > sizeof pac_sof_marker) - n -= sizeof pac_sof_marker; - else - n = 0; - gspca_frame_add(gspca_dev, LAST_PACKET, - data, n); - /* Start next frame. */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - pac_sof_marker, sizeof pac_sof_marker); - len -= sof - data; - data = sof; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ - {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ - {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ - {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c deleted file mode 100644 index 44c9964b1b3e..000000000000 --- a/drivers/media/video/gspca/nw80x.c +++ /dev/null @@ -1,2110 +0,0 @@ -/* - * DivIO nw80x subdriver - * - * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr) - * Copyright (C) 2003 Sylvain Munaut - * Kjell Claesson - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "nw80x" - -#include "gspca.h" - -MODULE_AUTHOR("Jean-Francois Moine "); -MODULE_DESCRIPTION("NW80x USB Camera Driver"); -MODULE_LICENSE("GPL"); - -static int webcam; - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - u32 ae_res; - s8 ag_cnt; -#define AG_CNT_START 13 - u8 exp_too_low_cnt; - u8 exp_too_high_cnt; - - u8 bridge; - u8 webcam; -}; - -enum bridges { - BRIDGE_NW800, /* and et31x110 */ - BRIDGE_NW801, - BRIDGE_NW802, -}; -enum webcams { - Generic800, - SpaceCam, /* Trust 120 SpaceCam */ - SpaceCam2, /* other Trust 120 SpaceCam */ - Cvideopro, /* Conceptronic Video Pro */ - Dlink350c, - DS3303u, - Kr651us, - Kritter, - Mustek300, - Proscope, - Twinkle, - DvcV6, - P35u, - Generic802, - NWEBCAMS /* number of webcams */ -}; - -static const u8 webcam_chip[NWEBCAMS] = { - [Generic800] = BRIDGE_NW800, /* 06a5:0000 - * Typhoon Webcam 100 USB */ - - [SpaceCam] = BRIDGE_NW800, /* 06a5:d800 - * Trust SpaceCam120 or SpaceCam100 PORTABLE */ - - [SpaceCam2] = BRIDGE_NW800, /* 06a5:d800 - pas106 - * other Trust SpaceCam120 or SpaceCam100 PORTABLE */ - - [Cvideopro] = BRIDGE_NW802, /* 06a5:d001 - * Conceptronic Video Pro 'CVIDEOPRO USB Webcam CCD' */ - - [Dlink350c] = BRIDGE_NW802, /* 06a5:d001 - * D-Link NetQam Pro 250plus */ - - [DS3303u] = BRIDGE_NW801, /* 06a5:d001 - * Plustek Opticam 500U or ProLink DS3303u */ - - [Kr651us] = BRIDGE_NW802, /* 06a5:d001 - * Panasonic GP-KR651US */ - - [Kritter] = BRIDGE_NW802, /* 06a5:d001 - * iRez Kritter cam */ - - [Mustek300] = BRIDGE_NW802, /* 055f:d001 - * Mustek Wcam 300 mini */ - - [Proscope] = BRIDGE_NW802, /* 06a5:d001 - * Scalar USB Microscope (ProScope) */ - - [Twinkle] = BRIDGE_NW800, /* 06a5:d800 - hv7121b? (seems pas106) - * Divio Chicony TwinkleCam - * DSB-C110 */ - - [DvcV6] = BRIDGE_NW802, /* 0502:d001 - * DVC V6 */ - - [P35u] = BRIDGE_NW801, /* 052b:d001, 06a5:d001 and 06be:d001 - * EZCam Pro p35u */ - - [Generic802] = BRIDGE_NW802, -}; -/* - * other webcams: - * - nw801 046d:d001 - * Logitech QuickCam Pro (dark focus ring) - * - nw801 0728:d001 - * AVerMedia Camguard - * - nw??? 06a5:d001 - * D-Link NetQam Pro 250plus - * - nw800 065a:d800 - * Showcam NGS webcam - * - nw??? ????:???? - * Sceptre svc300 - */ - -/* - * registers - * nw800/et31x110 nw801 nw802 - * 0000..009e 0000..00a1 0000..009e - * 0200..0211 id id - * 0300..0302 id id - * 0400..0406 (inex) 0400..0406 - * 0500..0505 0500..0506 (inex) - * 0600..061a 0600..0601 0600..0601 - * 0800..0814 id id - * 1000..109c 1000..10a1 1000..109a - */ - -/* resolutions - * nw800: 320x240, 352x288 - * nw801/802: 320x240, 640x480 - */ -static const struct v4l2_pix_format cif_mode[] = { - {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 4 / 8, - .colorspace = V4L2_COLORSPACE_JPEG}, - {352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 4 / 8, - .colorspace = V4L2_COLORSPACE_JPEG} -}; -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 4 / 8, - .colorspace = V4L2_COLORSPACE_JPEG}, - {640, 480, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8, - .colorspace = V4L2_COLORSPACE_JPEG}, -}; - -/* - * The sequences below contain: - * - 1st and 2nd bytes: either - * - register number (BE) - * - I2C0 + i2c address - * - 3rd byte: data length (=0 for end of sequence) - * - n bytes: data - */ -#define I2C0 0xff - -static const u8 nw800_init[] = { - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x01, - 0x04, 0x06, 0x01, 0x04, - 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, - 0x05, 0x05, 0x01, 0x00, - 0, 0, 0 -}; -static const u8 nw800_start[] = { - 0x04, 0x06, 0x01, 0xc0, - 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, - 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, - 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, - 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, - 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0, - 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20, - 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, - 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, - 0x01, 0x60, 0x01, 0x00, 0x00, - - 0x04, 0x04, 0x01, 0xff, - 0x04, 0x06, 0x01, 0xc4, - - 0x04, 0x06, 0x01, 0xc0, - 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, - 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, - 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, - 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, - 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0, - 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20, - 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, - 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, - 0x01, 0x60, 0x01, 0x00, 0x00, - - 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, - 0x40, - 0x00, 0x80, 0x01, 0xa0, - 0x10, 0x1a, 0x01, 0x00, - 0x00, 0x91, 0x02, 0x6c, 0x01, - 0x00, 0x03, 0x02, 0xc8, 0x01, - 0x10, 0x1a, 0x01, 0x00, - 0x10, 0x00, 0x01, 0x83, - 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, - 0x20, 0x01, 0x60, 0x01, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - 0x10, 0x1b, 0x02, 0x69, 0x00, - 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - 0x05, 0x02, 0x01, 0x02, - 0x06, 0x00, 0x02, 0x04, 0xd9, - 0x05, 0x05, 0x01, 0x20, - 0x05, 0x05, 0x01, 0x21, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83, - 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0, - 0xea, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x13, 0x13, - 0x10, 0x03, 0x01, 0x14, - 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83, - 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0, - 0xea, - 0x10, 0x0b, 0x01, 0x14, - 0x10, 0x0d, 0x01, 0x20, - 0x10, 0x0c, 0x01, 0x34, - 0x04, 0x06, 0x01, 0xc3, - 0x04, 0x04, 0x01, 0x00, - 0x05, 0x02, 0x01, 0x02, - 0x06, 0x00, 0x02, 0x00, 0x48, - 0x05, 0x05, 0x01, 0x20, - 0x05, 0x05, 0x01, 0x21, - 0, 0, 0 -}; - -/* 06a5:d001 - nw801 - Panasonic - * P35u */ -static const u8 nw801_start_1[] = { - 0x05, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, - 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, - 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x69, 0xa8, 0x1f, 0x00, - 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00, - 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00, - 0x36, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x22, 0x02, 0x80, 0x00, 0x1e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x15, 0x08, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x35, 0xfd, 0x07, 0x3d, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x14, 0x02, - 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7, - 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80, - 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, 0xa4, - 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, 0xcf, - 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, - 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, - 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x82, 0x02, - 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01, - 0xf0, 0x00, - 0, 0, 0, -}; -static const u8 nw801_start_qvga[] = { - 0x02, 0x00, 0x10, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x18, 0x0b, 0x06, 0xa2, 0x86, 0x78, - 0x02, 0x0f, 0x01, 0x6b, - 0x10, 0x1a, 0x01, 0x15, - 0x00, 0x00, 0x01, 0x1e, - 0x10, 0x00, 0x01, 0x2f, - 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00, - /* AE window */ - 0, 0, 0, -}; -static const u8 nw801_start_vga[] = { - 0x02, 0x00, 0x10, 0x78, 0xa0, 0x97, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xf0, - 0x02, 0x0f, 0x01, 0xd5, - 0x10, 0x1a, 0x01, 0x15, - 0x00, 0x00, 0x01, 0x0e, - 0x10, 0x00, 0x01, 0x22, - 0x10, 0x8c, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, - 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, - 0, 0, 0, -}; -static const u8 nw801_start_2[] = { - 0x10, 0x04, 0x01, 0x1a, - 0x10, 0x19, 0x01, 0x09, /* clock */ - 0x10, 0x24, 0x06, 0xc0, 0x00, 0x3f, 0x02, 0x00, 0x01, - /* .. gain .. */ - 0x00, 0x03, 0x02, 0x92, 0x03, - 0x00, 0x1d, 0x04, 0xf2, 0x00, 0x24, 0x07, - 0x00, 0x7b, 0x01, 0xcf, - 0x10, 0x94, 0x01, 0x07, - 0x05, 0x05, 0x01, 0x01, - 0x05, 0x04, 0x01, 0x01, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8, - 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0, - 0xf0, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x0c, 0x0c, - 0x10, 0x03, 0x01, 0x08, - 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8, - 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0, - 0xf0, - 0x10, 0x0b, 0x01, 0x0b, - 0x10, 0x0d, 0x01, 0x0b, - 0x10, 0x0c, 0x01, 0x1f, - 0x05, 0x06, 0x01, 0x03, - 0, 0, 0 -}; - -/* nw802 (sharp IR3Y38M?) */ -static const u8 nw802_start[] = { - 0x04, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x4d, - 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, - 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, - 0x00, 0x10, 0x06, 0x08, 0x00, 0x18, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x1d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0xff, 0x01, 0xc0, 0x00, 0x14, - 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x05, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, - 0x40, - 0x10, 0x1a, 0x01, 0x00, - 0x10, 0x00, 0x01, 0xad, - 0x00, 0x00, 0x01, 0x08, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00, - 0x10, 0x1d, 0x08, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, - 0x10, 0x0e, 0x01, 0x27, - 0x10, 0x41, 0x11, 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b, - 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2, - 0xd8, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x14, 0x14, - 0x10, 0x03, 0x01, 0x0c, - 0x10, 0x41, 0x11, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, 0x74, - 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, 0xf1, - 0xff, -/* 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b, - * 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2, - * 0xd8, */ - 0x10, 0x0b, 0x01, 0x10, - 0x10, 0x0d, 0x01, 0x11, - 0x10, 0x0c, 0x01, 0x1c, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x04, 0x01, 0x00, - 0, 0, 0 -}; -/* et31x110 - Trust 120 SpaceCam */ -static const u8 spacecam_init[] = { - 0x04, 0x05, 0x01, 0x01, - 0x04, 0x04, 0x01, 0x01, - 0x04, 0x06, 0x01, 0x04, - 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, - 0x05, 0x05, 0x01, 0x00, - 0, 0, 0 -}; -static const u8 spacecam_start[] = { - 0x04, 0x06, 0x01, 0x44, - 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, - 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, - 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, - 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, - 0x00, 0x4b, 0x00, 0x7c, 0x00, 0x80, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, - 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, - 0x01, 0x60, 0x01, 0x00, 0x00, - 0x04, 0x06, 0x01, 0xc0, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, - 0x40, - 0x00, 0x80, 0x01, 0xa0, - 0x10, 0x1a, 0x01, 0x00, - 0x00, 0x91, 0x02, 0x32, 0x01, - 0x00, 0x03, 0x02, 0x08, 0x02, - 0x10, 0x00, 0x01, 0x83, - 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, - 0x20, 0x01, 0x60, 0x01, - 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x13, 0x13, - 0x10, 0x03, 0x01, 0x06, - 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, - 0x10, 0x0b, 0x01, 0x08, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x1f, - 0x04, 0x06, 0x01, 0xc3, - 0x04, 0x05, 0x01, 0x40, - 0x04, 0x04, 0x01, 0x40, - 0, 0, 0 -}; -/* et31x110 - pas106 - other Trust SpaceCam120 */ -static const u8 spacecam2_start[] = { - 0x04, 0x06, 0x01, 0x44, - 0x04, 0x06, 0x01, 0x00, - 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, - 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, - 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, - 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, - 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, - 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, - 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, - 0x01, 0x60, 0x01, 0x00, 0x00, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - 0x04, 0x04, 0x01, 0x40, - 0x04, 0x04, 0x01, 0x00, - I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x05, 0x05, - I2C0, 0x40, 0x02, 0x11, 0x06, - I2C0, 0x40, 0x02, 0x14, 0x00, - I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ - 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, - 0x40, - I2C0, 0x40, 0x02, 0x02, 0x0c, /* pixel clock */ - I2C0, 0x40, 0x02, 0x0f, 0x00, - I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ - 0x10, 0x00, 0x01, 0x01, - 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, - 0x20, 0x01, 0x60, 0x01, - I2C0, 0x40, 0x02, 0x05, 0x0f, /* exposure */ - I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ - I2C0, 0x40, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, - /* gains */ - I2C0, 0x40, 0x03, 0x12, 0x04, 0x01, - 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, - 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, - 0xf9, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x13, 0x13, - 0x10, 0x03, 0x01, 0x06, - 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, - 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, - 0xf9, - 0x10, 0x0b, 0x01, 0x11, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x14, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x00, - 0, 0, 0 -}; - -/* nw802 - Conceptronic Video Pro */ -static const u8 cvideopro_start[] = { - 0x04, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, - 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, - 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, - 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, - 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, - 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, - 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, - 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, - 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, - 0x40, - 0x10, 0x1a, 0x01, 0x03, - 0x10, 0x00, 0x01, 0xac, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x3b, 0x01, - 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, - 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, - 0x10, 0x1d, 0x02, 0x40, 0x06, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0, - 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc, - 0xdc, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x12, 0x12, - 0x10, 0x03, 0x01, 0x0c, - 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0, - 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc, - 0xdc, - 0x10, 0x0b, 0x01, 0x09, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x2f, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x04, 0x01, 0x00, - 0, 0, 0 -}; - -/* nw802 - D-link dru-350c cam */ -static const u8 dlink_start[] = { - 0x04, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, - 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, - 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, - 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14, - 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, - 0x40, - 0x10, 0x1a, 0x01, 0x00, - 0x10, 0x00, 0x01, 0xad, - 0x00, 0x00, 0x01, 0x08, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00, - 0x10, 0x1d, 0x08, 0x40, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, - 0x10, 0x0e, 0x01, 0x20, - 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f, - 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf, - 0xea, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x11, 0x11, - 0x10, 0x03, 0x01, 0x10, - 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f, - 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf, - 0xea, - 0x10, 0x0b, 0x01, 0x19, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x1e, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x04, 0x01, 0x00, - 0, 0, 0 -}; - -/* 06a5:d001 - nw801 - Sony - * Plustek Opticam 500U or ProLink DS3303u (Hitachi HD49322BF) */ -/*fixme: 320x240 only*/ -static const u8 ds3303_start[] = { - 0x05, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x16, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, - 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, - 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa9, 0xa8, 0x1f, 0x00, - 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00, - 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00, - 0x36, 0x00, - 0x02, 0x00, 0x12, 0x03, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0x50, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x2f, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a, - 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4, - 0x00, 0x01, 0x15, 0xfd, 0x07, 0x3d, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x8c, 0x04, 0x01, 0x20, - 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7, - 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80, - 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, 0x88, - 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, 0xcb, - 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, - 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, - 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, 0x01, - 0x00, 0x00, 0xef, 0x00, 0x02, 0x0a, 0x82, 0x02, - 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01, - 0xf0, 0x00, - - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x3f, 0x00, 0xf2, 0x8f, 0x81, - 0x40, - 0x10, 0x1a, 0x01, 0x15, - 0x10, 0x00, 0x01, 0x2f, - 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, - 0x10, 0x26, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, - 0x10, 0x24, 0x02, 0x40, 0x06, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6, - 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x16, 0x16, - 0x10, 0x03, 0x01, 0x0c, - 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6, - 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, - 0x10, 0x0b, 0x01, 0x26, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x1c, - 0x05, 0x06, 0x01, 0x03, - 0x05, 0x04, 0x01, 0x00, - 0, 0, 0 -}; - -/* 06a5:d001 - nw802 - Panasonic - * GP-KR651US (Philips TDA8786) */ -static const u8 kr651_start_1[] = { - 0x04, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x48, - 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, - 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, - 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, - 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, - 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, - 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, - 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x02, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, - 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0, 0, 0 -}; -static const u8 kr651_start_qvga[] = { - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, - 0x40, - 0x10, 0x1a, 0x01, 0x03, - 0x10, 0x00, 0x01, 0xac, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00, - 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, - 0x10, 0x1d, 0x02, 0x28, 0x01, - 0, 0, 0 -}; -static const u8 kr651_start_vga[] = { - 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x30, 0x03, 0x01, 0x82, 0x82, 0x98, - 0x80, - 0x10, 0x1a, 0x01, 0x03, - 0x10, 0x00, 0x01, 0xa0, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, - 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, - 0x10, 0x1d, 0x02, 0x68, 0x00, -}; -static const u8 kr651_start_2[] = { - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8, - 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, - 0xdc, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x0c, 0x0c, - 0x10, 0x03, 0x01, 0x0c, - 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8, - 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, - 0xdc, - 0x10, 0x0b, 0x01, 0x10, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x2d, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x04, 0x01, 0x00, - 0, 0, 0 -}; - -/* nw802 - iRez Kritter cam */ -static const u8 kritter_start[] = { - 0x04, 0x06, 0x01, 0x06, - 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0x94, 0x03, 0x18, 0x00, 0x48, - 0x0f, 0x1e, 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x0b, 0x00, 0x1b, 0x00, 0x0a, 0x01, 0x28, - 0x07, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, - 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, - 0x00, 0x5d, 0x00, 0x0e, 0x00, 0x7e, 0x00, 0x30, - 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, - 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0b, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, - 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x02, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, - 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, - 0x40, - 0x10, 0x1a, 0x01, 0x03, - 0x10, 0x00, 0x01, 0xaf, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x3b, 0x01, - 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, - 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, - 0x10, 0x1d, 0x02, 0x00, 0x00, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86, - 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4, - 0xcb, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x0d, 0x0d, - 0x10, 0x03, 0x01, 0x02, - 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86, - 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4, - 0xcb, - 0x10, 0x0b, 0x01, 0x17, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x1e, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x04, 0x01, 0x00, - 0, 0, 0 -}; - -/* nw802 - Mustek Wcam 300 mini */ -static const u8 mustek_start[] = { - 0x04, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, - 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, - 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, - 0x00, 0x10, 0x06, 0xfc, 0x05, 0x0c, 0x06, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14, - 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, - 0x40, - 0x10, 0x1a, 0x01, 0x00, - 0x10, 0x00, 0x01, 0xad, - 0x00, 0x00, 0x01, 0x08, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1d, 0x08, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, - 0x10, 0x0e, 0x01, 0x0f, - 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e, - 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9, - 0xff, - 0x10, 0x0f, 0x02, 0x11, 0x11, - 0x10, 0x03, 0x01, 0x0c, - 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e, - 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9, - 0xff, - 0x10, 0x0b, 0x01, 0x1c, - 0x10, 0x0d, 0x01, 0x1a, - 0x10, 0x0c, 0x01, 0x34, - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x40, - 0x04, 0x06, 0x01, 0x03, - 0, 0, 0 -}; - -/* nw802 - Scope USB Microscope M2 (ProScope) (Hitachi HD49322BF) */ -static const u8 proscope_init[] = { - 0x04, 0x05, 0x01, 0x21, - 0x04, 0x04, 0x01, 0x01, - 0, 0, 0 -}; -static const u8 proscope_start_1[] = { - 0x04, 0x06, 0x01, 0x04, - 0x00, 0x00, 0x40, 0x10, 0x01, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x04, - 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x08, 0x00, 0x17, 0x00, 0xce, 0x00, 0xf4, - 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0xce, 0x00, 0xf8, 0x03, 0x3e, 0x00, 0x86, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0xb6, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xf6, 0x03, 0x34, 0x04, 0xf6, 0x03, 0x34, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xe8, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x1f, 0x0f, 0x08, 0x20, 0xa8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, 0x00, 0x94, - 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xad, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a, - 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4, - 0x00, 0x49, 0x13, 0x00, 0x00, 0x8c, 0x04, 0x01, - 0x20, 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, - 0x88, 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, - 0xcb, 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, - 0x01, 0x00, 0x00, 0xef, 0x00, 0x09, 0x05, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0, 0, 0 -}; -static const u8 proscope_start_qvga[] = { - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, - 0x40, - 0x10, 0x1a, 0x01, 0x06, - 0x00, 0x03, 0x02, 0xf9, 0x02, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, - 0x10, 0x0e, 0x01, 0x10, - 0, 0, 0 -}; -static const u8 proscope_start_vga[] = { - 0x00, 0x03, 0x02, 0xf9, 0x02, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, - 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x16, 0x00, 0x00, 0x82, 0x84, 0x00, - 0x80, - 0x10, 0x1a, 0x01, 0x06, - 0x10, 0x00, 0x01, 0xa1, - 0x10, 0x1b, 0x02, 0x00, 0x00, - 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, - 0x10, 0x11, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, - 0x10, 0x0e, 0x01, 0x10, - 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae, - 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2, - 0xf9, - 0x10, 0x03, 0x01, 0x00, - 0, 0, 0 -}; -static const u8 proscope_start_2[] = { - 0x10, 0x0f, 0x02, 0x0c, 0x0c, - 0x10, 0x03, 0x01, 0x0c, - 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae, - 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2, - 0xf9, - 0x10, 0x0b, 0x01, 0x0b, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x1b, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x05, 0x01, 0x21, - 0x04, 0x04, 0x01, 0x00, - 0, 0, 0 -}; - -/* nw800 - hv7121b? (seems pas106) - Divio Chicony TwinkleCam */ -static const u8 twinkle_start[] = { - 0x04, 0x06, 0x01, 0x44, - 0x04, 0x06, 0x01, 0x00, - 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, - 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, - 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, - 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, - 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, - 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x08, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x10, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x00, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, - 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, - 0x01, 0x60, 0x01, 0x00, 0x00, - - 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - 0x04, 0x04, 0x01, 0x10, - 0x04, 0x04, 0x01, 0x00, - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x01, - I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, - I2C0, 0x40, 0x02, 0x11, 0x06, - I2C0, 0x40, 0x02, 0x14, 0x00, - I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ - I2C0, 0x40, 0x02, 0x07, 0x01, - 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, - 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, - 0x40, - I2C0, 0x40, 0x02, 0x02, 0x0c, - I2C0, 0x40, 0x02, 0x13, 0x01, - 0x10, 0x00, 0x01, 0x01, - 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, - 0x20, 0x01, 0x60, 0x01, - I2C0, 0x40, 0x02, 0x05, 0x0f, - I2C0, 0x40, 0x02, 0x13, 0x01, - I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, - I2C0, 0x40, 0x03, 0x12, 0x00, 0x01, - 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, - I2C0, 0x40, 0x02, 0x12, 0x00, - I2C0, 0x40, 0x02, 0x0e, 0x00, - I2C0, 0x40, 0x02, 0x11, 0x06, - 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, - 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, - 0xf9, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x0c, 0x0c, - 0x10, 0x03, 0x01, 0x06, - 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, - 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, - 0xf9, - 0x10, 0x0b, 0x01, 0x19, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x0d, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x41, - 0, 0, 0 -}; - -/* nw802 dvc-v6 */ -static const u8 dvcv6_start[] = { - 0x04, 0x06, 0x01, 0x06, - 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, - 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, - 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, - 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, - 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, - 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, - 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, - 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, - 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, - 0x06, 0x00, 0x02, 0x09, 0x99, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, - 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, - 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, - 0x01, 0xf0, 0x00, - 0x00, 0x03, 0x02, 0x94, 0x03, - 0x00, 0x1d, 0x04, 0x0a, 0x01, 0x28, 0x07, - 0x00, 0x7b, 0x02, 0xe0, 0x00, - 0x10, 0x8d, 0x01, 0x00, - 0x00, 0x09, 0x04, 0x1e, 0x00, 0x0c, 0x02, - 0x00, 0x91, 0x02, 0x0b, 0x02, - 0x10, 0x00, 0x01, 0xaf, - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8f, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, - 0x40, - 0x10, 0x1a, 0x01, 0x02, - 0x10, 0x00, 0x01, 0xaf, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x10, 0x1b, 0x02, 0x07, 0x01, - 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, - 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, - 0x10, 0x1d, 0x02, 0x40, 0x06, - 0x10, 0x0e, 0x01, 0x08, - 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa, - 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc, - 0xdc, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x12, 0x12, - 0x10, 0x03, 0x01, 0x11, - 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa, - 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc, - 0xdc, - 0x10, 0x0b, 0x01, 0x16, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x1a, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x04, 0x01, 0x00, -}; - -static const u8 *webcam_start[] = { - [Generic800] = nw800_start, - [SpaceCam] = spacecam_start, - [SpaceCam2] = spacecam2_start, - [Cvideopro] = cvideopro_start, - [Dlink350c] = dlink_start, - [DS3303u] = ds3303_start, - [Kr651us] = kr651_start_1, - [Kritter] = kritter_start, - [Mustek300] = mustek_start, - [Proscope] = proscope_start_1, - [Twinkle] = twinkle_start, - [DvcV6] = dvcv6_start, - [P35u] = nw801_start_1, - [Generic802] = nw802_start, -}; - -/* -- write a register -- */ -static void reg_w(struct gspca_dev *gspca_dev, - u16 index, - const u8 *data, - int len) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - if (len == 1) - PDEBUG(D_USBO, "SET 00 0000 %04x %02x", index, *data); - else - PDEBUG(D_USBO, "SET 00 0000 %04x %02x %02x ...", - index, *data, data[1]); - memcpy(gspca_dev->usb_buf, data, len); - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x00, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, /* value */ - index, - gspca_dev->usb_buf, - len, - 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* -- read registers in usb_buf -- */ -static void reg_r(struct gspca_dev *gspca_dev, - u16 index, - int len) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, index, - gspca_dev->usb_buf, len, 500); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - return; - } - if (len == 1) - PDEBUG(D_USBI, "GET 00 0000 %04x %02x", - index, gspca_dev->usb_buf[0]); - else - PDEBUG(D_USBI, "GET 00 0000 %04x %02x %02x ..", - index, gspca_dev->usb_buf[0], - gspca_dev->usb_buf[1]); -} - -static void i2c_w(struct gspca_dev *gspca_dev, - u8 i2c_addr, - const u8 *data, - int len) -{ - u8 val[2]; - int i; - - reg_w(gspca_dev, 0x0600, data + 1, len - 1); - reg_w(gspca_dev, 0x0600, data, len); - val[0] = len; - val[1] = i2c_addr; - reg_w(gspca_dev, 0x0502, val, 2); - val[0] = 0x01; - reg_w(gspca_dev, 0x0501, val, 1); - for (i = 5; --i >= 0; ) { - msleep(4); - reg_r(gspca_dev, 0x0505, 1); - if (gspca_dev->usb_err < 0) - return; - if (gspca_dev->usb_buf[0] == 0) - return; - } - gspca_dev->usb_err = -ETIME; -} - -static void reg_w_buf(struct gspca_dev *gspca_dev, - const u8 *cmd) -{ - u16 reg; - int len; - - for (;;) { - reg = *cmd++ << 8; - reg += *cmd++; - len = *cmd++; - if (len == 0) - break; - if (cmd[-3] != I2C0) - reg_w(gspca_dev, reg, cmd, len); - else - i2c_w(gspca_dev, reg, cmd, len); - cmd += len; - } -} - -static int swap_bits(int v) -{ - int r, i; - - r = 0; - for (i = 0; i < 8; i++) { - r <<= 1; - if (v & 1) - r++; - v >>= 1; - } - return r; -} - -static void setgain(struct gspca_dev *gspca_dev, u8 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 v[2]; - - switch (sd->webcam) { - case P35u: - reg_w(gspca_dev, 0x1026, &val, 1); - break; - case Kr651us: - /* 0 - 253 */ - val = swap_bits(val); - v[0] = val << 3; - v[1] = val >> 5; - reg_w(gspca_dev, 0x101d, v, 2); /* SIF reg0/1 (AGC) */ - break; - } -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 v[2]; - - switch (sd->webcam) { - case P35u: - v[0] = ((9 - val) << 3) | 0x01; - reg_w(gspca_dev, 0x1019, v, 1); - break; - case Cvideopro: - case DvcV6: - case Kritter: - case Kr651us: - v[0] = val; - v[1] = val >> 8; - reg_w(gspca_dev, 0x101b, v, 2); - break; - } -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int w, h; - - if (!val) { - sd->ag_cnt = -1; - return; - } - sd->ag_cnt = AG_CNT_START; - - reg_r(gspca_dev, 0x1004, 1); - if (gspca_dev->usb_buf[0] & 0x04) { /* if AE_FULL_FRM */ - sd->ae_res = gspca_dev->width * gspca_dev->height; - } else { /* get the AE window size */ - reg_r(gspca_dev, 0x1011, 8); - w = (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0] - - (gspca_dev->usb_buf[3] << 8) - gspca_dev->usb_buf[2]; - h = (gspca_dev->usb_buf[5] << 8) + gspca_dev->usb_buf[4] - - (gspca_dev->usb_buf[7] << 8) - gspca_dev->usb_buf[6]; - sd->ae_res = h * w; - if (sd->ae_res == 0) - sd->ae_res = gspca_dev->width * gspca_dev->height; - } -} - -static int nw802_test_reg(struct gspca_dev *gspca_dev, - u16 index, - u8 value) -{ - /* write the value */ - reg_w(gspca_dev, index, &value, 1); - - /* read it */ - reg_r(gspca_dev, index, 1); - - return gspca_dev->usb_buf[0] == value; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if ((unsigned) webcam >= NWEBCAMS) - webcam = 0; - sd->webcam = webcam; - gspca_dev->cam.needs_full_bandwidth = 1; - sd->ag_cnt = -1; - - /* - * Autodetect sequence inspired from some log. - * We try to detect what registers exist or not. - * If 0x0500 does not exist => NW802 - * If it does, test 0x109b. If it doesn't exist, - * then it's a NW801. Else, a NW800 - * If a et31x110 (nw800 and 06a5:d800) - * get the sensor ID - */ - if (!nw802_test_reg(gspca_dev, 0x0500, 0x55)) { - sd->bridge = BRIDGE_NW802; - if (sd->webcam == Generic800) - sd->webcam = Generic802; - } else if (!nw802_test_reg(gspca_dev, 0x109b, 0xaa)) { - sd->bridge = BRIDGE_NW801; - if (sd->webcam == Generic800) - sd->webcam = P35u; - } else if (id->idVendor == 0x06a5 && id->idProduct == 0xd800) { - reg_r(gspca_dev, 0x0403, 1); /* GPIO */ - PDEBUG(D_PROBE, "et31x110 sensor type %02x", - gspca_dev->usb_buf[0]); - switch (gspca_dev->usb_buf[0] >> 1) { - case 0x00: /* ?? */ - if (sd->webcam == Generic800) - sd->webcam = SpaceCam; - break; - case 0x01: /* Hynix? */ - if (sd->webcam == Generic800) - sd->webcam = Twinkle; - break; - case 0x0a: /* Pixart */ - if (sd->webcam == Generic800) - sd->webcam = SpaceCam2; - break; - } - } - if (webcam_chip[sd->webcam] != sd->bridge) { - pr_err("Bad webcam type %d for NW80%d\n", - sd->webcam, sd->bridge); - gspca_dev->usb_err = -ENODEV; - return gspca_dev->usb_err; - } - PDEBUG(D_PROBE, "Bridge nw80%d - type: %d", sd->bridge, sd->webcam); - - if (sd->bridge == BRIDGE_NW800) { - switch (sd->webcam) { - case DS3303u: - gspca_dev->cam.cam_mode = cif_mode; /* qvga */ - break; - default: - gspca_dev->cam.cam_mode = &cif_mode[1]; /* cif */ - break; - } - gspca_dev->cam.nmodes = 1; - } else { - gspca_dev->cam.cam_mode = vga_mode; - switch (sd->webcam) { - case Kr651us: - case Proscope: - case P35u: - gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); - break; - default: - gspca_dev->cam.nmodes = 1; /* qvga only */ - break; - } - } - - return gspca_dev->usb_err; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->bridge) { - case BRIDGE_NW800: - switch (sd->webcam) { - case SpaceCam: - reg_w_buf(gspca_dev, spacecam_init); - break; - default: - reg_w_buf(gspca_dev, nw800_init); - break; - } - break; - default: - switch (sd->webcam) { - case Mustek300: - case P35u: - case Proscope: - reg_w_buf(gspca_dev, proscope_init); - break; - } - break; - } - return gspca_dev->usb_err; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - const u8 *cmd; - - cmd = webcam_start[sd->webcam]; - reg_w_buf(gspca_dev, cmd); - switch (sd->webcam) { - case P35u: - if (gspca_dev->width == 320) - reg_w_buf(gspca_dev, nw801_start_qvga); - else - reg_w_buf(gspca_dev, nw801_start_vga); - reg_w_buf(gspca_dev, nw801_start_2); - break; - case Kr651us: - if (gspca_dev->width == 320) - reg_w_buf(gspca_dev, kr651_start_qvga); - else - reg_w_buf(gspca_dev, kr651_start_vga); - reg_w_buf(gspca_dev, kr651_start_2); - break; - case Proscope: - if (gspca_dev->width == 320) - reg_w_buf(gspca_dev, proscope_start_qvga); - else - reg_w_buf(gspca_dev, proscope_start_vga); - reg_w_buf(gspca_dev, proscope_start_2); - break; - } - - sd->exp_too_high_cnt = 0; - sd->exp_too_low_cnt = 0; - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 value; - - /* 'go' off */ - if (sd->bridge != BRIDGE_NW801) { - value = 0x02; - reg_w(gspca_dev, 0x0406, &value, 1); - } - - /* LED off */ - switch (sd->webcam) { - case Cvideopro: - case Kr651us: - case DvcV6: - case Kritter: - value = 0xff; - break; - case Dlink350c: - value = 0x21; - break; - case SpaceCam: - case SpaceCam2: - case Proscope: - case Twinkle: - value = 0x01; - break; - default: - return; - } - reg_w(gspca_dev, 0x0404, &value, 1); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - /* - * frame header = '00 00 hh ww ss xx ff ff' - * with: - * - 'hh': height / 4 - * - 'ww': width / 4 - * - 'ss': frame sequence number c0..dd - */ - if (data[0] == 0x00 && data[1] == 0x00 - && data[6] == 0xff && data[7] == 0xff) { - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, data + 8, len - 8); - } else { - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - } -} - -static void do_autogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int luma; - - if (sd->ag_cnt < 0) - return; - if (--sd->ag_cnt >= 0) - return; - sd->ag_cnt = AG_CNT_START; - - /* get the average luma */ - reg_r(gspca_dev, sd->bridge == BRIDGE_NW801 ? 0x080d : 0x080c, 4); - luma = (gspca_dev->usb_buf[3] << 24) + (gspca_dev->usb_buf[2] << 16) - + (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; - luma /= sd->ae_res; - - switch (sd->webcam) { - case P35u: - gspca_coarse_grained_expo_autogain(gspca_dev, luma, 100, 5); - break; - default: - gspca_expo_autogain(gspca_dev, luma, 100, 5, 230, 0); - break; - } -} - - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - /* autogain/gain/exposure control cluster */ - case V4L2_CID_AUTOGAIN: - if (ctrl->is_new) - setautogain(gspca_dev, ctrl->val); - if (!ctrl->val) { - if (gspca_dev->gain->is_new) - setgain(gspca_dev, gspca_dev->gain->val); - if (gspca_dev->exposure->is_new) - setexposure(gspca_dev, - gspca_dev->exposure->val); - } - break; - /* Some webcams only have exposure, so handle that separately from the - autogain/gain/exposure cluster in the previous case. */ - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, gspca_dev->exposure->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 3); - switch (sd->webcam) { - case P35u: - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - /* For P35u choose coarse expo auto gain function gain minimum, - * to avoid a large settings jump the first auto adjustment */ - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 127 / 5 * 2); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 9, 1, 9); - break; - case Kr651us: - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 253, 1, 128); - /* fall through */ - case Cvideopro: - case DvcV6: - case Kritter: - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 315, 1, 150); - break; - default: - break; - } - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - if (gspca_dev->autogain) - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .dq_callback = do_autogain, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x046d, 0xd001)}, - {USB_DEVICE(0x0502, 0xd001)}, - {USB_DEVICE(0x052b, 0xd001)}, - {USB_DEVICE(0x055f, 0xd001)}, - {USB_DEVICE(0x06a5, 0x0000)}, - {USB_DEVICE(0x06a5, 0xd001)}, - {USB_DEVICE(0x06a5, 0xd800)}, - {USB_DEVICE(0x06be, 0xd001)}, - {USB_DEVICE(0x0728, 0xd001)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); - -module_param(webcam, int, 0644); -MODULE_PARM_DESC(webcam, - "Webcam type\n" - "0: generic\n" - "1: Trust 120 SpaceCam\n" - "2: other Trust 120 SpaceCam\n" - "3: Conceptronic Video Pro\n" - "4: D-link dru-350c\n" - "5: Plustek Opticam 500U\n" - "6: Panasonic GP-KR651US\n" - "7: iRez Kritter\n" - "8: Mustek Wcam 300 mini\n" - "9: Scalar USB Microscope M2 (Proscope)\n" - "10: Divio Chicony TwinkleCam\n" - "11: DVC-V6\n"); diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c deleted file mode 100644 index bfc7cefa59f8..000000000000 --- a/drivers/media/video/gspca/ov519.c +++ /dev/null @@ -1,4991 +0,0 @@ -/** - * OV519 driver - * - * Copyright (C) 2008-2011 Jean-François Moine - * Copyright (C) 2009 Hans de Goede - * - * This module is adapted from the ov51x-jpeg package, which itself - * was adapted from the ov511 driver. - * - * Original copyright for the ov511 driver is: - * - * Copyright (c) 1999-2006 Mark W. McClelland - * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach - * Many improvements by Bret Wallach - * Color fixes by by Orion Sky Lawlor (2/26/2000) - * OV7620 fixes by Charl P. Botha - * Changes by Claudio Matsuoka - * - * ov51x-jpeg original copyright is: - * - * Copyright (c) 2004-2007 Romain Beauxis - * Support for OV7670 sensors was contributed by Sam Skipsey - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "ov519" - -#include -#include "gspca.h" - -/* The jpeg_hdr is used by w996Xcf only */ -/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ -#define CONEX_CAM -#include "jpeg.h" - -MODULE_AUTHOR("Jean-Francois Moine "); -MODULE_DESCRIPTION("OV519 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* global parameters */ -static int frame_rate; - -/* Number of times to retry a failed I2C transaction. Increase this if you - * are getting "Failed to read sensor ID..." */ -static int i2c_detect_tries = 10; - -/* ov519 device descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct v4l2_ctrl *jpegqual; - struct v4l2_ctrl *freq; - struct { /* h/vflip control cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; - struct { /* autobrightness/brightness control cluster */ - struct v4l2_ctrl *autobright; - struct v4l2_ctrl *brightness; - }; - - u8 packet_nr; - - char bridge; -#define BRIDGE_OV511 0 -#define BRIDGE_OV511PLUS 1 -#define BRIDGE_OV518 2 -#define BRIDGE_OV518PLUS 3 -#define BRIDGE_OV519 4 /* = ov530 */ -#define BRIDGE_OVFX2 5 -#define BRIDGE_W9968CF 6 -#define BRIDGE_MASK 7 - - char invert_led; -#define BRIDGE_INVERT_LED 8 - - char snapshot_pressed; - char snapshot_needs_reset; - - /* Determined by sensor type */ - u8 sif; - -#define QUALITY_MIN 50 -#define QUALITY_MAX 70 -#define QUALITY_DEF 50 - - u8 stopped; /* Streaming is temporarily paused */ - u8 first_frame; - - u8 frame_rate; /* current Framerate */ - u8 clockdiv; /* clockdiv override */ - - s8 sensor; /* Type of image sensor chip (SEN_*) */ - - u8 sensor_addr; - u16 sensor_width; - u16 sensor_height; - s16 sensor_reg_cache[256]; - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; -enum sensors { - SEN_OV2610, - SEN_OV2610AE, - SEN_OV3610, - SEN_OV6620, - SEN_OV6630, - SEN_OV66308AF, - SEN_OV7610, - SEN_OV7620, - SEN_OV7620AE, - SEN_OV7640, - SEN_OV7648, - SEN_OV7660, - SEN_OV7670, - SEN_OV76BE, - SEN_OV8610, - SEN_OV9600, -}; - -/* Note this is a bit of a hack, but the w9968cf driver needs the code for all - the ov sensors which is already present here. When we have the time we - really should move the sensor drivers to v4l2 sub drivers. */ -#include "w996Xcf.c" - -/* table of the disabled controls */ -struct ctrl_valid { - int has_brightness:1; - int has_contrast:1; - int has_exposure:1; - int has_autogain:1; - int has_sat:1; - int has_hvflip:1; - int has_autobright:1; - int has_freq:1; -}; - -static const struct ctrl_valid valid_controls[] = { - [SEN_OV2610] = { - .has_exposure = 1, - .has_autogain = 1, - }, - [SEN_OV2610AE] = { - .has_exposure = 1, - .has_autogain = 1, - }, - [SEN_OV3610] = { - /* No controls */ - }, - [SEN_OV6620] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, - }, - [SEN_OV6630] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, - }, - [SEN_OV66308AF] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, - }, - [SEN_OV7610] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, - }, - [SEN_OV7620] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, - }, - [SEN_OV7620AE] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, - }, - [SEN_OV7640] = { - .has_brightness = 1, - .has_sat = 1, - .has_freq = 1, - }, - [SEN_OV7648] = { - .has_brightness = 1, - .has_sat = 1, - .has_freq = 1, - }, - [SEN_OV7660] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_hvflip = 1, - .has_freq = 1, - }, - [SEN_OV7670] = { - .has_brightness = 1, - .has_contrast = 1, - .has_hvflip = 1, - .has_freq = 1, - }, - [SEN_OV76BE] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, - }, - [SEN_OV8610] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - }, - [SEN_OV9600] = { - .has_exposure = 1, - .has_autogain = 1, - }, -}; - -static const struct v4l2_pix_format ov519_vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; -static const struct v4l2_pix_format ov519_sif_mode[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -/* Note some of the sizeimage values for the ov511 / ov518 may seem - larger then necessary, however they need to be this big as the ov511 / - ov518 always fills the entire isoc frame, using 0 padding bytes when - it doesn't have any data. So with low framerates the amount of data - transferred can become quite large (libv4l will remove all the 0 padding - in userspace). */ -static const struct v4l2_pix_format ov518_vga_mode[] = { - {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; -static const struct v4l2_pix_format ov518_sif_mode[] = { - {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 70000, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 70000, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -static const struct v4l2_pix_format ov511_vga_mode[] = { - {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; -static const struct v4l2_pix_format ov511_sif_mode[] = { - {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 70000, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 70000, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -static const struct v4l2_pix_format ovfx2_vga_mode[] = { - {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format ovfx2_cif_mode[] = { - {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format ovfx2_ov2610_mode[] = { - {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 800, - .sizeimage = 800 * 600, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 1600, - .sizeimage = 1600 * 1200, - .colorspace = V4L2_COLORSPACE_SRGB}, -}; -static const struct v4l2_pix_format ovfx2_ov3610_mode[] = { - {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 800, - .sizeimage = 800 * 600, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 1024, - .sizeimage = 1024 * 768, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 1600, - .sizeimage = 1600 * 1200, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, - {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 2048, - .sizeimage = 2048 * 1536, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format ovfx2_ov9600_mode[] = { - {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 1024, - .colorspace = V4L2_COLORSPACE_SRGB}, -}; - -/* Registers common to OV511 / OV518 */ -#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ -#define R51x_SYS_RESET 0x50 - /* Reset type flags */ - #define OV511_RESET_OMNICE 0x08 -#define R51x_SYS_INIT 0x53 -#define R51x_SYS_SNAP 0x52 -#define R51x_SYS_CUST_ID 0x5f -#define R51x_COMP_LUT_BEGIN 0x80 - -/* OV511 Camera interface register numbers */ -#define R511_CAM_DELAY 0x10 -#define R511_CAM_EDGE 0x11 -#define R511_CAM_PXCNT 0x12 -#define R511_CAM_LNCNT 0x13 -#define R511_CAM_PXDIV 0x14 -#define R511_CAM_LNDIV 0x15 -#define R511_CAM_UV_EN 0x16 -#define R511_CAM_LINE_MODE 0x17 -#define R511_CAM_OPTS 0x18 - -#define R511_SNAP_FRAME 0x19 -#define R511_SNAP_PXCNT 0x1a -#define R511_SNAP_LNCNT 0x1b -#define R511_SNAP_PXDIV 0x1c -#define R511_SNAP_LNDIV 0x1d -#define R511_SNAP_UV_EN 0x1e -#define R511_SNAP_OPTS 0x1f - -#define R511_DRAM_FLOW_CTL 0x20 -#define R511_FIFO_OPTS 0x31 -#define R511_I2C_CTL 0x40 -#define R511_SYS_LED_CTL 0x55 /* OV511+ only */ -#define R511_COMP_EN 0x78 -#define R511_COMP_LUT_EN 0x79 - -/* OV518 Camera interface register numbers */ -#define R518_GPIO_OUT 0x56 /* OV518(+) only */ -#define R518_GPIO_CTL 0x57 /* OV518(+) only */ - -/* OV519 Camera interface register numbers */ -#define OV519_R10_H_SIZE 0x10 -#define OV519_R11_V_SIZE 0x11 -#define OV519_R12_X_OFFSETL 0x12 -#define OV519_R13_X_OFFSETH 0x13 -#define OV519_R14_Y_OFFSETL 0x14 -#define OV519_R15_Y_OFFSETH 0x15 -#define OV519_R16_DIVIDER 0x16 -#define OV519_R20_DFR 0x20 -#define OV519_R25_FORMAT 0x25 - -/* OV519 System Controller register numbers */ -#define OV519_R51_RESET1 0x51 -#define OV519_R54_EN_CLK1 0x54 -#define OV519_R57_SNAPSHOT 0x57 - -#define OV519_GPIO_DATA_OUT0 0x71 -#define OV519_GPIO_IO_CTRL0 0x72 - -/*#define OV511_ENDPOINT_ADDRESS 1 * Isoc endpoint number */ - -/* - * The FX2 chip does not give us a zero length read at end of frame. - * It does, however, give a short read at the end of a frame, if - * necessary, rather than run two frames together. - * - * By choosing the right bulk transfer size, we are guaranteed to always - * get a short read for the last read of each frame. Frame sizes are - * always a composite number (width * height, or a multiple) so if we - * choose a prime number, we are guaranteed that the last read of a - * frame will be short. - * - * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB, - * otherwise EOVERFLOW "babbling" errors occur. I have not been able - * to figure out why. [PMiller] - * - * The constant (13 * 4096) is the largest "prime enough" number less than 64KB. - * - * It isn't enough to know the number of bytes per frame, in case we - * have data dropouts or buffer overruns (even though the FX2 double - * buffers, there are some pretty strict real time constraints for - * isochronous transfer for larger frame sizes). - */ -/*jfm: this value does not work for 800x600 - see isoc_init */ -#define OVFX2_BULK_SIZE (13 * 4096) - -/* I2C registers */ -#define R51x_I2C_W_SID 0x41 -#define R51x_I2C_SADDR_3 0x42 -#define R51x_I2C_SADDR_2 0x43 -#define R51x_I2C_R_SID 0x44 -#define R51x_I2C_DATA 0x45 -#define R518_I2C_CTL 0x47 /* OV518(+) only */ -#define OVFX2_I2C_ADDR 0x00 - -/* I2C ADDRESSES */ -#define OV7xx0_SID 0x42 -#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */ -#define OV8xx0_SID 0xa0 -#define OV6xx0_SID 0xc0 - -/* OV7610 registers */ -#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ -#define OV7610_REG_BLUE 0x01 /* blue channel balance */ -#define OV7610_REG_RED 0x02 /* red channel balance */ -#define OV7610_REG_SAT 0x03 /* saturation */ -#define OV8610_REG_HUE 0x04 /* 04 reserved */ -#define OV7610_REG_CNT 0x05 /* Y contrast */ -#define OV7610_REG_BRT 0x06 /* Y brightness */ -#define OV7610_REG_COM_C 0x14 /* misc common regs */ -#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */ -#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */ -#define OV7610_REG_COM_I 0x29 /* misc settings */ - -/* OV7660 and OV7670 registers */ -#define OV7670_R00_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ -#define OV7670_R01_BLUE 0x01 /* blue gain */ -#define OV7670_R02_RED 0x02 /* red gain */ -#define OV7670_R03_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ -#define OV7670_R04_COM1 0x04 /* Control 1 */ -/*#define OV7670_R07_AECHH 0x07 * AEC MS 5 bits */ -#define OV7670_R0C_COM3 0x0c /* Control 3 */ -#define OV7670_R0D_COM4 0x0d /* Control 4 */ -#define OV7670_R0E_COM5 0x0e /* All "reserved" */ -#define OV7670_R0F_COM6 0x0f /* Control 6 */ -#define OV7670_R10_AECH 0x10 /* More bits of AEC value */ -#define OV7670_R11_CLKRC 0x11 /* Clock control */ -#define OV7670_R12_COM7 0x12 /* Control 7 */ -#define OV7670_COM7_FMT_VGA 0x00 -/*#define OV7670_COM7_YUV 0x00 * YUV */ -#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */ -#define OV7670_COM7_FMT_MASK 0x38 -#define OV7670_COM7_RESET 0x80 /* Register reset */ -#define OV7670_R13_COM8 0x13 /* Control 8 */ -#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */ -#define OV7670_COM8_AWB 0x02 /* White balance enable */ -#define OV7670_COM8_AGC 0x04 /* Auto gain enable */ -#define OV7670_COM8_BFILT 0x20 /* Band filter enable */ -#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */ -#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ -#define OV7670_R14_COM9 0x14 /* Control 9 - gain ceiling */ -#define OV7670_R15_COM10 0x15 /* Control 10 */ -#define OV7670_R17_HSTART 0x17 /* Horiz start high bits */ -#define OV7670_R18_HSTOP 0x18 /* Horiz stop high bits */ -#define OV7670_R19_VSTART 0x19 /* Vert start high bits */ -#define OV7670_R1A_VSTOP 0x1a /* Vert stop high bits */ -#define OV7670_R1E_MVFP 0x1e /* Mirror / vflip */ -#define OV7670_MVFP_VFLIP 0x10 /* vertical flip */ -#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */ -#define OV7670_R24_AEW 0x24 /* AGC upper limit */ -#define OV7670_R25_AEB 0x25 /* AGC lower limit */ -#define OV7670_R26_VPT 0x26 /* AGC/AEC fast mode op region */ -#define OV7670_R32_HREF 0x32 /* HREF pieces */ -#define OV7670_R3A_TSLB 0x3a /* lots of stuff */ -#define OV7670_R3B_COM11 0x3b /* Control 11 */ -#define OV7670_COM11_EXP 0x02 -#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ -#define OV7670_R3C_COM12 0x3c /* Control 12 */ -#define OV7670_R3D_COM13 0x3d /* Control 13 */ -#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */ -#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */ -#define OV7670_R3E_COM14 0x3e /* Control 14 */ -#define OV7670_R3F_EDGE 0x3f /* Edge enhancement factor */ -#define OV7670_R40_COM15 0x40 /* Control 15 */ -/*#define OV7670_COM15_R00FF 0xc0 * 00 to FF */ -#define OV7670_R41_COM16 0x41 /* Control 16 */ -#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */ -/* end of ov7660 common registers */ -#define OV7670_R55_BRIGHT 0x55 /* Brightness */ -#define OV7670_R56_CONTRAS 0x56 /* Contrast control */ -#define OV7670_R69_GFIX 0x69 /* Fix gain control */ -/*#define OV7670_R8C_RGB444 0x8c * RGB 444 control */ -#define OV7670_R9F_HAECC1 0x9f /* Hist AEC/AGC control 1 */ -#define OV7670_RA0_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ -#define OV7670_RA5_BD50MAX 0xa5 /* 50hz banding step limit */ -#define OV7670_RA6_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ -#define OV7670_RA7_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ -#define OV7670_RA8_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ -#define OV7670_RA9_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ -#define OV7670_RAA_HAECC7 0xaa /* Hist AEC/AGC control 7 */ -#define OV7670_RAB_BD60MAX 0xab /* 60hz banding step limit */ - -struct ov_regvals { - u8 reg; - u8 val; -}; -struct ov_i2c_regvals { - u8 reg; - u8 val; -}; - -/* Settings for OV2610 camera chip */ -static const struct ov_i2c_regvals norm_2610[] = { - { 0x12, 0x80 }, /* reset */ -}; - -static const struct ov_i2c_regvals norm_2610ae[] = { - {0x12, 0x80}, /* reset */ - {0x13, 0xcd}, - {0x09, 0x01}, - {0x0d, 0x00}, - {0x11, 0x80}, - {0x12, 0x20}, /* 1600x1200 */ - {0x33, 0x0c}, - {0x35, 0x90}, - {0x36, 0x37}, -/* ms-win traces */ - {0x11, 0x83}, /* clock / 3 ? */ - {0x2d, 0x00}, /* 60 Hz filter */ - {0x24, 0xb0}, /* normal colors */ - {0x25, 0x90}, - {0x10, 0x43}, -}; - -static const struct ov_i2c_regvals norm_3620b[] = { - /* - * From the datasheet: "Note that after writing to register COMH - * (0x12) to change the sensor mode, registers related to the - * sensor’s cropping window will be reset back to their default - * values." - * - * "wait 4096 external clock ... to make sure the sensor is - * stable and ready to access registers" i.e. 160us at 24MHz - */ - { 0x12, 0x80 }, /* COMH reset */ - { 0x12, 0x00 }, /* QXGA, master */ - - /* - * 11 CLKRC "Clock Rate Control" - * [7] internal frequency doublers: on - * [6] video port mode: master - * [5:0] clock divider: 1 - */ - { 0x11, 0x80 }, - - /* - * 13 COMI "Common Control I" - * = 192 (0xC0) 11000000 - * COMI[7] "AEC speed selection" - * = 1 (0x01) 1....... "Faster AEC correction" - * COMI[6] "AEC speed step selection" - * = 1 (0x01) .1...... "Big steps, fast" - * COMI[5] "Banding filter on off" - * = 0 (0x00) ..0..... "Off" - * COMI[4] "Banding filter option" - * = 0 (0x00) ...0.... "Main clock is 48 MHz and - * the PLL is ON" - * COMI[3] "Reserved" - * = 0 (0x00) ....0... - * COMI[2] "AGC auto manual control selection" - * = 0 (0x00) .....0.. "Manual" - * COMI[1] "AWB auto manual control selection" - * = 0 (0x00) ......0. "Manual" - * COMI[0] "Exposure control" - * = 0 (0x00) .......0 "Manual" - */ - { 0x13, 0xc0 }, - - /* - * 09 COMC "Common Control C" - * = 8 (0x08) 00001000 - * COMC[7:5] "Reserved" - * = 0 (0x00) 000..... - * COMC[4] "Sleep Mode Enable" - * = 0 (0x00) ...0.... "Normal mode" - * COMC[3:2] "Sensor sampling reset timing selection" - * = 2 (0x02) ....10.. "Longer reset time" - * COMC[1:0] "Output drive current select" - * = 0 (0x00) ......00 "Weakest" - */ - { 0x09, 0x08 }, - - /* - * 0C COMD "Common Control D" - * = 8 (0x08) 00001000 - * COMD[7] "Reserved" - * = 0 (0x00) 0....... - * COMD[6] "Swap MSB and LSB at the output port" - * = 0 (0x00) .0...... "False" - * COMD[5:3] "Reserved" - * = 1 (0x01) ..001... - * COMD[2] "Output Average On Off" - * = 0 (0x00) .....0.. "Output Normal" - * COMD[1] "Sensor precharge voltage selection" - * = 0 (0x00) ......0. "Selects internal - * reference precharge - * voltage" - * COMD[0] "Snapshot option" - * = 0 (0x00) .......0 "Enable live video output - * after snapshot sequence" - */ - { 0x0c, 0x08 }, - - /* - * 0D COME "Common Control E" - * = 161 (0xA1) 10100001 - * COME[7] "Output average option" - * = 1 (0x01) 1....... "Output average of 4 pixels" - * COME[6] "Anti-blooming control" - * = 0 (0x00) .0...... "Off" - * COME[5:3] "Reserved" - * = 4 (0x04) ..100... - * COME[2] "Clock output power down pin status" - * = 0 (0x00) .....0.. "Tri-state data output pin - * on power down" - * COME[1] "Data output pin status selection at power down" - * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK, - * HREF, and CHSYNC pins on - * power down" - * COME[0] "Auto zero circuit select" - * = 1 (0x01) .......1 "On" - */ - { 0x0d, 0xa1 }, - - /* - * 0E COMF "Common Control F" - * = 112 (0x70) 01110000 - * COMF[7] "System clock selection" - * = 0 (0x00) 0....... "Use 24 MHz system clock" - * COMF[6:4] "Reserved" - * = 7 (0x07) .111.... - * COMF[3] "Manual auto negative offset canceling selection" - * = 0 (0x00) ....0... "Auto detect negative - * offset and cancel it" - * COMF[2:0] "Reserved" - * = 0 (0x00) .....000 - */ - { 0x0e, 0x70 }, - - /* - * 0F COMG "Common Control G" - * = 66 (0x42) 01000010 - * COMG[7] "Optical black output selection" - * = 0 (0x00) 0....... "Disable" - * COMG[6] "Black level calibrate selection" - * = 1 (0x01) .1...... "Use optical black pixels - * to calibrate" - * COMG[5:4] "Reserved" - * = 0 (0x00) ..00.... - * COMG[3] "Channel offset adjustment" - * = 0 (0x00) ....0... "Disable offset adjustment" - * COMG[2] "ADC black level calibration option" - * = 0 (0x00) .....0.. "Use B/G line and G/R - * line to calibrate each - * channel's black level" - * COMG[1] "Reserved" - * = 1 (0x01) ......1. - * COMG[0] "ADC black level calibration enable" - * = 0 (0x00) .......0 "Disable" - */ - { 0x0f, 0x42 }, - - /* - * 14 COMJ "Common Control J" - * = 198 (0xC6) 11000110 - * COMJ[7:6] "AGC gain ceiling" - * = 3 (0x03) 11...... "8x" - * COMJ[5:4] "Reserved" - * = 0 (0x00) ..00.... - * COMJ[3] "Auto banding filter" - * = 0 (0x00) ....0... "Banding filter is always - * on off depending on - * COMI[5] setting" - * COMJ[2] "VSYNC drop option" - * = 1 (0x01) .....1.. "SYNC is dropped if frame - * data is dropped" - * COMJ[1] "Frame data drop" - * = 1 (0x01) ......1. "Drop frame data if - * exposure is not within - * tolerance. In AEC mode, - * data is normally dropped - * when data is out of - * range." - * COMJ[0] "Reserved" - * = 0 (0x00) .......0 - */ - { 0x14, 0xc6 }, - - /* - * 15 COMK "Common Control K" - * = 2 (0x02) 00000010 - * COMK[7] "CHSYNC pin output swap" - * = 0 (0x00) 0....... "CHSYNC" - * COMK[6] "HREF pin output swap" - * = 0 (0x00) .0...... "HREF" - * COMK[5] "PCLK output selection" - * = 0 (0x00) ..0..... "PCLK always output" - * COMK[4] "PCLK edge selection" - * = 0 (0x00) ...0.... "Data valid on falling edge" - * COMK[3] "HREF output polarity" - * = 0 (0x00) ....0... "positive" - * COMK[2] "Reserved" - * = 0 (0x00) .....0.. - * COMK[1] "VSYNC polarity" - * = 1 (0x01) ......1. "negative" - * COMK[0] "HSYNC polarity" - * = 0 (0x00) .......0 "positive" - */ - { 0x15, 0x02 }, - - /* - * 33 CHLF "Current Control" - * = 9 (0x09) 00001001 - * CHLF[7:6] "Sensor current control" - * = 0 (0x00) 00...... - * CHLF[5] "Sensor current range control" - * = 0 (0x00) ..0..... "normal range" - * CHLF[4] "Sensor current" - * = 0 (0x00) ...0.... "normal current" - * CHLF[3] "Sensor buffer current control" - * = 1 (0x01) ....1... "half current" - * CHLF[2] "Column buffer current control" - * = 0 (0x00) .....0.. "normal current" - * CHLF[1] "Analog DSP current control" - * = 0 (0x00) ......0. "normal current" - * CHLF[1] "ADC current control" - * = 0 (0x00) ......0. "normal current" - */ - { 0x33, 0x09 }, - - /* - * 34 VBLM "Blooming Control" - * = 80 (0x50) 01010000 - * VBLM[7] "Hard soft reset switch" - * = 0 (0x00) 0....... "Hard reset" - * VBLM[6:4] "Blooming voltage selection" - * = 5 (0x05) .101.... - * VBLM[3:0] "Sensor current control" - * = 0 (0x00) ....0000 - */ - { 0x34, 0x50 }, - - /* - * 36 VCHG "Sensor Precharge Voltage Control" - * = 0 (0x00) 00000000 - * VCHG[7] "Reserved" - * = 0 (0x00) 0....... - * VCHG[6:4] "Sensor precharge voltage control" - * = 0 (0x00) .000.... - * VCHG[3:0] "Sensor array common reference" - * = 0 (0x00) ....0000 - */ - { 0x36, 0x00 }, - - /* - * 37 ADC "ADC Reference Control" - * = 4 (0x04) 00000100 - * ADC[7:4] "Reserved" - * = 0 (0x00) 0000.... - * ADC[3] "ADC input signal range" - * = 0 (0x00) ....0... "Input signal 1.0x" - * ADC[2:0] "ADC range control" - * = 4 (0x04) .....100 - */ - { 0x37, 0x04 }, - - /* - * 38 ACOM "Analog Common Ground" - * = 82 (0x52) 01010010 - * ACOM[7] "Analog gain control" - * = 0 (0x00) 0....... "Gain 1x" - * ACOM[6] "Analog black level calibration" - * = 1 (0x01) .1...... "On" - * ACOM[5:0] "Reserved" - * = 18 (0x12) ..010010 - */ - { 0x38, 0x52 }, - - /* - * 3A FREFA "Internal Reference Adjustment" - * = 0 (0x00) 00000000 - * FREFA[7:0] "Range" - * = 0 (0x00) 00000000 - */ - { 0x3a, 0x00 }, - - /* - * 3C FVOPT "Internal Reference Adjustment" - * = 31 (0x1F) 00011111 - * FVOPT[7:0] "Range" - * = 31 (0x1F) 00011111 - */ - { 0x3c, 0x1f }, - - /* - * 44 Undocumented = 0 (0x00) 00000000 - * 44[7:0] "It's a secret" - * = 0 (0x00) 00000000 - */ - { 0x44, 0x00 }, - - /* - * 40 Undocumented = 0 (0x00) 00000000 - * 40[7:0] "It's a secret" - * = 0 (0x00) 00000000 - */ - { 0x40, 0x00 }, - - /* - * 41 Undocumented = 0 (0x00) 00000000 - * 41[7:0] "It's a secret" - * = 0 (0x00) 00000000 - */ - { 0x41, 0x00 }, - - /* - * 42 Undocumented = 0 (0x00) 00000000 - * 42[7:0] "It's a secret" - * = 0 (0x00) 00000000 - */ - { 0x42, 0x00 }, - - /* - * 43 Undocumented = 0 (0x00) 00000000 - * 43[7:0] "It's a secret" - * = 0 (0x00) 00000000 - */ - { 0x43, 0x00 }, - - /* - * 45 Undocumented = 128 (0x80) 10000000 - * 45[7:0] "It's a secret" - * = 128 (0x80) 10000000 - */ - { 0x45, 0x80 }, - - /* - * 48 Undocumented = 192 (0xC0) 11000000 - * 48[7:0] "It's a secret" - * = 192 (0xC0) 11000000 - */ - { 0x48, 0xc0 }, - - /* - * 49 Undocumented = 25 (0x19) 00011001 - * 49[7:0] "It's a secret" - * = 25 (0x19) 00011001 - */ - { 0x49, 0x19 }, - - /* - * 4B Undocumented = 128 (0x80) 10000000 - * 4B[7:0] "It's a secret" - * = 128 (0x80) 10000000 - */ - { 0x4b, 0x80 }, - - /* - * 4D Undocumented = 196 (0xC4) 11000100 - * 4D[7:0] "It's a secret" - * = 196 (0xC4) 11000100 - */ - { 0x4d, 0xc4 }, - - /* - * 35 VREF "Reference Voltage Control" - * = 76 (0x4c) 01001100 - * VREF[7:5] "Column high reference control" - * = 2 (0x02) 010..... "higher voltage" - * VREF[4:2] "Column low reference control" - * = 3 (0x03) ...011.. "Highest voltage" - * VREF[1:0] "Reserved" - * = 0 (0x00) ......00 - */ - { 0x35, 0x4c }, - - /* - * 3D Undocumented = 0 (0x00) 00000000 - * 3D[7:0] "It's a secret" - * = 0 (0x00) 00000000 - */ - { 0x3d, 0x00 }, - - /* - * 3E Undocumented = 0 (0x00) 00000000 - * 3E[7:0] "It's a secret" - * = 0 (0x00) 00000000 - */ - { 0x3e, 0x00 }, - - /* - * 3B FREFB "Internal Reference Adjustment" - * = 24 (0x18) 00011000 - * FREFB[7:0] "Range" - * = 24 (0x18) 00011000 - */ - { 0x3b, 0x18 }, - - /* - * 33 CHLF "Current Control" - * = 25 (0x19) 00011001 - * CHLF[7:6] "Sensor current control" - * = 0 (0x00) 00...... - * CHLF[5] "Sensor current range control" - * = 0 (0x00) ..0..... "normal range" - * CHLF[4] "Sensor current" - * = 1 (0x01) ...1.... "double current" - * CHLF[3] "Sensor buffer current control" - * = 1 (0x01) ....1... "half current" - * CHLF[2] "Column buffer current control" - * = 0 (0x00) .....0.. "normal current" - * CHLF[1] "Analog DSP current control" - * = 0 (0x00) ......0. "normal current" - * CHLF[1] "ADC current control" - * = 0 (0x00) ......0. "normal current" - */ - { 0x33, 0x19 }, - - /* - * 34 VBLM "Blooming Control" - * = 90 (0x5A) 01011010 - * VBLM[7] "Hard soft reset switch" - * = 0 (0x00) 0....... "Hard reset" - * VBLM[6:4] "Blooming voltage selection" - * = 5 (0x05) .101.... - * VBLM[3:0] "Sensor current control" - * = 10 (0x0A) ....1010 - */ - { 0x34, 0x5a }, - - /* - * 3B FREFB "Internal Reference Adjustment" - * = 0 (0x00) 00000000 - * FREFB[7:0] "Range" - * = 0 (0x00) 00000000 - */ - { 0x3b, 0x00 }, - - /* - * 33 CHLF "Current Control" - * = 9 (0x09) 00001001 - * CHLF[7:6] "Sensor current control" - * = 0 (0x00) 00...... - * CHLF[5] "Sensor current range control" - * = 0 (0x00) ..0..... "normal range" - * CHLF[4] "Sensor current" - * = 0 (0x00) ...0.... "normal current" - * CHLF[3] "Sensor buffer current control" - * = 1 (0x01) ....1... "half current" - * CHLF[2] "Column buffer current control" - * = 0 (0x00) .....0.. "normal current" - * CHLF[1] "Analog DSP current control" - * = 0 (0x00) ......0. "normal current" - * CHLF[1] "ADC current control" - * = 0 (0x00) ......0. "normal current" - */ - { 0x33, 0x09 }, - - /* - * 34 VBLM "Blooming Control" - * = 80 (0x50) 01010000 - * VBLM[7] "Hard soft reset switch" - * = 0 (0x00) 0....... "Hard reset" - * VBLM[6:4] "Blooming voltage selection" - * = 5 (0x05) .101.... - * VBLM[3:0] "Sensor current control" - * = 0 (0x00) ....0000 - */ - { 0x34, 0x50 }, - - /* - * 12 COMH "Common Control H" - * = 64 (0x40) 01000000 - * COMH[7] "SRST" - * = 0 (0x00) 0....... "No-op" - * COMH[6:4] "Resolution selection" - * = 4 (0x04) .100.... "XGA" - * COMH[3] "Master slave selection" - * = 0 (0x00) ....0... "Master mode" - * COMH[2] "Internal B/R channel option" - * = 0 (0x00) .....0.. "B/R use same channel" - * COMH[1] "Color bar test pattern" - * = 0 (0x00) ......0. "Off" - * COMH[0] "Reserved" - * = 0 (0x00) .......0 - */ - { 0x12, 0x40 }, - - /* - * 17 HREFST "Horizontal window start" - * = 31 (0x1F) 00011111 - * HREFST[7:0] "Horizontal window start, 8 MSBs" - * = 31 (0x1F) 00011111 - */ - { 0x17, 0x1f }, - - /* - * 18 HREFEND "Horizontal window end" - * = 95 (0x5F) 01011111 - * HREFEND[7:0] "Horizontal Window End, 8 MSBs" - * = 95 (0x5F) 01011111 - */ - { 0x18, 0x5f }, - - /* - * 19 VSTRT "Vertical window start" - * = 0 (0x00) 00000000 - * VSTRT[7:0] "Vertical Window Start, 8 MSBs" - * = 0 (0x00) 00000000 - */ - { 0x19, 0x00 }, - - /* - * 1A VEND "Vertical window end" - * = 96 (0x60) 01100000 - * VEND[7:0] "Vertical Window End, 8 MSBs" - * = 96 (0x60) 01100000 - */ - { 0x1a, 0x60 }, - - /* - * 32 COMM "Common Control M" - * = 18 (0x12) 00010010 - * COMM[7:6] "Pixel clock divide option" - * = 0 (0x00) 00...... "/1" - * COMM[5:3] "Horizontal window end position, 3 LSBs" - * = 2 (0x02) ..010... - * COMM[2:0] "Horizontal window start position, 3 LSBs" - * = 2 (0x02) .....010 - */ - { 0x32, 0x12 }, - - /* - * 03 COMA "Common Control A" - * = 74 (0x4A) 01001010 - * COMA[7:4] "AWB Update Threshold" - * = 4 (0x04) 0100.... - * COMA[3:2] "Vertical window end line control 2 LSBs" - * = 2 (0x02) ....10.. - * COMA[1:0] "Vertical window start line control 2 LSBs" - * = 2 (0x02) ......10 - */ - { 0x03, 0x4a }, - - /* - * 11 CLKRC "Clock Rate Control" - * = 128 (0x80) 10000000 - * CLKRC[7] "Internal frequency doublers on off seclection" - * = 1 (0x01) 1....... "On" - * CLKRC[6] "Digital video master slave selection" - * = 0 (0x00) .0...... "Master mode, sensor - * provides PCLK" - * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }" - * = 0 (0x00) ..000000 - */ - { 0x11, 0x80 }, - - /* - * 12 COMH "Common Control H" - * = 0 (0x00) 00000000 - * COMH[7] "SRST" - * = 0 (0x00) 0....... "No-op" - * COMH[6:4] "Resolution selection" - * = 0 (0x00) .000.... "QXGA" - * COMH[3] "Master slave selection" - * = 0 (0x00) ....0... "Master mode" - * COMH[2] "Internal B/R channel option" - * = 0 (0x00) .....0.. "B/R use same channel" - * COMH[1] "Color bar test pattern" - * = 0 (0x00) ......0. "Off" - * COMH[0] "Reserved" - * = 0 (0x00) .......0 - */ - { 0x12, 0x00 }, - - /* - * 12 COMH "Common Control H" - * = 64 (0x40) 01000000 - * COMH[7] "SRST" - * = 0 (0x00) 0....... "No-op" - * COMH[6:4] "Resolution selection" - * = 4 (0x04) .100.... "XGA" - * COMH[3] "Master slave selection" - * = 0 (0x00) ....0... "Master mode" - * COMH[2] "Internal B/R channel option" - * = 0 (0x00) .....0.. "B/R use same channel" - * COMH[1] "Color bar test pattern" - * = 0 (0x00) ......0. "Off" - * COMH[0] "Reserved" - * = 0 (0x00) .......0 - */ - { 0x12, 0x40 }, - - /* - * 17 HREFST "Horizontal window start" - * = 31 (0x1F) 00011111 - * HREFST[7:0] "Horizontal window start, 8 MSBs" - * = 31 (0x1F) 00011111 - */ - { 0x17, 0x1f }, - - /* - * 18 HREFEND "Horizontal window end" - * = 95 (0x5F) 01011111 - * HREFEND[7:0] "Horizontal Window End, 8 MSBs" - * = 95 (0x5F) 01011111 - */ - { 0x18, 0x5f }, - - /* - * 19 VSTRT "Vertical window start" - * = 0 (0x00) 00000000 - * VSTRT[7:0] "Vertical Window Start, 8 MSBs" - * = 0 (0x00) 00000000 - */ - { 0x19, 0x00 }, - - /* - * 1A VEND "Vertical window end" - * = 96 (0x60) 01100000 - * VEND[7:0] "Vertical Window End, 8 MSBs" - * = 96 (0x60) 01100000 - */ - { 0x1a, 0x60 }, - - /* - * 32 COMM "Common Control M" - * = 18 (0x12) 00010010 - * COMM[7:6] "Pixel clock divide option" - * = 0 (0x00) 00...... "/1" - * COMM[5:3] "Horizontal window end position, 3 LSBs" - * = 2 (0x02) ..010... - * COMM[2:0] "Horizontal window start position, 3 LSBs" - * = 2 (0x02) .....010 - */ - { 0x32, 0x12 }, - - /* - * 03 COMA "Common Control A" - * = 74 (0x4A) 01001010 - * COMA[7:4] "AWB Update Threshold" - * = 4 (0x04) 0100.... - * COMA[3:2] "Vertical window end line control 2 LSBs" - * = 2 (0x02) ....10.. - * COMA[1:0] "Vertical window start line control 2 LSBs" - * = 2 (0x02) ......10 - */ - { 0x03, 0x4a }, - - /* - * 02 RED "Red Gain Control" - * = 175 (0xAF) 10101111 - * RED[7] "Action" - * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" - * RED[6:0] "Value" - * = 47 (0x2F) .0101111 - */ - { 0x02, 0xaf }, - - /* - * 2D ADDVSL "VSYNC Pulse Width" - * = 210 (0xD2) 11010010 - * ADDVSL[7:0] "VSYNC pulse width, LSB" - * = 210 (0xD2) 11010010 - */ - { 0x2d, 0xd2 }, - - /* - * 00 GAIN = 24 (0x18) 00011000 - * GAIN[7:6] "Reserved" - * = 0 (0x00) 00...... - * GAIN[5] "Double" - * = 0 (0x00) ..0..... "False" - * GAIN[4] "Double" - * = 1 (0x01) ...1.... "True" - * GAIN[3:0] "Range" - * = 8 (0x08) ....1000 - */ - { 0x00, 0x18 }, - - /* - * 01 BLUE "Blue Gain Control" - * = 240 (0xF0) 11110000 - * BLUE[7] "Action" - * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" - * BLUE[6:0] "Value" - * = 112 (0x70) .1110000 - */ - { 0x01, 0xf0 }, - - /* - * 10 AEC "Automatic Exposure Control" - * = 10 (0x0A) 00001010 - * AEC[7:0] "Automatic Exposure Control, 8 MSBs" - * = 10 (0x0A) 00001010 - */ - { 0x10, 0x0a }, - - { 0xe1, 0x67 }, - { 0xe3, 0x03 }, - { 0xe4, 0x26 }, - { 0xe5, 0x3e }, - { 0xf8, 0x01 }, - { 0xff, 0x01 }, -}; - -static const struct ov_i2c_regvals norm_6x20[] = { - { 0x12, 0x80 }, /* reset */ - { 0x11, 0x01 }, - { 0x03, 0x60 }, - { 0x05, 0x7f }, /* For when autoadjust is off */ - { 0x07, 0xa8 }, - /* The ratio of 0x0c and 0x0d controls the white point */ - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x0f, 0x15 }, /* COMS */ - { 0x10, 0x75 }, /* AEC Exposure time */ - { 0x12, 0x24 }, /* Enable AGC */ - { 0x14, 0x04 }, - /* 0x16: 0x06 helps frame stability with moving objects */ - { 0x16, 0x06 }, -/* { 0x20, 0x30 }, * Aperture correction enable */ - { 0x26, 0xb2 }, /* BLC enable */ - /* 0x28: 0x05 Selects RGB format if RGB on */ - { 0x28, 0x05 }, - { 0x2a, 0x04 }, /* Disable framerate adjust */ -/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ - { 0x2d, 0x85 }, - { 0x33, 0xa0 }, /* Color Processing Parameter */ - { 0x34, 0xd2 }, /* Max A/D range */ - { 0x38, 0x8b }, - { 0x39, 0x40 }, - - { 0x3c, 0x39 }, /* Enable AEC mode changing */ - { 0x3c, 0x3c }, /* Change AEC mode */ - { 0x3c, 0x24 }, /* Disable AEC mode changing */ - - { 0x3d, 0x80 }, - /* These next two registers (0x4a, 0x4b) are undocumented. - * They control the color balance */ - { 0x4a, 0x80 }, - { 0x4b, 0x80 }, - { 0x4d, 0xd2 }, /* This reduces noise a bit */ - { 0x4e, 0xc1 }, - { 0x4f, 0x04 }, -/* Do 50-53 have any effect? */ -/* Toggle 0x12[2] off and on here? */ -}; - -static const struct ov_i2c_regvals norm_6x30[] = { - { 0x12, 0x80 }, /* Reset */ - { 0x00, 0x1f }, /* Gain */ - { 0x01, 0x99 }, /* Blue gain */ - { 0x02, 0x7c }, /* Red gain */ - { 0x03, 0xc0 }, /* Saturation */ - { 0x05, 0x0a }, /* Contrast */ - { 0x06, 0x95 }, /* Brightness */ - { 0x07, 0x2d }, /* Sharpness */ - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */ - { 0x0f, 0x05 }, - { 0x10, 0x9a }, - { 0x11, 0x00 }, /* Pixel clock = fastest */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x13, 0x21 }, - { 0x14, 0x80 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x38 }, - { 0x18, 0xea }, - { 0x19, 0x04 }, - { 0x1a, 0x93 }, - { 0x1b, 0x00 }, - { 0x1e, 0xc4 }, - { 0x1f, 0x04 }, - { 0x20, 0x20 }, - { 0x21, 0x10 }, - { 0x22, 0x88 }, - { 0x23, 0xc0 }, /* Crystal circuit power level */ - { 0x25, 0x9a }, /* Increase AEC black ratio */ - { 0x26, 0xb2 }, /* BLC enable */ - { 0x27, 0xa2 }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x84 }, /* 60 Hz power */ - { 0x2b, 0xa8 }, /* 60 Hz power */ - { 0x2c, 0xa0 }, - { 0x2d, 0x95 }, /* Enable auto-brightness */ - { 0x2e, 0x88 }, - { 0x33, 0x26 }, - { 0x34, 0x03 }, - { 0x36, 0x8f }, - { 0x37, 0x80 }, - { 0x38, 0x83 }, - { 0x39, 0x80 }, - { 0x3a, 0x0f }, - { 0x3b, 0x3c }, - { 0x3c, 0x1a }, - { 0x3d, 0x80 }, - { 0x3e, 0x80 }, - { 0x3f, 0x0e }, - { 0x40, 0x00 }, /* White bal */ - { 0x41, 0x00 }, /* White bal */ - { 0x42, 0x80 }, - { 0x43, 0x3f }, /* White bal */ - { 0x44, 0x80 }, - { 0x45, 0x20 }, - { 0x46, 0x20 }, - { 0x47, 0x80 }, - { 0x48, 0x7f }, - { 0x49, 0x00 }, - { 0x4a, 0x00 }, - { 0x4b, 0x80 }, - { 0x4c, 0xd0 }, - { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ - { 0x4e, 0x40 }, - { 0x4f, 0x07 }, /* UV avg., col. killer: max */ - { 0x50, 0xff }, - { 0x54, 0x23 }, /* Max AGC gain: 18dB */ - { 0x55, 0xff }, - { 0x56, 0x12 }, - { 0x57, 0x81 }, - { 0x58, 0x75 }, - { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ - { 0x5a, 0x2c }, - { 0x5b, 0x0f }, /* AWB chrominance levels */ - { 0x5c, 0x10 }, - { 0x3d, 0x80 }, - { 0x27, 0xa6 }, - { 0x12, 0x20 }, /* Toggle AWB */ - { 0x12, 0x24 }, -}; - -/* Lawrence Glaister reports: - * - * Register 0x0f in the 7610 has the following effects: - * - * 0x85 (AEC method 1): Best overall, good contrast range - * 0x45 (AEC method 2): Very overexposed - * 0xa5 (spec sheet default): Ok, but the black level is - * shifted resulting in loss of contrast - * 0x05 (old driver setting): very overexposed, too much - * contrast - */ -static const struct ov_i2c_regvals norm_7610[] = { - { 0x10, 0xff }, - { 0x16, 0x06 }, - { 0x28, 0x24 }, - { 0x2b, 0xac }, - { 0x12, 0x00 }, - { 0x38, 0x81 }, - { 0x28, 0x24 }, /* 0c */ - { 0x0f, 0x85 }, /* lg's setting */ - { 0x15, 0x01 }, - { 0x20, 0x1c }, - { 0x23, 0x2a }, - { 0x24, 0x10 }, - { 0x25, 0x8a }, - { 0x26, 0xa2 }, - { 0x27, 0xc2 }, - { 0x2a, 0x04 }, - { 0x2c, 0xfe }, - { 0x2d, 0x93 }, - { 0x30, 0x71 }, - { 0x31, 0x60 }, - { 0x32, 0x26 }, - { 0x33, 0x20 }, - { 0x34, 0x48 }, - { 0x12, 0x24 }, - { 0x11, 0x01 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, -}; - -static const struct ov_i2c_regvals norm_7620[] = { - { 0x12, 0x80 }, /* reset */ - { 0x00, 0x00 }, /* gain */ - { 0x01, 0x80 }, /* blue gain */ - { 0x02, 0x80 }, /* red gain */ - { 0x03, 0xc0 }, /* OV7670_R03_VREF */ - { 0x06, 0x60 }, - { 0x07, 0x00 }, - { 0x0c, 0x24 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x11, 0x01 }, - { 0x12, 0x24 }, - { 0x13, 0x01 }, - { 0x14, 0x84 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x2f }, - { 0x18, 0xcf }, - { 0x19, 0x06 }, - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0x18 }, - { 0x21, 0x80 }, - { 0x22, 0x80 }, - { 0x23, 0x00 }, - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */ - { 0x29, 0x00 }, - { 0x2a, 0x10 }, - { 0x2b, 0x00 }, - { 0x2c, 0x88 }, - { 0x2d, 0x91 }, - { 0x2e, 0x80 }, - { 0x2f, 0x44 }, - { 0x60, 0x27 }, - { 0x61, 0x02 }, - { 0x62, 0x5f }, - { 0x63, 0xd5 }, - { 0x64, 0x57 }, - { 0x65, 0x83 }, - { 0x66, 0x55 }, - { 0x67, 0x92 }, - { 0x68, 0xcf }, - { 0x69, 0x76 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x02 }, - { 0x6d, 0x44 }, - { 0x6e, 0x80 }, - { 0x6f, 0x1d }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 }, - { 0x75, 0x8e }, - { 0x76, 0x00 }, - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0xe2 }, - { 0x7c, 0x00 }, -}; - -/* 7640 and 7648. The defaults should be OK for most registers. */ -static const struct ov_i2c_regvals norm_7640[] = { - { 0x12, 0x80 }, - { 0x12, 0x14 }, -}; - -static const struct ov_regvals init_519_ov7660[] = { - { 0x5d, 0x03 }, /* Turn off suspend mode */ - { 0x53, 0x9b }, /* 0x9f enables the (unused) microcontroller */ - { 0x54, 0x0f }, /* bit2 (jpeg enable) */ - { 0xa2, 0x20 }, /* a2-a5 are undocumented */ - { 0xa3, 0x18 }, - { 0xa4, 0x04 }, - { 0xa5, 0x28 }, - { 0x37, 0x00 }, /* SetUsbInit */ - { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ - /* Enable both fields, YUV Input, disable defect comp (why?) */ - { 0x20, 0x0c }, /* 0x0d does U <-> V swap */ - { 0x21, 0x38 }, - { 0x22, 0x1d }, - { 0x17, 0x50 }, /* undocumented */ - { 0x37, 0x00 }, /* undocumented */ - { 0x40, 0xff }, /* I2C timeout counter */ - { 0x46, 0x00 }, /* I2C clock prescaler */ -}; -static const struct ov_i2c_regvals norm_7660[] = { - {OV7670_R12_COM7, OV7670_COM7_RESET}, - {OV7670_R11_CLKRC, 0x81}, - {0x92, 0x00}, /* DM_LNL */ - {0x93, 0x00}, /* DM_LNH */ - {0x9d, 0x4c}, /* BD50ST */ - {0x9e, 0x3f}, /* BD60ST */ - {OV7670_R3B_COM11, 0x02}, - {OV7670_R13_COM8, 0xf5}, - {OV7670_R10_AECH, 0x00}, - {OV7670_R00_GAIN, 0x00}, - {OV7670_R01_BLUE, 0x7c}, - {OV7670_R02_RED, 0x9d}, - {OV7670_R12_COM7, 0x00}, - {OV7670_R04_COM1, 00}, - {OV7670_R18_HSTOP, 0x01}, - {OV7670_R17_HSTART, 0x13}, - {OV7670_R32_HREF, 0x92}, - {OV7670_R19_VSTART, 0x02}, - {OV7670_R1A_VSTOP, 0x7a}, - {OV7670_R03_VREF, 0x00}, - {OV7670_R0E_COM5, 0x04}, - {OV7670_R0F_COM6, 0x62}, - {OV7670_R15_COM10, 0x00}, - {0x16, 0x02}, /* RSVD */ - {0x1b, 0x00}, /* PSHFT */ - {OV7670_R1E_MVFP, 0x01}, - {0x29, 0x3c}, /* RSVD */ - {0x33, 0x00}, /* CHLF */ - {0x34, 0x07}, /* ARBLM */ - {0x35, 0x84}, /* RSVD */ - {0x36, 0x00}, /* RSVD */ - {0x37, 0x04}, /* ADC */ - {0x39, 0x43}, /* OFON */ - {OV7670_R3A_TSLB, 0x00}, - {OV7670_R3C_COM12, 0x6c}, - {OV7670_R3D_COM13, 0x98}, - {OV7670_R3F_EDGE, 0x23}, - {OV7670_R40_COM15, 0xc1}, - {OV7670_R41_COM16, 0x22}, - {0x6b, 0x0a}, /* DBLV */ - {0xa1, 0x08}, /* RSVD */ - {0x69, 0x80}, /* HV */ - {0x43, 0xf0}, /* RSVD.. */ - {0x44, 0x10}, - {0x45, 0x78}, - {0x46, 0xa8}, - {0x47, 0x60}, - {0x48, 0x80}, - {0x59, 0xba}, - {0x5a, 0x9a}, - {0x5b, 0x22}, - {0x5c, 0xb9}, - {0x5d, 0x9b}, - {0x5e, 0x10}, - {0x5f, 0xe0}, - {0x60, 0x85}, - {0x61, 0x60}, - {0x9f, 0x9d}, /* RSVD */ - {0xa0, 0xa0}, /* DSPC2 */ - {0x4f, 0x60}, /* matrix */ - {0x50, 0x64}, - {0x51, 0x04}, - {0x52, 0x18}, - {0x53, 0x3c}, - {0x54, 0x54}, - {0x55, 0x40}, - {0x56, 0x40}, - {0x57, 0x40}, - {0x58, 0x0d}, /* matrix sign */ - {0x8b, 0xcc}, /* RSVD */ - {0x8c, 0xcc}, - {0x8d, 0xcf}, - {0x6c, 0x40}, /* gamma curve */ - {0x6d, 0xe0}, - {0x6e, 0xa0}, - {0x6f, 0x80}, - {0x70, 0x70}, - {0x71, 0x80}, - {0x72, 0x60}, - {0x73, 0x60}, - {0x74, 0x50}, - {0x75, 0x40}, - {0x76, 0x38}, - {0x77, 0x3c}, - {0x78, 0x32}, - {0x79, 0x1a}, - {0x7a, 0x28}, - {0x7b, 0x24}, - {0x7c, 0x04}, /* gamma curve */ - {0x7d, 0x12}, - {0x7e, 0x26}, - {0x7f, 0x46}, - {0x80, 0x54}, - {0x81, 0x64}, - {0x82, 0x70}, - {0x83, 0x7c}, - {0x84, 0x86}, - {0x85, 0x8e}, - {0x86, 0x9c}, - {0x87, 0xab}, - {0x88, 0xc4}, - {0x89, 0xd1}, - {0x8a, 0xe5}, - {OV7670_R14_COM9, 0x1e}, - {OV7670_R24_AEW, 0x80}, - {OV7670_R25_AEB, 0x72}, - {OV7670_R26_VPT, 0xb3}, - {0x62, 0x80}, /* LCC1 */ - {0x63, 0x80}, /* LCC2 */ - {0x64, 0x06}, /* LCC3 */ - {0x65, 0x00}, /* LCC4 */ - {0x66, 0x01}, /* LCC5 */ - {0x94, 0x0e}, /* RSVD.. */ - {0x95, 0x14}, - {OV7670_R13_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT - | 0x10 - | OV7670_COM8_AGC - | OV7670_COM8_AWB - | OV7670_COM8_AEC}, - {0xa1, 0xc8} -}; -static const struct ov_i2c_regvals norm_9600[] = { - {0x12, 0x80}, - {0x0c, 0x28}, - {0x11, 0x80}, - {0x13, 0xb5}, - {0x14, 0x3e}, - {0x1b, 0x04}, - {0x24, 0xb0}, - {0x25, 0x90}, - {0x26, 0x94}, - {0x35, 0x90}, - {0x37, 0x07}, - {0x38, 0x08}, - {0x01, 0x8e}, - {0x02, 0x85} -}; - -/* 7670. Defaults taken from OmniVision provided data, -* as provided by Jonathan Corbet of OLPC */ -static const struct ov_i2c_regvals norm_7670[] = { - { OV7670_R12_COM7, OV7670_COM7_RESET }, - { OV7670_R3A_TSLB, 0x04 }, /* OV */ - { OV7670_R12_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ - { OV7670_R11_CLKRC, 0x01 }, -/* - * Set the hardware window. These values from OV don't entirely - * make sense - hstop is less than hstart. But they work... - */ - { OV7670_R17_HSTART, 0x13 }, - { OV7670_R18_HSTOP, 0x01 }, - { OV7670_R32_HREF, 0xb6 }, - { OV7670_R19_VSTART, 0x02 }, - { OV7670_R1A_VSTOP, 0x7a }, - { OV7670_R03_VREF, 0x0a }, - - { OV7670_R0C_COM3, 0x00 }, - { OV7670_R3E_COM14, 0x00 }, -/* Mystery scaling numbers */ - { 0x70, 0x3a }, - { 0x71, 0x35 }, - { 0x72, 0x11 }, - { 0x73, 0xf0 }, - { 0xa2, 0x02 }, -/* { OV7670_R15_COM10, 0x0 }, */ - -/* Gamma curve values */ - { 0x7a, 0x20 }, - { 0x7b, 0x10 }, - { 0x7c, 0x1e }, - { 0x7d, 0x35 }, - { 0x7e, 0x5a }, - { 0x7f, 0x69 }, - { 0x80, 0x76 }, - { 0x81, 0x80 }, - { 0x82, 0x88 }, - { 0x83, 0x8f }, - { 0x84, 0x96 }, - { 0x85, 0xa3 }, - { 0x86, 0xaf }, - { 0x87, 0xc4 }, - { 0x88, 0xd7 }, - { 0x89, 0xe8 }, - -/* AGC and AEC parameters. Note we start by disabling those features, - then turn them only after tweaking the values. */ - { OV7670_R13_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT }, - { OV7670_R00_GAIN, 0x00 }, - { OV7670_R10_AECH, 0x00 }, - { OV7670_R0D_COM4, 0x40 }, /* magic reserved bit */ - { OV7670_R14_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ - { OV7670_RA5_BD50MAX, 0x05 }, - { OV7670_RAB_BD60MAX, 0x07 }, - { OV7670_R24_AEW, 0x95 }, - { OV7670_R25_AEB, 0x33 }, - { OV7670_R26_VPT, 0xe3 }, - { OV7670_R9F_HAECC1, 0x78 }, - { OV7670_RA0_HAECC2, 0x68 }, - { 0xa1, 0x03 }, /* magic */ - { OV7670_RA6_HAECC3, 0xd8 }, - { OV7670_RA7_HAECC4, 0xd8 }, - { OV7670_RA8_HAECC5, 0xf0 }, - { OV7670_RA9_HAECC6, 0x90 }, - { OV7670_RAA_HAECC7, 0x94 }, - { OV7670_R13_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT - | OV7670_COM8_AGC - | OV7670_COM8_AEC }, - -/* Almost all of these are magic "reserved" values. */ - { OV7670_R0E_COM5, 0x61 }, - { OV7670_R0F_COM6, 0x4b }, - { 0x16, 0x02 }, - { OV7670_R1E_MVFP, 0x07 }, - { 0x21, 0x02 }, - { 0x22, 0x91 }, - { 0x29, 0x07 }, - { 0x33, 0x0b }, - { 0x35, 0x0b }, - { 0x37, 0x1d }, - { 0x38, 0x71 }, - { 0x39, 0x2a }, - { OV7670_R3C_COM12, 0x78 }, - { 0x4d, 0x40 }, - { 0x4e, 0x20 }, - { OV7670_R69_GFIX, 0x00 }, - { 0x6b, 0x4a }, - { 0x74, 0x10 }, - { 0x8d, 0x4f }, - { 0x8e, 0x00 }, - { 0x8f, 0x00 }, - { 0x90, 0x00 }, - { 0x91, 0x00 }, - { 0x96, 0x00 }, - { 0x9a, 0x00 }, - { 0xb0, 0x84 }, - { 0xb1, 0x0c }, - { 0xb2, 0x0e }, - { 0xb3, 0x82 }, - { 0xb8, 0x0a }, - -/* More reserved magic, some of which tweaks white balance */ - { 0x43, 0x0a }, - { 0x44, 0xf0 }, - { 0x45, 0x34 }, - { 0x46, 0x58 }, - { 0x47, 0x28 }, - { 0x48, 0x3a }, - { 0x59, 0x88 }, - { 0x5a, 0x88 }, - { 0x5b, 0x44 }, - { 0x5c, 0x67 }, - { 0x5d, 0x49 }, - { 0x5e, 0x0e }, - { 0x6c, 0x0a }, - { 0x6d, 0x55 }, - { 0x6e, 0x11 }, - { 0x6f, 0x9f }, /* "9e for advance AWB" */ - { 0x6a, 0x40 }, - { OV7670_R01_BLUE, 0x40 }, - { OV7670_R02_RED, 0x60 }, - { OV7670_R13_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT - | OV7670_COM8_AGC - | OV7670_COM8_AEC - | OV7670_COM8_AWB }, - -/* Matrix coefficients */ - { 0x4f, 0x80 }, - { 0x50, 0x80 }, - { 0x51, 0x00 }, - { 0x52, 0x22 }, - { 0x53, 0x5e }, - { 0x54, 0x80 }, - { 0x58, 0x9e }, - - { OV7670_R41_COM16, OV7670_COM16_AWBGAIN }, - { OV7670_R3F_EDGE, 0x00 }, - { 0x75, 0x05 }, - { 0x76, 0xe1 }, - { 0x4c, 0x00 }, - { 0x77, 0x01 }, - { OV7670_R3D_COM13, OV7670_COM13_GAMMA - | OV7670_COM13_UVSAT - | 2}, /* was 3 */ - { 0x4b, 0x09 }, - { 0xc9, 0x60 }, - { OV7670_R41_COM16, 0x38 }, - { 0x56, 0x40 }, - - { 0x34, 0x11 }, - { OV7670_R3B_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, - { 0xa4, 0x88 }, - { 0x96, 0x00 }, - { 0x97, 0x30 }, - { 0x98, 0x20 }, - { 0x99, 0x30 }, - { 0x9a, 0x84 }, - { 0x9b, 0x29 }, - { 0x9c, 0x03 }, - { 0x9d, 0x4c }, - { 0x9e, 0x3f }, - { 0x78, 0x04 }, - -/* Extra-weird stuff. Some sort of multiplexor register */ - { 0x79, 0x01 }, - { 0xc8, 0xf0 }, - { 0x79, 0x0f }, - { 0xc8, 0x00 }, - { 0x79, 0x10 }, - { 0xc8, 0x7e }, - { 0x79, 0x0a }, - { 0xc8, 0x80 }, - { 0x79, 0x0b }, - { 0xc8, 0x01 }, - { 0x79, 0x0c }, - { 0xc8, 0x0f }, - { 0x79, 0x0d }, - { 0xc8, 0x20 }, - { 0x79, 0x09 }, - { 0xc8, 0x80 }, - { 0x79, 0x02 }, - { 0xc8, 0xc0 }, - { 0x79, 0x03 }, - { 0xc8, 0x40 }, - { 0x79, 0x05 }, - { 0xc8, 0x30 }, - { 0x79, 0x26 }, -}; - -static const struct ov_i2c_regvals norm_8610[] = { - { 0x12, 0x80 }, - { 0x00, 0x00 }, - { 0x01, 0x80 }, - { 0x02, 0x80 }, - { 0x03, 0xc0 }, - { 0x04, 0x30 }, - { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ - { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ - { 0x0a, 0x86 }, - { 0x0b, 0xb0 }, - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x11, 0x01 }, - { 0x12, 0x25 }, - { 0x13, 0x01 }, - { 0x14, 0x04 }, - { 0x15, 0x01 }, /* Lin and Win think different about UV order */ - { 0x16, 0x03 }, - { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ - { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ - { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ - { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ - { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ - { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x80 }, - { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ - { 0x2c, 0xac }, - { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ - { 0x2e, 0x80 }, - { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ - { 0x4c, 0x00 }, - { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ - { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ - { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ - { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ - { 0x63, 0xff }, - { 0x64, 0x53 }, /* new windrv 090403 says 0x57, - * maybe thats wrong */ - { 0x65, 0x00 }, - { 0x66, 0x55 }, - { 0x67, 0xb0 }, - { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ - { 0x69, 0x02 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but - * deleting bit7 colors the first images red */ - { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ - { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ - { 0x6f, 0x01 }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ - { 0x75, 0x0e }, - { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ - { 0x7c, 0x00 }, - { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ - { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ - { 0x7f, 0xfb }, - { 0x80, 0x28 }, - { 0x81, 0x00 }, - { 0x82, 0x23 }, - { 0x83, 0x0b }, - { 0x84, 0x00 }, - { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ - { 0x86, 0xc9 }, - { 0x87, 0x00 }, - { 0x88, 0x00 }, - { 0x89, 0x01 }, - { 0x12, 0x20 }, - { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ -}; - -static unsigned char ov7670_abs_to_sm(unsigned char v) -{ - if (v > 127) - return v & 0x7f; - return (128 - v) | 0x80; -} - -/* Write a OV519 register */ -static void reg_w(struct sd *sd, u16 index, u16 value) -{ - int ret, req = 0; - - if (sd->gspca_dev.usb_err < 0) - return; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - req = 2; - break; - case BRIDGE_OVFX2: - req = 0x0a; - /* fall through */ - case BRIDGE_W9968CF: - PDEBUG(D_USBO, "SET %02x %04x %04x", - req, value, index); - ret = usb_control_msg(sd->gspca_dev.dev, - usb_sndctrlpipe(sd->gspca_dev.dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - goto leave; - default: - req = 1; - } - - PDEBUG(D_USBO, "SET %02x 0000 %04x %02x", - req, index, value); - sd->gspca_dev.usb_buf[0] = value; - ret = usb_control_msg(sd->gspca_dev.dev, - usb_sndctrlpipe(sd->gspca_dev.dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, - sd->gspca_dev.usb_buf, 1, 500); -leave: - if (ret < 0) { - pr_err("reg_w %02x failed %d\n", index, ret); - sd->gspca_dev.usb_err = ret; - return; - } -} - -/* Read from a OV519 register, note not valid for the w9968cf!! */ -/* returns: negative is error, pos or zero is data */ -static int reg_r(struct sd *sd, u16 index) -{ - int ret; - int req; - - if (sd->gspca_dev.usb_err < 0) - return -1; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - req = 3; - break; - case BRIDGE_OVFX2: - req = 0x0b; - break; - default: - req = 1; - } - - ret = usb_control_msg(sd->gspca_dev.dev, - usb_rcvctrlpipe(sd->gspca_dev.dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, sd->gspca_dev.usb_buf, 1, 500); - - if (ret >= 0) { - ret = sd->gspca_dev.usb_buf[0]; - PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", - req, index, ret); - } else { - pr_err("reg_r %02x failed %d\n", index, ret); - sd->gspca_dev.usb_err = ret; - } - - return ret; -} - -/* Read 8 values from a OV519 register */ -static int reg_r8(struct sd *sd, - u16 index) -{ - int ret; - - if (sd->gspca_dev.usb_err < 0) - return -1; - - ret = usb_control_msg(sd->gspca_dev.dev, - usb_rcvctrlpipe(sd->gspca_dev.dev, 0), - 1, /* REQ_IO */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, sd->gspca_dev.usb_buf, 8, 500); - - if (ret >= 0) { - ret = sd->gspca_dev.usb_buf[0]; - } else { - pr_err("reg_r8 %02x failed %d\n", index, ret); - sd->gspca_dev.usb_err = ret; - } - - return ret; -} - -/* - * Writes bits at positions specified by mask to an OV51x reg. Bits that are in - * the same position as 1's in "mask" are cleared and set to "value". Bits - * that are in the same position as 0's in "mask" are preserved, regardless - * of their respective state in "value". - */ -static void reg_w_mask(struct sd *sd, - u16 index, - u8 value, - u8 mask) -{ - int ret; - u8 oldval; - - if (mask != 0xff) { - value &= mask; /* Enforce mask on value */ - ret = reg_r(sd, index); - if (ret < 0) - return; - - oldval = ret & ~mask; /* Clear the masked bits */ - value |= oldval; /* Set the desired bits */ - } - reg_w(sd, index, value); -} - -/* - * Writes multiple (n) byte value to a single register. Only valid with certain - * registers (0x30 and 0xc4 - 0xce). - */ -static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n) -{ - int ret; - - if (sd->gspca_dev.usb_err < 0) - return; - - *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value); - - ret = usb_control_msg(sd->gspca_dev.dev, - usb_sndctrlpipe(sd->gspca_dev.dev, 0), - 1 /* REG_IO */, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, - sd->gspca_dev.usb_buf, n, 500); - if (ret < 0) { - pr_err("reg_w32 %02x failed %d\n", index, ret); - sd->gspca_dev.usb_err = ret; - } -} - -static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value) -{ - int rc, retries; - - PDEBUG(D_USBO, "ov511_i2c_w %02x %02x", reg, value); - - /* Three byte write cycle */ - for (retries = 6; ; ) { - /* Select camera register */ - reg_w(sd, R51x_I2C_SADDR_3, reg); - - /* Write "value" to I2C data port of OV511 */ - reg_w(sd, R51x_I2C_DATA, value); - - /* Initiate 3-byte write cycle */ - reg_w(sd, R511_I2C_CTL, 0x01); - - do { - rc = reg_r(sd, R511_I2C_CTL); - } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ - - if (rc < 0) - return; - - if ((rc & 2) == 0) /* Ack? */ - break; - if (--retries < 0) { - PDEBUG(D_USBO, "i2c write retries exhausted"); - return; - } - } -} - -static int ov511_i2c_r(struct sd *sd, u8 reg) -{ - int rc, value, retries; - - /* Two byte write cycle */ - for (retries = 6; ; ) { - /* Select camera register */ - reg_w(sd, R51x_I2C_SADDR_2, reg); - - /* Initiate 2-byte write cycle */ - reg_w(sd, R511_I2C_CTL, 0x03); - - do { - rc = reg_r(sd, R511_I2C_CTL); - } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ - - if (rc < 0) - return rc; - - if ((rc & 2) == 0) /* Ack? */ - break; - - /* I2C abort */ - reg_w(sd, R511_I2C_CTL, 0x10); - - if (--retries < 0) { - PDEBUG(D_USBI, "i2c write retries exhausted"); - return -1; - } - } - - /* Two byte read cycle */ - for (retries = 6; ; ) { - /* Initiate 2-byte read cycle */ - reg_w(sd, R511_I2C_CTL, 0x05); - - do { - rc = reg_r(sd, R511_I2C_CTL); - } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ - - if (rc < 0) - return rc; - - if ((rc & 2) == 0) /* Ack? */ - break; - - /* I2C abort */ - reg_w(sd, R511_I2C_CTL, 0x10); - - if (--retries < 0) { - PDEBUG(D_USBI, "i2c read retries exhausted"); - return -1; - } - } - - value = reg_r(sd, R51x_I2C_DATA); - - PDEBUG(D_USBI, "ov511_i2c_r %02x %02x", reg, value); - - /* This is needed to make i2c_w() work */ - reg_w(sd, R511_I2C_CTL, 0x05); - - return value; -} - -/* - * The OV518 I2C I/O procedure is different, hence, this function. - * This is normally only called from i2c_w(). Note that this function - * always succeeds regardless of whether the sensor is present and working. - */ -static void ov518_i2c_w(struct sd *sd, - u8 reg, - u8 value) -{ - PDEBUG(D_USBO, "ov518_i2c_w %02x %02x", reg, value); - - /* Select camera register */ - reg_w(sd, R51x_I2C_SADDR_3, reg); - - /* Write "value" to I2C data port of OV511 */ - reg_w(sd, R51x_I2C_DATA, value); - - /* Initiate 3-byte write cycle */ - reg_w(sd, R518_I2C_CTL, 0x01); - - /* wait for write complete */ - msleep(4); - reg_r8(sd, R518_I2C_CTL); -} - -/* - * returns: negative is error, pos or zero is data - * - * The OV518 I2C I/O procedure is different, hence, this function. - * This is normally only called from i2c_r(). Note that this function - * always succeeds regardless of whether the sensor is present and working. - */ -static int ov518_i2c_r(struct sd *sd, u8 reg) -{ - int value; - - /* Select camera register */ - reg_w(sd, R51x_I2C_SADDR_2, reg); - - /* Initiate 2-byte write cycle */ - reg_w(sd, R518_I2C_CTL, 0x03); - reg_r8(sd, R518_I2C_CTL); - - /* Initiate 2-byte read cycle */ - reg_w(sd, R518_I2C_CTL, 0x05); - reg_r8(sd, R518_I2C_CTL); - - value = reg_r(sd, R51x_I2C_DATA); - PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value); - return value; -} - -static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value) -{ - int ret; - - if (sd->gspca_dev.usb_err < 0) - return; - - ret = usb_control_msg(sd->gspca_dev.dev, - usb_sndctrlpipe(sd->gspca_dev.dev, 0), - 0x02, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (u16) value, (u16) reg, NULL, 0, 500); - - if (ret < 0) { - pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret); - sd->gspca_dev.usb_err = ret; - } - - PDEBUG(D_USBO, "ovfx2_i2c_w %02x %02x", reg, value); -} - -static int ovfx2_i2c_r(struct sd *sd, u8 reg) -{ - int ret; - - if (sd->gspca_dev.usb_err < 0) - return -1; - - ret = usb_control_msg(sd->gspca_dev.dev, - usb_rcvctrlpipe(sd->gspca_dev.dev, 0), - 0x03, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, (u16) reg, sd->gspca_dev.usb_buf, 1, 500); - - if (ret >= 0) { - ret = sd->gspca_dev.usb_buf[0]; - PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret); - } else { - pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret); - sd->gspca_dev.usb_err = ret; - } - - return ret; -} - -static void i2c_w(struct sd *sd, u8 reg, u8 value) -{ - if (sd->sensor_reg_cache[reg] == value) - return; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - ov511_i2c_w(sd, reg, value); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - case BRIDGE_OV519: - ov518_i2c_w(sd, reg, value); - break; - case BRIDGE_OVFX2: - ovfx2_i2c_w(sd, reg, value); - break; - case BRIDGE_W9968CF: - w9968cf_i2c_w(sd, reg, value); - break; - } - - if (sd->gspca_dev.usb_err >= 0) { - /* Up on sensor reset empty the register cache */ - if (reg == 0x12 && (value & 0x80)) - memset(sd->sensor_reg_cache, -1, - sizeof(sd->sensor_reg_cache)); - else - sd->sensor_reg_cache[reg] = value; - } -} - -static int i2c_r(struct sd *sd, u8 reg) -{ - int ret = -1; - - if (sd->sensor_reg_cache[reg] != -1) - return sd->sensor_reg_cache[reg]; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - ret = ov511_i2c_r(sd, reg); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - case BRIDGE_OV519: - ret = ov518_i2c_r(sd, reg); - break; - case BRIDGE_OVFX2: - ret = ovfx2_i2c_r(sd, reg); - break; - case BRIDGE_W9968CF: - ret = w9968cf_i2c_r(sd, reg); - break; - } - - if (ret >= 0) - sd->sensor_reg_cache[reg] = ret; - - return ret; -} - -/* Writes bits at positions specified by mask to an I2C reg. Bits that are in - * the same position as 1's in "mask" are cleared and set to "value". Bits - * that are in the same position as 0's in "mask" are preserved, regardless - * of their respective state in "value". - */ -static void i2c_w_mask(struct sd *sd, - u8 reg, - u8 value, - u8 mask) -{ - int rc; - u8 oldval; - - value &= mask; /* Enforce mask on value */ - rc = i2c_r(sd, reg); - if (rc < 0) - return; - oldval = rc & ~mask; /* Clear the masked bits */ - value |= oldval; /* Set the desired bits */ - i2c_w(sd, reg, value); -} - -/* Temporarily stops OV511 from functioning. Must do this before changing - * registers while the camera is streaming */ -static inline void ov51x_stop(struct sd *sd) -{ - PDEBUG(D_STREAM, "stopping"); - sd->stopped = 1; - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - reg_w(sd, R51x_SYS_RESET, 0x3d); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a); - break; - case BRIDGE_OV519: - reg_w(sd, OV519_R51_RESET1, 0x0f); - reg_w(sd, OV519_R51_RESET1, 0x00); - reg_w(sd, 0x22, 0x00); /* FRAR */ - break; - case BRIDGE_OVFX2: - reg_w_mask(sd, 0x0f, 0x00, 0x02); - break; - case BRIDGE_W9968CF: - reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */ - break; - } -} - -/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not - * actually stopped (for performance). */ -static inline void ov51x_restart(struct sd *sd) -{ - PDEBUG(D_STREAM, "restarting"); - if (!sd->stopped) - return; - sd->stopped = 0; - - /* Reinitialize the stream */ - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - reg_w(sd, R51x_SYS_RESET, 0x00); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - reg_w(sd, 0x2f, 0x80); - reg_w(sd, R51x_SYS_RESET, 0x00); - break; - case BRIDGE_OV519: - reg_w(sd, OV519_R51_RESET1, 0x0f); - reg_w(sd, OV519_R51_RESET1, 0x00); - reg_w(sd, 0x22, 0x1d); /* FRAR */ - break; - case BRIDGE_OVFX2: - reg_w_mask(sd, 0x0f, 0x02, 0x02); - break; - case BRIDGE_W9968CF: - reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */ - break; - } -} - -static void ov51x_set_slave_ids(struct sd *sd, u8 slave); - -/* This does an initial reset of an OmniVision sensor and ensures that I2C - * is synchronized. Returns <0 on failure. - */ -static int init_ov_sensor(struct sd *sd, u8 slave) -{ - int i; - - ov51x_set_slave_ids(sd, slave); - - /* Reset the sensor */ - i2c_w(sd, 0x12, 0x80); - - /* Wait for it to initialize */ - msleep(150); - - for (i = 0; i < i2c_detect_tries; i++) { - if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f && - i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) { - PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i); - return 0; - } - - /* Reset the sensor */ - i2c_w(sd, 0x12, 0x80); - - /* Wait for it to initialize */ - msleep(150); - - /* Dummy read to sync I2C */ - if (i2c_r(sd, 0x00) < 0) - return -1; - } - return -1; -} - -/* Set the read and write slave IDs. The "slave" argument is the write slave, - * and the read slave will be set to (slave + 1). - * This should not be called from outside the i2c I/O functions. - * Sets I2C read and write slave IDs. Returns <0 for error - */ -static void ov51x_set_slave_ids(struct sd *sd, - u8 slave) -{ - switch (sd->bridge) { - case BRIDGE_OVFX2: - reg_w(sd, OVFX2_I2C_ADDR, slave); - return; - case BRIDGE_W9968CF: - sd->sensor_addr = slave; - return; - } - - reg_w(sd, R51x_I2C_W_SID, slave); - reg_w(sd, R51x_I2C_R_SID, slave + 1); -} - -static void write_regvals(struct sd *sd, - const struct ov_regvals *regvals, - int n) -{ - while (--n >= 0) { - reg_w(sd, regvals->reg, regvals->val); - regvals++; - } -} - -static void write_i2c_regvals(struct sd *sd, - const struct ov_i2c_regvals *regvals, - int n) -{ - while (--n >= 0) { - i2c_w(sd, regvals->reg, regvals->val); - regvals++; - } -} - -/**************************************************************************** - * - * OV511 and sensor configuration - * - ***************************************************************************/ - -/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */ -static void ov_hires_configure(struct sd *sd) -{ - int high, low; - - if (sd->bridge != BRIDGE_OVFX2) { - pr_err("error hires sensors only supported with ovfx2\n"); - return; - } - - PDEBUG(D_PROBE, "starting ov hires configuration"); - - /* Detect sensor (sub)type */ - high = i2c_r(sd, 0x0a); - low = i2c_r(sd, 0x0b); - /* info("%x, %x", high, low); */ - switch (high) { - case 0x96: - switch (low) { - case 0x40: - PDEBUG(D_PROBE, "Sensor is a OV2610"); - sd->sensor = SEN_OV2610; - return; - case 0x41: - PDEBUG(D_PROBE, "Sensor is a OV2610AE"); - sd->sensor = SEN_OV2610AE; - return; - case 0xb1: - PDEBUG(D_PROBE, "Sensor is a OV9600"); - sd->sensor = SEN_OV9600; - return; - } - break; - case 0x36: - if ((low & 0x0f) == 0x00) { - PDEBUG(D_PROBE, "Sensor is a OV3610"); - sd->sensor = SEN_OV3610; - return; - } - break; - } - pr_err("Error unknown sensor type: %02x%02x\n", high, low); -} - -/* This initializes the OV8110, OV8610 sensor. The OV8110 uses - * the same register settings as the OV8610, since they are very similar. - */ -static void ov8xx0_configure(struct sd *sd) -{ - int rc; - - PDEBUG(D_PROBE, "starting ov8xx0 configuration"); - - /* Detect sensor (sub)type */ - rc = i2c_r(sd, OV7610_REG_COM_I); - if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); - return; - } - if ((rc & 3) == 1) - sd->sensor = SEN_OV8610; - else - pr_err("Unknown image sensor version: %d\n", rc & 3); -} - -/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses - * the same register settings as the OV7610, since they are very similar. - */ -static void ov7xx0_configure(struct sd *sd) -{ - int rc, high, low; - - PDEBUG(D_PROBE, "starting OV7xx0 configuration"); - - /* Detect sensor (sub)type */ - rc = i2c_r(sd, OV7610_REG_COM_I); - - /* add OV7670 here - * it appears to be wrongly detected as a 7610 by default */ - if (rc < 0) { - pr_err("Error detecting sensor type\n"); - return; - } - if ((rc & 3) == 3) { - /* quick hack to make OV7670s work */ - high = i2c_r(sd, 0x0a); - low = i2c_r(sd, 0x0b); - /* info("%x, %x", high, low); */ - if (high == 0x76 && (low & 0xf0) == 0x70) { - PDEBUG(D_PROBE, "Sensor is an OV76%02x", low); - sd->sensor = SEN_OV7670; - } else { - PDEBUG(D_PROBE, "Sensor is an OV7610"); - sd->sensor = SEN_OV7610; - } - } else if ((rc & 3) == 1) { - /* I don't know what's different about the 76BE yet. */ - if (i2c_r(sd, 0x15) & 1) { - PDEBUG(D_PROBE, "Sensor is an OV7620AE"); - sd->sensor = SEN_OV7620AE; - } else { - PDEBUG(D_PROBE, "Sensor is an OV76BE"); - sd->sensor = SEN_OV76BE; - } - } else if ((rc & 3) == 0) { - /* try to read product id registers */ - high = i2c_r(sd, 0x0a); - if (high < 0) { - pr_err("Error detecting camera chip PID\n"); - return; - } - low = i2c_r(sd, 0x0b); - if (low < 0) { - pr_err("Error detecting camera chip VER\n"); - return; - } - if (high == 0x76) { - switch (low) { - case 0x30: - pr_err("Sensor is an OV7630/OV7635\n"); - pr_err("7630 is not supported by this driver\n"); - return; - case 0x40: - PDEBUG(D_PROBE, "Sensor is an OV7645"); - sd->sensor = SEN_OV7640; /* FIXME */ - break; - case 0x45: - PDEBUG(D_PROBE, "Sensor is an OV7645B"); - sd->sensor = SEN_OV7640; /* FIXME */ - break; - case 0x48: - PDEBUG(D_PROBE, "Sensor is an OV7648"); - sd->sensor = SEN_OV7648; - break; - case 0x60: - PDEBUG(D_PROBE, "Sensor is a OV7660"); - sd->sensor = SEN_OV7660; - break; - default: - pr_err("Unknown sensor: 0x76%02x\n", low); - return; - } - } else { - PDEBUG(D_PROBE, "Sensor is an OV7620"); - sd->sensor = SEN_OV7620; - } - } else { - pr_err("Unknown image sensor version: %d\n", rc & 3); - } -} - -/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ -static void ov6xx0_configure(struct sd *sd) -{ - int rc; - PDEBUG(D_PROBE, "starting OV6xx0 configuration"); - - /* Detect sensor (sub)type */ - rc = i2c_r(sd, OV7610_REG_COM_I); - if (rc < 0) { - pr_err("Error detecting sensor type\n"); - return; - } - - /* Ugh. The first two bits are the version bits, but - * the entire register value must be used. I guess OVT - * underestimated how many variants they would make. */ - switch (rc) { - case 0x00: - sd->sensor = SEN_OV6630; - pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n"); - break; - case 0x01: - sd->sensor = SEN_OV6620; - PDEBUG(D_PROBE, "Sensor is an OV6620"); - break; - case 0x02: - sd->sensor = SEN_OV6630; - PDEBUG(D_PROBE, "Sensor is an OV66308AE"); - break; - case 0x03: - sd->sensor = SEN_OV66308AF; - PDEBUG(D_PROBE, "Sensor is an OV66308AF"); - break; - case 0x90: - sd->sensor = SEN_OV6630; - pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n"); - break; - default: - pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc); - return; - } - - /* Set sensor-specific vars */ - sd->sif = 1; -} - -/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ -static void ov51x_led_control(struct sd *sd, int on) -{ - if (sd->invert_led) - on = !on; - - switch (sd->bridge) { - /* OV511 has no LED control */ - case BRIDGE_OV511PLUS: - reg_w(sd, R511_SYS_LED_CTL, on); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - reg_w_mask(sd, R518_GPIO_OUT, 0x02 * on, 0x02); - break; - case BRIDGE_OV519: - reg_w_mask(sd, OV519_GPIO_DATA_OUT0, on, 1); - break; - } -} - -static void sd_reset_snapshot(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!sd->snapshot_needs_reset) - return; - - /* Note it is important that we clear sd->snapshot_needs_reset, - before actually clearing the snapshot state in the bridge - otherwise we might race with the pkt_scan interrupt handler */ - sd->snapshot_needs_reset = 0; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - reg_w(sd, R51x_SYS_SNAP, 0x02); - reg_w(sd, R51x_SYS_SNAP, 0x00); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */ - reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */ - break; - case BRIDGE_OV519: - reg_w(sd, R51x_SYS_RESET, 0x40); - reg_w(sd, R51x_SYS_RESET, 0x00); - break; - } -} - -static void ov51x_upload_quan_tables(struct sd *sd) -{ - const unsigned char yQuanTable511[] = { - 0, 1, 1, 2, 2, 3, 3, 4, - 1, 1, 1, 2, 2, 3, 4, 4, - 1, 1, 2, 2, 3, 4, 4, 4, - 2, 2, 2, 3, 4, 4, 4, 4, - 2, 2, 3, 4, 4, 5, 5, 5, - 3, 3, 4, 4, 5, 5, 5, 5, - 3, 4, 4, 4, 5, 5, 5, 5, - 4, 4, 4, 4, 5, 5, 5, 5 - }; - - const unsigned char uvQuanTable511[] = { - 0, 2, 2, 3, 4, 4, 4, 4, - 2, 2, 2, 4, 4, 4, 4, 4, - 2, 2, 3, 4, 4, 4, 4, 4, - 3, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4 - }; - - /* OV518 quantization tables are 8x4 (instead of 8x8) */ - const unsigned char yQuanTable518[] = { - 5, 4, 5, 6, 6, 7, 7, 7, - 5, 5, 5, 5, 6, 7, 7, 7, - 6, 6, 6, 6, 7, 7, 7, 8, - 7, 7, 6, 7, 7, 7, 8, 8 - }; - const unsigned char uvQuanTable518[] = { - 6, 6, 6, 7, 7, 7, 7, 7, - 6, 6, 6, 7, 7, 7, 7, 7, - 6, 6, 6, 7, 7, 7, 7, 8, - 7, 7, 7, 7, 7, 7, 8, 8 - }; - - const unsigned char *pYTable, *pUVTable; - unsigned char val0, val1; - int i, size, reg = R51x_COMP_LUT_BEGIN; - - PDEBUG(D_PROBE, "Uploading quantization tables"); - - if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) { - pYTable = yQuanTable511; - pUVTable = uvQuanTable511; - size = 32; - } else { - pYTable = yQuanTable518; - pUVTable = uvQuanTable518; - size = 16; - } - - for (i = 0; i < size; i++) { - val0 = *pYTable++; - val1 = *pYTable++; - val0 &= 0x0f; - val1 &= 0x0f; - val0 |= val1 << 4; - reg_w(sd, reg, val0); - - val0 = *pUVTable++; - val1 = *pUVTable++; - val0 &= 0x0f; - val1 &= 0x0f; - val0 |= val1 << 4; - reg_w(sd, reg + size, val0); - - reg++; - } -} - -/* This initializes the OV511/OV511+ and the sensor */ -static void ov511_configure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* For 511 and 511+ */ - const struct ov_regvals init_511[] = { - { R51x_SYS_RESET, 0x7f }, - { R51x_SYS_INIT, 0x01 }, - { R51x_SYS_RESET, 0x7f }, - { R51x_SYS_INIT, 0x01 }, - { R51x_SYS_RESET, 0x3f }, - { R51x_SYS_INIT, 0x01 }, - { R51x_SYS_RESET, 0x3d }, - }; - - const struct ov_regvals norm_511[] = { - { R511_DRAM_FLOW_CTL, 0x01 }, - { R51x_SYS_SNAP, 0x00 }, - { R51x_SYS_SNAP, 0x02 }, - { R51x_SYS_SNAP, 0x00 }, - { R511_FIFO_OPTS, 0x1f }, - { R511_COMP_EN, 0x00 }, - { R511_COMP_LUT_EN, 0x03 }, - }; - - const struct ov_regvals norm_511_p[] = { - { R511_DRAM_FLOW_CTL, 0xff }, - { R51x_SYS_SNAP, 0x00 }, - { R51x_SYS_SNAP, 0x02 }, - { R51x_SYS_SNAP, 0x00 }, - { R511_FIFO_OPTS, 0xff }, - { R511_COMP_EN, 0x00 }, - { R511_COMP_LUT_EN, 0x03 }, - }; - - const struct ov_regvals compress_511[] = { - { 0x70, 0x1f }, - { 0x71, 0x05 }, - { 0x72, 0x06 }, - { 0x73, 0x06 }, - { 0x74, 0x14 }, - { 0x75, 0x03 }, - { 0x76, 0x04 }, - { 0x77, 0x04 }, - }; - - PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID)); - - write_regvals(sd, init_511, ARRAY_SIZE(init_511)); - - switch (sd->bridge) { - case BRIDGE_OV511: - write_regvals(sd, norm_511, ARRAY_SIZE(norm_511)); - break; - case BRIDGE_OV511PLUS: - write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p)); - break; - } - - /* Init compression */ - write_regvals(sd, compress_511, ARRAY_SIZE(compress_511)); - - ov51x_upload_quan_tables(sd); -} - -/* This initializes the OV518/OV518+ and the sensor */ -static void ov518_configure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* For 518 and 518+ */ - const struct ov_regvals init_518[] = { - { R51x_SYS_RESET, 0x40 }, - { R51x_SYS_INIT, 0xe1 }, - { R51x_SYS_RESET, 0x3e }, - { R51x_SYS_INIT, 0xe1 }, - { R51x_SYS_RESET, 0x00 }, - { R51x_SYS_INIT, 0xe1 }, - { 0x46, 0x00 }, - { 0x5d, 0x03 }, - }; - - const struct ov_regvals norm_518[] = { - { R51x_SYS_SNAP, 0x02 }, /* Reset */ - { R51x_SYS_SNAP, 0x01 }, /* Enable */ - { 0x31, 0x0f }, - { 0x5d, 0x03 }, - { 0x24, 0x9f }, - { 0x25, 0x90 }, - { 0x20, 0x00 }, - { 0x51, 0x04 }, - { 0x71, 0x19 }, - { 0x2f, 0x80 }, - }; - - const struct ov_regvals norm_518_p[] = { - { R51x_SYS_SNAP, 0x02 }, /* Reset */ - { R51x_SYS_SNAP, 0x01 }, /* Enable */ - { 0x31, 0x0f }, - { 0x5d, 0x03 }, - { 0x24, 0x9f }, - { 0x25, 0x90 }, - { 0x20, 0x60 }, - { 0x51, 0x02 }, - { 0x71, 0x19 }, - { 0x40, 0xff }, - { 0x41, 0x42 }, - { 0x46, 0x00 }, - { 0x33, 0x04 }, - { 0x21, 0x19 }, - { 0x3f, 0x10 }, - { 0x2f, 0x80 }, - }; - - /* First 5 bits of custom ID reg are a revision ID on OV518 */ - PDEBUG(D_PROBE, "Device revision %d", - 0x1f & reg_r(sd, R51x_SYS_CUST_ID)); - - write_regvals(sd, init_518, ARRAY_SIZE(init_518)); - - /* Set LED GPIO pin to output mode */ - reg_w_mask(sd, R518_GPIO_CTL, 0x00, 0x02); - - switch (sd->bridge) { - case BRIDGE_OV518: - write_regvals(sd, norm_518, ARRAY_SIZE(norm_518)); - break; - case BRIDGE_OV518PLUS: - write_regvals(sd, norm_518_p, ARRAY_SIZE(norm_518_p)); - break; - } - - ov51x_upload_quan_tables(sd); - - reg_w(sd, 0x2f, 0x80); -} - -static void ov519_configure(struct sd *sd) -{ - static const struct ov_regvals init_519[] = { - { 0x5a, 0x6d }, /* EnableSystem */ - { 0x53, 0x9b }, /* don't enable the microcontroller */ - { OV519_R54_EN_CLK1, 0xff }, /* set bit2 to enable jpeg */ - { 0x5d, 0x03 }, - { 0x49, 0x01 }, - { 0x48, 0x00 }, - /* Set LED pin to output mode. Bit 4 must be cleared or sensor - * detection will fail. This deserves further investigation. */ - { OV519_GPIO_IO_CTRL0, 0xee }, - { OV519_R51_RESET1, 0x0f }, - { OV519_R51_RESET1, 0x00 }, - { 0x22, 0x00 }, - /* windows reads 0x55 at this point*/ - }; - - write_regvals(sd, init_519, ARRAY_SIZE(init_519)); -} - -static void ovfx2_configure(struct sd *sd) -{ - static const struct ov_regvals init_fx2[] = { - { 0x00, 0x60 }, - { 0x02, 0x01 }, - { 0x0f, 0x1d }, - { 0xe9, 0x82 }, - { 0xea, 0xc7 }, - { 0xeb, 0x10 }, - { 0xec, 0xf6 }, - }; - - sd->stopped = 1; - - write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2)); -} - -/* set the mode */ -/* This function works for ov7660 only */ -static void ov519_set_mode(struct sd *sd) -{ - static const struct ov_regvals bridge_ov7660[2][10] = { - {{0x10, 0x14}, {0x11, 0x1e}, {0x12, 0x00}, {0x13, 0x00}, - {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c}, - {0x25, 0x01}, {0x26, 0x00}}, - {{0x10, 0x28}, {0x11, 0x3c}, {0x12, 0x00}, {0x13, 0x00}, - {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c}, - {0x25, 0x03}, {0x26, 0x00}} - }; - static const struct ov_i2c_regvals sensor_ov7660[2][3] = { - {{0x12, 0x00}, {0x24, 0x00}, {0x0c, 0x0c}}, - {{0x12, 0x00}, {0x04, 0x00}, {0x0c, 0x00}} - }; - static const struct ov_i2c_regvals sensor_ov7660_2[] = { - {OV7670_R17_HSTART, 0x13}, - {OV7670_R18_HSTOP, 0x01}, - {OV7670_R32_HREF, 0x92}, - {OV7670_R19_VSTART, 0x02}, - {OV7670_R1A_VSTOP, 0x7a}, - {OV7670_R03_VREF, 0x00}, -/* {0x33, 0x00}, */ -/* {0x34, 0x07}, */ -/* {0x36, 0x00}, */ -/* {0x6b, 0x0a}, */ - }; - - write_regvals(sd, bridge_ov7660[sd->gspca_dev.curr_mode], - ARRAY_SIZE(bridge_ov7660[0])); - write_i2c_regvals(sd, sensor_ov7660[sd->gspca_dev.curr_mode], - ARRAY_SIZE(sensor_ov7660[0])); - write_i2c_regvals(sd, sensor_ov7660_2, - ARRAY_SIZE(sensor_ov7660_2)); -} - -/* set the frame rate */ -/* This function works for sensors ov7640, ov7648 ov7660 and ov7670 only */ -static void ov519_set_fr(struct sd *sd) -{ - int fr; - u8 clock; - /* frame rate table with indices: - * - mode = 0: 320x240, 1: 640x480 - * - fr rate = 0: 30, 1: 25, 2: 20, 3: 15, 4: 10, 5: 5 - * - reg = 0: bridge a4, 1: bridge 23, 2: sensor 11 (clock) - */ - static const u8 fr_tb[2][6][3] = { - {{0x04, 0xff, 0x00}, - {0x04, 0x1f, 0x00}, - {0x04, 0x1b, 0x00}, - {0x04, 0x15, 0x00}, - {0x04, 0x09, 0x00}, - {0x04, 0x01, 0x00}}, - {{0x0c, 0xff, 0x00}, - {0x0c, 0x1f, 0x00}, - {0x0c, 0x1b, 0x00}, - {0x04, 0xff, 0x01}, - {0x04, 0x1f, 0x01}, - {0x04, 0x1b, 0x01}}, - }; - - if (frame_rate > 0) - sd->frame_rate = frame_rate; - if (sd->frame_rate >= 30) - fr = 0; - else if (sd->frame_rate >= 25) - fr = 1; - else if (sd->frame_rate >= 20) - fr = 2; - else if (sd->frame_rate >= 15) - fr = 3; - else if (sd->frame_rate >= 10) - fr = 4; - else - fr = 5; - reg_w(sd, 0xa4, fr_tb[sd->gspca_dev.curr_mode][fr][0]); - reg_w(sd, 0x23, fr_tb[sd->gspca_dev.curr_mode][fr][1]); - clock = fr_tb[sd->gspca_dev.curr_mode][fr][2]; - if (sd->sensor == SEN_OV7660) - clock |= 0x80; /* enable double clock */ - ov518_i2c_w(sd, OV7670_R11_CLKRC, clock); -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - - sd->bridge = id->driver_info & BRIDGE_MASK; - sd->invert_led = (id->driver_info & BRIDGE_INVERT_LED) != 0; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - cam->cam_mode = ov511_vga_mode; - cam->nmodes = ARRAY_SIZE(ov511_vga_mode); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - cam->cam_mode = ov518_vga_mode; - cam->nmodes = ARRAY_SIZE(ov518_vga_mode); - break; - case BRIDGE_OV519: - cam->cam_mode = ov519_vga_mode; - cam->nmodes = ARRAY_SIZE(ov519_vga_mode); - break; - case BRIDGE_OVFX2: - cam->cam_mode = ov519_vga_mode; - cam->nmodes = ARRAY_SIZE(ov519_vga_mode); - cam->bulk_size = OVFX2_BULK_SIZE; - cam->bulk_nurbs = MAX_NURBS; - cam->bulk = 1; - break; - case BRIDGE_W9968CF: - cam->cam_mode = w9968cf_vga_mode; - cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode); - break; - } - - sd->frame_rate = 15; - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - ov511_configure(gspca_dev); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - ov518_configure(gspca_dev); - break; - case BRIDGE_OV519: - ov519_configure(sd); - break; - case BRIDGE_OVFX2: - ovfx2_configure(sd); - break; - case BRIDGE_W9968CF: - w9968cf_configure(sd); - break; - } - - /* The OV519 must be more aggressive about sensor detection since - * I2C write will never fail if the sensor is not present. We have - * to try to initialize the sensor to detect its presence */ - sd->sensor = -1; - - /* Test for 76xx */ - if (init_ov_sensor(sd, OV7xx0_SID) >= 0) { - ov7xx0_configure(sd); - - /* Test for 6xx0 */ - } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) { - ov6xx0_configure(sd); - - /* Test for 8xx0 */ - } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) { - ov8xx0_configure(sd); - - /* Test for 3xxx / 2xxx */ - } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) { - ov_hires_configure(sd); - } else { - pr_err("Can't determine sensor slave IDs\n"); - goto error; - } - - if (sd->sensor < 0) - goto error; - - ov51x_led_control(sd, 0); /* turn LED off */ - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - if (sd->sif) { - cam->cam_mode = ov511_sif_mode; - cam->nmodes = ARRAY_SIZE(ov511_sif_mode); - } - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - if (sd->sif) { - cam->cam_mode = ov518_sif_mode; - cam->nmodes = ARRAY_SIZE(ov518_sif_mode); - } - break; - case BRIDGE_OV519: - if (sd->sif) { - cam->cam_mode = ov519_sif_mode; - cam->nmodes = ARRAY_SIZE(ov519_sif_mode); - } - break; - case BRIDGE_OVFX2: - switch (sd->sensor) { - case SEN_OV2610: - case SEN_OV2610AE: - cam->cam_mode = ovfx2_ov2610_mode; - cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode); - break; - case SEN_OV3610: - cam->cam_mode = ovfx2_ov3610_mode; - cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode); - break; - case SEN_OV9600: - cam->cam_mode = ovfx2_ov9600_mode; - cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode); - break; - default: - if (sd->sif) { - cam->cam_mode = ov519_sif_mode; - cam->nmodes = ARRAY_SIZE(ov519_sif_mode); - } - break; - } - break; - case BRIDGE_W9968CF: - if (sd->sif) - cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode) - 1; - - /* w9968cf needs initialisation once the sensor is known */ - w9968cf_init(sd); - break; - } - - /* initialize the sensor */ - switch (sd->sensor) { - case SEN_OV2610: - write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610)); - - /* Enable autogain, autoexpo, awb, bandfilter */ - i2c_w_mask(sd, 0x13, 0x27, 0x27); - break; - case SEN_OV2610AE: - write_i2c_regvals(sd, norm_2610ae, ARRAY_SIZE(norm_2610ae)); - - /* enable autoexpo */ - i2c_w_mask(sd, 0x13, 0x05, 0x05); - break; - case SEN_OV3610: - write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b)); - - /* Enable autogain, autoexpo, awb, bandfilter */ - i2c_w_mask(sd, 0x13, 0x27, 0x27); - break; - case SEN_OV6620: - write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20)); - break; - case SEN_OV6630: - case SEN_OV66308AF: - write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30)); - break; - default: -/* case SEN_OV7610: */ -/* case SEN_OV76BE: */ - write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610)); - i2c_w_mask(sd, 0x0e, 0x00, 0x40); - break; - case SEN_OV7620: - case SEN_OV7620AE: - write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620)); - break; - case SEN_OV7640: - case SEN_OV7648: - write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640)); - break; - case SEN_OV7660: - i2c_w(sd, OV7670_R12_COM7, OV7670_COM7_RESET); - msleep(14); - reg_w(sd, OV519_R57_SNAPSHOT, 0x23); - write_regvals(sd, init_519_ov7660, - ARRAY_SIZE(init_519_ov7660)); - write_i2c_regvals(sd, norm_7660, ARRAY_SIZE(norm_7660)); - sd->gspca_dev.curr_mode = 1; /* 640x480 */ - ov519_set_mode(sd); - ov519_set_fr(sd); - sd_reset_snapshot(gspca_dev); - ov51x_restart(sd); - ov51x_stop(sd); /* not in win traces */ - ov51x_led_control(sd, 0); - break; - case SEN_OV7670: - write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670)); - break; - case SEN_OV8610: - write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610)); - break; - case SEN_OV9600: - write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600)); - - /* enable autoexpo */ -/* i2c_w_mask(sd, 0x13, 0x05, 0x05); */ - break; - } - return gspca_dev->usb_err; -error: - PDEBUG(D_ERR, "OV519 Config failed"); - return -EINVAL; -} - -/* function called at start time before URB creation */ -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->bridge) { - case BRIDGE_OVFX2: - if (gspca_dev->width != 800) - gspca_dev->cam.bulk_size = OVFX2_BULK_SIZE; - else - gspca_dev->cam.bulk_size = 7 * 4096; - break; - } - return 0; -} - -/* Set up the OV511/OV511+ with the given image parameters. - * - * Do not put any sensor-specific code in here (including I2C I/O functions) - */ -static void ov511_mode_init_regs(struct sd *sd) -{ - int hsegs, vsegs, packet_size, fps, needed; - int interlaced = 0; - struct usb_host_interface *alt; - struct usb_interface *intf; - - intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); - alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); - if (!alt) { - pr_err("Couldn't get altsetting\n"); - sd->gspca_dev.usb_err = -EIO; - return; - } - - packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5); - - reg_w(sd, R511_CAM_UV_EN, 0x01); - reg_w(sd, R511_SNAP_UV_EN, 0x01); - reg_w(sd, R511_SNAP_OPTS, 0x03); - - /* Here I'm assuming that snapshot size == image size. - * I hope that's always true. --claudio - */ - hsegs = (sd->gspca_dev.width >> 3) - 1; - vsegs = (sd->gspca_dev.height >> 3) - 1; - - reg_w(sd, R511_CAM_PXCNT, hsegs); - reg_w(sd, R511_CAM_LNCNT, vsegs); - reg_w(sd, R511_CAM_PXDIV, 0x00); - reg_w(sd, R511_CAM_LNDIV, 0x00); - - /* YUV420, low pass filter on */ - reg_w(sd, R511_CAM_OPTS, 0x03); - - /* Snapshot additions */ - reg_w(sd, R511_SNAP_PXCNT, hsegs); - reg_w(sd, R511_SNAP_LNCNT, vsegs); - reg_w(sd, R511_SNAP_PXDIV, 0x00); - reg_w(sd, R511_SNAP_LNDIV, 0x00); - - /******** Set the framerate ********/ - if (frame_rate > 0) - sd->frame_rate = frame_rate; - - switch (sd->sensor) { - case SEN_OV6620: - /* No framerate control, doesn't like higher rates yet */ - sd->clockdiv = 3; - break; - - /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed - for more sensors we need to do this for them too */ - case SEN_OV7620: - case SEN_OV7620AE: - case SEN_OV7640: - case SEN_OV7648: - case SEN_OV76BE: - if (sd->gspca_dev.width == 320) - interlaced = 1; - /* Fall through */ - case SEN_OV6630: - case SEN_OV7610: - case SEN_OV7670: - switch (sd->frame_rate) { - case 30: - case 25: - /* Not enough bandwidth to do 640x480 @ 30 fps */ - if (sd->gspca_dev.width != 640) { - sd->clockdiv = 0; - break; - } - /* Fall through for 640x480 case */ - default: -/* case 20: */ -/* case 15: */ - sd->clockdiv = 1; - break; - case 10: - sd->clockdiv = 2; - break; - case 5: - sd->clockdiv = 5; - break; - } - if (interlaced) { - sd->clockdiv = (sd->clockdiv + 1) * 2 - 1; - /* Higher then 10 does not work */ - if (sd->clockdiv > 10) - sd->clockdiv = 10; - } - break; - - case SEN_OV8610: - /* No framerate control ?? */ - sd->clockdiv = 0; - break; - } - - /* Check if we have enough bandwidth to disable compression */ - fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1; - needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2; - /* 1000 isoc packets/sec */ - if (needed > 1000 * packet_size) { - /* Enable Y and UV quantization and compression */ - reg_w(sd, R511_COMP_EN, 0x07); - reg_w(sd, R511_COMP_LUT_EN, 0x03); - } else { - reg_w(sd, R511_COMP_EN, 0x06); - reg_w(sd, R511_COMP_LUT_EN, 0x00); - } - - reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE); - reg_w(sd, R51x_SYS_RESET, 0); -} - -/* Sets up the OV518/OV518+ with the given image parameters - * - * OV518 needs a completely different approach, until we can figure out what - * the individual registers do. Also, only 15 FPS is supported now. - * - * Do not put any sensor-specific code in here (including I2C I/O functions) - */ -static void ov518_mode_init_regs(struct sd *sd) -{ - int hsegs, vsegs, packet_size; - struct usb_host_interface *alt; - struct usb_interface *intf; - - intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); - alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); - if (!alt) { - pr_err("Couldn't get altsetting\n"); - sd->gspca_dev.usb_err = -EIO; - return; - } - - packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2); - - /******** Set the mode ********/ - reg_w(sd, 0x2b, 0); - reg_w(sd, 0x2c, 0); - reg_w(sd, 0x2d, 0); - reg_w(sd, 0x2e, 0); - reg_w(sd, 0x3b, 0); - reg_w(sd, 0x3c, 0); - reg_w(sd, 0x3d, 0); - reg_w(sd, 0x3e, 0); - - if (sd->bridge == BRIDGE_OV518) { - /* Set 8-bit (YVYU) input format */ - reg_w_mask(sd, 0x20, 0x08, 0x08); - - /* Set 12-bit (4:2:0) output format */ - reg_w_mask(sd, 0x28, 0x80, 0xf0); - reg_w_mask(sd, 0x38, 0x80, 0xf0); - } else { - reg_w(sd, 0x28, 0x80); - reg_w(sd, 0x38, 0x80); - } - - hsegs = sd->gspca_dev.width / 16; - vsegs = sd->gspca_dev.height / 4; - - reg_w(sd, 0x29, hsegs); - reg_w(sd, 0x2a, vsegs); - - reg_w(sd, 0x39, hsegs); - reg_w(sd, 0x3a, vsegs); - - /* Windows driver does this here; who knows why */ - reg_w(sd, 0x2f, 0x80); - - /******** Set the framerate ********/ - sd->clockdiv = 1; - - /* Mode independent, but framerate dependent, regs */ - /* 0x51: Clock divider; Only works on some cams which use 2 crystals */ - reg_w(sd, 0x51, 0x04); - reg_w(sd, 0x22, 0x18); - reg_w(sd, 0x23, 0xff); - - if (sd->bridge == BRIDGE_OV518PLUS) { - switch (sd->sensor) { - case SEN_OV7620AE: - if (sd->gspca_dev.width == 320) { - reg_w(sd, 0x20, 0x00); - reg_w(sd, 0x21, 0x19); - } else { - reg_w(sd, 0x20, 0x60); - reg_w(sd, 0x21, 0x1f); - } - break; - case SEN_OV7620: - reg_w(sd, 0x20, 0x00); - reg_w(sd, 0x21, 0x19); - break; - default: - reg_w(sd, 0x21, 0x19); - } - } else - reg_w(sd, 0x71, 0x17); /* Compression-related? */ - - /* FIXME: Sensor-specific */ - /* Bit 5 is what matters here. Of course, it is "reserved" */ - i2c_w(sd, 0x54, 0x23); - - reg_w(sd, 0x2f, 0x80); - - if (sd->bridge == BRIDGE_OV518PLUS) { - reg_w(sd, 0x24, 0x94); - reg_w(sd, 0x25, 0x90); - ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */ - ov518_reg_w32(sd, 0xc6, 540, 2); /* 21ch */ - ov518_reg_w32(sd, 0xc7, 540, 2); /* 21ch */ - ov518_reg_w32(sd, 0xc8, 108, 2); /* 6ch */ - ov518_reg_w32(sd, 0xca, 131098, 3); /* 2001ah */ - ov518_reg_w32(sd, 0xcb, 532, 2); /* 214h */ - ov518_reg_w32(sd, 0xcc, 2400, 2); /* 960h */ - ov518_reg_w32(sd, 0xcd, 32, 2); /* 20h */ - ov518_reg_w32(sd, 0xce, 608, 2); /* 260h */ - } else { - reg_w(sd, 0x24, 0x9f); - reg_w(sd, 0x25, 0x90); - ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */ - ov518_reg_w32(sd, 0xc6, 381, 2); /* 17dh */ - ov518_reg_w32(sd, 0xc7, 381, 2); /* 17dh */ - ov518_reg_w32(sd, 0xc8, 128, 2); /* 80h */ - ov518_reg_w32(sd, 0xca, 183331, 3); /* 2cc23h */ - ov518_reg_w32(sd, 0xcb, 746, 2); /* 2eah */ - ov518_reg_w32(sd, 0xcc, 1750, 2); /* 6d6h */ - ov518_reg_w32(sd, 0xcd, 45, 2); /* 2dh */ - ov518_reg_w32(sd, 0xce, 851, 2); /* 353h */ - } - - reg_w(sd, 0x2f, 0x80); -} - -/* Sets up the OV519 with the given image parameters - * - * OV519 needs a completely different approach, until we can figure out what - * the individual registers do. - * - * Do not put any sensor-specific code in here (including I2C I/O functions) - */ -static void ov519_mode_init_regs(struct sd *sd) -{ - static const struct ov_regvals mode_init_519_ov7670[] = { - { 0x5d, 0x03 }, /* Turn off suspend mode */ - { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ - { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */ - { 0xa2, 0x20 }, /* a2-a5 are undocumented */ - { 0xa3, 0x18 }, - { 0xa4, 0x04 }, - { 0xa5, 0x28 }, - { 0x37, 0x00 }, /* SetUsbInit */ - { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ - /* Enable both fields, YUV Input, disable defect comp (why?) */ - { 0x20, 0x0c }, - { 0x21, 0x38 }, - { 0x22, 0x1d }, - { 0x17, 0x50 }, /* undocumented */ - { 0x37, 0x00 }, /* undocumented */ - { 0x40, 0xff }, /* I2C timeout counter */ - { 0x46, 0x00 }, /* I2C clock prescaler */ - { 0x59, 0x04 }, /* new from windrv 090403 */ - { 0xff, 0x00 }, /* undocumented */ - /* windows reads 0x55 at this point, why? */ - }; - - static const struct ov_regvals mode_init_519[] = { - { 0x5d, 0x03 }, /* Turn off suspend mode */ - { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ - { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */ - { 0xa2, 0x20 }, /* a2-a5 are undocumented */ - { 0xa3, 0x18 }, - { 0xa4, 0x04 }, - { 0xa5, 0x28 }, - { 0x37, 0x00 }, /* SetUsbInit */ - { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ - /* Enable both fields, YUV Input, disable defect comp (why?) */ - { 0x22, 0x1d }, - { 0x17, 0x50 }, /* undocumented */ - { 0x37, 0x00 }, /* undocumented */ - { 0x40, 0xff }, /* I2C timeout counter */ - { 0x46, 0x00 }, /* I2C clock prescaler */ - { 0x59, 0x04 }, /* new from windrv 090403 */ - { 0xff, 0x00 }, /* undocumented */ - /* windows reads 0x55 at this point, why? */ - }; - - /******** Set the mode ********/ - switch (sd->sensor) { - default: - write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519)); - if (sd->sensor == SEN_OV7640 || - sd->sensor == SEN_OV7648) { - /* Select 8-bit input mode */ - reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10); - } - break; - case SEN_OV7660: - return; /* done by ov519_set_mode/fr() */ - case SEN_OV7670: - write_regvals(sd, mode_init_519_ov7670, - ARRAY_SIZE(mode_init_519_ov7670)); - break; - } - - reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4); - reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3); - if (sd->sensor == SEN_OV7670 && - sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) - reg_w(sd, OV519_R12_X_OFFSETL, 0x04); - else if (sd->sensor == SEN_OV7648 && - sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) - reg_w(sd, OV519_R12_X_OFFSETL, 0x01); - else - reg_w(sd, OV519_R12_X_OFFSETL, 0x00); - reg_w(sd, OV519_R13_X_OFFSETH, 0x00); - reg_w(sd, OV519_R14_Y_OFFSETL, 0x00); - reg_w(sd, OV519_R15_Y_OFFSETH, 0x00); - reg_w(sd, OV519_R16_DIVIDER, 0x00); - reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */ - reg_w(sd, 0x26, 0x00); /* Undocumented */ - - /******** Set the framerate ********/ - if (frame_rate > 0) - sd->frame_rate = frame_rate; - -/* FIXME: These are only valid at the max resolution. */ - sd->clockdiv = 0; - switch (sd->sensor) { - case SEN_OV7640: - case SEN_OV7648: - switch (sd->frame_rate) { - default: -/* case 30: */ - reg_w(sd, 0xa4, 0x0c); - reg_w(sd, 0x23, 0xff); - break; - case 25: - reg_w(sd, 0xa4, 0x0c); - reg_w(sd, 0x23, 0x1f); - break; - case 20: - reg_w(sd, 0xa4, 0x0c); - reg_w(sd, 0x23, 0x1b); - break; - case 15: - reg_w(sd, 0xa4, 0x04); - reg_w(sd, 0x23, 0xff); - sd->clockdiv = 1; - break; - case 10: - reg_w(sd, 0xa4, 0x04); - reg_w(sd, 0x23, 0x1f); - sd->clockdiv = 1; - break; - case 5: - reg_w(sd, 0xa4, 0x04); - reg_w(sd, 0x23, 0x1b); - sd->clockdiv = 1; - break; - } - break; - case SEN_OV8610: - switch (sd->frame_rate) { - default: /* 15 fps */ -/* case 15: */ - reg_w(sd, 0xa4, 0x06); - reg_w(sd, 0x23, 0xff); - break; - case 10: - reg_w(sd, 0xa4, 0x06); - reg_w(sd, 0x23, 0x1f); - break; - case 5: - reg_w(sd, 0xa4, 0x06); - reg_w(sd, 0x23, 0x1b); - break; - } - break; - case SEN_OV7670: /* guesses, based on 7640 */ - PDEBUG(D_STREAM, "Setting framerate to %d fps", - (sd->frame_rate == 0) ? 15 : sd->frame_rate); - reg_w(sd, 0xa4, 0x10); - switch (sd->frame_rate) { - case 30: - reg_w(sd, 0x23, 0xff); - break; - case 20: - reg_w(sd, 0x23, 0x1b); - break; - default: -/* case 15: */ - reg_w(sd, 0x23, 0xff); - sd->clockdiv = 1; - break; - } - break; - } -} - -static void mode_init_ov_sensor_regs(struct sd *sd) -{ - struct gspca_dev *gspca_dev; - int qvga, xstart, xend, ystart, yend; - u8 v; - - gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1; - - /******** Mode (VGA/QVGA) and sensor specific regs ********/ - switch (sd->sensor) { - case SEN_OV2610: - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); - i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); - i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); - i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); - i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); - i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); - return; - case SEN_OV2610AE: { - u8 v; - - /* frame rates: - * 10fps / 5 fps for 1600x1200 - * 40fps / 20fps for 800x600 - */ - v = 80; - if (qvga) { - if (sd->frame_rate < 25) - v = 0x81; - } else { - if (sd->frame_rate < 10) - v = 0x81; - } - i2c_w(sd, 0x11, v); - i2c_w(sd, 0x12, qvga ? 0x60 : 0x20); - return; - } - case SEN_OV3610: - if (qvga) { - xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4); - ystart = (776 - gspca_dev->height) / 2; - } else { - xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4); - ystart = (1544 - gspca_dev->height) / 2; - } - xend = xstart + gspca_dev->width; - yend = ystart + gspca_dev->height; - /* Writing to the COMH register resets the other windowing regs - to their default values, so we must do this first. */ - i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0); - i2c_w_mask(sd, 0x32, - (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7), - 0x3f); - i2c_w_mask(sd, 0x03, - (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3), - 0x0f); - i2c_w(sd, 0x17, xstart >> 4); - i2c_w(sd, 0x18, xend >> 4); - i2c_w(sd, 0x19, ystart >> 3); - i2c_w(sd, 0x1a, yend >> 3); - return; - case SEN_OV8610: - /* For OV8610 qvga means qsvga */ - i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); - i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ - i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ - i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */ - i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */ - break; - case SEN_OV7610: - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); - i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ - i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ - break; - case SEN_OV7620: - case SEN_OV7620AE: - case SEN_OV76BE: - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); - i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); - i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); - i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); - i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); - i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ - i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ - if (sd->sensor == SEN_OV76BE) - i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); - break; - case SEN_OV7640: - case SEN_OV7648: - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); - /* Setting this undocumented bit in qvga mode removes a very - annoying vertical shaking of the image */ - i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); - /* Unknown */ - i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); - /* Allow higher automatic gain (to allow higher framerates) */ - i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */ - break; - case SEN_OV7670: - /* set COM7_FMT_VGA or COM7_FMT_QVGA - * do we need to set anything else? - * HSTART etc are set in set_ov_sensor_window itself */ - i2c_w_mask(sd, OV7670_R12_COM7, - qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, - OV7670_COM7_FMT_MASK); - i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ - i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_AWB, - OV7670_COM8_AWB); - if (qvga) { /* QVGA from ov7670.c by - * Jonathan Corbet */ - xstart = 164; - xend = 28; - ystart = 14; - yend = 494; - } else { /* VGA */ - xstart = 158; - xend = 14; - ystart = 10; - yend = 490; - } - /* OV7670 hardware window registers are split across - * multiple locations */ - i2c_w(sd, OV7670_R17_HSTART, xstart >> 3); - i2c_w(sd, OV7670_R18_HSTOP, xend >> 3); - v = i2c_r(sd, OV7670_R32_HREF); - v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_R32_HREF, v); - - i2c_w(sd, OV7670_R19_VSTART, ystart >> 2); - i2c_w(sd, OV7670_R1A_VSTOP, yend >> 2); - v = i2c_r(sd, OV7670_R03_VREF); - v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_R03_VREF, v); - break; - case SEN_OV6620: - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ - i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ - break; - case SEN_OV6630: - case SEN_OV66308AF: - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ - break; - case SEN_OV9600: { - const struct ov_i2c_regvals *vals; - static const struct ov_i2c_regvals sxga_15[] = { - {0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75} - }; - static const struct ov_i2c_regvals sxga_7_5[] = { - {0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75} - }; - static const struct ov_i2c_regvals vga_30[] = { - {0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60} - }; - static const struct ov_i2c_regvals vga_15[] = { - {0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70} - }; - - /* frame rates: - * 15fps / 7.5 fps for 1280x1024 - * 30fps / 15fps for 640x480 - */ - i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40); - if (qvga) - vals = sd->frame_rate < 30 ? vga_15 : vga_30; - else - vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15; - write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15)); - return; - } - default: - return; - } - - /******** Clock programming ********/ - i2c_w(sd, 0x11, sd->clockdiv); -} - -/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */ -static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->gspca_dev.streaming) - reg_w(sd, OV519_R51_RESET1, 0x0f); /* block stream */ - i2c_w_mask(sd, OV7670_R1E_MVFP, - OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip, - OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP); - if (sd->gspca_dev.streaming) - reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */ -} - -static void set_ov_sensor_window(struct sd *sd) -{ - struct gspca_dev *gspca_dev; - int qvga, crop; - int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; - - /* mode setup is fully handled in mode_init_ov_sensor_regs for these */ - switch (sd->sensor) { - case SEN_OV2610: - case SEN_OV2610AE: - case SEN_OV3610: - case SEN_OV7670: - case SEN_OV9600: - mode_init_ov_sensor_regs(sd); - return; - case SEN_OV7660: - ov519_set_mode(sd); - ov519_set_fr(sd); - return; - } - - gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1; - crop = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 2; - - /* The different sensor ICs handle setting up of window differently. - * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ - switch (sd->sensor) { - case SEN_OV8610: - hwsbase = 0x1e; - hwebase = 0x1e; - vwsbase = 0x02; - vwebase = 0x02; - break; - case SEN_OV7610: - case SEN_OV76BE: - hwsbase = 0x38; - hwebase = 0x3a; - vwsbase = vwebase = 0x05; - break; - case SEN_OV6620: - case SEN_OV6630: - case SEN_OV66308AF: - hwsbase = 0x38; - hwebase = 0x3a; - vwsbase = 0x05; - vwebase = 0x06; - if (sd->sensor == SEN_OV66308AF && qvga) - /* HDG: this fixes U and V getting swapped */ - hwsbase++; - if (crop) { - hwsbase += 8; - hwebase += 8; - vwsbase += 11; - vwebase += 11; - } - break; - case SEN_OV7620: - case SEN_OV7620AE: - hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ - hwebase = 0x2f; - vwsbase = vwebase = 0x05; - break; - case SEN_OV7640: - case SEN_OV7648: - hwsbase = 0x1a; - hwebase = 0x1a; - vwsbase = vwebase = 0x03; - break; - default: - return; - } - - switch (sd->sensor) { - case SEN_OV6620: - case SEN_OV6630: - case SEN_OV66308AF: - if (qvga) { /* QCIF */ - hwscale = 0; - vwscale = 0; - } else { /* CIF */ - hwscale = 1; - vwscale = 1; /* The datasheet says 0; - * it's wrong */ - } - break; - case SEN_OV8610: - if (qvga) { /* QSVGA */ - hwscale = 1; - vwscale = 1; - } else { /* SVGA */ - hwscale = 2; - vwscale = 2; - } - break; - default: /* SEN_OV7xx0 */ - if (qvga) { /* QVGA */ - hwscale = 1; - vwscale = 0; - } else { /* VGA */ - hwscale = 2; - vwscale = 1; - } - } - - mode_init_ov_sensor_regs(sd); - - i2c_w(sd, 0x17, hwsbase); - i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale)); - i2c_w(sd, 0x19, vwsbase); - i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale)); -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* Default for most bridges, allow bridge_mode_init_regs to override */ - sd->sensor_width = sd->gspca_dev.width; - sd->sensor_height = sd->gspca_dev.height; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - ov511_mode_init_regs(sd); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - ov518_mode_init_regs(sd); - break; - case BRIDGE_OV519: - ov519_mode_init_regs(sd); - break; - /* case BRIDGE_OVFX2: nothing to do */ - case BRIDGE_W9968CF: - w9968cf_mode_init_regs(sd); - break; - } - - set_ov_sensor_window(sd); - - /* Force clear snapshot state in case the snapshot button was - pressed while we weren't streaming */ - sd->snapshot_needs_reset = 1; - sd_reset_snapshot(gspca_dev); - - sd->first_frame = 3; - - ov51x_restart(sd); - ov51x_led_control(sd, 1); - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - ov51x_stop(sd); - ov51x_led_control(sd, 0); -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!sd->gspca_dev.present) - return; - if (sd->bridge == BRIDGE_W9968CF) - w9968cf_stop0(sd); - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - /* If the last button state is pressed, release it now! */ - if (sd->snapshot_pressed) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - sd->snapshot_pressed = 0; - } -#endif - if (sd->bridge == BRIDGE_OV519) - reg_w(sd, OV519_R57_SNAPSHOT, 0x23); -} - -static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->snapshot_pressed != state) { -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - input_report_key(gspca_dev->input_dev, KEY_CAMERA, state); - input_sync(gspca_dev->input_dev); -#endif - if (state) - sd->snapshot_needs_reset = 1; - - sd->snapshot_pressed = state; - } else { - /* On the ov511 / ov519 we need to reset the button state - multiple times, as resetting does not work as long as the - button stays pressed */ - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - case BRIDGE_OV519: - if (state) - sd->snapshot_needs_reset = 1; - break; - } - } -} - -static void ov511_pkt_scan(struct gspca_dev *gspca_dev, - u8 *in, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th - * byte non-zero. The EOF packet has image width/height in the - * 10th and 11th bytes. The 9th byte is given as follows: - * - * bit 7: EOF - * 6: compression enabled - * 5: 422/420/400 modes - * 4: 422/420/400 modes - * 3: 1 - * 2: snapshot button on - * 1: snapshot frame - * 0: even/odd field - */ - if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) && - (in[8] & 0x08)) { - ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1); - if (in[8] & 0x80) { - /* Frame end */ - if ((in[9] + 1) * 8 != gspca_dev->width || - (in[10] + 1) * 8 != gspca_dev->height) { - PDEBUG(D_ERR, "Invalid frame size, got: %dx%d," - " requested: %dx%d\n", - (in[9] + 1) * 8, (in[10] + 1) * 8, - gspca_dev->width, gspca_dev->height); - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - /* Add 11 byte footer to frame, might be useful */ - gspca_frame_add(gspca_dev, LAST_PACKET, in, 11); - return; - } else { - /* Frame start */ - gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0); - sd->packet_nr = 0; - } - } - - /* Ignore the packet number */ - len--; - - /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, in, len); -} - -static void ov518_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* A false positive here is likely, until OVT gives me - * the definitive SOF/EOF format */ - if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) { - ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1); - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - sd->packet_nr = 0; - } - - if (gspca_dev->last_packet_type == DISCARD_PACKET) - return; - - /* Does this device use packet numbers ? */ - if (len & 7) { - len--; - if (sd->packet_nr == data[len]) - sd->packet_nr++; - /* The last few packets of the frame (which are all 0's - except that they may contain part of the footer), are - numbered 0 */ - else if (sd->packet_nr == 0 || data[len]) { - PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)", - (int)data[len], (int)sd->packet_nr); - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - } - - /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static void ov519_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - /* Header of ov519 is 16 bytes: - * Byte Value Description - * 0 0xff magic - * 1 0xff magic - * 2 0xff magic - * 3 0xXX 0x50 = SOF, 0x51 = EOF - * 9 0xXX 0x01 initial frame without data, - * 0x00 standard frame with image - * 14 Lo in EOF: length of image data / 8 - * 15 Hi - */ - - if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { - switch (data[3]) { - case 0x50: /* start of frame */ - /* Don't check the button state here, as the state - usually (always ?) changes at EOF and checking it - here leads to unnecessary snapshot state resets. */ -#define HDRSZ 16 - data += HDRSZ; - len -= HDRSZ; -#undef HDRSZ - if (data[0] == 0xff || data[1] == 0xd8) - gspca_frame_add(gspca_dev, FIRST_PACKET, - data, len); - else - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - case 0x51: /* end of frame */ - ov51x_handle_button(gspca_dev, data[11] & 1); - if (data[9] != 0) - gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - return; - } - } - - /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - - /* A short read signals EOF */ - if (len < gspca_dev->cam.bulk_size) { - /* If the frame is short, and it is one of the first ones - the sensor and bridge are still syncing, so drop it. */ - if (sd->first_frame) { - sd->first_frame--; - if (gspca_dev->image_len < - sd->gspca_dev.width * sd->gspca_dev.height) - gspca_dev->last_packet_type = DISCARD_PACKET; - } - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - } -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->bridge) { - case BRIDGE_OV511: - case BRIDGE_OV511PLUS: - ov511_pkt_scan(gspca_dev, data, len); - break; - case BRIDGE_OV518: - case BRIDGE_OV518PLUS: - ov518_pkt_scan(gspca_dev, data, len); - break; - case BRIDGE_OV519: - ov519_pkt_scan(gspca_dev, data, len); - break; - case BRIDGE_OVFX2: - ovfx2_pkt_scan(gspca_dev, data, len); - break; - case BRIDGE_W9968CF: - w9968cf_pkt_scan(gspca_dev, data, len); - break; - } -} - -/* -- management routines -- */ - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct ov_i2c_regvals brit_7660[][7] = { - {{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90}, - {0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}}, - {{0x0f, 0x6a}, {0x24, 0x50}, {0x25, 0x40}, {0x26, 0xa1}, - {0x27, 0xc0}, {0x28, 0xc0}, {0x2c, 0xc0}}, - {{0x0f, 0x6a}, {0x24, 0x68}, {0x25, 0x58}, {0x26, 0xc2}, - {0x27, 0xa0}, {0x28, 0xa0}, {0x2c, 0xa0}}, - {{0x0f, 0x6a}, {0x24, 0x70}, {0x25, 0x68}, {0x26, 0xd3}, - {0x27, 0x80}, {0x28, 0x80}, {0x2c, 0x80}}, - {{0x0f, 0x6a}, {0x24, 0x80}, {0x25, 0x70}, {0x26, 0xd3}, - {0x27, 0x20}, {0x28, 0x20}, {0x2c, 0x20}}, - {{0x0f, 0x6a}, {0x24, 0x88}, {0x25, 0x78}, {0x26, 0xd3}, - {0x27, 0x40}, {0x28, 0x40}, {0x2c, 0x40}}, - {{0x0f, 0x6a}, {0x24, 0x90}, {0x25, 0x80}, {0x26, 0xd4}, - {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}} - }; - - switch (sd->sensor) { - case SEN_OV8610: - case SEN_OV7610: - case SEN_OV76BE: - case SEN_OV6620: - case SEN_OV6630: - case SEN_OV66308AF: - case SEN_OV7640: - case SEN_OV7648: - i2c_w(sd, OV7610_REG_BRT, val); - break; - case SEN_OV7620: - case SEN_OV7620AE: - i2c_w(sd, OV7610_REG_BRT, val); - break; - case SEN_OV7660: - write_i2c_regvals(sd, brit_7660[val], - ARRAY_SIZE(brit_7660[0])); - break; - case SEN_OV7670: -/*win trace - * i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_AEC); */ - i2c_w(sd, OV7670_R55_BRIGHT, ov7670_abs_to_sm(val)); - break; - } -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct ov_i2c_regvals contrast_7660[][31] = { - {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0}, - {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30}, - {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x24}, {0x77, 0x24}, - {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x34}, - {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x65}, - {0x80, 0x70}, {0x81, 0x77}, {0x82, 0x7d}, {0x83, 0x83}, - {0x84, 0x88}, {0x85, 0x8d}, {0x86, 0x96}, {0x87, 0x9f}, - {0x88, 0xb0}, {0x89, 0xc4}, {0x8a, 0xd9}}, - {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0x94}, - {0x70, 0x58}, {0x71, 0x40}, {0x72, 0x30}, {0x73, 0x30}, - {0x74, 0x30}, {0x75, 0x30}, {0x76, 0x2c}, {0x77, 0x24}, - {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x31}, - {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x62}, - {0x80, 0x6d}, {0x81, 0x75}, {0x82, 0x7b}, {0x83, 0x81}, - {0x84, 0x87}, {0x85, 0x8d}, {0x86, 0x98}, {0x87, 0xa1}, - {0x88, 0xb2}, {0x89, 0xc6}, {0x8a, 0xdb}}, - {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x84}, - {0x70, 0x58}, {0x71, 0x48}, {0x72, 0x40}, {0x73, 0x40}, - {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x28}, {0x77, 0x24}, - {0x78, 0x26}, {0x79, 0x28}, {0x7a, 0x28}, {0x7b, 0x34}, - {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x5d}, - {0x80, 0x68}, {0x81, 0x71}, {0x82, 0x79}, {0x83, 0x81}, - {0x84, 0x86}, {0x85, 0x8b}, {0x86, 0x95}, {0x87, 0x9e}, - {0x88, 0xb1}, {0x89, 0xc5}, {0x8a, 0xd9}}, - {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x70}, - {0x70, 0x58}, {0x71, 0x58}, {0x72, 0x48}, {0x73, 0x48}, - {0x74, 0x38}, {0x75, 0x40}, {0x76, 0x34}, {0x77, 0x34}, - {0x78, 0x2e}, {0x79, 0x28}, {0x7a, 0x24}, {0x7b, 0x22}, - {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x58}, - {0x80, 0x63}, {0x81, 0x6e}, {0x82, 0x77}, {0x83, 0x80}, - {0x84, 0x87}, {0x85, 0x8f}, {0x86, 0x9c}, {0x87, 0xa9}, - {0x88, 0xc0}, {0x89, 0xd4}, {0x8a, 0xe6}}, - {{0x6c, 0xa0}, {0x6d, 0xf0}, {0x6e, 0x90}, {0x6f, 0x80}, - {0x70, 0x70}, {0x71, 0x80}, {0x72, 0x60}, {0x73, 0x60}, - {0x74, 0x58}, {0x75, 0x60}, {0x76, 0x4c}, {0x77, 0x38}, - {0x78, 0x38}, {0x79, 0x2a}, {0x7a, 0x20}, {0x7b, 0x0e}, - {0x7c, 0x0a}, {0x7d, 0x14}, {0x7e, 0x26}, {0x7f, 0x46}, - {0x80, 0x54}, {0x81, 0x64}, {0x82, 0x70}, {0x83, 0x7c}, - {0x84, 0x87}, {0x85, 0x93}, {0x86, 0xa6}, {0x87, 0xb4}, - {0x88, 0xd0}, {0x89, 0xe5}, {0x8a, 0xf5}}, - {{0x6c, 0x60}, {0x6d, 0x80}, {0x6e, 0x60}, {0x6f, 0x80}, - {0x70, 0x80}, {0x71, 0x80}, {0x72, 0x88}, {0x73, 0x30}, - {0x74, 0x70}, {0x75, 0x68}, {0x76, 0x64}, {0x77, 0x50}, - {0x78, 0x3c}, {0x79, 0x22}, {0x7a, 0x10}, {0x7b, 0x08}, - {0x7c, 0x06}, {0x7d, 0x0e}, {0x7e, 0x1a}, {0x7f, 0x3a}, - {0x80, 0x4a}, {0x81, 0x5a}, {0x82, 0x6b}, {0x83, 0x7b}, - {0x84, 0x89}, {0x85, 0x96}, {0x86, 0xaf}, {0x87, 0xc3}, - {0x88, 0xe1}, {0x89, 0xf2}, {0x8a, 0xfa}}, - {{0x6c, 0x20}, {0x6d, 0x40}, {0x6e, 0x20}, {0x6f, 0x60}, - {0x70, 0x88}, {0x71, 0xc8}, {0x72, 0xc0}, {0x73, 0xb8}, - {0x74, 0xa8}, {0x75, 0xb8}, {0x76, 0x80}, {0x77, 0x5c}, - {0x78, 0x26}, {0x79, 0x10}, {0x7a, 0x08}, {0x7b, 0x04}, - {0x7c, 0x02}, {0x7d, 0x06}, {0x7e, 0x0a}, {0x7f, 0x22}, - {0x80, 0x33}, {0x81, 0x4c}, {0x82, 0x64}, {0x83, 0x7b}, - {0x84, 0x90}, {0x85, 0xa7}, {0x86, 0xc7}, {0x87, 0xde}, - {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}}, - }; - - switch (sd->sensor) { - case SEN_OV7610: - case SEN_OV6620: - i2c_w(sd, OV7610_REG_CNT, val); - break; - case SEN_OV6630: - case SEN_OV66308AF: - i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); - break; - case SEN_OV8610: { - static const u8 ctab[] = { - 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f - }; - - /* Use Y gamma control instead. Bit 0 enables it. */ - i2c_w(sd, 0x64, ctab[val >> 5]); - break; - } - case SEN_OV7620: - case SEN_OV7620AE: { - static const u8 ctab[] = { - 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, - 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff - }; - - /* Use Y gamma control instead. Bit 0 enables it. */ - i2c_w(sd, 0x64, ctab[val >> 4]); - break; - } - case SEN_OV7660: - write_i2c_regvals(sd, contrast_7660[val], - ARRAY_SIZE(contrast_7660[0])); - break; - case SEN_OV7670: - /* check that this isn't just the same as ov7610 */ - i2c_w(sd, OV7670_R56_CONTRAS, val >> 1); - break; - } -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w(sd, 0x10, val); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct ov_i2c_regvals colors_7660[][6] = { - {{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a}, - {0x53, 0x19}, {0x54, 0x23}}, - {{0x4f, 0x47}, {0x50, 0x4a}, {0x51, 0x03}, {0x52, 0x11}, - {0x53, 0x2c}, {0x54, 0x3e}}, - {{0x4f, 0x66}, {0x50, 0x6b}, {0x51, 0x05}, {0x52, 0x19}, - {0x53, 0x40}, {0x54, 0x59}}, - {{0x4f, 0x84}, {0x50, 0x8b}, {0x51, 0x06}, {0x52, 0x20}, - {0x53, 0x53}, {0x54, 0x73}}, - {{0x4f, 0xa3}, {0x50, 0xab}, {0x51, 0x08}, {0x52, 0x28}, - {0x53, 0x66}, {0x54, 0x8e}}, - }; - - switch (sd->sensor) { - case SEN_OV8610: - case SEN_OV7610: - case SEN_OV76BE: - case SEN_OV6620: - case SEN_OV6630: - case SEN_OV66308AF: - i2c_w(sd, OV7610_REG_SAT, val); - break; - case SEN_OV7620: - case SEN_OV7620AE: - /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ -/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e); - if (rc < 0) - goto out; */ - i2c_w(sd, OV7610_REG_SAT, val); - break; - case SEN_OV7640: - case SEN_OV7648: - i2c_w(sd, OV7610_REG_SAT, val & 0xf0); - break; - case SEN_OV7660: - write_i2c_regvals(sd, colors_7660[val], - ARRAY_SIZE(colors_7660[0])); - break; - case SEN_OV7670: - /* supported later once I work out how to do it - * transparently fail now! */ - /* set REG_COM13 values for UV sat auto mode */ - break; - } -} - -static void setautobright(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10); -} - -static void setfreq_i(struct sd *sd, s32 val) -{ - if (sd->sensor == SEN_OV7660 - || sd->sensor == SEN_OV7670) { - switch (val) { - case 0: /* Banding filter disabled */ - i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT); - break; - case 1: /* 50 hz */ - i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT, - OV7670_COM8_BFILT); - i2c_w_mask(sd, OV7670_R3B_COM11, 0x08, 0x18); - break; - case 2: /* 60 hz */ - i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT, - OV7670_COM8_BFILT); - i2c_w_mask(sd, OV7670_R3B_COM11, 0x00, 0x18); - break; - case 3: /* Auto hz - ov7670 only */ - i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT, - OV7670_COM8_BFILT); - i2c_w_mask(sd, OV7670_R3B_COM11, OV7670_COM11_HZAUTO, - 0x18); - break; - } - } else { - switch (val) { - case 0: /* Banding filter disabled */ - i2c_w_mask(sd, 0x2d, 0x00, 0x04); - i2c_w_mask(sd, 0x2a, 0x00, 0x80); - break; - case 1: /* 50 hz (filter on and framerate adj) */ - i2c_w_mask(sd, 0x2d, 0x04, 0x04); - i2c_w_mask(sd, 0x2a, 0x80, 0x80); - /* 20 fps -> 16.667 fps */ - if (sd->sensor == SEN_OV6620 || - sd->sensor == SEN_OV6630 || - sd->sensor == SEN_OV66308AF) - i2c_w(sd, 0x2b, 0x5e); - else - i2c_w(sd, 0x2b, 0xac); - break; - case 2: /* 60 hz (filter on, ...) */ - i2c_w_mask(sd, 0x2d, 0x04, 0x04); - if (sd->sensor == SEN_OV6620 || - sd->sensor == SEN_OV6630 || - sd->sensor == SEN_OV66308AF) { - /* 20 fps -> 15 fps */ - i2c_w_mask(sd, 0x2a, 0x80, 0x80); - i2c_w(sd, 0x2b, 0xa8); - } else { - /* no framerate adj. */ - i2c_w_mask(sd, 0x2a, 0x00, 0x80); - } - break; - } - } -} - -static void setfreq(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - setfreq_i(sd, val); - - /* Ugly but necessary */ - if (sd->bridge == BRIDGE_W9968CF) - w9968cf_set_crop_window(sd); -} - -static int sd_get_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->bridge != BRIDGE_W9968CF) - return -ENOTTY; - - memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); - jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | - V4L2_JPEG_MARKER_DRI; - return 0; -} - -static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->bridge != BRIDGE_W9968CF) - return -ENOTTY; - - v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); - return 0; -} - -static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - gspca_dev->exposure->val = i2c_r(sd, 0x10); - break; - } - return 0; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setfreq(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOBRIGHTNESS: - if (ctrl->is_new) - setautobright(gspca_dev, ctrl->val); - if (!ctrl->val && sd->brightness->is_new) - setbrightness(gspca_dev, sd->brightness->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_HFLIP: - sethvflip(gspca_dev, ctrl->val, sd->vflip->val); - break; - case V4L2_CID_AUTOGAIN: - if (ctrl->is_new) - setautogain(gspca_dev, ctrl->val); - if (!ctrl->val && gspca_dev->exposure->is_new) - setexposure(gspca_dev, gspca_dev->exposure->val); - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - return -EBUSY; /* Should never happen, as we grab the ctrl */ - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .g_volatile_ctrl = sd_g_volatile_ctrl, - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 10); - if (valid_controls[sd->sensor].has_brightness) - sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, - sd->sensor == SEN_OV7660 ? 6 : 255, 1, - sd->sensor == SEN_OV7660 ? 3 : 127); - if (valid_controls[sd->sensor].has_contrast) { - if (sd->sensor == SEN_OV7660) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 6, 1, 3); - else - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, - (sd->sensor == SEN_OV6630 || - sd->sensor == SEN_OV66308AF) ? 200 : 127); - } - if (valid_controls[sd->sensor].has_sat) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, - sd->sensor == SEN_OV7660 ? 4 : 255, 1, - sd->sensor == SEN_OV7660 ? 2 : 127); - if (valid_controls[sd->sensor].has_exposure) - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 255, 1, 127); - if (valid_controls[sd->sensor].has_hvflip) { - sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - } - if (valid_controls[sd->sensor].has_autobright) - sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1); - if (valid_controls[sd->sensor].has_autogain) - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - if (valid_controls[sd->sensor].has_freq) { - if (sd->sensor == SEN_OV7670) - sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO); - else - sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); - } - if (sd->bridge == BRIDGE_W9968CF) - sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - if (gspca_dev->autogain) - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true); - if (sd->autobright) - v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false); - if (sd->hflip) - v4l2_ctrl_cluster(2, &sd->hflip); - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_reset_snapshot, - .get_jcomp = sd_get_jcomp, - .set_jcomp = sd_set_jcomp, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .other_input = 1, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF }, - {USB_DEVICE(0x041e, 0x4052), - .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, - {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x045e, 0x028c), - .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, - {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 }, - {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, - {USB_DEVICE(0x05a9, 0x0519), - .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, - {USB_DEVICE(0x05a9, 0x0530), - .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, - {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 }, - {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS }, - {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, - {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, - {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 }, - {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 }, - {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF }, - {USB_DEVICE(0x8020, 0xef04), .driver_info = BRIDGE_OVFX2 }, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); - -module_param(frame_rate, int, 0644); -MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)"); diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c deleted file mode 100644 index bb09d7884b89..000000000000 --- a/drivers/media/video/gspca/ov534.c +++ /dev/null @@ -1,1544 +0,0 @@ -/* - * ov534-ov7xxx gspca driver - * - * Copyright (C) 2008 Antonio Ospite - * Copyright (C) 2008 Jim Paris - * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr - * - * Based on a prototype written by Mark Ferrell - * USB protocol reverse engineered by Jim Paris - * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ - * - * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr - * PS3 Eye camera - brightness, contrast, awb, agc, aec controls - * added by Max Thrun - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "ov534" - -#include "gspca.h" - -#include -#include - -#define OV534_REG_ADDRESS 0xf1 /* sensor address */ -#define OV534_REG_SUBADDR 0xf2 -#define OV534_REG_WRITE 0xf3 -#define OV534_REG_READ 0xf4 -#define OV534_REG_OPERATION 0xf5 -#define OV534_REG_STATUS 0xf6 - -#define OV534_OP_WRITE_3 0x37 -#define OV534_OP_WRITE_2 0x33 -#define OV534_OP_READ_2 0xf9 - -#define CTRL_TIMEOUT 500 - -MODULE_AUTHOR("Antonio Ospite "); -MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_ctrl *hue; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - struct { /* gain control cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - }; - struct v4l2_ctrl *autowhitebalance; - struct { /* exposure control cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_ctrl *sharpness; - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - struct v4l2_ctrl *plfreq; - - __u32 last_pts; - u16 last_fid; - u8 frame_rate; - - u8 sensor; -}; -enum sensors { - SENSOR_OV767x, - SENSOR_OV772x, - NSENSORS -}; - -static int sd_start(struct gspca_dev *gspca_dev); -static void sd_stopN(struct gspca_dev *gspca_dev); - - -static const struct v4l2_pix_format ov772x_mode[] = { - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format ov767x_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, -}; - -static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30}; -static const u8 vga_rates[] = {60, 50, 40, 30, 15}; - -static const struct framerates ov772x_framerates[] = { - { /* 320x240 */ - .rates = qvga_rates, - .nrates = ARRAY_SIZE(qvga_rates), - }, - { /* 640x480 */ - .rates = vga_rates, - .nrates = ARRAY_SIZE(vga_rates), - }, -}; - -struct reg_array { - const u8 (*val)[2]; - int len; -}; - -static const u8 bridge_init_767x[][2] = { -/* comments from the ms-win file apollo7670.set */ -/* str1 */ - {0xf1, 0x42}, - {0x88, 0xf8}, - {0x89, 0xff}, - {0x76, 0x03}, - {0x92, 0x03}, - {0x95, 0x10}, - {0xe2, 0x00}, - {0xe7, 0x3e}, - {0x8d, 0x1c}, - {0x8e, 0x00}, - {0x8f, 0x00}, - {0x1f, 0x00}, - {0xc3, 0xf9}, - {0x89, 0xff}, - {0x88, 0xf8}, - {0x76, 0x03}, - {0x92, 0x01}, - {0x93, 0x18}, - {0x1c, 0x00}, - {0x1d, 0x48}, - {0x1d, 0x00}, - {0x1d, 0xff}, - {0x1d, 0x02}, - {0x1d, 0x58}, - {0x1d, 0x00}, - {0x1c, 0x0a}, - {0x1d, 0x0a}, - {0x1d, 0x0e}, - {0xc0, 0x50}, /* HSize 640 */ - {0xc1, 0x3c}, /* VSize 480 */ - {0x34, 0x05}, /* enable Audio Suspend mode */ - {0xc2, 0x0c}, /* Input YUV */ - {0xc3, 0xf9}, /* enable PRE */ - {0x34, 0x05}, /* enable Audio Suspend mode */ - {0xe7, 0x2e}, /* this solves failure of "SuspendResumeTest" */ - {0x31, 0xf9}, /* enable 1.8V Suspend */ - {0x35, 0x02}, /* turn on JPEG */ - {0xd9, 0x10}, - {0x25, 0x42}, /* GPIO[8]:Input */ - {0x94, 0x11}, /* If the default setting is loaded when - * system boots up, this flag is closed here */ -}; -static const u8 sensor_init_767x[][2] = { - {0x12, 0x80}, - {0x11, 0x03}, - {0x3a, 0x04}, - {0x12, 0x00}, - {0x17, 0x13}, - {0x18, 0x01}, - {0x32, 0xb6}, - {0x19, 0x02}, - {0x1a, 0x7a}, - {0x03, 0x0a}, - {0x0c, 0x00}, - {0x3e, 0x00}, - {0x70, 0x3a}, - {0x71, 0x35}, - {0x72, 0x11}, - {0x73, 0xf0}, - {0xa2, 0x02}, - {0x7a, 0x2a}, /* set Gamma=1.6 below */ - {0x7b, 0x12}, - {0x7c, 0x1d}, - {0x7d, 0x2d}, - {0x7e, 0x45}, - {0x7f, 0x50}, - {0x80, 0x59}, - {0x81, 0x62}, - {0x82, 0x6b}, - {0x83, 0x73}, - {0x84, 0x7b}, - {0x85, 0x8a}, - {0x86, 0x98}, - {0x87, 0xb2}, - {0x88, 0xca}, - {0x89, 0xe0}, - {0x13, 0xe0}, - {0x00, 0x00}, - {0x10, 0x00}, - {0x0d, 0x40}, - {0x14, 0x38}, /* gain max 16x */ - {0xa5, 0x05}, - {0xab, 0x07}, - {0x24, 0x95}, - {0x25, 0x33}, - {0x26, 0xe3}, - {0x9f, 0x78}, - {0xa0, 0x68}, - {0xa1, 0x03}, - {0xa6, 0xd8}, - {0xa7, 0xd8}, - {0xa8, 0xf0}, - {0xa9, 0x90}, - {0xaa, 0x94}, - {0x13, 0xe5}, - {0x0e, 0x61}, - {0x0f, 0x4b}, - {0x16, 0x02}, - {0x21, 0x02}, - {0x22, 0x91}, - {0x29, 0x07}, - {0x33, 0x0b}, - {0x35, 0x0b}, - {0x37, 0x1d}, - {0x38, 0x71}, - {0x39, 0x2a}, - {0x3c, 0x78}, - {0x4d, 0x40}, - {0x4e, 0x20}, - {0x69, 0x00}, - {0x6b, 0x4a}, - {0x74, 0x10}, - {0x8d, 0x4f}, - {0x8e, 0x00}, - {0x8f, 0x00}, - {0x90, 0x00}, - {0x91, 0x00}, - {0x96, 0x00}, - {0x9a, 0x80}, - {0xb0, 0x84}, - {0xb1, 0x0c}, - {0xb2, 0x0e}, - {0xb3, 0x82}, - {0xb8, 0x0a}, - {0x43, 0x0a}, - {0x44, 0xf0}, - {0x45, 0x34}, - {0x46, 0x58}, - {0x47, 0x28}, - {0x48, 0x3a}, - {0x59, 0x88}, - {0x5a, 0x88}, - {0x5b, 0x44}, - {0x5c, 0x67}, - {0x5d, 0x49}, - {0x5e, 0x0e}, - {0x6c, 0x0a}, - {0x6d, 0x55}, - {0x6e, 0x11}, - {0x6f, 0x9f}, - {0x6a, 0x40}, - {0x01, 0x40}, - {0x02, 0x40}, - {0x13, 0xe7}, - {0x4f, 0x80}, - {0x50, 0x80}, - {0x51, 0x00}, - {0x52, 0x22}, - {0x53, 0x5e}, - {0x54, 0x80}, - {0x58, 0x9e}, - {0x41, 0x08}, - {0x3f, 0x00}, - {0x75, 0x04}, - {0x76, 0xe1}, - {0x4c, 0x00}, - {0x77, 0x01}, - {0x3d, 0xc2}, - {0x4b, 0x09}, - {0xc9, 0x60}, - {0x41, 0x38}, /* jfm: auto sharpness + auto de-noise */ - {0x56, 0x40}, - {0x34, 0x11}, - {0x3b, 0xc2}, - {0xa4, 0x8a}, /* Night mode trigger point */ - {0x96, 0x00}, - {0x97, 0x30}, - {0x98, 0x20}, - {0x99, 0x20}, - {0x9a, 0x84}, - {0x9b, 0x29}, - {0x9c, 0x03}, - {0x9d, 0x4c}, - {0x9e, 0x3f}, - {0x78, 0x04}, - {0x79, 0x01}, - {0xc8, 0xf0}, - {0x79, 0x0f}, - {0xc8, 0x00}, - {0x79, 0x10}, - {0xc8, 0x7e}, - {0x79, 0x0a}, - {0xc8, 0x80}, - {0x79, 0x0b}, - {0xc8, 0x01}, - {0x79, 0x0c}, - {0xc8, 0x0f}, - {0x79, 0x0d}, - {0xc8, 0x20}, - {0x79, 0x09}, - {0xc8, 0x80}, - {0x79, 0x02}, - {0xc8, 0xc0}, - {0x79, 0x03}, - {0xc8, 0x20}, - {0x79, 0x26}, -}; -static const u8 bridge_start_vga_767x[][2] = { -/* str59 JPG */ - {0x94, 0xaa}, - {0xf1, 0x42}, - {0xe5, 0x04}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {0xc2, 0x0c}, - {0x35, 0x02}, /* turn on JPEG */ - {0xd9, 0x10}, - {0xda, 0x00}, /* for higher clock rate(30fps) */ - {0x34, 0x05}, /* enable Audio Suspend mode */ - {0xc3, 0xf9}, /* enable PRE */ - {0x8c, 0x00}, /* CIF VSize LSB[2:0] */ - {0x8d, 0x1c}, /* output YUV */ -/* {0x34, 0x05}, * enable Audio Suspend mode (?) */ - {0x50, 0x00}, /* H/V divider=0 */ - {0x51, 0xa0}, /* input H=640/4 */ - {0x52, 0x3c}, /* input V=480/4 */ - {0x53, 0x00}, /* offset X=0 */ - {0x54, 0x00}, /* offset Y=0 */ - {0x55, 0x00}, /* H/V size[8]=0 */ - {0x57, 0x00}, /* H-size[9]=0 */ - {0x5c, 0x00}, /* output size[9:8]=0 */ - {0x5a, 0xa0}, /* output H=640/4 */ - {0x5b, 0x78}, /* output V=480/4 */ - {0x1c, 0x0a}, - {0x1d, 0x0a}, - {0x94, 0x11}, -}; -static const u8 sensor_start_vga_767x[][2] = { - {0x11, 0x01}, - {0x1e, 0x04}, - {0x19, 0x02}, - {0x1a, 0x7a}, -}; -static const u8 bridge_start_qvga_767x[][2] = { -/* str86 JPG */ - {0x94, 0xaa}, - {0xf1, 0x42}, - {0xe5, 0x04}, - {0xc0, 0x80}, - {0xc1, 0x60}, - {0xc2, 0x0c}, - {0x35, 0x02}, /* turn on JPEG */ - {0xd9, 0x10}, - {0xc0, 0x50}, /* CIF HSize 640 */ - {0xc1, 0x3c}, /* CIF VSize 480 */ - {0x8c, 0x00}, /* CIF VSize LSB[2:0] */ - {0x8d, 0x1c}, /* output YUV */ - {0x34, 0x05}, /* enable Audio Suspend mode */ - {0xc2, 0x4c}, /* output YUV and Enable DCW */ - {0xc3, 0xf9}, /* enable PRE */ - {0x1c, 0x00}, /* indirect addressing */ - {0x1d, 0x48}, /* output YUV422 */ - {0x50, 0x89}, /* H/V divider=/2; plus DCW AVG */ - {0x51, 0xa0}, /* DCW input H=640/4 */ - {0x52, 0x78}, /* DCW input V=480/4 */ - {0x53, 0x00}, /* offset X=0 */ - {0x54, 0x00}, /* offset Y=0 */ - {0x55, 0x00}, /* H/V size[8]=0 */ - {0x57, 0x00}, /* H-size[9]=0 */ - {0x5c, 0x00}, /* DCW output size[9:8]=0 */ - {0x5a, 0x50}, /* DCW output H=320/4 */ - {0x5b, 0x3c}, /* DCW output V=240/4 */ - {0x1c, 0x0a}, - {0x1d, 0x0a}, - {0x94, 0x11}, -}; -static const u8 sensor_start_qvga_767x[][2] = { - {0x11, 0x01}, - {0x1e, 0x04}, - {0x19, 0x02}, - {0x1a, 0x7a}, -}; - -static const u8 bridge_init_772x[][2] = { - { 0xc2, 0x0c }, - { 0x88, 0xf8 }, - { 0xc3, 0x69 }, - { 0x89, 0xff }, - { 0x76, 0x03 }, - { 0x92, 0x01 }, - { 0x93, 0x18 }, - { 0x94, 0x10 }, - { 0x95, 0x10 }, - { 0xe2, 0x00 }, - { 0xe7, 0x3e }, - - { 0x96, 0x00 }, - - { 0x97, 0x20 }, - { 0x97, 0x20 }, - { 0x97, 0x20 }, - { 0x97, 0x0a }, - { 0x97, 0x3f }, - { 0x97, 0x4a }, - { 0x97, 0x20 }, - { 0x97, 0x15 }, - { 0x97, 0x0b }, - - { 0x8e, 0x40 }, - { 0x1f, 0x81 }, - { 0x34, 0x05 }, - { 0xe3, 0x04 }, - { 0x88, 0x00 }, - { 0x89, 0x00 }, - { 0x76, 0x00 }, - { 0xe7, 0x2e }, - { 0x31, 0xf9 }, - { 0x25, 0x42 }, - { 0x21, 0xf0 }, - - { 0x1c, 0x00 }, - { 0x1d, 0x40 }, - { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ - { 0x1d, 0x00 }, /* payload size */ - - { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ - { 0x1d, 0x58 }, /* frame size */ - { 0x1d, 0x00 }, /* frame size */ - - { 0x1c, 0x0a }, - { 0x1d, 0x08 }, /* turn on UVC header */ - { 0x1d, 0x0e }, /* .. */ - - { 0x8d, 0x1c }, - { 0x8e, 0x80 }, - { 0xe5, 0x04 }, - - { 0xc0, 0x50 }, - { 0xc1, 0x3c }, - { 0xc2, 0x0c }, -}; -static const u8 sensor_init_772x[][2] = { - { 0x12, 0x80 }, - { 0x11, 0x01 }, -/*fixme: better have a delay?*/ - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - { 0x11, 0x01 }, - - { 0x3d, 0x03 }, - { 0x17, 0x26 }, - { 0x18, 0xa0 }, - { 0x19, 0x07 }, - { 0x1a, 0xf0 }, - { 0x32, 0x00 }, - { 0x29, 0xa0 }, - { 0x2c, 0xf0 }, - { 0x65, 0x20 }, - { 0x11, 0x01 }, - { 0x42, 0x7f }, - { 0x63, 0xaa }, /* AWB - was e0 */ - { 0x64, 0xff }, - { 0x66, 0x00 }, - { 0x13, 0xf0 }, /* com8 */ - { 0x0d, 0x41 }, - { 0x0f, 0xc5 }, - { 0x14, 0x11 }, - - { 0x22, 0x7f }, - { 0x23, 0x03 }, - { 0x24, 0x40 }, - { 0x25, 0x30 }, - { 0x26, 0xa1 }, - { 0x2a, 0x00 }, - { 0x2b, 0x00 }, - { 0x6b, 0xaa }, - { 0x13, 0xff }, /* AWB */ - - { 0x90, 0x05 }, - { 0x91, 0x01 }, - { 0x92, 0x03 }, - { 0x93, 0x00 }, - { 0x94, 0x60 }, - { 0x95, 0x3c }, - { 0x96, 0x24 }, - { 0x97, 0x1e }, - { 0x98, 0x62 }, - { 0x99, 0x80 }, - { 0x9a, 0x1e }, - { 0x9b, 0x08 }, - { 0x9c, 0x20 }, - { 0x9e, 0x81 }, - - { 0xa6, 0x07 }, - { 0x7e, 0x0c }, - { 0x7f, 0x16 }, - { 0x80, 0x2a }, - { 0x81, 0x4e }, - { 0x82, 0x61 }, - { 0x83, 0x6f }, - { 0x84, 0x7b }, - { 0x85, 0x86 }, - { 0x86, 0x8e }, - { 0x87, 0x97 }, - { 0x88, 0xa4 }, - { 0x89, 0xaf }, - { 0x8a, 0xc5 }, - { 0x8b, 0xd7 }, - { 0x8c, 0xe8 }, - { 0x8d, 0x20 }, - - { 0x0c, 0x90 }, - - { 0x2b, 0x00 }, - { 0x22, 0x7f }, - { 0x23, 0x03 }, - { 0x11, 0x01 }, - { 0x0c, 0xd0 }, - { 0x64, 0xff }, - { 0x0d, 0x41 }, - - { 0x14, 0x41 }, - { 0x0e, 0xcd }, - { 0xac, 0xbf }, - { 0x8e, 0x00 }, /* De-noise threshold */ - { 0x0c, 0xd0 } -}; -static const u8 bridge_start_vga_772x[][2] = { - {0x1c, 0x00}, - {0x1d, 0x40}, - {0x1d, 0x02}, - {0x1d, 0x00}, - {0x1d, 0x02}, - {0x1d, 0x58}, - {0x1d, 0x00}, - {0xc0, 0x50}, - {0xc1, 0x3c}, -}; -static const u8 sensor_start_vga_772x[][2] = { - {0x12, 0x00}, - {0x17, 0x26}, - {0x18, 0xa0}, - {0x19, 0x07}, - {0x1a, 0xf0}, - {0x29, 0xa0}, - {0x2c, 0xf0}, - {0x65, 0x20}, -}; -static const u8 bridge_start_qvga_772x[][2] = { - {0x1c, 0x00}, - {0x1d, 0x40}, - {0x1d, 0x02}, - {0x1d, 0x00}, - {0x1d, 0x01}, - {0x1d, 0x4b}, - {0x1d, 0x00}, - {0xc0, 0x28}, - {0xc1, 0x1e}, -}; -static const u8 sensor_start_qvga_772x[][2] = { - {0x12, 0x40}, - {0x17, 0x3f}, - {0x18, 0x50}, - {0x19, 0x03}, - {0x1a, 0x78}, - {0x29, 0x50}, - {0x2c, 0x78}, - {0x65, 0x2f}, -}; - -static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - - PDEBUG(D_USBO, "SET 01 0000 %04x %02x", reg, val); - gspca_dev->usb_buf[0] = val; - ret = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - if (ret < 0) { - pr_err("write failed %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return 0; - ret = usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - 0x01, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]); - if (ret < 0) { - pr_err("read failed %d\n", ret); - gspca_dev->usb_err = ret; - } - return gspca_dev->usb_buf[0]; -} - -/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. - * (direction and output)? */ -static void ov534_set_led(struct gspca_dev *gspca_dev, int status) -{ - u8 data; - - PDEBUG(D_CONF, "led status: %d", status); - - data = ov534_reg_read(gspca_dev, 0x21); - data |= 0x80; - ov534_reg_write(gspca_dev, 0x21, data); - - data = ov534_reg_read(gspca_dev, 0x23); - if (status) - data |= 0x80; - else - data &= ~0x80; - - ov534_reg_write(gspca_dev, 0x23, data); - - if (!status) { - data = ov534_reg_read(gspca_dev, 0x21); - data &= ~0x80; - ov534_reg_write(gspca_dev, 0x21, data); - } -} - -static int sccb_check_status(struct gspca_dev *gspca_dev) -{ - u8 data; - int i; - - for (i = 0; i < 5; i++) { - msleep(10); - data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); - - switch (data) { - case 0x00: - return 1; - case 0x04: - return 0; - case 0x03: - break; - default: - PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", - data, i + 1); - } - } - return 0; -} - -static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) -{ - PDEBUG(D_USBO, "sccb write: %02x %02x", reg, val); - ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); - ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); - - if (!sccb_check_status(gspca_dev)) { - pr_err("sccb_reg_write failed\n"); - gspca_dev->usb_err = -EIO; - } -} - -static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) -{ - ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); - if (!sccb_check_status(gspca_dev)) - pr_err("sccb_reg_read failed 1\n"); - - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); - if (!sccb_check_status(gspca_dev)) - pr_err("sccb_reg_read failed 2\n"); - - return ov534_reg_read(gspca_dev, OV534_REG_READ); -} - -/* output a bridge sequence (reg - val) */ -static void reg_w_array(struct gspca_dev *gspca_dev, - const u8 (*data)[2], int len) -{ - while (--len >= 0) { - ov534_reg_write(gspca_dev, (*data)[0], (*data)[1]); - data++; - } -} - -/* output a sensor sequence (reg - val) */ -static void sccb_w_array(struct gspca_dev *gspca_dev, - const u8 (*data)[2], int len) -{ - while (--len >= 0) { - if ((*data)[0] != 0xff) { - sccb_reg_write(gspca_dev, (*data)[0], (*data)[1]); - } else { - sccb_reg_read(gspca_dev, (*data)[1]); - sccb_reg_write(gspca_dev, 0xff, 0x00); - } - data++; - } -} - -/* ov772x specific controls */ -static void set_frame_rate(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - struct rate_s { - u8 fps; - u8 r11; - u8 r0d; - u8 re5; - }; - const struct rate_s *r; - static const struct rate_s rate_0[] = { /* 640x480 */ - {60, 0x01, 0xc1, 0x04}, - {50, 0x01, 0x41, 0x02}, - {40, 0x02, 0xc1, 0x04}, - {30, 0x04, 0x81, 0x02}, - {15, 0x03, 0x41, 0x04}, - }; - static const struct rate_s rate_1[] = { /* 320x240 */ - {125, 0x02, 0x81, 0x02}, - {100, 0x02, 0xc1, 0x04}, - {75, 0x03, 0xc1, 0x04}, - {60, 0x04, 0xc1, 0x04}, - {50, 0x02, 0x41, 0x04}, - {40, 0x03, 0x41, 0x04}, - {30, 0x04, 0x41, 0x04}, - }; - - if (sd->sensor != SENSOR_OV772x) - return; - if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) { - r = rate_0; - i = ARRAY_SIZE(rate_0); - } else { - r = rate_1; - i = ARRAY_SIZE(rate_1); - } - while (--i > 0) { - if (sd->frame_rate >= r->fps) - break; - r++; - } - - sccb_reg_write(gspca_dev, 0x11, r->r11); - sccb_reg_write(gspca_dev, 0x0d, r->r0d); - ov534_reg_write(gspca_dev, 0xe5, r->re5); - - PDEBUG(D_PROBE, "frame_rate: %d", r->fps); -} - -static void sethue(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV767x) { - /* TBD */ - } else { - s16 huesin; - s16 huecos; - - /* fixp_sin and fixp_cos accept only positive values, while - * our val is between -90 and 90 - */ - val += 360; - - /* According to the datasheet the registers expect HUESIN and - * HUECOS to be the result of the trigonometric functions, - * scaled by 0x80. - * - * The 0x100 here represents the maximun absolute value - * returned byt fixp_sin and fixp_cos, so the scaling will - * consider the result like in the interval [-1.0, 1.0]. - */ - huesin = fixp_sin(val) * 0x80 / 0x100; - huecos = fixp_cos(val) * 0x80 / 0x100; - - if (huesin < 0) { - sccb_reg_write(gspca_dev, 0xab, - sccb_reg_read(gspca_dev, 0xab) | 0x2); - huesin = -huesin; - } else { - sccb_reg_write(gspca_dev, 0xab, - sccb_reg_read(gspca_dev, 0xab) & ~0x2); - - } - sccb_reg_write(gspca_dev, 0xa9, (u8)huecos); - sccb_reg_write(gspca_dev, 0xaa, (u8)huesin); - } -} - -static void setsaturation(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV767x) { - int i; - static u8 color_tb[][6] = { - {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, - {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, - {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, - {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, - {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, - {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, - {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, - }; - - for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) - sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); - } else { - sccb_reg_write(gspca_dev, 0xa7, val); /* U saturation */ - sccb_reg_write(gspca_dev, 0xa8, val); /* V saturation */ - } -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV767x) { - if (val < 0) - val = 0x80 - val; - sccb_reg_write(gspca_dev, 0x55, val); /* bright */ - } else { - sccb_reg_write(gspca_dev, 0x9b, val); - } -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV767x) - sccb_reg_write(gspca_dev, 0x56, val); /* contras */ - else - sccb_reg_write(gspca_dev, 0x9c, val); -} - -static void setgain(struct gspca_dev *gspca_dev, s32 val) -{ - switch (val & 0x30) { - case 0x00: - val &= 0x0f; - break; - case 0x10: - val &= 0x0f; - val |= 0x30; - break; - case 0x20: - val &= 0x0f; - val |= 0x70; - break; - default: -/* case 0x30: */ - val &= 0x0f; - val |= 0xf0; - break; - } - sccb_reg_write(gspca_dev, 0x00, val); -} - -static s32 getgain(struct gspca_dev *gspca_dev) -{ - return sccb_reg_read(gspca_dev, 0x00); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV767x) { - - /* set only aec[9:2] */ - sccb_reg_write(gspca_dev, 0x10, val); /* aech */ - } else { - - /* 'val' is one byte and represents half of the exposure value - * we are going to set into registers, a two bytes value: - * - * MSB: ((u16) val << 1) >> 8 == val >> 7 - * LSB: ((u16) val << 1) & 0xff == val << 1 - */ - sccb_reg_write(gspca_dev, 0x08, val >> 7); - sccb_reg_write(gspca_dev, 0x10, val << 1); - } -} - -static s32 getexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV767x) { - /* get only aec[9:2] */ - return sccb_reg_read(gspca_dev, 0x10); /* aech */ - } else { - u8 hi = sccb_reg_read(gspca_dev, 0x08); - u8 lo = sccb_reg_read(gspca_dev, 0x10); - return (hi << 8 | lo) >> 1; - } -} - -static void setagc(struct gspca_dev *gspca_dev, s32 val) -{ - if (val) { - sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) | 0x04); - sccb_reg_write(gspca_dev, 0x64, - sccb_reg_read(gspca_dev, 0x64) | 0x03); - } else { - sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) & ~0x04); - sccb_reg_write(gspca_dev, 0x64, - sccb_reg_read(gspca_dev, 0x64) & ~0x03); - } -} - -static void setawb(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (val) { - sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) | 0x02); - if (sd->sensor == SENSOR_OV772x) - sccb_reg_write(gspca_dev, 0x63, - sccb_reg_read(gspca_dev, 0x63) | 0xc0); - } else { - sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) & ~0x02); - if (sd->sensor == SENSOR_OV772x) - sccb_reg_write(gspca_dev, 0x63, - sccb_reg_read(gspca_dev, 0x63) & ~0xc0); - } -} - -static void setaec(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 data; - - data = sd->sensor == SENSOR_OV767x ? - 0x05 : /* agc + aec */ - 0x01; /* agc */ - switch (val) { - case V4L2_EXPOSURE_AUTO: - sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) | data); - break; - case V4L2_EXPOSURE_MANUAL: - sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) & ~data); - break; - } -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */ - sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */ -} - -static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 val; - - if (sd->sensor == SENSOR_OV767x) { - val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */ - val &= ~0x30; - if (hflip) - val |= 0x20; - if (vflip) - val |= 0x10; - sccb_reg_write(gspca_dev, 0x1e, val); - } else { - val = sccb_reg_read(gspca_dev, 0x0c); - val &= ~0xc0; - if (hflip == 0) - val |= 0x40; - if (vflip == 0) - val |= 0x80; - sccb_reg_write(gspca_dev, 0x0c, val); - } -} - -static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - val = val ? 0x9e : 0x00; - if (sd->sensor == SENSOR_OV767x) { - sccb_reg_write(gspca_dev, 0x2a, 0x00); - if (val) - val = 0x9d; /* insert dummy to 25fps for 50Hz */ - } - sccb_reg_write(gspca_dev, 0x2b, val); -} - - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - - cam->cam_mode = ov772x_mode; - cam->nmodes = ARRAY_SIZE(ov772x_mode); - - sd->frame_rate = 30; - - return 0; -} - -static int ov534_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); - struct gspca_dev *gspca_dev = &sd->gspca_dev; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - gspca_dev->usb_err = 0; - if (ctrl->val && sd->gain && gspca_dev->streaming) - sd->gain->val = getgain(gspca_dev); - return gspca_dev->usb_err; - - case V4L2_CID_EXPOSURE_AUTO: - gspca_dev->usb_err = 0; - if (ctrl->val == V4L2_EXPOSURE_AUTO && sd->exposure && - gspca_dev->streaming) - sd->exposure->val = getexposure(gspca_dev); - return gspca_dev->usb_err; - } - return -EINVAL; -} - -static int ov534_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); - struct gspca_dev *gspca_dev = &sd->gspca_dev; - - gspca_dev->usb_err = 0; - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_HUE: - sethue(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setsaturation(gspca_dev, ctrl->val); - break; - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - /* case V4L2_CID_GAIN: */ - setagc(gspca_dev, ctrl->val); - if (!gspca_dev->usb_err && !ctrl->val && sd->gain) - setgain(gspca_dev, sd->gain->val); - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - setawb(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE_AUTO: - /* case V4L2_CID_EXPOSURE: */ - setaec(gspca_dev, ctrl->val); - if (!gspca_dev->usb_err && ctrl->val == V4L2_EXPOSURE_MANUAL && - sd->exposure) - setexposure(gspca_dev, sd->exposure->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_HFLIP: - sethvflip(gspca_dev, ctrl->val, sd->vflip->val); - break; - case V4L2_CID_VFLIP: - sethvflip(gspca_dev, sd->hflip->val, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setlightfreq(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops ov534_ctrl_ops = { - .g_volatile_ctrl = ov534_g_volatile_ctrl, - .s_ctrl = ov534_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; - /* parameters with different values between the supported sensors */ - int saturation_min; - int saturation_max; - int saturation_def; - int brightness_min; - int brightness_max; - int brightness_def; - int contrast_max; - int contrast_def; - int exposure_min; - int exposure_max; - int exposure_def; - int hflip_def; - - if (sd->sensor == SENSOR_OV767x) { - saturation_min = 0, - saturation_max = 6, - saturation_def = 3, - brightness_min = -127; - brightness_max = 127; - brightness_def = 0; - contrast_max = 0x80; - contrast_def = 0x40; - exposure_min = 0x08; - exposure_max = 0x60; - exposure_def = 0x13; - hflip_def = 1; - } else { - saturation_min = 0, - saturation_max = 255, - saturation_def = 64, - brightness_min = 0; - brightness_max = 255; - brightness_def = 0; - contrast_max = 255; - contrast_def = 32; - exposure_min = 0; - exposure_max = 255; - exposure_def = 120; - hflip_def = 0; - } - - gspca_dev->vdev.ctrl_handler = hdl; - - v4l2_ctrl_handler_init(hdl, 13); - - if (sd->sensor == SENSOR_OV772x) - sd->hue = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_HUE, -90, 90, 1, 0); - - sd->saturation = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_SATURATION, saturation_min, saturation_max, 1, - saturation_def); - sd->brightness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_BRIGHTNESS, brightness_min, brightness_max, 1, - brightness_def); - sd->contrast = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_CONTRAST, 0, contrast_max, 1, contrast_def); - - if (sd->sensor == SENSOR_OV772x) { - sd->autogain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - sd->gain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_GAIN, 0, 63, 1, 20); - } - - sd->autoexposure = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, - V4L2_EXPOSURE_AUTO); - sd->exposure = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_EXPOSURE, exposure_min, exposure_max, 1, - exposure_def); - - sd->autowhitebalance = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - - if (sd->sensor == SENSOR_OV772x) - sd->sharpness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 63, 1, 0); - - sd->hflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, hflip_def); - sd->vflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_50HZ, 0, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - - if (sd->sensor == SENSOR_OV772x) - v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); - - v4l2_ctrl_auto_cluster(2, &sd->autoexposure, V4L2_EXPOSURE_MANUAL, - true); - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 sensor_id; - static const struct reg_array bridge_init[NSENSORS] = { - [SENSOR_OV767x] = {bridge_init_767x, ARRAY_SIZE(bridge_init_767x)}, - [SENSOR_OV772x] = {bridge_init_772x, ARRAY_SIZE(bridge_init_772x)}, - }; - static const struct reg_array sensor_init[NSENSORS] = { - [SENSOR_OV767x] = {sensor_init_767x, ARRAY_SIZE(sensor_init_767x)}, - [SENSOR_OV772x] = {sensor_init_772x, ARRAY_SIZE(sensor_init_772x)}, - }; - - /* reset bridge */ - ov534_reg_write(gspca_dev, 0xe7, 0x3a); - ov534_reg_write(gspca_dev, 0xe0, 0x08); - msleep(100); - - /* initialize the sensor address */ - ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, 0x42); - - /* reset sensor */ - sccb_reg_write(gspca_dev, 0x12, 0x80); - msleep(10); - - /* probe the sensor */ - sccb_reg_read(gspca_dev, 0x0a); - sensor_id = sccb_reg_read(gspca_dev, 0x0a) << 8; - sccb_reg_read(gspca_dev, 0x0b); - sensor_id |= sccb_reg_read(gspca_dev, 0x0b); - PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); - - if ((sensor_id & 0xfff0) == 0x7670) { - sd->sensor = SENSOR_OV767x; - gspca_dev->cam.cam_mode = ov767x_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode); - } else { - sd->sensor = SENSOR_OV772x; - gspca_dev->cam.bulk = 1; - gspca_dev->cam.bulk_size = 16384; - gspca_dev->cam.bulk_nurbs = 2; - gspca_dev->cam.mode_framerates = ov772x_framerates; - } - - /* initialize */ - reg_w_array(gspca_dev, bridge_init[sd->sensor].val, - bridge_init[sd->sensor].len); - ov534_set_led(gspca_dev, 1); - sccb_w_array(gspca_dev, sensor_init[sd->sensor].val, - sensor_init[sd->sensor].len); - if (sd->sensor == SENSOR_OV767x) - sd_start(gspca_dev); - sd_stopN(gspca_dev); -/* set_frame_rate(gspca_dev); */ - - return gspca_dev->usb_err; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int mode; - static const struct reg_array bridge_start[NSENSORS][2] = { - [SENSOR_OV767x] = {{bridge_start_qvga_767x, - ARRAY_SIZE(bridge_start_qvga_767x)}, - {bridge_start_vga_767x, - ARRAY_SIZE(bridge_start_vga_767x)}}, - [SENSOR_OV772x] = {{bridge_start_qvga_772x, - ARRAY_SIZE(bridge_start_qvga_772x)}, - {bridge_start_vga_772x, - ARRAY_SIZE(bridge_start_vga_772x)}}, - }; - static const struct reg_array sensor_start[NSENSORS][2] = { - [SENSOR_OV767x] = {{sensor_start_qvga_767x, - ARRAY_SIZE(sensor_start_qvga_767x)}, - {sensor_start_vga_767x, - ARRAY_SIZE(sensor_start_vga_767x)}}, - [SENSOR_OV772x] = {{sensor_start_qvga_772x, - ARRAY_SIZE(sensor_start_qvga_772x)}, - {sensor_start_vga_772x, - ARRAY_SIZE(sensor_start_vga_772x)}}, - }; - - /* (from ms-win trace) */ - if (sd->sensor == SENSOR_OV767x) - sccb_reg_write(gspca_dev, 0x1e, 0x04); - /* black sun enable ? */ - - mode = gspca_dev->curr_mode; /* 0: 320x240, 1: 640x480 */ - reg_w_array(gspca_dev, bridge_start[sd->sensor][mode].val, - bridge_start[sd->sensor][mode].len); - sccb_w_array(gspca_dev, sensor_start[sd->sensor][mode].val, - sensor_start[sd->sensor][mode].len); - - set_frame_rate(gspca_dev); - - if (sd->hue) - sethue(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue)); - setsaturation(gspca_dev, v4l2_ctrl_g_ctrl(sd->saturation)); - if (sd->autogain) - setagc(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); - setawb(gspca_dev, v4l2_ctrl_g_ctrl(sd->autowhitebalance)); - setaec(gspca_dev, v4l2_ctrl_g_ctrl(sd->autoexposure)); - if (sd->gain) - setgain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); - setbrightness(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness)); - setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); - if (sd->sharpness) - setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); - sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), - v4l2_ctrl_g_ctrl(sd->vflip)); - setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq)); - - ov534_set_led(gspca_dev, 1); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); -} - -/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ -#define UVC_STREAM_EOH (1 << 7) -#define UVC_STREAM_ERR (1 << 6) -#define UVC_STREAM_STI (1 << 5) -#define UVC_STREAM_RES (1 << 4) -#define UVC_STREAM_SCR (1 << 3) -#define UVC_STREAM_PTS (1 << 2) -#define UVC_STREAM_EOF (1 << 1) -#define UVC_STREAM_FID (1 << 0) - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u32 this_pts; - u16 this_fid; - int remaining_len = len; - int payload_len; - - payload_len = gspca_dev->cam.bulk ? 2048 : 2040; - do { - len = min(remaining_len, payload_len); - - /* Payloads are prefixed with a UVC-style header. We - consider a frame to start when the FID toggles, or the PTS - changes. A frame ends when EOF is set, and we've received - the correct number of bytes. */ - - /* Verify UVC header. Header length is always 12 */ - if (data[0] != 12 || len < 12) { - PDEBUG(D_PACK, "bad header"); - goto discard; - } - - /* Check errors */ - if (data[1] & UVC_STREAM_ERR) { - PDEBUG(D_PACK, "payload error"); - goto discard; - } - - /* Extract PTS and FID */ - if (!(data[1] & UVC_STREAM_PTS)) { - PDEBUG(D_PACK, "PTS not present"); - goto discard; - } - this_pts = (data[5] << 24) | (data[4] << 16) - | (data[3] << 8) | data[2]; - this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; - - /* If PTS or FID has changed, start a new frame. */ - if (this_pts != sd->last_pts || this_fid != sd->last_fid) { - if (gspca_dev->last_packet_type == INTER_PACKET) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - sd->last_pts = this_pts; - sd->last_fid = this_fid; - gspca_frame_add(gspca_dev, FIRST_PACKET, - data + 12, len - 12); - /* If this packet is marked as EOF, end the frame */ - } else if (data[1] & UVC_STREAM_EOF) { - sd->last_pts = 0; - if (gspca_dev->pixfmt == V4L2_PIX_FMT_YUYV - && gspca_dev->image_len + len - 12 != - gspca_dev->width * gspca_dev->height * 2) { - PDEBUG(D_PACK, "wrong sized frame"); - goto discard; - } - gspca_frame_add(gspca_dev, LAST_PACKET, - data + 12, len - 12); - } else { - - /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, - data + 12, len - 12); - } - - /* Done this payload */ - goto scan_next; - -discard: - /* Discard data until a new frame starts. */ - gspca_dev->last_packet_type = DISCARD_PACKET; - -scan_next: - remaining_len -= len; - data += len; - } while (remaining_len > 0); -} - -/* get stream parameters (framerate) */ -static void sd_get_streamparm(struct gspca_dev *gspca_dev, - struct v4l2_streamparm *parm) -{ - struct v4l2_captureparm *cp = &parm->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - struct sd *sd = (struct sd *) gspca_dev; - - cp->capability |= V4L2_CAP_TIMEPERFRAME; - tpf->numerator = 1; - tpf->denominator = sd->frame_rate; -} - -/* set stream parameters (framerate) */ -static void sd_set_streamparm(struct gspca_dev *gspca_dev, - struct v4l2_streamparm *parm) -{ - struct v4l2_captureparm *cp = &parm->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - struct sd *sd = (struct sd *) gspca_dev; - - /* Set requested framerate */ - sd->frame_rate = tpf->denominator / tpf->numerator; - if (gspca_dev->streaming) - set_frame_rate(gspca_dev); - - /* Return the actual framerate */ - tpf->numerator = 1; - tpf->denominator = sd->frame_rate; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .get_streamparm = sd_get_streamparm, - .set_streamparm = sd_set_streamparm, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x1415, 0x2000)}, - {USB_DEVICE(0x06f8, 0x3002)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c deleted file mode 100644 index c4cd028fe0b4..000000000000 --- a/drivers/media/video/gspca/ov534_9.c +++ /dev/null @@ -1,1500 +0,0 @@ -/* - * ov534-ov9xxx gspca driver - * - * Copyright (C) 2009-2011 Jean-Francois Moine http://moinejf.free.fr - * Copyright (C) 2008 Antonio Ospite - * Copyright (C) 2008 Jim Paris - * - * Based on a prototype written by Mark Ferrell - * USB protocol reverse engineered by Jim Paris - * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "ov534_9" - -#include "gspca.h" - -#define OV534_REG_ADDRESS 0xf1 /* sensor address */ -#define OV534_REG_SUBADDR 0xf2 -#define OV534_REG_WRITE 0xf3 -#define OV534_REG_READ 0xf4 -#define OV534_REG_OPERATION 0xf5 -#define OV534_REG_STATUS 0xf6 - -#define OV534_OP_WRITE_3 0x37 -#define OV534_OP_WRITE_2 0x33 -#define OV534_OP_READ_2 0xf9 - -#define CTRL_TIMEOUT 500 - -MODULE_AUTHOR("Jean-Francois Moine "); -MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - __u32 last_pts; - u8 last_fid; - - u8 sensor; -}; -enum sensors { - SENSOR_OV965x, /* ov9657 */ - SENSOR_OV971x, /* ov9712 */ - SENSOR_OV562x, /* ov5621 */ - NSENSORS -}; - -static const struct v4l2_pix_format ov965x_mode[] = { -#define QVGA_MODE 0 - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, -#define VGA_MODE 1 - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, -#define SVGA_MODE 2 - {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 800, - .sizeimage = 800 * 600 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, -#define XGA_MODE 3 - {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 1024, - .sizeimage = 1024 * 768 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, -#define SXGA_MODE 4 - {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 1024 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, -}; - -static const struct v4l2_pix_format ov971x_mode[] = { - {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB - } -}; - -static const struct v4l2_pix_format ov562x_mode[] = { - {2592, 1680, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 2592, - .sizeimage = 2592 * 1680, - .colorspace = V4L2_COLORSPACE_SRGB - } -}; - -static const u8 bridge_init[][2] = { - {0x88, 0xf8}, - {0x89, 0xff}, - {0x76, 0x03}, - {0x92, 0x03}, - {0x95, 0x10}, - {0xe2, 0x00}, - {0xe7, 0x3e}, - {0x8d, 0x1c}, - {0x8e, 0x00}, - {0x8f, 0x00}, - {0x1f, 0x00}, - {0xc3, 0xf9}, - {0x89, 0xff}, - {0x88, 0xf8}, - {0x76, 0x03}, - {0x92, 0x01}, - {0x93, 0x18}, - {0x1c, 0x0a}, - {0x1d, 0x48}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {0x34, 0x05}, - {0xc2, 0x0c}, - {0xc3, 0xf9}, - {0x34, 0x05}, - {0xe7, 0x2e}, - {0x31, 0xf9}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x25, 0x42}, - {0x94, 0x11}, -}; - -static const u8 ov965x_init[][2] = { - {0x12, 0x80}, /* com7 - SSCB reset */ - {0x00, 0x00}, /* gain */ - {0x01, 0x80}, /* blue */ - {0x02, 0x80}, /* red */ - {0x03, 0x1b}, /* vref */ - {0x04, 0x03}, /* com1 - exposure low bits */ - {0x0b, 0x57}, /* ver */ - {0x0e, 0x61}, /* com5 */ - {0x0f, 0x42}, /* com6 */ - {0x11, 0x00}, /* clkrc */ - {0x12, 0x02}, /* com7 - 15fps VGA YUYV */ - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x14, 0x28}, /* com9 */ - {0x16, 0x24}, /* reg16 */ - {0x17, 0x1d}, /* hstart*/ - {0x18, 0xbd}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x81}, /* vstop*/ - {0x1e, 0x04}, /* mvfp */ - {0x24, 0x3c}, /* aew */ - {0x25, 0x36}, /* aeb */ - {0x26, 0x71}, /* vpt */ - {0x27, 0x08}, /* bbias */ - {0x28, 0x08}, /* gbbias */ - {0x29, 0x15}, /* gr com */ - {0x2a, 0x00}, /* exhch */ - {0x2b, 0x00}, /* exhcl */ - {0x2c, 0x08}, /* rbias */ - {0x32, 0xff}, /* href */ - {0x33, 0x00}, /* chlf */ - {0x34, 0x3f}, /* aref1 */ - {0x35, 0x00}, /* aref2 */ - {0x36, 0xf8}, /* aref3 */ - {0x38, 0x72}, /* adc2 */ - {0x39, 0x57}, /* aref4 */ - {0x3a, 0x80}, /* tslb - yuyv */ - {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ - {0x3d, 0x99}, /* com13 */ - {0x3f, 0xc1}, /* edge */ - {0x40, 0xc0}, /* com15 */ - {0x41, 0x40}, /* com16 */ - {0x42, 0xc0}, /* com17 */ - {0x43, 0x0a}, /* rsvd */ - {0x44, 0xf0}, - {0x45, 0x46}, - {0x46, 0x62}, - {0x47, 0x2a}, - {0x48, 0x3c}, - {0x4a, 0xfc}, - {0x4b, 0xfc}, - {0x4c, 0x7f}, - {0x4d, 0x7f}, - {0x4e, 0x7f}, - {0x4f, 0x98}, /* matrix */ - {0x50, 0x98}, - {0x51, 0x00}, - {0x52, 0x28}, - {0x53, 0x70}, - {0x54, 0x98}, - {0x58, 0x1a}, /* matrix coef sign */ - {0x59, 0x85}, /* AWB control */ - {0x5a, 0xa9}, - {0x5b, 0x64}, - {0x5c, 0x84}, - {0x5d, 0x53}, - {0x5e, 0x0e}, - {0x5f, 0xf0}, /* AWB blue limit */ - {0x60, 0xf0}, /* AWB red limit */ - {0x61, 0xf0}, /* AWB green limit */ - {0x62, 0x00}, /* lcc1 */ - {0x63, 0x00}, /* lcc2 */ - {0x64, 0x02}, /* lcc3 */ - {0x65, 0x16}, /* lcc4 */ - {0x66, 0x01}, /* lcc5 */ - {0x69, 0x02}, /* hv */ - {0x6b, 0x5a}, /* dbvl */ - {0x6c, 0x04}, - {0x6d, 0x55}, - {0x6e, 0x00}, - {0x6f, 0x9d}, - {0x70, 0x21}, /* dnsth */ - {0x71, 0x78}, - {0x72, 0x00}, /* poidx */ - {0x73, 0x01}, /* pckdv */ - {0x74, 0x3a}, /* xindx */ - {0x75, 0x35}, /* yindx */ - {0x76, 0x01}, - {0x77, 0x02}, - {0x7a, 0x12}, /* gamma curve */ - {0x7b, 0x08}, - {0x7c, 0x16}, - {0x7d, 0x30}, - {0x7e, 0x5e}, - {0x7f, 0x72}, - {0x80, 0x82}, - {0x81, 0x8e}, - {0x82, 0x9a}, - {0x83, 0xa4}, - {0x84, 0xac}, - {0x85, 0xb8}, - {0x86, 0xc3}, - {0x87, 0xd6}, - {0x88, 0xe6}, - {0x89, 0xf2}, - {0x8a, 0x03}, - {0x8c, 0x89}, /* com19 */ - {0x14, 0x28}, /* com9 */ - {0x90, 0x7d}, - {0x91, 0x7b}, - {0x9d, 0x03}, /* lcc6 */ - {0x9e, 0x04}, /* lcc7 */ - {0x9f, 0x7a}, - {0xa0, 0x79}, - {0xa1, 0x40}, /* aechm */ - {0xa4, 0x50}, /* com21 */ - {0xa5, 0x68}, /* com26 */ - {0xa6, 0x4a}, /* AWB green */ - {0xa8, 0xc1}, /* refa8 */ - {0xa9, 0xef}, /* refa9 */ - {0xaa, 0x92}, - {0xab, 0x04}, - {0xac, 0x80}, /* black level control */ - {0xad, 0x80}, - {0xae, 0x80}, - {0xaf, 0x80}, - {0xb2, 0xf2}, - {0xb3, 0x20}, - {0xb4, 0x20}, /* ctrlb4 */ - {0xb5, 0x00}, - {0xb6, 0xaf}, - {0xbb, 0xae}, - {0xbc, 0x7f}, /* ADC channel offsets */ - {0xdb, 0x7f}, - {0xbe, 0x7f}, - {0xbf, 0x7f}, - {0xc0, 0xe2}, - {0xc1, 0xc0}, - {0xc2, 0x01}, - {0xc3, 0x4e}, - {0xc6, 0x85}, - {0xc7, 0x80}, /* com24 */ - {0xc9, 0xe0}, - {0xca, 0xe8}, - {0xcb, 0xf0}, - {0xcc, 0xd8}, - {0xcd, 0xf1}, - {0x4f, 0x98}, /* matrix */ - {0x50, 0x98}, - {0x51, 0x00}, - {0x52, 0x28}, - {0x53, 0x70}, - {0x54, 0x98}, - {0x58, 0x1a}, - {0xff, 0x41}, /* read 41, write ff 00 */ - {0x41, 0x40}, /* com16 */ - - {0xc5, 0x03}, /* 60 Hz banding filter */ - {0x6a, 0x02}, /* 50 Hz banding filter */ - - {0x12, 0x62}, /* com7 - 30fps VGA YUV */ - {0x36, 0xfa}, /* aref3 */ - {0x69, 0x0a}, /* hv */ - {0x8c, 0x89}, /* com22 */ - {0x14, 0x28}, /* com9 */ - {0x3e, 0x0c}, - {0x41, 0x40}, /* com16 */ - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x3a}, - {0x75, 0x35}, - {0x76, 0x01}, - {0xc7, 0x80}, - {0x03, 0x12}, /* vref */ - {0x17, 0x16}, /* hstart */ - {0x18, 0x02}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x3d}, /* vstop */ - {0x32, 0xff}, /* href */ - {0xc0, 0xaa}, -}; - -static const u8 bridge_init_2[][2] = { - {0x94, 0xaa}, - {0xf1, 0x60}, - {0xe5, 0x04}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {0x8c, 0x00}, - {0x8d, 0x1c}, - {0x34, 0x05}, - - {0xc2, 0x0c}, - {0xc3, 0xf9}, - {0xda, 0x01}, - {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x3c}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x57, 0x00}, - {0x5c, 0x00}, - {0x5a, 0xa0}, - {0x5b, 0x78}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x94, 0x11}, -}; - -static const u8 ov965x_init_2[][2] = { - {0x3b, 0xc4}, - {0x1e, 0x04}, /* mvfp */ - {0x13, 0xe0}, /* com8 */ - {0x00, 0x00}, /* gain */ - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x11, 0x03}, /* clkrc */ - {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x05}, - {0xc5, 0x07}, - {0xa2, 0x4b}, - {0xa3, 0x3e}, - {0x2d, 0x00}, - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc0}, /* com17 */ - {0x2d, 0x00}, - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, /* com17 */ -/* sharpness */ - {0x3f, 0x01}, - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, /* com17 */ -/* saturation */ - {0x4f, 0x98}, /* matrix */ - {0x50, 0x98}, - {0x51, 0x00}, - {0x52, 0x28}, - {0x53, 0x70}, - {0x54, 0x98}, - {0x58, 0x1a}, - {0xff, 0x41}, /* read 41, write ff 00 */ - {0x41, 0x40}, /* com16 */ -/* contrast */ - {0x56, 0x40}, -/* brightness */ - {0x55, 0x8f}, -/* expo */ - {0x10, 0x25}, /* aech - exposure high bits */ - {0xff, 0x13}, /* read 13, write ff 00 */ - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ -}; - -static const u8 ov971x_init[][2] = { - {0x12, 0x80}, - {0x09, 0x10}, - {0x1e, 0x07}, - {0x5f, 0x18}, - {0x69, 0x04}, - {0x65, 0x2a}, - {0x68, 0x0a}, - {0x39, 0x28}, - {0x4d, 0x90}, - {0xc1, 0x80}, - {0x0c, 0x30}, - {0x6d, 0x02}, - {0x96, 0xf1}, - {0xbc, 0x68}, - {0x12, 0x00}, - {0x3b, 0x00}, - {0x97, 0x80}, - {0x17, 0x25}, - {0x18, 0xa2}, - {0x19, 0x01}, - {0x1a, 0xca}, - {0x03, 0x0a}, - {0x32, 0x07}, - {0x98, 0x40}, /*{0x98, 0x00},*/ - {0x99, 0xA0}, /*{0x99, 0x00},*/ - {0x9a, 0x01}, /*{0x9a, 0x00},*/ - {0x57, 0x00}, - {0x58, 0x78}, /*{0x58, 0xc8},*/ - {0x59, 0x50}, /*{0x59, 0xa0},*/ - {0x4c, 0x13}, - {0x4b, 0x36}, - {0x3d, 0x3c}, - {0x3e, 0x03}, - {0xbd, 0x50}, /*{0xbd, 0xa0},*/ - {0xbe, 0x78}, /*{0xbe, 0xc8},*/ - {0x4e, 0x55}, - {0x4f, 0x55}, - {0x50, 0x55}, - {0x51, 0x55}, - {0x24, 0x55}, - {0x25, 0x40}, - {0x26, 0xa1}, - {0x5c, 0x59}, - {0x5d, 0x00}, - {0x11, 0x00}, - {0x2a, 0x98}, - {0x2b, 0x06}, - {0x2d, 0x00}, - {0x2e, 0x00}, - {0x13, 0xa5}, - {0x14, 0x40}, - {0x4a, 0x00}, - {0x49, 0xce}, - {0x22, 0x03}, - {0x09, 0x00} -}; - -static const u8 ov965x_start_1_vga[][2] = { /* same for qvga */ - {0x12, 0x62}, /* com7 - 30fps VGA YUV */ - {0x36, 0xfa}, /* aref3 */ - {0x69, 0x0a}, /* hv */ - {0x8c, 0x89}, /* com22 */ - {0x14, 0x28}, /* com9 */ - {0x3e, 0x0c}, /* com14 */ - {0x41, 0x40}, /* com16 */ - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x3a}, - {0x75, 0x35}, - {0x76, 0x01}, - {0xc7, 0x80}, /* com24 */ - {0x03, 0x12}, /* vref */ - {0x17, 0x16}, /* hstart */ - {0x18, 0x02}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x3d}, /* vstop */ - {0x32, 0xff}, /* href */ - {0xc0, 0xaa}, -}; - -static const u8 ov965x_start_1_svga[][2] = { - {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */ - {0x36, 0xf8}, /* aref3 */ - {0x69, 0x02}, /* hv */ - {0x8c, 0x0d}, /* com22 */ - {0x3e, 0x0c}, /* com14 */ - {0x41, 0x40}, /* com16 */ - {0x72, 0x00}, - {0x73, 0x01}, - {0x74, 0x3a}, - {0x75, 0x35}, - {0x76, 0x01}, - {0xc7, 0x80}, /* com24 */ - {0x03, 0x1b}, /* vref */ - {0x17, 0x1d}, /* hstart */ - {0x18, 0xbd}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x81}, /* vstop */ - {0x32, 0xff}, /* href */ - {0xc0, 0xe2}, -}; - -static const u8 ov965x_start_1_xga[][2] = { - {0x12, 0x02}, /* com7 */ - {0x36, 0xf8}, /* aref3 */ - {0x69, 0x02}, /* hv */ - {0x8c, 0x89}, /* com22 */ - {0x14, 0x28}, /* com9 */ - {0x3e, 0x0c}, /* com14 */ - {0x41, 0x40}, /* com16 */ - {0x72, 0x00}, - {0x73, 0x01}, - {0x74, 0x3a}, - {0x75, 0x35}, - {0x76, 0x01}, - {0xc7, 0x80}, /* com24 */ - {0x03, 0x1b}, /* vref */ - {0x17, 0x1d}, /* hstart */ - {0x18, 0xbd}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x81}, /* vstop */ - {0x32, 0xff}, /* href */ - {0xc0, 0xe2}, -}; - -static const u8 ov965x_start_1_sxga[][2] = { - {0x12, 0x02}, /* com7 */ - {0x36, 0xf8}, /* aref3 */ - {0x69, 0x02}, /* hv */ - {0x8c, 0x89}, /* com22 */ - {0x14, 0x28}, /* com9 */ - {0x3e, 0x0c}, /* com14 */ - {0x41, 0x40}, /* com16 */ - {0x72, 0x00}, - {0x73, 0x01}, - {0x74, 0x3a}, - {0x75, 0x35}, - {0x76, 0x01}, - {0xc7, 0x80}, /* com24 */ - {0x03, 0x1b}, /* vref */ - {0x17, 0x1d}, /* hstart */ - {0x18, 0x02}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x81}, /* vstop */ - {0x32, 0xff}, /* href */ - {0xc0, 0xe2}, -}; - -static const u8 bridge_start_qvga[][2] = { - {0x94, 0xaa}, - {0xf1, 0x60}, - {0xe5, 0x04}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {0x8c, 0x00}, - {0x8d, 0x1c}, - {0x34, 0x05}, - - {0xc2, 0x4c}, - {0xc3, 0xf9}, - {0xda, 0x00}, - {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x78}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x57, 0x00}, - {0x5c, 0x00}, - {0x5a, 0x50}, - {0x5b, 0x3c}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x94, 0x11}, -}; - -static const u8 bridge_start_vga[][2] = { - {0x94, 0xaa}, - {0xf1, 0x60}, - {0xe5, 0x04}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {0x8c, 0x00}, - {0x8d, 0x1c}, - {0x34, 0x05}, - {0xc2, 0x0c}, - {0xc3, 0xf9}, - {0xda, 0x01}, - {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x3c}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x57, 0x00}, - {0x5c, 0x00}, - {0x5a, 0xa0}, - {0x5b, 0x78}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x94, 0x11}, -}; - -static const u8 bridge_start_svga[][2] = { - {0x94, 0xaa}, - {0xf1, 0x60}, - {0xe5, 0x04}, - {0xc0, 0xa0}, - {0xc1, 0x80}, - {0x8c, 0x00}, - {0x8d, 0x1c}, - {0x34, 0x05}, - {0xc2, 0x4c}, - {0xc3, 0xf9}, - {0x50, 0x00}, - {0x51, 0x40}, - {0x52, 0x00}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x88}, - {0x57, 0x00}, - {0x5c, 0x00}, - {0x5a, 0xc8}, - {0x5b, 0x96}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0xda, 0x00}, - {0x94, 0x11}, -}; - -static const u8 bridge_start_xga[][2] = { - {0x94, 0xaa}, - {0xf1, 0x60}, - {0xe5, 0x04}, - {0xc0, 0xa0}, - {0xc1, 0x80}, - {0x8c, 0x00}, - {0x8d, 0x1c}, - {0x34, 0x05}, - {0xc2, 0x4c}, - {0xc3, 0xf9}, - {0x50, 0x00}, - {0x51, 0x40}, - {0x52, 0x00}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x88}, - {0x57, 0x00}, - {0x5c, 0x01}, - {0x5a, 0x00}, - {0x5b, 0xc0}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0xda, 0x01}, - {0x94, 0x11}, -}; - -static const u8 bridge_start_sxga[][2] = { - {0x94, 0xaa}, - {0xf1, 0x60}, - {0xe5, 0x04}, - {0xc0, 0xa0}, - {0xc1, 0x80}, - {0x8c, 0x00}, - {0x8d, 0x1c}, - {0x34, 0x05}, - {0xc2, 0x0c}, - {0xc3, 0xf9}, - {0xda, 0x00}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x94, 0x11}, -}; - -static const u8 ov965x_start_2_qvga[][2] = { - {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ - {0x1e, 0x04}, /* mvfp */ - {0x13, 0xe0}, /* com8 */ - {0x00, 0x00}, - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x11, 0x01}, /* clkrc */ - {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x02}, /* 50 Hz banding filter */ - {0xc5, 0x03}, /* 60 Hz banding filter */ - {0xa2, 0x96}, /* bd50 */ - {0xa3, 0x7d}, /* bd60 */ - - {0xff, 0x13}, /* read 13, write ff 00 */ - {0x13, 0xe7}, - {0x3a, 0x80}, /* tslb - yuyv */ -}; - -static const u8 ov965x_start_2_vga[][2] = { - {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ - {0x1e, 0x04}, /* mvfp */ - {0x13, 0xe0}, /* com8 */ - {0x00, 0x00}, - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x11, 0x03}, /* clkrc */ - {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x05}, /* 50 Hz banding filter */ - {0xc5, 0x07}, /* 60 Hz banding filter */ - {0xa2, 0x4b}, /* bd50 */ - {0xa3, 0x3e}, /* bd60 */ - - {0x2d, 0x00}, /* advfl */ -}; - -static const u8 ov965x_start_2_svga[][2] = { /* same for xga */ - {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ - {0x1e, 0x04}, /* mvfp */ - {0x13, 0xe0}, /* com8 */ - {0x00, 0x00}, - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x11, 0x01}, /* clkrc */ - {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x0c}, /* 50 Hz banding filter */ - {0xc5, 0x0f}, /* 60 Hz banding filter */ - {0xa2, 0x4e}, /* bd50 */ - {0xa3, 0x41}, /* bd60 */ -}; - -static const u8 ov965x_start_2_sxga[][2] = { - {0x13, 0xe0}, /* com8 */ - {0x00, 0x00}, - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ - {0x1e, 0x04}, /* mvfp */ - {0x11, 0x01}, /* clkrc */ - {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x0c}, /* 50 Hz banding filter */ - {0xc5, 0x0f}, /* 60 Hz banding filter */ - {0xa2, 0x4e}, /* bd50 */ - {0xa3, 0x41}, /* bd60 */ -}; - -static const u8 ov562x_init[][2] = { - {0x88, 0x20}, - {0x89, 0x0a}, - {0x8a, 0x90}, - {0x8b, 0x06}, - {0x8c, 0x01}, - {0x8d, 0x10}, - {0x1c, 0x00}, - {0x1d, 0x48}, - {0x1d, 0x00}, - {0x1d, 0xff}, - {0x1c, 0x0a}, - {0x1d, 0x2e}, - {0x1d, 0x1e}, -}; - -static const u8 ov562x_init_2[][2] = { - {0x12, 0x80}, - {0x11, 0x41}, - {0x13, 0x00}, - {0x10, 0x1e}, - {0x3b, 0x07}, - {0x5b, 0x40}, - {0x39, 0x07}, - {0x53, 0x02}, - {0x54, 0x60}, - {0x04, 0x20}, - {0x27, 0x04}, - {0x3d, 0x40}, - {0x36, 0x00}, - {0xc5, 0x04}, - {0x4e, 0x00}, - {0x4f, 0x93}, - {0x50, 0x7b}, - {0xca, 0x0c}, - {0xcb, 0x0f}, - {0x39, 0x07}, - {0x4a, 0x10}, - {0x3e, 0x0a}, - {0x3d, 0x00}, - {0x0c, 0x38}, - {0x38, 0x90}, - {0x46, 0x30}, - {0x4f, 0x93}, - {0x50, 0x7b}, - {0xab, 0x00}, - {0xca, 0x0c}, - {0xcb, 0x0f}, - {0x37, 0x02}, - {0x44, 0x48}, - {0x8d, 0x44}, - {0x2a, 0x00}, - {0x2b, 0x00}, - {0x32, 0x00}, - {0x38, 0x90}, - {0x53, 0x02}, - {0x54, 0x60}, - {0x12, 0x00}, - {0x17, 0x12}, - {0x18, 0xb4}, - {0x19, 0x0c}, - {0x1a, 0xf4}, - {0x03, 0x4a}, - {0x89, 0x20}, - {0x83, 0x80}, - {0xb7, 0x9d}, - {0xb6, 0x11}, - {0xb5, 0x55}, - {0xb4, 0x00}, - {0xa9, 0xf0}, - {0xa8, 0x0a}, - {0xb8, 0xf0}, - {0xb9, 0xf0}, - {0xba, 0xf0}, - {0x81, 0x07}, - {0x63, 0x44}, - {0x13, 0xc7}, - {0x14, 0x60}, - {0x33, 0x75}, - {0x2c, 0x00}, - {0x09, 0x00}, - {0x35, 0x30}, - {0x27, 0x04}, - {0x3c, 0x07}, - {0x3a, 0x0a}, - {0x3b, 0x07}, - {0x01, 0x40}, - {0x02, 0x40}, - {0x16, 0x40}, - {0x52, 0xb0}, - {0x51, 0x83}, - {0x21, 0xbb}, - {0x22, 0x10}, - {0x23, 0x03}, - {0x35, 0x38}, - {0x20, 0x90}, - {0x28, 0x30}, - {0x73, 0xe1}, - {0x6c, 0x00}, - {0x6d, 0x80}, - {0x6e, 0x00}, - {0x70, 0x04}, - {0x71, 0x00}, - {0x8d, 0x04}, - {0x64, 0x00}, - {0x65, 0x00}, - {0x66, 0x00}, - {0x67, 0x00}, - {0x68, 0x00}, - {0x69, 0x00}, - {0x6a, 0x00}, - {0x6b, 0x00}, - {0x71, 0x94}, - {0x74, 0x20}, - {0x80, 0x09}, - {0x85, 0xc0}, -}; - -static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - gspca_dev->usb_buf[0] = val; - ret = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - if (ret < 0) { - pr_err("reg_w failed %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val) -{ - PDEBUG(D_USBO, "reg_w [%04x] = %02x", reg, val); - reg_w_i(gspca_dev, reg, val); -} - -static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return 0; - ret = usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - 0x01, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - } - return gspca_dev->usb_buf[0]; -} - -static int sccb_check_status(struct gspca_dev *gspca_dev) -{ - u8 data; - int i; - - for (i = 0; i < 5; i++) { - msleep(10); - data = reg_r(gspca_dev, OV534_REG_STATUS); - - switch (data) { - case 0x00: - return 1; - case 0x04: - return 0; - case 0x03: - break; - default: - PDEBUG(D_USBI|D_USBO, - "sccb status 0x%02x, attempt %d/5", - data, i + 1); - } - } - return 0; -} - -static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) -{ - PDEBUG(D_USBO, "sccb_write [%02x] = %02x", reg, val); - reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg); - reg_w_i(gspca_dev, OV534_REG_WRITE, val); - reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); - - if (!sccb_check_status(gspca_dev)) - pr_err("sccb_write failed\n"); -} - -static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg) -{ - reg_w(gspca_dev, OV534_REG_SUBADDR, reg); - reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); - if (!sccb_check_status(gspca_dev)) - pr_err("sccb_read failed 1\n"); - - reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); - if (!sccb_check_status(gspca_dev)) - pr_err("sccb_read failed 2\n"); - - return reg_r(gspca_dev, OV534_REG_READ); -} - -/* output a bridge sequence (reg - val) */ -static void reg_w_array(struct gspca_dev *gspca_dev, - const u8 (*data)[2], int len) -{ - while (--len >= 0) { - reg_w(gspca_dev, (*data)[0], (*data)[1]); - data++; - } -} - -/* output a sensor sequence (reg - val) */ -static void sccb_w_array(struct gspca_dev *gspca_dev, - const u8 (*data)[2], int len) -{ - while (--len >= 0) { - if ((*data)[0] != 0xff) { - sccb_write(gspca_dev, (*data)[0], (*data)[1]); - } else { - sccb_read(gspca_dev, (*data)[1]); - sccb_write(gspca_dev, 0xff, 0x00); - } - data++; - } -} - -/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. - * (direction and output)? */ -static void set_led(struct gspca_dev *gspca_dev, int status) -{ - u8 data; - - PDEBUG(D_CONF, "led status: %d", status); - - data = reg_r(gspca_dev, 0x21); - data |= 0x80; - reg_w(gspca_dev, 0x21, data); - - data = reg_r(gspca_dev, 0x23); - if (status) - data |= 0x80; - else - data &= ~0x80; - - reg_w(gspca_dev, 0x23, data); - - if (!status) { - data = reg_r(gspca_dev, 0x21); - data &= ~0x80; - reg_w(gspca_dev, 0x21, data); - } -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 val; - s8 sval; - - if (sd->sensor == SENSOR_OV562x) { - sval = brightness; - val = 0x76; - val += sval; - sccb_write(gspca_dev, 0x24, val); - val = 0x6a; - val += sval; - sccb_write(gspca_dev, 0x25, val); - if (sval < -40) - val = 0x71; - else if (sval < 20) - val = 0x94; - else - val = 0xe6; - sccb_write(gspca_dev, 0x26, val); - } else { - val = brightness; - if (val < 8) - val = 15 - val; /* f .. 8 */ - else - val = val - 8; /* 0 .. 7 */ - sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ - 0x0f | (val << 4)); - } -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */ - val << 4); -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 autogain) -{ - u8 val; - -/*fixme: should adjust agc/awb/aec by different controls */ - val = sccb_read(gspca_dev, 0x13); /* com8 */ - sccb_write(gspca_dev, 0xff, 0x00); - if (autogain) - val |= 0x05; /* agc & aec */ - else - val &= 0xfa; - sccb_write(gspca_dev, 0x13, val); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 exposure) -{ - static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; - u8 val; - - sccb_write(gspca_dev, 0x10, expo[exposure]); /* aec[9:2] */ - - val = sccb_read(gspca_dev, 0x13); /* com8 */ - sccb_write(gspca_dev, 0xff, 0x00); - sccb_write(gspca_dev, 0x13, val); - - val = sccb_read(gspca_dev, 0xa1); /* aech */ - sccb_write(gspca_dev, 0xff, 0x00); - sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */ -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - if (val < 0) { /* auto */ - val = sccb_read(gspca_dev, 0x42); /* com17 */ - sccb_write(gspca_dev, 0xff, 0x00); - sccb_write(gspca_dev, 0x42, val | 0x40); - /* Edge enhancement strength auto adjust */ - return; - } - if (val != 0) - val = 1 << (val - 1); - sccb_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */ - val); - val = sccb_read(gspca_dev, 0x42); /* com17 */ - sccb_write(gspca_dev, 0xff, 0x00); - sccb_write(gspca_dev, 0x42, val & 0xbf); -} - -static void setsatur(struct gspca_dev *gspca_dev, s32 val) -{ - u8 val1, val2, val3; - static const u8 matrix[5][2] = { - {0x14, 0x38}, - {0x1e, 0x54}, - {0x28, 0x70}, - {0x32, 0x8c}, - {0x48, 0x90} - }; - - val1 = matrix[val][0]; - val2 = matrix[val][1]; - val3 = val1 + val2; - sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */ - sccb_write(gspca_dev, 0x50, val3); - sccb_write(gspca_dev, 0x51, 0x00); - sccb_write(gspca_dev, 0x52, val1); - sccb_write(gspca_dev, 0x53, val2); - sccb_write(gspca_dev, 0x54, val3); - sccb_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */ - - val1 = sccb_read(gspca_dev, 0x41); /* com16 */ - sccb_write(gspca_dev, 0xff, 0x00); - sccb_write(gspca_dev, 0x41, val1); -} - -static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq) -{ - u8 val; - - val = sccb_read(gspca_dev, 0x13); /* com8 */ - sccb_write(gspca_dev, 0xff, 0x00); - if (freq == 0) { - sccb_write(gspca_dev, 0x13, val & 0xdf); - return; - } - sccb_write(gspca_dev, 0x13, val | 0x20); - - val = sccb_read(gspca_dev, 0x42); /* com17 */ - sccb_write(gspca_dev, 0xff, 0x00); - if (freq == 1) - val |= 0x01; - else - val &= 0xfe; - sccb_write(gspca_dev, 0x42, val); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 sensor_id; - - /* reset bridge */ - reg_w(gspca_dev, 0xe7, 0x3a); - reg_w(gspca_dev, 0xe0, 0x08); - msleep(100); - - /* initialize the sensor address */ - reg_w(gspca_dev, OV534_REG_ADDRESS, 0x60); - - /* reset sensor */ - sccb_write(gspca_dev, 0x12, 0x80); - msleep(10); - - /* probe the sensor */ - sccb_read(gspca_dev, 0x0a); - sensor_id = sccb_read(gspca_dev, 0x0a) << 8; - sccb_read(gspca_dev, 0x0b); - sensor_id |= sccb_read(gspca_dev, 0x0b); - PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); - - /* initialize */ - if ((sensor_id & 0xfff0) == 0x9650) { - sd->sensor = SENSOR_OV965x; - - gspca_dev->cam.cam_mode = ov965x_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode); - - reg_w_array(gspca_dev, bridge_init, - ARRAY_SIZE(bridge_init)); - sccb_w_array(gspca_dev, ov965x_init, - ARRAY_SIZE(ov965x_init)); - reg_w_array(gspca_dev, bridge_init_2, - ARRAY_SIZE(bridge_init_2)); - sccb_w_array(gspca_dev, ov965x_init_2, - ARRAY_SIZE(ov965x_init_2)); - reg_w(gspca_dev, 0xe0, 0x00); - reg_w(gspca_dev, 0xe0, 0x01); - set_led(gspca_dev, 0); - reg_w(gspca_dev, 0xe0, 0x00); - } else if ((sensor_id & 0xfff0) == 0x9710) { - const char *p; - int l; - - sd->sensor = SENSOR_OV971x; - - gspca_dev->cam.cam_mode = ov971x_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode); - - gspca_dev->cam.bulk = 1; - gspca_dev->cam.bulk_size = 16384; - gspca_dev->cam.bulk_nurbs = 2; - - sccb_w_array(gspca_dev, ov971x_init, - ARRAY_SIZE(ov971x_init)); - - /* set video format on bridge processor */ - /* access bridge processor's video format registers at: 0x00 */ - reg_w(gspca_dev, 0x1c, 0x00); - /*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/ - reg_w(gspca_dev, 0x1d, 0x00); - - /* Will W. specific stuff - * set VSYNC to - * output (0x1f) if first webcam - * input (0x17) if 2nd or 3rd webcam */ - p = video_device_node_name(&gspca_dev->vdev); - l = strlen(p) - 1; - if (p[l] == '0') - reg_w(gspca_dev, 0x56, 0x1f); - else - reg_w(gspca_dev, 0x56, 0x17); - } else if ((sensor_id & 0xfff0) == 0x5620) { - sd->sensor = SENSOR_OV562x; - gspca_dev->cam.cam_mode = ov562x_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode); - - reg_w_array(gspca_dev, ov562x_init, - ARRAY_SIZE(ov562x_init)); - sccb_w_array(gspca_dev, ov562x_init_2, - ARRAY_SIZE(ov562x_init_2)); - reg_w(gspca_dev, 0xe0, 0x00); - } else { - pr_err("Unknown sensor %04x", sensor_id); - return -EINVAL; - } - - return gspca_dev->usb_err; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV971x) - return gspca_dev->usb_err; - if (sd->sensor == SENSOR_OV562x) - return gspca_dev->usb_err; - - switch (gspca_dev->curr_mode) { - case QVGA_MODE: /* 320x240 */ - sccb_w_array(gspca_dev, ov965x_start_1_vga, - ARRAY_SIZE(ov965x_start_1_vga)); - reg_w_array(gspca_dev, bridge_start_qvga, - ARRAY_SIZE(bridge_start_qvga)); - sccb_w_array(gspca_dev, ov965x_start_2_qvga, - ARRAY_SIZE(ov965x_start_2_qvga)); - break; - case VGA_MODE: /* 640x480 */ - sccb_w_array(gspca_dev, ov965x_start_1_vga, - ARRAY_SIZE(ov965x_start_1_vga)); - reg_w_array(gspca_dev, bridge_start_vga, - ARRAY_SIZE(bridge_start_vga)); - sccb_w_array(gspca_dev, ov965x_start_2_vga, - ARRAY_SIZE(ov965x_start_2_vga)); - break; - case SVGA_MODE: /* 800x600 */ - sccb_w_array(gspca_dev, ov965x_start_1_svga, - ARRAY_SIZE(ov965x_start_1_svga)); - reg_w_array(gspca_dev, bridge_start_svga, - ARRAY_SIZE(bridge_start_svga)); - sccb_w_array(gspca_dev, ov965x_start_2_svga, - ARRAY_SIZE(ov965x_start_2_svga)); - break; - case XGA_MODE: /* 1024x768 */ - sccb_w_array(gspca_dev, ov965x_start_1_xga, - ARRAY_SIZE(ov965x_start_1_xga)); - reg_w_array(gspca_dev, bridge_start_xga, - ARRAY_SIZE(bridge_start_xga)); - sccb_w_array(gspca_dev, ov965x_start_2_svga, - ARRAY_SIZE(ov965x_start_2_svga)); - break; - default: -/* case SXGA_MODE: * 1280x1024 */ - sccb_w_array(gspca_dev, ov965x_start_1_sxga, - ARRAY_SIZE(ov965x_start_1_sxga)); - reg_w_array(gspca_dev, bridge_start_sxga, - ARRAY_SIZE(bridge_start_sxga)); - sccb_w_array(gspca_dev, ov965x_start_2_sxga, - ARRAY_SIZE(ov965x_start_2_sxga)); - break; - } - - reg_w(gspca_dev, 0xe0, 0x00); - reg_w(gspca_dev, 0xe0, 0x00); - set_led(gspca_dev, 1); - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 0xe0, 0x01); - set_led(gspca_dev, 0); - reg_w(gspca_dev, 0xe0, 0x00); -} - -/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ -#define UVC_STREAM_EOH (1 << 7) -#define UVC_STREAM_ERR (1 << 6) -#define UVC_STREAM_STI (1 << 5) -#define UVC_STREAM_RES (1 << 4) -#define UVC_STREAM_SCR (1 << 3) -#define UVC_STREAM_PTS (1 << 2) -#define UVC_STREAM_EOF (1 << 1) -#define UVC_STREAM_FID (1 << 0) - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u32 this_pts; - u8 this_fid; - int remaining_len = len; - int payload_len; - - payload_len = gspca_dev->cam.bulk ? 2048 : 2040; - do { - len = min(remaining_len, payload_len); - - /* Payloads are prefixed with a UVC-style header. We - consider a frame to start when the FID toggles, or the PTS - changes. A frame ends when EOF is set, and we've received - the correct number of bytes. */ - - /* Verify UVC header. Header length is always 12 */ - if (data[0] != 12 || len < 12) { - PDEBUG(D_PACK, "bad header"); - goto discard; - } - - /* Check errors */ - if (data[1] & UVC_STREAM_ERR) { - PDEBUG(D_PACK, "payload error"); - goto discard; - } - - /* Extract PTS and FID */ - if (!(data[1] & UVC_STREAM_PTS)) { - PDEBUG(D_PACK, "PTS not present"); - goto discard; - } - this_pts = (data[5] << 24) | (data[4] << 16) - | (data[3] << 8) | data[2]; - this_fid = data[1] & UVC_STREAM_FID; - - /* If PTS or FID has changed, start a new frame. */ - if (this_pts != sd->last_pts || this_fid != sd->last_fid) { - if (gspca_dev->last_packet_type == INTER_PACKET) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - sd->last_pts = this_pts; - sd->last_fid = this_fid; - gspca_frame_add(gspca_dev, FIRST_PACKET, - data + 12, len - 12); - /* If this packet is marked as EOF, end the frame */ - } else if (data[1] & UVC_STREAM_EOF) { - sd->last_pts = 0; - gspca_frame_add(gspca_dev, LAST_PACKET, - data + 12, len - 12); - } else { - - /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, - data + 12, len - 12); - } - - /* Done this payload */ - goto scan_next; - -discard: - /* Discard data until a new frame starts. */ - gspca_dev->last_packet_type = DISCARD_PACKET; - -scan_next: - remaining_len -= len; - data += len; - } while (remaining_len > 0); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setsatur(gspca_dev, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setlightfreq(gspca_dev, ctrl->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - if (ctrl->is_new) - setautogain(gspca_dev, ctrl->val); - if (!ctrl->val && gspca_dev->exposure->is_new) - setexposure(gspca_dev, gspca_dev->exposure->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - if (sd->sensor == SENSOR_OV971x) - return 0; - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 7); - if (sd->sensor == SENSOR_OV562x) { - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, -90, 90, 1, 0); - } else { - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 15, 1, 7); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 15, 1, 3); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 4, 1, 2); - /* -1 = auto */ - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, -1, 4, 1, -1); - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 3, 1, 0); - v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - } - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x05a9, 0x8065)}, - {USB_DEVICE(0x06f8, 0x3003)}, - {USB_DEVICE(0x05a9, 0x1550)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c deleted file mode 100644 index d236d1791f78..000000000000 --- a/drivers/media/video/gspca/pac207.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Pixart PAC207BCA library - * - * Copyright (C) 2008 Hans de Goede - * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li - * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "pac207" - -#include -#include "gspca.h" -/* Include pac common sof detection functions */ -#include "pac_common.h" - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("Pixart PAC207"); -MODULE_LICENSE("GPL"); - -#define PAC207_CTRL_TIMEOUT 100 /* ms */ - -#define PAC207_BRIGHTNESS_MIN 0 -#define PAC207_BRIGHTNESS_MAX 255 -#define PAC207_BRIGHTNESS_DEFAULT 46 -#define PAC207_BRIGHTNESS_REG 0x08 - -#define PAC207_EXPOSURE_MIN 3 -#define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */ -#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ -#define PAC207_EXPOSURE_REG 0x02 - -#define PAC207_GAIN_MIN 0 -#define PAC207_GAIN_MAX 31 -#define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */ -#define PAC207_GAIN_REG 0x0e - -#define PAC207_AUTOGAIN_DEADZONE 30 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct v4l2_ctrl *brightness; - - u8 mode; - u8 sof_read; - u8 header_read; - u8 autogain_ignore_frames; - - atomic_t avg_lum; -}; - -static const struct v4l2_pix_format sif_mode[] = { - {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = (176 + 2) * 144, - /* uncompressed, add 2 bytes / line for line header */ - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, - .bytesperline = 352, - /* compressed, but only when needed (not compressed - when the framerate is low) */ - .sizeimage = (352 + 2) * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -static const __u8 pac207_sensor_init[][8] = { - {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84}, - {0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, - {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00}, - {0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00}, -}; - -static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, - const u8 *buffer, u16 length) -{ - struct usb_device *udev = gspca_dev->dev; - int err; - - if (gspca_dev->usb_err < 0) - return; - - memcpy(gspca_dev->usb_buf, buffer, length); - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, index, - gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); - if (err < 0) { - pr_err("Failed to write registers to index 0x%04X, error %d\n", - index, err); - gspca_dev->usb_err = err; - } -} - -static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) -{ - struct usb_device *udev = gspca_dev->dev; - int err; - - if (gspca_dev->usb_err < 0) - return; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, index, NULL, 0, PAC207_CTRL_TIMEOUT); - if (err) { - pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", - index, value, err); - gspca_dev->usb_err = err; - } -} - -static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) -{ - struct usb_device *udev = gspca_dev->dev; - int res; - - if (gspca_dev->usb_err < 0) - return 0; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, index, - gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT); - if (res < 0) { - pr_err("Failed to read a register (index 0x%04X, error %d)\n", - index, res); - gspca_dev->usb_err = res; - return 0; - } - - return gspca_dev->usb_buf[0]; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam; - u8 idreg[2]; - - idreg[0] = pac207_read_reg(gspca_dev, 0x0000); - idreg[1] = pac207_read_reg(gspca_dev, 0x0001); - idreg[0] = ((idreg[0] & 0x0f) << 4) | ((idreg[1] & 0xf0) >> 4); - idreg[1] = idreg[1] & 0x0f; - PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X", - idreg[0], idreg[1]); - - if (idreg[0] != 0x27) { - PDEBUG(D_PROBE, "Error invalid sensor ID!"); - return -ENODEV; - } - - PDEBUG(D_PROBE, - "Pixart PAC207BCA Image Processor and Control Chip detected" - " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - - cam = &gspca_dev->cam; - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - pac207_write_reg(gspca_dev, 0x41, 0x00); - /* Bit_0=Image Format, - * Bit_1=LED, - * Bit_2=Compression test mode enable */ - pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ - - return gspca_dev->usb_err; -} - -static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val) -{ - pac207_write_reg(gspca_dev, reg, val); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - gspca_dev->exposure->val = PAC207_EXPOSURE_DEFAULT; - gspca_dev->gain->val = PAC207_GAIN_DEFAULT; - sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; - } - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) - setcontrol(gspca_dev, PAC207_EXPOSURE_REG, - gspca_dev->exposure->val); - if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) - setcontrol(gspca_dev, PAC207_GAIN_REG, - gspca_dev->gain->val); - break; - default: - return -EINVAL; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -/* this function is called at probe time */ -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - - sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, - PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX, - 1, PAC207_BRIGHTNESS_DEFAULT); - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, - PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX, - 1, PAC207_EXPOSURE_DEFAULT); - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, - PAC207_GAIN_MIN, PAC207_GAIN_MAX, - 1, PAC207_GAIN_DEFAULT); - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 mode; - - pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */ - pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8); - pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8); - pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8); - pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8); - - /* Compression Balance */ - if (gspca_dev->width == 176) - pac207_write_reg(gspca_dev, 0x4a, 0xff); - else - pac207_write_reg(gspca_dev, 0x4a, 0x30); - pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ - pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness)); - - /* PGA global gain (Bit 4-0) */ - pac207_write_reg(gspca_dev, 0x0e, - v4l2_ctrl_g_ctrl(gspca_dev->gain)); - pac207_write_reg(gspca_dev, 0x02, - v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ - - mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ - if (gspca_dev->width == 176) { /* 176x144 */ - mode |= 0x01; - PDEBUG(D_STREAM, "pac207_start mode 176x144"); - } else { /* 352x288 */ - PDEBUG(D_STREAM, "pac207_start mode 352x288"); - } - pac207_write_reg(gspca_dev, 0x41, mode); - - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ - msleep(10); - pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */ - - sd->sof_read = 0; - sd->autogain_ignore_frames = 0; - atomic_set(&sd->avg_lum, -1); - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ - pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ - pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ -} - - -static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int avg_lum = atomic_read(&sd->avg_lum); - - if (avg_lum == -1) - return; - - if (sd->autogain_ignore_frames > 0) - sd->autogain_ignore_frames--; - else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, - 90, PAC207_AUTOGAIN_DEADZONE)) - sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, - int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - unsigned char *sof; - - sof = pac_find_sof(&sd->sof_read, data, len); - if (sof) { - int n; - - /* finish decoding current frame */ - n = sof - data; - if (n > sizeof pac_sof_marker) - n -= sizeof pac_sof_marker; - else - n = 0; - gspca_frame_add(gspca_dev, LAST_PACKET, - data, n); - sd->header_read = 0; - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - len -= sof - data; - data = sof; - } - if (sd->header_read < 11) { - int needed; - - /* get average lumination from frame header (byte 5) */ - if (sd->header_read < 5) { - needed = 5 - sd->header_read; - if (len >= needed) - atomic_set(&sd->avg_lum, data[needed - 1]); - } - /* skip the rest of the header */ - needed = 11 - sd->header_read; - if (len <= needed) { - sd->header_read += len; - return; - } - data += needed; - len -= needed; - sd->header_read = 11; - } - - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet data */ - int len) /* interrput packet length */ -{ - int ret = -EINVAL; - - if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - ret = 0; - } - - return ret; -} -#endif - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .dq_callback = pac207_do_auto_gain, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x4028)}, - {USB_DEVICE(0x093a, 0x2460)}, - {USB_DEVICE(0x093a, 0x2461)}, - {USB_DEVICE(0x093a, 0x2463)}, - {USB_DEVICE(0x093a, 0x2464)}, - {USB_DEVICE(0x093a, 0x2468)}, - {USB_DEVICE(0x093a, 0x2470)}, - {USB_DEVICE(0x093a, 0x2471)}, - {USB_DEVICE(0x093a, 0x2472)}, - {USB_DEVICE(0x093a, 0x2474)}, - {USB_DEVICE(0x093a, 0x2476)}, - {USB_DEVICE(0x145f, 0x013a)}, - {USB_DEVICE(0x2001, 0xf115)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c deleted file mode 100644 index 4877f7ab3d59..000000000000 --- a/drivers/media/video/gspca/pac7302.c +++ /dev/null @@ -1,932 +0,0 @@ -/* - * Pixart PAC7302 driver - * - * Copyright (C) 2008-2012 Jean-Francois Moine - * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li - * - * Separated from Pixart PAC7311 library by Márton Németh - * Camera button input handling by Márton Németh - * Copyright (C) 2009-2010 Márton Németh - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * Some documentation about various registers as determined by trial and error. - * - * Register page 1: - * - * Address Description - * 0x78 Global control, bit 6 controls the LED (inverted) - * 0x80 Compression balance, 2 interesting settings: - * 0x0f Default - * 0x50 Values >= this switch the camera to a lower compression, - * using the same table for both luminance and chrominance. - * This gives a sharper picture. Only usable when running - * at < 15 fps! Note currently the driver does not use this - * as the quality gain is small and the generated JPG-s are - * only understood by v4l-utils >= 0.8.9 - * - * Register page 3: - * - * Address Description - * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on - * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps - * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, - * 63 -> ~27 fps, the 2 msb's must always be 1 !! - * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: - * 1 -> ~30 fps, 2 -> ~20 fps - * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time - * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all - * 0x10 Gain 0-31 - * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an - * amplification value of 1 rather then 0 at its lowest setting - * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - * 0x80 Another framerate control, best left at 1, moving it from 1 to - * 2 causes the framerate to become 3/4th of what it was, and - * also seems to cause pixel averaging, resulting in an effective - * resolution of 320x240 and thus a much blockier image - * - * The registers are accessed in the following functions: - * - * Page | Register | Function - * -----+------------+--------------------------------------------------- - * 0 | 0x0f..0x20 | setcolors() - * 0 | 0xa2..0xab | setbrightcont() - * 0 | 0xc5 | setredbalance() - * 0 | 0xc6 | setwhitebalance() - * 0 | 0xc7 | setbluebalance() - * 0 | 0xdc | setbrightcont(), setcolors() - * 3 | 0x02 | setexposure() - * 3 | 0x10, 0x12 | setgain() - * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() - * 3 | 0x21 | sethvflip() - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include "gspca.h" -/* Include pac common sof detection functions */ -#include "pac_common.h" - -#define PAC7302_GAIN_DEFAULT 15 -#define PAC7302_GAIN_KNEE 42 -#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */ -#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */ - -MODULE_AUTHOR("Jean-Francois Moine , " - "Thomas Kaiser thomas@kaiser-linux.li"); -MODULE_DESCRIPTION("Pixart PAC7302"); -MODULE_LICENSE("GPL"); - -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct { /* brightness / contrast cluster */ - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - }; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *white_balance; - struct v4l2_ctrl *red_balance; - struct v4l2_ctrl *blue_balance; - struct { /* flip cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; - u8 flags; -#define FL_HFLIP 0x01 /* mirrored by default */ -#define FL_VFLIP 0x02 /* vertical flipped by default */ - - u8 sof_read; - s8 autogain_ignore_frames; - - atomic_t avg_lum; -}; - -static const struct v4l2_pix_format vga_mode[] = { - {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; - -#define LOAD_PAGE3 255 -#define END_OF_SEQUENCE 0 - -static const u8 init_7302[] = { -/* index,value */ - 0xff, 0x01, /* page 1 */ - 0x78, 0x00, /* deactivate */ - 0xff, 0x01, - 0x78, 0x40, /* led off */ -}; -static const u8 start_7302[] = { -/* index, len, [value]* */ - 0xff, 1, 0x00, /* page 0 */ - 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, - 0x00, 0x00, 0x00, 0x00, - 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, - 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, - 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, - 0x26, 2, 0xaa, 0xaa, - 0x2e, 1, 0x31, - 0x38, 1, 0x01, - 0x3a, 3, 0x14, 0xff, 0x5a, - 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, - 0x00, 0x54, 0x11, - 0x55, 1, 0x00, - 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, - 0x6b, 1, 0x00, - 0x6e, 3, 0x08, 0x06, 0x00, - 0x72, 3, 0x00, 0xff, 0x00, - 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, - 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, - 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, - 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, - 0xd2, 0xeb, - 0xaf, 1, 0x02, - 0xb5, 2, 0x08, 0x08, - 0xb8, 2, 0x08, 0x88, - 0xc4, 4, 0xae, 0x01, 0x04, 0x01, - 0xcc, 1, 0x00, - 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, - 0xc1, 0xd7, 0xec, - 0xdc, 1, 0x01, - 0xff, 1, 0x01, /* page 1 */ - 0x12, 3, 0x02, 0x00, 0x01, - 0x3e, 2, 0x00, 0x00, - 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, - 0x7c, 1, 0x00, - 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, - 0x02, 0x00, - 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, - 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, - 0xd8, 1, 0x01, - 0xdb, 2, 0x00, 0x01, - 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, - 0xe6, 4, 0x00, 0x00, 0x00, 0x01, - 0xeb, 1, 0x00, - 0xff, 1, 0x02, /* page 2 */ - 0x22, 1, 0x00, - 0xff, 1, 0x03, /* page 3 */ - 0, LOAD_PAGE3, /* load the page 3 */ - 0x11, 1, 0x01, - 0xff, 1, 0x02, /* page 2 */ - 0x13, 1, 0x00, - 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, - 0x27, 2, 0x14, 0x0c, - 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, - 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, - 0x6e, 1, 0x08, - 0xff, 1, 0x01, /* page 1 */ - 0x78, 1, 0x00, - 0, END_OF_SEQUENCE /* end of sequence */ -}; - -#define SKIP 0xaa -/* page 3 - the value SKIP says skip the index - see reg_w_page() */ -static const u8 page3_7302[] = { - 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16, - 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, - 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, - 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, - 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, - SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, - 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, - 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, - 0x00 -}; - -static void reg_w_buf(struct gspca_dev *gspca_dev, - u8 index, - const u8 *buffer, int len) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - memcpy(gspca_dev->usb_buf, buffer, len); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, len, - 500); - if (ret < 0) { - pr_err("reg_w_buf failed i: %02x error %d\n", - index, ret); - gspca_dev->usb_err = ret; - } -} - - -static void reg_w(struct gspca_dev *gspca_dev, - u8 index, - u8 value) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - gspca_dev->usb_buf[0] = value; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_w() failed i: %02x v: %02x error %d\n", - index, value, ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_w_seq(struct gspca_dev *gspca_dev, - const u8 *seq, int len) -{ - while (--len >= 0) { - reg_w(gspca_dev, seq[0], seq[1]); - seq += 2; - } -} - -/* load the beginning of a page */ -static void reg_w_page(struct gspca_dev *gspca_dev, - const u8 *page, int len) -{ - int index; - int ret = 0; - - if (gspca_dev->usb_err < 0) - return; - for (index = 0; index < len; index++) { - if (page[index] == SKIP) /* skip this index */ - continue; - gspca_dev->usb_buf[0] = page[index]; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_w_page() failed i: %02x v: %02x error %d\n", - index, page[index], ret); - gspca_dev->usb_err = ret; - break; - } - } -} - -/* output a variable sequence */ -static void reg_w_var(struct gspca_dev *gspca_dev, - const u8 *seq, - const u8 *page3, unsigned int page3_len) -{ - int index, len; - - for (;;) { - index = *seq++; - len = *seq++; - switch (len) { - case END_OF_SEQUENCE: - return; - case LOAD_PAGE3: - reg_w_page(gspca_dev, page3, page3_len); - break; - default: -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - PDEBUG(D_ERR|D_STREAM, - "Incorrect variable sequence"); - return; - } -#endif - while (len > 0) { - if (len < 8) { - reg_w_buf(gspca_dev, - index, seq, len); - seq += len; - break; - } - reg_w_buf(gspca_dev, index, seq, 8); - seq += 8; - index += 8; - len -= 8; - } - } - } - /* not reached */ -} - -/* this function is called at probe time for pac7302 */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - - cam->cam_mode = vga_mode; /* only 640x480 */ - cam->nmodes = ARRAY_SIZE(vga_mode); - - sd->flags = id->driver_info; - return 0; -} - -static void setbrightcont(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const u8 max[10] = - {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, - 0xd4, 0xec}; - static const u8 delta[10] = - {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, - 0x11, 0x0b}; - - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 10; i++) { - v = max[i]; - v += (sd->brightness->val - sd->brightness->maximum) - * 150 / sd->brightness->maximum; /* 200 ? */ - v -= delta[i] * sd->contrast->val / sd->contrast->maximum; - if (v < 0) - v = 0; - else if (v > 0xff) - v = 0xff; - reg_w(gspca_dev, 0xa2 + i, v); - } - reg_w(gspca_dev, 0xdc, 0x01); -} - -static void setcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const int a[9] = - {217, -212, 0, -101, 170, -67, -38, -315, 355}; - static const int b[9] = - {19, 106, 0, 19, 106, 1, 19, 106, 1}; - - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x11, 0x01); - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 9; i++) { - v = a[i] * sd->saturation->val / sd->saturation->maximum; - v += b[i]; - reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); - reg_w(gspca_dev, 0x0f + 2 * i + 1, v); - } - reg_w(gspca_dev, 0xdc, 0x01); -} - -static void setwhitebalance(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc6, sd->white_balance->val); - - reg_w(gspca_dev, 0xdc, 0x01); -} - -static void setredbalance(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc5, sd->red_balance->val); - - reg_w(gspca_dev, 0xdc, 0x01); -} - -static void setbluebalance(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc7, sd->blue_balance->val); - - reg_w(gspca_dev, 0xdc, 0x01); -} - -static void setgain(struct gspca_dev *gspca_dev) -{ - u8 reg10, reg12; - - if (gspca_dev->gain->val < 32) { - reg10 = gspca_dev->gain->val; - reg12 = 0; - } else { - reg10 = 31; - reg12 = gspca_dev->gain->val - 31; - } - - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, reg10); - reg_w(gspca_dev, 0x12, reg12); - - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); -} - -static void setexposure(struct gspca_dev *gspca_dev) -{ - u8 clockdiv; - u16 exposure; - - /* - * Register 2 of frame 3 contains the clock divider configuring the - * no fps according to the formula: 90 / reg. sd->exposure is the - * desired exposure time in 0.5 ms. - */ - clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000; - - /* - * Note clockdiv = 3 also works, but when running at 30 fps, depending - * on the scene being recorded, the camera switches to another - * quantization table for certain JPEG blocks, and we don't know how - * to decompress these blocks. So we cap the framerate at 15 fps. - */ - if (clockdiv < 6) - clockdiv = 6; - else if (clockdiv > 63) - clockdiv = 63; - - /* - * Register 2 MUST be a multiple of 3, except when between 6 and 12? - * Always round up, otherwise we cannot get the desired frametime - * using the partial frame time exposure control. - */ - if (clockdiv < 6 || clockdiv > 12) - clockdiv = ((clockdiv + 2) / 3) * 3; - - /* - * frame exposure time in ms = 1000 * clockdiv / 90 -> - * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) - */ - exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv); - /* 0 = use full frametime, 448 = no exposure, reverse it */ - exposure = 448 - exposure; - - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x02, clockdiv); - reg_w(gspca_dev, 0x0e, exposure & 0xff); - reg_w(gspca_dev, 0x0f, exposure >> 8); - - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); -} - -static void sethvflip(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 data, hflip, vflip; - - hflip = sd->hflip->val; - if (sd->flags & FL_HFLIP) - hflip = !hflip; - vflip = sd->vflip->val; - if (sd->flags & FL_VFLIP) - vflip = !vflip; - - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00); - reg_w(gspca_dev, 0x21, data); - - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); -} - -/* this function is called at probe and resume time for pac7302 */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2); - return gspca_dev->usb_err; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - gspca_dev->exposure->val = PAC7302_EXPOSURE_DEFAULT; - gspca_dev->gain->val = PAC7302_GAIN_DEFAULT; - sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; - } - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightcont(gspca_dev); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev); - break; - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: - setwhitebalance(gspca_dev); - break; - case V4L2_CID_RED_BALANCE: - setredbalance(gspca_dev); - break; - case V4L2_CID_BLUE_BALANCE: - setbluebalance(gspca_dev); - break; - case V4L2_CID_AUTOGAIN: - if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) - setexposure(gspca_dev); - if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) - setgain(gspca_dev); - break; - case V4L2_CID_HFLIP: - sethvflip(gspca_dev); - break; - default: - return -EINVAL; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -/* this function is called at probe time */ -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 11); - - sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 32, 1, 16); - sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 127); - - sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 127); - sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_WHITE_BALANCE_TEMPERATURE, - 0, 255, 1, 4); - sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 3, 1, 1); - sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 3, 1, 1); - - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 1023, 1, - PAC7302_EXPOSURE_DEFAULT); - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 62, 1, - PAC7302_GAIN_DEFAULT); - - sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - - v4l2_ctrl_cluster(2, &sd->brightness); - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - v4l2_ctrl_cluster(2, &sd->hflip); - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w_var(gspca_dev, start_7302, - page3_7302, sizeof(page3_7302)); - setbrightcont(gspca_dev); - setcolors(gspca_dev); - setwhitebalance(gspca_dev); - setredbalance(gspca_dev); - setbluebalance(gspca_dev); - setexposure(gspca_dev); - setgain(gspca_dev); - sethvflip(gspca_dev); - - sd->sof_read = 0; - sd->autogain_ignore_frames = 0; - atomic_set(&sd->avg_lum, 270 + sd->brightness->val); - - /* start stream */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x01); - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - - /* stop stream */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x00); -} - -/* called on streamoff with alt 0 and on disconnect for pac7302 */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - if (!gspca_dev->present) - return; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x40); -} - -static void do_autogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int avg_lum = atomic_read(&sd->avg_lum); - int desired_lum; - const int deadzone = 30; - - if (sd->autogain_ignore_frames < 0) - return; - - if (sd->autogain_ignore_frames > 0) { - sd->autogain_ignore_frames--; - } else { - desired_lum = 270 + sd->brightness->val; - - if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum, - deadzone, PAC7302_GAIN_KNEE, - PAC7302_EXPOSURE_KNEE)) - sd->autogain_ignore_frames = - PAC_AUTOGAIN_IGNORE_FRAMES; - } -} - -/* JPEG header */ -static const u8 jpeg_header[] = { - 0xff, 0xd8, /* SOI: Start of Image */ - - 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ - 0x00, 0x11, /* length = 17 bytes (including this length field) */ - 0x08, /* Precision: 8 */ - 0x02, 0x80, /* height = 640 (image rotated) */ - 0x01, 0xe0, /* width = 480 */ - 0x03, /* Number of image components: 3 */ - 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ - 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ - 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ - - 0xff, 0xda, /* SOS: Start Of Scan */ - 0x00, 0x0c, /* length = 12 bytes (including this length field) */ - 0x03, /* number of components: 3 */ - 0x01, 0x00, /* selector 1, table 0x00 */ - 0x02, 0x11, /* selector 2, table 0x11 */ - 0x03, 0x11, /* selector 3, table 0x11 */ - 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ - 0x00 /* Successive approximation: 0 */ -}; - -/* this function is run at interrupt level */ -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 *image; - u8 *sof; - - sof = pac_find_sof(&sd->sof_read, data, len); - if (sof) { - int n, lum_offset, footer_length; - - /* - * 6 bytes after the FF D9 EOF marker a number of lumination - * bytes are send corresponding to different parts of the - * image, the 14th and 15th byte after the EOF seem to - * correspond to the center of the image. - */ - lum_offset = 61 + sizeof pac_sof_marker; - footer_length = 74; - - /* Finish decoding current frame */ - n = (sof - data) - (footer_length + sizeof pac_sof_marker); - if (n < 0) { - gspca_dev->image_len += n; - n = 0; - } else { - gspca_frame_add(gspca_dev, INTER_PACKET, data, n); - } - - image = gspca_dev->image; - if (image != NULL - && image[gspca_dev->image_len - 2] == 0xff - && image[gspca_dev->image_len - 1] == 0xd9) - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - - n = sof - data; - len -= n; - data = sof; - - /* Get average lumination */ - if (gspca_dev->last_packet_type == LAST_PACKET && - n >= lum_offset) - atomic_set(&sd->avg_lum, data[-lum_offset] + - data[-lum_offset + 1]); - - /* Start the new frame with the jpeg header */ - /* The PAC7302 has the image rotated 90 degrees */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - jpeg_header, sizeof jpeg_header); - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int sd_dbg_s_register(struct gspca_dev *gspca_dev, - struct v4l2_dbg_register *reg) -{ - u8 index; - u8 value; - - /* - * reg->reg: bit0..15: reserved for register index (wIndex is 16bit - * long on the USB bus) - */ - if (reg->match.type == V4L2_CHIP_MATCH_HOST && - reg->match.addr == 0 && - (reg->reg < 0x000000ff) && - (reg->val <= 0x000000ff) - ) { - /* Currently writing to page 0 is only supported. */ - /* reg_w() only supports 8bit index */ - index = reg->reg; - value = reg->val; - - /* - * Note that there shall be no access to other page - * by any other function between the page switch and - * the actual register write. - */ - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, index, value); - - reg_w(gspca_dev, 0xdc, 0x01); - } - return gspca_dev->usb_err; -} - -static int sd_chip_ident(struct gspca_dev *gspca_dev, - struct v4l2_dbg_chip_ident *chip) -{ - int ret = -EINVAL; - - if (chip->match.type == V4L2_CHIP_MATCH_HOST && - chip->match.addr == 0) { - chip->revision = 0; - chip->ident = V4L2_IDENT_UNKNOWN; - ret = 0; - } - return ret; -} -#endif - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet data */ - int len) /* interrput packet length */ -{ - int ret = -EINVAL; - u8 data0, data1; - - if (len == 2) { - data0 = data[0]; - data1 = data[1]; - if ((data0 == 0x00 && data1 == 0x11) || - (data0 == 0x22 && data1 == 0x33) || - (data0 == 0x44 && data1 == 0x55) || - (data0 == 0x66 && data1 == 0x77) || - (data0 == 0x88 && data1 == 0x99) || - (data0 == 0xaa && data1 == 0xbb) || - (data0 == 0xcc && data1 == 0xdd) || - (data0 == 0xee && data1 == 0xff)) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - ret = 0; - } - } - - return ret; -} -#endif - -/* sub-driver description for pac7302 */ -static const struct sd_desc sd_desc = { - .name = KBUILD_MODNAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = do_autogain, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .set_register = sd_dbg_s_register, - .get_chip_ident = sd_chip_ident, -#endif -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x06f8, 0x3009)}, - {USB_DEVICE(0x06f8, 0x301b)}, - {USB_DEVICE(0x093a, 0x2620)}, - {USB_DEVICE(0x093a, 0x2621)}, - {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, - {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, - {USB_DEVICE(0x093a, 0x2625)}, - {USB_DEVICE(0x093a, 0x2626)}, - {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP}, - {USB_DEVICE(0x093a, 0x2628)}, - {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP}, - {USB_DEVICE(0x093a, 0x262a)}, - {USB_DEVICE(0x093a, 0x262c)}, - {USB_DEVICE(0x145f, 0x013c)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = KBUILD_MODNAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c deleted file mode 100644 index ba3558d3f017..000000000000 --- a/drivers/media/video/gspca/pac7311.c +++ /dev/null @@ -1,701 +0,0 @@ -/* - * Pixart PAC7311 library - * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* Some documentation about various registers as determined by trial and error. - * - * Register page 1: - * - * Address Description - * 0x08 Unknown compressor related, must always be 8 except when not - * in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! - * 0x1b Auto white balance related, bit 0 is AWB enable (inverted) - * bits 345 seem to toggle per color gains on/off (inverted) - * 0x78 Global control, bit 6 controls the LED (inverted) - * 0x80 Compression balance, interesting settings: - * 0x01 Use this to allow the camera to switch to higher compr. - * on the fly. Needed to stay within bandwidth @ 640x480@30 - * 0x1c From usb captures under Windows for 640x480 - * 0x2a Values >= this switch the camera to a lower compression, - * using the same table for both luminance and chrominance. - * This gives a sharper picture. Usable only at 640x480@ < - * 15 fps or 320x240 / 160x120. Note currently the driver - * does not use this as the quality gain is small and the - * generated JPG-s are only understood by v4l-utils >= 0.8.9 - * 0x3f From usb captures under Windows for 320x240 - * 0x69 From usb captures under Windows for 160x120 - * - * Register page 4: - * - * Address Description - * 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on - * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - * 0x0f Master gain 1-245, low value = high gain - * 0x10 Another gain 0-15, limited influence (1-2x gain I guess) - * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - * Note setting vflip disabled leads to a much lower image quality, - * so we always vflip, and tell userspace to flip it back - * 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to - * completely disable the analog amplification block. Set to 0x68 - * for max gain, 0x14 for minimal gain. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "pac7311" - -#include -#include "gspca.h" -/* Include pac common sof detection functions */ -#include "pac_common.h" - -#define PAC7311_GAIN_DEFAULT 122 -#define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */ - -MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); -MODULE_DESCRIPTION("Pixart PAC7311"); -MODULE_LICENSE("GPL"); - -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *hflip; - - u8 sof_read; - u8 autogain_ignore_frames; - - atomic_t avg_lum; -}; - -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -#define LOAD_PAGE4 254 -#define END_OF_SEQUENCE 0 - -static const __u8 init_7311[] = { - 0xff, 0x01, - 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ - 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ - 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */ - 0xff, 0x04, - 0x27, 0x80, - 0x28, 0xca, - 0x29, 0x53, - 0x2a, 0x0e, - 0xff, 0x01, - 0x3e, 0x20, -}; - -static const __u8 start_7311[] = { -/* index, len, [value]* */ - 0xff, 1, 0x01, /* page 1 */ - 0x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00, - 0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c, - 0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e, - 0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49, - 0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48, - 0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78, - 0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b, - 0xd0, 0xff, - 0x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80, - 0x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84, - 0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14, - 0x18, 0x20, - 0x96, 3, 0x01, 0x08, 0x04, - 0xa0, 4, 0x44, 0x44, 0x44, 0x04, - 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, - 0x3f, 0x00, 0x0a, 0x01, 0x00, - 0xff, 1, 0x04, /* page 4 */ - 0, LOAD_PAGE4, /* load the page 4 */ - 0x11, 1, 0x01, - 0, END_OF_SEQUENCE /* end of sequence */ -}; - -#define SKIP 0xaa -/* page 4 - the value SKIP says skip the index - see reg_w_page() */ -static const __u8 page4_7311[] = { - SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, - 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62, - 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP, - SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68, - 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 -}; - -static void reg_w_buf(struct gspca_dev *gspca_dev, - __u8 index, - const u8 *buffer, int len) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - memcpy(gspca_dev->usb_buf, buffer, len); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, len, - 500); - if (ret < 0) { - pr_err("reg_w_buf() failed index 0x%02x, error %d\n", - index, ret); - gspca_dev->usb_err = ret; - } -} - - -static void reg_w(struct gspca_dev *gspca_dev, - __u8 index, - __u8 value) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - gspca_dev->usb_buf[0] = value; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n", - index, value, ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_w_seq(struct gspca_dev *gspca_dev, - const __u8 *seq, int len) -{ - while (--len >= 0) { - reg_w(gspca_dev, seq[0], seq[1]); - seq += 2; - } -} - -/* load the beginning of a page */ -static void reg_w_page(struct gspca_dev *gspca_dev, - const __u8 *page, int len) -{ - int index; - int ret = 0; - - if (gspca_dev->usb_err < 0) - return; - for (index = 0; index < len; index++) { - if (page[index] == SKIP) /* skip this index */ - continue; - gspca_dev->usb_buf[0] = page[index]; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n", - index, page[index], ret); - gspca_dev->usb_err = ret; - break; - } - } -} - -/* output a variable sequence */ -static void reg_w_var(struct gspca_dev *gspca_dev, - const __u8 *seq, - const __u8 *page4, unsigned int page4_len) -{ - int index, len; - - for (;;) { - index = *seq++; - len = *seq++; - switch (len) { - case END_OF_SEQUENCE: - return; - case LOAD_PAGE4: - reg_w_page(gspca_dev, page4, page4_len); - break; - default: - if (len > USB_BUF_SZ) { - PDEBUG(D_ERR|D_STREAM, - "Incorrect variable sequence"); - return; - } - while (len > 0) { - if (len < 8) { - reg_w_buf(gspca_dev, - index, seq, len); - seq += len; - break; - } - reg_w_buf(gspca_dev, index, seq, 8); - seq += 8; - index += 8; - len -= 8; - } - } - } - /* not reached */ -} - -/* this function is called at probe time for pac7311 */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam = &gspca_dev->cam; - - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - cam->input_flags = V4L2_IN_ST_VFLIP; - - return 0; -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, val); - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); -} - -static void setgain(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1); - - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, val); - - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); - - /* - * Page 1 register 8 must always be 0x08 except when not in - * 640x480 mode and page 4 reg 2 <= 3 then it must be 9 - */ - reg_w(gspca_dev, 0xff, 0x01); - if (gspca_dev->width != 640 && val <= 3) - reg_w(gspca_dev, 0x08, 0x09); - else - reg_w(gspca_dev, 0x08, 0x08); - - /* - * Page1 register 80 sets the compression balance, normally we - * want / use 0x1c, but for 640x480@30fps we must allow the - * camera to use higher compression or we may run out of - * bandwidth. - */ - if (gspca_dev->width == 640 && val == 2) - reg_w(gspca_dev, 0x80, 0x01); - else - reg_w(gspca_dev, 0x80, 0x1c); - - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); -} - -static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) -{ - __u8 data; - - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (hflip ? 0x04 : 0x00) | - (vflip ? 0x08 : 0x00); - reg_w(gspca_dev, 0x21, data); - - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); -} - -/* this function is called at probe and resume time for pac7311 */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2); - return gspca_dev->usb_err; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT; - gspca_dev->gain->val = PAC7311_GAIN_DEFAULT; - sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; - } - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) - setexposure(gspca_dev, gspca_dev->exposure->val); - if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) - setgain(gspca_dev, gspca_dev->gain->val); - break; - case V4L2_CID_HFLIP: - sethvflip(gspca_dev, sd->hflip->val, 1); - break; - default: - return -EINVAL; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -/* this function is called at probe time */ -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - - sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 15, 1, 7); - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 2, 63, 1, - PAC7311_EXPOSURE_DEFAULT); - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 244, 1, - PAC7311_GAIN_DEFAULT); - sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->sof_read = 0; - - reg_w_var(gspca_dev, start_7311, - page4_7311, sizeof(page4_7311)); - setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); - setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain)); - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); - sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1); - - /* set correct resolution */ - switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 2: /* 160x120 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x20); - reg_w(gspca_dev, 0x87, 0x10); - break; - case 1: /* 320x240 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x30); - reg_w(gspca_dev, 0x87, 0x11); - break; - case 0: /* 640x480 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x00); - reg_w(gspca_dev, 0x87, 0x12); - break; - } - - sd->sof_read = 0; - sd->autogain_ignore_frames = 0; - atomic_set(&sd->avg_lum, -1); - - /* start stream */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x05); - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ -} - -static void do_autogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int avg_lum = atomic_read(&sd->avg_lum); - int desired_lum, deadzone; - - if (avg_lum == -1) - return; - - desired_lum = 170; - deadzone = 20; - - if (sd->autogain_ignore_frames > 0) - sd->autogain_ignore_frames--; - else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, - desired_lum, deadzone)) - sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; -} - -/* JPEG header, part 1 */ -static const unsigned char pac_jpeg_header1[] = { - 0xff, 0xd8, /* SOI: Start of Image */ - - 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ - 0x00, 0x11, /* length = 17 bytes (including this length field) */ - 0x08 /* Precision: 8 */ - /* 2 bytes is placed here: number of image lines */ - /* 2 bytes is placed here: samples per line */ -}; - -/* JPEG header, continued */ -static const unsigned char pac_jpeg_header2[] = { - 0x03, /* Number of image components: 3 */ - 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ - 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ - 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ - - 0xff, 0xda, /* SOS: Start Of Scan */ - 0x00, 0x0c, /* length = 12 bytes (including this length field) */ - 0x03, /* number of components: 3 */ - 0x01, 0x00, /* selector 1, table 0x00 */ - 0x02, 0x11, /* selector 2, table 0x11 */ - 0x03, 0x11, /* selector 3, table 0x11 */ - 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ - 0x00 /* Successive approximation: 0 */ -}; - -static void pac_start_frame(struct gspca_dev *gspca_dev, - __u16 lines, __u16 samples_per_line) -{ - unsigned char tmpbuf[4]; - - gspca_frame_add(gspca_dev, FIRST_PACKET, - pac_jpeg_header1, sizeof(pac_jpeg_header1)); - - tmpbuf[0] = lines >> 8; - tmpbuf[1] = lines & 0xff; - tmpbuf[2] = samples_per_line >> 8; - tmpbuf[3] = samples_per_line & 0xff; - - gspca_frame_add(gspca_dev, INTER_PACKET, - tmpbuf, sizeof(tmpbuf)); - gspca_frame_add(gspca_dev, INTER_PACKET, - pac_jpeg_header2, sizeof(pac_jpeg_header2)); -} - -/* this function is run at interrupt level */ -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 *image; - unsigned char *sof; - - sof = pac_find_sof(&sd->sof_read, data, len); - if (sof) { - int n, lum_offset, footer_length; - - /* - * 6 bytes after the FF D9 EOF marker a number of lumination - * bytes are send corresponding to different parts of the - * image, the 14th and 15th byte after the EOF seem to - * correspond to the center of the image. - */ - lum_offset = 24 + sizeof pac_sof_marker; - footer_length = 26; - - /* Finish decoding current frame */ - n = (sof - data) - (footer_length + sizeof pac_sof_marker); - if (n < 0) { - gspca_dev->image_len += n; - n = 0; - } else { - gspca_frame_add(gspca_dev, INTER_PACKET, data, n); - } - image = gspca_dev->image; - if (image != NULL - && image[gspca_dev->image_len - 2] == 0xff - && image[gspca_dev->image_len - 1] == 0xd9) - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - - n = sof - data; - len -= n; - data = sof; - - /* Get average lumination */ - if (gspca_dev->last_packet_type == LAST_PACKET && - n >= lum_offset) - atomic_set(&sd->avg_lum, data[-lum_offset] + - data[-lum_offset + 1]); - else - atomic_set(&sd->avg_lum, -1); - - /* Start the new frame with the jpeg header */ - pac_start_frame(gspca_dev, - gspca_dev->height, gspca_dev->width); - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet data */ - int len) /* interrupt packet length */ -{ - int ret = -EINVAL; - u8 data0, data1; - - if (len == 2) { - data0 = data[0]; - data1 = data[1]; - if ((data0 == 0x00 && data1 == 0x11) || - (data0 == 0x22 && data1 == 0x33) || - (data0 == 0x44 && data1 == 0x55) || - (data0 == 0x66 && data1 == 0x77) || - (data0 == 0x88 && data1 == 0x99) || - (data0 == 0xaa && data1 == 0xbb) || - (data0 == 0xcc && data1 == 0xdd) || - (data0 == 0xee && data1 == 0xff)) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - ret = 0; - } - } - - return ret; -} -#endif - -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .dq_callback = do_autogain, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x093a, 0x2600)}, - {USB_DEVICE(0x093a, 0x2601)}, - {USB_DEVICE(0x093a, 0x2603)}, - {USB_DEVICE(0x093a, 0x2608)}, - {USB_DEVICE(0x093a, 0x260e)}, - {USB_DEVICE(0x093a, 0x260f)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h deleted file mode 100644 index 8462a7c1a338..000000000000 --- a/drivers/media/video/gspca/pac_common.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Pixart PAC207BCA / PAC73xx common functions - * - * Copyright (C) 2008 Hans de Goede - * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li - * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* We calculate the autogain at the end of the transfer of a frame, at this - moment a frame with the old settings is being captured and transmitted. So - if we adjust the gain or exposure we must ignore atleast the next frame for - the new settings to come into effect before doing any other adjustments. */ -#define PAC_AUTOGAIN_IGNORE_FRAMES 2 - -static const unsigned char pac_sof_marker[5] = - { 0xff, 0xff, 0x00, 0xff, 0x96 }; - -/* - The following state machine finds the SOF marker sequence - 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream. - - +----------+ - | 0: START |<---------------\ - +----------+<-\ | - | \---/otherwise | - v 0xff | - +----------+ otherwise | - | 1 |--------------->* - | | ^ - +----------+ | - | | - v 0xff | - +----------+<-\0xff | - /->| |--/ | - | | 2 |--------------->* - | | | otherwise ^ - | +----------+ | - | | | - | v 0x00 | - | +----------+ | - | | 3 | | - | | |--------------->* - | +----------+ otherwise ^ - | | | - 0xff | v 0xff | - | +----------+ | - \--| 4 | | - | |----------------/ - +----------+ otherwise - | - v 0x96 - +----------+ - | FOUND | - +----------+ -*/ - -static unsigned char *pac_find_sof(u8 *sof_read, - unsigned char *m, int len) -{ - int i; - - /* Search for the SOF marker (fixed part) in the header */ - for (i = 0; i < len; i++) { - switch (*sof_read) { - case 0: - if (m[i] == 0xff) - *sof_read = 1; - break; - case 1: - if (m[i] == 0xff) - *sof_read = 2; - else - *sof_read = 0; - break; - case 2: - switch (m[i]) { - case 0x00: - *sof_read = 3; - break; - case 0xff: - /* stay in this state */ - break; - default: - *sof_read = 0; - } - break; - case 3: - if (m[i] == 0xff) - *sof_read = 4; - else - *sof_read = 0; - break; - case 4: - switch (m[i]) { - case 0x96: - /* Pattern found */ - PDEBUG(D_FRAM, - "SOF found, bytes to analyze: %u." - " Frame starts at byte #%u", - len, i + 1); - *sof_read = 0; - return m + i + 1; - break; - case 0xff: - *sof_read = 2; - break; - default: - *sof_read = 0; - } - break; - default: - *sof_read = 0; - } - } - - return NULL; -} diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/video/gspca/se401.c deleted file mode 100644 index a33cb78a839c..000000000000 --- a/drivers/media/video/gspca/se401.c +++ /dev/null @@ -1,739 +0,0 @@ -/* - * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver - * - * Copyright (C) 2011 Hans de Goede - * - * Based on the v4l1 se401 driver which is: - * - * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "se401" - -#define BULK_SIZE 4096 -#define PACKET_SIZE 1024 -#define READ_REQ_SIZE 64 -#define MAX_MODES ((READ_REQ_SIZE - 6) / 4) -/* The se401 compression algorithm uses a fixed quant factor, which - can be configured by setting the high nibble of the SE401_OPERATINGMODE - feature. This needs to exactly match what is in libv4l! */ -#define SE401_QUANT_FACT 8 - -#include -#include -#include "gspca.h" -#include "se401.h" - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("Endpoints se401"); -MODULE_LICENSE("GPL"); - -/* exposure change state machine states */ -enum { - EXPO_CHANGED, - EXPO_DROP_FRAME, - EXPO_NO_CHANGE, -}; - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct { /* exposure/freq control cluster */ - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *freq; - }; - bool has_brightness; - struct v4l2_pix_format fmts[MAX_MODES]; - int pixels_read; - int packet_read; - u8 packet[PACKET_SIZE]; - u8 restart_stream; - u8 button_state; - u8 resetlevel; - u8 resetlevel_frame_count; - int resetlevel_adjust_dir; - int expo_change_state; -}; - - -static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value, - int silent) -{ - int err; - - if (gspca_dev->usb_err < 0) - return; - - err = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0, NULL, 0, 1000); - if (err < 0) { - if (!silent) - pr_err("write req failed req %#04x val %#04x error %d\n", - req, value, err); - gspca_dev->usb_err = err; - } -} - -static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent) -{ - int err; - - if (gspca_dev->usb_err < 0) - return; - - if (USB_BUF_SZ < READ_REQ_SIZE) { - pr_err("USB_BUF_SZ too small!!\n"); - gspca_dev->usb_err = -ENOBUFS; - return; - } - - err = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000); - if (err < 0) { - if (!silent) - pr_err("read req failed req %#04x error %d\n", - req, err); - gspca_dev->usb_err = err; - } -} - -static void se401_set_feature(struct gspca_dev *gspca_dev, - u16 selector, u16 param) -{ - int err; - - if (gspca_dev->usb_err < 0) - return; - - err = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - SE401_REQ_SET_EXT_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - param, selector, NULL, 0, 1000); - if (err < 0) { - pr_err("set feature failed sel %#04x param %#04x error %d\n", - selector, param, err); - gspca_dev->usb_err = err; - } -} - -static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector) -{ - int err; - - if (gspca_dev->usb_err < 0) - return gspca_dev->usb_err; - - if (USB_BUF_SZ < 2) { - pr_err("USB_BUF_SZ too small!!\n"); - gspca_dev->usb_err = -ENOBUFS; - return gspca_dev->usb_err; - } - - err = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - SE401_REQ_GET_EXT_FEATURE, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, selector, gspca_dev->usb_buf, 2, 1000); - if (err < 0) { - pr_err("get feature failed sel %#04x error %d\n", - selector, err); - gspca_dev->usb_err = err; - return err; - } - return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8); -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - /* HDG: this does not seem to do anything on my cam */ - se401_write_req(gspca_dev, SE401_REQ_SET_BRT, val, 0); -} - -static void setgain(struct gspca_dev *gspca_dev, s32 val) -{ - u16 gain = 63 - val; - - /* red color gain */ - se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain); - /* green color gain */ - se401_set_feature(gspca_dev, HV7131_REG_AGCG, gain); - /* blue color gain */ - se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq) -{ - struct sd *sd = (struct sd *) gspca_dev; - int integration = val << 6; - u8 expose_h, expose_m, expose_l; - - /* Do this before the set_feature calls, for proper timing wrt - the interrupt driven pkt_scan. Note we may still race but that - is not a big issue, the expo change state machine is merely for - avoiding underexposed frames getting send out, if one sneaks - through so be it */ - sd->expo_change_state = EXPO_CHANGED; - - if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) - integration = integration - integration % 106667; - if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) - integration = integration - integration % 88889; - - expose_h = (integration >> 16); - expose_m = (integration >> 8); - expose_l = integration; - - /* integration time low */ - se401_set_feature(gspca_dev, HV7131_REG_TITL, expose_l); - /* integration time mid */ - se401_set_feature(gspca_dev, HV7131_REG_TITM, expose_m); - /* integration time high */ - se401_set_feature(gspca_dev, HV7131_REG_TITU, expose_h); -} - -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct cam *cam = &gspca_dev->cam; - u8 *cd = gspca_dev->usb_buf; - int i, j, n; - int widths[MAX_MODES], heights[MAX_MODES]; - - /* Read the camera descriptor */ - se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 1); - if (gspca_dev->usb_err) { - /* Sometimes after being idle for a while the se401 won't - respond and needs a good kicking */ - usb_reset_device(gspca_dev->dev); - gspca_dev->usb_err = 0; - se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0); - } - - /* Some cameras start with their LED on */ - se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0); - if (gspca_dev->usb_err) - return gspca_dev->usb_err; - - if (cd[1] != 0x41) { - pr_err("Wrong descriptor type\n"); - return -ENODEV; - } - - if (!(cd[2] & SE401_FORMAT_BAYER)) { - pr_err("Bayer format not supported!\n"); - return -ENODEV; - } - - if (cd[3]) - pr_info("ExtraFeatures: %d\n", cd[3]); - - n = cd[4] | (cd[5] << 8); - if (n > MAX_MODES) { - pr_err("Too many frame sizes\n"); - return -ENODEV; - } - - for (i = 0; i < n ; i++) { - widths[i] = cd[6 + i * 4 + 0] | (cd[6 + i * 4 + 1] << 8); - heights[i] = cd[6 + i * 4 + 2] | (cd[6 + i * 4 + 3] << 8); - } - - for (i = 0; i < n ; i++) { - sd->fmts[i].width = widths[i]; - sd->fmts[i].height = heights[i]; - sd->fmts[i].field = V4L2_FIELD_NONE; - sd->fmts[i].colorspace = V4L2_COLORSPACE_SRGB; - sd->fmts[i].priv = 1; - - /* janggu compression only works for 1/4th or 1/16th res */ - for (j = 0; j < n; j++) { - if (widths[j] / 2 == widths[i] && - heights[j] / 2 == heights[i]) { - sd->fmts[i].priv = 2; - break; - } - } - /* 1/16th if available too is better then 1/4th, because - we then use a larger area of the sensor */ - for (j = 0; j < n; j++) { - if (widths[j] / 4 == widths[i] && - heights[j] / 4 == heights[i]) { - sd->fmts[i].priv = 4; - break; - } - } - - if (sd->fmts[i].priv == 1) { - /* Not a 1/4th or 1/16th res, use bayer */ - sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8; - sd->fmts[i].bytesperline = widths[i]; - sd->fmts[i].sizeimage = widths[i] * heights[i]; - pr_info("Frame size: %dx%d bayer\n", - widths[i], heights[i]); - } else { - /* Found a match use janggu compression */ - sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401; - sd->fmts[i].bytesperline = 0; - sd->fmts[i].sizeimage = widths[i] * heights[i] * 3; - pr_info("Frame size: %dx%d 1/%dth janggu\n", - widths[i], heights[i], - sd->fmts[i].priv * sd->fmts[i].priv); - } - } - - cam->cam_mode = sd->fmts; - cam->nmodes = n; - cam->bulk = 1; - cam->bulk_size = BULK_SIZE; - cam->bulk_nurbs = 4; - sd->resetlevel = 0x2d; /* Set initial resetlevel */ - - /* See if the camera supports brightness */ - se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1); - sd->has_brightness = !!gspca_dev->usb_err; - gspca_dev->usb_err = 0; - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -/* function called at start time before URB creation */ -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - gspca_dev->alt = 1; /* Ignore the bogus isoc alt settings */ - - return gspca_dev->usb_err; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - int mode = 0; - - se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 1); - if (gspca_dev->usb_err) { - /* Sometimes after being idle for a while the se401 won't - respond and needs a good kicking */ - usb_reset_device(gspca_dev->dev); - gspca_dev->usb_err = 0; - se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 0); - } - se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 1, 0); - - se401_set_feature(gspca_dev, HV7131_REG_MODE_B, 0x05); - - /* set size + mode */ - se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH, - gspca_dev->width * mult, 0); - se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT, - gspca_dev->height * mult, 0); - /* - * HDG: disabled this as it does not seem to do anything - * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE, - * SE401_FORMAT_BAYER, 0); - */ - - switch (mult) { - case 1: /* Raw bayer */ - mode = 0x03; break; - case 2: /* 1/4th janggu */ - mode = SE401_QUANT_FACT << 4; break; - case 4: /* 1/16th janggu */ - mode = (SE401_QUANT_FACT << 4) | 0x02; break; - } - se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode); - - se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel); - - sd->packet_read = 0; - sd->pixels_read = 0; - sd->restart_stream = 0; - sd->resetlevel_frame_count = 0; - sd->resetlevel_adjust_dir = 0; - sd->expo_change_state = EXPO_NO_CHANGE; - - se401_write_req(gspca_dev, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, 0); - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - se401_write_req(gspca_dev, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, 0); - se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0); - se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 0, 0); -} - -static void sd_dq_callback(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - unsigned int ahrc, alrc; - int oldreset, adjust_dir; - - /* Restart the stream if requested do so by pkt_scan */ - if (sd->restart_stream) { - sd_stopN(gspca_dev); - sd_start(gspca_dev); - sd->restart_stream = 0; - } - - /* Automatically adjust sensor reset level - Hyundai have some really nice docs about this and other sensor - related stuff on their homepage: www.hei.co.kr */ - sd->resetlevel_frame_count++; - if (sd->resetlevel_frame_count < 20) - return; - - /* For some reason this normally read-only register doesn't get reset - to zero after reading them just once... */ - se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH); - se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL); - se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH); - se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL); - ahrc = 256*se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH) + - se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL); - alrc = 256*se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH) + - se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL); - - /* Not an exact science, but it seems to work pretty well... */ - oldreset = sd->resetlevel; - if (alrc > 10) { - while (alrc >= 10 && sd->resetlevel < 63) { - sd->resetlevel++; - alrc /= 2; - } - } else if (ahrc > 20) { - while (ahrc >= 20 && sd->resetlevel > 0) { - sd->resetlevel--; - ahrc /= 2; - } - } - /* Detect ping-pong-ing and halve adjustment to avoid overshoot */ - if (sd->resetlevel > oldreset) - adjust_dir = 1; - else - adjust_dir = -1; - if (sd->resetlevel_adjust_dir && - sd->resetlevel_adjust_dir != adjust_dir) - sd->resetlevel = oldreset + (sd->resetlevel - oldreset) / 2; - - if (sd->resetlevel != oldreset) { - sd->resetlevel_adjust_dir = adjust_dir; - se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel); - } - - sd->resetlevel_frame_count = 0; -} - -static void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len) -{ - struct sd *sd = (struct sd *)gspca_dev; - - switch (sd->expo_change_state) { - case EXPO_CHANGED: - /* The exposure was changed while this frame - was being send, so this frame is ok */ - sd->expo_change_state = EXPO_DROP_FRAME; - break; - case EXPO_DROP_FRAME: - /* The exposure was changed while this frame - was being captured, drop it! */ - gspca_dev->last_packet_type = DISCARD_PACKET; - sd->expo_change_state = EXPO_NO_CHANGE; - break; - case EXPO_NO_CHANGE: - break; - } - gspca_frame_add(gspca_dev, LAST_PACKET, data, len); -} - -static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len) -{ - struct sd *sd = (struct sd *)gspca_dev; - int imagesize = gspca_dev->width * gspca_dev->height; - int i, plen, bits, pixels, info, count; - - if (sd->restart_stream) - return; - - /* Sometimes a 1024 bytes garbage bulk packet is send between frames */ - if (gspca_dev->last_packet_type == LAST_PACKET && len == 1024) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - - i = 0; - while (i < len) { - /* Read header if not already be present from prev bulk pkt */ - if (sd->packet_read < 4) { - count = 4 - sd->packet_read; - if (count > len - i) - count = len - i; - memcpy(&sd->packet[sd->packet_read], &data[i], count); - sd->packet_read += count; - i += count; - if (sd->packet_read < 4) - break; - } - bits = sd->packet[3] + (sd->packet[2] << 8); - pixels = sd->packet[1] + ((sd->packet[0] & 0x3f) << 8); - info = (sd->packet[0] & 0xc0) >> 6; - plen = ((bits + 47) >> 4) << 1; - /* Sanity checks */ - if (plen > 1024) { - pr_err("invalid packet len %d restarting stream\n", - plen); - goto error; - } - if (info == 3) { - pr_err("unknown frame info value restarting stream\n"); - goto error; - } - - /* Read (remainder of) packet contents */ - count = plen - sd->packet_read; - if (count > len - i) - count = len - i; - memcpy(&sd->packet[sd->packet_read], &data[i], count); - sd->packet_read += count; - i += count; - if (sd->packet_read < plen) - break; - - sd->pixels_read += pixels; - sd->packet_read = 0; - - switch (info) { - case 0: /* Frame data */ - gspca_frame_add(gspca_dev, INTER_PACKET, sd->packet, - plen); - break; - case 1: /* EOF */ - if (sd->pixels_read != imagesize) { - pr_err("frame size %d expected %d\n", - sd->pixels_read, imagesize); - goto error; - } - sd_complete_frame(gspca_dev, sd->packet, plen); - return; /* Discard the rest of the bulk packet !! */ - case 2: /* SOF */ - gspca_frame_add(gspca_dev, FIRST_PACKET, sd->packet, - plen); - sd->pixels_read = pixels; - break; - } - } - return; - -error: - sd->restart_stream = 1; - /* Give userspace a 0 bytes frame, so our dq callback gets - called and it can restart the stream */ - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); -} - -static void sd_pkt_scan_bayer(struct gspca_dev *gspca_dev, u8 *data, int len) -{ - struct cam *cam = &gspca_dev->cam; - int imagesize = cam->cam_mode[gspca_dev->curr_mode].sizeimage; - - if (gspca_dev->image_len == 0) { - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - return; - } - - if (gspca_dev->image_len + len >= imagesize) { - sd_complete_frame(gspca_dev, data, len); - return; - } - - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len) -{ - int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - - if (len == 0) - return; - - if (mult == 1) /* mult == 1 means raw bayer */ - sd_pkt_scan_bayer(gspca_dev, data, len); - else - sd_pkt_scan_janggu(gspca_dev, data, len); -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len) -{ - struct sd *sd = (struct sd *)gspca_dev; - u8 state; - - if (len != 2) - return -EINVAL; - - switch (data[0]) { - case 0: - case 1: - state = data[0]; - break; - default: - return -EINVAL; - } - if (sd->button_state != state) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, state); - input_sync(gspca_dev->input_dev); - sd->button_state = state; - } - - return 0; -} -#endif - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - setgain(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val, sd->freq->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - if (sd->has_brightness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 15); - /* max is really 63 but > 50 is not pretty */ - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 50, 1, 25); - sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 32767, 1, 15000); - sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_cluster(2, &sd->exposure); - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stopN = sd_stopN, - .dq_callback = sd_dq_callback, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x03e8, 0x0004)}, /* Endpoints/Aox SE401 */ - {USB_DEVICE(0x0471, 0x030b)}, /* Philips PCVC665K */ - {USB_DEVICE(0x047d, 0x5001)}, /* Kensington 67014 */ - {USB_DEVICE(0x047d, 0x5002)}, /* Kensington 6701(5/7) */ - {USB_DEVICE(0x047d, 0x5003)}, /* Kensington 67016 */ - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static int sd_pre_reset(struct usb_interface *intf) -{ - return 0; -} - -static int sd_post_reset(struct usb_interface *intf) -{ - return 0; -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif - .pre_reset = sd_pre_reset, - .post_reset = sd_post_reset, -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/se401.h b/drivers/media/video/gspca/se401.h deleted file mode 100644 index 96d8ebf3cf59..000000000000 --- a/drivers/media/video/gspca/se401.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver - * - * Copyright (C) 2011 Hans de Goede - * - * Based on the v4l1 se401 driver which is: - * - * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 -#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 -#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 -#define SE401_REQ_CAPTURE_FRAME 0x43 -#define SE401_REQ_GET_BRT 0x44 -#define SE401_REQ_SET_BRT 0x45 -#define SE401_REQ_GET_WIDTH 0x4c -#define SE401_REQ_SET_WIDTH 0x4d -#define SE401_REQ_GET_HEIGHT 0x4e -#define SE401_REQ_SET_HEIGHT 0x4f -#define SE401_REQ_GET_OUTPUT_MODE 0x50 -#define SE401_REQ_SET_OUTPUT_MODE 0x51 -#define SE401_REQ_GET_EXT_FEATURE 0x52 -#define SE401_REQ_SET_EXT_FEATURE 0x53 -#define SE401_REQ_CAMERA_POWER 0x56 -#define SE401_REQ_LED_CONTROL 0x57 -#define SE401_REQ_BIOS 0xff - -#define SE401_BIOS_READ 0x07 - -#define SE401_FORMAT_BAYER 0x40 - -/* Hyundai hv7131b registers - 7121 and 7141 should be the same (haven't really checked...) */ -/* Mode registers: */ -#define HV7131_REG_MODE_A 0x00 -#define HV7131_REG_MODE_B 0x01 -#define HV7131_REG_MODE_C 0x02 -/* Frame registers: */ -#define HV7131_REG_FRSU 0x10 -#define HV7131_REG_FRSL 0x11 -#define HV7131_REG_FCSU 0x12 -#define HV7131_REG_FCSL 0x13 -#define HV7131_REG_FWHU 0x14 -#define HV7131_REG_FWHL 0x15 -#define HV7131_REG_FWWU 0x16 -#define HV7131_REG_FWWL 0x17 -/* Timing registers: */ -#define HV7131_REG_THBU 0x20 -#define HV7131_REG_THBL 0x21 -#define HV7131_REG_TVBU 0x22 -#define HV7131_REG_TVBL 0x23 -#define HV7131_REG_TITU 0x25 -#define HV7131_REG_TITM 0x26 -#define HV7131_REG_TITL 0x27 -#define HV7131_REG_TMCD 0x28 -/* Adjust Registers: */ -#define HV7131_REG_ARLV 0x30 -#define HV7131_REG_ARCG 0x31 -#define HV7131_REG_AGCG 0x32 -#define HV7131_REG_ABCG 0x33 -#define HV7131_REG_APBV 0x34 -#define HV7131_REG_ASLP 0x54 -/* Offset Registers: */ -#define HV7131_REG_OFSR 0x50 -#define HV7131_REG_OFSG 0x51 -#define HV7131_REG_OFSB 0x52 -/* REset level statistics registers: */ -#define HV7131_REG_LOREFNOH 0x57 -#define HV7131_REG_LOREFNOL 0x58 -#define HV7131_REG_HIREFNOH 0x59 -#define HV7131_REG_HIREFNOL 0x5a - -/* se401 registers */ -#define SE401_OPERATINGMODE 0x2000 diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c deleted file mode 100644 index 03fa3fd940b4..000000000000 --- a/drivers/media/video/gspca/sn9c2028.c +++ /dev/null @@ -1,735 +0,0 @@ -/* - * SN9C2028 library - * - * Copyright (C) 2009 Theodore Kilgore - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "sn9c2028" - -#include "gspca.h" - -MODULE_AUTHOR("Theodore Kilgore"); -MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - u8 sof_read; - u16 model; -}; - -struct init_command { - unsigned char instruction[6]; - unsigned char to_read; /* length to read. 0 means no reply requested */ -}; - -/* How to change the resolution of any of the VGA cams is unknown */ -static const struct v4l2_pix_format vga_mode[] = { - {640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/* No way to change the resolution of the CIF cams is known */ -static const struct v4l2_pix_format cif_mode[] = { - {352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/* the bytes to write are in gspca_dev->usb_buf */ -static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command) -{ - int rc; - - PDEBUG(D_USBO, "sending command %02x%02x%02x%02x%02x%02x", command[0], - command[1], command[2], command[3], command[4], command[5]); - - memcpy(gspca_dev->usb_buf, command, 6); - rc = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_CONFIGURATION, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 2, 0, gspca_dev->usb_buf, 6, 500); - if (rc < 0) { - pr_err("command write [%02x] error %d\n", - gspca_dev->usb_buf[0], rc); - return rc; - } - - return 0; -} - -static int sn9c2028_read1(struct gspca_dev *gspca_dev) -{ - int rc; - - rc = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 1, 0, gspca_dev->usb_buf, 1, 500); - if (rc != 1) { - pr_err("read1 error %d\n", rc); - return (rc < 0) ? rc : -EIO; - } - PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]); - return gspca_dev->usb_buf[0]; -} - -static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading) -{ - int rc; - rc = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 4, 0, gspca_dev->usb_buf, 4, 500); - if (rc != 4) { - pr_err("read4 error %d\n", rc); - return (rc < 0) ? rc : -EIO; - } - memcpy(reading, gspca_dev->usb_buf, 4); - PDEBUG(D_USBI, "read4 response %02x%02x%02x%02x", reading[0], - reading[1], reading[2], reading[3]); - return rc; -} - -static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command) -{ - int i, status; - __u8 reading[4]; - - status = sn9c2028_command(gspca_dev, command); - if (status < 0) - return status; - - status = -1; - for (i = 0; i < 256 && status < 2; i++) - status = sn9c2028_read1(gspca_dev); - if (status != 2) { - pr_err("long command status read error %d\n", status); - return (status < 0) ? status : -EIO; - } - - memset(reading, 0, 4); - status = sn9c2028_read4(gspca_dev, reading); - if (status < 0) - return status; - - /* in general, the first byte of the response is the first byte of - * the command, or'ed with 8 */ - status = sn9c2028_read1(gspca_dev); - if (status < 0) - return status; - - return 0; -} - -static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command) -{ - int err_code; - - err_code = sn9c2028_command(gspca_dev, command); - if (err_code < 0) - return err_code; - - err_code = sn9c2028_read1(gspca_dev); - if (err_code < 0) - return err_code; - - return 0; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - - PDEBUG(D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)", - id->idVendor, id->idProduct); - - sd->model = id->idProduct; - - switch (sd->model) { - case 0x7005: - PDEBUG(D_PROBE, "Genius Smart 300 camera"); - break; - case 0x8000: - PDEBUG(D_PROBE, "DC31VC"); - break; - case 0x8001: - PDEBUG(D_PROBE, "Spy camera"); - break; - case 0x8003: - PDEBUG(D_PROBE, "CIF camera"); - break; - case 0x8008: - PDEBUG(D_PROBE, "Mini-Shotz ms-350 camera"); - break; - case 0x800a: - PDEBUG(D_PROBE, "Vivitar 3350b type camera"); - cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP; - break; - } - - switch (sd->model) { - case 0x8000: - case 0x8001: - case 0x8003: - cam->cam_mode = cif_mode; - cam->nmodes = ARRAY_SIZE(cif_mode); - break; - default: - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - } - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - int status = -1; - - sn9c2028_read1(gspca_dev); - sn9c2028_read1(gspca_dev); - status = sn9c2028_read1(gspca_dev); - - return (status < 0) ? status : 0; -} - -static int run_start_commands(struct gspca_dev *gspca_dev, - struct init_command *cam_commands, int n) -{ - int i, err_code = -1; - - for (i = 0; i < n; i++) { - switch (cam_commands[i].to_read) { - case 4: - err_code = sn9c2028_long_command(gspca_dev, - cam_commands[i].instruction); - break; - case 1: - err_code = sn9c2028_short_command(gspca_dev, - cam_commands[i].instruction); - break; - case 0: - err_code = sn9c2028_command(gspca_dev, - cam_commands[i].instruction); - break; - } - if (err_code < 0) - return err_code; - } - return 0; -} - -static int start_spy_cam(struct gspca_dev *gspca_dev) -{ - struct init_command spy_start_commands[] = { - {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4}, - {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4}, - {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width 352 */ - {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */ - /* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */ - {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, - {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/ - /* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */ - {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4}, - /* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */ - {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, - /* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */ - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, - /* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */ - {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, - {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, - {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/ - /*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */ - {{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */ - /*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */ - {{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */ - {{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */ - {{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4}, - /*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */ - {{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4}, - /* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */ - /* brightness or gain. 0 is default. 4 is good - * indoors at night with incandescent lighting */ - {{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/ - {{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4}, - /* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */ - {{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */ - /* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */ - {{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1}, - {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */ - /* Camera should start to capture now. */ - }; - - return run_start_commands(gspca_dev, spy_start_commands, - ARRAY_SIZE(spy_start_commands)); -} - -static int start_cif_cam(struct gspca_dev *gspca_dev) -{ - struct init_command cif_start_commands[] = { - {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, - /* The entire sequence below seems redundant */ - /* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4}, - {{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width? - {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height? - {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample? - {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4}, - {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, - {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, - {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, - {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/ - {{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, - {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */ - {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */ - /* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample? - * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing - * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */ - /* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, - * causes subsampling - * but not a change in the resolution setting! */ - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4}, - {{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4}, - {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1}, - {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1}, - {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, - {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */ - /* Camera should start to capture now. */ - }; - - return run_start_commands(gspca_dev, cif_start_commands, - ARRAY_SIZE(cif_start_commands)); -} - -static int start_ms350_cam(struct gspca_dev *gspca_dev) -{ - struct init_command ms350_start_commands[] = { - {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4}, - {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4}, - {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, - {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, - {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, - {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, - {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, - {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, - {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, - {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width */ - {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */ - {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */ - {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, - {{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */ - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, - {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, - {{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, - {{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0}, - /* Camera should start to capture now. */ - }; - - return run_start_commands(gspca_dev, ms350_start_commands, - ARRAY_SIZE(ms350_start_commands)); -} - -static int start_genius_cam(struct gspca_dev *gspca_dev) -{ - struct init_command genius_start_commands[] = { - {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, - {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, - /* "preliminary" width and height settings */ - {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, - {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, - {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, - {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */ - {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */ - {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, - {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, - {{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4}, - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, - {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, - {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4}, - {{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4}, - {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1}, - {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1}, - {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0} - /* Camera should start to capture now. */ - }; - - return run_start_commands(gspca_dev, genius_start_commands, - ARRAY_SIZE(genius_start_commands)); -} - -static int start_vivitar_cam(struct gspca_dev *gspca_dev) -{ - struct init_command vivitar_start_commands[] = { - {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4}, - {{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4}, - {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, - {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, - {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, - {{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4}, - /* - * Above is changed from OEM 0x0b. Fixes Bayer tiling. - * Presumably gives a vertical shift of one row. - */ - {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4}, - /* Above seems to do horizontal shift. */ - {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, - {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, - {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, - {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, - /* Above three commands seem to relate to brightness. */ - {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, - {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1}, - /* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, - {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, - {{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */ - {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1}, - {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1}, - {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1}, - {{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1}, - /* Above is brightness; OEM driver setting is 0x10 */ - {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, - {{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1}, - {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1} - }; - - return run_start_commands(gspca_dev, vivitar_start_commands, - ARRAY_SIZE(vivitar_start_commands)); -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int err_code; - - sd->sof_read = 0; - - switch (sd->model) { - case 0x7005: - err_code = start_genius_cam(gspca_dev); - break; - case 0x8001: - err_code = start_spy_cam(gspca_dev); - break; - case 0x8003: - err_code = start_cif_cam(gspca_dev); - break; - case 0x8008: - err_code = start_ms350_cam(gspca_dev); - break; - case 0x800a: - err_code = start_vivitar_cam(gspca_dev); - break; - default: - pr_err("Starting unknown camera, please report this\n"); - return -ENXIO; - } - - return err_code; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - int result; - __u8 data[6]; - - result = sn9c2028_read1(gspca_dev); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop read failed"); - - memset(data, 0, 6); - data[0] = 0x14; - result = sn9c2028_command(gspca_dev, data); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop command failed"); -} - -/* Include sn9c2028 sof detection functions */ -#include "sn9c2028.h" - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - __u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - unsigned char *sof; - - sof = sn9c2028_find_sof(gspca_dev, data, len); - if (sof) { - int n; - - /* finish decoding current frame */ - n = sof - data; - if (n > sizeof sn9c2028_sof_marker) - n -= sizeof sn9c2028_sof_marker; - else - n = 0; - gspca_frame_add(gspca_dev, LAST_PACKET, data, n); - /* Start next frame. */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sn9c2028_sof_marker, sizeof sn9c2028_sof_marker); - len -= sof - data; - data = sof; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */ - /* The Genius Smart is untested. I can't find an owner ! */ - /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */ - {USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */ - {USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */ - /* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */ - {USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */ - {USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */ - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/sn9c2028.h b/drivers/media/video/gspca/sn9c2028.h deleted file mode 100644 index 8fd1d3e05665..000000000000 --- a/drivers/media/video/gspca/sn9c2028.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SN9C2028 common functions - * - * Copyright (C) 2009 Theodore Kilgore - * - * Based closely upon the file gspca/pac_common.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -static const unsigned char sn9c2028_sof_marker[5] = - { 0xff, 0xff, 0x00, 0xc4, 0xc4 }; - -static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev, - unsigned char *m, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - - /* Search for the SOF marker (fixed part) in the header */ - for (i = 0; i < len; i++) { - if (m[i] == sn9c2028_sof_marker[sd->sof_read]) { - sd->sof_read++; - if (sd->sof_read == sizeof(sn9c2028_sof_marker)) { - PDEBUG(D_FRAM, - "SOF found, bytes to analyze: %u." - " Frame starts at byte #%u", - len, i + 1); - sd->sof_read = 0; - return m + i + 1; - } - } else { - sd->sof_read = 0; - } - } - - return NULL; -} diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c deleted file mode 100644 index b9c6f17eabb2..000000000000 --- a/drivers/media/video/gspca/sn9c20x.c +++ /dev/null @@ -1,2428 +0,0 @@ -/* - * Sonix sn9c201 sn9c202 library - * - * Copyright (C) 2012 Jean-Francois Moine - * Copyright (C) 2008-2009 microdia project - * Copyright (C) 2009 Brian Johnson - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include - -#include "gspca.h" -#include "jpeg.h" - -#include -#include - -MODULE_AUTHOR("Brian Johnson , " - "microdia project "); -MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* - * Pixel format private data - */ -#define SCALE_MASK 0x0f -#define SCALE_160x120 0 -#define SCALE_320x240 1 -#define SCALE_640x480 2 -#define SCALE_1280x1024 3 -#define MODE_RAW 0x10 -#define MODE_JPEG 0x20 -#define MODE_SXGA 0x80 - -#define SENSOR_OV9650 0 -#define SENSOR_OV9655 1 -#define SENSOR_SOI968 2 -#define SENSOR_OV7660 3 -#define SENSOR_OV7670 4 -#define SENSOR_MT9V011 5 -#define SENSOR_MT9V111 6 -#define SENSOR_MT9V112 7 -#define SENSOR_MT9M001 8 -#define SENSOR_MT9M111 9 -#define SENSOR_MT9M112 10 -#define SENSOR_HV7131R 11 -#define SENSOR_MT9VPRB 12 - -/* camera flags */ -#define HAS_NO_BUTTON 0x1 -#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ -#define FLIP_DETECT 0x4 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; - - struct { /* color control cluster */ - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *hue; - }; - struct { /* blue/red balance control cluster */ - struct v4l2_ctrl *blue; - struct v4l2_ctrl *red; - }; - struct { /* h/vflip control cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; - struct v4l2_ctrl *gamma; - struct { /* autogain and exposure or gain control cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; - }; - struct v4l2_ctrl *jpegqual; - - struct work_struct work; - struct workqueue_struct *work_thread; - - u32 pktsz; /* (used by pkt_scan) */ - u16 npkt; - s8 nchg; - u8 fmt; /* (used for JPEG QTAB update */ - -#define MIN_AVG_LUM 80 -#define MAX_AVG_LUM 130 - atomic_t avg_lum; - u8 old_step; - u8 older_step; - u8 exposure_step; - - u8 i2c_addr; - u8 i2c_intf; - u8 sensor; - u8 hstart; - u8 vstart; - - u8 jpeg_hdr[JPEG_HDR_SZ]; - - u8 flags; -}; - -static void qual_upd(struct work_struct *work); - -struct i2c_reg_u8 { - u8 reg; - u8 val; -}; - -struct i2c_reg_u16 { - u8 reg; - u16 val; -}; - -static const struct dmi_system_id flip_dmi_table[] = { - { - .ident = "MSI MS-1034", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"), - DMI_MATCH(DMI_PRODUCT_VERSION, "0341") - } - }, - { - .ident = "MSI MS-1632", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), - DMI_MATCH(DMI_BOARD_NAME, "MS-1632") - } - }, - { - .ident = "MSI MS-1633X", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), - DMI_MATCH(DMI_BOARD_NAME, "MS-1633X") - } - }, - { - .ident = "MSI MS-1635X", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), - DMI_MATCH(DMI_BOARD_NAME, "MS-1635X") - } - }, - { - .ident = "ASUSTeK W7J", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_BOARD_NAME, "W7J ") - } - }, - {} -}; - -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = SCALE_160x120 | MODE_JPEG}, - {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_160x120 | MODE_RAW}, - {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 240 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_160x120}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = SCALE_320x240 | MODE_JPEG}, - {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 , - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_320x240 | MODE_RAW}, - {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 480 * 240 , - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_320x240}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = SCALE_640x480 | MODE_JPEG}, - {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_640x480 | MODE_RAW}, - {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 960 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_640x480}, -}; - -static const struct v4l2_pix_format sxga_mode[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = SCALE_160x120 | MODE_JPEG}, - {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_160x120 | MODE_RAW}, - {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 240 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_160x120}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = SCALE_320x240 | MODE_JPEG}, - {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 , - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_320x240 | MODE_RAW}, - {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 480 * 240 , - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_320x240}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = SCALE_640x480 | MODE_JPEG}, - {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_640x480 | MODE_RAW}, - {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 960 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_640x480}, - {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 1024, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA}, -}; - -static const struct v4l2_pix_format mono_mode[] = { - {160, 120, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_160x120 | MODE_RAW}, - {320, 240, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 , - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_320x240 | MODE_RAW}, - {640, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_640x480 | MODE_RAW}, - {1280, 1024, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 1024, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA}, -}; - -static const s16 hsv_red_x[] = { - 41, 44, 46, 48, 50, 52, 54, 56, - 58, 60, 62, 64, 66, 68, 70, 72, - 74, 76, 78, 80, 81, 83, 85, 87, - 88, 90, 92, 93, 95, 97, 98, 100, - 101, 102, 104, 105, 107, 108, 109, 110, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 123, 124, 125, 125, - 126, 127, 127, 128, 128, 129, 129, 129, - 130, 130, 130, 130, 131, 131, 131, 131, - 131, 131, 131, 131, 130, 130, 130, 130, - 129, 129, 129, 128, 128, 127, 127, 126, - 125, 125, 124, 123, 122, 122, 121, 120, - 119, 118, 117, 116, 115, 114, 112, 111, - 110, 109, 107, 106, 105, 103, 102, 101, - 99, 98, 96, 94, 93, 91, 90, 88, - 86, 84, 83, 81, 79, 77, 75, 74, - 72, 70, 68, 66, 64, 62, 60, 58, - 56, 54, 52, 49, 47, 45, 43, 41, - 39, 36, 34, 32, 30, 28, 25, 23, - 21, 19, 16, 14, 12, 9, 7, 5, - 3, 0, -1, -3, -6, -8, -10, -12, - -15, -17, -19, -22, -24, -26, -28, -30, - -33, -35, -37, -39, -41, -44, -46, -48, - -50, -52, -54, -56, -58, -60, -62, -64, - -66, -68, -70, -72, -74, -76, -78, -80, - -81, -83, -85, -87, -88, -90, -92, -93, - -95, -97, -98, -100, -101, -102, -104, -105, - -107, -108, -109, -110, -112, -113, -114, -115, - -116, -117, -118, -119, -120, -121, -122, -123, - -123, -124, -125, -125, -126, -127, -127, -128, - -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, - -128, -127, -127, -126, -125, -125, -124, -123, - -122, -122, -121, -120, -119, -118, -117, -116, - -115, -114, -112, -111, -110, -109, -107, -106, - -105, -103, -102, -101, -99, -98, -96, -94, - -93, -91, -90, -88, -86, -84, -83, -81, - -79, -77, -75, -74, -72, -70, -68, -66, - -64, -62, -60, -58, -56, -54, -52, -49, - -47, -45, -43, -41, -39, -36, -34, -32, - -30, -28, -25, -23, -21, -19, -16, -14, - -12, -9, -7, -5, -3, 0, 1, 3, - 6, 8, 10, 12, 15, 17, 19, 22, - 24, 26, 28, 30, 33, 35, 37, 39, 41 -}; - -static const s16 hsv_red_y[] = { - 82, 80, 78, 76, 74, 73, 71, 69, - 67, 65, 63, 61, 58, 56, 54, 52, - 50, 48, 46, 44, 41, 39, 37, 35, - 32, 30, 28, 26, 23, 21, 19, 16, - 14, 12, 10, 7, 5, 3, 0, -1, - -3, -6, -8, -10, -13, -15, -17, -19, - -22, -24, -26, -29, -31, -33, -35, -38, - -40, -42, -44, -46, -48, -51, -53, -55, - -57, -59, -61, -63, -65, -67, -69, -71, - -73, -75, -77, -79, -81, -82, -84, -86, - -88, -89, -91, -93, -94, -96, -98, -99, - -101, -102, -104, -105, -106, -108, -109, -110, - -112, -113, -114, -115, -116, -117, -119, -120, - -120, -121, -122, -123, -124, -125, -126, -126, - -127, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, - -127, -127, -126, -125, -125, -124, -123, -122, - -121, -120, -119, -118, -117, -116, -115, -114, - -113, -111, -110, -109, -107, -106, -105, -103, - -102, -100, -99, -97, -96, -94, -92, -91, - -89, -87, -85, -84, -82, -80, -78, -76, - -74, -73, -71, -69, -67, -65, -63, -61, - -58, -56, -54, -52, -50, -48, -46, -44, - -41, -39, -37, -35, -32, -30, -28, -26, - -23, -21, -19, -16, -14, -12, -10, -7, - -5, -3, 0, 1, 3, 6, 8, 10, - 13, 15, 17, 19, 22, 24, 26, 29, - 31, 33, 35, 38, 40, 42, 44, 46, - 48, 51, 53, 55, 57, 59, 61, 63, - 65, 67, 69, 71, 73, 75, 77, 79, - 81, 82, 84, 86, 88, 89, 91, 93, - 94, 96, 98, 99, 101, 102, 104, 105, - 106, 108, 109, 110, 112, 113, 114, 115, - 116, 117, 119, 120, 120, 121, 122, 123, - 124, 125, 126, 126, 127, 128, 128, 129, - 129, 130, 130, 131, 131, 131, 131, 132, - 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 131, 131, 131, 130, 130, - 130, 129, 129, 128, 127, 127, 126, 125, - 125, 124, 123, 122, 121, 120, 119, 118, - 117, 116, 115, 114, 113, 111, 110, 109, - 107, 106, 105, 103, 102, 100, 99, 97, - 96, 94, 92, 91, 89, 87, 85, 84, 82 -}; - -static const s16 hsv_green_x[] = { - -124, -124, -125, -125, -125, -125, -125, -125, - -125, -126, -126, -125, -125, -125, -125, -125, - -125, -124, -124, -124, -123, -123, -122, -122, - -121, -121, -120, -120, -119, -118, -117, -117, - -116, -115, -114, -113, -112, -111, -110, -109, - -108, -107, -105, -104, -103, -102, -100, -99, - -98, -96, -95, -93, -92, -91, -89, -87, - -86, -84, -83, -81, -79, -77, -76, -74, - -72, -70, -69, -67, -65, -63, -61, -59, - -57, -55, -53, -51, -49, -47, -45, -43, - -41, -39, -37, -35, -33, -30, -28, -26, - -24, -22, -20, -18, -15, -13, -11, -9, - -7, -4, -2, 0, 1, 3, 6, 8, - 10, 12, 14, 17, 19, 21, 23, 25, - 27, 29, 32, 34, 36, 38, 40, 42, - 44, 46, 48, 50, 52, 54, 56, 58, - 60, 62, 64, 66, 68, 70, 71, 73, - 75, 77, 78, 80, 82, 83, 85, 87, - 88, 90, 91, 93, 94, 96, 97, 98, - 100, 101, 102, 104, 105, 106, 107, 108, - 109, 111, 112, 113, 113, 114, 115, 116, - 117, 118, 118, 119, 120, 120, 121, 122, - 122, 123, 123, 124, 124, 124, 125, 125, - 125, 125, 125, 125, 125, 126, 126, 125, - 125, 125, 125, 125, 125, 124, 124, 124, - 123, 123, 122, 122, 121, 121, 120, 120, - 119, 118, 117, 117, 116, 115, 114, 113, - 112, 111, 110, 109, 108, 107, 105, 104, - 103, 102, 100, 99, 98, 96, 95, 93, - 92, 91, 89, 87, 86, 84, 83, 81, - 79, 77, 76, 74, 72, 70, 69, 67, - 65, 63, 61, 59, 57, 55, 53, 51, - 49, 47, 45, 43, 41, 39, 37, 35, - 33, 30, 28, 26, 24, 22, 20, 18, - 15, 13, 11, 9, 7, 4, 2, 0, - -1, -3, -6, -8, -10, -12, -14, -17, - -19, -21, -23, -25, -27, -29, -32, -34, - -36, -38, -40, -42, -44, -46, -48, -50, - -52, -54, -56, -58, -60, -62, -64, -66, - -68, -70, -71, -73, -75, -77, -78, -80, - -82, -83, -85, -87, -88, -90, -91, -93, - -94, -96, -97, -98, -100, -101, -102, -104, - -105, -106, -107, -108, -109, -111, -112, -113, - -113, -114, -115, -116, -117, -118, -118, -119, - -120, -120, -121, -122, -122, -123, -123, -124, -124 -}; - -static const s16 hsv_green_y[] = { - -100, -99, -98, -97, -95, -94, -93, -91, - -90, -89, -87, -86, -84, -83, -81, -80, - -78, -76, -75, -73, -71, -70, -68, -66, - -64, -63, -61, -59, -57, -55, -53, -51, - -49, -48, -46, -44, -42, -40, -38, -36, - -34, -32, -30, -27, -25, -23, -21, -19, - -17, -15, -13, -11, -9, -7, -4, -2, - 0, 1, 3, 5, 7, 9, 11, 14, - 16, 18, 20, 22, 24, 26, 28, 30, - 32, 34, 36, 38, 40, 42, 44, 46, - 48, 50, 52, 54, 56, 58, 59, 61, - 63, 65, 67, 68, 70, 72, 74, 75, - 77, 78, 80, 82, 83, 85, 86, 88, - 89, 90, 92, 93, 95, 96, 97, 98, - 100, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 111, 112, 112, 113, 114, - 115, 115, 116, 116, 117, 117, 118, 118, - 119, 119, 119, 120, 120, 120, 120, 120, - 121, 121, 121, 121, 121, 121, 120, 120, - 120, 120, 120, 119, 119, 119, 118, 118, - 117, 117, 116, 116, 115, 114, 114, 113, - 112, 111, 111, 110, 109, 108, 107, 106, - 105, 104, 103, 102, 100, 99, 98, 97, - 95, 94, 93, 91, 90, 89, 87, 86, - 84, 83, 81, 80, 78, 76, 75, 73, - 71, 70, 68, 66, 64, 63, 61, 59, - 57, 55, 53, 51, 49, 48, 46, 44, - 42, 40, 38, 36, 34, 32, 30, 27, - 25, 23, 21, 19, 17, 15, 13, 11, - 9, 7, 4, 2, 0, -1, -3, -5, - -7, -9, -11, -14, -16, -18, -20, -22, - -24, -26, -28, -30, -32, -34, -36, -38, - -40, -42, -44, -46, -48, -50, -52, -54, - -56, -58, -59, -61, -63, -65, -67, -68, - -70, -72, -74, -75, -77, -78, -80, -82, - -83, -85, -86, -88, -89, -90, -92, -93, - -95, -96, -97, -98, -100, -101, -102, -103, - -104, -105, -106, -107, -108, -109, -110, -111, - -112, -112, -113, -114, -115, -115, -116, -116, - -117, -117, -118, -118, -119, -119, -119, -120, - -120, -120, -120, -120, -121, -121, -121, -121, - -121, -121, -120, -120, -120, -120, -120, -119, - -119, -119, -118, -118, -117, -117, -116, -116, - -115, -114, -114, -113, -112, -111, -111, -110, - -109, -108, -107, -106, -105, -104, -103, -102, -100 -}; - -static const s16 hsv_blue_x[] = { - 112, 113, 114, 114, 115, 116, 117, 117, - 118, 118, 119, 119, 120, 120, 120, 121, - 121, 121, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 121, 121, - 121, 120, 120, 120, 119, 119, 118, 118, - 117, 116, 116, 115, 114, 113, 113, 112, - 111, 110, 109, 108, 107, 106, 105, 104, - 103, 102, 100, 99, 98, 97, 95, 94, - 93, 91, 90, 88, 87, 85, 84, 82, - 80, 79, 77, 76, 74, 72, 70, 69, - 67, 65, 63, 61, 60, 58, 56, 54, - 52, 50, 48, 46, 44, 42, 40, 38, - 36, 34, 32, 30, 28, 26, 24, 22, - 19, 17, 15, 13, 11, 9, 7, 5, - 2, 0, -1, -3, -5, -7, -9, -12, - -14, -16, -18, -20, -22, -24, -26, -28, - -31, -33, -35, -37, -39, -41, -43, -45, - -47, -49, -51, -53, -54, -56, -58, -60, - -62, -64, -66, -67, -69, -71, -73, -74, - -76, -78, -79, -81, -83, -84, -86, -87, - -89, -90, -92, -93, -94, -96, -97, -98, - -99, -101, -102, -103, -104, -105, -106, -107, - -108, -109, -110, -111, -112, -113, -114, -114, - -115, -116, -117, -117, -118, -118, -119, -119, - -120, -120, -120, -121, -121, -121, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, - -122, -122, -121, -121, -121, -120, -120, -120, - -119, -119, -118, -118, -117, -116, -116, -115, - -114, -113, -113, -112, -111, -110, -109, -108, - -107, -106, -105, -104, -103, -102, -100, -99, - -98, -97, -95, -94, -93, -91, -90, -88, - -87, -85, -84, -82, -80, -79, -77, -76, - -74, -72, -70, -69, -67, -65, -63, -61, - -60, -58, -56, -54, -52, -50, -48, -46, - -44, -42, -40, -38, -36, -34, -32, -30, - -28, -26, -24, -22, -19, -17, -15, -13, - -11, -9, -7, -5, -2, 0, 1, 3, - 5, 7, 9, 12, 14, 16, 18, 20, - 22, 24, 26, 28, 31, 33, 35, 37, - 39, 41, 43, 45, 47, 49, 51, 53, - 54, 56, 58, 60, 62, 64, 66, 67, - 69, 71, 73, 74, 76, 78, 79, 81, - 83, 84, 86, 87, 89, 90, 92, 93, - 94, 96, 97, 98, 99, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, 112 -}; - -static const s16 hsv_blue_y[] = { - -11, -13, -15, -17, -19, -21, -23, -25, - -27, -29, -31, -33, -35, -37, -39, -41, - -43, -45, -46, -48, -50, -52, -54, -55, - -57, -59, -61, -62, -64, -66, -67, -69, - -71, -72, -74, -75, -77, -78, -80, -81, - -83, -84, -86, -87, -88, -90, -91, -92, - -93, -95, -96, -97, -98, -99, -100, -101, - -102, -103, -104, -105, -106, -106, -107, -108, - -109, -109, -110, -111, -111, -112, -112, -113, - -113, -114, -114, -114, -115, -115, -115, -115, - -116, -116, -116, -116, -116, -116, -116, -116, - -116, -115, -115, -115, -115, -114, -114, -114, - -113, -113, -112, -112, -111, -111, -110, -110, - -109, -108, -108, -107, -106, -105, -104, -103, - -102, -101, -100, -99, -98, -97, -96, -95, - -94, -93, -91, -90, -89, -88, -86, -85, - -84, -82, -81, -79, -78, -76, -75, -73, - -71, -70, -68, -67, -65, -63, -62, -60, - -58, -56, -55, -53, -51, -49, -47, -45, - -44, -42, -40, -38, -36, -34, -32, -30, - -28, -26, -24, -22, -20, -18, -16, -14, - -12, -10, -8, -6, -4, -2, 0, 1, - 3, 5, 7, 9, 11, 13, 15, 17, - 19, 21, 23, 25, 27, 29, 31, 33, - 35, 37, 39, 41, 43, 45, 46, 48, - 50, 52, 54, 55, 57, 59, 61, 62, - 64, 66, 67, 69, 71, 72, 74, 75, - 77, 78, 80, 81, 83, 84, 86, 87, - 88, 90, 91, 92, 93, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, - 106, 106, 107, 108, 109, 109, 110, 111, - 111, 112, 112, 113, 113, 114, 114, 114, - 115, 115, 115, 115, 116, 116, 116, 116, - 116, 116, 116, 116, 116, 115, 115, 115, - 115, 114, 114, 114, 113, 113, 112, 112, - 111, 111, 110, 110, 109, 108, 108, 107, - 106, 105, 104, 103, 102, 101, 100, 99, - 98, 97, 96, 95, 94, 93, 91, 90, - 89, 88, 86, 85, 84, 82, 81, 79, - 78, 76, 75, 73, 71, 70, 68, 67, - 65, 63, 62, 60, 58, 56, 55, 53, - 51, 49, 47, 45, 44, 42, 40, 38, - 36, 34, 32, 30, 28, 26, 24, 22, - 20, 18, 16, 14, 12, 10, 8, 6, - 4, 2, 0, -1, -3, -5, -7, -9, -11 -}; - -static const u16 i2c_ident[] = { - V4L2_IDENT_OV9650, - V4L2_IDENT_OV9655, - V4L2_IDENT_SOI968, - V4L2_IDENT_OV7660, - V4L2_IDENT_OV7670, - V4L2_IDENT_MT9V011, - V4L2_IDENT_MT9V111, - V4L2_IDENT_MT9V112, - V4L2_IDENT_MT9M001C12ST, - V4L2_IDENT_MT9M111, - V4L2_IDENT_MT9M112, - V4L2_IDENT_HV7131R, -[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN, -}; - -static const u16 bridge_init[][2] = { - {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, - {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, - {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, - {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00}, - {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50}, - {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50}, - {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04}, - {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05}, - {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00}, - {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d}, - {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04}, - {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8}, - {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32}, - {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd}, - {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01}, - {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f}, - {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f}, - {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01}, - {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80}, - {0x1007, 0x00} -}; - -/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ -static const u8 ov_gain[] = { - 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, - 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, - 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, - 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */, - 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */, - 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */, - 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */, - 0x70 /* 8x */ -}; - -/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ -static const u16 micron1_gain[] = { - /* 1x 1.25x 1.5x 1.75x */ - 0x0020, 0x0028, 0x0030, 0x0038, - /* 2x 2.25x 2.5x 2.75x */ - 0x00a0, 0x00a4, 0x00a8, 0x00ac, - /* 3x 3.25x 3.5x 3.75x */ - 0x00b0, 0x00b4, 0x00b8, 0x00bc, - /* 4x 4.25x 4.5x 4.75x */ - 0x00c0, 0x00c4, 0x00c8, 0x00cc, - /* 5x 5.25x 5.5x 5.75x */ - 0x00d0, 0x00d4, 0x00d8, 0x00dc, - /* 6x 6.25x 6.5x 6.75x */ - 0x00e0, 0x00e4, 0x00e8, 0x00ec, - /* 7x 7.25x 7.5x 7.75x */ - 0x00f0, 0x00f4, 0x00f8, 0x00fc, - /* 8x */ - 0x01c0 -}; - -/* mt9m001 sensor uses a different gain formula then other micron sensors */ -/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ -static const u16 micron2_gain[] = { - /* 1x 1.25x 1.5x 1.75x */ - 0x0008, 0x000a, 0x000c, 0x000e, - /* 2x 2.25x 2.5x 2.75x */ - 0x0010, 0x0012, 0x0014, 0x0016, - /* 3x 3.25x 3.5x 3.75x */ - 0x0018, 0x001a, 0x001c, 0x001e, - /* 4x 4.25x 4.5x 4.75x */ - 0x0020, 0x0051, 0x0052, 0x0053, - /* 5x 5.25x 5.5x 5.75x */ - 0x0054, 0x0055, 0x0056, 0x0057, - /* 6x 6.25x 6.5x 6.75x */ - 0x0058, 0x0059, 0x005a, 0x005b, - /* 7x 7.25x 7.5x 7.75x */ - 0x005c, 0x005d, 0x005e, 0x005f, - /* 8x */ - 0x0060 -}; - -/* Gain = .5 + bit[7:0] / 16 */ -static const u8 hv7131r_gain[] = { - 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, - 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, - 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, - 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */, - 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */, - 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */, - 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */, - 0x78 /* 8x */ -}; - -static const struct i2c_reg_u8 soi968_init[] = { - {0x0c, 0x00}, {0x0f, 0x1f}, - {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, - {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, - {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, - {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20}, - {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e}, - {0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13}, - {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79}, - {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40}, - {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32}, - {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, -}; - -static const struct i2c_reg_u8 ov7660_init[] = { - {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, - {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, - {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, - /* HDG Set hstart and hstop, datasheet default 0x11, 0x61, using - 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */ - {0x17, 0x10}, {0x18, 0x61}, - {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, - {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0x00}, - {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50}, -}; - -static const struct i2c_reg_u8 ov7670_init[] = { - {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, - {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, - {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, - {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00}, - {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07}, - {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75}, - {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8}, - {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5}, - {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27}, - {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b}, - {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a}, - {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00}, - {0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00}, - {0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80}, - {0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82}, - {0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20}, - {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c}, - {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66}, - {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11}, - {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40}, - {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02}, - {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a}, - {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08}, - {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04}, - {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30}, - {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88}, - {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30}, - {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99}, - {0x9e, 0x7f}, {0x78, 0x04}, {0x79, 0x01}, {0xc8, 0xf0}, - {0x79, 0x0f}, {0xc8, 0x00}, {0x79, 0x10}, {0xc8, 0x7e}, - {0x79, 0x0a}, {0xc8, 0x80}, {0x79, 0x0b}, {0xc8, 0x01}, - {0x79, 0x0c}, {0xc8, 0x0f}, {0x79, 0x0d}, {0xc8, 0x20}, - {0x79, 0x09}, {0xc8, 0x80}, {0x79, 0x02}, {0xc8, 0xc0}, - {0x79, 0x03}, {0xc8, 0x40}, {0x79, 0x05}, {0xc8, 0x30}, - {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06}, - {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a}, - {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a}, - {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84}, - {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d}, - {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d}, - {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00}, - {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, - {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60}, - {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, - {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e}, - {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56}, - {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03}, - {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47}, - {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74}, - {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2}, - {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00}, - {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a}, - {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00}, - {0x93, 0x00}, -}; - -static const struct i2c_reg_u8 ov9650_init[] = { - {0x00, 0x00}, {0x01, 0x78}, - {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, - {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, - {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00}, - {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c}, - {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2}, - {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07}, - {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00}, - {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04}, - {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68}, - {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80}, - {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00}, - {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00}, - {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30}, - {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf}, - {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00}, - {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01}, - {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19}, - {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1}, - {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80}, - {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00}, - {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20}, - {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf}, - {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88}, - {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00}, - {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8}, - {0xaa, 0x92}, {0xab, 0x0a}, -}; - -static const struct i2c_reg_u8 ov9655_init[] = { - {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, - {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, - {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, - {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57}, - {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19}, - {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80}, - {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c}, - {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc}, - {0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05}, - {0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e}, - {0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, - {0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, - {0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, - {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, - {0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, - {0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01}, - {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0}, - {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00}, - {0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61}, - {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a}, - {0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01}, - {0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a}, - {0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c}, - {0x04, 0x03}, {0x00, 0x13}, -}; - -static const struct i2c_reg_u16 mt9v112_init[] = { - {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, - {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, - {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, - {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a}, - {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58}, - {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001}, - {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020}, - {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020}, - {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020}, - {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, - {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2}, - {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, - {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020}, - {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, - {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae}, - {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, -}; - -static const struct i2c_reg_u16 mt9v111_init[] = { - {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, - {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0}, - {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e}, - {0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016}, - {0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004}, - {0x06, 0x002d}, {0x07, 0x3002}, {0x08, 0x0008}, - {0x0e, 0x0008}, {0x20, 0x0000} -}; - -static const struct i2c_reg_u16 mt9v011_init[] = { - {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, - {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, - {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, - {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000}, - {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000}, - {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000}, - {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000}, - {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000}, - {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000}, - {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000}, - {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000}, - {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000}, - {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024}, - {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000}, - {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100}, - {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1}, - {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000}, - {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000}, - {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000}, - {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101}, - {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003}, - {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, - {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000}, - {0x06, 0x0029}, {0x05, 0x0009}, -}; - -static const struct i2c_reg_u16 mt9m001_init[] = { - {0x0d, 0x0001}, - {0x0d, 0x0000}, - {0x04, 0x0500}, /* hres = 1280 */ - {0x03, 0x0400}, /* vres = 1024 */ - {0x20, 0x1100}, - {0x06, 0x0010}, - {0x2b, 0x0024}, - {0x2e, 0x0024}, - {0x35, 0x0024}, - {0x2d, 0x0020}, - {0x2c, 0x0020}, - {0x09, 0x0ad4}, - {0x35, 0x0057}, -}; - -static const struct i2c_reg_u16 mt9m111_init[] = { - {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, - {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, - {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, - {0xf0, 0x0000}, -}; - -static const struct i2c_reg_u16 mt9m112_init[] = { - {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, - {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, - {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, - {0xf0, 0x0000}, -}; - -static const struct i2c_reg_u8 hv7131r_init[] = { - {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, - {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, - {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, - {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07}, - {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62}, - {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10}, - {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, - {0x23, 0x09}, {0x01, 0x08}, -}; - -static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) -{ - struct usb_device *dev = gspca_dev->dev; - int result; - - if (gspca_dev->usb_err < 0) - return; - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - reg, - 0x00, - gspca_dev->usb_buf, - length, - 500); - if (unlikely(result < 0 || result != length)) { - pr_err("Read register %02x failed %d\n", reg, result); - gspca_dev->usb_err = result; - } -} - -static void reg_w(struct gspca_dev *gspca_dev, u16 reg, - const u8 *buffer, int length) -{ - struct usb_device *dev = gspca_dev->dev; - int result; - - if (gspca_dev->usb_err < 0) - return; - memcpy(gspca_dev->usb_buf, buffer, length); - result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - reg, - 0x00, - gspca_dev->usb_buf, - length, - 500); - if (unlikely(result < 0 || result != length)) { - pr_err("Write register %02x failed %d\n", reg, result); - gspca_dev->usb_err = result; - } -} - -static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) -{ - reg_w(gspca_dev, reg, &value, 1); -} - -static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) -{ - int i; - - reg_w(gspca_dev, 0x10c0, buffer, 8); - for (i = 0; i < 5; i++) { - reg_r(gspca_dev, 0x10c0, 1); - if (gspca_dev->usb_err < 0) - return; - if (gspca_dev->usb_buf[0] & 0x04) { - if (gspca_dev->usb_buf[0] & 0x08) { - pr_err("i2c_w error\n"); - gspca_dev->usb_err = -EIO; - } - return; - } - msleep(10); - } - pr_err("i2c_w reg %02x no response\n", buffer[2]); -/* gspca_dev->usb_err = -EIO; fixme: may occur */ -} - -static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 row[8]; - - /* - * from the point of view of the bridge, the length - * includes the address - */ - row[0] = sd->i2c_intf | (2 << 4); - row[1] = sd->i2c_addr; - row[2] = reg; - row[3] = val; - row[4] = 0x00; - row[5] = 0x00; - row[6] = 0x00; - row[7] = 0x10; - - i2c_w(gspca_dev, row); -} - -static void i2c_w1_buf(struct gspca_dev *gspca_dev, - const struct i2c_reg_u8 *buf, int sz) -{ - while (--sz >= 0) { - i2c_w1(gspca_dev, buf->reg, buf->val); - buf++; - } -} - -static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 row[8]; - - /* - * from the point of view of the bridge, the length - * includes the address - */ - row[0] = sd->i2c_intf | (3 << 4); - row[1] = sd->i2c_addr; - row[2] = reg; - row[3] = val >> 8; - row[4] = val; - row[5] = 0x00; - row[6] = 0x00; - row[7] = 0x10; - - i2c_w(gspca_dev, row); -} - -static void i2c_w2_buf(struct gspca_dev *gspca_dev, - const struct i2c_reg_u16 *buf, int sz) -{ - while (--sz >= 0) { - i2c_w2(gspca_dev, buf->reg, buf->val); - buf++; - } -} - -static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 row[8]; - - row[0] = sd->i2c_intf | (1 << 4); - row[1] = sd->i2c_addr; - row[2] = reg; - row[3] = 0; - row[4] = 0; - row[5] = 0; - row[6] = 0; - row[7] = 0x10; - i2c_w(gspca_dev, row); - row[0] = sd->i2c_intf | (1 << 4) | 0x02; - row[2] = 0; - i2c_w(gspca_dev, row); - reg_r(gspca_dev, 0x10c2, 5); - *val = gspca_dev->usb_buf[4]; -} - -static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 row[8]; - - row[0] = sd->i2c_intf | (1 << 4); - row[1] = sd->i2c_addr; - row[2] = reg; - row[3] = 0; - row[4] = 0; - row[5] = 0; - row[6] = 0; - row[7] = 0x10; - i2c_w(gspca_dev, row); - row[0] = sd->i2c_intf | (2 << 4) | 0x02; - row[2] = 0; - i2c_w(gspca_dev, row); - reg_r(gspca_dev, 0x10c2, 5); - *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; -} - -static void ov9650_init_sensor(struct gspca_dev *gspca_dev) -{ - u16 id; - struct sd *sd = (struct sd *) gspca_dev; - - i2c_r2(gspca_dev, 0x1c, &id); - if (gspca_dev->usb_err < 0) - return; - - if (id != 0x7fa2) { - pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id); - gspca_dev->usb_err = -ENODEV; - return; - } - - i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ - msleep(200); - i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init)); - if (gspca_dev->usb_err < 0) - pr_err("OV9650 sensor initialization failed\n"); - sd->hstart = 1; - sd->vstart = 7; -} - -static void ov9655_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ - msleep(200); - i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init)); - if (gspca_dev->usb_err < 0) - pr_err("OV9655 sensor initialization failed\n"); - - sd->hstart = 1; - sd->vstart = 2; -} - -static void soi968_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ - msleep(200); - i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init)); - if (gspca_dev->usb_err < 0) - pr_err("SOI968 sensor initialization failed\n"); - - sd->hstart = 60; - sd->vstart = 11; -} - -static void ov7660_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ - msleep(200); - i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init)); - if (gspca_dev->usb_err < 0) - pr_err("OV7660 sensor initialization failed\n"); - sd->hstart = 3; - sd->vstart = 3; -} - -static void ov7670_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ - msleep(200); - i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init)); - if (gspca_dev->usb_err < 0) - pr_err("OV7670 sensor initialization failed\n"); - - sd->hstart = 0; - sd->vstart = 1; -} - -static void mt9v_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 value; - - sd->i2c_addr = 0x5d; - i2c_r2(gspca_dev, 0xff, &value); - if (gspca_dev->usb_err >= 0 - && value == 0x8243) { - i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init)); - if (gspca_dev->usb_err < 0) { - pr_err("MT9V011 sensor initialization failed\n"); - return; - } - sd->hstart = 2; - sd->vstart = 2; - sd->sensor = SENSOR_MT9V011; - pr_info("MT9V011 sensor detected\n"); - return; - } - - gspca_dev->usb_err = 0; - sd->i2c_addr = 0x5c; - i2c_w2(gspca_dev, 0x01, 0x0004); - i2c_r2(gspca_dev, 0xff, &value); - if (gspca_dev->usb_err >= 0 - && value == 0x823a) { - i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init)); - if (gspca_dev->usb_err < 0) { - pr_err("MT9V111 sensor initialization failed\n"); - return; - } - sd->hstart = 2; - sd->vstart = 2; - sd->sensor = SENSOR_MT9V111; - pr_info("MT9V111 sensor detected\n"); - return; - } - - gspca_dev->usb_err = 0; - sd->i2c_addr = 0x5d; - i2c_w2(gspca_dev, 0xf0, 0x0000); - if (gspca_dev->usb_err < 0) { - gspca_dev->usb_err = 0; - sd->i2c_addr = 0x48; - i2c_w2(gspca_dev, 0xf0, 0x0000); - } - i2c_r2(gspca_dev, 0x00, &value); - if (gspca_dev->usb_err >= 0 - && value == 0x1229) { - i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init)); - if (gspca_dev->usb_err < 0) { - pr_err("MT9V112 sensor initialization failed\n"); - return; - } - sd->hstart = 6; - sd->vstart = 2; - sd->sensor = SENSOR_MT9V112; - pr_info("MT9V112 sensor detected\n"); - return; - } - - gspca_dev->usb_err = -ENODEV; -} - -static void mt9m112_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init)); - if (gspca_dev->usb_err < 0) - pr_err("MT9M112 sensor initialization failed\n"); - - sd->hstart = 0; - sd->vstart = 2; -} - -static void mt9m111_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init)); - if (gspca_dev->usb_err < 0) - pr_err("MT9M111 sensor initialization failed\n"); - - sd->hstart = 0; - sd->vstart = 2; -} - -static void mt9m001_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 id; - - i2c_r2(gspca_dev, 0x00, &id); - if (gspca_dev->usb_err < 0) - return; - - /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ - switch (id) { - case 0x8411: - case 0x8421: - pr_info("MT9M001 color sensor detected\n"); - break; - case 0x8431: - pr_info("MT9M001 mono sensor detected\n"); - break; - default: - pr_err("No MT9M001 chip detected, ID = %x\n\n", id); - gspca_dev->usb_err = -ENODEV; - return; - } - - i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init)); - if (gspca_dev->usb_err < 0) - pr_err("MT9M001 sensor initialization failed\n"); - - sd->hstart = 1; - sd->vstart = 1; -} - -static void hv7131r_init_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init)); - if (gspca_dev->usb_err < 0) - pr_err("HV7131R Sensor initialization failed\n"); - - sd->hstart = 0; - sd->vstart = 1; -} - -static void set_cmatrix(struct gspca_dev *gspca_dev, - s32 brightness, s32 contrast, s32 satur, s32 hue) -{ - s32 hue_coord, hue_index = 180 + hue; - u8 cmatrix[21]; - - memset(cmatrix, 0, sizeof cmatrix); - cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26; - cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; - cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; - cmatrix[18] = brightness - 0x80; - - hue_coord = (hsv_red_x[hue_index] * satur) >> 8; - cmatrix[6] = hue_coord; - cmatrix[7] = (hue_coord >> 8) & 0x0f; - - hue_coord = (hsv_red_y[hue_index] * satur) >> 8; - cmatrix[8] = hue_coord; - cmatrix[9] = (hue_coord >> 8) & 0x0f; - - hue_coord = (hsv_green_x[hue_index] * satur) >> 8; - cmatrix[10] = hue_coord; - cmatrix[11] = (hue_coord >> 8) & 0x0f; - - hue_coord = (hsv_green_y[hue_index] * satur) >> 8; - cmatrix[12] = hue_coord; - cmatrix[13] = (hue_coord >> 8) & 0x0f; - - hue_coord = (hsv_blue_x[hue_index] * satur) >> 8; - cmatrix[14] = hue_coord; - cmatrix[15] = (hue_coord >> 8) & 0x0f; - - hue_coord = (hsv_blue_y[hue_index] * satur) >> 8; - cmatrix[16] = hue_coord; - cmatrix[17] = (hue_coord >> 8) & 0x0f; - - reg_w(gspca_dev, 0x10e1, cmatrix, 21); -} - -static void set_gamma(struct gspca_dev *gspca_dev, s32 val) -{ - u8 gamma[17]; - u8 gval = val * 0xb8 / 0x100; - - gamma[0] = 0x0a; - gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); - gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8); - gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8); - gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8); - gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8); - gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8); - gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8); - gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8); - gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8); - gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8); - gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8); - gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8); - gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8); - gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8); - gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8); - gamma[16] = 0xf5; - - reg_w(gspca_dev, 0x1190, gamma, 17); -} - -static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red) -{ - reg_w1(gspca_dev, 0x118c, red); - reg_w1(gspca_dev, 0x118f, blue); -} - -static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) -{ - u8 value, tslb; - u16 value2; - struct sd *sd = (struct sd *) gspca_dev; - - if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { - hflip = !hflip; - vflip = !vflip; - } - - switch (sd->sensor) { - case SENSOR_OV7660: - value = 0x01; - if (hflip) - value |= 0x20; - if (vflip) { - value |= 0x10; - sd->vstart = 2; - } else { - sd->vstart = 3; - } - reg_w1(gspca_dev, 0x1182, sd->vstart); - i2c_w1(gspca_dev, 0x1e, value); - break; - case SENSOR_OV9650: - i2c_r1(gspca_dev, 0x1e, &value); - value &= ~0x30; - tslb = 0x01; - if (hflip) - value |= 0x20; - if (vflip) { - value |= 0x10; - tslb = 0x49; - } - i2c_w1(gspca_dev, 0x1e, value); - i2c_w1(gspca_dev, 0x3a, tslb); - break; - case SENSOR_MT9V111: - case SENSOR_MT9V011: - i2c_r2(gspca_dev, 0x20, &value2); - value2 &= ~0xc0a0; - if (hflip) - value2 |= 0x8080; - if (vflip) - value2 |= 0x4020; - i2c_w2(gspca_dev, 0x20, value2); - break; - case SENSOR_MT9M112: - case SENSOR_MT9M111: - case SENSOR_MT9V112: - i2c_r2(gspca_dev, 0x20, &value2); - value2 &= ~0x0003; - if (hflip) - value2 |= 0x0002; - if (vflip) - value2 |= 0x0001; - i2c_w2(gspca_dev, 0x20, value2); - break; - case SENSOR_HV7131R: - i2c_r1(gspca_dev, 0x01, &value); - value &= ~0x03; - if (vflip) - value |= 0x01; - if (hflip) - value |= 0x02; - i2c_w1(gspca_dev, 0x01, value); - break; - } -} - -static void set_exposure(struct gspca_dev *gspca_dev, s32 expo) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - int expo2; - - if (gspca_dev->streaming) - exp[7] = 0x1e; - - switch (sd->sensor) { - case SENSOR_OV7660: - case SENSOR_OV7670: - case SENSOR_OV9655: - case SENSOR_OV9650: - if (expo > 547) - expo2 = 547; - else - expo2 = expo; - exp[0] |= (2 << 4); - exp[2] = 0x10; /* AECH */ - exp[3] = expo2 >> 2; - exp[7] = 0x10; - i2c_w(gspca_dev, exp); - exp[2] = 0x04; /* COM1 */ - exp[3] = expo2 & 0x0003; - exp[7] = 0x10; - i2c_w(gspca_dev, exp); - expo -= expo2; - exp[7] = 0x1e; - exp[0] |= (3 << 4); - exp[2] = 0x2d; /* ADVFL & ADVFH */ - exp[3] = expo; - exp[4] = expo >> 8; - break; - case SENSOR_MT9M001: - case SENSOR_MT9V112: - case SENSOR_MT9V011: - exp[0] |= (3 << 4); - exp[2] = 0x09; - exp[3] = expo >> 8; - exp[4] = expo; - break; - case SENSOR_HV7131R: - exp[0] |= (4 << 4); - exp[2] = 0x25; - exp[3] = expo >> 5; - exp[4] = expo << 3; - exp[5] = 0; - break; - default: - return; - } - i2c_w(gspca_dev, exp); -} - -static void set_gain(struct gspca_dev *gspca_dev, s32 g) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 gain[8] = {sd->i2c_intf, sd->i2c_addr, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - - if (gspca_dev->streaming) - gain[7] = 0x15; /* or 1d ? */ - - switch (sd->sensor) { - case SENSOR_OV7660: - case SENSOR_OV7670: - case SENSOR_SOI968: - case SENSOR_OV9655: - case SENSOR_OV9650: - gain[0] |= (2 << 4); - gain[3] = ov_gain[g]; - break; - case SENSOR_MT9V011: - gain[0] |= (3 << 4); - gain[2] = 0x35; - gain[3] = micron1_gain[g] >> 8; - gain[4] = micron1_gain[g]; - break; - case SENSOR_MT9V112: - gain[0] |= (3 << 4); - gain[2] = 0x2f; - gain[3] = micron1_gain[g] >> 8; - gain[4] = micron1_gain[g]; - break; - case SENSOR_MT9M001: - gain[0] |= (3 << 4); - gain[2] = 0x2f; - gain[3] = micron2_gain[g] >> 8; - gain[4] = micron2_gain[g]; - break; - case SENSOR_HV7131R: - gain[0] |= (2 << 4); - gain[2] = 0x30; - gain[3] = hv7131r_gain[g]; - break; - default: - return; - } - i2c_w(gspca_dev, gain); -} - -static void set_quality(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - jpeg_set_qual(sd->jpeg_hdr, val); - reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */ - reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */ - reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); - reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); - reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */ - reg_w1(gspca_dev, 0x10e0, sd->fmt); - sd->fmt ^= 0x0c; /* invert QTAB use + write */ - reg_w1(gspca_dev, 0x10e0, sd->fmt); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int sd_dbg_g_register(struct gspca_dev *gspca_dev, - struct v4l2_dbg_register *reg) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_HOST: - if (reg->match.addr != 0) - return -EINVAL; - if (reg->reg < 0x1000 || reg->reg > 0x11ff) - return -EINVAL; - reg_r(gspca_dev, reg->reg, 1); - reg->val = gspca_dev->usb_buf[0]; - return gspca_dev->usb_err; - case V4L2_CHIP_MATCH_I2C_ADDR: - if (reg->match.addr != sd->i2c_addr) - return -EINVAL; - if (sd->sensor >= SENSOR_MT9V011 && - sd->sensor <= SENSOR_MT9M112) { - i2c_r2(gspca_dev, reg->reg, (u16 *) ®->val); - } else { - i2c_r1(gspca_dev, reg->reg, (u8 *) ®->val); - } - return gspca_dev->usb_err; - } - return -EINVAL; -} - -static int sd_dbg_s_register(struct gspca_dev *gspca_dev, - struct v4l2_dbg_register *reg) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_HOST: - if (reg->match.addr != 0) - return -EINVAL; - if (reg->reg < 0x1000 || reg->reg > 0x11ff) - return -EINVAL; - reg_w1(gspca_dev, reg->reg, reg->val); - return gspca_dev->usb_err; - case V4L2_CHIP_MATCH_I2C_ADDR: - if (reg->match.addr != sd->i2c_addr) - return -EINVAL; - if (sd->sensor >= SENSOR_MT9V011 && - sd->sensor <= SENSOR_MT9M112) { - i2c_w2(gspca_dev, reg->reg, reg->val); - } else { - i2c_w1(gspca_dev, reg->reg, reg->val); - } - return gspca_dev->usb_err; - } - return -EINVAL; -} -#endif - -static int sd_chip_ident(struct gspca_dev *gspca_dev, - struct v4l2_dbg_chip_ident *chip) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (chip->match.type) { - case V4L2_CHIP_MATCH_HOST: - if (chip->match.addr != 0) - return -EINVAL; - chip->revision = 0; - chip->ident = V4L2_IDENT_SN9C20X; - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - if (chip->match.addr != sd->i2c_addr) - return -EINVAL; - chip->revision = 0; - chip->ident = i2c_ident[sd->sensor]; - return 0; - } - return -EINVAL; -} - -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - cam->needs_full_bandwidth = 1; - - sd->sensor = id->driver_info >> 8; - sd->i2c_addr = id->driver_info; - sd->flags = id->driver_info >> 16; - sd->i2c_intf = 0x80; /* i2c 100 Kb/s */ - - switch (sd->sensor) { - case SENSOR_MT9M112: - case SENSOR_MT9M111: - case SENSOR_OV9650: - case SENSOR_SOI968: - cam->cam_mode = sxga_mode; - cam->nmodes = ARRAY_SIZE(sxga_mode); - break; - case SENSOR_MT9M001: - cam->cam_mode = mono_mode; - cam->nmodes = ARRAY_SIZE(mono_mode); - break; - case SENSOR_HV7131R: - sd->i2c_intf = 0x81; /* i2c 400 Kb/s */ - /* fall thru */ - default: - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - break; - } - - sd->old_step = 0; - sd->older_step = 0; - sd->exposure_step = 16; - - INIT_WORK(&sd->work, qual_upd); - - return 0; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - /* color control cluster */ - case V4L2_CID_BRIGHTNESS: - set_cmatrix(gspca_dev, sd->brightness->val, - sd->contrast->val, sd->saturation->val, sd->hue->val); - break; - case V4L2_CID_GAMMA: - set_gamma(gspca_dev, ctrl->val); - break; - /* blue/red balance cluster */ - case V4L2_CID_BLUE_BALANCE: - set_redblue(gspca_dev, sd->blue->val, sd->red->val); - break; - /* h/vflip cluster */ - case V4L2_CID_HFLIP: - set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val); - break; - /* standalone exposure control */ - case V4L2_CID_EXPOSURE: - set_exposure(gspca_dev, ctrl->val); - break; - /* standalone gain control */ - case V4L2_CID_GAIN: - set_gain(gspca_dev, ctrl->val); - break; - /* autogain + exposure or gain control cluster */ - case V4L2_CID_AUTOGAIN: - if (sd->sensor == SENSOR_SOI968) - set_gain(gspca_dev, sd->gain->val); - else - set_exposure(gspca_dev, sd->exposure->val); - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - set_quality(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 13); - - sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); - sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 127); - sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 127); - sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HUE, -180, 180, 1, 0); - - sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAMMA, 0, 255, 1, 0x10); - - sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28); - sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28); - - if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 && - sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 && - sd->sensor != SENSOR_MT9VPRB) { - sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - } - - if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB && - sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 && - sd->sensor != SENSOR_MT9V111) - sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33); - - if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 && - sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) { - sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 28, 1, 0); - sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - } - - sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80); - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - - v4l2_ctrl_cluster(4, &sd->brightness); - v4l2_ctrl_cluster(2, &sd->blue); - if (sd->hflip) - v4l2_ctrl_cluster(2, &sd->hflip); - if (sd->autogain) { - if (sd->sensor == SENSOR_SOI968) - /* this sensor doesn't have the exposure control and - autogain is clustered with gain instead. This works - because sd->exposure == NULL. */ - v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false); - else - /* Otherwise autogain is clustered with exposure. */ - v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); - } - return 0; -} - -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - u8 value; - u8 i2c_init[9] = - {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; - - for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { - value = bridge_init[i][1]; - reg_w(gspca_dev, bridge_init[i][0], &value, 1); - if (gspca_dev->usb_err < 0) { - pr_err("Device initialization failed\n"); - return gspca_dev->usb_err; - } - } - - if (sd->flags & LED_REVERSE) - reg_w1(gspca_dev, 0x1006, 0x00); - else - reg_w1(gspca_dev, 0x1006, 0x20); - - reg_w(gspca_dev, 0x10c0, i2c_init, 9); - if (gspca_dev->usb_err < 0) { - pr_err("Device initialization failed\n"); - return gspca_dev->usb_err; - } - - switch (sd->sensor) { - case SENSOR_OV9650: - ov9650_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("OV9650 sensor detected\n"); - break; - case SENSOR_OV9655: - ov9655_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("OV9655 sensor detected\n"); - break; - case SENSOR_SOI968: - soi968_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("SOI968 sensor detected\n"); - break; - case SENSOR_OV7660: - ov7660_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("OV7660 sensor detected\n"); - break; - case SENSOR_OV7670: - ov7670_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("OV7670 sensor detected\n"); - break; - case SENSOR_MT9VPRB: - mt9v_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("MT9VPRB sensor detected\n"); - break; - case SENSOR_MT9M111: - mt9m111_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("MT9M111 sensor detected\n"); - break; - case SENSOR_MT9M112: - mt9m112_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("MT9M112 sensor detected\n"); - break; - case SENSOR_MT9M001: - mt9m001_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - break; - case SENSOR_HV7131R: - hv7131r_init_sensor(gspca_dev); - if (gspca_dev->usb_err < 0) - break; - pr_info("HV7131R sensor detected\n"); - break; - default: - pr_err("Unsupported sensor\n"); - gspca_dev->usb_err = -ENODEV; - } - return gspca_dev->usb_err; -} - -static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 value; - - switch (sd->sensor) { - case SENSOR_SOI968: - if (mode & MODE_SXGA) { - i2c_w1(gspca_dev, 0x17, 0x1d); - i2c_w1(gspca_dev, 0x18, 0xbd); - i2c_w1(gspca_dev, 0x19, 0x01); - i2c_w1(gspca_dev, 0x1a, 0x81); - i2c_w1(gspca_dev, 0x12, 0x00); - sd->hstart = 140; - sd->vstart = 19; - } else { - i2c_w1(gspca_dev, 0x17, 0x13); - i2c_w1(gspca_dev, 0x18, 0x63); - i2c_w1(gspca_dev, 0x19, 0x01); - i2c_w1(gspca_dev, 0x1a, 0x79); - i2c_w1(gspca_dev, 0x12, 0x40); - sd->hstart = 60; - sd->vstart = 11; - } - break; - case SENSOR_OV9650: - if (mode & MODE_SXGA) { - i2c_w1(gspca_dev, 0x17, 0x1b); - i2c_w1(gspca_dev, 0x18, 0xbc); - i2c_w1(gspca_dev, 0x19, 0x01); - i2c_w1(gspca_dev, 0x1a, 0x82); - i2c_r1(gspca_dev, 0x12, &value); - i2c_w1(gspca_dev, 0x12, value & 0x07); - } else { - i2c_w1(gspca_dev, 0x17, 0x24); - i2c_w1(gspca_dev, 0x18, 0xc5); - i2c_w1(gspca_dev, 0x19, 0x00); - i2c_w1(gspca_dev, 0x1a, 0x3c); - i2c_r1(gspca_dev, 0x12, &value); - i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); - } - break; - case SENSOR_MT9M112: - case SENSOR_MT9M111: - if (mode & MODE_SXGA) { - i2c_w2(gspca_dev, 0xf0, 0x0002); - i2c_w2(gspca_dev, 0xc8, 0x970b); - i2c_w2(gspca_dev, 0xf0, 0x0000); - } else { - i2c_w2(gspca_dev, 0xf0, 0x0002); - i2c_w2(gspca_dev, 0xc8, 0x8000); - i2c_w2(gspca_dev, 0xf0, 0x0000); - } - break; - } -} - -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - struct usb_interface *intf; - u32 flags = gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv; - - /* - * When using the SN9C20X_I420 fmt the sn9c20x needs more bandwidth - * than our regular bandwidth calculations reserve, so we force the - * use of a specific altsetting when using the SN9C20X_I420 fmt. - */ - if (!(flags & (MODE_RAW | MODE_JPEG))) { - intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - - if (intf->num_altsetting != 9) { - pr_warn("sn9c20x camera with unknown number of alt " - "settings (%d), please report!\n", - intf->num_altsetting); - gspca_dev->alt = intf->num_altsetting; - return 0; - } - - switch (gspca_dev->width) { - case 160: /* 160x120 */ - gspca_dev->alt = 2; - break; - case 320: /* 320x240 */ - gspca_dev->alt = 6; - break; - default: /* >= 640x480 */ - gspca_dev->alt = 9; - break; - } - } - - return 0; -} - -#define HW_WIN(mode, hstart, vstart) \ -((const u8 []){hstart, 0, vstart, 0, \ -(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \ -(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)}) - -#define CLR_WIN(width, height) \ -((const u8 [])\ -{0, width >> 2, 0, height >> 1,\ -((width >> 10) & 0x01) | ((height >> 8) & 0x6)}) - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - int width = gspca_dev->width; - int height = gspca_dev->height; - u8 fmt, scale = 0; - - jpeg_define(sd->jpeg_hdr, height, width, - 0x21); - jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); - - if (mode & MODE_RAW) - fmt = 0x2d; - else if (mode & MODE_JPEG) - fmt = 0x24; - else - fmt = 0x2f; /* YUV 420 */ - sd->fmt = fmt; - - switch (mode & SCALE_MASK) { - case SCALE_1280x1024: - scale = 0xc0; - pr_info("Set 1280x1024\n"); - break; - case SCALE_640x480: - scale = 0x80; - pr_info("Set 640x480\n"); - break; - case SCALE_320x240: - scale = 0x90; - pr_info("Set 320x240\n"); - break; - case SCALE_160x120: - scale = 0xa0; - pr_info("Set 160x120\n"); - break; - } - - configure_sensor_output(gspca_dev, mode); - reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); - reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); - reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5); - reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6); - reg_w1(gspca_dev, 0x1189, scale); - reg_w1(gspca_dev, 0x10e0, fmt); - - set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness), - v4l2_ctrl_g_ctrl(sd->contrast), - v4l2_ctrl_g_ctrl(sd->saturation), - v4l2_ctrl_g_ctrl(sd->hue)); - set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); - set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue), - v4l2_ctrl_g_ctrl(sd->red)); - if (sd->gain) - set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); - if (sd->exposure) - set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); - if (sd->hflip) - set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), - v4l2_ctrl_g_ctrl(sd->vflip)); - - reg_w1(gspca_dev, 0x1007, 0x20); - reg_w1(gspca_dev, 0x1061, 0x03); - - /* if JPEG, prepare the compression quality update */ - if (mode & MODE_JPEG) { - sd->pktsz = sd->npkt = 0; - sd->nchg = 0; - sd->work_thread = - create_singlethread_workqueue(KBUILD_MODNAME); - } - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - reg_w1(gspca_dev, 0x1007, 0x00); - reg_w1(gspca_dev, 0x1061, 0x01); -} - -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->work_thread != NULL) { - mutex_unlock(&gspca_dev->usb_lock); - destroy_workqueue(sd->work_thread); - mutex_lock(&gspca_dev->usb_lock); - sd->work_thread = NULL; - } -} - -static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure); - s32 max = sd->exposure->maximum - sd->exposure_step; - s32 min = sd->exposure->minimum + sd->exposure_step; - s16 new_exp; - - /* - * some hardcoded values are present - * like those for maximal/minimal exposure - * and exposure steps - */ - if (avg_lum < MIN_AVG_LUM) { - if (cur_exp > max) - return; - - new_exp = cur_exp + sd->exposure_step; - if (new_exp > max) - new_exp = max; - if (new_exp < min) - new_exp = min; - v4l2_ctrl_s_ctrl(sd->exposure, new_exp); - - sd->older_step = sd->old_step; - sd->old_step = 1; - - if (sd->old_step ^ sd->older_step) - sd->exposure_step /= 2; - else - sd->exposure_step += 2; - } - if (avg_lum > MAX_AVG_LUM) { - if (cur_exp < min) - return; - new_exp = cur_exp - sd->exposure_step; - if (new_exp > max) - new_exp = max; - if (new_exp < min) - new_exp = min; - v4l2_ctrl_s_ctrl(sd->exposure, new_exp); - sd->older_step = sd->old_step; - sd->old_step = 0; - - if (sd->old_step ^ sd->older_step) - sd->exposure_step /= 2; - else - sd->exposure_step += 2; - } -} - -static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain); - - if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum) - v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1); - if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum) - v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1); -} - -static void sd_dqcallback(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int avg_lum; - - if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain)) - return; - - avg_lum = atomic_read(&sd->avg_lum); - if (sd->sensor == SENSOR_SOI968) - do_autogain(gspca_dev, avg_lum); - else - do_autoexposure(gspca_dev, avg_lum); -} - -/* JPEG quality update */ -/* This function is executed from a work queue. */ -static void qual_upd(struct work_struct *work) -{ - struct sd *sd = container_of(work, struct sd, work); - struct gspca_dev *gspca_dev = &sd->gspca_dev; - s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual); - - mutex_lock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, "qual_upd %d%%", qual); - set_quality(gspca_dev, qual); - mutex_unlock(&gspca_dev->usb_lock); -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet */ - int len) /* interrupt packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!(sd->flags & HAS_NO_BUTTON) && len == 1) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - return 0; - } - return -EINVAL; -} -#endif - -/* check the JPEG compression */ -static void transfer_check(struct gspca_dev *gspca_dev, - u8 *data) -{ - struct sd *sd = (struct sd *) gspca_dev; - int new_qual, r; - - new_qual = 0; - - /* if USB error, discard the frame and decrease the quality */ - if (data[6] & 0x08) { /* USB FIFO full */ - gspca_dev->last_packet_type = DISCARD_PACKET; - new_qual = -5; - } else { - - /* else, compute the filling rate and a new JPEG quality */ - r = (sd->pktsz * 100) / - (sd->npkt * - gspca_dev->urb[0]->iso_frame_desc[0].length); - if (r >= 85) - new_qual = -3; - else if (r < 75) - new_qual = 2; - } - if (new_qual != 0) { - sd->nchg += new_qual; - if (sd->nchg < -6 || sd->nchg >= 12) { - /* Note: we are in interrupt context, so we can't - use v4l2_ctrl_g/s_ctrl here. Access the value - directly instead. */ - s32 curqual = sd->jpegqual->cur.val; - sd->nchg = 0; - new_qual += curqual; - if (new_qual < sd->jpegqual->minimum) - new_qual = sd->jpegqual->minimum; - else if (new_qual > sd->jpegqual->maximum) - new_qual = sd->jpegqual->maximum; - if (new_qual != curqual) { - sd->jpegqual->cur.val = new_qual; - queue_work(sd->work_thread, &sd->work); - } - } - } else { - sd->nchg = 0; - } - sd->pktsz = sd->npkt = 0; -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - int avg_lum, is_jpeg; - static const u8 frame_header[] = - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - - is_jpeg = (sd->fmt & 0x03) == 0; - if (len >= 64 && memcmp(data, frame_header, 6) == 0) { - avg_lum = ((data[35] >> 2) & 3) | - (data[20] << 2) | - (data[19] << 10); - avg_lum += ((data[35] >> 4) & 3) | - (data[22] << 2) | - (data[21] << 10); - avg_lum += ((data[35] >> 6) & 3) | - (data[24] << 2) | - (data[23] << 10); - avg_lum += (data[36] & 3) | - (data[26] << 2) | - (data[25] << 10); - avg_lum += ((data[36] >> 2) & 3) | - (data[28] << 2) | - (data[27] << 10); - avg_lum += ((data[36] >> 4) & 3) | - (data[30] << 2) | - (data[29] << 10); - avg_lum += ((data[36] >> 6) & 3) | - (data[32] << 2) | - (data[31] << 10); - avg_lum += ((data[44] >> 4) & 3) | - (data[34] << 2) | - (data[33] << 10); - avg_lum >>= 9; - atomic_set(&sd->avg_lum, avg_lum); - - if (is_jpeg) - transfer_check(gspca_dev, data); - - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - len -= 64; - if (len == 0) - return; - data += 64; - } - if (gspca_dev->last_packet_type == LAST_PACKET) { - if (is_jpeg) { - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, - data, len); - } else { - gspca_frame_add(gspca_dev, FIRST_PACKET, - data, len); - } - } else { - /* if JPEG, count the packets and their size */ - if (is_jpeg) { - sd->npkt++; - sd->pktsz += len; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - } -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = KBUILD_MODNAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif - .dq_callback = sd_dqcallback, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .set_register = sd_dbg_s_register, - .get_register = sd_dbg_g_register, -#endif - .get_chip_ident = sd_chip_ident, -}; - -#define SN9C20X(sensor, i2c_addr, flags) \ - .driver_info = ((flags & 0xff) << 16) \ - | (SENSOR_ ## sensor << 8) \ - | (i2c_addr) - -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, - {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, - {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)}, - {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)}, - {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, - (FLIP_DETECT | HAS_NO_BUTTON))}, - {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, - {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)}, - {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, FLIP_DETECT)}, - {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)}, - {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, - {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)}, - {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)}, - {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, - {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)}, - {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)}, - {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)}, - {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)}, - {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, - {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, - {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)}, - {USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)}, - {USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)}, - {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)}, - {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)}, - {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)}, - {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)}, - {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)}, - {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)}, - {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = KBUILD_MODNAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c deleted file mode 100644 index fd1f8d2d3b0b..000000000000 --- a/drivers/media/video/gspca/sonixb.c +++ /dev/null @@ -1,1493 +0,0 @@ -/* - * sonix sn9c102 (bayer) library - * - * Copyright (C) 2009-2011 Jean-François Moine - * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr - * Add Pas106 Stefano Mozzi (C) 2004 - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* Some documentation on known sonixb registers: - -Reg Use -sn9c101 / sn9c102: -0x10 high nibble red gain low nibble blue gain -0x11 low nibble green gain -sn9c103: -0x05 red gain 0-127 -0x06 blue gain 0-127 -0x07 green gain 0-127 -all: -0x08-0x0f i2c / 3wire registers -0x12 hstart -0x13 vstart -0x15 hsize (hsize = register-value * 16) -0x16 vsize (vsize = register-value * 16) -0x17 bit 0 toggle compression quality (according to sn9c102 driver) -0x18 bit 7 enables compression, bit 4-5 set image down scaling: - 00 scale 1, 01 scale 1/2, 10, scale 1/4 -0x19 high-nibble is sensor clock divider, changes exposure on sensors which - use a clock generated by the bridge. Some sensors have their own clock. -0x1c auto_exposure area (for avg_lum) startx (startx = register-value * 32) -0x1d auto_exposure area (for avg_lum) starty (starty = register-value * 32) -0x1e auto_exposure area (for avg_lum) stopx (hsize = (0x1e - 0x1c) * 32) -0x1f auto_exposure area (for avg_lum) stopy (vsize = (0x1f - 0x1d) * 32) -*/ - -#define MODULE_NAME "sonixb" - -#include -#include "gspca.h" - -MODULE_AUTHOR("Jean-François Moine "); -MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *plfreq; - - atomic_t avg_lum; - int prev_avg_lum; - int exposure_knee; - int header_read; - u8 header[12]; /* Header without sof marker */ - - unsigned char autogain_ignore_frames; - unsigned char frames_to_drop; - - __u8 bridge; /* Type of bridge */ -#define BRIDGE_101 0 -#define BRIDGE_102 0 /* We make no difference between 101 and 102 */ -#define BRIDGE_103 1 - - __u8 sensor; /* Type of image sensor chip */ -#define SENSOR_HV7131D 0 -#define SENSOR_HV7131R 1 -#define SENSOR_OV6650 2 -#define SENSOR_OV7630 3 -#define SENSOR_PAS106 4 -#define SENSOR_PAS202 5 -#define SENSOR_TAS5110C 6 -#define SENSOR_TAS5110D 7 -#define SENSOR_TAS5130CXX 8 - __u8 reg11; -}; - -typedef const __u8 sensor_init_t[8]; - -struct sensor_data { - const __u8 *bridge_init; - sensor_init_t *sensor_init; - int sensor_init_size; - int flags; - __u8 sensor_addr; -}; - -/* sensor_data flags */ -#define F_SIF 0x01 /* sif or vga */ - -/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ -#define MODE_RAW 0x10 /* raw bayer mode */ -#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ - -#define COMP 0xc7 /* 0x87 //0x07 */ -#define COMP1 0xc9 /* 0x89 //0x09 */ - -#define MCK_INIT 0x63 -#define MCK_INIT1 0x20 /*fixme: Bayer - 0x50 for JPEG ??*/ - -#define SYS_CLK 0x04 - -#define SENS(bridge, sensor, _flags, _sensor_addr) \ -{ \ - .bridge_init = bridge, \ - .sensor_init = sensor, \ - .sensor_init_size = sizeof(sensor), \ - .flags = _flags, .sensor_addr = _sensor_addr \ -} - -/* We calculate the autogain at the end of the transfer of a frame, at this - moment a frame with the old settings is being captured and transmitted. So - if we adjust the gain or exposure we must ignore atleast the next frame for - the new settings to come into effect before doing any other adjustments. */ -#define AUTOGAIN_IGNORE_FRAMES 1 - -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2 | MODE_RAW}, - {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 5 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 5 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 5 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format sif_mode[] = { - {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 | MODE_RAW | MODE_REDUCED_SIF}, - {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 5 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 | MODE_REDUCED_SIF}, - {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 | MODE_RAW}, - {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 5 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 5 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 | MODE_REDUCED_SIF}, - {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 5 / 4, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -static const __u8 initHv7131d[] = { - 0x04, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, - 0x28, 0x1e, 0x60, 0x8e, 0x42, -}; -static const __u8 hv7131d_sensor_init[][8] = { - {0xa0, 0x11, 0x01, 0x04, 0x00, 0x00, 0x00, 0x17}, - {0xa0, 0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x17}, - {0xa0, 0x11, 0x28, 0x00, 0x00, 0x00, 0x00, 0x17}, - {0xa0, 0x11, 0x30, 0x30, 0x00, 0x00, 0x00, 0x17}, /* reset level */ - {0xa0, 0x11, 0x34, 0x02, 0x00, 0x00, 0x00, 0x17}, /* pixel bias volt */ -}; - -static const __u8 initHv7131r[] = { - 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, - 0x28, 0x1e, 0x60, 0x8a, 0x20, -}; -static const __u8 hv7131r_sensor_init[][8] = { - {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, - {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10}, - {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10}, - {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16}, - {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15}, -}; -static const __u8 initOv6650[] = { - 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b, - 0x10, -}; -static const __u8 ov6650_sensor_init[][8] = { - /* Bright, contrast, etc are set through SCBB interface. - * AVCAP on win2 do not send any data on this controls. */ - /* Anyway, some registers appears to alter bright and constrat */ - - /* Reset sensor */ - {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, - /* Set clock register 0x11 low nibble is clock divider */ - {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10}, - /* Next some unknown stuff */ - {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10}, -/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10}, - * THIS SET GREEN SCREEN - * (pixels could be innverted in decode kind of "brg", - * but blue wont be there. Avoid this data ... */ - {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */ - {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, - {0xa0, 0x60, 0x30, 0x3d, 0x0a, 0xd8, 0xa4, 0x10}, - /* Enable rgb brightness control */ - {0xa0, 0x60, 0x61, 0x08, 0x00, 0x00, 0x00, 0x10}, - /* HDG: Note windows uses the line below, which sets both register 0x60 - and 0x61 I believe these registers of the ov6650 are identical as - those of the ov7630, because if this is true the windows settings - add a bit additional red gain and a lot additional blue gain, which - matches my findings that the windows settings make blue much too - blue and red a little too red. - {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, */ - /* Some more unknown stuff */ - {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10}, - {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */ -}; - -static const __u8 initOv7630[] = { - 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ - 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ - 0x28, 0x1e, /* H & V sizes r15 .. r16 */ - 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ -}; -static const __u8 ov7630_sensor_init[][8] = { - {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, -/* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ - {0xd0, 0x21, 0x12, 0x5c, 0x00, 0x80, 0x34, 0x10}, /* jfm */ - {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, - {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, - {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, - {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10}, - {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10}, - {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10}, - {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10}, - {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10}, -/* {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, * jfm */ - {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10}, - {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10}, - {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10}, - {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10}, - {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, - {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, -}; - -static const __u8 initPas106[] = { - 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, - 0x16, 0x12, 0x24, COMP1, MCK_INIT1, -}; -/* compression 0x86 mckinit1 0x2b */ - -/* "Known" PAS106B registers: - 0x02 clock divider - 0x03 Variable framerate bits 4-11 - 0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !! - The variable framerate control must never be set lower then 300, - which sets the framerate at 90 / reg02, otherwise vsync is lost. - 0x05 Shutter Time Line Offset, this can be used as an exposure control: - 0 = use full frame time, 255 = no exposure at all - Note this may never be larger then "var-framerate control" / 2 - 2. - When var-framerate control is < 514, no exposure is reached at the max - allowed value for the framerate control value, rather then at 255. - 0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but - only a very little bit, leave at 0xcd - 0x07 offset sign bit (bit0 1 > negative offset) - 0x08 offset - 0x09 Blue Gain - 0x0a Green1 Gain - 0x0b Green2 Gain - 0x0c Red Gain - 0x0e Global gain - 0x13 Write 1 to commit settings to sensor -*/ - -static const __u8 pas106_sensor_init[][8] = { - /* Pixel Clock Divider 6 */ - { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 }, - /* Frame Time MSB (also seen as 0x12) */ - { 0xa1, 0x40, 0x03, 0x13, 0x00, 0x00, 0x00, 0x14 }, - /* Frame Time LSB (also seen as 0x05) */ - { 0xa1, 0x40, 0x04, 0x06, 0x00, 0x00, 0x00, 0x14 }, - /* Shutter Time Line Offset (also seen as 0x6d) */ - { 0xa1, 0x40, 0x05, 0x65, 0x00, 0x00, 0x00, 0x14 }, - /* Shutter Time Pixel Offset (also seen as 0xb1) */ - { 0xa1, 0x40, 0x06, 0xcd, 0x00, 0x00, 0x00, 0x14 }, - /* Black Level Subtract Sign (also seen 0x00) */ - { 0xa1, 0x40, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x14 }, - /* Black Level Subtract Level (also seen 0x01) */ - { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, - { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, - /* Color Gain B Pixel 5 a */ - { 0xa1, 0x40, 0x09, 0x05, 0x00, 0x00, 0x00, 0x14 }, - /* Color Gain G1 Pixel 1 5 */ - { 0xa1, 0x40, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x14 }, - /* Color Gain G2 Pixel 1 0 5 */ - { 0xa1, 0x40, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x14 }, - /* Color Gain R Pixel 3 1 */ - { 0xa1, 0x40, 0x0c, 0x05, 0x00, 0x00, 0x00, 0x14 }, - /* Color GainH Pixel */ - { 0xa1, 0x40, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x14 }, - /* Global Gain */ - { 0xa1, 0x40, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x14 }, - /* Contrast */ - { 0xa1, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14 }, - /* H&V synchro polarity */ - { 0xa1, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x14 }, - /* ?default */ - { 0xa1, 0x40, 0x11, 0x06, 0x00, 0x00, 0x00, 0x14 }, - /* DAC scale */ - { 0xa1, 0x40, 0x12, 0x06, 0x00, 0x00, 0x00, 0x14 }, - /* ?default */ - { 0xa1, 0x40, 0x14, 0x02, 0x00, 0x00, 0x00, 0x14 }, - /* Validate Settings */ - { 0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14 }, -}; - -static const __u8 initPas202[] = { - 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, - 0x28, 0x1e, 0x20, 0x89, 0x20, -}; - -/* "Known" PAS202BCB registers: - 0x02 clock divider - 0x04 Variable framerate bits 6-11 (*) - 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !! - 0x07 Blue Gain - 0x08 Green Gain - 0x09 Red Gain - 0x0b offset sign bit (bit0 1 > negative offset) - 0x0c offset - 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too, - leave at 1 otherwise we get a jump in our exposure control - 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all - 0x10 Master gain 0 - 31 - 0x11 write 1 to apply changes - (*) The variable framerate control must never be set lower then 500 - which sets the framerate at 30 / reg02, otherwise vsync is lost. -*/ -static const __u8 pas202_sensor_init[][8] = { - /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like - to set it lower, but for some reason the bridge starts missing - vsync's then */ - {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, - {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, - {0xd0, 0x40, 0x0c, 0x00, 0x0c, 0x01, 0x32, 0x10}, - {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, - {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, - {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, - {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, - {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, - {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, -}; - -static const __u8 initTas5110c[] = { - 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a, - 0x16, 0x12, 0x60, 0x86, 0x2b, -}; -/* Same as above, except a different hstart */ -static const __u8 initTas5110d[] = { - 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, - 0x16, 0x12, 0x60, 0x86, 0x2b, -}; -/* tas5110c is 3 wire, tas5110d is 2 wire (regular i2c) */ -static const __u8 tas5110c_sensor_init[][8] = { - {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, - {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, -}; -/* Known TAS5110D registers - * reg02: gain, bit order reversed!! 0 == max gain, 255 == min gain - * reg03: bit3: vflip, bit4: ~hflip, bit7: ~gainboost (~ == inverted) - * Note: writing reg03 seems to only work when written together with 02 - */ -static const __u8 tas5110d_sensor_init[][8] = { - {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, /* reset */ -}; - -static const __u8 initTas5130[] = { - 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a, - 0x28, 0x1e, 0x60, COMP, MCK_INIT, -}; -static const __u8 tas5130_sensor_init[][8] = { -/* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10}, - * shutter 0x47 short exposure? */ - {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10}, - /* shutter 0x01 long exposure */ - {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, -}; - -static const struct sensor_data sensor_data[] = { - SENS(initHv7131d, hv7131d_sensor_init, 0, 0), - SENS(initHv7131r, hv7131r_sensor_init, 0, 0), - SENS(initOv6650, ov6650_sensor_init, F_SIF, 0x60), - SENS(initOv7630, ov7630_sensor_init, 0, 0x21), - SENS(initPas106, pas106_sensor_init, F_SIF, 0), - SENS(initPas202, pas202_sensor_init, 0, 0), - SENS(initTas5110c, tas5110c_sensor_init, F_SIF, 0), - SENS(initTas5110d, tas5110d_sensor_init, F_SIF, 0), - SENS(initTas5130, tas5130_sensor_init, 0, 0), -}; - -/* get one byte in gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 value) -{ - int res; - - if (gspca_dev->usb_err < 0) - return; - - res = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, - 0, /* index */ - gspca_dev->usb_buf, 1, - 500); - - if (res < 0) { - dev_err(gspca_dev->v4l2_dev.dev, - "Error reading register %02x: %d\n", value, res); - gspca_dev->usb_err = res; - } -} - -static void reg_w(struct gspca_dev *gspca_dev, - __u16 value, - const __u8 *buffer, - int len) -{ - int res; - - if (gspca_dev->usb_err < 0) - return; - - memcpy(gspca_dev->usb_buf, buffer, len); - res = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, - 0, /* index */ - gspca_dev->usb_buf, len, - 500); - - if (res < 0) { - dev_err(gspca_dev->v4l2_dev.dev, - "Error writing register %02x: %d\n", value, res); - gspca_dev->usb_err = res; - } -} - -static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) -{ - int retry = 60; - - if (gspca_dev->usb_err < 0) - return; - - /* is i2c ready */ - reg_w(gspca_dev, 0x08, buffer, 8); - while (retry--) { - if (gspca_dev->usb_err < 0) - return; - msleep(10); - reg_r(gspca_dev, 0x08); - if (gspca_dev->usb_buf[0] & 0x04) { - if (gspca_dev->usb_buf[0] & 0x08) { - dev_err(gspca_dev->v4l2_dev.dev, - "i2c write error\n"); - gspca_dev->usb_err = -EIO; - } - return; - } - } - - dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n"); - gspca_dev->usb_err = -EIO; -} - -static void i2c_w_vector(struct gspca_dev *gspca_dev, - const __u8 buffer[][8], int len) -{ - for (;;) { - if (gspca_dev->usb_err < 0) - return; - reg_w(gspca_dev, 0x08, *buffer, 8); - len -= 8; - if (len <= 0) - break; - buffer++; - } -} - -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->sensor) { - case SENSOR_OV6650: - case SENSOR_OV7630: { - __u8 i2cOV[] = - {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; - - /* change reg 0x06 */ - i2cOV[1] = sensor_data[sd->sensor].sensor_addr; - i2cOV[3] = sd->brightness->val; - i2c_w(gspca_dev, i2cOV); - break; - } - case SENSOR_PAS106: - case SENSOR_PAS202: { - __u8 i2cpbright[] = - {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16}; - __u8 i2cpdoit[] = - {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; - - /* PAS106 uses reg 7 and 8 instead of b and c */ - if (sd->sensor == SENSOR_PAS106) { - i2cpbright[2] = 7; - i2cpdoit[2] = 0x13; - } - - if (sd->brightness->val < 127) { - /* change reg 0x0b, signreg */ - i2cpbright[3] = 0x01; - /* set reg 0x0c, offset */ - i2cpbright[4] = 127 - sd->brightness->val; - } else - i2cpbright[4] = sd->brightness->val - 127; - - i2c_w(gspca_dev, i2cpbright); - i2c_w(gspca_dev, i2cpdoit); - break; - } - default: - break; - } -} - -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 gain = gspca_dev->gain->val; - - switch (sd->sensor) { - case SENSOR_HV7131D: { - __u8 i2c[] = - {0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17}; - - i2c[3] = 0x3f - gain; - i2c[4] = 0x3f - gain; - i2c[5] = 0x3f - gain; - - i2c_w(gspca_dev, i2c); - break; - } - case SENSOR_TAS5110C: - case SENSOR_TAS5130CXX: { - __u8 i2c[] = - {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; - - i2c[4] = 255 - gain; - i2c_w(gspca_dev, i2c); - break; - } - case SENSOR_TAS5110D: { - __u8 i2c[] = { - 0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 }; - gain = 255 - gain; - /* The bits in the register are the wrong way around!! */ - i2c[3] |= (gain & 0x80) >> 7; - i2c[3] |= (gain & 0x40) >> 5; - i2c[3] |= (gain & 0x20) >> 3; - i2c[3] |= (gain & 0x10) >> 1; - i2c[3] |= (gain & 0x08) << 1; - i2c[3] |= (gain & 0x04) << 3; - i2c[3] |= (gain & 0x02) << 5; - i2c[3] |= (gain & 0x01) << 7; - i2c_w(gspca_dev, i2c); - break; - } - case SENSOR_OV6650: - case SENSOR_OV7630: { - __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - - /* - * The ov7630's gain is weird, at 32 the gain drops to the - * same level as at 16, so skip 32-47 (of the 0-63 scale). - */ - if (sd->sensor == SENSOR_OV7630 && gain >= 32) - gain += 16; - - i2c[1] = sensor_data[sd->sensor].sensor_addr; - i2c[3] = gain; - i2c_w(gspca_dev, i2c); - break; - } - case SENSOR_PAS106: - case SENSOR_PAS202: { - __u8 i2cpgain[] = - {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15}; - __u8 i2cpcolorgain[] = - {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; - __u8 i2cpdoit[] = - {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; - - /* PAS106 uses different regs (and has split green gains) */ - if (sd->sensor == SENSOR_PAS106) { - i2cpgain[2] = 0x0e; - i2cpcolorgain[0] = 0xd0; - i2cpcolorgain[2] = 0x09; - i2cpdoit[2] = 0x13; - } - - i2cpgain[3] = gain; - i2cpcolorgain[3] = gain >> 1; - i2cpcolorgain[4] = gain >> 1; - i2cpcolorgain[5] = gain >> 1; - i2cpcolorgain[6] = gain >> 1; - - i2c_w(gspca_dev, i2cpgain); - i2c_w(gspca_dev, i2cpcolorgain); - i2c_w(gspca_dev, i2cpdoit); - break; - } - default: - if (sd->bridge == BRIDGE_103) { - u8 buf[3] = { gain, gain, gain }; /* R, G, B */ - reg_w(gspca_dev, 0x05, buf, 3); - } else { - u8 buf[2]; - buf[0] = gain << 4 | gain; /* Red and blue */ - buf[1] = gain; /* Green */ - reg_w(gspca_dev, 0x10, buf, 2); - } - } -} - -static void setexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->sensor) { - case SENSOR_HV7131D: { - /* Note the datasheet wrongly says line mode exposure uses reg - 0x26 and 0x27, testing has shown 0x25 + 0x26 */ - __u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17}; - u16 reg = gspca_dev->exposure->val; - - i2c[3] = reg >> 8; - i2c[4] = reg & 0xff; - i2c_w(gspca_dev, i2c); - break; - } - case SENSOR_TAS5110C: - case SENSOR_TAS5110D: { - /* register 19's high nibble contains the sn9c10x clock divider - The high nibble configures the no fps according to the - formula: 60 / high_nibble. With a maximum of 30 fps */ - u8 reg = gspca_dev->exposure->val; - - reg = (reg << 4) | 0x0b; - reg_w(gspca_dev, 0x19, ®, 1); - break; - } - case SENSOR_OV6650: - case SENSOR_OV7630: { - /* The ov6650 / ov7630 have 2 registers which both influence - exposure, register 11, whose low nibble sets the nr off fps - according to: fps = 30 / (low_nibble + 1) - - The fps configures the maximum exposure setting, but it is - possible to use less exposure then what the fps maximum - allows by setting register 10. register 10 configures the - actual exposure as quotient of the full exposure, with 0 - being no exposure at all (not very useful) and reg10_max - being max exposure possible at that framerate. - - The code maps our 0 - 510 ms exposure ctrl to these 2 - registers, trying to keep fps as high as possible. - */ - __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}; - int reg10, reg11, reg10_max; - - /* ov6645 datasheet says reg10_max is 9a, but that uses - tline * 2 * reg10 as formula for calculating texpo, the - ov6650 probably uses the same formula as the 7730 which uses - tline * 4 * reg10, which explains why the reg10max we've - found experimentally for the ov6650 is exactly half that of - the ov6645. The ov7630 datasheet says the max is 0x41. */ - if (sd->sensor == SENSOR_OV6650) { - reg10_max = 0x4d; - i2c[4] = 0xc0; /* OV6650 needs non default vsync pol */ - } else - reg10_max = 0x41; - - reg11 = (15 * gspca_dev->exposure->val + 999) / 1000; - if (reg11 < 1) - reg11 = 1; - else if (reg11 > 16) - reg11 = 16; - - /* In 640x480, if the reg11 has less than 4, the image is - unstable (the bridge goes into a higher compression mode - which we have not reverse engineered yet). */ - if (gspca_dev->width == 640 && reg11 < 4) - reg11 = 4; - - /* frame exposure time in ms = 1000 * reg11 / 30 -> - reg10 = (gspca_dev->exposure->val / 2) * reg10_max - / (1000 * reg11 / 30) */ - reg10 = (gspca_dev->exposure->val * 15 * reg10_max) - / (1000 * reg11); - - /* Don't allow this to get below 10 when using autogain, the - steps become very large (relatively) when below 10 causing - the image to oscilate from much too dark, to much too bright - and back again. */ - if (gspca_dev->autogain->val && reg10 < 10) - reg10 = 10; - else if (reg10 > reg10_max) - reg10 = reg10_max; - - /* Write reg 10 and reg11 low nibble */ - i2c[1] = sensor_data[sd->sensor].sensor_addr; - i2c[3] = reg10; - i2c[4] |= reg11 - 1; - - /* If register 11 didn't change, don't change it */ - if (sd->reg11 == reg11) - i2c[0] = 0xa0; - - i2c_w(gspca_dev, i2c); - if (gspca_dev->usb_err == 0) - sd->reg11 = reg11; - break; - } - case SENSOR_PAS202: { - __u8 i2cpframerate[] = - {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16}; - __u8 i2cpexpo[] = - {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16}; - const __u8 i2cpdoit[] = - {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; - int framerate_ctrl; - - /* The exposure knee for the autogain algorithm is 200 - (100 ms / 10 fps on other sensors), for values below this - use the control for setting the partial frame expose time, - above that use variable framerate. This way we run at max - framerate (640x480@7.5 fps, 320x240@10fps) until the knee - is reached. Using the variable framerate control above 200 - is better then playing around with both clockdiv + partial - frame exposure times (like we are doing with the ov chips), - as that sometimes leads to jumps in the exposure control, - which are bad for auto exposure. */ - if (gspca_dev->exposure->val < 200) { - i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255) - / 200; - framerate_ctrl = 500; - } else { - /* The PAS202's exposure control goes from 0 - 4095, - but anything below 500 causes vsync issues, so scale - our 200-1023 to 500-4095 */ - framerate_ctrl = (gspca_dev->exposure->val - 200) - * 1000 / 229 + 500; - } - - i2cpframerate[3] = framerate_ctrl >> 6; - i2cpframerate[4] = framerate_ctrl & 0x3f; - i2c_w(gspca_dev, i2cpframerate); - i2c_w(gspca_dev, i2cpexpo); - i2c_w(gspca_dev, i2cpdoit); - break; - } - case SENSOR_PAS106: { - __u8 i2cpframerate[] = - {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14}; - __u8 i2cpexpo[] = - {0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14}; - const __u8 i2cpdoit[] = - {0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14}; - int framerate_ctrl; - - /* For values below 150 use partial frame exposure, above - that use framerate ctrl */ - if (gspca_dev->exposure->val < 150) { - i2cpexpo[3] = 150 - gspca_dev->exposure->val; - framerate_ctrl = 300; - } else { - /* The PAS106's exposure control goes from 0 - 4095, - but anything below 300 causes vsync issues, so scale - our 150-1023 to 300-4095 */ - framerate_ctrl = (gspca_dev->exposure->val - 150) - * 1000 / 230 + 300; - } - - i2cpframerate[3] = framerate_ctrl >> 4; - i2cpframerate[4] = framerate_ctrl & 0x0f; - i2c_w(gspca_dev, i2cpframerate); - i2c_w(gspca_dev, i2cpexpo); - i2c_w(gspca_dev, i2cpdoit); - break; - } - default: - break; - } -} - -static void setfreq(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) { - /* Framerate adjust register for artificial light 50 hz flicker - compensation, for the ov6650 this is identical to ov6630 - 0x2b register, see ov6630 datasheet. - 0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */ - __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; - switch (sd->plfreq->val) { - default: -/* case 0: * no filter*/ -/* case 2: * 60 hz */ - i2c[3] = 0; - break; - case 1: /* 50 hz */ - i2c[3] = (sd->sensor == SENSOR_OV6650) - ? 0x4f : 0x8a; - break; - } - i2c[1] = sensor_data[sd->sensor].sensor_addr; - i2c_w(gspca_dev, i2c); - } -} - -static void do_autogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int deadzone, desired_avg_lum, avg_lum; - - avg_lum = atomic_read(&sd->avg_lum); - if (avg_lum == -1) - return; - - if (sd->autogain_ignore_frames > 0) { - sd->autogain_ignore_frames--; - return; - } - - /* SIF / VGA sensors have a different autoexposure area and thus - different avg_lum values for the same picture brightness */ - if (sensor_data[sd->sensor].flags & F_SIF) { - deadzone = 500; - /* SIF sensors tend to overexpose, so keep this small */ - desired_avg_lum = 5000; - } else { - deadzone = 1500; - desired_avg_lum = 13000; - } - - if (sd->brightness) - desired_avg_lum = sd->brightness->val * desired_avg_lum / 127; - - if (gspca_dev->exposure->maximum < 500) { - if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, - desired_avg_lum, deadzone)) - sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; - } else { - int gain_knee = gspca_dev->gain->maximum * 9 / 10; - if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum, - deadzone, gain_knee, sd->exposure_knee)) - sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; - } -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - reg_r(gspca_dev, 0x00); - if (gspca_dev->usb_buf[0] != 0x10) - return -ENODEV; - - /* copy the webcam info from the device id */ - sd->sensor = id->driver_info >> 8; - sd->bridge = id->driver_info & 0xff; - - cam = &gspca_dev->cam; - if (!(sensor_data[sd->sensor].flags & F_SIF)) { - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - } else { - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); - } - cam->npkt = 36; /* 36 packets per ISOC message */ - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - const __u8 stop = 0x09; /* Disable stream turn of LED */ - - reg_w(gspca_dev, 0x01, &stop, 1); - - return gspca_dev->usb_err; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - gspca_dev->gain->val = gspca_dev->gain->default_value; - gspca_dev->exposure->val = gspca_dev->exposure->default_value; - sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; - } - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev); - break; - case V4L2_CID_AUTOGAIN: - if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) - setexposure(gspca_dev); - if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) - setgain(gspca_dev); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setfreq(gspca_dev); - break; - default: - return -EINVAL; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -/* this function is called at probe time */ -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - - if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630 || - sd->sensor == SENSOR_PAS106 || sd->sensor == SENSOR_PAS202) - sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); - - /* Gain range is sensor dependent */ - switch (sd->sensor) { - case SENSOR_OV6650: - case SENSOR_PAS106: - case SENSOR_PAS202: - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 31, 1, 15); - break; - case SENSOR_OV7630: - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 47, 1, 31); - break; - case SENSOR_HV7131D: - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 63, 1, 31); - break; - case SENSOR_TAS5110C: - case SENSOR_TAS5110D: - case SENSOR_TAS5130CXX: - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 255, 1, 127); - break; - default: - if (sd->bridge == BRIDGE_103) { - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 63); - } else { - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 15, 1, 7); - } - } - - /* Exposure range is sensor dependent, and not all have exposure */ - switch (sd->sensor) { - case SENSOR_HV7131D: - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 8191, 1, 482); - sd->exposure_knee = 964; - break; - case SENSOR_OV6650: - case SENSOR_OV7630: - case SENSOR_PAS106: - case SENSOR_PAS202: - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 1023, 1, 66); - sd->exposure_knee = 200; - break; - case SENSOR_TAS5110C: - case SENSOR_TAS5110D: - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 2, 15, 1, 2); - break; - } - - if (gspca_dev->exposure) { - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - } - - if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) - sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - - if (gspca_dev->autogain) - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - int i, mode; - __u8 regs[0x31]; - - mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07; - /* Copy registers 0x01 - 0x19 from the template */ - memcpy(®s[0x01], sensor_data[sd->sensor].bridge_init, 0x19); - /* Set the mode */ - regs[0x18] |= mode << 4; - - /* Set bridge gain to 1.0 */ - if (sd->bridge == BRIDGE_103) { - regs[0x05] = 0x20; /* Red */ - regs[0x06] = 0x20; /* Green */ - regs[0x07] = 0x20; /* Blue */ - } else { - regs[0x10] = 0x00; /* Red and blue */ - regs[0x11] = 0x00; /* Green */ - } - - /* Setup pixel numbers and auto exposure window */ - if (sensor_data[sd->sensor].flags & F_SIF) { - regs[0x1a] = 0x14; /* HO_SIZE 640, makes no sense */ - regs[0x1b] = 0x0a; /* VO_SIZE 320, makes no sense */ - regs[0x1c] = 0x02; /* AE H-start 64 */ - regs[0x1d] = 0x02; /* AE V-start 64 */ - regs[0x1e] = 0x09; /* AE H-end 288 */ - regs[0x1f] = 0x07; /* AE V-end 224 */ - } else { - regs[0x1a] = 0x1d; /* HO_SIZE 960, makes no sense */ - regs[0x1b] = 0x10; /* VO_SIZE 512, makes no sense */ - regs[0x1c] = 0x05; /* AE H-start 160 */ - regs[0x1d] = 0x03; /* AE V-start 96 */ - regs[0x1e] = 0x0f; /* AE H-end 480 */ - regs[0x1f] = 0x0c; /* AE V-end 384 */ - } - - /* Setup the gamma table (only used with the sn9c103 bridge) */ - for (i = 0; i < 16; i++) - regs[0x20 + i] = i * 16; - regs[0x20 + i] = 255; - - /* Special cases where some regs depend on mode or bridge */ - switch (sd->sensor) { - case SENSOR_TAS5130CXX: - /* FIXME / TESTME - probably not mode specific at all most likely the upper - nibble of 0x19 is exposure (clock divider) just as with - the tas5110, we need someone to test this. */ - regs[0x19] = mode ? 0x23 : 0x43; - break; - case SENSOR_OV7630: - /* FIXME / TESTME for some reason with the 101/102 bridge the - clock is set to 12 Mhz (reg1 == 0x04), rather then 24. - Also the hstart needs to go from 1 to 2 when using a 103, - which is likely related. This does not seem right. */ - if (sd->bridge == BRIDGE_103) { - regs[0x01] = 0x44; /* Select 24 Mhz clock */ - regs[0x12] = 0x02; /* Set hstart to 2 */ - } - } - /* Disable compression when the raw bayer format has been selected */ - if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) - regs[0x18] &= ~0x80; - - /* Vga mode emulation on SIF sensor? */ - if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) { - regs[0x12] += 16; /* hstart adjust */ - regs[0x13] += 24; /* vstart adjust */ - regs[0x15] = 320 / 16; /* hsize */ - regs[0x16] = 240 / 16; /* vsize */ - } - - /* reg 0x01 bit 2 video transfert on */ - reg_w(gspca_dev, 0x01, ®s[0x01], 1); - /* reg 0x17 SensorClk enable inv Clk 0x60 */ - reg_w(gspca_dev, 0x17, ®s[0x17], 1); - /* Set the registers from the template */ - reg_w(gspca_dev, 0x01, ®s[0x01], - (sd->bridge == BRIDGE_103) ? 0x30 : 0x1f); - - /* Init the sensor */ - i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init, - sensor_data[sd->sensor].sensor_init_size); - - /* Mode / bridge specific sensor setup */ - switch (sd->sensor) { - case SENSOR_PAS202: { - const __u8 i2cpclockdiv[] = - {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}; - /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */ - if (mode) - i2c_w(gspca_dev, i2cpclockdiv); - break; - } - case SENSOR_OV7630: - /* FIXME / TESTME We should be able to handle this identical - for the 101/102 and the 103 case */ - if (sd->bridge == BRIDGE_103) { - const __u8 i2c[] = { 0xa0, 0x21, 0x13, - 0x80, 0x00, 0x00, 0x00, 0x10 }; - i2c_w(gspca_dev, i2c); - } - break; - } - /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ - reg_w(gspca_dev, 0x15, ®s[0x15], 2); - /* compression register */ - reg_w(gspca_dev, 0x18, ®s[0x18], 1); - /* H_start */ - reg_w(gspca_dev, 0x12, ®s[0x12], 1); - /* V_START */ - reg_w(gspca_dev, 0x13, ®s[0x13], 1); - /* reset 0x17 SensorClk enable inv Clk 0x60 */ - /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ - reg_w(gspca_dev, 0x17, ®s[0x17], 1); - /*MCKSIZE ->3 */ /*fixme: not ov7630*/ - reg_w(gspca_dev, 0x19, ®s[0x19], 1); - /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ - reg_w(gspca_dev, 0x1c, ®s[0x1c], 4); - /* Enable video transfert */ - reg_w(gspca_dev, 0x01, ®s[0x01], 1); - /* Compression */ - reg_w(gspca_dev, 0x18, ®s[0x18], 2); - msleep(20); - - sd->reg11 = -1; - - setgain(gspca_dev); - setbrightness(gspca_dev); - setexposure(gspca_dev); - setfreq(gspca_dev); - - sd->frames_to_drop = 0; - sd->autogain_ignore_frames = 0; - gspca_dev->exp_too_high_cnt = 0; - gspca_dev->exp_too_low_cnt = 0; - atomic_set(&sd->avg_lum, -1); - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - sd_init(gspca_dev); -} - -static u8* find_sof(struct gspca_dev *gspca_dev, u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, header_size = (sd->bridge == BRIDGE_103) ? 18 : 12; - - /* frames start with: - * ff ff 00 c4 c4 96 synchro - * 00 (unknown) - * xx (frame sequence / size / compression) - * (xx) (idem - extra byte for sn9c103) - * ll mm brightness sum inside auto exposure - * ll mm brightness sum outside auto exposure - * (xx xx xx xx xx) audio values for snc103 - */ - for (i = 0; i < len; i++) { - switch (sd->header_read) { - case 0: - if (data[i] == 0xff) - sd->header_read++; - break; - case 1: - if (data[i] == 0xff) - sd->header_read++; - else - sd->header_read = 0; - break; - case 2: - if (data[i] == 0x00) - sd->header_read++; - else if (data[i] != 0xff) - sd->header_read = 0; - break; - case 3: - if (data[i] == 0xc4) - sd->header_read++; - else if (data[i] == 0xff) - sd->header_read = 1; - else - sd->header_read = 0; - break; - case 4: - if (data[i] == 0xc4) - sd->header_read++; - else if (data[i] == 0xff) - sd->header_read = 1; - else - sd->header_read = 0; - break; - case 5: - if (data[i] == 0x96) - sd->header_read++; - else if (data[i] == 0xff) - sd->header_read = 1; - else - sd->header_read = 0; - break; - default: - sd->header[sd->header_read - 6] = data[i]; - sd->header_read++; - if (sd->header_read == header_size) { - sd->header_read = 0; - return data + i + 1; - } - } - } - return NULL; -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - int fr_h_sz = 0, lum_offset = 0, len_after_sof = 0; - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - u8 *sof; - - sof = find_sof(gspca_dev, data, len); - if (sof) { - if (sd->bridge == BRIDGE_103) { - fr_h_sz = 18; - lum_offset = 3; - } else { - fr_h_sz = 12; - lum_offset = 2; - } - - len_after_sof = len - (sof - data); - len = (sof - data) - fr_h_sz; - if (len < 0) - len = 0; - } - - if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { - /* In raw mode we sometimes get some garbage after the frame - ignore this */ - int used; - int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; - - used = gspca_dev->image_len; - if (used + len > size) - len = size - used; - } - - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - - if (sof) { - int lum = sd->header[lum_offset] + - (sd->header[lum_offset + 1] << 8); - - /* When exposure changes midway a frame we - get a lum of 0 in this case drop 2 frames - as the frames directly after an exposure - change have an unstable image. Sometimes lum - *really* is 0 (cam used in low light with - low exposure setting), so do not drop frames - if the previous lum was 0 too. */ - if (lum == 0 && sd->prev_avg_lum != 0) { - lum = -1; - sd->frames_to_drop = 2; - sd->prev_avg_lum = 0; - } else - sd->prev_avg_lum = lum; - atomic_set(&sd->avg_lum, lum); - - if (sd->frames_to_drop) - sd->frames_to_drop--; - else - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - - gspca_frame_add(gspca_dev, FIRST_PACKET, sof, len_after_sof); - } -} - -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu) -{ - switch (menu->id) { - case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; - } - return -EINVAL; -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet data */ - int len) /* interrupt packet length */ -{ - int ret = -EINVAL; - - if (len == 1 && data[0] == 1) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - ret = 0; - } - - return ret; -} -#endif - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .querymenu = sd_querymenu, - .dq_callback = do_autogain, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -/* -- module initialisation -- */ -#define SB(sensor, bridge) \ - .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge - - -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */ - {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */ - {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */ - {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, - {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, - {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, - {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, - {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, -#endif - {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, - {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, - {USB_DEVICE(0x0c45, 0x602a), SB(HV7131D, 102)}, - /* {USB_DEVICE(0x0c45, 0x602b), SB(MI0343, 102)}, */ - {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, - {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, - {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)}, - /* {USB_DEVICE(0x0c45, 0x6030), SB(MI03XX, 102)}, */ /* MI0343 MI0360 MI0330 */ - /* {USB_DEVICE(0x0c45, 0x6082), SB(MI03XX, 103)}, */ /* MI0343 MI0360 */ - {USB_DEVICE(0x0c45, 0x6083), SB(HV7131D, 103)}, - {USB_DEVICE(0x0c45, 0x608c), SB(HV7131R, 103)}, - /* {USB_DEVICE(0x0c45, 0x608e), SB(CISVF10, 103)}, */ - {USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)}, - {USB_DEVICE(0x0c45, 0x60a8), SB(PAS106, 103)}, - {USB_DEVICE(0x0c45, 0x60aa), SB(TAS5130CXX, 103)}, - {USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)}, - {USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c deleted file mode 100644 index 150b2df40f7f..000000000000 --- a/drivers/media/video/gspca/sonixj.c +++ /dev/null @@ -1,3206 +0,0 @@ -/* - * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver - * - * Copyright (C) 2009-2011 Jean-François Moine - * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "sonixj" - -#include -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Jean-François Moine "); -MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - COLORS, - BLUE, - RED, - GAMMA, - EXPOSURE, - AUTOGAIN, - GAIN, - HFLIP, - VFLIP, - SHARPNESS, - ILLUM, - FREQ, - NCTRLS /* number of controls */ -}; - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct gspca_ctrl ctrls[NCTRLS]; - - atomic_t avg_lum; - u32 exposure; - - struct work_struct work; - struct workqueue_struct *work_thread; - - u32 pktsz; /* (used by pkt_scan) */ - u16 npkt; - s8 nchg; - s8 short_mark; - - u8 quality; /* image quality */ -#define QUALITY_MIN 25 -#define QUALITY_MAX 90 -#define QUALITY_DEF 70 - - u8 reg01; - u8 reg17; - u8 reg18; - u8 flags; - - s8 ag_cnt; -#define AG_CNT_START 13 - - u8 bridge; -#define BRIDGE_SN9C102P 0 -#define BRIDGE_SN9C105 1 -#define BRIDGE_SN9C110 2 -#define BRIDGE_SN9C120 3 - u8 sensor; /* Type of image sensor chip */ - u8 i2c_addr; - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; -enum sensors { - SENSOR_ADCM1700, - SENSOR_GC0307, - SENSOR_HV7131R, - SENSOR_MI0360, - SENSOR_MI0360B, - SENSOR_MO4000, - SENSOR_MT9V111, - SENSOR_OM6802, - SENSOR_OV7630, - SENSOR_OV7648, - SENSOR_OV7660, - SENSOR_PO1030, - SENSOR_PO2030N, - SENSOR_SOI768, - SENSOR_SP80708, -}; - -static void qual_upd(struct work_struct *work); - -/* device flags */ -#define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */ -#define F_ILLUM 0x02 /* presence of illuminator */ - -/* sn9c1xx definitions */ -/* register 0x01 */ -#define S_PWR_DN 0x01 /* sensor power down */ -#define S_PDN_INV 0x02 /* inverse pin S_PWR_DN */ -#define V_TX_EN 0x04 /* video transfer enable */ -#define LED 0x08 /* output to pin LED */ -#define SCL_SEL_OD 0x20 /* open-drain mode */ -#define SYS_SEL_48M 0x40 /* system clock 0: 24MHz, 1: 48MHz */ -/* register 0x17 */ -#define MCK_SIZE_MASK 0x1f /* sensor master clock */ -#define SEN_CLK_EN 0x20 /* enable sensor clock */ -#define DEF_EN 0x80 /* defect pixel by 0: soft, 1: hard */ - -/* V4L2 controls supported by the driver */ -static void setbrightness(struct gspca_dev *gspca_dev); -static void setcontrast(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); -static void setredblue(struct gspca_dev *gspca_dev); -static void setgamma(struct gspca_dev *gspca_dev); -static void setexposure(struct gspca_dev *gspca_dev); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static void setgain(struct gspca_dev *gspca_dev); -static void sethvflip(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static void setillum(struct gspca_dev *gspca_dev); -static void setfreq(struct gspca_dev *gspca_dev); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x80, - }, - .set_control = setbrightness - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, -#define CONTRAST_MAX 127 - .maximum = CONTRAST_MAX, - .step = 1, - .default_value = 20, - }, - .set_control = setcontrast - }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 40, - .step = 1, -#define COLORS_DEF 25 - .default_value = COLORS_DEF, - }, - .set_control = setcolors - }, -[BLUE] = { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 24, - .maximum = 40, - .step = 1, - .default_value = 32, - }, - .set_control = setredblue - }, -[RED] = { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 24, - .maximum = 40, - .step = 1, - .default_value = 32, - }, - .set_control = setredblue - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 40, - .step = 1, -#define GAMMA_DEF 20 - .default_value = GAMMA_DEF, - }, - .set_control = setgamma - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 500, - .maximum = 1500, - .step = 1, - .default_value = 1024 - }, - .set_control = setexposure - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = sd_setautogain, - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 4, - .maximum = 49, - .step = 1, - .default_value = 15 - }, - .set_control = setgain - }, -[HFLIP] = { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = sethvflip - }, -[VFLIP] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = sethvflip - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 90, - }, - .set_control = setsharpness - }, -[ILLUM] = { - { - .id = V4L2_CID_ILLUMINATORS_1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Illuminator / infrared", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = setillum - }, -/* ov7630/ov7648/ov7660 only */ -[FREQ] = { - { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Light frequency filter", - .minimum = 0, - .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ - .step = 1, - .default_value = 1, - }, - .set_control = setfreq - }, -}; - -/* table of the disabled controls */ -static const __u32 ctrl_dis[] = { -[SENSOR_ADCM1700] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_GC0307] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_HV7131R] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << FREQ), - -[SENSOR_MI0360] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_MI0360B] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_MO4000] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_MT9V111] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_OM6802] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_OV7630] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP), - -[SENSOR_OV7648] = (1 << EXPOSURE) | - (1 << GAIN) | - (1 << HFLIP), - -[SENSOR_OV7660] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP), - -[SENSOR_PO1030] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_PO2030N] = (1 << FREQ), - -[SENSOR_SOI768] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), - -[SENSOR_SP80708] = (1 << EXPOSURE) | - (1 << AUTOGAIN) | - (1 << GAIN) | - (1 << HFLIP) | - (1 << VFLIP) | - (1 << FREQ), -}; - -static const struct v4l2_pix_format cif_mode[] = { - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */ - .sizeimage = 640 * 480 * 3 / 4 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -static const u8 sn_adcm1700[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42, -/* reg18 reg19 reg1a reg1b */ - 0x06, 0x00, 0x00, 0x00 -}; - -static const u8 sn_gc0307[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x62, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x03, 0x01, 0x08, 0x28, 0x1e, 0x02, -/* reg18 reg19 reg1a reg1b */ - 0x06, 0x00, 0x00, 0x00 -}; - -static const u8 sn_hv7131[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x03, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, -/* reg18 reg19 reg1a reg1b */ - 0x0a, 0x00, 0x00, 0x00 -}; - -static const u8 sn_mi0360[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, -/* reg18 reg19 reg1a reg1b */ - 0x06, 0x00, 0x00, 0x00 -}; - -static const u8 sn_mi0360b[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x40, -/* reg18 reg19 reg1a reg1b */ - 0x06, 0x00, 0x00, 0x00 -}; - -static const u8 sn_mo4000[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40, -/* reg18 reg19 reg1a reg1b */ - 0x08, 0x00, 0x00, 0x00 -}; - -static const u8 sn_mt9v111[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40, -/* reg18 reg19 reg1a reg1b */ - 0x06, 0x00, 0x00, 0x00 -}; - -static const u8 sn_om6802[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40, -/* reg18 reg19 reg1a reg1b */ - 0x05, 0x00, 0x00, 0x00 -}; - -static const u8 sn_ov7630[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, -/* reg18 reg19 reg1a reg1b */ - 0x0b, 0x00, 0x00, 0x00 -}; - -static const u8 sn_ov7648[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, -/* reg18 reg19 reg1a reg1b */ - 0x0b, 0x00, 0x00, 0x00 -}; - -static const u8 sn_ov7660[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20, -/* reg18 reg19 reg1a reg1b */ - 0x07, 0x00, 0x00, 0x00 -}; - -static const u8 sn_po1030[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00, -/* reg18 reg19 reg1a reg1b */ - 0x07, 0x00, 0x00, 0x00 -}; - -static const u8 sn_po2030n[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x63, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x01, 0x14, 0x28, 0x1e, 0x00, -/* reg18 reg19 reg1a reg1b */ - 0x07, 0x00, 0x00, 0x00 -}; - -static const u8 sn_soi768[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x01, 0x08, 0x28, 0x1e, 0x00, -/* reg18 reg19 reg1a reg1b */ - 0x07, 0x00, 0x00, 0x00 -}; - -static const u8 sn_sp80708[0x1c] = { -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00, -/* reg18 reg19 reg1a reg1b */ - 0x07, 0x00, 0x00, 0x00 -}; - -/* sequence specific to the sensors - !! index = SENSOR_xxx */ -static const u8 *sn_tb[] = { -[SENSOR_ADCM1700] = sn_adcm1700, -[SENSOR_GC0307] = sn_gc0307, -[SENSOR_HV7131R] = sn_hv7131, -[SENSOR_MI0360] = sn_mi0360, -[SENSOR_MI0360B] = sn_mi0360b, -[SENSOR_MO4000] = sn_mo4000, -[SENSOR_MT9V111] = sn_mt9v111, -[SENSOR_OM6802] = sn_om6802, -[SENSOR_OV7630] = sn_ov7630, -[SENSOR_OV7648] = sn_ov7648, -[SENSOR_OV7660] = sn_ov7660, -[SENSOR_PO1030] = sn_po1030, -[SENSOR_PO2030N] = sn_po2030n, -[SENSOR_SOI768] = sn_soi768, -[SENSOR_SP80708] = sn_sp80708, -}; - -/* default gamma table */ -static const u8 gamma_def[17] = { - 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, - 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff -}; -/* gamma for sensor ADCM1700 */ -static const u8 gamma_spec_0[17] = { - 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4, - 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5 -}; -/* gamma for sensors HV7131R and MT9V111 */ -static const u8 gamma_spec_1[17] = { - 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d, - 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5 -}; -/* gamma for sensor GC0307 */ -static const u8 gamma_spec_2[17] = { - 0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab, - 0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb -}; -/* gamma for sensor SP80708 */ -static const u8 gamma_spec_3[17] = { - 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab, - 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6 -}; - -/* color matrix and offsets */ -static const u8 reg84[] = { - 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */ - 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */ - 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */ - 0x00, 0x00, 0x00 /* YUV offsets */ -}; - -#define DELAY 0xdd - -static const u8 adcm1700_sensor_init[][8] = { - {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */ - {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 adcm1700_sensor_param1[][8] = { - {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */ - {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10}, - - {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10}, - {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */ - - {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10}, - {} -}; -static const u8 gc0307_sensor_init[][8] = { - {0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/ - {0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 gc0307_sensor_param1[][8] = { - {0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10}, - {0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10}, - {0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10}, - {0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10}, -/*param3*/ - {0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10}, - {} -}; - -static const u8 hv7131r_sensor_init[][8] = { - {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, - {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10}, - {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10}, -/* {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, */ - {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10}, -/* {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, */ - - {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xc1, 0x11, 0x25, 0x00, 0x61, 0xa8, 0x00, 0x10}, - {0xa1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, - {0xc1, 0x11, 0x31, 0x20, 0x2e, 0x20, 0x00, 0x10}, - {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10}, - {0xa1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */ - {0xc1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */ - - {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, - - {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10}, - /* set sensor clock */ - {} -}; -static const u8 mi0360_sensor_init[][8] = { - {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, - {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10}, - {0xd1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, - {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, - {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10}, - {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, - {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10}, - {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, - {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10}, - {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, - - {0xb1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x2b, 0x00, 0xa0, 0x00, 0xb0, 0x10}, - {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0xa0, 0x10}, - - {0xb1, 0x5d, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */ - {0xb1, 0x5d, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ - - {0xd1, 0x5d, 0x2b, 0x00, 0xb9, 0x00, 0xe3, 0x10}, - {0xd1, 0x5d, 0x2d, 0x00, 0x5f, 0x00, 0xb9, 0x10}, /* 42 */ -/* {0xb1, 0x5d, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */ -/* {0xb1, 0x5d, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */ - {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ - {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ - {} -}; -static const u8 mi0360b_sensor_init[][8] = { - {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/ - {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/ - {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, - {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10}, - {0xd1, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, - {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10}, - {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, - {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10}, - {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, - {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10}, - {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, - - {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, - {0xd1, 0x5d, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10}, - {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10}, - {} -}; -static const u8 mi0360b_sensor_param1[][8] = { - {0xb1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x06, 0x00, 0x53, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, - {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ - - {0xd1, 0x5d, 0x2b, 0x00, 0xd1, 0x01, 0xc9, 0x10}, - {0xd1, 0x5d, 0x2d, 0x00, 0xed, 0x00, 0xd1, 0x10}, - {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ - {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ - {} -}; -static const u8 mo4000_sensor_init[][8] = { - {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 mt9v111_sensor_init[][8] = { - {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */ - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ - {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */ - {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */ - {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */ - {0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */ - {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */ - {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */ - {0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */ - {0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */ - {0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */ - {0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */ - {0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */ - {0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */ - {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */ - {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */ - {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 mt9v111_sensor_param1[][8] = { - {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xad, 0x10}, /* G1 and B gains */ - {0xd1, 0x5c, 0x2d, 0x00, 0xad, 0x00, 0x33, 0x10}, /* R and G2 gains */ - {0xb1, 0x5c, 0x06, 0x00, 0x40, 0x00, 0x00, 0x10}, /* vert blanking */ - {0xb1, 0x5c, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, /* horiz blanking */ - {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */ - {} -}; -static const u8 om6802_init0[2][8] = { -/*fixme: variable*/ - {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10}, -}; -static const u8 om6802_sensor_init[][8] = { - {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10}, - /* factory mode */ - {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10}, - /* output raw RGB */ - {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, -/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */ - {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10}, - /* auto-exposure speed (0) / white balance mode (auto RGB) */ -/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10}, - * set color mode */ -/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10}, - * max AGC value in AE */ -/* {0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10}, - * preset AGC */ -/* {0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10}, - * preset brightness */ -/* {0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10}, - * preset contrast */ -/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10}, - * preset gamma */ - {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10}, - /* luminance mode (0x4f -> AutoExpo on) */ - {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10}, - /* preset shutter */ -/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10}, - * auto frame rate */ -/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */ - {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 om6802_sensor_param1[][8] = { - {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 ov7630_sensor_init[][8] = { - {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ - {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ - {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, -/* win: i2c_r from 00 to 80 */ - {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, - {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10}, -/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30) - 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */ - {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10}, - {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, - {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10}, - {0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10}, - {0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10}, - {0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10}, - {0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10}, - {0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10}, - {0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10}, - {0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10}, - {0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10}, - {0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10}, - {0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10}, - {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10}, - {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, - {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 ov7630_sensor_param1[][8] = { - {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, -/*fixme: + 0x12, 0x04*/ -/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN - * set by setvflip */ - {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10}, -/* */ -/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ -/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ -/* */ - {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10}, -/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ - {} -}; - -static const u8 ov7648_sensor_init[][8] = { - {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ - {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10}, - {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10}, - {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10}, - {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10}, - {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10}, - {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10}, - {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10}, - {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10}, - {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10}, - {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10}, - {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10}, - {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10}, - {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10}, - {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10}, - {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10}, - - {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10}, -/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ -/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ -/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */ - {} -}; -static const u8 ov7648_sensor_param1[][8] = { -/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ -/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN - * set by setvflip */ - {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, -/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ -/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */ -/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */ -/*...*/ - {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */ -/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ -/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ -/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */ -/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ -/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */ - - {} -}; - -static const u8 ov7660_sensor_init[][8] = { - {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ - {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, - /* Outformat = rawRGB */ - {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ - {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, - /* GAIN BLUE RED VREF */ - {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, - /* COM 1 BAVE GEAVE AECHH */ - {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */ - {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */ - {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, - /* AECH CLKRC COM7 COM8 */ - {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */ - {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10}, - /* HSTART HSTOP VSTRT VSTOP */ - {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */ - {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */ - {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10}, - /* BOS GBOS GROS ROS (BGGR offset) */ -/* {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, */ - {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10}, - /* AEW AEB VPT BBIAS */ - {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10}, - /* GbBIAS RSVD EXHCH EXHCL */ - {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10}, - /* RBIAS ADVFL ASDVFH YAVE */ - {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10}, - /* HSYST HSYEN HREF */ - {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */ - {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10}, - /* ADC ACOM OFON TSLB */ - {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10}, - /* COM11 COM12 COM13 COM14 */ - {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10}, - /* EDGE COM15 COM16 COM17 */ - {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */ - {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */ - {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */ - {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */ - {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */ - {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */ - {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */ - {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */ - {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */ - {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10}, - /* LCC1 LCC2 LCC3 LCC4 */ - {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */ - {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, /* MANU */ - {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10}, - /* band gap reference [0:3] DBLV */ - {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */ - {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */ - {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */ - {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */ - {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */ - {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */ - {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */ - {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ - {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ - {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */ -/* not in all ms-win traces*/ - {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 ov7660_sensor_param1[][8] = { - {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */ - /* bits[3..0]reserved */ - {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, - /* VREF vertical frame ctrl */ - {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* AECH 0x20 */ - {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFL */ - {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFH */ - {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */ -/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */ -/****** (some exchanges in the win trace) ******/ -/*fixme:param2*/ - {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ - {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */ - {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */ - {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCL */ -/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */ -/****** (some exchanges in the win trace) ******/ -/******!! startsensor KO if changed !!****/ -/*fixme: param3*/ - {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10}, - {} -}; - -static const u8 po1030_sensor_init[][8] = { -/* the sensor registers are described in m5602/m5602_po1030.h */ - {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */ - {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ - {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10}, - {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10}, - {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, - {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */ - {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10}, - {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */ - {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10}, - {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */ - {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10}, - {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10}, - {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, - {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10}, - {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10}, - {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10}, - {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */ - {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10}, - - {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10}, - {} -}; -static const u8 po1030_sensor_param1[][8] = { -/* from ms-win traces - these values change with auto gain/expo/wb.. */ - {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, -/* mean values */ - {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */ - {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */ - {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */ - - {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */ - {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */ - {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10}, -/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */ - {} -}; - -static const u8 po2030n_sensor_init[][8] = { - {0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ - {0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ - {0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10}, - {0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10}, - {0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10}, - {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10}, - {0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10}, - {0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10}, - {0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10}, - {0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10}, - {0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10}, - {0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10}, - {0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10}, - {0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10}, - {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10}, - {0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10}, - {} -}; -static const u8 po2030n_sensor_param1[][8] = { - {0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */ - {0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xd1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x40, 0x10}, /* RGBG gains */ -/*param2*/ - {0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, - {} -}; - -static const u8 soi768_sensor_init[][8] = { - {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ - {DELAY, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */ - {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 soi768_sensor_param1[][8] = { - {0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10}, -/* */ -/* {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */ - {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, -/* {0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */ - {0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10}, -/* the next sequence should be used for auto gain */ - {0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10}, - /* global gain ? : 07 - change with 0x15 at the end */ - {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */ - {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10}, - /* exposure ? : 0200 - change with 0x1e at the end */ - {} -}; - -static const u8 sp80708_sensor_init[][8] = { - {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10}, - {} -}; -static const u8 sp80708_sensor_param1[][8] = { - {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10}, - {} -}; - -static const u8 (*sensor_init[])[8] = { -[SENSOR_ADCM1700] = adcm1700_sensor_init, -[SENSOR_GC0307] = gc0307_sensor_init, -[SENSOR_HV7131R] = hv7131r_sensor_init, -[SENSOR_MI0360] = mi0360_sensor_init, -[SENSOR_MI0360B] = mi0360b_sensor_init, -[SENSOR_MO4000] = mo4000_sensor_init, -[SENSOR_MT9V111] = mt9v111_sensor_init, -[SENSOR_OM6802] = om6802_sensor_init, -[SENSOR_OV7630] = ov7630_sensor_init, -[SENSOR_OV7648] = ov7648_sensor_init, -[SENSOR_OV7660] = ov7660_sensor_init, -[SENSOR_PO1030] = po1030_sensor_init, -[SENSOR_PO2030N] = po2030n_sensor_init, -[SENSOR_SOI768] = soi768_sensor_init, -[SENSOR_SP80708] = sp80708_sensor_init, -}; - -/* read bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - u16 value, int len) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); - return; - } -#endif - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, 0, - gspca_dev->usb_buf, len, - 500); - PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_w1(struct gspca_dev *gspca_dev, - u16 value, - u8 data) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data); - gspca_dev->usb_buf[0] = data; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, - 0, - gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_w1 err %d\n", ret); - gspca_dev->usb_err = ret; - } -} -static void reg_w(struct gspca_dev *gspca_dev, - u16 value, - const u8 *buffer, - int len) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..", - value, buffer[0], buffer[1]); -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - pr_err("reg_w: buffer overflow\n"); - return; - } -#endif - memcpy(gspca_dev->usb_buf, buffer, len); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, 0, - gspca_dev->usb_buf, len, - 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* I2C write 1 byte */ -static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val); - switch (sd->sensor) { - case SENSOR_ADCM1700: - case SENSOR_OM6802: - case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ - gspca_dev->usb_buf[0] = 0x80 | (2 << 4); - break; - default: /* i2c command = a1 (400 kHz) */ - gspca_dev->usb_buf[0] = 0x81 | (2 << 4); - break; - } - gspca_dev->usb_buf[1] = sd->i2c_addr; - gspca_dev->usb_buf[2] = reg; - gspca_dev->usb_buf[3] = val; - gspca_dev->usb_buf[4] = 0; - gspca_dev->usb_buf[5] = 0; - gspca_dev->usb_buf[6] = 0; - gspca_dev->usb_buf[7] = 0x10; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x08, /* value = i2c */ - 0, - gspca_dev->usb_buf, 8, - 500); - if (ret < 0) { - pr_err("i2c_w1 err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* I2C write 8 bytes */ -static void i2c_w8(struct gspca_dev *gspca_dev, - const u8 *buffer) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..", - buffer[2], buffer[3]); - memcpy(gspca_dev->usb_buf, buffer, 8); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x08, 0, /* value, index */ - gspca_dev->usb_buf, 8, - 500); - msleep(2); - if (ret < 0) { - pr_err("i2c_w8 err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */ -static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 mode[8]; - - switch (sd->sensor) { - case SENSOR_ADCM1700: - case SENSOR_OM6802: - case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ - mode[0] = 0x80 | 0x10; - break; - default: /* i2c command = 91 (400 kHz) */ - mode[0] = 0x81 | 0x10; - break; - } - mode[1] = sd->i2c_addr; - mode[2] = reg; - mode[3] = 0; - mode[4] = 0; - mode[5] = 0; - mode[6] = 0; - mode[7] = 0x10; - i2c_w8(gspca_dev, mode); - msleep(2); - mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02; - mode[2] = 0; - i2c_w8(gspca_dev, mode); - msleep(2); - reg_r(gspca_dev, 0x0a, 5); -} - -static void i2c_w_seq(struct gspca_dev *gspca_dev, - const u8 (*data)[8]) -{ - while ((*data)[0] != 0) { - if ((*data)[0] != DELAY) - i2c_w8(gspca_dev, *data); - else - msleep((*data)[1]); - data++; - } -} - -/* check the ID of the hv7131 sensor */ -/* this sequence is needed because it activates the sensor */ -static void hv7131r_probe(struct gspca_dev *gspca_dev) -{ - i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ - msleep(10); - reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ - msleep(10); - i2c_r(gspca_dev, 0, 5); /* read sensor id */ - if (gspca_dev->usb_buf[0] == 0x02 /* chip ID (02 is R) */ - && gspca_dev->usb_buf[1] == 0x09 - && gspca_dev->usb_buf[2] == 0x01) { - PDEBUG(D_PROBE, "Sensor HV7131R found"); - return; - } - pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n", - gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], - gspca_dev->usb_buf[2]); -} - -static void mi0360_probe(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, j; - u16 val = 0; - static const u8 probe_tb[][4][8] = { - { /* mi0360 */ - {0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, - {0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10} - }, - { /* mt9v111 */ - {0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, - {0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, - {} - }, - }; - - for (i = 0; i < ARRAY_SIZE(probe_tb); i++) { - reg_w1(gspca_dev, 0x17, 0x62); - reg_w1(gspca_dev, 0x01, 0x08); - for (j = 0; j < 3; j++) - i2c_w8(gspca_dev, probe_tb[i][j]); - msleep(2); - reg_r(gspca_dev, 0x0a, 5); - val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; - if (probe_tb[i][3][0] != 0) - i2c_w8(gspca_dev, probe_tb[i][3]); - reg_w1(gspca_dev, 0x01, 0x29); - reg_w1(gspca_dev, 0x17, 0x42); - if (val != 0xffff) - break; - } - if (gspca_dev->usb_err < 0) - return; - switch (val) { - case 0x8221: - PDEBUG(D_PROBE, "Sensor mi0360b"); - sd->sensor = SENSOR_MI0360B; - break; - case 0x823a: - PDEBUG(D_PROBE, "Sensor mt9v111"); - sd->sensor = SENSOR_MT9V111; - break; - case 0x8243: - PDEBUG(D_PROBE, "Sensor mi0360"); - break; - default: - PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val); - break; - } -} - -static void ov7630_probe(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 val; - - /* check ov76xx */ - reg_w1(gspca_dev, 0x17, 0x62); - reg_w1(gspca_dev, 0x01, 0x08); - sd->i2c_addr = 0x21; - i2c_r(gspca_dev, 0x0a, 2); - val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; - reg_w1(gspca_dev, 0x01, 0x29); - reg_w1(gspca_dev, 0x17, 0x42); - if (gspca_dev->usb_err < 0) - return; - if (val == 0x7628) { /* soi768 */ - sd->sensor = SENSOR_SOI768; -/*fixme: only valid for 0c45:613e?*/ - gspca_dev->cam.input_flags = - V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP; - PDEBUG(D_PROBE, "Sensor soi768"); - return; - } - PDEBUG(D_PROBE, "Sensor ov%04x", val); -} - -static void ov7648_probe(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 val; - - /* check ov76xx */ - reg_w1(gspca_dev, 0x17, 0x62); - reg_w1(gspca_dev, 0x01, 0x08); - sd->i2c_addr = 0x21; - i2c_r(gspca_dev, 0x0a, 2); - val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; - reg_w1(gspca_dev, 0x01, 0x29); - reg_w1(gspca_dev, 0x17, 0x42); - if ((val & 0xff00) == 0x7600) { /* ov76xx */ - PDEBUG(D_PROBE, "Sensor ov%04x", val); - return; - } - - /* check po1030 */ - reg_w1(gspca_dev, 0x17, 0x62); - reg_w1(gspca_dev, 0x01, 0x08); - sd->i2c_addr = 0x6e; - i2c_r(gspca_dev, 0x00, 2); - val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; - reg_w1(gspca_dev, 0x01, 0x29); - reg_w1(gspca_dev, 0x17, 0x42); - if (gspca_dev->usb_err < 0) - return; - if (val == 0x1030) { /* po1030 */ - PDEBUG(D_PROBE, "Sensor po1030"); - sd->sensor = SENSOR_PO1030; - return; - } - pr_err("Unknown sensor %04x\n", val); -} - -/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */ -static void po2030n_probe(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 val; - - /* check gc0307 */ - reg_w1(gspca_dev, 0x17, 0x62); - reg_w1(gspca_dev, 0x01, 0x08); - reg_w1(gspca_dev, 0x02, 0x22); - sd->i2c_addr = 0x21; - i2c_r(gspca_dev, 0x00, 1); - val = gspca_dev->usb_buf[4]; - reg_w1(gspca_dev, 0x01, 0x29); /* reset */ - reg_w1(gspca_dev, 0x17, 0x42); - if (val == 0x99) { /* gc0307 (?) */ - PDEBUG(D_PROBE, "Sensor gc0307"); - sd->sensor = SENSOR_GC0307; - return; - } - - /* check po2030n */ - reg_w1(gspca_dev, 0x17, 0x62); - reg_w1(gspca_dev, 0x01, 0x0a); - sd->i2c_addr = 0x6e; - i2c_r(gspca_dev, 0x00, 2); - val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; - reg_w1(gspca_dev, 0x01, 0x29); - reg_w1(gspca_dev, 0x17, 0x42); - if (gspca_dev->usb_err < 0) - return; - if (val == 0x2030) { - PDEBUG(D_PROBE, "Sensor po2030n"); -/* sd->sensor = SENSOR_PO2030N; */ - } else { - pr_err("Unknown sensor ID %04x\n", val); - } -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - sd->bridge = id->driver_info >> 16; - sd->sensor = id->driver_info >> 8; - sd->flags = id->driver_info; - - cam = &gspca_dev->cam; - if (sd->sensor == SENSOR_ADCM1700) { - cam->cam_mode = cif_mode; - cam->nmodes = ARRAY_SIZE(cif_mode); - } else { - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - } - cam->npkt = 24; /* 24 packets per ISOC message */ - cam->ctrls = sd->ctrls; - - sd->ag_cnt = -1; - sd->quality = QUALITY_DEF; - - INIT_WORK(&sd->work, qual_upd); - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - const u8 *sn9c1xx; - u8 regGpio[] = { 0x29, 0x70 }; /* no audio */ - u8 regF1; - - /* setup a selector by bridge */ - reg_w1(gspca_dev, 0xf1, 0x01); - reg_r(gspca_dev, 0x00, 1); - reg_w1(gspca_dev, 0xf1, 0x00); - reg_r(gspca_dev, 0x00, 1); /* get sonix chip id */ - regF1 = gspca_dev->usb_buf[0]; - if (gspca_dev->usb_err < 0) - return gspca_dev->usb_err; - PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1); - if (gspca_dev->audio) - regGpio[1] |= 0x04; /* with audio */ - switch (sd->bridge) { - case BRIDGE_SN9C102P: - case BRIDGE_SN9C105: - if (regF1 != 0x11) - return -ENODEV; - break; - default: -/* case BRIDGE_SN9C110: */ -/* case BRIDGE_SN9C120: */ - if (regF1 != 0x12) - return -ENODEV; - } - - switch (sd->sensor) { - case SENSOR_MI0360: - mi0360_probe(gspca_dev); - break; - case SENSOR_OV7630: - ov7630_probe(gspca_dev); - break; - case SENSOR_OV7648: - ov7648_probe(gspca_dev); - break; - case SENSOR_PO2030N: - po2030n_probe(gspca_dev); - break; - } - - switch (sd->bridge) { - case BRIDGE_SN9C102P: - reg_w1(gspca_dev, 0x02, regGpio[1]); - break; - default: - reg_w(gspca_dev, 0x01, regGpio, 2); - break; - } - - if (sd->sensor == SENSOR_OM6802) - sd->ctrls[SHARPNESS].def = 0x10; - - /* Note we do not disable the sensor clock here (power saving mode), - as that also disables the button on the cam. */ - reg_w1(gspca_dev, 0xf1, 0x00); - - /* set the i2c address */ - sn9c1xx = sn_tb[sd->sensor]; - sd->i2c_addr = sn9c1xx[9]; - - gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; - if (!(sd->flags & F_ILLUM)) - gspca_dev->ctrl_dis |= (1 << ILLUM); - - return gspca_dev->usb_err; -} - -static u32 expo_adjust(struct gspca_dev *gspca_dev, - u32 expo) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->sensor) { - case SENSOR_GC0307: { - int a, b; - - /* expo = 0..255 -> a = 19..43 */ - a = 19 + expo * 25 / 256; - i2c_w1(gspca_dev, 0x68, a); - a -= 12; - b = a * a * 4; /* heuristic */ - i2c_w1(gspca_dev, 0x03, b >> 8); - i2c_w1(gspca_dev, 0x04, b); - break; - } - case SENSOR_HV7131R: { - u8 Expodoit[] = - { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 }; - - Expodoit[3] = expo >> 16; - Expodoit[4] = expo >> 8; - Expodoit[5] = expo; - i2c_w8(gspca_dev, Expodoit); - break; - } - case SENSOR_MI0360: - case SENSOR_MI0360B: { - u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ - { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 }; - static const u8 doit[] = /* update sensor */ - { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; - static const u8 sensorgo[] = /* sensor on */ - { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; - - if (expo > 0x0635) - expo = 0x0635; - else if (expo < 0x0001) - expo = 0x0001; - expoMi[3] = expo >> 8; - expoMi[4] = expo; - i2c_w8(gspca_dev, expoMi); - i2c_w8(gspca_dev, doit); - i2c_w8(gspca_dev, sensorgo); - break; - } - case SENSOR_MO4000: { - u8 expoMof[] = - { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 }; - u8 expoMo10[] = - { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 }; - static const u8 gainMo[] = - { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; - - if (expo > 0x1fff) - expo = 0x1fff; - else if (expo < 0x0001) - expo = 0x0001; - expoMof[3] = (expo & 0x03fc) >> 2; - i2c_w8(gspca_dev, expoMof); - expoMo10[3] = ((expo & 0x1c00) >> 10) - | ((expo & 0x0003) << 4); - i2c_w8(gspca_dev, expoMo10); - i2c_w8(gspca_dev, gainMo); - PDEBUG(D_FRAM, "set exposure %d", - ((expoMo10[3] & 0x07) << 10) - | (expoMof[3] << 2) - | ((expoMo10[3] & 0x30) >> 4)); - break; - } - case SENSOR_MT9V111: { - u8 expo_c1[] = - { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 }; - - if (expo > 0x0390) - expo = 0x0390; - else if (expo < 0x0060) - expo = 0x0060; - expo_c1[3] = expo >> 8; - expo_c1[4] = expo; - i2c_w8(gspca_dev, expo_c1); - break; - } - case SENSOR_OM6802: { - u8 gainOm[] = - { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; - /* preset AGC - works when AutoExpo = off */ - - if (expo > 0x03ff) - expo = 0x03ff; - if (expo < 0x0001) - expo = 0x0001; - gainOm[3] = expo >> 2; - i2c_w8(gspca_dev, gainOm); - reg_w1(gspca_dev, 0x96, expo >> 5); - PDEBUG(D_FRAM, "set exposure %d", gainOm[3]); - break; - } - } - return expo; -} - -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - unsigned int expo; - int brightness; - u8 k2; - - brightness = sd->ctrls[BRIGHTNESS].val; - k2 = (brightness - 0x80) >> 2; - switch (sd->sensor) { - case SENSOR_ADCM1700: - if (k2 > 0x1f) - k2 = 0; /* only positive Y offset */ - break; - case SENSOR_HV7131R: - expo = brightness << 12; - if (expo > 0x002dc6c0) - expo = 0x002dc6c0; - else if (expo < 0x02a0) - expo = 0x02a0; - sd->exposure = expo_adjust(gspca_dev, expo); - break; - case SENSOR_MI0360: - case SENSOR_MO4000: - expo = brightness << 4; - sd->exposure = expo_adjust(gspca_dev, expo); - break; - case SENSOR_MI0360B: - expo = brightness << 2; - sd->exposure = expo_adjust(gspca_dev, expo); - break; - case SENSOR_GC0307: - expo = brightness; - sd->exposure = expo_adjust(gspca_dev, expo); - return; /* don't set the Y offset */ - case SENSOR_MT9V111: - expo = brightness << 2; - sd->exposure = expo_adjust(gspca_dev, expo); - return; /* don't set the Y offset */ - case SENSOR_OM6802: - expo = brightness << 2; - sd->exposure = expo_adjust(gspca_dev, expo); - return; /* Y offset already set */ - } - - reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ -} - -static void setcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 k2; - u8 contrast[6]; - - k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1) - + 37; /* 37..73 */ - contrast[0] = (k2 + 1) / 2; /* red */ - contrast[1] = 0; - contrast[2] = k2; /* green */ - contrast[3] = 0; - contrast[4] = k2 / 5; /* blue */ - contrast[5] = 0; - reg_w(gspca_dev, 0x84, contrast, sizeof contrast); -} - -static void setcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, v, colors; - const s16 *uv; - u8 reg8a[12]; /* U & V gains */ - static const s16 uv_com[6] = { /* same as reg84 in signed decimal */ - -24, -38, 64, /* UR UG UB */ - 62, -51, -9 /* VR VG VB */ - }; - static const s16 uv_mi0360b[6] = { - -20, -38, 64, /* UR UG UB */ - 60, -51, -9 /* VR VG VB */ - }; - - colors = sd->ctrls[COLORS].val; - if (sd->sensor == SENSOR_MI0360B) - uv = uv_mi0360b; - else - uv = uv_com; - for (i = 0; i < 6; i++) { - v = uv[i] * colors / COLORS_DEF; - reg8a[i * 2] = v; - reg8a[i * 2 + 1] = (v >> 8) & 0x0f; - } - reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a); -} - -static void setredblue(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PO2030N) { - u8 rg1b[] = /* red green1 blue (no g2) */ - {0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10}; - - /* 0x40 = normal value = gain x 1 */ - rg1b[3] = sd->ctrls[RED].val * 2; - rg1b[5] = sd->ctrls[BLUE].val * 2; - i2c_w8(gspca_dev, rg1b); - return; - } - reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val); -/* reg_w1(gspca_dev, 0x07, 32); */ - reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val); -} - -static void setgamma(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, val; - u8 gamma[17]; - const u8 *gamma_base; - static const u8 delta[17] = { - 0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a, - 0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00 - }; - - switch (sd->sensor) { - case SENSOR_ADCM1700: - gamma_base = gamma_spec_0; - break; - case SENSOR_HV7131R: - case SENSOR_MI0360B: - case SENSOR_MT9V111: - gamma_base = gamma_spec_1; - break; - case SENSOR_GC0307: - gamma_base = gamma_spec_2; - break; - case SENSOR_SP80708: - gamma_base = gamma_spec_3; - break; - default: - gamma_base = gamma_def; - break; - } - - val = sd->ctrls[GAMMA].val; - for (i = 0; i < sizeof gamma; i++) - gamma[i] = gamma_base[i] - + delta[i] * (val - GAMMA_DEF) / 32; - reg_w(gspca_dev, 0x20, gamma, sizeof gamma); -} - -static void setexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PO2030N) { - u8 rexpo[] = /* 1a: expo H, 1b: expo M */ - {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10}; - - rexpo[3] = sd->ctrls[EXPOSURE].val >> 8; - i2c_w8(gspca_dev, rexpo); - msleep(6); - rexpo[2] = 0x1b; - rexpo[3] = sd->ctrls[EXPOSURE].val; - i2c_w8(gspca_dev, rexpo); - } -} - -static void setautogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (gspca_dev->ctrl_dis & (1 << AUTOGAIN)) - return; - switch (sd->sensor) { - case SENSOR_OV7630: - case SENSOR_OV7648: { - u8 comb; - - if (sd->sensor == SENSOR_OV7630) - comb = 0xc0; - else - comb = 0xa0; - if (sd->ctrls[AUTOGAIN].val) - comb |= 0x03; - i2c_w1(&sd->gspca_dev, 0x13, comb); - return; - } - } - if (sd->ctrls[AUTOGAIN].val) - sd->ag_cnt = AG_CNT_START; - else - sd->ag_cnt = -1; -} - -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PO2030N) { - u8 rgain[] = /* 15: gain */ - {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15}; - - rgain[3] = sd->ctrls[GAIN].val; - i2c_w8(gspca_dev, rgain); - } -} - -static void sethvflip(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 comn; - - switch (sd->sensor) { - case SENSOR_HV7131R: - comn = 0x18; /* clkdiv = 1, ablcen = 1 */ - if (sd->ctrls[VFLIP].val) - comn |= 0x01; - i2c_w1(gspca_dev, 0x01, comn); /* sctra */ - break; - case SENSOR_OV7630: - comn = 0x02; - if (!sd->ctrls[VFLIP].val) - comn |= 0x80; - i2c_w1(gspca_dev, 0x75, comn); - break; - case SENSOR_OV7648: - comn = 0x06; - if (sd->ctrls[VFLIP].val) - comn |= 0x80; - i2c_w1(gspca_dev, 0x75, comn); - break; - case SENSOR_PO2030N: - /* Reg. 0x1E: Timing Generator Control Register 2 (Tgcontrol2) - * (reset value: 0x0A) - * bit7: HM: Horizontal Mirror: 0: disable, 1: enable - * bit6: VM: Vertical Mirror: 0: disable, 1: enable - * bit5: ST: Shutter Selection: 0: electrical, 1: mechanical - * bit4: FT: Single Frame Transfer: 0: disable, 1: enable - * bit3-0: X - */ - comn = 0x0a; - if (sd->ctrls[HFLIP].val) - comn |= 0x80; - if (sd->ctrls[VFLIP].val) - comn |= 0x40; - i2c_w1(&sd->gspca_dev, 0x1e, comn); - break; - } -} - -static void setsharpness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val); -} - -static void setillum(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (gspca_dev->ctrl_dis & (1 << ILLUM)) - return; - switch (sd->sensor) { - case SENSOR_ADCM1700: - reg_w1(gspca_dev, 0x02, /* gpio */ - sd->ctrls[ILLUM].val ? 0x64 : 0x60); - break; - case SENSOR_MT9V111: - reg_w1(gspca_dev, 0x02, - sd->ctrls[ILLUM].val ? 0x77 : 0x74); -/* should have been: */ -/* 0x55 : 0x54); * 370i */ -/* 0x66 : 0x64); * Clip */ - break; - } -} - -static void setfreq(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (gspca_dev->ctrl_dis & (1 << FREQ)) - return; - if (sd->sensor == SENSOR_OV7660) { - u8 com8; - - com8 = 0xdf; /* auto gain/wb/expo */ - switch (sd->ctrls[FREQ].val) { - case 0: /* Banding filter disabled */ - i2c_w1(gspca_dev, 0x13, com8 | 0x20); - break; - case 1: /* 50 hz */ - i2c_w1(gspca_dev, 0x13, com8); - i2c_w1(gspca_dev, 0x3b, 0x0a); - break; - case 2: /* 60 hz */ - i2c_w1(gspca_dev, 0x13, com8); - i2c_w1(gspca_dev, 0x3b, 0x02); - break; - } - } else { - u8 reg2a = 0, reg2b = 0, reg2d = 0; - - /* Get reg2a / reg2d base values */ - switch (sd->sensor) { - case SENSOR_OV7630: - reg2a = 0x08; - reg2d = 0x01; - break; - case SENSOR_OV7648: - reg2a = 0x11; - reg2d = 0x81; - break; - } - - switch (sd->ctrls[FREQ].val) { - case 0: /* Banding filter disabled */ - break; - case 1: /* 50 hz (filter on and framerate adj) */ - reg2a |= 0x80; - reg2b = 0xac; - reg2d |= 0x04; - break; - case 2: /* 60 hz (filter on, no framerate adj) */ - reg2a |= 0x80; - reg2d |= 0x04; - break; - } - i2c_w1(gspca_dev, 0x2a, reg2a); - i2c_w1(gspca_dev, 0x2b, reg2b); - i2c_w1(gspca_dev, 0x2d, reg2d); - } -} - -static void setjpegqual(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - jpeg_set_qual(sd->jpeg_hdr, sd->quality); -#if USB_BUF_SZ < 64 -#error "No room enough in usb_buf for quantization table" -#endif - memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x0100, 0, - gspca_dev->usb_buf, 64, - 500); - memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x0140, 0, - gspca_dev->usb_buf, 64, - 500); - - sd->reg18 ^= 0x40; - reg_w1(gspca_dev, 0x18, sd->reg18); -} - -/* JPEG quality update */ -/* This function is executed from a work queue. */ -static void qual_upd(struct work_struct *work) -{ - struct sd *sd = container_of(work, struct sd, work); - struct gspca_dev *gspca_dev = &sd->gspca_dev; - - mutex_lock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality); - setjpegqual(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - u8 reg01, reg17; - u8 reg0102[2]; - const u8 *sn9c1xx; - const u8 (*init)[8]; - const u8 *reg9a; - int mode; - static const u8 reg9a_def[] = - {0x00, 0x40, 0x20, 0x00, 0x00, 0x00}; - static const u8 reg9a_spec[] = - {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; - static const u8 regd4[] = {0x60, 0x00, 0x00}; - static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; - static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; - static const u8 CA_adcm1700[] = - { 0x14, 0xec, 0x0a, 0xf6 }; - static const u8 CA_po2030n[] = - { 0x1e, 0xe2, 0x14, 0xec }; - static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ - static const u8 CE_gc0307[] = - { 0x32, 0xce, 0x2d, 0xd3 }; - static const u8 CE_ov76xx[] = - { 0x32, 0xdd, 0x32, 0xdd }; - static const u8 CE_po2030n[] = - { 0x14, 0xe7, 0x1e, 0xdd }; - - /* create the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x21); /* JPEG 422 */ - - /* initialize the bridge */ - sn9c1xx = sn_tb[sd->sensor]; - - /* sensor clock already enabled in sd_init */ - /* reg_w1(gspca_dev, 0xf1, 0x00); */ - reg01 = sn9c1xx[1]; - if (sd->flags & F_PDN_INV) - reg01 ^= S_PDN_INV; /* power down inverted */ - reg_w1(gspca_dev, 0x01, reg01); - - /* configure gpio */ - reg0102[0] = reg01; - reg0102[1] = sn9c1xx[2]; - if (gspca_dev->audio) - reg0102[1] |= 0x04; /* keep the audio connection */ - reg_w(gspca_dev, 0x01, reg0102, 2); - reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); - reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); - switch (sd->sensor) { - case SENSOR_GC0307: - case SENSOR_OV7660: - case SENSOR_PO1030: - case SENSOR_PO2030N: - case SENSOR_SOI768: - case SENSOR_SP80708: - reg9a = reg9a_spec; - break; - default: - reg9a = reg9a_def; - break; - } - reg_w(gspca_dev, 0x9a, reg9a, 6); - - reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); - - reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); - - reg17 = sn9c1xx[0x17]; - switch (sd->sensor) { - case SENSOR_GC0307: - msleep(50); /*fixme: is it useful? */ - break; - case SENSOR_OM6802: - msleep(10); - reg_w1(gspca_dev, 0x02, 0x73); - reg17 |= SEN_CLK_EN; - reg_w1(gspca_dev, 0x17, reg17); - reg_w1(gspca_dev, 0x01, 0x22); - msleep(100); - reg01 = SCL_SEL_OD | S_PDN_INV; - reg17 &= ~MCK_SIZE_MASK; - reg17 |= 0x04; /* clock / 4 */ - break; - } - reg01 |= SYS_SEL_48M; - reg_w1(gspca_dev, 0x01, reg01); - reg17 |= SEN_CLK_EN; - reg_w1(gspca_dev, 0x17, reg17); - reg01 &= ~S_PWR_DN; /* sensor power on */ - reg_w1(gspca_dev, 0x01, reg01); - reg01 &= ~SCL_SEL_OD; /* remove open-drain mode */ - reg_w1(gspca_dev, 0x01, reg01); - - switch (sd->sensor) { - case SENSOR_HV7131R: - hv7131r_probe(gspca_dev); /*fixme: is it useful? */ - break; - case SENSOR_OM6802: - msleep(10); - reg_w1(gspca_dev, 0x01, reg01); - i2c_w8(gspca_dev, om6802_init0[0]); - i2c_w8(gspca_dev, om6802_init0[1]); - msleep(15); - reg_w1(gspca_dev, 0x02, 0x71); - msleep(150); - break; - case SENSOR_SP80708: - msleep(100); - reg_w1(gspca_dev, 0x02, 0x62); - break; - } - - /* initialize the sensor */ - i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); - - reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); - reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); - reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); - reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]); - reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); - if (sd->sensor == SENSOR_ADCM1700) { - reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */ - reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */ - } else { - reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */ - reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */ - } - reg_w1(gspca_dev, 0xc6, 0x00); - reg_w1(gspca_dev, 0xc7, 0x00); - if (sd->sensor == SENSOR_ADCM1700) { - reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */ - reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */ - } else { - reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */ - reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */ - } - reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); - switch (sd->sensor) { - case SENSOR_OM6802: -/* case SENSOR_OV7648: * fixme: sometimes */ - break; - default: - reg17 |= DEF_EN; - break; - } - reg_w1(gspca_dev, 0x17, reg17); - - reg_w1(gspca_dev, 0x05, 0x00); /* red */ - reg_w1(gspca_dev, 0x07, 0x00); /* green */ - reg_w1(gspca_dev, 0x06, 0x00); /* blue */ - reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); - - setgamma(gspca_dev); - -/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */ - for (i = 0; i < 8; i++) - reg_w(gspca_dev, 0x84, reg84, sizeof reg84); - switch (sd->sensor) { - case SENSOR_ADCM1700: - case SENSOR_OV7660: - case SENSOR_SP80708: - reg_w1(gspca_dev, 0x9a, 0x05); - break; - case SENSOR_GC0307: - case SENSOR_MT9V111: - case SENSOR_MI0360B: - reg_w1(gspca_dev, 0x9a, 0x07); - break; - case SENSOR_OV7630: - case SENSOR_OV7648: - reg_w1(gspca_dev, 0x9a, 0x0a); - break; - case SENSOR_PO2030N: - case SENSOR_SOI768: - reg_w1(gspca_dev, 0x9a, 0x06); - break; - default: - reg_w1(gspca_dev, 0x9a, 0x08); - break; - } - setsharpness(gspca_dev); - - reg_w(gspca_dev, 0x84, reg84, sizeof reg84); - reg_w1(gspca_dev, 0x05, 0x20); /* red */ - reg_w1(gspca_dev, 0x07, 0x20); /* green */ - reg_w1(gspca_dev, 0x06, 0x20); /* blue */ - - init = NULL; - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - reg01 |= SYS_SEL_48M | V_TX_EN; - reg17 &= ~MCK_SIZE_MASK; - reg17 |= 0x02; /* clock / 2 */ - switch (sd->sensor) { - case SENSOR_ADCM1700: - init = adcm1700_sensor_param1; - break; - case SENSOR_GC0307: - init = gc0307_sensor_param1; - break; - case SENSOR_HV7131R: - case SENSOR_MI0360: - if (!mode) - reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */ - reg17 &= ~MCK_SIZE_MASK; - reg17 |= 0x01; /* clock / 1 */ - break; - case SENSOR_MI0360B: - init = mi0360b_sensor_param1; - break; - case SENSOR_MO4000: - if (mode) { /* if 320x240 */ - reg01 &= ~SYS_SEL_48M; /* clk 24Mz */ - reg17 &= ~MCK_SIZE_MASK; - reg17 |= 0x01; /* clock / 1 */ - } - break; - case SENSOR_MT9V111: - init = mt9v111_sensor_param1; - break; - case SENSOR_OM6802: - init = om6802_sensor_param1; - if (!mode) { /* if 640x480 */ - reg17 &= ~MCK_SIZE_MASK; - reg17 |= 0x04; /* clock / 4 */ - } else { - reg01 &= ~SYS_SEL_48M; /* clk 24Mz */ - reg17 &= ~MCK_SIZE_MASK; - reg17 |= 0x02; /* clock / 2 */ - } - break; - case SENSOR_OV7630: - init = ov7630_sensor_param1; - break; - case SENSOR_OV7648: - init = ov7648_sensor_param1; - reg17 &= ~MCK_SIZE_MASK; - reg17 |= 0x01; /* clock / 1 */ - break; - case SENSOR_OV7660: - init = ov7660_sensor_param1; - break; - case SENSOR_PO1030: - init = po1030_sensor_param1; - break; - case SENSOR_PO2030N: - init = po2030n_sensor_param1; - break; - case SENSOR_SOI768: - init = soi768_sensor_param1; - break; - case SENSOR_SP80708: - init = sp80708_sensor_param1; - break; - } - - /* more sensor initialization - param1 */ - if (init != NULL) { - i2c_w_seq(gspca_dev, init); -/* init = NULL; */ - } - - reg_w(gspca_dev, 0xc0, C0, 6); - switch (sd->sensor) { - case SENSOR_ADCM1700: - case SENSOR_GC0307: - case SENSOR_SOI768: - reg_w(gspca_dev, 0xca, CA_adcm1700, 4); - break; - case SENSOR_PO2030N: - reg_w(gspca_dev, 0xca, CA_po2030n, 4); - break; - default: - reg_w(gspca_dev, 0xca, CA, 4); - break; - } - switch (sd->sensor) { - case SENSOR_ADCM1700: - case SENSOR_OV7630: - case SENSOR_OV7648: - case SENSOR_OV7660: - case SENSOR_SOI768: - reg_w(gspca_dev, 0xce, CE_ov76xx, 4); - break; - case SENSOR_GC0307: - reg_w(gspca_dev, 0xce, CE_gc0307, 4); - break; - case SENSOR_PO2030N: - reg_w(gspca_dev, 0xce, CE_po2030n, 4); - break; - default: - reg_w(gspca_dev, 0xce, CE, 4); - /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */ - break; - } - - /* here change size mode 0 -> VGA; 1 -> CIF */ - sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; - reg_w1(gspca_dev, 0x18, sd->reg18); - setjpegqual(gspca_dev); - - reg_w1(gspca_dev, 0x17, reg17); - reg_w1(gspca_dev, 0x01, reg01); - sd->reg01 = reg01; - sd->reg17 = reg17; - - sethvflip(gspca_dev); - setbrightness(gspca_dev); - setcontrast(gspca_dev); - setcolors(gspca_dev); - setautogain(gspca_dev); - if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) { - setexposure(gspca_dev); - setgain(gspca_dev); - } - setfreq(gspca_dev); - - sd->pktsz = sd->npkt = 0; - sd->nchg = sd->short_mark = 0; - sd->work_thread = create_singlethread_workqueue(MODULE_NAME); - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const u8 stophv7131[] = - { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; - static const u8 stopmi0360[] = - { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; - static const u8 stopov7648[] = - { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 }; - static const u8 stopsoi768[] = - { 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 }; - u8 reg01; - u8 reg17; - - reg01 = sd->reg01; - reg17 = sd->reg17 & ~SEN_CLK_EN; - switch (sd->sensor) { - case SENSOR_ADCM1700: - case SENSOR_GC0307: - case SENSOR_PO2030N: - case SENSOR_SP80708: - reg01 |= LED; - reg_w1(gspca_dev, 0x01, reg01); - reg01 &= ~(LED | V_TX_EN); - reg_w1(gspca_dev, 0x01, reg01); -/* reg_w1(gspca_dev, 0x02, 0x??); * LED off ? */ - break; - case SENSOR_HV7131R: - reg01 &= ~V_TX_EN; - reg_w1(gspca_dev, 0x01, reg01); - i2c_w8(gspca_dev, stophv7131); - break; - case SENSOR_MI0360: - case SENSOR_MI0360B: - reg01 &= ~V_TX_EN; - reg_w1(gspca_dev, 0x01, reg01); -/* reg_w1(gspca_dev, 0x02, 0x40); * LED off ? */ - i2c_w8(gspca_dev, stopmi0360); - break; - case SENSOR_MT9V111: - case SENSOR_OM6802: - case SENSOR_PO1030: - reg01 &= ~V_TX_EN; - reg_w1(gspca_dev, 0x01, reg01); - break; - case SENSOR_OV7630: - case SENSOR_OV7648: - reg01 &= ~V_TX_EN; - reg_w1(gspca_dev, 0x01, reg01); - i2c_w8(gspca_dev, stopov7648); - break; - case SENSOR_OV7660: - reg01 &= ~V_TX_EN; - reg_w1(gspca_dev, 0x01, reg01); - break; - case SENSOR_SOI768: - i2c_w8(gspca_dev, stopsoi768); - break; - } - - reg01 |= SCL_SEL_OD; - reg_w1(gspca_dev, 0x01, reg01); - reg01 |= S_PWR_DN; /* sensor power down */ - reg_w1(gspca_dev, 0x01, reg01); - reg_w1(gspca_dev, 0x17, reg17); - reg01 &= ~SYS_SEL_48M; /* clock 24MHz */ - reg_w1(gspca_dev, 0x01, reg01); - reg01 |= LED; - reg_w1(gspca_dev, 0x01, reg01); - /* Don't disable sensor clock as that disables the button on the cam */ - /* reg_w1(gspca_dev, 0xf1, 0x01); */ -} - -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->work_thread != NULL) { - mutex_unlock(&gspca_dev->usb_lock); - destroy_workqueue(sd->work_thread); - mutex_lock(&gspca_dev->usb_lock); - sd->work_thread = NULL; - } -} - -#define WANT_REGULAR_AUTOGAIN -#include "autogain_functions.h" - -static void do_autogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int delta; - int expotimes; - u8 luma_mean = 130; - u8 luma_delta = 20; - - /* Thanks S., without your advice, autobright should not work :) */ - if (sd->ag_cnt < 0) - return; - if (--sd->ag_cnt >= 0) - return; - sd->ag_cnt = AG_CNT_START; - - delta = atomic_read(&sd->avg_lum); - PDEBUG(D_FRAM, "mean lum %d", delta); - - if (sd->sensor == SENSOR_PO2030N) { - auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta, - 15, 1024); - return; - } - - if (delta < luma_mean - luma_delta || - delta > luma_mean + luma_delta) { - switch (sd->sensor) { - case SENSOR_GC0307: - expotimes = sd->exposure; - expotimes += (luma_mean - delta) >> 6; - if (expotimes < 0) - expotimes = 0; - sd->exposure = expo_adjust(gspca_dev, - (unsigned int) expotimes); - break; - case SENSOR_HV7131R: - expotimes = sd->exposure >> 8; - expotimes += (luma_mean - delta) >> 4; - if (expotimes < 0) - expotimes = 0; - sd->exposure = expo_adjust(gspca_dev, - (unsigned int) (expotimes << 8)); - break; - case SENSOR_OM6802: - case SENSOR_MT9V111: - expotimes = sd->exposure; - expotimes += (luma_mean - delta) >> 2; - if (expotimes < 0) - expotimes = 0; - sd->exposure = expo_adjust(gspca_dev, - (unsigned int) expotimes); - setredblue(gspca_dev); - break; - default: -/* case SENSOR_MO4000: */ -/* case SENSOR_MI0360: */ -/* case SENSOR_MI0360B: */ - expotimes = sd->exposure; - expotimes += (luma_mean - delta) >> 6; - if (expotimes < 0) - expotimes = 0; - sd->exposure = expo_adjust(gspca_dev, - (unsigned int) expotimes); - setredblue(gspca_dev); - break; - } - } -} - -/* set the average luminosity from an isoc marker */ -static void set_lum(struct sd *sd, - u8 *data) -{ - int avg_lum; - - /* w0 w1 w2 - * w3 w4 w5 - * w6 w7 w8 - */ - avg_lum = (data[27] << 8) + data[28] /* w3 */ - - + (data[31] << 8) + data[32] /* w5 */ - - + (data[23] << 8) + data[24] /* w1 */ - - + (data[35] << 8) + data[36] /* w7 */ - - + (data[29] << 10) + (data[30] << 2); /* w4 * 4 */ - avg_lum >>= 10; - atomic_set(&sd->avg_lum, avg_lum); -} - -/* scan the URB packets */ -/* This function is run at interrupt level. */ -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, new_qual; - - /* - * A frame ends on the marker - * ff ff 00 c4 c4 96 .. - * which is 62 bytes long and is followed by various information - * including statuses and luminosity. - * - * A marker may be splitted on two packets. - * - * The 6th byte of a marker contains the bits: - * 0x08: USB full - * 0xc0: frame sequence - * When the bit 'USB full' is set, the frame must be discarded; - * this is also the case when the 2 bytes before the marker are - * not the JPEG end of frame ('ff d9'). - */ - - /* count the packets and their size */ - sd->npkt++; - sd->pktsz += len; - -/*fixme: assumption about the following code: - * - there can be only one marker in a packet - */ - - /* skip the remaining bytes of a short marker */ - i = sd->short_mark; - if (i != 0) { - sd->short_mark = 0; - if (i < 0 /* if 'ff' at end of previous packet */ - && data[0] == 0xff - && data[1] == 0x00) - goto marker_found; - if (data[0] == 0xff && data[1] == 0xff) { - i = 0; - goto marker_found; - } - len -= i; - if (len <= 0) - return; - data += i; - } - - /* search backwards if there is a marker in the packet */ - for (i = len - 1; --i >= 0; ) { - if (data[i] != 0xff) { - i--; - continue; - } - if (data[i + 1] == 0xff) { - - /* (there may be 'ff ff' inside a marker) */ - if (i + 2 >= len || data[i + 2] == 0x00) - goto marker_found; - } - } - - /* no marker found */ - /* add the JPEG header if first fragment */ - if (data[len - 1] == 0xff) - sd->short_mark = -1; - if (gspca_dev->last_packet_type == LAST_PACKET) - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - return; - - /* marker found */ - /* if some error, discard the frame and decrease the quality */ -marker_found: - new_qual = 0; - if (i > 2) { - if (data[i - 2] != 0xff || data[i - 1] != 0xd9) { - gspca_dev->last_packet_type = DISCARD_PACKET; - new_qual = -3; - } - } else if (i + 6 < len) { - if (data[i + 6] & 0x08) { - gspca_dev->last_packet_type = DISCARD_PACKET; - new_qual = -5; - } - } - - gspca_frame_add(gspca_dev, LAST_PACKET, data, i); - - /* compute the filling rate and a new JPEG quality */ - if (new_qual == 0) { - int r; - - r = (sd->pktsz * 100) / - (sd->npkt * - gspca_dev->urb[0]->iso_frame_desc[0].length); - if (r >= 85) - new_qual = -3; - else if (r < 75) - new_qual = 2; - } - if (new_qual != 0) { - sd->nchg += new_qual; - if (sd->nchg < -6 || sd->nchg >= 12) { - sd->nchg = 0; - new_qual += sd->quality; - if (new_qual < QUALITY_MIN) - new_qual = QUALITY_MIN; - else if (new_qual > QUALITY_MAX) - new_qual = QUALITY_MAX; - if (new_qual != sd->quality) { - sd->quality = new_qual; - queue_work(sd->work_thread, &sd->work); - } - } - } else { - sd->nchg = 0; - } - sd->pktsz = sd->npkt = 0; - - /* if the marker is smaller than 62 bytes, - * memorize the number of bytes to skip in the next packet */ - if (i + 62 > len) { /* no more usable data */ - sd->short_mark = i + 62 - len; - return; - } - if (sd->ag_cnt >= 0) - set_lum(sd, data + i); - - /* if more data, start a new frame */ - i += 62; - if (i < len) { - data += i; - len -= i; - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - } -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->ctrls[AUTOGAIN].val = val; - if (val) - gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); - else - gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN); - if (gspca_dev->streaming) - setautogain(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu) -{ - switch (menu->id) { - case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; - } - return -EINVAL; -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet data */ - int len) /* interrupt packet length */ -{ - int ret = -EINVAL; - - if (len == 1 && data[0] == 1) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - ret = 0; - } - - return ret; -} -#endif - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = NCTRLS, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = do_autogain, - .querymenu = sd_querymenu, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -/* -- module initialisation -- */ -#define BS(bridge, sensor) \ - .driver_info = (BRIDGE_ ## bridge << 16) \ - | (SENSOR_ ## sensor << 8) -#define BSF(bridge, sensor, flags) \ - .driver_info = (BRIDGE_ ## bridge << 16) \ - | (SENSOR_ ## sensor << 8) \ - | (flags) -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0458, 0x7025), BSF(SN9C120, MI0360B, F_PDN_INV)}, - {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, - {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, F_PDN_INV)}, - {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, F_PDN_INV)}, - {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)}, - {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)}, - {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)}, - {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)}, - {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)}, -/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */ - {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)}, -/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */ -/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */ - {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)}, -/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */ - {USB_DEVICE(0x0c45, 0x60c0), BSF(SN9C105, MI0360, F_ILLUM)}, - /* or MT9V111 */ -/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */ -/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */ -/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */ - {USB_DEVICE(0x0c45, 0x60ce), BS(SN9C105, SP80708)}, - {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)}, -/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */ -/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */ -/* {USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */ - {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)}, - {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)}, - {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, - {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/ -/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ - {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */ -/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */ -/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */ - {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/ -/*bw600.inf:*/ - {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/ - {USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)}, - {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)}, - {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)}, -/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */ - {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)}, - /* or MT9V111 / MI0360B */ -/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */ - {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)}, - {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)}, - {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)}, - {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, - {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, - {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/ - /* or GC0305 / GC0307 */ - {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ - {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ - {USB_DEVICE(0x0c45, 0x614a), BSF(SN9C120, ADCM1700, F_ILLUM)}, -/* {USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */ /*sn9c120b*/ - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c deleted file mode 100644 index 14d635277d71..000000000000 --- a/drivers/media/video/gspca/spca1528.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - * spca1528 subdriver - * - * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "spca1528" - -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Jean-Francois Moine "); -MODULE_DESCRIPTION("SPCA1528 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - u8 pkt_seq; - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -static const struct v4l2_pix_format vga_mode[] = { -/* (does not work correctly) - {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 5 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 3}, -*/ - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, -}; - -/* read bytes to gspca usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - u8 req, - u16 index, - int len) -{ -#if USB_BUF_SZ < 64 -#error "USB buffer too small" -#endif - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, /* value */ - index, - gspca_dev->usb_buf, len, - 500); - PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, - gspca_dev->usb_buf[0]); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_w(struct gspca_dev *gspca_dev, - u8 req, - u16 value, - u16 index) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, - NULL, 0, 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_wb(struct gspca_dev *gspca_dev, - u8 req, - u16 value, - u16 index, - u8 byte) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "SET %02x %04x %04x %02x", req, value, index, byte); - gspca_dev->usb_buf[0] = byte; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, - gspca_dev->usb_buf, 1, 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void wait_status_0(struct gspca_dev *gspca_dev) -{ - int i, w; - - i = 16; - w = 0; - do { - reg_r(gspca_dev, 0x21, 0x0000, 1); - if (gspca_dev->usb_buf[0] == 0) - return; - w += 15; - msleep(w); - } while (--i > 0); - PDEBUG(D_ERR, "wait_status_0 timeout"); - gspca_dev->usb_err = -ETIME; -} - -static void wait_status_1(struct gspca_dev *gspca_dev) -{ - int i; - - i = 10; - do { - reg_r(gspca_dev, 0x21, 0x0001, 1); - msleep(10); - if (gspca_dev->usb_buf[0] == 1) { - reg_wb(gspca_dev, 0x21, 0x0000, 0x0001, 0x00); - reg_r(gspca_dev, 0x21, 0x0001, 1); - return; - } - } while (--i > 0); - PDEBUG(D_ERR, "wait_status_1 timeout"); - gspca_dev->usb_err = -ETIME; -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, val); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, val); -} - -static void sethue(struct gspca_dev *gspca_dev, s32 val) -{ - reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, val); -} - -static void setcolor(struct gspca_dev *gspca_dev, s32 val) -{ - reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, val); -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, val); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - gspca_dev->cam.cam_mode = vga_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */ - /*fixme: 256 in ms-win traces*/ - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 0x00, 0x0001, 0x2067); - reg_w(gspca_dev, 0x00, 0x00d0, 0x206b); - reg_w(gspca_dev, 0x00, 0x0000, 0x206c); - reg_w(gspca_dev, 0x00, 0x0001, 0x2069); - msleep(8); - reg_w(gspca_dev, 0x00, 0x00c0, 0x206b); - reg_w(gspca_dev, 0x00, 0x0000, 0x206c); - reg_w(gspca_dev, 0x00, 0x0001, 0x2069); - - reg_r(gspca_dev, 0x20, 0x0000, 1); - reg_r(gspca_dev, 0x20, 0x0000, 5); - reg_r(gspca_dev, 0x23, 0x0000, 64); - PDEBUG(D_PROBE, "%s%s", &gspca_dev->usb_buf[0x1c], - &gspca_dev->usb_buf[0x30]); - reg_r(gspca_dev, 0x23, 0x0001, 64); - return gspca_dev->usb_err; -} - -/* function called at start time before URB creation */ -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - u8 mode; - - reg_r(gspca_dev, 0x00, 0x2520, 1); - wait_status_0(gspca_dev); - reg_w(gspca_dev, 0xc5, 0x0003, 0x0000); - wait_status_1(gspca_dev); - - wait_status_0(gspca_dev); - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode); - reg_r(gspca_dev, 0x25, 0x0004, 1); - reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); /* 420 */ - reg_r(gspca_dev, 0x27, 0x0000, 1); - -/* not useful.. - gspca_dev->alt = 4; * use alternate setting 3 */ - - return gspca_dev->usb_err; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* initialize the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x22); /* JPEG 411 */ - - /* the JPEG quality shall be 85% */ - jpeg_set_qual(sd->jpeg_hdr, 85); - - reg_r(gspca_dev, 0x00, 0x2520, 1); - msleep(8); - - /* start the capture */ - wait_status_0(gspca_dev); - reg_w(gspca_dev, 0x31, 0x0000, 0x0004); /* start request */ - wait_status_1(gspca_dev); - wait_status_0(gspca_dev); - msleep(200); - - sd->pkt_seq = 0; - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - /* stop the capture */ - wait_status_0(gspca_dev); - reg_w(gspca_dev, 0x31, 0x0000, 0x0000); /* stop request */ - wait_status_1(gspca_dev); - wait_status_0(gspca_dev); -} - -/* move a packet adding 0x00 after 0xff */ -static void add_packet(struct gspca_dev *gspca_dev, - u8 *data, - int len) -{ - int i; - - i = 0; - do { - if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, - data, i + 1); - len -= i; - data += i; - *data = 0x00; - i = 0; - } - } while (++i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - static const u8 ffd9[] = {0xff, 0xd9}; - - /* image packets start with: - * 02 8n - * with bit: - * 0x01: even (0) / odd (1) image - * 0x02: end of image when set - */ - if (len < 3) - return; /* empty packet */ - if (*data == 0x02) { - if (data[1] & 0x02) { - sd->pkt_seq = !(data[1] & 1); - add_packet(gspca_dev, data + 2, len - 2); - gspca_frame_add(gspca_dev, LAST_PACKET, - ffd9, 2); - return; - } - if ((data[1] & 1) != sd->pkt_seq) - goto err; - if (gspca_dev->last_packet_type == LAST_PACKET) - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - add_packet(gspca_dev, data + 2, len - 2); - return; - } -err: - gspca_dev->last_packet_type = DISCARD_PACKET; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_HUE: - sethue(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolor(gspca_dev, ctrl->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 8, 1, 1); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HUE, 0, 255, 1, 0); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 8, 1, 1); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 255, 1, 0); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x04fc, 0x1528)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - /* the video interface for isochronous transfer is 1 */ - if (intf->cur_altsetting->desc.bInterfaceNumber != 1) - return -ENODEV; - - return gspca_dev_probe2(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c deleted file mode 100644 index 25cb68d0556d..000000000000 --- a/drivers/media/video/gspca/spca500.c +++ /dev/null @@ -1,990 +0,0 @@ -/* - * SPCA500 chip based cameras initialization data - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "spca500" - -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -#define QUALITY 85 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - char subtype; -#define AgfaCl20 0 -#define AiptekPocketDV 1 -#define BenqDC1016 2 -#define CreativePCCam300 3 -#define DLinkDSC350 4 -#define Gsmartmini 5 -#define IntelPocketPCCamera 6 -#define KodakEZ200 7 -#define LogitechClickSmart310 8 -#define LogitechClickSmart510 9 -#define LogitechTraveler 10 -#define MustekGsmart300 11 -#define Optimedia 12 -#define PalmPixDC85 13 -#define ToptroIndus 14 - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -static const struct v4l2_pix_format sif_mode[] = { - {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -/* Frame packet header offsets for the spca500 */ -#define SPCA500_OFFSET_PADDINGLB 2 -#define SPCA500_OFFSET_PADDINGHB 3 -#define SPCA500_OFFSET_MODE 4 -#define SPCA500_OFFSET_IMGWIDTH 5 -#define SPCA500_OFFSET_IMGHEIGHT 6 -#define SPCA500_OFFSET_IMGMODE 7 -#define SPCA500_OFFSET_QTBLINDEX 8 -#define SPCA500_OFFSET_FRAMSEQ 9 -#define SPCA500_OFFSET_CDSPINFO 10 -#define SPCA500_OFFSET_GPIO 11 -#define SPCA500_OFFSET_AUGPIO 12 -#define SPCA500_OFFSET_DATA 16 - - -static const __u16 spca500_visual_defaults[][3] = { - {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, - * hue (H byte) = 0, - * saturation/hue enable, - * brightness/contrast enable. - */ - {0x00, 0x0000, 0x8167}, /* brightness = 0 */ - {0x00, 0x0020, 0x8168}, /* contrast = 0 */ - {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, - * hue (H byte) = 0, saturation/hue enable, - * brightness/contrast enable. - * was 0x0003, now 0x0000. - */ - {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */ - {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */ - {0x00, 0x0050, 0x8157}, /* edge gain high threshold */ - {0x00, 0x0030, 0x8158}, /* edge gain low threshold */ - {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */ - {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */ - {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */ - {0x0c, 0x0004, 0x0000}, - /* set interface */ - {} -}; -static const __u16 Clicksmart510_defaults[][3] = { - {0x00, 0x00, 0x8211}, - {0x00, 0x01, 0x82c0}, - {0x00, 0x10, 0x82cb}, - {0x00, 0x0f, 0x800d}, - {0x00, 0x82, 0x8225}, - {0x00, 0x21, 0x8228}, - {0x00, 0x00, 0x8203}, - {0x00, 0x00, 0x8204}, - {0x00, 0x08, 0x8205}, - {0x00, 0xf8, 0x8206}, - {0x00, 0x28, 0x8207}, - {0x00, 0xa0, 0x8208}, - {0x00, 0x08, 0x824a}, - {0x00, 0x08, 0x8214}, - {0x00, 0x80, 0x82c1}, - {0x00, 0x00, 0x82c2}, - {0x00, 0x00, 0x82ca}, - {0x00, 0x80, 0x82c1}, - {0x00, 0x04, 0x82c2}, - {0x00, 0x00, 0x82ca}, - {0x00, 0xfc, 0x8100}, - {0x00, 0xfc, 0x8105}, - {0x00, 0x30, 0x8101}, - {0x00, 0x00, 0x8102}, - {0x00, 0x00, 0x8103}, - {0x00, 0x66, 0x8107}, - {0x00, 0x00, 0x816b}, - {0x00, 0x00, 0x8155}, - {0x00, 0x01, 0x8156}, - {0x00, 0x60, 0x8157}, - {0x00, 0x40, 0x8158}, - {0x00, 0x0a, 0x8159}, - {0x00, 0x06, 0x815a}, - {0x00, 0x00, 0x813f}, - {0x00, 0x00, 0x8200}, - {0x00, 0x19, 0x8201}, - {0x00, 0x00, 0x82c1}, - {0x00, 0xa0, 0x82c2}, - {0x00, 0x00, 0x82ca}, - {0x00, 0x00, 0x8117}, - {0x00, 0x00, 0x8118}, - {0x00, 0x65, 0x8119}, - {0x00, 0x00, 0x811a}, - {0x00, 0x00, 0x811b}, - {0x00, 0x55, 0x811c}, - {0x00, 0x65, 0x811d}, - {0x00, 0x55, 0x811e}, - {0x00, 0x16, 0x811f}, - {0x00, 0x19, 0x8120}, - {0x00, 0x80, 0x8103}, - {0x00, 0x83, 0x816b}, - {0x00, 0x25, 0x8168}, - {0x00, 0x01, 0x820f}, - {0x00, 0xff, 0x8115}, - {0x00, 0x48, 0x8116}, - {0x00, 0x50, 0x8151}, - {0x00, 0x40, 0x8152}, - {0x00, 0x78, 0x8153}, - {0x00, 0x40, 0x8154}, - {0x00, 0x00, 0x8167}, - {0x00, 0x20, 0x8168}, - {0x00, 0x00, 0x816a}, - {0x00, 0x03, 0x816b}, - {0x00, 0x20, 0x8169}, - {0x00, 0x60, 0x8157}, - {0x00, 0x00, 0x8190}, - {0x00, 0x00, 0x81a1}, - {0x00, 0x00, 0x81b2}, - {0x00, 0x27, 0x8191}, - {0x00, 0x27, 0x81a2}, - {0x00, 0x27, 0x81b3}, - {0x00, 0x4b, 0x8192}, - {0x00, 0x4b, 0x81a3}, - {0x00, 0x4b, 0x81b4}, - {0x00, 0x66, 0x8193}, - {0x00, 0x66, 0x81a4}, - {0x00, 0x66, 0x81b5}, - {0x00, 0x79, 0x8194}, - {0x00, 0x79, 0x81a5}, - {0x00, 0x79, 0x81b6}, - {0x00, 0x8a, 0x8195}, - {0x00, 0x8a, 0x81a6}, - {0x00, 0x8a, 0x81b7}, - {0x00, 0x9b, 0x8196}, - {0x00, 0x9b, 0x81a7}, - {0x00, 0x9b, 0x81b8}, - {0x00, 0xa6, 0x8197}, - {0x00, 0xa6, 0x81a8}, - {0x00, 0xa6, 0x81b9}, - {0x00, 0xb2, 0x8198}, - {0x00, 0xb2, 0x81a9}, - {0x00, 0xb2, 0x81ba}, - {0x00, 0xbe, 0x8199}, - {0x00, 0xbe, 0x81aa}, - {0x00, 0xbe, 0x81bb}, - {0x00, 0xc8, 0x819a}, - {0x00, 0xc8, 0x81ab}, - {0x00, 0xc8, 0x81bc}, - {0x00, 0xd2, 0x819b}, - {0x00, 0xd2, 0x81ac}, - {0x00, 0xd2, 0x81bd}, - {0x00, 0xdb, 0x819c}, - {0x00, 0xdb, 0x81ad}, - {0x00, 0xdb, 0x81be}, - {0x00, 0xe4, 0x819d}, - {0x00, 0xe4, 0x81ae}, - {0x00, 0xe4, 0x81bf}, - {0x00, 0xed, 0x819e}, - {0x00, 0xed, 0x81af}, - {0x00, 0xed, 0x81c0}, - {0x00, 0xf7, 0x819f}, - {0x00, 0xf7, 0x81b0}, - {0x00, 0xf7, 0x81c1}, - {0x00, 0xff, 0x81a0}, - {0x00, 0xff, 0x81b1}, - {0x00, 0xff, 0x81c2}, - {0x00, 0x03, 0x8156}, - {0x00, 0x00, 0x8211}, - {0x00, 0x20, 0x8168}, - {0x00, 0x01, 0x8202}, - {0x00, 0x30, 0x8101}, - {0x00, 0x00, 0x8111}, - {0x00, 0x00, 0x8112}, - {0x00, 0x00, 0x8113}, - {0x00, 0x00, 0x8114}, - {} -}; - -static const __u8 qtable_creative_pccam[2][64] = { - { /* Q-table Y-components */ - 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, - 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, - 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, - 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, - 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, - 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, - 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, - 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, - { /* Q-table C-components */ - 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, - 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} -}; - -static const __u8 qtable_kodak_ez200[2][64] = { - { /* Q-table Y-components */ - 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, - 0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06, - 0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06, - 0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06, - 0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08, - 0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09, - 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a, - 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a}, - { /* Q-table C-components */ - 0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a, - 0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, - 0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a} -}; - -static const __u8 qtable_pocketdv[2][64] = { - { /* Q-table Y-components start registers 0x8800 */ - 0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18, - 0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16, - 0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16, - 0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19, - 0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f, - 0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25, - 0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28, - 0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28, - }, - { /* Q-table C-components start registers 0x8840 */ - 0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28, - 0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28, - 0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28} -}; - -/* read 'len' bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 index, - __u16 length) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, length, 500); -} - -static int reg_w(struct gspca_dev *gspca_dev, - __u16 req, __u16 index, __u16 value) -{ - int ret; - - PDEBUG(D_USBO, "reg write: [0x%02x] = 0x%02x", index, value); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - if (ret < 0) - pr_err("reg write: error %d\n", ret); - return ret; -} - -/* returns: negative is error, pos or zero is data */ -static int reg_r_12(struct gspca_dev *gspca_dev, - __u16 req, /* bRequest */ - __u16 index, /* wIndex */ - __u16 length) /* wLength (1 or 2 only) */ -{ - int ret; - - gspca_dev->usb_buf[1] = 0; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, - gspca_dev->usb_buf, length, - 500); /* timeout */ - if (ret < 0) { - pr_err("reg_r_12 err %d\n", ret); - return ret; - } - return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; -} - -/* - * Simple function to wait for a given 8-bit value to be returned from - * a reg_read call. - * Returns: negative is error or timeout, zero is success. - */ -static int reg_r_wait(struct gspca_dev *gspca_dev, - __u16 reg, __u16 index, __u16 value) -{ - int ret, cnt = 20; - - while (--cnt > 0) { - ret = reg_r_12(gspca_dev, reg, index, 1); - if (ret == value) - return 0; - msleep(50); - } - return -EIO; -} - -static int write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][3]) -{ - int ret, i = 0; - - while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { - ret = reg_w(gspca_dev, data[i][0], data[i][2], data[i][1]); - if (ret < 0) - return ret; - i++; - } - return 0; -} - -static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, - unsigned int request, - unsigned int ybase, - unsigned int cbase, - const __u8 qtable[2][64]) -{ - int i, err; - - /* loop over y components */ - for (i = 0; i < 64; i++) { - err = reg_w(gspca_dev, request, ybase + i, qtable[0][i]); - if (err < 0) - return err; - } - - /* loop over c components */ - for (i = 0; i < 64; i++) { - err = reg_w(gspca_dev, request, cbase + i, qtable[1][i]); - if (err < 0) - return err; - } - return 0; -} - -static void spca500_ping310(struct gspca_dev *gspca_dev) -{ - reg_r(gspca_dev, 0x0d04, 2); - PDEBUG(D_STREAM, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x", - gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); -} - -static void spca500_clksmart310_init(struct gspca_dev *gspca_dev) -{ - reg_r(gspca_dev, 0x0d05, 2); - PDEBUG(D_STREAM, "ClickSmart310 init 0x0d05 0x%02x 0x%02x", - gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); - reg_w(gspca_dev, 0x00, 0x8167, 0x5a); - spca500_ping310(gspca_dev); - - reg_w(gspca_dev, 0x00, 0x8168, 0x22); - reg_w(gspca_dev, 0x00, 0x816a, 0xc0); - reg_w(gspca_dev, 0x00, 0x816b, 0x0b); - reg_w(gspca_dev, 0x00, 0x8169, 0x25); - reg_w(gspca_dev, 0x00, 0x8157, 0x5b); - reg_w(gspca_dev, 0x00, 0x8158, 0x5b); - reg_w(gspca_dev, 0x00, 0x813f, 0x03); - reg_w(gspca_dev, 0x00, 0x8151, 0x4a); - reg_w(gspca_dev, 0x00, 0x8153, 0x78); - reg_w(gspca_dev, 0x00, 0x0d01, 0x04); - /* 00 for adjust shutter */ - reg_w(gspca_dev, 0x00, 0x0d02, 0x01); - reg_w(gspca_dev, 0x00, 0x8169, 0x25); - reg_w(gspca_dev, 0x00, 0x0d01, 0x02); -} - -static void spca500_setmode(struct gspca_dev *gspca_dev, - __u8 xmult, __u8 ymult) -{ - int mode; - - /* set x multiplier */ - reg_w(gspca_dev, 0, 0x8001, xmult); - - /* set y multiplier */ - reg_w(gspca_dev, 0, 0x8002, ymult); - - /* use compressed mode, VGA, with mode specific subsample */ - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - reg_w(gspca_dev, 0, 0x8003, mode << 4); -} - -static int spca500_full_reset(struct gspca_dev *gspca_dev) -{ - int err; - - /* send the reset command */ - err = reg_w(gspca_dev, 0xe0, 0x0001, 0x0000); - if (err < 0) - return err; - - /* wait for the reset to complete */ - err = reg_r_wait(gspca_dev, 0x06, 0x0000, 0x0000); - if (err < 0) - return err; - err = reg_w(gspca_dev, 0xe0, 0x0000, 0x0000); - if (err < 0) - return err; - err = reg_r_wait(gspca_dev, 0x06, 0, 0); - if (err < 0) { - PDEBUG(D_ERR, "reg_r_wait() failed"); - return err; - } - /* all ok */ - return 0; -} - -/* Synchro the Bridge with sensor */ -/* Maybe that will work on all spca500 chip */ -/* because i only own a clicksmart310 try for that chip */ -/* using spca50x_set_packet_size() cause an Ooops here */ -/* usb_set_interface from kernel 2.6.x clear all the urb stuff */ -/* up-port the same feature as in 2.4.x kernel */ -static int spca500_synch310(struct gspca_dev *gspca_dev) -{ - if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) { - PDEBUG(D_ERR, "Set packet size: set interface error"); - goto error; - } - spca500_ping310(gspca_dev); - - reg_r(gspca_dev, 0x0d00, 1); - - /* need alt setting here */ - PDEBUG(D_PACK, "ClickSmart310 sync alt: %d", gspca_dev->alt); - - /* Windoze use pipe with altsetting 6 why 7 here */ - if (usb_set_interface(gspca_dev->dev, - gspca_dev->iface, - gspca_dev->alt) < 0) { - PDEBUG(D_ERR, "Set packet size: set interface error"); - goto error; - } - return 0; -error: - return -EBUSY; -} - -static void spca500_reinit(struct gspca_dev *gspca_dev) -{ - int err; - __u8 Data; - - /* some unknown command from Aiptek pocket dv and family300 */ - - reg_w(gspca_dev, 0x00, 0x0d01, 0x01); - reg_w(gspca_dev, 0x00, 0x0d03, 0x00); - reg_w(gspca_dev, 0x00, 0x0d02, 0x01); - - /* enable drop packet */ - reg_w(gspca_dev, 0x00, 0x850a, 0x0001); - - err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, - qtable_pocketdv); - if (err < 0) - PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init"); - - /* set qtable index */ - reg_w(gspca_dev, 0x00, 0x8880, 2); - /* family cam Quicksmart stuff */ - reg_w(gspca_dev, 0x00, 0x800a, 0x00); - /* Set agc transfer: synced between frames */ - reg_w(gspca_dev, 0x00, 0x820f, 0x01); - /* Init SDRAM - needed for SDRAM access */ - reg_w(gspca_dev, 0x00, 0x870a, 0x04); - /*Start init sequence or stream */ - reg_w(gspca_dev, 0, 0x8003, 0x00); - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - msleep(2000); - if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) { - reg_r(gspca_dev, 0x816b, 1); - Data = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0x00, 0x816b, Data); - } -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - sd->subtype = id->driver_info; - if (sd->subtype != LogitechClickSmart310) { - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - } else { - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); - } - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* initialisation of spca500 based cameras is deferred */ - PDEBUG(D_STREAM, "SPCA500 init"); - if (sd->subtype == LogitechClickSmart310) - spca500_clksmart310_init(gspca_dev); -/* else - spca500_initialise(gspca_dev); */ - PDEBUG(D_STREAM, "SPCA500 init done"); - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int err; - __u8 Data; - __u8 xmult, ymult; - - /* create the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x22); /* JPEG 411 */ - jpeg_set_qual(sd->jpeg_hdr, QUALITY); - - if (sd->subtype == LogitechClickSmart310) { - xmult = 0x16; - ymult = 0x12; - } else { - xmult = 0x28; - ymult = 0x1e; - } - - /* is there a sensor here ? */ - reg_r(gspca_dev, 0x8a04, 1); - PDEBUG(D_STREAM, "Spca500 Sensor Address 0x%02x", - gspca_dev->usb_buf[0]); - PDEBUG(D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02x, Ymult: 0x%02x", - gspca_dev->curr_mode, xmult, ymult); - - /* setup qtable */ - switch (sd->subtype) { - case LogitechClickSmart310: - spca500_setmode(gspca_dev, xmult, ymult); - - /* enable drop packet */ - reg_w(gspca_dev, 0x00, 0x850a, 0x0001); - reg_w(gspca_dev, 0x00, 0x8880, 3); - err = spca50x_setup_qtable(gspca_dev, - 0x00, 0x8800, 0x8840, - qtable_creative_pccam); - if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); - /* Init SDRAM - needed for SDRAM access */ - reg_w(gspca_dev, 0x00, 0x870a, 0x04); - - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - msleep(500); - if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); - - reg_r(gspca_dev, 0x816b, 1); - Data = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0x00, 0x816b, Data); - - spca500_synch310(gspca_dev); - - write_vector(gspca_dev, spca500_visual_defaults); - spca500_setmode(gspca_dev, xmult, ymult); - /* enable drop packet */ - err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); - if (err < 0) - PDEBUG(D_ERR, "failed to enable drop packet"); - reg_w(gspca_dev, 0x00, 0x8880, 3); - err = spca50x_setup_qtable(gspca_dev, - 0x00, 0x8800, 0x8840, - qtable_creative_pccam); - if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); - - /* Init SDRAM - needed for SDRAM access */ - reg_w(gspca_dev, 0x00, 0x870a, 0x04); - - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - - if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); - - reg_r(gspca_dev, 0x816b, 1); - Data = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0x00, 0x816b, Data); - break; - case CreativePCCam300: /* Creative PC-CAM 300 640x480 CCD */ - case IntelPocketPCCamera: /* FIXME: Temporary fix for - * Intel Pocket PC Camera - * - NWG (Sat 29th March 2003) */ - - /* do a full reset */ - err = spca500_full_reset(gspca_dev); - if (err < 0) - PDEBUG(D_ERR, "spca500_full_reset failed"); - - /* enable drop packet */ - err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); - if (err < 0) - PDEBUG(D_ERR, "failed to enable drop packet"); - reg_w(gspca_dev, 0x00, 0x8880, 3); - err = spca50x_setup_qtable(gspca_dev, - 0x00, 0x8800, 0x8840, - qtable_creative_pccam); - if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); - - spca500_setmode(gspca_dev, xmult, ymult); - reg_w(gspca_dev, 0x20, 0x0001, 0x0004); - - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - - if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); - - reg_r(gspca_dev, 0x816b, 1); - Data = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0x00, 0x816b, Data); - -/* write_vector(gspca_dev, spca500_visual_defaults); */ - break; - case KodakEZ200: /* Kodak EZ200 */ - - /* do a full reset */ - err = spca500_full_reset(gspca_dev); - if (err < 0) - PDEBUG(D_ERR, "spca500_full_reset failed"); - /* enable drop packet */ - reg_w(gspca_dev, 0x00, 0x850a, 0x0001); - reg_w(gspca_dev, 0x00, 0x8880, 0); - err = spca50x_setup_qtable(gspca_dev, - 0x00, 0x8800, 0x8840, - qtable_kodak_ez200); - if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); - spca500_setmode(gspca_dev, xmult, ymult); - - reg_w(gspca_dev, 0x20, 0x0001, 0x0004); - - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - - if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) - PDEBUG(D_ERR, "reg_r_wait() failed"); - - reg_r(gspca_dev, 0x816b, 1); - Data = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0x00, 0x816b, Data); - -/* write_vector(gspca_dev, spca500_visual_defaults); */ - break; - - case BenqDC1016: - case DLinkDSC350: /* FamilyCam 300 */ - case AiptekPocketDV: /* Aiptek PocketDV */ - case Gsmartmini: /*Mustek Gsmart Mini */ - case MustekGsmart300: /* Mustek Gsmart 300 */ - case PalmPixDC85: - case Optimedia: - case ToptroIndus: - case AgfaCl20: - spca500_reinit(gspca_dev); - reg_w(gspca_dev, 0x00, 0x0d01, 0x01); - /* enable drop packet */ - reg_w(gspca_dev, 0x00, 0x850a, 0x0001); - - err = spca50x_setup_qtable(gspca_dev, - 0x00, 0x8800, 0x8840, qtable_pocketdv); - if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); - reg_w(gspca_dev, 0x00, 0x8880, 2); - - /* familycam Quicksmart pocketDV stuff */ - reg_w(gspca_dev, 0x00, 0x800a, 0x00); - /* Set agc transfer: synced between frames */ - reg_w(gspca_dev, 0x00, 0x820f, 0x01); - /* Init SDRAM - needed for SDRAM access */ - reg_w(gspca_dev, 0x00, 0x870a, 0x04); - - spca500_setmode(gspca_dev, xmult, ymult); - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - - reg_r_wait(gspca_dev, 0, 0x8000, 0x44); - - reg_r(gspca_dev, 0x816b, 1); - Data = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0x00, 0x816b, Data); - break; - case LogitechTraveler: - case LogitechClickSmart510: - reg_w(gspca_dev, 0x02, 0x00, 0x00); - /* enable drop packet */ - reg_w(gspca_dev, 0x00, 0x850a, 0x0001); - - err = spca50x_setup_qtable(gspca_dev, - 0x00, 0x8800, - 0x8840, qtable_creative_pccam); - if (err < 0) - PDEBUG(D_ERR, "spca50x_setup_qtable failed"); - reg_w(gspca_dev, 0x00, 0x8880, 3); - reg_w(gspca_dev, 0x00, 0x800a, 0x00); - /* Init SDRAM - needed for SDRAM access */ - reg_w(gspca_dev, 0x00, 0x870a, 0x04); - - spca500_setmode(gspca_dev, xmult, ymult); - - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - reg_r_wait(gspca_dev, 0, 0x8000, 0x44); - - reg_r(gspca_dev, 0x816b, 1); - Data = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0x00, 0x816b, Data); - write_vector(gspca_dev, Clicksmart510_defaults); - break; - } - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 0, 0x8003, 0x00); - - /* switch to video camera mode */ - reg_w(gspca_dev, 0x00, 0x8000, 0x0004); - reg_r(gspca_dev, 0x8000, 1); - PDEBUG(D_STREAM, "stop SPCA500 done reg8000: 0x%2x", - gspca_dev->usb_buf[0]); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - static __u8 ffd9[] = {0xff, 0xd9}; - -/* frames are jpeg 4.1.1 without 0xff escape */ - if (data[0] == 0xff) { - if (data[1] != 0x01) { /* drop packet */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - } - gspca_frame_add(gspca_dev, LAST_PACKET, - ffd9, 2); - - /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - - data += SPCA500_OFFSET_DATA; - len -= SPCA500_OFFSET_DATA; - } else { - data += 1; - len -= 1; - } - - /* add 0x00 after 0xff */ - i = 0; - do { - if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, - data, i + 1); - len -= i; - data += i; - *data = 0x00; - i = 0; - } - i++; - } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w(gspca_dev, 0x00, 0x8167, - (__u8) (val - 128)); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w(gspca_dev, 0x00, 0x8168, val); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w(gspca_dev, 0x00, 0x8169, val); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 3); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 63, 1, 31); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 63, 1, 31); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x040a, 0x0300), .driver_info = KodakEZ200}, - {USB_DEVICE(0x041e, 0x400a), .driver_info = CreativePCCam300}, - {USB_DEVICE(0x046d, 0x0890), .driver_info = LogitechTraveler}, - {USB_DEVICE(0x046d, 0x0900), .driver_info = LogitechClickSmart310}, - {USB_DEVICE(0x046d, 0x0901), .driver_info = LogitechClickSmart510}, - {USB_DEVICE(0x04a5, 0x300c), .driver_info = BenqDC1016}, - {USB_DEVICE(0x04fc, 0x7333), .driver_info = PalmPixDC85}, - {USB_DEVICE(0x055f, 0xc200), .driver_info = MustekGsmart300}, - {USB_DEVICE(0x055f, 0xc220), .driver_info = Gsmartmini}, - {USB_DEVICE(0x06bd, 0x0404), .driver_info = AgfaCl20}, - {USB_DEVICE(0x06be, 0x0800), .driver_info = Optimedia}, - {USB_DEVICE(0x084d, 0x0003), .driver_info = DLinkDSC350}, - {USB_DEVICE(0x08ca, 0x0103), .driver_info = AiptekPocketDV}, - {USB_DEVICE(0x2899, 0x012c), .driver_info = ToptroIndus}, - {USB_DEVICE(0x8086, 0x0630), .driver_info = IntelPocketPCCamera}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c deleted file mode 100644 index 3b7f777785b4..000000000000 --- a/drivers/media/video/gspca/spca501.c +++ /dev/null @@ -1,2054 +0,0 @@ -/* - * SPCA501 chip based cameras initialization data - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "spca501" - -#include "gspca.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - unsigned short contrast; - __u8 brightness; - __u8 colors; - __u8 blue_balance; - __u8 red_balance; - - char subtype; -#define Arowana300KCMOSCamera 0 -#define IntelCreateAndShare 1 -#define KodakDVC325 2 -#define MystFromOriUnknownCamera 3 -#define SmileIntlCamera 4 -#define ThreeComHomeConnectLite 5 -#define ViewQuestM318B 6 -}; - -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -#define SPCA50X_REG_USB 0x2 /* spca505 501 */ -/* - * Data to initialize a SPCA501. From a capture file provided by Bill Roehl - * With SPCA501 chip description - */ -#define CCDSP_SET /* set CCDSP parameters */ -#define TG_SET /* set time generator set */ -#undef DSPWIN_SET /* set DSP windows parameters */ -#undef ALTER_GAMA /* Set alternate set to YUV transform coeffs. */ -#define SPCA501_SNAPBIT 0x80 -#define SPCA501_SNAPCTRL 0x10 -/* Frame packet header offsets for the spca501 */ -#define SPCA501_OFFSET_GPIO 1 -#define SPCA501_OFFSET_TYPE 2 -#define SPCA501_OFFSET_TURN3A 3 -#define SPCA501_OFFSET_FRAMSEQ 4 -#define SPCA501_OFFSET_COMPRESS 5 -#define SPCA501_OFFSET_QUANT 6 -#define SPCA501_OFFSET_QUANT2 7 -#define SPCA501_OFFSET_DATA 8 - -#define SPCA501_PROP_COMP_ENABLE(d) ((d) & 1) -#define SPCA501_PROP_SNAP(d) ((d) & 0x40) -#define SPCA501_PROP_SNAP_CTRL(d) ((d) & 0x10) -#define SPCA501_PROP_COMP_THRESH(d) (((d) & 0x0e) >> 1) -#define SPCA501_PROP_COMP_QUANT(d) (((d) & 0x70) >> 4) - -/* SPCA501 CCDSP control */ -#define SPCA501_REG_CCDSP 0x01 -/* SPCA501 control/status registers */ -#define SPCA501_REG_CTLRL 0x02 - -/* registers for color correction and YUV transformation */ -#define SPCA501_A11 0x08 -#define SPCA501_A12 0x09 -#define SPCA501_A13 0x0A -#define SPCA501_A21 0x0B -#define SPCA501_A22 0x0C -#define SPCA501_A23 0x0D -#define SPCA501_A31 0x0E -#define SPCA501_A32 0x0F -#define SPCA501_A33 0x10 - -/* Data for video camera initialization before capturing */ -static const __u16 spca501_open_data[][3] = { - /* bmRequest,value,index */ - - {0x2, 0x50, 0x00}, /* C/S enable soft reset */ - {0x2, 0x40, 0x00}, /* C/S disable soft reset */ - {0x2, 0x02, 0x05}, /* C/S general purpose I/O data */ - {0x2, 0x03, 0x05}, /* C/S general purpose I/O data */ - -#ifdef CCDSP_SET - {0x1, 0x38, 0x01}, /* CCDSP options */ - {0x1, 0x05, 0x02}, /* CCDSP Optical black level for user settings */ - {0x1, 0xC0, 0x03}, /* CCDSP Optical black settings */ - - {0x1, 0x67, 0x07}, - {0x1, 0x63, 0x3f}, /* CCDSP CCD gamma enable */ - {0x1, 0x03, 0x56}, /* Add gamma correction */ - - {0x1, 0xFF, 0x15}, /* CCDSP High luminance for white balance */ - {0x1, 0x01, 0x16}, /* CCDSP Low luminance for white balance */ - -/* Color correction and RGB-to-YUV transformation coefficients changing */ -#ifdef ALTER_GAMA - {0x0, 0x00, 0x08}, /* A11 */ - {0x0, 0x00, 0x09}, /* A12 */ - {0x0, 0x90, 0x0A}, /* A13 */ - {0x0, 0x12, 0x0B}, /* A21 */ - {0x0, 0x00, 0x0C}, /* A22 */ - {0x0, 0x00, 0x0D}, /* A23 */ - {0x0, 0x00, 0x0E}, /* A31 */ - {0x0, 0x02, 0x0F}, /* A32 */ - {0x0, 0x00, 0x10}, /* A33 */ -#else - {0x1, 0x2a, 0x08}, /* A11 0x31 */ - {0x1, 0xf8, 0x09}, /* A12 f8 */ - {0x1, 0xf8, 0x0A}, /* A13 f8 */ - {0x1, 0xf8, 0x0B}, /* A21 f8 */ - {0x1, 0x14, 0x0C}, /* A22 0x14 */ - {0x1, 0xf8, 0x0D}, /* A23 f8 */ - {0x1, 0xf8, 0x0E}, /* A31 f8 */ - {0x1, 0xf8, 0x0F}, /* A32 f8 */ - {0x1, 0x20, 0x10}, /* A33 0x20 */ -#endif - {0x1, 0x00, 0x11}, /* R offset */ - {0x1, 0x00, 0x12}, /* G offset */ - {0x1, 0x00, 0x13}, /* B offset */ - {0x1, 0x00, 0x14}, /* GB offset */ - -#endif - -#ifdef TG_SET - /* Time generator manipulations */ - {0x0, 0xfc, 0x0}, /* Set up high bits of shutter speed */ - {0x0, 0x01, 0x1}, /* Set up low bits of shutter speed */ - - {0x0, 0xe4, 0x04}, /* DCLK*2 clock phase adjustment */ - {0x0, 0x08, 0x05}, /* ADCK phase adjustment, inv. ext. VB */ - {0x0, 0x03, 0x06}, /* FR phase adjustment */ - {0x0, 0x01, 0x07}, /* FCDS phase adjustment */ - {0x0, 0x39, 0x08}, /* FS phase adjustment */ - {0x0, 0x88, 0x0a}, /* FH1 phase and delay adjustment */ - {0x0, 0x03, 0x0f}, /* pixel identification */ - {0x0, 0x00, 0x11}, /* clock source selection (default) */ - - /*VERY strange manipulations with - * select DMCLP or OBPX to be ADCLP output (0x0C) - * OPB always toggle or not (0x0D) but they allow - * us to set up brightness - */ - {0x0, 0x01, 0x0c}, - {0x0, 0xe0, 0x0d}, - /* Done */ -#endif - -#ifdef DSPWIN_SET - {0x1, 0xa0, 0x01}, /* Setting image processing parameters */ - {0x1, 0x1c, 0x17}, /* Changing Windows positions X1 */ - {0x1, 0xe2, 0x19}, /* X2 */ - {0x1, 0x1c, 0x1b}, /* X3 */ - {0x1, 0xe2, 0x1d}, /* X4 */ - {0x1, 0x5f, 0x1f}, /* X5 */ - {0x1, 0x32, 0x20}, /* Y5 */ - {0x1, 0x01, 0x10}, /* Changing A33 */ -#endif - - {0x2, 0x204a, 0x07},/* Setting video compression & resolution 160x120 */ - {0x2, 0x94, 0x06}, /* Setting video no compression */ - {} -}; - -/* - The SPCAxxx docs from Sunplus document these values - in tables, one table per register number. In the data - below, dmRequest is the register number, index is the Addr, - and value is a combination of Bit values. - Bit Value (hex) - 0 01 - 1 02 - 2 04 - 3 08 - 4 10 - 5 20 - 6 40 - 7 80 - */ - -/* Data for chip initialization (set default values) */ -static const __u16 spca501_init_data[][3] = { - /* Set all the values to powerup defaults */ - /* bmRequest,value,index */ - {0x0, 0xAA, 0x00}, - {0x0, 0x02, 0x01}, - {0x0, 0x01, 0x02}, - {0x0, 0x02, 0x03}, - {0x0, 0xCE, 0x04}, - {0x0, 0x00, 0x05}, - {0x0, 0x00, 0x06}, - {0x0, 0x00, 0x07}, - {0x0, 0x00, 0x08}, - {0x0, 0x00, 0x09}, - {0x0, 0x90, 0x0A}, - {0x0, 0x12, 0x0B}, - {0x0, 0x00, 0x0C}, - {0x0, 0x00, 0x0D}, - {0x0, 0x00, 0x0E}, - {0x0, 0x02, 0x0F}, - {0x0, 0x00, 0x10}, - {0x0, 0x00, 0x11}, - {0x0, 0x00, 0x12}, - {0x0, 0x00, 0x13}, - {0x0, 0x00, 0x14}, - {0x0, 0x00, 0x15}, - {0x0, 0x00, 0x16}, - {0x0, 0x00, 0x17}, - {0x0, 0x00, 0x18}, - {0x0, 0x00, 0x19}, - {0x0, 0x00, 0x1A}, - {0x0, 0x00, 0x1B}, - {0x0, 0x00, 0x1C}, - {0x0, 0x00, 0x1D}, - {0x0, 0x00, 0x1E}, - {0x0, 0x00, 0x1F}, - {0x0, 0x00, 0x20}, - {0x0, 0x00, 0x21}, - {0x0, 0x00, 0x22}, - {0x0, 0x00, 0x23}, - {0x0, 0x00, 0x24}, - {0x0, 0x00, 0x25}, - {0x0, 0x00, 0x26}, - {0x0, 0x00, 0x27}, - {0x0, 0x00, 0x28}, - {0x0, 0x00, 0x29}, - {0x0, 0x00, 0x2A}, - {0x0, 0x00, 0x2B}, - {0x0, 0x00, 0x2C}, - {0x0, 0x00, 0x2D}, - {0x0, 0x00, 0x2E}, - {0x0, 0x00, 0x2F}, - {0x0, 0x00, 0x30}, - {0x0, 0x00, 0x31}, - {0x0, 0x00, 0x32}, - {0x0, 0x00, 0x33}, - {0x0, 0x00, 0x34}, - {0x0, 0x00, 0x35}, - {0x0, 0x00, 0x36}, - {0x0, 0x00, 0x37}, - {0x0, 0x00, 0x38}, - {0x0, 0x00, 0x39}, - {0x0, 0x00, 0x3A}, - {0x0, 0x00, 0x3B}, - {0x0, 0x00, 0x3C}, - {0x0, 0x00, 0x3D}, - {0x0, 0x00, 0x3E}, - {0x0, 0x00, 0x3F}, - {0x0, 0x00, 0x40}, - {0x0, 0x00, 0x41}, - {0x0, 0x00, 0x42}, - {0x0, 0x00, 0x43}, - {0x0, 0x00, 0x44}, - {0x0, 0x00, 0x45}, - {0x0, 0x00, 0x46}, - {0x0, 0x00, 0x47}, - {0x0, 0x00, 0x48}, - {0x0, 0x00, 0x49}, - {0x0, 0x00, 0x4A}, - {0x0, 0x00, 0x4B}, - {0x0, 0x00, 0x4C}, - {0x0, 0x00, 0x4D}, - {0x0, 0x00, 0x4E}, - {0x0, 0x00, 0x4F}, - {0x0, 0x00, 0x50}, - {0x0, 0x00, 0x51}, - {0x0, 0x00, 0x52}, - {0x0, 0x00, 0x53}, - {0x0, 0x00, 0x54}, - {0x0, 0x00, 0x55}, - {0x0, 0x00, 0x56}, - {0x0, 0x00, 0x57}, - {0x0, 0x00, 0x58}, - {0x0, 0x00, 0x59}, - {0x0, 0x00, 0x5A}, - {0x0, 0x00, 0x5B}, - {0x0, 0x00, 0x5C}, - {0x0, 0x00, 0x5D}, - {0x0, 0x00, 0x5E}, - {0x0, 0x00, 0x5F}, - {0x0, 0x00, 0x60}, - {0x0, 0x00, 0x61}, - {0x0, 0x00, 0x62}, - {0x0, 0x00, 0x63}, - {0x0, 0x00, 0x64}, - {0x0, 0x00, 0x65}, - {0x0, 0x00, 0x66}, - {0x0, 0x00, 0x67}, - {0x0, 0x00, 0x68}, - {0x0, 0x00, 0x69}, - {0x0, 0x00, 0x6A}, - {0x0, 0x00, 0x6B}, - {0x0, 0x00, 0x6C}, - {0x0, 0x00, 0x6D}, - {0x0, 0x00, 0x6E}, - {0x0, 0x00, 0x6F}, - {0x0, 0x00, 0x70}, - {0x0, 0x00, 0x71}, - {0x0, 0x00, 0x72}, - {0x0, 0x00, 0x73}, - {0x0, 0x00, 0x74}, - {0x0, 0x00, 0x75}, - {0x0, 0x00, 0x76}, - {0x0, 0x00, 0x77}, - {0x0, 0x00, 0x78}, - {0x0, 0x00, 0x79}, - {0x0, 0x00, 0x7A}, - {0x0, 0x00, 0x7B}, - {0x0, 0x00, 0x7C}, - {0x0, 0x00, 0x7D}, - {0x0, 0x00, 0x7E}, - {0x0, 0x00, 0x7F}, - {0x0, 0x00, 0x80}, - {0x0, 0x00, 0x81}, - {0x0, 0x00, 0x82}, - {0x0, 0x00, 0x83}, - {0x0, 0x00, 0x84}, - {0x0, 0x00, 0x85}, - {0x0, 0x00, 0x86}, - {0x0, 0x00, 0x87}, - {0x0, 0x00, 0x88}, - {0x0, 0x00, 0x89}, - {0x0, 0x00, 0x8A}, - {0x0, 0x00, 0x8B}, - {0x0, 0x00, 0x8C}, - {0x0, 0x00, 0x8D}, - {0x0, 0x00, 0x8E}, - {0x0, 0x00, 0x8F}, - {0x0, 0x00, 0x90}, - {0x0, 0x00, 0x91}, - {0x0, 0x00, 0x92}, - {0x0, 0x00, 0x93}, - {0x0, 0x00, 0x94}, - {0x0, 0x00, 0x95}, - {0x0, 0x00, 0x96}, - {0x0, 0x00, 0x97}, - {0x0, 0x00, 0x98}, - {0x0, 0x00, 0x99}, - {0x0, 0x00, 0x9A}, - {0x0, 0x00, 0x9B}, - {0x0, 0x00, 0x9C}, - {0x0, 0x00, 0x9D}, - {0x0, 0x00, 0x9E}, - {0x0, 0x00, 0x9F}, - {0x0, 0x00, 0xA0}, - {0x0, 0x00, 0xA1}, - {0x0, 0x00, 0xA2}, - {0x0, 0x00, 0xA3}, - {0x0, 0x00, 0xA4}, - {0x0, 0x00, 0xA5}, - {0x0, 0x00, 0xA6}, - {0x0, 0x00, 0xA7}, - {0x0, 0x00, 0xA8}, - {0x0, 0x00, 0xA9}, - {0x0, 0x00, 0xAA}, - {0x0, 0x00, 0xAB}, - {0x0, 0x00, 0xAC}, - {0x0, 0x00, 0xAD}, - {0x0, 0x00, 0xAE}, - {0x0, 0x00, 0xAF}, - {0x0, 0x00, 0xB0}, - {0x0, 0x00, 0xB1}, - {0x0, 0x00, 0xB2}, - {0x0, 0x00, 0xB3}, - {0x0, 0x00, 0xB4}, - {0x0, 0x00, 0xB5}, - {0x0, 0x00, 0xB6}, - {0x0, 0x00, 0xB7}, - {0x0, 0x00, 0xB8}, - {0x0, 0x00, 0xB9}, - {0x0, 0x00, 0xBA}, - {0x0, 0x00, 0xBB}, - {0x0, 0x00, 0xBC}, - {0x0, 0x00, 0xBD}, - {0x0, 0x00, 0xBE}, - {0x0, 0x00, 0xBF}, - {0x0, 0x00, 0xC0}, - {0x0, 0x00, 0xC1}, - {0x0, 0x00, 0xC2}, - {0x0, 0x00, 0xC3}, - {0x0, 0x00, 0xC4}, - {0x0, 0x00, 0xC5}, - {0x0, 0x00, 0xC6}, - {0x0, 0x00, 0xC7}, - {0x0, 0x00, 0xC8}, - {0x0, 0x00, 0xC9}, - {0x0, 0x00, 0xCA}, - {0x0, 0x00, 0xCB}, - {0x0, 0x00, 0xCC}, - {0x1, 0xF4, 0x00}, - {0x1, 0x38, 0x01}, - {0x1, 0x40, 0x02}, - {0x1, 0x0A, 0x03}, - {0x1, 0x40, 0x04}, - {0x1, 0x40, 0x05}, - {0x1, 0x40, 0x06}, - {0x1, 0x67, 0x07}, - {0x1, 0x31, 0x08}, - {0x1, 0x00, 0x09}, - {0x1, 0x00, 0x0A}, - {0x1, 0x00, 0x0B}, - {0x1, 0x14, 0x0C}, - {0x1, 0x00, 0x0D}, - {0x1, 0x00, 0x0E}, - {0x1, 0x00, 0x0F}, - {0x1, 0x1E, 0x10}, - {0x1, 0x00, 0x11}, - {0x1, 0x00, 0x12}, - {0x1, 0x00, 0x13}, - {0x1, 0x00, 0x14}, - {0x1, 0xFF, 0x15}, - {0x1, 0x01, 0x16}, - {0x1, 0x32, 0x17}, - {0x1, 0x23, 0x18}, - {0x1, 0xCE, 0x19}, - {0x1, 0x23, 0x1A}, - {0x1, 0x32, 0x1B}, - {0x1, 0x8D, 0x1C}, - {0x1, 0xCE, 0x1D}, - {0x1, 0x8D, 0x1E}, - {0x1, 0x00, 0x1F}, - {0x1, 0x00, 0x20}, - {0x1, 0xFF, 0x3E}, - {0x1, 0x02, 0x3F}, - {0x1, 0x00, 0x40}, - {0x1, 0x00, 0x41}, - {0x1, 0x00, 0x42}, - {0x1, 0x00, 0x43}, - {0x1, 0x00, 0x44}, - {0x1, 0x00, 0x45}, - {0x1, 0x00, 0x46}, - {0x1, 0x00, 0x47}, - {0x1, 0x00, 0x48}, - {0x1, 0x00, 0x49}, - {0x1, 0x00, 0x4A}, - {0x1, 0x00, 0x4B}, - {0x1, 0x00, 0x4C}, - {0x1, 0x00, 0x4D}, - {0x1, 0x00, 0x4E}, - {0x1, 0x00, 0x4F}, - {0x1, 0x00, 0x50}, - {0x1, 0x00, 0x51}, - {0x1, 0x00, 0x52}, - {0x1, 0x00, 0x53}, - {0x1, 0x00, 0x54}, - {0x1, 0x00, 0x55}, - {0x1, 0x00, 0x56}, - {0x1, 0x00, 0x57}, - {0x1, 0x00, 0x58}, - {0x1, 0x00, 0x59}, - {0x1, 0x00, 0x5A}, - {0x2, 0x03, 0x00}, - {0x2, 0x00, 0x01}, - {0x2, 0x00, 0x05}, - {0x2, 0x00, 0x06}, - {0x2, 0x00, 0x07}, - {0x2, 0x00, 0x10}, - {0x2, 0x00, 0x11}, - /* Strange - looks like the 501 driver doesn't do anything - * at insert time except read the EEPROM - */ - {} -}; - -/* Data for video camera init before capture. - * Capture and decoding by Colin Peart. - * This is is for the 3com HomeConnect Lite which is spca501a based. - */ -static const __u16 spca501_3com_open_data[][3] = { - /* bmRequest,value,index */ - {0x2, 0x0050, 0x0000}, /* C/S Enable TG soft reset, timing mode=010 */ - {0x2, 0x0043, 0x0000}, /* C/S Disable TG soft reset, timing mode=010 */ - {0x2, 0x0002, 0x0005}, /* C/S GPIO */ - {0x2, 0x0003, 0x0005}, /* C/S GPIO */ - -#ifdef CCDSP_SET - {0x1, 0x0020, 0x0001}, /* CCDSP Options */ - - {0x1, 0x0020, 0x0002}, /* CCDSP Black Level */ - {0x1, 0x006e, 0x0007}, /* CCDSP Gamma options */ - {0x1, 0x0090, 0x0015}, /* CCDSP Luminance Low */ - {0x1, 0x00ff, 0x0016}, /* CCDSP Luminance High */ - {0x1, 0x0003, 0x003F}, /* CCDSP Gamma correction toggle */ - -#ifdef ALTER_GAMMA - {0x1, 0x0010, 0x0008}, /* CCDSP YUV A11 */ - {0x1, 0x0000, 0x0009}, /* CCDSP YUV A12 */ - {0x1, 0x0000, 0x000a}, /* CCDSP YUV A13 */ - {0x1, 0x0000, 0x000b}, /* CCDSP YUV A21 */ - {0x1, 0x0010, 0x000c}, /* CCDSP YUV A22 */ - {0x1, 0x0000, 0x000d}, /* CCDSP YUV A23 */ - {0x1, 0x0000, 0x000e}, /* CCDSP YUV A31 */ - {0x1, 0x0000, 0x000f}, /* CCDSP YUV A32 */ - {0x1, 0x0010, 0x0010}, /* CCDSP YUV A33 */ - {0x1, 0x0000, 0x0011}, /* CCDSP R Offset */ - {0x1, 0x0000, 0x0012}, /* CCDSP G Offset */ - {0x1, 0x0001, 0x0013}, /* CCDSP B Offset */ - {0x1, 0x0001, 0x0014}, /* CCDSP BG Offset */ - {0x1, 0x003f, 0x00C1}, /* CCDSP Gamma Correction Enable */ -#endif -#endif - -#ifdef TG_SET - {0x0, 0x00fc, 0x0000}, /* TG Shutter Speed High Bits */ - {0x0, 0x0000, 0x0001}, /* TG Shutter Speed Low Bits */ - {0x0, 0x00e4, 0x0004}, /* TG DCLK*2 Adjust */ - {0x0, 0x0008, 0x0005}, /* TG ADCK Adjust */ - {0x0, 0x0003, 0x0006}, /* TG FR Phase Adjust */ - {0x0, 0x0001, 0x0007}, /* TG FCDS Phase Adjust */ - {0x0, 0x0039, 0x0008}, /* TG FS Phase Adjust */ - {0x0, 0x0088, 0x000a}, /* TG MH1 */ - {0x0, 0x0003, 0x000f}, /* TG Pixel ID */ - - /* Like below, unexplained toglleing */ - {0x0, 0x0080, 0x000c}, - {0x0, 0x0000, 0x000d}, - {0x0, 0x0080, 0x000c}, - {0x0, 0x0004, 0x000d}, - {0x0, 0x0000, 0x000c}, - {0x0, 0x0000, 0x000d}, - {0x0, 0x0040, 0x000c}, - {0x0, 0x0017, 0x000d}, - {0x0, 0x00c0, 0x000c}, - {0x0, 0x0000, 0x000d}, - {0x0, 0x0080, 0x000c}, - {0x0, 0x0006, 0x000d}, - {0x0, 0x0080, 0x000c}, - {0x0, 0x0004, 0x000d}, - {0x0, 0x0002, 0x0003}, -#endif - -#ifdef DSPWIN_SET - {0x1, 0x001c, 0x0017}, /* CCDSP W1 Start X */ - {0x1, 0x00e2, 0x0019}, /* CCDSP W2 Start X */ - {0x1, 0x001c, 0x001b}, /* CCDSP W3 Start X */ - {0x1, 0x00e2, 0x001d}, /* CCDSP W4 Start X */ - {0x1, 0x00aa, 0x001f}, /* CCDSP W5 Start X */ - {0x1, 0x0070, 0x0020}, /* CCDSP W5 Start Y */ -#endif - {0x0, 0x0001, 0x0010}, /* TG Start Clock */ - -/* {0x2, 0x006a, 0x0001}, * C/S Enable ISOSYNCH Packet Engine */ - {0x2, 0x0068, 0x0001}, /* C/S Diable ISOSYNCH Packet Engine */ - {0x2, 0x0000, 0x0005}, - {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ - {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ - {0x2, 0x0002, 0x0005}, /* C/S GPIO */ - {0x2, 0x0003, 0x0005}, /* C/S GPIO */ - - {0x2, 0x006a, 0x0001}, /* C/S Enable ISOSYNCH Packet Engine */ - {} -}; - -/* - * Data used to initialize a SPCA501C with HV7131B sensor. - * From a capture file taken with USBSnoop v 1.5 - * I have a "SPCA501C pc camera chipset" manual by sunplus, but some - * of the value meanings are obscure or simply "reserved". - * to do list: - * 1) Understand what every value means - * 2) Understand why some values seem to appear more than once - * 3) Write a small comment for each line of the following arrays. - */ -static const __u16 spca501c_arowana_open_data[][3] = { - /* bmRequest,value,index */ - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x01, 0x0006, 0x0011}, - {0x01, 0x00ff, 0x0012}, - {0x01, 0x0014, 0x0013}, - {0x01, 0x0000, 0x0014}, - {0x01, 0x0042, 0x0051}, - {0x01, 0x0040, 0x0052}, - {0x01, 0x0051, 0x0053}, - {0x01, 0x0040, 0x0054}, - {0x01, 0x0000, 0x0055}, - {0x00, 0x0025, 0x0000}, - {0x00, 0x0026, 0x0000}, - {0x00, 0x0001, 0x0000}, - {0x00, 0x0027, 0x0000}, - {0x00, 0x008a, 0x0000}, - {} -}; - -static const __u16 spca501c_arowana_init_data[][3] = { - /* bmRequest,value,index */ - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x01, 0x0006, 0x0011}, - {0x01, 0x00ff, 0x0012}, - {0x01, 0x0014, 0x0013}, - {0x01, 0x0000, 0x0014}, - {0x01, 0x0042, 0x0051}, - {0x01, 0x0040, 0x0052}, - {0x01, 0x0051, 0x0053}, - {0x01, 0x0040, 0x0054}, - {0x01, 0x0000, 0x0055}, - {0x00, 0x0025, 0x0000}, - {0x00, 0x0026, 0x0000}, - {0x00, 0x0001, 0x0000}, - {0x00, 0x0027, 0x0000}, - {0x00, 0x008a, 0x0000}, - {0x02, 0x0000, 0x0005}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x2000, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0015, 0x0001}, - {0x05, 0x00ea, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0023, 0x0001}, - {0x05, 0x0003, 0x0000}, - {0x05, 0x0030, 0x0001}, - {0x05, 0x002b, 0x0000}, - {0x05, 0x0031, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0032, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0033, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0034, 0x0001}, - {0x05, 0x0002, 0x0000}, - {0x05, 0x0050, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0051, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0052, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0054, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x00, 0x0000, 0x0001}, - {0x00, 0x0000, 0x0002}, - {0x00, 0x000c, 0x0003}, - {0x00, 0x0000, 0x0004}, - {0x00, 0x0090, 0x0005}, - {0x00, 0x0000, 0x0006}, - {0x00, 0x0040, 0x0007}, - {0x00, 0x00c0, 0x0008}, - {0x00, 0x004a, 0x0009}, - {0x00, 0x0000, 0x000a}, - {0x00, 0x0000, 0x000b}, - {0x00, 0x0001, 0x000c}, - {0x00, 0x0001, 0x000d}, - {0x00, 0x0000, 0x000e}, - {0x00, 0x0002, 0x000f}, - {0x00, 0x0001, 0x0010}, - {0x00, 0x0000, 0x0011}, - {0x00, 0x0000, 0x0012}, - {0x00, 0x0002, 0x0020}, - {0x00, 0x0080, 0x0021}, - {0x00, 0x0001, 0x0022}, - {0x00, 0x00e0, 0x0023}, - {0x00, 0x0000, 0x0024}, - {0x00, 0x00d5, 0x0025}, - {0x00, 0x0000, 0x0026}, - {0x00, 0x000b, 0x0027}, - {0x00, 0x0000, 0x0046}, - {0x00, 0x0000, 0x0047}, - {0x00, 0x0000, 0x0048}, - {0x00, 0x0000, 0x0049}, - {0x00, 0x0008, 0x004a}, - {0xff, 0x0000, 0x00d0}, - {0xff, 0x00d8, 0x00d1}, - {0xff, 0x0000, 0x00d4}, - {0xff, 0x0000, 0x00d5}, - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0028, 0x0001}, - {0x01, 0x0000, 0x0002}, - {0x01, 0x000a, 0x0003}, - {0x01, 0x0040, 0x0004}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0x00fd, 0x000a}, - {0x01, 0x0038, 0x000b}, - {0x01, 0x00d1, 0x000c}, - {0x01, 0x00f7, 0x000d}, - {0x01, 0x00ed, 0x000e}, - {0x01, 0x00d8, 0x000f}, - {0x01, 0x0038, 0x0010}, - {0x01, 0x00ff, 0x0015}, - {0x01, 0x0001, 0x0016}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x0000, 0x001f}, - {0x01, 0x0000, 0x0020}, - {0x01, 0x00ff, 0x003e}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0000, 0x0040}, - {0x01, 0x0035, 0x0041}, - {0x01, 0x0053, 0x0042}, - {0x01, 0x0069, 0x0043}, - {0x01, 0x007c, 0x0044}, - {0x01, 0x008c, 0x0045}, - {0x01, 0x009a, 0x0046}, - {0x01, 0x00a8, 0x0047}, - {0x01, 0x00b4, 0x0048}, - {0x01, 0x00bf, 0x0049}, - {0x01, 0x00ca, 0x004a}, - {0x01, 0x00d4, 0x004b}, - {0x01, 0x00dd, 0x004c}, - {0x01, 0x00e7, 0x004d}, - {0x01, 0x00ef, 0x004e}, - {0x01, 0x00f8, 0x004f}, - {0x01, 0x00ff, 0x0050}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0060, 0x0057}, - {0x01, 0x0040, 0x0058}, - {0x01, 0x0011, 0x0059}, - {0x01, 0x0001, 0x005a}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x0015, 0x0006}, - {0x02, 0x100a, 0x0007}, - {0x02, 0xa048, 0x0000}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0025, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0001, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0020, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x00, 0x0090, 0x0005}, - {0x01, 0x00a6, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x2000, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0015, 0x0001}, - {0x05, 0x00ea, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0023, 0x0001}, - {0x05, 0x0003, 0x0000}, - {0x05, 0x0030, 0x0001}, - {0x05, 0x002b, 0x0000}, - {0x05, 0x0031, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0032, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0033, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0034, 0x0001}, - {0x05, 0x0002, 0x0000}, - {0x05, 0x0050, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0051, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0052, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0054, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x00, 0x0000, 0x0001}, - {0x00, 0x0000, 0x0002}, - {0x00, 0x000c, 0x0003}, - {0x00, 0x0000, 0x0004}, - {0x00, 0x0090, 0x0005}, - {0x00, 0x0000, 0x0006}, - {0x00, 0x0040, 0x0007}, - {0x00, 0x00c0, 0x0008}, - {0x00, 0x004a, 0x0009}, - {0x00, 0x0000, 0x000a}, - {0x00, 0x0000, 0x000b}, - {0x00, 0x0001, 0x000c}, - {0x00, 0x0001, 0x000d}, - {0x00, 0x0000, 0x000e}, - {0x00, 0x0002, 0x000f}, - {0x00, 0x0001, 0x0010}, - {0x00, 0x0000, 0x0011}, - {0x00, 0x0000, 0x0012}, - {0x00, 0x0002, 0x0020}, - {0x00, 0x0080, 0x0021}, - {0x00, 0x0001, 0x0022}, - {0x00, 0x00e0, 0x0023}, - {0x00, 0x0000, 0x0024}, - {0x00, 0x00d5, 0x0025}, - {0x00, 0x0000, 0x0026}, - {0x00, 0x000b, 0x0027}, - {0x00, 0x0000, 0x0046}, - {0x00, 0x0000, 0x0047}, - {0x00, 0x0000, 0x0048}, - {0x00, 0x0000, 0x0049}, - {0x00, 0x0008, 0x004a}, - {0xff, 0x0000, 0x00d0}, - {0xff, 0x00d8, 0x00d1}, - {0xff, 0x0000, 0x00d4}, - {0xff, 0x0000, 0x00d5}, - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0028, 0x0001}, - {0x01, 0x0000, 0x0002}, - {0x01, 0x000a, 0x0003}, - {0x01, 0x0040, 0x0004}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0x00fd, 0x000a}, - {0x01, 0x0038, 0x000b}, - {0x01, 0x00d1, 0x000c}, - {0x01, 0x00f7, 0x000d}, - {0x01, 0x00ed, 0x000e}, - {0x01, 0x00d8, 0x000f}, - {0x01, 0x0038, 0x0010}, - {0x01, 0x00ff, 0x0015}, - {0x01, 0x0001, 0x0016}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x0000, 0x001f}, - {0x01, 0x0000, 0x0020}, - {0x01, 0x00ff, 0x003e}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0000, 0x0040}, - {0x01, 0x0035, 0x0041}, - {0x01, 0x0053, 0x0042}, - {0x01, 0x0069, 0x0043}, - {0x01, 0x007c, 0x0044}, - {0x01, 0x008c, 0x0045}, - {0x01, 0x009a, 0x0046}, - {0x01, 0x00a8, 0x0047}, - {0x01, 0x00b4, 0x0048}, - {0x01, 0x00bf, 0x0049}, - {0x01, 0x00ca, 0x004a}, - {0x01, 0x00d4, 0x004b}, - {0x01, 0x00dd, 0x004c}, - {0x01, 0x00e7, 0x004d}, - {0x01, 0x00ef, 0x004e}, - {0x01, 0x00f8, 0x004f}, - {0x01, 0x00ff, 0x0050}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0060, 0x0057}, - {0x01, 0x0040, 0x0058}, - {0x01, 0x0011, 0x0059}, - {0x01, 0x0001, 0x005a}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x0015, 0x0006}, - {0x02, 0x100a, 0x0007}, - {0x02, 0xa048, 0x0000}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0025, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0001, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0020, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x00, 0x0090, 0x0005}, - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0xfffd, 0x000a}, - {0x01, 0x0023, 0x000b}, - {0x01, 0xffea, 0x000c}, - {0x01, 0xfff4, 0x000d}, - {0x01, 0xfffc, 0x000e}, - {0x01, 0xffe3, 0x000f}, - {0x01, 0x001f, 0x0010}, - {0x01, 0x00a8, 0x0001}, - {0x01, 0x0067, 0x0007}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x00c8, 0x0015}, - {0x01, 0x0032, 0x0016}, - {0x01, 0x0000, 0x0011}, - {0x01, 0x0000, 0x0012}, - {0x01, 0x0000, 0x0013}, - {0x01, 0x000a, 0x0003}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xc000, 0x0001}, - {0x02, 0x0000, 0x0005}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x2000, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0015, 0x0001}, - {0x05, 0x00ea, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0023, 0x0001}, - {0x05, 0x0003, 0x0000}, - {0x05, 0x0030, 0x0001}, - {0x05, 0x002b, 0x0000}, - {0x05, 0x0031, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0032, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0033, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0034, 0x0001}, - {0x05, 0x0002, 0x0000}, - {0x05, 0x0050, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0051, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0052, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0054, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x00, 0x0000, 0x0001}, - {0x00, 0x0000, 0x0002}, - {0x00, 0x000c, 0x0003}, - {0x00, 0x0000, 0x0004}, - {0x00, 0x0090, 0x0005}, - {0x00, 0x0000, 0x0006}, - {0x00, 0x0040, 0x0007}, - {0x00, 0x00c0, 0x0008}, - {0x00, 0x004a, 0x0009}, - {0x00, 0x0000, 0x000a}, - {0x00, 0x0000, 0x000b}, - {0x00, 0x0001, 0x000c}, - {0x00, 0x0001, 0x000d}, - {0x00, 0x0000, 0x000e}, - {0x00, 0x0002, 0x000f}, - {0x00, 0x0001, 0x0010}, - {0x00, 0x0000, 0x0011}, - {0x00, 0x0000, 0x0012}, - {0x00, 0x0002, 0x0020}, - {0x00, 0x0080, 0x0021}, - {0x00, 0x0001, 0x0022}, - {0x00, 0x00e0, 0x0023}, - {0x00, 0x0000, 0x0024}, - {0x00, 0x00d5, 0x0025}, - {0x00, 0x0000, 0x0026}, - {0x00, 0x000b, 0x0027}, - {0x00, 0x0000, 0x0046}, - {0x00, 0x0000, 0x0047}, - {0x00, 0x0000, 0x0048}, - {0x00, 0x0000, 0x0049}, - {0x00, 0x0008, 0x004a}, - {0xff, 0x0000, 0x00d0}, - {0xff, 0x00d8, 0x00d1}, - {0xff, 0x0000, 0x00d4}, - {0xff, 0x0000, 0x00d5}, - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0028, 0x0001}, - {0x01, 0x0000, 0x0002}, - {0x01, 0x000a, 0x0003}, - {0x01, 0x0040, 0x0004}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0x00fd, 0x000a}, - {0x01, 0x0038, 0x000b}, - {0x01, 0x00d1, 0x000c}, - {0x01, 0x00f7, 0x000d}, - {0x01, 0x00ed, 0x000e}, - {0x01, 0x00d8, 0x000f}, - {0x01, 0x0038, 0x0010}, - {0x01, 0x00ff, 0x0015}, - {0x01, 0x0001, 0x0016}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x0000, 0x001f}, - {0x01, 0x0000, 0x0020}, - {0x01, 0x00ff, 0x003e}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0000, 0x0040}, - {0x01, 0x0035, 0x0041}, - {0x01, 0x0053, 0x0042}, - {0x01, 0x0069, 0x0043}, - {0x01, 0x007c, 0x0044}, - {0x01, 0x008c, 0x0045}, - {0x01, 0x009a, 0x0046}, - {0x01, 0x00a8, 0x0047}, - {0x01, 0x00b4, 0x0048}, - {0x01, 0x00bf, 0x0049}, - {0x01, 0x00ca, 0x004a}, - {0x01, 0x00d4, 0x004b}, - {0x01, 0x00dd, 0x004c}, - {0x01, 0x00e7, 0x004d}, - {0x01, 0x00ef, 0x004e}, - {0x01, 0x00f8, 0x004f}, - {0x01, 0x00ff, 0x0050}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0060, 0x0057}, - {0x01, 0x0040, 0x0058}, - {0x01, 0x0011, 0x0059}, - {0x01, 0x0001, 0x005a}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x0015, 0x0006}, - {0x02, 0x100a, 0x0007}, - {0x02, 0xa048, 0x0000}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0025, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0001, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0020, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x00, 0x0090, 0x0005}, - {0x01, 0x00a6, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x2000, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0015, 0x0001}, - {0x05, 0x00ea, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0023, 0x0001}, - {0x05, 0x0003, 0x0000}, - {0x05, 0x0030, 0x0001}, - {0x05, 0x002b, 0x0000}, - {0x05, 0x0031, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0032, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0033, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0034, 0x0001}, - {0x05, 0x0002, 0x0000}, - {0x05, 0x0050, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0051, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0052, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0054, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x00, 0x0000, 0x0001}, - {0x00, 0x0000, 0x0002}, - {0x00, 0x000c, 0x0003}, - {0x00, 0x0000, 0x0004}, - {0x00, 0x0090, 0x0005}, - {0x00, 0x0000, 0x0006}, - {0x00, 0x0040, 0x0007}, - {0x00, 0x00c0, 0x0008}, - {0x00, 0x004a, 0x0009}, - {0x00, 0x0000, 0x000a}, - {0x00, 0x0000, 0x000b}, - {0x00, 0x0001, 0x000c}, - {0x00, 0x0001, 0x000d}, - {0x00, 0x0000, 0x000e}, - {0x00, 0x0002, 0x000f}, - {0x00, 0x0001, 0x0010}, - {0x00, 0x0000, 0x0011}, - {0x00, 0x0000, 0x0012}, - {0x00, 0x0002, 0x0020}, - {0x00, 0x0080, 0x0021}, - {0x00, 0x0001, 0x0022}, - {0x00, 0x00e0, 0x0023}, - {0x00, 0x0000, 0x0024}, - {0x00, 0x00d5, 0x0025}, - {0x00, 0x0000, 0x0026}, - {0x00, 0x000b, 0x0027}, - {0x00, 0x0000, 0x0046}, - {0x00, 0x0000, 0x0047}, - {0x00, 0x0000, 0x0048}, - {0x00, 0x0000, 0x0049}, - {0x00, 0x0008, 0x004a}, - {0xff, 0x0000, 0x00d0}, - {0xff, 0x00d8, 0x00d1}, - {0xff, 0x0000, 0x00d4}, - {0xff, 0x0000, 0x00d5}, - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0028, 0x0001}, - {0x01, 0x0000, 0x0002}, - {0x01, 0x000a, 0x0003}, - {0x01, 0x0040, 0x0004}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0x00fd, 0x000a}, - {0x01, 0x0038, 0x000b}, - {0x01, 0x00d1, 0x000c}, - {0x01, 0x00f7, 0x000d}, - {0x01, 0x00ed, 0x000e}, - {0x01, 0x00d8, 0x000f}, - {0x01, 0x0038, 0x0010}, - {0x01, 0x00ff, 0x0015}, - {0x01, 0x0001, 0x0016}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x0000, 0x001f}, - {0x01, 0x0000, 0x0020}, - {0x01, 0x00ff, 0x003e}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0000, 0x0040}, - {0x01, 0x0035, 0x0041}, - {0x01, 0x0053, 0x0042}, - {0x01, 0x0069, 0x0043}, - {0x01, 0x007c, 0x0044}, - {0x01, 0x008c, 0x0045}, - {0x01, 0x009a, 0x0046}, - {0x01, 0x00a8, 0x0047}, - {0x01, 0x00b4, 0x0048}, - {0x01, 0x00bf, 0x0049}, - {0x01, 0x00ca, 0x004a}, - {0x01, 0x00d4, 0x004b}, - {0x01, 0x00dd, 0x004c}, - {0x01, 0x00e7, 0x004d}, - {0x01, 0x00ef, 0x004e}, - {0x01, 0x00f8, 0x004f}, - {0x01, 0x00ff, 0x0050}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0060, 0x0057}, - {0x01, 0x0040, 0x0058}, - {0x01, 0x0011, 0x0059}, - {0x01, 0x0001, 0x005a}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x0015, 0x0006}, - {0x02, 0x100a, 0x0007}, - {0x02, 0xa048, 0x0000}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0025, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0001, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0020, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x00, 0x0090, 0x0005}, - {0x01, 0x00a6, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x000f, 0x0000}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0xfffd, 0x000a}, - {0x01, 0x0023, 0x000b}, - {0x01, 0xffea, 0x000c}, - {0x01, 0xfff4, 0x000d}, - {0x01, 0xfffc, 0x000e}, - {0x01, 0xffe3, 0x000f}, - {0x01, 0x001f, 0x0010}, - {0x01, 0x00a8, 0x0001}, - {0x01, 0x0067, 0x0007}, - {0x01, 0x0042, 0x0051}, - {0x01, 0x0051, 0x0053}, - {0x01, 0x000a, 0x0003}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xc000, 0x0001}, - {0x02, 0x0000, 0x0005}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x2000, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0015, 0x0001}, - {0x05, 0x00ea, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0023, 0x0001}, - {0x05, 0x0003, 0x0000}, - {0x05, 0x0030, 0x0001}, - {0x05, 0x002b, 0x0000}, - {0x05, 0x0031, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0032, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0033, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0034, 0x0001}, - {0x05, 0x0002, 0x0000}, - {0x05, 0x0050, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0051, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0052, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0054, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x00, 0x0000, 0x0001}, - {0x00, 0x0000, 0x0002}, - {0x00, 0x000c, 0x0003}, - {0x00, 0x0000, 0x0004}, - {0x00, 0x0090, 0x0005}, - {0x00, 0x0000, 0x0006}, - {0x00, 0x0040, 0x0007}, - {0x00, 0x00c0, 0x0008}, - {0x00, 0x004a, 0x0009}, - {0x00, 0x0000, 0x000a}, - {0x00, 0x0000, 0x000b}, - {0x00, 0x0001, 0x000c}, - {0x00, 0x0001, 0x000d}, - {0x00, 0x0000, 0x000e}, - {0x00, 0x0002, 0x000f}, - {0x00, 0x0001, 0x0010}, - {0x00, 0x0000, 0x0011}, - {0x00, 0x0000, 0x0012}, - {0x00, 0x0002, 0x0020}, - {0x00, 0x0080, 0x0021}, - {0x00, 0x0001, 0x0022}, - {0x00, 0x00e0, 0x0023}, - {0x00, 0x0000, 0x0024}, - {0x00, 0x00d5, 0x0025}, - {0x00, 0x0000, 0x0026}, - {0x00, 0x000b, 0x0027}, - {0x00, 0x0000, 0x0046}, - {0x00, 0x0000, 0x0047}, - {0x00, 0x0000, 0x0048}, - {0x00, 0x0000, 0x0049}, - {0x00, 0x0008, 0x004a}, - {0xff, 0x0000, 0x00d0}, - {0xff, 0x00d8, 0x00d1}, - {0xff, 0x0000, 0x00d4}, - {0xff, 0x0000, 0x00d5}, - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0028, 0x0001}, - {0x01, 0x0000, 0x0002}, - {0x01, 0x000a, 0x0003}, - {0x01, 0x0040, 0x0004}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0x00fd, 0x000a}, - {0x01, 0x0038, 0x000b}, - {0x01, 0x00d1, 0x000c}, - {0x01, 0x00f7, 0x000d}, - {0x01, 0x00ed, 0x000e}, - {0x01, 0x00d8, 0x000f}, - {0x01, 0x0038, 0x0010}, - {0x01, 0x00ff, 0x0015}, - {0x01, 0x0001, 0x0016}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x0000, 0x001f}, - {0x01, 0x0000, 0x0020}, - {0x01, 0x00ff, 0x003e}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0000, 0x0040}, - {0x01, 0x0035, 0x0041}, - {0x01, 0x0053, 0x0042}, - {0x01, 0x0069, 0x0043}, - {0x01, 0x007c, 0x0044}, - {0x01, 0x008c, 0x0045}, - {0x01, 0x009a, 0x0046}, - {0x01, 0x00a8, 0x0047}, - {0x01, 0x00b4, 0x0048}, - {0x01, 0x00bf, 0x0049}, - {0x01, 0x00ca, 0x004a}, - {0x01, 0x00d4, 0x004b}, - {0x01, 0x00dd, 0x004c}, - {0x01, 0x00e7, 0x004d}, - {0x01, 0x00ef, 0x004e}, - {0x01, 0x00f8, 0x004f}, - {0x01, 0x00ff, 0x0050}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0060, 0x0057}, - {0x01, 0x0040, 0x0058}, - {0x01, 0x0011, 0x0059}, - {0x01, 0x0001, 0x005a}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x0015, 0x0006}, - {0x02, 0x100a, 0x0007}, - {0x02, 0xa048, 0x0000}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0025, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0001, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0020, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x00, 0x0090, 0x0005}, - {0x01, 0x00a6, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x2000, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0015, 0x0001}, - {0x05, 0x00ea, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0023, 0x0001}, - {0x05, 0x0003, 0x0000}, - {0x05, 0x0030, 0x0001}, - {0x05, 0x002b, 0x0000}, - {0x05, 0x0031, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0032, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0033, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0034, 0x0001}, - {0x05, 0x0002, 0x0000}, - {0x05, 0x0050, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0051, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0052, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0054, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x00, 0x0000, 0x0001}, - {0x00, 0x0000, 0x0002}, - {0x00, 0x000c, 0x0003}, - {0x00, 0x0000, 0x0004}, - {0x00, 0x0090, 0x0005}, - {0x00, 0x0000, 0x0006}, - {0x00, 0x0040, 0x0007}, - {0x00, 0x00c0, 0x0008}, - {0x00, 0x004a, 0x0009}, - {0x00, 0x0000, 0x000a}, - {0x00, 0x0000, 0x000b}, - {0x00, 0x0001, 0x000c}, - {0x00, 0x0001, 0x000d}, - {0x00, 0x0000, 0x000e}, - {0x00, 0x0002, 0x000f}, - {0x00, 0x0001, 0x0010}, - {0x00, 0x0000, 0x0011}, - {0x00, 0x0000, 0x0012}, - {0x00, 0x0002, 0x0020}, - {0x00, 0x0080, 0x0021}, - {0x00, 0x0001, 0x0022}, - {0x00, 0x00e0, 0x0023}, - {0x00, 0x0000, 0x0024}, - {0x00, 0x00d5, 0x0025}, - {0x00, 0x0000, 0x0026}, - {0x00, 0x000b, 0x0027}, - {0x00, 0x0000, 0x0046}, - {0x00, 0x0000, 0x0047}, - {0x00, 0x0000, 0x0048}, - {0x00, 0x0000, 0x0049}, - {0x00, 0x0008, 0x004a}, - {0xff, 0x0000, 0x00d0}, - {0xff, 0x00d8, 0x00d1}, - {0xff, 0x0000, 0x00d4}, - {0xff, 0x0000, 0x00d5}, - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0028, 0x0001}, - {0x01, 0x0000, 0x0002}, - {0x01, 0x000a, 0x0003}, - {0x01, 0x0040, 0x0004}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0x00fd, 0x000a}, - {0x01, 0x0038, 0x000b}, - {0x01, 0x00d1, 0x000c}, - {0x01, 0x00f7, 0x000d}, - {0x01, 0x00ed, 0x000e}, - {0x01, 0x00d8, 0x000f}, - {0x01, 0x0038, 0x0010}, - {0x01, 0x00ff, 0x0015}, - {0x01, 0x0001, 0x0016}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x0000, 0x001f}, - {0x01, 0x0000, 0x0020}, - {0x01, 0x00ff, 0x003e}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0000, 0x0040}, - {0x01, 0x0035, 0x0041}, - {0x01, 0x0053, 0x0042}, - {0x01, 0x0069, 0x0043}, - {0x01, 0x007c, 0x0044}, - {0x01, 0x008c, 0x0045}, - {0x01, 0x009a, 0x0046}, - {0x01, 0x00a8, 0x0047}, - {0x01, 0x00b4, 0x0048}, - {0x01, 0x00bf, 0x0049}, - {0x01, 0x00ca, 0x004a}, - {0x01, 0x00d4, 0x004b}, - {0x01, 0x00dd, 0x004c}, - {0x01, 0x00e7, 0x004d}, - {0x01, 0x00ef, 0x004e}, - {0x01, 0x00f8, 0x004f}, - {0x01, 0x00ff, 0x0050}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0060, 0x0057}, - {0x01, 0x0040, 0x0058}, - {0x01, 0x0011, 0x0059}, - {0x01, 0x0001, 0x005a}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x0015, 0x0006}, - {0x02, 0x100a, 0x0007}, - {0x02, 0xa048, 0x0000}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0025, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0001, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0020, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x00, 0x0090, 0x0005}, - {0x01, 0x00a6, 0x0000}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x001e, 0x0000}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x0011, 0x0008}, - {0x01, 0x0032, 0x0009}, - {0x01, 0xfffd, 0x000a}, - {0x01, 0x0023, 0x000b}, - {0x01, 0xffea, 0x000c}, - {0x01, 0xfff4, 0x000d}, - {0x01, 0xfffc, 0x000e}, - {0x01, 0xffe3, 0x000f}, - {0x01, 0x001f, 0x0010}, - {0x01, 0x00a8, 0x0001}, - {0x01, 0x0067, 0x0007}, - {0x01, 0x0042, 0x0051}, - {0x01, 0x0051, 0x0053}, - {0x01, 0x000a, 0x0003}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x0007, 0x0005}, - {0x01, 0x0042, 0x0051}, - {0x01, 0x0051, 0x0053}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x002d, 0x0000}, - {0x01, 0x0003, 0x003f}, - {0x01, 0x0001, 0x0056}, - {0x02, 0xc000, 0x0001}, - {0x02, 0x0000, 0x0005}, - {} -}; - -/* Unknown camera from Ori Usbid 0x0000:0x0000 */ -/* Based on snoops from Ori Cohen */ -static const __u16 spca501c_mysterious_open_data[][3] = { - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, -/* DSP Registers */ - {0x01, 0x0016, 0x0011}, /* RGB offset */ - {0x01, 0x0000, 0x0012}, - {0x01, 0x0006, 0x0013}, - {0x01, 0x0078, 0x0051}, - {0x01, 0x0040, 0x0052}, - {0x01, 0x0046, 0x0053}, - {0x01, 0x0040, 0x0054}, - {0x00, 0x0025, 0x0000}, -/* {0x00, 0x0000, 0x0000 }, */ -/* Part 2 */ -/* TG Registers */ - {0x00, 0x0026, 0x0000}, - {0x00, 0x0001, 0x0000}, - {0x00, 0x0027, 0x0000}, - {0x00, 0x008a, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x2000, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0015, 0x0001}, - {0x05, 0x00ea, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0023, 0x0001}, - {0x05, 0x0003, 0x0000}, - {0x05, 0x0030, 0x0001}, - {0x05, 0x002b, 0x0000}, - {0x05, 0x0031, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0032, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0033, 0x0001}, - {0x05, 0x0023, 0x0000}, - {0x05, 0x0034, 0x0001}, - {0x05, 0x0002, 0x0000}, - {0x05, 0x0050, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0051, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0052, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0054, 0x0001}, - {0x05, 0x0001, 0x0000}, - {} -}; - -/* Based on snoops from Ori Cohen */ -static const __u16 spca501c_mysterious_init_data[][3] = { -/* Part 3 */ -/* TG registers */ -/* {0x00, 0x0000, 0x0000}, */ - {0x00, 0x0000, 0x0001}, - {0x00, 0x0000, 0x0002}, - {0x00, 0x0006, 0x0003}, - {0x00, 0x0000, 0x0004}, - {0x00, 0x0090, 0x0005}, - {0x00, 0x0000, 0x0006}, - {0x00, 0x0040, 0x0007}, - {0x00, 0x00c0, 0x0008}, - {0x00, 0x004a, 0x0009}, - {0x00, 0x0000, 0x000a}, - {0x00, 0x0000, 0x000b}, - {0x00, 0x0001, 0x000c}, - {0x00, 0x0001, 0x000d}, - {0x00, 0x0000, 0x000e}, - {0x00, 0x0002, 0x000f}, - {0x00, 0x0001, 0x0010}, - {0x00, 0x0000, 0x0011}, - {0x00, 0x0001, 0x0012}, - {0x00, 0x0002, 0x0020}, - {0x00, 0x0080, 0x0021}, /* 640 */ - {0x00, 0x0001, 0x0022}, - {0x00, 0x00e0, 0x0023}, /* 480 */ - {0x00, 0x0000, 0x0024}, /* Offset H hight */ - {0x00, 0x00d3, 0x0025}, /* low */ - {0x00, 0x0000, 0x0026}, /* Offset V */ - {0x00, 0x000d, 0x0027}, /* low */ - {0x00, 0x0000, 0x0046}, - {0x00, 0x0000, 0x0047}, - {0x00, 0x0000, 0x0048}, - {0x00, 0x0000, 0x0049}, - {0x00, 0x0008, 0x004a}, -/* DSP Registers */ - {0x01, 0x00a6, 0x0000}, - {0x01, 0x0028, 0x0001}, - {0x01, 0x0000, 0x0002}, - {0x01, 0x000a, 0x0003}, /* Level Calc bit7 ->1 Auto */ - {0x01, 0x0040, 0x0004}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x000f, 0x0008}, /* A11 Color correction coeff */ - {0x01, 0x002d, 0x0009}, /* A12 */ - {0x01, 0x0005, 0x000a}, /* A13 */ - {0x01, 0x0023, 0x000b}, /* A21 */ - {0x01, 0x00e0, 0x000c}, /* A22 */ - {0x01, 0x00fd, 0x000d}, /* A23 */ - {0x01, 0x00f4, 0x000e}, /* A31 */ - {0x01, 0x00e4, 0x000f}, /* A32 */ - {0x01, 0x0028, 0x0010}, /* A33 */ - {0x01, 0x00ff, 0x0015}, /* Reserved */ - {0x01, 0x0001, 0x0016}, /* Reserved */ - {0x01, 0x0032, 0x0017}, /* Win1 Start begin */ - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x0000, 0x001f}, - {0x01, 0x0000, 0x0020}, /* Win1 Start end */ - {0x01, 0x00ff, 0x003e}, /* Reserved begin */ - {0x01, 0x0002, 0x003f}, - {0x01, 0x0000, 0x0040}, - {0x01, 0x0035, 0x0041}, - {0x01, 0x0053, 0x0042}, - {0x01, 0x0069, 0x0043}, - {0x01, 0x007c, 0x0044}, - {0x01, 0x008c, 0x0045}, - {0x01, 0x009a, 0x0046}, - {0x01, 0x00a8, 0x0047}, - {0x01, 0x00b4, 0x0048}, - {0x01, 0x00bf, 0x0049}, - {0x01, 0x00ca, 0x004a}, - {0x01, 0x00d4, 0x004b}, - {0x01, 0x00dd, 0x004c}, - {0x01, 0x00e7, 0x004d}, - {0x01, 0x00ef, 0x004e}, - {0x01, 0x00f8, 0x004f}, - {0x01, 0x00ff, 0x0050}, - {0x01, 0x0003, 0x0056}, /* Reserved end */ - {0x01, 0x0060, 0x0057}, /* Edge Gain */ - {0x01, 0x0040, 0x0058}, - {0x01, 0x0011, 0x0059}, /* Edge Bandwidth */ - {0x01, 0x0001, 0x005a}, - {0x02, 0x0007, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x02, 0x0007, 0x0005}, - {0x02, 0x0015, 0x0006}, - {0x02, 0x200a, 0x0007}, - {0x02, 0xa048, 0x0000}, - {0x02, 0xc000, 0x0001}, - {0x02, 0x000f, 0x0005}, - {0x02, 0xa048, 0x0000}, - {0x05, 0x0022, 0x0004}, - {0x05, 0x0025, 0x0001}, - {0x05, 0x0000, 0x0000}, -/* Part 4 */ - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0001, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x05, 0x0021, 0x0001}, - {0x05, 0x00d2, 0x0000}, - {0x05, 0x0020, 0x0001}, - {0x05, 0x0000, 0x0000}, - {0x00, 0x0090, 0x0005}, - {0x01, 0x00a6, 0x0000}, - {0x02, 0x0000, 0x0005}, - {0x05, 0x0026, 0x0001}, - {0x05, 0x0001, 0x0000}, - {0x05, 0x0027, 0x0001}, - {0x05, 0x004e, 0x0000}, -/* Part 5 */ - {0x01, 0x0003, 0x003f}, - {0x01, 0x0001, 0x0056}, - {0x01, 0x000f, 0x0008}, - {0x01, 0x002d, 0x0009}, - {0x01, 0x0005, 0x000a}, - {0x01, 0x0023, 0x000b}, - {0x01, 0xffe0, 0x000c}, - {0x01, 0xfffd, 0x000d}, - {0x01, 0xfff4, 0x000e}, - {0x01, 0xffe4, 0x000f}, - {0x01, 0x0028, 0x0010}, - {0x01, 0x00a8, 0x0001}, - {0x01, 0x0066, 0x0007}, - {0x01, 0x0032, 0x0017}, - {0x01, 0x0023, 0x0018}, - {0x01, 0x00ce, 0x0019}, - {0x01, 0x0023, 0x001a}, - {0x01, 0x0032, 0x001b}, - {0x01, 0x008d, 0x001c}, - {0x01, 0x00ce, 0x001d}, - {0x01, 0x008d, 0x001e}, - {0x01, 0x00c8, 0x0015}, /* c8 Poids fort Luma */ - {0x01, 0x0032, 0x0016}, /* 32 */ - {0x01, 0x0016, 0x0011}, /* R 00 */ - {0x01, 0x0016, 0x0012}, /* G 00 */ - {0x01, 0x0016, 0x0013}, /* B 00 */ - {0x01, 0x000a, 0x0003}, - {0x02, 0xc002, 0x0001}, - {0x02, 0x0007, 0x0005}, - {} -}; - -static int reg_write(struct usb_device *dev, - __u16 req, __u16 index, __u16 value) -{ - int ret; - - ret = usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - req, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x", - req, index, value); - if (ret < 0) - pr_err("reg write: error %d\n", ret); - return ret; -} - - -static int write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][3]) -{ - struct usb_device *dev = gspca_dev->dev; - int ret, i = 0; - - while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { - ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); - if (ret < 0) { - PDEBUG(D_ERR, - "Reg write failed for 0x%02x,0x%02x,0x%02x", - data[i][0], data[i][1], data[i][2]); - return ret; - } - i++; - } - return 0; -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, val); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - reg_write(gspca_dev->dev, 0x00, 0x00, - (val >> 8) & 0xff); - reg_write(gspca_dev->dev, 0x00, 0x01, - val & 0xff); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, val); -} - -static void setblue_balance(struct gspca_dev *gspca_dev, s32 val) -{ - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, val); -} - -static void setred_balance(struct gspca_dev *gspca_dev, s32 val) -{ - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, val); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - sd->subtype = id->driver_info; - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->subtype) { - case Arowana300KCMOSCamera: - case SmileIntlCamera: - /* Arowana 300k CMOS Camera data */ - if (write_vector(gspca_dev, spca501c_arowana_init_data)) - goto error; - break; - case MystFromOriUnknownCamera: - /* Unknown Ori CMOS Camera data */ - if (write_vector(gspca_dev, spca501c_mysterious_open_data)) - goto error; - break; - default: - /* generic spca501 init data */ - if (write_vector(gspca_dev, spca501_init_data)) - goto error; - break; - } - PDEBUG(D_STREAM, "Initializing SPCA501 finished"); - return 0; -error: - return -EINVAL; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - int mode; - - switch (sd->subtype) { - case ThreeComHomeConnectLite: - /* Special handling for 3com data */ - write_vector(gspca_dev, spca501_3com_open_data); - break; - case Arowana300KCMOSCamera: - case SmileIntlCamera: - /* Arowana 300k CMOS Camera data */ - write_vector(gspca_dev, spca501c_arowana_open_data); - break; - case MystFromOriUnknownCamera: - /* Unknown CMOS Camera data */ - write_vector(gspca_dev, spca501c_mysterious_init_data); - break; - default: - /* Generic 501 open data */ - write_vector(gspca_dev, spca501_open_data); - } - - /* memorize the wanted pixel format */ - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - - /* Enable ISO packet machine CTRL reg=2, - * index=1 bitmask=0x2 (bit ordinal 1) */ - reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94); - switch (mode) { - case 0: /* 640x480 */ - reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a); - break; - case 1: /* 320x240 */ - reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a); - break; - default: -/* case 2: * 160x120 */ - reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a); - break; - } - reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02); - - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - /* Disable ISO packet - * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */ - reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00); -} - -/* called on streamoff with alt 0 and on disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - if (!gspca_dev->present) - return; - reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - switch (data[0]) { - case 0: /* start of frame */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - data += SPCA501_OFFSET_DATA; - len -= SPCA501_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - return; - case 0xff: /* drop */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - } - data++; - len--; - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_BLUE_BALANCE: - setblue_balance(gspca_dev, ctrl->val); - break; - case V4L2_CID_RED_BALANCE: - setred_balance(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 64725, 1, 64725); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 63, 1, 20); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 127, 1, 0); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x040a, 0x0002), .driver_info = KodakDVC325}, - {USB_DEVICE(0x0497, 0xc001), .driver_info = SmileIntlCamera}, - {USB_DEVICE(0x0506, 0x00df), .driver_info = ThreeComHomeConnectLite}, - {USB_DEVICE(0x0733, 0x0401), .driver_info = IntelCreateAndShare}, - {USB_DEVICE(0x0733, 0x0402), .driver_info = ViewQuestM318B}, - {USB_DEVICE(0x1776, 0x501c), .driver_info = Arowana300KCMOSCamera}, - {USB_DEVICE(0x0000, 0x0000), .driver_info = MystFromOriUnknownCamera}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c deleted file mode 100644 index bc7d67c3cb04..000000000000 --- a/drivers/media/video/gspca/spca505.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - * SPCA505 chip based cameras initialization data - * - * V4L2 by Jean-Francis Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "spca505" - -#include "gspca.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - u8 subtype; -#define IntelPCCameraPro 0 -#define Nxultra 1 -}; - -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 4}, - {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -#define SPCA50X_OFFSET_DATA 10 - -#define SPCA50X_REG_USB 0x02 /* spca505 501 */ - -#define SPCA50X_USB_CTRL 0x00 /* spca505 */ -#define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */ - -#define SPCA50X_REG_GLOBAL 0x03 /* spca505 */ -#define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */ -#define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */ - -#define SPCA50X_GLOBAL_MISC1 0x01 /* 505 */ -#define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */ -#define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */ - -/* Image format and compression control */ -#define SPCA50X_REG_COMPRESS 0x04 - -/* - * Data to initialize a SPCA505. Common to the CCD and external modes - */ -static const u8 spca505_init_data[][3] = { - /* bmRequest,value,index */ - {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3}, - /* Sensor reset */ - {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3}, - {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1}, - /* Block USB reset */ - {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, SPCA50X_GLOBAL_MISC0}, - - {0x05, 0x01, 0x10}, - /* Maybe power down some stuff */ - {0x05, 0x0f, 0x11}, - - /* Setup internal CCD ? */ - {0x06, 0x10, 0x08}, - {0x06, 0x00, 0x09}, - {0x06, 0x00, 0x0a}, - {0x06, 0x00, 0x0b}, - {0x06, 0x10, 0x0c}, - {0x06, 0x00, 0x0d}, - {0x06, 0x00, 0x0e}, - {0x06, 0x00, 0x0f}, - {0x06, 0x10, 0x10}, - {0x06, 0x02, 0x11}, - {0x06, 0x00, 0x12}, - {0x06, 0x04, 0x13}, - {0x06, 0x02, 0x14}, - {0x06, 0x8a, 0x51}, - {0x06, 0x40, 0x52}, - {0x06, 0xb6, 0x53}, - {0x06, 0x3d, 0x54}, - {} -}; - -/* - * Data to initialize the camera using the internal CCD - */ -static const u8 spca505_open_data_ccd[][3] = { - /* bmRequest,value,index */ - /* Internal CCD data set */ - {0x03, 0x04, 0x01}, - /* This could be a reset */ - {0x03, 0x00, 0x01}, - - /* Setup compression and image registers. 0x6 and 0x7 seem to be - related to H&V hold, and are resolution mode specific */ - {0x04, 0x10, 0x01}, - /* DIFF(0x50), was (0x10) */ - {0x04, 0x00, 0x04}, - {0x04, 0x00, 0x05}, - {0x04, 0x20, 0x06}, - {0x04, 0x20, 0x07}, - - {0x08, 0x0a, 0x00}, - /* DIFF (0x4a), was (0xa) */ - - {0x05, 0x00, 0x10}, - {0x05, 0x00, 0x11}, - {0x05, 0x00, 0x00}, - /* DIFF not written */ - {0x05, 0x00, 0x01}, - /* DIFF not written */ - {0x05, 0x00, 0x02}, - /* DIFF not written */ - {0x05, 0x00, 0x03}, - /* DIFF not written */ - {0x05, 0x00, 0x04}, - /* DIFF not written */ - {0x05, 0x80, 0x05}, - /* DIFF not written */ - {0x05, 0xe0, 0x06}, - /* DIFF not written */ - {0x05, 0x20, 0x07}, - /* DIFF not written */ - {0x05, 0xa0, 0x08}, - /* DIFF not written */ - {0x05, 0x0, 0x12}, - /* DIFF not written */ - {0x05, 0x02, 0x0f}, - /* DIFF not written */ - {0x05, 0x10, 0x46}, - /* DIFF not written */ - {0x05, 0x8, 0x4a}, - /* DIFF not written */ - - {0x03, 0x08, 0x03}, - /* DIFF (0x3,0x28,0x3) */ - {0x03, 0x08, 0x01}, - {0x03, 0x0c, 0x03}, - /* DIFF not written */ - {0x03, 0x21, 0x00}, - /* DIFF (0x39) */ - -/* Extra block copied from init to hopefully ensure CCD is in a sane state */ - {0x06, 0x10, 0x08}, - {0x06, 0x00, 0x09}, - {0x06, 0x00, 0x0a}, - {0x06, 0x00, 0x0b}, - {0x06, 0x10, 0x0c}, - {0x06, 0x00, 0x0d}, - {0x06, 0x00, 0x0e}, - {0x06, 0x00, 0x0f}, - {0x06, 0x10, 0x10}, - {0x06, 0x02, 0x11}, - {0x06, 0x00, 0x12}, - {0x06, 0x04, 0x13}, - {0x06, 0x02, 0x14}, - {0x06, 0x8a, 0x51}, - {0x06, 0x40, 0x52}, - {0x06, 0xb6, 0x53}, - {0x06, 0x3d, 0x54}, - /* End of extra block */ - - {0x06, 0x3f, 0x1}, - /* Block skipped */ - {0x06, 0x10, 0x02}, - {0x06, 0x64, 0x07}, - {0x06, 0x10, 0x08}, - {0x06, 0x00, 0x09}, - {0x06, 0x00, 0x0a}, - {0x06, 0x00, 0x0b}, - {0x06, 0x10, 0x0c}, - {0x06, 0x00, 0x0d}, - {0x06, 0x00, 0x0e}, - {0x06, 0x00, 0x0f}, - {0x06, 0x10, 0x10}, - {0x06, 0x02, 0x11}, - {0x06, 0x00, 0x12}, - {0x06, 0x04, 0x13}, - {0x06, 0x02, 0x14}, - {0x06, 0x8a, 0x51}, - {0x06, 0x40, 0x52}, - {0x06, 0xb6, 0x53}, - {0x06, 0x3d, 0x54}, - {0x06, 0x60, 0x57}, - {0x06, 0x20, 0x58}, - {0x06, 0x15, 0x59}, - {0x06, 0x05, 0x5a}, - - {0x05, 0x01, 0xc0}, - {0x05, 0x10, 0xcb}, - {0x05, 0x80, 0xc1}, - /* */ - {0x05, 0x0, 0xc2}, - /* 4 was 0 */ - {0x05, 0x00, 0xca}, - {0x05, 0x80, 0xc1}, - /* */ - {0x05, 0x04, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0x0, 0xc1}, - /* */ - {0x05, 0x00, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0x40, 0xc1}, - /* */ - {0x05, 0x17, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0x80, 0xc1}, - /* */ - {0x05, 0x06, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0x80, 0xc1}, - /* */ - {0x05, 0x04, 0xc2}, - {0x05, 0x00, 0xca}, - - {0x03, 0x4c, 0x3}, - {0x03, 0x18, 0x1}, - - {0x06, 0x70, 0x51}, - {0x06, 0xbe, 0x53}, - {0x06, 0x71, 0x57}, - {0x06, 0x20, 0x58}, - {0x06, 0x05, 0x59}, - {0x06, 0x15, 0x5a}, - - {0x04, 0x00, 0x08}, - /* Compress = OFF (0x1 to turn on) */ - {0x04, 0x12, 0x09}, - {0x04, 0x21, 0x0a}, - {0x04, 0x10, 0x0b}, - {0x04, 0x21, 0x0c}, - {0x04, 0x05, 0x00}, - /* was 5 (Image Type ? ) */ - {0x04, 0x00, 0x01}, - - {0x06, 0x3f, 0x01}, - - {0x04, 0x00, 0x04}, - {0x04, 0x00, 0x05}, - {0x04, 0x40, 0x06}, - {0x04, 0x40, 0x07}, - - {0x06, 0x1c, 0x17}, - {0x06, 0xe2, 0x19}, - {0x06, 0x1c, 0x1b}, - {0x06, 0xe2, 0x1d}, - {0x06, 0xaa, 0x1f}, - {0x06, 0x70, 0x20}, - - {0x05, 0x01, 0x10}, - {0x05, 0x00, 0x11}, - {0x05, 0x01, 0x00}, - {0x05, 0x05, 0x01}, - {0x05, 0x00, 0xc1}, - /* */ - {0x05, 0x00, 0xc2}, - {0x05, 0x00, 0xca}, - - {0x06, 0x70, 0x51}, - {0x06, 0xbe, 0x53}, - {} -}; - -/* - * Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) - * SPCA505b chip based cameras initialization data - */ -/* jfm */ -#define initial_brightness 0x7f /* 0x0(white)-0xff(black) */ -/* #define initial_brightness 0x0 //0x0(white)-0xff(black) */ -/* - * Data to initialize a SPCA505. Common to the CCD and external modes - */ -static const u8 spca505b_init_data[][3] = { -/* start */ - {0x02, 0x00, 0x00}, /* init */ - {0x02, 0x00, 0x01}, - {0x02, 0x00, 0x02}, - {0x02, 0x00, 0x03}, - {0x02, 0x00, 0x04}, - {0x02, 0x00, 0x05}, - {0x02, 0x00, 0x06}, - {0x02, 0x00, 0x07}, - {0x02, 0x00, 0x08}, - {0x02, 0x00, 0x09}, - {0x03, 0x00, 0x00}, - {0x03, 0x00, 0x01}, - {0x03, 0x00, 0x02}, - {0x03, 0x00, 0x03}, - {0x03, 0x00, 0x04}, - {0x03, 0x00, 0x05}, - {0x03, 0x00, 0x06}, - {0x04, 0x00, 0x00}, - {0x04, 0x00, 0x02}, - {0x04, 0x00, 0x04}, - {0x04, 0x00, 0x05}, - {0x04, 0x00, 0x06}, - {0x04, 0x00, 0x07}, - {0x04, 0x00, 0x08}, - {0x04, 0x00, 0x09}, - {0x04, 0x00, 0x0a}, - {0x04, 0x00, 0x0b}, - {0x04, 0x00, 0x0c}, - {0x07, 0x00, 0x00}, - {0x07, 0x00, 0x03}, - {0x08, 0x00, 0x00}, - {0x08, 0x00, 0x01}, - {0x08, 0x00, 0x02}, - {0x06, 0x18, 0x08}, - {0x06, 0xfc, 0x09}, - {0x06, 0xfc, 0x0a}, - {0x06, 0xfc, 0x0b}, - {0x06, 0x18, 0x0c}, - {0x06, 0xfc, 0x0d}, - {0x06, 0xfc, 0x0e}, - {0x06, 0xfc, 0x0f}, - {0x06, 0x18, 0x10}, - {0x06, 0xfe, 0x12}, - {0x06, 0x00, 0x11}, - {0x06, 0x00, 0x14}, - {0x06, 0x00, 0x13}, - {0x06, 0x28, 0x51}, - {0x06, 0xff, 0x53}, - {0x02, 0x00, 0x08}, - - {0x03, 0x00, 0x03}, - {0x03, 0x10, 0x03}, - {} -}; - -/* - * Data to initialize the camera using the internal CCD - */ -static const u8 spca505b_open_data_ccd[][3] = { - -/* {0x02,0x00,0x00}, */ - {0x03, 0x04, 0x01}, /* rst */ - {0x03, 0x00, 0x01}, - {0x03, 0x00, 0x00}, - {0x03, 0x21, 0x00}, - {0x03, 0x00, 0x04}, - {0x03, 0x00, 0x03}, - {0x03, 0x18, 0x03}, - {0x03, 0x08, 0x01}, - {0x03, 0x1c, 0x03}, - {0x03, 0x5c, 0x03}, - {0x03, 0x5c, 0x03}, - {0x03, 0x18, 0x01}, - -/* same as 505 */ - {0x04, 0x10, 0x01}, - {0x04, 0x00, 0x04}, - {0x04, 0x00, 0x05}, - {0x04, 0x20, 0x06}, - {0x04, 0x20, 0x07}, - - {0x08, 0x0a, 0x00}, - - {0x05, 0x00, 0x10}, - {0x05, 0x00, 0x11}, - {0x05, 0x00, 0x12}, - {0x05, 0x6f, 0x00}, - {0x05, initial_brightness >> 6, 0x00}, - {0x05, (initial_brightness << 2) & 0xff, 0x01}, - {0x05, 0x00, 0x02}, - {0x05, 0x01, 0x03}, - {0x05, 0x00, 0x04}, - {0x05, 0x03, 0x05}, - {0x05, 0xe0, 0x06}, - {0x05, 0x20, 0x07}, - {0x05, 0xa0, 0x08}, - {0x05, 0x00, 0x12}, - {0x05, 0x02, 0x0f}, - {0x05, 0x80, 0x14}, /* max exposure off (0=on) */ - {0x05, 0x01, 0xb0}, - {0x05, 0x01, 0xbf}, - {0x03, 0x02, 0x06}, - {0x05, 0x10, 0x46}, - {0x05, 0x08, 0x4a}, - - {0x06, 0x00, 0x01}, - {0x06, 0x10, 0x02}, - {0x06, 0x64, 0x07}, - {0x06, 0x18, 0x08}, - {0x06, 0xfc, 0x09}, - {0x06, 0xfc, 0x0a}, - {0x06, 0xfc, 0x0b}, - {0x04, 0x00, 0x01}, - {0x06, 0x18, 0x0c}, - {0x06, 0xfc, 0x0d}, - {0x06, 0xfc, 0x0e}, - {0x06, 0xfc, 0x0f}, - {0x06, 0x11, 0x10}, /* contrast */ - {0x06, 0x00, 0x11}, - {0x06, 0xfe, 0x12}, - {0x06, 0x00, 0x13}, - {0x06, 0x00, 0x14}, - {0x06, 0x9d, 0x51}, - {0x06, 0x40, 0x52}, - {0x06, 0x7c, 0x53}, - {0x06, 0x40, 0x54}, - {0x06, 0x02, 0x57}, - {0x06, 0x03, 0x58}, - {0x06, 0x15, 0x59}, - {0x06, 0x05, 0x5a}, - {0x06, 0x03, 0x56}, - {0x06, 0x02, 0x3f}, - {0x06, 0x00, 0x40}, - {0x06, 0x39, 0x41}, - {0x06, 0x69, 0x42}, - {0x06, 0x87, 0x43}, - {0x06, 0x9e, 0x44}, - {0x06, 0xb1, 0x45}, - {0x06, 0xbf, 0x46}, - {0x06, 0xcc, 0x47}, - {0x06, 0xd5, 0x48}, - {0x06, 0xdd, 0x49}, - {0x06, 0xe3, 0x4a}, - {0x06, 0xe8, 0x4b}, - {0x06, 0xed, 0x4c}, - {0x06, 0xf2, 0x4d}, - {0x06, 0xf7, 0x4e}, - {0x06, 0xfc, 0x4f}, - {0x06, 0xff, 0x50}, - - {0x05, 0x01, 0xc0}, - {0x05, 0x10, 0xcb}, - {0x05, 0x40, 0xc1}, - {0x05, 0x04, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0x40, 0xc1}, - {0x05, 0x09, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0xc0, 0xc1}, - {0x05, 0x09, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0x40, 0xc1}, - {0x05, 0x59, 0xc2}, - {0x05, 0x00, 0xca}, - {0x04, 0x00, 0x01}, - {0x05, 0x80, 0xc1}, - {0x05, 0xec, 0xc2}, - {0x05, 0x0, 0xca}, - - {0x06, 0x02, 0x57}, - {0x06, 0x01, 0x58}, - {0x06, 0x15, 0x59}, - {0x06, 0x0a, 0x5a}, - {0x06, 0x01, 0x57}, - {0x06, 0x8a, 0x03}, - {0x06, 0x0a, 0x6c}, - {0x06, 0x30, 0x01}, - {0x06, 0x20, 0x02}, - {0x06, 0x00, 0x03}, - - {0x05, 0x8c, 0x25}, - - {0x06, 0x4d, 0x51}, /* maybe saturation (4d) */ - {0x06, 0x84, 0x53}, /* making green (84) */ - {0x06, 0x00, 0x57}, /* sharpness (1) */ - {0x06, 0x18, 0x08}, - {0x06, 0xfc, 0x09}, - {0x06, 0xfc, 0x0a}, - {0x06, 0xfc, 0x0b}, - {0x06, 0x18, 0x0c}, /* maybe hue (18) */ - {0x06, 0xfc, 0x0d}, - {0x06, 0xfc, 0x0e}, - {0x06, 0xfc, 0x0f}, - {0x06, 0x18, 0x10}, /* maybe contrast (18) */ - - {0x05, 0x01, 0x02}, - - {0x04, 0x00, 0x08}, /* compression */ - {0x04, 0x12, 0x09}, - {0x04, 0x21, 0x0a}, - {0x04, 0x10, 0x0b}, - {0x04, 0x21, 0x0c}, - {0x04, 0x1d, 0x00}, /* imagetype (1d) */ - {0x04, 0x41, 0x01}, /* hardware snapcontrol */ - - {0x04, 0x00, 0x04}, - {0x04, 0x00, 0x05}, - {0x04, 0x10, 0x06}, - {0x04, 0x10, 0x07}, - {0x04, 0x40, 0x06}, - {0x04, 0x40, 0x07}, - {0x04, 0x00, 0x04}, - {0x04, 0x00, 0x05}, - - {0x06, 0x1c, 0x17}, - {0x06, 0xe2, 0x19}, - {0x06, 0x1c, 0x1b}, - {0x06, 0xe2, 0x1d}, - {0x06, 0x5f, 0x1f}, - {0x06, 0x32, 0x20}, - - {0x05, initial_brightness >> 6, 0x00}, - {0x05, (initial_brightness << 2) & 0xff, 0x01}, - {0x05, 0x06, 0xc1}, - {0x05, 0x58, 0xc2}, - {0x05, 0x00, 0xca}, - {0x05, 0x00, 0x11}, - {} -}; - -static int reg_write(struct usb_device *dev, - u16 req, u16 index, u16 value) -{ - int ret; - - ret = usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - req, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d", - req, index, value, ret); - if (ret < 0) - pr_err("reg write: error %d\n", ret); - return ret; -} - -/* returns: negative is error, pos or zero is data */ -static int reg_read(struct gspca_dev *gspca_dev, - u16 req, /* bRequest */ - u16 index) /* wIndex */ -{ - int ret; - - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, - gspca_dev->usb_buf, 2, - 500); /* timeout */ - if (ret < 0) - return ret; - return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; -} - -static int write_vector(struct gspca_dev *gspca_dev, - const u8 data[][3]) -{ - struct usb_device *dev = gspca_dev->dev; - int ret, i = 0; - - while (data[i][0] != 0) { - ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); - if (ret < 0) - return ret; - i++; - } - return 0; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - sd->subtype = id->driver_info; - if (sd->subtype != IntelPCCameraPro) - cam->nmodes = ARRAY_SIZE(vga_mode); - else /* no 640x480 for IntelPCCameraPro */ - cam->nmodes = ARRAY_SIZE(vga_mode) - 1; - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (write_vector(gspca_dev, - sd->subtype == Nxultra - ? spca505b_init_data - : spca505_init_data)) - return -EIO; - return 0; -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) -{ - reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6); - reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2); -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - int ret, mode; - static u8 mode_tb[][3] = { - /* r00 r06 r07 */ - {0x00, 0x10, 0x10}, /* 640x480 */ - {0x01, 0x1a, 0x1a}, /* 352x288 */ - {0x02, 0x1c, 0x1d}, /* 320x240 */ - {0x04, 0x34, 0x34}, /* 176x144 */ - {0x05, 0x40, 0x40} /* 160x120 */ - }; - - if (sd->subtype == Nxultra) - write_vector(gspca_dev, spca505b_open_data_ccd); - else - write_vector(gspca_dev, spca505_open_data_ccd); - ret = reg_read(gspca_dev, 0x06, 0x16); - - if (ret < 0) { - PDEBUG(D_ERR|D_CONF, - "register read failed err: %d", - ret); - return ret; - } - if (ret != 0x0101) { - pr_err("After vector read returns 0x%04x should be 0x0101\n", - ret); - } - - ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a); - if (ret < 0) - return ret; - reg_write(gspca_dev->dev, 0x05, 0xc2, 0x12); - - /* necessary because without it we can see stream - * only once after loading module */ - /* stopping usb registers Tomasz change */ - reg_write(dev, 0x02, 0x00, 0x00); - - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - reg_write(dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]); - reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]); - reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]); - - return reg_write(dev, SPCA50X_REG_USB, - SPCA50X_USB_CTRL, - SPCA50X_CUSB_ENABLE); -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - /* Disable ISO packet machine */ - reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); -} - -/* called on streamoff with alt 0 and on disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - if (!gspca_dev->present) - return; - - /* This maybe reset or power control */ - reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); - reg_write(gspca_dev->dev, 0x03, 0x01, 0x00); - reg_write(gspca_dev->dev, 0x03, 0x00, 0x01); - reg_write(gspca_dev->dev, 0x05, 0x10, 0x01); - reg_write(gspca_dev->dev, 0x05, 0x11, 0x0f); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - switch (data[0]) { - case 0: /* start of frame */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - data += SPCA50X_OFFSET_DATA; - len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - break; - case 0xff: /* drop */ - break; - default: - data += 1; - len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - break; - } -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init_controls = sd_init_controls, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x401d), .driver_info = Nxultra}, - {USB_DEVICE(0x0733, 0x0430), .driver_info = IntelPCCameraPro}, -/*fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c deleted file mode 100644 index 969bb5a4cd93..000000000000 --- a/drivers/media/video/gspca/spca506.c +++ /dev/null @@ -1,612 +0,0 @@ -/* - * SPCA506 chip based cameras function - * M Xhaard 15/04/2004 based on different work Mark Taylor and others - * and my own snoopy file on a pv-321c donate by a german compagny - * "Firma Frank Gmbh" from Saarbruecken - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define MODULE_NAME "spca506" - -#include "gspca.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - char norme; - char channel; -}; - -static const struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 5}, - {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 4}, - {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -#define SPCA50X_OFFSET_DATA 10 - -#define SAA7113_bright 0x0a /* defaults 0x80 */ -#define SAA7113_contrast 0x0b /* defaults 0x47 */ -#define SAA7113_saturation 0x0c /* defaults 0x40 */ -#define SAA7113_hue 0x0d /* defaults 0x00 */ -#define SAA7113_I2C_BASE_WRITE 0x4a - -/* read 'len' bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 req, - __u16 index, - __u16 length) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, length, - 500); -} - -static void reg_w(struct usb_device *dev, - __u16 req, - __u16 value, - __u16 index) -{ - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, - NULL, 0, 500); -} - -static void spca506_Initi2c(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); -} - -static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur, - __u16 reg) -{ - int retry = 60; - - reg_w(gspca_dev->dev, 0x07, reg, 0x0001); - reg_w(gspca_dev->dev, 0x07, valeur, 0x0000); - while (retry--) { - reg_r(gspca_dev, 0x07, 0x0003, 2); - if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00) - break; - } -} - -static void spca506_SetNormeInput(struct gspca_dev *gspca_dev, - __u16 norme, - __u16 channel) -{ - struct sd *sd = (struct sd *) gspca_dev; -/* fixme: check if channel == 0..3 and 6..9 (8 values) */ - __u8 setbit0 = 0x00; - __u8 setbit1 = 0x00; - __u8 videomask = 0x00; - - PDEBUG(D_STREAM, "** Open Set Norme **"); - spca506_Initi2c(gspca_dev); - /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */ - /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */ - /* and exclude SAA7113 reserved channel set default 0 otherwise */ - if (norme & V4L2_STD_NTSC) - setbit0 = 0x01; - if (channel == 4 || channel == 5 || channel > 9) - channel = 0; - if (channel < 4) - setbit1 = 0x02; - videomask = (0x48 | setbit0 | setbit1); - reg_w(gspca_dev->dev, 0x08, videomask, 0x0000); - spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02); - - if (norme & V4L2_STD_NTSC) - spca506_WriteI2c(gspca_dev, 0x33, 0x0e); - /* Chrominance Control NTSC N */ - else if (norme & V4L2_STD_SECAM) - spca506_WriteI2c(gspca_dev, 0x53, 0x0e); - /* Chrominance Control SECAM */ - else - spca506_WriteI2c(gspca_dev, 0x03, 0x0e); - /* Chrominance Control PAL BGHIV */ - - sd->norme = norme; - sd->channel = channel; - PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask); - PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel); -} - -static void spca506_GetNormeInput(struct gspca_dev *gspca_dev, - __u16 *norme, __u16 *channel) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* Read the register is not so good value change so - we use your own copy in spca50x struct */ - *norme = sd->norme; - *channel = sd->channel; - PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel); -} - -static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code, - __u16 xmult, __u16 ymult) -{ - struct usb_device *dev = gspca_dev->dev; - - PDEBUG(D_STREAM, "** SetSize **"); - reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000); - /* Soft snap 0x40 Hard 0x41 */ - reg_w(dev, 0x04, 0x41, 0x0001); - reg_w(dev, 0x04, 0x00, 0x0002); - /* reserved */ - reg_w(dev, 0x04, 0x00, 0x0003); - - /* reserved */ - reg_w(dev, 0x04, 0x00, 0x0004); - /* reserved */ - reg_w(dev, 0x04, 0x01, 0x0005); - /* reserced */ - reg_w(dev, 0x04, xmult, 0x0006); - /* reserved */ - reg_w(dev, 0x04, ymult, 0x0007); - /* compression 1 */ - reg_w(dev, 0x04, 0x00, 0x0008); - /* T=64 -> 2 */ - reg_w(dev, 0x04, 0x00, 0x0009); - /* threshold2D */ - reg_w(dev, 0x04, 0x21, 0x000a); - /* quantization */ - reg_w(dev, 0x04, 0x00, 0x000b); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam; - - cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - - reg_w(dev, 0x03, 0x00, 0x0004); - reg_w(dev, 0x03, 0xFF, 0x0003); - reg_w(dev, 0x03, 0x00, 0x0000); - reg_w(dev, 0x03, 0x1c, 0x0001); - reg_w(dev, 0x03, 0x18, 0x0001); - /* Init on PAL and composite input0 */ - spca506_SetNormeInput(gspca_dev, 0, 0); - reg_w(dev, 0x03, 0x1c, 0x0001); - reg_w(dev, 0x03, 0x18, 0x0001); - reg_w(dev, 0x05, 0x00, 0x0000); - reg_w(dev, 0x05, 0xef, 0x0001); - reg_w(dev, 0x05, 0x00, 0x00c1); - reg_w(dev, 0x05, 0x00, 0x00c2); - reg_w(dev, 0x06, 0x18, 0x0002); - reg_w(dev, 0x06, 0xf5, 0x0011); - reg_w(dev, 0x06, 0x02, 0x0012); - reg_w(dev, 0x06, 0xfb, 0x0013); - reg_w(dev, 0x06, 0x00, 0x0014); - reg_w(dev, 0x06, 0xa4, 0x0051); - reg_w(dev, 0x06, 0x40, 0x0052); - reg_w(dev, 0x06, 0x71, 0x0053); - reg_w(dev, 0x06, 0x40, 0x0054); - /************************************************/ - reg_w(dev, 0x03, 0x00, 0x0004); - reg_w(dev, 0x03, 0x00, 0x0003); - reg_w(dev, 0x03, 0x00, 0x0004); - reg_w(dev, 0x03, 0xFF, 0x0003); - reg_w(dev, 0x02, 0x00, 0x0000); - reg_w(dev, 0x03, 0x60, 0x0000); - reg_w(dev, 0x03, 0x18, 0x0001); - /* for a better reading mx :) */ - /*sdca506_WriteI2c(value,register) */ - spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, 0x08, 0x01); - spca506_WriteI2c(gspca_dev, 0xc0, 0x02); - /* input composite video */ - spca506_WriteI2c(gspca_dev, 0x33, 0x03); - spca506_WriteI2c(gspca_dev, 0x00, 0x04); - spca506_WriteI2c(gspca_dev, 0x00, 0x05); - spca506_WriteI2c(gspca_dev, 0x0d, 0x06); - spca506_WriteI2c(gspca_dev, 0xf0, 0x07); - spca506_WriteI2c(gspca_dev, 0x98, 0x08); - spca506_WriteI2c(gspca_dev, 0x03, 0x09); - spca506_WriteI2c(gspca_dev, 0x80, 0x0a); - spca506_WriteI2c(gspca_dev, 0x47, 0x0b); - spca506_WriteI2c(gspca_dev, 0x48, 0x0c); - spca506_WriteI2c(gspca_dev, 0x00, 0x0d); - spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */ - spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); - spca506_WriteI2c(gspca_dev, 0x00, 0x10); - spca506_WriteI2c(gspca_dev, 0x0c, 0x11); - spca506_WriteI2c(gspca_dev, 0xb8, 0x12); - spca506_WriteI2c(gspca_dev, 0x01, 0x13); - spca506_WriteI2c(gspca_dev, 0x00, 0x14); - spca506_WriteI2c(gspca_dev, 0x00, 0x15); - spca506_WriteI2c(gspca_dev, 0x00, 0x16); - spca506_WriteI2c(gspca_dev, 0x00, 0x17); - spca506_WriteI2c(gspca_dev, 0x00, 0x18); - spca506_WriteI2c(gspca_dev, 0x00, 0x19); - spca506_WriteI2c(gspca_dev, 0x00, 0x1a); - spca506_WriteI2c(gspca_dev, 0x00, 0x1b); - spca506_WriteI2c(gspca_dev, 0x00, 0x1c); - spca506_WriteI2c(gspca_dev, 0x00, 0x1d); - spca506_WriteI2c(gspca_dev, 0x00, 0x1e); - spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); - spca506_WriteI2c(gspca_dev, 0x02, 0x40); - spca506_WriteI2c(gspca_dev, 0xff, 0x41); - spca506_WriteI2c(gspca_dev, 0xff, 0x42); - spca506_WriteI2c(gspca_dev, 0xff, 0x43); - spca506_WriteI2c(gspca_dev, 0xff, 0x44); - spca506_WriteI2c(gspca_dev, 0xff, 0x45); - spca506_WriteI2c(gspca_dev, 0xff, 0x46); - spca506_WriteI2c(gspca_dev, 0xff, 0x47); - spca506_WriteI2c(gspca_dev, 0xff, 0x48); - spca506_WriteI2c(gspca_dev, 0xff, 0x49); - spca506_WriteI2c(gspca_dev, 0xff, 0x4a); - spca506_WriteI2c(gspca_dev, 0xff, 0x4b); - spca506_WriteI2c(gspca_dev, 0xff, 0x4c); - spca506_WriteI2c(gspca_dev, 0xff, 0x4d); - spca506_WriteI2c(gspca_dev, 0xff, 0x4e); - spca506_WriteI2c(gspca_dev, 0xff, 0x4f); - spca506_WriteI2c(gspca_dev, 0xff, 0x50); - spca506_WriteI2c(gspca_dev, 0xff, 0x51); - spca506_WriteI2c(gspca_dev, 0xff, 0x52); - spca506_WriteI2c(gspca_dev, 0xff, 0x53); - spca506_WriteI2c(gspca_dev, 0xff, 0x54); - spca506_WriteI2c(gspca_dev, 0xff, 0x55); - spca506_WriteI2c(gspca_dev, 0xff, 0x56); - spca506_WriteI2c(gspca_dev, 0xff, 0x57); - spca506_WriteI2c(gspca_dev, 0x00, 0x58); - spca506_WriteI2c(gspca_dev, 0x54, 0x59); - spca506_WriteI2c(gspca_dev, 0x07, 0x5a); - spca506_WriteI2c(gspca_dev, 0x83, 0x5b); - spca506_WriteI2c(gspca_dev, 0x00, 0x5c); - spca506_WriteI2c(gspca_dev, 0x00, 0x5d); - spca506_WriteI2c(gspca_dev, 0x00, 0x5e); - spca506_WriteI2c(gspca_dev, 0x00, 0x5f); - spca506_WriteI2c(gspca_dev, 0x00, 0x60); - spca506_WriteI2c(gspca_dev, 0x05, 0x61); - spca506_WriteI2c(gspca_dev, 0x9f, 0x62); - PDEBUG(D_STREAM, "** Close Init *"); - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - __u16 norme; - __u16 channel; - - /**************************************/ - reg_w(dev, 0x03, 0x00, 0x0004); - reg_w(dev, 0x03, 0x00, 0x0003); - reg_w(dev, 0x03, 0x00, 0x0004); - reg_w(dev, 0x03, 0xFF, 0x0003); - reg_w(dev, 0x02, 0x00, 0x0000); - reg_w(dev, 0x03, 0x60, 0x0000); - reg_w(dev, 0x03, 0x18, 0x0001); - - /*sdca506_WriteI2c(value,register) */ - spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */ -/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */ - spca506_WriteI2c(gspca_dev, 0x33, 0x03); - /* Analog Input Control 2 */ - spca506_WriteI2c(gspca_dev, 0x00, 0x04); - /* Analog Input Control 3 */ - spca506_WriteI2c(gspca_dev, 0x00, 0x05); - /* Analog Input Control 4 */ - spca506_WriteI2c(gspca_dev, 0x0d, 0x06); - /* Horizontal Sync Start 0xe9-0x0d */ - spca506_WriteI2c(gspca_dev, 0xf0, 0x07); - /* Horizontal Sync Stop 0x0d-0xf0 */ - - spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */ -/* Defaults value */ - spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */ - spca506_WriteI2c(gspca_dev, 0x80, 0x0a); - /* Luminance Brightness */ - spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */ - spca506_WriteI2c(gspca_dev, 0x48, 0x0c); - /* Chrominance Saturation */ - spca506_WriteI2c(gspca_dev, 0x00, 0x0d); - /* Chrominance Hue Control */ - spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); - /* Chrominance Gain Control */ - /**************************************/ - spca506_WriteI2c(gspca_dev, 0x00, 0x10); - /* Format/Delay Control */ - spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */ - spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */ - spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */ - spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */ - spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */ - spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */ - spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */ - spca506_WriteI2c(gspca_dev, 0x00, 0x18); - spca506_WriteI2c(gspca_dev, 0x00, 0x19); - spca506_WriteI2c(gspca_dev, 0x00, 0x1a); - spca506_WriteI2c(gspca_dev, 0x00, 0x1b); - spca506_WriteI2c(gspca_dev, 0x00, 0x1c); - spca506_WriteI2c(gspca_dev, 0x00, 0x1d); - spca506_WriteI2c(gspca_dev, 0x00, 0x1e); - spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); - spca506_WriteI2c(gspca_dev, 0x02, 0x40); - spca506_WriteI2c(gspca_dev, 0xff, 0x41); - spca506_WriteI2c(gspca_dev, 0xff, 0x42); - spca506_WriteI2c(gspca_dev, 0xff, 0x43); - spca506_WriteI2c(gspca_dev, 0xff, 0x44); - spca506_WriteI2c(gspca_dev, 0xff, 0x45); - spca506_WriteI2c(gspca_dev, 0xff, 0x46); - spca506_WriteI2c(gspca_dev, 0xff, 0x47); - spca506_WriteI2c(gspca_dev, 0xff, 0x48); - spca506_WriteI2c(gspca_dev, 0xff, 0x49); - spca506_WriteI2c(gspca_dev, 0xff, 0x4a); - spca506_WriteI2c(gspca_dev, 0xff, 0x4b); - spca506_WriteI2c(gspca_dev, 0xff, 0x4c); - spca506_WriteI2c(gspca_dev, 0xff, 0x4d); - spca506_WriteI2c(gspca_dev, 0xff, 0x4e); - spca506_WriteI2c(gspca_dev, 0xff, 0x4f); - spca506_WriteI2c(gspca_dev, 0xff, 0x50); - spca506_WriteI2c(gspca_dev, 0xff, 0x51); - spca506_WriteI2c(gspca_dev, 0xff, 0x52); - spca506_WriteI2c(gspca_dev, 0xff, 0x53); - spca506_WriteI2c(gspca_dev, 0xff, 0x54); - spca506_WriteI2c(gspca_dev, 0xff, 0x55); - spca506_WriteI2c(gspca_dev, 0xff, 0x56); - spca506_WriteI2c(gspca_dev, 0xff, 0x57); - spca506_WriteI2c(gspca_dev, 0x00, 0x58); - spca506_WriteI2c(gspca_dev, 0x54, 0x59); - spca506_WriteI2c(gspca_dev, 0x07, 0x5a); - spca506_WriteI2c(gspca_dev, 0x83, 0x5b); - spca506_WriteI2c(gspca_dev, 0x00, 0x5c); - spca506_WriteI2c(gspca_dev, 0x00, 0x5d); - spca506_WriteI2c(gspca_dev, 0x00, 0x5e); - spca506_WriteI2c(gspca_dev, 0x00, 0x5f); - spca506_WriteI2c(gspca_dev, 0x00, 0x60); - spca506_WriteI2c(gspca_dev, 0x05, 0x61); - spca506_WriteI2c(gspca_dev, 0x9f, 0x62); - /**************************************/ - reg_w(dev, 0x05, 0x00, 0x0003); - reg_w(dev, 0x05, 0x00, 0x0004); - reg_w(dev, 0x03, 0x10, 0x0001); - reg_w(dev, 0x03, 0x78, 0x0000); - switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 0: - spca506_Setsize(gspca_dev, 0, 0x10, 0x10); - break; - case 1: - spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a); - break; - case 2: - spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c); - break; - case 4: - spca506_Setsize(gspca_dev, 4, 0x34, 0x34); - break; - default: -/* case 5: */ - spca506_Setsize(gspca_dev, 5, 0x40, 0x40); - break; - } - - /* compress setting and size */ - /* set i2c luma */ - reg_w(dev, 0x02, 0x01, 0x0000); - reg_w(dev, 0x03, 0x12, 0x0000); - reg_r(gspca_dev, 0x04, 0x0001, 2); - PDEBUG(D_STREAM, "webcam started"); - spca506_GetNormeInput(gspca_dev, &norme, &channel); - spca506_SetNormeInput(gspca_dev, norme, channel); - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - - reg_w(dev, 0x02, 0x00, 0x0000); - reg_w(dev, 0x03, 0x00, 0x0004); - reg_w(dev, 0x03, 0x00, 0x0003); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - switch (data[0]) { - case 0: /* start of frame */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - data += SPCA50X_OFFSET_DATA; - len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - break; - case 0xff: /* drop */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - break; - default: - data += 1; - len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - break; - } -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_bright); - spca506_WriteI2c(gspca_dev, 0x01, 0x09); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_contrast); - spca506_WriteI2c(gspca_dev, 0x01, 0x09); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_saturation); - spca506_WriteI2c(gspca_dev, 0x01, 0x09); -} - -static void sethue(struct gspca_dev *gspca_dev, s32 val) -{ - spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_hue); - spca506_WriteI2c(gspca_dev, 0x01, 0x09); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_HUE: - sethue(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 0x47); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 0x40); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HUE, 0, 255, 1, 0); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] __devinitconst = { - {USB_DEVICE(0x06e1, 0xa190)}, -/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505 - {USB_DEVICE(0x0733, 0x0430)}, */ - {USB_DEVICE(0x0734, 0x043b)}, - {USB_DEVICE(0x99fa, 0x8988)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int __devinit sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c deleted file mode 100644 index 1286b4170b88..000000000000 --- a/drivers/media/video/gspca/spca508.c +++ /dev/null @@ -1,1540 +0,0 @@ -/* - * SPCA508 chip based cameras subdriver - * - * Copyright (C) 2009 Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "spca508" - -#include "gspca.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - u8 subtype; -#define CreativeVista 0 -#define HamaUSBSightcam 1 -#define HamaUSBSightcam2 2 -#define IntelEasyPCCamera 3 -#define MicroInnovationIC200 4 -#define ViewQuestVQ110 5 -}; - -static const struct v4l2_pix_format sif_mode[] = { - {160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/* Frame packet header offsets for the spca508 */ -#define SPCA508_OFFSET_DATA 37 - -/* - * Initialization data: this is the first set-up data written to the - * device (before the open data). - */ -static const u16 spca508_init_data[][2] = { - {0x0000, 0x870b}, - - {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ - {0x0003, 0x8111}, /* Reset compression & memory */ - {0x0000, 0x8110}, /* Disable all outputs */ - /* READ {0x0000, 0x8114} -> 0000: 00 */ - {0x0000, 0x8114}, /* SW GPIO data */ - {0x0008, 0x8110}, /* Enable charge pump output */ - {0x0002, 0x8116}, /* 200 kHz pump clock */ - /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ - {0x0003, 0x8111}, /* Reset compression & memory */ - {0x0000, 0x8111}, /* Normal mode (not reset) */ - {0x0098, 0x8110}, - /* Enable charge pump output, sync.serial,external 2x clock */ - {0x000d, 0x8114}, /* SW GPIO data */ - {0x0002, 0x8116}, /* 200 kHz pump clock */ - {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ -/* --------------------------------------- */ - {0x000f, 0x8402}, /* memory bank */ - {0x0000, 0x8403}, /* ... address */ -/* --------------------------------------- */ -/* 0x88__ is Synchronous Serial Interface. */ -/* TBD: This table could be expressed more compactly */ -/* using spca508_write_i2c_vector(). */ -/* TBD: Should see if the values in spca50x_i2c_data */ -/* would work with the VQ110 instead of the values */ -/* below. */ - {0x00c0, 0x8804}, /* SSI slave addr */ - {0x0008, 0x8802}, /* 375 Khz SSI clock */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, /* 375 Khz SSI clock */ - {0x0012, 0x8801}, /* SSI reg addr */ - {0x0080, 0x8800}, /* SSI data to write */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, /* 375 Khz SSI clock */ - {0x0012, 0x8801}, /* SSI reg addr */ - {0x0000, 0x8800}, /* SSI data to write */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, /* 375 Khz SSI clock */ - {0x0011, 0x8801}, /* SSI reg addr */ - {0x0040, 0x8800}, /* SSI data to write */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0013, 0x8801}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0014, 0x8801}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0015, 0x8801}, - {0x0001, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0016, 0x8801}, - {0x0003, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0017, 0x8801}, - {0x0036, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0018, 0x8801}, - {0x00ec, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x001a, 0x8801}, - {0x0094, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x001b, 0x8801}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0027, 0x8801}, - {0x00a2, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0028, 0x8801}, - {0x0040, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x002a, 0x8801}, - {0x0084, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x002b, 0x8801}, - {0x00a8, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x002c, 0x8801}, - {0x00fe, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x002d, 0x8801}, - {0x0003, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0038, 0x8801}, - {0x0083, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0033, 0x8801}, - {0x0081, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0034, 0x8801}, - {0x004a, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0039, 0x8801}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0010, 0x8801}, - {0x00a8, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0006, 0x8801}, - {0x0058, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0000, 0x8801}, - {0x0004, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0040, 0x8801}, - {0x0080, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0041, 0x8801}, - {0x000c, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0042, 0x8801}, - {0x000c, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0043, 0x8801}, - {0x0028, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0044, 0x8801}, - {0x0080, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0045, 0x8801}, - {0x0020, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0046, 0x8801}, - {0x0020, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0047, 0x8801}, - {0x0080, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0048, 0x8801}, - {0x004c, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x0049, 0x8801}, - {0x0084, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x004a, 0x8801}, - {0x0084, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x0008, 0x8802}, - {0x004b, 0x8801}, - {0x0084, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* --------------------------------------- */ - {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ - {0x0000, 0x8701}, /* CKx1 clock delay adj */ - {0x0000, 0x8701}, /* CKx1 clock delay adj */ - {0x0001, 0x870c}, /* CKOx2 output */ - /* --------------------------------------- */ - {0x0080, 0x8600}, /* Line memory read counter (L) */ - {0x0001, 0x8606}, /* reserved */ - {0x0064, 0x8607}, /* Line memory read counter (H) 0x6480=25,728 */ - {0x002a, 0x8601}, /* CDSP sharp interpolation mode, - * line sel for color sep, edge enhance enab */ - {0x0000, 0x8602}, /* optical black level for user settng = 0 */ - {0x0080, 0x8600}, /* Line memory read counter (L) */ - {0x000a, 0x8603}, /* optical black level calc mode: - * auto; optical black offset = 10 */ - {0x00df, 0x865b}, /* Horiz offset for valid pixels (L)=0xdf */ - {0x0012, 0x865c}, /* Vert offset for valid lines (L)=0x12 */ - -/* The following two lines seem to be the "wrong" resolution. */ -/* But perhaps these indicate the actual size of the sensor */ -/* rather than the size of the current video mode. */ - {0x0058, 0x865d}, /* Horiz valid pixels (*4) (L) = 352 */ - {0x0048, 0x865e}, /* Vert valid lines (*4) (L) = 288 */ - - {0x0015, 0x8608}, /* A11 Coef ... */ - {0x0030, 0x8609}, - {0x00fb, 0x860a}, - {0x003e, 0x860b}, - {0x00ce, 0x860c}, - {0x00f4, 0x860d}, - {0x00eb, 0x860e}, - {0x00dc, 0x860f}, - {0x0039, 0x8610}, - {0x0001, 0x8611}, /* R offset for white balance ... */ - {0x0000, 0x8612}, - {0x0001, 0x8613}, - {0x0000, 0x8614}, - {0x005b, 0x8651}, /* R gain for white balance ... */ - {0x0040, 0x8652}, - {0x0060, 0x8653}, - {0x0040, 0x8654}, - {0x0000, 0x8655}, - {0x0001, 0x863f}, /* Fixed gamma correction enable, USB control, - * lum filter disable, lum noise clip disable */ - {0x00a1, 0x8656}, /* Window1 size 256x256, Windows2 size 64x64, - * gamma look-up disable, - * new edge enhancement enable */ - {0x0018, 0x8657}, /* Edge gain high thresh */ - {0x0020, 0x8658}, /* Edge gain low thresh */ - {0x000a, 0x8659}, /* Edge bandwidth high threshold */ - {0x0005, 0x865a}, /* Edge bandwidth low threshold */ - /* -------------------------------- */ - {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0xa908, 0x8802}, - {0x0034, 0x8801}, /* SSI reg addr */ - {0x00ca, 0x8800}, - /* SSI data to write */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0x1f08, 0x8802}, - {0x0006, 0x8801}, - {0x0080, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - -/* ----- Read back coefs we wrote earlier. */ - /* READ { 0x0000, 0x8608 } -> 0000: 15 */ - /* READ { 0x0000, 0x8609 } -> 0000: 30 */ - /* READ { 0x0000, 0x860a } -> 0000: fb */ - /* READ { 0x0000, 0x860b } -> 0000: 3e */ - /* READ { 0x0000, 0x860c } -> 0000: ce */ - /* READ { 0x0000, 0x860d } -> 0000: f4 */ - /* READ { 0x0000, 0x860e } -> 0000: eb */ - /* READ { 0x0000, 0x860f } -> 0000: dc */ - /* READ { 0x0000, 0x8610 } -> 0000: 39 */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 08 */ - {0xb008, 0x8802}, - {0x0006, 0x8801}, - {0x007d, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - - /* This chunk is seemingly redundant with */ - /* earlier commands (A11 Coef...), but if I disable it, */ - /* the image appears too dark. Maybe there was some kind of */ - /* reset since the earlier commands, so this is necessary again. */ - {0x0015, 0x8608}, - {0x0030, 0x8609}, - {0xfffb, 0x860a}, - {0x003e, 0x860b}, - {0xffce, 0x860c}, - {0xfff4, 0x860d}, - {0xffeb, 0x860e}, - {0xffdc, 0x860f}, - {0x0039, 0x8610}, - {0x0018, 0x8657}, - - {0x0000, 0x8508}, /* Disable compression. */ - /* Previous line was: - {0x0021, 0x8508}, * Enable compression. */ - {0x0032, 0x850b}, /* compression stuff */ - {0x0003, 0x8509}, /* compression stuff */ - {0x0011, 0x850a}, /* compression stuff */ - {0x0021, 0x850d}, /* compression stuff */ - {0x0010, 0x850c}, /* compression stuff */ - {0x0003, 0x8500}, /* *** Video mode: 160x120 */ - {0x0001, 0x8501}, /* Hardware-dominated snap control */ - {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128, - * gamma look-up disable, - * new edge enhancement enable */ - {0x0018, 0x8617}, /* Window1 start X (*2) */ - {0x0008, 0x8618}, /* Window1 start Y (*2) */ - {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128, - * gamma look-up disable, - * new edge enhancement enable */ - {0x0058, 0x8619}, /* Window2 start X (*2) */ - {0x0008, 0x861a}, /* Window2 start Y (*2) */ - {0x00ff, 0x8615}, /* High lum thresh for white balance */ - {0x0000, 0x8616}, /* Low lum thresh for white balance */ - {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ - {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ - /* READ { 0x0000, 0x8656 } -> 0000: 61 */ - {0x0028, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 28 */ - {0x1f28, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ - {0x0010, 0x8801}, /* SSI reg addr */ - {0x003e, 0x8800}, /* SSI data to write */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - {0x0028, 0x8802}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 28 */ - {0x1f28, 0x8802}, - {0x0000, 0x8801}, - {0x001f, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - {0x0001, 0x8602}, /* optical black level for user settning = 1 */ - - /* Original: */ - {0x0023, 0x8700}, /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ - {0x000f, 0x8602}, /* optical black level for user settning = 15 */ - - {0x0028, 0x8802}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 28 */ - {0x1f28, 0x8802}, - {0x0010, 0x8801}, - {0x007b, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - {0x002f, 0x8651}, /* R gain for white balance ... */ - {0x0080, 0x8653}, - /* READ { 0x0000, 0x8655 } -> 0000: 00 */ - {0x0000, 0x8655}, - - {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */ - {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ - /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ - {} -}; - -/* - * Initialization data for Intel EasyPC Camera CS110 - */ -static const u16 spca508cs110_init_data[][2] = { - {0x0000, 0x870b}, /* Reset CTL3 */ - {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ - {0x0000, 0x8111}, /* Normal operation on reset */ - {0x0090, 0x8110}, - /* External Clock 2x & Synchronous Serial Interface Output */ - {0x0020, 0x8112}, /* Video Drop packet enable */ - {0x0000, 0x8114}, /* Software GPIO output data */ - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0003, 0x8114}, - - /* Initial sequence Synchronous Serial Interface */ - {0x000f, 0x8402}, /* Memory bank Address */ - {0x0000, 0x8403}, /* Memory bank Address */ - {0x00ba, 0x8804}, /* SSI Slave address */ - {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ - {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ - - {0x0001, 0x8801}, - {0x000a, 0x8805}, /* a - NWG: Dunno what this is about */ - {0x0000, 0x8800}, - {0x0010, 0x8802}, - - {0x0002, 0x8801}, - {0x0000, 0x8805}, - {0x0000, 0x8800}, - {0x0010, 0x8802}, - - {0x0003, 0x8801}, - {0x0027, 0x8805}, - {0x0001, 0x8800}, - {0x0010, 0x8802}, - - {0x0004, 0x8801}, - {0x0065, 0x8805}, - {0x0001, 0x8800}, - {0x0010, 0x8802}, - - {0x0005, 0x8801}, - {0x0003, 0x8805}, - {0x0000, 0x8800}, - {0x0010, 0x8802}, - - {0x0006, 0x8801}, - {0x001c, 0x8805}, - {0x0000, 0x8800}, - {0x0010, 0x8802}, - - {0x0007, 0x8801}, - {0x002a, 0x8805}, - {0x0000, 0x8800}, - {0x0010, 0x8802}, - - {0x0002, 0x8704}, /* External input CKIx1 */ - {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ - {0x009a, 0x8600}, /* Line memory Read Counter (L) */ - {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ - {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ - {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ - - {0x0006, 0x8660}, /* Nibble data + input order */ - - {0x000a, 0x8602}, /* Optical black level set to 0x0a */ - {0x0000, 0x8603}, /* Optical black level Offset */ - -/* {0x0000, 0x8611}, * 0 R Offset for white Balance */ -/* {0x0000, 0x8612}, * 1 Gr Offset for white Balance */ -/* {0x0000, 0x8613}, * 1f B Offset for white Balance */ -/* {0x0000, 0x8614}, * f0 Gb Offset for white Balance */ - - {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ - {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ - {0x0035, 0x8653}, /* 26 RED gain for white balance */ - {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ - {0x0041, 0x863f}, - /* Fixed Gamma correction enabled (makes colours look better) */ - - {0x0000, 0x8655}, - /* High bits for white balance*****brightness control*** */ - {} -}; - -static const u16 spca508_sightcam_init_data[][2] = { -/* This line seems to setup the frame/canvas */ - {0x000f, 0x8402}, - -/* These 6 lines are needed to startup the webcam */ - {0x0090, 0x8110}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0003, 0x8114}, - {0x0080, 0x8804}, - -/* This part seems to make the pictures darker? (autobrightness?) */ - {0x0001, 0x8801}, - {0x0004, 0x8800}, - {0x0003, 0x8801}, - {0x00e0, 0x8800}, - {0x0004, 0x8801}, - {0x00b4, 0x8800}, - {0x0005, 0x8801}, - {0x0000, 0x8800}, - - {0x0006, 0x8801}, - {0x00e0, 0x8800}, - {0x0007, 0x8801}, - {0x000c, 0x8800}, - -/* This section is just needed, it probably - * does something like the previous section, - * but the cam won't start if it's not included. - */ - {0x0014, 0x8801}, - {0x0008, 0x8800}, - {0x0015, 0x8801}, - {0x0067, 0x8800}, - {0x0016, 0x8801}, - {0x0000, 0x8800}, - {0x0017, 0x8801}, - {0x0020, 0x8800}, - {0x0018, 0x8801}, - {0x0044, 0x8800}, - -/* Makes the picture darker - and the - * cam won't start if not included - */ - {0x001e, 0x8801}, - {0x00ea, 0x8800}, - {0x001f, 0x8801}, - {0x0001, 0x8800}, - {0x0003, 0x8801}, - {0x00e0, 0x8800}, - -/* seems to place the colors ontop of each other #1 */ - {0x0006, 0x8704}, - {0x0001, 0x870c}, - {0x0016, 0x8600}, - {0x0002, 0x8606}, - -/* if not included the pictures becomes _very_ dark */ - {0x0064, 0x8607}, - {0x003a, 0x8601}, - {0x0000, 0x8602}, - -/* seems to place the colors ontop of each other #2 */ - {0x0016, 0x8600}, - {0x0018, 0x8617}, - {0x0008, 0x8618}, - {0x00a1, 0x8656}, - -/* webcam won't start if not included */ - {0x0007, 0x865b}, - {0x0001, 0x865c}, - {0x0058, 0x865d}, - {0x0048, 0x865e}, - -/* adjusts the colors */ - {0x0049, 0x8651}, - {0x0040, 0x8652}, - {0x004c, 0x8653}, - {0x0040, 0x8654}, - {} -}; - -static const u16 spca508_sightcam2_init_data[][2] = { - {0x0020, 0x8112}, - - {0x000f, 0x8402}, - {0x0000, 0x8403}, - - {0x0008, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x0009, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x000a, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x000b, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x000c, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x000d, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x000e, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x0007, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x000f, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - - {0x0018, 0x8660}, - {0x0010, 0x8201}, - - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x0011, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - - {0x0000, 0x86b0}, - {0x0034, 0x86b1}, - {0x0000, 0x86b2}, - {0x0049, 0x86b3}, - {0x0000, 0x86b4}, - {0x0000, 0x86b4}, - - {0x0012, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - {0x0013, 0x8201}, - {0x0008, 0x8200}, - {0x0001, 0x8200}, - - {0x0001, 0x86b0}, - {0x00aa, 0x86b1}, - {0x0000, 0x86b2}, - {0x00e4, 0x86b3}, - {0x0000, 0x86b4}, - {0x0000, 0x86b4}, - - {0x0018, 0x8660}, - - {0x0090, 0x8110}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0003, 0x8114}, - - {0x0080, 0x8804}, - {0x0003, 0x8801}, - {0x0012, 0x8800}, - {0x0004, 0x8801}, - {0x0005, 0x8800}, - {0x0005, 0x8801}, - {0x0000, 0x8800}, - {0x0006, 0x8801}, - {0x0000, 0x8800}, - {0x0007, 0x8801}, - {0x0000, 0x8800}, - {0x0008, 0x8801}, - {0x0005, 0x8800}, - {0x000a, 0x8700}, - {0x000e, 0x8801}, - {0x0004, 0x8800}, - {0x0005, 0x8801}, - {0x0047, 0x8800}, - {0x0006, 0x8801}, - {0x0000, 0x8800}, - {0x0007, 0x8801}, - {0x00c0, 0x8800}, - {0x0008, 0x8801}, - {0x0003, 0x8800}, - {0x0013, 0x8801}, - {0x0001, 0x8800}, - {0x0009, 0x8801}, - {0x0000, 0x8800}, - {0x000a, 0x8801}, - {0x0000, 0x8800}, - {0x000b, 0x8801}, - {0x0000, 0x8800}, - {0x000c, 0x8801}, - {0x0000, 0x8800}, - {0x000e, 0x8801}, - {0x0004, 0x8800}, - {0x000f, 0x8801}, - {0x0000, 0x8800}, - {0x0010, 0x8801}, - {0x0006, 0x8800}, - {0x0011, 0x8801}, - {0x0006, 0x8800}, - {0x0012, 0x8801}, - {0x0000, 0x8800}, - {0x0013, 0x8801}, - {0x0001, 0x8800}, - - {0x000a, 0x8700}, - {0x0000, 0x8702}, - {0x0000, 0x8703}, - {0x00c2, 0x8704}, - {0x0001, 0x870c}, - - {0x0044, 0x8600}, - {0x0002, 0x8606}, - {0x0064, 0x8607}, - {0x003a, 0x8601}, - {0x0008, 0x8602}, - {0x0044, 0x8600}, - {0x0018, 0x8617}, - {0x0008, 0x8618}, - {0x00a1, 0x8656}, - {0x0004, 0x865b}, - {0x0002, 0x865c}, - {0x0058, 0x865d}, - {0x0048, 0x865e}, - {0x0012, 0x8608}, - {0x002c, 0x8609}, - {0x0002, 0x860a}, - {0x002c, 0x860b}, - {0x00db, 0x860c}, - {0x00f9, 0x860d}, - {0x00f1, 0x860e}, - {0x00e3, 0x860f}, - {0x002c, 0x8610}, - {0x006c, 0x8651}, - {0x0041, 0x8652}, - {0x0059, 0x8653}, - {0x0040, 0x8654}, - {0x00fa, 0x8611}, - {0x00ff, 0x8612}, - {0x00f8, 0x8613}, - {0x0000, 0x8614}, - {0x0001, 0x863f}, - {0x0000, 0x8640}, - {0x0026, 0x8641}, - {0x0045, 0x8642}, - {0x0060, 0x8643}, - {0x0075, 0x8644}, - {0x0088, 0x8645}, - {0x009b, 0x8646}, - {0x00b0, 0x8647}, - {0x00c5, 0x8648}, - {0x00d2, 0x8649}, - {0x00dc, 0x864a}, - {0x00e5, 0x864b}, - {0x00eb, 0x864c}, - {0x00f0, 0x864d}, - {0x00f6, 0x864e}, - {0x00fa, 0x864f}, - {0x00ff, 0x8650}, - {0x0060, 0x8657}, - {0x0010, 0x8658}, - {0x0018, 0x8659}, - {0x0005, 0x865a}, - {0x0018, 0x8660}, - {0x0003, 0x8509}, - {0x0011, 0x850a}, - {0x0032, 0x850b}, - {0x0010, 0x850c}, - {0x0021, 0x850d}, - {0x0001, 0x8500}, - {0x0000, 0x8508}, - {0x0012, 0x8608}, - {0x002c, 0x8609}, - {0x0002, 0x860a}, - {0x0039, 0x860b}, - {0x00d0, 0x860c}, - {0x00f7, 0x860d}, - {0x00ed, 0x860e}, - {0x00db, 0x860f}, - {0x0039, 0x8610}, - {0x0012, 0x8657}, - {0x000c, 0x8619}, - {0x0004, 0x861a}, - {0x00a1, 0x8656}, - {0x00c8, 0x8615}, - {0x0032, 0x8616}, - - {0x0030, 0x8112}, - {0x0020, 0x8112}, - {0x0020, 0x8112}, - {0x000f, 0x8402}, - {0x0000, 0x8403}, - - {0x0090, 0x8110}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0003, 0x8114}, - {0x0080, 0x8804}, - - {0x0003, 0x8801}, - {0x0012, 0x8800}, - {0x0004, 0x8801}, - {0x0005, 0x8800}, - {0x0005, 0x8801}, - {0x0047, 0x8800}, - {0x0006, 0x8801}, - {0x0000, 0x8800}, - {0x0007, 0x8801}, - {0x00c0, 0x8800}, - {0x0008, 0x8801}, - {0x0003, 0x8800}, - {0x000a, 0x8700}, - {0x000e, 0x8801}, - {0x0004, 0x8800}, - {0x0005, 0x8801}, - {0x0047, 0x8800}, - {0x0006, 0x8801}, - {0x0000, 0x8800}, - {0x0007, 0x8801}, - {0x00c0, 0x8800}, - {0x0008, 0x8801}, - {0x0003, 0x8800}, - {0x0013, 0x8801}, - {0x0001, 0x8800}, - {0x0009, 0x8801}, - {0x0000, 0x8800}, - {0x000a, 0x8801}, - {0x0000, 0x8800}, - {0x000b, 0x8801}, - {0x0000, 0x8800}, - {0x000c, 0x8801}, - {0x0000, 0x8800}, - {0x000e, 0x8801}, - {0x0004, 0x8800}, - {0x000f, 0x8801}, - {0x0000, 0x8800}, - {0x0010, 0x8801}, - {0x0006, 0x8800}, - {0x0011, 0x8801}, - {0x0006, 0x8800}, - {0x0012, 0x8801}, - {0x0000, 0x8800}, - {0x0013, 0x8801}, - {0x0001, 0x8800}, - {0x000a, 0x8700}, - {0x0000, 0x8702}, - {0x0000, 0x8703}, - {0x00c2, 0x8704}, - {0x0001, 0x870c}, - {0x0044, 0x8600}, - {0x0002, 0x8606}, - {0x0064, 0x8607}, - {0x003a, 0x8601}, - {0x0008, 0x8602}, - {0x0044, 0x8600}, - {0x0018, 0x8617}, - {0x0008, 0x8618}, - {0x00a1, 0x8656}, - {0x0004, 0x865b}, - {0x0002, 0x865c}, - {0x0058, 0x865d}, - {0x0048, 0x865e}, - {0x0012, 0x8608}, - {0x002c, 0x8609}, - {0x0002, 0x860a}, - {0x002c, 0x860b}, - {0x00db, 0x860c}, - {0x00f9, 0x860d}, - {0x00f1, 0x860e}, - {0x00e3, 0x860f}, - {0x002c, 0x8610}, - {0x006c, 0x8651}, - {0x0041, 0x8652}, - {0x0059, 0x8653}, - {0x0040, 0x8654}, - {0x00fa, 0x8611}, - {0x00ff, 0x8612}, - {0x00f8, 0x8613}, - {0x0000, 0x8614}, - {0x0001, 0x863f}, - {0x0000, 0x8640}, - {0x0026, 0x8641}, - {0x0045, 0x8642}, - {0x0060, 0x8643}, - {0x0075, 0x8644}, - {0x0088, 0x8645}, - {0x009b, 0x8646}, - {0x00b0, 0x8647}, - {0x00c5, 0x8648}, - {0x00d2, 0x8649}, - {0x00dc, 0x864a}, - {0x00e5, 0x864b}, - {0x00eb, 0x864c}, - {0x00f0, 0x864d}, - {0x00f6, 0x864e}, - {0x00fa, 0x864f}, - {0x00ff, 0x8650}, - {0x0060, 0x8657}, - {0x0010, 0x8658}, - {0x0018, 0x8659}, - {0x0005, 0x865a}, - {0x0018, 0x8660}, - {0x0003, 0x8509}, - {0x0011, 0x850a}, - {0x0032, 0x850b}, - {0x0010, 0x850c}, - {0x0021, 0x850d}, - {0x0001, 0x8500}, - {0x0000, 0x8508}, - - {0x0012, 0x8608}, - {0x002c, 0x8609}, - {0x0002, 0x860a}, - {0x0039, 0x860b}, - {0x00d0, 0x860c}, - {0x00f7, 0x860d}, - {0x00ed, 0x860e}, - {0x00db, 0x860f}, - {0x0039, 0x8610}, - {0x0012, 0x8657}, - {0x0064, 0x8619}, - -/* This line starts it all, it is not needed here */ -/* since it has been build into the driver */ -/* jfm: don't start now */ -/* {0x0030, 0x8112}, */ - {} -}; - -/* - * Initialization data for Creative Webcam Vista - */ -static const u16 spca508_vista_init_data[][2] = { - {0x0008, 0x8200}, /* Clear register */ - {0x0000, 0x870b}, /* Reset CTL3 */ - {0x0020, 0x8112}, /* Video Drop packet enable */ - {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ - {0x0000, 0x8110}, /* Disable everything */ - {0x0000, 0x8114}, /* Software GPIO output data */ - {0x0000, 0x8114}, - - {0x0003, 0x8111}, - {0x0000, 0x8111}, - {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ - {0x0020, 0x8112}, - {0x0000, 0x8114}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0001, 0x8114}, - {0x0003, 0x8114}, - - {0x000f, 0x8402}, /* Memory bank Address */ - {0x0000, 0x8403}, /* Memory bank Address */ - {0x00ba, 0x8804}, /* SSI Slave address */ - {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */ - {0x0020, 0x8801}, /* Register address for SSI read/write */ - {0x0044, 0x8805}, /* DATA2 */ - {0x0004, 0x8800}, /* DATA1 -> write triggered */ - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0009, 0x8801}, - {0x0042, 0x8805}, - {0x0001, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x003c, 0x8801}, - {0x0001, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0001, 0x8801}, - {0x000a, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0002, 0x8801}, - {0x0000, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0003, 0x8801}, - {0x0027, 0x8805}, - {0x0001, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0004, 0x8801}, - {0x0065, 0x8805}, - {0x0001, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0005, 0x8801}, - {0x0003, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0006, 0x8801}, - {0x001c, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0007, 0x8801}, - {0x002a, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x000e, 0x8801}, - {0x0000, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0028, 0x8801}, - {0x002e, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0039, 0x8801}, - {0x0013, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x003b, 0x8801}, - {0x000c, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0035, 0x8801}, - {0x0028, 0x8805}, - {0x0000, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0x0001, 0x8802 } -> 0000: 10 */ - {0x0010, 0x8802}, - {0x0009, 0x8801}, - {0x0042, 0x8805}, - {0x0001, 0x8800}, - /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - - {0x0050, 0x8703}, - {0x0002, 0x8704}, /* External input CKIx1 */ - {0x0001, 0x870c}, /* Select CKOx2 output */ - {0x009a, 0x8600}, /* Line memory Read Counter (L) */ - {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ - {0x0023, 0x8601}, - {0x0010, 0x8602}, - {0x000a, 0x8603}, - {0x009a, 0x8600}, - {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ - {0x0003, 0x865c}, /* Vertical offset for valid lines (L) */ - {0x0058, 0x865d}, /* Horizontal valid pixels window (L) */ - {0x0048, 0x865e}, /* Vertical valid lines window (L) */ - {0x0000, 0x865f}, - - {0x0006, 0x8660}, - /* Enable nibble data input, select nibble input order */ - - {0x0013, 0x8608}, /* A11 Coeficients for color correction */ - {0x0028, 0x8609}, - /* Note: these values are confirmed at the end of array */ - {0x0005, 0x860a}, /* ... */ - {0x0025, 0x860b}, - {0x00e1, 0x860c}, - {0x00fa, 0x860d}, - {0x00f4, 0x860e}, - {0x00e8, 0x860f}, - {0x0025, 0x8610}, /* A33 Coef. */ - {0x00fc, 0x8611}, /* White balance offset: R */ - {0x0001, 0x8612}, /* White balance offset: Gr */ - {0x00fe, 0x8613}, /* White balance offset: B */ - {0x0000, 0x8614}, /* White balance offset: Gb */ - - {0x0064, 0x8651}, /* R gain for white balance (L) */ - {0x0040, 0x8652}, /* Gr gain for white balance (L) */ - {0x0066, 0x8653}, /* B gain for white balance (L) */ - {0x0040, 0x8654}, /* Gb gain for white balance (L) */ - {0x0001, 0x863f}, /* Enable fixed gamma correction */ - - {0x00a1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128, - * UV division: UV no change, - * Enable New edge enhancement */ - {0x0018, 0x8657}, /* Edge gain high threshold */ - {0x0020, 0x8658}, /* Edge gain low threshold */ - {0x000a, 0x8659}, /* Edge bandwidth high threshold */ - {0x0005, 0x865a}, /* Edge bandwidth low threshold */ - {0x0064, 0x8607}, /* UV filter enable */ - - {0x0016, 0x8660}, - {0x0000, 0x86b0}, /* Bad pixels compensation address */ - {0x00dc, 0x86b1}, /* X coord for bad pixels compensation (L) */ - {0x0000, 0x86b2}, - {0x0009, 0x86b3}, /* Y coord for bad pixels compensation (L) */ - {0x0000, 0x86b4}, - - {0x0001, 0x86b0}, - {0x00f5, 0x86b1}, - {0x0000, 0x86b2}, - {0x00c6, 0x86b3}, - {0x0000, 0x86b4}, - - {0x0002, 0x86b0}, - {0x001c, 0x86b1}, - {0x0001, 0x86b2}, - {0x00d7, 0x86b3}, - {0x0000, 0x86b4}, - - {0x0003, 0x86b0}, - {0x001c, 0x86b1}, - {0x0001, 0x86b2}, - {0x00d8, 0x86b3}, - {0x0000, 0x86b4}, - - {0x0004, 0x86b0}, - {0x001d, 0x86b1}, - {0x0001, 0x86b2}, - {0x00d8, 0x86b3}, - {0x0000, 0x86b4}, - {0x001e, 0x8660}, - - /* READ { 0x0000, 0x8608 } -> 0000: 13 */ - /* READ { 0x0000, 0x8609 } -> 0000: 28 */ - /* READ { 0x0000, 0x8610 } -> 0000: 05 */ - /* READ { 0x0000, 0x8611 } -> 0000: 25 */ - /* READ { 0x0000, 0x8612 } -> 0000: e1 */ - /* READ { 0x0000, 0x8613 } -> 0000: fa */ - /* READ { 0x0000, 0x8614 } -> 0000: f4 */ - /* READ { 0x0000, 0x8615 } -> 0000: e8 */ - /* READ { 0x0000, 0x8616 } -> 0000: 25 */ - {} -}; - -static int reg_write(struct usb_device *dev, - u16 index, u16 value) -{ - int ret; - - ret = usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - 0, /* request */ - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x", - index, value); - if (ret < 0) - pr_err("reg write: error %d\n", ret); - return ret; -} - -/* read 1 byte */ -/* returns: negative is error, pos or zero is data */ -static int reg_read(struct gspca_dev *gspca_dev, - u16 index) /* wIndex */ -{ - int ret; - - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, /* register */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, - gspca_dev->usb_buf, 1, - 500); /* timeout */ - PDEBUG(D_USBI, "reg read i:%04x --> %02x", - index, gspca_dev->usb_buf[0]); - if (ret < 0) { - pr_err("reg_read err %d\n", ret); - return ret; - } - return gspca_dev->usb_buf[0]; -} - -/* send 1 or 2 bytes to the sensor via the Synchronous Serial Interface */ -static int ssi_w(struct gspca_dev *gspca_dev, - u16 reg, u16 val) -{ - struct usb_device *dev = gspca_dev->dev; - int ret, retry; - - ret = reg_write(dev, 0x8802, reg >> 8); - if (ret < 0) - goto out; - ret = reg_write(dev, 0x8801, reg & 0x00ff); - if (ret < 0) - goto out; - if ((reg & 0xff00) == 0x1000) { /* if 2 bytes */ - ret = reg_write(dev, 0x8805, val & 0x00ff); - if (ret < 0) - goto out; - val >>= 8; - } - ret = reg_write(dev, 0x8800, val); - if (ret < 0) - goto out; - - /* poll until not busy */ - retry = 10; - for (;;) { - ret = reg_read(gspca_dev, 0x8803); - if (ret < 0) - break; - if (gspca_dev->usb_buf[0] == 0) - break; - if (--retry <= 0) { - PDEBUG(D_ERR, "ssi_w busy %02x", - gspca_dev->usb_buf[0]); - ret = -1; - break; - } - msleep(8); - } - -out: - return ret; -} - -static int write_vector(struct gspca_dev *gspca_dev, - const u16 (*data)[2]) -{ - struct usb_device *dev = gspca_dev->dev; - int ret = 0; - - while ((*data)[1] != 0) { - if ((*data)[1] & 0x8000) { - if ((*data)[1] == 0xdd00) /* delay */ - msleep((*data)[0]); - else - ret = reg_write(dev, (*data)[1], (*data)[0]); - } else { - ret = ssi_w(gspca_dev, (*data)[1], (*data)[0]); - } - if (ret < 0) - break; - data++; - } - return ret; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - const u16 (*init_data)[2]; - static const u16 (*(init_data_tb[]))[2] = { - spca508_vista_init_data, /* CreativeVista 0 */ - spca508_sightcam_init_data, /* HamaUSBSightcam 1 */ - spca508_sightcam2_init_data, /* HamaUSBSightcam2 2 */ - spca508cs110_init_data, /* IntelEasyPCCamera 3 */ - spca508cs110_init_data, /* MicroInnovationIC200 4 */ - spca508_init_data, /* ViewQuestVQ110 5 */ - }; - -#ifdef GSPCA_DEBUG - int data1, data2; - - /* Read from global register the USB product and vendor IDs, just to - * prove that we can communicate with the device. This works, which - * confirms at we are communicating properly and that the device - * is a 508. */ - data1 = reg_read(gspca_dev, 0x8104); - data2 = reg_read(gspca_dev, 0x8105); - PDEBUG(D_PROBE, "Webcam Vendor ID: 0x%02x%02x", data2, data1); - - data1 = reg_read(gspca_dev, 0x8106); - data2 = reg_read(gspca_dev, 0x8107); - PDEBUG(D_PROBE, "Webcam Product ID: 0x%02x%02x", data2, data1); - - data1 = reg_read(gspca_dev, 0x8621); - PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1); -#endif - - cam = &gspca_dev->cam; - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); - - sd->subtype = id->driver_info; - - init_data = init_data_tb[sd->subtype]; - return write_vector(gspca_dev, init_data); -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - int mode; - - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - reg_write(gspca_dev->dev, 0x8500, mode); - switch (mode) { - case 0: - case 1: - reg_write(gspca_dev->dev, 0x8700, 0x28); /* clock */ - break; - default: -/* case 2: */ -/* case 3: */ - reg_write(gspca_dev->dev, 0x8700, 0x23); /* clock */ - break; - } - reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20); - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - /* Video ISO disable, Video Drop Packet enable: */ - reg_write(gspca_dev->dev, 0x8112, 0x20); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - switch (data[0]) { - case 0: /* start of frame */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - data += SPCA508_OFFSET_DATA; - len -= SPCA508_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - break; - case 0xff: /* drop */ - break; - default: - data += 1; - len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - break; - } -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) -{ - /* MX seem contrast */ - reg_write(gspca_dev->dev, 0x8651, brightness); - reg_write(gspca_dev->dev, 0x8652, brightness); - reg_write(gspca_dev->dev, 0x8653, brightness); - reg_write(gspca_dev->dev, 0x8654, brightness); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0130, 0x0130), .driver_info = HamaUSBSightcam}, - {USB_DEVICE(0x041e, 0x4018), .driver_info = CreativeVista}, - {USB_DEVICE(0x0733, 0x0110), .driver_info = ViewQuestVQ110}, - {USB_DEVICE(0x0af9, 0x0010), .driver_info = HamaUSBSightcam}, - {USB_DEVICE(0x0af9, 0x0011), .driver_info = HamaUSBSightcam2}, - {USB_DEVICE(0x8086, 0x0110), .driver_info = IntelEasyPCCamera}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c deleted file mode 100644 index cfe71dd6747d..000000000000 --- a/drivers/media/video/gspca/spca561.c +++ /dev/null @@ -1,936 +0,0 @@ -/* - * Sunplus spca561 subdriver - * - * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "spca561" - -#include -#include "gspca.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -#define EXPOSURE_MAX (2047 + 325) - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct { /* hue/contrast control cluster */ - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *hue; - }; - struct v4l2_ctrl *autogain; - -#define EXPO12A_DEF 3 - __u8 expo12a; /* expo/gain? for rev 12a */ - - __u8 chip_revision; -#define Rev012A 0 -#define Rev072A 1 - - signed char ag_cnt; -#define AG_CNT_START 13 -}; - -static const struct v4l2_pix_format sif_012a_mode[] = { - {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 4 / 8, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 4 / 8, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -static const struct v4l2_pix_format sif_072a_mode[] = { - {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/* - * Initialization data - * I'm not very sure how to split initialization from open data - * chunks. For now, we'll consider everything as initialization - */ -/* Frame packet header offsets for the spca561 */ -#define SPCA561_OFFSET_SNAP 1 -#define SPCA561_OFFSET_TYPE 2 -#define SPCA561_OFFSET_COMPRESS 3 -#define SPCA561_OFFSET_FRAMSEQ 4 -#define SPCA561_OFFSET_GPIO 5 -#define SPCA561_OFFSET_USBBUFF 6 -#define SPCA561_OFFSET_WIN2GRAVE 7 -#define SPCA561_OFFSET_WIN2RAVE 8 -#define SPCA561_OFFSET_WIN2BAVE 9 -#define SPCA561_OFFSET_WIN2GBAVE 10 -#define SPCA561_OFFSET_WIN1GRAVE 11 -#define SPCA561_OFFSET_WIN1RAVE 12 -#define SPCA561_OFFSET_WIN1BAVE 13 -#define SPCA561_OFFSET_WIN1GBAVE 14 -#define SPCA561_OFFSET_FREQ 15 -#define SPCA561_OFFSET_VSYNC 16 -#define SPCA561_INDEX_I2C_BASE 0x8800 -#define SPCA561_SNAPBIT 0x20 -#define SPCA561_SNAPCTRL 0x40 - -static const u16 rev72a_reset[][2] = { - {0x0000, 0x8114}, /* Software GPIO output data */ - {0x0001, 0x8114}, /* Software GPIO output data */ - {0x0000, 0x8112}, /* Some kind of reset */ - {} -}; -static const __u16 rev72a_init_data1[][2] = { - {0x0003, 0x8701}, /* PCLK clock delay adjustment */ - {0x0001, 0x8703}, /* HSYNC from cmos inverted */ - {0x0011, 0x8118}, /* Enable and conf sensor */ - {0x0001, 0x8118}, /* Conf sensor */ - {0x0092, 0x8804}, /* I know nothing about these */ - {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ - {} -}; -static const u16 rev72a_init_sensor1[][2] = { - {0x0001, 0x000d}, - {0x0002, 0x0018}, - {0x0004, 0x0165}, - {0x0005, 0x0021}, - {0x0007, 0x00aa}, - {0x0020, 0x1504}, - {0x0039, 0x0002}, - {0x0035, 0x0010}, - {0x0009, 0x1049}, - {0x0028, 0x000b}, - {0x003b, 0x000f}, - {0x003c, 0x0000}, - {} -}; -static const __u16 rev72a_init_data2[][2] = { - {0x0018, 0x8601}, /* Pixel/line selection for color separation */ - {0x0000, 0x8602}, /* Optical black level for user setting */ - {0x0060, 0x8604}, /* Optical black horizontal offset */ - {0x0002, 0x8605}, /* Optical black vertical offset */ - {0x0000, 0x8603}, /* Non-automatic optical black level */ - {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ - {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */ - {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */ - {0x0090, 0x865e}, /* Vertical valid lines window (x2) */ - {0x00e0, 0x8406}, /* Memory buffer threshold */ - {0x0000, 0x8660}, /* Compensation memory stuff */ - {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ - {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ - {0x0001, 0x8200}, /* OprMode to be executed by hardware */ -/* from ms-win */ - {0x0000, 0x8611}, /* R offset for white balance */ - {0x00fd, 0x8612}, /* Gr offset for white balance */ - {0x0003, 0x8613}, /* B offset for white balance */ - {0x0000, 0x8614}, /* Gb offset for white balance */ -/* from ms-win */ - {0x0035, 0x8651}, /* R gain for white balance */ - {0x0040, 0x8652}, /* Gr gain for white balance */ - {0x005f, 0x8653}, /* B gain for white balance */ - {0x0040, 0x8654}, /* Gb gain for white balance */ - {0x0002, 0x8502}, /* Maximum average bit rate stuff */ - {0x0011, 0x8802}, - - {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ - {0x0081, 0x8702}, /* Master clock output enable */ - - {0x0000, 0x8500}, /* Set image type (352x288 no compression) */ - /* Originally was 0x0010 (352x288 compression) */ - - {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ - {0x0003, 0x865c}, /* Vertical offset for valid lines */ - {} -}; -static const u16 rev72a_init_sensor2[][2] = { - {0x0003, 0x0121}, - {0x0004, 0x0165}, - {0x0005, 0x002f}, /* blanking control column */ - {0x0006, 0x0000}, /* blanking mode row*/ - {0x000a, 0x0002}, - {0x0009, 0x1061}, /* setexposure times && pixel clock - * 0001 0 | 000 0110 0001 */ - {0x0035, 0x0014}, - {} -}; - -/******************** QC Express etch2 stuff ********************/ -static const __u16 Pb100_1map8300[][2] = { - /* reg, value */ - {0x8320, 0x3304}, - - {0x8303, 0x0125}, /* image area */ - {0x8304, 0x0169}, - {0x8328, 0x000b}, - {0x833c, 0x0001}, /*fixme: win:07*/ - - {0x832f, 0x1904}, /*fixme: was 0419*/ - {0x8307, 0x00aa}, - {0x8301, 0x0003}, - {0x8302, 0x000e}, - {} -}; -static const __u16 Pb100_2map8300[][2] = { - /* reg, value */ - {0x8339, 0x0000}, - {0x8307, 0x00aa}, - {} -}; - -static const __u16 spca561_161rev12A_data1[][2] = { - {0x29, 0x8118}, /* Control register (various enable bits) */ - {0x08, 0x8114}, /* GPIO: Led off */ - {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */ - {0x00, 0x8102}, /* white balance - new */ - {0x92, 0x8804}, - {0x04, 0x8802}, /* windows uses 08 */ - {} -}; -static const __u16 spca561_161rev12A_data2[][2] = { - {0x21, 0x8118}, - {0x10, 0x8500}, - {0x07, 0x8601}, - {0x07, 0x8602}, - {0x04, 0x8501}, - - {0x07, 0x8201}, /* windows uses 02 */ - {0x08, 0x8200}, - {0x01, 0x8200}, - - {0x90, 0x8604}, - {0x00, 0x8605}, - {0xb0, 0x8603}, - - /* sensor gains */ - {0x07, 0x8601}, /* white balance - new */ - {0x07, 0x8602}, /* white balance - new */ - {0x00, 0x8610}, /* *red */ - {0x00, 0x8611}, /* 3f *green */ - {0x00, 0x8612}, /* green *blue */ - {0x00, 0x8613}, /* blue *green */ - {0x43, 0x8614}, /* green *red - white balance - was 0x35 */ - {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */ - {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */ - {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */ - - {0x0c, 0x8620}, /* 0c */ - {0xc8, 0x8631}, /* c8 */ - {0xc8, 0x8634}, /* c8 */ - {0x23, 0x8635}, /* 23 */ - {0x1f, 0x8636}, /* 1f */ - {0xdd, 0x8637}, /* dd */ - {0xe1, 0x8638}, /* e1 */ - {0x1d, 0x8639}, /* 1d */ - {0x21, 0x863a}, /* 21 */ - {0xe3, 0x863b}, /* e3 */ - {0xdf, 0x863c}, /* df */ - {0xf0, 0x8505}, - {0x32, 0x850a}, -/* {0x99, 0x8700}, * - white balance - new (removed) */ - /* HDG we used to do this in stop0, making the init state and the state - after a start / stop different, so do this here instead. */ - {0x29, 0x8118}, - {} -}; - -static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) -{ - int ret; - - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); - if (ret < 0) - pr_err("reg write: error %d\n", ret); -} - -static void write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][2]) -{ - struct usb_device *dev = gspca_dev->dev; - int i; - - i = 0; - while (data[i][1] != 0) { - reg_w_val(dev, data[i][1], data[i][0]); - i++; - } -} - -/* read 'len' bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 index, __u16 length) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, length, 500); -} - -/* write 'len' bytes from gspca_dev->usb_buf */ -static void reg_w_buf(struct gspca_dev *gspca_dev, - __u16 index, __u16 len) -{ - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, len, 500); -} - -static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) -{ - int retry = 60; - - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8805, value); - reg_w_val(gspca_dev->dev, 0x8800, value >> 8); - do { - reg_r(gspca_dev, 0x8803, 1); - if (!gspca_dev->usb_buf[0]) - return; - msleep(10); - } while (--retry); -} - -static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) -{ - int retry = 60; - __u8 value; - - reg_w_val(gspca_dev->dev, 0x8804, 0x92); - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01); - do { - reg_r(gspca_dev, 0x8803, 1); - if (!gspca_dev->usb_buf[0]) { - reg_r(gspca_dev, 0x8800, 1); - value = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8805, 1); - return ((int) value << 8) | gspca_dev->usb_buf[0]; - } - msleep(10); - } while (--retry); - return -1; -} - -static void sensor_mapwrite(struct gspca_dev *gspca_dev, - const __u16 (*sensormap)[2]) -{ - while ((*sensormap)[0]) { - gspca_dev->usb_buf[0] = (*sensormap)[1]; - gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8; - reg_w_buf(gspca_dev, (*sensormap)[0], 2); - sensormap++; - } -} - -static void write_sensor_72a(struct gspca_dev *gspca_dev, - const __u16 (*sensor)[2]) -{ - while ((*sensor)[0]) { - i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]); - sensor++; - } -} - -static void init_161rev12A(struct gspca_dev *gspca_dev) -{ - write_vector(gspca_dev, spca561_161rev12A_data1); - sensor_mapwrite(gspca_dev, Pb100_1map8300); -/*fixme: should be in sd_start*/ - write_vector(gspca_dev, spca561_161rev12A_data2); - sensor_mapwrite(gspca_dev, Pb100_2map8300); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - __u16 vendor, product; - __u8 data1, data2; - - /* Read frm global register the USB product and vendor IDs, just to - * prove that we can communicate with the device. This works, which - * confirms at we are communicating properly and that the device - * is a 561. */ - reg_r(gspca_dev, 0x8104, 1); - data1 = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8105, 1); - data2 = gspca_dev->usb_buf[0]; - vendor = (data2 << 8) | data1; - reg_r(gspca_dev, 0x8106, 1); - data1 = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8107, 1); - data2 = gspca_dev->usb_buf[0]; - product = (data2 << 8) | data1; - if (vendor != id->idVendor || product != id->idProduct) { - PDEBUG(D_PROBE, "Bad vendor / product from device"); - return -EINVAL; - } - - cam = &gspca_dev->cam; - cam->needs_full_bandwidth = 1; - - sd->chip_revision = id->driver_info; - if (sd->chip_revision == Rev012A) { - cam->cam_mode = sif_012a_mode; - cam->nmodes = ARRAY_SIZE(sif_012a_mode); - } else { - cam->cam_mode = sif_072a_mode; - cam->nmodes = ARRAY_SIZE(sif_072a_mode); - } - sd->expo12a = EXPO12A_DEF; - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init_12a(struct gspca_dev *gspca_dev) -{ - PDEBUG(D_STREAM, "Chip revision: 012a"); - init_161rev12A(gspca_dev); - return 0; -} -static int sd_init_72a(struct gspca_dev *gspca_dev) -{ - PDEBUG(D_STREAM, "Chip revision: 072a"); - write_vector(gspca_dev, rev72a_reset); - msleep(200); - write_vector(gspca_dev, rev72a_init_data1); - write_sensor_72a(gspca_dev, rev72a_init_sensor1); - write_vector(gspca_dev, rev72a_init_data2); - write_sensor_72a(gspca_dev, rev72a_init_sensor2); - reg_w_val(gspca_dev->dev, 0x8112, 0x30); - return 0; -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - __u16 reg; - - if (sd->chip_revision == Rev012A) - reg = 0x8610; - else - reg = 0x8611; - - reg_w_val(dev, reg + 0, val); /* R */ - reg_w_val(dev, reg + 1, val); /* Gr */ - reg_w_val(dev, reg + 2, val); /* B */ - reg_w_val(dev, reg + 3, val); /* Gb */ -} - -static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - __u8 blue, red; - __u16 reg; - - /* try to emulate MS-win as possible */ - red = 0x20 + white * 3 / 8; - blue = 0x90 - white * 5 / 8; - if (sd->chip_revision == Rev012A) { - reg = 0x8614; - } else { - reg = 0x8651; - red += contrast - 0x20; - blue += contrast - 0x20; - reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */ - reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */ - } - reg_w_val(dev, reg, red); - reg_w_val(dev, reg + 2, blue); -} - -/* rev 12a only */ -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - int i, expo = 0; - - /* Register 0x8309 controls exposure for the spca561, - the basic exposure setting goes from 1-2047, where 1 is completely - dark and 2047 is very bright. It not only influences exposure but - also the framerate (to allow for longer exposure) from 1 - 300 it - only raises the exposure time then from 300 - 600 it halves the - framerate to be able to further raise the exposure time and for every - 300 more it halves the framerate again. This allows for a maximum - exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps). - Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12 - configure a divider for the base framerate which us used at the - exposure setting of 1-300. These bits configure the base framerate - according to the following formula: fps = 60 / (value + 2) */ - - /* We choose to use the high bits setting the fixed framerate divisor - asap, as setting high basic exposure setting without the fixed - divider in combination with high gains makes the cam stop */ - int table[] = { 0, 450, 550, 625, EXPOSURE_MAX }; - - for (i = 0; i < ARRAY_SIZE(table) - 1; i++) { - if (val <= table[i + 1]) { - expo = val - table[i]; - if (i) - expo += 300; - expo |= i << 11; - break; - } - } - - gspca_dev->usb_buf[0] = expo; - gspca_dev->usb_buf[1] = expo >> 8; - reg_w_buf(gspca_dev, 0x8309, 2); -} - -/* rev 12a only */ -static void setgain(struct gspca_dev *gspca_dev, s32 val) -{ - /* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the - sensitivity when set, so 31 + one of them set == 63, and 15 - with both of them set == 63 */ - if (val < 64) - gspca_dev->usb_buf[0] = val; - else if (val < 128) - gspca_dev->usb_buf[0] = (val / 2) | 0x40; - else - gspca_dev->usb_buf[0] = (val / 4) | 0xc0; - - gspca_dev->usb_buf[1] = 0; - reg_w_buf(gspca_dev, 0x8335, 2); -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (val) - sd->ag_cnt = AG_CNT_START; - else - sd->ag_cnt = -1; -} - -static int sd_start_12a(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - int mode; - static const __u8 Reg8391[8] = - {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00}; - - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - if (mode <= 1) { - /* Use compression on 320x240 and above */ - reg_w_val(dev, 0x8500, 0x10 | mode); - } else { - /* I couldn't get the compression to work below 320x240 - * Fortunately at these resolutions the bandwidth - * is sufficient to push raw frames at ~20fps */ - reg_w_val(dev, 0x8500, mode); - } /* -- qq@kuku.eu.org */ - - gspca_dev->usb_buf[0] = 0xaa; - gspca_dev->usb_buf[1] = 0x00; - reg_w_buf(gspca_dev, 0x8307, 2); - /* clock - lower 0x8X values lead to fps > 30 */ - reg_w_val(gspca_dev->dev, 0x8700, 0x8a); - /* 0x8f 0x85 0x27 clock */ - reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); - reg_w_val(gspca_dev->dev, 0x850b, 0x03); - memcpy(gspca_dev->usb_buf, Reg8391, 8); - reg_w_buf(gspca_dev, 0x8391, 8); - reg_w_buf(gspca_dev, 0x8390, 8); - - /* Led ON (bit 3 -> 0 */ - reg_w_val(gspca_dev->dev, 0x8114, 0x00); - return 0; -} -static int sd_start_72a(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - int Clck; - int mode; - - write_vector(gspca_dev, rev72a_reset); - msleep(200); - write_vector(gspca_dev, rev72a_init_data1); - write_sensor_72a(gspca_dev, rev72a_init_sensor1); - - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - switch (mode) { - default: - case 0: - Clck = 0x27; /* ms-win 0x87 */ - break; - case 1: - Clck = 0x25; - break; - case 2: - Clck = 0x22; - break; - case 3: - Clck = 0x21; - break; - } - reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ - reg_w_val(dev, 0x8702, 0x81); - reg_w_val(dev, 0x8500, mode); /* mode */ - write_sensor_72a(gspca_dev, rev72a_init_sensor2); - setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue), - v4l2_ctrl_g_ctrl(sd->contrast)); -/* setbrightness(gspca_dev); * fixme: bad values */ - setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); - reg_w_val(dev, 0x8112, 0x10 | 0x20); - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->chip_revision == Rev012A) { - reg_w_val(gspca_dev->dev, 0x8112, 0x0e); - /* Led Off (bit 3 -> 1 */ - reg_w_val(gspca_dev->dev, 0x8114, 0x08); - } else { - reg_w_val(gspca_dev->dev, 0x8112, 0x20); -/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ - } -} - -static void do_autogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int expotimes; - int pixelclk; - int gainG; - __u8 R, Gr, Gb, B; - int y; - __u8 luma_mean = 110; - __u8 luma_delta = 20; - __u8 spring = 4; - - if (sd->ag_cnt < 0) - return; - if (--sd->ag_cnt >= 0) - return; - sd->ag_cnt = AG_CNT_START; - - switch (sd->chip_revision) { - case Rev072A: - reg_r(gspca_dev, 0x8621, 1); - Gr = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8622, 1); - R = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8623, 1); - B = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8624, 1); - Gb = gspca_dev->usb_buf[0]; - y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; - /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */ - /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */ - /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */ - - if (y < luma_mean - luma_delta || - y > luma_mean + luma_delta) { - expotimes = i2c_read(gspca_dev, 0x09, 0x10); - pixelclk = 0x0800; - expotimes = expotimes & 0x07ff; - /* PDEBUG(D_PACK, - "Exposition Times 0x%03X Clock 0x%04X ", - expotimes,pixelclk); */ - gainG = i2c_read(gspca_dev, 0x35, 0x10); - /* PDEBUG(D_PACK, - "reading Gain register %d", gainG); */ - - expotimes += (luma_mean - y) >> spring; - gainG += (luma_mean - y) / 50; - /* PDEBUG(D_PACK, - "compute expotimes %d gain %d", - expotimes,gainG); */ - - if (gainG > 0x3f) - gainG = 0x3f; - else if (gainG < 3) - gainG = 3; - i2c_write(gspca_dev, gainG, 0x35); - - if (expotimes > 0x0256) - expotimes = 0x0256; - else if (expotimes < 3) - expotimes = 3; - i2c_write(gspca_dev, expotimes | pixelclk, 0x09); - } - break; - } -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - len--; - switch (*data++) { /* sequence number */ - case 0: /* start of frame */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - - /* This should never happen */ - if (len < 2) { - PDEBUG(D_ERR, "Short SOF packet, ignoring"); - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - if (data[0] & 0x20) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - } -#endif - - if (data[1] & 0x10) { - /* compressed bayer */ - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - } else { - /* raw bayer (with a header, which we skip) */ - if (sd->chip_revision == Rev012A) { - data += 20; - len -= 20; - } else { - data += 16; - len -= 16; - } - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - } - return; - case 0xff: /* drop (empty mpackets) */ - return; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - /* hue/contrast control cluster for 72a */ - setwhite(gspca_dev, sd->hue->val, ctrl->val); - break; - case V4L2_CID_HUE: - /* just plain hue control for 12a */ - setwhite(gspca_dev, ctrl->val, 0); - break; - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - setgain(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - setautogain(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls_12a(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 3); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HUE, 1, 0x7f, 1, 0x40); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 255, 1, 63); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -static int sd_init_controls_72a(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20); - sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HUE, 1, 0x7f, 1, 0x40); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20); - sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_cluster(2, &sd->contrast); - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc_12a = { - .name = MODULE_NAME, - .init_controls = sd_init_controls_12a, - .config = sd_config, - .init = sd_init_12a, - .start = sd_start_12a, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .other_input = 1, -#endif -}; -static const struct sd_desc sd_desc_72a = { - .name = MODULE_NAME, - .init_controls = sd_init_controls_72a, - .config = sd_config, - .init = sd_init_72a, - .start = sd_start_72a, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .dq_callback = do_autogain, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .other_input = 1, -#endif -}; -static const struct sd_desc *sd_desc[2] = { - &sd_desc_12a, - &sd_desc_72a -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A}, - {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A}, - {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A}, - {USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A}, - {USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A}, - {USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A}, - {USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A}, - {USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A}, - {USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A}, - {USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A}, - {USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A}, - {USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A}, - {USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A}, - {USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A}, - {USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A}, - {USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, - sd_desc[id->driver_info], - sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c deleted file mode 100644 index a8ac97931ad6..000000000000 --- a/drivers/media/video/gspca/sq905.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * SQ905 subdriver - * - * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * History and Acknowledgments - * - * The original Linux driver for SQ905 based cameras was written by - * Marcell Lengyel and furter developed by many other contributors - * and is available from http://sourceforge.net/projects/sqcam/ - * - * This driver takes advantage of the reverse engineering work done for - * that driver and for libgphoto2 but shares no code with them. - * - * This driver has used as a base the finepix driver and other gspca - * based drivers and may still contain code fragments taken from those - * drivers. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "sq905" - -#include -#include -#include "gspca.h" - -MODULE_AUTHOR("Adam Baker , " - "Theodore Kilgore "); -MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* Default timeouts, in ms */ -#define SQ905_CMD_TIMEOUT 500 -#define SQ905_DATA_TIMEOUT 1000 - -/* Maximum transfer size to use. */ -#define SQ905_MAX_TRANSFER 0x8000 -#define FRAME_HEADER_LEN 64 - -/* The known modes, or registers. These go in the "value" slot. */ - -/* 00 is "none" obviously */ - -#define SQ905_BULK_READ 0x03 /* precedes any bulk read */ -#define SQ905_COMMAND 0x06 /* precedes the command codes below */ -#define SQ905_PING 0x07 /* when reading an "idling" command */ -#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */ - -/* Any non-zero value in the bottom 2 bits of the 2nd byte of - * the ID appears to indicate the camera can do 640*480. If the - * LSB of that byte is set the image is just upside down, otherwise - * it is rotated 180 degrees. */ -#define SQ905_HIRES_MASK 0x00000300 -#define SQ905_ORIENTATION_MASK 0x00000100 - -/* Some command codes. These go in the "index" slot. */ - -#define SQ905_ID 0xf0 /* asks for model string */ -#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */ -#define SQ905_DATA 0x30 /* accesses photo data, not used here */ -#define SQ905_CLEAR 0xa0 /* clear everything */ -#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */ -#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */ -#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */ -/* note that the capture command also controls the output dimensions */ - -/* Structure to hold all of our device specific stuff */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - /* - * Driver stuff - */ - struct work_struct work_struct; - struct workqueue_struct *work_thread; -}; - -static struct v4l2_pix_format sq905_mode[] = { - { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, - { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, - { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0} -}; - -/* - * Send a command to the camera. - */ -static int sq905_command(struct gspca_dev *gspca_dev, u16 index) -{ - int ret; - - gspca_dev->usb_buf[0] = '\0'; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_SYNCH_FRAME, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - SQ905_COMMAND, index, gspca_dev->usb_buf, 1, - SQ905_CMD_TIMEOUT); - if (ret < 0) { - pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); - return ret; - } - - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_SYNCH_FRAME, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - SQ905_PING, 0, gspca_dev->usb_buf, 1, - SQ905_CMD_TIMEOUT); - if (ret < 0) { - pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret); - return ret; - } - - return 0; -} - -/* - * Acknowledge the end of a frame - see warning on sq905_command. - */ -static int sq905_ack_frame(struct gspca_dev *gspca_dev) -{ - int ret; - - gspca_dev->usb_buf[0] = '\0'; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_SYNCH_FRAME, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1, - SQ905_CMD_TIMEOUT); - if (ret < 0) { - pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); - return ret; - } - - return 0; -} - -/* - * request and read a block of data - see warning on sq905_command. - */ -static int -sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) -{ - int ret; - int act_len; - - gspca_dev->usb_buf[0] = '\0'; - if (need_lock) - mutex_lock(&gspca_dev->usb_lock); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_SYNCH_FRAME, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - SQ905_BULK_READ, size, gspca_dev->usb_buf, - 1, SQ905_CMD_TIMEOUT); - if (need_lock) - mutex_unlock(&gspca_dev->usb_lock); - if (ret < 0) { - pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); - return ret; - } - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x81), - data, size, &act_len, SQ905_DATA_TIMEOUT); - - /* successful, it returns 0, otherwise negative */ - if (ret < 0 || act_len != size) { - pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size); - return -EIO; - } - return 0; -} - -/* This function is called as a workqueue function and runs whenever the camera - * is streaming data. Because it is a workqueue function it is allowed to sleep - * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface we take the gspca - * usb_lock when performing USB operations. In practice the only thing we need - * to protect against is the usb_set_interface call that gspca makes during - * stream_off as the camera doesn't provide any controls that the user could try - * to change. - */ -static void sq905_dostream(struct work_struct *work) -{ - struct sd *dev = container_of(work, struct sd, work_struct); - struct gspca_dev *gspca_dev = &dev->gspca_dev; - int bytes_left; /* bytes remaining in current frame. */ - int data_len; /* size to use for the next read. */ - int header_read; /* true if we have already read the frame header. */ - int packet_type; - int frame_sz; - int ret; - u8 *data; - u8 *buffer; - - buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); - if (!buffer) { - pr_err("Couldn't allocate USB buffer\n"); - goto quit_stream; - } - - frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage - + FRAME_HEADER_LEN; - - while (gspca_dev->dev && gspca_dev->streaming) { -#ifdef CONFIG_PM - if (gspca_dev->frozen) - break; -#endif - /* request some data and then read it until we have - * a complete frame. */ - bytes_left = frame_sz; - header_read = 0; - - /* Note we do not check for gspca_dev->streaming here, as - we must finish reading an entire frame, otherwise the - next time we stream we start reading in the middle of a - frame. */ - while (bytes_left > 0 && gspca_dev->dev) { - data_len = bytes_left > SQ905_MAX_TRANSFER ? - SQ905_MAX_TRANSFER : bytes_left; - ret = sq905_read_data(gspca_dev, buffer, data_len, 1); - if (ret < 0) - goto quit_stream; - PDEBUG(D_PACK, - "Got %d bytes out of %d for frame", - data_len, bytes_left); - bytes_left -= data_len; - data = buffer; - if (!header_read) { - packet_type = FIRST_PACKET; - /* The first 64 bytes of each frame are - * a header full of FF 00 bytes */ - data += FRAME_HEADER_LEN; - data_len -= FRAME_HEADER_LEN; - header_read = 1; - } else if (bytes_left == 0) { - packet_type = LAST_PACKET; - } else { - packet_type = INTER_PACKET; - } - gspca_frame_add(gspca_dev, packet_type, - data, data_len); - /* If entire frame fits in one packet we still - need to add a LAST_PACKET */ - if (packet_type == FIRST_PACKET && - bytes_left == 0) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - } - if (gspca_dev->dev) { - /* acknowledge the frame */ - mutex_lock(&gspca_dev->usb_lock); - ret = sq905_ack_frame(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - if (ret < 0) - goto quit_stream; - } - } -quit_stream: - if (gspca_dev->dev) { - mutex_lock(&gspca_dev->usb_lock); - sq905_command(gspca_dev, SQ905_CLEAR); - mutex_unlock(&gspca_dev->usb_lock); - } - kfree(buffer); -} - -/* This function is called at probe time just before sd_init */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam = &gspca_dev->cam; - struct sd *dev = (struct sd *) gspca_dev; - - /* We don't use the buffer gspca allocates so make it small. */ - cam->bulk = 1; - cam->bulk_size = 64; - - INIT_WORK(&dev->work_struct, sq905_dostream); - - return 0; -} - -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *dev = (struct sd *) gspca_dev; - - /* wait for the work queue to terminate */ - mutex_unlock(&gspca_dev->usb_lock); - /* This waits for sq905_dostream to finish */ - destroy_workqueue(dev->work_thread); - dev->work_thread = NULL; - mutex_lock(&gspca_dev->usb_lock); -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - u32 ident; - int ret; - - /* connect to the camera and read - * the model ID and process that and put it away. - */ - ret = sq905_command(gspca_dev, SQ905_CLEAR); - if (ret < 0) - return ret; - ret = sq905_command(gspca_dev, SQ905_ID); - if (ret < 0) - return ret; - ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0); - if (ret < 0) - return ret; - /* usb_buf is allocated with kmalloc so is aligned. - * Camera model number is the right way round if we assume this - * reverse engineered ID is supposed to be big endian. */ - ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf); - ret = sq905_command(gspca_dev, SQ905_CLEAR); - if (ret < 0) - return ret; - PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident); - gspca_dev->cam.cam_mode = sq905_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode); - if (!(ident & SQ905_HIRES_MASK)) - gspca_dev->cam.nmodes--; - - if (ident & SQ905_ORIENTATION_MASK) - gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP; - else - gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP | - V4L2_IN_ST_HFLIP; - return 0; -} - -/* Set up for getting frames. */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *dev = (struct sd *) gspca_dev; - int ret; - - /* "Open the shutter" and set size, to start capture */ - switch (gspca_dev->curr_mode) { - default: -/* case 2: */ - PDEBUG(D_STREAM, "Start streaming at high resolution"); - ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH); - break; - case 1: - PDEBUG(D_STREAM, "Start streaming at medium resolution"); - ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED); - break; - case 0: - PDEBUG(D_STREAM, "Start streaming at low resolution"); - ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW); - } - - if (ret < 0) { - PDEBUG(D_ERR, "Start streaming command failed"); - return ret; - } - /* Start the workqueue function to do the streaming */ - dev->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(dev->work_thread, &dev->work_struct); - - return 0; -} - -/* Table of supported USB devices */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x2770, 0x9120)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stop0 = sd_stop0, -}; - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, - &sd_desc, - sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c deleted file mode 100644 index 70fae6982e96..000000000000 --- a/drivers/media/video/gspca/sq905c.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * SQ905C subdriver - * - * Copyright (C) 2009 Theodore Kilgore - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * - * This driver uses work done in - * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore. - * - * This driver has also used as a base the sq905c driver - * and may contain code fragments from it. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "sq905c" - -#include -#include -#include "gspca.h" - -MODULE_AUTHOR("Theodore Kilgore "); -MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* Default timeouts, in ms */ -#define SQ905C_CMD_TIMEOUT 500 -#define SQ905C_DATA_TIMEOUT 1000 - -/* Maximum transfer size to use. */ -#define SQ905C_MAX_TRANSFER 0x8000 - -#define FRAME_HEADER_LEN 0x50 - -/* Commands. These go in the "value" slot. */ -#define SQ905C_CLEAR 0xa0 /* clear everything */ -#define SQ905C_GET_ID 0x14f4 /* Read version number */ -#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */ -#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */ -#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */ - -/* For capture, this must go in the "index" slot. */ -#define SQ905C_CAPTURE_INDEX 0x110f - -/* Structure to hold all of our device specific stuff */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - const struct v4l2_pix_format *cap_mode; - /* Driver stuff */ - struct work_struct work_struct; - struct workqueue_struct *work_thread; -}; - -/* - * Most of these cameras will do 640x480 and 320x240. 160x120 works - * in theory but gives very poor output. Therefore, not supported. - * The 0x2770:0x9050 cameras have max resolution of 320x240. - */ -static struct v4l2_pix_format sq905c_mode[] = { - { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, - { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0} -}; - -/* Send a command to the camera. */ -static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index) -{ - int ret; - - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_SYNCH_FRAME, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - command, index, NULL, 0, - SQ905C_CMD_TIMEOUT); - if (ret < 0) { - pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); - return ret; - } - - return 0; -} - -static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index, - int size) -{ - int ret; - - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - USB_REQ_SYNCH_FRAME, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - command, index, gspca_dev->usb_buf, size, - SQ905C_CMD_TIMEOUT); - if (ret < 0) { - pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); - return ret; - } - - return 0; -} - -/* This function is called as a workqueue function and runs whenever the camera - * is streaming data. Because it is a workqueue function it is allowed to sleep - * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface the gspca usb_lock is - * used when performing the one USB control operation inside the workqueue, - * which tells the camera to close the stream. In practice the only thing - * which needs to be protected against is the usb_set_interface call that - * gspca makes during stream_off. Otherwise the camera doesn't provide any - * controls that the user could try to change. - */ -static void sq905c_dostream(struct work_struct *work) -{ - struct sd *dev = container_of(work, struct sd, work_struct); - struct gspca_dev *gspca_dev = &dev->gspca_dev; - int bytes_left; /* bytes remaining in current frame. */ - int data_len; /* size to use for the next read. */ - int act_len; - int packet_type; - int ret; - u8 *buffer; - - buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); - if (!buffer) { - pr_err("Couldn't allocate USB buffer\n"); - goto quit_stream; - } - - while (gspca_dev->dev && gspca_dev->streaming) { -#ifdef CONFIG_PM - if (gspca_dev->frozen) - break; -#endif - /* Request the header, which tells the size to download */ - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x81), - buffer, FRAME_HEADER_LEN, &act_len, - SQ905C_DATA_TIMEOUT); - PDEBUG(D_STREAM, - "Got %d bytes out of %d for header", - act_len, FRAME_HEADER_LEN); - if (ret < 0 || act_len < FRAME_HEADER_LEN) - goto quit_stream; - /* size is read from 4 bytes starting 0x40, little endian */ - bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16) - |(buffer[0x43]<<24); - PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left); - /* We keep the header. It has other information, too. */ - packet_type = FIRST_PACKET; - gspca_frame_add(gspca_dev, packet_type, - buffer, FRAME_HEADER_LEN); - while (bytes_left > 0 && gspca_dev->dev) { - data_len = bytes_left > SQ905C_MAX_TRANSFER ? - SQ905C_MAX_TRANSFER : bytes_left; - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x81), - buffer, data_len, &act_len, - SQ905C_DATA_TIMEOUT); - if (ret < 0 || act_len < data_len) - goto quit_stream; - PDEBUG(D_STREAM, - "Got %d bytes out of %d for frame", - data_len, bytes_left); - bytes_left -= data_len; - if (bytes_left == 0) - packet_type = LAST_PACKET; - else - packet_type = INTER_PACKET; - gspca_frame_add(gspca_dev, packet_type, - buffer, data_len); - } - } -quit_stream: - if (gspca_dev->dev) { - mutex_lock(&gspca_dev->usb_lock); - sq905c_command(gspca_dev, SQ905C_CLEAR, 0); - mutex_unlock(&gspca_dev->usb_lock); - } - kfree(buffer); -} - -/* This function is called at probe time just before sd_init */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam = &gspca_dev->cam; - struct sd *dev = (struct sd *) gspca_dev; - int ret; - - PDEBUG(D_PROBE, - "SQ9050 camera detected" - " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - - ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0); - if (ret < 0) { - PDEBUG(D_ERR, "Get version command failed"); - return ret; - } - - ret = sq905c_read(gspca_dev, 0xf5, 0, 20); - if (ret < 0) { - PDEBUG(D_ERR, "Reading version command failed"); - return ret; - } - /* Note we leave out the usb id and the manufacturing date */ - PDEBUG(D_PROBE, - "SQ9050 ID string: %02x - %*ph", - gspca_dev->usb_buf[3], 6, gspca_dev->usb_buf + 14); - - cam->cam_mode = sq905c_mode; - cam->nmodes = 2; - if (gspca_dev->usb_buf[15] == 0) - cam->nmodes = 1; - /* We don't use the buffer gspca allocates so make it small. */ - cam->bulk_size = 32; - cam->bulk = 1; - INIT_WORK(&dev->work_struct, sq905c_dostream); - return 0; -} - -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *dev = (struct sd *) gspca_dev; - - /* wait for the work queue to terminate */ - mutex_unlock(&gspca_dev->usb_lock); - /* This waits for sq905c_dostream to finish */ - destroy_workqueue(dev->work_thread); - dev->work_thread = NULL; - mutex_lock(&gspca_dev->usb_lock); -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - int ret; - - /* connect to the camera and reset it. */ - ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0); - return ret; -} - -/* Set up for getting frames. */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *dev = (struct sd *) gspca_dev; - int ret; - - dev->cap_mode = gspca_dev->cam.cam_mode; - /* "Open the shutter" and set size, to start capture */ - switch (gspca_dev->width) { - case 640: - PDEBUG(D_STREAM, "Start streaming at high resolution"); - dev->cap_mode++; - ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI, - SQ905C_CAPTURE_INDEX); - break; - default: /* 320 */ - PDEBUG(D_STREAM, "Start streaming at medium resolution"); - ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED, - SQ905C_CAPTURE_INDEX); - } - - if (ret < 0) { - PDEBUG(D_ERR, "Start streaming command failed"); - return ret; - } - /* Start the workqueue function to do the streaming */ - dev->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(dev->work_thread, &dev->work_struct); - - return 0; -} - -/* Table of supported USB devices */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x2770, 0x905c)}, - {USB_DEVICE(0x2770, 0x9050)}, - {USB_DEVICE(0x2770, 0x9051)}, - {USB_DEVICE(0x2770, 0x9052)}, - {USB_DEVICE(0x2770, 0x913d)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stop0 = sd_stop0, -}; - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, - &sd_desc, - sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c deleted file mode 100644 index 7e8748b31e85..000000000000 --- a/drivers/media/video/gspca/sq930x.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* - * SQ930x subdriver - * - * Copyright (C) 2010 Jean-François Moine - * Copyright (C) 2006 -2008 Gerard Klaver - * Copyright (C) 2007 Sam Revitch - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "sq930x" - -#include "gspca.h" - -MODULE_AUTHOR("Jean-Francois Moine \n" - "Gerard Klaver "); -MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* Structure to hold all of our device specific stuff */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct { /* exposure/gain control cluster */ - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; - }; - - u8 do_ctrl; - u8 gpio[2]; - u8 sensor; - u8 type; -#define Generic 0 -#define Creative_live_motion 1 -}; -enum sensors { - SENSOR_ICX098BQ, - SENSOR_LZ24BP, - SENSOR_MI0360, - SENSOR_MT9V111, /* = MI360SOC */ - SENSOR_OV7660, - SENSOR_OV9630, -}; - -static struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, - {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, -}; - -/* sq930x registers */ -#define SQ930_CTRL_UCBUS_IO 0x0001 -#define SQ930_CTRL_I2C_IO 0x0002 -#define SQ930_CTRL_GPIO 0x0005 -#define SQ930_CTRL_CAP_START 0x0010 -#define SQ930_CTRL_CAP_STOP 0x0011 -#define SQ930_CTRL_SET_EXPOSURE 0x001d -#define SQ930_CTRL_RESET 0x001e -#define SQ930_CTRL_GET_DEV_INFO 0x001f - -/* gpio 1 (8..15) */ -#define SQ930_GPIO_DFL_I2C_SDA 0x0001 -#define SQ930_GPIO_DFL_I2C_SCL 0x0002 -#define SQ930_GPIO_RSTBAR 0x0004 -#define SQ930_GPIO_EXTRA1 0x0040 -#define SQ930_GPIO_EXTRA2 0x0080 -/* gpio 3 (24..31) */ -#define SQ930_GPIO_POWER 0x0200 -#define SQ930_GPIO_DFL_LED 0x1000 - -struct ucbus_write_cmd { - u16 bw_addr; - u8 bw_data; -}; -struct i2c_write_cmd { - u8 reg; - u16 val; -}; - -static const struct ucbus_write_cmd icx098bq_start_0[] = { - {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce}, - {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e}, - {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02}, - {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02}, - {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00}, - {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04}, - {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00}, - {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48}, - {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, - {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, - {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff}, - {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, - {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00}, - {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, - {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, - {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c}, - {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30}, - {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30}, - {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc}, - {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, - {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00}, - {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00}, - {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa}, - {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa}, - {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, - {0xf800, 0x03} -}; -static const struct ucbus_write_cmd icx098bq_start_1[] = { - {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xc0}, - {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xc0}, - {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, - {0xf5f9, 0x00} -}; - -static const struct ucbus_write_cmd icx098bq_start_2[] = { - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00}, - {0xf807, 0x7f}, {0xf800, 0x03}, - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00}, - {0xf807, 0x7f}, {0xf800, 0x03}, - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0}, - {0xf807, 0x7f}, {0xf800, 0x03}, - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, - {0xf807, 0x7f}, {0xf800, 0x03} -}; - -static const struct ucbus_write_cmd lz24bp_start_0[] = { - {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe}, - {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06}, - {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02}, - {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00}, - {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00}, - {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03}, - {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00}, - {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48}, - {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, - {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, - {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0}, - {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, - {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00}, - {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, - {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, - {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30}, - {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c}, - {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c}, - {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d}, - {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, - {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d}, - {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d}, - {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04}, - {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04}, - {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, - {0xf800, 0x03} -}; -static const struct ucbus_write_cmd lz24bp_start_1_gen[] = { - {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xb3}, - {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xb3}, - {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, - {0xf5f9, 0x00} -}; - -static const struct ucbus_write_cmd lz24bp_start_1_clm[] = { - {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, - {0xf5f4, 0xc0}, - {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, - {0xf5f4, 0xc0}, - {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, - {0xf5f9, 0x00} -}; - -static const struct ucbus_write_cmd lz24bp_start_2[] = { - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00}, - {0xf807, 0x7f}, {0xf800, 0x03}, - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00}, - {0xf807, 0x7f}, {0xf800, 0x03}, - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48}, - {0xf807, 0x7f}, {0xf800, 0x03}, - {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, - {0xf807, 0x7f}, {0xf800, 0x03} -}; - -static const struct ucbus_write_cmd mi0360_start_0[] = { - {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc}, - {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00} -}; -static const struct i2c_write_cmd mi0360_init_23[] = { - {0x30, 0x0040}, /* reserved - def 0x0005 */ - {0x31, 0x0000}, /* reserved - def 0x002a */ - {0x34, 0x0100}, /* reserved - def 0x0100 */ - {0x3d, 0x068f}, /* reserved - def 0x068f */ -}; -static const struct i2c_write_cmd mi0360_init_24[] = { - {0x03, 0x01e5}, /* window height */ - {0x04, 0x0285}, /* window width */ -}; -static const struct i2c_write_cmd mi0360_init_25[] = { - {0x35, 0x0020}, /* global gain */ - {0x2b, 0x0020}, /* green1 gain */ - {0x2c, 0x002a}, /* blue gain */ - {0x2d, 0x0028}, /* red gain */ - {0x2e, 0x0020}, /* green2 gain */ -}; -static const struct ucbus_write_cmd mi0360_start_1[] = { - {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xa6}, - {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xa6}, - {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, - {0xf5f9, 0x00} -}; -static const struct i2c_write_cmd mi0360_start_2[] = { - {0x62, 0x041d}, /* reserved - def 0x0418 */ -}; -static const struct i2c_write_cmd mi0360_start_3[] = { - {0x05, 0x007b}, /* horiz blanking */ -}; -static const struct i2c_write_cmd mi0360_start_4[] = { - {0x05, 0x03f5}, /* horiz blanking */ -}; - -static const struct i2c_write_cmd mt9v111_init_0[] = { - {0x01, 0x0001}, /* select IFP/SOC registers */ - {0x06, 0x300c}, /* operating mode control */ - {0x08, 0xcc00}, /* output format control (RGB) */ - {0x01, 0x0004}, /* select sensor core registers */ -}; -static const struct i2c_write_cmd mt9v111_init_1[] = { - {0x03, 0x01e5}, /* window height */ - {0x04, 0x0285}, /* window width */ -}; -static const struct i2c_write_cmd mt9v111_init_2[] = { - {0x30, 0x7800}, - {0x31, 0x0000}, - {0x07, 0x3002}, /* output control */ - {0x35, 0x0020}, /* global gain */ - {0x2b, 0x0020}, /* green1 gain */ - {0x2c, 0x0020}, /* blue gain */ - {0x2d, 0x0020}, /* red gain */ - {0x2e, 0x0020}, /* green2 gain */ -}; -static const struct ucbus_write_cmd mt9v111_start_1[] = { - {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xaa}, - {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, - {0xf5f4, 0xaa}, - {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a}, - {0xf5f9, 0x0a} -}; -static const struct i2c_write_cmd mt9v111_init_3[] = { - {0x62, 0x0405}, -}; -static const struct i2c_write_cmd mt9v111_init_4[] = { -/* {0x05, 0x00ce}, */ - {0x05, 0x005d}, /* horizontal blanking */ -}; - -static const struct ucbus_write_cmd ov7660_start_0[] = { - {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0}, - {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03} -}; - -static const struct ucbus_write_cmd ov9630_start_0[] = { - {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00}, - {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03} -}; - -/* start parameters indexed by [sensor][mode] */ -static const struct cap_s { - u8 cc_sizeid; - u8 cc_bytes[32]; -} capconfig[4][2] = { - [SENSOR_ICX098BQ] = { - {2, /* Bayer 320x240 */ - {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, - 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - {4, /* Bayer 640x480 */ - {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, - 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - }, - [SENSOR_LZ24BP] = { - {2, /* Bayer 320x240 */ - {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, - 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - {4, /* Bayer 640x480 */ - {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, - 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - }, - [SENSOR_MI0360] = { - {2, /* Bayer 320x240 */ - {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, - 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - {4, /* Bayer 640x480 */ - {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, - 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - }, - [SENSOR_MT9V111] = { - {2, /* Bayer 320x240 */ - {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, - 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - {4, /* Bayer 640x480 */ - {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, - 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, - }, -}; - -struct sensor_s { - const char *name; - u8 i2c_addr; - u8 i2c_dum; - u8 gpio[5]; - u8 cmd_len; - const struct ucbus_write_cmd *cmd; -}; - -static const struct sensor_s sensor_tb[] = { - [SENSOR_ICX098BQ] = { - "icx098bp", - 0x00, 0x00, - {0, - SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, - SQ930_GPIO_DFL_I2C_SDA, - 0, - SQ930_GPIO_RSTBAR - }, - 8, icx098bq_start_0 - }, - [SENSOR_LZ24BP] = { - "lz24bp", - 0x00, 0x00, - {0, - SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, - SQ930_GPIO_DFL_I2C_SDA, - 0, - SQ930_GPIO_RSTBAR - }, - 8, lz24bp_start_0 - }, - [SENSOR_MI0360] = { - "mi0360", - 0x5d, 0x80, - {SQ930_GPIO_RSTBAR, - SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, - SQ930_GPIO_DFL_I2C_SDA, - 0, - 0 - }, - 7, mi0360_start_0 - }, - [SENSOR_MT9V111] = { - "mt9v111", - 0x5c, 0x7f, - {SQ930_GPIO_RSTBAR, - SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, - SQ930_GPIO_DFL_I2C_SDA, - 0, - 0 - }, - 7, mi0360_start_0 - }, - [SENSOR_OV7660] = { - "ov7660", - 0x21, 0x00, - {0, - SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, - SQ930_GPIO_DFL_I2C_SDA, - 0, - SQ930_GPIO_RSTBAR - }, - 7, ov7660_start_0 - }, - [SENSOR_OV9630] = { - "ov9630", - 0x30, 0x00, - {0, - SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, - SQ930_GPIO_DFL_I2C_SDA, - 0, - SQ930_GPIO_RSTBAR - }, - 7, ov9630_start_0 - }, -}; - -static void reg_r(struct gspca_dev *gspca_dev, - u16 value, int len) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0x0c, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0, gspca_dev->usb_buf, len, - 500); - if (ret < 0) { - pr_err("reg_r %04x failed %d\n", value, ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x0c, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, - 500); - msleep(30); - if (ret < 0) { - pr_err("reg_w %04x %04x failed %d\n", value, index, ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index, - const u8 *data, int len) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x", - value, index, *data, data[len - 1]); - memcpy(gspca_dev->usb_buf, data, len); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x0c, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, gspca_dev->usb_buf, len, - 1000); - msleep(30); - if (ret < 0) { - pr_err("reg_wb %04x %04x failed %d\n", value, index, ret); - gspca_dev->usb_err = ret; - } -} - -static void i2c_write(struct sd *sd, - const struct i2c_write_cmd *cmd, - int ncmds) -{ - struct gspca_dev *gspca_dev = &sd->gspca_dev; - const struct sensor_s *sensor; - u16 val, idx; - u8 *buf; - int ret; - - if (gspca_dev->usb_err < 0) - return; - - sensor = &sensor_tb[sd->sensor]; - - val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO; - idx = (cmd->val & 0xff00) | cmd->reg; - - buf = gspca_dev->usb_buf; - *buf++ = sensor->i2c_dum; - *buf++ = cmd->val; - - while (--ncmds > 0) { - cmd++; - *buf++ = cmd->reg; - *buf++ = cmd->val >> 8; - *buf++ = sensor->i2c_dum; - *buf++ = cmd->val; - } - - PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x", - val, idx, gspca_dev->usb_buf[0], buf[-1]); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x0c, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - val, idx, - gspca_dev->usb_buf, buf - gspca_dev->usb_buf, - 500); - if (ret < 0) { - pr_err("i2c_write failed %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void ucbus_write(struct gspca_dev *gspca_dev, - const struct ucbus_write_cmd *cmd, - int ncmds, - int batchsize) -{ - u8 *buf; - u16 val, idx; - int len, ret; - - if (gspca_dev->usb_err < 0) - return; - -#ifdef GSPCA_DEBUG - if ((batchsize - 1) * 3 > USB_BUF_SZ) { - pr_err("Bug: usb_buf overflow\n"); - gspca_dev->usb_err = -ENOMEM; - return; - } -#endif - - for (;;) { - len = ncmds; - if (len > batchsize) - len = batchsize; - ncmds -= len; - - val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO; - idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8); - - buf = gspca_dev->usb_buf; - while (--len > 0) { - cmd++; - *buf++ = cmd->bw_addr; - *buf++ = cmd->bw_addr >> 8; - *buf++ = cmd->bw_data; - } - if (buf != gspca_dev->usb_buf) - PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x", - val, idx, - gspca_dev->usb_buf[0], buf[-1]); - else - PDEBUG(D_USBO, "ucbus v: %04x i: %04x", - val, idx); - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x0c, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - val, idx, - gspca_dev->usb_buf, buf - gspca_dev->usb_buf, - 500); - if (ret < 0) { - pr_err("ucbus_write failed %d\n", ret); - gspca_dev->usb_err = ret; - return; - } - msleep(30); - if (ncmds <= 0) - break; - cmd++; - } -} - -static void gpio_set(struct sd *sd, u16 val, u16 mask) -{ - struct gspca_dev *gspca_dev = &sd->gspca_dev; - - if (mask & 0x00ff) { - sd->gpio[0] &= ~mask; - sd->gpio[0] |= val; - reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO, - ~sd->gpio[0] << 8); - } - mask >>= 8; - val >>= 8; - if (mask) { - sd->gpio[1] &= ~mask; - sd->gpio[1] |= val; - reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO, - ~sd->gpio[1] << 8); - } -} - -static void gpio_init(struct sd *sd, - const u8 *gpio) -{ - gpio_set(sd, *gpio++, 0x000f); - gpio_set(sd, *gpio++, 0x000f); - gpio_set(sd, *gpio++, 0x000f); - gpio_set(sd, *gpio++, 0x000f); - gpio_set(sd, *gpio, 0x000f); -} - -static void bridge_init(struct sd *sd) -{ - static const struct ucbus_write_cmd clkfreq_cmd = { - 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */ - }; - - ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1); - - gpio_set(sd, SQ930_GPIO_POWER, 0xff00); -} - -static void cmos_probe(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - const struct sensor_s *sensor; - static const u8 probe_order[] = { -/* SENSOR_LZ24BP, (tested as ccd) */ - SENSOR_OV9630, - SENSOR_MI0360, - SENSOR_OV7660, - SENSOR_MT9V111, - }; - - for (i = 0; i < ARRAY_SIZE(probe_order); i++) { - sensor = &sensor_tb[probe_order[i]]; - ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8); - gpio_init(sd, sensor->gpio); - msleep(100); - reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1); - msleep(100); - if (gspca_dev->usb_buf[0] != 0) - break; - } - if (i >= ARRAY_SIZE(probe_order)) { - pr_err("Unknown sensor\n"); - gspca_dev->usb_err = -EINVAL; - return; - } - sd->sensor = probe_order[i]; - switch (sd->sensor) { - case SENSOR_OV7660: - case SENSOR_OV9630: - pr_err("Sensor %s not yet treated\n", - sensor_tb[sd->sensor].name); - gspca_dev->usb_err = -EINVAL; - break; - } -} - -static void mt9v111_init(struct gspca_dev *gspca_dev) -{ - int i, nwait; - static const u8 cmd_001b[] = { - 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00 - }; - static const u8 cmd_011b[][7] = { - {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00}, - {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00}, - {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00}, - {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00}, - }; - - reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b); - for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) { - reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i], - ARRAY_SIZE(cmd_011b[0])); - msleep(400); - nwait = 20; - for (;;) { - reg_r(gspca_dev, 0x031b, 1); - if (gspca_dev->usb_buf[0] == 0 - || gspca_dev->usb_err != 0) - break; - if (--nwait < 0) { - PDEBUG(D_PROBE, "mt9v111_init timeout"); - gspca_dev->usb_err = -ETIME; - return; - } - msleep(50); - } - } -} - -static void global_init(struct sd *sd, int first_time) -{ - switch (sd->sensor) { - case SENSOR_ICX098BQ: - if (first_time) - ucbus_write(&sd->gspca_dev, - icx098bq_start_0, - 8, 8); - gpio_init(sd, sensor_tb[sd->sensor].gpio); - break; - case SENSOR_LZ24BP: - if (sd->type != Creative_live_motion) - gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff); - else - gpio_set(sd, 0, 0x00ff); - msleep(50); - if (first_time) - ucbus_write(&sd->gspca_dev, - lz24bp_start_0, - 8, 8); - gpio_init(sd, sensor_tb[sd->sensor].gpio); - break; - case SENSOR_MI0360: - if (first_time) - ucbus_write(&sd->gspca_dev, - mi0360_start_0, - ARRAY_SIZE(mi0360_start_0), - 8); - gpio_init(sd, sensor_tb[sd->sensor].gpio); - gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2); - break; - default: -/* case SENSOR_MT9V111: */ - if (first_time) - mt9v111_init(&sd->gspca_dev); - else - gpio_init(sd, sensor_tb[sd->sensor].gpio); - break; - } -} - -static void lz24bp_ppl(struct sd *sd, u16 ppl) -{ - struct ucbus_write_cmd cmds[2] = { - {0xf810, ppl >> 8}, - {0xf811, ppl} - }; - - ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, integclks, intstartclk, frameclks, min_frclk; - const struct sensor_s *sensor; - u16 cmd; - u8 buf[15]; - - integclks = expo; - i = 0; - cmd = SQ930_CTRL_SET_EXPOSURE; - - switch (sd->sensor) { - case SENSOR_ICX098BQ: /* ccd */ - case SENSOR_LZ24BP: - min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f; - if (integclks >= min_frclk) { - intstartclk = 0; - frameclks = integclks; - } else { - intstartclk = min_frclk - integclks; - frameclks = min_frclk; - } - buf[i++] = intstartclk >> 8; - buf[i++] = intstartclk; - buf[i++] = frameclks >> 8; - buf[i++] = frameclks; - buf[i++] = gain; - break; - default: /* cmos */ -/* case SENSOR_MI0360: */ -/* case SENSOR_MT9V111: */ - cmd |= 0x0100; - sensor = &sensor_tb[sd->sensor]; - buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */ - buf[i++] = 0x08; /* 2 * ni2c */ - buf[i++] = 0x09; /* reg = shutter width */ - buf[i++] = integclks >> 8; /* val H */ - buf[i++] = sensor->i2c_dum; - buf[i++] = integclks; /* val L */ - buf[i++] = 0x35; /* reg = global gain */ - buf[i++] = 0x00; /* val H */ - buf[i++] = sensor->i2c_dum; - buf[i++] = 0x80 + gain / 2; /* val L */ - buf[i++] = 0x00; - buf[i++] = 0x00; - buf[i++] = 0x00; - buf[i++] = 0x00; - buf[i++] = 0x83; - break; - } - reg_wb(gspca_dev, cmd, 0, buf, i); -} - -/* This function is called at probe time just before sd_init */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - - sd->sensor = id->driver_info >> 8; - sd->type = id->driver_info; - - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - - cam->bulk = 1; - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */ - -/*fixme: is this needed for icx098bp and mi0360? - if (sd->sensor != SENSOR_LZ24BP) - reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000); - */ - - reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8); - if (gspca_dev->usb_err < 0) - return gspca_dev->usb_err; - -/* it returns: - * 03 00 12 93 0b f6 c9 00 live! ultra - * 03 00 07 93 0b f6 ca 00 live! ultra for notebook - * 03 00 12 93 0b fe c8 00 Trust WB-3500T - * 02 00 06 93 0b fe c8 00 Joy-IT 318S - * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq - * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam - * - * byte - * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit) - * 1: 00 - * 2: 06 / 07 / 12 = mode webcam? firmware?? - * 3: 93 chip = 930b (930b or 930c) - * 4: 0b - * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors) - * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam? - * 7: 00 - */ - PDEBUG(D_PROBE, "info: %*ph", 8, gspca_dev->usb_buf); - - bridge_init(sd); - - if (sd->sensor == SENSOR_MI0360) { - - /* no sensor probe for icam tracer */ - if (gspca_dev->usb_buf[5] == 0xf6) /* if ccd */ - sd->sensor = SENSOR_ICX098BQ; - else - cmos_probe(gspca_dev); - } - if (gspca_dev->usb_err >= 0) { - PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name); - global_init(sd, 1); - } - return gspca_dev->usb_err; -} - -/* send the start/stop commands to the webcam */ -static void send_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - const struct cap_s *cap; - int mode; - - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - cap = &capconfig[sd->sensor][mode]; - reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START, - 0x0a00 | cap->cc_sizeid, - cap->cc_bytes, 32); -} - -static void send_stop(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0); -} - -/* function called at start time before URB creation */ -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */ - sd->do_ctrl = 0; - gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8; - return 0; -} - -/* start the capture */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int mode; - - bridge_init(sd); - global_init(sd, 0); - msleep(100); - - switch (sd->sensor) { - case SENSOR_ICX098BQ: - ucbus_write(gspca_dev, icx098bq_start_0, - ARRAY_SIZE(icx098bq_start_0), - 8); - ucbus_write(gspca_dev, icx098bq_start_1, - ARRAY_SIZE(icx098bq_start_1), - 5); - ucbus_write(gspca_dev, icx098bq_start_2, - ARRAY_SIZE(icx098bq_start_2), - 6); - msleep(50); - - /* 1st start */ - send_start(gspca_dev); - gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); - msleep(70); - reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000); - gpio_set(sd, 0x7f, 0x00ff); - - /* 2nd start */ - send_start(gspca_dev); - gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); - goto out; - case SENSOR_LZ24BP: - ucbus_write(gspca_dev, lz24bp_start_0, - ARRAY_SIZE(lz24bp_start_0), - 8); - if (sd->type != Creative_live_motion) - ucbus_write(gspca_dev, lz24bp_start_1_gen, - ARRAY_SIZE(lz24bp_start_1_gen), - 5); - else - ucbus_write(gspca_dev, lz24bp_start_1_clm, - ARRAY_SIZE(lz24bp_start_1_clm), - 5); - ucbus_write(gspca_dev, lz24bp_start_2, - ARRAY_SIZE(lz24bp_start_2), - 6); - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310); - msleep(10); - break; - case SENSOR_MI0360: - ucbus_write(gspca_dev, mi0360_start_0, - ARRAY_SIZE(mi0360_start_0), - 8); - i2c_write(sd, mi0360_init_23, - ARRAY_SIZE(mi0360_init_23)); - i2c_write(sd, mi0360_init_24, - ARRAY_SIZE(mi0360_init_24)); - i2c_write(sd, mi0360_init_25, - ARRAY_SIZE(mi0360_init_25)); - ucbus_write(gspca_dev, mi0360_start_1, - ARRAY_SIZE(mi0360_start_1), - 5); - i2c_write(sd, mi0360_start_2, - ARRAY_SIZE(mi0360_start_2)); - i2c_write(sd, mi0360_start_3, - ARRAY_SIZE(mi0360_start_3)); - - /* 1st start */ - send_start(gspca_dev); - msleep(60); - send_stop(gspca_dev); - - i2c_write(sd, - mi0360_start_4, ARRAY_SIZE(mi0360_start_4)); - break; - default: -/* case SENSOR_MT9V111: */ - ucbus_write(gspca_dev, mi0360_start_0, - ARRAY_SIZE(mi0360_start_0), - 8); - i2c_write(sd, mt9v111_init_0, - ARRAY_SIZE(mt9v111_init_0)); - i2c_write(sd, mt9v111_init_1, - ARRAY_SIZE(mt9v111_init_1)); - i2c_write(sd, mt9v111_init_2, - ARRAY_SIZE(mt9v111_init_2)); - ucbus_write(gspca_dev, mt9v111_start_1, - ARRAY_SIZE(mt9v111_start_1), - 5); - i2c_write(sd, mt9v111_init_3, - ARRAY_SIZE(mt9v111_init_3)); - i2c_write(sd, mt9v111_init_4, - ARRAY_SIZE(mt9v111_init_4)); - break; - } - - send_start(gspca_dev); -out: - msleep(1000); - - if (sd->sensor == SENSOR_MT9V111) - gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED); - - sd->do_ctrl = 1; /* set the exposure */ - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_MT9V111) - gpio_set(sd, 0, SQ930_GPIO_DFL_LED); - send_stop(gspca_dev); -} - -/* function called when the application gets a new frame */ -/* It sets the exposure if required and restart the bulk transfer. */ -static void sd_dq_callback(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0) - return; - sd->do_ctrl = 0; - - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure), - v4l2_ctrl_g_ctrl(sd->gain)); - - gspca_dev->cam.bulk_nurbs = 1; - ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC); - if (ret < 0) - pr_err("sd_dq_callback() err %d\n", ret); - - /* wait a little time, otherwise the webcam crashes */ - msleep(100); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->do_ctrl) - gspca_dev->cam.bulk_nurbs = 0; - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8); - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *) gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val, sd->gain->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - struct sd *sd = (struct sd *) gspca_dev; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 2); - sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356); - sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 1, 255, 1, 0x8d); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_cluster(2, &sd->exposure); - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_dq_callback, -}; - -/* Table of supported USB devices */ -#define ST(sensor, type) \ - .driver_info = (SENSOR_ ## sensor << 8) \ - | (type) -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)}, - {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)}, - {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)}, - {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)}, - {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)}, - {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c deleted file mode 100644 index 8c0982607f25..000000000000 --- a/drivers/media/video/gspca/stk014.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Syntek DV4000 (STK014) subdriver - * - * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "stk014" - -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Jean-Francois Moine "); -MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); -MODULE_LICENSE("GPL"); - -#define QUALITY 50 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -/* -- read a register -- */ -static u8 reg_r(struct gspca_dev *gspca_dev, - __u16 index) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return 0; - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, - index, - gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - return 0; - } - return gspca_dev->usb_buf[0]; -} - -/* -- write a register -- */ -static void reg_w(struct gspca_dev *gspca_dev, - __u16 index, __u16 value) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - NULL, - 0, - 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* -- get a bulk value (4 bytes) -- */ -static void rcv_val(struct gspca_dev *gspca_dev, - int ads) -{ - struct usb_device *dev = gspca_dev->dev; - int alen, ret; - - reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff); - reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff); - reg_w(gspca_dev, 0x636, ads & 0xff); - reg_w(gspca_dev, 0x637, 0); - reg_w(gspca_dev, 0x638, 4); /* len & 0xff */ - reg_w(gspca_dev, 0x639, 0); /* len >> 8 */ - reg_w(gspca_dev, 0x63a, 0); - reg_w(gspca_dev, 0x63b, 0); - reg_w(gspca_dev, 0x630, 5); - if (gspca_dev->usb_err < 0) - return; - ret = usb_bulk_msg(dev, - usb_rcvbulkpipe(dev, 0x05), - gspca_dev->usb_buf, - 4, /* length */ - &alen, - 500); /* timeout in milliseconds */ - if (ret < 0) { - pr_err("rcv_val err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* -- send a bulk value -- */ -static void snd_val(struct gspca_dev *gspca_dev, - int ads, - unsigned int val) -{ - struct usb_device *dev = gspca_dev->dev; - int alen, ret; - __u8 seq = 0; - - if (ads == 0x003f08) { - reg_r(gspca_dev, 0x0704); - seq = reg_r(gspca_dev, 0x0705); - reg_r(gspca_dev, 0x0650); - reg_w(gspca_dev, 0x654, seq); - } else { - reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff); - } - reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff); - reg_w(gspca_dev, 0x656, ads & 0xff); - reg_w(gspca_dev, 0x657, 0); - reg_w(gspca_dev, 0x658, 0x04); /* size */ - reg_w(gspca_dev, 0x659, 0); - reg_w(gspca_dev, 0x65a, 0); - reg_w(gspca_dev, 0x65b, 0); - reg_w(gspca_dev, 0x650, 5); - if (gspca_dev->usb_err < 0) - return; - gspca_dev->usb_buf[0] = val >> 24; - gspca_dev->usb_buf[1] = val >> 16; - gspca_dev->usb_buf[2] = val >> 8; - gspca_dev->usb_buf[3] = val; - ret = usb_bulk_msg(dev, - usb_sndbulkpipe(dev, 6), - gspca_dev->usb_buf, - 4, - &alen, - 500); /* timeout in milliseconds */ - if (ret < 0) { - pr_err("snd_val err %d\n", ret); - gspca_dev->usb_err = ret; - } else { - if (ads == 0x003f08) { - seq += 4; - seq &= 0x3f; - reg_w(gspca_dev, 0x705, seq); - } - } -} - -/* set a camera parameter */ -static void set_par(struct gspca_dev *gspca_dev, - int parval) -{ - snd_val(gspca_dev, 0x003f08, parval); -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - int parval; - - parval = 0x06000000 /* whiteness */ - + (val << 16); - set_par(gspca_dev, parval); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - int parval; - - parval = 0x07000000 /* contrast */ - + (val << 16); - set_par(gspca_dev, parval); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - int parval; - - parval = 0x08000000 /* saturation */ - + (val << 16); - set_par(gspca_dev, parval); -} - -static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) -{ - set_par(gspca_dev, val == 1 - ? 0x33640000 /* 50 Hz */ - : 0x33780000); /* 60 Hz */ -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - gspca_dev->cam.cam_mode = vga_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - u8 ret; - - /* check if the device responds */ - usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); - ret = reg_r(gspca_dev, 0x0740); - if (gspca_dev->usb_err >= 0) { - if (ret != 0xff) { - pr_err("init reg: 0x%02x\n", ret); - gspca_dev->usb_err = -EIO; - } - } - return gspca_dev->usb_err; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret, value; - - /* create the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x22); /* JPEG 411 */ - jpeg_set_qual(sd->jpeg_hdr, QUALITY); - - /* work on alternate 1 */ - usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); - - set_par(gspca_dev, 0x10000000); - set_par(gspca_dev, 0x00000000); - set_par(gspca_dev, 0x8002e001); - set_par(gspca_dev, 0x14000000); - if (gspca_dev->width > 320) - value = 0x8002e001; /* 640x480 */ - else - value = 0x4001f000; /* 320x240 */ - set_par(gspca_dev, value); - ret = usb_set_interface(gspca_dev->dev, - gspca_dev->iface, - gspca_dev->alt); - if (ret < 0) { - pr_err("set intf %d %d failed\n", - gspca_dev->iface, gspca_dev->alt); - gspca_dev->usb_err = ret; - goto out; - } - reg_r(gspca_dev, 0x0630); - rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ - reg_r(gspca_dev, 0x0650); - snd_val(gspca_dev, 0x000020, 0xffffffff); - reg_w(gspca_dev, 0x0620, 0); - reg_w(gspca_dev, 0x0630, 0); - reg_w(gspca_dev, 0x0640, 0); - reg_w(gspca_dev, 0x0650, 0); - reg_w(gspca_dev, 0x0660, 0); - set_par(gspca_dev, 0x09800000); /* Red ? */ - set_par(gspca_dev, 0x0a800000); /* Green ? */ - set_par(gspca_dev, 0x0b800000); /* Blue ? */ - set_par(gspca_dev, 0x0d030000); /* Gamma ? */ - - /* start the video flow */ - set_par(gspca_dev, 0x01000000); - set_par(gspca_dev, 0x01000000); - if (gspca_dev->usb_err >= 0) - PDEBUG(D_STREAM, "camera started alt: 0x%02x", - gspca_dev->alt); -out: - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - - set_par(gspca_dev, 0x02000000); - set_par(gspca_dev, 0x02000000); - usb_set_interface(dev, gspca_dev->iface, 1); - reg_r(gspca_dev, 0x0630); - rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ - reg_r(gspca_dev, 0x0650); - snd_val(gspca_dev, 0x000020, 0xffffffff); - reg_w(gspca_dev, 0x0620, 0); - reg_w(gspca_dev, 0x0630, 0); - reg_w(gspca_dev, 0x0640, 0); - reg_w(gspca_dev, 0x0650, 0); - reg_w(gspca_dev, 0x0660, 0); - PDEBUG(D_STREAM, "camera stopped"); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - static unsigned char ffd9[] = {0xff, 0xd9}; - - /* a frame starts with: - * - 0xff 0xfe - * - 0x08 0x00 - length (little endian ?!) - * - 4 bytes = size of whole frame (BE - including header) - * - 0x00 0x0c - * - 0xff 0xd8 - * - .. JPEG image with escape sequences (ff 00) - * (without ending - ff d9) - */ - if (data[0] == 0xff && data[1] == 0xfe) { - gspca_frame_add(gspca_dev, LAST_PACKET, - ffd9, 2); - - /* put the JPEG 411 header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - - /* beginning of the frame */ -#define STKHDRSZ 12 - data += STKHDRSZ; - len -= STKHDRSZ; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setlightfreq(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 127); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 127); - v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, - V4L2_CID_POWER_LINE_FREQUENCY_50HZ); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x05e1, 0x0893)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c deleted file mode 100644 index 67605272aaa8..000000000000 --- a/drivers/media/video/gspca/stv0680.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * STV0680 USB Camera Driver - * - * Copyright (C) 2009 Hans de Goede - * - * This module is adapted from the in kernel v4l1 stv680 driver: - * - * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) - * - * Thanks to STMicroelectronics for information on the usb commands, and - * to Steve Miller at STM for his help and encouragement while I was - * writing this driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "stv0680" - -#include "gspca.h" - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("STV0680 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_pix_format mode; - u8 orig_mode; - u8 video_mode; - u8 current_mode; -}; - -static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, - int size) -{ - int ret = -1; - u8 req_type = 0; - unsigned int pipe = 0; - - switch (set) { - case 0: /* 0xc1 */ - req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; - pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); - break; - case 1: /* 0x41 */ - req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; - pipe = usb_sndctrlpipe(gspca_dev->dev, 0); - break; - case 2: /* 0x80 */ - req_type = USB_DIR_IN | USB_RECIP_DEVICE; - pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); - break; - case 3: /* 0x40 */ - req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; - pipe = usb_sndctrlpipe(gspca_dev->dev, 0); - break; - } - - ret = usb_control_msg(gspca_dev->dev, pipe, - req, req_type, - val, 0, gspca_dev->usb_buf, size, 500); - - if ((ret < 0) && (req != 0x0a)) - pr_err("usb_control_msg error %i, request = 0x%x, error = %i\n", - set, req, ret); - - return ret; -} - -static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret) -{ - stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */ - PDEBUG(D_ERR, "last error: %i, command = 0x%x", - gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); - return ret; -} - -static int stv0680_get_video_mode(struct gspca_dev *gspca_dev) -{ - /* Note not sure if this init of usb_buf is really necessary */ - memset(gspca_dev->usb_buf, 0, 8); - gspca_dev->usb_buf[0] = 0x0f; - - if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) { - PDEBUG(D_ERR, "Get_Camera_Mode failed"); - return stv0680_handle_error(gspca_dev, -EIO); - } - - return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ -} - -static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->current_mode == mode) - return 0; - - memset(gspca_dev->usb_buf, 0, 8); - gspca_dev->usb_buf[0] = mode; - - if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) { - PDEBUG(D_ERR, "Set_Camera_Mode failed"); - return stv0680_handle_error(gspca_dev, -EIO); - } - - /* Verify we got what we've asked for */ - if (stv0680_get_video_mode(gspca_dev) != mode) { - PDEBUG(D_ERR, "Error setting camera video mode!"); - return -EIO; - } - - sd->current_mode = mode; - - return 0; -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - int ret; - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - - /* Give the camera some time to settle, otherwise initalization will - fail on hotplug, and yes it really needs a full second. */ - msleep(1000); - - /* ping camera to be sure STV0680 is present */ - if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 || - gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) { - PDEBUG(D_ERR, "STV(e): camera ping failed!!"); - return stv0680_handle_error(gspca_dev, -ENODEV); - } - - /* get camera descriptor */ - if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09) - return stv0680_handle_error(gspca_dev, -ENODEV); - - if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 || - gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) { - PDEBUG(D_ERR, "Could not get descriptor 0200."); - return stv0680_handle_error(gspca_dev, -ENODEV); - } - if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02) - return stv0680_handle_error(gspca_dev, -ENODEV); - if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24) - return stv0680_handle_error(gspca_dev, -ENODEV); - if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) - return stv0680_handle_error(gspca_dev, -ENODEV); - - if (!(gspca_dev->usb_buf[7] & 0x09)) { - PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode"); - return -ENODEV; - } - if (gspca_dev->usb_buf[7] & 0x01) - PDEBUG(D_PROBE, "Camera supports CIF mode"); - if (gspca_dev->usb_buf[7] & 0x02) - PDEBUG(D_PROBE, "Camera supports VGA mode"); - if (gspca_dev->usb_buf[7] & 0x04) - PDEBUG(D_PROBE, "Camera supports QCIF mode"); - if (gspca_dev->usb_buf[7] & 0x08) - PDEBUG(D_PROBE, "Camera supports QVGA mode"); - - if (gspca_dev->usb_buf[7] & 0x01) - sd->video_mode = 0x00; /* CIF */ - else - sd->video_mode = 0x03; /* QVGA */ - - /* FW rev, ASIC rev, sensor ID */ - PDEBUG(D_PROBE, "Firmware rev is %i.%i", - gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); - PDEBUG(D_PROBE, "ASIC rev is %i.%i", - gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]); - PDEBUG(D_PROBE, "Sensor ID is %i", - (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4)); - - - ret = stv0680_get_video_mode(gspca_dev); - if (ret < 0) - return ret; - sd->current_mode = sd->orig_mode = ret; - - ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); - if (ret < 0) - return ret; - - /* Get mode details */ - if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10) - return stv0680_handle_error(gspca_dev, -EIO); - - cam->bulk = 1; - cam->bulk_nurbs = 1; /* The cam cannot handle more */ - cam->bulk_size = (gspca_dev->usb_buf[0] << 24) | - (gspca_dev->usb_buf[1] << 16) | - (gspca_dev->usb_buf[2] << 8) | - (gspca_dev->usb_buf[3]); - sd->mode.width = (gspca_dev->usb_buf[4] << 8) | - (gspca_dev->usb_buf[5]); /* 322, 356, 644 */ - sd->mode.height = (gspca_dev->usb_buf[6] << 8) | - (gspca_dev->usb_buf[7]); /* 242, 292, 484 */ - sd->mode.pixelformat = V4L2_PIX_FMT_STV0680; - sd->mode.field = V4L2_FIELD_NONE; - sd->mode.bytesperline = sd->mode.width; - sd->mode.sizeimage = cam->bulk_size; - sd->mode.colorspace = V4L2_COLORSPACE_SRGB; - - /* origGain = gspca_dev->usb_buf[12]; */ - - cam->cam_mode = &sd->mode; - cam->nmodes = 1; - - - ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode); - if (ret < 0) - return ret; - - if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 || - gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) { - pr_err("Could not get descriptor 0100\n"); - return stv0680_handle_error(gspca_dev, -EIO); - } - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - int ret; - struct sd *sd = (struct sd *) gspca_dev; - - ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); - if (ret < 0) - return ret; - - if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) - return stv0680_handle_error(gspca_dev, -EIO); - - /* Start stream at: - 0x0000 = CIF (352x288) - 0x0100 = VGA (640x480) - 0x0300 = QVGA (320x240) */ - if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0) - return stv0680_handle_error(gspca_dev, -EIO); - - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - /* This is a high priority command; it stops all lower order cmds */ - if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0) - stv0680_handle_error(gspca_dev, -EIO); -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!sd->gspca_dev.present) - return; - - stv0680_set_video_mode(gspca_dev, sd->orig_mode); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, - int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* Every now and then the camera sends a 16 byte packet, no idea - what it contains, but it is not image data, when this - happens the frame received before this packet is corrupt, - so discard it. */ - if (len != sd->mode.sizeimage) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - - /* Finish the previous frame, we do this upon reception of the next - packet, even though it is already complete so that the strange 16 - byte packets send after a corrupt frame can discard it. */ - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - - /* Store the just received frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x0553, 0x0202)}, - {USB_DEVICE(0x041e, 0x4007)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/stv06xx/Kconfig b/drivers/media/video/gspca/stv06xx/Kconfig deleted file mode 100644 index 634ad38d9fb8..000000000000 --- a/drivers/media/video/gspca/stv06xx/Kconfig +++ /dev/null @@ -1,9 +0,0 @@ -config USB_STV06XX - tristate "STV06XX USB Camera Driver" - depends on USB_GSPCA - help - Say Y here if you want support for cameras based on - the ST STV06XX chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_stv06xx. diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile deleted file mode 100644 index 38bc41061d83..000000000000 --- a/drivers/media/video/gspca/stv06xx/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o - -gspca_stv06xx-objs := stv06xx.o \ - stv06xx_vv6410.o \ - stv06xx_hdcs.o \ - stv06xx_pb0100.o \ - stv06xx_st6422.o - -ccflags-y += -I$(srctree)/drivers/media/video/gspca - diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c deleted file mode 100644 index 999ec7764449..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include "stv06xx_sensor.h" - -MODULE_AUTHOR("Erik Andrén"); -MODULE_DESCRIPTION("STV06XX USB Camera Driver"); -MODULE_LICENSE("GPL"); - -static bool dump_bridge; -static bool dump_sensor; - -int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data) -{ - int err; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - u8 len = (i2c_data > 0xff) ? 2 : 1; - - buf[0] = i2c_data & 0xff; - buf[1] = (i2c_data >> 8) & 0xff; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, address, 0, buf, len, - STV06XX_URB_MSG_TIMEOUT); - - PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d", - i2c_data, address, err); - - return (err < 0) ? err : 0; -} - -int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data) -{ - int err; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x04, 0xc0, address, 0, buf, 1, - STV06XX_URB_MSG_TIMEOUT); - - *i2c_data = buf[0]; - - PDEBUG(D_CONF, "Reading 0x%x from address 0x%x, status %d", - *i2c_data, address, err); - - return (err < 0) ? err : 0; -} - -/* Wraps the normal write sensor bytes / words functions for writing a - single value */ -int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value) -{ - if (sd->sensor->i2c_len == 2) { - u16 data[2] = { address, value }; - return stv06xx_write_sensor_words(sd, data, 1); - } else { - u8 data[2] = { address, value }; - return stv06xx_write_sensor_bytes(sd, data, 1); - } -} - -static int stv06xx_write_sensor_finish(struct sd *sd) -{ - int err = 0; - - if (sd->bridge == BRIDGE_STV610) { - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - buf[0] = 0; - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x1704, 0, buf, 1, - STV06XX_URB_MSG_TIMEOUT); - } - - return (err < 0) ? err : 0; -} - -int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) -{ - int err, i, j; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len); - for (i = 0; i < len;) { - /* Build the command buffer */ - memset(buf, 0, I2C_BUFFER_LENGTH); - for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) { - buf[j] = data[2*i]; - buf[0x10 + j] = data[2*i+1]; - PDEBUG(D_CONF, "I2C: Writing 0x%02x to reg 0x%02x", - data[2*i+1], data[2*i]); - } - buf[0x20] = sd->sensor->i2c_addr; - buf[0x21] = j - 1; /* Number of commands to send - 1 */ - buf[0x22] = I2C_WRITE_CMD; - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x0400, 0, buf, - I2C_BUFFER_LENGTH, - STV06XX_URB_MSG_TIMEOUT); - if (err < 0) - return err; - } - return stv06xx_write_sensor_finish(sd); -} - -int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) -{ - int err, i, j; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len); - - for (i = 0; i < len;) { - /* Build the command buffer */ - memset(buf, 0, I2C_BUFFER_LENGTH); - for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) { - buf[j] = data[2*i]; - buf[0x10 + j * 2] = data[2*i+1]; - buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8; - PDEBUG(D_CONF, "I2C: Writing 0x%04x to reg 0x%02x", - data[2*i+1], data[2*i]); - } - buf[0x20] = sd->sensor->i2c_addr; - buf[0x21] = j - 1; /* Number of commands to send - 1 */ - buf[0x22] = I2C_WRITE_CMD; - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x0400, 0, buf, - I2C_BUFFER_LENGTH, - STV06XX_URB_MSG_TIMEOUT); - if (err < 0) - return err; - } - return stv06xx_write_sensor_finish(sd); -} - -int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) -{ - int err; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush); - if (err < 0) - return err; - - /* Clear mem */ - memset(buf, 0, I2C_BUFFER_LENGTH); - - buf[0] = address; - buf[0x20] = sd->sensor->i2c_addr; - buf[0x21] = 0; - - /* Read I2C register */ - buf[0x22] = I2C_READ_CMD; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH, - STV06XX_URB_MSG_TIMEOUT); - if (err < 0) { - pr_err("I2C: Read error writing address: %d\n", err); - return err; - } - - err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len, - STV06XX_URB_MSG_TIMEOUT); - if (sd->sensor->i2c_len == 2) - *value = buf[0] | (buf[1] << 8); - else - *value = buf[0]; - - PDEBUG(D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d", - *value, address, err); - - return (err < 0) ? err : 0; -} - -/* Dumps all bridge registers */ -static void stv06xx_dump_bridge(struct sd *sd) -{ - int i; - u8 data, buf; - - pr_info("Dumping all stv06xx bridge registers\n"); - for (i = 0x1400; i < 0x160f; i++) { - stv06xx_read_bridge(sd, i, &data); - - pr_info("Read 0x%x from address 0x%x\n", data, i); - } - - pr_info("Testing stv06xx bridge registers for writability\n"); - for (i = 0x1400; i < 0x160f; i++) { - stv06xx_read_bridge(sd, i, &data); - buf = data; - - stv06xx_write_bridge(sd, i, 0xff); - stv06xx_read_bridge(sd, i, &data); - if (data == 0xff) - pr_info("Register 0x%x is read/write\n", i); - else if (data != buf) - pr_info("Register 0x%x is read/write, but only partially\n", - i); - else - pr_info("Register 0x%x is read-only\n", i); - - stv06xx_write_bridge(sd, i, buf); - } -} - -/* this function is called at probe and resume time */ -static int stv06xx_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int err; - - PDEBUG(D_PROBE, "Initializing camera"); - - /* Let the usb init settle for a bit - before performing the initialization */ - msleep(250); - - err = sd->sensor->init(sd); - - if (dump_sensor && sd->sensor->dump) - sd->sensor->dump(sd); - - return (err < 0) ? err : 0; -} - -/* this function is called at probe time */ -static int stv06xx_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - PDEBUG(D_PROBE, "Initializing controls"); - - gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler; - return sd->sensor->init_controls(sd); -} - -/* Start the camera */ -static int stv06xx_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_host_interface *alt; - struct usb_interface *intf; - int err, packet_size; - - intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); - alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); - if (!alt) { - PDEBUG(D_ERR, "Couldn't get altsetting"); - return -EIO; - } - - packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size); - if (err < 0) - return err; - - /* Prepare the sensor for start */ - err = sd->sensor->start(sd); - if (err < 0) - goto out; - - /* Start isochronous streaming */ - err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1); - -out: - if (err < 0) - PDEBUG(D_STREAM, "Starting stream failed"); - else - PDEBUG(D_STREAM, "Started streaming"); - - return (err < 0) ? err : 0; -} - -static int stv06xx_isoc_init(struct gspca_dev *gspca_dev) -{ - struct usb_host_interface *alt; - struct sd *sd = (struct sd *) gspca_dev; - - /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ - alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; - alt->endpoint[0].desc.wMaxPacketSize = - cpu_to_le16(sd->sensor->max_packet_size[gspca_dev->curr_mode]); - - return 0; -} - -static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev) -{ - int ret, packet_size, min_packet_size; - struct usb_host_interface *alt; - struct sd *sd = (struct sd *) gspca_dev; - - alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; - packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode]; - if (packet_size <= min_packet_size) - return -EIO; - - packet_size -= 100; - if (packet_size < min_packet_size) - packet_size = min_packet_size; - alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size); - - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); - if (ret < 0) - PDEBUG(D_ERR|D_STREAM, "set alt 1 err %d", ret); - - return ret; -} - -static void stv06xx_stopN(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - /* stop ISO-streaming */ - err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0); - if (err < 0) - goto out; - - err = sd->sensor->stop(sd); - -out: - if (err < 0) - PDEBUG(D_STREAM, "Failed to stop stream"); - else - PDEBUG(D_STREAM, "Stopped streaming"); -} - -/* - * Analyse an USB packet of the data stream and store it appropriately. - * Each packet contains an integral number of chunks. Each chunk has - * 2-bytes identification, followed by 2-bytes that describe the chunk - * length. Known/guessed chunk identifications are: - * 8001/8005/C001/C005 - Begin new frame - * 8002/8006/C002/C006 - End frame - * 0200/4200 - Contains actual image data, bayer or compressed - * 0005 - 11 bytes of unknown data - * 0100 - 2 bytes of unknown data - * The 0005 and 0100 chunks seem to appear only in compressed stream. - */ -static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - PDEBUG(D_PACK, "Packet of length %d arrived", len); - - /* A packet may contain several frames - loop until the whole packet is reached */ - while (len) { - int id, chunk_len; - - if (len < 4) { - PDEBUG(D_PACK, "Packet is smaller than 4 bytes"); - return; - } - - /* Capture the id */ - id = (data[0] << 8) | data[1]; - - /* Capture the chunk length */ - chunk_len = (data[2] << 8) | data[3]; - PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len); - - data += 4; - len -= 4; - - if (len < chunk_len) { - PDEBUG(D_ERR, "URB packet length is smaller" - " than the specified chunk length"); - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - - /* First byte seem to be 02=data 2nd byte is unknown??? */ - if (sd->bridge == BRIDGE_ST6422 && (id & 0xff00) == 0x0200) - goto frame_data; - - switch (id) { - case 0x0200: - case 0x4200: -frame_data: - PDEBUG(D_PACK, "Frame data packet detected"); - - if (sd->to_skip) { - int skip = (sd->to_skip < chunk_len) ? - sd->to_skip : chunk_len; - data += skip; - len -= skip; - chunk_len -= skip; - sd->to_skip -= skip; - } - - gspca_frame_add(gspca_dev, INTER_PACKET, - data, chunk_len); - break; - - case 0x8001: - case 0x8005: - case 0xc001: - case 0xc005: - PDEBUG(D_PACK, "Starting new frame"); - - /* Create a new frame, chunk length should be zero */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - NULL, 0); - - if (sd->bridge == BRIDGE_ST6422) - sd->to_skip = gspca_dev->width * 4; - - if (chunk_len) - PDEBUG(D_ERR, "Chunk length is " - "non-zero on a SOF"); - break; - - case 0x8002: - case 0x8006: - case 0xc002: - PDEBUG(D_PACK, "End of frame detected"); - - /* Complete the last frame (if any) */ - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - - if (chunk_len) - PDEBUG(D_ERR, "Chunk length is " - "non-zero on a EOF"); - break; - - case 0x0005: - PDEBUG(D_PACK, "Chunk 0x005 detected"); - /* Unknown chunk with 11 bytes of data, - occurs just before end of each frame - in compressed mode */ - break; - - case 0x0100: - PDEBUG(D_PACK, "Chunk 0x0100 detected"); - /* Unknown chunk with 2 bytes of data, - occurs 2-3 times per USB interrupt */ - break; - case 0x42ff: - PDEBUG(D_PACK, "Chunk 0x42ff detected"); - /* Special chunk seen sometimes on the ST6422 */ - break; - default: - PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id); - /* Unknown chunk */ - } - data += chunk_len; - len -= chunk_len; - } -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet data */ - int len) /* interrupt packet length */ -{ - int ret = -EINVAL; - - if (len == 1 && data[0] == 0x80) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - ret = 0; - } - - if (len == 1 && data[0] == 0x88) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - ret = 0; - } - - return ret; -} -#endif - -static int stv06xx_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id); - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = stv06xx_config, - .init = stv06xx_init, - .init_controls = stv06xx_init_controls, - .start = stv06xx_start, - .stopN = stv06xx_stopN, - .pkt_scan = stv06xx_pkt_scan, - .isoc_init = stv06xx_isoc_init, - .isoc_nego = stv06xx_isoc_nego, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -/* This function is called at probe time */ -static int stv06xx_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - - PDEBUG(D_PROBE, "Configuring camera"); - - sd->bridge = id->driver_info; - gspca_dev->sd_desc = &sd_desc; - - if (dump_bridge) - stv06xx_dump_bridge(sd); - - sd->sensor = &stv06xx_sensor_st6422; - if (!sd->sensor->probe(sd)) - return 0; - - sd->sensor = &stv06xx_sensor_vv6410; - if (!sd->sensor->probe(sd)) - return 0; - - sd->sensor = &stv06xx_sensor_hdcs1x00; - if (!sd->sensor->probe(sd)) - return 0; - - sd->sensor = &stv06xx_sensor_hdcs1020; - if (!sd->sensor->probe(sd)) - return 0; - - sd->sensor = &stv06xx_sensor_pb0100; - if (!sd->sensor->probe(sd)) - return 0; - - sd->sensor = NULL; - return -ENODEV; -} - - - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - /* QuickCam Express */ - {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 }, - /* LEGO cam / QuickCam Web */ - {USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 }, - /* Dexxa WebCam USB */ - {USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 }, - /* QuickCam Messenger */ - {USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 }, - /* QuickCam Communicate */ - {USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 }, - /* QuickCam Messenger (new) */ - {USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 }, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - PDEBUG(D_PROBE, "Probing for a stv06xx device"); - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static void sd_disconnect(struct usb_interface *intf) -{ - struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - struct sd *sd = (struct sd *) gspca_dev; - void *priv = sd->sensor_priv; - PDEBUG(D_PROBE, "Disconnecting the stv06xx device"); - - sd->sensor = NULL; - gspca_disconnect(intf); - kfree(priv); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = sd_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); - -module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); - -module_param(dump_sensor, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup"); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h deleted file mode 100644 index 34957a4ec150..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#ifndef STV06XX_H_ -#define STV06XX_H_ - -#include -#include "gspca.h" - -#define MODULE_NAME "STV06xx" - -#define STV_ISOC_ENDPOINT_ADDR 0x81 - -#define STV_R 0x0509 - -#define STV_REG23 0x0423 - -/* Control registers of the STV0600 ASIC */ -#define STV_I2C_PARTNER 0x1420 -#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421 -#define STV_I2C_READ_WRITE_TOGGLE 0x1422 -#define STV_I2C_FLUSH 0x1423 -#define STV_I2C_SUCC_READ_REG_VALS 0x1424 - -#define STV_ISO_ENABLE 0x1440 -#define STV_SCAN_RATE 0x1443 -#define STV_LED_CTRL 0x1445 -#define STV_STV0600_EMULATION 0x1446 -#define STV_REG00 0x1500 -#define STV_REG01 0x1501 -#define STV_REG02 0x1502 -#define STV_REG03 0x1503 -#define STV_REG04 0x1504 - -#define STV_ISO_SIZE_L 0x15c1 -#define STV_ISO_SIZE_H 0x15c2 - -/* Refers to the CIF 352x288 and QCIF 176x144 */ -/* 1: 288 lines, 2: 144 lines */ -#define STV_Y_CTRL 0x15c3 - -#define STV_RESET 0x1620 - -/* 0xa: 352 columns, 0x6: 176 columns */ -#define STV_X_CTRL 0x1680 - -#define STV06XX_URB_MSG_TIMEOUT 5000 - -#define I2C_MAX_BYTES 16 -#define I2C_MAX_WORDS 8 - -#define I2C_BUFFER_LENGTH 0x23 -#define I2C_READ_CMD 3 -#define I2C_WRITE_CMD 1 - -#define LED_ON 1 -#define LED_OFF 0 - -/* STV06xx device descriptor */ -struct sd { - struct gspca_dev gspca_dev; - - /* A pointer to the currently connected sensor */ - const struct stv06xx_sensor *sensor; - - /* Sensor private data */ - void *sensor_priv; - - /* The first 4 lines produced by the stv6422 are no good, this keeps - track of how many bytes we still need to skip during a frame */ - int to_skip; - - /* Bridge / Camera type */ - u8 bridge; - #define BRIDGE_STV600 0 - #define BRIDGE_STV602 1 - #define BRIDGE_STV610 2 - #define BRIDGE_ST6422 3 /* With integrated sensor */ -}; - -int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data); -int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data); - -int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len); -int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len); - -int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value); -int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value); - -#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c deleted file mode 100644 index 06fa54c5efb2..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * Copyright (c) 2008 Chia-I Wu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "stv06xx_hdcs.h" - -static struct v4l2_pix_format hdcs1x00_mode[] = { - { - HDCS_1X00_DEF_WIDTH, - HDCS_1X00_DEF_HEIGHT, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = - HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, - .bytesperline = HDCS_1X00_DEF_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } -}; - -static struct v4l2_pix_format hdcs1020_mode[] = { - { - HDCS_1020_DEF_WIDTH, - HDCS_1020_DEF_HEIGHT, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = - HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, - .bytesperline = HDCS_1020_DEF_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } -}; - -enum hdcs_power_state { - HDCS_STATE_SLEEP, - HDCS_STATE_IDLE, - HDCS_STATE_RUN -}; - -/* no lock? */ -struct hdcs { - enum hdcs_power_state state; - int w, h; - - /* visible area of the sensor array */ - struct { - int left, top; - int width, height; - int border; - } array; - - struct { - /* Column timing overhead */ - u8 cto; - /* Column processing overhead */ - u8 cpo; - /* Row sample period constant */ - u16 rs; - /* Exposure reset duration */ - u16 er; - } exp; - - int psmp; -}; - -static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) -{ - u8 regs[I2C_MAX_BYTES * 2]; - int i; - - if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) || - (reg + len > 0xff))) - return -EINVAL; - - for (i = 0; i < len; i++) { - regs[2 * i] = reg; - regs[2 * i + 1] = vals[i]; - /* All addresses are shifted left one bit - * as bit 0 toggles r/w */ - reg += 2; - } - - return stv06xx_write_sensor_bytes(sd, regs, len); -} - -static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) -{ - struct hdcs *hdcs = sd->sensor_priv; - u8 val; - int ret; - - if (hdcs->state == state) - return 0; - - /* we need to go idle before running or sleeping */ - if (hdcs->state != HDCS_STATE_IDLE) { - ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); - if (ret) - return ret; - } - - hdcs->state = HDCS_STATE_IDLE; - - if (state == HDCS_STATE_IDLE) - return 0; - - switch (state) { - case HDCS_STATE_SLEEP: - val = HDCS_SLEEP_MODE; - break; - - case HDCS_STATE_RUN: - val = HDCS_RUN_ENABLE; - break; - - default: - return -EINVAL; - } - - ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); - - /* Update the state if the write succeeded */ - if (!ret) - hdcs->state = state; - - return ret; -} - -static int hdcs_reset(struct sd *sd) -{ - struct hdcs *hdcs = sd->sensor_priv; - int err; - - err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1); - if (err < 0) - return err; - - err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); - if (err < 0) - hdcs->state = HDCS_STATE_IDLE; - - return err; -} - -static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct hdcs *hdcs = sd->sensor_priv; - int rowexp, srowexp; - int max_srowexp; - /* Column time period */ - int ct; - /* Column processing period */ - int cp; - /* Row processing period */ - int rp; - /* Minimum number of column timing periods - within the column processing period */ - int mnct; - int cycles, err; - u8 exp[14]; - - cycles = val * HDCS_CLK_FREQ_MHZ * 257; - - ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); - cp = hdcs->exp.cto + (hdcs->w * ct / 2); - - /* the cycles one row takes */ - rp = hdcs->exp.rs + cp; - - rowexp = cycles / rp; - - /* the remaining cycles */ - cycles -= rowexp * rp; - - /* calculate sub-row exposure */ - if (IS_1020(sd)) { - /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */ - srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct; - - mnct = (hdcs->exp.er + 12 + ct - 1) / ct; - max_srowexp = hdcs->w - mnct; - } else { - /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */ - srowexp = cp - hdcs->exp.er - 6 - cycles; - - mnct = (hdcs->exp.er + 5 + ct - 1) / ct; - max_srowexp = cp - mnct * ct - 1; - } - - if (srowexp < 0) - srowexp = 0; - else if (srowexp > max_srowexp) - srowexp = max_srowexp; - - if (IS_1020(sd)) { - exp[0] = HDCS20_CONTROL; - exp[1] = 0x00; /* Stop streaming */ - exp[2] = HDCS_ROWEXPL; - exp[3] = rowexp & 0xff; - exp[4] = HDCS_ROWEXPH; - exp[5] = rowexp >> 8; - exp[6] = HDCS20_SROWEXP; - exp[7] = (srowexp >> 2) & 0xff; - exp[8] = HDCS20_ERROR; - exp[9] = 0x10; /* Clear exposure error flag*/ - exp[10] = HDCS20_CONTROL; - exp[11] = 0x04; /* Restart streaming */ - err = stv06xx_write_sensor_bytes(sd, exp, 6); - } else { - exp[0] = HDCS00_CONTROL; - exp[1] = 0x00; /* Stop streaming */ - exp[2] = HDCS_ROWEXPL; - exp[3] = rowexp & 0xff; - exp[4] = HDCS_ROWEXPH; - exp[5] = rowexp >> 8; - exp[6] = HDCS00_SROWEXPL; - exp[7] = srowexp & 0xff; - exp[8] = HDCS00_SROWEXPH; - exp[9] = srowexp >> 8; - exp[10] = HDCS_STATUS; - exp[11] = 0x10; /* Clear exposure error flag*/ - exp[12] = HDCS00_CONTROL; - exp[13] = 0x04; /* Restart streaming */ - err = stv06xx_write_sensor_bytes(sd, exp, 7); - if (err < 0) - return err; - } - PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d", - val, rowexp, srowexp); - return err; -} - -static int hdcs_set_gains(struct sd *sd, u8 g) -{ - int err; - u8 gains[4]; - - /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ - if (g > 127) - g = 0x80 | (g / 2); - - gains[0] = g; - gains[1] = g; - gains[2] = g; - gains[3] = g; - - err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); - return err; -} - -static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - PDEBUG(D_V4L2, "Writing gain %d", val); - return hdcs_set_gains((struct sd *) gspca_dev, - val & 0xff); -} - -static int hdcs_set_size(struct sd *sd, - unsigned int width, unsigned int height) -{ - struct hdcs *hdcs = sd->sensor_priv; - u8 win[4]; - unsigned int x, y; - int err; - - /* must be multiple of 4 */ - width = (width + 3) & ~0x3; - height = (height + 3) & ~0x3; - - if (width > hdcs->array.width) - width = hdcs->array.width; - - if (IS_1020(sd)) { - /* the borders are also invalid */ - if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP - > hdcs->array.height) - height = hdcs->array.height - 2 * hdcs->array.border - - HDCS_1020_BOTTOM_Y_SKIP; - - y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2 - + hdcs->array.top; - } else { - if (height > hdcs->array.height) - height = hdcs->array.height; - - y = hdcs->array.top + (hdcs->array.height - height) / 2; - } - - x = hdcs->array.left + (hdcs->array.width - width) / 2; - - win[0] = y / 4; - win[1] = x / 4; - win[2] = (y + height) / 4 - 1; - win[3] = (x + width) / 4 - 1; - - err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4); - if (err < 0) - return err; - - /* Update the current width and height */ - hdcs->w = width; - hdcs->h = height; - return err; -} - -static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - int err = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err = hdcs_set_gain(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - err = hdcs_set_exposure(gspca_dev, ctrl->val); - break; - } - return err; -} - -static const struct v4l2_ctrl_ops hdcs_ctrl_ops = { - .s_ctrl = hdcs_s_ctrl, -}; - -static int hdcs_init_controls(struct sd *sd) -{ - struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - - v4l2_ctrl_handler_init(hdl, 2); - v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE); - v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, - V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN); - return hdl->error; -} - -static int hdcs_probe_1x00(struct sd *sd) -{ - struct hdcs *hdcs; - u16 sensor; - int ret; - - ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); - if (ret < 0 || sensor != 0x08) - return -ENODEV; - - pr_info("HDCS-1000/1100 sensor detected\n"); - - sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); - - hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); - if (!hdcs) - return -ENOMEM; - - hdcs->array.left = 8; - hdcs->array.top = 8; - hdcs->array.width = HDCS_1X00_DEF_WIDTH; - hdcs->array.height = HDCS_1X00_DEF_HEIGHT; - hdcs->array.border = 4; - - hdcs->exp.cto = 4; - hdcs->exp.cpo = 2; - hdcs->exp.rs = 186; - hdcs->exp.er = 100; - - /* - * Frame rate on HDCS-1000 with STV600 depends on PSMP: - * 4 = doesn't work at all - * 5 = 7.8 fps, - * 6 = 6.9 fps, - * 8 = 6.3 fps, - * 10 = 5.5 fps, - * 15 = 4.4 fps, - * 31 = 2.8 fps - * - * Frame rate on HDCS-1000 with STV602 depends on PSMP: - * 15 = doesn't work at all - * 18 = doesn't work at all - * 19 = 7.3 fps - * 20 = 7.4 fps - * 21 = 7.4 fps - * 22 = 7.4 fps - * 24 = 6.3 fps - * 30 = 5.4 fps - */ - hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5; - - sd->sensor_priv = hdcs; - - return 0; -} - -static int hdcs_probe_1020(struct sd *sd) -{ - struct hdcs *hdcs; - u16 sensor; - int ret; - - ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); - if (ret < 0 || sensor != 0x10) - return -ENODEV; - - pr_info("HDCS-1020 sensor detected\n"); - - sd->gspca_dev.cam.cam_mode = hdcs1020_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); - - hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); - if (!hdcs) - return -ENOMEM; - - /* - * From Andrey's test image: looks like HDCS-1020 upper-left - * visible pixel is at 24,8 (y maybe even smaller?) and lower-right - * visible pixel at 375,299 (x maybe even larger?) - */ - hdcs->array.left = 24; - hdcs->array.top = 4; - hdcs->array.width = HDCS_1020_DEF_WIDTH; - hdcs->array.height = 304; - hdcs->array.border = 4; - - hdcs->psmp = 6; - - hdcs->exp.cto = 3; - hdcs->exp.cpo = 3; - hdcs->exp.rs = 155; - hdcs->exp.er = 96; - - sd->sensor_priv = hdcs; - - return 0; -} - -static int hdcs_start(struct sd *sd) -{ - PDEBUG(D_STREAM, "Starting stream"); - - return hdcs_set_state(sd, HDCS_STATE_RUN); -} - -static int hdcs_stop(struct sd *sd) -{ - PDEBUG(D_STREAM, "Halting stream"); - - return hdcs_set_state(sd, HDCS_STATE_SLEEP); -} - -static int hdcs_init(struct sd *sd) -{ - struct hdcs *hdcs = sd->sensor_priv; - int i, err = 0; - - /* Set the STV0602AA in STV0600 emulation mode */ - if (sd->bridge == BRIDGE_STV602) - stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); - - /* Execute the bridge init */ - for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) { - err = stv06xx_write_bridge(sd, stv_bridge_init[i][0], - stv_bridge_init[i][1]); - } - if (err < 0) - return err; - - /* sensor soft reset */ - hdcs_reset(sd); - - /* Execute the sensor init */ - for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) { - err = stv06xx_write_sensor(sd, stv_sensor_init[i][0], - stv_sensor_init[i][1]); - } - if (err < 0) - return err; - - /* Enable continuous frame capture, bit 2: stop when frame complete */ - err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3)); - if (err < 0) - return err; - - /* Set PGA sample duration - (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */ - if (IS_1020(sd)) - err = stv06xx_write_sensor(sd, HDCS_TCTRL, - (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); - else - err = stv06xx_write_sensor(sd, HDCS_TCTRL, - (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp); - if (err < 0) - return err; - - return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); -} - -static int hdcs_dump(struct sd *sd) -{ - u16 reg, val; - - pr_info("Dumping sensor registers:\n"); - - for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { - stv06xx_read_sensor(sd, reg, &val); - pr_info("reg 0x%02x = 0x%02x\n", reg, val); - } - return 0; -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h deleted file mode 100644 index 1ba9158d0102..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * Copyright (c) 2008 Chia-I Wu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#ifndef STV06XX_HDCS_H_ -#define STV06XX_HDCS_H_ - -#include "stv06xx_sensor.h" - -#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG) -#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL) - -#define HDCS_1X00_DEF_WIDTH 360 -#define HDCS_1X00_DEF_HEIGHT 296 - -#define HDCS_1020_DEF_WIDTH 352 -#define HDCS_1020_DEF_HEIGHT 292 - -#define HDCS_1020_BOTTOM_Y_SKIP 4 - -#define HDCS_CLK_FREQ_MHZ 25 - -#define HDCS_ADC_START_SIG_DUR 3 - -/* LSB bit of I2C or register address signifies write (0) or read (1) */ -/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */ -/* Identifications Register */ -#define HDCS_IDENT (0x00 << 1) -/* Status Register */ -#define HDCS_STATUS (0x01 << 1) -/* Interrupt Mask Register */ -#define HDCS_IMASK (0x02 << 1) -/* Pad Control Register */ -#define HDCS_PCTRL (0x03 << 1) -/* Pad Drive Control Register */ -#define HDCS_PDRV (0x04 << 1) -/* Interface Control Register */ -#define HDCS_ICTRL (0x05 << 1) -/* Interface Timing Register */ -#define HDCS_ITMG (0x06 << 1) -/* Baud Fraction Register */ -#define HDCS_BFRAC (0x07 << 1) -/* Baud Rate Register */ -#define HDCS_BRATE (0x08 << 1) -/* ADC Control Register */ -#define HDCS_ADCCTRL (0x09 << 1) -/* First Window Row Register */ -#define HDCS_FWROW (0x0a << 1) -/* First Window Column Register */ -#define HDCS_FWCOL (0x0b << 1) -/* Last Window Row Register */ -#define HDCS_LWROW (0x0c << 1) -/* Last Window Column Register */ -#define HDCS_LWCOL (0x0d << 1) -/* Timing Control Register */ -#define HDCS_TCTRL (0x0e << 1) -/* PGA Gain Register: Even Row, Even Column */ -#define HDCS_ERECPGA (0x0f << 1) -/* PGA Gain Register: Even Row, Odd Column */ -#define HDCS_EROCPGA (0x10 << 1) -/* PGA Gain Register: Odd Row, Even Column */ -#define HDCS_ORECPGA (0x11 << 1) -/* PGA Gain Register: Odd Row, Odd Column */ -#define HDCS_OROCPGA (0x12 << 1) -/* Row Exposure Low Register */ -#define HDCS_ROWEXPL (0x13 << 1) -/* Row Exposure High Register */ -#define HDCS_ROWEXPH (0x14 << 1) - -/* I2C Registers only for HDCS-1000/1100 */ -/* Sub-Row Exposure Low Register */ -#define HDCS00_SROWEXPL (0x15 << 1) -/* Sub-Row Exposure High Register */ -#define HDCS00_SROWEXPH (0x16 << 1) -/* Configuration Register */ -#define HDCS00_CONFIG (0x17 << 1) -/* Control Register */ -#define HDCS00_CONTROL (0x18 << 1) - -/* I2C Registers only for HDCS-1020 */ -/* Sub-Row Exposure Register */ -#define HDCS20_SROWEXP (0x15 << 1) -/* Error Control Register */ -#define HDCS20_ERROR (0x16 << 1) -/* Interface Timing 2 Register */ -#define HDCS20_ITMG2 (0x17 << 1) -/* Interface Control 2 Register */ -#define HDCS20_ICTRL2 (0x18 << 1) -/* Horizontal Blank Register */ -#define HDCS20_HBLANK (0x19 << 1) -/* Vertical Blank Register */ -#define HDCS20_VBLANK (0x1a << 1) -/* Configuration Register */ -#define HDCS20_CONFIG (0x1b << 1) -/* Control Register */ -#define HDCS20_CONTROL (0x1c << 1) - -#define HDCS_RUN_ENABLE (1 << 2) -#define HDCS_SLEEP_MODE (1 << 1) - -#define HDCS_DEFAULT_EXPOSURE 48 -#define HDCS_DEFAULT_GAIN 50 - -static int hdcs_probe_1x00(struct sd *sd); -static int hdcs_probe_1020(struct sd *sd); -static int hdcs_start(struct sd *sd); -static int hdcs_init(struct sd *sd); -static int hdcs_init_controls(struct sd *sd); -static int hdcs_stop(struct sd *sd); -static int hdcs_dump(struct sd *sd); - -static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val); - -const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { - .name = "HP HDCS-1000/1100", - .i2c_flush = 0, - .i2c_addr = (0x55 << 1), - .i2c_len = 1, - - /* FIXME (see if we can lower min_packet_size, needs testing, and also - adjusting framerate when the bandwidth gets lower) */ - .min_packet_size = { 847 }, - .max_packet_size = { 847 }, - - .init = hdcs_init, - .init_controls = hdcs_init_controls, - .probe = hdcs_probe_1x00, - .start = hdcs_start, - .stop = hdcs_stop, - .dump = hdcs_dump, -}; - -const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { - .name = "HDCS-1020", - .i2c_flush = 0, - .i2c_addr = (0x55 << 1), - .i2c_len = 1, - - /* FIXME (see if we can lower min_packet_size, needs testing, and also - adjusting framerate when the bandwidthm gets lower) */ - .min_packet_size = { 847 }, - .max_packet_size = { 847 }, - - .init = hdcs_init, - .init_controls = hdcs_init_controls, - .probe = hdcs_probe_1020, - .start = hdcs_start, - .stop = hdcs_stop, - .dump = hdcs_dump, -}; - -static const u16 stv_bridge_init[][2] = { - {STV_ISO_ENABLE, 0}, - {STV_REG23, 0}, - {STV_REG00, 0x1d}, - {STV_REG01, 0xb5}, - {STV_REG02, 0xa8}, - {STV_REG03, 0x95}, - {STV_REG04, 0x07}, - - {STV_SCAN_RATE, 0x20}, - {STV_Y_CTRL, 0x01}, - {STV_X_CTRL, 0x0a} -}; - -static const u8 stv_sensor_init[][2] = { - /* Clear status (writing 1 will clear the corresponding status bit) */ - {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, - /* Disable all interrupts */ - {HDCS_IMASK, 0x00}, - {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)}, - {HDCS_PDRV, 0x00}, - {HDCS_ICTRL, BIT(5)}, - {HDCS_ITMG, BIT(4) | BIT(1)}, - /* ADC output resolution to 10 bits */ - {HDCS_ADCCTRL, 10} -}; - -#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c deleted file mode 100644 index cdfc3d05ab6b..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -/* - * The spec file for the PB-0100 suggests the following for best quality - * images after the sensor has been reset : - * - * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC - to produce good black level - * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes - through R53 - * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for - auto-exposure - * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain - * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value - * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on - auto-exposure routine - * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "stv06xx_pb0100.h" - -struct pb0100_ctrls { - struct { /* one big happy control cluster... */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *red; - struct v4l2_ctrl *blue; - struct v4l2_ctrl *natural; - }; - struct v4l2_ctrl *target; -}; - -static struct v4l2_pix_format pb0100_mode[] = { -/* low res / subsample modes disabled as they are only half res horizontal, - halving the vertical resolution does not seem to work */ - { - 320, - 240, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = 320 * 240, - .bytesperline = 320, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = PB0100_CROP_TO_VGA - }, - { - 352, - 288, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = 352 * 288, - .bytesperline = 352, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } -}; - -static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - struct pb0100_ctrls *ctrls = sd->sensor_priv; - int err = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - err = pb0100_set_autogain(gspca_dev, ctrl->val); - if (err) - break; - if (ctrl->val) - break; - err = pb0100_set_gain(gspca_dev, ctrls->gain->val); - if (err) - break; - err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val); - break; - case V4L2_CTRL_CLASS_USER + 0x1001: - err = pb0100_set_autogain_target(gspca_dev, ctrl->val); - break; - } - return err; -} - -static const struct v4l2_ctrl_ops pb0100_ctrl_ops = { - .s_ctrl = pb0100_s_ctrl, -}; - -static int pb0100_init_controls(struct sd *sd) -{ - struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - struct pb0100_ctrls *ctrls; - static const struct v4l2_ctrl_config autogain_target = { - .ops = &pb0100_ctrl_ops, - .id = V4L2_CTRL_CLASS_USER + 0x1000, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Automatic Gain Target", - .max = 255, - .step = 1, - .def = 128, - }; - static const struct v4l2_ctrl_config natural_light = { - .ops = &pb0100_ctrl_ops, - .id = V4L2_CTRL_CLASS_USER + 0x1001, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Natural Light Source", - .max = 1, - .step = 1, - .def = 1, - }; - - ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL); - if (!ctrls) - return -ENOMEM; - - v4l2_ctrl_handler_init(hdl, 6); - ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 511, 1, 12); - ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, - V4L2_CID_GAIN, 0, 255, 1, 128); - ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, - V4L2_CID_RED_BALANCE, -255, 255, 1, 0); - ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, - V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0); - ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL); - ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL); - if (hdl->error) { - kfree(ctrls); - return hdl->error; - } - sd->sensor_priv = ctrls; - v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false); - return 0; -} - -static int pb0100_probe(struct sd *sd) -{ - u16 sensor; - int err; - - err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); - - if (err < 0) - return -ENODEV; - if ((sensor >> 8) != 0x64) - return -ENODEV; - - pr_info("Photobit pb0100 sensor detected\n"); - - sd->gspca_dev.cam.cam_mode = pb0100_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); - - return 0; -} - -static int pb0100_start(struct sd *sd) -{ - int err, packet_size, max_packet_size; - struct usb_host_interface *alt; - struct usb_interface *intf; - struct cam *cam = &sd->gspca_dev.cam; - u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; - - intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); - alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); - if (!alt) - return -ENODEV; - packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - - /* If we don't have enough bandwidth use a lower framerate */ - max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode]; - if (packet_size < max_packet_size) - stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); - else - stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1)); - - /* Setup sensor window */ - if (mode & PB0100_CROP_TO_VGA) { - stv06xx_write_sensor(sd, PB_RSTART, 30); - stv06xx_write_sensor(sd, PB_CSTART, 20); - stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1); - stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1); - } else { - stv06xx_write_sensor(sd, PB_RSTART, 8); - stv06xx_write_sensor(sd, PB_CSTART, 4); - stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1); - stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1); - } - - if (mode & PB0100_SUBSAMPLE) { - stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */ - stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); - - stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); - } else { - stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); - stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); - /* larger -> slower */ - stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); - } - - err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); - PDEBUG(D_STREAM, "Started stream, status: %d", err); - - return (err < 0) ? err : 0; -} - -static int pb0100_stop(struct sd *sd) -{ - int err; - - err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1); - - if (err < 0) - goto out; - - /* Set bit 1 to zero */ - err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); - - PDEBUG(D_STREAM, "Halting stream"); -out: - return (err < 0) ? err : 0; -} - -/* FIXME: Sort the init commands out and put them into tables, - this is only for getting the camera to work */ -/* FIXME: No error handling for now, - add this once the init has been converted to proper tables */ -static int pb0100_init(struct sd *sd) -{ - stv06xx_write_bridge(sd, STV_REG00, 1); - stv06xx_write_bridge(sd, STV_SCAN_RATE, 0); - - /* Reset sensor */ - stv06xx_write_sensor(sd, PB_RESET, 1); - stv06xx_write_sensor(sd, PB_RESET, 0); - - /* Disable chip */ - stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); - - /* Gain stuff...*/ - stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6)); - stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12); - - /* Set up auto-exposure */ - /* ADC VREF_HI new setting for a transition - from the Expose1 to the Expose2 setting */ - stv06xx_write_sensor(sd, PB_R28, 12); - /* gain max for autoexposure */ - stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180); - /* gain min for autoexposure */ - stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12); - /* Maximum frame integration time (programmed into R8) - allowed for auto-exposure routine */ - stv06xx_write_sensor(sd, PB_R54, 3); - /* Minimum frame integration time (programmed into R8) - allowed for auto-exposure routine */ - stv06xx_write_sensor(sd, PB_R55, 0); - stv06xx_write_sensor(sd, PB_UPDATEINT, 1); - /* R15 Expose0 (maximum that auto-exposure may use) */ - stv06xx_write_sensor(sd, PB_R15, 800); - /* R17 Expose2 (minimum that auto-exposure may use) */ - stv06xx_write_sensor(sd, PB_R17, 10); - - stv06xx_write_sensor(sd, PB_EXPGAIN, 0); - - /* 0x14 */ - stv06xx_write_sensor(sd, PB_VOFFSET, 0); - /* 0x0D */ - stv06xx_write_sensor(sd, PB_ADCGAINH, 11); - /* Set black level (important!) */ - stv06xx_write_sensor(sd, PB_ADCGAINL, 0); - - /* ??? */ - stv06xx_write_bridge(sd, STV_REG00, 0x11); - stv06xx_write_bridge(sd, STV_REG03, 0x45); - stv06xx_write_bridge(sd, STV_REG04, 0x07); - - /* Scan/timing for the sensor */ - stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); - stv06xx_write_sensor(sd, PB_CFILLIN, 14); - stv06xx_write_sensor(sd, PB_VBL, 0); - stv06xx_write_sensor(sd, PB_FINTTIME, 0); - stv06xx_write_sensor(sd, PB_RINTTIME, 123); - - stv06xx_write_bridge(sd, STV_REG01, 0xc2); - stv06xx_write_bridge(sd, STV_REG02, 0xb0); - return 0; -} - -static int pb0100_dump(struct sd *sd) -{ - return 0; -} - -static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - struct pb0100_ctrls *ctrls = sd->sensor_priv; - - err = stv06xx_write_sensor(sd, PB_G1GAIN, val); - if (!err) - err = stv06xx_write_sensor(sd, PB_G2GAIN, val); - PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); - - if (!err) - err = pb0100_set_red_balance(gspca_dev, ctrls->red->val); - if (!err) - err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val); - - return err; -} - -static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - struct pb0100_ctrls *ctrls = sd->sensor_priv; - - val += ctrls->gain->val; - if (val < 0) - val = 0; - else if (val > 255) - val = 255; - - err = stv06xx_write_sensor(sd, PB_RGAIN, val); - PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err); - - return err; -} - -static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - struct pb0100_ctrls *ctrls = sd->sensor_priv; - - val += ctrls->gain->val; - if (val < 0) - val = 0; - else if (val > 255) - val = 255; - - err = stv06xx_write_sensor(sd, PB_BGAIN, val); - PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err); - - return err; -} - -static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int err; - - err = stv06xx_write_sensor(sd, PB_RINTTIME, val); - PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); - - return err; -} - -static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - struct pb0100_ctrls *ctrls = sd->sensor_priv; - - if (val) { - if (ctrls->natural->val) - val = BIT(6)|BIT(4)|BIT(0); - else - val = BIT(4)|BIT(0); - } else - val = 0; - - err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); - PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", - val, ctrls->natural->val, err); - - return err; -} - -static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) -{ - int err, totalpixels, brightpixels, darkpixels; - struct sd *sd = (struct sd *) gspca_dev; - - /* Number of pixels counted by the sensor when subsampling the pixels. - * Slightly larger than the real value to avoid oscillation */ - totalpixels = gspca_dev->width * gspca_dev->height; - totalpixels = totalpixels/(8*8) + totalpixels/(64*64); - - brightpixels = (totalpixels * val) >> 8; - darkpixels = totalpixels - brightpixels; - err = stv06xx_write_sensor(sd, PB_R21, brightpixels); - if (!err) - err = stv06xx_write_sensor(sd, PB_R22, darkpixels); - - PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err); - - return err; -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h deleted file mode 100644 index 5071e5353fd3..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#ifndef STV06XX_PB0100_H_ -#define STV06XX_PB0100_H_ - -#include "stv06xx_sensor.h" - -/* mode priv field flags */ -#define PB0100_CROP_TO_VGA 0x01 -#define PB0100_SUBSAMPLE 0x02 - -/* I2C Registers */ -#define PB_IDENT 0x00 /* Chip Version */ -#define PB_RSTART 0x01 /* Row Window Start */ -#define PB_CSTART 0x02 /* Column Window Start */ -#define PB_RWSIZE 0x03 /* Row Window Size */ -#define PB_CWSIZE 0x04 /* Column Window Size */ -#define PB_CFILLIN 0x05 /* Column Fill-In */ -#define PB_VBL 0x06 /* Vertical Blank Count */ -#define PB_CONTROL 0x07 /* Control Mode */ -#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */ -#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */ -#define PB_ROWSPEED 0x0a /* Row Speed Control */ -#define PB_ABORTFRAME 0x0b /* Abort Frame */ -#define PB_R12 0x0c /* Reserved */ -#define PB_RESET 0x0d /* Reset */ -#define PB_EXPGAIN 0x0e /* Exposure Gain Command */ -#define PB_R15 0x0f /* Expose0 */ -#define PB_R16 0x10 /* Expose1 */ -#define PB_R17 0x11 /* Expose2 */ -#define PB_R18 0x12 /* Low0_DAC */ -#define PB_R19 0x13 /* Low1_DAC */ -#define PB_R20 0x14 /* Low2_DAC */ -#define PB_R21 0x15 /* Threshold11 */ -#define PB_R22 0x16 /* Threshold0x */ -#define PB_UPDATEINT 0x17 /* Update Interval */ -#define PB_R24 0x18 /* High_DAC */ -#define PB_R25 0x19 /* Trans0H */ -#define PB_R26 0x1a /* Trans1L */ -#define PB_R27 0x1b /* Trans1H */ -#define PB_R28 0x1c /* Trans2L */ -#define PB_R29 0x1d /* Reserved */ -#define PB_R30 0x1e /* Reserved */ -#define PB_R31 0x1f /* Wait to Read */ -#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */ -#define PB_R33 0x21 /* IREF_VLN */ -#define PB_R34 0x22 /* IREF_VLP */ -#define PB_R35 0x23 /* IREF_VLN_INTEG */ -#define PB_R36 0x24 /* IREF_MASTER */ -#define PB_R37 0x25 /* IDACP */ -#define PB_R38 0x26 /* IDACN */ -#define PB_R39 0x27 /* DAC_Control_Reg */ -#define PB_R40 0x28 /* VCL */ -#define PB_R41 0x29 /* IREF_VLN_ADCIN */ -#define PB_R42 0x2a /* Reserved */ -#define PB_G1GAIN 0x2b /* Green 1 Gain */ -#define PB_BGAIN 0x2c /* Blue Gain */ -#define PB_RGAIN 0x2d /* Red Gain */ -#define PB_G2GAIN 0x2e /* Green 2 Gain */ -#define PB_R47 0x2f /* Dark Row Address */ -#define PB_R48 0x30 /* Dark Row Options */ -#define PB_R49 0x31 /* Reserved */ -#define PB_R50 0x32 /* Image Test Data */ -#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */ -#define PB_ADCMINGAIN 0x34 /* Minimum Gain */ -#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */ -#define PB_R54 0x36 /* Maximum Frame */ -#define PB_R55 0x37 /* Minimum Frame */ -#define PB_R56 0x38 /* Reserved */ -#define PB_VOFFSET 0x39 /* VOFFSET */ -#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */ -#define PB_ADCGAINH 0x3b /* VREF_HI */ -#define PB_ADCGAINL 0x3c /* VREF_LO */ -#define PB_R61 0x3d /* Reserved */ -#define PB_R62 0x3e /* Reserved */ -#define PB_R63 0x3f /* Reserved */ -#define PB_R64 0x40 /* Red/Blue Gain */ -#define PB_R65 0x41 /* Green 2/Green 1 Gain */ -#define PB_R66 0x42 /* VREF_HI/LO */ -#define PB_R67 0x43 /* Integration Time/Row Unit Count */ -#define PB_R240 0xf0 /* ADC Test */ -#define PB_R241 0xf1 /* Chip Enable */ -#define PB_R242 0xf2 /* Reserved */ - -static int pb0100_probe(struct sd *sd); -static int pb0100_start(struct sd *sd); -static int pb0100_init(struct sd *sd); -static int pb0100_init_controls(struct sd *sd); -static int pb0100_stop(struct sd *sd); -static int pb0100_dump(struct sd *sd); - -/* V4L2 controls supported by the driver */ -static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val); - -const struct stv06xx_sensor stv06xx_sensor_pb0100 = { - .name = "PB-0100", - .i2c_flush = 1, - .i2c_addr = 0xba, - .i2c_len = 2, - - .min_packet_size = { 635, 847 }, - .max_packet_size = { 847, 923 }, - - .init = pb0100_init, - .init_controls = pb0100_init_controls, - .probe = pb0100_probe, - .start = pb0100_start, - .stop = pb0100_stop, - .dump = pb0100_dump, -}; - -#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h deleted file mode 100644 index 3a498c2495c6..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#ifndef STV06XX_SENSOR_H_ -#define STV06XX_SENSOR_H_ - -#include "stv06xx.h" - -#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020) - -extern const struct stv06xx_sensor stv06xx_sensor_vv6410; -extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; -extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; -extern const struct stv06xx_sensor stv06xx_sensor_pb0100; -extern const struct stv06xx_sensor stv06xx_sensor_st6422; - -struct stv06xx_sensor { - /* Defines the name of a sensor */ - char name[32]; - - /* Sensor i2c address */ - u8 i2c_addr; - - /* Flush value*/ - u8 i2c_flush; - - /* length of an i2c word */ - u8 i2c_len; - - /* Isoc packet size (per mode) */ - int min_packet_size[4]; - int max_packet_size[4]; - - /* Probes if the sensor is connected */ - int (*probe)(struct sd *sd); - - /* Performs a initialization sequence */ - int (*init)(struct sd *sd); - - /* Initializes the controls */ - int (*init_controls)(struct sd *sd); - - /* Reads a sensor register */ - int (*read_sensor)(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - - /* Writes to a sensor register */ - int (*write_sensor)(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - - /* Instructs the sensor to start streaming */ - int (*start)(struct sd *sd); - - /* Instructs the sensor to stop streaming */ - int (*stop)(struct sd *sd); - - /* Instructs the sensor to dump all its contents */ - int (*dump)(struct sd *sd); -}; - -#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c deleted file mode 100644 index 8a57990dfe0f..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Support for the sensor part which is integrated (I think) into the - * st6422 stv06xx alike bridge, as its integrated there are no i2c writes - * but instead direct bridge writes. - * - * Copyright (c) 2009 Hans de Goede - * - * Strongly based on qc-usb-messenger, which is: - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "stv06xx_st6422.h" - -static struct v4l2_pix_format st6422_mode[] = { - /* Note we actually get 124 lines of data, of which we skip the 4st - 4 as they are garbage */ - { - 162, - 120, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = 162 * 120, - .bytesperline = 162, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - }, - /* Note we actually get 248 lines of data, of which we skip the 4st - 4 as they are garbage, and we tell the app it only gets the - first 240 of the 244 lines it actually gets, so that it ignores - the last 4. */ - { - 324, - 240, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = 324 * 244, - .bytesperline = 324, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, -}; - -/* V4L2 controls supported by the driver */ -static int setbrightness(struct sd *sd, s32 val); -static int setcontrast(struct sd *sd, s32 val); -static int setgain(struct sd *sd, u8 gain); -static int setexposure(struct sd *sd, s16 expo); - -static int st6422_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - int err = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - err = setbrightness(sd, ctrl->val); - break; - case V4L2_CID_CONTRAST: - err = setcontrast(sd, ctrl->val); - break; - case V4L2_CID_GAIN: - err = setgain(sd, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - err = setexposure(sd, ctrl->val); - break; - } - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - sd->gspca_dev.usb_err = err; - return err; -} - -static const struct v4l2_ctrl_ops st6422_ctrl_ops = { - .s_ctrl = st6422_s_ctrl, -}; - -static int st6422_init_controls(struct sd *sd) -{ - struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 31, 1, 3); - v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, - V4L2_CID_CONTRAST, 0, 15, 1, 11); - v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 1023, 1, 256); - v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, - V4L2_CID_GAIN, 0, 255, 1, 64); - - return hdl->error; -} - -static int st6422_probe(struct sd *sd) -{ - if (sd->bridge != BRIDGE_ST6422) - return -ENODEV; - - pr_info("st6422 sensor detected\n"); - - sd->gspca_dev.cam.cam_mode = st6422_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); - return 0; -} - -static int st6422_init(struct sd *sd) -{ - int err = 0, i; - - const u16 st6422_bridge_init[][2] = { - { STV_ISO_ENABLE, 0x00 }, /* disable capture */ - { 0x1436, 0x00 }, - { 0x1432, 0x03 }, /* 0x00-0x1F brightness */ - { 0x143a, 0xf9 }, /* 0x00-0x0F contrast */ - { 0x0509, 0x38 }, /* R */ - { 0x050a, 0x38 }, /* G */ - { 0x050b, 0x38 }, /* B */ - { 0x050c, 0x2a }, - { 0x050d, 0x01 }, - - - { 0x1431, 0x00 }, /* 0x00-0x07 ??? */ - { 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */ - { 0x1438, 0x18 }, /* 640x480 */ -/* 18 bayes */ -/* 10 compressed? */ - - { 0x1439, 0x00 }, -/* anti-noise? 0xa2 gives a perfect image */ - - { 0x143b, 0x05 }, - { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */ - - -/* shutter time 0x0000-0x03FF */ -/* low value give good picures on moving objects (but requires much light) */ -/* high value gives good picures in darkness (but tends to be overexposed) */ - { 0x143e, 0x01 }, - { 0x143d, 0x00 }, - - { 0x1442, 0xe2 }, -/* write: 1x1x xxxx */ -/* read: 1x1x xxxx */ -/* bit 5 == button pressed and hold if 0 */ -/* write 0xe2,0xea */ - -/* 0x144a */ -/* 0x00 init */ -/* bit 7 == button has been pressed, but not handled */ - -/* interrupt */ -/* if(urb->iso_frame_desc[i].status == 0x80) { */ -/* if(urb->iso_frame_desc[i].status == 0x88) { */ - - { 0x1500, 0xd0 }, - { 0x1500, 0xd0 }, - { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */ - - { 0x1501, 0xaf }, -/* high val-> light area gets darker */ -/* low val -> light area gets lighter */ - { 0x1502, 0xc2 }, -/* high val-> light area gets darker */ -/* low val -> light area gets lighter */ - { 0x1503, 0x45 }, -/* high val-> light area gets darker */ -/* low val -> light area gets lighter */ - { 0x1505, 0x02 }, -/* 2 : 324x248 80352 bytes */ -/* 7 : 248x162 40176 bytes */ -/* c+f: 162*124 20088 bytes */ - - { 0x150e, 0x8e }, - { 0x150f, 0x37 }, - { 0x15c0, 0x00 }, - { 0x15c3, 0x08 }, /* 0x04/0x14 ... test pictures ??? */ - - - { 0x143f, 0x01 }, /* commit settings */ - - }; - - for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) { - err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0], - st6422_bridge_init[i][1]); - } - - return err; -} - -static int setbrightness(struct sd *sd, s32 val) -{ - /* val goes from 0 -> 31 */ - return stv06xx_write_bridge(sd, 0x1432, val); -} - -static int setcontrast(struct sd *sd, s32 val) -{ - /* Val goes from 0 -> 15 */ - return stv06xx_write_bridge(sd, 0x143a, val | 0xf0); -} - -static int setgain(struct sd *sd, u8 gain) -{ - int err; - - /* Set red, green, blue, gain */ - err = stv06xx_write_bridge(sd, 0x0509, gain); - if (err < 0) - return err; - - err = stv06xx_write_bridge(sd, 0x050a, gain); - if (err < 0) - return err; - - err = stv06xx_write_bridge(sd, 0x050b, gain); - if (err < 0) - return err; - - /* 2 mystery writes */ - err = stv06xx_write_bridge(sd, 0x050c, 0x2a); - if (err < 0) - return err; - - return stv06xx_write_bridge(sd, 0x050d, 0x01); -} - -static int setexposure(struct sd *sd, s16 expo) -{ - int err; - - err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff); - if (err < 0) - return err; - - return stv06xx_write_bridge(sd, 0x143e, expo >> 8); -} - -static int st6422_start(struct sd *sd) -{ - int err; - struct cam *cam = &sd->gspca_dev.cam; - - if (cam->cam_mode[sd->gspca_dev.curr_mode].priv) - err = stv06xx_write_bridge(sd, 0x1505, 0x0f); - else - err = stv06xx_write_bridge(sd, 0x1505, 0x02); - if (err < 0) - return err; - - /* commit settings */ - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - return (err < 0) ? err : 0; -} - -static int st6422_stop(struct sd *sd) -{ - PDEBUG(D_STREAM, "Halting stream"); - - return 0; -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h deleted file mode 100644 index 8f20fbf30f33..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Support for the sensor part which is integrated (I think) into the - * st6422 stv06xx alike bridge, as its integrated there are no i2c writes - * but instead direct bridge writes. - * - * Copyright (c) 2009 Hans de Goede - * - * Strongly based on qc-usb-messenger, which is: - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef STV06XX_ST6422_H_ -#define STV06XX_ST6422_H_ - -#include "stv06xx_sensor.h" - -static int st6422_probe(struct sd *sd); -static int st6422_start(struct sd *sd); -static int st6422_init(struct sd *sd); -static int st6422_init_controls(struct sd *sd); -static int st6422_stop(struct sd *sd); - -const struct stv06xx_sensor stv06xx_sensor_st6422 = { - .name = "ST6422", - /* No known way to lower framerate in case of less bandwidth */ - .min_packet_size = { 300, 847 }, - .max_packet_size = { 300, 847 }, - .init = st6422_init, - .init_controls = st6422_init_controls, - .probe = st6422_probe, - .start = st6422_start, - .stop = st6422_stop, -}; - -#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c deleted file mode 100644 index 748e1421d6d8..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "stv06xx_vv6410.h" - -static struct v4l2_pix_format vv6410_mode[] = { - { - 356, - 292, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = 356 * 292, - .bytesperline = 356, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } -}; - -static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - int err = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_HFLIP: - err = vv6410_set_hflip(gspca_dev, ctrl->val); - break; - case V4L2_CID_VFLIP: - err = vv6410_set_vflip(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - err = vv6410_set_analog_gain(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - err = vv6410_set_exposure(gspca_dev, ctrl->val); - break; - } - return err; -} - -static const struct v4l2_ctrl_ops vv6410_ctrl_ops = { - .s_ctrl = vv6410_s_ctrl, -}; - -static int vv6410_probe(struct sd *sd) -{ - u16 data; - int err; - - err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); - if (err < 0) - return -ENODEV; - - if (data != 0x19) - return -ENODEV; - - pr_info("vv6410 sensor detected\n"); - - sd->gspca_dev.cam.cam_mode = vv6410_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); - return 0; -} - -static int vv6410_init_controls(struct sd *sd) -{ - struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_GAIN, 0, 15, 1, 10); - return hdl->error; -} - -static int vv6410_init(struct sd *sd) -{ - int err = 0, i; - - for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) - stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); - - if (err < 0) - return err; - - err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, - ARRAY_SIZE(vv6410_sensor_init)); - return (err < 0) ? err : 0; -} - -static int vv6410_start(struct sd *sd) -{ - int err; - struct cam *cam = &sd->gspca_dev.cam; - u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; - - if (priv & VV6410_SUBSAMPLE) { - PDEBUG(D_CONF, "Enabling subsampling"); - stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); - stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); - - stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); - } else { - stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); - stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); - stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00); - - } - - /* Turn on LED */ - err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON); - if (err < 0) - return err; - - err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0); - if (err < 0) - return err; - - PDEBUG(D_STREAM, "Starting stream"); - - return 0; -} - -static int vv6410_stop(struct sd *sd) -{ - int err; - - /* Turn off LED */ - err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF); - if (err < 0) - return err; - - err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE); - if (err < 0) - return err; - - PDEBUG(D_STREAM, "Halting stream"); - - return (err < 0) ? err : 0; -} - -static int vv6410_dump(struct sd *sd) -{ - u8 i; - int err = 0; - - pr_info("Dumping all vv6410 sensor registers\n"); - for (i = 0; i < 0xff && !err; i++) { - u16 data; - err = stv06xx_read_sensor(sd, i, &data); - pr_info("Register 0x%x contained 0x%x\n", i, data); - } - return (err < 0) ? err : 0; -} - -static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u16 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - - err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); - if (err < 0) - return err; - - if (val) - i2c_data |= VV6410_HFLIP; - else - i2c_data &= ~VV6410_HFLIP; - - PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); - - return (err < 0) ? err : 0; -} - -static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - u16 i2c_data; - struct sd *sd = (struct sd *) gspca_dev; - - err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); - if (err < 0) - return err; - - if (val) - i2c_data |= VV6410_VFLIP; - else - i2c_data &= ~VV6410_VFLIP; - - PDEBUG(D_V4L2, "Set vertical flip to %d", val); - err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); - - return (err < 0) ? err : 0; -} - -static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - PDEBUG(D_V4L2, "Set analog gain to %d", val); - err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); - - return (err < 0) ? err : 0; -} - -static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - unsigned int fine, coarse; - - val = (val * val >> 14) + val / 4; - - fine = val % VV6410_CIF_LINELENGTH; - coarse = min(512, val / VV6410_CIF_LINELENGTH); - - PDEBUG(D_V4L2, "Set coarse exposure to %d, fine expsure to %d", - coarse, fine); - - err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8); - if (err < 0) - goto out; - - err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff); - if (err < 0) - goto out; - - err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8); - if (err < 0) - goto out; - - err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff); - -out: - return err; -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h deleted file mode 100644 index 53e67b40ca05..000000000000 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher - * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland - * Copyright (c) 2002, 2003 Tuukka Toivonen - * Copyright (c) 2008 Erik Andrén - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * P/N 861037: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 - * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express - * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam - * P/N 861075-0040: Sensor HDCS1000 ASIC - * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB - * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web - */ - -#ifndef STV06XX_VV6410_H_ -#define STV06XX_VV6410_H_ - -#include "stv06xx_sensor.h" - -#define VV6410_COLS 416 -#define VV6410_ROWS 320 - -/* Status registers */ -/* Chip identification number including revision indicator */ -#define VV6410_DEVICEH 0x00 -#define VV6410_DEVICEL 0x01 - -/* User can determine whether timed I2C data - has been consumed by interrogating flag states */ -#define VV6410_STATUS0 0x02 - -/* Current line counter value */ -#define VV6410_LINECOUNTH 0x03 -#define VV6410_LINECOUNTL 0x04 - -/* End x coordinate of image size */ -#define VV6410_XENDH 0x05 -#define VV6410_XENDL 0x06 - -/* End y coordinate of image size */ -#define VV6410_YENDH 0x07 -#define VV6410_YENDL 0x08 - -/* This is the average pixel value returned from the - dark line offset cancellation algorithm */ -#define VV6410_DARKAVGH 0x09 -#define VV6410_DARKAVGL 0x0a - -/* This is the average pixel value returned from the - black line offset cancellation algorithm */ -#define VV6410_BLACKAVGH 0x0b -#define VV6410_BLACKAVGL 0x0c - -/* Flags to indicate whether the x or y image coordinates have been clipped */ -#define VV6410_STATUS1 0x0d - -/* Setup registers */ - -/* Low-power/sleep modes & video timing */ -#define VV6410_SETUP0 0x10 - -/* Various parameters */ -#define VV6410_SETUP1 0x11 - -/* Contains pixel counter reset value used by external sync */ -#define VV6410_SYNCVALUE 0x12 - -/* Frame grabbing modes (FST, LST and QCK) */ -#define VV6410_FGMODES 0x14 - -/* FST and QCK mapping modes. */ -#define VV6410_PINMAPPING 0x15 - -/* Data resolution */ -#define VV6410_DATAFORMAT 0x16 - -/* Output coding formats */ -#define VV6410_OPFORMAT 0x17 - -/* Various mode select bits */ -#define VV6410_MODESELECT 0x18 - -/* Exposure registers */ -/* Fine exposure. */ -#define VV6410_FINEH 0x20 -#define VV6410_FINEL 0x21 - -/* Coarse exposure */ -#define VV6410_COARSEH 0x22 -#define VV6410_COARSEL 0x23 - -/* Analog gain setting */ -#define VV6410_ANALOGGAIN 0x24 - -/* Clock division */ -#define VV6410_CLKDIV 0x25 - -/* Dark line offset cancellation value */ -#define VV6410_DARKOFFSETH 0x2c -#define VV6410_DARKOFFSETL 0x2d - -/* Dark line offset cancellation enable */ -#define VV6410_DARKOFFSETSETUP 0x2e - -/* Video timing registers */ -/* Line Length (Pixel Clocks) */ -#define VV6410_LINELENGTHH 0x52 -#define VV6410_LINELENGTHL 0x53 - -/* X-co-ordinate of top left corner of region of interest (x-offset) */ -#define VV6410_XOFFSETH 0x57 -#define VV6410_XOFFSETL 0x58 - -/* Y-coordinate of top left corner of region of interest (y-offset) */ -#define VV6410_YOFFSETH 0x59 -#define VV6410_YOFFSETL 0x5a - -/* Field length (Lines) */ -#define VV6410_FIELDLENGTHH 0x61 -#define VV6410_FIELDLENGTHL 0x62 - -/* System registers */ -/* Black offset cancellation default value */ -#define VV6410_BLACKOFFSETH 0x70 -#define VV6410_BLACKOFFSETL 0x71 - -/* Black offset cancellation setup */ -#define VV6410_BLACKOFFSETSETUP 0x72 - -/* Analog Control Register 0 */ -#define VV6410_CR0 0x75 - -/* Analog Control Register 1 */ -#define VV6410_CR1 0x76 - -/* ADC Setup Register */ -#define VV6410_AS0 0x77 - -/* Analog Test Register */ -#define VV6410_AT0 0x78 - -/* Audio Amplifier Setup Register */ -#define VV6410_AT1 0x79 - -#define VV6410_HFLIP (1 << 3) -#define VV6410_VFLIP (1 << 4) - -#define VV6410_LOW_POWER_MODE (1 << 0) -#define VV6410_SOFT_RESET (1 << 2) -#define VV6410_PAL_25_FPS (0 << 3) - -#define VV6410_CLK_DIV_2 (1 << 1) - -#define VV6410_FINE_EXPOSURE 320 -#define VV6410_COARSE_EXPOSURE 192 -#define VV6410_DEFAULT_GAIN 5 - -#define VV6410_SUBSAMPLE 0x01 -#define VV6410_CROP_TO_QVGA 0x02 - -#define VV6410_CIF_LINELENGTH 415 - -static int vv6410_probe(struct sd *sd); -static int vv6410_start(struct sd *sd); -static int vv6410_init(struct sd *sd); -static int vv6410_init_controls(struct sd *sd); -static int vv6410_stop(struct sd *sd); -static int vv6410_dump(struct sd *sd); - -/* V4L2 controls supported by the driver */ -static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val); - -const struct stv06xx_sensor stv06xx_sensor_vv6410 = { - .name = "ST VV6410", - .i2c_flush = 5, - .i2c_addr = 0x20, - .i2c_len = 1, - /* FIXME (see if we can lower packet_size-s, needs testing, and also - adjusting framerate when the bandwidth gets lower) */ - .min_packet_size = { 1023 }, - .max_packet_size = { 1023 }, - .init = vv6410_init, - .init_controls = vv6410_init_controls, - .probe = vv6410_probe, - .start = vv6410_start, - .stop = vv6410_stop, - .dump = vv6410_dump, -}; - -/* If NULL, only single value to write, stored in len */ -struct stv_init { - u16 addr; - u8 data; -}; - -static const struct stv_init stv_bridge_init[] = { - /* This reg is written twice. Some kind of reset? */ - {STV_RESET, 0x80}, - {STV_RESET, 0x00}, - {STV_SCAN_RATE, 0x00}, - {STV_I2C_FLUSH, 0x04}, - {STV_REG00, 0x0b}, - {STV_REG01, 0xa7}, - {STV_REG02, 0xb7}, - {STV_REG03, 0x00}, - {STV_REG04, 0x00}, - {0x1536, 0x02}, - {0x1537, 0x00}, - {0x1538, 0x60}, - {0x1539, 0x01}, - {0x153a, 0x20}, - {0x153b, 0x01}, -}; - -static const u8 vv6410_sensor_init[][2] = { - /* Setup registers */ - {VV6410_SETUP0, VV6410_SOFT_RESET}, - {VV6410_SETUP0, VV6410_LOW_POWER_MODE}, - /* Use shuffled read-out mode */ - {VV6410_SETUP1, BIT(6)}, - /* All modes to 1, FST, Fast QCK, Free running QCK, Free running LST, FST will qualify visible pixels */ - {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)}, - {VV6410_PINMAPPING, 0x00}, - /* Pre-clock generator divide off */ - {VV6410_DATAFORMAT, BIT(7) | BIT(0)}, - - {VV6410_CLKDIV, VV6410_CLK_DIV_2}, - - /* System registers */ - /* Enable voltage doubler */ - {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, - {VV6410_AT0, 0x00}, - /* Power up audio, differential */ - {VV6410_AT1, BIT(4) | BIT(0)}, -}; - -#endif diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c deleted file mode 100644 index 9ccfcb1c6479..000000000000 --- a/drivers/media/video/gspca/sunplus.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * Sunplus spca504(abc) spca533 spca536 library - * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "sunplus" - -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver"); -MODULE_LICENSE("GPL"); - -#define QUALITY 85 - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - bool autogain; - - u8 bridge; -#define BRIDGE_SPCA504 0 -#define BRIDGE_SPCA504B 1 -#define BRIDGE_SPCA504C 2 -#define BRIDGE_SPCA533 3 -#define BRIDGE_SPCA536 4 - u8 subtype; -#define AiptekMiniPenCam13 1 -#define LogitechClickSmart420 2 -#define LogitechClickSmart820 3 -#define MegapixV4 4 -#define MegaImageVI 5 - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, -}; - -static const struct v4l2_pix_format custom_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 464, - .sizeimage = 464 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, -}; - -static const struct v4l2_pix_format vga_mode2[] = { - {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 4}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 3}, - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, -}; - -#define SPCA50X_OFFSET_DATA 10 -#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3 -#define SPCA504_PCCAM600_OFFSET_COMPRESS 4 -#define SPCA504_PCCAM600_OFFSET_MODE 5 -#define SPCA504_PCCAM600_OFFSET_DATA 14 - /* Frame packet header offsets for the spca533 */ -#define SPCA533_OFFSET_DATA 16 -#define SPCA533_OFFSET_FRAMSEQ 15 -/* Frame packet header offsets for the spca536 */ -#define SPCA536_OFFSET_DATA 4 -#define SPCA536_OFFSET_FRAMSEQ 1 - -struct cmd { - u8 req; - u16 val; - u16 idx; -}; - -/* Initialisation data for the Creative PC-CAM 600 */ -static const struct cmd spca504_pccam600_init_data[] = { -/* {0xa0, 0x0000, 0x0503}, * capture mode */ - {0x00, 0x0000, 0x2000}, - {0x00, 0x0013, 0x2301}, - {0x00, 0x0003, 0x2000}, - {0x00, 0x0001, 0x21ac}, - {0x00, 0x0001, 0x21a6}, - {0x00, 0x0000, 0x21a7}, /* brightness */ - {0x00, 0x0020, 0x21a8}, /* contrast */ - {0x00, 0x0001, 0x21ac}, /* sat/hue */ - {0x00, 0x0000, 0x21ad}, /* hue */ - {0x00, 0x001a, 0x21ae}, /* saturation */ - {0x00, 0x0002, 0x21a3}, /* gamma */ - {0x30, 0x0154, 0x0008}, - {0x30, 0x0004, 0x0006}, - {0x30, 0x0258, 0x0009}, - {0x30, 0x0004, 0x0000}, - {0x30, 0x0093, 0x0004}, - {0x30, 0x0066, 0x0005}, - {0x00, 0x0000, 0x2000}, - {0x00, 0x0013, 0x2301}, - {0x00, 0x0003, 0x2000}, - {0x00, 0x0013, 0x2301}, - {0x00, 0x0003, 0x2000}, -}; - -/* Creative PC-CAM 600 specific open data, sent before using the - * generic initialisation data from spca504_open_data. - */ -static const struct cmd spca504_pccam600_open_data[] = { - {0x00, 0x0001, 0x2501}, - {0x20, 0x0500, 0x0001}, /* snapshot mode */ - {0x00, 0x0003, 0x2880}, - {0x00, 0x0001, 0x2881}, -}; - -/* Initialisation data for the logitech clicksmart 420 */ -static const struct cmd spca504A_clicksmart420_init_data[] = { -/* {0xa0, 0x0000, 0x0503}, * capture mode */ - {0x00, 0x0000, 0x2000}, - {0x00, 0x0013, 0x2301}, - {0x00, 0x0003, 0x2000}, - {0x00, 0x0001, 0x21ac}, - {0x00, 0x0001, 0x21a6}, - {0x00, 0x0000, 0x21a7}, /* brightness */ - {0x00, 0x0020, 0x21a8}, /* contrast */ - {0x00, 0x0001, 0x21ac}, /* sat/hue */ - {0x00, 0x0000, 0x21ad}, /* hue */ - {0x00, 0x001a, 0x21ae}, /* saturation */ - {0x00, 0x0002, 0x21a3}, /* gamma */ - {0x30, 0x0004, 0x000a}, - {0xb0, 0x0001, 0x0000}, - - {0xa1, 0x0080, 0x0001}, - {0x30, 0x0049, 0x0000}, - {0x30, 0x0060, 0x0005}, - {0x0c, 0x0004, 0x0000}, - {0x00, 0x0000, 0x0000}, - {0x00, 0x0000, 0x2000}, - {0x00, 0x0013, 0x2301}, - {0x00, 0x0003, 0x2000}, -}; - -/* clicksmart 420 open data ? */ -static const struct cmd spca504A_clicksmart420_open_data[] = { - {0x00, 0x0001, 0x2501}, - {0x20, 0x0502, 0x0000}, - {0x06, 0x0000, 0x0000}, - {0x00, 0x0004, 0x2880}, - {0x00, 0x0001, 0x2881}, - - {0xa0, 0x0000, 0x0503}, -}; - -static const u8 qtable_creative_pccam[2][64] = { - { /* Q-table Y-components */ - 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, - 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, - 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, - 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, - 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, - 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, - 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, - 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, - { /* Q-table C-components */ - 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, - 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} -}; - -/* FIXME: This Q-table is identical to the Creative PC-CAM one, - * except for one byte. Possibly a typo? - * NWG: 18/05/2003. - */ -static const u8 qtable_spca504_default[2][64] = { - { /* Q-table Y-components */ - 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, - 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, - 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, - 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, - 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, - 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, - 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, - 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e, - }, - { /* Q-table C-components */ - 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, - 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} -}; - -/* read bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - u8 req, - u16 index, - u16 len) -{ - int ret; - -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - pr_err("reg_r: buffer overflow\n"); - return; - } -#endif - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, - len ? gspca_dev->usb_buf : NULL, len, - 500); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* write one byte */ -static void reg_w_1(struct gspca_dev *gspca_dev, - u8 req, - u16 value, - u16 index, - u16 byte) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - gspca_dev->usb_buf[0] = byte; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, - gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_w_1 err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* write req / index / value */ -static void reg_w_riv(struct gspca_dev *gspca_dev, - u8 req, u16 index, u16 value) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - if (ret < 0) { - pr_err("reg_w_riv err %d\n", ret); - gspca_dev->usb_err = ret; - return; - } - PDEBUG(D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x", - req, index, value); -} - -static void write_vector(struct gspca_dev *gspca_dev, - const struct cmd *data, int ncmds) -{ - while (--ncmds >= 0) { - reg_w_riv(gspca_dev, data->req, data->idx, data->val); - data++; - } -} - -static void setup_qtable(struct gspca_dev *gspca_dev, - const u8 qtable[2][64]) -{ - int i; - - /* loop over y components */ - for (i = 0; i < 64; i++) - reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]); - - /* loop over c components */ - for (i = 0; i < 64; i++) - reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]); -} - -static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, - u8 req, u16 idx, u16 val) -{ - reg_w_riv(gspca_dev, req, idx, val); - reg_r(gspca_dev, 0x01, 0x0001, 1); - PDEBUG(D_FRAM, "before wait 0x%04x", gspca_dev->usb_buf[0]); - reg_w_riv(gspca_dev, req, idx, val); - - msleep(200); - reg_r(gspca_dev, 0x01, 0x0001, 1); - PDEBUG(D_FRAM, "after wait 0x%04x", gspca_dev->usb_buf[0]); -} - -#ifdef GSPCA_DEBUG -static void spca504_read_info(struct gspca_dev *gspca_dev) -{ - int i; - u8 info[6]; - - for (i = 0; i < 6; i++) { - reg_r(gspca_dev, 0, i, 1); - info[i] = gspca_dev->usb_buf[0]; - } - PDEBUG(D_STREAM, - "Read info: %d %d %d %d %d %d." - " Should be 1,0,2,2,0,0", - info[0], info[1], info[2], - info[3], info[4], info[5]); -} -#endif - -static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, - u8 req, - u16 idx, u16 val, u8 endcode, u8 count) -{ - u16 status; - - reg_w_riv(gspca_dev, req, idx, val); - reg_r(gspca_dev, 0x01, 0x0001, 1); - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_FRAM, "Status 0x%02x Need 0x%02x", - gspca_dev->usb_buf[0], endcode); - if (!count) - return; - count = 200; - while (--count > 0) { - msleep(10); - /* gsmart mini2 write a each wait setting 1 ms is enough */ -/* reg_w_riv(gspca_dev, req, idx, val); */ - reg_r(gspca_dev, 0x01, 0x0001, 1); - status = gspca_dev->usb_buf[0]; - if (status == endcode) { - PDEBUG(D_FRAM, "status 0x%04x after wait %d", - status, 200 - count); - break; - } - } -} - -static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev) -{ - int count = 10; - - while (--count > 0) { - reg_r(gspca_dev, 0x21, 0, 1); - if ((gspca_dev->usb_buf[0] & 0x01) == 0) - break; - msleep(10); - } -} - -static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) -{ - int count = 50; - - while (--count > 0) { - reg_r(gspca_dev, 0x21, 1, 1); - if (gspca_dev->usb_buf[0] != 0) { - reg_w_1(gspca_dev, 0x21, 0, 1, 0); - reg_r(gspca_dev, 0x21, 1, 1); - spca504B_PollingDataReady(gspca_dev); - break; - } - msleep(10); - } -} - -#ifdef GSPCA_DEBUG -static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) -{ - u8 *data; - - data = gspca_dev->usb_buf; - reg_r(gspca_dev, 0x20, 0, 5); - PDEBUG(D_STREAM, "FirmWare: %d %d %d %d %d", - data[0], data[1], data[2], data[3], data[4]); - reg_r(gspca_dev, 0x23, 0, 64); - reg_r(gspca_dev, 0x23, 1, 64); -} -#endif - -static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 Size; - - Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - switch (sd->bridge) { - case BRIDGE_SPCA533: - reg_w_riv(gspca_dev, 0x31, 0, 0); - spca504B_WaitCmdStatus(gspca_dev); - spca504B_PollingDataReady(gspca_dev); -#ifdef GSPCA_DEBUG - spca50x_GetFirmware(gspca_dev); -#endif - reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */ - reg_r(gspca_dev, 0x24, 8, 1); - - reg_w_1(gspca_dev, 0x25, 0, 4, Size); - reg_r(gspca_dev, 0x25, 4, 1); /* size */ - spca504B_PollingDataReady(gspca_dev); - - /* Init the cam width height with some values get on init ? */ - reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00); - spca504B_WaitCmdStatus(gspca_dev); - spca504B_PollingDataReady(gspca_dev); - break; - default: -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA536: */ - reg_w_1(gspca_dev, 0x25, 0, 4, Size); - reg_r(gspca_dev, 0x25, 4, 1); /* size */ - reg_w_1(gspca_dev, 0x27, 0, 0, 6); - reg_r(gspca_dev, 0x27, 0, 1); /* type */ - spca504B_PollingDataReady(gspca_dev); - break; - case BRIDGE_SPCA504: - Size += 3; - if (sd->subtype == AiptekMiniPenCam13) { - /* spca504a aiptek */ - spca504A_acknowledged_command(gspca_dev, - 0x08, Size, 0, - 0x80 | (Size & 0x0f), 1); - spca504A_acknowledged_command(gspca_dev, - 1, 3, 0, 0x9f, 0); - } else { - spca504_acknowledged_command(gspca_dev, 0x08, Size, 0); - } - break; - case BRIDGE_SPCA504C: - /* capture mode */ - reg_w_riv(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00); - reg_w_riv(gspca_dev, 0x20, 0x01, 0x0500 | (Size & 0x0f)); - break; - } -} - -static void spca504_wait_status(struct gspca_dev *gspca_dev) -{ - int cnt; - - cnt = 256; - while (--cnt > 0) { - /* With this we get the status, when return 0 it's all ok */ - reg_r(gspca_dev, 0x06, 0x00, 1); - if (gspca_dev->usb_buf[0] == 0) - return; - msleep(10); - } -} - -static void spca504B_setQtable(struct gspca_dev *gspca_dev) -{ - reg_w_1(gspca_dev, 0x26, 0, 0, 3); - reg_r(gspca_dev, 0x26, 0, 1); - spca504B_PollingDataReady(gspca_dev); -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 reg; - - reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7; - reg_w_riv(gspca_dev, 0x00, reg, val); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 reg; - - reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8; - reg_w_riv(gspca_dev, 0x00, reg, val); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u16 reg; - - reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae; - reg_w_riv(gspca_dev, 0x00, reg, val); -} - -static void init_ctl_reg(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int pollreg = 1; - - switch (sd->bridge) { - case BRIDGE_SPCA504: - case BRIDGE_SPCA504C: - pollreg = 0; - /* fall thru */ - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ - reg_w_riv(gspca_dev, 0, 0x21ad, 0x00); /* hue */ - reg_w_riv(gspca_dev, 0, 0x21ac, 0x01); /* sat/hue */ - reg_w_riv(gspca_dev, 0, 0x21a3, 0x00); /* gamma */ - break; - case BRIDGE_SPCA536: - reg_w_riv(gspca_dev, 0, 0x20f5, 0x40); - reg_w_riv(gspca_dev, 0, 0x20f4, 0x01); - reg_w_riv(gspca_dev, 0, 0x2089, 0x00); - break; - } - if (pollreg) - spca504B_PollingDataReady(gspca_dev); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - - sd->bridge = id->driver_info >> 8; - sd->subtype = id->driver_info; - - if (sd->subtype == AiptekMiniPenCam13) { - - /* try to get the firmware as some cam answer 2.0.1.2.2 - * and should be a spca504b then overwrite that setting */ - reg_r(gspca_dev, 0x20, 0, 1); - switch (gspca_dev->usb_buf[0]) { - case 1: - break; /* (right bridge/subtype) */ - case 2: - sd->bridge = BRIDGE_SPCA504B; - sd->subtype = 0; - break; - default: - return -ENODEV; - } - } - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA536: */ - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - break; - case BRIDGE_SPCA533: - cam->cam_mode = custom_mode; - if (sd->subtype == MegaImageVI) /* 320x240 only */ - cam->nmodes = ARRAY_SIZE(custom_mode) - 1; - else - cam->nmodes = ARRAY_SIZE(custom_mode); - break; - case BRIDGE_SPCA504C: - cam->cam_mode = vga_mode2; - cam->nmodes = ARRAY_SIZE(vga_mode2); - break; - } - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->bridge) { - case BRIDGE_SPCA504B: - reg_w_riv(gspca_dev, 0x1d, 0x00, 0); - reg_w_riv(gspca_dev, 0x00, 0x2306, 0x01); - reg_w_riv(gspca_dev, 0x00, 0x0d04, 0x00); - reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00); - reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13); - reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00); - /* fall thru */ - case BRIDGE_SPCA533: - spca504B_PollingDataReady(gspca_dev); -#ifdef GSPCA_DEBUG - spca50x_GetFirmware(gspca_dev); -#endif - break; - case BRIDGE_SPCA536: -#ifdef GSPCA_DEBUG - spca50x_GetFirmware(gspca_dev); -#endif - reg_r(gspca_dev, 0x00, 0x5002, 1); - reg_w_1(gspca_dev, 0x24, 0, 0, 0); - reg_r(gspca_dev, 0x24, 0, 1); - spca504B_PollingDataReady(gspca_dev); - reg_w_riv(gspca_dev, 0x34, 0, 0); - spca504B_WaitCmdStatus(gspca_dev); - break; - case BRIDGE_SPCA504C: /* pccam600 */ - PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)"); - reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000); - reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001); /* reset */ - spca504_wait_status(gspca_dev); - if (sd->subtype == LogitechClickSmart420) - write_vector(gspca_dev, - spca504A_clicksmart420_open_data, - ARRAY_SIZE(spca504A_clicksmart420_open_data)); - else - write_vector(gspca_dev, spca504_pccam600_open_data, - ARRAY_SIZE(spca504_pccam600_open_data)); - setup_qtable(gspca_dev, qtable_creative_pccam); - break; - default: -/* case BRIDGE_SPCA504: */ - PDEBUG(D_STREAM, "Opening SPCA504"); - if (sd->subtype == AiptekMiniPenCam13) { -#ifdef GSPCA_DEBUG - spca504_read_info(gspca_dev); -#endif - - /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ - spca504A_acknowledged_command(gspca_dev, 0x24, - 8, 3, 0x9e, 1); - /* Twice sequential need status 0xff->0x9e->0x9d */ - spca504A_acknowledged_command(gspca_dev, 0x24, - 8, 3, 0x9e, 0); - - spca504A_acknowledged_command(gspca_dev, 0x24, - 0, 0, 0x9d, 1); - /******************************/ - /* spca504a aiptek */ - spca504A_acknowledged_command(gspca_dev, 0x08, - 6, 0, 0x86, 1); -/* reg_write (dev, 0, 0x2000, 0); */ -/* reg_write (dev, 0, 0x2883, 1); */ -/* spca504A_acknowledged_command (gspca_dev, 0x08, - 6, 0, 0x86, 1); */ -/* spca504A_acknowledged_command (gspca_dev, 0x24, - 0, 0, 0x9D, 1); */ - reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05); - /* L92 sno1t.txt */ - reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05); - spca504A_acknowledged_command(gspca_dev, 0x01, - 0x0f, 0, 0xff, 0); - } - /* setup qtable */ - reg_w_riv(gspca_dev, 0, 0x2000, 0); - reg_w_riv(gspca_dev, 0, 0x2883, 1); - setup_qtable(gspca_dev, qtable_spca504_default); - break; - } - return gspca_dev->usb_err; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int enable; - - /* create the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x22); /* JPEG 411 */ - jpeg_set_qual(sd->jpeg_hdr, QUALITY); - - if (sd->bridge == BRIDGE_SPCA504B) - spca504B_setQtable(gspca_dev); - spca504B_SetSizeType(gspca_dev); - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA536: */ - switch (sd->subtype) { - case MegapixV4: - case LogitechClickSmart820: - case MegaImageVI: - reg_w_riv(gspca_dev, 0xf0, 0, 0); - spca504B_WaitCmdStatus(gspca_dev); - reg_r(gspca_dev, 0xf0, 4, 0); - spca504B_WaitCmdStatus(gspca_dev); - break; - default: - reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00); - spca504B_WaitCmdStatus(gspca_dev); - spca504B_PollingDataReady(gspca_dev); - break; - } - break; - case BRIDGE_SPCA504: - if (sd->subtype == AiptekMiniPenCam13) { -#ifdef GSPCA_DEBUG - spca504_read_info(gspca_dev); -#endif - - /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ - spca504A_acknowledged_command(gspca_dev, 0x24, - 8, 3, 0x9e, 1); - /* Twice sequential need status 0xff->0x9e->0x9d */ - spca504A_acknowledged_command(gspca_dev, 0x24, - 8, 3, 0x9e, 0); - spca504A_acknowledged_command(gspca_dev, 0x24, - 0, 0, 0x9d, 1); - } else { - spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); -#ifdef GSPCA_DEBUG - spca504_read_info(gspca_dev); -#endif - spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); - spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); - } - spca504B_SetSizeType(gspca_dev); - reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05); - /* L92 sno1t.txt */ - reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05); - break; - case BRIDGE_SPCA504C: - if (sd->subtype == LogitechClickSmart420) { - write_vector(gspca_dev, - spca504A_clicksmart420_init_data, - ARRAY_SIZE(spca504A_clicksmart420_init_data)); - } else { - write_vector(gspca_dev, spca504_pccam600_init_data, - ARRAY_SIZE(spca504_pccam600_init_data)); - } - enable = (sd->autogain ? 0x04 : 0x01); - reg_w_riv(gspca_dev, 0x0c, 0x0000, enable); - /* auto exposure */ - reg_w_riv(gspca_dev, 0xb0, 0x0000, enable); - /* auto whiteness */ - - /* set default exposure compensation and whiteness balance */ - reg_w_riv(gspca_dev, 0x30, 0x0001, 800); /* ~ 20 fps */ - reg_w_riv(gspca_dev, 0x30, 0x0002, 1600); - spca504B_SetSizeType(gspca_dev); - break; - } - init_ctl_reg(gspca_dev); - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA536: */ -/* case BRIDGE_SPCA504B: */ - reg_w_riv(gspca_dev, 0x31, 0, 0); - spca504B_WaitCmdStatus(gspca_dev); - spca504B_PollingDataReady(gspca_dev); - break; - case BRIDGE_SPCA504: - case BRIDGE_SPCA504C: - reg_w_riv(gspca_dev, 0x00, 0x2000, 0x0000); - - if (sd->subtype == AiptekMiniPenCam13) { - /* spca504a aiptek */ -/* spca504A_acknowledged_command(gspca_dev, 0x08, - 6, 0, 0x86, 1); */ - spca504A_acknowledged_command(gspca_dev, 0x24, - 0x00, 0x00, 0x9d, 1); - spca504A_acknowledged_command(gspca_dev, 0x01, - 0x0f, 0x00, 0xff, 1); - } else { - spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); - reg_w_riv(gspca_dev, 0x01, 0x000f, 0x0000); - } - break; - } -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, sof = 0; - static u8 ffd9[] = {0xff, 0xd9}; - -/* frames are jpeg 4.1.1 without 0xff escape */ - switch (sd->bridge) { - case BRIDGE_SPCA533: - if (data[0] == 0xff) { - if (data[1] != 0x01) { /* drop packet */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - } - sof = 1; - data += SPCA533_OFFSET_DATA; - len -= SPCA533_OFFSET_DATA; - } else { - data += 1; - len -= 1; - } - break; - case BRIDGE_SPCA536: - if (data[0] == 0xff) { - sof = 1; - data += SPCA536_OFFSET_DATA; - len -= SPCA536_OFFSET_DATA; - } else { - data += 2; - len -= 2; - } - break; - default: -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504B: */ - switch (data[0]) { - case 0xfe: /* start of frame */ - sof = 1; - data += SPCA50X_OFFSET_DATA; - len -= SPCA50X_OFFSET_DATA; - break; - case 0xff: /* drop packet */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - default: - data += 1; - len -= 1; - break; - } - break; - case BRIDGE_SPCA504C: - switch (data[0]) { - case 0xfe: /* start of frame */ - sof = 1; - data += SPCA504_PCCAM600_OFFSET_DATA; - len -= SPCA504_PCCAM600_OFFSET_DATA; - break; - case 0xff: /* drop packet */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - default: - data += 1; - len -= 1; - break; - } - break; - } - if (sof) { /* start of frame */ - gspca_frame_add(gspca_dev, LAST_PACKET, - ffd9, 2); - - /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - } - - /* add 0x00 after 0xff */ - i = 0; - do { - if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, - data, i + 1); - len -= i; - data += i; - *data = 0x00; - i = 0; - } - i++; - } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - sd->autogain = ctrl->val; - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 0x20); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 0x1a); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -#define BS(bridge, subtype) \ - .driver_info = (BRIDGE_ ## bridge << 8) \ - | (subtype) -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)}, - {USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)}, - {USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)}, - {USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)}, - {USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)}, - {USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)}, - {USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)}, - {USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)}, - {USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)}, - {USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)}, - {USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)}, - {USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)}, - {USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)}, - {USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)}, - {USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)}, - {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)}, - {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, - {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, - {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)}, - {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, - {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)}, - {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, - {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)}, - {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)}, - {USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)}, - {USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)}, - {USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)}, - {USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)}, - {USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)}, - {USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)}, - {USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)}, - {USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)}, - {USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)}, - {USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)}, - {USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)}, - {USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)}, - {USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)}, - {USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)}, - {USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)}, - {USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)}, - {USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)}, - {USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)}, - {USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)}, - {USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)}, - {USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)}, - {USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)}, - {USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)}, - {USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)}, - {USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)}, - {USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)}, - {USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)}, - {USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)}, - {USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)}, - {USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)}, - {USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)}, - {USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)}, - {USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)}, - {USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)}, - {USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c deleted file mode 100644 index 8bc6c3ceec2c..000000000000 --- a/drivers/media/video/gspca/t613.c +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * T613 subdriver - * - * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *Notes: * t613 + tas5130A - * * Focus to light do not balance well as in win. - * Quality in win is not good, but its kinda better. - * * Fix some "extraneous bytes", most of apps will show the image anyway - * * Gamma table, is there, but its really doing something? - * * 7~8 Fps, its ok, max on win its 10. - * Costantino Leandro - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "t613" - -#include -#include -#include "gspca.h" - -MODULE_AUTHOR("Leandro Costantino "); -MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); -MODULE_LICENSE("GPL"); - -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl *freq; - struct { /* awb / color gains control cluster */ - struct v4l2_ctrl *awb; - struct v4l2_ctrl *gain; - struct v4l2_ctrl *red_balance; - struct v4l2_ctrl *blue_balance; - }; - - u8 sensor; - u8 button_pressed; -}; -enum sensors { - SENSOR_OM6802, - SENSOR_OTHER, - SENSOR_TAS5130A, - SENSOR_LT168G, /* must verify if this is the actual model */ -}; - -static const struct v4l2_pix_format vga_mode_t16[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 4}, -#if 0 /* HDG: broken with my test cam, so lets disable it */ - {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 3}, -#endif - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, -#if 0 /* HDG: broken with my test cam, so lets disable it */ - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, -#endif - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -/* sensor specific data */ -struct additional_sensor_data { - const u8 n3[6]; - const u8 *n4, n4sz; - const u8 reg80, reg8e; - const u8 nset8[6]; - const u8 data1[10]; - const u8 data2[9]; - const u8 data3[9]; - const u8 data5[6]; - const u8 stream[4]; -}; - -static const u8 n4_om6802[] = { - 0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, - 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, - 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, - 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, - 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, - 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, - 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, - 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, - 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46 -}; -static const u8 n4_other[] = { - 0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69, - 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68, - 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8, - 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8, - 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56, - 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5, - 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0, - 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00 -}; -static const u8 n4_tas5130a[] = { - 0x80, 0x3c, 0x81, 0x68, 0x83, 0xa0, 0x84, 0x20, - 0x8a, 0x68, 0x8b, 0x58, 0x8c, 0x88, 0x8e, 0xb4, - 0x8f, 0x24, 0xa1, 0xb1, 0xa2, 0x30, 0xa5, 0x10, - 0xa6, 0x4a, 0xae, 0x03, 0xb1, 0x44, 0xb2, 0x08, - 0xb7, 0x06, 0xb9, 0xe7, 0xbb, 0xc4, 0xbc, 0x4a, - 0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8, - 0xc6, 0xda -}; -static const u8 n4_lt168g[] = { - 0x66, 0x01, 0x7f, 0x00, 0x80, 0x7c, 0x81, 0x28, - 0x83, 0x44, 0x84, 0x20, 0x86, 0x20, 0x8a, 0x70, - 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xa0, 0x8e, 0xb3, - 0x8f, 0x24, 0xa1, 0xb0, 0xa2, 0x38, 0xa5, 0x20, - 0xa6, 0x4a, 0xa8, 0xe8, 0xaf, 0x38, 0xb0, 0x68, - 0xb1, 0x44, 0xb2, 0x88, 0xbb, 0x86, 0xbd, 0x40, - 0xbe, 0x26, 0xc1, 0x05, 0xc2, 0x88, 0xc5, 0xc0, - 0xda, 0x8e, 0xdb, 0xca, 0xdc, 0xa8, 0xdd, 0x8c, - 0xde, 0x44, 0xdf, 0x0c, 0xe9, 0x80 -}; - -static const struct additional_sensor_data sensor_data[] = { -[SENSOR_OM6802] = { - .n3 = - {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}, - .n4 = n4_om6802, - .n4sz = sizeof n4_om6802, - .reg80 = 0x3c, - .reg8e = 0x33, - .nset8 = {0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00}, - .data1 = - {0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06, - 0xb3, 0xfc}, - .data2 = - {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, - 0xff}, - .data3 = - {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, - 0xff}, - .data5 = /* this could be removed later */ - {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23}, - .stream = - {0x0b, 0x04, 0x0a, 0x78}, - }, -[SENSOR_OTHER] = { - .n3 = - {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}, - .n4 = n4_other, - .n4sz = sizeof n4_other, - .reg80 = 0xac, - .reg8e = 0xb8, - .nset8 = {0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00}, - .data1 = - {0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a, - 0xe8, 0xfc}, - .data2 = - {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, - 0xd9}, - .data3 = - {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, - 0xd9}, - .data5 = - {0x0c, 0x03, 0xab, 0x29, 0x81, 0x69}, - .stream = - {0x0b, 0x04, 0x0a, 0x00}, - }, -[SENSOR_TAS5130A] = { - .n3 = - {0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08}, - .n4 = n4_tas5130a, - .n4sz = sizeof n4_tas5130a, - .reg80 = 0x3c, - .reg8e = 0xb4, - .nset8 = {0xa8, 0xf0, 0xc6, 0xda, 0xc0, 0x00}, - .data1 = - {0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27, - 0xc8, 0xfc}, - .data2 = - {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, - 0xe0}, - .data3 = - {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, - 0xe0}, - .data5 = - {0x0c, 0x03, 0xab, 0x10, 0x81, 0x20}, - .stream = - {0x0b, 0x04, 0x0a, 0x40}, - }, -[SENSOR_LT168G] = { - .n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00}, - .n4 = n4_lt168g, - .n4sz = sizeof n4_lt168g, - .reg80 = 0x7c, - .reg8e = 0xb3, - .nset8 = {0xa8, 0xf0, 0xc6, 0xba, 0xc0, 0x00}, - .data1 = {0xc0, 0x38, 0x08, 0x10, 0xc0, 0x30, 0x10, 0x40, - 0xb0, 0xf4}, - .data2 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, - 0xff}, - .data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, - 0xff}, - .data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b}, - .stream = {0x0b, 0x04, 0x0a, 0x28}, - }, -}; - -#define MAX_EFFECTS 7 -static const u8 effects_table[MAX_EFFECTS][6] = { - {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ - {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ - {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */ - {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80}, /* Sepia */ - {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02}, /* Croquis */ - {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10}, /* Sun Effect */ - {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */ -}; - -#define GAMMA_MAX (15) -static const u8 gamma_table[GAMMA_MAX+1][17] = { -/* gamma table from cam1690.ini */ - {0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21, /* 0 */ - 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb, - 0xff}, - {0x00, 0x01, 0x03, 0x08, 0x0e, 0x16, 0x21, 0x2d, /* 1 */ - 0x3c, 0x4d, 0x60, 0x75, 0x8d, 0xa6, 0xc2, 0xe1, - 0xff}, - {0x00, 0x01, 0x05, 0x0b, 0x12, 0x1c, 0x28, 0x35, /* 2 */ - 0x45, 0x56, 0x69, 0x7e, 0x95, 0xad, 0xc7, 0xe3, - 0xff}, - {0x00, 0x02, 0x07, 0x0f, 0x18, 0x24, 0x30, 0x3f, /* 3 */ - 0x4f, 0x61, 0x73, 0x88, 0x9d, 0xb4, 0xcd, 0xe6, - 0xff}, - {0x00, 0x04, 0x0b, 0x15, 0x20, 0x2d, 0x3b, 0x4a, /* 4 */ - 0x5b, 0x6c, 0x7f, 0x92, 0xa7, 0xbc, 0xd2, 0xe9, - 0xff}, - {0x00, 0x07, 0x11, 0x15, 0x20, 0x2d, 0x48, 0x58, /* 5 */ - 0x68, 0x79, 0x8b, 0x9d, 0xb0, 0xc4, 0xd7, 0xec, - 0xff}, - {0x00, 0x0c, 0x1a, 0x29, 0x38, 0x47, 0x57, 0x67, /* 6 */ - 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff}, - {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, /* 7 */ - 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, - 0xff}, - {0x00, 0x15, 0x27, 0x38, 0x49, 0x59, 0x69, 0x79, /* 8 */ - 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe2, 0xf0, - 0xff}, - {0x00, 0x1c, 0x30, 0x43, 0x54, 0x65, 0x75, 0x84, /* 9 */ - 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd8, 0xe5, 0xf2, - 0xff}, - {0x00, 0x24, 0x3b, 0x4f, 0x60, 0x70, 0x80, 0x8e, /* 10 */ - 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xdc, 0xe8, 0xf3, - 0xff}, - {0x00, 0x2a, 0x3c, 0x5d, 0x6e, 0x7e, 0x8d, 0x9b, /* 11 */ - 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, - 0xff}, - {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */ - 0xb4, 0xbf, 0xc9, 0xd3, 0xdc, 0xe5, 0xee, 0xf6, - 0xff}, - {0x00, 0x54, 0x6f, 0x83, 0x93, 0xa0, 0xad, 0xb7, /* 13 */ - 0xc2, 0xcb, 0xd4, 0xdc, 0xe4, 0xeb, 0xf2, 0xf9, - 0xff}, - {0x00, 0x6e, 0x88, 0x9a, 0xa8, 0xb3, 0xbd, 0xc6, /* 14 */ - 0xcf, 0xd6, 0xdd, 0xe3, 0xe9, 0xef, 0xf4, 0xfa, - 0xff}, - {0x00, 0x93, 0xa8, 0xb7, 0xc1, 0xca, 0xd2, 0xd8, /* 15 */ - 0xde, 0xe3, 0xe8, 0xed, 0xf1, 0xf5, 0xf8, 0xfc, - 0xff} -}; - -static const u8 tas5130a_sensor_init[][8] = { - {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09}, - {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09}, - {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, -}; - -static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07}; - -/* read 1 byte */ -static u8 reg_r(struct gspca_dev *gspca_dev, - u16 index) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, - gspca_dev->usb_buf, 1, 500); - return gspca_dev->usb_buf[0]; -} - -static void reg_w(struct gspca_dev *gspca_dev, - u16 index) -{ - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, - NULL, 0, 500); -} - -static void reg_w_buf(struct gspca_dev *gspca_dev, - const u8 *buffer, u16 len) -{ - if (len <= USB_BUF_SZ) { - memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x01, 0, - gspca_dev->usb_buf, len, 500); - } else { - u8 *tmpbuf; - - tmpbuf = kmemdup(buffer, len, GFP_KERNEL); - if (!tmpbuf) { - pr_err("Out of memory\n"); - return; - } - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x01, 0, - tmpbuf, len, 500); - kfree(tmpbuf); - } -} - -/* write values to consecutive registers */ -static void reg_w_ixbuf(struct gspca_dev *gspca_dev, - u8 reg, - const u8 *buffer, u16 len) -{ - int i; - u8 *p, *tmpbuf; - - if (len * 2 <= USB_BUF_SZ) { - p = tmpbuf = gspca_dev->usb_buf; - } else { - p = tmpbuf = kmalloc(len * 2, GFP_KERNEL); - if (!tmpbuf) { - pr_err("Out of memory\n"); - return; - } - } - i = len; - while (--i >= 0) { - *p++ = reg++; - *p++ = *buffer++; - } - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x01, 0, - tmpbuf, len * 2, 500); - if (len * 2 > USB_BUF_SZ) - kfree(tmpbuf); -} - -static void om6802_sensor_init(struct gspca_dev *gspca_dev) -{ - int i; - const u8 *p; - u8 byte; - u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05}; - static const u8 sensor_init[] = { - 0xdf, 0x6d, - 0xdd, 0x18, - 0x5a, 0xe0, - 0x5c, 0x07, - 0x5d, 0xb0, - 0x5e, 0x1e, - 0x60, 0x71, - 0xef, 0x00, - 0xe9, 0x00, - 0xea, 0x00, - 0x90, 0x24, - 0x91, 0xb2, - 0x82, 0x32, - 0xfd, 0x41, - 0x00 /* table end */ - }; - - reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); - msleep(100); - i = 4; - while (--i > 0) { - byte = reg_r(gspca_dev, 0x0060); - if (!(byte & 0x01)) - break; - msleep(100); - } - byte = reg_r(gspca_dev, 0x0063); - if (byte != 0x17) { - pr_err("Bad sensor reset %02x\n", byte); - /* continue? */ - } - - p = sensor_init; - while (*p != 0) { - val[1] = *p++; - val[3] = *p++; - if (*p == 0) - reg_w(gspca_dev, 0x3c80); - reg_w_buf(gspca_dev, val, sizeof val); - i = 4; - while (--i >= 0) { - msleep(15); - byte = reg_r(gspca_dev, 0x60); - if (!(byte & 0x01)) - break; - } - } - msleep(15); - reg_w(gspca_dev, 0x3c80); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam = &gspca_dev->cam; - - cam->cam_mode = vga_mode_t16; - cam->nmodes = ARRAY_SIZE(vga_mode_t16); - - return 0; -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) -{ - u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 }; - - if (brightness < 7) { - set6[1] = 0x26; - set6[3] = 0x70 - brightness * 0x10; - } else { - set6[3] = 0x00 + ((brightness - 7) * 0x10); - } - - reg_w_buf(gspca_dev, set6, sizeof set6); -} - -static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast) -{ - u16 reg_to_write; - - if (contrast < 7) - reg_to_write = 0x8ea9 - contrast * 0x200; - else - reg_to_write = 0x00a9 + (contrast - 7) * 0x200; - - reg_w(gspca_dev, reg_to_write); -} - -static void setcolors(struct gspca_dev *gspca_dev, s32 val) -{ - u16 reg_to_write; - - reg_to_write = 0x80bb + val * 0x100; /* was 0xc0 */ - reg_w(gspca_dev, reg_to_write); -} - -static void setgamma(struct gspca_dev *gspca_dev, s32 val) -{ - PDEBUG(D_CONF, "Gamma: %d", sd->gamma); - reg_w_ixbuf(gspca_dev, 0x90, - gamma_table[val], sizeof gamma_table[0]); -} - -static void setawb_n_RGB(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 all_gain_reg[8] = { - 0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00 }; - s32 red_gain, blue_gain, green_gain; - - green_gain = sd->gain->val; - - red_gain = green_gain + sd->red_balance->val; - if (red_gain > 0x40) - red_gain = 0x40; - else if (red_gain < 0x10) - red_gain = 0x10; - - blue_gain = green_gain + sd->blue_balance->val; - if (blue_gain > 0x40) - blue_gain = 0x40; - else if (blue_gain < 0x10) - blue_gain = 0x10; - - all_gain_reg[1] = red_gain; - all_gain_reg[3] = blue_gain; - all_gain_reg[5] = green_gain; - all_gain_reg[7] = sensor_data[sd->sensor].reg80; - if (!sd->awb->val) - all_gain_reg[7] &= ~0x04; /* AWB off */ - - reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - u16 reg_to_write; - - reg_to_write = 0x0aa6 + 0x1000 * val; - - reg_w(gspca_dev, reg_to_write); -} - -static void setfreq(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 reg66; - u8 freq[4] = { 0x66, 0x00, 0xa8, 0xe8 }; - - switch (sd->sensor) { - case SENSOR_LT168G: - if (val != 0) - freq[3] = 0xa8; - reg66 = 0x41; - break; - case SENSOR_OM6802: - reg66 = 0xca; - break; - default: - reg66 = 0x40; - break; - } - switch (val) { - case 0: /* no flicker */ - freq[3] = 0xf0; - break; - case 2: /* 60Hz */ - reg66 &= ~0x40; - break; - } - freq[1] = reg66; - - reg_w_buf(gspca_dev, freq, sizeof freq); -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - /* some of this registers are not really neded, because - * they are overriden by setbrigthness, setcontrast, etc, - * but wont hurt anyway, and can help someone with similar webcam - * to see the initial parameters.*/ - struct sd *sd = (struct sd *) gspca_dev; - const struct additional_sensor_data *sensor; - int i; - u16 sensor_id; - u8 test_byte = 0; - - static const u8 read_indexs[] = - { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, - 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 }; - static const u8 n1[] = - {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; - static const u8 n2[] = - {0x08, 0x00}; - - sensor_id = (reg_r(gspca_dev, 0x06) << 8) - | reg_r(gspca_dev, 0x07); - switch (sensor_id & 0xff0f) { - case 0x0801: - PDEBUG(D_PROBE, "sensor tas5130a"); - sd->sensor = SENSOR_TAS5130A; - break; - case 0x0802: - PDEBUG(D_PROBE, "sensor lt168g"); - sd->sensor = SENSOR_LT168G; - break; - case 0x0803: - PDEBUG(D_PROBE, "sensor 'other'"); - sd->sensor = SENSOR_OTHER; - break; - case 0x0807: - PDEBUG(D_PROBE, "sensor om6802"); - sd->sensor = SENSOR_OM6802; - break; - default: - pr_err("unknown sensor %04x\n", sensor_id); - return -EINVAL; - } - - if (sd->sensor == SENSOR_OM6802) { - reg_w_buf(gspca_dev, n1, sizeof n1); - i = 5; - while (--i >= 0) { - reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); - test_byte = reg_r(gspca_dev, 0x0063); - msleep(100); - if (test_byte == 0x17) - break; /* OK */ - } - if (i < 0) { - pr_err("Bad sensor reset %02x\n", test_byte); - return -EIO; - } - reg_w_buf(gspca_dev, n2, sizeof n2); - } - - i = 0; - while (read_indexs[i] != 0x00) { - test_byte = reg_r(gspca_dev, read_indexs[i]); - PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", read_indexs[i], - test_byte); - i++; - } - - sensor = &sensor_data[sd->sensor]; - reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3); - reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz); - - if (sd->sensor == SENSOR_LT168G) { - test_byte = reg_r(gspca_dev, 0x80); - PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80, - test_byte); - reg_w(gspca_dev, 0x6c80); - } - - reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); - reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); - reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); - - reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); - reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); - reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e); - reg_w(gspca_dev, (0x20 << 8) + 0x87); - reg_w(gspca_dev, (0x20 << 8) + 0x88); - reg_w(gspca_dev, (0x20 << 8) + 0x89); - - reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5); - reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); - reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); - - if (sd->sensor == SENSOR_LT168G) { - test_byte = reg_r(gspca_dev, 0x80); - PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80, - test_byte); - reg_w(gspca_dev, 0x6c80); - } - - reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); - reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); - reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); - - return 0; -} - -static void setmirror(struct gspca_dev *gspca_dev, s32 val) -{ - u8 hflipcmd[8] = - {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}; - - if (val) - hflipcmd[3] = 0x01; - - reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd); -} - -static void seteffect(struct gspca_dev *gspca_dev, s32 val) -{ - int idx = 0; - - switch (val) { - case V4L2_COLORFX_NONE: - break; - case V4L2_COLORFX_BW: - idx = 2; - break; - case V4L2_COLORFX_SEPIA: - idx = 3; - break; - case V4L2_COLORFX_SKETCH: - idx = 4; - break; - case V4L2_COLORFX_NEGATIVE: - idx = 6; - break; - default: - break; - } - - reg_w_buf(gspca_dev, effects_table[idx], - sizeof effects_table[0]); - - if (val == V4L2_COLORFX_SKETCH) - reg_w(gspca_dev, 0x4aa6); - else - reg_w(gspca_dev, 0xfaa6); -} - -/* Is this really needed? - * i added some module parameters for test with some users */ -static void poll_sensor(struct gspca_dev *gspca_dev) -{ - static const u8 poll1[] = - {0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82, - 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34, - 0x74, 0x32, 0x75, 0x92, 0x76, 0x00, 0x09, 0x01, - 0x60, 0x14}; - static const u8 poll2[] = - {0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9, - 0x73, 0x02, 0x73, 0x02, 0x60, 0x14}; - static const u8 noise03[] = /* (some differences / ms-drv) */ - {0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f, - 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c, - 0xc2, 0x80, 0xc3, 0x10}; - - PDEBUG(D_STREAM, "[Sensor requires polling]"); - reg_w_buf(gspca_dev, poll1, sizeof poll1); - reg_w_buf(gspca_dev, poll2, sizeof poll2); - reg_w_buf(gspca_dev, noise03, sizeof noise03); -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - const struct additional_sensor_data *sensor; - int i, mode; - u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; - static const u8 t3[] = - { 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 }; - - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - switch (mode) { - case 0: /* 640x480 (0x00) */ - break; - case 1: /* 352x288 */ - t2[1] = 0x40; - break; - case 2: /* 320x240 */ - t2[1] = 0x10; - break; - case 3: /* 176x144 */ - t2[1] = 0x50; - break; - default: -/* case 4: * 160x120 */ - t2[1] = 0x20; - break; - } - - switch (sd->sensor) { - case SENSOR_OM6802: - om6802_sensor_init(gspca_dev); - break; - case SENSOR_TAS5130A: - i = 0; - for (;;) { - reg_w_buf(gspca_dev, tas5130a_sensor_init[i], - sizeof tas5130a_sensor_init[0]); - if (i >= ARRAY_SIZE(tas5130a_sensor_init) - 1) - break; - i++; - } - reg_w(gspca_dev, 0x3c80); - /* just in case and to keep sync with logs (for mine) */ - reg_w_buf(gspca_dev, tas5130a_sensor_init[i], - sizeof tas5130a_sensor_init[0]); - reg_w(gspca_dev, 0x3c80); - break; - } - sensor = &sensor_data[sd->sensor]; - setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); - reg_r(gspca_dev, 0x0012); - reg_w_buf(gspca_dev, t2, sizeof t2); - reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3); - reg_w(gspca_dev, 0x0013); - msleep(15); - reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); - reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); - - if (sd->sensor == SENSOR_OM6802) - poll_sensor(gspca_dev); - - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, - sizeof sensor_data[sd->sensor].stream); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, - sizeof sensor_data[sd->sensor].stream); - if (sd->sensor == SENSOR_OM6802) { - msleep(20); - reg_w(gspca_dev, 0x0309); - } -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - /* If the last button state is pressed, release it now! */ - if (sd->button_pressed) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - sd->button_pressed = 0; - } -#endif -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - int pkt_type; - - if (data[0] == 0x5a) { -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - if (len > 20) { - u8 state = (data[20] & 0x80) ? 1 : 0; - if (sd->button_pressed != state) { - input_report_key(gspca_dev->input_dev, - KEY_CAMERA, state); - input_sync(gspca_dev->input_dev); - sd->button_pressed = state; - } - } -#endif - /* Control Packet, after this came the header again, - * but extra bytes came in the packet before this, - * sometimes an EOF arrives, sometimes not... */ - return; - } - data += 2; - len -= 2; - if (data[0] == 0xff && data[1] == 0xd8) - pkt_type = FIRST_PACKET; - else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) - pkt_type = LAST_PACKET; - else - pkt_type = INTER_PACKET; - gspca_frame_add(gspca_dev, pkt_type, data, len); -} - -static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - s32 red_gain, blue_gain, green_gain; - - gspca_dev->usb_err = 0; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - red_gain = reg_r(gspca_dev, 0x0087); - if (red_gain > 0x40) - red_gain = 0x40; - else if (red_gain < 0x10) - red_gain = 0x10; - - blue_gain = reg_r(gspca_dev, 0x0088); - if (blue_gain > 0x40) - blue_gain = 0x40; - else if (blue_gain < 0x10) - blue_gain = 0x10; - - green_gain = reg_r(gspca_dev, 0x0089); - if (green_gain > 0x40) - green_gain = 0x40; - else if (green_gain < 0x10) - green_gain = 0x10; - - sd->gain->val = green_gain; - sd->red_balance->val = red_gain - green_gain; - sd->blue_balance->val = blue_gain - green_gain; - break; - } - return 0; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAMMA: - setgamma(gspca_dev, ctrl->val); - break; - case V4L2_CID_HFLIP: - setmirror(gspca_dev, ctrl->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setfreq(gspca_dev, ctrl->val); - break; - case V4L2_CID_BACKLIGHT_COMPENSATION: - reg_w(gspca_dev, ctrl->val ? 0xf48e : 0xb48e); - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - setawb_n_RGB(gspca_dev); - break; - case V4L2_CID_COLORFX: - seteffect(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .g_volatile_ctrl = sd_g_volatile_ctrl, - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 12); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 14, 1, 8); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0x0d, 1, 7); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xf, 1, 5); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10); - /* Activate lowlight, some apps dont bring up the - backlight_compensation control) */ - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1); - if (sd->sensor == SENSOR_TAS5130A) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sd->awb = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0x10, 0x40, 1, 0x20); - sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, 0); - sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, 0); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 15, 1, 6); - v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_COLORFX, V4L2_COLORFX_SKETCH, - ~((1 << V4L2_COLORFX_NONE) | - (1 << V4L2_COLORFX_BW) | - (1 << V4L2_COLORFX_SEPIA) | - (1 << V4L2_COLORFX_SKETCH) | - (1 << V4L2_COLORFX_NEGATIVE)), - V4L2_COLORFX_NONE); - sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, - V4L2_CID_POWER_LINE_FREQUENCY_50HZ); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - - v4l2_ctrl_auto_cluster(4, &sd->awb, 0, true); - - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .other_input = 1, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x17a1, 0x0128)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c deleted file mode 100644 index a6055246cb9d..000000000000 --- a/drivers/media/video/gspca/topro.c +++ /dev/null @@ -1,4969 +0,0 @@ -/* - * Topro TP6800/6810 webcam driver. - * - * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr) - * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se) - * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; If not, see . - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "gspca.h" - -MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver"); -MODULE_AUTHOR("Jean-Francois Moine , " - "Anders Blomdell "); -MODULE_LICENSE("GPL"); - -static int force_sensor = -1; - -/* JPEG header */ -static const u8 jpeg_head[] = { - 0xff, 0xd8, /* jpeg */ - -/* quantization table quality 50% */ - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, -#define JPEG_QT0_OFFSET 7 - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, -1, -#define JPEG_QT1_OFFSET 72 - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - - /* Define Huffman table (thanks to Thomas Kaiser) */ - 0xff, 0xc4, 0x01, 0x5e, - 0x00, 0x00, 0x02, 0x03, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, - 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21, - 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32, - 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52, - 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1, - 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2, - 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25, - 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73, - 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85, - 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, - 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, - 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, - 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02, - 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, - 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01, - 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13, - 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14, - 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1, - 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43, - 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82, - 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28, - 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3, - 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, - 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ - 0x08, /* data precision */ -#define JPEG_HEIGHT_OFFSET 493 - 0x01, 0xe0, /* height */ - 0x02, 0x80, /* width */ - 0x03, /* component number */ - 0x01, - 0x21, /* samples Y = jpeg 422 */ - 0x00, /* quant Y */ - 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ - 0x03, 0x11, 0x01, - - 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ - 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -#define JPEG_HDR_SZ 521 -}; - -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl *jpegqual; - struct v4l2_ctrl *sharpness; - struct v4l2_ctrl *gamma; - struct v4l2_ctrl *blue; - struct v4l2_ctrl *red; - - u8 framerate; - u8 quality; /* webcam current JPEG quality (0..16) */ - s8 ag_cnt; /* autogain / start counter for tp6810 */ -#define AG_CNT_START 13 /* check gain every N frames */ - - u8 bridge; - u8 sensor; - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; - -enum bridges { - BRIDGE_TP6800, - BRIDGE_TP6810, -}; - -enum sensors { - SENSOR_CX0342, - SENSOR_SOI763A, /* ~= ov7630 / ov7648 */ - NSENSORS -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG} -}; - -/* - * JPEG quality - * index: webcam compression - * value: JPEG quality in % - */ -static const u8 jpeg_q[17] = { - 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94 -}; - -#define BULK_OUT_SIZE 0x20 -#if BULK_OUT_SIZE > USB_BUF_SZ -#error "USB buffer too small" -#endif - -static const u8 rates[] = {30, 20, 15, 10, 7, 5}; -static const struct framerates framerates[] = { - { - .rates = rates, - .nrates = ARRAY_SIZE(rates) - }, - { - .rates = rates, - .nrates = ARRAY_SIZE(rates) - } -}; -static const u8 rates_6810[] = {30, 15, 10, 7, 5}; -static const struct framerates framerates_6810[] = { - { - .rates = rates_6810, - .nrates = ARRAY_SIZE(rates_6810) - }, - { - .rates = rates_6810, - .nrates = ARRAY_SIZE(rates_6810) - } -}; - -/* - * webcam quality in % - * the last value is the ultra fine quality - */ - -/* TP6800 register offsets */ -#define TP6800_R10_SIF_TYPE 0x10 -#define TP6800_R11_SIF_CONTROL 0x11 -#define TP6800_R12_SIF_ADDR_S 0x12 -#define TP6800_R13_SIF_TX_DATA 0x13 -#define TP6800_R14_SIF_RX_DATA 0x14 -#define TP6800_R15_GPIO_PU 0x15 -#define TP6800_R16_GPIO_PD 0x16 -#define TP6800_R17_GPIO_IO 0x17 -#define TP6800_R18_GPIO_DATA 0x18 -#define TP6800_R19_SIF_ADDR_S2 0x19 -#define TP6800_R1A_SIF_TX_DATA2 0x1a -#define TP6800_R1B_SIF_RX_DATA2 0x1b -#define TP6800_R21_ENDP_1_CTL 0x21 -#define TP6800_R2F_TIMING_CFG 0x2f -#define TP6800_R30_SENSOR_CFG 0x30 -#define TP6800_R31_PIXEL_START 0x31 -#define TP6800_R32_PIXEL_END_L 0x32 -#define TP6800_R33_PIXEL_END_H 0x33 -#define TP6800_R34_LINE_START 0x34 -#define TP6800_R35_LINE_END_L 0x35 -#define TP6800_R36_LINE_END_H 0x36 -#define TP6800_R37_FRONT_DARK_ST 0x37 -#define TP6800_R38_FRONT_DARK_END 0x38 -#define TP6800_R39_REAR_DARK_ST_L 0x39 -#define TP6800_R3A_REAR_DARK_ST_H 0x3a -#define TP6800_R3B_REAR_DARK_END_L 0x3b -#define TP6800_R3C_REAR_DARK_END_H 0x3c -#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d -#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e -#define TP6800_R3F_FRAME_RATE 0x3f -#define TP6800_R50 0x50 -#define TP6800_R51 0x51 -#define TP6800_R52 0x52 -#define TP6800_R53 0x53 -#define TP6800_R54_DARK_CFG 0x54 -#define TP6800_R55_GAMMA_R 0x55 -#define TP6800_R56_GAMMA_G 0x56 -#define TP6800_R57_GAMMA_B 0x57 -#define TP6800_R5C_EDGE_THRLD 0x5c -#define TP6800_R5D_DEMOSAIC_CFG 0x5d -#define TP6800_R78_FORMAT 0x78 -#define TP6800_R79_QUALITY 0x79 -#define TP6800_R7A_BLK_THRLD 0x7a - -/* CX0342 register offsets */ - -#define CX0342_SENSOR_ID 0x00 -#define CX0342_VERSION_NO 0x01 -#define CX0342_ORG_X_L 0x02 -#define CX0342_ORG_X_H 0x03 -#define CX0342_ORG_Y_L 0x04 -#define CX0342_ORG_Y_H 0x05 -#define CX0342_STOP_X_L 0x06 -#define CX0342_STOP_X_H 0x07 -#define CX0342_STOP_Y_L 0x08 -#define CX0342_STOP_Y_H 0x09 -#define CX0342_FRAME_WIDTH_L 0x0a -#define CX0342_FRAME_WIDTH_H 0x0b -#define CX0342_FRAME_HEIGH_L 0x0c -#define CX0342_FRAME_HEIGH_H 0x0d -#define CX0342_EXPO_LINE_L 0x10 -#define CX0342_EXPO_LINE_H 0x11 -#define CX0342_EXPO_CLK_L 0x12 -#define CX0342_EXPO_CLK_H 0x13 -#define CX0342_RAW_GRGAIN_L 0x14 -#define CX0342_RAW_GRGAIN_H 0x15 -#define CX0342_RAW_GBGAIN_L 0x16 -#define CX0342_RAW_GBGAIN_H 0x17 -#define CX0342_RAW_RGAIN_L 0x18 -#define CX0342_RAW_RGAIN_H 0x19 -#define CX0342_RAW_BGAIN_L 0x1a -#define CX0342_RAW_BGAIN_H 0x1b -#define CX0342_GLOBAL_GAIN 0x1c -#define CX0342_SYS_CTRL_0 0x20 -#define CX0342_SYS_CTRL_1 0x21 -#define CX0342_SYS_CTRL_2 0x22 -#define CX0342_BYPASS_MODE 0x23 -#define CX0342_SYS_CTRL_3 0x24 -#define CX0342_TIMING_EN 0x25 -#define CX0342_OUTPUT_CTRL 0x26 -#define CX0342_AUTO_ADC_CALIB 0x27 -#define CX0342_SYS_CTRL_4 0x28 -#define CX0342_ADCGN 0x30 -#define CX0342_SLPCR 0x31 -#define CX0342_SLPFN_LO 0x32 -#define CX0342_ADC_CTL 0x33 -#define CX0342_LVRST_BLBIAS 0x34 -#define CX0342_VTHSEL 0x35 -#define CX0342_RAMP_RIV 0x36 -#define CX0342_LDOSEL 0x37 -#define CX0342_CLOCK_GEN 0x40 -#define CX0342_SOFT_RESET 0x41 -#define CX0342_PLL 0x42 -#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43 -#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44 -#define CX0342_DR_ENH_PULSE_POS_L 0x45 -#define CX0342_DR_ENH_PULSE_POS_H 0x46 -#define CX0342_DR_ENH_PULSE_WIDTH 0x47 -#define CX0342_AS_CURRENT_CNT_L 0x48 -#define CX0342_AS_CURRENT_CNT_H 0x49 -#define CX0342_AS_PREVIOUS_CNT_L 0x4a -#define CX0342_AS_PREVIOUS_CNT_H 0x4b -#define CX0342_SPV_VALUE_L 0x4c -#define CX0342_SPV_VALUE_H 0x4d -#define CX0342_GPXLTHD_L 0x50 -#define CX0342_GPXLTHD_H 0x51 -#define CX0342_RBPXLTHD_L 0x52 -#define CX0342_RBPXLTHD_H 0x53 -#define CX0342_PLANETHD_L 0x54 -#define CX0342_PLANETHD_H 0x55 -#define CX0342_ROWDARK_TH 0x56 -#define CX0342_ROWDARK_TOL 0x57 -#define CX0342_RB_GAP_L 0x58 -#define CX0342_RB_GAP_H 0x59 -#define CX0342_G_GAP_L 0x5a -#define CX0342_G_GAP_H 0x5b -#define CX0342_AUTO_ROW_DARK 0x60 -#define CX0342_MANUAL_DARK_VALUE 0x61 -#define CX0342_GB_DARK_OFFSET 0x62 -#define CX0342_GR_DARK_OFFSET 0x63 -#define CX0342_RED_DARK_OFFSET 0x64 -#define CX0342_BLUE_DARK_OFFSET 0x65 -#define CX0342_DATA_SCALING_MULTI 0x66 -#define CX0342_AUTOD_Q_FRAME 0x67 -#define CX0342_AUTOD_ALLOW_VARI 0x68 -#define CX0342_AUTO_DARK_VALUE_L 0x69 -#define CX0342_AUTO_DARK_VALUE_H 0x6a -#define CX0342_IO_CTRL_0 0x70 -#define CX0342_IO_CTRL_1 0x71 -#define CX0342_IO_CTRL_2 0x72 -#define CX0342_IDLE_CTRL 0x73 -#define CX0342_TEST_MODE 0x74 -#define CX0342_FRAME_FIX_DATA_TEST 0x75 -#define CX0342_FRAME_CNT_TEST 0x76 -#define CX0342_RST_OVERFLOW_L 0x80 -#define CX0342_RST_OVERFLOW_H 0x81 -#define CX0342_RST_UNDERFLOW_L 0x82 -#define CX0342_RST_UNDERFLOW_H 0x83 -#define CX0342_DATA_OVERFLOW_L 0x84 -#define CX0342_DATA_OVERFLOW_H 0x85 -#define CX0342_DATA_UNDERFLOW_L 0x86 -#define CX0342_DATA_UNDERFLOW_H 0x87 -#define CX0342_CHANNEL_0_0_L_irst 0x90 -#define CX0342_CHANNEL_0_0_H_irst 0x91 -#define CX0342_CHANNEL_0_1_L_irst 0x92 -#define CX0342_CHANNEL_0_1_H_irst 0x93 -#define CX0342_CHANNEL_0_2_L_irst 0x94 -#define CX0342_CHANNEL_0_2_H_irst 0x95 -#define CX0342_CHANNEL_0_3_L_irst 0x96 -#define CX0342_CHANNEL_0_3_H_irst 0x97 -#define CX0342_CHANNEL_0_4_L_irst 0x98 -#define CX0342_CHANNEL_0_4_H_irst 0x99 -#define CX0342_CHANNEL_0_5_L_irst 0x9a -#define CX0342_CHANNEL_0_5_H_irst 0x9b -#define CX0342_CHANNEL_0_6_L_irst 0x9c -#define CX0342_CHANNEL_0_6_H_irst 0x9d -#define CX0342_CHANNEL_0_7_L_irst 0x9e -#define CX0342_CHANNEL_0_7_H_irst 0x9f -#define CX0342_CHANNEL_1_0_L_itx 0xa0 -#define CX0342_CHANNEL_1_0_H_itx 0xa1 -#define CX0342_CHANNEL_1_1_L_itx 0xa2 -#define CX0342_CHANNEL_1_1_H_itx 0xa3 -#define CX0342_CHANNEL_1_2_L_itx 0xa4 -#define CX0342_CHANNEL_1_2_H_itx 0xa5 -#define CX0342_CHANNEL_1_3_L_itx 0xa6 -#define CX0342_CHANNEL_1_3_H_itx 0xa7 -#define CX0342_CHANNEL_1_4_L_itx 0xa8 -#define CX0342_CHANNEL_1_4_H_itx 0xa9 -#define CX0342_CHANNEL_1_5_L_itx 0xaa -#define CX0342_CHANNEL_1_5_H_itx 0xab -#define CX0342_CHANNEL_1_6_L_itx 0xac -#define CX0342_CHANNEL_1_6_H_itx 0xad -#define CX0342_CHANNEL_1_7_L_itx 0xae -#define CX0342_CHANNEL_1_7_H_itx 0xaf -#define CX0342_CHANNEL_2_0_L_iwl 0xb0 -#define CX0342_CHANNEL_2_0_H_iwl 0xb1 -#define CX0342_CHANNEL_2_1_L_iwl 0xb2 -#define CX0342_CHANNEL_2_1_H_iwl 0xb3 -#define CX0342_CHANNEL_2_2_L_iwl 0xb4 -#define CX0342_CHANNEL_2_2_H_iwl 0xb5 -#define CX0342_CHANNEL_2_3_L_iwl 0xb6 -#define CX0342_CHANNEL_2_3_H_iwl 0xb7 -#define CX0342_CHANNEL_2_4_L_iwl 0xb8 -#define CX0342_CHANNEL_2_4_H_iwl 0xb9 -#define CX0342_CHANNEL_2_5_L_iwl 0xba -#define CX0342_CHANNEL_2_5_H_iwl 0xbb -#define CX0342_CHANNEL_2_6_L_iwl 0xbc -#define CX0342_CHANNEL_2_6_H_iwl 0xbd -#define CX0342_CHANNEL_2_7_L_iwl 0xbe -#define CX0342_CHANNEL_2_7_H_iwl 0xbf -#define CX0342_CHANNEL_3_0_L_ensp 0xc0 -#define CX0342_CHANNEL_3_0_H_ensp 0xc1 -#define CX0342_CHANNEL_3_1_L_ensp 0xc2 -#define CX0342_CHANNEL_3_1_H_ensp 0xc3 -#define CX0342_CHANNEL_3_2_L_ensp 0xc4 -#define CX0342_CHANNEL_3_2_H_ensp 0xc5 -#define CX0342_CHANNEL_3_3_L_ensp 0xc6 -#define CX0342_CHANNEL_3_3_H_ensp 0xc7 -#define CX0342_CHANNEL_3_4_L_ensp 0xc8 -#define CX0342_CHANNEL_3_4_H_ensp 0xc9 -#define CX0342_CHANNEL_3_5_L_ensp 0xca -#define CX0342_CHANNEL_3_5_H_ensp 0xcb -#define CX0342_CHANNEL_3_6_L_ensp 0xcc -#define CX0342_CHANNEL_3_6_H_ensp 0xcd -#define CX0342_CHANNEL_3_7_L_ensp 0xce -#define CX0342_CHANNEL_3_7_H_ensp 0xcf -#define CX0342_CHANNEL_4_0_L_sela 0xd0 -#define CX0342_CHANNEL_4_0_H_sela 0xd1 -#define CX0342_CHANNEL_4_1_L_sela 0xd2 -#define CX0342_CHANNEL_4_1_H_sela 0xd3 -#define CX0342_CHANNEL_5_0_L_intla 0xe0 -#define CX0342_CHANNEL_5_0_H_intla 0xe1 -#define CX0342_CHANNEL_5_1_L_intla 0xe2 -#define CX0342_CHANNEL_5_1_H_intla 0xe3 -#define CX0342_CHANNEL_5_2_L_intla 0xe4 -#define CX0342_CHANNEL_5_2_H_intla 0xe5 -#define CX0342_CHANNEL_5_3_L_intla 0xe6 -#define CX0342_CHANNEL_5_3_H_intla 0xe7 -#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0 -#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1 -#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2 -#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3 -#define CX0342_SENSOR_HEIGHT_L 0xfb -#define CX0342_SENSOR_HEIGHT_H 0xfc -#define CX0342_SENSOR_WIDTH_L 0xfd -#define CX0342_SENSOR_WIDTH_H 0xfe -#define CX0342_VSYNC_HSYNC_READ 0xff - -struct cmd { - u8 reg; - u8 val; -}; - -static const u8 DQT[17][130] = { - /* Define quantization table (thanks to Thomas Kaiser) */ - { /* Quality 0 */ - 0x00, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x01, - 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06, - 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - }, - { /* Quality 1 */ - 0x00, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x01, - 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d, - 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - }, - { /* Quality 2 */ - 0x00, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04, - 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x01, - 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13, - 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - }, - { /* Quality 3 */ - 0x00, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, - 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x01, - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - }, - { /* Quality 4 */ - 0x00, - 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05, - 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x01, - 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20, - 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - }, - { /* Quality 5 */ - 0x00, - 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06, - 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x01, - 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27, - 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - }, - { /* Quality 6 */ - 0x00, - 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07, - 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x01, - 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d, - 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - }, - { /* Quality 7 */ - 0x00, - 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, - 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x01, - 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, - 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - }, - { /* Quality 8 */ - 0x00, - 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a, - 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x01, - 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41, - 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - }, - { /* Quality 9 */ - 0x00, - 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c, - 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x01, - 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e, - 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - { /* Quality 10 */ - 0x00, - 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e, - 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x01, - 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b, - 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - { /* Quality 11 */ - 0x00, - 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, - 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, - 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, - 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, - 0x01, - 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68, - 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - { /* Quality 12 */ - 0x00, - 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14, - 0x14, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x01, - 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82, - 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - { /* Quality 13 */ - 0x00, - 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, - 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72, - 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, - 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, - 0x01, - 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c, - 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - { /* Quality 14 */ - 0x00, - 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c, - 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x01, - 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6, - 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - { /* Quality 15 */ - 0x00, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, - 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x01, - 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0, - 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - { /* Quality 16-31 */ - 0x00, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x01, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - } -}; - -static const struct cmd tp6810_cx_init_common[] = { - {0x1c, 0x00}, - {TP6800_R10_SIF_TYPE, 0x00}, - {0x4e, 0x00}, - {0x4f, 0x00}, - {TP6800_R50, 0xff}, - {TP6800_R51, 0x03}, - {0x00, 0x07}, - {TP6800_R79_QUALITY, 0x03}, - {TP6800_R2F_TIMING_CFG, 0x37}, - {TP6800_R30_SENSOR_CFG, 0x10}, - {TP6800_R21_ENDP_1_CTL, 0x00}, - {TP6800_R52, 0x40}, - {TP6800_R53, 0x40}, - {TP6800_R54_DARK_CFG, 0x40}, - {TP6800_R30_SENSOR_CFG, 0x18}, - {0x4b, 0x00}, - {TP6800_R3F_FRAME_RATE, 0x83}, - {TP6800_R79_QUALITY, 0x05}, - {TP6800_R21_ENDP_1_CTL, 0x00}, - {0x7c, 0x04}, - {0x25, 0x14}, - {0x26, 0x0f}, - {0x7b, 0x10}, -}; - -static const struct cmd tp6810_ov_init_common[] = { - {0x1c, 0x00}, - {TP6800_R10_SIF_TYPE, 0x00}, - {0x4e, 0x00}, - {0x4f, 0x00}, - {TP6800_R50, 0xff}, - {TP6800_R51, 0x03}, - {0x00, 0x07}, - {TP6800_R52, 0x40}, - {TP6800_R53, 0x40}, - {TP6800_R54_DARK_CFG, 0x40}, - {TP6800_R79_QUALITY, 0x03}, - {TP6800_R2F_TIMING_CFG, 0x17}, - {TP6800_R30_SENSOR_CFG, 0x18}, - {TP6800_R21_ENDP_1_CTL, 0x00}, - {TP6800_R3F_FRAME_RATE, 0x86}, - {0x25, 0x18}, - {0x26, 0x0f}, - {0x7b, 0x90}, -}; - -static const struct cmd tp6810_bridge_start[] = { - {0x59, 0x88}, - {0x5a, 0x0f}, - {0x5b, 0x4e}, - {TP6800_R5C_EDGE_THRLD, 0x63}, - {TP6800_R5D_DEMOSAIC_CFG, 0x00}, - {0x03, 0x7f}, - {0x04, 0x80}, - {0x06, 0x00}, - {0x00, 0x00}, -}; - -static const struct cmd tp6810_late_start[] = { - {0x7d, 0x01}, - {0xb0, 0x04}, - {0xb1, 0x04}, - {0xb2, 0x04}, - {0xb3, 0x04}, - {0xb4, 0x04}, - {0xb5, 0x04}, - {0xb6, 0x08}, - {0xb7, 0x08}, - {0xb8, 0x04}, - {0xb9, 0x04}, - {0xba, 0x04}, - {0xbb, 0x04}, - {0xbc, 0x04}, - {0xbd, 0x08}, - {0xbe, 0x08}, - {0xbf, 0x08}, - {0xc0, 0x04}, - {0xc1, 0x04}, - {0xc2, 0x08}, - {0xc3, 0x08}, - {0xc4, 0x08}, - {0xc5, 0x08}, - {0xc6, 0x08}, - {0xc7, 0x13}, - {0xc8, 0x04}, - {0xc9, 0x08}, - {0xca, 0x08}, - {0xcb, 0x08}, - {0xcc, 0x08}, - {0xcd, 0x08}, - {0xce, 0x13}, - {0xcf, 0x13}, - {0xd0, 0x08}, - {0xd1, 0x08}, - {0xd2, 0x08}, - {0xd3, 0x08}, - {0xd4, 0x08}, - {0xd5, 0x13}, - {0xd6, 0x13}, - {0xd7, 0x13}, - {0xd8, 0x08}, - {0xd9, 0x08}, - {0xda, 0x08}, - {0xdb, 0x08}, - {0xdc, 0x13}, - {0xdd, 0x13}, - {0xde, 0x13}, - {0xdf, 0x13}, - {0xe0, 0x08}, - {0xe1, 0x08}, - {0xe2, 0x08}, - {0xe3, 0x13}, - {0xe4, 0x13}, - {0xe5, 0x13}, - {0xe6, 0x13}, - {0xe7, 0x13}, - {0xe8, 0x08}, - {0xe9, 0x08}, - {0xea, 0x13}, - {0xeb, 0x13}, - {0xec, 0x13}, - {0xed, 0x13}, - {0xee, 0x13}, - {0xef, 0x13}, - {0x7d, 0x02}, - - /* later after isoc start */ - {0x7d, 0x08}, - {0x7d, 0x00}, -}; - -static const struct cmd cx0342_timing_seq[] = { - {CX0342_CHANNEL_0_1_L_irst, 0x20}, - {CX0342_CHANNEL_0_2_L_irst, 0x24}, - {CX0342_CHANNEL_0_2_H_irst, 0x00}, - {CX0342_CHANNEL_0_3_L_irst, 0x2f}, - {CX0342_CHANNEL_0_3_H_irst, 0x00}, - {CX0342_CHANNEL_1_0_L_itx, 0x02}, - {CX0342_CHANNEL_1_0_H_itx, 0x00}, - {CX0342_CHANNEL_1_1_L_itx, 0x20}, - {CX0342_CHANNEL_1_1_H_itx, 0x00}, - {CX0342_CHANNEL_1_2_L_itx, 0xe4}, - {CX0342_CHANNEL_1_2_H_itx, 0x00}, - {CX0342_CHANNEL_1_3_L_itx, 0xee}, - {CX0342_CHANNEL_1_3_H_itx, 0x00}, - {CX0342_CHANNEL_2_0_L_iwl, 0x30}, - {CX0342_CHANNEL_2_0_H_iwl, 0x00}, - {CX0342_CHANNEL_3_0_L_ensp, 0x34}, - {CX0342_CHANNEL_3_1_L_ensp, 0xe2}, - {CX0342_CHANNEL_3_1_H_ensp, 0x00}, - {CX0342_CHANNEL_3_2_L_ensp, 0xf6}, - {CX0342_CHANNEL_3_2_H_ensp, 0x00}, - {CX0342_CHANNEL_3_3_L_ensp, 0xf4}, - {CX0342_CHANNEL_3_3_H_ensp, 0x02}, - {CX0342_CHANNEL_4_0_L_sela, 0x26}, - {CX0342_CHANNEL_4_0_H_sela, 0x00}, - {CX0342_CHANNEL_4_1_L_sela, 0xe2}, - {CX0342_CHANNEL_4_1_H_sela, 0x00}, - {CX0342_CHANNEL_5_0_L_intla, 0x26}, - {CX0342_CHANNEL_5_1_L_intla, 0x29}, - {CX0342_CHANNEL_5_2_L_intla, 0xf0}, - {CX0342_CHANNEL_5_2_H_intla, 0x00}, - {CX0342_CHANNEL_5_3_L_intla, 0xf3}, - {CX0342_CHANNEL_5_3_H_intla, 0x00}, - {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24}, - {CX0342_CHANNEL_7_1_L_cds_pos, 0x02}, - {CX0342_TIMING_EN, 0x01}, -}; - -/* define the JPEG header */ -static void jpeg_define(u8 *jpeg_hdr, - int height, - int width) -{ - memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); - jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width; -} - -/* set the JPEG quality for sensor soi763a */ -static void jpeg_set_qual(u8 *jpeg_hdr, - int quality) -{ - int i, sc; - - if (quality < 50) - sc = 5000 / quality; - else - sc = 200 - quality * 2; - for (i = 0; i < 64; i++) { - jpeg_hdr[JPEG_QT0_OFFSET + i] = - (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; - jpeg_hdr[JPEG_QT1_OFFSET + i] = - (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; - } -} - -static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x0e, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -/* the returned value is in gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, u8 index) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x0d, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, gspca_dev->usb_buf, 1, 500); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static void reg_w_buf(struct gspca_dev *gspca_dev, - const struct cmd *p, int l) -{ - do { - reg_w(gspca_dev, p->reg, p->val); - p++; - } while (--l > 0); -} - -static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); - reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index); - reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value); - reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01); - if (sd->bridge == BRIDGE_TP6800) - return 0; - msleep(5); - reg_r(gspca_dev, TP6800_R11_SIF_CONTROL); - if (gspca_dev->usb_buf[0] == 0) - return 0; - reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); - return -1; /* error */ -} - -static void i2c_w_buf(struct gspca_dev *gspca_dev, - const struct cmd *p, int l) -{ - do { - i2c_w(gspca_dev, p->reg, p->val); - p++; - } while (--l > 0); -} - -static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - int v; - - reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index); - reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02); - msleep(5); - reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA); - v = gspca_dev->usb_buf[0]; - if (sd->bridge == BRIDGE_TP6800) - return v; - if (len > 1) { - reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2); - v |= (gspca_dev->usb_buf[0] << 8); - } - reg_r(gspca_dev, TP6800_R11_SIF_CONTROL); - if (gspca_dev->usb_buf[0] == 0) - return v; - reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); - return -1; -} - -static void bulk_w(struct gspca_dev *gspca_dev, - u8 tag, - const u8 *data, - int length) -{ - struct usb_device *dev = gspca_dev->dev; - int count, actual_count, ret; - - if (gspca_dev->usb_err < 0) - return; - for (;;) { - count = length > BULK_OUT_SIZE - 1 - ? BULK_OUT_SIZE - 1 : length; - gspca_dev->usb_buf[0] = tag; - memcpy(&gspca_dev->usb_buf[1], data, count); - ret = usb_bulk_msg(dev, - usb_sndbulkpipe(dev, 3), - gspca_dev->usb_buf, count + 1, - &actual_count, 500); - if (ret < 0) { - pr_err("bulk write error %d tag=%02x\n", - ret, tag); - gspca_dev->usb_err = ret; - return; - } - length -= count; - if (length <= 0) - break; - data += count; - } -} - -static int probe_6810(struct gspca_dev *gspca_dev) -{ - u8 gpio; - int ret; - - reg_r(gspca_dev, TP6800_R18_GPIO_DATA); - gpio = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */ - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */ - reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00); - if (i2c_w(gspca_dev, 0x00, 0x00) >= 0) - return SENSOR_SOI763A; - - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */ - if (i2c_w(gspca_dev, 0x00, 0x00) >= 0) - return -2; - - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */ - ret = i2c_r(gspca_dev, 0x00, 1); - if (ret > 0) - return -3; - - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */ - ret = i2c_r(gspca_dev, 0x00, 1); - if (ret > 0) - return -4; - - ret = i2c_r(gspca_dev, 0x01, 1); - if (ret > 0) - return -5; - - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */ - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */ - ret = i2c_r(gspca_dev, 0x00, 2); - if (ret > 0) - return -6; - - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */ - ret = i2c_r(gspca_dev, 0x36, 2); - if (ret > 0) - return -7; - - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */ - reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10); - if (i2c_w(gspca_dev, 0xff, 0x00) >= 0) - return -8; - - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); - reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */ - ret = i2c_r(gspca_dev, 0x00, 1); - if (ret > 0) - return SENSOR_CX0342; - return -9; -} - -static void cx0342_6810_init(struct gspca_dev *gspca_dev) -{ - static const struct cmd reg_init_1[] = { - {TP6800_R2F_TIMING_CFG, 0x2f}, - {0x25, 0x02}, - {TP6800_R21_ENDP_1_CTL, 0x00}, - {TP6800_R3F_FRAME_RATE, 0x80}, - {TP6800_R2F_TIMING_CFG, 0x2f}, - {TP6800_R18_GPIO_DATA, 0xe1}, - {TP6800_R18_GPIO_DATA, 0xc1}, - {TP6800_R18_GPIO_DATA, 0xe1}, - {TP6800_R11_SIF_CONTROL, 0x00}, - }; - static const struct cmd reg_init_2[] = { - {TP6800_R78_FORMAT, 0x48}, - {TP6800_R11_SIF_CONTROL, 0x00}, - }; - static const struct cmd sensor_init[] = { - {CX0342_OUTPUT_CTRL, 0x07}, - {CX0342_BYPASS_MODE, 0x58}, - {CX0342_GPXLTHD_L, 0x28}, - {CX0342_RBPXLTHD_L, 0x28}, - {CX0342_PLANETHD_L, 0x50}, - {CX0342_PLANETHD_H, 0x03}, - {CX0342_RB_GAP_L, 0xff}, - {CX0342_RB_GAP_H, 0x07}, - {CX0342_G_GAP_L, 0xff}, - {CX0342_G_GAP_H, 0x07}, - {CX0342_RST_OVERFLOW_L, 0x5c}, - {CX0342_RST_OVERFLOW_H, 0x01}, - {CX0342_DATA_OVERFLOW_L, 0xfc}, - {CX0342_DATA_OVERFLOW_H, 0x03}, - {CX0342_DATA_UNDERFLOW_L, 0x00}, - {CX0342_DATA_UNDERFLOW_H, 0x00}, - {CX0342_SYS_CTRL_0, 0x40}, - {CX0342_GLOBAL_GAIN, 0x01}, - {CX0342_CLOCK_GEN, 0x00}, - {CX0342_SYS_CTRL_0, 0x02}, - {CX0342_IDLE_CTRL, 0x05}, - {CX0342_ADCGN, 0x00}, - {CX0342_ADC_CTL, 0x00}, - {CX0342_LVRST_BLBIAS, 0x01}, - {CX0342_VTHSEL, 0x0b}, - {CX0342_RAMP_RIV, 0x0b}, - {CX0342_LDOSEL, 0x07}, - {CX0342_SPV_VALUE_L, 0x40}, - {CX0342_SPV_VALUE_H, 0x02}, - - {CX0342_AUTO_ADC_CALIB, 0x81}, - {CX0342_TIMING_EN, 0x01}, - }; - - reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1)); - reg_w_buf(gspca_dev, tp6810_cx_init_common, - ARRAY_SIZE(tp6810_cx_init_common)); - reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2)); - - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */ - i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); - i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq)); -} - -static void soi763a_6810_init(struct gspca_dev *gspca_dev) -{ - static const struct cmd reg_init_1[] = { - {TP6800_R2F_TIMING_CFG, 0x2f}, - {TP6800_R18_GPIO_DATA, 0xe1}, - {0x25, 0x02}, - {TP6800_R21_ENDP_1_CTL, 0x00}, - {TP6800_R3F_FRAME_RATE, 0x80}, - {TP6800_R2F_TIMING_CFG, 0x2f}, - {TP6800_R18_GPIO_DATA, 0xc1}, - }; - static const struct cmd reg_init_2[] = { - {TP6800_R78_FORMAT, 0x54}, - }; - static const struct cmd sensor_init[] = { - {0x00, 0x00}, - {0x01, 0x80}, - {0x02, 0x80}, - {0x03, 0x90}, - {0x04, 0x20}, - {0x05, 0x20}, - {0x06, 0x80}, - {0x07, 0x00}, - {0x08, 0xff}, - {0x09, 0xff}, - {0x0a, 0x76}, /* 7630 = soi673a */ - {0x0b, 0x30}, - {0x0c, 0x20}, - {0x0d, 0x20}, - {0x0e, 0xff}, - {0x0f, 0xff}, - {0x10, 0x41}, - {0x15, 0x14}, - {0x11, 0x40}, - {0x12, 0x48}, - {0x13, 0x80}, - {0x14, 0x80}, - {0x16, 0x03}, - {0x28, 0xb0}, - {0x71, 0x20}, - {0x75, 0x8e}, - {0x17, 0x1b}, - {0x18, 0xbd}, - {0x19, 0x05}, - {0x1a, 0xf6}, - {0x1b, 0x04}, - {0x1c, 0x7f}, /* omnivision */ - {0x1d, 0xa2}, - {0x1e, 0x00}, - {0x1f, 0x00}, - {0x20, 0x45}, - {0x21, 0x80}, - {0x22, 0x80}, - {0x23, 0xee}, - {0x24, 0x50}, - {0x25, 0x7a}, - {0x26, 0xa0}, - {0x27, 0x9a}, - {0x29, 0x30}, - {0x2a, 0x80}, - {0x2b, 0x00}, - {0x2c, 0xac}, - {0x2d, 0x05}, - {0x2e, 0x80}, - {0x2f, 0x3c}, - {0x30, 0x22}, - {0x31, 0x00}, - {0x32, 0x86}, - {0x33, 0x08}, - {0x34, 0xff}, - {0x35, 0xff}, - {0x36, 0xff}, - {0x37, 0xff}, - {0x38, 0xff}, - {0x39, 0xff}, - {0x3a, 0xfe}, - {0x3b, 0xfe}, - {0x3c, 0xfe}, - {0x3d, 0xfe}, - {0x3e, 0xfe}, - {0x3f, 0x71}, - {0x40, 0xff}, - {0x41, 0xff}, - {0x42, 0xff}, - {0x43, 0xff}, - {0x44, 0xff}, - {0x45, 0xff}, - {0x46, 0xff}, - {0x47, 0xff}, - {0x48, 0xff}, - {0x49, 0xff}, - {0x4a, 0xfe}, - {0x4b, 0xff}, - {0x4c, 0x00}, - {0x4d, 0x00}, - {0x4e, 0xff}, - {0x4f, 0xff}, - {0x50, 0xff}, - {0x51, 0xff}, - {0x52, 0xff}, - {0x53, 0xff}, - {0x54, 0xff}, - {0x55, 0xff}, - {0x56, 0xff}, - {0x57, 0xff}, - {0x58, 0xff}, - {0x59, 0xff}, - {0x5a, 0xff}, - {0x5b, 0xfe}, - {0x5c, 0xff}, - {0x5d, 0x8f}, - {0x5e, 0xff}, - {0x5f, 0x8f}, - {0x60, 0xa2}, - {0x61, 0x4a}, - {0x62, 0xf3}, - {0x63, 0x75}, - {0x64, 0xf0}, - {0x65, 0x00}, - {0x66, 0x55}, - {0x67, 0x92}, - {0x68, 0xa0}, - {0x69, 0x4a}, - {0x6a, 0x22}, - {0x6b, 0x00}, - {0x6c, 0x33}, - {0x6d, 0x44}, - {0x6e, 0x22}, - {0x6f, 0x84}, - {0x70, 0x0b}, - {0x72, 0x10}, - {0x73, 0x50}, - {0x74, 0x21}, - {0x76, 0x00}, - {0x77, 0xa5}, - {0x78, 0x80}, - {0x79, 0x80}, - {0x7a, 0x80}, - {0x7b, 0xe2}, - {0x7c, 0x00}, - {0x7d, 0xf7}, - {0x7e, 0x00}, - {0x7f, 0x00}, - }; - - reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1)); - reg_w_buf(gspca_dev, tp6810_ov_init_common, - ARRAY_SIZE(tp6810_ov_init_common)); - reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2)); - - i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */ - msleep(10); - i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); -} - -/* set the gain and exposure */ -static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain, - s32 blue, s32 red) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_CX0342) { - expo = (expo << 2) - 1; - i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo); - i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8); - if (sd->bridge == BRIDGE_TP6800) - i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H, - gain >> 8); - i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain); - if (sd->bridge == BRIDGE_TP6800) - i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H, - gain >> 8); - i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain); - if (sd->sensor == SENSOR_CX0342) { - if (sd->bridge == BRIDGE_TP6800) - i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, - blue >> 8); - i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue); - if (sd->bridge == BRIDGE_TP6800) - i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, - red >> 8); - i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red); - } - i2c_w(gspca_dev, CX0342_SYS_CTRL_0, - sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81); - return; - } - - /* soi763a */ - i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */ - expo); -/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */ - i2c_w(gspca_dev, 0x00, /* gain */ - gain); -} - -/* set the JPEG quantization tables */ -static void set_dqt(struct gspca_dev *gspca_dev, u8 q) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* update the jpeg quantization tables */ - PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q); - sd->quality = q; - if (q > 16) - q = 16; - if (sd->sensor == SENSOR_SOI763A) - jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]); - else - memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1], - DQT[q], sizeof DQT[0]); -} - -/* set the JPEG compression quality factor */ -static void setquality(struct gspca_dev *gspca_dev, s32 q) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (q != 16) - q = 15 - q; - - reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00); - reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04); - reg_w(gspca_dev, TP6800_R79_QUALITY, q); - - /* auto quality */ - if (q == 15 && sd->bridge == BRIDGE_TP6810) { - msleep(4); - reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19); - } -} - -static const u8 color_null[18] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -static const u8 color_gain[NSENSORS][18] = { -[SENSOR_CX0342] = - {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */ - 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */ - 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */ -[SENSOR_SOI763A] = - {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */ - 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */ - 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */ -}; - -static void setgamma(struct gspca_dev *gspca_dev, s32 gamma) -{ - struct sd *sd = (struct sd *) gspca_dev; -#define NGAMMA 6 - static const u8 gamma_tb[NGAMMA][3][1024] = { - { /* gamma 0 - from tp6800 + soi763a */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, - 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, - 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, - 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, - 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, - 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, - 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, - 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, - 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, - 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, - 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, - 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, - 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, - 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, - 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, - 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, - 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, - 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, - 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, - 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, - 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, - 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, - 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, - 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, - 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, - 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, - 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, - 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, - 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, - 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, - 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, - 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, - 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, - 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, - 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, - 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, - 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, - 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, - 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, - 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, - 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, - 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, - 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, - 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, - 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, - 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, - 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, - 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, - 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, - 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, - 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, - 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, - 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, - 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, - 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, - 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, - 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, - 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, - 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, - 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, - 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, - 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, - 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, - 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, - 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, - 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, - 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, - 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, - 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, - 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, - 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, - 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, - 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, - 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, - 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, - 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, - 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, - 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, - 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, - 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, - 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, - 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, - 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, - 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, - 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, - 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, - 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, - 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, - 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, - 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, - 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, - 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, - 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, - 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, - 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, - 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, - 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, - 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, - 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, - 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, - 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, - 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, - 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, - 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, - 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, - 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, - 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, - 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, - 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, - 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, - 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, - 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, - 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, - 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, - 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, - 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, - 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, - 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, - 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, - 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, - 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, - 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, - 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, - 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, - 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, - 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, - 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, - 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, - 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, - 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, - 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, - 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, - 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, - 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, - 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, - 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, - 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, - 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, - 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, - 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, - 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, - 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, - 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, - 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, - 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} - }, - { /* gamma 1 - from tp6810 + soi763a */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a, - 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, - 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e, - 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27, - 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f, - 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35, - 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, - 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43, - 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49, - 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, - 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, - 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, - 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, - 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61, - 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, - 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69, - 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, - 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, - 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, - 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79, - 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, - 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80, - 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84, - 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88, - 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, - 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, - 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, - 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, - 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, - 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, - 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, - 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, - 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab, - 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae, - 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, - 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, - 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, - 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, - 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, - 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, - 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, - 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, - 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, - 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, - 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, - 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, - 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, - 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, - 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, - 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, - 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, - 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, - 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, - 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d, - 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15, - 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e, - 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25, - 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c, - 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31, - 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, - 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, - 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43, - 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, - 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, - 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, - 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55, - 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59, - 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, - 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, - 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65, - 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, - 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, - 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, - 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, - 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, - 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78, - 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, - 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, - 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, - 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, - 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, - 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, - 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, - 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, - 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, - 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, - 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, - 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, - 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, - 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, - 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, - 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf, - 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, - 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, - 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, - 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, - 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, - 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, - 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, - 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, - 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, - 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, - 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, - 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, - 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07, - 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e, - 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16, - 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, - 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23, - 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, - 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30, - 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35, - 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a, - 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, - 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43, - 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47, - 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b, - 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, - 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53, - 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, - 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, - 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, - 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, - 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, - 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, - 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, - 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, - 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, - 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, - 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, - 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77, - 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, - 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, - 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, - 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, - 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, - 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, - 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, - 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, - 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, - 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, - 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, - 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, - 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, - 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, - 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, - 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, - 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, - 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, - 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, - 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, - 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, - 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, - 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, - 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, - 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - }, - { /* gamma 2 */ - {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c, - 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22, - 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c, - 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, - 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b, - 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43, - 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49, - 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f, - 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55, - 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a, - 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, - 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, - 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, - 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, - 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, - 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, - 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, - 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, - 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, - 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, - 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, - 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89, - 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, - 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, - 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, - 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, - 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, - 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, - 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, - 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, - 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, - 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, - 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, - 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, - 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, - 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, - 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, - 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, - 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, - 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, - 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, - 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, - 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, - 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, - 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, - 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, - 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, - 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, - 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, - 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, - {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, - 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11, - 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a, - 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, - 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b, - 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33, - 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39, - 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f, - 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, - 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a, - 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, - 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, - 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58, - 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, - 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, - 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63, - 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, - 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, - 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, - 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, - 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73, - 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, - 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, - 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, - 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, - 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, - 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84, - 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88, - 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, - 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, - 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, - 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, - 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, - 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, - 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, - 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, - 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, - 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, - 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, - 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, - 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, - 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, - 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, - 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, - 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, - 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, - 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, - 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, - 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, - 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, - 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, - 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, - 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, - {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08, - 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14, - 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e, - 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27, - 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f, - 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37, - 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c, - 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43, - 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, - 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, - 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, - 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, - 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, - 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, - 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, - 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, - 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, - 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, - 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, - 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, - 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, - 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, - 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, - 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, - 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, - 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, - 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, - 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, - 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, - 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, - 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, - 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, - 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, - 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, - 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, - 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, - 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, - 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, - 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, - 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, - 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, - 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, - 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, - 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, - 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} - }, - { /* gamma 3 - from tp6810 + cx0342 */ - {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15, - 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23, - 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f, - 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39, - 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43, - 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b, - 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53, - 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a, - 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61, - 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67, - 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, - 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73, - 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, - 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, - 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, - 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, - 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, - 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, - 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, - 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97, - 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, - 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, - 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, - 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8, - 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, - 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, - 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, - 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, - 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, - 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, - 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, - 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, - 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, - 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, - 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, - 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, - 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, - 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, - 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, - 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, - 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, - 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, - 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, - 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, - 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, - 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, - 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, - 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, - 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, - 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10, - 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b, - 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26, - 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f, - 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38, - 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, - 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, - 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, - 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, - 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58, - 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, - 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, - 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66, - 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, - 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, - 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, - 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, - 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79, - 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, - 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, - 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, - 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, - 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, - 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, - 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, - 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, - 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98, - 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, - 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, - 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, - 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, - 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, - 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, - 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, - 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, - 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, - 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, - 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, - 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, - 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, - 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, - 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, - 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, - 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, - 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, - 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, - 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, - 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, - 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, - 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, - 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, - 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, - 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d, - 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, - 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, - 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49, - 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50, - 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58, - 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, - 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, - 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, - 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, - 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74, - 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79, - 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, - 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, - 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, - 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, - 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, - 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92, - 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, - 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, - 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, - 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0, - 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, - 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, - 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, - 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, - 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, - 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, - 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, - 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, - 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, - 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, - 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, - 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, - 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, - 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, - 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, - 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, - 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, - 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, - 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, - 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, - 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, - 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, - 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, - 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, - 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, - 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - }, - { /* gamma 4 - from tp6800 + soi763a */ - {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f, - 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d, - 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a, - 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45, - 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f, - 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58, - 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60, - 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67, - 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e, - 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74, - 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a, - 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80, - 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, - 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b, - 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91, - 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96, - 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, - 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d, - 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, - 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, - 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab, - 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, - 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, - 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, - 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, - 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc, - 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, - 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, - 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9, - 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, - 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, - 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, - 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, - 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, - 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, - 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, - 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, - 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, - 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, - 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, - 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, - 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, - 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, - 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, - {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15, - 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, - 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c, - 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35, - 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, - 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, - 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c, - 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54, - 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a, - 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, - 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65, - 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, - 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, - 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, - 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, - 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, - 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, - 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, - 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86, - 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a, - 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, - 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, - 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94, - 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, - 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, - 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, - 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, - 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, - 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, - 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, - 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, - 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, - 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, - 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, - 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, - 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, - 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, - 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, - 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, - 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, - 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, - 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, - 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, - 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, - 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, - 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, - 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, - {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b, - 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28, - 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, - 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, - 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, - 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50, - 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58, - 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, - 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65, - 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c, - 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, - 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, - 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, - 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, - 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, - 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, - 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, - 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, - 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97, - 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, - 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, - 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, - 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8, - 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, - 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, - 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4, - 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, - 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, - 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, - 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, - 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, - 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, - 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, - 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, - 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, - 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, - 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, - 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, - 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, - 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, - 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, - 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, - 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, - 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} - }, - { /* gamma 5 */ - {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21, - 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c, - 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, - 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b, - 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61, - 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66, - 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, - 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, - 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, - 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, - 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e, - 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82, - 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, - 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89, - 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d, - 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90, - 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, - 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, - 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d, - 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0, - 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, - 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, - 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, - 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, - 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, - 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, - 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, - 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, - 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, - 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, - 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, - 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, - 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, - 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, - 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, - 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, - 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, - 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, - 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, - 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, - 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, - 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, - 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, - 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, - 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, - 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, - 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, - 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19, - 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, - 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32, - 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39, - 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, - 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45, - 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, - 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50, - 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54, - 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59, - 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d, - 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, - 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65, - 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, - 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, - 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, - 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, - 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75, - 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78, - 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, - 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, - 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81, - 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, - 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, - 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, - 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, - 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, - 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, - 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, - 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, - 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, - 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, - 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab, - 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, - 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, - 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, - 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, - 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, - 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, - 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, - 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, - 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, - 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, - 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, - 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, - 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, - 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, - 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, - 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, - 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, - 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, - 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, - 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, - 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, - 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, - 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, - 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e, - 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, - 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, - 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, - 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, - 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59, - 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f, - 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63, - 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, - 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, - 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, - 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, - 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, - 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, - 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, - 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83, - 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, - 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89, - 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, - 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, - 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92, - 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95, - 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, - 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, - 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, - 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, - 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, - 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9, - 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, - 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, - 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, - 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, - 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, - 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, - 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, - 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, - 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, - 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, - 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, - 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, - 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, - 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, - 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, - 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, - 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, - 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, - 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, - 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, - 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, - 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, - 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, - 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, - 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, - 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, - 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, - 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - }, - }; - - reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); - if (sd->bridge == BRIDGE_TP6810) - reg_w(gspca_dev, 0x02, 0x28); -/* msleep(50); */ - bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024); - bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024); - bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024); - if (sd->bridge == BRIDGE_TP6810) { - int i; - - reg_w(gspca_dev, 0x02, 0x2b); - reg_w(gspca_dev, 0x02, 0x28); - for (i = 0; i < 6; i++) - reg_w(gspca_dev, TP6800_R55_GAMMA_R, - gamma_tb[gamma][0][i]); - reg_w(gspca_dev, 0x02, 0x2b); - reg_w(gspca_dev, 0x02, 0x28); - for (i = 0; i < 6; i++) - reg_w(gspca_dev, TP6800_R56_GAMMA_G, - gamma_tb[gamma][1][i]); - reg_w(gspca_dev, 0x02, 0x2b); - reg_w(gspca_dev, 0x02, 0x28); - for (i = 0; i < 6; i++) - reg_w(gspca_dev, TP6800_R57_GAMMA_B, - gamma_tb[gamma][2][i]); - reg_w(gspca_dev, 0x02, 0x28); - } - reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); -/* msleep(50); */ -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->bridge == BRIDGE_TP6800) { - val |= 0x08; /* grid compensation enable */ - if (gspca_dev->width == 640) - reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ - else - val |= 0x04; /* scaling down enable */ - reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val); - } else { - val = (val << 5) | 0x08; - reg_w(gspca_dev, 0x59, val); - } -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->ag_cnt = val ? AG_CNT_START : -1; -} - -/* set the resolution for sensor cx0342 */ -static void set_resolution(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); - if (gspca_dev->width == 320) { - reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06); - msleep(100); - i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); - msleep(100); - reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); - reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ - reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d); - i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37); - i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01); - } else { - reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05); - msleep(100); - i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); - msleep(100); - reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); - reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ - reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09); - i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf); - i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00); - } - i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01); - bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342], - ARRAY_SIZE(color_gain[0])); - setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); - if (sd->sensor == SENSOR_SOI763A) - setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); -} - -/* convert the frame rate to a tp68x0 value */ -static int get_fr_idx(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - - if (sd->bridge == BRIDGE_TP6800) { - for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) { - if (sd->framerate >= rates[i]) - break; - } - i = 6 - i; /* 1 = 5fps .. 6 = 30fps */ - - /* 640x480 * 30 fps does not work */ - if (i == 6 /* if 30 fps */ - && gspca_dev->width == 640) - i = 0x05; /* 15 fps */ - } else { - for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) { - if (sd->framerate >= rates_6810[i]) - break; - } - i = 7 - i; /* 3 = 5fps .. 7 = 30fps */ - - /* 640x480 * 30 fps does not work */ - if (i == 7 /* if 30 fps */ - && gspca_dev->width == 640) - i = 6; /* 15 fps */ - i |= 0x80; /* clock * 1 */ - } - return i; -} - -static void setframerate(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 fr_idx; - - fr_idx = get_fr_idx(gspca_dev); - - if (sd->bridge == BRIDGE_TP6810) { - reg_r(gspca_dev, 0x7b); - reg_w(gspca_dev, 0x7b, - sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90); - if (val >= 128) - fr_idx = 0xf0; /* lower frame rate */ - } - - reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx); - - if (sd->sensor == SENSOR_CX0342) - i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); -} - -static void setrgain(struct gspca_dev *gspca_dev, s32 rgain) -{ - i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8); - i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain); - i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80); -} - -static int sd_setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 val = gspca_dev->gain->val; - - if (sd->sensor == SENSOR_CX0342) { - s32 old = gspca_dev->gain->cur.val ? - gspca_dev->gain->cur.val : 1; - - sd->blue->val = sd->blue->val * val / old; - if (sd->blue->val > 4095) - sd->blue->val = 4095; - sd->red->val = sd->red->val * val / old; - if (sd->red->val > 4095) - sd->red->val = 4095; - } - if (gspca_dev->streaming) { - if (sd->sensor == SENSOR_CX0342) - setexposure(gspca_dev, gspca_dev->exposure->val, - gspca_dev->gain->val, - sd->blue->val, sd->red->val); - else - setexposure(gspca_dev, gspca_dev->exposure->val, - gspca_dev->gain->val, 0, 0); - } - return gspca_dev->usb_err; -} - -static void setbgain(struct gspca_dev *gspca_dev, s32 bgain) -{ - i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8); - i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain); - i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->bridge = id->driver_info; - - gspca_dev->cam.cam_mode = vga_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ? - framerates : framerates_6810; - - sd->framerate = 30; /* default: 30 fps */ - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct cmd tp6800_preinit[] = { - {TP6800_R10_SIF_TYPE, 0x01}, /* sif */ - {TP6800_R11_SIF_CONTROL, 0x01}, - {TP6800_R15_GPIO_PU, 0x9f}, - {TP6800_R16_GPIO_PD, 0x9f}, - {TP6800_R17_GPIO_IO, 0x80}, - {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */ - }; - static const struct cmd tp6810_preinit[] = { - {TP6800_R2F_TIMING_CFG, 0x2f}, - {TP6800_R15_GPIO_PU, 0x6f}, - {TP6800_R16_GPIO_PD, 0x40}, - {TP6800_R17_GPIO_IO, 0x9f}, - {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */ - }; - - if (sd->bridge == BRIDGE_TP6800) - reg_w_buf(gspca_dev, tp6800_preinit, - ARRAY_SIZE(tp6800_preinit)); - else - reg_w_buf(gspca_dev, tp6810_preinit, - ARRAY_SIZE(tp6810_preinit)); - msleep(15); - reg_r(gspca_dev, TP6800_R18_GPIO_DATA); - PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]); -/* values: - * 0x80: snapshot button - * 0x40: LED - * 0x20: (bridge / sensor) reset for tp6810 ? - * 0x07: sensor type ? - */ - - /* guess the sensor type */ - if (force_sensor >= 0) { - sd->sensor = force_sensor; - } else { - if (sd->bridge == BRIDGE_TP6800) { -/*fixme: not sure this is working*/ - switch (gspca_dev->usb_buf[0] & 0x07) { - case 0: - sd->sensor = SENSOR_SOI763A; - break; - case 1: - sd->sensor = SENSOR_CX0342; - break; - } - } else { - int sensor; - - sensor = probe_6810(gspca_dev); - if (sensor < 0) { - pr_warn("Unknown sensor %d - forced to soi763a\n", - -sensor); - sensor = SENSOR_SOI763A; - } - sd->sensor = sensor; - } - } - if (sd->sensor == SENSOR_SOI763A) { - pr_info("Sensor soi763a\n"); - if (sd->bridge == BRIDGE_TP6810) { - soi763a_6810_init(gspca_dev); - } - } else { - pr_info("Sensor cx0342\n"); - if (sd->bridge == BRIDGE_TP6810) { - cx0342_6810_init(gspca_dev); - } - } - - set_dqt(gspca_dev, 0); - return 0; -} - -/* This function is called before choosing the alt setting */ -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct cmd cx_sensor_init[] = { - {CX0342_AUTO_ADC_CALIB, 0x81}, - {CX0342_EXPO_LINE_L, 0x37}, - {CX0342_EXPO_LINE_H, 0x01}, - {CX0342_RAW_GRGAIN_L, 0x00}, - {CX0342_RAW_GBGAIN_L, 0x00}, - {CX0342_RAW_RGAIN_L, 0x00}, - {CX0342_RAW_BGAIN_L, 0x00}, - {CX0342_SYS_CTRL_0, 0x81}, - }; - static const struct cmd cx_bridge_init[] = { - {0x4d, 0x00}, - {0x4c, 0xff}, - {0x4e, 0xff}, - {0x4f, 0x00}, - }; - static const struct cmd ov_sensor_init[] = { - {0x10, 0x75}, /* exposure */ - {0x76, 0x03}, - {0x00, 0x00}, /* gain */ - }; - static const struct cmd ov_bridge_init[] = { - {0x7b, 0x90}, - {TP6800_R3F_FRAME_RATE, 0x87}, - }; - - if (sd->bridge == BRIDGE_TP6800) - return 0; - if (sd->sensor == SENSOR_CX0342) { - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); - reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87); - i2c_w_buf(gspca_dev, cx_sensor_init, - ARRAY_SIZE(cx_sensor_init)); - reg_w_buf(gspca_dev, cx_bridge_init, - ARRAY_SIZE(cx_bridge_init)); - bulk_w(gspca_dev, 0x03, color_null, sizeof color_null); - reg_w(gspca_dev, 0x59, 0x40); - } else { - reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); - i2c_w_buf(gspca_dev, ov_sensor_init, - ARRAY_SIZE(ov_sensor_init)); - reg_r(gspca_dev, 0x7b); - reg_w_buf(gspca_dev, ov_bridge_init, - ARRAY_SIZE(ov_bridge_init)); - } - reg_w(gspca_dev, TP6800_R78_FORMAT, - gspca_dev->curr_mode ? 0x00 : 0x01); - return gspca_dev->usb_err; -} - -static void set_led(struct gspca_dev *gspca_dev, int on) -{ - u8 data; - - reg_r(gspca_dev, TP6800_R18_GPIO_DATA); - data = gspca_dev->usb_buf[0]; - if (on) - data &= ~0x40; - else - data |= 0x40; - reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data); -} - -static void cx0342_6800_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct cmd reg_init[] = { - /* fixme: is this useful? */ - {TP6800_R17_GPIO_IO, 0x9f}, - {TP6800_R16_GPIO_PD, 0x40}, - {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */ - {TP6800_R50, 0x00}, - {TP6800_R51, 0x00}, - {TP6800_R52, 0xff}, - {TP6800_R53, 0x03}, - {TP6800_R54_DARK_CFG, 0x07}, - {TP6800_R5C_EDGE_THRLD, 0x40}, - {TP6800_R7A_BLK_THRLD, 0x40}, - {TP6800_R2F_TIMING_CFG, 0x17}, - {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */ - {TP6800_R37_FRONT_DARK_ST, 0x00}, - {TP6800_R38_FRONT_DARK_END, 0x00}, - {TP6800_R39_REAR_DARK_ST_L, 0x00}, - {TP6800_R3A_REAR_DARK_ST_H, 0x00}, - {TP6800_R3B_REAR_DARK_END_L, 0x00}, - {TP6800_R3C_REAR_DARK_END_H, 0x00}, - {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00}, - {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00}, - {TP6800_R21_ENDP_1_CTL, 0x03}, - - {TP6800_R31_PIXEL_START, 0x0b}, - {TP6800_R32_PIXEL_END_L, 0x8a}, - {TP6800_R33_PIXEL_END_H, 0x02}, - {TP6800_R34_LINE_START, 0x0e}, - {TP6800_R35_LINE_END_L, 0xf4}, - {TP6800_R36_LINE_END_H, 0x01}, - {TP6800_R78_FORMAT, 0x00}, - {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */ - }; - static const struct cmd sensor_init[] = { - {CX0342_OUTPUT_CTRL, 0x07}, - {CX0342_BYPASS_MODE, 0x58}, - {CX0342_GPXLTHD_L, 0x16}, - {CX0342_RBPXLTHD_L, 0x16}, - {CX0342_PLANETHD_L, 0xc0}, - {CX0342_PLANETHD_H, 0x03}, - {CX0342_RB_GAP_L, 0xff}, - {CX0342_RB_GAP_H, 0x07}, - {CX0342_G_GAP_L, 0xff}, - {CX0342_G_GAP_H, 0x07}, - {CX0342_RST_OVERFLOW_L, 0x5c}, - {CX0342_RST_OVERFLOW_H, 0x01}, - {CX0342_DATA_OVERFLOW_L, 0xfc}, - {CX0342_DATA_OVERFLOW_H, 0x03}, - {CX0342_DATA_UNDERFLOW_L, 0x00}, - {CX0342_DATA_UNDERFLOW_H, 0x00}, - {CX0342_SYS_CTRL_0, 0x40}, - {CX0342_GLOBAL_GAIN, 0x01}, - {CX0342_CLOCK_GEN, 0x00}, - {CX0342_SYS_CTRL_0, 0x02}, - {CX0342_IDLE_CTRL, 0x05}, - {CX0342_ADCGN, 0x00}, - {CX0342_ADC_CTL, 0x00}, - {CX0342_LVRST_BLBIAS, 0x01}, - {CX0342_VTHSEL, 0x0b}, - {CX0342_RAMP_RIV, 0x0b}, - {CX0342_LDOSEL, 0x07}, - {CX0342_SPV_VALUE_L, 0x40}, - {CX0342_SPV_VALUE_H, 0x02}, - }; - - reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init)); - i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); - i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq)); - reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10); - reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00); - i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00); - i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01); - if (sd->sensor == SENSOR_CX0342) - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), - v4l2_ctrl_g_ctrl(sd->blue), - v4l2_ctrl_g_ctrl(sd->red)); - else - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); - set_led(gspca_dev, 1); - set_resolution(gspca_dev); -} - -static void cx0342_6810_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct cmd sensor_init_2[] = { - {CX0342_EXPO_LINE_L, 0x6f}, - {CX0342_EXPO_LINE_H, 0x02}, - {CX0342_RAW_GRGAIN_L, 0x00}, - {CX0342_RAW_GBGAIN_L, 0x00}, - {CX0342_RAW_RGAIN_L, 0x00}, - {CX0342_RAW_BGAIN_L, 0x00}, - {CX0342_SYS_CTRL_0, 0x81}, - }; - static const struct cmd bridge_init_2[] = { - {0x4d, 0x00}, - {0x4c, 0xff}, - {0x4e, 0xff}, - {0x4f, 0x00}, - {TP6800_R7A_BLK_THRLD, 0x00}, - {TP6800_R79_QUALITY, 0x04}, - {TP6800_R79_QUALITY, 0x01}, - }; - static const struct cmd bridge_init_3[] = { - {TP6800_R31_PIXEL_START, 0x08}, - {TP6800_R32_PIXEL_END_L, 0x87}, - {TP6800_R33_PIXEL_END_H, 0x02}, - {TP6800_R34_LINE_START, 0x0e}, - {TP6800_R35_LINE_END_L, 0xf4}, - {TP6800_R36_LINE_END_H, 0x01}, - }; - static const struct cmd sensor_init_3[] = { - {CX0342_AUTO_ADC_CALIB, 0x81}, - {CX0342_EXPO_LINE_L, 0x6f}, - {CX0342_EXPO_LINE_H, 0x02}, - {CX0342_RAW_GRGAIN_L, 0x00}, - {CX0342_RAW_GBGAIN_L, 0x00}, - {CX0342_RAW_RGAIN_L, 0x00}, - {CX0342_RAW_BGAIN_L, 0x00}, - {CX0342_SYS_CTRL_0, 0x81}, - }; - static const struct cmd bridge_init_5[] = { - {0x4d, 0x00}, - {0x4c, 0xff}, - {0x4e, 0xff}, - {0x4f, 0x00}, - }; - static const struct cmd sensor_init_4[] = { - {CX0342_EXPO_LINE_L, 0xd3}, - {CX0342_EXPO_LINE_H, 0x01}, -/*fixme: gains, but 00..80 only*/ - {CX0342_RAW_GRGAIN_L, 0x40}, - {CX0342_RAW_GBGAIN_L, 0x40}, - {CX0342_RAW_RGAIN_L, 0x40}, - {CX0342_RAW_BGAIN_L, 0x40}, - {CX0342_SYS_CTRL_0, 0x81}, - }; - static const struct cmd sensor_init_5[] = { - {CX0342_IDLE_CTRL, 0x05}, - {CX0342_ADCGN, 0x00}, - {CX0342_ADC_CTL, 0x00}, - {CX0342_LVRST_BLBIAS, 0x01}, - {CX0342_VTHSEL, 0x0b}, - {CX0342_RAMP_RIV, 0x0b}, - {CX0342_LDOSEL, 0x07}, - {CX0342_SPV_VALUE_L, 0x40}, - {CX0342_SPV_VALUE_H, 0x02}, - {CX0342_AUTO_ADC_CALIB, 0x81}, - }; - - reg_w(gspca_dev, 0x22, gspca_dev->alt); - i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2)); - reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2)); - reg_w_buf(gspca_dev, tp6810_cx_init_common, - ARRAY_SIZE(tp6810_cx_init_common)); - reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3)); - if (gspca_dev->curr_mode) { - reg_w(gspca_dev, 0x4a, 0x7f); - reg_w(gspca_dev, 0x07, 0x05); - reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ - } else { - reg_w(gspca_dev, 0x4a, 0xff); - reg_w(gspca_dev, 0x07, 0x85); - reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ - } - setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); - reg_w_buf(gspca_dev, tp6810_bridge_start, - ARRAY_SIZE(tp6810_bridge_start)); - setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); - bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342], - ARRAY_SIZE(color_gain[0])); - reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87); - i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3)); - reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5)); - i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4)); - reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5)); - i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5)); - - set_led(gspca_dev, 1); -/* setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */ -} - -static void soi763a_6800_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct cmd reg_init[] = { - {TP6800_R79_QUALITY, 0x04}, - {TP6800_R79_QUALITY, 0x01}, - {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */ - - {TP6800_R50, 0x00}, - {TP6800_R51, 0x00}, - {TP6800_R52, 0xff}, - {TP6800_R53, 0x03}, - {TP6800_R54_DARK_CFG, 0x07}, - {TP6800_R5C_EDGE_THRLD, 0x40}, - - {TP6800_R79_QUALITY, 0x03}, - {TP6800_R7A_BLK_THRLD, 0x40}, - - {TP6800_R2F_TIMING_CFG, 0x46}, - {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */ - {TP6800_R37_FRONT_DARK_ST, 0x00}, - {TP6800_R38_FRONT_DARK_END, 0x00}, - {TP6800_R39_REAR_DARK_ST_L, 0x00}, - {TP6800_R3A_REAR_DARK_ST_H, 0x00}, - {TP6800_R3B_REAR_DARK_END_L, 0x00}, - {TP6800_R3C_REAR_DARK_END_H, 0x00}, - {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00}, - {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00}, - {TP6800_R21_ENDP_1_CTL, 0x03}, - - {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */ - {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */ - - {TP6800_R31_PIXEL_START, 0x1b}, - {TP6800_R32_PIXEL_END_L, 0x9a}, - {TP6800_R33_PIXEL_END_H, 0x02}, - {TP6800_R34_LINE_START, 0x0f}, - {TP6800_R35_LINE_END_L, 0xf4}, - {TP6800_R36_LINE_END_H, 0x01}, - {TP6800_R78_FORMAT, 0x01}, /* qvga */ - {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */ - {TP6800_R1A_SIF_TX_DATA2, 0x00}, - }; - static const struct cmd sensor_init[] = { - {0x12, 0x48}, /* mirror - RGB */ - {0x13, 0xa0}, /* clock - no AGC nor AEC */ - {0x03, 0xa4}, /* saturation */ - {0x04, 0x30}, /* hue */ - {0x05, 0x88}, /* contrast */ - {0x06, 0x60}, /* brightness */ - {0x10, 0x41}, /* AEC */ - {0x11, 0x40}, /* clock rate */ - {0x13, 0xa0}, - {0x14, 0x00}, /* 640x480 */ - {0x15, 0x14}, - {0x1f, 0x41}, - {0x20, 0x80}, - {0x23, 0xee}, - {0x24, 0x50}, - {0x25, 0x7a}, - {0x26, 0x00}, - {0x27, 0xe2}, - {0x28, 0xb0}, - {0x2a, 0x00}, - {0x2b, 0x00}, - {0x2d, 0x81}, - {0x2f, 0x9d}, - {0x60, 0x80}, - {0x61, 0x00}, - {0x62, 0x88}, - {0x63, 0x11}, - {0x64, 0x89}, - {0x65, 0x00}, - {0x67, 0x94}, - {0x68, 0x7a}, - {0x69, 0x0f}, - {0x6c, 0x80}, - {0x6d, 0x80}, - {0x6e, 0x80}, - {0x6f, 0xff}, - {0x71, 0x20}, - {0x74, 0x20}, - {0x75, 0x86}, - {0x77, 0xb5}, - {0x17, 0x18}, /* H href start */ - {0x18, 0xbf}, /* H href end */ - {0x19, 0x03}, /* V start */ - {0x1a, 0xf8}, /* V end */ - {0x01, 0x80}, /* blue gain */ - {0x02, 0x80}, /* red gain */ - }; - - reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init)); - - i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */ - msleep(10); - - i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); - - reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10); - reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00); - - setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); - - bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A], - ARRAY_SIZE(color_gain[0])); - - set_led(gspca_dev, 1); - if (sd->sensor == SENSOR_CX0342) - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), - v4l2_ctrl_g_ctrl(sd->blue), - v4l2_ctrl_g_ctrl(sd->red)); - else - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); - if (sd->sensor == SENSOR_SOI763A) - setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); - setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); -} - -static void soi763a_6810_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const struct cmd bridge_init_2[] = { - {TP6800_R7A_BLK_THRLD, 0x00}, - {TP6800_R79_QUALITY, 0x04}, - {TP6800_R79_QUALITY, 0x01}, - }; - static const struct cmd bridge_init_3[] = { - {TP6800_R31_PIXEL_START, 0x20}, - {TP6800_R32_PIXEL_END_L, 0x9f}, - {TP6800_R33_PIXEL_END_H, 0x02}, - {TP6800_R34_LINE_START, 0x13}, - {TP6800_R35_LINE_END_L, 0xf8}, - {TP6800_R36_LINE_END_H, 0x01}, - }; - static const struct cmd bridge_init_6[] = { - {0x08, 0xff}, - {0x09, 0xff}, - {0x0a, 0x5f}, - {0x0b, 0x80}, - }; - - reg_w(gspca_dev, 0x22, gspca_dev->alt); - bulk_w(gspca_dev, 0x03, color_null, sizeof color_null); - reg_w(gspca_dev, 0x59, 0x40); - if (sd->sensor == SENSOR_CX0342) - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), - v4l2_ctrl_g_ctrl(sd->blue), - v4l2_ctrl_g_ctrl(sd->red)); - else - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); - reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2)); - reg_w_buf(gspca_dev, tp6810_ov_init_common, - ARRAY_SIZE(tp6810_ov_init_common)); - reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3)); - if (gspca_dev->curr_mode) { - reg_w(gspca_dev, 0x4a, 0x7f); - reg_w(gspca_dev, 0x07, 0x05); - reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ - } else { - reg_w(gspca_dev, 0x4a, 0xff); - reg_w(gspca_dev, 0x07, 0x85); - reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ - } - setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); - reg_w_buf(gspca_dev, tp6810_bridge_start, - ARRAY_SIZE(tp6810_bridge_start)); - - if (gspca_dev->curr_mode) { - reg_w(gspca_dev, 0x4f, 0x00); - reg_w(gspca_dev, 0x4e, 0x7c); - } - - reg_w(gspca_dev, 0x00, 0x00); - - setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); - bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A], - ARRAY_SIZE(color_gain[0])); - set_led(gspca_dev, 1); - reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0); - if (sd->sensor == SENSOR_CX0342) - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), - v4l2_ctrl_g_ctrl(sd->blue), - v4l2_ctrl_g_ctrl(sd->red)); - else - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); - reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6)); -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width); - set_dqt(gspca_dev, sd->quality); - if (sd->bridge == BRIDGE_TP6800) { - if (sd->sensor == SENSOR_CX0342) - cx0342_6800_start(gspca_dev); - else - soi763a_6800_start(gspca_dev); - } else { - if (sd->sensor == SENSOR_CX0342) - cx0342_6810_start(gspca_dev); - else - soi763a_6810_start(gspca_dev); - reg_w_buf(gspca_dev, tp6810_late_start, - ARRAY_SIZE(tp6810_late_start)); - reg_w(gspca_dev, 0x80, 0x03); - reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e); - - if (sd->sensor == SENSOR_CX0342) - setexposure(gspca_dev, - v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), - v4l2_ctrl_g_ctrl(sd->blue), - v4l2_ctrl_g_ctrl(sd->red)); - else - setexposure(gspca_dev, - v4l2_ctrl_g_ctrl(gspca_dev->exposure), - v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0); - if (sd->sensor == SENSOR_SOI763A) - setquality(gspca_dev, - v4l2_ctrl_g_ctrl(sd->jpegqual)); - if (sd->bridge == BRIDGE_TP6810) - setautogain(gspca_dev, - v4l2_ctrl_g_ctrl(gspca_dev->autogain)); - } - - setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); - - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->bridge == BRIDGE_TP6800) - reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03); - set_led(gspca_dev, 0); - reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, - int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* the start of frame contains: - * ff d8 - * ff fe - * width / 16 - * height / 8 - * quality - */ - if (sd->bridge == BRIDGE_TP6810) { - if (*data != 0x5a) { -/*fixme: don't discard the whole frame..*/ - if (*data == 0xaa || *data == 0x00) - return; - if (*data > 0xc0) { - PDEBUG(D_FRAM, "bad frame"); - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - } - data++; - len--; - if (*data == 0xff && data[1] == 0xd8) { -/*fixme: there may be information in the 4 high bits*/ - if ((data[6] & 0x0f) != sd->quality) - set_dqt(gspca_dev, data[6] & 0x0f); - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, - data + 7, len - 7); - } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) { - gspca_frame_add(gspca_dev, LAST_PACKET, - data, len); - } else { - gspca_frame_add(gspca_dev, INTER_PACKET, - data, len); - } - return; - } - - switch (*data) { - case 0x55: - gspca_frame_add(gspca_dev, LAST_PACKET, data, 0); - - if (len < 8 - || data[1] != 0xff || data[2] != 0xd8 - || data[3] != 0xff || data[4] != 0xfe) { - - /* Have only seen this with corrupt frames */ - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - if (data[7] != sd->quality) - set_dqt(gspca_dev, data[7]); - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, - data + 8, len - 8); - break; - case 0xaa: - gspca_dev->last_packet_type = DISCARD_PACKET; - break; - case 0xcc: - if (data[1] != 0xff || data[2] != 0xd8) - gspca_frame_add(gspca_dev, INTER_PACKET, - data + 1, len - 1); - else - gspca_dev->last_packet_type = DISCARD_PACKET; - break; - } -} - -static void sd_dq_callback(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret, alen; - int luma, expo; - - if (sd->ag_cnt < 0) - return; - if (--sd->ag_cnt > 5) - return; - switch (sd->ag_cnt) { -/* case 5: */ - default: - reg_w(gspca_dev, 0x7d, 0x00); - break; - case 4: - reg_w(gspca_dev, 0x27, 0xb0); - break; - case 3: - reg_w(gspca_dev, 0x0c, 0x01); - break; - case 2: - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x02), - gspca_dev->usb_buf, - 32, - &alen, - 500); - if (ret < 0) { - pr_err("bulk err %d\n", ret); - break; - } - /* values not used (unknown) */ - break; - case 1: - reg_w(gspca_dev, 0x27, 0xd0); - break; - case 0: - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x02), - gspca_dev->usb_buf, - 32, - &alen, - 500); - if (ret < 0) { - pr_err("bulk err %d\n", ret); - break; - } - luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] + - (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] + - (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] + - (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] + - (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] + - (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] + - (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] + - (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28]) - / 8; - if (gspca_dev->width == 640) - luma /= 4; - reg_w(gspca_dev, 0x7d, 0x00); - - expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - ret = gspca_expo_autogain(gspca_dev, luma, - 60, /* desired luma */ - 6, /* dead zone */ - 2, /* gain knee */ - 70); /* expo knee */ - sd->ag_cnt = AG_CNT_START; - if (sd->bridge == BRIDGE_TP6810) { - int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - - if ((expo >= 128 && new_expo < 128) - || (expo < 128 && new_expo >= 128)) - setframerate(gspca_dev, new_expo); - } - break; - } -} - -/* get stream parameters (framerate) */ -static void sd_get_streamparm(struct gspca_dev *gspca_dev, - struct v4l2_streamparm *parm) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_captureparm *cp = &parm->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - int fr, i; - - cp->capability |= V4L2_CAP_TIMEPERFRAME; - tpf->numerator = 1; - i = get_fr_idx(gspca_dev); - if (i & 0x80) { - if (sd->bridge == BRIDGE_TP6800) - fr = rates[6 - (i & 0x07)]; - else - fr = rates_6810[7 - (i & 0x07)]; - } else { - fr = rates[6 - i]; - } - tpf->denominator = fr; -} - -/* set stream parameters (framerate) */ -static void sd_set_streamparm(struct gspca_dev *gspca_dev, - struct v4l2_streamparm *parm) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_captureparm *cp = &parm->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - int fr, i; - - sd->framerate = tpf->denominator / tpf->numerator; - if (gspca_dev->streaming) - setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); - - /* Return the actual framerate */ - i = get_fr_idx(gspca_dev); - if (i & 0x80) - fr = rates_6810[7 - (i & 0x07)]; - else - fr = rates[6 - i]; - tpf->numerator = 1; - tpf->denominator = fr; -} - -static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor != SENSOR_SOI763A) - return -ENOTTY; - v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); - return 0; -} - -static int sd_get_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor != SENSOR_SOI763A) - return -ENOTTY; - memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); - jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT - | V4L2_JPEG_MARKER_DQT; - return 0; -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAMMA: - setgamma(gspca_dev, ctrl->val); - break; - case V4L2_CID_BLUE_BALANCE: - setbgain(gspca_dev, ctrl->val); - break; - case V4L2_CID_RED_BALANCE: - setrgain(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - sd_setgain(gspca_dev); - break; - case V4L2_CID_AUTOGAIN: - if (ctrl->val) - break; - sd_setgain(gspca_dev); - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - jpeg_set_qual(sd->jpeg_hdr, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e); - if (sd->sensor == SENSOR_CX0342) { - sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 4095, 1, 256); - sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256); - } - if (sd->sensor == SENSOR_SOI763A) - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 15, 1, 3); - else - gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 4095, 1, 256); - sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 3, 1, 2); - sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAMMA, 0, NGAMMA - 1, 1, - (sd->sensor == SENSOR_SOI763A && - sd->bridge == BRIDGE_TP6800) ? 0 : 1); - if (sd->bridge == BRIDGE_TP6810) - gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - if (sd->sensor == SENSOR_SOI763A) - sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - 0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - if (gspca_dev->autogain) - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - else - v4l2_ctrl_cluster(2, &gspca_dev->exposure); - return 0; -} - -static const struct sd_desc sd_desc = { - .name = KBUILD_MODNAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_dq_callback, - .get_streamparm = sd_get_streamparm, - .set_streamparm = sd_set_streamparm, - .get_jcomp = sd_get_jcomp, - .set_jcomp = sd_set_jcomp, -}; - -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800}, - {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -static int sd_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = KBUILD_MODNAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); - -module_param(force_sensor, int, 0644); -MODULE_PARM_DESC(force_sensor, - "Force sensor. 0: cx0342, 1: soi763a"); diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c deleted file mode 100644 index 8591324a53e1..000000000000 --- a/drivers/media/video/gspca/tv8532.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Quickcam cameras initialization data - * - * V4L2 by Jean-Francois Moine - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#define MODULE_NAME "tv8532" - -#include "gspca.h" - -MODULE_AUTHOR("Michel Xhaard "); -MODULE_DESCRIPTION("TV8532 USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - __u8 packet; -}; - -static const struct v4l2_pix_format sif_mode[] = { - {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; - -/* TV-8532A (ICM532A) registers (LE) */ -#define R00_PART_CONTROL 0x00 -#define LATENT_CHANGE 0x80 -#define EXPO_CHANGE 0x04 -#define R01_TIMING_CONTROL_LOW 0x01 -#define CMD_EEprom_Open 0x30 -#define CMD_EEprom_Close 0x29 -#define R03_TABLE_ADDR 0x03 -#define R04_WTRAM_DATA_L 0x04 -#define R05_WTRAM_DATA_M 0x05 -#define R06_WTRAM_DATA_H 0x06 -#define R07_TABLE_LEN 0x07 -#define R08_RAM_WRITE_ACTION 0x08 -#define R0C_AD_WIDTHL 0x0c -#define R0D_AD_WIDTHH 0x0d -#define R0E_AD_HEIGHTL 0x0e -#define R0F_AD_HEIGHTH 0x0f -#define R10_AD_COL_BEGINL 0x10 -#define R11_AD_COL_BEGINH 0x11 -#define MIRROR 0x04 /* [10] */ -#define R14_AD_ROW_BEGINL 0x14 -#define R15_AD_ROWBEGINH 0x15 -#define R1C_AD_EXPOSE_TIMEL 0x1c -#define R20_GAIN_G1L 0x20 -#define R21_GAIN_G1H 0x21 -#define R22_GAIN_RL 0x22 -#define R23_GAIN_RH 0x23 -#define R24_GAIN_BL 0x24 -#define R25_GAIN_BH 0x25 -#define R26_GAIN_G2L 0x26 -#define R27_GAIN_G2H 0x27 -#define R28_QUANT 0x28 -#define R29_LINE 0x29 -#define R2C_POLARITY 0x2c -#define R2D_POINT 0x2d -#define R2E_POINTH 0x2e -#define R2F_POINTB 0x2f -#define R30_POINTBH 0x30 -#define R31_UPD 0x31 -#define R2A_HIGH_BUDGET 0x2a -#define R2B_LOW_BUDGET 0x2b -#define R34_VID 0x34 -#define R35_VIDH 0x35 -#define R36_PID 0x36 -#define R37_PIDH 0x37 -#define R39_Test1 0x39 /* GPIO */ -#define R3B_Test3 0x3b /* GPIO */ -#define R83_AD_IDH 0x83 -#define R91_AD_SLOPEREG 0x91 -#define R94_AD_BITCONTROL 0x94 - -static const u8 eeprom_data[][3] = { -/* dataH dataM dataL */ - {0x01, 0x00, 0x01}, - {0x01, 0x80, 0x11}, - {0x05, 0x00, 0x14}, - {0x05, 0x00, 0x1c}, - {0x0d, 0x00, 0x1e}, - {0x05, 0x00, 0x1f}, - {0x05, 0x05, 0x19}, - {0x05, 0x01, 0x1b}, - {0x05, 0x09, 0x1e}, - {0x0d, 0x89, 0x2e}, - {0x05, 0x89, 0x2f}, - {0x05, 0x0d, 0xd9}, - {0x05, 0x09, 0xf1}, -}; - - -/* write 1 byte */ -static void reg_w1(struct gspca_dev *gspca_dev, - __u16 index, __u8 value) -{ - gspca_dev->usb_buf[0] = value; - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x02, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, 1, 500); -} - -/* write 2 bytes */ -static void reg_w2(struct gspca_dev *gspca_dev, - u16 index, u16 value) -{ - gspca_dev->usb_buf[0] = value; - gspca_dev->usb_buf[1] = value >> 8; - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x02, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, 2, 500); -} - -static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) -{ - int i; - - reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Open); - for (i = 0; i < ARRAY_SIZE(eeprom_data); i++) { - reg_w1(gspca_dev, R03_TABLE_ADDR, i); - reg_w1(gspca_dev, R04_WTRAM_DATA_L, eeprom_data[i][2]); - reg_w1(gspca_dev, R05_WTRAM_DATA_M, eeprom_data[i][1]); - reg_w1(gspca_dev, R06_WTRAM_DATA_H, eeprom_data[i][0]); - reg_w1(gspca_dev, R08_RAM_WRITE_ACTION, 0); - } - reg_w1(gspca_dev, R07_TABLE_LEN, i); - reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam; - - cam = &gspca_dev->cam; - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); - - return 0; -} - -static void tv_8532_setReg(struct gspca_dev *gspca_dev) -{ - reg_w1(gspca_dev, R3B_Test3, 0x0a); /* Test0Sel = 10 */ - /******************************************************/ - reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90); - reg_w1(gspca_dev, R0F_AD_HEIGHTH, 0x01); - reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f); - reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44); - /* begin active line */ - reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00); - /* mirror and digital gain */ - reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a); - - reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02); - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); - /* = 0x84 */ -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - tv_8532WriteEEprom(gspca_dev); - - return 0; -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, val); - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); - /* 0x84 */ -} - -static void setgain(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w2(gspca_dev, R20_GAIN_G1L, val); - reg_w2(gspca_dev, R22_GAIN_RL, val); - reg_w2(gspca_dev, R24_GAIN_BL, val); - reg_w2(gspca_dev, R26_GAIN_G2L, val); -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); /* 0x20; 0x0c */ - reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); - - /************************************************/ - reg_w1(gspca_dev, R28_QUANT, 0x90); - /* 0x72 compressed mode 0x28 */ - if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - /* 176x144 */ - reg_w1(gspca_dev, R29_LINE, 0x41); - /* CIF - 2 lines/packet */ - } else { - /* 352x288 */ - reg_w1(gspca_dev, R29_LINE, 0x81); - /* CIF - 2 lines/packet */ - } - /************************************************/ - reg_w1(gspca_dev, R2C_POLARITY, 0x10); /* slow clock */ - reg_w1(gspca_dev, R2D_POINT, 0x14); - reg_w1(gspca_dev, R2E_POINTH, 0x01); - reg_w1(gspca_dev, R2F_POINTB, 0x12); - reg_w1(gspca_dev, R30_POINTBH, 0x01); - - tv_8532_setReg(gspca_dev); - - /************************************************/ - reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ - msleep(200); - reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ - - gspca_dev->empty_packet = 0; /* check the empty packets */ - sd->packet = 0; /* ignore the first packets */ - - return 0; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - int packet_type0, packet_type1; - - packet_type0 = packet_type1 = INTER_PACKET; - if (gspca_dev->empty_packet) { - gspca_dev->empty_packet = 0; - sd->packet = gspca_dev->height / 2; - packet_type0 = FIRST_PACKET; - } else if (sd->packet == 0) - return; /* 2 more lines in 352x288 ! */ - sd->packet--; - if (sd->packet == 0) - packet_type1 = LAST_PACKET; - - /* each packet contains: - * - header 2 bytes - * - RGRG line - * - 4 bytes - * - GBGB line - * - 4 bytes - */ - gspca_frame_add(gspca_dev, packet_type0, - data + 2, gspca_dev->width); - gspca_frame_add(gspca_dev, packet_type1, - data + gspca_dev->width + 5, gspca_dev->width); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - setgain(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 2); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0x18f, 1, 0x18f); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 0x7ff, 1, 0x100); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x046d, 0x0920)}, - {USB_DEVICE(0x046d, 0x0921)}, - {USB_DEVICE(0x0545, 0x808b)}, - {USB_DEVICE(0x0545, 0x8333)}, - {USB_DEVICE(0x0923, 0x010f)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c deleted file mode 100644 index e50079503d96..000000000000 --- a/drivers/media/video/gspca/vc032x.c +++ /dev/null @@ -1,3850 +0,0 @@ -/* - * Z-star vc0321 library - * - * Copyright (C) 2009-2010 Jean-François Moine - * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl - * Copyright (C) 2006 Michel Xhaard - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "vc032x" - -#include "gspca.h" - -MODULE_AUTHOR("Jean-François Moine "); -MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); -MODULE_LICENSE("GPL"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct { /* hvflip cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; - - u8 image_offset; - - u8 bridge; - u8 sensor; - u8 flags; -#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */ -#define FL_HFLIP 0x02 /* mirrored by default */ -#define FL_VFLIP 0x04 /* vertical flipped by default */ -}; -enum bridges { - BRIDGE_VC0321, - BRIDGE_VC0323, -}; -enum sensors { - SENSOR_HV7131R, - SENSOR_MI0360, - SENSOR_MI1310_SOC, - SENSOR_MI1320, - SENSOR_MI1320_SOC, - SENSOR_OV7660, - SENSOR_OV7670, - SENSOR_PO1200, - SENSOR_PO3130NC, - SENSOR_POxxxx, - NSENSORS -}; - - -static const struct v4l2_pix_format vc0321_mode[] = { - {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format vc0323_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, - {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */ - .bytesperline = 1280, - .sizeimage = 1280 * 960 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 2}, -}; -static const struct v4l2_pix_format bi_mode[] = { - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 1280, - .sizeimage = 1280 * 1024 * 2, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format svga_mode[] = { - {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 800, - .sizeimage = 800 * 600 * 1 / 4 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -/* OV7660/7670 registers */ -#define OV7660_REG_MVFP 0x1e -#define OV7660_MVFP_MIRROR 0x20 -#define OV7660_MVFP_VFLIP 0x10 - -static const u8 mi0360_matrix[9] = { - 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 -}; - -static const u8 mi0360_initVGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */ - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x2c, 0x50, 0xcc}, - {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, - {0xb8, 0x2f, 0xf8, 0xcc}, - {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf8, 0xcc}, - {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x50, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xb8, 0x01, 0x79, 0xcc}, - {0xb8, 0x08, 0xe0, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xb8, 0x01, 0x79, 0xcc}, - {0xb8, 0x14, 0x18, 0xcc}, - {0xb8, 0xb2, 0x0a, 0xcc}, - {0xb8, 0xb4, 0x0a, 0xcc}, - {0xb8, 0xb5, 0x0a, 0xcc}, - {0xb8, 0xfe, 0x00, 0xcc}, - {0xb8, 0xff, 0x28, 0xcc}, - {0xb9, 0x00, 0x28, 0xcc}, - {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, - {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, - {0xb9, 0x05, 0x3c, 0xcc}, - {0xb9, 0x06, 0x3c, 0xcc}, - {0xb9, 0x07, 0x3c, 0xcc}, - {0xb9, 0x08, 0x3c, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0x31, 0x00, 0x00, 0xbb}, - {0x09, 0x01, 0xc7, 0xbb}, - {0x34, 0x01, 0x00, 0xbb}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x62, 0x04, 0x11, 0xbb}, - {0x03, 0x01, 0xe0, 0xbb}, - {0x2c, 0x00, 0x2c, 0xbb}, - {0x20, 0xd0, 0x00, 0xbb}, - {0x01, 0x00, 0x08, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0x05, 0x00, 0x20, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x02, 0xcc}, - {0xb6, 0x02, 0x80, 0xcc}, - {0xb6, 0x05, 0x01, 0xcc}, - {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, - {0xb6, 0x18, 0x02, 0xcc}, - {0xb6, 0x17, 0x58, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x35, 0x00, 0x60, 0xbb}, - {0xb3, 0x5c, 0x01, 0xcc}, - {} -}; -static const u8 mi0360_initQVGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xbc, 0x00, 0xd1, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x2c, 0x50, 0xcc}, - {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, - {0xb8, 0x2f, 0xf8, 0xcc}, - {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf8, 0xcc}, - {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x50, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xb8, 0x01, 0x79, 0xcc}, - {0xb8, 0x08, 0xe0, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xb8, 0x01, 0x79, 0xcc}, - {0xb8, 0x14, 0x18, 0xcc}, - {0xb8, 0xb2, 0x0a, 0xcc}, - {0xb8, 0xb4, 0x0a, 0xcc}, - {0xb8, 0xb5, 0x0a, 0xcc}, - {0xb8, 0xfe, 0x00, 0xcc}, - {0xb8, 0xff, 0x28, 0xcc}, - {0xb9, 0x00, 0x28, 0xcc}, - {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, - {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, - {0xb9, 0x05, 0x3c, 0xcc}, - {0xb9, 0x06, 0x3c, 0xcc}, - {0xb9, 0x07, 0x3c, 0xcc}, - {0xb9, 0x08, 0x3c, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0x31, 0x00, 0x00, 0xbb}, - {0x09, 0x01, 0xc7, 0xbb}, - {0x34, 0x01, 0x00, 0xbb}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x62, 0x04, 0x11, 0xbb}, - {0x03, 0x01, 0xe0, 0xbb}, - {0x2c, 0x00, 0x2c, 0xbb}, - {0x20, 0xd0, 0x00, 0xbb}, - {0x01, 0x00, 0x08, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0x05, 0x00, 0x20, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, - {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, - {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, - {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0xbc, 0x02, 0x18, 0xcc}, - {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, - {0xbc, 0x0a, 0x10, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, - {0xbc, 0x0c, 0x00, 0xcc}, - {0x35, 0x00, 0xef, 0xbb}, - {0xb3, 0x5c, 0x01, 0xcc}, - {} -}; - -static const u8 mi1310_socinitVGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x64, 0xcc}, - {0xb3, 0x00, 0x65, 0xcc}, - {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x00, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */ - {0xb3, 0x02, 0x00, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x03, 0xcc}, - {0xb3, 0x23, 0xc0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x04, 0xcc}, - {0xb3, 0x17, 0xff, 0xcc}, - {0xb3, 0x00, 0x65, 0xcc}, - {0xb8, 0x00, 0x00, 0xcc}, - {0xbc, 0x00, 0xd0, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xf0, 0x00, 0x02, 0xbb}, - {0xc8, 0x9f, 0x0b, 0xbb}, - {0x5b, 0x00, 0x01, 0xbb}, - {0x2f, 0xde, 0x20, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ - {0xf0, 0x00, 0x01, 0xbb}, - {0x05, 0x00, 0x07, 0xbb}, - {0x34, 0x00, 0x00, 0xbb}, - {0x35, 0xff, 0x00, 0xbb}, - {0xdc, 0x07, 0x02, 0xbb}, - {0xdd, 0x3c, 0x18, 0xbb}, - {0xde, 0x92, 0x6d, 0xbb}, - {0xdf, 0xcd, 0xb1, 0xbb}, - {0xe0, 0xff, 0xe7, 0xbb}, - {0x06, 0xf0, 0x0d, 0xbb}, - {0x06, 0x70, 0x0e, 0xbb}, - {0x4c, 0x00, 0x01, 0xbb}, - {0x4d, 0x00, 0x01, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x2e, 0x0c, 0x55, 0xbb}, - {0x21, 0xb6, 0x6e, 0xbb}, - {0x36, 0x30, 0x10, 0xbb}, - {0x37, 0x00, 0xc1, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x07, 0x00, 0x84, 0xbb}, - {0x08, 0x02, 0x4a, 0xbb}, - {0x05, 0x01, 0x10, 0xbb}, - {0x06, 0x00, 0x39, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x58, 0x02, 0x67, 0xbb}, - {0x57, 0x02, 0x00, 0xbb}, - {0x5a, 0x02, 0x67, 0xbb}, - {0x59, 0x02, 0x00, 0xbb}, - {0x5c, 0x12, 0x0d, 0xbb}, - {0x5d, 0x16, 0x11, 0xbb}, - {0x39, 0x06, 0x18, 0xbb}, - {0x3a, 0x06, 0x18, 0xbb}, - {0x3b, 0x06, 0x18, 0xbb}, - {0x3c, 0x06, 0x18, 0xbb}, - {0x64, 0x7b, 0x5b, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x36, 0x30, 0x10, 0xbb}, - {0x37, 0x00, 0xc0, 0xbb}, - {0xbc, 0x0e, 0x00, 0xcc}, - {0xbc, 0x0f, 0x05, 0xcc}, - {0xbc, 0x10, 0xc0, 0xcc}, - {0xbc, 0x11, 0x03, 0xcc}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x02, 0xcc}, - {0xb6, 0x02, 0x80, 0xcc}, - {0xb6, 0x05, 0x01, 0xcc}, - {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x25, 0xcc}, - {0xb6, 0x18, 0x02, 0xcc}, - {0xb6, 0x17, 0x58, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x00, 0xcc}, - {0xbc, 0x02, 0x18, 0xcc}, - {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, - {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, - {0xbc, 0x0c, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x80, 0x00, 0x03, 0xbb}, - {0x81, 0xc7, 0x14, 0xbb}, - {0x82, 0xeb, 0xe8, 0xbb}, - {0x83, 0xfe, 0xf4, 0xbb}, - {0x84, 0xcd, 0x10, 0xbb}, - {0x85, 0xf3, 0xee, 0xbb}, - {0x86, 0xff, 0xf1, 0xbb}, - {0x87, 0xcd, 0x10, 0xbb}, - {0x88, 0xf3, 0xee, 0xbb}, - {0x89, 0x01, 0xf1, 0xbb}, - {0x8a, 0xe5, 0x17, 0xbb}, - {0x8b, 0xe8, 0xe2, 0xbb}, - {0x8c, 0xf7, 0xed, 0xbb}, - {0x8d, 0x00, 0xff, 0xbb}, - {0x8e, 0xec, 0x10, 0xbb}, - {0x8f, 0xf0, 0xed, 0xbb}, - {0x90, 0xf9, 0xf2, 0xbb}, - {0x91, 0x00, 0x00, 0xbb}, - {0x92, 0xe9, 0x0d, 0xbb}, - {0x93, 0xf4, 0xf2, 0xbb}, - {0x94, 0xfb, 0xf5, 0xbb}, - {0x95, 0x00, 0xff, 0xbb}, - {0xb6, 0x0f, 0x08, 0xbb}, - {0xb7, 0x3d, 0x16, 0xbb}, - {0xb8, 0x0c, 0x04, 0xbb}, - {0xb9, 0x1c, 0x07, 0xbb}, - {0xba, 0x0a, 0x03, 0xbb}, - {0xbb, 0x1b, 0x09, 0xbb}, - {0xbc, 0x17, 0x0d, 0xbb}, - {0xbd, 0x23, 0x1d, 0xbb}, - {0xbe, 0x00, 0x28, 0xbb}, - {0xbf, 0x11, 0x09, 0xbb}, - {0xc0, 0x16, 0x15, 0xbb}, - {0xc1, 0x00, 0x1b, 0xbb}, - {0xc2, 0x0e, 0x07, 0xbb}, - {0xc3, 0x14, 0x10, 0xbb}, - {0xc4, 0x00, 0x17, 0xbb}, - {0x06, 0x74, 0x8e, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x06, 0xf4, 0x8e, 0xbb}, - {0x00, 0x00, 0x50, 0xdd}, - {0x06, 0x74, 0x8e, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x24, 0x50, 0x20, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x34, 0x0c, 0x50, 0xbb}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x03, 0x03, 0xc0, 0xbb}, - {}, -}; -static const u8 mi1310_socinitQVGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, - {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, - {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, - {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, - {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, - {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, - {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, - {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ - {0xf0, 0x00, 0x01, 0xbb}, - {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, - {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, - {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, - {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, - {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, - {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, - {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, - {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, - {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, - {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, - {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, - {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, - {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, - {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, - {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, - {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, - {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, - {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, - {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, - {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, - {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, - {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, - {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, - {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, - {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, - {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, - {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, - {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, - {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, - {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, - {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, - {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, - {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, - {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, - {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, - {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, - {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, - {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, - {0x03, 0x03, 0xc0, 0xbb}, - {}, -}; -static const u8 mi1310_soc_InitSXGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x64, 0xcc}, - {0xb3, 0x00, 0x65, 0xcc}, - {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x00, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, - {0xb3, 0x02, 0x00, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x0d, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x03, 0xcc}, - {0xb3, 0x23, 0xc0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x04, 0xcc}, - {0xb3, 0x17, 0xff, 0xcc}, - {0xb3, 0x00, 0x65, 0xcc}, - {0xb8, 0x00, 0x00, 0xcc}, - {0xbc, 0x00, 0x70, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xf0, 0x00, 0x02, 0xbb}, - {0xc8, 0x9f, 0x0b, 0xbb}, - {0x5b, 0x00, 0x01, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ - {0xf0, 0x00, 0x01, 0xbb}, - {0x05, 0x00, 0x07, 0xbb}, - {0x34, 0x00, 0x00, 0xbb}, - {0x35, 0xff, 0x00, 0xbb}, - {0xdc, 0x07, 0x02, 0xbb}, - {0xdd, 0x3c, 0x18, 0xbb}, - {0xde, 0x92, 0x6d, 0xbb}, - {0xdf, 0xcd, 0xb1, 0xbb}, - {0xe0, 0xff, 0xe7, 0xbb}, - {0x06, 0xf0, 0x0d, 0xbb}, - {0x06, 0x70, 0x0e, 0xbb}, - {0x4c, 0x00, 0x01, 0xbb}, - {0x4d, 0x00, 0x01, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x2e, 0x0c, 0x60, 0xbb}, - {0x21, 0xb6, 0x6e, 0xbb}, - {0x37, 0x01, 0x40, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x07, 0x00, 0x84, 0xbb}, - {0x08, 0x02, 0x4a, 0xbb}, - {0x05, 0x01, 0x10, 0xbb}, - {0x06, 0x00, 0x39, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x58, 0x02, 0x67, 0xbb}, - {0x57, 0x02, 0x00, 0xbb}, - {0x5a, 0x02, 0x67, 0xbb}, - {0x59, 0x02, 0x00, 0xbb}, - {0x5c, 0x12, 0x0d, 0xbb}, - {0x5d, 0x16, 0x11, 0xbb}, - {0x39, 0x06, 0x18, 0xbb}, - {0x3a, 0x06, 0x18, 0xbb}, - {0x3b, 0x06, 0x18, 0xbb}, - {0x3c, 0x06, 0x18, 0xbb}, - {0x64, 0x7b, 0x5b, 0xbb}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x05, 0xcc}, - {0xb6, 0x02, 0x00, 0xcc}, - {0xb6, 0x05, 0x03, 0xcc}, - {0xb6, 0x04, 0xc0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x29, 0xcc}, - {0xb6, 0x18, 0x09, 0xcc}, - {0xb6, 0x17, 0x60, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x00, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0x00, 0x00, 0x80, 0xdd}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x22, 0xa0, 0x78, 0xbb}, - {0x23, 0xa0, 0x78, 0xbb}, - {0x24, 0x7f, 0x00, 0xbb}, - {0x28, 0xea, 0x02, 0xbb}, - {0x29, 0x86, 0x7a, 0xbb}, - {0x5e, 0x52, 0x4c, 0xbb}, - {0x5f, 0x20, 0x24, 0xbb}, - {0x60, 0x00, 0x02, 0xbb}, - {0x02, 0x00, 0xee, 0xbb}, - {0x03, 0x39, 0x23, 0xbb}, - {0x04, 0x07, 0x24, 0xbb}, - {0x09, 0x00, 0xc0, 0xbb}, - {0x0a, 0x00, 0x79, 0xbb}, - {0x0b, 0x00, 0x04, 0xbb}, - {0x0c, 0x00, 0x5c, 0xbb}, - {0x0d, 0x00, 0xd9, 0xbb}, - {0x0e, 0x00, 0x53, 0xbb}, - {0x0f, 0x00, 0x21, 0xbb}, - {0x10, 0x00, 0xa4, 0xbb}, - {0x11, 0x00, 0xe5, 0xbb}, - {0x15, 0x00, 0x00, 0xbb}, - {0x16, 0x00, 0x00, 0xbb}, - {0x17, 0x00, 0x00, 0xbb}, - {0x18, 0x00, 0x00, 0xbb}, - {0x19, 0x00, 0x00, 0xbb}, - {0x1a, 0x00, 0x00, 0xbb}, - {0x1b, 0x00, 0x00, 0xbb}, - {0x1c, 0x00, 0x00, 0xbb}, - {0x1d, 0x00, 0x00, 0xbb}, - {0x1e, 0x00, 0x00, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x00, 0x20, 0xdd}, - {0x06, 0xf0, 0x8e, 0xbb}, - {0x00, 0x00, 0x80, 0xdd}, - {0x06, 0x70, 0x8e, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x00, 0x00, 0x20, 0xdd}, - {0x5e, 0x6a, 0x53, 0xbb}, - {0x5f, 0x40, 0x2c, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x00, 0x20, 0xdd}, - {0x58, 0x00, 0x00, 0xbb}, - {0x53, 0x09, 0x03, 0xbb}, - {0x54, 0x31, 0x18, 0xbb}, - {0x55, 0x8b, 0x5f, 0xbb}, - {0x56, 0xc0, 0xa9, 0xbb}, - {0x57, 0xe0, 0xd2, 0xbb}, - {0xe1, 0x00, 0x00, 0xbb}, - {0xdc, 0x09, 0x03, 0xbb}, - {0xdd, 0x31, 0x18, 0xbb}, - {0xde, 0x8b, 0x5f, 0xbb}, - {0xdf, 0xc0, 0xa9, 0xbb}, - {0xe0, 0xe0, 0xd2, 0xbb}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x06, 0xf0, 0x8e, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x2f, 0xde, 0x20, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x24, 0x50, 0x20, 0xbb}, - {0xbc, 0x0e, 0x00, 0xcc}, - {0xbc, 0x0f, 0x05, 0xcc}, - {0xbc, 0x10, 0xc0, 0xcc}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x34, 0x0c, 0x50, 0xbb}, - {0xbc, 0x11, 0x03, 0xcc}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x80, 0x00, 0x03, 0xbb}, - {0x81, 0xc7, 0x14, 0xbb}, - {0x82, 0xeb, 0xe8, 0xbb}, - {0x83, 0xfe, 0xf4, 0xbb}, - {0x84, 0xcd, 0x10, 0xbb}, - {0x85, 0xf3, 0xee, 0xbb}, - {0x86, 0xff, 0xf1, 0xbb}, - {0x87, 0xcd, 0x10, 0xbb}, - {0x88, 0xf3, 0xee, 0xbb}, - {0x89, 0x01, 0xf1, 0xbb}, - {0x8a, 0xe5, 0x17, 0xbb}, - {0x8b, 0xe8, 0xe2, 0xbb}, - {0x8c, 0xf7, 0xed, 0xbb}, - {0x8d, 0x00, 0xff, 0xbb}, - {0x8e, 0xec, 0x10, 0xbb}, - {0x8f, 0xf0, 0xed, 0xbb}, - {0x90, 0xf9, 0xf2, 0xbb}, - {0x91, 0x00, 0x00, 0xbb}, - {0x92, 0xe9, 0x0d, 0xbb}, - {0x93, 0xf4, 0xf2, 0xbb}, - {0x94, 0xfb, 0xf5, 0xbb}, - {0x95, 0x00, 0xff, 0xbb}, - {0xb6, 0x0f, 0x08, 0xbb}, - {0xb7, 0x3d, 0x16, 0xbb}, - {0xb8, 0x0c, 0x04, 0xbb}, - {0xb9, 0x1c, 0x07, 0xbb}, - {0xba, 0x0a, 0x03, 0xbb}, - {0xbb, 0x1b, 0x09, 0xbb}, - {0xbc, 0x17, 0x0d, 0xbb}, - {0xbd, 0x23, 0x1d, 0xbb}, - {0xbe, 0x00, 0x28, 0xbb}, - {0xbf, 0x11, 0x09, 0xbb}, - {0xc0, 0x16, 0x15, 0xbb}, - {0xc1, 0x00, 0x1b, 0xbb}, - {0xc2, 0x0e, 0x07, 0xbb}, - {0xc3, 0x14, 0x10, 0xbb}, - {0xc4, 0x00, 0x17, 0xbb}, - {0x06, 0x74, 0x8e, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x03, 0x03, 0xc0, 0xbb}, - {} -}; - -static const u8 mi1320_gamma[17] = { - 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff -}; -static const u8 mi1320_matrix[9] = { - 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 -}; -static const u8 mi1320_initVGA_data[][4] = { - {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, - {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, - {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, - {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */ - {0xb3, 0x02, 0x00, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, - {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, - {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, - {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, - {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, - {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, - {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb}, - {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb}, - {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, - {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb}, - {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb}, - {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb}, - {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb}, - {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb}, - {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb}, - {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb}, - {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb}, - {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb}, - {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb}, - {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb}, - {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb}, - {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb}, - {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb}, - {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb}, - {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb}, - {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb}, - {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb}, - {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb}, - {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, - {0x08, 0x00, 0x27, 0xbb}, - {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */ - {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, - {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb}, - {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb}, - {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb}, - {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, - {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, - {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, - {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, - {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, - {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, - {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, - {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, - {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc}, - {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc}, - {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, - {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, - {} -}; -static const u8 mi1320_initQVGA_data[][4] = { - {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, - {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, - {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, - {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc}, - {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb}, - {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb}, - {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb}, - {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, - {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, - {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb}, - {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb}, - {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, - {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb}, - {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb}, - {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb}, - {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb}, - {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb}, - {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb}, - {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb}, - {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb}, - {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb}, - {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, - {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb}, - {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb}, - {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb}, - {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb}, - {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb}, - {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb}, - {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb}, - {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb}, - {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb}, - {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, - {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb}, - {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, - {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb}, - {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb}, - {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb}, - {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb}, - {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb}, - {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb}, - {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb}, - {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, - {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, - {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {} -}; - -static const u8 mi1320_soc_InitVGA[][4] = { - {0xb3, 0x01, 0x01, 0xcc}, - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0x00, 0x00, 0x30, 0xdd}, - {0xb3, 0x00, 0x64, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */ - {0xb3, 0x02, 0x00, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb8, 0x00, 0x00, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0xc8, 0x00, 0x00, 0xbb}, - {0x00, 0x00, 0x30, 0xdd}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x07, 0x00, 0xe0, 0xbb}, - {0x08, 0x00, 0x0b, 0xbb}, - {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ - {0xbf, 0xc0, 0x26, 0xcc}, - {0xbf, 0xc1, 0x02, 0xcc}, - {0xbf, 0xcc, 0x04, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x05, 0x01, 0x78, 0xbb}, - {0x06, 0x00, 0x11, 0xbb}, - {0x07, 0x01, 0x42, 0xbb}, - {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ - {0x21, 0x80, 0x00, 0xbb}, - {0x22, 0x0d, 0x0f, 0xbb}, - {0x24, 0x80, 0x00, 0xbb}, - {0x59, 0x00, 0xff, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x39, 0x03, 0xca, 0xbb}, - {0x3a, 0x06, 0x80, 0xbb}, - {0x3b, 0x01, 0x52, 0xbb}, - {0x3c, 0x05, 0x40, 0xbb}, - {0x57, 0x01, 0x9c, 0xbb}, - {0x58, 0x01, 0xee, 0xbb}, - {0x59, 0x00, 0xf0, 0xbb}, - {0x5a, 0x01, 0x20, 0xbb}, - {0x5c, 0x1d, 0x17, 0xbb}, - {0x5d, 0x22, 0x1c, 0xbb}, - {0x64, 0x1e, 0x1c, 0xbb}, - {0x5b, 0x00, 0x00, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x22, 0xa0, 0x78, 0xbb}, - {0x23, 0xa0, 0x78, 0xbb}, - {0x24, 0x7f, 0x00, 0xbb}, - {0x28, 0xea, 0x02, 0xbb}, - {0x29, 0x86, 0x7a, 0xbb}, - {0x5e, 0x52, 0x4c, 0xbb}, - {0x5f, 0x20, 0x24, 0xbb}, - {0x60, 0x00, 0x02, 0xbb}, - {0x02, 0x00, 0xee, 0xbb}, - {0x03, 0x39, 0x23, 0xbb}, - {0x04, 0x07, 0x24, 0xbb}, - {0x09, 0x00, 0xc0, 0xbb}, - {0x0a, 0x00, 0x79, 0xbb}, - {0x0b, 0x00, 0x04, 0xbb}, - {0x0c, 0x00, 0x5c, 0xbb}, - {0x0d, 0x00, 0xd9, 0xbb}, - {0x0e, 0x00, 0x53, 0xbb}, - {0x0f, 0x00, 0x21, 0xbb}, - {0x10, 0x00, 0xa4, 0xbb}, - {0x11, 0x00, 0xe5, 0xbb}, - {0x15, 0x00, 0x00, 0xbb}, - {0x16, 0x00, 0x00, 0xbb}, - {0x17, 0x00, 0x00, 0xbb}, - {0x18, 0x00, 0x00, 0xbb}, - {0x19, 0x00, 0x00, 0xbb}, - {0x1a, 0x00, 0x00, 0xbb}, - {0x1b, 0x00, 0x00, 0xbb}, - {0x1c, 0x00, 0x00, 0xbb}, - {0x1d, 0x00, 0x00, 0xbb}, - {0x1e, 0x00, 0x00, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x06, 0xe0, 0x0e, 0xbb}, - {0x06, 0x60, 0x0e, 0xbb}, - {0xb3, 0x5c, 0x01, 0xcc}, - {} -}; -static const u8 mi1320_soc_InitQVGA[][4] = { - {0xb3, 0x01, 0x01, 0xcc}, - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0x00, 0x00, 0x30, 0xdd}, - {0xb3, 0x00, 0x64, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xc8, 0xcc}, - {0xb3, 0x02, 0x00, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb8, 0x00, 0x00, 0xcc}, - {0xbc, 0x00, 0xd1, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0xc8, 0x00, 0x00, 0xbb}, - {0x00, 0x00, 0x30, 0xdd}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x07, 0x00, 0xe0, 0xbb}, - {0x08, 0x00, 0x0b, 0xbb}, - {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ - {0xbf, 0xc0, 0x26, 0xcc}, - {0xbf, 0xc1, 0x02, 0xcc}, - {0xbf, 0xcc, 0x04, 0xcc}, - {0xbc, 0x02, 0x18, 0xcc}, - {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, - {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, - {0xbc, 0x0c, 0x00, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x05, 0x01, 0x78, 0xbb}, - {0x06, 0x00, 0x11, 0xbb}, - {0x07, 0x01, 0x42, 0xbb}, - {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ - {0x21, 0x80, 0x00, 0xbb}, - {0x22, 0x0d, 0x0f, 0xbb}, - {0x24, 0x80, 0x00, 0xbb}, - {0x59, 0x00, 0xff, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x39, 0x03, 0xca, 0xbb}, - {0x3a, 0x06, 0x80, 0xbb}, - {0x3b, 0x01, 0x52, 0xbb}, - {0x3c, 0x05, 0x40, 0xbb}, - {0x57, 0x01, 0x9c, 0xbb}, - {0x58, 0x01, 0xee, 0xbb}, - {0x59, 0x00, 0xf0, 0xbb}, - {0x5a, 0x01, 0x20, 0xbb}, - {0x5c, 0x1d, 0x17, 0xbb}, - {0x5d, 0x22, 0x1c, 0xbb}, - {0x64, 0x1e, 0x1c, 0xbb}, - {0x5b, 0x00, 0x00, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x22, 0xa0, 0x78, 0xbb}, - {0x23, 0xa0, 0x78, 0xbb}, - {0x24, 0x7f, 0x00, 0xbb}, - {0x28, 0xea, 0x02, 0xbb}, - {0x29, 0x86, 0x7a, 0xbb}, - {0x5e, 0x52, 0x4c, 0xbb}, - {0x5f, 0x20, 0x24, 0xbb}, - {0x60, 0x00, 0x02, 0xbb}, - {0x02, 0x00, 0xee, 0xbb}, - {0x03, 0x39, 0x23, 0xbb}, - {0x04, 0x07, 0x24, 0xbb}, - {0x09, 0x00, 0xc0, 0xbb}, - {0x0a, 0x00, 0x79, 0xbb}, - {0x0b, 0x00, 0x04, 0xbb}, - {0x0c, 0x00, 0x5c, 0xbb}, - {0x0d, 0x00, 0xd9, 0xbb}, - {0x0e, 0x00, 0x53, 0xbb}, - {0x0f, 0x00, 0x21, 0xbb}, - {0x10, 0x00, 0xa4, 0xbb}, - {0x11, 0x00, 0xe5, 0xbb}, - {0x15, 0x00, 0x00, 0xbb}, - {0x16, 0x00, 0x00, 0xbb}, - {0x17, 0x00, 0x00, 0xbb}, - {0x18, 0x00, 0x00, 0xbb}, - {0x19, 0x00, 0x00, 0xbb}, - {0x1a, 0x00, 0x00, 0xbb}, - {0x1b, 0x00, 0x00, 0xbb}, - {0x1c, 0x00, 0x00, 0xbb}, - {0x1d, 0x00, 0x00, 0xbb}, - {0x1e, 0x00, 0x00, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x06, 0xe0, 0x0e, 0xbb}, - {0x06, 0x60, 0x0e, 0xbb}, - {0xb3, 0x5c, 0x01, 0xcc}, - {} -}; -static const u8 mi1320_soc_InitSXGA[][4] = { - {0xb3, 0x01, 0x01, 0xcc}, - {0xb0, 0x03, 0x19, 0xcc}, - {0x00, 0x00, 0x30, 0xdd}, - {0xb3, 0x00, 0x64, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xc8, 0xcc}, - {0xb3, 0x02, 0x00, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x04, 0xcc}, - {0xb3, 0x23, 0x00, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x04, 0xcc}, - {0xb3, 0x17, 0xff, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x00, 0x00, 0x30, 0xdd}, - {0xc8, 0x9f, 0x0b, 0xbb}, - {0x00, 0x00, 0x20, 0xdd}, - {0x5b, 0x00, 0x01, 0xbb}, - {0x00, 0x00, 0x20, 0xdd}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x00, 0x30, 0xdd}, - {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ - {0x00, 0x00, 0x20, 0xdd}, - {0xbf, 0xc0, 0x26, 0xcc}, - {0xbf, 0xc1, 0x02, 0xcc}, - {0xbf, 0xcc, 0x04, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x05, 0x01, 0x78, 0xbb}, - {0x06, 0x00, 0x11, 0xbb}, - {0x07, 0x01, 0x42, 0xbb}, - {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ - {0x21, 0x80, 0x00, 0xbb}, - {0x22, 0x0d, 0x0f, 0xbb}, - {0x24, 0x80, 0x00, 0xbb}, - {0x59, 0x00, 0xff, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x39, 0x03, 0xca, 0xbb}, - {0x3a, 0x06, 0x80, 0xbb}, - {0x3b, 0x01, 0x52, 0xbb}, - {0x3c, 0x05, 0x40, 0xbb}, - {0x57, 0x01, 0x9c, 0xbb}, - {0x58, 0x01, 0xee, 0xbb}, - {0x59, 0x00, 0xf0, 0xbb}, - {0x5a, 0x01, 0x20, 0xbb}, - {0x5c, 0x1d, 0x17, 0xbb}, - {0x5d, 0x22, 0x1c, 0xbb}, - {0x64, 0x1e, 0x1c, 0xbb}, - {0x5b, 0x00, 0x00, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x22, 0xa0, 0x78, 0xbb}, - {0x23, 0xa0, 0x78, 0xbb}, - {0x24, 0x7f, 0x00, 0xbb}, - {0x28, 0xea, 0x02, 0xbb}, - {0x29, 0x86, 0x7a, 0xbb}, - {0x5e, 0x52, 0x4c, 0xbb}, - {0x5f, 0x20, 0x24, 0xbb}, - {0x60, 0x00, 0x02, 0xbb}, - {0x02, 0x00, 0xee, 0xbb}, - {0x03, 0x39, 0x23, 0xbb}, - {0x04, 0x07, 0x24, 0xbb}, - {0x09, 0x00, 0xc0, 0xbb}, - {0x0a, 0x00, 0x79, 0xbb}, - {0x0b, 0x00, 0x04, 0xbb}, - {0x0c, 0x00, 0x5c, 0xbb}, - {0x0d, 0x00, 0xd9, 0xbb}, - {0x0e, 0x00, 0x53, 0xbb}, - {0x0f, 0x00, 0x21, 0xbb}, - {0x10, 0x00, 0xa4, 0xbb}, - {0x11, 0x00, 0xe5, 0xbb}, - {0x15, 0x00, 0x00, 0xbb}, - {0x16, 0x00, 0x00, 0xbb}, - {0x17, 0x00, 0x00, 0xbb}, - {0x18, 0x00, 0x00, 0xbb}, - {0x19, 0x00, 0x00, 0xbb}, - {0x1a, 0x00, 0x00, 0xbb}, - {0x1b, 0x00, 0x00, 0xbb}, - {0x1c, 0x00, 0x00, 0xbb}, - {0x1d, 0x00, 0x00, 0xbb}, - {0x1e, 0x00, 0x00, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x06, 0xe0, 0x0e, 0xbb}, - {0x06, 0x60, 0x0e, 0xbb}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x05, 0x01, 0x13, 0xbb}, - {0x06, 0x00, 0x11, 0xbb}, - {0x07, 0x00, 0x85, 0xbb}, - {0x08, 0x00, 0x27, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ - {0x21, 0x80, 0x00, 0xbb}, - {0x22, 0x0d, 0x0f, 0xbb}, - {0x24, 0x80, 0x00, 0xbb}, - {0x59, 0x00, 0xff, 0xbb}, - {0xf0, 0x00, 0x02, 0xbb}, - {0x39, 0x03, 0x0d, 0xbb}, - {0x3a, 0x06, 0x1b, 0xbb}, - {0x3b, 0x00, 0x95, 0xbb}, - {0x3c, 0x04, 0xdb, 0xbb}, - {0x57, 0x02, 0x00, 0xbb}, - {0x58, 0x02, 0x66, 0xbb}, - {0x59, 0x00, 0xff, 0xbb}, - {0x5a, 0x01, 0x33, 0xbb}, - {0x5c, 0x12, 0x0d, 0xbb}, - {0x5d, 0x16, 0x11, 0xbb}, - {0x64, 0x5e, 0x1c, 0xbb}, - {} -}; -static const u8 po3130_gamma[17] = { - 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff -}; -static const u8 po3130_matrix[9] = { - 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 -}; - -static const u8 po3130_initVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, - {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, - {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */ - {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, - {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, - {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, - {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, - {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, - {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, - {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, - {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, - {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, - {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, - {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, - {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, - {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, - {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, - {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, - {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, - {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, - {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, - {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, - {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, - {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, - {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, - {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, - {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, - {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, - {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, - {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, - {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, - {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, - {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, - {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, - {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, - {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, - {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, - {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, - {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, - {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, - {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, - {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, - {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, - {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, - {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, - {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, - {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, - {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, - {0x00, 0x7e, 0xea, 0xaa}, - {0x00, 0x4c, 0x07, 0xaa}, - {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, - {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, -/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, - {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */ - {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, - {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, - {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, - {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, - {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, - {0xb9, 0x08, 0x3c, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, - {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, - {} -}; -static const u8 po3130_rundata[][4] = { - {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, - {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, - {0x00, 0x44, 0x40, 0xaa}, -/* {0x00, 0xd5, 0x7c, 0xaa}, */ - {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa}, - {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa}, - {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa}, - {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa}, - {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa}, - {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa}, - {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, - {} -}; - -static const u8 po3130_initQVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, - {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, - {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, - {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, - {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, - {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, - {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, - {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, - {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, - {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, - {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, - {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, - {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, - {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, - {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, - {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, - {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, - {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, - {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, - {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, - {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, - {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, - {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, - {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, - {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, - {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, - {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, - {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, - {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, - {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, - {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, - {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, - {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, - {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, - {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, - {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, - {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, - {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, - {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, - {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, - {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, - {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, - {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, - {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, - {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, - {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, - {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, - {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, - {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, - {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa}, - {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, - {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, - {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, - {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, - {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, - {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, - {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, - {0xb9, 0x08, 0x3c, 0xcc}, {0xbc, 0x02, 0x18, 0xcc}, - {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x30, 0xcc}, {0xbc, 0x09, 0x40, 0xcc}, - {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc}, - {0xbc, 0x0c, 0x00, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, - {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, - {} -}; - -static const u8 hv7131r_gamma[17] = { - 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff -}; -static const u8 hv7131r_matrix[9] = { - 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 -}; -static const u8 hv7131r_initVGA_data[][4] = { - {0xb3, 0x01, 0x01, 0xcc}, - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0x00, 0x00, 0x20, 0xdd}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x01, 0x45, 0xcc}, - {0xb3, 0x03, 0x0b, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x02, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */ - {0xb3, 0x00, 0x27, 0xcc}, - {0xbc, 0x00, 0x73, 0xcc}, - {0xb8, 0x00, 0x23, 0xcc}, - {0xb8, 0x2c, 0x50, 0xcc}, - {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, - {0xb8, 0x2f, 0xf8, 0xcc}, - {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf8, 0xcc}, - {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x58, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0x00, 0x01, 0x0c, 0xaa}, - {0x00, 0x14, 0x01, 0xaa}, - {0x00, 0x15, 0xe6, 0xaa}, - {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x17, 0x86, 0xaa}, - {0x00, 0x23, 0x00, 0xaa}, - {0x00, 0x25, 0x03, 0xaa}, - {0x00, 0x26, 0xa9, 0xaa}, - {0x00, 0x27, 0x80, 0xaa}, - {0x00, 0x30, 0x18, 0xaa}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x02, 0xcc}, - {0xb6, 0x02, 0x80, 0xcc}, - {0xb6, 0x05, 0x01, 0xcc}, - {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, - {0xb6, 0x18, 0x02, 0xcc}, - {0xb6, 0x17, 0x58, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {} -}; - -static const u8 hv7131r_initQVGA_data[][4] = { - {0xb3, 0x01, 0x01, 0xcc}, - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0x00, 0x00, 0x20, 0xdd}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x01, 0x45, 0xcc}, - {0xb3, 0x03, 0x0b, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x02, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0x91, 0xcc}, - {0xb3, 0x00, 0x27, 0xcc}, - {0xbc, 0x00, 0xd3, 0xcc}, - {0xb8, 0x00, 0x23, 0xcc}, - {0xb8, 0x2c, 0x50, 0xcc}, - {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, - {0xb8, 0x2f, 0xf8, 0xcc}, - {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf8, 0xcc}, - {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x58, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0x00, 0x01, 0x0c, 0xaa}, - {0x00, 0x14, 0x01, 0xaa}, - {0x00, 0x15, 0xe6, 0xaa}, - {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x17, 0x86, 0xaa}, - {0x00, 0x23, 0x00, 0xaa}, - {0x00, 0x25, 0x03, 0xaa}, - {0x00, 0x26, 0xa9, 0xaa}, - {0x00, 0x27, 0x80, 0xaa}, - {0x00, 0x30, 0x18, 0xaa}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, - {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, - {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, - {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xbc, 0x02, 0x18, 0xcc}, - {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, - {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, - {0xbc, 0x0c, 0x00, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {} -}; - -static const u8 ov7660_gamma[17] = { - 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff -}; -static const u8 ov7660_matrix[9] = { - 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 -}; -static const u8 ov7660_initVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, - {0xb0, 0x03, 0x01, 0xcc}, - {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, - {0xb3, 0x1f, 0x02, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ - {0xb3, 0x00, 0x26, 0xcc}, - {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ - {0xb8, 0x01, 0x7d, 0xcc}, - {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x8f, 0x50, 0xcc}, - {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, - {0x00, 0x12, 0x80, 0xaa}, - {0x00, 0x12, 0x05, 0xaa}, - {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ - {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ - {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ - {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, - {0x00, 0x13, 0xa7, 0xaa}, - {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, - {0x00, 0x36, 0x00, 0xaa}, - {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, - {0x00, 0x39, 0x43, 0xaa}, - {0x00, 0x8d, 0xcf, 0xaa}, - {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, - {0x00, 0x0f, 0x62, 0xaa}, - {0x00, 0x35, 0x84, 0xaa}, - {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ - {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ - {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ - {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, - {0x00, 0x01, 0x80, 0xaa}, - {0x00, 0x02, 0x80, 0xaa}, - {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, - {0xb9, 0x00, 0x28, 0xcc}, - {0xb9, 0x01, 0x28, 0xcc}, {0xb9, 0x02, 0x28, 0xcc}, - {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, - {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, - {0xb9, 0x07, 0x3c, 0xcc}, - {0xb9, 0x08, 0x3c, 0xcc}, - - {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, - - {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, - {} -}; -static const u8 ov7660_initQVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, - {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, - {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, - {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ - {0xb8, 0x01, 0x7d, 0xcc}, -/* sizer */ - {0xbc, 0x00, 0xd3, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc}, - {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, - {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, - {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ - {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ - {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ - {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, - {0x00, 0x13, 0xa7, 0xaa}, - {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, - {0x00, 0x36, 0x00, 0xaa}, - {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, - {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa}, - {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, - {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa}, - {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ - {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ - {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ - {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, - {0x00, 0x01, 0x80, 0xaa}, - {0x00, 0x02, 0x80, 0xaa}, -/* sizer filters */ - {0xbc, 0x02, 0x08, 0xcc}, - {0xbc, 0x03, 0x70, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xbc, 0x04, 0x08, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x3c, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, - {0xbc, 0x0a, 0x04, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, - {0xbc, 0x0c, 0x00, 0xcc}, -/* */ - {0xb8, 0xfe, 0x00, 0xcc}, - {0xb8, 0xff, 0x28, 0xcc}, -/* */ - {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, - {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, - {0xb9, 0x08, 0x3c, 0xcc}, -/* */ - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, /* ff */ - {0x00, 0x29, 0x3c, 0xaa}, - {0xb3, 0x01, 0x45, 0xcc}, /* 45 */ - {} -}; - -static const u8 ov7660_50HZ[][4] = { - {0x00, 0x3b, 0x08, 0xaa}, - {0x00, 0x9d, 0x40, 0xaa}, - {0x00, 0x13, 0xa7, 0xaa}, - {} -}; - -static const u8 ov7660_60HZ[][4] = { - {0x00, 0x3b, 0x00, 0xaa}, - {0x00, 0x9e, 0x40, 0xaa}, - {0x00, 0x13, 0xa7, 0xaa}, - {} -}; - -static const u8 ov7660_NoFliker[][4] = { - {0x00, 0x13, 0x87, 0xaa}, - {} -}; - -static const u8 ov7670_InitVGA[][4] = { - {0xb3, 0x01, 0x05, 0xcc}, - {0x00, 0x00, 0x30, 0xdd}, - {0xb0, 0x03, 0x19, 0xcc}, - {0x00, 0x00, 0x10, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, - {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x66, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb0, 0x16, 0x01, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xb3, 0x03, 0x1f, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xbc, 0x00, 0x41, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0x00, 0x12, 0x80, 0xaa}, - {0x00, 0x00, 0x20, 0xdd}, - {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, - {0x00, 0x6b, 0x0a, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, - {0x00, 0x7a, 0x29, 0xaa}, - {0x00, 0x7b, 0x0e, 0xaa}, - {0x00, 0x7c, 0x1a, 0xaa}, - {0x00, 0x7d, 0x31, 0xaa}, - {0x00, 0x7e, 0x53, 0xaa}, - {0x00, 0x7f, 0x60, 0xaa}, - {0x00, 0x80, 0x6b, 0xaa}, - {0x00, 0x81, 0x73, 0xaa}, - {0x00, 0x82, 0x7b, 0xaa}, - {0x00, 0x83, 0x82, 0xaa}, - {0x00, 0x84, 0x89, 0xaa}, - {0x00, 0x85, 0x96, 0xaa}, - {0x00, 0x86, 0xa1, 0xaa}, - {0x00, 0x87, 0xb7, 0xaa}, - {0x00, 0x88, 0xcc, 0xaa}, - {0x00, 0x89, 0xe1, 0xaa}, - {0x00, 0x13, 0xe0, 0xaa}, - {0x00, 0x00, 0x00, 0xaa}, - {0x00, 0x10, 0x00, 0xaa}, - {0x00, 0x0d, 0x40, 0xaa}, - {0x00, 0x14, 0x28, 0xaa}, - {0x00, 0xa5, 0x05, 0xaa}, - {0x00, 0xab, 0x07, 0xaa}, - {0x00, 0x24, 0x95, 0xaa}, - {0x00, 0x25, 0x33, 0xaa}, - {0x00, 0x26, 0xe3, 0xaa}, - {0x00, 0x9f, 0x88, 0xaa}, - {0x00, 0xa0, 0x78, 0xaa}, - {0x00, 0x55, 0x90, 0xaa}, - {0x00, 0xa1, 0x03, 0xaa}, - {0x00, 0xa6, 0xe0, 0xaa}, - {0x00, 0xa7, 0xd8, 0xaa}, - {0x00, 0xa8, 0xf0, 0xaa}, - {0x00, 0xa9, 0x90, 0xaa}, - {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x13, 0xe5, 0xaa}, - {0x00, 0x0e, 0x61, 0xaa}, - {0x00, 0x0f, 0x4b, 0xaa}, - {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ - {0x00, 0x21, 0x02, 0xaa}, - {0x00, 0x22, 0x91, 0xaa}, - {0x00, 0x29, 0x07, 0xaa}, - {0x00, 0x33, 0x0b, 0xaa}, - {0x00, 0x35, 0x0b, 0xaa}, - {0x00, 0x37, 0x1d, 0xaa}, - {0x00, 0x38, 0x71, 0xaa}, - {0x00, 0x39, 0x2a, 0xaa}, - {0x00, 0x3c, 0x78, 0xaa}, - {0x00, 0x4d, 0x40, 0xaa}, - {0x00, 0x4e, 0x20, 0xaa}, - {0x00, 0x74, 0x19, 0xaa}, - {0x00, 0x8d, 0x4f, 0xaa}, - {0x00, 0x8e, 0x00, 0xaa}, - {0x00, 0x8f, 0x00, 0xaa}, - {0x00, 0x90, 0x00, 0xaa}, - {0x00, 0x91, 0x00, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x9a, 0x80, 0xaa}, - {0x00, 0xb0, 0x84, 0xaa}, - {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0xb2, 0x0e, 0xaa}, - {0x00, 0xb3, 0x82, 0xaa}, - {0x00, 0xb8, 0x0a, 0xaa}, - {0x00, 0x43, 0x14, 0xaa}, - {0x00, 0x44, 0xf0, 0xaa}, - {0x00, 0x45, 0x45, 0xaa}, - {0x00, 0x46, 0x63, 0xaa}, - {0x00, 0x47, 0x2d, 0xaa}, - {0x00, 0x48, 0x46, 0xaa}, - {0x00, 0x59, 0x88, 0xaa}, - {0x00, 0x5a, 0xa0, 0xaa}, - {0x00, 0x5b, 0xc6, 0xaa}, - {0x00, 0x5c, 0x7d, 0xaa}, - {0x00, 0x5d, 0x5f, 0xaa}, - {0x00, 0x5e, 0x19, 0xaa}, - {0x00, 0x6c, 0x0a, 0xaa}, - {0x00, 0x6d, 0x55, 0xaa}, - {0x00, 0x6e, 0x11, 0xaa}, - {0x00, 0x6f, 0x9e, 0xaa}, - {0x00, 0x69, 0x00, 0xaa}, - {0x00, 0x6a, 0x40, 0xaa}, - {0x00, 0x01, 0x40, 0xaa}, - {0x00, 0x02, 0x40, 0xaa}, - {0x00, 0x13, 0xe7, 0xaa}, - {0x00, 0x5f, 0xf0, 0xaa}, - {0x00, 0x60, 0xf0, 0xaa}, - {0x00, 0x61, 0xf0, 0xaa}, - {0x00, 0x27, 0xa0, 0xaa}, - {0x00, 0x28, 0x80, 0xaa}, - {0x00, 0x2c, 0x90, 0xaa}, - {0x00, 0x4f, 0x66, 0xaa}, - {0x00, 0x50, 0x66, 0xaa}, - {0x00, 0x51, 0x00, 0xaa}, - {0x00, 0x52, 0x22, 0xaa}, - {0x00, 0x53, 0x5e, 0xaa}, - {0x00, 0x54, 0x80, 0xaa}, - {0x00, 0x58, 0x9e, 0xaa}, - {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, - {0x00, 0x75, 0x85, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, - {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x0a, 0xaa}, - {0x00, 0x3d, 0x88, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, - {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, - {0x00, 0x62, 0x30, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, - {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, - {0x00, 0x95, 0x0b, 0xaa}, - {0x00, 0x65, 0x00, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x56, 0x50, 0xaa}, - {0x00, 0x34, 0x11, 0xaa}, - {0x00, 0xa4, 0x88, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x97, 0x30, 0xaa}, - {0x00, 0x98, 0x20, 0xaa}, - {0x00, 0x99, 0x30, 0xaa}, - {0x00, 0x9a, 0x84, 0xaa}, - {0x00, 0x9b, 0x29, 0xaa}, - {0x00, 0x9c, 0x03, 0xaa}, - {0x00, 0x78, 0x04, 0xaa}, - {0x00, 0x79, 0x01, 0xaa}, - {0x00, 0xc8, 0xf0, 0xaa}, - {0x00, 0x79, 0x0f, 0xaa}, - {0x00, 0xc8, 0x00, 0xaa}, - {0x00, 0x79, 0x10, 0xaa}, - {0x00, 0xc8, 0x7e, 0xaa}, - {0x00, 0x79, 0x0a, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, - {0x00, 0x79, 0x0b, 0xaa}, - {0x00, 0xc8, 0x01, 0xaa}, - {0x00, 0x79, 0x0c, 0xaa}, - {0x00, 0xc8, 0x0f, 0xaa}, - {0x00, 0x79, 0x0d, 0xaa}, - {0x00, 0xc8, 0x20, 0xaa}, - {0x00, 0x79, 0x09, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, - {0x00, 0x79, 0x02, 0xaa}, - {0x00, 0xc8, 0xc0, 0xaa}, - {0x00, 0x79, 0x03, 0xaa}, - {0x00, 0xc8, 0x40, 0xaa}, - {0x00, 0x79, 0x05, 0xaa}, - {0x00, 0xc8, 0x30, 0xaa}, - {0x00, 0x79, 0x26, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, - {0x00, 0x17, 0x14, 0xaa}, - {0x00, 0x18, 0x02, 0xaa}, - {0x00, 0x32, 0x92, 0xaa}, - {0x00, 0x19, 0x02, 0xaa}, - {0x00, 0x1a, 0x7a, 0xaa}, - {0x00, 0x03, 0x0a, 0xaa}, - {0x00, 0x0c, 0x00, 0xaa}, - {0x00, 0x3e, 0x00, 0xaa}, - {0x00, 0x70, 0x3a, 0xaa}, - {0x00, 0x71, 0x35, 0xaa}, - {0x00, 0x72, 0x11, 0xaa}, - {0x00, 0x73, 0xf0, 0xaa}, - {0x00, 0xa2, 0x02, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, - {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ - {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x24, 0x80, 0xaa}, - {0x00, 0x25, 0x74, 0xaa}, - {0x00, 0x26, 0xd3, 0xaa}, - {0x00, 0x0d, 0x00, 0xaa}, - {0x00, 0x14, 0x18, 0xaa}, - {0x00, 0x9d, 0x99, 0xaa}, - {0x00, 0x9e, 0x7f, 0xaa}, - {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, - {0x00, 0x95, 0x06, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, - {0x00, 0x75, 0x07, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, - {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x00, 0xaa}, - {0x00, 0x3d, 0xc2, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, - {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, - {0xbf, 0xc0, 0x26, 0xcc}, - {0xbf, 0xc1, 0x02, 0xcc}, - {0xbf, 0xcc, 0x04, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x01, 0x45, 0xcc}, - {0x00, 0x77, 0x05, 0xaa}, - {}, -}; - -static const u8 ov7670_InitQVGA[][4] = { - {0xb3, 0x01, 0x05, 0xcc}, - {0x00, 0x00, 0x30, 0xdd}, - {0xb0, 0x03, 0x19, 0xcc}, - {0x00, 0x00, 0x10, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, - {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x66, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb0, 0x16, 0x01, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xb3, 0x03, 0x1f, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xbc, 0x00, 0xd1, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0x00, 0x12, 0x80, 0xaa}, - {0x00, 0x00, 0x20, 0xdd}, - {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, - {0x00, 0x6b, 0x0a, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, - {0x00, 0x7a, 0x29, 0xaa}, - {0x00, 0x7b, 0x0e, 0xaa}, - {0x00, 0x7c, 0x1a, 0xaa}, - {0x00, 0x7d, 0x31, 0xaa}, - {0x00, 0x7e, 0x53, 0xaa}, - {0x00, 0x7f, 0x60, 0xaa}, - {0x00, 0x80, 0x6b, 0xaa}, - {0x00, 0x81, 0x73, 0xaa}, - {0x00, 0x82, 0x7b, 0xaa}, - {0x00, 0x83, 0x82, 0xaa}, - {0x00, 0x84, 0x89, 0xaa}, - {0x00, 0x85, 0x96, 0xaa}, - {0x00, 0x86, 0xa1, 0xaa}, - {0x00, 0x87, 0xb7, 0xaa}, - {0x00, 0x88, 0xcc, 0xaa}, - {0x00, 0x89, 0xe1, 0xaa}, - {0x00, 0x13, 0xe0, 0xaa}, - {0x00, 0x00, 0x00, 0xaa}, - {0x00, 0x10, 0x00, 0xaa}, - {0x00, 0x0d, 0x40, 0xaa}, - {0x00, 0x14, 0x28, 0xaa}, - {0x00, 0xa5, 0x05, 0xaa}, - {0x00, 0xab, 0x07, 0xaa}, - {0x00, 0x24, 0x95, 0xaa}, - {0x00, 0x25, 0x33, 0xaa}, - {0x00, 0x26, 0xe3, 0xaa}, - {0x00, 0x9f, 0x88, 0xaa}, - {0x00, 0xa0, 0x78, 0xaa}, - {0x00, 0x55, 0x90, 0xaa}, - {0x00, 0xa1, 0x03, 0xaa}, - {0x00, 0xa6, 0xe0, 0xaa}, - {0x00, 0xa7, 0xd8, 0xaa}, - {0x00, 0xa8, 0xf0, 0xaa}, - {0x00, 0xa9, 0x90, 0xaa}, - {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x13, 0xe5, 0xaa}, - {0x00, 0x0e, 0x61, 0xaa}, - {0x00, 0x0f, 0x4b, 0xaa}, - {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ - {0x00, 0x21, 0x02, 0xaa}, - {0x00, 0x22, 0x91, 0xaa}, - {0x00, 0x29, 0x07, 0xaa}, - {0x00, 0x33, 0x0b, 0xaa}, - {0x00, 0x35, 0x0b, 0xaa}, - {0x00, 0x37, 0x1d, 0xaa}, - {0x00, 0x38, 0x71, 0xaa}, - {0x00, 0x39, 0x2a, 0xaa}, - {0x00, 0x3c, 0x78, 0xaa}, - {0x00, 0x4d, 0x40, 0xaa}, - {0x00, 0x4e, 0x20, 0xaa}, - {0x00, 0x74, 0x19, 0xaa}, - {0x00, 0x8d, 0x4f, 0xaa}, - {0x00, 0x8e, 0x00, 0xaa}, - {0x00, 0x8f, 0x00, 0xaa}, - {0x00, 0x90, 0x00, 0xaa}, - {0x00, 0x91, 0x00, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x9a, 0x80, 0xaa}, - {0x00, 0xb0, 0x84, 0xaa}, - {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0xb2, 0x0e, 0xaa}, - {0x00, 0xb3, 0x82, 0xaa}, - {0x00, 0xb8, 0x0a, 0xaa}, - {0x00, 0x43, 0x14, 0xaa}, - {0x00, 0x44, 0xf0, 0xaa}, - {0x00, 0x45, 0x45, 0xaa}, - {0x00, 0x46, 0x63, 0xaa}, - {0x00, 0x47, 0x2d, 0xaa}, - {0x00, 0x48, 0x46, 0xaa}, - {0x00, 0x59, 0x88, 0xaa}, - {0x00, 0x5a, 0xa0, 0xaa}, - {0x00, 0x5b, 0xc6, 0xaa}, - {0x00, 0x5c, 0x7d, 0xaa}, - {0x00, 0x5d, 0x5f, 0xaa}, - {0x00, 0x5e, 0x19, 0xaa}, - {0x00, 0x6c, 0x0a, 0xaa}, - {0x00, 0x6d, 0x55, 0xaa}, - {0x00, 0x6e, 0x11, 0xaa}, - {0x00, 0x6f, 0x9e, 0xaa}, - {0x00, 0x69, 0x00, 0xaa}, - {0x00, 0x6a, 0x40, 0xaa}, - {0x00, 0x01, 0x40, 0xaa}, - {0x00, 0x02, 0x40, 0xaa}, - {0x00, 0x13, 0xe7, 0xaa}, - {0x00, 0x5f, 0xf0, 0xaa}, - {0x00, 0x60, 0xf0, 0xaa}, - {0x00, 0x61, 0xf0, 0xaa}, - {0x00, 0x27, 0xa0, 0xaa}, - {0x00, 0x28, 0x80, 0xaa}, - {0x00, 0x2c, 0x90, 0xaa}, - {0x00, 0x4f, 0x66, 0xaa}, - {0x00, 0x50, 0x66, 0xaa}, - {0x00, 0x51, 0x00, 0xaa}, - {0x00, 0x52, 0x22, 0xaa}, - {0x00, 0x53, 0x5e, 0xaa}, - {0x00, 0x54, 0x80, 0xaa}, - {0x00, 0x58, 0x9e, 0xaa}, - {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, - {0x00, 0x75, 0x85, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, - {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x0a, 0xaa}, - {0x00, 0x3d, 0x88, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, - {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, - {0x00, 0x62, 0x30, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, - {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, - {0x00, 0x95, 0x0b, 0xaa}, - {0x00, 0x65, 0x00, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x56, 0x50, 0xaa}, - {0x00, 0x34, 0x11, 0xaa}, - {0x00, 0xa4, 0x88, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x97, 0x30, 0xaa}, - {0x00, 0x98, 0x20, 0xaa}, - {0x00, 0x99, 0x30, 0xaa}, - {0x00, 0x9a, 0x84, 0xaa}, - {0x00, 0x9b, 0x29, 0xaa}, - {0x00, 0x9c, 0x03, 0xaa}, - {0x00, 0x78, 0x04, 0xaa}, - {0x00, 0x79, 0x01, 0xaa}, - {0x00, 0xc8, 0xf0, 0xaa}, - {0x00, 0x79, 0x0f, 0xaa}, - {0x00, 0xc8, 0x00, 0xaa}, - {0x00, 0x79, 0x10, 0xaa}, - {0x00, 0xc8, 0x7e, 0xaa}, - {0x00, 0x79, 0x0a, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, - {0x00, 0x79, 0x0b, 0xaa}, - {0x00, 0xc8, 0x01, 0xaa}, - {0x00, 0x79, 0x0c, 0xaa}, - {0x00, 0xc8, 0x0f, 0xaa}, - {0x00, 0x79, 0x0d, 0xaa}, - {0x00, 0xc8, 0x20, 0xaa}, - {0x00, 0x79, 0x09, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, - {0x00, 0x79, 0x02, 0xaa}, - {0x00, 0xc8, 0xc0, 0xaa}, - {0x00, 0x79, 0x03, 0xaa}, - {0x00, 0xc8, 0x40, 0xaa}, - {0x00, 0x79, 0x05, 0xaa}, - {0x00, 0xc8, 0x30, 0xaa}, - {0x00, 0x79, 0x26, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, - {0x00, 0x17, 0x14, 0xaa}, - {0x00, 0x18, 0x02, 0xaa}, - {0x00, 0x32, 0x92, 0xaa}, - {0x00, 0x19, 0x02, 0xaa}, - {0x00, 0x1a, 0x7a, 0xaa}, - {0x00, 0x03, 0x0a, 0xaa}, - {0x00, 0x0c, 0x00, 0xaa}, - {0x00, 0x3e, 0x00, 0xaa}, - {0x00, 0x70, 0x3a, 0xaa}, - {0x00, 0x71, 0x35, 0xaa}, - {0x00, 0x72, 0x11, 0xaa}, - {0x00, 0x73, 0xf0, 0xaa}, - {0x00, 0xa2, 0x02, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, - {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ - {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x24, 0x80, 0xaa}, - {0x00, 0x25, 0x74, 0xaa}, - {0x00, 0x26, 0xd3, 0xaa}, - {0x00, 0x0d, 0x00, 0xaa}, - {0x00, 0x14, 0x18, 0xaa}, - {0x00, 0x9d, 0x99, 0xaa}, - {0x00, 0x9e, 0x7f, 0xaa}, - {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, - {0x00, 0x95, 0x06, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, - {0x00, 0x75, 0x07, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, - {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x00, 0xaa}, - {0x00, 0x3d, 0xc2, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, - {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, - {0xbc, 0x02, 0x18, 0xcc}, - {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, - {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, - {0xbc, 0x0c, 0x00, 0xcc}, - {0xbf, 0xc0, 0x26, 0xcc}, - {0xbf, 0xc1, 0x02, 0xcc}, - {0xbf, 0xcc, 0x04, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x01, 0x45, 0xcc}, - {0x00, 0x77, 0x05, 0xaa}, - {}, -}; - -/* PO1200 - values from usbvm326.inf and ms-win trace */ -static const u8 po1200_gamma[17] = { - 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff -}; -static const u8 po1200_matrix[9] = { - 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e -}; -static const u8 po1200_initVGA_data[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, /* reset? */ - {0xb0, 0x03, 0x19, 0xcc}, -/* {0x00, 0x00, 0x33, 0xdd}, */ - {0xb0, 0x04, 0x02, 0xcc}, - {0xb0, 0x02, 0x02, 0xcc}, - {0xb3, 0x5d, 0x00, 0xcc}, - {0xb3, 0x01, 0x01, 0xcc}, - {0xb3, 0x00, 0x64, 0xcc}, - {0xb3, 0x00, 0x65, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x02, 0xb2, 0xcc}, - {0xb3, 0x03, 0x18, 0xcc}, - {0xb3, 0x04, 0x15, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x02, 0xcc}, - {0xb3, 0x23, 0x58, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x03, 0xcc}, - {0xb3, 0x17, 0x1f, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xb0, 0x54, 0x13, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x12, 0x05, 0xaa}, - {0x00, 0x13, 0x02, 0xaa}, - {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */ - {0x00, 0x21, 0x00, 0xaa}, - {0x00, 0x25, 0x02, 0xaa}, - {0x00, 0x3c, 0x4f, 0xaa}, - {0x00, 0x3f, 0xe0, 0xaa}, - {0x00, 0x42, 0xff, 0xaa}, - {0x00, 0x45, 0x34, 0xaa}, - {0x00, 0x55, 0xfe, 0xaa}, - {0x00, 0x59, 0xd3, 0xaa}, - {0x00, 0x5e, 0x04, 0xaa}, - {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */ - {0x00, 0x62, 0x02, 0xaa}, - {0x00, 0xa7, 0x31, 0xaa}, - {0x00, 0xa9, 0x66, 0xaa}, - {0x00, 0xb0, 0x00, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, - {0x00, 0xb3, 0x11, 0xaa}, - {0x00, 0xb6, 0x26, 0xaa}, - {0x00, 0xb7, 0x20, 0xaa}, - {0x00, 0xba, 0x04, 0xaa}, - {0x00, 0x88, 0x42, 0xaa}, - {0x00, 0x89, 0x9a, 0xaa}, - {0x00, 0x8a, 0x88, 0xaa}, - {0x00, 0x8b, 0x8e, 0xaa}, - {0x00, 0x8c, 0x3e, 0xaa}, - {0x00, 0x8d, 0x90, 0xaa}, - {0x00, 0x8e, 0x87, 0xaa}, - {0x00, 0x8f, 0x96, 0xaa}, - {0x00, 0x90, 0x3d, 0xaa}, - {0x00, 0x64, 0x00, 0xaa}, - {0x00, 0x65, 0x10, 0xaa}, - {0x00, 0x66, 0x20, 0xaa}, - {0x00, 0x67, 0x2b, 0xaa}, - {0x00, 0x68, 0x36, 0xaa}, - {0x00, 0x69, 0x49, 0xaa}, - {0x00, 0x6a, 0x5a, 0xaa}, - {0x00, 0x6b, 0x7f, 0xaa}, - {0x00, 0x6c, 0x9b, 0xaa}, - {0x00, 0x6d, 0xba, 0xaa}, - {0x00, 0x6e, 0xd4, 0xaa}, - {0x00, 0x6f, 0xea, 0xaa}, - {0x00, 0x70, 0x00, 0xaa}, - {0x00, 0x71, 0x10, 0xaa}, - {0x00, 0x72, 0x20, 0xaa}, - {0x00, 0x73, 0x2b, 0xaa}, - {0x00, 0x74, 0x36, 0xaa}, - {0x00, 0x75, 0x49, 0xaa}, - {0x00, 0x76, 0x5a, 0xaa}, - {0x00, 0x77, 0x7f, 0xaa}, - {0x00, 0x78, 0x9b, 0xaa}, - {0x00, 0x79, 0xba, 0xaa}, - {0x00, 0x7a, 0xd4, 0xaa}, - {0x00, 0x7b, 0xea, 0xaa}, - {0x00, 0x7c, 0x00, 0xaa}, - {0x00, 0x7d, 0x10, 0xaa}, - {0x00, 0x7e, 0x20, 0xaa}, - {0x00, 0x7f, 0x2b, 0xaa}, - {0x00, 0x80, 0x36, 0xaa}, - {0x00, 0x81, 0x49, 0xaa}, - {0x00, 0x82, 0x5a, 0xaa}, - {0x00, 0x83, 0x7f, 0xaa}, - {0x00, 0x84, 0x9b, 0xaa}, - {0x00, 0x85, 0xba, 0xaa}, - {0x00, 0x86, 0xd4, 0xaa}, - {0x00, 0x87, 0xea, 0xaa}, - {0x00, 0x57, 0x2a, 0xaa}, - {0x00, 0x03, 0x01, 0xaa}, - {0x00, 0x04, 0x10, 0xaa}, - {0x00, 0x05, 0x10, 0xaa}, - {0x00, 0x06, 0x10, 0xaa}, - {0x00, 0x07, 0x10, 0xaa}, - {0x00, 0x08, 0x13, 0xaa}, - {0x00, 0x0a, 0x00, 0xaa}, - {0x00, 0x0b, 0x10, 0xaa}, - {0x00, 0x0c, 0x20, 0xaa}, - {0x00, 0x0d, 0x18, 0xaa}, - {0x00, 0x22, 0x01, 0xaa}, - {0x00, 0x23, 0x60, 0xaa}, - {0x00, 0x25, 0x08, 0xaa}, - {0x00, 0x26, 0x82, 0xaa}, - {0x00, 0x2e, 0x0f, 0xaa}, - {0x00, 0x2f, 0x1e, 0xaa}, - {0x00, 0x30, 0x2d, 0xaa}, - {0x00, 0x31, 0x3c, 0xaa}, - {0x00, 0x32, 0x4b, 0xaa}, - {0x00, 0x33, 0x5a, 0xaa}, - {0x00, 0x34, 0x69, 0xaa}, - {0x00, 0x35, 0x78, 0xaa}, - {0x00, 0x36, 0x87, 0xaa}, - {0x00, 0x37, 0x96, 0xaa}, - {0x00, 0x38, 0xa5, 0xaa}, - {0x00, 0x39, 0xb4, 0xaa}, - {0x00, 0x3a, 0xc3, 0xaa}, - {0x00, 0x3b, 0xd2, 0xaa}, - {0x00, 0x3c, 0xe1, 0xaa}, - {0x00, 0x3e, 0xff, 0xaa}, - {0x00, 0x3f, 0xff, 0xaa}, - {0x00, 0x40, 0xff, 0xaa}, - {0x00, 0x41, 0xff, 0xaa}, - {0x00, 0x42, 0xff, 0xaa}, - {0x00, 0x43, 0xff, 0xaa}, - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x20, 0xc4, 0xaa}, - {0x00, 0x13, 0x03, 0xaa}, - {0x00, 0x3c, 0x50, 0xaa}, - {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */ - {0x00, 0x51, 0x5b, 0xaa}, - {0x00, 0x52, 0x91, 0xaa}, - {0x00, 0x53, 0x4c, 0xaa}, - {0x00, 0x54, 0x50, 0xaa}, - {0x00, 0x56, 0x02, 0xaa}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x03, 0xcc}, - {0xb6, 0x02, 0x20, 0xcc}, - {0xb6, 0x05, 0x02, 0xcc}, - {0xb6, 0x04, 0x58, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x21, 0xcc}, - {0xb6, 0x18, 0x03, 0xcc}, - {0xb6, 0x17, 0xa9, 0xcc}, - {0xb6, 0x16, 0x80, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x00, 0xcc}, - {0xb8, 0x06, 0x20, 0xcc}, - {0xb8, 0x07, 0x03, 0xcc}, - {0xb8, 0x08, 0x58, 0xcc}, - {0xb8, 0x09, 0x02, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0xd9, 0x0f, 0xaa}, - {0x00, 0xda, 0xaa, 0xaa}, - {0x00, 0xd9, 0x10, 0xaa}, - {0x00, 0xda, 0xaa, 0xaa}, - {0x00, 0xd9, 0x11, 0xaa}, - {0x00, 0xda, 0x00, 0xaa}, - {0x00, 0xd9, 0x12, 0xaa}, - {0x00, 0xda, 0xff, 0xaa}, - {0x00, 0xd9, 0x13, 0xaa}, - {0x00, 0xda, 0xff, 0xaa}, - {0x00, 0xe8, 0x11, 0xaa}, - {0x00, 0xe9, 0x12, 0xaa}, - {0x00, 0xea, 0x5c, 0xaa}, - {0x00, 0xeb, 0xff, 0xaa}, - {0x00, 0xd8, 0x80, 0xaa}, - {0x00, 0xe6, 0x02, 0xaa}, - {0x00, 0xd6, 0x40, 0xaa}, - {0x00, 0xe3, 0x05, 0xaa}, - {0x00, 0xe0, 0x40, 0xaa}, - {0x00, 0xde, 0x03, 0xaa}, - {0x00, 0xdf, 0x03, 0xaa}, - {0x00, 0xdb, 0x02, 0xaa}, - {0x00, 0xdc, 0x00, 0xaa}, - {0x00, 0xdd, 0x03, 0xaa}, - {0x00, 0xe1, 0x08, 0xaa}, - {0x00, 0xe2, 0x01, 0xaa}, - {0x00, 0xd6, 0x40, 0xaa}, - {0x00, 0xe4, 0x40, 0xaa}, - {0x00, 0xa8, 0x8f, 0xaa}, - {0x00, 0xb4, 0x16, 0xaa}, - {0xb0, 0x02, 0x06, 0xcc}, - {0xb0, 0x18, 0x06, 0xcc}, - {0xb0, 0x19, 0x06, 0xcc}, - {0xb3, 0x5d, 0x18, 0xcc}, - {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x00, 0xcc}, - {0x00, 0xb4, 0x0e, 0xaa}, - {0x00, 0xb5, 0x49, 0xaa}, - {0x00, 0xb6, 0x1c, 0xaa}, - {0x00, 0xb7, 0x96, 0xaa}, -/* end of usbvm326.inf - start of ms-win trace */ - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x3d, 0xcc}, -/*read b306*/ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x1a, 0x09, 0xaa}, - {0x00, 0x1b, 0x8a, 0xaa}, -/*read b827*/ - {0xb8, 0x27, 0x00, 0xcc}, - {0xb8, 0x26, 0x60, 0xcc}, - {0xb8, 0x26, 0x60, 0xcc}, -/*gamma - to do?*/ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0xae, 0x84, 0xaa}, -/*gamma again*/ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x96, 0xa0, 0xaa}, -/*matrix*/ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x91, 0x35, 0xaa}, - {0x00, 0x92, 0x22, 0xaa}, -/*gamma*/ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x95, 0x85, 0xaa}, -/*matrix*/ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x4d, 0x20, 0xaa}, - {0xb8, 0x22, 0x40, 0xcc}, - {0xb8, 0x23, 0x40, 0xcc}, - {0xb8, 0x24, 0x40, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0x00, 0x00, 0x64, 0xdd}, - {0x00, 0x03, 0x01, 0xaa}, -/*read 46*/ - {0x00, 0x46, 0x3c, 0xaa}, - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x16, 0x40, 0xaa}, - {0x00, 0x17, 0x40, 0xaa}, - {0x00, 0x18, 0x40, 0xaa}, - {0x00, 0x19, 0x41, 0xaa}, - {0x00, 0x03, 0x01, 0xaa}, - {0x00, 0x46, 0x3c, 0xaa}, - {0x00, 0x00, 0x18, 0xdd}, -/*read bfff*/ - {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0xb4, 0x1c, 0xaa}, - {0x00, 0xb5, 0x92, 0xaa}, - {0x00, 0xb6, 0x39, 0xaa}, - {0x00, 0xb7, 0x24, 0xaa}, -/*write 89 0400 1415*/ - {} -}; - -static const u8 poxxxx_init_common[][4] = { - {0xb3, 0x00, 0x04, 0xcc}, - {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x64, 0xcc}, - {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x65, 0xcc}, - {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x67, 0xcc}, - {0xb0, 0x03, 0x09, 0xcc}, - {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */ - {0xb3, 0x02, 0xb0, 0xcc}, - {0xb3, 0x03, 0x18, 0xcc}, - {0xb3, 0x04, 0x15, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x04, 0xcc}, /* sensor height = 1024 */ - {0xb3, 0x23, 0x00, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x04, 0xcc}, /* sensor width = 1280 */ - {0xb3, 0x17, 0xff, 0xcc}, - {0xb3, 0x2c, 0x03, 0xcc}, - {0xb3, 0x2d, 0x56, 0xcc}, - {0xb3, 0x2e, 0x02, 0xcc}, - {0xb3, 0x2f, 0x0a, 0xcc}, - {0xb3, 0x40, 0x00, 0xcc}, - {0xb3, 0x41, 0x34, 0xcc}, - {0xb3, 0x42, 0x01, 0xcc}, - {0xb3, 0x43, 0xe0, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0xb3, 0x4d, 0x00, 0xcc}, - {0x00, 0x0b, 0x2a, 0xaa}, - {0x00, 0x0e, 0x03, 0xaa}, - {0x00, 0x0f, 0xea, 0xaa}, - {0x00, 0x12, 0x08, 0xaa}, - {0x00, 0x1e, 0x06, 0xaa}, - {0x00, 0x21, 0x00, 0xaa}, - {0x00, 0x31, 0x1f, 0xaa}, - {0x00, 0x33, 0x38, 0xaa}, - {0x00, 0x36, 0xc0, 0xaa}, - {0x00, 0x37, 0xc8, 0xaa}, - {0x00, 0x3b, 0x36, 0xaa}, - {0x00, 0x4b, 0xfe, 0xaa}, - {0x00, 0x4d, 0x2e, 0xaa}, - {0x00, 0x51, 0x1c, 0xaa}, - {0x00, 0x52, 0x01, 0xaa}, - {0x00, 0x55, 0x0a, 0xaa}, - {0x00, 0x56, 0x0a, 0xaa}, - {0x00, 0x57, 0x07, 0xaa}, - {0x00, 0x58, 0x07, 0xaa}, - {0x00, 0x59, 0x04, 0xaa}, - {0x00, 0x70, 0x68, 0xaa}, - {0x00, 0x71, 0x04, 0xaa}, - {0x00, 0x72, 0x10, 0xaa}, - {0x00, 0x80, 0x71, 0xaa}, - {0x00, 0x81, 0x08, 0xaa}, - {0x00, 0x82, 0x00, 0xaa}, - {0x00, 0x83, 0x55, 0xaa}, - {0x00, 0x84, 0x06, 0xaa}, - {0x00, 0x85, 0x06, 0xaa}, - {0x00, 0x8b, 0x25, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, - {0x00, 0x8d, 0x86, 0xaa}, - {0x00, 0x8e, 0x82, 0xaa}, - {0x00, 0x8f, 0x2d, 0xaa}, - {0x00, 0x90, 0x8b, 0xaa}, - {0x00, 0x91, 0x81, 0xaa}, - {0x00, 0x92, 0x81, 0xaa}, - {0x00, 0x93, 0x23, 0xaa}, - {0x00, 0xa3, 0x2a, 0xaa}, - {0x00, 0xa4, 0x03, 0xaa}, - {0x00, 0xa5, 0xea, 0xaa}, - {0x00, 0xb0, 0x68, 0xaa}, - {0x00, 0xbc, 0x04, 0xaa}, - {0x00, 0xbe, 0x3b, 0xaa}, - {0x00, 0x4e, 0x40, 0xaa}, - {0x00, 0x06, 0x04, 0xaa}, - {0x00, 0x07, 0x03, 0xaa}, - {0x00, 0xcd, 0x18, 0xaa}, - {0x00, 0x28, 0x03, 0xaa}, - {0x00, 0x29, 0xef, 0xaa}, -/* reinit on alt 2 (qvga) or alt7 (vga) */ - {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x00, 0xcc}, - {0xb8, 0x00, 0x01, 0xcc}, - - {0x00, 0x1d, 0x85, 0xaa}, - {0x00, 0x1e, 0xc6, 0xaa}, - {0x00, 0x00, 0x40, 0xdd}, - {0x00, 0x1d, 0x05, 0xaa}, - {} -}; -static const u8 poxxxx_gamma[][4] = { - {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */ - {0x00, 0x73, 0x00, 0xaa}, - {0x00, 0x74, 0x0a, 0xaa}, - {0x00, 0x75, 0x16, 0xaa}, - {0x00, 0x76, 0x25, 0xaa}, - {0x00, 0x77, 0x34, 0xaa}, - {0x00, 0x78, 0x49, 0xaa}, - {0x00, 0x79, 0x5a, 0xaa}, - {0x00, 0x7a, 0x7f, 0xaa}, - {0x00, 0x7b, 0x9b, 0xaa}, - {0x00, 0x7c, 0xba, 0xaa}, - {0x00, 0x7d, 0xd4, 0xaa}, - {0x00, 0x7e, 0xea, 0xaa}, - - {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */ - {0x00, 0x73, 0x00, 0xaa}, - {0x00, 0x74, 0x0a, 0xaa}, - {0x00, 0x75, 0x16, 0xaa}, - {0x00, 0x76, 0x25, 0xaa}, - {0x00, 0x77, 0x34, 0xaa}, - {0x00, 0x78, 0x49, 0xaa}, - {0x00, 0x79, 0x5a, 0xaa}, - {0x00, 0x7a, 0x7f, 0xaa}, - {0x00, 0x7b, 0x9b, 0xaa}, - {0x00, 0x7c, 0xba, 0xaa}, - {0x00, 0x7d, 0xd4, 0xaa}, - {0x00, 0x7e, 0xea, 0xaa}, - - {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */ - {0x00, 0x73, 0x00, 0xaa}, - {0x00, 0x74, 0x0a, 0xaa}, - {0x00, 0x75, 0x16, 0xaa}, - {0x00, 0x76, 0x25, 0xaa}, - {0x00, 0x77, 0x34, 0xaa}, - {0x00, 0x78, 0x49, 0xaa}, - {0x00, 0x79, 0x5a, 0xaa}, - {0x00, 0x7a, 0x7f, 0xaa}, - {0x00, 0x7b, 0x9b, 0xaa}, - {0x00, 0x7c, 0xba, 0xaa}, - {0x00, 0x7d, 0xd4, 0xaa}, - {0x00, 0x7e, 0xea, 0xaa}, - {} -}; -static const u8 poxxxx_init_start_3[][4] = { - {0x00, 0xb8, 0x28, 0xaa}, - {0x00, 0xb9, 0x1e, 0xaa}, - {0x00, 0xb6, 0x14, 0xaa}, - {0x00, 0xb7, 0x0f, 0xaa}, - {0x00, 0x5c, 0x10, 0xaa}, - {0x00, 0x5d, 0x18, 0xaa}, - {0x00, 0x5e, 0x24, 0xaa}, - {0x00, 0x5f, 0x24, 0xaa}, - {0x00, 0x86, 0x1a, 0xaa}, - {0x00, 0x60, 0x00, 0xaa}, - {0x00, 0x61, 0x1b, 0xaa}, - {0x00, 0x62, 0x30, 0xaa}, - {0x00, 0x63, 0x40, 0xaa}, - {0x00, 0x87, 0x1a, 0xaa}, - {0x00, 0x64, 0x00, 0xaa}, - {0x00, 0x65, 0x08, 0xaa}, - {0x00, 0x66, 0x10, 0xaa}, - {0x00, 0x67, 0x20, 0xaa}, - {0x00, 0x88, 0x10, 0xaa}, - {0x00, 0x68, 0x00, 0xaa}, - {0x00, 0x69, 0x08, 0xaa}, - {0x00, 0x6a, 0x0f, 0xaa}, - {0x00, 0x6b, 0x0f, 0xaa}, - {0x00, 0x89, 0x07, 0xaa}, - {0x00, 0xd5, 0x4c, 0xaa}, - {0x00, 0x0a, 0x00, 0xaa}, - {0x00, 0x0b, 0x2a, 0xaa}, - {0x00, 0x0e, 0x03, 0xaa}, - {0x00, 0x0f, 0xea, 0xaa}, - {0x00, 0xa2, 0x00, 0xaa}, - {0x00, 0xa3, 0x2a, 0xaa}, - {0x00, 0xa4, 0x03, 0xaa}, - {0x00, 0xa5, 0xea, 0xaa}, - {} -}; -static const u8 poxxxx_initVGA[][4] = { - {0x00, 0x20, 0x11, 0xaa}, - {0x00, 0x33, 0x38, 0xaa}, - {0x00, 0xbb, 0x0d, 0xaa}, - {0xb3, 0x22, 0x01, 0xcc}, /* change to 640x480 */ - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x02, 0xb0, 0xcc}, - {0xb3, 0x06, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0x00, 0x04, 0x06, 0xaa}, - {0x00, 0x05, 0x3f, 0xaa}, - {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */ - {} -}; -static const u8 poxxxx_initQVGA[][4] = { - {0x00, 0x20, 0x33, 0xaa}, - {0x00, 0x33, 0x38, 0xaa}, - {0x00, 0xbb, 0x0d, 0xaa}, - {0xb3, 0x22, 0x00, 0xcc}, /* change to 320x240 */ - {0xb3, 0x23, 0xf0, 0xcc}, - {0xb3, 0x16, 0x01, 0xcc}, - {0xb3, 0x17, 0x3f, 0xcc}, - {0xb3, 0x02, 0xb0, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x5c, 0x00, 0xcc}, - {0x00, 0x04, 0x06, 0xaa}, - {0x00, 0x05, 0x3f, 0xaa}, - {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */ - {} -}; -static const u8 poxxxx_init_end_1[][4] = { - {0x00, 0x47, 0x25, 0xaa}, - {0x00, 0x48, 0x80, 0xaa}, - {0x00, 0x49, 0x1f, 0xaa}, - {0x00, 0x4a, 0x40, 0xaa}, - {0x00, 0x44, 0x40, 0xaa}, - {0x00, 0xab, 0x4a, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, - {0x00, 0xb2, 0x04, 0xaa}, - {0x00, 0xb3, 0x08, 0xaa}, - {0x00, 0xb4, 0x0b, 0xaa}, - {0x00, 0xb5, 0x0d, 0xaa}, - {} -}; -static const u8 poxxxx_init_end_2[][4] = { - {0x00, 0x1d, 0x85, 0xaa}, - {0x00, 0x1e, 0x06, 0xaa}, - {0x00, 0x1d, 0x05, 0xaa}, - {} -}; - -struct sensor_info { - s8 sensorId; - u8 I2cAdd; - u8 IdAdd; - u16 VpId; - u8 m1; - u8 m2; - u8 op; -}; - -/* probe values */ -static const struct sensor_info vc0321_probe_data[] = { -/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ -/* 0 OV9640 */ - {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, -/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */ - {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, -/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/ - {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* 3 MI1310 */ - {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* 4 MI360 - tested in vc032x_probe_sensor */ -/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ -/* 5 7131R */ - {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, -/* 6 OV7649 */ - {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, -/* 7 PAS302BCW */ - {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, -/* 8 OV7660 */ - {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, -/* 9 PO3130NC - (tested in vc032x_probe_sensor) */ -/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */ -/* 10 PO1030KC */ - {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* 11 MI1310_SOC */ - {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, -/* 12 OV9650 */ - {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, -/* 13 S5K532 */ - {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, -/* 14 MI360_SOC - ??? */ -/* 15 PO1200N */ - {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, -/* 16 PO3030K */ - {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* 17 PO2030 */ - {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* ?? */ - {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, - {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01}, -}; -static const struct sensor_info vc0323_probe_data[] = { -/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ -/* 0 OV9640 */ - {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, -/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */ - {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, -/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/ - {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* 3 MI1310 */ - {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* 4 MI360 - tested in vc032x_probe_sensor */ -/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ -/* 5 7131R */ - {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, -/* 6 OV7649 */ - {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, -/* 7 PAS302BCW */ - {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, -/* 8 OV7660 */ - {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, -/* 9 PO3130NC - (tested in vc032x_probe_sensor) */ -/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */ -/* 10 PO1030KC */ - {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* 11 MI1310_SOC */ - {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, -/* 12 OV9650 */ - {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, -/* 13 S5K532 */ - {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, -/* 14 MI360_SOC - ??? */ -/* 15 PO1200N */ - {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, -/* 16 ?? */ - {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01}, -/* 17 PO2030 */ - {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* ?? */ - {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, - {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01}, -/*fixme: not in the ms-win probe - may be found before? */ - {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, -}; - -/* read 'len' bytes in gspca_dev->usb_buf */ -static void reg_r_i(struct gspca_dev *gspca_dev, - u16 req, - u16 index, - u16 len) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 1, /* value */ - index, gspca_dev->usb_buf, len, - 500); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - } -} -static void reg_r(struct gspca_dev *gspca_dev, - u16 req, - u16 index, - u16 len) -{ - reg_r_i(gspca_dev, req, index, len); -#ifdef GSPCA_DEBUG - if (gspca_dev->usb_err < 0) - return; - if (len == 1) - PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index, - gspca_dev->usb_buf[0]); - else - PDEBUG(D_USBI, "GET %02x 0001 %04x %*ph", - req, index, 3, gspca_dev->usb_buf); -#endif -} - -static void reg_w_i(struct gspca_dev *gspca_dev, - u16 req, - u16 value, - u16 index) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, - 500); - if (ret < 0) { - pr_err("reg_w err %d\n", ret); - gspca_dev->usb_err = ret; - } -} -static void reg_w(struct gspca_dev *gspca_dev, - u16 req, - u16 value, - u16 index) -{ -#ifdef GSPCA_DEBUG - if (gspca_dev->usb_err < 0) - return; - PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); -#endif - reg_w_i(gspca_dev, req, value, index); -} - -static u16 read_sensor_register(struct gspca_dev *gspca_dev, - u16 address) -{ - u8 ldata, mdata, hdata; - int retry = 50; - - reg_r(gspca_dev, 0xa1, 0xb33f, 1); - if (!(gspca_dev->usb_buf[0] & 0x02)) { - pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]); - return 0; - } - reg_w(gspca_dev, 0xa0, address, 0xb33a); - reg_w(gspca_dev, 0xa0, 0x02, 0xb339); - - do { - reg_r(gspca_dev, 0xa1, 0xb33b, 1); - if (gspca_dev->usb_buf[0] == 0x00) - break; - msleep(40); - } while (--retry >= 0); - - reg_r(gspca_dev, 0xa1, 0xb33e, 1); - ldata = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0xa1, 0xb33d, 1); - mdata = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0xa1, 0xb33c, 1); - hdata = gspca_dev->usb_buf[0]; - if (hdata != 0 && mdata != 0 && ldata != 0) - PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x", - hdata, mdata, ldata); - reg_r(gspca_dev, 0xa1, 0xb334, 1); - if (gspca_dev->usb_buf[0] == 0x02) - return (hdata << 8) + mdata; - return hdata; -} - -static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, n; - u16 value; - const struct sensor_info *ptsensor_info; - -/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/ - if (sd->flags & FL_SAMSUNG) { - reg_w(gspca_dev, 0xa0, 0x01, 0xb301); - reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); - /* select the back sensor */ - } - - reg_r(gspca_dev, 0xa1, 0xbfcf, 1); - PDEBUG(D_PROBE, "vc032%d check sensor header %02x", - sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]); - if (sd->bridge == BRIDGE_VC0321) { - ptsensor_info = vc0321_probe_data; - n = ARRAY_SIZE(vc0321_probe_data); - } else { - ptsensor_info = vc0323_probe_data; - n = ARRAY_SIZE(vc0323_probe_data); - } - for (i = 0; i < n; i++) { - reg_w(gspca_dev, 0xa0, 0x02, 0xb334); - reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300); - reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300); - reg_w(gspca_dev, 0xa0, 0x01, 0xb308); - reg_w(gspca_dev, 0xa0, 0x0c, 0xb309); - reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); - reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301); - value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd); - if (value == 0 && ptsensor_info->IdAdd == 0x82) - value = read_sensor_register(gspca_dev, 0x83); - if (value != 0) { - PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)", - value, i); - if (value == ptsensor_info->VpId) - return ptsensor_info->sensorId; - - switch (value) { - case 0x3130: - return SENSOR_PO3130NC; - case 0x7673: - return SENSOR_OV7670; - case 0x8243: - return SENSOR_MI0360; - } - } - ptsensor_info++; - } - return -1; -} - -static void i2c_write(struct gspca_dev *gspca_dev, - u8 reg, const u8 *val, - u8 size) /* 1 or 2 */ -{ - int retry; - -#ifdef GSPCA_DEBUG - if (gspca_dev->usb_err < 0) - return; - if (size == 1) - PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val); - else - PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]); -#endif - reg_r_i(gspca_dev, 0xa1, 0xb33f, 1); -/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/ - reg_w_i(gspca_dev, 0xa0, size, 0xb334); - reg_w_i(gspca_dev, 0xa0, reg, 0xb33a); - reg_w_i(gspca_dev, 0xa0, val[0], 0xb336); - if (size > 1) - reg_w_i(gspca_dev, 0xa0, val[1], 0xb337); - reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339); - retry = 4; - do { - reg_r_i(gspca_dev, 0xa1, 0xb33b, 1); - if (gspca_dev->usb_buf[0] == 0) - break; - msleep(20); - } while (--retry > 0); - if (retry <= 0) - pr_err("i2c_write timeout\n"); -} - -static void put_tab_to_reg(struct gspca_dev *gspca_dev, - const u8 *tab, u8 tabsize, u16 addr) -{ - int j; - u16 ad = addr; - - for (j = 0; j < tabsize; j++) - reg_w(gspca_dev, 0xa0, tab[j], ad++); -} - -static void usb_exchange(struct gspca_dev *gspca_dev, - const u8 data[][4]) -{ - int i = 0; - - for (;;) { - switch (data[i][3]) { - default: - return; - case 0xcc: /* normal write */ - reg_w(gspca_dev, 0xa0, data[i][2], - (data[i][0]) << 8 | data[i][1]); - break; - case 0xaa: /* i2c op */ - i2c_write(gspca_dev, data[i][1], &data[i][2], 1); - break; - case 0xbb: /* i2c op */ - i2c_write(gspca_dev, data[i][0], &data[i][1], 2); - break; - case 0xdd: - msleep(data[i][1] * 256 + data[i][2] + 10); - break; - } - i++; - } - /*not reached*/ -} - - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->bridge = id->driver_info >> 8; - sd->flags = id->driver_info & 0xff; - - if (id->idVendor == 0x046d && - (id->idProduct == 0x0892 || id->idProduct == 0x0896)) - sd->sensor = SENSOR_POxxxx; /* no probe */ - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - int sensor; - /* number of packets per ISOC message */ - static u8 npkt[NSENSORS] = { - [SENSOR_HV7131R] = 64, - [SENSOR_MI0360] = 32, - [SENSOR_MI1310_SOC] = 32, - [SENSOR_MI1320] = 64, - [SENSOR_MI1320_SOC] = 128, - [SENSOR_OV7660] = 32, - [SENSOR_OV7670] = 64, - [SENSOR_PO1200] = 128, - [SENSOR_PO3130NC] = 128, - [SENSOR_POxxxx] = 128, - }; - - if (sd->sensor != SENSOR_POxxxx) - sensor = vc032x_probe_sensor(gspca_dev); - else - sensor = sd->sensor; - - switch (sensor) { - case -1: - pr_err("Unknown sensor...\n"); - return -EINVAL; - case SENSOR_HV7131R: - PDEBUG(D_PROBE, "Find Sensor HV7131R"); - break; - case SENSOR_MI0360: - PDEBUG(D_PROBE, "Find Sensor MI0360"); - sd->bridge = BRIDGE_VC0323; - break; - case SENSOR_MI1310_SOC: - PDEBUG(D_PROBE, "Find Sensor MI1310_SOC"); - break; - case SENSOR_MI1320: - PDEBUG(D_PROBE, "Find Sensor MI1320"); - break; - case SENSOR_MI1320_SOC: - PDEBUG(D_PROBE, "Find Sensor MI1320_SOC"); - break; - case SENSOR_OV7660: - PDEBUG(D_PROBE, "Find Sensor OV7660"); - break; - case SENSOR_OV7670: - PDEBUG(D_PROBE, "Find Sensor OV7670"); - break; - case SENSOR_PO1200: - PDEBUG(D_PROBE, "Find Sensor PO1200"); - break; - case SENSOR_PO3130NC: - PDEBUG(D_PROBE, "Find Sensor PO3130NC"); - break; - case SENSOR_POxxxx: - PDEBUG(D_PROBE, "Sensor POxxxx"); - break; - } - sd->sensor = sensor; - - cam = &gspca_dev->cam; - if (sd->bridge == BRIDGE_VC0321) { - cam->cam_mode = vc0321_mode; - cam->nmodes = ARRAY_SIZE(vc0321_mode); - } else { - switch (sensor) { - case SENSOR_PO1200: - cam->cam_mode = svga_mode; - cam->nmodes = ARRAY_SIZE(svga_mode); - break; - case SENSOR_MI1310_SOC: - cam->cam_mode = vc0323_mode; - cam->nmodes = ARRAY_SIZE(vc0323_mode); - break; - case SENSOR_MI1320_SOC: - cam->cam_mode = bi_mode; - cam->nmodes = ARRAY_SIZE(bi_mode); - break; - case SENSOR_OV7670: - cam->cam_mode = bi_mode; - cam->nmodes = ARRAY_SIZE(bi_mode) - 1; - break; - default: - cam->cam_mode = vc0323_mode; - cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; - break; - } - } - cam->npkt = npkt[sd->sensor]; - - if (sd->sensor == SENSOR_OV7670) - sd->flags |= FL_HFLIP | FL_VFLIP; - - if (sd->bridge == BRIDGE_VC0321) { - reg_r(gspca_dev, 0x8a, 0, 3); - reg_w(gspca_dev, 0x87, 0x00, 0x0f0f); - reg_r(gspca_dev, 0x8b, 0, 3); - reg_w(gspca_dev, 0x88, 0x00, 0x0202); - if (sd->sensor == SENSOR_POxxxx) { - reg_r(gspca_dev, 0xa1, 0xb300, 1); - if (gspca_dev->usb_buf[0] != 0) { - reg_w(gspca_dev, 0xa0, 0x26, 0xb300); - reg_w(gspca_dev, 0xa0, 0x04, 0xb300); - } - reg_w(gspca_dev, 0xa0, 0x00, 0xb300); - } - } - return gspca_dev->usb_err; -} - -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) -{ - u8 data; - - data = val; - if (data >= 0x80) - data &= 0x7f; - else - data = 0xff ^ data; - i2c_write(gspca_dev, 0x98, &data, 1); -} - -static void setcontrast(struct gspca_dev *gspca_dev, u8 val) -{ - i2c_write(gspca_dev, 0x99, &val, 1); -} - -static void setcolors(struct gspca_dev *gspca_dev, u8 val) -{ - u8 data; - - data = val - (val >> 3) - 1; - i2c_write(gspca_dev, 0x94, &data, 1); - i2c_write(gspca_dev, 0x95, &val, 1); -} - -static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 data[2]; - - if (sd->flags & FL_HFLIP) - hflip = !hflip; - if (sd->flags & FL_VFLIP) - vflip = !vflip; - switch (sd->sensor) { - case SENSOR_MI1310_SOC: - case SENSOR_MI1320: - case SENSOR_MI1320_SOC: - data[0] = data[1] = 0; /* select page 0 */ - i2c_write(gspca_dev, 0xf0, data, 2); - data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01; - data[1] = 0x02 * hflip - | 0x01 * vflip; - i2c_write(gspca_dev, 0x20, data, 2); - break; - case SENSOR_OV7660: - case SENSOR_OV7670: - data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07; - data[0] |= OV7660_MVFP_MIRROR * hflip - | OV7660_MVFP_VFLIP * vflip; - i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1); - break; - case SENSOR_PO1200: - data[0] = 0; - i2c_write(gspca_dev, 0x03, data, 1); - data[0] = 0x80 * hflip - | 0x40 * vflip - | 0x06; - i2c_write(gspca_dev, 0x1e, data, 1); - break; - } -} - -static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - static const u8 (*ov7660_freq_tb[3])[4] = - {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; - - if (sd->sensor != SENSOR_OV7660) - return; - usb_exchange(gspca_dev, ov7660_freq_tb[val]); -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 data; - - switch (sd->sensor) { - case SENSOR_PO1200: - data = 0; - i2c_write(gspca_dev, 0x03, &data, 1); - if (val < 0) - data = 0x6a; - else - data = 0xb5 + val * 3; - i2c_write(gspca_dev, 0x61, &data, 1); - break; - case SENSOR_POxxxx: - if (val < 0) - data = 0x7e; /* def = max */ - else - data = 0x60 + val * 0x0f; - i2c_write(gspca_dev, 0x59, &data, 1); - break; - } -} -static void setgain(struct gspca_dev *gspca_dev, u8 val) -{ - i2c_write(gspca_dev, 0x15, &val, 1); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - u8 data; - - data = val >> 8; - i2c_write(gspca_dev, 0x1a, &data, 1); - data = val; - i2c_write(gspca_dev, 0x1b, &data, 1); -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 val) -{ - static const u8 data[2] = {0x28, 0x3c}; - - i2c_write(gspca_dev, 0xd1, &data[val], 1); -} - -static void setgamma(struct gspca_dev *gspca_dev) -{ -/*fixme:to do */ - usb_exchange(gspca_dev, poxxxx_gamma); -} - -static void setbacklight(struct gspca_dev *gspca_dev, s32 val) -{ - u16 v; - u8 data; - - data = (val << 4) | 0x0f; - i2c_write(gspca_dev, 0xaa, &data, 1); - v = 613 + 12 * val; - data = v >> 8; - i2c_write(gspca_dev, 0xc4, &data, 1); - data = v; - i2c_write(gspca_dev, 0xc5, &data, 1); - v = 1093 - 12 * val; - data = v >> 8; - i2c_write(gspca_dev, 0xc6, &data, 1); - data = v; - i2c_write(gspca_dev, 0xc7, &data, 1); - v = 342 + 9 * val; - data = v >> 8; - i2c_write(gspca_dev, 0xc8, &data, 1); - data = v; - i2c_write(gspca_dev, 0xc9, &data, 1); - v = 702 - 9 * val; - data = v >> 8; - i2c_write(gspca_dev, 0xca, &data, 1); - data = v; - i2c_write(gspca_dev, 0xcb, &data, 1); -} - -static void setwb(struct gspca_dev *gspca_dev) -{ -/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/ - static const u8 data[2] = {0x00, 0x00}; - - i2c_write(gspca_dev, 0x16, &data[0], 1); - i2c_write(gspca_dev, 0x18, &data[1], 1); -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - const u8 (*init)[4]; - const u8 *GammaT = NULL; - const u8 *MatrixT = NULL; - int mode; - static const u8 (*mi1320_soc_init[])[4] = { - mi1320_soc_InitSXGA, - mi1320_soc_InitVGA, - mi1320_soc_InitQVGA, - }; - -/*fixme: back sensor only*/ - if (sd->flags & FL_SAMSUNG) { - reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); - reg_w(gspca_dev, 0xa9, 0x8348, 0x000e); - reg_w(gspca_dev, 0xa9, 0x0000, 0x001a); - } - - /* Assume start use the good resolution from gspca_dev->mode */ - if (sd->bridge == BRIDGE_VC0321) { - reg_w(gspca_dev, 0xa0, 0xff, 0xbfec); - reg_w(gspca_dev, 0xa0, 0xff, 0xbfed); - reg_w(gspca_dev, 0xa0, 0xff, 0xbfee); - reg_w(gspca_dev, 0xa0, 0xff, 0xbfef); - sd->image_offset = 46; - } else { - if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat - == V4L2_PIX_FMT_JPEG) - sd->image_offset = 0; - else - sd->image_offset = 32; - } - - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - switch (sd->sensor) { - case SENSOR_HV7131R: - GammaT = hv7131r_gamma; - MatrixT = hv7131r_matrix; - if (mode) - init = hv7131r_initQVGA_data; /* 320x240 */ - else - init = hv7131r_initVGA_data; /* 640x480 */ - break; - case SENSOR_OV7660: - GammaT = ov7660_gamma; - MatrixT = ov7660_matrix; - if (mode) - init = ov7660_initQVGA_data; /* 320x240 */ - else - init = ov7660_initVGA_data; /* 640x480 */ - break; - case SENSOR_MI0360: - GammaT = mi1320_gamma; - MatrixT = mi0360_matrix; - if (mode) - init = mi0360_initQVGA_JPG; /* 320x240 */ - else - init = mi0360_initVGA_JPG; /* 640x480 */ - break; - case SENSOR_MI1310_SOC: - GammaT = mi1320_gamma; - MatrixT = mi1320_matrix; - switch (mode) { - case 1: - init = mi1310_socinitQVGA_JPG; /* 320x240 */ - break; - case 0: - init = mi1310_socinitVGA_JPG; /* 640x480 */ - break; - default: - init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */ - break; - } - break; - case SENSOR_MI1320: - GammaT = mi1320_gamma; - MatrixT = mi1320_matrix; - if (mode) - init = mi1320_initQVGA_data; /* 320x240 */ - else - init = mi1320_initVGA_data; /* 640x480 */ - break; - case SENSOR_MI1320_SOC: - GammaT = mi1320_gamma; - MatrixT = mi1320_matrix; - init = mi1320_soc_init[mode]; - break; - case SENSOR_OV7670: - init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA; - break; - case SENSOR_PO3130NC: - GammaT = po3130_gamma; - MatrixT = po3130_matrix; - if (mode) - init = po3130_initQVGA_data; /* 320x240 */ - else - init = po3130_initVGA_data; /* 640x480 */ - usb_exchange(gspca_dev, init); - init = po3130_rundata; - break; - case SENSOR_PO1200: - GammaT = po1200_gamma; - MatrixT = po1200_matrix; - init = po1200_initVGA_data; - break; - default: -/* case SENSOR_POxxxx: */ - usb_exchange(gspca_dev, poxxxx_init_common); - setgamma(gspca_dev); - usb_exchange(gspca_dev, poxxxx_init_start_3); - if (mode) - init = poxxxx_initQVGA; - else - init = poxxxx_initVGA; - usb_exchange(gspca_dev, init); - reg_r(gspca_dev, 0x8c, 0x0000, 3); - reg_w(gspca_dev, 0xa0, - gspca_dev->usb_buf[2] & 1 ? 0 : 1, - 0xb35c); - msleep(300); -/*fixme: i2c read 04 and 05*/ - init = poxxxx_init_end_1; - break; - } - usb_exchange(gspca_dev, init); - if (GammaT && MatrixT) { - put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a); - put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b); - put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c); - put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); - - switch (sd->sensor) { - case SENSOR_PO1200: - case SENSOR_HV7131R: - reg_w(gspca_dev, 0x89, 0x0400, 0x1415); - break; - case SENSOR_MI1310_SOC: - reg_w(gspca_dev, 0x89, 0x058c, 0x0000); - break; - } - msleep(100); - } - switch (sd->sensor) { - case SENSOR_OV7670: - reg_w(gspca_dev, 0x87, 0xffff, 0xffff); - reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1); - reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff); - break; - case SENSOR_POxxxx: - usb_exchange(gspca_dev, poxxxx_init_end_2); - setwb(gspca_dev); - msleep(80); /* led on */ - reg_w(gspca_dev, 0x89, 0xffff, 0xfdff); - break; - } - return gspca_dev->usb_err; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->sensor) { - case SENSOR_MI1310_SOC: - reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); - break; - case SENSOR_POxxxx: - return; - default: - if (!(sd->flags & FL_SAMSUNG)) - reg_w(gspca_dev, 0x89, 0xffff, 0xffff); - break; - } - reg_w(gspca_dev, 0xa0, 0x01, 0xb301); - reg_w(gspca_dev, 0xa0, 0x09, 0xb003); -} - -/* called on streamoff with alt 0 and on disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!gspca_dev->present) - return; -/*fixme: is this useful?*/ - if (sd->sensor == SENSOR_MI1310_SOC) - reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); - else if (!(sd->flags & FL_SAMSUNG)) - reg_w(gspca_dev, 0x89, 0xffff, 0xffff); - - if (sd->sensor == SENSOR_POxxxx) { - reg_w(gspca_dev, 0xa0, 0x26, 0xb300); - reg_w(gspca_dev, 0xa0, 0x04, 0xb300); - reg_w(gspca_dev, 0xa0, 0x00, 0xb300); - } -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso pkt length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (data[0] == 0xff && data[1] == 0xd8) { - PDEBUG(D_PACK, - "vc032x header packet found len %d", len); - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - data += sd->image_offset; - len -= sd->image_offset; - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - return; - } - - /* The vc0321 sends some additional data after sending the complete - * frame, we ignore this. */ - if (sd->bridge == BRIDGE_VC0321) { - int size, l; - - l = gspca_dev->image_len; - size = gspca_dev->frsz; - if (len > size - l) - len = size - l; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - setcontrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_HFLIP: - sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - setautogain(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - setgain(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val); - break; - case V4L2_CID_BACKLIGHT_COMPENSATION: - setbacklight(gspca_dev, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setlightfreq(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - bool has_brightness = false; - bool has_contrast = false; - bool has_sat = false; - bool has_hvflip = false; - bool has_freq = false; - bool has_backlight = false; - bool has_exposure = false; - bool has_autogain = false; - bool has_gain = false; - bool has_sharpness = false; - - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MI0360: - case SENSOR_PO3130NC: - break; - case SENSOR_MI1310_SOC: - case SENSOR_MI1320: - case SENSOR_MI1320_SOC: - case SENSOR_OV7660: - has_hvflip = true; - break; - case SENSOR_OV7670: - has_hvflip = has_freq = true; - break; - case SENSOR_PO1200: - has_hvflip = has_sharpness = true; - break; - case SENSOR_POxxxx: - has_brightness = has_contrast = has_sat = has_backlight = - has_exposure = has_autogain = has_gain = - has_sharpness = true; - break; - } - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 8); - if (has_brightness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - if (has_contrast) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 127); - if (has_sat) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 1, 127, 1, 63); - if (has_hvflip) { - sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - } - if (has_sharpness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, -1, 2, 1, -1); - if (has_freq) - v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, - V4L2_CID_POWER_LINE_FREQUENCY_50HZ); - if (has_autogain) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - if (has_gain) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 78, 1, 0); - if (has_exposure) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 4095, 1, 450); - if (has_backlight) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - if (sd->hflip) - v4l2_ctrl_cluster(2, &sd->hflip); - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .init_controls = sd_init_controls, - .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -}; - -/* -- module initialisation -- */ -#define BF(bridge, flags) \ - .driver_info = (BRIDGE_ ## bridge << 8) \ - | (flags) -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)}, - {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)}, - {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)}, - {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)}, - {USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)}, - {USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)}, - {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)}, - {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)}, - {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)}, - {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)}, - {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)}, - {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)}, - {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)}, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c deleted file mode 100644 index b1a64b912666..000000000000 --- a/drivers/media/video/gspca/vicam.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * gspca ViCam subdriver - * - * Copyright (C) 2011 Hans de Goede - * - * Based on the usbvideo vicam driver, which is: - * - * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), - * Christopher L Cheney (ccheney@cheney.cx), - * Pavel Machek (pavel@ucw.cz), - * John Tyner (jtyner@cs.ucr.edu), - * Monroe Williams (monroe@pobox.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "vicam" -#define HEADER_SIZE 64 - -#include -#include -#include -#include -#include "gspca.h" - -#define VICAM_FIRMWARE "vicam/firmware.fw" - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(VICAM_FIRMWARE); - -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct work_struct work_struct; - struct workqueue_struct *work_thread; -}; - -/* The vicam sensor has a resolution of 512 x 244, with I believe square - pixels, but this is forced to a 4:3 ratio by optics. So it has - non square pixels :( */ -static struct v4l2_pix_format vicam_mode[] = { - { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 256, - .sizeimage = 256 * 122, - .colorspace = V4L2_COLORSPACE_SRGB,}, - /* 2 modes with somewhat more square pixels */ - { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 256, - .sizeimage = 256 * 200, - .colorspace = V4L2_COLORSPACE_SRGB,}, - { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 256, - .sizeimage = 256 * 240, - .colorspace = V4L2_COLORSPACE_SRGB,}, -#if 0 /* This mode has extremely non square pixels, testing use only */ - { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 512, - .sizeimage = 512 * 122, - .colorspace = V4L2_COLORSPACE_SRGB,}, -#endif - { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 512, - .sizeimage = 512 * 244, - .colorspace = V4L2_COLORSPACE_SRGB,}, -}; - -static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, - u16 value, u16 index, u8 *data, u16 len) -{ - int ret; - - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, data, len, 1000); - if (ret < 0) - pr_err("control msg req %02X error %d\n", request, ret); - - return ret; -} - -static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) -{ - int ret; - - ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0); - if (ret < 0) - return ret; - - if (state) - ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0); - - return ret; -} - -/* - * request and read a block of data - see warning on vicam_command. - */ -static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) -{ - int ret, unscaled_height, act_len = 0; - u8 *req_data = gspca_dev->usb_buf; - s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); - - memset(req_data, 0, 16); - req_data[0] = gain; - if (gspca_dev->width == 256) - req_data[1] |= 0x01; /* low nibble x-scale */ - if (gspca_dev->height <= 122) { - req_data[1] |= 0x10; /* high nibble y-scale */ - unscaled_height = gspca_dev->height * 2; - } else - unscaled_height = gspca_dev->height; - req_data[2] = 0x90; /* unknown, does not seem to do anything */ - if (unscaled_height <= 200) - req_data[3] = 0x06; /* vend? */ - else if (unscaled_height <= 242) /* Yes 242 not 240 */ - req_data[3] = 0x07; /* vend? */ - else /* Up to 244 lines with req_data[3] == 0x08 */ - req_data[3] = 0x08; /* vend? */ - - if (expo < 256) { - /* Frame rate maxed out, use partial frame expo time */ - req_data[4] = 255 - expo; - req_data[5] = 0x00; - req_data[6] = 0x00; - req_data[7] = 0x01; - } else { - /* Modify frame rate */ - req_data[4] = 0x00; - req_data[5] = 0x00; - req_data[6] = expo & 0xFF; - req_data[7] = expo >> 8; - } - req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */ - /* bytes 9-15 do not seem to affect exposure or image quality */ - - mutex_lock(&gspca_dev->usb_lock); - ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16); - mutex_unlock(&gspca_dev->usb_lock); - if (ret < 0) - return ret; - - ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, 0x81), - data, size, &act_len, 10000); - /* successful, it returns 0, otherwise negative */ - if (ret < 0 || act_len != size) { - pr_err("bulk read fail (%d) len %d/%d\n", - ret, act_len, size); - return -EIO; - } - return 0; -} - -/* This function is called as a workqueue function and runs whenever the camera - * is streaming data. Because it is a workqueue function it is allowed to sleep - * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface we take the gspca - * usb_lock when performing USB operations. In practice the only thing we need - * to protect against is the usb_set_interface call that gspca makes during - * stream_off as the camera doesn't provide any controls that the user could try - * to change. - */ -static void vicam_dostream(struct work_struct *work) -{ - struct sd *sd = container_of(work, struct sd, work_struct); - struct gspca_dev *gspca_dev = &sd->gspca_dev; - int ret, frame_sz; - u8 *buffer; - - frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + - HEADER_SIZE; - buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA); - if (!buffer) { - pr_err("Couldn't allocate USB buffer\n"); - goto exit; - } - - while (gspca_dev->dev && gspca_dev->streaming) { -#ifdef CONFIG_PM - if (gspca_dev->frozen) - break; -#endif - ret = vicam_read_frame(gspca_dev, buffer, frame_sz); - if (ret < 0) - break; - - /* Note the frame header contents seem to be completely - constant, they do not change with either image, or - settings. So we simply discard it. The frames have - a very similar 64 byte footer, which we don't even - bother reading from the cam */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - buffer + HEADER_SIZE, - frame_sz - HEADER_SIZE); - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - } -exit: - kfree(buffer); -} - -/* This function is called at probe time just before sd_init */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct cam *cam = &gspca_dev->cam; - struct sd *sd = (struct sd *)gspca_dev; - - /* We don't use the buffer gspca allocates so make it small. */ - cam->bulk = 1; - cam->bulk_size = 64; - cam->cam_mode = vicam_mode; - cam->nmodes = ARRAY_SIZE(vicam_mode); - - INIT_WORK(&sd->work_struct, vicam_dostream); - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - int ret; - const struct ihex_binrec *rec; - const struct firmware *uninitialized_var(fw); - u8 *firmware_buf; - - ret = request_ihex_firmware(&fw, VICAM_FIRMWARE, - &gspca_dev->dev->dev); - if (ret) { - pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); - return ret; - } - - firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!firmware_buf) { - ret = -ENOMEM; - goto exit; - } - for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { - memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len)); - ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf, - be16_to_cpu(rec->len)); - if (ret < 0) - break; - } - - kfree(firmware_buf); -exit: - release_firmware(fw); - return ret; -} - -/* Set up for getting frames. */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - int ret; - - ret = vicam_set_camera_power(gspca_dev, 1); - if (ret < 0) - return ret; - - /* Start the workqueue function to do the streaming */ - sd->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(sd->work_thread, &sd->work_struct); - - return 0; -} - -/* called on streamoff with alt==0 and on disconnect */ -/* the usb_lock is held at entry - restore on exit */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *dev = (struct sd *)gspca_dev; - - /* wait for the work queue to terminate */ - mutex_unlock(&gspca_dev->usb_lock); - /* This waits for vicam_dostream to finish */ - destroy_workqueue(dev->work_thread); - dev->work_thread = NULL; - mutex_lock(&gspca_dev->usb_lock); - - if (gspca_dev->dev) - vicam_set_camera_power(gspca_dev, 0); -} - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 2); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_EXPOSURE, 0, 2047, 1, 256); - gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_GAIN, 0, 255, 1, 200); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* Table of supported USB devices */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x04c1, 0x009d)}, - {USB_DEVICE(0x0602, 0x1001)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stop0 = sd_stop0, -}; - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, - &sd_desc, - sizeof(struct sd), - THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c deleted file mode 100644 index 9e3a909e0a00..000000000000 --- a/drivers/media/video/gspca/w996Xcf.c +++ /dev/null @@ -1,567 +0,0 @@ -/** - * - * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip. - * - * Copyright (C) 2009 Hans de Goede - * - * This module is adapted from the in kernel v4l1 w9968cf driver: - * - * Copyright (C) 2002-2004 by Luca Risolia - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* Note this is not a stand alone driver, it gets included in ov519.c, this - is a bit of a hack, but it needs the driver code for a lot of different - ov sensors which is already present in ov519.c (the old v4l1 driver used - the ovchipcam framework). When we have the time we really should move - the sensor drivers to v4l2 sub drivers, and properly split of this - driver from ov519.c */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ - -#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET]) -#define UV_QUANTABLE (&sd->jpeg_hdr[JPEG_QT1_OFFSET]) - -static const struct v4l2_pix_format w9968cf_vga_mode[] = { - {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, - .colorspace = V4L2_COLORSPACE_JPEG}, - {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, - .colorspace = V4L2_COLORSPACE_JPEG}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, - .colorspace = V4L2_COLORSPACE_JPEG}, - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, - .colorspace = V4L2_COLORSPACE_JPEG}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_JPEG}, -}; - -static void reg_w(struct sd *sd, u16 index, u16 value); - -/*-------------------------------------------------------------------------- - Write 64-bit data to the fast serial bus registers. - Return 0 on success, -1 otherwise. - --------------------------------------------------------------------------*/ -static void w9968cf_write_fsb(struct sd *sd, u16* data) -{ - struct usb_device *udev = sd->gspca_dev.dev; - u16 value; - int ret; - - if (sd->gspca_dev.usb_err < 0) - return; - - value = *data++; - memcpy(sd->gspca_dev.usb_buf, data, 6); - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - value, 0x06, sd->gspca_dev.usb_buf, 6, 500); - if (ret < 0) { - pr_err("Write FSB registers failed (%d)\n", ret); - sd->gspca_dev.usb_err = ret; - } -} - -/*-------------------------------------------------------------------------- - Write data to the serial bus control register. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static void w9968cf_write_sb(struct sd *sd, u16 value) -{ - int ret; - - if (sd->gspca_dev.usb_err < 0) - return; - - /* We don't use reg_w here, as that would cause all writes when - bitbanging i2c to be logged, making the logs impossible to read */ - ret = usb_control_msg(sd->gspca_dev.dev, - usb_sndctrlpipe(sd->gspca_dev.dev, 0), - 0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0x01, NULL, 0, 500); - - udelay(W9968CF_I2C_BUS_DELAY); - - if (ret < 0) { - pr_err("Write SB reg [01] %04x failed\n", value); - sd->gspca_dev.usb_err = ret; - } -} - -/*-------------------------------------------------------------------------- - Read data from the serial bus control register. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_read_sb(struct sd *sd) -{ - int ret; - - if (sd->gspca_dev.usb_err < 0) - return -1; - - /* We don't use reg_r here, as the w9968cf is special and has 16 - bit registers instead of 8 bit */ - ret = usb_control_msg(sd->gspca_dev.dev, - usb_rcvctrlpipe(sd->gspca_dev.dev, 0), - 1, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0x01, sd->gspca_dev.usb_buf, 2, 500); - if (ret >= 0) { - ret = sd->gspca_dev.usb_buf[0] | - (sd->gspca_dev.usb_buf[1] << 8); - } else { - pr_err("Read SB reg [01] failed\n"); - sd->gspca_dev.usb_err = ret; - } - - udelay(W9968CF_I2C_BUS_DELAY); - - return ret; -} - -/*-------------------------------------------------------------------------- - Upload quantization tables for the JPEG compression. - This function is called by w9968cf_start_transfer(). - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static void w9968cf_upload_quantizationtables(struct sd *sd) -{ - u16 a, b; - int i, j; - - reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */ - - for (i = 0, j = 0; i < 32; i++, j += 2) { - a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j + 1]) << 8); - b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j + 1]) << 8); - reg_w(sd, 0x40 + i, a); - reg_w(sd, 0x60 + i, b); - } - reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */ -} - -/**************************************************************************** - * Low-level I2C I/O functions. * - * The adapter supports the following I2C transfer functions: * - * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * - * i2c_adap_read_byte_data() * - * i2c_adap_read_byte() * - ****************************************************************************/ - -static void w9968cf_smbus_start(struct sd *sd) -{ - w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ - w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ -} - -static void w9968cf_smbus_stop(struct sd *sd) -{ - w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ - w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ - w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ -} - -static void w9968cf_smbus_write_byte(struct sd *sd, u8 v) -{ - u8 bit; - int sda; - - for (bit = 0 ; bit < 8 ; bit++) { - sda = (v & 0x80) ? 2 : 0; - v <<= 1; - /* SDE=1, SDA=sda, SCL=0 */ - w9968cf_write_sb(sd, 0x10 | sda); - /* SDE=1, SDA=sda, SCL=1 */ - w9968cf_write_sb(sd, 0x11 | sda); - /* SDE=1, SDA=sda, SCL=0 */ - w9968cf_write_sb(sd, 0x10 | sda); - } -} - -static void w9968cf_smbus_read_byte(struct sd *sd, u8 *v) -{ - u8 bit; - - /* No need to ensure SDA is high as we are always called after - read_ack which ends with SDA high */ - *v = 0; - for (bit = 0 ; bit < 8 ; bit++) { - *v <<= 1; - /* SDE=1, SDA=1, SCL=1 */ - w9968cf_write_sb(sd, 0x0013); - *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0; - /* SDE=1, SDA=1, SCL=0 */ - w9968cf_write_sb(sd, 0x0012); - } -} - -static void w9968cf_smbus_write_nack(struct sd *sd) -{ - /* No need to ensure SDA is high as we are always called after - read_byte which ends with SDA high */ - w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ - w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ -} - -static void w9968cf_smbus_read_ack(struct sd *sd) -{ - int sda; - - /* Ensure SDA is high before raising clock to avoid a spurious stop */ - w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ - w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ - sda = w9968cf_read_sb(sd); - w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ - if (sda >= 0 && (sda & 0x08)) { - PDEBUG(D_USBI, "Did not receive i2c ACK"); - sd->gspca_dev.usb_err = -EIO; - } -} - -/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ -static void w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value) -{ - u16* data = (u16 *)sd->gspca_dev.usb_buf; - - data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0); - data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0; - data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0); - data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0; - data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0; - data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0); - data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0; - data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0; - data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0); - data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0; - - w9968cf_write_fsb(sd, data); - - data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0); - data[0] |= (reg & 0x40) ? 0x0540 : 0x0; - data[0] |= (reg & 0x20) ? 0x5000 : 0x0; - data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0); - data[1] |= (reg & 0x10) ? 0x0054 : 0x0; - data[1] |= (reg & 0x08) ? 0x1500 : 0x0; - data[1] |= (reg & 0x04) ? 0x4000 : 0x0; - data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0); - data[2] |= (reg & 0x02) ? 0x0150 : 0x0; - data[2] |= (reg & 0x01) ? 0x5400 : 0x0; - data[3] = 0x001d; - - w9968cf_write_fsb(sd, data); - - data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); - data[0] |= (value & 0x40) ? 0x0540 : 0x0; - data[0] |= (value & 0x20) ? 0x5000 : 0x0; - data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); - data[1] |= (value & 0x10) ? 0x0054 : 0x0; - data[1] |= (value & 0x08) ? 0x1500 : 0x0; - data[1] |= (value & 0x04) ? 0x4000 : 0x0; - data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); - data[2] |= (value & 0x02) ? 0x0150 : 0x0; - data[2] |= (value & 0x01) ? 0x5400 : 0x0; - data[3] = 0xfe1d; - - w9968cf_write_fsb(sd, data); - - PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); -} - -/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ -static int w9968cf_i2c_r(struct sd *sd, u8 reg) -{ - int ret = 0; - u8 value; - - /* Fast serial bus data control disable */ - w9968cf_write_sb(sd, 0x0013); /* don't change ! */ - - w9968cf_smbus_start(sd); - w9968cf_smbus_write_byte(sd, sd->sensor_addr); - w9968cf_smbus_read_ack(sd); - w9968cf_smbus_write_byte(sd, reg); - w9968cf_smbus_read_ack(sd); - w9968cf_smbus_stop(sd); - w9968cf_smbus_start(sd); - w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1); - w9968cf_smbus_read_ack(sd); - w9968cf_smbus_read_byte(sd, &value); - /* signal we don't want to read anymore, the v4l1 driver used to - send an ack here which is very wrong! (and then fixed - the issues this gave by retrying reads) */ - w9968cf_smbus_write_nack(sd); - w9968cf_smbus_stop(sd); - - /* Fast serial bus data control re-enable */ - w9968cf_write_sb(sd, 0x0030); - - if (sd->gspca_dev.usb_err >= 0) { - ret = value; - PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); - } else - PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); - - return ret; -} - -/*-------------------------------------------------------------------------- - Turn on the LED on some webcams. A beep should be heard too. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static void w9968cf_configure(struct sd *sd) -{ - reg_w(sd, 0x00, 0xff00); /* power-down */ - reg_w(sd, 0x00, 0xbf17); /* reset everything */ - reg_w(sd, 0x00, 0xbf10); /* normal operation */ - reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */ - reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */ - reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */ - reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */ - - sd->stopped = 1; -} - -static void w9968cf_init(struct sd *sd) -{ - unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2), - y0 = 0x0000, - u0 = y0 + hw_bufsize / 2, - v0 = u0 + hw_bufsize / 4, - y1 = v0 + hw_bufsize / 4, - u1 = y1 + hw_bufsize / 2, - v1 = u1 + hw_bufsize / 4; - - reg_w(sd, 0x00, 0xff00); /* power off */ - reg_w(sd, 0x00, 0xbf10); /* power on */ - - reg_w(sd, 0x03, 0x405d); /* DRAM timings */ - reg_w(sd, 0x04, 0x0030); /* SDRAM timings */ - - reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */ - reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */ - reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */ - reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */ - reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */ - reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */ - - reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */ - reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */ - reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */ - reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */ - reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */ - reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */ - - reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */ - reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */ - - reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */ - reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */ - - reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */ - reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/ - reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */ - reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */ -} - -static void w9968cf_set_crop_window(struct sd *sd) -{ - int start_cropx, start_cropy, x, y, fw, fh, cw, ch, - max_width, max_height; - - if (sd->sif) { - max_width = 352; - max_height = 288; - } else { - max_width = 640; - max_height = 480; - } - - if (sd->sensor == SEN_OV7620) { - /* - * Sigh, this is dependend on the clock / framerate changes - * made by the frequency control, sick. - * - * Note we cannot use v4l2_ctrl_g_ctrl here, as we get called - * from ov519.c:setfreq() with the ctrl lock held! - */ - if (sd->freq->val == 1) { - start_cropx = 277; - start_cropy = 37; - } else { - start_cropx = 105; - start_cropy = 37; - } - } else { - start_cropx = 320; - start_cropy = 35; - } - - /* Work around to avoid FP arithmetics */ - #define SC(x) ((x) << 10) - - /* Scaling factors */ - fw = SC(sd->gspca_dev.width) / max_width; - fh = SC(sd->gspca_dev.height) / max_height; - - cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width) / fh; - ch = (fw >= fh) ? SC(sd->gspca_dev.height) / fw : max_height; - - sd->sensor_width = max_width; - sd->sensor_height = max_height; - - x = (max_width - cw) / 2; - y = (max_height - ch) / 2; - - reg_w(sd, 0x10, start_cropx + x); - reg_w(sd, 0x11, start_cropy + y); - reg_w(sd, 0x12, start_cropx + x + cw); - reg_w(sd, 0x13, start_cropy + y + ch); -} - -static void w9968cf_mode_init_regs(struct sd *sd) -{ - int val, vs_polarity, hs_polarity; - - w9968cf_set_crop_window(sd); - - reg_w(sd, 0x14, sd->gspca_dev.width); - reg_w(sd, 0x15, sd->gspca_dev.height); - - /* JPEG width & height */ - reg_w(sd, 0x30, sd->gspca_dev.width); - reg_w(sd, 0x31, sd->gspca_dev.height); - - /* Y & UV frame buffer strides (in WORD) */ - if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == - V4L2_PIX_FMT_JPEG) { - reg_w(sd, 0x2c, sd->gspca_dev.width / 2); - reg_w(sd, 0x2d, sd->gspca_dev.width / 4); - } else - reg_w(sd, 0x2c, sd->gspca_dev.width); - - reg_w(sd, 0x00, 0xbf17); /* reset everything */ - reg_w(sd, 0x00, 0xbf10); /* normal operation */ - - /* Transfer size in WORDS (for UYVY format only) */ - val = sd->gspca_dev.width * sd->gspca_dev.height; - reg_w(sd, 0x3d, val & 0xffff); /* low bits */ - reg_w(sd, 0x3e, val >> 16); /* high bits */ - - if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == - V4L2_PIX_FMT_JPEG) { - /* We may get called multiple times (usb isoc bw negotiat.) */ - jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, - sd->gspca_dev.width, 0x22); /* JPEG 420 */ - jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); - w9968cf_upload_quantizationtables(sd); - v4l2_ctrl_grab(sd->jpegqual, true); - } - - /* Video Capture Control Register */ - if (sd->sensor == SEN_OV7620) { - /* Seems to work around a bug in the image sensor */ - vs_polarity = 1; - hs_polarity = 1; - } else { - vs_polarity = 1; - hs_polarity = 0; - } - - val = (vs_polarity << 12) | (hs_polarity << 11); - - /* NOTE: We may not have enough memory to do double buffering while - doing compression (amount of memory differs per model cam). - So we use the second image buffer also as jpeg stream buffer - (see w9968cf_init), and disable double buffering. */ - if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == - V4L2_PIX_FMT_JPEG) { - /* val |= 0x0002; YUV422P */ - val |= 0x0003; /* YUV420P */ - } else - val |= 0x0080; /* Enable HW double buffering */ - - /* val |= 0x0020; enable clamping */ - /* val |= 0x0008; enable (1-2-1) filter */ - /* val |= 0x000c; enable (2-3-6-3-2) filter */ - - val |= 0x8000; /* capt. enable */ - - reg_w(sd, 0x16, val); - - sd->gspca_dev.empty_packet = 0; -} - -static void w9968cf_stop0(struct sd *sd) -{ - v4l2_ctrl_grab(sd->jpegqual, false); - reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ - reg_w(sd, 0x16, 0x0000); /* stop video capture */ -} - -/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF - for the next frame). This seems to simply not be true when operating - in JPEG mode, in this case there may be empty packets within the - frame. So in JPEG mode use the JPEG SOI marker to detect SOF. - - Note to make things even more interesting the w9968cf sends *PLANAR* jpeg, - to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS, - V-data, EOI. */ -static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* isoc packet */ - int len) /* iso packet length */ -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat == - V4L2_PIX_FMT_JPEG) { - if (len >= 2 && - data[0] == 0xff && - data[1] == 0xd8) { - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - /* Strip the ff d8, our own header (which adds - huffman and quantization tables) already has this */ - len -= 2; - data += 2; - } - } else { - /* In UYVY mode an empty packet signals EOF */ - if (gspca_dev->empty_packet) { - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, - NULL, 0); - gspca_dev->empty_packet = 0; - } - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c deleted file mode 100644 index 13b8d395d210..000000000000 --- a/drivers/media/video/gspca/xirlink_cit.c +++ /dev/null @@ -1,3145 +0,0 @@ -/* - * USB IBM C-It Video Camera driver - * - * Supports Xirlink C-It Video Camera, IBM PC Camera, - * IBM NetCamera and Veo Stingray. - * - * Copyright (C) 2010 Hans de Goede - * - * This driver is based on earlier work of: - * - * (C) Copyright 1999 Johannes Erdfelt - * (C) Copyright 1999 Randy Dunlap - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define MODULE_NAME "xirlink-cit" - -#include -#include "gspca.h" - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("Xirlink C-IT"); -MODULE_LICENSE("GPL"); - -/* FIXME we should autodetect this */ -static int ibm_netcam_pro; -module_param(ibm_netcam_pro, int, 0); -MODULE_PARM_DESC(ibm_netcam_pro, - "Use IBM Netcamera Pro init sequences for Model 3 cams"); - -/* FIXME this should be handled through the V4L2 input selection API */ -static int rca_input; -module_param(rca_input, int, 0644); -MODULE_PARM_DESC(rca_input, - "Use rca input instead of ccd sensor on Model 3 cams"); - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl *lighting; - u8 model; -#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */ -#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */ -#define CIT_MODEL2 2 /* ibmcam driver */ -#define CIT_MODEL3 3 -#define CIT_MODEL4 4 -#define CIT_IBM_NETCAM_PRO 5 - u8 input_index; - u8 button_state; - u8 stop_on_control_change; - u8 sof_read; - u8 sof_len; -}; - -static void sd_stop0(struct gspca_dev *gspca_dev); - -static const struct v4l2_pix_format cif_yuv_mode[] = { - {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {352, 288, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, -}; - -static const struct v4l2_pix_format vga_yuv_mode[] = { - {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {640, 480, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, -}; - -static const struct v4l2_pix_format model0_mode[] = { - {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, -}; - -static const struct v4l2_pix_format model2_mode[] = { - {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 2 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, - {352, 288, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 + 4, - .colorspace = V4L2_COLORSPACE_SRGB}, -}; - -/* - * 01.01.08 - Added for RCA video in support -LO - * This struct is used to init the Model3 cam to use the RCA video in port - * instead of the CCD sensor. - */ -static const u16 rca_initdata[][3] = { - {0, 0x0000, 0x010c}, - {0, 0x0006, 0x012c}, - {0, 0x0078, 0x012d}, - {0, 0x0046, 0x012f}, - {0, 0xd141, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfea8, 0x0124}, - {1, 0x0000, 0x0116}, - {0, 0x0064, 0x0116}, - {1, 0x0000, 0x0115}, - {0, 0x0003, 0x0115}, - {0, 0x0008, 0x0123}, - {0, 0x0000, 0x0117}, - {0, 0x0000, 0x0112}, - {0, 0x0080, 0x0100}, - {0, 0x0000, 0x0100}, - {1, 0x0000, 0x0116}, - {0, 0x0060, 0x0116}, - {0, 0x0002, 0x0112}, - {0, 0x0000, 0x0123}, - {0, 0x0001, 0x0117}, - {0, 0x0040, 0x0108}, - {0, 0x0019, 0x012c}, - {0, 0x0040, 0x0116}, - {0, 0x000a, 0x0115}, - {0, 0x000b, 0x0115}, - {0, 0x0078, 0x012d}, - {0, 0x0046, 0x012f}, - {0, 0xd141, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfea8, 0x0124}, - {0, 0x0064, 0x0116}, - {0, 0x0000, 0x0115}, - {0, 0x0001, 0x0115}, - {0, 0xffff, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x00aa, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xffff, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x00f2, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x000f, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xffff, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x00f8, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x00fc, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xffff, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x00f9, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x003c, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xffff, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0027, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0019, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0021, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0006, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0045, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x002a, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x000e, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x002b, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x00f4, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x002c, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0004, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x002d, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0014, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x002e, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0003, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x002f, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0003, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0014, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0040, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0040, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0053, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0x0000, 0x0101}, - {0, 0x00a0, 0x0103}, - {0, 0x0078, 0x0105}, - {0, 0x0000, 0x010a}, - {0, 0x0024, 0x010b}, - {0, 0x0028, 0x0119}, - {0, 0x0088, 0x011b}, - {0, 0x0002, 0x011d}, - {0, 0x0003, 0x011e}, - {0, 0x0000, 0x0129}, - {0, 0x00fc, 0x012b}, - {0, 0x0008, 0x0102}, - {0, 0x0000, 0x0104}, - {0, 0x0008, 0x011a}, - {0, 0x0028, 0x011c}, - {0, 0x0021, 0x012a}, - {0, 0x0000, 0x0118}, - {0, 0x0000, 0x0132}, - {0, 0x0000, 0x0109}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0031, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0040, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0040, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x00dc, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0032, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0020, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0001, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0040, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0040, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0037, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0030, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0xfff9, 0x0124}, - {0, 0x0086, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0038, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0008, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0x0000, 0x0127}, - {0, 0xfff8, 0x0124}, - {0, 0xfffd, 0x0124}, - {0, 0xfffa, 0x0124}, - {0, 0x0003, 0x0111}, -}; - -/* TESTME the old ibmcam driver repeats certain commands to Model1 cameras, we - do the same for now (testing needed to see if this is really necessary) */ -static const int cit_model1_ntries = 5; -static const int cit_model1_ntries2 = 2; - -static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index) -{ - struct usb_device *udev = gspca_dev->dev; - int err; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, - value, index, NULL, 0, 1000); - if (err < 0) - pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", - index, value, err); - - return 0; -} - -static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose) -{ - struct usb_device *udev = gspca_dev->dev; - __u8 *buf = gspca_dev->usb_buf; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, - 0x00, index, buf, 8, 1000); - if (res < 0) { - pr_err("Failed to read a register (index 0x%04X, error %d)\n", - index, res); - return res; - } - - if (verbose) - PDEBUG(D_PROBE, "Register %04x value: %02x", index, buf[0]); - - return 0; -} - -/* - * cit_send_FF_04_02() - * - * This procedure sends magic 3-command prefix to the camera. - * The purpose of this prefix is not known. - * - * History: - * 1/2/00 Created. - */ -static void cit_send_FF_04_02(struct gspca_dev *gspca_dev) -{ - cit_write_reg(gspca_dev, 0x00FF, 0x0127); - cit_write_reg(gspca_dev, 0x0004, 0x0124); - cit_write_reg(gspca_dev, 0x0002, 0x0124); -} - -static void cit_send_00_04_06(struct gspca_dev *gspca_dev) -{ - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0x0004, 0x0124); - cit_write_reg(gspca_dev, 0x0006, 0x0124); -} - -static void cit_send_x_00(struct gspca_dev *gspca_dev, unsigned short x) -{ - cit_write_reg(gspca_dev, x, 0x0127); - cit_write_reg(gspca_dev, 0x0000, 0x0124); -} - -static void cit_send_x_00_05(struct gspca_dev *gspca_dev, unsigned short x) -{ - cit_send_x_00(gspca_dev, x); - cit_write_reg(gspca_dev, 0x0005, 0x0124); -} - -static void cit_send_x_00_05_02(struct gspca_dev *gspca_dev, unsigned short x) -{ - cit_write_reg(gspca_dev, x, 0x0127); - cit_write_reg(gspca_dev, 0x0000, 0x0124); - cit_write_reg(gspca_dev, 0x0005, 0x0124); - cit_write_reg(gspca_dev, 0x0002, 0x0124); -} - -static void cit_send_x_01_00_05(struct gspca_dev *gspca_dev, u16 x) -{ - cit_write_reg(gspca_dev, x, 0x0127); - cit_write_reg(gspca_dev, 0x0001, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0124); - cit_write_reg(gspca_dev, 0x0005, 0x0124); -} - -static void cit_send_x_00_05_02_01(struct gspca_dev *gspca_dev, u16 x) -{ - cit_write_reg(gspca_dev, x, 0x0127); - cit_write_reg(gspca_dev, 0x0000, 0x0124); - cit_write_reg(gspca_dev, 0x0005, 0x0124); - cit_write_reg(gspca_dev, 0x0002, 0x0124); - cit_write_reg(gspca_dev, 0x0001, 0x0124); -} - -static void cit_send_x_00_05_02_08_01(struct gspca_dev *gspca_dev, u16 x) -{ - cit_write_reg(gspca_dev, x, 0x0127); - cit_write_reg(gspca_dev, 0x0000, 0x0124); - cit_write_reg(gspca_dev, 0x0005, 0x0124); - cit_write_reg(gspca_dev, 0x0002, 0x0124); - cit_write_reg(gspca_dev, 0x0008, 0x0124); - cit_write_reg(gspca_dev, 0x0001, 0x0124); -} - -static void cit_Packet_Format1(struct gspca_dev *gspca_dev, u16 fkey, u16 val) -{ - cit_send_x_01_00_05(gspca_dev, 0x0088); - cit_send_x_00_05(gspca_dev, fkey); - cit_send_x_00_05_02_08_01(gspca_dev, val); - cit_send_x_00_05(gspca_dev, 0x0088); - cit_send_x_00_05_02_01(gspca_dev, fkey); - cit_send_x_00_05(gspca_dev, 0x0089); - cit_send_x_00(gspca_dev, fkey); - cit_send_00_04_06(gspca_dev); - cit_read_reg(gspca_dev, 0x0126, 0); - cit_send_FF_04_02(gspca_dev); -} - -static void cit_PacketFormat2(struct gspca_dev *gspca_dev, u16 fkey, u16 val) -{ - cit_send_x_01_00_05(gspca_dev, 0x0088); - cit_send_x_00_05(gspca_dev, fkey); - cit_send_x_00_05_02(gspca_dev, val); -} - -static void cit_model2_Packet2(struct gspca_dev *gspca_dev) -{ - cit_write_reg(gspca_dev, 0x00ff, 0x012d); - cit_write_reg(gspca_dev, 0xfea3, 0x0124); -} - -static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) -{ - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x00ff, 0x012e); - cit_write_reg(gspca_dev, v1, 0x012f); - cit_write_reg(gspca_dev, 0x00ff, 0x0130); - cit_write_reg(gspca_dev, 0xc719, 0x0124); - cit_write_reg(gspca_dev, v2, 0x0127); - - cit_model2_Packet2(gspca_dev); -} - -/* - * cit_model3_Packet1() - * - * 00_0078_012d - * 00_0097_012f - * 00_d141_0124 - * 00_0096_0127 - * 00_fea8_0124 -*/ -static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) -{ - cit_write_reg(gspca_dev, 0x0078, 0x012d); - cit_write_reg(gspca_dev, v1, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, v2, 0x0127); - cit_write_reg(gspca_dev, 0xfea8, 0x0124); -} - -static void cit_model4_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) -{ - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, v1, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, v2, 0x0127); - cit_write_reg(gspca_dev, 0xfea8, 0x0124); -} - -static void cit_model4_BrightnessPacket(struct gspca_dev *gspca_dev, u16 val) -{ - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0026, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, val, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0038, 0x012d); - cit_write_reg(gspca_dev, 0x0004, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - sd->model = id->driver_info; - if (sd->model == CIT_MODEL3 && ibm_netcam_pro) - sd->model = CIT_IBM_NETCAM_PRO; - - cam = &gspca_dev->cam; - switch (sd->model) { - case CIT_MODEL0: - cam->cam_mode = model0_mode; - cam->nmodes = ARRAY_SIZE(model0_mode); - sd->sof_len = 4; - break; - case CIT_MODEL1: - cam->cam_mode = cif_yuv_mode; - cam->nmodes = ARRAY_SIZE(cif_yuv_mode); - sd->sof_len = 4; - break; - case CIT_MODEL2: - cam->cam_mode = model2_mode + 1; /* no 160x120 */ - cam->nmodes = 3; - break; - case CIT_MODEL3: - cam->cam_mode = vga_yuv_mode; - cam->nmodes = ARRAY_SIZE(vga_yuv_mode); - sd->stop_on_control_change = 1; - sd->sof_len = 4; - break; - case CIT_MODEL4: - cam->cam_mode = model2_mode; - cam->nmodes = ARRAY_SIZE(model2_mode); - break; - case CIT_IBM_NETCAM_PRO: - cam->cam_mode = vga_yuv_mode; - cam->nmodes = 2; /* no 640 x 480 */ - cam->input_flags = V4L2_IN_ST_VFLIP; - sd->stop_on_control_change = 1; - sd->sof_len = 4; - break; - } - - return 0; -} - -static int cit_init_model0(struct gspca_dev *gspca_dev) -{ - cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */ - cit_write_reg(gspca_dev, 0x0001, 0x0112); /* turn on autogain ? */ - cit_write_reg(gspca_dev, 0x0000, 0x0400); - cit_write_reg(gspca_dev, 0x0001, 0x0400); - cit_write_reg(gspca_dev, 0x0000, 0x0420); - cit_write_reg(gspca_dev, 0x0001, 0x0420); - cit_write_reg(gspca_dev, 0x000d, 0x0409); - cit_write_reg(gspca_dev, 0x0002, 0x040a); - cit_write_reg(gspca_dev, 0x0018, 0x0405); - cit_write_reg(gspca_dev, 0x0008, 0x0435); - cit_write_reg(gspca_dev, 0x0026, 0x040b); - cit_write_reg(gspca_dev, 0x0007, 0x0437); - cit_write_reg(gspca_dev, 0x0015, 0x042f); - cit_write_reg(gspca_dev, 0x002b, 0x0439); - cit_write_reg(gspca_dev, 0x0026, 0x043a); - cit_write_reg(gspca_dev, 0x0008, 0x0438); - cit_write_reg(gspca_dev, 0x001e, 0x042b); - cit_write_reg(gspca_dev, 0x0041, 0x042c); - - return 0; -} - -static int cit_init_ibm_netcam_pro(struct gspca_dev *gspca_dev) -{ - cit_read_reg(gspca_dev, 0x128, 1); - cit_write_reg(gspca_dev, 0x0003, 0x0133); - cit_write_reg(gspca_dev, 0x0000, 0x0117); - cit_write_reg(gspca_dev, 0x0008, 0x0123); - cit_write_reg(gspca_dev, 0x0000, 0x0100); - cit_read_reg(gspca_dev, 0x0116, 0); - cit_write_reg(gspca_dev, 0x0060, 0x0116); - cit_write_reg(gspca_dev, 0x0002, 0x0112); - cit_write_reg(gspca_dev, 0x0000, 0x0133); - cit_write_reg(gspca_dev, 0x0000, 0x0123); - cit_write_reg(gspca_dev, 0x0001, 0x0117); - cit_write_reg(gspca_dev, 0x0040, 0x0108); - cit_write_reg(gspca_dev, 0x0019, 0x012c); - cit_write_reg(gspca_dev, 0x0060, 0x0116); - cit_write_reg(gspca_dev, 0x0002, 0x0115); - cit_write_reg(gspca_dev, 0x000b, 0x0115); - - cit_write_reg(gspca_dev, 0x0078, 0x012d); - cit_write_reg(gspca_dev, 0x0001, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0079, 0x012d); - cit_write_reg(gspca_dev, 0x00ff, 0x0130); - cit_write_reg(gspca_dev, 0xcd41, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_read_reg(gspca_dev, 0x0126, 1); - - cit_model3_Packet1(gspca_dev, 0x0000, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0000, 0x0001); - cit_model3_Packet1(gspca_dev, 0x000b, 0x0000); - cit_model3_Packet1(gspca_dev, 0x000c, 0x0008); - cit_model3_Packet1(gspca_dev, 0x000d, 0x003a); - cit_model3_Packet1(gspca_dev, 0x000e, 0x0060); - cit_model3_Packet1(gspca_dev, 0x000f, 0x0060); - cit_model3_Packet1(gspca_dev, 0x0010, 0x0008); - cit_model3_Packet1(gspca_dev, 0x0011, 0x0004); - cit_model3_Packet1(gspca_dev, 0x0012, 0x0028); - cit_model3_Packet1(gspca_dev, 0x0013, 0x0002); - cit_model3_Packet1(gspca_dev, 0x0014, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0015, 0x00fb); - cit_model3_Packet1(gspca_dev, 0x0016, 0x0002); - cit_model3_Packet1(gspca_dev, 0x0017, 0x0037); - cit_model3_Packet1(gspca_dev, 0x0018, 0x0036); - cit_model3_Packet1(gspca_dev, 0x001e, 0x0000); - cit_model3_Packet1(gspca_dev, 0x001f, 0x0008); - cit_model3_Packet1(gspca_dev, 0x0020, 0x00c1); - cit_model3_Packet1(gspca_dev, 0x0021, 0x0034); - cit_model3_Packet1(gspca_dev, 0x0022, 0x0034); - cit_model3_Packet1(gspca_dev, 0x0025, 0x0002); - cit_model3_Packet1(gspca_dev, 0x0028, 0x0022); - cit_model3_Packet1(gspca_dev, 0x0029, 0x000a); - cit_model3_Packet1(gspca_dev, 0x002b, 0x0000); - cit_model3_Packet1(gspca_dev, 0x002c, 0x0000); - cit_model3_Packet1(gspca_dev, 0x002d, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x002e, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x002f, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x0030, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x0031, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x0032, 0x0007); - cit_model3_Packet1(gspca_dev, 0x0033, 0x0005); - cit_model3_Packet1(gspca_dev, 0x0037, 0x0040); - cit_model3_Packet1(gspca_dev, 0x0039, 0x0000); - cit_model3_Packet1(gspca_dev, 0x003a, 0x0000); - cit_model3_Packet1(gspca_dev, 0x003b, 0x0001); - cit_model3_Packet1(gspca_dev, 0x003c, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0040, 0x000c); - cit_model3_Packet1(gspca_dev, 0x0041, 0x00fb); - cit_model3_Packet1(gspca_dev, 0x0042, 0x0002); - cit_model3_Packet1(gspca_dev, 0x0043, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0045, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0046, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0047, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0048, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0049, 0x0000); - cit_model3_Packet1(gspca_dev, 0x004a, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x004b, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x004c, 0x00ff); - cit_model3_Packet1(gspca_dev, 0x004f, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0050, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0051, 0x0002); - cit_model3_Packet1(gspca_dev, 0x0055, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0056, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0057, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0058, 0x0002); - cit_model3_Packet1(gspca_dev, 0x0059, 0x0000); - cit_model3_Packet1(gspca_dev, 0x005c, 0x0016); - cit_model3_Packet1(gspca_dev, 0x005d, 0x0022); - cit_model3_Packet1(gspca_dev, 0x005e, 0x003c); - cit_model3_Packet1(gspca_dev, 0x005f, 0x0050); - cit_model3_Packet1(gspca_dev, 0x0060, 0x0044); - cit_model3_Packet1(gspca_dev, 0x0061, 0x0005); - cit_model3_Packet1(gspca_dev, 0x006a, 0x007e); - cit_model3_Packet1(gspca_dev, 0x006f, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0072, 0x001b); - cit_model3_Packet1(gspca_dev, 0x0073, 0x0005); - cit_model3_Packet1(gspca_dev, 0x0074, 0x000a); - cit_model3_Packet1(gspca_dev, 0x0075, 0x001b); - cit_model3_Packet1(gspca_dev, 0x0076, 0x002a); - cit_model3_Packet1(gspca_dev, 0x0077, 0x003c); - cit_model3_Packet1(gspca_dev, 0x0078, 0x0050); - cit_model3_Packet1(gspca_dev, 0x007b, 0x0000); - cit_model3_Packet1(gspca_dev, 0x007c, 0x0011); - cit_model3_Packet1(gspca_dev, 0x007d, 0x0024); - cit_model3_Packet1(gspca_dev, 0x007e, 0x0043); - cit_model3_Packet1(gspca_dev, 0x007f, 0x005a); - cit_model3_Packet1(gspca_dev, 0x0084, 0x0020); - cit_model3_Packet1(gspca_dev, 0x0085, 0x0033); - cit_model3_Packet1(gspca_dev, 0x0086, 0x000a); - cit_model3_Packet1(gspca_dev, 0x0087, 0x0030); - cit_model3_Packet1(gspca_dev, 0x0088, 0x0070); - cit_model3_Packet1(gspca_dev, 0x008b, 0x0008); - cit_model3_Packet1(gspca_dev, 0x008f, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0090, 0x0006); - cit_model3_Packet1(gspca_dev, 0x0091, 0x0028); - cit_model3_Packet1(gspca_dev, 0x0092, 0x005a); - cit_model3_Packet1(gspca_dev, 0x0093, 0x0082); - cit_model3_Packet1(gspca_dev, 0x0096, 0x0014); - cit_model3_Packet1(gspca_dev, 0x0097, 0x0020); - cit_model3_Packet1(gspca_dev, 0x0098, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00b0, 0x0046); - cit_model3_Packet1(gspca_dev, 0x00b1, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00b2, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00b3, 0x0004); - cit_model3_Packet1(gspca_dev, 0x00b4, 0x0007); - cit_model3_Packet1(gspca_dev, 0x00b6, 0x0002); - cit_model3_Packet1(gspca_dev, 0x00b7, 0x0004); - cit_model3_Packet1(gspca_dev, 0x00bb, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00bc, 0x0001); - cit_model3_Packet1(gspca_dev, 0x00bd, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00bf, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00c0, 0x00c8); - cit_model3_Packet1(gspca_dev, 0x00c1, 0x0014); - cit_model3_Packet1(gspca_dev, 0x00c2, 0x0001); - cit_model3_Packet1(gspca_dev, 0x00c3, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00c4, 0x0004); - cit_model3_Packet1(gspca_dev, 0x00cb, 0x00bf); - cit_model3_Packet1(gspca_dev, 0x00cc, 0x00bf); - cit_model3_Packet1(gspca_dev, 0x00cd, 0x00bf); - cit_model3_Packet1(gspca_dev, 0x00ce, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00cf, 0x0020); - cit_model3_Packet1(gspca_dev, 0x00d0, 0x0040); - cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf); - cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf); - cit_model3_Packet1(gspca_dev, 0x00d2, 0x00bf); - cit_model3_Packet1(gspca_dev, 0x00d3, 0x00bf); - cit_model3_Packet1(gspca_dev, 0x00ea, 0x0008); - cit_model3_Packet1(gspca_dev, 0x00eb, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00ec, 0x00e8); - cit_model3_Packet1(gspca_dev, 0x00ed, 0x0001); - cit_model3_Packet1(gspca_dev, 0x00ef, 0x0022); - cit_model3_Packet1(gspca_dev, 0x00f0, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00f2, 0x0028); - cit_model3_Packet1(gspca_dev, 0x00f4, 0x0002); - cit_model3_Packet1(gspca_dev, 0x00f5, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00fa, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00fb, 0x0001); - cit_model3_Packet1(gspca_dev, 0x00fc, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00fd, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00fe, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00ff, 0x0000); - - cit_model3_Packet1(gspca_dev, 0x00be, 0x0003); - cit_model3_Packet1(gspca_dev, 0x00c8, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00c9, 0x0020); - cit_model3_Packet1(gspca_dev, 0x00ca, 0x0040); - cit_model3_Packet1(gspca_dev, 0x0053, 0x0001); - cit_model3_Packet1(gspca_dev, 0x0082, 0x000e); - cit_model3_Packet1(gspca_dev, 0x0083, 0x0020); - cit_model3_Packet1(gspca_dev, 0x0034, 0x003c); - cit_model3_Packet1(gspca_dev, 0x006e, 0x0055); - cit_model3_Packet1(gspca_dev, 0x0062, 0x0005); - cit_model3_Packet1(gspca_dev, 0x0063, 0x0008); - cit_model3_Packet1(gspca_dev, 0x0066, 0x000a); - cit_model3_Packet1(gspca_dev, 0x0067, 0x0006); - cit_model3_Packet1(gspca_dev, 0x006b, 0x0010); - cit_model3_Packet1(gspca_dev, 0x005a, 0x0001); - cit_model3_Packet1(gspca_dev, 0x005b, 0x000a); - cit_model3_Packet1(gspca_dev, 0x0023, 0x0006); - cit_model3_Packet1(gspca_dev, 0x0026, 0x0004); - cit_model3_Packet1(gspca_dev, 0x0036, 0x0069); - cit_model3_Packet1(gspca_dev, 0x0038, 0x0064); - cit_model3_Packet1(gspca_dev, 0x003d, 0x0003); - cit_model3_Packet1(gspca_dev, 0x003e, 0x0001); - cit_model3_Packet1(gspca_dev, 0x00b8, 0x0014); - cit_model3_Packet1(gspca_dev, 0x00b9, 0x0014); - cit_model3_Packet1(gspca_dev, 0x00e6, 0x0004); - cit_model3_Packet1(gspca_dev, 0x00e8, 0x0001); - - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->model) { - case CIT_MODEL0: - cit_init_model0(gspca_dev); - sd_stop0(gspca_dev); - break; - case CIT_MODEL1: - case CIT_MODEL2: - case CIT_MODEL3: - case CIT_MODEL4: - break; /* All is done in sd_start */ - case CIT_IBM_NETCAM_PRO: - cit_init_ibm_netcam_pro(gspca_dev); - sd_stop0(gspca_dev); - break; - } - return 0; -} - -static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - - switch (sd->model) { - case CIT_MODEL0: - case CIT_IBM_NETCAM_PRO: - /* No (known) brightness control for these */ - break; - case CIT_MODEL1: - /* Model 1: Brightness range 0 - 63 */ - cit_Packet_Format1(gspca_dev, 0x0031, val); - cit_Packet_Format1(gspca_dev, 0x0032, val); - cit_Packet_Format1(gspca_dev, 0x0033, val); - break; - case CIT_MODEL2: - /* Model 2: Brightness range 0x60 - 0xee */ - /* Scale 0 - 63 to 0x60 - 0xee */ - i = 0x60 + val * 2254 / 1000; - cit_model2_Packet1(gspca_dev, 0x001a, i); - break; - case CIT_MODEL3: - /* Model 3: Brightness range 'i' in [0x0C..0x3F] */ - i = val; - if (i < 0x0c) - i = 0x0c; - cit_model3_Packet1(gspca_dev, 0x0036, i); - break; - case CIT_MODEL4: - /* Model 4: Brightness range 'i' in [0x04..0xb4] */ - /* Scale 0 - 63 to 0x04 - 0xb4 */ - i = 0x04 + val * 2794 / 1000; - cit_model4_BrightnessPacket(gspca_dev, i); - break; - } - - return 0; -} - -static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->model) { - case CIT_MODEL0: { - int i; - /* gain 0-15, 0-20 -> 0-15 */ - i = val * 1000 / 1333; - cit_write_reg(gspca_dev, i, 0x0422); - /* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */ - i = val * 2000 / 1333; - cit_write_reg(gspca_dev, i, 0x0423); - /* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63 */ - i = val * 4000 / 1333; - cit_write_reg(gspca_dev, i, 0x0424); - /* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */ - i = val * 8000 / 1333; - cit_write_reg(gspca_dev, i, 0x0425); - break; - } - case CIT_MODEL2: - case CIT_MODEL4: - /* These models do not have this control. */ - break; - case CIT_MODEL1: - { - /* Scale 0 - 20 to 15 - 0 */ - int i, new_contrast = (20 - val) * 1000 / 1333; - for (i = 0; i < cit_model1_ntries; i++) { - cit_Packet_Format1(gspca_dev, 0x0014, new_contrast); - cit_send_FF_04_02(gspca_dev); - } - break; - } - case CIT_MODEL3: - { /* Preset hardware values */ - static const struct { - unsigned short cv1; - unsigned short cv2; - unsigned short cv3; - } cv[7] = { - { 0x05, 0x05, 0x0f }, /* Minimum */ - { 0x04, 0x04, 0x16 }, - { 0x02, 0x03, 0x16 }, - { 0x02, 0x08, 0x16 }, - { 0x01, 0x0c, 0x16 }, - { 0x01, 0x0e, 0x16 }, - { 0x01, 0x10, 0x16 } /* Maximum */ - }; - int i = val / 3; - cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1); - cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2); - cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3); - break; - } - case CIT_IBM_NETCAM_PRO: - cit_model3_Packet1(gspca_dev, 0x005b, val + 1); - break; - } - return 0; -} - -static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->model) { - case CIT_MODEL0: - case CIT_MODEL1: - case CIT_IBM_NETCAM_PRO: - /* No hue control for these models */ - break; - case CIT_MODEL2: - cit_model2_Packet1(gspca_dev, 0x0024, val); - /* cit_model2_Packet1(gspca_dev, 0x0020, sat); */ - break; - case CIT_MODEL3: { - /* Model 3: Brightness range 'i' in [0x05..0x37] */ - /* TESTME according to the ibmcam driver this does not work */ - if (0) { - /* Scale 0 - 127 to 0x05 - 0x37 */ - int i = 0x05 + val * 1000 / 2540; - cit_model3_Packet1(gspca_dev, 0x007e, i); - } - break; - } - case CIT_MODEL4: - /* HDG: taken from ibmcam, setting the color gains does not - * really belong here. - * - * I am not sure r/g/b_gain variables exactly control gain - * of those channels. Most likely they subtly change some - * very internal image processing settings in the camera. - * In any case, here is what they do, and feel free to tweak: - * - * r_gain: seriously affects red gain - * g_gain: seriously affects green gain - * b_gain: seriously affects blue gain - * hue: changes average color from violet (0) to red (0xFF) - */ - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x001e, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 160, 0x0127); /* Green gain */ - cit_write_reg(gspca_dev, 160, 0x012e); /* Red gain */ - cit_write_reg(gspca_dev, 160, 0x0130); /* Blue gain */ - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, val, 0x012d); /* Hue */ - cit_write_reg(gspca_dev, 0xf545, 0x0124); - break; - } - return 0; -} - -static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->model) { - case CIT_MODEL0: - case CIT_MODEL2: - case CIT_MODEL4: - case CIT_IBM_NETCAM_PRO: - /* These models do not have this control */ - break; - case CIT_MODEL1: { - int i; - const unsigned short sa[] = { - 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; - - for (i = 0; i < cit_model1_ntries; i++) - cit_PacketFormat2(gspca_dev, 0x0013, sa[val]); - break; - } - case CIT_MODEL3: - { /* - * "Use a table of magic numbers. - * This setting doesn't really change much. - * But that's how Windows does it." - */ - static const struct { - unsigned short sv1; - unsigned short sv2; - unsigned short sv3; - unsigned short sv4; - } sv[7] = { - { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */ - { 0x01, 0x04, 0x05, 0x14 }, - { 0x02, 0x04, 0x05, 0x14 }, - { 0x03, 0x04, 0x05, 0x14 }, - { 0x03, 0x05, 0x05, 0x14 }, - { 0x03, 0x06, 0x05, 0x14 }, - { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */ - }; - cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1); - cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2); - cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3); - cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4); - break; - } - } - return 0; -} - -/* - * cit_set_lighting() - * - * Camera model 1: - * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low. - * - * Camera model 2: - * We have 16 levels of lighting, 0 for bright light and up to 15 for - * low light. But values above 5 or so are useless because camera is - * not really capable to produce anything worth viewing at such light. - * This setting may be altered only in certain camera state. - * - * Low lighting forces slower FPS. - * - * History: - * 1/5/00 Created. - * 2/20/00 Added support for Model 2 cameras. - */ -static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->model) { - case CIT_MODEL0: - case CIT_MODEL2: - case CIT_MODEL3: - case CIT_MODEL4: - case CIT_IBM_NETCAM_PRO: - break; - case CIT_MODEL1: { - int i; - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x0027, val); - break; - } - } -} - -static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->model) { - case CIT_MODEL0: - if (val) - cit_write_reg(gspca_dev, 0x0020, 0x0115); - else - cit_write_reg(gspca_dev, 0x0040, 0x0115); - break; - case CIT_MODEL1: - case CIT_MODEL2: - case CIT_MODEL3: - case CIT_MODEL4: - case CIT_IBM_NETCAM_PRO: - break; - } -} - -static int cit_restart_stream(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->model) { - case CIT_MODEL0: - case CIT_MODEL1: - cit_write_reg(gspca_dev, 0x0001, 0x0114); - /* Fall through */ - case CIT_MODEL2: - case CIT_MODEL4: - cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */ - usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); - break; - case CIT_MODEL3: - case CIT_IBM_NETCAM_PRO: - cit_write_reg(gspca_dev, 0x0001, 0x0114); - cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */ - usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); - /* Clear button events from while we were not streaming */ - cit_write_reg(gspca_dev, 0x0001, 0x0113); - break; - } - - sd->sof_read = 0; - - return 0; -} - -static int cit_get_packet_size(struct gspca_dev *gspca_dev) -{ - struct usb_host_interface *alt; - struct usb_interface *intf; - - intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - alt = usb_altnum_to_altsetting(intf, gspca_dev->alt); - if (!alt) { - pr_err("Couldn't get altsetting\n"); - return -EIO; - } - - return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); -} - -/* Calculate the clockdiv giving us max fps given the available bandwidth */ -static int cit_get_clock_div(struct gspca_dev *gspca_dev) -{ - int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */ - int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 }; - int packet_size; - - packet_size = cit_get_packet_size(gspca_dev); - if (packet_size < 0) - return packet_size; - - while (clock_div > 3 && - 1000 * packet_size > - gspca_dev->width * gspca_dev->height * - fps[clock_div - 1] * 3 / 2) - clock_div--; - - PDEBUG(D_PROBE, - "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)", - packet_size, gspca_dev->width, gspca_dev->height, clock_div, - fps[clock_div]); - - return clock_div; -} - -static int cit_start_model0(struct gspca_dev *gspca_dev) -{ - const unsigned short compression = 0; /* 0=none, 7=best frame rate */ - int clock_div; - - clock_div = cit_get_clock_div(gspca_dev); - if (clock_div < 0) - return clock_div; - - cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */ - cit_write_reg(gspca_dev, 0x0003, 0x0438); - cit_write_reg(gspca_dev, 0x001e, 0x042b); - cit_write_reg(gspca_dev, 0x0041, 0x042c); - cit_write_reg(gspca_dev, 0x0008, 0x0436); - cit_write_reg(gspca_dev, 0x0024, 0x0403); - cit_write_reg(gspca_dev, 0x002c, 0x0404); - cit_write_reg(gspca_dev, 0x0002, 0x0426); - cit_write_reg(gspca_dev, 0x0014, 0x0427); - - switch (gspca_dev->width) { - case 160: /* 160x120 */ - cit_write_reg(gspca_dev, 0x0004, 0x010b); - cit_write_reg(gspca_dev, 0x0001, 0x010a); - cit_write_reg(gspca_dev, 0x0010, 0x0102); - cit_write_reg(gspca_dev, 0x00a0, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0104); - cit_write_reg(gspca_dev, 0x0078, 0x0105); - break; - - case 176: /* 176x144 */ - cit_write_reg(gspca_dev, 0x0006, 0x010b); - cit_write_reg(gspca_dev, 0x0000, 0x010a); - cit_write_reg(gspca_dev, 0x0005, 0x0102); - cit_write_reg(gspca_dev, 0x00b0, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0104); - cit_write_reg(gspca_dev, 0x0090, 0x0105); - break; - - case 320: /* 320x240 */ - cit_write_reg(gspca_dev, 0x0008, 0x010b); - cit_write_reg(gspca_dev, 0x0004, 0x010a); - cit_write_reg(gspca_dev, 0x0005, 0x0102); - cit_write_reg(gspca_dev, 0x00a0, 0x0103); - cit_write_reg(gspca_dev, 0x0010, 0x0104); - cit_write_reg(gspca_dev, 0x0078, 0x0105); - break; - } - - cit_write_reg(gspca_dev, compression, 0x0109); - cit_write_reg(gspca_dev, clock_div, 0x0111); - - return 0; -} - -static int cit_start_model1(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, clock_div; - - clock_div = cit_get_clock_div(gspca_dev); - if (clock_div < 0) - return clock_div; - - cit_read_reg(gspca_dev, 0x0128, 1); - cit_read_reg(gspca_dev, 0x0100, 0); - cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */ - cit_read_reg(gspca_dev, 0x0100, 0); - cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */ - cit_read_reg(gspca_dev, 0x0100, 0); - cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */ - cit_write_reg(gspca_dev, 0x01, 0x0108); - - cit_write_reg(gspca_dev, 0x03, 0x0112); - cit_read_reg(gspca_dev, 0x0115, 0); - cit_write_reg(gspca_dev, 0x06, 0x0115); - cit_read_reg(gspca_dev, 0x0116, 0); - cit_write_reg(gspca_dev, 0x44, 0x0116); - cit_read_reg(gspca_dev, 0x0116, 0); - cit_write_reg(gspca_dev, 0x40, 0x0116); - cit_read_reg(gspca_dev, 0x0115, 0); - cit_write_reg(gspca_dev, 0x0e, 0x0115); - cit_write_reg(gspca_dev, 0x19, 0x012c); - - cit_Packet_Format1(gspca_dev, 0x00, 0x1e); - cit_Packet_Format1(gspca_dev, 0x39, 0x0d); - cit_Packet_Format1(gspca_dev, 0x39, 0x09); - cit_Packet_Format1(gspca_dev, 0x3b, 0x00); - cit_Packet_Format1(gspca_dev, 0x28, 0x22); - cit_Packet_Format1(gspca_dev, 0x27, 0x00); - cit_Packet_Format1(gspca_dev, 0x2b, 0x1f); - cit_Packet_Format1(gspca_dev, 0x39, 0x08); - - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x2c, 0x00); - - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x30, 0x14); - - cit_PacketFormat2(gspca_dev, 0x39, 0x02); - cit_PacketFormat2(gspca_dev, 0x01, 0xe1); - cit_PacketFormat2(gspca_dev, 0x02, 0xcd); - cit_PacketFormat2(gspca_dev, 0x03, 0xcd); - cit_PacketFormat2(gspca_dev, 0x04, 0xfa); - cit_PacketFormat2(gspca_dev, 0x3f, 0xff); - cit_PacketFormat2(gspca_dev, 0x39, 0x00); - - cit_PacketFormat2(gspca_dev, 0x39, 0x02); - cit_PacketFormat2(gspca_dev, 0x0a, 0x37); - cit_PacketFormat2(gspca_dev, 0x0b, 0xb8); - cit_PacketFormat2(gspca_dev, 0x0c, 0xf3); - cit_PacketFormat2(gspca_dev, 0x0d, 0xe3); - cit_PacketFormat2(gspca_dev, 0x0e, 0x0d); - cit_PacketFormat2(gspca_dev, 0x0f, 0xf2); - cit_PacketFormat2(gspca_dev, 0x10, 0xd5); - cit_PacketFormat2(gspca_dev, 0x11, 0xba); - cit_PacketFormat2(gspca_dev, 0x12, 0x53); - cit_PacketFormat2(gspca_dev, 0x3f, 0xff); - cit_PacketFormat2(gspca_dev, 0x39, 0x00); - - cit_PacketFormat2(gspca_dev, 0x39, 0x02); - cit_PacketFormat2(gspca_dev, 0x16, 0x00); - cit_PacketFormat2(gspca_dev, 0x17, 0x28); - cit_PacketFormat2(gspca_dev, 0x18, 0x7d); - cit_PacketFormat2(gspca_dev, 0x19, 0xbe); - cit_PacketFormat2(gspca_dev, 0x3f, 0xff); - cit_PacketFormat2(gspca_dev, 0x39, 0x00); - - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x00, 0x18); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x13, 0x18); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x14, 0x06); - - /* TESTME These are handled through controls - KEEP until someone can test leaving this out is ok */ - if (0) { - /* This is default brightness */ - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x31, 0x37); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x32, 0x46); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x33, 0x55); - } - - cit_Packet_Format1(gspca_dev, 0x2e, 0x04); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x2d, 0x04); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x29, 0x80); - cit_Packet_Format1(gspca_dev, 0x2c, 0x01); - cit_Packet_Format1(gspca_dev, 0x30, 0x17); - cit_Packet_Format1(gspca_dev, 0x39, 0x08); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x34, 0x00); - - cit_write_reg(gspca_dev, 0x00, 0x0101); - cit_write_reg(gspca_dev, 0x00, 0x010a); - - switch (gspca_dev->width) { - case 128: /* 128x96 */ - cit_write_reg(gspca_dev, 0x80, 0x0103); - cit_write_reg(gspca_dev, 0x60, 0x0105); - cit_write_reg(gspca_dev, 0x0c, 0x010b); - cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x0b, 0x011d); - cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x00, 0x0129); - break; - case 176: /* 176x144 */ - cit_write_reg(gspca_dev, 0xb0, 0x0103); - cit_write_reg(gspca_dev, 0x8f, 0x0105); - cit_write_reg(gspca_dev, 0x06, 0x010b); - cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x0d, 0x011d); - cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x03, 0x0129); - break; - case 352: /* 352x288 */ - cit_write_reg(gspca_dev, 0xb0, 0x0103); - cit_write_reg(gspca_dev, 0x90, 0x0105); - cit_write_reg(gspca_dev, 0x02, 0x010b); - cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x05, 0x011d); - cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x00, 0x0129); - break; - } - - cit_write_reg(gspca_dev, 0xff, 0x012b); - - /* TESTME These are handled through controls - KEEP until someone can test leaving this out is ok */ - if (0) { - /* This is another brightness - don't know why */ - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x31, 0xc3); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x32, 0xd2); - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x33, 0xe1); - - /* Default contrast */ - for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x14, 0x0a); - - /* Default sharpness */ - for (i = 0; i < cit_model1_ntries2; i++) - cit_PacketFormat2(gspca_dev, 0x13, 0x1a); - - /* Default lighting conditions */ - cit_Packet_Format1(gspca_dev, 0x0027, - v4l2_ctrl_g_ctrl(sd->lighting)); - } - - /* Assorted init */ - switch (gspca_dev->width) { - case 128: /* 128x96 */ - cit_Packet_Format1(gspca_dev, 0x2b, 0x1e); - cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x36, 0x0102); - cit_write_reg(gspca_dev, 0x1a, 0x0104); - cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x2b, 0x011c); - cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */ - break; - case 176: /* 176x144 */ - cit_Packet_Format1(gspca_dev, 0x2b, 0x1e); - cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x04, 0x0102); - cit_write_reg(gspca_dev, 0x02, 0x0104); - cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x2b, 0x011c); - cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */ - break; - case 352: /* 352x288 */ - cit_Packet_Format1(gspca_dev, 0x2b, 0x1f); - cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x08, 0x0102); - cit_write_reg(gspca_dev, 0x01, 0x0104); - cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */ - cit_write_reg(gspca_dev, 0x2f, 0x011c); - cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */ - break; - } - - cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */ - cit_write_reg(gspca_dev, clock_div, 0x0111); - - return 0; -} - -static int cit_start_model2(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int clock_div = 0; - - cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */ - cit_read_reg(gspca_dev, 0x0116, 0); - cit_write_reg(gspca_dev, 0x0060, 0x0116); - cit_write_reg(gspca_dev, 0x0002, 0x0112); - cit_write_reg(gspca_dev, 0x00bc, 0x012c); - cit_write_reg(gspca_dev, 0x0008, 0x012b); - cit_write_reg(gspca_dev, 0x0000, 0x0108); - cit_write_reg(gspca_dev, 0x0001, 0x0133); - cit_write_reg(gspca_dev, 0x0001, 0x0102); - switch (gspca_dev->width) { - case 176: /* 176x144 */ - cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ - cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ - cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */ - cit_write_reg(gspca_dev, 0x00b9, 0x010a); /* Unique to this mode */ - cit_write_reg(gspca_dev, 0x0038, 0x0119); /* Unique to this mode */ - /* TESTME HDG: this does not seem right - (it is 2 for all other resolutions) */ - sd->sof_len = 10; - break; - case 320: /* 320x240 */ - cit_write_reg(gspca_dev, 0x0028, 0x0103); /* Unique to this mode */ - cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ - cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */ - cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */ - cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ - sd->sof_len = 2; - break; - /* case VIDEOSIZE_352x240: */ - cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ - cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ - cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */ - cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */ - cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ - sd->sof_len = 2; - break; - case 352: /* 352x288 */ - cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ - cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ - cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */ - cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */ - cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ - sd->sof_len = 2; - break; - } - - cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */ - - switch (gspca_dev->width) { - case 176: /* 176x144 */ - cit_write_reg(gspca_dev, 0x0050, 0x0111); - cit_write_reg(gspca_dev, 0x00d0, 0x0111); - break; - case 320: /* 320x240 */ - case 352: /* 352x288 */ - cit_write_reg(gspca_dev, 0x0040, 0x0111); - cit_write_reg(gspca_dev, 0x00c0, 0x0111); - break; - } - cit_write_reg(gspca_dev, 0x009b, 0x010f); - cit_write_reg(gspca_dev, 0x00bb, 0x010f); - - /* - * Hardware settings, may affect CMOS sensor; not user controls! - * ------------------------------------------------------------- - * 0x0004: no effect - * 0x0006: hardware effect - * 0x0008: no effect - * 0x000a: stops video stream, probably important h/w setting - * 0x000c: changes color in hardware manner (not user setting) - * 0x0012: changes number of colors (does not affect speed) - * 0x002a: no effect - * 0x002c: hardware setting (related to scan lines) - * 0x002e: stops video stream, probably important h/w setting - */ - cit_model2_Packet1(gspca_dev, 0x000a, 0x005c); - cit_model2_Packet1(gspca_dev, 0x0004, 0x0000); - cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb); - cit_model2_Packet1(gspca_dev, 0x0008, 0x0000); - cit_model2_Packet1(gspca_dev, 0x000c, 0x0009); - cit_model2_Packet1(gspca_dev, 0x0012, 0x000a); - cit_model2_Packet1(gspca_dev, 0x002a, 0x0000); - cit_model2_Packet1(gspca_dev, 0x002c, 0x0000); - cit_model2_Packet1(gspca_dev, 0x002e, 0x0008); - - /* - * Function 0x0030 pops up all over the place. Apparently - * it is a hardware control register, with every bit assigned to - * do something. - */ - cit_model2_Packet1(gspca_dev, 0x0030, 0x0000); - - /* - * Magic control of CMOS sensor. Only lower values like - * 0-3 work, and picture shifts left or right. Don't change. - */ - switch (gspca_dev->width) { - case 176: /* 176x144 */ - cit_model2_Packet1(gspca_dev, 0x0014, 0x0002); - cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */ - cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */ - clock_div = 6; - break; - case 320: /* 320x240 */ - cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); - cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */ - cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */ - clock_div = 8; - break; - /* case VIDEOSIZE_352x240: */ - /* This mode doesn't work as Windows programs it; changed to work */ - cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */ - cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */ - cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ - clock_div = 10; - break; - case 352: /* 352x288 */ - cit_model2_Packet1(gspca_dev, 0x0014, 0x0003); - cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */ - cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */ - clock_div = 16; - break; - } - - /* TESTME These are handled through controls - KEEP until someone can test leaving this out is ok */ - if (0) - cit_model2_Packet1(gspca_dev, 0x001a, 0x005a); - - /* - * We have our own frame rate setting varying from 0 (slowest) to 6 - * (fastest). The camera model 2 allows frame rate in range [0..0x1F] - # where 0 is also the slowest setting. However for all practical - # reasons high settings make no sense because USB is not fast enough - # to support high FPS. Be aware that the picture datastream will be - # severely disrupted if you ask for frame rate faster than allowed - # for the video size - see below: - * - * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz): - * ----------------------------------------------------------------- - * 176x144: [6..31] - * 320x240: [8..31] - * 352x240: [10..31] - * 352x288: [16..31] I have to raise lower threshold for stability... - * - * As usual, slower FPS provides better sensitivity. - */ - cit_model2_Packet1(gspca_dev, 0x001c, clock_div); - - /* - * This setting does not visibly affect pictures; left it here - * because it was present in Windows USB data stream. This function - * does not allow arbitrary values and apparently is a bit mask, to - * be activated only at appropriate time. Don't change it randomly! - */ - switch (gspca_dev->width) { - case 176: /* 176x144 */ - cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2); - break; - case 320: /* 320x240 */ - cit_model2_Packet1(gspca_dev, 0x0026, 0x0044); - break; - /* case VIDEOSIZE_352x240: */ - cit_model2_Packet1(gspca_dev, 0x0026, 0x0046); - break; - case 352: /* 352x288 */ - cit_model2_Packet1(gspca_dev, 0x0026, 0x0048); - break; - } - - cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting)); - /* model2 cannot change the backlight compensation while streaming */ - v4l2_ctrl_grab(sd->lighting, true); - - /* color balance rg2 */ - cit_model2_Packet1(gspca_dev, 0x001e, 0x002f); - /* saturation */ - cit_model2_Packet1(gspca_dev, 0x0020, 0x0034); - /* color balance yb */ - cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0); - - /* Hardware control command */ - cit_model2_Packet1(gspca_dev, 0x0030, 0x0004); - - return 0; -} - -static int cit_start_model3(struct gspca_dev *gspca_dev) -{ - const unsigned short compression = 0; /* 0=none, 7=best frame rate */ - int i, clock_div = 0; - - /* HDG not in ibmcam driver, added to see if it helps with - auto-detecting between model3 and ibm netcamera pro */ - cit_read_reg(gspca_dev, 0x128, 1); - - cit_write_reg(gspca_dev, 0x0000, 0x0100); - cit_read_reg(gspca_dev, 0x0116, 0); - cit_write_reg(gspca_dev, 0x0060, 0x0116); - cit_write_reg(gspca_dev, 0x0002, 0x0112); - cit_write_reg(gspca_dev, 0x0000, 0x0123); - cit_write_reg(gspca_dev, 0x0001, 0x0117); - cit_write_reg(gspca_dev, 0x0040, 0x0108); - cit_write_reg(gspca_dev, 0x0019, 0x012c); - cit_write_reg(gspca_dev, 0x0060, 0x0116); - cit_write_reg(gspca_dev, 0x0002, 0x0115); - cit_write_reg(gspca_dev, 0x0003, 0x0115); - cit_read_reg(gspca_dev, 0x0115, 0); - cit_write_reg(gspca_dev, 0x000b, 0x0115); - - /* TESTME HDG not in ibmcam driver, added to see if it helps with - auto-detecting between model3 and ibm netcamera pro */ - if (0) { - cit_write_reg(gspca_dev, 0x0078, 0x012d); - cit_write_reg(gspca_dev, 0x0001, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0079, 0x012d); - cit_write_reg(gspca_dev, 0x00ff, 0x0130); - cit_write_reg(gspca_dev, 0xcd41, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_read_reg(gspca_dev, 0x0126, 1); - } - - cit_model3_Packet1(gspca_dev, 0x000a, 0x0040); - cit_model3_Packet1(gspca_dev, 0x000b, 0x00f6); - cit_model3_Packet1(gspca_dev, 0x000c, 0x0002); - cit_model3_Packet1(gspca_dev, 0x000d, 0x0020); - cit_model3_Packet1(gspca_dev, 0x000e, 0x0033); - cit_model3_Packet1(gspca_dev, 0x000f, 0x0007); - cit_model3_Packet1(gspca_dev, 0x0010, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0011, 0x0070); - cit_model3_Packet1(gspca_dev, 0x0012, 0x0030); - cit_model3_Packet1(gspca_dev, 0x0013, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0014, 0x0001); - cit_model3_Packet1(gspca_dev, 0x0015, 0x0001); - cit_model3_Packet1(gspca_dev, 0x0016, 0x0001); - cit_model3_Packet1(gspca_dev, 0x0017, 0x0001); - cit_model3_Packet1(gspca_dev, 0x0018, 0x0000); - cit_model3_Packet1(gspca_dev, 0x001e, 0x00c3); - cit_model3_Packet1(gspca_dev, 0x0020, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0028, 0x0010); - cit_model3_Packet1(gspca_dev, 0x0029, 0x0054); - cit_model3_Packet1(gspca_dev, 0x002a, 0x0013); - cit_model3_Packet1(gspca_dev, 0x002b, 0x0007); - cit_model3_Packet1(gspca_dev, 0x002d, 0x0028); - cit_model3_Packet1(gspca_dev, 0x002e, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0031, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0032, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0033, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0034, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0035, 0x0038); - cit_model3_Packet1(gspca_dev, 0x003a, 0x0001); - cit_model3_Packet1(gspca_dev, 0x003c, 0x001e); - cit_model3_Packet1(gspca_dev, 0x003f, 0x000a); - cit_model3_Packet1(gspca_dev, 0x0041, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0046, 0x003f); - cit_model3_Packet1(gspca_dev, 0x0047, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0050, 0x0005); - cit_model3_Packet1(gspca_dev, 0x0052, 0x001a); - cit_model3_Packet1(gspca_dev, 0x0053, 0x0003); - cit_model3_Packet1(gspca_dev, 0x005a, 0x006b); - cit_model3_Packet1(gspca_dev, 0x005d, 0x001e); - cit_model3_Packet1(gspca_dev, 0x005e, 0x0030); - cit_model3_Packet1(gspca_dev, 0x005f, 0x0041); - cit_model3_Packet1(gspca_dev, 0x0064, 0x0008); - cit_model3_Packet1(gspca_dev, 0x0065, 0x0015); - cit_model3_Packet1(gspca_dev, 0x0068, 0x000f); - cit_model3_Packet1(gspca_dev, 0x0079, 0x0000); - cit_model3_Packet1(gspca_dev, 0x007a, 0x0000); - cit_model3_Packet1(gspca_dev, 0x007c, 0x003f); - cit_model3_Packet1(gspca_dev, 0x0082, 0x000f); - cit_model3_Packet1(gspca_dev, 0x0085, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0099, 0x0000); - cit_model3_Packet1(gspca_dev, 0x009b, 0x0023); - cit_model3_Packet1(gspca_dev, 0x009c, 0x0022); - cit_model3_Packet1(gspca_dev, 0x009d, 0x0096); - cit_model3_Packet1(gspca_dev, 0x009e, 0x0096); - cit_model3_Packet1(gspca_dev, 0x009f, 0x000a); - - switch (gspca_dev->width) { - case 160: - cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ - cit_write_reg(gspca_dev, 0x0024, 0x010b); /* Differs everywhere */ - cit_write_reg(gspca_dev, 0x00a9, 0x0119); - cit_write_reg(gspca_dev, 0x0016, 0x011b); - cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */ - cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ - cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ - cit_write_reg(gspca_dev, 0x0018, 0x0102); - cit_write_reg(gspca_dev, 0x0004, 0x0104); - cit_write_reg(gspca_dev, 0x0004, 0x011a); - cit_write_reg(gspca_dev, 0x0028, 0x011c); - cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ - cit_write_reg(gspca_dev, 0x0000, 0x0118); - cit_write_reg(gspca_dev, 0x0000, 0x0132); - cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */ - cit_write_reg(gspca_dev, compression, 0x0109); - clock_div = 3; - break; - case 320: - cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ - cit_write_reg(gspca_dev, 0x0028, 0x010b); /* Differs everywhere */ - cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same */ - cit_write_reg(gspca_dev, 0x0000, 0x011e); - cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ - cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ - /* 4 commands from 160x120 skipped */ - cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ - cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */ - cit_write_reg(gspca_dev, compression, 0x0109); - cit_write_reg(gspca_dev, 0x00d9, 0x0119); - cit_write_reg(gspca_dev, 0x0006, 0x011b); - cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ - cit_write_reg(gspca_dev, 0x0010, 0x0104); - cit_write_reg(gspca_dev, 0x0004, 0x011a); - cit_write_reg(gspca_dev, 0x003f, 0x011c); - cit_write_reg(gspca_dev, 0x001c, 0x0118); - cit_write_reg(gspca_dev, 0x0000, 0x0132); - clock_div = 5; - break; - case 640: - cit_write_reg(gspca_dev, 0x00f0, 0x0105); - cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ - cit_write_reg(gspca_dev, 0x0038, 0x010b); /* Differs everywhere */ - cit_write_reg(gspca_dev, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */ - cit_write_reg(gspca_dev, 0x0006, 0x011b); /* Same on 320x240, 640x480 */ - cit_write_reg(gspca_dev, 0x0004, 0x011d); /* NC */ - cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */ - cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ - cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ - cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ - cit_write_reg(gspca_dev, 0x0016, 0x0104); /* NC */ - cit_write_reg(gspca_dev, 0x0004, 0x011a); /* Same on 320x240, 640x480 */ - cit_write_reg(gspca_dev, 0x003f, 0x011c); /* Same on 320x240, 640x480 */ - cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ - cit_write_reg(gspca_dev, 0x001c, 0x0118); /* Same on 320x240, 640x480 */ - cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */ - cit_write_reg(gspca_dev, compression, 0x0109); - cit_write_reg(gspca_dev, 0x0040, 0x0101); - cit_write_reg(gspca_dev, 0x0040, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0132); /* Same on 320x240, 640x480 */ - clock_div = 7; - break; - } - - cit_model3_Packet1(gspca_dev, 0x007e, 0x000e); /* Hue */ - cit_model3_Packet1(gspca_dev, 0x0036, 0x0011); /* Brightness */ - cit_model3_Packet1(gspca_dev, 0x0060, 0x0002); /* Sharpness */ - cit_model3_Packet1(gspca_dev, 0x0061, 0x0004); /* Sharpness */ - cit_model3_Packet1(gspca_dev, 0x0062, 0x0005); /* Sharpness */ - cit_model3_Packet1(gspca_dev, 0x0063, 0x0014); /* Sharpness */ - cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */ - cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */ - cit_model3_Packet1(gspca_dev, 0x0067, 0x0001); /* Contrast */ - cit_model3_Packet1(gspca_dev, 0x005b, 0x000c); /* Contrast */ - cit_model3_Packet1(gspca_dev, 0x005c, 0x0016); /* Contrast */ - cit_model3_Packet1(gspca_dev, 0x0098, 0x000b); - cit_model3_Packet1(gspca_dev, 0x002c, 0x0003); /* Was 1, broke 640x480 */ - cit_model3_Packet1(gspca_dev, 0x002f, 0x002a); - cit_model3_Packet1(gspca_dev, 0x0030, 0x0029); - cit_model3_Packet1(gspca_dev, 0x0037, 0x0002); - cit_model3_Packet1(gspca_dev, 0x0038, 0x0059); - cit_model3_Packet1(gspca_dev, 0x003d, 0x002e); - cit_model3_Packet1(gspca_dev, 0x003e, 0x0028); - cit_model3_Packet1(gspca_dev, 0x0078, 0x0005); - cit_model3_Packet1(gspca_dev, 0x007b, 0x0011); - cit_model3_Packet1(gspca_dev, 0x007d, 0x004b); - cit_model3_Packet1(gspca_dev, 0x007f, 0x0022); - cit_model3_Packet1(gspca_dev, 0x0080, 0x000c); - cit_model3_Packet1(gspca_dev, 0x0081, 0x000b); - cit_model3_Packet1(gspca_dev, 0x0083, 0x00fd); - cit_model3_Packet1(gspca_dev, 0x0086, 0x000b); - cit_model3_Packet1(gspca_dev, 0x0087, 0x000b); - cit_model3_Packet1(gspca_dev, 0x007e, 0x000e); - cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */ - cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */ - cit_model3_Packet1(gspca_dev, 0x0098, 0x000b); - - /* FIXME we should probably use cit_get_clock_div() here (in - combination with isoc negotiation using the programmable isoc size) - like with the IBM netcam pro). */ - cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */ - - switch (gspca_dev->width) { - case 160: - cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */ - cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */ - cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */ - cit_model3_Packet1(gspca_dev, 0x0040, 0x000a); - cit_model3_Packet1(gspca_dev, 0x0051, 0x000a); - break; - case 320: - cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */ - cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */ - cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */ - cit_model3_Packet1(gspca_dev, 0x0040, 0x0008); - cit_model3_Packet1(gspca_dev, 0x0051, 0x000b); - break; - case 640: - cit_model3_Packet1(gspca_dev, 0x001f, 0x0002); /* !Same */ - cit_model3_Packet1(gspca_dev, 0x0039, 0x003e); /* !Same */ - cit_model3_Packet1(gspca_dev, 0x0040, 0x0008); - cit_model3_Packet1(gspca_dev, 0x0051, 0x000a); - break; - } - -/* if (sd->input_index) { */ - if (rca_input) { - for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) { - if (rca_initdata[i][0]) - cit_read_reg(gspca_dev, rca_initdata[i][2], 0); - else - cit_write_reg(gspca_dev, rca_initdata[i][1], - rca_initdata[i][2]); - } - } - - return 0; -} - -static int cit_start_model4(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - cit_write_reg(gspca_dev, 0x0000, 0x0100); - cit_write_reg(gspca_dev, 0x00c0, 0x0111); - cit_write_reg(gspca_dev, 0x00bc, 0x012c); - cit_write_reg(gspca_dev, 0x0080, 0x012b); - cit_write_reg(gspca_dev, 0x0000, 0x0108); - cit_write_reg(gspca_dev, 0x0001, 0x0133); - cit_write_reg(gspca_dev, 0x009b, 0x010f); - cit_write_reg(gspca_dev, 0x00bb, 0x010f); - cit_model4_Packet1(gspca_dev, 0x0038, 0x0000); - cit_model4_Packet1(gspca_dev, 0x000a, 0x005c); - - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0004, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0x00fb, 0x012e); - cit_write_reg(gspca_dev, 0x0000, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012f); - cit_write_reg(gspca_dev, 0xd055, 0x0124); - cit_write_reg(gspca_dev, 0x000c, 0x0127); - cit_write_reg(gspca_dev, 0x0009, 0x012e); - cit_write_reg(gspca_dev, 0xaa28, 0x0124); - - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0012, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0008, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x002a, 0x012d); - cit_write_reg(gspca_dev, 0x0000, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_model4_Packet1(gspca_dev, 0x0034, 0x0000); - - switch (gspca_dev->width) { - case 128: /* 128x96 */ - cit_write_reg(gspca_dev, 0x0070, 0x0119); - cit_write_reg(gspca_dev, 0x00d0, 0x0111); - cit_write_reg(gspca_dev, 0x0039, 0x010a); - cit_write_reg(gspca_dev, 0x0001, 0x0102); - cit_write_reg(gspca_dev, 0x0028, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0104); - cit_write_reg(gspca_dev, 0x001e, 0x0105); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0016, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x000a, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0014, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012e); - cit_write_reg(gspca_dev, 0x001a, 0x0130); - cit_write_reg(gspca_dev, 0x8a0a, 0x0124); - cit_write_reg(gspca_dev, 0x005a, 0x012d); - cit_write_reg(gspca_dev, 0x9545, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x0127); - cit_write_reg(gspca_dev, 0x0018, 0x012e); - cit_write_reg(gspca_dev, 0x0043, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012f); - cit_write_reg(gspca_dev, 0xd055, 0x0124); - cit_write_reg(gspca_dev, 0x001c, 0x0127); - cit_write_reg(gspca_dev, 0x00eb, 0x012e); - cit_write_reg(gspca_dev, 0xaa28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0032, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0036, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x001e, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0017, 0x0127); - cit_write_reg(gspca_dev, 0x0013, 0x012e); - cit_write_reg(gspca_dev, 0x0031, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x0017, 0x012d); - cit_write_reg(gspca_dev, 0x0078, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0xfea8, 0x0124); - sd->sof_len = 2; - break; - case 160: /* 160x120 */ - cit_write_reg(gspca_dev, 0x0038, 0x0119); - cit_write_reg(gspca_dev, 0x00d0, 0x0111); - cit_write_reg(gspca_dev, 0x00b9, 0x010a); - cit_write_reg(gspca_dev, 0x0001, 0x0102); - cit_write_reg(gspca_dev, 0x0028, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0104); - cit_write_reg(gspca_dev, 0x001e, 0x0105); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0016, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x000b, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0014, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012e); - cit_write_reg(gspca_dev, 0x001a, 0x0130); - cit_write_reg(gspca_dev, 0x8a0a, 0x0124); - cit_write_reg(gspca_dev, 0x005a, 0x012d); - cit_write_reg(gspca_dev, 0x9545, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x0127); - cit_write_reg(gspca_dev, 0x0018, 0x012e); - cit_write_reg(gspca_dev, 0x0043, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012f); - cit_write_reg(gspca_dev, 0xd055, 0x0124); - cit_write_reg(gspca_dev, 0x001c, 0x0127); - cit_write_reg(gspca_dev, 0x00c7, 0x012e); - cit_write_reg(gspca_dev, 0xaa28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0032, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0025, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0036, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x001e, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0048, 0x0127); - cit_write_reg(gspca_dev, 0x0035, 0x012e); - cit_write_reg(gspca_dev, 0x00d0, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x0048, 0x012d); - cit_write_reg(gspca_dev, 0x0090, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x0001, 0x0127); - cit_write_reg(gspca_dev, 0xfea8, 0x0124); - sd->sof_len = 2; - break; - case 176: /* 176x144 */ - cit_write_reg(gspca_dev, 0x0038, 0x0119); - cit_write_reg(gspca_dev, 0x00d0, 0x0111); - cit_write_reg(gspca_dev, 0x00b9, 0x010a); - cit_write_reg(gspca_dev, 0x0001, 0x0102); - cit_write_reg(gspca_dev, 0x002c, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0104); - cit_write_reg(gspca_dev, 0x0024, 0x0105); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0016, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0007, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0014, 0x012d); - cit_write_reg(gspca_dev, 0x0001, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012e); - cit_write_reg(gspca_dev, 0x001a, 0x0130); - cit_write_reg(gspca_dev, 0x8a0a, 0x0124); - cit_write_reg(gspca_dev, 0x005e, 0x012d); - cit_write_reg(gspca_dev, 0x9545, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x0127); - cit_write_reg(gspca_dev, 0x0018, 0x012e); - cit_write_reg(gspca_dev, 0x0049, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012f); - cit_write_reg(gspca_dev, 0xd055, 0x0124); - cit_write_reg(gspca_dev, 0x001c, 0x0127); - cit_write_reg(gspca_dev, 0x00c7, 0x012e); - cit_write_reg(gspca_dev, 0xaa28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0032, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0028, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0036, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x001e, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0010, 0x0127); - cit_write_reg(gspca_dev, 0x0013, 0x012e); - cit_write_reg(gspca_dev, 0x002a, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x0010, 0x012d); - cit_write_reg(gspca_dev, 0x006d, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x0001, 0x0127); - cit_write_reg(gspca_dev, 0xfea8, 0x0124); - /* TESTME HDG: this does not seem right - (it is 2 for all other resolutions) */ - sd->sof_len = 10; - break; - case 320: /* 320x240 */ - cit_write_reg(gspca_dev, 0x0070, 0x0119); - cit_write_reg(gspca_dev, 0x00d0, 0x0111); - cit_write_reg(gspca_dev, 0x0039, 0x010a); - cit_write_reg(gspca_dev, 0x0001, 0x0102); - cit_write_reg(gspca_dev, 0x0028, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0104); - cit_write_reg(gspca_dev, 0x001e, 0x0105); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0016, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x000a, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0014, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012e); - cit_write_reg(gspca_dev, 0x001a, 0x0130); - cit_write_reg(gspca_dev, 0x8a0a, 0x0124); - cit_write_reg(gspca_dev, 0x005a, 0x012d); - cit_write_reg(gspca_dev, 0x9545, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x0127); - cit_write_reg(gspca_dev, 0x0018, 0x012e); - cit_write_reg(gspca_dev, 0x0043, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012f); - cit_write_reg(gspca_dev, 0xd055, 0x0124); - cit_write_reg(gspca_dev, 0x001c, 0x0127); - cit_write_reg(gspca_dev, 0x00eb, 0x012e); - cit_write_reg(gspca_dev, 0xaa28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0032, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0036, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x001e, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0017, 0x0127); - cit_write_reg(gspca_dev, 0x0013, 0x012e); - cit_write_reg(gspca_dev, 0x0031, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x0017, 0x012d); - cit_write_reg(gspca_dev, 0x0078, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0xfea8, 0x0124); - sd->sof_len = 2; - break; - case 352: /* 352x288 */ - cit_write_reg(gspca_dev, 0x0070, 0x0119); - cit_write_reg(gspca_dev, 0x00c0, 0x0111); - cit_write_reg(gspca_dev, 0x0039, 0x010a); - cit_write_reg(gspca_dev, 0x0001, 0x0102); - cit_write_reg(gspca_dev, 0x002c, 0x0103); - cit_write_reg(gspca_dev, 0x0000, 0x0104); - cit_write_reg(gspca_dev, 0x0024, 0x0105); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0016, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0006, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0014, 0x012d); - cit_write_reg(gspca_dev, 0x0002, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012e); - cit_write_reg(gspca_dev, 0x001a, 0x0130); - cit_write_reg(gspca_dev, 0x8a0a, 0x0124); - cit_write_reg(gspca_dev, 0x005e, 0x012d); - cit_write_reg(gspca_dev, 0x9545, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x0127); - cit_write_reg(gspca_dev, 0x0018, 0x012e); - cit_write_reg(gspca_dev, 0x0049, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012f); - cit_write_reg(gspca_dev, 0xd055, 0x0124); - cit_write_reg(gspca_dev, 0x001c, 0x0127); - cit_write_reg(gspca_dev, 0x00cf, 0x012e); - cit_write_reg(gspca_dev, 0xaa28, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x0032, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0x00aa, 0x0130); - cit_write_reg(gspca_dev, 0x82a8, 0x0124); - cit_write_reg(gspca_dev, 0x0036, 0x012d); - cit_write_reg(gspca_dev, 0x0008, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0xfffa, 0x0124); - cit_write_reg(gspca_dev, 0x00aa, 0x012d); - cit_write_reg(gspca_dev, 0x001e, 0x012f); - cit_write_reg(gspca_dev, 0xd141, 0x0124); - cit_write_reg(gspca_dev, 0x0010, 0x0127); - cit_write_reg(gspca_dev, 0x0013, 0x012e); - cit_write_reg(gspca_dev, 0x0025, 0x0130); - cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, 0x0010, 0x012d); - cit_write_reg(gspca_dev, 0x0048, 0x012f); - cit_write_reg(gspca_dev, 0xd145, 0x0124); - cit_write_reg(gspca_dev, 0x0000, 0x0127); - cit_write_reg(gspca_dev, 0xfea8, 0x0124); - sd->sof_len = 2; - break; - } - - cit_model4_Packet1(gspca_dev, 0x0038, 0x0004); - - return 0; -} - -static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev) -{ - const unsigned short compression = 0; /* 0=none, 7=best frame rate */ - int i, clock_div; - - clock_div = cit_get_clock_div(gspca_dev); - if (clock_div < 0) - return clock_div; - - cit_write_reg(gspca_dev, 0x0003, 0x0133); - cit_write_reg(gspca_dev, 0x0000, 0x0117); - cit_write_reg(gspca_dev, 0x0008, 0x0123); - cit_write_reg(gspca_dev, 0x0000, 0x0100); - cit_write_reg(gspca_dev, 0x0060, 0x0116); - /* cit_write_reg(gspca_dev, 0x0002, 0x0112); see sd_stop0 */ - cit_write_reg(gspca_dev, 0x0000, 0x0133); - cit_write_reg(gspca_dev, 0x0000, 0x0123); - cit_write_reg(gspca_dev, 0x0001, 0x0117); - cit_write_reg(gspca_dev, 0x0040, 0x0108); - cit_write_reg(gspca_dev, 0x0019, 0x012c); - cit_write_reg(gspca_dev, 0x0060, 0x0116); - /* cit_write_reg(gspca_dev, 0x000b, 0x0115); see sd_stop0 */ - - cit_model3_Packet1(gspca_dev, 0x0049, 0x0000); - - cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x003a, 0x0102); /* Hstart */ - cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */ - cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */ - cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */ - cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */ - cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */ - - switch (gspca_dev->width) { - case 160: /* 160x120 */ - cit_write_reg(gspca_dev, 0x0024, 0x010b); - cit_write_reg(gspca_dev, 0x0089, 0x0119); - cit_write_reg(gspca_dev, 0x000a, 0x011b); - cit_write_reg(gspca_dev, 0x0003, 0x011e); - cit_write_reg(gspca_dev, 0x0007, 0x0104); - cit_write_reg(gspca_dev, 0x0009, 0x011a); - cit_write_reg(gspca_dev, 0x008b, 0x011c); - cit_write_reg(gspca_dev, 0x0008, 0x0118); - cit_write_reg(gspca_dev, 0x0000, 0x0132); - break; - case 320: /* 320x240 */ - cit_write_reg(gspca_dev, 0x0028, 0x010b); - cit_write_reg(gspca_dev, 0x00d9, 0x0119); - cit_write_reg(gspca_dev, 0x0006, 0x011b); - cit_write_reg(gspca_dev, 0x0000, 0x011e); - cit_write_reg(gspca_dev, 0x000e, 0x0104); - cit_write_reg(gspca_dev, 0x0004, 0x011a); - cit_write_reg(gspca_dev, 0x003f, 0x011c); - cit_write_reg(gspca_dev, 0x000c, 0x0118); - cit_write_reg(gspca_dev, 0x0000, 0x0132); - break; - } - - cit_model3_Packet1(gspca_dev, 0x0019, 0x0031); - cit_model3_Packet1(gspca_dev, 0x001a, 0x0003); - cit_model3_Packet1(gspca_dev, 0x001b, 0x0038); - cit_model3_Packet1(gspca_dev, 0x001c, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0024, 0x0001); - cit_model3_Packet1(gspca_dev, 0x0027, 0x0001); - cit_model3_Packet1(gspca_dev, 0x002a, 0x0004); - cit_model3_Packet1(gspca_dev, 0x0035, 0x000b); - cit_model3_Packet1(gspca_dev, 0x003f, 0x0001); - cit_model3_Packet1(gspca_dev, 0x0044, 0x0000); - cit_model3_Packet1(gspca_dev, 0x0054, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00c4, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00e7, 0x0001); - cit_model3_Packet1(gspca_dev, 0x00e9, 0x0001); - cit_model3_Packet1(gspca_dev, 0x00ee, 0x0000); - cit_model3_Packet1(gspca_dev, 0x00f3, 0x00c0); - - cit_write_reg(gspca_dev, compression, 0x0109); - cit_write_reg(gspca_dev, clock_div, 0x0111); - -/* if (sd->input_index) { */ - if (rca_input) { - for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) { - if (rca_initdata[i][0]) - cit_read_reg(gspca_dev, rca_initdata[i][2], 0); - else - cit_write_reg(gspca_dev, rca_initdata[i][1], - rca_initdata[i][2]); - } - } - - return 0; -} - -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int packet_size; - - packet_size = cit_get_packet_size(gspca_dev); - if (packet_size < 0) - return packet_size; - - switch (sd->model) { - case CIT_MODEL0: - cit_start_model0(gspca_dev); - break; - case CIT_MODEL1: - cit_start_model1(gspca_dev); - break; - case CIT_MODEL2: - cit_start_model2(gspca_dev); - break; - case CIT_MODEL3: - cit_start_model3(gspca_dev); - break; - case CIT_MODEL4: - cit_start_model4(gspca_dev); - break; - case CIT_IBM_NETCAM_PRO: - cit_start_ibm_netcam_pro(gspca_dev); - break; - } - - /* Program max isoc packet size */ - cit_write_reg(gspca_dev, packet_size >> 8, 0x0106); - cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107); - - cit_restart_stream(gspca_dev); - - return 0; -} - -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - struct usb_host_interface *alt; - int max_packet_size; - - switch (gspca_dev->width) { - case 160: - max_packet_size = 450; - break; - case 176: - max_packet_size = 600; - break; - default: - max_packet_size = 1022; - break; - } - - /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ - alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; - alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size); - - return 0; -} - -static int sd_isoc_nego(struct gspca_dev *gspca_dev) -{ - int ret, packet_size, min_packet_size; - struct usb_host_interface *alt; - - switch (gspca_dev->width) { - case 160: - min_packet_size = 200; - break; - case 176: - min_packet_size = 266; - break; - default: - min_packet_size = 400; - break; - } - - alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; - packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - if (packet_size <= min_packet_size) - return -EIO; - - packet_size -= 100; - if (packet_size < min_packet_size) - packet_size = min_packet_size; - alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size); - - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); - if (ret < 0) - pr_err("set alt 1 err %d\n", ret); - - return ret; -} - -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - cit_write_reg(gspca_dev, 0x0000, 0x010c); -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* We cannot use gspca_dev->present here as that is not set when - sd_init gets called and we get called from sd_init */ - if (!gspca_dev->dev) - return; - - switch (sd->model) { - case CIT_MODEL0: - /* HDG windows does this, but it causes the cams autogain to - restart from a gain of 0, which does not look good when - changing resolutions. */ - /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */ - cit_write_reg(gspca_dev, 0x00c0, 0x0100); /* LED Off */ - break; - case CIT_MODEL1: - cit_send_FF_04_02(gspca_dev); - cit_read_reg(gspca_dev, 0x0100, 0); - cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */ - break; - case CIT_MODEL2: - v4l2_ctrl_grab(sd->lighting, false); - /* Fall through! */ - case CIT_MODEL4: - cit_model2_Packet1(gspca_dev, 0x0030, 0x0004); - - cit_write_reg(gspca_dev, 0x0080, 0x0100); /* LED Off */ - cit_write_reg(gspca_dev, 0x0020, 0x0111); - cit_write_reg(gspca_dev, 0x00a0, 0x0111); - - cit_model2_Packet1(gspca_dev, 0x0030, 0x0002); - - cit_write_reg(gspca_dev, 0x0020, 0x0111); - cit_write_reg(gspca_dev, 0x0000, 0x0112); - break; - case CIT_MODEL3: - cit_write_reg(gspca_dev, 0x0006, 0x012c); - cit_model3_Packet1(gspca_dev, 0x0046, 0x0000); - cit_read_reg(gspca_dev, 0x0116, 0); - cit_write_reg(gspca_dev, 0x0064, 0x0116); - cit_read_reg(gspca_dev, 0x0115, 0); - cit_write_reg(gspca_dev, 0x0003, 0x0115); - cit_write_reg(gspca_dev, 0x0008, 0x0123); - cit_write_reg(gspca_dev, 0x0000, 0x0117); - cit_write_reg(gspca_dev, 0x0000, 0x0112); - cit_write_reg(gspca_dev, 0x0080, 0x0100); - break; - case CIT_IBM_NETCAM_PRO: - cit_model3_Packet1(gspca_dev, 0x0049, 0x00ff); - cit_write_reg(gspca_dev, 0x0006, 0x012c); - cit_write_reg(gspca_dev, 0x0000, 0x0116); - /* HDG windows does this, but I cannot get the camera - to restart with this without redoing the entire init - sequence which makes switching modes really slow */ - /* cit_write_reg(gspca_dev, 0x0006, 0x0115); */ - cit_write_reg(gspca_dev, 0x0008, 0x0123); - cit_write_reg(gspca_dev, 0x0000, 0x0117); - cit_write_reg(gspca_dev, 0x0003, 0x0133); - cit_write_reg(gspca_dev, 0x0000, 0x0111); - /* HDG windows does this, but I get a green picture when - restarting the stream after this */ - /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */ - cit_write_reg(gspca_dev, 0x00c0, 0x0100); - break; - } - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - /* If the last button state is pressed, release it now! */ - if (sd->button_state) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - sd->button_state = 0; - } -#endif -} - -static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 byte3 = 0, byte4 = 0; - int i; - - switch (sd->model) { - case CIT_MODEL0: - case CIT_MODEL1: - case CIT_MODEL3: - case CIT_IBM_NETCAM_PRO: - switch (gspca_dev->width) { - case 160: /* 160x120 */ - byte3 = 0x02; - byte4 = 0x0a; - break; - case 176: /* 176x144 */ - byte3 = 0x02; - byte4 = 0x0e; - break; - case 320: /* 320x240 */ - byte3 = 0x02; - byte4 = 0x08; - break; - case 352: /* 352x288 */ - byte3 = 0x02; - byte4 = 0x00; - break; - case 640: - byte3 = 0x03; - byte4 = 0x08; - break; - } - - /* These have a different byte3 */ - if (sd->model <= CIT_MODEL1) - byte3 = 0x00; - - for (i = 0; i < len; i++) { - /* For this model the SOF always starts at offset 0 - so no need to search the entire frame */ - if (sd->model == CIT_MODEL0 && sd->sof_read != i) - break; - - switch (sd->sof_read) { - case 0: - if (data[i] == 0x00) - sd->sof_read++; - break; - case 1: - if (data[i] == 0xff) - sd->sof_read++; - else if (data[i] == 0x00) - sd->sof_read = 1; - else - sd->sof_read = 0; - break; - case 2: - if (data[i] == byte3) - sd->sof_read++; - else if (data[i] == 0x00) - sd->sof_read = 1; - else - sd->sof_read = 0; - break; - case 3: - if (data[i] == byte4) { - sd->sof_read = 0; - return data + i + (sd->sof_len - 3); - } - if (byte3 == 0x00 && data[i] == 0xff) - sd->sof_read = 2; - else if (data[i] == 0x00) - sd->sof_read = 1; - else - sd->sof_read = 0; - break; - } - } - break; - case CIT_MODEL2: - case CIT_MODEL4: - /* TESTME we need to find a longer sof signature to avoid - false positives */ - for (i = 0; i < len; i++) { - switch (sd->sof_read) { - case 0: - if (data[i] == 0x00) - sd->sof_read++; - break; - case 1: - sd->sof_read = 0; - if (data[i] == 0xff) { - if (i >= 4) - PDEBUG(D_FRAM, - "header found at offset: %d: %02x %02x 00 %02x %02x %02x\n", - i - 1, - data[i - 4], - data[i - 3], - data[i], - data[i + 1], - data[i + 2]); - else - PDEBUG(D_FRAM, - "header found at offset: %d: 00 %02x %02x %02x\n", - i - 1, - data[i], - data[i + 1], - data[i + 2]); - return data + i + (sd->sof_len - 1); - } - break; - } - } - break; - } - return NULL; -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - unsigned char *sof; - - sof = cit_find_sof(gspca_dev, data, len); - if (sof) { - int n; - - /* finish decoding current frame */ - n = sof - data; - if (n > sd->sof_len) - n -= sd->sof_len; - else - n = 0; - gspca_frame_add(gspca_dev, LAST_PACKET, - data, n); - gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - len -= sof - data; - data = sof; - } - - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static void cit_check_button(struct gspca_dev *gspca_dev) -{ - int new_button_state; - struct sd *sd = (struct sd *)gspca_dev; - - switch (sd->model) { - case CIT_MODEL3: - case CIT_IBM_NETCAM_PRO: - break; - default: /* TEST ME unknown if this works on other models too */ - return; - } - - /* Read the button state */ - cit_read_reg(gspca_dev, 0x0113, 0); - new_button_state = !gspca_dev->usb_buf[0]; - - /* Tell the cam we've seen the button press, notice that this - is a nop (iow the cam keeps reporting pressed) until the - button is actually released. */ - if (new_button_state) - cit_write_reg(gspca_dev, 0x01, 0x0113); - - if (sd->button_state != new_button_state) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, - new_button_state); - input_sync(gspca_dev->input_dev); - sd->button_state = new_button_state; - } -} -#endif - -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - gspca_dev->usb_err = 0; - - if (!gspca_dev->streaming) - return 0; - - if (sd->stop_on_control_change) - sd_stopN(gspca_dev); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - cit_set_brightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - cit_set_contrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_HUE: - cit_set_hue(gspca_dev, ctrl->val); - break; - case V4L2_CID_HFLIP: - cit_set_hflip(gspca_dev, ctrl->val); - break; - case V4L2_CID_SHARPNESS: - cit_set_sharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_BACKLIGHT_COMPENSATION: - cit_set_lighting(gspca_dev, ctrl->val); - break; - } - if (sd->stop_on_control_change) - cit_restart_stream(gspca_dev); - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - bool has_brightness; - bool has_contrast; - bool has_hue; - bool has_sharpness; - bool has_lighting; - bool has_hflip; - - has_brightness = has_contrast = has_hue = - has_sharpness = has_hflip = has_lighting = false; - switch (sd->model) { - case CIT_MODEL0: - has_contrast = has_hflip = true; - break; - case CIT_MODEL1: - has_brightness = has_contrast = - has_sharpness = has_lighting = true; - break; - case CIT_MODEL2: - has_brightness = has_hue = has_lighting = true; - break; - case CIT_MODEL3: - has_brightness = has_contrast = has_sharpness = true; - break; - case CIT_MODEL4: - has_brightness = has_hue = true; - break; - case CIT_IBM_NETCAM_PRO: - has_brightness = has_hue = - has_sharpness = has_hflip = has_lighting = true; - break; - } - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - if (has_brightness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); - if (has_contrast) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 20, 1, 10); - if (has_hue) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HUE, 0, 127, 1, 63); - if (has_sharpness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 6, 1, 3); - if (has_lighting) - sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1); - if (has_hflip) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - -/* sub-driver description */ -static const struct sd_desc sd_desc = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .dq_callback = cit_check_button, - .other_input = 1, -#endif -}; - -static const struct sd_desc sd_desc_isoc_nego = { - .name = MODULE_NAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .start = sd_start, - .isoc_init = sd_isoc_init, - .isoc_nego = sd_isoc_nego, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .dq_callback = cit_check_button, - .other_input = 1, -#endif -}; - -/* -- module initialisation -- */ -static const struct usb_device_id device_table[] = { - { USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 }, - { USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 }, - { USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 }, - { USB_DEVICE_VER(0x0545, 0x8080, 0x0301, 0x0301), .driver_info = CIT_MODEL3 }, - { USB_DEVICE_VER(0x0545, 0x8002, 0x030a, 0x030a), .driver_info = CIT_MODEL4 }, - { USB_DEVICE_VER(0x0545, 0x800c, 0x030a, 0x030a), .driver_info = CIT_MODEL2 }, - { USB_DEVICE_VER(0x0545, 0x800d, 0x030a, 0x030a), .driver_info = CIT_MODEL4 }, - {} -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - const struct sd_desc *desc = &sd_desc; - - switch (id->driver_info) { - case CIT_MODEL0: - case CIT_MODEL1: - if (intf->cur_altsetting->desc.bInterfaceNumber != 2) - return -ENODEV; - break; - case CIT_MODEL2: - case CIT_MODEL4: - if (intf->cur_altsetting->desc.bInterfaceNumber != 0) - return -ENODEV; - break; - case CIT_MODEL3: - if (intf->cur_altsetting->desc.bInterfaceNumber != 0) - return -ENODEV; - /* FIXME this likely applies to all model3 cams and probably - to other models too. */ - if (ibm_netcam_pro) - desc = &sd_desc_isoc_nego; - break; - } - - return gspca_dev_probe2(intf, id, desc, sizeof(struct sd), THIS_MODULE); -} - -static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h deleted file mode 100644 index a1bd94e8ce52..000000000000 --- a/drivers/media/video/gspca/zc3xx-reg.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * zc030x registers - * - * Copyright (c) 2008 Mauro Carvalho Chehab - * - * The register aliases used here came from this driver: - * http://zc0302.sourceforge.net/zc0302.php - * - * This code is placed under the terms of the GNU General Public License v2 - */ - -/* Define the register map */ -#define ZC3XX_R000_SYSTEMCONTROL 0x0000 -#define ZC3XX_R001_SYSTEMOPERATING 0x0001 - -/* Picture size */ -#define ZC3XX_R002_CLOCKSELECT 0x0002 -#define ZC3XX_R003_FRAMEWIDTHHIGH 0x0003 -#define ZC3XX_R004_FRAMEWIDTHLOW 0x0004 -#define ZC3XX_R005_FRAMEHEIGHTHIGH 0x0005 -#define ZC3XX_R006_FRAMEHEIGHTLOW 0x0006 - -/* JPEG control */ -#define ZC3XX_R008_CLOCKSETTING 0x0008 - -/* Test mode */ -#define ZC3XX_R00B_TESTMODECONTROL 0x000b - -/* Frame retreiving */ -#define ZC3XX_R00C_LASTACQTIME 0x000c -#define ZC3XX_R00D_MONITORRES 0x000d -#define ZC3XX_R00E_TIMESTAMPHIGH 0x000e -#define ZC3XX_R00F_TIMESTAMPLOW 0x000f -#define ZC3XX_R018_FRAMELOST 0x0018 -#define ZC3XX_R019_AUTOADJUSTFPS 0x0019 -#define ZC3XX_R01A_LASTFRAMESTATE 0x001a -#define ZC3XX_R025_DATACOUNTER 0x0025 - -/* Stream and sensor specific */ -#define ZC3XX_R010_CMOSSENSORSELECT 0x0010 -#define ZC3XX_R011_VIDEOSTATUS 0x0011 -#define ZC3XX_R012_VIDEOCONTROLFUNC 0x0012 - -/* Horizontal and vertical synchros */ -#define ZC3XX_R01D_HSYNC_0 0x001d -#define ZC3XX_R01E_HSYNC_1 0x001e -#define ZC3XX_R01F_HSYNC_2 0x001f -#define ZC3XX_R020_HSYNC_3 0x0020 - -/* Target picture size in byte */ -#define ZC3XX_R022_TARGETPICTSIZE_0 0x0022 -#define ZC3XX_R023_TARGETPICTSIZE_1 0x0023 -#define ZC3XX_R024_TARGETPICTSIZE_2 0x0024 - -/* Audio registers */ -#define ZC3XX_R030_AUDIOADC 0x0030 -#define ZC3XX_R031_AUDIOSTREAMSTATUS 0x0031 -#define ZC3XX_R032_AUDIOSTATUS 0x0032 - -/* Sensor interface */ -#define ZC3XX_R080_HBLANKHIGH 0x0080 -#define ZC3XX_R081_HBLANKLOW 0x0081 -#define ZC3XX_R082_RESETLEVELADDR 0x0082 -#define ZC3XX_R083_RGAINADDR 0x0083 -#define ZC3XX_R084_GGAINADDR 0x0084 -#define ZC3XX_R085_BGAINADDR 0x0085 -#define ZC3XX_R086_EXPTIMEHIGH 0x0086 -#define ZC3XX_R087_EXPTIMEMID 0x0087 -#define ZC3XX_R088_EXPTIMELOW 0x0088 -#define ZC3XX_R089_RESETBLACKHIGH 0x0089 -#define ZC3XX_R08A_RESETWHITEHIGH 0x008a -#define ZC3XX_R08B_I2CDEVICEADDR 0x008b -#define ZC3XX_R08C_I2CIDLEANDNACK 0x008c -#define ZC3XX_R08D_COMPABILITYMODE 0x008d -#define ZC3XX_R08E_COMPABILITYMODE2 0x008e - -/* I2C control */ -#define ZC3XX_R090_I2CCOMMAND 0x0090 -#define ZC3XX_R091_I2CSTATUS 0x0091 -#define ZC3XX_R092_I2CADDRESSSELECT 0x0092 -#define ZC3XX_R093_I2CSETVALUE 0x0093 -#define ZC3XX_R094_I2CWRITEACK 0x0094 -#define ZC3XX_R095_I2CREAD 0x0095 -#define ZC3XX_R096_I2CREADACK 0x0096 - -/* Window inside the sensor array */ -#define ZC3XX_R097_WINYSTARTHIGH 0x0097 -#define ZC3XX_R098_WINYSTARTLOW 0x0098 -#define ZC3XX_R099_WINXSTARTHIGH 0x0099 -#define ZC3XX_R09A_WINXSTARTLOW 0x009a -#define ZC3XX_R09B_WINHEIGHTHIGH 0x009b -#define ZC3XX_R09C_WINHEIGHTLOW 0x009c -#define ZC3XX_R09D_WINWIDTHHIGH 0x009d -#define ZC3XX_R09E_WINWIDTHLOW 0x009e -#define ZC3XX_R119_FIRSTYHIGH 0x0119 -#define ZC3XX_R11A_FIRSTYLOW 0x011a -#define ZC3XX_R11B_FIRSTXHIGH 0x011b -#define ZC3XX_R11C_FIRSTXLOW 0x011c - -/* Max sensor array size */ -#define ZC3XX_R09F_MAXXHIGH 0x009f -#define ZC3XX_R0A0_MAXXLOW 0x00a0 -#define ZC3XX_R0A1_MAXYHIGH 0x00a1 -#define ZC3XX_R0A2_MAXYLOW 0x00a2 -#define ZC3XX_R0A3_EXPOSURETIMEHIGH 0x00a3 -#define ZC3XX_R0A4_EXPOSURETIMELOW 0x00a4 -#define ZC3XX_R0A5_EXPOSUREGAIN 0x00a5 -#define ZC3XX_R0A6_EXPOSUREBLACKLVL 0x00a6 - -/* Other registers */ -#define ZC3XX_R100_OPERATIONMODE 0x0100 -#define ZC3XX_R101_SENSORCORRECTION 0x0101 - -/* Gains */ -#define ZC3XX_R116_RGAIN 0x0116 -#define ZC3XX_R117_GGAIN 0x0117 -#define ZC3XX_R118_BGAIN 0x0118 -#define ZC3XX_R11D_GLOBALGAIN 0x011d -#define ZC3XX_R1A8_DIGITALGAIN 0x01a8 -#define ZC3XX_R1A9_DIGITALLIMITDIFF 0x01a9 -#define ZC3XX_R1AA_DIGITALGAINSTEP 0x01aa - -/* Auto correction */ -#define ZC3XX_R180_AUTOCORRECTENABLE 0x0180 -#define ZC3XX_R181_WINXSTART 0x0181 -#define ZC3XX_R182_WINXWIDTH 0x0182 -#define ZC3XX_R183_WINXCENTER 0x0183 -#define ZC3XX_R184_WINYSTART 0x0184 -#define ZC3XX_R185_WINYWIDTH 0x0185 -#define ZC3XX_R186_WINYCENTER 0x0186 - -/* Gain range */ -#define ZC3XX_R187_MAXGAIN 0x0187 -#define ZC3XX_R188_MINGAIN 0x0188 - -/* Auto exposure and white balance */ -#define ZC3XX_R189_AWBSTATUS 0x0189 -#define ZC3XX_R18A_AWBFREEZE 0x018a -#define ZC3XX_R18B_AESTATUS 0x018b -#define ZC3XX_R18C_AEFREEZE 0x018c -#define ZC3XX_R18F_AEUNFREEZE 0x018f -#define ZC3XX_R190_EXPOSURELIMITHIGH 0x0190 -#define ZC3XX_R191_EXPOSURELIMITMID 0x0191 -#define ZC3XX_R192_EXPOSURELIMITLOW 0x0192 -#define ZC3XX_R195_ANTIFLICKERHIGH 0x0195 -#define ZC3XX_R196_ANTIFLICKERMID 0x0196 -#define ZC3XX_R197_ANTIFLICKERLOW 0x0197 - -/* What is this ? */ -#define ZC3XX_R18D_YTARGET 0x018d -#define ZC3XX_R18E_RESETLVL 0x018e - -/* Color */ -#define ZC3XX_R1A0_REDMEANAFTERAGC 0x01a0 -#define ZC3XX_R1A1_GREENMEANAFTERAGC 0x01a1 -#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2 -#define ZC3XX_R1A3_REDMEANAFTERAWB 0x01a3 -#define ZC3XX_R1A4_GREENMEANAFTERAWB 0x01a4 -#define ZC3XX_R1A5_BLUEMEANAFTERAWB 0x01a5 -#define ZC3XX_R1A6_YMEANAFTERAE 0x01a6 -#define ZC3XX_R1A7_CALCGLOBALMEAN 0x01a7 - -/* Matrixes */ - -/* Color matrix is like : - R' = R * RGB00 + G * RGB01 + B * RGB02 + RGB03 - G' = R * RGB10 + G * RGB11 + B * RGB22 + RGB13 - B' = R * RGB20 + G * RGB21 + B * RGB12 + RGB23 - */ -#define ZC3XX_R10A_RGB00 0x010a -#define ZC3XX_R10B_RGB01 0x010b -#define ZC3XX_R10C_RGB02 0x010c -#define ZC3XX_R113_RGB03 0x0113 -#define ZC3XX_R10D_RGB10 0x010d -#define ZC3XX_R10E_RGB11 0x010e -#define ZC3XX_R10F_RGB12 0x010f -#define ZC3XX_R114_RGB13 0x0114 -#define ZC3XX_R110_RGB20 0x0110 -#define ZC3XX_R111_RGB21 0x0111 -#define ZC3XX_R112_RGB22 0x0112 -#define ZC3XX_R115_RGB23 0x0115 - -/* Gamma matrix */ -#define ZC3XX_R120_GAMMA00 0x0120 -#define ZC3XX_R121_GAMMA01 0x0121 -#define ZC3XX_R122_GAMMA02 0x0122 -#define ZC3XX_R123_GAMMA03 0x0123 -#define ZC3XX_R124_GAMMA04 0x0124 -#define ZC3XX_R125_GAMMA05 0x0125 -#define ZC3XX_R126_GAMMA06 0x0126 -#define ZC3XX_R127_GAMMA07 0x0127 -#define ZC3XX_R128_GAMMA08 0x0128 -#define ZC3XX_R129_GAMMA09 0x0129 -#define ZC3XX_R12A_GAMMA0A 0x012a -#define ZC3XX_R12B_GAMMA0B 0x012b -#define ZC3XX_R12C_GAMMA0C 0x012c -#define ZC3XX_R12D_GAMMA0D 0x012d -#define ZC3XX_R12E_GAMMA0E 0x012e -#define ZC3XX_R12F_GAMMA0F 0x012f -#define ZC3XX_R130_GAMMA10 0x0130 -#define ZC3XX_R131_GAMMA11 0x0131 -#define ZC3XX_R132_GAMMA12 0x0132 -#define ZC3XX_R133_GAMMA13 0x0133 -#define ZC3XX_R134_GAMMA14 0x0134 -#define ZC3XX_R135_GAMMA15 0x0135 -#define ZC3XX_R136_GAMMA16 0x0136 -#define ZC3XX_R137_GAMMA17 0x0137 -#define ZC3XX_R138_GAMMA18 0x0138 -#define ZC3XX_R139_GAMMA19 0x0139 -#define ZC3XX_R13A_GAMMA1A 0x013a -#define ZC3XX_R13B_GAMMA1B 0x013b -#define ZC3XX_R13C_GAMMA1C 0x013c -#define ZC3XX_R13D_GAMMA1D 0x013d -#define ZC3XX_R13E_GAMMA1E 0x013e -#define ZC3XX_R13F_GAMMA1F 0x013f - -/* Luminance gamma */ -#define ZC3XX_R140_YGAMMA00 0x0140 -#define ZC3XX_R141_YGAMMA01 0x0141 -#define ZC3XX_R142_YGAMMA02 0x0142 -#define ZC3XX_R143_YGAMMA03 0x0143 -#define ZC3XX_R144_YGAMMA04 0x0144 -#define ZC3XX_R145_YGAMMA05 0x0145 -#define ZC3XX_R146_YGAMMA06 0x0146 -#define ZC3XX_R147_YGAMMA07 0x0147 -#define ZC3XX_R148_YGAMMA08 0x0148 -#define ZC3XX_R149_YGAMMA09 0x0149 -#define ZC3XX_R14A_YGAMMA0A 0x014a -#define ZC3XX_R14B_YGAMMA0B 0x014b -#define ZC3XX_R14C_YGAMMA0C 0x014c -#define ZC3XX_R14D_YGAMMA0D 0x014d -#define ZC3XX_R14E_YGAMMA0E 0x014e -#define ZC3XX_R14F_YGAMMA0F 0x014f -#define ZC3XX_R150_YGAMMA10 0x0150 -#define ZC3XX_R151_YGAMMA11 0x0151 - -#define ZC3XX_R1C5_SHARPNESSMODE 0x01c5 -#define ZC3XX_R1C6_SHARPNESS00 0x01c6 -#define ZC3XX_R1C7_SHARPNESS01 0x01c7 -#define ZC3XX_R1C8_SHARPNESS02 0x01c8 -#define ZC3XX_R1C9_SHARPNESS03 0x01c9 -#define ZC3XX_R1CA_SHARPNESS04 0x01ca -#define ZC3XX_R1CB_SHARPNESS05 0x01cb - -/* Dead pixels */ -#define ZC3XX_R250_DEADPIXELSMODE 0x0250 - -/* EEPROM */ -#define ZC3XX_R300_EEPROMCONFIG 0x0300 -#define ZC3XX_R301_EEPROMACCESS 0x0301 -#define ZC3XX_R302_EEPROMSTATUS 0x0302 diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c deleted file mode 100644 index f0bacee33ef9..000000000000 --- a/drivers/media/video/gspca/zc3xx.c +++ /dev/null @@ -1,7024 +0,0 @@ -/* - * Z-Star/Vimicro zc301/zc302p/vc30x driver - * - * Copyright (C) 2009-2012 Jean-Francois Moine - * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include "gspca.h" -#include "jpeg.h" - -MODULE_AUTHOR("Jean-Francois Moine , " - "Serge A. Suchkov "); -MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); -MODULE_LICENSE("GPL"); - -static int force_sensor = -1; - -#define REG08_DEF 3 /* default JPEG compression (75%) */ -#include "zc3xx-reg.h" - -/* specific webcam descriptor */ -struct sd { - struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct { /* gamma/brightness/contrast control cluster */ - struct v4l2_ctrl *gamma; - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - }; - struct { /* autogain/exposure control cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *exposure; - }; - struct v4l2_ctrl *plfreq; - struct v4l2_ctrl *sharpness; - struct v4l2_ctrl *jpegqual; - - struct work_struct work; - struct workqueue_struct *work_thread; - - u8 reg08; /* webcam compression quality */ - - u8 bridge; - u8 sensor; /* Type of image sensor chip */ - u16 chip_revision; - - u8 jpeg_hdr[JPEG_HDR_SZ]; -}; -enum bridges { - BRIDGE_ZC301, - BRIDGE_ZC303, -}; -enum sensors { - SENSOR_ADCM2700, - SENSOR_CS2102, - SENSOR_CS2102K, - SENSOR_GC0303, - SENSOR_GC0305, - SENSOR_HDCS2020, - SENSOR_HV7131B, - SENSOR_HV7131R, - SENSOR_ICM105A, - SENSOR_MC501CB, - SENSOR_MT9V111_1, /* (mi360soc) zc301 */ - SENSOR_MT9V111_3, /* (mi360soc) zc303 */ - SENSOR_OV7620, /* OV7648 - same values */ - SENSOR_OV7630C, - SENSOR_PAS106, - SENSOR_PAS202B, - SENSOR_PB0330, - SENSOR_PO2030, - SENSOR_TAS5130C, - SENSOR_MAX -}; - -static const struct v4l2_pix_format vga_mode[] = { - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -static const struct v4l2_pix_format broken_vga_mode[] = { - {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 232 * 4 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 472 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -static const struct v4l2_pix_format sif_mode[] = { - {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, - {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, - .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, -}; - -/* - * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest - * quality setting is not usable as USB 1 does not have enough bandwidth. - */ -static u8 jpeg_qual[] = {50, 75, 87, /* 94 */}; - -/* usb exchanges */ -struct usb_action { - u8 req; - u8 val; - u16 idx; -}; - -static const struct usb_action adcm2700_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ - {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ - {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */ - {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */ - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ - {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ - {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */ - {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */ - {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ - {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */ - {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */ - {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */ - {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */ - {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */ - {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */ - {0xbb, 0x86, 0x0002}, /* 00,86,02,bb */ - {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */ - {0xbb, 0x86, 0x0802}, /* 08,86,02,bb */ - {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */ -/*mswin+*/ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, - {0xaa, 0xfe, 0x0002}, - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xaa, 0xb4, 0xcd37}, - {0xaa, 0xa4, 0x0004}, - {0xaa, 0xa8, 0x0007}, - {0xaa, 0xac, 0x0004}, -/*mswin-*/ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */ - {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ - {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */ - {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ - {} -}; -static const struct usb_action adcm2700_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ - {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ - {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */ - {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */ - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ - {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ - {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */ - {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */ - {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ - {0xdd, 0x00, 0x0050}, /* 00,00,50,dd */ - {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ - {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */ - {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */ - {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */ - {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */ - {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */ - {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */ - {0xbb, 0x86, 0x0002}, /* 00,88,02,bb */ - {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */ - {0xbb, 0x86, 0x0802}, /* 08,88,02,bb */ - {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */ - /*******/ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ - {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */ - {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ - {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */ - {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ - {} -}; -static const struct usb_action adcm2700_50HZ[] = { - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xbb, 0x05, 0x8400}, /* 84,05,00,bb */ - {0xbb, 0xd0, 0xb007}, /* b0,d0,07,bb */ - {0xbb, 0xa0, 0xb80f}, /* b8,a0,0f,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ - {0xaa, 0x26, 0x00d0}, /* 00,26,d0,aa */ - {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */ - {} -}; -static const struct usb_action adcm2700_60HZ[] = { - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */ - {0xbb, 0x82, 0xb006}, /* b0,82,06,bb */ - {0xbb, 0x04, 0xb80d}, /* b8,04,0d,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ - {0xaa, 0x26, 0x0057}, /* 00,26,57,aa */ - {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */ - {} -}; -static const struct usb_action adcm2700_NoFliker[] = { - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ - {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */ - {0xbb, 0x05, 0xb000}, /* b0,05,00,bb */ - {0xbb, 0xa0, 0xb801}, /* b8,a0,01,bb */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ - {} -}; -static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH}, - {0xa0, 0x21, ZC3XX_R081_HBLANKLOW}, - {0xa0, 0x30, ZC3XX_R083_RGAINADDR}, - {0xa0, 0x31, ZC3XX_R084_GGAINADDR}, - {0xa0, 0x32, ZC3XX_R085_BGAINADDR}, - {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH}, - {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xaa, 0x02, 0x0008}, - {0xaa, 0x03, 0x0000}, - {0xaa, 0x11, 0x0000}, - {0xaa, 0x12, 0x0089}, - {0xaa, 0x13, 0x0000}, - {0xaa, 0x14, 0x00e9}, - {0xaa, 0x20, 0x0000}, - {0xaa, 0x22, 0x0000}, - {0xaa, 0x0b, 0x0004}, - {0xaa, 0x30, 0x0030}, - {0xaa, 0x31, 0x0030}, - {0xaa, 0x32, 0x0030}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x10, 0x01ae}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x00, 0x01ad}, - {} -}; - -static const struct usb_action cs2102_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH}, - {0xa0, 0x21, ZC3XX_R081_HBLANKLOW}, - {0xa0, 0x30, ZC3XX_R083_RGAINADDR}, - {0xa0, 0x31, ZC3XX_R084_GGAINADDR}, - {0xa0, 0x32, ZC3XX_R085_BGAINADDR}, - {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH}, - {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xaa, 0x02, 0x0008}, - {0xaa, 0x03, 0x0000}, - {0xaa, 0x11, 0x0001}, - {0xaa, 0x12, 0x0087}, - {0xaa, 0x13, 0x0001}, - {0xaa, 0x14, 0x00e7}, - {0xaa, 0x20, 0x0000}, - {0xaa, 0x22, 0x0000}, - {0xaa, 0x0b, 0x0004}, - {0xaa, 0x30, 0x0030}, - {0xaa, 0x31, 0x0030}, - {0xaa, 0x32, 0x0030}, - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x00, 0x01ad}, - {} -}; -static const struct usb_action cs2102_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0001}, - {0xaa, 0x24, 0x005f}, - {0xaa, 0x25, 0x0090}, - {0xaa, 0x21, 0x00dd}, - {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action cs2102_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0000}, - {0xaa, 0x24, 0x00af}, - {0xaa, 0x25, 0x00c8}, - {0xaa, 0x21, 0x0068}, - {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action cs2102_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0001}, - {0xaa, 0x24, 0x0055}, - {0xaa, 0x25, 0x00cc}, - {0xaa, 0x21, 0x003f}, - {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action cs2102_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0000}, - {0xaa, 0x24, 0x00aa}, - {0xaa, 0x25, 0x00e6}, - {0xaa, 0x21, 0x003f}, - {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x55, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xcc, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x18, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x6a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x3f, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action cs2102_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0001}, - {0xaa, 0x24, 0x005f}, - {0xaa, 0x25, 0x0000}, - {0xaa, 0x21, 0x0001}, - {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action cs2102_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0000}, - {0xaa, 0x24, 0x00af}, - {0xaa, 0x25, 0x0080}, - {0xaa, 0x21, 0x0001}, - {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; - -/* CS2102_KOCOM */ -static const struct usb_action cs2102K_InitialScale[] = { - {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x01, 0x01b1}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x4c, ZC3XX_R118_BGAIN}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x4c, ZC3XX_R118_BGAIN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {} -}; - -static const struct usb_action cs2102K_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, -/*fixme: next sequence = i2c exchanges*/ - {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x01, 0x01b1}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x4c, ZC3XX_R118_BGAIN}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x4c, ZC3XX_R118_BGAIN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, -/*fixme:what does the next sequence?*/ - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {} -}; - -static const struct usb_action gc0305_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ - {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */ - {0xaa, 0x13, 0x0002}, /* 00,13,02,aa */ - {0xaa, 0x15, 0x0003}, /* 00,15,03,aa */ - {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ - {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */ - {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ - {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */ - {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */ - {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */ - {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */ - {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */ - {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ - {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */ - {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */ - {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */ - {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */ - {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */ - {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */ - {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */ - {0xaa, 0x17, 0x00e6}, /* 00,17,e6,aa */ - {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */ - {0xaa, 0x19, 0x0086}, /* 00,19,86,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */ - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */ - {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ - {0xa0, 0x85, ZC3XX_R18D_YTARGET}, /* 01,8d,85,cc */ - {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */ - {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */ - {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */ - {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ - {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */ - {} -}; -static const struct usb_action gc0305_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ - {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */ - {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ - {0xaa, 0x15, 0x0001}, /* 00,15,01,aa */ - {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ - {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */ - {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ - {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */ - {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */ - {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */ - {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */ - {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */ - {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ - {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */ - {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */ - {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */ - {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */ - {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */ - {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */ - {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */ - {0xaa, 0x17, 0x00e8}, /* 00,17,e8,aa */ - {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */ - {0xaa, 0x19, 0x0088}, /* 00,19,88,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */ - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */ - {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ - {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */ - {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */ - {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */ - {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ - {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */ - {} -}; -static const struct usb_action gc0305_50HZ[] = { - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */ - {0xaa, 0x84, 0x0038}, /* 00,84,38,aa */ /* win: 00,84,ec */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */ - {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ - /* win: 01,92,10 */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc */ - /* win: 01,97,ec */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ -/* {0xa0, 0x85, ZC3XX_R18D_YTARGET}, * 01,8d,85,cc * - * if 640x480 */ - {} -}; -static const struct usb_action gc0305_60HZ[] = { - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ - {0xaa, 0x84, 0x00ec}, /* 00,84,ec,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */ - {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0xec, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,ec,cc */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */ - {} -}; - -static const struct usb_action gc0305_NoFliker[] = { - {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */ - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ - {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,00,cc */ - {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,48,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ - {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */ - {} -}; - -static const struct usb_action hdcs2020_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */ - {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x1c, 0x0000}, - {0xaa, 0x0a, 0x0001}, - {0xaa, 0x0b, 0x0006}, - {0xaa, 0x0c, 0x007b}, - {0xaa, 0x0d, 0x00a7}, - {0xaa, 0x03, 0x00fb}, - {0xaa, 0x05, 0x0000}, - {0xaa, 0x06, 0x0003}, - {0xaa, 0x09, 0x0008}, - - {0xaa, 0x0f, 0x0018}, /* set sensor gain */ - {0xaa, 0x10, 0x0018}, - {0xaa, 0x11, 0x0018}, - {0xaa, 0x12, 0x0018}, - - {0xaa, 0x15, 0x004e}, - {0xaa, 0x1c, 0x0004}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - - {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xed, ZC3XX_R10B_RGB01}, - {0xa0, 0xed, ZC3XX_R10C_RGB02}, - {0xa0, 0xed, ZC3XX_R10D_RGB10}, - {0xa0, 0x66, ZC3XX_R10E_RGB11}, - {0xa0, 0xed, ZC3XX_R10F_RGB12}, - {0xa0, 0xed, ZC3XX_R110_RGB20}, - {0xa0, 0xed, ZC3XX_R111_RGB21}, - {0xa0, 0x66, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x13, 0x0031}, - {0xaa, 0x14, 0x0001}, - {0xaa, 0x0e, 0x0004}, - {0xaa, 0x19, 0x00cd}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 0x14 */ - {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x41, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action hdcs2020_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x1c, 0x0000}, - {0xaa, 0x0a, 0x0001}, - {0xaa, 0x0b, 0x0006}, - {0xaa, 0x0c, 0x007a}, - {0xaa, 0x0d, 0x00a7}, - {0xaa, 0x03, 0x00fb}, - {0xaa, 0x05, 0x0000}, - {0xaa, 0x06, 0x0003}, - {0xaa, 0x09, 0x0008}, - {0xaa, 0x0f, 0x0018}, /* original setting */ - {0xaa, 0x10, 0x0018}, - {0xaa, 0x11, 0x0018}, - {0xaa, 0x12, 0x0018}, - {0xaa, 0x15, 0x004e}, - {0xaa, 0x1c, 0x0004}, - {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xed, ZC3XX_R10B_RGB01}, - {0xa0, 0xed, ZC3XX_R10C_RGB02}, - {0xa0, 0xed, ZC3XX_R10D_RGB10}, - {0xa0, 0x66, ZC3XX_R10E_RGB11}, - {0xa0, 0xed, ZC3XX_R10F_RGB12}, - {0xa0, 0xed, ZC3XX_R110_RGB20}, - {0xa0, 0xed, ZC3XX_R111_RGB21}, - {0xa0, 0x66, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - /**** set exposure ***/ - {0xaa, 0x13, 0x0031}, - {0xaa, 0x14, 0x0001}, - {0xaa, 0x0e, 0x0004}, - {0xaa, 0x19, 0x00cd}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x41, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action hdcs2020_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */ - {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ - {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */ - {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ - {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */ - {0xa0, 0x05, ZC3XX_R01D_HSYNC_0}, /* 00,1d,05,cc */ - {0xa0, 0x1a, ZC3XX_R01E_HSYNC_1}, /* 00,1e,1a,cc */ - {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */ - {} -}; -static const struct usb_action hdcs2020_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */ - {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ - {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ - {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ - {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */ - {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */ - {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, /* 00,1e,18,cc */ - {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */ - {} -}; -static const struct usb_action hdcs2020_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */ - {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ - {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ - {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ - {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */ - {0xa0, 0x17, ZC3XX_R01E_HSYNC_1}, /* 00,1e,17,cc */ - {0xa0, 0x2a, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2a,cc */ - {} -}; - -static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xaa, 0x30, 0x002d}, - {0xaa, 0x01, 0x0005}, - {0xaa, 0x11, 0x0000}, - {0xaa, 0x13, 0x0001}, /* {0xaa, 0x13, 0x0000}, */ - {0xaa, 0x14, 0x0001}, - {0xaa, 0x15, 0x00e8}, - {0xaa, 0x16, 0x0002}, - {0xaa, 0x17, 0x0086}, /* 00,17,88,aa */ - {0xaa, 0x31, 0x0038}, - {0xaa, 0x32, 0x0038}, - {0xaa, 0x33, 0x0038}, - {0xaa, 0x5b, 0x0001}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0xc0, 0x019b}, - {0xa0, 0xa0, 0x019c}, - {0xa0, 0x02, ZC3XX_R188_MINGAIN}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xaa, 0x02, 0x0090}, /* 00,02,80,aa */ - {} -}; - -static const struct usb_action hv7131b_Initial[] = { /* 640x480*/ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xaa, 0x30, 0x002d}, - {0xaa, 0x01, 0x0005}, - {0xaa, 0x11, 0x0001}, - {0xaa, 0x13, 0x0000}, /* {0xaa, 0x13, 0x0001}; */ - {0xaa, 0x14, 0x0001}, - {0xaa, 0x15, 0x00e6}, - {0xaa, 0x16, 0x0002}, - {0xaa, 0x17, 0x0086}, - {0xaa, 0x31, 0x0038}, - {0xaa, 0x32, 0x0038}, - {0xaa, 0x33, 0x0038}, - {0xaa, 0x5b, 0x0001}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0xc0, 0x019b}, - {0xa0, 0xa0, 0x019c}, - {0xa0, 0x02, ZC3XX_R188_MINGAIN}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */ - {} -}; -static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ - {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */ - {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ - {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */ - {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */ - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ - {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ - {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */ - {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ - {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */ - {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */ - {} -}; -static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ - {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */ - {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ - {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */ - {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */ - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ - {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ - {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ - {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ - {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */ - {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */ - {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ - {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ - {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */ - {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */ - {} -}; -static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ - {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */ - {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */ - {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */ - {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */ - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ - {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ - {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */ - {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ - {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ - {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */ - {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */ - {} -}; -static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ - {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */ - {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ - {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ - {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ - {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ - {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ - {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ - {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */ - {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */ - {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ - {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ - {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ - {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */ - {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */ - {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */ - {} -}; -static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ - {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ - {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */ - {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ - {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */ - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ - {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ - {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ - {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */ - {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */ - {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */ - {} -}; -static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ - {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ - {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ - {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ - {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ - {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ - {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ - {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ - {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ - {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */ - {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */ - {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */ - {} -}; - -/* from lPEPI264v.inf (hv7131b!) */ -static const struct usb_action hv7131r_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x000c}, - {0xaa, 0x11, 0x0000}, - {0xaa, 0x13, 0x0000}, - {0xaa, 0x14, 0x0001}, - {0xaa, 0x15, 0x00e8}, - {0xaa, 0x16, 0x0002}, - {0xaa, 0x17, 0x0088}, - {0xaa, 0x30, 0x000b}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, - {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0xc0, 0x019b}, - {0xa0, 0xa0, 0x019c}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {} -}; -static const struct usb_action hv7131r_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x000c}, - {0xaa, 0x11, 0x0000}, - {0xaa, 0x13, 0x0000}, - {0xaa, 0x14, 0x0001}, - {0xaa, 0x15, 0x00e6}, - {0xaa, 0x16, 0x0002}, - {0xaa, 0x17, 0x0086}, - {0xaa, 0x30, 0x000b}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, - {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0xc0, 0x019b}, - {0xa0, 0xa0, 0x019c}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {} -}; -static const struct usb_action hv7131r_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action hv7131r_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action hv7131r_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action hv7131r_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action hv7131r_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action hv7131r_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, - {} -}; - -static const struct usb_action icm105a_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH}, - {0xa0, 0x01, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH}, - {0xa0, 0x01, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x01, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x01, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xaa, 0x01, 0x0010}, - {0xaa, 0x03, 0x0000}, - {0xaa, 0x04, 0x0001}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0001}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0001}, - {0xaa, 0x04, 0x0011}, - {0xaa, 0x05, 0x00a0}, - {0xaa, 0x06, 0x0001}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0002}, - {0xaa, 0x04, 0x0013}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0001}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0003}, - {0xaa, 0x04, 0x0015}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0004}, - {0xaa, 0x04, 0x0017}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x000d}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0005}, - {0xaa, 0x04, 0x0019}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0006}, - {0xaa, 0x04, 0x0017}, - {0xaa, 0x05, 0x0026}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0007}, - {0xaa, 0x04, 0x0019}, - {0xaa, 0x05, 0x0022}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0008}, - {0xaa, 0x04, 0x0021}, - {0xaa, 0x05, 0x00aa}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0009}, - {0xaa, 0x04, 0x0023}, - {0xaa, 0x05, 0x00aa}, - {0xaa, 0x06, 0x000d}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x000a}, - {0xaa, 0x04, 0x0025}, - {0xaa, 0x05, 0x00aa}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x000b}, - {0xaa, 0x04, 0x00ec}, - {0xaa, 0x05, 0x002e}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x000c}, - {0xaa, 0x04, 0x00fa}, - {0xaa, 0x05, 0x002a}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x07, 0x000d}, - {0xaa, 0x01, 0x0005}, - {0xaa, 0x94, 0x0002}, - {0xaa, 0x90, 0x0000}, - {0xaa, 0x91, 0x001f}, - {0xaa, 0x10, 0x0064}, - {0xaa, 0x9b, 0x00f0}, - {0xaa, 0x9c, 0x0002}, - {0xaa, 0x14, 0x001a}, - {0xaa, 0x20, 0x0080}, - {0xaa, 0x22, 0x0080}, - {0xaa, 0x24, 0x0080}, - {0xaa, 0x26, 0x0080}, - {0xaa, 0x00, 0x0084}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xaa, 0xa8, 0x00c0}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0008}, - - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf7, ZC3XX_R10B_RGB01}, - {0xa0, 0xf7, ZC3XX_R10C_RGB02}, - {0xa0, 0xf7, ZC3XX_R10D_RGB10}, - {0xa0, 0x52, ZC3XX_R10E_RGB11}, - {0xa0, 0xf7, ZC3XX_R10F_RGB12}, - {0xa0, 0xf7, ZC3XX_R110_RGB20}, - {0xa0, 0xf7, ZC3XX_R111_RGB21}, - {0xa0, 0x52, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x0d, 0x0003}, - {0xaa, 0x0c, 0x008c}, - {0xaa, 0x0e, 0x0095}, - {0xaa, 0x0f, 0x0002}, - {0xaa, 0x1c, 0x0094}, - {0xaa, 0x1d, 0x0002}, - {0xaa, 0x20, 0x0080}, - {0xaa, 0x22, 0x0080}, - {0xaa, 0x24, 0x0080}, - {0xaa, 0x26, 0x0080}, - {0xaa, 0x00, 0x0084}, - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0xc0, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; - -static const struct usb_action icm105a_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH}, - {0xa0, 0x02, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH}, - {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x02, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xaa, 0x01, 0x0010}, - {0xaa, 0x03, 0x0000}, - {0xaa, 0x04, 0x0001}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0001}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0001}, - {0xaa, 0x04, 0x0011}, - {0xaa, 0x05, 0x00a0}, - {0xaa, 0x06, 0x0001}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0002}, - {0xaa, 0x04, 0x0013}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0001}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0003}, - {0xaa, 0x04, 0x0015}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0004}, - {0xaa, 0x04, 0x0017}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x000d}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0005}, - {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x19, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xaa, 0x05, 0x0020}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0006}, - {0xaa, 0x04, 0x0017}, - {0xaa, 0x05, 0x0026}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0007}, - {0xaa, 0x04, 0x0019}, - {0xaa, 0x05, 0x0022}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0008}, - {0xaa, 0x04, 0x0021}, - {0xaa, 0x05, 0x00aa}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x0009}, - {0xaa, 0x04, 0x0023}, - {0xaa, 0x05, 0x00aa}, - {0xaa, 0x06, 0x000d}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x000a}, - {0xaa, 0x04, 0x0025}, - {0xaa, 0x05, 0x00aa}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x000b}, - {0xaa, 0x04, 0x00ec}, - {0xaa, 0x05, 0x002e}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x03, 0x000c}, - {0xaa, 0x04, 0x00fa}, - {0xaa, 0x05, 0x002a}, - {0xaa, 0x06, 0x0005}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x07, 0x000d}, - {0xaa, 0x01, 0x0005}, - {0xaa, 0x94, 0x0002}, - {0xaa, 0x90, 0x0000}, - {0xaa, 0x91, 0x0010}, - {0xaa, 0x10, 0x0064}, - {0xaa, 0x9b, 0x00f0}, - {0xaa, 0x9c, 0x0002}, - {0xaa, 0x14, 0x001a}, - {0xaa, 0x20, 0x0080}, - {0xaa, 0x22, 0x0080}, - {0xaa, 0x24, 0x0080}, - {0xaa, 0x26, 0x0080}, - {0xaa, 0x00, 0x0084}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xaa, 0xa8, 0x0080}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0008}, - - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf7, ZC3XX_R10B_RGB01}, - {0xa0, 0xf7, ZC3XX_R10C_RGB02}, - {0xa0, 0xf7, ZC3XX_R10D_RGB10}, - {0xa0, 0x52, ZC3XX_R10E_RGB11}, - {0xa0, 0xf7, ZC3XX_R10F_RGB12}, - {0xa0, 0xf7, ZC3XX_R110_RGB20}, - {0xa0, 0xf7, ZC3XX_R111_RGB21}, - {0xa0, 0x52, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x0d, 0x0003}, - {0xaa, 0x0c, 0x0020}, - {0xaa, 0x0e, 0x000e}, - {0xaa, 0x0f, 0x0002}, - {0xaa, 0x1c, 0x000d}, - {0xaa, 0x1d, 0x0002}, - {0xaa, 0x20, 0x0080}, - {0xaa, 0x22, 0x0080}, - {0xaa, 0x24, 0x0080}, - {0xaa, 0x26, 0x0080}, - {0xaa, 0x00, 0x0084}, - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action icm105a_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ - {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */ - {0xaa, 0x0e, 0x000e}, /* 00,0e,0e,aa */ - {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ - {0xaa, 0x1c, 0x000d}, /* 00,1c,0d,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ - {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ - {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ - {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ - {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ - {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,0d,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,1a,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4b,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ - {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ - {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d8,cc */ - {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {} -}; -static const struct usb_action icm105a_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ - {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */ - {0xaa, 0x0e, 0x0095}, /* 00,0e,95,aa */ - {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ - {0xaa, 0x1c, 0x0094}, /* 00,1c,94,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ - {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ - {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ - {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ - {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ - {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,94,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,84,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ - {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e3,cc */ - {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, /* 00,1e,ec,cc */ - {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f5,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ - {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ - {} -}; -static const struct usb_action icm105a_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ - {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ - {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */ - {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ - {0xaa, 0x1c, 0x0008}, /* 00,1c,08,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ - {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ - {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ - {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ - {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ - {0xa0, 0x08, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,08,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x41, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,41,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ - {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ - {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ - {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {} -}; -static const struct usb_action icm105a_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ - {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ - {0xaa, 0x0e, 0x0086}, /* 00,0e,86,aa */ - {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ - {0xaa, 0x1c, 0x0085}, /* 00,1c,85,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ - {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ - {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ - {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ - {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ - {0xa0, 0x85, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,85,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,08,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ - {0xa0, 0xc2, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c2,cc */ - {0xa0, 0xd6, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d6,cc */ - {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ - {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ - {} -}; -static const struct usb_action icm105a_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ - {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ - {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */ - {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ - {0xaa, 0x1c, 0x0000}, /* 00,1c,00,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ - {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ - {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ - {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ - {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ - {0xa0, 0x00, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ - {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ - {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {} -}; -static const struct usb_action icm105a_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ - {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ - {0xaa, 0x0e, 0x0081}, /* 00,0e,81,aa */ - {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ - {0xaa, 0x1c, 0x0080}, /* 00,1c,80,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ - {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ - {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ - {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ - {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ - {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ - {0xa0, 0x80, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,80,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ - {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ - {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ - {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ - {} -}; - -static const struct usb_action mc501cb_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ - {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ - {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */ - {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */ - {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */ - {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ - {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */ - {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ - {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */ - {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ - {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ - {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ - {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */ - {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */ - {0xaa, 0x18, 0x00de}, /* 00,18,de,aa */ - {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ - {0xaa, 0x1a, 0x0086}, /* 00,1a,86,aa */ - {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */ - {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ - {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */ - {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */ - {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */ - {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */ - {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */ - {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */ - {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */ - {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */ - {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */ - {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */ - {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */ - {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */ - {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */ - {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */ - {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */ - {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ - {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ - {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ - {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */ - {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ - {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ - {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ - {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */ - {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */ - {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */ - {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */ - {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */ - {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */ - {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */ - {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */ - {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */ - {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */ - {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */ - {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */ - {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */ - {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */ - {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */ - {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */ - {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x1c, 0x0050}, /* 00,1C,50,aa */ - {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */ - {0xaa, 0x3b, 0x001d}, /* 00,3b,1D,aa */ - {0xaa, 0x3c, 0x004c}, /* 00,3c,4C,aa */ - {0xaa, 0x3d, 0x0018}, /* 00,3d,18,aa */ - {0xaa, 0x3e, 0x006a}, /* 00,3e,6A,aa */ - {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ - {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ - {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */ - {0xaa, 0x51, 0x0027}, /* 00,51,27,aa */ - {0xaa, 0x52, 0x0020}, /* 00,52,20,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */ - {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */ - {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */ - {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */ - {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */ - {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */ - - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ - {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ - {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ - {} -}; - -static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ - {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ - {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */ - {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */ - {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */ - {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ - {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */ - {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ - {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */ - {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ - {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ - {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ - {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */ - {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */ - {0xaa, 0x18, 0x00d8}, /* 00,18,d8,aa */ - {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ - {0xaa, 0x1a, 0x0088}, /* 00,1a,88,aa */ - {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */ - {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ - {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */ - {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */ - {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */ - {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */ - {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */ - {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */ - {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */ - {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */ - {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */ - {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */ - {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */ - {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */ - {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */ - {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */ - {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */ - {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ - {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ - {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ - {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */ - {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ - {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ - {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ - {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */ - {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */ - {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */ - {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */ - {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */ - {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */ - {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */ - {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */ - {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */ - {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */ - {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */ - {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */ - {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */ - {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */ - {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */ - {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */ - {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x1c, 0x0050}, /* 00,1c,50,aa */ - {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */ - {0xaa, 0x3b, 0x003a}, /* 00,3b,3A,aa */ - {0xaa, 0x3c, 0x0098}, /* 00,3c,98,aa */ - {0xaa, 0x3d, 0x0030}, /* 00,3d,30,aa */ - {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ - {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ - {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ - {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */ - {0xaa, 0x51, 0x004e}, /* 00,51,4E,aa */ - {0xaa, 0x52, 0x0041}, /* 00,52,41,aa */ - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */ - {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */ - {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */ - {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */ - {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */ - {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */ - {} -}; - -static const struct usb_action mc501cb_50HZ[] = { - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ - {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ - {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ - {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */ - {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */ - {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */ - {} -}; - -static const struct usb_action mc501cb_50HZScale[] = { - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ - {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */ - {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */ - {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */ - {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */ - {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */ - {} -}; - -static const struct usb_action mc501cb_60HZ[] = { - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ - {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ - {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ - {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ - {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ - {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ - {} -}; - -static const struct usb_action mc501cb_60HZScale[] = { - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ - {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ - {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ - {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ - {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ - {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ - {} -}; - -static const struct usb_action mc501cb_NoFliker[] = { - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ - {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ - {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ - {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ - {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ - {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ - {} -}; - -static const struct usb_action mc501cb_NoFlikerScale[] = { - {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ - {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ - {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ - {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ - {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ - {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ - {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ - {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ - {} -}; - -/* from zs211.inf */ -static const struct usb_action ov7620_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */ - {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */ - {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */ - {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */ - {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */ - {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */ - {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */ - {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ - {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */ - {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */ - {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ - {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ - {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ - {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ - {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ - {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */ - {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */ - {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ - {0xaa, 0x1a, 0x00f1}, /* 00,1a,f1,aa */ - {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */ - {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */ - {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */ - {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */ - {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */ - {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ - {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */ - {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ - {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ - {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */ - {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */ - {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */ - {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */ - {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */ - {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */ - {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */ - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */ - {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ - {0xa0, 0x40, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,40,cc */ - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ - {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ - {} -}; -static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ - /* mx change? */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */ - {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */ - {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */ - {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */ - {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */ - {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */ - {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xd6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d6,cc */ - /* OV7648 00,9c,d8,cc */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ - {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */ - {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */ - {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ - {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ - {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ - {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ - {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ - {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */ - {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */ - {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */ - {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */ - {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ - {0xaa, 0x1a, 0x00f2}, /* 00,1a,f2,aa */ - {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */ - {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */ - {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */ - {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ - {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */ - {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ - {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ - {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */ - {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */ - {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */ - {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */ - {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */ - {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */ - {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */ - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */ - {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,50,cc */ - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ - {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ - {} -}; -static const struct usb_action ov7620_50HZ[] = { - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ - {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ - {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ - {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ - {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ - {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */ - {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ -/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc - * if mode0 (640x480) */ - {} -}; -static const struct usb_action ov7620_60HZ[] = { - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ - /* (bug in zs211.inf) */ - {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ - {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ - {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ - {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ - {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */ - {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ -/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc - * if mode0 (640x480) */ -/* ?? in gspca v1, it was - {0xa0, 0x00, 0x0039}, * 00,00,00,dd * - {0xa1, 0x01, 0x0037}, */ - {} -}; -static const struct usb_action ov7620_NoFliker[] = { - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ - /* (bug in zs211.inf) */ - {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ - {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ - {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */ - {0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ - {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */ -/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc - * if mode1 (320x240) */ -/* ?? was - {0xa0, 0x00, 0x0039}, * 00,00,00,dd * - {0xa1, 0x01, 0x0037}, */ - {} -}; - -static const struct usb_action ov7630c_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x12, 0x0080}, - {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, - {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, - {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH}, - {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x12, 0x0069}, - {0xaa, 0x04, 0x0020}, - {0xaa, 0x06, 0x0050}, - {0xaa, 0x13, 0x0083}, - {0xaa, 0x14, 0x0000}, - {0xaa, 0x15, 0x0024}, - {0xaa, 0x17, 0x0018}, - {0xaa, 0x18, 0x00ba}, - {0xaa, 0x19, 0x0002}, - {0xaa, 0x1a, 0x00f6}, - {0xaa, 0x1b, 0x0002}, - {0xaa, 0x20, 0x00c2}, - {0xaa, 0x24, 0x0060}, - {0xaa, 0x25, 0x0040}, - {0xaa, 0x26, 0x0030}, - {0xaa, 0x27, 0x00ea}, - {0xaa, 0x28, 0x00a0}, - {0xaa, 0x21, 0x0000}, - {0xaa, 0x2a, 0x0081}, - {0xaa, 0x2b, 0x0096}, - {0xaa, 0x2d, 0x0094}, - {0xaa, 0x2f, 0x003d}, - {0xaa, 0x30, 0x0024}, - {0xaa, 0x60, 0x0000}, - {0xaa, 0x61, 0x0040}, - {0xaa, 0x68, 0x007c}, - {0xaa, 0x6f, 0x0015}, - {0xaa, 0x75, 0x0088}, - {0xaa, 0x77, 0x00b5}, - {0xaa, 0x01, 0x0060}, - {0xaa, 0x02, 0x0060}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x46, ZC3XX_R118_BGAIN}, - {0xa0, 0x04, ZC3XX_R113_RGB03}, -/* 0x10, */ - {0xa1, 0x01, 0x0002}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x01, ZC3XX_R120_GAMMA00}, /* gamma 2 ?*/ - {0xa0, 0x0c, ZC3XX_R121_GAMMA01}, - {0xa0, 0x1f, ZC3XX_R122_GAMMA02}, - {0xa0, 0x3a, ZC3XX_R123_GAMMA03}, - {0xa0, 0x53, ZC3XX_R124_GAMMA04}, - {0xa0, 0x6d, ZC3XX_R125_GAMMA05}, - {0xa0, 0x85, ZC3XX_R126_GAMMA06}, - {0xa0, 0x9c, ZC3XX_R127_GAMMA07}, - {0xa0, 0xb0, ZC3XX_R128_GAMMA08}, - {0xa0, 0xc2, ZC3XX_R129_GAMMA09}, - {0xa0, 0xd1, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xde, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xe9, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf2, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xf9, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x05, ZC3XX_R130_GAMMA10}, - {0xa0, 0x0f, ZC3XX_R131_GAMMA11}, - {0xa0, 0x16, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1a, ZC3XX_R133_GAMMA13}, - {0xa0, 0x19, ZC3XX_R134_GAMMA14}, - {0xa0, 0x19, ZC3XX_R135_GAMMA15}, - {0xa0, 0x17, ZC3XX_R136_GAMMA16}, - {0xa0, 0x15, ZC3XX_R137_GAMMA17}, - {0xa0, 0x12, ZC3XX_R138_GAMMA18}, - {0xa0, 0x10, ZC3XX_R139_GAMMA19}, - {0xa0, 0x0e, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x0b, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x09, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x08, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x06, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x03, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xaa, 0x10, 0x001b}, - {0xaa, 0x76, 0x0002}, - {0xaa, 0x2a, 0x0081}, - {0xaa, 0x2b, 0x0000}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xb8, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x37, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xaa, 0x13, 0x0083}, /* 40 */ - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; - -static const struct usb_action ov7630c_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - - {0xaa, 0x12, 0x0080}, - {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, - {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, - {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH}, - {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x12, 0x0069}, /* i2c */ - {0xaa, 0x04, 0x0020}, - {0xaa, 0x06, 0x0050}, - {0xaa, 0x13, 0x00c3}, - {0xaa, 0x14, 0x0000}, - {0xaa, 0x15, 0x0024}, - {0xaa, 0x19, 0x0003}, - {0xaa, 0x1a, 0x00f6}, - {0xaa, 0x1b, 0x0002}, - {0xaa, 0x20, 0x00c2}, - {0xaa, 0x24, 0x0060}, - {0xaa, 0x25, 0x0040}, - {0xaa, 0x26, 0x0030}, - {0xaa, 0x27, 0x00ea}, - {0xaa, 0x28, 0x00a0}, - {0xaa, 0x21, 0x0000}, - {0xaa, 0x2a, 0x0081}, - {0xaa, 0x2b, 0x0096}, - {0xaa, 0x2d, 0x0084}, - {0xaa, 0x2f, 0x003d}, - {0xaa, 0x30, 0x0024}, - {0xaa, 0x60, 0x0000}, - {0xaa, 0x61, 0x0040}, - {0xaa, 0x68, 0x007c}, - {0xaa, 0x6f, 0x0015}, - {0xaa, 0x75, 0x0088}, - {0xaa, 0x77, 0x00b5}, - {0xaa, 0x01, 0x0060}, - {0xaa, 0x02, 0x0060}, - {0xaa, 0x17, 0x0018}, - {0xaa, 0x18, 0x00ba}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x46, ZC3XX_R118_BGAIN}, - {0xa0, 0x04, ZC3XX_R113_RGB03}, - - {0xa1, 0x01, 0x0002}, - {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xfe, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf7, ZC3XX_R10D_RGB10}, - {0xa0, 0x4d, ZC3XX_R10E_RGB11}, - {0xa0, 0xfc, ZC3XX_R10F_RGB12}, - {0xa0, 0x00, ZC3XX_R110_RGB20}, - {0xa0, 0xf6, ZC3XX_R111_RGB21}, - {0xa0, 0x4a, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x16, ZC3XX_R120_GAMMA00}, /* gamma ~4 */ - {0xa0, 0x3a, ZC3XX_R121_GAMMA01}, - {0xa0, 0x5b, ZC3XX_R122_GAMMA02}, - {0xa0, 0x7c, ZC3XX_R123_GAMMA03}, - {0xa0, 0x94, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa9, ZC3XX_R125_GAMMA05}, - {0xa0, 0xbb, ZC3XX_R126_GAMMA06}, - {0xa0, 0xca, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd7, ZC3XX_R128_GAMMA08}, - {0xa0, 0xe1, ZC3XX_R129_GAMMA09}, - {0xa0, 0xea, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xf1, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf7, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xfc, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x20, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xfe, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf7, ZC3XX_R10D_RGB10}, - {0xa0, 0x4d, ZC3XX_R10E_RGB11}, - {0xa0, 0xfc, ZC3XX_R10F_RGB12}, - {0xa0, 0x00, ZC3XX_R110_RGB20}, - {0xa0, 0xf6, ZC3XX_R111_RGB21}, - {0xa0, 0x4a, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xaa, 0x10, 0x000d}, - {0xaa, 0x76, 0x0002}, - {0xaa, 0x2a, 0x0081}, - {0xaa, 0x2b, 0x0000}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xd8, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x1b, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xaa, 0x13, 0x00c3}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; - -static const struct usb_action pas106b_Initial_com[] = { -/* Sream and Sensor specific */ - {0xa1, 0x01, 0x0010}, /* CMOSSensorSelect */ -/* System */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */ -/* Picture size */ - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* ClockSelect */ - {0xa0, 0x03, 0x003a}, - {0xa0, 0x0c, 0x003b}, - {0xa0, 0x04, 0x0038}, - {} -}; - -static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */ -/* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, -/* Sream and Sensor specific */ - {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, -/* Picture size */ - {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW}, -/* System */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, -/* Sream and Sensor specific */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, -/* Sensor Interface */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, -/* Window inside sensor array */ - {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, -/* Init the sensor */ - {0xaa, 0x02, 0x0004}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x09, 0x0005}, - {0xaa, 0x0a, 0x0002}, - {0xaa, 0x0b, 0x0002}, - {0xaa, 0x0c, 0x0005}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x0e, 0x0002}, - {0xaa, 0x14, 0x0081}, -/* Other registers */ - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, -/* Frame retreiving */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, -/* Gains */ - {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, -/* Unknown */ - {0xa0, 0x00, 0x01ad}, -/* Sharpness */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, -/* Other registers */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, -/* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, -/*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, -/* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, -/* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, -/* Other registers */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, -/* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, -/*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, -/* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, -/* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, - - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, -/* Auto correction */ - {0xa0, 0x03, ZC3XX_R181_WINXSTART}, - {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, - {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, - {0xa0, 0x03, ZC3XX_R184_WINYSTART}, - {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, - {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, -/* Auto exposure and white balance */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, -/* sensor on */ - {0xaa, 0x07, 0x00b1}, - {0xaa, 0x05, 0x0003}, - {0xaa, 0x04, 0x0001}, - {0xaa, 0x03, 0x003b}, -/* Gains */ - {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, -/* Auto correction */ - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, -/* Gains */ - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; - -static const struct usb_action pas106b_Initial[] = { /* 352x288 */ -/* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, -/* Sream and Sensor specific */ - {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, -/* Picture size */ - {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW}, -/* System */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, -/* Sream and Sensor specific */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, -/* Sensor Interface */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, -/* Window inside sensor array */ - {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, -/* Init the sensor */ - {0xaa, 0x02, 0x0004}, - {0xaa, 0x08, 0x0000}, - {0xaa, 0x09, 0x0005}, - {0xaa, 0x0a, 0x0002}, - {0xaa, 0x0b, 0x0002}, - {0xaa, 0x0c, 0x0005}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x0e, 0x0002}, - {0xaa, 0x14, 0x0081}, -/* Other registers */ - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, -/* Frame retreiving */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, -/* Gains */ - {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, -/* Unknown */ - {0xa0, 0x00, 0x01ad}, -/* Sharpness */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, -/* Other registers */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, -/* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, -/*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, -/* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, -/* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, -/* Other registers */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, -/* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, -/*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, -/* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, -/* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, - - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, -/* Auto correction */ - {0xa0, 0x03, ZC3XX_R181_WINXSTART}, - {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, - {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, - {0xa0, 0x03, ZC3XX_R184_WINYSTART}, - {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, - {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - -/* Auto exposure and white balance */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, - - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, - - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, -/* sensor on */ - {0xaa, 0x07, 0x00b1}, - {0xaa, 0x05, 0x0003}, - {0xaa, 0x04, 0x0001}, - {0xaa, 0x03, 0x003b}, -/* Gains */ - {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, -/* Auto correction */ - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, -/* Gains */ - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - - {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */ - {0xa0, 0xff, ZC3XX_R018_FRAMELOST}, /* Frame adjust */ - {} -}; -static const struct usb_action pas106b_50HZ[] = { - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ - {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,54,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,87,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */ - {0xaa, 0x03, 0x0021}, /* 00,03,21,aa */ - {0xaa, 0x04, 0x000c}, /* 00,04,0c,aa */ - {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */ - {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */ - {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */ - {} -}; -static const struct usb_action pas106b_60HZ[] = { - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ - {0xa0, 0x2e, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,2e,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x71, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,71,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */ - {0xaa, 0x03, 0x001c}, /* 00,03,1c,aa */ - {0xaa, 0x04, 0x0004}, /* 00,04,04,aa */ - {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ - {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */ - {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */ - {} -}; -static const struct usb_action pas106b_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ - {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xaa, 0x03, 0x0013}, /* 00,03,13,aa */ - {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ - {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ - {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {} -}; - -/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */ -static const struct usb_action pas202b_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */ - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ - {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */ - {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ - {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ - {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ - {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ - {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ - {0xaa, 0x0c, 0x0006}, - {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ - {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ - {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ - {} -}; -static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ - {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */ - {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */ - {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */ - {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */ - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ - {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */ - {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ - {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ - {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ - {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ - {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ - {0xaa, 0x0c, 0x0006}, - {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ - {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ - {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ - {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ - {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ - {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH}, - {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW}, - {} -}; -static const struct usb_action pas202b_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x001b}, - {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */ - {0xaa, 0x04, 0x0008}, - {0xaa, 0x05, 0x001b}, - {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ - {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */ - {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ - {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ - {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */ - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ - {} -}; -static const struct usb_action pas202b_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0004}, - {0xaa, 0x21, 0x003d}, - {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */ - {0xaa, 0x04, 0x0010}, - {0xaa, 0x05, 0x003d}, - {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ - {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ - {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ - {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ - {} -}; -static const struct usb_action pas202b_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ - {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */ - {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ - {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ - {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ - {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */ - {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */ - {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */ - {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */ - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ - {} -}; -static const struct usb_action pas202b_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0004}, - {0xaa, 0x21, 0x0008}, - {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */ - {0xaa, 0x04, 0x0010}, - {0xaa, 0x05, 0x0008}, - {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ - {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */ - {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ - {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ - {} -}; -static const struct usb_action pas202b_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x0006}, - {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ - {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ - {0xaa, 0x05, 0x0006}, - {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ - {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ - {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ - {} -}; -static const struct usb_action pas202b_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0004}, - {0xaa, 0x21, 0x000c}, - {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ - {0xaa, 0x04, 0x0010}, - {0xaa, 0x05, 0x000c}, - {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ - {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ - {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ - {} -}; - -/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */ -static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x0001}, - {0xaa, 0x06, 0x0000}, - {0xaa, 0x08, 0x0483}, - {0xaa, 0x01, 0x0004}, - {0xaa, 0x08, 0x0006}, - {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/ - {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ - {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x5100}, - {0xaa, 0x35, 0x507f}, - {0xaa, 0x30, 0x0005}, - {0xaa, 0x31, 0x0000}, - {0xaa, 0x58, 0x0078}, - {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x007f}, - {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/ - {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/ - {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/ - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, - {0xa0, 0x65, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x0001}, - {0xaa, 0x06, 0x0000}, - {0xaa, 0x08, 0x0483}, - {0xaa, 0x01, 0x0004}, - {0xaa, 0x08, 0x0006}, - {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, - {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x5100}, - {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/ - {0xaa, 0x30, 0x0005}, - {0xaa, 0x31, 0x0000}, - {0xaa, 0x58, 0x0078}, - {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/ - {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/ - {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/ - {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/ - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, - {0xa0, 0x65, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action mt9v111_1_AE50HZ[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0562}, - {0xbb, 0x01, 0x09aa}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_1_AE50HZScale[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0509}, - {0xbb, 0x01, 0x0934}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_1_AE60HZ[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x003d}, - {0xaa, 0x09, 0x016e}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_1_AE60HZScale[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0509}, - {0xbb, 0x01, 0x0983}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_1_AENoFliker[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0509}, - {0xbb, 0x01, 0x0960}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_1_AENoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0534}, - {0xbb, 0x02, 0x0960}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */ -static const struct usb_action mt9v111_3_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */ - {0xaa, 0x06, 0x0000}, /* operating mode control */ - {0xaa, 0x08, 0x0483}, /* output format control */ - /* H red first, V red or blue first, - * raw Bayer, auto flicker */ - {0xaa, 0x01, 0x0004}, /* select sensor core registers */ - {0xaa, 0x08, 0x0006}, /* row start */ - {0xaa, 0x02, 0x0011}, /* column start */ - {0xaa, 0x03, 0x01e5}, /* window height - 1 */ - {0xaa, 0x04, 0x0285}, /* window width - 1 */ - {0xaa, 0x07, 0x3002}, /* output control */ - {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */ - {0xaa, 0x35, 0x007f}, /* global gain */ - {0xaa, 0x30, 0x0005}, - {0xaa, 0x31, 0x0000}, - {0xaa, 0x58, 0x0078}, - {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x007f}, /* green1 gain */ - {0xaa, 0x2c, 0x007f}, /* blue gain */ - {0xaa, 0x2d, 0x007f}, /* red gain */ - {0xaa, 0x2e, 0x007f}, /* green2 gain */ - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, - {0xa0, 0x65, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action mt9v111_3_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x0001}, - {0xaa, 0x06, 0x0000}, - {0xaa, 0x08, 0x0483}, - {0xaa, 0x01, 0x0004}, - {0xaa, 0x08, 0x0006}, - {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, - {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x35, 0x007f}, - {0xaa, 0x30, 0x0005}, - {0xaa, 0x31, 0x0000}, - {0xaa, 0x58, 0x0078}, - {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x007f}, - {0xaa, 0x2c, 0x007f}, - {0xaa, 0x2d, 0x007f}, - {0xaa, 0x2e, 0x007f}, - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, - {0xa0, 0x65, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action mt9v111_3_AE50HZ[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, /* horizontal blanking */ - {0xaa, 0x09, 0x01ce}, /* shutter width */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_3_AE50HZScale[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x01ce}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_3_AE60HZ[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0083}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_3_AE60HZScale[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0083}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_3_AENoFliker[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0034}, - {0xaa, 0x09, 0x0260}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; -static const struct usb_action mt9v111_3_AENoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0034}, - {0xaa, 0x09, 0x0260}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; - -static const struct usb_action pb0330_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x0006}, - {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/ - {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ - {0xaa, 0x06, 0x0003}, - {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x2f, 0xf7b0}, - {0xaa, 0x30, 0x0005}, - {0xaa, 0x31, 0x0000}, - {0xaa, 0x34, 0x0100}, - {0xaa, 0x35, 0x0060}, - {0xaa, 0x3d, 0x068f}, - {0xaa, 0x40, 0x01e0}, - {0xaa, 0x58, 0x0078}, - {0xaa, 0x62, 0x0411}, - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */ - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ - {} -}; -static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xdd, 0x00, 0x0200}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xaa, 0x01, 0x0006}, - {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, - {0xaa, 0x06, 0x0003}, - {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x2f, 0xf7b0}, - {0xaa, 0x30, 0x0005}, - {0xaa, 0x31, 0x0000}, - {0xaa, 0x34, 0x0100}, - {0xaa, 0x35, 0x0060}, - {0xaa, 0x3d, 0x068f}, - {0xaa, 0x40, 0x01e0}, - {0xaa, 0x58, 0x0078}, - {0xaa, 0x62, 0x0411}, - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ - {} -}; -static const struct usb_action pb0330_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x055c}, - {0xbb, 0x01, 0x09aa}, - {0xbb, 0x00, 0x1001}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action pb0330_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0566}, - {0xbb, 0x02, 0x09b2}, - {0xbb, 0x00, 0x1002}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action pb0330_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0535}, - {0xbb, 0x01, 0x0974}, - {0xbb, 0x00, 0x1001}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action pb0330_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0535}, - {0xbb, 0x02, 0x096c}, - {0xbb, 0x00, 0x1002}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action pb0330_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0509}, - {0xbb, 0x02, 0x0940}, - {0xbb, 0x00, 0x1002}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, - {} -}; -static const struct usb_action pb0330_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x0535}, - {0xbb, 0x01, 0x0980}, - {0xbb, 0x00, 0x1001}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, - {} -}; - -/* from oem9.inf */ -static const struct usb_action po2030_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */ - {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */ - {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */ - {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */ - {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */ - {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */ - {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */ - {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ - {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ - {0xaa, 0x09, 0x00ce}, /* 00,09,ce,aa */ - {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */ - {0xaa, 0x0d, 0x0054}, /* 00,0d,54,aa */ - {0xaa, 0x0f, 0x00eb}, /* 00,0f,eb,aa */ - {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */ - {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */ - {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */ - {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */ - {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */ - {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */ - {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */ - {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */ - {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */ - {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */ - {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */ - {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */ - {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */ - {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */ - {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */ - {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */ - {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ - {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */ - {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */ - {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */ - {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */ - {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */ - {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */ - {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */ - {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */ - {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */ - {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */ - {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */ - {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */ - {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */ - {} -}; - -/* from oem9.inf */ -static const struct usb_action po2030_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ - {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */ - {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */ - {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */ - {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */ - {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */ - {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */ - {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */ - {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ - {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ - {0xaa, 0x09, 0x00cc}, /* 00,09,cc,aa */ - {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */ - {0xaa, 0x0d, 0x0058}, /* 00,0d,58,aa */ - {0xaa, 0x0f, 0x00ed}, /* 00,0f,ed,aa */ - {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */ - {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */ - {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */ - {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */ - {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */ - {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */ - {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */ - {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ - {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */ - {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */ - {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */ - {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */ - {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */ - {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */ - {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */ - {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */ - {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */ - {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */ - {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ - {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */ - {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */ - {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */ - {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */ - {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */ - {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */ - {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */ - {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */ - {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */ - {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */ - {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */ - {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ - {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ - {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */ - {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */ - {} -}; - -static const struct usb_action po2030_50HZ[] = { - {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ - {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */ - {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */ - {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */ - {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */ - {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */ - {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */ - {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */ - {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ - {} -}; - -static const struct usb_action po2030_60HZ[] = { - {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ - {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ - {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */ - {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */ - {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */ - {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */ - {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */ - {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */ - {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */ - /* win: 01,8d,80 */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ - {} -}; - -static const struct usb_action po2030_NoFliker[] = { - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ - {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */ - {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ - {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */ - {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */ - {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */ - {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ - {} -}; - -static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - - {0xa0, 0x04, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x04, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN}, - {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, - {} -}; -static const struct usb_action tas5130c_Initial[] = { /* 640x480 */ - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x05, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x05, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN}, - {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, - {} -}; -static const struct usb_action tas5130c_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ - {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */ - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ - {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */ - {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */ - {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ - {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {} -}; -static const struct usb_action tas5130c_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ - {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ - {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */ - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */ - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ - {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {} -}; -static const struct usb_action tas5130c_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ - {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */ - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ - {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */ - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ - {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ - {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {} -}; -static const struct usb_action tas5130c_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ - {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ - {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ - {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ - {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {} -}; -static const struct usb_action tas5130c_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ - {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */ - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ - {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ - {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ - {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {} -}; - -static const struct usb_action tas5130c_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ - {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */ - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ - {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ - {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ - {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ - {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {} -}; - -/* from usbvm305.inf 0ac8:305b 07/06/15 (3 - tas5130c) */ -static const struct usb_action gc0303_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ - {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */ - {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */ - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc, - * 6<->8 */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc, - * 6<->8 */ - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */ - {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ - {0xaa, 0x01, 0x0000}, - {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ - {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ - {0xaa, 0x1b, 0x0000}, - {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */ - {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */ - {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */ - {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */ - {0xaa, 0x0a, 0x0002}, - {0xaa, 0x0b, 0x0000}, - {0xaa, 0x0c, 0x0002}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x0e, 0x0002}, - {0xaa, 0x0f, 0x0000}, - {0xaa, 0x10, 0x0002}, - {0xaa, 0x11, 0x0000}, - {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ - {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa, (e6 -> e8) */ - {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ - {0xaa, 0x19, 0x0088}, /* 00,19,86,aa, */ - {0xaa, 0x20, 0x0020}, /* 00,20,20,aa, */ - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */ - {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */ - {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */ - {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */ - {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */ - {0xaa, 0x1b, 0x0000}, - {} -}; - -static const struct usb_action gc0303_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ - {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ - {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */ - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */ - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */ - {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */ - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */ - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */ - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */ - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc, - * 8<->6 */ - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc, - * 8<->6 */ - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */ - {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ - {0xaa, 0x01, 0x0000}, - {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ - {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ - {0xaa, 0x1b, 0x0000}, - {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */ - {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */ - {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */ - {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */ - {0xaa, 0x0a, 0x0001}, - {0xaa, 0x0b, 0x0000}, - {0xaa, 0x0c, 0x0001}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x0e, 0x0001}, - {0xaa, 0x0f, 0x0000}, - {0xaa, 0x10, 0x0001}, - {0xaa, 0x11, 0x0000}, - {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ - {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */ - {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ - {0xaa, 0x19, 0x0088}, /* 00,19,88,aa, */ - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */ - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */ - {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */ - {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */ - {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */ - {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */ - {0xaa, 0x1b, 0x0000}, - {} -}; -static const struct usb_action gc0303_50HZ[] = { - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */ - {0xaa, 0x84, 0x0063}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ - {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */ - {0xa0, 0xa8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc, */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ - {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ - {0xa0, 0x7f, ZC3XX_R18D_YTARGET}, - {} -}; - -static const struct usb_action gc0303_50HZScale[] = { - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */ - {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ - {0xa0, 0x0d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */ - {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ - {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc, */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ - {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ - {0xa0, 0x7f, ZC3XX_R18D_YTARGET}, - {} -}; - -static const struct usb_action gc0303_60HZ[] = { - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0000}, - {0xaa, 0x84, 0x003b}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ - {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,05,cc, */ - {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,88,cc, */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ - {0xa0, 0x3b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3b,cc, */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, - {} -}; - -static const struct usb_action gc0303_60HZScale[] = { - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0000}, - {0xaa, 0x84, 0x0076}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ - {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,1,0b,cc, */ - {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,2,10,cc, */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,5,00,cc, */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,6,00,cc, */ - {0xa0, 0x76, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,7,76,cc, */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,c,0e,cc, */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,f,15,cc, */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,9,10,cc, */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,a,24,cc, */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,d,62,cc, */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,e,90,cc, */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,f,c8,cc, */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,0,ff,cc, */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,d,58,cc, */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, - {} -}; - -static const struct usb_action gc0303_NoFliker[] = { - {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ - {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,0,00,cc, */ - {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ - {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ - {} -}; - -static const struct usb_action gc0303_NoFlikerScale[] = { - {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ - {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ - {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ - {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ - {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */ - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ - {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ - {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ - {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ - {} -}; - -static u8 reg_r(struct gspca_dev *gspca_dev, - u16 index) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return 0; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0xa1, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x01, /* value */ - index, gspca_dev->usb_buf, 1, - 500); - if (ret < 0) { - pr_err("reg_r err %d\n", ret); - gspca_dev->usb_err = ret; - return 0; - } - return gspca_dev->usb_buf[0]; -} - -static void reg_w(struct gspca_dev *gspca_dev, - u8 value, - u16 index) -{ - int ret; - - if (gspca_dev->usb_err < 0) - return; - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0xa0, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, - 500); - if (ret < 0) { - pr_err("reg_w_i err %d\n", ret); - gspca_dev->usb_err = ret; - } -} - -static u16 i2c_read(struct gspca_dev *gspca_dev, - u8 reg) -{ - u8 retbyte; - u16 retval; - - if (gspca_dev->usb_err < 0) - return 0; - reg_w(gspca_dev, reg, 0x0092); - reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */ - msleep(20); - retbyte = reg_r(gspca_dev, 0x0091); /* read status */ - if (retbyte != 0x00) - pr_err("i2c_r status error %02x\n", retbyte); - retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */ - retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */ - return retval; -} - -static u8 i2c_write(struct gspca_dev *gspca_dev, - u8 reg, - u8 valL, - u8 valH) -{ - u8 retbyte; - - if (gspca_dev->usb_err < 0) - return 0; - reg_w(gspca_dev, reg, 0x92); - reg_w(gspca_dev, valL, 0x93); - reg_w(gspca_dev, valH, 0x94); - reg_w(gspca_dev, 0x01, 0x90); /* <- write command */ - msleep(1); - retbyte = reg_r(gspca_dev, 0x0091); /* read status */ - if (retbyte != 0x00) - pr_err("i2c_w status error %02x\n", retbyte); - return retbyte; -} - -static void usb_exchange(struct gspca_dev *gspca_dev, - const struct usb_action *action) -{ - while (action->req) { - switch (action->req) { - case 0xa0: /* write register */ - reg_w(gspca_dev, action->val, action->idx); - break; - case 0xa1: /* read status */ - reg_r(gspca_dev, action->idx); - break; - case 0xaa: - i2c_write(gspca_dev, - action->val, /* reg */ - action->idx & 0xff, /* valL */ - action->idx >> 8); /* valH */ - break; - case 0xbb: - i2c_write(gspca_dev, - action->idx >> 8, /* reg */ - action->idx & 0xff, /* valL */ - action->val); /* valH */ - break; - default: -/* case 0xdd: * delay */ - msleep(action->idx); - break; - } - action++; - msleep(1); - } -} - -static void setmatrix(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - const u8 *matrix; - static const u8 adcm2700_matrix[9] = -/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */ -/*ms-win*/ - {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74}; - static const u8 gc0305_matrix[9] = - {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50}; - static const u8 ov7620_matrix[9] = - {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58}; - static const u8 pas202b_matrix[9] = - {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; - static const u8 po2030_matrix[9] = - {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; - static const u8 tas5130c_matrix[9] = - {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68}; - static const u8 gc0303_matrix[9] = - {0x6c, 0xea, 0xea, 0xea, 0x6c, 0xea, 0xea, 0xea, 0x6c}; - static const u8 *matrix_tb[SENSOR_MAX] = { - [SENSOR_ADCM2700] = adcm2700_matrix, - [SENSOR_CS2102] = ov7620_matrix, - [SENSOR_CS2102K] = NULL, - [SENSOR_GC0303] = gc0303_matrix, - [SENSOR_GC0305] = gc0305_matrix, - [SENSOR_HDCS2020] = NULL, - [SENSOR_HV7131B] = NULL, - [SENSOR_HV7131R] = po2030_matrix, - [SENSOR_ICM105A] = po2030_matrix, - [SENSOR_MC501CB] = NULL, - [SENSOR_MT9V111_1] = gc0305_matrix, - [SENSOR_MT9V111_3] = gc0305_matrix, - [SENSOR_OV7620] = ov7620_matrix, - [SENSOR_OV7630C] = NULL, - [SENSOR_PAS106] = NULL, - [SENSOR_PAS202B] = pas202b_matrix, - [SENSOR_PB0330] = gc0305_matrix, - [SENSOR_PO2030] = po2030_matrix, - [SENSOR_TAS5130C] = tas5130c_matrix, - }; - - matrix = matrix_tb[sd->sensor]; - if (matrix == NULL) - return; /* matrix already loaded */ - for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++) - reg_w(gspca_dev, matrix[i], 0x010a + i); -} - -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) -{ - static const u8 sharpness_tb[][2] = { - {0x02, 0x03}, - {0x04, 0x07}, - {0x08, 0x0f}, - {0x10, 0x1e} - }; - - reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6); - reg_r(gspca_dev, 0x01c8); - reg_r(gspca_dev, 0x01c9); - reg_r(gspca_dev, 0x01ca); - reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb); -} - -static void setcontrast(struct gspca_dev *gspca_dev, - s32 gamma, s32 brightness, s32 contrast) -{ - const u8 *Tgamma; - int g, i, adj, gp1, gp2; - u8 gr[16]; - static const u8 delta_b[16] = /* delta for brightness */ - {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d, - 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18}; - static const u8 delta_c[16] = /* delta for contrast */ - {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06, - 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02}; - static const u8 gamma_tb[6][16] = { - {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f, - 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff}, - {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c, - 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff}, - {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac, - 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff}, - {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff}, - {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2, - 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff}, - {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3, - 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}, - }; - - Tgamma = gamma_tb[gamma - 1]; - - contrast -= 128; /* -128 / 127 */ - brightness -= 128; /* -128 / 92 */ - adj = 0; - gp1 = gp2 = 0; - for (i = 0; i < 16; i++) { - g = Tgamma[i] + delta_b[i] * brightness / 256 - - delta_c[i] * contrast / 256 - adj / 2; - if (g > 0xff) - g = 0xff; - else if (g < 0) - g = 0; - reg_w(gspca_dev, g, 0x0120 + i); /* gamma */ - if (contrast > 0) - adj--; - else if (contrast < 0) - adj++; - if (i > 1) - gr[i - 1] = (g - gp2) / 2; - else if (i != 0) - gr[0] = gp1 == 0 ? 0 : (g - gp1); - gp2 = gp1; - gp1 = g; - } - gr[15] = (0xff - gp2) / 2; - for (i = 0; i < 16; i++) - reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */ -} - -static s32 getexposure(struct gspca_dev *gspca_dev) -{ - return (i2c_read(gspca_dev, 0x25) << 9) - | (i2c_read(gspca_dev, 0x26) << 1) - | (i2c_read(gspca_dev, 0x27) >> 7); -} - -static void setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - i2c_write(gspca_dev, 0x25, val >> 9, 0x00); - i2c_write(gspca_dev, 0x26, val >> 1, 0x00); - i2c_write(gspca_dev, 0x27, val << 7, 0x00); -} - -static void setquality(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]); - reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); -} - -/* Matches the sensor's internal frame rate to the lighting frequency. - * Valid frequencies are: - * 50Hz, for European and Asian lighting (default) - * 60Hz, for American lighting - * 0 = No Fliker (for outdoore usage) - */ -static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, mode; - const struct usb_action *zc3_freq; - static const struct usb_action *freq_tb[SENSOR_MAX][6] = { - [SENSOR_ADCM2700] = - {adcm2700_NoFliker, adcm2700_NoFliker, - adcm2700_50HZ, adcm2700_50HZ, - adcm2700_60HZ, adcm2700_60HZ}, - [SENSOR_CS2102] = - {cs2102_NoFliker, cs2102_NoFlikerScale, - cs2102_50HZ, cs2102_50HZScale, - cs2102_60HZ, cs2102_60HZScale}, - [SENSOR_CS2102K] = - {cs2102_NoFliker, cs2102_NoFlikerScale, - NULL, NULL, /* currently disabled */ - NULL, NULL}, - [SENSOR_GC0303] = - {gc0303_NoFliker, gc0303_NoFlikerScale, - gc0303_50HZ, gc0303_50HZScale, - gc0303_60HZ, gc0303_60HZScale}, - [SENSOR_GC0305] = - {gc0305_NoFliker, gc0305_NoFliker, - gc0305_50HZ, gc0305_50HZ, - gc0305_60HZ, gc0305_60HZ}, - [SENSOR_HDCS2020] = - {hdcs2020_NoFliker, hdcs2020_NoFliker, - hdcs2020_50HZ, hdcs2020_50HZ, - hdcs2020_60HZ, hdcs2020_60HZ}, - [SENSOR_HV7131B] = - {hv7131b_NoFliker, hv7131b_NoFlikerScale, - hv7131b_50HZ, hv7131b_50HZScale, - hv7131b_60HZ, hv7131b_60HZScale}, - [SENSOR_HV7131R] = - {hv7131r_NoFliker, hv7131r_NoFlikerScale, - hv7131r_50HZ, hv7131r_50HZScale, - hv7131r_60HZ, hv7131r_60HZScale}, - [SENSOR_ICM105A] = - {icm105a_NoFliker, icm105a_NoFlikerScale, - icm105a_50HZ, icm105a_50HZScale, - icm105a_60HZ, icm105a_60HZScale}, - [SENSOR_MC501CB] = - {mc501cb_NoFliker, mc501cb_NoFlikerScale, - mc501cb_50HZ, mc501cb_50HZScale, - mc501cb_60HZ, mc501cb_60HZScale}, - [SENSOR_MT9V111_1] = - {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale, - mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale, - mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale}, - [SENSOR_MT9V111_3] = - {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale, - mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale, - mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale}, - [SENSOR_OV7620] = - {ov7620_NoFliker, ov7620_NoFliker, - ov7620_50HZ, ov7620_50HZ, - ov7620_60HZ, ov7620_60HZ}, - [SENSOR_OV7630C] = - {NULL, NULL, - NULL, NULL, - NULL, NULL}, - [SENSOR_PAS106] = - {pas106b_NoFliker, pas106b_NoFliker, - pas106b_50HZ, pas106b_50HZ, - pas106b_60HZ, pas106b_60HZ}, - [SENSOR_PAS202B] = - {pas202b_NoFliker, pas202b_NoFlikerScale, - pas202b_50HZ, pas202b_50HZScale, - pas202b_60HZ, pas202b_60HZScale}, - [SENSOR_PB0330] = - {pb0330_NoFliker, pb0330_NoFlikerScale, - pb0330_50HZ, pb0330_50HZScale, - pb0330_60HZ, pb0330_60HZScale}, - [SENSOR_PO2030] = - {po2030_NoFliker, po2030_NoFliker, - po2030_50HZ, po2030_50HZ, - po2030_60HZ, po2030_60HZ}, - [SENSOR_TAS5130C] = - {tas5130c_NoFliker, tas5130c_NoFlikerScale, - tas5130c_50HZ, tas5130c_50HZScale, - tas5130c_60HZ, tas5130c_60HZScale}, - }; - - i = val * 2; - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - if (mode) - i++; /* 320x240 */ - zc3_freq = freq_tb[sd->sensor][i]; - if (zc3_freq == NULL) - return; - usb_exchange(gspca_dev, zc3_freq); - switch (sd->sensor) { - case SENSOR_GC0305: - if (mode /* if 320x240 */ - && val == 1) /* and 50Hz */ - reg_w(gspca_dev, 0x85, 0x018d); - /* win: 0x80, 0x018d */ - break; - case SENSOR_OV7620: - if (!mode) { /* if 640x480 */ - if (val != 0) /* and filter */ - reg_w(gspca_dev, 0x40, 0x0002); - else - reg_w(gspca_dev, 0x44, 0x0002); - } - break; - case SENSOR_PAS202B: - reg_w(gspca_dev, 0x00, 0x01a7); - break; - } -} - -static void setautogain(struct gspca_dev *gspca_dev, s32 val) -{ - reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180); -} - -/* - * Update the transfer parameters. - * This function is executed from a work queue. - */ -static void transfer_update(struct work_struct *work) -{ - struct sd *sd = container_of(work, struct sd, work); - struct gspca_dev *gspca_dev = &sd->gspca_dev; - int change, good; - u8 reg07, reg11; - - /* reg07 gets set to 0 by sd_start before starting us */ - reg07 = 0; - - good = 0; - for (;;) { - msleep(100); - - mutex_lock(&gspca_dev->usb_lock); -#ifdef CONFIG_PM - if (gspca_dev->frozen) - goto err; -#endif - if (!gspca_dev->dev || !gspca_dev->streaming) - goto err; - - /* Bit 0 of register 11 indicates FIFO overflow */ - gspca_dev->usb_err = 0; - reg11 = reg_r(gspca_dev, 0x0011); - if (gspca_dev->usb_err) - goto err; - - change = reg11 & 0x01; - if (change) { /* overflow */ - good = 0; - - if (reg07 == 0) /* Bit Rate Control not enabled? */ - reg07 = 0x32; /* Allow 98 bytes / unit */ - else if (reg07 > 2) - reg07 -= 2; /* Decrease allowed bytes / unit */ - else - change = 0; - } else { /* no overflow */ - good++; - if (good >= 10) { - good = 0; - if (reg07) { /* BRC enabled? */ - change = 1; - if (reg07 < 0x32) - reg07 += 2; - else - reg07 = 0; - } - } - } - if (change) { - gspca_dev->usb_err = 0; - reg_w(gspca_dev, reg07, 0x0007); - if (gspca_dev->usb_err) - goto err; - } - mutex_unlock(&gspca_dev->usb_lock); - } - return; -err: - mutex_unlock(&gspca_dev->usb_lock); -} - -static void send_unknown(struct gspca_dev *gspca_dev, int sensor) -{ - reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */ - switch (sensor) { - case SENSOR_PAS106: - reg_w(gspca_dev, 0x03, 0x003a); - reg_w(gspca_dev, 0x0c, 0x003b); - reg_w(gspca_dev, 0x08, 0x0038); - break; - case SENSOR_ADCM2700: - case SENSOR_GC0305: - case SENSOR_OV7620: - case SENSOR_MT9V111_1: - case SENSOR_MT9V111_3: - case SENSOR_PB0330: - case SENSOR_PO2030: - reg_w(gspca_dev, 0x0d, 0x003a); - reg_w(gspca_dev, 0x02, 0x003b); - reg_w(gspca_dev, 0x00, 0x0038); - break; - case SENSOR_HV7131R: - case SENSOR_PAS202B: - reg_w(gspca_dev, 0x03, 0x003b); - reg_w(gspca_dev, 0x0c, 0x003a); - reg_w(gspca_dev, 0x0b, 0x0039); - if (sensor == SENSOR_PAS202B) - reg_w(gspca_dev, 0x0b, 0x0038); - break; - } -} - -/* start probe 2 wires */ -static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor) -{ - reg_w(gspca_dev, 0x01, 0x0000); - reg_w(gspca_dev, sensor, 0x0010); - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0x03, 0x0012); - reg_w(gspca_dev, 0x01, 0x0012); -/* msleep(2); */ -} - -static int sif_probe(struct gspca_dev *gspca_dev) -{ - u16 checkword; - - start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */ - reg_w(gspca_dev, 0x08, 0x008d); - msleep(150); - checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4) - | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4); - PDEBUG(D_PROBE, "probe sif 0x%04x", checkword); - if (checkword == 0x0007) { - send_unknown(gspca_dev, SENSOR_PAS106); - return 0x0f; /* PAS106 */ - } - return -1; -} - -static int vga_2wr_probe(struct gspca_dev *gspca_dev) -{ - u16 retword; - - start_2wr_probe(gspca_dev, 0x00); /* HV7131B */ - i2c_write(gspca_dev, 0x01, 0xaa, 0x00); - retword = i2c_read(gspca_dev, 0x01); - if (retword != 0) - return 0x00; /* HV7131B */ - - start_2wr_probe(gspca_dev, 0x04); /* CS2102 */ - i2c_write(gspca_dev, 0x01, 0xaa, 0x00); - retword = i2c_read(gspca_dev, 0x01); - if (retword != 0) - return 0x04; /* CS2102 */ - - start_2wr_probe(gspca_dev, 0x06); /* OmniVision */ - reg_w(gspca_dev, 0x08, 0x008d); - i2c_write(gspca_dev, 0x11, 0xaa, 0x00); - retword = i2c_read(gspca_dev, 0x11); - if (retword != 0) { - /* (should have returned 0xaa) --> Omnivision? */ - /* reg_r 0x10 -> 0x06 --> */ - goto ov_check; - } - - start_2wr_probe(gspca_dev, 0x08); /* HDCS2020 */ - i2c_write(gspca_dev, 0x1c, 0x00, 0x00); - i2c_write(gspca_dev, 0x15, 0xaa, 0x00); - retword = i2c_read(gspca_dev, 0x15); - if (retword != 0) - return 0x08; /* HDCS2020 */ - - start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */ - i2c_write(gspca_dev, 0x07, 0xaa, 0xaa); - retword = i2c_read(gspca_dev, 0x07); - if (retword != 0) - return 0x0a; /* PB0330 */ - retword = i2c_read(gspca_dev, 0x03); - if (retword != 0) - return 0x0a; /* PB0330 ?? */ - retword = i2c_read(gspca_dev, 0x04); - if (retword != 0) - return 0x0a; /* PB0330 ?? */ - - start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */ - i2c_write(gspca_dev, 0x01, 0x11, 0x00); - retword = i2c_read(gspca_dev, 0x01); - if (retword != 0) - return 0x0c; /* ICM105A */ - - start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */ - reg_w(gspca_dev, 0x08, 0x008d); - i2c_write(gspca_dev, 0x03, 0xaa, 0x00); - msleep(50); - retword = i2c_read(gspca_dev, 0x03); - if (retword != 0) { - send_unknown(gspca_dev, SENSOR_PAS202B); - return 0x0e; /* PAS202BCB */ - } - - start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */ - i2c_write(gspca_dev, 0x01, 0xaa, 0x00); - retword = i2c_read(gspca_dev, 0x01); - if (retword != 0) - return 0x02; /* TAS5130C */ -ov_check: - reg_r(gspca_dev, 0x0010); /* ?? */ - reg_r(gspca_dev, 0x0010); - - reg_w(gspca_dev, 0x01, 0x0000); - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */ - reg_w(gspca_dev, 0xa1, 0x008b); - reg_w(gspca_dev, 0x08, 0x008d); - msleep(500); - reg_w(gspca_dev, 0x01, 0x0012); - i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */ - retword = i2c_read(gspca_dev, 0x0a) << 8; - retword |= i2c_read(gspca_dev, 0x0b); - PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword); - switch (retword) { - case 0x7631: /* OV7630C */ - reg_w(gspca_dev, 0x06, 0x0010); - break; - case 0x7620: /* OV7620 */ - case 0x7648: /* OV7648 */ - break; - default: - return -1; /* not OmniVision */ - } - return retword; -} - -struct sensor_by_chipset_revision { - u16 revision; - u8 internal_sensor_id; -}; -static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { - {0xc000, 0x12}, /* TAS5130C */ - {0xc001, 0x13}, /* MT9V111 */ - {0xe001, 0x13}, - {0x8001, 0x13}, - {0x8000, 0x14}, /* CS2102K */ - {0x8400, 0x15}, /* MT9V111 */ - {0xe400, 0x15}, -}; - -static int vga_3wr_probe(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - u16 retword; - -/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ - reg_w(gspca_dev, 0x02, 0x0010); - reg_r(gspca_dev, 0x0010); - reg_w(gspca_dev, 0x01, 0x0000); - reg_w(gspca_dev, 0x00, 0x0010); - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0x91, 0x008b); - reg_w(gspca_dev, 0x03, 0x0012); - reg_w(gspca_dev, 0x01, 0x0012); - reg_w(gspca_dev, 0x05, 0x0012); - retword = i2c_read(gspca_dev, 0x14); - if (retword != 0) - return 0x11; /* HV7131R */ - retword = i2c_read(gspca_dev, 0x15); - if (retword != 0) - return 0x11; /* HV7131R */ - retword = i2c_read(gspca_dev, 0x16); - if (retword != 0) - return 0x11; /* HV7131R */ - - reg_w(gspca_dev, 0x02, 0x0010); - retword = reg_r(gspca_dev, 0x000b) << 8; - retword |= reg_r(gspca_dev, 0x000a); - PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword); - reg_r(gspca_dev, 0x0010); - if ((retword & 0xff00) == 0x6400) - return 0x02; /* TAS5130C */ - for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { - if (chipset_revision_sensor[i].revision == retword) { - sd->chip_revision = retword; - send_unknown(gspca_dev, SENSOR_PB0330); - return chipset_revision_sensor[i].internal_sensor_id; - } - } - - reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */ - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0xdd, 0x008b); - reg_w(gspca_dev, 0x0a, 0x0010); - reg_w(gspca_dev, 0x03, 0x0012); - reg_w(gspca_dev, 0x01, 0x0012); - retword = i2c_read(gspca_dev, 0x00); - if (retword != 0) { - PDEBUG(D_PROBE, "probe 3wr vga type 0a"); - return 0x0a; /* PB0330 */ - } - - /* probe gc0303 / gc0305 */ - reg_w(gspca_dev, 0x01, 0x0000); - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0x98, 0x008b); - reg_w(gspca_dev, 0x01, 0x0010); - reg_w(gspca_dev, 0x03, 0x0012); - msleep(2); - reg_w(gspca_dev, 0x01, 0x0012); - retword = i2c_read(gspca_dev, 0x00); - if (retword != 0) { - PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword); - if (retword == 0x0011) /* gc0303 */ - return 0x0303; - if (retword == 0x0029) /* gc0305 */ - send_unknown(gspca_dev, SENSOR_GC0305); - return retword; - } - - reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */ - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0xa1, 0x008b); - reg_w(gspca_dev, 0x08, 0x008d); - reg_w(gspca_dev, 0x06, 0x0010); - reg_w(gspca_dev, 0x01, 0x0012); - reg_w(gspca_dev, 0x05, 0x0012); - if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */ - && i2c_read(gspca_dev, 0x1d) == 0x00a2) { - send_unknown(gspca_dev, SENSOR_OV7620); - return 0x06; /* OmniVision confirm ? */ - } - - reg_w(gspca_dev, 0x01, 0x0000); - reg_w(gspca_dev, 0x00, 0x0002); - reg_w(gspca_dev, 0x01, 0x0010); - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0xee, 0x008b); - reg_w(gspca_dev, 0x03, 0x0012); - reg_w(gspca_dev, 0x01, 0x0012); - reg_w(gspca_dev, 0x05, 0x0012); - retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */ - retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */ - PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword); - if (retword == 0x2030) { -#ifdef GSPCA_DEBUG - u8 retbyte; - - retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ - PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte); -#endif - send_unknown(gspca_dev, SENSOR_PO2030); - return retword; - } - - reg_w(gspca_dev, 0x01, 0x0000); - reg_w(gspca_dev, 0x0a, 0x0010); - reg_w(gspca_dev, 0xd3, 0x008b); - reg_w(gspca_dev, 0x01, 0x0001); - reg_w(gspca_dev, 0x03, 0x0012); - reg_w(gspca_dev, 0x01, 0x0012); - reg_w(gspca_dev, 0x05, 0x0012); - reg_w(gspca_dev, 0xd3, 0x008b); - retword = i2c_read(gspca_dev, 0x01); - if (retword != 0) { - PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword); - return 0x16; /* adcm2700 (6100/6200) */ - } - return -1; -} - -static int zcxx_probeSensor(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int sensor; - - switch (sd->sensor) { - case SENSOR_MC501CB: - return -1; /* don't probe */ - case SENSOR_GC0303: - /* may probe but with no write in reg 0x0010 */ - return -1; /* don't probe */ - case SENSOR_PAS106: - sensor = sif_probe(gspca_dev); - if (sensor >= 0) - return sensor; - break; - } - sensor = vga_2wr_probe(gspca_dev); - if (sensor >= 0) - return sensor; - return vga_3wr_probe(gspca_dev); -} - -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (id->idProduct == 0x301b) - sd->bridge = BRIDGE_ZC301; - else - sd->bridge = BRIDGE_ZC303; - - /* define some sensors from the vendor/product */ - sd->sensor = id->driver_info; - - sd->reg08 = REG08_DEF; - - INIT_WORK(&sd->work, transfer_update); - - return 0; -} - -static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - gspca_dev->usb_err = 0; - if (ctrl->val && sd->exposure && gspca_dev->streaming) - sd->exposure->val = getexposure(gspca_dev); - return gspca_dev->usb_err; - } - return -EINVAL; -} - -static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - int i, qual; - - gspca_dev->usb_err = 0; - - if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) { - qual = sd->reg08 >> 1; - - for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) { - if (ctrl->val <= jpeg_qual[i]) - break; - } - if (i > 0 && i == qual && ctrl->val < jpeg_qual[i]) - i--; - - /* With high quality settings we need max bandwidth */ - if (i >= 2 && gspca_dev->streaming && - !gspca_dev->cam.needs_full_bandwidth) - return -EBUSY; - - sd->reg08 = (i << 1) | 1; - ctrl->val = jpeg_qual[i]; - } - - if (!gspca_dev->streaming) - return 0; - - switch (ctrl->id) { - /* gamma/brightness/contrast cluster */ - case V4L2_CID_GAMMA: - setcontrast(gspca_dev, sd->gamma->val, - sd->brightness->val, sd->contrast->val); - break; - /* autogain/exposure cluster */ - case V4L2_CID_AUTOGAIN: - setautogain(gspca_dev, ctrl->val); - if (!gspca_dev->usb_err && !ctrl->val && sd->exposure) - setexposure(gspca_dev, sd->exposure->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setlightfreq(gspca_dev, ctrl->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - setquality(gspca_dev); - break; - } - return gspca_dev->usb_err; -} - -static const struct v4l2_ctrl_ops zcxx_ctrl_ops = { - .g_volatile_ctrl = zcxx_g_volatile_ctrl, - .s_ctrl = zcxx_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - static const u8 gamma[SENSOR_MAX] = { - [SENSOR_ADCM2700] = 4, - [SENSOR_CS2102] = 4, - [SENSOR_CS2102K] = 5, - [SENSOR_GC0303] = 3, - [SENSOR_GC0305] = 4, - [SENSOR_HDCS2020] = 4, - [SENSOR_HV7131B] = 4, - [SENSOR_HV7131R] = 4, - [SENSOR_ICM105A] = 4, - [SENSOR_MC501CB] = 4, - [SENSOR_MT9V111_1] = 4, - [SENSOR_MT9V111_3] = 4, - [SENSOR_OV7620] = 3, - [SENSOR_OV7630C] = 4, - [SENSOR_PAS106] = 4, - [SENSOR_PAS202B] = 4, - [SENSOR_PB0330] = 4, - [SENSOR_PO2030] = 4, - [SENSOR_TAS5130C] = 3, - }; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 8); - sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, - V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]); - if (sd->sensor == SENSOR_HV7131R) - sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, - V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927); - sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - if (sd->sensor != SENSOR_OV7630C) - sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); - sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 3, 1, - sd->sensor == SENSOR_PO2030 ? 0 : 2); - sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1, - jpeg_qual[REG08_DEF >> 1]); - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_cluster(3, &sd->gamma); - if (sd->sensor == SENSOR_HV7131R) - v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - int sensor; - static const u8 mode_tb[SENSOR_MAX] = { - [SENSOR_ADCM2700] = 2, - [SENSOR_CS2102] = 1, - [SENSOR_CS2102K] = 1, - [SENSOR_GC0303] = 1, - [SENSOR_GC0305] = 1, - [SENSOR_HDCS2020] = 1, - [SENSOR_HV7131B] = 1, - [SENSOR_HV7131R] = 1, - [SENSOR_ICM105A] = 1, - [SENSOR_MC501CB] = 2, - [SENSOR_MT9V111_1] = 1, - [SENSOR_MT9V111_3] = 1, - [SENSOR_OV7620] = 2, - [SENSOR_OV7630C] = 1, - [SENSOR_PAS106] = 0, - [SENSOR_PAS202B] = 1, - [SENSOR_PB0330] = 1, - [SENSOR_PO2030] = 1, - [SENSOR_TAS5130C] = 1, - }; - - sensor = zcxx_probeSensor(gspca_dev); - if (sensor >= 0) - PDEBUG(D_PROBE, "probe sensor -> %04x", sensor); - if ((unsigned) force_sensor < SENSOR_MAX) { - sd->sensor = force_sensor; - PDEBUG(D_PROBE, "sensor forced to %d", force_sensor); - } else { - switch (sensor) { - case -1: - switch (sd->sensor) { - case SENSOR_MC501CB: - PDEBUG(D_PROBE, "Sensor MC501CB"); - break; - case SENSOR_GC0303: - PDEBUG(D_PROBE, "Sensor GC0303"); - break; - default: - pr_warn("Unknown sensor - set to TAS5130C\n"); - sd->sensor = SENSOR_TAS5130C; - } - break; - case 0: - /* check the sensor type */ - sensor = i2c_read(gspca_dev, 0x00); - PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor); - switch (sensor) { - case 0: /* hv7131b */ - case 1: /* hv7131e */ - PDEBUG(D_PROBE, "Find Sensor HV7131B"); - sd->sensor = SENSOR_HV7131B; - break; - default: -/* case 2: * hv7131r */ - PDEBUG(D_PROBE, "Find Sensor HV7131R"); - sd->sensor = SENSOR_HV7131R; - break; - } - break; - case 0x02: - PDEBUG(D_PROBE, "Sensor TAS5130C"); - sd->sensor = SENSOR_TAS5130C; - break; - case 0x04: - PDEBUG(D_PROBE, "Find Sensor CS2102"); - sd->sensor = SENSOR_CS2102; - break; - case 0x08: - PDEBUG(D_PROBE, "Find Sensor HDCS2020"); - sd->sensor = SENSOR_HDCS2020; - break; - case 0x0a: - PDEBUG(D_PROBE, - "Find Sensor PB0330. Chip revision %x", - sd->chip_revision); - sd->sensor = SENSOR_PB0330; - break; - case 0x0c: - PDEBUG(D_PROBE, "Find Sensor ICM105A"); - sd->sensor = SENSOR_ICM105A; - break; - case 0x0e: - PDEBUG(D_PROBE, "Find Sensor PAS202B"); - sd->sensor = SENSOR_PAS202B; - break; - case 0x0f: - PDEBUG(D_PROBE, "Find Sensor PAS106"); - sd->sensor = SENSOR_PAS106; - break; - case 0x10: - case 0x12: - PDEBUG(D_PROBE, "Find Sensor TAS5130C"); - sd->sensor = SENSOR_TAS5130C; - break; - case 0x11: - PDEBUG(D_PROBE, "Find Sensor HV7131R"); - sd->sensor = SENSOR_HV7131R; - break; - case 0x13: - case 0x15: - PDEBUG(D_PROBE, - "Sensor MT9V111. Chip revision %04x", - sd->chip_revision); - sd->sensor = sd->bridge == BRIDGE_ZC301 - ? SENSOR_MT9V111_1 - : SENSOR_MT9V111_3; - break; - case 0x14: - PDEBUG(D_PROBE, - "Find Sensor CS2102K?. Chip revision %x", - sd->chip_revision); - sd->sensor = SENSOR_CS2102K; - break; - case 0x16: - PDEBUG(D_PROBE, "Find Sensor ADCM2700"); - sd->sensor = SENSOR_ADCM2700; - break; - case 0x29: - PDEBUG(D_PROBE, "Find Sensor GC0305"); - sd->sensor = SENSOR_GC0305; - break; - case 0x0303: - PDEBUG(D_PROBE, "Sensor GC0303"); - sd->sensor = SENSOR_GC0303; - break; - case 0x2030: - PDEBUG(D_PROBE, "Find Sensor PO2030"); - sd->sensor = SENSOR_PO2030; - break; - case 0x7620: - PDEBUG(D_PROBE, "Find Sensor OV7620"); - sd->sensor = SENSOR_OV7620; - break; - case 0x7631: - PDEBUG(D_PROBE, "Find Sensor OV7630C"); - sd->sensor = SENSOR_OV7630C; - break; - case 0x7648: - PDEBUG(D_PROBE, "Find Sensor OV7648"); - sd->sensor = SENSOR_OV7620; /* same sensor (?) */ - break; - default: - pr_err("Unknown sensor %04x\n", sensor); - return -EINVAL; - } - } - if (sensor < 0x20) { - if (sensor == -1 || sensor == 0x10 || sensor == 0x12) - reg_w(gspca_dev, 0x02, 0x0010); - reg_r(gspca_dev, 0x0010); - } - - cam = &gspca_dev->cam; - switch (mode_tb[sd->sensor]) { - case 0: - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); - break; - case 1: - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - break; - default: -/* case 2: */ - cam->cam_mode = broken_vga_mode; - cam->nmodes = ARRAY_SIZE(broken_vga_mode); - break; - } - - /* switch off the led */ - reg_w(gspca_dev, 0x01, 0x0000); - return gspca_dev->usb_err; -} - -static int sd_pre_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0; - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int mode; - static const struct usb_action *init_tb[SENSOR_MAX][2] = { - [SENSOR_ADCM2700] = - {adcm2700_Initial, adcm2700_InitialScale}, - [SENSOR_CS2102] = - {cs2102_Initial, cs2102_InitialScale}, - [SENSOR_CS2102K] = - {cs2102K_Initial, cs2102K_InitialScale}, - [SENSOR_GC0303] = - {gc0303_Initial, gc0303_InitialScale}, - [SENSOR_GC0305] = - {gc0305_Initial, gc0305_InitialScale}, - [SENSOR_HDCS2020] = - {hdcs2020_Initial, hdcs2020_InitialScale}, - [SENSOR_HV7131B] = - {hv7131b_Initial, hv7131b_InitialScale}, - [SENSOR_HV7131R] = - {hv7131r_Initial, hv7131r_InitialScale}, - [SENSOR_ICM105A] = - {icm105a_Initial, icm105a_InitialScale}, - [SENSOR_MC501CB] = - {mc501cb_Initial, mc501cb_InitialScale}, - [SENSOR_MT9V111_1] = - {mt9v111_1_Initial, mt9v111_1_InitialScale}, - [SENSOR_MT9V111_3] = - {mt9v111_3_Initial, mt9v111_3_InitialScale}, - [SENSOR_OV7620] = - {ov7620_Initial, ov7620_InitialScale}, - [SENSOR_OV7630C] = - {ov7630c_Initial, ov7630c_InitialScale}, - [SENSOR_PAS106] = - {pas106b_Initial, pas106b_InitialScale}, - [SENSOR_PAS202B] = - {pas202b_Initial, pas202b_InitialScale}, - [SENSOR_PB0330] = - {pb0330_Initial, pb0330_InitialScale}, - [SENSOR_PO2030] = - {po2030_Initial, po2030_InitialScale}, - [SENSOR_TAS5130C] = - {tas5130c_Initial, tas5130c_InitialScale}, - }; - - /* create the JPEG header */ - jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, - 0x21); /* JPEG 422 */ - - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - switch (sd->sensor) { - case SENSOR_HV7131R: - zcxx_probeSensor(gspca_dev); - break; - case SENSOR_PAS106: - usb_exchange(gspca_dev, pas106b_Initial_com); - break; - } - usb_exchange(gspca_dev, init_tb[sd->sensor][mode]); - - switch (sd->sensor) { - case SENSOR_ADCM2700: - case SENSOR_GC0305: - case SENSOR_OV7620: - case SENSOR_PO2030: - case SENSOR_TAS5130C: - case SENSOR_GC0303: -/* msleep(100); * ?? */ - reg_r(gspca_dev, 0x0002); /* --> 0x40 */ - reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(gspca_dev, 0x15, 0x01ae); - if (sd->sensor == SENSOR_TAS5130C) - break; - reg_w(gspca_dev, 0x0d, 0x003a); - reg_w(gspca_dev, 0x02, 0x003b); - reg_w(gspca_dev, 0x00, 0x0038); - break; - case SENSOR_HV7131R: - case SENSOR_PAS202B: - reg_w(gspca_dev, 0x03, 0x003b); - reg_w(gspca_dev, 0x0c, 0x003a); - reg_w(gspca_dev, 0x0b, 0x0039); - if (sd->sensor == SENSOR_HV7131R) - reg_w(gspca_dev, 0x50, ZC3XX_R11D_GLOBALGAIN); - break; - } - - setmatrix(gspca_dev); - switch (sd->sensor) { - case SENSOR_ADCM2700: - case SENSOR_OV7620: - reg_r(gspca_dev, 0x0008); - reg_w(gspca_dev, 0x00, 0x0008); - break; - case SENSOR_PAS202B: - case SENSOR_GC0305: - case SENSOR_HV7131R: - case SENSOR_TAS5130C: - reg_r(gspca_dev, 0x0008); - /* fall thru */ - case SENSOR_PO2030: - reg_w(gspca_dev, 0x03, 0x0008); - break; - } - setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); - - /* set the gamma tables when not set */ - switch (sd->sensor) { - case SENSOR_CS2102K: /* gamma set in xxx_Initial */ - case SENSOR_HDCS2020: - case SENSOR_OV7630C: - break; - default: - setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma), - v4l2_ctrl_g_ctrl(sd->brightness), - v4l2_ctrl_g_ctrl(sd->contrast)); - break; - } - setmatrix(gspca_dev); /* one more time? */ - switch (sd->sensor) { - case SENSOR_OV7620: - case SENSOR_PAS202B: - reg_r(gspca_dev, 0x0180); /* from win */ - reg_w(gspca_dev, 0x00, 0x0180); - break; - } - setquality(gspca_dev); - /* Start with BRC disabled, transfer_update will enable it if needed */ - reg_w(gspca_dev, 0x00, 0x0007); - if (sd->plfreq) - setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq)); - - switch (sd->sensor) { - case SENSOR_ADCM2700: - reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(gspca_dev, 0x15, 0x01ae); - reg_w(gspca_dev, 0x02, 0x0180); - /* ms-win + */ - reg_w(gspca_dev, 0x40, 0x0117); - break; - case SENSOR_HV7131R: - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); - reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); - break; - case SENSOR_GC0305: - case SENSOR_TAS5130C: - reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(gspca_dev, 0x15, 0x01ae); - /* fall thru */ - case SENSOR_PAS202B: - case SENSOR_PO2030: -/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */ - reg_r(gspca_dev, 0x0180); - break; - case SENSOR_OV7620: - reg_w(gspca_dev, 0x09, 0x01ad); - reg_w(gspca_dev, 0x15, 0x01ae); - i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */ - i2c_write(gspca_dev, 0x13, 0xa3, 0x00); - /*fixme: returned value to send? */ - reg_w(gspca_dev, 0x40, 0x0117); - reg_r(gspca_dev, 0x0180); - break; - } - - setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); - - if (gspca_dev->usb_err < 0) - return gspca_dev->usb_err; - - /* Start the transfer parameters update thread */ - sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME); - queue_work(sd->work_thread, &sd->work); - - return 0; -} - -/* called on streamoff with alt 0 and on disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->work_thread != NULL) { - mutex_unlock(&gspca_dev->usb_lock); - destroy_workqueue(sd->work_thread); - mutex_lock(&gspca_dev->usb_lock); - sd->work_thread = NULL; - } - if (!gspca_dev->dev) - return; - send_unknown(gspca_dev, sd->sensor); -} - -static void sd_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, - int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* check the JPEG end of frame */ - if (len >= 3 - && data[len - 3] == 0xff && data[len - 2] == 0xd9) { -/*fixme: what does the last byte mean?*/ - gspca_frame_add(gspca_dev, LAST_PACKET, - data, len - 1); - return; - } - - /* check the JPEG start of a frame */ - if (data[0] == 0xff && data[1] == 0xd8) { - /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - sd->jpeg_hdr, JPEG_HDR_SZ); - - /* remove the webcam's header: - * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp - * - 'ss ss' is the frame sequence number (BE) - * - 'ww ww' and 'hh hh' are the window dimensions (BE) - * - 'pp pp' is the packet sequence number (BE) - */ - data += 18; - len -= 18; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); -} - -static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); - if (ret) - return ret; - jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); - return 0; -} - -static int sd_get_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) -{ - struct sd *sd = (struct sd *) gspca_dev; - - memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); - jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT - | V4L2_JPEG_MARKER_DQT; - return 0; -} - -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, - u8 *data, /* interrupt packet data */ - int len) /* interrput packet length */ -{ - if (len == 8 && data[4] == 1) { - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); - input_sync(gspca_dev->input_dev); - input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); - input_sync(gspca_dev->input_dev); - } - - return 0; -} -#endif - -static const struct sd_desc sd_desc = { - .name = KBUILD_MODNAME, - .config = sd_config, - .init = sd_init, - .init_controls = sd_init_controls, - .isoc_init = sd_pre_start, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .get_jcomp = sd_get_jcomp, - .set_jcomp = sd_set_jcomp, -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) - .int_pkt_scan = sd_int_pkt_scan, -#endif -}; - -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x03f0, 0x1b07)}, - {USB_DEVICE(0x041e, 0x041e)}, - {USB_DEVICE(0x041e, 0x4017)}, - {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x041e, 0x401e)}, - {USB_DEVICE(0x041e, 0x401f)}, - {USB_DEVICE(0x041e, 0x4022)}, - {USB_DEVICE(0x041e, 0x4029)}, - {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x041e, 0x4036)}, - {USB_DEVICE(0x041e, 0x403a)}, - {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_GC0303}, - {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_GC0303}, - {USB_DEVICE(0x0458, 0x7007)}, - {USB_DEVICE(0x0458, 0x700c)}, - {USB_DEVICE(0x0458, 0x700f)}, - {USB_DEVICE(0x0461, 0x0a00)}, - {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB}, - {USB_DEVICE(0x046d, 0x08a0)}, - {USB_DEVICE(0x046d, 0x08a1)}, - {USB_DEVICE(0x046d, 0x08a2)}, - {USB_DEVICE(0x046d, 0x08a3)}, - {USB_DEVICE(0x046d, 0x08a6)}, - {USB_DEVICE(0x046d, 0x08a7)}, - {USB_DEVICE(0x046d, 0x08a9)}, - {USB_DEVICE(0x046d, 0x08aa)}, - {USB_DEVICE(0x046d, 0x08ac)}, - {USB_DEVICE(0x046d, 0x08ad)}, - {USB_DEVICE(0x046d, 0x08ae)}, - {USB_DEVICE(0x046d, 0x08af)}, - {USB_DEVICE(0x046d, 0x08b9)}, - {USB_DEVICE(0x046d, 0x08d7)}, - {USB_DEVICE(0x046d, 0x08d8)}, - {USB_DEVICE(0x046d, 0x08d9)}, - {USB_DEVICE(0x046d, 0x08da)}, - {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB}, - {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x055f, 0xc005)}, - {USB_DEVICE(0x055f, 0xd003)}, - {USB_DEVICE(0x055f, 0xd004)}, - {USB_DEVICE(0x0698, 0x2003)}, - {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0ac8, 0x301b)}, - {USB_DEVICE(0x0ac8, 0x303b)}, - {USB_DEVICE(0x0ac8, 0x305b)}, - {USB_DEVICE(0x0ac8, 0x307b)}, - {USB_DEVICE(0x10fd, 0x0128)}, - {USB_DEVICE(0x10fd, 0x804d)}, - {USB_DEVICE(0x10fd, 0x8050)}, - {} /* end of entry */ -}; -MODULE_DEVICE_TABLE(usb, device_table); - -/* -- device connect -- */ -static int sd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); -} - -/* USB driver */ -static struct usb_driver sd_driver = { - .name = KBUILD_MODNAME, - .id_table = device_table, - .probe = sd_probe, - .disconnect = gspca_disconnect, -#ifdef CONFIG_PM - .suspend = gspca_suspend, - .resume = gspca_resume, - .reset_resume = gspca_resume, -#endif -}; - -module_usb_driver(sd_driver); - -module_param(force_sensor, int, 0644); -MODULE_PARM_DESC(force_sensor, - "Force sensor. Only for experts!!!"); diff --git a/drivers/media/video/hdpvr/Kconfig b/drivers/media/video/hdpvr/Kconfig deleted file mode 100644 index de247f3c7d05..000000000000 --- a/drivers/media/video/hdpvr/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ - -config VIDEO_HDPVR - tristate "Hauppauge HD PVR support" - depends on VIDEO_DEV - ---help--- - This is a video4linux driver for Hauppauge's HD PVR USB device. - - To compile this driver as a module, choose M here: the - module will be called hdpvr - diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile deleted file mode 100644 index 52f057f24e39..000000000000 --- a/drivers/media/video/hdpvr/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o - -obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o - -ccflags-y += -Idrivers/media/video - -ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c deleted file mode 100644 index ae8f229d1141..000000000000 --- a/drivers/media/video/hdpvr/hdpvr-control.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Hauppauge HD PVR USB driver - video 4 linux 2 interface - * - * Copyright (C) 2008 Janne Grunau (j@jannau.net) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "hdpvr.h" - - -int hdpvr_config_call(struct hdpvr_device *dev, uint value, u8 valbuf) -{ - int ret; - char request_type = 0x38, snd_request = 0x01; - - mutex_lock(&dev->usbc_mutex); - dev->usbc_buf[0] = valbuf; - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - snd_request, 0x00 | request_type, - value, CTRL_DEFAULT_INDEX, - dev->usbc_buf, 1, 10000); - - mutex_unlock(&dev->usbc_mutex); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "config call request for value 0x%x returned %d\n", value, - ret); - - return ret < 0 ? ret : 0; -} - -struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev) -{ - struct hdpvr_video_info *vidinf = NULL; -#ifdef HDPVR_DEBUG - char print_buf[15]; -#endif - int ret; - - vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL); - if (!vidinf) { - v4l2_err(&dev->v4l2_dev, "out of memory\n"); - goto err; - } - - mutex_lock(&dev->usbc_mutex); - ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), - 0x81, 0x80 | 0x38, - 0x1400, 0x0003, - dev->usbc_buf, 5, - 1000); - if (ret == 5) { - vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; - vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2]; - vidinf->fps = dev->usbc_buf[4]; - } - -#ifdef HDPVR_DEBUG - if (hdpvr_debug & MSG_INFO) { - hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf, - sizeof(print_buf), 0); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "get video info returned: %d, %s\n", ret, print_buf); - } -#endif - mutex_unlock(&dev->usbc_mutex); - - if (!vidinf->width || !vidinf->height || !vidinf->fps) { - kfree(vidinf); - vidinf = NULL; - } -err: - return vidinf; -} - -int get_input_lines_info(struct hdpvr_device *dev) -{ -#ifdef HDPVR_DEBUG - char print_buf[9]; -#endif - int ret, lines; - - mutex_lock(&dev->usbc_mutex); - ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), - 0x81, 0x80 | 0x38, - 0x1800, 0x0003, - dev->usbc_buf, 3, - 1000); - -#ifdef HDPVR_DEBUG - if (hdpvr_debug & MSG_INFO) { - hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf, - sizeof(print_buf), 0); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "get input lines info returned: %d, %s\n", ret, - print_buf); - } -#else - (void)ret; /* suppress compiler warning */ -#endif - lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; - mutex_unlock(&dev->usbc_mutex); - return lines; -} - - -int hdpvr_set_bitrate(struct hdpvr_device *dev) -{ - int ret; - - mutex_lock(&dev->usbc_mutex); - memset(dev->usbc_buf, 0, 4); - dev->usbc_buf[0] = dev->options.bitrate; - dev->usbc_buf[2] = dev->options.peak_bitrate; - - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - 0x01, 0x38, CTRL_BITRATE_VALUE, - CTRL_DEFAULT_INDEX, dev->usbc_buf, 4, 1000); - mutex_unlock(&dev->usbc_mutex); - - return ret; -} - -int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, - enum v4l2_mpeg_audio_encoding codec) -{ - int ret = 0; - - if (dev->flags & HDPVR_FLAG_AC3_CAP) { - mutex_lock(&dev->usbc_mutex); - memset(dev->usbc_buf, 0, 2); - dev->usbc_buf[0] = input; - if (codec == V4L2_MPEG_AUDIO_ENCODING_AAC) - dev->usbc_buf[1] = 0; - else if (codec == V4L2_MPEG_AUDIO_ENCODING_AC3) - dev->usbc_buf[1] = 1; - else { - mutex_unlock(&dev->usbc_mutex); - v4l2_err(&dev->v4l2_dev, "invalid audio codec %d\n", - codec); - ret = -EINVAL; - goto error; - } - - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - 0x01, 0x38, CTRL_AUDIO_INPUT_VALUE, - CTRL_DEFAULT_INDEX, dev->usbc_buf, 2, - 1000); - mutex_unlock(&dev->usbc_mutex); - if (ret == 2) - ret = 0; - } else - ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE, input); -error: - return ret; -} - -int hdpvr_set_options(struct hdpvr_device *dev) -{ - hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std); - - hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, - dev->options.video_input+1); - - hdpvr_set_audio(dev, dev->options.audio_input+1, - dev->options.audio_codec); - - hdpvr_set_bitrate(dev); - hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, - dev->options.bitrate_mode); - hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode); - - hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness); - hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast); - hdpvr_config_call(dev, CTRL_HUE, dev->options.hue); - hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation); - hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness); - - return 0; -} diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c deleted file mode 100644 index 304f43ef59eb..000000000000 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Hauppauge HD PVR USB driver - * - * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2008 Janne Grunau (j@jannau.net) - * Copyright (C) 2008 John Poet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "hdpvr.h" - -static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET}; -module_param_array(video_nr, int, NULL, 0); -MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)"); - -/* holds the number of currently registered devices */ -static atomic_t dev_nr = ATOMIC_INIT(-1); - -int hdpvr_debug; -module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(hdpvr_debug, "enable debugging output"); - -static uint default_video_input = HDPVR_VIDEO_INPUTS; -module_param(default_video_input, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / " - "1=S-Video / 2=Composite"); - -static uint default_audio_input = HDPVR_AUDIO_INPUTS; -module_param(default_audio_input, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / " - "1=RCA front / 2=S/PDIF"); - -static bool boost_audio; -module_param(boost_audio, bool, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(boost_audio, "boost the audio signal"); - - -/* table of devices that work with this driver */ -static struct usb_device_id hdpvr_table[] = { - { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) }, - { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) }, - { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) }, - { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) }, - { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID4) }, - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, hdpvr_table); - - -void hdpvr_delete(struct hdpvr_device *dev) -{ - hdpvr_free_buffers(dev); - - if (dev->video_dev) - video_device_release(dev->video_dev); - - usb_put_dev(dev->udev); -} - -static void challenge(u8 *bytes) -{ - u64 *i64P, tmp64; - uint i, idx; - - for (idx = 0; idx < 32; ++idx) { - - if (idx & 0x3) - bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3]; - - switch (idx & 0x3) { - case 0x3: - bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5]; - bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9; - break; - case 0x1: - bytes[0] *= 8; - bytes[0] += 7*idx + 4; - bytes[6] += bytes[3] * 3; - break; - case 0x0: - bytes[3 - (idx >> 3)] = bytes[idx >> 2]; - bytes[5] += bytes[6] * 3; - for (i = 0; i < 3; i++) - bytes[3] *= bytes[3] + 1; - break; - case 0x2: - for (i = 0; i < 3; i++) - bytes[1] *= bytes[6] + 1; - for (i = 0; i < 3; i++) { - i64P = (u64 *)bytes; - tmp64 = le64_to_cpup(i64P); - tmp64 <<= bytes[7] & 0x0f; - *i64P += cpu_to_le64(tmp64); - } - break; - } - } -} - -/* try to init the device like the windows driver */ -static int device_authorization(struct hdpvr_device *dev) -{ - - int ret, retval = -ENOMEM; - char request_type = 0x38, rcv_request = 0x81; - char *response; -#ifdef HDPVR_DEBUG - size_t buf_size = 46; - char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); - if (!print_buf) { - v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - return retval; - } -#endif - - mutex_lock(&dev->usbc_mutex); - ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), - rcv_request, 0x80 | request_type, - 0x0400, 0x0003, - dev->usbc_buf, 46, - 10000); - if (ret != 46) { - v4l2_err(&dev->v4l2_dev, - "unexpected answer of status request, len %d\n", ret); - goto unlock; - } -#ifdef HDPVR_DEBUG - else { - hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf, - 5*buf_size+1, 0); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "Status request returned, len %d: %s\n", - ret, print_buf); - } -#endif - - dev->fw_ver = dev->usbc_buf[1]; - - v4l2_info(&dev->v4l2_dev, "firmware version 0x%x dated %s\n", - dev->fw_ver, &dev->usbc_buf[2]); - - if (dev->fw_ver > 0x15) { - dev->options.brightness = 0x80; - dev->options.contrast = 0x40; - dev->options.hue = 0xf; - dev->options.saturation = 0x40; - dev->options.sharpness = 0x80; - } - - switch (dev->fw_ver) { - case HDPVR_FIRMWARE_VERSION: - dev->flags &= ~HDPVR_FLAG_AC3_CAP; - break; - case HDPVR_FIRMWARE_VERSION_AC3: - case HDPVR_FIRMWARE_VERSION_0X12: - case HDPVR_FIRMWARE_VERSION_0X15: - dev->flags |= HDPVR_FLAG_AC3_CAP; - break; - default: - v4l2_info(&dev->v4l2_dev, "untested firmware, the driver might" - " not work.\n"); - if (dev->fw_ver >= HDPVR_FIRMWARE_VERSION_AC3) - dev->flags |= HDPVR_FLAG_AC3_CAP; - else - dev->flags &= ~HDPVR_FLAG_AC3_CAP; - } - - response = dev->usbc_buf+38; -#ifdef HDPVR_DEBUG - hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n", - print_buf); -#endif - challenge(response); -#ifdef HDPVR_DEBUG - hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n", - print_buf); -#endif - - msleep(100); - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - 0xd1, 0x00 | request_type, - 0x0000, 0x0000, - response, 8, - 10000); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "magic request returned %d\n", ret); - - retval = ret != 8; -unlock: - mutex_unlock(&dev->usbc_mutex); - return retval; -} - -static int hdpvr_device_init(struct hdpvr_device *dev) -{ - int ret; - u8 *buf; - struct hdpvr_video_info *vidinf; - - if (device_authorization(dev)) - return -EACCES; - - /* default options for init */ - hdpvr_set_options(dev); - - /* set filter options */ - mutex_lock(&dev->usbc_mutex); - buf = dev->usbc_buf; - buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00; - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - 0x01, 0x38, - CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX, - buf, 4, - 1000); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "control request returned %d\n", ret); - mutex_unlock(&dev->usbc_mutex); - - vidinf = get_video_info(dev); - if (!vidinf) - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "no valid video signal or device init failed\n"); - else - kfree(vidinf); - - /* enable fan and bling leds */ - mutex_lock(&dev->usbc_mutex); - buf[0] = 0x1; - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - 0xd4, 0x38, 0, 0, buf, 1, - 1000); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "control request returned %d\n", ret); - - /* boost analog audio */ - buf[0] = boost_audio; - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - 0xd5, 0x38, 0, 0, buf, 1, - 1000); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "control request returned %d\n", ret); - mutex_unlock(&dev->usbc_mutex); - - dev->status = STATUS_IDLE; - return 0; -} - -static const struct hdpvr_options hdpvr_default_options = { - .video_std = HDPVR_60HZ, - .video_input = HDPVR_COMPONENT, - .audio_input = HDPVR_RCA_BACK, - .bitrate = 65, /* 6 mbps */ - .peak_bitrate = 90, /* 9 mbps */ - .bitrate_mode = HDPVR_CONSTANT, - .gop_mode = HDPVR_SIMPLE_IDR_GOP, - .audio_codec = V4L2_MPEG_AUDIO_ENCODING_AAC, - /* original picture controls for firmware version <= 0x15 */ - /* updated in device_authorization() for newer firmware */ - .brightness = 0x86, - .contrast = 0x80, - .hue = 0x80, - .saturation = 0x80, - .sharpness = 0x80, -}; - -static int hdpvr_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct hdpvr_device *dev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct i2c_client *client; - size_t buffer_size; - int i; - int retval = -ENOMEM; - - /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - dev_err(&interface->dev, "Out of memory\n"); - goto error; - } - - dev->workqueue = 0; - - /* register v4l2_device early so it can be used for printks */ - if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { - dev_err(&interface->dev, "v4l2_device_register failed\n"); - goto error; - } - - mutex_init(&dev->io_mutex); - mutex_init(&dev->i2c_mutex); - mutex_init(&dev->usbc_mutex); - dev->usbc_buf = kmalloc(64, GFP_KERNEL); - if (!dev->usbc_buf) { - v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - goto error; - } - - init_waitqueue_head(&dev->wait_buffer); - init_waitqueue_head(&dev->wait_data); - - dev->workqueue = create_singlethread_workqueue("hdpvr_buffer"); - if (!dev->workqueue) - goto error; - - /* init video transfer queues */ - INIT_LIST_HEAD(&dev->free_buff_list); - INIT_LIST_HEAD(&dev->rec_buff_list); - - dev->options = hdpvr_default_options; - - if (default_video_input < HDPVR_VIDEO_INPUTS) - dev->options.video_input = default_video_input; - - if (default_audio_input < HDPVR_AUDIO_INPUTS) { - dev->options.audio_input = default_audio_input; - if (default_audio_input == HDPVR_SPDIF) - dev->options.audio_codec = - V4L2_MPEG_AUDIO_ENCODING_AC3; - } - - dev->udev = usb_get_dev(interface_to_usbdev(interface)); - - /* set up the endpoint information */ - /* use only the first bulk-in and bulk-out endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->bulk_in_endpointAddr && - usb_endpoint_is_bulk_in(endpoint)) { - /* USB interface description is buggy, reported max - * packet size is 512 bytes, windows driver uses 8192 */ - buffer_size = 8192; - dev->bulk_in_size = buffer_size; - dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; - } - - } - if (!dev->bulk_in_endpointAddr) { - v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n"); - goto error; - } - - /* init the device */ - if (hdpvr_device_init(dev)) { - v4l2_err(&dev->v4l2_dev, "device init failed\n"); - goto error; - } - - mutex_lock(&dev->io_mutex); - if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) { - mutex_unlock(&dev->io_mutex); - v4l2_err(&dev->v4l2_dev, - "allocating transfer buffers failed\n"); - goto error; - } - mutex_unlock(&dev->io_mutex); - - if (hdpvr_register_videodev(dev, &interface->dev, - video_nr[atomic_inc_return(&dev_nr)])) { - v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); - goto error; - } - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - retval = hdpvr_register_i2c_adapter(dev); - if (retval < 0) { - v4l2_err(&dev->v4l2_dev, "i2c adapter register failed\n"); - goto error; - } - - client = hdpvr_register_ir_rx_i2c(dev); - if (!client) { - v4l2_err(&dev->v4l2_dev, "i2c IR RX device register failed\n"); - goto reg_fail; - } - - client = hdpvr_register_ir_tx_i2c(dev); - if (!client) { - v4l2_err(&dev->v4l2_dev, "i2c IR TX device register failed\n"); - goto reg_fail; - } -#endif - - /* let the user know what node this device is now attached to */ - v4l2_info(&dev->v4l2_dev, "device now attached to %s\n", - video_device_node_name(dev->video_dev)); - return 0; - -reg_fail: -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_adapter(&dev->i2c_adapter); -#endif -error: - if (dev) { - /* Destroy single thread */ - if (dev->workqueue) - destroy_workqueue(dev->workqueue); - /* this frees allocated memory */ - hdpvr_delete(dev); - } - return retval; -} - -static void hdpvr_disconnect(struct usb_interface *interface) -{ - struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface)); - - v4l2_info(&dev->v4l2_dev, "device %s disconnected\n", - video_device_node_name(dev->video_dev)); - /* prevent more I/O from starting and stop any ongoing */ - mutex_lock(&dev->io_mutex); - dev->status = STATUS_DISCONNECTED; - wake_up_interruptible(&dev->wait_data); - wake_up_interruptible(&dev->wait_buffer); - mutex_unlock(&dev->io_mutex); - v4l2_device_disconnect(&dev->v4l2_dev); - msleep(100); - flush_workqueue(dev->workqueue); - mutex_lock(&dev->io_mutex); - hdpvr_cancel_queue(dev); - mutex_unlock(&dev->io_mutex); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_adapter(&dev->i2c_adapter); -#endif - video_unregister_device(dev->video_dev); - atomic_dec(&dev_nr); -} - - -static struct usb_driver hdpvr_usb_driver = { - .name = "hdpvr", - .probe = hdpvr_probe, - .disconnect = hdpvr_disconnect, - .id_table = hdpvr_table, -}; - -module_usb_driver(hdpvr_usb_driver); - -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.2.1"); -MODULE_AUTHOR("Janne Grunau"); -MODULE_DESCRIPTION("Hauppauge HD PVR driver"); diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c deleted file mode 100644 index 82e819fa91c0..000000000000 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ /dev/null @@ -1,231 +0,0 @@ - -/* - * Hauppauge HD PVR USB driver - * - * Copyright (C) 2008 Janne Grunau (j@jannau.net) - * - * IR device registration code is - * Copyright (C) 2010 Andy Walls - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - -#include -#include -#include - -#include "hdpvr.h" - -#define CTRL_READ_REQUEST 0xb8 -#define CTRL_WRITE_REQUEST 0x38 - -#define REQTYPE_I2C_READ 0xb1 -#define REQTYPE_I2C_WRITE 0xb0 -#define REQTYPE_I2C_WRITE_STATT 0xd0 - -#define Z8F0811_IR_TX_I2C_ADDR 0x70 -#define Z8F0811_IR_RX_I2C_ADDR 0x71 - - -struct i2c_client *hdpvr_register_ir_tx_i2c(struct hdpvr_device *dev) -{ - struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data; - struct i2c_board_info hdpvr_ir_tx_i2c_board_info = { - I2C_BOARD_INFO("ir_tx_z8f0811_hdpvr", Z8F0811_IR_TX_I2C_ADDR), - }; - - init_data->name = "HD-PVR"; - hdpvr_ir_tx_i2c_board_info.platform_data = init_data; - - return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_tx_i2c_board_info); -} - -struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev) -{ - struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data; - struct i2c_board_info hdpvr_ir_rx_i2c_board_info = { - I2C_BOARD_INFO("ir_rx_z8f0811_hdpvr", Z8F0811_IR_RX_I2C_ADDR), - }; - - /* Our default information for ir-kbd-i2c.c to use */ - init_data->ir_codes = RC_MAP_HAUPPAUGE; - init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - init_data->type = RC_TYPE_RC5; - init_data->name = "HD-PVR"; - init_data->polling_interval = 405; /* ms, duplicated from Windows */ - hdpvr_ir_rx_i2c_board_info.platform_data = init_data; - - return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_rx_i2c_board_info); -} - -static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, - unsigned char addr, char *wdata, int wlen, - char *data, int len) -{ - int ret; - - if ((len > sizeof(dev->i2c_buf)) || (wlen > sizeof(dev->i2c_buf))) - return -EINVAL; - - if (wlen) { - memcpy(&dev->i2c_buf, wdata, wlen); - ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), - REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, - wlen, 1000); - if (ret < 0) - return ret; - } - - ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), - REQTYPE_I2C_READ, CTRL_READ_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); - - if (ret == len) { - memcpy(data, &dev->i2c_buf, len); - ret = 0; - } else if (ret >= 0) - ret = -EIO; - - return ret; -} - -static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus, - unsigned char addr, char *data, int len) -{ - int ret; - - if (len > sizeof(dev->i2c_buf)) - return -EINVAL; - - memcpy(&dev->i2c_buf, data, len); - ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), - REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); - - if (ret < 0) - return ret; - - ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), - REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, - 0, 0, &dev->i2c_buf, 2, 1000); - - if ((ret == 2) && (dev->i2c_buf[1] == (len - 1))) - ret = 0; - else if (ret >= 0) - ret = -EIO; - - return ret; -} - -static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, - int num) -{ - struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter); - int retval = 0, addr; - - if (num <= 0) - return 0; - - mutex_lock(&dev->i2c_mutex); - - addr = msgs[0].addr << 1; - - if (num == 1) { - if (msgs[0].flags & I2C_M_RD) - retval = hdpvr_i2c_read(dev, 1, addr, NULL, 0, - msgs[0].buf, msgs[0].len); - else - retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf, - msgs[0].len); - } else if (num == 2) { - if (msgs[0].addr != msgs[1].addr) { - v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer " - "with conflicting target addresses\n"); - retval = -EINVAL; - goto out; - } - - if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) { - v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with " - "r0=%d, r1=%d\n", msgs[0].flags & I2C_M_RD, - msgs[1].flags & I2C_M_RD); - retval = -EINVAL; - goto out; - } - - /* - * Write followed by atomic read is the only complex xfer that - * we actually support here. - */ - retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len, - msgs[1].buf, msgs[1].len); - } else { - v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num); - } - -out: - mutex_unlock(&dev->i2c_mutex); - - return retval ? retval : num; -} - -static u32 hdpvr_functionality(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -static struct i2c_algorithm hdpvr_algo = { - .master_xfer = hdpvr_transfer, - .functionality = hdpvr_functionality, -}; - -static struct i2c_adapter hdpvr_i2c_adapter_template = { - .name = "Hauppage HD PVR I2C", - .owner = THIS_MODULE, - .algo = &hdpvr_algo, -}; - -static int hdpvr_activate_ir(struct hdpvr_device *dev) -{ - char buffer[2]; - - mutex_lock(&dev->i2c_mutex); - - hdpvr_i2c_read(dev, 0, 0x54, NULL, 0, buffer, 1); - - buffer[0] = 0; - buffer[1] = 0x8; - hdpvr_i2c_write(dev, 1, 0x54, buffer, 2); - - buffer[1] = 0x18; - hdpvr_i2c_write(dev, 1, 0x54, buffer, 2); - - mutex_unlock(&dev->i2c_mutex); - - return 0; -} - -int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) -{ - int retval = -ENOMEM; - - hdpvr_activate_ir(dev); - - memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template, - sizeof(struct i2c_adapter)); - dev->i2c_adapter.dev.parent = &dev->udev->dev; - - i2c_set_adapdata(&dev->i2c_adapter, dev); - - retval = i2c_add_adapter(&dev->i2c_adapter); - - return retval; -} - -#endif diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c deleted file mode 100644 index 0e9e156bb2aa..000000000000 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ /dev/null @@ -1,1289 +0,0 @@ -/* - * Hauppauge HD PVR USB driver - video 4 linux 2 interface - * - * Copyright (C) 2008 Janne Grunau (j@jannau.net) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "hdpvr.h" - -#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */ - -#define print_buffer_status() { \ - v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \ - "%s:%d buffer stat: %d free, %d proc\n", \ - __func__, __LINE__, \ - list_size(&dev->free_buff_list), \ - list_size(&dev->rec_buff_list)); } - -struct hdpvr_fh { - struct hdpvr_device *dev; -}; - -static uint list_size(struct list_head *list) -{ - struct list_head *tmp; - uint count = 0; - - list_for_each(tmp, list) { - count++; - } - - return count; -} - -/*=========================================================================*/ -/* urb callback */ -static void hdpvr_read_bulk_callback(struct urb *urb) -{ - struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context; - struct hdpvr_device *dev = buf->dev; - - /* marking buffer as received and wake waiting */ - buf->status = BUFSTAT_READY; - wake_up_interruptible(&dev->wait_data); -} - -/*=========================================================================*/ -/* bufffer bits */ - -/* function expects dev->io_mutex to be hold by caller */ -int hdpvr_cancel_queue(struct hdpvr_device *dev) -{ - struct hdpvr_buffer *buf; - - list_for_each_entry(buf, &dev->rec_buff_list, buff_list) { - usb_kill_urb(buf->urb); - buf->status = BUFSTAT_AVAILABLE; - } - - list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev); - - return 0; -} - -static int hdpvr_free_queue(struct list_head *q) -{ - struct list_head *tmp; - struct list_head *p; - struct hdpvr_buffer *buf; - struct urb *urb; - - for (p = q->next; p != q;) { - buf = list_entry(p, struct hdpvr_buffer, buff_list); - - urb = buf->urb; - usb_free_coherent(urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, urb->transfer_dma); - usb_free_urb(urb); - tmp = p->next; - list_del(p); - kfree(buf); - p = tmp; - } - - return 0; -} - -/* function expects dev->io_mutex to be hold by caller */ -int hdpvr_free_buffers(struct hdpvr_device *dev) -{ - hdpvr_cancel_queue(dev); - - hdpvr_free_queue(&dev->free_buff_list); - hdpvr_free_queue(&dev->rec_buff_list); - - return 0; -} - -/* function expects dev->io_mutex to be hold by caller */ -int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) -{ - uint i; - int retval = -ENOMEM; - u8 *mem; - struct hdpvr_buffer *buf; - struct urb *urb; - - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "allocating %u buffers\n", count); - - for (i = 0; i < count; i++) { - - buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL); - if (!buf) { - v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n"); - goto exit; - } - buf->dev = dev; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); - goto exit_urb; - } - buf->urb = urb; - - mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL, - &urb->transfer_dma); - if (!mem) { - v4l2_err(&dev->v4l2_dev, - "cannot allocate usb transfer buffer\n"); - goto exit_urb_buffer; - } - - usb_fill_bulk_urb(buf->urb, dev->udev, - usb_rcvbulkpipe(dev->udev, - dev->bulk_in_endpointAddr), - mem, dev->bulk_in_size, - hdpvr_read_bulk_callback, buf); - - buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - buf->status = BUFSTAT_AVAILABLE; - list_add_tail(&buf->buff_list, &dev->free_buff_list); - } - return 0; -exit_urb_buffer: - usb_free_urb(urb); -exit_urb: - kfree(buf); -exit: - hdpvr_free_buffers(dev); - return retval; -} - -static int hdpvr_submit_buffers(struct hdpvr_device *dev) -{ - struct hdpvr_buffer *buf; - struct urb *urb; - int ret = 0, err_count = 0; - - mutex_lock(&dev->io_mutex); - - while (dev->status == STATUS_STREAMING && - !list_empty(&dev->free_buff_list)) { - - buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer, - buff_list); - if (buf->status != BUFSTAT_AVAILABLE) { - v4l2_err(&dev->v4l2_dev, - "buffer not marked as available\n"); - ret = -EFAULT; - goto err; - } - - urb = buf->urb; - urb->status = 0; - urb->actual_length = 0; - ret = usb_submit_urb(urb, GFP_KERNEL); - if (ret) { - v4l2_err(&dev->v4l2_dev, - "usb_submit_urb in %s returned %d\n", - __func__, ret); - if (++err_count > 2) - break; - continue; - } - buf->status = BUFSTAT_INPROGRESS; - list_move_tail(&buf->buff_list, &dev->rec_buff_list); - } -err: - print_buffer_status(); - mutex_unlock(&dev->io_mutex); - return ret; -} - -static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev) -{ - struct hdpvr_buffer *buf; - - mutex_lock(&dev->io_mutex); - - if (list_empty(&dev->rec_buff_list)) { - mutex_unlock(&dev->io_mutex); - return NULL; - } - - buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer, - buff_list); - mutex_unlock(&dev->io_mutex); - - return buf; -} - -static void hdpvr_transmit_buffers(struct work_struct *work) -{ - struct hdpvr_device *dev = container_of(work, struct hdpvr_device, - worker); - - while (dev->status == STATUS_STREAMING) { - - if (hdpvr_submit_buffers(dev)) { - v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n"); - goto error; - } - if (wait_event_interruptible(dev->wait_buffer, - !list_empty(&dev->free_buff_list) || - dev->status != STATUS_STREAMING)) - goto error; - } - - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "transmit worker exited\n"); - return; -error: - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "transmit buffers errored\n"); - dev->status = STATUS_ERROR; -} - -/* function expects dev->io_mutex to be hold by caller */ -static int hdpvr_start_streaming(struct hdpvr_device *dev) -{ - int ret; - struct hdpvr_video_info *vidinf; - - if (dev->status == STATUS_STREAMING) - return 0; - else if (dev->status != STATUS_IDLE) - return -EAGAIN; - - vidinf = get_video_info(dev); - - if (vidinf) { - v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, - "video signal: %dx%d@%dhz\n", vidinf->width, - vidinf->height, vidinf->fps); - kfree(vidinf); - - /* start streaming 2 request */ - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), - 0xb8, 0x38, 0x1, 0, NULL, 0, 8000); - v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, - "encoder start control request returned %d\n", ret); - - hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00); - - dev->status = STATUS_STREAMING; - - INIT_WORK(&dev->worker, hdpvr_transmit_buffers); - queue_work(dev->workqueue, &dev->worker); - - v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, - "streaming started\n"); - - return 0; - } - msleep(250); - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "no video signal at input %d\n", dev->options.video_input); - return -EAGAIN; -} - - -/* function expects dev->io_mutex to be hold by caller */ -static int hdpvr_stop_streaming(struct hdpvr_device *dev) -{ - int actual_length; - uint c = 0; - u8 *buf; - - if (dev->status == STATUS_IDLE) - return 0; - else if (dev->status != STATUS_STREAMING) - return -EAGAIN; - - buf = kmalloc(dev->bulk_in_size, GFP_KERNEL); - if (!buf) - v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer " - "for emptying the internal device buffer. " - "Next capture start will be slow\n"); - - dev->status = STATUS_SHUTTING_DOWN; - hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00); - mutex_unlock(&dev->io_mutex); - - wake_up_interruptible(&dev->wait_buffer); - msleep(50); - - flush_workqueue(dev->workqueue); - - mutex_lock(&dev->io_mutex); - /* kill the still outstanding urbs */ - hdpvr_cancel_queue(dev); - - /* emptying the device buffer beforeshutting it down */ - while (buf && ++c < 500 && - !usb_bulk_msg(dev->udev, - usb_rcvbulkpipe(dev->udev, - dev->bulk_in_endpointAddr), - buf, dev->bulk_in_size, &actual_length, - BULK_URB_TIMEOUT)) { - v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, - "%2d: got %d bytes\n", c, actual_length); - } - kfree(buf); - v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, - "used %d urbs to empty device buffers\n", c-1); - msleep(10); - - dev->status = STATUS_IDLE; - - return 0; -} - - -/*=======================================================================*/ -/* - * video 4 linux 2 file operations - */ - -static int hdpvr_open(struct file *file) -{ - struct hdpvr_device *dev; - struct hdpvr_fh *fh; - int retval = -ENOMEM; - - dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file)); - if (!dev) { - pr_err("open failing with with ENODEV\n"); - retval = -ENODEV; - goto err; - } - - fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL); - if (!fh) { - v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - goto err; - } - /* lock the device to allow correctly handling errors - * in resumption */ - mutex_lock(&dev->io_mutex); - dev->open_count++; - mutex_unlock(&dev->io_mutex); - - fh->dev = dev; - - /* save our object in the file's private structure */ - file->private_data = fh; - - retval = 0; -err: - return retval; -} - -static int hdpvr_release(struct file *file) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - - if (!dev) - return -ENODEV; - - mutex_lock(&dev->io_mutex); - if (!(--dev->open_count) && dev->status == STATUS_STREAMING) - hdpvr_stop_streaming(dev); - - mutex_unlock(&dev->io_mutex); - - return 0; -} - -/* - * hdpvr_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, - loff_t *pos) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - struct hdpvr_buffer *buf = NULL; - struct urb *urb; - unsigned int ret = 0; - int rem, cnt; - - if (*pos) - return -ESPIPE; - - if (!dev) - return -ENODEV; - - mutex_lock(&dev->io_mutex); - if (dev->status == STATUS_IDLE) { - if (hdpvr_start_streaming(dev)) { - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "start_streaming failed\n"); - ret = -EIO; - msleep(200); - dev->status = STATUS_IDLE; - mutex_unlock(&dev->io_mutex); - goto err; - } - print_buffer_status(); - } - mutex_unlock(&dev->io_mutex); - - /* wait for the first buffer */ - if (!(file->f_flags & O_NONBLOCK)) { - if (wait_event_interruptible(dev->wait_data, - hdpvr_get_next_buffer(dev))) - return -ERESTARTSYS; - } - - buf = hdpvr_get_next_buffer(dev); - - while (count > 0 && buf) { - - if (buf->status != BUFSTAT_READY && - dev->status != STATUS_DISCONNECTED) { - /* return nonblocking */ - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto err; - } - - if (wait_event_interruptible(dev->wait_data, - buf->status == BUFSTAT_READY)) { - ret = -ERESTARTSYS; - goto err; - } - } - - if (buf->status != BUFSTAT_READY) - break; - - /* set remaining bytes to copy */ - urb = buf->urb; - rem = urb->actual_length - buf->pos; - cnt = rem > count ? count : rem; - - if (copy_to_user(buffer, urb->transfer_buffer + buf->pos, - cnt)) { - v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n"); - if (!ret) - ret = -EFAULT; - goto err; - } - - buf->pos += cnt; - count -= cnt; - buffer += cnt; - ret += cnt; - - /* finished, take next buffer */ - if (buf->pos == urb->actual_length) { - mutex_lock(&dev->io_mutex); - buf->pos = 0; - buf->status = BUFSTAT_AVAILABLE; - - list_move_tail(&buf->buff_list, &dev->free_buff_list); - - print_buffer_status(); - - mutex_unlock(&dev->io_mutex); - - wake_up_interruptible(&dev->wait_buffer); - - buf = hdpvr_get_next_buffer(dev); - } - } -err: - if (!ret && !buf) - ret = -EAGAIN; - return ret; -} - -static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) -{ - struct hdpvr_buffer *buf = NULL; - struct hdpvr_fh *fh = filp->private_data; - struct hdpvr_device *dev = fh->dev; - unsigned int mask = 0; - - mutex_lock(&dev->io_mutex); - - if (!video_is_registered(dev->video_dev)) { - mutex_unlock(&dev->io_mutex); - return -EIO; - } - - if (dev->status == STATUS_IDLE) { - if (hdpvr_start_streaming(dev)) { - v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, - "start_streaming failed\n"); - dev->status = STATUS_IDLE; - } - - print_buffer_status(); - } - mutex_unlock(&dev->io_mutex); - - buf = hdpvr_get_next_buffer(dev); - /* only wait if no data is available */ - if (!buf || buf->status != BUFSTAT_READY) { - poll_wait(filp, &dev->wait_data, wait); - buf = hdpvr_get_next_buffer(dev); - } - if (buf && buf->status == BUFSTAT_READY) - mask |= POLLIN | POLLRDNORM; - - return mask; -} - - -static const struct v4l2_file_operations hdpvr_fops = { - .owner = THIS_MODULE, - .open = hdpvr_open, - .release = hdpvr_release, - .read = hdpvr_read, - .poll = hdpvr_poll, - .unlocked_ioctl = video_ioctl2, -}; - -/*=======================================================================*/ -/* - * V4L2 ioctl handling - */ - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct hdpvr_device *dev = video_drvdata(file); - - strcpy(cap->driver, "hdpvr"); - strcpy(cap->card, "Hauppauge HD PVR"); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; - return 0; -} - -static int vidioc_s_std(struct file *file, void *private_data, - v4l2_std_id *std) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - u8 std_type = 1; - - if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60)) - std_type = 0; - - return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type); -} - -static const char *iname[] = { - [HDPVR_COMPONENT] = "Component", - [HDPVR_SVIDEO] = "S-Video", - [HDPVR_COMPOSITE] = "Composite", -}; - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - unsigned int n; - - n = i->index; - if (n >= HDPVR_VIDEO_INPUTS) - return -EINVAL; - - i->type = V4L2_INPUT_TYPE_CAMERA; - - strncpy(i->name, iname[n], sizeof(i->name) - 1); - i->name[sizeof(i->name) - 1] = '\0'; - - i->audioset = 1<std = dev->video_dev->tvnorms; - - return 0; -} - -static int vidioc_s_input(struct file *file, void *private_data, - unsigned int index) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int retval; - - if (index >= HDPVR_VIDEO_INPUTS) - return -EINVAL; - - if (dev->status != STATUS_IDLE) - return -EAGAIN; - - retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1); - if (!retval) - dev->options.video_input = index; - - return retval; -} - -static int vidioc_g_input(struct file *file, void *private_data, - unsigned int *index) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - - *index = dev->options.video_input; - return 0; -} - - -static const char *audio_iname[] = { - [HDPVR_RCA_FRONT] = "RCA front", - [HDPVR_RCA_BACK] = "RCA back", - [HDPVR_SPDIF] = "SPDIF", -}; - -static int vidioc_enumaudio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - unsigned int n; - - n = audio->index; - if (n >= HDPVR_AUDIO_INPUTS) - return -EINVAL; - - audio->capability = V4L2_AUDCAP_STEREO; - - strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1); - audio->name[sizeof(audio->name) - 1] = '\0'; - - return 0; -} - -static int vidioc_s_audio(struct file *file, void *private_data, - struct v4l2_audio *audio) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int retval; - - if (audio->index >= HDPVR_AUDIO_INPUTS) - return -EINVAL; - - if (dev->status != STATUS_IDLE) - return -EAGAIN; - - retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec); - if (!retval) - dev->options.audio_input = audio->index; - - return retval; -} - -static int vidioc_g_audio(struct file *file, void *private_data, - struct v4l2_audio *audio) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - - audio->index = dev->options.audio_input; - audio->capability = V4L2_AUDCAP_STEREO; - strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name)); - audio->name[sizeof(audio->name) - 1] = '\0'; - return 0; -} - -static const s32 supported_v4l2_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_SHARPNESS, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_BITRATE, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, -}; - -static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc, - int ac3, int fw_ver) -{ - int err; - - if (fw_ver > 0x15) { - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - } - } else { - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); - } - } - - switch (qc->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_AUDIO_ENCODING_AAC, - ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 - : V4L2_MPEG_AUDIO_ENCODING_AAC, - 1, V4L2_MPEG_AUDIO_ENCODING_AAC); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); - -/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */ -/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000, - 6500000); - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000, - 9000000); - if (!err && opt->bitrate_mode == HDPVR_CONSTANT) - qc->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - default: - return -EINVAL; - } -} - -static int vidioc_queryctrl(struct file *file, void *private_data, - struct v4l2_queryctrl *qc) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, next; - u32 id = qc->id; - - memset(qc, 0, sizeof(*qc)); - - next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); - qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; - - for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) { - if (next) { - if (qc->id < supported_v4l2_ctrls[i]) - qc->id = supported_v4l2_ctrls[i]; - else - continue; - } - - if (qc->id == supported_v4l2_ctrls[i]) - return fill_queryctrl(&dev->options, qc, - dev->flags & HDPVR_FLAG_AC3_CAP, - dev->fw_ver); - - if (qc->id < supported_v4l2_ctrls[i]) - break; - } - - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *private_data, - struct v4l2_control *ctrl) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dev->options.brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dev->options.contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dev->options.saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dev->options.hue; - break; - case V4L2_CID_SHARPNESS: - ctrl->value = dev->options.sharpness; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *private_data, - struct v4l2_control *ctrl) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int retval; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value); - if (!retval) - dev->options.brightness = ctrl->value; - break; - case V4L2_CID_CONTRAST: - retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value); - if (!retval) - dev->options.contrast = ctrl->value; - break; - case V4L2_CID_SATURATION: - retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value); - if (!retval) - dev->options.saturation = ctrl->value; - break; - case V4L2_CID_HUE: - retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value); - if (!retval) - dev->options.hue = ctrl->value; - break; - case V4L2_CID_SHARPNESS: - retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value); - if (!retval) - dev->options.sharpness = ctrl->value; - break; - default: - return -EINVAL; - } - - return retval; -} - - -static int hdpvr_get_ctrl(struct hdpvr_options *opt, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - ctrl->value = opt->audio_codec; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; - break; -/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ -/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */ -/* break; */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT - ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR - : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = opt->bitrate * 100000; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - ctrl->value = opt->peak_bitrate * 100000; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = hdpvr_get_ctrl(&dev->options, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - - -static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) -{ - int ret = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC || - (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) - ret = 0; - break; -/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ -/* if (ctrl->value == 0 || ctrl->value == 128) */ -/* ret = 0; */ -/* break; */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR || - ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - { - uint bitrate = ctrl->value / 100000; - if (bitrate >= 10 && bitrate <= 135) - ret = 0; - break; - } - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - { - uint peak_bitrate = ctrl->value / 100000; - if (peak_bitrate >= 10 && peak_bitrate <= 202) - ret = 0; - break; - } - case V4L2_CID_MPEG_STREAM_TYPE: - if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) - ret = 0; - break; - default: - return -EINVAL; - } - return ret; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = hdpvr_try_ctrl(ctrl, - dev->flags & HDPVR_FLAG_AC3_CAP); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - } - - return -EINVAL; -} - - -static int hdpvr_set_ctrl(struct hdpvr_device *dev, - struct v4l2_ext_control *ctrl) -{ - struct hdpvr_options *opt = &dev->options; - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (dev->flags & HDPVR_FLAG_AC3_CAP) { - opt->audio_codec = ctrl->value; - ret = hdpvr_set_audio(dev, opt->audio_input, - opt->audio_codec); - } - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - break; -/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ -/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */ -/* opt->gop_mode |= 0x2; */ -/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ -/* opt->gop_mode); */ -/* } */ -/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */ -/* opt->gop_mode &= ~0x2; */ -/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ -/* opt->gop_mode); */ -/* } */ -/* break; */ - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR && - opt->bitrate_mode != HDPVR_CONSTANT) { - opt->bitrate_mode = HDPVR_CONSTANT; - hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, - opt->bitrate_mode); - } - if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - opt->bitrate_mode == HDPVR_CONSTANT) { - opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE; - hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, - opt->bitrate_mode); - } - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: { - uint bitrate = ctrl->value / 100000; - - opt->bitrate = bitrate; - if (bitrate >= opt->peak_bitrate) - opt->peak_bitrate = bitrate+1; - - hdpvr_set_bitrate(dev); - break; - } - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: { - uint peak_bitrate = ctrl->value / 100000; - - if (opt->bitrate_mode == HDPVR_CONSTANT) - break; - - if (opt->bitrate < peak_bitrate) { - opt->peak_bitrate = peak_bitrate; - hdpvr_set_bitrate(dev); - } else - ret = -EINVAL; - break; - } - case V4L2_CID_MPEG_STREAM_TYPE: - break; - default: - return -EINVAL; - } - return ret; -} - -static int vidioc_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = hdpvr_try_ctrl(ctrl, - dev->flags & HDPVR_FLAG_AC3_CAP); - if (err) { - ctrls->error_idx = i; - break; - } - err = hdpvr_set_ctrl(dev, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data, - struct v4l2_fmtdesc *f) -{ - - if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - f->flags = V4L2_FMT_FLAG_COMPRESSED; - strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32); - f->pixelformat = V4L2_PIX_FMT_MPEG; - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data, - struct v4l2_format *f) -{ - struct hdpvr_fh *fh = file->private_data; - struct hdpvr_device *dev = fh->dev; - struct hdpvr_video_info *vid_info; - - if (!dev) - return -ENODEV; - - vid_info = get_video_info(dev); - if (!vid_info) - return -EFAULT; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.width = vid_info->width; - f->fmt.pix.height = vid_info->height; - f->fmt.pix.sizeimage = dev->bulk_in_size; - f->fmt.pix.colorspace = 0; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.field = V4L2_FIELD_ANY; - - kfree(vid_info); - return 0; -} - -static int vidioc_encoder_cmd(struct file *filp, void *priv, - struct v4l2_encoder_cmd *a) -{ - struct hdpvr_fh *fh = filp->private_data; - struct hdpvr_device *dev = fh->dev; - int res; - - mutex_lock(&dev->io_mutex); - - memset(&a->raw, 0, sizeof(a->raw)); - switch (a->cmd) { - case V4L2_ENC_CMD_START: - a->flags = 0; - res = hdpvr_start_streaming(dev); - break; - case V4L2_ENC_CMD_STOP: - res = hdpvr_stop_streaming(dev); - break; - default: - v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, - "Unsupported encoder cmd %d\n", a->cmd); - res = -EINVAL; - } - mutex_unlock(&dev->io_mutex); - return res; -} - -static int vidioc_try_encoder_cmd(struct file *filp, void *priv, - struct v4l2_encoder_cmd *a) -{ - switch (a->cmd) { - case V4L2_ENC_CMD_START: - case V4L2_ENC_CMD_STOP: - return 0; - default: - return -EINVAL; - } -} - -static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_s_std = vidioc_s_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_enumaudio = vidioc_enumaudio, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, - .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_encoder_cmd = vidioc_encoder_cmd, - .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd, -}; - -static void hdpvr_device_release(struct video_device *vdev) -{ - struct hdpvr_device *dev = video_get_drvdata(vdev); - - hdpvr_delete(dev); - mutex_lock(&dev->io_mutex); - destroy_workqueue(dev->workqueue); - mutex_unlock(&dev->io_mutex); - - v4l2_device_unregister(&dev->v4l2_dev); - - /* deregister I2C adapter */ -#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE) - mutex_lock(&dev->i2c_mutex); - i2c_del_adapter(&dev->i2c_adapter); - mutex_unlock(&dev->i2c_mutex); -#endif /* CONFIG_I2C */ - - kfree(dev->usbc_buf); - kfree(dev); -} - -static const struct video_device hdpvr_video_template = { -/* .type = VFL_TYPE_GRABBER, */ -/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */ - .fops = &hdpvr_fops, - .release = hdpvr_device_release, - .ioctl_ops = &hdpvr_ioctl_ops, - .tvnorms = - V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B | - V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | - V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | - V4L2_STD_PAL_60, - .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M | - V4L2_STD_PAL_60, -}; - -int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, - int devnum) -{ - /* setup and register video device */ - dev->video_dev = video_device_alloc(); - if (!dev->video_dev) { - v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n"); - goto error; - } - - *(dev->video_dev) = hdpvr_video_template; - strcpy(dev->video_dev->name, "Hauppauge HD PVR"); - dev->video_dev->parent = parent; - video_set_drvdata(dev->video_dev, dev); - - if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) { - v4l2_err(&dev->v4l2_dev, "video_device registration failed\n"); - goto error; - } - - return 0; -error: - return -ENOMEM; -} diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h deleted file mode 100644 index fea3c6926997..000000000000 --- a/drivers/media/video/hdpvr/hdpvr.h +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Hauppauge HD PVR USB driver - * - * Copyright (C) 2008 Janne Grunau (j@jannau.net) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include -#include -#include -#include -#include - -#include -#include - -#define HDPVR_MAX 8 -#define HDPVR_I2C_MAX_SIZE 128 - -/* Define these values to match your devices */ -#define HD_PVR_VENDOR_ID 0x2040 -#define HD_PVR_PRODUCT_ID 0x4900 -#define HD_PVR_PRODUCT_ID1 0x4901 -#define HD_PVR_PRODUCT_ID2 0x4902 -#define HD_PVR_PRODUCT_ID4 0x4903 -#define HD_PVR_PRODUCT_ID3 0x4982 - -#define UNSET (-1U) - -#define NUM_BUFFERS 64 - -#define HDPVR_FIRMWARE_VERSION 0x08 -#define HDPVR_FIRMWARE_VERSION_AC3 0x0d -#define HDPVR_FIRMWARE_VERSION_0X12 0x12 -#define HDPVR_FIRMWARE_VERSION_0X15 0x15 - -/* #define HDPVR_DEBUG */ - -extern int hdpvr_debug; - -#define MSG_INFO 1 -#define MSG_BUFFER 2 - -struct hdpvr_options { - u8 video_std; - u8 video_input; - u8 audio_input; - u8 bitrate; /* in 100kbps */ - u8 peak_bitrate; /* in 100kbps */ - u8 bitrate_mode; - u8 gop_mode; - enum v4l2_mpeg_audio_encoding audio_codec; - u8 brightness; - u8 contrast; - u8 hue; - u8 saturation; - u8 sharpness; -}; - -/* Structure to hold all of our device specific stuff */ -struct hdpvr_device { - /* the v4l device for this device */ - struct video_device *video_dev; - /* the usb device for this device */ - struct usb_device *udev; - /* v4l2-device unused */ - struct v4l2_device v4l2_dev; - - /* the max packet size of the bulk endpoint */ - size_t bulk_in_size; - /* the address of the bulk in endpoint */ - __u8 bulk_in_endpointAddr; - - /* holds the current device status */ - __u8 status; - /* count the number of openers */ - uint open_count; - - /* holds the cureent set options */ - struct hdpvr_options options; - - uint flags; - - /* synchronize I/O */ - struct mutex io_mutex; - /* available buffers */ - struct list_head free_buff_list; - /* in progress buffers */ - struct list_head rec_buff_list; - /* waitqueue for buffers */ - wait_queue_head_t wait_buffer; - /* waitqueue for data */ - wait_queue_head_t wait_data; - /**/ - struct workqueue_struct *workqueue; - /**/ - struct work_struct worker; - - /* I2C adapter */ - struct i2c_adapter i2c_adapter; - /* I2C lock */ - struct mutex i2c_mutex; - /* I2C message buffer space */ - char i2c_buf[HDPVR_I2C_MAX_SIZE]; - - /* For passing data to ir-kbd-i2c */ - struct IR_i2c_init_data ir_i2c_init_data; - - /* usb control transfer buffer and lock */ - struct mutex usbc_mutex; - u8 *usbc_buf; - u8 fw_ver; -}; - -static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct hdpvr_device, v4l2_dev); -} - - -/* buffer one bulk urb of data */ -struct hdpvr_buffer { - struct list_head buff_list; - - struct urb *urb; - - struct hdpvr_device *dev; - - uint pos; - - __u8 status; -}; - -/* */ - -struct hdpvr_video_info { - u16 width; - u16 height; - u8 fps; -}; - -enum { - STATUS_UNINITIALIZED = 0, - STATUS_IDLE, - STATUS_STARTING, - STATUS_SHUTTING_DOWN, - STATUS_STREAMING, - STATUS_ERROR, - STATUS_DISCONNECTED, -}; - -enum { - HDPVR_FLAG_AC3_CAP = 1, -}; - -enum { - BUFSTAT_UNINITIALIZED = 0, - BUFSTAT_AVAILABLE, - BUFSTAT_INPROGRESS, - BUFSTAT_READY, -}; - -#define CTRL_START_STREAMING_VALUE 0x0700 -#define CTRL_STOP_STREAMING_VALUE 0x0800 -#define CTRL_BITRATE_VALUE 0x1000 -#define CTRL_BITRATE_MODE_VALUE 0x1200 -#define CTRL_GOP_MODE_VALUE 0x1300 -#define CTRL_VIDEO_INPUT_VALUE 0x1500 -#define CTRL_VIDEO_STD_TYPE 0x1700 -#define CTRL_AUDIO_INPUT_VALUE 0x2500 -#define CTRL_BRIGHTNESS 0x2900 -#define CTRL_CONTRAST 0x2a00 -#define CTRL_HUE 0x2b00 -#define CTRL_SATURATION 0x2c00 -#define CTRL_SHARPNESS 0x2d00 -#define CTRL_LOW_PASS_FILTER_VALUE 0x3100 - -#define CTRL_DEFAULT_INDEX 0x0003 - - - /* :0 s 38 01 1000 0003 0004 4 = 0a00ca00 - * BITRATE SETTING - * 1st and 2nd byte (little endian): average bitrate in 100 000 bit/s - * min: 1 mbit/s, max: 13.5 mbit/s - * 3rd and 4th byte (little endian): peak bitrate in 100 000 bit/s - * min: average + 100kbit/s, - * max: 20.2 mbit/s - */ - - /* :0 s 38 01 1200 0003 0001 1 = 02 - * BIT RATE MODE - * constant = 1, variable (peak) = 2, variable (average) = 3 - */ - - /* :0 s 38 01 1300 0003 0001 1 = 03 - * GOP MODE (2 bit) - * low bit 0/1: advanced/simple GOP - * high bit 0/1: IDR(4/32/128) / no IDR (4/32/0) - */ - - /* :0 s 38 01 1700 0003 0001 1 = 00 - * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz - */ - - /* :0 s 38 01 3100 0003 0004 4 = 03030000 - * FILTER CONTROL - * 1st byte luma low pass filter strength, - * 2nd byte chroma low pass filter strength, - * 3rd byte MF enable chroma, min=0, max=1 - * 4th byte n - */ - - - /* :0 s 38 b9 0001 0000 0000 0 */ - - - -/* :0 s 38 d3 0000 0000 0001 1 = 00 */ -/* ret = usb_control_msg(dev->udev, */ -/* usb_sndctrlpipe(dev->udev, 0), */ -/* 0xd3, 0x38, */ -/* 0, 0, */ -/* "\0", 1, */ -/* 1000); */ - -/* info("control request returned %d", ret); */ -/* msleep(5000); */ - - - /* :0 s b8 81 1400 0003 0005 5 < - * :0 0 5 = d0024002 19 - * QUERY FRAME SIZE AND RATE - * 1st and 2nd byte (little endian): horizontal resolution - * 3rd and 4th byte (little endian): vertical resolution - * 5th byte: frame rate - */ - - /* :0 s b8 81 1800 0003 0003 3 < - * :0 0 3 = 030104 - * QUERY SIGNAL AND DETECTED LINES, maybe INPUT - */ - -enum hdpvr_video_std { - HDPVR_60HZ = 0, - HDPVR_50HZ, -}; - -enum hdpvr_video_input { - HDPVR_COMPONENT = 0, - HDPVR_SVIDEO, - HDPVR_COMPOSITE, - HDPVR_VIDEO_INPUTS -}; - -enum hdpvr_audio_inputs { - HDPVR_RCA_BACK = 0, - HDPVR_RCA_FRONT, - HDPVR_SPDIF, - HDPVR_AUDIO_INPUTS -}; - -enum hdpvr_bitrate_mode { - HDPVR_CONSTANT = 1, - HDPVR_VARIABLE_PEAK, - HDPVR_VARIABLE_AVERAGE, -}; - -enum hdpvr_gop_mode { - HDPVR_ADVANCED_IDR_GOP = 0, - HDPVR_SIMPLE_IDR_GOP, - HDPVR_ADVANCED_NOIDR_GOP, - HDPVR_SIMPLE_NOIDR_GOP, -}; - -void hdpvr_delete(struct hdpvr_device *dev); - -/*========================================================================*/ -/* hardware control functions */ -int hdpvr_set_options(struct hdpvr_device *dev); - -int hdpvr_set_bitrate(struct hdpvr_device *dev); - -int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, - enum v4l2_mpeg_audio_encoding codec); - -int hdpvr_config_call(struct hdpvr_device *dev, uint value, - unsigned char valbuf); - -struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev); - -/* :0 s b8 81 1800 0003 0003 3 < */ -/* :0 0 3 = 0301ff */ -int get_input_lines_info(struct hdpvr_device *dev); - - -/*========================================================================*/ -/* v4l2 registration */ -int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, - int devnumber); - -int hdpvr_cancel_queue(struct hdpvr_device *dev); - -/*========================================================================*/ -/* i2c adapter registration */ -int hdpvr_register_i2c_adapter(struct hdpvr_device *dev); - -struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev); -struct i2c_client *hdpvr_register_ir_tx_i2c(struct hdpvr_device *dev); - -/*========================================================================*/ -/* buffer management */ -int hdpvr_free_buffers(struct hdpvr_device *dev); -int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count); diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig deleted file mode 100644 index 25e412ecad2c..000000000000 --- a/drivers/media/video/pvrusb2/Kconfig +++ /dev/null @@ -1,65 +0,0 @@ -config VIDEO_PVRUSB2 - tristate "Hauppauge WinTV-PVR USB2 support" - depends on VIDEO_V4L2 && I2C - select VIDEO_TUNER - select VIDEO_TVEEPROM - select VIDEO_CX2341X - select VIDEO_SAA711X - select VIDEO_CX25840 - select VIDEO_MSP3400 - select VIDEO_WM8775 - select VIDEO_CS53L32A - ---help--- - This is a video4linux driver for Conexant 23416 based - usb2 personal video recorder devices. - - To compile this driver as a module, choose M here: the - module will be called pvrusb2 - -config VIDEO_PVRUSB2_SYSFS - bool "pvrusb2 sysfs support (EXPERIMENTAL)" - default y - depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL - ---help--- - This option enables the operation of a sysfs based - interface for query and control of the pvrusb2 driver. - - This is not generally needed for v4l applications, - although certain applications are optimized to take - advantage of this feature. - - If you are in doubt, say Y. - - Note: This feature is experimental and subject to change. - -config VIDEO_PVRUSB2_DVB - bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)" - default y - depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE - ---help--- - - This option enables a DVB interface for the pvrusb2 driver. - If your device does not support digital television, this - feature will have no affect on the driver's operation. - - If you are in doubt, say Y. - -config VIDEO_PVRUSB2_DEBUGIFC - bool "pvrusb2 debug interface" - depends on VIDEO_PVRUSB2_SYSFS - ---help--- - This option enables the inclusion of a debug interface - in the pvrusb2 driver, hosted through sysfs. - - You do not need to select this option unless you plan - on debugging the driver or performing a manual firmware - extraction. - - If you are in doubt, say N. diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile deleted file mode 100644 index bc716db797e3..000000000000 --- a/drivers/media/video/pvrusb2/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o -obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o -obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o - -pvrusb2-objs := pvrusb2-i2c-core.o \ - pvrusb2-audio.o \ - pvrusb2-encoder.o pvrusb2-video-v4l.o \ - pvrusb2-eeprom.o \ - pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ - pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ - pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ - pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ - pvrusb2-cs53l32a.o \ - $(obj-pvrusb2-dvb-y) \ - $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) - -obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o - -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/tuners -ccflags-y += -Idrivers/media/dvb-core -ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c deleted file mode 100644 index cc06d5e4adcc..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-audio.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include -#include -#include - - -struct routing_scheme { - const int *def; - unsigned int cnt; -}; - -static const int routing_scheme0[] = { - [PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT, - [PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2, - MSP_IN_TUNER1, - MSP_DSP_IN_SCART, - MSP_DSP_IN_SCART), - [PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1, - MSP_IN_TUNER1, - MSP_DSP_IN_SCART, - MSP_DSP_IN_SCART), - [PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1, - MSP_IN_TUNER1, - MSP_DSP_IN_SCART, - MSP_DSP_IN_SCART), -}; - -static const struct routing_scheme routing_def0 = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), -}; - -static const struct routing_scheme *routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, -}; - -void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) -{ - if (hdw->input_dirty || hdw->force_dirty) { - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - u32 input; - - pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); - sp = (sid < ARRAY_SIZE(routing_schemes)) ? - routing_schemes[sid] : NULL; - - if ((sp != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - input = sp->def[hdw->input_val]; - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** subdev msp3400 set_input:" - " Invalid routing scheme (%u)" - " and/or input (%d)", - sid, hdw->input_val); - return; - } - sd->ops->audio->s_routing(sd, input, - MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); - } -} - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h deleted file mode 100644 index e3e63d750891..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_AUDIO_H -#define __PVRUSB2_AUDIO_H - -#include "pvrusb2-hdw-internal.h" -void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); -#endif /* __PVRUSB2_AUDIO_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c deleted file mode 100644 index 7c19ff72e6b3..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-context.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-context.h" -#include "pvrusb2-io.h" -#include "pvrusb2-ioread.h" -#include "pvrusb2-hdw.h" -#include "pvrusb2-debug.h" -#include -#include -#include -#include -#include - -static struct pvr2_context *pvr2_context_exist_first; -static struct pvr2_context *pvr2_context_exist_last; -static struct pvr2_context *pvr2_context_notify_first; -static struct pvr2_context *pvr2_context_notify_last; -static DEFINE_MUTEX(pvr2_context_mutex); -static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data); -static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data); -static int pvr2_context_cleanup_flag; -static int pvr2_context_cleaned_flag; -static struct task_struct *pvr2_context_thread_ptr; - - -static void pvr2_context_set_notify(struct pvr2_context *mp, int fl) -{ - int signal_flag = 0; - mutex_lock(&pvr2_context_mutex); - if (fl) { - if (!mp->notify_flag) { - signal_flag = (pvr2_context_notify_first == NULL); - mp->notify_prev = pvr2_context_notify_last; - mp->notify_next = NULL; - pvr2_context_notify_last = mp; - if (mp->notify_prev) { - mp->notify_prev->notify_next = mp; - } else { - pvr2_context_notify_first = mp; - } - mp->notify_flag = !0; - } - } else { - if (mp->notify_flag) { - mp->notify_flag = 0; - if (mp->notify_next) { - mp->notify_next->notify_prev = mp->notify_prev; - } else { - pvr2_context_notify_last = mp->notify_prev; - } - if (mp->notify_prev) { - mp->notify_prev->notify_next = mp->notify_next; - } else { - pvr2_context_notify_first = mp->notify_next; - } - } - } - mutex_unlock(&pvr2_context_mutex); - if (signal_flag) wake_up(&pvr2_context_sync_data); -} - - -static void pvr2_context_destroy(struct pvr2_context *mp) -{ - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); - if (mp->hdw) pvr2_hdw_destroy(mp->hdw); - pvr2_context_set_notify(mp, 0); - mutex_lock(&pvr2_context_mutex); - if (mp->exist_next) { - mp->exist_next->exist_prev = mp->exist_prev; - } else { - pvr2_context_exist_last = mp->exist_prev; - } - if (mp->exist_prev) { - mp->exist_prev->exist_next = mp->exist_next; - } else { - pvr2_context_exist_first = mp->exist_next; - } - if (!pvr2_context_exist_first) { - /* Trigger wakeup on control thread in case it is waiting - for an exit condition. */ - wake_up(&pvr2_context_sync_data); - } - mutex_unlock(&pvr2_context_mutex); - kfree(mp); -} - - -static void pvr2_context_notify(struct pvr2_context *mp) -{ - pvr2_context_set_notify(mp,!0); -} - - -static void pvr2_context_check(struct pvr2_context *mp) -{ - struct pvr2_channel *ch1, *ch2; - pvr2_trace(PVR2_TRACE_CTXT, - "pvr2_context %p (notify)", mp); - if (!mp->initialized_flag && !mp->disconnect_flag) { - mp->initialized_flag = !0; - pvr2_trace(PVR2_TRACE_CTXT, - "pvr2_context %p (initialize)", mp); - /* Finish hardware initialization */ - if (pvr2_hdw_initialize(mp->hdw, - (void (*)(void *))pvr2_context_notify, - mp)) { - mp->video_stream.stream = - pvr2_hdw_get_video_stream(mp->hdw); - /* Trigger interface initialization. By doing this - here initialization runs in our own safe and - cozy thread context. */ - if (mp->setup_func) mp->setup_func(mp); - } else { - pvr2_trace(PVR2_TRACE_CTXT, - "pvr2_context %p (thread skipping setup)", - mp); - /* Even though initialization did not succeed, - we're still going to continue anyway. We need - to do this in order to await the expected - disconnect (which we will detect in the normal - course of operation). */ - } - } - - for (ch1 = mp->mc_first; ch1; ch1 = ch2) { - ch2 = ch1->mc_next; - if (ch1->check_func) ch1->check_func(ch1); - } - - if (mp->disconnect_flag && !mp->mc_first) { - /* Go away... */ - pvr2_context_destroy(mp); - return; - } -} - - -static int pvr2_context_shutok(void) -{ - return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL); -} - - -static int pvr2_context_thread_func(void *foo) -{ - struct pvr2_context *mp; - - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start"); - - do { - while ((mp = pvr2_context_notify_first) != NULL) { - pvr2_context_set_notify(mp, 0); - pvr2_context_check(mp); - } - wait_event_interruptible( - pvr2_context_sync_data, - ((pvr2_context_notify_first != NULL) || - pvr2_context_shutok())); - } while (!pvr2_context_shutok()); - - pvr2_context_cleaned_flag = !0; - wake_up(&pvr2_context_cleanup_data); - - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up"); - - wait_event_interruptible( - pvr2_context_sync_data, - kthread_should_stop()); - - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end"); - - return 0; -} - - -int pvr2_context_global_init(void) -{ - pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func, - NULL, - "pvrusb2-context"); - return (pvr2_context_thread_ptr ? 0 : -ENOMEM); -} - - -void pvr2_context_global_done(void) -{ - pvr2_context_cleanup_flag = !0; - wake_up(&pvr2_context_sync_data); - wait_event_interruptible( - pvr2_context_cleanup_data, - pvr2_context_cleaned_flag); - kthread_stop(pvr2_context_thread_ptr); -} - - -struct pvr2_context *pvr2_context_create( - struct usb_interface *intf, - const struct usb_device_id *devid, - void (*setup_func)(struct pvr2_context *)) -{ - struct pvr2_context *mp = NULL; - mp = kzalloc(sizeof(*mp),GFP_KERNEL); - if (!mp) goto done; - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); - mp->setup_func = setup_func; - mutex_init(&mp->mutex); - mutex_lock(&pvr2_context_mutex); - mp->exist_prev = pvr2_context_exist_last; - mp->exist_next = NULL; - pvr2_context_exist_last = mp; - if (mp->exist_prev) { - mp->exist_prev->exist_next = mp; - } else { - pvr2_context_exist_first = mp; - } - mutex_unlock(&pvr2_context_mutex); - mp->hdw = pvr2_hdw_create(intf,devid); - if (!mp->hdw) { - pvr2_context_destroy(mp); - mp = NULL; - goto done; - } - pvr2_context_set_notify(mp, !0); - done: - return mp; -} - - -static void pvr2_context_reset_input_limits(struct pvr2_context *mp) -{ - unsigned int tmsk,mmsk; - struct pvr2_channel *cp; - struct pvr2_hdw *hdw = mp->hdw; - mmsk = pvr2_hdw_get_input_available(hdw); - tmsk = mmsk; - for (cp = mp->mc_first; cp; cp = cp->mc_next) { - if (!cp->input_mask) continue; - tmsk &= cp->input_mask; - } - pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk); - pvr2_hdw_commit_ctl(hdw); -} - - -static void pvr2_context_enter(struct pvr2_context *mp) -{ - mutex_lock(&mp->mutex); -} - - -static void pvr2_context_exit(struct pvr2_context *mp) -{ - int destroy_flag = 0; - if (!(mp->mc_first || !mp->disconnect_flag)) { - destroy_flag = !0; - } - mutex_unlock(&mp->mutex); - if (destroy_flag) pvr2_context_notify(mp); -} - - -void pvr2_context_disconnect(struct pvr2_context *mp) -{ - pvr2_hdw_disconnect(mp->hdw); - mp->disconnect_flag = !0; - pvr2_context_notify(mp); -} - - -void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) -{ - pvr2_context_enter(mp); - cp->hdw = mp->hdw; - cp->mc_head = mp; - cp->mc_next = NULL; - cp->mc_prev = mp->mc_last; - if (mp->mc_last) { - mp->mc_last->mc_next = cp; - } else { - mp->mc_first = cp; - } - mp->mc_last = cp; - pvr2_context_exit(mp); -} - - -static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) -{ - if (!cp->stream) return; - pvr2_stream_kill(cp->stream->stream); - cp->stream->user = NULL; - cp->stream = NULL; -} - - -void pvr2_channel_done(struct pvr2_channel *cp) -{ - struct pvr2_context *mp = cp->mc_head; - pvr2_context_enter(mp); - cp->input_mask = 0; - pvr2_channel_disclaim_stream(cp); - pvr2_context_reset_input_limits(mp); - if (cp->mc_next) { - cp->mc_next->mc_prev = cp->mc_prev; - } else { - mp->mc_last = cp->mc_prev; - } - if (cp->mc_prev) { - cp->mc_prev->mc_next = cp->mc_next; - } else { - mp->mc_first = cp->mc_next; - } - cp->hdw = NULL; - pvr2_context_exit(mp); -} - - -int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk) -{ - unsigned int tmsk,mmsk; - int ret = 0; - struct pvr2_channel *p2; - struct pvr2_hdw *hdw = cp->hdw; - - mmsk = pvr2_hdw_get_input_available(hdw); - cmsk &= mmsk; - if (cmsk == cp->input_mask) { - /* No change; nothing to do */ - return 0; - } - - pvr2_context_enter(cp->mc_head); - do { - if (!cmsk) { - cp->input_mask = 0; - pvr2_context_reset_input_limits(cp->mc_head); - break; - } - tmsk = mmsk; - for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) { - if (p2 == cp) continue; - if (!p2->input_mask) continue; - tmsk &= p2->input_mask; - } - if (!(tmsk & cmsk)) { - ret = -EPERM; - break; - } - tmsk &= cmsk; - if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) { - /* Internal failure changing allowed list; probably - should not happen, but react if it does. */ - break; - } - cp->input_mask = cmsk; - pvr2_hdw_commit_ctl(hdw); - } while (0); - pvr2_context_exit(cp->mc_head); - return ret; -} - - -unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) -{ - return cp->input_mask; -} - - -int pvr2_channel_claim_stream(struct pvr2_channel *cp, - struct pvr2_context_stream *sp) -{ - int code = 0; - pvr2_context_enter(cp->mc_head); do { - if (sp == cp->stream) break; - if (sp && sp->user) { - code = -EBUSY; - break; - } - pvr2_channel_disclaim_stream(cp); - if (!sp) break; - sp->user = cp; - cp->stream = sp; - } while (0); pvr2_context_exit(cp->mc_head); - return code; -} - - -// This is the marker for the real beginning of a legitimate mpeg2 stream. -static char stream_sync_key[] = { - 0x00, 0x00, 0x01, 0xba, -}; - -struct pvr2_ioread *pvr2_channel_create_mpeg_stream( - struct pvr2_context_stream *sp) -{ - struct pvr2_ioread *cp; - cp = pvr2_ioread_create(); - if (!cp) return NULL; - pvr2_ioread_setup(cp,sp->stream); - pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key)); - return cp; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h deleted file mode 100644 index d657e53bbfa3..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-context.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_CONTEXT_H -#define __PVRUSB2_CONTEXT_H - -#include -#include -#include - -struct pvr2_hdw; /* hardware interface - defined elsewhere */ -struct pvr2_stream; /* stream interface - defined elsewhere */ - -struct pvr2_context; /* All central state */ -struct pvr2_channel; /* One I/O pathway to a user */ -struct pvr2_context_stream; /* Wrapper for a stream */ -struct pvr2_ioread; /* Low level stream structure */ - -struct pvr2_context_stream { - struct pvr2_channel *user; - struct pvr2_stream *stream; -}; - -struct pvr2_context { - struct pvr2_channel *mc_first; - struct pvr2_channel *mc_last; - struct pvr2_context *exist_next; - struct pvr2_context *exist_prev; - struct pvr2_context *notify_next; - struct pvr2_context *notify_prev; - struct pvr2_hdw *hdw; - struct pvr2_context_stream video_stream; - struct mutex mutex; - int notify_flag; - int initialized_flag; - int disconnect_flag; - - /* Called after pvr2_context initialization is complete */ - void (*setup_func)(struct pvr2_context *); - -}; - -struct pvr2_channel { - struct pvr2_context *mc_head; - struct pvr2_channel *mc_next; - struct pvr2_channel *mc_prev; - struct pvr2_context_stream *stream; - struct pvr2_hdw *hdw; - unsigned int input_mask; - void (*check_func)(struct pvr2_channel *); -}; - -struct pvr2_context *pvr2_context_create(struct usb_interface *intf, - const struct usb_device_id *devid, - void (*setup_func)(struct pvr2_context *)); -void pvr2_context_disconnect(struct pvr2_context *); - -void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *); -void pvr2_channel_done(struct pvr2_channel *); -int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int); -unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *); -int pvr2_channel_claim_stream(struct pvr2_channel *, - struct pvr2_context_stream *); -struct pvr2_ioread *pvr2_channel_create_mpeg_stream( - struct pvr2_context_stream *); - -int pvr2_context_global_init(void); -void pvr2_context_global_done(void); - -#endif /* __PVRUSB2_CONTEXT_H */ -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c deleted file mode 100644 index 88320900dbd4..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* - - This source file is specifically designed to interface with the - v4l-dvb cs53l32a module. - -*/ - -#include "pvrusb2-cs53l32a.h" - - -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include -#include -#include - -struct routing_scheme { - const int *def; - unsigned int cnt; -}; - - -static const int routing_scheme1[] = { - [PVR2_CVAL_INPUT_TV] = 2, /* 1 or 2 seems to work here */ - [PVR2_CVAL_INPUT_RADIO] = 2, - [PVR2_CVAL_INPUT_COMPOSITE] = 0, - [PVR2_CVAL_INPUT_SVIDEO] = 0, -}; - -static const struct routing_scheme routing_def1 = { - .def = routing_scheme1, - .cnt = ARRAY_SIZE(routing_scheme1), -}; - -static const struct routing_scheme *routing_schemes[] = { - [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, -}; - - -void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) -{ - if (hdw->input_dirty || hdw->force_dirty) { - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - u32 input; - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", - hdw->input_val); - sp = (sid < ARRAY_SIZE(routing_schemes)) ? - routing_schemes[sid] : NULL; - if ((sp == NULL) || - (hdw->input_val < 0) || - (hdw->input_val >= sp->cnt)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** subdev v4l2 set_input:" - " Invalid routing scheme (%u)" - " and/or input (%d)", - sid, hdw->input_val); - return; - } - input = sp->def[hdw->input_val]; - sd->ops->audio->s_routing(sd, input, 0, 0); - } -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h deleted file mode 100644 index 53ba548b72a7..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_CS53L32A_H -#define __PVRUSB2_CS53L32A_H - -/* - - This module connects the pvrusb2 driver to the I2C chip level - driver which handles device video processing. This interface is - used internally by the driver; higher level code should only - interact through the interface provided by pvrusb2-hdw.h. - -*/ - - -#include "pvrusb2-hdw-internal.h" -void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); - -#endif /* __PVRUSB2_AUDIO_CS53L32A_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c deleted file mode 100644 index 7d5a7139a45a..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ /dev/null @@ -1,609 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-ctrl.h" -#include "pvrusb2-hdw-internal.h" -#include -#include -#include - - -static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val) -{ - if (cptr->info->check_value) { - if (!cptr->info->check_value(cptr,val)) return -ERANGE; - } else if (cptr->info->type == pvr2_ctl_enum) { - if (val < 0) return -ERANGE; - if (val >= cptr->info->def.type_enum.count) return -ERANGE; - } else { - int lim; - lim = cptr->info->def.type_int.min_value; - if (cptr->info->get_min_value) { - cptr->info->get_min_value(cptr,&lim); - } - if (val < lim) return -ERANGE; - lim = cptr->info->def.type_int.max_value; - if (cptr->info->get_max_value) { - cptr->info->get_max_value(cptr,&lim); - } - if (val > lim) return -ERANGE; - } - return 0; -} - - -/* Set the given control. */ -int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) -{ - return pvr2_ctrl_set_mask_value(cptr,~0,val); -} - - -/* Set/clear specific bits of the given control. */ -int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) -{ - int ret = 0; - if (!cptr) return -EINVAL; - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->set_value) { - if (cptr->info->type == pvr2_ctl_bitmask) { - mask &= cptr->info->def.type_bitmask.valid_bits; - } else if ((cptr->info->type == pvr2_ctl_int)|| - (cptr->info->type == pvr2_ctl_enum)) { - ret = pvr2_ctrl_range_check(cptr,val); - if (ret < 0) break; - } else if (cptr->info->type != pvr2_ctl_bool) { - break; - } - ret = cptr->info->set_value(cptr,mask,val); - } else { - ret = -EPERM; - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Get the current value of the given control. */ -int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr) -{ - int ret = 0; - if (!cptr) return -EINVAL; - LOCK_TAKE(cptr->hdw->big_lock); do { - ret = cptr->info->get_value(cptr,valptr); - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Retrieve control's type */ -enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) -{ - if (!cptr) return pvr2_ctl_int; - return cptr->info->type; -} - - -/* Retrieve control's maximum value (int type) */ -int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr) -{ - int ret = 0; - if (!cptr) return 0; - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->get_max_value) { - cptr->info->get_max_value(cptr,&ret); - } else if (cptr->info->type == pvr2_ctl_int) { - ret = cptr->info->def.type_int.max_value; - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Retrieve control's minimum value (int type) */ -int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) -{ - int ret = 0; - if (!cptr) return 0; - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->get_min_value) { - cptr->info->get_min_value(cptr,&ret); - } else if (cptr->info->type == pvr2_ctl_int) { - ret = cptr->info->def.type_int.min_value; - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Retrieve control's default value (any type) */ -int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr) -{ - int ret = 0; - if (!cptr) return -EINVAL; - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->get_def_value) { - ret = cptr->info->get_def_value(cptr, valptr); - } else { - *valptr = cptr->info->default_value; - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Retrieve control's enumeration count (enum only) */ -int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr) -{ - int ret = 0; - if (!cptr) return 0; - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->type == pvr2_ctl_enum) { - ret = cptr->info->def.type_enum.count; - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Retrieve control's valid mask bits (bit mask only) */ -int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr) -{ - int ret = 0; - if (!cptr) return 0; - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->type == pvr2_ctl_bitmask) { - ret = cptr->info->def.type_bitmask.valid_bits; - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Retrieve the control's name */ -const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) -{ - if (!cptr) return NULL; - return cptr->info->name; -} - - -/* Retrieve the control's desc */ -const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) -{ - if (!cptr) return NULL; - return cptr->info->desc; -} - - -/* Retrieve a control enumeration or bit mask value */ -int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, - char *bptr,unsigned int bmax, - unsigned int *blen) -{ - int ret = -EINVAL; - if (!cptr) return 0; - *blen = 0; - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->type == pvr2_ctl_enum) { - const char * const *names; - names = cptr->info->def.type_enum.value_names; - if (pvr2_ctrl_range_check(cptr,val) == 0) { - if (names[val]) { - *blen = scnprintf( - bptr,bmax,"%s", - names[val]); - } else { - *blen = 0; - } - ret = 0; - } - } else if (cptr->info->type == pvr2_ctl_bitmask) { - const char **names; - unsigned int idx; - int msk; - names = cptr->info->def.type_bitmask.bit_names; - val &= cptr->info->def.type_bitmask.valid_bits; - for (idx = 0, msk = 1; val; idx++, msk <<= 1) { - if (val & msk) { - *blen = scnprintf(bptr,bmax,"%s", - names[idx]); - ret = 0; - break; - } - } - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Return V4L ID for this control or zero if none */ -int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr) -{ - if (!cptr) return 0; - return cptr->info->v4l_id; -} - - -unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr) -{ - unsigned int flags = 0; - - if (cptr->info->get_v4lflags) { - flags = cptr->info->get_v4lflags(cptr); - } - - if (cptr->info->set_value) { - flags &= ~V4L2_CTRL_FLAG_READ_ONLY; - } else { - flags |= V4L2_CTRL_FLAG_READ_ONLY; - } - - return flags; -} - - -/* Return true if control is writable */ -int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr) -{ - if (!cptr) return 0; - return cptr->info->set_value != NULL; -} - - -/* Return true if control has custom symbolic representation */ -int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr) -{ - if (!cptr) return 0; - if (!cptr->info->val_to_sym) return 0; - if (!cptr->info->sym_to_val) return 0; - return !0; -} - - -/* Convert a given mask/val to a custom symbolic value */ -int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr, - int mask,int val, - char *buf,unsigned int maxlen, - unsigned int *len) -{ - if (!cptr) return -EINVAL; - if (!cptr->info->val_to_sym) return -EINVAL; - return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len); -} - - -/* Convert a symbolic value to a mask/value pair */ -int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr, - const char *buf,unsigned int len, - int *maskptr,int *valptr) -{ - if (!cptr) return -EINVAL; - if (!cptr->info->sym_to_val) return -EINVAL; - return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr); -} - - -static unsigned int gen_bitmask_string(int msk,int val,int msk_only, - const char **names, - char *ptr,unsigned int len) -{ - unsigned int idx; - long sm,um; - int spcFl; - unsigned int uc,cnt; - const char *idStr; - - spcFl = 0; - uc = 0; - um = 0; - for (idx = 0, sm = 1; msk; idx++, sm <<= 1) { - if (sm & msk) { - msk &= ~sm; - idStr = names[idx]; - if (idStr) { - cnt = scnprintf(ptr,len,"%s%s%s", - (spcFl ? " " : ""), - (msk_only ? "" : - ((val & sm) ? "+" : "-")), - idStr); - ptr += cnt; len -= cnt; uc += cnt; - spcFl = !0; - } else { - um |= sm; - } - } - } - if (um) { - if (msk_only) { - cnt = scnprintf(ptr,len,"%s0x%lx", - (spcFl ? " " : ""), - um); - ptr += cnt; len -= cnt; uc += cnt; - spcFl = !0; - } else if (um & val) { - cnt = scnprintf(ptr,len,"%s+0x%lx", - (spcFl ? " " : ""), - um & val); - ptr += cnt; len -= cnt; uc += cnt; - spcFl = !0; - } else if (um & ~val) { - cnt = scnprintf(ptr,len,"%s+0x%lx", - (spcFl ? " " : ""), - um & ~val); - ptr += cnt; len -= cnt; uc += cnt; - spcFl = !0; - } - } - return uc; -} - - -static const char *boolNames[] = { - "false", - "true", - "no", - "yes", -}; - - -static int parse_token(const char *ptr,unsigned int len, - int *valptr, - const char * const *names, unsigned int namecnt) -{ - char buf[33]; - unsigned int slen; - unsigned int idx; - int negfl; - char *p2; - *valptr = 0; - if (!names) namecnt = 0; - for (idx = 0; idx < namecnt; idx++) { - if (!names[idx]) continue; - slen = strlen(names[idx]); - if (slen != len) continue; - if (memcmp(names[idx],ptr,slen)) continue; - *valptr = idx; - return 0; - } - negfl = 0; - if ((*ptr == '-') || (*ptr == '+')) { - negfl = (*ptr == '-'); - ptr++; len--; - } - if (len >= sizeof(buf)) return -EINVAL; - memcpy(buf,ptr,len); - buf[len] = 0; - *valptr = simple_strtol(buf,&p2,0); - if (negfl) *valptr = -(*valptr); - if (*p2) return -EINVAL; - return 1; -} - - -static int parse_mtoken(const char *ptr,unsigned int len, - int *valptr, - const char **names,int valid_bits) -{ - char buf[33]; - unsigned int slen; - unsigned int idx; - char *p2; - int msk; - *valptr = 0; - for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) { - if (!(msk & valid_bits)) continue; - valid_bits &= ~msk; - if (!names[idx]) continue; - slen = strlen(names[idx]); - if (slen != len) continue; - if (memcmp(names[idx],ptr,slen)) continue; - *valptr = msk; - return 0; - } - if (len >= sizeof(buf)) return -EINVAL; - memcpy(buf,ptr,len); - buf[len] = 0; - *valptr = simple_strtol(buf,&p2,0); - if (*p2) return -EINVAL; - return 0; -} - - -static int parse_tlist(const char *ptr,unsigned int len, - int *maskptr,int *valptr, - const char **names,int valid_bits) -{ - unsigned int cnt; - int mask,val,kv,mode,ret; - mask = 0; - val = 0; - ret = 0; - while (len) { - cnt = 0; - while ((cnt < len) && - ((ptr[cnt] <= 32) || - (ptr[cnt] >= 127))) cnt++; - ptr += cnt; - len -= cnt; - mode = 0; - if ((*ptr == '-') || (*ptr == '+')) { - mode = (*ptr == '-') ? -1 : 1; - ptr++; - len--; - } - cnt = 0; - while (cnt < len) { - if (ptr[cnt] <= 32) break; - if (ptr[cnt] >= 127) break; - cnt++; - } - if (!cnt) break; - if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) { - ret = -EINVAL; - break; - } - ptr += cnt; - len -= cnt; - switch (mode) { - case 0: - mask = valid_bits; - val |= kv; - break; - case -1: - mask |= kv; - val &= ~kv; - break; - case 1: - mask |= kv; - val |= kv; - break; - default: - break; - } - } - *maskptr = mask; - *valptr = val; - return ret; -} - - -/* Convert a symbolic value to a mask/value pair */ -int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, - const char *ptr,unsigned int len, - int *maskptr,int *valptr) -{ - int ret = -EINVAL; - unsigned int cnt; - - *maskptr = 0; - *valptr = 0; - - cnt = 0; - while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++; - len -= cnt; ptr += cnt; - cnt = 0; - while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) || - (ptr[len-(cnt+1)] >= 127))) cnt++; - len -= cnt; - - if (!len) return -EINVAL; - - LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->type == pvr2_ctl_int) { - ret = parse_token(ptr,len,valptr,NULL,0); - if (ret >= 0) { - ret = pvr2_ctrl_range_check(cptr,*valptr); - } - *maskptr = ~0; - } else if (cptr->info->type == pvr2_ctl_bool) { - ret = parse_token(ptr,len,valptr,boolNames, - ARRAY_SIZE(boolNames)); - if (ret == 1) { - *valptr = *valptr ? !0 : 0; - } else if (ret == 0) { - *valptr = (*valptr & 1) ? !0 : 0; - } - *maskptr = 1; - } else if (cptr->info->type == pvr2_ctl_enum) { - ret = parse_token( - ptr,len,valptr, - cptr->info->def.type_enum.value_names, - cptr->info->def.type_enum.count); - if (ret >= 0) { - ret = pvr2_ctrl_range_check(cptr,*valptr); - } - *maskptr = ~0; - } else if (cptr->info->type == pvr2_ctl_bitmask) { - ret = parse_tlist( - ptr,len,maskptr,valptr, - cptr->info->def.type_bitmask.bit_names, - cptr->info->def.type_bitmask.valid_bits); - } - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Convert a given mask/val to a symbolic value */ -int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr, - int mask,int val, - char *buf,unsigned int maxlen, - unsigned int *len) -{ - int ret = -EINVAL; - - *len = 0; - if (cptr->info->type == pvr2_ctl_int) { - *len = scnprintf(buf,maxlen,"%d",val); - ret = 0; - } else if (cptr->info->type == pvr2_ctl_bool) { - *len = scnprintf(buf,maxlen,"%s",val ? "true" : "false"); - ret = 0; - } else if (cptr->info->type == pvr2_ctl_enum) { - const char * const *names; - names = cptr->info->def.type_enum.value_names; - if ((val >= 0) && - (val < cptr->info->def.type_enum.count)) { - if (names[val]) { - *len = scnprintf( - buf,maxlen,"%s", - names[val]); - } else { - *len = 0; - } - ret = 0; - } - } else if (cptr->info->type == pvr2_ctl_bitmask) { - *len = gen_bitmask_string( - val & mask & cptr->info->def.type_bitmask.valid_bits, - ~0,!0, - cptr->info->def.type_bitmask.bit_names, - buf,maxlen); - } - return ret; -} - - -/* Convert a given mask/val to a symbolic value */ -int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, - int mask,int val, - char *buf,unsigned int maxlen, - unsigned int *len) -{ - int ret; - LOCK_TAKE(cptr->hdw->big_lock); do { - ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val, - buf,maxlen,len); - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h deleted file mode 100644 index 794ff90121c7..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_CTRL_H -#define __PVRUSB2_CTRL_H - -struct pvr2_ctrl; - -enum pvr2_ctl_type { - pvr2_ctl_int = 0, - pvr2_ctl_enum = 1, - pvr2_ctl_bitmask = 2, - pvr2_ctl_bool = 3, -}; - - -/* Set the given control. */ -int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); - -/* Set/clear specific bits of the given control. */ -int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val); - -/* Get the current value of the given control. */ -int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr); - -/* Retrieve control's type */ -enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *); - -/* Retrieve control's maximum value (int type) */ -int pvr2_ctrl_get_max(struct pvr2_ctrl *); - -/* Retrieve control's minimum value (int type) */ -int pvr2_ctrl_get_min(struct pvr2_ctrl *); - -/* Retrieve control's default value (any type) */ -int pvr2_ctrl_get_def(struct pvr2_ctrl *, int *valptr); - -/* Retrieve control's enumeration count (enum only) */ -int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); - -/* Retrieve control's valid mask bits (bit mask only) */ -int pvr2_ctrl_get_mask(struct pvr2_ctrl *); - -/* Retrieve the control's name */ -const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); - -/* Retrieve the control's desc */ -const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); - -/* Retrieve a control enumeration or bit mask value */ -int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int, - unsigned int *); - -/* Return true if control is writable */ -int pvr2_ctrl_is_writable(struct pvr2_ctrl *); - -/* Return V4L flags value for control (or zero if there is no v4l control - actually under this control) */ -unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *); - -/* Return V4L ID for this control or zero if none */ -int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *); - -/* Return true if control has custom symbolic representation */ -int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *); - -/* Convert a given mask/val to a custom symbolic value */ -int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *, - int mask,int val, - char *buf,unsigned int maxlen, - unsigned int *len); - -/* Convert a symbolic value to a mask/value pair */ -int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *, - const char *buf,unsigned int len, - int *maskptr,int *valptr); - -/* Convert a given mask/val to a symbolic value */ -int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *, - int mask,int val, - char *buf,unsigned int maxlen, - unsigned int *len); - -/* Convert a symbolic value to a mask/value pair */ -int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *, - const char *buf,unsigned int len, - int *maskptr,int *valptr); - -/* Convert a given mask/val to a symbolic value - must already be - inside of critical region. */ -int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, - int mask,int val, - char *buf,unsigned int maxlen, - unsigned int *len); - -#endif /* __PVRUSB2_CTRL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c deleted file mode 100644 index c514d0b9ffdc..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* - - This source file is specifically designed to interface with the - cx2584x, in kernels 2.6.16 or newer. - -*/ - -#include "pvrusb2-cx2584x-v4l.h" -#include "pvrusb2-video-v4l.h" - - -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include -#include -#include -#include - - -struct routing_scheme_item { - int vid; - int aud; -}; - -struct routing_scheme { - const struct routing_scheme_item *def; - unsigned int cnt; -}; - -static const struct routing_scheme_item routing_scheme0[] = { - [PVR2_CVAL_INPUT_TV] = { - .vid = CX25840_COMPOSITE7, - .aud = CX25840_AUDIO8, - }, - [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ - .vid = CX25840_COMPOSITE3, - .aud = CX25840_AUDIO_SERIAL, - }, - [PVR2_CVAL_INPUT_COMPOSITE] = { - .vid = CX25840_COMPOSITE3, - .aud = CX25840_AUDIO_SERIAL, - }, - [PVR2_CVAL_INPUT_SVIDEO] = { - .vid = CX25840_SVIDEO1, - .aud = CX25840_AUDIO_SERIAL, - }, -}; - -static const struct routing_scheme routing_def0 = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), -}; - -/* Specific to gotview device */ -static const struct routing_scheme_item routing_schemegv[] = { - [PVR2_CVAL_INPUT_TV] = { - .vid = CX25840_COMPOSITE2, - .aud = CX25840_AUDIO5, - }, - [PVR2_CVAL_INPUT_RADIO] = { - /* line-in is used for radio and composite. A GPIO is - used to switch between the two choices. */ - .vid = CX25840_COMPOSITE1, - .aud = CX25840_AUDIO_SERIAL, - }, - [PVR2_CVAL_INPUT_COMPOSITE] = { - .vid = CX25840_COMPOSITE1, - .aud = CX25840_AUDIO_SERIAL, - }, - [PVR2_CVAL_INPUT_SVIDEO] = { - .vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4), - .aud = CX25840_AUDIO_SERIAL, - }, -}; - -static const struct routing_scheme routing_defgv = { - .def = routing_schemegv, - .cnt = ARRAY_SIZE(routing_schemegv), -}; - -/* Specific to grabster av400 device */ -static const struct routing_scheme_item routing_schemeav400[] = { - [PVR2_CVAL_INPUT_COMPOSITE] = { - .vid = CX25840_COMPOSITE1, - .aud = CX25840_AUDIO_SERIAL, - }, - [PVR2_CVAL_INPUT_SVIDEO] = { - .vid = (CX25840_SVIDEO_LUMA2|CX25840_SVIDEO_CHROMA4), - .aud = CX25840_AUDIO_SERIAL, - }, -}; - -static const struct routing_scheme routing_defav400 = { - .def = routing_schemeav400, - .cnt = ARRAY_SIZE(routing_schemeav400), -}; - -static const struct routing_scheme *routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, - [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, - [PVR2_ROUTING_SCHEME_AV400] = &routing_defav400, -}; - -void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) -{ - pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update..."); - if (hdw->input_dirty || hdw->force_dirty) { - enum cx25840_video_input vid_input; - enum cx25840_audio_input aud_input; - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - - sp = (sid < ARRAY_SIZE(routing_schemes)) ? - routing_schemes[sid] : NULL; - if ((sp == NULL) || - (hdw->input_val < 0) || - (hdw->input_val >= sp->cnt)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** subdev cx2584x set_input:" - " Invalid routing scheme (%u)" - " and/or input (%d)", - sid, hdw->input_val); - return; - } - vid_input = sp->def[hdw->input_val].vid; - aud_input = sp->def[hdw->input_val].aud; - pvr2_trace(PVR2_TRACE_CHIPS, - "subdev cx2584x set_input vid=0x%x aud=0x%x", - vid_input, aud_input); - sd->ops->video->s_routing(sd, (u32)vid_input, 0, 0); - sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0); - } -} - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h deleted file mode 100644 index e35c2322a08c..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_CX2584X_V4L_H -#define __PVRUSB2_CX2584X_V4L_H - -/* - - This module connects the pvrusb2 driver to the I2C chip level - driver which handles combined device audio & video processing. - This interface is used internally by the driver; higher level code - should only interact through the interface provided by - pvrusb2-hdw.h. - -*/ - - - -#include "pvrusb2-hdw-internal.h" - -void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); - - -#endif /* __PVRUSB2_CX2584X_V4L_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h deleted file mode 100644 index be79249f8628..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-debug.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_DEBUG_H -#define __PVRUSB2_DEBUG_H - -extern int pvrusb2_debug; - -#define pvr2_trace(msk, fmt, arg...) do {if(msk & pvrusb2_debug) printk(KERN_INFO "pvrusb2: " fmt "\n", ##arg); } while (0) - -/* These are listed in *rough* order of decreasing usefulness and - increasing noise level. */ -#define PVR2_TRACE_INFO (1 << 0) /* Normal messages */ -#define PVR2_TRACE_ERROR_LEGS (1 << 1) /* error messages */ -#define PVR2_TRACE_TOLERANCE (1 << 2) /* track tolerance-affected errors */ -#define PVR2_TRACE_TRAP (1 << 3) /* Trap & report app misbehavior */ -#define PVR2_TRACE_STD (1 << 4) /* Log video standard stuff */ -#define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */ -#define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */ -#define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */ -#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */ -#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */ -#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */ -#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */ -#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ -#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */ -#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */ -#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */ -#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */ -#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */ -#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */ -#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */ -#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */ -#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */ -#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */ -#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */ -#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */ -#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */ -#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */ -#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */ -#define PVR2_TRACE_DVB_FEED (1 << 28) /* DVB transport feed debug */ - - -#endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c deleted file mode 100644 index 4279ebb811a1..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include "pvrusb2-debugifc.h" -#include "pvrusb2-hdw.h" -#include "pvrusb2-debug.h" - -struct debugifc_mask_item { - const char *name; - unsigned long msk; -}; - - -static unsigned int debugifc_count_whitespace(const char *buf, - unsigned int count) -{ - unsigned int scnt; - char ch; - - for (scnt = 0; scnt < count; scnt++) { - ch = buf[scnt]; - if (ch == ' ') continue; - if (ch == '\t') continue; - if (ch == '\n') continue; - break; - } - return scnt; -} - - -static unsigned int debugifc_count_nonwhitespace(const char *buf, - unsigned int count) -{ - unsigned int scnt; - char ch; - - for (scnt = 0; scnt < count; scnt++) { - ch = buf[scnt]; - if (ch == ' ') break; - if (ch == '\t') break; - if (ch == '\n') break; - } - return scnt; -} - - -static unsigned int debugifc_isolate_word(const char *buf,unsigned int count, - const char **wstrPtr, - unsigned int *wlenPtr) -{ - const char *wptr; - unsigned int consume_cnt = 0; - unsigned int wlen; - unsigned int scnt; - - wptr = NULL; - wlen = 0; - scnt = debugifc_count_whitespace(buf,count); - consume_cnt += scnt; count -= scnt; buf += scnt; - if (!count) goto done; - - scnt = debugifc_count_nonwhitespace(buf,count); - if (!scnt) goto done; - wptr = buf; - wlen = scnt; - consume_cnt += scnt; count -= scnt; buf += scnt; - - done: - *wstrPtr = wptr; - *wlenPtr = wlen; - return consume_cnt; -} - - -static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, - u32 *num_ptr) -{ - u32 result = 0; - int radix = 10; - if ((count >= 2) && (buf[0] == '0') && - ((buf[1] == 'x') || (buf[1] == 'X'))) { - radix = 16; - count -= 2; - buf += 2; - } else if ((count >= 1) && (buf[0] == '0')) { - radix = 8; - } - - while (count--) { - int val = hex_to_bin(*buf++); - if (val < 0 || val >= radix) - return -EINVAL; - result *= radix; - result += val; - } - *num_ptr = result; - return 0; -} - - -static int debugifc_match_keyword(const char *buf,unsigned int count, - const char *keyword) -{ - unsigned int kl; - if (!keyword) return 0; - kl = strlen(keyword); - if (kl != count) return 0; - return !memcmp(buf,keyword,kl); -} - - -int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) -{ - int bcnt = 0; - int ccnt; - ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n", - pvr2_hdw_get_desc(hdw)); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Driver state info:\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = pvr2_hdw_state_report(hdw,buf,acnt); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - - return bcnt; -} - - -int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, - char *buf,unsigned int acnt) -{ - int bcnt = 0; - int ccnt; - int ret; - u32 gpio_dir,gpio_in,gpio_out; - struct pvr2_stream_stats stats; - struct pvr2_stream *sp; - - ret = pvr2_hdw_is_hsm(hdw); - ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", - (ret < 0 ? "FAIL" : (ret ? "high" : "full"))); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - - gpio_dir = 0; gpio_in = 0; gpio_out = 0; - pvr2_hdw_gpio_get_dir(hdw,&gpio_dir); - pvr2_hdw_gpio_get_out(hdw,&gpio_out); - pvr2_hdw_gpio_get_in(hdw,&gpio_in); - ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n", - gpio_dir,gpio_in,gpio_out); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - - ccnt = scnprintf(buf,acnt,"Streaming is %s\n", - pvr2_hdw_get_streaming(hdw) ? "on" : "off"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - - - sp = pvr2_hdw_get_video_stream(hdw); - if (sp) { - pvr2_stream_get_stats(sp, &stats, 0); - ccnt = scnprintf( - buf,acnt, - "Bytes streamed=%u" - " URBs: queued=%u idle=%u ready=%u" - " processed=%u failed=%u\n", - stats.bytes_processed, - stats.buffers_in_queue, - stats.buffers_in_idle, - stats.buffers_in_ready, - stats.buffers_processed, - stats.buffers_failed); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - } - - return bcnt; -} - - -static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, - unsigned int count) -{ - const char *wptr; - unsigned int wlen; - unsigned int scnt; - - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (!scnt) return 0; - count -= scnt; buf += scnt; - if (!wptr) return 0; - - pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr); - if (debugifc_match_keyword(wptr,wlen,"reset")) { - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (!scnt) return -EINVAL; - count -= scnt; buf += scnt; - if (!wptr) return -EINVAL; - if (debugifc_match_keyword(wptr,wlen,"cpu")) { - pvr2_hdw_cpureset_assert(hdw,!0); - pvr2_hdw_cpureset_assert(hdw,0); - return 0; - } else if (debugifc_match_keyword(wptr,wlen,"bus")) { - pvr2_hdw_device_reset(hdw); - } else if (debugifc_match_keyword(wptr,wlen,"soft")) { - return pvr2_hdw_cmd_powerup(hdw); - } else if (debugifc_match_keyword(wptr,wlen,"deep")) { - return pvr2_hdw_cmd_deep_reset(hdw); - } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { - return pvr2_upload_firmware2(hdw); - } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { - return pvr2_hdw_cmd_decoder_reset(hdw); - } else if (debugifc_match_keyword(wptr,wlen,"worker")) { - return pvr2_hdw_untrip(hdw); - } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) { - pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), - NULL, !0); - return 0; - } - return -EINVAL; - } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (!scnt) return -EINVAL; - count -= scnt; buf += scnt; - if (!wptr) return -EINVAL; - if (debugifc_match_keyword(wptr,wlen,"fetch")) { - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (scnt && wptr) { - count -= scnt; buf += scnt; - if (debugifc_match_keyword(wptr, wlen, - "prom")) { - pvr2_hdw_cpufw_set_enabled(hdw, 2, !0); - } else if (debugifc_match_keyword(wptr, wlen, - "ram8k")) { - pvr2_hdw_cpufw_set_enabled(hdw, 0, !0); - } else if (debugifc_match_keyword(wptr, wlen, - "ram16k")) { - pvr2_hdw_cpufw_set_enabled(hdw, 1, !0); - } else { - return -EINVAL; - } - } - pvr2_hdw_cpufw_set_enabled(hdw,0,!0); - return 0; - } else if (debugifc_match_keyword(wptr,wlen,"done")) { - pvr2_hdw_cpufw_set_enabled(hdw,0,0); - return 0; - } else { - return -EINVAL; - } - } else if (debugifc_match_keyword(wptr,wlen,"gpio")) { - int dir_fl = 0; - int ret; - u32 msk,val; - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (!scnt) return -EINVAL; - count -= scnt; buf += scnt; - if (!wptr) return -EINVAL; - if (debugifc_match_keyword(wptr,wlen,"dir")) { - dir_fl = !0; - } else if (!debugifc_match_keyword(wptr,wlen,"out")) { - return -EINVAL; - } - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (!scnt) return -EINVAL; - count -= scnt; buf += scnt; - if (!wptr) return -EINVAL; - ret = debugifc_parse_unsigned_number(wptr,wlen,&msk); - if (ret) return ret; - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (wptr) { - ret = debugifc_parse_unsigned_number(wptr,wlen,&val); - if (ret) return ret; - } else { - val = msk; - msk = 0xffffffff; - } - if (dir_fl) { - ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val); - } else { - ret = pvr2_hdw_gpio_chg_out(hdw,msk,val); - } - return ret; - } - pvr2_trace(PVR2_TRACE_DEBUGIFC, - "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr); - return -EINVAL; -} - - -int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, - unsigned int count) -{ - unsigned int bcnt = 0; - int ret; - - while (count) { - for (bcnt = 0; bcnt < count; bcnt++) { - if (buf[bcnt] == '\n') break; - } - - ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt); - if (ret < 0) return ret; - if (bcnt < count) bcnt++; - buf += bcnt; - count -= bcnt; - } - - return 0; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h deleted file mode 100644 index 2f8d46761cd0..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_DEBUGIFC_H -#define __PVRUSB2_DEBUGIFC_H - -struct pvr2_hdw; - -/* Print general status of driver. This will also trigger a probe of - the USB link. Unlike print_info(), this one synchronizes with the - driver so the information should be self-consistent (but it will - hang if the driver is wedged). */ -int pvr2_debugifc_print_info(struct pvr2_hdw *, - char *buf_ptr, unsigned int buf_size); - -/* Non-intrusively print some useful debugging info from inside the - driver. This should work even if the driver appears to be - wedged. */ -int pvr2_debugifc_print_status(struct pvr2_hdw *, - char *buf_ptr,unsigned int buf_size); - -/* Parse a string command into a driver action. */ -int pvr2_debugifc_docmd(struct pvr2_hdw *, - const char *buf_ptr,unsigned int buf_size); - -#endif /* __PVRUSB2_DEBUGIFC_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c deleted file mode 100644 index adc501d3c287..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * - * - * Copyright (C) 2007 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* - -This source file should encompass ALL per-device type information for the -driver. To define a new device, add elements to the pvr2_device_table and -pvr2_device_desc structures. - -*/ - -#include "pvrusb2-devattr.h" -#include -#include -/* This is needed in order to pull in tuner type ids... */ -#include -#include -#ifdef CONFIG_VIDEO_PVRUSB2_DVB -#include "pvrusb2-hdw-internal.h" -#include "lgdt330x.h" -#include "s5h1409.h" -#include "s5h1411.h" -#include "tda10048.h" -#include "tda18271.h" -#include "tda8290.h" -#include "tuner-simple.h" -#endif - - -/*------------------------------------------------------------------------*/ -/* Hauppauge PVR-USB2 Model 29xxx */ - -static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = { - { .module_id = PVR2_CLIENT_ID_SAA7115 }, - { .module_id = PVR2_CLIENT_ID_MSP3400 }, - { .module_id = PVR2_CLIENT_ID_TUNER }, - { .module_id = PVR2_CLIENT_ID_DEMOD }, -}; - -#define PVR2_FIRMWARE_29xxx "v4l-pvrusb2-29xxx-01.fw" -static const char *pvr2_fw1_names_29xxx[] = { - PVR2_FIRMWARE_29xxx, -}; - -static const struct pvr2_device_desc pvr2_device_29xxx = { - .description = "WinTV PVR USB2 Model 29xxx", - .shortname = "29xxx", - .client_table.lst = pvr2_cli_29xxx, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), - .fx2_firmware.lst = pvr2_fw1_names_29xxx, - .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), - .flag_has_hauppauge_rom = !0, - .flag_has_analogtuner = !0, - .flag_has_fmradio = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, - .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, - .ir_scheme = PVR2_IR_SCHEME_29XXX, -}; - - - -/*------------------------------------------------------------------------*/ -/* Hauppauge PVR-USB2 Model 24xxx */ - -static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = { - { .module_id = PVR2_CLIENT_ID_CX25840 }, - { .module_id = PVR2_CLIENT_ID_TUNER }, - { .module_id = PVR2_CLIENT_ID_WM8775 }, - { .module_id = PVR2_CLIENT_ID_DEMOD }, -}; - -#define PVR2_FIRMWARE_24xxx "v4l-pvrusb2-24xxx-01.fw" -static const char *pvr2_fw1_names_24xxx[] = { - PVR2_FIRMWARE_24xxx, -}; - -static const struct pvr2_device_desc pvr2_device_24xxx = { - .description = "WinTV PVR USB2 Model 24xxx", - .shortname = "24xxx", - .client_table.lst = pvr2_cli_24xxx, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), - .fx2_firmware.lst = pvr2_fw1_names_24xxx, - .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), - .flag_has_cx25840 = !0, - .flag_has_wm8775 = !0, - .flag_has_hauppauge_rom = !0, - .flag_has_analogtuner = !0, - .flag_has_fmradio = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, - .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, - .ir_scheme = PVR2_IR_SCHEME_24XXX, -}; - - - -/*------------------------------------------------------------------------*/ -/* GOTVIEW USB2.0 DVD2 */ - -static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = { - { .module_id = PVR2_CLIENT_ID_CX25840 }, - { .module_id = PVR2_CLIENT_ID_TUNER }, - { .module_id = PVR2_CLIENT_ID_DEMOD }, -}; - -static const struct pvr2_device_desc pvr2_device_gotview_2 = { - .description = "Gotview USB 2.0 DVD 2", - .shortname = "gv2", - .client_table.lst = pvr2_cli_gotview_2, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), - .flag_has_cx25840 = !0, - .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .flag_has_analogtuner = !0, - .flag_has_fmradio = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, -}; - - - -/*------------------------------------------------------------------------*/ -/* GOTVIEW USB2.0 DVD Deluxe */ - -/* (same module list as gotview_2) */ - -static const struct pvr2_device_desc pvr2_device_gotview_2d = { - .description = "Gotview USB 2.0 DVD Deluxe", - .shortname = "gv2d", - .client_table.lst = pvr2_cli_gotview_2, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), - .flag_has_cx25840 = !0, - .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .flag_has_analogtuner = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, -}; - - - -/*------------------------------------------------------------------------*/ -/* Terratec Grabster AV400 */ - -static const struct pvr2_device_client_desc pvr2_cli_av400[] = { - { .module_id = PVR2_CLIENT_ID_CX25840 }, -}; - -static const struct pvr2_device_desc pvr2_device_av400 = { - .description = "Terratec Grabster AV400", - .shortname = "av400", - .flag_is_experimental = 1, - .client_table.lst = pvr2_cli_av400, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_av400), - .flag_has_cx25840 = !0, - .flag_has_analogtuner = 0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400, -}; - - - -/*------------------------------------------------------------------------*/ -/* OnAir Creator */ - -#ifdef CONFIG_VIDEO_PVRUSB2_DVB -static struct lgdt330x_config pvr2_lgdt3303_config = { - .demod_address = 0x0e, - .demod_chip = LGDT3303, - .clock_polarity_flip = 1, -}; - -static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) -{ - adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) - return 0; - - return -EIO; -} - -static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) -{ - dvb_attach(simple_tuner_attach, adap->fe, - &adap->channel.hdw->i2c_adap, 0x61, - TUNER_LG_TDVS_H06XF); - - return 0; -} - -static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = { - .frontend_attach = pvr2_lgdt3303_attach, - .tuner_attach = pvr2_lgh06xf_attach, -}; -#endif - -static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = { - { .module_id = PVR2_CLIENT_ID_SAA7115 }, - { .module_id = PVR2_CLIENT_ID_CS53L32A }, - { .module_id = PVR2_CLIENT_ID_TUNER }, -}; - -static const struct pvr2_device_desc pvr2_device_onair_creator = { - .description = "OnAir Creator Hybrid USB tuner", - .shortname = "oac", - .client_table.lst = pvr2_cli_onair_creator, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator), - .default_tuner_type = TUNER_LG_TDVS_H06XF, - .flag_has_analogtuner = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .flag_digital_requires_cx23416 = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, - .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, - .default_std_mask = V4L2_STD_NTSC_M, -#ifdef CONFIG_VIDEO_PVRUSB2_DVB - .dvb_props = &pvr2_onair_creator_fe_props, -#endif -}; - - - -/*------------------------------------------------------------------------*/ -/* OnAir USB 2.0 */ - -#ifdef CONFIG_VIDEO_PVRUSB2_DVB -static struct lgdt330x_config pvr2_lgdt3302_config = { - .demod_address = 0x0e, - .demod_chip = LGDT3302, -}; - -static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) -{ - adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) - return 0; - - return -EIO; -} - -static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) -{ - dvb_attach(simple_tuner_attach, adap->fe, - &adap->channel.hdw->i2c_adap, 0x61, - TUNER_PHILIPS_FCV1236D); - - return 0; -} - -static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { - .frontend_attach = pvr2_lgdt3302_attach, - .tuner_attach = pvr2_fcv1236d_attach, -}; -#endif - -static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = { - { .module_id = PVR2_CLIENT_ID_SAA7115 }, - { .module_id = PVR2_CLIENT_ID_CS53L32A }, - { .module_id = PVR2_CLIENT_ID_TUNER }, -}; - -static const struct pvr2_device_desc pvr2_device_onair_usb2 = { - .description = "OnAir USB2 Hybrid USB tuner", - .shortname = "oa2", - .client_table.lst = pvr2_cli_onair_usb2, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2), - .default_tuner_type = TUNER_PHILIPS_FCV1236D, - .flag_has_analogtuner = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .flag_digital_requires_cx23416 = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, - .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, - .default_std_mask = V4L2_STD_NTSC_M, -#ifdef CONFIG_VIDEO_PVRUSB2_DVB - .dvb_props = &pvr2_onair_usb2_fe_props, -#endif -}; - - - -/*------------------------------------------------------------------------*/ -/* Hauppauge PVR-USB2 Model 73xxx */ - -#ifdef CONFIG_VIDEO_PVRUSB2_DVB -static struct tda10048_config hauppauge_tda10048_config = { - .demod_address = 0x10 >> 1, - .output_mode = TDA10048_PARALLEL_OUTPUT, - .fwbulkwritelen = TDA10048_BULKWRITE_50, - .inversion = TDA10048_INVERSION_ON, - .dtv6_if_freq_khz = TDA10048_IF_3300, - .dtv7_if_freq_khz = TDA10048_IF_3800, - .dtv8_if_freq_khz = TDA10048_IF_4300, - .clk_freq_khz = TDA10048_CLK_16000, - .disable_gate_access = 1, -}; - -static struct tda829x_config tda829x_no_probe = { - .probe_tuner = TDA829X_DONT_PROBE, -}; - -static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = { - .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x37, }, - .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, - .if_lvl = 1, .rfagc_top = 0x37, }, - .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, - .if_lvl = 1, .rfagc_top = 0x37, }, -}; - -static struct tda18271_config hauppauge_tda18271_dvb_config = { - .std_map = &hauppauge_tda18271_dvbt_std_map, - .gate = TDA18271_GATE_ANALOG, - .output_opt = TDA18271_OUTPUT_LT_OFF, -}; - -static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) -{ - adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) - return 0; - - return -EIO; -} - -static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) -{ - dvb_attach(tda829x_attach, adap->fe, - &adap->channel.hdw->i2c_adap, 0x42, - &tda829x_no_probe); - dvb_attach(tda18271_attach, adap->fe, 0x60, - &adap->channel.hdw->i2c_adap, - &hauppauge_tda18271_dvb_config); - - return 0; -} - -static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = { - .frontend_attach = pvr2_tda10048_attach, - .tuner_attach = pvr2_73xxx_tda18271_8295_attach, -}; -#endif - -static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = { - { .module_id = PVR2_CLIENT_ID_CX25840 }, - { .module_id = PVR2_CLIENT_ID_TUNER, - .i2c_address_list = "\x42"}, -}; - -#define PVR2_FIRMWARE_73xxx "v4l-pvrusb2-73xxx-01.fw" -static const char *pvr2_fw1_names_73xxx[] = { - PVR2_FIRMWARE_73xxx, -}; - -static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV HVR-1900 Model 73xxx", - .shortname = "73xxx", - .client_table.lst = pvr2_cli_73xxx, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), - .fx2_firmware.lst = pvr2_fw1_names_73xxx, - .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx), - .flag_has_cx25840 = !0, - .flag_has_hauppauge_rom = !0, - .flag_has_analogtuner = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .flag_fx2_16kb = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, - .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, - .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, - .ir_scheme = PVR2_IR_SCHEME_ZILOG, -#ifdef CONFIG_VIDEO_PVRUSB2_DVB - .dvb_props = &pvr2_73xxx_dvb_props, -#endif -}; - - - -/*------------------------------------------------------------------------*/ -/* Hauppauge PVR-USB2 Model 75xxx */ - -#ifdef CONFIG_VIDEO_PVRUSB2_DVB -static struct s5h1409_config pvr2_s5h1409_config = { - .demod_address = 0x32 >> 1, - .output_mode = S5H1409_PARALLEL_OUTPUT, - .gpio = S5H1409_GPIO_OFF, - .qam_if = 4000, - .inversion = S5H1409_INVERSION_ON, - .status_mode = S5H1409_DEMODLOCKING, -}; - -static struct s5h1411_config pvr2_s5h1411_config = { - .output_mode = S5H1411_PARALLEL_OUTPUT, - .gpio = S5H1411_GPIO_OFF, - .vsb_if = S5H1411_IF_44000, - .qam_if = S5H1411_IF_4000, - .inversion = S5H1411_INVERSION_ON, - .status_mode = S5H1411_DEMODLOCKING, -}; - -static struct tda18271_std_map hauppauge_tda18271_std_map = { - .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, - .if_lvl = 6, .rfagc_top = 0x37, }, - .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, - .if_lvl = 6, .rfagc_top = 0x37, }, -}; - -static struct tda18271_config hauppauge_tda18271_config = { - .std_map = &hauppauge_tda18271_std_map, - .gate = TDA18271_GATE_ANALOG, - .output_opt = TDA18271_OUTPUT_LT_OFF, -}; - -static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) -{ - adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) - return 0; - - return -EIO; -} - -static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap) -{ - adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config, - &adap->channel.hdw->i2c_adap); - if (adap->fe) - return 0; - - return -EIO; -} - -static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) -{ - dvb_attach(tda829x_attach, adap->fe, - &adap->channel.hdw->i2c_adap, 0x42, - &tda829x_no_probe); - dvb_attach(tda18271_attach, adap->fe, 0x60, - &adap->channel.hdw->i2c_adap, - &hauppauge_tda18271_config); - - return 0; -} - -static const struct pvr2_dvb_props pvr2_750xx_dvb_props = { - .frontend_attach = pvr2_s5h1409_attach, - .tuner_attach = pvr2_tda18271_8295_attach, -}; - -static const struct pvr2_dvb_props pvr2_751xx_dvb_props = { - .frontend_attach = pvr2_s5h1411_attach, - .tuner_attach = pvr2_tda18271_8295_attach, -}; -#endif - -#define PVR2_FIRMWARE_75xxx "v4l-pvrusb2-73xxx-01.fw" -static const char *pvr2_fw1_names_75xxx[] = { - PVR2_FIRMWARE_75xxx, -}; - -static const struct pvr2_device_desc pvr2_device_750xx = { - .description = "WinTV HVR-1950 Model 750xx", - .shortname = "750xx", - .client_table.lst = pvr2_cli_73xxx, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), - .fx2_firmware.lst = pvr2_fw1_names_75xxx, - .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), - .flag_has_cx25840 = !0, - .flag_has_hauppauge_rom = !0, - .flag_has_analogtuner = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .flag_fx2_16kb = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, - .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, - .default_std_mask = V4L2_STD_NTSC_M, - .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, - .ir_scheme = PVR2_IR_SCHEME_ZILOG, -#ifdef CONFIG_VIDEO_PVRUSB2_DVB - .dvb_props = &pvr2_750xx_dvb_props, -#endif -}; - -static const struct pvr2_device_desc pvr2_device_751xx = { - .description = "WinTV HVR-1950 Model 751xx", - .shortname = "751xx", - .client_table.lst = pvr2_cli_73xxx, - .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), - .fx2_firmware.lst = pvr2_fw1_names_75xxx, - .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), - .flag_has_cx25840 = !0, - .flag_has_hauppauge_rom = !0, - .flag_has_analogtuner = !0, - .flag_has_composite = !0, - .flag_has_svideo = !0, - .flag_fx2_16kb = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, - .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, - .default_std_mask = V4L2_STD_NTSC_M, - .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, - .ir_scheme = PVR2_IR_SCHEME_ZILOG, -#ifdef CONFIG_VIDEO_PVRUSB2_DVB - .dvb_props = &pvr2_751xx_dvb_props, -#endif -}; - - - -/*------------------------------------------------------------------------*/ - -struct usb_device_id pvr2_device_table[] = { - { USB_DEVICE(0x2040, 0x2900), - .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, - { USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */ - .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, - { USB_DEVICE(0x2040, 0x2400), - .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, - { USB_DEVICE(0x1164, 0x0622), - .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2}, - { USB_DEVICE(0x1164, 0x0602), - .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d}, - { USB_DEVICE(0x11ba, 0x1003), - .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator}, - { USB_DEVICE(0x11ba, 0x1001), - .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2}, - { USB_DEVICE(0x2040, 0x7300), - .driver_info = (kernel_ulong_t)&pvr2_device_73xxx}, - { USB_DEVICE(0x2040, 0x7500), - .driver_info = (kernel_ulong_t)&pvr2_device_750xx}, - { USB_DEVICE(0x2040, 0x7501), - .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, - { USB_DEVICE(0x0ccd, 0x0039), - .driver_info = (kernel_ulong_t)&pvr2_device_av400}, - { } -}; - -MODULE_DEVICE_TABLE(usb, pvr2_device_table); -MODULE_FIRMWARE(PVR2_FIRMWARE_29xxx); -MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx); -MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx); -MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx); - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h deleted file mode 100644 index 273c8d4b3853..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_DEVATTR_H -#define __PVRUSB2_DEVATTR_H - -#include -#include -#ifdef CONFIG_VIDEO_PVRUSB2_DVB -#include "pvrusb2-dvb.h" -#endif - -/* - - This header defines structures used to describe attributes of a device. - -*/ - - -#define PVR2_CLIENT_ID_NULL 0 -#define PVR2_CLIENT_ID_MSP3400 1 -#define PVR2_CLIENT_ID_CX25840 2 -#define PVR2_CLIENT_ID_SAA7115 3 -#define PVR2_CLIENT_ID_TUNER 4 -#define PVR2_CLIENT_ID_CS53L32A 5 -#define PVR2_CLIENT_ID_WM8775 6 -#define PVR2_CLIENT_ID_DEMOD 7 - -struct pvr2_device_client_desc { - /* One ovr PVR2_CLIENT_ID_xxxx */ - unsigned char module_id; - - /* Null-terminated array of I2C addresses to try in order - initialize the module. It's safe to make this null terminated - since we're never going to encounter an i2c device with an - address of zero. If this is a null pointer or zero-length, - then no I2C addresses have been specified, in which case we'll - try some compiled in defaults for now. */ - unsigned char *i2c_address_list; -}; - -struct pvr2_device_client_table { - const struct pvr2_device_client_desc *lst; - unsigned char cnt; -}; - - -struct pvr2_string_table { - const char **lst; - unsigned int cnt; -}; - -#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 -#define PVR2_ROUTING_SCHEME_GOTVIEW 1 -#define PVR2_ROUTING_SCHEME_ONAIR 2 -#define PVR2_ROUTING_SCHEME_AV400 3 - -#define PVR2_DIGITAL_SCHEME_NONE 0 -#define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 -#define PVR2_DIGITAL_SCHEME_ONAIR 2 - -#define PVR2_LED_SCHEME_NONE 0 -#define PVR2_LED_SCHEME_HAUPPAUGE 1 - -#define PVR2_IR_SCHEME_NONE 0 -#define PVR2_IR_SCHEME_24XXX 1 /* FX2-controlled IR */ -#define PVR2_IR_SCHEME_ZILOG 2 /* HVR-1950 style (must be taken out of reset) */ -#define PVR2_IR_SCHEME_24XXX_MCE 3 /* 24xxx MCE device */ -#define PVR2_IR_SCHEME_29XXX 4 /* Original 29xxx device */ - -/* This describes a particular hardware type (except for the USB device ID - which must live in a separate structure due to environmental - constraints). See the top of pvrusb2-hdw.c for where this is - instantiated. */ -struct pvr2_device_desc { - /* Single line text description of hardware */ - const char *description; - - /* Single token identifier for hardware */ - const char *shortname; - - /* List of additional client modules we need to load */ - struct pvr2_string_table client_modules; - - /* List of defined client modules we need to load */ - struct pvr2_device_client_table client_table; - - /* List of FX2 firmware file names we should search; if empty then - FX2 firmware check / load is skipped and we assume the device - was initialized from internal ROM. */ - struct pvr2_string_table fx2_firmware; - -#ifdef CONFIG_VIDEO_PVRUSB2_DVB - /* callback functions to handle attachment of digital tuner & demod */ - const struct pvr2_dvb_props *dvb_props; - -#endif - /* Initial standard bits to use for this device, if not zero. - Anything set here is also implied as an available standard. - Note: This is ignored if overridden on the module load line via - the video_std module option. */ - v4l2_std_id default_std_mask; - - /* V4L tuner type ID to use with this device (only used if the - driver could not discover the type any other way). */ - int default_tuner_type; - - /* Signal routing scheme used by device, contains one of - PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we - encounter them. This is an arbitrary integer scheme id; its - meaning is contained entirely within the driver and is - interpreted by logic which must send commands to the chip-level - drivers (search for things which touch this field). */ - unsigned char signal_routing_scheme; - - /* Indicates scheme for controlling device's LED (if any). The - driver will turn on the LED when streaming is underway. This - contains one of PVR2_LED_SCHEME_XXX. */ - unsigned char led_scheme; - - /* Control scheme to use if there is a digital tuner. This - contains one of PVR2_DIGITAL_SCHEME_XXX. This is an arbitrary - integer scheme id; its meaning is contained entirely within the - driver and is interpreted by logic which must control the - streaming pathway (search for things which touch this field). */ - unsigned char digital_control_scheme; - - /* If set, we don't bother trying to load cx23416 firmware. */ - unsigned int flag_skip_cx23416_firmware:1; - - /* If set, the encoder must be healthy in order for digital mode to - work (otherwise we assume that digital streaming will work even - if we fail to locate firmware for the encoder). If the device - doesn't support digital streaming then this flag has no - effect. */ - unsigned int flag_digital_requires_cx23416:1; - - /* Device has a hauppauge eeprom which we can interrogate. */ - unsigned int flag_has_hauppauge_rom:1; - - /* Device does not require a powerup command to be issued. */ - unsigned int flag_no_powerup:1; - - /* Device has a cx25840 - this enables special additional logic to - handle it. */ - unsigned int flag_has_cx25840:1; - - /* Device has a wm8775 - this enables special additional logic to - ensure that it is found. */ - unsigned int flag_has_wm8775:1; - - /* Indicate IR scheme of hardware. If not set, then it is assumed - that IR can work without any help from the driver. */ - unsigned int ir_scheme:3; - - /* These bits define which kinds of sources the device can handle. - Note: Digital tuner presence is inferred by the - digital_control_scheme enumeration. */ - unsigned int flag_has_fmradio:1; /* Has FM radio receiver */ - unsigned int flag_has_analogtuner:1; /* Has analog tuner */ - unsigned int flag_has_composite:1; /* Has composite input */ - unsigned int flag_has_svideo:1; /* Has s-video input */ - unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */ - - /* If this driver is considered experimental, i.e. not all aspects - are working correctly and/or it is untested, mark that fact - with this flag. */ - unsigned int flag_is_experimental:1; -}; - -extern struct usb_device_id pvr2_device_table[]; - -#endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c deleted file mode 100644 index 8c95793433e7..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver. - * - * 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include "dvbdev.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-hdw.h" -#include "pvrusb2-io.h" -#include "pvrusb2-dvb.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) -{ - int ret; - unsigned int count; - struct pvr2_buffer *bp; - struct pvr2_stream *stream; - - pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started"); - set_freezable(); - - stream = adap->channel.stream->stream; - - for (;;) { - if (kthread_should_stop()) break; - - /* Not sure about this... */ - try_to_freeze(); - - bp = pvr2_stream_get_ready_buffer(stream); - if (bp != NULL) { - count = pvr2_buffer_get_count(bp); - if (count) { - dvb_dmx_swfilter( - &adap->demux, - adap->buffer_storage[ - pvr2_buffer_get_id(bp)], - count); - } else { - ret = pvr2_buffer_get_status(bp); - if (ret < 0) break; - } - ret = pvr2_buffer_queue(bp); - if (ret < 0) break; - - /* Since we know we did something to a buffer, - just go back and try again. No point in - blocking unless we really ran out of - buffers to process. */ - continue; - } - - - /* Wait until more buffers become available or we're - told not to wait any longer. */ - ret = wait_event_interruptible( - adap->buffer_wait_data, - (pvr2_stream_get_ready_count(stream) > 0) || - kthread_should_stop()); - if (ret < 0) break; - } - - /* If we get here and ret is < 0, then an error has occurred. - Probably would be a good idea to communicate that to DVB core... */ - - pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped"); - - return 0; -} - -static int pvr2_dvb_feed_thread(void *data) -{ - int stat = pvr2_dvb_feed_func(data); - /* from videobuf-dvb.c: */ - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - return stat; -} - -static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap) -{ - wake_up(&adap->buffer_wait_data); -} - -static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap) -{ - unsigned int idx; - struct pvr2_stream *stream; - - if (adap->thread) { - kthread_stop(adap->thread); - adap->thread = NULL; - } - - if (adap->channel.stream) { - stream = adap->channel.stream->stream; - } else { - stream = NULL; - } - if (stream) { - pvr2_hdw_set_streaming(adap->channel.hdw, 0); - pvr2_stream_set_callback(stream, NULL, NULL); - pvr2_stream_kill(stream); - pvr2_stream_set_buffer_count(stream, 0); - pvr2_channel_claim_stream(&adap->channel, NULL); - } - - if (adap->stream_run) { - for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { - if (!(adap->buffer_storage[idx])) continue; - kfree(adap->buffer_storage[idx]); - adap->buffer_storage[idx] = NULL; - } - adap->stream_run = 0; - } -} - -static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap) -{ - struct pvr2_context *pvr = adap->channel.mc_head; - unsigned int idx; - int ret; - struct pvr2_buffer *bp; - struct pvr2_stream *stream = NULL; - - if (adap->stream_run) return -EIO; - - ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream); - /* somebody else already has the stream */ - if (ret < 0) return ret; - - stream = adap->channel.stream->stream; - - for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { - adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE, - GFP_KERNEL); - if (!(adap->buffer_storage[idx])) return -ENOMEM; - } - - pvr2_stream_set_callback(pvr->video_stream.stream, - (pvr2_stream_callback) pvr2_dvb_notify, adap); - - ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT); - if (ret < 0) return ret; - - for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { - bp = pvr2_stream_get_buffer(stream, idx); - pvr2_buffer_set_buffer(bp, - adap->buffer_storage[idx], - PVR2_DVB_BUFFER_SIZE); - } - - ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1); - if (ret < 0) return ret; - - while ((bp = pvr2_stream_get_idle_buffer(stream)) != NULL) { - ret = pvr2_buffer_queue(bp); - if (ret < 0) return ret; - } - - adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb"); - - if (IS_ERR(adap->thread)) { - ret = PTR_ERR(adap->thread); - adap->thread = NULL; - return ret; - } - - adap->stream_run = !0; - - return 0; -} - -static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap) -{ - int ret = pvr2_dvb_stream_do_start(adap); - if (ret < 0) pvr2_dvb_stream_end(adap); - return ret; -} - -static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) -{ - struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv; - int ret = 0; - - if (adap == NULL) return -ENODEV; - - mutex_lock(&adap->lock); - do { - if (onoff) { - if (!adap->feedcount) { - pvr2_trace(PVR2_TRACE_DVB_FEED, - "start feeding demux"); - ret = pvr2_dvb_stream_start(adap); - if (ret < 0) break; - } - (adap->feedcount)++; - } else if (adap->feedcount > 0) { - (adap->feedcount)--; - if (!adap->feedcount) { - pvr2_trace(PVR2_TRACE_DVB_FEED, - "stop feeding demux"); - pvr2_dvb_stream_end(adap); - } - } - } while (0); - mutex_unlock(&adap->lock); - - return ret; -} - -static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid); - return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1); -} - -static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid); - return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0); -} - -static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) -{ - struct pvr2_dvb_adapter *adap = fe->dvb->priv; - return pvr2_channel_limit_inputs( - &adap->channel, - (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0)); -} - -static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap) -{ - int ret; - - ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb", - THIS_MODULE/*&hdw->usb_dev->owner*/, - &adap->channel.hdw->usb_dev->dev, - adapter_nr); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "dvb_register_adapter failed: error %d", ret); - goto err; - } - adap->dvb_adap.priv = adap; - - adap->demux.dmx.capabilities = DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - adap->demux.priv = adap; - adap->demux.filternum = 256; - adap->demux.feednum = 256; - adap->demux.start_feed = pvr2_dvb_start_feed; - adap->demux.stop_feed = pvr2_dvb_stop_feed; - adap->demux.write_to_decoder = NULL; - - ret = dvb_dmx_init(&adap->demux); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "dvb_dmx_init failed: error %d", ret); - goto err_dmx; - } - - adap->dmxdev.filternum = adap->demux.filternum; - adap->dmxdev.demux = &adap->demux.dmx; - adap->dmxdev.capabilities = 0; - - ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "dvb_dmxdev_init failed: error %d", ret); - goto err_dmx_dev; - } - - dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); - - return 0; - -err_dmx_dev: - dvb_dmx_release(&adap->demux); -err_dmx: - dvb_unregister_adapter(&adap->dvb_adap); -err: - return ret; -} - -static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) -{ - pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices"); - dvb_net_release(&adap->dvb_net); - adap->demux.dmx.close(&adap->demux.dmx); - dvb_dmxdev_release(&adap->dmxdev); - dvb_dmx_release(&adap->demux); - dvb_unregister_adapter(&adap->dvb_adap); - return 0; -} - -static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) -{ - struct pvr2_hdw *hdw = adap->channel.hdw; - const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; - int ret = 0; - - if (dvb_props == NULL) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!"); - return -EINVAL; - } - - ret = pvr2_channel_limit_inputs( - &adap->channel, - (1 << PVR2_CVAL_INPUT_DTV)); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "failed to grab control of dtv input (code=%d)", - ret); - return ret; - } - - if (dvb_props->frontend_attach == NULL) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "frontend_attach not defined!"); - ret = -EINVAL; - goto done; - } - - if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { - - if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "frontend registration failed!"); - dvb_frontend_detach(adap->fe); - adap->fe = NULL; - ret = -ENODEV; - goto done; - } - - if (dvb_props->tuner_attach) - dvb_props->tuner_attach(adap); - - if (adap->fe->ops.analog_ops.standby) - adap->fe->ops.analog_ops.standby(adap->fe); - - /* Ensure all frontends negotiate bus access */ - adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; - - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "no frontend was attached!"); - ret = -ENODEV; - return ret; - } - - done: - pvr2_channel_limit_inputs(&adap->channel, 0); - return ret; -} - -static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) -{ - if (adap->fe != NULL) { - dvb_unregister_frontend(adap->fe); - dvb_frontend_detach(adap->fe); - } - return 0; -} - -static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap) -{ - pvr2_dvb_stream_end(adap); - pvr2_dvb_frontend_exit(adap); - pvr2_dvb_adapter_exit(adap); - pvr2_channel_done(&adap->channel); - kfree(adap); -} - -static void pvr2_dvb_internal_check(struct pvr2_channel *chp) -{ - struct pvr2_dvb_adapter *adap; - adap = container_of(chp, struct pvr2_dvb_adapter, channel); - if (!adap->channel.mc_head->disconnect_flag) return; - pvr2_dvb_destroy(adap); -} - -struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr) -{ - int ret = 0; - struct pvr2_dvb_adapter *adap; - if (!pvr->hdw->hdw_desc->dvb_props) { - /* Device lacks a digital interface so don't set up - the DVB side of the driver either. For now. */ - return NULL; - } - adap = kzalloc(sizeof(*adap), GFP_KERNEL); - if (!adap) return adap; - pvr2_channel_init(&adap->channel, pvr); - adap->channel.check_func = pvr2_dvb_internal_check; - init_waitqueue_head(&adap->buffer_wait_data); - mutex_init(&adap->lock); - ret = pvr2_dvb_adapter_init(adap); - if (ret < 0) goto fail1; - ret = pvr2_dvb_frontend_init(adap); - if (ret < 0) goto fail2; - return adap; - -fail2: - pvr2_dvb_adapter_exit(adap); -fail1: - pvr2_channel_done(&adap->channel); - return NULL; -} - diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.h b/drivers/media/video/pvrusb2/pvrusb2-dvb.h deleted file mode 100644 index 884ff916a352..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-dvb.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __PVRUSB2_DVB_H__ -#define __PVRUSB2_DVB_H__ - -#include "dvb_frontend.h" -#include "dvb_demux.h" -#include "dvb_net.h" -#include "dmxdev.h" -#include "pvrusb2-context.h" - -#define PVR2_DVB_BUFFER_COUNT 32 -#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000) - -struct pvr2_dvb_adapter { - struct pvr2_channel channel; - - struct dvb_adapter dvb_adap; - struct dmxdev dmxdev; - struct dvb_demux demux; - struct dvb_net dvb_net; - struct dvb_frontend *fe; - - int feedcount; - int max_feed_count; - - struct task_struct *thread; - struct mutex lock; - - unsigned int stream_run:1; - - wait_queue_head_t buffer_wait_data; - char *buffer_storage[PVR2_DVB_BUFFER_COUNT]; -}; - -struct pvr2_dvb_props { - int (*frontend_attach) (struct pvr2_dvb_adapter *); - int (*tuner_attach) (struct pvr2_dvb_adapter *); -}; - -struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr); - -#endif /* __PVRUSB2_DVB_H__ */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c deleted file mode 100644 index 9515f3a68f8f..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include "pvrusb2-eeprom.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" - -#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) - - - -/* - - Read and analyze data in the eeprom. Use tveeprom to figure out - the packet structure, since this is another Hauppauge device and - internally it has a family resemblance to ivtv-type devices - -*/ - -#include - -/* We seem to only be interested in the last 128 bytes of the EEPROM */ -#define EEPROM_SIZE 128 - -/* Grab EEPROM contents, needed for direct method. */ -static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) -{ - struct i2c_msg msg[2]; - u8 *eeprom; - u8 iadd[2]; - u8 addr; - u16 eepromSize; - unsigned int offs; - int ret; - int mode16 = 0; - unsigned pcnt,tcnt; - eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL); - if (!eeprom) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to allocate memory" - " required to read eeprom"); - return NULL; - } - - trace_eeprom("Value for eeprom addr from controller was 0x%x", - hdw->eeprom_addr); - addr = hdw->eeprom_addr; - /* Seems that if the high bit is set, then the *real* eeprom - address is shifted right now bit position (noticed this in - newer PVR USB2 hardware) */ - if (addr & 0x80) addr >>= 1; - - /* FX2 documentation states that a 16bit-addressed eeprom is - expected if the I2C address is an odd number (yeah, this is - strange but it's what they do) */ - mode16 = (addr & 1); - eepromSize = (mode16 ? 4096 : 256); - trace_eeprom("Examining %d byte eeprom at location 0x%x" - " using %d bit addressing",eepromSize,addr, - mode16 ? 16 : 8); - - msg[0].addr = addr; - msg[0].flags = 0; - msg[0].len = mode16 ? 2 : 1; - msg[0].buf = iadd; - msg[1].addr = addr; - msg[1].flags = I2C_M_RD; - - /* We have to do the actual eeprom data fetch ourselves, because - (1) we're only fetching part of the eeprom, and (2) if we were - getting the whole thing our I2C driver can't grab it in one - pass - which is what tveeprom is otherwise going to attempt */ - memset(eeprom,0,EEPROM_SIZE); - for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) { - pcnt = 16; - if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt; - offs = tcnt + (eepromSize - EEPROM_SIZE); - if (mode16) { - iadd[0] = offs >> 8; - iadd[1] = offs; - } else { - iadd[0] = offs; - } - msg[1].len = pcnt; - msg[1].buf = eeprom+tcnt; - if ((ret = i2c_transfer(&hdw->i2c_adap, - msg,ARRAY_SIZE(msg))) != 2) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "eeprom fetch set offs err=%d",ret); - kfree(eeprom); - return NULL; - } - } - return eeprom; -} - - -/* Directly call eeprom analysis function within tveeprom. */ -int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) -{ - u8 *eeprom; - struct tveeprom tvdata; - - memset(&tvdata,0,sizeof(tvdata)); - - eeprom = pvr2_eeprom_fetch(hdw); - if (!eeprom) return -EINVAL; - - { - struct i2c_client fake_client; - /* Newer version expects a useless client interface */ - fake_client.addr = hdw->eeprom_addr; - fake_client.adapter = &hdw->i2c_adap; - tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom); - } - - trace_eeprom("eeprom assumed v4l tveeprom module"); - trace_eeprom("eeprom direct call results:"); - trace_eeprom("has_radio=%d",tvdata.has_radio); - trace_eeprom("tuner_type=%d",tvdata.tuner_type); - trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats); - trace_eeprom("audio_processor=%d",tvdata.audio_processor); - trace_eeprom("model=%d",tvdata.model); - trace_eeprom("revision=%d",tvdata.revision); - trace_eeprom("serial_number=%d",tvdata.serial_number); - trace_eeprom("rev_str=%s",tvdata.rev_str); - hdw->tuner_type = tvdata.tuner_type; - hdw->tuner_updated = !0; - hdw->serial_number = tvdata.serial_number; - hdw->std_mask_eeprom = tvdata.tuner_formats; - - kfree(eeprom); - - return 0; -} - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h deleted file mode 100644 index cca3216f94cc..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_EEPROM_H -#define __PVRUSB2_EEPROM_H - -struct pvr2_hdw; - -int pvr2_eeprom_analyze(struct pvr2_hdw *); - -#endif /* __PVRUSB2_EEPROM_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c deleted file mode 100644 index e046fdaec5ae..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ /dev/null @@ -1,552 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include // for linux/firmware.h -#include -#include "pvrusb2-util.h" -#include "pvrusb2-encoder.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-fx2-cmd.h" - - - -/* Firmware mailbox flags - definitions found from ivtv */ -#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 -#define IVTV_MBOX_DRIVER_DONE 0x00000002 -#define IVTV_MBOX_DRIVER_BUSY 0x00000001 - -#define MBOX_BASE 0x44 - - -static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, - unsigned int offs, - const u32 *data, unsigned int dlen) -{ - unsigned int idx,addr; - unsigned int bAddr; - int ret; - unsigned int chunkCnt; - - /* - - Format: First byte must be 0x01. Remaining 32 bit words are - spread out into chunks of 7 bytes each, with the first 4 bytes - being the data word (little endian), and the next 3 bytes - being the address where that data word is to be written (big - endian). Repeat request for additional words, with offset - adjusted accordingly. - - */ - while (dlen) { - chunkCnt = 8; - if (chunkCnt > dlen) chunkCnt = dlen; - memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); - bAddr = 0; - hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; - for (idx = 0; idx < chunkCnt; idx++) { - addr = idx + offs; - hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); - hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); - hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); - PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); - bAddr += 7; - } - ret = pvr2_send_request(hdw, - hdw->cmd_buffer,1+(chunkCnt*7), - NULL,0); - if (ret) return ret; - data += chunkCnt; - dlen -= chunkCnt; - offs += chunkCnt; - } - - return 0; -} - - -static int pvr2_encoder_read_words(struct pvr2_hdw *hdw, - unsigned int offs, - u32 *data, unsigned int dlen) -{ - unsigned int idx; - int ret; - unsigned int chunkCnt; - - /* - - Format: First byte must be 0x02 (status check) or 0x28 (read - back block of 32 bit words). Next 6 bytes must be zero, - followed by a single byte of MBOX_BASE+offset for portion to - be read. Returned data is packed set of 32 bits words that - were read. - - */ - - while (dlen) { - chunkCnt = 16; - if (chunkCnt > dlen) chunkCnt = dlen; - if (chunkCnt < 16) chunkCnt = 1; - hdw->cmd_buffer[0] = - ((chunkCnt == 1) ? - FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); - hdw->cmd_buffer[1] = 0; - hdw->cmd_buffer[2] = 0; - hdw->cmd_buffer[3] = 0; - hdw->cmd_buffer[4] = 0; - hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); - hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); - hdw->cmd_buffer[7] = (offs & 0xffu); - ret = pvr2_send_request(hdw, - hdw->cmd_buffer,8, - hdw->cmd_buffer, - (chunkCnt == 1 ? 4 : 16 * 4)); - if (ret) return ret; - - for (idx = 0; idx < chunkCnt; idx++) { - data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); - } - data += chunkCnt; - dlen -= chunkCnt; - offs += chunkCnt; - } - - return 0; -} - - -/* This prototype is set up to be compatible with the - cx2341x_mbox_func prototype in cx2341x.h, which should be in - kernels 2.6.18 or later. We do this so that we can enable - cx2341x.ko to write to our encoder (by handing it a pointer to this - function). For earlier kernels this doesn't really matter. */ -static int pvr2_encoder_cmd(void *ctxt, - u32 cmd, - int arg_cnt_send, - int arg_cnt_recv, - u32 *argp) -{ - unsigned int poll_count; - unsigned int try_count = 0; - int retry_flag; - int ret = 0; - unsigned int idx; - /* These sizes look to be limited by the FX2 firmware implementation */ - u32 wrData[16]; - u32 rdData[16]; - struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; - - - /* - - The encoder seems to speak entirely using blocks 32 bit words. - In ivtv driver terms, this is a mailbox at MBOX_BASE which we - populate with data and watch what the hardware does with it. - The first word is a set of flags used to control the - transaction, the second word is the command to execute, the - third byte is zero (ivtv driver suggests that this is some - kind of return value), and the fourth byte is a specified - timeout (windows driver always uses 0x00060000 except for one - case when it is zero). All successive words are the argument - words for the command. - - First, write out the entire set of words, with the first word - being zero. - - Next, write out just the first word again, but set it to - IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which - probably means "go"). - - Next, read back the return count words. Check the first word, - which should have IVTV_MBOX_FIRMWARE_DONE set. If however - that bit is not set, then the command isn't done so repeat the - read until it is set. - - Finally, write out just the first word again, but set it to - 0x0 this time (which probably means "idle"). - - */ - - if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Failed to write cx23416 command" - " - too many input arguments" - " (was given %u limit %lu)", - arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); - return -EINVAL; - } - - if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Failed to write cx23416 command" - " - too many return arguments" - " (was given %u limit %lu)", - arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); - return -EINVAL; - } - - - LOCK_TAKE(hdw->ctl_lock); do { - - if (!hdw->state_encoder_ok) { - ret = -EIO; - break; - } - - retry_flag = 0; - try_count++; - ret = 0; - wrData[0] = 0; - wrData[1] = cmd; - wrData[2] = 0; - wrData[3] = 0x00060000; - for (idx = 0; idx < arg_cnt_send; idx++) { - wrData[idx+4] = argp[idx]; - } - for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { - wrData[idx+4] = 0; - } - - ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx); - if (ret) break; - wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; - ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); - if (ret) break; - poll_count = 0; - while (1) { - poll_count++; - ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData, - arg_cnt_recv+4); - if (ret) { - break; - } - if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { - break; - } - if (rdData[0] && (poll_count < 1000)) continue; - if (!rdData[0]) { - retry_flag = !0; - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Encoder timed out waiting for us" - "; arranging to retry"); - } else { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "***WARNING*** device's encoder" - " appears to be stuck" - " (status=0x%08x)",rdData[0]); - } - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Encoder command: 0x%02x",cmd); - for (idx = 4; idx < arg_cnt_send; idx++) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Encoder arg%d: 0x%08x", - idx-3,wrData[idx]); - } - ret = -EBUSY; - break; - } - if (retry_flag) { - if (try_count < 20) continue; - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Too many retries..."); - ret = -EBUSY; - } - if (ret) { - del_timer_sync(&hdw->encoder_run_timer); - hdw->state_encoder_ok = 0; - pvr2_trace(PVR2_TRACE_STBITS, - "State bit %s <-- %s", - "state_encoder_ok", - (hdw->state_encoder_ok ? "true" : "false")); - if (hdw->state_encoder_runok) { - hdw->state_encoder_runok = 0; - pvr2_trace(PVR2_TRACE_STBITS, - "State bit %s <-- %s", - "state_encoder_runok", - (hdw->state_encoder_runok ? - "true" : "false")); - } - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Giving up on command." - " This is normally recovered via a firmware" - " reload and re-initialization; concern" - " is only warranted if this happens repeatedly" - " and rapidly."); - break; - } - wrData[0] = 0x7; - for (idx = 0; idx < arg_cnt_recv; idx++) { - argp[idx] = rdData[idx+4]; - } - - wrData[0] = 0x0; - ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); - if (ret) break; - - } while(0); LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - - -static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, - int args, ...) -{ - va_list vl; - unsigned int idx; - u32 data[12]; - - if (args > ARRAY_SIZE(data)) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Failed to write cx23416 command" - " - too many arguments" - " (was given %u limit %lu)", - args, (long unsigned) ARRAY_SIZE(data)); - return -EINVAL; - } - - va_start(vl, args); - for (idx = 0; idx < args; idx++) { - data[idx] = va_arg(vl, u32); - } - va_end(vl); - - return pvr2_encoder_cmd(hdw,cmd,args,0,data); -} - - -/* This implements some extra setup for the encoder that seems to be - specific to the PVR USB2 hardware. */ -static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) -{ - int ret = 0; - int encMisc3Arg = 0; - -#if 0 - /* This inexplicable bit happens in the Hauppauge windows - driver (for both 24xxx and 29xxx devices). However I - currently see no difference in behavior with or without - this stuff. Leave this here as a note of its existence, - but don't use it. */ - LOCK_TAKE(hdw->ctl_lock); do { - u32 dat[1]; - dat[0] = 0x80000640; - pvr2_encoder_write_words(hdw,0x01fe,dat,1); - pvr2_encoder_write_words(hdw,0x023e,dat,1); - } while(0); LOCK_GIVE(hdw->ctl_lock); -#endif - - /* Mike Isely 26-Jan-2006 The windows driver - sends the following list of ENC_MISC commands (for both - 24xxx and 29xxx devices). Meanings are not entirely clear, - however without the ENC_MISC(3,1) command then we risk - random perpetual video corruption whenever the video input - breaks up for a moment (like when switching channels). */ - - -#if 0 - /* This ENC_MISC(5,0) command seems to hurt 29xxx sync - performance on channel changes, but is not a problem on - 24xxx devices. */ - ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); -#endif - - /* This ENC_MISC(3,encMisc3Arg) command is critical - without - it there will eventually be video corruption. Also, the - saa7115 case is strange - the Windows driver is passing 1 - regardless of device type but if we have 1 for saa7115 - devices the video turns sluggish. */ - if (hdw->hdw_desc->flag_has_cx25840) { - encMisc3Arg = 1; - } else { - encMisc3Arg = 0; - } - ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, - encMisc3Arg,0,0); - - ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); - -#if 0 - /* This ENC_MISC(4,1) command is poisonous, so it is commented - out. But I'm leaving it here anyway to document its - existence in the Windows driver. The effect of this - command is that apps displaying the stream become sluggish - with stuttering video. */ - ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); -#endif - - ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); - ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); - - /* prevent the PTSs from slowly drifting away in the generated - MPEG stream */ - ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1); - - return ret; -} - -int pvr2_encoder_adjust(struct pvr2_hdw *hdw) -{ - int ret; - ret = cx2341x_update(hdw,pvr2_encoder_cmd, - (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), - &hdw->enc_ctl_state); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Error from cx2341x module code=%d",ret); - } else { - memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, - sizeof(struct cx2341x_mpeg_params)); - hdw->enc_cur_valid = !0; - } - return ret; -} - - -int pvr2_encoder_configure(struct pvr2_hdw *hdw) -{ - int ret; - int val; - pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure" - " (cx2341x module)"); - hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; - hdw->enc_ctl_state.width = hdw->res_hor_val; - hdw->enc_ctl_state.height = hdw->res_ver_val; - hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ? - 0 : 1); - - ret = 0; - - ret |= pvr2_encoder_prep_config(hdw); - - /* saa7115: 0xf0 */ - val = 0xf0; - if (hdw->hdw_desc->flag_has_cx25840) { - /* ivtv cx25840: 0x140 */ - val = 0x140; - } - - if (!ret) ret = pvr2_encoder_vcmd( - hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, - val, val); - - /* setup firmware to notify us about some events (don't know why...) */ - if (!ret) ret = pvr2_encoder_vcmd( - hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, - 0, 0, 0x10000000, 0xffffffff); - - if (!ret) ret = pvr2_encoder_vcmd( - hdw,CX2341X_ENC_SET_VBI_LINE, 5, - 0xffffffff,0,0,0,0); - - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to configure cx23416"); - return ret; - } - - ret = pvr2_encoder_adjust(hdw); - if (ret) return ret; - - ret = pvr2_encoder_vcmd( - hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); - - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to initialize cx23416 video input"); - return ret; - } - - return 0; -} - - -int pvr2_encoder_start(struct pvr2_hdw *hdw) -{ - int status; - - /* unmask some interrupts */ - pvr2_write_register(hdw, 0x0048, 0xbfffffff); - - pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, - hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); - - switch (hdw->active_stream_type) { - case pvr2_config_vbi: - status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, - 0x01,0x14); - break; - case pvr2_config_mpeg: - status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, - 0,0x13); - break; - default: /* Unhandled cases for now */ - status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, - 0,0x13); - break; - } - return status; -} - -int pvr2_encoder_stop(struct pvr2_hdw *hdw) -{ - int status; - - /* mask all interrupts */ - pvr2_write_register(hdw, 0x0048, 0xffffffff); - - switch (hdw->active_stream_type) { - case pvr2_config_vbi: - status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, - 0x01,0x01,0x14); - break; - case pvr2_config_mpeg: - status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, - 0x01,0,0x13); - break; - default: /* Unhandled cases for now */ - status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, - 0x01,0,0x13); - break; - } - - return status; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h deleted file mode 100644 index 232fefbcd1ac..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_ENCODER_H -#define __PVRUSB2_ENCODER_H - -struct pvr2_hdw; - -int pvr2_encoder_adjust(struct pvr2_hdw *); -int pvr2_encoder_configure(struct pvr2_hdw *); -int pvr2_encoder_start(struct pvr2_hdw *); -int pvr2_encoder_stop(struct pvr2_hdw *); - -#endif /* __PVRUSB2_ENCODER_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h deleted file mode 100644 index 614755ea2ea3..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * - * Copyright (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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef _PVRUSB2_FX2_CMD_H_ -#define _PVRUSB2_FX2_CMD_H_ - -#define FX2CMD_MEM_WRITE_DWORD 0x01u -#define FX2CMD_MEM_READ_DWORD 0x02u - -#define FX2CMD_HCW_ZILOG_RESET 0x10u /* 1=reset 0=release */ - -#define FX2CMD_MEM_READ_64BYTES 0x28u - -#define FX2CMD_REG_WRITE 0x04u -#define FX2CMD_REG_READ 0x05u -#define FX2CMD_MEMSEL 0x06u - -#define FX2CMD_I2C_WRITE 0x08u -#define FX2CMD_I2C_READ 0x09u - -#define FX2CMD_GET_USB_SPEED 0x0bu - -#define FX2CMD_STREAMING_ON 0x36u -#define FX2CMD_STREAMING_OFF 0x37u - -#define FX2CMD_FWPOST1 0x52u - -#define FX2CMD_POWER_OFF 0xdcu -#define FX2CMD_POWER_ON 0xdeu - -#define FX2CMD_DEEP_RESET 0xddu - -#define FX2CMD_GET_EEPROM_ADDR 0xebu -#define FX2CMD_GET_IR_CODE 0xecu - -#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u -#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u -#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u - -#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u -#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u -#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u -#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u - -#endif /* _PVRUSB2_FX2_CMD_H_ */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h deleted file mode 100644 index 036952f2a3cb..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ /dev/null @@ -1,406 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_HDW_INTERNAL_H -#define __PVRUSB2_HDW_INTERNAL_H - -/* - - This header sets up all the internal structures and definitions needed to - track and coordinate the driver's interaction with the hardware. ONLY - source files which actually implement part of that whole circus should be - including this header. Higher levels, like the external layers to the - various public APIs (V4L, sysfs, etc) should NOT ever include this - private, internal header. This means that pvrusb2-hdw, pvrusb2-encoder, - etc will include this, but pvrusb2-v4l should not. - -*/ - -#include -#include -#include -#include -#include "pvrusb2-hdw.h" -#include "pvrusb2-io.h" -#include -#include -#include -#include "pvrusb2-devattr.h" - -/* Legal values for PVR2_CID_HSM */ -#define PVR2_CVAL_HSM_FAIL 0 -#define PVR2_CVAL_HSM_FULL 1 -#define PVR2_CVAL_HSM_HIGH 2 - -#define PVR2_VID_ENDPOINT 0x84 -#define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */ -#define PVR2_VBI_ENDPOINT 0x88 - -#define PVR2_CTL_BUFFSIZE 64 - -#define FREQTABLE_SIZE 500 - -#define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0) -#define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0) - -typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); -typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); -typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int); -typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *); -typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val); -typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val, - char *,unsigned int,unsigned int *); -typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *, - const char *,unsigned int, - int *mskp,int *valp); -typedef unsigned int (*pvr2_ctlf_get_v4lflags)(struct pvr2_ctrl *); - -/* This structure describes a specific control. A table of these is set up - in pvrusb2-hdw.c. */ -struct pvr2_ctl_info { - /* Control's name suitable for use as an identifier */ - const char *name; - - /* Short description of control */ - const char *desc; - - /* Control's implementation */ - pvr2_ctlf_get_value get_value; /* Get its value */ - pvr2_ctlf_get_value get_def_value; /* Get its default value */ - pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */ - pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */ - pvr2_ctlf_set_value set_value; /* Set its value */ - pvr2_ctlf_check_value check_value; /* Check that value is valid */ - pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */ - pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */ - pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */ - pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */ - pvr2_ctlf_get_v4lflags get_v4lflags;/* Retrieve v4l flags */ - - /* Control's type (int, enum, bitmask) */ - enum pvr2_ctl_type type; - - /* Associated V4L control ID, if any */ - int v4l_id; - - /* Associated driver internal ID, if any */ - int internal_id; - - /* Don't implicitly initialize this control's value */ - int skip_init; - - /* Starting value for this control */ - int default_value; - - /* Type-specific control information */ - union { - struct { /* Integer control */ - long min_value; /* lower limit */ - long max_value; /* upper limit */ - } type_int; - struct { /* enumerated control */ - unsigned int count; /* enum value count */ - const char * const *value_names; /* symbol names */ - } type_enum; - struct { /* bitmask control */ - unsigned int valid_bits; /* bits in use */ - const char **bit_names; /* symbol name/bit */ - } type_bitmask; - } def; -}; - - -/* Same as pvr2_ctl_info, but includes storage for the control description */ -#define PVR2_CTLD_INFO_DESC_SIZE 32 -struct pvr2_ctld_info { - struct pvr2_ctl_info info; - char desc[PVR2_CTLD_INFO_DESC_SIZE]; -}; - -struct pvr2_ctrl { - const struct pvr2_ctl_info *info; - struct pvr2_hdw *hdw; -}; - - - -/* Disposition of firmware1 loading situation */ -#define FW1_STATE_UNKNOWN 0 -#define FW1_STATE_MISSING 1 -#define FW1_STATE_FAILED 2 -#define FW1_STATE_RELOAD 3 -#define FW1_STATE_OK 4 - -/* What state the device is in if it is a hybrid */ -#define PVR2_PATHWAY_UNKNOWN 0 -#define PVR2_PATHWAY_ANALOG 1 -#define PVR2_PATHWAY_DIGITAL 2 - -typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); -#define PVR2_I2C_FUNC_CNT 128 - -/* This structure contains all state data directly needed to - manipulate the hardware (as opposed to complying with a kernel - interface) */ -struct pvr2_hdw { - /* Underlying USB device handle */ - struct usb_device *usb_dev; - struct usb_interface *usb_intf; - - /* Our handle into the v4l2 sub-device architecture */ - struct v4l2_device v4l2_dev; - /* Device description, anything that must adjust behavior based on - device specific info will use information held here. */ - const struct pvr2_device_desc *hdw_desc; - - /* Kernel worker thread handling */ - struct workqueue_struct *workqueue; - struct work_struct workpoll; /* Update driver state */ - - /* Video spigot */ - struct pvr2_stream *vid_stream; - - /* Mutex for all hardware state control */ - struct mutex big_lock_mutex; - int big_lock_held; /* For debugging */ - - /* This is a simple string which identifies the instance of this - driver. It is unique within the set of existing devices, but - there is no attempt to keep the name consistent with the same - physical device each time. */ - char name[32]; - - /* This is a simple string which identifies the physical device - instance itself - if possible. (If not possible, then it is - based on the specific driver instance, similar to name above.) - The idea here is that userspace might hopefully be able to use - this recognize specific tuners. It will encode a serial number, - if available. */ - char identifier[32]; - - /* I2C stuff */ - struct i2c_adapter i2c_adap; - struct i2c_algorithm i2c_algo; - pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; - int i2c_cx25840_hack_state; - int i2c_linked; - - /* IR related */ - unsigned int ir_scheme_active; /* IR scheme as seen from the outside */ - struct IR_i2c_init_data ir_init_data; /* params passed to IR modules */ - - /* Frequency table */ - unsigned int freqTable[FREQTABLE_SIZE]; - unsigned int freqProgSlot; - - /* Stuff for handling low level control interaction with device */ - struct mutex ctl_lock_mutex; - int ctl_lock_held; /* For debugging */ - struct urb *ctl_write_urb; - struct urb *ctl_read_urb; - unsigned char *ctl_write_buffer; - unsigned char *ctl_read_buffer; - int ctl_write_pend_flag; - int ctl_read_pend_flag; - int ctl_timeout_flag; - struct completion ctl_done; - unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE]; - int cmd_debug_state; // Low level command debugging info - unsigned char cmd_debug_code; // - unsigned int cmd_debug_write_len; // - unsigned int cmd_debug_read_len; // - - /* Bits of state that describe what is going on with various parts - of the driver. */ - int state_pathway_ok; /* Pathway config is ok */ - int state_encoder_ok; /* Encoder is operational */ - int state_encoder_run; /* Encoder is running */ - int state_encoder_config; /* Encoder is configured */ - int state_encoder_waitok; /* Encoder pre-wait done */ - int state_encoder_runok; /* Encoder has run for >= .25 sec */ - int state_decoder_run; /* Decoder is running */ - int state_decoder_ready; /* Decoder is stabilized & streamable */ - int state_usbstream_run; /* FX2 is streaming */ - int state_decoder_quiescent; /* Decoder idle for minimal interval */ - int state_pipeline_config; /* Pipeline is configured */ - int state_pipeline_req; /* Somebody wants to stream */ - int state_pipeline_pause; /* Pipeline must be paused */ - int state_pipeline_idle; /* Pipeline not running */ - - /* This is the master state of the driver. It is the combined - result of other bits of state. Examining this will indicate the - overall state of the driver. Values here are one of - PVR2_STATE_xxxx */ - unsigned int master_state; - - /* True if device led is currently on */ - int led_on; - - /* True if states must be re-evaluated */ - int state_stale; - - void (*state_func)(void *); - void *state_data; - - /* Timer for measuring required decoder settling time before we're - allowed to fire it up again. */ - struct timer_list quiescent_timer; - - /* Timer for measuring decoder stabilization time, which is the - amount of time we need to let the decoder run before we can - trust its output (otherwise the encoder might see garbage and - then fail to start correctly). */ - struct timer_list decoder_stabilization_timer; - - /* Timer for measuring encoder pre-wait time */ - struct timer_list encoder_wait_timer; - - /* Timer for measuring encoder minimum run time */ - struct timer_list encoder_run_timer; - - /* Place to block while waiting for state changes */ - wait_queue_head_t state_wait_data; - - - int force_dirty; /* consider all controls dirty if true */ - int flag_ok; /* device in known good state */ - int flag_modulefail; /* true if at least one module failed to load */ - int flag_disconnected; /* flag_ok == 0 due to disconnect */ - int flag_init_ok; /* true if structure is fully initialized */ - int fw1_state; /* current situation with fw1 */ - int pathway_state; /* one of PVR2_PATHWAY_xxx */ - int flag_decoder_missed;/* We've noticed missing decoder */ - int flag_tripped; /* Indicates overall failure to start */ - - unsigned int decoder_client_id; - - // CPU firmware info (used to help find / save firmware data) - char *fw_buffer; - unsigned int fw_size; - int fw_cpu_flag; /* True if we are dealing with the CPU */ - - /* Tuner / frequency control stuff */ - unsigned int tuner_type; - int tuner_updated; - unsigned int freqValTelevision; /* Current freq for tv mode */ - unsigned int freqValRadio; /* Current freq for radio mode */ - unsigned int freqSlotTelevision; /* Current slot for tv mode */ - unsigned int freqSlotRadio; /* Current slot for radio mode */ - unsigned int freqSelector; /* 0=radio 1=television */ - int freqDirty; - - /* Current tuner info - this information is polled from the I2C bus */ - struct v4l2_tuner tuner_signal_info; - int tuner_signal_stale; - - /* Cropping capability info */ - struct v4l2_cropcap cropcap_info; - int cropcap_stale; - - /* Video standard handling */ - v4l2_std_id std_mask_eeprom; // Hardware supported selections - v4l2_std_id std_mask_avail; // Which standards we may select from - v4l2_std_id std_mask_cur; // Currently selected standard(s) - int std_enum_cur; // selected standard enumeration value - int std_dirty; // True if std_mask_cur has changed - struct pvr2_ctl_info std_info_enum; - struct pvr2_ctl_info std_info_avail; - struct pvr2_ctl_info std_info_cur; - struct pvr2_ctl_info std_info_detect; - - // Generated string names, one per actual V4L2 standard - const char *std_mask_ptrs[32]; - char std_mask_names[32][16]; - - int unit_number; /* ID for driver instance */ - unsigned long serial_number; /* ID for hardware itself */ - - char bus_info[32]; /* Bus location info */ - - /* Minor numbers used by v4l logic (yes, this is a hack, as there - should be no v4l junk here). Probably a better way to do this. */ - int v4l_minor_number_video; - int v4l_minor_number_vbi; - int v4l_minor_number_radio; - - /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */ - unsigned int input_avail_mask; - /* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */ - unsigned int input_allowed_mask; - - /* Location of eeprom or a negative number if none */ - int eeprom_addr; - - enum pvr2_config active_stream_type; - enum pvr2_config desired_stream_type; - - /* Control state needed for cx2341x module */ - struct cx2341x_mpeg_params enc_cur_state; - struct cx2341x_mpeg_params enc_ctl_state; - /* True if an encoder attribute has changed */ - int enc_stale; - /* True if an unsafe encoder attribute has changed */ - int enc_unsafe_stale; - /* True if enc_cur_state is valid */ - int enc_cur_valid; - - /* Control state */ -#define VCREATE_DATA(lab) int lab##_val; int lab##_dirty - VCREATE_DATA(brightness); - VCREATE_DATA(contrast); - VCREATE_DATA(saturation); - VCREATE_DATA(hue); - VCREATE_DATA(volume); - VCREATE_DATA(balance); - VCREATE_DATA(bass); - VCREATE_DATA(treble); - VCREATE_DATA(mute); - VCREATE_DATA(cropl); - VCREATE_DATA(cropt); - VCREATE_DATA(cropw); - VCREATE_DATA(croph); - VCREATE_DATA(input); - VCREATE_DATA(audiomode); - VCREATE_DATA(res_hor); - VCREATE_DATA(res_ver); - VCREATE_DATA(srate); -#undef VCREATE_DATA - - struct pvr2_ctld_info *mpeg_ctrl_info; - - struct pvr2_ctrl *controls; - unsigned int control_cnt; -}; - -/* This function gets the current frequency */ -unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); - -void pvr2_hdw_status_poll(struct pvr2_hdw *); - -#endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c deleted file mode 100644 index fb828ba1dbbe..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ /dev/null @@ -1,5202 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "pvrusb2.h" -#include "pvrusb2-std.h" -#include "pvrusb2-util.h" -#include "pvrusb2-hdw.h" -#include "pvrusb2-i2c-core.h" -#include "pvrusb2-eeprom.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-encoder.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-fx2-cmd.h" -#include "pvrusb2-wm8775.h" -#include "pvrusb2-video-v4l.h" -#include "pvrusb2-cx2584x-v4l.h" -#include "pvrusb2-cs53l32a.h" -#include "pvrusb2-audio.h" - -#define TV_MIN_FREQ 55250000L -#define TV_MAX_FREQ 850000000L - -/* This defines a minimum interval that the decoder must remain quiet - before we are allowed to start it running. */ -#define TIME_MSEC_DECODER_WAIT 50 - -/* This defines a minimum interval that the decoder must be allowed to run - before we can safely begin using its streaming output. */ -#define TIME_MSEC_DECODER_STABILIZATION_WAIT 300 - -/* This defines a minimum interval that the encoder must remain quiet - before we are allowed to configure it. */ -#define TIME_MSEC_ENCODER_WAIT 50 - -/* This defines the minimum interval that the encoder must successfully run - before we consider that the encoder has run at least once since its - firmware has been loaded. This measurement is in important for cases - where we can't do something until we know that the encoder has been run - at least once. */ -#define TIME_MSEC_ENCODER_OK 250 - -static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; -static DEFINE_MUTEX(pvr2_unit_mtx); - -static int ctlchg; -static int procreload; -static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; -static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; -static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; -static int init_pause_msec; - -module_param(ctlchg, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value"); -module_param(init_pause_msec, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay"); -module_param(procreload, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(procreload, - "Attempt init failure recovery with firmware reload"); -module_param_array(tuner, int, NULL, 0444); -MODULE_PARM_DESC(tuner,"specify installed tuner type"); -module_param_array(video_std, int, NULL, 0444); -MODULE_PARM_DESC(video_std,"specify initial video standard"); -module_param_array(tolerance, int, NULL, 0444); -MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); - -/* US Broadcast channel 3 (61.25 MHz), to help with testing */ -static int default_tv_freq = 61250000L; -/* 104.3 MHz, a usable FM station for my area */ -static int default_radio_freq = 104300000L; - -module_param_named(tv_freq, default_tv_freq, int, 0444); -MODULE_PARM_DESC(tv_freq, "specify initial television frequency"); -module_param_named(radio_freq, default_radio_freq, int, 0444); -MODULE_PARM_DESC(radio_freq, "specify initial radio frequency"); - -#define PVR2_CTL_WRITE_ENDPOINT 0x01 -#define PVR2_CTL_READ_ENDPOINT 0x81 - -#define PVR2_GPIO_IN 0x9008 -#define PVR2_GPIO_OUT 0x900c -#define PVR2_GPIO_DIR 0x9020 - -#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__) - -#define PVR2_FIRMWARE_ENDPOINT 0x02 - -/* size of a firmware chunk */ -#define FIRMWARE_CHUNK_SIZE 0x2000 - -typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *, - struct v4l2_subdev *); - -static const pvr2_subdev_update_func pvr2_module_update_functions[] = { - [PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update, - [PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update, - [PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update, - [PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update, - [PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update, -}; - -static const char *module_names[] = { - [PVR2_CLIENT_ID_MSP3400] = "msp3400", - [PVR2_CLIENT_ID_CX25840] = "cx25840", - [PVR2_CLIENT_ID_SAA7115] = "saa7115", - [PVR2_CLIENT_ID_TUNER] = "tuner", - [PVR2_CLIENT_ID_DEMOD] = "tuner", - [PVR2_CLIENT_ID_CS53L32A] = "cs53l32a", - [PVR2_CLIENT_ID_WM8775] = "wm8775", -}; - - -static const unsigned char *module_i2c_addresses[] = { - [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63", - [PVR2_CLIENT_ID_DEMOD] = "\x43", - [PVR2_CLIENT_ID_MSP3400] = "\x40", - [PVR2_CLIENT_ID_SAA7115] = "\x21", - [PVR2_CLIENT_ID_WM8775] = "\x1b", - [PVR2_CLIENT_ID_CX25840] = "\x44", - [PVR2_CLIENT_ID_CS53L32A] = "\x11", -}; - - -static const char *ir_scheme_names[] = { - [PVR2_IR_SCHEME_NONE] = "none", - [PVR2_IR_SCHEME_29XXX] = "29xxx", - [PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)", - [PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)", - [PVR2_IR_SCHEME_ZILOG] = "Zilog", -}; - - -/* Define the list of additional controls we'll dynamically construct based - on query of the cx2341x module. */ -struct pvr2_mpeg_ids { - const char *strid; - int id; -}; -static const struct pvr2_mpeg_ids mpeg_ids[] = { - { - .strid = "audio_layer", - .id = V4L2_CID_MPEG_AUDIO_ENCODING, - },{ - .strid = "audio_bitrate", - .id = V4L2_CID_MPEG_AUDIO_L2_BITRATE, - },{ - /* Already using audio_mode elsewhere :-( */ - .strid = "mpeg_audio_mode", - .id = V4L2_CID_MPEG_AUDIO_MODE, - },{ - .strid = "mpeg_audio_mode_extension", - .id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, - },{ - .strid = "audio_emphasis", - .id = V4L2_CID_MPEG_AUDIO_EMPHASIS, - },{ - .strid = "audio_crc", - .id = V4L2_CID_MPEG_AUDIO_CRC, - },{ - .strid = "video_aspect", - .id = V4L2_CID_MPEG_VIDEO_ASPECT, - },{ - .strid = "video_b_frames", - .id = V4L2_CID_MPEG_VIDEO_B_FRAMES, - },{ - .strid = "video_gop_size", - .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE, - },{ - .strid = "video_gop_closure", - .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - },{ - .strid = "video_bitrate_mode", - .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - },{ - .strid = "video_bitrate", - .id = V4L2_CID_MPEG_VIDEO_BITRATE, - },{ - .strid = "video_bitrate_peak", - .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - },{ - .strid = "video_temporal_decimation", - .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, - },{ - .strid = "stream_type", - .id = V4L2_CID_MPEG_STREAM_TYPE, - },{ - .strid = "video_spatial_filter_mode", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, - },{ - .strid = "video_spatial_filter", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, - },{ - .strid = "video_luma_spatial_filter_type", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, - },{ - .strid = "video_chroma_spatial_filter_type", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, - },{ - .strid = "video_temporal_filter_mode", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, - },{ - .strid = "video_temporal_filter", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, - },{ - .strid = "video_median_filter_type", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, - },{ - .strid = "video_luma_median_filter_top", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, - },{ - .strid = "video_luma_median_filter_bottom", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, - },{ - .strid = "video_chroma_median_filter_top", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, - },{ - .strid = "video_chroma_median_filter_bottom", - .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, - } -}; -#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids) - - -static const char *control_values_srate[] = { - [V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100] = "44.1 kHz", - [V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000] = "48 kHz", - [V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000] = "32 kHz", -}; - - - -static const char *control_values_input[] = { - [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ - [PVR2_CVAL_INPUT_DTV] = "dtv", - [PVR2_CVAL_INPUT_RADIO] = "radio", - [PVR2_CVAL_INPUT_SVIDEO] = "s-video", - [PVR2_CVAL_INPUT_COMPOSITE] = "composite", -}; - - -static const char *control_values_audiomode[] = { - [V4L2_TUNER_MODE_MONO] = "Mono", - [V4L2_TUNER_MODE_STEREO] = "Stereo", - [V4L2_TUNER_MODE_LANG1] = "Lang1", - [V4L2_TUNER_MODE_LANG2] = "Lang2", - [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2", -}; - - -static const char *control_values_hsm[] = { - [PVR2_CVAL_HSM_FAIL] = "Fail", - [PVR2_CVAL_HSM_HIGH] = "High", - [PVR2_CVAL_HSM_FULL] = "Full", -}; - - -static const char *pvr2_state_names[] = { - [PVR2_STATE_NONE] = "none", - [PVR2_STATE_DEAD] = "dead", - [PVR2_STATE_COLD] = "cold", - [PVR2_STATE_WARM] = "warm", - [PVR2_STATE_ERROR] = "error", - [PVR2_STATE_READY] = "ready", - [PVR2_STATE_RUN] = "run", -}; - - -struct pvr2_fx2cmd_descdef { - unsigned char id; - unsigned char *desc; -}; - -static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { - {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, - {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, - {FX2CMD_HCW_ZILOG_RESET, "zilog IR reset control"}, - {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, - {FX2CMD_REG_WRITE, "write encoder register"}, - {FX2CMD_REG_READ, "read encoder register"}, - {FX2CMD_MEMSEL, "encoder memsel"}, - {FX2CMD_I2C_WRITE, "i2c write"}, - {FX2CMD_I2C_READ, "i2c read"}, - {FX2CMD_GET_USB_SPEED, "get USB speed"}, - {FX2CMD_STREAMING_ON, "stream on"}, - {FX2CMD_STREAMING_OFF, "stream off"}, - {FX2CMD_FWPOST1, "fwpost1"}, - {FX2CMD_POWER_OFF, "power off"}, - {FX2CMD_POWER_ON, "power on"}, - {FX2CMD_DEEP_RESET, "deep reset"}, - {FX2CMD_GET_EEPROM_ADDR, "get rom addr"}, - {FX2CMD_GET_IR_CODE, "get IR code"}, - {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"}, - {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"}, - {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"}, - {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"}, - {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"}, - {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"}, - {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"}, -}; - - -static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); -static void pvr2_hdw_state_sched(struct pvr2_hdw *); -static int pvr2_hdw_state_eval(struct pvr2_hdw *); -static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); -static void pvr2_hdw_worker_poll(struct work_struct *work); -static int pvr2_hdw_wait(struct pvr2_hdw *,int state); -static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); -static void pvr2_hdw_state_log_state(struct pvr2_hdw *); -static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); -static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw); -static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); -static void pvr2_hdw_quiescent_timeout(unsigned long); -static void pvr2_hdw_decoder_stabilization_timeout(unsigned long); -static void pvr2_hdw_encoder_wait_timeout(unsigned long); -static void pvr2_hdw_encoder_run_timeout(unsigned long); -static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); -static int pvr2_send_request_ex(struct pvr2_hdw *hdw, - unsigned int timeout,int probe_fl, - void *write_data,unsigned int write_len, - void *read_data,unsigned int read_len); -static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw); -static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw); - -static void trace_stbit(const char *name,int val) -{ - pvr2_trace(PVR2_TRACE_STBITS, - "State bit %s <-- %s", - name,(val ? "true" : "false")); -} - -static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) -{ - struct pvr2_hdw *hdw = cptr->hdw; - if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { - *vp = hdw->freqTable[hdw->freqProgSlot-1]; - } else { - *vp = 0; - } - return 0; -} - -static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) -{ - struct pvr2_hdw *hdw = cptr->hdw; - unsigned int slotId = hdw->freqProgSlot; - if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) { - hdw->freqTable[slotId-1] = v; - /* Handle side effects correctly - if we're tuned to this - slot, then forgot the slot id relation since the stored - frequency has been changed. */ - if (hdw->freqSelector) { - if (hdw->freqSlotRadio == slotId) { - hdw->freqSlotRadio = 0; - } - } else { - if (hdw->freqSlotTelevision == slotId) { - hdw->freqSlotTelevision = 0; - } - } - } - return 0; -} - -static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->freqProgSlot; - return 0; -} - -static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) -{ - struct pvr2_hdw *hdw = cptr->hdw; - if ((v >= 0) && (v <= FREQTABLE_SIZE)) { - hdw->freqProgSlot = v; - } - return 0; -} - -static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) -{ - struct pvr2_hdw *hdw = cptr->hdw; - *vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision; - return 0; -} - -static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId) -{ - unsigned freq = 0; - struct pvr2_hdw *hdw = cptr->hdw; - if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0; - if (slotId > 0) { - freq = hdw->freqTable[slotId-1]; - if (!freq) return 0; - pvr2_hdw_set_cur_freq(hdw,freq); - } - if (hdw->freqSelector) { - hdw->freqSlotRadio = slotId; - } else { - hdw->freqSlotTelevision = slotId; - } - return 0; -} - -static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = pvr2_hdw_get_cur_freq(cptr->hdw); - return 0; -} - -static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->freqDirty != 0; -} - -static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) -{ - cptr->hdw->freqDirty = 0; -} - -static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) -{ - pvr2_hdw_set_cur_freq(cptr->hdw,v); - return 0; -} - -static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *left = cap->bounds.left; - return 0; -} - -static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *left = cap->bounds.left; - if (cap->bounds.width > cptr->hdw->cropw_val) { - *left += cap->bounds.width - cptr->hdw->cropw_val; - } - return 0; -} - -static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *top = cap->bounds.top; - return 0; -} - -static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *top = cap->bounds.top; - if (cap->bounds.height > cptr->hdw->croph_val) { - *top += cap->bounds.height - cptr->hdw->croph_val; - } - return 0; -} - -static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat, bleftend, cleft; - - stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - bleftend = cap->bounds.left+cap->bounds.width; - cleft = cptr->hdw->cropl_val; - - *width = cleft < bleftend ? bleftend-cleft : 0; - return 0; -} - -static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat, btopend, ctop; - - stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - btopend = cap->bounds.top+cap->bounds.height; - ctop = cptr->hdw->cropt_val; - - *height = ctop < btopend ? btopend-ctop : 0; - return 0; -} - -static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->bounds.left; - return 0; -} - -static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->bounds.top; - return 0; -} - -static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->bounds.width; - return 0; -} - -static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->bounds.height; - return 0; -} - -static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->defrect.left; - return 0; -} - -static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->defrect.top; - return 0; -} - -static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->defrect.width; - return 0; -} - -static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->defrect.height; - return 0; -} - -static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->pixelaspect.numerator; - return 0; -} - -static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val) -{ - struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); - if (stat != 0) { - return stat; - } - *val = cap->pixelaspect.denominator; - return 0; -} - -static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) -{ - /* Actual maximum depends on the video standard in effect. */ - if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) { - *vp = 480; - } else { - *vp = 576; - } - return 0; -} - -static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) -{ - /* Actual minimum depends on device digitizer type. */ - if (cptr->hdw->hdw_desc->flag_has_cx25840) { - *vp = 75; - } else { - *vp = 17; - } - return 0; -} - -static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->input_val; - return 0; -} - -static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) -{ - return ((1 << v) & cptr->hdw->input_allowed_mask) != 0; -} - -static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) -{ - return pvr2_hdw_set_input(cptr->hdw,v); -} - -static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->input_dirty != 0; -} - -static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr) -{ - cptr->hdw->input_dirty = 0; -} - - -static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) -{ - unsigned long fv; - struct pvr2_hdw *hdw = cptr->hdw; - if (hdw->tuner_signal_stale) { - pvr2_hdw_status_poll(hdw); - } - fv = hdw->tuner_signal_info.rangehigh; - if (!fv) { - /* Safety fallback */ - *vp = TV_MAX_FREQ; - return 0; - } - if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { - fv = (fv * 125) / 2; - } else { - fv = fv * 62500; - } - *vp = fv; - return 0; -} - -static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp) -{ - unsigned long fv; - struct pvr2_hdw *hdw = cptr->hdw; - if (hdw->tuner_signal_stale) { - pvr2_hdw_status_poll(hdw); - } - fv = hdw->tuner_signal_info.rangelow; - if (!fv) { - /* Safety fallback */ - *vp = TV_MIN_FREQ; - return 0; - } - if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { - fv = (fv * 125) / 2; - } else { - fv = fv * 62500; - } - *vp = fv; - return 0; -} - -static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->enc_stale != 0; -} - -static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr) -{ - cptr->hdw->enc_stale = 0; - cptr->hdw->enc_unsafe_stale = 0; -} - -static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp) -{ - int ret; - struct v4l2_ext_controls cs; - struct v4l2_ext_control c1; - memset(&cs,0,sizeof(cs)); - memset(&c1,0,sizeof(c1)); - cs.controls = &c1; - cs.count = 1; - c1.id = cptr->info->v4l_id; - ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs, - VIDIOC_G_EXT_CTRLS); - if (ret) return ret; - *vp = c1.value; - return 0; -} - -static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v) -{ - int ret; - struct pvr2_hdw *hdw = cptr->hdw; - struct v4l2_ext_controls cs; - struct v4l2_ext_control c1; - memset(&cs,0,sizeof(cs)); - memset(&c1,0,sizeof(c1)); - cs.controls = &c1; - cs.count = 1; - c1.id = cptr->info->v4l_id; - c1.value = v; - ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, - hdw->state_encoder_run, &cs, - VIDIOC_S_EXT_CTRLS); - if (ret == -EBUSY) { - /* Oops. cx2341x is telling us it's not safe to change - this control while we're capturing. Make a note of this - fact so that the pipeline will be stopped the next time - controls are committed. Then go on ahead and store this - change anyway. */ - ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, - 0, &cs, - VIDIOC_S_EXT_CTRLS); - if (!ret) hdw->enc_unsafe_stale = !0; - } - if (ret) return ret; - hdw->enc_stale = !0; - return 0; -} - -static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr) -{ - struct v4l2_queryctrl qctrl; - struct pvr2_ctl_info *info; - qctrl.id = cptr->info->v4l_id; - cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl); - /* Strip out the const so we can adjust a function pointer. It's - OK to do this here because we know this is a dynamically created - control, so the underlying storage for the info pointer is (a) - private to us, and (b) not in read-only storage. Either we do - this or we significantly complicate the underlying control - implementation. */ - info = (struct pvr2_ctl_info *)(cptr->info); - if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) { - if (info->set_value) { - info->set_value = NULL; - } - } else { - if (!(info->set_value)) { - info->set_value = ctrl_cx2341x_set; - } - } - return qctrl.flags; -} - -static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->state_pipeline_req; - return 0; -} - -static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->master_state; - return 0; -} - -static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) -{ - int result = pvr2_hdw_is_hsm(cptr->hdw); - *vp = PVR2_CVAL_HSM_FULL; - if (result < 0) *vp = PVR2_CVAL_HSM_FAIL; - if (result) *vp = PVR2_CVAL_HSM_HIGH; - return 0; -} - -static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp) -{ - *vp = pvr2_hdw_get_detected_std(cptr->hdw); - return 0; -} - -static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->std_mask_avail; - return 0; -} - -static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) -{ - struct pvr2_hdw *hdw = cptr->hdw; - v4l2_std_id ns; - ns = hdw->std_mask_avail; - ns = (ns & ~m) | (v & m); - if (ns == hdw->std_mask_avail) return 0; - hdw->std_mask_avail = ns; - hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; - return 0; -} - -static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val, - char *bufPtr,unsigned int bufSize, - unsigned int *len) -{ - *len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val); - return 0; -} - -static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr, - const char *bufPtr,unsigned int bufSize, - int *mskp,int *valp) -{ - int ret; - v4l2_std_id id; - ret = pvr2_std_str_to_id(&id,bufPtr,bufSize); - if (ret < 0) return ret; - if (mskp) *mskp = id; - if (valp) *valp = id; - return 0; -} - -static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->std_mask_cur; - return 0; -} - -static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) -{ - struct pvr2_hdw *hdw = cptr->hdw; - v4l2_std_id ns; - ns = hdw->std_mask_cur; - ns = (ns & ~m) | (v & m); - if (ns == hdw->std_mask_cur) return 0; - hdw->std_mask_cur = ns; - hdw->std_dirty = !0; - return 0; -} - -static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->std_dirty != 0; -} - -static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) -{ - cptr->hdw->std_dirty = 0; -} - -static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) -{ - struct pvr2_hdw *hdw = cptr->hdw; - pvr2_hdw_status_poll(hdw); - *vp = hdw->tuner_signal_info.signal; - return 0; -} - -static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) -{ - int val = 0; - unsigned int subchan; - struct pvr2_hdw *hdw = cptr->hdw; - pvr2_hdw_status_poll(hdw); - subchan = hdw->tuner_signal_info.rxsubchans; - if (subchan & V4L2_TUNER_SUB_MONO) { - val |= (1 << V4L2_TUNER_MODE_MONO); - } - if (subchan & V4L2_TUNER_SUB_STEREO) { - val |= (1 << V4L2_TUNER_MODE_STEREO); - } - if (subchan & V4L2_TUNER_SUB_LANG1) { - val |= (1 << V4L2_TUNER_MODE_LANG1); - } - if (subchan & V4L2_TUNER_SUB_LANG2) { - val |= (1 << V4L2_TUNER_MODE_LANG2); - } - *vp = val; - return 0; -} - - -#define DEFINT(vmin,vmax) \ - .type = pvr2_ctl_int, \ - .def.type_int.min_value = vmin, \ - .def.type_int.max_value = vmax - -#define DEFENUM(tab) \ - .type = pvr2_ctl_enum, \ - .def.type_enum.count = ARRAY_SIZE(tab), \ - .def.type_enum.value_names = tab - -#define DEFBOOL \ - .type = pvr2_ctl_bool - -#define DEFMASK(msk,tab) \ - .type = pvr2_ctl_bitmask, \ - .def.type_bitmask.valid_bits = msk, \ - .def.type_bitmask.bit_names = tab - -#define DEFREF(vname) \ - .set_value = ctrl_set_##vname, \ - .get_value = ctrl_get_##vname, \ - .is_dirty = ctrl_isdirty_##vname, \ - .clear_dirty = ctrl_cleardirty_##vname - - -#define VCREATE_FUNCS(vname) \ -static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \ -{*vp = cptr->hdw->vname##_val; return 0;} \ -static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \ -{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \ -static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \ -{return cptr->hdw->vname##_dirty != 0;} \ -static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \ -{cptr->hdw->vname##_dirty = 0;} - -VCREATE_FUNCS(brightness) -VCREATE_FUNCS(contrast) -VCREATE_FUNCS(saturation) -VCREATE_FUNCS(hue) -VCREATE_FUNCS(volume) -VCREATE_FUNCS(balance) -VCREATE_FUNCS(bass) -VCREATE_FUNCS(treble) -VCREATE_FUNCS(mute) -VCREATE_FUNCS(cropl) -VCREATE_FUNCS(cropt) -VCREATE_FUNCS(cropw) -VCREATE_FUNCS(croph) -VCREATE_FUNCS(audiomode) -VCREATE_FUNCS(res_hor) -VCREATE_FUNCS(res_ver) -VCREATE_FUNCS(srate) - -/* Table definition of all controls which can be manipulated */ -static const struct pvr2_ctl_info control_defs[] = { - { - .v4l_id = V4L2_CID_BRIGHTNESS, - .desc = "Brightness", - .name = "brightness", - .default_value = 128, - DEFREF(brightness), - DEFINT(0,255), - },{ - .v4l_id = V4L2_CID_CONTRAST, - .desc = "Contrast", - .name = "contrast", - .default_value = 68, - DEFREF(contrast), - DEFINT(0,127), - },{ - .v4l_id = V4L2_CID_SATURATION, - .desc = "Saturation", - .name = "saturation", - .default_value = 64, - DEFREF(saturation), - DEFINT(0,127), - },{ - .v4l_id = V4L2_CID_HUE, - .desc = "Hue", - .name = "hue", - .default_value = 0, - DEFREF(hue), - DEFINT(-128,127), - },{ - .v4l_id = V4L2_CID_AUDIO_VOLUME, - .desc = "Volume", - .name = "volume", - .default_value = 62000, - DEFREF(volume), - DEFINT(0,65535), - },{ - .v4l_id = V4L2_CID_AUDIO_BALANCE, - .desc = "Balance", - .name = "balance", - .default_value = 0, - DEFREF(balance), - DEFINT(-32768,32767), - },{ - .v4l_id = V4L2_CID_AUDIO_BASS, - .desc = "Bass", - .name = "bass", - .default_value = 0, - DEFREF(bass), - DEFINT(-32768,32767), - },{ - .v4l_id = V4L2_CID_AUDIO_TREBLE, - .desc = "Treble", - .name = "treble", - .default_value = 0, - DEFREF(treble), - DEFINT(-32768,32767), - },{ - .v4l_id = V4L2_CID_AUDIO_MUTE, - .desc = "Mute", - .name = "mute", - .default_value = 0, - DEFREF(mute), - DEFBOOL, - }, { - .desc = "Capture crop left margin", - .name = "crop_left", - .internal_id = PVR2_CID_CROPL, - .default_value = 0, - DEFREF(cropl), - DEFINT(-129, 340), - .get_min_value = ctrl_cropl_min_get, - .get_max_value = ctrl_cropl_max_get, - .get_def_value = ctrl_get_cropcapdl, - }, { - .desc = "Capture crop top margin", - .name = "crop_top", - .internal_id = PVR2_CID_CROPT, - .default_value = 0, - DEFREF(cropt), - DEFINT(-35, 544), - .get_min_value = ctrl_cropt_min_get, - .get_max_value = ctrl_cropt_max_get, - .get_def_value = ctrl_get_cropcapdt, - }, { - .desc = "Capture crop width", - .name = "crop_width", - .internal_id = PVR2_CID_CROPW, - .default_value = 720, - DEFREF(cropw), - DEFINT(0, 864), - .get_max_value = ctrl_cropw_max_get, - .get_def_value = ctrl_get_cropcapdw, - }, { - .desc = "Capture crop height", - .name = "crop_height", - .internal_id = PVR2_CID_CROPH, - .default_value = 480, - DEFREF(croph), - DEFINT(0, 576), - .get_max_value = ctrl_croph_max_get, - .get_def_value = ctrl_get_cropcapdh, - }, { - .desc = "Capture capability pixel aspect numerator", - .name = "cropcap_pixel_numerator", - .internal_id = PVR2_CID_CROPCAPPAN, - .get_value = ctrl_get_cropcappan, - }, { - .desc = "Capture capability pixel aspect denominator", - .name = "cropcap_pixel_denominator", - .internal_id = PVR2_CID_CROPCAPPAD, - .get_value = ctrl_get_cropcappad, - }, { - .desc = "Capture capability bounds top", - .name = "cropcap_bounds_top", - .internal_id = PVR2_CID_CROPCAPBT, - .get_value = ctrl_get_cropcapbt, - }, { - .desc = "Capture capability bounds left", - .name = "cropcap_bounds_left", - .internal_id = PVR2_CID_CROPCAPBL, - .get_value = ctrl_get_cropcapbl, - }, { - .desc = "Capture capability bounds width", - .name = "cropcap_bounds_width", - .internal_id = PVR2_CID_CROPCAPBW, - .get_value = ctrl_get_cropcapbw, - }, { - .desc = "Capture capability bounds height", - .name = "cropcap_bounds_height", - .internal_id = PVR2_CID_CROPCAPBH, - .get_value = ctrl_get_cropcapbh, - },{ - .desc = "Video Source", - .name = "input", - .internal_id = PVR2_CID_INPUT, - .default_value = PVR2_CVAL_INPUT_TV, - .check_value = ctrl_check_input, - DEFREF(input), - DEFENUM(control_values_input), - },{ - .desc = "Audio Mode", - .name = "audio_mode", - .internal_id = PVR2_CID_AUDIOMODE, - .default_value = V4L2_TUNER_MODE_STEREO, - DEFREF(audiomode), - DEFENUM(control_values_audiomode), - },{ - .desc = "Horizontal capture resolution", - .name = "resolution_hor", - .internal_id = PVR2_CID_HRES, - .default_value = 720, - DEFREF(res_hor), - DEFINT(19,720), - },{ - .desc = "Vertical capture resolution", - .name = "resolution_ver", - .internal_id = PVR2_CID_VRES, - .default_value = 480, - DEFREF(res_ver), - DEFINT(17,576), - /* Hook in check for video standard and adjust maximum - depending on the standard. */ - .get_max_value = ctrl_vres_max_get, - .get_min_value = ctrl_vres_min_get, - },{ - .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, - .desc = "Audio Sampling Frequency", - .name = "srate", - DEFREF(srate), - DEFENUM(control_values_srate), - },{ - .desc = "Tuner Frequency (Hz)", - .name = "frequency", - .internal_id = PVR2_CID_FREQUENCY, - .default_value = 0, - .set_value = ctrl_freq_set, - .get_value = ctrl_freq_get, - .is_dirty = ctrl_freq_is_dirty, - .clear_dirty = ctrl_freq_clear_dirty, - DEFINT(0,0), - /* Hook in check for input value (tv/radio) and adjust - max/min values accordingly */ - .get_max_value = ctrl_freq_max_get, - .get_min_value = ctrl_freq_min_get, - },{ - .desc = "Channel", - .name = "channel", - .set_value = ctrl_channel_set, - .get_value = ctrl_channel_get, - DEFINT(0,FREQTABLE_SIZE), - },{ - .desc = "Channel Program Frequency", - .name = "freq_table_value", - .set_value = ctrl_channelfreq_set, - .get_value = ctrl_channelfreq_get, - DEFINT(0,0), - /* Hook in check for input value (tv/radio) and adjust - max/min values accordingly */ - .get_max_value = ctrl_freq_max_get, - .get_min_value = ctrl_freq_min_get, - },{ - .desc = "Channel Program ID", - .name = "freq_table_channel", - .set_value = ctrl_channelprog_set, - .get_value = ctrl_channelprog_get, - DEFINT(0,FREQTABLE_SIZE), - },{ - .desc = "Streaming Enabled", - .name = "streaming_enabled", - .get_value = ctrl_streamingenabled_get, - DEFBOOL, - },{ - .desc = "USB Speed", - .name = "usb_speed", - .get_value = ctrl_hsm_get, - DEFENUM(control_values_hsm), - },{ - .desc = "Master State", - .name = "master_state", - .get_value = ctrl_masterstate_get, - DEFENUM(pvr2_state_names), - },{ - .desc = "Signal Present", - .name = "signal_present", - .get_value = ctrl_signal_get, - DEFINT(0,65535), - },{ - .desc = "Audio Modes Present", - .name = "audio_modes_present", - .get_value = ctrl_audio_modes_present_get, - /* For this type we "borrow" the V4L2_TUNER_MODE enum from - v4l. Nothing outside of this module cares about this, - but I reuse it in order to also reuse the - control_values_audiomode string table. */ - DEFMASK(((1 << V4L2_TUNER_MODE_MONO)| - (1 << V4L2_TUNER_MODE_STEREO)| - (1 << V4L2_TUNER_MODE_LANG1)| - (1 << V4L2_TUNER_MODE_LANG2)), - control_values_audiomode), - },{ - .desc = "Video Standards Available Mask", - .name = "video_standard_mask_available", - .internal_id = PVR2_CID_STDAVAIL, - .skip_init = !0, - .get_value = ctrl_stdavail_get, - .set_value = ctrl_stdavail_set, - .val_to_sym = ctrl_std_val_to_sym, - .sym_to_val = ctrl_std_sym_to_val, - .type = pvr2_ctl_bitmask, - },{ - .desc = "Video Standards In Use Mask", - .name = "video_standard_mask_active", - .internal_id = PVR2_CID_STDCUR, - .skip_init = !0, - .get_value = ctrl_stdcur_get, - .set_value = ctrl_stdcur_set, - .is_dirty = ctrl_stdcur_is_dirty, - .clear_dirty = ctrl_stdcur_clear_dirty, - .val_to_sym = ctrl_std_val_to_sym, - .sym_to_val = ctrl_std_sym_to_val, - .type = pvr2_ctl_bitmask, - },{ - .desc = "Video Standards Detected Mask", - .name = "video_standard_mask_detected", - .internal_id = PVR2_CID_STDDETECT, - .skip_init = !0, - .get_value = ctrl_stddetect_get, - .val_to_sym = ctrl_std_val_to_sym, - .sym_to_val = ctrl_std_sym_to_val, - .type = pvr2_ctl_bitmask, - } -}; - -#define CTRLDEF_COUNT ARRAY_SIZE(control_defs) - - -const char *pvr2_config_get_name(enum pvr2_config cfg) -{ - switch (cfg) { - case pvr2_config_empty: return "empty"; - case pvr2_config_mpeg: return "mpeg"; - case pvr2_config_vbi: return "vbi"; - case pvr2_config_pcm: return "pcm"; - case pvr2_config_rawvideo: return "raw video"; - } - return ""; -} - - -struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw) -{ - return hdw->usb_dev; -} - - -unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw) -{ - return hdw->serial_number; -} - - -const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw) -{ - return hdw->bus_info; -} - - -const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw) -{ - return hdw->identifier; -} - - -unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) -{ - return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio; -} - -/* Set the currently tuned frequency and account for all possible - driver-core side effects of this action. */ -static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) -{ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - if (hdw->freqSelector) { - /* Swing over to radio frequency selection */ - hdw->freqSelector = 0; - hdw->freqDirty = !0; - } - if (hdw->freqValRadio != val) { - hdw->freqValRadio = val; - hdw->freqSlotRadio = 0; - hdw->freqDirty = !0; - } - } else { - if (!(hdw->freqSelector)) { - /* Swing over to television frequency selection */ - hdw->freqSelector = 1; - hdw->freqDirty = !0; - } - if (hdw->freqValTelevision != val) { - hdw->freqValTelevision = val; - hdw->freqSlotTelevision = 0; - hdw->freqDirty = !0; - } - } -} - -int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw) -{ - return hdw->unit_number; -} - - -/* Attempt to locate one of the given set of files. Messages are logged - appropriate to what has been found. The return value will be 0 or - greater on success (it will be the index of the file name found) and - fw_entry will be filled in. Otherwise a negative error is returned on - failure. If the return value is -ENOENT then no viable firmware file - could be located. */ -static int pvr2_locate_firmware(struct pvr2_hdw *hdw, - const struct firmware **fw_entry, - const char *fwtypename, - unsigned int fwcount, - const char *fwnames[]) -{ - unsigned int idx; - int ret = -EINVAL; - for (idx = 0; idx < fwcount; idx++) { - ret = request_firmware(fw_entry, - fwnames[idx], - &hdw->usb_dev->dev); - if (!ret) { - trace_firmware("Located %s firmware: %s;" - " uploading...", - fwtypename, - fwnames[idx]); - return idx; - } - if (ret == -ENOENT) continue; - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "request_firmware fatal error with code=%d",ret); - return ret; - } - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "***WARNING***" - " Device %s firmware" - " seems to be missing.", - fwtypename); - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Did you install the pvrusb2 firmware files" - " in their proper location?"); - if (fwcount == 1) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "request_firmware unable to locate %s file %s", - fwtypename,fwnames[0]); - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "request_firmware unable to locate" - " one of the following %s files:", - fwtypename); - for (idx = 0; idx < fwcount; idx++) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "request_firmware: Failed to find %s", - fwnames[idx]); - } - } - return ret; -} - - -/* - * pvr2_upload_firmware1(). - * - * Send the 8051 firmware to the device. After the upload, arrange for - * device to re-enumerate. - * - * NOTE : the pointer to the firmware data given by request_firmware() - * is not suitable for an usb transaction. - * - */ -static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) -{ - const struct firmware *fw_entry = NULL; - void *fw_ptr; - unsigned int pipe; - unsigned int fwsize; - int ret; - u16 address; - - if (!hdw->hdw_desc->fx2_firmware.cnt) { - hdw->fw1_state = FW1_STATE_OK; - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Connected device type defines" - " no firmware to upload; ignoring firmware"); - return -ENOTTY; - } - - hdw->fw1_state = FW1_STATE_FAILED; // default result - - trace_firmware("pvr2_upload_firmware1"); - - ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller", - hdw->hdw_desc->fx2_firmware.cnt, - hdw->hdw_desc->fx2_firmware.lst); - if (ret < 0) { - if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING; - return ret; - } - - usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); - - pipe = usb_sndctrlpipe(hdw->usb_dev, 0); - fwsize = fw_entry->size; - - if ((fwsize != 0x2000) && - (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) { - if (hdw->hdw_desc->flag_fx2_16kb) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Wrong fx2 firmware size" - " (expected 8192 or 16384, got %u)", - fwsize); - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Wrong fx2 firmware size" - " (expected 8192, got %u)", - fwsize); - } - release_firmware(fw_entry); - return -ENOMEM; - } - - fw_ptr = kmalloc(0x800, GFP_KERNEL); - if (fw_ptr == NULL){ - release_firmware(fw_entry); - return -ENOMEM; - } - - /* We have to hold the CPU during firmware upload. */ - pvr2_hdw_cpureset_assert(hdw,1); - - /* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes - chunk. */ - - ret = 0; - for (address = 0; address < fwsize; address += 0x800) { - memcpy(fw_ptr, fw_entry->data + address, 0x800); - ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address, - 0, fw_ptr, 0x800, HZ); - } - - trace_firmware("Upload done, releasing device's CPU"); - - /* Now release the CPU. It will disconnect and reconnect later. */ - pvr2_hdw_cpureset_assert(hdw,0); - - kfree(fw_ptr); - release_firmware(fw_entry); - - trace_firmware("Upload done (%d bytes sent)",ret); - - /* We should have written fwsize bytes */ - if (ret == fwsize) { - hdw->fw1_state = FW1_STATE_RELOAD; - return 0; - } - - return -EIO; -} - - -/* - * pvr2_upload_firmware2() - * - * This uploads encoder firmware on endpoint 2. - * - */ - -int pvr2_upload_firmware2(struct pvr2_hdw *hdw) -{ - const struct firmware *fw_entry = NULL; - void *fw_ptr; - unsigned int pipe, fw_len, fw_done, bcnt, icnt; - int actual_length; - int ret = 0; - int fwidx; - static const char *fw_files[] = { - CX2341X_FIRM_ENC_FILENAME, - }; - - if (hdw->hdw_desc->flag_skip_cx23416_firmware) { - return 0; - } - - trace_firmware("pvr2_upload_firmware2"); - - ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder", - ARRAY_SIZE(fw_files), fw_files); - if (ret < 0) return ret; - fwidx = ret; - ret = 0; - /* Since we're about to completely reinitialize the encoder, - invalidate our cached copy of its configuration state. Next - time we configure the encoder, then we'll fully configure it. */ - hdw->enc_cur_valid = 0; - - /* Encoder is about to be reset so note that as far as we're - concerned now, the encoder has never been run. */ - del_timer_sync(&hdw->encoder_run_timer); - if (hdw->state_encoder_runok) { - hdw->state_encoder_runok = 0; - trace_stbit("state_encoder_runok",hdw->state_encoder_runok); - } - - /* First prepare firmware loading */ - ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ - ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ - ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ - ret |= pvr2_hdw_cmd_deep_reset(hdw); - ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/ - ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/ - ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ - ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/ - ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/ - ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/ - ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/ - ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/ - ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/ - ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ - ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ - ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ - ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1); - ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); - - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "firmware2 upload prep failed, ret=%d",ret); - release_firmware(fw_entry); - goto done; - } - - /* Now send firmware */ - - fw_len = fw_entry->size; - - if (fw_len % sizeof(u32)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "size of %s firmware" - " must be a multiple of %zu bytes", - fw_files[fwidx],sizeof(u32)); - release_firmware(fw_entry); - ret = -EINVAL; - goto done; - } - - fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); - if (fw_ptr == NULL){ - release_firmware(fw_entry); - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "failed to allocate memory for firmware2 upload"); - ret = -ENOMEM; - goto done; - } - - pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); - - fw_done = 0; - for (fw_done = 0; fw_done < fw_len;) { - bcnt = fw_len - fw_done; - if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE; - memcpy(fw_ptr, fw_entry->data + fw_done, bcnt); - /* Usbsnoop log shows that we must swap bytes... */ - /* Some background info: The data being swapped here is a - firmware image destined for the mpeg encoder chip that - lives at the other end of a USB endpoint. The encoder - chip always talks in 32 bit chunks and its storage is - organized into 32 bit words. However from the file - system to the encoder chip everything is purely a byte - stream. The firmware file's contents are always 32 bit - swapped from what the encoder expects. Thus the need - always exists to swap the bytes regardless of the endian - type of the host processor and therefore swab32() makes - the most sense. */ - for (icnt = 0; icnt < bcnt/4 ; icnt++) - ((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]); - - ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt, - &actual_length, HZ); - ret |= (actual_length != bcnt); - if (ret) break; - fw_done += bcnt; - } - - trace_firmware("upload of %s : %i / %i ", - fw_files[fwidx],fw_done,fw_len); - - kfree(fw_ptr); - release_firmware(fw_entry); - - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "firmware2 upload transfer failure"); - goto done; - } - - /* Finish upload */ - - ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ - ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ - ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); - - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "firmware2 upload post-proc failure"); - } - - done: - if (hdw->hdw_desc->signal_routing_scheme == - PVR2_ROUTING_SCHEME_GOTVIEW) { - /* Ensure that GPIO 11 is set to output for GOTVIEW - hardware. */ - pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); - } - return ret; -} - - -static const char *pvr2_get_state_name(unsigned int st) -{ - if (st < ARRAY_SIZE(pvr2_state_names)) { - return pvr2_state_names[st]; - } - return "???"; -} - -static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) -{ - /* Even though we really only care about the video decoder chip at - this point, we'll broadcast stream on/off to all sub-devices - anyway, just in case somebody else wants to hear the - command... */ - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s", - (enablefl ? "on" : "off")); - v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl); - v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl); - if (hdw->decoder_client_id) { - /* We get here if the encoder has been noticed. Otherwise - we'll issue a warning to the user (which should - normally never happen). */ - return 0; - } - if (!hdw->flag_decoder_missed) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: No decoder present"); - hdw->flag_decoder_missed = !0; - trace_stbit("flag_decoder_missed", - hdw->flag_decoder_missed); - } - return -EIO; -} - - -int pvr2_hdw_get_state(struct pvr2_hdw *hdw) -{ - return hdw->master_state; -} - - -static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw) -{ - if (!hdw->flag_tripped) return 0; - hdw->flag_tripped = 0; - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Clearing driver error statuss"); - return !0; -} - - -int pvr2_hdw_untrip(struct pvr2_hdw *hdw) -{ - int fl; - LOCK_TAKE(hdw->big_lock); do { - fl = pvr2_hdw_untrip_unlocked(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); - if (fl) pvr2_hdw_state_sched(hdw); - return 0; -} - - - - -int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) -{ - return hdw->state_pipeline_req != 0; -} - - -int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag) -{ - int ret,st; - LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_untrip_unlocked(hdw); - if ((!enable_flag) != !(hdw->state_pipeline_req)) { - hdw->state_pipeline_req = enable_flag != 0; - pvr2_trace(PVR2_TRACE_START_STOP, - "/*--TRACE_STREAM--*/ %s", - enable_flag ? "enable" : "disable"); - } - pvr2_hdw_state_sched(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); - if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret; - if (enable_flag) { - while ((st = hdw->master_state) != PVR2_STATE_RUN) { - if (st != PVR2_STATE_READY) return -EIO; - if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret; - } - } - return 0; -} - - -int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config) -{ - int fl; - LOCK_TAKE(hdw->big_lock); - if ((fl = (hdw->desired_stream_type != config)) != 0) { - hdw->desired_stream_type = config; - hdw->state_pipeline_config = 0; - trace_stbit("state_pipeline_config", - hdw->state_pipeline_config); - pvr2_hdw_state_sched(hdw); - } - LOCK_GIVE(hdw->big_lock); - if (fl) return 0; - return pvr2_hdw_wait(hdw,0); -} - - -static int get_default_tuner_type(struct pvr2_hdw *hdw) -{ - int unit_number = hdw->unit_number; - int tp = -1; - if ((unit_number >= 0) && (unit_number < PVR_NUM)) { - tp = tuner[unit_number]; - } - if (tp < 0) return -EINVAL; - hdw->tuner_type = tp; - hdw->tuner_updated = !0; - return 0; -} - - -static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) -{ - int unit_number = hdw->unit_number; - int tp = 0; - if ((unit_number >= 0) && (unit_number < PVR_NUM)) { - tp = video_std[unit_number]; - if (tp) return tp; - } - return 0; -} - - -static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw) -{ - int unit_number = hdw->unit_number; - int tp = 0; - if ((unit_number >= 0) && (unit_number < PVR_NUM)) { - tp = tolerance[unit_number]; - } - return tp; -} - - -static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) -{ - /* Try a harmless request to fetch the eeprom's address over - endpoint 1. See what happens. Only the full FX2 image can - respond to this. If this probe fails then likely the FX2 - firmware needs be loaded. */ - int result; - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR; - result = pvr2_send_request_ex(hdw,HZ*1,!0, - hdw->cmd_buffer,1, - hdw->cmd_buffer,1); - if (result < 0) break; - } while(0); LOCK_GIVE(hdw->ctl_lock); - if (result) { - pvr2_trace(PVR2_TRACE_INIT, - "Probe of device endpoint 1 result status %d", - result); - } else { - pvr2_trace(PVR2_TRACE_INIT, - "Probe of device endpoint 1 succeeded"); - } - return result == 0; -} - -struct pvr2_std_hack { - v4l2_std_id pat; /* Pattern to match */ - v4l2_std_id msk; /* Which bits we care about */ - v4l2_std_id std; /* What additional standards or default to set */ -}; - -/* This data structure labels specific combinations of standards from - tveeprom that we'll try to recognize. If we recognize one, then assume - a specified default standard to use. This is here because tveeprom only - tells us about available standards not the intended default standard (if - any) for the device in question. We guess the default based on what has - been reported as available. Note that this is only for guessing a - default - which can always be overridden explicitly - and if the user - has otherwise named a default then that default will always be used in - place of this table. */ -static const struct pvr2_std_hack std_eeprom_maps[] = { - { /* PAL(B/G) */ - .pat = V4L2_STD_B|V4L2_STD_GH, - .std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G, - }, - { /* NTSC(M) */ - .pat = V4L2_STD_MN, - .std = V4L2_STD_NTSC_M, - }, - { /* PAL(I) */ - .pat = V4L2_STD_PAL_I, - .std = V4L2_STD_PAL_I, - }, - { /* SECAM(L/L') */ - .pat = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, - .std = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, - }, - { /* PAL(D/D1/K) */ - .pat = V4L2_STD_DK, - .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K, - }, -}; - -static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) -{ - char buf[40]; - unsigned int bcnt; - v4l2_std_id std1,std2,std3; - - std1 = get_default_standard(hdw); - std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask; - - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); - pvr2_trace(PVR2_TRACE_STD, - "Supported video standard(s) reported available" - " in hardware: %.*s", - bcnt,buf); - - hdw->std_mask_avail = hdw->std_mask_eeprom; - - std2 = (std1|std3) & ~hdw->std_mask_avail; - if (std2) { - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); - pvr2_trace(PVR2_TRACE_STD, - "Expanding supported video standards" - " to include: %.*s", - bcnt,buf); - hdw->std_mask_avail |= std2; - } - - hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; - - if (std1) { - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); - pvr2_trace(PVR2_TRACE_STD, - "Initial video standard forced to %.*s", - bcnt,buf); - hdw->std_mask_cur = std1; - hdw->std_dirty = !0; - return; - } - if (std3) { - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3); - pvr2_trace(PVR2_TRACE_STD, - "Initial video standard" - " (determined by device type): %.*s",bcnt,buf); - hdw->std_mask_cur = std3; - hdw->std_dirty = !0; - return; - } - - { - unsigned int idx; - for (idx = 0; idx < ARRAY_SIZE(std_eeprom_maps); idx++) { - if (std_eeprom_maps[idx].msk ? - ((std_eeprom_maps[idx].pat ^ - hdw->std_mask_eeprom) & - std_eeprom_maps[idx].msk) : - (std_eeprom_maps[idx].pat != - hdw->std_mask_eeprom)) continue; - bcnt = pvr2_std_id_to_str(buf,sizeof(buf), - std_eeprom_maps[idx].std); - pvr2_trace(PVR2_TRACE_STD, - "Initial video standard guessed as %.*s", - bcnt,buf); - hdw->std_mask_cur = std_eeprom_maps[idx].std; - hdw->std_dirty = !0; - return; - } - } - -} - - -static unsigned int pvr2_copy_i2c_addr_list( - unsigned short *dst, const unsigned char *src, - unsigned int dst_max) -{ - unsigned int cnt = 0; - if (!src) return 0; - while (src[cnt] && (cnt + 1) < dst_max) { - dst[cnt] = src[cnt]; - cnt++; - } - dst[cnt] = I2C_CLIENT_END; - return cnt; -} - - -static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) -{ - /* - Mike Isely 19-Nov-2006 - This bit of nuttiness - for cx25840 causes that module to correctly set up its video - scaling. This is really a problem in the cx25840 module itself, - but we work around it here. The problem has not been seen in - ivtv because there VBI is supported and set up. We don't do VBI - here (at least not yet) and thus we never attempted to even set - it up. - */ - struct v4l2_format fmt; - if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) { - /* We're not using a cx25840 so don't enable the hack */ - return; - } - - pvr2_trace(PVR2_TRACE_INIT, - "Module ID %u:" - " Executing cx25840 VBI hack", - hdw->decoder_client_id); - memset(&fmt, 0, sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; - v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, - vbi, s_sliced_fmt, &fmt.fmt.sliced); -} - - -static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, - const struct pvr2_device_client_desc *cd) -{ - const char *fname; - unsigned char mid; - struct v4l2_subdev *sd; - unsigned int i2ccnt; - const unsigned char *p; - /* Arbitrary count - max # i2c addresses we will probe */ - unsigned short i2caddr[25]; - - mid = cd->module_id; - fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL; - if (!fname) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u for device %s has no name?" - " The driver might have a configuration problem.", - mid, - hdw->hdw_desc->description); - return -EINVAL; - } - pvr2_trace(PVR2_TRACE_INIT, - "Module ID %u (%s) for device %s being loaded...", - mid, fname, - hdw->hdw_desc->description); - - i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list, - ARRAY_SIZE(i2caddr)); - if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ? - module_i2c_addresses[mid] : NULL) != NULL)) { - /* Second chance: Try default i2c address list */ - i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p, - ARRAY_SIZE(i2caddr)); - if (i2ccnt) { - pvr2_trace(PVR2_TRACE_INIT, - "Module ID %u:" - " Using default i2c address list", - mid); - } - } - - if (!i2ccnt) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u (%s) for device %s:" - " No i2c addresses." - " The driver might have a configuration problem.", - mid, fname, hdw->hdw_desc->description); - return -EINVAL; - } - - if (i2ccnt == 1) { - pvr2_trace(PVR2_TRACE_INIT, - "Module ID %u:" - " Setting up with specified i2c address 0x%x", - mid, i2caddr[0]); - sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, - fname, i2caddr[0], NULL); - } else { - pvr2_trace(PVR2_TRACE_INIT, - "Module ID %u:" - " Setting up with address probe list", - mid); - sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, - fname, 0, i2caddr); - } - - if (!sd) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u (%s) for device %s failed to load." - " Possible missing sub-device kernel module or" - " initialization failure within module.", - mid, fname, hdw->hdw_desc->description); - return -EIO; - } - - /* Tag this sub-device instance with the module ID we know about. - In other places we'll use that tag to determine if the instance - requires special handling. */ - sd->grp_id = mid; - - pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname); - - - /* client-specific setup... */ - switch (mid) { - case PVR2_CLIENT_ID_CX25840: - case PVR2_CLIENT_ID_SAA7115: - hdw->decoder_client_id = mid; - break; - default: break; - } - - return 0; -} - - -static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw) -{ - unsigned int idx; - const struct pvr2_string_table *cm; - const struct pvr2_device_client_table *ct; - int okFl = !0; - - cm = &hdw->hdw_desc->client_modules; - for (idx = 0; idx < cm->cnt; idx++) { - request_module(cm->lst[idx]); - } - - ct = &hdw->hdw_desc->client_table; - for (idx = 0; idx < ct->cnt; idx++) { - if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0; - } - if (!okFl) { - hdw->flag_modulefail = !0; - pvr2_hdw_render_useless(hdw); - } -} - - -static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) -{ - int ret; - unsigned int idx; - struct pvr2_ctrl *cptr; - int reloadFl = 0; - if (hdw->hdw_desc->fx2_firmware.cnt) { - if (!reloadFl) { - reloadFl = - (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints - == 0); - if (reloadFl) { - pvr2_trace(PVR2_TRACE_INIT, - "USB endpoint config looks strange" - "; possibly firmware needs to be" - " loaded"); - } - } - if (!reloadFl) { - reloadFl = !pvr2_hdw_check_firmware(hdw); - if (reloadFl) { - pvr2_trace(PVR2_TRACE_INIT, - "Check for FX2 firmware failed" - "; possibly firmware needs to be" - " loaded"); - } - } - if (reloadFl) { - if (pvr2_upload_firmware1(hdw) != 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failure uploading firmware1"); - } - return; - } - } - hdw->fw1_state = FW1_STATE_OK; - - if (!pvr2_hdw_dev_ok(hdw)) return; - - hdw->force_dirty = !0; - - if (!hdw->hdw_desc->flag_no_powerup) { - pvr2_hdw_cmd_powerup(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; - } - - /* Take the IR chip out of reset, if appropriate */ - if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) { - pvr2_issue_simple_cmd(hdw, - FX2CMD_HCW_ZILOG_RESET | - (1 << 8) | - ((0) << 16)); - } - - // This step MUST happen after the earlier powerup step. - pvr2_i2c_core_init(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; - - pvr2_hdw_load_modules(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; - - v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw); - - for (idx = 0; idx < CTRLDEF_COUNT; idx++) { - cptr = hdw->controls + idx; - if (cptr->info->skip_init) continue; - if (!cptr->info->set_value) continue; - cptr->info->set_value(cptr,~0,cptr->info->default_value); - } - - pvr2_hdw_cx25840_vbi_hack(hdw); - - /* Set up special default values for the television and radio - frequencies here. It's not really important what these defaults - are, but I set them to something usable in the Chicago area just - to make driver testing a little easier. */ - - hdw->freqValTelevision = default_tv_freq; - hdw->freqValRadio = default_radio_freq; - - // Do not use pvr2_reset_ctl_endpoints() here. It is not - // thread-safe against the normal pvr2_send_request() mechanism. - // (We should make it thread safe). - - if (hdw->hdw_desc->flag_has_hauppauge_rom) { - ret = pvr2_hdw_get_eeprom_addr(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Unable to determine location of eeprom," - " skipping"); - } else { - hdw->eeprom_addr = ret; - pvr2_eeprom_analyze(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; - } - } else { - hdw->tuner_type = hdw->hdw_desc->default_tuner_type; - hdw->tuner_updated = !0; - hdw->std_mask_eeprom = V4L2_STD_ALL; - } - - if (hdw->serial_number) { - idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, - "sn-%lu", hdw->serial_number); - } else if (hdw->unit_number >= 0) { - idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, - "unit-%c", - hdw->unit_number + 'a'); - } else { - idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, - "unit-??"); - } - hdw->identifier[idx] = 0; - - pvr2_hdw_setup_std(hdw); - - if (!get_default_tuner_type(hdw)) { - pvr2_trace(PVR2_TRACE_INIT, - "pvr2_hdw_setup: Tuner type overridden to %d", - hdw->tuner_type); - } - - - if (!pvr2_hdw_dev_ok(hdw)) return; - - if (hdw->hdw_desc->signal_routing_scheme == - PVR2_ROUTING_SCHEME_GOTVIEW) { - /* Ensure that GPIO 11 is set to output for GOTVIEW - hardware. */ - pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); - } - - pvr2_hdw_commit_setup(hdw); - - hdw->vid_stream = pvr2_stream_create(); - if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_trace(PVR2_TRACE_INIT, - "pvr2_hdw_setup: video stream is %p",hdw->vid_stream); - if (hdw->vid_stream) { - idx = get_default_error_tolerance(hdw); - if (idx) { - pvr2_trace(PVR2_TRACE_INIT, - "pvr2_hdw_setup: video stream %p" - " setting tolerance %u", - hdw->vid_stream,idx); - } - pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev, - PVR2_VID_ENDPOINT,idx); - } - - if (!pvr2_hdw_dev_ok(hdw)) return; - - hdw->flag_init_ok = !0; - - pvr2_hdw_state_sched(hdw); -} - - -/* Set up the structure and attempt to put the device into a usable state. - This can be a time-consuming operation, which is why it is not done - internally as part of the create() step. */ -static void pvr2_hdw_setup(struct pvr2_hdw *hdw) -{ - pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw); - do { - pvr2_hdw_setup_low(hdw); - pvr2_trace(PVR2_TRACE_INIT, - "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d", - hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok); - if (pvr2_hdw_dev_ok(hdw)) { - if (hdw->flag_init_ok) { - pvr2_trace( - PVR2_TRACE_INFO, - "Device initialization" - " completed successfully."); - break; - } - if (hdw->fw1_state == FW1_STATE_RELOAD) { - pvr2_trace( - PVR2_TRACE_INFO, - "Device microcontroller firmware" - " (re)loaded; it should now reset" - " and reconnect."); - break; - } - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Device initialization was not successful."); - if (hdw->fw1_state == FW1_STATE_MISSING) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Giving up since device" - " microcontroller firmware" - " appears to be missing."); - break; - } - } - if (hdw->flag_modulefail) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "***WARNING*** pvrusb2 driver initialization" - " failed due to the failure of one or more" - " sub-device kernel modules."); - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "You need to resolve the failing condition" - " before this driver can function. There" - " should be some earlier messages giving more" - " information about the problem."); - break; - } - if (procreload) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Attempting pvrusb2 recovery by reloading" - " primary firmware."); - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "If this works, device should disconnect" - " and reconnect in a sane state."); - hdw->fw1_state = FW1_STATE_UNKNOWN; - pvr2_upload_firmware1(hdw); - } else { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "***WARNING*** pvrusb2 device hardware" - " appears to be jammed" - " and I can't clear it."); - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "You might need to power cycle" - " the pvrusb2 device" - " in order to recover."); - } - } while (0); - pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw); -} - - -/* Perform second stage initialization. Set callback pointer first so that - we can avoid a possible initialization race (if the kernel thread runs - before the callback has been set). */ -int pvr2_hdw_initialize(struct pvr2_hdw *hdw, - void (*callback_func)(void *), - void *callback_data) -{ - LOCK_TAKE(hdw->big_lock); do { - if (hdw->flag_disconnected) { - /* Handle a race here: If we're already - disconnected by this point, then give up. If we - get past this then we'll remain connected for - the duration of initialization since the entire - initialization sequence is now protected by the - big_lock. */ - break; - } - hdw->state_data = callback_data; - hdw->state_func = callback_func; - pvr2_hdw_setup(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); - return hdw->flag_init_ok; -} - - -/* Create, set up, and return a structure for interacting with the - underlying hardware. */ -struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, - const struct usb_device_id *devid) -{ - unsigned int idx,cnt1,cnt2,m; - struct pvr2_hdw *hdw = NULL; - int valid_std_mask; - struct pvr2_ctrl *cptr; - struct usb_device *usb_dev; - const struct pvr2_device_desc *hdw_desc; - __u8 ifnum; - struct v4l2_queryctrl qctrl; - struct pvr2_ctl_info *ciptr; - - usb_dev = interface_to_usbdev(intf); - - hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info); - - if (hdw_desc == NULL) { - pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create:" - " No device description pointer," - " unable to continue."); - pvr2_trace(PVR2_TRACE_INIT, "If you have a new device type," - " please contact Mike Isely " - " to get it included in the driver\n"); - goto fail; - } - - hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); - pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", - hdw,hdw_desc->description); - pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s", - hdw_desc->description); - if (hdw_desc->flag_is_experimental) { - pvr2_trace(PVR2_TRACE_INFO, "**********"); - pvr2_trace(PVR2_TRACE_INFO, - "WARNING: Support for this device (%s) is" - " experimental.", hdw_desc->description); - pvr2_trace(PVR2_TRACE_INFO, - "Important functionality might not be" - " entirely working."); - pvr2_trace(PVR2_TRACE_INFO, - "Please consider contacting the driver author to" - " help with further stabilization of the driver."); - pvr2_trace(PVR2_TRACE_INFO, "**********"); - } - if (!hdw) goto fail; - - init_timer(&hdw->quiescent_timer); - hdw->quiescent_timer.data = (unsigned long)hdw; - hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout; - - init_timer(&hdw->decoder_stabilization_timer); - hdw->decoder_stabilization_timer.data = (unsigned long)hdw; - hdw->decoder_stabilization_timer.function = - pvr2_hdw_decoder_stabilization_timeout; - - init_timer(&hdw->encoder_wait_timer); - hdw->encoder_wait_timer.data = (unsigned long)hdw; - hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; - - init_timer(&hdw->encoder_run_timer); - hdw->encoder_run_timer.data = (unsigned long)hdw; - hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout; - - hdw->master_state = PVR2_STATE_DEAD; - - init_waitqueue_head(&hdw->state_wait_data); - - hdw->tuner_signal_stale = !0; - cx2341x_fill_defaults(&hdw->enc_ctl_state); - - /* Calculate which inputs are OK */ - m = 0; - if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV; - if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) { - m |= 1 << PVR2_CVAL_INPUT_DTV; - } - if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO; - if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; - if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; - hdw->input_avail_mask = m; - hdw->input_allowed_mask = hdw->input_avail_mask; - - /* If not a hybrid device, pathway_state never changes. So - initialize it here to what it should forever be. */ - if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) { - hdw->pathway_state = PVR2_PATHWAY_ANALOG; - } else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) { - hdw->pathway_state = PVR2_PATHWAY_DIGITAL; - } - - hdw->control_cnt = CTRLDEF_COUNT; - hdw->control_cnt += MPEGDEF_COUNT; - hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, - GFP_KERNEL); - if (!hdw->controls) goto fail; - hdw->hdw_desc = hdw_desc; - hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme; - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - cptr->hdw = hdw; - } - for (idx = 0; idx < 32; idx++) { - hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx]; - } - for (idx = 0; idx < CTRLDEF_COUNT; idx++) { - cptr = hdw->controls + idx; - cptr->info = control_defs+idx; - } - - /* Ensure that default input choice is a valid one. */ - m = hdw->input_avail_mask; - if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) { - if (!((1 << idx) & m)) continue; - hdw->input_val = idx; - break; - } - - /* Define and configure additional controls from cx2341x module. */ - hdw->mpeg_ctrl_info = kcalloc(MPEGDEF_COUNT, - sizeof(*(hdw->mpeg_ctrl_info)), - GFP_KERNEL); - if (!hdw->mpeg_ctrl_info) goto fail; - for (idx = 0; idx < MPEGDEF_COUNT; idx++) { - cptr = hdw->controls + idx + CTRLDEF_COUNT; - ciptr = &(hdw->mpeg_ctrl_info[idx].info); - ciptr->desc = hdw->mpeg_ctrl_info[idx].desc; - ciptr->name = mpeg_ids[idx].strid; - ciptr->v4l_id = mpeg_ids[idx].id; - ciptr->skip_init = !0; - ciptr->get_value = ctrl_cx2341x_get; - ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags; - ciptr->is_dirty = ctrl_cx2341x_is_dirty; - if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty; - qctrl.id = ciptr->v4l_id; - cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl); - if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) { - ciptr->set_value = ctrl_cx2341x_set; - } - strncpy(hdw->mpeg_ctrl_info[idx].desc,qctrl.name, - PVR2_CTLD_INFO_DESC_SIZE); - hdw->mpeg_ctrl_info[idx].desc[PVR2_CTLD_INFO_DESC_SIZE-1] = 0; - ciptr->default_value = qctrl.default_value; - switch (qctrl.type) { - default: - case V4L2_CTRL_TYPE_INTEGER: - ciptr->type = pvr2_ctl_int; - ciptr->def.type_int.min_value = qctrl.minimum; - ciptr->def.type_int.max_value = qctrl.maximum; - break; - case V4L2_CTRL_TYPE_BOOLEAN: - ciptr->type = pvr2_ctl_bool; - break; - case V4L2_CTRL_TYPE_MENU: - ciptr->type = pvr2_ctl_enum; - ciptr->def.type_enum.value_names = - cx2341x_ctrl_get_menu(&hdw->enc_ctl_state, - ciptr->v4l_id); - for (cnt1 = 0; - ciptr->def.type_enum.value_names[cnt1] != NULL; - cnt1++) { } - ciptr->def.type_enum.count = cnt1; - break; - } - cptr->info = ciptr; - } - - // Initialize control data regarding video standard masks - valid_std_mask = pvr2_std_get_usable(); - for (idx = 0; idx < 32; idx++) { - if (!(valid_std_mask & (1 << idx))) continue; - cnt1 = pvr2_std_id_to_str( - hdw->std_mask_names[idx], - sizeof(hdw->std_mask_names[idx])-1, - 1 << idx); - hdw->std_mask_names[idx][cnt1] = 0; - } - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL); - if (cptr) { - memcpy(&hdw->std_info_avail,cptr->info, - sizeof(hdw->std_info_avail)); - cptr->info = &hdw->std_info_avail; - hdw->std_info_avail.def.type_bitmask.bit_names = - hdw->std_mask_ptrs; - hdw->std_info_avail.def.type_bitmask.valid_bits = - valid_std_mask; - } - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR); - if (cptr) { - memcpy(&hdw->std_info_cur,cptr->info, - sizeof(hdw->std_info_cur)); - cptr->info = &hdw->std_info_cur; - hdw->std_info_cur.def.type_bitmask.bit_names = - hdw->std_mask_ptrs; - hdw->std_info_cur.def.type_bitmask.valid_bits = - valid_std_mask; - } - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT); - if (cptr) { - memcpy(&hdw->std_info_detect,cptr->info, - sizeof(hdw->std_info_detect)); - cptr->info = &hdw->std_info_detect; - hdw->std_info_detect.def.type_bitmask.bit_names = - hdw->std_mask_ptrs; - hdw->std_info_detect.def.type_bitmask.valid_bits = - valid_std_mask; - } - - hdw->cropcap_stale = !0; - hdw->eeprom_addr = -1; - hdw->unit_number = -1; - hdw->v4l_minor_number_video = -1; - hdw->v4l_minor_number_vbi = -1; - hdw->v4l_minor_number_radio = -1; - hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); - if (!hdw->ctl_write_buffer) goto fail; - hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); - if (!hdw->ctl_read_buffer) goto fail; - hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL); - if (!hdw->ctl_write_urb) goto fail; - hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL); - if (!hdw->ctl_read_urb) goto fail; - - if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Error registering with v4l core, giving up"); - goto fail; - } - mutex_lock(&pvr2_unit_mtx); do { - for (idx = 0; idx < PVR_NUM; idx++) { - if (unit_pointers[idx]) continue; - hdw->unit_number = idx; - unit_pointers[idx] = hdw; - break; - } - } while (0); mutex_unlock(&pvr2_unit_mtx); - - cnt1 = 0; - cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2"); - cnt1 += cnt2; - if (hdw->unit_number >= 0) { - cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c", - ('a' + hdw->unit_number)); - cnt1 += cnt2; - } - if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1; - hdw->name[cnt1] = 0; - - hdw->workqueue = create_singlethread_workqueue(hdw->name); - INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); - - pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", - hdw->unit_number,hdw->name); - - hdw->tuner_type = -1; - hdw->flag_ok = !0; - - hdw->usb_intf = intf; - hdw->usb_dev = usb_dev; - - usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info)); - - ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; - usb_set_interface(hdw->usb_dev,ifnum,0); - - mutex_init(&hdw->ctl_lock_mutex); - mutex_init(&hdw->big_lock_mutex); - - return hdw; - fail: - if (hdw) { - del_timer_sync(&hdw->quiescent_timer); - del_timer_sync(&hdw->decoder_stabilization_timer); - del_timer_sync(&hdw->encoder_run_timer); - del_timer_sync(&hdw->encoder_wait_timer); - if (hdw->workqueue) { - flush_workqueue(hdw->workqueue); - destroy_workqueue(hdw->workqueue); - hdw->workqueue = NULL; - } - usb_free_urb(hdw->ctl_read_urb); - usb_free_urb(hdw->ctl_write_urb); - kfree(hdw->ctl_read_buffer); - kfree(hdw->ctl_write_buffer); - kfree(hdw->controls); - kfree(hdw->mpeg_ctrl_info); - kfree(hdw); - } - return NULL; -} - - -/* Remove _all_ associations between this driver and the underlying USB - layer. */ -static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw) -{ - if (hdw->flag_disconnected) return; - pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw); - if (hdw->ctl_read_urb) { - usb_kill_urb(hdw->ctl_read_urb); - usb_free_urb(hdw->ctl_read_urb); - hdw->ctl_read_urb = NULL; - } - if (hdw->ctl_write_urb) { - usb_kill_urb(hdw->ctl_write_urb); - usb_free_urb(hdw->ctl_write_urb); - hdw->ctl_write_urb = NULL; - } - if (hdw->ctl_read_buffer) { - kfree(hdw->ctl_read_buffer); - hdw->ctl_read_buffer = NULL; - } - if (hdw->ctl_write_buffer) { - kfree(hdw->ctl_write_buffer); - hdw->ctl_write_buffer = NULL; - } - hdw->flag_disconnected = !0; - /* If we don't do this, then there will be a dangling struct device - reference to our disappearing device persisting inside the V4L - core... */ - v4l2_device_disconnect(&hdw->v4l2_dev); - hdw->usb_dev = NULL; - hdw->usb_intf = NULL; - pvr2_hdw_render_useless(hdw); -} - - -/* Destroy hardware interaction structure */ -void pvr2_hdw_destroy(struct pvr2_hdw *hdw) -{ - if (!hdw) return; - pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); - if (hdw->workqueue) { - flush_workqueue(hdw->workqueue); - destroy_workqueue(hdw->workqueue); - hdw->workqueue = NULL; - } - del_timer_sync(&hdw->quiescent_timer); - del_timer_sync(&hdw->decoder_stabilization_timer); - del_timer_sync(&hdw->encoder_run_timer); - del_timer_sync(&hdw->encoder_wait_timer); - if (hdw->fw_buffer) { - kfree(hdw->fw_buffer); - hdw->fw_buffer = NULL; - } - if (hdw->vid_stream) { - pvr2_stream_destroy(hdw->vid_stream); - hdw->vid_stream = NULL; - } - pvr2_i2c_core_done(hdw); - v4l2_device_unregister(&hdw->v4l2_dev); - pvr2_hdw_remove_usb_stuff(hdw); - mutex_lock(&pvr2_unit_mtx); do { - if ((hdw->unit_number >= 0) && - (hdw->unit_number < PVR_NUM) && - (unit_pointers[hdw->unit_number] == hdw)) { - unit_pointers[hdw->unit_number] = NULL; - } - } while (0); mutex_unlock(&pvr2_unit_mtx); - kfree(hdw->controls); - kfree(hdw->mpeg_ctrl_info); - kfree(hdw); -} - - -int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw) -{ - return (hdw && hdw->flag_ok); -} - - -/* Called when hardware has been unplugged */ -void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) -{ - pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw); - LOCK_TAKE(hdw->big_lock); - LOCK_TAKE(hdw->ctl_lock); - pvr2_hdw_remove_usb_stuff(hdw); - LOCK_GIVE(hdw->ctl_lock); - LOCK_GIVE(hdw->big_lock); -} - - -/* Get the number of defined controls */ -unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) -{ - return hdw->control_cnt; -} - - -/* Retrieve a control handle given its index (0..count-1) */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw, - unsigned int idx) -{ - if (idx >= hdw->control_cnt) return NULL; - return hdw->controls + idx; -} - - -/* Retrieve a control handle given its index (0..count-1) */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw, - unsigned int ctl_id) -{ - struct pvr2_ctrl *cptr; - unsigned int idx; - int i; - - /* This could be made a lot more efficient, but for now... */ - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - i = cptr->info->internal_id; - if (i && (i == ctl_id)) return cptr; - } - return NULL; -} - - -/* Given a V4L ID, retrieve the control structure associated with it. */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id) -{ - struct pvr2_ctrl *cptr; - unsigned int idx; - int i; - - /* This could be made a lot more efficient, but for now... */ - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - i = cptr->info->v4l_id; - if (i && (i == ctl_id)) return cptr; - } - return NULL; -} - - -/* Given a V4L ID for its immediate predecessor, retrieve the control - structure associated with it. */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw, - unsigned int ctl_id) -{ - struct pvr2_ctrl *cptr,*cp2; - unsigned int idx; - int i; - - /* This could be made a lot more efficient, but for now... */ - cp2 = NULL; - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - i = cptr->info->v4l_id; - if (!i) continue; - if (i <= ctl_id) continue; - if (cp2 && (cp2->info->v4l_id < i)) continue; - cp2 = cptr; - } - return cp2; - return NULL; -} - - -static const char *get_ctrl_typename(enum pvr2_ctl_type tp) -{ - switch (tp) { - case pvr2_ctl_int: return "integer"; - case pvr2_ctl_enum: return "enum"; - case pvr2_ctl_bool: return "boolean"; - case pvr2_ctl_bitmask: return "bitmask"; - } - return ""; -} - - -static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, - const char *name, int val) -{ - struct v4l2_control ctrl; - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val); - memset(&ctrl, 0, sizeof(ctrl)); - ctrl.id = id; - ctrl.value = val; - v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl); -} - -#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \ - if ((hdw)->lab##_dirty || (hdw)->force_dirty) { \ - pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ - } - -v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw) -{ - v4l2_std_id std; - std = (v4l2_std_id)hdw->std_mask_avail; - v4l2_device_call_all(&hdw->v4l2_dev, 0, - video, querystd, &std); - return std; -} - -/* Execute whatever commands are required to update the state of all the - sub-devices so that they match our current control values. */ -static void pvr2_subdev_update(struct pvr2_hdw *hdw) -{ - struct v4l2_subdev *sd; - unsigned int id; - pvr2_subdev_update_func fp; - - pvr2_trace(PVR2_TRACE_CHIPS, "subdev update..."); - - if (hdw->tuner_updated || hdw->force_dirty) { - struct tuner_setup setup; - pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)", - hdw->tuner_type); - if (((int)(hdw->tuner_type)) >= 0) { - memset(&setup, 0, sizeof(setup)); - setup.addr = ADDR_UNSET; - setup.type = hdw->tuner_type; - setup.mode_mask = T_RADIO | T_ANALOG_TV; - v4l2_device_call_all(&hdw->v4l2_dev, 0, - tuner, s_type_addr, &setup); - } - } - - if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) { - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard"); - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - v4l2_device_call_all(&hdw->v4l2_dev, 0, - tuner, s_radio); - } else { - v4l2_std_id vs; - vs = hdw->std_mask_cur; - v4l2_device_call_all(&hdw->v4l2_dev, 0, - core, s_std, vs); - pvr2_hdw_cx25840_vbi_hack(hdw); - } - hdw->tuner_signal_stale = !0; - hdw->cropcap_stale = !0; - } - - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass); - PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble); - - if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) { - struct v4l2_tuner vt; - memset(&vt, 0, sizeof(vt)); - vt.type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - vt.audmode = hdw->audiomode_val; - v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt); - } - - if (hdw->freqDirty || hdw->force_dirty) { - unsigned long fv; - struct v4l2_frequency freq; - fv = pvr2_hdw_get_cur_freq(hdw); - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv); - if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw); - memset(&freq, 0, sizeof(freq)); - if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { - /* ((fv * 1000) / 62500) */ - freq.frequency = (fv * 2) / 125; - } else { - freq.frequency = fv / 62500; - } - /* tuner-core currently doesn't seem to care about this, but - let's set it anyway for completeness. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - freq.type = V4L2_TUNER_RADIO; - } else { - freq.type = V4L2_TUNER_ANALOG_TV; - } - freq.tuner = 0; - v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, - s_frequency, &freq); - } - - if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) { - struct v4l2_mbus_framefmt fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.width = hdw->res_hor_val; - fmt.height = hdw->res_ver_val; - fmt.code = V4L2_MBUS_FMT_FIXED; - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)", - fmt.width, fmt.height); - v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_mbus_fmt, &fmt); - } - - if (hdw->srate_dirty || hdw->force_dirty) { - u32 val; - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d", - hdw->srate_val); - switch (hdw->srate_val) { - default: - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: - val = 48000; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: - val = 44100; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: - val = 32000; - break; - } - v4l2_device_call_all(&hdw->v4l2_dev, 0, - audio, s_clock_freq, val); - } - - /* Unable to set crop parameters; there is apparently no equivalent - for VIDIOC_S_CROP */ - - v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { - id = sd->grp_id; - if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue; - fp = pvr2_module_update_functions[id]; - if (!fp) continue; - (*fp)(hdw, sd); - } - - if (hdw->tuner_signal_stale || hdw->cropcap_stale) { - pvr2_hdw_status_poll(hdw); - } -} - - -/* Figure out if we need to commit control changes. If so, mark internal - state flags to indicate this fact and return true. Otherwise do nothing - else and return false. */ -static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw) -{ - unsigned int idx; - struct pvr2_ctrl *cptr; - int value; - int commit_flag = hdw->force_dirty; - char buf[100]; - unsigned int bcnt,ccnt; - - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - if (!cptr->info->is_dirty) continue; - if (!cptr->info->is_dirty(cptr)) continue; - commit_flag = !0; - - if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue; - bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ", - cptr->info->name); - value = 0; - cptr->info->get_value(cptr,&value); - pvr2_ctrl_value_to_sym_internal(cptr,~0,value, - buf+bcnt, - sizeof(buf)-bcnt,&ccnt); - bcnt += ccnt; - bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>", - get_ctrl_typename(cptr->info->type)); - pvr2_trace(PVR2_TRACE_CTL, - "/*--TRACE_COMMIT--*/ %.*s", - bcnt,buf); - } - - if (!commit_flag) { - /* Nothing has changed */ - return 0; - } - - hdw->state_pipeline_config = 0; - trace_stbit("state_pipeline_config",hdw->state_pipeline_config); - pvr2_hdw_state_sched(hdw); - - return !0; -} - - -/* Perform all operations needed to commit all control changes. This must - be performed in synchronization with the pipeline state and is thus - expected to be called as part of the driver's worker thread. Return - true if commit successful, otherwise return false to indicate that - commit isn't possible at this time. */ -static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) -{ - unsigned int idx; - struct pvr2_ctrl *cptr; - int disruptive_change; - - if (hdw->input_dirty && hdw->state_pathway_ok && - (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? - PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != - hdw->pathway_state)) { - /* Change of mode being asked for... */ - hdw->state_pathway_ok = 0; - trace_stbit("state_pathway_ok", hdw->state_pathway_ok); - } - if (!hdw->state_pathway_ok) { - /* Can't commit anything until pathway is ok. */ - return 0; - } - - /* Handle some required side effects when the video standard is - changed.... */ - if (hdw->std_dirty) { - int nvres; - int gop_size; - if (hdw->std_mask_cur & V4L2_STD_525_60) { - nvres = 480; - gop_size = 15; - } else { - nvres = 576; - gop_size = 12; - } - /* Rewrite the vertical resolution to be appropriate to the - video standard that has been selected. */ - if (nvres != hdw->res_ver_val) { - hdw->res_ver_val = nvres; - hdw->res_ver_dirty = !0; - } - /* Rewrite the GOP size to be appropriate to the video - standard that has been selected. */ - if (gop_size != hdw->enc_ctl_state.video_gop_size) { - struct v4l2_ext_controls cs; - struct v4l2_ext_control c1; - memset(&cs, 0, sizeof(cs)); - memset(&c1, 0, sizeof(c1)); - cs.controls = &c1; - cs.count = 1; - c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; - c1.value = gop_size; - cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs, - VIDIOC_S_EXT_CTRLS); - } - } - - /* The broadcast decoder can only scale down, so if - * res_*_dirty && crop window < output format ==> enlarge crop. - * - * The mpeg encoder receives fields of res_hor_val dots and - * res_ver_val halflines. Limits: hor<=720, ver<=576. - */ - if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) { - hdw->cropw_val = hdw->res_hor_val; - hdw->cropw_dirty = !0; - } else if (hdw->cropw_dirty) { - hdw->res_hor_dirty = !0; /* must rescale */ - hdw->res_hor_val = min(720, hdw->cropw_val); - } - if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) { - hdw->croph_val = hdw->res_ver_val; - hdw->croph_dirty = !0; - } else if (hdw->croph_dirty) { - int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576; - hdw->res_ver_dirty = !0; - hdw->res_ver_val = min(nvres, hdw->croph_val); - } - - /* If any of the below has changed, then we can't do the update - while the pipeline is running. Pipeline must be paused first - and decoder -> encoder connection be made quiescent before we - can proceed. */ - disruptive_change = - (hdw->std_dirty || - hdw->enc_unsafe_stale || - hdw->srate_dirty || - hdw->res_ver_dirty || - hdw->res_hor_dirty || - hdw->cropw_dirty || - hdw->croph_dirty || - hdw->input_dirty || - (hdw->active_stream_type != hdw->desired_stream_type)); - if (disruptive_change && !hdw->state_pipeline_idle) { - /* Pipeline is not idle; we can't proceed. Arrange to - cause pipeline to stop so that we can try this again - later.... */ - hdw->state_pipeline_pause = !0; - return 0; - } - - if (hdw->srate_dirty) { - /* Write new sample rate into control structure since - * the master copy is stale. We must track srate - * separate from the mpeg control structure because - * other logic also uses this value. */ - struct v4l2_ext_controls cs; - struct v4l2_ext_control c1; - memset(&cs,0,sizeof(cs)); - memset(&c1,0,sizeof(c1)); - cs.controls = &c1; - cs.count = 1; - c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ; - c1.value = hdw->srate_val; - cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS); - } - - if (hdw->active_stream_type != hdw->desired_stream_type) { - /* Handle any side effects of stream config here */ - hdw->active_stream_type = hdw->desired_stream_type; - } - - if (hdw->hdw_desc->signal_routing_scheme == - PVR2_ROUTING_SCHEME_GOTVIEW) { - u32 b; - /* Handle GOTVIEW audio switching */ - pvr2_hdw_gpio_get_out(hdw,&b); - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - /* Set GPIO 11 */ - pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0); - } else { - /* Clear GPIO 11 */ - pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0); - } - } - - /* Check and update state for all sub-devices. */ - pvr2_subdev_update(hdw); - - hdw->tuner_updated = 0; - hdw->force_dirty = 0; - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - if (!cptr->info->clear_dirty) continue; - cptr->info->clear_dirty(cptr); - } - - if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) && - hdw->state_encoder_run) { - /* If encoder isn't running or it can't be touched, then - this will get worked out later when we start the - encoder. */ - if (pvr2_encoder_adjust(hdw) < 0) return !0; - } - - hdw->state_pipeline_config = !0; - /* Hardware state may have changed in a way to cause the cropping - capabilities to have changed. So mark it stale, which will - cause a later re-fetch. */ - trace_stbit("state_pipeline_config",hdw->state_pipeline_config); - return !0; -} - - -int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) -{ - int fl; - LOCK_TAKE(hdw->big_lock); - fl = pvr2_hdw_commit_setup(hdw); - LOCK_GIVE(hdw->big_lock); - if (!fl) return 0; - return pvr2_hdw_wait(hdw,0); -} - - -static void pvr2_hdw_worker_poll(struct work_struct *work) -{ - int fl = 0; - struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll); - LOCK_TAKE(hdw->big_lock); do { - fl = pvr2_hdw_state_eval(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); - if (fl && hdw->state_func) { - hdw->state_func(hdw->state_data); - } -} - - -static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) -{ - return wait_event_interruptible( - hdw->state_wait_data, - (hdw->state_stale == 0) && - (!state || (hdw->master_state != state))); -} - - -/* Return name for this driver instance */ -const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) -{ - return hdw->name; -} - - -const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw) -{ - return hdw->hdw_desc->description; -} - - -const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw) -{ - return hdw->hdw_desc->shortname; -} - - -int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) -{ - int result; - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED; - result = pvr2_send_request(hdw, - hdw->cmd_buffer,1, - hdw->cmd_buffer,1); - if (result < 0) break; - result = (hdw->cmd_buffer[0] != 0); - } while(0); LOCK_GIVE(hdw->ctl_lock); - return result; -} - - -/* Execute poll of tuner status */ -void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) -{ - LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_status_poll(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); -} - - -static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw) -{ - if (!hdw->cropcap_stale) { - return 0; - } - pvr2_hdw_status_poll(hdw); - if (hdw->cropcap_stale) { - return -EIO; - } - return 0; -} - - -/* Return information about cropping capabilities */ -int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp) -{ - int stat = 0; - LOCK_TAKE(hdw->big_lock); - stat = pvr2_hdw_check_cropcap(hdw); - if (!stat) { - memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info)); - } - LOCK_GIVE(hdw->big_lock); - return stat; -} - - -/* Return information about the tuner */ -int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) -{ - LOCK_TAKE(hdw->big_lock); do { - if (hdw->tuner_signal_stale) { - pvr2_hdw_status_poll(hdw); - } - memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner)); - } while (0); LOCK_GIVE(hdw->big_lock); - return 0; -} - - -/* Get handle to video output stream */ -struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp) -{ - return hp->vid_stream; -} - - -void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) -{ - int nr = pvr2_hdw_get_unit_number(hdw); - LOCK_TAKE(hdw->big_lock); do { - printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); - v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status); - pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); - cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); - pvr2_hdw_state_log_state(hdw); - printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); - } while (0); LOCK_GIVE(hdw->big_lock); -} - - -/* Grab EEPROM contents, needed for direct method. */ -#define EEPROM_SIZE 8192 -#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) -static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw) -{ - struct i2c_msg msg[2]; - u8 *eeprom; - u8 iadd[2]; - u8 addr; - u16 eepromSize; - unsigned int offs; - int ret; - int mode16 = 0; - unsigned pcnt,tcnt; - eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL); - if (!eeprom) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to allocate memory" - " required to read eeprom"); - return NULL; - } - - trace_eeprom("Value for eeprom addr from controller was 0x%x", - hdw->eeprom_addr); - addr = hdw->eeprom_addr; - /* Seems that if the high bit is set, then the *real* eeprom - address is shifted right now bit position (noticed this in - newer PVR USB2 hardware) */ - if (addr & 0x80) addr >>= 1; - - /* FX2 documentation states that a 16bit-addressed eeprom is - expected if the I2C address is an odd number (yeah, this is - strange but it's what they do) */ - mode16 = (addr & 1); - eepromSize = (mode16 ? EEPROM_SIZE : 256); - trace_eeprom("Examining %d byte eeprom at location 0x%x" - " using %d bit addressing",eepromSize,addr, - mode16 ? 16 : 8); - - msg[0].addr = addr; - msg[0].flags = 0; - msg[0].len = mode16 ? 2 : 1; - msg[0].buf = iadd; - msg[1].addr = addr; - msg[1].flags = I2C_M_RD; - - /* We have to do the actual eeprom data fetch ourselves, because - (1) we're only fetching part of the eeprom, and (2) if we were - getting the whole thing our I2C driver can't grab it in one - pass - which is what tveeprom is otherwise going to attempt */ - memset(eeprom,0,EEPROM_SIZE); - for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) { - pcnt = 16; - if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt; - offs = tcnt + (eepromSize - EEPROM_SIZE); - if (mode16) { - iadd[0] = offs >> 8; - iadd[1] = offs; - } else { - iadd[0] = offs; - } - msg[1].len = pcnt; - msg[1].buf = eeprom+tcnt; - if ((ret = i2c_transfer(&hdw->i2c_adap, - msg,ARRAY_SIZE(msg))) != 2) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "eeprom fetch set offs err=%d",ret); - kfree(eeprom); - return NULL; - } - } - return eeprom; -} - - -void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, - int mode, - int enable_flag) -{ - int ret; - u16 address; - unsigned int pipe; - LOCK_TAKE(hdw->big_lock); do { - if ((hdw->fw_buffer == NULL) == !enable_flag) break; - - if (!enable_flag) { - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Cleaning up after CPU firmware fetch"); - kfree(hdw->fw_buffer); - hdw->fw_buffer = NULL; - hdw->fw_size = 0; - if (hdw->fw_cpu_flag) { - /* Now release the CPU. It will disconnect - and reconnect later. */ - pvr2_hdw_cpureset_assert(hdw,0); - } - break; - } - - hdw->fw_cpu_flag = (mode != 2); - if (hdw->fw_cpu_flag) { - hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000; - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Preparing to suck out CPU firmware" - " (size=%u)", hdw->fw_size); - hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL); - if (!hdw->fw_buffer) { - hdw->fw_size = 0; - break; - } - - /* We have to hold the CPU during firmware upload. */ - pvr2_hdw_cpureset_assert(hdw,1); - - /* download the firmware from address 0000-1fff in 2048 - (=0x800) bytes chunk. */ - - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Grabbing CPU firmware"); - pipe = usb_rcvctrlpipe(hdw->usb_dev, 0); - for(address = 0; address < hdw->fw_size; - address += 0x800) { - ret = usb_control_msg(hdw->usb_dev,pipe, - 0xa0,0xc0, - address,0, - hdw->fw_buffer+address, - 0x800,HZ); - if (ret < 0) break; - } - - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Done grabbing CPU firmware"); - } else { - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Sucking down EEPROM contents"); - hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw); - if (!hdw->fw_buffer) { - pvr2_trace(PVR2_TRACE_FIRMWARE, - "EEPROM content suck failed."); - break; - } - hdw->fw_size = EEPROM_SIZE; - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Done sucking down EEPROM contents"); - } - - } while (0); LOCK_GIVE(hdw->big_lock); -} - - -/* Return true if we're in a mode for retrieval CPU firmware */ -int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw) -{ - return hdw->fw_buffer != NULL; -} - - -int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs, - char *buf,unsigned int cnt) -{ - int ret = -EINVAL; - LOCK_TAKE(hdw->big_lock); do { - if (!buf) break; - if (!cnt) break; - - if (!hdw->fw_buffer) { - ret = -EIO; - break; - } - - if (offs >= hdw->fw_size) { - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Read firmware data offs=%d EOF", - offs); - ret = 0; - break; - } - - if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs; - - memcpy(buf,hdw->fw_buffer+offs,cnt); - - pvr2_trace(PVR2_TRACE_FIRMWARE, - "Read firmware data offs=%d cnt=%d", - offs,cnt); - ret = cnt; - } while (0); LOCK_GIVE(hdw->big_lock); - - return ret; -} - - -int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw, - enum pvr2_v4l_type index) -{ - switch (index) { - case pvr2_v4l_type_video: return hdw->v4l_minor_number_video; - case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi; - case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio; - default: return -1; - } -} - - -/* Store a v4l minor device number */ -void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw, - enum pvr2_v4l_type index,int v) -{ - switch (index) { - case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v; - case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v; - case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v; - default: break; - } -} - - -static void pvr2_ctl_write_complete(struct urb *urb) -{ - struct pvr2_hdw *hdw = urb->context; - hdw->ctl_write_pend_flag = 0; - if (hdw->ctl_read_pend_flag) return; - complete(&hdw->ctl_done); -} - - -static void pvr2_ctl_read_complete(struct urb *urb) -{ - struct pvr2_hdw *hdw = urb->context; - hdw->ctl_read_pend_flag = 0; - if (hdw->ctl_write_pend_flag) return; - complete(&hdw->ctl_done); -} - - -static void pvr2_ctl_timeout(unsigned long data) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; - if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) { - hdw->ctl_timeout_flag = !0; - if (hdw->ctl_write_pend_flag) - usb_unlink_urb(hdw->ctl_write_urb); - if (hdw->ctl_read_pend_flag) - usb_unlink_urb(hdw->ctl_read_urb); - } -} - - -/* Issue a command and get a response from the device. This extended - version includes a probe flag (which if set means that device errors - should not be logged or treated as fatal) and a timeout in jiffies. - This can be used to non-lethally probe the health of endpoint 1. */ -static int pvr2_send_request_ex(struct pvr2_hdw *hdw, - unsigned int timeout,int probe_fl, - void *write_data,unsigned int write_len, - void *read_data,unsigned int read_len) -{ - unsigned int idx; - int status = 0; - struct timer_list timer; - if (!hdw->ctl_lock_held) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Attempted to execute control transfer" - " without lock!!"); - return -EDEADLK; - } - if (!hdw->flag_ok && !probe_fl) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Attempted to execute control transfer" - " when device not ok"); - return -EIO; - } - if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) { - if (!probe_fl) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Attempted to execute control transfer" - " when USB is disconnected"); - } - return -ENOTTY; - } - - /* Ensure that we have sane parameters */ - if (!write_data) write_len = 0; - if (!read_data) read_len = 0; - if (write_len > PVR2_CTL_BUFFSIZE) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Attempted to execute %d byte" - " control-write transfer (limit=%d)", - write_len,PVR2_CTL_BUFFSIZE); - return -EINVAL; - } - if (read_len > PVR2_CTL_BUFFSIZE) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Attempted to execute %d byte" - " control-read transfer (limit=%d)", - write_len,PVR2_CTL_BUFFSIZE); - return -EINVAL; - } - if ((!write_len) && (!read_len)) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Attempted to execute null control transfer?"); - return -EINVAL; - } - - - hdw->cmd_debug_state = 1; - if (write_len) { - hdw->cmd_debug_code = ((unsigned char *)write_data)[0]; - } else { - hdw->cmd_debug_code = 0; - } - hdw->cmd_debug_write_len = write_len; - hdw->cmd_debug_read_len = read_len; - - /* Initialize common stuff */ - init_completion(&hdw->ctl_done); - hdw->ctl_timeout_flag = 0; - hdw->ctl_write_pend_flag = 0; - hdw->ctl_read_pend_flag = 0; - init_timer(&timer); - timer.expires = jiffies + timeout; - timer.data = (unsigned long)hdw; - timer.function = pvr2_ctl_timeout; - - if (write_len) { - hdw->cmd_debug_state = 2; - /* Transfer write data to internal buffer */ - for (idx = 0; idx < write_len; idx++) { - hdw->ctl_write_buffer[idx] = - ((unsigned char *)write_data)[idx]; - } - /* Initiate a write request */ - usb_fill_bulk_urb(hdw->ctl_write_urb, - hdw->usb_dev, - usb_sndbulkpipe(hdw->usb_dev, - PVR2_CTL_WRITE_ENDPOINT), - hdw->ctl_write_buffer, - write_len, - pvr2_ctl_write_complete, - hdw); - hdw->ctl_write_urb->actual_length = 0; - hdw->ctl_write_pend_flag = !0; - status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL); - if (status < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to submit write-control" - " URB status=%d",status); - hdw->ctl_write_pend_flag = 0; - goto done; - } - } - - if (read_len) { - hdw->cmd_debug_state = 3; - memset(hdw->ctl_read_buffer,0x43,read_len); - /* Initiate a read request */ - usb_fill_bulk_urb(hdw->ctl_read_urb, - hdw->usb_dev, - usb_rcvbulkpipe(hdw->usb_dev, - PVR2_CTL_READ_ENDPOINT), - hdw->ctl_read_buffer, - read_len, - pvr2_ctl_read_complete, - hdw); - hdw->ctl_read_urb->actual_length = 0; - hdw->ctl_read_pend_flag = !0; - status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL); - if (status < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to submit read-control" - " URB status=%d",status); - hdw->ctl_read_pend_flag = 0; - goto done; - } - } - - /* Start timer */ - add_timer(&timer); - - /* Now wait for all I/O to complete */ - hdw->cmd_debug_state = 4; - while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) { - wait_for_completion(&hdw->ctl_done); - } - hdw->cmd_debug_state = 5; - - /* Stop timer */ - del_timer_sync(&timer); - - hdw->cmd_debug_state = 6; - status = 0; - - if (hdw->ctl_timeout_flag) { - status = -ETIMEDOUT; - if (!probe_fl) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Timed out control-write"); - } - goto done; - } - - if (write_len) { - /* Validate results of write request */ - if ((hdw->ctl_write_urb->status != 0) && - (hdw->ctl_write_urb->status != -ENOENT) && - (hdw->ctl_write_urb->status != -ESHUTDOWN) && - (hdw->ctl_write_urb->status != -ECONNRESET)) { - /* USB subsystem is reporting some kind of failure - on the write */ - status = hdw->ctl_write_urb->status; - if (!probe_fl) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "control-write URB failure," - " status=%d", - status); - } - goto done; - } - if (hdw->ctl_write_urb->actual_length < write_len) { - /* Failed to write enough data */ - status = -EIO; - if (!probe_fl) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "control-write URB short," - " expected=%d got=%d", - write_len, - hdw->ctl_write_urb->actual_length); - } - goto done; - } - } - if (read_len) { - /* Validate results of read request */ - if ((hdw->ctl_read_urb->status != 0) && - (hdw->ctl_read_urb->status != -ENOENT) && - (hdw->ctl_read_urb->status != -ESHUTDOWN) && - (hdw->ctl_read_urb->status != -ECONNRESET)) { - /* USB subsystem is reporting some kind of failure - on the read */ - status = hdw->ctl_read_urb->status; - if (!probe_fl) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "control-read URB failure," - " status=%d", - status); - } - goto done; - } - if (hdw->ctl_read_urb->actual_length < read_len) { - /* Failed to read enough data */ - status = -EIO; - if (!probe_fl) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "control-read URB short," - " expected=%d got=%d", - read_len, - hdw->ctl_read_urb->actual_length); - } - goto done; - } - /* Transfer retrieved data out from internal buffer */ - for (idx = 0; idx < read_len; idx++) { - ((unsigned char *)read_data)[idx] = - hdw->ctl_read_buffer[idx]; - } - } - - done: - - hdw->cmd_debug_state = 0; - if ((status < 0) && (!probe_fl)) { - pvr2_hdw_render_useless(hdw); - } - return status; -} - - -int pvr2_send_request(struct pvr2_hdw *hdw, - void *write_data,unsigned int write_len, - void *read_data,unsigned int read_len) -{ - return pvr2_send_request_ex(hdw,HZ*4,0, - write_data,write_len, - read_data,read_len); -} - - -static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode) -{ - int ret; - unsigned int cnt = 1; - unsigned int args = 0; - LOCK_TAKE(hdw->ctl_lock); - hdw->cmd_buffer[0] = cmdcode & 0xffu; - args = (cmdcode >> 8) & 0xffu; - args = (args > 2) ? 2 : args; - if (args) { - cnt += args; - hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu; - if (args > 1) { - hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu; - } - } - if (pvrusb2_debug & PVR2_TRACE_INIT) { - unsigned int idx; - unsigned int ccnt,bcnt; - char tbuf[50]; - cmdcode &= 0xffu; - bcnt = 0; - ccnt = scnprintf(tbuf+bcnt, - sizeof(tbuf)-bcnt, - "Sending FX2 command 0x%x",cmdcode); - bcnt += ccnt; - for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) { - if (pvr2_fx2cmd_desc[idx].id == cmdcode) { - ccnt = scnprintf(tbuf+bcnt, - sizeof(tbuf)-bcnt, - " \"%s\"", - pvr2_fx2cmd_desc[idx].desc); - bcnt += ccnt; - break; - } - } - if (args) { - ccnt = scnprintf(tbuf+bcnt, - sizeof(tbuf)-bcnt, - " (%u",hdw->cmd_buffer[1]); - bcnt += ccnt; - if (args > 1) { - ccnt = scnprintf(tbuf+bcnt, - sizeof(tbuf)-bcnt, - ",%u",hdw->cmd_buffer[2]); - bcnt += ccnt; - } - ccnt = scnprintf(tbuf+bcnt, - sizeof(tbuf)-bcnt, - ")"); - bcnt += ccnt; - } - pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf); - } - ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0); - LOCK_GIVE(hdw->ctl_lock); - return ret; -} - - -int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) -{ - int ret; - - LOCK_TAKE(hdw->ctl_lock); - - hdw->cmd_buffer[0] = FX2CMD_REG_WRITE; /* write register prefix */ - PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data); - hdw->cmd_buffer[5] = 0; - hdw->cmd_buffer[6] = (reg >> 8) & 0xff; - hdw->cmd_buffer[7] = reg & 0xff; - - - ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0); - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - - -static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data) -{ - int ret = 0; - - LOCK_TAKE(hdw->ctl_lock); - - hdw->cmd_buffer[0] = FX2CMD_REG_READ; /* read register prefix */ - hdw->cmd_buffer[1] = 0; - hdw->cmd_buffer[2] = 0; - hdw->cmd_buffer[3] = 0; - hdw->cmd_buffer[4] = 0; - hdw->cmd_buffer[5] = 0; - hdw->cmd_buffer[6] = (reg >> 8) & 0xff; - hdw->cmd_buffer[7] = reg & 0xff; - - ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4); - *data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0); - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - - -void pvr2_hdw_render_useless(struct pvr2_hdw *hdw) -{ - if (!hdw->flag_ok) return; - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Device being rendered inoperable"); - if (hdw->vid_stream) { - pvr2_stream_setup(hdw->vid_stream,NULL,0,0); - } - hdw->flag_ok = 0; - trace_stbit("flag_ok",hdw->flag_ok); - pvr2_hdw_state_sched(hdw); -} - - -void pvr2_hdw_device_reset(struct pvr2_hdw *hdw) -{ - int ret; - pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset..."); - ret = usb_lock_device_for_reset(hdw->usb_dev,NULL); - if (ret == 0) { - ret = usb_reset_device(hdw->usb_dev); - usb_unlock_device(hdw->usb_dev); - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to lock USB device ret=%d",ret); - } - if (init_pause_msec) { - pvr2_trace(PVR2_TRACE_INFO, - "Waiting %u msec for hardware to settle", - init_pause_msec); - msleep(init_pause_msec); - } - -} - - -void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val) -{ - char *da; - unsigned int pipe; - int ret; - - if (!hdw->usb_dev) return; - - da = kmalloc(16, GFP_KERNEL); - - if (da == NULL) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Unable to allocate memory to control CPU reset"); - return; - } - - pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val); - - da[0] = val ? 0x01 : 0x00; - - /* Write the CPUCS register on the 8051. The lsb of the register - is the reset bit; a 1 asserts reset while a 0 clears it. */ - pipe = usb_sndctrlpipe(hdw->usb_dev, 0); - ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,HZ); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "cpureset_assert(%d) error=%d",val,ret); - pvr2_hdw_render_useless(hdw); - } - - kfree(da); -} - - -int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) -{ - return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET); -} - - -int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) -{ - return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON); -} - - -int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) -{ - return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF); -} - - -int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) -{ - pvr2_trace(PVR2_TRACE_INIT, - "Requesting decoder reset"); - if (hdw->decoder_client_id) { - v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, - core, reset, 0); - pvr2_hdw_cx25840_vbi_hack(hdw); - return 0; - } - pvr2_trace(PVR2_TRACE_INIT, - "Unable to reset decoder: nothing attached"); - return -ENOTTY; -} - - -static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) -{ - hdw->flag_ok = !0; - return pvr2_issue_simple_cmd(hdw, - FX2CMD_HCW_DEMOD_RESETIN | - (1 << 8) | - ((onoff ? 1 : 0) << 16)); -} - - -static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) -{ - hdw->flag_ok = !0; - return pvr2_issue_simple_cmd(hdw,(onoff ? - FX2CMD_ONAIR_DTV_POWER_ON : - FX2CMD_ONAIR_DTV_POWER_OFF)); -} - - -static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, - int onoff) -{ - return pvr2_issue_simple_cmd(hdw,(onoff ? - FX2CMD_ONAIR_DTV_STREAMING_ON : - FX2CMD_ONAIR_DTV_STREAMING_OFF)); -} - - -static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) -{ - int cmode; - /* Compare digital/analog desired setting with current setting. If - they don't match, fix it... */ - cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG); - if (cmode == hdw->pathway_state) { - /* They match; nothing to do */ - return; - } - - switch (hdw->hdw_desc->digital_control_scheme) { - case PVR2_DIGITAL_SCHEME_HAUPPAUGE: - pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl); - if (cmode == PVR2_PATHWAY_ANALOG) { - /* If moving to analog mode, also force the decoder - to reset. If no decoder is attached, then it's - ok to ignore this because if/when the decoder - attaches, it will reset itself at that time. */ - pvr2_hdw_cmd_decoder_reset(hdw); - } - break; - case PVR2_DIGITAL_SCHEME_ONAIR: - /* Supposedly we should always have the power on whether in - digital or analog mode. But for now do what appears to - work... */ - pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl); - break; - default: break; - } - - pvr2_hdw_untrip_unlocked(hdw); - hdw->pathway_state = cmode; -} - - -static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) -{ - /* change some GPIO data - * - * note: bit d7 of dir appears to control the LED, - * so we shut it off here. - * - */ - if (onoff) { - pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481); - } else { - pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401); - } - pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000); -} - - -typedef void (*led_method_func)(struct pvr2_hdw *,int); - -static led_method_func led_methods[] = { - [PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge, -}; - - -/* Toggle LED */ -static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) -{ - unsigned int scheme_id; - led_method_func fp; - - if ((!onoff) == (!hdw->led_on)) return; - - hdw->led_on = onoff != 0; - - scheme_id = hdw->hdw_desc->led_scheme; - if (scheme_id < ARRAY_SIZE(led_methods)) { - fp = led_methods[scheme_id]; - } else { - fp = NULL; - } - - if (fp) (*fp)(hdw,onoff); -} - - -/* Stop / start video stream transport */ -static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) -{ - int ret; - - /* If we're in analog mode, then just issue the usual analog - command. */ - if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { - return pvr2_issue_simple_cmd(hdw, - (runFl ? - FX2CMD_STREAMING_ON : - FX2CMD_STREAMING_OFF)); - /*Note: Not reached */ - } - - if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) { - /* Whoops, we don't know what mode we're in... */ - return -EINVAL; - } - - /* To get here we have to be in digital mode. The mechanism here - is unfortunately different for different vendors. So we switch - on the device's digital scheme attribute in order to figure out - what to do. */ - switch (hdw->hdw_desc->digital_control_scheme) { - case PVR2_DIGITAL_SCHEME_HAUPPAUGE: - return pvr2_issue_simple_cmd(hdw, - (runFl ? - FX2CMD_HCW_DTV_STREAMING_ON : - FX2CMD_HCW_DTV_STREAMING_OFF)); - case PVR2_DIGITAL_SCHEME_ONAIR: - ret = pvr2_issue_simple_cmd(hdw, - (runFl ? - FX2CMD_STREAMING_ON : - FX2CMD_STREAMING_OFF)); - if (ret) return ret; - return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl); - default: - return -EINVAL; - } -} - - -/* Evaluate whether or not state_pathway_ok can change */ -static int state_eval_pathway_ok(struct pvr2_hdw *hdw) -{ - if (hdw->state_pathway_ok) { - /* Nothing to do if pathway is already ok */ - return 0; - } - if (!hdw->state_pipeline_idle) { - /* Not allowed to change anything if pipeline is not idle */ - return 0; - } - pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV); - hdw->state_pathway_ok = !0; - trace_stbit("state_pathway_ok",hdw->state_pathway_ok); - return !0; -} - - -/* Evaluate whether or not state_encoder_ok can change */ -static int state_eval_encoder_ok(struct pvr2_hdw *hdw) -{ - if (hdw->state_encoder_ok) return 0; - if (hdw->flag_tripped) return 0; - if (hdw->state_encoder_run) return 0; - if (hdw->state_encoder_config) return 0; - if (hdw->state_decoder_run) return 0; - if (hdw->state_usbstream_run) return 0; - if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) { - if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0; - } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) { - return 0; - } - - if (pvr2_upload_firmware2(hdw) < 0) { - hdw->flag_tripped = !0; - trace_stbit("flag_tripped",hdw->flag_tripped); - return !0; - } - hdw->state_encoder_ok = !0; - trace_stbit("state_encoder_ok",hdw->state_encoder_ok); - return !0; -} - - -/* Evaluate whether or not state_encoder_config can change */ -static int state_eval_encoder_config(struct pvr2_hdw *hdw) -{ - if (hdw->state_encoder_config) { - if (hdw->state_encoder_ok) { - if (hdw->state_pipeline_req && - !hdw->state_pipeline_pause) return 0; - } - hdw->state_encoder_config = 0; - hdw->state_encoder_waitok = 0; - trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); - /* paranoia - solve race if timer just completed */ - del_timer_sync(&hdw->encoder_wait_timer); - } else { - if (!hdw->state_pathway_ok || - (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || - !hdw->state_encoder_ok || - !hdw->state_pipeline_idle || - hdw->state_pipeline_pause || - !hdw->state_pipeline_req || - !hdw->state_pipeline_config) { - /* We must reset the enforced wait interval if - anything has happened that might have disturbed - the encoder. This should be a rare case. */ - if (timer_pending(&hdw->encoder_wait_timer)) { - del_timer_sync(&hdw->encoder_wait_timer); - } - if (hdw->state_encoder_waitok) { - /* Must clear the state - therefore we did - something to a state bit and must also - return true. */ - hdw->state_encoder_waitok = 0; - trace_stbit("state_encoder_waitok", - hdw->state_encoder_waitok); - return !0; - } - return 0; - } - if (!hdw->state_encoder_waitok) { - if (!timer_pending(&hdw->encoder_wait_timer)) { - /* waitok flag wasn't set and timer isn't - running. Check flag once more to avoid - a race then start the timer. This is - the point when we measure out a minimal - quiet interval before doing something to - the encoder. */ - if (!hdw->state_encoder_waitok) { - hdw->encoder_wait_timer.expires = - jiffies + - (HZ * TIME_MSEC_ENCODER_WAIT - / 1000); - add_timer(&hdw->encoder_wait_timer); - } - } - /* We can't continue until we know we have been - quiet for the interval measured by this - timer. */ - return 0; - } - pvr2_encoder_configure(hdw); - if (hdw->state_encoder_ok) hdw->state_encoder_config = !0; - } - trace_stbit("state_encoder_config",hdw->state_encoder_config); - return !0; -} - - -/* Return true if the encoder should not be running. */ -static int state_check_disable_encoder_run(struct pvr2_hdw *hdw) -{ - if (!hdw->state_encoder_ok) { - /* Encoder isn't healthy at the moment, so stop it. */ - return !0; - } - if (!hdw->state_pathway_ok) { - /* Mode is not understood at the moment (i.e. it wants to - change), so encoder must be stopped. */ - return !0; - } - - switch (hdw->pathway_state) { - case PVR2_PATHWAY_ANALOG: - if (!hdw->state_decoder_run) { - /* We're in analog mode and the decoder is not - running; thus the encoder should be stopped as - well. */ - return !0; - } - break; - case PVR2_PATHWAY_DIGITAL: - if (hdw->state_encoder_runok) { - /* This is a funny case. We're in digital mode so - really the encoder should be stopped. However - if it really is running, only kill it after - runok has been set. This gives a chance for the - onair quirk to function (encoder must run - briefly first, at least once, before onair - digital streaming can work). */ - return !0; - } - break; - default: - /* Unknown mode; so encoder should be stopped. */ - return !0; - } - - /* If we get here, we haven't found a reason to stop the - encoder. */ - return 0; -} - - -/* Return true if the encoder should be running. */ -static int state_check_enable_encoder_run(struct pvr2_hdw *hdw) -{ - if (!hdw->state_encoder_ok) { - /* Don't run the encoder if it isn't healthy... */ - return 0; - } - if (!hdw->state_pathway_ok) { - /* Don't run the encoder if we don't (yet) know what mode - we need to be in... */ - return 0; - } - - switch (hdw->pathway_state) { - case PVR2_PATHWAY_ANALOG: - if (hdw->state_decoder_run && hdw->state_decoder_ready) { - /* In analog mode, if the decoder is running, then - run the encoder. */ - return !0; - } - break; - case PVR2_PATHWAY_DIGITAL: - if ((hdw->hdw_desc->digital_control_scheme == - PVR2_DIGITAL_SCHEME_ONAIR) && - !hdw->state_encoder_runok) { - /* This is a quirk. OnAir hardware won't stream - digital until the encoder has been run at least - once, for a minimal period of time (empiricially - measured to be 1/4 second). So if we're on - OnAir hardware and the encoder has never been - run at all, then start the encoder. Normal - state machine logic in the driver will - automatically handle the remaining bits. */ - return !0; - } - break; - default: - /* For completeness (unknown mode; encoder won't run ever) */ - break; - } - /* If we get here, then we haven't found any reason to run the - encoder, so don't run it. */ - return 0; -} - - -/* Evaluate whether or not state_encoder_run can change */ -static int state_eval_encoder_run(struct pvr2_hdw *hdw) -{ - if (hdw->state_encoder_run) { - if (!state_check_disable_encoder_run(hdw)) return 0; - if (hdw->state_encoder_ok) { - del_timer_sync(&hdw->encoder_run_timer); - if (pvr2_encoder_stop(hdw) < 0) return !0; - } - hdw->state_encoder_run = 0; - } else { - if (!state_check_enable_encoder_run(hdw)) return 0; - if (pvr2_encoder_start(hdw) < 0) return !0; - hdw->state_encoder_run = !0; - if (!hdw->state_encoder_runok) { - hdw->encoder_run_timer.expires = - jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000); - add_timer(&hdw->encoder_run_timer); - } - } - trace_stbit("state_encoder_run",hdw->state_encoder_run); - return !0; -} - - -/* Timeout function for quiescent timer. */ -static void pvr2_hdw_quiescent_timeout(unsigned long data) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; - hdw->state_decoder_quiescent = !0; - trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); - hdw->state_stale = !0; - queue_work(hdw->workqueue,&hdw->workpoll); -} - - -/* Timeout function for decoder stabilization timer. */ -static void pvr2_hdw_decoder_stabilization_timeout(unsigned long data) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; - hdw->state_decoder_ready = !0; - trace_stbit("state_decoder_ready", hdw->state_decoder_ready); - hdw->state_stale = !0; - queue_work(hdw->workqueue, &hdw->workpoll); -} - - -/* Timeout function for encoder wait timer. */ -static void pvr2_hdw_encoder_wait_timeout(unsigned long data) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; - hdw->state_encoder_waitok = !0; - trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); - hdw->state_stale = !0; - queue_work(hdw->workqueue,&hdw->workpoll); -} - - -/* Timeout function for encoder run timer. */ -static void pvr2_hdw_encoder_run_timeout(unsigned long data) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; - if (!hdw->state_encoder_runok) { - hdw->state_encoder_runok = !0; - trace_stbit("state_encoder_runok",hdw->state_encoder_runok); - hdw->state_stale = !0; - queue_work(hdw->workqueue,&hdw->workpoll); - } -} - - -/* Evaluate whether or not state_decoder_run can change */ -static int state_eval_decoder_run(struct pvr2_hdw *hdw) -{ - if (hdw->state_decoder_run) { - if (hdw->state_encoder_ok) { - if (hdw->state_pipeline_req && - !hdw->state_pipeline_pause && - hdw->state_pathway_ok) return 0; - } - if (!hdw->flag_decoder_missed) { - pvr2_decoder_enable(hdw,0); - } - hdw->state_decoder_quiescent = 0; - hdw->state_decoder_run = 0; - /* paranoia - solve race if timer(s) just completed */ - del_timer_sync(&hdw->quiescent_timer); - /* Kill the stabilization timer, in case we're killing the - encoder before the previous stabilization interval has - been properly timed. */ - del_timer_sync(&hdw->decoder_stabilization_timer); - hdw->state_decoder_ready = 0; - } else { - if (!hdw->state_decoder_quiescent) { - if (!timer_pending(&hdw->quiescent_timer)) { - /* We don't do something about the - quiescent timer until right here because - we also want to catch cases where the - decoder was already not running (like - after initialization) as opposed to - knowing that we had just stopped it. - The second flag check is here to cover a - race - the timer could have run and set - this flag just after the previous check - but before we did the pending check. */ - if (!hdw->state_decoder_quiescent) { - hdw->quiescent_timer.expires = - jiffies + - (HZ * TIME_MSEC_DECODER_WAIT - / 1000); - add_timer(&hdw->quiescent_timer); - } - } - /* Don't allow decoder to start again until it has - been quiesced first. This little detail should - hopefully further stabilize the encoder. */ - return 0; - } - if (!hdw->state_pathway_ok || - (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || - !hdw->state_pipeline_req || - hdw->state_pipeline_pause || - !hdw->state_pipeline_config || - !hdw->state_encoder_config || - !hdw->state_encoder_ok) return 0; - del_timer_sync(&hdw->quiescent_timer); - if (hdw->flag_decoder_missed) return 0; - if (pvr2_decoder_enable(hdw,!0) < 0) return 0; - hdw->state_decoder_quiescent = 0; - hdw->state_decoder_ready = 0; - hdw->state_decoder_run = !0; - if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) { - hdw->decoder_stabilization_timer.expires = - jiffies + - (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT / - 1000); - add_timer(&hdw->decoder_stabilization_timer); - } else { - hdw->state_decoder_ready = !0; - } - } - trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); - trace_stbit("state_decoder_run",hdw->state_decoder_run); - trace_stbit("state_decoder_ready", hdw->state_decoder_ready); - return !0; -} - - -/* Evaluate whether or not state_usbstream_run can change */ -static int state_eval_usbstream_run(struct pvr2_hdw *hdw) -{ - if (hdw->state_usbstream_run) { - int fl = !0; - if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { - fl = (hdw->state_encoder_ok && - hdw->state_encoder_run); - } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && - (hdw->hdw_desc->flag_digital_requires_cx23416)) { - fl = hdw->state_encoder_ok; - } - if (fl && - hdw->state_pipeline_req && - !hdw->state_pipeline_pause && - hdw->state_pathway_ok) { - return 0; - } - pvr2_hdw_cmd_usbstream(hdw,0); - hdw->state_usbstream_run = 0; - } else { - if (!hdw->state_pipeline_req || - hdw->state_pipeline_pause || - !hdw->state_pathway_ok) return 0; - if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { - if (!hdw->state_encoder_ok || - !hdw->state_encoder_run) return 0; - } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && - (hdw->hdw_desc->flag_digital_requires_cx23416)) { - if (!hdw->state_encoder_ok) return 0; - if (hdw->state_encoder_run) return 0; - if (hdw->hdw_desc->digital_control_scheme == - PVR2_DIGITAL_SCHEME_ONAIR) { - /* OnAir digital receivers won't stream - unless the analog encoder has run first. - Why? I have no idea. But don't even - try until we know the analog side is - known to have run. */ - if (!hdw->state_encoder_runok) return 0; - } - } - if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; - hdw->state_usbstream_run = !0; - } - trace_stbit("state_usbstream_run",hdw->state_usbstream_run); - return !0; -} - - -/* Attempt to configure pipeline, if needed */ -static int state_eval_pipeline_config(struct pvr2_hdw *hdw) -{ - if (hdw->state_pipeline_config || - hdw->state_pipeline_pause) return 0; - pvr2_hdw_commit_execute(hdw); - return !0; -} - - -/* Update pipeline idle and pipeline pause tracking states based on other - inputs. This must be called whenever the other relevant inputs have - changed. */ -static int state_update_pipeline_state(struct pvr2_hdw *hdw) -{ - unsigned int st; - int updatedFl = 0; - /* Update pipeline state */ - st = !(hdw->state_encoder_run || - hdw->state_decoder_run || - hdw->state_usbstream_run || - (!hdw->state_decoder_quiescent)); - if (!st != !hdw->state_pipeline_idle) { - hdw->state_pipeline_idle = st; - updatedFl = !0; - } - if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) { - hdw->state_pipeline_pause = 0; - updatedFl = !0; - } - return updatedFl; -} - - -typedef int (*state_eval_func)(struct pvr2_hdw *); - -/* Set of functions to be run to evaluate various states in the driver. */ -static const state_eval_func eval_funcs[] = { - state_eval_pathway_ok, - state_eval_pipeline_config, - state_eval_encoder_ok, - state_eval_encoder_config, - state_eval_decoder_run, - state_eval_encoder_run, - state_eval_usbstream_run, -}; - - -/* Process various states and return true if we did anything interesting. */ -static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) -{ - unsigned int i; - int state_updated = 0; - int check_flag; - - if (!hdw->state_stale) return 0; - if ((hdw->fw1_state != FW1_STATE_OK) || - !hdw->flag_ok) { - hdw->state_stale = 0; - return !0; - } - /* This loop is the heart of the entire driver. It keeps trying to - evaluate various bits of driver state until nothing changes for - one full iteration. Each "bit of state" tracks some global - aspect of the driver, e.g. whether decoder should run, if - pipeline is configured, usb streaming is on, etc. We separately - evaluate each of those questions based on other driver state to - arrive at the correct running configuration. */ - do { - check_flag = 0; - state_update_pipeline_state(hdw); - /* Iterate over each bit of state */ - for (i = 0; (iflag_ok; i++) { - if ((*eval_funcs[i])(hdw)) { - check_flag = !0; - state_updated = !0; - state_update_pipeline_state(hdw); - } - } - } while (check_flag && hdw->flag_ok); - hdw->state_stale = 0; - trace_stbit("state_stale",hdw->state_stale); - return state_updated; -} - - -static unsigned int print_input_mask(unsigned int msk, - char *buf,unsigned int acnt) -{ - unsigned int idx,ccnt; - unsigned int tcnt = 0; - for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) { - if (!((1 << idx) & msk)) continue; - ccnt = scnprintf(buf+tcnt, - acnt-tcnt, - "%s%s", - (tcnt ? ", " : ""), - control_values_input[idx]); - tcnt += ccnt; - } - return tcnt; -} - - -static const char *pvr2_pathway_state_name(int id) -{ - switch (id) { - case PVR2_PATHWAY_ANALOG: return "analog"; - case PVR2_PATHWAY_DIGITAL: return "digital"; - default: return "unknown"; - } -} - - -static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, - char *buf,unsigned int acnt) -{ - switch (which) { - case 0: - return scnprintf( - buf,acnt, - "driver:%s%s%s%s%s ", - (hdw->flag_ok ? " " : " "), - (hdw->flag_init_ok ? " " : " "), - (hdw->flag_disconnected ? " " : - " "), - (hdw->flag_tripped ? " " : ""), - (hdw->flag_decoder_missed ? " " : ""), - pvr2_pathway_state_name(hdw->pathway_state)); - - case 1: - return scnprintf( - buf,acnt, - "pipeline:%s%s%s%s", - (hdw->state_pipeline_idle ? " " : ""), - (hdw->state_pipeline_config ? - " " : " "), - (hdw->state_pipeline_req ? " " : ""), - (hdw->state_pipeline_pause ? " " : "")); - case 2: - return scnprintf( - buf,acnt, - "worker:%s%s%s%s%s%s%s", - (hdw->state_decoder_run ? - (hdw->state_decoder_ready ? - "" : " ") : - (hdw->state_decoder_quiescent ? - "" : " ")), - (hdw->state_decoder_quiescent ? - " " : ""), - (hdw->state_encoder_ok ? - "" : " "), - (hdw->state_encoder_run ? - (hdw->state_encoder_runok ? - " " : - " ") : - (hdw->state_encoder_runok ? - " " : - " ")), - (hdw->state_encoder_config ? - " " : - (hdw->state_encoder_waitok ? - "" : " ")), - (hdw->state_usbstream_run ? - " " : " "), - (hdw->state_pathway_ok ? - " " : "")); - case 3: - return scnprintf( - buf,acnt, - "state: %s", - pvr2_get_state_name(hdw->master_state)); - case 4: { - unsigned int tcnt = 0; - unsigned int ccnt; - - ccnt = scnprintf(buf, - acnt, - "Hardware supported inputs: "); - tcnt += ccnt; - tcnt += print_input_mask(hdw->input_avail_mask, - buf+tcnt, - acnt-tcnt); - if (hdw->input_avail_mask != hdw->input_allowed_mask) { - ccnt = scnprintf(buf+tcnt, - acnt-tcnt, - "; allowed inputs: "); - tcnt += ccnt; - tcnt += print_input_mask(hdw->input_allowed_mask, - buf+tcnt, - acnt-tcnt); - } - return tcnt; - } - case 5: { - struct pvr2_stream_stats stats; - if (!hdw->vid_stream) break; - pvr2_stream_get_stats(hdw->vid_stream, - &stats, - 0); - return scnprintf( - buf,acnt, - "Bytes streamed=%u" - " URBs: queued=%u idle=%u ready=%u" - " processed=%u failed=%u", - stats.bytes_processed, - stats.buffers_in_queue, - stats.buffers_in_idle, - stats.buffers_in_ready, - stats.buffers_processed, - stats.buffers_failed); - } - case 6: { - unsigned int id = hdw->ir_scheme_active; - return scnprintf(buf, acnt, "ir scheme: id=%d %s", id, - (id >= ARRAY_SIZE(ir_scheme_names) ? - "?" : ir_scheme_names[id])); - } - default: break; - } - return 0; -} - - -/* Generate report containing info about attached sub-devices and attached - i2c clients, including an indication of which attached i2c clients are - actually sub-devices. */ -static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw, - char *buf, unsigned int acnt) -{ - struct v4l2_subdev *sd; - unsigned int tcnt = 0; - unsigned int ccnt; - struct i2c_client *client; - const char *p; - unsigned int id; - - ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n"); - tcnt += ccnt; - v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { - id = sd->grp_id; - p = NULL; - if (id < ARRAY_SIZE(module_names)) p = module_names[id]; - if (p) { - ccnt = scnprintf(buf + tcnt, acnt - tcnt, " %s:", p); - tcnt += ccnt; - } else { - ccnt = scnprintf(buf + tcnt, acnt - tcnt, - " (unknown id=%u):", id); - tcnt += ccnt; - } - client = v4l2_get_subdevdata(sd); - if (client) { - ccnt = scnprintf(buf + tcnt, acnt - tcnt, - " %s @ %02x\n", client->name, - client->addr); - tcnt += ccnt; - } else { - ccnt = scnprintf(buf + tcnt, acnt - tcnt, - " no i2c client\n"); - tcnt += ccnt; - } - } - return tcnt; -} - - -unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, - char *buf,unsigned int acnt) -{ - unsigned int bcnt,ccnt,idx; - bcnt = 0; - LOCK_TAKE(hdw->big_lock); - for (idx = 0; ; idx++) { - ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt); - if (!ccnt) break; - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - if (!acnt) break; - buf[0] = '\n'; ccnt = 1; - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - } - ccnt = pvr2_hdw_report_clients(hdw, buf, acnt); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - LOCK_GIVE(hdw->big_lock); - return bcnt; -} - - -static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw) -{ - char buf[256]; - unsigned int idx, ccnt; - unsigned int lcnt, ucnt; - - for (idx = 0; ; idx++) { - ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf)); - if (!ccnt) break; - printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf); - } - ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf)); - ucnt = 0; - while (ucnt < ccnt) { - lcnt = 0; - while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) { - lcnt++; - } - printk(KERN_INFO "%s %.*s\n", hdw->name, lcnt, buf + ucnt); - ucnt += lcnt + 1; - } -} - - -/* Evaluate and update the driver's current state, taking various actions - as appropriate for the update. */ -static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) -{ - unsigned int st; - int state_updated = 0; - int callback_flag = 0; - int analog_mode; - - pvr2_trace(PVR2_TRACE_STBITS, - "Drive state check START"); - if (pvrusb2_debug & PVR2_TRACE_STBITS) { - pvr2_hdw_state_log_state(hdw); - } - - /* Process all state and get back over disposition */ - state_updated = pvr2_hdw_state_update(hdw); - - analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL); - - /* Update master state based upon all other states. */ - if (!hdw->flag_ok) { - st = PVR2_STATE_DEAD; - } else if (hdw->fw1_state != FW1_STATE_OK) { - st = PVR2_STATE_COLD; - } else if ((analog_mode || - hdw->hdw_desc->flag_digital_requires_cx23416) && - !hdw->state_encoder_ok) { - st = PVR2_STATE_WARM; - } else if (hdw->flag_tripped || - (analog_mode && hdw->flag_decoder_missed)) { - st = PVR2_STATE_ERROR; - } else if (hdw->state_usbstream_run && - (!analog_mode || - (hdw->state_encoder_run && hdw->state_decoder_run))) { - st = PVR2_STATE_RUN; - } else { - st = PVR2_STATE_READY; - } - if (hdw->master_state != st) { - pvr2_trace(PVR2_TRACE_STATE, - "Device state change from %s to %s", - pvr2_get_state_name(hdw->master_state), - pvr2_get_state_name(st)); - pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN); - hdw->master_state = st; - state_updated = !0; - callback_flag = !0; - } - if (state_updated) { - /* Trigger anyone waiting on any state changes here. */ - wake_up(&hdw->state_wait_data); - } - - if (pvrusb2_debug & PVR2_TRACE_STBITS) { - pvr2_hdw_state_log_state(hdw); - } - pvr2_trace(PVR2_TRACE_STBITS, - "Drive state check DONE callback=%d",callback_flag); - - return callback_flag; -} - - -/* Cause kernel thread to check / update driver state */ -static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) -{ - if (hdw->state_stale) return; - hdw->state_stale = !0; - trace_stbit("state_stale",hdw->state_stale); - queue_work(hdw->workqueue,&hdw->workpoll); -} - - -int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) -{ - return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp); -} - - -int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp) -{ - return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp); -} - - -int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp) -{ - return pvr2_read_register(hdw,PVR2_GPIO_IN,dp); -} - - -int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val) -{ - u32 cval,nval; - int ret; - if (~msk) { - ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval); - if (ret) return ret; - nval = (cval & ~msk) | (val & msk); - pvr2_trace(PVR2_TRACE_GPIO, - "GPIO direction changing 0x%x:0x%x" - " from 0x%x to 0x%x", - msk,val,cval,nval); - } else { - nval = val; - pvr2_trace(PVR2_TRACE_GPIO, - "GPIO direction changing to 0x%x",nval); - } - return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval); -} - - -int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) -{ - u32 cval,nval; - int ret; - if (~msk) { - ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval); - if (ret) return ret; - nval = (cval & ~msk) | (val & msk); - pvr2_trace(PVR2_TRACE_GPIO, - "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x", - msk,val,cval,nval); - } else { - nval = val; - pvr2_trace(PVR2_TRACE_GPIO, - "GPIO output changing to 0x%x",nval); - } - return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval); -} - - -void pvr2_hdw_status_poll(struct pvr2_hdw *hdw) -{ - struct v4l2_tuner *vtp = &hdw->tuner_signal_info; - memset(vtp, 0, sizeof(*vtp)); - vtp->type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - hdw->tuner_signal_stale = 0; - /* Note: There apparently is no replacement for VIDIOC_CROPCAP - using v4l2-subdev - therefore we can't support that AT ALL right - now. (Of course, no sub-drivers seem to implement it either. - But now it's a a chicken and egg problem...) */ - v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp); - pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll" - " type=%u strength=%u audio=0x%x cap=0x%x" - " low=%u hi=%u", - vtp->type, - vtp->signal, vtp->rxsubchans, vtp->capability, - vtp->rangelow, vtp->rangehigh); - - /* We have to do this to avoid getting into constant polling if - there's nobody to answer a poll of cropcap info. */ - hdw->cropcap_stale = 0; -} - - -unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) -{ - return hdw->input_avail_mask; -} - - -unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw) -{ - return hdw->input_allowed_mask; -} - - -static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v) -{ - if (hdw->input_val != v) { - hdw->input_val = v; - hdw->input_dirty = !0; - } - - /* Handle side effects - if we switch to a mode that needs the RF - tuner, then select the right frequency choice as well and mark - it dirty. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - hdw->freqSelector = 0; - hdw->freqDirty = !0; - } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || - (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { - hdw->freqSelector = 1; - hdw->freqDirty = !0; - } - return 0; -} - - -int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw, - unsigned int change_mask, - unsigned int change_val) -{ - int ret = 0; - unsigned int nv,m,idx; - LOCK_TAKE(hdw->big_lock); - do { - nv = hdw->input_allowed_mask & ~change_mask; - nv |= (change_val & change_mask); - nv &= hdw->input_avail_mask; - if (!nv) { - /* No legal modes left; return error instead. */ - ret = -EPERM; - break; - } - hdw->input_allowed_mask = nv; - if ((1 << hdw->input_val) & hdw->input_allowed_mask) { - /* Current mode is still in the allowed mask, so - we're done. */ - break; - } - /* Select and switch to a mode that is still in the allowed - mask */ - if (!hdw->input_allowed_mask) { - /* Nothing legal; give up */ - break; - } - m = hdw->input_allowed_mask; - for (idx = 0; idx < (sizeof(m) << 3); idx++) { - if (!((1 << idx) & m)) continue; - pvr2_hdw_set_input(hdw,idx); - break; - } - } while (0); - LOCK_GIVE(hdw->big_lock); - return ret; -} - - -/* Find I2C address of eeprom */ -static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) -{ - int result; - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR; - result = pvr2_send_request(hdw, - hdw->cmd_buffer,1, - hdw->cmd_buffer,1); - if (result < 0) break; - result = hdw->cmd_buffer[0]; - } while(0); LOCK_GIVE(hdw->ctl_lock); - return result; -} - - -int pvr2_hdw_register_access(struct pvr2_hdw *hdw, - struct v4l2_dbg_match *match, u64 reg_id, - int setFl, u64 *val_ptr) -{ -#ifdef CONFIG_VIDEO_ADV_DEBUG - struct v4l2_dbg_register req; - int stat = 0; - int okFl = 0; - - if (!capable(CAP_SYS_ADMIN)) return -EPERM; - - req.match = *match; - req.reg = reg_id; - if (setFl) req.val = *val_ptr; - /* It would be nice to know if a sub-device answered the request */ - v4l2_device_call_all(&hdw->v4l2_dev, 0, core, g_register, &req); - if (!setFl) *val_ptr = req.val; - if (okFl) { - return stat; - } - return -EINVAL; -#else - return -ENOSYS; -#endif -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h deleted file mode 100644 index 8060fc666eeb..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ /dev/null @@ -1,360 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_HDW_H -#define __PVRUSB2_HDW_H - -#include -#include -#include "pvrusb2-io.h" -#include "pvrusb2-ctrl.h" - - -/* Private internal control ids, look these up with - pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ -#define PVR2_CID_STDCUR 2 -#define PVR2_CID_STDAVAIL 3 -#define PVR2_CID_INPUT 4 -#define PVR2_CID_AUDIOMODE 5 -#define PVR2_CID_FREQUENCY 6 -#define PVR2_CID_HRES 7 -#define PVR2_CID_VRES 8 -#define PVR2_CID_CROPL 9 -#define PVR2_CID_CROPT 10 -#define PVR2_CID_CROPW 11 -#define PVR2_CID_CROPH 12 -#define PVR2_CID_CROPCAPPAN 13 -#define PVR2_CID_CROPCAPPAD 14 -#define PVR2_CID_CROPCAPBL 15 -#define PVR2_CID_CROPCAPBT 16 -#define PVR2_CID_CROPCAPBW 17 -#define PVR2_CID_CROPCAPBH 18 -#define PVR2_CID_STDDETECT 19 - -/* Legal values for the INPUT state variable */ -#define PVR2_CVAL_INPUT_TV 0 -#define PVR2_CVAL_INPUT_DTV 1 -#define PVR2_CVAL_INPUT_COMPOSITE 2 -#define PVR2_CVAL_INPUT_SVIDEO 3 -#define PVR2_CVAL_INPUT_RADIO 4 - -enum pvr2_config { - pvr2_config_empty, /* No configuration */ - pvr2_config_mpeg, /* Encoded / compressed video */ - pvr2_config_vbi, /* Standard vbi info */ - pvr2_config_pcm, /* Audio raw pcm stream */ - pvr2_config_rawvideo, /* Video raw frames */ -}; - -enum pvr2_v4l_type { - pvr2_v4l_type_video, - pvr2_v4l_type_vbi, - pvr2_v4l_type_radio, -}; - -/* Major states that we can be in: - * - * DEAD - Device is in an unusable state and cannot be recovered. This - * can happen if we completely lose the ability to communicate with it - * (but it might still on the bus). In this state there's nothing we can - * do; it must be replugged in order to recover. - * - * COLD - Device is in an unusable state, needs microcontroller firmware. - * - * WARM - We can communicate with the device and the proper - * microcontroller firmware is running, but other device initialization is - * still needed (e.g. encoder firmware). - * - * ERROR - A problem prevents capture operation (e.g. encoder firmware - * missing). - * - * READY - Device is operational, but not streaming. - * - * RUN - Device is streaming. - * - */ -#define PVR2_STATE_NONE 0 -#define PVR2_STATE_DEAD 1 -#define PVR2_STATE_COLD 2 -#define PVR2_STATE_WARM 3 -#define PVR2_STATE_ERROR 4 -#define PVR2_STATE_READY 5 -#define PVR2_STATE_RUN 6 - -/* Translate configuration enum to a string label */ -const char *pvr2_config_get_name(enum pvr2_config); - -struct pvr2_hdw; - -/* Create and return a structure for interacting with the underlying - hardware */ -struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, - const struct usb_device_id *devid); - -/* Perform second stage initialization, passing in a notification callback - for when the master state changes. */ -int pvr2_hdw_initialize(struct pvr2_hdw *, - void (*callback_func)(void *), - void *callback_data); - -/* Destroy hardware interaction structure */ -void pvr2_hdw_destroy(struct pvr2_hdw *); - -/* Return true if in the ready (normal) state */ -int pvr2_hdw_dev_ok(struct pvr2_hdw *); - -/* Return small integer number [1..N] for logical instance number of this - device. This is useful for indexing array-valued module parameters. */ -int pvr2_hdw_get_unit_number(struct pvr2_hdw *); - -/* Get pointer to underlying USB device */ -struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *); - -/* Retrieve serial number of device */ -unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); - -/* Retrieve bus location info of device */ -const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *); - -/* Retrieve per-instance string identifier for this specific device */ -const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *); - -/* Called when hardware has been unplugged */ -void pvr2_hdw_disconnect(struct pvr2_hdw *); - -/* Get the number of defined controls */ -unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *); - -/* Retrieve a control handle given its index (0..count-1) */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int); - -/* Retrieve a control handle given its internal ID (if any) */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int); - -/* Retrieve a control handle given its V4L ID (if any) */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id); - -/* Retrieve a control handle given its immediate predecessor V4L ID (if any) */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *, - unsigned int ctl_id); - -/* Commit all control changes made up to this point */ -int pvr2_hdw_commit_ctl(struct pvr2_hdw *); - -/* Return a bit mask of valid input selections for this device. Mask bits - * will be according to PVR_CVAL_INPUT_xxxx definitions. */ -unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *); - -/* Return a bit mask of allowed input selections for this device. Mask bits - * will be according to PVR_CVAL_INPUT_xxxx definitions. */ -unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *); - -/* Change the set of allowed input selections for this device. Both - change_mask and change_valu are mask bits according to - PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate - which settings are being changed and the change_val parameter indicates - whether corresponding settings are being set or cleared. */ -int pvr2_hdw_set_input_allowed(struct pvr2_hdw *, - unsigned int change_mask, - unsigned int change_val); - -/* Return name for this driver instance */ -const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); - -/* Mark tuner status stale so that it will be re-fetched */ -void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *); - -/* Return information about the tuner */ -int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); - -/* Return information about cropping capabilities */ -int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *); - -/* Query device and see if it thinks it is on a high-speed USB link */ -int pvr2_hdw_is_hsm(struct pvr2_hdw *); - -/* Return a string token representative of the hardware type */ -const char *pvr2_hdw_get_type(struct pvr2_hdw *); - -/* Return a single line description of the hardware type */ -const char *pvr2_hdw_get_desc(struct pvr2_hdw *); - -/* Turn streaming on/off */ -int pvr2_hdw_set_streaming(struct pvr2_hdw *,int); - -/* Find out if streaming is on */ -int pvr2_hdw_get_streaming(struct pvr2_hdw *); - -/* Retrieve driver overall state */ -int pvr2_hdw_get_state(struct pvr2_hdw *); - -/* Configure the type of stream to generate */ -int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); - -/* Get handle to video output stream */ -struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); - -/* Enable / disable retrieval of CPU firmware or prom contents. This must - be enabled before pvr2_hdw_cpufw_get() will function. Note that doing - this may prevent the device from running (and leaving this mode may - imply a device reset). */ -void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, - int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */ - int enable_flag); - -/* Return true if we're in a mode for retrieval CPU firmware */ -int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *); - -/* Retrieve a piece of the CPU's firmware at the given offset. Return - value is the number of bytes retrieved or zero if we're past the end or - an error otherwise (e.g. if firmware retrieval is not enabled). */ -int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs, - char *buf,unsigned int cnt); - -/* Retrieve a previously stored v4l minor device number */ -int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *,enum pvr2_v4l_type index); - -/* Store a v4l minor device number */ -void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, - enum pvr2_v4l_type index,int); - -/* Direct read/write access to chip's registers: - match - specify criteria to identify target chip (this is a v4l dbg struct) - reg_id - register number to access - setFl - true to set the register, false to read it - val_ptr - storage location for source / result. */ -int pvr2_hdw_register_access(struct pvr2_hdw *, - struct v4l2_dbg_match *match, u64 reg_id, - int setFl, u64 *val_ptr); - -/* The following entry points are all lower level things you normally don't - want to worry about. */ - -/* Issue a command and get a response from the device. LOTS of higher - level stuff is built on this. */ -int pvr2_send_request(struct pvr2_hdw *, - void *write_ptr,unsigned int write_len, - void *read_ptr,unsigned int read_len); - -/* Slightly higher level device communication functions. */ -int pvr2_write_register(struct pvr2_hdw *, u16, u32); - -/* Call if for any reason we can't talk to the hardware anymore - this will - cause the driver to stop flailing on the device. */ -void pvr2_hdw_render_useless(struct pvr2_hdw *); - -/* Set / clear 8051's reset bit */ -void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int); - -/* Execute a USB-commanded device reset */ -void pvr2_hdw_device_reset(struct pvr2_hdw *); - -/* Reset worker's error trapping circuit breaker */ -int pvr2_hdw_untrip(struct pvr2_hdw *); - -/* Execute hard reset command (after this point it's likely that the - encoder will have to be reconfigured). This also clears the "useless" - state. */ -int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); - -/* Execute simple reset command */ -int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); - -/* suspend */ -int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *); - -/* Order decoder to reset */ -int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); - -/* Direct manipulation of GPIO bits */ -int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *); -int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *); -int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *); -int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val); -int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val); - -/* This data structure is specifically for the next function... */ -struct pvr2_hdw_debug_info { - int big_lock_held; - int ctl_lock_held; - int flag_disconnected; - int flag_init_ok; - int flag_ok; - int fw1_state; - int flag_decoder_missed; - int flag_tripped; - int state_encoder_ok; - int state_encoder_run; - int state_decoder_run; - int state_decoder_ready; - int state_usbstream_run; - int state_decoder_quiescent; - int state_pipeline_config; - int state_pipeline_req; - int state_pipeline_pause; - int state_pipeline_idle; - int cmd_debug_state; - int cmd_debug_write_len; - int cmd_debug_read_len; - int cmd_debug_write_pend; - int cmd_debug_read_pend; - int cmd_debug_timeout; - int cmd_debug_rstatus; - int cmd_debug_wstatus; - unsigned char cmd_code; -}; - -/* Non-intrusively retrieve internal state info - this is useful for - diagnosing lockups. Note that this operation is completed without any - kind of locking and so it is not atomic and may yield inconsistent - results. This is *purely* a debugging aid. */ -void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, - struct pvr2_hdw_debug_info *); - -/* Intrusively retrieve internal state info - this is useful for - diagnosing overall driver state. This operation synchronizes against - the overall driver mutex - so if there are locking problems this will - likely hang! This is *purely* a debugging aid. */ -void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, - struct pvr2_hdw_debug_info *); - -/* Report out several lines of text that describes driver internal state. - Results are written into the passed-in buffer. */ -unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, - char *buf_ptr,unsigned int buf_size); - -/* Cause modules to log their state once */ -void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); - -/* Cause encoder firmware to be uploaded into the device. This is normally - done autonomously, but the interface is exported here because it is also - a debugging aid. */ -int pvr2_upload_firmware2(struct pvr2_hdw *hdw); - -#endif /* __PVRUSB2_HDW_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c deleted file mode 100644 index 885ce11f222d..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ /dev/null @@ -1,698 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include "pvrusb2-i2c-core.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-fx2-cmd.h" -#include "pvrusb2.h" - -#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) - -/* - - This module attempts to implement a compliant I2C adapter for the pvrusb2 - device. - -*/ - -static unsigned int i2c_scan; -module_param(i2c_scan, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); - -static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 }; -module_param_array(ir_mode, int, NULL, 0444); -MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR"); - -static int pvr2_disable_ir_video; -module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video, - int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(disable_autoload_ir_video, - "1=do not try to autoload ir_video IR receiver"); - -static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ - u8 i2c_addr, /* I2C address we're talking to */ - u8 *data, /* Data to write */ - u16 length) /* Size of data to write */ -{ - /* Return value - default 0 means success */ - int ret; - - - if (!data) length = 0; - if (length > (sizeof(hdw->cmd_buffer) - 3)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Killing an I2C write to %u that is too large" - " (desired=%u limit=%u)", - i2c_addr, - length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3)); - return -ENOTSUPP; - } - - LOCK_TAKE(hdw->ctl_lock); - - /* Clear the command buffer (likely to be paranoia) */ - memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); - - /* Set up command buffer for an I2C write */ - hdw->cmd_buffer[0] = FX2CMD_I2C_WRITE; /* write prefix */ - hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */ - hdw->cmd_buffer[2] = length; /* length of what follows */ - if (length) memcpy(hdw->cmd_buffer + 3, data, length); - - /* Do the operation */ - ret = pvr2_send_request(hdw, - hdw->cmd_buffer, - length + 3, - hdw->cmd_buffer, - 1); - if (!ret) { - if (hdw->cmd_buffer[0] != 8) { - ret = -EIO; - if (hdw->cmd_buffer[0] != 7) { - trace_i2c("unexpected status" - " from i2_write[%d]: %d", - i2c_addr,hdw->cmd_buffer[0]); - } - } - } - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - -static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ - u8 i2c_addr, /* I2C address we're talking to */ - u8 *data, /* Data to write */ - u16 dlen, /* Size of data to write */ - u8 *res, /* Where to put data we read */ - u16 rlen) /* Amount of data to read */ -{ - /* Return value - default 0 means success */ - int ret; - - - if (!data) dlen = 0; - if (dlen > (sizeof(hdw->cmd_buffer) - 4)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Killing an I2C read to %u that has wlen too large" - " (desired=%u limit=%u)", - i2c_addr, - dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4)); - return -ENOTSUPP; - } - if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Killing an I2C read to %u that has rlen too large" - " (desired=%u limit=%u)", - i2c_addr, - rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1)); - return -ENOTSUPP; - } - - LOCK_TAKE(hdw->ctl_lock); - - /* Clear the command buffer (likely to be paranoia) */ - memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); - - /* Set up command buffer for an I2C write followed by a read */ - hdw->cmd_buffer[0] = FX2CMD_I2C_READ; /* read prefix */ - hdw->cmd_buffer[1] = dlen; /* arg length */ - hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one - more byte (status). */ - hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */ - if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen); - - /* Do the operation */ - ret = pvr2_send_request(hdw, - hdw->cmd_buffer, - 4 + dlen, - hdw->cmd_buffer, - rlen + 1); - if (!ret) { - if (hdw->cmd_buffer[0] != 8) { - ret = -EIO; - if (hdw->cmd_buffer[0] != 7) { - trace_i2c("unexpected status" - " from i2_read[%d]: %d", - i2c_addr,hdw->cmd_buffer[0]); - } - } - } - - /* Copy back the result */ - if (res && rlen) { - if (ret) { - /* Error, just blank out the return buffer */ - memset(res, 0, rlen); - } else { - memcpy(res, hdw->cmd_buffer + 1, rlen); - } - } - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - -/* This is the common low level entry point for doing I2C operations to the - hardware. */ -static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, - u8 i2c_addr, - u8 *wdata, - u16 wlen, - u8 *rdata, - u16 rlen) -{ - if (!rdata) rlen = 0; - if (!wdata) wlen = 0; - if (rlen || !wlen) { - return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen); - } else { - return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen); - } -} - - -/* This is a special entry point for cases of I2C transaction attempts to - the IR receiver. The implementation here simulates the IR receiver by - issuing a command to the FX2 firmware and using that response to return - what the real I2C receiver would have returned. We use this for 24xxx - devices, where the IR receiver chip has been removed and replaced with - FX2 related logic. */ -static int i2c_24xxx_ir(struct pvr2_hdw *hdw, - u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) -{ - u8 dat[4]; - unsigned int stat; - - if (!(rlen || wlen)) { - /* This is a probe attempt. Just let it succeed. */ - return 0; - } - - /* We don't understand this kind of transaction */ - if ((wlen != 0) || (rlen == 0)) return -EIO; - - if (rlen < 3) { - /* Mike Isely Appears to be a probe - attempt from lirc. Just fill in zeroes and return. If - we try instead to do the full transaction here, then bad - things seem to happen within the lirc driver module - (version 0.8.0-7 sources from Debian, when run under - vanilla 2.6.17.6 kernel) - and I don't have the patience - to chase it down. */ - if (rlen > 0) rdata[0] = 0; - if (rlen > 1) rdata[1] = 0; - return 0; - } - - /* Issue a command to the FX2 to read the IR receiver. */ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_GET_IR_CODE; - stat = pvr2_send_request(hdw, - hdw->cmd_buffer,1, - hdw->cmd_buffer,4); - dat[0] = hdw->cmd_buffer[0]; - dat[1] = hdw->cmd_buffer[1]; - dat[2] = hdw->cmd_buffer[2]; - dat[3] = hdw->cmd_buffer[3]; - } while (0); LOCK_GIVE(hdw->ctl_lock); - - /* Give up if that operation failed. */ - if (stat != 0) return stat; - - /* Mangle the results into something that looks like the real IR - receiver. */ - rdata[2] = 0xc1; - if (dat[0] != 1) { - /* No code received. */ - rdata[0] = 0; - rdata[1] = 0; - } else { - u16 val; - /* Mash the FX2 firmware-provided IR code into something - that the normal i2c chip-level driver expects. */ - val = dat[1]; - val <<= 8; - val |= dat[2]; - val >>= 1; - val &= ~0x0003; - val |= 0x8000; - rdata[0] = (val >> 8) & 0xffu; - rdata[1] = val & 0xffu; - } - - return 0; -} - -/* This is a special entry point that is entered if an I2C operation is - attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this - part doesn't work, but we know it is really there. So let's look for - the autodetect attempt and just return success if we see that. */ -static int i2c_hack_wm8775(struct pvr2_hdw *hdw, - u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) -{ - if (!(rlen || wlen)) { - // This is a probe attempt. Just let it succeed. - return 0; - } - return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); -} - -/* This is an entry point designed to always fail any attempt to perform a - transfer. We use this to cause certain I2C addresses to not be - probed. */ -static int i2c_black_hole(struct pvr2_hdw *hdw, - u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) -{ - return -EIO; -} - -/* This is a special entry point that is entered if an I2C operation is - attempted to a cx25840 chip on model 24xxx hardware. This chip can - sometimes wedge itself. Worse still, when this happens msp3400 can - falsely detect this part and then the system gets hosed up after msp3400 - gets confused and dies. What we want to do here is try to keep msp3400 - away and also try to notice if the chip is wedged and send a warning to - the system log. */ -static int i2c_hack_cx25840(struct pvr2_hdw *hdw, - u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) -{ - int ret; - unsigned int subaddr; - u8 wbuf[2]; - int state = hdw->i2c_cx25840_hack_state; - - if (!(rlen || wlen)) { - // Probe attempt - always just succeed and don't bother the - // hardware (this helps to make the state machine further - // down somewhat easier). - return 0; - } - - if (state == 3) { - return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); - } - - /* We're looking for the exact pattern where the revision register - is being read. The cx25840 module will always look at the - revision register first. Any other pattern of access therefore - has to be a probe attempt from somebody else so we'll reject it. - Normally we could just let each client just probe the part - anyway, but when the cx25840 is wedged, msp3400 will get a false - positive and that just screws things up... */ - - if (wlen == 0) { - switch (state) { - case 1: subaddr = 0x0100; break; - case 2: subaddr = 0x0101; break; - default: goto fail; - } - } else if (wlen == 2) { - subaddr = (wdata[0] << 8) | wdata[1]; - switch (subaddr) { - case 0x0100: state = 1; break; - case 0x0101: state = 2; break; - default: goto fail; - } - } else { - goto fail; - } - if (!rlen) goto success; - state = 0; - if (rlen != 1) goto fail; - - /* If we get to here then we have a legitimate read for one of the - two revision bytes, so pass it through. */ - wbuf[0] = subaddr >> 8; - wbuf[1] = subaddr; - ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen); - - if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Detected a wedged cx25840 chip;" - " the device will not work."); - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Try power cycling the pvrusb2 device."); - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Disabling further access to the device" - " to prevent other foul-ups."); - // This blocks all further communication with the part. - hdw->i2c_func[0x44] = NULL; - pvr2_hdw_render_useless(hdw); - goto fail; - } - - /* Success! */ - pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK."); - state = 3; - - success: - hdw->i2c_cx25840_hack_state = state; - return 0; - - fail: - hdw->i2c_cx25840_hack_state = state; - return -EIO; -} - -/* This is a very, very limited I2C adapter implementation. We can only - support what we actually know will work on the device... */ -static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], - int num) -{ - int ret = -ENOTSUPP; - pvr2_i2c_func funcp = NULL; - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); - - if (!num) { - ret = -EINVAL; - goto done; - } - if (msgs[0].addr < PVR2_I2C_FUNC_CNT) { - funcp = hdw->i2c_func[msgs[0].addr]; - } - if (!funcp) { - ret = -EIO; - goto done; - } - - if (num == 1) { - if (msgs[0].flags & I2C_M_RD) { - /* Simple read */ - u16 tcnt,bcnt,offs; - if (!msgs[0].len) { - /* Length == 0 read. This is a probe. */ - if (funcp(hdw,msgs[0].addr,NULL,0,NULL,0)) { - ret = -EIO; - goto done; - } - ret = 1; - goto done; - } - /* If the read is short enough we'll do the whole - thing atomically. Otherwise we have no choice - but to break apart the reads. */ - tcnt = msgs[0].len; - offs = 0; - while (tcnt) { - bcnt = tcnt; - if (bcnt > sizeof(hdw->cmd_buffer)-1) { - bcnt = sizeof(hdw->cmd_buffer)-1; - } - if (funcp(hdw,msgs[0].addr,NULL,0, - msgs[0].buf+offs,bcnt)) { - ret = -EIO; - goto done; - } - offs += bcnt; - tcnt -= bcnt; - } - ret = 1; - goto done; - } else { - /* Simple write */ - ret = 1; - if (funcp(hdw,msgs[0].addr, - msgs[0].buf,msgs[0].len,NULL,0)) { - ret = -EIO; - } - goto done; - } - } else if (num == 2) { - if (msgs[0].addr != msgs[1].addr) { - trace_i2c("i2c refusing 2 phase transfer with" - " conflicting target addresses"); - ret = -ENOTSUPP; - goto done; - } - if ((!((msgs[0].flags & I2C_M_RD))) && - (msgs[1].flags & I2C_M_RD)) { - u16 tcnt,bcnt,wcnt,offs; - /* Write followed by atomic read. If the read - portion is short enough we'll do the whole thing - atomically. Otherwise we have no choice but to - break apart the reads. */ - tcnt = msgs[1].len; - wcnt = msgs[0].len; - offs = 0; - while (tcnt || wcnt) { - bcnt = tcnt; - if (bcnt > sizeof(hdw->cmd_buffer)-1) { - bcnt = sizeof(hdw->cmd_buffer)-1; - } - if (funcp(hdw,msgs[0].addr, - msgs[0].buf,wcnt, - msgs[1].buf+offs,bcnt)) { - ret = -EIO; - goto done; - } - offs += bcnt; - tcnt -= bcnt; - wcnt = 0; - } - ret = 2; - goto done; - } else { - trace_i2c("i2c refusing complex transfer" - " read0=%d read1=%d", - (msgs[0].flags & I2C_M_RD), - (msgs[1].flags & I2C_M_RD)); - } - } else { - trace_i2c("i2c refusing %d phase transfer",num); - } - - done: - if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) { - unsigned int idx,offs,cnt; - for (idx = 0; idx < num; idx++) { - cnt = msgs[idx].len; - printk(KERN_INFO - "pvrusb2 i2c xfer %u/%u:" - " addr=0x%x len=%d %s", - idx+1,num, - msgs[idx].addr, - cnt, - (msgs[idx].flags & I2C_M_RD ? - "read" : "write")); - if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) { - if (cnt > 8) cnt = 8; - printk(" ["); - for (offs = 0; offs < (cnt>8?8:cnt); offs++) { - if (offs) printk(" "); - printk("%02x",msgs[idx].buf[offs]); - } - if (offs < cnt) printk(" ..."); - printk("]"); - } - if (idx+1 == num) { - printk(" result=%d",ret); - } - printk("\n"); - } - if (!num) { - printk(KERN_INFO - "pvrusb2 i2c xfer null transfer result=%d\n", - ret); - } - } - return ret; -} - -static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; -} - -static struct i2c_algorithm pvr2_i2c_algo_template = { - .master_xfer = pvr2_i2c_xfer, - .functionality = pvr2_i2c_functionality, -}; - -static struct i2c_adapter pvr2_i2c_adap_template = { - .owner = THIS_MODULE, - .class = 0, -}; - - -/* Return true if device exists at given address */ -static int do_i2c_probe(struct pvr2_hdw *hdw, int addr) -{ - struct i2c_msg msg[1]; - int rc; - msg[0].addr = 0; - msg[0].flags = I2C_M_RD; - msg[0].len = 0; - msg[0].buf = NULL; - msg[0].addr = addr; - rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg)); - return rc == 1; -} - -static void do_i2c_scan(struct pvr2_hdw *hdw) -{ - int i; - printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name); - for (i = 0; i < 128; i++) { - if (do_i2c_probe(hdw, i)) { - printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n", - hdw->name, i); - } - } - printk(KERN_INFO "%s: i2c scan done.\n", hdw->name); -} - -static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) -{ - struct i2c_board_info info; - struct IR_i2c_init_data *init_data = &hdw->ir_init_data; - if (pvr2_disable_ir_video) { - pvr2_trace(PVR2_TRACE_INFO, - "Automatic binding of ir_video has been disabled."); - return; - } - memset(&info, 0, sizeof(struct i2c_board_info)); - switch (hdw->ir_scheme_active) { - case PVR2_IR_SCHEME_24XXX: /* FX2-controlled IR */ - case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */ - init_data->ir_codes = RC_MAP_HAUPPAUGE; - init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; - init_data->type = RC_TYPE_RC5; - init_data->name = hdw->hdw_desc->description; - init_data->polling_interval = 100; /* ms From ir-kbd-i2c */ - /* IR Receiver */ - info.addr = 0x18; - info.platform_data = init_data; - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", - info.type, info.addr); - i2c_new_device(&hdw->i2c_adap, &info); - break; - case PVR2_IR_SCHEME_ZILOG: /* HVR-1950 style */ - case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */ - init_data->ir_codes = RC_MAP_HAUPPAUGE; - init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - init_data->type = RC_TYPE_RC5; - init_data->name = hdw->hdw_desc->description; - /* IR Receiver */ - info.addr = 0x71; - info.platform_data = init_data; - strlcpy(info.type, "ir_rx_z8f0811_haup", I2C_NAME_SIZE); - pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", - info.type, info.addr); - i2c_new_device(&hdw->i2c_adap, &info); - /* IR Trasmitter */ - info.addr = 0x70; - info.platform_data = init_data; - strlcpy(info.type, "ir_tx_z8f0811_haup", I2C_NAME_SIZE); - pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", - info.type, info.addr); - i2c_new_device(&hdw->i2c_adap, &info); - break; - default: - /* The device either doesn't support I2C-based IR or we - don't know (yet) how to operate IR on the device. */ - break; - } -} - -void pvr2_i2c_core_init(struct pvr2_hdw *hdw) -{ - unsigned int idx; - - /* The default action for all possible I2C addresses is just to do - the transfer normally. */ - for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { - hdw->i2c_func[idx] = pvr2_i2c_basic_op; - } - - /* However, deal with various special cases for 24xxx hardware. */ - if (ir_mode[hdw->unit_number] == 0) { - printk(KERN_INFO "%s: IR disabled\n",hdw->name); - hdw->i2c_func[0x18] = i2c_black_hole; - } else if (ir_mode[hdw->unit_number] == 1) { - if (hdw->ir_scheme_active == PVR2_IR_SCHEME_24XXX) { - /* Set up translation so that our IR looks like a - 29xxx device */ - hdw->i2c_func[0x18] = i2c_24xxx_ir; - } - } - if (hdw->hdw_desc->flag_has_cx25840) { - hdw->i2c_func[0x44] = i2c_hack_cx25840; - } - if (hdw->hdw_desc->flag_has_wm8775) { - hdw->i2c_func[0x1b] = i2c_hack_wm8775; - } - - // Configure the adapter and set up everything else related to it. - memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); - memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); - strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); - hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; - hdw->i2c_adap.algo = &hdw->i2c_algo; - hdw->i2c_adap.algo_data = hdw; - hdw->i2c_linked = !0; - i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev); - i2c_add_adapter(&hdw->i2c_adap); - if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { - /* Probe for a different type of IR receiver on this - device. This is really the only way to differentiate - older 24xxx devices from 24xxx variants that include an - IR blaster. If the IR blaster is present, the IR - receiver is part of that chip and thus we must disable - the emulated IR receiver. */ - if (do_i2c_probe(hdw, 0x71)) { - pvr2_trace(PVR2_TRACE_INFO, - "Device has newer IR hardware;" - " disabling unneeded virtual IR device"); - hdw->i2c_func[0x18] = NULL; - /* Remember that this is a different device... */ - hdw->ir_scheme_active = PVR2_IR_SCHEME_24XXX_MCE; - } - } - if (i2c_scan) do_i2c_scan(hdw); - - pvr2_i2c_register_ir(hdw); -} - -void pvr2_i2c_core_done(struct pvr2_hdw *hdw) -{ - if (hdw->i2c_linked) { - i2c_del_adapter(&hdw->i2c_adap); - hdw->i2c_linked = 0; - } -} - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h deleted file mode 100644 index 6a75769200bd..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_I2C_CORE_H -#define __PVRUSB2_I2C_CORE_H - -struct pvr2_hdw; - -void pvr2_i2c_core_init(struct pvr2_hdw *); -void pvr2_i2c_core_done(struct pvr2_hdw *); - - -#endif /* __PVRUSB2_I2C_ADAPTER_H */ - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c deleted file mode 100644 index 20b6ae0bb40d..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-io.c +++ /dev/null @@ -1,695 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-io.h" -#include "pvrusb2-debug.h" -#include -#include -#include -#include - -static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state); - -#define BUFFER_SIG 0x47653271 - -// #define SANITY_CHECK_BUFFERS - - -#ifdef SANITY_CHECK_BUFFERS -#define BUFFER_CHECK(bp) do { \ - if ((bp)->signature != BUFFER_SIG) { \ - pvr2_trace(PVR2_TRACE_ERROR_LEGS, \ - "Buffer %p is bad at %s:%d", \ - (bp),__FILE__,__LINE__); \ - pvr2_buffer_describe(bp,"BadSig"); \ - BUG(); \ - } \ -} while (0) -#else -#define BUFFER_CHECK(bp) do {} while(0) -#endif - -struct pvr2_stream { - /* Buffers queued for reading */ - struct list_head queued_list; - unsigned int q_count; - unsigned int q_bcount; - /* Buffers with retrieved data */ - struct list_head ready_list; - unsigned int r_count; - unsigned int r_bcount; - /* Buffers available for use */ - struct list_head idle_list; - unsigned int i_count; - unsigned int i_bcount; - /* Pointers to all buffers */ - struct pvr2_buffer **buffers; - /* Array size of buffers */ - unsigned int buffer_slot_count; - /* Total buffers actually in circulation */ - unsigned int buffer_total_count; - /* Designed number of buffers to be in circulation */ - unsigned int buffer_target_count; - /* Executed when ready list become non-empty */ - pvr2_stream_callback callback_func; - void *callback_data; - /* Context for transfer endpoint */ - struct usb_device *dev; - int endpoint; - /* Overhead for mutex enforcement */ - spinlock_t list_lock; - struct mutex mutex; - /* Tracking state for tolerating errors */ - unsigned int fail_count; - unsigned int fail_tolerance; - - unsigned int buffers_processed; - unsigned int buffers_failed; - unsigned int bytes_processed; -}; - -struct pvr2_buffer { - int id; - int signature; - enum pvr2_buffer_state state; - void *ptr; /* Pointer to storage area */ - unsigned int max_count; /* Size of storage area */ - unsigned int used_count; /* Amount of valid data in storage area */ - int status; /* Transfer result status */ - struct pvr2_stream *stream; - struct list_head list_overhead; - struct urb *purb; -}; - -static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st) -{ - switch (st) { - case pvr2_buffer_state_none: return "none"; - case pvr2_buffer_state_idle: return "idle"; - case pvr2_buffer_state_queued: return "queued"; - case pvr2_buffer_state_ready: return "ready"; - } - return "unknown"; -} - -#ifdef SANITY_CHECK_BUFFERS -static void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg) -{ - pvr2_trace(PVR2_TRACE_INFO, - "buffer%s%s %p state=%s id=%d status=%d" - " stream=%p purb=%p sig=0x%x", - (msg ? " " : ""), - (msg ? msg : ""), - bp, - (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"), - (bp ? bp->id : 0), - (bp ? bp->status : 0), - (bp ? bp->stream : NULL), - (bp ? bp->purb : NULL), - (bp ? bp->signature : 0)); -} -#endif /* SANITY_CHECK_BUFFERS */ - -static void pvr2_buffer_remove(struct pvr2_buffer *bp) -{ - unsigned int *cnt; - unsigned int *bcnt; - unsigned int ccnt; - struct pvr2_stream *sp = bp->stream; - switch (bp->state) { - case pvr2_buffer_state_idle: - cnt = &sp->i_count; - bcnt = &sp->i_bcount; - ccnt = bp->max_count; - break; - case pvr2_buffer_state_queued: - cnt = &sp->q_count; - bcnt = &sp->q_bcount; - ccnt = bp->max_count; - break; - case pvr2_buffer_state_ready: - cnt = &sp->r_count; - bcnt = &sp->r_bcount; - ccnt = bp->used_count; - break; - default: - return; - } - list_del_init(&bp->list_overhead); - (*cnt)--; - (*bcnt) -= ccnt; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/" - " bufferPool %8s dec cap=%07d cnt=%02d", - pvr2_buffer_state_decode(bp->state),*bcnt,*cnt); - bp->state = pvr2_buffer_state_none; -} - -static void pvr2_buffer_set_none(struct pvr2_buffer *bp) -{ - unsigned long irq_flags; - struct pvr2_stream *sp; - BUFFER_CHECK(bp); - sp = bp->stream; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", - bp, - pvr2_buffer_state_decode(bp->state), - pvr2_buffer_state_decode(pvr2_buffer_state_none)); - spin_lock_irqsave(&sp->list_lock,irq_flags); - pvr2_buffer_remove(bp); - spin_unlock_irqrestore(&sp->list_lock,irq_flags); -} - -static int pvr2_buffer_set_ready(struct pvr2_buffer *bp) -{ - int fl; - unsigned long irq_flags; - struct pvr2_stream *sp; - BUFFER_CHECK(bp); - sp = bp->stream; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", - bp, - pvr2_buffer_state_decode(bp->state), - pvr2_buffer_state_decode(pvr2_buffer_state_ready)); - spin_lock_irqsave(&sp->list_lock,irq_flags); - fl = (sp->r_count == 0); - pvr2_buffer_remove(bp); - list_add_tail(&bp->list_overhead,&sp->ready_list); - bp->state = pvr2_buffer_state_ready; - (sp->r_count)++; - sp->r_bcount += bp->used_count; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/" - " bufferPool %8s inc cap=%07d cnt=%02d", - pvr2_buffer_state_decode(bp->state), - sp->r_bcount,sp->r_count); - spin_unlock_irqrestore(&sp->list_lock,irq_flags); - return fl; -} - -static void pvr2_buffer_set_idle(struct pvr2_buffer *bp) -{ - unsigned long irq_flags; - struct pvr2_stream *sp; - BUFFER_CHECK(bp); - sp = bp->stream; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", - bp, - pvr2_buffer_state_decode(bp->state), - pvr2_buffer_state_decode(pvr2_buffer_state_idle)); - spin_lock_irqsave(&sp->list_lock,irq_flags); - pvr2_buffer_remove(bp); - list_add_tail(&bp->list_overhead,&sp->idle_list); - bp->state = pvr2_buffer_state_idle; - (sp->i_count)++; - sp->i_bcount += bp->max_count; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/" - " bufferPool %8s inc cap=%07d cnt=%02d", - pvr2_buffer_state_decode(bp->state), - sp->i_bcount,sp->i_count); - spin_unlock_irqrestore(&sp->list_lock,irq_flags); -} - -static void pvr2_buffer_set_queued(struct pvr2_buffer *bp) -{ - unsigned long irq_flags; - struct pvr2_stream *sp; - BUFFER_CHECK(bp); - sp = bp->stream; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", - bp, - pvr2_buffer_state_decode(bp->state), - pvr2_buffer_state_decode(pvr2_buffer_state_queued)); - spin_lock_irqsave(&sp->list_lock,irq_flags); - pvr2_buffer_remove(bp); - list_add_tail(&bp->list_overhead,&sp->queued_list); - bp->state = pvr2_buffer_state_queued; - (sp->q_count)++; - sp->q_bcount += bp->max_count; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/" - " bufferPool %8s inc cap=%07d cnt=%02d", - pvr2_buffer_state_decode(bp->state), - sp->q_bcount,sp->q_count); - spin_unlock_irqrestore(&sp->list_lock,irq_flags); -} - -static void pvr2_buffer_wipe(struct pvr2_buffer *bp) -{ - if (bp->state == pvr2_buffer_state_queued) { - usb_kill_urb(bp->purb); - } -} - -static int pvr2_buffer_init(struct pvr2_buffer *bp, - struct pvr2_stream *sp, - unsigned int id) -{ - memset(bp,0,sizeof(*bp)); - bp->signature = BUFFER_SIG; - bp->id = id; - pvr2_trace(PVR2_TRACE_BUF_POOL, - "/*---TRACE_FLOW---*/ bufferInit %p stream=%p",bp,sp); - bp->stream = sp; - bp->state = pvr2_buffer_state_none; - INIT_LIST_HEAD(&bp->list_overhead); - bp->purb = usb_alloc_urb(0,GFP_KERNEL); - if (! bp->purb) return -ENOMEM; -#ifdef SANITY_CHECK_BUFFERS - pvr2_buffer_describe(bp,"create"); -#endif - return 0; -} - -static void pvr2_buffer_done(struct pvr2_buffer *bp) -{ -#ifdef SANITY_CHECK_BUFFERS - pvr2_buffer_describe(bp,"delete"); -#endif - pvr2_buffer_wipe(bp); - pvr2_buffer_set_none(bp); - bp->signature = 0; - bp->stream = NULL; - usb_free_urb(bp->purb); - pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/" - " bufferDone %p",bp); -} - -static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt) -{ - int ret; - unsigned int scnt; - - /* Allocate buffers pointer array in multiples of 32 entries */ - if (cnt == sp->buffer_total_count) return 0; - - pvr2_trace(PVR2_TRACE_BUF_POOL, - "/*---TRACE_FLOW---*/ poolResize " - " stream=%p cur=%d adj=%+d", - sp, - sp->buffer_total_count, - cnt-sp->buffer_total_count); - - scnt = cnt & ~0x1f; - if (cnt > scnt) scnt += 0x20; - - if (cnt > sp->buffer_total_count) { - if (scnt > sp->buffer_slot_count) { - struct pvr2_buffer **nb; - nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL); - if (!nb) return -ENOMEM; - if (sp->buffer_slot_count) { - memcpy(nb,sp->buffers, - sp->buffer_slot_count * sizeof(*nb)); - kfree(sp->buffers); - } - sp->buffers = nb; - sp->buffer_slot_count = scnt; - } - while (sp->buffer_total_count < cnt) { - struct pvr2_buffer *bp; - bp = kmalloc(sizeof(*bp),GFP_KERNEL); - if (!bp) return -ENOMEM; - ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count); - if (ret) { - kfree(bp); - return -ENOMEM; - } - sp->buffers[sp->buffer_total_count] = bp; - (sp->buffer_total_count)++; - pvr2_buffer_set_idle(bp); - } - } else { - while (sp->buffer_total_count > cnt) { - struct pvr2_buffer *bp; - bp = sp->buffers[sp->buffer_total_count - 1]; - /* Paranoia */ - sp->buffers[sp->buffer_total_count - 1] = NULL; - (sp->buffer_total_count)--; - pvr2_buffer_done(bp); - kfree(bp); - } - if (scnt < sp->buffer_slot_count) { - struct pvr2_buffer **nb = NULL; - if (scnt) { - nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL); - if (!nb) return -ENOMEM; - memcpy(nb,sp->buffers,scnt * sizeof(*nb)); - } - kfree(sp->buffers); - sp->buffers = nb; - sp->buffer_slot_count = scnt; - } - } - return 0; -} - -static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp) -{ - struct pvr2_buffer *bp; - unsigned int cnt; - - if (sp->buffer_total_count == sp->buffer_target_count) return 0; - - pvr2_trace(PVR2_TRACE_BUF_POOL, - "/*---TRACE_FLOW---*/" - " poolCheck stream=%p cur=%d tgt=%d", - sp,sp->buffer_total_count,sp->buffer_target_count); - - if (sp->buffer_total_count < sp->buffer_target_count) { - return pvr2_stream_buffer_count(sp,sp->buffer_target_count); - } - - cnt = 0; - while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) { - bp = sp->buffers[sp->buffer_total_count - (cnt + 1)]; - if (bp->state != pvr2_buffer_state_idle) break; - cnt++; - } - if (cnt) { - pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt); - } - - return 0; -} - -static void pvr2_stream_internal_flush(struct pvr2_stream *sp) -{ - struct list_head *lp; - struct pvr2_buffer *bp1; - while ((lp = sp->queued_list.next) != &sp->queued_list) { - bp1 = list_entry(lp,struct pvr2_buffer,list_overhead); - pvr2_buffer_wipe(bp1); - /* At this point, we should be guaranteed that no - completion callback may happen on this buffer. But it's - possible that it might have completed after we noticed - it but before we wiped it. So double check its status - here first. */ - if (bp1->state != pvr2_buffer_state_queued) continue; - pvr2_buffer_set_idle(bp1); - } - if (sp->buffer_total_count != sp->buffer_target_count) { - pvr2_stream_achieve_buffer_count(sp); - } -} - -static void pvr2_stream_init(struct pvr2_stream *sp) -{ - spin_lock_init(&sp->list_lock); - mutex_init(&sp->mutex); - INIT_LIST_HEAD(&sp->queued_list); - INIT_LIST_HEAD(&sp->ready_list); - INIT_LIST_HEAD(&sp->idle_list); -} - -static void pvr2_stream_done(struct pvr2_stream *sp) -{ - mutex_lock(&sp->mutex); do { - pvr2_stream_internal_flush(sp); - pvr2_stream_buffer_count(sp,0); - } while (0); mutex_unlock(&sp->mutex); -} - -static void buffer_complete(struct urb *urb) -{ - struct pvr2_buffer *bp = urb->context; - struct pvr2_stream *sp; - unsigned long irq_flags; - BUFFER_CHECK(bp); - sp = bp->stream; - bp->used_count = 0; - bp->status = 0; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d", - bp,urb->status,urb->actual_length); - spin_lock_irqsave(&sp->list_lock,irq_flags); - if ((!(urb->status)) || - (urb->status == -ENOENT) || - (urb->status == -ECONNRESET) || - (urb->status == -ESHUTDOWN)) { - (sp->buffers_processed)++; - sp->bytes_processed += urb->actual_length; - bp->used_count = urb->actual_length; - if (sp->fail_count) { - pvr2_trace(PVR2_TRACE_TOLERANCE, - "stream %p transfer ok" - " - fail count reset",sp); - sp->fail_count = 0; - } - } else if (sp->fail_count < sp->fail_tolerance) { - // We can tolerate this error, because we're below the - // threshold... - (sp->fail_count)++; - (sp->buffers_failed)++; - pvr2_trace(PVR2_TRACE_TOLERANCE, - "stream %p ignoring error %d" - " - fail count increased to %u", - sp,urb->status,sp->fail_count); - } else { - (sp->buffers_failed)++; - bp->status = urb->status; - } - spin_unlock_irqrestore(&sp->list_lock,irq_flags); - pvr2_buffer_set_ready(bp); - if (sp && sp->callback_func) { - sp->callback_func(sp->callback_data); - } -} - -struct pvr2_stream *pvr2_stream_create(void) -{ - struct pvr2_stream *sp; - sp = kzalloc(sizeof(*sp),GFP_KERNEL); - if (!sp) return sp; - pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp); - pvr2_stream_init(sp); - return sp; -} - -void pvr2_stream_destroy(struct pvr2_stream *sp) -{ - if (!sp) return; - pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp); - pvr2_stream_done(sp); - kfree(sp); -} - -void pvr2_stream_setup(struct pvr2_stream *sp, - struct usb_device *dev, - int endpoint, - unsigned int tolerance) -{ - mutex_lock(&sp->mutex); do { - pvr2_stream_internal_flush(sp); - sp->dev = dev; - sp->endpoint = endpoint; - sp->fail_tolerance = tolerance; - } while(0); mutex_unlock(&sp->mutex); -} - -void pvr2_stream_set_callback(struct pvr2_stream *sp, - pvr2_stream_callback func, - void *data) -{ - unsigned long irq_flags; - mutex_lock(&sp->mutex); do { - spin_lock_irqsave(&sp->list_lock,irq_flags); - sp->callback_data = data; - sp->callback_func = func; - spin_unlock_irqrestore(&sp->list_lock,irq_flags); - } while(0); mutex_unlock(&sp->mutex); -} - -void pvr2_stream_get_stats(struct pvr2_stream *sp, - struct pvr2_stream_stats *stats, - int zero_counts) -{ - unsigned long irq_flags; - spin_lock_irqsave(&sp->list_lock,irq_flags); - if (stats) { - stats->buffers_in_queue = sp->q_count; - stats->buffers_in_idle = sp->i_count; - stats->buffers_in_ready = sp->r_count; - stats->buffers_processed = sp->buffers_processed; - stats->buffers_failed = sp->buffers_failed; - stats->bytes_processed = sp->bytes_processed; - } - if (zero_counts) { - sp->buffers_processed = 0; - sp->buffers_failed = 0; - sp->bytes_processed = 0; - } - spin_unlock_irqrestore(&sp->list_lock,irq_flags); -} - -/* Query / set the nominal buffer count */ -int pvr2_stream_get_buffer_count(struct pvr2_stream *sp) -{ - return sp->buffer_target_count; -} - -int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt) -{ - int ret; - if (sp->buffer_target_count == cnt) return 0; - mutex_lock(&sp->mutex); do { - sp->buffer_target_count = cnt; - ret = pvr2_stream_achieve_buffer_count(sp); - } while(0); mutex_unlock(&sp->mutex); - return ret; -} - -struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp) -{ - struct list_head *lp = sp->idle_list.next; - if (lp == &sp->idle_list) return NULL; - return list_entry(lp,struct pvr2_buffer,list_overhead); -} - -struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp) -{ - struct list_head *lp = sp->ready_list.next; - if (lp == &sp->ready_list) return NULL; - return list_entry(lp,struct pvr2_buffer,list_overhead); -} - -struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id) -{ - if (id < 0) return NULL; - if (id >= sp->buffer_total_count) return NULL; - return sp->buffers[id]; -} - -int pvr2_stream_get_ready_count(struct pvr2_stream *sp) -{ - return sp->r_count; -} - -void pvr2_stream_kill(struct pvr2_stream *sp) -{ - struct pvr2_buffer *bp; - mutex_lock(&sp->mutex); do { - pvr2_stream_internal_flush(sp); - while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) { - pvr2_buffer_set_idle(bp); - } - if (sp->buffer_total_count != sp->buffer_target_count) { - pvr2_stream_achieve_buffer_count(sp); - } - } while(0); mutex_unlock(&sp->mutex); -} - -int pvr2_buffer_queue(struct pvr2_buffer *bp) -{ -#undef SEED_BUFFER -#ifdef SEED_BUFFER - unsigned int idx; - unsigned int val; -#endif - int ret = 0; - struct pvr2_stream *sp; - if (!bp) return -EINVAL; - sp = bp->stream; - mutex_lock(&sp->mutex); do { - pvr2_buffer_wipe(bp); - if (!sp->dev) { - ret = -EIO; - break; - } - pvr2_buffer_set_queued(bp); -#ifdef SEED_BUFFER - for (idx = 0; idx < (bp->max_count) / 4; idx++) { - val = bp->id << 24; - val |= idx; - ((unsigned int *)(bp->ptr))[idx] = val; - } -#endif - bp->status = -EINPROGRESS; - usb_fill_bulk_urb(bp->purb, // struct urb *urb - sp->dev, // struct usb_device *dev - // endpoint (below) - usb_rcvbulkpipe(sp->dev,sp->endpoint), - bp->ptr, // void *transfer_buffer - bp->max_count, // int buffer_length - buffer_complete, - bp); - usb_submit_urb(bp->purb,GFP_KERNEL); - } while(0); mutex_unlock(&sp->mutex); - return ret; -} - -int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt) -{ - int ret = 0; - unsigned long irq_flags; - struct pvr2_stream *sp; - if (!bp) return -EINVAL; - sp = bp->stream; - mutex_lock(&sp->mutex); do { - spin_lock_irqsave(&sp->list_lock,irq_flags); - if (bp->state != pvr2_buffer_state_idle) { - ret = -EPERM; - } else { - bp->ptr = ptr; - bp->stream->i_bcount -= bp->max_count; - bp->max_count = cnt; - bp->stream->i_bcount += bp->max_count; - pvr2_trace(PVR2_TRACE_BUF_FLOW, - "/*---TRACE_FLOW---*/ bufferPool " - " %8s cap cap=%07d cnt=%02d", - pvr2_buffer_state_decode( - pvr2_buffer_state_idle), - bp->stream->i_bcount,bp->stream->i_count); - } - spin_unlock_irqrestore(&sp->list_lock,irq_flags); - } while(0); mutex_unlock(&sp->mutex); - return ret; -} - -unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp) -{ - return bp->used_count; -} - -int pvr2_buffer_get_status(struct pvr2_buffer *bp) -{ - return bp->status; -} - -int pvr2_buffer_get_id(struct pvr2_buffer *bp) -{ - return bp->id; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h deleted file mode 100644 index afb7e87c0394..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-io.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_IO_H -#define __PVRUSB2_IO_H - -#include -#include - -typedef void (*pvr2_stream_callback)(void *); - -enum pvr2_buffer_state { - pvr2_buffer_state_none = 0, // Not on any list - pvr2_buffer_state_idle = 1, // Buffer is ready to be used again - pvr2_buffer_state_queued = 2, // Buffer has been queued for filling - pvr2_buffer_state_ready = 3, // Buffer has data available -}; - -struct pvr2_stream; -struct pvr2_buffer; - -struct pvr2_stream_stats { - unsigned int buffers_in_queue; - unsigned int buffers_in_idle; - unsigned int buffers_in_ready; - unsigned int buffers_processed; - unsigned int buffers_failed; - unsigned int bytes_processed; -}; - -/* Initialize / tear down stream structure */ -struct pvr2_stream *pvr2_stream_create(void); -void pvr2_stream_destroy(struct pvr2_stream *); -void pvr2_stream_setup(struct pvr2_stream *, - struct usb_device *dev,int endpoint, - unsigned int tolerance); -void pvr2_stream_set_callback(struct pvr2_stream *, - pvr2_stream_callback func, - void *data); -void pvr2_stream_get_stats(struct pvr2_stream *, - struct pvr2_stream_stats *, - int zero_counts); - -/* Query / set the nominal buffer count */ -int pvr2_stream_get_buffer_count(struct pvr2_stream *); -int pvr2_stream_set_buffer_count(struct pvr2_stream *,unsigned int); - -/* Get a pointer to a buffer that is either idle, ready, or is specified - named. */ -struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *); -struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *); -struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id); - -/* Find out how many buffers are idle or ready */ -int pvr2_stream_get_ready_count(struct pvr2_stream *); - - -/* Kill all pending buffers and throw away any ready buffers as well */ -void pvr2_stream_kill(struct pvr2_stream *); - -/* Set up the actual storage for a buffer */ -int pvr2_buffer_set_buffer(struct pvr2_buffer *,void *ptr,unsigned int cnt); - -/* Find out size of data in the given ready buffer */ -unsigned int pvr2_buffer_get_count(struct pvr2_buffer *); - -/* Retrieve completion code for given ready buffer */ -int pvr2_buffer_get_status(struct pvr2_buffer *); - -/* Retrieve ID of given buffer */ -int pvr2_buffer_get_id(struct pvr2_buffer *); - -/* Start reading into given buffer (kill it if needed) */ -int pvr2_buffer_queue(struct pvr2_buffer *); - -#endif /* __PVRUSB2_IO_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c deleted file mode 100644 index bba6115c9ae8..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-ioread.h" -#include "pvrusb2-debug.h" -#include -#include -#include -#include -#include -#include - -#define BUFFER_COUNT 32 -#define BUFFER_SIZE PAGE_ALIGN(0x4000) - -struct pvr2_ioread { - struct pvr2_stream *stream; - char *buffer_storage[BUFFER_COUNT]; - char *sync_key_ptr; - unsigned int sync_key_len; - unsigned int sync_buf_offs; - unsigned int sync_state; - unsigned int sync_trashed_count; - int enabled; // Streaming is on - int spigot_open; // OK to pass data to client - int stream_running; // Passing data to client now - - /* State relevant to current buffer being read */ - struct pvr2_buffer *c_buf; - char *c_data_ptr; - unsigned int c_data_len; - unsigned int c_data_offs; - struct mutex mutex; -}; - -static int pvr2_ioread_init(struct pvr2_ioread *cp) -{ - unsigned int idx; - - cp->stream = NULL; - mutex_init(&cp->mutex); - - for (idx = 0; idx < BUFFER_COUNT; idx++) { - cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL); - if (!(cp->buffer_storage[idx])) break; - } - - if (idx < BUFFER_COUNT) { - // An allocation appears to have failed - for (idx = 0; idx < BUFFER_COUNT; idx++) { - if (!(cp->buffer_storage[idx])) continue; - kfree(cp->buffer_storage[idx]); - } - return -ENOMEM; - } - return 0; -} - -static void pvr2_ioread_done(struct pvr2_ioread *cp) -{ - unsigned int idx; - - pvr2_ioread_setup(cp,NULL); - for (idx = 0; idx < BUFFER_COUNT; idx++) { - if (!(cp->buffer_storage[idx])) continue; - kfree(cp->buffer_storage[idx]); - } -} - -struct pvr2_ioread *pvr2_ioread_create(void) -{ - struct pvr2_ioread *cp; - cp = kzalloc(sizeof(*cp),GFP_KERNEL); - if (!cp) return NULL; - pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp); - if (pvr2_ioread_init(cp) < 0) { - kfree(cp); - return NULL; - } - return cp; -} - -void pvr2_ioread_destroy(struct pvr2_ioread *cp) -{ - if (!cp) return; - pvr2_ioread_done(cp); - pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp); - if (cp->sync_key_ptr) { - kfree(cp->sync_key_ptr); - cp->sync_key_ptr = NULL; - } - kfree(cp); -} - -void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp, - const char *sync_key_ptr, - unsigned int sync_key_len) -{ - if (!cp) return; - - if (!sync_key_ptr) sync_key_len = 0; - if ((sync_key_len == cp->sync_key_len) && - ((!sync_key_len) || - (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return; - - if (sync_key_len != cp->sync_key_len) { - if (cp->sync_key_ptr) { - kfree(cp->sync_key_ptr); - cp->sync_key_ptr = NULL; - } - cp->sync_key_len = 0; - if (sync_key_len) { - cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL); - if (cp->sync_key_ptr) { - cp->sync_key_len = sync_key_len; - } - } - } - if (!cp->sync_key_len) return; - memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len); -} - -static void pvr2_ioread_stop(struct pvr2_ioread *cp) -{ - if (!(cp->enabled)) return; - pvr2_trace(PVR2_TRACE_START_STOP, - "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp); - pvr2_stream_kill(cp->stream); - cp->c_buf = NULL; - cp->c_data_ptr = NULL; - cp->c_data_len = 0; - cp->c_data_offs = 0; - cp->enabled = 0; - cp->stream_running = 0; - cp->spigot_open = 0; - if (cp->sync_state) { - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/ sync_state <== 0"); - cp->sync_state = 0; - } -} - -static int pvr2_ioread_start(struct pvr2_ioread *cp) -{ - int stat; - struct pvr2_buffer *bp; - if (cp->enabled) return 0; - if (!(cp->stream)) return 0; - pvr2_trace(PVR2_TRACE_START_STOP, - "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp); - while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != NULL) { - stat = pvr2_buffer_queue(bp); - if (stat < 0) { - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/" - " pvr2_ioread_start id=%p" - " error=%d", - cp,stat); - pvr2_ioread_stop(cp); - return stat; - } - } - cp->enabled = !0; - cp->c_buf = NULL; - cp->c_data_ptr = NULL; - cp->c_data_len = 0; - cp->c_data_offs = 0; - cp->stream_running = 0; - if (cp->sync_key_len) { - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/ sync_state <== 1"); - cp->sync_state = 1; - cp->sync_trashed_count = 0; - cp->sync_buf_offs = 0; - } - cp->spigot_open = 0; - return 0; -} - -struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp) -{ - return cp->stream; -} - -int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) -{ - int ret; - unsigned int idx; - struct pvr2_buffer *bp; - - mutex_lock(&cp->mutex); do { - if (cp->stream) { - pvr2_trace(PVR2_TRACE_START_STOP, - "/*---TRACE_READ---*/" - " pvr2_ioread_setup (tear-down) id=%p",cp); - pvr2_ioread_stop(cp); - pvr2_stream_kill(cp->stream); - if (pvr2_stream_get_buffer_count(cp->stream)) { - pvr2_stream_set_buffer_count(cp->stream,0); - } - cp->stream = NULL; - } - if (sp) { - pvr2_trace(PVR2_TRACE_START_STOP, - "/*---TRACE_READ---*/" - " pvr2_ioread_setup (setup) id=%p",cp); - pvr2_stream_kill(sp); - ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); - if (ret < 0) { - mutex_unlock(&cp->mutex); - return ret; - } - for (idx = 0; idx < BUFFER_COUNT; idx++) { - bp = pvr2_stream_get_buffer(sp,idx); - pvr2_buffer_set_buffer(bp, - cp->buffer_storage[idx], - BUFFER_SIZE); - } - cp->stream = sp; - } - } while (0); mutex_unlock(&cp->mutex); - - return 0; -} - -int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl) -{ - int ret = 0; - if ((!fl) == (!(cp->enabled))) return ret; - - mutex_lock(&cp->mutex); do { - if (fl) { - ret = pvr2_ioread_start(cp); - } else { - pvr2_ioread_stop(cp); - } - } while (0); mutex_unlock(&cp->mutex); - return ret; -} - -static int pvr2_ioread_get_buffer(struct pvr2_ioread *cp) -{ - int stat; - - while (cp->c_data_len <= cp->c_data_offs) { - if (cp->c_buf) { - // Flush out current buffer first. - stat = pvr2_buffer_queue(cp->c_buf); - if (stat < 0) { - // Streaming error... - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/" - " pvr2_ioread_read id=%p" - " queue_error=%d", - cp,stat); - pvr2_ioread_stop(cp); - return 0; - } - cp->c_buf = NULL; - cp->c_data_ptr = NULL; - cp->c_data_len = 0; - cp->c_data_offs = 0; - } - // Now get a freshly filled buffer. - cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream); - if (!cp->c_buf) break; // Nothing ready; done. - cp->c_data_len = pvr2_buffer_get_count(cp->c_buf); - if (!cp->c_data_len) { - // Nothing transferred. Was there an error? - stat = pvr2_buffer_get_status(cp->c_buf); - if (stat < 0) { - // Streaming error... - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/" - " pvr2_ioread_read id=%p" - " buffer_error=%d", - cp,stat); - pvr2_ioread_stop(cp); - // Give up. - return 0; - } - // Start over... - continue; - } - cp->c_data_offs = 0; - cp->c_data_ptr = cp->buffer_storage[ - pvr2_buffer_get_id(cp->c_buf)]; - } - return !0; -} - -static void pvr2_ioread_filter(struct pvr2_ioread *cp) -{ - unsigned int idx; - if (!cp->enabled) return; - if (cp->sync_state != 1) return; - - // Search the stream for our synchronization key. This is made - // complicated by the fact that in order to be honest with - // ourselves here we must search across buffer boundaries... - mutex_lock(&cp->mutex); while (1) { - // Ensure we have a buffer - if (!pvr2_ioread_get_buffer(cp)) break; - if (!cp->c_data_len) break; - - // Now walk the buffer contents until we match the key or - // run out of buffer data. - for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) { - if (cp->sync_buf_offs >= cp->sync_key_len) break; - if (cp->c_data_ptr[idx] == - cp->sync_key_ptr[cp->sync_buf_offs]) { - // Found the next key byte - (cp->sync_buf_offs)++; - } else { - // Whoops, mismatched. Start key over... - cp->sync_buf_offs = 0; - } - } - - // Consume what we've walked through - cp->c_data_offs += idx; - cp->sync_trashed_count += idx; - - // If we've found the key, then update state and get out. - if (cp->sync_buf_offs >= cp->sync_key_len) { - cp->sync_trashed_count -= cp->sync_key_len; - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/" - " sync_state <== 2 (skipped %u bytes)", - cp->sync_trashed_count); - cp->sync_state = 2; - cp->sync_buf_offs = 0; - break; - } - - if (cp->c_data_offs < cp->c_data_len) { - // Sanity check - should NEVER get here - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "ERROR: pvr2_ioread filter sync problem" - " len=%u offs=%u", - cp->c_data_len,cp->c_data_offs); - // Get out so we don't get stuck in an infinite - // loop. - break; - } - - continue; // (for clarity) - } mutex_unlock(&cp->mutex); -} - -int pvr2_ioread_avail(struct pvr2_ioread *cp) -{ - int ret; - if (!(cp->enabled)) { - // Stream is not enabled; so this is an I/O error - return -EIO; - } - - if (cp->sync_state == 1) { - pvr2_ioread_filter(cp); - if (cp->sync_state == 1) return -EAGAIN; - } - - ret = 0; - if (cp->stream_running) { - if (!pvr2_stream_get_ready_count(cp->stream)) { - // No data available at all right now. - ret = -EAGAIN; - } - } else { - if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) { - // Haven't buffered up enough yet; try again later - ret = -EAGAIN; - } - } - - if ((!(cp->spigot_open)) != (!(ret == 0))) { - cp->spigot_open = (ret == 0); - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/ data is %s", - cp->spigot_open ? "available" : "pending"); - } - - return ret; -} - -int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) -{ - unsigned int copied_cnt; - unsigned int bcnt; - const char *src; - int stat; - int ret = 0; - unsigned int req_cnt = cnt; - - if (!cnt) { - pvr2_trace(PVR2_TRACE_TRAP, - "/*---TRACE_READ---*/ pvr2_ioread_read id=%p" - " ZERO Request? Returning zero.",cp); - return 0; - } - - stat = pvr2_ioread_avail(cp); - if (stat < 0) return stat; - - cp->stream_running = !0; - - mutex_lock(&cp->mutex); do { - - // Suck data out of the buffers and copy to the user - copied_cnt = 0; - if (!buf) cnt = 0; - while (1) { - if (!pvr2_ioread_get_buffer(cp)) { - ret = -EIO; - break; - } - - if (!cnt) break; - - if (cp->sync_state == 2) { - // We're repeating the sync key data into - // the stream. - src = cp->sync_key_ptr + cp->sync_buf_offs; - bcnt = cp->sync_key_len - cp->sync_buf_offs; - } else { - // Normal buffer copy - src = cp->c_data_ptr + cp->c_data_offs; - bcnt = cp->c_data_len - cp->c_data_offs; - } - - if (!bcnt) break; - - // Don't run past user's buffer - if (bcnt > cnt) bcnt = cnt; - - if (copy_to_user(buf,src,bcnt)) { - // User supplied a bad pointer? - // Give up - this *will* cause data - // to be lost. - ret = -EFAULT; - break; - } - cnt -= bcnt; - buf += bcnt; - copied_cnt += bcnt; - - if (cp->sync_state == 2) { - // Update offset inside sync key that we're - // repeating back out. - cp->sync_buf_offs += bcnt; - if (cp->sync_buf_offs >= cp->sync_key_len) { - // Consumed entire key; switch mode - // to normal. - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/" - " sync_state <== 0"); - cp->sync_state = 0; - } - } else { - // Update buffer offset. - cp->c_data_offs += bcnt; - } - } - - } while (0); mutex_unlock(&cp->mutex); - - if (!ret) { - if (copied_cnt) { - // If anything was copied, return that count - ret = copied_cnt; - } else { - // Nothing copied; suggest to caller that another - // attempt should be tried again later - ret = -EAGAIN; - } - } - - pvr2_trace(PVR2_TRACE_DATA_FLOW, - "/*---TRACE_READ---*/ pvr2_ioread_read" - " id=%p request=%d result=%d", - cp,req_cnt,ret); - return ret; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/drivers/media/video/pvrusb2/pvrusb2-ioread.h deleted file mode 100644 index 100e0780e1aa..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_IOREAD_H -#define __PVRUSB2_IOREAD_H - -#include "pvrusb2-io.h" - -struct pvr2_ioread; - -struct pvr2_ioread *pvr2_ioread_create(void); -void pvr2_ioread_destroy(struct pvr2_ioread *); -int pvr2_ioread_setup(struct pvr2_ioread *,struct pvr2_stream *); -struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *); -void pvr2_ioread_set_sync_key(struct pvr2_ioread *, - const char *sync_key_ptr, - unsigned int sync_key_len); -int pvr2_ioread_set_enabled(struct pvr2_ioread *,int fl); -int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt); -int pvr2_ioread_avail(struct pvr2_ioread *); - -#endif /* __PVRUSB2_IOREAD_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c deleted file mode 100644 index c1d9bb61cd77..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-main.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include - -#include "pvrusb2-hdw.h" -#include "pvrusb2-devattr.h" -#include "pvrusb2-context.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-v4l2.h" -#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS -#include "pvrusb2-sysfs.h" -#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ - -#define DRIVER_AUTHOR "Mike Isely " -#define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner" -#define DRIVER_VERSION "V4L in-tree version" - -#define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \ - PVR2_TRACE_INFO| \ - PVR2_TRACE_STD| \ - PVR2_TRACE_TOLERANCE| \ - PVR2_TRACE_TRAP| \ - 0) - -int pvrusb2_debug = DEFAULT_DEBUG_MASK; - -module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(debug, "Debug trace mask"); - -#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS -static struct pvr2_sysfs_class *class_ptr = NULL; -#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ - -static void pvr_setup_attach(struct pvr2_context *pvr) -{ - /* Create association with v4l layer */ - pvr2_v4l2_create(pvr); -#ifdef CONFIG_VIDEO_PVRUSB2_DVB - /* Create association with dvb layer */ - pvr2_dvb_create(pvr); -#endif -#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS - pvr2_sysfs_create(pvr,class_ptr); -#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ -} - -static int pvr_probe(struct usb_interface *intf, - const struct usb_device_id *devid) -{ - struct pvr2_context *pvr; - - /* Create underlying hardware interface */ - pvr = pvr2_context_create(intf,devid,pvr_setup_attach); - if (!pvr) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to create hdw handler"); - return -ENOMEM; - } - - pvr2_trace(PVR2_TRACE_INIT,"pvr_probe(pvr=%p)",pvr); - - usb_set_intfdata(intf, pvr); - - return 0; -} - -/* - * pvr_disconnect() - * - */ -static void pvr_disconnect(struct usb_interface *intf) -{ - struct pvr2_context *pvr = usb_get_intfdata(intf); - - pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) BEGIN",pvr); - - usb_set_intfdata (intf, NULL); - pvr2_context_disconnect(pvr); - - pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) DONE",pvr); - -} - -static struct usb_driver pvr_driver = { - .name = "pvrusb2", - .id_table = pvr2_device_table, - .probe = pvr_probe, - .disconnect = pvr_disconnect -}; - -/* - * pvr_init() / pvr_exit() - * - * This code is run to initialize/exit the driver. - * - */ -static int __init pvr_init(void) -{ - int ret; - - pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); - - ret = pvr2_context_global_init(); - if (ret != 0) { - pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret); - return ret; - } - -#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS - class_ptr = pvr2_sysfs_class_create(); -#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ - - ret = usb_register(&pvr_driver); - - if (ret == 0) - printk(KERN_INFO "pvrusb2: " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - if (pvrusb2_debug) - printk(KERN_INFO "pvrusb2: Debug mask is %d (0x%x)\n", - pvrusb2_debug,pvrusb2_debug); - - pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete"); - - return ret; -} - -static void __exit pvr_exit(void) -{ - pvr2_trace(PVR2_TRACE_INIT,"pvr_exit"); - - usb_deregister(&pvr_driver); - - pvr2_context_global_done(); - -#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS - pvr2_sysfs_class_destroy(class_ptr); -#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ - - pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete"); -} - -module_init(pvr_init); -module_exit(pvr_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.9.1"); - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c deleted file mode 100644 index 453627b07833..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-std.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-std.h" -#include "pvrusb2-debug.h" -#include -#include - -struct std_name { - const char *name; - v4l2_std_id id; -}; - - -#define CSTD_PAL \ - (V4L2_STD_PAL_B| \ - V4L2_STD_PAL_B1| \ - V4L2_STD_PAL_G| \ - V4L2_STD_PAL_H| \ - V4L2_STD_PAL_I| \ - V4L2_STD_PAL_D| \ - V4L2_STD_PAL_D1| \ - V4L2_STD_PAL_K| \ - V4L2_STD_PAL_M| \ - V4L2_STD_PAL_N| \ - V4L2_STD_PAL_Nc| \ - V4L2_STD_PAL_60) - -#define CSTD_NTSC \ - (V4L2_STD_NTSC_M| \ - V4L2_STD_NTSC_M_JP| \ - V4L2_STD_NTSC_M_KR| \ - V4L2_STD_NTSC_443) - -#define CSTD_ATSC \ - (V4L2_STD_ATSC_8_VSB| \ - V4L2_STD_ATSC_16_VSB) - -#define CSTD_SECAM \ - (V4L2_STD_SECAM_B| \ - V4L2_STD_SECAM_D| \ - V4L2_STD_SECAM_G| \ - V4L2_STD_SECAM_H| \ - V4L2_STD_SECAM_K| \ - V4L2_STD_SECAM_K1| \ - V4L2_STD_SECAM_L| \ - V4L2_STD_SECAM_LC) - -#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B) -#define TSTD_B1 (V4L2_STD_PAL_B1) -#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D) -#define TSTD_D1 (V4L2_STD_PAL_D1) -#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G) -#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H) -#define TSTD_I (V4L2_STD_PAL_I) -#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K) -#define TSTD_K1 (V4L2_STD_SECAM_K1) -#define TSTD_L (V4L2_STD_SECAM_L) -#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M) -#define TSTD_N (V4L2_STD_PAL_N) -#define TSTD_Nc (V4L2_STD_PAL_Nc) -#define TSTD_60 (V4L2_STD_PAL_60) - -#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_ATSC|CSTD_SECAM) - -/* Mapping of standard bits to color system */ -static const struct std_name std_groups[] = { - {"PAL",CSTD_PAL}, - {"NTSC",CSTD_NTSC}, - {"SECAM",CSTD_SECAM}, - {"ATSC",CSTD_ATSC}, -}; - -/* Mapping of standard bits to modulation system */ -static const struct std_name std_items[] = { - {"B",TSTD_B}, - {"B1",TSTD_B1}, - {"D",TSTD_D}, - {"D1",TSTD_D1}, - {"G",TSTD_G}, - {"H",TSTD_H}, - {"I",TSTD_I}, - {"K",TSTD_K}, - {"K1",TSTD_K1}, - {"L",TSTD_L}, - {"LC",V4L2_STD_SECAM_LC}, - {"M",TSTD_M}, - {"Mj",V4L2_STD_NTSC_M_JP}, - {"443",V4L2_STD_NTSC_443}, - {"Mk",V4L2_STD_NTSC_M_KR}, - {"N",TSTD_N}, - {"Nc",TSTD_Nc}, - {"60",TSTD_60}, - {"8VSB",V4L2_STD_ATSC_8_VSB}, - {"16VSB",V4L2_STD_ATSC_16_VSB}, -}; - - -// Search an array of std_name structures and return a pointer to the -// element with the matching name. -static const struct std_name *find_std_name(const struct std_name *arrPtr, - unsigned int arrSize, - const char *bufPtr, - unsigned int bufSize) -{ - unsigned int idx; - const struct std_name *p; - for (idx = 0; idx < arrSize; idx++) { - p = arrPtr + idx; - if (strlen(p->name) != bufSize) continue; - if (!memcmp(bufPtr,p->name,bufSize)) return p; - } - return NULL; -} - - -int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, - unsigned int bufSize) -{ - v4l2_std_id id = 0; - v4l2_std_id cmsk = 0; - v4l2_std_id t; - int mMode = 0; - unsigned int cnt; - char ch; - const struct std_name *sp; - - while (bufSize) { - if (!mMode) { - cnt = 0; - while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; - if (cnt >= bufSize) return 0; // No more characters - sp = find_std_name(std_groups, ARRAY_SIZE(std_groups), - bufPtr,cnt); - if (!sp) return 0; // Illegal color system name - cnt++; - bufPtr += cnt; - bufSize -= cnt; - mMode = !0; - cmsk = sp->id; - continue; - } - cnt = 0; - while (cnt < bufSize) { - ch = bufPtr[cnt]; - if (ch == ';') { - mMode = 0; - break; - } - if (ch == '/') break; - cnt++; - } - sp = find_std_name(std_items, ARRAY_SIZE(std_items), - bufPtr,cnt); - if (!sp) return 0; // Illegal modulation system ID - t = sp->id & cmsk; - if (!t) return 0; // Specific color + modulation system illegal - id |= t; - if (cnt < bufSize) cnt++; - bufPtr += cnt; - bufSize -= cnt; - } - - if (idPtr) *idPtr = id; - return !0; -} - - -unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, - v4l2_std_id id) -{ - unsigned int idx1,idx2; - const struct std_name *ip,*gp; - int gfl,cfl; - unsigned int c1,c2; - cfl = 0; - c1 = 0; - for (idx1 = 0; idx1 < ARRAY_SIZE(std_groups); idx1++) { - gp = std_groups + idx1; - gfl = 0; - for (idx2 = 0; idx2 < ARRAY_SIZE(std_items); idx2++) { - ip = std_items + idx2; - if (!(gp->id & ip->id & id)) continue; - if (!gfl) { - if (cfl) { - c2 = scnprintf(bufPtr,bufSize,";"); - c1 += c2; - bufSize -= c2; - bufPtr += c2; - } - cfl = !0; - c2 = scnprintf(bufPtr,bufSize, - "%s-",gp->name); - gfl = !0; - } else { - c2 = scnprintf(bufPtr,bufSize,"/"); - } - c1 += c2; - bufSize -= c2; - bufPtr += c2; - c2 = scnprintf(bufPtr,bufSize, - ip->name); - c1 += c2; - bufSize -= c2; - bufPtr += c2; - } - } - return c1; -} - - -// Template data for possible enumerated video standards. Here we group -// standards which share common frame rates and resolution. -static struct v4l2_standard generic_standards[] = { - { - .id = (TSTD_B|TSTD_B1| - TSTD_D|TSTD_D1| - TSTD_G| - TSTD_H| - TSTD_I| - TSTD_K|TSTD_K1| - TSTD_L| - V4L2_STD_SECAM_LC | - TSTD_N|TSTD_Nc), - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, { - .id = (TSTD_M| - V4L2_STD_NTSC_M_JP| - V4L2_STD_NTSC_M_KR), - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, { // This is a total wild guess - .id = (TSTD_60), - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, { // This is total wild guess - .id = V4L2_STD_NTSC_443, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - } -}; - -static struct v4l2_standard *match_std(v4l2_std_id id) -{ - unsigned int idx; - for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) { - if (generic_standards[idx].id & id) { - return generic_standards + idx; - } - } - return NULL; -} - -static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) -{ - struct v4l2_standard *template; - int idx; - unsigned int bcnt; - template = match_std(id); - if (!template) return 0; - idx = std->index; - memcpy(std,template,sizeof(*template)); - std->index = idx; - std->id = id; - bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); - std->name[bcnt] = 0; - pvr2_trace(PVR2_TRACE_STD,"Set up standard idx=%u name=%s", - std->index,std->name); - return !0; -} - -/* These are special cases of combined standards that we should enumerate - separately if the component pieces are present. */ -static v4l2_std_id std_mixes[] = { - V4L2_STD_PAL_B | V4L2_STD_PAL_G, - V4L2_STD_PAL_D | V4L2_STD_PAL_K, - V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, - V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, -}; - -struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, - v4l2_std_id id) -{ - unsigned int std_cnt = 0; - unsigned int idx,bcnt,idx2; - v4l2_std_id idmsk,cmsk,fmsk; - struct v4l2_standard *stddefs; - - if (pvrusb2_debug & PVR2_TRACE_STD) { - char buf[100]; - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); - pvr2_trace( - PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)", - (int)id,bcnt,buf); - } - - *countptr = 0; - std_cnt = 0; - fmsk = 0; - for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { - if (!(idmsk & cmsk)) continue; - cmsk &= ~idmsk; - if (match_std(idmsk)) { - std_cnt++; - continue; - } - fmsk |= idmsk; - } - - for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) { - if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; - } - - /* Don't complain about ATSC standard values */ - fmsk &= ~CSTD_ATSC; - - if (fmsk) { - char buf[100]; - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "WARNING:" - " Failed to classify the following standard(s): %.*s", - bcnt,buf); - } - - pvr2_trace(PVR2_TRACE_STD,"Setting up %u unique standard(s)", - std_cnt); - if (!std_cnt) return NULL; // paranoia - - stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt, - GFP_KERNEL); - if (!stddefs) - return NULL; - - for (idx = 0; idx < std_cnt; idx++) - stddefs[idx].index = idx; - - idx = 0; - - /* Enumerate potential special cases */ - for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt); - idx2++) { - if (!(id & std_mixes[idx2])) continue; - if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; - } - /* Now enumerate individual pieces */ - for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { - if (!(idmsk & cmsk)) continue; - cmsk &= ~idmsk; - if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; - idx++; - } - - *countptr = std_cnt; - return stddefs; -} - -v4l2_std_id pvr2_std_get_usable(void) -{ - return CSTD_ALL; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.h b/drivers/media/video/pvrusb2/pvrusb2-std.h deleted file mode 100644 index a35c53d0b320..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-std.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_STD_H -#define __PVRUSB2_STD_H - -#include - -// Convert string describing one or more video standards into a mask of V4L -// standard bits. Return true if conversion succeeds otherwise return -// false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and -// C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are -// modulation schemes (e.g. "M", "B", "G", etc). -int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, - unsigned int bufSize); - -// Convert any arbitrary set of video standard bits into an unambiguous -// readable string. Return value is the number of bytes consumed in the -// buffer. The formatted string is of a form that can be parsed by our -// sibling std_std_to_id() function. -unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, - v4l2_std_id id); - -// Create an array of suitable v4l2_standard structures given a bit mask of -// video standards to support. The array is allocated from the heap, and -// the number of elements is returned in the first argument. -struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, - v4l2_std_id id); - -// Return mask of which video standard bits are valid -v4l2_std_id pvr2_std_get_usable(void); - -#endif /* __PVRUSB2_STD_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c deleted file mode 100644 index 6ef1335b2858..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ /dev/null @@ -1,861 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include "pvrusb2-sysfs.h" -#include "pvrusb2-hdw.h" -#include "pvrusb2-debug.h" -#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC -#include "pvrusb2-debugifc.h" -#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - -#define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) - -struct pvr2_sysfs { - struct pvr2_channel channel; - struct device *class_dev; -#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC - struct pvr2_sysfs_debugifc *debugifc; -#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - struct pvr2_sysfs_ctl_item *item_first; - struct pvr2_sysfs_ctl_item *item_last; - struct device_attribute attr_v4l_minor_number; - struct device_attribute attr_v4l_radio_minor_number; - struct device_attribute attr_unit_number; - struct device_attribute attr_bus_info; - struct device_attribute attr_hdw_name; - struct device_attribute attr_hdw_desc; - int v4l_minor_number_created_ok; - int v4l_radio_minor_number_created_ok; - int unit_number_created_ok; - int bus_info_created_ok; - int hdw_name_created_ok; - int hdw_desc_created_ok; -}; - -#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC -struct pvr2_sysfs_debugifc { - struct device_attribute attr_debugcmd; - struct device_attribute attr_debuginfo; - int debugcmd_created_ok; - int debuginfo_created_ok; -}; -#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - -struct pvr2_sysfs_ctl_item { - struct device_attribute attr_name; - struct device_attribute attr_type; - struct device_attribute attr_min; - struct device_attribute attr_max; - struct device_attribute attr_def; - struct device_attribute attr_enum; - struct device_attribute attr_bits; - struct device_attribute attr_val; - struct device_attribute attr_custom; - struct pvr2_ctrl *cptr; - int ctl_id; - struct pvr2_sysfs *chptr; - struct pvr2_sysfs_ctl_item *item_next; - struct attribute *attr_gen[8]; - struct attribute_group grp; - int created_ok; - char name[80]; -}; - -struct pvr2_sysfs_class { - struct class class; -}; - -static ssize_t show_name(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - const char *name; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name); - name = pvr2_ctrl_get_desc(cip->cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s", - cip->chptr, cip->ctl_id, name); - if (!name) return -EINVAL; - return scnprintf(buf, PAGE_SIZE, "%s\n", name); -} - -static ssize_t show_type(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - const char *name; - enum pvr2_ctl_type tp; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type); - tp = pvr2_ctrl_get_type(cip->cptr); - switch (tp) { - case pvr2_ctl_int: name = "integer"; break; - case pvr2_ctl_enum: name = "enum"; break; - case pvr2_ctl_bitmask: name = "bitmask"; break; - case pvr2_ctl_bool: name = "boolean"; break; - default: name = "?"; break; - } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s", - cip->chptr, cip->ctl_id, name); - if (!name) return -EINVAL; - return scnprintf(buf, PAGE_SIZE, "%s\n", name); -} - -static ssize_t show_min(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - long val; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min); - val = pvr2_ctrl_get_min(cip->cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld", - cip->chptr, cip->ctl_id, val); - return scnprintf(buf, PAGE_SIZE, "%ld\n", val); -} - -static ssize_t show_max(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - long val; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max); - val = pvr2_ctrl_get_max(cip->cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld", - cip->chptr, cip->ctl_id, val); - return scnprintf(buf, PAGE_SIZE, "%ld\n", val); -} - -static ssize_t show_def(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - int val; - int ret; - unsigned int cnt = 0; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def); - ret = pvr2_ctrl_get_def(cip->cptr, &val); - if (ret < 0) return ret; - ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, - buf, PAGE_SIZE - 1, &cnt); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %.*s (%d)", - cip->chptr, cip->ctl_id, cnt, buf, val); - buf[cnt] = '\n'; - return cnt + 1; -} - -static ssize_t show_val_norm(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - int val; - int ret; - unsigned int cnt = 0; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); - ret = pvr2_ctrl_get_value(cip->cptr, &val); - if (ret < 0) return ret; - ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, - buf, PAGE_SIZE - 1, &cnt); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", - cip->chptr, cip->ctl_id, cnt, buf, val); - buf[cnt] = '\n'; - return cnt+1; -} - -static ssize_t show_val_custom(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - int val; - int ret; - unsigned int cnt = 0; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); - ret = pvr2_ctrl_get_value(cip->cptr, &val); - if (ret < 0) return ret; - ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val, - buf, PAGE_SIZE - 1, &cnt); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", - cip->chptr, cip->ctl_id, cnt, buf, val); - buf[cnt] = '\n'; - return cnt+1; -} - -static ssize_t show_enum(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - long val; - unsigned int bcnt, ccnt, ecnt; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum); - ecnt = pvr2_ctrl_get_cnt(cip->cptr); - bcnt = 0; - for (val = 0; val < ecnt; val++) { - pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt, - PAGE_SIZE - bcnt, &ccnt); - if (!ccnt) continue; - bcnt += ccnt; - if (bcnt >= PAGE_SIZE) break; - buf[bcnt] = '\n'; - bcnt++; - } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)", - cip->chptr, cip->ctl_id); - return bcnt; -} - -static ssize_t show_bits(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs_ctl_item *cip; - int valid_bits, msk; - unsigned int bcnt, ccnt; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits); - valid_bits = pvr2_ctrl_get_mask(cip->cptr); - bcnt = 0; - for (msk = 1; valid_bits; msk <<= 1) { - if (!(msk & valid_bits)) continue; - valid_bits &= ~msk; - pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt, - PAGE_SIZE - bcnt, &ccnt); - bcnt += ccnt; - if (bcnt >= PAGE_SIZE) break; - buf[bcnt] = '\n'; - bcnt++; - } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)", - cip->chptr, cip->ctl_id); - return bcnt; -} - -static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl, - const char *buf,unsigned int count) -{ - int ret; - int mask,val; - if (customfl) { - ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count, - &mask, &val); - } else { - ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count, - &mask, &val); - } - if (ret < 0) return ret; - ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val); - pvr2_hdw_commit_ctl(cip->chptr->channel.hdw); - return ret; -} - -static ssize_t store_val_norm(struct device *class_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct pvr2_sysfs_ctl_item *cip; - int ret; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); - pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"", - cip->chptr, cip->ctl_id, (int)count, buf); - ret = store_val_any(cip, 0, buf, count); - if (!ret) ret = count; - return ret; -} - -static ssize_t store_val_custom(struct device *class_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct pvr2_sysfs_ctl_item *cip; - int ret; - cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); - pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"", - cip->chptr, cip->ctl_id, (int)count, buf); - ret = store_val_any(cip, 1, buf, count); - if (!ret) ret = count; - return ret; -} - -static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) -{ - struct pvr2_sysfs_ctl_item *cip; - struct pvr2_ctrl *cptr; - unsigned int cnt,acnt; - int ret; - - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); - if (!cptr) return; - - cip = kzalloc(sizeof(*cip),GFP_KERNEL); - if (!cip) return; - pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); - - cip->cptr = cptr; - cip->ctl_id = ctl_id; - - cip->chptr = sfp; - cip->item_next = NULL; - if (sfp->item_last) { - sfp->item_last->item_next = cip; - } else { - sfp->item_first = cip; - } - sfp->item_last = cip; - - sysfs_attr_init(&cip->attr_name.attr); - cip->attr_name.attr.name = "name"; - cip->attr_name.attr.mode = S_IRUGO; - cip->attr_name.show = show_name; - - sysfs_attr_init(&cip->attr_type.attr); - cip->attr_type.attr.name = "type"; - cip->attr_type.attr.mode = S_IRUGO; - cip->attr_type.show = show_type; - - sysfs_attr_init(&cip->attr_min.attr); - cip->attr_min.attr.name = "min_val"; - cip->attr_min.attr.mode = S_IRUGO; - cip->attr_min.show = show_min; - - sysfs_attr_init(&cip->attr_max.attr); - cip->attr_max.attr.name = "max_val"; - cip->attr_max.attr.mode = S_IRUGO; - cip->attr_max.show = show_max; - - sysfs_attr_init(&cip->attr_def.attr); - cip->attr_def.attr.name = "def_val"; - cip->attr_def.attr.mode = S_IRUGO; - cip->attr_def.show = show_def; - - sysfs_attr_init(&cip->attr_val.attr); - cip->attr_val.attr.name = "cur_val"; - cip->attr_val.attr.mode = S_IRUGO; - - sysfs_attr_init(&cip->attr_custom.attr); - cip->attr_custom.attr.name = "custom_val"; - cip->attr_custom.attr.mode = S_IRUGO; - - sysfs_attr_init(&cip->attr_enum.attr); - cip->attr_enum.attr.name = "enum_val"; - cip->attr_enum.attr.mode = S_IRUGO; - cip->attr_enum.show = show_enum; - - sysfs_attr_init(&cip->attr_bits.attr); - cip->attr_bits.attr.name = "bit_val"; - cip->attr_bits.attr.mode = S_IRUGO; - cip->attr_bits.show = show_bits; - - if (pvr2_ctrl_is_writable(cptr)) { - cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; - cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; - } - - acnt = 0; - cip->attr_gen[acnt++] = &cip->attr_name.attr; - cip->attr_gen[acnt++] = &cip->attr_type.attr; - cip->attr_gen[acnt++] = &cip->attr_val.attr; - cip->attr_gen[acnt++] = &cip->attr_def.attr; - cip->attr_val.show = show_val_norm; - cip->attr_val.store = store_val_norm; - if (pvr2_ctrl_has_custom_symbols(cptr)) { - cip->attr_gen[acnt++] = &cip->attr_custom.attr; - cip->attr_custom.show = show_val_custom; - cip->attr_custom.store = store_val_custom; - } - switch (pvr2_ctrl_get_type(cptr)) { - case pvr2_ctl_enum: - // Control is an enumeration - cip->attr_gen[acnt++] = &cip->attr_enum.attr; - break; - case pvr2_ctl_int: - // Control is an integer - cip->attr_gen[acnt++] = &cip->attr_min.attr; - cip->attr_gen[acnt++] = &cip->attr_max.attr; - break; - case pvr2_ctl_bitmask: - // Control is an bitmask - cip->attr_gen[acnt++] = &cip->attr_bits.attr; - break; - default: break; - } - - cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", - pvr2_ctrl_get_name(cptr)); - cip->name[cnt] = 0; - cip->grp.name = cip->name; - cip->grp.attrs = cip->attr_gen; - - ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "sysfs_create_group error: %d", - ret); - return; - } - cip->created_ok = !0; -} - -#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC -static ssize_t debuginfo_show(struct device *, struct device_attribute *, - char *); -static ssize_t debugcmd_show(struct device *, struct device_attribute *, - char *); -static ssize_t debugcmd_store(struct device *, struct device_attribute *, - const char *, size_t count); - -static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) -{ - struct pvr2_sysfs_debugifc *dip; - int ret; - - dip = kzalloc(sizeof(*dip),GFP_KERNEL); - if (!dip) return; - sysfs_attr_init(&dip->attr_debugcmd.attr); - dip->attr_debugcmd.attr.name = "debugcmd"; - dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; - dip->attr_debugcmd.show = debugcmd_show; - dip->attr_debugcmd.store = debugcmd_store; - sysfs_attr_init(&dip->attr_debuginfo.attr); - dip->attr_debuginfo.attr.name = "debuginfo"; - dip->attr_debuginfo.attr.mode = S_IRUGO; - dip->attr_debuginfo.show = debuginfo_show; - sfp->debugifc = dip; - ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - dip->debugcmd_created_ok = !0; - } - ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - dip->debuginfo_created_ok = !0; - } -} - - -static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) -{ - if (!sfp->debugifc) return; - if (sfp->debugifc->debuginfo_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->debugifc->attr_debuginfo); - } - if (sfp->debugifc->debugcmd_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->debugifc->attr_debugcmd); - } - kfree(sfp->debugifc); - sfp->debugifc = NULL; -} -#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - - -static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) -{ - unsigned int idx,cnt; - cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); - for (idx = 0; idx < cnt; idx++) { - pvr2_sysfs_add_control(sfp,idx); - } -} - - -static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp) -{ - struct pvr2_sysfs_ctl_item *cip1,*cip2; - for (cip1 = sfp->item_first; cip1; cip1 = cip2) { - cip2 = cip1->item_next; - if (cip1->created_ok) { - sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp); - } - pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1); - kfree(cip1); - } -} - - -static void pvr2_sysfs_class_release(struct class *class) -{ - struct pvr2_sysfs_class *clp; - clp = container_of(class,struct pvr2_sysfs_class,class); - pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp); - kfree(clp); -} - - -static void pvr2_sysfs_release(struct device *class_dev) -{ - pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev); - kfree(class_dev); -} - - -static void class_dev_destroy(struct pvr2_sysfs *sfp) -{ - struct device *dev; - if (!sfp->class_dev) return; -#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC - pvr2_sysfs_tear_down_debugifc(sfp); -#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - pvr2_sysfs_tear_down_controls(sfp); - if (sfp->hdw_desc_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->attr_hdw_desc); - } - if (sfp->hdw_name_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->attr_hdw_name); - } - if (sfp->bus_info_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->attr_bus_info); - } - if (sfp->v4l_minor_number_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->attr_v4l_minor_number); - } - if (sfp->v4l_radio_minor_number_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->attr_v4l_radio_minor_number); - } - if (sfp->unit_number_created_ok) { - device_remove_file(sfp->class_dev, - &sfp->attr_unit_number); - } - pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev); - dev_set_drvdata(sfp->class_dev, NULL); - dev = sfp->class_dev->parent; - sfp->class_dev->parent = NULL; - put_device(dev); - device_unregister(sfp->class_dev); - sfp->class_dev = NULL; -} - - -static ssize_t v4l_minor_number_show(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%d\n", - pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, - pvr2_v4l_type_video)); -} - - -static ssize_t bus_info_show(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%s\n", - pvr2_hdw_get_bus_info(sfp->channel.hdw)); -} - - -static ssize_t hdw_name_show(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%s\n", - pvr2_hdw_get_type(sfp->channel.hdw)); -} - - -static ssize_t hdw_desc_show(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%s\n", - pvr2_hdw_get_desc(sfp->channel.hdw)); -} - - -static ssize_t v4l_radio_minor_number_show(struct device *class_dev, - struct device_attribute *attr, - char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%d\n", - pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, - pvr2_v4l_type_radio)); -} - - -static ssize_t unit_number_show(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%d\n", - pvr2_hdw_get_unit_number(sfp->channel.hdw)); -} - - -static void class_dev_create(struct pvr2_sysfs *sfp, - struct pvr2_sysfs_class *class_ptr) -{ - struct usb_device *usb_dev; - struct device *class_dev; - int ret; - - usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); - if (!usb_dev) return; - class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL); - if (!class_dev) return; - - pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); - - class_dev->class = &class_ptr->class; - - dev_set_name(class_dev, "%s", - pvr2_hdw_get_device_identifier(sfp->channel.hdw)); - - class_dev->parent = get_device(&usb_dev->dev); - - sfp->class_dev = class_dev; - dev_set_drvdata(class_dev, sfp); - ret = device_register(class_dev); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_register failed"); - put_device(class_dev); - return; - } - - sysfs_attr_init(&sfp->attr_v4l_minor_number.attr); - sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number"; - sfp->attr_v4l_minor_number.attr.mode = S_IRUGO; - sfp->attr_v4l_minor_number.show = v4l_minor_number_show; - sfp->attr_v4l_minor_number.store = NULL; - ret = device_create_file(sfp->class_dev, - &sfp->attr_v4l_minor_number); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - sfp->v4l_minor_number_created_ok = !0; - } - - sysfs_attr_init(&sfp->attr_v4l_radio_minor_number.attr); - sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number"; - sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO; - sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show; - sfp->attr_v4l_radio_minor_number.store = NULL; - ret = device_create_file(sfp->class_dev, - &sfp->attr_v4l_radio_minor_number); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - sfp->v4l_radio_minor_number_created_ok = !0; - } - - sysfs_attr_init(&sfp->attr_unit_number.attr); - sfp->attr_unit_number.attr.name = "unit_number"; - sfp->attr_unit_number.attr.mode = S_IRUGO; - sfp->attr_unit_number.show = unit_number_show; - sfp->attr_unit_number.store = NULL; - ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - sfp->unit_number_created_ok = !0; - } - - sysfs_attr_init(&sfp->attr_bus_info.attr); - sfp->attr_bus_info.attr.name = "bus_info_str"; - sfp->attr_bus_info.attr.mode = S_IRUGO; - sfp->attr_bus_info.show = bus_info_show; - sfp->attr_bus_info.store = NULL; - ret = device_create_file(sfp->class_dev, - &sfp->attr_bus_info); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - sfp->bus_info_created_ok = !0; - } - - sysfs_attr_init(&sfp->attr_hdw_name.attr); - sfp->attr_hdw_name.attr.name = "device_hardware_type"; - sfp->attr_hdw_name.attr.mode = S_IRUGO; - sfp->attr_hdw_name.show = hdw_name_show; - sfp->attr_hdw_name.store = NULL; - ret = device_create_file(sfp->class_dev, - &sfp->attr_hdw_name); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - sfp->hdw_name_created_ok = !0; - } - - sysfs_attr_init(&sfp->attr_hdw_desc.attr); - sfp->attr_hdw_desc.attr.name = "device_hardware_description"; - sfp->attr_hdw_desc.attr.mode = S_IRUGO; - sfp->attr_hdw_desc.show = hdw_desc_show; - sfp->attr_hdw_desc.store = NULL; - ret = device_create_file(sfp->class_dev, - &sfp->attr_hdw_desc); - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "device_create_file error: %d", - ret); - } else { - sfp->hdw_desc_created_ok = !0; - } - - pvr2_sysfs_add_controls(sfp); -#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC - pvr2_sysfs_add_debugifc(sfp); -#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ -} - - -static void pvr2_sysfs_internal_check(struct pvr2_channel *chp) -{ - struct pvr2_sysfs *sfp; - sfp = container_of(chp,struct pvr2_sysfs,channel); - if (!sfp->channel.mc_head->disconnect_flag) return; - pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp); - class_dev_destroy(sfp); - pvr2_channel_done(&sfp->channel); - kfree(sfp); -} - - -struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp, - struct pvr2_sysfs_class *class_ptr) -{ - struct pvr2_sysfs *sfp; - sfp = kzalloc(sizeof(*sfp),GFP_KERNEL); - if (!sfp) return sfp; - pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp); - pvr2_channel_init(&sfp->channel,mp); - sfp->channel.check_func = pvr2_sysfs_internal_check; - - class_dev_create(sfp,class_ptr); - return sfp; -} - - - -struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) -{ - struct pvr2_sysfs_class *clp; - clp = kzalloc(sizeof(*clp),GFP_KERNEL); - if (!clp) return clp; - pvr2_sysfs_trace("Creating and registering pvr2_sysfs_class id=%p", - clp); - clp->class.name = "pvrusb2"; - clp->class.class_release = pvr2_sysfs_class_release; - clp->class.dev_release = pvr2_sysfs_release; - if (class_register(&clp->class)) { - pvr2_sysfs_trace( - "Registration failed for pvr2_sysfs_class id=%p",clp); - kfree(clp); - clp = NULL; - } - return clp; -} - - -void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) -{ - pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp); - class_unregister(&clp->class); -} - - -#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC -static ssize_t debuginfo_show(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - pvr2_hdw_trigger_module_log(sfp->channel.hdw); - return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE); -} - - -static ssize_t debugcmd_show(struct device *class_dev, - struct device_attribute *attr, char *buf) -{ - struct pvr2_sysfs *sfp; - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE); -} - - -static ssize_t debugcmd_store(struct device *class_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct pvr2_sysfs *sfp; - int ret; - - sfp = dev_get_drvdata(class_dev); - if (!sfp) return -EINVAL; - - ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count); - if (ret < 0) return ret; - return count; -} -#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h deleted file mode 100644 index 6d875bfe7991..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_SYSFS_H -#define __PVRUSB2_SYSFS_H - -#include -#include -#include "pvrusb2-context.h" - -struct pvr2_sysfs; -struct pvr2_sysfs_class; - -struct pvr2_sysfs_class *pvr2_sysfs_class_create(void); -void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *); - -struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *, - struct pvr2_sysfs_class *); - -#endif /* __PVRUSB2_SYSFS_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-util.h b/drivers/media/video/pvrusb2/pvrusb2-util.h deleted file mode 100644 index 92b75544ee2e..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-util.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_UTIL_H -#define __PVRUSB2_UTIL_H - -#define PVR2_DECOMPOSE_LE(t,i,d) \ - do { \ - (t)[i] = (d) & 0xff;\ - (t)[i+1] = ((d) >> 8) & 0xff;\ - (t)[i+2] = ((d) >> 16) & 0xff;\ - (t)[i+3] = ((d) >> 24) & 0xff;\ - } while(0) - -#define PVR2_DECOMPOSE_BE(t,i,d) \ - do { \ - (t)[i+3] = (d) & 0xff;\ - (t)[i+2] = ((d) >> 8) & 0xff;\ - (t)[i+1] = ((d) >> 16) & 0xff;\ - (t)[i] = ((d) >> 24) & 0xff;\ - } while(0) - -#define PVR2_COMPOSE_LE(t,i) \ - ((((u32)((t)[i+3])) << 24) | \ - (((u32)((t)[i+2])) << 16) | \ - (((u32)((t)[i+1])) << 8) | \ - ((u32)((t)[i]))) - -#define PVR2_COMPOSE_BE(t,i) \ - ((((u32)((t)[i])) << 24) | \ - (((u32)((t)[i+1])) << 16) | \ - (((u32)((t)[i+2])) << 8) | \ - ((u32)((t)[i+3]))) - - -#endif /* __PVRUSB2_UTIL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c deleted file mode 100644 index f344aed32a93..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ /dev/null @@ -1,1413 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include "pvrusb2-context.h" -#include "pvrusb2-hdw.h" -#include "pvrusb2.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-v4l2.h" -#include "pvrusb2-ioread.h" -#include -#include -#include -#include -#include - -struct pvr2_v4l2_dev; -struct pvr2_v4l2_fh; -struct pvr2_v4l2; - -struct pvr2_v4l2_dev { - struct video_device devbase; /* MUST be first! */ - struct pvr2_v4l2 *v4lp; - struct pvr2_context_stream *stream; - /* Information about this device: */ - enum pvr2_config config; /* Expected stream format */ - int v4l_type; /* V4L defined type for this device node */ - enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */ -}; - -struct pvr2_v4l2_fh { - struct pvr2_channel channel; - struct pvr2_v4l2_dev *pdi; - enum v4l2_priority prio; - struct pvr2_ioread *rhp; - struct file *file; - struct pvr2_v4l2 *vhead; - struct pvr2_v4l2_fh *vnext; - struct pvr2_v4l2_fh *vprev; - wait_queue_head_t wait_data; - int fw_mode_flag; - /* Map contiguous ordinal value to input id */ - unsigned char *input_map; - unsigned int input_cnt; -}; - -struct pvr2_v4l2 { - struct pvr2_channel channel; - struct pvr2_v4l2_fh *vfirst; - struct pvr2_v4l2_fh *vlast; - - struct v4l2_prio_state prio; - - /* streams - Note that these must be separately, individually, - * allocated pointers. This is because the v4l core is going to - * manage their deletion - separately, individually... */ - struct pvr2_v4l2_dev *dev_video; - struct pvr2_v4l2_dev *dev_radio; -}; - -static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; -module_param_array(video_nr, int, NULL, 0444); -MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor"); -static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; -module_param_array(radio_nr, int, NULL, 0444); -MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor"); -static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; -module_param_array(vbi_nr, int, NULL, 0444); -MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor"); - -static struct v4l2_capability pvr_capability ={ - .driver = "pvrusb2", - .card = "Hauppauge WinTV pvr-usb2", - .bus_info = "usb", - .version = LINUX_VERSION_CODE, - .capabilities = (V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | - V4L2_CAP_READWRITE), -}; - -static struct v4l2_fmtdesc pvr_fmtdesc [] = { - { - .index = 0, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .flags = V4L2_FMT_FLAG_COMPRESSED, - .description = "MPEG1/2", - // This should really be V4L2_PIX_FMT_MPEG, but xawtv - // breaks when I do that. - .pixelformat = 0, // V4L2_PIX_FMT_MPEG, - } -}; - -#define PVR_FORMAT_PIX 0 -#define PVR_FORMAT_VBI 1 - -static struct v4l2_format pvr_format [] = { - [PVR_FORMAT_PIX] = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt = { - .pix = { - .width = 720, - .height = 576, - // This should really be V4L2_PIX_FMT_MPEG, - // but xawtv breaks when I do that. - .pixelformat = 0, // V4L2_PIX_FMT_MPEG, - .field = V4L2_FIELD_INTERLACED, - .bytesperline = 0, // doesn't make sense - // here - //FIXME : Don't know what to put here... - .sizeimage = (32*1024), - .colorspace = 0, // doesn't make sense here - .priv = 0 - } - } - }, - [PVR_FORMAT_VBI] = { - .type = V4L2_BUF_TYPE_VBI_CAPTURE, - .fmt = { - .vbi = { - .sampling_rate = 27000000, - .offset = 248, - .samples_per_line = 1443, - .sample_format = V4L2_PIX_FMT_GREY, - .start = { 0, 0 }, - .count = { 0, 0 }, - .flags = 0, - } - } - } -}; - - - -/* - * This is part of Video 4 Linux API. These procedures handle ioctl() calls. - */ -static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); - strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw), - sizeof(cap->bus_info)); - strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card)); - return 0; -} - -static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_v4l2 *vp = fh->vhead; - - *p = v4l2_prio_max(&vp->prio); - return 0; -} - -static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_v4l2 *vp = fh->vhead; - - return v4l2_prio_change(&vp->prio, &fh->prio, prio); -} - -static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int val = 0; - int ret; - - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val); - *std = val; - return ret; -} - -int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - return pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std); -} - -static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int val = 0; - int ret; - - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val); - *std = val; - return ret; -} - -static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct pvr2_ctrl *cptr; - struct v4l2_input tmp; - unsigned int cnt; - int val; - - cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); - - memset(&tmp, 0, sizeof(tmp)); - tmp.index = vi->index; - if (vi->index >= fh->input_cnt) - return -EINVAL; - val = fh->input_map[vi->index]; - switch (val) { - case PVR2_CVAL_INPUT_TV: - case PVR2_CVAL_INPUT_DTV: - case PVR2_CVAL_INPUT_RADIO: - tmp.type = V4L2_INPUT_TYPE_TUNER; - break; - case PVR2_CVAL_INPUT_SVIDEO: - case PVR2_CVAL_INPUT_COMPOSITE: - tmp.type = V4L2_INPUT_TYPE_CAMERA; - break; - default: - return -EINVAL; - } - - cnt = 0; - pvr2_ctrl_get_valname(cptr, val, - tmp.name, sizeof(tmp.name) - 1, &cnt); - tmp.name[cnt] = 0; - - /* Don't bother with audioset, since this driver currently - always switches the audio whenever the video is - switched. */ - - /* Handling std is a tougher problem. It doesn't make - sense in cases where a device might be multi-standard. - We could just copy out the current value for the - standard, but it can change over time. For now just - leave it zero. */ - *vi = tmp; - return 0; -} - -static int pvr2_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - unsigned int idx; - struct pvr2_ctrl *cptr; - int val; - int ret; - - cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); - val = 0; - ret = pvr2_ctrl_get_value(cptr, &val); - *i = 0; - for (idx = 0; idx < fh->input_cnt; idx++) { - if (fh->input_map[idx] == val) { - *i = idx; - break; - } - } - return ret; -} - -static int pvr2_s_input(struct file *file, void *priv, unsigned int inp) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - if (inp >= fh->input_cnt) - return -EINVAL; - return pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), - fh->input_map[inp]); -} - -static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin) -{ - /* pkt: FIXME: We are returning one "fake" input here - which could very well be called "whatever_we_like". - This is for apps that want to see an audio input - just to feel comfortable, as well as to test if - it can do stereo or sth. There is actually no guarantee - that the actual audio input cannot change behind the app's - back, but most applications should not mind that either. - - Hopefully, mplayer people will work with us on this (this - whole mess is to support mplayer pvr://), or Hans will come - up with a more standard way to say "we have inputs but we - don 't want you to change them independent of video" which - will sort this mess. - */ - - if (vin->index > 0) - return -EINVAL; - strncpy(vin->name, "PVRUSB2 Audio", 14); - vin->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) -{ - /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ - vin->index = 0; - strncpy(vin->name, "PVRUSB2 Audio", 14); - vin->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout) -{ - if (vout->index) - return -EINVAL; - return 0; -} - -static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - if (vt->index != 0) - return -EINVAL; /* Only answer for the 1st tuner */ - - pvr2_hdw_execute_tuner_poll(hdw); - return pvr2_hdw_get_tuner_status(hdw, vt); -} - -static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - if (vt->index != 0) - return -EINVAL; - - return pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE), - vt->audmode); -} - -int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - unsigned long fv; - struct v4l2_tuner vt; - int cur_input; - struct pvr2_ctrl *ctrlp; - int ret; - - ret = pvr2_hdw_get_tuner_status(hdw, &vt); - if (ret != 0) - return ret; - ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); - ret = pvr2_ctrl_get_value(ctrlp, &cur_input); - if (ret != 0) - return ret; - if (vf->type == V4L2_TUNER_RADIO) { - if (cur_input != PVR2_CVAL_INPUT_RADIO) - pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO); - } else { - if (cur_input == PVR2_CVAL_INPUT_RADIO) - pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV); - } - fv = vf->frequency; - if (vt.capability & V4L2_TUNER_CAP_LOW) - fv = (fv * 125) / 2; - else - fv = fv * 62500; - return pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); -} - -static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int val = 0; - int cur_input; - struct v4l2_tuner vt; - int ret; - - ret = pvr2_hdw_get_tuner_status(hdw, &vt); - if (ret != 0) - return ret; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY), - &val); - if (ret != 0) - return ret; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), - &cur_input); - if (cur_input == PVR2_CVAL_INPUT_RADIO) - vf->type = V4L2_TUNER_RADIO; - else - vf->type = V4L2_TUNER_ANALOG_TV; - if (vt.capability & V4L2_TUNER_CAP_LOW) - val = (val * 2) / 125; - else - val /= 62500; - vf->frequency = val; - return 0; -} - -static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd) -{ - /* Only one format is supported : mpeg.*/ - if (fd->index != 0) - return -EINVAL; - - memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); - return 0; -} - -static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int val; - - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES), - &val); - vf->fmt.pix.width = val; - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES), - &val); - vf->fmt.pix.height = val; - return 0; -} - -static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int lmin, lmax, ldef; - struct pvr2_ctrl *hcp, *vcp; - int h = vf->fmt.pix.height; - int w = vf->fmt.pix.width; - - hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); - vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); - - lmin = pvr2_ctrl_get_min(hcp); - lmax = pvr2_ctrl_get_max(hcp); - pvr2_ctrl_get_def(hcp, &ldef); - if (w == -1) - w = ldef; - else if (w < lmin) - w = lmin; - else if (w > lmax) - w = lmax; - lmin = pvr2_ctrl_get_min(vcp); - lmax = pvr2_ctrl_get_max(vcp); - pvr2_ctrl_get_def(vcp, &ldef); - if (h == -1) - h = ldef; - else if (h < lmin) - h = lmin; - else if (h > lmax) - h = lmax; - - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], - sizeof(struct v4l2_format)); - vf->fmt.pix.width = w; - vf->fmt.pix.height = h; - return 0; -} - -static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct pvr2_ctrl *hcp, *vcp; - int ret = pvr2_try_fmt_vid_cap(file, fh, vf); - - if (ret) - return ret; - hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); - vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); - pvr2_ctrl_set_value(hcp, vf->fmt.pix.width); - pvr2_ctrl_set_value(vcp, vf->fmt.pix.height); - return 0; -} - -static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct pvr2_v4l2_dev *pdi = fh->pdi; - int ret; - - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - return -EPERM; - } - ret = pvr2_hdw_set_stream_type(hdw, pdi->config); - if (ret < 0) - return ret; - return pvr2_hdw_set_streaming(hdw, !0); -} - -static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - return -EPERM; - } - return pvr2_hdw_set_streaming(hdw, 0); -} - -static int pvr2_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *vc) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct pvr2_ctrl *cptr; - int val; - - if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { - cptr = pvr2_hdw_get_ctrl_nextv4l( - hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); - if (cptr) - vc->id = pvr2_ctrl_get_v4lid(cptr); - } else { - cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id); - } - if (!cptr) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x not implemented here", - vc->id); - return -EINVAL; - } - - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x mapping name=%s (%s)", - vc->id, pvr2_ctrl_get_name(cptr), - pvr2_ctrl_get_desc(cptr)); - strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name)); - vc->flags = pvr2_ctrl_get_v4lflags(cptr); - pvr2_ctrl_get_def(cptr, &val); - vc->default_value = val; - switch (pvr2_ctrl_get_type(cptr)) { - case pvr2_ctl_enum: - vc->type = V4L2_CTRL_TYPE_MENU; - vc->minimum = 0; - vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; - vc->step = 1; - break; - case pvr2_ctl_bool: - vc->type = V4L2_CTRL_TYPE_BOOLEAN; - vc->minimum = 0; - vc->maximum = 1; - vc->step = 1; - break; - case pvr2_ctl_int: - vc->type = V4L2_CTRL_TYPE_INTEGER; - vc->minimum = pvr2_ctrl_get_min(cptr); - vc->maximum = pvr2_ctrl_get_max(cptr); - vc->step = 1; - break; - default: - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x name=%s not mappable", - vc->id, pvr2_ctrl_get_name(cptr)); - return -EINVAL; - } - return 0; -} - -static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - unsigned int cnt = 0; - int ret; - - ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id), - vm->index, - vm->name, sizeof(vm->name) - 1, - &cnt); - vm->name[cnt] = 0; - return ret; -} - -static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int val = 0; - int ret; - - ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), - &val); - vc->value = val; - return ret; -} - -static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), - vc->value); -} - -static int pvr2_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctls) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct v4l2_ext_control *ctrl; - unsigned int idx; - int val; - int ret; - - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val); - if (ret) { - ctls->error_idx = idx; - return ret; - } - /* Ensure that if read as a 64 bit value, the user - will still get a hopefully sane value */ - ctrl->value64 = 0; - ctrl->value = val; - } - return 0; -} - -static int pvr2_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctls) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct v4l2_ext_control *ctrl; - unsigned int idx; - int ret; - - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), - ctrl->value); - if (ret) { - ctls->error_idx = idx; - return ret; - } - } - return 0; -} - -static int pvr2_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctls) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct v4l2_ext_control *ctrl; - struct pvr2_ctrl *pctl; - unsigned int idx; - - /* For the moment just validate that the requested control - actually exists. */ - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); - if (!pctl) { - ctls->error_idx = idx; - return -EINVAL; - } - } - return 0; -} - -static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int ret; - - if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - ret = pvr2_hdw_get_cropcap(hdw, cap); - cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ - return ret; -} - -static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int val = 0; - int ret; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); - if (ret != 0) - return -EINVAL; - crop->c.left = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); - if (ret != 0) - return -EINVAL; - crop->c.top = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); - if (ret != 0) - return -EINVAL; - crop->c.width = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); - if (ret != 0) - return -EINVAL; - crop->c.height = val; - return 0; -} - -static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - int ret; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), - crop->c.left); - if (ret != 0) - return -EINVAL; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), - crop->c.top); - if (ret != 0) - return -EINVAL; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), - crop->c.width); - if (ret != 0) - return -EINVAL; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), - crop->c.height); - if (ret != 0) - return -EINVAL; - return 0; -} - -static int pvr2_log_status(struct file *file, void *priv) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - - pvr2_hdw_trigger_module_log(hdw); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - u64 val; - int ret; - - ret = pvr2_hdw_register_access( - hdw, &req->match, req->reg, - 0, &val); - req->val = val; - return ret; -} - -static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - u64 val; - int ret; - - val = req->val; - ret = pvr2_hdw_register_access( - hdw, &req->match, req->reg, - 1, &val); - return ret; -} -#endif - -static const struct v4l2_ioctl_ops pvr2_ioctl_ops = { - .vidioc_querycap = pvr2_querycap, - .vidioc_g_priority = pvr2_g_priority, - .vidioc_s_priority = pvr2_s_priority, - .vidioc_s_audio = pvr2_s_audio, - .vidioc_g_audio = pvr2_g_audio, - .vidioc_enumaudio = pvr2_enumaudio, - .vidioc_enum_input = pvr2_enum_input, - .vidioc_cropcap = pvr2_cropcap, - .vidioc_s_crop = pvr2_s_crop, - .vidioc_g_crop = pvr2_g_crop, - .vidioc_g_input = pvr2_g_input, - .vidioc_s_input = pvr2_s_input, - .vidioc_g_frequency = pvr2_g_frequency, - .vidioc_s_frequency = pvr2_s_frequency, - .vidioc_s_tuner = pvr2_s_tuner, - .vidioc_g_tuner = pvr2_g_tuner, - .vidioc_g_std = pvr2_g_std, - .vidioc_s_std = pvr2_s_std, - .vidioc_querystd = pvr2_querystd, - .vidioc_log_status = pvr2_log_status, - .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap, - .vidioc_streamon = pvr2_streamon, - .vidioc_streamoff = pvr2_streamoff, - .vidioc_queryctrl = pvr2_queryctrl, - .vidioc_querymenu = pvr2_querymenu, - .vidioc_g_ctrl = pvr2_g_ctrl, - .vidioc_s_ctrl = pvr2_s_ctrl, - .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls, - .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls, - .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = pvr2_g_register, - .vidioc_s_register = pvr2_s_register, -#endif -}; - -static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) -{ - struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw; - enum pvr2_config cfg = dip->config; - char msg[80]; - unsigned int mcnt; - - /* Construct the unregistration message *before* we actually - perform the unregistration step. By doing it this way we don't - have to worry about potentially touching deleted resources. */ - mcnt = scnprintf(msg, sizeof(msg) - 1, - "pvrusb2: unregistered device %s [%s]", - video_device_node_name(&dip->devbase), - pvr2_config_get_name(cfg)); - msg[mcnt] = 0; - - pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1); - - /* Paranoia */ - dip->v4lp = NULL; - dip->stream = NULL; - - /* Actual deallocation happens later when all internal references - are gone. */ - video_unregister_device(&dip->devbase); - - printk(KERN_INFO "%s\n", msg); - -} - - -static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip) -{ - if (!dip) return; - if (!dip->devbase.parent) return; - dip->devbase.parent = NULL; - device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE); -} - - -static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) -{ - if (vp->dev_video) { - pvr2_v4l2_dev_destroy(vp->dev_video); - vp->dev_video = NULL; - } - if (vp->dev_radio) { - pvr2_v4l2_dev_destroy(vp->dev_radio); - vp->dev_radio = NULL; - } - - pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); - pvr2_channel_done(&vp->channel); - kfree(vp); -} - - -static void pvr2_video_device_release(struct video_device *vdev) -{ - struct pvr2_v4l2_dev *dev; - dev = container_of(vdev,struct pvr2_v4l2_dev,devbase); - kfree(dev); -} - - -static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) -{ - struct pvr2_v4l2 *vp; - vp = container_of(chp,struct pvr2_v4l2,channel); - if (!vp->channel.mc_head->disconnect_flag) return; - pvr2_v4l2_dev_disassociate_parent(vp->dev_video); - pvr2_v4l2_dev_disassociate_parent(vp->dev_radio); - if (vp->vfirst) return; - pvr2_v4l2_destroy_no_lock(vp); -} - - -static long pvr2_v4l2_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_v4l2 *vp = fh->vhead; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - long ret = -EINVAL; - - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) - v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); - - if (!pvr2_hdw_dev_ok(hdw)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "ioctl failed - bad or no context"); - return -EFAULT; - } - - /* check priority */ - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - ret = v4l2_prio_check(&vp->prio, fh->prio); - if (ret) - return ret; - } - - ret = video_ioctl2(file, cmd, arg); - - pvr2_hdw_commit_ctl(hdw); - - if (ret < 0) { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); - } else { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld" - " command was:", ret); - v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), - cmd); - } - } - } else { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", - ret, ret); - } - return ret; - -} - - -static int pvr2_v4l2_release(struct file *file) -{ - struct pvr2_v4l2_fh *fhp = file->private_data; - struct pvr2_v4l2 *vp = fhp->vhead; - struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw; - - pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); - - if (fhp->rhp) { - struct pvr2_stream *sp; - pvr2_hdw_set_streaming(hdw,0); - sp = pvr2_ioread_get_stream(fhp->rhp); - if (sp) pvr2_stream_set_callback(sp,NULL,NULL); - pvr2_ioread_destroy(fhp->rhp); - fhp->rhp = NULL; - } - - v4l2_prio_close(&vp->prio, fhp->prio); - file->private_data = NULL; - - if (fhp->vnext) { - fhp->vnext->vprev = fhp->vprev; - } else { - vp->vlast = fhp->vprev; - } - if (fhp->vprev) { - fhp->vprev->vnext = fhp->vnext; - } else { - vp->vfirst = fhp->vnext; - } - fhp->vnext = NULL; - fhp->vprev = NULL; - fhp->vhead = NULL; - pvr2_channel_done(&fhp->channel); - pvr2_trace(PVR2_TRACE_STRUCT, - "Destroying pvr_v4l2_fh id=%p",fhp); - if (fhp->input_map) { - kfree(fhp->input_map); - fhp->input_map = NULL; - } - kfree(fhp); - if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { - pvr2_v4l2_destroy_no_lock(vp); - } - return 0; -} - - -static int pvr2_v4l2_open(struct file *file) -{ - struct pvr2_v4l2_dev *dip; /* Our own context pointer */ - struct pvr2_v4l2_fh *fhp; - struct pvr2_v4l2 *vp; - struct pvr2_hdw *hdw; - unsigned int input_mask = 0; - unsigned int input_cnt,idx; - int ret = 0; - - dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); - - vp = dip->v4lp; - hdw = vp->channel.hdw; - - pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open"); - - if (!pvr2_hdw_dev_ok(hdw)) { - pvr2_trace(PVR2_TRACE_OPEN_CLOSE, - "pvr2_v4l2_open: hardware not ready"); - return -EIO; - } - - fhp = kzalloc(sizeof(*fhp),GFP_KERNEL); - if (!fhp) { - return -ENOMEM; - } - - init_waitqueue_head(&fhp->wait_data); - fhp->pdi = dip; - - pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); - pvr2_channel_init(&fhp->channel,vp->channel.mc_head); - - if (dip->v4l_type == VFL_TYPE_RADIO) { - /* Opening device as a radio, legal input selection subset - is just the radio. */ - input_mask = (1 << PVR2_CVAL_INPUT_RADIO); - } else { - /* Opening the main V4L device, legal input selection - subset includes all analog inputs. */ - input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) | - (1 << PVR2_CVAL_INPUT_TV) | - (1 << PVR2_CVAL_INPUT_COMPOSITE) | - (1 << PVR2_CVAL_INPUT_SVIDEO)); - } - ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask); - if (ret) { - pvr2_channel_done(&fhp->channel); - pvr2_trace(PVR2_TRACE_STRUCT, - "Destroying pvr_v4l2_fh id=%p (input mask error)", - fhp); - - kfree(fhp); - return ret; - } - - input_mask &= pvr2_hdw_get_input_available(hdw); - input_cnt = 0; - for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { - if (input_mask & (1 << idx)) input_cnt++; - } - fhp->input_cnt = input_cnt; - fhp->input_map = kzalloc(input_cnt,GFP_KERNEL); - if (!fhp->input_map) { - pvr2_channel_done(&fhp->channel); - pvr2_trace(PVR2_TRACE_STRUCT, - "Destroying pvr_v4l2_fh id=%p (input map failure)", - fhp); - kfree(fhp); - return -ENOMEM; - } - input_cnt = 0; - for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { - if (!(input_mask & (1 << idx))) continue; - fhp->input_map[input_cnt++] = idx; - } - - fhp->vnext = NULL; - fhp->vprev = vp->vlast; - if (vp->vlast) { - vp->vlast->vnext = fhp; - } else { - vp->vfirst = fhp; - } - vp->vlast = fhp; - fhp->vhead = vp; - - fhp->file = file; - file->private_data = fhp; - v4l2_prio_open(&vp->prio, &fhp->prio); - - fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); - - return 0; -} - - -static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp) -{ - wake_up(&fhp->wait_data); -} - -static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) -{ - int ret; - struct pvr2_stream *sp; - struct pvr2_hdw *hdw; - if (fh->rhp) return 0; - - if (!fh->pdi->stream) { - /* No stream defined for this node. This means that we're - not currently allowed to stream from this node. */ - return -EPERM; - } - - /* First read() attempt. Try to claim the stream and start - it... */ - if ((ret = pvr2_channel_claim_stream(&fh->channel, - fh->pdi->stream)) != 0) { - /* Someone else must already have it */ - return ret; - } - - fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream); - if (!fh->rhp) { - pvr2_channel_claim_stream(&fh->channel,NULL); - return -ENOMEM; - } - - hdw = fh->channel.mc_head->hdw; - sp = fh->pdi->stream->stream; - pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh); - pvr2_hdw_set_stream_type(hdw,fh->pdi->config); - if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret; - return pvr2_ioread_set_enabled(fh->rhp,!0); -} - - -static ssize_t pvr2_v4l2_read(struct file *file, - char __user *buff, size_t count, loff_t *ppos) -{ - struct pvr2_v4l2_fh *fh = file->private_data; - int ret; - - if (fh->fw_mode_flag) { - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - char *tbuf; - int c1,c2; - int tcnt = 0; - unsigned int offs = *ppos; - - tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL); - if (!tbuf) return -ENOMEM; - - while (count) { - c1 = count; - if (c1 > PAGE_SIZE) c1 = PAGE_SIZE; - c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1); - if (c2 < 0) { - tcnt = c2; - break; - } - if (!c2) break; - if (copy_to_user(buff,tbuf,c2)) { - tcnt = -EFAULT; - break; - } - offs += c2; - tcnt += c2; - buff += c2; - count -= c2; - *ppos += c2; - } - kfree(tbuf); - return tcnt; - } - - if (!fh->rhp) { - ret = pvr2_v4l2_iosetup(fh); - if (ret) { - return ret; - } - } - - for (;;) { - ret = pvr2_ioread_read(fh->rhp,buff,count); - if (ret >= 0) break; - if (ret != -EAGAIN) break; - if (file->f_flags & O_NONBLOCK) break; - /* Doing blocking I/O. Wait here. */ - ret = wait_event_interruptible( - fh->wait_data, - pvr2_ioread_avail(fh->rhp) >= 0); - if (ret < 0) break; - } - - return ret; -} - - -static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait) -{ - unsigned int mask = 0; - struct pvr2_v4l2_fh *fh = file->private_data; - int ret; - - if (fh->fw_mode_flag) { - mask |= POLLIN | POLLRDNORM; - return mask; - } - - if (!fh->rhp) { - ret = pvr2_v4l2_iosetup(fh); - if (ret) return POLLERR; - } - - poll_wait(file,&fh->wait_data,wait); - - if (pvr2_ioread_avail(fh->rhp) >= 0) { - mask |= POLLIN | POLLRDNORM; - } - - return mask; -} - - -static const struct v4l2_file_operations vdev_fops = { - .owner = THIS_MODULE, - .open = pvr2_v4l2_open, - .release = pvr2_v4l2_release, - .read = pvr2_v4l2_read, - .ioctl = pvr2_v4l2_ioctl, - .poll = pvr2_v4l2_poll, -}; - - -static struct video_device vdev_template = { - .fops = &vdev_fops, -}; - - -static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, - struct pvr2_v4l2 *vp, - int v4l_type) -{ - struct usb_device *usbdev; - int mindevnum; - int unit_number; - struct pvr2_hdw *hdw; - int *nr_ptr = NULL; - dip->v4lp = vp; - - hdw = vp->channel.mc_head->hdw; - usbdev = pvr2_hdw_get_dev(hdw); - dip->v4l_type = v4l_type; - switch (v4l_type) { - case VFL_TYPE_GRABBER: - dip->stream = &vp->channel.mc_head->video_stream; - dip->config = pvr2_config_mpeg; - dip->minor_type = pvr2_v4l_type_video; - nr_ptr = video_nr; - if (!dip->stream) { - pr_err(KBUILD_MODNAME - ": Failed to set up pvrusb2 v4l video dev" - " due to missing stream instance\n"); - return; - } - break; - case VFL_TYPE_VBI: - dip->config = pvr2_config_vbi; - dip->minor_type = pvr2_v4l_type_vbi; - nr_ptr = vbi_nr; - break; - case VFL_TYPE_RADIO: - dip->stream = &vp->channel.mc_head->video_stream; - dip->config = pvr2_config_mpeg; - dip->minor_type = pvr2_v4l_type_radio; - nr_ptr = radio_nr; - break; - default: - /* Bail out (this should be impossible) */ - pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev" - " due to unrecognized config\n"); - return; - } - - memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); - dip->devbase.release = pvr2_video_device_release; - dip->devbase.ioctl_ops = &pvr2_ioctl_ops; - { - int val; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, - PVR2_CID_STDAVAIL), &val); - dip->devbase.tvnorms = (v4l2_std_id)val; - } - - mindevnum = -1; - unit_number = pvr2_hdw_get_unit_number(hdw); - if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { - mindevnum = nr_ptr[unit_number]; - } - dip->devbase.parent = &usbdev->dev; - if ((video_register_device(&dip->devbase, - dip->v4l_type, mindevnum) < 0) && - (video_register_device(&dip->devbase, - dip->v4l_type, -1) < 0)) { - pr_err(KBUILD_MODNAME - ": Failed to register pvrusb2 v4l device\n"); - } - - printk(KERN_INFO "pvrusb2: registered device %s [%s]\n", - video_device_node_name(&dip->devbase), - pvr2_config_get_name(dip->config)); - - pvr2_hdw_v4l_store_minor_number(hdw, - dip->minor_type,dip->devbase.minor); -} - - -struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) -{ - struct pvr2_v4l2 *vp; - - vp = kzalloc(sizeof(*vp),GFP_KERNEL); - if (!vp) return vp; - pvr2_channel_init(&vp->channel,mnp); - pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); - - vp->channel.check_func = pvr2_v4l2_internal_check; - - /* register streams */ - vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); - if (!vp->dev_video) goto fail; - pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); - if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & - (1 << PVR2_CVAL_INPUT_RADIO)) { - vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); - if (!vp->dev_radio) goto fail; - pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); - } - - return vp; - fail: - pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp); - pvr2_v4l2_destroy_no_lock(vp); - return NULL; -} - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h deleted file mode 100644 index 34c011a7b107..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __PVRUSB2_V4L2_H -#define __PVRUSB2_V4L2_H - -#include "pvrusb2-context.h" - -struct pvr2_v4l2; - -struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *); - -#endif /* __PVRUSB2_V4L2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c deleted file mode 100644 index 2e205c99eb96..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* - - This source file is specifically designed to interface with the - saa711x support that is available in the v4l available starting - with linux 2.6.15. - -*/ - -#include "pvrusb2-video-v4l.h" - - - -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include -#include -#include -#include - -struct routing_scheme { - const int *def; - unsigned int cnt; -}; - - -static const int routing_scheme0[] = { - [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, - /* In radio mode, we mute the video, but point at one - spot just to stay consistent */ - [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, - [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5, - [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, -}; - -static const struct routing_scheme routing_def0 = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), -}; - -static const int routing_scheme1[] = { - [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, - [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, - [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3, - [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */ -}; - -static const struct routing_scheme routing_def1 = { - .def = routing_scheme1, - .cnt = ARRAY_SIZE(routing_scheme1), -}; - -static const struct routing_scheme *routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, - [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, -}; - -void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) -{ - if (hdw->input_dirty || hdw->force_dirty) { - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - u32 input; - - pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", - hdw->input_val); - - sp = (sid < ARRAY_SIZE(routing_schemes)) ? - routing_schemes[sid] : NULL; - if ((sp == NULL) || - (hdw->input_val < 0) || - (hdw->input_val >= sp->cnt)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** subdev v4l2 set_input:" - " Invalid routing scheme (%u)" - " and/or input (%d)", - sid, hdw->input_val); - return; - } - input = sp->def[hdw->input_val]; - sd->ops->video->s_routing(sd, input, 0, 0); - } -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h deleted file mode 100644 index 3b0bd5db602b..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_VIDEO_V4L_H -#define __PVRUSB2_VIDEO_V4L_H - -/* - - This module connects the pvrusb2 driver to the I2C chip level - driver which handles device video processing. This interface is - used internally by the driver; higher level code should only - interact through the interface provided by pvrusb2-hdw.h. - -*/ - - -#include "pvrusb2-hdw-internal.h" -void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); - -#endif /* __PVRUSB2_VIDEO_V4L_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c deleted file mode 100644 index 3ac8d751a5c0..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* - - This source file is specifically designed to interface with the - wm8775. - -*/ - -#include "pvrusb2-wm8775.h" - - -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include -#include -#include - -void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) -{ - if (hdw->input_dirty || hdw->force_dirty) { - u32 input; - - switch (hdw->input_val) { - case PVR2_CVAL_INPUT_RADIO: - input = 1; - break; - default: - /* All other cases just use the second input */ - input = 2; - break; - } - pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775" - " set_input(val=%d route=0x%x)", - hdw->input_val, input); - - sd->ops->audio->s_routing(sd, input, 0, 0); - } -} - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h deleted file mode 100644 index 0577bc7246fb..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_WM8775_H -#define __PVRUSB2_WM8775_H - -/* - - This module connects the pvrusb2 driver to the I2C chip level - driver which performs analog -> digital audio conversion for - external audio inputs. This interface is used internally by the - driver; higher level code should only interact through the - interface provided by pvrusb2-hdw.h. - -*/ - - - -#include "pvrusb2-hdw-internal.h" - -void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); - - -#endif /* __PVRUSB2_WM8775_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2.h b/drivers/media/video/pvrusb2/pvrusb2.h deleted file mode 100644 index 240de9b35661..000000000000 --- a/drivers/media/video/pvrusb2/pvrusb2.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; 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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_H -#define __PVRUSB2_H - -/* Maximum number of pvrusb2 instances we can track at once. You - might want to increase this - however the driver operation will not - be impaired if it is too small. Instead additional units just - won't have an ID assigned and it might not be possible to specify - module parameters for those extra units. */ -#define PVR_NUM 20 - -#endif /* __PVRUSB2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig deleted file mode 100644 index d63d0a850035..000000000000 --- a/drivers/media/video/pwc/Kconfig +++ /dev/null @@ -1,48 +0,0 @@ -config USB_PWC - tristate "USB Philips Cameras" - depends on VIDEO_V4L2 - select VIDEOBUF2_VMALLOC - ---help--- - Say Y or M here if you want to use one of these Philips & OEM - webcams: - * Philips PCA645, PCA646 - * Philips PCVC675, PCVC680, PCVC690 - * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 - * Philips SPC900NC - * Askey VC010 - * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' - and 'Orbit'/'Sphere' - * Samsung MPC-C10, MPC-C30 - * Creative Webcam 5, Pro Ex - * SOTEC Afina Eye - * Visionite VCS-UC300, VCS-UM100 - - The PCA635, PCVC665 and PCVC720/20 are not supported by this driver - and never will be, but the 665 and 720/20 are supported by other - drivers. - - Some newer logitech webcams are not handled by this driver but by the - Usb Video Class driver (linux-uvc). - - The built-in microphone is enabled by selecting USB Audio support. - - To compile this driver as a module, choose M here: the - module will be called pwc. - -config USB_PWC_DEBUG - bool "USB Philips Cameras verbose debug" - depends on USB_PWC - help - Say Y here in order to have the pwc driver generate verbose debugging - messages. - A special module options 'trace' is used to control the verbosity. - -config USB_PWC_INPUT_EVDEV - bool "USB Philips Cameras input events device support" - default y - depends on USB_PWC && (USB_PWC=INPUT || INPUT=y) - ---help--- - This option makes USB Philips cameras register the snapshot button as - an input device to report button events. - - If you are in doubt, say Y. diff --git a/drivers/media/video/pwc/Makefile b/drivers/media/video/pwc/Makefile deleted file mode 100644 index f5c8ec261e87..000000000000 --- a/drivers/media/video/pwc/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o -pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o - -obj-$(CONFIG_USB_PWC) += pwc.o diff --git a/drivers/media/video/pwc/philips.txt b/drivers/media/video/pwc/philips.txt deleted file mode 100644 index d38dd791511e..000000000000 --- a/drivers/media/video/pwc/philips.txt +++ /dev/null @@ -1,236 +0,0 @@ -This file contains some additional information for the Philips and OEM webcams. -E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19 -Site: http://www.smcc.demon.nl/webcam/ - -As of this moment, the following cameras are supported: - * Philips PCA645 - * Philips PCA646 - * Philips PCVC675 - * Philips PCVC680 - * Philips PCVC690 - * Philips PCVC720/40 - * Philips PCVC730 - * Philips PCVC740 - * Philips PCVC750 - * Askey VC010 - * Creative Labs Webcam 5 - * Creative Labs Webcam Pro Ex - * Logitech QuickCam 3000 Pro - * Logitech QuickCam 4000 Pro - * Logitech QuickCam Notebook Pro - * Logitech QuickCam Zoom - * Logitech QuickCam Orbit - * Logitech QuickCam Sphere - * Samsung MPC-C10 - * Samsung MPC-C30 - * Sotec Afina Eye - * AME CU-001 - * Visionite VCS-UM100 - * Visionite VCS-UC300 - -The main webpage for the Philips driver is at the address above. It contains -a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin -contains decompression routines that allow you to use higher image sizes and -framerates; in addition the webcam uses less bandwidth on the USB bus (handy -if you want to run more than 1 camera simultaneously). These routines fall -under a NDA, and may therefore not be distributed as source; however, its use -is completely optional. - -You can build this code either into your kernel, or as a module. I recommend -the latter, since it makes troubleshooting a lot easier. The built-in -microphone is supported through the USB Audio class. - -When you load the module you can set some default settings for the -camera; some programs depend on a particular image-size or -format and -don't know how to set it properly in the driver. The options are: - -size - Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or - 'vga', for an image size of resp. 128x96, 160x120, 176x144, - 320x240, 352x288 and 640x480 (of course, only for those cameras that - support these resolutions). - -fps - Specifies the desired framerate. Is an integer in the range of 4-30. - -fbufs - This parameter specifies the number of internal buffers to use for storing - frames from the cam. This will help if the process that reads images from - the cam is a bit slow or momentarily busy. However, on slow machines it - only introduces lag, so choose carefully. The default is 3, which is - reasonable. You can set it between 2 and 5. - -mbufs - This is an integer between 1 and 10. It will tell the module the number of - buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends. - The default is 2, which is adequate for most applications (double - buffering). - - Should you experience a lot of 'Dumping frame...' messages during - grabbing with a tool that uses mmap(), you might want to increase if. - However, it doesn't really buffer images, it just gives you a bit more - slack when your program is behind. But you need a multi-threaded or - forked program to really take advantage of these buffers. - - The absolute maximum is 10, but don't set it too high! Every buffer takes - up 460 KB of RAM, so unless you have a lot of memory setting this to - something more than 4 is an absolute waste. This memory is only - allocated during open(), so nothing is wasted when the camera is not in - use. - -power_save - When power_save is enabled (set to 1), the module will try to shut down - the cam on close() and re-activate on open(). This will save power and - turn off the LED. Not all cameras support this though (the 645 and 646 - don't have power saving at all), and some models don't work either (they - will shut down, but never wake up). Consider this experimental. By - default this option is disabled. - -compression (only useful with the plugin) - With this option you can control the compression factor that the camera - uses to squeeze the image through the USB bus. You can set the - parameter between 0 and 3: - 0 = prefer uncompressed images; if the requested mode is not available - in an uncompressed format, the driver will silently switch to low - compression. - 1 = low compression. - 2 = medium compression. - 3 = high compression. - - High compression takes less bandwidth of course, but it could also - introduce some unwanted artefacts. The default is 2, medium compression. - See the FAQ on the website for an overview of which modes require - compression. - - The compression parameter does not apply to the 645 and 646 cameras - and OEM models derived from those (only a few). Most cams honour this - parameter. - -leds - This settings takes 2 integers, that define the on/off time for the LED - (in milliseconds). One of the interesting things that you can do with - this is let the LED blink while the camera is in use. This: - - leds=500,500 - - will blink the LED once every second. But with: - - leds=0,0 - - the LED never goes on, making it suitable for silent surveillance. - - By default the camera's LED is on solid while in use, and turned off - when the camera is not used anymore. - - This parameter works only with the ToUCam range of cameras (720, 730, 740, - 750) and OEMs. For other cameras this command is silently ignored, and - the LED cannot be controlled. - - Finally: this parameters does not take effect UNTIL the first time you - open the camera device. Until then, the LED remains on. - -dev_hint - A long standing problem with USB devices is their dynamic nature: you - never know what device a camera gets assigned; it depends on module load - order, the hub configuration, the order in which devices are plugged in, - and the phase of the moon (i.e. it can be random). With this option you - can give the driver a hint as to what video device node (/dev/videoX) it - should use with a specific camera. This is also handy if you have two - cameras of the same model. - - A camera is specified by its type (the number from the camera model, - like PCA645, PCVC750VC, etc) and optionally the serial number (visible - in /proc/bus/usb/devices). A hint consists of a string with the following - format: - - [type[.serialnumber]:]node - - The square brackets mean that both the type and the serialnumber are - optional, but a serialnumber cannot be specified without a type (which - would be rather pointless). The serialnumber is separated from the type - by a '.'; the node number by a ':'. - - This somewhat cryptic syntax is best explained by a few examples: - - dev_hint=3,5 The first detected cam gets assigned - /dev/video3, the second /dev/video5. Any - other cameras will get the first free - available slot (see below). - - dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1, - and a PCVC680 /dev/video2. - - dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber - 0123 goes to /dev/video3, the same - camera model with the 4567 serial - gets /dev/video0. - - dev_hint=750:1,4,5,6 The PCVC750 camera will get /dev/video1, the - next 3 Philips cams will use /dev/video4 - through /dev/video6. - - Some points worth knowing: - - Serialnumbers are case sensitive and must be written full, including - leading zeroes (it's treated as a string). - - If a device node is already occupied, registration will fail and - the webcam is not available. - - You can have up to 64 video devices; be sure to make enough device - nodes in /dev if you want to spread the numbers. - After /dev/video9 comes /dev/video10 (not /dev/videoA). - - If a camera does not match any dev_hint, it will simply get assigned - the first available device node, just as it used to be. - -trace - In order to better detect problems, it is now possible to turn on a - 'trace' of some of the calls the module makes; it logs all items in your - kernel log at debug level. - - The trace variable is a bitmask; each bit represents a certain feature. - If you want to trace something, look up the bit value(s) in the table - below, add the values together and supply that to the trace variable. - - Value Value Description Default - (dec) (hex) - 1 0x1 Module initialization; this will log messages On - while loading and unloading the module - - 2 0x2 probe() and disconnect() traces On - - 4 0x4 Trace open() and close() calls Off - - 8 0x8 read(), mmap() and associated ioctl() calls Off - - 16 0x10 Memory allocation of buffers, etc. Off - - 32 0x20 Showing underflow, overflow and Dumping frame On - messages - - 64 0x40 Show viewport and image sizes Off - - 128 0x80 PWCX debugging Off - - For example, to trace the open() & read() functions, sum 8 + 4 = 12, - so you would supply trace=12 during insmod or modprobe. If - you want to turn the initialization and probing tracing off, set trace=0. - The default value for trace is 35 (0x23). - - - -Example: - - # modprobe pwc size=cif fps=15 power_save=1 - -The fbufs, mbufs and trace parameters are global and apply to all connected -cameras. Each camera has its own set of buffers. - -size and fps only specify defaults when you open() the device; this is to -accommodate some tools that don't set the size. You can change these -settings after open() with the Video4Linux ioctl() calls. The default of -defaults is QCIF size at 10 fps. - -The compression parameter is semiglobal; it sets the initial compression -preference for all camera's, but this parameter can be set per camera with -the VIDIOCPWCSCQUAL ioctl() call. - -All parameters are optional. - diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c deleted file mode 100644 index 1f506fde97d0..000000000000 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ /dev/null @@ -1,553 +0,0 @@ -/* Driver for Philips webcam - Functions that send various control messages to the webcam, including - video modes. - (C) 1999-2003 Nemosoft Unv. - (C) 2004-2006 Luc Saillard (luc@saillard.org) - (C) 2011 Hans de Goede - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* - Changes - 2001/08/03 Alvarado Added methods for changing white balance and - red/green gains - */ - -/* Control functions for the cam; brightness, contrast, video mode, etc. */ - -#ifdef __KERNEL__ -#include -#endif -#include - -#include "pwc.h" -#include "pwc-kiara.h" -#include "pwc-timon.h" -#include "pwc-dec1.h" -#include "pwc-dec23.h" - -/* Selectors for status controls used only in this file */ -#define GET_STATUS_B00 0x0B00 -#define SENSOR_TYPE_FORMATTER1 0x0C00 -#define GET_STATUS_3000 0x3000 -#define READ_RAW_Y_MEAN_FORMATTER 0x3100 -#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 -#define MIRROR_IMAGE_FORMATTER 0x3300 -#define LED_FORMATTER 0x3400 -#define LOWLIGHT 0x3500 -#define GET_STATUS_3600 0x3600 -#define SENSOR_TYPE_FORMATTER2 0x3700 -#define GET_STATUS_3800 0x3800 -#define GET_STATUS_4000 0x4000 -#define GET_STATUS_4100 0x4100 /* Get */ -#define CTL_STATUS_4200 0x4200 /* [GS] 1 */ - -/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ -#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 - -static const char *size2name[PSZ_MAX] = -{ - "subQCIF", - "QSIF", - "QCIF", - "SIF", - "CIF", - "VGA", -}; - -/********/ - -/* Entries for the Nala (645/646) camera; the Nala doesn't have compression - preferences, so you either get compressed or non-compressed streams. - - An alternate value of 0 means this mode is not available at all. - */ - -#define PWC_FPS_MAX_NALA 8 - -struct Nala_table_entry { - char alternate; /* USB alternate setting */ - int compressed; /* Compressed yes/no */ - - unsigned char mode[3]; /* precomputed mode table */ -}; - -static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; - -static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = -{ -#include "pwc-nala.h" -}; - -/****************************************************************************/ - -static int recv_control_msg(struct pwc_device *pdev, - u8 request, u16 value, int recv_count) -{ - int rc; - - rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), - request, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, pdev->vcinterface, - pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT); - if (rc < 0) - PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", - rc, request, value); - return rc; -} - -static inline int send_video_command(struct pwc_device *pdev, - int index, const unsigned char *buf, int buflen) -{ - int rc; - - memcpy(pdev->ctrl_buf, buf, buflen); - - rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), - SET_EP_STREAM_CTL, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - VIDEO_OUTPUT_CONTROL_FORMATTER, index, - pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT); - if (rc >= 0) - memcpy(pdev->cmd_buf, buf, buflen); - else - PWC_ERROR("send_video_command error %d\n", rc); - - return rc; -} - -int send_control_msg(struct pwc_device *pdev, - u8 request, u16 value, void *buf, int buflen) -{ - return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, pdev->vcinterface, - buf, buflen, USB_CTRL_SET_TIMEOUT); -} - -static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, - int frames, int *compression, int send_to_cam) -{ - int fps, ret = 0; - struct Nala_table_entry *pEntry; - int frames2frames[31] = - { /* closest match of framerate */ - 0, 0, 0, 0, 4, /* 0-4 */ - 5, 5, 7, 7, 10, /* 5-9 */ - 10, 10, 12, 12, 15, /* 10-14 */ - 15, 15, 15, 20, 20, /* 15-19 */ - 20, 20, 20, 24, 24, /* 20-24 */ - 24, 24, 24, 24, 24, /* 25-29 */ - 24 /* 30 */ - }; - int frames2table[31] = - { 0, 0, 0, 0, 0, /* 0-4 */ - 1, 1, 1, 2, 2, /* 5-9 */ - 3, 3, 4, 4, 4, /* 10-14 */ - 5, 5, 5, 5, 5, /* 15-19 */ - 6, 6, 6, 6, 7, /* 20-24 */ - 7, 7, 7, 7, 7, /* 25-29 */ - 7 /* 30 */ - }; - - if (size < 0 || size > PSZ_CIF) - return -EINVAL; - if (frames < 4) - frames = 4; - else if (frames > 25) - frames = 25; - frames = frames2frames[frames]; - fps = frames2table[frames]; - pEntry = &Nala_table[size][fps]; - if (pEntry->alternate == 0) - return -EINVAL; - - if (send_to_cam) - ret = send_video_command(pdev, pdev->vendpoint, - pEntry->mode, 3); - if (ret < 0) - return ret; - - if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) - pwc_dec1_init(pdev, pEntry->mode); - - /* Set various parameters */ - pdev->pixfmt = pixfmt; - pdev->vframes = frames; - pdev->valternate = pEntry->alternate; - pdev->width = pwc_image_sizes[size][0]; - pdev->height = pwc_image_sizes[size][1]; - pdev->frame_size = (pdev->width * pdev->height * 3) / 2; - if (pEntry->compressed) { - if (pdev->release < 5) { /* 4 fold compression */ - pdev->vbandlength = 528; - pdev->frame_size /= 4; - } - else { - pdev->vbandlength = 704; - pdev->frame_size /= 3; - } - } - else - pdev->vbandlength = 0; - - /* Let pwc-if.c:isoc_init know we don't support higher compression */ - *compression = 3; - - return 0; -} - - -static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, - int frames, int *compression, int send_to_cam) -{ - const struct Timon_table_entry *pChoose; - int fps, ret = 0; - - if (size >= PSZ_MAX || *compression < 0 || *compression > 3) - return -EINVAL; - if (frames < 5) - frames = 5; - else if (size == PSZ_VGA && frames > 15) - frames = 15; - else if (frames > 30) - frames = 30; - fps = (frames / 5) - 1; - - /* Find a supported framerate with progressively higher compression */ - pChoose = NULL; - while (*compression <= 3) { - pChoose = &Timon_table[size][fps][*compression]; - if (pChoose->alternate != 0) - break; - (*compression)++; - } - if (pChoose == NULL || pChoose->alternate == 0) - return -ENOENT; /* Not supported. */ - - if (send_to_cam) - ret = send_video_command(pdev, pdev->vendpoint, - pChoose->mode, 13); - if (ret < 0) - return ret; - - if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) - pwc_dec23_init(pdev, pChoose->mode); - - /* Set various parameters */ - pdev->pixfmt = pixfmt; - pdev->vframes = (fps + 1) * 5; - pdev->valternate = pChoose->alternate; - pdev->width = pwc_image_sizes[size][0]; - pdev->height = pwc_image_sizes[size][1]; - pdev->vbandlength = pChoose->bandlength; - if (pChoose->bandlength > 0) - pdev->frame_size = (pChoose->bandlength * pdev->height) / 4; - else - pdev->frame_size = (pdev->width * pdev->height * 12) / 8; - return 0; -} - - -static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, - int frames, int *compression, int send_to_cam) -{ - const struct Kiara_table_entry *pChoose = NULL; - int fps, ret = 0; - - if (size >= PSZ_MAX || *compression < 0 || *compression > 3) - return -EINVAL; - if (frames < 5) - frames = 5; - else if (size == PSZ_VGA && frames > 15) - frames = 15; - else if (frames > 30) - frames = 30; - fps = (frames / 5) - 1; - - /* Find a supported framerate with progressively higher compression */ - while (*compression <= 3) { - pChoose = &Kiara_table[size][fps][*compression]; - if (pChoose->alternate != 0) - break; - (*compression)++; - } - if (pChoose == NULL || pChoose->alternate == 0) - return -ENOENT; /* Not supported. */ - - /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ - if (send_to_cam) - ret = send_video_command(pdev, 4, pChoose->mode, 12); - if (ret < 0) - return ret; - - if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) - pwc_dec23_init(pdev, pChoose->mode); - - /* All set and go */ - pdev->pixfmt = pixfmt; - pdev->vframes = (fps + 1) * 5; - pdev->valternate = pChoose->alternate; - pdev->width = pwc_image_sizes[size][0]; - pdev->height = pwc_image_sizes[size][1]; - pdev->vbandlength = pChoose->bandlength; - if (pdev->vbandlength > 0) - pdev->frame_size = (pdev->vbandlength * pdev->height) / 4; - else - pdev->frame_size = (pdev->width * pdev->height * 12) / 8; - PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n", - pdev->frame_size, pdev->vframes, size, pdev->vbandlength); - return 0; -} - -int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, - int pixfmt, int frames, int *compression, int send_to_cam) -{ - int ret, size; - - PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", - width, height, frames, pixfmt); - size = pwc_get_size(pdev, width, height); - PWC_TRACE("decode_size = %d.\n", size); - - if (DEVICE_USE_CODEC1(pdev->type)) { - ret = set_video_mode_Nala(pdev, size, pixfmt, frames, - compression, send_to_cam); - } else if (DEVICE_USE_CODEC3(pdev->type)) { - ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, - compression, send_to_cam); - } else { - ret = set_video_mode_Timon(pdev, size, pixfmt, frames, - compression, send_to_cam); - } - if (ret < 0) { - PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); - return ret; - } - pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; - PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height); - return 0; -} - -static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) -{ - unsigned int i; - - for (i = 0; i < PWC_FPS_MAX_NALA; i++) { - if (Nala_table[size][i].alternate) { - if (index--==0) return Nala_fps_vector[i]; - } - } - return 0; -} - -static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) -{ - unsigned int i; - - for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { - if (Kiara_table[size][i][3].alternate) { - if (index--==0) return Kiara_fps_vector[i]; - } - } - return 0; -} - -static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) -{ - unsigned int i; - - for (i=0; i < PWC_FPS_MAX_TIMON; i++) { - if (Timon_table[size][i][3].alternate) { - if (index--==0) return Timon_fps_vector[i]; - } - } - return 0; -} - -unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) -{ - unsigned int ret; - - if (DEVICE_USE_CODEC1(pdev->type)) { - ret = pwc_get_fps_Nala(pdev, index, size); - - } else if (DEVICE_USE_CODEC3(pdev->type)) { - ret = pwc_get_fps_Kiara(pdev, index, size); - - } else { - ret = pwc_get_fps_Timon(pdev, index, size); - } - - return ret; -} - -int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) -{ - int ret; - - ret = recv_control_msg(pdev, request, value, 1); - if (ret < 0) - return ret; - - *data = pdev->ctrl_buf[0]; - return 0; -} - -int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) -{ - int ret; - - pdev->ctrl_buf[0] = data; - ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1); - if (ret < 0) - return ret; - - return 0; -} - -int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) -{ - int ret; - - ret = recv_control_msg(pdev, request, value, 1); - if (ret < 0) - return ret; - - *data = ((s8 *)pdev->ctrl_buf)[0]; - return 0; -} - -int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) -{ - int ret; - - ret = recv_control_msg(pdev, request, value, 2); - if (ret < 0) - return ret; - - *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; - return 0; -} - -int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) -{ - int ret; - - pdev->ctrl_buf[0] = data & 0xff; - pdev->ctrl_buf[1] = data >> 8; - ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2); - if (ret < 0) - return ret; - - return 0; -} - -int pwc_button_ctrl(struct pwc_device *pdev, u16 value) -{ - int ret; - - ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0); - if (ret < 0) - return ret; - - return 0; -} - -/* POWER */ -void pwc_camera_power(struct pwc_device *pdev, int power) -{ - int r; - - if (!pdev->power_save) - return; - - if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) - return; /* Not supported by Nala or Timon < release 6 */ - - if (power) - pdev->ctrl_buf[0] = 0x00; /* active */ - else - pdev->ctrl_buf[0] = 0xFF; /* power save */ - r = send_control_msg(pdev, SET_STATUS_CTL, - SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1); - if (r < 0) - PWC_ERROR("Failed to power %s camera (%d)\n", - power ? "on" : "off", r); -} - -int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) -{ - int r; - - if (pdev->type < 730) - return 0; - on_value /= 100; - off_value /= 100; - if (on_value < 0) - on_value = 0; - if (on_value > 0xff) - on_value = 0xff; - if (off_value < 0) - off_value = 0; - if (off_value > 0xff) - off_value = 0xff; - - pdev->ctrl_buf[0] = on_value; - pdev->ctrl_buf[1] = off_value; - - r = send_control_msg(pdev, - SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2); - if (r < 0) - PWC_ERROR("Failed to set LED on/off time (%d)\n", r); - - return r; -} - -#ifdef CONFIG_USB_PWC_DEBUG -int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) -{ - int ret = -1, request; - - if (pdev->type < 675) - request = SENSOR_TYPE_FORMATTER1; - else if (pdev->type < 730) - return -1; /* The Vesta series doesn't have this call */ - else - request = SENSOR_TYPE_FORMATTER2; - - ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1); - if (ret < 0) - return ret; - if (pdev->type < 675) - *sensor = pdev->ctrl_buf[0] | 0x100; - else - *sensor = pdev->ctrl_buf[0]; - return 0; -} -#endif diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/video/pwc/pwc-dec1.c deleted file mode 100644 index e899036aadf4..000000000000 --- a/drivers/media/video/pwc/pwc-dec1.c +++ /dev/null @@ -1,32 +0,0 @@ -/* Linux driver for Philips webcam - Decompression for chipset version 1 - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "pwc.h" - -void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd) -{ - struct pwc_dec1_private *pdec = &pdev->dec1; - - pdec->version = pdev->release; -} diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/video/pwc/pwc-dec1.h deleted file mode 100644 index c565ef8f52fb..000000000000 --- a/drivers/media/video/pwc/pwc-dec1.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Linux driver for Philips webcam - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef PWC_DEC1_H -#define PWC_DEC1_H - -#include - -struct pwc_device; - -struct pwc_dec1_private -{ - int version; -}; - -void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd); - -#endif diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c deleted file mode 100644 index 3792fedff951..000000000000 --- a/drivers/media/video/pwc/pwc-dec23.c +++ /dev/null @@ -1,691 +0,0 @@ -/* Linux driver for Philips webcam - Decompression for chipset version 2 et 3 - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -#include "pwc-timon.h" -#include "pwc-kiara.h" -#include "pwc-dec23.h" - -#include -#include - -/* - * USE_LOOKUP_TABLE_TO_CLAMP - * 0: use a C version of this tests: { a<0?0:(a>255?255:a) } - * 1: use a faster lookup table for cpu with a big cache (intel) - */ -#define USE_LOOKUP_TABLE_TO_CLAMP 1 -/* - * UNROLL_LOOP_FOR_COPYING_BLOCK - * 0: use a loop for a smaller code (but little slower) - * 1: when unrolling the loop, gcc produces some faster code (perhaps only - * valid for intel processor class). Activating this option, automaticaly - * activate USE_LOOKUP_TABLE_TO_CLAMP - */ -#define UNROLL_LOOP_FOR_COPY 1 -#if UNROLL_LOOP_FOR_COPY -# undef USE_LOOKUP_TABLE_TO_CLAMP -# define USE_LOOKUP_TABLE_TO_CLAMP 1 -#endif - -static void build_subblock_pattern(struct pwc_dec23_private *pdec) -{ - static const unsigned int initial_values[12] = { - -0x526500, -0x221200, 0x221200, 0x526500, - -0x3de200, 0x3de200, - -0x6db480, -0x2d5d00, 0x2d5d00, 0x6db480, - -0x12c200, 0x12c200 - - }; - static const unsigned int values_derivated[12] = { - 0xa4ca, 0x4424, -0x4424, -0xa4ca, - 0x7bc4, -0x7bc4, - 0xdb69, 0x5aba, -0x5aba, -0xdb69, - 0x2584, -0x2584 - }; - unsigned int temp_values[12]; - int i, j; - - memcpy(temp_values, initial_values, sizeof(initial_values)); - for (i = 0; i < 256; i++) { - for (j = 0; j < 12; j++) { - pdec->table_subblock[i][j] = temp_values[j]; - temp_values[j] += values_derivated[j]; - } - } -} - -static void build_bit_powermask_table(struct pwc_dec23_private *pdec) -{ - unsigned char *p; - unsigned int bit, byte, mask, val; - unsigned int bitpower = 1; - - for (bit = 0; bit < 8; bit++) { - mask = bitpower - 1; - p = pdec->table_bitpowermask[bit]; - for (byte = 0; byte < 256; byte++) { - val = (byte & mask); - if (byte & bitpower) - val = -val; - *p++ = val; - } - bitpower<<=1; - } -} - - -static void build_table_color(const unsigned int romtable[16][8], - unsigned char p0004[16][1024], - unsigned char p8004[16][256]) -{ - int compression_mode, j, k, bit, pw; - unsigned char *p0, *p8; - const unsigned int *r; - - /* We have 16 compressions tables */ - for (compression_mode = 0; compression_mode < 16; compression_mode++) { - p0 = p0004[compression_mode]; - p8 = p8004[compression_mode]; - r = romtable[compression_mode]; - - for (j = 0; j < 8; j++, r++, p0 += 128) { - - for (k = 0; k < 16; k++) { - if (k == 0) - bit = 1; - else if (k >= 1 && k < 3) - bit = (r[0] >> 15) & 7; - else if (k >= 3 && k < 6) - bit = (r[0] >> 12) & 7; - else if (k >= 6 && k < 10) - bit = (r[0] >> 9) & 7; - else if (k >= 10 && k < 13) - bit = (r[0] >> 6) & 7; - else if (k >= 13 && k < 15) - bit = (r[0] >> 3) & 7; - else - bit = (r[0]) & 7; - if (k == 0) - *p8++ = 8; - else - *p8++ = j - bit; - *p8++ = bit; - - pw = 1 << bit; - p0[k + 0x00] = (1 * pw) + 0x80; - p0[k + 0x10] = (2 * pw) + 0x80; - p0[k + 0x20] = (3 * pw) + 0x80; - p0[k + 0x30] = (4 * pw) + 0x80; - p0[k + 0x40] = (-1 * pw) + 0x80; - p0[k + 0x50] = (-2 * pw) + 0x80; - p0[k + 0x60] = (-3 * pw) + 0x80; - p0[k + 0x70] = (-4 * pw) + 0x80; - } /* end of for (k=0; k<16; k++, p8++) */ - } /* end of for (j=0; j<8; j++ , table++) */ - } /* end of foreach compression_mode */ -} - -/* - * - */ -static void fill_table_dc00_d800(struct pwc_dec23_private *pdec) -{ -#define SCALEBITS 15 -#define ONE_HALF (1UL << (SCALEBITS - 1)) - int i; - unsigned int offset1 = ONE_HALF; - unsigned int offset2 = 0x0000; - - for (i=0; i<256; i++) { - pdec->table_dc00[i] = offset1 & ~(ONE_HALF); - pdec->table_d800[i] = offset2; - - offset1 += 0x7bc4; - offset2 += 0x7bc4; - } -} - -/* - * To decode the stream: - * if look_bits(2) == 0: # op == 2 in the lookup table - * skip_bits(2) - * end of the stream - * elif look_bits(3) == 7: # op == 1 in the lookup table - * skip_bits(3) - * yyyy = get_bits(4) - * xxxx = get_bits(8) - * else: # op == 0 in the lookup table - * skip_bits(x) - * - * For speedup processing, we build a lookup table and we takes the first 6 bits. - * - * struct { - * unsigned char op; // operation to execute - * unsigned char bits; // bits use to perform operation - * unsigned char offset1; // offset to add to access in the table_0004 % 16 - * unsigned char offset2; // offset to add to access in the table_0004 - * } - * - * How to build this table ? - * op == 2 when (i%4)==0 - * op == 1 when (i%8)==7 - * op == 0 otherwise - * - */ -static const unsigned char hash_table_ops[64*4] = { - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x10, - 0x00, 0x06, 0x01, 0x30, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x01, 0x20, - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x50, - 0x00, 0x05, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x03, 0x00, - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x10, - 0x00, 0x06, 0x02, 0x10, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x01, 0x60, - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x50, - 0x00, 0x05, 0x02, 0x40, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x03, 0x40, - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x10, - 0x00, 0x06, 0x01, 0x70, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x01, 0x20, - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x50, - 0x00, 0x05, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x03, 0x00, - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x10, - 0x00, 0x06, 0x02, 0x50, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x01, 0x60, - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x00, - 0x00, 0x04, 0x01, 0x50, - 0x00, 0x05, 0x02, 0x40, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x40, - 0x00, 0x05, 0x03, 0x40, - 0x01, 0x00, 0x00, 0x00 -}; - -/* - * - */ -static const unsigned int MulIdx[16][16] = { - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, - {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,}, - {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,}, - {4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,}, - {6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6,}, - {4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4,}, - {1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2,}, - {0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3,}, - {0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3,}, - {1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2,}, - {7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7,}, - {4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4,}, - {7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7,}, - {1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2,}, - {1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2,}, - {10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10} -}; - -#if USE_LOOKUP_TABLE_TO_CLAMP -#define MAX_OUTER_CROP_VALUE (512) -static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; -#define CLAMP(x) (pwc_crop_table[MAX_OUTER_CROP_VALUE+(x)]) -#else -#define CLAMP(x) ((x)>255?255:((x)<0?0:x)) -#endif - - -/* If the type or the command change, we rebuild the lookup table */ -void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd) -{ - int flags, version, shift, i; - struct pwc_dec23_private *pdec = &pdev->dec23; - - mutex_init(&pdec->lock); - - if (pdec->last_cmd_valid && pdec->last_cmd == cmd[2]) - return; - - if (DEVICE_USE_CODEC3(pdev->type)) { - flags = cmd[2] & 0x18; - if (flags == 8) - pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */ - else if (flags == 0x10) - pdec->nbits = 8; - else - pdec->nbits = 6; - - version = cmd[2] >> 5; - build_table_color(KiaraRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); - build_table_color(KiaraRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); - - } else { - - flags = cmd[2] & 6; - if (flags == 2) - pdec->nbits = 7; - else if (flags == 4) - pdec->nbits = 8; - else - pdec->nbits = 6; - - version = cmd[2] >> 3; - build_table_color(TimonRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); - build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); - } - - /* Informations can be coded on a variable number of bits but never less than 8 */ - shift = 8 - pdec->nbits; - pdec->scalebits = SCALEBITS - shift; - pdec->nbitsmask = 0xFF >> shift; - - fill_table_dc00_d800(pdec); - build_subblock_pattern(pdec); - build_bit_powermask_table(pdec); - -#if USE_LOOKUP_TABLE_TO_CLAMP - /* Build the static table to clamp value [0-255] */ - for (i=0;ilast_cmd = cmd[2]; - pdec->last_cmd_valid = 1; -} - -/* - * Copy the 4x4 image block to Y plane buffer - */ -static void copy_image_block_Y(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) -{ -#if UNROLL_LOOP_FOR_COPY - const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; - const int *c = src; - unsigned char *d = dst; - - *d++ = cm[c[0] >> scalebits]; - *d++ = cm[c[1] >> scalebits]; - *d++ = cm[c[2] >> scalebits]; - *d++ = cm[c[3] >> scalebits]; - - d = dst + bytes_per_line; - *d++ = cm[c[4] >> scalebits]; - *d++ = cm[c[5] >> scalebits]; - *d++ = cm[c[6] >> scalebits]; - *d++ = cm[c[7] >> scalebits]; - - d = dst + bytes_per_line*2; - *d++ = cm[c[8] >> scalebits]; - *d++ = cm[c[9] >> scalebits]; - *d++ = cm[c[10] >> scalebits]; - *d++ = cm[c[11] >> scalebits]; - - d = dst + bytes_per_line*3; - *d++ = cm[c[12] >> scalebits]; - *d++ = cm[c[13] >> scalebits]; - *d++ = cm[c[14] >> scalebits]; - *d++ = cm[c[15] >> scalebits]; -#else - int i; - const int *c = src; - unsigned char *d = dst; - for (i = 0; i < 4; i++, c++) - *d++ = CLAMP((*c) >> scalebits); - - d = dst + bytes_per_line; - for (i = 0; i < 4; i++, c++) - *d++ = CLAMP((*c) >> scalebits); - - d = dst + bytes_per_line*2; - for (i = 0; i < 4; i++, c++) - *d++ = CLAMP((*c) >> scalebits); - - d = dst + bytes_per_line*3; - for (i = 0; i < 4; i++, c++) - *d++ = CLAMP((*c) >> scalebits); -#endif -} - -/* - * Copy the 4x4 image block to a CrCb plane buffer - * - */ -static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) -{ -#if UNROLL_LOOP_FOR_COPY - /* Unroll all loops */ - const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; - const int *c = src; - unsigned char *d = dst; - - *d++ = cm[c[0] >> scalebits]; - *d++ = cm[c[4] >> scalebits]; - *d++ = cm[c[1] >> scalebits]; - *d++ = cm[c[5] >> scalebits]; - *d++ = cm[c[2] >> scalebits]; - *d++ = cm[c[6] >> scalebits]; - *d++ = cm[c[3] >> scalebits]; - *d++ = cm[c[7] >> scalebits]; - - d = dst + bytes_per_line; - *d++ = cm[c[12] >> scalebits]; - *d++ = cm[c[8] >> scalebits]; - *d++ = cm[c[13] >> scalebits]; - *d++ = cm[c[9] >> scalebits]; - *d++ = cm[c[14] >> scalebits]; - *d++ = cm[c[10] >> scalebits]; - *d++ = cm[c[15] >> scalebits]; - *d++ = cm[c[11] >> scalebits]; -#else - int i; - const int *c1 = src; - const int *c2 = src + 4; - unsigned char *d = dst; - - for (i = 0; i < 4; i++, c1++, c2++) { - *d++ = CLAMP((*c1) >> scalebits); - *d++ = CLAMP((*c2) >> scalebits); - } - c1 = src + 12; - d = dst + bytes_per_line; - for (i = 0; i < 4; i++, c1++, c2++) { - *d++ = CLAMP((*c1) >> scalebits); - *d++ = CLAMP((*c2) >> scalebits); - } -#endif -} - -/* - * To manage the stream, we keep bits in a 32 bits register. - * fill_nbits(n): fill the reservoir with at least n bits - * skip_bits(n): discard n bits from the reservoir - * get_bits(n): fill the reservoir, returns the first n bits and discard the - * bits from the reservoir. - * __get_nbits(n): faster version of get_bits(n), but asumes that the reservoir - * contains at least n bits. bits returned is discarded. - */ -#define fill_nbits(pdec, nbits_wanted) do { \ - while (pdec->nbits_in_reservoir<(nbits_wanted)) \ - { \ - pdec->reservoir |= (*(pdec->stream)++) << (pdec->nbits_in_reservoir); \ - pdec->nbits_in_reservoir += 8; \ - } \ -} while(0); - -#define skip_nbits(pdec, nbits_to_skip) do { \ - pdec->reservoir >>= (nbits_to_skip); \ - pdec->nbits_in_reservoir -= (nbits_to_skip); \ -} while(0); - -#define get_nbits(pdec, nbits_wanted, result) do { \ - fill_nbits(pdec, nbits_wanted); \ - result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ - skip_nbits(pdec, nbits_wanted); \ -} while(0); - -#define __get_nbits(pdec, nbits_wanted, result) do { \ - result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ - skip_nbits(pdec, nbits_wanted); \ -} while(0); - -#define look_nbits(pdec, nbits_wanted) \ - ((pdec->reservoir) & ((1U<<(nbits_wanted))-1)) - -/* - * Decode a 4x4 pixel block - */ -static void decode_block(struct pwc_dec23_private *pdec, - const unsigned char *ptable0004, - const unsigned char *ptable8004) -{ - unsigned int primary_color; - unsigned int channel_v, offset1, op; - int i; - - fill_nbits(pdec, 16); - __get_nbits(pdec, pdec->nbits, primary_color); - - if (look_nbits(pdec,2) == 0) { - skip_nbits(pdec, 2); - /* Very simple, the color is the same for all pixels of the square */ - for (i = 0; i < 16; i++) - pdec->temp_colors[i] = pdec->table_dc00[primary_color]; - - return; - } - - /* This block is encoded with small pattern */ - for (i = 0; i < 16; i++) - pdec->temp_colors[i] = pdec->table_d800[primary_color]; - - __get_nbits(pdec, 3, channel_v); - channel_v = ((channel_v & 1) << 2) | (channel_v & 2) | ((channel_v & 4) >> 2); - - ptable0004 += (channel_v * 128); - ptable8004 += (channel_v * 32); - - offset1 = 0; - do - { - unsigned int htable_idx, rows = 0; - const unsigned int *block; - - /* [ zzzz y x x ] - * xx == 00 :=> end of the block def, remove the two bits from the stream - * yxx == 111 - * yxx == any other value - * - */ - fill_nbits(pdec, 16); - htable_idx = look_nbits(pdec, 6); - op = hash_table_ops[htable_idx * 4]; - - if (op == 2) { - skip_nbits(pdec, 2); - - } else if (op == 1) { - /* 15bits [ xxxx xxxx yyyy 111 ] - * yyy => offset in the table8004 - * xxx => offset in the tabled004 (tree) - */ - unsigned int mask, shift; - unsigned int nbits, col1; - unsigned int yyyy; - - skip_nbits(pdec, 3); - /* offset1 += yyyy */ - __get_nbits(pdec, 4, yyyy); - offset1 += 1 + yyyy; - offset1 &= 0x0F; - nbits = ptable8004[offset1 * 2]; - - /* col1 = xxxx xxxx */ - __get_nbits(pdec, nbits+1, col1); - - /* Bit mask table */ - mask = pdec->table_bitpowermask[nbits][col1]; - shift = ptable8004[offset1 * 2 + 1]; - rows = ((mask << shift) + 0x80) & 0xFF; - - block = pdec->table_subblock[rows]; - for (i = 0; i < 16; i++) - pdec->temp_colors[i] += block[MulIdx[offset1][i]]; - - } else { - /* op == 0 - * offset1 is coded on 3 bits - */ - unsigned int shift; - - offset1 += hash_table_ops [htable_idx * 4 + 2]; - offset1 &= 0x0F; - - rows = ptable0004[offset1 + hash_table_ops [htable_idx * 4 + 3]]; - block = pdec->table_subblock[rows]; - for (i = 0; i < 16; i++) - pdec->temp_colors[i] += block[MulIdx[offset1][i]]; - - shift = hash_table_ops[htable_idx * 4 + 1]; - skip_nbits(pdec, shift); - } - - } while (op != 2); - -} - -static void DecompressBand23(struct pwc_dec23_private *pdec, - const unsigned char *rawyuv, - unsigned char *planar_y, - unsigned char *planar_u, - unsigned char *planar_v, - unsigned int compressed_image_width, - unsigned int real_image_width) -{ - int compression_index, nblocks; - const unsigned char *ptable0004; - const unsigned char *ptable8004; - - pdec->reservoir = 0; - pdec->nbits_in_reservoir = 0; - pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ - - get_nbits(pdec, 4, compression_index); - - /* pass 1: uncompress Y component */ - nblocks = compressed_image_width / 4; - - ptable0004 = pdec->table_0004_pass1[compression_index]; - ptable8004 = pdec->table_8004_pass1[compression_index]; - - /* Each block decode a square of 4x4 */ - while (nblocks) { - decode_block(pdec, ptable0004, ptable8004); - copy_image_block_Y(pdec->temp_colors, planar_y, real_image_width, pdec->scalebits); - planar_y += 4; - nblocks--; - } - - /* pass 2: uncompress UV component */ - nblocks = compressed_image_width / 8; - - ptable0004 = pdec->table_0004_pass2[compression_index]; - ptable8004 = pdec->table_8004_pass2[compression_index]; - - /* Each block decode a square of 4x4 */ - while (nblocks) { - decode_block(pdec, ptable0004, ptable8004); - copy_image_block_CrCb(pdec->temp_colors, planar_u, real_image_width/2, pdec->scalebits); - - decode_block(pdec, ptable0004, ptable8004); - copy_image_block_CrCb(pdec->temp_colors, planar_v, real_image_width/2, pdec->scalebits); - - planar_v += 8; - planar_u += 8; - nblocks -= 2; - } - -} - -/** - * - * Uncompress a pwc23 buffer. - * - * src: raw data - * dst: image output - */ -void pwc_dec23_decompress(struct pwc_device *pdev, - const void *src, - void *dst) -{ - int bandlines_left, bytes_per_block; - struct pwc_dec23_private *pdec = &pdev->dec23; - - /* YUV420P image format */ - unsigned char *pout_planar_y; - unsigned char *pout_planar_u; - unsigned char *pout_planar_v; - unsigned int plane_size; - - mutex_lock(&pdec->lock); - - bandlines_left = pdev->height / 4; - bytes_per_block = pdev->width * 4; - plane_size = pdev->height * pdev->width; - - pout_planar_y = dst; - pout_planar_u = dst + plane_size; - pout_planar_v = dst + plane_size + plane_size / 4; - - while (bandlines_left--) { - DecompressBand23(pdec, src, - pout_planar_y, pout_planar_u, pout_planar_v, - pdev->width, pdev->width); - src += pdev->vbandlength; - pout_planar_y += bytes_per_block; - pout_planar_u += pdev->width; - pout_planar_v += pdev->width; - } - mutex_unlock(&pdec->lock); -} diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h deleted file mode 100644 index c655b1c1e6a9..000000000000 --- a/drivers/media/video/pwc/pwc-dec23.h +++ /dev/null @@ -1,61 +0,0 @@ -/* Linux driver for Philips webcam - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef PWC_DEC23_H -#define PWC_DEC23_H - -struct pwc_device; - -struct pwc_dec23_private -{ - struct mutex lock; - - unsigned char last_cmd, last_cmd_valid; - - unsigned int scalebits; - unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */ - - unsigned int reservoir; - unsigned int nbits_in_reservoir; - - const unsigned char *stream; - int temp_colors[16]; - - unsigned char table_0004_pass1[16][1024]; - unsigned char table_0004_pass2[16][1024]; - unsigned char table_8004_pass1[16][256]; - unsigned char table_8004_pass2[16][256]; - unsigned int table_subblock[256][12]; - - unsigned char table_bitpowermask[8][256]; - unsigned int table_d800[256]; - unsigned int table_dc00[256]; - -}; - -void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd); -void pwc_dec23_decompress(struct pwc_device *pdev, - const void *src, - void *dst); -#endif diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c deleted file mode 100644 index de7c7ba99ef4..000000000000 --- a/drivers/media/video/pwc/pwc-if.c +++ /dev/null @@ -1,1165 +0,0 @@ -/* Linux driver for Philips webcam - USB and Video4Linux interface part. - (C) 1999-2004 Nemosoft Unv. - (C) 2004-2006 Luc Saillard (luc@saillard.org) - (C) 2011 Hans de Goede - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -/* - This code forms the interface between the USB layers and the Philips - specific stuff. Some adanved stuff of the driver falls under an - NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and - is thus not distributed in source form. The binary pwcx.o module - contains the code that falls under the NDA. - - In case you're wondering: 'pwc' stands for "Philips WebCam", but - I really didn't want to type 'philips_web_cam' every time (I'm lazy as - any Linux kernel hacker, but I don't like uncomprehensible abbreviations - without explanation). - - Oh yes, convention: to disctinguish between all the various pointers to - device-structures, I use these names for the pointer variables: - udev: struct usb_device * - vdev: struct video_device (member of pwc_dev) - pdev: struct pwc_devive * -*/ - -/* Contributors: - - Alvarado: adding whitebalance code - - Alistar Moire: QuickCam 3000 Pro device/product ID - - Tony Hoyle: Creative Labs Webcam 5 device/product ID - - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged - - Jk Fang: Sotec Afina Eye ID - - Xavier Roche: QuickCam Pro 4000 ID - - Jens Knudsen: QuickCam Zoom ID - - J. Debert: QuickCam for Notebooks ID - - Pham Thanh Nam: webcam snapshot button as an event input device -*/ - -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_USB_PWC_INPUT_EVDEV -#include -#endif -#include -#include -#include /* simple_strtol() */ - -#include "pwc.h" -#include "pwc-kiara.h" -#include "pwc-timon.h" -#include "pwc-dec23.h" -#include "pwc-dec1.h" - -/* Function prototypes and driver templates */ - -/* hotplug device table support */ -static const struct usb_device_id pwc_device_table [] = { - { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ - { USB_DEVICE(0x0471, 0x0303) }, - { USB_DEVICE(0x0471, 0x0304) }, - { USB_DEVICE(0x0471, 0x0307) }, - { USB_DEVICE(0x0471, 0x0308) }, - { USB_DEVICE(0x0471, 0x030C) }, - { USB_DEVICE(0x0471, 0x0310) }, - { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */ - { USB_DEVICE(0x0471, 0x0312) }, - { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ - { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */ - { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ - { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ - { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ - { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ - { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ - { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ - { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ - { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */ - { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */ - { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ - { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ - { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ - { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */ - { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ - { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ - { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ - { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ - { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ - { USB_DEVICE(0x0d81, 0x1900) }, - { } -}; -MODULE_DEVICE_TABLE(usb, pwc_device_table); - -static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); -static void usb_pwc_disconnect(struct usb_interface *intf); -static void pwc_isoc_cleanup(struct pwc_device *pdev); - -static struct usb_driver pwc_driver = { - .name = "Philips webcam", /* name */ - .id_table = pwc_device_table, - .probe = usb_pwc_probe, /* probe() */ - .disconnect = usb_pwc_disconnect, /* disconnect() */ -}; - -#define MAX_DEV_HINTS 20 -#define MAX_ISOC_ERRORS 20 - -#ifdef CONFIG_USB_PWC_DEBUG - int pwc_trace = PWC_DEBUG_LEVEL; -#endif -static int power_save = -1; -static int leds[2] = { 100, 0 }; - -/***/ - -static const struct v4l2_file_operations pwc_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; -static struct video_device pwc_template = { - .name = "Philips Webcam", /* Filled in later */ - .release = video_device_release_empty, - .fops = &pwc_fops, - .ioctl_ops = &pwc_ioctl_ops, -}; - -/***************************************************************************/ -/* Private functions */ - -struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev) -{ - unsigned long flags = 0; - struct pwc_frame_buf *buf = NULL; - - spin_lock_irqsave(&pdev->queued_bufs_lock, flags); - if (list_empty(&pdev->queued_bufs)) - goto leave; - - buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list); - list_del(&buf->list); -leave: - spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); - return buf; -} - -static void pwc_snapshot_button(struct pwc_device *pdev, int down) -{ - if (down) { - PWC_TRACE("Snapshot button pressed.\n"); - } else { - PWC_TRACE("Snapshot button released.\n"); - } - -#ifdef CONFIG_USB_PWC_INPUT_EVDEV - if (pdev->button_dev) { - input_report_key(pdev->button_dev, KEY_CAMERA, down); - input_sync(pdev->button_dev); - } -#endif -} - -static void pwc_frame_complete(struct pwc_device *pdev) -{ - struct pwc_frame_buf *fbuf = pdev->fill_buf; - - /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus - frames on the USB wire after an exposure change. This conditition is - however detected in the cam and a bit is set in the header. - */ - if (pdev->type == 730) { - unsigned char *ptr = (unsigned char *)fbuf->data; - - if (ptr[1] == 1 && ptr[0] & 0x10) { - PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n"); - pdev->drop_frames += 2; - } - if ((ptr[0] ^ pdev->vmirror) & 0x01) { - pwc_snapshot_button(pdev, ptr[0] & 0x01); - } - if ((ptr[0] ^ pdev->vmirror) & 0x02) { - if (ptr[0] & 0x02) - PWC_TRACE("Image is mirrored.\n"); - else - PWC_TRACE("Image is normal.\n"); - } - pdev->vmirror = ptr[0] & 0x03; - /* Sometimes the trailer of the 730 is still sent as a 4 byte packet - after a short frame; this condition is filtered out specifically. A 4 byte - frame doesn't make sense anyway. - So we get either this sequence: - drop_bit set -> 4 byte frame -> short frame -> good frame - Or this one: - drop_bit set -> short frame -> good frame - So we drop either 3 or 2 frames in all! - */ - if (fbuf->filled == 4) - pdev->drop_frames++; - } else if (pdev->type == 740 || pdev->type == 720) { - unsigned char *ptr = (unsigned char *)fbuf->data; - if ((ptr[0] ^ pdev->vmirror) & 0x01) { - pwc_snapshot_button(pdev, ptr[0] & 0x01); - } - pdev->vmirror = ptr[0] & 0x03; - } - - /* In case we were instructed to drop the frame, do so silently. */ - if (pdev->drop_frames > 0) { - pdev->drop_frames--; - } else { - /* Check for underflow first */ - if (fbuf->filled < pdev->frame_total_size) { - PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);" - " discarded.\n", fbuf->filled); - } else { - fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE; - fbuf->vb.v4l2_buf.sequence = pdev->vframe_count; - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); - pdev->fill_buf = NULL; - pdev->vsync = 0; - } - } /* !drop_frames */ - pdev->vframe_count++; -} - -/* This gets called for the Isochronous pipe (video). This is done in - * interrupt time, so it has to be fast, not crash, and not stall. Neat. - */ -static void pwc_isoc_handler(struct urb *urb) -{ - struct pwc_device *pdev = (struct pwc_device *)urb->context; - int i, fst, flen; - unsigned char *iso_buf = NULL; - - if (urb->status == -ENOENT || urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN) { - PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); - return; - } - - if (pdev->fill_buf == NULL) - pdev->fill_buf = pwc_get_next_fill_buf(pdev); - - if (urb->status != 0) { - const char *errmsg; - - errmsg = "Unknown"; - switch(urb->status) { - case -ENOSR: errmsg = "Buffer error (overrun)"; break; - case -EPIPE: errmsg = "Stalled (device not responding)"; break; - case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; - case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; - case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; - case -ETIME: errmsg = "Device does not respond"; break; - } - PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n", - urb->status, errmsg); - /* Give up after a number of contiguous errors */ - if (++pdev->visoc_errors > MAX_ISOC_ERRORS) - { - PWC_ERROR("Too many ISOC errors, bailing out.\n"); - if (pdev->fill_buf) { - vb2_buffer_done(&pdev->fill_buf->vb, - VB2_BUF_STATE_ERROR); - pdev->fill_buf = NULL; - } - } - pdev->vsync = 0; /* Drop the current frame */ - goto handler_end; - } - - /* Reset ISOC error counter. We did get here, after all. */ - pdev->visoc_errors = 0; - - /* vsync: 0 = don't copy data - 1 = sync-hunt - 2 = synched - */ - /* Compact data */ - for (i = 0; i < urb->number_of_packets; i++) { - fst = urb->iso_frame_desc[i].status; - flen = urb->iso_frame_desc[i].actual_length; - iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - if (fst != 0) { - PWC_ERROR("Iso frame %d has error %d\n", i, fst); - continue; - } - if (flen > 0 && pdev->vsync) { - struct pwc_frame_buf *fbuf = pdev->fill_buf; - - if (pdev->vsync == 1) { - do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp); - pdev->vsync = 2; - } - - if (flen + fbuf->filled > pdev->frame_total_size) { - PWC_ERROR("Frame overflow (%d > %d)\n", - flen + fbuf->filled, - pdev->frame_total_size); - pdev->vsync = 0; /* Let's wait for an EOF */ - } else { - memcpy(fbuf->data + fbuf->filled, iso_buf, - flen); - fbuf->filled += flen; - } - } - if (flen < pdev->vlast_packet_size) { - /* Shorter packet... end of frame */ - if (pdev->vsync == 2) - pwc_frame_complete(pdev); - if (pdev->fill_buf == NULL) - pdev->fill_buf = pwc_get_next_fill_buf(pdev); - if (pdev->fill_buf) { - pdev->fill_buf->filled = 0; - pdev->vsync = 1; - } - } - pdev->vlast_packet_size = flen; - } - -handler_end: - i = usb_submit_urb(urb, GFP_ATOMIC); - if (i != 0) - PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); -} - -/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static int pwc_isoc_init(struct pwc_device *pdev) -{ - struct usb_device *udev; - struct urb *urb; - int i, j, ret; - struct usb_interface *intf; - struct usb_host_interface *idesc = NULL; - int compression = 0; /* 0..3 = uncompressed..high */ - - pdev->vsync = 0; - pdev->vlast_packet_size = 0; - pdev->fill_buf = NULL; - pdev->vframe_count = 0; - pdev->visoc_errors = 0; - udev = pdev->udev; - -retry: - /* We first try with low compression and then retry with a higher - compression setting if there is not enough bandwidth. */ - ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, - pdev->vframes, &compression, 1); - - /* Get the current alternate interface, adjust packet size */ - intf = usb_ifnum_to_if(udev, 0); - if (intf) - idesc = usb_altnum_to_altsetting(intf, pdev->valternate); - if (!idesc) - return -EIO; - - /* Search video endpoint */ - pdev->vmax_packet_size = -1; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) { - if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { - pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize); - break; - } - } - - if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { - PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n"); - return -ENFILE; /* Odd error, that should be noticeable */ - } - - /* Set alternate interface */ - PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate); - ret = usb_set_interface(pdev->udev, 0, pdev->valternate); - if (ret == -ENOSPC && compression < 3) { - compression++; - goto retry; - } - if (ret < 0) - return ret; - - /* Allocate and init Isochronuous urbs */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); - if (urb == NULL) { - PWC_ERROR("Failed to allocate urb %d\n", i); - pwc_isoc_cleanup(pdev); - return -ENOMEM; - } - pdev->urbs[i] = urb; - PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb); - - urb->interval = 1; // devik - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->transfer_buffer = usb_alloc_coherent(udev, - ISO_BUFFER_SIZE, - GFP_KERNEL, - &urb->transfer_dma); - if (urb->transfer_buffer == NULL) { - PWC_ERROR("Failed to allocate urb buffer %d\n", i); - pwc_isoc_cleanup(pdev); - return -ENOMEM; - } - urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = pwc_isoc_handler; - urb->context = pdev; - urb->start_frame = 0; - urb->number_of_packets = ISO_FRAMES_PER_DESC; - for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { - urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; - urb->iso_frame_desc[j].length = pdev->vmax_packet_size; - } - } - - /* link */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL); - if (ret == -ENOSPC && compression < 3) { - compression++; - pwc_isoc_cleanup(pdev); - goto retry; - } - if (ret) { - PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); - pwc_isoc_cleanup(pdev); - return ret; - } - PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]); - } - - /* All is done... */ - PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); - return 0; -} - -static void pwc_iso_stop(struct pwc_device *pdev) -{ - int i; - - /* Unlinking ISOC buffers one by one */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (pdev->urbs[i]) { - PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]); - usb_kill_urb(pdev->urbs[i]); - } - } -} - -static void pwc_iso_free(struct pwc_device *pdev) -{ - int i; - - /* Freeing ISOC buffers one by one */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (pdev->urbs[i]) { - PWC_DEBUG_MEMORY("Freeing URB\n"); - if (pdev->urbs[i]->transfer_buffer) { - usb_free_coherent(pdev->udev, - pdev->urbs[i]->transfer_buffer_length, - pdev->urbs[i]->transfer_buffer, - pdev->urbs[i]->transfer_dma); - } - usb_free_urb(pdev->urbs[i]); - pdev->urbs[i] = NULL; - } - } -} - -/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static void pwc_isoc_cleanup(struct pwc_device *pdev) -{ - PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); - - pwc_iso_stop(pdev); - pwc_iso_free(pdev); - usb_set_interface(pdev->udev, 0, 0); - - PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); -} - -/* Must be called with vb_queue_lock hold */ -static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) -{ - unsigned long flags = 0; - - spin_lock_irqsave(&pdev->queued_bufs_lock, flags); - while (!list_empty(&pdev->queued_bufs)) { - struct pwc_frame_buf *buf; - - buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, - list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - } - spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); -} - -#ifdef CONFIG_USB_PWC_DEBUG -static const char *pwc_sensor_type_to_string(unsigned int sensor_type) -{ - switch(sensor_type) { - case 0x00: - return "Hyundai CMOS sensor"; - case 0x20: - return "Sony CCD sensor + TDA8787"; - case 0x2E: - return "Sony CCD sensor + Exas 98L59"; - case 0x2F: - return "Sony CCD sensor + ADI 9804"; - case 0x30: - return "Sharp CCD sensor + TDA8787"; - case 0x3E: - return "Sharp CCD sensor + Exas 98L59"; - case 0x3F: - return "Sharp CCD sensor + ADI 9804"; - case 0x40: - return "UPA 1021 sensor"; - case 0x100: - return "VGA sensor"; - case 0x101: - return "PAL MR sensor"; - default: - return "unknown type of sensor"; - } -} -#endif - -/***************************************************************************/ -/* Video4Linux functions */ - -static void pwc_video_release(struct v4l2_device *v) -{ - struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); - - v4l2_ctrl_handler_free(&pdev->ctrl_handler); - v4l2_device_unregister(&pdev->v4l2_dev); - kfree(pdev->ctrl_buf); - kfree(pdev); -} - -/***************************************************************************/ -/* Videobuf2 operations */ - -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vq); - int size; - - if (*nbuffers < MIN_FRAMES) - *nbuffers = MIN_FRAMES; - else if (*nbuffers > MAX_FRAMES) - *nbuffers = MAX_FRAMES; - - *nplanes = 1; - - size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT); - sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] * - pwc_image_sizes[size][1] * 3 / 2); - - return 0; -} - -static int buffer_init(struct vb2_buffer *vb) -{ - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); - - /* need vmalloc since frame buffer > 128K */ - buf->data = vzalloc(PWC_FRAME_SIZE); - if (buf->data == NULL) - return -ENOMEM; - - return 0; -} - -static int buffer_prepare(struct vb2_buffer *vb) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); - - /* Don't allow queing new buffers after device disconnection */ - if (!pdev->udev) - return -ENODEV; - - return 0; -} - -static int buffer_finish(struct vb2_buffer *vb) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); - - /* - * Application has called dqbuf and is getting back a buffer we've - * filled, take the pwc data we've stored in buf->data and decompress - * it into a usable format, storing the result in the vb2_buffer - */ - return pwc_decompress(pdev, buf); -} - -static void buffer_cleanup(struct vb2_buffer *vb) -{ - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); - - vfree(buf->data); -} - -static void buffer_queue(struct vb2_buffer *vb) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); - unsigned long flags = 0; - - /* Check the device has not disconnected between prep and queuing */ - if (!pdev->udev) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - return; - } - - spin_lock_irqsave(&pdev->queued_bufs_lock, flags); - list_add_tail(&buf->list, &pdev->queued_bufs); - spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); -} - -static int start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vq); - int r; - - if (!pdev->udev) - return -ENODEV; - - if (mutex_lock_interruptible(&pdev->v4l2_lock)) - return -ERESTARTSYS; - /* Turn on camera and set LEDS on */ - pwc_camera_power(pdev, 1); - pwc_set_leds(pdev, leds[0], leds[1]); - - r = pwc_isoc_init(pdev); - if (r) { - /* If we failed turn camera and LEDS back off */ - pwc_set_leds(pdev, 0, 0); - pwc_camera_power(pdev, 0); - /* And cleanup any queued bufs!! */ - pwc_cleanup_queued_bufs(pdev); - } - mutex_unlock(&pdev->v4l2_lock); - - return r; -} - -static int stop_streaming(struct vb2_queue *vq) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vq); - - if (mutex_lock_interruptible(&pdev->v4l2_lock)) - return -ERESTARTSYS; - if (pdev->udev) { - pwc_set_leds(pdev, 0, 0); - pwc_camera_power(pdev, 0); - pwc_isoc_cleanup(pdev); - } - - pwc_cleanup_queued_bufs(pdev); - mutex_unlock(&pdev->v4l2_lock); - - return 0; -} - -static struct vb2_ops pwc_vb_queue_ops = { - .queue_setup = queue_setup, - .buf_init = buffer_init, - .buf_prepare = buffer_prepare, - .buf_finish = buffer_finish, - .buf_cleanup = buffer_cleanup, - .buf_queue = buffer_queue, - .start_streaming = start_streaming, - .stop_streaming = stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -/***************************************************************************/ -/* USB functions */ - -/* This function gets called when a new device is plugged in or the usb core - * is loaded. - */ - -static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct pwc_device *pdev = NULL; - int vendor_id, product_id, type_id; - int rc; - int features = 0; - int compression = 0; - int my_power_save = power_save; - char serial_number[30], *name; - - vendor_id = le16_to_cpu(udev->descriptor.idVendor); - product_id = le16_to_cpu(udev->descriptor.idProduct); - - /* Check if we can handle this device */ - PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n", - vendor_id, product_id, - intf->altsetting->desc.bInterfaceNumber); - - /* the interfaces are probed one by one. We are only interested in the - video interface (0) now. - Interface 1 is the Audio Control, and interface 2 Audio itself. - */ - if (intf->altsetting->desc.bInterfaceNumber > 0) - return -ENODEV; - - if (vendor_id == 0x0471) { - switch (product_id) { - case 0x0302: - PWC_INFO("Philips PCA645VC USB webcam detected.\n"); - name = "Philips 645 webcam"; - type_id = 645; - break; - case 0x0303: - PWC_INFO("Philips PCA646VC USB webcam detected.\n"); - name = "Philips 646 webcam"; - type_id = 646; - break; - case 0x0304: - PWC_INFO("Askey VC010 type 2 USB webcam detected.\n"); - name = "Askey VC010 webcam"; - type_id = 646; - break; - case 0x0307: - PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n"); - name = "Philips 675 webcam"; - type_id = 675; - break; - case 0x0308: - PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); - name = "Philips 680 webcam"; - type_id = 680; - break; - case 0x030C: - PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); - name = "Philips 690 webcam"; - type_id = 690; - break; - case 0x0310: - PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); - name = "Philips 730 webcam"; - type_id = 730; - break; - case 0x0311: - PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); - name = "Philips 740 webcam"; - type_id = 740; - break; - case 0x0312: - PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); - name = "Philips 750 webcam"; - type_id = 750; - break; - case 0x0313: - PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); - name = "Philips 720K/40 webcam"; - type_id = 720; - break; - case 0x0329: - PWC_INFO("Philips SPC 900NC USB webcam detected.\n"); - name = "Philips SPC 900NC webcam"; - type_id = 740; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x069A) { - switch(product_id) { - case 0x0001: - PWC_INFO("Askey VC010 type 1 USB webcam detected.\n"); - name = "Askey VC010 webcam"; - type_id = 645; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x046d) { - switch(product_id) { - case 0x08b0: - PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n"); - name = "Logitech QuickCam Pro 3000"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b1: - PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n"); - name = "Logitech QuickCam Notebook Pro"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b2: - PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n"); - name = "Logitech QuickCam Pro 4000"; - type_id = 740; /* CCD sensor */ - if (my_power_save == -1) - my_power_save = 1; - break; - case 0x08b3: - PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n"); - name = "Logitech QuickCam Zoom"; - type_id = 740; /* CCD sensor */ - break; - case 0x08B4: - PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); - name = "Logitech QuickCam Zoom"; - type_id = 740; /* CCD sensor */ - if (my_power_save == -1) - my_power_save = 1; - break; - case 0x08b5: - PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); - name = "Logitech QuickCam Orbit"; - type_id = 740; /* CCD sensor */ - if (my_power_save == -1) - my_power_save = 1; - features |= FEATURE_MOTOR_PANTILT; - break; - case 0x08b6: - PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n"); - name = "Cisco VT Camera"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b7: - PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n"); - name = "Logitech ViewPort AV 100"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b8: /* Where this released? */ - PWC_INFO("Logitech QuickCam detected (reserved ID).\n"); - name = "Logitech QuickCam (res.)"; - type_id = 730; /* Assuming CMOS */ - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x055d) { - /* I don't know the difference between the C10 and the C30; - I suppose the difference is the sensor, but both cameras - work equally well with a type_id of 675 - */ - switch(product_id) { - case 0x9000: - PWC_INFO("Samsung MPC-C10 USB webcam detected.\n"); - name = "Samsung MPC-C10"; - type_id = 675; - break; - case 0x9001: - PWC_INFO("Samsung MPC-C30 USB webcam detected.\n"); - name = "Samsung MPC-C30"; - type_id = 675; - break; - case 0x9002: - PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n"); - name = "Samsung MPC-C30"; - type_id = 740; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x041e) { - switch(product_id) { - case 0x400c: - PWC_INFO("Creative Labs Webcam 5 detected.\n"); - name = "Creative Labs Webcam 5"; - type_id = 730; - if (my_power_save == -1) - my_power_save = 1; - break; - case 0x4011: - PWC_INFO("Creative Labs Webcam Pro Ex detected.\n"); - name = "Creative Labs Webcam Pro Ex"; - type_id = 740; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x04cc) { - switch(product_id) { - case 0x8116: - PWC_INFO("Sotec Afina Eye USB webcam detected.\n"); - name = "Sotec Afina Eye"; - type_id = 730; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x06be) { - switch(product_id) { - case 0x8116: - /* This is essentially the same cam as the Sotec Afina Eye */ - PWC_INFO("AME Co. Afina Eye USB webcam detected.\n"); - name = "AME Co. Afina Eye"; - type_id = 750; - break; - default: - return -ENODEV; - break; - } - - } - else if (vendor_id == 0x0d81) { - switch(product_id) { - case 0x1900: - PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n"); - name = "Visionite VCS-UC300"; - type_id = 740; /* CCD sensor */ - break; - case 0x1910: - PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n"); - name = "Visionite VCS-UM100"; - type_id = 730; /* CMOS sensor */ - break; - default: - return -ENODEV; - break; - } - } - else - return -ENODEV; /* Not any of the know types; but the list keeps growing. */ - - if (my_power_save == -1) - my_power_save = 0; - - memset(serial_number, 0, 30); - usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); - PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number); - - if (udev->descriptor.bNumConfigurations > 1) - PWC_WARNING("Warning: more than 1 configuration available.\n"); - - /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ - pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); - if (pdev == NULL) { - PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); - return -ENOMEM; - } - pdev->type = type_id; - pdev->features = features; - pwc_construct(pdev); /* set min/max sizes correct */ - - mutex_init(&pdev->v4l2_lock); - mutex_init(&pdev->vb_queue_lock); - spin_lock_init(&pdev->queued_bufs_lock); - INIT_LIST_HEAD(&pdev->queued_bufs); - - pdev->udev = udev; - pdev->power_save = my_power_save; - - /* Init videobuf2 queue structure */ - memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue)); - pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - pdev->vb_queue.drv_priv = pdev; - pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf); - pdev->vb_queue.ops = &pwc_vb_queue_ops; - pdev->vb_queue.mem_ops = &vb2_vmalloc_memops; - vb2_queue_init(&pdev->vb_queue); - - /* Init video_device structure */ - memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); - strcpy(pdev->vdev.name, name); - pdev->vdev.queue = &pdev->vb_queue; - pdev->vdev.queue->lock = &pdev->vb_queue_lock; - set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags); - video_set_drvdata(&pdev->vdev, pdev); - - pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); - PWC_DEBUG_PROBE("Release: %04x\n", pdev->release); - - /* Allocate USB command buffers */ - pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL); - if (!pdev->ctrl_buf) { - PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); - rc = -ENOMEM; - goto err_free_mem; - } - -#ifdef CONFIG_USB_PWC_DEBUG - /* Query sensor type */ - if (pwc_get_cmos_sensor(pdev, &rc) >= 0) { - PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n", - pdev->vdev.name, - pwc_sensor_type_to_string(rc), rc); - } -#endif - - /* Set the leds off */ - pwc_set_leds(pdev, 0, 0); - - /* Setup intial videomode */ - rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, - V4L2_PIX_FMT_YUV420, 30, &compression, 1); - if (rc) - goto err_free_mem; - - /* Register controls (and read default values from camera */ - rc = pwc_init_controls(pdev); - if (rc) { - PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc); - goto err_free_mem; - } - - /* And powerdown the camera until streaming starts */ - pwc_camera_power(pdev, 0); - - /* Register the v4l2_device structure */ - pdev->v4l2_dev.release = pwc_video_release; - rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev); - if (rc) { - PWC_ERROR("Failed to register v4l2-device (%d).\n", rc); - goto err_free_controls; - } - - pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; - pdev->vdev.v4l2_dev = &pdev->v4l2_dev; - pdev->vdev.lock = &pdev->v4l2_lock; - - rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); - if (rc < 0) { - PWC_ERROR("Failed to register as video device (%d).\n", rc); - goto err_unregister_v4l2_dev; - } - PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev)); - -#ifdef CONFIG_USB_PWC_INPUT_EVDEV - /* register webcam snapshot button input device */ - pdev->button_dev = input_allocate_device(); - if (!pdev->button_dev) { - PWC_ERROR("Err, insufficient memory for webcam snapshot button device."); - rc = -ENOMEM; - goto err_video_unreg; - } - - usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys)); - strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys)); - - pdev->button_dev->name = "PWC snapshot button"; - pdev->button_dev->phys = pdev->button_phys; - usb_to_input_id(pdev->udev, &pdev->button_dev->id); - pdev->button_dev->dev.parent = &pdev->udev->dev; - pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY); - pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); - - rc = input_register_device(pdev->button_dev); - if (rc) { - input_free_device(pdev->button_dev); - pdev->button_dev = NULL; - goto err_video_unreg; - } -#endif - - return 0; - -err_video_unreg: - video_unregister_device(&pdev->vdev); -err_unregister_v4l2_dev: - v4l2_device_unregister(&pdev->v4l2_dev); -err_free_controls: - v4l2_ctrl_handler_free(&pdev->ctrl_handler); -err_free_mem: - kfree(pdev->ctrl_buf); - kfree(pdev); - return rc; -} - -/* The user yanked out the cable... */ -static void usb_pwc_disconnect(struct usb_interface *intf) -{ - struct v4l2_device *v = usb_get_intfdata(intf); - struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); - - mutex_lock(&pdev->vb_queue_lock); - mutex_lock(&pdev->v4l2_lock); - /* No need to keep the urbs around after disconnection */ - if (pdev->vb_queue.streaming) - pwc_isoc_cleanup(pdev); - pdev->udev = NULL; - pwc_cleanup_queued_bufs(pdev); - - v4l2_device_disconnect(&pdev->v4l2_dev); - video_unregister_device(&pdev->vdev); - mutex_unlock(&pdev->v4l2_lock); - mutex_unlock(pdev->vb_queue.lock); - -#ifdef CONFIG_USB_PWC_INPUT_EVDEV - if (pdev->button_dev) - input_unregister_device(pdev->button_dev); -#endif - - v4l2_device_put(&pdev->v4l2_dev); -} - - -/* - * Initialization code & module stuff - */ - -static unsigned int leds_nargs; - -#ifdef CONFIG_USB_PWC_DEBUG -module_param_named(trace, pwc_trace, int, 0644); -#endif -module_param(power_save, int, 0644); -module_param_array(leds, int, &leds_nargs, 0444); - -#ifdef CONFIG_USB_PWC_DEBUG -MODULE_PARM_DESC(trace, "For debugging purposes"); -#endif -MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off"); -MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); - -MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); -MODULE_AUTHOR("Luc Saillard "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("pwcx"); -MODULE_VERSION( PWC_VERSION ); - -module_usb_driver(pwc_driver); diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c deleted file mode 100644 index e5f4fd817125..000000000000 --- a/drivers/media/video/pwc/pwc-kiara.c +++ /dev/null @@ -1,892 +0,0 @@ -/* Linux driver for Philips webcam - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - - -/* This tables contains entries for the 730/740/750 (Kiara) camera, with - 4 different qualities (no compression, low, medium, high). - It lists the bandwidth requirements for said mode by its alternate interface - number. An alternate of 0 means that the mode is unavailable. - - There are 6 * 4 * 4 entries: - 6 different resolutions subqcif, qsif, qcif, sif, cif, vga - 6 framerates: 5, 10, 15, 20, 25, 30 - 4 compression modi: none, low, medium, high - - When an uncompressed mode is not available, the next available compressed mode - will be chosen (unless the decompressor is absent). Sometimes there are only - 1 or 2 compressed modes available; in that case entries are duplicated. -*/ - - -#include "pwc-kiara.h" - -const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 }; - -const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = -{ - /* SQCIF */ - { - /* 5 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 10 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 15 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, - /* QSIF */ - { - /* 5 fps */ - { - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - }, - /* 10 fps */ - { - {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, - {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, - {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, - {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, - }, - /* 15 fps */ - { - {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, - {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, - {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, - {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, - }, - /* 20 fps */ - { - {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, - {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, - {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, - {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, - }, - /* 25 fps */ - { - {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, - {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, - {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, - {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, - }, - /* 30 fps */ - { - {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, - {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, - {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, - {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, - }, - }, - /* QCIF */ - { - /* 5 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 10 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 15 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, - /* SIF */ - { - /* 5 fps */ - { - {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, - {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, - {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, - {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, - }, - /* 10 fps */ - { - {0, }, - {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, - {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, - {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, - }, - /* 15 fps */ - { - {0, }, - {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, - {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, - {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, - }, - /* 20 fps */ - { - {0, }, - {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, - {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, - {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, - }, - /* 25 fps */ - { - {0, }, - {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, - {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, - {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, - }, - /* 30 fps */ - { - {0, }, - {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, - {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, - {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, - }, - }, - /* CIF */ - { - /* 5 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 10 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 15 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, - /* VGA */ - { - /* 5 fps */ - { - {0, }, - {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, - {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, - {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, - }, - /* 10 fps */ - { - {0, }, - {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, - {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, - {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, - }, - /* 15 fps */ - { - {0, }, - {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, - {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, - {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, -}; - - -/* - * Rom table for kiara chips - * - * 32 roms tables (one for each resolution ?) - * 2 tables per roms (one for each passes) (Y, and U&V) - * 128 bytes per passes - */ - -const unsigned int KiaraRomTable [8][2][16][8] = -{ - { /* version 0 */ - { /* version 0, passes 0 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000001,0x00000001}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000009,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000249,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000249,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x0000124a,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x0000124a,0x00009252,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00009252,0x00009292,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009292,0x00009292,0x00009493,0x000124db}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x0000a493,0x000124db,0x000124db,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x000124db,0x000126dc,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000124db,0x000136e4,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 0, passes 1 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000001,0x00000009, - 0x00000009,0x00000009,0x00000009,0x00000001}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000249,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00001252}, - {0x00000000,0x00000000,0x00000049,0x00001249, - 0x0000124a,0x0000124a,0x00001252,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009252,0x00009292,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009292,0x00009292,0x00009292,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00009292, - 0x00009492,0x00009493,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009252,0x00009493, - 0x000126dc,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000136e4,0x000136e4,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 1 */ - { /* version 1, passes 0 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000001}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00001252}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009252,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009252,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009292,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00009252, - 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x000124db,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 1, passes 1 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000009, - 0x00000049,0x00000009,0x00000001,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000000}, - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000249,0x00000049,0x0000024a,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x0000024a,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x0000024a,0x00000009}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009252,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009292,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009292,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009292,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x0000924a,0x0000924a, - 0x00009492,0x00009493,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 2 */ - { /* version 2, passes 0 */ - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x00009252,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009493,0x00009493,0x0000a49b}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009292,0x00009493,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x000124db,0x000124db,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x00009252,0x000124db, - 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 2, passes 1 */ - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00000249, - 0x0000124a,0x0000124a,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00009292,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x0000a49b,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00009252,0x0000a49b, - 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 3 */ - { /* version 3, passes 0 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x0001b725,0x0001b925}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 3, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00000249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x00009493,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000136e4,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 4 */ - { /* version 4, passes 0 */ - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000249,0x00000249,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x00009252,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009292,0x00009493,0x00009493,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0001249b,0x000126dc,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00009252,0x00009493, - 0x000124db,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009252,0x0000a49b, - 0x000124db,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 4, passes 1 */ - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000009,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000049,0x00000049,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00000249,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x0000124a,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009252,0x0000124a,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x00009252,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x00009292,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009292,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009493,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009252,0x000124db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 5 */ - { /* version 5, passes 0 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 5, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009252,0x00009252,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000124db,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000124db,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 6 */ - { /* version 6, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, - {0x00000000,0x00000000,0x00012492,0x000126db, - 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 6, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x00009252,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009292,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000124db,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 7 */ - { /* version 7, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x0000a49b, - 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x0001b725,0x000124db}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, - {0x00000000,0x00000000,0x00012492,0x000136db, - 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 7, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x00009492,0x00009292,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000124db,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000136db, - 0x0001b724,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000136db, - 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00009292,0x000136db, - 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00012492,0x0001b6db, - 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - } -}; - diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h deleted file mode 100644 index 8e02b7ac2139..000000000000 --- a/drivers/media/video/pwc/pwc-kiara.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Linux driver for Philips webcam - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* Entries for the Kiara (730/740/750) camera */ - -#ifndef PWC_KIARA_H -#define PWC_KIARA_H - -#include "pwc.h" - -#define PWC_FPS_MAX_KIARA 6 - -struct Kiara_table_entry -{ - char alternate; /* USB alternate interface */ - unsigned short packetsize; /* Normal packet size */ - unsigned short bandlength; /* Bandlength when decompressing */ - unsigned char mode[12]; /* precomputed mode settings for cam */ -}; - -extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][PWC_FPS_MAX_KIARA][4]; -extern const unsigned int KiaraRomTable[8][2][16][8]; -extern const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA]; - -#endif - - diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c deleted file mode 100644 index 9be5adffa874..000000000000 --- a/drivers/media/video/pwc/pwc-misc.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Linux driver for Philips webcam - Various miscellaneous functions and tables. - (C) 1999-2003 Nemosoft Unv. - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - - -#include "pwc.h" - -const int pwc_image_sizes[PSZ_MAX][2] = -{ - { 128, 96 }, /* sqcif */ - { 160, 120 }, /* qsif */ - { 176, 144 }, /* qcif */ - { 320, 240 }, /* sif */ - { 352, 288 }, /* cif */ - { 640, 480 }, /* vga */ -}; - -/* x,y -> PSZ_ */ -int pwc_get_size(struct pwc_device *pdev, int width, int height) -{ - int i; - - /* Find the largest size supported by the camera that fits into the - requested size. */ - for (i = PSZ_MAX - 1; i >= 0; i--) { - if (!(pdev->image_mask & (1 << i))) - continue; - - if (pwc_image_sizes[i][0] <= width && - pwc_image_sizes[i][1] <= height) - return i; - } - - /* No mode found, return the smallest mode we have */ - for (i = 0; i < PSZ_MAX; i++) { - if (pdev->image_mask & (1 << i)) - return i; - } - - /* Never reached there always is atleast one supported mode */ - return 0; -} - -/* initialize variables depending on type and decompressor */ -void pwc_construct(struct pwc_device *pdev) -{ - if (DEVICE_USE_CODEC1(pdev->type)) { - - pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; - pdev->vcinterface = 2; - pdev->vendpoint = 4; - pdev->frame_header_size = 0; - pdev->frame_trailer_size = 0; - - } else if (DEVICE_USE_CODEC3(pdev->type)) { - - pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; - pdev->vcinterface = 3; - pdev->vendpoint = 5; - pdev->frame_header_size = TOUCAM_HEADER_SIZE; - pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; - - } else /* if (DEVICE_USE_CODEC2(pdev->type)) */ { - - pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; - pdev->vcinterface = 3; - pdev->vendpoint = 4; - pdev->frame_header_size = 0; - pdev->frame_trailer_size = 0; - } -} diff --git a/drivers/media/video/pwc/pwc-nala.h b/drivers/media/video/pwc/pwc-nala.h deleted file mode 100644 index 168c73ef75d8..000000000000 --- a/drivers/media/video/pwc/pwc-nala.h +++ /dev/null @@ -1,66 +0,0 @@ - /* SQCIF */ - { - {0, 0, {0x04, 0x01, 0x03}}, - {8, 0, {0x05, 0x01, 0x03}}, - {7, 0, {0x08, 0x01, 0x03}}, - {7, 0, {0x0A, 0x01, 0x03}}, - {6, 0, {0x0C, 0x01, 0x03}}, - {5, 0, {0x0F, 0x01, 0x03}}, - {4, 0, {0x14, 0x01, 0x03}}, - {3, 0, {0x18, 0x01, 0x03}}, - }, - /* QSIF */ - { - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - }, - /* QCIF */ - { - {0, 0, {0x04, 0x01, 0x02}}, - {8, 0, {0x05, 0x01, 0x02}}, - {7, 0, {0x08, 0x01, 0x02}}, - {6, 0, {0x0A, 0x01, 0x02}}, - {5, 0, {0x0C, 0x01, 0x02}}, - {4, 0, {0x0F, 0x01, 0x02}}, - {1, 0, {0x14, 0x01, 0x02}}, - {1, 0, {0x18, 0x01, 0x02}}, - }, - /* SIF */ - { - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - }, - /* CIF */ - { - {4, 0, {0x04, 0x01, 0x01}}, - {7, 1, {0x05, 0x03, 0x01}}, - {6, 1, {0x08, 0x03, 0x01}}, - {4, 1, {0x0A, 0x03, 0x01}}, - {3, 1, {0x0C, 0x03, 0x01}}, - {2, 1, {0x0F, 0x03, 0x01}}, - {0}, - {0}, - }, - /* VGA */ - { - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - }, diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/video/pwc/pwc-timon.c deleted file mode 100644 index c56c174b161c..000000000000 --- a/drivers/media/video/pwc/pwc-timon.c +++ /dev/null @@ -1,1448 +0,0 @@ -/* Linux driver for Philips webcam - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - - -/* This tables contains entries for the 675/680/690 (Timon) camera, with - 4 different qualities (no compression, low, medium, high). - It lists the bandwidth requirements for said mode by its alternate interface - number. An alternate of 0 means that the mode is unavailable. - - There are 6 * 4 * 4 entries: - 6 different resolutions subqcif, qsif, qcif, sif, cif, vga - 6 framerates: 5, 10, 15, 20, 25, 30 - 4 compression modi: none, low, medium, high - - When an uncompressed mode is not available, the next available compressed mode - will be chosen (unless the decompressor is absent). Sometimes there are only - 1 or 2 compressed modes available; in that case entries are duplicated. -*/ - -#include "pwc-timon.h" - -const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON] = { 5, 10, 15, 20, 25, 30 }; - -const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4] = -{ - /* SQCIF */ - { - /* 5 fps */ - { - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - }, - /* 10 fps */ - { - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - }, - /* 15 fps */ - { - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - }, - /* 20 fps */ - { - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - }, - /* 25 fps */ - { - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - }, - /* 30 fps */ - { - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - }, - }, - /* QSIF */ - { - /* 5 fps */ - { - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - }, - /* 10 fps */ - { - {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, - {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - }, - /* 15 fps */ - { - {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, - {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - }, - /* 20 fps */ - { - {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, - {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, - {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, - }, - /* 25 fps */ - { - {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, - {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, - {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, - }, - /* 30 fps */ - { - {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, - {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, - {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, - {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, - }, - }, - /* QCIF */ - { - /* 5 fps */ - { - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - }, - /* 10 fps */ - { - {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, - {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, - }, - /* 15 fps */ - { - {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, - {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, - {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, - }, - /* 20 fps */ - { - {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, - {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, - {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, - }, - /* 25 fps */ - { - {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, - {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, - {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, - }, - /* 30 fps */ - { - {0, }, - {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, - {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, - {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, - }, - }, - /* SIF */ - { - /* 5 fps */ - { - {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, - {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, - {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, - {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, - }, - /* 10 fps */ - { - {0, }, - {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, - {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, - {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, - }, - /* 15 fps */ - { - {0, }, - {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, - {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, - {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, - }, - /* 20 fps */ - { - {0, }, - {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, - {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, - {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, - }, - /* 25 fps */ - { - {0, }, - {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, - {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, - {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, - }, - /* 30 fps */ - { - {0, }, - {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, - {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, - {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, - }, - }, - /* CIF */ - { - /* 5 fps */ - { - {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, - {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, - {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, - {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, - }, - /* 10 fps */ - { - {0, }, - {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, - {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, - {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, - }, - /* 15 fps */ - { - {0, }, - {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, - {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, - {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, - }, - /* 20 fps */ - { - {0, }, - {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, - {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, - {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, - }, - /* 25 fps */ - { - {0, }, - {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, - {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, - {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, - }, - /* 30 fps */ - { - {0, }, - {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, - {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, - {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, - }, - }, - /* VGA */ - { - /* 5 fps */ - { - {0, }, - {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, - {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, - {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, - }, - /* 10 fps */ - { - {0, }, - {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, - {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, - {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, - }, - /* 15 fps */ - { - {0, }, - {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, - {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, - {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, -}; - -/* - * 16 versions: - * 2 tables (one for Y, and one for U&V) - * 16 levels of details per tables - * 8 blocs - */ - -const unsigned int TimonRomTable [16][2][16][8] = -{ - { /* version 0 */ - { /* version 0, passes 0 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000001}, - {0x00000000,0x00000000,0x00000001,0x00000001, - 0x00000001,0x00000001,0x00000001,0x00000001}, - {0x00000000,0x00000000,0x00000001,0x00000001, - 0x00000001,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000009,0x00000001, - 0x00000009,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000049,0x00000009}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000009,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000249,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000249,0x00000249,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000249,0x0000124a,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009252,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 0, passes 1 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000001,0x00000001, - 0x00000001,0x00000001,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000009,0x00000001, - 0x00000001,0x00000009,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000001,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000009, - 0x00000009,0x00000049,0x00000001,0x00000001}, - {0x00000000,0x00000000,0x00000049,0x00000009, - 0x00000009,0x00000049,0x00000001,0x00000001}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000009,0x00000001}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000009,0x00000001}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000009,0x00000001}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000249,0x00000049,0x00000009}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000249,0x00000049,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000249,0x00000249,0x00000049,0x00000009}, - {0x00000000,0x00000000,0x00001249,0x00000249, - 0x0000124a,0x0000124a,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 1 */ - { /* version 1, passes 0 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000001}, - {0x00000000,0x00000000,0x00000001,0x00000001, - 0x00000001,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000009,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000249,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00001252}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009252,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009252,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009292,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 1, passes 1 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000001,0x00000001,0x00000000}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000001,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000009,0x00000001,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000001,0x00000001}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000009,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000049,0x00000249,0x00000009,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000249,0x00000249,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x00000049,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00000049,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00000049,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009252,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 2 */ - { /* version 2, passes 0 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000001}, - {0x00000000,0x00000000,0x00000009,0x00000009, - 0x00000009,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00001252}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009252,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009252,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009292,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00009252, - 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x000124db,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 2, passes 1 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000009, - 0x00000049,0x00000009,0x00000001,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000000}, - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000249,0x00000049,0x0000024a,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x0000024a,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x0000024a,0x00000009}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009252,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009292,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009292,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009292,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x0000924a,0x0000924a, - 0x00009492,0x00009493,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 3 */ - { /* version 3, passes 0 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000001}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000049,0x00000249, - 0x00000249,0x00000249,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x00009252,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009292,0x00009292,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009292,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00009252, - 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x0000a49b,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x0001b725,0x000136e4}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 3, passes 1 */ - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000}, - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000001,0x00000000}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x00000049,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00000001}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x00001252,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009252,0x00009292,0x00000009}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009252,0x00009292,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009252,0x00009292,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009493,0x00009292,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009493,0x00009292,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009493,0x00009493,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009292,0x00009493,0x00009493,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x00009493,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009292, - 0x0000a493,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 4 */ - { /* version 4, passes 0 */ - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x00009252,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009493,0x00009493,0x0000a49b}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009292,0x00009493,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x000124db,0x000124db,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x00009252,0x000124db, - 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 4, passes 1 */ - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00000249, - 0x0000124a,0x0000124a,0x00001252,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00009292,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x0000a49b,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00009252,0x0000a49b, - 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 5 */ - { /* version 5, passes 0 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x0000124a,0x00001252,0x00009292}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x0000124a,0x00009292,0x00009292,0x00009493}, - {0x00000000,0x00000000,0x00000249,0x0000924a, - 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x000124db,0x000124db,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0001249b,0x000126dc,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000126dc,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 5, passes 1 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x00009493,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x00009493,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x00009493,0x000124db,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x00009493,0x000124db,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x000124db,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x000124db,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009252,0x000124db, - 0x000126dc,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 6 */ - { /* version 6, passes 0 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x0000124a,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x0001b725,0x0001b925}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 6, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00000249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x00009493,0x0000a49b,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000136e4,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 7 */ - { /* version 7, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x0000a49b,0x000124db,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b724,0x0001c96e,0x0002496e}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x000136e4,0x0001b925,0x0001c96e,0x0002496e}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 7, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x00009493,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x000136e4,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x000136e4,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000124db,0x000136e4,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x000136e4,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00012492,0x000126db, - 0x0001b724,0x0001b925,0x0001b725,0x000136e4}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 8 */ - { /* version 8, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009292,0x00009493,0x0000a49b,0x000124db}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x000124db,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000136e4}, - {0x00000000,0x00000000,0x00001249,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000136e4,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, - {0x00000000,0x00000000,0x00009252,0x000124db, - 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x00024b76,0x00024b77}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x000136e4,0x0001b925,0x00024b76,0x00025bbf}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf}, - {0x00000000,0x00000000,0x00012492,0x000136db, - 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 8, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000126dc,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000136e4,0x0001b724,0x0001b725,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x0002496d,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 9 */ - { /* version 9, passes 0 */ - {0x00000000,0x00000000,0x00000049,0x00000049, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000249,0x00000249,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x0000124a,0x00009252,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009292,0x00009493,0x00009493,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0001249b,0x000126dc,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00009252,0x00009493, - 0x000124db,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009252,0x0000a49b, - 0x000124db,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 9, passes 1 */ - {0x00000000,0x00000000,0x00000249,0x00000049, - 0x00000009,0x00000009,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000049,0x00000049,0x00000009,0x00000009}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00000249,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x0000124a,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009252,0x0000124a,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x00009252,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x00009292,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009292,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009493,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009252,0x000124db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 10 */ - { /* version 10, passes 0 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00000249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x00009493,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x000124db,0x000124db,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0001249b,0x000126dc,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000126dc,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009252,0x0000a49b, - 0x000124db,0x000136e4,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000126dc,0x0001b925,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 10, passes 1 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000049,0x00000049,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00000249,0x00000049,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x00009252,0x0000024a,0x00000049}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009493,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00009252, - 0x00009492,0x00009493,0x00001252,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x00009493,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x00009492,0x00009493,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009493,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009252,0x000126db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 11 */ - { /* version 11, passes 0 */ - {0x00000000,0x00000000,0x00000249,0x00000249, - 0x00000249,0x00000249,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x000124db,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 11, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00000249, - 0x00000249,0x00000249,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009252,0x00009252,0x0000024a,0x0000024a}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x0000a49b,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x0000a49b,0x00009292,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000124db,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000124db,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 12 */ - { /* version 12, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, - {0x00000000,0x00000000,0x00012492,0x000126db, - 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 12, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x00001249,0x00009292, - 0x00009492,0x00009252,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009292,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000124db,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000124db,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00009492,0x000126db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 13 */ - { /* version 13, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x00009252,0x00009292,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x000124db,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x0000a49b, - 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x000136e4,0x0001b725,0x000124db}, - {0x00000000,0x00000000,0x00009292,0x0000a49b, - 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, - {0x00000000,0x00000000,0x00012492,0x000136db, - 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 13, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x00009492,0x00009292,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x0000a49b,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000124db,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000136db, - 0x0001b724,0x000124db,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000136db, - 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00009292,0x000136db, - 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00012492,0x0001b6db, - 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 14 */ - { /* version 14, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x0000924a, - 0x00009292,0x00009493,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00001249,0x0000a49b, - 0x0000a493,0x000124db,0x000126dc,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x000136e4,0x0001b725,0x000124db}, - {0x00000000,0x00000000,0x00009292,0x000124db, - 0x000126dc,0x0001b724,0x0001b92d,0x000126dc}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x00024b76,0x0001b925}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b924,0x0002496d,0x00024b76,0x00024b77}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf}, - {0x00000000,0x00000000,0x00012492,0x0001b6db, - 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 14, passes 1 */ - {0x00000000,0x00000000,0x00001249,0x00001249, - 0x0000124a,0x0000124a,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x00009493, - 0x0000a493,0x00009292,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x0000a49b,0x00001252,0x00001252}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000136e4,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000136e4,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x000136e4,0x00009493,0x00009292}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001b724,0x000136e4,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b724,0x000136e4,0x000126dc,0x000124db}, - {0x00000000,0x00000000,0x00012492,0x0001b6db, - 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - }, - { /* version 15 */ - { /* version 15, passes 0 */ - {0x00000000,0x00000000,0x00001249,0x00009493, - 0x0000a493,0x0000a49b,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0001249b,0x000126dc,0x000136e4,0x000124db}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x000136e4,0x0001b925,0x0001c96e,0x000136e4}, - {0x00000000,0x00000000,0x00009492,0x000124db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000124db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, - {0x00000000,0x00000000,0x0000a492,0x000126db, - 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001b924,0x0002496d,0x00024b76,0x0002496e}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77}, - {0x00000000,0x00000000,0x00012492,0x000136db, - 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, - {0x00000000,0x00000000,0x00012492,0x0001b6db, - 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - }, - { /* version 15, passes 1 */ - {0x00000000,0x00000000,0x0000924a,0x0000924a, - 0x00009292,0x00009292,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x0000a49b, - 0x0000a493,0x000124db,0x00009292,0x00009292}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000124db,0x0001b724,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000126dc,0x0001b724,0x00009493,0x00009493}, - {0x00000000,0x00000000,0x0000924a,0x000124db, - 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00009292,0x000136db, - 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001c924,0x0001b724,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x00009492,0x000136db, - 0x0001c924,0x0001b724,0x000124db,0x000124db}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0001b724,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0001b925,0x000126dc,0x000126dc}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, - {0x00000000,0x00000000,0x0000a492,0x000136db, - 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x00012492,0x000136db, - 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, - {0x00000000,0x00000000,0x00012492,0x0001b6db, - 0x00024924,0x0002496d,0x0001b92d,0x0001b925}, - {0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000000,0x00000000,0x00000000,0x00000000} - } - } -}; diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h deleted file mode 100644 index 270c5b9010f6..000000000000 --- a/drivers/media/video/pwc/pwc-timon.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Linux driver for Philips webcam - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - - - -/* This tables contains entries for the 675/680/690 (Timon) camera, with - 4 different qualities (no compression, low, medium, high). - It lists the bandwidth requirements for said mode by its alternate interface - number. An alternate of 0 means that the mode is unavailable. - - There are 6 * 4 * 4 entries: - 6 different resolutions subqcif, qsif, qcif, sif, cif, vga - 6 framerates: 5, 10, 15, 20, 25, 30 - 4 compression modi: none, low, medium, high - - When an uncompressed mode is not available, the next available compressed mode - will be chosen (unless the decompressor is absent). Sometimes there are only - 1 or 2 compressed modes available; in that case entries are duplicated. -*/ - -#ifndef PWC_TIMON_H -#define PWC_TIMON_H - -#include "pwc.h" - -#define PWC_FPS_MAX_TIMON 6 - -struct Timon_table_entry -{ - char alternate; /* USB alternate interface */ - unsigned short packetsize; /* Normal packet size */ - unsigned short bandlength; /* Bandlength when decompressing */ - unsigned char mode[13]; /* precomputed mode settings for cam */ -}; - -extern const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4]; -extern const unsigned int TimonRomTable [16][2][16][8]; -extern const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON]; - -#endif - - diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c deleted file mode 100644 index b65903fbcf0d..000000000000 --- a/drivers/media/video/pwc/pwc-uncompress.c +++ /dev/null @@ -1,107 +0,0 @@ -/* Linux driver for Philips webcam - Decompression frontend. - (C) 1999-2003 Nemosoft Unv. - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - vim: set ts=8: -*/ - -#include -#include - -#include "pwc.h" -#include "pwc-dec1.h" -#include "pwc-dec23.h" - -int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) -{ - int n, line, col; - void *yuv, *image; - u16 *src; - u16 *dsty, *dstu, *dstv; - - image = vb2_plane_vaddr(&fbuf->vb, 0); - - yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ - - /* Raw format; that's easy... */ - if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) - { - struct pwc_raw_frame *raw_frame = image; - raw_frame->type = cpu_to_le16(pdev->type); - raw_frame->vbandlength = cpu_to_le16(pdev->vbandlength); - /* cmd_buf is always 4 bytes, but sometimes, only the - * first 3 bytes is filled (Nala case). We can - * determine this using the type of the webcam */ - memcpy(raw_frame->cmd, pdev->cmd_buf, 4); - memcpy(raw_frame+1, yuv, pdev->frame_size); - vb2_set_plane_payload(&fbuf->vb, 0, - pdev->frame_size + sizeof(struct pwc_raw_frame)); - return 0; - } - - vb2_set_plane_payload(&fbuf->vb, 0, - pdev->width * pdev->height * 3 / 2); - - if (pdev->vbandlength == 0) { - /* Uncompressed mode. - * - * We do some byte shuffling here to go from the - * native format to YUV420P. - */ - src = (u16 *)yuv; - n = pdev->width * pdev->height; - dsty = (u16 *)(image); - dstu = (u16 *)(image + n); - dstv = (u16 *)(image + n + n / 4); - - for (line = 0; line < pdev->height; line++) { - for (col = 0; col < pdev->width; col += 4) { - *dsty++ = *src++; - *dsty++ = *src++; - if (line & 1) - *dstv++ = *src++; - else - *dstu++ = *src++; - } - } - - return 0; - } - - /* - * Compressed; - * the decompressor routines will write the data in planar format - * immediately. - */ - if (DEVICE_USE_CODEC1(pdev->type)) { - - /* TODO & FIXME */ - PWC_ERROR("This chipset is not supported for now\n"); - return -ENXIO; /* No such device or address: missing decompressor */ - - } else { - pwc_dec23_decompress(pdev, yuv, image); - } - return 0; -} diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c deleted file mode 100644 index 545e9bbdeede..000000000000 --- a/drivers/media/video/pwc/pwc-v4l.c +++ /dev/null @@ -1,1053 +0,0 @@ -/* Linux driver for Philips webcam - USB and Video4Linux interface part. - (C) 1999-2004 Nemosoft Unv. - (C) 2004-2006 Luc Saillard (luc@saillard.org) - (C) 2011 Hans de Goede - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pwc.h" - -#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl) - -static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl); -static int pwc_s_ctrl(struct v4l2_ctrl *ctrl); - -static const struct v4l2_ctrl_ops pwc_ctrl_ops = { - .g_volatile_ctrl = pwc_g_volatile_ctrl, - .s_ctrl = pwc_s_ctrl, -}; - -enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto }; -enum { custom_autocontour, custom_contour, custom_noise_reduction, - custom_awb_speed, custom_awb_delay, - custom_save_user, custom_restore_user, custom_restore_factory }; - -const char * const pwc_auto_whitebal_qmenu[] = { - "Indoor (Incandescant Lighting) Mode", - "Outdoor (Sunlight) Mode", - "Indoor (Fluorescent Lighting) Mode", - "Manual Mode", - "Auto Mode", - NULL -}; - -static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = { - .ops = &pwc_ctrl_ops, - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_MENU, - .max = awb_auto, - .qmenu = pwc_auto_whitebal_qmenu, -}; - -static const struct v4l2_ctrl_config pwc_autocontour_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(autocontour), - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto contour", - .min = 0, - .max = 1, - .step = 1, -}; - -static const struct v4l2_ctrl_config pwc_contour_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(contour), - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contour", - .flags = V4L2_CTRL_FLAG_SLIDER, - .min = 0, - .max = 63, - .step = 1, -}; - -static const struct v4l2_ctrl_config pwc_backlight_cfg = { - .ops = &pwc_ctrl_ops, - .id = V4L2_CID_BACKLIGHT_COMPENSATION, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = 0, - .max = 1, - .step = 1, -}; - -static const struct v4l2_ctrl_config pwc_flicker_cfg = { - .ops = &pwc_ctrl_ops, - .id = V4L2_CID_BAND_STOP_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = 0, - .max = 1, - .step = 1, -}; - -static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(noise_reduction), - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Dynamic Noise Reduction", - .min = 0, - .max = 3, - .step = 1, -}; - -static const struct v4l2_ctrl_config pwc_save_user_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(save_user), - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Save User Settings", -}; - -static const struct v4l2_ctrl_config pwc_restore_user_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(restore_user), - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Restore User Settings", -}; - -static const struct v4l2_ctrl_config pwc_restore_factory_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(restore_factory), - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Restore Factory Settings", -}; - -static const struct v4l2_ctrl_config pwc_awb_speed_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(awb_speed), - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Auto White Balance Speed", - .min = 1, - .max = 32, - .step = 1, -}; - -static const struct v4l2_ctrl_config pwc_awb_delay_cfg = { - .ops = &pwc_ctrl_ops, - .id = PWC_CID_CUSTOM(awb_delay), - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Auto White Balance Delay", - .min = 0, - .max = 63, - .step = 1, -}; - -int pwc_init_controls(struct pwc_device *pdev) -{ - struct v4l2_ctrl_handler *hdl; - struct v4l2_ctrl_config cfg; - int r, def; - - hdl = &pdev->ctrl_handler; - r = v4l2_ctrl_handler_init(hdl, 20); - if (r) - return r; - - /* Brightness, contrast, saturation, gamma */ - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def); - if (r || def > 127) - def = 63; - pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 127, 1, def); - - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def); - if (r || def > 63) - def = 31; - pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_CONTRAST, 0, 63, 1, def); - - if (pdev->type >= 675) { - if (pdev->type < 730) - pdev->saturation_fmt = SATURATION_MODE_FORMATTER2; - else - pdev->saturation_fmt = SATURATION_MODE_FORMATTER1; - r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt, - &def); - if (r || def < -100 || def > 100) - def = 0; - pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_SATURATION, -100, 100, 1, def); - } - - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def); - if (r || def > 31) - def = 15; - pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_GAMMA, 0, 31, 1, def); - - /* auto white balance, red gain, blue gain */ - r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def); - if (r || def > awb_auto) - def = awb_auto; - cfg = pwc_auto_white_balance_cfg; - cfg.name = v4l2_ctrl_get_name(cfg.id); - cfg.def = def; - pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */ - if (!pdev->auto_white_balance) - return hdl->error; - - r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, - PRESET_MANUAL_RED_GAIN_FORMATTER, &def); - if (r) - def = 127; - pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 255, 1, def); - - r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, - PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def); - if (r) - def = 127; - pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 255, 1, def); - - v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true); - - /* autogain, gain */ - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def); - if (r || (def != 0 && def != 0xff)) - def = 0; - /* Note a register value if 0 means auto gain is on */ - pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0); - if (!pdev->autogain) - return hdl->error; - - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def); - if (r || def > 63) - def = 31; - pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_GAIN, 0, 63, 1, def); - - /* auto exposure, exposure */ - if (DEVICE_USE_CODEC2(pdev->type)) { - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER, - &def); - if (r || (def != 0 && def != 0xff)) - def = 0; - /* - * def = 0 auto, def = ff manual - * menu idx 0 = auto, idx 1 = manual - */ - pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl, - &pwc_ctrl_ops, - V4L2_CID_EXPOSURE_AUTO, - 1, 0, def != 0); - if (!pdev->exposure_auto) - return hdl->error; - - /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ - r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, - READ_SHUTTER_FORMATTER, &def); - if (r || def > 655) - def = 655; - pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 655, 1, def); - /* CODEC2: separate auto gain & auto exposure */ - v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true); - v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto, - V4L2_EXPOSURE_MANUAL, true); - } else if (DEVICE_USE_CODEC3(pdev->type)) { - /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ - r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, - READ_SHUTTER_FORMATTER, &def); - if (r || def > 255) - def = 255; - pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 255, 1, def); - /* CODEC3: both gain and exposure controlled by autogain */ - pdev->autogain_expo_cluster[0] = pdev->autogain; - pdev->autogain_expo_cluster[1] = pdev->gain; - pdev->autogain_expo_cluster[2] = pdev->exposure; - v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster, - 0, true); - } - - /* color / bw setting */ - r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER, - &def); - if (r || (def != 0 && def != 0xff)) - def = 0xff; - /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */ - pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops, - V4L2_CID_COLORFX, 1, 0, def == 0); - - /* autocontour, contour */ - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def); - if (r || (def != 0 && def != 0xff)) - def = 0; - cfg = pwc_autocontour_cfg; - cfg.def = def == 0; - pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - if (!pdev->autocontour) - return hdl->error; - - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def); - if (r || def > 63) - def = 31; - cfg = pwc_contour_cfg; - cfg.def = def; - pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - - v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false); - - /* backlight */ - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, - BACK_LIGHT_COMPENSATION_FORMATTER, &def); - if (r || (def != 0 && def != 0xff)) - def = 0; - cfg = pwc_backlight_cfg; - cfg.name = v4l2_ctrl_get_name(cfg.id); - cfg.def = def == 0; - pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - - /* flikker rediction */ - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, - FLICKERLESS_MODE_FORMATTER, &def); - if (r || (def != 0 && def != 0xff)) - def = 0; - cfg = pwc_flicker_cfg; - cfg.name = v4l2_ctrl_get_name(cfg.id); - cfg.def = def == 0; - pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - - /* Dynamic noise reduction */ - r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, - DYNAMIC_NOISE_CONTROL_FORMATTER, &def); - if (r || def > 3) - def = 2; - cfg = pwc_noise_reduction_cfg; - cfg.def = def; - pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - - /* Save / Restore User / Factory Settings */ - pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL); - pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg, - NULL); - if (pdev->restore_user) - pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE; - pdev->restore_factory = v4l2_ctrl_new_custom(hdl, - &pwc_restore_factory_cfg, - NULL); - if (pdev->restore_factory) - pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE; - - /* Auto White Balance speed & delay */ - r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, - AWB_CONTROL_SPEED_FORMATTER, &def); - if (r || def < 1 || def > 32) - def = 1; - cfg = pwc_awb_speed_cfg; - cfg.def = def; - pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - - r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, - AWB_CONTROL_DELAY_FORMATTER, &def); - if (r || def > 63) - def = 0; - cfg = pwc_awb_delay_cfg; - cfg.def = def; - pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL); - - if (!(pdev->features & FEATURE_MOTOR_PANTILT)) - return hdl->error; - - /* Motor pan / tilt / reset */ - pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0); - if (!pdev->motor_pan) - return hdl->error; - pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0); - pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_PAN_RESET, 0, 0, 0, 0); - pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, - V4L2_CID_TILT_RESET, 0, 0, 0, 0); - v4l2_ctrl_cluster(4, &pdev->motor_pan); - - return hdl->error; -} - -static void pwc_vidioc_fill_fmt(struct v4l2_format *f, - int width, int height, u32 pixfmt) -{ - memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->fmt.pix.width = width; - f->fmt.pix.height = height; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = pixfmt; - f->fmt.pix.bytesperline = f->fmt.pix.width; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() " - "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", - f->fmt.pix.width, - f->fmt.pix.height, - f->fmt.pix.bytesperline, - f->fmt.pix.sizeimage, - (f->fmt.pix.pixelformat)&255, - (f->fmt.pix.pixelformat>>8)&255, - (f->fmt.pix.pixelformat>>16)&255, - (f->fmt.pix.pixelformat>>24)&255); -} - -/* ioctl(VIDIOC_TRY_FMT) */ -static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) -{ - int size; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); - return -EINVAL; - } - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUV420: - break; - case V4L2_PIX_FMT_PWC1: - if (DEVICE_USE_CODEC23(pdev->type)) { - PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n"); - return -EINVAL; - } - break; - case V4L2_PIX_FMT_PWC2: - if (DEVICE_USE_CODEC1(pdev->type)) { - PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n"); - return -EINVAL; - } - break; - default: - PWC_DEBUG_IOCTL("Unsupported pixel format\n"); - return -EINVAL; - - } - - size = pwc_get_size(pdev, f->fmt.pix.width, f->fmt.pix.height); - pwc_vidioc_fill_fmt(f, - pwc_image_sizes[size][0], - pwc_image_sizes[size][1], - f->fmt.pix.pixelformat); - - return 0; -} - -/* ioctl(VIDIOC_SET_FMT) */ - -static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret, pixelformat, compression = 0; - - ret = pwc_vidioc_try_fmt(pdev, f); - if (ret < 0) - return ret; - - if (vb2_is_busy(&pdev->vb_queue)) - return -EBUSY; - - pixelformat = f->fmt.pix.pixelformat; - - PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " - "format=%c%c%c%c\n", - f->fmt.pix.width, f->fmt.pix.height, pdev->vframes, - (pixelformat)&255, - (pixelformat>>8)&255, - (pixelformat>>16)&255, - (pixelformat>>24)&255); - - ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height, - pixelformat, 30, &compression, 0); - - PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); - - pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); - return ret; -} - -static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) -{ - struct pwc_device *pdev = video_drvdata(file); - - strcpy(cap->driver, PWC_NAME); - strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card)); - usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - if (i->index) /* Only one INPUT is supported */ - return -EINVAL; - - strlcpy(i->name, "Camera", sizeof(i->name)); - i->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - -static int pwc_g_input(struct file *file, void *fh, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int pwc_s_input(struct file *file, void *fh, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct pwc_device *pdev = - container_of(ctrl->handler, struct pwc_device, ctrl_handler); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - if (pdev->color_bal_valid && - (pdev->auto_white_balance->val != awb_auto || - time_before(jiffies, - pdev->last_color_bal_update + HZ / 4))) { - pdev->red_balance->val = pdev->last_red_balance; - pdev->blue_balance->val = pdev->last_blue_balance; - break; - } - ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, - READ_RED_GAIN_FORMATTER, - &pdev->red_balance->val); - if (ret) - break; - ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, - READ_BLUE_GAIN_FORMATTER, - &pdev->blue_balance->val); - if (ret) - break; - pdev->last_red_balance = pdev->red_balance->val; - pdev->last_blue_balance = pdev->blue_balance->val; - pdev->last_color_bal_update = jiffies; - pdev->color_bal_valid = true; - break; - case V4L2_CID_AUTOGAIN: - if (pdev->gain_valid && time_before(jiffies, - pdev->last_gain_update + HZ / 4)) { - pdev->gain->val = pdev->last_gain; - break; - } - ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, - READ_AGC_FORMATTER, &pdev->gain->val); - if (ret) - break; - pdev->last_gain = pdev->gain->val; - pdev->last_gain_update = jiffies; - pdev->gain_valid = true; - if (!DEVICE_USE_CODEC3(pdev->type)) - break; - /* Fall through for CODEC3 where autogain also controls expo */ - case V4L2_CID_EXPOSURE_AUTO: - if (pdev->exposure_valid && time_before(jiffies, - pdev->last_exposure_update + HZ / 4)) { - pdev->exposure->val = pdev->last_exposure; - break; - } - ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, - READ_SHUTTER_FORMATTER, - &pdev->exposure->val); - if (ret) - break; - pdev->last_exposure = pdev->exposure->val; - pdev->last_exposure_update = jiffies; - pdev->exposure_valid = true; - break; - default: - ret = -EINVAL; - } - - if (ret) - PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret); - - return ret; -} - -static int pwc_set_awb(struct pwc_device *pdev) -{ - int ret; - - if (pdev->auto_white_balance->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, - WB_MODE_FORMATTER, - pdev->auto_white_balance->val); - if (ret) - return ret; - - if (pdev->auto_white_balance->val != awb_manual) - pdev->color_bal_valid = false; /* Force cache update */ - - /* - * If this is a preset, update our red / blue balance values - * so that events get generated for the new preset values - */ - if (pdev->auto_white_balance->val == awb_indoor || - pdev->auto_white_balance->val == awb_outdoor || - pdev->auto_white_balance->val == awb_fl) - pwc_g_volatile_ctrl(pdev->auto_white_balance); - } - if (pdev->auto_white_balance->val != awb_manual) - return 0; - - if (pdev->red_balance->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, - PRESET_MANUAL_RED_GAIN_FORMATTER, - pdev->red_balance->val); - if (ret) - return ret; - } - - if (pdev->blue_balance->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, - PRESET_MANUAL_BLUE_GAIN_FORMATTER, - pdev->blue_balance->val); - if (ret) - return ret; - } - return 0; -} - -/* For CODEC2 models which have separate autogain and auto exposure */ -static int pwc_set_autogain(struct pwc_device *pdev) -{ - int ret; - - if (pdev->autogain->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - AGC_MODE_FORMATTER, - pdev->autogain->val ? 0 : 0xff); - if (ret) - return ret; - - if (pdev->autogain->val) - pdev->gain_valid = false; /* Force cache update */ - } - - if (pdev->autogain->val) - return 0; - - if (pdev->gain->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - PRESET_AGC_FORMATTER, - pdev->gain->val); - if (ret) - return ret; - } - return 0; -} - -/* For CODEC2 models which have separate autogain and auto exposure */ -static int pwc_set_exposure_auto(struct pwc_device *pdev) -{ - int ret; - int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO; - - if (pdev->exposure_auto->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - SHUTTER_MODE_FORMATTER, - is_auto ? 0 : 0xff); - if (ret) - return ret; - - if (is_auto) - pdev->exposure_valid = false; /* Force cache update */ - } - - if (is_auto) - return 0; - - if (pdev->exposure->is_new) { - ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, - PRESET_SHUTTER_FORMATTER, - pdev->exposure->val); - if (ret) - return ret; - } - return 0; -} - -/* For CODEC3 models which have autogain controlling both gain and exposure */ -static int pwc_set_autogain_expo(struct pwc_device *pdev) -{ - int ret; - - if (pdev->autogain->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - AGC_MODE_FORMATTER, - pdev->autogain->val ? 0 : 0xff); - if (ret) - return ret; - - if (pdev->autogain->val) { - pdev->gain_valid = false; /* Force cache update */ - pdev->exposure_valid = false; /* Force cache update */ - } - } - - if (pdev->autogain->val) - return 0; - - if (pdev->gain->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - PRESET_AGC_FORMATTER, - pdev->gain->val); - if (ret) - return ret; - } - - if (pdev->exposure->is_new) { - ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, - PRESET_SHUTTER_FORMATTER, - pdev->exposure->val); - if (ret) - return ret; - } - return 0; -} - -static int pwc_set_motor(struct pwc_device *pdev) -{ - int ret; - - pdev->ctrl_buf[0] = 0; - if (pdev->motor_pan_reset->is_new) - pdev->ctrl_buf[0] |= 0x01; - if (pdev->motor_tilt_reset->is_new) - pdev->ctrl_buf[0] |= 0x02; - if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) { - ret = send_control_msg(pdev, SET_MPT_CTL, - PT_RESET_CONTROL_FORMATTER, - pdev->ctrl_buf, 1); - if (ret < 0) - return ret; - } - - memset(pdev->ctrl_buf, 0, 4); - if (pdev->motor_pan->is_new) { - pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF; - pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8); - } - if (pdev->motor_tilt->is_new) { - pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF; - pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8); - } - if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) { - ret = send_control_msg(pdev, SET_MPT_CTL, - PT_RELATIVE_CONTROL_FORMATTER, - pdev->ctrl_buf, 4); - if (ret < 0) - return ret; - } - - return 0; -} - -static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct pwc_device *pdev = - container_of(ctrl->handler, struct pwc_device, ctrl_handler); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - BRIGHTNESS_FORMATTER, ctrl->val); - break; - case V4L2_CID_CONTRAST: - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - CONTRAST_FORMATTER, ctrl->val); - break; - case V4L2_CID_SATURATION: - ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL, - pdev->saturation_fmt, ctrl->val); - break; - case V4L2_CID_GAMMA: - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - GAMMA_FORMATTER, ctrl->val); - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = pwc_set_awb(pdev); - break; - case V4L2_CID_AUTOGAIN: - if (DEVICE_USE_CODEC2(pdev->type)) - ret = pwc_set_autogain(pdev); - else if (DEVICE_USE_CODEC3(pdev->type)) - ret = pwc_set_autogain_expo(pdev); - else - ret = -EINVAL; - break; - case V4L2_CID_EXPOSURE_AUTO: - if (DEVICE_USE_CODEC2(pdev->type)) - ret = pwc_set_exposure_auto(pdev); - else - ret = -EINVAL; - break; - case V4L2_CID_COLORFX: - ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, - COLOUR_MODE_FORMATTER, - ctrl->val ? 0 : 0xff); - break; - case PWC_CID_CUSTOM(autocontour): - if (pdev->autocontour->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - AUTO_CONTOUR_FORMATTER, - pdev->autocontour->val ? 0 : 0xff); - } - if (ret == 0 && pdev->contour->is_new) { - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - PRESET_CONTOUR_FORMATTER, - pdev->contour->val); - } - break; - case V4L2_CID_BACKLIGHT_COMPENSATION: - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - BACK_LIGHT_COMPENSATION_FORMATTER, - ctrl->val ? 0 : 0xff); - break; - case V4L2_CID_BAND_STOP_FILTER: - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - FLICKERLESS_MODE_FORMATTER, - ctrl->val ? 0 : 0xff); - break; - case PWC_CID_CUSTOM(noise_reduction): - ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, - DYNAMIC_NOISE_CONTROL_FORMATTER, - ctrl->val); - break; - case PWC_CID_CUSTOM(save_user): - ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER); - break; - case PWC_CID_CUSTOM(restore_user): - ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER); - break; - case PWC_CID_CUSTOM(restore_factory): - ret = pwc_button_ctrl(pdev, - RESTORE_FACTORY_DEFAULTS_FORMATTER); - break; - case PWC_CID_CUSTOM(awb_speed): - ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, - AWB_CONTROL_SPEED_FORMATTER, - ctrl->val); - break; - case PWC_CID_CUSTOM(awb_delay): - ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, - AWB_CONTROL_DELAY_FORMATTER, - ctrl->val); - break; - case V4L2_CID_PAN_RELATIVE: - ret = pwc_set_motor(pdev); - break; - default: - ret = -EINVAL; - } - - if (ret) - PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); - - return ret; -} - -static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) -{ - struct pwc_device *pdev = video_drvdata(file); - - /* We only support two format: the raw format, and YUV */ - switch (f->index) { - case 0: - /* RAW format */ - f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - strlcpy(f->description, "Raw Philips Webcam", sizeof(f->description)); - break; - case 1: - f->pixelformat = V4L2_PIX_FMT_YUV420; - strlcpy(f->description, "4:2:0, planar, Y-Cb-Cr", sizeof(f->description)); - break; - default: - return -EINVAL; - } - return 0; -} - -static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - struct pwc_device *pdev = video_drvdata(file); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", - pdev->width, pdev->height); - pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); - return 0; -} - -static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - struct pwc_device *pdev = video_drvdata(file); - - return pwc_vidioc_try_fmt(pdev, f); -} - -static int pwc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct pwc_device *pdev = video_drvdata(file); - unsigned int i = 0, index = fsize->index; - - if (fsize->pixel_format == V4L2_PIX_FMT_YUV420 || - (fsize->pixel_format == V4L2_PIX_FMT_PWC1 && - DEVICE_USE_CODEC1(pdev->type)) || - (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && - DEVICE_USE_CODEC23(pdev->type))) { - for (i = 0; i < PSZ_MAX; i++) { - if (!(pdev->image_mask & (1UL << i))) - continue; - if (!index--) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = pwc_image_sizes[i][0]; - fsize->discrete.height = pwc_image_sizes[i][1]; - return 0; - } - } - } - return -EINVAL; -} - -static int pwc_enum_frameintervals(struct file *file, void *fh, - struct v4l2_frmivalenum *fival) -{ - struct pwc_device *pdev = video_drvdata(file); - int size = -1; - unsigned int i; - - for (i = 0; i < PSZ_MAX; i++) { - if (pwc_image_sizes[i][0] == fival->width && - pwc_image_sizes[i][1] == fival->height) { - size = i; - break; - } - } - - /* TODO: Support raw format */ - if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) - return -EINVAL; - - i = pwc_get_fps(pdev, fival->index, size); - if (!i) - return -EINVAL; - - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete.numerator = 1; - fival->discrete.denominator = i; - - return 0; -} - -static int pwc_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *parm) -{ - struct pwc_device *pdev = video_drvdata(file); - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(parm, 0, sizeof(*parm)); - - parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - parm->parm.capture.readbuffers = MIN_FRAMES; - parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.timeperframe.denominator = pdev->vframes; - parm->parm.capture.timeperframe.numerator = 1; - - return 0; -} - -static int pwc_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *parm) -{ - struct pwc_device *pdev = video_drvdata(file); - int compression = 0; - int ret, fps; - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* If timeperframe == 0, then reset the framerate to the nominal value. - We pick a high framerate here, and let pwc_set_video_mode() figure - out the best match. */ - if (parm->parm.capture.timeperframe.numerator == 0 || - parm->parm.capture.timeperframe.denominator == 0) - fps = 30; - else - fps = parm->parm.capture.timeperframe.denominator / - parm->parm.capture.timeperframe.numerator; - - if (vb2_is_busy(&pdev->vb_queue)) - return -EBUSY; - - ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, - fps, &compression, 0); - - pwc_g_parm(file, fh, parm); - - return ret; -} - -const struct v4l2_ioctl_ops pwc_ioctl_ops = { - .vidioc_querycap = pwc_querycap, - .vidioc_enum_input = pwc_enum_input, - .vidioc_g_input = pwc_g_input, - .vidioc_s_input = pwc_s_input, - .vidioc_enum_fmt_vid_cap = pwc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_enum_framesizes = pwc_enum_framesizes, - .vidioc_enum_frameintervals = pwc_enum_frameintervals, - .vidioc_g_parm = pwc_g_parm, - .vidioc_s_parm = pwc_s_parm, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h deleted file mode 100644 index 7a6a0d39c2c6..000000000000 --- a/drivers/media/video/pwc/pwc.h +++ /dev/null @@ -1,393 +0,0 @@ -/* (C) 1999-2003 Nemosoft Unv. - (C) 2004-2006 Luc Saillard (luc@saillard.org) - - NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx - driver and thus may have bugs that are not present in the original version. - Please send bug reports and support requests to . - The decompression routines have been implemented by reverse-engineering the - Nemosoft binary pwcx module. Caveat emptor. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef PWC_H -#define PWC_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_USB_PWC_INPUT_EVDEV -#include -#endif -#include "pwc-dec1.h" -#include "pwc-dec23.h" - -/* Version block */ -#define PWC_VERSION "10.0.15" -#define PWC_NAME "pwc" -#define PFX PWC_NAME ": " - - -/* Trace certain actions in the driver */ -#define PWC_DEBUG_LEVEL_MODULE (1<<0) -#define PWC_DEBUG_LEVEL_PROBE (1<<1) -#define PWC_DEBUG_LEVEL_OPEN (1<<2) -#define PWC_DEBUG_LEVEL_READ (1<<3) -#define PWC_DEBUG_LEVEL_MEMORY (1<<4) -#define PWC_DEBUG_LEVEL_FLOW (1<<5) -#define PWC_DEBUG_LEVEL_SIZE (1<<6) -#define PWC_DEBUG_LEVEL_IOCTL (1<<7) -#define PWC_DEBUG_LEVEL_TRACE (1<<8) - -#define PWC_DEBUG_MODULE(fmt, args...) PWC_DEBUG(MODULE, fmt, ##args) -#define PWC_DEBUG_PROBE(fmt, args...) PWC_DEBUG(PROBE, fmt, ##args) -#define PWC_DEBUG_OPEN(fmt, args...) PWC_DEBUG(OPEN, fmt, ##args) -#define PWC_DEBUG_READ(fmt, args...) PWC_DEBUG(READ, fmt, ##args) -#define PWC_DEBUG_MEMORY(fmt, args...) PWC_DEBUG(MEMORY, fmt, ##args) -#define PWC_DEBUG_FLOW(fmt, args...) PWC_DEBUG(FLOW, fmt, ##args) -#define PWC_DEBUG_SIZE(fmt, args...) PWC_DEBUG(SIZE, fmt, ##args) -#define PWC_DEBUG_IOCTL(fmt, args...) PWC_DEBUG(IOCTL, fmt, ##args) -#define PWC_DEBUG_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) - - -#ifdef CONFIG_USB_PWC_DEBUG - -#define PWC_DEBUG_LEVEL (PWC_DEBUG_LEVEL_MODULE) - -#define PWC_DEBUG(level, fmt, args...) do {\ - if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \ - printk(KERN_DEBUG PFX fmt, ##args); \ - } while (0) - -#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) -#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) -#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) -#define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) - -#else /* if ! CONFIG_USB_PWC_DEBUG */ - -#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) -#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) -#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) -#define PWC_TRACE(fmt, args...) do { } while(0) -#define PWC_DEBUG(level, fmt, args...) do { } while(0) - -#define pwc_trace 0 - -#endif - -/* Defines for ToUCam cameras */ -#define TOUCAM_HEADER_SIZE 8 -#define TOUCAM_TRAILER_SIZE 4 - -#define FEATURE_MOTOR_PANTILT 0x0001 -#define FEATURE_CODEC1 0x0002 -#define FEATURE_CODEC2 0x0004 - -#define MAX_WIDTH 640 -#define MAX_HEIGHT 480 - -/* Ignore errors in the first N frames, to allow for startup delays */ -#define FRAME_LOWMARK 5 - -/* Size and number of buffers for the ISO pipe. */ -#define MAX_ISO_BUFS 3 -#define ISO_FRAMES_PER_DESC 10 -#define ISO_MAX_FRAME_SIZE 960 -#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) - -/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ -#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) - -/* Absolute minimum and maximum number of buffers available for mmap() */ -#define MIN_FRAMES 2 -#define MAX_FRAMES 16 - -/* Some macros to quickly find the type of a webcam */ -#define DEVICE_USE_CODEC1(x) ((x)<675) -#define DEVICE_USE_CODEC2(x) ((x)>=675 && (x)<700) -#define DEVICE_USE_CODEC3(x) ((x)>=700) -#define DEVICE_USE_CODEC23(x) ((x)>=675) - -/* Request types: video */ -#define SET_LUM_CTL 0x01 -#define GET_LUM_CTL 0x02 -#define SET_CHROM_CTL 0x03 -#define GET_CHROM_CTL 0x04 -#define SET_STATUS_CTL 0x05 -#define GET_STATUS_CTL 0x06 -#define SET_EP_STREAM_CTL 0x07 -#define GET_EP_STREAM_CTL 0x08 -#define GET_XX_CTL 0x09 -#define SET_XX_CTL 0x0A -#define GET_XY_CTL 0x0B -#define SET_XY_CTL 0x0C -#define SET_MPT_CTL 0x0D -#define GET_MPT_CTL 0x0E - -/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ -#define AGC_MODE_FORMATTER 0x2000 -#define PRESET_AGC_FORMATTER 0x2100 -#define SHUTTER_MODE_FORMATTER 0x2200 -#define PRESET_SHUTTER_FORMATTER 0x2300 -#define PRESET_CONTOUR_FORMATTER 0x2400 -#define AUTO_CONTOUR_FORMATTER 0x2500 -#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 -#define CONTRAST_FORMATTER 0x2700 -#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 -#define FLICKERLESS_MODE_FORMATTER 0x2900 -#define AE_CONTROL_SPEED 0x2A00 -#define BRIGHTNESS_FORMATTER 0x2B00 -#define GAMMA_FORMATTER 0x2C00 - -/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ -#define WB_MODE_FORMATTER 0x1000 -#define AWB_CONTROL_SPEED_FORMATTER 0x1100 -#define AWB_CONTROL_DELAY_FORMATTER 0x1200 -#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 -#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 -#define COLOUR_MODE_FORMATTER 0x1500 -#define SATURATION_MODE_FORMATTER1 0x1600 -#define SATURATION_MODE_FORMATTER2 0x1700 - -/* Selectors for the Status controls [GS]ET_STATUS_CTL */ -#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 -#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 -#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 -#define READ_AGC_FORMATTER 0x0500 -#define READ_SHUTTER_FORMATTER 0x0600 -#define READ_RED_GAIN_FORMATTER 0x0700 -#define READ_BLUE_GAIN_FORMATTER 0x0800 - -/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ -#define PT_RELATIVE_CONTROL_FORMATTER 0x01 -#define PT_RESET_CONTROL_FORMATTER 0x02 -#define PT_STATUS_FORMATTER 0x03 - -/* Enumeration of image sizes */ -#define PSZ_SQCIF 0x00 -#define PSZ_QSIF 0x01 -#define PSZ_QCIF 0x02 -#define PSZ_SIF 0x03 -#define PSZ_CIF 0x04 -#define PSZ_VGA 0x05 -#define PSZ_MAX 6 - -struct pwc_raw_frame { - __le16 type; /* type of the webcam */ - __le16 vbandlength; /* Size of 4 lines compressed (used by the - decompressor) */ - __u8 cmd[4]; /* the four byte of the command (in case of - nala, only the first 3 bytes is filled) */ - __u8 rawframe[0]; /* frame_size = H / 4 * vbandlength */ -} __packed; - -/* intermediate buffers with raw data from the USB cam */ -struct pwc_frame_buf -{ - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ - struct list_head list; - void *data; - int filled; /* number of bytes filled */ -}; - -struct pwc_device -{ - struct video_device vdev; - struct v4l2_device v4l2_dev; - - /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* Protects queued_bufs */ - - /* Note if taking both locks v4l2_lock must always be locked first! */ - struct mutex v4l2_lock; /* Protects everything else */ - struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ - - /* Pointer to our usb_device, will be NULL after unplug */ - struct usb_device *udev; /* Both mutexes most be hold when setting! */ - - /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ - int type; - int release; /* release number */ - int features; /* feature bits */ - - /*** Video data ***/ - int vendpoint; /* video isoc endpoint */ - int vcinterface; /* video control interface */ - int valternate; /* alternate interface needed */ - int vframes; /* frames-per-second */ - int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or _PWCX */ - int vframe_count; /* received frames */ - int vmax_packet_size; /* USB maxpacket size */ - int vlast_packet_size; /* for frame synchronisation */ - int visoc_errors; /* number of contiguous ISOC errors */ - int vbandlength; /* compressed band length; 0 is uncompressed */ - char vsync; /* used by isoc handler */ - char vmirror; /* for ToUCaM series */ - char power_save; /* Do powersaving for this cam */ - - unsigned char cmd_buf[13]; - unsigned char *ctrl_buf; - - struct urb *urbs[MAX_ISO_BUFS]; - - /* - * Frame currently being filled, this only gets touched by the - * isoc urb complete handler, and by stream start / stop since - * start / stop touch it before / after starting / killing the urbs - * no locking is needed around this - */ - struct pwc_frame_buf *fill_buf; - - int frame_header_size, frame_trailer_size; - int frame_size; - int frame_total_size; /* including header & trailer */ - int drop_frames; - - union { /* private data for decompression engine */ - struct pwc_dec1_private dec1; - struct pwc_dec23_private dec23; - }; - - /* - * We have an 'image' and a 'view', where 'image' is the fixed-size img - * as delivered by the camera, and 'view' is the size requested by the - * program. The camera image is centered in this viewport, laced with - * a gray or black border. view_min <= image <= view <= view_max; - */ - int image_mask; /* supported sizes */ - int width, height; /* current resolution */ - -#ifdef CONFIG_USB_PWC_INPUT_EVDEV - struct input_dev *button_dev; /* webcam snapshot button input */ - char button_phys[64]; -#endif - - /* controls */ - struct v4l2_ctrl_handler ctrl_handler; - u16 saturation_fmt; - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *gamma; - struct { - /* awb / red-blue balance cluster */ - struct v4l2_ctrl *auto_white_balance; - struct v4l2_ctrl *red_balance; - struct v4l2_ctrl *blue_balance; - /* usb ctrl transfers are slow, so we cache things */ - int color_bal_valid; - unsigned long last_color_bal_update; /* In jiffies */ - s32 last_red_balance; - s32 last_blue_balance; - }; - struct { - /* autogain / gain cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - int gain_valid; - unsigned long last_gain_update; /* In jiffies */ - s32 last_gain; - }; - struct { - /* exposure_auto / exposure cluster */ - struct v4l2_ctrl *exposure_auto; - struct v4l2_ctrl *exposure; - int exposure_valid; - unsigned long last_exposure_update; /* In jiffies */ - s32 last_exposure; - }; - struct v4l2_ctrl *colorfx; - struct { - /* autocontour/contour cluster */ - struct v4l2_ctrl *autocontour; - struct v4l2_ctrl *contour; - }; - struct v4l2_ctrl *backlight; - struct v4l2_ctrl *flicker; - struct v4l2_ctrl *noise_reduction; - struct v4l2_ctrl *save_user; - struct v4l2_ctrl *restore_user; - struct v4l2_ctrl *restore_factory; - struct v4l2_ctrl *awb_speed; - struct v4l2_ctrl *awb_delay; - struct { - /* motor control cluster */ - struct v4l2_ctrl *motor_pan; - struct v4l2_ctrl *motor_tilt; - struct v4l2_ctrl *motor_pan_reset; - struct v4l2_ctrl *motor_tilt_reset; - }; - /* CODEC3 models have both gain and exposure controlled by autogain */ - struct v4l2_ctrl *autogain_expo_cluster[3]; -}; - -/* Global variables */ -#ifdef CONFIG_USB_PWC_DEBUG -extern int pwc_trace; -#endif - -/** Functions in pwc-misc.c */ -/* sizes in pixels */ -extern const int pwc_image_sizes[PSZ_MAX][2]; - -int pwc_get_size(struct pwc_device *pdev, int width, int height); -void pwc_construct(struct pwc_device *pdev); - -/** Functions in pwc-ctrl.c */ -/* Request a certain video mode. Returns < 0 if not possible */ -extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, - int pixfmt, int frames, int *compression, int send_to_cam); -extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); -extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); -extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); -extern int send_control_msg(struct pwc_device *pdev, - u8 request, u16 value, void *buf, int buflen); - -/* Control get / set helpers */ -int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data); -int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data); -int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data); -#define pwc_set_s8_ctrl pwc_set_u8_ctrl -int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *dat); -int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data); -int pwc_button_ctrl(struct pwc_device *pdev, u16 value); -int pwc_init_controls(struct pwc_device *pdev); - -/* Power down or up the camera; not supported by all models */ -extern void pwc_camera_power(struct pwc_device *pdev, int power); - -extern const struct v4l2_ioctl_ops pwc_ioctl_ops; - -/** pwc-uncompress.c */ -/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ -int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf); - -#endif diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig deleted file mode 100644 index 6ebaf2940d06..000000000000 --- a/drivers/media/video/sn9c102/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config USB_SN9C102 - tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 - ---help--- - This driver is DEPRECATED please use the gspca sonixb and - sonixj modules instead. - - Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. - - See for more info. - - To compile this driver as a module, choose M here: the - module will be called sn9c102. diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile deleted file mode 100644 index 7ecd5a90c7c9..000000000000 --- a/drivers/media/video/sn9c102/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -sn9c102-objs := sn9c102_core.o \ - sn9c102_hv7131d.o \ - sn9c102_hv7131r.o \ - sn9c102_mi0343.o \ - sn9c102_mi0360.o \ - sn9c102_mt9v111.o \ - sn9c102_ov7630.o \ - sn9c102_ov7660.o \ - sn9c102_pas106b.o \ - sn9c102_pas202bcb.o \ - sn9c102_tas5110c1b.o \ - sn9c102_tas5110d.o \ - sn9c102_tas5130d1b.o - -obj-$(CONFIG_USB_SN9C102) += sn9c102.o diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h deleted file mode 100644 index 2bc153e869be..000000000000 --- a/drivers/media/video/sn9c102/sn9c102.h +++ /dev/null @@ -1,211 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2006 by Luca Risolia * - * * - * This program is free software; 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 _SN9C102_H_ -#define _SN9C102_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sn9c102_config.h" -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -enum sn9c102_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct sn9c102_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum sn9c102_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum sn9c102_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum sn9c102_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum sn9c102_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -typedef char sn9c102_sof_header_t[62]; - -struct sn9c102_sof_t { - sn9c102_sof_header_t header; - u16 bytesread; -}; - -struct sn9c102_sysfs_attr { - u16 reg, i2c_reg; - sn9c102_sof_header_t frame_header; -}; - -struct sn9c102_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DEFINE_MUTEX(sn9c102_sysfs_lock); -static DECLARE_RWSEM(sn9c102_dev_lock); - -struct sn9c102_device { - struct video_device* v4ldev; - - enum sn9c102_bridge bridge; - struct sn9c102_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[SN9C102_URBS]; - void* transfer_buffer[SN9C102_URBS]; - u8* control_buffer; - - struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum sn9c102_io_method io; - enum sn9c102_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct sn9c102_sysfs_attr sysfs; - struct sn9c102_sof_t sof; - u16 reg[384]; - - struct sn9c102_module_param module_param; - - struct kref kref; - enum sn9c102_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct sn9c102_device* -sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - - -void -sn9c102_attach_sensor(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); -} - - -enum sn9c102_bridge -sn9c102_get_bridge(struct sn9c102_device* cam) -{ - return cam->bridge; -} - - -struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam) -{ - return &cam->sensor; -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef SN9C102_DEBUG -# define DBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_printk_ioctl(name, cmd); \ -} while (0) -# define KDBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info("sn9c102: " fmt "\n", ## args); \ - else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -#else -# define DBG(level, fmt, args...) do {;} while(0) -# define V4LDBG(level, name, cmd) do {;} while(0) -# define KDBG(level, fmt, args...) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ - __LINE__ , ## args) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ - -#endif /* _SN9C102_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_config.h b/drivers/media/video/sn9c102/sn9c102_config.h deleted file mode 100644 index 0f4e0378b071..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_config.h +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * This program is free software; 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 _SN9C102_CONFIG_H_ -#define _SN9C102_CONFIG_H_ - -#include -#include - -#define SN9C102_DEBUG -#define SN9C102_DEBUG_LEVEL 2 -#define SN9C102_MAX_DEVICES 64 -#define SN9C102_PRESERVE_IMGSCALE 0 -#define SN9C102_FORCE_MUNMAP 0 -#define SN9C102_MAX_FRAMES 32 -#define SN9C102_URBS 2 -#define SN9C102_ISO_PACKETS 7 -#define SN9C102_ALTERNATE_SETTING 8 -#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) -#define SN9C102_CTRL_TIMEOUT 300 -#define SN9C102_FRAME_TIMEOUT 0 - -/*****************************************************************************/ - -static const u8 SN9C102_Y_QTABLE0[64] = { - 8, 5, 5, 8, 12, 20, 25, 30, - 6, 6, 7, 9, 13, 29, 30, 27, - 7, 6, 8, 12, 20, 28, 34, 28, - 7, 8, 11, 14, 25, 43, 40, 31, - 9, 11, 18, 28, 34, 54, 51, 38, - 12, 17, 27, 32, 40, 52, 56, 46, - 24, 32, 39, 43, 51, 60, 60, 50, - 36, 46, 47, 49, 56, 50, 51, 49 -}; - -static const u8 SN9C102_UV_QTABLE0[64] = { - 8, 9, 12, 23, 49, 49, 49, 49, - 9, 10, 13, 33, 49, 49, 49, 49, - 12, 13, 28, 49, 49, 49, 49, 49, - 23, 33, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49 -}; - -static const u8 SN9C102_Y_QTABLE1[64] = { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99 -}; - -static const u8 SN9C102_UV_QTABLE1[64] = { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; - -#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c deleted file mode 100644 index 19ea780b16ff..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ /dev/null @@ -1,3421 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * This program is free software; 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sn9c102.h" - -/*****************************************************************************/ - -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" -#define SN9C102_MODULE_ALIAS "sn9c1xx" -#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" -#define SN9C102_AUTHOR_EMAIL "" -#define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.48" - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, sn9c102_id_table); - -MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); -MODULE_DESCRIPTION(SN9C102_MODULE_NAME); -MODULE_ALIAS(SN9C102_MODULE_ALIAS); -MODULE_VERSION(SN9C102_MODULE_VERSION); -MODULE_LICENSE(SN9C102_MODULE_LICENSE); - -static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - " <-1|n[,...]>" - "\nSpecify V4L2 minor mode number." - "\n-1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES) - " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - " <0|1[,...]>" - "\nForce the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n0 = do not force memory unmapping" - "\n1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - " <0|n[,...]>" - "\nTimeout for a video frame in seconds before" - "\nreturning an I/O error; 0 for infinity." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." - "\n"); - -#ifdef SN9C102_DEBUG -static unsigned short debug = SN9C102_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - " " - "\nDebugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only." - "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." - "\n"); -#endif - -/* - Add the probe entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ -}; - -/*****************************************************************************/ - -static u32 -sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, - enum sn9c102_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void* buff = NULL; - u32 i; - - if (count > SN9C102_MAX_FRAMES) - count = SN9C102_MAX_FRAMES; - - if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) - imagesize += 589 + 2; /* length of JPEG header + EOI marker */ - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - if ((buff = vmalloc_32_user(cam->nbuffers * - PAGE_ALIGN(imagesize)))) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; - } - - return cam->nbuffers; -} - - -static void sn9c102_release_buffers(struct sn9c102_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void sn9c102_empty_framequeues(struct sn9c102_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < SN9C102_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void sn9c102_requeue_outqueue(struct sn9c102_device* cam) -{ - struct sn9c102_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -/* - Write a sequence of count value/register pairs. Returns -1 after the first - failed write, or 0 for no errors. -*/ -int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2], - int count) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int i, res; - - for (i = 0; i < count; i++) { - u8 index = valreg[i][1]; - - /* - index is a u8, so it must be <256 and can't be out of range. - If we put in a check anyway, gcc annoys us with a warning - hat our check is useless. People get all uppity when they - see warnings in the kernel compile. - */ - - *buff = valreg[i][0]; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, - 0x41, index, 0, buff, 1, - SN9C102_CTRL_TIMEOUT); - - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, " - "index 0x%02X, error %d)", *buff, index, res); - return -1; - } - - cam->reg[index] = *buff; - } - - return 0; -} - - -int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - *buff = value; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, index " - "0x%02X, error %d)", value, index, res); - return -1; - } - - cam->reg[index] = value; - - return 0; -} - - -/* NOTE: with the SN9C10[123] reading some registers always returns 0 */ -int sn9c102_read_reg(struct sn9c102_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%02X, error %d)", - index, res); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) -{ - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - return cam->reg[index]; -} - - -static int -sn9c102_i2c_wait(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int i, r; - - for (i = 1; i <= 5; i++) { - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - return -EIO; - if (r & 0x04) - return 0; - if (sensor->frequency & SN9C102_I2C_400KHZ) - udelay(5*16); - else - udelay(16*16); - } - return -EBUSY; -} - - -static int -sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int r , err = 0; - - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - err += r; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if (!(r & 0x08)) - err += -1; - } else { - if (r & 0x08) - err += -1; - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int r; - r = sn9c102_read_reg(cam, 0x08); - return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; -} - - -int -sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 data0, - u8 data1, u8 n, u8 buffer[]) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int i = 0, err = 0, res; - - /* Write cycle */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; - data[1] = data0; /* I2C slave id */ - data[2] = data1; /* address */ - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* Read cycle - n bytes */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | - (n << 4) | 0x02; - data[1] = data0; - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* The first read byte will be placed in data[4] */ - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_detect_read_error(cam, sensor); - - PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, - data[4]); - - if (err) { - DBG(3, "I2C read failed for %s image sensor", sensor->name); - return -1; - } - - if (buffer) - for (i = 0; i < n && i < 5; i++) - buffer[n-i-1] = data[4-i]; - - return (int)data[4]; -} - - -int -sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 n, u8 data0, - u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - /* Write cycle. It usually is address + value */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) - | ((n - 1) << 4); - data[1] = data0; - data[2] = data1; - data[3] = data2; - data[4] = data3; - data[5] = data4; - data[6] = data5; - data[7] = 0x17; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - err += sn9c102_i2c_detect_write_error(cam, sensor); - - if (err) - DBG(3, "I2C write failed for %s image sensor", sensor->name); - - PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " - "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", - n, data0, data1, data2, data3, data4, data5); - - return err ? -1 : 0; -} - - -int -sn9c102_i2c_try_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 address) -{ - return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, - address, 1, NULL); -} - - -static int sn9c102_i2c_try_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, - u8 address, u8 value) -{ - return sn9c102_i2c_try_raw_write(cam, sensor, 3, - sensor->i2c_slave_id, address, - value, 0, 0, 0); -} - - -int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) -{ - return sn9c102_i2c_try_read(cam, &cam->sensor, address); -} - - -int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) -{ - return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); -} - -/*****************************************************************************/ - -static size_t sn9c102_sof_length(struct sn9c102_device* cam) -{ - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - return 12; - case BRIDGE_SN9C103: - return 18; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - return 62; - } - - return 0; -} - - -static void* -sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) -{ - static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - const char *m = mem; - size_t soflen = 0, i, j; - - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < len; i++) { - size_t b; - - /* Read the variable part of the header */ - if (unlikely(cam->sof.bytesread >= sizeof(marker))) { - cam->sof.header[cam->sof.bytesread] = *(m+i); - if (++cam->sof.bytesread == soflen) { - cam->sof.bytesread = 0; - return mem + i; - } - continue; - } - - /* Search for the SOF marker (fixed part) in the header */ - for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { - if (unlikely(i+j == len)) - return NULL; - if (*(m+i+j) == marker[cam->sof.bytesread]) { - cam->sof.header[cam->sof.bytesread] = *(m+i+j); - if (++cam->sof.bytesread == sizeof(marker)) { - PDBGG("Bytes to analyze: %zd. SOF " - "starts at byte #%zd", len, i); - i += j+1; - break; - } - } else { - cam->sof.bytesread = 0; - break; - } - } - } - - return NULL; -} - - -static void* -sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) -{ - static const u8 eof_header[4][4] = { - {0x00, 0x00, 0x00, 0x00}, - {0x40, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}, - {0xc0, 0x00, 0x00, 0x00}, - }; - size_t i, j; - - /* The EOF header does not exist in compressed data */ - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - return NULL; - - /* - The EOF header might cross the packet boundary, but this is not a - problem, since the end of a frame is determined by checking its size - in the first place. - */ - for (i = 0; (len >= 4) && (i <= len - 4); i++) - for (j = 0; j < ARRAY_SIZE(eof_header); j++) - if (!memcmp(mem + i, eof_header[j], 4)) - return mem + i; - - return NULL; -} - - -static void -sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) -{ - static const u8 jpeg_header[589] = { - 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, - 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, - 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, - 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, - 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, - 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, - 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, - 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, - 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, - 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, - 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, - 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, - 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, - 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, - 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, - 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, - 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, - 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, - 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, - 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, - 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, - 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, - 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, - 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 - }; - u8 *pos = f->bufmem; - - memcpy(pos, jpeg_header, sizeof(jpeg_header)); - *(pos + 6) = 0x00; - *(pos + 7 + 64) = 0x01; - if (cam->compression.quality == 0) { - memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); - } else if (cam->compression.quality == 1) { - memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); - } - *(pos + 564) = cam->sensor.pix_format.width & 0xFF; - *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; - *(pos + 562) = cam->sensor.pix_format.height & 0xFF; - *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; - *(pos + 567) = 0x21; - - f->buf.bytesused += sizeof(jpeg_header); -} - - -static void sn9c102_urb_complete(struct urb *urb) -{ - struct sn9c102_device* cam = urb->context; - struct sn9c102_frame_t** f; - size_t imagesize, soflen; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - cam->sof.bytesread = 0; - DBG(3, "Stream interrupted by application"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - imagesize += 589; /* length of jpeg header */ - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int img, len, status; - void *pos, *sof, *eof; - - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - cam->sof.bytesread = 0; - continue; - } - - PDBGG("Isochrnous frame: length %u, #%u i", len, i); - -redo: - sof = sn9c102_find_sof_header(cam, pos, len); - if (likely(!sof)) { - eof = sn9c102_find_eof_header(cam, pos, len); - if ((*f)->state == F_GRABBING) { -end_of_frame: - img = len; - - if (eof) - img = (eof > pos) ? eof - pos - 1 : 0; - - if ((*f)->buf.bytesused + img > imagesize) { - u32 b; - b = (*f)->buf.bytesused + img - - imagesize; - img = imagesize - (*f)->buf.bytesused; - PDBGG("Expected EOF not found: video " - "frame cut"); - if (eof) - DBG(3, "Exceeded limit: +%u " - "bytes", (unsigned)(b)); - } - - memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, - img); - - if ((*f)->buf.bytesused == 0) - do_gettimeofday(&(*f)->buf.timestamp); - - (*f)->buf.bytesused += img; - - if ((*f)->buf.bytesused == imagesize || - ((cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) && eof)) { - u32 b; - - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence= ++cam->frame_count; - - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, - &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry( - cam->inqueue.next, - struct sn9c102_frame_t, - frame ); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - - memcpy(cam->sysfs.frame_header, - cam->sof.header, soflen); - - DBG(3, "Video frame captured: %lu " - "bytes", (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - } else if (eof) { - (*f)->state = F_ERROR; - DBG(3, "Not expected EOF after %lu " - "bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - } - - if (sof) /* (1) */ - goto start_of_frame; - - } else if (eof) { - DBG(3, "EOF without SOF"); - continue; - - } else { - PDBGG("Ignoring pointless isochronous frame"); - continue; - } - - } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { -start_of_frame: - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - len -= (sof - pos); - pos = sof; - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) - sn9c102_write_jpegheader(cam, (*f)); - DBG(3, "SOF detected: new video frame"); - if (len) - goto redo; - - } else if ((*f)->state == F_GRABBING) { - eof = sn9c102_find_eof_header(cam, pos, len); - if (eof && eof < sof) - goto end_of_frame; /* (1) */ - else { - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) { - if (sof - pos >= soflen) { - eof = sof - soflen; - } else { /* remove header */ - eof = pos; - (*f)->buf.bytesused -= - (soflen - (sof - pos)); - } - goto end_of_frame; - } else { - DBG(3, "SOF before expected EOF after " - "%lu bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - goto start_of_frame; - } - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int sn9c102_start_transfer(struct sn9c102_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct urb* urb; - struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - SN9C102_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - s8 i, j; - int err = 0; - - for (i = 0; i < SN9C102_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < SN9C102_URBS; i++) { - urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = SN9C102_ISO_PACKETS; - urb->complete = sn9c102_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < SN9C102_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - /* Enable video */ - if (!(cam->reg[0x01] & 0x04)) { - err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); - if (err) { - err = -EIO; - DBG(1, "I/O hardware error"); - goto free_urbs; - } - } - - err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - cam->sof.bytesread = 0; - - for (i = 0; i < SN9C102_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int sn9c102_stop_transfer(struct sn9c102_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = SN9C102_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int sn9c102_stream_interrupt(struct sn9c102_device* cam) -{ - cam->stream = STREAM_INTERRUPT; - wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - SN9C102_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. " - "To use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) -{ - char str[7]; - char* endp; - unsigned long val; - - if (len < 6) { - strncpy(str, buff, len); - str[len] = '\0'; - } else { - strncpy(str, buff, 6); - str[6] = '\0'; - } - - val = simple_strtoul(str, &endp, 0); - - *count = 0; - if (val <= 0xffff) - *count = (ssize_t)(endp - str); - if ((*count) && (len == *count+1) && (buff[*count] == '\n')) - *count += 1; - - return (u16)val; -} - -/* - NOTE 1: being inside one of the following methods implies that the v4l - device exists for sure (see kobjects and reference counters) - NOTE 2: buffers are PAGE_SIZE long -*/ - -static ssize_t sn9c102_show_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.reg); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (index >= ARRAY_SIZE(cam->reg) || !count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.reg = index; - - DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_write_reg(cam, value, cam->sysfs.reg); - if (err) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", - cam->sysfs.reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.i2c_reg = index; - - DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); - if (err) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", - cam->sysfs.i2c_reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_green(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - enum sn9c102_bridge bridge; - ssize_t res = 0; - u16 value; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - bridge = cam->bridge; - - mutex_unlock(&sn9c102_sysfs_lock); - - value = sn9c102_strtou16(buf, len, &count); - if (!count) - return -EINVAL; - - switch (bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - if (value > 0x0f) - return -EINVAL; - if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - case BRIDGE_SN9C103: - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (value > 0x7f) - return -EINVAL; - if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - } - - return res; -} - - -static ssize_t -sn9c102_store_blue(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - - if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t -sn9c102_store_red(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - - if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t sn9c102_show_frame_header(struct device* cd, - struct device_attribute *attr, - char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) - return -ENODEV; - - count = sizeof(cam->sysfs.frame_header); - memcpy(buf, cam->sysfs.frame_header, count); - - DBG(3, "Frame header, read bytes: %zd", count); - - return count; -} - - -static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg); -static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val); -static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); -static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); -static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); -static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); -static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); - - -static int sn9c102_create_sysfs(struct sn9c102_device* cam) -{ - struct device *dev = &(cam->v4ldev->dev); - int err = 0; - - if ((err = device_create_file(dev, &dev_attr_reg))) - goto err_out; - if ((err = device_create_file(dev, &dev_attr_val))) - goto err_reg; - if ((err = device_create_file(dev, &dev_attr_frame_header))) - goto err_val; - - if (cam->sensor.sysfs_ops) { - if ((err = device_create_file(dev, &dev_attr_i2c_reg))) - goto err_frame_header; - if ((err = device_create_file(dev, &dev_attr_i2c_val))) - goto err_i2c_reg; - } - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if ((err = device_create_file(dev, &dev_attr_green))) - goto err_i2c_val; - } else { - if ((err = device_create_file(dev, &dev_attr_blue))) - goto err_i2c_val; - if ((err = device_create_file(dev, &dev_attr_red))) - goto err_blue; - } - - return 0; - -err_blue: - device_remove_file(dev, &dev_attr_blue); -err_i2c_val: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_val); -err_i2c_reg: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_reg); -err_frame_header: - device_remove_file(dev, &dev_attr_frame_header); -err_val: - device_remove_file(dev, &dev_attr_val); -err_reg: - device_remove_file(dev, &dev_attr_reg); -err_out: - return err; -} -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - -/*****************************************************************************/ - -static int -sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - } - } else { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - } - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_set_compression(struct sn9c102_device* cam, - struct v4l2_jpegcompression* compression) -{ - int i, err = 0; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (compression->quality == 0) - err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, - 0x17); - else if (compression->quality == 1) - err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, - 0x17); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (compression->quality == 0) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, - 0x18); - } else if (compression->quality == 1) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, - 0x18); - } - break; - } - - return err ? -EIO : 0; -} - - -static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) -{ - u8 r = 0; - int err = 0; - - if (scale == 1) - r = cam->reg[0x18] & 0xcf; - else if (scale == 2) { - r = cam->reg[0x18] & 0xcf; - r |= 0x10; - } else if (scale == 4) - r = cam->reg[0x18] | 0x20; - - err += sn9c102_write_reg(cam, r, 0x18); - if (err) - return -EIO; - - PDBGG("Scaling factor: %u", scale); - - return 0; -} - - -static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = &cam->sensor; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), - v_start = (u8)(rect->top - s->cropcap.bounds.top), - h_size = (u8)(rect->width / 16), - v_size = (u8)(rect->height / 16); - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - err += sn9c102_write_reg(cam, h_size, 0x15); - err += sn9c102_write_reg(cam, v_size, 0x16); - if (err) - return -EIO; - - PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " - "%u %u %u %u", h_start, v_start, h_size, v_size); - - return 0; -} - - -static int sn9c102_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect* rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); - err += sn9c102_set_crop(cam, rect); - if (err) - return err; - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - if (!(cam->state & DEV_INITIALIZED)) - if (cam->bridge == BRIDGE_SN9C101 || - cam->bridge == BRIDGE_SN9C102 || - cam->bridge == BRIDGE_SN9C103) { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; - cam->compression.quality = cam->reg[0x17] & 0x01 ? - 0 : 1; - } else { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) - s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; - cam->compression.quality = cam->reg[0x18] & 0x40 ? - 0 : 1; - err += sn9c102_set_compression(cam, &cam->compression); - } - else - err += sn9c102_set_compression(cam, &cam->compression); - err += sn9c102_set_pix_format(cam, &s->pix_format); - if (s->set_pix_format) - err += s->set_pix_format(cam, &s->pix_format); - if (err) - return err; - - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || - s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - DBG(3, "Compressed video format is active, quality %d", - cam->compression.quality); - else - DBG(3, "Uncompressed video format is active"); - - if (s->set_crop) - if ((err = s->set_crop(cam, rect))) { - DBG(3, "set_crop() failed"); - return err; - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void sn9c102_release_resources(struct kref *kref) -{ - struct sn9c102_device *cam; - - mutex_lock(&sn9c102_sysfs_lock); - - cam = container_of(kref, struct sn9c102_device, kref); - - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); - - mutex_unlock(&sn9c102_sysfs_lock); - -} - - -static int sn9c102_open(struct file *filp) -{ - struct sn9c102_device* cam; - int err = 0; - - /* - A read_trylock() in open() is the only safe way to prevent race - conditions with disconnect(), one close() and multiple (not - necessarily simultaneous) attempts to open(). For example, it - prevents from waiting for a second access, while the device - structure is being deallocated, after a possible disconnect() and - during a following close() holding the write lock: given that, after - this deallocation, no access will be possible anymore, using the - non-trylock version would have let open() gain the access to the - device structure improperly. - For this reason the lock must also not be per-device. - */ - if (!down_read_trylock(&sn9c102_dev_lock)) - return -ERESTARTSYS; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&sn9c102_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - /* - Make sure to isolate all the simultaneous opens. - */ - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, sn9c102_release_resources); - up_read(&sn9c102_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is already in use", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - /* - open() must follow the open flags and should block - eventually while the device is in use. - */ - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&sn9c102_dev_lock); - /* - We will not release the "open_mutex" lock, so that only one - process can be in the wait queue below. This way the process - will be sleeping while holding the lock, without losing its - priority after any wake_up(). - */ - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&sn9c102_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = sn9c102_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = sn9c102_start_transfer(cam))) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - sn9c102_empty_framequeues(cam); - - DBG(3, "Video device %s is open", video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, sn9c102_release_resources); - - up_read(&sn9c102_dev_lock); - return err; -} - - -static int sn9c102_release(struct file *filp) -{ - struct sn9c102_device* cam; - - down_write(&sn9c102_dev_lock); - - cam = video_drvdata(filp); - - sn9c102_stop_transfer(cam); - sn9c102_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); - - return 0; -} - - -static ssize_t -sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_frame_t* f, * i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose " - "the read method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - sn9c102_empty_framequeues(cam); - sn9c102_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) { - mutex_unlock(&cam->fileop_mutex); - return err; - } - } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (cam->state & DEV_MISCONFIGURED) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - sn9c102_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return count; -} - - -static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_frame_t* f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - sn9c102_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void sn9c102_vm_open(struct vm_area_struct* vma) -{ - struct sn9c102_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void sn9c102_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct sn9c102_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct sn9c102_vm_ops = { - .open = sn9c102_vm_open, - .close = sn9c102_vm_close, -}; - - -static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct sn9c102_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &sn9c102_vm_ops; - vma->vm_private_data = &cam->frame[i]; - sn9c102_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "sn9c102", - .version = LINUX_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - i.capabilities = V4L2_IN_CAP_STD; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id && ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return err; -} - - -static int -sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return 0; -} - - -static int -sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_cropcap* cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect* rect; - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_pix_format* pix_format = &(s->pix_format); - u8 scale; - const enum sn9c102_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - /* Preserve R,G or B origin */ - rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; - rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - - if (rect->width < 16) - rect->width = 16; - if (rect->height < 16) - rect->height = 16; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - - rect->width &= ~15L; - rect->height &= ~15L; - - if (SN9C102_PRESERVE_IMGSCALE) { - /* Calculate the actual scaling factor */ - u32 a, b; - a = rect->width * rect->height; - b = pix_format->width * pix_format->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } else - scale = 1; - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - sn9c102_release_buffers(cam); - - err = sn9c102_set_crop(cam, rect); - if (s->set_crop) - err += s->set_crop(cam, rect); - err += sn9c102_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width/scale; - s->pix_format.height = rect->height/scale; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0) - return -EINVAL; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - } - - frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; - frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; - frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; - frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; - frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "bayer rgb"); - fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; - } else if (fmtd.index == 1) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - strcpy(fmtd.description, "JPEG"); - fmtd.pixelformat = V4L2_PIX_FMT_JPEG; - break; - } - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || - pfmt->pixelformat == V4L2_PIX_FMT_JPEG) - ? 0 : (pfmt->width * pfmt->priv) / 8; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, - void __user * arg) -{ - struct sn9c102_sensor* s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format* pix; - struct v4l2_pix_format* pfmt = &(s->pix_format); - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - u8 scale; - const enum sn9c102_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - { /* calculate the actual scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } - - rect.width = scale * pix->width; - rect.height = scale * pix->height; - - if (rect.width < 16) - rect.width = 16; - if (rect.height < 16) - rect.height = 16; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - - rect.width &= ~15L; - rect.height &= ~15L; - - { /* adjust the scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } - - pix->width = rect.width / scale; - pix->height = rect.height / scale; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - } - pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) - ? 0 : (pix->width * pix->priv) / 8; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. Unmap the " - "buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - sn9c102_release_buffers(cam); - - err += sn9c102_set_pix_format(cam, pix); - err += sn9c102_set_crop(cam, &rect); - if (s->set_pix_format) - err += s->set_pix_format(cam, pix); - if (s->set_crop) - err += s->set_crop(cam, &rect); - err += sn9c102_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum sn9c102_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0 && jc.quality != 1) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - err += sn9c102_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware problems. " - "To use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " - "still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - sn9c102_empty_framequeues(cam); - - sn9c102_release_buffers(cam); - if (rb.count) - rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - sn9c102_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct sn9c102_frame_t *f; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) - return err; - } else { - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; - else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (cam->state & DEV_MISCONFIGURED) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - memcpy(&b, &f->buf, sizeof(b)); - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_stream_interrupt(cam))) - return err; - - sn9c102_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES) - sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static int -sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - strcpy(audio.name, "Microphone"); - audio.capability = 0; - audio.mode = 0; - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - memset(&audio, 0, sizeof(audio)); - strcpy(audio.name, "Microphone"); - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - return 0; -} - - -static long sn9c102_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return sn9c102_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return sn9c102_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return sn9c102_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return sn9c102_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return sn9c102_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return sn9c102_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return sn9c102_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return sn9c102_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return sn9c102_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return sn9c102_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return sn9c102_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_ENUM_FMT: - return sn9c102_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return sn9c102_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_G_JPEGCOMP: - return sn9c102_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return sn9c102_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return sn9c102_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return sn9c102_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return sn9c102_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return sn9c102_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return sn9c102_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return sn9c102_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return sn9c102_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return sn9c102_vidioc_s_parm(cam, arg); - - case VIDIOC_ENUMAUDIO: - return sn9c102_vidioc_enumaudio(cam, arg); - - case VIDIOC_G_AUDIO: - return sn9c102_vidioc_g_audio(cam, arg); - - case VIDIOC_S_AUDIO: - return sn9c102_vidioc_s_audio(cam, arg); - - default: - return -ENOTTY; - - } -} - - -static long sn9c102_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "sn9c102", cmd); - - err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - -/*****************************************************************************/ - -static const struct v4l2_file_operations sn9c102_fops = { - .owner = THIS_MODULE, - .open = sn9c102_open, - .release = sn9c102_release, - .unlocked_ioctl = sn9c102_ioctl, - .read = sn9c102_read, - .poll = sn9c102_poll, - .mmap = sn9c102_mmap, -}; - -/*****************************************************************************/ - -/* It exists a single interface only. We do not need to validate anything. */ -static int -sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct sn9c102_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0, r; - - if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kzalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - r = sn9c102_read_reg(cam, 0x00); - if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { - DBG(1, "Sorry, this is not a SN9C1xx-based camera " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - err = -ENODEV; - goto fail; - } - - cam->bridge = id->driver_info; - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C103: - DBG(2, "SN9C103 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C105: - DBG(2, "SN9C105 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C120: - DBG(2, "SN9C120 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - } - - for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) { - err = sn9c102_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) { - DBG(2, "%s image sensor detected", cam->sensor.name); - DBG(3, "Support for %s maintained by %s", - cam->sensor.name, cam->sensor.maintainer); - } else { - DBG(1, "No supported image sensor detected for this bridge"); - err = -ENODEV; - goto fail; - } - - if (!(cam->bridge & cam->sensor.supported_bridge)) { - DBG(1, "Bridge not supported"); - err = -ENODEV; - goto fail; - } - - if (sn9c102_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); - cam->v4ldev->fops = &sn9c102_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->parent = &udev->dev; - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - video_set_drvdata(cam->v4ldev, cam); - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - err = sn9c102_create_sysfs(cam); - if (!err) - DBG(2, "Optional device control through 'sysfs' " - "interface ready"); - else - DBG(2, "Failed to create optional 'sysfs' interface for " - "device controlling. Error #%d", err); -#else - DBG(2, "Optional device control through 'sysfs' interface disabled"); - DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " - "configuration option to enable it."); -#endif - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - kfree(cam); - } - return err; -} - - -static void sn9c102_usb_disconnect(struct usb_interface* intf) -{ - struct sn9c102_device* cam; - - down_write(&sn9c102_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and memory " - "deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - sn9c102_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - kref_put(&cam->kref, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); -} - - -static struct usb_driver sn9c102_usb_driver = { - .name = "sn9c102", - .id_table = sn9c102_id_table, - .probe = sn9c102_usb_probe, - .disconnect = sn9c102_usb_disconnect, -}; - -module_usb_driver(sn9c102_usb_driver); diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h deleted file mode 100644 index b3d2cc729657..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ /dev/null @@ -1,147 +0,0 @@ -/*************************************************************************** - * Table of device identifiers of the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * This program is free software; 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 _SN9C102_DEVTABLE_H_ -#define _SN9C102_DEVTABLE_H_ - -#include - -struct sn9c102_device; - -/* - Each SN9C1xx camera has proper PID/VID identifiers. - SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to - handle the video class interface. -*/ -#define SN9C102_USB_DEVICE(vend, prod, bridge) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = 0xff, \ - .driver_info = (bridge) - -static const struct usb_device_id sn9c102_id_table[] = { - /* SN9C101 and SN9C102 */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ - { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ - { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */ - /* SN9C103 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE -/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ - { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ -#endif - /* SN9C105 */ -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE - { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ - { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, - /* SN9C120 */ - { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ - { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ - { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, -#endif - { } -}; - -/* - Probing functions: on success, you must attach the sensor to the camera - by calling sn9c102_attach_sensor(). - To enable the I2C communication, you might need to perform a really basic - initialization of the SN9C1XX chip. - Functions must return 0 on success, the appropriate error otherwise. -*/ -extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); -extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); -extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam); -extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); -extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); -extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); -extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); - -#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c deleted file mode 100644 index 2dce5c908c8e..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ /dev/null @@ -1,264 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131d_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x60, 0x17}, - {0x0e, 0x18}, {0xf2, 0x19}); - - err += sn9c102_i2c_write(cam, 0x01, 0x04); - err += sn9c102_i2c_write(cam, 0x02, 0x00); - err += sn9c102_i2c_write(cam, 0x28, 0x00); - - return err; -} - - -static int hv7131d_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x26), - r2 = sn9c102_i2c_read(cam, 0x27); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 8) | (r2 & 0xff); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_RESET_LEVEL: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) - return -EIO; - ctrl->value &= 0x3f; - return 0; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131d_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); - err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_RESET_LEVEL: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - err += sn9c102_i2c_write(cam, 0x34, ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131d_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131d_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x42, 0x19); - else - err += sn9c102_write_reg(cam, 0xf2, 0x19); - - return err; -} - - -static const struct sn9c102_sensor hv7131d = { - .name = "HV7131D", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131d_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x0250, - .maximum = 0xffff, - .step = 0x0001, - .default_value = 0x0250, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1e, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_RESET_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "reset level", - .minimum = 0x19, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x30, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "pixel bias voltage", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x02, - .flags = 0, - }, - }, - .get_ctrl = &hv7131d_get_ctrl, - .set_ctrl = &hv7131d_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131d_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131d_set_pix_format -}; - - -int sn9c102_probe_hv7131d(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); - r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); - if (err || r0 < 0 || r1 < 0) - return -EIO; - - if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131d); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c deleted file mode 100644 index 4295887ff609..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_hv7131r.c +++ /dev/null @@ -1,363 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131r_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, - {0x20, 0x05}, {0x20, 0x06}, - {0x03, 0x10}, {0x00, 0x14}, - {0x60, 0x17}, {0x0a, 0x18}, - {0xf0, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x44, 0x05}, {0x3e, 0x06}, - {0x1a, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa3, 0x17}, - {0x4b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x20, 0x00); - err += sn9c102_i2c_write(cam, 0x21, 0xd6); - err += sn9c102_i2c_write(cam, 0x25, 0x06); - - return err; -} - - -static int hv7131r_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) - return -EIO; - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLACK_LEVEL: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x08) ? 1 : 0; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131r_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, ctrl->value); - break; - case V4L2_CID_BLACK_LEVEL: - { - int r = sn9c102_i2c_read(cam, 0x01); - if (r < 0) - return -EIO; - err += sn9c102_i2c_write(cam, 0x01, - (ctrl->value<<3) | (r&0xf7)); - } - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131r_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131r_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa0, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } else { - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa5, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x24); - } else { - err += sn9c102_write_reg(cam, 0xa3, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor hv7131r = { - .name = "HV7131R", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131r_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x08, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1a, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x2f, - .flags = 0, - }, - { - .id = V4L2_CID_BLACK_LEVEL, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto black level compensation", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &hv7131r_get_ctrl, - .set_ctrl = &hv7131r_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131r_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131r_set_pix_format -}; - - -int sn9c102_probe_hv7131r(struct sn9c102_device* cam) -{ - int devid, err; - - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, - {0x34, 0x01}, {0x20, 0x17}, - {0x34, 0x01}, {0x46, 0x01}); - - devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); - if (err || devid < 0) - return -EIO; - - if (devid != 0x02) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131r); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c deleted file mode 100644 index 1f5b09bec89c..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_mi0343.c +++ /dev/null @@ -1,352 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0343_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0343_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - break; - default: - return -EINVAL; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = data[1] | (data[0] << 8); - if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) - ctrl->value -= 0x10; - else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) - ctrl->value -= 0x60; - else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) - ctrl->value -= 0xe0; - } - - return 0; -} - - -static int mi0343_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u16 reg = 0; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (ctrl->value <= (0x3f-0x10)) - reg = 0x10 + ctrl->value; - else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) - reg = 0x60 + (ctrl->value - (0x3f-0x10)); - else - reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); - break; - } - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, reg >> 8, reg & 0xff, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, reg >> 8, reg & 0xff, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, reg >> 8, reg & 0xff, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0343_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0343_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x03, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0xa0, 0x19); - } - - return err; -} - - -static const struct sn9c102_sensor mi0343 = { - .name = "MI-0343", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0343_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &mi0343_get_ctrl, - .set_ctrl = &mi0343_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0343_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0343_set_pix_format -}; - - -int sn9c102_probe_mi0343(struct sn9c102_device* cam) -{ - u8 data[2]; - - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[1] != 0x42 || data[0] != 0xe3) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0343); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c deleted file mode 100644 index d973fc1973d9..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_mi0360.c +++ /dev/null @@ -1,453 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0360_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x50, 0x05}, {0x20, 0x06}, - {0x10, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa2, 0x17}, - {0x47, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0360_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return 0; -} - - -static int mi0360_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, 0x03, ctrl->value, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, 0x03, ctrl->value, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0360_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0360_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0x60, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa6, 0x17); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x02, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mi0360 = { - .name = "MI-0360", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0360_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x0f, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x32, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - }, - .get_ctrl = &mi0360_get_ctrl, - .set_ctrl = &mi0360_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0360_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0360_set_pix_format -}; - - -int sn9c102_probe_mi0360(struct sn9c102_device* cam) -{ - - u8 data[2]; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - default: - break; - } - - if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x43) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0360); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_mt9v111.c b/drivers/media/video/sn9c102/sn9c102_mt9v111.c deleted file mode 100644 index 95986eb492e4..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_mt9v111.c +++ /dev/null @@ -1,260 +0,0 @@ -/*************************************************************************** - * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mt9v111_init(struct sn9c102_device *cam) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x1f, 0x05}, {0x20, 0x06}, - {0x1f, 0x07}, {0x81, 0x08}, - {0x5c, 0x09}, {0x00, 0x0a}, - {0x00, 0x0b}, {0x00, 0x0c}, - {0x00, 0x0d}, {0x00, 0x0e}, - {0x00, 0x0f}, {0x03, 0x10}, - {0x00, 0x11}, {0x00, 0x12}, - {0x02, 0x13}, {0x14, 0x14}, - {0x28, 0x15}, {0x1e, 0x16}, - {0xe2, 0x17}, {0x06, 0x18}, - {0x00, 0x19}, {0x00, 0x1a}, - {0x00, 0x1b}, {0x08, 0x20}, - {0x39, 0x21}, {0x51, 0x22}, - {0x63, 0x23}, {0x73, 0x24}, - {0x82, 0x25}, {0x8f, 0x26}, - {0x9b, 0x27}, {0xa7, 0x28}, - {0xb1, 0x29}, {0xbc, 0x2a}, - {0xc6, 0x2b}, {0xcf, 0x2c}, - {0xd8, 0x2d}, {0xe1, 0x2e}, - {0xea, 0x2f}, {0xf2, 0x30}, - {0x13, 0x84}, {0x00, 0x85}, - {0x25, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xee, 0x8a}, {0x0f, 0x8b}, - {0xe5, 0x8c}, {0x0f, 0x8d}, - {0x2e, 0x8e}, {0x00, 0x8f}, - {0x30, 0x90}, {0x00, 0x91}, - {0xd4, 0x92}, {0x0f, 0x93}, - {0xfc, 0x94}, {0x0f, 0x95}, - {0x14, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x60, 0x99}, - {0x07, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x2d, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x04, 0x80, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x00, 0x08, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02, - 0x00, 0x16, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe7, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x87, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x40, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x09, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07, - 0x30, 0x02, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12, - 0x00, 0xb0, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13, - 0x00, 0x7c, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - - return err; -} - -static int mt9v111_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u8 data[2]; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, - ctrl->value ? 0x80 : 0x00, - ctrl->value ? 0x80 : 0x00, 0, - 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - -static int mt9v111_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xb4, 0x17); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mt9v111 = { - .name = "MT9V111", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5c, - .init = &mt9v111_init, - .qctrl = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - }, - .get_ctrl = &mt9v111_get_ctrl, - .set_ctrl = &mt9v111_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mt9v111_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mt9v111_set_pix_format -}; - - -int sn9c102_probe_mt9v111(struct sn9c102_device *cam) -{ - u8 data[2]; - int err = 0; - - err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x42, 0x17}, - {0x62, 0x17}, {0x08, 0x01}); - err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4, - mt9v111.i2c_slave_id, 0x01, 0x00, - 0x04, 0, 0); - if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111, - mt9v111.i2c_slave_id, 0x36, 2, - data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x3a) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mt9v111); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c deleted file mode 100644 index 803712c29f02..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ /dev/null @@ -1,626 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7630_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, - {0x0f, 0x18}, {0x50, 0x19}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x15, 0x35); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1c); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x06); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x03, 0x10}, {0x0a, 0x14}, - {0x60, 0x17}, {0x0f, 0x18}, - {0x50, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x15, 0x34); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x0a, 0x14}, {0xe2, 0x17}, - {0x0b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x24, 0x21}, - {0x3b, 0x22}, {0x47, 0x23}, - {0x60, 0x24}, {0x71, 0x25}, - {0x80, 0x26}, {0x8f, 0x27}, - {0x9d, 0x28}, {0xaa, 0x29}, - {0xb8, 0x2a}, {0xc4, 0x2b}, - {0xd1, 0x2c}, {0xdd, 0x2d}, - {0xe8, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x17, 0x84}, {0x00, 0x85}, - {0x2e, 0x86}, {0x00, 0x87}, - {0x09, 0x88}, {0x00, 0x89}, - {0xe8, 0x8a}, {0x0f, 0x8b}, - {0xda, 0x8c}, {0x0f, 0x8d}, - {0x40, 0x8e}, {0x00, 0x8f}, - {0x37, 0x90}, {0x00, 0x91}, - {0xcf, 0x92}, {0x0f, 0x93}, - {0xfa, 0x94}, {0x0f, 0x95}, - {0x00, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x00, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x00, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x12, 0x48); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x80); - err += sn9c102_i2c_write(cam, 0x04, 0x10); - err += sn9c102_i2c_write(cam, 0x05, 0x20); - err += sn9c102_i2c_write(cam, 0x06, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x0c, 0x20); - err += sn9c102_i2c_write(cam, 0x0d, 0x20); - err += sn9c102_i2c_write(cam, 0x15, 0x80); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1b); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x05); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x21, 0x1b); - err += sn9c102_i2c_write(cam, 0x22, 0x00); - err += sn9c102_i2c_write(cam, 0x23, 0xde); - err += sn9c102_i2c_write(cam, 0x24, 0x10); - err += sn9c102_i2c_write(cam, 0x25, 0x8a); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0xca); - err += sn9c102_i2c_write(cam, 0x28, 0xa2); - err += sn9c102_i2c_write(cam, 0x29, 0x74); - err += sn9c102_i2c_write(cam, 0x2a, 0x88); - err += sn9c102_i2c_write(cam, 0x2b, 0x34); - err += sn9c102_i2c_write(cam, 0x2c, 0x88); - err += sn9c102_i2c_write(cam, 0x2e, 0x00); - err += sn9c102_i2c_write(cam, 0x2f, 0x00); - err += sn9c102_i2c_write(cam, 0x30, 0x00); - err += sn9c102_i2c_write(cam, 0x32, 0xc2); - err += sn9c102_i2c_write(cam, 0x33, 0x08); - err += sn9c102_i2c_write(cam, 0x4c, 0x40); - err += sn9c102_i2c_write(cam, 0x4d, 0xf3); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x40); - err += sn9c102_i2c_write(cam, 0x62, 0x12); - err += sn9c102_i2c_write(cam, 0x63, 0x57); - err += sn9c102_i2c_write(cam, 0x64, 0x73); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x66, 0x55); - err += sn9c102_i2c_write(cam, 0x67, 0x01); - err += sn9c102_i2c_write(cam, 0x68, 0xac); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x1f); - err += sn9c102_i2c_write(cam, 0x70, 0x01); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x72, 0x10); - err += sn9c102_i2c_write(cam, 0x73, 0x50); - err += sn9c102_i2c_write(cam, 0x74, 0x20); - err += sn9c102_i2c_write(cam, 0x76, 0x01); - err += sn9c102_i2c_write(cam, 0x77, 0xf3); - err += sn9c102_i2c_write(cam, 0x78, 0x90); - err += sn9c102_i2c_write(cam, 0x79, 0x98); - err += sn9c102_i2c_write(cam, 0x7a, 0x98); - err += sn9c102_i2c_write(cam, 0x7b, 0x00); - err += sn9c102_i2c_write(cam, 0x7c, 0x38); - err += sn9c102_i2c_write(cam, 0x7d, 0xff); - break; - default: - break; - } - - return err; -} - - -static int ov7630_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x05); - else - ctrl->value = sn9c102_pread_reg(cam, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_pread_reg(cam, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x07); - else - ctrl->value = sn9c102_pread_reg(cam, 0x05); - break; - break; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_DO_WHITE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_WHITENESS: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_AUTOGAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) - return -EIO; - ctrl->value &= 0x01; - break; - case V4L2_CID_VFLIP: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x80) ? 1 : 0; - break; - case SN9C102_V4L2_CID_GAMMA: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_WHITENESS: - err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, ctrl->value | - (ctrl->value << 1)); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); - break; - case SN9C102_V4L2_CID_GAMMA: - err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7630_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) - err += sn9c102_write_reg(cam, 0x50, 0x19); - else - err += sn9c102_write_reg(cam, 0x20, 0x19); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xe5, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x04); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x02); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor ov7630 = { - .name = "OV7630", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 | - BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7630_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x60, - .flags = 0, - }, - { - .id = V4L2_CID_WHITENESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: red", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: blue", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "rgb gamma", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7630_get_ctrl, - .set_ctrl = &ov7630_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7630_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SN9C10X, - .priv = 8, - }, - .set_pix_format = &ov7630_set_pix_format -}; - - -int sn9c102_probe_ov7630(struct sn9c102_device* cam) -{ - int pid, ver, err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01}, - {0x28, 0x17}, {0x44, 0x02}); - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - if (err || pid < 0) /* try a different initialization */ - err += sn9c102_write_const_regs(cam, {0x01, 0x01}, - {0x00, 0x01}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x74, 0x02}, - {0x0e, 0x01}, {0x44, 0x01}); - break; - default: - break; - } - - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x31) - return -ENODEV; - sn9c102_attach_sensor(cam, &ov7630); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c deleted file mode 100644 index 7977795d342b..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_ov7660.c +++ /dev/null @@ -1,538 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7660_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x08, 0x14}, {0x20, 0x17}, - {0x8b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xec, 0x8a}, {0x0f, 0x8b}, - {0xd8, 0x8c}, {0x0f, 0x8d}, - {0x3d, 0x8e}, {0x00, 0x8f}, - {0x3d, 0x90}, {0x00, 0x91}, - {0xcd, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0c, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9a}, {0x00, 0x9b}, - {0x04, 0x9c}, {0x00, 0x9d}, - {0x08, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3C, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x00, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x09); - err += sn9c102_i2c_write(cam, 0x00, 0x0A); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x00); - err += sn9c102_i2c_write(cam, 0x04, 0x00); - err += sn9c102_i2c_write(cam, 0x05, 0x08); - err += sn9c102_i2c_write(cam, 0x06, 0x0B); - err += sn9c102_i2c_write(cam, 0x07, 0x00); - err += sn9c102_i2c_write(cam, 0x08, 0x1C); - err += sn9c102_i2c_write(cam, 0x09, 0x01); - err += sn9c102_i2c_write(cam, 0x0A, 0x76); - err += sn9c102_i2c_write(cam, 0x0B, 0x60); - err += sn9c102_i2c_write(cam, 0x0C, 0x00); - err += sn9c102_i2c_write(cam, 0x0D, 0x08); - err += sn9c102_i2c_write(cam, 0x0E, 0x04); - err += sn9c102_i2c_write(cam, 0x0F, 0x6F); - err += sn9c102_i2c_write(cam, 0x10, 0x20); - err += sn9c102_i2c_write(cam, 0x11, 0x03); - err += sn9c102_i2c_write(cam, 0x12, 0x05); - err += sn9c102_i2c_write(cam, 0x13, 0xC7); - err += sn9c102_i2c_write(cam, 0x14, 0x2C); - err += sn9c102_i2c_write(cam, 0x15, 0x00); - err += sn9c102_i2c_write(cam, 0x16, 0x02); - err += sn9c102_i2c_write(cam, 0x17, 0x10); - err += sn9c102_i2c_write(cam, 0x18, 0x60); - err += sn9c102_i2c_write(cam, 0x19, 0x02); - err += sn9c102_i2c_write(cam, 0x1A, 0x7B); - err += sn9c102_i2c_write(cam, 0x1B, 0x02); - err += sn9c102_i2c_write(cam, 0x1C, 0x7F); - err += sn9c102_i2c_write(cam, 0x1D, 0xA2); - err += sn9c102_i2c_write(cam, 0x1E, 0x01); - err += sn9c102_i2c_write(cam, 0x1F, 0x0E); - err += sn9c102_i2c_write(cam, 0x20, 0x05); - err += sn9c102_i2c_write(cam, 0x21, 0x05); - err += sn9c102_i2c_write(cam, 0x22, 0x05); - err += sn9c102_i2c_write(cam, 0x23, 0x05); - err += sn9c102_i2c_write(cam, 0x24, 0x68); - err += sn9c102_i2c_write(cam, 0x25, 0x58); - err += sn9c102_i2c_write(cam, 0x26, 0xD4); - err += sn9c102_i2c_write(cam, 0x27, 0x80); - err += sn9c102_i2c_write(cam, 0x28, 0x80); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2A, 0x00); - err += sn9c102_i2c_write(cam, 0x2B, 0x00); - err += sn9c102_i2c_write(cam, 0x2C, 0x80); - err += sn9c102_i2c_write(cam, 0x2D, 0x00); - err += sn9c102_i2c_write(cam, 0x2E, 0x00); - err += sn9c102_i2c_write(cam, 0x2F, 0x0E); - err += sn9c102_i2c_write(cam, 0x30, 0x08); - err += sn9c102_i2c_write(cam, 0x31, 0x30); - err += sn9c102_i2c_write(cam, 0x32, 0xB4); - err += sn9c102_i2c_write(cam, 0x33, 0x00); - err += sn9c102_i2c_write(cam, 0x34, 0x07); - err += sn9c102_i2c_write(cam, 0x35, 0x84); - err += sn9c102_i2c_write(cam, 0x36, 0x00); - err += sn9c102_i2c_write(cam, 0x37, 0x0C); - err += sn9c102_i2c_write(cam, 0x38, 0x02); - err += sn9c102_i2c_write(cam, 0x39, 0x43); - err += sn9c102_i2c_write(cam, 0x3A, 0x00); - err += sn9c102_i2c_write(cam, 0x3B, 0x0A); - err += sn9c102_i2c_write(cam, 0x3C, 0x6C); - err += sn9c102_i2c_write(cam, 0x3D, 0x99); - err += sn9c102_i2c_write(cam, 0x3E, 0x0E); - err += sn9c102_i2c_write(cam, 0x3F, 0x41); - err += sn9c102_i2c_write(cam, 0x40, 0xC1); - err += sn9c102_i2c_write(cam, 0x41, 0x22); - err += sn9c102_i2c_write(cam, 0x42, 0x08); - err += sn9c102_i2c_write(cam, 0x43, 0xF0); - err += sn9c102_i2c_write(cam, 0x44, 0x10); - err += sn9c102_i2c_write(cam, 0x45, 0x78); - err += sn9c102_i2c_write(cam, 0x46, 0xA8); - err += sn9c102_i2c_write(cam, 0x47, 0x60); - err += sn9c102_i2c_write(cam, 0x48, 0x80); - err += sn9c102_i2c_write(cam, 0x49, 0x00); - err += sn9c102_i2c_write(cam, 0x4A, 0x00); - err += sn9c102_i2c_write(cam, 0x4B, 0x00); - err += sn9c102_i2c_write(cam, 0x4C, 0x00); - err += sn9c102_i2c_write(cam, 0x4D, 0x00); - err += sn9c102_i2c_write(cam, 0x4E, 0x00); - err += sn9c102_i2c_write(cam, 0x4F, 0x46); - err += sn9c102_i2c_write(cam, 0x50, 0x36); - err += sn9c102_i2c_write(cam, 0x51, 0x0F); - err += sn9c102_i2c_write(cam, 0x52, 0x17); - err += sn9c102_i2c_write(cam, 0x53, 0x7F); - err += sn9c102_i2c_write(cam, 0x54, 0x96); - err += sn9c102_i2c_write(cam, 0x55, 0x40); - err += sn9c102_i2c_write(cam, 0x56, 0x40); - err += sn9c102_i2c_write(cam, 0x57, 0x40); - err += sn9c102_i2c_write(cam, 0x58, 0x0F); - err += sn9c102_i2c_write(cam, 0x59, 0xBA); - err += sn9c102_i2c_write(cam, 0x5A, 0x9A); - err += sn9c102_i2c_write(cam, 0x5B, 0x22); - err += sn9c102_i2c_write(cam, 0x5C, 0xB9); - err += sn9c102_i2c_write(cam, 0x5D, 0x9B); - err += sn9c102_i2c_write(cam, 0x5E, 0x10); - err += sn9c102_i2c_write(cam, 0x5F, 0xF0); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x60); - err += sn9c102_i2c_write(cam, 0x62, 0x00); - err += sn9c102_i2c_write(cam, 0x63, 0x00); - err += sn9c102_i2c_write(cam, 0x64, 0x50); - err += sn9c102_i2c_write(cam, 0x65, 0x30); - err += sn9c102_i2c_write(cam, 0x66, 0x00); - err += sn9c102_i2c_write(cam, 0x67, 0x80); - err += sn9c102_i2c_write(cam, 0x68, 0x7A); - err += sn9c102_i2c_write(cam, 0x69, 0x90); - err += sn9c102_i2c_write(cam, 0x6A, 0x80); - err += sn9c102_i2c_write(cam, 0x6B, 0x0A); - err += sn9c102_i2c_write(cam, 0x6C, 0x30); - err += sn9c102_i2c_write(cam, 0x6D, 0x48); - err += sn9c102_i2c_write(cam, 0x6E, 0x80); - err += sn9c102_i2c_write(cam, 0x6F, 0x74); - err += sn9c102_i2c_write(cam, 0x70, 0x64); - err += sn9c102_i2c_write(cam, 0x71, 0x60); - err += sn9c102_i2c_write(cam, 0x72, 0x5C); - err += sn9c102_i2c_write(cam, 0x73, 0x58); - err += sn9c102_i2c_write(cam, 0x74, 0x54); - err += sn9c102_i2c_write(cam, 0x75, 0x4C); - err += sn9c102_i2c_write(cam, 0x76, 0x40); - err += sn9c102_i2c_write(cam, 0x77, 0x38); - err += sn9c102_i2c_write(cam, 0x78, 0x34); - err += sn9c102_i2c_write(cam, 0x79, 0x30); - err += sn9c102_i2c_write(cam, 0x7A, 0x2F); - err += sn9c102_i2c_write(cam, 0x7B, 0x2B); - err += sn9c102_i2c_write(cam, 0x7C, 0x03); - err += sn9c102_i2c_write(cam, 0x7D, 0x07); - err += sn9c102_i2c_write(cam, 0x7E, 0x17); - err += sn9c102_i2c_write(cam, 0x7F, 0x34); - err += sn9c102_i2c_write(cam, 0x80, 0x41); - err += sn9c102_i2c_write(cam, 0x81, 0x4D); - err += sn9c102_i2c_write(cam, 0x82, 0x58); - err += sn9c102_i2c_write(cam, 0x83, 0x63); - err += sn9c102_i2c_write(cam, 0x84, 0x6E); - err += sn9c102_i2c_write(cam, 0x85, 0x77); - err += sn9c102_i2c_write(cam, 0x86, 0x87); - err += sn9c102_i2c_write(cam, 0x87, 0x95); - err += sn9c102_i2c_write(cam, 0x88, 0xAF); - err += sn9c102_i2c_write(cam, 0x89, 0xC7); - err += sn9c102_i2c_write(cam, 0x8A, 0xDF); - err += sn9c102_i2c_write(cam, 0x8B, 0x99); - err += sn9c102_i2c_write(cam, 0x8C, 0x99); - err += sn9c102_i2c_write(cam, 0x8D, 0xCF); - err += sn9c102_i2c_write(cam, 0x8E, 0x20); - err += sn9c102_i2c_write(cam, 0x8F, 0x26); - err += sn9c102_i2c_write(cam, 0x90, 0x10); - err += sn9c102_i2c_write(cam, 0x91, 0x0C); - err += sn9c102_i2c_write(cam, 0x92, 0x25); - err += sn9c102_i2c_write(cam, 0x93, 0x00); - err += sn9c102_i2c_write(cam, 0x94, 0x50); - err += sn9c102_i2c_write(cam, 0x95, 0x50); - err += sn9c102_i2c_write(cam, 0x96, 0x00); - err += sn9c102_i2c_write(cam, 0x97, 0x01); - err += sn9c102_i2c_write(cam, 0x98, 0x10); - err += sn9c102_i2c_write(cam, 0x99, 0x40); - err += sn9c102_i2c_write(cam, 0x9A, 0x40); - err += sn9c102_i2c_write(cam, 0x9B, 0x20); - err += sn9c102_i2c_write(cam, 0x9C, 0x00); - err += sn9c102_i2c_write(cam, 0x9D, 0x99); - err += sn9c102_i2c_write(cam, 0x9E, 0x7F); - err += sn9c102_i2c_write(cam, 0x9F, 0x00); - err += sn9c102_i2c_write(cam, 0xA0, 0x00); - err += sn9c102_i2c_write(cam, 0xA1, 0x00); - - return err; -} - - -static int ov7660_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - break; - case V4L2_CID_DO_WHITE_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x02)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x04) ? 1 : 0; - break; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x05)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x06)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x07)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x3b)) < 0) - return -EIO; - ctrl->value &= 0x08; - break; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) - return -EIO; - ctrl->value &= 0x1f; - break; - case V4L2_CID_AUTOGAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) - return -EIO; - ctrl->value &= 0x01; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, ctrl->value << 3, 0x3b); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, 0x60 + ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, 0xc0 | - (ctrl->value * 0x07)); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7660_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int r0, err = 0; - - r0 = sn9c102_pread_reg(cam, 0x01); - - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - } else { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x0d); - } - - return err; -} - - -static const struct sn9c102_sensor ov7660 = { - .name = "OV7660", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7660_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x09, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x27, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "night mode", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7660_get_ctrl, - .set_ctrl = &ov7660_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7660_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - }, - .set_pix_format = &ov7660_set_pix_format -}; - - -int sn9c102_probe_ov7660(struct sn9c102_device* cam) -{ - int pid, ver, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x60) - return -ENODEV; - - sn9c102_attach_sensor(cam, &ov7660); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c deleted file mode 100644 index 81cd969c1b7b..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_pas106b.c +++ /dev/null @@ -1,302 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas106b_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x20, 0x19}, {0x09, 0x18}); - - err += sn9c102_i2c_write(cam, 0x02, 0x0c); - err += sn9c102_i2c_write(cam, 0x05, 0x5a); - err += sn9c102_i2c_write(cam, 0x06, 0x88); - err += sn9c102_i2c_write(cam, 0x07, 0x80); - err += sn9c102_i2c_write(cam, 0x10, 0x06); - err += sn9c102_i2c_write(cam, 0x11, 0x06); - err += sn9c102_i2c_write(cam, 0x12, 0x00); - err += sn9c102_i2c_write(cam, 0x14, 0x02); - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - msleep(400); - - return err; -} - - -static int pas106b_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x03), - r2 = sn9c102_i2c_read(cam, 0x04); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 4) | (r2 & 0x0f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_CONTRAST: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x1f) << 1; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) - return -EIO; - ctrl->value &= 0xf8; - return 0; - default: - return -EINVAL; - } -} - - -static int pas106b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); - err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); - break; - case V4L2_CID_CONTRAST: - err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); - err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - return err ? -EIO : 0; -} - - -static int pas106b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int pas106b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2c, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static const struct sn9c102_sensor pas106b = { - .name = "PAS106B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas106b_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x125, - .maximum = 0xfff, - .step = 0x001, - .default_value = 0x140, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0d, - .flags = 0, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "contrast", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x00, /* 0x00~0x03 have same effect */ - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3e, - .step = 0x02, - .default_value = 0x02, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - }, - .get_ctrl = &pas106b_get_ctrl, - .set_ctrl = &pas106b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &pas106b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, /* we use this field as 'bits per pixel' */ - }, - .set_pix_format = &pas106b_set_pix_format -}; - - -int sn9c102_probe_pas106b(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0; - unsigned int pid = 0; - - /* - Minimal initialization to enable the I2C communication - NOTE: do NOT change the values! - */ - if (sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* sensor power down */ - {0x00, 0x01}, /* sensor power on */ - {0x28, 0x17})) /* sensor clock at 24 MHz */ - return -EIO; - - r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); - if (r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 11) | ((r1 & 0xf0) >> 4); - if (pid != 0x007) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas106b); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c deleted file mode 100644 index 2e86fdc86989..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c +++ /dev/null @@ -1,335 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * - * * - * * - * Support for SN9C103, DAC Magnitude, exposure and green gain controls * - * added by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas202bcb_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x10, 0x21}, - {0x20, 0x22}, {0x30, 0x23}, - {0x40, 0x24}, {0x50, 0x25}, - {0x60, 0x26}, {0x70, 0x27}, - {0x80, 0x28}, {0x90, 0x29}, - {0xa0, 0x2a}, {0xb0, 0x2b}, - {0xc0, 0x2c}, {0xd0, 0x2d}, - {0xe0, 0x2e}, {0xf0, 0x2f}, - {0xff, 0x30}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x02, 0x14); - err += sn9c102_i2c_write(cam, 0x03, 0x40); - err += sn9c102_i2c_write(cam, 0x0d, 0x2c); - err += sn9c102_i2c_write(cam, 0x0e, 0x01); - err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x10, 0x08); - err += sn9c102_i2c_write(cam, 0x13, 0x63); - err += sn9c102_i2c_write(cam, 0x15, 0x70); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - msleep(400); - - return err; -} - - -static int pas202bcb_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x04), - r2 = sn9c102_i2c_read(cam, 0x05); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 6) | (r2 & 0x3f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - return 0; - default: - return -EINVAL; - } -} - - -static int pas202bcb_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x28, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static int pas202bcb_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); - err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static int pas202bcb_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static const struct sn9c102_sensor pas202bcb = { - .name = "PAS202BCB", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas202bcb_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0b, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - }, - .get_ctrl = &pas202bcb_get_ctrl, - .set_ctrl = &pas202bcb_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &pas202bcb_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &pas202bcb_set_pix_format -}; - - -int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0, err = 0; - unsigned int pid = 0; - - /* - * Minimal initialization to enable the I2C communication - * NOTE: do NOT change the values! - */ - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* power down */ - {0x40, 0x01}, /* power on */ - {0x28, 0x17});/* clock 24 MHz */ - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x01}, - {0x44, 0x02}, {0x29, 0x17}); - break; - default: - break; - } - - r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); - - if (err || r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 4) | ((r1 & 0xf0) >> 4); - if (pid != 0x017) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas202bcb); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h deleted file mode 100644 index 3679970dba2c..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_sensor.h +++ /dev/null @@ -1,307 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * This program is free software; 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 _SN9C102_SENSOR_H_ -#define _SN9C102_SENSOR_H_ - -#include -#include -#include -#include -#include -#include - -struct sn9c102_device; -struct sn9c102_sensor; - -/*****************************************************************************/ - -/* - OVERVIEW. - This is a small interface that allows you to add support for any CCD/CMOS - image sensors connected to the SN9C1XX bridges. The entire API is documented - below. In the most general case, to support a sensor there are three steps - you have to follow: - 1) define the main "sn9c102_sensor" structure by setting the basic fields; - 2) write a probing function to be called by the core module when the USB - camera is recognized, then add both the USB ids and the name of that - function to the two corresponding tables in sn9c102_devtable.h; - 3) implement the methods that you want/need (and fill the rest of the main - structure accordingly). - "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do - NOT need to touch the source code of the core module for the things to work - properly, unless you find bugs or flaws in it. Finally, do not forget to - read the V4L2 API for completeness. -*/ - -/*****************************************************************************/ - -enum sn9c102_bridge { - BRIDGE_SN9C101 = 0x01, - BRIDGE_SN9C102 = 0x02, - BRIDGE_SN9C103 = 0x04, - BRIDGE_SN9C105 = 0x08, - BRIDGE_SN9C120 = 0x10, -}; - -/* Return the bridge name */ -enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam); - -/* Return a pointer the sensor struct attached to the camera */ -struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam); - -/* Identify a device */ -extern struct sn9c102_device* -sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); - -/* Attach a probed sensor to the camera. */ -extern void -sn9c102_attach_sensor(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor); - -/* - Read/write routines: they always return -1 on error, 0 or the read value - otherwise. NOTE that a real read operation is not supported by the SN9C1XX - chip for some of its registers. To work around this problem, a pseudo-read - call is provided instead: it returns the last successfully written value - on the register (0 if it has never been written), the usual -1 on error. -*/ - -/* The "try" I2C I/O versions are used when probing the sensor */ -extern int sn9c102_i2c_try_read(struct sn9c102_device*, - const struct sn9c102_sensor*, u8 address); - -/* - These must be used if and only if the sensor doesn't implement the standard - I2C protocol. There are a number of good reasons why you must use the - single-byte versions of these functions: do not abuse. The first function - writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX - chip. The second one programs the registers 0x09 and 0x10 with data0 and - data1, and places the n bytes read from the sensor register table in the - buffer pointed by 'buffer'. Both the functions return -1 on error; the write - version returns 0 on success, while the read version returns the first read - byte. -*/ -extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 n, - u8 data0, u8 data1, u8 data2, u8 data3, - u8 data4, u8 data5); -extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, - u8 data0, u8 data1, u8 n, u8 buffer[]); - -/* To be used after the sensor struct has been attached to the camera struct */ -extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); -extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); - -/* I/O on registers in the bridge. Could be used by the sensor methods too */ -extern int sn9c102_read_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); -extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], - int count); -/* - Write multiple registers with constant values. For example: - sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); - Register addresses must be < 256. -*/ -#define sn9c102_write_const_regs(sn9c102_device, data...) \ - ({ static const u8 _valreg[][2] = {data}; \ - sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) - -/*****************************************************************************/ - -enum sn9c102_i2c_sysfs_ops { - SN9C102_I2C_READ = 0x01, - SN9C102_I2C_WRITE = 0x02, -}; - -enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ - SN9C102_I2C_100KHZ = 0x01, - SN9C102_I2C_400KHZ = 0x02, -}; - -enum sn9c102_i2c_interface { - SN9C102_I2C_2WIRES, - SN9C102_I2C_3WIRES, -}; - -#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) - -struct sn9c102_sensor { - char name[32], /* sensor name */ - maintainer[64]; /* name of the maintainer */ - - enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ - - /* Supported operations through the 'sysfs' interface */ - enum sn9c102_i2c_sysfs_ops sysfs_ops; - - /* - These sensor capabilities must be provided if the SN9C1XX controller - needs to communicate through the sensor serial interface by using - at least one of the i2c functions available. - */ - enum sn9c102_i2c_frequency frequency; - enum sn9c102_i2c_interface interface; - - /* - This identifier must be provided if the image sensor implements - the standard I2C protocol. - */ - u8 i2c_slave_id; /* reg. 0x09 */ - - /* - NOTE: Where not noted,most of the functions below are not mandatory. - Set to null if you do not implement them. If implemented, - they must return 0 on success, the proper error otherwise. - */ - - int (*init)(struct sn9c102_device* cam); - /* - This function will be called after the sensor has been attached. - It should be used to initialize the sensor only, but may also - configure part of the SN9C1XX chip if necessary. You don't need to - setup picture settings like brightness, contrast, etc.. here, if - the corresponding controls are implemented (see below), since - they are adjusted in the core driver by calling the set_ctrl() - method after init(), where the arguments are the default values - specified in the v4l2_queryctrl list of supported controls; - Same suggestions apply for other settings, _if_ the corresponding - methods are present; if not, the initialization must configure the - sensor according to the default configuration structures below. - */ - - struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; - /* - Optional list of default controls, defined as indicated in the - V4L2 API. Menu type controls are not handled by this interface. - */ - - int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); - int (*set_ctrl)(struct sn9c102_device* cam, - const struct v4l2_control* ctrl); - /* - You must implement at least the set_ctrl method if you have defined - the list above. The returned value must follow the V4L2 - specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER - are not supported by this driver, so do not implement them. Also, - you don't have to check whether the passed values are out of bounds, - given that this is done by the core module. - */ - - struct v4l2_cropcap cropcap; - /* - Think the image sensor as a grid of R,G,B monochromatic pixels - disposed according to a particular Bayer pattern, which describes - the complete array of pixels, from (0,0) to (xmax, ymax). We will - use this coordinate system from now on. It is assumed the sensor - chip can be programmed to capture/transmit a subsection of that - array of pixels: we will call this subsection "active window". - It is not always true that the largest achievable active window can - cover the whole array of pixels. The V4L2 API defines another - area called "source rectangle", which, in turn, is a subrectangle of - the active window. The SN9C1XX chip is always programmed to read the - source rectangle. - The bounds of both the active window and the source rectangle are - specified in the cropcap substructures 'bounds' and 'defrect'. - By default, the source rectangle should cover the largest possible - area. Again, it is not always true that the largest source rectangle - can cover the entire active window, although it is a rare case for - the hardware we have. The bounds of the source rectangle _must_ be - multiple of 16 and must use the same coordinate system as indicated - before; their centers shall align initially. - If necessary, the sensor chip must be initialized during init() to - set the bounds of the active sensor window; however, by default, it - usually covers the largest achievable area (maxwidth x maxheight) - of pixels, so no particular initialization is needed, if you have - defined the correct default bounds in the structures. - See the V4L2 API for further details. - NOTE: once you have defined the bounds of the active window - (struct cropcap.bounds) you must not change them.anymore. - Only 'bounds' and 'defrect' fields are mandatory, other fields - will be ignored. - */ - - int (*set_crop)(struct sn9c102_device* cam, - const struct v4l2_rect* rect); - /* - To be called on VIDIOC_C_SETCROP. The core module always calls a - default routine which configures the appropriate SN9C1XX regs (also - scaling), but you may need to override/adjust specific stuff. - 'rect' contains width and height values that are multiple of 16: in - case you override the default function, you always have to program - the chip to match those values; on error return the corresponding - error code without rolling back. - NOTE: in case, you must program the SN9C1XX chip to get rid of - blank pixels or blank lines at the _start_ of each line or - frame after each HSYNC or VSYNC, so that the image starts with - real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, - V_SIZE you don't have to care about blank pixels or blank - lines at the end of each line or frame). - */ - - struct v4l2_pix_format pix_format; - /* - What you have to define here are: 1) initial 'width' and 'height' of - the target rectangle 2) the initial 'pixelformat', which can be - either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) - or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate - the number of bits per pixel for uncompressed video, 8 or 9 (despite - the current value of 'pixelformat'). - NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 - of cropcap.defrect.width and cropcap.defrect.height. I - suggest 1/1. - NOTE 2: The initial compression quality is defined by the first bit - of reg 0x17 during the initialization of the image sensor. - NOTE 3: as said above, you have to program the SN9C1XX chip to get - rid of any blank pixels, so that the output of the sensor - matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). - */ - - int (*set_pix_format)(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix); - /* - To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to - SN9C10X pixel format or viceversa. On error return the corresponding - error code without rolling back. - */ - - /* - Do NOT write to the data below, it's READ ONLY. It is used by the - core module to store successfully updated values of the above - settings, for rollbacks..etc..in case of errors during atomic I/O - */ - struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -/*****************************************************************************/ - -/* Private ioctl's for control settings supported by some image sensors */ -#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) -#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) -#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) -#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) -#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) -#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) -#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) - -#endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c deleted file mode 100644 index 04cdfdde8564..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c +++ /dev/null @@ -1,154 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110c1b_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, - {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0xc0, 0x80); - - return err; -} - - -static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5110c1b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Don't change ! */ - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110c1b = { - .name = "TAS5110C1B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5110c1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - }, - .set_ctrl = &tas5110c1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110c1b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110c1b_set_pix_format -}; - - -int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5110c1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6001), }, - { USB_DEVICE(0x0c45, 0x6005), }, - { USB_DEVICE(0x0c45, 0x60ab), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5110c1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110c1b); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c deleted file mode 100644 index 9372e6f9fcff..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_tas5110d.c +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110d_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0x9a, 0xca); - - return err; -} - - -static int tas5110d_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - - return err; -} - - -static int tas5110d_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x3b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110d = { - .name = "TAS5110D", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x61, - .init = &tas5110d_init, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110d_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110d_set_pix_format -}; - - -int sn9c102_probe_tas5110d(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5110d_id_table[] = { - { USB_DEVICE(0x0c45, 0x6007), }, - { } - }; - - if (!sn9c102_match_id(cam, tas5110d_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110d); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c deleted file mode 100644 index a30bbc4389f5..000000000000 --- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c +++ /dev/null @@ -1,165 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * This program is free software; 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 "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5130d1b_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, - {0x04, 0x01}, {0x01, 0x10}, - {0x00, 0x11}, {0x00, 0x14}, - {0x60, 0x17}, {0x07, 0x18}); - - return err; -} - - -static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5130d1b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Do NOT change! */ - err += sn9c102_write_reg(cam, 0x1f, 0x1a); - err += sn9c102_write_reg(cam, 0x1a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x63, 0x19); - else - err += sn9c102_write_reg(cam, 0xf3, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5130d1b = { - .name = "TAS5130D1B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5130d1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x02, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x47, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .set_ctrl = &tas5130d1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &tas5130d1b_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5130d1b_set_pix_format -}; - - -int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5130d1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6024), }, - { USB_DEVICE(0x0c45, 0x6025), }, - { USB_DEVICE(0x0c45, 0x60aa), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5130d1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5130d1b); - - return 0; -} diff --git a/drivers/media/video/stk1160/Kconfig b/drivers/media/video/stk1160/Kconfig deleted file mode 100644 index 1c3a1ec00237..000000000000 --- a/drivers/media/video/stk1160/Kconfig +++ /dev/null @@ -1,20 +0,0 @@ -config VIDEO_STK1160 - tristate "STK1160 USB video capture support" - depends on VIDEO_DEV && I2C - select VIDEOBUF2_VMALLOC - select VIDEO_SAA711X - - ---help--- - This is a video4linux driver for STK1160 based video capture devices. - - To compile this driver as a module, choose M here: the - module will be called stk1160 - -config VIDEO_STK1160_AC97 - bool "STK1160 AC97 codec support" - depends on VIDEO_STK1160 && SND - select SND_AC97_CODEC - - ---help--- - Enables AC97 codec support for stk1160 driver. -. diff --git a/drivers/media/video/stk1160/Makefile b/drivers/media/video/stk1160/Makefile deleted file mode 100644 index 8a3c78482e73..000000000000 --- a/drivers/media/video/stk1160/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -obj-stk1160-ac97-$(CONFIG_VIDEO_STK1160_AC97) := stk1160-ac97.o - -stk1160-y := stk1160-core.o \ - stk1160-v4l.o \ - stk1160-video.o \ - stk1160-i2c.o \ - $(obj-stk1160-ac97-y) - -obj-$(CONFIG_VIDEO_STK1160) += stk1160.o - -ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/stk1160/stk1160-ac97.c b/drivers/media/video/stk1160/stk1160-ac97.c deleted file mode 100644 index 8d325f50c87b..000000000000 --- a/drivers/media/video/stk1160/stk1160-ac97.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * STK1160 driver - * - * Copyright (C) 2012 Ezequiel Garcia - * - * - * Based on Easycap driver by R.M. Thomas - * Copyright (C) 2010 R.M. Thomas - * - * - * This program is free software; 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 "stk1160.h" -#include "stk1160-reg.h" - -static struct snd_ac97 *stk1160_ac97; - -static void stk1160_write_ac97(struct snd_ac97 *ac97, u16 reg, u16 value) -{ - struct stk1160 *dev = ac97->private_data; - - /* Set codec register address */ - stk1160_write_reg(dev, STK1160_AC97_ADDR, reg); - - /* Set codec command */ - stk1160_write_reg(dev, STK1160_AC97_CMD, value & 0xff); - stk1160_write_reg(dev, STK1160_AC97_CMD + 1, (value & 0xff00) >> 8); - - /* - * Set command write bit to initiate write operation. - * The bit will be cleared when transfer is done. - */ - stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8c); -} - -static u16 stk1160_read_ac97(struct snd_ac97 *ac97, u16 reg) -{ - struct stk1160 *dev = ac97->private_data; - u8 vall = 0; - u8 valh = 0; - - /* Set codec register address */ - stk1160_write_reg(dev, STK1160_AC97_ADDR, reg); - - /* - * Set command read bit to initiate read operation. - * The bit will be cleared when transfer is done. - */ - stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8b); - - /* Retrieve register value */ - stk1160_read_reg(dev, STK1160_AC97_CMD, &vall); - stk1160_read_reg(dev, STK1160_AC97_CMD + 1, &valh); - - return (valh << 8) | vall; -} - -static void stk1160_reset_ac97(struct snd_ac97 *ac97) -{ - struct stk1160 *dev = ac97->private_data; - /* Two-step reset AC97 interface and hardware codec */ - stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x94); - stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x88); - - /* Set 16-bit audio data and choose L&R channel*/ - stk1160_write_reg(dev, STK1160_AC97CTL_1 + 2, 0x01); -} - -static struct snd_ac97_bus_ops stk1160_ac97_ops = { - .read = stk1160_read_ac97, - .write = stk1160_write_ac97, - .reset = stk1160_reset_ac97, -}; - -int stk1160_ac97_register(struct stk1160 *dev) -{ - struct snd_card *card = NULL; - struct snd_ac97_bus *ac97_bus; - struct snd_ac97_template ac97_template; - int rc; - - /* - * Just want a card to access ac96 controls, - * the actual capture interface will be handled by snd-usb-audio - */ - rc = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); - if (rc < 0) - return rc; - - snd_card_set_dev(card, dev->dev); - - /* TODO: I'm not sure where should I get these names :-( */ - snprintf(card->shortname, sizeof(card->shortname), - "stk1160-mixer"); - snprintf(card->longname, sizeof(card->longname), - "stk1160 ac97 codec mixer control"); - strncpy(card->driver, dev->dev->driver->name, sizeof(card->driver)); - - rc = snd_ac97_bus(card, 0, &stk1160_ac97_ops, NULL, &ac97_bus); - if (rc) - goto err; - - /* We must set private_data before calling snd_ac97_mixer */ - memset(&ac97_template, 0, sizeof(ac97_template)); - ac97_template.private_data = dev; - ac97_template.scaps = AC97_SCAP_SKIP_MODEM; - rc = snd_ac97_mixer(ac97_bus, &ac97_template, &stk1160_ac97); - if (rc) - goto err; - - dev->snd_card = card; - rc = snd_card_register(card); - if (rc) - goto err; - - return 0; - -err: - dev->snd_card = NULL; - if (card) - snd_card_free(card); - return rc; -} - -int stk1160_ac97_unregister(struct stk1160 *dev) -{ - struct snd_card *card = dev->snd_card; - - /* - * We need to check usb_device, - * because ac97 release attempts to communicate with codec - */ - if (card && dev->udev) - snd_card_free(card); - - return 0; -} diff --git a/drivers/media/video/stk1160/stk1160-core.c b/drivers/media/video/stk1160/stk1160-core.c deleted file mode 100644 index 74236fd3b7ec..000000000000 --- a/drivers/media/video/stk1160/stk1160-core.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * STK1160 driver - * - * Copyright (C) 2012 Ezequiel Garcia - * - * - * Based on Easycap driver by R.M. Thomas - * Copyright (C) 2010 R.M. Thomas - * - * - * This program is free software; 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. - * - * TODO: - * - * 1. (Try to) detect if we must register ac97 mixer - * 2. Support stream at lower speed: lower frame rate or lower frame size. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "stk1160.h" -#include "stk1160-reg.h" - -static unsigned int input; -module_param(input, int, 0644); -MODULE_PARM_DESC(input, "Set default input"); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ezequiel Garcia"); -MODULE_DESCRIPTION("STK1160 driver"); - -/* Devices supported by this driver */ -static struct usb_device_id stk1160_id_table[] = { - { USB_DEVICE(0x05e1, 0x0408) }, - { } -}; -MODULE_DEVICE_TABLE(usb, stk1160_id_table); - -/* saa7113 I2C address */ -static unsigned short saa7113_addrs[] = { - 0x4a >> 1, - I2C_CLIENT_END -}; - -/* - * Read/Write stk registers - */ -int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value) -{ - int ret; - int pipe = usb_rcvctrlpipe(dev->udev, 0); - - *value = 0; - ret = usb_control_msg(dev->udev, pipe, 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, reg, value, sizeof(u8), HZ); - if (ret < 0) { - stk1160_err("read failed on reg 0x%x (%d)\n", - reg, ret); - return ret; - } - - return 0; -} - -int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value) -{ - int ret; - int pipe = usb_sndctrlpipe(dev->udev, 0); - - ret = usb_control_msg(dev->udev, pipe, 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, reg, NULL, 0, HZ); - if (ret < 0) { - stk1160_err("write failed on reg 0x%x (%d)\n", - reg, ret); - return ret; - } - - return 0; -} - -void stk1160_select_input(struct stk1160 *dev) -{ - static const u8 gctrl[] = { - 0x98, 0x90, 0x88, 0x80 - }; - - if (dev->ctl_input < ARRAY_SIZE(gctrl)) - stk1160_write_reg(dev, STK1160_GCTRL, gctrl[dev->ctl_input]); -} - -/* TODO: We should break this into pieces */ -static void stk1160_reg_reset(struct stk1160 *dev) -{ - int i; - - static const struct regval ctl[] = { - {STK1160_GCTRL+2, 0x0078}, - - {STK1160_RMCTL+1, 0x0000}, - {STK1160_RMCTL+3, 0x0002}, - - {STK1160_PLLSO, 0x0010}, - {STK1160_PLLSO+1, 0x0000}, - {STK1160_PLLSO+2, 0x0014}, - {STK1160_PLLSO+3, 0x000E}, - - {STK1160_PLLFD, 0x0046}, - - /* Timing generator setup */ - {STK1160_TIGEN, 0x0012}, - {STK1160_TICTL, 0x002D}, - {STK1160_TICTL+1, 0x0001}, - {STK1160_TICTL+2, 0x0000}, - {STK1160_TICTL+3, 0x0000}, - {STK1160_TIGEN, 0x0080}, - - {0xffff, 0xffff} - }; - - for (i = 0; ctl[i].reg != 0xffff; i++) - stk1160_write_reg(dev, ctl[i].reg, ctl[i].val); -} - -static void stk1160_release(struct v4l2_device *v4l2_dev) -{ - struct stk1160 *dev = container_of(v4l2_dev, struct stk1160, v4l2_dev); - - stk1160_info("releasing all resources\n"); - - stk1160_i2c_unregister(dev); - - v4l2_ctrl_handler_free(&dev->ctrl_handler); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev->alt_max_pkt_size); - kfree(dev); -} - -/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) - -/* - * Scan usb interface and populate max_pkt_size array - * with information on each alternate setting. - * The array should be allocated by the caller. - */ -static int stk1160_scan_usb(struct usb_interface *intf, struct usb_device *udev, - unsigned int *max_pkt_size) -{ - int i, e, sizedescr, size, ifnum; - const struct usb_endpoint_descriptor *desc; - - bool has_video = false, has_audio = false; - const char *speed; - - ifnum = intf->altsetting[0].desc.bInterfaceNumber; - - /* Get endpoints */ - for (i = 0; i < intf->num_altsetting; i++) { - - for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) { - - /* This isn't clear enough, at least to me */ - desc = &intf->altsetting[i].endpoint[e].desc; - sizedescr = le16_to_cpu(desc->wMaxPacketSize); - size = sizedescr & 0x7ff; - - if (udev->speed == USB_SPEED_HIGH) - size = size * hb_mult(sizedescr); - - if (usb_endpoint_xfer_isoc(desc) && - usb_endpoint_dir_in(desc)) { - switch (desc->bEndpointAddress) { - case STK1160_EP_AUDIO: - has_audio = true; - break; - case STK1160_EP_VIDEO: - has_video = true; - max_pkt_size[i] = size; - break; - } - } - } - } - - /* Is this even possible? */ - if (!(has_audio || has_video)) { - dev_err(&udev->dev, "no audio or video endpoints found\n"); - return -ENODEV; - } - - switch (udev->speed) { - case USB_SPEED_LOW: - speed = "1.5"; - break; - case USB_SPEED_FULL: - speed = "12"; - break; - case USB_SPEED_HIGH: - speed = "480"; - break; - default: - speed = "unknown"; - } - - dev_info(&udev->dev, "New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n", - udev->manufacturer ? udev->manufacturer : "", - udev->product ? udev->product : "", - speed, - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - ifnum, - intf->altsetting->desc.bInterfaceNumber); - - /* This should never happen, since we rejected audio interfaces */ - if (has_audio) - dev_warn(&udev->dev, "audio interface %d found.\n\ - This is not implemented by this driver,\ - you should use snd-usb-audio instead\n", ifnum); - - if (has_video) - dev_info(&udev->dev, "video interface %d found\n", - ifnum); - - /* - * Make sure we have 480 Mbps of bandwidth, otherwise things like - * video stream wouldn't likely work, since 12 Mbps is generally - * not enough even for most streams. - */ - if (udev->speed != USB_SPEED_HIGH) - dev_warn(&udev->dev, "must be connected to a high-speed USB 2.0 port\n\ - You may not be able to stream video smoothly\n"); - - return 0; -} - -static int stk1160_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int ifnum; - int rc = 0; - - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - struct usb_device *udev; - struct stk1160 *dev; - - ifnum = interface->altsetting[0].desc.bInterfaceNumber; - udev = interface_to_usbdev(interface); - - /* - * Since usb audio class is supported by snd-usb-audio, - * we reject audio interface. - */ - if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) - return -ENODEV; - - /* Alloc an array for all possible max_pkt_size */ - alt_max_pkt_size = kmalloc(sizeof(alt_max_pkt_size[0]) * - interface->num_altsetting, GFP_KERNEL); - if (alt_max_pkt_size == NULL) - return -ENOMEM; - - /* - * Scan usb posibilities and populate alt_max_pkt_size array. - * Also, check if device speed is fast enough. - */ - rc = stk1160_scan_usb(interface, udev, alt_max_pkt_size); - if (rc < 0) { - kfree(alt_max_pkt_size); - return rc; - } - - dev = kzalloc(sizeof(struct stk1160), GFP_KERNEL); - if (dev == NULL) { - kfree(alt_max_pkt_size); - return -ENOMEM; - } - - dev->alt_max_pkt_size = alt_max_pkt_size; - dev->udev = udev; - dev->num_alt = interface->num_altsetting; - dev->ctl_input = input; - - /* We save struct device for debug purposes only */ - dev->dev = &interface->dev; - - usb_set_intfdata(interface, dev); - - /* initialize videobuf2 stuff */ - rc = stk1160_vb2_setup(dev); - if (rc < 0) - goto free_err; - - /* - * There is no need to take any locks here in probe - * because we register the device node as the *last* thing. - */ - spin_lock_init(&dev->buf_lock); - mutex_init(&dev->v4l_lock); - mutex_init(&dev->vb_queue_lock); - - rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0); - if (rc) { - stk1160_err("v4l2_ctrl_handler_init failed (%d)\n", rc); - goto free_err; - } - - /* - * We obtain a v4l2_dev but defer - * registration of video device node as the last thing. - * There is no need to set the name if we give a device struct - */ - dev->v4l2_dev.release = stk1160_release; - dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; - rc = v4l2_device_register(dev->dev, &dev->v4l2_dev); - if (rc) { - stk1160_err("v4l2_device_register failed (%d)\n", rc); - goto free_ctrl; - } - - rc = stk1160_i2c_register(dev); - if (rc < 0) - goto unreg_v4l2; - - /* - * To the best of my knowledge stk1160 boards only have - * saa7113, but it doesn't hurt to support them all. - */ - dev->sd_saa7115 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "saa7115_auto", 0, saa7113_addrs); - - stk1160_info("driver ver %s successfully loaded\n", - STK1160_VERSION); - - /* i2c reset saa711x */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, - 0, 0, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); - - /* reset stk1160 to default values */ - stk1160_reg_reset(dev); - - /* select default input */ - stk1160_select_input(dev); - - stk1160_ac97_register(dev); - - rc = stk1160_video_register(dev); - if (rc < 0) - goto unreg_i2c; - - return 0; - -unreg_i2c: - stk1160_i2c_unregister(dev); -unreg_v4l2: - v4l2_device_unregister(&dev->v4l2_dev); -free_ctrl: - v4l2_ctrl_handler_free(&dev->ctrl_handler); -free_err: - kfree(alt_max_pkt_size); - kfree(dev); - - return rc; -} - -static void stk1160_disconnect(struct usb_interface *interface) -{ - struct stk1160 *dev; - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - /* - * Wait until all current v4l2 operation are finished - * then deallocate resources - */ - mutex_lock(&dev->vb_queue_lock); - mutex_lock(&dev->v4l_lock); - - /* Here is the only place where isoc get released */ - stk1160_uninit_isoc(dev); - - /* ac97 unregister needs to be done before usb_device is cleared */ - stk1160_ac97_unregister(dev); - - stk1160_clear_queue(dev); - - video_unregister_device(&dev->vdev); - v4l2_device_disconnect(&dev->v4l2_dev); - - /* This way current users can detect device is gone */ - dev->udev = NULL; - - mutex_unlock(&dev->v4l_lock); - mutex_unlock(&dev->vb_queue_lock); - - /* - * This calls stk1160_release if it's the last reference. - * therwise, release is posponed until there are no users left. - */ - v4l2_device_put(&dev->v4l2_dev); -} - -static struct usb_driver stk1160_usb_driver = { - .name = "stk1160", - .id_table = stk1160_id_table, - .probe = stk1160_probe, - .disconnect = stk1160_disconnect, -}; - -module_usb_driver(stk1160_usb_driver); diff --git a/drivers/media/video/stk1160/stk1160-i2c.c b/drivers/media/video/stk1160/stk1160-i2c.c deleted file mode 100644 index 176ac937306b..000000000000 --- a/drivers/media/video/stk1160/stk1160-i2c.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * STK1160 driver - * - * Copyright (C) 2012 Ezequiel Garcia - * - * - * Based on Easycap driver by R.M. Thomas - * Copyright (C) 2010 R.M. Thomas - * - * - * This program is free software; 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 "stk1160.h" -#include "stk1160-reg.h" - -static unsigned int i2c_debug; -module_param(i2c_debug, int, 0644); -MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define dprintk_i2c(fmt, args...) \ -do { \ - if (i2c_debug) \ - printk(KERN_DEBUG fmt, ##args); \ -} while (0) - -static int stk1160_i2c_busy_wait(struct stk1160 *dev, u8 wait_bit_mask) -{ - unsigned long end; - u8 flag; - - /* Wait until read/write finish bit is set */ - end = jiffies + msecs_to_jiffies(STK1160_I2C_TIMEOUT); - while (time_is_after_jiffies(end)) { - - stk1160_read_reg(dev, STK1160_SICTL+1, &flag); - /* read/write done? */ - if (flag & wait_bit_mask) - goto done; - - usleep_range(10 * USEC_PER_MSEC, 20 * USEC_PER_MSEC); - } - - return -ETIMEDOUT; - -done: - return 0; -} - -static int stk1160_i2c_write_reg(struct stk1160 *dev, u8 addr, - u8 reg, u8 value) -{ - int rc; - - /* Set serial device address */ - rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr); - if (rc < 0) - return rc; - - /* Set i2c device register sub-address */ - rc = stk1160_write_reg(dev, STK1160_SBUSW_WA, reg); - if (rc < 0) - return rc; - - /* Set i2c device register value */ - rc = stk1160_write_reg(dev, STK1160_SBUSW_WD, value); - if (rc < 0) - return rc; - - /* Start write now */ - rc = stk1160_write_reg(dev, STK1160_SICTL, 0x01); - if (rc < 0) - return rc; - - rc = stk1160_i2c_busy_wait(dev, 0x04); - if (rc < 0) - return rc; - - return 0; -} - -static int stk1160_i2c_read_reg(struct stk1160 *dev, u8 addr, - u8 reg, u8 *value) -{ - int rc; - - /* Set serial device address */ - rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr); - if (rc < 0) - return rc; - - /* Set i2c device register sub-address */ - rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, reg); - if (rc < 0) - return rc; - - /* Start read now */ - rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20); - if (rc < 0) - return rc; - - rc = stk1160_i2c_busy_wait(dev, 0x01); - if (rc < 0) - return rc; - - stk1160_read_reg(dev, STK1160_SBUSR_RD, value); - if (rc < 0) - return rc; - - return 0; -} - -/* - * stk1160_i2c_check_for_device() - * check if there is a i2c_device at the supplied address - */ -static int stk1160_i2c_check_for_device(struct stk1160 *dev, - unsigned char addr) -{ - int rc; - - /* Set serial device address */ - rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr); - if (rc < 0) - return rc; - - /* Set device sub-address, we'll chip version reg */ - rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, 0x00); - if (rc < 0) - return rc; - - /* Start read now */ - rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20); - if (rc < 0) - return rc; - - rc = stk1160_i2c_busy_wait(dev, 0x01); - if (rc < 0) - return -ENODEV; - - return 0; -} - -/* - * stk1160_i2c_xfer() - * the main i2c transfer function - */ -static int stk1160_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) -{ - struct stk1160 *dev = i2c_adap->algo_data; - int addr, rc, i; - - for (i = 0; i < num; i++) { - addr = msgs[i].addr << 1; - dprintk_i2c("%s: addr=%x", __func__, addr); - - if (!msgs[i].len) { - /* no len: check only for device presence */ - rc = stk1160_i2c_check_for_device(dev, addr); - if (rc < 0) { - dprintk_i2c(" no device\n"); - return rc; - } - - } else if (msgs[i].flags & I2C_M_RD) { - /* read request without preceding register selection */ - dprintk_i2c(" subaddr not selected"); - rc = -EOPNOTSUPP; - goto err; - - } else if (i + 1 < num && msgs[i].len <= 2 && - (msgs[i + 1].flags & I2C_M_RD) && - msgs[i].addr == msgs[i + 1].addr) { - - if (msgs[i].len != 1 || msgs[i + 1].len != 1) { - dprintk_i2c(" len not supported"); - rc = -EOPNOTSUPP; - goto err; - } - - dprintk_i2c(" subaddr=%x", msgs[i].buf[0]); - - rc = stk1160_i2c_read_reg(dev, addr, msgs[i].buf[0], - msgs[i + 1].buf); - - dprintk_i2c(" read=%x", *msgs[i + 1].buf); - - /* consumed two msgs, so we skip one of them */ - i++; - - } else { - if (msgs[i].len != 2) { - dprintk_i2c(" len not supported"); - rc = -EOPNOTSUPP; - goto err; - } - - dprintk_i2c(" subaddr=%x write=%x", - msgs[i].buf[0], msgs[i].buf[1]); - - rc = stk1160_i2c_write_reg(dev, addr, msgs[i].buf[0], - msgs[i].buf[1]); - } - - if (rc < 0) - goto err; - dprintk_i2c(" OK\n"); - } - - return num; -err: - dprintk_i2c(" ERROR: %d\n", rc); - return num; -} - -/* - * functionality(), what da heck is this? - */ -static u32 functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static struct i2c_algorithm algo = { - .master_xfer = stk1160_i2c_xfer, - .functionality = functionality, -}; - -static struct i2c_adapter adap_template = { - .owner = THIS_MODULE, - .name = "stk1160", - .algo = &algo, -}; - -static struct i2c_client client_template = { - .name = "stk1160 internal", -}; - -/* - * stk1160_i2c_register() - * register i2c bus - */ -int stk1160_i2c_register(struct stk1160 *dev) -{ - int rc; - - dev->i2c_adap = adap_template; - dev->i2c_adap.dev.parent = dev->dev; - strcpy(dev->i2c_adap.name, "stk1160"); - dev->i2c_adap.algo_data = dev; - - i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); - - rc = i2c_add_adapter(&dev->i2c_adap); - if (rc < 0) { - stk1160_err("cannot add i2c adapter (%d)\n", rc); - return rc; - } - - dev->i2c_client = client_template; - dev->i2c_client.adapter = &dev->i2c_adap; - - /* Set i2c clock divider device address */ - stk1160_write_reg(dev, STK1160_SICTL_CD, 0x0f); - - /* ??? */ - stk1160_write_reg(dev, STK1160_ASIC + 3, 0x00); - - return 0; -} - -/* - * stk1160_i2c_unregister() - * unregister i2c_bus - */ -int stk1160_i2c_unregister(struct stk1160 *dev) -{ - i2c_del_adapter(&dev->i2c_adap); - return 0; -} diff --git a/drivers/media/video/stk1160/stk1160-reg.h b/drivers/media/video/stk1160/stk1160-reg.h deleted file mode 100644 index 3e49da6e7edd..000000000000 --- a/drivers/media/video/stk1160/stk1160-reg.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * STK1160 driver - * - * Copyright (C) 2012 Ezequiel Garcia - * - * - * Based on Easycap driver by R.M. Thomas - * Copyright (C) 2010 R.M. Thomas - * - * - * This program is free software; 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. - * - */ - -/* GPIO Control */ -#define STK1160_GCTRL 0x000 - -/* Remote Wakup Control */ -#define STK1160_RMCTL 0x00c - -/* - * Decoder Control Register: - * This byte controls capture start/stop - * with bit #7 (0x?? OR 0x80 to activate). - */ -#define STK1160_DCTRL 0x100 - -/* Capture Frame Start Position */ -#define STK116_CFSPO 0x110 -#define STK116_CFSPO_STX_L 0x110 -#define STK116_CFSPO_STX_H 0x111 -#define STK116_CFSPO_STY_L 0x112 -#define STK116_CFSPO_STY_H 0x113 - -/* Capture Frame End Position */ -#define STK116_CFEPO 0x114 -#define STK116_CFEPO_ENX_L 0x114 -#define STK116_CFEPO_ENX_H 0x115 -#define STK116_CFEPO_ENY_L 0x116 -#define STK116_CFEPO_ENY_H 0x117 - -/* Serial Interface Control */ -#define STK1160_SICTL 0x200 -#define STK1160_SICTL_CD 0x202 -#define STK1160_SICTL_SDA 0x203 - -/* Serial Bus Write */ -#define STK1160_SBUSW 0x204 -#define STK1160_SBUSW_WA 0x204 -#define STK1160_SBUSW_WD 0x205 - -/* Serial Bus Read */ -#define STK1160_SBUSR 0x208 -#define STK1160_SBUSR_RA 0x208 -#define STK1160_SBUSR_RD 0x209 - -/* Alternate Serial Inteface Control */ -#define STK1160_ASIC 0x2fc - -/* PLL Select Options */ -#define STK1160_PLLSO 0x018 - -/* PLL Frequency Divider */ -#define STK1160_PLLFD 0x01c - -/* Timing Generator */ -#define STK1160_TIGEN 0x300 - -/* Timing Control Parameter */ -#define STK1160_TICTL 0x350 - -/* AC97 Audio Control */ -#define STK1160_AC97CTL_0 0x500 -#define STK1160_AC97CTL_1 0x504 - -/* Use [0:6] bits of register 0x504 to set codec command address */ -#define STK1160_AC97_ADDR 0x504 -/* Use [16:31] bits of register 0x500 to set codec command data */ -#define STK1160_AC97_CMD 0x502 - -/* Audio I2S Interface */ -#define STK1160_I2SCTL 0x50c - -/* EEPROM Interface */ -#define STK1160_EEPROM_SZ 0x5f0 diff --git a/drivers/media/video/stk1160/stk1160-v4l.c b/drivers/media/video/stk1160/stk1160-v4l.c deleted file mode 100644 index 360bdbee4270..000000000000 --- a/drivers/media/video/stk1160/stk1160-v4l.c +++ /dev/null @@ -1,738 +0,0 @@ -/* - * STK1160 driver - * - * Copyright (C) 2012 Ezequiel Garcia - * - * - * Based on Easycap driver by R.M. Thomas - * Copyright (C) 2010 R.M. Thomas - * - * - * This program is free software; 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 -#include -#include -#include -#include -#include -#include - -#include - -#include "stk1160.h" -#include "stk1160-reg.h" - -static unsigned int vidioc_debug; -module_param(vidioc_debug, int, 0644); -MODULE_PARM_DESC(vidioc_debug, "enable debug messages [vidioc]"); - -static bool keep_buffers; -module_param(keep_buffers, bool, 0644); -MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); - -/* supported video standards */ -static struct stk1160_fmt format[] = { - { - .name = "16 bpp YUY2, 4:2:2, packed", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - } -}; - -static void stk1160_set_std(struct stk1160 *dev) -{ - int i; - - static struct regval std525[] = { - - /* 720x480 */ - - /* Frame start */ - {STK116_CFSPO_STX_L, 0x0000}, - {STK116_CFSPO_STX_H, 0x0000}, - {STK116_CFSPO_STY_L, 0x0003}, - {STK116_CFSPO_STY_H, 0x0000}, - - /* Frame end */ - {STK116_CFEPO_ENX_L, 0x05a0}, - {STK116_CFEPO_ENX_H, 0x0005}, - {STK116_CFEPO_ENY_L, 0x00f3}, - {STK116_CFEPO_ENY_H, 0x0000}, - - {0xffff, 0xffff} - }; - - static struct regval std625[] = { - - /* 720x576 */ - - /* TODO: Each line of frame has some junk at the end */ - /* Frame start */ - {STK116_CFSPO, 0x0000}, - {STK116_CFSPO+1, 0x0000}, - {STK116_CFSPO+2, 0x0001}, - {STK116_CFSPO+3, 0x0000}, - - /* Frame end */ - {STK116_CFEPO, 0x05a0}, - {STK116_CFEPO+1, 0x0005}, - {STK116_CFEPO+2, 0x0121}, - {STK116_CFEPO+3, 0x0001}, - - {0xffff, 0xffff} - }; - - if (dev->norm & V4L2_STD_525_60) { - stk1160_dbg("registers to NTSC like standard\n"); - for (i = 0; std525[i].reg != 0xffff; i++) - stk1160_write_reg(dev, std525[i].reg, std525[i].val); - } else { - stk1160_dbg("registers to PAL like standard\n"); - for (i = 0; std625[i].reg != 0xffff; i++) - stk1160_write_reg(dev, std625[i].reg, std625[i].val); - } - -} - -/* - * Set a new alternate setting. - * Returns true is dev->max_pkt_size has changed, false otherwise. - */ -static bool stk1160_set_alternate(struct stk1160 *dev) -{ - int i, prev_alt = dev->alt; - unsigned int min_pkt_size; - bool new_pkt_size; - - /* - * If we don't set right alternate, - * then we will get a green screen with junk. - */ - min_pkt_size = STK1160_MIN_PKT_SIZE; - - for (i = 0; i < dev->num_alt; i++) { - /* stop when the selected alt setting offers enough bandwidth */ - if (dev->alt_max_pkt_size[i] >= min_pkt_size) { - dev->alt = i; - break; - /* - * otherwise make sure that we end up with the maximum bandwidth - * because the min_pkt_size equation might be wrong... - */ - } else if (dev->alt_max_pkt_size[i] > - dev->alt_max_pkt_size[dev->alt]) - dev->alt = i; - } - - stk1160_info("setting alternate %d\n", dev->alt); - - if (dev->alt != prev_alt) { - stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - stk1160_dbg("setting alt %d with wMaxPacketSize=%u\n", - dev->alt, dev->alt_max_pkt_size[dev->alt]); - usb_set_interface(dev->udev, 0, dev->alt); - } - - new_pkt_size = dev->max_pkt_size != dev->alt_max_pkt_size[dev->alt]; - dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; - - return new_pkt_size; -} - -static int stk1160_start_streaming(struct stk1160 *dev) -{ - int i, rc; - bool new_pkt_size; - - /* Check device presence */ - if (!dev->udev) - return -ENODEV; - - if (mutex_lock_interruptible(&dev->v4l_lock)) - return -ERESTARTSYS; - /* - * For some reason it is mandatory to set alternate *first* - * and only *then* initialize isoc urbs. - * Someone please explain me why ;) - */ - new_pkt_size = stk1160_set_alternate(dev); - - /* - * We (re)allocate isoc urbs if: - * there is no allocated isoc urbs, OR - * a new dev->max_pkt_size is detected - */ - if (!dev->isoc_ctl.num_bufs || new_pkt_size) { - rc = stk1160_alloc_isoc(dev); - if (rc < 0) - return rc; - } - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL); - if (rc) { - stk1160_err("cannot submit urb[%d] (%d)\n", i, rc); - stk1160_uninit_isoc(dev); - return rc; - } - } - - /* Start saa711x */ - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); - - /* Start stk1160 */ - stk1160_write_reg(dev, STK1160_DCTRL, 0xb3); - stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); - - stk1160_dbg("streaming started\n"); - - mutex_unlock(&dev->v4l_lock); - - return 0; -} - -/* Must be called with v4l_lock hold */ -static void stk1160_stop_hw(struct stk1160 *dev) -{ - /* If the device is not physically present, there is nothing to do */ - if (!dev->udev) - return; - - /* set alternate 0 */ - dev->alt = 0; - stk1160_info("setting alternate %d\n", dev->alt); - usb_set_interface(dev->udev, 0, 0); - - /* Stop stk1160 */ - stk1160_write_reg(dev, STK1160_DCTRL, 0x00); - stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); - - /* Stop saa711x */ - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); -} - -static int stk1160_stop_streaming(struct stk1160 *dev) -{ - if (mutex_lock_interruptible(&dev->v4l_lock)) - return -ERESTARTSYS; - - stk1160_cancel_isoc(dev); - - /* - * It is possible to keep buffers around using a module parameter. - * This is intended to avoid memory fragmentation. - */ - if (!keep_buffers) - stk1160_free_isoc(dev); - - stk1160_stop_hw(dev); - - stk1160_clear_queue(dev); - - stk1160_dbg("streaming stopped\n"); - - mutex_unlock(&dev->v4l_lock); - - return 0; -} - -static struct v4l2_file_operations stk1160_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -/* - * vidioc ioctls - */ -static int vidioc_querycap(struct file *file, - void *priv, struct v4l2_capability *cap) -{ - struct stk1160 *dev = video_drvdata(file); - - strcpy(cap->driver, "stk1160"); - strcpy(cap->card, "stk1160"); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index != 0) - return -EINVAL; - - strlcpy(f->description, format[f->index].name, sizeof(f->description)); - f->pixelformat = format[f->index].fourcc; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct stk1160 *dev = video_drvdata(file); - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.pixelformat = dev->fmt->fourcc; - f->fmt.pix.bytesperline = dev->width * 2; - f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct stk1160 *dev = video_drvdata(file); - - if (f->fmt.pix.pixelformat != format[0].fourcc) { - stk1160_err("fourcc format 0x%08x invalid\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - /* - * User can't choose size at his own will, - * so we just return him the current size chosen - * at standard selection. - * TODO: Implement frame scaling? - */ - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.bytesperline = dev->width * 2; - f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct stk1160 *dev = video_drvdata(file); - struct vb2_queue *q = &dev->vb_vidq; - int rc; - - if (vb2_is_busy(q)) - return -EBUSY; - - rc = vidioc_try_fmt_vid_cap(file, priv, f); - if (rc < 0) - return rc; - - /* We don't support any format changes */ - - return 0; -} - -static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct stk1160 *dev = video_drvdata(file); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); - return 0; -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct stk1160 *dev = video_drvdata(file); - - *norm = dev->norm; - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct stk1160 *dev = video_drvdata(file); - struct vb2_queue *q = &dev->vb_vidq; - - if (vb2_is_busy(q)) - return -EBUSY; - - /* Check device presence */ - if (!dev->udev) - return -ENODEV; - - /* We need to set this now, before we call stk1160_set_std */ - dev->norm = *norm; - - /* This is taken from saa7115 video decoder */ - if (dev->norm & V4L2_STD_525_60) { - dev->width = 720; - dev->height = 480; - } else if (dev->norm & V4L2_STD_625_50) { - dev->width = 720; - dev->height = 576; - } else { - stk1160_err("invalid standard\n"); - return -EINVAL; - } - - stk1160_set_std(dev); - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, - dev->norm); - - return 0; -} - - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct stk1160 *dev = video_drvdata(file); - - if (i->index > STK1160_MAX_INPUT) - return -EINVAL; - - sprintf(i->name, "Composite%d", i->index); - i->type = V4L2_INPUT_TYPE_CAMERA; - i->std = dev->vdev.tvnorms; - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct stk1160 *dev = video_drvdata(file); - *i = dev->ctl_input; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct stk1160 *dev = video_drvdata(file); - - if (vb2_is_busy(&dev->vb_vidq)) - return -EBUSY; - - if (i > STK1160_MAX_INPUT) - return -EINVAL; - - dev->ctl_input = i; - - stk1160_select_input(dev); - - return 0; -} - -static int vidioc_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - switch (chip->match.type) { - case V4L2_CHIP_MATCH_HOST: - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - return 0; - default: - return -EINVAL; - } -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct stk1160 *dev = video_drvdata(file); - int rc; - u8 val; - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: - /* TODO: Support me please :-( */ - return -EINVAL; - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - /* TODO: is this correct? */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } - - /* Match host */ - rc = stk1160_read_reg(dev, reg->reg, &val); - reg->val = val; - reg->size = 1; - - return rc; -} - -static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct stk1160 *dev = video_drvdata(file); - - switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: - return -EINVAL; - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - /* TODO: is this correct? */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } - - /* Match host */ - return stk1160_write_reg(dev, reg->reg, cpu_to_le16(reg->val)); -} -#endif - -static const struct v4l2_ioctl_ops stk1160_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_querystd = vidioc_querystd, - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - - /* vb2 takes care of these */ - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_g_chip_ident = vidioc_g_chip_ident, - -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, -#endif -}; - -/********************************************************************/ - -/* - * Videobuf2 operations - */ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct stk1160 *dev = vb2_get_drv_priv(vq); - unsigned long size; - - size = dev->width * dev->height * 2; - - /* - * Here we can change the number of buffers being requested. - * So, we set a minimum and a maximum like this: - */ - *nbuffers = clamp_t(unsigned int, *nbuffers, - STK1160_MIN_VIDEO_BUFFERS, STK1160_MAX_VIDEO_BUFFERS); - - /* This means a packed colorformat */ - *nplanes = 1; - - sizes[0] = size; - - stk1160_info("%s: buffer count %d, each %ld bytes\n", - __func__, *nbuffers, size); - - return 0; -} - -static void buffer_queue(struct vb2_buffer *vb) -{ - unsigned long flags; - struct stk1160 *dev = vb2_get_drv_priv(vb->vb2_queue); - struct stk1160_buffer *buf = - container_of(vb, struct stk1160_buffer, vb); - - spin_lock_irqsave(&dev->buf_lock, flags); - if (!dev->udev) { - /* - * If the device is disconnected return the buffer to userspace - * directly. The next QBUF call will fail with -ENODEV. - */ - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - } else { - - buf->mem = vb2_plane_vaddr(vb, 0); - buf->length = vb2_plane_size(vb, 0); - buf->bytesused = 0; - buf->pos = 0; - - /* - * If buffer length is less from expected then we return - * the buffer to userspace directly. - */ - if (buf->length < dev->width * dev->height * 2) - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - else - list_add_tail(&buf->list, &dev->avail_bufs); - - } - spin_unlock_irqrestore(&dev->buf_lock, flags); -} - -static int start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct stk1160 *dev = vb2_get_drv_priv(vq); - return stk1160_start_streaming(dev); -} - -/* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) -{ - struct stk1160 *dev = vb2_get_drv_priv(vq); - return stk1160_stop_streaming(dev); -} - -static struct vb2_ops stk1160_video_qops = { - .queue_setup = queue_setup, - .buf_queue = buffer_queue, - .start_streaming = start_streaming, - .stop_streaming = stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static struct video_device v4l_template = { - .name = "stk1160", - .tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50, - .fops = &stk1160_fops, - .ioctl_ops = &stk1160_ioctl_ops, - .release = video_device_release_empty, -}; - -/********************************************************************/ - -/* Must be called with both v4l_lock and vb_queue_lock hold */ -void stk1160_clear_queue(struct stk1160 *dev) -{ - struct stk1160_buffer *buf; - unsigned long flags; - - /* Release all active buffers */ - spin_lock_irqsave(&dev->buf_lock, flags); - while (!list_empty(&dev->avail_bufs)) { - buf = list_first_entry(&dev->avail_bufs, - struct stk1160_buffer, list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - stk1160_info("buffer [%p/%d] aborted\n", - buf, buf->vb.v4l2_buf.index); - } - /* It's important to clear current buffer */ - dev->isoc_ctl.buf = NULL; - spin_unlock_irqrestore(&dev->buf_lock, flags); -} - -int stk1160_vb2_setup(struct stk1160 *dev) -{ - int rc; - struct vb2_queue *q; - - q = &dev->vb_vidq; - memset(q, 0, sizeof(dev->vb_vidq)); - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct stk1160_buffer); - q->ops = &stk1160_video_qops; - q->mem_ops = &vb2_vmalloc_memops; - - rc = vb2_queue_init(q); - if (rc < 0) - return rc; - - /* initialize video dma queue */ - INIT_LIST_HEAD(&dev->avail_bufs); - - return 0; -} - -int stk1160_video_register(struct stk1160 *dev) -{ - int rc; - - /* Initialize video_device with a template structure */ - dev->vdev = v4l_template; - dev->vdev.debug = vidioc_debug; - dev->vdev.queue = &dev->vb_vidq; - - /* - * Provide mutexes for v4l2 core and for videobuf2 queue. - * It will be used to protect *only* v4l2 ioctls. - */ - dev->vdev.lock = &dev->v4l_lock; - dev->vdev.queue->lock = &dev->vb_queue_lock; - - /* This will be used to set video_device parent */ - dev->vdev.v4l2_dev = &dev->v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); - - /* NTSC is default */ - dev->norm = V4L2_STD_NTSC_M; - dev->width = 720; - dev->height = 480; - - /* set default format */ - dev->fmt = &format[0]; - stk1160_set_std(dev); - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, - dev->norm); - - video_set_drvdata(&dev->vdev, dev); - rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); - if (rc < 0) { - stk1160_err("video_register_device failed (%d)\n", rc); - return rc; - } - - v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(&dev->vdev)); - - return 0; -} diff --git a/drivers/media/video/stk1160/stk1160-video.c b/drivers/media/video/stk1160/stk1160-video.c deleted file mode 100644 index 378526981146..000000000000 --- a/drivers/media/video/stk1160/stk1160-video.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * STK1160 driver - * - * Copyright (C) 2012 Ezequiel Garcia - * - * - * Based on Easycap driver by R.M. Thomas - * Copyright (C) 2010 R.M. Thomas - * - * - * This program is free software; 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 "stk1160.h" - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages"); - -static inline void print_err_status(struct stk1160 *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - - if (packet < 0) - printk_ratelimited(KERN_WARNING "URB status %d [%s].\n", - status, errmsg); - else - printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s].\n", - packet, status, errmsg); -} - -static inline -struct stk1160_buffer *stk1160_next_buffer(struct stk1160 *dev) -{ - struct stk1160_buffer *buf = NULL; - unsigned long flags = 0; - - /* Current buffer must be NULL when this functions gets called */ - BUG_ON(dev->isoc_ctl.buf); - - spin_lock_irqsave(&dev->buf_lock, flags); - if (!list_empty(&dev->avail_bufs)) { - buf = list_first_entry(&dev->avail_bufs, - struct stk1160_buffer, list); - list_del(&buf->list); - } - spin_unlock_irqrestore(&dev->buf_lock, flags); - - return buf; -} - -static inline -void stk1160_buffer_done(struct stk1160 *dev) -{ - struct stk1160_buffer *buf = dev->isoc_ctl.buf; - - dev->field_count++; - - buf->vb.v4l2_buf.sequence = dev->field_count >> 1; - buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; - buf->vb.v4l2_buf.bytesused = buf->bytesused; - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); - - vb2_set_plane_payload(&buf->vb, 0, buf->bytesused); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); - - dev->isoc_ctl.buf = NULL; -} - -static inline -void stk1160_copy_video(struct stk1160 *dev, u8 *src, int len) -{ - int linesdone, lineoff, lencopy; - int bytesperline = dev->width * 2; - struct stk1160_buffer *buf = dev->isoc_ctl.buf; - u8 *dst = buf->mem; - int remain; - - /* - * TODO: These stk1160_dbg are very spammy! - * We should 1) check why we are getting them - * and 2) add ratelimit. - * - * UPDATE: One of the reasons (the only one?) for getting these - * is incorrect standard (mismatch between expected and configured). - * So perhaps, we could add a counter for errors. When the counter - * reaches some value, we simply stop streaming. - */ - - len -= 4; - src += 4; - - remain = len; - - linesdone = buf->pos / bytesperline; - lineoff = buf->pos % bytesperline; /* offset in current line */ - - if (!buf->odd) - dst += bytesperline; - - /* Multiply linesdone by two, to take account of the other field */ - dst += linesdone * bytesperline * 2 + lineoff; - - /* Copy the remaining of current line */ - if (remain < (bytesperline - lineoff)) - lencopy = remain; - else - lencopy = bytesperline - lineoff; - - /* - * Check if we have enough space left in the buffer. - * In that case, we force loop exit after copy. - */ - if (lencopy > buf->bytesused - buf->length) { - lencopy = buf->bytesused - buf->length; - remain = lencopy; - } - - /* Check if the copy is done */ - if (lencopy == 0 || remain == 0) - return; - - /* Let the bug hunt begin! sanity checks! */ - if (lencopy < 0) { - stk1160_dbg("copy skipped: negative lencopy\n"); - return; - } - - if ((unsigned long)dst + lencopy > - (unsigned long)buf->mem + buf->length) { - printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n"); - return; - } - - memcpy(dst, src, lencopy); - - buf->bytesused += lencopy; - buf->pos += lencopy; - remain -= lencopy; - - /* Copy current field line by line, interlacing with the other field */ - while (remain > 0) { - - dst += lencopy + bytesperline; - src += lencopy; - - /* Copy one line at a time */ - if (remain < bytesperline) - lencopy = remain; - else - lencopy = bytesperline; - - /* - * Check if we have enough space left in the buffer. - * In that case, we force loop exit after copy. - */ - if (lencopy > buf->bytesused - buf->length) { - lencopy = buf->bytesused - buf->length; - remain = lencopy; - } - - /* Check if the copy is done */ - if (lencopy == 0 || remain == 0) - return; - - if (lencopy < 0) { - printk_ratelimited(KERN_WARNING "stk1160: negative lencopy detected\n"); - return; - } - - if ((unsigned long)dst + lencopy > - (unsigned long)buf->mem + buf->length) { - printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n"); - return; - } - - memcpy(dst, src, lencopy); - remain -= lencopy; - - buf->bytesused += lencopy; - buf->pos += lencopy; - } -} - -/* - * Controls the isoc copy of each urb packet - */ -static void stk1160_process_isoc(struct stk1160 *dev, struct urb *urb) -{ - int i, len, status; - u8 *p; - - if (!dev) { - stk1160_warn("%s called with null device\n", __func__); - return; - } - - if (urb->status < 0) { - /* Print status and drop current packet (or field?) */ - print_err_status(dev, -1, urb->status); - return; - } - - for (i = 0; i < urb->number_of_packets; i++) { - status = urb->iso_frame_desc[i].status; - if (status < 0) { - print_err_status(dev, i, status); - continue; - } - - /* Get packet actual length and pointer to data */ - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - len = urb->iso_frame_desc[i].actual_length; - - /* Empty packet */ - if (len <= 4) - continue; - - /* - * An 8-byte packet sequence means end of field. - * So if we don't have any packet, we start receiving one now - * and if we do have a packet, then we are done with it. - * - * These end of field packets are always 0xc0 or 0x80, - * but not always 8-byte long so we don't check packet length. - */ - if (p[0] == 0xc0) { - - /* - * If first byte is 0xc0 then we received - * second field, and frame has ended. - */ - if (dev->isoc_ctl.buf != NULL) - stk1160_buffer_done(dev); - - dev->isoc_ctl.buf = stk1160_next_buffer(dev); - if (dev->isoc_ctl.buf == NULL) - return; - } - - /* - * If we don't have a buffer here, then it means we - * haven't found the start mark sequence. - */ - if (dev->isoc_ctl.buf == NULL) - continue; - - if (p[0] == 0xc0 || p[0] == 0x80) { - - /* We set next packet parity and - * continue to get next one - */ - dev->isoc_ctl.buf->odd = *p & 0x40; - dev->isoc_ctl.buf->pos = 0; - continue; - } - - stk1160_copy_video(dev, p, len); - } -} - - -/* - * IRQ callback, called by URB callback - */ -static void stk1160_isoc_irq(struct urb *urb) -{ - int i, rc; - struct stk1160 *dev = urb->context; - - switch (urb->status) { - case 0: - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - /* TODO: check uvc driver: he frees the queue here */ - return; - default: - stk1160_err("urb error! status %d\n", urb->status); - return; - } - - stk1160_process_isoc(dev, urb); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - - rc = usb_submit_urb(urb, GFP_ATOMIC); - if (rc) - stk1160_err("urb re-submit failed (%d)\n", rc); -} - -/* - * Cancel urbs - * This function can't be called in atomic context - */ -void stk1160_cancel_isoc(struct stk1160 *dev) -{ - int i; - - /* - * This check is not necessary, but we add it - * to avoid a spurious debug message - */ - if (!dev->isoc_ctl.num_bufs) - return; - - stk1160_dbg("killing urbs...\n"); - - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - - /* - * To kill urbs we can't be in atomic context. - * We don't care for NULL pointer since - * usb_kill_urb allows it. - */ - usb_kill_urb(dev->isoc_ctl.urb[i]); - } - - stk1160_dbg("all urbs killed\n"); -} - -/* - * Releases urb and transfer buffers - * Obviusly, associated urb must be killed before releasing it. - */ -void stk1160_free_isoc(struct stk1160 *dev) -{ - struct urb *urb; - int i; - - stk1160_dbg("freeing urb buffers...\n"); - - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - - urb = dev->isoc_ctl.urb[i]; - if (urb) { - - if (dev->isoc_ctl.transfer_buffer[i]) { -#ifndef CONFIG_DMA_NONCOHERENT - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); -#else - kfree(dev->isoc_ctl.transfer_buffer[i]); -#endif - } - usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; - } - dev->isoc_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - dev->isoc_ctl.num_bufs = 0; - - stk1160_dbg("all urb buffers freed\n"); -} - -/* - * Helper for cancelling and freeing urbs - * This function can't be called in atomic context - */ -void stk1160_uninit_isoc(struct stk1160 *dev) -{ - stk1160_cancel_isoc(dev); - stk1160_free_isoc(dev); -} - -/* - * Allocate URBs - */ -int stk1160_alloc_isoc(struct stk1160 *dev) -{ - struct urb *urb; - int i, j, k, sb_size, max_packets, num_bufs; - - /* - * It may be necessary to release isoc here, - * since isoc are only released on disconnection. - * (see new_pkt_size flag) - */ - if (dev->isoc_ctl.num_bufs) - stk1160_uninit_isoc(dev); - - stk1160_dbg("allocating urbs...\n"); - - num_bufs = STK1160_NUM_BUFS; - max_packets = STK1160_NUM_PACKETS; - sb_size = max_packets * dev->max_pkt_size; - - dev->isoc_ctl.buf = NULL; - dev->isoc_ctl.max_pkt_size = dev->max_pkt_size; - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - stk1160_err("out of memory for urb array\n"); - return -ENOMEM; - } - - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { - stk1160_err("out of memory for usb transfers\n"); - kfree(dev->isoc_ctl.urb); - return -ENOMEM; - } - - /* allocate urbs and transfer buffers */ - for (i = 0; i < num_bufs; i++) { - - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) { - stk1160_err("cannot alloc urb[%d]\n", i); - stk1160_uninit_isoc(dev); - return -ENOMEM; - } - dev->isoc_ctl.urb[i] = urb; - -#ifndef CONFIG_DMA_NONCOHERENT - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); -#else - dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size, GFP_KERNEL); -#endif - if (!dev->isoc_ctl.transfer_buffer[i]) { - stk1160_err("cannot alloc %d bytes for tx buffer\n", - sb_size); - stk1160_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); - - /* - * FIXME: Where can I get the endpoint? - */ - urb->dev = dev->udev; - urb->pipe = usb_rcvisocpipe(dev->udev, STK1160_EP_VIDEO); - urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i]; - urb->transfer_buffer_length = sb_size; - urb->complete = stk1160_isoc_irq; - urb->context = dev; - urb->interval = 1; - urb->start_frame = 0; - urb->number_of_packets = max_packets; -#ifndef CONFIG_DMA_NONCOHERENT - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; -#else - urb->transfer_flags = URB_ISO_ASAP; -#endif - - k = 0; - for (j = 0; j < max_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - dev->isoc_ctl.max_pkt_size; - k += dev->isoc_ctl.max_pkt_size; - } - } - - stk1160_dbg("urbs allocated\n"); - - /* At last we can say we have some buffers */ - dev->isoc_ctl.num_bufs = num_bufs; - - return 0; -} - diff --git a/drivers/media/video/stk1160/stk1160.h b/drivers/media/video/stk1160/stk1160.h deleted file mode 100644 index 3feba0033f98..000000000000 --- a/drivers/media/video/stk1160/stk1160.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * STK1160 driver - * - * Copyright (C) 2012 Ezequiel Garcia - * - * - * Based on Easycap driver by R.M. Thomas - * Copyright (C) 2010 R.M. Thomas - * - * - * This program is free software; 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 - -#define STK1160_VERSION "0.9.5" -#define STK1160_VERSION_NUM 0x000905 - -/* TODO: Decide on number of packets for each buffer */ -#define STK1160_NUM_PACKETS 64 - -/* Number of buffers for isoc transfers */ -#define STK1160_NUM_BUFS 16 /* TODO */ - -/* TODO: This endpoint address should be retrieved */ -#define STK1160_EP_VIDEO 0x82 -#define STK1160_EP_AUDIO 0x81 - -/* Max and min video buffers */ -#define STK1160_MIN_VIDEO_BUFFERS 8 -#define STK1160_MAX_VIDEO_BUFFERS 32 - -#define STK1160_MIN_PKT_SIZE 3072 - -#define STK1160_MAX_INPUT 3 - -#define STK1160_I2C_TIMEOUT 100 - -/* TODO: Print helpers - * I could use dev_xxx, pr_xxx, v4l2_xxx or printk. - * However, there isn't a solid consensus on which - * new drivers should use. - * - */ -#define DEBUG -#ifdef DEBUG -#define stk1160_dbg(fmt, args...) \ - printk(KERN_DEBUG "stk1160: " fmt, ## args) -#else -#define stk1160_dbg(fmt, args...) -#endif - -#define stk1160_info(fmt, args...) \ - pr_info("stk1160: " fmt, ## args) - -#define stk1160_warn(fmt, args...) \ - pr_warn("stk1160: " fmt, ## args) - -#define stk1160_err(fmt, args...) \ - pr_err("stk1160: " fmt, ## args) - -/* Buffer for one video frame */ -struct stk1160_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; - struct list_head list; - - void *mem; - unsigned int length; /* buffer length */ - unsigned int bytesused; /* bytes written */ - int odd; /* current oddity */ - - /* - * Since we interlace two fields per frame, - * this is different from bytesused. - */ - unsigned int pos; /* current pos inside buffer */ -}; - -struct stk1160_isoc_ctl { - /* max packet size of isoc transaction */ - int max_pkt_size; - - /* number of allocated urbs */ - int num_bufs; - - /* urb for isoc transfers */ - struct urb **urb; - - /* transfer buffers for isoc transfer */ - char **transfer_buffer; - - /* current buffer */ - struct stk1160_buffer *buf; -}; - -struct stk1160_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ - int depth; -}; - -struct stk1160 { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct v4l2_ctrl_handler ctrl_handler; - - struct device *dev; - struct usb_device *udev; - - /* saa7115 subdev */ - struct v4l2_subdev *sd_saa7115; - - /* isoc control struct */ - struct list_head avail_bufs; - - /* video capture */ - struct vb2_queue vb_vidq; - - /* max packet size of isoc transaction */ - int max_pkt_size; - /* array of wMaxPacketSize */ - unsigned int *alt_max_pkt_size; - /* alternate */ - int alt; - /* Number of alternative settings */ - int num_alt; - - struct stk1160_isoc_ctl isoc_ctl; - char urb_buf[255]; /* urb control msg buffer */ - - /* frame properties */ - int width; /* current frame width */ - int height; /* current frame height */ - unsigned int ctl_input; /* selected input */ - v4l2_std_id norm; /* current norm */ - struct stk1160_fmt *fmt; /* selected format */ - - unsigned int field_count; /* not sure ??? */ - enum v4l2_field field; /* also not sure :/ */ - - /* i2c i/o */ - struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; - - struct mutex v4l_lock; - struct mutex vb_queue_lock; - spinlock_t buf_lock; - - struct file *fh_owner; /* filehandle ownership */ - - /* EXPERIMENTAL */ - struct snd_card *snd_card; -}; - -struct regval { - u16 reg; - u16 val; -}; - -/* Provided by stk1160-v4l.c */ -int stk1160_vb2_setup(struct stk1160 *dev); -int stk1160_video_register(struct stk1160 *dev); -void stk1160_video_unregister(struct stk1160 *dev); -void stk1160_clear_queue(struct stk1160 *dev); - -/* Provided by stk1160-video.c */ -int stk1160_alloc_isoc(struct stk1160 *dev); -void stk1160_free_isoc(struct stk1160 *dev); -void stk1160_cancel_isoc(struct stk1160 *dev); -void stk1160_uninit_isoc(struct stk1160 *dev); - -/* Provided by stk1160-i2c.c */ -int stk1160_i2c_register(struct stk1160 *dev); -int stk1160_i2c_unregister(struct stk1160 *dev); - -/* Provided by stk1160-core.c */ -int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value); -int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value); -int stk1160_write_regs_req(struct stk1160 *dev, u8 req, u16 reg, - char *buf, int len); -int stk1160_read_reg_req_len(struct stk1160 *dev, u8 req, u16 reg, - char *buf, int len); -void stk1160_select_input(struct stk1160 *dev); - -/* Provided by stk1160-ac97.c */ -#ifdef CONFIG_VIDEO_STK1160_AC97 -int stk1160_ac97_register(struct stk1160 *dev); -int stk1160_ac97_unregister(struct stk1160 *dev); -#else -static inline int stk1160_ac97_register(struct stk1160 *dev) { return 0; } -static inline int stk1160_ac97_unregister(struct stk1160 *dev) { return 0; } -#endif - diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig deleted file mode 100644 index 645d915267e6..000000000000 --- a/drivers/media/video/tlg2300/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config VIDEO_TLG2300 - tristate "Telegent TLG2300 USB video capture support" - depends on VIDEO_DEV && I2C && SND && DVB_CORE - select VIDEO_TUNER - select VIDEO_TVEEPROM - depends on RC_CORE - select VIDEOBUF_VMALLOC - select SND_PCM - select VIDEOBUF_DVB - - ---help--- - This is a video4linux driver for Telegent tlg2300 based TV cards. - The driver supports V4L2, DVB-T and radio. - - To compile this driver as a module, choose M here: the - module will be called poseidon diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile deleted file mode 100644 index 4d660879999f..000000000000 --- a/drivers/media/video/tlg2300/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -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/tuners -ccflags-y += -Idrivers/media/dvb-core -ccflags-y += -Idrivers/media/dvb-frontends - diff --git a/drivers/media/video/tlg2300/pd-alsa.c b/drivers/media/video/tlg2300/pd-alsa.c deleted file mode 100644 index 9f8b7da56b67..000000000000 --- a/drivers/media/video/tlg2300/pd-alsa.c +++ /dev/null @@ -1,332 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pd-common.h" -#include "vendorcmds.h" - -static void complete_handler_audio(struct urb *urb); -#define AUDIO_EP (0x83) -#define AUDIO_BUF_SIZE (512) -#define PERIOD_SIZE (1024 * 8) -#define PERIOD_MIN (4) -#define PERIOD_MAX PERIOD_MIN - -static struct snd_pcm_hardware snd_pd_hw_capture = { - .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID, - - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN, - .period_bytes_min = PERIOD_SIZE, - .period_bytes_max = PERIOD_SIZE, - .periods_min = PERIOD_MIN, - .periods_max = PERIOD_MAX, - /* - .buffer_bytes_max = 62720 * 8, - .period_bytes_min = 64, - .period_bytes_max = 12544, - .periods_min = 2, - .periods_max = 98 - */ -}; - -static int snd_pd_capture_open(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (!p) - return -ENODEV; - pa->users++; - pa->card_close = 0; - pa->capture_pcm_substream = substream; - runtime->private_data = p; - - runtime->hw = snd_pd_hw_capture; - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - usb_autopm_get_interface(p->interface); - kref_get(&p->kref); - return 0; -} - -static int snd_pd_pcm_close(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - - pa->users--; - pa->card_close = 1; - usb_autopm_put_interface(p->interface); - kref_put(&p->kref, poseidon_delete); - return 0; -} - -static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int size; - - size = params_buffer_bytes(hw_params); - if (runtime->dma_area) { - if (runtime->dma_bytes > size) - return 0; - vfree(runtime->dma_area); - } - runtime->dma_area = vmalloc(size); - if (!runtime->dma_area) - return -ENOMEM; - else - runtime->dma_bytes = size; - return 0; -} - -static int audio_buf_free(struct poseidon *p) -{ - struct poseidon_audio *pa = &p->audio; - int i; - - for (i = 0; i < AUDIO_BUFS; i++) - if (pa->urb_array[i]) - usb_kill_urb(pa->urb_array[i]); - free_all_urb_generic(pa->urb_array, AUDIO_BUFS); - logpm(); - return 0; -} - -static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - - logpm(); - audio_buf_free(p); - return 0; -} - -static int snd_pd_prepare(struct snd_pcm_substream *substream) -{ - return 0; -} - -#define AUDIO_TRAILER_SIZE (16) -static inline void handle_audio_data(struct urb *urb, int *period_elapsed) -{ - struct poseidon_audio *pa = urb->context; - struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime; - - int stride = runtime->frame_bits >> 3; - int len = urb->actual_length / stride; - unsigned char *cp = urb->transfer_buffer; - unsigned int oldptr = pa->rcv_position; - - if (urb->actual_length == AUDIO_BUF_SIZE - 4) - len -= (AUDIO_TRAILER_SIZE / stride); - - /* do the copy */ - if (oldptr + len >= runtime->buffer_size) { - unsigned int cnt = runtime->buffer_size - oldptr; - - memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); - memcpy(runtime->dma_area, (cp + cnt * stride), - (len * stride - cnt * stride)); - } else - memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); - - /* update the statas */ - snd_pcm_stream_lock(pa->capture_pcm_substream); - pa->rcv_position += len; - if (pa->rcv_position >= runtime->buffer_size) - pa->rcv_position -= runtime->buffer_size; - - pa->copied_position += (len); - if (pa->copied_position >= runtime->period_size) { - pa->copied_position -= runtime->period_size; - *period_elapsed = 1; - } - snd_pcm_stream_unlock(pa->capture_pcm_substream); -} - -static void complete_handler_audio(struct urb *urb) -{ - struct poseidon_audio *pa = urb->context; - struct snd_pcm_substream *substream = pa->capture_pcm_substream; - int period_elapsed = 0; - int ret; - - if (1 == pa->card_close || pa->capture_stream != STREAM_ON) - return; - - if (urb->status != 0) { - /*if (urb->status == -ESHUTDOWN)*/ - return; - } - - if (substream) { - if (urb->actual_length) { - handle_audio_data(urb, &period_elapsed); - if (period_elapsed) - snd_pcm_period_elapsed(substream); - } - } - - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) - log("audio urb failed (errcod = %i)", ret); - return; -} - -static int fire_audio_urb(struct poseidon *p) -{ - int i, ret = 0; - struct poseidon_audio *pa = &p->audio; - - alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS, - p->udev, AUDIO_EP, - AUDIO_BUF_SIZE, GFP_ATOMIC, - complete_handler_audio, pa); - - for (i = 0; i < AUDIO_BUFS; i++) { - ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL); - if (ret) - log("urb err : %d", ret); - } - log(); - return ret; -} - -static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - - if (debug_mode) - log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_START: - if (pa->capture_stream == STREAM_ON) - return 0; - - pa->rcv_position = pa->copied_position = 0; - pa->capture_stream = STREAM_ON; - - if (in_hibernation(p)) - return 0; - fire_audio_urb(p); - return 0; - - case SNDRV_PCM_TRIGGER_SUSPEND: - pa->capture_stream = STREAM_SUSPEND; - return 0; - case SNDRV_PCM_TRIGGER_STOP: - pa->capture_stream = STREAM_OFF; - return 0; - default: - return -EINVAL; - } -} - -static snd_pcm_uframes_t -snd_pd_capture_pointer(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - return pa->rcv_position; -} - -static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs, - unsigned long offset) -{ - void *pageptr = subs->runtime->dma_area + offset; - return vmalloc_to_page(pageptr); -} - -static struct snd_pcm_ops pcm_capture_ops = { - .open = snd_pd_capture_open, - .close = snd_pd_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_pd_hw_capture_params, - .hw_free = snd_pd_hw_capture_free, - .prepare = snd_pd_prepare, - .trigger = snd_pd_capture_trigger, - .pointer = snd_pd_capture_pointer, - .page = snd_pcm_pd_get_page, -}; - -#ifdef CONFIG_PM -int pm_alsa_suspend(struct poseidon *p) -{ - logpm(p); - audio_buf_free(p); - return 0; -} - -int pm_alsa_resume(struct poseidon *p) -{ - logpm(p); - fire_audio_urb(p); - return 0; -} -#endif - -int poseidon_audio_init(struct poseidon *p) -{ - struct poseidon_audio *pa = &p->audio; - struct snd_card *card; - struct snd_pcm *pcm; - int ret; - - ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card); - if (ret != 0) - return ret; - - ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); - pcm->info_flags = 0; - pcm->private_data = p; - strcpy(pcm->name, "poseidon audio capture"); - - strcpy(card->driver, "ALSA driver"); - strcpy(card->shortname, "poseidon Audio"); - strcpy(card->longname, "poseidon ALSA Audio"); - - if (snd_card_register(card)) { - snd_card_free(card); - return -ENOMEM; - } - pa->card = card; - return 0; -} - -int poseidon_audio_free(struct poseidon *p) -{ - struct poseidon_audio *pa = &p->audio; - - if (pa->card) - snd_card_free(pa->card); - return 0; -} diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h deleted file mode 100644 index 5dd73b7857d1..000000000000 --- a/drivers/media/video/tlg2300/pd-common.h +++ /dev/null @@ -1,281 +0,0 @@ -#ifndef PD_COMMON_H -#define PD_COMMON_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dvb_frontend.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dmxdev.h" - -#define SBUF_NUM 8 -#define MAX_BUFFER_NUM 6 -#define PK_PER_URB 32 -#define ISO_PKT_SIZE 3072 - -#define POSEIDON_STATE_NONE (0x0000) -#define POSEIDON_STATE_ANALOG (0x0001) -#define POSEIDON_STATE_FM (0x0002) -#define POSEIDON_STATE_DVBT (0x0004) -#define POSEIDON_STATE_VBI (0x0008) -#define POSEIDON_STATE_DISCONNECT (0x0080) - -#define PM_SUSPEND_DELAY 3 - -#define V4L_PAL_VBI_LINES 18 -#define V4L_NTSC_VBI_LINES 12 -#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2) -#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2) - -#define TUNER_FREQ_MIN (45000000) -#define TUNER_FREQ_MAX (862000000) - -struct vbi_data { - struct video_device *v_dev; - struct video_data *video; - struct front_face *front; - - unsigned int copied; - unsigned int vbi_size; /* the whole size of two fields */ - int users; -}; - -/* - * This is the running context of the video, it is useful for - * resume() - */ -struct running_context { - u32 freq; /* VIDIOC_S_FREQUENCY */ - int audio_idx; /* VIDIOC_S_TUNER */ - v4l2_std_id tvnormid; /* VIDIOC_S_STD */ - int sig_index; /* VIDIOC_S_INPUT */ - struct v4l2_pix_format pix; /* VIDIOC_S_FMT */ -}; - -struct video_data { - /* v4l2 video device */ - struct video_device *v_dev; - - /* the working context */ - struct running_context context; - - /* for data copy */ - int field_count; - - char *dst; - int lines_copied; - int prev_left; - - int lines_per_field; - int lines_size; - - /* for communication */ - u8 endpoint_addr; - struct urb *urb_array[SBUF_NUM]; - struct vbi_data *vbi; - struct poseidon *pd; - struct front_face *front; - - int is_streaming; - int users; - - /* for bubble handler */ - struct work_struct bubble_work; -}; - -enum pcm_stream_state { - STREAM_OFF, - STREAM_ON, - STREAM_SUSPEND, -}; - -#define AUDIO_BUFS (3) -#define CAPTURE_STREAM_EN 1 -struct poseidon_audio { - struct urb *urb_array[AUDIO_BUFS]; - unsigned int copied_position; - struct snd_pcm_substream *capture_pcm_substream; - - unsigned int rcv_position; - struct snd_card *card; - int card_close; - - int users; - int pm_state; - enum pcm_stream_state capture_stream; -}; - -struct radio_data { - __u32 fm_freq; - int users; - unsigned int is_radio_streaming; - int pre_emphasis; - struct video_device *fm_dev; -}; - -#define DVB_SBUF_NUM 4 -#define DVB_URB_BUF_SIZE 0x2000 -struct pd_dvb_adapter { - struct dvb_adapter dvb_adap; - struct dvb_frontend dvb_fe; - struct dmxdev dmxdev; - struct dvb_demux demux; - - atomic_t users; - atomic_t active_feed; - - /* data transfer */ - s32 is_streaming; - struct urb *urb_array[DVB_SBUF_NUM]; - struct poseidon *pd_device; - u8 ep_addr; - u8 reserved[3]; - - /* data for power resume*/ - struct dtv_frontend_properties fe_param; - - /* for channel scanning */ - int prev_freq; - int bandwidth; - unsigned long last_jiffies; -}; - -struct front_face { - /* use this field to distinguish VIDEO and VBI */ - enum v4l2_buf_type type; - - /* for host */ - struct videobuf_queue q; - - /* the bridge for host and device */ - struct videobuf_buffer *curr_frame; - - /* for device */ - spinlock_t queue_lock; - struct list_head active; - struct poseidon *pd; -}; - -struct poseidon { - struct list_head device_list; - - struct mutex lock; - struct kref kref; - - /* for V4L2 */ - struct v4l2_device v4l2_dev; - - /* hardware info */ - struct usb_device *udev; - struct usb_interface *interface; - int cur_transfer_mode; - - struct video_data video_data; /* video */ - struct vbi_data vbi_data; /* vbi */ - struct poseidon_audio audio; /* audio (alsa) */ - struct radio_data radio_data; /* FM */ - struct pd_dvb_adapter dvb_data; /* DVB */ - - u32 state; - struct file *file_for_stream; /* the active stream*/ - -#ifdef CONFIG_PM - int (*pm_suspend)(struct poseidon *); - int (*pm_resume)(struct poseidon *); - pm_message_t msg; - - struct work_struct pm_work; - u8 portnum; -#endif -}; - -struct poseidon_format { - char *name; - int fourcc; /* video4linux 2 */ - int depth; /* bit/pixel */ - int flags; -}; - -struct poseidon_tvnorm { - v4l2_std_id v4l2_id; - char name[12]; - u32 tlg_tvnorm; -}; - -/* video */ -int pd_video_init(struct poseidon *); -void pd_video_exit(struct poseidon *); -int stop_all_video_stream(struct poseidon *); - -/* alsa audio */ -int poseidon_audio_init(struct poseidon *); -int poseidon_audio_free(struct poseidon *); -#ifdef CONFIG_PM -int pm_alsa_suspend(struct poseidon *); -int pm_alsa_resume(struct poseidon *); -#endif - -/* dvb */ -int pd_dvb_usb_device_init(struct poseidon *); -void pd_dvb_usb_device_exit(struct poseidon *); -void pd_dvb_usb_device_cleanup(struct poseidon *); -int pd_dvb_get_adapter_num(struct pd_dvb_adapter *); -void dvb_stop_streaming(struct pd_dvb_adapter *); - -/* FM */ -int poseidon_fm_init(struct poseidon *); -int poseidon_fm_exit(struct poseidon *); -struct video_device *vdev_init(struct poseidon *, struct video_device *); - -/* vendor command ops */ -int send_set_req(struct poseidon*, u8, s32, s32*); -int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32); -s32 set_tuner_mode(struct poseidon*, unsigned char); - -/* bulk urb alloc/free */ -int alloc_bulk_urbs_generic(struct urb **urb_array, int num, - struct usb_device *udev, u8 ep_addr, - int buf_size, gfp_t gfp_flags, - usb_complete_t complete_fn, void *context); -void free_all_urb_generic(struct urb **urb_array, int num); - -/* misc */ -void poseidon_delete(struct kref *kref); -void destroy_video_device(struct video_device **v_dev); -extern int debug_mode; -void set_debug_mode(struct video_device *vfd, int debug_mode); - -#ifdef CONFIG_PM -#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE) -#else -#define in_hibernation(pd) (0) -#endif -#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt)) - -#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \ - __func__, __LINE__, ## __VA_ARGS__) - -/* for power management */ -#define logpm(pd) do {\ - if (debug_mode & 0x10)\ - log();\ - } while (0) - -#define logs(f) do { \ - if ((debug_mode & 0x4) && \ - (f)->type == V4L2_BUF_TYPE_VBI_CAPTURE) \ - log("type : VBI");\ - \ - if ((debug_mode & 0x8) && \ - (f)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) \ - log("type : VIDEO");\ - } while (0) -#endif diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c deleted file mode 100644 index 30fcb117e898..000000000000 --- a/drivers/media/video/tlg2300/pd-dvb.c +++ /dev/null @@ -1,596 +0,0 @@ -#include "pd-common.h" -#include -#include -#include -#include -#include - -#include "vendorcmds.h" -#include -#include - -static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb); - -static int dvb_bandwidth[][2] = { - { TLG_BW_8, 8000000 }, - { TLG_BW_7, 7000000 }, - { TLG_BW_6, 6000000 } -}; -static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth); - -static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb); -static int poseidon_check_mode_dvbt(struct poseidon *pd) -{ - s32 ret = 0, cmd_status = 0; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - - ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE); - if (ret != 0) - return ret; - - ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T); - if (ret) - return ret; - - /* signal source */ - ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status); - if (ret|cmd_status) - return ret; - - return 0; -} - -/* acquire : - * 1 == open - * 0 == release - */ -static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) -{ - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb; - int ret = 0; - - if (!pd) - return -ENODEV; - - pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe); - if (acquire) { - mutex_lock(&pd->lock); - if (pd->state & POSEIDON_STATE_DISCONNECT) { - ret = -ENODEV; - goto open_out; - } - - if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) { - ret = -EBUSY; - goto open_out; - } - - usb_autopm_get_interface(pd->interface); - if (0 == pd->state) { - ret = poseidon_check_mode_dvbt(pd); - if (ret < 0) { - usb_autopm_put_interface(pd->interface); - goto open_out; - } - pd->state |= POSEIDON_STATE_DVBT; - pd_dvb->bandwidth = 0; - pd_dvb->prev_freq = 0; - } - atomic_inc(&pd_dvb->users); - kref_get(&pd->kref); -open_out: - mutex_unlock(&pd->lock); - } else { - dvb_stop_streaming(pd_dvb); - - if (atomic_dec_and_test(&pd_dvb->users)) { - mutex_lock(&pd->lock); - pd->state &= ~POSEIDON_STATE_DVBT; - mutex_unlock(&pd->lock); - } - kref_put(&pd->kref, poseidon_delete); - usb_autopm_put_interface(pd->interface); - } - return ret; -} - -#ifdef CONFIG_PM -static void poseidon_fe_release(struct dvb_frontend *fe) -{ - struct poseidon *pd = fe->demodulator_priv; - - pd->pm_suspend = NULL; - pd->pm_resume = NULL; -} -#else -#define poseidon_fe_release NULL -#endif - -static s32 poseidon_fe_sleep(struct dvb_frontend *fe) -{ - return 0; -} - -/* - * return true if we can satisfy the conditions, else return false. - */ -static bool check_scan_ok(__u32 freq, int bandwidth, - struct pd_dvb_adapter *adapter) -{ - if (bandwidth < 0) - return false; - - if (adapter->prev_freq == freq - && adapter->bandwidth == bandwidth) { - long nl = jiffies - adapter->last_jiffies; - unsigned int msec ; - - msec = jiffies_to_msecs(abs(nl)); - return msec > 15000 ? true : false; - } - return true; -} - -/* - * Check if the firmware delays too long for an invalid frequency. - */ -static int fw_delay_overflow(struct pd_dvb_adapter *adapter) -{ - long nl = jiffies - adapter->last_jiffies; - unsigned int msec ; - - msec = jiffies_to_msecs(abs(nl)); - return msec > 800 ? true : false; -} - -static int poseidon_set_fe(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *fep = &fe->dtv_property_cache; - s32 ret = 0, cmd_status = 0; - s32 i, bandwidth = -1; - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - if (in_hibernation(pd)) - return -EBUSY; - - mutex_lock(&pd->lock); - for (i = 0; i < dvb_bandwidth_length; i++) - if (fep->bandwidth_hz == dvb_bandwidth[i][1]) - bandwidth = dvb_bandwidth[i][0]; - - if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) { - ret = send_set_req(pd, TUNE_FREQ_SELECT, - fep->frequency / 1000, &cmd_status); - if (ret | cmd_status) { - log("error line"); - goto front_out; - } - - ret = send_set_req(pd, DVBT_BANDW_SEL, - bandwidth, &cmd_status); - if (ret | cmd_status) { - log("error line"); - goto front_out; - } - - ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - if (ret | cmd_status) { - log("error line"); - goto front_out; - } - - /* save the context for future */ - memcpy(&pd_dvb->fe_param, fep, sizeof(*fep)); - pd_dvb->bandwidth = bandwidth; - pd_dvb->prev_freq = fep->frequency; - pd_dvb->last_jiffies = jiffies; - } -front_out: - mutex_unlock(&pd->lock); - return ret; -} - -#ifdef CONFIG_PM -static int pm_dvb_suspend(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - dvb_stop_streaming(pd_dvb); - dvb_urb_cleanup(pd_dvb); - msleep(500); - return 0; -} - -static int pm_dvb_resume(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - poseidon_check_mode_dvbt(pd); - msleep(300); - poseidon_set_fe(&pd_dvb->dvb_fe); - - dvb_start_streaming(pd_dvb); - return 0; -} -#endif - -static s32 poseidon_fe_init(struct dvb_frontend *fe) -{ - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - -#ifdef CONFIG_PM - pd->pm_suspend = pm_dvb_suspend; - pd->pm_resume = pm_dvb_resume; -#endif - memset(&pd_dvb->fe_param, 0, - sizeof(struct dtv_frontend_properties)); - return 0; -} - -static int poseidon_get_fe(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *fep = &fe->dtv_property_cache; - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - memcpy(fep, &pd_dvb->fe_param, sizeof(*fep)); - return 0; -} - -static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *tune) -{ - tune->min_delay_ms = 1000; - return 0; -} - -static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat) -{ - struct poseidon *pd = fe->demodulator_priv; - s32 ret = -1, cmd_status; - struct tuner_dtv_sig_stat_s status = {}; - - if (in_hibernation(pd)) - return -EBUSY; - mutex_lock(&pd->lock); - - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, - &status, &cmd_status, sizeof(status)); - if (ret | cmd_status) { - log("get tuner status error"); - goto out; - } - - if (debug_mode) - log("P : %d, L %d, LB :%d", status.sig_present, - status.sig_locked, status.sig_lock_busy); - - if (status.sig_lock_busy) { - goto out; - } else if (status.sig_present || status.sig_locked) { - *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER - | FE_HAS_SYNC | FE_HAS_VITERBI; - } else { - if (fw_delay_overflow(&pd->dvb_data)) - *stat |= FE_TIMEDOUT; - } -out: - mutex_unlock(&pd->lock); - return ret; -} - -static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber) -{ - struct poseidon *pd = fe->demodulator_priv; - struct tuner_ber_rate_s tlg_ber = {}; - s32 ret = -1, cmd_status; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_BER_RATE, 0, - &tlg_ber, &cmd_status, sizeof(tlg_ber)); - if (ret | cmd_status) - goto out; - *ber = tlg_ber.ber_rate; -out: - mutex_unlock(&pd->lock); - return ret; -} - -static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct poseidon *pd = fe->demodulator_priv; - struct tuner_dtv_sig_stat_s status = {}; - s32 ret = 0, cmd_status; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, - &status, &cmd_status, sizeof(status)); - if (ret | cmd_status) - goto out; - if ((status.sig_present || status.sig_locked) && !status.sig_strength) - *strength = 0xFFFF; - else - *strength = status.sig_strength; -out: - mutex_unlock(&pd->lock); - return ret; -} - -static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - return 0; -} - -static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) -{ - *unc = 0; - return 0; -} - -static struct dvb_frontend_ops poseidon_frontend_ops = { - .delsys = { SYS_DVBT }, - .info = { - .name = "Poseidon DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 62500,/* FIXME */ - .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 = poseidon_fe_release, - - .init = poseidon_fe_init, - .sleep = poseidon_fe_sleep, - - .set_frontend = poseidon_set_fe, - .get_frontend = poseidon_get_fe, - .get_tune_settings = poseidon_fe_get_tune_settings, - - .read_status = poseidon_read_status, - .read_ber = poseidon_read_ber, - .read_signal_strength = poseidon_read_signal_strength, - .read_snr = poseidon_read_snr, - .read_ucblocks = poseidon_read_unc_blocks, - - .ts_bus_ctrl = poseidon_ts_bus_ctrl, -}; - -static void dvb_urb_irq(struct urb *urb) -{ - struct pd_dvb_adapter *pd_dvb = urb->context; - int len = urb->transfer_buffer_length; - struct dvb_demux *demux = &pd_dvb->demux; - s32 ret; - - if (!pd_dvb->is_streaming || urb->status) { - if (urb->status == -EPROTO) - goto resend; - return; - } - - if (urb->actual_length == len) - dvb_dmx_swfilter(demux, urb->transfer_buffer, len); - else if (urb->actual_length == len - 4) { - int offset; - u8 *buf = urb->transfer_buffer; - - /* - * The packet size is 512, - * last packet contains 456 bytes tsp data - */ - for (offset = 456; offset < len; offset += 512) { - if (!strncmp(buf + offset, "DVHS", 4)) { - dvb_dmx_swfilter(demux, buf, offset); - if (len > offset + 52 + 4) { - /*16 bytes trailer + 36 bytes padding */ - buf += offset + 52; - len -= offset + 52 + 4; - dvb_dmx_swfilter(demux, buf, len); - } - break; - } - } - } - -resend: - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - log(" usb_submit_urb failed: error %d", ret); -} - -static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb) -{ - if (pd_dvb->urb_array[0]) - return 0; - - alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM, - pd_dvb->pd_device->udev, pd_dvb->ep_addr, - DVB_URB_BUF_SIZE, GFP_KERNEL, - dvb_urb_irq, pd_dvb); - return 0; -} - -static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb) -{ - free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM); -} - -static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb) -{ - struct poseidon *pd = pd_dvb->pd_device; - int ret = 0; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - mutex_lock(&pd->lock); - if (!pd_dvb->is_streaming) { - s32 i, cmd_status = 0; - /* - * Once upon a time, there was a difficult bug lying here. - * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - */ - - ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status); - if (ret | cmd_status) - goto out; - - ret = dvb_urb_init(pd_dvb); - if (ret < 0) - goto out; - - pd_dvb->is_streaming = 1; - for (i = 0; i < DVB_SBUF_NUM; i++) { - ret = usb_submit_urb(pd_dvb->urb_array[i], - GFP_KERNEL); - if (ret) { - log(" submit urb error %d", ret); - goto out; - } - } - } -out: - mutex_unlock(&pd->lock); - return ret; -} - -void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb) -{ - struct poseidon *pd = pd_dvb->pd_device; - - mutex_lock(&pd->lock); - if (pd_dvb->is_streaming) { - s32 i, ret, cmd_status = 0; - - pd_dvb->is_streaming = 0; - - for (i = 0; i < DVB_SBUF_NUM; i++) - if (pd_dvb->urb_array[i]) - usb_kill_urb(pd_dvb->urb_array[i]); - - ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, - &cmd_status); - if (ret | cmd_status) - log("error"); - } - mutex_unlock(&pd->lock); -} - -static int pd_start_feed(struct dvb_demux_feed *feed) -{ - struct pd_dvb_adapter *pd_dvb = feed->demux->priv; - int ret = 0; - - if (!pd_dvb) - return -1; - if (atomic_inc_return(&pd_dvb->active_feed) == 1) - ret = dvb_start_streaming(pd_dvb); - return ret; -} - -static int pd_stop_feed(struct dvb_demux_feed *feed) -{ - struct pd_dvb_adapter *pd_dvb = feed->demux->priv; - - if (!pd_dvb) - return -1; - if (atomic_dec_and_test(&pd_dvb->active_feed)) - dvb_stop_streaming(pd_dvb); - return 0; -} - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -int pd_dvb_usb_device_init(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - struct dvb_demux *dvbdemux; - int ret = 0; - - pd_dvb->ep_addr = 0x82; - atomic_set(&pd_dvb->users, 0); - atomic_set(&pd_dvb->active_feed, 0); - pd_dvb->pd_device = pd; - - ret = dvb_register_adapter(&pd_dvb->dvb_adap, - "Poseidon dvbt adapter", - THIS_MODULE, - NULL /* for hibernation correctly*/, - adapter_nr); - if (ret < 0) - goto error1; - - /* register frontend */ - pd_dvb->dvb_fe.demodulator_priv = pd; - memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops, - sizeof(struct dvb_frontend_ops)); - ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe); - if (ret < 0) - goto error2; - - /* register demux device */ - dvbdemux = &pd_dvb->demux; - dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; - dvbdemux->priv = pd_dvb; - dvbdemux->feednum = dvbdemux->filternum = 64; - dvbdemux->start_feed = pd_start_feed; - dvbdemux->stop_feed = pd_stop_feed; - dvbdemux->write_to_decoder = NULL; - - ret = dvb_dmx_init(dvbdemux); - if (ret < 0) - goto error3; - - pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum; - pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx; - pd_dvb->dmxdev.capabilities = 0; - - ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap); - if (ret < 0) - goto error3; - return 0; - -error3: - dvb_unregister_frontend(&pd_dvb->dvb_fe); -error2: - dvb_unregister_adapter(&pd_dvb->dvb_adap); -error1: - return ret; -} - -void pd_dvb_usb_device_exit(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - while (atomic_read(&pd_dvb->users) != 0 - || atomic_read(&pd_dvb->active_feed) != 0) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - } - dvb_dmxdev_release(&pd_dvb->dmxdev); - dvb_unregister_frontend(&pd_dvb->dvb_fe); - dvb_unregister_adapter(&pd_dvb->dvb_adap); - pd_dvb_usb_device_cleanup(pd); -} - -void pd_dvb_usb_device_cleanup(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - dvb_urb_cleanup(pd_dvb); -} - -int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb) -{ - return pd_dvb->dvb_adap.num; -} diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c deleted file mode 100644 index 7b1f6ebd0e2c..000000000000 --- a/drivers/media/video/tlg2300/pd-main.c +++ /dev/null @@ -1,536 +0,0 @@ -/* - * device driver for Telegent tlg2300 based TV cards - * - * Author : - * Kang Yong - * Zhang Xiaobing - * Huang Shijie or - * - * (c) 2009 Telegent Systems - * (c) 2010 Telegent Systems - * - * This program is free software; 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 -#include -#include -#include - -#include "vendorcmds.h" -#include "pd-common.h" - -#define VENDOR_ID 0x1B24 -#define PRODUCT_ID 0x4001 -static struct usb_device_id id_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) }, - { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -int debug_mode; -module_param(debug_mode, int, 0644); -MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); - -#define TLG2300_FIRMWARE "tlg2300_firmware.bin" -static const char *firmware_name = TLG2300_FIRMWARE; -static struct usb_driver poseidon_driver; -static LIST_HEAD(pd_device_list); - -/* - * send set request to USB firmware. - */ -s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status) -{ - s32 ret; - s8 data[32] = {}; - u16 lower_16, upper_16; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - mdelay(30); - - if (param == 0) { - upper_16 = lower_16 = 0; - } else { - /* send 32 bit param as two 16 bit param,little endian */ - lower_16 = (unsigned short)(param & 0xffff); - upper_16 = (unsigned short)((param >> 16) & 0xffff); - } - ret = usb_control_msg(pd->udev, - usb_rcvctrlpipe(pd->udev, 0), - REQ_SET_CMD | cmdid, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - lower_16, - upper_16, - &data, - sizeof(*cmd_status), - USB_CTRL_GET_TIMEOUT); - - if (!ret) { - return -ENXIO; - } else { - /* 1st 4 bytes into cmd_status */ - memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status)); - } - return 0; -} - -/* - * send get request to Poseidon firmware. - */ -s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param, - void *buf, s32 *cmd_status, s32 datalen) -{ - s32 ret; - s8 data[128] = {}; - u16 lower_16, upper_16; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - mdelay(30); - if (param == 0) { - upper_16 = lower_16 = 0; - } else { - /*send 32 bit param as two 16 bit param, little endian */ - lower_16 = (unsigned short)(param & 0xffff); - upper_16 = (unsigned short)((param >> 16) & 0xffff); - } - ret = usb_control_msg(pd->udev, - usb_rcvctrlpipe(pd->udev, 0), - REQ_GET_CMD | cmdid, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - lower_16, - upper_16, - &data, - (datalen + sizeof(*cmd_status)), - USB_CTRL_GET_TIMEOUT); - - if (ret < 0) { - return -ENXIO; - } else { - /* 1st 4 bytes into cmd_status, remaining data into cmd_data */ - memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status)); - memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen); - } - return 0; -} - -static int pm_notifier_block(struct notifier_block *nb, - unsigned long event, void *dummy) -{ - struct poseidon *pd = NULL; - struct list_head *node, *next; - - switch (event) { - case PM_POST_HIBERNATION: - list_for_each_safe(node, next, &pd_device_list) { - struct usb_device *udev; - struct usb_interface *iface; - int rc = 0; - - pd = container_of(node, struct poseidon, device_list); - udev = pd->udev; - iface = pd->interface; - - /* It will cause the system to reload the firmware */ - rc = usb_lock_device_for_reset(udev, iface); - if (rc >= 0) { - usb_reset_device(udev); - usb_unlock_device(udev); - } - } - break; - default: - break; - } - log("event :%ld\n", event); - return 0; -} - -static struct notifier_block pm_notifer = { - .notifier_call = pm_notifier_block, -}; - -int set_tuner_mode(struct poseidon *pd, unsigned char mode) -{ - s32 ret, cmd_status; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status); - if (ret || cmd_status) - return -ENXIO; - return 0; -} - -void poseidon_delete(struct kref *kref) -{ - struct poseidon *pd = container_of(kref, struct poseidon, kref); - - if (!pd) - return; - list_del_init(&pd->device_list); - - pd_dvb_usb_device_cleanup(pd); - /* clean_audio_data(&pd->audio_data);*/ - - if (pd->udev) { - usb_put_dev(pd->udev); - pd->udev = NULL; - } - if (pd->interface) { - usb_put_intf(pd->interface); - pd->interface = NULL; - } - kfree(pd); - log(); -} - -static int firmware_download(struct usb_device *udev) -{ - int ret = 0, actual_length; - const struct firmware *fw = NULL; - void *fwbuf = NULL; - size_t fwlength = 0, offset; - size_t max_packet_size; - - ret = request_firmware(&fw, firmware_name, &udev->dev); - if (ret) { - log("download err : %d", ret); - return ret; - } - - fwlength = fw->size; - - fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL); - if (!fwbuf) { - ret = -ENOMEM; - goto out; - } - - max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize; - log("\t\t download size : %d", (int)max_packet_size); - - for (offset = 0; offset < fwlength; offset += max_packet_size) { - actual_length = 0; - ret = usb_bulk_msg(udev, - usb_sndbulkpipe(udev, 0x01), /* ep 1 */ - fwbuf + offset, - min(max_packet_size, fwlength - offset), - &actual_length, - HZ * 10); - if (ret) - break; - } - kfree(fwbuf); -out: - release_firmware(fw); - return ret; -} - -static inline struct poseidon *get_pd(struct usb_interface *intf) -{ - return usb_get_intfdata(intf); -} - -#ifdef CONFIG_PM -/* one-to-one map : poseidon{} <----> usb_device{}'s port */ -static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) -{ - pd->portnum = udev->portnum; -} - -static inline int get_autopm_ref(struct poseidon *pd) -{ - return pd->video_data.users + pd->vbi_data.users + pd->audio.users - + atomic_read(&pd->dvb_data.users) + pd->radio_data.users; -} - -/* fixup something for poseidon */ -static inline struct poseidon *fixup(struct poseidon *pd) -{ - int count; - - /* old udev and interface have gone, so put back reference . */ - count = get_autopm_ref(pd); - log("count : %d, ref count : %d", count, get_pm_count(pd)); - while (count--) - usb_autopm_put_interface(pd->interface); - /*usb_autopm_set_interface(pd->interface); */ - - usb_put_dev(pd->udev); - usb_put_intf(pd->interface); - log("event : %d\n", pd->msg.event); - return pd; -} - -static struct poseidon *find_old_poseidon(struct usb_device *udev) -{ - struct poseidon *pd; - - list_for_each_entry(pd, &pd_device_list, device_list) { - if (pd->portnum == udev->portnum && in_hibernation(pd)) - return fixup(pd); - } - return NULL; -} - -/* Is the card working now ? */ -static inline int is_working(struct poseidon *pd) -{ - return get_pm_count(pd) > 0; -} - -static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg) -{ - struct poseidon *pd = get_pd(intf); - - if (!pd) - return 0; - if (!is_working(pd)) { - if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) { - pd->msg.event = PM_EVENT_AUTO_SUSPEND; - pd->pm_resume = NULL; /* a good guard */ - printk(KERN_DEBUG "\n\t+ TLG2300 auto suspend +\n\n"); - } - return 0; - } - pd->msg = msg; /* save it here */ - logpm(pd); - return pd->pm_suspend ? pd->pm_suspend(pd) : 0; -} - -static int poseidon_resume(struct usb_interface *intf) -{ - struct poseidon *pd = get_pd(intf); - - if (!pd) - return 0; - printk(KERN_DEBUG "\n\t ++ TLG2300 resume ++\n\n"); - - if (!is_working(pd)) { - if (PM_EVENT_AUTO_SUSPEND == pd->msg.event) - pd->msg = PMSG_ON; - return 0; - } - if (in_hibernation(pd)) { - logpm(pd); - return 0; - } - logpm(pd); - return pd->pm_resume ? pd->pm_resume(pd) : 0; -} - -static void hibernation_resume(struct work_struct *w) -{ - struct poseidon *pd = container_of(w, struct poseidon, pm_work); - int count; - - pd->msg.event = 0; /* clear it here */ - pd->state &= ~POSEIDON_STATE_DISCONNECT; - - /* set the new interface's reference */ - count = get_autopm_ref(pd); - while (count--) - usb_autopm_get_interface(pd->interface); - - /* resume the context */ - logpm(pd); - if (pd->pm_resume) - pd->pm_resume(pd); -} -#else /* CONFIG_PM is not enabled: */ -static inline struct poseidon *find_old_poseidon(struct usb_device *udev) -{ - return NULL; -} - -static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) -{ -} -#endif - -static int check_firmware(struct usb_device *udev, int *down_firmware) -{ - void *buf; - int ret; - struct cmd_firmware_vers_s *cmd_firm; - - buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL); - if (!buf) - return -ENOMEM; - ret = usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - REQ_GET_CMD | GET_FW_ID, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - 0, - buf, - sizeof(*cmd_firm) + sizeof(u32), - USB_CTRL_GET_TIMEOUT); - kfree(buf); - - if (ret < 0) { - *down_firmware = 1; - return firmware_download(udev); - } - return 0; -} - -static int poseidon_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(interface); - struct poseidon *pd = NULL; - int ret = 0; - int new_one = 0; - - /* download firmware */ - check_firmware(udev, &ret); - if (ret) - return 0; - - /* Do I recovery from the hibernate ? */ - pd = find_old_poseidon(udev); - if (!pd) { - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - kref_init(&pd->kref); - set_map_flags(pd, udev); - new_one = 1; - } - - pd->udev = usb_get_dev(udev); - pd->interface = usb_get_intf(interface); - usb_set_intfdata(interface, pd); - - if (new_one) { - struct device *dev = &interface->dev; - - logpm(pd); - mutex_init(&pd->lock); - - /* register v4l2 device */ - snprintf(pd->v4l2_dev.name, sizeof(pd->v4l2_dev.name), "%s %s", - dev->driver->name, dev_name(dev)); - ret = v4l2_device_register(NULL, &pd->v4l2_dev); - - /* register devices in directory /dev */ - ret = pd_video_init(pd); - poseidon_audio_init(pd); - poseidon_fm_init(pd); - pd_dvb_usb_device_init(pd); - - INIT_LIST_HEAD(&pd->device_list); - list_add_tail(&pd->device_list, &pd_device_list); - } - - device_init_wakeup(&udev->dev, 1); -#ifdef CONFIG_PM - pm_runtime_set_autosuspend_delay(&pd->udev->dev, - 1000 * PM_SUSPEND_DELAY); - usb_enable_autosuspend(pd->udev); - - if (in_hibernation(pd)) { - INIT_WORK(&pd->pm_work, hibernation_resume); - schedule_work(&pd->pm_work); - } -#endif - return 0; -} - -static void poseidon_disconnect(struct usb_interface *interface) -{ - struct poseidon *pd = get_pd(interface); - - if (!pd) - return; - logpm(pd); - if (in_hibernation(pd)) - return; - - mutex_lock(&pd->lock); - pd->state |= POSEIDON_STATE_DISCONNECT; - mutex_unlock(&pd->lock); - - /* stop urb transferring */ - stop_all_video_stream(pd); - dvb_stop_streaming(&pd->dvb_data); - - /*unregister v4l2 device */ - v4l2_device_unregister(&pd->v4l2_dev); - - pd_dvb_usb_device_exit(pd); - poseidon_fm_exit(pd); - - poseidon_audio_free(pd); - pd_video_exit(pd); - - usb_set_intfdata(interface, NULL); - kref_put(&pd->kref, poseidon_delete); -} - -static struct usb_driver poseidon_driver = { - .name = "poseidon", - .probe = poseidon_probe, - .disconnect = poseidon_disconnect, - .id_table = id_table, -#ifdef CONFIG_PM - .suspend = poseidon_suspend, - .resume = poseidon_resume, -#endif - .supports_autosuspend = 1, -}; - -static int __init poseidon_init(void) -{ - int ret; - - ret = usb_register(&poseidon_driver); - if (ret) - return ret; - register_pm_notifier(&pm_notifer); - return ret; -} - -static void __exit poseidon_exit(void) -{ - log(); - unregister_pm_notifier(&pm_notifer); - usb_deregister(&poseidon_driver); -} - -module_init(poseidon_init); -module_exit(poseidon_exit); - -MODULE_AUTHOR("Telegent Systems"); -MODULE_DESCRIPTION("For tlg2300-based USB device "); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.2"); -MODULE_FIRMWARE(TLG2300_FIRMWARE); diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c deleted file mode 100644 index 4fad1dfb92cf..000000000000 --- a/drivers/media/video/tlg2300/pd-radio.c +++ /dev/null @@ -1,421 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pd-common.h" -#include "vendorcmds.h" - -static int set_frequency(struct poseidon *p, __u32 frequency); -static int poseidon_fm_close(struct file *filp); -static int poseidon_fm_open(struct file *filp); - -#define TUNER_FREQ_MIN_FM 76000000 -#define TUNER_FREQ_MAX_FM 108000000 - -#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1) -static int preemphasis[MAX_PREEMPHASIS] = { - TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */ - TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */ - TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */ -}; - -static int poseidon_check_mode_radio(struct poseidon *p) -{ - int ret; - u32 status; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/2); - ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE); - if (ret < 0) - goto out; - - ret = set_tuner_mode(p, TLG_MODE_FM_RADIO); - if (ret != 0) - goto out; - - ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status); - ret = send_set_req(p, TUNER_AUD_ANA_STD, - p->radio_data.pre_emphasis, &status); - ret |= send_set_req(p, TUNER_AUD_MODE, - TLG_TUNE_TVAUDIO_MODE_STEREO, &status); - ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL, - ATV_AUDIO_RATE_48K, &status); - ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status); -out: - return ret; -} - -#ifdef CONFIG_PM -static int pm_fm_suspend(struct poseidon *p) -{ - logpm(p); - pm_alsa_suspend(p); - usb_set_interface(p->udev, 0, 0); - msleep(300); - return 0; -} - -static int pm_fm_resume(struct poseidon *p) -{ - logpm(p); - poseidon_check_mode_radio(p); - set_frequency(p, p->radio_data.fm_freq); - pm_alsa_resume(p); - return 0; -} -#endif - -static int poseidon_fm_open(struct file *filp) -{ - struct video_device *vfd = video_devdata(filp); - struct poseidon *p = video_get_drvdata(vfd); - int ret = 0; - - if (!p) - return -1; - - mutex_lock(&p->lock); - if (p->state & POSEIDON_STATE_DISCONNECT) { - ret = -ENODEV; - goto out; - } - - if (p->state && !(p->state & POSEIDON_STATE_FM)) { - ret = -EBUSY; - goto out; - } - - usb_autopm_get_interface(p->interface); - if (0 == p->state) { - /* default pre-emphasis */ - if (p->radio_data.pre_emphasis == 0) - p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR; - set_debug_mode(vfd, debug_mode); - - ret = poseidon_check_mode_radio(p); - if (ret < 0) { - usb_autopm_put_interface(p->interface); - goto out; - } - p->state |= POSEIDON_STATE_FM; - } - p->radio_data.users++; - kref_get(&p->kref); - filp->private_data = p; -out: - mutex_unlock(&p->lock); - return ret; -} - -static int poseidon_fm_close(struct file *filp) -{ - struct poseidon *p = filp->private_data; - struct radio_data *fm = &p->radio_data; - uint32_t status; - - mutex_lock(&p->lock); - fm->users--; - if (0 == fm->users) - p->state &= ~POSEIDON_STATE_FM; - - if (fm->is_radio_streaming && filp == p->file_for_stream) { - fm->is_radio_streaming = 0; - send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status); - } - usb_autopm_put_interface(p->interface); - mutex_unlock(&p->lock); - - kref_put(&p->kref, poseidon_delete); - filp->private_data = NULL; - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - struct poseidon *p = file->private_data; - - strlcpy(v->driver, "tele-radio", sizeof(v->driver)); - strlcpy(v->card, "Telegent Poseidon", sizeof(v->card)); - usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static const struct v4l2_file_operations poseidon_fm_fops = { - .owner = THIS_MODULE, - .open = poseidon_fm_open, - .release = poseidon_fm_close, - .ioctl = video_ioctl2, -}; - -static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *vt) -{ - struct tuner_fm_sig_stat_s fm_stat = {}; - int ret, status, count = 5; - struct poseidon *p = file->private_data; - - if (vt->index != 0) - return -EINVAL; - - vt->type = V4L2_TUNER_RADIO; - vt->capability = V4L2_TUNER_CAP_STEREO; - vt->rangelow = TUNER_FREQ_MIN_FM / 62500; - vt->rangehigh = TUNER_FREQ_MAX_FM / 62500; - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - vt->audmode = V4L2_TUNER_MODE_STEREO; - vt->signal = 0; - vt->afc = 0; - - mutex_lock(&p->lock); - ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, - &fm_stat, &status, sizeof(fm_stat)); - - while (fm_stat.sig_lock_busy && count-- && !ret) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - - ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, - &fm_stat, &status, sizeof(fm_stat)); - } - mutex_unlock(&p->lock); - - if (ret || status) { - vt->signal = 0; - } else if ((fm_stat.sig_present || fm_stat.sig_locked) - && fm_stat.sig_strength == 0) { - vt->signal = 0xffff; - } else - vt->signal = (fm_stat.sig_strength * 255 / 10) << 8; - - return 0; -} - -static int fm_get_freq(struct file *file, void *priv, - struct v4l2_frequency *argp) -{ - struct poseidon *p = file->private_data; - - argp->frequency = p->radio_data.fm_freq; - return 0; -} - -static int set_frequency(struct poseidon *p, __u32 frequency) -{ - __u32 freq ; - int ret, status; - - mutex_lock(&p->lock); - - ret = send_set_req(p, TUNER_AUD_ANA_STD, - p->radio_data.pre_emphasis, &status); - - freq = (frequency * 125) * 500 / 1000;/* kHZ */ - if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) { - ret = -EINVAL; - goto error; - } - - ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status); - if (ret < 0) - goto error ; - ret = send_set_req(p, TAKE_REQUEST, 0, &status); - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - if (!p->radio_data.is_radio_streaming) { - ret = send_set_req(p, TAKE_REQUEST, 0, &status); - ret = send_set_req(p, PLAY_SERVICE, - TLG_TUNE_PLAY_SVC_START, &status); - p->radio_data.is_radio_streaming = 1; - } - p->radio_data.fm_freq = frequency; -error: - mutex_unlock(&p->lock); - return ret; -} - -static int fm_set_freq(struct file *file, void *priv, - struct v4l2_frequency *argp) -{ - struct poseidon *p = file->private_data; - - p->file_for_stream = file; -#ifdef CONFIG_PM - p->pm_suspend = pm_fm_suspend; - p->pm_resume = pm_fm_resume; -#endif - return set_frequency(p, argp->frequency); -} - -static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *arg) -{ - return 0; -} - -static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, - struct v4l2_ext_controls *ctrls) -{ - struct poseidon *p = file->private_data; - int i; - - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) - return -EINVAL; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) - continue; - - if (i < MAX_PREEMPHASIS) - ctrl->value = p->radio_data.pre_emphasis; - } - return 0; -} - -static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, - struct v4l2_ext_controls *ctrls) -{ - int i; - - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) - return -EINVAL; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) - continue; - - if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) { - struct poseidon *p = file->private_data; - int pre_emphasis = preemphasis[ctrl->value]; - u32 status; - - send_set_req(p, TUNER_AUD_ANA_STD, - pre_emphasis, &status); - p->radio_data.pre_emphasis = pre_emphasis; - } - } - return 0; -} - -static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - return 0; -} - -static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *ctrl) -{ - if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) - return -EINVAL; - - ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; - if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) { - /* return the next supported control */ - ctrl->id = V4L2_CID_TUNE_PREEMPHASIS; - v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED, - V4L2_PREEMPHASIS_75_uS, 1, - V4L2_PREEMPHASIS_50_uS); - ctrl->flags = V4L2_CTRL_FLAG_UPDATE; - return 0; - } - return -EINVAL; -} - -static int tlg_fm_vidioc_querymenu(struct file *file, void *fh, - struct v4l2_querymenu *qmenu) -{ - return v4l2_ctrl_query_menu(qmenu, NULL, NULL); -} - -static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) -{ - return vt->index > 0 ? -EINVAL : 0; -} -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va) -{ - return (va->index != 0) ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - a->index = 0; - a->mode = 0; - a->capability = V4L2_AUDCAP_STEREO; - strcpy(a->name, "Radio"); - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, u32 i) -{ - return (i != 0) ? -EINVAL : 0; -} - -static int vidioc_g_input(struct file *filp, void *priv, u32 *i) -{ - return (*i != 0) ? -EINVAL : 0; -} - -static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = tlg_fm_vidioc_queryctrl, - .vidioc_querymenu = tlg_fm_vidioc_querymenu, - .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl, - .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl, - .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl, - .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_tuner = tlg_fm_vidioc_g_tuner, - .vidioc_g_frequency = fm_get_freq, - .vidioc_s_frequency = fm_set_freq, -}; - -static struct video_device poseidon_fm_template = { - .name = "Telegent-Radio", - .fops = &poseidon_fm_fops, - .minor = -1, - .release = video_device_release, - .ioctl_ops = &poseidon_fm_ioctl_ops, -}; - -int poseidon_fm_init(struct poseidon *p) -{ - struct video_device *fm_dev; - - fm_dev = vdev_init(p, &poseidon_fm_template); - if (fm_dev == NULL) - return -1; - - if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) { - video_device_release(fm_dev); - return -1; - } - p->radio_data.fm_dev = fm_dev; - return 0; -} - -int poseidon_fm_exit(struct poseidon *p) -{ - destroy_video_device(&p->radio_data.fm_dev); - return 0; -} diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c deleted file mode 100644 index bfbf9e56b0a4..000000000000 --- a/drivers/media/video/tlg2300/pd-video.c +++ /dev/null @@ -1,1668 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "pd-common.h" -#include "vendorcmds.h" - -#ifdef CONFIG_PM -static int pm_video_suspend(struct poseidon *pd); -static int pm_video_resume(struct poseidon *pd); -#endif -static void iso_bubble_handler(struct work_struct *w); - -static int usb_transfer_mode; -module_param(usb_transfer_mode, int, 0644); -MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous"); - -static const struct poseidon_format poseidon_formats[] = { - { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0}, - { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0}, -}; - -static const struct poseidon_tvnorm poseidon_tvnorms[] = { - { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D }, - { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B }, - { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G }, - { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H }, - { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I }, - { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M }, - { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO }, - { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO }, - { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M }, - { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J }, - { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B }, - { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D }, - { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G }, - { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H }, - { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K }, - { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 }, - { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L }, - { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 }, -}; -static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms); - -struct pd_audio_mode { - u32 tlg_audio_mode; - u32 v4l2_audio_sub; - u32 v4l2_audio_mode; -}; - -static const struct pd_audio_mode pd_audio_modes[] = { - { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO, - V4L2_TUNER_MODE_MONO }, - { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO, - V4L2_TUNER_MODE_STEREO }, - { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1, - V4L2_TUNER_MODE_LANG1 }, - { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2, - V4L2_TUNER_MODE_LANG2 }, - { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1, - V4L2_TUNER_MODE_LANG1_LANG2 } -}; -static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes); - -struct pd_input { - char *name; - uint32_t tlg_src; -}; - -static const struct pd_input pd_inputs[] = { - { "TV Antenna", TLG_SIG_SRC_ANTENNA }, - { "TV Cable", TLG_SIG_SRC_CABLE }, - { "TV SVideo", TLG_SIG_SRC_SVIDEO }, - { "TV Composite", TLG_SIG_SRC_COMPOSITE } -}; -static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs); - -struct poseidon_control { - struct v4l2_queryctrl v4l2_ctrl; - enum cmd_custom_param_id vc_id; -}; - -static struct poseidon_control controls[] = { - { - { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, - "brightness", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_BRIGHTNESS_CTRL - }, { - { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, - "contrast", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_CONTRAST_CTRL, - }, { - { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER, - "hue", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_HUE_CTRL, - }, { - { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER, - "saturation", 0, 10000, 1, 100, 0, }, - CUST_PARM_ID_SATURATION_CTRL, - }, -}; - -struct video_std_to_audio_std { - v4l2_std_id video_std; - int audio_std; -}; - -static const struct video_std_to_audio_std video_to_audio_map[] = { - /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64, - 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */ - { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D | - V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM }, - - /* country : { 1, 52, 54, 55, 886 } */ - {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC}, - - /* country : { 81 } */ - { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ }, - - /* other country : TLG_TUNE_ASTD_A2 */ -}; -static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map); - -static int get_audio_std(v4l2_std_id v4l2_std) -{ - int i = 0; - - for (; i < map_size; i++) { - if (v4l2_std & video_to_audio_map[i].video_std) - return video_to_audio_map[i].audio_std; - } - return TLG_TUNE_ASTD_A2; -} - -static int vidioc_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct front_face *front = fh; - struct poseidon *p = front->pd; - - logs(front); - - strcpy(cap->driver, "tele-video"); - strcpy(cap->card, "Telegent Poseidon"); - usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | - V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; - return 0; -} - -/*====================================================================*/ -static void init_copy(struct video_data *video, bool index) -{ - struct front_face *front = video->front; - - video->field_count = index; - video->lines_copied = 0; - video->prev_left = 0 ; - video->dst = (char *)videobuf_to_vmalloc(front->curr_frame) - + index * video->lines_size; - video->vbi->copied = 0; /* set it here */ -} - -static bool get_frame(struct front_face *front, int *need_init) -{ - struct videobuf_buffer *vb = front->curr_frame; - - if (vb) - return true; - - spin_lock(&front->queue_lock); - if (!list_empty(&front->active)) { - vb = list_entry(front->active.next, - struct videobuf_buffer, queue); - if (need_init) - *need_init = 1; - front->curr_frame = vb; - list_del_init(&vb->queue); - } - spin_unlock(&front->queue_lock); - - return !!vb; -} - -/* check if the video's buffer is ready */ -static bool get_video_frame(struct front_face *front, struct video_data *video) -{ - int need_init = 0; - bool ret = true; - - ret = get_frame(front, &need_init); - if (ret && need_init) - init_copy(video, 0); - return ret; -} - -static void submit_frame(struct front_face *front) -{ - struct videobuf_buffer *vb = front->curr_frame; - - if (vb == NULL) - return; - - front->curr_frame = NULL; - vb->state = VIDEOBUF_DONE; - vb->field_count++; - do_gettimeofday(&vb->ts); - - wake_up(&vb->done); -} - -/* - * A frame is composed of two fields. If we receive all the two fields, - * call the submit_frame() to submit the whole frame to applications. - */ -static void end_field(struct video_data *video) -{ - /* logs(video->front); */ - if (1 == video->field_count) - submit_frame(video->front); - else - init_copy(video, 1); -} - -static void copy_video_data(struct video_data *video, char *src, - unsigned int count) -{ -#define copy_data(len) \ - do { \ - if (++video->lines_copied > video->lines_per_field) \ - goto overflow; \ - memcpy(video->dst, src, len);\ - video->dst += len + video->lines_size; \ - src += len; \ - count -= len; \ - } while (0) - - while (count && count >= video->lines_size) { - if (video->prev_left) { - copy_data(video->prev_left); - video->prev_left = 0; - continue; - } - copy_data(video->lines_size); - } - if (count && count < video->lines_size) { - memcpy(video->dst, src, count); - - video->prev_left = video->lines_size - count; - video->dst += count; - } - return; - -overflow: - end_field(video); -} - -static void check_trailer(struct video_data *video, char *src, int count) -{ - struct vbi_data *vbi = video->vbi; - int offset; /* trailer's offset */ - char *buf; - - offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2) - - (vbi->copied + video->lines_size * video->lines_copied); - if (video->prev_left) - offset -= (video->lines_size - video->prev_left); - - if (offset > count || offset <= 0) - goto short_package; - - buf = src + offset; - - /* trailer : (VFHS) + U32 + U32 + field_num */ - if (!strncmp(buf, "VFHS", 4)) { - int field_num = *((u32 *)(buf + 12)); - - if ((field_num & 1) ^ video->field_count) { - init_copy(video, video->field_count); - return; - } - copy_video_data(video, src, offset); - } -short_package: - end_field(video); -} - -/* ========== Check this more carefully! =========== */ -static inline void copy_vbi_data(struct vbi_data *vbi, - char *src, unsigned int count) -{ - struct front_face *front = vbi->front; - - if (front && get_frame(front, NULL)) { - char *buf = videobuf_to_vmalloc(front->curr_frame); - - if (vbi->video->field_count) - buf += (vbi->vbi_size / 2); - memcpy(buf + vbi->copied, src, count); - } - vbi->copied += count; -} - -/* - * Copy the normal data (VBI or VIDEO) without the trailer. - * VBI is not interlaced, while VIDEO is interlaced. - */ -static inline void copy_vbi_video_data(struct video_data *video, - char *src, unsigned int count) -{ - struct vbi_data *vbi = video->vbi; - unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied; - - if (vbi_delta >= count) { - copy_vbi_data(vbi, src, count); - } else { - if (vbi_delta) { - copy_vbi_data(vbi, src, vbi_delta); - - /* we receive the two fields of the VBI*/ - if (vbi->front && video->field_count) - submit_frame(vbi->front); - } - copy_video_data(video, src + vbi_delta, count - vbi_delta); - } -} - -static void urb_complete_bulk(struct urb *urb) -{ - struct front_face *front = urb->context; - struct video_data *video = &front->pd->video_data; - char *src = (char *)urb->transfer_buffer; - int count = urb->actual_length; - int ret = 0; - - if (!video->is_streaming || urb->status) { - if (urb->status == -EPROTO) - goto resend_it; - return; - } - if (!get_video_frame(front, video)) - goto resend_it; - - if (count == urb->transfer_buffer_length) - copy_vbi_video_data(video, src, count); - else - check_trailer(video, src, count); - -resend_it: - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - log(" submit failed: error %d", ret); -} - -/************************* for ISO *********************/ -#define GET_SUCCESS (0) -#define GET_TRAILER (1) -#define GET_TOO_MUCH_BUBBLE (2) -#define GET_NONE (3) -static int get_chunk(int start, struct urb *urb, - int *head, int *tail, int *bubble_err) -{ - struct usb_iso_packet_descriptor *pkt = NULL; - int ret = GET_SUCCESS; - - for (*head = *tail = -1; start < urb->number_of_packets; start++) { - pkt = &urb->iso_frame_desc[start]; - - /* handle the bubble of the Hub */ - if (-EOVERFLOW == pkt->status) { - if (++*bubble_err > urb->number_of_packets / 3) - return GET_TOO_MUCH_BUBBLE; - continue; - } - - /* This is the gap */ - if (pkt->status || pkt->actual_length <= 0 - || pkt->actual_length > ISO_PKT_SIZE) { - if (*head != -1) - break; - continue; - } - - /* a good isochronous packet */ - if (pkt->actual_length == ISO_PKT_SIZE) { - if (*head == -1) - *head = start; - *tail = start; - continue; - } - - /* trailer is here */ - if (pkt->actual_length < ISO_PKT_SIZE) { - if (*head == -1) { - *head = start; - *tail = start; - return GET_TRAILER; - } - break; - } - } - - if (*head == -1 && *tail == -1) - ret = GET_NONE; - return ret; -} - -/* - * |__|------|___|-----|_______| - * ^ ^ - * | | - * gap gap - */ -static void urb_complete_iso(struct urb *urb) -{ - struct front_face *front = urb->context; - struct video_data *video = &front->pd->video_data; - int bubble_err = 0, head = 0, tail = 0; - char *src = (char *)urb->transfer_buffer; - int ret = 0; - - if (!video->is_streaming) - return; - - do { - if (!get_video_frame(front, video)) - goto out; - - switch (get_chunk(head, urb, &head, &tail, &bubble_err)) { - case GET_SUCCESS: - copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE), - (tail - head + 1) * ISO_PKT_SIZE); - break; - case GET_TRAILER: - check_trailer(video, src + (head * ISO_PKT_SIZE), - ISO_PKT_SIZE); - break; - case GET_NONE: - goto out; - case GET_TOO_MUCH_BUBBLE: - log("\t We got too much bubble"); - schedule_work(&video->bubble_work); - return; - } - } while (head = tail + 1, head < urb->number_of_packets); - -out: - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - log("usb_submit_urb err : %d", ret); -} -/*============================= [ end ] =====================*/ - -static int prepare_iso_urb(struct video_data *video) -{ - struct usb_device *udev = video->pd->udev; - int i; - - if (video->urb_array[0]) - return 0; - - for (i = 0; i < SBUF_NUM; i++) { - struct urb *urb; - void *mem; - int j; - - urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL); - if (urb == NULL) - goto out; - - video->urb_array[i] = urb; - mem = usb_alloc_coherent(udev, - ISO_PKT_SIZE * PK_PER_URB, - GFP_KERNEL, - &urb->transfer_dma); - - urb->complete = urb_complete_iso; /* handler */ - urb->dev = udev; - urb->context = video->front; - urb->pipe = usb_rcvisocpipe(udev, - video->endpoint_addr); - urb->interval = 1; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->number_of_packets = PK_PER_URB; - urb->transfer_buffer = mem; - urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE; - - for (j = 0; j < PK_PER_URB; j++) { - urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j; - urb->iso_frame_desc[j].length = ISO_PKT_SIZE; - } - } - return 0; -out: - for (; i > 0; i--) - ; - return -ENOMEM; -} - -/* return the succeeded number of the allocation */ -int alloc_bulk_urbs_generic(struct urb **urb_array, int num, - struct usb_device *udev, u8 ep_addr, - int buf_size, gfp_t gfp_flags, - usb_complete_t complete_fn, void *context) -{ - int i = 0; - - for (; i < num; i++) { - void *mem; - struct urb *urb = usb_alloc_urb(0, gfp_flags); - if (urb == NULL) - return i; - - mem = usb_alloc_coherent(udev, buf_size, gfp_flags, - &urb->transfer_dma); - if (mem == NULL) { - usb_free_urb(urb); - return i; - } - - usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr), - mem, buf_size, complete_fn, context); - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - urb_array[i] = urb; - } - return i; -} - -void free_all_urb_generic(struct urb **urb_array, int num) -{ - int i; - struct urb *urb; - - for (i = 0; i < num; i++) { - urb = urb_array[i]; - if (urb) { - usb_free_coherent(urb->dev, - urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); - usb_free_urb(urb); - urb_array[i] = NULL; - } - } -} - -static int prepare_bulk_urb(struct video_data *video) -{ - if (video->urb_array[0]) - return 0; - - alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM, - video->pd->udev, video->endpoint_addr, - 0x2000, GFP_KERNEL, - urb_complete_bulk, video->front); - return 0; -} - -/* free the URBs */ -static void free_all_urb(struct video_data *video) -{ - free_all_urb_generic(video->urb_array, SBUF_NUM); -} - -static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - videobuf_vmalloc_free(vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct front_face *front = q->priv_data; - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &front->active); -} - -static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct front_face *front = q->priv_data; - int rc; - - switch (front->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (VIDEOBUF_NEEDS_INIT == vb->state) { - struct v4l2_pix_format *pix; - - pix = &front->pd->video_data.context.pix; - vb->size = pix->sizeimage; /* real frame size */ - vb->width = pix->width; - vb->height = pix->height; - rc = videobuf_iolock(q, vb, NULL); - if (rc < 0) - return rc; - } - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (VIDEOBUF_NEEDS_INIT == vb->state) { - vb->size = front->pd->vbi_data.vbi_size; - rc = videobuf_iolock(q, vb, NULL); - if (rc < 0) - return rc; - } - break; - default: - return -EINVAL; - } - vb->field = field; - vb->state = VIDEOBUF_PREPARED; - return 0; -} - -static int fire_all_urb(struct video_data *video) -{ - int i, ret; - - video->is_streaming = 1; - - for (i = 0; i < SBUF_NUM; i++) { - ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL); - if (ret) - log("(%d) failed: error %d", i, ret); - } - return ret; -} - -static int start_video_stream(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - s32 cmd_status; - - send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status); - - if (pd->cur_transfer_mode) { - prepare_iso_urb(video); - INIT_WORK(&video->bubble_work, iso_bubble_handler); - } else { - /* The bulk mode does not need a bubble handler */ - prepare_bulk_urb(video); - } - fire_all_urb(video); - return 0; -} - -static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count, - unsigned int *size) -{ - struct front_face *front = q->priv_data; - struct poseidon *pd = front->pd; - - switch (front->type) { - default: - return -EINVAL; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct video_data *video = &pd->video_data; - struct v4l2_pix_format *pix = &video->context.pix; - - *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */ - if (*count < 4) - *count = 4; - if (1) { - /* same in different altersetting */ - video->endpoint_addr = 0x82; - video->vbi = &pd->vbi_data; - video->vbi->video = video; - video->pd = pd; - video->lines_per_field = pix->height / 2; - video->lines_size = pix->width * 2; - video->front = front; - } - return start_video_stream(pd); - } - - case V4L2_BUF_TYPE_VBI_CAPTURE: { - struct vbi_data *vbi = &pd->vbi_data; - - *size = PAGE_ALIGN(vbi->vbi_size); - log("size : %d", *size); - if (*count == 0) - *count = 4; - } - break; - } - return 0; -} - -static struct videobuf_queue_ops pd_video_qops = { - .buf_setup = pd_buf_setup, - .buf_prepare = pd_buf_prepare, - .buf_queue = pd_buf_queue, - .buf_release = pd_buf_release, -}; - -static int vidioc_enum_fmt(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (ARRAY_SIZE(poseidon_formats) <= f->index) - return -EINVAL; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = 0; - f->pixelformat = poseidon_formats[f->index].fourcc; - strcpy(f->description, poseidon_formats[f->index].name); - return 0; -} - -static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - - logs(front); - f->fmt.pix = pd->video_data.context.pix; - return 0; -} - -static int vidioc_try_fmt(struct file *file, void *fh, - struct v4l2_format *f) -{ - return 0; -} - -/* - * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while - * Mplayer calls them in the reverse order. - */ -static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) -{ - struct video_data *video = &pd->video_data; - struct running_context *context = &video->context; - struct v4l2_pix_format *pix_def = &context->pix; - s32 ret = 0, cmd_status = 0, vid_resol; - - /* set the pixel format to firmware */ - if (pix->pixelformat == V4L2_PIX_FMT_RGB565) { - vid_resol = TLG_TUNER_VID_FORMAT_RGB_565; - } else { - pix->pixelformat = V4L2_PIX_FMT_YUYV; - vid_resol = TLG_TUNER_VID_FORMAT_YUV; - } - ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL, - vid_resol, &cmd_status); - - /* set the resolution to firmware */ - vid_resol = TLG_TUNE_VID_RES_720; - switch (pix->width) { - case 704: - vid_resol = TLG_TUNE_VID_RES_704; - break; - default: - pix->width = 720; - case 720: - break; - } - ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, - vid_resol, &cmd_status); - if (ret || cmd_status) - return -EBUSY; - - pix_def->pixelformat = pix->pixelformat; /* save it */ - pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; - - /* Compare with the default setting */ - if ((pix_def->width != pix->width) - || (pix_def->height != pix->height)) { - pix_def->width = pix->width; - pix_def->height = pix->height; - pix_def->bytesperline = pix->width * 2; - pix_def->sizeimage = pix->width * pix->height * 2; - } - *pix = *pix_def; - - return 0; -} - -static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - - logs(front); - /* stop VBI here */ - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) - return -EINVAL; - - mutex_lock(&pd->lock); - if (pd->file_for_stream == NULL) - pd->file_for_stream = file; - else if (file != pd->file_for_stream) { - mutex_unlock(&pd->lock); - return -EINVAL; - } - - pd_vidioc_s_fmt(pd, &f->fmt.pix); - mutex_unlock(&pd->lock); - return 0; -} - -static int vidioc_g_fmt_vbi(struct file *file, void *fh, - struct v4l2_format *v4l2_f) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi; - - vbi_fmt->samples_per_line = 720 * 2; - vbi_fmt->sampling_rate = 6750000 * 4; - vbi_fmt->sample_format = V4L2_PIX_FMT_GREY; - vbi_fmt->offset = 64 * 4; /*FIXME: why offset */ - if (pd->video_data.context.tvnormid & V4L2_STD_525_60) { - vbi_fmt->start[0] = 10; - vbi_fmt->start[1] = 264; - vbi_fmt->count[0] = V4L_NTSC_VBI_LINES; - vbi_fmt->count[1] = V4L_NTSC_VBI_LINES; - } else { - vbi_fmt->start[0] = 6; - vbi_fmt->start[1] = 314; - vbi_fmt->count[0] = V4L_PAL_VBI_LINES; - vbi_fmt->count[1] = V4L_PAL_VBI_LINES; - } - vbi_fmt->flags = V4L2_VBI_UNSYNC; - logs(front); - return 0; -} - -static int set_std(struct poseidon *pd, v4l2_std_id *norm) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - struct running_context *context; - struct v4l2_pix_format *pix; - s32 i, ret = 0, cmd_status, param; - int height; - - for (i = 0; i < POSEIDON_TVNORMS; i++) { - if (*norm & poseidon_tvnorms[i].v4l2_id) { - param = poseidon_tvnorms[i].tlg_tvnorm; - log("name : %s", poseidon_tvnorms[i].name); - goto found; - } - } - return -EINVAL; -found: - mutex_lock(&pd->lock); - ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status); - if (ret || cmd_status) - goto out; - - /* Set vbi size and check the height of the frame */ - context = &video->context; - context->tvnormid = poseidon_tvnorms[i].v4l2_id; - if (context->tvnormid & V4L2_STD_525_60) { - vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE; - height = 480; - } else { - vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE; - height = 576; - } - - pix = &context->pix; - if (pix->height != height) { - pix->height = height; - pix->sizeimage = pix->width * pix->height * 2; - } - -out: - mutex_unlock(&pd->lock); - return ret; -} - -static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - struct front_face *front = fh; - logs(front); - return set_std(front->pd, norm); -} - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in) -{ - struct front_face *front = fh; - - if (in->index < 0 || in->index >= POSEIDON_INPUTS) - return -EINVAL; - strcpy(in->name, pd_inputs[in->index].name); - in->type = V4L2_INPUT_TYPE_TUNER; - - /* - * the audio input index mixed with this video input, - * Poseidon only have one audio/video, set to "0" - */ - in->audioset = 0; - in->tuner = 0; - in->std = V4L2_STD_ALL; - in->status = 0; - logs(front); - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct running_context *context = &pd->video_data.context; - - logs(front); - *i = context->sig_index; - return 0; -} - -/* We can support several inputs */ -static int vidioc_s_input(struct file *file, void *fh, unsigned int i) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - s32 ret, cmd_status; - - if (i < 0 || i >= POSEIDON_INPUTS) - return -EINVAL; - ret = send_set_req(pd, SGNL_SRC_SEL, - pd_inputs[i].tlg_src, &cmd_status); - if (ret) - return ret; - - pd->video_data.context.sig_index = i; - return 0; -} - -static struct poseidon_control *check_control_id(__u32 id) -{ - struct poseidon_control *control = &controls[0]; - int array_size = ARRAY_SIZE(controls); - - for (; control < &controls[array_size]; control++) - if (control->v4l2_ctrl.id == id) - return control; - return NULL; -} - -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *a) -{ - struct poseidon_control *control = NULL; - - control = check_control_id(a->id); - if (!control) - return -EINVAL; - - *a = control->v4l2_ctrl; - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct poseidon_control *control = NULL; - struct tuner_custom_parameter_s tuner_param; - s32 ret = 0, cmd_status; - - control = check_control_id(ctrl->id); - if (!control) - return -EINVAL; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id, - &tuner_param, &cmd_status, sizeof(tuner_param)); - mutex_unlock(&pd->lock); - - if (ret || cmd_status) - return -1; - - ctrl->value = tuner_param.param_value; - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) -{ - struct tuner_custom_parameter_s param = {0}; - struct poseidon_control *control = NULL; - struct front_face *front = fh; - struct poseidon *pd = front->pd; - s32 ret = 0, cmd_status, params; - - control = check_control_id(a->id); - if (!control) - return -EINVAL; - - param.param_value = a->value; - param.param_id = control->vc_id; - params = *(s32 *)¶m; /* temp code */ - - mutex_lock(&pd->lock); - ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status); - ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - mutex_unlock(&pd->lock); - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - return ret; -} - -/* Audio ioctls */ -static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) -{ - if (0 != a->index) - return -EINVAL; - a->capability = V4L2_AUDCAP_STEREO; - strcpy(a->name, "USB audio in"); - /*Poseidon have no AVL function.*/ - a->mode = 0; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - a->index = 0; - a->capability = V4L2_AUDCAP_STEREO; - strcpy(a->name, "USB audio in"); - a->mode = 0; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - return (0 == a->index) ? 0 : -EINVAL; -} - -/* Tuner ioctls */ -static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct tuner_atv_sig_stat_s atv_stat; - s32 count = 5, ret, cmd_status; - int index; - - if (0 != tuner->index) - return -EINVAL; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, - &atv_stat, &cmd_status, sizeof(atv_stat)); - - while (atv_stat.sig_lock_busy && count-- && !ret) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, - &atv_stat, &cmd_status, sizeof(atv_stat)); - } - mutex_unlock(&pd->lock); - - if (debug_mode) - log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength); - - if (ret || cmd_status) - tuner->signal = 0; - else if (atv_stat.sig_present && !atv_stat.sig_strength) - tuner->signal = 0xFFFF; - else - tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8; - - strcpy(tuner->name, "Telegent Systems"); - tuner->type = V4L2_TUNER_ANALOG_TV; - tuner->rangelow = TUNER_FREQ_MIN / 62500; - tuner->rangehigh = TUNER_FREQ_MAX / 62500; - tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - index = pd->video_data.context.audio_idx; - tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub; - tuner->audmode = pd_audio_modes[index].v4l2_audio_mode; - tuner->afc = 0; - logs(front); - return 0; -} - -static int pd_vidioc_s_tuner(struct poseidon *pd, int index) -{ - s32 ret = 0, cmd_status, param, audiomode; - - mutex_lock(&pd->lock); - param = pd_audio_modes[index].tlg_audio_mode; - ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status); - audiomode = get_audio_std(pd->video_data.context.tvnormid); - ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, - &cmd_status); - if (!ret) - pd->video_data.context.audio_idx = index; - mutex_unlock(&pd->lock); - return ret; -} - -static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - int index; - - if (0 != a->index) - return -EINVAL; - logs(front); - for (index = 0; index < POSEIDON_AUDIOMODS; index++) - if (a->audmode == pd_audio_modes[index].v4l2_audio_mode) - return pd_vidioc_s_tuner(pd, index); - return -EINVAL; -} - -static int vidioc_g_frequency(struct file *file, void *fh, - struct v4l2_frequency *freq) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct running_context *context = &pd->video_data.context; - - if (0 != freq->tuner) - return -EINVAL; - freq->frequency = context->freq; - freq->type = V4L2_TUNER_ANALOG_TV; - return 0; -} - -static int set_frequency(struct poseidon *pd, __u32 frequency) -{ - s32 ret = 0, param, cmd_status; - struct running_context *context = &pd->video_data.context; - - param = frequency * 62500 / 1000; - if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000) - return -EINVAL; - - mutex_lock(&pd->lock); - ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status); - ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - - msleep(250); /* wait for a while until the hardware is ready. */ - context->freq = frequency; - mutex_unlock(&pd->lock); - return ret; -} - -static int vidioc_s_frequency(struct file *file, void *fh, - struct v4l2_frequency *freq) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - - logs(front); -#ifdef CONFIG_PM - pd->pm_suspend = pm_video_suspend; - pd->pm_resume = pm_video_resume; -#endif - return set_frequency(pd, freq->frequency); -} - -static int vidioc_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *b) -{ - struct front_face *front = file->private_data; - logs(front); - return videobuf_reqbufs(&front->q, b); -} - -static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct front_face *front = file->private_data; - logs(front); - return videobuf_querybuf(&front->q, b); -} - -static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct front_face *front = file->private_data; - return videobuf_qbuf(&front->q, b); -} - -static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct front_face *front = file->private_data; - return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK); -} - -/* Just stop the URBs, do not free the URBs */ -static int usb_transfer_stop(struct video_data *video) -{ - if (video->is_streaming) { - int i; - s32 cmd_status; - struct poseidon *pd = video->pd; - - video->is_streaming = 0; - for (i = 0; i < SBUF_NUM; ++i) { - if (video->urb_array[i]) - usb_kill_urb(video->urb_array[i]); - } - - send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, - &cmd_status); - } - return 0; -} - -int stop_all_video_stream(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - - mutex_lock(&pd->lock); - if (video->is_streaming) { - struct front_face *front = video->front; - - /* stop the URBs */ - usb_transfer_stop(video); - free_all_urb(video); - - /* stop the host side of VIDEO */ - videobuf_stop(&front->q); - videobuf_mmap_free(&front->q); - - /* stop the host side of VBI */ - front = vbi->front; - if (front) { - videobuf_stop(&front->q); - videobuf_mmap_free(&front->q); - } - } - mutex_unlock(&pd->lock); - return 0; -} - -/* - * The bubbles can seriously damage the video's quality, - * though it occurs in very rare situation. - */ -static void iso_bubble_handler(struct work_struct *w) -{ - struct video_data *video; - struct poseidon *pd; - - video = container_of(w, struct video_data, bubble_work); - pd = video->pd; - - mutex_lock(&pd->lock); - usb_transfer_stop(video); - msleep(500); - start_video_stream(pd); - mutex_unlock(&pd->lock); -} - - -static int vidioc_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct front_face *front = fh; - - logs(front); - if (unlikely(type != front->type)) - return -EINVAL; - return videobuf_streamon(&front->q); -} - -static int vidioc_streamoff(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct front_face *front = file->private_data; - - logs(front); - if (unlikely(type != front->type)) - return -EINVAL; - return videobuf_streamoff(&front->q); -} - -/* Set the firmware's default values : need altersetting */ -static int pd_video_checkmode(struct poseidon *pd) -{ - s32 ret = 0, cmd_status, audiomode; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/2); - - /* choose the altersetting */ - ret = usb_set_interface(pd->udev, 0, - (pd->cur_transfer_mode ? - ISO_3K_BULK_ALTERNATE_IFACE : - BULK_ALTERNATE_IFACE)); - if (ret < 0) - goto error; - - /* set default parameters for PAL-D , with the VBI enabled*/ - ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV); - ret |= send_set_req(pd, SGNL_SRC_SEL, - TLG_SIG_SRC_ANTENNA, &cmd_status); - ret |= send_set_req(pd, VIDEO_STD_SEL, - TLG_TUNE_VSTD_PAL_D, &cmd_status); - ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL, - TLG_TUNER_VID_FORMAT_YUV, &cmd_status); - ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, - TLG_TUNE_VID_RES_720, &cmd_status); - ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status); - ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */ - - /* set the audio */ - audiomode = get_audio_std(pd->video_data.context.tvnormid); - ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status); - ret |= send_set_req(pd, TUNER_AUD_MODE, - TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status); - ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL, - ATV_AUDIO_RATE_48K, &cmd_status); -error: - return ret; -} - -#ifdef CONFIG_PM -static int pm_video_suspend(struct poseidon *pd) -{ - /* stop audio */ - pm_alsa_suspend(pd); - - /* stop and free all the URBs */ - usb_transfer_stop(&pd->video_data); - free_all_urb(&pd->video_data); - - /* reset the interface */ - usb_set_interface(pd->udev, 0, 0); - msleep(300); - return 0; -} - -static int restore_v4l2_context(struct poseidon *pd, - struct running_context *context) -{ - struct front_face *front = pd->video_data.front; - - pd_video_checkmode(pd); - - set_std(pd, &context->tvnormid); - vidioc_s_input(NULL, front, context->sig_index); - pd_vidioc_s_tuner(pd, context->audio_idx); - pd_vidioc_s_fmt(pd, &context->pix); - set_frequency(pd, context->freq); - return 0; -} - -static int pm_video_resume(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - - /* resume the video */ - /* [1] restore the origin V4L2 parameters */ - restore_v4l2_context(pd, &video->context); - - /* [2] initiate video copy variables */ - if (video->front->curr_frame) - init_copy(video, 0); - - /* [3] fire urbs */ - start_video_stream(pd); - - /* resume the audio */ - pm_alsa_resume(pd); - return 0; -} -#endif - -void set_debug_mode(struct video_device *vfd, int debug_mode) -{ - vfd->debug = 0; - if (debug_mode & 0x1) - vfd->debug = V4L2_DEBUG_IOCTL; - if (debug_mode & 0x2) - vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; -} - -static void init_video_context(struct running_context *context) -{ - context->sig_index = 0; - context->audio_idx = 1; /* stereo */ - context->tvnormid = V4L2_STD_PAL_D; - context->pix = (struct v4l2_pix_format) { - .width = 720, - .height = 576, - .pixelformat = V4L2_PIX_FMT_YUYV, - .field = V4L2_FIELD_INTERLACED, - .bytesperline = 720 * 2, - .sizeimage = 720 * 576 * 2, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .priv = 0 - }; -} - -static int pd_video_open(struct file *file) -{ - struct video_device *vfd = video_devdata(file); - struct poseidon *pd = video_get_drvdata(vfd); - struct front_face *front = NULL; - int ret = -ENOMEM; - - mutex_lock(&pd->lock); - usb_autopm_get_interface(pd->interface); - - if (vfd->vfl_type == VFL_TYPE_GRABBER - && !(pd->state & POSEIDON_STATE_ANALOG)) { - front = kzalloc(sizeof(struct front_face), GFP_KERNEL); - if (!front) - goto out; - - pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */ - init_video_context(&pd->video_data.context); - - ret = pd_video_checkmode(pd); - if (ret < 0) { - kfree(front); - ret = -1; - goto out; - } - - pd->state |= POSEIDON_STATE_ANALOG; - front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - pd->video_data.users++; - set_debug_mode(vfd, debug_mode); - - videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, - NULL, &front->queue_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED,/* video is interlacd */ - sizeof(struct videobuf_buffer),/*it's enough*/ - front, NULL); - } else if (vfd->vfl_type == VFL_TYPE_VBI - && !(pd->state & POSEIDON_STATE_VBI)) { - front = kzalloc(sizeof(struct front_face), GFP_KERNEL); - if (!front) - goto out; - - pd->state |= POSEIDON_STATE_VBI; - front->type = V4L2_BUF_TYPE_VBI_CAPTURE; - pd->vbi_data.front = front; - pd->vbi_data.users++; - - videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, - NULL, &front->queue_lock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_NONE, /* vbi is NONE mode */ - sizeof(struct videobuf_buffer), - front, NULL); - } else { - /* maybe add FM support here */ - log("other "); - ret = -EINVAL; - goto out; - } - - front->pd = pd; - front->curr_frame = NULL; - INIT_LIST_HEAD(&front->active); - spin_lock_init(&front->queue_lock); - - file->private_data = front; - kref_get(&pd->kref); - - mutex_unlock(&pd->lock); - return 0; -out: - usb_autopm_put_interface(pd->interface); - mutex_unlock(&pd->lock); - return ret; -} - -static int pd_video_release(struct file *file) -{ - struct front_face *front = file->private_data; - struct poseidon *pd = front->pd; - s32 cmd_status = 0; - - logs(front); - mutex_lock(&pd->lock); - - if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - pd->state &= ~POSEIDON_STATE_ANALOG; - - /* stop the device, and free the URBs */ - usb_transfer_stop(&pd->video_data); - free_all_urb(&pd->video_data); - - /* stop the firmware */ - send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, - &cmd_status); - - pd->file_for_stream = NULL; - pd->video_data.users--; - } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - pd->state &= ~POSEIDON_STATE_VBI; - pd->vbi_data.front = NULL; - pd->vbi_data.users--; - } - videobuf_stop(&front->q); - videobuf_mmap_free(&front->q); - - usb_autopm_put_interface(pd->interface); - mutex_unlock(&pd->lock); - - kfree(front); - file->private_data = NULL; - kref_put(&pd->kref, poseidon_delete); - return 0; -} - -static int pd_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct front_face *front = file->private_data; - return videobuf_mmap_mapper(&front->q, vma); -} - -static unsigned int pd_video_poll(struct file *file, poll_table *table) -{ - struct front_face *front = file->private_data; - return videobuf_poll_stream(file, &front->q, table); -} - -static ssize_t pd_video_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct front_face *front = file->private_data; - return videobuf_read_stream(&front->q, buffer, count, ppos, - 0, file->f_flags & O_NONBLOCK); -} - -/* This struct works for both VIDEO and VBI */ -static const struct v4l2_file_operations pd_video_fops = { - .owner = THIS_MODULE, - .open = pd_video_open, - .release = pd_video_release, - .read = pd_video_read, - .poll = pd_video_poll, - .mmap = pd_video_mmap, - .ioctl = video_ioctl2, /* maybe changed in future */ -}; - -static const struct v4l2_ioctl_ops pd_video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - - /* Video format */ - .vidioc_g_fmt_vid_cap = vidioc_g_fmt, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */ - .vidioc_try_fmt_vid_cap = vidioc_try_fmt, - - /* Input */ - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_enum_input = vidioc_enum_input, - - /* Audio ioctls */ - .vidioc_enumaudio = vidioc_enumaudio, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - - /* Tuner ioctls */ - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_s_std = vidioc_s_std, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - - /* Buffer handlers */ - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - - /* Stream on/off */ - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - - /* Control handling */ - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, -}; - -static struct video_device pd_video_template = { - .name = "Telegent-Video", - .fops = &pd_video_fops, - .minor = -1, - .release = video_device_release, - .tvnorms = V4L2_STD_ALL, - .ioctl_ops = &pd_video_ioctl_ops, -}; - -struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp) -{ - struct video_device *vfd; - - vfd = video_device_alloc(); - if (vfd == NULL) - return NULL; - *vfd = *tmp; - vfd->minor = -1; - vfd->v4l2_dev = &pd->v4l2_dev; - /*vfd->parent = &(pd->udev->dev); */ - vfd->release = video_device_release; - video_set_drvdata(vfd, pd); - return vfd; -} - -void destroy_video_device(struct video_device **v_dev) -{ - struct video_device *dev = *v_dev; - - if (dev == NULL) - return; - - if (video_is_registered(dev)) - video_unregister_device(dev); - else - video_device_release(dev); - *v_dev = NULL; -} - -void pd_video_exit(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - - destroy_video_device(&video->v_dev); - destroy_video_device(&vbi->v_dev); - log(); -} - -int pd_video_init(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - int ret = -ENOMEM; - - video->v_dev = vdev_init(pd, &pd_video_template); - if (video->v_dev == NULL) - goto out; - - ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1); - if (ret != 0) - goto out; - - /* VBI uses the same template as video */ - vbi->v_dev = vdev_init(pd, &pd_video_template); - if (vbi->v_dev == NULL) { - ret = -ENOMEM; - goto out; - } - ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1); - if (ret != 0) - goto out; - log("register VIDEO/VBI devices"); - return 0; -out: - log("VIDEO/VBI devices register failed, : %d", ret); - pd_video_exit(pd); - return ret; -} - diff --git a/drivers/media/video/tlg2300/vendorcmds.h b/drivers/media/video/tlg2300/vendorcmds.h deleted file mode 100644 index ba6f4ae3b2c2..000000000000 --- a/drivers/media/video/tlg2300/vendorcmds.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef VENDOR_CMD_H_ -#define VENDOR_CMD_H_ - -#define BULK_ALTERNATE_IFACE (2) -#define ISO_3K_BULK_ALTERNATE_IFACE (1) -#define REQ_SET_CMD (0X00) -#define REQ_GET_CMD (0X80) - -enum tlg__analog_audio_standard { - TLG_TUNE_ASTD_NONE = 0x00000000, - TLG_TUNE_ASTD_A2 = 0x00000001, - TLG_TUNE_ASTD_NICAM = 0x00000002, - TLG_TUNE_ASTD_EIAJ = 0x00000004, - TLG_TUNE_ASTD_BTSC = 0x00000008, - TLG_TUNE_ASTD_FM_US = 0x00000010, - TLG_TUNE_ASTD_FM_EUR = 0x00000020, - TLG_TUNE_ASTD_ALL = 0x0000003f -}; - -/* - * identifiers for Custom Parameter messages. - * @typedef cmd_custom_param_id_t - */ -enum cmd_custom_param_id { - CUST_PARM_ID_NONE = 0x00, - CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01, - CUST_PARM_ID_CONTRAST_CTRL = 0x02, - CUST_PARM_ID_HUE_CTRL = 0x03, - CUST_PARM_ID_SATURATION_CTRL = 0x04, - CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10, - CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11, - CUST_PARM_ID_MAX -}; - -struct tuner_custom_parameter_s { - uint16_t param_id; /* Parameter identifier */ - uint16_t param_value; /* Parameter value */ -}; - -struct tuner_ber_rate_s { - uint32_t ber_rate; /* BER sample rate in seconds */ -}; - -struct tuner_atv_sig_stat_s { - uint32_t sig_present; - uint32_t sig_locked; - uint32_t sig_lock_busy; - uint32_t sig_strength; /* milliDb */ - uint32_t tv_audio_chan; /* mono/stereo/sap*/ - uint32_t mvision_stat; /* macrovision status */ -}; - -struct tuner_dtv_sig_stat_s { - uint32_t sig_present; /* Boolean*/ - uint32_t sig_locked; /* Boolean */ - uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */ - uint32_t sig_strength; /* milliDb*/ -}; - -struct tuner_fm_sig_stat_s { - uint32_t sig_present; /* Boolean*/ - uint32_t sig_locked; /* Boolean */ - uint32_t sig_lock_busy; /* Boolean */ - uint32_t sig_stereo_mono;/* TBD*/ - uint32_t sig_strength; /* milliDb*/ -}; - -enum _tag_tlg_tune_srv_cmd { - TLG_TUNE_PLAY_SVC_START = 1, - TLG_TUNE_PLAY_SVC_STOP -}; - -enum _tag_tune_atv_audio_mode_caps { - TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001, - TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002, - TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/ - TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/ - TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040 -}; - - -enum _tag_tuner_atv_audio_rates { - ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/ - ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/ - ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/ - ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */ -}; - -enum _tag_tune_atv_vid_res_caps { - TLG_TUNE_VID_RES_NONE = 0x00000000, - TLG_TUNE_VID_RES_720 = 0x00000001, - TLG_TUNE_VID_RES_704 = 0x00000002, - TLG_TUNE_VID_RES_360 = 0x00000004 -}; - -enum _tag_tuner_analog_video_format { - TLG_TUNER_VID_FORMAT_YUV = 0x00000001, - TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002, - TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004, -}; - -enum tlg_ext_audio_support { - TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */ - TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/ -}; - -enum { - TLG_MODE_NONE = 0x00, /* No Mode specified*/ - TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/ - TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/ - TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/ - TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/ - TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/ -}; - -enum tlg_signal_sources_t { - TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */ - TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */ - TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/ - TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */ - TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */ -}; - -enum tuner_analog_video_standard { - TLG_TUNE_VSTD_NONE = 0x00000000, - TLG_TUNE_VSTD_NTSC_M = 0x00000001, - TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */ - TLG_TUNE_VSTD_PAL_B = 0x00000010, - TLG_TUNE_VSTD_PAL_D = 0x00000020, - TLG_TUNE_VSTD_PAL_G = 0x00000040, - TLG_TUNE_VSTD_PAL_H = 0x00000080, - TLG_TUNE_VSTD_PAL_I = 0x00000100, - TLG_TUNE_VSTD_PAL_M = 0x00000200, - TLG_TUNE_VSTD_PAL_N = 0x00000400, - TLG_TUNE_VSTD_SECAM_B = 0x00001000, - TLG_TUNE_VSTD_SECAM_D = 0x00002000, - TLG_TUNE_VSTD_SECAM_G = 0x00004000, - TLG_TUNE_VSTD_SECAM_H = 0x00008000, - TLG_TUNE_VSTD_SECAM_K = 0x00010000, - TLG_TUNE_VSTD_SECAM_K1 = 0x00020000, - TLG_TUNE_VSTD_SECAM_L = 0x00040000, - TLG_TUNE_VSTD_SECAM_L1 = 0x00080000, - TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000 -}; - -enum tlg_mode_caps { - TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */ - TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */ - TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/ - TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */ - TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */ -}; - -enum poseidon_vendor_cmds { - LAST_CMD_STAT = 0x00, - GET_CHIP_ID = 0x01, - GET_FW_ID = 0x02, - PRODUCT_CAPS = 0x03, - - TUNE_MODE_CAP_ATV = 0x10, - TUNE_MODE_CAP_ATVCOMP = 0X10, - TUNE_MODE_CAP_DVBT = 0x10, - TUNE_MODE_CAP_FM = 0x10, - TUNE_MODE_SELECT = 0x11, - TUNE_FREQ_SELECT = 0x12, - SGNL_SRC_SEL = 0x13, - - VIDEO_STD_SEL = 0x14, - VIDEO_STREAM_FMT_SEL = 0x15, - VIDEO_ROSOLU_AVAIL = 0x16, - VIDEO_ROSOLU_SEL = 0x17, - VIDEO_CONT_PROTECT = 0x20, - - VCR_TIMING_MODSEL = 0x21, - EXT_AUDIO_CAP = 0x22, - EXT_AUDIO_SEL = 0x23, - TEST_PATTERN_SEL = 0x24, - VBI_DATA_SEL = 0x25, - AUDIO_SAMPLE_RATE_CAP = 0x28, - AUDIO_SAMPLE_RATE_SEL = 0x29, - TUNER_AUD_MODE = 0x2a, - TUNER_AUD_MODE_AVAIL = 0x2b, - TUNER_AUD_ANA_STD = 0x2c, - TUNER_CUSTOM_PARAMETER = 0x2f, - - DVBT_TUNE_MODE_SEL = 0x30, - DVBT_BANDW_CAP = 0x31, - DVBT_BANDW_SEL = 0x32, - DVBT_GUARD_INTERV_CAP = 0x33, - DVBT_GUARD_INTERV_SEL = 0x34, - DVBT_MODULATION_CAP = 0x35, - DVBT_MODULATION_SEL = 0x36, - DVBT_INNER_FEC_RATE_CAP = 0x37, - DVBT_INNER_FEC_RATE_SEL = 0x38, - DVBT_TRANS_MODE_CAP = 0x39, - DVBT_TRANS_MODE_SEL = 0x3a, - DVBT_SEARCH_RANG = 0x3c, - - TUNER_SETUP_ANALOG = 0x40, - TUNER_SETUP_DIGITAL = 0x41, - TUNER_SETUP_FM_RADIO = 0x42, - TAKE_REQUEST = 0x43, /* Take effect of the command */ - PLAY_SERVICE = 0x44, /* Play start or Play stop */ - TUNER_STATUS = 0x45, - TUNE_PROP_DVBT = 0x46, - ERR_RATE_STATS = 0x47, - TUNER_BER_RATE = 0x48, - - SCAN_CAPS = 0x50, - SCAN_SETUP = 0x51, - SCAN_SERVICE = 0x52, - SCAN_STATS = 0x53, - - PID_SET = 0x58, - PID_UNSET = 0x59, - PID_LIST = 0x5a, - - IRD_CAP = 0x60, - IRD_MODE_SEL = 0x61, - IRD_SETUP = 0x62, - - PTM_MODE_CAP = 0x70, - PTM_MODE_SEL = 0x71, - PTM_SERVICE = 0x72, - TUNER_REG_SCRIPT = 0x73, - CMD_CHIP_RST = 0x74, -}; - -enum tlg_bw { - TLG_BW_5 = 5, - TLG_BW_6 = 6, - TLG_BW_7 = 7, - TLG_BW_8 = 8, - TLG_BW_12 = 12, - TLG_BW_15 = 15 -}; - -struct cmd_firmware_vers_s { - uint8_t fw_rev_major; - uint8_t fw_rev_minor; - uint16_t fw_patch; -}; -#endif /* VENDOR_CMD_H_ */ diff --git a/drivers/media/video/tm6000/Kconfig b/drivers/media/video/tm6000/Kconfig deleted file mode 100644 index a43b77abd931..000000000000 --- a/drivers/media/video/tm6000/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -config VIDEO_TM6000 - tristate "TV Master TM5600/6000/6010 driver" - depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB - select VIDEO_TUNER - select MEDIA_TUNER_XC2028 - select MEDIA_TUNER_XC5000 - select VIDEOBUF_VMALLOC - help - Support for TM5600/TM6000/TM6010 USB Device - - Since these cards have no MPEG decoder onboard, they transmit - only compressed MPEG data over the usb bus, so you need - an external software decoder to watch TV on your computer. - - Say Y if you own such a device and want to use it. - -config VIDEO_TM6000_ALSA - tristate "TV Master TM5600/6000/6010 audio support" - depends on VIDEO_TM6000 && SND - select SND_PCM - ---help--- - This is a video4linux driver for direct (DMA) audio for - TM5600/TM6000/TM6010 USB Devices. - - To compile this driver as a module, choose M here: the - module will be called tm6000-alsa. - -config VIDEO_TM6000_DVB - tristate "DVB Support for tm6000 based TV cards" - depends on VIDEO_TM6000 && DVB_CORE && USB - select DVB_ZL10353 - ---help--- - This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/media/video/tm6000/Makefile b/drivers/media/video/tm6000/Makefile deleted file mode 100644 index 1feb8c9c816c..000000000000 --- a/drivers/media/video/tm6000/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -tm6000-y := tm6000-cards.o \ - tm6000-core.o \ - tm6000-i2c.o \ - tm6000-video.o \ - tm6000-stds.o \ - tm6000-input.o - -obj-$(CONFIG_VIDEO_TM6000) += tm6000.o -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/tuners -ccflags-y += -Idrivers/media/dvb-core -ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/tm6000/tm6000-alsa.c b/drivers/media/video/tm6000/tm6000-alsa.c deleted file mode 100644 index bd07ec707956..000000000000 --- a/drivers/media/video/tm6000/tm6000-alsa.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * - * Support for audio capture for tm5600/6000/6010 - * (c) 2007-2008 Mauro Carvalho Chehab - * - * Based on cx88-alsa.c - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -#include "tm6000.h" -#include "tm6000-regs.h" - -#undef dprintk - -#define dprintk(level, fmt, arg...) do { \ - if (debug >= level) \ - printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ - } while (0) - -/**************************************************************************** - Module global static vars - ****************************************************************************/ - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ - -static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; - -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); - - -/**************************************************************************** - Module macros - ****************************************************************************/ - -MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Trident,tm5600}," - "{{Trident,tm6000}," - "{{Trident,tm6010}"); -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages"); - -/**************************************************************************** - Module specific funtions - ****************************************************************************/ - -/* - * BOARD Specific: Sets audio DMA - */ - -static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) -{ - struct tm6000_core *core = chip->core; - - dprintk(1, "Starting audio DMA\n"); - - /* Enables audio */ - tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40); - - tm6000_set_audio_bitrate(core, 48000); - - return 0; -} - -/* - * BOARD Specific: Resets audio DMA - */ -static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) -{ - struct tm6000_core *core = chip->core; - - dprintk(1, "Stopping audio DMA\n"); - - /* Disables audio */ - tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40); - - return 0; -} - -static void dsp_buffer_free(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - - dprintk(2, "Freeing buffer\n"); - - vfree(substream->runtime->dma_area); - substream->runtime->dma_area = NULL; - substream->runtime->dma_bytes = 0; -} - -static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - - dprintk(2, "Allocating buffer\n"); - - if (substream->runtime->dma_area) { - if (substream->runtime->dma_bytes > size) - return 0; - - dsp_buffer_free(substream); - } - - substream->runtime->dma_area = vmalloc(size); - if (!substream->runtime->dma_area) - return -ENOMEM; - - substream->runtime->dma_bytes = size; - - return 0; -} - - -/**************************************************************************** - ALSA PCM Interface - ****************************************************************************/ - -/* - * Digital hardware definition - */ -#define DEFAULT_FIFO_SIZE 4096 - -static struct snd_pcm_hardware snd_tm6000_digital_hw = { - .info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .period_bytes_min = 64, - .period_bytes_max = 12544, - .periods_min = 2, - .periods_max = 98, - .buffer_bytes_max = 62720 * 8, -}; - -/* - * audio pcm capture open callback - */ -static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - - err = snd_pcm_hw_constraint_pow2(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIODS); - if (err < 0) - goto _error; - - chip->substream = substream; - - runtime->hw = snd_tm6000_digital_hw; - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - - return 0; -_error: - dprintk(1, "Error opening PCM!\n"); - return err; -} - -/* - * audio close callback - */ -static int snd_tm6000_close(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct tm6000_core *core = chip->core; - - if (atomic_read(&core->stream_started) > 0) { - atomic_set(&core->stream_started, 0); - schedule_work(&core->wq_trigger); - } - - return 0; -} - -static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) -{ - struct snd_tm6000_card *chip = core->adev; - struct snd_pcm_substream *substream = chip->substream; - struct snd_pcm_runtime *runtime; - int period_elapsed = 0; - unsigned int stride, buf_pos; - int length; - - if (atomic_read(&core->stream_started) == 0) - return 0; - - if (!size || !substream) { - dprintk(1, "substream was NULL\n"); - return -EINVAL; - } - - runtime = substream->runtime; - if (!runtime || !runtime->dma_area) { - dprintk(1, "runtime was NULL\n"); - return -EINVAL; - } - - buf_pos = chip->buf_pos; - stride = runtime->frame_bits >> 3; - - if (stride == 0) { - dprintk(1, "stride is zero\n"); - return -EINVAL; - } - - length = size / stride; - if (length == 0) { - dprintk(1, "%s: length was zero\n", __func__); - return -EINVAL; - } - - dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, - runtime->dma_area, buf_pos, - (unsigned int)runtime->buffer_size, stride); - - if (buf_pos + length >= runtime->buffer_size) { - unsigned int cnt = runtime->buffer_size - buf_pos; - memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); - memcpy(runtime->dma_area, buf + cnt * stride, - length * stride - cnt * stride); - } else - memcpy(runtime->dma_area + buf_pos * stride, buf, - length * stride); - - snd_pcm_stream_lock(substream); - - chip->buf_pos += length; - if (chip->buf_pos >= runtime->buffer_size) - chip->buf_pos -= runtime->buffer_size; - - chip->period_pos += length; - if (chip->period_pos >= runtime->period_size) { - chip->period_pos -= runtime->period_size; - period_elapsed = 1; - } - - snd_pcm_stream_unlock(substream); - - if (period_elapsed) - snd_pcm_period_elapsed(substream); - - return 0; -} - -/* - * hw_params callback - */ -static int snd_tm6000_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int size, rc; - - size = params_period_bytes(hw_params) * params_periods(hw_params); - - rc = dsp_buffer_alloc(substream, size); - if (rc < 0) - return rc; - - return 0; -} - -/* - * hw free callback - */ -static int snd_tm6000_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct tm6000_core *core = chip->core; - - if (atomic_read(&core->stream_started) > 0) { - atomic_set(&core->stream_started, 0); - schedule_work(&core->wq_trigger); - } - - dsp_buffer_free(substream); - return 0; -} - -/* - * prepare callback - */ -static int snd_tm6000_prepare(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - - chip->buf_pos = 0; - chip->period_pos = 0; - - return 0; -} - - -/* - * trigger callback - */ -static void audio_trigger(struct work_struct *work) -{ - struct tm6000_core *core = container_of(work, struct tm6000_core, - wq_trigger); - struct snd_tm6000_card *chip = core->adev; - - if (atomic_read(&core->stream_started)) { - dprintk(1, "starting capture"); - _tm6000_start_audio_dma(chip); - } else { - dprintk(1, "stopping capture"); - _tm6000_stop_audio_dma(chip); - } -} - -static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct tm6000_core *core = chip->core; - int err = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ - case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ - case SNDRV_PCM_TRIGGER_START: - atomic_set(&core->stream_started, 1); - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ - case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ - case SNDRV_PCM_TRIGGER_STOP: - atomic_set(&core->stream_started, 0); - break; - default: - err = -EINVAL; - break; - } - schedule_work(&core->wq_trigger); - - return err; -} -/* - * pointer callback - */ -static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - - return chip->buf_pos; -} - -static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, - unsigned long offset) -{ - void *pageptr = subs->runtime->dma_area + offset; - - return vmalloc_to_page(pageptr); -} - -/* - * operators - */ -static struct snd_pcm_ops snd_tm6000_pcm_ops = { - .open = snd_tm6000_pcm_open, - .close = snd_tm6000_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_tm6000_hw_params, - .hw_free = snd_tm6000_hw_free, - .prepare = snd_tm6000_prepare, - .trigger = snd_tm6000_card_trigger, - .pointer = snd_tm6000_pointer, - .page = snd_pcm_get_vmalloc_page, -}; - -/* - * create a PCM device - */ - -/* FIXME: Control interface - How to control volume/mute? */ - -/**************************************************************************** - Basic Flow for Sound Devices - ****************************************************************************/ - -/* - * Alsa Constructor - Component probe - */ -static int tm6000_audio_init(struct tm6000_core *dev) -{ - struct snd_card *card; - struct snd_tm6000_card *chip; - int rc; - static int devnr; - char component[14]; - struct snd_pcm *pcm; - - if (!dev) - return 0; - - if (devnr >= SNDRV_CARDS) - return -ENODEV; - - if (!enable[devnr]) - return -ENOENT; - - rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card); - if (rc < 0) { - snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); - return rc; - } - strcpy(card->driver, "tm6000-alsa"); - strcpy(card->shortname, "TM5600/60x0"); - sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", - dev->udev->bus->busnum, dev->udev->devnum); - - sprintf(component, "USB%04x:%04x", - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct)); - snd_component_add(card, component); - snd_card_set_dev(card, &dev->udev->dev); - - chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); - if (!chip) { - rc = -ENOMEM; - goto error; - } - - chip->core = dev; - chip->card = card; - dev->adev = chip; - spin_lock_init(&chip->reg_lock); - - rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); - if (rc < 0) - goto error_chip; - - pcm->info_flags = 0; - pcm->private_data = chip; - strcpy(pcm->name, "Trident TM5600/60x0"); - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); - - INIT_WORK(&dev->wq_trigger, audio_trigger); - rc = snd_card_register(card); - if (rc < 0) - goto error_chip; - - dprintk(1, "Registered audio driver for %s\n", card->longname); - - return 0; - -error_chip: - kfree(chip); - dev->adev = NULL; -error: - snd_card_free(card); - return rc; -} - -static int tm6000_audio_fini(struct tm6000_core *dev) -{ - struct snd_tm6000_card *chip = dev->adev; - - if (!dev) - return 0; - - if (!chip) - return 0; - - if (!chip->card) - return 0; - - snd_card_free(chip->card); - chip->card = NULL; - kfree(chip); - dev->adev = NULL; - - return 0; -} - -static struct tm6000_ops audio_ops = { - .type = TM6000_AUDIO, - .name = "TM6000 Audio Extension", - .init = tm6000_audio_init, - .fini = tm6000_audio_fini, - .fillbuf = tm6000_fillbuf, -}; - -static int __init tm6000_alsa_register(void) -{ - return tm6000_register_extension(&audio_ops); -} - -static void __exit tm6000_alsa_unregister(void) -{ - tm6000_unregister_extension(&audio_ops); -} - -module_init(tm6000_alsa_register); -module_exit(tm6000_alsa_unregister); diff --git a/drivers/media/video/tm6000/tm6000-cards.c b/drivers/media/video/tm6000/tm6000-cards.c deleted file mode 100644 index 034659b13174..000000000000 --- a/drivers/media/video/tm6000/tm6000-cards.c +++ /dev/null @@ -1,1413 +0,0 @@ -/* - * tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2006-2007 Mauro Carvalho Chehab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 - -#include "tm6000.h" -#include "tm6000-regs.h" -#include "tuner-xc2028.h" -#include "xc5000.h" - -#define TM6000_BOARD_UNKNOWN 0 -#define TM5600_BOARD_GENERIC 1 -#define TM6000_BOARD_GENERIC 2 -#define TM6010_BOARD_GENERIC 3 -#define TM5600_BOARD_10MOONS_UT821 4 -#define TM5600_BOARD_10MOONS_UT330 5 -#define TM6000_BOARD_ADSTECH_DUAL_TV 6 -#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 -#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 -#define TM6010_BOARD_HAUPPAUGE_900H 9 -#define TM6010_BOARD_BEHOLD_WANDER 10 -#define TM6010_BOARD_BEHOLD_VOYAGER 11 -#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 -#define TM6010_BOARD_TWINHAN_TU501 13 -#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 -#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 -#define TM5600_BOARD_TERRATEC_GRABSTER 16 - -#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \ - (model == TM5600_BOARD_GENERIC) || \ - (model == TM6000_BOARD_GENERIC) || \ - (model == TM6010_BOARD_GENERIC)) - -#define TM6000_MAXBOARDS 16 -static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; - -module_param_array(card, int, NULL, 0444); - -static unsigned long tm6000_devused; - - -struct tm6000_board { - char *name; - char eename[16]; /* EEPROM name */ - unsigned eename_size; /* size of EEPROM name */ - unsigned eename_pos; /* Position where it appears at ROM */ - - struct tm6000_capabilities caps; - - enum tm6000_devtype type; /* variant of the chipset */ - int tuner_type; /* type of the tuner */ - int tuner_addr; /* tuner address */ - int demod_addr; /* demodulator address */ - - struct tm6000_gpio gpio; - - struct tm6000_input vinput[3]; - struct tm6000_input rinput; - - char *ir_codes; -}; - -static struct tm6000_board tm6000_boards[] = { - [TM6000_BOARD_UNKNOWN] = { - .name = "Unknown tm6000 video grabber", - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM5600_BOARD_GENERIC] = { - .name = "Generic tm5600 board", - .type = TM5600, - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc2 >> 1, - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_GENERIC] = { - .name = "Generic tm6000 board", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc2 >> 1, - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_GENERIC] = { - .name = "Generic tm6010 board", - .type = TM6010, - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM5600_BOARD_10MOONS_UT821] = { - .name = "10Moons UT 821", - .tuner_type = TUNER_XC2028, - .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b}, - .eename_size = 14, - .eename_pos = 0x14, - .type = TM5600, - .tuner_addr = 0xc2 >> 1, - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM5600_BOARD_10MOONS_UT330] = { - .name = "10Moons UT 330", - .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, - .tuner_addr = 0xc8 >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_ADSTECH_DUAL_TV] = { - .name = "ADSTECH Dual TV USB", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc8 >> 1, - .caps = { - .has_tuner = 1, - .has_tda9874 = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_FREECOM_AND_SIMILAR] = { - .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 0, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_4, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { - .name = "ADSTECH Mini Dual TV USB", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc8 >> 1, - .demod_addr = 0x1e >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 0, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_4, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_HAUPPAUGE_900H] = { - .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", - .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 }, - .eename_size = 14, - .eename_pos = 0x42, - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .ir_codes = RC_MAP_HAUPPAUGE, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_BEHOLD_WANDER] = { - .name = "Beholder Wander DVB-T/TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .demod_reset = TM6010_GPIO_1, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, - [TM6010_BOARD_BEHOLD_VOYAGER] = { - .name = "Beholder Voyager TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - .has_remote = 1, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, - [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = { - .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_SIF1, - }, - }, - [TM5600_BOARD_TERRATEC_GRABSTER] = { - .name = "Terratec Grabster AV 150/250 MX", - .type = TM5600, - .tuner_type = TUNER_ABSENT, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_TWINHAN_TU501] = { - .name = "Twinhan TU501(704D1)", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_BEHOLD_WANDER_LITE] = { - .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 0, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .demod_reset = TM6010_GPIO_1, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, - [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { - .name = "Beholder Voyager Lite TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - .has_remote = 0, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, -}; - -/* table of devices that work with this driver */ -static struct usb_device_id tm6000_id_table[] = { - { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC }, - { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, - { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, - { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, - { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, - { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER }, - { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, - { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, - { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, - { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, - { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, - { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, - { } -}; -MODULE_DEVICE_TABLE(usb, tm6000_id_table); - -/* Control power led for show some activity */ -void tm6000_flash_led(struct tm6000_core *dev, u8 state) -{ - /* Power LED unconfigured */ - if (!dev->gpio.power_led) - return; - - /* ON Power LED */ - if (state) { - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x00); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x01); - break; - } - } - /* OFF Power LED */ - else { - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x01); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x00); - break; - } - } -} - -/* Tuner callback to provide the proper gpio changes needed for xc5000 */ -int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) -{ - int rc = 0; - struct tm6000_core *dev = ptr; - - if (dev->tuner_type != TUNER_XC5000) - return 0; - - switch (command) { - case XC5000_TUNER_RESET: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(15); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - msleep(15); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - break; - } - return rc; -} -EXPORT_SYMBOL_GPL(tm6000_xc5000_callback); - -/* Tuner callback to provide the proper gpio changes needed for xc2028 */ - -int tm6000_tuner_callback(void *ptr, int component, int command, int arg) -{ - int rc = 0; - struct tm6000_core *dev = ptr; - - if (dev->tuner_type != TUNER_XC2028) - return 0; - - switch (command) { - case XC2028_RESET_CLK: - tm6000_ir_wait(dev, 0); - - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, - 0x02, arg); - msleep(10); - rc = tm6000_i2c_reset(dev, 10); - break; - case XC2028_TUNER_RESET: - /* Reset codes during load firmware */ - switch (arg) { - case 0: - /* newer tuner can faster reset */ - switch (dev->model) { - case TM5600_BOARD_10MOONS_UT821: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - 0x300, 0x01); - msleep(10); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - 0x300, 0x00); - msleep(10); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - 0x300, 0x01); - break; - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(60); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - msleep(75); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(60); - break; - default: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - msleep(130); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(130); - break; - } - - tm6000_ir_wait(dev, 1); - break; - case 1: - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, - 0x02, 0x01); - msleep(10); - break; - case 2: - rc = tm6000_i2c_reset(dev, 100); - break; - } - break; - case XC2028_I2C_FLUSH: - tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); - tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); - break; - } - return rc; -} -EXPORT_SYMBOL_GPL(tm6000_tuner_callback); - -int tm6000_cards_setup(struct tm6000_core *dev) -{ - /* - * Board-specific initialization sequence. Handles all GPIO - * initialization sequences that are board-specific. - * Up to now, all found devices use GPIO1 and GPIO4 at the same way. - * Probably, they're all based on some reference device. Due to that, - * there's a common routine at the end to handle those GPIO's. Devices - * that use different pinups or init sequences can just return at - * the board-specific session. - */ - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - case TM6010_BOARD_GENERIC: - /* Turn xceive 3028 on */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); - msleep(15); - /* Turn zarlink zl10353 on */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); - msleep(15); - /* Reset zarlink zl10353 */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); - msleep(50); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); - msleep(15); - /* Turn zarlink zl10353 off */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); - msleep(15); - /* ir ? */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); - msleep(15); - /* Power led on (blue) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); - msleep(15); - /* DVB led off (orange) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); - msleep(15); - /* Turn zarlink zl10353 on */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); - msleep(15); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - /* Power led on (blue) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); - msleep(15); - /* Reset zarlink zl10353 */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); - msleep(50); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); - msleep(15); - break; - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - /* Power led on (blue) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); - msleep(15); - break; - default: - break; - } - - /* - * Default initialization. Most of the devices seem to use GPIO1 - * and GPIO4.on the same way, so, this handles the common sequence - * used by most devices. - * If a device uses a different sequence or different GPIO pins for - * reset, just add the code at the board-specific part - */ - - if (dev->gpio.tuner_reset) { - int rc; - int i; - - for (i = 0; i < 2; i++) { - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - if (rc < 0) { - printk(KERN_ERR "Error %i doing tuner reset\n", rc); - return rc; - } - - msleep(10); /* Just to be conservative */ - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - if (rc < 0) { - printk(KERN_ERR "Error %i doing tuner reset\n", rc); - return rc; - } - } - } else { - printk(KERN_ERR "Tuner reset is not configured\n"); - return -1; - } - - msleep(50); - - return 0; -}; - -static void tm6000_config_tuner(struct tm6000_core *dev) -{ - struct tuner_setup tun_setup; - - /* Load tuner module */ - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", dev->tuner_addr, NULL); - - memset(&tun_setup, 0, sizeof(tun_setup)); - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - - tun_setup.mode_mask = 0; - if (dev->caps.has_tuner) - tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); - - switch (dev->tuner_type) { - case TUNER_XC2028: - tun_setup.tuner_callback = tm6000_tuner_callback; - break; - case TUNER_XC5000: - tun_setup.tuner_callback = tm6000_xc5000_callback; - break; - } - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); - - switch (dev->tuner_type) { - case TUNER_XC2028: { - struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; - - memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); - memset(&ctl, 0, sizeof(ctl)); - - ctl.demod = XC3028_FE_ZARLINK456; - - xc2028_cfg.tuner = TUNER_XC2028; - xc2028_cfg.priv = &ctl; - - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - ctl.max_len = 80; - ctl.fname = "xc3028L-v36.fw"; - break; - default: - if (dev->dev_type == TM6010) - ctl.fname = "xc3028-v27.fw"; - else - ctl.fname = "xc3028-v24.fw"; - } - - printk(KERN_INFO "Setting firmware parameters for xc2028\n"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, - &xc2028_cfg); - - } - break; - case TUNER_XC5000: - { - struct v4l2_priv_tun_config xc5000_cfg; - struct xc5000_config ctl = { - .i2c_address = dev->tuner_addr, - .if_khz = 4570, - .radio_input = XC5000_RADIO_FM1_MONO, - }; - - xc5000_cfg.tuner = TUNER_XC5000; - xc5000_cfg.priv = &ctl; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, - &xc5000_cfg); - } - break; - default: - printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n"); - break; - } -} - -static int fill_board_specific_data(struct tm6000_core *dev) -{ - int rc; - - dev->dev_type = tm6000_boards[dev->model].type; - dev->tuner_type = tm6000_boards[dev->model].tuner_type; - dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; - - dev->gpio = tm6000_boards[dev->model].gpio; - - dev->ir_codes = tm6000_boards[dev->model].ir_codes; - - dev->demod_addr = tm6000_boards[dev->model].demod_addr; - - dev->caps = tm6000_boards[dev->model].caps; - - dev->vinput[0] = tm6000_boards[dev->model].vinput[0]; - dev->vinput[1] = tm6000_boards[dev->model].vinput[1]; - dev->vinput[2] = tm6000_boards[dev->model].vinput[2]; - dev->rinput = tm6000_boards[dev->model].rinput; - - /* setup per-model quirks */ - switch (dev->model) { - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_HAUPPAUGE_900H: - dev->quirks |= TM6000_QUIRK_NO_USB_DELAY; - break; - - default: - break; - } - - /* initialize hardware */ - rc = tm6000_init(dev); - if (rc < 0) - return rc; - - return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); -} - - -static void use_alternative_detection_method(struct tm6000_core *dev) -{ - int i, model = -1; - - if (!dev->eedata_size) - return; - - for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) { - if (!tm6000_boards[i].eename_size) - continue; - if (dev->eedata_size < tm6000_boards[i].eename_pos + - tm6000_boards[i].eename_size) - continue; - - if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos], - tm6000_boards[i].eename, - tm6000_boards[i].eename_size)) { - model = i; - break; - } - } - if (model < 0) { - printk(KERN_INFO "Device has eeprom but is currently unknown\n"); - return; - } - - dev->model = model; - - printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n", - tm6000_boards[model].name, model); -} - -#if defined(CONFIG_MODULES) && defined(MODULE) -static void request_module_async(struct work_struct *work) -{ - struct tm6000_core *dev = container_of(work, struct tm6000_core, - request_module_wk); - - request_module("tm6000-alsa"); - - if (dev->caps.has_dvb) - request_module("tm6000-dvb"); -} - -static void request_modules(struct tm6000_core *dev) -{ - INIT_WORK(&dev->request_module_wk, request_module_async); - schedule_work(&dev->request_module_wk); -} - -static void flush_request_modules(struct tm6000_core *dev) -{ - flush_work_sync(&dev->request_module_wk); -} -#else -#define request_modules(dev) -#define flush_request_modules(dev) -#endif /* CONFIG_MODULES */ - -static int tm6000_init_dev(struct tm6000_core *dev) -{ - struct v4l2_frequency f; - int rc = 0; - - mutex_init(&dev->lock); - mutex_lock(&dev->lock); - - if (!is_generic(dev->model)) { - rc = fill_board_specific_data(dev); - if (rc < 0) - goto err; - - /* register i2c bus */ - rc = tm6000_i2c_register(dev); - if (rc < 0) - goto err; - } else { - /* register i2c bus */ - rc = tm6000_i2c_register(dev); - if (rc < 0) - goto err; - - use_alternative_detection_method(dev); - - rc = fill_board_specific_data(dev); - if (rc < 0) - goto err; - } - - /* Default values for STD and resolutions */ - dev->width = 720; - dev->height = 480; - dev->norm = V4L2_STD_PAL_M; - - /* Configure tuner */ - tm6000_config_tuner(dev); - - /* Set video standard */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); - - /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ - f.tuner = 0; - f.type = V4L2_TUNER_ANALOG_TV; - f.frequency = 3092; /* 193.25 MHz */ - dev->freq = f.frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); - - if (dev->caps.has_tda9874) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvaudio", I2C_ADDR_TDA9874, NULL); - - /* register and initialize V4L2 */ - rc = tm6000_v4l2_register(dev); - if (rc < 0) - goto err; - - tm6000_add_into_devlist(dev); - tm6000_init_extension(dev); - - tm6000_ir_init(dev); - - request_modules(dev); - - mutex_unlock(&dev->lock); - return 0; - -err: - mutex_unlock(&dev->lock); - return rc; -} - -/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) - -static void get_max_endpoint(struct usb_device *udev, - struct usb_host_interface *alt, - char *msgtype, - struct usb_host_endpoint *curr_e, - struct tm6000_endpoint *tm_ep) -{ - u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); - unsigned int size = tmp & 0x7ff; - - if (udev->speed == USB_SPEED_HIGH) - size = size * hb_mult(tmp); - - if (size > tm_ep->maxsize) { - tm_ep->endp = curr_e; - tm_ep->maxsize = size; - tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber; - tm_ep->bAlternateSetting = alt->desc.bAlternateSetting; - - printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", - msgtype, curr_e->desc.bEndpointAddress, - size); - } -} - -/* - * tm6000_usb_probe() - * checks for supported devices - */ -static int tm6000_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *usbdev; - struct tm6000_core *dev = NULL; - int i, rc = 0; - int nr = 0; - char *speed; - - usbdev = usb_get_dev(interface_to_usbdev(interface)); - - /* Selects the proper interface */ - rc = usb_set_interface(usbdev, 0, 1); - if (rc < 0) - goto err; - - /* Check to see next free device and mark as used */ - nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); - if (nr >= TM6000_MAXBOARDS) { - printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); - usb_put_dev(usbdev); - return -ENOMEM; - } - - /* Create and initialize dev struct */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - printk(KERN_ERR "tm6000" ": out of memory!\n"); - usb_put_dev(usbdev); - return -ENOMEM; - } - spin_lock_init(&dev->slock); - mutex_init(&dev->usb_lock); - - /* Increment usage count */ - set_bit(nr, &tm6000_devused); - snprintf(dev->name, 29, "tm6000 #%d", nr); - - dev->model = id->driver_info; - if (card[nr] < ARRAY_SIZE(tm6000_boards)) - dev->model = card[nr]; - - dev->udev = usbdev; - dev->devno = nr; - - switch (usbdev->speed) { - case USB_SPEED_LOW: - speed = "1.5"; - break; - case USB_SPEED_UNKNOWN: - case USB_SPEED_FULL: - speed = "12"; - break; - case USB_SPEED_HIGH: - speed = "480"; - break; - default: - speed = "unknown"; - } - - /* Get endpoints */ - for (i = 0; i < interface->num_altsetting; i++) { - int ep; - - for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { - struct usb_host_endpoint *e; - int dir_out; - - e = &interface->altsetting[i].endpoint[ep]; - - dir_out = ((e->desc.bEndpointAddress & - USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); - - printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n", - i, - interface->altsetting[i].desc.bInterfaceNumber, - interface->altsetting[i].desc.bInterfaceClass); - - switch (e->desc.bmAttributes) { - case USB_ENDPOINT_XFER_BULK: - if (!dir_out) { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "Bulk IN", e, - &dev->bulk_in); - } else { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "Bulk OUT", e, - &dev->bulk_out); - } - break; - case USB_ENDPOINT_XFER_ISOC: - if (!dir_out) { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "ISOC IN", e, - &dev->isoc_in); - } else { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "ISOC OUT", e, - &dev->isoc_out); - } - break; - case USB_ENDPOINT_XFER_INT: - if (!dir_out) { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "INT IN", e, - &dev->int_in); - } else { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "INT OUT", e, - &dev->int_out); - } - break; - } - } - } - - - printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", - speed, - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct), - interface->altsetting->desc.bInterfaceNumber); - -/* check if the the device has the iso in endpoint at the correct place */ - if (!dev->isoc_in.endp) { - printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); - rc = -ENODEV; - - goto err; - } - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name); - - rc = tm6000_init_dev(dev); - if (rc < 0) - goto err; - - return 0; - -err: - printk(KERN_ERR "tm6000: Error %d while registering\n", rc); - - clear_bit(nr, &tm6000_devused); - usb_put_dev(usbdev); - - kfree(dev); - return rc; -} - -/* - * tm6000_usb_disconnect() - * called when the device gets diconencted - * video device will be unregistered on v4l2_close in case it is still open - */ -static void tm6000_usb_disconnect(struct usb_interface *interface) -{ - struct tm6000_core *dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - if (!dev) - return; - - printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name); - - flush_request_modules(dev); - - tm6000_ir_fini(dev); - - if (dev->gpio.power_led) { - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - /* Power led off */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x01); - msleep(15); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - /* Power led off */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x00); - msleep(15); - break; - } - } - tm6000_v4l2_unregister(dev); - - tm6000_i2c_unregister(dev); - - v4l2_device_unregister(&dev->v4l2_dev); - - dev->state |= DEV_DISCONNECTED; - - usb_put_dev(dev->udev); - - tm6000_close_extension(dev); - tm6000_remove_from_devlist(dev); - - clear_bit(dev->devno, &tm6000_devused); - kfree(dev); -} - -static struct usb_driver tm6000_usb_driver = { - .name = "tm6000", - .probe = tm6000_usb_probe, - .disconnect = tm6000_usb_disconnect, - .id_table = tm6000_id_table, -}; - -module_usb_driver(tm6000_usb_driver); - -MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter"); -MODULE_AUTHOR("Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tm6000/tm6000-core.c b/drivers/media/video/tm6000/tm6000-core.c deleted file mode 100644 index 22cc0116deb6..000000000000 --- a/drivers/media/video/tm6000/tm6000-core.c +++ /dev/null @@ -1,935 +0,0 @@ -/* - * tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2006-2007 Mauro Carvalho Chehab - * - * Copyright (C) 2007 Michel Ludwig - * - DVB-T support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 "tm6000.h" -#include "tm6000-regs.h" -#include -#include - -#define USB_TIMEOUT (5 * HZ) /* ms */ - -int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, - u16 value, u16 index, u8 *buf, u16 len) -{ - int ret, i; - unsigned int pipe; - u8 *data = NULL; - int delay = 5000; - - mutex_lock(&dev->usb_lock); - - if (len) - data = kzalloc(len, GFP_KERNEL); - - if (req_type & USB_DIR_IN) - pipe = usb_rcvctrlpipe(dev->udev, 0); - else { - pipe = usb_sndctrlpipe(dev->udev, 0); - memcpy(data, buf, len); - } - - if (tm6000_debug & V4L2_DEBUG_I2C) { - printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe); - - printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", - (req_type & USB_DIR_IN) ? " IN" : "OUT", - req_type, req, value&0xff, value>>8, index&0xff, - index>>8, len&0xff, len>>8); - - if (!(req_type & USB_DIR_IN)) { - printk(KERN_CONT ">>> "); - for (i = 0; i < len; i++) - printk(KERN_CONT " %02x", buf[i]); - printk(KERN_CONT "\n"); - } - } - - ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, - data, len, USB_TIMEOUT); - - if (req_type & USB_DIR_IN) - memcpy(buf, data, len); - - if (tm6000_debug & V4L2_DEBUG_I2C) { - if (ret < 0) { - if (req_type & USB_DIR_IN) - printk(KERN_DEBUG "<<< (len=%d)\n", len); - - printk(KERN_CONT "%s: Error #%d\n", __func__, ret); - } else if (req_type & USB_DIR_IN) { - printk(KERN_CONT "<<< "); - for (i = 0; i < len; i++) - printk(KERN_CONT " %02x", buf[i]); - printk(KERN_CONT "\n"); - } - } - - kfree(data); - - if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY) - delay = 0; - - if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) { - unsigned int tsleep; - /* Calculate delay time, 14000us for 64 bytes */ - tsleep = (len * 200) + 200; - if (tsleep < delay) - tsleep = delay; - usleep_range(tsleep, tsleep + 1000); - } - else if (delay) - usleep_range(delay, delay + 1000); - - mutex_unlock(&dev->usb_lock); - return ret; -} - -int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - return - tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, - req, value, index, NULL, 0); -} -EXPORT_SYMBOL_GPL(tm6000_set_reg); - -int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - int rc; - u8 buf[1]; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 1); - - if (rc < 0) - return rc; - - return *buf; -} -EXPORT_SYMBOL_GPL(tm6000_get_reg); - -int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, - u16 index, u16 mask) -{ - int rc; - u8 buf[1]; - u8 new_index; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, 0, buf, 1); - - if (rc < 0) - return rc; - - new_index = (buf[0] & ~mask) | (index & mask); - - if (new_index == buf[0]) - return 0; - - return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, - req, value, new_index, NULL, 0); -} -EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); - -int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - int rc; - u8 buf[2]; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 2); - - if (rc < 0) - return rc; - - return buf[1]|buf[0]<<8; -} - -int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - int rc; - u8 buf[4]; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 4); - - if (rc < 0) - return rc; - - return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; -} - -int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep) -{ - int rc; - - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0); - if (rc < 0) - return rc; - - msleep(tsleep); - - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1); - msleep(tsleep); - - return rc; -} - -void tm6000_set_fourcc_format(struct tm6000_core *dev) -{ - if (dev->dev_type == TM6010) { - int val; - - val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc; - if (dev->fourcc == V4L2_PIX_FMT_UYVY) - tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val); - else - tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1); - } else { - if (dev->fourcc == V4L2_PIX_FMT_UYVY) - tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); - else - tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); - } -} - -static void tm6000_set_vbi(struct tm6000_core *dev) -{ - /* - * FIXME: - * VBI lines and start/end are different between 60Hz and 50Hz - * So, it is very likely that we need to change the config to - * something that takes it into account, doing something different - * if (dev->norm & V4L2_STD_525_60) - */ - - if (dev->dev_type == TM6010) { - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); - tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); - tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); - tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); - tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); - tm6000_set_reg(dev, - TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); - tm6000_set_reg(dev, - TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); - tm6000_set_reg(dev, - TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); - tm6000_set_reg(dev, - TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); - tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); - tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); - tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); - tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); - tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); - } -} - -int tm6000_init_analog_mode(struct tm6000_core *dev) -{ - struct v4l2_frequency f; - - if (dev->dev_type == TM6010) { - u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE; - - if (!dev->radio) - active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE; - - /* Enable video and audio */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, - active, 0x60); - /* Disable TS input */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, - 0x00, 0x40); - } else { - /* Enables soft reset */ - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - - if (dev->scaler) - /* Disable Hfilter and Enable TS Drop err */ - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); - else /* Enable Hfilter and disable TS Drop err */ - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); - - tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); - tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); - tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); - tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); - tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); - tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); - - /* AP Software reset */ - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); - - tm6000_set_fourcc_format(dev); - - /* Disables soft reset */ - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); - } - msleep(20); - - /* Tuner firmware can now be loaded */ - - /* - * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected - * for more than a few seconds. Not sure why, as this behavior does - * not happen on other devices with xc3028. So, I suspect that it - * is yet another bug at tm6000. After start sleeping, decoding - * doesn't start automatically. Instead, it requires some - * I2C commands to wake it up. As we want to have image at the - * beginning, we needed to add this hack. The better would be to - * discover some way to make tm6000 to wake up without this hack. - */ - f.frequency = dev->freq; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); - - msleep(100); - tm6000_set_standard(dev); - tm6000_set_vbi(dev); - tm6000_set_audio_bitrate(dev, 48000); - - /* switch dvb led off */ - if (dev->gpio.dvb_led) { - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.dvb_led, 0x01); - } - - return 0; -} - -int tm6000_init_digital_mode(struct tm6000_core *dev) -{ - if (dev->dev_type == TM6010) { - /* Disable video and audio */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, - 0x00, 0x60); - /* Enable TS input */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, - 0x40, 0x40); - /* all power down, but not the digital data port */ - tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); - tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); - tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); - } else { - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); - tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); - tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); - tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); - tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); - tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); - tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); - tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); - tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); - - tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); - tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); - msleep(50); - - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); - msleep(50); - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); - msleep(50); - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); - msleep(100); - } - - /* switch dvb led on */ - if (dev->gpio.dvb_led) { - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.dvb_led, 0x00); - } - - return 0; -} -EXPORT_SYMBOL(tm6000_init_digital_mode); - -struct reg_init { - u8 req; - u8 reg; - u8 val; -}; - -/* The meaning of those initializations are unknown */ -static struct reg_init tm6000_init_tab[] = { - /* REG VALUE */ - { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, - { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, - { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, - { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, - { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, - { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, - { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, - { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, - { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, - { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, - { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ - { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, - - { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, - { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, - { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, - { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, - { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, - { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, - { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, - { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, - { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, - { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, - { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, - { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, - { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, - { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, - { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, - { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, - { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, - { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, - { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, - { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, - { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, - { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, - { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, - { TM6010_REQ07_RC3_HSTART1, 0x88 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ - { TM6010_REQ05_R18_IMASK7, 0x00 }, -}; - -static struct reg_init tm6010_init_tab[] = { - { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, - { TM6010_REQ07_RC4_HSTART0, 0xa0 }, - { TM6010_REQ07_RC6_HEND0, 0x40 }, - { TM6010_REQ07_RCA_VEND0, 0x31 }, - { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 }, - { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, - { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, - - { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, - { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, - { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, - { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, - { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, - { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, - { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, - { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, - { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, - - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, - { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, - { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, - { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, - { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, - { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, - { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, - { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, - { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, - { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, - { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, - { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, - { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, - { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, - { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, - { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, - { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, - { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, - { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, - { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, - { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, - { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, - { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, - { TM6010_REQ07_RC3_HSTART1, 0x88 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - - { TM6010_REQ05_R18_IMASK7, 0x00 }, - - { TM6010_REQ07_RDC_IR_LEADER1, 0xaa }, - { TM6010_REQ07_RDD_IR_LEADER0, 0x30 }, - { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 }, - { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 }, - { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, - { TM6010_REQ07_RD8_IR, 0x0f }, - - /* set remote wakeup key:any key wakeup */ - { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, - { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff }, -}; - -int tm6000_init(struct tm6000_core *dev) -{ - int board, rc = 0, i, size; - struct reg_init *tab; - - /* Check board revision */ - board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); - if (board >= 0) { - switch (board & 0xff) { - case 0xf3: - printk(KERN_INFO "Found tm6000\n"); - if (dev->dev_type != TM6000) - dev->dev_type = TM6000; - break; - case 0xf4: - printk(KERN_INFO "Found tm6010\n"); - if (dev->dev_type != TM6010) - dev->dev_type = TM6010; - break; - default: - printk(KERN_INFO "Unknown board version = 0x%08x\n", board); - } - } else - printk(KERN_ERR "Error %i while retrieving board version\n", board); - - if (dev->dev_type == TM6010) { - tab = tm6010_init_tab; - size = ARRAY_SIZE(tm6010_init_tab); - } else { - tab = tm6000_init_tab; - size = ARRAY_SIZE(tm6000_init_tab); - } - - /* Load board's initialization table */ - for (i = 0; i < size; i++) { - rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); - if (rc < 0) { - printk(KERN_ERR "Error %i while setting req %d, " - "reg %d to value %d\n", rc, - tab[i].req, tab[i].reg, tab[i].val); - return rc; - } - } - - msleep(5); /* Just to be conservative */ - - rc = tm6000_cards_setup(dev); - - return rc; -} - - -int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) -{ - int val = 0; - u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ - u8 areg_0a = 0x91; /* SIF 48KHz */ - - switch (bitrate) { - case 48000: - areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ - areg_0a = 0x91; /* SIF 48KHz */ - dev->audio_bitrate = bitrate; - break; - case 32000: - areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ - areg_0a = 0x90; /* SIF 32KHz */ - dev->audio_bitrate = bitrate; - break; - default: - return -EINVAL; - } - - - /* enable I2S, if we use sif or external I2S device */ - if (dev->dev_type == TM6010) { - val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); - if (val < 0) - return val; - - val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - areg_f0, 0xf0); - if (val < 0) - return val; - } else { - val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, - areg_f0, 0xf0); - if (val < 0) - return val; - } - return 0; -} -EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); - -int tm6000_set_audio_rinput(struct tm6000_core *dev) -{ - if (dev->dev_type == TM6010) { - /* Audio crossbar setting, default SIF1 */ - u8 areg_f0; - u8 areg_07 = 0x10; - - switch (dev->rinput.amux) { - case TM6000_AMUX_SIF1: - case TM6000_AMUX_SIF2: - areg_f0 = 0x03; - areg_07 = 0x30; - break; - case TM6000_AMUX_ADC1: - areg_f0 = 0x00; - break; - case TM6000_AMUX_ADC2: - areg_f0 = 0x08; - break; - case TM6000_AMUX_I2S: - areg_f0 = 0x04; - break; - default: - printk(KERN_INFO "%s: audio input dosn't support\n", - dev->name); - return 0; - break; - } - /* Set audio input crossbar */ - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - areg_f0, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - areg_07, 0xf0); - } else { - u8 areg_eb; - /* Audio setting, default LINE1 */ - switch (dev->rinput.amux) { - case TM6000_AMUX_ADC1: - areg_eb = 0x00; - break; - case TM6000_AMUX_ADC2: - areg_eb = 0x04; - break; - default: - printk(KERN_INFO "%s: audio input dosn't support\n", - dev->name); - return 0; - break; - } - /* Set audio input */ - tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, - areg_eb, 0x0f); - } - return 0; -} - -static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) -{ - u8 mute_reg = 0; - - if (mute) - mute_reg = 0x08; - - tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); -} - -static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) -{ - u8 mute_reg = 0; - - if (mute) - mute_reg = 0x20; - - if (dev->dev_type == TM6010) { - tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, - mute_reg, 0x20); - tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, - mute_reg, 0x20); - } else { - tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, - mute_reg, 0x20); - tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, - mute_reg, 0x20); - } -} - -int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) -{ - enum tm6000_mux mux; - - if (dev->radio) - mux = dev->rinput.amux; - else - mux = dev->vinput[dev->input].amux; - - switch (mux) { - case TM6000_AMUX_SIF1: - case TM6000_AMUX_SIF2: - if (dev->dev_type == TM6010) - tm6010_set_mute_sif(dev, mute); - else { - printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" - " SIF audio inputs. Please check the %s" - " configuration.\n", dev->name); - return -EINVAL; - } - break; - case TM6000_AMUX_ADC1: - case TM6000_AMUX_ADC2: - tm6010_set_mute_adc(dev, mute); - break; - default: - return -EINVAL; - break; - } - return 0; -} - -static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) -{ - u8 vol_reg; - - vol_reg = vol & 0x0F; - - if (vol < 0) - vol_reg |= 0x40; - - tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); - tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); -} - -static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) -{ - u8 vol_reg; - - vol_reg = (vol + 0x10) & 0x1f; - - if (dev->dev_type == TM6010) { - tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); - tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); - } else { - tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); - tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); - } -} - -void tm6000_set_volume(struct tm6000_core *dev, int vol) -{ - enum tm6000_mux mux; - - if (dev->radio) { - mux = dev->rinput.amux; - vol += 8; /* Offset to 0 dB */ - } else - mux = dev->vinput[dev->input].amux; - - switch (mux) { - case TM6000_AMUX_SIF1: - case TM6000_AMUX_SIF2: - if (dev->dev_type == TM6010) - tm6010_set_volume_sif(dev, vol); - else - printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" - " SIF audio inputs. Please check the %s" - " configuration.\n", dev->name); - break; - case TM6000_AMUX_ADC1: - case TM6000_AMUX_ADC2: - tm6010_set_volume_adc(dev, vol); - break; - default: - break; - } -} - -static LIST_HEAD(tm6000_devlist); -static DEFINE_MUTEX(tm6000_devlist_mutex); - -/* - * tm6000_realease_resource() - */ - -void tm6000_remove_from_devlist(struct tm6000_core *dev) -{ - mutex_lock(&tm6000_devlist_mutex); - list_del(&dev->devlist); - mutex_unlock(&tm6000_devlist_mutex); -}; - -void tm6000_add_into_devlist(struct tm6000_core *dev) -{ - mutex_lock(&tm6000_devlist_mutex); - list_add_tail(&dev->devlist, &tm6000_devlist); - mutex_unlock(&tm6000_devlist_mutex); -}; - -/* - * Extension interface - */ - -static LIST_HEAD(tm6000_extension_devlist); - -int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, - char *buf, int size) -{ - struct tm6000_ops *ops = NULL; - - /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ - - if (!list_empty(&tm6000_extension_devlist)) { - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->fillbuf && ops->type == type) - ops->fillbuf(dev, buf, size); - } - } - - return 0; -} - -int tm6000_register_extension(struct tm6000_ops *ops) -{ - struct tm6000_core *dev = NULL; - - mutex_lock(&tm6000_devlist_mutex); - list_add_tail(&ops->next, &tm6000_extension_devlist); - list_for_each_entry(dev, &tm6000_devlist, devlist) { - ops->init(dev); - printk(KERN_INFO "%s: Initialized (%s) extension\n", - dev->name, ops->name); - } - mutex_unlock(&tm6000_devlist_mutex); - return 0; -} -EXPORT_SYMBOL(tm6000_register_extension); - -void tm6000_unregister_extension(struct tm6000_ops *ops) -{ - struct tm6000_core *dev = NULL; - - mutex_lock(&tm6000_devlist_mutex); - list_for_each_entry(dev, &tm6000_devlist, devlist) - ops->fini(dev); - - printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name); - list_del(&ops->next); - mutex_unlock(&tm6000_devlist_mutex); -} -EXPORT_SYMBOL(tm6000_unregister_extension); - -void tm6000_init_extension(struct tm6000_core *dev) -{ - struct tm6000_ops *ops = NULL; - - mutex_lock(&tm6000_devlist_mutex); - if (!list_empty(&tm6000_extension_devlist)) { - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->init) - ops->init(dev); - } - } - mutex_unlock(&tm6000_devlist_mutex); -} - -void tm6000_close_extension(struct tm6000_core *dev) -{ - struct tm6000_ops *ops = NULL; - - mutex_lock(&tm6000_devlist_mutex); - if (!list_empty(&tm6000_extension_devlist)) { - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->fini) - ops->fini(dev); - } - } - mutex_unlock(&tm6000_devlist_mutex); -} diff --git a/drivers/media/video/tm6000/tm6000-dvb.c b/drivers/media/video/tm6000/tm6000-dvb.c deleted file mode 100644 index e1f3f66e1e63..000000000000 --- a/drivers/media/video/tm6000/tm6000-dvb.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2007 Michel Ludwig - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 "tm6000.h" -#include "tm6000-regs.h" - -#include "zl10353.h" - -#include - -#include "tuner-xc2028.h" -#include "xc5000.h" - -MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); - -MODULE_SUPPORTED_DEVICE("{{Trident, tm5600}," - "{{Trident, tm6000}," - "{{Trident, tm6010}"); - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug message"); - -static inline void print_err_status(struct tm6000_core *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - dprintk(dev, 1, "URB status %d [%s].\n", - status, errmsg); - } else { - dprintk(dev, 1, "URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -static void tm6000_urb_received(struct urb *urb) -{ - int ret; - struct tm6000_core *dev = urb->context; - - switch (urb->status) { - case 0: - case -ETIMEDOUT: - break; - case -ENOENT: - case -ECONNRESET: - case -ESHUTDOWN: - return; - default: - print_err_status(dev, 0, urb->status); - } - - if (urb->actual_length > 0) - dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, - urb->actual_length); - - if (dev->dvb->streams > 0) { - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) { - printk(KERN_ERR "tm6000: error %s\n", __func__); - kfree(urb->transfer_buffer); - usb_free_urb(urb); - } - } -} - -static int tm6000_start_stream(struct tm6000_core *dev) -{ - int ret; - unsigned int pipe, size; - struct tm6000_dvb *dvb = dev->dvb; - - printk(KERN_INFO "tm6000: got start stream request %s\n", __func__); - - if (dev->mode != TM6000_MODE_DIGITAL) { - tm6000_init_digital_mode(dev); - dev->mode = TM6000_MODE_DIGITAL; - } - - dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); - if (dvb->bulk_urb == NULL) { - printk(KERN_ERR "tm6000: couldn't allocate urb\n"); - return -ENOMEM; - } - - pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress - & USB_ENDPOINT_NUMBER_MASK); - - size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); - size = size * 15; /* 512 x 8 or 12 or 15 */ - - dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); - if (dvb->bulk_urb->transfer_buffer == NULL) { - usb_free_urb(dvb->bulk_urb); - printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n"); - return -ENOMEM; - } - - usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, - dvb->bulk_urb->transfer_buffer, - size, - tm6000_urb_received, dev); - - ret = usb_clear_halt(dev->udev, pipe); - if (ret < 0) { - printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", - ret, __func__); - return ret; - } else - printk(KERN_ERR "tm6000: pipe resetted\n"); - -/* mutex_lock(&tm6000_driver.open_close_mutex); */ - ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC); - -/* mutex_unlock(&tm6000_driver.open_close_mutex); */ - if (ret) { - printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", - ret); - - kfree(dvb->bulk_urb->transfer_buffer); - usb_free_urb(dvb->bulk_urb); - return ret; - } - - return 0; -} - -static void tm6000_stop_stream(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb = dev->dvb; - - if (dvb->bulk_urb) { - printk(KERN_INFO "urb killing\n"); - usb_kill_urb(dvb->bulk_urb); - printk(KERN_INFO "urb buffer free\n"); - kfree(dvb->bulk_urb->transfer_buffer); - usb_free_urb(dvb->bulk_urb); - dvb->bulk_urb = NULL; - } -} - -static int tm6000_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct tm6000_core *dev = demux->priv; - struct tm6000_dvb *dvb = dev->dvb; - printk(KERN_INFO "tm6000: got start feed request %s\n", __func__); - - mutex_lock(&dvb->mutex); - if (dvb->streams == 0) { - dvb->streams = 1; -/* mutex_init(&tm6000_dev->streming_mutex); */ - tm6000_start_stream(dev); - } else - ++(dvb->streams); - mutex_unlock(&dvb->mutex); - - return 0; -} - -static int tm6000_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct tm6000_core *dev = demux->priv; - struct tm6000_dvb *dvb = dev->dvb; - - printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__); - - mutex_lock(&dvb->mutex); - - printk(KERN_INFO "stream %#x\n", dvb->streams); - --(dvb->streams); - if (dvb->streams == 0) { - printk(KERN_INFO "stop stream\n"); - tm6000_stop_stream(dev); -/* mutex_destroy(&tm6000_dev->streaming_mutex); */ - } - mutex_unlock(&dvb->mutex); -/* mutex_destroy(&tm6000_dev->streaming_mutex); */ - - return 0; -} - -static int tm6000_dvb_attach_frontend(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb = dev->dvb; - - if (dev->caps.has_zl10353) { - struct zl10353_config config = { - .demod_address = dev->demod_addr, - .no_tuner = 1, - .parallel_ts = 1, - .if2 = 45700, - .disable_i2c_gate_ctrl = 1, - }; - - dvb->frontend = dvb_attach(zl10353_attach, &config, - &dev->i2c_adap); - } else { - printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); - return -1; - } - - return (!dvb->frontend) ? -1 : 0; -} - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static int register_dvb(struct tm6000_core *dev) -{ - int ret = -1; - struct tm6000_dvb *dvb = dev->dvb; - - mutex_init(&dvb->mutex); - - dvb->streams = 0; - - /* attach the frontend */ - ret = tm6000_dvb_attach_frontend(dev); - if (ret < 0) { - printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); - goto err; - } - - ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", - THIS_MODULE, &dev->udev->dev, adapter_nr); - dvb->adapter.priv = dev; - - if (dvb->frontend) { - switch (dev->tuner_type) { - case TUNER_XC2028: { - struct xc2028_config cfg = { - .i2c_adap = &dev->i2c_adap, - .i2c_addr = dev->tuner_addr, - }; - - dvb->frontend->callback = tm6000_tuner_callback; - ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); - if (ret < 0) { - printk(KERN_ERR - "tm6000: couldn't register frontend\n"); - goto adapter_err; - } - - if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { - printk(KERN_ERR "tm6000: couldn't register " - "frontend (xc3028)\n"); - ret = -EINVAL; - goto frontend_err; - } - printk(KERN_INFO "tm6000: XC2028/3028 asked to be " - "attached to frontend!\n"); - break; - } - case TUNER_XC5000: { - struct xc5000_config cfg = { - .i2c_address = dev->tuner_addr, - }; - - dvb->frontend->callback = tm6000_xc5000_callback; - ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); - if (ret < 0) { - printk(KERN_ERR - "tm6000: couldn't register frontend\n"); - goto adapter_err; - } - - if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) { - printk(KERN_ERR "tm6000: couldn't register " - "frontend (xc5000)\n"); - ret = -EINVAL; - goto frontend_err; - } - printk(KERN_INFO "tm6000: XC5000 asked to be " - "attached to frontend!\n"); - break; - } - } - } else - printk(KERN_ERR "tm6000: no frontend found\n"); - - dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING - | DMX_MEMORY_BASED_FILTERING; - dvb->demux.priv = dev; - dvb->demux.filternum = 8; - dvb->demux.feednum = 8; - dvb->demux.start_feed = tm6000_start_feed; - dvb->demux.stop_feed = tm6000_stop_feed; - dvb->demux.write_to_decoder = NULL; - ret = dvb_dmx_init(&dvb->demux); - if (ret < 0) { - printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret); - goto frontend_err; - } - - dvb->dmxdev.filternum = dev->dvb->demux.filternum; - dvb->dmxdev.demux = &dev->dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - - ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); - if (ret < 0) { - printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); - goto dvb_dmx_err; - } - - return 0; - -dvb_dmx_err: - dvb_dmx_release(&dvb->demux); -frontend_err: - if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); - dvb_unregister_frontend(dvb->frontend); - } -adapter_err: - dvb_unregister_adapter(&dvb->adapter); -err: - return ret; -} - -static void unregister_dvb(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb = dev->dvb; - - if (dvb->bulk_urb != NULL) { - struct urb *bulk_urb = dvb->bulk_urb; - - kfree(bulk_urb->transfer_buffer); - bulk_urb->transfer_buffer = NULL; - usb_unlink_urb(bulk_urb); - usb_free_urb(bulk_urb); - } - -/* mutex_lock(&tm6000_driver.open_close_mutex); */ - if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); - dvb_unregister_frontend(dvb->frontend); - } - - dvb_dmxdev_release(&dvb->dmxdev); - dvb_dmx_release(&dvb->demux); - dvb_unregister_adapter(&dvb->adapter); - mutex_destroy(&dvb->mutex); -/* mutex_unlock(&tm6000_driver.open_close_mutex); */ -} - -static int dvb_init(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb; - int rc; - - if (!dev) - return 0; - - if (!dev->caps.has_dvb) - return 0; - - if (dev->udev->speed == USB_SPEED_FULL) { - printk(KERN_INFO "This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)\n"); - return 0; - } - - dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); - if (!dvb) { - printk(KERN_INFO "Cannot allocate memory\n"); - return -ENOMEM; - } - - dev->dvb = dvb; - - rc = register_dvb(dev); - if (rc < 0) { - kfree(dvb); - dev->dvb = NULL; - return 0; - } - - return 0; -} - -static int dvb_fini(struct tm6000_core *dev) -{ - if (!dev) - return 0; - - if (!dev->caps.has_dvb) - return 0; - - if (dev->dvb) { - unregister_dvb(dev); - kfree(dev->dvb); - dev->dvb = NULL; - } - - return 0; -} - -static struct tm6000_ops dvb_ops = { - .type = TM6000_DVB, - .name = "TM6000 dvb Extension", - .init = dvb_init, - .fini = dvb_fini, -}; - -static int __init tm6000_dvb_register(void) -{ - return tm6000_register_extension(&dvb_ops); -} - -static void __exit tm6000_dvb_unregister(void) -{ - tm6000_unregister_extension(&dvb_ops); -} - -module_init(tm6000_dvb_register); -module_exit(tm6000_dvb_unregister); diff --git a/drivers/media/video/tm6000/tm6000-i2c.c b/drivers/media/video/tm6000/tm6000-i2c.c deleted file mode 100644 index c7e23e3dd75e..000000000000 --- a/drivers/media/video/tm6000/tm6000-i2c.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2006-2007 Mauro Carvalho Chehab - * - * Copyright (C) 2007 Michel Ludwig - * - Fix SMBus Read Byte command - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 "tm6000.h" -#include "tm6000-regs.h" -#include -#include -#include "tuner-xc2028.h" - - -/* ----------------------------------------------------------- */ - -static unsigned int i2c_debug; -module_param(i2c_debug, int, 0644); -MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __func__, ##args); } while (0) - -static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, - __u8 reg, char *buf, int len) -{ - int rc; - unsigned int i2c_packet_limit = 16; - - if (dev->dev_type == TM6010) - i2c_packet_limit = 80; - - if (!buf) - return -1; - - if (len < 1 || len > i2c_packet_limit) { - printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", - len, i2c_packet_limit); - return -1; - } - - /* capture mutex */ - rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, - addr | reg << 8, 0, buf, len); - - if (rc < 0) { - /* release mutex */ - return rc; - } - - /* release mutex */ - return rc; -} - -/* Generic read - doesn't work fine with 16bit registers */ -static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, - __u8 reg, char *buf, int len) -{ - int rc; - u8 b[2]; - unsigned int i2c_packet_limit = 16; - - if (dev->dev_type == TM6010) - i2c_packet_limit = 64; - - if (!buf) - return -1; - - if (len < 1 || len > i2c_packet_limit) { - printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", - len, i2c_packet_limit); - return -1; - } - - /* capture mutex */ - if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { - /* - * Workaround an I2C bug when reading from zl10353 - */ - reg -= 1; - len += 1; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); - - *buf = b[1]; - } else { - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); - } - - /* release mutex */ - return rc; -} - -/* - * read from a 16bit register - * for example xc2028, xc3028 or xc3028L - */ -static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, - __u16 reg, char *buf, int len) -{ - int rc; - unsigned char ureg; - - if (!buf || len != 2) - return -1; - - /* capture mutex */ - if (dev->dev_type == TM6010) { - ureg = reg & 0xFF; - rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, - addr | (reg & 0xFF00), 0, &ureg, 1); - - if (rc < 0) { - /* release mutex */ - return rc; - } - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, - reg, 0, buf, len); - } else { - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, - addr, reg, buf, len); - } - - /* release mutex */ - return rc; -} - -static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) -{ - struct tm6000_core *dev = i2c_adap->algo_data; - int addr, rc, i, byte; - - if (num <= 0) - return 0; - for (i = 0; i < num; i++) { - addr = (msgs[i].addr << 1) & 0xff; - i2c_dprintk(2, "%s %s addr=0x%x len=%d:", - (msgs[i].flags & I2C_M_RD) ? "read" : "write", - i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); - if (msgs[i].flags & I2C_M_RD) { - /* read request without preceding register selection */ - /* - * The TM6000 only supports a read transaction - * immediately after a 1 or 2 byte write to select - * a register. We cannot fulfil this request. - */ - i2c_dprintk(2, " read without preceding write not" - " supported"); - rc = -EOPNOTSUPP; - goto err; - } else if (i + 1 < num && msgs[i].len <= 2 && - (msgs[i + 1].flags & I2C_M_RD) && - msgs[i].addr == msgs[i + 1].addr) { - /* 1 or 2 byte write followed by a read */ - if (i2c_debug >= 2) - for (byte = 0; byte < msgs[i].len; byte++) - printk(KERN_CONT " %02x", msgs[i].buf[byte]); - i2c_dprintk(2, "; joined to read %s len=%d:", - i == num - 2 ? "stop" : "nonstop", - msgs[i + 1].len); - - if (msgs[i].len == 2) { - rc = tm6000_i2c_recv_regs16(dev, addr, - msgs[i].buf[0] << 8 | msgs[i].buf[1], - msgs[i + 1].buf, msgs[i + 1].len); - } else { - rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], - msgs[i + 1].buf, msgs[i + 1].len); - } - - i++; - - if (addr == dev->tuner_addr << 1) { - tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); - tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); - } - if (i2c_debug >= 2) - for (byte = 0; byte < msgs[i].len; byte++) - printk(KERN_CONT " %02x", msgs[i].buf[byte]); - } else { - /* write bytes */ - if (i2c_debug >= 2) - for (byte = 0; byte < msgs[i].len; byte++) - printk(KERN_CONT " %02x", msgs[i].buf[byte]); - rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], - msgs[i].buf + 1, msgs[i].len - 1); - } - if (i2c_debug >= 2) - printk(KERN_CONT "\n"); - if (rc < 0) - goto err; - } - - return num; -err: - i2c_dprintk(2, " ERROR: %i\n", rc); - return rc; -} - -static int tm6000_i2c_eeprom(struct tm6000_core *dev) -{ - int i, rc; - unsigned char *p = dev->eedata; - unsigned char bytes[17]; - - dev->i2c_client.addr = 0xa0 >> 1; - dev->eedata_size = 0; - - bytes[16] = '\0'; - for (i = 0; i < sizeof(dev->eedata); ) { - *p = i; - rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); - if (rc < 1) { - if (p == dev->eedata) - goto noeeprom; - else { - printk(KERN_WARNING - "%s: i2c eeprom read error (err=%d)\n", - dev->name, rc); - } - return -EINVAL; - } - dev->eedata_size++; - p++; - if (0 == (i % 16)) - printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); - printk(KERN_CONT " %02x", dev->eedata[i]); - if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) - bytes[i%16] = dev->eedata[i]; - else - bytes[i%16] = '.'; - - i++; - - if (0 == (i % 16)) { - bytes[16] = '\0'; - printk(KERN_CONT " %s\n", bytes); - } - } - if (0 != (i%16)) { - bytes[i%16] = '\0'; - for (i %= 16; i < 16; i++) - printk(KERN_CONT " "); - printk(KERN_CONT " %s\n", bytes); - } - - return 0; - -noeeprom: - printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", - dev->name, rc); - return -EINVAL; -} - -/* ----------------------------------------------------------- */ - -/* - * functionality() - */ -static u32 functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static const struct i2c_algorithm tm6000_algo = { - .master_xfer = tm6000_i2c_xfer, - .functionality = functionality, -}; - -/* ----------------------------------------------------------- */ - -/* - * tm6000_i2c_register() - * register i2c bus - */ -int tm6000_i2c_register(struct tm6000_core *dev) -{ - int rc; - - dev->i2c_adap.owner = THIS_MODULE; - dev->i2c_adap.algo = &tm6000_algo; - dev->i2c_adap.dev.parent = &dev->udev->dev; - strlcpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); - dev->i2c_adap.algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); - rc = i2c_add_adapter(&dev->i2c_adap); - if (rc) - return rc; - - dev->i2c_client.adapter = &dev->i2c_adap; - strlcpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); - tm6000_i2c_eeprom(dev); - - return 0; -} - -/* - * tm6000_i2c_unregister() - * unregister i2c_bus - */ -int tm6000_i2c_unregister(struct tm6000_core *dev) -{ - i2c_del_adapter(&dev->i2c_adap); - return 0; -} diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c deleted file mode 100644 index e80b7e190471..000000000000 --- a/drivers/media/video/tm6000/tm6000-input.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2010 Stefan Ringel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 "tm6000.h" -#include "tm6000-regs.h" - -static unsigned int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "debug message level"); - -static unsigned int enable_ir = 1; -module_param(enable_ir, int, 0644); -MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); - -static unsigned int ir_clock_mhz = 12; -module_param(ir_clock_mhz, int, 0644); -MODULE_PARM_DESC(enable_ir, "ir clock, in MHz"); - -#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */ -#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */ - -#undef dprintk - -#define dprintk(level, fmt, arg...) do {\ - if (ir_debug >= level) \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ - } while (0) - -struct tm6000_ir_poll_result { - u16 rc_data; -}; - -struct tm6000_IR { - struct tm6000_core *dev; - struct rc_dev *rc; - char name[32]; - char phys[32]; - - /* poll expernal decoder */ - int polling; - struct delayed_work work; - u8 wait:1; - u8 pwled:2; - u8 submit_urb:1; - u16 key_addr; - struct urb *int_urb; - - /* IR device properties */ - u64 rc_type; -}; - -void tm6000_ir_wait(struct tm6000_core *dev, u8 state) -{ - struct tm6000_IR *ir = dev->ir; - - if (!dev->ir) - return; - - dprintk(2, "%s: %i\n",__func__, ir->wait); - - if (state) - ir->wait = 1; - else - ir->wait = 0; -} - -static int tm6000_ir_config(struct tm6000_IR *ir) -{ - struct tm6000_core *dev = ir->dev; - u32 pulse = 0, leader = 0; - - dprintk(2, "%s\n",__func__); - - /* - * The IR decoder supports RC-5 or NEC, with a configurable timing. - * The timing configuration there is not that accurate, as it uses - * approximate values. The NEC spec mentions a 562.5 unit period, - * and RC-5 uses a 888.8 period. - * Currently, driver assumes a clock provided by a 12 MHz XTAL, but - * a modprobe parameter can adjust it. - * Adjustments are required for other timings. - * It seems that the 900ms timing for NEC is used to detect a RC-5 - * IR, in order to discard such decoding - */ - - switch (ir->rc_type) { - case RC_TYPE_NEC: - leader = 900; /* ms */ - pulse = 700; /* ms - the actual value would be 562 */ - break; - default: - case RC_TYPE_RC5: - leader = 900; /* ms - from the NEC decoding */ - pulse = 1780; /* ms - The actual value would be 1776 */ - break; - } - - pulse = ir_clock_mhz * pulse; - leader = ir_clock_mhz * leader; - if (ir->rc_type == RC_TYPE_NEC) - leader = leader | 0x8000; - - dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n", - __func__, - (ir->rc_type == RC_TYPE_NEC) ? "NEC" : "RC-5", - ir_clock_mhz, leader, pulse); - - /* Remote WAKEUP = enable, normal mode, from IR decoder output */ - tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); - - /* Enable IR reception on non-busrt mode */ - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f); - - /* IR_WKUP_SEL = Low byte in decoded IR data */ - tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff); - /* IR_WKU_ADD code */ - tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff); - - tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8); - tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader); - - tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8); - tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse); - - if (!ir->polling) - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); - else - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); - msleep(10); - - /* Shows that IR is working via the LED */ - tm6000_flash_led(dev, 0); - msleep(100); - tm6000_flash_led(dev, 1); - ir->pwled = 1; - - return 0; -} - -static void tm6000_ir_urb_received(struct urb *urb) -{ - struct tm6000_core *dev = urb->context; - struct tm6000_IR *ir = dev->ir; - struct tm6000_ir_poll_result poll_result; - char *buf; - - dprintk(2, "%s\n",__func__); - if (urb->status < 0 || urb->actual_length <= 0) { - printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n", - urb->status, urb->actual_length); - ir->submit_urb = 1; - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); - return; - } - buf = urb->transfer_buffer; - - if (ir_debug) - print_hex_dump(KERN_DEBUG, "tm6000: IR data: ", - DUMP_PREFIX_OFFSET,16, 1, - buf, urb->actual_length, false); - - poll_result.rc_data = buf[0]; - if (urb->actual_length > 1) - poll_result.rc_data |= buf[1] << 8; - - dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); - rc_keydown(ir->rc, poll_result.rc_data, 0); - - usb_submit_urb(urb, GFP_ATOMIC); - /* - * Flash the led. We can't do it here, as it is running on IRQ context. - * So, use the scheduler to do it, in a few ms. - */ - ir->pwled = 2; - schedule_delayed_work(&ir->work, msecs_to_jiffies(10)); -} - -static void tm6000_ir_handle_key(struct work_struct *work) -{ - struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); - struct tm6000_core *dev = ir->dev; - struct tm6000_ir_poll_result poll_result; - int rc; - u8 buf[2]; - - if (ir->wait) - return; - - dprintk(3, "%s\n",__func__); - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - REQ_02_GET_IR_CODE, 0, 0, buf, 2); - if (rc < 0) - return; - - if (rc > 1) - poll_result.rc_data = buf[0] | buf[1] << 8; - else - poll_result.rc_data = buf[0]; - - /* Check if something was read */ - if ((poll_result.rc_data & 0xff) == 0xff) { - if (!ir->pwled) { - tm6000_flash_led(dev, 1); - ir->pwled = 1; - } - return; - } - - dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); - rc_keydown(ir->rc, poll_result.rc_data, 0); - tm6000_flash_led(dev, 0); - ir->pwled = 0; - - /* Re-schedule polling */ - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); -} - -static void tm6000_ir_int_work(struct work_struct *work) -{ - struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); - struct tm6000_core *dev = ir->dev; - int rc; - - dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb, - ir->pwled); - - if (ir->submit_urb) { - dprintk(3, "Resubmit urb\n"); - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); - - rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC); - if (rc < 0) { - printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n", - rc); - /* Retry in 100 ms */ - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); - return; - } - ir->submit_urb = 0; - } - - /* Led is enabled only if USB submit doesn't fail */ - if (ir->pwled == 2) { - tm6000_flash_led(dev, 0); - ir->pwled = 0; - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY)); - } else if (!ir->pwled) { - tm6000_flash_led(dev, 1); - ir->pwled = 1; - } -} - -static int tm6000_ir_start(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - - dprintk(2, "%s\n",__func__); - - schedule_delayed_work(&ir->work, 0); - - return 0; -} - -static void tm6000_ir_stop(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - - dprintk(2, "%s\n",__func__); - - cancel_delayed_work_sync(&ir->work); -} - -static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type) -{ - struct tm6000_IR *ir = rc->priv; - - if (!ir) - return 0; - - dprintk(2, "%s\n",__func__); - - if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC)) - ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff); - - ir->rc_type = rc_type; - - tm6000_ir_config(ir); - /* TODO */ - return 0; -} - -static int __tm6000_ir_int_start(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - struct tm6000_core *dev = ir->dev; - int pipe, size; - int err = -ENOMEM; - - if (!ir) - return -ENODEV; - - dprintk(2, "%s\n",__func__); - - ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!ir->int_urb) - return -ENOMEM; - - pipe = usb_rcvintpipe(dev->udev, - dev->int_in.endp->desc.bEndpointAddress - & USB_ENDPOINT_NUMBER_MASK); - - size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); - dprintk(1, "IR max size: %d\n", size); - - ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC); - if (ir->int_urb->transfer_buffer == NULL) { - usb_free_urb(ir->int_urb); - return err; - } - dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval); - - usb_fill_int_urb(ir->int_urb, dev->udev, pipe, - ir->int_urb->transfer_buffer, size, - tm6000_ir_urb_received, dev, - dev->int_in.endp->desc.bInterval); - - ir->submit_urb = 1; - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); - - return 0; -} - -static void __tm6000_ir_int_stop(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - - if (!ir || !ir->int_urb) - return; - - dprintk(2, "%s\n",__func__); - - usb_kill_urb(ir->int_urb); - kfree(ir->int_urb->transfer_buffer); - usb_free_urb(ir->int_urb); - ir->int_urb = NULL; -} - -int tm6000_ir_int_start(struct tm6000_core *dev) -{ - struct tm6000_IR *ir = dev->ir; - - if (!ir) - return 0; - - return __tm6000_ir_int_start(ir->rc); -} - -void tm6000_ir_int_stop(struct tm6000_core *dev) -{ - struct tm6000_IR *ir = dev->ir; - - if (!ir || !ir->rc) - return; - - __tm6000_ir_int_stop(ir->rc); -} - -int tm6000_ir_init(struct tm6000_core *dev) -{ - struct tm6000_IR *ir; - struct rc_dev *rc; - int err = -ENOMEM; - - if (!enable_ir) - return -ENODEV; - - if (!dev->caps.has_remote) - return 0; - - if (!dev->ir_codes) - return 0; - - ir = kzalloc(sizeof(*ir), GFP_ATOMIC); - rc = rc_allocate_device(); - if (!ir || !rc) - goto out; - - dprintk(2, "%s\n", __func__); - - /* record handles to ourself */ - ir->dev = dev; - dev->ir = ir; - ir->rc = rc; - - /* input setup */ - rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC; - /* Neded, in order to support NEC remotes with 24 or 32 bits */ - rc->scanmask = 0xffff; - rc->priv = ir; - rc->change_protocol = tm6000_ir_change_protocol; - if (dev->int_in.endp) { - rc->open = __tm6000_ir_int_start; - rc->close = __tm6000_ir_int_stop; - INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work); - } else { - rc->open = tm6000_ir_start; - rc->close = tm6000_ir_stop; - ir->polling = 50; - INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key); - } - rc->driver_type = RC_DRIVER_SCANCODE; - - snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", - dev->name); - - usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); - strlcat(ir->phys, "/input0", sizeof(ir->phys)); - - tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN); - - rc->input_name = ir->name; - rc->input_phys = ir->phys; - rc->input_id.bustype = BUS_USB; - rc->input_id.version = 1; - rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - rc->map_name = dev->ir_codes; - rc->driver_name = "tm6000"; - rc->dev.parent = &dev->udev->dev; - - /* ir register */ - err = rc_register_device(rc); - if (err) - goto out; - - return 0; - -out: - dev->ir = NULL; - rc_free_device(rc); - kfree(ir); - return err; -} - -int tm6000_ir_fini(struct tm6000_core *dev) -{ - struct tm6000_IR *ir = dev->ir; - - /* skip detach on non attached board */ - - if (!ir) - return 0; - - dprintk(2, "%s\n",__func__); - - if (!ir->polling) - __tm6000_ir_int_stop(ir->rc); - - tm6000_ir_stop(ir->rc); - - /* Turn off the led */ - tm6000_flash_led(dev, 0); - ir->pwled = 0; - - rc_unregister_device(ir->rc); - - kfree(ir); - dev->ir = NULL; - - return 0; -} diff --git a/drivers/media/video/tm6000/tm6000-regs.h b/drivers/media/video/tm6000/tm6000-regs.h deleted file mode 100644 index a38c251ed57b..000000000000 --- a/drivers/media/video/tm6000/tm6000-regs.h +++ /dev/null @@ -1,600 +0,0 @@ -/* - * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2006-2007 Mauro Carvalho Chehab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 TV Master TM5600/TM6000/TM6010 Request codes - */ -#define REQ_00_SET_IR_VALUE 0 -#define REQ_01_SET_WAKEUP_IRCODE 1 -#define REQ_02_GET_IR_CODE 2 -#define REQ_03_SET_GET_MCU_PIN 3 -#define REQ_04_EN_DISABLE_MCU_INT 4 -#define REQ_05_SET_GET_USBREG 5 - /* Write: RegNum, Value, 0 */ - /* Read : RegNum, Value, 1, RegStatus */ -#define REQ_06_SET_GET_USBREG_BIT 6 -#define REQ_07_SET_GET_AVREG 7 - /* Write: RegNum, Value, 0 */ - /* Read : RegNum, Value, 1, RegStatus */ -#define REQ_08_SET_GET_AVREG_BIT 8 -#define REQ_09_SET_GET_TUNER_FQ 9 -#define REQ_10_SET_TUNER_SYSTEM 10 -#define REQ_11_SET_EEPROM_ADDR 11 -#define REQ_12_SET_GET_EEPROMBYTE 12 -#define REQ_13_GET_EEPROM_SEQREAD 13 -#define REQ_14_SET_GET_I2C_WR2_RDN 14 -#define REQ_15_SET_GET_I2CBYTE 15 - /* Write: Subaddr, Slave Addr, value, 0 */ - /* Read : Subaddr, Slave Addr, value, 1 */ -#define REQ_16_SET_GET_I2C_WR1_RDN 16 - /* Subaddr, Slave Addr, 0, length */ -#define REQ_17_SET_GET_I2CFP 17 - /* Write: Slave Addr, register, value */ - /* Read : Slave Addr, register, 2, data */ -#define REQ_20_DATA_TRANSFER 20 -#define REQ_30_I2C_WRITE 30 -#define REQ_31_I2C_READ 31 -#define REQ_35_AFTEK_TUNER_READ 35 -#define REQ_40_GET_VERSION 40 -#define REQ_50_SET_START 50 -#define REQ_51_SET_STOP 51 -#define REQ_52_TRANSMIT_DATA 52 -#define REQ_53_SPI_INITIAL 53 -#define REQ_54_SPI_SETSTART 54 -#define REQ_55_SPI_INOUTDATA 55 -#define REQ_56_SPI_SETSTOP 56 - -/* - * Define TV Master TM5600/TM6000/TM6010 GPIO lines - */ - -#define TM6000_GPIO_CLK 0x101 -#define TM6000_GPIO_DATA 0x100 - -#define TM6000_GPIO_1 0x102 -#define TM6000_GPIO_2 0x103 -#define TM6000_GPIO_3 0x104 -#define TM6000_GPIO_4 0x300 -#define TM6000_GPIO_5 0x301 -#define TM6000_GPIO_6 0x304 -#define TM6000_GPIO_7 0x305 - -/* tm6010 defines GPIO with different values */ -#define TM6010_GPIO_0 0x0102 -#define TM6010_GPIO_1 0x0103 -#define TM6010_GPIO_2 0x0104 -#define TM6010_GPIO_3 0x0105 -#define TM6010_GPIO_4 0x0106 -#define TM6010_GPIO_5 0x0107 -#define TM6010_GPIO_6 0x0300 -#define TM6010_GPIO_7 0x0301 -#define TM6010_GPIO_9 0x0305 -/* - * Define TV Master TM5600/TM6000/TM6010 URB message codes and length - */ - -enum { - TM6000_URB_MSG_VIDEO = 1, - TM6000_URB_MSG_AUDIO, - TM6000_URB_MSG_VBI, - TM6000_URB_MSG_PTS, - TM6000_URB_MSG_ERR, -}; - -/* Define specific TM6000 Video decoder registers */ -#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 -#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 -#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda -#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb -#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc -#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd -#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde -#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf -#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 -#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 -#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 -#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 -#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 -#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 -#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 -#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 -#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 -#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 -#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea -#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb -#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec -#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed -#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee -#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef -#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd -#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe - -/* Define TM6000/TM6010 Video decoder registers */ -#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 -#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 -#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02 -#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03 -#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04 -#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05 -#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06 -#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07 -#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08 -#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09 -#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a -#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b -#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c -#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d -#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f -#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10 -#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11 -#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12 -#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13 -#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14 -#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15 -#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16 -#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17 -#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18 -#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19 -#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a -#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b -#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c -#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d -#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e -#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f -#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20 -#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21 -#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22 -#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23 -#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24 -#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25 -#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26 -#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27 -#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28 -#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29 -#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a -#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b -#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c -#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d -#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e -#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f -#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30 -#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31 -#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32 -#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33 -#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34 -#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35 -#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36 -#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37 -#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38 -#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39 -#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a -#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b -#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c -#define TM6010_REQ07_R3F_RESET 0x07, 0x3f -#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40 -#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41 -#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42 -#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43 -#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44 -#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45 -#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46 -#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47 -#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48 -#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49 -#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a -#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b -#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c -#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d -#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e -#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f -#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50 -#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51 -#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52 -#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53 -#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54 -#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55 -#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56 -#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57 -#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58 -#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59 -#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a -#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b -#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c -#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d -#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e -#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f -#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60 -#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61 -#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62 -#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63 -#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64 -#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65 -#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66 -#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67 -#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68 -#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70 -#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71 -#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72 -#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73 -#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74 -#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75 -#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76 -#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77 -#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78 -#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79 -#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a -#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b -#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c -#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d -#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f -#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80 -#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82 -#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83 -#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84 -#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85 -#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86 -#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87 -#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a -#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b -#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d -#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e -#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f - -/* Define TM6000/TM6010 Miscellaneous registers */ -#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0 -#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1 -#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2 -#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3 -#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4 -#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5 -#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6 -#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7 -#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8 -#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 -#define TM6010_REQ07_RCA_VEND0 0x07, 0xca -#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb -/* ONLY for TM6010 */ -#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc -#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5) -#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6) -#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 -#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 -#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2 -#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3 -#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4 -#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 -#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 -#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RD8_IR 0x07, 0xd8 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RD9_IR_BSIZE 0x07, 0xd9 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDA_IR_WAKEUP_SEL 0x07, 0xda -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDB_IR_WAKEUP_ADD 0x07, 0xdb -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDC_IR_LEADER1 0x07, 0xdc -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDD_IR_LEADER0 0x07, 0xdd -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDE_IR_PULSE_CNT1 0x07, 0xde -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDF_IR_PULSE_CNT0 0x07, 0xdf -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 -/* ONLY for TM6010 */ -#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF7_BIST 0x07, 0xf7 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe -#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff - -/* Define TM6000/TM6010 USB registers */ -#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00 -#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01 -#define TM6010_REQ05_R02_TEST 0x05, 0x02 -#define TM6010_REQ05_R04_SOFN0 0x05, 0x04 -#define TM6010_REQ05_R05_SOFN1 0x05, 0x05 -#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06 -#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07 -#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08 -#define TM6010_REQ05_R09_VCTL 0x05, 0x09 -#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a -#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b -#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c -#define TM6010_REQ05_R10_GMASK 0x05, 0x10 -#define TM6010_REQ05_R11_IMASK0 0x05, 0x11 -#define TM6010_REQ05_R12_IMASK1 0x05, 0x12 -#define TM6010_REQ05_R13_IMASK2 0x05, 0x13 -#define TM6010_REQ05_R14_IMASK3 0x05, 0x14 -#define TM6010_REQ05_R15_IMASK4 0x05, 0x15 -#define TM6010_REQ05_R16_IMASK5 0x05, 0x16 -#define TM6010_REQ05_R17_IMASK6 0x05, 0x17 -#define TM6010_REQ05_R18_IMASK7 0x05, 0x18 -#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19 -#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a -#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c -#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d -#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20 -#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21 -#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22 -#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23 -#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24 -#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25 -#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26 -#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27 -#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28 -#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29 -#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a -#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b -#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c -#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d -#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e -#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f -#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30 -#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31 -#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32 -#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33 -#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34 -#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35 -#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36 -#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37 -#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38 -#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39 -#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a -#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b -#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c -#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d -#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e -#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41 -#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42 -#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43 -#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44 -#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45 -#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46 -#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47 -#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48 -#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49 -#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a -#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b -#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c -#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d -#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e -#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f -#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50 -#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51 -#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53 -#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55 -#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57 -#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59 -#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a -#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b -#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c -#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d -#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61 -#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62 -#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63 -#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64 -#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65 -#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66 -#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67 -#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68 -#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69 -#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a -#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b -#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c -#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d -#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e -#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f -#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70 -#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b -#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d -#define TM6010_REQ05_R80_FIFO0 0x05, 0x80 -#define TM6010_REQ05_R81_FIFO1 0x05, 0x81 -#define TM6010_REQ05_R82_FIFO2 0x05, 0x82 -#define TM6010_REQ05_R83_FIFO3 0x05, 0x83 -#define TM6010_REQ05_R84_FIFO4 0x05, 0x84 -#define TM6010_REQ05_R85_FIFO5 0x05, 0x85 -#define TM6010_REQ05_R86_FIFO6 0x05, 0x86 -#define TM6010_REQ05_R87_FIFO7 0x05, 0x87 -#define TM6010_REQ05_R88_FIFO8 0x05, 0x88 -#define TM6010_REQ05_R89_FIFO9 0x05, 0x89 -#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a -#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b -#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c -#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d -#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e -#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f -#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90 -#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91 -#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92 -#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93 -#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94 -#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95 -#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96 -#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97 -#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98 -#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99 -#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a -#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b -#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c -#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d -#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e -#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f -#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0 -#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1 -#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2 -#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3 -#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4 -#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5 -#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6 -#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7 -#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8 -#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9 -#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa -#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab -#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac -#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad -#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae -#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf -#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0 -#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1 -#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2 -#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3 -#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4 -#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5 -#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6 -#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7 -#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8 -#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9 -#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba -#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb -#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc -#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd -#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe -#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf -#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0 -#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4 -#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8 -#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc -#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0 -#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4 -#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8 -#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc -#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0 -#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4 -#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8 -#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec -#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0 -#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4 -#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 -#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc - -/* Define TM6010 Audio decoder registers */ -/* This core available only in TM6010 */ -#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 -#define TM6010_REQ08_R01_A_INIT 0x08, 0x01 -#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 -#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03 -#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04 -#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05 -#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06 -#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07 -#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08 -#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09 -#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a -#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b -#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c -#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d -#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e -#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f -#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10 -#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11 -#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12 -#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13 -#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14 -#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15 -#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16 -#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17 -#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18 -#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19 -#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a -#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b -#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e -#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f -#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20 -#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21 -#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22 -#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23 -#define TM6010_REQ08_R24_A_SER 0x08, 0x24 -#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25 -#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26 -#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 -#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 - -/* Define TM6010 Video ADC registers */ -#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 -#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 -#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 -#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3 -#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4 -#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5 -#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6 -#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7 -#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8 -#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9 -#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea -#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb -#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec -#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed - -/* Define TM6010 Audio ADC registers */ -#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 -#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 -#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 -#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3 diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c deleted file mode 100644 index 5e28d6a2412f..000000000000 --- a/drivers/media/video/tm6000/tm6000-stds.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2007 Mauro Carvalho Chehab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 "tm6000.h" -#include "tm6000-regs.h" - -static unsigned int tm6010_a_mode; -module_param(tm6010_a_mode, int, 0644); -MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode"); - -struct tm6000_reg_settings { - unsigned char req; - unsigned char reg; - unsigned char value; -}; - - -struct tm6000_std_settings { - v4l2_std_id id; - struct tm6000_reg_settings *common; -}; - -static struct tm6000_reg_settings composite_pal_m[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_pal_nc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_pal[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_secam[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_ntsc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_std_settings composite_stds[] = { - { .id = V4L2_STD_PAL_M, .common = composite_pal_m, }, - { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, }, - { .id = V4L2_STD_PAL, .common = composite_pal, }, - { .id = V4L2_STD_SECAM, .common = composite_secam, }, - { .id = V4L2_STD_NTSC, .common = composite_ntsc, }, -}; - -static struct tm6000_reg_settings svideo_pal_m[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_pal_nc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_pal[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_secam[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_ntsc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, - { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_std_settings svideo_stds[] = { - { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, }, - { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, }, - { .id = V4L2_STD_PAL, .common = svideo_pal, }, - { .id = V4L2_STD_SECAM, .common = svideo_secam, }, - { .id = V4L2_STD_NTSC, .common = svideo_ntsc, }, -}; - -static int tm6000_set_audio_std(struct tm6000_core *dev) -{ - uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ - uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ - uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */ - - if (dev->radio) { - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); - tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); - tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); - /* set mono or stereo */ - if (dev->amode == V4L2_TUNER_MODE_MONO) - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); - else if (dev->amode == V4L2_TUNER_MODE_STEREO) - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02); - tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); - tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); - tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); - tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); - tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff); - return 0; - } - - /* - * STD/MN shouldn't be affected by tm6010_a_mode, as there's just one - * audio standard for each V4L2_STD type. - */ - if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_KR) { - areg_05 |= 0x04; - } else if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_JP) { - areg_05 |= 0x43; - } else if (dev->norm & V4L2_STD_MN) { - areg_05 |= 0x22; - } else switch (tm6010_a_mode) { - /* auto */ - case 0: - if ((dev->norm & V4L2_STD_SECAM) == V4L2_STD_SECAM_L) - areg_05 |= 0x00; - else /* Other PAL/SECAM standards */ - areg_05 |= 0x10; - break; - /* A2 */ - case 1: - if (dev->norm & V4L2_STD_DK) - areg_05 = 0x09; - else - areg_05 = 0x05; - break; - /* NICAM */ - case 2: - if (dev->norm & V4L2_STD_DK) { - areg_05 = 0x06; - } else if (dev->norm & V4L2_STD_PAL_I) { - areg_05 = 0x08; - } else if (dev->norm & V4L2_STD_SECAM_L) { - areg_05 = 0x0a; - areg_02 = 0x02; - } else { - areg_05 = 0x07; - } - break; - /* other */ - case 3: - if (dev->norm & V4L2_STD_DK) { - areg_05 = 0x0b; - } else { - areg_05 = 0x02; - } - break; - } - - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02); - tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0); - tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05); - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06); - tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08); - tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); - tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20); - tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12); - tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20); - tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0); - tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80); - tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0); - tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80); - tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12); - tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20); - tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14); - tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); - tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0); - tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32); - tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64); - tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20); - tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00); - tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); - tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); - - return 0; -} - -void tm6000_get_std_res(struct tm6000_core *dev) -{ - /* Currently, those are the only supported resoltions */ - if (dev->norm & V4L2_STD_525_60) - dev->height = 480; - else - dev->height = 576; - - dev->width = 720; -} - -static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set) -{ - int i, rc; - - /* Load board's initialization table */ - for (i = 0; set[i].req; i++) { - rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); - if (rc < 0) { - printk(KERN_ERR "Error %i while setting " - "req %d, reg %d to value %d\n", - rc, set[i].req, set[i].reg, set[i].value); - return rc; - } - } - - return 0; -} - -int tm6000_set_standard(struct tm6000_core *dev) -{ - struct tm6000_input *input; - int i, rc = 0; - u8 reg_07_fe = 0x8a; - u8 reg_08_f1 = 0xfc; - u8 reg_08_e2 = 0xf0; - u8 reg_08_e6 = 0x0f; - - tm6000_get_std_res(dev); - - if (!dev->radio) - input = &dev->vinput[dev->input]; - else - input = &dev->rinput; - - if (dev->dev_type == TM6010) { - switch (input->vmux) { - case TM6000_VMUX_VIDEO_A: - tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4); - tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); - tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); - tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); - tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); - reg_07_fe |= 0x01; - break; - case TM6000_VMUX_VIDEO_B: - tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8); - tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); - tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); - tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); - tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); - reg_07_fe |= 0x01; - break; - case TM6000_VMUX_VIDEO_AB: - tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc); - tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8); - reg_08_e6 = 0x00; - tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2); - tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0); - tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); - tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0); - break; - default: - break; - } - switch (input->amux) { - case TM6000_AMUX_ADC1: - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x00, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x10, 0xf0); - break; - case TM6000_AMUX_ADC2: - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x08, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x10, 0xf0); - break; - case TM6000_AMUX_SIF1: - reg_08_e2 |= 0x02; - reg_08_e6 = 0x08; - reg_07_fe |= 0x40; - reg_08_f1 |= 0x02; - tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3); - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x02, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x30, 0xf0); - break; - case TM6000_AMUX_SIF2: - reg_08_e2 |= 0x02; - reg_08_e6 = 0x08; - reg_07_fe |= 0x40; - reg_08_f1 |= 0x02; - tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7); - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x02, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x30, 0xf0); - break; - default: - break; - } - tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2); - tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6); - tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1); - tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe); - } else { - switch (input->vmux) { - case TM6000_VMUX_VIDEO_A: - tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); - tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); - tm6000_set_reg(dev, - REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); - break; - case TM6000_VMUX_VIDEO_B: - tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); - tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); - tm6000_set_reg(dev, - REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); - break; - case TM6000_VMUX_VIDEO_AB: - tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); - tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00); - tm6000_set_reg(dev, - REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1); - break; - default: - break; - } - switch (input->amux) { - case TM6000_AMUX_ADC1: - tm6000_set_reg_mask(dev, - TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f); - break; - case TM6000_AMUX_ADC2: - tm6000_set_reg_mask(dev, - TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f); - break; - default: - break; - } - } - if (input->type == TM6000_INPUT_SVIDEO) { - for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { - if (dev->norm & svideo_stds[i].id) { - rc = tm6000_load_std(dev, svideo_stds[i].common); - goto ret; - } - } - return -EINVAL; - } else { - for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { - if (dev->norm & composite_stds[i].id) { - rc = tm6000_load_std(dev, composite_stds[i].common); - goto ret; - } - } - return -EINVAL; - } - -ret: - if (rc < 0) - return rc; - - if ((dev->dev_type == TM6010) && - ((input->amux == TM6000_AMUX_SIF1) || - (input->amux == TM6000_AMUX_SIF2))) - tm6000_set_audio_std(dev); - - msleep(40); - - return 0; -} diff --git a/drivers/media/video/tm6000/tm6000-usb-isoc.h b/drivers/media/video/tm6000/tm6000-usb-isoc.h deleted file mode 100644 index 99d15a55aa03..000000000000 --- a/drivers/media/video/tm6000/tm6000-usb-isoc.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2006-2007 Mauro Carvalho Chehab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 - -#define TM6000_URB_MSG_LEN 180 - -struct usb_isoc_ctl { - /* max packet size of isoc transaction */ - int max_pkt_size; - - /* number of allocated urbs */ - int num_bufs; - - /* urb for isoc transfers */ - struct urb **urb; - - /* transfer buffers for isoc transfer */ - char **transfer_buffer; - - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int vfield, field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - - /* Stores already requested buffers */ - struct tm6000_buffer *buf; -}; diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c deleted file mode 100644 index 45ed59cb2d04..000000000000 --- a/drivers/media/video/tm6000/tm6000-video.c +++ /dev/null @@ -1,1852 +0,0 @@ -/* - * tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2006-2007 Mauro Carvalho Chehab - * - * Copyright (C) 2007 Michel Ludwig - * - Fixed module load/unload - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "tm6000-regs.h" -#include "tm6000.h" - -#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ - -/* Limits minimum and default number of buffers */ -#define TM6000_MIN_BUF 4 -#define TM6000_DEF_BUF 8 - -#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ - -/* Declare static vars that will be used as parameters */ -static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ -static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ -static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ - -/* Debug level */ -int tm6000_debug; -EXPORT_SYMBOL_GPL(tm6000_debug); - -static const struct v4l2_queryctrl no_ctrl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -/* supported controls */ -static struct v4l2_queryctrl tm6000_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 54, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 119, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 112, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = 0, - }, - /* --- audio --- */ - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = -15, - .maximum = 15, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - } -}; - -static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl); -static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; - -static struct tm6000_fmt format[] = { - { - .name = "4:2:2, packed, YVY2", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - }, { - .name = "A/V + VBI mux packet", - .fourcc = V4L2_PIX_FMT_TM6000, - .depth = 16, - } -}; - -static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) -{ - unsigned int i; - - for (i = 0; i < CTRLS; i++) - if (tm6000_qctrl[i].id == id) - return tm6000_qctrl+i; - return NULL; -} - -/* ------------------------------------------------------------------ - * DMA and thread functions - * ------------------------------------------------------------------ - */ - -#define norm_maxw(a) 720 -#define norm_maxh(a) 576 - -#define norm_minw(a) norm_maxw(a) -#define norm_minh(a) norm_maxh(a) - -/* - * video-buf generic routine to get the next available buffer - */ -static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, - struct tm6000_buffer **buf) -{ - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - - if (list_empty(&dma_q->active)) { - dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); - *buf = NULL; - return; - } - - *buf = list_entry(dma_q->active.next, - struct tm6000_buffer, vb.queue); -} - -/* - * Announces that a buffer were filled and request the next - */ -static inline void buffer_filled(struct tm6000_core *dev, - struct tm6000_dmaqueue *dma_q, - struct tm6000_buffer *buf) -{ - /* Advice that buffer was filled */ - dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -/* - * Identify the tm5600/6000 buffer header type and properly handles - */ -static int copy_streams(u8 *data, unsigned long len, - struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - u8 *ptr = data, *endp = data+len; - unsigned long header = 0; - int rc = 0; - unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; - struct tm6000_buffer *vbuf = NULL; - char *voutp = NULL; - unsigned int linewidth; - - if (!dev->radio) { - /* get video buffer */ - get_next_buf(dma_q, &vbuf); - - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - - if (!voutp) - return 0; - } - - for (ptr = data; ptr < endp;) { - if (!dev->isoc_ctl.cmd) { - /* Header */ - if (dev->isoc_ctl.tmp_buf_len > 0) { - /* from last urb or packet */ - header = dev->isoc_ctl.tmp_buf; - if (4 - dev->isoc_ctl.tmp_buf_len > 0) { - memcpy((u8 *)&header + - dev->isoc_ctl.tmp_buf_len, - ptr, - 4 - dev->isoc_ctl.tmp_buf_len); - ptr += 4 - dev->isoc_ctl.tmp_buf_len; - } - dev->isoc_ctl.tmp_buf_len = 0; - } else { - if (ptr + 3 >= endp) { - /* have incomplete header */ - dev->isoc_ctl.tmp_buf_len = endp - ptr; - memcpy(&dev->isoc_ctl.tmp_buf, ptr, - dev->isoc_ctl.tmp_buf_len); - return rc; - } - /* Seek for sync */ - for (; ptr < endp - 3; ptr++) { - if (*(ptr + 3) == 0x47) - break; - } - /* Get message header */ - header = *(unsigned long *)ptr; - ptr += 4; - } - - /* split the header fields */ - size = ((header & 0x7e) << 1); - if (size > 0) - size -= 4; - block = (header >> 7) & 0xf; - field = (header >> 11) & 0x1; - line = (header >> 12) & 0x1ff; - cmd = (header >> 21) & 0x7; - /* Validates haeder fields */ - if (size > TM6000_URB_MSG_LEN) - size = TM6000_URB_MSG_LEN; - pktsize = TM6000_URB_MSG_LEN; - /* - * calculate position in buffer and change the buffer - */ - switch (cmd) { - case TM6000_URB_MSG_VIDEO: - if (!dev->radio) { - if ((dev->isoc_ctl.vfield != field) && - (field == 1)) { - /* - * Announces that a new buffer - * were filled - */ - buffer_filled(dev, dma_q, vbuf); - dprintk(dev, V4L2_DEBUG_ISOC, - "new buffer filled\n"); - get_next_buf(dma_q, &vbuf); - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - if (!voutp) - return rc; - memset(voutp, 0, vbuf->vb.size); - } - linewidth = vbuf->vb.width << 1; - pos = ((line << 1) - field - 1) * - linewidth + block * TM6000_URB_MSG_LEN; - /* Don't allow to write out of the buffer */ - if (pos + size > vbuf->vb.size) - cmd = TM6000_URB_MSG_ERR; - dev->isoc_ctl.vfield = field; - } - break; - case TM6000_URB_MSG_VBI: - break; - case TM6000_URB_MSG_AUDIO: - case TM6000_URB_MSG_PTS: - size = pktsize; /* Size is always 180 bytes */ - break; - } - } else { - /* Continue the last copy */ - cmd = dev->isoc_ctl.cmd; - size = dev->isoc_ctl.size; - pos = dev->isoc_ctl.pos; - pktsize = dev->isoc_ctl.pktsize; - field = dev->isoc_ctl.field; - } - cpysize = (endp - ptr > size) ? size : endp - ptr; - if (cpysize) { - /* copy data in different buffers */ - switch (cmd) { - case TM6000_URB_MSG_VIDEO: - /* Fills video buffer */ - if (vbuf) - memcpy(&voutp[pos], ptr, cpysize); - break; - case TM6000_URB_MSG_AUDIO: { - int i; - for (i = 0; i < cpysize; i += 2) - swab16s((u16 *)(ptr + i)); - - tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize); - break; - } - case TM6000_URB_MSG_VBI: - /* Need some code to copy vbi buffer */ - break; - case TM6000_URB_MSG_PTS: { - /* Need some code to copy pts */ - u32 pts; - pts = *(u32 *)ptr; - dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x", - field, pts); - break; - } - } - } - if (ptr + pktsize > endp) { - /* - * End of URB packet, but cmd processing is not - * complete. Preserve the state for a next packet - */ - dev->isoc_ctl.pos = pos + cpysize; - dev->isoc_ctl.size = size - cpysize; - dev->isoc_ctl.cmd = cmd; - dev->isoc_ctl.field = field; - dev->isoc_ctl.pktsize = pktsize - (endp - ptr); - ptr += endp - ptr; - } else { - dev->isoc_ctl.cmd = 0; - ptr += pktsize; - } - } - return 0; -} - -/* - * Identify the tm5600/6000 buffer header type and properly handles - */ -static int copy_multiplexed(u8 *ptr, unsigned long len, - struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - unsigned int pos = dev->isoc_ctl.pos, cpysize; - int rc = 1; - struct tm6000_buffer *buf; - char *outp = NULL; - - get_next_buf(dma_q, &buf); - if (buf) - outp = videobuf_to_vmalloc(&buf->vb); - - if (!outp) - return 0; - - while (len > 0) { - cpysize = min(len, buf->vb.size-pos); - memcpy(&outp[pos], ptr, cpysize); - pos += cpysize; - ptr += cpysize; - len -= cpysize; - if (pos >= buf->vb.size) { - pos = 0; - /* Announces that a new buffer were filled */ - buffer_filled(dev, dma_q, buf); - dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); - get_next_buf(dma_q, &buf); - if (!buf) - break; - outp = videobuf_to_vmalloc(&(buf->vb)); - if (!outp) - return rc; - pos = 0; - } - } - - dev->isoc_ctl.pos = pos; - return rc; -} - -static inline void print_err_status(struct tm6000_core *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronuously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronuously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", - status, errmsg); - } else { - dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - - -/* - * Controls the isoc copy of each urb packet - */ -static inline int tm6000_isoc_copy(struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - int i, len = 0, rc = 1, status; - char *p; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - return 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - continue; - } - - len = urb->iso_frame_desc[i].actual_length; - - if (len > 0) { - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - if (!urb->iso_frame_desc[i].status) { - if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) { - rc = copy_multiplexed(p, len, urb); - if (rc <= 0) - return rc; - } else { - copy_streams(p, len, urb); - } - } - } - } - return rc; -} - -/* ------------------------------------------------------------------ - * URB control - * ------------------------------------------------------------------ - */ - -/* - * IRQ callback, called by URB callback - */ -static void tm6000_irq_callback(struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - int i; - - switch (urb->status) { - case 0: - case -ETIMEDOUT: - break; - - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - return; - - default: - tm6000_err("urb completion error %d.\n", urb->status); - break; - } - - spin_lock(&dev->slock); - tm6000_isoc_copy(urb); - spin_unlock(&dev->slock); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) - tm6000_err("urb resubmit failed (error=%i)\n", - urb->status); -} - -/* - * Stop and Deallocate URBs - */ -static void tm6000_uninit_isoc(struct tm6000_core *dev) -{ - struct urb *urb; - int i; - - dev->isoc_ctl.buf = NULL; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; - if (urb) { - usb_kill_urb(urb); - usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } - usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; - } - dev->isoc_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - dev->isoc_ctl.num_bufs = 0; -} - -/* - * Allocate URBs and start IRQ - */ -static int tm6000_prepare_isoc(struct tm6000_core *dev) -{ - struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; - struct urb *urb; - - /* De-allocates all pending stuff */ - tm6000_uninit_isoc(dev); - /* Stop interrupt USB pipe */ - tm6000_ir_int_stop(dev); - - usb_set_interface(dev->udev, - dev->isoc_in.bInterfaceNumber, - dev->isoc_in.bAlternateSetting); - - /* Start interrupt USB pipe */ - tm6000_ir_int_start(dev); - - pipe = usb_rcvisocpipe(dev->udev, - dev->isoc_in.endp->desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK); - - size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); - - if (size > dev->isoc_in.maxsize) - size = dev->isoc_in.maxsize; - - dev->isoc_ctl.max_pkt_size = size; - - max_packets = TM6000_MAX_ISO_PACKETS; - sb_size = max_packets * size; - - dev->isoc_ctl.num_bufs = num_bufs; - - dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - tm6000_err("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { - tm6000_err("cannot allocate memory for usbtransfer\n"); - kfree(dev->isoc_ctl.urb); - return -ENOMEM; - } - - dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets" - " (%d bytes) of %d bytes each to handle %u size\n", - max_packets, num_bufs, sb_size, - dev->isoc_in.maxsize, size); - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) { - tm6000_err("cannot alloc isoc_ctl.urb %i\n", i); - tm6000_uninit_isoc(dev); - usb_free_urb(urb); - return -ENOMEM; - } - dev->isoc_ctl.urb[i] = urb; - - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - tm6000_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - tm6000_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); - - usb_fill_bulk_urb(urb, dev->udev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, - tm6000_irq_callback, dma_q); - urb->interval = dev->isoc_in.endp->desc.bInterval; - urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - for (j = 0; j < max_packets; j++) { - urb->iso_frame_desc[j].offset = size * j; - urb->iso_frame_desc[j].length = size; - } - } - - return 0; -} - -static int tm6000_start_thread(struct tm6000_core *dev) -{ - struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i; - - dma_q->frame = 0; - dma_q->ini_jiffies = jiffies; - - init_waitqueue_head(&dma_q->wq); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); - if (rc) { - tm6000_err("submit of urb %i failed (error=%i)\n", i, - rc); - tm6000_uninit_isoc(dev); - return rc; - } - } - - return 0; -} - -/* ------------------------------------------------------------------ - * Videobuf operations - * ------------------------------------------------------------------ - */ - -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) -{ - struct tm6000_fh *fh = vq->priv_data; - - *size = fh->fmt->depth * fh->width * fh->height >> 3; - if (0 == *count) - *count = TM6000_DEF_BUF; - - if (*count < TM6000_MIN_BUF) - *count = TM6000_MIN_BUF; - - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) -{ - struct tm6000_fh *fh = vq->priv_data; - struct tm6000_core *dev = fh->dev; - unsigned long flags; - - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct tm6000_fh *fh = vq->priv_data; - struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); - struct tm6000_core *dev = fh->dev; - int rc = 0; - - BUG_ON(NULL == fh->fmt); - - - /* FIXME: It assumes depth=2 */ - /* The only currently supported format is 16 bits/pixel */ - buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - if (buf->fmt != fh->fmt || - buf->vb.width != fh->width || - buf->vb.height != fh->height || - buf->vb.field != field) { - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; - buf->vb.field = field; - buf->vb.state = VIDEOBUF_NEEDS_INIT; - } - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc != 0) - goto fail; - } - - if (!dev->isoc_ctl.num_bufs) { - rc = tm6000_prepare_isoc(dev); - if (rc < 0) - goto fail; - - rc = tm6000_start_thread(dev); - if (rc < 0) - goto fail; - - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(vq, buf); - return rc; -} - -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); - struct tm6000_fh *fh = vq->priv_data; - struct tm6000_core *dev = fh->dev; - struct tm6000_dmaqueue *vidq = &dev->vidq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); -} - -static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); - - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops tm6000_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* ------------------------------------------------------------------ - * IOCTL handling - * ------------------------------------------------------------------ - */ - -static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources == fh && dev->is_res_read) - return true; - - return false; -} - -static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources == fh) - return true; - - return false; -} - -static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh, - bool is_res_read) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources == fh && dev->is_res_read == is_res_read) - return true; - - /* is it free? */ - if (dev->resources) - return false; - - /* grab it */ - dev->resources = fh; - dev->is_res_read = is_res_read; - dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); - return true; -} - -static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources != fh) - return; - - dev->resources = NULL; - dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); -} - -/* ------------------------------------------------------------------ - * IOCTL vidioc handling - * ------------------------------------------------------------------ - */ -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; - - strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); - strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; - - if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (unlikely(f->index >= ARRAY_SIZE(format))) - return -EINVAL; - - strlcpy(f->description, format[f->index].name, sizeof(f->description)); - f->pixelformat = format[f->index].fourcc; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tm6000_fh *fh = priv; - - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - - return 0; -} - -static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(format); i++) - if (format[i].fourcc == fourcc) - return format+i; - return NULL; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; - struct tm6000_fmt *fmt; - enum v4l2_field field; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (NULL == fmt) { - dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" - " invalid.\n", f->fmt.pix.pixelformat); - return -EINVAL; - } - - field = f->fmt.pix.field; - - if (field == V4L2_FIELD_ANY) - field = V4L2_FIELD_SEQ_TB; - else if (V4L2_FIELD_INTERLACED != field) { - dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); - return -EINVAL; - } - - tm6000_get_std_res(dev); - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - - f->fmt.pix.width &= ~0x01; - - f->fmt.pix.field = field; - - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - - return 0; -} - -/*FIXME: This seems to be generic enough to be at videodev2 */ -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - int ret = vidioc_try_fmt_vid_cap(file, fh, f); - if (ret < 0) - return ret; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; - - dev->fourcc = f->fmt.pix.pixelformat; - - tm6000_set_fourcc_format(dev); - - return 0; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_reqbufs(&fh->vb_vidq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_querybuf(&fh->vb_vidq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_qbuf(&fh->vb_vidq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_dqbuf(&fh->vb_vidq, p, - file->f_flags & O_NONBLOCK); -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - if (!res_get(dev, fh, false)) - return -EBUSY; - return videobuf_streamon(&fh->vb_vidq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (i != fh->type) - return -EINVAL; - - videobuf_streamoff(&fh->vb_vidq); - res_free(dev, fh); - - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - int rc = 0; - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - dev->norm = *norm; - rc = tm6000_init_analog_mode(dev); - - fh->width = dev->width; - fh->height = dev->height; - - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); - - return 0; -} - -static const char *iname[] = { - [TM6000_INPUT_TV] = "Television", - [TM6000_INPUT_COMPOSITE1] = "Composite 1", - [TM6000_INPUT_COMPOSITE2] = "Composite 2", - [TM6000_INPUT_SVIDEO] = "S-Video", -}; - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - unsigned int n; - - n = i->index; - if (n >= 3) - return -EINVAL; - - if (!dev->vinput[n].type) - return -EINVAL; - - i->index = n; - - if (dev->vinput[n].type == TM6000_INPUT_TV) - i->type = V4L2_INPUT_TYPE_TUNER; - else - i->type = V4L2_INPUT_TYPE_CAMERA; - - strcpy(i->name, iname[dev->vinput[n].type]); - - i->std = TM6000_STD; - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - *i = dev->input; - - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - int rc = 0; - - if (i >= 3) - return -EINVAL; - if (!dev->vinput[i].type) - return -EINVAL; - - dev->input = i; - - rc = vidioc_s_std(file, priv, &dev->vfd->current_norm); - - return rc; -} - -/* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) - if (qc->id && qc->id == tm6000_qctrl[i].id) { - memcpy(qc, &(tm6000_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - int val; - - /* FIXME: Probably, those won't work! Maybe we need shadow regs */ - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0); - break; - case V4L2_CID_BRIGHTNESS: - val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0); - return 0; - case V4L2_CID_SATURATION: - val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0); - return 0; - case V4L2_CID_HUE: - val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); - return 0; - case V4L2_CID_AUDIO_MUTE: - val = dev->ctl_mute; - return 0; - case V4L2_CID_AUDIO_VOLUME: - val = dev->ctl_volume; - return 0; - default: - return -EINVAL; - } - - if (val < 0) - return val; - - ctrl->value = val; - - return 0; -} -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - u8 val = ctrl->value; - - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); - return 0; - case V4L2_CID_BRIGHTNESS: - tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); - return 0; - case V4L2_CID_SATURATION: - tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); - return 0; - case V4L2_CID_HUE: - tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); - return 0; - case V4L2_CID_AUDIO_MUTE: - dev->ctl_mute = val; - tm6000_tvaudio_set_mute(dev, val); - return 0; - case V4L2_CID_AUDIO_VOLUME: - dev->ctl_volume = val; - tm6000_set_volume(dev, val); - return 0; - } - return -EINVAL; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - - strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; - t->rxsubchans = V4L2_TUNER_SUB_STEREO; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - - t->audmode = dev->amode; - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (UNSET == dev->tuner_type) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - - dev->amode = t->audmode; - dprintk(dev, 3, "audio mode: %x\n", t->audmode); - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; - - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = dev->freq; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); - - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; - if (unlikely(f->tuner != 0)) - return -EINVAL; - if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; - if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) - return -EINVAL; - - dev->freq = f->frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); - - return 0; -} - -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - strcpy(cap->driver, "tm6000"); - strlcpy(cap->card, dev->name, sizeof(dev->name)); - sprintf(cap->bus_info, "USB%04x:%04x", - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct)); - cap->version = dev->dev_type; - cap->capabilities = V4L2_CAP_TUNER | - V4L2_CAP_AUDIO | - V4L2_CAP_RADIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - return 0; -} - -static int radio_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - if (0 != t->index) - return -EINVAL; - - memset(t, 0, sizeof(*t)); - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; - t->rxsubchans = V4L2_TUNER_SUB_STEREO; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - - return 0; -} - -static int radio_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - if (0 != t->index) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - - return 0; -} - -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (i->index != 0) - return -EINVAL; - - if (!dev->rinput.type) - return -EINVAL; - - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_input(struct file *filp, void *priv, unsigned int *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (dev->input != 5) - return -EINVAL; - - *i = dev->input - 5; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - memset(a, 0, sizeof(*a)); - strcpy(a->name, "Radio"); - return 0; -} - -static int radio_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *filp, void *priv, unsigned int i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (i) - return -EINVAL; - - if (!dev->rinput.type) - return -EINVAL; - - dev->input = i + 5; - - return 0; -} - -static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - return 0; -} - -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - const struct v4l2_queryctrl *ctrl; - - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { - ctrl = ctrl_by_id(c->id); - *c = *ctrl; - } else - *c = no_ctrl; - - return 0; -} - -/* ------------------------------------------------------------------ - File operations for the device - ------------------------------------------------------------------*/ - -static int __tm6000_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct tm6000_core *dev = video_drvdata(file); - struct tm6000_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int i, rc; - int radio = 0; - - dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", - video_device_node_name(vdev)); - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - case VFL_TYPE_RADIO: - radio = 1; - break; - } - - /* If more than one user, mutex should be added */ - dev->users++; - - dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[type], - dev->users); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - dev->users--; - return -ENOMEM; - } - - file->private_data = fh; - fh->dev = dev; - fh->radio = radio; - dev->radio = radio; - fh->type = type; - dev->fourcc = format[0].fourcc; - - fh->fmt = format_by_fourcc(dev->fourcc); - - tm6000_get_std_res(dev); - - fh->width = dev->width; - fh->height = dev->height; - - dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, " - "dev->vidq=0x%08lx\n", - (unsigned long)fh, (unsigned long)dev, - (unsigned long)&dev->vidq); - dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " - "queued=%d\n", list_empty(&dev->vidq.queued)); - dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " - "active=%d\n", list_empty(&dev->vidq.active)); - - /* initialize hardware on analog mode */ - rc = tm6000_init_analog_mode(dev); - if (rc < 0) - return rc; - - if (dev->mode != TM6000_MODE_ANALOG) { - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) - qctl_regs[i] = tm6000_qctrl[i].default_value; - - dev->mode = TM6000_MODE_ANALOG; - } - - if (!fh->radio) { - videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, - NULL, &dev->slock, - fh->type, - V4L2_FIELD_INTERLACED, - sizeof(struct tm6000_buffer), fh, &dev->lock); - } else { - dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); - dev->input = 5; - tm6000_set_audio_rinput(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); - tm6000_prepare_isoc(dev); - tm6000_start_thread(dev); - } - - return 0; -} - -static int tm6000_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - int res; - - mutex_lock(vdev->lock); - res = __tm6000_open(file); - mutex_unlock(vdev->lock); - return res; -} - -static ssize_t -tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - int res; - - if (!res_get(fh->dev, fh, true)) - return -EBUSY; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - res = videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, - file->f_flags & O_NONBLOCK); - mutex_unlock(&dev->lock); - return res; - } - return 0; -} - -static unsigned int -__tm6000_poll(struct file *file, struct poll_table_struct *wait) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_buffer *buf; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return POLLERR; - - if (!!is_res_streaming(fh->dev, fh)) - return POLLERR; - - if (!is_res_read(fh->dev, fh)) { - /* streaming capture */ - if (list_empty(&fh->vb_vidq.stream)) - return POLLERR; - buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); - } else { - /* read() capture */ - return videobuf_poll_stream(file, &fh->vb_vidq, wait); - } - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - return 0; -} - -static unsigned int tm6000_poll(struct file *file, struct poll_table_struct *wait) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - unsigned int res; - - mutex_lock(&dev->lock); - res = __tm6000_poll(file, wait); - mutex_unlock(&dev->lock); - return res; -} - -static int tm6000_release(struct file *file) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - struct video_device *vdev = video_devdata(file); - - dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n", - video_device_node_name(vdev), dev->users); - - mutex_lock(&dev->lock); - dev->users--; - - res_free(dev, fh); - - if (!dev->users) { - tm6000_uninit_isoc(dev); - - /* Stop interrupt USB pipe */ - tm6000_ir_int_stop(dev); - - usb_reset_configuration(dev->udev); - - if (dev->int_in.endp) - usb_set_interface(dev->udev, - dev->isoc_in.bInterfaceNumber, 2); - else - usb_set_interface(dev->udev, - dev->isoc_in.bInterfaceNumber, 0); - - /* Start interrupt USB pipe */ - tm6000_ir_int_start(dev); - - if (!fh->radio) - videobuf_mmap_free(&fh->vb_vidq); - } - - kfree(fh); - mutex_unlock(&dev->lock); - - return 0; -} - -static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - int res; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - res = videobuf_mmap_mapper(&fh->vb_vidq, vma); - mutex_unlock(&dev->lock); - return res; -} - -static struct v4l2_file_operations tm6000_fops = { - .owner = THIS_MODULE, - .open = tm6000_open, - .release = tm6000_release, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .read = tm6000_read, - .poll = tm6000_poll, - .mmap = tm6000_mmap, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_s_std = vidioc_s_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -}; - -static struct video_device tm6000_template = { - .name = "tm6000", - .fops = &tm6000_fops, - .ioctl_ops = &video_ioctl_ops, - .release = video_device_release, - .tvnorms = TM6000_STD, - .current_norm = V4L2_STD_NTSC_M, -}; - -static const struct v4l2_file_operations radio_fops = { - .owner = THIS_MODULE, - .open = tm6000_open, - .release = tm6000_release, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, - .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, - .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_s_std = radio_s_std, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_input = radio_g_input, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -}; - -static struct video_device tm6000_radio_template = { - .name = "tm6000", - .fops = &radio_fops, - .ioctl_ops = &radio_ioctl_ops, -}; - -/* ----------------------------------------------------------------- - * Initialization and module stuff - * ------------------------------------------------------------------ - */ - -static struct video_device *vdev_init(struct tm6000_core *dev, - const struct video_device - *template, const char *type_name) -{ - struct video_device *vfd; - - vfd = video_device_alloc(); - if (NULL == vfd) - return NULL; - - *vfd = *template; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; - vfd->debug = tm6000_debug; - vfd->lock = &dev->lock; - - snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); - - video_set_drvdata(vfd, dev); - return vfd; -} - -int tm6000_v4l2_register(struct tm6000_core *dev) -{ - int ret = -1; - - dev->vfd = vdev_init(dev, &tm6000_template, "video"); - - if (!dev->vfd) { - printk(KERN_INFO "%s: can't register video device\n", - dev->name); - return -ENOMEM; - } - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); - - ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); - - if (ret < 0) { - printk(KERN_INFO "%s: can't register video device\n", - dev->name); - return ret; - } - - printk(KERN_INFO "%s: registered device %s\n", - dev->name, video_device_node_name(dev->vfd)); - - if (dev->caps.has_radio) { - dev->radio_dev = vdev_init(dev, &tm6000_radio_template, - "radio"); - if (!dev->radio_dev) { - printk(KERN_INFO "%s: can't register radio device\n", - dev->name); - return ret; /* FIXME release resource */ - } - - ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, - radio_nr); - if (ret < 0) { - printk(KERN_INFO "%s: can't register radio device\n", - dev->name); - return ret; /* FIXME release resource */ - } - - printk(KERN_INFO "%s: registered device %s\n", - dev->name, video_device_node_name(dev->radio_dev)); - } - - printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); - return ret; -} - -int tm6000_v4l2_unregister(struct tm6000_core *dev) -{ - video_unregister_device(dev->vfd); - - if (dev->radio_dev) { - if (video_is_registered(dev->radio_dev)) - video_unregister_device(dev->radio_dev); - else - video_device_release(dev->radio_dev); - dev->radio_dev = NULL; - } - - return 0; -} - -int tm6000_v4l2_exit(void) -{ - return 0; -} - -module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr, "Allow changing video device number"); - -module_param_named(debug, tm6000_debug, int, 0444); -MODULE_PARM_DESC(debug, "activates debug info"); - -module_param(vid_limit, int, 0644); -MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); - diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h deleted file mode 100644 index 6df418658c9c..000000000000 --- a/drivers/media/video/tm6000/tm6000.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2006-2007 Mauro Carvalho Chehab - * - * Copyright (C) 2007 Michel Ludwig - * - DVB-T support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 "tm6000-usb-isoc.h" -#include -#include -#include - -#include -#include "dvb_demux.h" -#include "dvb_frontend.h" -#include "dmxdev.h" - -/* Inputs */ -enum tm6000_itype { - TM6000_INPUT_TV = 1, - TM6000_INPUT_COMPOSITE1, - TM6000_INPUT_COMPOSITE2, - TM6000_INPUT_SVIDEO, - TM6000_INPUT_DVB, - TM6000_INPUT_RADIO, -}; - -enum tm6000_mux { - TM6000_VMUX_VIDEO_A = 1, - TM6000_VMUX_VIDEO_B, - TM6000_VMUX_VIDEO_AB, - TM6000_AMUX_ADC1, - TM6000_AMUX_ADC2, - TM6000_AMUX_SIF1, - TM6000_AMUX_SIF2, - TM6000_AMUX_I2S, -}; - -enum tm6000_devtype { - TM6000 = 0, - TM5600, - TM6010, -}; - -struct tm6000_input { - enum tm6000_itype type; - enum tm6000_mux vmux; - enum tm6000_mux amux; - unsigned int v_gpio; - unsigned int a_gpio; -}; - -/* ------------------------------------------------------------------ - * Basic structures - * ------------------------------------------------------------------ - */ - -struct tm6000_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ - int depth; -}; - -/* buffer for one video frame */ -struct tm6000_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - struct tm6000_fmt *fmt; -}; - -struct tm6000_dmaqueue { - struct list_head active; - struct list_head queued; - - /* thread for generating video stream*/ - struct task_struct *kthread; - wait_queue_head_t wq; - /* Counters to control fps rate */ - int frame; - int ini_jiffies; -}; - -/* device states */ -enum tm6000_core_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -/* io methods */ -enum tm6000_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum tm6000_mode { - TM6000_MODE_UNKNOWN = 0, - TM6000_MODE_ANALOG, - TM6000_MODE_DIGITAL, -}; - -struct tm6000_gpio { - int tuner_reset; - int tuner_on; - int demod_reset; - int demod_on; - int power_led; - int dvb_led; - int ir; -}; - -struct tm6000_capabilities { - unsigned int has_tuner:1; - unsigned int has_tda9874:1; - unsigned int has_dvb:1; - unsigned int has_zl10353:1; - unsigned int has_eeprom:1; - unsigned int has_remote:1; - unsigned int has_radio:1; -}; - -struct tm6000_dvb { - struct dvb_adapter adapter; - struct dvb_demux demux; - struct dvb_frontend *frontend; - struct dmxdev dmxdev; - unsigned int streams; - struct urb *bulk_urb; - struct mutex mutex; -}; - -struct snd_tm6000_card { - struct snd_card *card; - spinlock_t reg_lock; - struct tm6000_core *core; - struct snd_pcm_substream *substream; - - /* temporary data for buffer fill processing */ - unsigned buf_pos; - unsigned period_pos; -}; - -struct tm6000_endpoint { - struct usb_host_endpoint *endp; - __u8 bInterfaceNumber; - __u8 bAlternateSetting; - unsigned maxsize; -}; - -#define TM6000_QUIRK_NO_USB_DELAY (1 << 0) - -struct tm6000_core { - /* generic device properties */ - char name[30]; /* name (including minor) of the device */ - int model; /* index in the device_data struct */ - int devno; /* marks the number of this device */ - enum tm6000_devtype dev_type; /* type of device */ - unsigned char eedata[256]; /* Eeprom data */ - unsigned eedata_size; /* Size of the eeprom info */ - - v4l2_std_id norm; /* Current norm */ - int width, height; /* Selected resolution */ - - enum tm6000_core_state state; - - /* Device Capabilities*/ - struct tm6000_capabilities caps; - - /* Used to load alsa/dvb */ - struct work_struct request_module_wk; - - /* Tuner configuration */ - int tuner_type; /* type of the tuner */ - int tuner_addr; /* tuner address */ - - struct tm6000_gpio gpio; - - char *ir_codes; - - __u8 radio; - - /* Demodulator configuration */ - int demod_addr; /* demodulator address */ - - int audio_bitrate; - /* i2c i/o */ - struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; - - - /* extension */ - struct list_head devlist; - - /* video for linux */ - int users; - - /* various device info */ - struct tm6000_fh *resources; /* Points to fh that is streaming */ - bool is_res_read; - - struct video_device *vfd; - struct video_device *radio_dev; - struct tm6000_dmaqueue vidq; - struct v4l2_device v4l2_dev; - - int input; - struct tm6000_input vinput[3]; /* video input */ - struct tm6000_input rinput; /* radio input */ - - int freq; - unsigned int fourcc; - - enum tm6000_mode mode; - - int ctl_mute; /* audio */ - int ctl_volume; - int amode; - - /* DVB-T support */ - struct tm6000_dvb *dvb; - - /* audio support */ - struct snd_tm6000_card *adev; - struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ - atomic_t stream_started; /* stream should be running if true */ - - struct tm6000_IR *ir; - - /* locks */ - struct mutex lock; - struct mutex usb_lock; - - /* usb transfer */ - struct usb_device *udev; /* the usb device */ - - struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; - struct tm6000_endpoint int_in, int_out; - - /* scaler!=0 if scaler is active*/ - int scaler; - - /* Isoc control struct */ - struct usb_isoc_ctl isoc_ctl; - - spinlock_t slock; - - unsigned long quirks; -}; - -enum tm6000_ops_type { - TM6000_AUDIO = 0x10, - TM6000_DVB = 0x20, -}; - -struct tm6000_ops { - struct list_head next; - char *name; - enum tm6000_ops_type type; - int (*init)(struct tm6000_core *); - int (*fini)(struct tm6000_core *); - int (*fillbuf)(struct tm6000_core *, char *buf, int size); -}; - -struct tm6000_fh { - struct tm6000_core *dev; - unsigned int radio; - - /* video capture */ - struct tm6000_fmt *fmt; - unsigned int width, height; - struct videobuf_queue vb_vidq; - - enum v4l2_buf_type type; -}; - -#define TM6000_STD (V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ - V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ - V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM) - -/* In tm6000-cards.c */ - -int tm6000_tuner_callback(void *ptr, int component, int command, int arg); -int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); -int tm6000_cards_setup(struct tm6000_core *dev); -void tm6000_flash_led(struct tm6000_core *dev, u8 state); - -/* In tm6000-core.c */ - -int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req, - u16 value, u16 index, u8 *buf, u16 len); -int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, - u16 index, u16 mask); -int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); -int tm6000_init(struct tm6000_core *dev); -int tm6000_reset(struct tm6000_core *dev); - -int tm6000_init_analog_mode(struct tm6000_core *dev); -int tm6000_init_digital_mode(struct tm6000_core *dev); -int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); -int tm6000_set_audio_rinput(struct tm6000_core *dev); -int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); -void tm6000_set_volume(struct tm6000_core *dev, int vol); - -int tm6000_v4l2_register(struct tm6000_core *dev); -int tm6000_v4l2_unregister(struct tm6000_core *dev); -int tm6000_v4l2_exit(void); -void tm6000_set_fourcc_format(struct tm6000_core *dev); - -void tm6000_remove_from_devlist(struct tm6000_core *dev); -void tm6000_add_into_devlist(struct tm6000_core *dev); -int tm6000_register_extension(struct tm6000_ops *ops); -void tm6000_unregister_extension(struct tm6000_ops *ops); -void tm6000_init_extension(struct tm6000_core *dev); -void tm6000_close_extension(struct tm6000_core *dev); -int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, - char *buf, int size); - - -/* In tm6000-stds.c */ -void tm6000_get_std_res(struct tm6000_core *dev); -int tm6000_set_standard(struct tm6000_core *dev); - -/* In tm6000-i2c.c */ -int tm6000_i2c_register(struct tm6000_core *dev); -int tm6000_i2c_unregister(struct tm6000_core *dev); - -/* In tm6000-queue.c */ - -int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); - -int tm6000_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i); -int tm6000_vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i); -int tm6000_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb); -int tm6000_vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b); -int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); -int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); -ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, - loff_t *f_pos); -unsigned int tm6000_v4l2_poll(struct file *file, - struct poll_table_struct *wait); -int tm6000_queue_init(struct tm6000_core *dev); - -/* In tm6000-alsa.c */ -/*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/ - -/* In tm6000-input.c */ -int tm6000_ir_init(struct tm6000_core *dev); -int tm6000_ir_fini(struct tm6000_core *dev); -void tm6000_ir_wait(struct tm6000_core *dev, u8 state); -int tm6000_ir_int_start(struct tm6000_core *dev); -void tm6000_ir_int_stop(struct tm6000_core *dev); - -/* Debug stuff */ - -extern int tm6000_debug; - -#define dprintk(dev, level, fmt, arg...) do {\ - if (tm6000_debug & level) \ - printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ - dev->name, __func__ , ##arg); } while (0) - -#define V4L2_DEBUG_REG 0x0004 -#define V4L2_DEBUG_I2C 0x0008 -#define V4L2_DEBUG_QUEUE 0x0010 -#define V4L2_DEBUG_ISOC 0x0020 -#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ -#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ - -#define tm6000_err(fmt, arg...) do {\ - printk(KERN_ERR "tm6000 %s :"fmt, \ - __func__ , ##arg); } while (0) diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig deleted file mode 100644 index fc24ef05b3f3..000000000000 --- a/drivers/media/video/usbvision/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config VIDEO_USBVISION - tristate "USB video devices based on Nogatech NT1003/1004/1005" - depends on I2C && VIDEO_V4L2 - select VIDEO_TUNER - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - ---help--- - There are more than 50 different USB video devices based on - NT1003/1004/1005 USB Bridges. This driver enables using those - devices. - - To compile this driver as a module, choose M here: the - module will be called usbvision. diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile deleted file mode 100644 index d55c6bd97a35..000000000000 --- a/drivers/media/video/usbvision/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o - -obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o - -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c deleted file mode 100644 index 3103d0d020e8..000000000000 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ /dev/null @@ -1,1133 +0,0 @@ -/* - * usbvision-cards.c - * usbvision cards definition file - * - * Copyright (c) 1999-2005 Joerg Heckenbach - * - * This module is part of usbvision driver project. - * Updates to driver completed by Dwaine P. Garden - * - * This program is free software; 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 "usbvision.h" -#include "usbvision-cards.h" - -/* Supported Devices: A table for usbvision.c*/ -struct usbvision_device_data_st usbvision_device_data[] = { - [XANBOO] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 4, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = -1, - .y_offset = -1, - .model_string = "Xanboo", - }, - [BELKIN_VIDEOBUS_II] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Belkin USB VideoBus II Adapter", - }, - [BELKIN_VIDEOBUS] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = -1, - .y_offset = -1, - .model_string = "Belkin Components USB VideoBus", - }, - [BELKIN_USB_VIDEOBUS_II] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Belkin USB VideoBus II", - }, - [ECHOFX_INTERVIEW_LITE] = { - .interface = 0, - .codec = CODEC_SAA7111, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = -1, - .y_offset = -1, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "echoFX InterView Lite", - }, - [USBGEAR_USBG_V1] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = -1, - .y_offset = -1, - .model_string = "USBGear USBG-V1 resp. HAMA USB", - }, - [D_LINK_V100] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 4, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "D-Link V100", - }, - [X10_USB_CAMERA] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = -1, - .y_offset = -1, - .model_string = "X10 USB Camera", - }, - [HPG_WINTV_LIVE_PAL_BG] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = -1, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Live (PAL B/G)", - }, - [HPG_WINTV_LIVE_PRO_NTSC_MN] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Live Pro (NTSC M/N)", - }, - [ZORAN_PMD_NOGATECH] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 2, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan", - }, - [NOGATECH_USB_TV_NTSC_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = -1, - .y_offset = 20, - .model_string = "Nogatech USB-TV (NTSC) FM", - }, - [PNY_USB_TV_NTSC_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = -1, - .y_offset = 20, - .model_string = "PNY USB-TV (NTSC) FM", - }, - [PV_PLAYTV_USB_PRO_PAL_FM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "PixelView PlayTv-USB PRO (PAL) FM", - }, - [ZT_721] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "ZTV ZT-721 2.4GHz USB A/V Receiver", - }, - [HPG_WINTV_NTSC_MN] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = -1, - .y_offset = 20, - .model_string = "Hauppauge WinTV USB (NTSC M/N)", - }, - [HPG_WINTV_PAL_BG] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTV USB (PAL B/G)", - }, - [HPG_WINTV_PAL_I] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTV USB (PAL I)", - }, - [HPG_WINTV_PAL_SECAM_L] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_SECAM, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_SECAM, - .x_offset = 0x80, - .y_offset = 0x16, - .model_string = "Hauppauge WinTV USB (PAL/SECAM L)", - }, - [HPG_WINTV_PAL_D_K] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTV USB (PAL D/K)", - }, - [HPG_WINTV_NTSC_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTV USB (NTSC FM)", - }, - [HPG_WINTV_PAL_BG_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTV USB (PAL B/G FM)", - }, - [HPG_WINTV_PAL_I_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTV USB (PAL I FM)", - }, - [HPG_WINTV_PAL_D_K_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTV USB (PAL D/K FM)", - }, - [HPG_WINTV_PRO_NTSC_MN] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_MICROTUNE_4049FM5, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (NTSC M/N)", - }, - [HPG_WINTV_PRO_NTSC_MN_V2] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_MICROTUNE_4049FM5, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V2", - }, - [HPG_WINTV_PRO_PAL] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)", - }, - [HPG_WINTV_PRO_NTSC_MN_V3] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V3", - }, - [HPG_WINTV_PRO_PAL_BG] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL B/G)", - }, - [HPG_WINTV_PRO_PAL_I] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL I)", - }, - [HPG_WINTV_PRO_PAL_SECAM_L] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_SECAM, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_SECAM, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM L)", - }, - [HPG_WINTV_PRO_PAL_D_K] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL D/K)", - }, - [HPG_WINTV_PRO_PAL_SECAM] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_SECAM, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_SECAM, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)", - }, - [HPG_WINTV_PRO_PAL_SECAM_V2] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_SECAM, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_SECAM, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2", - }, - [HPG_WINTV_PRO_PAL_BG_V2] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_ALPS_TSBE1_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL B/G) V2", - }, - [HPG_WINTV_PRO_PAL_BG_D_K] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_ALPS_TSBE1_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL B/G,D/K)", - }, - [HPG_WINTV_PRO_PAL_I_D_K] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL I,D/K)", - }, - [HPG_WINTV_PRO_NTSC_MN_FM] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM)", - }, - [HPG_WINTV_PRO_PAL_BG_FM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL B/G FM)", - }, - [HPG_WINTV_PRO_PAL_I_FM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL I FM)", - }, - [HPG_WINTV_PRO_PAL_D_K_FM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL D/K FM)", - }, - [HPG_WINTV_PRO_TEMIC_PAL_FM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_MICROTUNE_4049FM5, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)", - }, - [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_MICROTUNE_4049FM5, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)", - }, - [HPG_WINTV_PRO_PAL_FM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)", - }, - [HPG_WINTV_PRO_NTSC_MN_FM_V2] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2", - }, - [CAMTEL_TVB330] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = 5, - .y_offset = 5, - .model_string = "Camtel Technology USB TV Genie Pro FM Model TVB330", - }, - [DIGITAL_VIDEO_CREATOR_I] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Digital Video Creator I", - }, - [GLOBAL_VILLAGE_GV_007_NTSC] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 82, - .y_offset = 20, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Global Village GV-007 (NTSC)", - }, - [DAZZLE_DVC_50_REV_1_NTSC] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)", - }, - [DAZZLE_DVC_80_REV_1_PAL] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)", - }, - [DAZZLE_DVC_90_REV_1_SECAM] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_SECAM, - .audio_channels = 0, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)", - }, - [ESKAPE_LABS_MYTV2GO] = { - .interface = 0, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Eskape Labs MyTV2Go", - }, - [PINNA_PCTV_USB_PAL] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 0, - .tuner = 1, - .tuner_type = TUNER_TEMIC_4066FY5_PAL_I, - .x_offset = -1, - .y_offset = -1, - .model_string = "Pinnacle Studio PCTV USB (PAL)", - }, - [PINNA_PCTV_USB_SECAM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_SECAM, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_SECAM, - .x_offset = -1, - .y_offset = -1, - .model_string = "Pinnacle Studio PCTV USB (SECAM)", - }, - [PINNA_PCTV_USB_PAL_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = 128, - .y_offset = 23, - .model_string = "Pinnacle Studio PCTV USB (PAL) FM", - }, - [MIRO_PCTV_USB] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_PAL, - .x_offset = -1, - .y_offset = -1, - .model_string = "Miro PCTV USB", - }, - [PINNA_PCTV_USB_NTSC_FM] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = -1, - .y_offset = -1, - .model_string = "Pinnacle Studio PCTV USB (NTSC) FM", - }, - [PINNA_PCTV_USB_NTSC_FM_V3] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = -1, - .y_offset = -1, - .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V3", - }, - [PINNA_PCTV_USB_PAL_FM_V2] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_TEMIC_4009FR5_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Pinnacle Studio PCTV USB (PAL) FM V2", - }, - [PINNA_PCTV_USB_NTSC_FM_V2] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_TEMIC_4039FR5_NTSC, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V2", - }, - [PINNA_PCTV_USB_PAL_FM_V3] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_TEMIC_4009FR5_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Pinnacle Studio PCTV USB (PAL) FM V3", - }, - [PINNA_LINX_VD_IN_CAB_NTSC] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Pinnacle Studio Linx Video input cable (NTSC)", - }, - [PINNA_LINX_VD_IN_CAB_PAL] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 2, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 0, - .tuner_type = 0, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Pinnacle Studio Linx Video input cable (PAL)", - }, - [PINNA_PCTV_BUNGEE_PAL_FM] = { - .interface = -1, - .codec = CODEC_SAA7113, - .video_channels = 3, - .video_norm = V4L2_STD_PAL, - .audio_channels = 1, - .radio = 1, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_TEMIC_4009FR5_PAL, - .x_offset = 0, - .y_offset = 3, - .dvi_yuv_override = 1, - .dvi_yuv = 7, - .model_string = "Pinnacle PCTV Bungee USB (PAL) FM", - }, - [HPG_WINTV] = { - .interface = -1, - .codec = CODEC_SAA7111, - .video_channels = 3, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 1, - .radio = 0, - .vbi = 1, - .tuner = 1, - .tuner_type = TUNER_PHILIPS_NTSC_M, - .x_offset = -1, - .y_offset = -1, - .model_string = "Hauppauge WinTv-USB", - }, - [MICROCAM_NTSC] = { - .interface = -1, - .codec = CODEC_WEBCAM, - .video_channels = 1, - .video_norm = V4L2_STD_NTSC, - .audio_channels = 0, - .radio = 0, - .vbi = 0, - .tuner = 0, - .tuner_type = 0, - .x_offset = 71, - .y_offset = 15, - .model_string = "Nogatech USB MicroCam NTSC (NV3000N)", - }, - [MICROCAM_PAL] = { - .interface = -1, - .codec = CODEC_WEBCAM, - .video_channels = 1, - .video_norm = V4L2_STD_PAL, - .audio_channels = 0, - .radio = 0, - .vbi = 0, - .tuner = 0, - .tuner_type = 0, - .x_offset = 71, - .y_offset = 18, - .model_string = "Nogatech USB MicroCam PAL (NV3001P)", - }, -}; -const int usbvision_device_data_size = ARRAY_SIZE(usbvision_device_data); - -/* Supported Devices */ - -struct usb_device_id usbvision_table[] = { - { USB_DEVICE(0x0a6f, 0x0400), .driver_info = XANBOO }, - { USB_DEVICE(0x050d, 0x0106), .driver_info = BELKIN_VIDEOBUS_II }, - { USB_DEVICE(0x050d, 0x0207), .driver_info = BELKIN_VIDEOBUS }, - { USB_DEVICE(0x050d, 0x0208), .driver_info = BELKIN_USB_VIDEOBUS_II }, - { USB_DEVICE(0x0571, 0x0002), .driver_info = ECHOFX_INTERVIEW_LITE }, - { USB_DEVICE(0x0573, 0x0003), .driver_info = USBGEAR_USBG_V1 }, - { USB_DEVICE(0x0573, 0x0400), .driver_info = D_LINK_V100 }, - { USB_DEVICE(0x0573, 0x2000), .driver_info = X10_USB_CAMERA }, - { USB_DEVICE(0x0573, 0x2d00), .driver_info = HPG_WINTV_LIVE_PAL_BG }, - { USB_DEVICE(0x0573, 0x2d01), .driver_info = HPG_WINTV_LIVE_PRO_NTSC_MN }, - { USB_DEVICE(0x0573, 0x2101), .driver_info = ZORAN_PMD_NOGATECH }, - { USB_DEVICE(0x0573, 0x3000), .driver_info = MICROCAM_NTSC }, - { USB_DEVICE(0x0573, 0x3001), .driver_info = MICROCAM_PAL }, - { USB_DEVICE(0x0573, 0x4100), .driver_info = NOGATECH_USB_TV_NTSC_FM }, - { USB_DEVICE(0x0573, 0x4110), .driver_info = PNY_USB_TV_NTSC_FM }, - { USB_DEVICE(0x0573, 0x4450), .driver_info = PV_PLAYTV_USB_PRO_PAL_FM }, - { USB_DEVICE(0x0573, 0x4550), .driver_info = ZT_721 }, - { USB_DEVICE(0x0573, 0x4d00), .driver_info = HPG_WINTV_NTSC_MN }, - { USB_DEVICE(0x0573, 0x4d01), .driver_info = HPG_WINTV_PAL_BG }, - { USB_DEVICE(0x0573, 0x4d02), .driver_info = HPG_WINTV_PAL_I }, - { USB_DEVICE(0x0573, 0x4d03), .driver_info = HPG_WINTV_PAL_SECAM_L }, - { USB_DEVICE(0x0573, 0x4d04), .driver_info = HPG_WINTV_PAL_D_K }, - { USB_DEVICE(0x0573, 0x4d10), .driver_info = HPG_WINTV_NTSC_FM }, - { USB_DEVICE(0x0573, 0x4d11), .driver_info = HPG_WINTV_PAL_BG_FM }, - { USB_DEVICE(0x0573, 0x4d12), .driver_info = HPG_WINTV_PAL_I_FM }, - { USB_DEVICE(0x0573, 0x4d14), .driver_info = HPG_WINTV_PAL_D_K_FM }, - { USB_DEVICE(0x0573, 0x4d2a), .driver_info = HPG_WINTV_PRO_NTSC_MN }, - { USB_DEVICE(0x0573, 0x4d2b), .driver_info = HPG_WINTV_PRO_NTSC_MN_V2 }, - { USB_DEVICE(0x0573, 0x4d2c), .driver_info = HPG_WINTV_PRO_PAL }, - { USB_DEVICE(0x0573, 0x4d20), .driver_info = HPG_WINTV_PRO_NTSC_MN_V3 }, - { USB_DEVICE(0x0573, 0x4d21), .driver_info = HPG_WINTV_PRO_PAL_BG }, - { USB_DEVICE(0x0573, 0x4d22), .driver_info = HPG_WINTV_PRO_PAL_I }, - { USB_DEVICE(0x0573, 0x4d23), .driver_info = HPG_WINTV_PRO_PAL_SECAM_L }, - { USB_DEVICE(0x0573, 0x4d24), .driver_info = HPG_WINTV_PRO_PAL_D_K }, - { USB_DEVICE(0x0573, 0x4d25), .driver_info = HPG_WINTV_PRO_PAL_SECAM }, - { USB_DEVICE(0x0573, 0x4d26), .driver_info = HPG_WINTV_PRO_PAL_SECAM_V2 }, - { USB_DEVICE(0x0573, 0x4d27), .driver_info = HPG_WINTV_PRO_PAL_BG_V2 }, - { USB_DEVICE(0x0573, 0x4d28), .driver_info = HPG_WINTV_PRO_PAL_BG_D_K }, - { USB_DEVICE(0x0573, 0x4d29), .driver_info = HPG_WINTV_PRO_PAL_I_D_K }, - { USB_DEVICE(0x0573, 0x4d30), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM }, - { USB_DEVICE(0x0573, 0x4d31), .driver_info = HPG_WINTV_PRO_PAL_BG_FM }, - { USB_DEVICE(0x0573, 0x4d32), .driver_info = HPG_WINTV_PRO_PAL_I_FM }, - { USB_DEVICE(0x0573, 0x4d34), .driver_info = HPG_WINTV_PRO_PAL_D_K_FM }, - { USB_DEVICE(0x0573, 0x4d35), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_FM }, - { USB_DEVICE(0x0573, 0x4d36), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_BG_FM }, - { USB_DEVICE(0x0573, 0x4d37), .driver_info = HPG_WINTV_PRO_PAL_FM }, - { USB_DEVICE(0x0573, 0x4d38), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM_V2 }, - { USB_DEVICE(0x0768, 0x0006), .driver_info = CAMTEL_TVB330 }, - { USB_DEVICE(0x07d0, 0x0001), .driver_info = DIGITAL_VIDEO_CREATOR_I }, - { USB_DEVICE(0x07d0, 0x0002), .driver_info = GLOBAL_VILLAGE_GV_007_NTSC }, - { USB_DEVICE(0x07d0, 0x0003), .driver_info = DAZZLE_DVC_50_REV_1_NTSC }, - { USB_DEVICE(0x07d0, 0x0004), .driver_info = DAZZLE_DVC_80_REV_1_PAL }, - { USB_DEVICE(0x07d0, 0x0005), .driver_info = DAZZLE_DVC_90_REV_1_SECAM }, - { USB_DEVICE(0x07f8, 0x9104), .driver_info = ESKAPE_LABS_MYTV2GO }, - { USB_DEVICE(0x2304, 0x010d), .driver_info = PINNA_PCTV_USB_PAL }, - { USB_DEVICE(0x2304, 0x0109), .driver_info = PINNA_PCTV_USB_SECAM }, - { USB_DEVICE(0x2304, 0x0110), .driver_info = PINNA_PCTV_USB_PAL_FM }, - { USB_DEVICE(0x2304, 0x0111), .driver_info = MIRO_PCTV_USB }, - { USB_DEVICE(0x2304, 0x0112), .driver_info = PINNA_PCTV_USB_NTSC_FM }, - { USB_DEVICE(0x2304, 0x0113), .driver_info = PINNA_PCTV_USB_NTSC_FM_V3 }, - { USB_DEVICE(0x2304, 0x0210), .driver_info = PINNA_PCTV_USB_PAL_FM_V2 }, - { USB_DEVICE(0x2304, 0x0212), .driver_info = PINNA_PCTV_USB_NTSC_FM_V2 }, - { USB_DEVICE(0x2304, 0x0214), .driver_info = PINNA_PCTV_USB_PAL_FM_V3 }, - { USB_DEVICE(0x2304, 0x0300), .driver_info = PINNA_LINX_VD_IN_CAB_NTSC }, - { USB_DEVICE(0x2304, 0x0301), .driver_info = PINNA_LINX_VD_IN_CAB_PAL }, - { USB_DEVICE(0x2304, 0x0419), .driver_info = PINNA_PCTV_BUNGEE_PAL_FM }, - { USB_DEVICE(0x2400, 0x4200), .driver_info = HPG_WINTV }, - { }, /* terminate list */ -}; - -MODULE_DEVICE_TABLE(usb, usbvision_table); diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h deleted file mode 100644 index a51cc1185cce..000000000000 --- a/drivers/media/video/usbvision/usbvision-cards.h +++ /dev/null @@ -1,69 +0,0 @@ -#define XANBOO 0 -#define BELKIN_VIDEOBUS_II 1 -#define BELKIN_VIDEOBUS 2 -#define BELKIN_USB_VIDEOBUS_II 3 -#define ECHOFX_INTERVIEW_LITE 4 -#define USBGEAR_USBG_V1 5 -#define D_LINK_V100 6 -#define X10_USB_CAMERA 7 -#define HPG_WINTV_LIVE_PAL_BG 8 -#define HPG_WINTV_LIVE_PRO_NTSC_MN 9 -#define ZORAN_PMD_NOGATECH 10 -#define NOGATECH_USB_TV_NTSC_FM 11 -#define PNY_USB_TV_NTSC_FM 12 -#define PV_PLAYTV_USB_PRO_PAL_FM 13 -#define ZT_721 14 -#define HPG_WINTV_NTSC_MN 15 -#define HPG_WINTV_PAL_BG 16 -#define HPG_WINTV_PAL_I 17 -#define HPG_WINTV_PAL_SECAM_L 18 -#define HPG_WINTV_PAL_D_K 19 -#define HPG_WINTV_NTSC_FM 20 -#define HPG_WINTV_PAL_BG_FM 21 -#define HPG_WINTV_PAL_I_FM 22 -#define HPG_WINTV_PAL_D_K_FM 23 -#define HPG_WINTV_PRO_NTSC_MN 24 -#define HPG_WINTV_PRO_NTSC_MN_V2 25 -#define HPG_WINTV_PRO_PAL 26 -#define HPG_WINTV_PRO_NTSC_MN_V3 27 -#define HPG_WINTV_PRO_PAL_BG 28 -#define HPG_WINTV_PRO_PAL_I 29 -#define HPG_WINTV_PRO_PAL_SECAM_L 30 -#define HPG_WINTV_PRO_PAL_D_K 31 -#define HPG_WINTV_PRO_PAL_SECAM 32 -#define HPG_WINTV_PRO_PAL_SECAM_V2 33 -#define HPG_WINTV_PRO_PAL_BG_V2 34 -#define HPG_WINTV_PRO_PAL_BG_D_K 35 -#define HPG_WINTV_PRO_PAL_I_D_K 36 -#define HPG_WINTV_PRO_NTSC_MN_FM 37 -#define HPG_WINTV_PRO_PAL_BG_FM 38 -#define HPG_WINTV_PRO_PAL_I_FM 39 -#define HPG_WINTV_PRO_PAL_D_K_FM 40 -#define HPG_WINTV_PRO_TEMIC_PAL_FM 41 -#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42 -#define HPG_WINTV_PRO_PAL_FM 43 -#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44 -#define CAMTEL_TVB330 45 -#define DIGITAL_VIDEO_CREATOR_I 46 -#define GLOBAL_VILLAGE_GV_007_NTSC 47 -#define DAZZLE_DVC_50_REV_1_NTSC 48 -#define DAZZLE_DVC_80_REV_1_PAL 49 -#define DAZZLE_DVC_90_REV_1_SECAM 50 -#define ESKAPE_LABS_MYTV2GO 51 -#define PINNA_PCTV_USB_PAL 52 -#define PINNA_PCTV_USB_SECAM 53 -#define PINNA_PCTV_USB_PAL_FM 54 -#define MIRO_PCTV_USB 55 -#define PINNA_PCTV_USB_NTSC_FM 56 -#define PINNA_PCTV_USB_PAL_FM_V2 57 -#define PINNA_PCTV_USB_NTSC_FM_V2 58 -#define PINNA_PCTV_USB_PAL_FM_V3 59 -#define PINNA_LINX_VD_IN_CAB_NTSC 60 -#define PINNA_LINX_VD_IN_CAB_PAL 61 -#define PINNA_PCTV_BUNGEE_PAL_FM 62 -#define HPG_WINTV 63 -#define PINNA_PCTV_USB_NTSC_FM_V3 64 -#define MICROCAM_NTSC 65 -#define MICROCAM_PAL 66 - -extern const int usbvision_device_data_size; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c deleted file mode 100644 index c9b2042f8bdf..000000000000 --- a/drivers/media/video/usbvision/usbvision-core.c +++ /dev/null @@ -1,2518 +0,0 @@ -/* - * usbvision-core.c - driver for NT100x USB video capture devices - * - * - * Copyright (c) 1999-2005 Joerg Heckenbach - * Dwaine Garden - * - * This module is part of usbvision driver project. - * Updates to driver completed by Dwaine P. Garden - * - * This program is free software; 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 -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "usbvision.h" - -static unsigned int core_debug; -module_param(core_debug, int, 0644); -MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); - -static int adjust_compression = 1; /* Set the compression to be adaptive */ -module_param(adjust_compression, int, 0444); -MODULE_PARM_DESC(adjust_compression, " Set the ADPCM compression for the device. Default: 1 (On)"); - -/* To help people with Black and White output with using s-video input. - * Some cables and input device are wired differently. */ -static int switch_svideo_input; -module_param(switch_svideo_input, int, 0444); -MODULE_PARM_DESC(switch_svideo_input, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)"); - -static unsigned int adjust_x_offset = -1; -module_param(adjust_x_offset, int, 0644); -MODULE_PARM_DESC(adjust_x_offset, "adjust X offset display [core]"); - -static unsigned int adjust_y_offset = -1; -module_param(adjust_y_offset, int, 0644); -MODULE_PARM_DESC(adjust_y_offset, "adjust Y offset display [core]"); - - -#define ENABLE_HEXDUMP 0 /* Enable if you need it */ - - -#ifdef USBVISION_DEBUG - #define PDEBUG(level, fmt, args...) { \ - if (core_debug & (level)) \ - printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ - __func__, __LINE__ , ## args); \ - } -#else - #define PDEBUG(level, fmt, args...) do {} while (0) -#endif - -#define DBG_HEADER (1 << 0) -#define DBG_IRQ (1 << 1) -#define DBG_ISOC (1 << 2) -#define DBG_PARSE (1 << 3) -#define DBG_SCRATCH (1 << 4) -#define DBG_FUNC (1 << 5) - -static const int max_imgwidth = MAX_FRAME_WIDTH; -static const int max_imgheight = MAX_FRAME_HEIGHT; -static const int min_imgwidth = MIN_FRAME_WIDTH; -static const int min_imgheight = MIN_FRAME_HEIGHT; - -/* The value of 'scratch_buf_size' affects quality of the picture - * in many ways. Shorter buffers may cause loss of data when client - * is too slow. Larger buffers are memory-consuming and take longer - * to work with. This setting can be adjusted, but the default value - * should be OK for most desktop users. - */ -#define DEFAULT_SCRATCH_BUF_SIZE (0x20000) /* 128kB memory scratch buffer */ -static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE; - -/* Function prototypes */ -static int usbvision_request_intra(struct usb_usbvision *usbvision); -static int usbvision_unrequest_intra(struct usb_usbvision *usbvision); -static int usbvision_adjust_compression(struct usb_usbvision *usbvision); -static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision); - -/*******************************/ -/* Memory management functions */ -/*******************************/ - -/* - * Here we want the physical address of the memory. - * This is used when initializing the contents of the area. - */ - -static void *usbvision_rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void usbvision_rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - size = PAGE_ALIGN(size); - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vfree(mem); -} - - -#if ENABLE_HEXDUMP -static void usbvision_hexdump(const unsigned char *data, int len) -{ - char tmp[80]; - int i, k; - - for (i = k = 0; len > 0; i++, len--) { - if (i > 0 && (i % 16 == 0)) { - printk("%s\n", tmp); - k = 0; - } - k += sprintf(&tmp[k], "%02x ", data[i]); - } - if (k > 0) - printk(KERN_CONT "%s\n", tmp); -} -#endif - -/******************************** - * scratch ring buffer handling - ********************************/ -static int scratch_len(struct usb_usbvision *usbvision) /* This returns the amount of data actually in the buffer */ -{ - int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr; - - if (len < 0) - len += scratch_buf_size; - PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len); - - return len; -} - - -/* This returns the free space left in the buffer */ -static int scratch_free(struct usb_usbvision *usbvision) -{ - int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr; - if (free <= 0) - free += scratch_buf_size; - if (free) { - free -= 1; /* at least one byte in the buffer must */ - /* left blank, otherwise there is no chance to differ between full and empty */ - } - PDEBUG(DBG_SCRATCH, "return %d\n", free); - - return free; -} - - -/* This puts data into the buffer */ -static int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, - int len) -{ - int len_part; - - if (usbvision->scratch_write_ptr + len < scratch_buf_size) { - memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len); - usbvision->scratch_write_ptr += len; - } else { - len_part = scratch_buf_size - usbvision->scratch_write_ptr; - memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part); - if (len == len_part) { - usbvision->scratch_write_ptr = 0; /* just set write_ptr to zero */ - } else { - memcpy(usbvision->scratch, data + len_part, len - len_part); - usbvision->scratch_write_ptr = len - len_part; - } - } - - PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr); - - return len; -} - -/* This marks the write_ptr as position of new frame header */ -static void scratch_mark_header(struct usb_usbvision *usbvision) -{ - PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr); - - usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] = - usbvision->scratch_write_ptr; - usbvision->scratch_headermarker_write_ptr += 1; - usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER; -} - -/* This gets data from the buffer at the given "ptr" position */ -static int scratch_get_extra(struct usb_usbvision *usbvision, - unsigned char *data, int *ptr, int len) -{ - int len_part; - - if (*ptr + len < scratch_buf_size) { - memcpy(data, usbvision->scratch + *ptr, len); - *ptr += len; - } else { - len_part = scratch_buf_size - *ptr; - memcpy(data, usbvision->scratch + *ptr, len_part); - if (len == len_part) { - *ptr = 0; /* just set the y_ptr to zero */ - } else { - memcpy(data + len_part, usbvision->scratch, len - len_part); - *ptr = len - len_part; - } - } - - PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr); - - return len; -} - - -/* This sets the scratch extra read pointer */ -static void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr, - int len) -{ - *ptr = (usbvision->scratch_read_ptr + len) % scratch_buf_size; - - PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); -} - - -/* This increments the scratch extra read pointer */ -static void scratch_inc_extra_ptr(int *ptr, int len) -{ - *ptr = (*ptr + len) % scratch_buf_size; - - PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); -} - - -/* This gets data from the buffer */ -static int scratch_get(struct usb_usbvision *usbvision, unsigned char *data, - int len) -{ - int len_part; - - if (usbvision->scratch_read_ptr + len < scratch_buf_size) { - memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len); - usbvision->scratch_read_ptr += len; - } else { - len_part = scratch_buf_size - usbvision->scratch_read_ptr; - memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part); - if (len == len_part) { - usbvision->scratch_read_ptr = 0; /* just set the read_ptr to zero */ - } else { - memcpy(data + len_part, usbvision->scratch, len - len_part); - usbvision->scratch_read_ptr = len - len_part; - } - } - - PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr); - - return len; -} - - -/* This sets read pointer to next header and returns it */ -static int scratch_get_header(struct usb_usbvision *usbvision, - struct usbvision_frame_header *header) -{ - int err_code = 0; - - PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr); - - while (usbvision->scratch_headermarker_write_ptr - - usbvision->scratch_headermarker_read_ptr != 0) { - usbvision->scratch_read_ptr = - usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr]; - usbvision->scratch_headermarker_read_ptr += 1; - usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER; - scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH); - if ((header->magic_1 == USBVISION_MAGIC_1) - && (header->magic_2 == USBVISION_MAGIC_2) - && (header->header_length == USBVISION_HEADER_LENGTH)) { - err_code = USBVISION_HEADER_LENGTH; - header->frame_width = header->frame_width_lo + (header->frame_width_hi << 8); - header->frame_height = header->frame_height_lo + (header->frame_height_hi << 8); - break; - } - } - - return err_code; -} - - -/* This removes len bytes of old data from the buffer */ -static void scratch_rm_old(struct usb_usbvision *usbvision, int len) -{ - usbvision->scratch_read_ptr += len; - usbvision->scratch_read_ptr %= scratch_buf_size; - PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr); -} - - -/* This resets the buffer - kills all data in it too */ -static void scratch_reset(struct usb_usbvision *usbvision) -{ - PDEBUG(DBG_SCRATCH, "\n"); - - usbvision->scratch_read_ptr = 0; - usbvision->scratch_write_ptr = 0; - usbvision->scratch_headermarker_read_ptr = 0; - usbvision->scratch_headermarker_write_ptr = 0; - usbvision->isocstate = isoc_state_no_frame; -} - -int usbvision_scratch_alloc(struct usb_usbvision *usbvision) -{ - usbvision->scratch = vmalloc_32(scratch_buf_size); - scratch_reset(usbvision); - if (usbvision->scratch == NULL) { - dev_err(&usbvision->dev->dev, - "%s: unable to allocate %d bytes for scratch\n", - __func__, scratch_buf_size); - return -ENOMEM; - } - return 0; -} - -void usbvision_scratch_free(struct usb_usbvision *usbvision) -{ - vfree(usbvision->scratch); - usbvision->scratch = NULL; -} - -/* - * usbvision_decompress_alloc() - * - * allocates intermediate buffer for decompression - */ -int usbvision_decompress_alloc(struct usb_usbvision *usbvision) -{ - int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; - - usbvision->intra_frame_buffer = vmalloc_32(IFB_size); - if (usbvision->intra_frame_buffer == NULL) { - dev_err(&usbvision->dev->dev, - "%s: unable to allocate %d for compr. frame buffer\n", - __func__, IFB_size); - return -ENOMEM; - } - return 0; -} - -/* - * usbvision_decompress_free() - * - * frees intermediate buffer for decompression - */ -void usbvision_decompress_free(struct usb_usbvision *usbvision) -{ - vfree(usbvision->intra_frame_buffer); - usbvision->intra_frame_buffer = NULL; - -} - -/************************************************************ - * Here comes the data parsing stuff that is run as interrupt - ************************************************************/ -/* - * usbvision_find_header() - * - * Locate one of supported header markers in the scratch buffer. - */ -static enum parse_state usbvision_find_header(struct usb_usbvision *usbvision) -{ - struct usbvision_frame *frame; - int found_header = 0; - - frame = usbvision->cur_frame; - - while (scratch_get_header(usbvision, &frame->isoc_header) == USBVISION_HEADER_LENGTH) { - /* found header in scratch */ - PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u", - frame->isoc_header.magic_2, - frame->isoc_header.magic_1, - frame->isoc_header.header_length, - frame->isoc_header.frame_num, - frame->isoc_header.frame_phase, - frame->isoc_header.frame_latency, - frame->isoc_header.data_format, - frame->isoc_header.format_param, - frame->isoc_header.frame_width, - frame->isoc_header.frame_height); - - if (usbvision->request_intra) { - if (frame->isoc_header.format_param & 0x80) { - found_header = 1; - usbvision->last_isoc_frame_num = -1; /* do not check for lost frames this time */ - usbvision_unrequest_intra(usbvision); - break; - } - } else { - found_header = 1; - break; - } - } - - if (found_header) { - frame->frmwidth = frame->isoc_header.frame_width * usbvision->stretch_width; - frame->frmheight = frame->isoc_header.frame_height * usbvision->stretch_height; - frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth) >> 3; - } else { /* no header found */ - PDEBUG(DBG_HEADER, "skipping scratch data, no header"); - scratch_reset(usbvision); - return parse_state_end_parse; - } - - /* found header */ - if (frame->isoc_header.data_format == ISOC_MODE_COMPRESS) { - /* check isoc_header.frame_num for lost frames */ - if (usbvision->last_isoc_frame_num >= 0) { - if (((usbvision->last_isoc_frame_num + 1) % 32) != frame->isoc_header.frame_num) { - /* unexpected frame drop: need to request new intra frame */ - PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isoc_header.frame_num); - usbvision_request_intra(usbvision); - return parse_state_next_frame; - } - } - usbvision->last_isoc_frame_num = frame->isoc_header.frame_num; - } - usbvision->header_count++; - frame->scanstate = scan_state_lines; - frame->curline = 0; - - return parse_state_continue; -} - -static enum parse_state usbvision_parse_lines_422(struct usb_usbvision *usbvision, - long *pcopylen) -{ - volatile struct usbvision_frame *frame; - unsigned char *f; - int len; - int i; - unsigned char yuyv[4] = { 180, 128, 10, 128 }; /* YUV components */ - unsigned char rv, gv, bv; /* RGB components */ - int clipmask_index, bytes_per_pixel; - int stretch_bytes, clipmask_add; - - frame = usbvision->cur_frame; - f = frame->data + (frame->v4l2_linesize * frame->curline); - - /* Make sure there's enough data for the entire line */ - len = (frame->isoc_header.frame_width * 2) + 5; - if (scratch_len(usbvision) < len) { - PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len); - return parse_state_out; - } - - if ((frame->curline + 1) >= frame->frmheight) - return parse_state_next_frame; - - bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; - stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; - clipmask_index = frame->curline * MAX_FRAME_WIDTH; - clipmask_add = usbvision->stretch_width; - - for (i = 0; i < frame->frmwidth; i += (2 * usbvision->stretch_width)) { - scratch_get(usbvision, &yuyv[0], 4); - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { - *f++ = yuyv[0]; /* Y */ - *f++ = yuyv[3]; /* U */ - } else { - YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv); - switch (frame->v4l2_format.format) { - case V4L2_PIX_FMT_RGB565: - *f++ = (0x1F & rv) | - (0xE0 & (gv << 5)); - *f++ = (0x07 & (gv >> 3)) | - (0xF8 & bv); - break; - case V4L2_PIX_FMT_RGB24: - *f++ = rv; - *f++ = gv; - *f++ = bv; - break; - case V4L2_PIX_FMT_RGB32: - *f++ = rv; - *f++ = gv; - *f++ = bv; - f++; - break; - case V4L2_PIX_FMT_RGB555: - *f++ = (0x1F & rv) | - (0xE0 & (gv << 5)); - *f++ = (0x03 & (gv >> 3)) | - (0x7C & (bv << 2)); - break; - } - } - clipmask_index += clipmask_add; - f += stretch_bytes; - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { - *f++ = yuyv[2]; /* Y */ - *f++ = yuyv[1]; /* V */ - } else { - YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv); - switch (frame->v4l2_format.format) { - case V4L2_PIX_FMT_RGB565: - *f++ = (0x1F & rv) | - (0xE0 & (gv << 5)); - *f++ = (0x07 & (gv >> 3)) | - (0xF8 & bv); - break; - case V4L2_PIX_FMT_RGB24: - *f++ = rv; - *f++ = gv; - *f++ = bv; - break; - case V4L2_PIX_FMT_RGB32: - *f++ = rv; - *f++ = gv; - *f++ = bv; - f++; - break; - case V4L2_PIX_FMT_RGB555: - *f++ = (0x1F & rv) | - (0xE0 & (gv << 5)); - *f++ = (0x03 & (gv >> 3)) | - (0x7C & (bv << 2)); - break; - } - } - clipmask_index += clipmask_add; - f += stretch_bytes; - } - - frame->curline += usbvision->stretch_height; - *pcopylen += frame->v4l2_linesize * usbvision->stretch_height; - - if (frame->curline >= frame->frmheight) - return parse_state_next_frame; - return parse_state_continue; -} - -/* The decompression routine */ -static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char *compressed, - unsigned char *decompressed, int *start_pos, - int *block_typestart_pos, int len) -{ - int rest_pixel, idx, pos, extra_pos, block_len, block_type_pos, block_type_len; - unsigned char block_byte, block_code, block_type, block_type_byte, integrator; - - integrator = 0; - pos = *start_pos; - block_type_pos = *block_typestart_pos; - extra_pos = pos; - block_len = 0; - block_byte = 0; - block_code = 0; - block_type = 0; - block_type_byte = 0; - block_type_len = 0; - rest_pixel = len; - - for (idx = 0; idx < len; idx++) { - if (block_len == 0) { - if (block_type_len == 0) { - block_type_byte = compressed[block_type_pos]; - block_type_pos++; - block_type_len = 4; - } - block_type = (block_type_byte & 0xC0) >> 6; - - /* statistic: */ - usbvision->compr_block_types[block_type]++; - - pos = extra_pos; - if (block_type == 0) { - if (rest_pixel >= 24) { - idx += 23; - rest_pixel -= 24; - integrator = decompressed[idx]; - } else { - idx += rest_pixel - 1; - rest_pixel = 0; - } - } else { - block_code = compressed[pos]; - pos++; - if (rest_pixel >= 24) - block_len = 24; - else - block_len = rest_pixel; - rest_pixel -= block_len; - extra_pos = pos + (block_len / 4); - } - block_type_byte <<= 2; - block_type_len -= 1; - } - if (block_len > 0) { - if ((block_len % 4) == 0) { - block_byte = compressed[pos]; - pos++; - } - if (block_type == 1) /* inter Block */ - integrator = decompressed[idx]; - switch (block_byte & 0xC0) { - case 0x03 << 6: - integrator += compressed[extra_pos]; - extra_pos++; - break; - case 0x02 << 6: - integrator += block_code; - break; - case 0x00: - integrator -= block_code; - break; - } - decompressed[idx] = integrator; - block_byte <<= 2; - block_len -= 1; - } - } - *start_pos = extra_pos; - *block_typestart_pos = block_type_pos; - return idx; -} - - -/* - * usbvision_parse_compress() - * - * Parse compressed frame from the scratch buffer, put - * decoded RGB value into the current frame buffer and add the written - * number of bytes (RGB) to the *pcopylen. - * - */ -static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision, - long *pcopylen) -{ -#define USBVISION_STRIP_MAGIC 0x5A -#define USBVISION_STRIP_LEN_MAX 400 -#define USBVISION_STRIP_HEADER_LEN 3 - - struct usbvision_frame *frame; - unsigned char *f, *u = NULL, *v = NULL; - unsigned char strip_data[USBVISION_STRIP_LEN_MAX]; - unsigned char strip_header[USBVISION_STRIP_HEADER_LEN]; - int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos; - int clipmask_index; - int image_size; - unsigned char rv, gv, bv; - static unsigned char *Y, *U, *V; - - frame = usbvision->cur_frame; - image_size = frame->frmwidth * frame->frmheight; - if ((frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || - (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420)) { /* this is a planar format */ - /* ... v4l2_linesize not used here. */ - f = frame->data + (frame->width * frame->curline); - } else - f = frame->data + (frame->v4l2_linesize * frame->curline); - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { /* initialise u and v pointers */ - /* get base of u and b planes add halfoffset */ - u = frame->data - + image_size - + (frame->frmwidth >> 1) * frame->curline; - v = u + (image_size >> 1); - } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { - v = frame->data + image_size + ((frame->curline * (frame->width)) >> 2); - u = v + (image_size >> 2); - } - - if (frame->curline == 0) - usbvision_adjust_compression(usbvision); - - if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) - return parse_state_out; - - /* get strip header without changing the scratch_read_ptr */ - scratch_set_extra_ptr(usbvision, &strip_ptr, 0); - scratch_get_extra(usbvision, &strip_header[0], &strip_ptr, - USBVISION_STRIP_HEADER_LEN); - - if (strip_header[0] != USBVISION_STRIP_MAGIC) { - /* wrong strip magic */ - usbvision->strip_magic_errors++; - return parse_state_next_frame; - } - - if (frame->curline != (int)strip_header[2]) { - /* line number mismatch error */ - usbvision->strip_line_number_errors++; - } - - strip_len = 2 * (unsigned int)strip_header[1]; - if (strip_len > USBVISION_STRIP_LEN_MAX) { - /* strip overrun */ - /* I think this never happens */ - usbvision_request_intra(usbvision); - } - - if (scratch_len(usbvision) < strip_len) { - /* there is not enough data for the strip */ - return parse_state_out; - } - - if (usbvision->intra_frame_buffer) { - Y = usbvision->intra_frame_buffer + frame->frmwidth * frame->curline; - U = usbvision->intra_frame_buffer + image_size + (frame->frmwidth / 2) * (frame->curline / 2); - V = usbvision->intra_frame_buffer + image_size / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2); - } else { - return parse_state_next_frame; - } - - clipmask_index = frame->curline * MAX_FRAME_WIDTH; - - scratch_get(usbvision, strip_data, strip_len); - - idx_end = frame->frmwidth; - block_type_pos = USBVISION_STRIP_HEADER_LEN; - startblock_pos = block_type_pos + (idx_end - 1) / 96 + (idx_end / 2 - 1) / 96 + 2; - block_pos = startblock_pos; - - usbvision->block_pos = block_pos; - - usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); - if (strip_len > usbvision->max_strip_len) - usbvision->max_strip_len = strip_len; - - if (frame->curline % 2) - usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); - else - usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); - - if (block_pos > usbvision->comprblock_pos) - usbvision->comprblock_pos = block_pos; - if (block_pos > strip_len) - usbvision->strip_len_errors++; - - for (idx = 0; idx < idx_end; idx++) { - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { - *f++ = Y[idx]; - *f++ = idx & 0x01 ? U[idx / 2] : V[idx / 2]; - } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) { - *f++ = Y[idx]; - if (idx & 0x01) - *u++ = U[idx >> 1]; - else - *v++ = V[idx >> 1]; - } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { - *f++ = Y[idx]; - if (!((idx & 0x01) | (frame->curline & 0x01))) { - /* only need do this for 1 in 4 pixels */ - /* intraframe buffer is YUV420 format */ - *u++ = U[idx >> 1]; - *v++ = V[idx >> 1]; - } - } else { - YUV_TO_RGB_BY_THE_BOOK(Y[idx], U[idx / 2], V[idx / 2], rv, gv, bv); - switch (frame->v4l2_format.format) { - case V4L2_PIX_FMT_GREY: - *f++ = Y[idx]; - break; - case V4L2_PIX_FMT_RGB555: - *f++ = (0x1F & rv) | - (0xE0 & (gv << 5)); - *f++ = (0x03 & (gv >> 3)) | - (0x7C & (bv << 2)); - break; - case V4L2_PIX_FMT_RGB565: - *f++ = (0x1F & rv) | - (0xE0 & (gv << 5)); - *f++ = (0x07 & (gv >> 3)) | - (0xF8 & bv); - break; - case V4L2_PIX_FMT_RGB24: - *f++ = rv; - *f++ = gv; - *f++ = bv; - break; - case V4L2_PIX_FMT_RGB32: - *f++ = rv; - *f++ = gv; - *f++ = bv; - f++; - break; - } - } - clipmask_index++; - } - /* Deal with non-integer no. of bytes for YUV420P */ - if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420) - *pcopylen += frame->v4l2_linesize; - else - *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1; - - frame->curline += 1; - - if (frame->curline >= frame->frmheight) - return parse_state_next_frame; - return parse_state_continue; - -} - - -/* - * usbvision_parse_lines_420() - * - * Parse two lines from the scratch buffer, put - * decoded RGB value into the current frame buffer and add the written - * number of bytes (RGB) to the *pcopylen. - * - */ -static enum parse_state usbvision_parse_lines_420(struct usb_usbvision *usbvision, - long *pcopylen) -{ - struct usbvision_frame *frame; - unsigned char *f_even = NULL, *f_odd = NULL; - unsigned int pixel_per_line, block; - int pixel, block_split; - int y_ptr, u_ptr, v_ptr, y_odd_offset; - const int y_block_size = 128; - const int uv_block_size = 64; - const int sub_block_size = 32; - const int y_step[] = { 0, 0, 0, 2 }, y_step_size = 4; - const int uv_step[] = { 0, 0, 0, 4 }, uv_step_size = 4; - unsigned char y[2], u, v; /* YUV components */ - int y_, u_, v_, vb, uvg, ur; - int r_, g_, b_; /* RGB components */ - unsigned char g; - int clipmask_even_index, clipmask_odd_index, bytes_per_pixel; - int clipmask_add, stretch_bytes; - - frame = usbvision->cur_frame; - f_even = frame->data + (frame->v4l2_linesize * frame->curline); - f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height; - - /* Make sure there's enough data for the entire line */ - /* In this mode usbvision transfer 3 bytes for every 2 pixels */ - /* I need two lines to decode the color */ - bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; - stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; - clipmask_even_index = frame->curline * MAX_FRAME_WIDTH; - clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH; - clipmask_add = usbvision->stretch_width; - pixel_per_line = frame->isoc_header.frame_width; - - if (scratch_len(usbvision) < (int)pixel_per_line * 3) { - /* printk(KERN_DEBUG "out of data, need %d\n", len); */ - return parse_state_out; - } - - if ((frame->curline + 1) >= frame->frmheight) - return parse_state_next_frame; - - block_split = (pixel_per_line%y_block_size) ? 1 : 0; /* are some blocks splitted into different lines? */ - - y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size) - + block_split * uv_block_size; - - scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset); - scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size); - scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset - + (4 - block_split) * sub_block_size); - - for (block = 0; block < (pixel_per_line / sub_block_size); block++) { - for (pixel = 0; pixel < sub_block_size; pixel += 2) { - scratch_get(usbvision, &y[0], 2); - scratch_get_extra(usbvision, &u, &u_ptr, 1); - scratch_get_extra(usbvision, &v, &v_ptr, 1); - - /* I don't use the YUV_TO_RGB macro for better performance */ - v_ = v - 128; - u_ = u - 128; - vb = 132252 * v_; - uvg = -53281 * u_ - 25625 * v_; - ur = 104595 * u_; - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { - *f_even++ = y[0]; - *f_even++ = v; - } else { - y_ = 76284 * (y[0] - 16); - - b_ = (y_ + vb) >> 16; - g_ = (y_ + uvg) >> 16; - r_ = (y_ + ur) >> 16; - - switch (frame->v4l2_format.format) { - case V4L2_PIX_FMT_RGB565: - g = LIMIT_RGB(g_); - *f_even++ = - (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_even++ = - (0x07 & (g >> 3)) | - (0xF8 & LIMIT_RGB(b_)); - break; - case V4L2_PIX_FMT_RGB24: - *f_even++ = LIMIT_RGB(r_); - *f_even++ = LIMIT_RGB(g_); - *f_even++ = LIMIT_RGB(b_); - break; - case V4L2_PIX_FMT_RGB32: - *f_even++ = LIMIT_RGB(r_); - *f_even++ = LIMIT_RGB(g_); - *f_even++ = LIMIT_RGB(b_); - f_even++; - break; - case V4L2_PIX_FMT_RGB555: - g = LIMIT_RGB(g_); - *f_even++ = (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_even++ = (0x03 & (g >> 3)) | - (0x7C & (LIMIT_RGB(b_) << 2)); - break; - } - } - clipmask_even_index += clipmask_add; - f_even += stretch_bytes; - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { - *f_even++ = y[1]; - *f_even++ = u; - } else { - y_ = 76284 * (y[1] - 16); - - b_ = (y_ + vb) >> 16; - g_ = (y_ + uvg) >> 16; - r_ = (y_ + ur) >> 16; - - switch (frame->v4l2_format.format) { - case V4L2_PIX_FMT_RGB565: - g = LIMIT_RGB(g_); - *f_even++ = - (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_even++ = - (0x07 & (g >> 3)) | - (0xF8 & LIMIT_RGB(b_)); - break; - case V4L2_PIX_FMT_RGB24: - *f_even++ = LIMIT_RGB(r_); - *f_even++ = LIMIT_RGB(g_); - *f_even++ = LIMIT_RGB(b_); - break; - case V4L2_PIX_FMT_RGB32: - *f_even++ = LIMIT_RGB(r_); - *f_even++ = LIMIT_RGB(g_); - *f_even++ = LIMIT_RGB(b_); - f_even++; - break; - case V4L2_PIX_FMT_RGB555: - g = LIMIT_RGB(g_); - *f_even++ = (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_even++ = (0x03 & (g >> 3)) | - (0x7C & (LIMIT_RGB(b_) << 2)); - break; - } - } - clipmask_even_index += clipmask_add; - f_even += stretch_bytes; - - scratch_get_extra(usbvision, &y[0], &y_ptr, 2); - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { - *f_odd++ = y[0]; - *f_odd++ = v; - } else { - y_ = 76284 * (y[0] - 16); - - b_ = (y_ + vb) >> 16; - g_ = (y_ + uvg) >> 16; - r_ = (y_ + ur) >> 16; - - switch (frame->v4l2_format.format) { - case V4L2_PIX_FMT_RGB565: - g = LIMIT_RGB(g_); - *f_odd++ = - (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_odd++ = - (0x07 & (g >> 3)) | - (0xF8 & LIMIT_RGB(b_)); - break; - case V4L2_PIX_FMT_RGB24: - *f_odd++ = LIMIT_RGB(r_); - *f_odd++ = LIMIT_RGB(g_); - *f_odd++ = LIMIT_RGB(b_); - break; - case V4L2_PIX_FMT_RGB32: - *f_odd++ = LIMIT_RGB(r_); - *f_odd++ = LIMIT_RGB(g_); - *f_odd++ = LIMIT_RGB(b_); - f_odd++; - break; - case V4L2_PIX_FMT_RGB555: - g = LIMIT_RGB(g_); - *f_odd++ = (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_odd++ = (0x03 & (g >> 3)) | - (0x7C & (LIMIT_RGB(b_) << 2)); - break; - } - } - clipmask_odd_index += clipmask_add; - f_odd += stretch_bytes; - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { - *f_odd++ = y[1]; - *f_odd++ = u; - } else { - y_ = 76284 * (y[1] - 16); - - b_ = (y_ + vb) >> 16; - g_ = (y_ + uvg) >> 16; - r_ = (y_ + ur) >> 16; - - switch (frame->v4l2_format.format) { - case V4L2_PIX_FMT_RGB565: - g = LIMIT_RGB(g_); - *f_odd++ = - (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_odd++ = - (0x07 & (g >> 3)) | - (0xF8 & LIMIT_RGB(b_)); - break; - case V4L2_PIX_FMT_RGB24: - *f_odd++ = LIMIT_RGB(r_); - *f_odd++ = LIMIT_RGB(g_); - *f_odd++ = LIMIT_RGB(b_); - break; - case V4L2_PIX_FMT_RGB32: - *f_odd++ = LIMIT_RGB(r_); - *f_odd++ = LIMIT_RGB(g_); - *f_odd++ = LIMIT_RGB(b_); - f_odd++; - break; - case V4L2_PIX_FMT_RGB555: - g = LIMIT_RGB(g_); - *f_odd++ = (0x1F & LIMIT_RGB(r_)) | - (0xE0 & (g << 5)); - *f_odd++ = (0x03 & (g >> 3)) | - (0x7C & (LIMIT_RGB(b_) << 2)); - break; - } - } - clipmask_odd_index += clipmask_add; - f_odd += stretch_bytes; - } - - scratch_rm_old(usbvision, y_step[block % y_step_size] * sub_block_size); - scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size] - * sub_block_size); - scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size] - * sub_block_size); - scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size] - * sub_block_size); - } - - scratch_rm_old(usbvision, pixel_per_line * 3 / 2 - + block_split * sub_block_size); - - frame->curline += 2 * usbvision->stretch_height; - *pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height; - - if (frame->curline >= frame->frmheight) - return parse_state_next_frame; - return parse_state_continue; -} - -/* - * usbvision_parse_data() - * - * Generic routine to parse the scratch buffer. It employs either - * usbvision_find_header() or usbvision_parse_lines() to do most - * of work. - * - */ -static void usbvision_parse_data(struct usb_usbvision *usbvision) -{ - struct usbvision_frame *frame; - enum parse_state newstate; - long copylen = 0; - unsigned long lock_flags; - - frame = usbvision->cur_frame; - - PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision)); - - while (1) { - newstate = parse_state_out; - if (scratch_len(usbvision)) { - if (frame->scanstate == scan_state_scanning) { - newstate = usbvision_find_header(usbvision); - } else if (frame->scanstate == scan_state_lines) { - if (usbvision->isoc_mode == ISOC_MODE_YUV420) - newstate = usbvision_parse_lines_420(usbvision, ©len); - else if (usbvision->isoc_mode == ISOC_MODE_YUV422) - newstate = usbvision_parse_lines_422(usbvision, ©len); - else if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) - newstate = usbvision_parse_compress(usbvision, ©len); - } - } - if (newstate == parse_state_continue) - continue; - if ((newstate == parse_state_next_frame) || (newstate == parse_state_out)) - break; - return; /* parse_state_end_parse */ - } - - if (newstate == parse_state_next_frame) { - frame->grabstate = frame_state_done; - do_gettimeofday(&(frame->timestamp)); - frame->sequence = usbvision->frame_num; - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - list_move_tail(&(frame->frame), &usbvision->outqueue); - usbvision->cur_frame = NULL; - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - usbvision->frame_num++; - - /* This will cause the process to request another frame. */ - if (waitqueue_active(&usbvision->wait_frame)) { - PDEBUG(DBG_PARSE, "Wake up !"); - wake_up_interruptible(&usbvision->wait_frame); - } - } else { - frame->grabstate = frame_state_grabbing; - } - - /* Update the frame's uncompressed length. */ - frame->scanlength += copylen; -} - - -/* - * Make all of the blocks of data contiguous - */ -static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, - struct urb *urb) -{ - unsigned char *packet_data; - int i, totlen = 0; - - for (i = 0; i < urb->number_of_packets; i++) { - int packet_len = urb->iso_frame_desc[i].actual_length; - int packet_stat = urb->iso_frame_desc[i].status; - - packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* Detect and ignore errored packets */ - if (packet_stat) { /* packet_stat != 0 ????????????? */ - PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat); - usbvision->isoc_err_count++; - continue; - } - - /* Detect and ignore empty packets */ - if (packet_len < 0) { - PDEBUG(DBG_ISOC, "error packet [%d]", i); - usbvision->isoc_skip_count++; - continue; - } else if (packet_len == 0) { /* Frame end ????? */ - PDEBUG(DBG_ISOC, "null packet [%d]", i); - usbvision->isocstate = isoc_state_no_frame; - usbvision->isoc_skip_count++; - continue; - } else if (packet_len > usbvision->isoc_packet_size) { - PDEBUG(DBG_ISOC, "packet[%d] > isoc_packet_size", i); - usbvision->isoc_skip_count++; - continue; - } - - PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len); - - if (usbvision->isocstate == isoc_state_no_frame) { /* new frame begins */ - usbvision->isocstate = isoc_state_in_frame; - scratch_mark_header(usbvision); - usbvision_measure_bandwidth(usbvision); - PDEBUG(DBG_ISOC, "packet with header"); - } - - /* - * If usbvision continues to feed us with data but there is no - * consumption (if, for example, V4L client fell asleep) we - * may overflow the buffer. We have to move old data over to - * free room for new data. This is bad for old data. If we - * just drop new data then it's bad for new data... choose - * your favorite evil here. - */ - if (scratch_free(usbvision) < packet_len) { - usbvision->scratch_ovf_count++; - PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d", - scratch_len(usbvision), packet_len); - scratch_rm_old(usbvision, packet_len - scratch_free(usbvision)); - } - - /* Now we know that there is enough room in scratch buffer */ - scratch_put(usbvision, packet_data, packet_len); - totlen += packet_len; - usbvision->isoc_data_count += packet_len; - usbvision->isoc_packet_count++; - } -#if ENABLE_HEXDUMP - if (totlen > 0) { - static int foo; - - if (foo < 1) { - printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen); - usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen); - ++foo; - } - } -#endif - return totlen; -} - -static void usbvision_isoc_irq(struct urb *urb) -{ - int err_code = 0; - int len; - struct usb_usbvision *usbvision = urb->context; - int i; - unsigned long start_time = jiffies; - struct usbvision_frame **f; - - /* We don't want to do anything if we are about to be removed! */ - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return; - - /* any urb with wrong status is ignored without acknowledgement */ - if (urb->status == -ENOENT) - return; - - f = &usbvision->cur_frame; - - /* Manage streaming interruption */ - if (usbvision->streaming == stream_interrupt) { - usbvision->streaming = stream_idle; - if ((*f)) { - (*f)->grabstate = frame_state_ready; - (*f)->scanstate = scan_state_scanning; - } - PDEBUG(DBG_IRQ, "stream interrupted"); - wake_up_interruptible(&usbvision->wait_stream); - } - - /* Copy the data received into our scratch buffer */ - len = usbvision_compress_isochronous(usbvision, urb); - - usbvision->isoc_urb_count++; - usbvision->urb_length = len; - - if (usbvision->streaming == stream_on) { - /* If we collected enough data let's parse! */ - if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH && - !list_empty(&(usbvision->inqueue))) { - if (!(*f)) { - (*f) = list_entry(usbvision->inqueue.next, - struct usbvision_frame, - frame); - } - usbvision_parse_data(usbvision); - } else { - /* If we don't have a frame - we're current working on, complain */ - PDEBUG(DBG_IRQ, - "received data, but no one needs it"); - scratch_reset(usbvision); - } - } else { - PDEBUG(DBG_IRQ, "received data, but no one needs it"); - scratch_reset(usbvision); - } - - usbvision->time_in_irq += jiffies - start_time; - - for (i = 0; i < USBVISION_URB_FRAMES; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - - urb->status = 0; - urb->dev = usbvision->dev; - err_code = usb_submit_urb(urb, GFP_ATOMIC); - - if (err_code) { - dev_err(&usbvision->dev->dev, - "%s: usb_submit_urb failed: error %d\n", - __func__, err_code); - } - - return; -} - -/*************************************/ -/* Low level usbvision access functions */ -/*************************************/ - -/* - * usbvision_read_reg() - * - * return < 0 -> Error - * >= 0 -> Data - */ - -int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) -{ - int err_code = 0; - unsigned char buffer[1]; - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -1; - - err_code = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, - 0, (__u16) reg, buffer, 1, HZ); - - if (err_code < 0) { - dev_err(&usbvision->dev->dev, - "%s: failed: error %d\n", __func__, err_code); - return err_code; - } - return buffer[0]; -} - -/* - * usbvision_write_reg() - * - * return 1 -> Reg written - * 0 -> usbvision is not yet ready - * -1 -> Something went wrong - */ - -int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, - unsigned char value) -{ - int err_code = 0; - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return 0; - - err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ); - - if (err_code < 0) { - dev_err(&usbvision->dev->dev, - "%s: failed: error %d\n", __func__, err_code); - } - return err_code; -} - - -static void usbvision_ctrl_urb_complete(struct urb *urb) -{ - struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context; - - PDEBUG(DBG_IRQ, ""); - usbvision->ctrl_urb_busy = 0; - if (waitqueue_active(&usbvision->ctrl_urb_wq)) - wake_up_interruptible(&usbvision->ctrl_urb_wq); -} - - -static int usbvision_write_reg_irq(struct usb_usbvision *usbvision, int address, - unsigned char *data, int len) -{ - int err_code = 0; - - PDEBUG(DBG_IRQ, ""); - if (len > 8) - return -EFAULT; - if (usbvision->ctrl_urb_busy) - return -EBUSY; - usbvision->ctrl_urb_busy = 1; - - usbvision->ctrl_urb_setup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; - usbvision->ctrl_urb_setup.bRequest = USBVISION_OP_CODE; - usbvision->ctrl_urb_setup.wValue = 0; - usbvision->ctrl_urb_setup.wIndex = cpu_to_le16(address); - usbvision->ctrl_urb_setup.wLength = cpu_to_le16(len); - usb_fill_control_urb(usbvision->ctrl_urb, usbvision->dev, - usb_sndctrlpipe(usbvision->dev, 1), - (unsigned char *)&usbvision->ctrl_urb_setup, - (void *)usbvision->ctrl_urb_buffer, len, - usbvision_ctrl_urb_complete, - (void *)usbvision); - - memcpy(usbvision->ctrl_urb_buffer, data, len); - - err_code = usb_submit_urb(usbvision->ctrl_urb, GFP_ATOMIC); - if (err_code < 0) { - /* error in usb_submit_urb() */ - usbvision->ctrl_urb_busy = 0; - } - PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, err_code); - return err_code; -} - - -static int usbvision_init_compression(struct usb_usbvision *usbvision) -{ - int err_code = 0; - - usbvision->last_isoc_frame_num = -1; - usbvision->isoc_data_count = 0; - usbvision->isoc_packet_count = 0; - usbvision->isoc_skip_count = 0; - usbvision->compr_level = 50; - usbvision->last_compr_level = -1; - usbvision->isoc_urb_count = 0; - usbvision->request_intra = 1; - usbvision->isoc_measure_bandwidth_count = 0; - - return err_code; -} - -/* this function measures the used bandwidth since last call - * return: 0 : no error - * sets used_bandwidth to 1-100 : 1-100% of full bandwidth resp. to isoc_packet_size - */ -static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision) -{ - int err_code = 0; - - if (usbvision->isoc_measure_bandwidth_count < 2) { /* this gives an average bandwidth of 3 frames */ - usbvision->isoc_measure_bandwidth_count++; - return err_code; - } - if ((usbvision->isoc_packet_size > 0) && (usbvision->isoc_packet_count > 0)) { - usbvision->used_bandwidth = usbvision->isoc_data_count / - (usbvision->isoc_packet_count + usbvision->isoc_skip_count) * - 100 / usbvision->isoc_packet_size; - } - usbvision->isoc_measure_bandwidth_count = 0; - usbvision->isoc_data_count = 0; - usbvision->isoc_packet_count = 0; - usbvision->isoc_skip_count = 0; - return err_code; -} - -static int usbvision_adjust_compression(struct usb_usbvision *usbvision) -{ - int err_code = 0; - unsigned char buffer[6]; - - PDEBUG(DBG_IRQ, ""); - if ((adjust_compression) && (usbvision->used_bandwidth > 0)) { - usbvision->compr_level += (usbvision->used_bandwidth - 90) / 2; - RESTRICT_TO_RANGE(usbvision->compr_level, 0, 100); - if (usbvision->compr_level != usbvision->last_compr_level) { - int distortion; - - if (usbvision->bridge_type == BRIDGE_NT1004 || usbvision->bridge_type == BRIDGE_NT1005) { - buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM Threshold 1 */ - buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM Threshold 2 */ - distortion = 7 + 248 * usbvision->compr_level / 100; - buffer[2] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (inter) */ - buffer[3] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (intra) */ - distortion = 1 + 42 * usbvision->compr_level / 100; - buffer[4] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (inter) */ - buffer[5] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (intra) */ - } else { /* BRIDGE_NT1003 */ - buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM threshold 1 */ - buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM threshold 2 */ - distortion = 2 + 253 * usbvision->compr_level / 100; - buffer[2] = (unsigned char)(distortion & 0xFF); /* distortion threshold bit0-7 */ - buffer[3] = 0; /* (unsigned char)((distortion >> 8) & 0x0F); distortion threshold bit 8-11 */ - distortion = 0 + 43 * usbvision->compr_level / 100; - buffer[4] = (unsigned char)(distortion & 0xFF); /* maximum distortion bit0-7 */ - buffer[5] = 0; /* (unsigned char)((distortion >> 8) & 0x01); maximum distortion bit 8 */ - } - err_code = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6); - if (err_code == 0) { - PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0], - buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); - usbvision->last_compr_level = usbvision->compr_level; - } - } - } - return err_code; -} - -static int usbvision_request_intra(struct usb_usbvision *usbvision) -{ - int err_code = 0; - unsigned char buffer[1]; - - PDEBUG(DBG_IRQ, ""); - usbvision->request_intra = 1; - buffer[0] = 1; - usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); - return err_code; -} - -static int usbvision_unrequest_intra(struct usb_usbvision *usbvision) -{ - int err_code = 0; - unsigned char buffer[1]; - - PDEBUG(DBG_IRQ, ""); - usbvision->request_intra = 0; - buffer[0] = 0; - usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); - return err_code; -} - -/******************************* - * usbvision utility functions - *******************************/ - -int usbvision_power_off(struct usb_usbvision *usbvision) -{ - int err_code = 0; - - PDEBUG(DBG_FUNC, ""); - - err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); - if (err_code == 1) - usbvision->power = 0; - PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code != 1) ? "ERROR" : "power is off", err_code); - return err_code; -} - -/* configure webcam image sensor using the serial port */ -static int usbvision_init_webcam(struct usb_usbvision *usbvision) -{ - int rc; - int i; - static char init_values[38][3] = { - { 0x04, 0x12, 0x08 }, { 0x05, 0xff, 0xc8 }, { 0x06, 0x18, 0x07 }, { 0x07, 0x90, 0x00 }, - { 0x09, 0x00, 0x00 }, { 0x0a, 0x00, 0x00 }, { 0x0b, 0x08, 0x00 }, { 0x0d, 0xcc, 0xcc }, - { 0x0e, 0x13, 0x14 }, { 0x10, 0x9b, 0x83 }, { 0x11, 0x5a, 0x3f }, { 0x12, 0xe4, 0x73 }, - { 0x13, 0x88, 0x84 }, { 0x14, 0x89, 0x80 }, { 0x15, 0x00, 0x20 }, { 0x16, 0x00, 0x00 }, - { 0x17, 0xff, 0xa0 }, { 0x18, 0x6b, 0x20 }, { 0x19, 0x22, 0x40 }, { 0x1a, 0x10, 0x07 }, - { 0x1b, 0x00, 0x47 }, { 0x1c, 0x03, 0xe0 }, { 0x1d, 0x00, 0x00 }, { 0x1e, 0x00, 0x00 }, - { 0x1f, 0x00, 0x00 }, { 0x20, 0x00, 0x00 }, { 0x21, 0x00, 0x00 }, { 0x22, 0x00, 0x00 }, - { 0x23, 0x00, 0x00 }, { 0x24, 0x00, 0x00 }, { 0x25, 0x00, 0x00 }, { 0x26, 0x00, 0x00 }, - { 0x27, 0x00, 0x00 }, { 0x28, 0x00, 0x00 }, { 0x29, 0x00, 0x00 }, { 0x08, 0x80, 0x60 }, - { 0x0f, 0x2d, 0x24 }, { 0x0c, 0x80, 0x80 } - }; - char value[3]; - - /* the only difference between PAL and NTSC init_values */ - if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_NTSC) - init_values[4][1] = 0x34; - - for (i = 0; i < sizeof(init_values) / 3; i++) { - usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); - memcpy(value, init_values[i], 3); - rc = usb_control_msg(usbvision->dev, - usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_SER_DAT1, value, - 3, HZ); - if (rc < 0) - return rc; - usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SIO); - /* write 3 bytes to the serial port using SIO mode */ - usbvision_write_reg(usbvision, USBVISION_SER_CONT, 3 | 0x10); - usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0); - usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); - usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_IO_2); - usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT); - usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_DAT_IO); - usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT | USBVISION_DAT_IO); - } - - return 0; -} - -/* - * usbvision_set_video_format() - * - */ -static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format) -{ - static const char proc[] = "usbvision_set_video_format"; - int rc; - unsigned char value[2]; - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return 0; - - PDEBUG(DBG_FUNC, "isoc_mode %#02x", format); - - if ((format != ISOC_MODE_YUV422) - && (format != ISOC_MODE_YUV420) - && (format != ISOC_MODE_COMPRESS)) { - printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420", - format); - format = ISOC_MODE_YUV420; - } - value[0] = 0x0A; /* TODO: See the effect of the filter */ - value[1] = format; /* Sets the VO_MODE register which follows FILT_CONT */ - rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_FILT_CONT, value, 2, HZ); - - if (rc < 0) { - printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - " - "reconnect or reload driver.\n", proc, rc); - } - usbvision->isoc_mode = format; - return rc; -} - -/* - * usbvision_set_output() - * - */ - -int usbvision_set_output(struct usb_usbvision *usbvision, int width, - int height) -{ - int err_code = 0; - int usb_width, usb_height; - unsigned int frame_rate = 0, frame_drop = 0; - unsigned char value[4]; - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return 0; - - if (width > MAX_USB_WIDTH) { - usb_width = width / 2; - usbvision->stretch_width = 2; - } else { - usb_width = width; - usbvision->stretch_width = 1; - } - - if (height > MAX_USB_HEIGHT) { - usb_height = height / 2; - usbvision->stretch_height = 2; - } else { - usb_height = height; - usbvision->stretch_height = 1; - } - - RESTRICT_TO_RANGE(usb_width, MIN_FRAME_WIDTH, MAX_USB_WIDTH); - usb_width &= ~(MIN_FRAME_WIDTH-1); - RESTRICT_TO_RANGE(usb_height, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT); - usb_height &= ~(1); - - PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d", - usb_width, usb_height, width, height, - usbvision->stretch_width, usbvision->stretch_height); - - /* I'll not rewrite the same values */ - if ((usb_width != usbvision->curwidth) || (usb_height != usbvision->curheight)) { - value[0] = usb_width & 0xff; /* LSB */ - value[1] = (usb_width >> 8) & 0x03; /* MSB */ - value[2] = usb_height & 0xff; /* LSB */ - value[3] = (usb_height >> 8) & 0x03; /* MSB */ - - err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, - 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ); - - if (err_code < 0) { - dev_err(&usbvision->dev->dev, - "%s failed: error %d\n", __func__, err_code); - return err_code; - } - usbvision->curwidth = usbvision->stretch_width * usb_width; - usbvision->curheight = usbvision->stretch_height * usb_height; - } - - if (usbvision->isoc_mode == ISOC_MODE_YUV422) - frame_rate = (usbvision->isoc_packet_size * 1000) / (usb_width * usb_height * 2); - else if (usbvision->isoc_mode == ISOC_MODE_YUV420) - frame_rate = (usbvision->isoc_packet_size * 1000) / ((usb_width * usb_height * 12) / 8); - else - frame_rate = FRAMERATE_MAX; - - if (usbvision->tvnorm_id & V4L2_STD_625_50) - frame_drop = frame_rate * 32 / 25 - 1; - else if (usbvision->tvnorm_id & V4L2_STD_525_60) - frame_drop = frame_rate * 32 / 30 - 1; - - RESTRICT_TO_RANGE(frame_drop, FRAMERATE_MIN, FRAMERATE_MAX); - - PDEBUG(DBG_FUNC, "frame_rate %d fps, frame_drop %d", frame_rate, frame_drop); - - frame_drop = FRAMERATE_MAX; /* We can allow the maximum here, because dropping is controlled */ - - if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { - if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_PAL) - frame_drop = 25; - else - frame_drop = 30; - } - - /* frame_drop = 7; => frame_phase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ... - => frame_skip = 4; - => frame_rate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25; - - frame_drop = 9; => frame_phase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ... - => frame_skip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ... - => frame_rate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125; - */ - err_code = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frame_drop); - return err_code; -} - - -/* - * usbvision_frames_alloc - * allocate the required frames - */ -int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames) -{ - int i; - - /* needs to be page aligned cause the buffers can be mapped individually! */ - usbvision->max_frame_size = PAGE_ALIGN(usbvision->curwidth * - usbvision->curheight * - usbvision->palette.bytes_per_pixel); - - /* Try to do my best to allocate the frames the user want in the remaining memory */ - usbvision->num_frames = number_of_frames; - while (usbvision->num_frames > 0) { - usbvision->fbuf_size = usbvision->num_frames * usbvision->max_frame_size; - usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size); - if (usbvision->fbuf) - break; - usbvision->num_frames--; - } - - spin_lock_init(&usbvision->queue_lock); - init_waitqueue_head(&usbvision->wait_frame); - init_waitqueue_head(&usbvision->wait_stream); - - /* Allocate all buffers */ - for (i = 0; i < usbvision->num_frames; i++) { - usbvision->frame[i].index = i; - usbvision->frame[i].grabstate = frame_state_unused; - usbvision->frame[i].data = usbvision->fbuf + - i * usbvision->max_frame_size; - /* - * Set default sizes for read operation. - */ - usbvision->stretch_width = 1; - usbvision->stretch_height = 1; - usbvision->frame[i].width = usbvision->curwidth; - usbvision->frame[i].height = usbvision->curheight; - usbvision->frame[i].bytes_read = 0; - } - PDEBUG(DBG_FUNC, "allocated %d frames (%d bytes per frame)", - usbvision->num_frames, usbvision->max_frame_size); - return usbvision->num_frames; -} - -/* - * usbvision_frames_free - * frees memory allocated for the frames - */ -void usbvision_frames_free(struct usb_usbvision *usbvision) -{ - /* Have to free all that memory */ - PDEBUG(DBG_FUNC, "free %d frames", usbvision->num_frames); - - if (usbvision->fbuf != NULL) { - usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); - usbvision->fbuf = NULL; - - usbvision->num_frames = 0; - } -} -/* - * usbvision_empty_framequeues() - * prepare queues for incoming and outgoing frames - */ -void usbvision_empty_framequeues(struct usb_usbvision *usbvision) -{ - u32 i; - - INIT_LIST_HEAD(&(usbvision->inqueue)); - INIT_LIST_HEAD(&(usbvision->outqueue)); - - for (i = 0; i < USBVISION_NUMFRAMES; i++) { - usbvision->frame[i].grabstate = frame_state_unused; - usbvision->frame[i].bytes_read = 0; - } -} - -/* - * usbvision_stream_interrupt() - * stops streaming - */ -int usbvision_stream_interrupt(struct usb_usbvision *usbvision) -{ - int ret = 0; - - /* stop reading from the device */ - - usbvision->streaming = stream_interrupt; - ret = wait_event_timeout(usbvision->wait_stream, - (usbvision->streaming == stream_idle), - msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES)); - return ret; -} - -/* - * usbvision_set_compress_params() - * - */ - -static int usbvision_set_compress_params(struct usb_usbvision *usbvision) -{ - static const char proc[] = "usbvision_set_compresion_params: "; - int rc; - unsigned char value[6]; - - value[0] = 0x0F; /* Intra-Compression cycle */ - value[1] = 0x01; /* Reg.45 one line per strip */ - value[2] = 0x00; /* Reg.46 Force intra mode on all new frames */ - value[3] = 0x00; /* Reg.47 FORCE_UP <- 0 normal operation (not force) */ - value[4] = 0xA2; /* Reg.48 BUF_THR I'm not sure if this does something in not compressed mode. */ - value[5] = 0x00; /* Reg.49 DVI_YUV This has nothing to do with compression */ - - /* catched values for NT1004 */ - /* value[0] = 0xFF; Never apply intra mode automatically */ - /* value[1] = 0xF1; Use full frame height for virtual strip width; One line per strip */ - /* value[2] = 0x01; Force intra mode on all new frames */ - /* value[3] = 0x00; Strip size 400 Bytes; do not force up */ - /* value[4] = 0xA2; */ - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return 0; - - rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_INTRA_CYC, value, 5, HZ); - - if (rc < 0) { - printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " - "reconnect or reload driver.\n", proc, rc); - return rc; - } - - if (usbvision->bridge_type == BRIDGE_NT1004) { - value[0] = 20; /* PCM Threshold 1 */ - value[1] = 12; /* PCM Threshold 2 */ - value[2] = 255; /* Distortion Threshold inter */ - value[3] = 255; /* Distortion Threshold intra */ - value[4] = 43; /* Max Distortion inter */ - value[5] = 43; /* Max Distortion intra */ - } else { - value[0] = 20; /* PCM Threshold 1 */ - value[1] = 12; /* PCM Threshold 2 */ - value[2] = 255; /* Distortion Threshold d7-d0 */ - value[3] = 0; /* Distortion Threshold d11-d8 */ - value[4] = 43; /* Max Distortion d7-d0 */ - value[5] = 0; /* Max Distortion d8 */ - } - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return 0; - - rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_PCM_THR1, value, 6, HZ); - - if (rc < 0) { - printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " - "reconnect or reload driver.\n", proc, rc); - } - return rc; -} - - -/* - * usbvision_set_input() - * - * Set the input (saa711x, ...) size x y and other misc input params - * I've no idea if this parameters are right - * - */ -int usbvision_set_input(struct usb_usbvision *usbvision) -{ - static const char proc[] = "usbvision_set_input: "; - int rc; - unsigned char value[8]; - unsigned char dvi_yuv_value; - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return 0; - - /* Set input format expected from decoder*/ - if (usbvision_device_data[usbvision->dev_model].vin_reg1_override) { - value[0] = usbvision_device_data[usbvision->dev_model].vin_reg1; - } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) { - /* SAA7113 uses 8 bit output */ - value[0] = USBVISION_8_422_SYNC; - } else { - /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses - * as that is how saa7111 is configured */ - value[0] = USBVISION_16_422_SYNC; - /* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/ - } - - rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]); - if (rc < 0) { - printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " - "reconnect or reload driver.\n", proc, rc); - return rc; - } - - - if (usbvision->tvnorm_id & V4L2_STD_PAL) { - value[0] = 0xC0; - value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */ - value[2] = 0x20; - value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */ - value[4] = 0x60; - value[5] = 0x00; /* 0x0060 -> 96 Input video h offset */ - value[6] = 0x16; - value[7] = 0x00; /* 0x0016 -> 22 Input video v offset */ - } else if (usbvision->tvnorm_id & V4L2_STD_SECAM) { - value[0] = 0xC0; - value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */ - value[2] = 0x20; - value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */ - value[4] = 0x01; - value[5] = 0x00; /* 0x0001 -> 01 Input video h offset */ - value[6] = 0x01; - value[7] = 0x00; /* 0x0001 -> 01 Input video v offset */ - } else { /* V4L2_STD_NTSC */ - value[0] = 0xD0; - value[1] = 0x02; /* 0x02D0 -> 720 Input video line length */ - value[2] = 0xF0; - value[3] = 0x00; /* 0x00F0 -> 240 Input video number of lines */ - value[4] = 0x50; - value[5] = 0x00; /* 0x0050 -> 80 Input video h offset */ - value[6] = 0x10; - value[7] = 0x00; /* 0x0010 -> 16 Input video v offset */ - } - - /* webcam is only 480 pixels wide, both PAL and NTSC version */ - if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { - value[0] = 0xe0; - value[1] = 0x01; /* 0x01E0 -> 480 Input video line length */ - } - - if (usbvision_device_data[usbvision->dev_model].x_offset >= 0) { - value[4] = usbvision_device_data[usbvision->dev_model].x_offset & 0xff; - value[5] = (usbvision_device_data[usbvision->dev_model].x_offset & 0x0300) >> 8; - } - - if (adjust_x_offset != -1) { - value[4] = adjust_x_offset & 0xff; - value[5] = (adjust_x_offset & 0x0300) >> 8; - } - - if (usbvision_device_data[usbvision->dev_model].y_offset >= 0) { - value[6] = usbvision_device_data[usbvision->dev_model].y_offset & 0xff; - value[7] = (usbvision_device_data[usbvision->dev_model].y_offset & 0x0300) >> 8; - } - - if (adjust_y_offset != -1) { - value[6] = adjust_y_offset & 0xff; - value[7] = (adjust_y_offset & 0x0300) >> 8; - } - - rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, /* USBVISION specific code */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_LXSIZE_I, value, 8, HZ); - if (rc < 0) { - printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " - "reconnect or reload driver.\n", proc, rc); - return rc; - } - - - dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */ - - if (usbvision_device_data[usbvision->dev_model].dvi_yuv_override) { - dvi_yuv_value = usbvision_device_data[usbvision->dev_model].dvi_yuv; - } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) { - /* This changes as the fine sync control changes. Further investigation necessary */ - dvi_yuv_value = 0x06; - } - - return usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value); -} - - -/* - * usbvision_set_dram_settings() - * - * Set the buffer address needed by the usbvision dram to operate - * This values has been taken with usbsnoop. - * - */ - -static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) -{ - int rc; - unsigned char value[8]; - - if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) { - value[0] = 0x42; - value[1] = 0x71; - value[2] = 0xff; - value[3] = 0x00; - value[4] = 0x98; - value[5] = 0xe0; - value[6] = 0x71; - value[7] = 0xff; - /* UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte) */ - /* FDL: 0x00000-0x0E099 = 57498 Words */ - /* VDW: 0x0E3FF-0x3FFFF */ - } else { - value[0] = 0x42; - value[1] = 0x00; - value[2] = 0xff; - value[3] = 0x00; - value[4] = 0x00; - value[5] = 0x00; - value[6] = 0x00; - value[7] = 0xff; - } - /* These are the values of the address of the video buffer, - * they have to be loaded into the USBVISION_DRM_PRM1-8 - * - * Start address of video output buffer for read: drm_prm1-2 -> 0x00000 - * End address of video output buffer for read: drm_prm1-3 -> 0x1ffff - * Start address of video frame delay buffer: drm_prm1-4 -> 0x20000 - * Only used in compressed mode - * End address of video frame delay buffer: drm_prm1-5-6 -> 0x3ffff - * Only used in compressed mode - * Start address of video output buffer for write: drm_prm1-7 -> 0x00000 - * End address of video output buffer for write: drm_prm1-8 -> 0x1ffff - */ - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return 0; - - rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, /* USBVISION specific code */ - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_DRM_PRM1, value, 8, HZ); - - if (rc < 0) { - dev_err(&usbvision->dev->dev, "%s: ERROR=%d\n", __func__, rc); - return rc; - } - - /* Restart the video buffer logic */ - rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR | - USBVISION_RES_FDL | USBVISION_RES_VDW); - if (rc < 0) - return rc; - rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00); - - return rc; -} - -/* - * () - * - * Power on the device, enables suspend-resume logic - * & reset the isoc End-Point - * - */ - -int usbvision_power_on(struct usb_usbvision *usbvision) -{ - int err_code = 0; - - PDEBUG(DBG_FUNC, ""); - - usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); - usbvision_write_reg(usbvision, USBVISION_PWR_REG, - USBVISION_SSPND_EN | USBVISION_RES2); - - if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { - usbvision_write_reg(usbvision, USBVISION_VIN_REG1, - USBVISION_16_422_SYNC | USBVISION_HVALID_PO); - usbvision_write_reg(usbvision, USBVISION_VIN_REG2, - USBVISION_NOHVALID | USBVISION_KEEP_BLANK); - } - usbvision_write_reg(usbvision, USBVISION_PWR_REG, - USBVISION_SSPND_EN | USBVISION_PWR_VID); - mdelay(10); - err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, - USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2); - if (err_code == 1) - usbvision->power = 1; - PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code < 0) ? "ERROR" : "power is on", err_code); - return err_code; -} - - -/* - * usbvision timer stuff - */ - -/* to call usbvision_power_off from task queue */ -static void call_usbvision_power_off(struct work_struct *work) -{ - struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, power_off_work); - - PDEBUG(DBG_FUNC, ""); - if (mutex_lock_interruptible(&usbvision->v4l2_lock)) - return; - - if (usbvision->user == 0) { - usbvision_i2c_unregister(usbvision); - - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - mutex_unlock(&usbvision->v4l2_lock); -} - -static void usbvision_power_off_timer(unsigned long data) -{ - struct usb_usbvision *usbvision = (void *)data; - - PDEBUG(DBG_FUNC, ""); - del_timer(&usbvision->power_off_timer); - INIT_WORK(&usbvision->power_off_work, call_usbvision_power_off); - (void) schedule_work(&usbvision->power_off_work); -} - -void usbvision_init_power_off_timer(struct usb_usbvision *usbvision) -{ - init_timer(&usbvision->power_off_timer); - usbvision->power_off_timer.data = (long)usbvision; - usbvision->power_off_timer.function = usbvision_power_off_timer; -} - -void usbvision_set_power_off_timer(struct usb_usbvision *usbvision) -{ - mod_timer(&usbvision->power_off_timer, jiffies + USBVISION_POWEROFF_TIME); -} - -void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision) -{ - if (timer_pending(&usbvision->power_off_timer)) - del_timer(&usbvision->power_off_timer); -} - -/* - * usbvision_begin_streaming() - * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no - * idea about the rest - */ -int usbvision_begin_streaming(struct usb_usbvision *usbvision) -{ - if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) - usbvision_init_compression(usbvision); - return usbvision_write_reg(usbvision, USBVISION_VIN_REG2, - USBVISION_NOHVALID | usbvision->vin_reg2_preset); -} - -/* - * usbvision_restart_isoc() - * Not sure yet if touching here PWR_REG make loose the config - */ - -int usbvision_restart_isoc(struct usb_usbvision *usbvision) -{ - int ret; - - ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG, - USBVISION_SSPND_EN | USBVISION_PWR_VID); - if (ret < 0) - return ret; - ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG, - USBVISION_SSPND_EN | USBVISION_PWR_VID | - USBVISION_RES2); - if (ret < 0) - return ret; - ret = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, - USBVISION_KEEP_BLANK | USBVISION_NOHVALID | - usbvision->vin_reg2_preset); - if (ret < 0) - return ret; - - /* TODO: schedule timeout */ - while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) & 0x01) != 1) - ; - - return 0; -} - -int usbvision_audio_off(struct usb_usbvision *usbvision) -{ - if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) { - printk(KERN_ERR "usbvision_audio_off: can't write reg\n"); - return -1; - } - usbvision->audio_mute = 0; - usbvision->audio_channel = USBVISION_AUDIO_MUTE; - return 0; -} - -int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel) -{ - if (!usbvision->audio_mute) { - if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, audio_channel) < 0) { - printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n"); - return -1; - } - } - usbvision->audio_channel = audio_channel; - return 0; -} - -int usbvision_setup(struct usb_usbvision *usbvision, int format) -{ - if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) - usbvision_init_webcam(usbvision); - usbvision_set_video_format(usbvision, format); - usbvision_set_dram_settings(usbvision); - usbvision_set_compress_params(usbvision); - usbvision_set_input(usbvision); - usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT); - usbvision_restart_isoc(usbvision); - - /* cosas del PCM */ - return USBVISION_IS_OPERATIONAL(usbvision); -} - -int usbvision_set_alternate(struct usb_usbvision *dev) -{ - int err_code, prev_alt = dev->iface_alt; - int i; - - dev->iface_alt = 0; - for (i = 0; i < dev->num_alt; i++) - if (dev->alt_max_pkt_size[i] > dev->alt_max_pkt_size[dev->iface_alt]) - dev->iface_alt = i; - - if (dev->iface_alt != prev_alt) { - dev->isoc_packet_size = dev->alt_max_pkt_size[dev->iface_alt]; - PDEBUG(DBG_FUNC, "setting alternate %d with max_packet_size=%u", - dev->iface_alt, dev->isoc_packet_size); - err_code = usb_set_interface(dev->dev, dev->iface, dev->iface_alt); - if (err_code < 0) { - dev_err(&dev->dev->dev, - "cannot change alternate number to %d (error=%i)\n", - dev->iface_alt, err_code); - return err_code; - } - } - - PDEBUG(DBG_ISOC, "ISO Packet Length:%d", dev->isoc_packet_size); - - return 0; -} - -/* - * usbvision_init_isoc() - * - */ -int usbvision_init_isoc(struct usb_usbvision *usbvision) -{ - struct usb_device *dev = usbvision->dev; - int buf_idx, err_code, reg_value; - int sb_size; - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EFAULT; - - usbvision->cur_frame = NULL; - scratch_reset(usbvision); - - /* Alternate interface 1 is is the biggest frame size */ - err_code = usbvision_set_alternate(usbvision); - if (err_code < 0) { - usbvision->last_error = err_code; - return -EBUSY; - } - sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size; - - reg_value = (16 - usbvision_read_reg(usbvision, - USBVISION_ALTER_REG)) & 0x0F; - - usbvision->usb_bandwidth = reg_value >> 1; - PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", - usbvision->usb_bandwidth); - - - - /* We double buffer the Iso lists */ - - for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { - int j, k; - struct urb *urb; - - urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); - if (urb == NULL) { - dev_err(&usbvision->dev->dev, - "%s: usb_alloc_urb() failed\n", __func__); - return -ENOMEM; - } - usbvision->sbuf[buf_idx].urb = urb; - usbvision->sbuf[buf_idx].data = - usb_alloc_coherent(usbvision->dev, - sb_size, - GFP_KERNEL, - &urb->transfer_dma); - urb->dev = dev; - urb->context = usbvision; - urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp); - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->interval = 1; - urb->transfer_buffer = usbvision->sbuf[buf_idx].data; - urb->complete = usbvision_isoc_irq; - urb->number_of_packets = USBVISION_URB_FRAMES; - urb->transfer_buffer_length = - usbvision->isoc_packet_size * USBVISION_URB_FRAMES; - for (j = k = 0; j < USBVISION_URB_FRAMES; j++, - k += usbvision->isoc_packet_size) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - usbvision->isoc_packet_size; - } - } - - /* Submit all URBs */ - for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { - err_code = usb_submit_urb(usbvision->sbuf[buf_idx].urb, - GFP_KERNEL); - if (err_code) { - dev_err(&usbvision->dev->dev, - "%s: usb_submit_urb(%d) failed: error %d\n", - __func__, buf_idx, err_code); - } - } - - usbvision->streaming = stream_idle; - PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", - __func__, - usbvision->video_endp); - return 0; -} - -/* - * usbvision_stop_isoc() - * - * This procedure stops streaming and deallocates URBs. Then it - * activates zero-bandwidth alt. setting of the video interface. - * - */ -void usbvision_stop_isoc(struct usb_usbvision *usbvision) -{ - int buf_idx, err_code, reg_value; - int sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size; - - if ((usbvision->streaming == stream_off) || (usbvision->dev == NULL)) - return; - - /* Unschedule all of the iso td's */ - for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { - usb_kill_urb(usbvision->sbuf[buf_idx].urb); - if (usbvision->sbuf[buf_idx].data) { - usb_free_coherent(usbvision->dev, - sb_size, - usbvision->sbuf[buf_idx].data, - usbvision->sbuf[buf_idx].urb->transfer_dma); - } - usb_free_urb(usbvision->sbuf[buf_idx].urb); - usbvision->sbuf[buf_idx].urb = NULL; - } - - PDEBUG(DBG_ISOC, "%s: streaming=stream_off\n", __func__); - usbvision->streaming = stream_off; - - if (!usbvision->remove_pending) { - /* Set packet size to 0 */ - usbvision->iface_alt = 0; - err_code = usb_set_interface(usbvision->dev, usbvision->iface, - usbvision->iface_alt); - if (err_code < 0) { - dev_err(&usbvision->dev->dev, - "%s: usb_set_interface() failed: error %d\n", - __func__, err_code); - usbvision->last_error = err_code; - } - reg_value = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; - usbvision->isoc_packet_size = - (reg_value == 0) ? 0 : (reg_value * 64) - 1; - PDEBUG(DBG_ISOC, "ISO Packet Length:%d", - usbvision->isoc_packet_size); - - usbvision->usb_bandwidth = reg_value >> 1; - PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", - usbvision->usb_bandwidth); - } -} - -int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) -{ - /* inputs #0 and #3 are constant for every SAA711x. */ - /* inputs #1 and #2 are variable for SAA7111 and SAA7113 */ - int mode[4] = { SAA7115_COMPOSITE0, 0, 0, SAA7115_COMPOSITE3 }; - int audio[] = { 1, 0, 0, 0 }; - /* channel 0 is TV with audiochannel 1 (tuner mono) */ - /* channel 1 is Composite with audio channel 0 (line in) */ - /* channel 2 is S-Video with audio channel 0 (line in) */ - /* channel 3 is additional video inputs to the device with audio channel 0 (line in) */ - - RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs); - usbvision->ctl_input = channel; - - /* set the new channel */ - /* Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video */ - /* Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red */ - - switch (usbvision_device_data[usbvision->dev_model].codec) { - case CODEC_SAA7113: - mode[1] = SAA7115_COMPOSITE2; - if (switch_svideo_input) { - /* To handle problems with S-Video Input for - * some devices. Use switch_svideo_input - * parameter when loading the module.*/ - mode[2] = SAA7115_COMPOSITE1; - } else { - mode[2] = SAA7115_SVIDEO1; - } - break; - case CODEC_SAA7111: - default: - /* modes for saa7111 */ - mode[1] = SAA7115_COMPOSITE1; - mode[2] = SAA7115_SVIDEO1; - break; - } - call_all(usbvision, video, s_routing, mode[channel], 0, 0); - usbvision_set_audio(usbvision, audio[channel]); - return 0; -} - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c deleted file mode 100644 index 89fec029e924..000000000000 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * usbvision_i2c.c - * i2c algorithm for USB-I2C Bridges - * - * Copyright (c) 1999-2007 Joerg Heckenbach - * Dwaine Garden - * - * This module is part of usbvision driver project. - * Updates to driver completed by Dwaine P. Garden - * - * This program is free software; 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 -#include "usbvision.h" - -#define DBG_I2C (1 << 0) - -static int i2c_debug; - -module_param(i2c_debug, int, 0644); /* debug_i2c_usb mode of the device driver */ -MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define PDEBUG(level, fmt, args...) { \ - if (i2c_debug & (level)) \ - printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ - __func__, __LINE__ , ## args); \ - } - -static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, - short len); -static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, - short len); - -static inline int try_write_address(struct i2c_adapter *i2c_adap, - unsigned char addr, int retries) -{ - struct usb_usbvision *usbvision; - int i, ret = -1; - char buf[4]; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); - buf[0] = 0x00; - for (i = 0; i <= retries; i++) { - ret = (usbvision_i2c_write(usbvision, addr, buf, 1)); - if (ret == 1) - break; /* success! */ - udelay(5); - if (i == retries) /* no success */ - break; - udelay(10); - } - if (i) { - PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr); - PDEBUG(DBG_I2C, "Maybe there's no device at this address"); - } - return ret; -} - -static inline int try_read_address(struct i2c_adapter *i2c_adap, - unsigned char addr, int retries) -{ - struct usb_usbvision *usbvision; - int i, ret = -1; - char buf[4]; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); - for (i = 0; i <= retries; i++) { - ret = (usbvision_i2c_read(usbvision, addr, buf, 1)); - if (ret == 1) - break; /* success! */ - udelay(5); - if (i == retries) /* no success */ - break; - udelay(10); - } - if (i) { - PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr); - PDEBUG(DBG_I2C, "Maybe there's no device at this address"); - } - return ret; -} - -static inline int usb_find_address(struct i2c_adapter *i2c_adap, - struct i2c_msg *msg, int retries, - unsigned char *add) -{ - unsigned short flags = msg->flags; - - unsigned char addr; - int ret; - - addr = (msg->addr << 1); - if (flags & I2C_M_RD) - addr |= 1; - - add[0] = addr; - if (flags & I2C_M_RD) - ret = try_read_address(i2c_adap, addr, retries); - else - ret = try_write_address(i2c_adap, addr, retries); - - if (ret != 1) - return -EREMOTEIO; - - return 0; -} - -static int -usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) -{ - struct i2c_msg *pmsg; - struct usb_usbvision *usbvision; - int i, ret; - unsigned char addr = 0; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); - - for (i = 0; i < num; i++) { - pmsg = &msgs[i]; - ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); - if (ret != 0) { - PDEBUG(DBG_I2C, "got NAK from device, message #%d", i); - return (ret < 0) ? ret : -EREMOTEIO; - } - - if (pmsg->flags & I2C_M_RD) { - /* read bytes into buffer */ - ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len)); - if (ret < pmsg->len) - return (ret < 0) ? ret : -EREMOTEIO; - } else { - /* write bytes from buffer */ - ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len)); - if (ret < pmsg->len) - return (ret < 0) ? ret : -EREMOTEIO; - } - } - return num; -} - -static u32 functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -/* -----exported algorithm data: ------------------------------------- */ - -static struct i2c_algorithm usbvision_algo = { - .master_xfer = usbvision_i2c_xfer, - .smbus_xfer = NULL, - .functionality = functionality, -}; - - -/* ----------------------------------------------------------------------- */ -/* usbvision specific I2C functions */ -/* ----------------------------------------------------------------------- */ -static struct i2c_adapter i2c_adap_template; - -int usbvision_i2c_register(struct usb_usbvision *usbvision) -{ - static unsigned short saa711x_addrs[] = { - 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ - 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ - I2C_CLIENT_END }; - - if (usbvision->registered_i2c) - return 0; - - memcpy(&usbvision->i2c_adap, &i2c_adap_template, - sizeof(struct i2c_adapter)); - - sprintf(usbvision->i2c_adap.name, "%s-%d-%s", i2c_adap_template.name, - usbvision->dev->bus->busnum, usbvision->dev->devpath); - PDEBUG(DBG_I2C, "Adaptername: %s", usbvision->i2c_adap.name); - usbvision->i2c_adap.dev.parent = &usbvision->dev->dev; - - i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev); - - if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { - printk(KERN_ERR "usbvision_i2c_register: can't write reg\n"); - return -EBUSY; - } - - PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); - PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); - - /* register new adapter to i2c module... */ - - usbvision->i2c_adap.algo = &usbvision_algo; - - usbvision->i2c_adap.timeout = 100; /* default values, should */ - usbvision->i2c_adap.retries = 3; /* be replaced by defines */ - - i2c_add_adapter(&usbvision->i2c_adap); - - PDEBUG(DBG_I2C, "i2c bus for %s registered", usbvision->i2c_adap.name); - - /* Request the load of the i2c modules we need */ - switch (usbvision_device_data[usbvision->dev_model].codec) { - case CODEC_SAA7113: - case CODEC_SAA7111: - /* Without this delay the detection of the saa711x is - hit-and-miss. */ - mdelay(10); - v4l2_i2c_new_subdev(&usbvision->v4l2_dev, - &usbvision->i2c_adap, - "saa7115_auto", 0, saa711x_addrs); - break; - } - if (usbvision_device_data[usbvision->dev_model].tuner == 1) { - struct v4l2_subdev *sd; - enum v4l2_i2c_tuner_type type; - struct tuner_setup tun_setup; - - sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, - &usbvision->i2c_adap, - "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); - /* depending on whether we found a demod or not, select - the tuner type. */ - type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; - - sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, - &usbvision->i2c_adap, - "tuner", 0, v4l2_i2c_tuner_addrs(type)); - - if (sd == NULL) - return -ENODEV; - if (usbvision->tuner_type != -1) { - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = usbvision->tuner_type; - tun_setup.addr = v4l2_i2c_subdev_addr(sd); - call_all(usbvision, tuner, s_type_addr, &tun_setup); - } - } - usbvision->registered_i2c = 1; - - return 0; -} - -int usbvision_i2c_unregister(struct usb_usbvision *usbvision) -{ - if (!usbvision->registered_i2c) - return 0; - - i2c_del_adapter(&(usbvision->i2c_adap)); - usbvision->registered_i2c = 0; - - PDEBUG(DBG_I2C, "i2c bus for %s unregistered", usbvision->i2c_adap.name); - - return 0; -} - -static int -usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, - char *buf, short len) -{ - int rc, retries; - - for (retries = 5;;) { - rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr); - if (rc < 0) - return rc; - - /* Initiate byte read cycle */ - /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */ - /* d3 0=Wr 1=Rd */ - rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, - (len & 0x07) | 0x18); - if (rc < 0) - return rc; - - /* Test for Busy and ACK */ - do { - /* USBVISION_SER_CONT -> d4 == 0 busy */ - rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); - } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ - if (rc < 0) - return rc; - - /* USBVISION_SER_CONT -> d5 == 1 Not ack */ - if ((rc & 0x20) == 0) /* Ack? */ - break; - - /* I2C abort */ - rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); - if (rc < 0) - return rc; - - if (--retries < 0) - return -1; - } - - switch (len) { - case 4: - buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4); - case 3: - buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3); - case 2: - buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2); - case 1: - buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1); - break; - default: - printk(KERN_ERR - "usbvision_i2c_read_max4: buffer length > 4\n"); - } - - if (i2c_debug & DBG_I2C) { - int idx; - - for (idx = 0; idx < len; idx++) - PDEBUG(DBG_I2C, "read %x from address %x", (unsigned char)buf[idx], addr); - } - return len; -} - - -static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, - unsigned char addr, const char *buf, - short len) -{ - int rc, retries; - int i; - unsigned char value[6]; - unsigned char ser_cont; - - ser_cont = (len & 0x07) | 0x10; - - value[0] = addr; - value[1] = ser_cont; - for (i = 0; i < len; i++) - value[i + 2] = buf[i]; - - for (retries = 5;;) { - rc = usb_control_msg(usbvision->dev, - usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_SER_ADRS, value, - len + 2, HZ); - - if (rc < 0) - return rc; - - rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, - (len & 0x07) | 0x10); - if (rc < 0) - return rc; - - /* Test for Busy and ACK */ - do { - rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); - } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ - if (rc < 0) - return rc; - - if ((rc & 0x20) == 0) /* Ack? */ - break; - - /* I2C abort */ - usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); - - if (--retries < 0) - return -1; - - } - - if (i2c_debug & DBG_I2C) { - int idx; - - for (idx = 0; idx < len; idx++) - PDEBUG(DBG_I2C, "wrote %x at address %x", (unsigned char)buf[idx], addr); - } - return len; -} - -static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, - short len) -{ - char *buf_ptr = buf; - int retval; - int wrcount = 0; - int count; - int max_len = 4; - - while (len > 0) { - count = (len > max_len) ? max_len : len; - retval = usbvision_i2c_write_max4(usbvision, addr, buf_ptr, count); - if (retval > 0) { - len -= count; - buf_ptr += count; - wrcount += count; - } else - return (retval < 0) ? retval : -EFAULT; - } - return wrcount; -} - -static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, - short len) -{ - char temp[4]; - int retval, i; - int rdcount = 0; - int count; - - while (len > 0) { - count = (len > 3) ? 4 : len; - retval = usbvision_i2c_read_max4(usbvision, addr, temp, count); - if (retval > 0) { - for (i = 0; i < len; i++) - buf[rdcount + i] = temp[i]; - len -= count; - rdcount += count; - } else - return (retval < 0) ? retval : -EFAULT; - } - return rdcount; -} - -static struct i2c_adapter i2c_adap_template = { - .owner = THIS_MODULE, - .name = "usbvision", -}; - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c deleted file mode 100644 index 8a4317979a43..000000000000 --- a/drivers/media/video/usbvision/usbvision-video.c +++ /dev/null @@ -1,1720 +0,0 @@ -/* - * USB USBVISION Video device driver 0.9.10 - * - * - * - * Copyright (c) 1999-2005 Joerg Heckenbach - * - * This module is part of usbvision driver project. - * - * This program is free software; 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. - * - * Let's call the version 0.... until compression decoding is completely - * implemented. - * - * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach. - * It was based on USB CPiA driver written by Peter Pregler, - * Scott J. Bertin and Johannes Erdfelt - * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler & - * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink - * Updates to driver completed by Dwaine P. Garden - * - * - * TODO: - * - use submit_urb for all setup packets - * - Fix memory settings for nt1004. It is 4 times as big as the - * nt1003 memory. - * - Add audio on endpoint 3 for nt1004 chip. - * Seems impossible, needs a codec interface. Which one? - * - Clean up the driver. - * - optimization for performance. - * - Add Videotext capability (VBI). Working on it..... - * - Check audio for other devices - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "usbvision.h" -#include "usbvision-cards.h" - -#define DRIVER_AUTHOR \ - "Joerg Heckenbach , " \ - "Dwaine Garden " -#define DRIVER_NAME "usbvision" -#define DRIVER_ALIAS "USBVision" -#define DRIVER_DESC "USBVision USB Video Device Driver for Linux" -#define DRIVER_LICENSE "GPL" -#define USBVISION_VERSION_STRING "0.9.11" - -#define ENABLE_HEXDUMP 0 /* Enable if you need it */ - - -#ifdef USBVISION_DEBUG - #define PDEBUG(level, fmt, args...) { \ - if (video_debug & (level)) \ - printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ - __func__, __LINE__ , ## args); \ - } -#else - #define PDEBUG(level, fmt, args...) do {} while (0) -#endif - -#define DBG_IO (1 << 1) -#define DBG_PROBE (1 << 2) -#define DBG_MMAP (1 << 3) - -/* String operations */ -#define rmspace(str) while (*str == ' ') str++; -#define goto2next(str) while (*str != ' ') str++; while (*str == ' ') str++; - - -/* sequential number of usbvision device */ -static int usbvision_nr; - -static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { - { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, - { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, - { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, - { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, - { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, /* 1.5 ! */ - { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } -}; - -/* Function prototypes */ -static void usbvision_release(struct usb_usbvision *usbvision); - -/* Default initialization of device driver parameters */ -/* Set the default format for ISOC endpoint */ -static int isoc_mode = ISOC_MODE_COMPRESS; -/* Set the default Debug Mode of the device driver */ -static int video_debug; -/* Set the default device to power on at startup */ -static int power_on_at_open = 1; -/* Sequential Number of Video Device */ -static int video_nr = -1; -/* Sequential Number of Radio Device */ -static int radio_nr = -1; - -/* Grab parameters for the device driver */ - -/* Showing parameters under SYSFS */ -module_param(isoc_mode, int, 0444); -module_param(video_debug, int, 0444); -module_param(power_on_at_open, int, 0444); -module_param(video_nr, int, 0444); -module_param(radio_nr, int, 0444); - -MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); -MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); -MODULE_PARM_DESC(power_on_at_open, " Set the default device to power on when device is opened. Default: 1 (On)"); -MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); - - -/* Misc stuff */ -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); -MODULE_VERSION(USBVISION_VERSION_STRING); -MODULE_ALIAS(DRIVER_ALIAS); - - -/*****************************************************************************/ -/* SYSFS Code - Copied from the stv680.c usb module. */ -/* Device information is located at /sys/class/video4linux/video0 */ -/* Device parameters information is located at /sys/module/usbvision */ -/* Device USB Information is located at */ -/* /sys/bus/usb/drivers/USBVision Video Grabber */ -/*****************************************************************************/ - -#define YES_NO(x) ((x) ? "Yes" : "No") - -static inline struct usb_usbvision *cd_to_usbvision(struct device *cd) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - return video_get_drvdata(vdev); -} - -static ssize_t show_version(struct device *cd, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%s\n", USBVISION_VERSION_STRING); -} -static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); - -static ssize_t show_model(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", - usbvision_device_data[usbvision->dev_model].model_string); -} -static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); - -static ssize_t show_hue(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - struct v4l2_control ctrl; - ctrl.id = V4L2_CID_HUE; - ctrl.value = 0; - if (usbvision->user) - call_all(usbvision, core, g_ctrl, &ctrl); - return sprintf(buf, "%d\n", ctrl.value); -} -static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); - -static ssize_t show_contrast(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - struct v4l2_control ctrl; - ctrl.id = V4L2_CID_CONTRAST; - ctrl.value = 0; - if (usbvision->user) - call_all(usbvision, core, g_ctrl, &ctrl); - return sprintf(buf, "%d\n", ctrl.value); -} -static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); - -static ssize_t show_brightness(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - struct v4l2_control ctrl; - ctrl.id = V4L2_CID_BRIGHTNESS; - ctrl.value = 0; - if (usbvision->user) - call_all(usbvision, core, g_ctrl, &ctrl); - return sprintf(buf, "%d\n", ctrl.value); -} -static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); - -static ssize_t show_saturation(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - struct v4l2_control ctrl; - ctrl.id = V4L2_CID_SATURATION; - ctrl.value = 0; - if (usbvision->user) - call_all(usbvision, core, g_ctrl, &ctrl); - return sprintf(buf, "%d\n", ctrl.value); -} -static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); - -static ssize_t show_streaming(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", - YES_NO(usbvision->streaming == stream_on ? 1 : 0)); -} -static DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); - -static ssize_t show_compression(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", - YES_NO(usbvision->isoc_mode == ISOC_MODE_COMPRESS)); -} -static DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); - -static ssize_t show_device_bridge(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = - container_of(cd, struct video_device, dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%d\n", usbvision->bridge_type); -} -static DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); - -static void usbvision_create_sysfs(struct video_device *vdev) -{ - int res; - - if (!vdev) - return; - do { - res = device_create_file(&vdev->dev, &dev_attr_version); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_model); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_hue); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_contrast); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_brightness); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_saturation); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_streaming); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_compression); - if (res < 0) - break; - res = device_create_file(&vdev->dev, &dev_attr_bridge); - if (res >= 0) - return; - } while (0); - - dev_err(&vdev->dev, "%s error: %d\n", __func__, res); -} - -static void usbvision_remove_sysfs(struct video_device *vdev) -{ - if (vdev) { - device_remove_file(&vdev->dev, &dev_attr_version); - device_remove_file(&vdev->dev, &dev_attr_model); - device_remove_file(&vdev->dev, &dev_attr_hue); - device_remove_file(&vdev->dev, &dev_attr_contrast); - device_remove_file(&vdev->dev, &dev_attr_brightness); - device_remove_file(&vdev->dev, &dev_attr_saturation); - device_remove_file(&vdev->dev, &dev_attr_streaming); - device_remove_file(&vdev->dev, &dev_attr_compression); - device_remove_file(&vdev->dev, &dev_attr_bridge); - } -} - -/* - * usbvision_open() - * - * This is part of Video 4 Linux API. The driver can be opened by one - * client only (checks internal counter 'usbvision->user'). The procedure - * then allocates buffers needed for video processing. - * - */ -static int usbvision_v4l2_open(struct file *file) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int err_code = 0; - - PDEBUG(DBG_IO, "open"); - - if (mutex_lock_interruptible(&usbvision->v4l2_lock)) - return -ERESTARTSYS; - usbvision_reset_power_off_timer(usbvision); - - if (usbvision->user) - err_code = -EBUSY; - else { - /* Allocate memory for the scratch ring buffer */ - err_code = usbvision_scratch_alloc(usbvision); - if (isoc_mode == ISOC_MODE_COMPRESS) { - /* Allocate intermediate decompression buffers - only if needed */ - err_code = usbvision_decompress_alloc(usbvision); - } - if (err_code) { - /* Deallocate all buffers if trouble */ - usbvision_scratch_free(usbvision); - usbvision_decompress_free(usbvision); - } - } - - /* If so far no errors then we shall start the camera */ - if (!err_code) { - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } - - /* Send init sequence only once, it's large! */ - if (!usbvision->initialized) { - int setup_ok = 0; - setup_ok = usbvision_setup(usbvision, isoc_mode); - if (setup_ok) - usbvision->initialized = 1; - else - err_code = -EBUSY; - } - - if (!err_code) { - usbvision_begin_streaming(usbvision); - err_code = usbvision_init_isoc(usbvision); - /* device must be initialized before isoc transfer */ - usbvision_muxsel(usbvision, 0); - usbvision->user++; - } else { - if (power_on_at_open) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - } - } - - /* prepare queues */ - usbvision_empty_framequeues(usbvision); - mutex_unlock(&usbvision->v4l2_lock); - - PDEBUG(DBG_IO, "success"); - return err_code; -} - -/* - * usbvision_v4l2_close() - * - * This is part of Video 4 Linux API. The procedure - * stops streaming and deallocates all buffers that were earlier - * allocated in usbvision_v4l2_open(). - * - */ -static int usbvision_v4l2_close(struct file *file) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - PDEBUG(DBG_IO, "close"); - - mutex_lock(&usbvision->v4l2_lock); - usbvision_audio_off(usbvision); - usbvision_restart_isoc(usbvision); - usbvision_stop_isoc(usbvision); - - usbvision_decompress_free(usbvision); - usbvision_frames_free(usbvision); - usbvision_empty_framequeues(usbvision); - usbvision_scratch_free(usbvision); - - usbvision->user--; - - if (power_on_at_open) { - /* power off in a little while - to avoid off/on every close/open short sequences */ - usbvision_set_power_off_timer(usbvision); - usbvision->initialized = 0; - } - - if (usbvision->remove_pending) { - printk(KERN_INFO "%s: Final disconnect\n", __func__); - usbvision_release(usbvision); - } - mutex_unlock(&usbvision->v4l2_lock); - - PDEBUG(DBG_IO, "success"); - return 0; -} - - -/* - * usbvision_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * - */ -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int err_code; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - /* NT100x has a 8-bit register space */ - err_code = usbvision_read_reg(usbvision, reg->reg&0xff); - if (err_code < 0) { - dev_err(&usbvision->vdev->dev, - "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n", - __func__, err_code); - return err_code; - } - reg->val = err_code; - reg->size = 1; - return 0; -} - -static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int err_code; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - /* NT100x has a 8-bit register space */ - err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val); - if (err_code < 0) { - dev_err(&usbvision->vdev->dev, - "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n", - __func__, err_code); - return err_code; - } - return 0; -} -#endif - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *vc) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); - strlcpy(vc->card, - usbvision_device_data[usbvision->dev_model].model_string, - sizeof(vc->card)); - usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); - vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - return 0; -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *vi) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int chan; - - if (vi->index >= usbvision->video_inputs) - return -EINVAL; - if (usbvision->have_tuner) - chan = vi->index; - else - chan = vi->index + 1; /* skip Television string*/ - - /* Determine the requested input characteristics - specific for each usbvision card model */ - switch (chan) { - case 0: - if (usbvision_device_data[usbvision->dev_model].video_channels == 4) { - strcpy(vi->name, "White Video Input"); - } else { - strcpy(vi->name, "Television"); - vi->type = V4L2_INPUT_TYPE_TUNER; - vi->audioset = 1; - vi->tuner = chan; - vi->std = USBVISION_NORMS; - } - break; - case 1: - vi->type = V4L2_INPUT_TYPE_CAMERA; - if (usbvision_device_data[usbvision->dev_model].video_channels == 4) - strcpy(vi->name, "Green Video Input"); - else - strcpy(vi->name, "Composite Video Input"); - vi->std = V4L2_STD_PAL; - break; - case 2: - vi->type = V4L2_INPUT_TYPE_CAMERA; - if (usbvision_device_data[usbvision->dev_model].video_channels == 4) - strcpy(vi->name, "Yellow Video Input"); - else - strcpy(vi->name, "S-Video Input"); - vi->std = V4L2_STD_PAL; - break; - case 3: - vi->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(vi->name, "Red Video Input"); - vi->std = V4L2_STD_PAL; - break; - } - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - *input = usbvision->ctl_input; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int input) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - if (input >= usbvision->video_inputs) - return -EINVAL; - - usbvision_muxsel(usbvision, input); - usbvision_set_input(usbvision); - usbvision_set_output(usbvision, - usbvision->curwidth, - usbvision->curheight); - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - usbvision->tvnorm_id = *id; - - call_all(usbvision, core, s_std, usbvision->tvnorm_id); - /* propagate the change to the decoder */ - usbvision_muxsel(usbvision, usbvision->ctl_input); - - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *vt) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - if (!usbvision->have_tuner || vt->index) /* Only tuner 0 */ - return -EINVAL; - if (usbvision->radio) { - strcpy(vt->name, "Radio"); - vt->type = V4L2_TUNER_RADIO; - } else { - strcpy(vt->name, "Television"); - } - /* Let clients fill in the remainder of this struct */ - call_all(usbvision, tuner, g_tuner, vt); - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *vt) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - /* Only no or one tuner for now */ - if (!usbvision->have_tuner || vt->index) - return -EINVAL; - /* let clients handle this */ - call_all(usbvision, tuner, s_tuner, vt); - - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - freq->tuner = 0; /* Only one tuner */ - if (usbvision->radio) - freq->type = V4L2_TUNER_RADIO; - else - freq->type = V4L2_TUNER_ANALOG_TV; - freq->frequency = usbvision->freq; - - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - /* Only no or one tuner for now */ - if (!usbvision->have_tuner || freq->tuner) - return -EINVAL; - - usbvision->freq = freq->frequency; - call_all(usbvision, tuner, s_frequency, freq); - - return 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - if (usbvision->radio) - strcpy(a->name, "Radio"); - else - strcpy(a->name, "TV"); - - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, - struct v4l2_audio *a) -{ - if (a->index) - return -EINVAL; - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, queryctrl, ctrl); - - if (!ctrl->type) - return -EINVAL; - - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, g_ctrl, ctrl); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, s_ctrl, ctrl); - return 0; -} - -static int vidioc_reqbufs(struct file *file, - void *priv, struct v4l2_requestbuffers *vr) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int ret; - - RESTRICT_TO_RANGE(vr->count, 1, USBVISION_NUMFRAMES); - - /* Check input validity: - the user must do a VIDEO CAPTURE and MMAP method. */ - if (vr->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (usbvision->streaming == stream_on) { - ret = usbvision_stream_interrupt(usbvision); - if (ret) - return ret; - } - - usbvision_frames_free(usbvision); - usbvision_empty_framequeues(usbvision); - vr->count = usbvision_frames_alloc(usbvision, vr->count); - - usbvision->cur_frame = NULL; - - return 0; -} - -static int vidioc_querybuf(struct file *file, - void *priv, struct v4l2_buffer *vb) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - struct usbvision_frame *frame; - - /* FIXME : must control - that buffers are mapped (VIDIOC_REQBUFS has been called) */ - if (vb->index >= usbvision->num_frames) - return -EINVAL; - /* Updating the corresponding frame state */ - vb->flags = 0; - frame = &usbvision->frame[vb->index]; - if (frame->grabstate >= frame_state_ready) - vb->flags |= V4L2_BUF_FLAG_QUEUED; - if (frame->grabstate >= frame_state_done) - vb->flags |= V4L2_BUF_FLAG_DONE; - if (frame->grabstate == frame_state_unused) - vb->flags |= V4L2_BUF_FLAG_MAPPED; - vb->memory = V4L2_MEMORY_MMAP; - - vb->m.offset = vb->index * PAGE_ALIGN(usbvision->max_frame_size); - - vb->memory = V4L2_MEMORY_MMAP; - vb->field = V4L2_FIELD_NONE; - vb->length = usbvision->curwidth * - usbvision->curheight * - usbvision->palette.bytes_per_pixel; - vb->timestamp = usbvision->frame[vb->index].timestamp; - vb->sequence = usbvision->frame[vb->index].sequence; - return 0; -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *vb) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - struct usbvision_frame *frame; - unsigned long lock_flags; - - /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */ - if (vb->index >= usbvision->num_frames) - return -EINVAL; - - frame = &usbvision->frame[vb->index]; - - if (frame->grabstate != frame_state_unused) - return -EAGAIN; - - /* Mark it as ready and enqueue frame */ - frame->grabstate = frame_state_ready; - frame->scanstate = scan_state_scanning; - frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ - - vb->flags &= ~V4L2_BUF_FLAG_DONE; - - /* set v4l2_format index */ - frame->v4l2_format = usbvision->palette; - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int ret; - struct usbvision_frame *f; - unsigned long lock_flags; - - if (list_empty(&(usbvision->outqueue))) { - if (usbvision->streaming == stream_idle) - return -EINVAL; - ret = wait_event_interruptible - (usbvision->wait_frame, - !list_empty(&(usbvision->outqueue))); - if (ret) - return ret; - } - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - f = list_entry(usbvision->outqueue.next, - struct usbvision_frame, frame); - list_del(usbvision->outqueue.next); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - f->grabstate = frame_state_unused; - - vb->memory = V4L2_MEMORY_MMAP; - vb->flags = V4L2_BUF_FLAG_MAPPED | - V4L2_BUF_FLAG_QUEUED | - V4L2_BUF_FLAG_DONE; - vb->index = f->index; - vb->sequence = f->sequence; - vb->timestamp = f->timestamp; - vb->field = V4L2_FIELD_NONE; - vb->bytesused = f->scanlength; - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - usbvision->streaming = stream_on; - call_all(usbvision, video, s_stream, 1); - - return 0; -} - -static int vidioc_streamoff(struct file *file, - void *priv, enum v4l2_buf_type type) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (usbvision->streaming == stream_on) { - usbvision_stream_interrupt(usbvision); - /* Stop all video streamings */ - call_all(usbvision, video, s_stream, 0); - } - usbvision_empty_framequeues(usbvision); - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *vfd) -{ - if (vfd->index >= USBVISION_SUPPORTED_PALETTES - 1) - return -EINVAL; - strcpy(vfd->description, usbvision_v4l2_format[vfd->index].desc); - vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *vf) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - vf->fmt.pix.width = usbvision->curwidth; - vf->fmt.pix.height = usbvision->curheight; - vf->fmt.pix.pixelformat = usbvision->palette.format; - vf->fmt.pix.bytesperline = - usbvision->curwidth * usbvision->palette.bytes_per_pixel; - vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * usbvision->curheight; - vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ - - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *vf) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int format_idx; - - /* Find requested format in available ones */ - for (format_idx = 0; format_idx < USBVISION_SUPPORTED_PALETTES; format_idx++) { - if (vf->fmt.pix.pixelformat == - usbvision_v4l2_format[format_idx].format) { - usbvision->palette = usbvision_v4l2_format[format_idx]; - break; - } - } - /* robustness */ - if (format_idx == USBVISION_SUPPORTED_PALETTES) - return -EINVAL; - RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); - RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); - - vf->fmt.pix.bytesperline = vf->fmt.pix.width* - usbvision->palette.bytes_per_pixel; - vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *vf) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int ret; - - ret = vidioc_try_fmt_vid_cap(file, priv, vf); - if (ret) - return ret; - - /* stop io in case it is already in progress */ - if (usbvision->streaming == stream_on) { - ret = usbvision_stream_interrupt(usbvision); - if (ret) - return ret; - } - usbvision_frames_free(usbvision); - usbvision_empty_framequeues(usbvision); - - usbvision->cur_frame = NULL; - - /* by now we are committed to the new data... */ - usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); - - return 0; -} - -static ssize_t usbvision_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int noblock = file->f_flags & O_NONBLOCK; - unsigned long lock_flags; - int ret, i; - struct usbvision_frame *frame; - - PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __func__, - (unsigned long)count, noblock); - - if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) - return -EFAULT; - - /* This entry point is compatible with the mmap routines - so that a user can do either VIDIOC_QBUF/VIDIOC_DQBUF - to get frames or call read on the device. */ - if (!usbvision->num_frames) { - /* First, allocate some frames to work with - if this has not been done with VIDIOC_REQBUF */ - usbvision_frames_free(usbvision); - usbvision_empty_framequeues(usbvision); - usbvision_frames_alloc(usbvision, USBVISION_NUMFRAMES); - } - - if (usbvision->streaming != stream_on) { - /* no stream is running, make it running ! */ - usbvision->streaming = stream_on; - call_all(usbvision, video, s_stream, 1); - } - - /* Then, enqueue as many frames as possible - (like a user of VIDIOC_QBUF would do) */ - for (i = 0; i < usbvision->num_frames; i++) { - frame = &usbvision->frame[i]; - if (frame->grabstate == frame_state_unused) { - /* Mark it as ready and enqueue frame */ - frame->grabstate = frame_state_ready; - frame->scanstate = scan_state_scanning; - /* Accumulated in usbvision_parse_data() */ - frame->scanlength = 0; - - /* set v4l2_format index */ - frame->v4l2_format = usbvision->palette; - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - list_add_tail(&frame->frame, &usbvision->inqueue); - spin_unlock_irqrestore(&usbvision->queue_lock, - lock_flags); - } - } - - /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */ - if (list_empty(&(usbvision->outqueue))) { - if (noblock) - return -EAGAIN; - - ret = wait_event_interruptible - (usbvision->wait_frame, - !list_empty(&(usbvision->outqueue))); - if (ret) - return ret; - } - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - frame = list_entry(usbvision->outqueue.next, - struct usbvision_frame, frame); - list_del(usbvision->outqueue.next); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - /* An error returns an empty frame */ - if (frame->grabstate == frame_state_error) { - frame->bytes_read = 0; - return 0; - } - - PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", - __func__, - frame->index, frame->bytes_read, frame->scanlength); - - /* copy bytes to user space; we allow for partials reads */ - if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) - count = frame->scanlength - frame->bytes_read; - - if (copy_to_user(buf, frame->data + frame->bytes_read, count)) - return -EFAULT; - - frame->bytes_read += count; - PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", - __func__, - (unsigned long)count, frame->bytes_read); - - /* For now, forget the frame if it has not been read in one shot. */ -/* if (frame->bytes_read >= frame->scanlength) {*/ /* All data has been read */ - frame->bytes_read = 0; - - /* Mark it as available to be used again. */ - frame->grabstate = frame_state_unused; -/* } */ - - return count; -} - -static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int res; - - if (mutex_lock_interruptible(&usbvision->v4l2_lock)) - return -ERESTARTSYS; - res = usbvision_read(file, buf, count, ppos); - mutex_unlock(&usbvision->v4l2_lock); - return res; -} - -static int usbvision_mmap(struct file *file, struct vm_area_struct *vma) -{ - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - struct usb_usbvision *usbvision = video_drvdata(file); - - PDEBUG(DBG_MMAP, "mmap"); - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EFAULT; - - if (!(vma->vm_flags & VM_WRITE) || - size != PAGE_ALIGN(usbvision->max_frame_size)) { - return -EINVAL; - } - - for (i = 0; i < usbvision->num_frames; i++) { - if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) == - vma->vm_pgoff) - break; - } - if (i == usbvision->num_frames) { - PDEBUG(DBG_MMAP, - "mmap: user supplied mapping address is out of range"); - return -EINVAL; - } - - /* VM_IO is eventually going to replace PageReserved altogether */ - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ - - pos = usbvision->frame[i].data; - while (size > 0) { - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed"); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return 0; -} - -static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int res; - - if (mutex_lock_interruptible(&usbvision->v4l2_lock)) - return -ERESTARTSYS; - res = usbvision_mmap(file, vma); - mutex_unlock(&usbvision->v4l2_lock); - return res; -} - -/* - * Here comes the stuff for radio on usbvision based devices - * - */ -static int usbvision_radio_open(struct file *file) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int err_code = 0; - - PDEBUG(DBG_IO, "%s:", __func__); - - if (mutex_lock_interruptible(&usbvision->v4l2_lock)) - return -ERESTARTSYS; - if (usbvision->user) { - dev_err(&usbvision->rdev->dev, - "%s: Someone tried to open an already opened USBVision Radio!\n", - __func__); - err_code = -EBUSY; - } else { - if (power_on_at_open) { - usbvision_reset_power_off_timer(usbvision); - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } - } - - /* Alternate interface 1 is is the biggest frame size */ - err_code = usbvision_set_alternate(usbvision); - if (err_code < 0) { - usbvision->last_error = err_code; - err_code = -EBUSY; - goto out; - } - - /* If so far no errors then we shall start the radio */ - usbvision->radio = 1; - call_all(usbvision, tuner, s_radio); - usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); - usbvision->user++; - } - - if (err_code) { - if (power_on_at_open) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - } -out: - mutex_unlock(&usbvision->v4l2_lock); - return err_code; -} - - -static int usbvision_radio_close(struct file *file) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - int err_code = 0; - - PDEBUG(DBG_IO, ""); - - mutex_lock(&usbvision->v4l2_lock); - /* Set packet size to 0 */ - usbvision->iface_alt = 0; - err_code = usb_set_interface(usbvision->dev, usbvision->iface, - usbvision->iface_alt); - - usbvision_audio_off(usbvision); - usbvision->radio = 0; - usbvision->user--; - - if (power_on_at_open) { - usbvision_set_power_off_timer(usbvision); - usbvision->initialized = 0; - } - - if (usbvision->remove_pending) { - printk(KERN_INFO "%s: Final disconnect\n", __func__); - usbvision_release(usbvision); - } - - mutex_unlock(&usbvision->v4l2_lock); - PDEBUG(DBG_IO, "success"); - return err_code; -} - -/* Video registration stuff */ - -/* Video template */ -static const struct v4l2_file_operations usbvision_fops = { - .owner = THIS_MODULE, - .open = usbvision_v4l2_open, - .release = usbvision_v4l2_close, - .read = usbvision_v4l2_read, - .mmap = usbvision_v4l2_mmap, - .unlocked_ioctl = video_ioctl2, -/* .poll = video_poll, */ -}; - -static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_s_std = vidioc_s_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, -#endif -}; - -static struct video_device usbvision_video_template = { - .fops = &usbvision_fops, - .ioctl_ops = &usbvision_ioctl_ops, - .name = "usbvision-video", - .release = video_device_release, - .tvnorms = USBVISION_NORMS, - .current_norm = V4L2_STD_PAL -}; - - -/* Radio template */ -static const struct v4l2_file_operations usbvision_radio_fops = { - .owner = THIS_MODULE, - .open = usbvision_radio_open, - .release = usbvision_radio_close, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, -}; - -static struct video_device usbvision_radio_template = { - .fops = &usbvision_radio_fops, - .name = "usbvision-radio", - .release = video_device_release, - .ioctl_ops = &usbvision_radio_ioctl_ops, - - .tvnorms = USBVISION_NORMS, - .current_norm = V4L2_STD_PAL -}; - - -static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, - struct video_device *vdev_template, - char *name) -{ - struct usb_device *usb_dev = usbvision->dev; - struct video_device *vdev; - - if (usb_dev == NULL) { - dev_err(&usbvision->dev->dev, - "%s: usbvision->dev is not set\n", __func__); - return NULL; - } - - vdev = video_device_alloc(); - if (NULL == vdev) - return NULL; - *vdev = *vdev_template; - vdev->lock = &usbvision->v4l2_lock; - vdev->v4l2_dev = &usbvision->v4l2_dev; - snprintf(vdev->name, sizeof(vdev->name), "%s", name); - video_set_drvdata(vdev, usbvision); - return vdev; -} - -/* unregister video4linux devices */ -static void usbvision_unregister_video(struct usb_usbvision *usbvision) -{ - /* Radio Device: */ - if (usbvision->rdev) { - PDEBUG(DBG_PROBE, "unregister %s [v4l2]", - video_device_node_name(usbvision->rdev)); - if (video_is_registered(usbvision->rdev)) - video_unregister_device(usbvision->rdev); - else - video_device_release(usbvision->rdev); - usbvision->rdev = NULL; - } - - /* Video Device: */ - if (usbvision->vdev) { - PDEBUG(DBG_PROBE, "unregister %s [v4l2]", - video_device_node_name(usbvision->vdev)); - if (video_is_registered(usbvision->vdev)) - video_unregister_device(usbvision->vdev); - else - video_device_release(usbvision->vdev); - usbvision->vdev = NULL; - } -} - -/* register video4linux devices */ -static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) -{ - /* Video Device: */ - usbvision->vdev = usbvision_vdev_init(usbvision, - &usbvision_video_template, - "USBVision Video"); - if (usbvision->vdev == NULL) - goto err_exit; - if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) - goto err_exit; - printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", - usbvision->nr, video_device_node_name(usbvision->vdev)); - - /* Radio Device: */ - if (usbvision_device_data[usbvision->dev_model].radio) { - /* usbvision has radio */ - usbvision->rdev = usbvision_vdev_init(usbvision, - &usbvision_radio_template, - "USBVision Radio"); - if (usbvision->rdev == NULL) - goto err_exit; - if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0) - goto err_exit; - printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n", - usbvision->nr, video_device_node_name(usbvision->rdev)); - } - /* all done */ - return 0; - - err_exit: - dev_err(&usbvision->dev->dev, - "USBVision[%d]: video_register_device() failed\n", - usbvision->nr); - usbvision_unregister_video(usbvision); - return -1; -} - -/* - * usbvision_alloc() - * - * This code allocates the struct usb_usbvision. - * It is filled with default values. - * - * Returns NULL on error, a pointer to usb_usbvision else. - * - */ -static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, - struct usb_interface *intf) -{ - struct usb_usbvision *usbvision; - - usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL); - if (usbvision == NULL) - return NULL; - - usbvision->dev = dev; - if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev)) - goto err_free; - - mutex_init(&usbvision->v4l2_lock); - - /* prepare control urb for control messages during interrupts */ - usbvision->ctrl_urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); - if (usbvision->ctrl_urb == NULL) - goto err_unreg; - init_waitqueue_head(&usbvision->ctrl_urb_wq); - - usbvision_init_power_off_timer(usbvision); - - return usbvision; - -err_unreg: - v4l2_device_unregister(&usbvision->v4l2_dev); -err_free: - kfree(usbvision); - return NULL; -} - -/* - * usbvision_release() - * - * This code does final release of struct usb_usbvision. This happens - * after the device is disconnected -and- all clients closed their files. - * - */ -static void usbvision_release(struct usb_usbvision *usbvision) -{ - PDEBUG(DBG_PROBE, ""); - - usbvision_reset_power_off_timer(usbvision); - - usbvision->initialized = 0; - - usbvision_remove_sysfs(usbvision->vdev); - usbvision_unregister_video(usbvision); - - usb_free_urb(usbvision->ctrl_urb); - - v4l2_device_unregister(&usbvision->v4l2_dev); - kfree(usbvision); - - PDEBUG(DBG_PROBE, "success"); -} - - -/*********************** usb interface **********************************/ - -static void usbvision_configure_video(struct usb_usbvision *usbvision) -{ - int model; - - if (usbvision == NULL) - return; - - model = usbvision->dev_model; - usbvision->palette = usbvision_v4l2_format[2]; /* V4L2_PIX_FMT_RGB24; */ - - if (usbvision_device_data[usbvision->dev_model].vin_reg2_override) { - usbvision->vin_reg2_preset = - usbvision_device_data[usbvision->dev_model].vin_reg2; - } else { - usbvision->vin_reg2_preset = 0; - } - - usbvision->tvnorm_id = usbvision_device_data[model].video_norm; - - usbvision->video_inputs = usbvision_device_data[model].video_channels; - usbvision->ctl_input = 0; - - /* This should be here to make i2c clients to be able to register */ - /* first switch off audio */ - if (usbvision_device_data[model].audio_channels > 0) - usbvision_audio_off(usbvision); - if (!power_on_at_open) { - /* and then power up the noisy tuner */ - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } -} - -/* - * usbvision_probe() - * - * This procedure queries device descriptor and accepts the interface - * if it looks like USBVISION video device - * - */ -static int __devinit usbvision_probe(struct usb_interface *intf, - const struct usb_device_id *devid) -{ - struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf)); - struct usb_interface *uif; - __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; - const struct usb_host_interface *interface; - struct usb_usbvision *usbvision = NULL; - const struct usb_endpoint_descriptor *endpoint; - int model, i; - - PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", - dev->descriptor.idVendor, - dev->descriptor.idProduct, ifnum); - - model = devid->driver_info; - if (model < 0 || model >= usbvision_device_data_size) { - PDEBUG(DBG_PROBE, "model out of bounds %d", model); - return -ENODEV; - } - printk(KERN_INFO "%s: %s found\n", __func__, - usbvision_device_data[model].model_string); - - if (usbvision_device_data[model].interface >= 0) - interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0]; - else - interface = &dev->actconfig->interface[ifnum]->altsetting[0]; - endpoint = &interface->endpoint[1].desc; - if (!usb_endpoint_xfer_isoc(endpoint)) { - dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n", - __func__, ifnum); - dev_err(&intf->dev, "%s: Endpoint attributes %d", - __func__, endpoint->bmAttributes); - return -ENODEV; - } - if (usb_endpoint_dir_out(endpoint)) { - dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n", - __func__, ifnum); - return -ENODEV; - } - - usbvision = usbvision_alloc(dev, intf); - if (usbvision == NULL) { - dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__); - return -ENOMEM; - } - - if (dev->descriptor.bNumConfigurations > 1) - usbvision->bridge_type = BRIDGE_NT1004; - else if (model == DAZZLE_DVC_90_REV_1_SECAM) - usbvision->bridge_type = BRIDGE_NT1005; - else - usbvision->bridge_type = BRIDGE_NT1003; - PDEBUG(DBG_PROBE, "bridge_type %d", usbvision->bridge_type); - - /* compute alternate max packet sizes */ - uif = dev->actconfig->interface[0]; - - usbvision->num_alt = uif->num_altsetting; - PDEBUG(DBG_PROBE, "Alternate settings: %i", usbvision->num_alt); - usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL); - if (usbvision->alt_max_pkt_size == NULL) { - dev_err(&intf->dev, "usbvision: out of memory!\n"); - return -ENOMEM; - } - - for (i = 0; i < usbvision->num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. - wMaxPacketSize); - usbvision->alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - PDEBUG(DBG_PROBE, "Alternate setting %i, max size= %i", i, - usbvision->alt_max_pkt_size[i]); - } - - - usbvision->nr = usbvision_nr++; - - usbvision->have_tuner = usbvision_device_data[model].tuner; - if (usbvision->have_tuner) - usbvision->tuner_type = usbvision_device_data[model].tuner_type; - - usbvision->dev_model = model; - usbvision->remove_pending = 0; - usbvision->iface = ifnum; - usbvision->iface_alt = 0; - usbvision->video_endp = endpoint->bEndpointAddress; - usbvision->isoc_packet_size = 0; - usbvision->usb_bandwidth = 0; - usbvision->user = 0; - usbvision->streaming = stream_off; - usbvision_configure_video(usbvision); - usbvision_register_video(usbvision); - - usbvision_create_sysfs(usbvision->vdev); - - PDEBUG(DBG_PROBE, "success"); - return 0; -} - - -/* - * usbvision_disconnect() - * - * This procedure stops all driver activity, deallocates interface-private - * structure (pointed by 'ptr') and after that driver should be removable - * with no ill consequences. - * - */ -static void __devexit usbvision_disconnect(struct usb_interface *intf) -{ - struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf)); - - PDEBUG(DBG_PROBE, ""); - - if (usbvision == NULL) { - pr_err("%s: usb_get_intfdata() failed\n", __func__); - return; - } - - mutex_lock(&usbvision->v4l2_lock); - - /* At this time we ask to cancel outstanding URBs */ - usbvision_stop_isoc(usbvision); - - v4l2_device_disconnect(&usbvision->v4l2_dev); - - if (usbvision->power) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - } - usbvision->remove_pending = 1; /* Now all ISO data will be ignored */ - - usb_put_dev(usbvision->dev); - usbvision->dev = NULL; /* USB device is no more */ - - mutex_unlock(&usbvision->v4l2_lock); - - if (usbvision->user) { - printk(KERN_INFO "%s: In use, disconnect pending\n", - __func__); - wake_up_interruptible(&usbvision->wait_frame); - wake_up_interruptible(&usbvision->wait_stream); - } else { - usbvision_release(usbvision); - } - - PDEBUG(DBG_PROBE, "success"); -} - -static struct usb_driver usbvision_driver = { - .name = "usbvision", - .id_table = usbvision_table, - .probe = usbvision_probe, - .disconnect = __devexit_p(usbvision_disconnect), -}; - -/* - * usbvision_init() - * - * This code is run to initialize the driver. - * - */ -static int __init usbvision_init(void) -{ - int err_code; - - PDEBUG(DBG_PROBE, ""); - - PDEBUG(DBG_IO, "IO debugging is enabled [video]"); - PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]"); - PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]"); - - /* disable planar mode support unless compression enabled */ - if (isoc_mode != ISOC_MODE_COMPRESS) { - /* FIXME : not the right way to set supported flag */ - usbvision_v4l2_format[6].supported = 0; /* V4L2_PIX_FMT_YVU420 */ - usbvision_v4l2_format[7].supported = 0; /* V4L2_PIX_FMT_YUV422P */ - } - - err_code = usb_register(&usbvision_driver); - - if (err_code == 0) { - printk(KERN_INFO DRIVER_DESC " : " USBVISION_VERSION_STRING "\n"); - PDEBUG(DBG_PROBE, "success"); - } - return err_code; -} - -static void __exit usbvision_exit(void) -{ - PDEBUG(DBG_PROBE, ""); - - usb_deregister(&usbvision_driver); - PDEBUG(DBG_PROBE, "success"); -} - -module_init(usbvision_init); -module_exit(usbvision_exit); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h deleted file mode 100644 index 43cf61fe4943..000000000000 --- a/drivers/media/video/usbvision/usbvision.h +++ /dev/null @@ -1,535 +0,0 @@ -/* - * USBVISION.H - * usbvision header file - * - * Copyright (c) 1999-2005 Joerg Heckenbach - * Dwaine Garden - * - * - * Report problems to v4l MailingList: linux-media@vger.kernel.org - * - * This module is part of usbvision driver project. - * Updates to driver completed by Dwaine P. Garden - * v4l2 conversion by Thierry Merle - * - * This program is free software; 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 __LINUX_USBVISION_H -#define __LINUX_USBVISION_H - -#include -#include -#include -#include -#include -#include -#include - -#define USBVISION_DEBUG /* Turn on debug messages */ - -#define USBVISION_PWR_REG 0x00 - #define USBVISION_SSPND_EN (1 << 1) - #define USBVISION_RES2 (1 << 2) - #define USBVISION_PWR_VID (1 << 5) - #define USBVISION_E2_EN (1 << 7) -#define USBVISION_CONFIG_REG 0x01 -#define USBVISION_ADRS_REG 0x02 -#define USBVISION_ALTER_REG 0x03 -#define USBVISION_FORCE_ALTER_REG 0x04 -#define USBVISION_STATUS_REG 0x05 -#define USBVISION_IOPIN_REG 0x06 - #define USBVISION_IO_1 (1 << 0) - #define USBVISION_IO_2 (1 << 1) - #define USBVISION_AUDIO_IN 0 - #define USBVISION_AUDIO_TV 1 - #define USBVISION_AUDIO_RADIO 2 - #define USBVISION_AUDIO_MUTE 3 -#define USBVISION_SER_MODE 0x07 - #define USBVISION_CLK_OUT (1 << 0) - #define USBVISION_DAT_IO (1 << 1) - #define USBVISION_SENS_OUT (1 << 2) - #define USBVISION_SER_MODE_SOFT (0 << 4) - #define USBVISION_SER_MODE_SIO (1 << 4) -#define USBVISION_SER_ADRS 0x08 -#define USBVISION_SER_CONT 0x09 -#define USBVISION_SER_DAT1 0x0A -#define USBVISION_SER_DAT2 0x0B -#define USBVISION_SER_DAT3 0x0C -#define USBVISION_SER_DAT4 0x0D -#define USBVISION_EE_DATA 0x0E -#define USBVISION_EE_LSBAD 0x0F -#define USBVISION_EE_CONT 0x10 -#define USBVISION_DRM_CONT 0x12 - #define USBVISION_REF (1 << 0) - #define USBVISION_RES_UR (1 << 2) - #define USBVISION_RES_FDL (1 << 3) - #define USBVISION_RES_VDW (1 << 4) -#define USBVISION_DRM_PRM1 0x13 -#define USBVISION_DRM_PRM2 0x14 -#define USBVISION_DRM_PRM3 0x15 -#define USBVISION_DRM_PRM4 0x16 -#define USBVISION_DRM_PRM5 0x17 -#define USBVISION_DRM_PRM6 0x18 -#define USBVISION_DRM_PRM7 0x19 -#define USBVISION_DRM_PRM8 0x1A -#define USBVISION_VIN_REG1 0x1B - #define USBVISION_8_422_SYNC 0x01 - #define USBVISION_16_422_SYNC 0x02 - #define USBVISION_VSNC_POL (1 << 3) - #define USBVISION_HSNC_POL (1 << 4) - #define USBVISION_FID_POL (1 << 5) - #define USBVISION_HVALID_PO (1 << 6) - #define USBVISION_VCLK_POL (1 << 7) -#define USBVISION_VIN_REG2 0x1C - #define USBVISION_AUTO_FID (1 << 0) - #define USBVISION_NONE_INTER (1 << 1) - #define USBVISION_NOHVALID (1 << 2) - #define USBVISION_UV_ID (1 << 3) - #define USBVISION_FIX_2C (1 << 4) - #define USBVISION_SEND_FID (1 << 5) - #define USBVISION_KEEP_BLANK (1 << 7) -#define USBVISION_LXSIZE_I 0x1D -#define USBVISION_MXSIZE_I 0x1E -#define USBVISION_LYSIZE_I 0x1F -#define USBVISION_MYSIZE_I 0x20 -#define USBVISION_LX_OFFST 0x21 -#define USBVISION_MX_OFFST 0x22 -#define USBVISION_LY_OFFST 0x23 -#define USBVISION_MY_OFFST 0x24 -#define USBVISION_FRM_RATE 0x25 -#define USBVISION_LXSIZE_O 0x26 -#define USBVISION_MXSIZE_O 0x27 -#define USBVISION_LYSIZE_O 0x28 -#define USBVISION_MYSIZE_O 0x29 -#define USBVISION_FILT_CONT 0x2A -#define USBVISION_VO_MODE 0x2B -#define USBVISION_INTRA_CYC 0x2C -#define USBVISION_STRIP_SZ 0x2D -#define USBVISION_FORCE_INTRA 0x2E -#define USBVISION_FORCE_UP 0x2F -#define USBVISION_BUF_THR 0x30 -#define USBVISION_DVI_YUV 0x31 -#define USBVISION_AUDIO_CONT 0x32 -#define USBVISION_AUD_PK_LEN 0x33 -#define USBVISION_BLK_PK_LEN 0x34 -#define USBVISION_PCM_THR1 0x38 -#define USBVISION_PCM_THR2 0x39 -#define USBVISION_DIST_THR_L 0x3A -#define USBVISION_DIST_THR_H 0x3B -#define USBVISION_MAX_DIST_L 0x3C -#define USBVISION_MAX_DIST_H 0x3D -#define USBVISION_OP_CODE 0x33 - -#define MAX_BYTES_PER_PIXEL 4 - -#define MIN_FRAME_WIDTH 64 -#define MAX_USB_WIDTH 320 /* 384 */ -#define MAX_FRAME_WIDTH 320 /* 384 */ /* streching sometimes causes crashes*/ - -#define MIN_FRAME_HEIGHT 48 -#define MAX_USB_HEIGHT 240 /* 288 */ -#define MAX_FRAME_HEIGHT 240 /* 288 */ /* Streching sometimes causes crashes*/ - -#define MAX_FRAME_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL) -#define USBVISION_CLIPMASK_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) /* bytesize of clipmask */ - -#define USBVISION_URB_FRAMES 32 - -#define USBVISION_NUM_HEADERMARKER 20 -#define USBVISION_NUMFRAMES 3 /* Maximum number of frames an application can get */ -#define USBVISION_NUMSBUF 2 /* Dimensioning the USB S buffering */ - -#define USBVISION_POWEROFF_TIME (3 * HZ) /* 3 seconds */ - - -#define FRAMERATE_MIN 0 -#define FRAMERATE_MAX 31 - -enum { - ISOC_MODE_YUV422 = 0x03, - ISOC_MODE_YUV420 = 0x14, - ISOC_MODE_COMPRESS = 0x60, -}; - -/* This macro restricts an int variable to an inclusive range */ -#define RESTRICT_TO_RANGE(v, mi, ma) \ - { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } - -/* - * We use macros to do YUV -> RGB conversion because this is - * very important for speed and totally unimportant for size. - * - * YUV -> RGB Conversion - * --------------------- - * - * B = 1.164*(Y-16) + 2.018*(V-128) - * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) - * R = 1.164*(Y-16) + 1.596*(U-128) - * - * If you fancy integer arithmetics (as you should), hear this: - * - * 65536*B = 76284*(Y-16) + 132252*(V-128) - * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) - * 65536*R = 76284*(Y-16) + 104595*(U-128) - * - * Make sure the output values are within [0..255] range. - */ -#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) -#define YUV_TO_RGB_BY_THE_BOOK(my, mu, mv, mr, mg, mb) { \ - int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ - mm_y = (my) - 16; \ - mm_u = (mu) - 128; \ - mm_v = (mv) - 128; \ - mm_yc = mm_y * 76284; \ - mm_b = (mm_yc + 132252 * mm_v) >> 16; \ - mm_g = (mm_yc - 53281 * mm_u - 25625 * mm_v) >> 16; \ - mm_r = (mm_yc + 104595 * mm_u) >> 16; \ - mb = LIMIT_RGB(mm_b); \ - mg = LIMIT_RGB(mm_g); \ - mr = LIMIT_RGB(mm_r); \ -} - -/* Debugging aid */ -#define USBVISION_SAY_AND_WAIT(what) { \ - wait_queue_head_t wq; \ - init_waitqueue_head(&wq); \ - printk(KERN_INFO "Say: %s\n", what); \ - interruptible_sleep_on_timeout(&wq, HZ * 3); \ -} - -/* - * This macro checks if usbvision is still operational. The 'usbvision' - * pointer must be valid, usbvision->dev must be valid, we are not - * removing the device and the device has not erred on us. - */ -#define USBVISION_IS_OPERATIONAL(udevice) (\ - (udevice != NULL) && \ - ((udevice)->dev != NULL) && \ - ((udevice)->last_error == 0) && \ - (!(udevice)->remove_pending)) - -#define I2C_USB_ADAP_MAX 16 - -#define USBVISION_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_M) - -/* ----------------------------------------------------------------- */ -/* usbvision video structures */ -/* ----------------------------------------------------------------- */ -enum scan_state { - scan_state_scanning, /* Scanning for header */ - scan_state_lines /* Parsing lines */ -}; - -/* Completion states of the data parser */ -enum parse_state { - parse_state_continue, /* Just parse next item */ - parse_state_next_frame, /* Frame done, send it to V4L */ - parse_state_out, /* Not enough data for frame */ - parse_state_end_parse /* End parsing */ -}; - -enum frame_state { - frame_state_unused, /* Unused (no MCAPTURE) */ - frame_state_ready, /* Ready to start grabbing */ - frame_state_grabbing, /* In the process of being grabbed into */ - frame_state_done, /* Finished grabbing, but not been synced yet */ - frame_state_done_hold, /* Are syncing or reading */ - frame_state_error, /* Something bad happened while processing */ -}; - -/* stream states */ -enum stream_state { - stream_off, /* Driver streaming is completely OFF */ - stream_idle, /* Driver streaming is ready to be put ON by the application */ - stream_interrupt, /* Driver streaming must be interrupted */ - stream_on, /* Driver streaming is put ON by the application */ -}; - -enum isoc_state { - isoc_state_in_frame, /* Isoc packet is member of frame */ - isoc_state_no_frame, /* Isoc packet is not member of any frame */ -}; - -struct usb_device; - -struct usbvision_sbuf { - char *data; - struct urb *urb; -}; - -#define USBVISION_MAGIC_1 0x55 -#define USBVISION_MAGIC_2 0xAA -#define USBVISION_HEADER_LENGTH 0x0c -#define USBVISION_SAA7111_ADDR 0x48 -#define USBVISION_SAA7113_ADDR 0x4a -#define USBVISION_IIC_LRACK 0x20 -#define USBVISION_IIC_LRNACK 0x30 -#define USBVISION_FRAME_FORMAT_PARAM_INTRA (1<<7) - -struct usbvision_v4l2_format_st { - int supported; - int bytes_per_pixel; - int depth; - int format; - char *desc; -}; -#define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format) - -struct usbvision_frame_header { - unsigned char magic_1; /* 0 magic */ - unsigned char magic_2; /* 1 magic */ - unsigned char header_length; /* 2 */ - unsigned char frame_num; /* 3 */ - unsigned char frame_phase; /* 4 */ - unsigned char frame_latency; /* 5 */ - unsigned char data_format; /* 6 */ - unsigned char format_param; /* 7 */ - unsigned char frame_width_lo; /* 8 */ - unsigned char frame_width_hi; /* 9 */ - unsigned char frame_height_lo; /* 10 */ - unsigned char frame_height_hi; /* 11 */ - __u16 frame_width; /* 8 - 9 after endian correction*/ - __u16 frame_height; /* 10 - 11 after endian correction*/ -}; - -struct usbvision_frame { - char *data; /* Frame buffer */ - struct usbvision_frame_header isoc_header; /* Header from stream */ - - int width; /* Width application is expecting */ - int height; /* Height */ - int index; /* Frame index */ - int frmwidth; /* Width the frame actually is */ - int frmheight; /* Height */ - - volatile int grabstate; /* State of grabbing */ - int scanstate; /* State of scanning */ - - struct list_head frame; - - int curline; /* Line of frame we're working on */ - - long scanlength; /* uncompressed, raw data length of frame */ - long bytes_read; /* amount of scanlength that has been read from data */ - struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/ - int v4l2_linesize; /* bytes for one videoline*/ - struct timeval timestamp; - int sequence; /* How many video frames we send to user */ -}; - -#define CODEC_SAA7113 7113 -#define CODEC_SAA7111 7111 -#define CODEC_WEBCAM 3000 -#define BRIDGE_NT1003 1003 -#define BRIDGE_NT1004 1004 -#define BRIDGE_NT1005 1005 - -struct usbvision_device_data_st { - __u64 video_norm; - const char *model_string; - int interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ - __u16 codec; - unsigned video_channels:3; - unsigned audio_channels:2; - unsigned radio:1; - unsigned vbi:1; - unsigned tuner:1; - unsigned vin_reg1_override:1; /* Override default value with */ - unsigned vin_reg2_override:1; /* vin_reg1, vin_reg2, etc. */ - unsigned dvi_yuv_override:1; - __u8 vin_reg1; - __u8 vin_reg2; - __u8 dvi_yuv; - __u8 tuner_type; - __s16 x_offset; - __s16 y_offset; -}; - -/* Declared on usbvision-cards.c */ -extern struct usbvision_device_data_st usbvision_device_data[]; -extern struct usb_device_id usbvision_table[]; - -struct usb_usbvision { - struct v4l2_device v4l2_dev; - struct video_device *vdev; /* Video Device */ - struct video_device *rdev; /* Radio Device */ - - /* i2c Declaration Section*/ - struct i2c_adapter i2c_adap; - int registered_i2c; - - struct urb *ctrl_urb; - unsigned char ctrl_urb_buffer[8]; - int ctrl_urb_busy; - struct usb_ctrlrequest ctrl_urb_setup; - wait_queue_head_t ctrl_urb_wq; /* Processes waiting */ - - /* configuration part */ - int have_tuner; - int tuner_type; - int bridge_type; /* NT1003, NT1004, NT1005 */ - int radio; - int video_inputs; /* # of inputs */ - unsigned long freq; - int audio_mute; - int audio_channel; - int isoc_mode; /* format of video data for the usb isoc-transfer */ - unsigned int nr; /* Number of the device */ - - /* Device structure */ - struct usb_device *dev; - /* usb transfer */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of max_packet_size */ - unsigned char iface; /* Video interface number */ - unsigned char iface_alt; /* Alt settings */ - unsigned char vin_reg2_preset; - struct mutex v4l2_lock; - struct timer_list power_off_timer; - struct work_struct power_off_work; - int power; /* is the device powered on? */ - int user; /* user count for exclusive use */ - int initialized; /* Had we already sent init sequence? */ - int dev_model; /* What type of USBVISION device we got? */ - enum stream_state streaming; /* Are we streaming Isochronous? */ - int last_error; /* What calamity struck us? */ - int curwidth; /* width of the frame the device is currently set to*/ - int curheight; /* height of the frame the device is currently set to*/ - int stretch_width; /* stretch-factor for frame width (from usb to screen)*/ - int stretch_height; /* stretch-factor for frame height (from usb to screen)*/ - char *fbuf; /* Videodev buffer area for mmap*/ - int max_frame_size; /* Bytes in one video frame */ - int fbuf_size; /* Videodev buffer size */ - spinlock_t queue_lock; /* spinlock for protecting mods on inqueue and outqueue */ - struct list_head inqueue, outqueue; /* queued frame list and ready to dequeue frame list */ - wait_queue_head_t wait_frame; /* Processes waiting */ - wait_queue_head_t wait_stream; /* Processes waiting */ - struct usbvision_frame *cur_frame; /* pointer to current frame, set by usbvision_find_header */ - struct usbvision_frame frame[USBVISION_NUMFRAMES]; /* frame buffer */ - int num_frames; /* number of frames allocated */ - struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; /* S buffering */ - volatile int remove_pending; /* If set then about to exit */ - - /* Scratch space from the Isochronous Pipe.*/ - unsigned char *scratch; - int scratch_read_ptr; - int scratch_write_ptr; - int scratch_headermarker[USBVISION_NUM_HEADERMARKER]; - int scratch_headermarker_read_ptr; - int scratch_headermarker_write_ptr; - enum isoc_state isocstate; - struct usbvision_v4l2_format_st palette; - - struct v4l2_capability vcap; /* Video capabilities */ - unsigned int ctl_input; /* selected input */ - v4l2_std_id tvnorm_id; /* selected tv norm */ - unsigned char video_endp; /* 0x82 for USBVISION devices based */ - - /* Decompression stuff: */ - unsigned char *intra_frame_buffer; /* Buffer for reference frame */ - int block_pos; /* for test only */ - int request_intra; /* 0 = normal; 1 = intra frame is requested; */ - int last_isoc_frame_num; /* check for lost isoc frames */ - int isoc_packet_size; /* need to calculate used_bandwidth */ - int used_bandwidth; /* used bandwidth 0-100%, need to set compr_level */ - int compr_level; /* How strong (100) or weak (0) is compression */ - int last_compr_level; /* How strong (100) or weak (0) was compression */ - int usb_bandwidth; /* Mbit/s */ - - /* Statistics that can be overlayed on the screen */ - unsigned long isoc_urb_count; /* How many URBs we received so far */ - unsigned long urb_length; /* Length of last URB */ - unsigned long isoc_data_count; /* How many bytes we received */ - unsigned long header_count; /* How many frame headers we found */ - unsigned long scratch_ovf_count; /* How many times we overflowed scratch */ - unsigned long isoc_skip_count; /* How many empty ISO packets received */ - unsigned long isoc_err_count; /* How many bad ISO packets received */ - unsigned long isoc_packet_count; /* How many packets we totally got */ - unsigned long time_in_irq; /* How long do we need for interrupt */ - int isoc_measure_bandwidth_count; - int frame_num; /* How many video frames we send to user */ - int max_strip_len; /* How big is the biggest strip */ - int comprblock_pos; - int strip_len_errors; /* How many times was block_pos greater than strip_len */ - int strip_magic_errors; - int strip_line_number_errors; - int compr_block_types[4]; -}; - -static inline struct usb_usbvision *to_usbvision(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct usb_usbvision, v4l2_dev); -} - -#define call_all(usbvision, o, f, args...) \ - v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args) - -/* --------------------------------------------------------------- */ -/* defined in usbvision-i2c.c */ -/* i2c-algo-usb declaration */ -/* --------------------------------------------------------------- */ - -/* ----------------------------------------------------------------------- */ -/* usbvision specific I2C functions */ -/* ----------------------------------------------------------------------- */ -int usbvision_i2c_register(struct usb_usbvision *usbvision); -int usbvision_i2c_unregister(struct usb_usbvision *usbvision); - -/* defined in usbvision-core.c */ -int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); -int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, - unsigned char value); - -int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames); -void usbvision_frames_free(struct usb_usbvision *usbvision); -int usbvision_scratch_alloc(struct usb_usbvision *usbvision); -void usbvision_scratch_free(struct usb_usbvision *usbvision); -int usbvision_decompress_alloc(struct usb_usbvision *usbvision); -void usbvision_decompress_free(struct usb_usbvision *usbvision); - -int usbvision_setup(struct usb_usbvision *usbvision, int format); -int usbvision_init_isoc(struct usb_usbvision *usbvision); -int usbvision_restart_isoc(struct usb_usbvision *usbvision); -void usbvision_stop_isoc(struct usb_usbvision *usbvision); -int usbvision_set_alternate(struct usb_usbvision *dev); - -int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel); -int usbvision_audio_off(struct usb_usbvision *usbvision); - -int usbvision_begin_streaming(struct usb_usbvision *usbvision); -void usbvision_empty_framequeues(struct usb_usbvision *dev); -int usbvision_stream_interrupt(struct usb_usbvision *dev); - -int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); -int usbvision_set_input(struct usb_usbvision *usbvision); -int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); - -void usbvision_init_power_off_timer(struct usb_usbvision *usbvision); -void usbvision_set_power_off_timer(struct usb_usbvision *usbvision); -void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision); -int usbvision_power_off(struct usb_usbvision *usbvision); -int usbvision_power_on(struct usb_usbvision *usbvision); - -#endif /* __LINUX_USBVISION_H */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/video/uvc/Kconfig deleted file mode 100644 index 541c9f1e4c6a..000000000000 --- a/drivers/media/video/uvc/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -config USB_VIDEO_CLASS - tristate "USB Video Class (UVC)" - select VIDEOBUF2_VMALLOC - ---help--- - Support for the USB Video Class (UVC). Currently only video - input devices, such as webcams, are supported. - - For more information see: - -config USB_VIDEO_CLASS_INPUT_EVDEV - bool "UVC input events device support" - default y - depends on USB_VIDEO_CLASS - depends on USB_VIDEO_CLASS=INPUT || INPUT=y - ---help--- - This option makes USB Video Class devices register an input device - to report button events. - - If you are in doubt, say Y. diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile deleted file mode 100644 index c26d12fdb8f4..000000000000 --- a/drivers/media/video/uvc/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ - uvc_status.o uvc_isight.o uvc_debugfs.o -ifeq ($(CONFIG_MEDIA_CONTROLLER),y) -uvcvideo-objs += uvc_entity.o -endif -obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c deleted file mode 100644 index f7061a5ef1d2..000000000000 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ /dev/null @@ -1,2165 +0,0 @@ -/* - * uvc_ctrl.c -- USB Video Class driver - Controls - * - * Copyright (C) 2005-2010 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "uvcvideo.h" - -#define UVC_CTRL_DATA_CURRENT 0 -#define UVC_CTRL_DATA_BACKUP 1 -#define UVC_CTRL_DATA_MIN 2 -#define UVC_CTRL_DATA_MAX 3 -#define UVC_CTRL_DATA_RES 4 -#define UVC_CTRL_DATA_DEF 5 -#define UVC_CTRL_DATA_LAST 6 - -/* ------------------------------------------------------------------------ - * Controls - */ - -static struct uvc_control_info uvc_ctrls[] = { - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_BRIGHTNESS_CONTROL, - .index = 0, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_CONTRAST_CONTROL, - .index = 1, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_HUE_CONTROL, - .index = 2, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_SATURATION_CONTROL, - .index = 3, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_SHARPNESS_CONTROL, - .index = 4, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_GAMMA_CONTROL, - .index = 5, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, - .index = 6, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, - .index = 7, - .size = 4, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, - .index = 8, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_GAIN_CONTROL, - .index = 9, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .index = 10, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_HUE_AUTO_CONTROL, - .index = 11, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, - .index = 12, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, - .index = 13, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL, - .index = 14, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, - .index = 15, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL, - .index = 16, - .size = 1, - .flags = UVC_CTRL_FLAG_GET_CUR, - }, - { - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL, - .index = 17, - .size = 1, - .flags = UVC_CTRL_FLAG_GET_CUR, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_SCANNING_MODE_CONTROL, - .index = 0, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_AE_MODE_CONTROL, - .index = 1, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_GET_RES - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_AE_PRIORITY_CONTROL, - .index = 2, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, - .index = 3, - .size = 4, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL, - .index = 4, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, - .index = 5, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_FOCUS_RELATIVE_CONTROL, - .index = 6, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN - | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES - | UVC_CTRL_FLAG_GET_DEF - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, - .index = 7, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_IRIS_RELATIVE_CONTROL, - .index = 8, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, - .index = 9, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, - .index = 10, - .size = 3, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN - | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES - | UVC_CTRL_FLAG_GET_DEF - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, - .index = 11, - .size = 8, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, - .index = 12, - .size = 4, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN - | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES - | UVC_CTRL_FLAG_GET_DEF - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_ROLL_ABSOLUTE_CONTROL, - .index = 13, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR - | UVC_CTRL_FLAG_GET_RANGE - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_ROLL_RELATIVE_CONTROL, - .index = 14, - .size = 2, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN - | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES - | UVC_CTRL_FLAG_GET_DEF - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_FOCUS_AUTO_CONTROL, - .index = 17, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE, - }, - { - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_PRIVACY_CONTROL, - .index = 18, - .size = 1, - .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR - | UVC_CTRL_FLAG_RESTORE - | UVC_CTRL_FLAG_AUTO_UPDATE, - }, -}; - -static struct uvc_menu_info power_line_frequency_controls[] = { - { 0, "Disabled" }, - { 1, "50 Hz" }, - { 2, "60 Hz" }, -}; - -static struct uvc_menu_info exposure_auto_controls[] = { - { 2, "Auto Mode" }, - { 1, "Manual Mode" }, - { 4, "Shutter Priority Mode" }, - { 8, "Aperture Priority Mode" }, -}; - -static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, - __u8 query, const __u8 *data) -{ - __s8 zoom = (__s8)data[0]; - - switch (query) { - case UVC_GET_CUR: - return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); - - case UVC_GET_MIN: - case UVC_GET_MAX: - case UVC_GET_RES: - case UVC_GET_DEF: - default: - return data[2]; - } -} - -static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, - __s32 value, __u8 *data) -{ - data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; - data[2] = min((int)abs(value), 0xff); -} - -static struct uvc_control_mapping uvc_ctrl_mappings[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_BRIGHTNESS_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_SIGNED, - }, - { - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_CONTRAST_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_HUE, - .name = "Hue", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_HUE_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_SIGNED, - .master_id = V4L2_CID_HUE_AUTO, - .master_manual = 0, - }, - { - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_SATURATION_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_SHARPNESS, - .name = "Sharpness", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_SHARPNESS_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_GAMMA, - .name = "Gamma", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_GAMMA_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_BACKLIGHT_COMPENSATION, - .name = "Backlight Compensation", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_GAIN, - .name = "Gain", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_GAIN_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .name = "Power Line Frequency", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls, - .menu_count = ARRAY_SIZE(power_line_frequency_controls), - }, - { - .id = V4L2_CID_HUE_AUTO, - .name = "Hue, Auto", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_HUE_AUTO_CONTROL, - .size = 1, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, - .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, - .slave_ids = { V4L2_CID_HUE, }, - }, - { - .id = V4L2_CID_EXPOSURE_AUTO, - .name = "Exposure, Auto", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_AE_MODE_CONTROL, - .size = 4, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_BITMASK, - .menu_info = exposure_auto_controls, - .menu_count = ARRAY_SIZE(exposure_auto_controls), - .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, }, - }, - { - .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, - .name = "Exposure, Auto Priority", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_AE_PRIORITY_CONTROL, - .size = 1, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, - .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, - }, - { - .id = V4L2_CID_EXPOSURE_ABSOLUTE, - .name = "Exposure (Absolute)", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, - .size = 32, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - .master_id = V4L2_CID_EXPOSURE_AUTO, - .master_manual = V4L2_EXPOSURE_MANUAL, - }, - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .name = "White Balance Temperature, Auto", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, - .size = 1, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, - .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, - .slave_ids = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, }, - }, - { - .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, - .name = "White Balance Temperature", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - .master_id = V4L2_CID_AUTO_WHITE_BALANCE, - .master_manual = 0, - }, - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .name = "White Balance Component, Auto", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, - .size = 1, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, - .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, - .slave_ids = { V4L2_CID_BLUE_BALANCE, - V4L2_CID_RED_BALANCE }, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .name = "White Balance Blue Component", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_SIGNED, - .master_id = V4L2_CID_AUTO_WHITE_BALANCE, - .master_manual = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .name = "White Balance Red Component", - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, - .size = 16, - .offset = 16, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_SIGNED, - .master_id = V4L2_CID_AUTO_WHITE_BALANCE, - .master_manual = 0, - }, - { - .id = V4L2_CID_FOCUS_ABSOLUTE, - .name = "Focus (absolute)", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - .master_id = V4L2_CID_FOCUS_AUTO, - .master_manual = 0, - }, - { - .id = V4L2_CID_FOCUS_AUTO, - .name = "Focus, Auto", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_FOCUS_AUTO_CONTROL, - .size = 1, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, - .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, - .slave_ids = { V4L2_CID_FOCUS_ABSOLUTE, }, - }, - { - .id = V4L2_CID_IRIS_ABSOLUTE, - .name = "Iris, Absolute", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_IRIS_RELATIVE, - .name = "Iris, Relative", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_IRIS_RELATIVE_CONTROL, - .size = 8, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_SIGNED, - }, - { - .id = V4L2_CID_ZOOM_ABSOLUTE, - .name = "Zoom, Absolute", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, - .size = 16, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_ZOOM_CONTINUOUS, - .name = "Zoom, Continuous", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, - .size = 0, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_SIGNED, - .get = uvc_ctrl_get_zoom, - .set = uvc_ctrl_set_zoom, - }, - { - .id = V4L2_CID_PAN_ABSOLUTE, - .name = "Pan (Absolute)", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, - .size = 32, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_TILT_ABSOLUTE, - .name = "Tilt (Absolute)", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, - .size = 32, - .offset = 32, - .v4l2_type = V4L2_CTRL_TYPE_INTEGER, - .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, - }, - { - .id = V4L2_CID_PRIVACY, - .name = "Privacy", - .entity = UVC_GUID_UVC_CAMERA, - .selector = UVC_CT_PRIVACY_CONTROL, - .size = 1, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, - .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, - }, -}; - -/* ------------------------------------------------------------------------ - * Utility functions - */ - -static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) -{ - return ctrl->uvc_data + id * ctrl->info.size; -} - -static inline int uvc_test_bit(const __u8 *data, int bit) -{ - return (data[bit >> 3] >> (bit & 7)) & 1; -} - -static inline void uvc_clear_bit(__u8 *data, int bit) -{ - data[bit >> 3] &= ~(1 << (bit & 7)); -} - -/* Extract the bit string specified by mapping->offset and mapping->size - * from the little-endian data stored at 'data' and return the result as - * a signed 32bit integer. Sign extension will be performed if the mapping - * references a signed data type. - */ -static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, - __u8 query, const __u8 *data) -{ - int bits = mapping->size; - int offset = mapping->offset; - __s32 value = 0; - __u8 mask; - - data += offset / 8; - offset &= 7; - mask = ((1LL << bits) - 1) << offset; - - for (; bits > 0; data++) { - __u8 byte = *data & mask; - value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); - bits -= 8 - (offset > 0 ? offset : 0); - offset -= 8; - mask = (1 << bits) - 1; - } - - /* Sign-extend the value if needed. */ - if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) - value |= -(value & (1 << (mapping->size - 1))); - - return value; -} - -/* Set the bit string specified by mapping->offset and mapping->size - * in the little-endian data stored at 'data' to the value 'value'. - */ -static void uvc_set_le_value(struct uvc_control_mapping *mapping, - __s32 value, __u8 *data) -{ - int bits = mapping->size; - int offset = mapping->offset; - __u8 mask; - - /* According to the v4l2 spec, writing any value to a button control - * should result in the action belonging to the button control being - * triggered. UVC devices however want to see a 1 written -> override - * value. - */ - if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON) - value = -1; - - data += offset / 8; - offset &= 7; - - for (; bits > 0; data++) { - mask = ((1LL << bits) - 1) << offset; - *data = (*data & ~mask) | ((value << offset) & mask); - value >>= offset ? offset : 8; - bits -= 8 - offset; - offset = 0; - } -} - -/* ------------------------------------------------------------------------ - * Terminal and unit management - */ - -static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; -static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; -static const __u8 uvc_media_transport_input_guid[16] = - UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; - -static int uvc_entity_match_guid(const struct uvc_entity *entity, - const __u8 guid[16]) -{ - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_ITT_CAMERA: - return memcmp(uvc_camera_guid, guid, 16) == 0; - - case UVC_ITT_MEDIA_TRANSPORT_INPUT: - return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; - - case UVC_VC_PROCESSING_UNIT: - return memcmp(uvc_processing_guid, guid, 16) == 0; - - case UVC_VC_EXTENSION_UNIT: - return memcmp(entity->extension.guidExtensionCode, - guid, 16) == 0; - - default: - return 0; - } -} - -/* ------------------------------------------------------------------------ - * UVC Controls - */ - -static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, - struct uvc_control_mapping **mapping, struct uvc_control **control, - int next) -{ - struct uvc_control *ctrl; - struct uvc_control_mapping *map; - unsigned int i; - - if (entity == NULL) - return; - - for (i = 0; i < entity->ncontrols; ++i) { - ctrl = &entity->controls[i]; - if (!ctrl->initialized) - continue; - - list_for_each_entry(map, &ctrl->info.mappings, list) { - if ((map->id == v4l2_id) && !next) { - *control = ctrl; - *mapping = map; - return; - } - - if ((*mapping == NULL || (*mapping)->id > map->id) && - (map->id > v4l2_id) && next) { - *control = ctrl; - *mapping = map; - } - } - } -} - -static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, - __u32 v4l2_id, struct uvc_control_mapping **mapping) -{ - struct uvc_control *ctrl = NULL; - struct uvc_entity *entity; - int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL; - - *mapping = NULL; - - /* Mask the query flags. */ - v4l2_id &= V4L2_CTRL_ID_MASK; - - /* Find the control. */ - list_for_each_entry(entity, &chain->entities, chain) { - __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - } - - if (ctrl == NULL && !next) - uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", - v4l2_id); - - return ctrl; -} - -static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, - struct uvc_control *ctrl) -{ - int ret; - - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF), - ctrl->info.size); - if (ret < 0) - return ret; - } - - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN), - ctrl->info.size); - if (ret < 0) - return ret; - } - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX), - ctrl->info.size); - if (ret < 0) - return ret; - } - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), - ctrl->info.size); - if (ret < 0) { - if (UVC_ENTITY_TYPE(ctrl->entity) != - UVC_VC_EXTENSION_UNIT) - return ret; - - /* GET_RES is mandatory for XU controls, but some - * cameras still choke on it. Ignore errors and set the - * resolution value to zero. - */ - uvc_warn_once(chain->dev, UVC_WARN_XU_GET_RES, - "UVC non compliance - GET_RES failed on " - "an XU control. Enabling workaround.\n"); - memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0, - ctrl->info.size); - } - } - - ctrl->cached = 1; - return 0; -} - -static int __uvc_ctrl_get(struct uvc_video_chain *chain, - struct uvc_control *ctrl, struct uvc_control_mapping *mapping, - s32 *value) -{ - struct uvc_menu_info *menu; - unsigned int i; - int ret; - - if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) - return -EINVAL; - - if (!ctrl->loaded) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - if (ret < 0) - return ret; - - ctrl->loaded = 1; - } - - *value = mapping->get(mapping, UVC_GET_CUR, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == *value) { - *value = i; - break; - } - } - } - - return 0; -} - -static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, - struct uvc_control *ctrl, - struct uvc_control_mapping *mapping, - struct v4l2_queryctrl *v4l2_ctrl) -{ - struct uvc_control_mapping *master_map = NULL; - struct uvc_control *master_ctrl = NULL; - struct uvc_menu_info *menu; - unsigned int i; - - memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); - v4l2_ctrl->id = mapping->id; - v4l2_ctrl->type = mapping->v4l2_type; - strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); - v4l2_ctrl->flags = 0; - - if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) - v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; - if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) - v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - - if (mapping->master_id) - __uvc_find_control(ctrl->entity, mapping->master_id, - &master_map, &master_ctrl, 0); - if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) { - s32 val; - int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); - if (ret < 0) - return ret; - - if (val != mapping->master_manual) - v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - } - - if (!ctrl->cached) { - int ret = uvc_ctrl_populate_cache(chain, ctrl); - if (ret < 0) - return ret; - } - - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { - v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF)); - } - - switch (mapping->v4l2_type) { - case V4L2_CTRL_TYPE_MENU: - v4l2_ctrl->minimum = 0; - v4l2_ctrl->maximum = mapping->menu_count - 1; - v4l2_ctrl->step = 1; - - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == v4l2_ctrl->default_value) { - v4l2_ctrl->default_value = i; - break; - } - } - - return 0; - - case V4L2_CTRL_TYPE_BOOLEAN: - v4l2_ctrl->minimum = 0; - v4l2_ctrl->maximum = 1; - v4l2_ctrl->step = 1; - return 0; - - case V4L2_CTRL_TYPE_BUTTON: - v4l2_ctrl->minimum = 0; - v4l2_ctrl->maximum = 0; - v4l2_ctrl->step = 0; - return 0; - - default: - break; - } - - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) - v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); - - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) - v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); - - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) - v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); - - return 0; -} - -int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, - struct v4l2_queryctrl *v4l2_ctrl) -{ - struct uvc_control *ctrl; - struct uvc_control_mapping *mapping; - int ret; - - ret = mutex_lock_interruptible(&chain->ctrl_mutex); - if (ret < 0) - return -ERESTARTSYS; - - ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); - if (ctrl == NULL) { - ret = -EINVAL; - goto done; - } - - ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl); -done: - mutex_unlock(&chain->ctrl_mutex); - return ret; -} - -/* - * Mapping V4L2 controls to UVC controls can be straighforward if done well. - * Most of the UVC controls exist in V4L2, and can be mapped directly. Some - * must be grouped (for instance the Red Balance, Blue Balance and Do White - * Balance V4L2 controls use the White Balance Component UVC control) or - * otherwise translated. The approach we take here is to use a translation - * table for the controls that can be mapped directly, and handle the others - * manually. - */ -int uvc_query_v4l2_menu(struct uvc_video_chain *chain, - struct v4l2_querymenu *query_menu) -{ - struct uvc_menu_info *menu_info; - struct uvc_control_mapping *mapping; - struct uvc_control *ctrl; - u32 index = query_menu->index; - u32 id = query_menu->id; - int ret; - - memset(query_menu, 0, sizeof(*query_menu)); - query_menu->id = id; - query_menu->index = index; - - ret = mutex_lock_interruptible(&chain->ctrl_mutex); - if (ret < 0) - return -ERESTARTSYS; - - ctrl = uvc_find_control(chain, query_menu->id, &mapping); - if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) { - ret = -EINVAL; - goto done; - } - - if (query_menu->index >= mapping->menu_count) { - ret = -EINVAL; - goto done; - } - - menu_info = &mapping->menu_info[query_menu->index]; - - if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && - (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { - s32 bitmap; - - if (!ctrl->cached) { - ret = uvc_ctrl_populate_cache(chain, ctrl); - if (ret < 0) - goto done; - } - - bitmap = mapping->get(mapping, UVC_GET_RES, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); - if (!(bitmap & menu_info->value)) { - ret = -EINVAL; - goto done; - } - } - - strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name); - -done: - mutex_unlock(&chain->ctrl_mutex); - return ret; -} - -/* -------------------------------------------------------------------------- - * Ctrl event handling - */ - -static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, - struct v4l2_event *ev, - struct uvc_control *ctrl, - struct uvc_control_mapping *mapping, - s32 value, u32 changes) -{ - struct v4l2_queryctrl v4l2_ctrl; - - __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); - - memset(ev->reserved, 0, sizeof(ev->reserved)); - ev->type = V4L2_EVENT_CTRL; - ev->id = v4l2_ctrl.id; - ev->u.ctrl.value = value; - ev->u.ctrl.changes = changes; - ev->u.ctrl.type = v4l2_ctrl.type; - ev->u.ctrl.flags = v4l2_ctrl.flags; - ev->u.ctrl.minimum = v4l2_ctrl.minimum; - ev->u.ctrl.maximum = v4l2_ctrl.maximum; - ev->u.ctrl.step = v4l2_ctrl.step; - ev->u.ctrl.default_value = v4l2_ctrl.default_value; -} - -static void uvc_ctrl_send_event(struct uvc_fh *handle, - struct uvc_control *ctrl, struct uvc_control_mapping *mapping, - s32 value, u32 changes) -{ - struct v4l2_subscribed_event *sev; - struct v4l2_event ev; - - if (list_empty(&mapping->ev_subs)) - return; - - uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); - - list_for_each_entry(sev, &mapping->ev_subs, node) { - if (sev->fh && (sev->fh != &handle->vfh || - (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || - (changes & V4L2_EVENT_CTRL_CH_FLAGS))) - v4l2_event_queue_fh(sev->fh, &ev); - } -} - -static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, - struct uvc_control *master, u32 slave_id, - const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) -{ - struct uvc_control_mapping *mapping = NULL; - struct uvc_control *ctrl = NULL; - u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; - unsigned int i; - s32 val = 0; - - /* - * We can skip sending an event for the slave if the slave - * is being modified in the same transaction. - */ - for (i = 0; i < xctrls_count; i++) { - if (xctrls[i].id == slave_id) - return; - } - - __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); - if (ctrl == NULL) - return; - - if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) - changes |= V4L2_EVENT_CTRL_CH_VALUE; - - uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); -} - -static void uvc_ctrl_send_events(struct uvc_fh *handle, - const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) -{ - struct uvc_control_mapping *mapping; - struct uvc_control *ctrl; - u32 changes = V4L2_EVENT_CTRL_CH_VALUE; - unsigned int i; - unsigned int j; - - for (i = 0; i < xctrls_count; ++i) { - ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); - - for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { - if (!mapping->slave_ids[j]) - break; - uvc_ctrl_send_slave_event(handle, ctrl, - mapping->slave_ids[j], - xctrls, xctrls_count); - } - - /* - * If the master is being modified in the same transaction - * flags may change too. - */ - if (mapping->master_id) { - for (j = 0; j < xctrls_count; j++) { - if (xctrls[j].id == mapping->master_id) { - changes |= V4L2_EVENT_CTRL_CH_FLAGS; - break; - } - } - } - - uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, - changes); - } -} - -static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) -{ - struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); - struct uvc_control_mapping *mapping; - struct uvc_control *ctrl; - int ret; - - ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex); - if (ret < 0) - return -ERESTARTSYS; - - ctrl = uvc_find_control(handle->chain, sev->id, &mapping); - if (ctrl == NULL) { - ret = -EINVAL; - goto done; - } - - list_add_tail(&sev->node, &mapping->ev_subs); - if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) { - struct v4l2_event ev; - u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; - s32 val = 0; - - if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) - changes |= V4L2_EVENT_CTRL_CH_VALUE; - - uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, - changes); - /* Mark the queue as active, allowing this initial - event to be accepted. */ - sev->elems = elems; - v4l2_event_queue_fh(sev->fh, &ev); - } - -done: - mutex_unlock(&handle->chain->ctrl_mutex); - return ret; -} - -static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev) -{ - struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); - - mutex_lock(&handle->chain->ctrl_mutex); - list_del(&sev->node); - mutex_unlock(&handle->chain->ctrl_mutex); -} - -const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = { - .add = uvc_ctrl_add_event, - .del = uvc_ctrl_del_event, - .replace = v4l2_ctrl_replace, - .merge = v4l2_ctrl_merge, -}; - -/* -------------------------------------------------------------------------- - * Control transactions - * - * To make extended set operations as atomic as the hardware allows, controls - * are handled using begin/commit/rollback operations. - * - * At the beginning of a set request, uvc_ctrl_begin should be called to - * initialize the request. This function acquires the control lock. - * - * When setting a control, the new value is stored in the control data field - * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for - * later processing. If the UVC and V4L2 control sizes differ, the current - * value is loaded from the hardware before storing the new value in the data - * field. - * - * After processing all controls in the transaction, uvc_ctrl_commit or - * uvc_ctrl_rollback must be called to apply the pending changes to the - * hardware or revert them. When applying changes, all controls marked as - * dirty will be modified in the UVC device, and the dirty flag will be - * cleared. When reverting controls, the control data field - * UVC_CTRL_DATA_CURRENT is reverted to its previous value - * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the - * control lock. - */ -int uvc_ctrl_begin(struct uvc_video_chain *chain) -{ - return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0; -} - -static int uvc_ctrl_commit_entity(struct uvc_device *dev, - struct uvc_entity *entity, int rollback) -{ - struct uvc_control *ctrl; - unsigned int i; - int ret; - - if (entity == NULL) - return 0; - - for (i = 0; i < entity->ncontrols; ++i) { - ctrl = &entity->controls[i]; - if (!ctrl->initialized) - continue; - - /* Reset the loaded flag for auto-update controls that were - * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent - * uvc_ctrl_get from using the cached value, and for write-only - * controls to prevent uvc_ctrl_set from setting bits not - * explicitly set by the user. - */ - if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE || - !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) - ctrl->loaded = 0; - - if (!ctrl->dirty) - continue; - - if (!rollback) - ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id, - dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - else - ret = 0; - - if (rollback || ret < 0) - memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), - ctrl->info.size); - - ctrl->dirty = 0; - - if (ret < 0) - return ret; - } - - return 0; -} - -int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, - const struct v4l2_ext_control *xctrls, - unsigned int xctrls_count) -{ - struct uvc_video_chain *chain = handle->chain; - struct uvc_entity *entity; - int ret = 0; - - /* Find the control. */ - list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); - if (ret < 0) - goto done; - } - - if (!rollback) - uvc_ctrl_send_events(handle, xctrls, xctrls_count); -done: - mutex_unlock(&chain->ctrl_mutex); - return ret; -} - -int uvc_ctrl_get(struct uvc_video_chain *chain, - struct v4l2_ext_control *xctrl) -{ - struct uvc_control *ctrl; - struct uvc_control_mapping *mapping; - - ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL) - return -EINVAL; - - return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); -} - -int uvc_ctrl_set(struct uvc_video_chain *chain, - struct v4l2_ext_control *xctrl) -{ - struct uvc_control *ctrl; - struct uvc_control_mapping *mapping; - s32 value; - u32 step; - s32 min; - s32 max; - int ret; - - ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0) - return -EINVAL; - - /* Clamp out of range values. */ - switch (mapping->v4l2_type) { - case V4L2_CTRL_TYPE_INTEGER: - if (!ctrl->cached) { - ret = uvc_ctrl_populate_cache(chain, ctrl); - if (ret < 0) - return ret; - } - - min = mapping->get(mapping, UVC_GET_MIN, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); - max = mapping->get(mapping, UVC_GET_MAX, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); - step = mapping->get(mapping, UVC_GET_RES, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); - if (step == 0) - step = 1; - - xctrl->value = min + (xctrl->value - min + step/2) / step * step; - xctrl->value = clamp(xctrl->value, min, max); - value = xctrl->value; - break; - - case V4L2_CTRL_TYPE_BOOLEAN: - xctrl->value = clamp(xctrl->value, 0, 1); - value = xctrl->value; - break; - - case V4L2_CTRL_TYPE_MENU: - if (xctrl->value < 0 || xctrl->value >= mapping->menu_count) - return -ERANGE; - value = mapping->menu_info[xctrl->value].value; - - /* Valid menu indices are reported by the GET_RES request for - * UVC controls that support it. - */ - if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && - (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { - if (!ctrl->cached) { - ret = uvc_ctrl_populate_cache(chain, ctrl); - if (ret < 0) - return ret; - } - - step = mapping->get(mapping, UVC_GET_RES, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); - if (!(step & value)) - return -ERANGE; - } - - break; - - default: - value = xctrl->value; - break; - } - - /* If the mapping doesn't span the whole UVC control, the current value - * needs to be loaded from the device to perform the read-modify-write - * operation. - */ - if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) { - if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) { - memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - 0, ctrl->info.size); - } else { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, - ctrl->entity->id, chain->dev->intfnum, - ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - if (ret < 0) - return ret; - } - - ctrl->loaded = 1; - } - - /* Backup the current value in case we need to rollback later. */ - if (!ctrl->dirty) { - memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - } - - mapping->set(mapping, value, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - ctrl->dirty = 1; - ctrl->modified = 1; - return 0; -} - -/* -------------------------------------------------------------------------- - * Dynamic controls - */ - -static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev, - const struct uvc_control *ctrl, struct uvc_control_info *info) -{ - struct uvc_ctrl_fixup { - struct usb_device_id id; - u8 entity; - u8 selector; - u8 flags; - }; - - static const struct uvc_ctrl_fixup fixups[] = { - { { USB_DEVICE(0x046d, 0x08c2) }, 9, 1, - UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | - UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | - UVC_CTRL_FLAG_AUTO_UPDATE }, - { { USB_DEVICE(0x046d, 0x08cc) }, 9, 1, - UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | - UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | - UVC_CTRL_FLAG_AUTO_UPDATE }, - { { USB_DEVICE(0x046d, 0x0994) }, 9, 1, - UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX | - UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR | - UVC_CTRL_FLAG_AUTO_UPDATE }, - }; - - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(fixups); ++i) { - if (!usb_match_one_id(dev->intf, &fixups[i].id)) - continue; - - if (fixups[i].entity == ctrl->entity->id && - fixups[i].selector == info->selector) { - info->flags = fixups[i].flags; - return; - } - } -} - -/* - * Query control information (size and flags) for XU controls. - */ -static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, - const struct uvc_control *ctrl, struct uvc_control_info *info) -{ - u8 *data; - int ret; - - data = kmalloc(2, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - memcpy(info->entity, ctrl->entity->extension.guidExtensionCode, - sizeof(info->entity)); - info->index = ctrl->index; - info->selector = ctrl->index + 1; - - /* Query and verify the control length (GET_LEN) */ - ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, - info->selector, data, 2); - if (ret < 0) { - uvc_trace(UVC_TRACE_CONTROL, - "GET_LEN failed on control %pUl/%u (%d).\n", - info->entity, info->selector, ret); - goto done; - } - - info->size = le16_to_cpup((__le16 *)data); - - /* Query the control information (GET_INFO) */ - ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, - info->selector, data, 1); - if (ret < 0) { - uvc_trace(UVC_TRACE_CONTROL, - "GET_INFO failed on control %pUl/%u (%d).\n", - info->entity, info->selector, ret); - goto done; - } - - info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX - | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF - | (data[0] & UVC_CONTROL_CAP_GET ? - UVC_CTRL_FLAG_GET_CUR : 0) - | (data[0] & UVC_CONTROL_CAP_SET ? - UVC_CTRL_FLAG_SET_CUR : 0) - | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? - UVC_CTRL_FLAG_AUTO_UPDATE : 0); - - uvc_ctrl_fixup_xu_info(dev, ctrl, info); - - uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " - "flags { get %u set %u auto %u }.\n", - info->entity, info->selector, info->size, - (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0, - (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0, - (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0); - -done: - kfree(data); - return ret; -} - -static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, - const struct uvc_control_info *info); - -static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, - struct uvc_control *ctrl) -{ - struct uvc_control_info info; - int ret; - - if (ctrl->initialized) - return 0; - - ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info); - if (ret < 0) - return ret; - - ret = uvc_ctrl_add_info(dev, ctrl, &info); - if (ret < 0) - uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control " - "%pUl/%u on device %s entity %u\n", info.entity, - info.selector, dev->udev->devpath, ctrl->entity->id); - - return ret; -} - -int uvc_xu_ctrl_query(struct uvc_video_chain *chain, - struct uvc_xu_control_query *xqry) -{ - struct uvc_entity *entity; - struct uvc_control *ctrl; - unsigned int i, found = 0; - __u32 reqflags; - __u16 size; - __u8 *data = NULL; - int ret; - - /* Find the extension unit. */ - list_for_each_entry(entity, &chain->entities, chain) { - if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && - entity->id == xqry->unit) - break; - } - - if (entity->id != xqry->unit) { - uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", - xqry->unit); - return -ENOENT; - } - - /* Find the control and perform delayed initialization if needed. */ - for (i = 0; i < entity->ncontrols; ++i) { - ctrl = &entity->controls[i]; - if (ctrl->index == xqry->selector - 1) { - found = 1; - break; - } - } - - if (!found) { - uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", - entity->extension.guidExtensionCode, xqry->selector); - return -ENOENT; - } - - if (mutex_lock_interruptible(&chain->ctrl_mutex)) - return -ERESTARTSYS; - - ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl); - if (ret < 0) { - ret = -ENOENT; - goto done; - } - - /* Validate the required buffer size and flags for the request */ - reqflags = 0; - size = ctrl->info.size; - - switch (xqry->query) { - case UVC_GET_CUR: - reqflags = UVC_CTRL_FLAG_GET_CUR; - break; - case UVC_GET_MIN: - reqflags = UVC_CTRL_FLAG_GET_MIN; - break; - case UVC_GET_MAX: - reqflags = UVC_CTRL_FLAG_GET_MAX; - break; - case UVC_GET_DEF: - reqflags = UVC_CTRL_FLAG_GET_DEF; - break; - case UVC_GET_RES: - reqflags = UVC_CTRL_FLAG_GET_RES; - break; - case UVC_SET_CUR: - reqflags = UVC_CTRL_FLAG_SET_CUR; - break; - case UVC_GET_LEN: - size = 2; - break; - case UVC_GET_INFO: - size = 1; - break; - default: - ret = -EINVAL; - goto done; - } - - if (size != xqry->size) { - ret = -ENOBUFS; - goto done; - } - - if (reqflags && !(ctrl->info.flags & reqflags)) { - ret = -EBADRQC; - goto done; - } - - data = kmalloc(size, GFP_KERNEL); - if (data == NULL) { - ret = -ENOMEM; - goto done; - } - - if (xqry->query == UVC_SET_CUR && - copy_from_user(data, xqry->data, size)) { - ret = -EFAULT; - goto done; - } - - ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit, - chain->dev->intfnum, xqry->selector, data, size); - if (ret < 0) - goto done; - - if (xqry->query != UVC_SET_CUR && - copy_to_user(xqry->data, data, size)) - ret = -EFAULT; -done: - kfree(data); - mutex_unlock(&chain->ctrl_mutex); - return ret; -} - -/* -------------------------------------------------------------------------- - * Suspend/resume - */ - -/* - * Restore control values after resume, skipping controls that haven't been - * changed. - * - * TODO - * - Don't restore modified controls that are back to their default value. - * - Handle restore order (Auto-Exposure Mode should be restored before - * Exposure Time). - */ -int uvc_ctrl_resume_device(struct uvc_device *dev) -{ - struct uvc_control *ctrl; - struct uvc_entity *entity; - unsigned int i; - int ret; - - /* Walk the entities list and restore controls when possible. */ - list_for_each_entry(entity, &dev->entities, list) { - - for (i = 0; i < entity->ncontrols; ++i) { - ctrl = &entity->controls[i]; - - if (!ctrl->initialized || !ctrl->modified || - (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0) - continue; - - printk(KERN_INFO "restoring control %pUl/%u/%u\n", - ctrl->info.entity, ctrl->info.index, - ctrl->info.selector); - ctrl->dirty = 1; - } - - ret = uvc_ctrl_commit_entity(dev, entity, 0); - if (ret < 0) - return ret; - } - - return 0; -} - -/* -------------------------------------------------------------------------- - * Control and mapping handling - */ - -/* - * Add control information to a given control. - */ -static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, - const struct uvc_control_info *info) -{ - int ret = 0; - - memcpy(&ctrl->info, info, sizeof(*info)); - INIT_LIST_HEAD(&ctrl->info.mappings); - - /* Allocate an array to save control values (cur, def, max, etc.) */ - ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + 1, - GFP_KERNEL); - if (ctrl->uvc_data == NULL) { - ret = -ENOMEM; - goto done; - } - - ctrl->initialized = 1; - - uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " - "entity %u\n", ctrl->info.entity, ctrl->info.selector, - dev->udev->devpath, ctrl->entity->id); - -done: - if (ret < 0) - kfree(ctrl->uvc_data); - return ret; -} - -/* - * Add a control mapping to a given control. - */ -static int __uvc_ctrl_add_mapping(struct uvc_device *dev, - struct uvc_control *ctrl, const struct uvc_control_mapping *mapping) -{ - struct uvc_control_mapping *map; - unsigned int size; - - /* Most mappings come from static kernel data and need to be duplicated. - * Mappings that come from userspace will be unnecessarily duplicated, - * this could be optimized. - */ - map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL); - if (map == NULL) - return -ENOMEM; - - INIT_LIST_HEAD(&map->ev_subs); - - size = sizeof(*mapping->menu_info) * mapping->menu_count; - map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); - if (map->menu_info == NULL) { - kfree(map); - return -ENOMEM; - } - - if (map->get == NULL) - map->get = uvc_get_le_value; - if (map->set == NULL) - map->set = uvc_set_le_value; - - list_add_tail(&map->list, &ctrl->info.mappings); - uvc_trace(UVC_TRACE_CONTROL, - "Adding mapping '%s' to control %pUl/%u.\n", - map->name, ctrl->info.entity, ctrl->info.selector); - - return 0; -} - -int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, - const struct uvc_control_mapping *mapping) -{ - struct uvc_device *dev = chain->dev; - struct uvc_control_mapping *map; - struct uvc_entity *entity; - struct uvc_control *ctrl; - int found = 0; - int ret; - - if (mapping->id & ~V4L2_CTRL_ID_MASK) { - uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control " - "id 0x%08x is invalid.\n", mapping->name, - mapping->id); - return -EINVAL; - } - - /* Search for the matching (GUID/CS) control on the current chain */ - list_for_each_entry(entity, &chain->entities, chain) { - unsigned int i; - - if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT || - !uvc_entity_match_guid(entity, mapping->entity)) - continue; - - for (i = 0; i < entity->ncontrols; ++i) { - ctrl = &entity->controls[i]; - if (ctrl->index == mapping->selector - 1) { - found = 1; - break; - } - } - - if (found) - break; - } - if (!found) - return -ENOENT; - - if (mutex_lock_interruptible(&chain->ctrl_mutex)) - return -ERESTARTSYS; - - /* Perform delayed initialization of XU controls */ - ret = uvc_ctrl_init_xu_ctrl(dev, ctrl); - if (ret < 0) { - ret = -ENOENT; - goto done; - } - - list_for_each_entry(map, &ctrl->info.mappings, list) { - if (mapping->id == map->id) { - uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', " - "control id 0x%08x already exists.\n", - mapping->name, mapping->id); - ret = -EEXIST; - goto done; - } - } - - /* Prevent excess memory consumption */ - if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) { - atomic_dec(&dev->nmappings); - uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', maximum " - "mappings count (%u) exceeded.\n", mapping->name, - UVC_MAX_CONTROL_MAPPINGS); - ret = -ENOMEM; - goto done; - } - - ret = __uvc_ctrl_add_mapping(dev, ctrl, mapping); - if (ret < 0) - atomic_dec(&dev->nmappings); - -done: - mutex_unlock(&chain->ctrl_mutex); - return ret; -} - -/* - * Prune an entity of its bogus controls using a blacklist. Bogus controls - * are currently the ones that crash the camera or unconditionally return an - * error when queried. - */ -static void uvc_ctrl_prune_entity(struct uvc_device *dev, - struct uvc_entity *entity) -{ - struct uvc_ctrl_blacklist { - struct usb_device_id id; - u8 index; - }; - - static const struct uvc_ctrl_blacklist processing_blacklist[] = { - { { USB_DEVICE(0x13d3, 0x509b) }, 9 }, /* Gain */ - { { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */ - { { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */ - }; - static const struct uvc_ctrl_blacklist camera_blacklist[] = { - { { USB_DEVICE(0x06f8, 0x3005) }, 9 }, /* Zoom, Absolute */ - }; - - const struct uvc_ctrl_blacklist *blacklist; - unsigned int size; - unsigned int count; - unsigned int i; - u8 *controls; - - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_VC_PROCESSING_UNIT: - blacklist = processing_blacklist; - count = ARRAY_SIZE(processing_blacklist); - controls = entity->processing.bmControls; - size = entity->processing.bControlSize; - break; - - case UVC_ITT_CAMERA: - blacklist = camera_blacklist; - count = ARRAY_SIZE(camera_blacklist); - controls = entity->camera.bmControls; - size = entity->camera.bControlSize; - break; - - default: - return; - } - - for (i = 0; i < count; ++i) { - if (!usb_match_one_id(dev->intf, &blacklist[i].id)) - continue; - - if (blacklist[i].index >= 8 * size || - !uvc_test_bit(controls, blacklist[i].index)) - continue; - - uvc_trace(UVC_TRACE_CONTROL, "%u/%u control is black listed, " - "removing it.\n", entity->id, blacklist[i].index); - - uvc_clear_bit(controls, blacklist[i].index); - } -} - -/* - * Add control information and hardcoded stock control mappings to the given - * device. - */ -static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl) -{ - const struct uvc_control_info *info = uvc_ctrls; - const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls); - const struct uvc_control_mapping *mapping = uvc_ctrl_mappings; - const struct uvc_control_mapping *mend = - mapping + ARRAY_SIZE(uvc_ctrl_mappings); - - /* XU controls initialization requires querying the device for control - * information. As some buggy UVC devices will crash when queried - * repeatedly in a tight loop, delay XU controls initialization until - * first use. - */ - if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) - return; - - for (; info < iend; ++info) { - if (uvc_entity_match_guid(ctrl->entity, info->entity) && - ctrl->index == info->index) { - uvc_ctrl_add_info(dev, ctrl, info); - break; - } - } - - if (!ctrl->initialized) - return; - - for (; mapping < mend; ++mapping) { - if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && - ctrl->info.selector == mapping->selector) - __uvc_ctrl_add_mapping(dev, ctrl, mapping); - } -} - -/* - * Initialize device controls. - */ -int uvc_ctrl_init_device(struct uvc_device *dev) -{ - struct uvc_entity *entity; - unsigned int i; - - /* Walk the entities list and instantiate controls */ - list_for_each_entry(entity, &dev->entities, list) { - struct uvc_control *ctrl; - unsigned int bControlSize = 0, ncontrols; - __u8 *bmControls = NULL; - - if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { - bmControls = entity->extension.bmControls; - bControlSize = entity->extension.bControlSize; - } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) { - bmControls = entity->processing.bmControls; - bControlSize = entity->processing.bControlSize; - } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) { - bmControls = entity->camera.bmControls; - bControlSize = entity->camera.bControlSize; - } - - /* Remove bogus/blacklisted controls */ - uvc_ctrl_prune_entity(dev, entity); - - /* Count supported controls and allocate the controls array */ - ncontrols = memweight(bmControls, bControlSize); - if (ncontrols == 0) - continue; - - entity->controls = kcalloc(ncontrols, sizeof(*ctrl), - GFP_KERNEL); - if (entity->controls == NULL) - return -ENOMEM; - entity->ncontrols = ncontrols; - - /* Initialize all supported controls */ - ctrl = entity->controls; - for (i = 0; i < bControlSize * 8; ++i) { - if (uvc_test_bit(bmControls, i) == 0) - continue; - - ctrl->entity = entity; - ctrl->index = i; - - uvc_ctrl_init_ctrl(dev, ctrl); - ctrl++; - } - } - - return 0; -} - -/* - * Cleanup device controls. - */ -static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev, - struct uvc_control *ctrl) -{ - struct uvc_control_mapping *mapping, *nm; - - list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) { - list_del(&mapping->list); - kfree(mapping->menu_info); - kfree(mapping); - } -} - -void uvc_ctrl_cleanup_device(struct uvc_device *dev) -{ - struct uvc_entity *entity; - unsigned int i; - - /* Free controls and control mappings for all entities. */ - list_for_each_entry(entity, &dev->entities, list) { - for (i = 0; i < entity->ncontrols; ++i) { - struct uvc_control *ctrl = &entity->controls[i]; - - if (!ctrl->initialized) - continue; - - uvc_ctrl_cleanup_mappings(dev, ctrl); - kfree(ctrl->uvc_data); - } - - kfree(entity->controls); - } -} diff --git a/drivers/media/video/uvc/uvc_debugfs.c b/drivers/media/video/uvc/uvc_debugfs.c deleted file mode 100644 index 14561a5abb79..000000000000 --- a/drivers/media/video/uvc/uvc_debugfs.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * uvc_debugfs.c -- USB Video Class driver - Debugging support - * - * Copyright (C) 2011 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include - -#include "uvcvideo.h" - -/* ----------------------------------------------------------------------------- - * Statistics - */ - -#define UVC_DEBUGFS_BUF_SIZE 1024 - -struct uvc_debugfs_buffer { - size_t count; - char data[UVC_DEBUGFS_BUF_SIZE]; -}; - -static int uvc_debugfs_stats_open(struct inode *inode, struct file *file) -{ - struct uvc_streaming *stream = inode->i_private; - struct uvc_debugfs_buffer *buf; - - buf = kmalloc(sizeof(*buf), GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data)); - - file->private_data = buf; - return 0; -} - -static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf, - size_t nbytes, loff_t *ppos) -{ - struct uvc_debugfs_buffer *buf = file->private_data; - - return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data, - buf->count); -} - -static int uvc_debugfs_stats_release(struct inode *inode, struct file *file) -{ - kfree(file->private_data); - file->private_data = NULL; - - return 0; -} - -static const struct file_operations uvc_debugfs_stats_fops = { - .owner = THIS_MODULE, - .open = uvc_debugfs_stats_open, - .llseek = no_llseek, - .read = uvc_debugfs_stats_read, - .release = uvc_debugfs_stats_release, -}; - -/* ----------------------------------------------------------------------------- - * Global and stream initialization/cleanup - */ - -static struct dentry *uvc_debugfs_root_dir; - -int uvc_debugfs_init_stream(struct uvc_streaming *stream) -{ - struct usb_device *udev = stream->dev->udev; - struct dentry *dent; - char dir_name[32]; - - if (uvc_debugfs_root_dir == NULL) - return -ENODEV; - - sprintf(dir_name, "%u-%u", udev->bus->busnum, udev->devnum); - - dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir); - if (IS_ERR_OR_NULL(dent)) { - uvc_printk(KERN_INFO, "Unable to create debugfs %s " - "directory.\n", dir_name); - return -ENODEV; - } - - stream->debugfs_dir = dent; - - dent = debugfs_create_file("stats", 0444, stream->debugfs_dir, - stream, &uvc_debugfs_stats_fops); - if (IS_ERR_OR_NULL(dent)) { - uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n"); - uvc_debugfs_cleanup_stream(stream); - return -ENODEV; - } - - return 0; -} - -void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream) -{ - if (stream->debugfs_dir == NULL) - return; - - debugfs_remove_recursive(stream->debugfs_dir); - stream->debugfs_dir = NULL; -} - -int uvc_debugfs_init(void) -{ - struct dentry *dir; - - dir = debugfs_create_dir("uvcvideo", usb_debug_root); - if (IS_ERR_OR_NULL(dir)) { - uvc_printk(KERN_INFO, "Unable to create debugfs directory\n"); - return -ENODATA; - } - - uvc_debugfs_root_dir = dir; - return 0; -} - -void uvc_debugfs_cleanup(void) -{ - if (uvc_debugfs_root_dir != NULL) - debugfs_remove_recursive(uvc_debugfs_root_dir); -} diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c deleted file mode 100644 index 45d7aa162d9d..000000000000 --- a/drivers/media/video/uvc/uvc_driver.c +++ /dev/null @@ -1,2472 +0,0 @@ -/* - * uvc_driver.c -- USB Video Class driver - * - * Copyright (C) 2005-2010 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -/* - * This driver aims to support video input and ouput devices compliant with the - * 'USB Video Class' specification. - * - * The driver doesn't support the deprecated v4l1 interface. It implements the - * mmap capture method only, and doesn't do any image format conversion in - * software. If your user-space application doesn't support YUYV or MJPEG, fix - * it :-). Please note that the MJPEG data have been stripped from their - * Huffman tables (DHT marker), you will need to add it back if your JPEG - * codec can't handle MJPEG data. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "uvcvideo.h" - -#define DRIVER_AUTHOR "Laurent Pinchart " \ - "" -#define DRIVER_DESC "USB Video Class driver" - -unsigned int uvc_clock_param = CLOCK_MONOTONIC; -unsigned int uvc_no_drop_param; -static unsigned int uvc_quirks_param = -1; -unsigned int uvc_trace_param; -unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; - -/* ------------------------------------------------------------------------ - * Video formats - */ - -static struct uvc_format_desc uvc_fmts[] = { - { - .name = "YUV 4:2:2 (YUYV)", - .guid = UVC_GUID_FORMAT_YUY2, - .fcc = V4L2_PIX_FMT_YUYV, - }, - { - .name = "YUV 4:2:2 (YUYV)", - .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, - .fcc = V4L2_PIX_FMT_YUYV, - }, - { - .name = "YUV 4:2:0 (NV12)", - .guid = UVC_GUID_FORMAT_NV12, - .fcc = V4L2_PIX_FMT_NV12, - }, - { - .name = "MJPEG", - .guid = UVC_GUID_FORMAT_MJPEG, - .fcc = V4L2_PIX_FMT_MJPEG, - }, - { - .name = "YVU 4:2:0 (YV12)", - .guid = UVC_GUID_FORMAT_YV12, - .fcc = V4L2_PIX_FMT_YVU420, - }, - { - .name = "YUV 4:2:0 (I420)", - .guid = UVC_GUID_FORMAT_I420, - .fcc = V4L2_PIX_FMT_YUV420, - }, - { - .name = "YUV 4:2:0 (M420)", - .guid = UVC_GUID_FORMAT_M420, - .fcc = V4L2_PIX_FMT_M420, - }, - { - .name = "YUV 4:2:2 (UYVY)", - .guid = UVC_GUID_FORMAT_UYVY, - .fcc = V4L2_PIX_FMT_UYVY, - }, - { - .name = "Greyscale 8-bit (Y800)", - .guid = UVC_GUID_FORMAT_Y800, - .fcc = V4L2_PIX_FMT_GREY, - }, - { - .name = "Greyscale 8-bit (Y8 )", - .guid = UVC_GUID_FORMAT_Y8, - .fcc = V4L2_PIX_FMT_GREY, - }, - { - .name = "Greyscale 10-bit (Y10 )", - .guid = UVC_GUID_FORMAT_Y10, - .fcc = V4L2_PIX_FMT_Y10, - }, - { - .name = "Greyscale 12-bit (Y12 )", - .guid = UVC_GUID_FORMAT_Y12, - .fcc = V4L2_PIX_FMT_Y12, - }, - { - .name = "Greyscale 16-bit (Y16 )", - .guid = UVC_GUID_FORMAT_Y16, - .fcc = V4L2_PIX_FMT_Y16, - }, - { - .name = "RGB Bayer", - .guid = UVC_GUID_FORMAT_BY8, - .fcc = V4L2_PIX_FMT_SBGGR8, - }, - { - .name = "RGB565", - .guid = UVC_GUID_FORMAT_RGBP, - .fcc = V4L2_PIX_FMT_RGB565, - }, - { - .name = "H.264", - .guid = UVC_GUID_FORMAT_H264, - .fcc = V4L2_PIX_FMT_H264, - }, -}; - -/* ------------------------------------------------------------------------ - * Utility functions - */ - -struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, - __u8 epaddr) -{ - struct usb_host_endpoint *ep; - unsigned int i; - - for (i = 0; i < alts->desc.bNumEndpoints; ++i) { - ep = &alts->endpoint[i]; - if (ep->desc.bEndpointAddress == epaddr) - return ep; - } - - return NULL; -} - -static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) -{ - unsigned int len = ARRAY_SIZE(uvc_fmts); - unsigned int i; - - for (i = 0; i < len; ++i) { - if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) - return &uvc_fmts[i]; - } - - return NULL; -} - -static __u32 uvc_colorspace(const __u8 primaries) -{ - static const __u8 colorprimaries[] = { - 0, - V4L2_COLORSPACE_SRGB, - V4L2_COLORSPACE_470_SYSTEM_M, - V4L2_COLORSPACE_470_SYSTEM_BG, - V4L2_COLORSPACE_SMPTE170M, - V4L2_COLORSPACE_SMPTE240M, - }; - - if (primaries < ARRAY_SIZE(colorprimaries)) - return colorprimaries[primaries]; - - return 0; -} - -/* Simplify a fraction using a simple continued fraction decomposition. The - * idea here is to convert fractions such as 333333/10000000 to 1/30 using - * 32 bit arithmetic only. The algorithm is not perfect and relies upon two - * arbitrary parameters to remove non-significative terms from the simple - * continued fraction decomposition. Using 8 and 333 for n_terms and threshold - * respectively seems to give nice results. - */ -void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, - unsigned int n_terms, unsigned int threshold) -{ - uint32_t *an; - uint32_t x, y, r; - unsigned int i, n; - - an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); - if (an == NULL) - return; - - /* Convert the fraction to a simple continued fraction. See - * http://mathforum.org/dr.math/faq/faq.fractions.html - * Stop if the current term is bigger than or equal to the given - * threshold. - */ - x = *numerator; - y = *denominator; - - for (n = 0; n < n_terms && y != 0; ++n) { - an[n] = x / y; - if (an[n] >= threshold) { - if (n < 2) - n++; - break; - } - - r = x - an[n] * y; - x = y; - y = r; - } - - /* Expand the simple continued fraction back to an integer fraction. */ - x = 0; - y = 1; - - for (i = n; i > 0; --i) { - r = y; - y = an[i-1] * y + x; - x = r; - } - - *numerator = y; - *denominator = x; - kfree(an); -} - -/* Convert a fraction to a frame interval in 100ns multiples. The idea here is - * to compute numerator / denominator * 10000000 using 32 bit fixed point - * arithmetic only. - */ -uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) -{ - uint32_t multiplier; - - /* Saturate the result if the operation would overflow. */ - if (denominator == 0 || - numerator/denominator >= ((uint32_t)-1)/10000000) - return (uint32_t)-1; - - /* Divide both the denominator and the multiplier by two until - * numerator * multiplier doesn't overflow. If anyone knows a better - * algorithm please let me know. - */ - multiplier = 10000000; - while (numerator > ((uint32_t)-1)/multiplier) { - multiplier /= 2; - denominator /= 2; - } - - return denominator ? numerator * multiplier / denominator : 0; -} - -/* ------------------------------------------------------------------------ - * Terminal and unit management - */ - -struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) -{ - struct uvc_entity *entity; - - list_for_each_entry(entity, &dev->entities, list) { - if (entity->id == id) - return entity; - } - - return NULL; -} - -static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, - int id, struct uvc_entity *entity) -{ - unsigned int i; - - if (entity == NULL) - entity = list_entry(&dev->entities, struct uvc_entity, list); - - list_for_each_entry_continue(entity, &dev->entities, list) { - for (i = 0; i < entity->bNrInPins; ++i) - if (entity->baSourceID[i] == id) - return entity; - } - - return NULL; -} - -static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) -{ - struct uvc_streaming *stream; - - list_for_each_entry(stream, &dev->streams, list) { - if (stream->header.bTerminalLink == id) - return stream; - } - - return NULL; -} - -/* ------------------------------------------------------------------------ - * Descriptors parsing - */ - -static int uvc_parse_format(struct uvc_device *dev, - struct uvc_streaming *streaming, struct uvc_format *format, - __u32 **intervals, unsigned char *buffer, int buflen) -{ - struct usb_interface *intf = streaming->intf; - struct usb_host_interface *alts = intf->cur_altsetting; - struct uvc_format_desc *fmtdesc; - struct uvc_frame *frame; - const unsigned char *start = buffer; - unsigned int interval; - unsigned int i, n; - __u8 ftype; - - format->type = buffer[2]; - format->index = buffer[3]; - - switch (buffer[2]) { - case UVC_VS_FORMAT_UNCOMPRESSED: - case UVC_VS_FORMAT_FRAME_BASED: - n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; - if (buflen < n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - /* Find the format descriptor from its GUID. */ - fmtdesc = uvc_format_by_guid(&buffer[5]); - - if (fmtdesc != NULL) { - strlcpy(format->name, fmtdesc->name, - sizeof format->name); - format->fcc = fmtdesc->fcc; - } else { - uvc_printk(KERN_INFO, "Unknown video format %pUl\n", - &buffer[5]); - snprintf(format->name, sizeof(format->name), "%pUl\n", - &buffer[5]); - format->fcc = 0; - } - - format->bpp = buffer[21]; - if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { - ftype = UVC_VS_FRAME_UNCOMPRESSED; - } else { - ftype = UVC_VS_FRAME_FRAME_BASED; - if (buffer[27]) - format->flags = UVC_FMT_FLAG_COMPRESSED; - } - break; - - case UVC_VS_FORMAT_MJPEG: - if (buflen < 11) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - strlcpy(format->name, "MJPEG", sizeof format->name); - format->fcc = V4L2_PIX_FMT_MJPEG; - format->flags = UVC_FMT_FLAG_COMPRESSED; - format->bpp = 0; - ftype = UVC_VS_FRAME_MJPEG; - break; - - case UVC_VS_FORMAT_DV: - if (buflen < 9) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - switch (buffer[8] & 0x7f) { - case 0: - strlcpy(format->name, "SD-DV", sizeof format->name); - break; - case 1: - strlcpy(format->name, "SDL-DV", sizeof format->name); - break; - case 2: - strlcpy(format->name, "HD-DV", sizeof format->name); - break; - default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d: unknown DV format %u\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber, buffer[8]); - return -EINVAL; - } - - strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", - sizeof format->name); - - format->fcc = V4L2_PIX_FMT_DV; - format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; - format->bpp = 0; - ftype = 0; - - /* Create a dummy frame descriptor. */ - frame = &format->frame[0]; - memset(&format->frame[0], 0, sizeof format->frame[0]); - frame->bFrameIntervalType = 1; - frame->dwDefaultFrameInterval = 1; - frame->dwFrameInterval = *intervals; - *(*intervals)++ = 1; - format->nframes = 1; - break; - - case UVC_VS_FORMAT_MPEG2TS: - case UVC_VS_FORMAT_STREAM_BASED: - /* Not supported yet. */ - default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d unsupported format %u\n", - dev->udev->devnum, alts->desc.bInterfaceNumber, - buffer[2]); - return -EINVAL; - } - - uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); - - buflen -= buffer[0]; - buffer += buffer[0]; - - /* Parse the frame descriptors. Only uncompressed, MJPEG and frame - * based formats have frame descriptors. - */ - while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && - buffer[2] == ftype) { - frame = &format->frame[format->nframes]; - if (ftype != UVC_VS_FRAME_FRAME_BASED) - n = buflen > 25 ? buffer[25] : 0; - else - n = buflen > 21 ? buffer[21] : 0; - - n = n ? n : 3; - - if (buflen < 26 + 4*n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FRAME error\n", dev->udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - frame->bFrameIndex = buffer[3]; - frame->bmCapabilities = buffer[4]; - frame->wWidth = get_unaligned_le16(&buffer[5]); - frame->wHeight = get_unaligned_le16(&buffer[7]); - frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); - frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); - if (ftype != UVC_VS_FRAME_FRAME_BASED) { - frame->dwMaxVideoFrameBufferSize = - get_unaligned_le32(&buffer[17]); - frame->dwDefaultFrameInterval = - get_unaligned_le32(&buffer[21]); - frame->bFrameIntervalType = buffer[25]; - } else { - frame->dwMaxVideoFrameBufferSize = 0; - frame->dwDefaultFrameInterval = - get_unaligned_le32(&buffer[17]); - frame->bFrameIntervalType = buffer[21]; - } - frame->dwFrameInterval = *intervals; - - /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize - * completely. Observed behaviours range from setting the - * value to 1.1x the actual frame size to hardwiring the - * 16 low bits to 0. This results in a higher than necessary - * memory usage as well as a wrong image size information. For - * uncompressed formats this can be fixed by computing the - * value from the frame size. - */ - if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) - frame->dwMaxVideoFrameBufferSize = format->bpp - * frame->wWidth * frame->wHeight / 8; - - /* Some bogus devices report dwMinFrameInterval equal to - * dwMaxFrameInterval and have dwFrameIntervalStep set to - * zero. Setting all null intervals to 1 fixes the problem and - * some other divisions by zero that could happen. - */ - for (i = 0; i < n; ++i) { - interval = get_unaligned_le32(&buffer[26+4*i]); - *(*intervals)++ = interval ? interval : 1; - } - - /* Make sure that the default frame interval stays between - * the boundaries. - */ - n -= frame->bFrameIntervalType ? 1 : 2; - frame->dwDefaultFrameInterval = - min(frame->dwFrameInterval[n], - max(frame->dwFrameInterval[0], - frame->dwDefaultFrameInterval)); - - if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { - frame->bFrameIntervalType = 1; - frame->dwFrameInterval[0] = - frame->dwDefaultFrameInterval; - } - - uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", - frame->wWidth, frame->wHeight, - 10000000/frame->dwDefaultFrameInterval, - (100000000/frame->dwDefaultFrameInterval)%10); - - format->nframes++; - buflen -= buffer[0]; - buffer += buffer[0]; - } - - if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && - buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { - buflen -= buffer[0]; - buffer += buffer[0]; - } - - if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && - buffer[2] == UVC_VS_COLORFORMAT) { - if (buflen < 6) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d COLORFORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - format->colorspace = uvc_colorspace(buffer[3]); - - buflen -= buffer[0]; - buffer += buffer[0]; - } - - return buffer - start; -} - -static int uvc_parse_streaming(struct uvc_device *dev, - struct usb_interface *intf) -{ - struct uvc_streaming *streaming = NULL; - struct uvc_format *format; - struct uvc_frame *frame; - struct usb_host_interface *alts = &intf->altsetting[0]; - unsigned char *_buffer, *buffer = alts->extra; - int _buflen, buflen = alts->extralen; - unsigned int nformats = 0, nframes = 0, nintervals = 0; - unsigned int size, i, n, p; - __u32 *interval; - __u16 psize; - int ret = -EINVAL; - - if (intf->cur_altsetting->desc.bInterfaceSubClass - != UVC_SC_VIDEOSTREAMING) { - uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " - "video streaming interface\n", dev->udev->devnum, - intf->altsetting[0].desc.bInterfaceNumber); - return -EINVAL; - } - - if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { - uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " - "claimed\n", dev->udev->devnum, - intf->altsetting[0].desc.bInterfaceNumber); - return -EINVAL; - } - - streaming = kzalloc(sizeof *streaming, GFP_KERNEL); - if (streaming == NULL) { - usb_driver_release_interface(&uvc_driver.driver, intf); - return -EINVAL; - } - - mutex_init(&streaming->mutex); - streaming->dev = dev; - streaming->intf = usb_get_intf(intf); - streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; - - /* The Pico iMage webcam has its class-specific interface descriptors - * after the endpoint descriptors. - */ - if (buflen == 0) { - for (i = 0; i < alts->desc.bNumEndpoints; ++i) { - struct usb_host_endpoint *ep = &alts->endpoint[i]; - - if (ep->extralen == 0) - continue; - - if (ep->extralen > 2 && - ep->extra[1] == USB_DT_CS_INTERFACE) { - uvc_trace(UVC_TRACE_DESCR, "trying extra data " - "from endpoint %u.\n", i); - buffer = alts->endpoint[i].extra; - buflen = alts->endpoint[i].extralen; - break; - } - } - } - - /* Skip the standard interface descriptors. */ - while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { - buflen -= buffer[0]; - buffer += buffer[0]; - } - - if (buflen <= 2) { - uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " - "interface descriptors found.\n"); - goto error; - } - - /* Parse the header descriptor. */ - switch (buffer[2]) { - case UVC_VS_OUTPUT_HEADER: - streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - size = 9; - break; - - case UVC_VS_INPUT_HEADER: - streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - size = 13; - break; - - default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d HEADER descriptor not found.\n", dev->udev->devnum, - alts->desc.bInterfaceNumber); - goto error; - } - - p = buflen >= 4 ? buffer[3] : 0; - n = buflen >= size ? buffer[size-1] : 0; - - if (buflen < size + p*n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d HEADER descriptor is invalid.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); - goto error; - } - - streaming->header.bNumFormats = p; - streaming->header.bEndpointAddress = buffer[6]; - if (buffer[2] == UVC_VS_INPUT_HEADER) { - streaming->header.bmInfo = buffer[7]; - streaming->header.bTerminalLink = buffer[8]; - streaming->header.bStillCaptureMethod = buffer[9]; - streaming->header.bTriggerSupport = buffer[10]; - streaming->header.bTriggerUsage = buffer[11]; - } else { - streaming->header.bTerminalLink = buffer[7]; - } - streaming->header.bControlSize = n; - - streaming->header.bmaControls = kmemdup(&buffer[size], p * n, - GFP_KERNEL); - if (streaming->header.bmaControls == NULL) { - ret = -ENOMEM; - goto error; - } - - buflen -= buffer[0]; - buffer += buffer[0]; - - _buffer = buffer; - _buflen = buflen; - - /* Count the format and frame descriptors. */ - while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { - switch (_buffer[2]) { - case UVC_VS_FORMAT_UNCOMPRESSED: - case UVC_VS_FORMAT_MJPEG: - case UVC_VS_FORMAT_FRAME_BASED: - nformats++; - break; - - case UVC_VS_FORMAT_DV: - /* DV format has no frame descriptor. We will create a - * dummy frame descriptor with a dummy frame interval. - */ - nformats++; - nframes++; - nintervals++; - break; - - case UVC_VS_FORMAT_MPEG2TS: - case UVC_VS_FORMAT_STREAM_BASED: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT %u is not supported.\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber, _buffer[2]); - break; - - case UVC_VS_FRAME_UNCOMPRESSED: - case UVC_VS_FRAME_MJPEG: - nframes++; - if (_buflen > 25) - nintervals += _buffer[25] ? _buffer[25] : 3; - break; - - case UVC_VS_FRAME_FRAME_BASED: - nframes++; - if (_buflen > 21) - nintervals += _buffer[21] ? _buffer[21] : 3; - break; - } - - _buflen -= _buffer[0]; - _buffer += _buffer[0]; - } - - if (nformats == 0) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d has no supported formats defined.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); - goto error; - } - - size = nformats * sizeof *format + nframes * sizeof *frame - + nintervals * sizeof *interval; - format = kzalloc(size, GFP_KERNEL); - if (format == NULL) { - ret = -ENOMEM; - goto error; - } - - frame = (struct uvc_frame *)&format[nformats]; - interval = (__u32 *)&frame[nframes]; - - streaming->format = format; - streaming->nformats = nformats; - - /* Parse the format descriptors. */ - while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { - switch (buffer[2]) { - case UVC_VS_FORMAT_UNCOMPRESSED: - case UVC_VS_FORMAT_MJPEG: - case UVC_VS_FORMAT_DV: - case UVC_VS_FORMAT_FRAME_BASED: - format->frame = frame; - ret = uvc_parse_format(dev, streaming, format, - &interval, buffer, buflen); - if (ret < 0) - goto error; - - frame += format->nframes; - format++; - - buflen -= ret; - buffer += ret; - continue; - - default: - break; - } - - buflen -= buffer[0]; - buffer += buffer[0]; - } - - if (buflen) - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d has %u bytes of trailing descriptor garbage.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); - - /* Parse the alternate settings to find the maximum bandwidth. */ - for (i = 0; i < intf->num_altsetting; ++i) { - struct usb_host_endpoint *ep; - alts = &intf->altsetting[i]; - ep = uvc_find_endpoint(alts, - streaming->header.bEndpointAddress); - if (ep == NULL) - continue; - - psize = le16_to_cpu(ep->desc.wMaxPacketSize); - psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - if (psize > streaming->maxpsize) - streaming->maxpsize = psize; - } - - list_add_tail(&streaming->list, &dev->streams); - return 0; - -error: - usb_driver_release_interface(&uvc_driver.driver, intf); - usb_put_intf(intf); - kfree(streaming->format); - kfree(streaming->header.bmaControls); - kfree(streaming); - return ret; -} - -static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, - unsigned int num_pads, unsigned int extra_size) -{ - struct uvc_entity *entity; - unsigned int num_inputs; - unsigned int size; - unsigned int i; - - extra_size = ALIGN(extra_size, sizeof(*entity->pads)); - num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; - size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads - + num_inputs; - entity = kzalloc(size, GFP_KERNEL); - if (entity == NULL) - return NULL; - - entity->id = id; - entity->type = type; - - entity->num_links = 0; - entity->num_pads = num_pads; - entity->pads = ((void *)(entity + 1)) + extra_size; - - for (i = 0; i < num_inputs; ++i) - entity->pads[i].flags = MEDIA_PAD_FL_SINK; - if (!UVC_ENTITY_IS_OTERM(entity)) - entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; - - entity->bNrInPins = num_inputs; - entity->baSourceID = (__u8 *)(&entity->pads[num_pads]); - - return entity; -} - -/* Parse vendor-specific extensions. */ -static int uvc_parse_vendor_control(struct uvc_device *dev, - const unsigned char *buffer, int buflen) -{ - struct usb_device *udev = dev->udev; - struct usb_host_interface *alts = dev->intf->cur_altsetting; - struct uvc_entity *unit; - unsigned int n, p; - int handled = 0; - - switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { - case 0x046d: /* Logitech */ - if (buffer[1] != 0x41 || buffer[2] != 0x01) - break; - - /* Logitech implements several vendor specific functions - * through vendor specific extension units (LXU). - * - * The LXU descriptors are similar to XU descriptors - * (see "USB Device Video Class for Video Devices", section - * 3.7.2.6 "Extension Unit Descriptor") with the following - * differences: - * - * ---------------------------------------------------------- - * 0 bLength 1 Number - * Size of this descriptor, in bytes: 24+p+n*2 - * ---------------------------------------------------------- - * 23+p+n bmControlsType N Bitmap - * Individual bits in the set are defined: - * 0: Absolute - * 1: Relative - * - * This bitset is mapped exactly the same as bmControls. - * ---------------------------------------------------------- - * 23+p+n*2 bReserved 1 Boolean - * ---------------------------------------------------------- - * 24+p+n*2 iExtension 1 Index - * Index of a string descriptor that describes this - * extension unit. - * ---------------------------------------------------------- - */ - p = buflen >= 22 ? buffer[21] : 0; - n = buflen >= 25 + p ? buffer[22+p] : 0; - - if (buflen < 25 + p + 2*n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d EXTENSION_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); - break; - } - - unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], - p + 1, 2*n); - if (unit == NULL) - return -ENOMEM; - - memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); - unit->extension.bNumControls = buffer[20]; - memcpy(unit->baSourceID, &buffer[22], p); - unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); - unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) - + n; - memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); - - if (buffer[24+p+2*n] != 0) - usb_string(udev, buffer[24+p+2*n], unit->name, - sizeof unit->name); - else - sprintf(unit->name, "Extension %u", buffer[3]); - - list_add_tail(&unit->list, &dev->entities); - handled = 1; - break; - } - - return handled; -} - -static int uvc_parse_standard_control(struct uvc_device *dev, - const unsigned char *buffer, int buflen) -{ - struct usb_device *udev = dev->udev; - struct uvc_entity *unit, *term; - struct usb_interface *intf; - struct usb_host_interface *alts = dev->intf->cur_altsetting; - unsigned int i, n, p, len; - __u16 type; - - switch (buffer[2]) { - case UVC_VC_HEADER: - n = buflen >= 12 ? buffer[11] : 0; - - if (buflen < 12 || buflen < 12 + n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d HEADER error\n", udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - dev->uvc_version = get_unaligned_le16(&buffer[3]); - dev->clock_frequency = get_unaligned_le32(&buffer[7]); - - /* Parse all USB Video Streaming interfaces. */ - for (i = 0; i < n; ++i) { - intf = usb_ifnum_to_if(udev, buffer[12+i]); - if (intf == NULL) { - uvc_trace(UVC_TRACE_DESCR, "device %d " - "interface %d doesn't exists\n", - udev->devnum, i); - continue; - } - - uvc_parse_streaming(dev, intf); - } - break; - - case UVC_VC_INPUT_TERMINAL: - if (buflen < 8) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d INPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); - return -EINVAL; - } - - /* Make sure the terminal type MSB is not null, otherwise it - * could be confused with a unit. - */ - type = get_unaligned_le16(&buffer[4]); - if ((type & 0xff00) == 0) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d INPUT_TERMINAL %d has invalid " - "type 0x%04x, skipping\n", udev->devnum, - alts->desc.bInterfaceNumber, - buffer[3], type); - return 0; - } - - n = 0; - p = 0; - len = 8; - - if (type == UVC_ITT_CAMERA) { - n = buflen >= 15 ? buffer[14] : 0; - len = 15; - - } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { - n = buflen >= 9 ? buffer[8] : 0; - p = buflen >= 10 + n ? buffer[9+n] : 0; - len = 10; - } - - if (buflen < len + n + p) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d INPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); - return -EINVAL; - } - - term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], - 1, n + p); - if (term == NULL) - return -ENOMEM; - - if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { - term->camera.bControlSize = n; - term->camera.bmControls = (__u8 *)term + sizeof *term; - term->camera.wObjectiveFocalLengthMin = - get_unaligned_le16(&buffer[8]); - term->camera.wObjectiveFocalLengthMax = - get_unaligned_le16(&buffer[10]); - term->camera.wOcularFocalLength = - get_unaligned_le16(&buffer[12]); - memcpy(term->camera.bmControls, &buffer[15], n); - } else if (UVC_ENTITY_TYPE(term) == - UVC_ITT_MEDIA_TRANSPORT_INPUT) { - term->media.bControlSize = n; - term->media.bmControls = (__u8 *)term + sizeof *term; - term->media.bTransportModeSize = p; - term->media.bmTransportModes = (__u8 *)term - + sizeof *term + n; - memcpy(term->media.bmControls, &buffer[9], n); - memcpy(term->media.bmTransportModes, &buffer[10+n], p); - } - - if (buffer[7] != 0) - usb_string(udev, buffer[7], term->name, - sizeof term->name); - else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) - sprintf(term->name, "Camera %u", buffer[3]); - else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) - sprintf(term->name, "Media %u", buffer[3]); - else - sprintf(term->name, "Input %u", buffer[3]); - - list_add_tail(&term->list, &dev->entities); - break; - - case UVC_VC_OUTPUT_TERMINAL: - if (buflen < 9) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d OUTPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); - return -EINVAL; - } - - /* Make sure the terminal type MSB is not null, otherwise it - * could be confused with a unit. - */ - type = get_unaligned_le16(&buffer[4]); - if ((type & 0xff00) == 0) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d OUTPUT_TERMINAL %d has invalid " - "type 0x%04x, skipping\n", udev->devnum, - alts->desc.bInterfaceNumber, buffer[3], type); - return 0; - } - - term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], - 1, 0); - if (term == NULL) - return -ENOMEM; - - memcpy(term->baSourceID, &buffer[7], 1); - - if (buffer[8] != 0) - usb_string(udev, buffer[8], term->name, - sizeof term->name); - else - sprintf(term->name, "Output %u", buffer[3]); - - list_add_tail(&term->list, &dev->entities); - break; - - case UVC_VC_SELECTOR_UNIT: - p = buflen >= 5 ? buffer[4] : 0; - - if (buflen < 5 || buflen < 6 + p) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d SELECTOR_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); - return -EINVAL; - } - - unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); - if (unit == NULL) - return -ENOMEM; - - memcpy(unit->baSourceID, &buffer[5], p); - - if (buffer[5+p] != 0) - usb_string(udev, buffer[5+p], unit->name, - sizeof unit->name); - else - sprintf(unit->name, "Selector %u", buffer[3]); - - list_add_tail(&unit->list, &dev->entities); - break; - - case UVC_VC_PROCESSING_UNIT: - n = buflen >= 8 ? buffer[7] : 0; - p = dev->uvc_version >= 0x0110 ? 10 : 9; - - if (buflen < p + n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d PROCESSING_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); - return -EINVAL; - } - - unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); - if (unit == NULL) - return -ENOMEM; - - memcpy(unit->baSourceID, &buffer[4], 1); - unit->processing.wMaxMultiplier = - get_unaligned_le16(&buffer[5]); - unit->processing.bControlSize = buffer[7]; - unit->processing.bmControls = (__u8 *)unit + sizeof *unit; - memcpy(unit->processing.bmControls, &buffer[8], n); - if (dev->uvc_version >= 0x0110) - unit->processing.bmVideoStandards = buffer[9+n]; - - if (buffer[8+n] != 0) - usb_string(udev, buffer[8+n], unit->name, - sizeof unit->name); - else - sprintf(unit->name, "Processing %u", buffer[3]); - - list_add_tail(&unit->list, &dev->entities); - break; - - case UVC_VC_EXTENSION_UNIT: - p = buflen >= 22 ? buffer[21] : 0; - n = buflen >= 24 + p ? buffer[22+p] : 0; - - if (buflen < 24 + p + n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d EXTENSION_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); - return -EINVAL; - } - - unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); - if (unit == NULL) - return -ENOMEM; - - memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); - unit->extension.bNumControls = buffer[20]; - memcpy(unit->baSourceID, &buffer[22], p); - unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.bmControls, &buffer[23+p], n); - - if (buffer[23+p+n] != 0) - usb_string(udev, buffer[23+p+n], unit->name, - sizeof unit->name); - else - sprintf(unit->name, "Extension %u", buffer[3]); - - list_add_tail(&unit->list, &dev->entities); - break; - - default: - uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " - "descriptor (%u)\n", buffer[2]); - break; - } - - return 0; -} - -static int uvc_parse_control(struct uvc_device *dev) -{ - struct usb_host_interface *alts = dev->intf->cur_altsetting; - unsigned char *buffer = alts->extra; - int buflen = alts->extralen; - int ret; - - /* Parse the default alternate setting only, as the UVC specification - * defines a single alternate setting, the default alternate setting - * zero. - */ - - while (buflen > 2) { - if (uvc_parse_vendor_control(dev, buffer, buflen) || - buffer[1] != USB_DT_CS_INTERFACE) - goto next_descriptor; - - if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) - return ret; - -next_descriptor: - buflen -= buffer[0]; - buffer += buffer[0]; - } - - /* Check if the optional status endpoint is present. Built-in iSight - * webcams have an interrupt endpoint but spit proprietary data that - * don't conform to the UVC status endpoint messages. Don't try to - * handle the interrupt endpoint for those cameras. - */ - if (alts->desc.bNumEndpoints == 1 && - !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { - struct usb_host_endpoint *ep = &alts->endpoint[0]; - struct usb_endpoint_descriptor *desc = &ep->desc; - - if (usb_endpoint_is_int_in(desc) && - le16_to_cpu(desc->wMaxPacketSize) >= 8 && - desc->bInterval != 0) { - uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " - "(addr %02x).\n", desc->bEndpointAddress); - dev->int_ep = ep; - } - } - - return 0; -} - -/* ------------------------------------------------------------------------ - * UVC device scan - */ - -/* - * Scan the UVC descriptors to locate a chain starting at an Output Terminal - * and containing the following units: - * - * - one or more Output Terminals (USB Streaming or Display) - * - zero or one Processing Unit - * - zero, one or more single-input Selector Units - * - zero or one multiple-input Selector Units, provided all inputs are - * connected to input terminals - * - zero, one or mode single-input Extension Units - * - one or more Input Terminals (Camera, External or USB Streaming) - * - * The terminal and units must match on of the following structures: - * - * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) - * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... - * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) - * - * +---------+ +---------+ -> OTT_*(0) - * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... - * +---------+ +---------+ -> OTT_*(n) - * - * The Processing Unit and Extension Units can be in any order. Additional - * Extension Units connected to the main chain as single-unit branches are - * also supported. Single-input Selector Units are ignored. - */ -static int uvc_scan_chain_entity(struct uvc_video_chain *chain, - struct uvc_entity *entity) -{ - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_VC_EXTENSION_UNIT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- XU %d", entity->id); - - if (entity->bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " - "than 1 input pin.\n", entity->id); - return -1; - } - - break; - - case UVC_VC_PROCESSING_UNIT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- PU %d", entity->id); - - if (chain->processing != NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found multiple " - "Processing Units in chain.\n"); - return -1; - } - - chain->processing = entity; - break; - - case UVC_VC_SELECTOR_UNIT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- SU %d", entity->id); - - /* Single-input selector units are ignored. */ - if (entity->bNrInPins == 1) - break; - - if (chain->selector != NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " - "Units in chain.\n"); - return -1; - } - - chain->selector = entity; - break; - - case UVC_ITT_VENDOR_SPECIFIC: - case UVC_ITT_CAMERA: - case UVC_ITT_MEDIA_TRANSPORT_INPUT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- IT %d\n", entity->id); - - break; - - case UVC_OTT_VENDOR_SPECIFIC: - case UVC_OTT_DISPLAY: - case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" OT %d", entity->id); - - break; - - case UVC_TT_STREAMING: - if (UVC_ENTITY_IS_ITERM(entity)) { - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- IT %d\n", entity->id); - } else { - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" OT %d", entity->id); - } - - break; - - default: - uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " - "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); - return -1; - } - - list_add_tail(&entity->chain, &chain->entities); - return 0; -} - -static int uvc_scan_chain_forward(struct uvc_video_chain *chain, - struct uvc_entity *entity, struct uvc_entity *prev) -{ - struct uvc_entity *forward; - int found; - - /* Forward scan */ - forward = NULL; - found = 0; - - while (1) { - forward = uvc_entity_by_reference(chain->dev, entity->id, - forward); - if (forward == NULL) - break; - if (forward == prev) - continue; - - switch (UVC_ENTITY_TYPE(forward)) { - case UVC_VC_EXTENSION_UNIT: - if (forward->bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " - "has more than 1 input pin.\n", - entity->id); - return -EINVAL; - } - - list_add_tail(&forward->chain, &chain->entities); - if (uvc_trace_param & UVC_TRACE_PROBE) { - if (!found) - printk(" (->"); - - printk(" XU %d", forward->id); - found = 1; - } - break; - - case UVC_OTT_VENDOR_SPECIFIC: - case UVC_OTT_DISPLAY: - case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: - case UVC_TT_STREAMING: - if (UVC_ENTITY_IS_ITERM(forward)) { - uvc_trace(UVC_TRACE_DESCR, "Unsupported input " - "terminal %u.\n", forward->id); - return -EINVAL; - } - - list_add_tail(&forward->chain, &chain->entities); - if (uvc_trace_param & UVC_TRACE_PROBE) { - if (!found) - printk(" (->"); - - printk(" OT %d", forward->id); - found = 1; - } - break; - } - } - if (found) - printk(")"); - - return 0; -} - -static int uvc_scan_chain_backward(struct uvc_video_chain *chain, - struct uvc_entity **_entity) -{ - struct uvc_entity *entity = *_entity; - struct uvc_entity *term; - int id = -EINVAL, i; - - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_VC_EXTENSION_UNIT: - case UVC_VC_PROCESSING_UNIT: - id = entity->baSourceID[0]; - break; - - case UVC_VC_SELECTOR_UNIT: - /* Single-input selector units are ignored. */ - if (entity->bNrInPins == 1) { - id = entity->baSourceID[0]; - break; - } - - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- IT"); - - chain->selector = entity; - for (i = 0; i < entity->bNrInPins; ++i) { - id = entity->baSourceID[i]; - term = uvc_entity_by_id(chain->dev, id); - if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { - uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " - "input %d isn't connected to an " - "input terminal\n", entity->id, i); - return -1; - } - - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" %d", term->id); - - list_add_tail(&term->chain, &chain->entities); - uvc_scan_chain_forward(chain, term, entity); - } - - if (uvc_trace_param & UVC_TRACE_PROBE) - printk("\n"); - - id = 0; - break; - - case UVC_ITT_VENDOR_SPECIFIC: - case UVC_ITT_CAMERA: - case UVC_ITT_MEDIA_TRANSPORT_INPUT: - case UVC_OTT_VENDOR_SPECIFIC: - case UVC_OTT_DISPLAY: - case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: - case UVC_TT_STREAMING: - id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; - break; - } - - if (id <= 0) { - *_entity = NULL; - return id; - } - - entity = uvc_entity_by_id(chain->dev, id); - if (entity == NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "unknown entity %d.\n", id); - return -EINVAL; - } - - *_entity = entity; - return 0; -} - -static int uvc_scan_chain(struct uvc_video_chain *chain, - struct uvc_entity *term) -{ - struct uvc_entity *entity, *prev; - - uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); - - entity = term; - prev = NULL; - - while (entity != NULL) { - /* Entity must not be part of an existing chain */ - if (entity->chain.next || entity->chain.prev) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", entity->id); - return -EINVAL; - } - - /* Process entity */ - if (uvc_scan_chain_entity(chain, entity) < 0) - return -EINVAL; - - /* Forward scan */ - if (uvc_scan_chain_forward(chain, entity, prev) < 0) - return -EINVAL; - - /* Backward scan */ - prev = entity; - if (uvc_scan_chain_backward(chain, &entity) < 0) - return -EINVAL; - } - - return 0; -} - -static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, - char *buffer) -{ - struct uvc_entity *term; - unsigned int nterms = 0; - char *p = buffer; - - list_for_each_entry(term, terms, chain) { - if (!UVC_ENTITY_IS_TERM(term) || - UVC_TERM_DIRECTION(term) != dir) - continue; - - if (nterms) - p += sprintf(p, ","); - if (++nterms >= 4) { - p += sprintf(p, "..."); - break; - } - p += sprintf(p, "%u", term->id); - } - - return p - buffer; -} - -static const char *uvc_print_chain(struct uvc_video_chain *chain) -{ - static char buffer[43]; - char *p = buffer; - - p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); - p += sprintf(p, " -> "); - uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); - - return buffer; -} - -/* - * Scan the device for video chains and register video devices. - * - * Chains are scanned starting at their output terminals and walked backwards. - */ -static int uvc_scan_device(struct uvc_device *dev) -{ - struct uvc_video_chain *chain; - struct uvc_entity *term; - - list_for_each_entry(term, &dev->entities, list) { - if (!UVC_ENTITY_IS_OTERM(term)) - continue; - - /* If the terminal is already included in a chain, skip it. - * This can happen for chains that have multiple output - * terminals, where all output terminals beside the first one - * will be inserted in the chain in forward scans. - */ - if (term->chain.next || term->chain.prev) - continue; - - chain = kzalloc(sizeof(*chain), GFP_KERNEL); - if (chain == NULL) - return -ENOMEM; - - INIT_LIST_HEAD(&chain->entities); - mutex_init(&chain->ctrl_mutex); - chain->dev = dev; - - if (uvc_scan_chain(chain, term) < 0) { - kfree(chain); - continue; - } - - uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", - uvc_print_chain(chain)); - - list_add_tail(&chain->list, &dev->chains); - } - - if (list_empty(&dev->chains)) { - uvc_printk(KERN_INFO, "No valid video chain found.\n"); - return -1; - } - - return 0; -} - -/* ------------------------------------------------------------------------ - * Video device registration and unregistration - */ - -/* - * Delete the UVC device. - * - * Called by the kernel when the last reference to the uvc_device structure - * is released. - * - * As this function is called after or during disconnect(), all URBs have - * already been canceled by the USB core. There is no need to kill the - * interrupt URB manually. - */ -static void uvc_delete(struct uvc_device *dev) -{ - struct list_head *p, *n; - - usb_put_intf(dev->intf); - usb_put_dev(dev->udev); - - uvc_status_cleanup(dev); - uvc_ctrl_cleanup_device(dev); - - if (dev->vdev.dev) - v4l2_device_unregister(&dev->vdev); -#ifdef CONFIG_MEDIA_CONTROLLER - if (media_devnode_is_registered(&dev->mdev.devnode)) - media_device_unregister(&dev->mdev); -#endif - - list_for_each_safe(p, n, &dev->chains) { - struct uvc_video_chain *chain; - chain = list_entry(p, struct uvc_video_chain, list); - kfree(chain); - } - - list_for_each_safe(p, n, &dev->entities) { - struct uvc_entity *entity; - entity = list_entry(p, struct uvc_entity, list); -#ifdef CONFIG_MEDIA_CONTROLLER - uvc_mc_cleanup_entity(entity); -#endif - if (entity->vdev) { - video_device_release(entity->vdev); - entity->vdev = NULL; - } - kfree(entity); - } - - list_for_each_safe(p, n, &dev->streams) { - struct uvc_streaming *streaming; - streaming = list_entry(p, struct uvc_streaming, list); - usb_driver_release_interface(&uvc_driver.driver, - streaming->intf); - usb_put_intf(streaming->intf); - kfree(streaming->format); - kfree(streaming->header.bmaControls); - kfree(streaming); - } - - kfree(dev); -} - -static void uvc_release(struct video_device *vdev) -{ - struct uvc_streaming *stream = video_get_drvdata(vdev); - struct uvc_device *dev = stream->dev; - - /* Decrement the registered streams count and delete the device when it - * reaches zero. - */ - if (atomic_dec_and_test(&dev->nstreams)) - uvc_delete(dev); -} - -/* - * Unregister the video devices. - */ -static void uvc_unregister_video(struct uvc_device *dev) -{ - struct uvc_streaming *stream; - - /* Unregistering all video devices might result in uvc_delete() being - * called from inside the loop if there's no open file handle. To avoid - * that, increment the stream count before iterating over the streams - * and decrement it when done. - */ - atomic_inc(&dev->nstreams); - - list_for_each_entry(stream, &dev->streams, list) { - if (stream->vdev == NULL) - continue; - - video_unregister_device(stream->vdev); - stream->vdev = NULL; - - uvc_debugfs_cleanup_stream(stream); - } - - /* Decrement the stream count and call uvc_delete explicitly if there - * are no stream left. - */ - if (atomic_dec_and_test(&dev->nstreams)) - uvc_delete(dev); -} - -static int uvc_register_video(struct uvc_device *dev, - struct uvc_streaming *stream) -{ - struct video_device *vdev; - int ret; - - /* Initialize the streaming interface with default streaming - * parameters. - */ - ret = uvc_video_init(stream); - if (ret < 0) { - uvc_printk(KERN_ERR, "Failed to initialize the device " - "(%d).\n", ret); - return ret; - } - - uvc_debugfs_init_stream(stream); - - /* Register the device with V4L. */ - vdev = video_device_alloc(); - if (vdev == NULL) { - uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n", - ret); - return -ENOMEM; - } - - /* We already hold a reference to dev->udev. The video device will be - * unregistered before the reference is released, so we don't need to - * get another one. - */ - vdev->v4l2_dev = &dev->vdev; - vdev->fops = &uvc_fops; - vdev->release = uvc_release; - strlcpy(vdev->name, dev->name, sizeof vdev->name); - - /* Set the driver data before calling video_register_device, otherwise - * uvc_v4l2_open might race us. - */ - stream->vdev = vdev; - video_set_drvdata(vdev, stream); - - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (ret < 0) { - uvc_printk(KERN_ERR, "Failed to register video device (%d).\n", - ret); - stream->vdev = NULL; - video_device_release(vdev); - return ret; - } - - atomic_inc(&dev->nstreams); - return 0; -} - -/* - * Register all video devices in all chains. - */ -static int uvc_register_terms(struct uvc_device *dev, - struct uvc_video_chain *chain) -{ - struct uvc_streaming *stream; - struct uvc_entity *term; - int ret; - - list_for_each_entry(term, &chain->entities, chain) { - if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) - continue; - - stream = uvc_stream_by_id(dev, term->id); - if (stream == NULL) { - uvc_printk(KERN_INFO, "No streaming interface found " - "for terminal %u.", term->id); - continue; - } - - stream->chain = chain; - ret = uvc_register_video(dev, stream); - if (ret < 0) - return ret; - - term->vdev = stream->vdev; - } - - return 0; -} - -static int uvc_register_chains(struct uvc_device *dev) -{ - struct uvc_video_chain *chain; - int ret; - - list_for_each_entry(chain, &dev->chains, list) { - ret = uvc_register_terms(dev, chain); - if (ret < 0) - return ret; - -#ifdef CONFIG_MEDIA_CONTROLLER - ret = uvc_mc_register_entities(chain); - if (ret < 0) { - uvc_printk(KERN_INFO, "Failed to register entites " - "(%d).\n", ret); - } -#endif - } - - return 0; -} - -/* ------------------------------------------------------------------------ - * USB probe, disconnect, suspend and resume - */ - -static int uvc_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct uvc_device *dev; - int ret; - - if (id->idVendor && id->idProduct) - uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " - "(%04x:%04x)\n", udev->devpath, id->idVendor, - id->idProduct); - else - uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", - udev->devpath); - - /* Allocate memory for the device and initialize it. */ - if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) - return -ENOMEM; - - INIT_LIST_HEAD(&dev->entities); - INIT_LIST_HEAD(&dev->chains); - INIT_LIST_HEAD(&dev->streams); - atomic_set(&dev->nstreams, 0); - atomic_set(&dev->users, 0); - atomic_set(&dev->nmappings, 0); - - dev->udev = usb_get_dev(udev); - dev->intf = usb_get_intf(intf); - dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; - dev->quirks = (uvc_quirks_param == -1) - ? id->driver_info : uvc_quirks_param; - - if (udev->product != NULL) - strlcpy(dev->name, udev->product, sizeof dev->name); - else - snprintf(dev->name, sizeof dev->name, - "UVC Camera (%04x:%04x)", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); - - /* Parse the Video Class control descriptor. */ - if (uvc_parse_control(dev) < 0) { - uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " - "descriptors.\n"); - goto error; - } - - uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", - dev->uvc_version >> 8, dev->uvc_version & 0xff, - udev->product ? udev->product : "", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); - - if (dev->quirks != id->driver_info) { - uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " - "parameter for testing purpose.\n", dev->quirks); - uvc_printk(KERN_INFO, "Please report required quirks to the " - "linux-uvc-devel mailing list.\n"); - } - - /* Register the media and V4L2 devices. */ -#ifdef CONFIG_MEDIA_CONTROLLER - dev->mdev.dev = &intf->dev; - strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); - if (udev->serial) - strlcpy(dev->mdev.serial, udev->serial, - sizeof(dev->mdev.serial)); - strcpy(dev->mdev.bus_info, udev->devpath); - dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); - dev->mdev.driver_version = LINUX_VERSION_CODE; - if (media_device_register(&dev->mdev) < 0) - goto error; - - dev->vdev.mdev = &dev->mdev; -#endif - if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) - goto error; - - /* Initialize controls. */ - if (uvc_ctrl_init_device(dev) < 0) - goto error; - - /* Scan the device for video chains. */ - if (uvc_scan_device(dev) < 0) - goto error; - - /* Register video device nodes. */ - if (uvc_register_chains(dev) < 0) - goto error; - - /* Save our data pointer in the interface data. */ - usb_set_intfdata(intf, dev); - - /* Initialize the interrupt URB. */ - if ((ret = uvc_status_init(dev)) < 0) { - uvc_printk(KERN_INFO, "Unable to initialize the status " - "endpoint (%d), status interrupt will not be " - "supported.\n", ret); - } - - uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); - usb_enable_autosuspend(udev); - return 0; - -error: - uvc_unregister_video(dev); - return -ENODEV; -} - -static void uvc_disconnect(struct usb_interface *intf) -{ - struct uvc_device *dev = usb_get_intfdata(intf); - - /* Set the USB interface data to NULL. This can be done outside the - * lock, as there's no other reader. - */ - usb_set_intfdata(intf, NULL); - - if (intf->cur_altsetting->desc.bInterfaceSubClass == - UVC_SC_VIDEOSTREAMING) - return; - - dev->state |= UVC_DEV_DISCONNECTED; - - uvc_unregister_video(dev); -} - -static int uvc_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct uvc_device *dev = usb_get_intfdata(intf); - struct uvc_streaming *stream; - - uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", - intf->cur_altsetting->desc.bInterfaceNumber); - - /* Controls are cached on the fly so they don't need to be saved. */ - if (intf->cur_altsetting->desc.bInterfaceSubClass == - UVC_SC_VIDEOCONTROL) - return uvc_status_suspend(dev); - - list_for_each_entry(stream, &dev->streams, list) { - if (stream->intf == intf) - return uvc_video_suspend(stream); - } - - uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " - "mismatch.\n"); - return -EINVAL; -} - -static int __uvc_resume(struct usb_interface *intf, int reset) -{ - struct uvc_device *dev = usb_get_intfdata(intf); - struct uvc_streaming *stream; - - uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", - intf->cur_altsetting->desc.bInterfaceNumber); - - if (intf->cur_altsetting->desc.bInterfaceSubClass == - UVC_SC_VIDEOCONTROL) { - if (reset) { - int ret = uvc_ctrl_resume_device(dev); - - if (ret < 0) - return ret; - } - - return uvc_status_resume(dev); - } - - list_for_each_entry(stream, &dev->streams, list) { - if (stream->intf == intf) - return uvc_video_resume(stream, reset); - } - - uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " - "mismatch.\n"); - return -EINVAL; -} - -static int uvc_resume(struct usb_interface *intf) -{ - return __uvc_resume(intf, 0); -} - -static int uvc_reset_resume(struct usb_interface *intf) -{ - return __uvc_resume(intf, 1); -} - -/* ------------------------------------------------------------------------ - * Module parameters - */ - -static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) -{ - if (uvc_clock_param == CLOCK_MONOTONIC) - return sprintf(buffer, "CLOCK_MONOTONIC"); - else - return sprintf(buffer, "CLOCK_REALTIME"); -} - -static int uvc_clock_param_set(const char *val, struct kernel_param *kp) -{ - if (strncasecmp(val, "clock_", strlen("clock_")) == 0) - val += strlen("clock_"); - - if (strcasecmp(val, "monotonic") == 0) - uvc_clock_param = CLOCK_MONOTONIC; - else if (strcasecmp(val, "realtime") == 0) - uvc_clock_param = CLOCK_REALTIME; - else - return -EINVAL; - - return 0; -} - -module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, - &uvc_clock_param, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); -module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); -module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(quirks, "Forced device quirks"); -module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(trace, "Trace level bitmask"); -module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); - -/* ------------------------------------------------------------------------ - * Driver initialization and cleanup - */ - -/* - * The Logitech cameras listed below have their interface class set to - * VENDOR_SPEC because they don't announce themselves as UVC devices, even - * though they are compliant. - */ -static struct usb_device_id uvc_ids[] = { - /* LogiLink Wireless Webcam */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0416, - .idProduct = 0xa91a, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Genius eFace 2025 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0458, - .idProduct = 0x706e, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Microsoft Lifecam NX-6000 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x045e, - .idProduct = 0x00f8, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Microsoft Lifecam VX-7000 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x045e, - .idProduct = 0x0723, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Logitech Quickcam Fusion */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x046d, - .idProduct = 0x08c1, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0 }, - /* Logitech Quickcam Orbit MP */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x046d, - .idProduct = 0x08c2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0 }, - /* Logitech Quickcam Pro for Notebook */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x046d, - .idProduct = 0x08c3, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0 }, - /* Logitech Quickcam Pro 5000 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x046d, - .idProduct = 0x08c5, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0 }, - /* Logitech Quickcam OEM Dell Notebook */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x046d, - .idProduct = 0x08c6, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0 }, - /* Logitech Quickcam OEM Cisco VT Camera II */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x046d, - .idProduct = 0x08c7, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0 }, - /* Chicony CNF7129 (Asus EEE 100HE) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x04f2, - .idProduct = 0xb071, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE }, - /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x058f, - .idProduct = 0x3820, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Dell XPS m1530 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x05a9, - .idProduct = 0x2640, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_DEF }, - /* Apple Built-In iSight */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x05ac, - .idProduct = 0x8501, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_BUILTIN_ISIGHT }, - /* Foxlink ("HP Webcam" on HP Mini 5103) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x05c8, - .idProduct = 0x0403, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, - /* Genesys Logic USB 2.0 PC Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x05e3, - .idProduct = 0x0505, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Hercules Classic Silver */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x06f8, - .idProduct = 0x300c, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, - /* ViMicro Vega */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0ac8, - .idProduct = 0x332d, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, - /* ViMicro - Minoru3D */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0ac8, - .idProduct = 0x3410, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, - /* ViMicro Venus - Minoru3D */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0ac8, - .idProduct = 0x3420, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, - /* Ophir Optronics - SPCAM 620U */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0bd3, - .idProduct = 0x0555, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* MT6227 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0e8d, - .idProduct = 0x0004, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_PROBE_DEF }, - /* IMC Networks (Medion Akoya) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x13d3, - .idProduct = 0x5103, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* JMicron USB2.0 XGA WebCam */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x152d, - .idProduct = 0x0310, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Syntek (HP Spartan) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x174f, - .idProduct = 0x5212, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Syntek (Samsung Q310) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x174f, - .idProduct = 0x5931, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Syntek (Packard Bell EasyNote MX52 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x174f, - .idProduct = 0x8a12, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Syntek (Asus F9SG) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x174f, - .idProduct = 0x8a31, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Syntek (Asus U3S) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x174f, - .idProduct = 0x8a33, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Syntek (JAOtech Smart Terminal) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x174f, - .idProduct = 0x8a34, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Miricle 307K */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x17dc, - .idProduct = 0x0202, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Lenovo Thinkpad SL400/SL500 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x17ef, - .idProduct = 0x480b, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Aveo Technology USB 2.0 Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x1871, - .idProduct = 0x0306, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_PROBE_EXTRAFIELDS }, - /* Ecamm Pico iMage */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x18cd, - .idProduct = 0xcafe, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, - /* Manta MM-353 Plako */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x18ec, - .idProduct = 0x3188, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* FSC WebCam V30S */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x18ec, - .idProduct = 0x3288, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Arkmicro unbranded */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x18ec, - .idProduct = 0x3290, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_DEF }, - /* The Imaging Source USB CCD cameras */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x199e, - .idProduct = 0x8102, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0 }, - /* Bodelin ProScopeHR */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_DEV_HI - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x19ab, - .idProduct = 0x1000, - .bcdDevice_hi = 0x0126, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_STATUS_INTERVAL }, - /* MSI StarCam 370i */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x1b3b, - .idProduct = 0x2951, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* SiGma Micro USB Web Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x1c4f, - .idProduct = 0x3000, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, - /* Generic USB Video Class */ - { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, - {} -}; - -MODULE_DEVICE_TABLE(usb, uvc_ids); - -struct uvc_driver uvc_driver = { - .driver = { - .name = "uvcvideo", - .probe = uvc_probe, - .disconnect = uvc_disconnect, - .suspend = uvc_suspend, - .resume = uvc_resume, - .reset_resume = uvc_reset_resume, - .id_table = uvc_ids, - .supports_autosuspend = 1, - }, -}; - -static int __init uvc_init(void) -{ - int ret; - - uvc_debugfs_init(); - - ret = usb_register(&uvc_driver.driver); - if (ret < 0) { - uvc_debugfs_cleanup(); - return ret; - } - - printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); - return 0; -} - -static void __exit uvc_cleanup(void) -{ - usb_deregister(&uvc_driver.driver); - uvc_debugfs_cleanup(); -} - -module_init(uvc_init); -module_exit(uvc_cleanup); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); - diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c deleted file mode 100644 index 29e239911d0e..000000000000 --- a/drivers/media/video/uvc/uvc_entity.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * uvc_entity.c -- USB Video Class driver - * - * Copyright (C) 2005-2011 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include - -#include - -#include "uvcvideo.h" - -/* ------------------------------------------------------------------------ - * Video subdevices registration and unregistration - */ - -static int uvc_mc_register_entity(struct uvc_video_chain *chain, - struct uvc_entity *entity) -{ - const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; - struct media_entity *sink; - unsigned int i; - int ret; - - sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) - ? (entity->vdev ? &entity->vdev->entity : NULL) - : &entity->subdev.entity; - if (sink == NULL) - return 0; - - for (i = 0; i < entity->num_pads; ++i) { - struct media_entity *source; - struct uvc_entity *remote; - u8 remote_pad; - - if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK)) - continue; - - remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]); - if (remote == NULL) - return -EINVAL; - - source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) - ? (remote->vdev ? &remote->vdev->entity : NULL) - : &remote->subdev.entity; - if (source == NULL) - continue; - - remote_pad = remote->num_pads - 1; - ret = media_entity_create_link(source, remote_pad, - sink, i, flags); - if (ret < 0) - return ret; - } - - if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) - return 0; - - return v4l2_device_register_subdev(&chain->dev->vdev, &entity->subdev); -} - -static struct v4l2_subdev_ops uvc_subdev_ops = { -}; - -void uvc_mc_cleanup_entity(struct uvc_entity *entity) -{ - if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) - media_entity_cleanup(&entity->subdev.entity); - else if (entity->vdev != NULL) - media_entity_cleanup(&entity->vdev->entity); -} - -static int uvc_mc_init_entity(struct uvc_entity *entity) -{ - int ret; - - if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { - v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops); - strlcpy(entity->subdev.name, entity->name, - sizeof(entity->subdev.name)); - - ret = media_entity_init(&entity->subdev.entity, - entity->num_pads, entity->pads, 0); - } else if (entity->vdev != NULL) { - ret = media_entity_init(&entity->vdev->entity, - entity->num_pads, entity->pads, 0); - } else - ret = 0; - - return ret; -} - -int uvc_mc_register_entities(struct uvc_video_chain *chain) -{ - struct uvc_entity *entity; - int ret; - - list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_mc_init_entity(entity); - if (ret < 0) { - uvc_printk(KERN_INFO, "Failed to initialize entity for " - "entity %u\n", entity->id); - return ret; - } - } - - list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_mc_register_entity(chain, entity); - if (ret < 0) { - uvc_printk(KERN_INFO, "Failed to register entity for " - "entity %u\n", entity->id); - return ret; - } - } - - return 0; -} diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c deleted file mode 100644 index 8510e7259e76..000000000000 --- a/drivers/media/video/uvc/uvc_isight.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * uvc_isight.c -- USB Video Class driver - iSight support - * - * Copyright (C) 2006-2007 - * Ivan N. Zlatev - * Copyright (C) 2008-2009 - * Laurent Pinchart - * - * This program is free software; 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. - * - */ - -#include -#include -#include - -#include "uvcvideo.h" - -/* Built-in iSight webcams implements most of UVC 1.0 except a - * different packet format. Instead of sending a header at the - * beginning of each isochronous transfer payload, the webcam sends a - * single header per image (on its own in a packet), followed by - * packets containing data only. - * - * Offset Size (bytes) Description - * ------------------------------------------------------------------ - * 0x00 1 Header length - * 0x01 1 Flags (UVC-compliant) - * 0x02 4 Always equal to '11223344' - * 0x06 8 Always equal to 'deadbeefdeadface' - * 0x0e 16 Unknown - * - * The header can be prefixed by an optional, unknown-purpose byte. - */ - -static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, - const __u8 *data, unsigned int len) -{ - static const __u8 hdr[] = { - 0x11, 0x22, 0x33, 0x44, - 0xde, 0xad, 0xbe, 0xef, - 0xde, 0xad, 0xfa, 0xce - }; - - unsigned int maxlen, nbytes; - __u8 *mem; - int is_header = 0; - - if (buf == NULL) - return 0; - - if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) || - (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) { - uvc_trace(UVC_TRACE_FRAME, "iSight header found\n"); - is_header = 1; - } - - /* Synchronize to the input stream by waiting for a header packet. */ - if (buf->state != UVC_BUF_STATE_ACTIVE) { - if (!is_header) { - uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of " - "sync).\n"); - return 0; - } - - buf->state = UVC_BUF_STATE_ACTIVE; - } - - /* Mark the buffer as done if we're at the beginning of a new frame. - * - * Empty buffers (bytesused == 0) don't trigger end of frame detection - * as it doesn't make sense to return an empty buffer. - */ - if (is_header && buf->bytesused != 0) { - buf->state = UVC_BUF_STATE_DONE; - return -EAGAIN; - } - - /* Copy the video data to the buffer. Skip header packets, as they - * contain no data. - */ - if (!is_header) { - maxlen = buf->length - buf->bytesused; - mem = buf->mem + buf->bytesused; - nbytes = min(len, maxlen); - memcpy(mem, data, nbytes); - buf->bytesused += nbytes; - - if (len > maxlen || buf->bytesused == buf->length) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete " - "(overflow).\n"); - buf->state = UVC_BUF_STATE_DONE; - } - } - - return 0; -} - -void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, - struct uvc_buffer *buf) -{ - int ret, i; - - for (i = 0; i < urb->number_of_packets; ++i) { - if (urb->iso_frame_desc[i].status < 0) { - uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " - "lost (%d).\n", - urb->iso_frame_desc[i].status); - } - - /* Decode the payload packet. - * uvc_video_decode is entered twice when a frame transition - * has been detected because the end of frame can only be - * reliably detected when the first packet of the new frame - * is processed. The first pass detects the transition and - * closes the previous frame's buffer, the second pass - * processes the data of the first payload of the new frame. - */ - do { - ret = isight_decode(&stream->queue, buf, - urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); - - if (buf == NULL) - break; - - if (buf->state == UVC_BUF_STATE_DONE || - buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&stream->queue, - buf); - } while (ret == -EAGAIN); - } -} diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c deleted file mode 100644 index 9288fbd5001b..000000000000 --- a/drivers/media/video/uvc/uvc_queue.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * uvc_queue.c -- USB Video Class driver - Buffers management - * - * Copyright (C) 2005-2010 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "uvcvideo.h" - -/* ------------------------------------------------------------------------ - * Video buffers queue management. - * - * Video queues is initialized by uvc_queue_init(). The function performs - * basic initialization of the uvc_video_queue struct and never fails. - * - * Video buffers are managed by videobuf2. The driver uses a mutex to protect - * the videobuf2 queue operations by serializing calls to videobuf2 and a - * spinlock to protect the IRQ queue that holds the buffers to be processed by - * the driver. - */ - -/* ----------------------------------------------------------------------------- - * videobuf2 queue operations - */ - -static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - struct uvc_streaming *stream = - container_of(queue, struct uvc_streaming, queue); - - if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) - *nbuffers = UVC_MAX_VIDEO_BUFFERS; - - *nplanes = 1; - - sizes[0] = stream->ctrl.dwMaxVideoFrameSize; - - return 0; -} - -static int uvc_buffer_prepare(struct vb2_buffer *vb) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); - - if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); - return -EINVAL; - } - - if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED)) - return -ENODEV; - - buf->state = UVC_BUF_STATE_QUEUED; - buf->error = 0; - buf->mem = vb2_plane_vaddr(vb, 0); - buf->length = vb2_plane_size(vb, 0); - if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - buf->bytesused = 0; - else - buf->bytesused = vb2_get_plane_payload(vb, 0); - - return 0; -} - -static void uvc_buffer_queue(struct vb2_buffer *vb) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); - unsigned long flags; - - spin_lock_irqsave(&queue->irqlock, flags); - if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { - list_add_tail(&buf->queue, &queue->irqqueue); - } else { - /* If the device is disconnected return the buffer to userspace - * directly. The next QBUF call will fail with -ENODEV. - */ - buf->state = UVC_BUF_STATE_ERROR; - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); - } - - spin_unlock_irqrestore(&queue->irqlock, flags); -} - -static int uvc_buffer_finish(struct vb2_buffer *vb) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - struct uvc_streaming *stream = - container_of(queue, struct uvc_streaming, queue); - struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); - - uvc_video_clock_update(stream, &vb->v4l2_buf, buf); - return 0; -} - -static struct vb2_ops uvc_queue_qops = { - .queue_setup = uvc_queue_setup, - .buf_prepare = uvc_buffer_prepare, - .buf_queue = uvc_buffer_queue, - .buf_finish = uvc_buffer_finish, -}; - -void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, - int drop_corrupted) -{ - queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; - queue->queue.drv_priv = queue; - queue->queue.buf_struct_size = sizeof(struct uvc_buffer); - queue->queue.ops = &uvc_queue_qops; - queue->queue.mem_ops = &vb2_vmalloc_memops; - vb2_queue_init(&queue->queue); - - mutex_init(&queue->mutex); - spin_lock_init(&queue->irqlock); - INIT_LIST_HEAD(&queue->irqqueue); - queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 queue operations - */ - -int uvc_alloc_buffers(struct uvc_video_queue *queue, - struct v4l2_requestbuffers *rb) -{ - int ret; - - mutex_lock(&queue->mutex); - ret = vb2_reqbufs(&queue->queue, rb); - mutex_unlock(&queue->mutex); - - return ret ? ret : rb->count; -} - -void uvc_free_buffers(struct uvc_video_queue *queue) -{ - mutex_lock(&queue->mutex); - vb2_queue_release(&queue->queue); - mutex_unlock(&queue->mutex); -} - -int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) -{ - int ret; - - mutex_lock(&queue->mutex); - ret = vb2_querybuf(&queue->queue, buf); - mutex_unlock(&queue->mutex); - - return ret; -} - -int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) -{ - int ret; - - mutex_lock(&queue->mutex); - ret = vb2_qbuf(&queue->queue, buf); - mutex_unlock(&queue->mutex); - - return ret; -} - -int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf, - int nonblocking) -{ - int ret; - - mutex_lock(&queue->mutex); - ret = vb2_dqbuf(&queue->queue, buf, nonblocking); - mutex_unlock(&queue->mutex); - - return ret; -} - -int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) -{ - int ret; - - mutex_lock(&queue->mutex); - ret = vb2_mmap(&queue->queue, vma); - mutex_unlock(&queue->mutex); - - return ret; -} - -#ifndef CONFIG_MMU -unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff) -{ - unsigned long ret; - - mutex_lock(&queue->mutex); - ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); - mutex_unlock(&queue->mutex); - return ret; -} -#endif - -unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, - poll_table *wait) -{ - unsigned int ret; - - mutex_lock(&queue->mutex); - ret = vb2_poll(&queue->queue, file, wait); - mutex_unlock(&queue->mutex); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * - */ - -/* - * Check if buffers have been allocated. - */ -int uvc_queue_allocated(struct uvc_video_queue *queue) -{ - int allocated; - - mutex_lock(&queue->mutex); - allocated = vb2_is_busy(&queue->queue); - mutex_unlock(&queue->mutex); - - return allocated; -} - -/* - * Enable or disable the video buffers queue. - * - * The queue must be enabled before starting video acquisition and must be - * disabled after stopping it. This ensures that the video buffers queue - * state can be properly initialized before buffers are accessed from the - * interrupt handler. - * - * Enabling the video queue returns -EBUSY if the queue is already enabled. - * - * Disabling the video queue cancels the queue and removes all buffers from - * the main queue. - * - * This function can't be called from interrupt context. Use - * uvc_queue_cancel() instead. - */ -int uvc_queue_enable(struct uvc_video_queue *queue, int enable) -{ - unsigned long flags; - int ret; - - mutex_lock(&queue->mutex); - if (enable) { - ret = vb2_streamon(&queue->queue, queue->queue.type); - if (ret < 0) - goto done; - - queue->buf_used = 0; - } else { - ret = vb2_streamoff(&queue->queue, queue->queue.type); - if (ret < 0) - goto done; - - spin_lock_irqsave(&queue->irqlock, flags); - INIT_LIST_HEAD(&queue->irqqueue); - spin_unlock_irqrestore(&queue->irqlock, flags); - } - -done: - mutex_unlock(&queue->mutex); - return ret; -} - -/* - * Cancel the video buffers queue. - * - * Cancelling the queue marks all buffers on the irq queue as erroneous, - * wakes them up and removes them from the queue. - * - * If the disconnect parameter is set, further calls to uvc_queue_buffer will - * fail with -ENODEV. - * - * This function acquires the irq spinlock and can be called from interrupt - * context. - */ -void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) -{ - struct uvc_buffer *buf; - unsigned long flags; - - spin_lock_irqsave(&queue->irqlock, flags); - while (!list_empty(&queue->irqqueue)) { - buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, - queue); - list_del(&buf->queue); - buf->state = UVC_BUF_STATE_ERROR; - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); - } - /* This must be protected by the irqlock spinlock to avoid race - * conditions between uvc_buffer_queue and the disconnection event that - * could result in an interruptible wait in uvc_dequeue_buffer. Do not - * blindly replace this logic by checking for the UVC_QUEUE_DISCONNECTED - * state outside the queue code. - */ - if (disconnect) - queue->flags |= UVC_QUEUE_DISCONNECTED; - spin_unlock_irqrestore(&queue->irqlock, flags); -} - -struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, - struct uvc_buffer *buf) -{ - struct uvc_buffer *nextbuf; - unsigned long flags; - - if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) { - buf->error = 0; - buf->state = UVC_BUF_STATE_QUEUED; - vb2_set_plane_payload(&buf->buf, 0, 0); - return buf; - } - - spin_lock_irqsave(&queue->irqlock, flags); - list_del(&buf->queue); - if (!list_empty(&queue->irqqueue)) - nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, - queue); - else - nextbuf = NULL; - spin_unlock_irqrestore(&queue->irqlock, flags); - - buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; - vb2_set_plane_payload(&buf->buf, 0, buf->bytesused); - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); - - return nextbuf; -} diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c deleted file mode 100644 index b7492775e6ae..000000000000 --- a/drivers/media/video/uvc/uvc_status.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * uvc_status.c -- USB Video Class driver - Status endpoint - * - * Copyright (C) 2005-2009 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include - -#include "uvcvideo.h" - -/* -------------------------------------------------------------------------- - * Input device - */ -#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV -static int uvc_input_init(struct uvc_device *dev) -{ - struct input_dev *input; - int ret; - - input = input_allocate_device(); - if (input == NULL) - return -ENOMEM; - - usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys)); - strlcat(dev->input_phys, "/button", sizeof(dev->input_phys)); - - input->name = dev->name; - input->phys = dev->input_phys; - usb_to_input_id(dev->udev, &input->id); - input->dev.parent = &dev->intf->dev; - - __set_bit(EV_KEY, input->evbit); - __set_bit(KEY_CAMERA, input->keybit); - - if ((ret = input_register_device(input)) < 0) - goto error; - - dev->input = input; - return 0; - -error: - input_free_device(input); - return ret; -} - -static void uvc_input_cleanup(struct uvc_device *dev) -{ - if (dev->input) - input_unregister_device(dev->input); -} - -static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, - int value) -{ - if (dev->input) { - input_report_key(dev->input, code, value); - input_sync(dev->input); - } -} - -#else -#define uvc_input_init(dev) -#define uvc_input_cleanup(dev) -#define uvc_input_report_key(dev, code, value) -#endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */ - -/* -------------------------------------------------------------------------- - * Status interrupt endpoint - */ -static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) -{ - if (len < 3) { - uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " - "received.\n"); - return; - } - - if (data[2] == 0) { - if (len < 4) - return; - uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", - data[1], data[3] ? "pressed" : "released", len); - uvc_input_report_key(dev, KEY_CAMERA, data[3]); - } else { - uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " - "len %d.\n", data[1], data[2], data[3], len); - } -} - -static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) -{ - char *attrs[3] = { "value", "info", "failure" }; - - if (len < 6 || data[2] != 0 || data[4] > 2) { - uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " - "received.\n"); - return; - } - - uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", - data[1], data[3], attrs[data[4]], len); -} - -static void uvc_status_complete(struct urb *urb) -{ - struct uvc_device *dev = urb->context; - int len, ret; - - switch (urb->status) { - case 0: - break; - - case -ENOENT: /* usb_kill_urb() called. */ - case -ECONNRESET: /* usb_unlink_urb() called. */ - case -ESHUTDOWN: /* The endpoint is being disabled. */ - case -EPROTO: /* Device is disconnected (reported by some - * host controller). */ - return; - - default: - uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " - "completion handler.\n", urb->status); - return; - } - - len = urb->actual_length; - if (len > 0) { - switch (dev->status[0] & 0x0f) { - case UVC_STATUS_TYPE_CONTROL: - uvc_event_control(dev, dev->status, len); - break; - - case UVC_STATUS_TYPE_STREAMING: - uvc_event_streaming(dev, dev->status, len); - break; - - default: - uvc_trace(UVC_TRACE_STATUS, "Unknown status event " - "type %u.\n", dev->status[0]); - break; - } - } - - /* Resubmit the URB. */ - urb->interval = dev->int_ep->desc.bInterval; - if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { - uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", - ret); - } -} - -int uvc_status_init(struct uvc_device *dev) -{ - struct usb_host_endpoint *ep = dev->int_ep; - unsigned int pipe; - int interval; - - if (ep == NULL) - return 0; - - uvc_input_init(dev); - - dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL); - if (dev->status == NULL) - return -ENOMEM; - - dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); - if (dev->int_urb == NULL) { - kfree(dev->status); - return -ENOMEM; - } - - pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); - - /* For high-speed interrupt endpoints, the bInterval value is used as - * an exponent of two. Some developers forgot about it. - */ - interval = ep->desc.bInterval; - if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH && - (dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) - interval = fls(interval) - 1; - - usb_fill_int_urb(dev->int_urb, dev->udev, pipe, - dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete, - dev, interval); - - return 0; -} - -void uvc_status_cleanup(struct uvc_device *dev) -{ - usb_kill_urb(dev->int_urb); - usb_free_urb(dev->int_urb); - kfree(dev->status); - uvc_input_cleanup(dev); -} - -int uvc_status_start(struct uvc_device *dev) -{ - if (dev->int_urb == NULL) - return 0; - - return usb_submit_urb(dev->int_urb, GFP_KERNEL); -} - -void uvc_status_stop(struct uvc_device *dev) -{ - usb_kill_urb(dev->int_urb); -} - -int uvc_status_suspend(struct uvc_device *dev) -{ - if (atomic_read(&dev->users)) - usb_kill_urb(dev->int_urb); - - return 0; -} - -int uvc_status_resume(struct uvc_device *dev) -{ - if (dev->int_urb == NULL || atomic_read(&dev->users) == 0) - return 0; - - return usb_submit_urb(dev->int_urb, GFP_NOIO); -} - diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c deleted file mode 100644 index f00db3060e0e..000000000000 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ /dev/null @@ -1,1317 +0,0 @@ -/* - * uvc_v4l2.c -- USB Video Class driver - V4L2 API - * - * Copyright (C) 2005-2010 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "uvcvideo.h" - -/* ------------------------------------------------------------------------ - * UVC ioctls - */ -static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, - struct uvc_xu_control_mapping *xmap) -{ - struct uvc_control_mapping *map; - unsigned int size; - int ret; - - map = kzalloc(sizeof *map, GFP_KERNEL); - if (map == NULL) - return -ENOMEM; - - map->id = xmap->id; - memcpy(map->name, xmap->name, sizeof map->name); - memcpy(map->entity, xmap->entity, sizeof map->entity); - map->selector = xmap->selector; - map->size = xmap->size; - map->offset = xmap->offset; - map->v4l2_type = xmap->v4l2_type; - map->data_type = xmap->data_type; - - switch (xmap->v4l2_type) { - case V4L2_CTRL_TYPE_INTEGER: - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_BUTTON: - break; - - case V4L2_CTRL_TYPE_MENU: - /* Prevent excessive memory consumption, as well as integer - * overflows. - */ - if (xmap->menu_count == 0 || - xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) { - ret = -EINVAL; - goto done; - } - - size = xmap->menu_count * sizeof(*map->menu_info); - map->menu_info = kmalloc(size, GFP_KERNEL); - if (map->menu_info == NULL) { - ret = -ENOMEM; - goto done; - } - - if (copy_from_user(map->menu_info, xmap->menu_info, size)) { - ret = -EFAULT; - goto done; - } - - map->menu_count = xmap->menu_count; - break; - - default: - uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type " - "%u.\n", xmap->v4l2_type); - ret = -ENOTTY; - goto done; - } - - ret = uvc_ctrl_add_mapping(chain, map); - -done: - kfree(map->menu_info); - kfree(map); - - return ret; -} - -/* ------------------------------------------------------------------------ - * V4L2 interface - */ - -/* - * Find the frame interval closest to the requested frame interval for the - * given frame format and size. This should be done by the device as part of - * the Video Probe and Commit negotiation, but some hardware don't implement - * that feature. - */ -static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) -{ - unsigned int i; - - if (frame->bFrameIntervalType) { - __u32 best = -1, dist; - - for (i = 0; i < frame->bFrameIntervalType; ++i) { - dist = interval > frame->dwFrameInterval[i] - ? interval - frame->dwFrameInterval[i] - : frame->dwFrameInterval[i] - interval; - - if (dist > best) - break; - - best = dist; - } - - interval = frame->dwFrameInterval[i-1]; - } else { - const __u32 min = frame->dwFrameInterval[0]; - const __u32 max = frame->dwFrameInterval[1]; - const __u32 step = frame->dwFrameInterval[2]; - - interval = min + (interval - min + step/2) / step * step; - if (interval > max) - interval = max; - } - - return interval; -} - -static int uvc_v4l2_try_format(struct uvc_streaming *stream, - struct v4l2_format *fmt, struct uvc_streaming_control *probe, - struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) -{ - struct uvc_format *format = NULL; - struct uvc_frame *frame = NULL; - __u16 rw, rh; - unsigned int d, maxd; - unsigned int i; - __u32 interval; - int ret = 0; - __u8 *fcc; - - if (fmt->type != stream->type) - return -EINVAL; - - fcc = (__u8 *)&fmt->fmt.pix.pixelformat; - uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", - fmt->fmt.pix.pixelformat, - fcc[0], fcc[1], fcc[2], fcc[3], - fmt->fmt.pix.width, fmt->fmt.pix.height); - - /* Check if the hardware supports the requested format. */ - for (i = 0; i < stream->nformats; ++i) { - format = &stream->format[i]; - if (format->fcc == fmt->fmt.pix.pixelformat) - break; - } - - if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) { - uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n", - fmt->fmt.pix.pixelformat); - return -EINVAL; - } - - /* Find the closest image size. The distance between image sizes is - * the size in pixels of the non-overlapping regions between the - * requested size and the frame-specified size. - */ - rw = fmt->fmt.pix.width; - rh = fmt->fmt.pix.height; - maxd = (unsigned int)-1; - - for (i = 0; i < format->nframes; ++i) { - __u16 w = format->frame[i].wWidth; - __u16 h = format->frame[i].wHeight; - - d = min(w, rw) * min(h, rh); - d = w*h + rw*rh - 2*d; - if (d < maxd) { - maxd = d; - frame = &format->frame[i]; - } - - if (maxd == 0) - break; - } - - if (frame == NULL) { - uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", - fmt->fmt.pix.width, fmt->fmt.pix.height); - return -EINVAL; - } - - /* Use the default frame interval. */ - interval = frame->dwDefaultFrameInterval; - uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " - "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, - (100000000/interval)%10); - - /* Set the format index, frame index and frame interval. */ - memset(probe, 0, sizeof *probe); - probe->bmHint = 1; /* dwFrameInterval */ - probe->bFormatIndex = format->index; - probe->bFrameIndex = frame->bFrameIndex; - probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); - /* Some webcams stall the probe control set request when the - * dwMaxVideoFrameSize field is set to zero. The UVC specification - * clearly states that the field is read-only from the host, so this - * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by - * the webcam to work around the problem. - * - * The workaround could probably be enabled for all webcams, so the - * quirk can be removed if needed. It's currently useful to detect - * webcam bugs and fix them before they hit the market (providing - * developers test their webcams with the Linux driver as well as with - * the Windows driver). - */ - mutex_lock(&stream->mutex); - if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) - probe->dwMaxVideoFrameSize = - stream->ctrl.dwMaxVideoFrameSize; - - /* Probe the device. */ - ret = uvc_probe_video(stream, probe); - mutex_unlock(&stream->mutex); - if (ret < 0) - goto done; - - fmt->fmt.pix.width = frame->wWidth; - fmt->fmt.pix.height = frame->wHeight; - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; - fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; - fmt->fmt.pix.colorspace = format->colorspace; - fmt->fmt.pix.priv = 0; - - if (uvc_format != NULL) - *uvc_format = format; - if (uvc_frame != NULL) - *uvc_frame = frame; - -done: - return ret; -} - -static int uvc_v4l2_get_format(struct uvc_streaming *stream, - struct v4l2_format *fmt) -{ - struct uvc_format *format; - struct uvc_frame *frame; - int ret = 0; - - if (fmt->type != stream->type) - return -EINVAL; - - mutex_lock(&stream->mutex); - format = stream->cur_format; - frame = stream->cur_frame; - - if (format == NULL || frame == NULL) { - ret = -EINVAL; - goto done; - } - - fmt->fmt.pix.pixelformat = format->fcc; - fmt->fmt.pix.width = frame->wWidth; - fmt->fmt.pix.height = frame->wHeight; - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; - fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; - fmt->fmt.pix.colorspace = format->colorspace; - fmt->fmt.pix.priv = 0; - -done: - mutex_unlock(&stream->mutex); - return ret; -} - -static int uvc_v4l2_set_format(struct uvc_streaming *stream, - struct v4l2_format *fmt) -{ - struct uvc_streaming_control probe; - struct uvc_format *format; - struct uvc_frame *frame; - int ret; - - if (fmt->type != stream->type) - return -EINVAL; - - ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame); - if (ret < 0) - return ret; - - mutex_lock(&stream->mutex); - - if (uvc_queue_allocated(&stream->queue)) { - ret = -EBUSY; - goto done; - } - - memcpy(&stream->ctrl, &probe, sizeof probe); - stream->cur_format = format; - stream->cur_frame = frame; - -done: - mutex_unlock(&stream->mutex); - return ret; -} - -static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, - struct v4l2_streamparm *parm) -{ - uint32_t numerator, denominator; - - if (parm->type != stream->type) - return -EINVAL; - - mutex_lock(&stream->mutex); - numerator = stream->ctrl.dwFrameInterval; - mutex_unlock(&stream->mutex); - - denominator = 10000000; - uvc_simplify_fraction(&numerator, &denominator, 8, 333); - - memset(parm, 0, sizeof *parm); - parm->type = stream->type; - - if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.capturemode = 0; - parm->parm.capture.timeperframe.numerator = numerator; - parm->parm.capture.timeperframe.denominator = denominator; - parm->parm.capture.extendedmode = 0; - parm->parm.capture.readbuffers = 0; - } else { - parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - parm->parm.output.outputmode = 0; - parm->parm.output.timeperframe.numerator = numerator; - parm->parm.output.timeperframe.denominator = denominator; - } - - return 0; -} - -static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, - struct v4l2_streamparm *parm) -{ - struct uvc_streaming_control probe; - struct v4l2_fract timeperframe; - uint32_t interval; - int ret; - - if (parm->type != stream->type) - return -EINVAL; - - if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - timeperframe = parm->parm.capture.timeperframe; - else - timeperframe = parm->parm.output.timeperframe; - - interval = uvc_fraction_to_interval(timeperframe.numerator, - timeperframe.denominator); - uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", - timeperframe.numerator, timeperframe.denominator, interval); - - mutex_lock(&stream->mutex); - - if (uvc_queue_streaming(&stream->queue)) { - mutex_unlock(&stream->mutex); - return -EBUSY; - } - - memcpy(&probe, &stream->ctrl, sizeof probe); - probe.dwFrameInterval = - uvc_try_frame_interval(stream->cur_frame, interval); - - /* Probe the device with the new settings. */ - ret = uvc_probe_video(stream, &probe); - if (ret < 0) { - mutex_unlock(&stream->mutex); - return ret; - } - - memcpy(&stream->ctrl, &probe, sizeof probe); - mutex_unlock(&stream->mutex); - - /* Return the actual frame period. */ - timeperframe.numerator = probe.dwFrameInterval; - timeperframe.denominator = 10000000; - uvc_simplify_fraction(&timeperframe.numerator, - &timeperframe.denominator, 8, 333); - - if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - parm->parm.capture.timeperframe = timeperframe; - else - parm->parm.output.timeperframe = timeperframe; - - return 0; -} - -/* ------------------------------------------------------------------------ - * Privilege management - */ - -/* - * Privilege management is the multiple-open implementation basis. The current - * implementation is completely transparent for the end-user and doesn't - * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls. - * Those ioctls enable finer control on the device (by making possible for a - * user to request exclusive access to a device), but are not mature yet. - * Switching to the V4L2 priority mechanism might be considered in the future - * if this situation changes. - * - * Each open instance of a UVC device can either be in a privileged or - * unprivileged state. Only a single instance can be in a privileged state at - * a given time. Trying to perform an operation that requires privileges will - * automatically acquire the required privileges if possible, or return -EBUSY - * otherwise. Privileges are dismissed when closing the instance or when - * freeing the video buffers using VIDIOC_REQBUFS. - * - * Operations that require privileges are: - * - * - VIDIOC_S_INPUT - * - VIDIOC_S_PARM - * - VIDIOC_S_FMT - * - VIDIOC_REQBUFS - */ -static int uvc_acquire_privileges(struct uvc_fh *handle) -{ - /* Always succeed if the handle is already privileged. */ - if (handle->state == UVC_HANDLE_ACTIVE) - return 0; - - /* Check if the device already has a privileged handle. */ - if (atomic_inc_return(&handle->stream->active) != 1) { - atomic_dec(&handle->stream->active); - return -EBUSY; - } - - handle->state = UVC_HANDLE_ACTIVE; - return 0; -} - -static void uvc_dismiss_privileges(struct uvc_fh *handle) -{ - if (handle->state == UVC_HANDLE_ACTIVE) - atomic_dec(&handle->stream->active); - - handle->state = UVC_HANDLE_PASSIVE; -} - -static int uvc_has_privileges(struct uvc_fh *handle) -{ - return handle->state == UVC_HANDLE_ACTIVE; -} - -/* ------------------------------------------------------------------------ - * V4L2 file operations - */ - -static int uvc_v4l2_open(struct file *file) -{ - struct uvc_streaming *stream; - struct uvc_fh *handle; - int ret = 0; - - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); - stream = video_drvdata(file); - - if (stream->dev->state & UVC_DEV_DISCONNECTED) - return -ENODEV; - - ret = usb_autopm_get_interface(stream->dev->intf); - if (ret < 0) - return ret; - - /* Create the device handle. */ - handle = kzalloc(sizeof *handle, GFP_KERNEL); - if (handle == NULL) { - usb_autopm_put_interface(stream->dev->intf); - return -ENOMEM; - } - - if (atomic_inc_return(&stream->dev->users) == 1) { - ret = uvc_status_start(stream->dev); - if (ret < 0) { - usb_autopm_put_interface(stream->dev->intf); - atomic_dec(&stream->dev->users); - kfree(handle); - return ret; - } - } - - v4l2_fh_init(&handle->vfh, stream->vdev); - v4l2_fh_add(&handle->vfh); - handle->chain = stream->chain; - handle->stream = stream; - handle->state = UVC_HANDLE_PASSIVE; - file->private_data = handle; - - return 0; -} - -static int uvc_v4l2_release(struct file *file) -{ - struct uvc_fh *handle = file->private_data; - struct uvc_streaming *stream = handle->stream; - - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); - - /* Only free resources if this is a privileged handle. */ - if (uvc_has_privileges(handle)) { - uvc_video_enable(stream, 0); - uvc_free_buffers(&stream->queue); - } - - /* Release the file handle. */ - uvc_dismiss_privileges(handle); - v4l2_fh_del(&handle->vfh); - v4l2_fh_exit(&handle->vfh); - kfree(handle); - file->private_data = NULL; - - if (atomic_dec_return(&stream->dev->users) == 0) - uvc_status_stop(stream->dev); - - usb_autopm_put_interface(stream->dev->intf); - return 0; -} - -static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *vdev = video_devdata(file); - struct uvc_fh *handle = file->private_data; - struct uvc_video_chain *chain = handle->chain; - struct uvc_streaming *stream = handle->stream; - long ret = 0; - - switch (cmd) { - /* Query capabilities */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap, 0, sizeof *cap); - strlcpy(cap->driver, "uvcvideo", sizeof cap->driver); - strlcpy(cap->card, vdev->name, sizeof cap->card); - usb_make_path(stream->dev->udev, - cap->bus_info, sizeof(cap->bus_info)); - cap->version = LINUX_VERSION_CODE; - if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_STREAMING; - else - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; - break; - } - - /* Get, Set & Query control */ - case VIDIOC_QUERYCTRL: - return uvc_query_v4l2_ctrl(chain, arg); - - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - struct v4l2_ext_control xctrl; - - memset(&xctrl, 0, sizeof xctrl); - xctrl.id = ctrl->id; - - ret = uvc_ctrl_begin(chain); - if (ret < 0) - return ret; - - ret = uvc_ctrl_get(chain, &xctrl); - uvc_ctrl_rollback(handle); - if (ret >= 0) - ctrl->value = xctrl.value; - break; - } - - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - struct v4l2_ext_control xctrl; - - memset(&xctrl, 0, sizeof xctrl); - xctrl.id = ctrl->id; - xctrl.value = ctrl->value; - - ret = uvc_ctrl_begin(chain); - if (ret < 0) - return ret; - - ret = uvc_ctrl_set(chain, &xctrl); - if (ret < 0) { - uvc_ctrl_rollback(handle); - return ret; - } - ret = uvc_ctrl_commit(handle, &xctrl, 1); - if (ret == 0) - ctrl->value = xctrl.value; - break; - } - - case VIDIOC_QUERYMENU: - return uvc_query_v4l2_menu(chain, arg); - - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *ctrls = arg; - struct v4l2_ext_control *ctrl = ctrls->controls; - unsigned int i; - - ret = uvc_ctrl_begin(chain); - if (ret < 0) - return ret; - - for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_get(chain, ctrl); - if (ret < 0) { - uvc_ctrl_rollback(handle); - ctrls->error_idx = i; - return ret; - } - } - ctrls->error_idx = 0; - ret = uvc_ctrl_rollback(handle); - break; - } - - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *ctrls = arg; - struct v4l2_ext_control *ctrl = ctrls->controls; - unsigned int i; - - ret = uvc_ctrl_begin(chain); - if (ret < 0) - return ret; - - for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_set(chain, ctrl); - if (ret < 0) { - uvc_ctrl_rollback(handle); - ctrls->error_idx = i; - return ret; - } - } - - ctrls->error_idx = 0; - - if (cmd == VIDIOC_S_EXT_CTRLS) - ret = uvc_ctrl_commit(handle, - ctrls->controls, ctrls->count); - else - ret = uvc_ctrl_rollback(handle); - break; - } - - /* Get, Set & Enum input */ - case VIDIOC_ENUMINPUT: - { - const struct uvc_entity *selector = chain->selector; - struct v4l2_input *input = arg; - struct uvc_entity *iterm = NULL; - u32 index = input->index; - int pin = 0; - - if (selector == NULL || - (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { - if (index != 0) - return -EINVAL; - list_for_each_entry(iterm, &chain->entities, chain) { - if (UVC_ENTITY_IS_ITERM(iterm)) - break; - } - pin = iterm->id; - } else if (index < selector->bNrInPins) { - pin = selector->baSourceID[index]; - list_for_each_entry(iterm, &chain->entities, chain) { - if (!UVC_ENTITY_IS_ITERM(iterm)) - continue; - if (iterm->id == pin) - break; - } - } - - if (iterm == NULL || iterm->id != pin) - return -EINVAL; - - memset(input, 0, sizeof *input); - input->index = index; - strlcpy(input->name, iterm->name, sizeof input->name); - if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA) - input->type = V4L2_INPUT_TYPE_CAMERA; - break; - } - - case VIDIOC_G_INPUT: - { - u8 input; - - if (chain->selector == NULL || - (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { - *(int *)arg = 0; - break; - } - - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, - chain->selector->id, chain->dev->intfnum, - UVC_SU_INPUT_SELECT_CONTROL, &input, 1); - if (ret < 0) - return ret; - - *(int *)arg = input - 1; - break; - } - - case VIDIOC_S_INPUT: - { - u32 input = *(u32 *)arg + 1; - - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - - if (chain->selector == NULL || - (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { - if (input != 1) - return -EINVAL; - break; - } - - if (input == 0 || input > chain->selector->bNrInPins) - return -EINVAL; - - return uvc_query_ctrl(chain->dev, UVC_SET_CUR, - chain->selector->id, chain->dev->intfnum, - UVC_SU_INPUT_SELECT_CONTROL, &input, 1); - } - - /* Try, Get, Set & Enum format */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *fmt = arg; - struct uvc_format *format; - enum v4l2_buf_type type = fmt->type; - __u32 index = fmt->index; - - if (fmt->type != stream->type || - fmt->index >= stream->nformats) - return -EINVAL; - - memset(fmt, 0, sizeof(*fmt)); - fmt->index = index; - fmt->type = type; - - format = &stream->format[fmt->index]; - fmt->flags = 0; - if (format->flags & UVC_FMT_FLAG_COMPRESSED) - fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - strlcpy(fmt->description, format->name, - sizeof fmt->description); - fmt->description[sizeof fmt->description - 1] = 0; - fmt->pixelformat = format->fcc; - break; - } - - case VIDIOC_TRY_FMT: - { - struct uvc_streaming_control probe; - - return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); - } - - case VIDIOC_S_FMT: - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - - return uvc_v4l2_set_format(stream, arg); - - case VIDIOC_G_FMT: - return uvc_v4l2_get_format(stream, arg); - - /* Frame size enumeration */ - case VIDIOC_ENUM_FRAMESIZES: - { - struct v4l2_frmsizeenum *fsize = arg; - struct uvc_format *format = NULL; - struct uvc_frame *frame; - int i; - - /* Look for the given pixel format */ - for (i = 0; i < stream->nformats; i++) { - if (stream->format[i].fcc == - fsize->pixel_format) { - format = &stream->format[i]; - break; - } - } - if (format == NULL) - return -EINVAL; - - if (fsize->index >= format->nframes) - return -EINVAL; - - frame = &format->frame[fsize->index]; - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = frame->wWidth; - fsize->discrete.height = frame->wHeight; - break; - } - - /* Frame interval enumeration */ - case VIDIOC_ENUM_FRAMEINTERVALS: - { - struct v4l2_frmivalenum *fival = arg; - struct uvc_format *format = NULL; - struct uvc_frame *frame = NULL; - int i; - - /* Look for the given pixel format and frame size */ - for (i = 0; i < stream->nformats; i++) { - if (stream->format[i].fcc == - fival->pixel_format) { - format = &stream->format[i]; - break; - } - } - if (format == NULL) - return -EINVAL; - - for (i = 0; i < format->nframes; i++) { - if (format->frame[i].wWidth == fival->width && - format->frame[i].wHeight == fival->height) { - frame = &format->frame[i]; - break; - } - } - if (frame == NULL) - return -EINVAL; - - if (frame->bFrameIntervalType) { - if (fival->index >= frame->bFrameIntervalType) - return -EINVAL; - - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete.numerator = - frame->dwFrameInterval[fival->index]; - fival->discrete.denominator = 10000000; - uvc_simplify_fraction(&fival->discrete.numerator, - &fival->discrete.denominator, 8, 333); - } else { - fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; - fival->stepwise.min.numerator = - frame->dwFrameInterval[0]; - fival->stepwise.min.denominator = 10000000; - fival->stepwise.max.numerator = - frame->dwFrameInterval[1]; - fival->stepwise.max.denominator = 10000000; - fival->stepwise.step.numerator = - frame->dwFrameInterval[2]; - fival->stepwise.step.denominator = 10000000; - uvc_simplify_fraction(&fival->stepwise.min.numerator, - &fival->stepwise.min.denominator, 8, 333); - uvc_simplify_fraction(&fival->stepwise.max.numerator, - &fival->stepwise.max.denominator, 8, 333); - uvc_simplify_fraction(&fival->stepwise.step.numerator, - &fival->stepwise.step.denominator, 8, 333); - } - break; - } - - /* Get & Set streaming parameters */ - case VIDIOC_G_PARM: - return uvc_v4l2_get_streamparm(stream, arg); - - case VIDIOC_S_PARM: - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - - return uvc_v4l2_set_streamparm(stream, arg); - - /* Cropping and scaling */ - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *ccap = arg; - - if (ccap->type != stream->type) - return -EINVAL; - - ccap->bounds.left = 0; - ccap->bounds.top = 0; - - mutex_lock(&stream->mutex); - ccap->bounds.width = stream->cur_frame->wWidth; - ccap->bounds.height = stream->cur_frame->wHeight; - mutex_unlock(&stream->mutex); - - ccap->defrect = ccap->bounds; - - ccap->pixelaspect.numerator = 1; - ccap->pixelaspect.denominator = 1; - break; - } - - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: - return -EINVAL; - - /* Buffers & streaming */ - case VIDIOC_REQBUFS: - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - - mutex_lock(&stream->mutex); - ret = uvc_alloc_buffers(&stream->queue, arg); - mutex_unlock(&stream->mutex); - if (ret < 0) - return ret; - - if (ret == 0) - uvc_dismiss_privileges(handle); - - ret = 0; - break; - - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; - - if (!uvc_has_privileges(handle)) - return -EBUSY; - - return uvc_query_buffer(&stream->queue, buf); - } - - case VIDIOC_QBUF: - if (!uvc_has_privileges(handle)) - return -EBUSY; - - return uvc_queue_buffer(&stream->queue, arg); - - case VIDIOC_DQBUF: - if (!uvc_has_privileges(handle)) - return -EBUSY; - - return uvc_dequeue_buffer(&stream->queue, arg, - file->f_flags & O_NONBLOCK); - - case VIDIOC_STREAMON: - { - int *type = arg; - - if (*type != stream->type) - return -EINVAL; - - if (!uvc_has_privileges(handle)) - return -EBUSY; - - mutex_lock(&stream->mutex); - ret = uvc_video_enable(stream, 1); - mutex_unlock(&stream->mutex); - if (ret < 0) - return ret; - break; - } - - case VIDIOC_STREAMOFF: - { - int *type = arg; - - if (*type != stream->type) - return -EINVAL; - - if (!uvc_has_privileges(handle)) - return -EBUSY; - - return uvc_video_enable(stream, 0); - } - - case VIDIOC_SUBSCRIBE_EVENT: - { - struct v4l2_event_subscription *sub = arg; - - switch (sub->type) { - case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(&handle->vfh, sub, 0, - &uvc_ctrl_sub_ev_ops); - default: - return -EINVAL; - } - } - - case VIDIOC_UNSUBSCRIBE_EVENT: - return v4l2_event_unsubscribe(&handle->vfh, arg); - - case VIDIOC_DQEVENT: - return v4l2_event_dequeue(&handle->vfh, arg, - file->f_flags & O_NONBLOCK); - - /* Analog video standards make no sense for digital cameras. */ - case VIDIOC_ENUMSTD: - case VIDIOC_QUERYSTD: - case VIDIOC_G_STD: - case VIDIOC_S_STD: - - case VIDIOC_OVERLAY: - - case VIDIOC_ENUMAUDIO: - case VIDIOC_ENUMAUDOUT: - - case VIDIOC_ENUMOUTPUT: - uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); - return -EINVAL; - - case UVCIOC_CTRL_MAP: - return uvc_ioctl_ctrl_map(chain, arg); - - case UVCIOC_CTRL_QUERY: - return uvc_xu_ctrl_query(chain, arg); - - default: - uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); - return -ENOTTY; - } - - return ret; -} - -static long uvc_v4l2_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - if (uvc_trace_param & UVC_TRACE_IOCTL) { - uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); - v4l_printk_ioctl(NULL, cmd); - printk(")\n"); - } - - return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); -} - -#ifdef CONFIG_COMPAT -struct uvc_xu_control_mapping32 { - __u32 id; - __u8 name[32]; - __u8 entity[16]; - __u8 selector; - - __u8 size; - __u8 offset; - __u32 v4l2_type; - __u32 data_type; - - compat_caddr_t menu_info; - __u32 menu_count; - - __u32 reserved[4]; -}; - -static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, - const struct uvc_xu_control_mapping32 __user *up) -{ - struct uvc_menu_info __user *umenus; - struct uvc_menu_info __user *kmenus; - compat_caddr_t p; - - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) || - __get_user(kp->menu_count, &up->menu_count)) - return -EFAULT; - - memset(kp->reserved, 0, sizeof(kp->reserved)); - - if (kp->menu_count == 0) { - kp->menu_info = NULL; - return 0; - } - - if (__get_user(p, &up->menu_info)) - return -EFAULT; - umenus = compat_ptr(p); - if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus))) - return -EFAULT; - - kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus)); - if (kmenus == NULL) - return -EFAULT; - kp->menu_info = kmenus; - - if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus))) - return -EFAULT; - - return 0; -} - -static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, - struct uvc_xu_control_mapping32 __user *up) -{ - struct uvc_menu_info __user *umenus; - struct uvc_menu_info __user *kmenus = kp->menu_info; - compat_caddr_t p; - - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) || - __put_user(kp->menu_count, &up->menu_count)) - return -EFAULT; - - if (__clear_user(up->reserved, sizeof(up->reserved))) - return -EFAULT; - - if (kp->menu_count == 0) - return 0; - - if (get_user(p, &up->menu_info)) - return -EFAULT; - umenus = compat_ptr(p); - - if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) - return -EFAULT; - - return 0; -} - -struct uvc_xu_control_query32 { - __u8 unit; - __u8 selector; - __u8 query; - __u16 size; - compat_caddr_t data; -}; - -static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp, - const struct uvc_xu_control_query32 __user *up) -{ - u8 __user *udata; - u8 __user *kdata; - compat_caddr_t p; - - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - __copy_from_user(kp, up, offsetof(typeof(*up), data))) - return -EFAULT; - - if (kp->size == 0) { - kp->data = NULL; - return 0; - } - - if (__get_user(p, &up->data)) - return -EFAULT; - udata = compat_ptr(p); - if (!access_ok(VERIFY_READ, udata, kp->size)) - return -EFAULT; - - kdata = compat_alloc_user_space(kp->size); - if (kdata == NULL) - return -EFAULT; - kp->data = kdata; - - if (copy_in_user(kdata, udata, kp->size)) - return -EFAULT; - - return 0; -} - -static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, - struct uvc_xu_control_query32 __user *up) -{ - u8 __user *udata; - u8 __user *kdata = kp->data; - compat_caddr_t p; - - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - __copy_to_user(up, kp, offsetof(typeof(*up), data))) - return -EFAULT; - - if (kp->size == 0) - return 0; - - if (get_user(p, &up->data)) - return -EFAULT; - udata = compat_ptr(p); - if (!access_ok(VERIFY_READ, udata, kp->size)) - return -EFAULT; - - if (copy_in_user(udata, kdata, kp->size)) - return -EFAULT; - - return 0; -} - -#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) -#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) - -static long uvc_v4l2_compat_ioctl32(struct file *file, - unsigned int cmd, unsigned long arg) -{ - union { - struct uvc_xu_control_mapping xmap; - struct uvc_xu_control_query xqry; - } karg; - void __user *up = compat_ptr(arg); - mm_segment_t old_fs; - long ret; - - switch (cmd) { - case UVCIOC_CTRL_MAP32: - cmd = UVCIOC_CTRL_MAP; - ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); - break; - - case UVCIOC_CTRL_QUERY32: - cmd = UVCIOC_CTRL_QUERY; - ret = uvc_v4l2_get_xu_query(&karg.xqry, up); - break; - - default: - return -ENOIOCTLCMD; - } - - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg); - set_fs(old_fs); - - if (ret < 0) - return ret; - - switch (cmd) { - case UVCIOC_CTRL_MAP: - ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); - break; - - case UVCIOC_CTRL_QUERY: - ret = uvc_v4l2_put_xu_query(&karg.xqry, up); - break; - } - - return ret; -} -#endif - -static ssize_t uvc_v4l2_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); - return -EINVAL; -} - -static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct uvc_fh *handle = file->private_data; - struct uvc_streaming *stream = handle->stream; - - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); - - return uvc_queue_mmap(&stream->queue, vma); -} - -static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) -{ - struct uvc_fh *handle = file->private_data; - struct uvc_streaming *stream = handle->stream; - - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); - - return uvc_queue_poll(&stream->queue, file, wait); -} - -#ifndef CONFIG_MMU -static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, - unsigned long addr, unsigned long len, unsigned long pgoff, - unsigned long flags) -{ - struct uvc_fh *handle = file->private_data; - struct uvc_streaming *stream = handle->stream; - - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n"); - - return uvc_queue_get_unmapped_area(&stream->queue, pgoff); -} -#endif - -const struct v4l2_file_operations uvc_fops = { - .owner = THIS_MODULE, - .open = uvc_v4l2_open, - .release = uvc_v4l2_release, - .unlocked_ioctl = uvc_v4l2_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = uvc_v4l2_compat_ioctl32, -#endif - .read = uvc_v4l2_read, - .mmap = uvc_v4l2_mmap, - .poll = uvc_v4l2_poll, -#ifndef CONFIG_MMU - .get_unmapped_area = uvc_v4l2_get_unmapped_area, -#endif -}; - diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c deleted file mode 100644 index 1c15b4227bdb..000000000000 --- a/drivers/media/video/uvc/uvc_video.c +++ /dev/null @@ -1,1879 +0,0 @@ -/* - * uvc_video.c -- USB Video Class driver - Video handling - * - * Copyright (C) 2005-2010 - * Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "uvcvideo.h" - -/* ------------------------------------------------------------------------ - * UVC Controls - */ - -static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size, - int timeout) -{ - __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; - unsigned int pipe; - - pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) - : usb_sndctrlpipe(dev->udev, 0); - type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; - - return usb_control_msg(dev->udev, pipe, query, type, cs << 8, - unit << 8 | intfnum, data, size, timeout); -} - -static const char *uvc_query_name(__u8 query) -{ - switch (query) { - case UVC_SET_CUR: - return "SET_CUR"; - case UVC_GET_CUR: - return "GET_CUR"; - case UVC_GET_MIN: - return "GET_MIN"; - case UVC_GET_MAX: - return "GET_MAX"; - case UVC_GET_RES: - return "GET_RES"; - case UVC_GET_LEN: - return "GET_LEN"; - case UVC_GET_INFO: - return "GET_INFO"; - case UVC_GET_DEF: - return "GET_DEF"; - default: - return ""; - } -} - -int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size) -{ - int ret; - - ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, - UVC_CTRL_CONTROL_TIMEOUT); - if (ret != size) { - uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on " - "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, - unit, ret, size); - return -EIO; - } - - return 0; -} - -static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, - struct uvc_streaming_control *ctrl) -{ - struct uvc_format *format = NULL; - struct uvc_frame *frame = NULL; - unsigned int i; - - for (i = 0; i < stream->nformats; ++i) { - if (stream->format[i].index == ctrl->bFormatIndex) { - format = &stream->format[i]; - break; - } - } - - if (format == NULL) - return; - - for (i = 0; i < format->nframes; ++i) { - if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { - frame = &format->frame[i]; - break; - } - } - - if (frame == NULL) - return; - - if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || - (ctrl->dwMaxVideoFrameSize == 0 && - stream->dev->uvc_version < 0x0110)) - ctrl->dwMaxVideoFrameSize = - frame->dwMaxVideoFrameBufferSize; - - if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) && - stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && - stream->intf->num_altsetting > 1) { - u32 interval; - u32 bandwidth; - - interval = (ctrl->dwFrameInterval > 100000) - ? ctrl->dwFrameInterval - : frame->dwFrameInterval[0]; - - /* Compute a bandwidth estimation by multiplying the frame - * size by the number of video frames per second, divide the - * result by the number of USB frames (or micro-frames for - * high-speed devices) per second and add the UVC header size - * (assumed to be 12 bytes long). - */ - bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; - bandwidth *= 10000000 / interval + 1; - bandwidth /= 1000; - if (stream->dev->udev->speed == USB_SPEED_HIGH) - bandwidth /= 8; - bandwidth += 12; - - /* The bandwidth estimate is too low for many cameras. Don't use - * maximum packet sizes lower than 1024 bytes to try and work - * around the problem. According to measurements done on two - * different camera models, the value is high enough to get most - * resolutions working while not preventing two simultaneous - * VGA streams at 15 fps. - */ - bandwidth = max_t(u32, bandwidth, 1024); - - ctrl->dwMaxPayloadTransferSize = bandwidth; - } -} - -static int uvc_get_video_ctrl(struct uvc_streaming *stream, - struct uvc_streaming_control *ctrl, int probe, __u8 query) -{ - __u8 *data; - __u16 size; - int ret; - - size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; - if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && - query == UVC_GET_DEF) - return -EIO; - - data = kmalloc(size, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, - probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, uvc_timeout_param); - - if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { - /* Some cameras, mostly based on Bison Electronics chipsets, - * answer a GET_MIN or GET_MAX request with the wCompQuality - * field only. - */ - uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non " - "compliance - GET_MIN/MAX(PROBE) incorrectly " - "supported. Enabling workaround.\n"); - memset(ctrl, 0, sizeof *ctrl); - ctrl->wCompQuality = le16_to_cpup((__le16 *)data); - ret = 0; - goto out; - } else if (query == UVC_GET_DEF && probe == 1 && ret != size) { - /* Many cameras don't support the GET_DEF request on their - * video probe control. Warn once and return, the caller will - * fall back to GET_CUR. - */ - uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non " - "compliance - GET_DEF(PROBE) not supported. " - "Enabling workaround.\n"); - ret = -EIO; - goto out; - } else if (ret != size) { - uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : " - "%d (exp. %u).\n", query, probe ? "probe" : "commit", - ret, size); - ret = -EIO; - goto out; - } - - ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); - ctrl->bFormatIndex = data[2]; - ctrl->bFrameIndex = data[3]; - ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]); - ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]); - ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]); - ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); - ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); - ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); - ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]); - ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]); - - if (size == 34) { - ctrl->dwClockFrequency = get_unaligned_le32(&data[26]); - ctrl->bmFramingInfo = data[30]; - ctrl->bPreferedVersion = data[31]; - ctrl->bMinVersion = data[32]; - ctrl->bMaxVersion = data[33]; - } else { - ctrl->dwClockFrequency = stream->dev->clock_frequency; - ctrl->bmFramingInfo = 0; - ctrl->bPreferedVersion = 0; - ctrl->bMinVersion = 0; - ctrl->bMaxVersion = 0; - } - - /* Some broken devices return null or wrong dwMaxVideoFrameSize and - * dwMaxPayloadTransferSize fields. Try to get the value from the - * format and frame descriptors. - */ - uvc_fixup_video_ctrl(stream, ctrl); - ret = 0; - -out: - kfree(data); - return ret; -} - -static int uvc_set_video_ctrl(struct uvc_streaming *stream, - struct uvc_streaming_control *ctrl, int probe) -{ - __u8 *data; - __u16 size; - int ret; - - size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; - data = kzalloc(size, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint); - data[2] = ctrl->bFormatIndex; - data[3] = ctrl->bFrameIndex; - *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval); - *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate); - *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate); - *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality); - *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize); - *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay); - put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]); - put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]); - - if (size == 34) { - put_unaligned_le32(ctrl->dwClockFrequency, &data[26]); - data[30] = ctrl->bmFramingInfo; - data[31] = ctrl->bPreferedVersion; - data[32] = ctrl->bMinVersion; - data[33] = ctrl->bMaxVersion; - } - - ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, - probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, uvc_timeout_param); - if (ret != size) { - uvc_printk(KERN_ERR, "Failed to set UVC %s control : " - "%d (exp. %u).\n", probe ? "probe" : "commit", - ret, size); - ret = -EIO; - } - - kfree(data); - return ret; -} - -int uvc_probe_video(struct uvc_streaming *stream, - struct uvc_streaming_control *probe) -{ - struct uvc_streaming_control probe_min, probe_max; - __u16 bandwidth; - unsigned int i; - int ret; - - /* Perform probing. The device should adjust the requested values - * according to its capabilities. However, some devices, namely the - * first generation UVC Logitech webcams, don't implement the Video - * Probe control properly, and just return the needed bandwidth. For - * that reason, if the needed bandwidth exceeds the maximum available - * bandwidth, try to lower the quality. - */ - ret = uvc_set_video_ctrl(stream, probe, 1); - if (ret < 0) - goto done; - - /* Get the minimum and maximum values for compression settings. */ - if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { - ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN); - if (ret < 0) - goto done; - ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX); - if (ret < 0) - goto done; - - probe->wCompQuality = probe_max.wCompQuality; - } - - for (i = 0; i < 2; ++i) { - ret = uvc_set_video_ctrl(stream, probe, 1); - if (ret < 0) - goto done; - ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); - if (ret < 0) - goto done; - - if (stream->intf->num_altsetting == 1) - break; - - bandwidth = probe->dwMaxPayloadTransferSize; - if (bandwidth <= stream->maxpsize) - break; - - if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { - ret = -ENOSPC; - goto done; - } - - /* TODO: negotiate compression parameters */ - probe->wKeyFrameRate = probe_min.wKeyFrameRate; - probe->wPFrameRate = probe_min.wPFrameRate; - probe->wCompQuality = probe_max.wCompQuality; - probe->wCompWindowSize = probe_min.wCompWindowSize; - } - -done: - return ret; -} - -static int uvc_commit_video(struct uvc_streaming *stream, - struct uvc_streaming_control *probe) -{ - return uvc_set_video_ctrl(stream, probe, 0); -} - -/* ----------------------------------------------------------------------------- - * Clocks and timestamps - */ - -static void -uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, - const __u8 *data, int len) -{ - struct uvc_clock_sample *sample; - unsigned int header_size; - bool has_pts = false; - bool has_scr = false; - unsigned long flags; - struct timespec ts; - u16 host_sof; - u16 dev_sof; - - switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { - case UVC_STREAM_PTS | UVC_STREAM_SCR: - header_size = 12; - has_pts = true; - has_scr = true; - break; - case UVC_STREAM_PTS: - header_size = 6; - has_pts = true; - break; - case UVC_STREAM_SCR: - header_size = 8; - has_scr = true; - break; - default: - header_size = 2; - break; - } - - /* Check for invalid headers. */ - if (len < header_size) - return; - - /* Extract the timestamps: - * - * - store the frame PTS in the buffer structure - * - if the SCR field is present, retrieve the host SOF counter and - * kernel timestamps and store them with the SCR STC and SOF fields - * in the ring buffer - */ - if (has_pts && buf != NULL) - buf->pts = get_unaligned_le32(&data[2]); - - if (!has_scr) - return; - - /* To limit the amount of data, drop SCRs with an SOF identical to the - * previous one. - */ - dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (dev_sof == stream->clock.last_sof) - return; - - stream->clock.last_sof = dev_sof; - - host_sof = usb_get_current_frame_number(stream->dev->udev); - ktime_get_ts(&ts); - - /* The UVC specification allows device implementations that can't obtain - * the USB frame number to keep their own frame counters as long as they - * match the size and frequency of the frame number associated with USB - * SOF tokens. The SOF values sent by such devices differ from the USB - * SOF tokens by a fixed offset that needs to be estimated and accounted - * for to make timestamp recovery as accurate as possible. - * - * The offset is estimated the first time a device SOF value is received - * as the difference between the host and device SOF values. As the two - * SOF values can differ slightly due to transmission delays, consider - * that the offset is null if the difference is not higher than 10 ms - * (negative differences can not happen and are thus considered as an - * offset). The video commit control wDelay field should be used to - * compute a dynamic threshold instead of using a fixed 10 ms value, but - * devices don't report reliable wDelay values. - * - * See uvc_video_clock_host_sof() for an explanation regarding why only - * the 8 LSBs of the delta are kept. - */ - if (stream->clock.sof_offset == (u16)-1) { - u16 delta_sof = (host_sof - dev_sof) & 255; - if (delta_sof >= 10) - stream->clock.sof_offset = delta_sof; - else - stream->clock.sof_offset = 0; - } - - dev_sof = (dev_sof + stream->clock.sof_offset) & 2047; - - spin_lock_irqsave(&stream->clock.lock, flags); - - sample = &stream->clock.samples[stream->clock.head]; - sample->dev_stc = get_unaligned_le32(&data[header_size - 6]); - sample->dev_sof = dev_sof; - sample->host_sof = host_sof; - sample->host_ts = ts; - - /* Update the sliding window head and count. */ - stream->clock.head = (stream->clock.head + 1) % stream->clock.size; - - if (stream->clock.count < stream->clock.size) - stream->clock.count++; - - spin_unlock_irqrestore(&stream->clock.lock, flags); -} - -static void uvc_video_clock_reset(struct uvc_streaming *stream) -{ - struct uvc_clock *clock = &stream->clock; - - clock->head = 0; - clock->count = 0; - clock->last_sof = -1; - clock->sof_offset = -1; -} - -static int uvc_video_clock_init(struct uvc_streaming *stream) -{ - struct uvc_clock *clock = &stream->clock; - - spin_lock_init(&clock->lock); - clock->size = 32; - - clock->samples = kmalloc(clock->size * sizeof(*clock->samples), - GFP_KERNEL); - if (clock->samples == NULL) - return -ENOMEM; - - uvc_video_clock_reset(stream); - - return 0; -} - -static void uvc_video_clock_cleanup(struct uvc_streaming *stream) -{ - kfree(stream->clock.samples); - stream->clock.samples = NULL; -} - -/* - * uvc_video_clock_host_sof - Return the host SOF value for a clock sample - * - * Host SOF counters reported by usb_get_current_frame_number() usually don't - * cover the whole 11-bits SOF range (0-2047) but are limited to the HCI frame - * schedule window. They can be limited to 8, 9 or 10 bits depending on the host - * controller and its configuration. - * - * We thus need to recover the SOF value corresponding to the host frame number. - * As the device and host frame numbers are sampled in a short interval, the - * difference between their values should be equal to a small delta plus an - * integer multiple of 256 caused by the host frame number limited precision. - * - * To obtain the recovered host SOF value, compute the small delta by masking - * the high bits of the host frame counter and device SOF difference and add it - * to the device SOF value. - */ -static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample) -{ - /* The delta value can be negative. */ - s8 delta_sof; - - delta_sof = (sample->host_sof - sample->dev_sof) & 255; - - return (sample->dev_sof + delta_sof) & 2047; -} - -/* - * uvc_video_clock_update - Update the buffer timestamp - * - * This function converts the buffer PTS timestamp to the host clock domain by - * going through the USB SOF clock domain and stores the result in the V4L2 - * buffer timestamp field. - * - * The relationship between the device clock and the host clock isn't known. - * However, the device and the host share the common USB SOF clock which can be - * used to recover that relationship. - * - * The relationship between the device clock and the USB SOF clock is considered - * to be linear over the clock samples sliding window and is given by - * - * SOF = m * PTS + p - * - * Several methods to compute the slope (m) and intercept (p) can be used. As - * the clock drift should be small compared to the sliding window size, we - * assume that the line that goes through the points at both ends of the window - * is a good approximation. Naming those points P1 and P2, we get - * - * SOF = (SOF2 - SOF1) / (STC2 - STC1) * PTS - * + (SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) - * - * or - * - * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1) - * - * to avoid loosing precision in the division. Similarly, the host timestamp is - * computed with - * - * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) - * - * SOF values are coded on 11 bits by USB. We extend their precision with 16 - * decimal bits, leading to a 11.16 coding. - * - * TODO: To avoid surprises with device clock values, PTS/STC timestamps should - * be normalized using the nominal device clock frequency reported through the - * UVC descriptors. - * - * Both the PTS/STC and SOF counters roll over, after a fixed but device - * specific amount of time for PTS/STC and after 2048ms for SOF. As long as the - * sliding window size is smaller than the rollover period, differences computed - * on unsigned integers will produce the correct result. However, the p term in - * the linear relations will be miscomputed. - * - * To fix the issue, we subtract a constant from the PTS and STC values to bring - * PTS to half the 32 bit STC range. The sliding window STC values then fit into - * the 32 bit range without any rollover. - * - * Similarly, we add 2048 to the device SOF values to make sure that the SOF - * computed by (1) will never be smaller than 0. This offset is then compensated - * by adding 2048 to the SOF values used in (2). However, this doesn't prevent - * rollovers between (1) and (2): the SOF value computed by (1) can be slightly - * lower than 4096, and the host SOF counters can have rolled over to 2048. This - * case is handled by subtracting 2048 from the SOF value if it exceeds the host - * SOF value at the end of the sliding window. - * - * Finally we subtract a constant from the host timestamps to bring the first - * timestamp of the sliding window to 1s. - */ -void uvc_video_clock_update(struct uvc_streaming *stream, - struct v4l2_buffer *v4l2_buf, - struct uvc_buffer *buf) -{ - struct uvc_clock *clock = &stream->clock; - struct uvc_clock_sample *first; - struct uvc_clock_sample *last; - unsigned long flags; - struct timespec ts; - u32 delta_stc; - u32 y1, y2; - u32 x1, x2; - u32 mean; - u32 sof; - u32 div; - u32 rem; - u64 y; - - spin_lock_irqsave(&clock->lock, flags); - - if (clock->count < clock->size) - goto done; - - first = &clock->samples[clock->head]; - last = &clock->samples[(clock->head - 1) % clock->size]; - - /* First step, PTS to SOF conversion. */ - delta_stc = buf->pts - (1UL << 31); - x1 = first->dev_stc - delta_stc; - x2 = last->dev_stc - delta_stc; - if (x1 == x2) - goto done; - - y1 = (first->dev_sof + 2048) << 16; - y2 = (last->dev_sof + 2048) << 16; - if (y2 < y1) - y2 += 2048 << 16; - - y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 - - (u64)y2 * (u64)x1; - y = div_u64(y, x2 - x1); - - sof = y; - - uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu " - "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", - stream->dev->name, buf->pts, - y >> 16, div_u64((y & 0xffff) * 1000000, 65536), - sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), - x1, x2, y1, y2, clock->sof_offset); - - /* Second step, SOF to host clock conversion. */ - x1 = (uvc_video_clock_host_sof(first) + 2048) << 16; - x2 = (uvc_video_clock_host_sof(last) + 2048) << 16; - if (x2 < x1) - x2 += 2048 << 16; - if (x1 == x2) - goto done; - - ts = timespec_sub(last->host_ts, first->host_ts); - y1 = NSEC_PER_SEC; - y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec; - - /* Interpolated and host SOF timestamps can wrap around at slightly - * different times. Handle this by adding or removing 2048 to or from - * the computed SOF value to keep it close to the SOF samples mean - * value. - */ - mean = (x1 + x2) / 2; - if (mean - (1024 << 16) > sof) - sof += 2048 << 16; - else if (sof > mean + (1024 << 16)) - sof -= 2048 << 16; - - y = (u64)(y2 - y1) * (u64)sof + (u64)y1 * (u64)x2 - - (u64)y2 * (u64)x1; - y = div_u64(y, x2 - x1); - - div = div_u64_rem(y, NSEC_PER_SEC, &rem); - ts.tv_sec = first->host_ts.tv_sec - 1 + div; - ts.tv_nsec = first->host_ts.tv_nsec + rem; - if (ts.tv_nsec >= NSEC_PER_SEC) { - ts.tv_sec++; - ts.tv_nsec -= NSEC_PER_SEC; - } - - uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu " - "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", - stream->dev->name, - sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), - y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC, - v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec, - x1, first->host_sof, first->dev_sof, - x2, last->host_sof, last->dev_sof, y1, y2); - - /* Update the V4L2 buffer. */ - v4l2_buf->timestamp.tv_sec = ts.tv_sec; - v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; - -done: - spin_unlock_irqrestore(&stream->clock.lock, flags); -} - -/* ------------------------------------------------------------------------ - * Stream statistics - */ - -static void uvc_video_stats_decode(struct uvc_streaming *stream, - const __u8 *data, int len) -{ - unsigned int header_size; - bool has_pts = false; - bool has_scr = false; - u16 uninitialized_var(scr_sof); - u32 uninitialized_var(scr_stc); - u32 uninitialized_var(pts); - - if (stream->stats.stream.nb_frames == 0 && - stream->stats.frame.nb_packets == 0) - ktime_get_ts(&stream->stats.stream.start_ts); - - switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { - case UVC_STREAM_PTS | UVC_STREAM_SCR: - header_size = 12; - has_pts = true; - has_scr = true; - break; - case UVC_STREAM_PTS: - header_size = 6; - has_pts = true; - break; - case UVC_STREAM_SCR: - header_size = 8; - has_scr = true; - break; - default: - header_size = 2; - break; - } - - /* Check for invalid headers. */ - if (len < header_size || data[0] < header_size) { - stream->stats.frame.nb_invalid++; - return; - } - - /* Extract the timestamps. */ - if (has_pts) - pts = get_unaligned_le32(&data[2]); - - if (has_scr) { - scr_stc = get_unaligned_le32(&data[header_size - 6]); - scr_sof = get_unaligned_le16(&data[header_size - 2]); - } - - /* Is PTS constant through the whole frame ? */ - if (has_pts && stream->stats.frame.nb_pts) { - if (stream->stats.frame.pts != pts) { - stream->stats.frame.nb_pts_diffs++; - stream->stats.frame.last_pts_diff = - stream->stats.frame.nb_packets; - } - } - - if (has_pts) { - stream->stats.frame.nb_pts++; - stream->stats.frame.pts = pts; - } - - /* Do all frames have a PTS in their first non-empty packet, or before - * their first empty packet ? - */ - if (stream->stats.frame.size == 0) { - if (len > header_size) - stream->stats.frame.has_initial_pts = has_pts; - if (len == header_size && has_pts) - stream->stats.frame.has_early_pts = true; - } - - /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */ - if (has_scr && stream->stats.frame.nb_scr) { - if (stream->stats.frame.scr_stc != scr_stc) - stream->stats.frame.nb_scr_diffs++; - } - - if (has_scr) { - /* Expand the SOF counter to 32 bits and store its value. */ - if (stream->stats.stream.nb_frames > 0 || - stream->stats.frame.nb_scr > 0) - stream->stats.stream.scr_sof_count += - (scr_sof - stream->stats.stream.scr_sof) % 2048; - stream->stats.stream.scr_sof = scr_sof; - - stream->stats.frame.nb_scr++; - stream->stats.frame.scr_stc = scr_stc; - stream->stats.frame.scr_sof = scr_sof; - - if (scr_sof < stream->stats.stream.min_sof) - stream->stats.stream.min_sof = scr_sof; - if (scr_sof > stream->stats.stream.max_sof) - stream->stats.stream.max_sof = scr_sof; - } - - /* Record the first non-empty packet number. */ - if (stream->stats.frame.size == 0 && len > header_size) - stream->stats.frame.first_data = stream->stats.frame.nb_packets; - - /* Update the frame size. */ - stream->stats.frame.size += len - header_size; - - /* Update the packets counters. */ - stream->stats.frame.nb_packets++; - if (len > header_size) - stream->stats.frame.nb_empty++; - - if (data[1] & UVC_STREAM_ERR) - stream->stats.frame.nb_errors++; -} - -static void uvc_video_stats_update(struct uvc_streaming *stream) -{ - struct uvc_stats_frame *frame = &stream->stats.frame; - - uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, " - "%u/%u/%u pts (%searly %sinitial), %u/%u scr, " - "last pts/stc/sof %u/%u/%u\n", - stream->sequence, frame->first_data, - frame->nb_packets - frame->nb_empty, frame->nb_packets, - frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts, - frame->has_early_pts ? "" : "!", - frame->has_initial_pts ? "" : "!", - frame->nb_scr_diffs, frame->nb_scr, - frame->pts, frame->scr_stc, frame->scr_sof); - - stream->stats.stream.nb_frames++; - stream->stats.stream.nb_packets += stream->stats.frame.nb_packets; - stream->stats.stream.nb_empty += stream->stats.frame.nb_empty; - stream->stats.stream.nb_errors += stream->stats.frame.nb_errors; - stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid; - - if (frame->has_early_pts) - stream->stats.stream.nb_pts_early++; - if (frame->has_initial_pts) - stream->stats.stream.nb_pts_initial++; - if (frame->last_pts_diff <= frame->first_data) - stream->stats.stream.nb_pts_constant++; - if (frame->nb_scr >= frame->nb_packets - frame->nb_empty) - stream->stats.stream.nb_scr_count_ok++; - if (frame->nb_scr_diffs + 1 == frame->nb_scr) - stream->stats.stream.nb_scr_diffs_ok++; - - memset(&stream->stats.frame, 0, sizeof(stream->stats.frame)); -} - -size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, - size_t size) -{ - unsigned int scr_sof_freq; - unsigned int duration; - struct timespec ts; - size_t count = 0; - - ts.tv_sec = stream->stats.stream.stop_ts.tv_sec - - stream->stats.stream.start_ts.tv_sec; - ts.tv_nsec = stream->stats.stream.stop_ts.tv_nsec - - stream->stats.stream.start_ts.tv_nsec; - if (ts.tv_nsec < 0) { - ts.tv_sec--; - ts.tv_nsec += 1000000000; - } - - /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF - * frequency this will not overflow before more than 1h. - */ - duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; - if (duration != 0) - scr_sof_freq = stream->stats.stream.scr_sof_count * 1000 - / duration; - else - scr_sof_freq = 0; - - count += scnprintf(buf + count, size - count, - "frames: %u\npackets: %u\nempty: %u\n" - "errors: %u\ninvalid: %u\n", - stream->stats.stream.nb_frames, - stream->stats.stream.nb_packets, - stream->stats.stream.nb_empty, - stream->stats.stream.nb_errors, - stream->stats.stream.nb_invalid); - count += scnprintf(buf + count, size - count, - "pts: %u early, %u initial, %u ok\n", - stream->stats.stream.nb_pts_early, - stream->stats.stream.nb_pts_initial, - stream->stats.stream.nb_pts_constant); - count += scnprintf(buf + count, size - count, - "scr: %u count ok, %u diff ok\n", - stream->stats.stream.nb_scr_count_ok, - stream->stats.stream.nb_scr_diffs_ok); - count += scnprintf(buf + count, size - count, - "sof: %u <= sof <= %u, freq %u.%03u kHz\n", - stream->stats.stream.min_sof, - stream->stats.stream.max_sof, - scr_sof_freq / 1000, scr_sof_freq % 1000); - - return count; -} - -static void uvc_video_stats_start(struct uvc_streaming *stream) -{ - memset(&stream->stats, 0, sizeof(stream->stats)); - stream->stats.stream.min_sof = 2048; -} - -static void uvc_video_stats_stop(struct uvc_streaming *stream) -{ - ktime_get_ts(&stream->stats.stream.stop_ts); -} - -/* ------------------------------------------------------------------------ - * Video codecs - */ - -/* Video payload decoding is handled by uvc_video_decode_start(), - * uvc_video_decode_data() and uvc_video_decode_end(). - * - * uvc_video_decode_start is called with URB data at the start of a bulk or - * isochronous payload. It processes header data and returns the header size - * in bytes if successful. If an error occurs, it returns a negative error - * code. The following error codes have special meanings. - * - * - EAGAIN informs the caller that the current video buffer should be marked - * as done, and that the function should be called again with the same data - * and a new video buffer. This is used when end of frame conditions can be - * reliably detected at the beginning of the next frame only. - * - * If an error other than -EAGAIN is returned, the caller will drop the current - * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be - * made until the next payload. -ENODATA can be used to drop the current - * payload if no other error code is appropriate. - * - * uvc_video_decode_data is called for every URB with URB data. It copies the - * data to the video buffer. - * - * uvc_video_decode_end is called with header data at the end of a bulk or - * isochronous payload. It performs any additional header data processing and - * returns 0 or a negative error code if an error occurred. As header data have - * already been processed by uvc_video_decode_start, this functions isn't - * required to perform sanity checks a second time. - * - * For isochronous transfers where a payload is always transferred in a single - * URB, the three functions will be called in a row. - * - * To let the decoder process header data and update its internal state even - * when no video buffer is available, uvc_video_decode_start must be prepared - * to be called with a NULL buf parameter. uvc_video_decode_data and - * uvc_video_decode_end will never be called with a NULL buffer. - */ -static int uvc_video_decode_start(struct uvc_streaming *stream, - struct uvc_buffer *buf, const __u8 *data, int len) -{ - __u8 fid; - - /* Sanity checks: - * - packet must be at least 2 bytes long - * - bHeaderLength value must be at least 2 bytes (see above) - * - bHeaderLength value can't be larger than the packet size. - */ - if (len < 2 || data[0] < 2 || data[0] > len) { - stream->stats.frame.nb_invalid++; - return -EINVAL; - } - - fid = data[1] & UVC_STREAM_FID; - - /* Increase the sequence number regardless of any buffer states, so - * that discontinuous sequence numbers always indicate lost frames. - */ - if (stream->last_fid != fid) { - stream->sequence++; - if (stream->sequence) - uvc_video_stats_update(stream); - } - - uvc_video_clock_decode(stream, buf, data, len); - uvc_video_stats_decode(stream, data, len); - - /* Store the payload FID bit and return immediately when the buffer is - * NULL. - */ - if (buf == NULL) { - stream->last_fid = fid; - return -ENODATA; - } - - /* Mark the buffer as bad if the error bit is set. */ - if (data[1] & UVC_STREAM_ERR) { - uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit " - "set).\n"); - buf->error = 1; - } - - /* Synchronize to the input stream by waiting for the FID bit to be - * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. - * stream->last_fid is initialized to -1, so the first isochronous - * frame will always be in sync. - * - * If the device doesn't toggle the FID bit, invert stream->last_fid - * when the EOF bit is set to force synchronisation on the next packet. - */ - if (buf->state != UVC_BUF_STATE_ACTIVE) { - struct timespec ts; - - if (fid == stream->last_fid) { - uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " - "sync).\n"); - if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && - (data[1] & UVC_STREAM_EOF)) - stream->last_fid ^= UVC_STREAM_FID; - return -ENODATA; - } - - if (uvc_clock_param == CLOCK_MONOTONIC) - ktime_get_ts(&ts); - else - ktime_get_real_ts(&ts); - - buf->buf.v4l2_buf.sequence = stream->sequence; - buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec; - buf->buf.v4l2_buf.timestamp.tv_usec = - ts.tv_nsec / NSEC_PER_USEC; - - /* TODO: Handle PTS and SCR. */ - buf->state = UVC_BUF_STATE_ACTIVE; - } - - /* Mark the buffer as done if we're at the beginning of a new frame. - * End of frame detection is better implemented by checking the EOF - * bit (FID bit toggling is delayed by one frame compared to the EOF - * bit), but some devices don't set the bit at end of frame (and the - * last payload can be lost anyway). We thus must check if the FID has - * been toggled. - * - * stream->last_fid is initialized to -1, so the first isochronous - * frame will never trigger an end of frame detection. - * - * Empty buffers (bytesused == 0) don't trigger end of frame detection - * as it doesn't make sense to return an empty buffer. This also - * avoids detecting end of frame conditions at FID toggling if the - * previous payload had the EOF bit set. - */ - if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " - "toggled).\n"); - buf->state = UVC_BUF_STATE_READY; - return -EAGAIN; - } - - stream->last_fid = fid; - - return data[0]; -} - -static void uvc_video_decode_data(struct uvc_streaming *stream, - struct uvc_buffer *buf, const __u8 *data, int len) -{ - unsigned int maxlen, nbytes; - void *mem; - - if (len <= 0) - return; - - /* Copy the video data to the buffer. */ - maxlen = buf->length - buf->bytesused; - mem = buf->mem + buf->bytesused; - nbytes = min((unsigned int)len, maxlen); - memcpy(mem, data, nbytes); - buf->bytesused += nbytes; - - /* Complete the current frame if the buffer size was exceeded. */ - if (len > maxlen) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n"); - buf->state = UVC_BUF_STATE_READY; - } -} - -static void uvc_video_decode_end(struct uvc_streaming *stream, - struct uvc_buffer *buf, const __u8 *data, int len) -{ - /* Mark the buffer as done if the EOF marker is set. */ - if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); - if (data[0] == len) - uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); - buf->state = UVC_BUF_STATE_READY; - if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) - stream->last_fid ^= UVC_STREAM_FID; - } -} - -/* Video payload encoding is handled by uvc_video_encode_header() and - * uvc_video_encode_data(). Only bulk transfers are currently supported. - * - * uvc_video_encode_header is called at the start of a payload. It adds header - * data to the transfer buffer and returns the header size. As the only known - * UVC output device transfers a whole frame in a single payload, the EOF bit - * is always set in the header. - * - * uvc_video_encode_data is called for every URB and copies the data from the - * video buffer to the transfer buffer. - */ -static int uvc_video_encode_header(struct uvc_streaming *stream, - struct uvc_buffer *buf, __u8 *data, int len) -{ - data[0] = 2; /* Header length */ - data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF - | (stream->last_fid & UVC_STREAM_FID); - return 2; -} - -static int uvc_video_encode_data(struct uvc_streaming *stream, - struct uvc_buffer *buf, __u8 *data, int len) -{ - struct uvc_video_queue *queue = &stream->queue; - unsigned int nbytes; - void *mem; - - /* Copy video data to the URB buffer. */ - mem = buf->mem + queue->buf_used; - nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); - nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size, - nbytes); - memcpy(data, mem, nbytes); - - queue->buf_used += nbytes; - - return nbytes; -} - -/* ------------------------------------------------------------------------ - * URB handling - */ - -/* - * Completion handler for video URBs. - */ -static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, - struct uvc_buffer *buf) -{ - u8 *mem; - int ret, i; - - for (i = 0; i < urb->number_of_packets; ++i) { - if (urb->iso_frame_desc[i].status < 0) { - uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " - "lost (%d).\n", urb->iso_frame_desc[i].status); - /* Mark the buffer as faulty. */ - if (buf != NULL) - buf->error = 1; - continue; - } - - /* Decode the payload header. */ - mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - do { - ret = uvc_video_decode_start(stream, buf, mem, - urb->iso_frame_desc[i].actual_length); - if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&stream->queue, - buf); - } while (ret == -EAGAIN); - - if (ret < 0) - continue; - - /* Decode the payload data. */ - uvc_video_decode_data(stream, buf, mem + ret, - urb->iso_frame_desc[i].actual_length - ret); - - /* Process the header again. */ - uvc_video_decode_end(stream, buf, mem, - urb->iso_frame_desc[i].actual_length); - - if (buf->state == UVC_BUF_STATE_READY) { - if (buf->length != buf->bytesused && - !(stream->cur_format->flags & - UVC_FMT_FLAG_COMPRESSED)) - buf->error = 1; - - buf = uvc_queue_next_buffer(&stream->queue, buf); - } - } -} - -static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream, - struct uvc_buffer *buf) -{ - u8 *mem; - int len, ret; - - /* - * Ignore ZLPs if they're not part of a frame, otherwise process them - * to trigger the end of payload detection. - */ - if (urb->actual_length == 0 && stream->bulk.header_size == 0) - return; - - mem = urb->transfer_buffer; - len = urb->actual_length; - stream->bulk.payload_size += len; - - /* If the URB is the first of its payload, decode and save the - * header. - */ - if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { - do { - ret = uvc_video_decode_start(stream, buf, mem, len); - if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&stream->queue, - buf); - } while (ret == -EAGAIN); - - /* If an error occurred skip the rest of the payload. */ - if (ret < 0 || buf == NULL) { - stream->bulk.skip_payload = 1; - } else { - memcpy(stream->bulk.header, mem, ret); - stream->bulk.header_size = ret; - - mem += ret; - len -= ret; - } - } - - /* The buffer queue might have been cancelled while a bulk transfer - * was in progress, so we can reach here with buf equal to NULL. Make - * sure buf is never dereferenced if NULL. - */ - - /* Process video data. */ - if (!stream->bulk.skip_payload && buf != NULL) - uvc_video_decode_data(stream, buf, mem, len); - - /* Detect the payload end by a URB smaller than the maximum size (or - * a payload size equal to the maximum) and process the header again. - */ - if (urb->actual_length < urb->transfer_buffer_length || - stream->bulk.payload_size >= stream->bulk.max_payload_size) { - if (!stream->bulk.skip_payload && buf != NULL) { - uvc_video_decode_end(stream, buf, stream->bulk.header, - stream->bulk.payload_size); - if (buf->state == UVC_BUF_STATE_READY) - buf = uvc_queue_next_buffer(&stream->queue, - buf); - } - - stream->bulk.header_size = 0; - stream->bulk.skip_payload = 0; - stream->bulk.payload_size = 0; - } -} - -static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream, - struct uvc_buffer *buf) -{ - u8 *mem = urb->transfer_buffer; - int len = stream->urb_size, ret; - - if (buf == NULL) { - urb->transfer_buffer_length = 0; - return; - } - - /* If the URB is the first of its payload, add the header. */ - if (stream->bulk.header_size == 0) { - ret = uvc_video_encode_header(stream, buf, mem, len); - stream->bulk.header_size = ret; - stream->bulk.payload_size += ret; - mem += ret; - len -= ret; - } - - /* Process video data. */ - ret = uvc_video_encode_data(stream, buf, mem, len); - - stream->bulk.payload_size += ret; - len -= ret; - - if (buf->bytesused == stream->queue.buf_used || - stream->bulk.payload_size == stream->bulk.max_payload_size) { - if (buf->bytesused == stream->queue.buf_used) { - stream->queue.buf_used = 0; - buf->state = UVC_BUF_STATE_READY; - buf->buf.v4l2_buf.sequence = ++stream->sequence; - uvc_queue_next_buffer(&stream->queue, buf); - stream->last_fid ^= UVC_STREAM_FID; - } - - stream->bulk.header_size = 0; - stream->bulk.payload_size = 0; - } - - urb->transfer_buffer_length = stream->urb_size - len; -} - -static void uvc_video_complete(struct urb *urb) -{ - struct uvc_streaming *stream = urb->context; - struct uvc_video_queue *queue = &stream->queue; - struct uvc_buffer *buf = NULL; - unsigned long flags; - int ret; - - switch (urb->status) { - case 0: - break; - - default: - uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " - "completion handler.\n", urb->status); - - case -ENOENT: /* usb_kill_urb() called. */ - if (stream->frozen) - return; - - case -ECONNRESET: /* usb_unlink_urb() called. */ - case -ESHUTDOWN: /* The endpoint is being disabled. */ - uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); - return; - } - - spin_lock_irqsave(&queue->irqlock, flags); - if (!list_empty(&queue->irqqueue)) - buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, - queue); - spin_unlock_irqrestore(&queue->irqlock, flags); - - stream->decode(urb, stream, buf); - - if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { - uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", - ret); - } -} - -/* - * Free transfer buffers. - */ -static void uvc_free_urb_buffers(struct uvc_streaming *stream) -{ - unsigned int i; - - for (i = 0; i < UVC_URBS; ++i) { - if (stream->urb_buffer[i]) { -#ifndef CONFIG_DMA_NONCOHERENT - usb_free_coherent(stream->dev->udev, stream->urb_size, - stream->urb_buffer[i], stream->urb_dma[i]); -#else - kfree(stream->urb_buffer[i]); -#endif - stream->urb_buffer[i] = NULL; - } - } - - stream->urb_size = 0; -} - -/* - * Allocate transfer buffers. This function can be called with buffers - * already allocated when resuming from suspend, in which case it will - * return without touching the buffers. - * - * Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the - * system is too low on memory try successively smaller numbers of packets - * until allocation succeeds. - * - * Return the number of allocated packets on success or 0 when out of memory. - */ -static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, - unsigned int size, unsigned int psize, gfp_t gfp_flags) -{ - unsigned int npackets; - unsigned int i; - - /* Buffers are already allocated, bail out. */ - if (stream->urb_size) - return stream->urb_size / psize; - - /* Compute the number of packets. Bulk endpoints might transfer UVC - * payloads across multiple URBs. - */ - npackets = DIV_ROUND_UP(size, psize); - if (npackets > UVC_MAX_PACKETS) - npackets = UVC_MAX_PACKETS; - - /* Retry allocations until one succeed. */ - for (; npackets > 1; npackets /= 2) { - for (i = 0; i < UVC_URBS; ++i) { - stream->urb_size = psize * npackets; -#ifndef CONFIG_DMA_NONCOHERENT - stream->urb_buffer[i] = usb_alloc_coherent( - stream->dev->udev, stream->urb_size, - gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); -#else - stream->urb_buffer[i] = - kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN); -#endif - if (!stream->urb_buffer[i]) { - uvc_free_urb_buffers(stream); - break; - } - } - - if (i == UVC_URBS) { - uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " - "of %ux%u bytes each.\n", UVC_URBS, npackets, - psize); - return npackets; - } - } - - uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes " - "per packet).\n", psize); - return 0; -} - -/* - * Uninitialize isochronous/bulk URBs and free transfer buffers. - */ -static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) -{ - struct urb *urb; - unsigned int i; - - uvc_video_stats_stop(stream); - - for (i = 0; i < UVC_URBS; ++i) { - urb = stream->urb[i]; - if (urb == NULL) - continue; - - usb_kill_urb(urb); - usb_free_urb(urb); - stream->urb[i] = NULL; - } - - if (free_buffers) - uvc_free_urb_buffers(stream); -} - -/* - * Compute the maximum number of bytes per interval for an endpoint. - */ -static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev, - struct usb_host_endpoint *ep) -{ - u16 psize; - - switch (dev->speed) { - case USB_SPEED_SUPER: - return ep->ss_ep_comp.wBytesPerInterval; - case USB_SPEED_HIGH: - psize = usb_endpoint_maxp(&ep->desc); - return (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - default: - psize = usb_endpoint_maxp(&ep->desc); - return psize & 0x07ff; - } -} - -/* - * Initialize isochronous URBs and allocate transfer buffers. The packet size - * is given by the endpoint. - */ -static int uvc_init_video_isoc(struct uvc_streaming *stream, - struct usb_host_endpoint *ep, gfp_t gfp_flags) -{ - struct urb *urb; - unsigned int npackets, i, j; - u16 psize; - u32 size; - - psize = uvc_endpoint_max_bpi(stream->dev->udev, ep); - size = stream->ctrl.dwMaxVideoFrameSize; - - npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); - if (npackets == 0) - return -ENOMEM; - - size = npackets * psize; - - for (i = 0; i < UVC_URBS; ++i) { - urb = usb_alloc_urb(npackets, gfp_flags); - if (urb == NULL) { - uvc_uninit_video(stream, 1); - return -ENOMEM; - } - - urb->dev = stream->dev->udev; - urb->context = stream; - urb->pipe = usb_rcvisocpipe(stream->dev->udev, - ep->desc.bEndpointAddress); -#ifndef CONFIG_DMA_NONCOHERENT - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->transfer_dma = stream->urb_dma[i]; -#else - urb->transfer_flags = URB_ISO_ASAP; -#endif - urb->interval = ep->desc.bInterval; - urb->transfer_buffer = stream->urb_buffer[i]; - urb->complete = uvc_video_complete; - urb->number_of_packets = npackets; - urb->transfer_buffer_length = size; - - for (j = 0; j < npackets; ++j) { - urb->iso_frame_desc[j].offset = j * psize; - urb->iso_frame_desc[j].length = psize; - } - - stream->urb[i] = urb; - } - - return 0; -} - -/* - * Initialize bulk URBs and allocate transfer buffers. The packet size is - * given by the endpoint. - */ -static int uvc_init_video_bulk(struct uvc_streaming *stream, - struct usb_host_endpoint *ep, gfp_t gfp_flags) -{ - struct urb *urb; - unsigned int npackets, pipe, i; - u16 psize; - u32 size; - - psize = usb_endpoint_maxp(&ep->desc) & 0x7ff; - size = stream->ctrl.dwMaxPayloadTransferSize; - stream->bulk.max_payload_size = size; - - npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); - if (npackets == 0) - return -ENOMEM; - - size = npackets * psize; - - if (usb_endpoint_dir_in(&ep->desc)) - pipe = usb_rcvbulkpipe(stream->dev->udev, - ep->desc.bEndpointAddress); - else - pipe = usb_sndbulkpipe(stream->dev->udev, - ep->desc.bEndpointAddress); - - if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - size = 0; - - for (i = 0; i < UVC_URBS; ++i) { - urb = usb_alloc_urb(0, gfp_flags); - if (urb == NULL) { - uvc_uninit_video(stream, 1); - return -ENOMEM; - } - - usb_fill_bulk_urb(urb, stream->dev->udev, pipe, - stream->urb_buffer[i], size, uvc_video_complete, - stream); -#ifndef CONFIG_DMA_NONCOHERENT - urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - urb->transfer_dma = stream->urb_dma[i]; -#endif - - stream->urb[i] = urb; - } - - return 0; -} - -/* - * Initialize isochronous/bulk URBs and allocate transfer buffers. - */ -static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) -{ - struct usb_interface *intf = stream->intf; - struct usb_host_endpoint *ep; - unsigned int i; - int ret; - - stream->sequence = -1; - stream->last_fid = -1; - stream->bulk.header_size = 0; - stream->bulk.skip_payload = 0; - stream->bulk.payload_size = 0; - - uvc_video_stats_start(stream); - - if (intf->num_altsetting > 1) { - struct usb_host_endpoint *best_ep = NULL; - unsigned int best_psize = UINT_MAX; - unsigned int bandwidth; - unsigned int uninitialized_var(altsetting); - int intfnum = stream->intfnum; - - /* Isochronous endpoint, select the alternate setting. */ - bandwidth = stream->ctrl.dwMaxPayloadTransferSize; - - if (bandwidth == 0) { - uvc_trace(UVC_TRACE_VIDEO, "Device requested null " - "bandwidth, defaulting to lowest.\n"); - bandwidth = 1; - } else { - uvc_trace(UVC_TRACE_VIDEO, "Device requested %u " - "B/frame bandwidth.\n", bandwidth); - } - - for (i = 0; i < intf->num_altsetting; ++i) { - struct usb_host_interface *alts; - unsigned int psize; - - alts = &intf->altsetting[i]; - ep = uvc_find_endpoint(alts, - stream->header.bEndpointAddress); - if (ep == NULL) - continue; - - /* Check if the bandwidth is high enough. */ - psize = uvc_endpoint_max_bpi(stream->dev->udev, ep); - if (psize >= bandwidth && psize <= best_psize) { - altsetting = alts->desc.bAlternateSetting; - best_psize = psize; - best_ep = ep; - } - } - - if (best_ep == NULL) { - uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " - "for requested bandwidth.\n"); - return -EIO; - } - - uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u " - "(%u B/frame bandwidth).\n", altsetting, best_psize); - - ret = usb_set_interface(stream->dev->udev, intfnum, altsetting); - if (ret < 0) - return ret; - - ret = uvc_init_video_isoc(stream, best_ep, gfp_flags); - } else { - /* Bulk endpoint, proceed to URB initialization. */ - ep = uvc_find_endpoint(&intf->altsetting[0], - stream->header.bEndpointAddress); - if (ep == NULL) - return -EIO; - - ret = uvc_init_video_bulk(stream, ep, gfp_flags); - } - - if (ret < 0) - return ret; - - /* Submit the URBs. */ - for (i = 0; i < UVC_URBS; ++i) { - ret = usb_submit_urb(stream->urb[i], gfp_flags); - if (ret < 0) { - uvc_printk(KERN_ERR, "Failed to submit URB %u " - "(%d).\n", i, ret); - uvc_uninit_video(stream, 1); - return ret; - } - } - - return 0; -} - -/* -------------------------------------------------------------------------- - * Suspend/resume - */ - -/* - * Stop streaming without disabling the video queue. - * - * To let userspace applications resume without trouble, we must not touch the - * video buffers in any way. We mark the device as frozen to make sure the URB - * completion handler won't try to cancel the queue when we kill the URBs. - */ -int uvc_video_suspend(struct uvc_streaming *stream) -{ - if (!uvc_queue_streaming(&stream->queue)) - return 0; - - stream->frozen = 1; - uvc_uninit_video(stream, 0); - usb_set_interface(stream->dev->udev, stream->intfnum, 0); - return 0; -} - -/* - * Reconfigure the video interface and restart streaming if it was enabled - * before suspend. - * - * If an error occurs, disable the video queue. This will wake all pending - * buffers, making sure userspace applications are notified of the problem - * instead of waiting forever. - */ -int uvc_video_resume(struct uvc_streaming *stream, int reset) -{ - int ret; - - /* If the bus has been reset on resume, set the alternate setting to 0. - * This should be the default value, but some devices crash or otherwise - * misbehave if they don't receive a SET_INTERFACE request before any - * other video control request. - */ - if (reset) - usb_set_interface(stream->dev->udev, stream->intfnum, 0); - - stream->frozen = 0; - - uvc_video_clock_reset(stream); - - ret = uvc_commit_video(stream, &stream->ctrl); - if (ret < 0) { - uvc_queue_enable(&stream->queue, 0); - return ret; - } - - if (!uvc_queue_streaming(&stream->queue)) - return 0; - - ret = uvc_init_video(stream, GFP_NOIO); - if (ret < 0) - uvc_queue_enable(&stream->queue, 0); - - return ret; -} - -/* ------------------------------------------------------------------------ - * Video device - */ - -/* - * Initialize the UVC video device by switching to alternate setting 0 and - * retrieve the default format. - * - * Some cameras (namely the Fuji Finepix) set the format and frame - * indexes to zero. The UVC standard doesn't clearly make this a spec - * violation, so try to silently fix the values if possible. - * - * This function is called before registering the device with V4L. - */ -int uvc_video_init(struct uvc_streaming *stream) -{ - struct uvc_streaming_control *probe = &stream->ctrl; - struct uvc_format *format = NULL; - struct uvc_frame *frame = NULL; - unsigned int i; - int ret; - - if (stream->nformats == 0) { - uvc_printk(KERN_INFO, "No supported video formats found.\n"); - return -EINVAL; - } - - atomic_set(&stream->active, 0); - - /* Initialize the video buffers queue. */ - uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); - - /* Alternate setting 0 should be the default, yet the XBox Live Vision - * Cam (and possibly other devices) crash or otherwise misbehave if - * they don't receive a SET_INTERFACE request before any other video - * control request. - */ - usb_set_interface(stream->dev->udev, stream->intfnum, 0); - - /* Set the streaming probe control with default streaming parameters - * retrieved from the device. Webcams that don't suport GET_DEF - * requests on the probe control will just keep their current streaming - * parameters. - */ - if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) - uvc_set_video_ctrl(stream, probe, 1); - - /* Initialize the streaming parameters with the probe control current - * value. This makes sure SET_CUR requests on the streaming commit - * control will always use values retrieved from a successful GET_CUR - * request on the probe control, as required by the UVC specification. - */ - ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); - if (ret < 0) - return ret; - - /* Check if the default format descriptor exists. Use the first - * available format otherwise. - */ - for (i = stream->nformats; i > 0; --i) { - format = &stream->format[i-1]; - if (format->index == probe->bFormatIndex) - break; - } - - if (format->nframes == 0) { - uvc_printk(KERN_INFO, "No frame descriptor found for the " - "default format.\n"); - return -EINVAL; - } - - /* Zero bFrameIndex might be correct. Stream-based formats (including - * MPEG-2 TS and DV) do not support frames but have a dummy frame - * descriptor with bFrameIndex set to zero. If the default frame - * descriptor is not found, use the first available frame. - */ - for (i = format->nframes; i > 0; --i) { - frame = &format->frame[i-1]; - if (frame->bFrameIndex == probe->bFrameIndex) - break; - } - - probe->bFormatIndex = format->index; - probe->bFrameIndex = frame->bFrameIndex; - - stream->cur_format = format; - stream->cur_frame = frame; - - /* Select the video decoding function */ - if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) - stream->decode = uvc_video_decode_isight; - else if (stream->intf->num_altsetting > 1) - stream->decode = uvc_video_decode_isoc; - else - stream->decode = uvc_video_decode_bulk; - } else { - if (stream->intf->num_altsetting == 1) - stream->decode = uvc_video_encode_bulk; - else { - uvc_printk(KERN_INFO, "Isochronous endpoints are not " - "supported for video output devices.\n"); - return -EINVAL; - } - } - - return 0; -} - -/* - * Enable or disable the video stream. - */ -int uvc_video_enable(struct uvc_streaming *stream, int enable) -{ - int ret; - - if (!enable) { - uvc_uninit_video(stream, 1); - usb_set_interface(stream->dev->udev, stream->intfnum, 0); - uvc_queue_enable(&stream->queue, 0); - uvc_video_clock_cleanup(stream); - return 0; - } - - ret = uvc_video_clock_init(stream); - if (ret < 0) - return ret; - - ret = uvc_queue_enable(&stream->queue, 1); - if (ret < 0) - goto error_queue; - - /* Commit the streaming parameters. */ - ret = uvc_commit_video(stream, &stream->ctrl); - if (ret < 0) - goto error_commit; - - ret = uvc_init_video(stream, GFP_KERNEL); - if (ret < 0) - goto error_video; - - return 0; - -error_video: - usb_set_interface(stream->dev->udev, stream->intfnum, 0); -error_commit: - uvc_queue_enable(&stream->queue, 0); -error_queue: - uvc_video_clock_cleanup(stream); - - return ret; -} diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h deleted file mode 100644 index 3764040475bb..000000000000 --- a/drivers/media/video/uvc/uvcvideo.h +++ /dev/null @@ -1,718 +0,0 @@ -#ifndef _USB_VIDEO_H_ -#define _USB_VIDEO_H_ - -#ifndef __KERNEL__ -#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead." -#endif /* __KERNEL__ */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* -------------------------------------------------------------------------- - * UVC constants - */ - -#define UVC_TERM_INPUT 0x0000 -#define UVC_TERM_OUTPUT 0x8000 -#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000) - -#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) -#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) -#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) -#define UVC_ENTITY_IS_ITERM(entity) \ - (UVC_ENTITY_IS_TERM(entity) && \ - ((entity)->type & 0x8000) == UVC_TERM_INPUT) -#define UVC_ENTITY_IS_OTERM(entity) \ - (UVC_ENTITY_IS_TERM(entity) && \ - ((entity)->type & 0x8000) == UVC_TERM_OUTPUT) - - -/* ------------------------------------------------------------------------ - * GUIDs - */ -#define UVC_GUID_UVC_CAMERA \ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} -#define UVC_GUID_UVC_OUTPUT \ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} -#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03} -#define UVC_GUID_UVC_PROCESSING \ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01} -#define UVC_GUID_UVC_SELECTOR \ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} - -#define UVC_GUID_FORMAT_MJPEG \ - { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_YUY2 \ - { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_YUY2_ISIGHT \ - { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_NV12 \ - { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_YV12 \ - { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_I420 \ - { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_UYVY \ - { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_Y800 \ - { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_Y8 \ - { 'Y', '8', ' ', ' ', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_Y10 \ - { 'Y', '1', '0', ' ', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_Y12 \ - { 'Y', '1', '2', ' ', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_Y16 \ - { 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_BY8 \ - { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_RGBP \ - { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -#define UVC_GUID_FORMAT_M420 \ - { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} - -#define UVC_GUID_FORMAT_H264 \ - { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \ - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} - -/* ------------------------------------------------------------------------ - * Driver specific constants. - */ - -#define DRIVER_VERSION "1.1.1" - -/* Number of isochronous URBs. */ -#define UVC_URBS 5 -/* Maximum number of packets per URB. */ -#define UVC_MAX_PACKETS 32 -/* Maximum number of video buffers. */ -#define UVC_MAX_VIDEO_BUFFERS 32 -/* Maximum status buffer size in bytes of interrupt URB. */ -#define UVC_MAX_STATUS_SIZE 16 - -#define UVC_CTRL_CONTROL_TIMEOUT 300 -#define UVC_CTRL_STREAMING_TIMEOUT 5000 - -/* Maximum allowed number of control mappings per device */ -#define UVC_MAX_CONTROL_MAPPINGS 1024 -#define UVC_MAX_CONTROL_MENU_ENTRIES 32 - -/* Devices quirks */ -#define UVC_QUIRK_STATUS_INTERVAL 0x00000001 -#define UVC_QUIRK_PROBE_MINMAX 0x00000002 -#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004 -#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 -#define UVC_QUIRK_STREAM_NO_FID 0x00000010 -#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 -#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 -#define UVC_QUIRK_PROBE_DEF 0x00000100 -#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200 - -/* Format flags */ -#define UVC_FMT_FLAG_COMPRESSED 0x00000001 -#define UVC_FMT_FLAG_STREAM 0x00000002 - -/* ------------------------------------------------------------------------ - * Structures. - */ - -struct uvc_device; - -/* TODO: Put the most frequently accessed fields at the beginning of - * structures to maximize cache efficiency. - */ -struct uvc_control_info { - struct list_head mappings; - - __u8 entity[16]; - __u8 index; /* Bit index in bmControls */ - __u8 selector; - - __u16 size; - __u32 flags; -}; - -struct uvc_control_mapping { - struct list_head list; - struct list_head ev_subs; - - __u32 id; - __u8 name[32]; - __u8 entity[16]; - __u8 selector; - - __u8 size; - __u8 offset; - enum v4l2_ctrl_type v4l2_type; - __u32 data_type; - - struct uvc_menu_info *menu_info; - __u32 menu_count; - - __u32 master_id; - __s32 master_manual; - __u32 slave_ids[2]; - - __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, - const __u8 *data); - void (*set) (struct uvc_control_mapping *mapping, __s32 value, - __u8 *data); -}; - -struct uvc_control { - struct uvc_entity *entity; - struct uvc_control_info info; - - __u8 index; /* Used to match the uvc_control entry with a - uvc_control_info. */ - __u8 dirty:1, - loaded:1, - modified:1, - cached:1, - initialized:1; - - __u8 *uvc_data; -}; - -struct uvc_format_desc { - char *name; - __u8 guid[16]; - __u32 fcc; -}; - -/* The term 'entity' refers to both UVC units and UVC terminals. - * - * The type field is either the terminal type (wTerminalType in the terminal - * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor). - * As the bDescriptorSubtype field is one byte long, the type value will - * always have a null MSB for units. All terminal types defined by the UVC - * specification have a non-null MSB, so it is safe to use the MSB to - * differentiate between units and terminals as long as the descriptor parsing - * code makes sure terminal types have a non-null MSB. - * - * For terminals, the type's most significant bit stores the terminal - * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should - * always be accessed with the UVC_ENTITY_* macros and never directly. - */ - -struct uvc_entity { - struct list_head list; /* Entity as part of a UVC device. */ - struct list_head chain; /* Entity as part of a video device - * chain. */ - __u8 id; - __u16 type; - char name[64]; - - /* Media controller-related fields. */ - struct video_device *vdev; - struct v4l2_subdev subdev; - unsigned int num_pads; - unsigned int num_links; - struct media_pad *pads; - - union { - struct { - __u16 wObjectiveFocalLengthMin; - __u16 wObjectiveFocalLengthMax; - __u16 wOcularFocalLength; - __u8 bControlSize; - __u8 *bmControls; - } camera; - - struct { - __u8 bControlSize; - __u8 *bmControls; - __u8 bTransportModeSize; - __u8 *bmTransportModes; - } media; - - struct { - } output; - - struct { - __u16 wMaxMultiplier; - __u8 bControlSize; - __u8 *bmControls; - __u8 bmVideoStandards; - } processing; - - struct { - } selector; - - struct { - __u8 guidExtensionCode[16]; - __u8 bNumControls; - __u8 bControlSize; - __u8 *bmControls; - __u8 *bmControlsType; - } extension; - }; - - __u8 bNrInPins; - __u8 *baSourceID; - - unsigned int ncontrols; - struct uvc_control *controls; -}; - -struct uvc_frame { - __u8 bFrameIndex; - __u8 bmCapabilities; - __u16 wWidth; - __u16 wHeight; - __u32 dwMinBitRate; - __u32 dwMaxBitRate; - __u32 dwMaxVideoFrameBufferSize; - __u8 bFrameIntervalType; - __u32 dwDefaultFrameInterval; - __u32 *dwFrameInterval; -}; - -struct uvc_format { - __u8 type; - __u8 index; - __u8 bpp; - __u8 colorspace; - __u32 fcc; - __u32 flags; - - char name[32]; - - unsigned int nframes; - struct uvc_frame *frame; -}; - -struct uvc_streaming_header { - __u8 bNumFormats; - __u8 bEndpointAddress; - __u8 bTerminalLink; - __u8 bControlSize; - __u8 *bmaControls; - /* The following fields are used by input headers only. */ - __u8 bmInfo; - __u8 bStillCaptureMethod; - __u8 bTriggerSupport; - __u8 bTriggerUsage; -}; - -enum uvc_buffer_state { - UVC_BUF_STATE_IDLE = 0, - UVC_BUF_STATE_QUEUED = 1, - UVC_BUF_STATE_ACTIVE = 2, - UVC_BUF_STATE_READY = 3, - UVC_BUF_STATE_DONE = 4, - UVC_BUF_STATE_ERROR = 5, -}; - -struct uvc_buffer { - struct vb2_buffer buf; - struct list_head queue; - - enum uvc_buffer_state state; - unsigned int error; - - void *mem; - unsigned int length; - unsigned int bytesused; - - u32 pts; -}; - -#define UVC_QUEUE_DISCONNECTED (1 << 0) -#define UVC_QUEUE_DROP_CORRUPTED (1 << 1) - -struct uvc_video_queue { - struct vb2_queue queue; - struct mutex mutex; /* Protects queue */ - - unsigned int flags; - unsigned int buf_used; - - spinlock_t irqlock; /* Protects irqqueue */ - struct list_head irqqueue; -}; - -struct uvc_video_chain { - struct uvc_device *dev; - struct list_head list; - - struct list_head entities; /* All entities */ - struct uvc_entity *processing; /* Processing unit */ - struct uvc_entity *selector; /* Selector unit */ - - struct mutex ctrl_mutex; /* Protects ctrl.info */ -}; - -struct uvc_stats_frame { - unsigned int size; /* Number of bytes captured */ - unsigned int first_data; /* Index of the first non-empty packet */ - - unsigned int nb_packets; /* Number of packets */ - unsigned int nb_empty; /* Number of empty packets */ - unsigned int nb_invalid; /* Number of packets with an invalid header */ - unsigned int nb_errors; /* Number of packets with the error bit set */ - - unsigned int nb_pts; /* Number of packets with a PTS timestamp */ - unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */ - unsigned int last_pts_diff; /* Index of the last PTS difference */ - bool has_initial_pts; /* Whether the first non-empty packet has a PTS */ - bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */ - u32 pts; /* PTS of the last packet */ - - unsigned int nb_scr; /* Number of packets with a SCR timestamp */ - unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */ - u16 scr_sof; /* SCR.SOF of the last packet */ - u32 scr_stc; /* SCR.STC of the last packet */ -}; - -struct uvc_stats_stream { - struct timespec start_ts; /* Stream start timestamp */ - struct timespec stop_ts; /* Stream stop timestamp */ - - unsigned int nb_frames; /* Number of frames */ - - unsigned int nb_packets; /* Number of packets */ - unsigned int nb_empty; /* Number of empty packets */ - unsigned int nb_invalid; /* Number of packets with an invalid header */ - unsigned int nb_errors; /* Number of packets with the error bit set */ - - unsigned int nb_pts_constant; /* Number of frames with constant PTS */ - unsigned int nb_pts_early; /* Number of frames with early PTS */ - unsigned int nb_pts_initial; /* Number of frames with initial PTS */ - - unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */ - unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */ - unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */ - unsigned int scr_sof; /* STC.SOF of the last packet */ - unsigned int min_sof; /* Minimum STC.SOF value */ - unsigned int max_sof; /* Maximum STC.SOF value */ -}; - -struct uvc_streaming { - struct list_head list; - struct uvc_device *dev; - struct video_device *vdev; - struct uvc_video_chain *chain; - atomic_t active; - - struct usb_interface *intf; - int intfnum; - __u16 maxpsize; - - struct uvc_streaming_header header; - enum v4l2_buf_type type; - - unsigned int nformats; - struct uvc_format *format; - - struct uvc_streaming_control ctrl; - struct uvc_format *cur_format; - struct uvc_frame *cur_frame; - /* Protect access to ctrl, cur_format, cur_frame and hardware video - * probe control. - */ - struct mutex mutex; - - /* Buffers queue. */ - unsigned int frozen : 1; - struct uvc_video_queue queue; - void (*decode) (struct urb *urb, struct uvc_streaming *video, - struct uvc_buffer *buf); - - /* Context data used by the bulk completion handler. */ - struct { - __u8 header[256]; - unsigned int header_size; - int skip_payload; - __u32 payload_size; - __u32 max_payload_size; - } bulk; - - struct urb *urb[UVC_URBS]; - char *urb_buffer[UVC_URBS]; - dma_addr_t urb_dma[UVC_URBS]; - unsigned int urb_size; - - __u32 sequence; - __u8 last_fid; - - /* debugfs */ - struct dentry *debugfs_dir; - struct { - struct uvc_stats_frame frame; - struct uvc_stats_stream stream; - } stats; - - /* Timestamps support. */ - struct uvc_clock { - struct uvc_clock_sample { - u32 dev_stc; - u16 dev_sof; - struct timespec host_ts; - u16 host_sof; - } *samples; - - unsigned int head; - unsigned int count; - unsigned int size; - - u16 last_sof; - u16 sof_offset; - - spinlock_t lock; - } clock; -}; - -enum uvc_device_state { - UVC_DEV_DISCONNECTED = 1, -}; - -struct uvc_device { - struct usb_device *udev; - struct usb_interface *intf; - unsigned long warnings; - __u32 quirks; - int intfnum; - char name[32]; - - enum uvc_device_state state; - atomic_t users; - atomic_t nmappings; - - /* Video control interface */ -#ifdef CONFIG_MEDIA_CONTROLLER - struct media_device mdev; -#endif - struct v4l2_device vdev; - __u16 uvc_version; - __u32 clock_frequency; - - struct list_head entities; - struct list_head chains; - - /* Video Streaming interfaces */ - struct list_head streams; - atomic_t nstreams; - - /* Status Interrupt Endpoint */ - struct usb_host_endpoint *int_ep; - struct urb *int_urb; - __u8 *status; - struct input_dev *input; - char input_phys[64]; -}; - -enum uvc_handle_state { - UVC_HANDLE_PASSIVE = 0, - UVC_HANDLE_ACTIVE = 1, -}; - -struct uvc_fh { - struct v4l2_fh vfh; - struct uvc_video_chain *chain; - struct uvc_streaming *stream; - enum uvc_handle_state state; -}; - -struct uvc_driver { - struct usb_driver driver; -}; - -/* ------------------------------------------------------------------------ - * Debugging, printing and logging - */ - -#define UVC_TRACE_PROBE (1 << 0) -#define UVC_TRACE_DESCR (1 << 1) -#define UVC_TRACE_CONTROL (1 << 2) -#define UVC_TRACE_FORMAT (1 << 3) -#define UVC_TRACE_CAPTURE (1 << 4) -#define UVC_TRACE_CALLS (1 << 5) -#define UVC_TRACE_IOCTL (1 << 6) -#define UVC_TRACE_FRAME (1 << 7) -#define UVC_TRACE_SUSPEND (1 << 8) -#define UVC_TRACE_STATUS (1 << 9) -#define UVC_TRACE_VIDEO (1 << 10) -#define UVC_TRACE_STATS (1 << 11) -#define UVC_TRACE_CLOCK (1 << 12) - -#define UVC_WARN_MINMAX 0 -#define UVC_WARN_PROBE_DEF 1 -#define UVC_WARN_XU_GET_RES 2 - -extern unsigned int uvc_clock_param; -extern unsigned int uvc_no_drop_param; -extern unsigned int uvc_trace_param; -extern unsigned int uvc_timeout_param; - -#define uvc_trace(flag, msg...) \ - do { \ - if (uvc_trace_param & flag) \ - printk(KERN_DEBUG "uvcvideo: " msg); \ - } while (0) - -#define uvc_warn_once(dev, warn, msg...) \ - do { \ - if (!test_and_set_bit(warn, &dev->warnings)) \ - printk(KERN_INFO "uvcvideo: " msg); \ - } while (0) - -#define uvc_printk(level, msg...) \ - printk(level "uvcvideo: " msg) - -/* -------------------------------------------------------------------------- - * Internal functions. - */ - -/* Core driver */ -extern struct uvc_driver uvc_driver; - -extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); - -/* Video buffers queue management. */ -extern void uvc_queue_init(struct uvc_video_queue *queue, - enum v4l2_buf_type type, int drop_corrupted); -extern int uvc_alloc_buffers(struct uvc_video_queue *queue, - struct v4l2_requestbuffers *rb); -extern void uvc_free_buffers(struct uvc_video_queue *queue); -extern int uvc_query_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *v4l2_buf); -extern int uvc_queue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *v4l2_buf); -extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *v4l2_buf, int nonblocking); -extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable); -extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); -extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, - struct uvc_buffer *buf); -extern int uvc_queue_mmap(struct uvc_video_queue *queue, - struct vm_area_struct *vma); -extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, - struct file *file, poll_table *wait); -#ifndef CONFIG_MMU -extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff); -#endif -extern int uvc_queue_allocated(struct uvc_video_queue *queue); -static inline int uvc_queue_streaming(struct uvc_video_queue *queue) -{ - return vb2_is_streaming(&queue->queue); -} - -/* V4L2 interface */ -extern const struct v4l2_file_operations uvc_fops; - -/* Media controller */ -extern int uvc_mc_register_entities(struct uvc_video_chain *chain); -extern void uvc_mc_cleanup_entity(struct uvc_entity *entity); - -/* Video */ -extern int uvc_video_init(struct uvc_streaming *stream); -extern int uvc_video_suspend(struct uvc_streaming *stream); -extern int uvc_video_resume(struct uvc_streaming *stream, int reset); -extern int uvc_video_enable(struct uvc_streaming *stream, int enable); -extern int uvc_probe_video(struct uvc_streaming *stream, - struct uvc_streaming_control *probe); -extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size); -void uvc_video_clock_update(struct uvc_streaming *stream, - struct v4l2_buffer *v4l2_buf, - struct uvc_buffer *buf); - -/* Status */ -extern int uvc_status_init(struct uvc_device *dev); -extern void uvc_status_cleanup(struct uvc_device *dev); -extern int uvc_status_start(struct uvc_device *dev); -extern void uvc_status_stop(struct uvc_device *dev); -extern int uvc_status_suspend(struct uvc_device *dev); -extern int uvc_status_resume(struct uvc_device *dev); - -/* Controls */ -extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; - -extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, - struct v4l2_queryctrl *v4l2_ctrl); -extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, - struct v4l2_querymenu *query_menu); - -extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, - const struct uvc_control_mapping *mapping); -extern int uvc_ctrl_init_device(struct uvc_device *dev); -extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); -extern int uvc_ctrl_resume_device(struct uvc_device *dev); - -extern int uvc_ctrl_begin(struct uvc_video_chain *chain); -extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, - const struct v4l2_ext_control *xctrls, - unsigned int xctrls_count); -static inline int uvc_ctrl_commit(struct uvc_fh *handle, - const struct v4l2_ext_control *xctrls, - unsigned int xctrls_count) -{ - return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count); -} -static inline int uvc_ctrl_rollback(struct uvc_fh *handle) -{ - return __uvc_ctrl_commit(handle, 1, NULL, 0); -} - -extern int uvc_ctrl_get(struct uvc_video_chain *chain, - struct v4l2_ext_control *xctrl); -extern int uvc_ctrl_set(struct uvc_video_chain *chain, - struct v4l2_ext_control *xctrl); - -extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, - struct uvc_xu_control_query *xqry); - -/* Utility functions */ -extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, - unsigned int n_terms, unsigned int threshold); -extern uint32_t uvc_fraction_to_interval(uint32_t numerator, - uint32_t denominator); -extern struct usb_host_endpoint *uvc_find_endpoint( - struct usb_host_interface *alts, __u8 epaddr); - -/* Quirks support */ -void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, - struct uvc_buffer *buf); - -/* debugfs and statistics */ -int uvc_debugfs_init(void); -void uvc_debugfs_cleanup(void); -int uvc_debugfs_init_stream(struct uvc_streaming *stream); -void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream); - -size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, - size_t size); - -#endif -- cgit v1.2.3 From 0aa77f6c2954896b132f8b6f2e9f063c52800913 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Aug 2012 01:01:29 -0300 Subject: [media] move the remaining USB drivers to drivers/media/usb Move the 3 remaining usb drivers to their proper space. Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/usb/Kconfig | 3 + drivers/media/usb/Makefile | 2 + drivers/media/usb/s2255/Kconfig | 9 + drivers/media/usb/s2255/Makefile | 2 + drivers/media/usb/s2255/s2255drv.c | 2689 ++++++++++++++++++++++++++++++ drivers/media/usb/stkwebcam/Kconfig | 13 + drivers/media/usb/stkwebcam/Makefile | 4 + drivers/media/usb/stkwebcam/stk-sensor.c | 595 +++++++ drivers/media/usb/stkwebcam/stk-webcam.c | 1380 +++++++++++++++ drivers/media/usb/stkwebcam/stk-webcam.h | 134 ++ drivers/media/usb/zr364xx/Kconfig | 14 + drivers/media/usb/zr364xx/Makefile | 2 + drivers/media/usb/zr364xx/zr364xx.c | 1643 ++++++++++++++++++ drivers/media/video/Kconfig | 50 - drivers/media/video/Makefile | 8 - drivers/media/video/s2255drv.c | 2689 ------------------------------ drivers/media/video/stk-sensor.c | 595 ------- drivers/media/video/stk-webcam.c | 1380 --------------- drivers/media/video/stk-webcam.h | 134 -- drivers/media/video/zr364xx.c | 1643 ------------------ 21 files changed, 6491 insertions(+), 6500 deletions(-) create mode 100644 drivers/media/usb/s2255/Kconfig create mode 100644 drivers/media/usb/s2255/Makefile create mode 100644 drivers/media/usb/s2255/s2255drv.c create mode 100644 drivers/media/usb/stkwebcam/Kconfig create mode 100644 drivers/media/usb/stkwebcam/Makefile create mode 100644 drivers/media/usb/stkwebcam/stk-sensor.c create mode 100644 drivers/media/usb/stkwebcam/stk-webcam.c create mode 100644 drivers/media/usb/stkwebcam/stk-webcam.h create mode 100644 drivers/media/usb/zr364xx/Kconfig create mode 100644 drivers/media/usb/zr364xx/Makefile create mode 100644 drivers/media/usb/zr364xx/zr364xx.c delete mode 100644 drivers/media/video/s2255drv.c delete mode 100644 drivers/media/video/stk-sensor.c delete mode 100644 drivers/media/video/stk-webcam.c delete mode 100644 drivers/media/video/stk-webcam.h delete mode 100644 drivers/media/video/zr364xx.c (limited to 'drivers/media/usb') diff --git a/MAINTAINERS b/MAINTAINERS index 13fd97f6466a..99a930d712cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7371,7 +7371,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://royale.zerezo.com/zr364xx/ S: Maintained F: Documentation/video4linux/zr364xx.txt -F: drivers/media/video/zr364xx.c +F: drivers/media/usb/zr364xx.c USER-MODE LINUX (UML) M: Jeff Dike diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index a1e25ee9d671..069a3c1d03f5 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -11,6 +11,9 @@ source "drivers/media/usb/uvc/Kconfig" source "drivers/media/usb/gspca/Kconfig" source "drivers/media/usb/pwc/Kconfig" source "drivers/media/usb/cpia2/Kconfig" +source "drivers/media/usb/zr364xx/Kconfig" +source "drivers/media/usb/stkwebcam/Kconfig" +source "drivers/media/usb/s2255/Kconfig" source "drivers/media/usb/sn9c102/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 428827a4d97a..63e37bb2ed74 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -4,6 +4,8 @@ # DVB USB-only drivers obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ +obj-y := zr364xx/ stkwebcam/ s2255/ + obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_PWC) += pwc/ diff --git a/drivers/media/usb/s2255/Kconfig b/drivers/media/usb/s2255/Kconfig new file mode 100644 index 000000000000..7e8ee1f864ab --- /dev/null +++ b/drivers/media/usb/s2255/Kconfig @@ -0,0 +1,9 @@ +config USB_S2255 + tristate "USB Sensoray 2255 video capture device" + depends on VIDEO_V4L2 + select VIDEOBUF_VMALLOC + default n + help + Say Y here if you want support for the Sensoray 2255 USB device. + This driver can be compiled as a module, called s2255drv. + diff --git a/drivers/media/usb/s2255/Makefile b/drivers/media/usb/s2255/Makefile new file mode 100644 index 000000000000..197d0bb2adfd --- /dev/null +++ b/drivers/media/usb/s2255/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_USB_S2255) += s2255drv.o + diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c new file mode 100644 index 000000000000..6c7960cc7506 --- /dev/null +++ b/drivers/media/usb/s2255/s2255drv.c @@ -0,0 +1,2689 @@ +/* + * s2255drv.c - a driver for the Sensoray 2255 USB video capture device + * + * Copyright (C) 2007-2010 by Sensoray Company Inc. + * Dean Anderson + * + * Some video buffer code based on vivi driver: + * + * Sensoray 2255 device supports 4 simultaneous channels. + * The channels are not "crossbar" inputs, they are physically + * attached to separate video decoders. + * + * Because of USB2.0 bandwidth limitations. There is only a + * certain amount of data which may be transferred at one time. + * + * Example maximum bandwidth utilization: + * + * -full size, color mode YUYV or YUV422P: 2 channels at once + * -full or half size Grey scale: all 4 channels at once + * -half size, color mode YUYV or YUV422P: all 4 channels at once + * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels + * at once. + * + * This program is free software; 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 +#include +#include +#include +#include + +#define S2255_VERSION "1.22.1" +#define FIRMWARE_FILE_NAME "f2255usb.bin" + +/* default JPEG quality */ +#define S2255_DEF_JPEG_QUAL 50 +/* vendor request in */ +#define S2255_VR_IN 0 +/* vendor request out */ +#define S2255_VR_OUT 1 +/* firmware query */ +#define S2255_VR_FW 0x30 +/* USB endpoint number for configuring the device */ +#define S2255_CONFIG_EP 2 +/* maximum time for DSP to start responding after last FW word loaded(ms) */ +#define S2255_DSP_BOOTTIME 800 +/* maximum time to wait for firmware to load (ms) */ +#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) +#define S2255_DEF_BUFS 16 +#define S2255_SETMODE_TIMEOUT 500 +#define S2255_VIDSTATUS_TIMEOUT 350 +#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) +#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) +#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) +#define S2255_RESPONSE_FW cpu_to_le32(0x10) +#define S2255_RESPONSE_STATUS cpu_to_le32(0x20) +#define S2255_USB_XFER_SIZE (16 * 1024) +#define MAX_CHANNELS 4 +#define SYS_FRAMES 4 +/* maximum size is PAL full size plus room for the marker header(s) */ +#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) +#define DEF_USB_BLOCK S2255_USB_XFER_SIZE +#define LINE_SZ_4CIFS_NTSC 640 +#define LINE_SZ_2CIFS_NTSC 640 +#define LINE_SZ_1CIFS_NTSC 320 +#define LINE_SZ_4CIFS_PAL 704 +#define LINE_SZ_2CIFS_PAL 704 +#define LINE_SZ_1CIFS_PAL 352 +#define NUM_LINES_4CIFS_NTSC 240 +#define NUM_LINES_2CIFS_NTSC 240 +#define NUM_LINES_1CIFS_NTSC 240 +#define NUM_LINES_4CIFS_PAL 288 +#define NUM_LINES_2CIFS_PAL 288 +#define NUM_LINES_1CIFS_PAL 288 +#define LINE_SZ_DEF 640 +#define NUM_LINES_DEF 240 + + +/* predefined settings */ +#define FORMAT_NTSC 1 +#define FORMAT_PAL 2 + +#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ +#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ +#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ +/* SCALE_4CIFSI is the 2 fields interpolated into one */ +#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */ + +#define COLOR_YUVPL 1 /* YUV planar */ +#define COLOR_YUVPK 2 /* YUV packed */ +#define COLOR_Y8 4 /* monochrome */ +#define COLOR_JPG 5 /* JPEG */ + +#define MASK_COLOR 0x000000ff +#define MASK_JPG_QUALITY 0x0000ff00 +#define MASK_INPUT_TYPE 0x000f0000 +/* frame decimation. */ +#define FDEC_1 1 /* capture every frame. default */ +#define FDEC_2 2 /* capture every 2nd frame */ +#define FDEC_3 3 /* capture every 3rd frame */ +#define FDEC_5 5 /* capture every 5th frame */ + +/*------------------------------------------------------- + * Default mode parameters. + *-------------------------------------------------------*/ +#define DEF_SCALE SCALE_4CIFS +#define DEF_COLOR COLOR_YUVPL +#define DEF_FDEC FDEC_1 +#define DEF_BRIGHT 0 +#define DEF_CONTRAST 0x5c +#define DEF_SATURATION 0x80 +#define DEF_HUE 0 + +/* usb config commands */ +#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) +#define CMD_2255 0xc2255000 +#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) +#define CMD_START cpu_to_le32((CMD_2255 | 0x20)) +#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) +#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) + +struct s2255_mode { + u32 format; /* input video format (NTSC, PAL) */ + u32 scale; /* output video scale */ + u32 color; /* output video color format */ + u32 fdec; /* frame decimation */ + u32 bright; /* brightness */ + u32 contrast; /* contrast */ + u32 saturation; /* saturation */ + u32 hue; /* hue (NTSC only)*/ + u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/ + u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */ + u32 restart; /* if DSP requires restart */ +}; + + +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 + +/* frame structure */ +struct s2255_framei { + unsigned long size; + unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct s2255_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ +}; + +#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \ + DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \ + DEF_HUE, 0, DEF_USB_BLOCK, 0} + +struct s2255_dmaqueue { + struct list_head active; + struct s2255_dev *dev; +}; + +/* for firmware loading, fw_state */ +#define S2255_FW_NOTLOADED 0 +#define S2255_FW_LOADED_DSPWAIT 1 +#define S2255_FW_SUCCESS 2 +#define S2255_FW_FAILED 3 +#define S2255_FW_DISCONNECTING 4 +#define S2255_FW_MARKER cpu_to_le32(0x22552f2f) +/* 2255 read states */ +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 +struct s2255_fw { + int fw_loaded; + int fw_size; + struct urb *fw_urb; + atomic_t fw_state; + void *pfw_data; + wait_queue_head_t wait_fw; + const struct firmware *fw; +}; + +struct s2255_pipeinfo { + u32 max_transfer_size; + u32 cur_transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *dev; /* back pointer to s2255_dev struct*/ + u32 err_count; + u32 idx; +}; + +struct s2255_fmt; /*forward declaration */ +struct s2255_dev; + +struct s2255_channel { + struct video_device vdev; + int resources; + struct s2255_dmaqueue vidq; + struct s2255_bufferi buffer; + struct s2255_mode mode; + /* jpeg compression */ + struct v4l2_jpegcompression jc; + /* capture parameters (for high quality mode full size) */ + struct v4l2_captureparm cap_parm; + int cur_frame; + int last_frame; + + int b_acquire; + /* allocated image size */ + unsigned long req_image_size; + /* received packet size */ + unsigned long pkt_size; + int bad_payload; + unsigned long frame_count; + /* if JPEG image */ + int jpg_size; + /* if channel configured to default state */ + int configured; + wait_queue_head_t wait_setmode; + int setmode_ready; + /* video status items */ + int vidstatus; + wait_queue_head_t wait_vidstatus; + int vidstatus_ready; + unsigned int width; + unsigned int height; + const struct s2255_fmt *fmt; + int idx; /* channel number on device, 0-3 */ +}; + + +struct s2255_dev { + struct s2255_channel channel[MAX_CHANNELS]; + struct v4l2_device v4l2_dev; + atomic_t num_channels; + int frames; + struct mutex lock; /* channels[].vdev.lock */ + struct usb_device *udev; + struct usb_interface *interface; + u8 read_endpoint; + struct timer_list timer; + struct s2255_fw *fw_data; + struct s2255_pipeinfo pipe; + u32 cc; /* current channel */ + int frame_ready; + int chn_ready; + spinlock_t slock; + /* dsp firmware version (f2255usb.bin) */ + int dsp_fw_ver; + u16 pid; /* product id */ +}; + +static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); +} + +struct s2255_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* buffer for one video frame */ +struct s2255_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct s2255_fmt *fmt; +}; + +struct s2255_fh { + struct s2255_dev *dev; + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; + struct s2255_channel *channel; + int resources; +}; + +/* current cypress EEPROM firmware version */ +#define S2255_CUR_USB_FWVER ((3 << 8) | 12) +/* current DSP FW version */ +#define S2255_CUR_DSP_FWVER 10104 +/* Need DSP version 5+ for video status feature */ +#define S2255_MIN_DSP_STATUS 5 +#define S2255_MIN_DSP_COLORFILTER 8 +#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) + +/* private V4L2 controls */ + +/* + * The following chart displays how COLORFILTER should be set + * ========================================================= + * = fourcc = COLORFILTER = + * = =============================== + * = = 0 = 1 = + * ========================================================= + * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= + * = = s-video or = composite = + * = = B/W camera = input = + * ========================================================= + * = other = color, svideo = color, = + * = = = composite = + * ========================================================= + * + * Notes: + * channels 0-3 on 2255 are composite + * channels 0-1 on 2257 are composite, 2-3 are s-video + * If COLORFILTER is 0 with a composite color camera connected, + * the output will appear monochrome but hatching + * will occur. + * COLORFILTER is different from "color killer" and "color effects" + * for reasons above. + */ +#define S2255_V4L2_YC_ON 1 +#define S2255_V4L2_YC_OFF 0 +#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) + +/* frame prefix size (sent once every frame) */ +#define PREFIX_SIZE 512 + +/* Channels on box are in reverse order */ +static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0}; + +static int debug; +static int *s2255_debug = &debug; + +static int s2255_start_readpipe(struct s2255_dev *dev); +static void s2255_stop_readpipe(struct s2255_dev *dev); +static int s2255_start_acquire(struct s2255_channel *channel); +static int s2255_stop_acquire(struct s2255_channel *channel); +static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf, + int jpgsize); +static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode); +static int s2255_board_shutdown(struct s2255_dev *dev); +static void s2255_fwload_start(struct s2255_dev *dev, int reset); +static void s2255_destroy(struct s2255_dev *dev); +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, + u16 index, u16 value, void *buf, + s32 buf_len, int bOut); + +/* dev_err macro with driver name */ +#define S2255_DRIVER_NAME "s2255" +#define s2255_dev_err(dev, fmt, arg...) \ + dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) + +#define dprintk(level, fmt, arg...) \ + do { \ + if (*s2255_debug >= (level)) { \ + printk(KERN_DEBUG S2255_DRIVER_NAME \ + ": " fmt, ##arg); \ + } \ + } while (0) + +static struct usb_driver s2255_driver; + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ + +/* start video number */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ + +/* Enable jpeg capture. */ +static int jpeg_enable = 1; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)"); +module_param(video_nr, int, 0644); +MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); +module_param(jpeg_enable, int, 0644); +MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1"); + +/* USB device table */ +#define USB_SENSORAY_VID 0x1943 +static struct usb_device_id s2255_table[] = { + {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, + {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, s2255_table); + +#define BUFFER_TIMEOUT msecs_to_jiffies(400) + +/* image formats. */ +/* JPEG formats must be defined last to support jpeg_enable parameter */ +static const struct s2255_fmt formats[] = { + { + .name = "4:2:2, planar, YUV422P", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 16 + + }, { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16 + + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16 + }, { + .name = "8bpp GREY", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8 + }, { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + }, { + .name = "MJPG", + .fourcc = V4L2_PIX_FMT_MJPEG, + .depth = 24 + } +}; + +static int norm_maxw(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; +} + +static int norm_maxh(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); +} + +static int norm_minw(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; +} + +static int norm_minh(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); +} + + +/* + * TODO: fixme: move YUV reordering to hardware + * converts 2255 planar format to yuyv or uyvy + */ +static void planar422p_to_yuv_packed(const unsigned char *in, + unsigned char *out, + int width, int height, + int fmt) +{ + unsigned char *pY; + unsigned char *pCb; + unsigned char *pCr; + unsigned long size = height * width; + unsigned int i; + pY = (unsigned char *)in; + pCr = (unsigned char *)in + height * width; + pCb = (unsigned char *)in + height * width + (height * width / 2); + for (i = 0; i < size * 2; i += 4) { + out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; + out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; + out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; + out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; + } + return; +} + +static void s2255_reset_dsppower(struct s2255_dev *dev) +{ + s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1); + msleep(10); + s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); + msleep(600); + s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1); + return; +} + +/* kickstarts the firmware loading. from probe + */ +static void s2255_timer(unsigned long user_data) +{ + struct s2255_fw *data = (struct s2255_fw *)user_data; + dprintk(100, "%s\n", __func__); + if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { + printk(KERN_ERR "s2255: can't submit urb\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } +} + + +/* this loads the firmware asynchronously. + Originally this was done synchroously in probe. + But it is better to load it asynchronously here than block + inside the probe function. Blocking inside probe affects boot time. + FW loading is triggered by the timer in the probe function +*/ +static void s2255_fwchunk_complete(struct urb *urb) +{ + struct s2255_fw *data = urb->context; + struct usb_device *udev = urb->dev; + int len; + dprintk(100, "%s: udev %p urb %p", __func__, udev, urb); + if (urb->status) { + dev_err(&udev->dev, "URB failed with status %d\n", urb->status); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } + if (data->fw_urb == NULL) { + s2255_dev_err(&udev->dev, "disconnected\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } +#define CHUNK_SIZE 512 + /* all USB transfers must be done with continuous kernel memory. + can't allocate more than 128k in current linux kernel, so + upload the firmware in chunks + */ + if (data->fw_loaded < data->fw_size) { + len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? + data->fw_size % CHUNK_SIZE : CHUNK_SIZE; + + if (len < CHUNK_SIZE) + memset(data->pfw_data, 0, CHUNK_SIZE); + + dprintk(100, "completed len %d, loaded %d \n", len, + data->fw_loaded); + + memcpy(data->pfw_data, + (char *) data->fw->data + data->fw_loaded, len); + + usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), + data->pfw_data, CHUNK_SIZE, + s2255_fwchunk_complete, data); + if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { + dev_err(&udev->dev, "failed submit URB\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } + data->fw_loaded += len; + } else { + atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); + dprintk(100, "%s: firmware upload complete\n", __func__); + } + return; + +} + +static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) +{ + struct s2255_dmaqueue *dma_q = &channel->vidq; + struct s2255_buffer *buf; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + unsigned long flags = 0; + int rc = 0; + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&dma_q->active)) { + dprintk(1, "No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct s2255_buffer, vb.queue); + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + s2255_fillbuff(channel, buf, jpgsize); + wake_up(&buf->vb.done); + dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&dev->slock, flags); + return rc; +} + +static const struct s2255_fmt *format_by_fourcc(int fourcc) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (-1 == formats[i].fourcc) + continue; + if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) + continue; + if (formats[i].fourcc == fourcc) + return formats + i; + } + return NULL; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab + * Ted Walther + * John Sokol + * http://v4l.videotechnology.com/ + * + */ +static void s2255_fillbuff(struct s2255_channel *channel, + struct s2255_buffer *buf, int jpgsize) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + + if (!vbuf) + return; + last_frame = channel->last_frame; + if (last_frame != -1) { + tmpbuf = + (const char *)channel->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + planar422p_to_yuv_packed((const unsigned char *)tmpbuf, + vbuf, buf->vb.width, + buf->vb.height, + buf->fmt->fourcc); + break; + case V4L2_PIX_FMT_GREY: + memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); + break; + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_MJPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + case V4L2_PIX_FMT_YUV422P: + memcpy(vbuf, tmpbuf, + buf->vb.width * buf->vb.height * 2); + break; + default: + printk(KERN_DEBUG "s2255: unknown format?\n"); + } + channel->last_frame = -1; + } else { + printk(KERN_ERR "s2255: =======no frame\n"); + return; + + } + dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n", + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = channel->frame_count * 2; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +} + + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; + *size = channel->width * channel->height * (channel->fmt->depth >> 3); + + if (0 == *count) + *count = S2255_DEF_BUFS; + + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf) +{ + dprintk(4, "%s\n", __func__); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + int rc; + int w = channel->width; + int h = channel->height; + dprintk(4, "%s, field=%d\n", __func__, field); + if (channel->fmt == NULL) + return -EINVAL; + + if ((w < norm_minw(&channel->vdev)) || + (w > norm_maxw(&channel->vdev)) || + (h < norm_minh(&channel->vdev)) || + (h > norm_maxh(&channel->vdev))) { + dprintk(4, "invalid buffer prepare\n"); + return -EINVAL; + } + buf->vb.size = w * h * (channel->fmt->depth >> 3); + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) { + dprintk(4, "invalid buffer prepare\n"); + return -EINVAL; + } + + buf->fmt = channel->fmt; + buf->vb.width = w; + buf->vb.height = h; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; + struct s2255_dmaqueue *vidq = &channel->vidq; + dprintk(1, "%s\n", __func__); + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct s2255_fh *fh = vq->priv_data; + dprintk(4, "%s %d\n", __func__, fh->channel->idx); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops s2255_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + + +static int res_get(struct s2255_fh *fh) +{ + struct s2255_channel *channel = fh->channel; + /* is it free? */ + if (channel->resources) + return 0; /* no, someone else uses it */ + /* it's free, grab it */ + channel->resources = 1; + fh->resources = 1; + dprintk(1, "s2255: res: get\n"); + return 1; +} + +static int res_locked(struct s2255_fh *fh) +{ + return fh->channel->resources; +} + +static int res_check(struct s2255_fh *fh) +{ + return fh->resources; +} + + +static void res_free(struct s2255_fh *fh) +{ + struct s2255_channel *channel = fh->channel; + channel->resources = 0; + fh->resources = 0; + dprintk(1, "res: put\n"); +} + +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + static const char *colorfilter[] = { + "Off", + "On", + NULL + }; + if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { + int i; + const char **menu_items = colorfilter; + for (i = 0; i < qmenu->index && menu_items[i]; i++) + ; /* do nothing (from v4l2-common.c) */ + if (menu_items[i] == NULL || menu_items[i][0] == '\0') + return -EINVAL; + strlcpy(qmenu->name, menu_items[qmenu->index], + sizeof(qmenu->name)); + return 0; + } + return v4l2_ctrl_query_menu(qmenu, NULL, NULL); +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + strlcpy(cap->driver, "s2255", sizeof(cap->driver)); + strlcpy(cap->card, "s2255", sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index = f->index; + + if (index >= ARRAY_SIZE(formats)) + return -EINVAL; + if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) + return -EINVAL; + dprintk(4, "name %s\n", formats[index].name); + strlcpy(f->description, formats[index].name, sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = channel->fmt->fourcc; + f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct s2255_fmt *fmt; + enum v4l2_field field; + int b_any_field = 0; + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + int is_ntsc; + is_ntsc = + (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + if (fmt == NULL) + return -EINVAL; + + field = f->fmt.pix.field; + if (field == V4L2_FIELD_ANY) + b_any_field = 1; + + dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n", + __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); + if (is_ntsc) { + /* NTSC */ + if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { + f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; + if (b_any_field) { + field = V4L2_FIELD_SEQ_TB; + } else if (!((field == V4L2_FIELD_INTERLACED) || + (field == V4L2_FIELD_SEQ_TB) || + (field == V4L2_FIELD_INTERLACED_TB))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } else { + f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; + if (b_any_field) { + field = V4L2_FIELD_TOP; + } else if (!((field == V4L2_FIELD_TOP) || + (field == V4L2_FIELD_BOTTOM))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + + } + if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; + else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_2CIFS_NTSC; + else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; + else + f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; + } else { + /* PAL */ + if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { + f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; + if (b_any_field) { + field = V4L2_FIELD_SEQ_TB; + } else if (!((field == V4L2_FIELD_INTERLACED) || + (field == V4L2_FIELD_SEQ_TB) || + (field == V4L2_FIELD_INTERLACED_TB))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } else { + f->fmt.pix.height = NUM_LINES_1CIFS_PAL; + if (b_any_field) { + field = V4L2_FIELD_TOP; + } else if (!((field == V4L2_FIELD_TOP) || + (field == V4L2_FIELD_BOTTOM))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } + if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { + f->fmt.pix.width = LINE_SZ_4CIFS_PAL; + field = V4L2_FIELD_SEQ_TB; + } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { + f->fmt.pix.width = LINE_SZ_2CIFS_PAL; + field = V4L2_FIELD_TOP; + } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { + f->fmt.pix.width = LINE_SZ_1CIFS_PAL; + field = V4L2_FIELD_TOP; + } else { + f->fmt.pix.width = LINE_SZ_1CIFS_PAL; + field = V4L2_FIELD_TOP; + } + } + f->fmt.pix.field = field; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + dprintk(50, "%s: set width %d height %d field %d\n", __func__, + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + const struct s2255_fmt *fmt; + struct videobuf_queue *q = &fh->vb_vidq; + struct s2255_mode mode; + int ret; + + ret = vidioc_try_fmt_vid_cap(file, fh, f); + + if (ret < 0) + return ret; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + if (fmt == NULL) + return -EINVAL; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + dprintk(1, "queue busy\n"); + ret = -EBUSY; + goto out_s_fmt; + } + + if (res_locked(fh)) { + dprintk(1, "%s: channel busy\n", __func__); + ret = -EBUSY; + goto out_s_fmt; + } + mode = channel->mode; + channel->fmt = fmt; + channel->width = f->fmt.pix.width; + channel->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + if (channel->width > norm_minw(&channel->vdev)) { + if (channel->height > norm_minh(&channel->vdev)) { + if (channel->cap_parm.capturemode & + V4L2_MODE_HIGHQUALITY) + mode.scale = SCALE_4CIFSI; + else + mode.scale = SCALE_4CIFS; + } else + mode.scale = SCALE_2CIFS; + + } else { + mode.scale = SCALE_1CIFS; + } + /* color mode */ + switch (channel->fmt->fourcc) { + case V4L2_PIX_FMT_GREY: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_Y8; + break; + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_MJPEG: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_JPG; + mode.color |= (channel->jc.quality << 8); + break; + case V4L2_PIX_FMT_YUV422P: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPL; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPK; + break; + } + if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR)) + mode.restart = 1; + else if (mode.scale != channel->mode.scale) + mode.restart = 1; + else if (mode.format != channel->mode.format) + mode.restart = 1; + channel->mode = mode; + (void) s2255_set_mode(channel, &mode); + ret = 0; +out_s_fmt: + mutex_unlock(&q->vb_lock); + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_reqbufs(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_querybuf(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_qbuf(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +/* write to the configuration pipe, synchronously */ +static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, + int size) +{ + int pipe; + int done; + long retval = -1; + if (udev) { + pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); + retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); + } + return retval; +} + +static u32 get_transfer_size(struct s2255_mode *mode) +{ + int linesPerFrame = LINE_SZ_DEF; + int pixelsPerLine = NUM_LINES_DEF; + u32 outImageSize; + u32 usbInSize; + unsigned int mask_mult; + + if (mode == NULL) + return 0; + + if (mode->format == FORMAT_NTSC) { + switch (mode->scale) { + case SCALE_4CIFS: + case SCALE_4CIFSI: + linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; + pixelsPerLine = LINE_SZ_4CIFS_NTSC; + break; + case SCALE_2CIFS: + linesPerFrame = NUM_LINES_2CIFS_NTSC; + pixelsPerLine = LINE_SZ_2CIFS_NTSC; + break; + case SCALE_1CIFS: + linesPerFrame = NUM_LINES_1CIFS_NTSC; + pixelsPerLine = LINE_SZ_1CIFS_NTSC; + break; + default: + break; + } + } else if (mode->format == FORMAT_PAL) { + switch (mode->scale) { + case SCALE_4CIFS: + case SCALE_4CIFSI: + linesPerFrame = NUM_LINES_4CIFS_PAL * 2; + pixelsPerLine = LINE_SZ_4CIFS_PAL; + break; + case SCALE_2CIFS: + linesPerFrame = NUM_LINES_2CIFS_PAL; + pixelsPerLine = LINE_SZ_2CIFS_PAL; + break; + case SCALE_1CIFS: + linesPerFrame = NUM_LINES_1CIFS_PAL; + pixelsPerLine = LINE_SZ_1CIFS_PAL; + break; + default: + break; + } + } + outImageSize = linesPerFrame * pixelsPerLine; + if ((mode->color & MASK_COLOR) != COLOR_Y8) { + /* 2 bytes/pixel if not monochrome */ + outImageSize *= 2; + } + + /* total bytes to send including prefix and 4K padding; + must be a multiple of USB_READ_SIZE */ + usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ + mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; + /* if size not a multiple of USB_READ_SIZE */ + if (usbInSize & ~mask_mult) + usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); + return usbInSize; +} + +static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) +{ + struct device *dev = &sdev->udev->dev; + dev_info(dev, "------------------------------------------------\n"); + dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); + dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); + dev_info(dev, "bright: 0x%x\n", mode->bright); + dev_info(dev, "------------------------------------------------\n"); +} + +/* + * set mode is the function which controls the DSP. + * the restart parameter in struct s2255_mode should be set whenever + * the image size could change via color format, video system or image + * size. + * When the restart parameter is set, we sleep for ONE frame to allow the + * DSP time to get the new frame + */ +static int s2255_set_mode(struct s2255_channel *channel, + struct s2255_mode *mode) +{ + int res; + __le32 *buffer; + unsigned long chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + dprintk(3, "%s channel: %d\n", __func__, channel->idx); + /* if JPEG, set the quality */ + if ((mode->color & MASK_COLOR) == COLOR_JPG) { + mode->color &= ~MASK_COLOR; + mode->color |= COLOR_JPG; + mode->color &= ~MASK_JPG_QUALITY; + mode->color |= (channel->jc.quality << 8); + } + /* save the mode */ + channel->mode = *mode; + channel->req_image_size = get_transfer_size(mode); + dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size); + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + /* set the mode */ + buffer[0] = IN_DATA_TOKEN; + buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[2] = CMD_SET_MODE; + memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode)); + channel->setmode_ready = 0; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (debug) + s2255_print_cfg(dev, mode); + kfree(buffer); + /* wait at least 3 frames before continuing */ + if (mode->restart) { + wait_event_timeout(channel->wait_setmode, + (channel->setmode_ready != 0), + msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); + if (channel->setmode_ready != 1) { + printk(KERN_DEBUG "s2255: no set mode response\n"); + res = -EFAULT; + } + } + /* clear the restart flag */ + channel->mode.restart = 0; + dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res); + return res; +} + +static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus) +{ + int res; + __le32 *buffer; + u32 chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + dprintk(4, "%s chan %d\n", __func__, channel->idx); + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + /* form the get vid status command */ + buffer[0] = IN_DATA_TOKEN; + buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[2] = CMD_STATUS; + *pstatus = 0; + channel->vidstatus_ready = 0; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + kfree(buffer); + wait_event_timeout(channel->wait_vidstatus, + (channel->vidstatus_ready != 0), + msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); + if (channel->vidstatus_ready != 1) { + printk(KERN_DEBUG "s2255: no vidstatus response\n"); + res = -EFAULT; + } + *pstatus = channel->vidstatus; + dprintk(4, "%s, vid status %d\n", __func__, *pstatus); + return res; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + int res; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + int j; + dprintk(4, "%s\n", __func__); + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&dev->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (i != fh->type) { + dev_err(&dev->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + + if (!res_get(fh)) { + s2255_dev_err(&dev->udev->dev, "stream busy\n"); + return -EBUSY; + } + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; + channel->frame_count = 0; + for (j = 0; j < SYS_FRAMES; j++) { + channel->buffer.frame[j].ulState = S2255_READ_IDLE; + channel->buffer.frame[j].cur_size = 0; + } + res = videobuf_streamon(&fh->vb_vidq); + if (res == 0) { + s2255_start_acquire(channel); + channel->b_acquire = 1; + } else + res_free(fh); + + return res; +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct s2255_fh *fh = priv; + dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx); + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + printk(KERN_ERR "invalid fh type0\n"); + return -EINVAL; + } + if (i != fh->type) { + printk(KERN_ERR "invalid type i\n"); + return -EINVAL; + } + s2255_stop_acquire(fh->channel); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct s2255_fh *fh = priv; + struct s2255_mode mode; + struct videobuf_queue *q = &fh->vb_vidq; + int ret = 0; + mutex_lock(&q->vb_lock); + if (videobuf_queue_is_busy(q)) { + dprintk(1, "queue busy\n"); + ret = -EBUSY; + goto out_s_std; + } + if (res_locked(fh)) { + dprintk(1, "can't change standard after started\n"); + ret = -EBUSY; + goto out_s_std; + } + mode = fh->channel->mode; + if (*i & V4L2_STD_NTSC) { + dprintk(4, "%s NTSC\n", __func__); + /* if changing format, reset frame decimation/intervals */ + if (mode.format != FORMAT_NTSC) { + mode.restart = 1; + mode.format = FORMAT_NTSC; + mode.fdec = FDEC_1; + } + } else if (*i & V4L2_STD_PAL) { + dprintk(4, "%s PAL\n", __func__); + if (mode.format != FORMAT_PAL) { + mode.restart = 1; + mode.format = FORMAT_PAL; + mode.fdec = FDEC_1; + } + } else { + ret = -EINVAL; + } + if (mode.restart) + s2255_set_mode(fh->channel, &mode); +out_s_std: + mutex_unlock(&q->vb_lock); + return ret; +} + +/* Sensoray 2255 is a multiple channel capture device. + It does not have a "crossbar" of inputs. + We use one V4L device per channel. The user must + be aware that certain combinations are not allowed. + For instance, you cannot do full FPS on more than 2 channels(2 videodevs) + at once in color(you can do full fps on 4 channels with greyscale. +*/ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + u32 status = 0; + if (inp->index != 0) + return -EINVAL; + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = S2255_NORMS; + inp->status = 0; + if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { + int rc; + rc = s2255_cmd_status(fh->channel, &status); + dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); + if (rc == 0) + inp->status = (status & 0x01) ? 0 + : V4L2_IN_ST_NO_SIGNAL; + } + switch (dev->pid) { + case 0x2255: + default: + strlcpy(inp->name, "Composite", sizeof(inp->name)); + break; + case 0x2257: + strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video", + sizeof(inp->name)); + break; + } + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return 0; +} + +/* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = fh->dev; + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); + break; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); + break; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); + break; + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE); + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (channel->idx > 1)) + return -EINVAL; + strlcpy(qc->name, "Color Filter", sizeof(qc->name)); + qc->type = V4L2_CTRL_TYPE_MENU; + qc->minimum = 0; + qc->maximum = 1; + qc->step = 1; + qc->default_value = 1; + qc->flags = 0; + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d\n", __func__, qc->id); + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = channel->mode.bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = channel->mode.contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = channel->mode.saturation; + break; + case V4L2_CID_HUE: + ctrl->value = channel->mode.hue; + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (channel->idx > 1)) + return -EINVAL; + ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + struct s2255_mode mode; + mode = channel->mode; + dprintk(4, "%s\n", __func__); + /* update the mode to the corresponding value */ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + mode.bright = ctrl->value; + break; + case V4L2_CID_CONTRAST: + mode.contrast = ctrl->value; + break; + case V4L2_CID_HUE: + mode.hue = ctrl->value; + break; + case V4L2_CID_SATURATION: + mode.saturation = ctrl->value; + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (channel->idx > 1)) + return -EINVAL; + mode.color &= ~MASK_INPUT_TYPE; + mode.color |= ((ctrl->value ? 0 : 1) << 16); + break; + default: + return -EINVAL; + } + mode.restart = 0; + /* set mode here. Note: stream does not need restarted. + some V4L programs restart stream unnecessarily + after a s_crtl. + */ + s2255_set_mode(fh->channel, &mode); + return 0; +} + +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + *jc = channel->jc; + dprintk(2, "%s: quality %d\n", __func__, jc->quality); + return 0; +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + if (jc->quality < 0 || jc->quality > 100) + return -EINVAL; + channel->jc.quality = jc->quality; + dprintk(2, "%s: quality %d\n", __func__, jc->quality); + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct s2255_fh *fh = priv; + __u32 def_num, def_dem; + struct s2255_channel *channel = fh->channel; + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(sp, 0, sizeof(struct v4l2_streamparm)); + sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + sp->parm.capture.capturemode = channel->cap_parm.capturemode; + def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000; + sp->parm.capture.timeperframe.denominator = def_dem; + switch (channel->mode.fdec) { + default: + case FDEC_1: + sp->parm.capture.timeperframe.numerator = def_num; + break; + case FDEC_2: + sp->parm.capture.timeperframe.numerator = def_num * 2; + break; + case FDEC_3: + sp->parm.capture.timeperframe.numerator = def_num * 3; + break; + case FDEC_5: + sp->parm.capture.timeperframe.numerator = def_num * 5; + break; + } + dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator); + return 0; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + struct s2255_mode mode; + int fdec = FDEC_1; + __u32 def_num, def_dem; + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + mode = channel->mode; + /* high quality capture mode requires a stream restart */ + if (channel->cap_parm.capturemode + != sp->parm.capture.capturemode && res_locked(fh)) + return -EBUSY; + def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; + if (def_dem != sp->parm.capture.timeperframe.denominator) + sp->parm.capture.timeperframe.numerator = def_num; + else if (sp->parm.capture.timeperframe.numerator <= def_num) + sp->parm.capture.timeperframe.numerator = def_num; + else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { + sp->parm.capture.timeperframe.numerator = def_num * 2; + fdec = FDEC_2; + } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { + sp->parm.capture.timeperframe.numerator = def_num * 3; + fdec = FDEC_3; + } else { + sp->parm.capture.timeperframe.numerator = def_num * 5; + fdec = FDEC_5; + } + mode.fdec = fdec; + sp->parm.capture.timeperframe.denominator = def_dem; + s2255_set_mode(channel, &mode); + dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", + __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator, fdec); + return 0; +} + +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fe) +{ + int is_ntsc = 0; +#define NUM_FRAME_ENUMS 4 + int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; + if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS) + return -EINVAL; + switch (fe->width) { + case 640: + if (fe->height != 240 && fe->height != 480) + return -EINVAL; + is_ntsc = 1; + break; + case 320: + if (fe->height != 240) + return -EINVAL; + is_ntsc = 1; + break; + case 704: + if (fe->height != 288 && fe->height != 576) + return -EINVAL; + break; + case 352: + if (fe->height != 288) + return -EINVAL; + break; + default: + return -EINVAL; + } + fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fe->discrete.denominator = is_ntsc ? 30000 : 25000; + fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; + dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator, + fe->discrete.denominator); + return 0; +} + +static int __s2255_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct s2255_channel *channel = video_drvdata(file); + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); + struct s2255_fh *fh; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int state; + dprintk(1, "s2255: open called (dev=%s)\n", + video_device_node_name(vdev)); + state = atomic_read(&dev->fw_data->fw_state); + switch (state) { + case S2255_FW_DISCONNECTING: + return -ENODEV; + case S2255_FW_FAILED: + s2255_dev_err(&dev->udev->dev, + "firmware load failed. retrying.\n"); + s2255_fwload_start(dev, 1); + wait_event_timeout(dev->fw_data->wait_fw, + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); + break; + case S2255_FW_NOTLOADED: + case S2255_FW_LOADED_DSPWAIT: + /* give S2255_LOAD_TIMEOUT time for firmware to load in case + driver loaded and then device immediately opened */ + printk(KERN_INFO "%s waiting for firmware load\n", __func__); + wait_event_timeout(dev->fw_data->wait_fw, + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); + break; + case S2255_FW_SUCCESS: + default: + break; + } + /* state may have changed in above switch statement */ + switch (state) { + case S2255_FW_SUCCESS: + break; + case S2255_FW_FAILED: + printk(KERN_INFO "2255 firmware load failed.\n"); + return -ENODEV; + case S2255_FW_DISCONNECTING: + printk(KERN_INFO "%s: disconnecting\n", __func__); + return -ENODEV; + case S2255_FW_LOADED_DSPWAIT: + case S2255_FW_NOTLOADED: + printk(KERN_INFO "%s: firmware not loaded yet" + "please try again later\n", + __func__); + /* + * Timeout on firmware load means device unusable. + * Set firmware failure state. + * On next s2255_open the firmware will be reloaded. + */ + atomic_set(&dev->fw_data->fw_state, + S2255_FW_FAILED); + return -EAGAIN; + default: + printk(KERN_INFO "%s: unknown state\n", __func__); + return -EFAULT; + } + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + file->private_data = fh; + fh->dev = dev; + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->channel = channel; + if (!channel->configured) { + /* configure channel to default state */ + channel->fmt = &formats[0]; + s2255_set_mode(channel, &channel->mode); + channel->configured = 1; + } + dprintk(1, "%s: dev=%s type=%s\n", __func__, + video_device_node_name(vdev), v4l2_type_names[type]); + dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__, + (unsigned long)fh, (unsigned long)dev, + (unsigned long)&channel->vidq); + dprintk(4, "%s: list_empty active=%d\n", __func__, + list_empty(&channel->vidq.active)); + videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct s2255_buffer), + fh, vdev->lock); + return 0; +} + +static int s2255_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + int ret; + + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = __s2255_open(file); + mutex_unlock(vdev->lock); + return ret; +} + +static unsigned int s2255_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + int rc; + dprintk(100, "%s\n", __func__); + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + mutex_lock(&dev->lock); + rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); + mutex_unlock(&dev->lock); + return rc; +} + +static void s2255_destroy(struct s2255_dev *dev) +{ + /* board shutdown stops the read pipe if it is running */ + s2255_board_shutdown(dev); + /* make sure firmware still not trying to load */ + del_timer(&dev->timer); /* only started in .probe and .open */ + if (dev->fw_data->fw_urb) { + usb_kill_urb(dev->fw_data->fw_urb); + usb_free_urb(dev->fw_data->fw_urb); + dev->fw_data->fw_urb = NULL; + } + release_firmware(dev->fw_data->fw); + kfree(dev->fw_data->pfw_data); + kfree(dev->fw_data); + /* reset the DSP so firmware can be reloaded next time */ + s2255_reset_dsppower(dev); + mutex_destroy(&dev->lock); + usb_put_dev(dev->udev); + v4l2_device_unregister(&dev->v4l2_dev); + dprintk(1, "%s", __func__); + kfree(dev); +} + +static int s2255_release(struct file *file) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + struct s2255_channel *channel = fh->channel; + if (!dev) + return -ENODEV; + mutex_lock(&dev->lock); + /* turn off stream */ + if (res_check(fh)) { + if (channel->b_acquire) + s2255_stop_acquire(fh->channel); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + } + videobuf_mmap_free(&fh->vb_vidq); + mutex_unlock(&dev->lock); + dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); + kfree(fh); + return 0; +} + +static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + int ret; + + if (!fh) + return -ENODEV; + dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma); + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__, + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; +} + +static const struct v4l2_file_operations s2255_fops_v4l = { + .owner = THIS_MODULE, + .open = s2255_open, + .release = s2255_release, + .poll = s2255_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = s2255_mmap_v4l, +}; + +static const struct v4l2_ioctl_ops s2255_ioctl_ops = { + .vidioc_querymenu = vidioc_querymenu, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, +}; + +static void s2255_video_device_release(struct video_device *vdev) +{ + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); + dprintk(4, "%s, chnls: %d \n", __func__, + atomic_read(&dev->num_channels)); + if (atomic_dec_and_test(&dev->num_channels)) + s2255_destroy(dev); + return; +} + +static struct video_device template = { + .name = "s2255v", + .fops = &s2255_fops_v4l, + .ioctl_ops = &s2255_ioctl_ops, + .release = s2255_video_device_release, + .tvnorms = S2255_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; + +static int s2255_probe_v4l(struct s2255_dev *dev) +{ + int ret; + int i; + int cur_nr = video_nr; + struct s2255_channel *channel; + ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); + if (ret) + return ret; + /* initialize all video 4 linux */ + /* register 4 video devices */ + for (i = 0; i < MAX_CHANNELS; i++) { + channel = &dev->channel[i]; + INIT_LIST_HEAD(&channel->vidq.active); + channel->vidq.dev = dev; + /* register 4 video devices */ + channel->vdev = template; + channel->vdev.lock = &dev->lock; + channel->vdev.v4l2_dev = &dev->v4l2_dev; + video_set_drvdata(&channel->vdev, channel); + if (video_nr == -1) + ret = video_register_device(&channel->vdev, + VFL_TYPE_GRABBER, + video_nr); + else + ret = video_register_device(&channel->vdev, + VFL_TYPE_GRABBER, + cur_nr + i); + + if (ret) { + dev_err(&dev->udev->dev, + "failed to register video device!\n"); + break; + } + atomic_inc(&dev->num_channels); + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(&channel->vdev)); + + } + printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n", + S2255_VERSION); + /* if no channels registered, return error and probe will fail*/ + if (atomic_read(&dev->num_channels) == 0) { + v4l2_device_unregister(&dev->v4l2_dev); + return ret; + } + if (atomic_read(&dev->num_channels) != MAX_CHANNELS) + printk(KERN_WARNING "s2255: Not all channels available.\n"); + return 0; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process( call this + * function again). + * + * Received frame structure: + * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) + * bytes 4-7: channel: 0-3 + * bytes 8-11: payload size: size of the frame + * bytes 12-payloadsize+12: frame data + */ +static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) +{ + char *pdest; + u32 offset = 0; + int bframe = 0; + char *psrc; + unsigned long copy_size; + unsigned long size; + s32 idx = -1; + struct s2255_framei *frm; + unsigned char *pdata; + struct s2255_channel *channel; + dprintk(100, "buffer to user\n"); + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; + if (frm->ulState == S2255_READ_IDLE) { + int jj; + unsigned int cc; + __le32 *pdword; /*data from dsp is little endian */ + int payload; + /* search for marker codes */ + pdata = (unsigned char *)pipe_info->transfer_buffer; + pdword = (__le32 *)pdata; + for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { + switch (*pdword) { + case S2255_MARKER_FRAME: + dprintk(4, "found frame marker at offset:" + " %d [%x %x]\n", jj, pdata[0], + pdata[1]); + offset = jj + PREFIX_SIZE; + bframe = 1; + cc = le32_to_cpu(pdword[1]); + if (cc >= MAX_CHANNELS) { + printk(KERN_ERR + "bad channel\n"); + return -EINVAL; + } + /* reverse it */ + dev->cc = G_chnmap[cc]; + channel = &dev->channel[dev->cc]; + payload = le32_to_cpu(pdword[3]); + if (payload > channel->req_image_size) { + channel->bad_payload++; + /* discard the bad frame */ + return -EINVAL; + } + channel->pkt_size = payload; + channel->jpg_size = le32_to_cpu(pdword[4]); + break; + case S2255_MARKER_RESPONSE: + + pdata += DEF_USB_BLOCK; + jj += DEF_USB_BLOCK; + if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) + break; + cc = G_chnmap[le32_to_cpu(pdword[1])]; + if (cc >= MAX_CHANNELS) + break; + channel = &dev->channel[cc]; + switch (pdword[2]) { + case S2255_RESPONSE_SETMODE: + /* check if channel valid */ + /* set mode ready */ + channel->setmode_ready = 1; + wake_up(&channel->wait_setmode); + dprintk(5, "setmode ready %d\n", cc); + break; + case S2255_RESPONSE_FW: + dev->chn_ready |= (1 << cc); + if ((dev->chn_ready & 0x0f) != 0x0f) + break; + /* all channels ready */ + printk(KERN_INFO "s2255: fw loaded\n"); + atomic_set(&dev->fw_data->fw_state, + S2255_FW_SUCCESS); + wake_up(&dev->fw_data->wait_fw); + break; + case S2255_RESPONSE_STATUS: + channel->vidstatus = le32_to_cpu(pdword[3]); + channel->vidstatus_ready = 1; + wake_up(&channel->wait_vidstatus); + dprintk(5, "got vidstatus %x chan %d\n", + le32_to_cpu(pdword[3]), cc); + break; + default: + printk(KERN_INFO "s2255 unknown resp\n"); + } + default: + pdata++; + break; + } + if (bframe) + break; + } /* for */ + if (!bframe) + return -EINVAL; + } + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; + /* search done. now find out if should be acquiring on this channel */ + if (!channel->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = S2255_READ_IDLE; + return -EINVAL; + } + + if (frm->ulState == S2255_READ_IDLE) { + frm->ulState = S2255_READ_FRAME; + frm->cur_size = 0; + } + + /* skip the marker 512 bytes (and offset if out of sync) */ + psrc = (u8 *)pipe_info->transfer_buffer + offset; + + + if (frm->lpvbits == NULL) { + dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d", + frm, dev, dev->cc, idx); + return -ENOMEM; + } + + pdest = frm->lpvbits + frm->cur_size; + + copy_size = (pipe_info->cur_transfer_size - offset); + + size = channel->pkt_size - PREFIX_SIZE; + + /* sanity check on pdest */ + if ((copy_size + frm->cur_size) < channel->req_image_size) + memcpy(pdest, psrc, copy_size); + + frm->cur_size += copy_size; + dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); + + if (frm->cur_size >= size) { + dprintk(2, "****************[%d]Buffer[%d]full*************\n", + dev->cc, idx); + channel->last_frame = channel->cur_frame; + channel->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if ((channel->cur_frame == SYS_FRAMES) || + (channel->cur_frame == channel->buffer.dwFrames)) + channel->cur_frame = 0; + /* frame ready */ + if (channel->b_acquire) + s2255_got_frame(channel, channel->jpg_size); + channel->frame_count++; + frm->ulState = S2255_READ_IDLE; + frm->cur_size = 0; + + } + /* done successfully */ + return 0; +} + +static void s2255_read_video_callback(struct s2255_dev *dev, + struct s2255_pipeinfo *pipe_info) +{ + int res; + dprintk(50, "callback read video \n"); + + if (dev->cc >= MAX_CHANNELS) { + dev->cc = 0; + dev_err(&dev->udev->dev, "invalid channel\n"); + return; + } + /* otherwise copy to the system buffers */ + res = save_frame(dev, pipe_info); + if (res != 0) + dprintk(4, "s2255: read callback failed\n"); + + dprintk(50, "callback read video done\n"); + return; +} + +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, + u16 Index, u16 Value, void *TransferBuffer, + s32 TransferBufferLength, int bOut) +{ + int r; + if (!bOut) { + r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + Request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_IN, + Value, Index, TransferBuffer, + TransferBufferLength, HZ * 5); + } else { + r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + Value, Index, TransferBuffer, + TransferBufferLength, HZ * 5); + } + return r; +} + +/* + * retrieve FX2 firmware version. future use. + * @param dev pointer to device extension + * @return -1 for fail, else returns firmware version as an int(16 bits) + */ +static int s2255_get_fx2fw(struct s2255_dev *dev) +{ + int fw; + int ret; + unsigned char transBuffer[64]; + ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2, + S2255_VR_IN); + if (ret < 0) + dprintk(2, "get fw error: %x\n", ret); + fw = transBuffer[0] + (transBuffer[1] << 8); + dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); + return fw; +} + +/* + * Create the system ring buffer to copy frames into from the + * usb read pipe. + */ +static int s2255_create_sys_buffers(struct s2255_channel *channel) +{ + unsigned long i; + unsigned long reqsize; + dprintk(1, "create sys buffers\n"); + channel->buffer.dwFrames = SYS_FRAMES; + /* always allocate maximum size(PAL) for system buffers */ + reqsize = SYS_FRAMES_MAXSIZE; + + if (reqsize > SYS_FRAMES_MAXSIZE) + reqsize = SYS_FRAMES_MAXSIZE; + + for (i = 0; i < SYS_FRAMES; i++) { + /* allocate the frames */ + channel->buffer.frame[i].lpvbits = vmalloc(reqsize); + dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n", + &channel->buffer.frame[i], channel->idx, i, + channel->buffer.frame[i].lpvbits); + channel->buffer.frame[i].size = reqsize; + if (channel->buffer.frame[i].lpvbits == NULL) { + printk(KERN_INFO "out of memory. using less frames\n"); + channel->buffer.dwFrames = i; + break; + } + } + + /* make sure internal states are set */ + for (i = 0; i < SYS_FRAMES; i++) { + channel->buffer.frame[i].ulState = 0; + channel->buffer.frame[i].cur_size = 0; + } + + channel->cur_frame = 0; + channel->last_frame = -1; + return 0; +} + +static int s2255_release_sys_buffers(struct s2255_channel *channel) +{ + unsigned long i; + dprintk(1, "release sys buffers\n"); + for (i = 0; i < SYS_FRAMES; i++) { + if (channel->buffer.frame[i].lpvbits) { + dprintk(1, "vfree %p\n", + channel->buffer.frame[i].lpvbits); + vfree(channel->buffer.frame[i].lpvbits); + } + channel->buffer.frame[i].lpvbits = NULL; + } + return 0; +} + +static int s2255_board_init(struct s2255_dev *dev) +{ + struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; + int fw_ver; + int j; + struct s2255_pipeinfo *pipe = &dev->pipe; + dprintk(4, "board init: %p", dev); + memset(pipe, 0, sizeof(*pipe)); + pipe->dev = dev; + pipe->cur_transfer_size = S2255_USB_XFER_SIZE; + pipe->max_transfer_size = S2255_USB_XFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + dprintk(1, "out of memory!\n"); + return -ENOMEM; + } + /* query the firmware */ + fw_ver = s2255_get_fx2fw(dev); + + printk(KERN_INFO "s2255: usb firmware version %d.%d\n", + (fw_ver >> 8) & 0xff, + fw_ver & 0xff); + + if (fw_ver < S2255_CUR_USB_FWVER) + printk(KERN_INFO "s2255: newer USB firmware available\n"); + + for (j = 0; j < MAX_CHANNELS; j++) { + struct s2255_channel *channel = &dev->channel[j]; + channel->b_acquire = 0; + channel->mode = mode_def; + if (dev->pid == 0x2257 && j > 1) + channel->mode.color |= (1 << 16); + channel->jc.quality = S2255_DEF_JPEG_QUAL; + channel->width = LINE_SZ_4CIFS_NTSC; + channel->height = NUM_LINES_4CIFS_NTSC * 2; + channel->fmt = &formats[0]; + channel->mode.restart = 1; + channel->req_image_size = get_transfer_size(&mode_def); + channel->frame_count = 0; + /* create the system buffers */ + s2255_create_sys_buffers(channel); + } + /* start read pipe */ + s2255_start_readpipe(dev); + dprintk(1, "%s: success\n", __func__); + return 0; +} + +static int s2255_board_shutdown(struct s2255_dev *dev) +{ + u32 i; + dprintk(1, "%s: dev: %p", __func__, dev); + + for (i = 0; i < MAX_CHANNELS; i++) { + if (dev->channel[i].b_acquire) + s2255_stop_acquire(&dev->channel[i]); + } + s2255_stop_readpipe(dev); + for (i = 0; i < MAX_CHANNELS; i++) + s2255_release_sys_buffers(&dev->channel[i]); + /* release transfer buffer */ + kfree(dev->pipe.transfer_buffer); + return 0; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct s2255_pipeinfo *pipe_info; + struct s2255_dev *dev; + int status; + int pipe; + pipe_info = purb->context; + dprintk(100, "%s: urb:%p, status %d\n", __func__, purb, + purb->status); + if (pipe_info == NULL) { + dev_err(&purb->dev->dev, "no context!\n"); + return; + } + + dev = pipe_info->dev; + if (dev == NULL) { + dev_err(&purb->dev->dev, "no context!\n"); + return; + } + status = purb->status; + /* if shutting down, do not resubmit, exit immediately */ + if (status == -ESHUTDOWN) { + dprintk(2, "%s: err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + dprintk(2, "%s: exiting USB pipe", __func__); + return; + } + + if (status == 0) + s2255_read_video_callback(dev, pipe_info); + else { + pipe_info->err_count++; + dprintk(1, "%s: failed URB %d\n", __func__, status); + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) { + dev_err(&dev->udev->dev, "error submitting urb\n"); + } + } else { + dprintk(2, "%s :complete state 0\n", __func__); + } + return; +} + +static int s2255_start_readpipe(struct s2255_dev *dev) +{ + int pipe; + int retval; + struct s2255_pipeinfo *pipe_info = &dev->pipe; + pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); + dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint); + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&dev->udev->dev, + "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR "s2255: start read pipe failed\n"); + return retval; + } + return 0; +} + +/* starts acquisition process */ +static int s2255_start_acquire(struct s2255_channel *channel) +{ + unsigned char *buffer; + int res; + unsigned long chn_rev; + int j; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; + for (j = 0; j < SYS_FRAMES; j++) { + channel->buffer.frame[j].ulState = 0; + channel->buffer.frame[j].cur_size = 0; + } + + /* send the start command */ + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_START; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (res != 0) + dev_err(&dev->udev->dev, "CMD_START error\n"); + + dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res); + kfree(buffer); + return 0; +} + +static int s2255_stop_acquire(struct s2255_channel *channel) +{ + unsigned char *buffer; + int res; + unsigned long chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + /* send the stop command */ + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_STOP; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (res != 0) + dev_err(&dev->udev->dev, "CMD_STOP error\n"); + kfree(buffer); + channel->b_acquire = 0; + dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res); + return res; +} + +static void s2255_stop_readpipe(struct s2255_dev *dev) +{ + struct s2255_pipeinfo *pipe = &dev->pipe; + + pipe->state = 0; + if (pipe->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe->stream_urb); + usb_free_urb(pipe->stream_urb); + pipe->stream_urb = NULL; + } + dprintk(4, "%s", __func__); + return; +} + +static void s2255_fwload_start(struct s2255_dev *dev, int reset) +{ + if (reset) + s2255_reset_dsppower(dev); + dev->fw_data->fw_size = dev->fw_data->fw->size; + atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); + memcpy(dev->fw_data->pfw_data, + dev->fw_data->fw->data, CHUNK_SIZE); + dev->fw_data->fw_loaded = CHUNK_SIZE; + usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev, + usb_sndbulkpipe(dev->udev, 2), + dev->fw_data->pfw_data, + CHUNK_SIZE, s2255_fwchunk_complete, + dev->fw_data); + mod_timer(&dev->timer, jiffies + HZ); +} + +/* standard usb probe function */ +static int s2255_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct s2255_dev *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + int retval = -ENOMEM; + __le32 *pdata; + int fw_size; + dprintk(2, "%s\n", __func__); + /* allocate memory for our device state and initialize it to zero */ + dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); + if (dev == NULL) { + s2255_dev_err(&interface->dev, "out of memory\n"); + return -ENOMEM; + } + atomic_set(&dev->num_channels, 0); + dev->pid = id->idProduct; + dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); + if (!dev->fw_data) + goto errorFWDATA1; + mutex_init(&dev->lock); + /* grab usb_device and save it */ + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + if (dev->udev == NULL) { + dev_err(&interface->dev, "null usb device\n"); + retval = -ENODEV; + goto errorUDEV; + } + dprintk(1, "dev: %p, udev %p interface %p\n", dev, + dev->udev, interface); + dev->interface = interface; + /* set up the endpoint information */ + iface_desc = interface->cur_altsetting; + dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + dev->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!dev->read_endpoint) { + dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); + goto errorEP; + } + init_timer(&dev->timer); + dev->timer.function = s2255_timer; + dev->timer.data = (unsigned long)dev->fw_data; + init_waitqueue_head(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) { + struct s2255_channel *channel = &dev->channel[i]; + dev->channel[i].idx = i; + init_waitqueue_head(&channel->wait_setmode); + init_waitqueue_head(&channel->wait_vidstatus); + } + + dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->fw_data->fw_urb) { + dev_err(&interface->dev, "out of memory!\n"); + goto errorFWURB; + } + + dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); + if (!dev->fw_data->pfw_data) { + dev_err(&interface->dev, "out of memory!\n"); + goto errorFWDATA2; + } + /* load the first chunk */ + if (request_firmware(&dev->fw_data->fw, + FIRMWARE_FILE_NAME, &dev->udev->dev)) { + printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); + goto errorREQFW; + } + /* check the firmware is valid */ + fw_size = dev->fw_data->fw->size; + pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; + + if (*pdata != S2255_FW_MARKER) { + printk(KERN_INFO "Firmware invalid.\n"); + retval = -ENODEV; + goto errorFWMARKER; + } else { + /* make sure firmware is the latest */ + __le32 *pRel; + pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; + printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); + dev->dsp_fw_ver = le32_to_cpu(*pRel); + if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) + printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); + if (dev->pid == 0x2257 && + dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + printk(KERN_WARNING "s2255: 2257 requires firmware %d" + " or above.\n", S2255_MIN_DSP_COLORFILTER); + } + usb_reset_device(dev->udev); + /* load 2255 board specific */ + retval = s2255_board_init(dev); + if (retval) + goto errorBOARDINIT; + spin_lock_init(&dev->slock); + s2255_fwload_start(dev, 0); + /* loads v4l specific */ + retval = s2255_probe_v4l(dev); + if (retval) + goto errorBOARDINIT; + dev_info(&interface->dev, "Sensoray 2255 detected\n"); + return 0; +errorBOARDINIT: + s2255_board_shutdown(dev); +errorFWMARKER: + release_firmware(dev->fw_data->fw); +errorREQFW: + kfree(dev->fw_data->pfw_data); +errorFWDATA2: + usb_free_urb(dev->fw_data->fw_urb); +errorFWURB: + del_timer(&dev->timer); +errorEP: + usb_put_dev(dev->udev); +errorUDEV: + kfree(dev->fw_data); + mutex_destroy(&dev->lock); +errorFWDATA1: + kfree(dev); + printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval); + return retval; +} + +/* disconnect routine. when board is removed physically or with rmmod */ +static void s2255_disconnect(struct usb_interface *interface) +{ + struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); + int i; + int channels = atomic_read(&dev->num_channels); + mutex_lock(&dev->lock); + v4l2_device_disconnect(&dev->v4l2_dev); + mutex_unlock(&dev->lock); + /*see comments in the uvc_driver.c usb disconnect function */ + atomic_inc(&dev->num_channels); + /* unregister each video device. */ + for (i = 0; i < channels; i++) + video_unregister_device(&dev->channel[i].vdev); + /* wake up any of our timers */ + atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); + wake_up(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) { + dev->channel[i].setmode_ready = 1; + wake_up(&dev->channel[i].wait_setmode); + dev->channel[i].vidstatus_ready = 1; + wake_up(&dev->channel[i].wait_vidstatus); + } + if (atomic_dec_and_test(&dev->num_channels)) + s2255_destroy(dev); + dev_info(&interface->dev, "%s\n", __func__); +} + +static struct usb_driver s2255_driver = { + .name = S2255_DRIVER_NAME, + .probe = s2255_probe, + .disconnect = s2255_disconnect, + .id_table = s2255_table, +}; + +module_usb_driver(s2255_driver); + +MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); +MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(S2255_VERSION); +MODULE_FIRMWARE(FIRMWARE_FILE_NAME); diff --git a/drivers/media/usb/stkwebcam/Kconfig b/drivers/media/usb/stkwebcam/Kconfig new file mode 100644 index 000000000000..2fb0c2b687f4 --- /dev/null +++ b/drivers/media/usb/stkwebcam/Kconfig @@ -0,0 +1,13 @@ +config USB_STKWEBCAM + tristate "USB Syntek DC1125 Camera support" + depends on VIDEO_V4L2 && EXPERIMENTAL + ---help--- + Say Y here if you want to use this type of camera. + Supported devices are typically found in some Asus laptops, + with USB id 174f:a311 and 05e1:0501. Other Syntek cameras + may be supported by the stk11xx driver, from which this is + derived, see + + To compile this driver as a module, choose M here: the + module will be called stkwebcam. + diff --git a/drivers/media/usb/stkwebcam/Makefile b/drivers/media/usb/stkwebcam/Makefile new file mode 100644 index 000000000000..20ef8a4b990c --- /dev/null +++ b/drivers/media/usb/stkwebcam/Makefile @@ -0,0 +1,4 @@ +stkwebcam-objs := stk-webcam.o stk-sensor.o + +obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o + diff --git a/drivers/media/usb/stkwebcam/stk-sensor.c b/drivers/media/usb/stkwebcam/stk-sensor.c new file mode 100644 index 000000000000..e546b014d7ad --- /dev/null +++ b/drivers/media/usb/stkwebcam/stk-sensor.c @@ -0,0 +1,595 @@ +/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams) + * + * Copyright 2007-2008 Jaime Velasco Juan + * + * Some parts derived from ov7670.c: + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * This program is free software; 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. + */ + +/* Controlling the sensor via the STK1125 vendor specific control interface: + * The camera uses an OmniVision sensor and the stk1125 provides an + * SCCB(i2c)-USB bridge which let us program the sensor. + * In my case the sensor id is 0x9652, it can be read from sensor's register + * 0x0A and 0x0B as follows: + * - read register #R: + * output #R to index 0x0208 + * output 0x0070 to index 0x0200 + * input 1 byte from index 0x0201 (some kind of status register) + * until its value is 0x01 + * input 1 byte from index 0x0209. This is the value of #R + * - write value V to register #R + * output #R to index 0x0204 + * output V to index 0x0205 + * output 0x0005 to index 0x0200 + * input 1 byte from index 0x0201 until its value becomes 0x04 + */ + +/* It seems the i2c bus is controlled with these registers */ + +#include "stk-webcam.h" + +#define STK_IIC_BASE (0x0200) +# define STK_IIC_OP (STK_IIC_BASE) +# define STK_IIC_OP_TX (0x05) +# define STK_IIC_OP_RX (0x70) +# define STK_IIC_STAT (STK_IIC_BASE+1) +# define STK_IIC_STAT_TX_OK (0x04) +# define STK_IIC_STAT_RX_OK (0x01) +/* I don't know what does this register. + * when it is 0x00 or 0x01, we cannot talk to the sensor, + * other values work */ +# define STK_IIC_ENABLE (STK_IIC_BASE+2) +# define STK_IIC_ENABLE_NO (0x00) +/* This is what the driver writes in windows */ +# define STK_IIC_ENABLE_YES (0x1e) +/* + * Address of the slave. Seems like the binary driver look for the + * sensor in multiple places, attempting a reset sequence. + * We only know about the ov9650 + */ +# define STK_IIC_ADDR (STK_IIC_BASE+3) +# define STK_IIC_TX_INDEX (STK_IIC_BASE+4) +# define STK_IIC_TX_VALUE (STK_IIC_BASE+5) +# define STK_IIC_RX_INDEX (STK_IIC_BASE+8) +# define STK_IIC_RX_VALUE (STK_IIC_BASE+9) + +#define MAX_RETRIES (50) + +#define SENSOR_ADDRESS (0x60) + +/* From ov7670.c (These registers aren't fully accurate) */ + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define COM1_QFMT 0x20 /* QVGA/QCIF format */ +#define COM1_SKIP_0 0x00 /* Do not skip any row */ +#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */ +#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clock control */ +#define CLK_PLL 0x80 /* Enable internal PLL */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_SXGA 0x00 +#define COM7_FMT_VGA 0x40 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */ +#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGBFIXME 0x20 /* FIXME */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + + + +/* Returns 0 if OK */ +static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) +{ + int i = 0; + int tmpval = 0; + + if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX)) + return 1; + do { + if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) + return 1; + i++; + } while (tmpval == 0 && i < MAX_RETRIES); + if (tmpval != STK_IIC_STAT_TX_OK) { + if (tmpval) + STK_ERROR("stk_sensor_outb failed, status=0x%02x\n", + tmpval); + return 1; + } else + return 0; +} + +static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) +{ + int i = 0; + int tmpval = 0; + + if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX)) + return 1; + do { + if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) + return 1; + i++; + } while (tmpval == 0 && i < MAX_RETRIES); + if (tmpval != STK_IIC_STAT_RX_OK) { + if (tmpval) + STK_ERROR("stk_sensor_inb failed, status=0x%02x\n", + tmpval); + return 1; + } + + if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval)) + return 1; + + *val = (u8) tmpval; + return 0; +} + +static int stk_sensor_write_regvals(struct stk_camera *dev, + struct regval *rv) +{ + int ret; + if (rv == NULL) + return 0; + while (rv->reg != 0xff || rv->val != 0xff) { + ret = stk_sensor_outb(dev, rv->reg, rv->val); + if (ret != 0) + return ret; + rv++; + } + return 0; +} + +int stk_sensor_sleep(struct stk_camera *dev) +{ + u8 tmp; + return stk_sensor_inb(dev, REG_COM2, &tmp) + || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP); +} + +int stk_sensor_wakeup(struct stk_camera *dev) +{ + u8 tmp; + return stk_sensor_inb(dev, REG_COM2, &tmp) + || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP); +} + +static struct regval ov_initvals[] = { + {REG_CLKRC, CLK_PLL}, + {REG_COM11, 0x01}, + {0x6a, 0x7d}, + {REG_AECH, 0x40}, + {REG_GAIN, 0x00}, + {REG_BLUE, 0x80}, + {REG_RED, 0x80}, + /* Do not enable fast AEC for now */ + /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/ + {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC}, + {0x39, 0x50}, {0x38, 0x93}, + {0x37, 0x00}, {0x35, 0x81}, + {REG_COM5, 0x20}, + {REG_COM1, 0x00}, + {REG_COM3, 0x00}, + {REG_COM4, 0x00}, + {REG_PSHFT, 0x00}, + {0x16, 0x07}, + {0x33, 0xe2}, {0x34, 0xbf}, + {REG_COM16, 0x00}, + {0x96, 0x04}, + /* Gamma curve values */ +/* { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, +*/ + {REG_GFIX, 0x40}, + {0x8e, 0x00}, + {REG_COM12, 0x73}, + {0x8f, 0xdf}, {0x8b, 0x06}, + {0x8c, 0x20}, + {0x94, 0x88}, {0x95, 0x88}, +/* {REG_COM15, 0xc1}, TODO */ + {0x29, 0x3f}, + {REG_COM6, 0x42}, + {REG_BD50MAX, 0x80}, + {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92}, + {REG_BD60MAX, 0x0a}, + {0x90, 0x00}, {0x91, 0x00}, + {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00}, + {REG_AEW, 0x68}, {REG_AEB, 0x5c}, + {REG_VPT, 0xc3}, + {REG_COM9, 0x2e}, + {0x2a, 0x00}, {0x2b, 0x00}, + + {0xff, 0xff}, /* END MARKER */ +}; + +/* Probe the I2C bus and initialise the sensor chip */ +int stk_sensor_init(struct stk_camera *dev) +{ + u8 idl = 0; + u8 idh = 0; + + if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) + || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) + || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { + STK_ERROR("Sensor resetting failed\n"); + return -ENODEV; + } + msleep(10); + /* Read the manufacturer ID: ov = 0x7FA2 */ + if (stk_sensor_inb(dev, REG_MIDH, &idh) + || stk_sensor_inb(dev, REG_MIDL, &idl)) { + STK_ERROR("Strange error reading sensor ID\n"); + return -ENODEV; + } + if (idh != 0x7f || idl != 0xa2) { + STK_ERROR("Huh? you don't have a sensor from ovt\n"); + return -ENODEV; + } + if (stk_sensor_inb(dev, REG_PID, &idh) + || stk_sensor_inb(dev, REG_VER, &idl)) { + STK_ERROR("Could not read sensor model\n"); + return -ENODEV; + } + stk_sensor_write_regvals(dev, ov_initvals); + msleep(10); + STK_INFO("OmniVision sensor detected, id %02X%02X" + " at address %x\n", idh, idl, SENSOR_ADDRESS); + return 0; +} + +/* V4L2_PIX_FMT_UYVY */ +static struct regval ov_fmt_uyvy[] = { + {REG_TSLB, TSLB_YLAST|0x08 }, + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; +/* V4L2_PIX_FMT_YUYV */ +static struct regval ov_fmt_yuyv[] = { + {REG_TSLB, 0 }, + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; + +/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */ +static struct regval ov_fmt_rgbr[] = { + { REG_RGB444, 0 }, /* No RGB444 please */ + {REG_TSLB, 0x00}, + { REG_COM1, 0x0 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA }, + { REG_COM15, COM15_RGB565|COM15_R00FF }, + { 0xff, 0xff }, +}; + +/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */ +static struct regval ov_fmt_rgbp[] = { + { REG_RGB444, 0 }, /* No RGB444 please */ + {REG_TSLB, TSLB_BYTEORD }, + { REG_COM1, 0x0 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA }, + { REG_COM15, COM15_RGB565|COM15_R00FF }, + { 0xff, 0xff }, +}; + +/* V4L2_PIX_FMT_SRGGB8 */ +static struct regval ov_fmt_bayer[] = { + /* This changes color order */ + {REG_TSLB, 0x40}, /* BGGR */ + /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */ + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; +/* + * Store a set of start/stop values into the camera. + */ +static int stk_sensor_set_hw(struct stk_camera *dev, + int hstart, int hstop, int vstart, int vstop) +{ + int ret; + unsigned char v; +/* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff); + ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff); + ret += stk_sensor_inb(dev, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += stk_sensor_outb(dev, REG_HREF, v); +/* + * Vertical: similar arrangement (note: this is different from ov7670.c) + */ + ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff); + ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff); + ret += stk_sensor_inb(dev, REG_VREF, &v); + v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); + msleep(10); + ret += stk_sensor_outb(dev, REG_VREF, v); + return ret; +} + + +int stk_sensor_configure(struct stk_camera *dev) +{ + int com7; + /* + * We setup the sensor to output dummy lines in low-res modes, + * so we don't get absurdly hight framerates. + */ + unsigned dummylines; + int flip; + struct regval *rv; + + switch (dev->vsettings.mode) { + case MODE_QCIF: com7 = COM7_FMT_QCIF; + dummylines = 604; + break; + case MODE_QVGA: com7 = COM7_FMT_QVGA; + dummylines = 267; + break; + case MODE_CIF: com7 = COM7_FMT_CIF; + dummylines = 412; + break; + case MODE_VGA: com7 = COM7_FMT_VGA; + dummylines = 11; + break; + case MODE_SXGA: com7 = COM7_FMT_SXGA; + dummylines = 0; + break; + default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode); + return -EFAULT; + } + switch (dev->vsettings.palette) { + case V4L2_PIX_FMT_UYVY: + com7 |= COM7_YUV; + rv = ov_fmt_uyvy; + break; + case V4L2_PIX_FMT_YUYV: + com7 |= COM7_YUV; + rv = ov_fmt_yuyv; + break; + case V4L2_PIX_FMT_RGB565: + com7 |= COM7_RGB; + rv = ov_fmt_rgbp; + break; + case V4L2_PIX_FMT_RGB565X: + com7 |= COM7_RGB; + rv = ov_fmt_rgbr; + break; + case V4L2_PIX_FMT_SBGGR8: + com7 |= COM7_PBAYER; + rv = ov_fmt_bayer; + break; + default: STK_ERROR("Unsupported colorspace\n"); + return -EFAULT; + } + /*FIXME sometimes the sensor go to a bad state + stk_sensor_write_regvals(dev, ov_initvals); */ + stk_sensor_outb(dev, REG_COM7, com7); + msleep(50); + stk_sensor_write_regvals(dev, rv); + flip = (dev->vsettings.vflip?MVFP_FLIP:0) + | (dev->vsettings.hflip?MVFP_MIRROR:0); + stk_sensor_outb(dev, REG_MVFP, flip); + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8 + && !dev->vsettings.vflip) + stk_sensor_outb(dev, REG_TSLB, 0x08); + stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8); + stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff); + msleep(50); + switch (dev->vsettings.mode) { + case MODE_VGA: + if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) + STK_ERROR("stk_sensor_set_hw failed (VGA)\n"); + break; + case MODE_SXGA: + case MODE_CIF: + case MODE_QVGA: + case MODE_QCIF: + /*FIXME These settings seem ignored by the sensor + if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) + STK_ERROR("stk_sensor_set_hw failed (SXGA)\n"); + */ + break; + } + msleep(10); + return 0; +} + +int stk_sensor_set_brightness(struct stk_camera *dev, int br) +{ + if (br < 0 || br > 0xff) + return -EINVAL; + stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6)); + stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6)); + return 0; +} + diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c new file mode 100644 index 000000000000..86a0fc56c330 --- /dev/null +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -0,0 +1,1380 @@ +/* + * stk-webcam.c : Driver for Syntek 1125 USB webcam controller + * + * Copyright (C) 2006 Nicolas VIVIEN + * Copyright 2007-2008 Jaime Velasco Juan + * + * Some parts are inspired from cafe_ccic.c + * Copyright 2006-2007 Jonathan Corbet + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "stk-webcam.h" + + +static bool hflip; +module_param(hflip, bool, 0444); +MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); + +static bool vflip; +module_param(vflip, bool, 0444); +MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); + +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jaime Velasco Juan and Nicolas VIVIEN"); +MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); + + +/* bool for webcam LED management */ +int first_init = 1; + +/* Some cameras have audio interfaces, we aren't interested in those */ +static struct usb_device_id stkwebcam_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, + { } +}; +MODULE_DEVICE_TABLE(usb, stkwebcam_table); + +/* + * Basic stuff + */ +int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) +{ + struct usb_device *udev = dev->udev; + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 500); + if (ret < 0) + return ret; + else + return 0; +} + +int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value) +{ + struct usb_device *udev = dev->udev; + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, + index, + (u8 *) value, + sizeof(u8), + 500); + if (ret < 0) + return ret; + else + return 0; +} + +static int stk_start_stream(struct stk_camera *dev) +{ + int value; + int i, ret; + int value_116, value_117; + + if (!is_present(dev)) + return -ENODEV; + if (!is_memallocd(dev) || !is_initialised(dev)) { + STK_ERROR("FIXME: Buffers are not allocated\n"); + return -EFAULT; + } + ret = usb_set_interface(dev->udev, 0, 5); + + if (ret < 0) + STK_ERROR("usb_set_interface failed !\n"); + if (stk_sensor_wakeup(dev)) + STK_ERROR("error awaking the sensor\n"); + + stk_camera_read_reg(dev, 0x0116, &value_116); + stk_camera_read_reg(dev, 0x0117, &value_117); + + stk_camera_write_reg(dev, 0x0116, 0x0000); + stk_camera_write_reg(dev, 0x0117, 0x0000); + + stk_camera_read_reg(dev, 0x0100, &value); + stk_camera_write_reg(dev, 0x0100, value | 0x80); + + stk_camera_write_reg(dev, 0x0116, value_116); + stk_camera_write_reg(dev, 0x0117, value_117); + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].urb) { + ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); + atomic_inc(&dev->urbs_used); + if (ret) + return ret; + } + } + set_streaming(dev); + return 0; +} + +static int stk_stop_stream(struct stk_camera *dev) +{ + int value; + int i; + if (is_present(dev)) { + stk_camera_read_reg(dev, 0x0100, &value); + stk_camera_write_reg(dev, 0x0100, value & ~0x80); + if (dev->isobufs != NULL) { + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].urb) + usb_kill_urb(dev->isobufs[i].urb); + } + } + unset_streaming(dev); + + if (usb_set_interface(dev->udev, 0, 0)) + STK_ERROR("usb_set_interface failed !\n"); + if (stk_sensor_sleep(dev)) + STK_ERROR("error suspending the sensor\n"); + } + return 0; +} + +/* + * This seems to be the shortest init sequence we + * must do in order to find the sensor + * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor + * is also reset. Maybe powers down it? + * Rest of values don't make a difference + */ + +static struct regval stk1125_initvals[] = { + /*TODO: What means this sequence? */ + {0x0000, 0x24}, + {0x0100, 0x21}, + {0x0002, 0x68}, + {0x0003, 0x80}, + {0x0005, 0x00}, + {0x0007, 0x03}, + {0x000d, 0x00}, + {0x000f, 0x02}, + {0x0300, 0x12}, + {0x0350, 0x41}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0018, 0x10}, + {0x0019, 0x00}, + {0x001b, 0x0e}, + {0x001c, 0x46}, + {0x0300, 0x80}, + {0x001a, 0x04}, + {0x0110, 0x00}, + {0x0111, 0x00}, + {0x0112, 0x00}, + {0x0113, 0x00}, + + {0xffff, 0xff}, +}; + + +static int stk_initialise(struct stk_camera *dev) +{ + struct regval *rv; + int ret; + if (!is_present(dev)) + return -ENODEV; + if (is_initialised(dev)) + return 0; + rv = stk1125_initvals; + while (rv->reg != 0xffff) { + ret = stk_camera_write_reg(dev, rv->reg, rv->val); + if (ret) + return ret; + rv++; + } + if (stk_sensor_init(dev) == 0) { + set_initialised(dev); + return 0; + } else + return -1; +} + +/* *********************************************** */ +/* + * This function is called as an URB transfert is complete (Isochronous pipe). + * So, the traitement is done in interrupt time, so it has be fast, not crash, + * and not stall. Neat. + */ +static void stk_isoc_handler(struct urb *urb) +{ + int i; + int ret; + int framelen; + unsigned long flags; + + unsigned char *fill = NULL; + unsigned char *iso_buf = NULL; + + struct stk_camera *dev; + struct stk_sio_buffer *fb; + + dev = (struct stk_camera *) urb->context; + + if (dev == NULL) { + STK_ERROR("isoc_handler called with NULL device !\n"); + return; + } + + if (urb->status == -ENOENT || urb->status == -ECONNRESET + || urb->status == -ESHUTDOWN) { + atomic_dec(&dev->urbs_used); + return; + } + + spin_lock_irqsave(&dev->spinlock, flags); + + if (urb->status != -EINPROGRESS && urb->status != 0) { + STK_ERROR("isoc_handler: urb->status == %d\n", urb->status); + goto resubmit; + } + + if (list_empty(&dev->sio_avail)) { + /*FIXME Stop streaming after a while */ + (void) (printk_ratelimit() && + STK_ERROR("isoc_handler without available buffer!\n")); + goto resubmit; + } + fb = list_first_entry(&dev->sio_avail, + struct stk_sio_buffer, list); + fill = fb->buffer + fb->v4lbuf.bytesused; + + for (i = 0; i < urb->number_of_packets; i++) { + if (urb->iso_frame_desc[i].status != 0) { + if (urb->iso_frame_desc[i].status != -EXDEV) + STK_ERROR("Frame %d has error %d\n", i, + urb->iso_frame_desc[i].status); + continue; + } + framelen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (framelen <= 4) + continue; /* no data */ + + /* + * we found something informational from there + * the isoc frames have to type of headers + * type1: 00 xx 00 00 or 20 xx 00 00 + * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 + * xx is a sequencer which has never been seen over 0x3f + * imho data written down looks like bayer, i see similarities + * after every 640 bytes + */ + if (*iso_buf & 0x80) { + framelen -= 8; + iso_buf += 8; + /* This marks a new frame */ + if (fb->v4lbuf.bytesused != 0 + && fb->v4lbuf.bytesused != dev->frame_size) { + (void) (printk_ratelimit() && + STK_ERROR("frame %d, " + "bytesused=%d, skipping\n", + i, fb->v4lbuf.bytesused)); + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } else if (fb->v4lbuf.bytesused == dev->frame_size) { + if (list_is_singular(&dev->sio_avail)) { + /* Always reuse the last buffer */ + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } else { + list_move_tail(dev->sio_avail.next, + &dev->sio_full); + wake_up(&dev->wait_frame); + fb = list_first_entry(&dev->sio_avail, + struct stk_sio_buffer, list); + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } + } + } else { + framelen -= 4; + iso_buf += 4; + } + + /* Our buffer is full !!! */ + if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { + (void) (printk_ratelimit() && + STK_ERROR("Frame buffer overflow, lost sync\n")); + /*FIXME Do something here? */ + continue; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + memcpy(fill, iso_buf, framelen); + spin_lock_irqsave(&dev->spinlock, flags); + fill += framelen; + + /* New size of our buffer */ + fb->v4lbuf.bytesused += framelen; + } + +resubmit: + spin_unlock_irqrestore(&dev->spinlock, flags); + urb->dev = dev->udev; + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret != 0) { + STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n", + ret); + } +} + +/* -------------------------------------------- */ + +static int stk_prepare_iso(struct stk_camera *dev) +{ + void *kbuf; + int i, j; + struct urb *urb; + struct usb_device *udev; + + if (dev == NULL) + return -ENXIO; + udev = dev->udev; + + if (dev->isobufs) + STK_ERROR("isobufs already allocated. Bad\n"); + else + dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), + GFP_KERNEL); + if (dev->isobufs == NULL) { + STK_ERROR("Unable to allocate iso buffers\n"); + return -ENOMEM; + } + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].data == NULL) { + kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + if (kbuf == NULL) { + STK_ERROR("Failed to allocate iso buffer %d\n", + i); + goto isobufs_out; + } + dev->isobufs[i].data = kbuf; + } else + STK_ERROR("isobuf data already allocated\n"); + if (dev->isobufs[i].urb == NULL) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + STK_ERROR("Failed to allocate URB %d\n", i); + goto isobufs_out; + } + dev->isobufs[i].urb = urb; + } else { + STK_ERROR("Killing URB\n"); + usb_kill_urb(dev->isobufs[i].urb); + urb = dev->isobufs[i].urb; + } + urb->interval = 1; + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->isobufs[i].data; + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = stk_isoc_handler; + urb->context = dev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; + } + } + set_memallocd(dev); + return 0; + +isobufs_out: + for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) + kfree(dev->isobufs[i].data); + for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) + usb_free_urb(dev->isobufs[i].urb); + kfree(dev->isobufs); + dev->isobufs = NULL; + return -ENOMEM; +} + +static void stk_clean_iso(struct stk_camera *dev) +{ + int i; + + if (dev == NULL || dev->isobufs == NULL) + return; + + for (i = 0; i < MAX_ISO_BUFS; i++) { + struct urb *urb; + + urb = dev->isobufs[i].urb; + if (urb) { + if (atomic_read(&dev->urbs_used) && is_present(dev)) + usb_kill_urb(urb); + usb_free_urb(urb); + } + kfree(dev->isobufs[i].data); + } + kfree(dev->isobufs); + dev->isobufs = NULL; + unset_memallocd(dev); +} + +static int stk_setup_siobuf(struct stk_camera *dev, int index) +{ + struct stk_sio_buffer *buf = dev->sio_bufs + index; + INIT_LIST_HEAD(&buf->list); + buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); + buf->buffer = vmalloc_user(buf->v4lbuf.length); + if (buf->buffer == NULL) + return -ENOMEM; + buf->mapcount = 0; + buf->dev = dev; + buf->v4lbuf.index = index; + buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.field = V4L2_FIELD_NONE; + buf->v4lbuf.memory = V4L2_MEMORY_MMAP; + buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; + return 0; +} + +static int stk_free_sio_buffers(struct stk_camera *dev) +{ + int i; + int nbufs; + unsigned long flags; + if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) + return 0; + /* + * If any buffers are mapped, we cannot free them at all. + */ + for (i = 0; i < dev->n_sbufs; i++) { + if (dev->sio_bufs[i].mapcount > 0) + return -EBUSY; + } + /* + * OK, let's do it. + */ + spin_lock_irqsave(&dev->spinlock, flags); + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + nbufs = dev->n_sbufs; + dev->n_sbufs = 0; + spin_unlock_irqrestore(&dev->spinlock, flags); + for (i = 0; i < nbufs; i++) { + if (dev->sio_bufs[i].buffer != NULL) + vfree(dev->sio_bufs[i].buffer); + } + kfree(dev->sio_bufs); + dev->sio_bufs = NULL; + return 0; +} + +static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) +{ + int i; + if (dev->sio_bufs != NULL) + STK_ERROR("sio_bufs already allocated\n"); + else { + dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer), + GFP_KERNEL); + if (dev->sio_bufs == NULL) + return -ENOMEM; + for (i = 0; i < n_sbufs; i++) { + if (stk_setup_siobuf(dev, i)) + return (dev->n_sbufs > 1 ? 0 : -ENOMEM); + dev->n_sbufs = i+1; + } + } + return 0; +} + +static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) +{ + int err; + err = stk_prepare_iso(dev); + if (err) { + stk_clean_iso(dev); + return err; + } + err = stk_prepare_sio_buffers(dev, n_sbufs); + if (err) { + stk_free_sio_buffers(dev); + return err; + } + return 0; +} + +static void stk_free_buffers(struct stk_camera *dev) +{ + stk_clean_iso(dev); + stk_free_sio_buffers(dev); +} +/* -------------------------------------------- */ + +/* v4l file operations */ + +static int v4l_stk_open(struct file *fp) +{ + struct stk_camera *dev; + struct video_device *vdev; + + vdev = video_devdata(fp); + dev = vdev_to_camera(vdev); + + if (dev == NULL || !is_present(dev)) + return -ENXIO; + + if (!first_init) + stk_camera_write_reg(dev, 0x0, 0x24); + else + first_init = 0; + + fp->private_data = dev; + usb_autopm_get_interface(dev->interface); + + return 0; +} + +static int v4l_stk_release(struct file *fp) +{ + struct stk_camera *dev = fp->private_data; + + if (dev->owner == fp) { + stk_stop_stream(dev); + stk_free_buffers(dev); + stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ + unset_initialised(dev); + dev->owner = NULL; + } + + if (is_present(dev)) + usb_autopm_put_interface(dev->interface); + + return 0; +} + +static ssize_t v4l_stk_read(struct file *fp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int i; + int ret; + unsigned long flags; + struct stk_sio_buffer *sbuf; + struct stk_camera *dev = fp->private_data; + + if (!is_present(dev)) + return -EIO; + if (dev->owner && dev->owner != fp) + return -EBUSY; + dev->owner = fp; + if (!is_streaming(dev)) { + if (stk_initialise(dev) + || stk_allocate_buffers(dev, 3) + || stk_start_stream(dev)) + return -ENOMEM; + spin_lock_irqsave(&dev->spinlock, flags); + for (i = 0; i < dev->n_sbufs; i++) { + list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); + dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + } + if (*f_pos == 0) { + if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) + return -EWOULDBLOCK; + ret = wait_event_interruptible(dev->wait_frame, + !list_empty(&dev->sio_full) || !is_present(dev)); + if (ret) + return ret; + if (!is_present(dev)) + return -EIO; + } + if (count + *f_pos > dev->frame_size) + count = dev->frame_size - *f_pos; + spin_lock_irqsave(&dev->spinlock, flags); + if (list_empty(&dev->sio_full)) { + spin_unlock_irqrestore(&dev->spinlock, flags); + STK_ERROR("BUG: No siobufs ready\n"); + return 0; + } + sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); + spin_unlock_irqrestore(&dev->spinlock, flags); + + if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) + return -EFAULT; + + *f_pos += count; + + if (*f_pos >= dev->frame_size) { + *f_pos = 0; + spin_lock_irqsave(&dev->spinlock, flags); + list_move_tail(&sbuf->list, &dev->sio_avail); + spin_unlock_irqrestore(&dev->spinlock, flags); + } + return count; +} + +static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait) +{ + struct stk_camera *dev = fp->private_data; + + poll_wait(fp, &dev->wait_frame, wait); + + if (!is_present(dev)) + return POLLERR; + + if (!list_empty(&dev->sio_full)) + return POLLIN | POLLRDNORM; + + return 0; +} + + +static void stk_v4l_vm_open(struct vm_area_struct *vma) +{ + struct stk_sio_buffer *sbuf = vma->vm_private_data; + sbuf->mapcount++; +} +static void stk_v4l_vm_close(struct vm_area_struct *vma) +{ + struct stk_sio_buffer *sbuf = vma->vm_private_data; + sbuf->mapcount--; + if (sbuf->mapcount == 0) + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; +} +static const struct vm_operations_struct stk_v4l_vm_ops = { + .open = stk_v4l_vm_open, + .close = stk_v4l_vm_close +}; + +static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) +{ + unsigned int i; + int ret; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct stk_camera *dev = fp->private_data; + struct stk_sio_buffer *sbuf = NULL; + + if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + for (i = 0; i < dev->n_sbufs; i++) { + if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { + sbuf = dev->sio_bufs + i; + break; + } + } + if (sbuf == NULL) + return -EINVAL; + ret = remap_vmalloc_range(vma, sbuf->buffer, 0); + if (ret) + return ret; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = sbuf; + vma->vm_ops = &stk_v4l_vm_ops; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + stk_v4l_vm_open(vma); + return 0; +} + +/* v4l ioctl handlers */ + +static int stk_vidioc_querycap(struct file *filp, + void *priv, struct v4l2_capability *cap) +{ + strcpy(cap->driver, "stk"); + strcpy(cap->card, "stk"); + cap->version = DRIVER_VERSION_NUM; + + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + return 0; +} + +static int stk_vidioc_enum_input(struct file *filp, + void *priv, struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + strcpy(input->name, "Syntek USB Camera"); + input->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + + +static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + else + return 0; +} + +/* from vivi.c */ +static int stk_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) +{ + return 0; +} + +/* List of all V4Lv2 controls supported by the driver */ +static struct v4l2_queryctrl stk_controls[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xffff, + .step = 0x0100, + .default_value = 0x6000, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, +}; + +static int stk_vidioc_queryctrl(struct file *filp, + void *priv, struct v4l2_queryctrl *c) +{ + int i; + int nbr; + nbr = ARRAY_SIZE(stk_controls); + + for (i = 0; i < nbr; i++) { + if (stk_controls[i].id == c->id) { + memcpy(c, &stk_controls[i], + sizeof(struct v4l2_queryctrl)); + return 0; + } + } + return -EINVAL; +} + +static int stk_vidioc_g_ctrl(struct file *filp, + void *priv, struct v4l2_control *c) +{ + struct stk_camera *dev = priv; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = dev->vsettings.brightness; + break; + case V4L2_CID_HFLIP: + c->value = dev->vsettings.hflip; + break; + case V4L2_CID_VFLIP: + c->value = dev->vsettings.vflip; + break; + default: + return -EINVAL; + } + return 0; +} + +static int stk_vidioc_s_ctrl(struct file *filp, + void *priv, struct v4l2_control *c) +{ + struct stk_camera *dev = priv; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + dev->vsettings.brightness = c->value; + return stk_sensor_set_brightness(dev, c->value >> 8); + case V4L2_CID_HFLIP: + dev->vsettings.hflip = c->value; + return 0; + case V4L2_CID_VFLIP: + dev->vsettings.vflip = c->value; + return 0; + default: + return -EINVAL; + } + return 0; +} + + +static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_fmtdesc *fmtd) +{ + switch (fmtd->index) { + case 0: + fmtd->pixelformat = V4L2_PIX_FMT_RGB565; + strcpy(fmtd->description, "r5g6b5"); + break; + case 1: + fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; + strcpy(fmtd->description, "r5g6b5BE"); + break; + case 2: + fmtd->pixelformat = V4L2_PIX_FMT_UYVY; + strcpy(fmtd->description, "yuv4:2:2"); + break; + case 3: + fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; + strcpy(fmtd->description, "Raw bayer"); + break; + case 4: + fmtd->pixelformat = V4L2_PIX_FMT_YUYV; + strcpy(fmtd->description, "yuv4:2:2"); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct stk_size { + unsigned w; + unsigned h; + enum stk_mode m; +} stk_sizes[] = { + { .w = 1280, .h = 1024, .m = MODE_SXGA, }, + { .w = 640, .h = 480, .m = MODE_VGA, }, + { .w = 352, .h = 288, .m = MODE_CIF, }, + { .w = 320, .h = 240, .m = MODE_QVGA, }, + { .w = 176, .h = 144, .m = MODE_QCIF, }, +}; + +static int stk_vidioc_g_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix_format = &f->fmt.pix; + struct stk_camera *dev = priv; + int i; + + for (i = 0; i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode; i++) + ; + if (i == ARRAY_SIZE(stk_sizes)) { + STK_ERROR("ERROR: mode invalid\n"); + return -EINVAL; + } + pix_format->width = stk_sizes[i].w; + pix_format->height = stk_sizes[i].h; + pix_format->field = V4L2_FIELD_NONE; + pix_format->colorspace = V4L2_COLORSPACE_SRGB; + pix_format->pixelformat = dev->vsettings.palette; + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) + pix_format->bytesperline = pix_format->width; + else + pix_format->bytesperline = 2 * pix_format->width; + pix_format->sizeimage = pix_format->bytesperline + * pix_format->height; + return 0; +} + +static int stk_vidioc_try_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *fmtd) +{ + int i; + switch (fmtd->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_SBGGR8: + break; + default: + return -EINVAL; + } + for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { + if (fmtd->fmt.pix.width > stk_sizes[i].w) + break; + } + if (i == ARRAY_SIZE(stk_sizes) + || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) + < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { + fmtd->fmt.pix.height = stk_sizes[i-1].h; + fmtd->fmt.pix.width = stk_sizes[i-1].w; + fmtd->fmt.pix.priv = i - 1; + } else { + fmtd->fmt.pix.height = stk_sizes[i].h; + fmtd->fmt.pix.width = stk_sizes[i].w; + fmtd->fmt.pix.priv = i; + } + + fmtd->fmt.pix.field = V4L2_FIELD_NONE; + fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) + fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; + else + fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; + fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline + * fmtd->fmt.pix.height; + return 0; +} + +static int stk_setup_format(struct stk_camera *dev) +{ + int i = 0; + int depth; + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) + depth = 1; + else + depth = 2; + while (i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode) + i++; + if (i == ARRAY_SIZE(stk_sizes)) { + STK_ERROR("Something is broken in %s\n", __func__); + return -EFAULT; + } + /* This registers controls some timings, not sure of what. */ + stk_camera_write_reg(dev, 0x001b, 0x0e); + if (dev->vsettings.mode == MODE_SXGA) + stk_camera_write_reg(dev, 0x001c, 0x0e); + else + stk_camera_write_reg(dev, 0x001c, 0x46); + /* + * Registers 0x0115 0x0114 are the size of each line (bytes), + * regs 0x0117 0x0116 are the heigth of the image. + */ + stk_camera_write_reg(dev, 0x0115, + ((stk_sizes[i].w * depth) >> 8) & 0xff); + stk_camera_write_reg(dev, 0x0114, + (stk_sizes[i].w * depth) & 0xff); + stk_camera_write_reg(dev, 0x0117, + (stk_sizes[i].h >> 8) & 0xff); + stk_camera_write_reg(dev, 0x0116, + stk_sizes[i].h & 0xff); + return stk_sensor_configure(dev); +} + +static int stk_vidioc_s_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *fmtd) +{ + int ret; + struct stk_camera *dev = priv; + + if (dev == NULL) + return -ENODEV; + if (!is_present(dev)) + return -ENODEV; + if (is_streaming(dev)) + return -EBUSY; + if (dev->owner && dev->owner != filp) + return -EBUSY; + ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd); + if (ret) + return ret; + dev->owner = filp; + + dev->vsettings.palette = fmtd->fmt.pix.pixelformat; + stk_free_buffers(dev); + dev->frame_size = fmtd->fmt.pix.sizeimage; + dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m; + + stk_initialise(dev); + return stk_setup_format(dev); +} + +static int stk_vidioc_reqbufs(struct file *filp, + void *priv, struct v4l2_requestbuffers *rb) +{ + struct stk_camera *dev = priv; + + if (dev == NULL) + return -ENODEV; + if (rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (is_streaming(dev) + || (dev->owner && dev->owner != filp)) + return -EBUSY; + dev->owner = filp; + + /*FIXME If they ask for zero, we must stop streaming and free */ + if (rb->count < 3) + rb->count = 3; + /* Arbitrary limit */ + else if (rb->count > 5) + rb->count = 5; + + stk_allocate_buffers(dev, rb->count); + rb->count = dev->n_sbufs; + return 0; +} + +static int stk_vidioc_querybuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = priv; + struct stk_sio_buffer *sbuf; + + if (buf->index >= dev->n_sbufs) + return -EINVAL; + sbuf = dev->sio_bufs + buf->index; + *buf = sbuf->v4lbuf; + return 0; +} + +static int stk_vidioc_qbuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = priv; + struct stk_sio_buffer *sbuf; + unsigned long flags; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (buf->index >= dev->n_sbufs) + return -EINVAL; + sbuf = dev->sio_bufs + buf->index; + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) + return 0; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + spin_lock_irqsave(&dev->spinlock, flags); + list_add_tail(&sbuf->list, &dev->sio_avail); + *buf = sbuf->v4lbuf; + spin_unlock_irqrestore(&dev->spinlock, flags); + return 0; +} + +static int stk_vidioc_dqbuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = priv; + struct stk_sio_buffer *sbuf; + unsigned long flags; + int ret; + + if (!is_streaming(dev)) + return -EINVAL; + + if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) + return -EWOULDBLOCK; + ret = wait_event_interruptible(dev->wait_frame, + !list_empty(&dev->sio_full) || !is_present(dev)); + if (ret) + return ret; + if (!is_present(dev)) + return -EIO; + + spin_lock_irqsave(&dev->spinlock, flags); + sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); + list_del_init(&sbuf->list); + spin_unlock_irqrestore(&dev->spinlock, flags); + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + sbuf->v4lbuf.sequence = ++dev->sequence; + do_gettimeofday(&sbuf->v4lbuf.timestamp); + + *buf = sbuf->v4lbuf; + return 0; +} + +static int stk_vidioc_streamon(struct file *filp, + void *priv, enum v4l2_buf_type type) +{ + struct stk_camera *dev = priv; + if (is_streaming(dev)) + return 0; + if (dev->sio_bufs == NULL) + return -EINVAL; + dev->sequence = 0; + return stk_start_stream(dev); +} + +static int stk_vidioc_streamoff(struct file *filp, + void *priv, enum v4l2_buf_type type) +{ + struct stk_camera *dev = priv; + unsigned long flags; + int i; + stk_stop_stream(dev); + spin_lock_irqsave(&dev->spinlock, flags); + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + for (i = 0; i < dev->n_sbufs; i++) { + INIT_LIST_HEAD(&dev->sio_bufs[i].list); + dev->sio_bufs[i].v4lbuf.flags = 0; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + return 0; +} + + +static int stk_vidioc_g_parm(struct file *filp, + void *priv, struct v4l2_streamparm *sp) +{ + /*FIXME This is not correct */ + sp->parm.capture.timeperframe.numerator = 1; + sp->parm.capture.timeperframe.denominator = 30; + sp->parm.capture.readbuffers = 2; + return 0; +} + +static int stk_vidioc_enum_framesizes(struct file *filp, + void *priv, struct v4l2_frmsizeenum *frms) +{ + if (frms->index >= ARRAY_SIZE(stk_sizes)) + return -EINVAL; + switch (frms->pixel_format) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_SBGGR8: + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = stk_sizes[frms->index].w; + frms->discrete.height = stk_sizes[frms->index].h; + return 0; + default: return -EINVAL; + } +} + +static struct v4l2_file_operations v4l_stk_fops = { + .owner = THIS_MODULE, + .open = v4l_stk_open, + .release = v4l_stk_release, + .read = v4l_stk_read, + .poll = v4l_stk_poll, + .mmap = v4l_stk_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { + .vidioc_querycap = stk_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, + .vidioc_enum_input = stk_vidioc_enum_input, + .vidioc_s_input = stk_vidioc_s_input, + .vidioc_g_input = stk_vidioc_g_input, + .vidioc_s_std = stk_vidioc_s_std, + .vidioc_reqbufs = stk_vidioc_reqbufs, + .vidioc_querybuf = stk_vidioc_querybuf, + .vidioc_qbuf = stk_vidioc_qbuf, + .vidioc_dqbuf = stk_vidioc_dqbuf, + .vidioc_streamon = stk_vidioc_streamon, + .vidioc_streamoff = stk_vidioc_streamoff, + .vidioc_queryctrl = stk_vidioc_queryctrl, + .vidioc_g_ctrl = stk_vidioc_g_ctrl, + .vidioc_s_ctrl = stk_vidioc_s_ctrl, + .vidioc_g_parm = stk_vidioc_g_parm, + .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, +}; + +static void stk_v4l_dev_release(struct video_device *vd) +{ + struct stk_camera *dev = vdev_to_camera(vd); + + if (dev->sio_bufs != NULL || dev->isobufs != NULL) + STK_ERROR("We are leaking memory\n"); + usb_put_intf(dev->interface); + kfree(dev); +} + +static struct video_device stk_v4l_data = { + .name = "stkwebcam", + .tvnorms = V4L2_STD_UNKNOWN, + .current_norm = V4L2_STD_UNKNOWN, + .fops = &v4l_stk_fops, + .ioctl_ops = &v4l_stk_ioctl_ops, + .release = stk_v4l_dev_release, +}; + + +static int stk_register_video_device(struct stk_camera *dev) +{ + int err; + + dev->vdev = stk_v4l_data; + dev->vdev.debug = debug; + dev->vdev.parent = &dev->interface->dev; + err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + if (err) + STK_ERROR("v4l registration failed\n"); + else + STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n", + video_device_node_name(&dev->vdev)); + return err; +} + + +/* USB Stuff */ + +static int stk_camera_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int i; + int err = 0; + + struct stk_camera *dev = NULL; + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + + dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); + if (dev == NULL) { + STK_ERROR("Out of memory !\n"); + return -ENOMEM; + } + + spin_lock_init(&dev->spinlock); + init_waitqueue_head(&dev->wait_frame); + + dev->udev = udev; + dev->interface = interface; + usb_get_intf(interface); + + dev->vsettings.vflip = vflip; + dev->vsettings.hflip = hflip; + dev->n_sbufs = 0; + set_present(dev); + + /* Set up the endpoint information + * use only the first isoc-in endpoint + * for the current alternate setting */ + iface_desc = interface->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->isoc_ep + && usb_endpoint_is_isoc_in(endpoint)) { + /* we found an isoc in endpoint */ + dev->isoc_ep = usb_endpoint_num(endpoint); + break; + } + } + if (!dev->isoc_ep) { + STK_ERROR("Could not find isoc-in endpoint"); + err = -ENODEV; + goto error; + } + dev->vsettings.brightness = 0x7fff; + dev->vsettings.palette = V4L2_PIX_FMT_RGB565; + dev->vsettings.mode = MODE_VGA; + dev->frame_size = 640 * 480 * 2; + + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + + usb_set_intfdata(interface, dev); + + err = stk_register_video_device(dev); + if (err) + goto error; + + return 0; + +error: + kfree(dev); + return err; +} + +static void stk_camera_disconnect(struct usb_interface *interface) +{ + struct stk_camera *dev = usb_get_intfdata(interface); + + usb_set_intfdata(interface, NULL); + unset_present(dev); + + wake_up_interruptible(&dev->wait_frame); + + STK_INFO("Syntek USB2.0 Camera release resources device %s\n", + video_device_node_name(&dev->vdev)); + + video_unregister_device(&dev->vdev); +} + +#ifdef CONFIG_PM +static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct stk_camera *dev = usb_get_intfdata(intf); + if (is_streaming(dev)) { + stk_stop_stream(dev); + /* yes, this is ugly */ + set_streaming(dev); + } + return 0; +} + +static int stk_camera_resume(struct usb_interface *intf) +{ + struct stk_camera *dev = usb_get_intfdata(intf); + if (!is_initialised(dev)) + return 0; + unset_initialised(dev); + stk_initialise(dev); + stk_camera_write_reg(dev, 0x0, 0x49); + stk_setup_format(dev); + if (is_streaming(dev)) + stk_start_stream(dev); + return 0; +} +#endif + +static struct usb_driver stk_camera_driver = { + .name = "stkwebcam", + .probe = stk_camera_probe, + .disconnect = stk_camera_disconnect, + .id_table = stkwebcam_table, +#ifdef CONFIG_PM + .suspend = stk_camera_suspend, + .resume = stk_camera_resume, +#endif +}; + +module_usb_driver(stk_camera_driver); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h new file mode 100644 index 000000000000..9f6736637571 --- /dev/null +++ b/drivers/media/usb/stkwebcam/stk-webcam.h @@ -0,0 +1,134 @@ +/* + * stk-webcam.h : Driver for Syntek 1125 USB webcam controller + * + * Copyright (C) 2006 Nicolas VIVIEN + * Copyright 2007-2008 Jaime Velasco Juan + * + * This program is free software; 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef STKWEBCAM_H +#define STKWEBCAM_H + +#include +#include + +#define DRIVER_VERSION "v0.0.1" +#define DRIVER_VERSION_NUM 0x000001 + +#define MAX_ISO_BUFS 3 +#define ISO_FRAMES_PER_DESC 16 +#define ISO_MAX_FRAME_SIZE 3 * 1024 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + + +#define PREFIX "stkwebcam: " +#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args) +#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args) +#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args) + +struct stk_iso_buf { + void *data; + int length; + int read; + struct urb *urb; +}; + +/* Streaming IO buffers */ +struct stk_sio_buffer { + struct v4l2_buffer v4lbuf; + char *buffer; + int mapcount; + struct stk_camera *dev; + struct list_head list; +}; + +enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; + +struct stk_video { + enum stk_mode mode; + int brightness; + __u32 palette; + int hflip; + int vflip; +}; + +enum stk_status { + S_PRESENT = 1, + S_INITIALISED = 2, + S_MEMALLOCD = 4, + S_STREAMING = 8, +}; +#define is_present(dev) ((dev)->status & S_PRESENT) +#define is_initialised(dev) ((dev)->status & S_INITIALISED) +#define is_streaming(dev) ((dev)->status & S_STREAMING) +#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD) +#define set_present(dev) ((dev)->status = S_PRESENT) +#define unset_present(dev) ((dev)->status &= \ + ~(S_PRESENT|S_INITIALISED|S_STREAMING)) +#define set_initialised(dev) ((dev)->status |= S_INITIALISED) +#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED) +#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD) +#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD) +#define set_streaming(dev) ((dev)->status |= S_STREAMING) +#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING) + +struct regval { + unsigned reg; + unsigned val; +}; + +struct stk_camera { + struct video_device vdev; + struct usb_device *udev; + struct usb_interface *interface; + int webcam_model; + struct file *owner; + + u8 isoc_ep; + + /* Not sure if this is right */ + atomic_t urbs_used; + + struct stk_video vsettings; + + enum stk_status status; + + spinlock_t spinlock; + wait_queue_head_t wait_frame; + + struct stk_iso_buf *isobufs; + + int frame_size; + /* Streaming buffers */ + unsigned int n_sbufs; + struct stk_sio_buffer *sio_bufs; + struct list_head sio_avail; + struct list_head sio_full; + unsigned sequence; +}; + +#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev) + +int stk_camera_write_reg(struct stk_camera *, u16, u8); +int stk_camera_read_reg(struct stk_camera *, u16, int *); + +int stk_sensor_init(struct stk_camera *); +int stk_sensor_configure(struct stk_camera *); +int stk_sensor_sleep(struct stk_camera *dev); +int stk_sensor_wakeup(struct stk_camera *dev); +int stk_sensor_set_brightness(struct stk_camera *dev, int br); + +#endif diff --git a/drivers/media/usb/zr364xx/Kconfig b/drivers/media/usb/zr364xx/Kconfig new file mode 100644 index 000000000000..0f585662881d --- /dev/null +++ b/drivers/media/usb/zr364xx/Kconfig @@ -0,0 +1,14 @@ +config USB_ZR364XX + tristate "USB ZR364XX Camera support" + depends on VIDEO_V4L2 + select VIDEOBUF_GEN + select VIDEOBUF_VMALLOC + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. + See for more info + and list of supported cameras. + + To compile this driver as a module, choose M here: the + module will be called zr364xx. + diff --git a/drivers/media/usb/zr364xx/Makefile b/drivers/media/usb/zr364xx/Makefile new file mode 100644 index 000000000000..a5777883a1f8 --- /dev/null +++ b/drivers/media/usb/zr364xx/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_USB_ZR364XX) += zr364xx.o + diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c new file mode 100644 index 000000000000..9afab35878b4 --- /dev/null +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -0,0 +1,1643 @@ +/* + * Zoran 364xx based USB webcam module version 0.73 + * + * Allows you to use your USB webcam with V4L2 applications + * This is still in heavy developpement ! + * + * Copyright (C) 2004 Antoine Jacquet + * http://royale.zerezo.com/zr364xx/ + * + * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers + * V4L2 version inspired by meye.c driver + * + * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Version Information */ +#define DRIVER_VERSION "0.7.4" +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "Zoran 364xx" + + +/* Camera */ +#define FRAMES 1 +#define MAX_FRAME_SIZE 200000 +#define BUFFER_SIZE 0x1000 +#define CTRL_TIMEOUT 500 + +#define ZR364XX_DEF_BUFS 4 +#define ZR364XX_READ_IDLE 0 +#define ZR364XX_READ_FRAME 1 + +/* Debug macro */ +#define DBG(fmt, args...) \ + do { \ + if (debug) { \ + printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ + } \ + } while (0) + +/*#define FULL_DEBUG 1*/ +#ifdef FULL_DEBUG +#define _DBG DBG +#else +#define _DBG(fmt, args...) +#endif + +/* Init methods, need to find nicer names for these + * the exact names of the chipsets would be the best if someone finds it */ +#define METHOD0 0 +#define METHOD1 1 +#define METHOD2 2 +#define METHOD3 3 + + +/* Module parameters */ +static int debug; +static int mode; + + +/* Module parameters interface */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); +module_param(mode, int, 0644); +MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); + + +/* Devices supported by this driver + * .driver_info contains the init method used by the camera */ +static struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, + {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, + {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, + {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, + {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, + {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, + {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, + {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, + {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, + {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, + {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, + {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, + {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, + {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* frame structure */ +struct zr364xx_framei { + unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, + ZR364XX_READ_FRAME */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct zr364xx_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ +}; + +struct zr364xx_dmaqueue { + struct list_head active; + struct zr364xx_camera *cam; +}; + +struct zr364xx_pipeinfo { + u32 transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *cam; /* back pointer to zr364xx_camera struct */ + u32 err_count; + u32 idx; +}; + +struct zr364xx_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* image formats. */ +static const struct zr364xx_fmt formats[] = { + { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + } +}; + +/* Camera stuff */ +struct zr364xx_camera { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface;/* the interface for this device */ + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; /* v4l video device */ + struct v4l2_fh *owner; /* owns the streaming */ + int nb; + struct zr364xx_bufferi buffer; + int skip; + int width; + int height; + int method; + struct mutex lock; + + spinlock_t slock; + struct zr364xx_dmaqueue vidq; + int last_frame; + int cur_frame; + unsigned long frame_count; + int b_acquire; + struct zr364xx_pipeinfo pipe[1]; + + u8 read_endpoint; + + const struct zr364xx_fmt *fmt; + struct videobuf_queue vb_vidq; + bool was_streaming; +}; + +/* buffer for one video frame */ +struct zr364xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct zr364xx_fmt *fmt; +}; + +/* function used to send initialisation commands to the camera */ +static int send_control_msg(struct usb_device *udev, u8 request, u16 value, + u16 index, unsigned char *cp, u16 size) +{ + int status; + + unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); + if (!transfer_buffer) { + dev_err(&udev->dev, "kmalloc(%d) failed\n", size); + return -ENOMEM; + } + + memcpy(transfer_buffer, cp, size); + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, CTRL_TIMEOUT); + + kfree(transfer_buffer); + return status; +} + + +/* Control messages sent to the camera to initialize it + * and launch the capture */ +typedef struct { + unsigned int value; + unsigned int size; + unsigned char *bytes; +} message; + +/* method 0 */ +static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d3[] = { 0, 0 }; +static message m0[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0x3370, sizeof(m0d1), m0d1}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2610, sizeof(m0d2), m0d2}, + {0xe107, 0, NULL}, + {0x2502, 0, NULL}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m0d3), m0d3}, + {-1, -1, NULL} +}; + +/* method 1 */ +static unsigned char m1d1[] = { 0xff, 0xff }; +static unsigned char m1d2[] = { 0x00, 0x00 }; +static message m1[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m1d1), m1d1}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m1d2), m1d2}, + {-1, -1, NULL} +}; + +/* method 2 */ +static unsigned char m2d1[] = { 0xff, 0xff }; +static message m2[] = { + {0x1f30, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m2d1), m2d1}, + {0x1f70, 0, NULL}, + {-1, -1, NULL} +}; + +/* init table */ +static message *init[4] = { m0, m1, m2, m2 }; + + +/* JPEG static data in header (Huffman table, etc) */ +static unsigned char header1[] = { + 0xFF, 0xD8, + /* + 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', + 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, + */ + 0xFF, 0xDB, 0x00, 0x84 +}; +static unsigned char header2[] = { + 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, + 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, + 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, + 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, + 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, + 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, + 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, + 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, + 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, + 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, + 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00 +}; +static unsigned char header3; + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct zr364xx_camera *cam = vq->priv_data; + + *size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (*count == 0) + *count = ZR364XX_DEF_BUFS; + + if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) + *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) +{ + _DBG("%s\n", __func__); + + if (in_interrupt()) + BUG(); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct zr364xx_camera *cam = vq->priv_data; + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + int rc; + + DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ? + cam->fmt->name : ""); + if (cam->fmt == NULL) + return -EINVAL; + + buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { + DBG("invalid buffer prepare\n"); + return -EINVAL; + } + + buf->fmt = cam->fmt; + buf->vb.width = cam->width; + buf->vb.height = cam->height; + buf->vb.field = field; + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + struct zr364xx_camera *cam = vq->priv_data; + + _DBG("%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &cam->vidq.active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + + _DBG("%s\n", __func__); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops zr364xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************/ +/* V4L2 integration */ +/********************/ +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); + +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, + loff_t * ppos) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err = 0; + + _DBG("%s\n", __func__); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = zr364xx_vidioc_streamon(file, file->private_data, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (err == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, + (int) count, (int) *ppos); + + /* NoMan Sux ! */ + err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + } + mutex_unlock(&cam->lock); + return err; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab + * Ted Walther + * John Sokol + * http://v4l.videotechnology.com/ + * + */ +static void zr364xx_fillbuff(struct zr364xx_camera *cam, + struct zr364xx_buffer *buf, + int jpgsize) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + + if (!vbuf) + return; + + last_frame = cam->last_frame; + if (last_frame != -1) { + tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + default: + printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); + } + cam->last_frame = -1; + } else { + printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); + return; + } + DBG("%s: Buffer 0x%08lx size= %d\n", __func__, + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = cam->frame_count * 2; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +} + +static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) +{ + struct zr364xx_dmaqueue *dma_q = &cam->vidq; + struct zr364xx_buffer *buf; + unsigned long flags = 0; + int rc = 0; + + DBG("wakeup: %p\n", &dma_q); + spin_lock_irqsave(&cam->slock, flags); + + if (list_empty(&dma_q->active)) { + DBG("No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct zr364xx_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + DBG("[%p/%d] wakeup\n", buf, buf->vb.i); + zr364xx_fillbuff(cam, buf, jpgsize); + wake_up(&buf->vb.done); + DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&cam->slock, flags); + return rc; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process (call this + * function again). + */ +static int zr364xx_read_video_callback(struct zr364xx_camera *cam, + struct zr364xx_pipeinfo *pipe_info, + struct urb *purb) +{ + unsigned char *pdest; + unsigned char *psrc; + s32 idx = -1; + struct zr364xx_framei *frm; + int i = 0; + unsigned char *ptr = NULL; + + _DBG("buffer to user\n"); + idx = cam->cur_frame; + frm = &cam->buffer.frame[idx]; + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16 *)pipe_info->transfer_buffer; + for (i = 0; i < purb->actual_length/2; i++) + swab16s(buf + i); + } + + /* search done. now find out if should be acquiring */ + if (!cam->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = ZR364XX_READ_IDLE; + return -EINVAL; + } + + psrc = (u8 *)pipe_info->transfer_buffer; + ptr = pdest = frm->lpvbits; + + if (frm->ulState == ZR364XX_READ_IDLE) { + frm->ulState = ZR364XX_READ_FRAME; + frm->cur_size = 0; + + _DBG("jpeg header, "); + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, psrc + 128, + purb->actual_length - 128); + ptr += purb->actual_length - 128; + _DBG("header : %d %d %d %d %d %d %d %d %d\n", + psrc[0], psrc[1], psrc[2], + psrc[3], psrc[4], psrc[5], + psrc[6], psrc[7], psrc[8]); + frm->cur_size = ptr - pdest; + } else { + if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold " + "frame data. Discarding frame data.\n", + __func__, MAX_FRAME_SIZE); + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } + } + /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, + purb->actual_length);*/ + + if (purb->actual_length < pipe_info->transfer_size) { + _DBG("****************Buffer[%d]full*************\n", idx); + cam->last_frame = cam->cur_frame; + cam->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if (cam->cur_frame == cam->buffer.dwFrames) + cam->cur_frame = 0; + + /* frame ready */ + /* go back to find the JPEG EOI marker */ + ptr = pdest = frm->lpvbits; + ptr += frm->cur_size - 2; + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == pdest) + DBG("No EOI marker\n"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != pdest) { + DBG("Bogus frame ? %d\n", ++(cam->nb)); + } else if (cam->b_acquire) { + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) + cam->skip--; + else { + _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + frm->cur_size, + pdest[0], pdest[1], pdest[2], pdest[3], + pdest[4], pdest[5], pdest[6], pdest[7]); + + zr364xx_got_frame(cam, frm->cur_size); + } + } + cam->frame_count++; + frm->ulState = ZR364XX_READ_IDLE; + frm->cur_size = 0; + } + /* done successfully */ + return 0; +} + +static int zr364xx_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); + strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cam->udev->dev), + sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int zr364xx_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, DRIVER_DESC " Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int zr364xx_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int zr364xx_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct zr364xx_camera *cam = + container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); + int temp; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* hardware brightness */ + send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - ctrl->val; + send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, formats[0].name); + f->pixelformat = formats[0].fourcc; + return 0; +} + +static char *decode_fourcc(__u32 pixelformat, char *buf) +{ + buf[0] = pixelformat & 0xff; + buf[1] = (pixelformat >> 8) & 0xff; + buf[2] = (pixelformat >> 16) & 0xff; + buf[3] = (pixelformat >> 24) & 0xff; + buf[4] = '\0'; + return buf; +} + +static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + char pixelformat_name[5]; + + if (cam == NULL) + return -ENODEV; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { + DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); + return -EINVAL; + } + + if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && + !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return 0; +} + +static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam; + + if (file == NULL) + return -ENODEV; + cam = video_drvdata(file); + + f->fmt.pix.pixelformat = formats[0].fourcc; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + char pixelformat_name[5]; + int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); + int i; + + if (ret < 0) + return ret; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&cam->vb_vidq)) { + DBG("%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + if (cam->owner) { + DBG("%s can't change format after started\n", __func__); + ret = -EBUSY; + goto out; + } + + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + DBG("%s: %dx%d mode selected\n", __func__, + cam->width, cam->height); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + cam->vb_vidq.field = f->fmt.pix.field; + + if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) + mode = 1; + else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) + mode = 2; + else + mode = 0; + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + for (i = 0; init[cam->method][i].size != -1; i++) { + ret = + send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (ret < 0) { + dev_err(&cam->udev->dev, + "error during resolution change sequence: %d\n", i); + goto out; + } + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + cam->skip = 2; + ret = 0; + +out: + mutex_unlock(&q->vb_lock); + + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return ret; +} + +static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + if (cam->owner && cam->owner != priv) + return -EBUSY; + return videobuf_reqbufs(&cam->vb_vidq, p); +} + +static int zr364xx_vidioc_querybuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_querybuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_qbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_qbuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_dqbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct zr364xx_pipeinfo *pipe_info; + struct zr364xx_camera *cam; + int pipe; + + pipe_info = purb->context; + _DBG("%s %p, status %d\n", __func__, purb, purb->status); + if (pipe_info == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + cam = pipe_info->cam; + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + /* if shutting down, do not resubmit, exit immediately */ + if (purb->status == -ESHUTDOWN) { + DBG("%s, err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + DBG("exiting USB pipe\n"); + return; + } + + if (purb->actual_length < 0 || + purb->actual_length > pipe_info->transfer_size) { + dev_err(&cam->udev->dev, "wrong number of bytes\n"); + return; + } + + if (purb->status == 0) + zr364xx_read_video_callback(cam, pipe_info, purb); + else { + pipe_info->err_count++; + DBG("%s: failed URB %d\n", __func__, purb->status); + } + + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + purb->status = usb_submit_urb(pipe_info->stream_urb, + GFP_ATOMIC); + + if (purb->status) + dev_err(&cam->udev->dev, + "error submitting urb (error=%i)\n", + purb->status); + } else + DBG("read pipe complete state 0\n"); +} + +static int zr364xx_start_readpipe(struct zr364xx_camera *cam) +{ + int pipe; + int retval; + struct zr364xx_pipeinfo *pipe_info = cam->pipe; + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); + + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + DBG("submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); + return retval; + } + + return 0; +} + +static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe_info; + + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); + return; + } + DBG("stop read pipe\n"); + pipe_info = cam->pipe; + if (pipe_info) { + if (pipe_info->state != 0) + pipe_info->state = 0; + + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + return; +} + +/* starts acquisition process */ +static int zr364xx_start_acquire(struct zr364xx_camera *cam) +{ + int j; + + DBG("start acquire\n"); + + cam->last_frame = -1; + cam->cur_frame = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + cam->b_acquire = 1; + return 0; +} + +static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) +{ + cam->b_acquire = 0; + return 0; +} + +static int zr364xx_prepare(struct zr364xx_camera *cam) +{ + int res; + int i, j; + + for (i = 0; init[cam->method][i].size != -1; i++) { + res = send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (res < 0) { + dev_err(&cam->udev->dev, + "error during open sequence: %d\n", i); + return res; + } + } + + cam->skip = 2; + cam->last_frame = -1; + cam->cur_frame = 0; + cam->frame_count = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + v4l2_ctrl_handler_setup(&cam->ctrl_handler); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int res; + + DBG("%s\n", __func__); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->owner && cam->owner != priv) + return -EBUSY; + + res = zr364xx_prepare(cam); + if (res) + return res; + res = videobuf_streamon(&cam->vb_vidq); + if (res == 0) { + zr364xx_start_acquire(cam); + cam->owner = file->private_data; + } + return res; +} + +static int zr364xx_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + DBG("%s\n", __func__); + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cam->owner && cam->owner != priv) + return -EBUSY; + zr364xx_stop_acquire(cam); + return videobuf_streamoff(&cam->vb_vidq); +} + + +/* open the camera */ +static int zr364xx_open(struct file *file) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err; + + DBG("%s\n", __func__); + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = v4l2_fh_open(file); + if (err) + goto out; + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + err = 0; + +out: + mutex_unlock(&cam->lock); + DBG("%s: %d\n", __func__, err); + return err; +} + +static void zr364xx_release(struct v4l2_device *v4l2_dev) +{ + struct zr364xx_camera *cam = + container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); + unsigned long i; + + v4l2_device_unregister(&cam->v4l2_dev); + + videobuf_mmap_free(&cam->vb_vidq); + + /* release sys buffers */ + for (i = 0; i < FRAMES; i++) { + if (cam->buffer.frame[i].lpvbits) { + DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); + vfree(cam->buffer.frame[i].lpvbits); + } + cam->buffer.frame[i].lpvbits = NULL; + } + + v4l2_ctrl_handler_free(&cam->ctrl_handler); + /* release transfer buffer */ + kfree(cam->pipe->transfer_buffer); + kfree(cam); +} + +/* release the camera */ +static int zr364xx_close(struct file *file) +{ + struct zr364xx_camera *cam; + struct usb_device *udev; + int i; + + DBG("%s\n", __func__); + cam = video_drvdata(file); + + mutex_lock(&cam->lock); + udev = cam->udev; + + if (file->private_data == cam->owner) { + /* turn off stream */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + videobuf_streamoff(&cam->vb_vidq); + + for (i = 0; i < 2; i++) { + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + } + cam->owner = NULL; + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + mutex_unlock(&cam->lock); + return v4l2_fh_release(file); +} + + +static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int ret; + + if (cam == NULL) { + DBG("%s: cam == NULL\n", __func__); + return -ENODEV; + } + DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); + + ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); + + DBG("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; +} + +static unsigned int zr364xx_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + unsigned res = v4l2_ctrl_poll(file, wait); + + _DBG("%s\n", __func__); + + return res | videobuf_poll_stream(file, q, wait); +} + +static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { + .s_ctrl = zr364xx_s_ctrl, +}; + +static const struct v4l2_file_operations zr364xx_fops = { + .owner = THIS_MODULE, + .open = zr364xx_open, + .release = zr364xx_close, + .read = zr364xx_read, + .mmap = zr364xx_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = zr364xx_poll, +}; + +static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { + .vidioc_querycap = zr364xx_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, + .vidioc_enum_input = zr364xx_vidioc_enum_input, + .vidioc_g_input = zr364xx_vidioc_g_input, + .vidioc_s_input = zr364xx_vidioc_s_input, + .vidioc_streamon = zr364xx_vidioc_streamon, + .vidioc_streamoff = zr364xx_vidioc_streamoff, + .vidioc_reqbufs = zr364xx_vidioc_reqbufs, + .vidioc_querybuf = zr364xx_vidioc_querybuf, + .vidioc_qbuf = zr364xx_vidioc_qbuf, + .vidioc_dqbuf = zr364xx_vidioc_dqbuf, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device zr364xx_template = { + .name = DRIVER_DESC, + .fops = &zr364xx_fops, + .ioctl_ops = &zr364xx_ioctl_ops, + .release = video_device_release_empty, +}; + + + +/*******************/ +/* USB integration */ +/*******************/ +static int zr364xx_board_init(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe = cam->pipe; + unsigned long i; + + DBG("board init: %p\n", cam); + memset(pipe, 0, sizeof(*pipe)); + pipe->cam = cam; + pipe->transfer_size = BUFFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + DBG("out of memory!\n"); + return -ENOMEM; + } + + cam->b_acquire = 0; + cam->frame_count = 0; + + /*** start create system buffers ***/ + for (i = 0; i < FRAMES; i++) { + /* always allocate maximum size for system buffers */ + cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); + + DBG("valloc %p, idx %lu, pdata %p\n", + &cam->buffer.frame[i], i, + cam->buffer.frame[i].lpvbits); + if (cam->buffer.frame[i].lpvbits == NULL) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. " + "Using less frames\n"); + break; + } + } + + if (i == 0) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return -ENOMEM; + } else + cam->buffer.dwFrames = i; + + /* make sure internal states are set */ + for (i = 0; i < FRAMES; i++) { + cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[i].cur_size = 0; + } + + cam->cur_frame = 0; + cam->last_frame = -1; + /*** end create system buffers ***/ + + /* start read pipe */ + zr364xx_start_readpipe(cam); + DBG(": board initialized\n"); + return 0; +} + +static int zr364xx_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zr364xx_camera *cam = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct v4l2_ctrl_handler *hdl; + int err; + int i; + + DBG("probing...\n"); + + dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); + dev_info(&intf->dev, "model %04x:%04x detected\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + cam = kzalloc(sizeof(struct zr364xx_camera), GFP_KERNEL); + if (cam == NULL) { + dev_err(&udev->dev, "cam: out of memory !\n"); + return -ENOMEM; + } + + cam->v4l2_dev.release = zr364xx_release; + err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); + if (err < 0) { + dev_err(&udev->dev, "couldn't register v4l2_device\n"); + kfree(cam); + return err; + } + hdl = &cam->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); + if (hdl->error) { + err = hdl->error; + dev_err(&udev->dev, "couldn't register control\n"); + goto fail; + } + /* save the init method used by this camera */ + cam->method = id->driver_info; + mutex_init(&cam->lock); + cam->vdev = zr364xx_template; + cam->vdev.lock = &cam->lock; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.ctrl_handler = &cam->ctrl_handler; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); + video_set_drvdata(&cam->vdev, cam); + if (debug) + cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + + cam->udev = udev; + + switch (mode) { + case 1: + dev_info(&udev->dev, "160x120 mode selected\n"); + cam->width = 160; + cam->height = 120; + break; + case 2: + dev_info(&udev->dev, "640x480 mode selected\n"); + cam->width = 640; + cam->height = 480; + break; + default: + dev_info(&udev->dev, "320x240 mode selected\n"); + cam->width = 320; + cam->height = 240; + break; + } + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + cam->nb = 0; + + DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); + + /* set up the endpoint information */ + iface_desc = intf->cur_altsetting; + DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + cam->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!cam->read_endpoint) { + err = -ENOMEM; + dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + goto fail; + } + + /* v4l */ + INIT_LIST_HEAD(&cam->vidq.active); + cam->vidq.cam = cam; + + usb_set_intfdata(intf, cam); + + /* load zr364xx board specific */ + err = zr364xx_board_init(cam); + if (!err) + err = v4l2_ctrl_handler_setup(hdl); + if (err) + goto fail; + + spin_lock_init(&cam->slock); + + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam, &cam->lock); + + err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + if (err) { + dev_err(&udev->dev, "video_register_device failed\n"); + goto fail; + } + + dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", + video_device_node_name(&cam->vdev)); + return 0; + +fail: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); + return err; +} + + +static void zr364xx_disconnect(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->lock); + usb_set_intfdata(intf, NULL); + dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); + video_unregister_device(&cam->vdev); + v4l2_device_disconnect(&cam->v4l2_dev); + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + mutex_unlock(&cam->lock); + v4l2_device_put(&cam->v4l2_dev); +} + + +#ifdef CONFIG_PM +static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + cam->was_streaming = cam->b_acquire; + if (!cam->was_streaming) + return 0; + zr364xx_stop_acquire(cam); + zr364xx_stop_readpipe(cam); + return 0; +} + +static int zr364xx_resume(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + int res; + + if (!cam->was_streaming) + return 0; + + zr364xx_start_readpipe(cam); + res = zr364xx_prepare(cam); + if (!res) + zr364xx_start_acquire(cam); + return res; +} +#endif + +/**********************/ +/* Module integration */ +/**********************/ + +static struct usb_driver zr364xx_driver = { + .name = "zr364xx", + .probe = zr364xx_probe, + .disconnect = zr364xx_disconnect, +#ifdef CONFIG_PM + .suspend = zr364xx_suspend, + .resume = zr364xx_resume, + .reset_resume = zr364xx_resume, +#endif + .id_table = device_table +}; + +module_usb_driver(zr364xx_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 097b17ced172..f52799232029 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -605,56 +605,6 @@ config VIDEO_VIVI Say Y here if you want to test video apps or debug V4L devices. In doubt, say N. -# -# USB Multimedia device configuration -# - -menuconfig V4L_USB_DRIVERS - bool "V4L USB devices" - depends on USB - default y - -if V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT - -config USB_ZR364XX - tristate "USB ZR364XX Camera support" - depends on VIDEO_V4L2 - select VIDEOBUF_GEN - select VIDEOBUF_VMALLOC - ---help--- - Say Y here if you want to connect this type of camera to your - computer's USB port. - See for more info - and list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called zr364xx. - -config USB_STKWEBCAM - tristate "USB Syntek DC1125 Camera support" - depends on VIDEO_V4L2 && EXPERIMENTAL - ---help--- - Say Y here if you want to use this type of camera. - Supported devices are typically found in some Asus laptops, - with USB id 174f:a311 and 05e1:0501. Other Syntek cameras - may be supported by the stk11xx driver, from which this is - derived, see - - To compile this driver as a module, choose M here: the - module will be called stkwebcam. - -config USB_S2255 - tristate "USB Sensoray 2255 video capture device" - depends on VIDEO_V4L2 - select VIDEOBUF_VMALLOC - default n - help - Say Y here if you want support for the Sensoray 2255 USB device. - This driver can be compiled as a module, called s2255drv. - - -endif # V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT - # # PCI drivers configuration - No devices here are for webcams # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a22a2580ce30..4ad5bd9246bf 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -4,8 +4,6 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o -stkwebcam-objs := stk-webcam.o stk-sensor.o - omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o # Helper modules @@ -119,12 +117,6 @@ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ -obj-$(CONFIG_USB_ZR364XX) += zr364xx.o -obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o - - -obj-$(CONFIG_USB_S2255) += s2255drv.o - obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_CX18) += cx18/ diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c deleted file mode 100644 index 6c7960cc7506..000000000000 --- a/drivers/media/video/s2255drv.c +++ /dev/null @@ -1,2689 +0,0 @@ -/* - * s2255drv.c - a driver for the Sensoray 2255 USB video capture device - * - * Copyright (C) 2007-2010 by Sensoray Company Inc. - * Dean Anderson - * - * Some video buffer code based on vivi driver: - * - * Sensoray 2255 device supports 4 simultaneous channels. - * The channels are not "crossbar" inputs, they are physically - * attached to separate video decoders. - * - * Because of USB2.0 bandwidth limitations. There is only a - * certain amount of data which may be transferred at one time. - * - * Example maximum bandwidth utilization: - * - * -full size, color mode YUYV or YUV422P: 2 channels at once - * -full or half size Grey scale: all 4 channels at once - * -half size, color mode YUYV or YUV422P: all 4 channels at once - * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels - * at once. - * - * This program is free software; 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 -#include -#include -#include -#include - -#define S2255_VERSION "1.22.1" -#define FIRMWARE_FILE_NAME "f2255usb.bin" - -/* default JPEG quality */ -#define S2255_DEF_JPEG_QUAL 50 -/* vendor request in */ -#define S2255_VR_IN 0 -/* vendor request out */ -#define S2255_VR_OUT 1 -/* firmware query */ -#define S2255_VR_FW 0x30 -/* USB endpoint number for configuring the device */ -#define S2255_CONFIG_EP 2 -/* maximum time for DSP to start responding after last FW word loaded(ms) */ -#define S2255_DSP_BOOTTIME 800 -/* maximum time to wait for firmware to load (ms) */ -#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) -#define S2255_DEF_BUFS 16 -#define S2255_SETMODE_TIMEOUT 500 -#define S2255_VIDSTATUS_TIMEOUT 350 -#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) -#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) -#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) -#define S2255_RESPONSE_FW cpu_to_le32(0x10) -#define S2255_RESPONSE_STATUS cpu_to_le32(0x20) -#define S2255_USB_XFER_SIZE (16 * 1024) -#define MAX_CHANNELS 4 -#define SYS_FRAMES 4 -/* maximum size is PAL full size plus room for the marker header(s) */ -#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) -#define DEF_USB_BLOCK S2255_USB_XFER_SIZE -#define LINE_SZ_4CIFS_NTSC 640 -#define LINE_SZ_2CIFS_NTSC 640 -#define LINE_SZ_1CIFS_NTSC 320 -#define LINE_SZ_4CIFS_PAL 704 -#define LINE_SZ_2CIFS_PAL 704 -#define LINE_SZ_1CIFS_PAL 352 -#define NUM_LINES_4CIFS_NTSC 240 -#define NUM_LINES_2CIFS_NTSC 240 -#define NUM_LINES_1CIFS_NTSC 240 -#define NUM_LINES_4CIFS_PAL 288 -#define NUM_LINES_2CIFS_PAL 288 -#define NUM_LINES_1CIFS_PAL 288 -#define LINE_SZ_DEF 640 -#define NUM_LINES_DEF 240 - - -/* predefined settings */ -#define FORMAT_NTSC 1 -#define FORMAT_PAL 2 - -#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ -#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ -#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ -/* SCALE_4CIFSI is the 2 fields interpolated into one */ -#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */ - -#define COLOR_YUVPL 1 /* YUV planar */ -#define COLOR_YUVPK 2 /* YUV packed */ -#define COLOR_Y8 4 /* monochrome */ -#define COLOR_JPG 5 /* JPEG */ - -#define MASK_COLOR 0x000000ff -#define MASK_JPG_QUALITY 0x0000ff00 -#define MASK_INPUT_TYPE 0x000f0000 -/* frame decimation. */ -#define FDEC_1 1 /* capture every frame. default */ -#define FDEC_2 2 /* capture every 2nd frame */ -#define FDEC_3 3 /* capture every 3rd frame */ -#define FDEC_5 5 /* capture every 5th frame */ - -/*------------------------------------------------------- - * Default mode parameters. - *-------------------------------------------------------*/ -#define DEF_SCALE SCALE_4CIFS -#define DEF_COLOR COLOR_YUVPL -#define DEF_FDEC FDEC_1 -#define DEF_BRIGHT 0 -#define DEF_CONTRAST 0x5c -#define DEF_SATURATION 0x80 -#define DEF_HUE 0 - -/* usb config commands */ -#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) -#define CMD_2255 0xc2255000 -#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) -#define CMD_START cpu_to_le32((CMD_2255 | 0x20)) -#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) -#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) - -struct s2255_mode { - u32 format; /* input video format (NTSC, PAL) */ - u32 scale; /* output video scale */ - u32 color; /* output video color format */ - u32 fdec; /* frame decimation */ - u32 bright; /* brightness */ - u32 contrast; /* contrast */ - u32 saturation; /* saturation */ - u32 hue; /* hue (NTSC only)*/ - u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/ - u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */ - u32 restart; /* if DSP requires restart */ -}; - - -#define S2255_READ_IDLE 0 -#define S2255_READ_FRAME 1 - -/* frame structure */ -struct s2255_framei { - unsigned long size; - unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ - void *lpvbits; /* image data */ - unsigned long cur_size; /* current data copied to it */ -}; - -/* image buffer structure */ -struct s2255_bufferi { - unsigned long dwFrames; /* number of frames in buffer */ - struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ -}; - -#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \ - DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \ - DEF_HUE, 0, DEF_USB_BLOCK, 0} - -struct s2255_dmaqueue { - struct list_head active; - struct s2255_dev *dev; -}; - -/* for firmware loading, fw_state */ -#define S2255_FW_NOTLOADED 0 -#define S2255_FW_LOADED_DSPWAIT 1 -#define S2255_FW_SUCCESS 2 -#define S2255_FW_FAILED 3 -#define S2255_FW_DISCONNECTING 4 -#define S2255_FW_MARKER cpu_to_le32(0x22552f2f) -/* 2255 read states */ -#define S2255_READ_IDLE 0 -#define S2255_READ_FRAME 1 -struct s2255_fw { - int fw_loaded; - int fw_size; - struct urb *fw_urb; - atomic_t fw_state; - void *pfw_data; - wait_queue_head_t wait_fw; - const struct firmware *fw; -}; - -struct s2255_pipeinfo { - u32 max_transfer_size; - u32 cur_transfer_size; - u8 *transfer_buffer; - u32 state; - void *stream_urb; - void *dev; /* back pointer to s2255_dev struct*/ - u32 err_count; - u32 idx; -}; - -struct s2255_fmt; /*forward declaration */ -struct s2255_dev; - -struct s2255_channel { - struct video_device vdev; - int resources; - struct s2255_dmaqueue vidq; - struct s2255_bufferi buffer; - struct s2255_mode mode; - /* jpeg compression */ - struct v4l2_jpegcompression jc; - /* capture parameters (for high quality mode full size) */ - struct v4l2_captureparm cap_parm; - int cur_frame; - int last_frame; - - int b_acquire; - /* allocated image size */ - unsigned long req_image_size; - /* received packet size */ - unsigned long pkt_size; - int bad_payload; - unsigned long frame_count; - /* if JPEG image */ - int jpg_size; - /* if channel configured to default state */ - int configured; - wait_queue_head_t wait_setmode; - int setmode_ready; - /* video status items */ - int vidstatus; - wait_queue_head_t wait_vidstatus; - int vidstatus_ready; - unsigned int width; - unsigned int height; - const struct s2255_fmt *fmt; - int idx; /* channel number on device, 0-3 */ -}; - - -struct s2255_dev { - struct s2255_channel channel[MAX_CHANNELS]; - struct v4l2_device v4l2_dev; - atomic_t num_channels; - int frames; - struct mutex lock; /* channels[].vdev.lock */ - struct usb_device *udev; - struct usb_interface *interface; - u8 read_endpoint; - struct timer_list timer; - struct s2255_fw *fw_data; - struct s2255_pipeinfo pipe; - u32 cc; /* current channel */ - int frame_ready; - int chn_ready; - spinlock_t slock; - /* dsp firmware version (f2255usb.bin) */ - int dsp_fw_ver; - u16 pid; /* product id */ -}; - -static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); -} - -struct s2255_fmt { - char *name; - u32 fourcc; - int depth; -}; - -/* buffer for one video frame */ -struct s2255_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - const struct s2255_fmt *fmt; -}; - -struct s2255_fh { - struct s2255_dev *dev; - struct videobuf_queue vb_vidq; - enum v4l2_buf_type type; - struct s2255_channel *channel; - int resources; -}; - -/* current cypress EEPROM firmware version */ -#define S2255_CUR_USB_FWVER ((3 << 8) | 12) -/* current DSP FW version */ -#define S2255_CUR_DSP_FWVER 10104 -/* Need DSP version 5+ for video status feature */ -#define S2255_MIN_DSP_STATUS 5 -#define S2255_MIN_DSP_COLORFILTER 8 -#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) - -/* private V4L2 controls */ - -/* - * The following chart displays how COLORFILTER should be set - * ========================================================= - * = fourcc = COLORFILTER = - * = =============================== - * = = 0 = 1 = - * ========================================================= - * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= - * = = s-video or = composite = - * = = B/W camera = input = - * ========================================================= - * = other = color, svideo = color, = - * = = = composite = - * ========================================================= - * - * Notes: - * channels 0-3 on 2255 are composite - * channels 0-1 on 2257 are composite, 2-3 are s-video - * If COLORFILTER is 0 with a composite color camera connected, - * the output will appear monochrome but hatching - * will occur. - * COLORFILTER is different from "color killer" and "color effects" - * for reasons above. - */ -#define S2255_V4L2_YC_ON 1 -#define S2255_V4L2_YC_OFF 0 -#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) - -/* frame prefix size (sent once every frame) */ -#define PREFIX_SIZE 512 - -/* Channels on box are in reverse order */ -static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0}; - -static int debug; -static int *s2255_debug = &debug; - -static int s2255_start_readpipe(struct s2255_dev *dev); -static void s2255_stop_readpipe(struct s2255_dev *dev); -static int s2255_start_acquire(struct s2255_channel *channel); -static int s2255_stop_acquire(struct s2255_channel *channel); -static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf, - int jpgsize); -static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode); -static int s2255_board_shutdown(struct s2255_dev *dev); -static void s2255_fwload_start(struct s2255_dev *dev, int reset); -static void s2255_destroy(struct s2255_dev *dev); -static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, - u16 index, u16 value, void *buf, - s32 buf_len, int bOut); - -/* dev_err macro with driver name */ -#define S2255_DRIVER_NAME "s2255" -#define s2255_dev_err(dev, fmt, arg...) \ - dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) - -#define dprintk(level, fmt, arg...) \ - do { \ - if (*s2255_debug >= (level)) { \ - printk(KERN_DEBUG S2255_DRIVER_NAME \ - ": " fmt, ##arg); \ - } \ - } while (0) - -static struct usb_driver s2255_driver; - -/* Declare static vars that will be used as parameters */ -static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ - -/* start video number */ -static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ - -/* Enable jpeg capture. */ -static int jpeg_enable = 1; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); -module_param(vid_limit, int, 0644); -MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)"); -module_param(video_nr, int, 0644); -MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); -module_param(jpeg_enable, int, 0644); -MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1"); - -/* USB device table */ -#define USB_SENSORAY_VID 0x1943 -static struct usb_device_id s2255_table[] = { - {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, - {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, s2255_table); - -#define BUFFER_TIMEOUT msecs_to_jiffies(400) - -/* image formats. */ -/* JPEG formats must be defined last to support jpeg_enable parameter */ -static const struct s2255_fmt formats[] = { - { - .name = "4:2:2, planar, YUV422P", - .fourcc = V4L2_PIX_FMT_YUV422P, - .depth = 16 - - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16 - - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16 - }, { - .name = "8bpp GREY", - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8 - }, { - .name = "JPG", - .fourcc = V4L2_PIX_FMT_JPEG, - .depth = 24 - }, { - .name = "MJPG", - .fourcc = V4L2_PIX_FMT_MJPEG, - .depth = 24 - } -}; - -static int norm_maxw(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; -} - -static int norm_maxh(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); -} - -static int norm_minw(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; -} - -static int norm_minh(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); -} - - -/* - * TODO: fixme: move YUV reordering to hardware - * converts 2255 planar format to yuyv or uyvy - */ -static void planar422p_to_yuv_packed(const unsigned char *in, - unsigned char *out, - int width, int height, - int fmt) -{ - unsigned char *pY; - unsigned char *pCb; - unsigned char *pCr; - unsigned long size = height * width; - unsigned int i; - pY = (unsigned char *)in; - pCr = (unsigned char *)in + height * width; - pCb = (unsigned char *)in + height * width + (height * width / 2); - for (i = 0; i < size * 2; i += 4) { - out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; - out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; - out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; - out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; - } - return; -} - -static void s2255_reset_dsppower(struct s2255_dev *dev) -{ - s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1); - msleep(10); - s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); - msleep(600); - s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1); - return; -} - -/* kickstarts the firmware loading. from probe - */ -static void s2255_timer(unsigned long user_data) -{ - struct s2255_fw *data = (struct s2255_fw *)user_data; - dprintk(100, "%s\n", __func__); - if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { - printk(KERN_ERR "s2255: can't submit urb\n"); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } -} - - -/* this loads the firmware asynchronously. - Originally this was done synchroously in probe. - But it is better to load it asynchronously here than block - inside the probe function. Blocking inside probe affects boot time. - FW loading is triggered by the timer in the probe function -*/ -static void s2255_fwchunk_complete(struct urb *urb) -{ - struct s2255_fw *data = urb->context; - struct usb_device *udev = urb->dev; - int len; - dprintk(100, "%s: udev %p urb %p", __func__, udev, urb); - if (urb->status) { - dev_err(&udev->dev, "URB failed with status %d\n", urb->status); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } - if (data->fw_urb == NULL) { - s2255_dev_err(&udev->dev, "disconnected\n"); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } -#define CHUNK_SIZE 512 - /* all USB transfers must be done with continuous kernel memory. - can't allocate more than 128k in current linux kernel, so - upload the firmware in chunks - */ - if (data->fw_loaded < data->fw_size) { - len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? - data->fw_size % CHUNK_SIZE : CHUNK_SIZE; - - if (len < CHUNK_SIZE) - memset(data->pfw_data, 0, CHUNK_SIZE); - - dprintk(100, "completed len %d, loaded %d \n", len, - data->fw_loaded); - - memcpy(data->pfw_data, - (char *) data->fw->data + data->fw_loaded, len); - - usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), - data->pfw_data, CHUNK_SIZE, - s2255_fwchunk_complete, data); - if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { - dev_err(&udev->dev, "failed submit URB\n"); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } - data->fw_loaded += len; - } else { - atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); - dprintk(100, "%s: firmware upload complete\n", __func__); - } - return; - -} - -static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) -{ - struct s2255_dmaqueue *dma_q = &channel->vidq; - struct s2255_buffer *buf; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - unsigned long flags = 0; - int rc = 0; - spin_lock_irqsave(&dev->slock, flags); - if (list_empty(&dma_q->active)) { - dprintk(1, "No active queue to serve\n"); - rc = -1; - goto unlock; - } - buf = list_entry(dma_q->active.next, - struct s2255_buffer, vb.queue); - list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); - s2255_fillbuff(channel, buf, jpgsize); - wake_up(&buf->vb.done); - dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); -unlock: - spin_unlock_irqrestore(&dev->slock, flags); - return rc; -} - -static const struct s2255_fmt *format_by_fourcc(int fourcc) -{ - unsigned int i; - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (-1 == formats[i].fourcc) - continue; - if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) - continue; - if (formats[i].fourcc == fourcc) - return formats + i; - } - return NULL; -} - -/* video buffer vmalloc implementation based partly on VIVI driver which is - * Copyright (c) 2006 by - * Mauro Carvalho Chehab - * Ted Walther - * John Sokol - * http://v4l.videotechnology.com/ - * - */ -static void s2255_fillbuff(struct s2255_channel *channel, - struct s2255_buffer *buf, int jpgsize) -{ - int pos = 0; - struct timeval ts; - const char *tmpbuf; - char *vbuf = videobuf_to_vmalloc(&buf->vb); - unsigned long last_frame; - - if (!vbuf) - return; - last_frame = channel->last_frame; - if (last_frame != -1) { - tmpbuf = - (const char *)channel->buffer.frame[last_frame].lpvbits; - switch (buf->fmt->fourcc) { - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - planar422p_to_yuv_packed((const unsigned char *)tmpbuf, - vbuf, buf->vb.width, - buf->vb.height, - buf->fmt->fourcc); - break; - case V4L2_PIX_FMT_GREY: - memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); - break; - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_MJPEG: - buf->vb.size = jpgsize; - memcpy(vbuf, tmpbuf, buf->vb.size); - break; - case V4L2_PIX_FMT_YUV422P: - memcpy(vbuf, tmpbuf, - buf->vb.width * buf->vb.height * 2); - break; - default: - printk(KERN_DEBUG "s2255: unknown format?\n"); - } - channel->last_frame = -1; - } else { - printk(KERN_ERR "s2255: =======no frame\n"); - return; - - } - dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n", - (unsigned long)vbuf, pos); - /* tell v4l buffer was filled */ - - buf->vb.field_count = channel->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; - buf->vb.state = VIDEOBUF_DONE; -} - - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct s2255_fh *fh = vq->priv_data; - struct s2255_channel *channel = fh->channel; - *size = channel->width * channel->height * (channel->fmt->depth >> 3); - - if (0 == *count) - *count = S2255_DEF_BUFS; - - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf) -{ - dprintk(4, "%s\n", __func__); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct s2255_fh *fh = vq->priv_data; - struct s2255_channel *channel = fh->channel; - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); - int rc; - int w = channel->width; - int h = channel->height; - dprintk(4, "%s, field=%d\n", __func__, field); - if (channel->fmt == NULL) - return -EINVAL; - - if ((w < norm_minw(&channel->vdev)) || - (w > norm_maxw(&channel->vdev)) || - (h < norm_minh(&channel->vdev)) || - (h > norm_maxh(&channel->vdev))) { - dprintk(4, "invalid buffer prepare\n"); - return -EINVAL; - } - buf->vb.size = w * h * (channel->fmt->depth >> 3); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) { - dprintk(4, "invalid buffer prepare\n"); - return -EINVAL; - } - - buf->fmt = channel->fmt; - buf->vb.width = w; - buf->vb.height = h; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; -fail: - free_buffer(vq, buf); - return rc; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); - struct s2255_fh *fh = vq->priv_data; - struct s2255_channel *channel = fh->channel; - struct s2255_dmaqueue *vidq = &channel->vidq; - dprintk(1, "%s\n", __func__); - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); - struct s2255_fh *fh = vq->priv_data; - dprintk(4, "%s %d\n", __func__, fh->channel->idx); - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops s2255_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - - -static int res_get(struct s2255_fh *fh) -{ - struct s2255_channel *channel = fh->channel; - /* is it free? */ - if (channel->resources) - return 0; /* no, someone else uses it */ - /* it's free, grab it */ - channel->resources = 1; - fh->resources = 1; - dprintk(1, "s2255: res: get\n"); - return 1; -} - -static int res_locked(struct s2255_fh *fh) -{ - return fh->channel->resources; -} - -static int res_check(struct s2255_fh *fh) -{ - return fh->resources; -} - - -static void res_free(struct s2255_fh *fh) -{ - struct s2255_channel *channel = fh->channel; - channel->resources = 0; - fh->resources = 0; - dprintk(1, "res: put\n"); -} - -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - static const char *colorfilter[] = { - "Off", - "On", - NULL - }; - if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { - int i; - const char **menu_items = colorfilter; - for (i = 0; i < qmenu->index && menu_items[i]; i++) - ; /* do nothing (from v4l2-common.c) */ - if (menu_items[i] == NULL || menu_items[i][0] == '\0') - return -EINVAL; - strlcpy(qmenu->name, menu_items[qmenu->index], - sizeof(qmenu->name)); - return 0; - } - return v4l2_ctrl_query_menu(qmenu, NULL, NULL); -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - strlcpy(cap->driver, "s2255", sizeof(cap->driver)); - strlcpy(cap->card, "s2255", sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - int index = f->index; - - if (index >= ARRAY_SIZE(formats)) - return -EINVAL; - if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) - return -EINVAL; - dprintk(4, "name %s\n", formats[index].name); - strlcpy(f->description, formats[index].name, sizeof(f->description)); - f->pixelformat = formats[index].fourcc; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - - f->fmt.pix.width = channel->width; - f->fmt.pix.height = channel->height; - f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = channel->fmt->fourcc; - f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3); - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - const struct s2255_fmt *fmt; - enum v4l2_field field; - int b_any_field = 0; - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - int is_ntsc; - is_ntsc = - (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - - if (fmt == NULL) - return -EINVAL; - - field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - b_any_field = 1; - - dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n", - __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); - if (is_ntsc) { - /* NTSC */ - if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { - f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; - if (b_any_field) { - field = V4L2_FIELD_SEQ_TB; - } else if (!((field == V4L2_FIELD_INTERLACED) || - (field == V4L2_FIELD_SEQ_TB) || - (field == V4L2_FIELD_INTERLACED_TB))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - } else { - f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; - if (b_any_field) { - field = V4L2_FIELD_TOP; - } else if (!((field == V4L2_FIELD_TOP) || - (field == V4L2_FIELD_BOTTOM))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - - } - if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; - else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_2CIFS_NTSC; - else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; - else - f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; - } else { - /* PAL */ - if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { - f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; - if (b_any_field) { - field = V4L2_FIELD_SEQ_TB; - } else if (!((field == V4L2_FIELD_INTERLACED) || - (field == V4L2_FIELD_SEQ_TB) || - (field == V4L2_FIELD_INTERLACED_TB))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - } else { - f->fmt.pix.height = NUM_LINES_1CIFS_PAL; - if (b_any_field) { - field = V4L2_FIELD_TOP; - } else if (!((field == V4L2_FIELD_TOP) || - (field == V4L2_FIELD_BOTTOM))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - } - if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { - f->fmt.pix.width = LINE_SZ_4CIFS_PAL; - field = V4L2_FIELD_SEQ_TB; - } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { - f->fmt.pix.width = LINE_SZ_2CIFS_PAL; - field = V4L2_FIELD_TOP; - } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { - f->fmt.pix.width = LINE_SZ_1CIFS_PAL; - field = V4L2_FIELD_TOP; - } else { - f->fmt.pix.width = LINE_SZ_1CIFS_PAL; - field = V4L2_FIELD_TOP; - } - } - f->fmt.pix.field = field; - f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - dprintk(50, "%s: set width %d height %d field %d\n", __func__, - f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - const struct s2255_fmt *fmt; - struct videobuf_queue *q = &fh->vb_vidq; - struct s2255_mode mode; - int ret; - - ret = vidioc_try_fmt_vid_cap(file, fh, f); - - if (ret < 0) - return ret; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - - if (fmt == NULL) - return -EINVAL; - - mutex_lock(&q->vb_lock); - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dprintk(1, "queue busy\n"); - ret = -EBUSY; - goto out_s_fmt; - } - - if (res_locked(fh)) { - dprintk(1, "%s: channel busy\n", __func__); - ret = -EBUSY; - goto out_s_fmt; - } - mode = channel->mode; - channel->fmt = fmt; - channel->width = f->fmt.pix.width; - channel->height = f->fmt.pix.height; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; - if (channel->width > norm_minw(&channel->vdev)) { - if (channel->height > norm_minh(&channel->vdev)) { - if (channel->cap_parm.capturemode & - V4L2_MODE_HIGHQUALITY) - mode.scale = SCALE_4CIFSI; - else - mode.scale = SCALE_4CIFS; - } else - mode.scale = SCALE_2CIFS; - - } else { - mode.scale = SCALE_1CIFS; - } - /* color mode */ - switch (channel->fmt->fourcc) { - case V4L2_PIX_FMT_GREY: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_Y8; - break; - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_MJPEG: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_JPG; - mode.color |= (channel->jc.quality << 8); - break; - case V4L2_PIX_FMT_YUV422P: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_YUVPL; - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - default: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_YUVPK; - break; - } - if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR)) - mode.restart = 1; - else if (mode.scale != channel->mode.scale) - mode.restart = 1; - else if (mode.format != channel->mode.format) - mode.restart = 1; - channel->mode = mode; - (void) s2255_set_mode(channel, &mode); - ret = 0; -out_s_fmt: - mutex_unlock(&q->vb_lock); - return ret; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_reqbufs(&fh->vb_vidq, p); - return rc; -} - -static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_querybuf(&fh->vb_vidq, p); - return rc; -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_qbuf(&fh->vb_vidq, p); - return rc; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK); - return rc; -} - -/* write to the configuration pipe, synchronously */ -static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, - int size) -{ - int pipe; - int done; - long retval = -1; - if (udev) { - pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); - retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); - } - return retval; -} - -static u32 get_transfer_size(struct s2255_mode *mode) -{ - int linesPerFrame = LINE_SZ_DEF; - int pixelsPerLine = NUM_LINES_DEF; - u32 outImageSize; - u32 usbInSize; - unsigned int mask_mult; - - if (mode == NULL) - return 0; - - if (mode->format == FORMAT_NTSC) { - switch (mode->scale) { - case SCALE_4CIFS: - case SCALE_4CIFSI: - linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; - pixelsPerLine = LINE_SZ_4CIFS_NTSC; - break; - case SCALE_2CIFS: - linesPerFrame = NUM_LINES_2CIFS_NTSC; - pixelsPerLine = LINE_SZ_2CIFS_NTSC; - break; - case SCALE_1CIFS: - linesPerFrame = NUM_LINES_1CIFS_NTSC; - pixelsPerLine = LINE_SZ_1CIFS_NTSC; - break; - default: - break; - } - } else if (mode->format == FORMAT_PAL) { - switch (mode->scale) { - case SCALE_4CIFS: - case SCALE_4CIFSI: - linesPerFrame = NUM_LINES_4CIFS_PAL * 2; - pixelsPerLine = LINE_SZ_4CIFS_PAL; - break; - case SCALE_2CIFS: - linesPerFrame = NUM_LINES_2CIFS_PAL; - pixelsPerLine = LINE_SZ_2CIFS_PAL; - break; - case SCALE_1CIFS: - linesPerFrame = NUM_LINES_1CIFS_PAL; - pixelsPerLine = LINE_SZ_1CIFS_PAL; - break; - default: - break; - } - } - outImageSize = linesPerFrame * pixelsPerLine; - if ((mode->color & MASK_COLOR) != COLOR_Y8) { - /* 2 bytes/pixel if not monochrome */ - outImageSize *= 2; - } - - /* total bytes to send including prefix and 4K padding; - must be a multiple of USB_READ_SIZE */ - usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ - mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; - /* if size not a multiple of USB_READ_SIZE */ - if (usbInSize & ~mask_mult) - usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); - return usbInSize; -} - -static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) -{ - struct device *dev = &sdev->udev->dev; - dev_info(dev, "------------------------------------------------\n"); - dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); - dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); - dev_info(dev, "bright: 0x%x\n", mode->bright); - dev_info(dev, "------------------------------------------------\n"); -} - -/* - * set mode is the function which controls the DSP. - * the restart parameter in struct s2255_mode should be set whenever - * the image size could change via color format, video system or image - * size. - * When the restart parameter is set, we sleep for ONE frame to allow the - * DSP time to get the new frame - */ -static int s2255_set_mode(struct s2255_channel *channel, - struct s2255_mode *mode) -{ - int res; - __le32 *buffer; - unsigned long chn_rev; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - dprintk(3, "%s channel: %d\n", __func__, channel->idx); - /* if JPEG, set the quality */ - if ((mode->color & MASK_COLOR) == COLOR_JPG) { - mode->color &= ~MASK_COLOR; - mode->color |= COLOR_JPG; - mode->color &= ~MASK_JPG_QUALITY; - mode->color |= (channel->jc.quality << 8); - } - /* save the mode */ - channel->mode = *mode; - channel->req_image_size = get_transfer_size(mode); - dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size); - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - /* set the mode */ - buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); - buffer[2] = CMD_SET_MODE; - memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode)); - channel->setmode_ready = 0; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (debug) - s2255_print_cfg(dev, mode); - kfree(buffer); - /* wait at least 3 frames before continuing */ - if (mode->restart) { - wait_event_timeout(channel->wait_setmode, - (channel->setmode_ready != 0), - msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); - if (channel->setmode_ready != 1) { - printk(KERN_DEBUG "s2255: no set mode response\n"); - res = -EFAULT; - } - } - /* clear the restart flag */ - channel->mode.restart = 0; - dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res); - return res; -} - -static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus) -{ - int res; - __le32 *buffer; - u32 chn_rev; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - dprintk(4, "%s chan %d\n", __func__, channel->idx); - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - /* form the get vid status command */ - buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); - buffer[2] = CMD_STATUS; - *pstatus = 0; - channel->vidstatus_ready = 0; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - kfree(buffer); - wait_event_timeout(channel->wait_vidstatus, - (channel->vidstatus_ready != 0), - msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); - if (channel->vidstatus_ready != 1) { - printk(KERN_DEBUG "s2255: no vidstatus response\n"); - res = -EFAULT; - } - *pstatus = channel->vidstatus; - dprintk(4, "%s, vid status %d\n", __func__, *pstatus); - return res; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - int res; - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - int j; - dprintk(4, "%s\n", __func__); - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_err(&dev->udev->dev, "invalid fh type0\n"); - return -EINVAL; - } - if (i != fh->type) { - dev_err(&dev->udev->dev, "invalid fh type1\n"); - return -EINVAL; - } - - if (!res_get(fh)) { - s2255_dev_err(&dev->udev->dev, "stream busy\n"); - return -EBUSY; - } - channel->last_frame = -1; - channel->bad_payload = 0; - channel->cur_frame = 0; - channel->frame_count = 0; - for (j = 0; j < SYS_FRAMES; j++) { - channel->buffer.frame[j].ulState = S2255_READ_IDLE; - channel->buffer.frame[j].cur_size = 0; - } - res = videobuf_streamon(&fh->vb_vidq); - if (res == 0) { - s2255_start_acquire(channel); - channel->b_acquire = 1; - } else - res_free(fh); - - return res; -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct s2255_fh *fh = priv; - dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx); - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - printk(KERN_ERR "invalid fh type0\n"); - return -EINVAL; - } - if (i != fh->type) { - printk(KERN_ERR "invalid type i\n"); - return -EINVAL; - } - s2255_stop_acquire(fh->channel); - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - struct s2255_fh *fh = priv; - struct s2255_mode mode; - struct videobuf_queue *q = &fh->vb_vidq; - int ret = 0; - mutex_lock(&q->vb_lock); - if (videobuf_queue_is_busy(q)) { - dprintk(1, "queue busy\n"); - ret = -EBUSY; - goto out_s_std; - } - if (res_locked(fh)) { - dprintk(1, "can't change standard after started\n"); - ret = -EBUSY; - goto out_s_std; - } - mode = fh->channel->mode; - if (*i & V4L2_STD_NTSC) { - dprintk(4, "%s NTSC\n", __func__); - /* if changing format, reset frame decimation/intervals */ - if (mode.format != FORMAT_NTSC) { - mode.restart = 1; - mode.format = FORMAT_NTSC; - mode.fdec = FDEC_1; - } - } else if (*i & V4L2_STD_PAL) { - dprintk(4, "%s PAL\n", __func__); - if (mode.format != FORMAT_PAL) { - mode.restart = 1; - mode.format = FORMAT_PAL; - mode.fdec = FDEC_1; - } - } else { - ret = -EINVAL; - } - if (mode.restart) - s2255_set_mode(fh->channel, &mode); -out_s_std: - mutex_unlock(&q->vb_lock); - return ret; -} - -/* Sensoray 2255 is a multiple channel capture device. - It does not have a "crossbar" of inputs. - We use one V4L device per channel. The user must - be aware that certain combinations are not allowed. - For instance, you cannot do full FPS on more than 2 channels(2 videodevs) - at once in color(you can do full fps on 4 channels with greyscale. -*/ -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - u32 status = 0; - if (inp->index != 0) - return -EINVAL; - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = S2255_NORMS; - inp->status = 0; - if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { - int rc; - rc = s2255_cmd_status(fh->channel, &status); - dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); - if (rc == 0) - inp->status = (status & 0x01) ? 0 - : V4L2_IN_ST_NO_SIGNAL; - } - switch (dev->pid) { - case 0x2255: - default: - strlcpy(inp->name, "Composite", sizeof(inp->name)); - break; - case 0x2257: - strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video", - sizeof(inp->name)); - break; - } - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - return 0; -} - -/* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = fh->dev; - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); - break; - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); - break; - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); - break; - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE); - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - strlcpy(qc->name, "Color Filter", sizeof(qc->name)); - qc->type = V4L2_CTRL_TYPE_MENU; - qc->minimum = 0; - qc->maximum = 1; - qc->step = 1; - qc->default_value = 1; - qc->flags = 0; - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d\n", __func__, qc->id); - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = channel->mode.bright; - break; - case V4L2_CID_CONTRAST: - ctrl->value = channel->mode.contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = channel->mode.saturation; - break; - case V4L2_CID_HUE: - ctrl->value = channel->mode.hue; - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - struct s2255_mode mode; - mode = channel->mode; - dprintk(4, "%s\n", __func__); - /* update the mode to the corresponding value */ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - mode.bright = ctrl->value; - break; - case V4L2_CID_CONTRAST: - mode.contrast = ctrl->value; - break; - case V4L2_CID_HUE: - mode.hue = ctrl->value; - break; - case V4L2_CID_SATURATION: - mode.saturation = ctrl->value; - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - mode.color &= ~MASK_INPUT_TYPE; - mode.color |= ((ctrl->value ? 0 : 1) << 16); - break; - default: - return -EINVAL; - } - mode.restart = 0; - /* set mode here. Note: stream does not need restarted. - some V4L programs restart stream unnecessarily - after a s_crtl. - */ - s2255_set_mode(fh->channel, &mode); - return 0; -} - -static int vidioc_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jc) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - *jc = channel->jc; - dprintk(2, "%s: quality %d\n", __func__, jc->quality); - return 0; -} - -static int vidioc_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jc) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - if (jc->quality < 0 || jc->quality > 100) - return -EINVAL; - channel->jc.quality = jc->quality; - dprintk(2, "%s: quality %d\n", __func__, jc->quality); - return 0; -} - -static int vidioc_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct s2255_fh *fh = priv; - __u32 def_num, def_dem; - struct s2255_channel *channel = fh->channel; - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(sp, 0, sizeof(struct v4l2_streamparm)); - sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - sp->parm.capture.capturemode = channel->cap_parm.capturemode; - def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000; - sp->parm.capture.timeperframe.denominator = def_dem; - switch (channel->mode.fdec) { - default: - case FDEC_1: - sp->parm.capture.timeperframe.numerator = def_num; - break; - case FDEC_2: - sp->parm.capture.timeperframe.numerator = def_num * 2; - break; - case FDEC_3: - sp->parm.capture.timeperframe.numerator = def_num * 3; - break; - case FDEC_5: - sp->parm.capture.timeperframe.numerator = def_num * 5; - break; - } - dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__, - sp->parm.capture.capturemode, - sp->parm.capture.timeperframe.numerator, - sp->parm.capture.timeperframe.denominator); - return 0; -} - -static int vidioc_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_mode mode; - int fdec = FDEC_1; - __u32 def_num, def_dem; - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mode = channel->mode; - /* high quality capture mode requires a stream restart */ - if (channel->cap_parm.capturemode - != sp->parm.capture.capturemode && res_locked(fh)) - return -EBUSY; - def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; - if (def_dem != sp->parm.capture.timeperframe.denominator) - sp->parm.capture.timeperframe.numerator = def_num; - else if (sp->parm.capture.timeperframe.numerator <= def_num) - sp->parm.capture.timeperframe.numerator = def_num; - else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { - sp->parm.capture.timeperframe.numerator = def_num * 2; - fdec = FDEC_2; - } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { - sp->parm.capture.timeperframe.numerator = def_num * 3; - fdec = FDEC_3; - } else { - sp->parm.capture.timeperframe.numerator = def_num * 5; - fdec = FDEC_5; - } - mode.fdec = fdec; - sp->parm.capture.timeperframe.denominator = def_dem; - s2255_set_mode(channel, &mode); - dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", - __func__, - sp->parm.capture.capturemode, - sp->parm.capture.timeperframe.numerator, - sp->parm.capture.timeperframe.denominator, fdec); - return 0; -} - -static int vidioc_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fe) -{ - int is_ntsc = 0; -#define NUM_FRAME_ENUMS 4 - int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; - if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS) - return -EINVAL; - switch (fe->width) { - case 640: - if (fe->height != 240 && fe->height != 480) - return -EINVAL; - is_ntsc = 1; - break; - case 320: - if (fe->height != 240) - return -EINVAL; - is_ntsc = 1; - break; - case 704: - if (fe->height != 288 && fe->height != 576) - return -EINVAL; - break; - case 352: - if (fe->height != 288) - return -EINVAL; - break; - default: - return -EINVAL; - } - fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fe->discrete.denominator = is_ntsc ? 30000 : 25000; - fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; - dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator, - fe->discrete.denominator); - return 0; -} - -static int __s2255_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct s2255_channel *channel = video_drvdata(file); - struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); - struct s2255_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int state; - dprintk(1, "s2255: open called (dev=%s)\n", - video_device_node_name(vdev)); - state = atomic_read(&dev->fw_data->fw_state); - switch (state) { - case S2255_FW_DISCONNECTING: - return -ENODEV; - case S2255_FW_FAILED: - s2255_dev_err(&dev->udev->dev, - "firmware load failed. retrying.\n"); - s2255_fwload_start(dev, 1); - wait_event_timeout(dev->fw_data->wait_fw, - ((atomic_read(&dev->fw_data->fw_state) - == S2255_FW_SUCCESS) || - (atomic_read(&dev->fw_data->fw_state) - == S2255_FW_DISCONNECTING)), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - /* state may have changed, re-read */ - state = atomic_read(&dev->fw_data->fw_state); - break; - case S2255_FW_NOTLOADED: - case S2255_FW_LOADED_DSPWAIT: - /* give S2255_LOAD_TIMEOUT time for firmware to load in case - driver loaded and then device immediately opened */ - printk(KERN_INFO "%s waiting for firmware load\n", __func__); - wait_event_timeout(dev->fw_data->wait_fw, - ((atomic_read(&dev->fw_data->fw_state) - == S2255_FW_SUCCESS) || - (atomic_read(&dev->fw_data->fw_state) - == S2255_FW_DISCONNECTING)), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - /* state may have changed, re-read */ - state = atomic_read(&dev->fw_data->fw_state); - break; - case S2255_FW_SUCCESS: - default: - break; - } - /* state may have changed in above switch statement */ - switch (state) { - case S2255_FW_SUCCESS: - break; - case S2255_FW_FAILED: - printk(KERN_INFO "2255 firmware load failed.\n"); - return -ENODEV; - case S2255_FW_DISCONNECTING: - printk(KERN_INFO "%s: disconnecting\n", __func__); - return -ENODEV; - case S2255_FW_LOADED_DSPWAIT: - case S2255_FW_NOTLOADED: - printk(KERN_INFO "%s: firmware not loaded yet" - "please try again later\n", - __func__); - /* - * Timeout on firmware load means device unusable. - * Set firmware failure state. - * On next s2255_open the firmware will be reloaded. - */ - atomic_set(&dev->fw_data->fw_state, - S2255_FW_FAILED); - return -EAGAIN; - default: - printk(KERN_INFO "%s: unknown state\n", __func__); - return -EFAULT; - } - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - file->private_data = fh; - fh->dev = dev; - fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fh->channel = channel; - if (!channel->configured) { - /* configure channel to default state */ - channel->fmt = &formats[0]; - s2255_set_mode(channel, &channel->mode); - channel->configured = 1; - } - dprintk(1, "%s: dev=%s type=%s\n", __func__, - video_device_node_name(vdev), v4l2_type_names[type]); - dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__, - (unsigned long)fh, (unsigned long)dev, - (unsigned long)&channel->vidq); - dprintk(4, "%s: list_empty active=%d\n", __func__, - list_empty(&channel->vidq.active)); - videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, - NULL, &dev->slock, - fh->type, - V4L2_FIELD_INTERLACED, - sizeof(struct s2255_buffer), - fh, vdev->lock); - return 0; -} - -static int s2255_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - int ret; - - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - ret = __s2255_open(file); - mutex_unlock(vdev->lock); - return ret; -} - -static unsigned int s2255_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - int rc; - dprintk(100, "%s\n", __func__); - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return POLLERR; - mutex_lock(&dev->lock); - rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); - mutex_unlock(&dev->lock); - return rc; -} - -static void s2255_destroy(struct s2255_dev *dev) -{ - /* board shutdown stops the read pipe if it is running */ - s2255_board_shutdown(dev); - /* make sure firmware still not trying to load */ - del_timer(&dev->timer); /* only started in .probe and .open */ - if (dev->fw_data->fw_urb) { - usb_kill_urb(dev->fw_data->fw_urb); - usb_free_urb(dev->fw_data->fw_urb); - dev->fw_data->fw_urb = NULL; - } - release_firmware(dev->fw_data->fw); - kfree(dev->fw_data->pfw_data); - kfree(dev->fw_data); - /* reset the DSP so firmware can be reloaded next time */ - s2255_reset_dsppower(dev); - mutex_destroy(&dev->lock); - usb_put_dev(dev->udev); - v4l2_device_unregister(&dev->v4l2_dev); - dprintk(1, "%s", __func__); - kfree(dev); -} - -static int s2255_release(struct file *file) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - struct video_device *vdev = video_devdata(file); - struct s2255_channel *channel = fh->channel; - if (!dev) - return -ENODEV; - mutex_lock(&dev->lock); - /* turn off stream */ - if (res_check(fh)) { - if (channel->b_acquire) - s2255_stop_acquire(fh->channel); - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - } - videobuf_mmap_free(&fh->vb_vidq); - mutex_unlock(&dev->lock); - dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); - kfree(fh); - return 0; -} - -static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - int ret; - - if (!fh) - return -ENODEV; - dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma); - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); - mutex_unlock(&dev->lock); - dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__, - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; -} - -static const struct v4l2_file_operations s2255_fops_v4l = { - .owner = THIS_MODULE, - .open = s2255_open, - .release = s2255_release, - .poll = s2255_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = s2255_mmap_v4l, -}; - -static const struct v4l2_ioctl_ops s2255_ioctl_ops = { - .vidioc_querymenu = vidioc_querymenu, - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_s_std = vidioc_s_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_s_jpegcomp = vidioc_s_jpegcomp, - .vidioc_g_jpegcomp = vidioc_g_jpegcomp, - .vidioc_s_parm = vidioc_s_parm, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_enum_frameintervals = vidioc_enum_frameintervals, -}; - -static void s2255_video_device_release(struct video_device *vdev) -{ - struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); - dprintk(4, "%s, chnls: %d \n", __func__, - atomic_read(&dev->num_channels)); - if (atomic_dec_and_test(&dev->num_channels)) - s2255_destroy(dev); - return; -} - -static struct video_device template = { - .name = "s2255v", - .fops = &s2255_fops_v4l, - .ioctl_ops = &s2255_ioctl_ops, - .release = s2255_video_device_release, - .tvnorms = S2255_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; - -static int s2255_probe_v4l(struct s2255_dev *dev) -{ - int ret; - int i; - int cur_nr = video_nr; - struct s2255_channel *channel; - ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); - if (ret) - return ret; - /* initialize all video 4 linux */ - /* register 4 video devices */ - for (i = 0; i < MAX_CHANNELS; i++) { - channel = &dev->channel[i]; - INIT_LIST_HEAD(&channel->vidq.active); - channel->vidq.dev = dev; - /* register 4 video devices */ - channel->vdev = template; - channel->vdev.lock = &dev->lock; - channel->vdev.v4l2_dev = &dev->v4l2_dev; - video_set_drvdata(&channel->vdev, channel); - if (video_nr == -1) - ret = video_register_device(&channel->vdev, - VFL_TYPE_GRABBER, - video_nr); - else - ret = video_register_device(&channel->vdev, - VFL_TYPE_GRABBER, - cur_nr + i); - - if (ret) { - dev_err(&dev->udev->dev, - "failed to register video device!\n"); - break; - } - atomic_inc(&dev->num_channels); - v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(&channel->vdev)); - - } - printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n", - S2255_VERSION); - /* if no channels registered, return error and probe will fail*/ - if (atomic_read(&dev->num_channels) == 0) { - v4l2_device_unregister(&dev->v4l2_dev); - return ret; - } - if (atomic_read(&dev->num_channels) != MAX_CHANNELS) - printk(KERN_WARNING "s2255: Not all channels available.\n"); - return 0; -} - -/* this function moves the usb stream read pipe data - * into the system buffers. - * returns 0 on success, EAGAIN if more data to process( call this - * function again). - * - * Received frame structure: - * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) - * bytes 4-7: channel: 0-3 - * bytes 8-11: payload size: size of the frame - * bytes 12-payloadsize+12: frame data - */ -static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) -{ - char *pdest; - u32 offset = 0; - int bframe = 0; - char *psrc; - unsigned long copy_size; - unsigned long size; - s32 idx = -1; - struct s2255_framei *frm; - unsigned char *pdata; - struct s2255_channel *channel; - dprintk(100, "buffer to user\n"); - channel = &dev->channel[dev->cc]; - idx = channel->cur_frame; - frm = &channel->buffer.frame[idx]; - if (frm->ulState == S2255_READ_IDLE) { - int jj; - unsigned int cc; - __le32 *pdword; /*data from dsp is little endian */ - int payload; - /* search for marker codes */ - pdata = (unsigned char *)pipe_info->transfer_buffer; - pdword = (__le32 *)pdata; - for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { - switch (*pdword) { - case S2255_MARKER_FRAME: - dprintk(4, "found frame marker at offset:" - " %d [%x %x]\n", jj, pdata[0], - pdata[1]); - offset = jj + PREFIX_SIZE; - bframe = 1; - cc = le32_to_cpu(pdword[1]); - if (cc >= MAX_CHANNELS) { - printk(KERN_ERR - "bad channel\n"); - return -EINVAL; - } - /* reverse it */ - dev->cc = G_chnmap[cc]; - channel = &dev->channel[dev->cc]; - payload = le32_to_cpu(pdword[3]); - if (payload > channel->req_image_size) { - channel->bad_payload++; - /* discard the bad frame */ - return -EINVAL; - } - channel->pkt_size = payload; - channel->jpg_size = le32_to_cpu(pdword[4]); - break; - case S2255_MARKER_RESPONSE: - - pdata += DEF_USB_BLOCK; - jj += DEF_USB_BLOCK; - if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) - break; - cc = G_chnmap[le32_to_cpu(pdword[1])]; - if (cc >= MAX_CHANNELS) - break; - channel = &dev->channel[cc]; - switch (pdword[2]) { - case S2255_RESPONSE_SETMODE: - /* check if channel valid */ - /* set mode ready */ - channel->setmode_ready = 1; - wake_up(&channel->wait_setmode); - dprintk(5, "setmode ready %d\n", cc); - break; - case S2255_RESPONSE_FW: - dev->chn_ready |= (1 << cc); - if ((dev->chn_ready & 0x0f) != 0x0f) - break; - /* all channels ready */ - printk(KERN_INFO "s2255: fw loaded\n"); - atomic_set(&dev->fw_data->fw_state, - S2255_FW_SUCCESS); - wake_up(&dev->fw_data->wait_fw); - break; - case S2255_RESPONSE_STATUS: - channel->vidstatus = le32_to_cpu(pdword[3]); - channel->vidstatus_ready = 1; - wake_up(&channel->wait_vidstatus); - dprintk(5, "got vidstatus %x chan %d\n", - le32_to_cpu(pdword[3]), cc); - break; - default: - printk(KERN_INFO "s2255 unknown resp\n"); - } - default: - pdata++; - break; - } - if (bframe) - break; - } /* for */ - if (!bframe) - return -EINVAL; - } - channel = &dev->channel[dev->cc]; - idx = channel->cur_frame; - frm = &channel->buffer.frame[idx]; - /* search done. now find out if should be acquiring on this channel */ - if (!channel->b_acquire) { - /* we found a frame, but this channel is turned off */ - frm->ulState = S2255_READ_IDLE; - return -EINVAL; - } - - if (frm->ulState == S2255_READ_IDLE) { - frm->ulState = S2255_READ_FRAME; - frm->cur_size = 0; - } - - /* skip the marker 512 bytes (and offset if out of sync) */ - psrc = (u8 *)pipe_info->transfer_buffer + offset; - - - if (frm->lpvbits == NULL) { - dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d", - frm, dev, dev->cc, idx); - return -ENOMEM; - } - - pdest = frm->lpvbits + frm->cur_size; - - copy_size = (pipe_info->cur_transfer_size - offset); - - size = channel->pkt_size - PREFIX_SIZE; - - /* sanity check on pdest */ - if ((copy_size + frm->cur_size) < channel->req_image_size) - memcpy(pdest, psrc, copy_size); - - frm->cur_size += copy_size; - dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); - - if (frm->cur_size >= size) { - dprintk(2, "****************[%d]Buffer[%d]full*************\n", - dev->cc, idx); - channel->last_frame = channel->cur_frame; - channel->cur_frame++; - /* end of system frame ring buffer, start at zero */ - if ((channel->cur_frame == SYS_FRAMES) || - (channel->cur_frame == channel->buffer.dwFrames)) - channel->cur_frame = 0; - /* frame ready */ - if (channel->b_acquire) - s2255_got_frame(channel, channel->jpg_size); - channel->frame_count++; - frm->ulState = S2255_READ_IDLE; - frm->cur_size = 0; - - } - /* done successfully */ - return 0; -} - -static void s2255_read_video_callback(struct s2255_dev *dev, - struct s2255_pipeinfo *pipe_info) -{ - int res; - dprintk(50, "callback read video \n"); - - if (dev->cc >= MAX_CHANNELS) { - dev->cc = 0; - dev_err(&dev->udev->dev, "invalid channel\n"); - return; - } - /* otherwise copy to the system buffers */ - res = save_frame(dev, pipe_info); - if (res != 0) - dprintk(4, "s2255: read callback failed\n"); - - dprintk(50, "callback read video done\n"); - return; -} - -static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, - u16 Index, u16 Value, void *TransferBuffer, - s32 TransferBufferLength, int bOut) -{ - int r; - if (!bOut) { - r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), - Request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - Value, Index, TransferBuffer, - TransferBufferLength, HZ * 5); - } else { - r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), - Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - Value, Index, TransferBuffer, - TransferBufferLength, HZ * 5); - } - return r; -} - -/* - * retrieve FX2 firmware version. future use. - * @param dev pointer to device extension - * @return -1 for fail, else returns firmware version as an int(16 bits) - */ -static int s2255_get_fx2fw(struct s2255_dev *dev) -{ - int fw; - int ret; - unsigned char transBuffer[64]; - ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2, - S2255_VR_IN); - if (ret < 0) - dprintk(2, "get fw error: %x\n", ret); - fw = transBuffer[0] + (transBuffer[1] << 8); - dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); - return fw; -} - -/* - * Create the system ring buffer to copy frames into from the - * usb read pipe. - */ -static int s2255_create_sys_buffers(struct s2255_channel *channel) -{ - unsigned long i; - unsigned long reqsize; - dprintk(1, "create sys buffers\n"); - channel->buffer.dwFrames = SYS_FRAMES; - /* always allocate maximum size(PAL) for system buffers */ - reqsize = SYS_FRAMES_MAXSIZE; - - if (reqsize > SYS_FRAMES_MAXSIZE) - reqsize = SYS_FRAMES_MAXSIZE; - - for (i = 0; i < SYS_FRAMES; i++) { - /* allocate the frames */ - channel->buffer.frame[i].lpvbits = vmalloc(reqsize); - dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n", - &channel->buffer.frame[i], channel->idx, i, - channel->buffer.frame[i].lpvbits); - channel->buffer.frame[i].size = reqsize; - if (channel->buffer.frame[i].lpvbits == NULL) { - printk(KERN_INFO "out of memory. using less frames\n"); - channel->buffer.dwFrames = i; - break; - } - } - - /* make sure internal states are set */ - for (i = 0; i < SYS_FRAMES; i++) { - channel->buffer.frame[i].ulState = 0; - channel->buffer.frame[i].cur_size = 0; - } - - channel->cur_frame = 0; - channel->last_frame = -1; - return 0; -} - -static int s2255_release_sys_buffers(struct s2255_channel *channel) -{ - unsigned long i; - dprintk(1, "release sys buffers\n"); - for (i = 0; i < SYS_FRAMES; i++) { - if (channel->buffer.frame[i].lpvbits) { - dprintk(1, "vfree %p\n", - channel->buffer.frame[i].lpvbits); - vfree(channel->buffer.frame[i].lpvbits); - } - channel->buffer.frame[i].lpvbits = NULL; - } - return 0; -} - -static int s2255_board_init(struct s2255_dev *dev) -{ - struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; - int fw_ver; - int j; - struct s2255_pipeinfo *pipe = &dev->pipe; - dprintk(4, "board init: %p", dev); - memset(pipe, 0, sizeof(*pipe)); - pipe->dev = dev; - pipe->cur_transfer_size = S2255_USB_XFER_SIZE; - pipe->max_transfer_size = S2255_USB_XFER_SIZE; - - pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, - GFP_KERNEL); - if (pipe->transfer_buffer == NULL) { - dprintk(1, "out of memory!\n"); - return -ENOMEM; - } - /* query the firmware */ - fw_ver = s2255_get_fx2fw(dev); - - printk(KERN_INFO "s2255: usb firmware version %d.%d\n", - (fw_ver >> 8) & 0xff, - fw_ver & 0xff); - - if (fw_ver < S2255_CUR_USB_FWVER) - printk(KERN_INFO "s2255: newer USB firmware available\n"); - - for (j = 0; j < MAX_CHANNELS; j++) { - struct s2255_channel *channel = &dev->channel[j]; - channel->b_acquire = 0; - channel->mode = mode_def; - if (dev->pid == 0x2257 && j > 1) - channel->mode.color |= (1 << 16); - channel->jc.quality = S2255_DEF_JPEG_QUAL; - channel->width = LINE_SZ_4CIFS_NTSC; - channel->height = NUM_LINES_4CIFS_NTSC * 2; - channel->fmt = &formats[0]; - channel->mode.restart = 1; - channel->req_image_size = get_transfer_size(&mode_def); - channel->frame_count = 0; - /* create the system buffers */ - s2255_create_sys_buffers(channel); - } - /* start read pipe */ - s2255_start_readpipe(dev); - dprintk(1, "%s: success\n", __func__); - return 0; -} - -static int s2255_board_shutdown(struct s2255_dev *dev) -{ - u32 i; - dprintk(1, "%s: dev: %p", __func__, dev); - - for (i = 0; i < MAX_CHANNELS; i++) { - if (dev->channel[i].b_acquire) - s2255_stop_acquire(&dev->channel[i]); - } - s2255_stop_readpipe(dev); - for (i = 0; i < MAX_CHANNELS; i++) - s2255_release_sys_buffers(&dev->channel[i]); - /* release transfer buffer */ - kfree(dev->pipe.transfer_buffer); - return 0; -} - -static void read_pipe_completion(struct urb *purb) -{ - struct s2255_pipeinfo *pipe_info; - struct s2255_dev *dev; - int status; - int pipe; - pipe_info = purb->context; - dprintk(100, "%s: urb:%p, status %d\n", __func__, purb, - purb->status); - if (pipe_info == NULL) { - dev_err(&purb->dev->dev, "no context!\n"); - return; - } - - dev = pipe_info->dev; - if (dev == NULL) { - dev_err(&purb->dev->dev, "no context!\n"); - return; - } - status = purb->status; - /* if shutting down, do not resubmit, exit immediately */ - if (status == -ESHUTDOWN) { - dprintk(2, "%s: err shutdown\n", __func__); - pipe_info->err_count++; - return; - } - - if (pipe_info->state == 0) { - dprintk(2, "%s: exiting USB pipe", __func__); - return; - } - - if (status == 0) - s2255_read_video_callback(dev, pipe_info); - else { - pipe_info->err_count++; - dprintk(1, "%s: failed URB %d\n", __func__, status); - } - - pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); - /* reuse urb */ - usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->cur_transfer_size, - read_pipe_completion, pipe_info); - - if (pipe_info->state != 0) { - if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) { - dev_err(&dev->udev->dev, "error submitting urb\n"); - } - } else { - dprintk(2, "%s :complete state 0\n", __func__); - } - return; -} - -static int s2255_start_readpipe(struct s2255_dev *dev) -{ - int pipe; - int retval; - struct s2255_pipeinfo *pipe_info = &dev->pipe; - pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); - dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint); - pipe_info->state = 1; - pipe_info->err_count = 0; - pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pipe_info->stream_urb) { - dev_err(&dev->udev->dev, - "ReadStream: Unable to alloc URB\n"); - return -ENOMEM; - } - /* transfer buffer allocated in board_init */ - usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->cur_transfer_size, - read_pipe_completion, pipe_info); - retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); - if (retval) { - printk(KERN_ERR "s2255: start read pipe failed\n"); - return retval; - } - return 0; -} - -/* starts acquisition process */ -static int s2255_start_acquire(struct s2255_channel *channel) -{ - unsigned char *buffer; - int res; - unsigned long chn_rev; - int j; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - - channel->last_frame = -1; - channel->bad_payload = 0; - channel->cur_frame = 0; - for (j = 0; j < SYS_FRAMES; j++) { - channel->buffer.frame[j].ulState = 0; - channel->buffer.frame[j].cur_size = 0; - } - - /* send the start command */ - *(__le32 *) buffer = IN_DATA_TOKEN; - *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); - *((__le32 *) buffer + 2) = CMD_START; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (res != 0) - dev_err(&dev->udev->dev, "CMD_START error\n"); - - dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res); - kfree(buffer); - return 0; -} - -static int s2255_stop_acquire(struct s2255_channel *channel) -{ - unsigned char *buffer; - int res; - unsigned long chn_rev; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - /* send the stop command */ - *(__le32 *) buffer = IN_DATA_TOKEN; - *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); - *((__le32 *) buffer + 2) = CMD_STOP; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (res != 0) - dev_err(&dev->udev->dev, "CMD_STOP error\n"); - kfree(buffer); - channel->b_acquire = 0; - dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res); - return res; -} - -static void s2255_stop_readpipe(struct s2255_dev *dev) -{ - struct s2255_pipeinfo *pipe = &dev->pipe; - - pipe->state = 0; - if (pipe->stream_urb) { - /* cancel urb */ - usb_kill_urb(pipe->stream_urb); - usb_free_urb(pipe->stream_urb); - pipe->stream_urb = NULL; - } - dprintk(4, "%s", __func__); - return; -} - -static void s2255_fwload_start(struct s2255_dev *dev, int reset) -{ - if (reset) - s2255_reset_dsppower(dev); - dev->fw_data->fw_size = dev->fw_data->fw->size; - atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); - memcpy(dev->fw_data->pfw_data, - dev->fw_data->fw->data, CHUNK_SIZE); - dev->fw_data->fw_loaded = CHUNK_SIZE; - usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev, - usb_sndbulkpipe(dev->udev, 2), - dev->fw_data->pfw_data, - CHUNK_SIZE, s2255_fwchunk_complete, - dev->fw_data); - mod_timer(&dev->timer, jiffies + HZ); -} - -/* standard usb probe function */ -static int s2255_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct s2255_dev *dev = NULL; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i; - int retval = -ENOMEM; - __le32 *pdata; - int fw_size; - dprintk(2, "%s\n", __func__); - /* allocate memory for our device state and initialize it to zero */ - dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); - if (dev == NULL) { - s2255_dev_err(&interface->dev, "out of memory\n"); - return -ENOMEM; - } - atomic_set(&dev->num_channels, 0); - dev->pid = id->idProduct; - dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); - if (!dev->fw_data) - goto errorFWDATA1; - mutex_init(&dev->lock); - /* grab usb_device and save it */ - dev->udev = usb_get_dev(interface_to_usbdev(interface)); - if (dev->udev == NULL) { - dev_err(&interface->dev, "null usb device\n"); - retval = -ENODEV; - goto errorUDEV; - } - dprintk(1, "dev: %p, udev %p interface %p\n", dev, - dev->udev, interface); - dev->interface = interface; - /* set up the endpoint information */ - iface_desc = interface->cur_altsetting; - dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - dev->read_endpoint = endpoint->bEndpointAddress; - } - } - - if (!dev->read_endpoint) { - dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); - goto errorEP; - } - init_timer(&dev->timer); - dev->timer.function = s2255_timer; - dev->timer.data = (unsigned long)dev->fw_data; - init_waitqueue_head(&dev->fw_data->wait_fw); - for (i = 0; i < MAX_CHANNELS; i++) { - struct s2255_channel *channel = &dev->channel[i]; - dev->channel[i].idx = i; - init_waitqueue_head(&channel->wait_setmode); - init_waitqueue_head(&channel->wait_vidstatus); - } - - dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->fw_data->fw_urb) { - dev_err(&interface->dev, "out of memory!\n"); - goto errorFWURB; - } - - dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); - if (!dev->fw_data->pfw_data) { - dev_err(&interface->dev, "out of memory!\n"); - goto errorFWDATA2; - } - /* load the first chunk */ - if (request_firmware(&dev->fw_data->fw, - FIRMWARE_FILE_NAME, &dev->udev->dev)) { - printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); - goto errorREQFW; - } - /* check the firmware is valid */ - fw_size = dev->fw_data->fw->size; - pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; - - if (*pdata != S2255_FW_MARKER) { - printk(KERN_INFO "Firmware invalid.\n"); - retval = -ENODEV; - goto errorFWMARKER; - } else { - /* make sure firmware is the latest */ - __le32 *pRel; - pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; - printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); - dev->dsp_fw_ver = le32_to_cpu(*pRel); - if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) - printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); - if (dev->pid == 0x2257 && - dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - printk(KERN_WARNING "s2255: 2257 requires firmware %d" - " or above.\n", S2255_MIN_DSP_COLORFILTER); - } - usb_reset_device(dev->udev); - /* load 2255 board specific */ - retval = s2255_board_init(dev); - if (retval) - goto errorBOARDINIT; - spin_lock_init(&dev->slock); - s2255_fwload_start(dev, 0); - /* loads v4l specific */ - retval = s2255_probe_v4l(dev); - if (retval) - goto errorBOARDINIT; - dev_info(&interface->dev, "Sensoray 2255 detected\n"); - return 0; -errorBOARDINIT: - s2255_board_shutdown(dev); -errorFWMARKER: - release_firmware(dev->fw_data->fw); -errorREQFW: - kfree(dev->fw_data->pfw_data); -errorFWDATA2: - usb_free_urb(dev->fw_data->fw_urb); -errorFWURB: - del_timer(&dev->timer); -errorEP: - usb_put_dev(dev->udev); -errorUDEV: - kfree(dev->fw_data); - mutex_destroy(&dev->lock); -errorFWDATA1: - kfree(dev); - printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval); - return retval; -} - -/* disconnect routine. when board is removed physically or with rmmod */ -static void s2255_disconnect(struct usb_interface *interface) -{ - struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); - int i; - int channels = atomic_read(&dev->num_channels); - mutex_lock(&dev->lock); - v4l2_device_disconnect(&dev->v4l2_dev); - mutex_unlock(&dev->lock); - /*see comments in the uvc_driver.c usb disconnect function */ - atomic_inc(&dev->num_channels); - /* unregister each video device. */ - for (i = 0; i < channels; i++) - video_unregister_device(&dev->channel[i].vdev); - /* wake up any of our timers */ - atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); - wake_up(&dev->fw_data->wait_fw); - for (i = 0; i < MAX_CHANNELS; i++) { - dev->channel[i].setmode_ready = 1; - wake_up(&dev->channel[i].wait_setmode); - dev->channel[i].vidstatus_ready = 1; - wake_up(&dev->channel[i].wait_vidstatus); - } - if (atomic_dec_and_test(&dev->num_channels)) - s2255_destroy(dev); - dev_info(&interface->dev, "%s\n", __func__); -} - -static struct usb_driver s2255_driver = { - .name = S2255_DRIVER_NAME, - .probe = s2255_probe, - .disconnect = s2255_disconnect, - .id_table = s2255_table, -}; - -module_usb_driver(s2255_driver); - -MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); -MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(S2255_VERSION); -MODULE_FIRMWARE(FIRMWARE_FILE_NAME); diff --git a/drivers/media/video/stk-sensor.c b/drivers/media/video/stk-sensor.c deleted file mode 100644 index e546b014d7ad..000000000000 --- a/drivers/media/video/stk-sensor.c +++ /dev/null @@ -1,595 +0,0 @@ -/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams) - * - * Copyright 2007-2008 Jaime Velasco Juan - * - * Some parts derived from ov7670.c: - * Copyright 2006 One Laptop Per Child Association, Inc. Written - * by Jonathan Corbet with substantial inspiration from Mark - * McClelland's ovcamchip code. - * - * Copyright 2006-7 Jonathan Corbet - * - * This file may be distributed under the terms of the GNU General - * This program is free software; 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. - */ - -/* Controlling the sensor via the STK1125 vendor specific control interface: - * The camera uses an OmniVision sensor and the stk1125 provides an - * SCCB(i2c)-USB bridge which let us program the sensor. - * In my case the sensor id is 0x9652, it can be read from sensor's register - * 0x0A and 0x0B as follows: - * - read register #R: - * output #R to index 0x0208 - * output 0x0070 to index 0x0200 - * input 1 byte from index 0x0201 (some kind of status register) - * until its value is 0x01 - * input 1 byte from index 0x0209. This is the value of #R - * - write value V to register #R - * output #R to index 0x0204 - * output V to index 0x0205 - * output 0x0005 to index 0x0200 - * input 1 byte from index 0x0201 until its value becomes 0x04 - */ - -/* It seems the i2c bus is controlled with these registers */ - -#include "stk-webcam.h" - -#define STK_IIC_BASE (0x0200) -# define STK_IIC_OP (STK_IIC_BASE) -# define STK_IIC_OP_TX (0x05) -# define STK_IIC_OP_RX (0x70) -# define STK_IIC_STAT (STK_IIC_BASE+1) -# define STK_IIC_STAT_TX_OK (0x04) -# define STK_IIC_STAT_RX_OK (0x01) -/* I don't know what does this register. - * when it is 0x00 or 0x01, we cannot talk to the sensor, - * other values work */ -# define STK_IIC_ENABLE (STK_IIC_BASE+2) -# define STK_IIC_ENABLE_NO (0x00) -/* This is what the driver writes in windows */ -# define STK_IIC_ENABLE_YES (0x1e) -/* - * Address of the slave. Seems like the binary driver look for the - * sensor in multiple places, attempting a reset sequence. - * We only know about the ov9650 - */ -# define STK_IIC_ADDR (STK_IIC_BASE+3) -# define STK_IIC_TX_INDEX (STK_IIC_BASE+4) -# define STK_IIC_TX_VALUE (STK_IIC_BASE+5) -# define STK_IIC_RX_INDEX (STK_IIC_BASE+8) -# define STK_IIC_RX_VALUE (STK_IIC_BASE+9) - -#define MAX_RETRIES (50) - -#define SENSOR_ADDRESS (0x60) - -/* From ov7670.c (These registers aren't fully accurate) */ - -/* Registers */ -#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ -#define REG_COM1 0x04 /* Control 1 */ -#define COM1_CCIR656 0x40 /* CCIR656 enable */ -#define COM1_QFMT 0x20 /* QVGA/QCIF format */ -#define COM1_SKIP_0 0x00 /* Do not skip any row */ -#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */ -#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */ -#define REG_BAVE 0x05 /* U/B Average level */ -#define REG_GbAVE 0x06 /* Y/Gb Average level */ -#define REG_AECHH 0x07 /* AEC MS 5 bits */ -#define REG_RAVE 0x08 /* V/R Average level */ -#define REG_COM2 0x09 /* Control 2 */ -#define COM2_SSLEEP 0x10 /* Soft sleep mode */ -#define REG_PID 0x0a /* Product ID MSB */ -#define REG_VER 0x0b /* Product ID LSB */ -#define REG_COM3 0x0c /* Control 3 */ -#define COM3_SWAP 0x40 /* Byte swap */ -#define COM3_SCALEEN 0x08 /* Enable scaling */ -#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ -#define REG_COM4 0x0d /* Control 4 */ -#define REG_COM5 0x0e /* All "reserved" */ -#define REG_COM6 0x0f /* Control 6 */ -#define REG_AECH 0x10 /* More bits of AEC value */ -#define REG_CLKRC 0x11 /* Clock control */ -#define CLK_PLL 0x80 /* Enable internal PLL */ -#define CLK_EXT 0x40 /* Use external clock directly */ -#define CLK_SCALE 0x3f /* Mask for internal clock scale */ -#define REG_COM7 0x12 /* Control 7 */ -#define COM7_RESET 0x80 /* Register reset */ -#define COM7_FMT_MASK 0x38 -#define COM7_FMT_SXGA 0x00 -#define COM7_FMT_VGA 0x40 -#define COM7_FMT_CIF 0x20 /* CIF format */ -#define COM7_FMT_QVGA 0x10 /* QVGA format */ -#define COM7_FMT_QCIF 0x08 /* QCIF format */ -#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ -#define COM7_YUV 0x00 /* YUV */ -#define COM7_BAYER 0x01 /* Bayer format */ -#define COM7_PBAYER 0x05 /* "Processed bayer" */ -#define REG_COM8 0x13 /* Control 8 */ -#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ -#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ -#define COM8_BFILT 0x20 /* Band filter enable */ -#define COM8_AGC 0x04 /* Auto gain enable */ -#define COM8_AWB 0x02 /* White balance enable */ -#define COM8_AEC 0x01 /* Auto exposure enable */ -#define REG_COM9 0x14 /* Control 9 - gain ceiling */ -#define REG_COM10 0x15 /* Control 10 */ -#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ -#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ -#define COM10_HREF_REV 0x08 /* Reverse HREF */ -#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ -#define COM10_VS_NEG 0x02 /* VSYNC negative */ -#define COM10_HS_NEG 0x01 /* HSYNC negative */ -#define REG_HSTART 0x17 /* Horiz start high bits */ -#define REG_HSTOP 0x18 /* Horiz stop high bits */ -#define REG_VSTART 0x19 /* Vert start high bits */ -#define REG_VSTOP 0x1a /* Vert stop high bits */ -#define REG_PSHFT 0x1b /* Pixel delay after HREF */ -#define REG_MIDH 0x1c /* Manuf. ID high */ -#define REG_MIDL 0x1d /* Manuf. ID low */ -#define REG_MVFP 0x1e /* Mirror / vflip */ -#define MVFP_MIRROR 0x20 /* Mirror image */ -#define MVFP_FLIP 0x10 /* Vertical flip */ - -#define REG_AEW 0x24 /* AGC upper limit */ -#define REG_AEB 0x25 /* AGC lower limit */ -#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ -#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */ -#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */ -#define REG_HSYST 0x30 /* HSYNC rising edge delay */ -#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ -#define REG_HREF 0x32 /* HREF pieces */ -#define REG_TSLB 0x3a /* lots of stuff */ -#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ -#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */ -#define REG_COM11 0x3b /* Control 11 */ -#define COM11_NIGHT 0x80 /* NIght mode enable */ -#define COM11_NMFR 0x60 /* Two bit NM frame rate */ -#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ -#define COM11_50HZ 0x08 /* Manual 50Hz select */ -#define COM11_EXP 0x02 -#define REG_COM12 0x3c /* Control 12 */ -#define COM12_HREF 0x80 /* HREF always */ -#define REG_COM13 0x3d /* Control 13 */ -#define COM13_GAMMA 0x80 /* Gamma enable */ -#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ -#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */ -#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ -#define REG_COM14 0x3e /* Control 14 */ -#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ -#define REG_EDGE 0x3f /* Edge enhancement factor */ -#define REG_COM15 0x40 /* Control 15 */ -#define COM15_R10F0 0x00 /* Data range 10 to F0 */ -#define COM15_R01FE 0x80 /* 01 to FE */ -#define COM15_R00FF 0xc0 /* 00 to FF */ -#define COM15_RGB565 0x10 /* RGB565 output */ -#define COM15_RGBFIXME 0x20 /* FIXME */ -#define COM15_RGB555 0x30 /* RGB555 output */ -#define REG_COM16 0x41 /* Control 16 */ -#define COM16_AWBGAIN 0x08 /* AWB gain enable */ -#define REG_COM17 0x42 /* Control 17 */ -#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ -#define COM17_CBAR 0x08 /* DSP Color bar */ - -/* - * This matrix defines how the colors are generated, must be - * tweaked to adjust hue and saturation. - * - * Order: v-red, v-green, v-blue, u-red, u-green, u-blue - * - * They are nine-bit signed quantities, with the sign bit - * stored in 0x58. Sign for v-red is bit 0, and up from there. - */ -#define REG_CMATRIX_BASE 0x4f -#define CMATRIX_LEN 6 -#define REG_CMATRIX_SIGN 0x58 - - -#define REG_BRIGHT 0x55 /* Brightness */ -#define REG_CONTRAS 0x56 /* Contrast control */ - -#define REG_GFIX 0x69 /* Fix gain control */ - -#define REG_RGB444 0x8c /* RGB 444 control */ -#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ -#define R444_RGBX 0x01 /* Empty nibble at end */ - -#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ -#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ - -#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ -#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ -#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ -#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ -#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ -#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ -#define REG_BD60MAX 0xab /* 60hz banding step limit */ - - - - -/* Returns 0 if OK */ -static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) -{ - int i = 0; - int tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_TX_OK) { - if (tmpval) - STK_ERROR("stk_sensor_outb failed, status=0x%02x\n", - tmpval); - return 1; - } else - return 0; -} - -static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) -{ - int i = 0; - int tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_RX_OK) { - if (tmpval) - STK_ERROR("stk_sensor_inb failed, status=0x%02x\n", - tmpval); - return 1; - } - - if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval)) - return 1; - - *val = (u8) tmpval; - return 0; -} - -static int stk_sensor_write_regvals(struct stk_camera *dev, - struct regval *rv) -{ - int ret; - if (rv == NULL) - return 0; - while (rv->reg != 0xff || rv->val != 0xff) { - ret = stk_sensor_outb(dev, rv->reg, rv->val); - if (ret != 0) - return ret; - rv++; - } - return 0; -} - -int stk_sensor_sleep(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP); -} - -int stk_sensor_wakeup(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP); -} - -static struct regval ov_initvals[] = { - {REG_CLKRC, CLK_PLL}, - {REG_COM11, 0x01}, - {0x6a, 0x7d}, - {REG_AECH, 0x40}, - {REG_GAIN, 0x00}, - {REG_BLUE, 0x80}, - {REG_RED, 0x80}, - /* Do not enable fast AEC for now */ - /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/ - {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC}, - {0x39, 0x50}, {0x38, 0x93}, - {0x37, 0x00}, {0x35, 0x81}, - {REG_COM5, 0x20}, - {REG_COM1, 0x00}, - {REG_COM3, 0x00}, - {REG_COM4, 0x00}, - {REG_PSHFT, 0x00}, - {0x16, 0x07}, - {0x33, 0xe2}, {0x34, 0xbf}, - {REG_COM16, 0x00}, - {0x96, 0x04}, - /* Gamma curve values */ -/* { 0x7a, 0x20 }, { 0x7b, 0x10 }, - { 0x7c, 0x1e }, { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, -*/ - {REG_GFIX, 0x40}, - {0x8e, 0x00}, - {REG_COM12, 0x73}, - {0x8f, 0xdf}, {0x8b, 0x06}, - {0x8c, 0x20}, - {0x94, 0x88}, {0x95, 0x88}, -/* {REG_COM15, 0xc1}, TODO */ - {0x29, 0x3f}, - {REG_COM6, 0x42}, - {REG_BD50MAX, 0x80}, - {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92}, - {REG_BD60MAX, 0x0a}, - {0x90, 0x00}, {0x91, 0x00}, - {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00}, - {REG_AEW, 0x68}, {REG_AEB, 0x5c}, - {REG_VPT, 0xc3}, - {REG_COM9, 0x2e}, - {0x2a, 0x00}, {0x2b, 0x00}, - - {0xff, 0xff}, /* END MARKER */ -}; - -/* Probe the I2C bus and initialise the sensor chip */ -int stk_sensor_init(struct stk_camera *dev) -{ - u8 idl = 0; - u8 idh = 0; - - if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) - || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) - || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { - STK_ERROR("Sensor resetting failed\n"); - return -ENODEV; - } - msleep(10); - /* Read the manufacturer ID: ov = 0x7FA2 */ - if (stk_sensor_inb(dev, REG_MIDH, &idh) - || stk_sensor_inb(dev, REG_MIDL, &idl)) { - STK_ERROR("Strange error reading sensor ID\n"); - return -ENODEV; - } - if (idh != 0x7f || idl != 0xa2) { - STK_ERROR("Huh? you don't have a sensor from ovt\n"); - return -ENODEV; - } - if (stk_sensor_inb(dev, REG_PID, &idh) - || stk_sensor_inb(dev, REG_VER, &idl)) { - STK_ERROR("Could not read sensor model\n"); - return -ENODEV; - } - stk_sensor_write_regvals(dev, ov_initvals); - msleep(10); - STK_INFO("OmniVision sensor detected, id %02X%02X" - " at address %x\n", idh, idl, SENSOR_ADDRESS); - return 0; -} - -/* V4L2_PIX_FMT_UYVY */ -static struct regval ov_fmt_uyvy[] = { - {REG_TSLB, TSLB_YLAST|0x08 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* V4L2_PIX_FMT_YUYV */ -static struct regval ov_fmt_yuyv[] = { - {REG_TSLB, 0 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; - -/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */ -static struct regval ov_fmt_rgbr[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, 0x00}, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */ -static struct regval ov_fmt_rgbp[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, TSLB_BYTEORD }, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_SRGGB8 */ -static struct regval ov_fmt_bayer[] = { - /* This changes color order */ - {REG_TSLB, 0x40}, /* BGGR */ - /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */ - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* - * Store a set of start/stop values into the camera. - */ -static int stk_sensor_set_hw(struct stk_camera *dev, - int hstart, int hstop, int vstart, int vstop) -{ - int ret; - unsigned char v; -/* - * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of - * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is - * a mystery "edge offset" value in the top two bits of href. - */ - ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_HREF, &v); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_HREF, v); -/* - * Vertical: similar arrangement (note: this is different from ov7670.c) - */ - ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_VREF, &v); - v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_VREF, v); - return ret; -} - - -int stk_sensor_configure(struct stk_camera *dev) -{ - int com7; - /* - * We setup the sensor to output dummy lines in low-res modes, - * so we don't get absurdly hight framerates. - */ - unsigned dummylines; - int flip; - struct regval *rv; - - switch (dev->vsettings.mode) { - case MODE_QCIF: com7 = COM7_FMT_QCIF; - dummylines = 604; - break; - case MODE_QVGA: com7 = COM7_FMT_QVGA; - dummylines = 267; - break; - case MODE_CIF: com7 = COM7_FMT_CIF; - dummylines = 412; - break; - case MODE_VGA: com7 = COM7_FMT_VGA; - dummylines = 11; - break; - case MODE_SXGA: com7 = COM7_FMT_SXGA; - dummylines = 0; - break; - default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode); - return -EFAULT; - } - switch (dev->vsettings.palette) { - case V4L2_PIX_FMT_UYVY: - com7 |= COM7_YUV; - rv = ov_fmt_uyvy; - break; - case V4L2_PIX_FMT_YUYV: - com7 |= COM7_YUV; - rv = ov_fmt_yuyv; - break; - case V4L2_PIX_FMT_RGB565: - com7 |= COM7_RGB; - rv = ov_fmt_rgbp; - break; - case V4L2_PIX_FMT_RGB565X: - com7 |= COM7_RGB; - rv = ov_fmt_rgbr; - break; - case V4L2_PIX_FMT_SBGGR8: - com7 |= COM7_PBAYER; - rv = ov_fmt_bayer; - break; - default: STK_ERROR("Unsupported colorspace\n"); - return -EFAULT; - } - /*FIXME sometimes the sensor go to a bad state - stk_sensor_write_regvals(dev, ov_initvals); */ - stk_sensor_outb(dev, REG_COM7, com7); - msleep(50); - stk_sensor_write_regvals(dev, rv); - flip = (dev->vsettings.vflip?MVFP_FLIP:0) - | (dev->vsettings.hflip?MVFP_MIRROR:0); - stk_sensor_outb(dev, REG_MVFP, flip); - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8 - && !dev->vsettings.vflip) - stk_sensor_outb(dev, REG_TSLB, 0x08); - stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8); - stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff); - msleep(50); - switch (dev->vsettings.mode) { - case MODE_VGA: - if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) - STK_ERROR("stk_sensor_set_hw failed (VGA)\n"); - break; - case MODE_SXGA: - case MODE_CIF: - case MODE_QVGA: - case MODE_QCIF: - /*FIXME These settings seem ignored by the sensor - if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) - STK_ERROR("stk_sensor_set_hw failed (SXGA)\n"); - */ - break; - } - msleep(10); - return 0; -} - -int stk_sensor_set_brightness(struct stk_camera *dev, int br) -{ - if (br < 0 || br > 0xff) - return -EINVAL; - stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6)); - stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6)); - return 0; -} - diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c deleted file mode 100644 index 86a0fc56c330..000000000000 --- a/drivers/media/video/stk-webcam.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * stk-webcam.c : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan - * - * Some parts are inspired from cafe_ccic.c - * Copyright 2006-2007 Jonathan Corbet - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "stk-webcam.h" - - -static bool hflip; -module_param(hflip, bool, 0444); -MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); - -static bool vflip; -module_param(vflip, bool, 0444); -MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); - -static int debug; -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jaime Velasco Juan and Nicolas VIVIEN"); -MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); - - -/* bool for webcam LED management */ -int first_init = 1; - -/* Some cameras have audio interfaces, we aren't interested in those */ -static struct usb_device_id stkwebcam_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, - { } -}; -MODULE_DEVICE_TABLE(usb, stkwebcam_table); - -/* - * Basic stuff - */ -int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - NULL, - 0, - 500); - if (ret < 0) - return ret; - else - return 0; -} - -int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, - index, - (u8 *) value, - sizeof(u8), - 500); - if (ret < 0) - return ret; - else - return 0; -} - -static int stk_start_stream(struct stk_camera *dev) -{ - int value; - int i, ret; - int value_116, value_117; - - if (!is_present(dev)) - return -ENODEV; - if (!is_memallocd(dev) || !is_initialised(dev)) { - STK_ERROR("FIXME: Buffers are not allocated\n"); - return -EFAULT; - } - ret = usb_set_interface(dev->udev, 0, 5); - - if (ret < 0) - STK_ERROR("usb_set_interface failed !\n"); - if (stk_sensor_wakeup(dev)) - STK_ERROR("error awaking the sensor\n"); - - stk_camera_read_reg(dev, 0x0116, &value_116); - stk_camera_read_reg(dev, 0x0117, &value_117); - - stk_camera_write_reg(dev, 0x0116, 0x0000); - stk_camera_write_reg(dev, 0x0117, 0x0000); - - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value | 0x80); - - stk_camera_write_reg(dev, 0x0116, value_116); - stk_camera_write_reg(dev, 0x0117, value_117); - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) { - ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); - atomic_inc(&dev->urbs_used); - if (ret) - return ret; - } - } - set_streaming(dev); - return 0; -} - -static int stk_stop_stream(struct stk_camera *dev) -{ - int value; - int i; - if (is_present(dev)) { - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value & ~0x80); - if (dev->isobufs != NULL) { - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) - usb_kill_urb(dev->isobufs[i].urb); - } - } - unset_streaming(dev); - - if (usb_set_interface(dev->udev, 0, 0)) - STK_ERROR("usb_set_interface failed !\n"); - if (stk_sensor_sleep(dev)) - STK_ERROR("error suspending the sensor\n"); - } - return 0; -} - -/* - * This seems to be the shortest init sequence we - * must do in order to find the sensor - * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor - * is also reset. Maybe powers down it? - * Rest of values don't make a difference - */ - -static struct regval stk1125_initvals[] = { - /*TODO: What means this sequence? */ - {0x0000, 0x24}, - {0x0100, 0x21}, - {0x0002, 0x68}, - {0x0003, 0x80}, - {0x0005, 0x00}, - {0x0007, 0x03}, - {0x000d, 0x00}, - {0x000f, 0x02}, - {0x0300, 0x12}, - {0x0350, 0x41}, - {0x0351, 0x00}, - {0x0352, 0x00}, - {0x0353, 0x00}, - {0x0018, 0x10}, - {0x0019, 0x00}, - {0x001b, 0x0e}, - {0x001c, 0x46}, - {0x0300, 0x80}, - {0x001a, 0x04}, - {0x0110, 0x00}, - {0x0111, 0x00}, - {0x0112, 0x00}, - {0x0113, 0x00}, - - {0xffff, 0xff}, -}; - - -static int stk_initialise(struct stk_camera *dev) -{ - struct regval *rv; - int ret; - if (!is_present(dev)) - return -ENODEV; - if (is_initialised(dev)) - return 0; - rv = stk1125_initvals; - while (rv->reg != 0xffff) { - ret = stk_camera_write_reg(dev, rv->reg, rv->val); - if (ret) - return ret; - rv++; - } - if (stk_sensor_init(dev) == 0) { - set_initialised(dev); - return 0; - } else - return -1; -} - -/* *********************************************** */ -/* - * This function is called as an URB transfert is complete (Isochronous pipe). - * So, the traitement is done in interrupt time, so it has be fast, not crash, - * and not stall. Neat. - */ -static void stk_isoc_handler(struct urb *urb) -{ - int i; - int ret; - int framelen; - unsigned long flags; - - unsigned char *fill = NULL; - unsigned char *iso_buf = NULL; - - struct stk_camera *dev; - struct stk_sio_buffer *fb; - - dev = (struct stk_camera *) urb->context; - - if (dev == NULL) { - STK_ERROR("isoc_handler called with NULL device !\n"); - return; - } - - if (urb->status == -ENOENT || urb->status == -ECONNRESET - || urb->status == -ESHUTDOWN) { - atomic_dec(&dev->urbs_used); - return; - } - - spin_lock_irqsave(&dev->spinlock, flags); - - if (urb->status != -EINPROGRESS && urb->status != 0) { - STK_ERROR("isoc_handler: urb->status == %d\n", urb->status); - goto resubmit; - } - - if (list_empty(&dev->sio_avail)) { - /*FIXME Stop streaming after a while */ - (void) (printk_ratelimit() && - STK_ERROR("isoc_handler without available buffer!\n")); - goto resubmit; - } - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fill = fb->buffer + fb->v4lbuf.bytesused; - - for (i = 0; i < urb->number_of_packets; i++) { - if (urb->iso_frame_desc[i].status != 0) { - if (urb->iso_frame_desc[i].status != -EXDEV) - STK_ERROR("Frame %d has error %d\n", i, - urb->iso_frame_desc[i].status); - continue; - } - framelen = urb->iso_frame_desc[i].actual_length; - iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (framelen <= 4) - continue; /* no data */ - - /* - * we found something informational from there - * the isoc frames have to type of headers - * type1: 00 xx 00 00 or 20 xx 00 00 - * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 - * xx is a sequencer which has never been seen over 0x3f - * imho data written down looks like bayer, i see similarities - * after every 640 bytes - */ - if (*iso_buf & 0x80) { - framelen -= 8; - iso_buf += 8; - /* This marks a new frame */ - if (fb->v4lbuf.bytesused != 0 - && fb->v4lbuf.bytesused != dev->frame_size) { - (void) (printk_ratelimit() && - STK_ERROR("frame %d, " - "bytesused=%d, skipping\n", - i, fb->v4lbuf.bytesused)); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else if (fb->v4lbuf.bytesused == dev->frame_size) { - if (list_is_singular(&dev->sio_avail)) { - /* Always reuse the last buffer */ - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else { - list_move_tail(dev->sio_avail.next, - &dev->sio_full); - wake_up(&dev->wait_frame); - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } - } - } else { - framelen -= 4; - iso_buf += 4; - } - - /* Our buffer is full !!! */ - if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { - (void) (printk_ratelimit() && - STK_ERROR("Frame buffer overflow, lost sync\n")); - /*FIXME Do something here? */ - continue; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - memcpy(fill, iso_buf, framelen); - spin_lock_irqsave(&dev->spinlock, flags); - fill += framelen; - - /* New size of our buffer */ - fb->v4lbuf.bytesused += framelen; - } - -resubmit: - spin_unlock_irqrestore(&dev->spinlock, flags); - urb->dev = dev->udev; - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret != 0) { - STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n", - ret); - } -} - -/* -------------------------------------------- */ - -static int stk_prepare_iso(struct stk_camera *dev) -{ - void *kbuf; - int i, j; - struct urb *urb; - struct usb_device *udev; - - if (dev == NULL) - return -ENXIO; - udev = dev->udev; - - if (dev->isobufs) - STK_ERROR("isobufs already allocated. Bad\n"); - else - dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), - GFP_KERNEL); - if (dev->isobufs == NULL) { - STK_ERROR("Unable to allocate iso buffers\n"); - return -ENOMEM; - } - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].data == NULL) { - kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); - if (kbuf == NULL) { - STK_ERROR("Failed to allocate iso buffer %d\n", - i); - goto isobufs_out; - } - dev->isobufs[i].data = kbuf; - } else - STK_ERROR("isobuf data already allocated\n"); - if (dev->isobufs[i].urb == NULL) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); - if (urb == NULL) { - STK_ERROR("Failed to allocate URB %d\n", i); - goto isobufs_out; - } - dev->isobufs[i].urb = urb; - } else { - STK_ERROR("Killing URB\n"); - usb_kill_urb(dev->isobufs[i].urb); - urb = dev->isobufs[i].urb; - } - urb->interval = 1; - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->isobufs[i].data; - urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = stk_isoc_handler; - urb->context = dev; - urb->start_frame = 0; - urb->number_of_packets = ISO_FRAMES_PER_DESC; - - for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { - urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; - urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; - } - } - set_memallocd(dev); - return 0; - -isobufs_out: - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) - kfree(dev->isobufs[i].data); - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) - usb_free_urb(dev->isobufs[i].urb); - kfree(dev->isobufs); - dev->isobufs = NULL; - return -ENOMEM; -} - -static void stk_clean_iso(struct stk_camera *dev) -{ - int i; - - if (dev == NULL || dev->isobufs == NULL) - return; - - for (i = 0; i < MAX_ISO_BUFS; i++) { - struct urb *urb; - - urb = dev->isobufs[i].urb; - if (urb) { - if (atomic_read(&dev->urbs_used) && is_present(dev)) - usb_kill_urb(urb); - usb_free_urb(urb); - } - kfree(dev->isobufs[i].data); - } - kfree(dev->isobufs); - dev->isobufs = NULL; - unset_memallocd(dev); -} - -static int stk_setup_siobuf(struct stk_camera *dev, int index) -{ - struct stk_sio_buffer *buf = dev->sio_bufs + index; - INIT_LIST_HEAD(&buf->list); - buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); - buf->buffer = vmalloc_user(buf->v4lbuf.length); - if (buf->buffer == NULL) - return -ENOMEM; - buf->mapcount = 0; - buf->dev = dev; - buf->v4lbuf.index = index; - buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->v4lbuf.field = V4L2_FIELD_NONE; - buf->v4lbuf.memory = V4L2_MEMORY_MMAP; - buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; - return 0; -} - -static int stk_free_sio_buffers(struct stk_camera *dev) -{ - int i; - int nbufs; - unsigned long flags; - if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) - return 0; - /* - * If any buffers are mapped, we cannot free them at all. - */ - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].mapcount > 0) - return -EBUSY; - } - /* - * OK, let's do it. - */ - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - nbufs = dev->n_sbufs; - dev->n_sbufs = 0; - spin_unlock_irqrestore(&dev->spinlock, flags); - for (i = 0; i < nbufs; i++) { - if (dev->sio_bufs[i].buffer != NULL) - vfree(dev->sio_bufs[i].buffer); - } - kfree(dev->sio_bufs); - dev->sio_bufs = NULL; - return 0; -} - -static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int i; - if (dev->sio_bufs != NULL) - STK_ERROR("sio_bufs already allocated\n"); - else { - dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer), - GFP_KERNEL); - if (dev->sio_bufs == NULL) - return -ENOMEM; - for (i = 0; i < n_sbufs; i++) { - if (stk_setup_siobuf(dev, i)) - return (dev->n_sbufs > 1 ? 0 : -ENOMEM); - dev->n_sbufs = i+1; - } - } - return 0; -} - -static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int err; - err = stk_prepare_iso(dev); - if (err) { - stk_clean_iso(dev); - return err; - } - err = stk_prepare_sio_buffers(dev, n_sbufs); - if (err) { - stk_free_sio_buffers(dev); - return err; - } - return 0; -} - -static void stk_free_buffers(struct stk_camera *dev) -{ - stk_clean_iso(dev); - stk_free_sio_buffers(dev); -} -/* -------------------------------------------- */ - -/* v4l file operations */ - -static int v4l_stk_open(struct file *fp) -{ - struct stk_camera *dev; - struct video_device *vdev; - - vdev = video_devdata(fp); - dev = vdev_to_camera(vdev); - - if (dev == NULL || !is_present(dev)) - return -ENXIO; - - if (!first_init) - stk_camera_write_reg(dev, 0x0, 0x24); - else - first_init = 0; - - fp->private_data = dev; - usb_autopm_get_interface(dev->interface); - - return 0; -} - -static int v4l_stk_release(struct file *fp) -{ - struct stk_camera *dev = fp->private_data; - - if (dev->owner == fp) { - stk_stop_stream(dev); - stk_free_buffers(dev); - stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ - unset_initialised(dev); - dev->owner = NULL; - } - - if (is_present(dev)) - usb_autopm_put_interface(dev->interface); - - return 0; -} - -static ssize_t v4l_stk_read(struct file *fp, char __user *buf, - size_t count, loff_t *f_pos) -{ - int i; - int ret; - unsigned long flags; - struct stk_sio_buffer *sbuf; - struct stk_camera *dev = fp->private_data; - - if (!is_present(dev)) - return -EIO; - if (dev->owner && dev->owner != fp) - return -EBUSY; - dev->owner = fp; - if (!is_streaming(dev)) { - if (stk_initialise(dev) - || stk_allocate_buffers(dev, 3) - || stk_start_stream(dev)) - return -ENOMEM; - spin_lock_irqsave(&dev->spinlock, flags); - for (i = 0; i < dev->n_sbufs; i++) { - list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); - dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - } - if (*f_pos == 0) { - if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - } - if (count + *f_pos > dev->frame_size) - count = dev->frame_size - *f_pos; - spin_lock_irqsave(&dev->spinlock, flags); - if (list_empty(&dev->sio_full)) { - spin_unlock_irqrestore(&dev->spinlock, flags); - STK_ERROR("BUG: No siobufs ready\n"); - return 0; - } - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - spin_unlock_irqrestore(&dev->spinlock, flags); - - if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) - return -EFAULT; - - *f_pos += count; - - if (*f_pos >= dev->frame_size) { - *f_pos = 0; - spin_lock_irqsave(&dev->spinlock, flags); - list_move_tail(&sbuf->list, &dev->sio_avail); - spin_unlock_irqrestore(&dev->spinlock, flags); - } - return count; -} - -static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait) -{ - struct stk_camera *dev = fp->private_data; - - poll_wait(fp, &dev->wait_frame, wait); - - if (!is_present(dev)) - return POLLERR; - - if (!list_empty(&dev->sio_full)) - return POLLIN | POLLRDNORM; - - return 0; -} - - -static void stk_v4l_vm_open(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount++; -} -static void stk_v4l_vm_close(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount--; - if (sbuf->mapcount == 0) - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; -} -static const struct vm_operations_struct stk_v4l_vm_ops = { - .open = stk_v4l_vm_open, - .close = stk_v4l_vm_close -}; - -static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) -{ - unsigned int i; - int ret; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - struct stk_camera *dev = fp->private_data; - struct stk_sio_buffer *sbuf = NULL; - - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { - sbuf = dev->sio_bufs + i; - break; - } - } - if (sbuf == NULL) - return -EINVAL; - ret = remap_vmalloc_range(vma, sbuf->buffer, 0); - if (ret) - return ret; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = sbuf; - vma->vm_ops = &stk_v4l_vm_ops; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; - stk_v4l_vm_open(vma); - return 0; -} - -/* v4l ioctl handlers */ - -static int stk_vidioc_querycap(struct file *filp, - void *priv, struct v4l2_capability *cap) -{ - strcpy(cap->driver, "stk"); - strcpy(cap->card, "stk"); - cap->version = DRIVER_VERSION_NUM; - - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - return 0; -} - -static int stk_vidioc_enum_input(struct file *filp, - void *priv, struct v4l2_input *input) -{ - if (input->index != 0) - return -EINVAL; - - strcpy(input->name, "Syntek USB Camera"); - input->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - - -static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - if (i != 0) - return -EINVAL; - else - return 0; -} - -/* from vivi.c */ -static int stk_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) -{ - return 0; -} - -/* List of all V4Lv2 controls supported by the driver */ -static struct v4l2_queryctrl stk_controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xffff, - .step = 0x0100, - .default_value = 0x6000, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, -}; - -static int stk_vidioc_queryctrl(struct file *filp, - void *priv, struct v4l2_queryctrl *c) -{ - int i; - int nbr; - nbr = ARRAY_SIZE(stk_controls); - - for (i = 0; i < nbr; i++) { - if (stk_controls[i].id == c->id) { - memcpy(c, &stk_controls[i], - sizeof(struct v4l2_queryctrl)); - return 0; - } - } - return -EINVAL; -} - -static int stk_vidioc_g_ctrl(struct file *filp, - void *priv, struct v4l2_control *c) -{ - struct stk_camera *dev = priv; - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = dev->vsettings.brightness; - break; - case V4L2_CID_HFLIP: - c->value = dev->vsettings.hflip; - break; - case V4L2_CID_VFLIP: - c->value = dev->vsettings.vflip; - break; - default: - return -EINVAL; - } - return 0; -} - -static int stk_vidioc_s_ctrl(struct file *filp, - void *priv, struct v4l2_control *c) -{ - struct stk_camera *dev = priv; - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - dev->vsettings.brightness = c->value; - return stk_sensor_set_brightness(dev, c->value >> 8); - case V4L2_CID_HFLIP: - dev->vsettings.hflip = c->value; - return 0; - case V4L2_CID_VFLIP: - dev->vsettings.vflip = c->value; - return 0; - default: - return -EINVAL; - } - return 0; -} - - -static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_fmtdesc *fmtd) -{ - switch (fmtd->index) { - case 0: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565; - strcpy(fmtd->description, "r5g6b5"); - break; - case 1: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; - strcpy(fmtd->description, "r5g6b5BE"); - break; - case 2: - fmtd->pixelformat = V4L2_PIX_FMT_UYVY; - strcpy(fmtd->description, "yuv4:2:2"); - break; - case 3: - fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; - strcpy(fmtd->description, "Raw bayer"); - break; - case 4: - fmtd->pixelformat = V4L2_PIX_FMT_YUYV; - strcpy(fmtd->description, "yuv4:2:2"); - break; - default: - return -EINVAL; - } - return 0; -} - -static struct stk_size { - unsigned w; - unsigned h; - enum stk_mode m; -} stk_sizes[] = { - { .w = 1280, .h = 1024, .m = MODE_SXGA, }, - { .w = 640, .h = 480, .m = MODE_VGA, }, - { .w = 352, .h = 288, .m = MODE_CIF, }, - { .w = 320, .h = 240, .m = MODE_QVGA, }, - { .w = 176, .h = 144, .m = MODE_QCIF, }, -}; - -static int stk_vidioc_g_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format *pix_format = &f->fmt.pix; - struct stk_camera *dev = priv; - int i; - - for (i = 0; i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode; i++) - ; - if (i == ARRAY_SIZE(stk_sizes)) { - STK_ERROR("ERROR: mode invalid\n"); - return -EINVAL; - } - pix_format->width = stk_sizes[i].w; - pix_format->height = stk_sizes[i].h; - pix_format->field = V4L2_FIELD_NONE; - pix_format->colorspace = V4L2_COLORSPACE_SRGB; - pix_format->pixelformat = dev->vsettings.palette; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - pix_format->bytesperline = pix_format->width; - else - pix_format->bytesperline = 2 * pix_format->width; - pix_format->sizeimage = pix_format->bytesperline - * pix_format->height; - return 0; -} - -static int stk_vidioc_try_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - int i; - switch (fmtd->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - break; - default: - return -EINVAL; - } - for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { - if (fmtd->fmt.pix.width > stk_sizes[i].w) - break; - } - if (i == ARRAY_SIZE(stk_sizes) - || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) - < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { - fmtd->fmt.pix.height = stk_sizes[i-1].h; - fmtd->fmt.pix.width = stk_sizes[i-1].w; - fmtd->fmt.pix.priv = i - 1; - } else { - fmtd->fmt.pix.height = stk_sizes[i].h; - fmtd->fmt.pix.width = stk_sizes[i].w; - fmtd->fmt.pix.priv = i; - } - - fmtd->fmt.pix.field = V4L2_FIELD_NONE; - fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) - fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; - else - fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; - fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline - * fmtd->fmt.pix.height; - return 0; -} - -static int stk_setup_format(struct stk_camera *dev) -{ - int i = 0; - int depth; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - depth = 1; - else - depth = 2; - while (i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode) - i++; - if (i == ARRAY_SIZE(stk_sizes)) { - STK_ERROR("Something is broken in %s\n", __func__); - return -EFAULT; - } - /* This registers controls some timings, not sure of what. */ - stk_camera_write_reg(dev, 0x001b, 0x0e); - if (dev->vsettings.mode == MODE_SXGA) - stk_camera_write_reg(dev, 0x001c, 0x0e); - else - stk_camera_write_reg(dev, 0x001c, 0x46); - /* - * Registers 0x0115 0x0114 are the size of each line (bytes), - * regs 0x0117 0x0116 are the heigth of the image. - */ - stk_camera_write_reg(dev, 0x0115, - ((stk_sizes[i].w * depth) >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0114, - (stk_sizes[i].w * depth) & 0xff); - stk_camera_write_reg(dev, 0x0117, - (stk_sizes[i].h >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0116, - stk_sizes[i].h & 0xff); - return stk_sensor_configure(dev); -} - -static int stk_vidioc_s_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - int ret; - struct stk_camera *dev = priv; - - if (dev == NULL) - return -ENODEV; - if (!is_present(dev)) - return -ENODEV; - if (is_streaming(dev)) - return -EBUSY; - if (dev->owner && dev->owner != filp) - return -EBUSY; - ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd); - if (ret) - return ret; - dev->owner = filp; - - dev->vsettings.palette = fmtd->fmt.pix.pixelformat; - stk_free_buffers(dev); - dev->frame_size = fmtd->fmt.pix.sizeimage; - dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m; - - stk_initialise(dev); - return stk_setup_format(dev); -} - -static int stk_vidioc_reqbufs(struct file *filp, - void *priv, struct v4l2_requestbuffers *rb) -{ - struct stk_camera *dev = priv; - - if (dev == NULL) - return -ENODEV; - if (rb->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (is_streaming(dev) - || (dev->owner && dev->owner != filp)) - return -EBUSY; - dev->owner = filp; - - /*FIXME If they ask for zero, we must stop streaming and free */ - if (rb->count < 3) - rb->count = 3; - /* Arbitrary limit */ - else if (rb->count > 5) - rb->count = 5; - - stk_allocate_buffers(dev, rb->count); - rb->count = dev->n_sbufs; - return 0; -} - -static int stk_vidioc_querybuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = priv; - struct stk_sio_buffer *sbuf; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_qbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = priv; - struct stk_sio_buffer *sbuf; - unsigned long flags; - - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) - return 0; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; - spin_lock_irqsave(&dev->spinlock, flags); - list_add_tail(&sbuf->list, &dev->sio_avail); - *buf = sbuf->v4lbuf; - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - -static int stk_vidioc_dqbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = priv; - struct stk_sio_buffer *sbuf; - unsigned long flags; - int ret; - - if (!is_streaming(dev)) - return -EINVAL; - - if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - - spin_lock_irqsave(&dev->spinlock, flags); - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - list_del_init(&sbuf->list); - spin_unlock_irqrestore(&dev->spinlock, flags); - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; - sbuf->v4lbuf.sequence = ++dev->sequence; - do_gettimeofday(&sbuf->v4lbuf.timestamp); - - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_streamon(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = priv; - if (is_streaming(dev)) - return 0; - if (dev->sio_bufs == NULL) - return -EINVAL; - dev->sequence = 0; - return stk_start_stream(dev); -} - -static int stk_vidioc_streamoff(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = priv; - unsigned long flags; - int i; - stk_stop_stream(dev); - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - for (i = 0; i < dev->n_sbufs; i++) { - INIT_LIST_HEAD(&dev->sio_bufs[i].list); - dev->sio_bufs[i].v4lbuf.flags = 0; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - - -static int stk_vidioc_g_parm(struct file *filp, - void *priv, struct v4l2_streamparm *sp) -{ - /*FIXME This is not correct */ - sp->parm.capture.timeperframe.numerator = 1; - sp->parm.capture.timeperframe.denominator = 30; - sp->parm.capture.readbuffers = 2; - return 0; -} - -static int stk_vidioc_enum_framesizes(struct file *filp, - void *priv, struct v4l2_frmsizeenum *frms) -{ - if (frms->index >= ARRAY_SIZE(stk_sizes)) - return -EINVAL; - switch (frms->pixel_format) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; - frms->discrete.width = stk_sizes[frms->index].w; - frms->discrete.height = stk_sizes[frms->index].h; - return 0; - default: return -EINVAL; - } -} - -static struct v4l2_file_operations v4l_stk_fops = { - .owner = THIS_MODULE, - .open = v4l_stk_open, - .release = v4l_stk_release, - .read = v4l_stk_read, - .poll = v4l_stk_poll, - .mmap = v4l_stk_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { - .vidioc_querycap = stk_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, - .vidioc_enum_input = stk_vidioc_enum_input, - .vidioc_s_input = stk_vidioc_s_input, - .vidioc_g_input = stk_vidioc_g_input, - .vidioc_s_std = stk_vidioc_s_std, - .vidioc_reqbufs = stk_vidioc_reqbufs, - .vidioc_querybuf = stk_vidioc_querybuf, - .vidioc_qbuf = stk_vidioc_qbuf, - .vidioc_dqbuf = stk_vidioc_dqbuf, - .vidioc_streamon = stk_vidioc_streamon, - .vidioc_streamoff = stk_vidioc_streamoff, - .vidioc_queryctrl = stk_vidioc_queryctrl, - .vidioc_g_ctrl = stk_vidioc_g_ctrl, - .vidioc_s_ctrl = stk_vidioc_s_ctrl, - .vidioc_g_parm = stk_vidioc_g_parm, - .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, -}; - -static void stk_v4l_dev_release(struct video_device *vd) -{ - struct stk_camera *dev = vdev_to_camera(vd); - - if (dev->sio_bufs != NULL || dev->isobufs != NULL) - STK_ERROR("We are leaking memory\n"); - usb_put_intf(dev->interface); - kfree(dev); -} - -static struct video_device stk_v4l_data = { - .name = "stkwebcam", - .tvnorms = V4L2_STD_UNKNOWN, - .current_norm = V4L2_STD_UNKNOWN, - .fops = &v4l_stk_fops, - .ioctl_ops = &v4l_stk_ioctl_ops, - .release = stk_v4l_dev_release, -}; - - -static int stk_register_video_device(struct stk_camera *dev) -{ - int err; - - dev->vdev = stk_v4l_data; - dev->vdev.debug = debug; - dev->vdev.parent = &dev->interface->dev; - err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); - if (err) - STK_ERROR("v4l registration failed\n"); - else - STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n", - video_device_node_name(&dev->vdev)); - return err; -} - - -/* USB Stuff */ - -static int stk_camera_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int i; - int err = 0; - - struct stk_camera *dev = NULL; - struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - - dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); - if (dev == NULL) { - STK_ERROR("Out of memory !\n"); - return -ENOMEM; - } - - spin_lock_init(&dev->spinlock); - init_waitqueue_head(&dev->wait_frame); - - dev->udev = udev; - dev->interface = interface; - usb_get_intf(interface); - - dev->vsettings.vflip = vflip; - dev->vsettings.hflip = hflip; - dev->n_sbufs = 0; - set_present(dev); - - /* Set up the endpoint information - * use only the first isoc-in endpoint - * for the current alternate setting */ - iface_desc = interface->cur_altsetting; - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->isoc_ep - && usb_endpoint_is_isoc_in(endpoint)) { - /* we found an isoc in endpoint */ - dev->isoc_ep = usb_endpoint_num(endpoint); - break; - } - } - if (!dev->isoc_ep) { - STK_ERROR("Could not find isoc-in endpoint"); - err = -ENODEV; - goto error; - } - dev->vsettings.brightness = 0x7fff; - dev->vsettings.palette = V4L2_PIX_FMT_RGB565; - dev->vsettings.mode = MODE_VGA; - dev->frame_size = 640 * 480 * 2; - - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - - usb_set_intfdata(interface, dev); - - err = stk_register_video_device(dev); - if (err) - goto error; - - return 0; - -error: - kfree(dev); - return err; -} - -static void stk_camera_disconnect(struct usb_interface *interface) -{ - struct stk_camera *dev = usb_get_intfdata(interface); - - usb_set_intfdata(interface, NULL); - unset_present(dev); - - wake_up_interruptible(&dev->wait_frame); - - STK_INFO("Syntek USB2.0 Camera release resources device %s\n", - video_device_node_name(&dev->vdev)); - - video_unregister_device(&dev->vdev); -} - -#ifdef CONFIG_PM -static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (is_streaming(dev)) { - stk_stop_stream(dev); - /* yes, this is ugly */ - set_streaming(dev); - } - return 0; -} - -static int stk_camera_resume(struct usb_interface *intf) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (!is_initialised(dev)) - return 0; - unset_initialised(dev); - stk_initialise(dev); - stk_camera_write_reg(dev, 0x0, 0x49); - stk_setup_format(dev); - if (is_streaming(dev)) - stk_start_stream(dev); - return 0; -} -#endif - -static struct usb_driver stk_camera_driver = { - .name = "stkwebcam", - .probe = stk_camera_probe, - .disconnect = stk_camera_disconnect, - .id_table = stkwebcam_table, -#ifdef CONFIG_PM - .suspend = stk_camera_suspend, - .resume = stk_camera_resume, -#endif -}; - -module_usb_driver(stk_camera_driver); diff --git a/drivers/media/video/stk-webcam.h b/drivers/media/video/stk-webcam.h deleted file mode 100644 index 9f6736637571..000000000000 --- a/drivers/media/video/stk-webcam.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * stk-webcam.h : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan - * - * This program is free software; 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 - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef STKWEBCAM_H -#define STKWEBCAM_H - -#include -#include - -#define DRIVER_VERSION "v0.0.1" -#define DRIVER_VERSION_NUM 0x000001 - -#define MAX_ISO_BUFS 3 -#define ISO_FRAMES_PER_DESC 16 -#define ISO_MAX_FRAME_SIZE 3 * 1024 -#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) - - -#define PREFIX "stkwebcam: " -#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args) -#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args) -#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args) - -struct stk_iso_buf { - void *data; - int length; - int read; - struct urb *urb; -}; - -/* Streaming IO buffers */ -struct stk_sio_buffer { - struct v4l2_buffer v4lbuf; - char *buffer; - int mapcount; - struct stk_camera *dev; - struct list_head list; -}; - -enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; - -struct stk_video { - enum stk_mode mode; - int brightness; - __u32 palette; - int hflip; - int vflip; -}; - -enum stk_status { - S_PRESENT = 1, - S_INITIALISED = 2, - S_MEMALLOCD = 4, - S_STREAMING = 8, -}; -#define is_present(dev) ((dev)->status & S_PRESENT) -#define is_initialised(dev) ((dev)->status & S_INITIALISED) -#define is_streaming(dev) ((dev)->status & S_STREAMING) -#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD) -#define set_present(dev) ((dev)->status = S_PRESENT) -#define unset_present(dev) ((dev)->status &= \ - ~(S_PRESENT|S_INITIALISED|S_STREAMING)) -#define set_initialised(dev) ((dev)->status |= S_INITIALISED) -#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED) -#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD) -#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD) -#define set_streaming(dev) ((dev)->status |= S_STREAMING) -#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING) - -struct regval { - unsigned reg; - unsigned val; -}; - -struct stk_camera { - struct video_device vdev; - struct usb_device *udev; - struct usb_interface *interface; - int webcam_model; - struct file *owner; - - u8 isoc_ep; - - /* Not sure if this is right */ - atomic_t urbs_used; - - struct stk_video vsettings; - - enum stk_status status; - - spinlock_t spinlock; - wait_queue_head_t wait_frame; - - struct stk_iso_buf *isobufs; - - int frame_size; - /* Streaming buffers */ - unsigned int n_sbufs; - struct stk_sio_buffer *sio_bufs; - struct list_head sio_avail; - struct list_head sio_full; - unsigned sequence; -}; - -#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev) - -int stk_camera_write_reg(struct stk_camera *, u16, u8); -int stk_camera_read_reg(struct stk_camera *, u16, int *); - -int stk_sensor_init(struct stk_camera *); -int stk_sensor_configure(struct stk_camera *); -int stk_sensor_sleep(struct stk_camera *dev); -int stk_sensor_wakeup(struct stk_camera *dev); -int stk_sensor_set_brightness(struct stk_camera *dev, int br); - -#endif diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c deleted file mode 100644 index 9afab35878b4..000000000000 --- a/drivers/media/video/zr364xx.c +++ /dev/null @@ -1,1643 +0,0 @@ -/* - * Zoran 364xx based USB webcam module version 0.73 - * - * Allows you to use your USB webcam with V4L2 applications - * This is still in heavy developpement ! - * - * Copyright (C) 2004 Antoine Jacquet - * http://royale.zerezo.com/zr364xx/ - * - * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers - * V4L2 version inspired by meye.c driver - * - * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* Version Information */ -#define DRIVER_VERSION "0.7.4" -#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" -#define DRIVER_DESC "Zoran 364xx" - - -/* Camera */ -#define FRAMES 1 -#define MAX_FRAME_SIZE 200000 -#define BUFFER_SIZE 0x1000 -#define CTRL_TIMEOUT 500 - -#define ZR364XX_DEF_BUFS 4 -#define ZR364XX_READ_IDLE 0 -#define ZR364XX_READ_FRAME 1 - -/* Debug macro */ -#define DBG(fmt, args...) \ - do { \ - if (debug) { \ - printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ - } \ - } while (0) - -/*#define FULL_DEBUG 1*/ -#ifdef FULL_DEBUG -#define _DBG DBG -#else -#define _DBG(fmt, args...) -#endif - -/* Init methods, need to find nicer names for these - * the exact names of the chipsets would be the best if someone finds it */ -#define METHOD0 0 -#define METHOD1 1 -#define METHOD2 2 -#define METHOD3 3 - - -/* Module parameters */ -static int debug; -static int mode; - - -/* Module parameters interface */ -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level"); -module_param(mode, int, 0644); -MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); - - -/* Devices supported by this driver - * .driver_info contains the init method used by the camera */ -static struct usb_device_id device_table[] = { - {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, - {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, - {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, - {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, - {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, - {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, - {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, - {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, - {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, - {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, - {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, - {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, - {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, - {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, - {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, - {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, - {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, - {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, - {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, - {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, - {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* frame structure */ -struct zr364xx_framei { - unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, - ZR364XX_READ_FRAME */ - void *lpvbits; /* image data */ - unsigned long cur_size; /* current data copied to it */ -}; - -/* image buffer structure */ -struct zr364xx_bufferi { - unsigned long dwFrames; /* number of frames in buffer */ - struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ -}; - -struct zr364xx_dmaqueue { - struct list_head active; - struct zr364xx_camera *cam; -}; - -struct zr364xx_pipeinfo { - u32 transfer_size; - u8 *transfer_buffer; - u32 state; - void *stream_urb; - void *cam; /* back pointer to zr364xx_camera struct */ - u32 err_count; - u32 idx; -}; - -struct zr364xx_fmt { - char *name; - u32 fourcc; - int depth; -}; - -/* image formats. */ -static const struct zr364xx_fmt formats[] = { - { - .name = "JPG", - .fourcc = V4L2_PIX_FMT_JPEG, - .depth = 24 - } -}; - -/* Camera stuff */ -struct zr364xx_camera { - struct usb_device *udev; /* save off the usb device pointer */ - struct usb_interface *interface;/* the interface for this device */ - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; /* v4l video device */ - struct v4l2_fh *owner; /* owns the streaming */ - int nb; - struct zr364xx_bufferi buffer; - int skip; - int width; - int height; - int method; - struct mutex lock; - - spinlock_t slock; - struct zr364xx_dmaqueue vidq; - int last_frame; - int cur_frame; - unsigned long frame_count; - int b_acquire; - struct zr364xx_pipeinfo pipe[1]; - - u8 read_endpoint; - - const struct zr364xx_fmt *fmt; - struct videobuf_queue vb_vidq; - bool was_streaming; -}; - -/* buffer for one video frame */ -struct zr364xx_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - const struct zr364xx_fmt *fmt; -}; - -/* function used to send initialisation commands to the camera */ -static int send_control_msg(struct usb_device *udev, u8 request, u16 value, - u16 index, unsigned char *cp, u16 size) -{ - int status; - - unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); - if (!transfer_buffer) { - dev_err(&udev->dev, "kmalloc(%d) failed\n", size); - return -ENOMEM; - } - - memcpy(transfer_buffer, cp, size); - - status = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, - transfer_buffer, size, CTRL_TIMEOUT); - - kfree(transfer_buffer); - return status; -} - - -/* Control messages sent to the camera to initialize it - * and launch the capture */ -typedef struct { - unsigned int value; - unsigned int size; - unsigned char *bytes; -} message; - -/* method 0 */ -static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; -static unsigned char m0d3[] = { 0, 0 }; -static message m0[] = { - {0x1f30, 0, NULL}, - {0xd000, 0, NULL}, - {0x3370, sizeof(m0d1), m0d1}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2610, sizeof(m0d2), m0d2}, - {0xe107, 0, NULL}, - {0x2502, 0, NULL}, - {0x1f70, 0, NULL}, - {0xd000, 0, NULL}, - {0x9a01, sizeof(m0d3), m0d3}, - {-1, -1, NULL} -}; - -/* method 1 */ -static unsigned char m1d1[] = { 0xff, 0xff }; -static unsigned char m1d2[] = { 0x00, 0x00 }; -static message m1[] = { - {0x1f30, 0, NULL}, - {0xd000, 0, NULL}, - {0xf000, 0, NULL}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2650, 0, NULL}, - {0xe107, 0, NULL}, - {0x2502, sizeof(m1d1), m1d1}, - {0x1f70, 0, NULL}, - {0xd000, 0, NULL}, - {0xd000, 0, NULL}, - {0xd000, 0, NULL}, - {0x9a01, sizeof(m1d2), m1d2}, - {-1, -1, NULL} -}; - -/* method 2 */ -static unsigned char m2d1[] = { 0xff, 0xff }; -static message m2[] = { - {0x1f30, 0, NULL}, - {0xf000, 0, NULL}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2650, 0, NULL}, - {0xe107, 0, NULL}, - {0x2502, sizeof(m2d1), m2d1}, - {0x1f70, 0, NULL}, - {-1, -1, NULL} -}; - -/* init table */ -static message *init[4] = { m0, m1, m2, m2 }; - - -/* JPEG static data in header (Huffman table, etc) */ -static unsigned char header1[] = { - 0xFF, 0xD8, - /* - 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', - 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, - */ - 0xFF, 0xDB, 0x00, 0x84 -}; -static unsigned char header2[] = { - 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, - 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, - 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, - 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, - 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, - 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, - 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, - 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, - 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, - 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, - 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, - 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, - 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, - 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, - 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, - 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, - 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00 -}; -static unsigned char header3; - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct zr364xx_camera *cam = vq->priv_data; - - *size = cam->width * cam->height * (cam->fmt->depth >> 3); - - if (*count == 0) - *count = ZR364XX_DEF_BUFS; - - if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) - *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) -{ - _DBG("%s\n", __func__); - - if (in_interrupt()) - BUG(); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct zr364xx_camera *cam = vq->priv_data; - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - int rc; - - DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ? - cam->fmt->name : ""); - if (cam->fmt == NULL) - return -EINVAL; - - buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); - - if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { - DBG("invalid buffer prepare\n"); - return -EINVAL; - } - - buf->fmt = cam->fmt; - buf->vb.width = cam->width; - buf->vb.height = cam->height; - buf->vb.field = field; - - if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; -fail: - free_buffer(vq, buf); - return rc; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - struct zr364xx_camera *cam = vq->priv_data; - - _DBG("%s\n", __func__); - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &cam->vidq.active); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - - _DBG("%s\n", __func__); - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops zr364xx_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/********************/ -/* V4L2 integration */ -/********************/ -static int zr364xx_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type); - -static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, - loff_t * ppos) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int err = 0; - - _DBG("%s\n", __func__); - - if (!buf) - return -EINVAL; - - if (!count) - return -EINVAL; - - if (mutex_lock_interruptible(&cam->lock)) - return -ERESTARTSYS; - - err = zr364xx_vidioc_streamon(file, file->private_data, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (err == 0) { - DBG("%s: reading %d bytes at pos %d.\n", __func__, - (int) count, (int) *ppos); - - /* NoMan Sux ! */ - err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, - file->f_flags & O_NONBLOCK); - } - mutex_unlock(&cam->lock); - return err; -} - -/* video buffer vmalloc implementation based partly on VIVI driver which is - * Copyright (c) 2006 by - * Mauro Carvalho Chehab - * Ted Walther - * John Sokol - * http://v4l.videotechnology.com/ - * - */ -static void zr364xx_fillbuff(struct zr364xx_camera *cam, - struct zr364xx_buffer *buf, - int jpgsize) -{ - int pos = 0; - struct timeval ts; - const char *tmpbuf; - char *vbuf = videobuf_to_vmalloc(&buf->vb); - unsigned long last_frame; - - if (!vbuf) - return; - - last_frame = cam->last_frame; - if (last_frame != -1) { - tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; - switch (buf->fmt->fourcc) { - case V4L2_PIX_FMT_JPEG: - buf->vb.size = jpgsize; - memcpy(vbuf, tmpbuf, buf->vb.size); - break; - default: - printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); - } - cam->last_frame = -1; - } else { - printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); - return; - } - DBG("%s: Buffer 0x%08lx size= %d\n", __func__, - (unsigned long)vbuf, pos); - /* tell v4l buffer was filled */ - - buf->vb.field_count = cam->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; - buf->vb.state = VIDEOBUF_DONE; -} - -static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) -{ - struct zr364xx_dmaqueue *dma_q = &cam->vidq; - struct zr364xx_buffer *buf; - unsigned long flags = 0; - int rc = 0; - - DBG("wakeup: %p\n", &dma_q); - spin_lock_irqsave(&cam->slock, flags); - - if (list_empty(&dma_q->active)) { - DBG("No active queue to serve\n"); - rc = -1; - goto unlock; - } - buf = list_entry(dma_q->active.next, - struct zr364xx_buffer, vb.queue); - - if (!waitqueue_active(&buf->vb.done)) { - /* no one active */ - rc = -1; - goto unlock; - } - list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); - DBG("[%p/%d] wakeup\n", buf, buf->vb.i); - zr364xx_fillbuff(cam, buf, jpgsize); - wake_up(&buf->vb.done); - DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); -unlock: - spin_unlock_irqrestore(&cam->slock, flags); - return rc; -} - -/* this function moves the usb stream read pipe data - * into the system buffers. - * returns 0 on success, EAGAIN if more data to process (call this - * function again). - */ -static int zr364xx_read_video_callback(struct zr364xx_camera *cam, - struct zr364xx_pipeinfo *pipe_info, - struct urb *purb) -{ - unsigned char *pdest; - unsigned char *psrc; - s32 idx = -1; - struct zr364xx_framei *frm; - int i = 0; - unsigned char *ptr = NULL; - - _DBG("buffer to user\n"); - idx = cam->cur_frame; - frm = &cam->buffer.frame[idx]; - - /* swap bytes if camera needs it */ - if (cam->method == METHOD0) { - u16 *buf = (u16 *)pipe_info->transfer_buffer; - for (i = 0; i < purb->actual_length/2; i++) - swab16s(buf + i); - } - - /* search done. now find out if should be acquiring */ - if (!cam->b_acquire) { - /* we found a frame, but this channel is turned off */ - frm->ulState = ZR364XX_READ_IDLE; - return -EINVAL; - } - - psrc = (u8 *)pipe_info->transfer_buffer; - ptr = pdest = frm->lpvbits; - - if (frm->ulState == ZR364XX_READ_IDLE) { - frm->ulState = ZR364XX_READ_FRAME; - frm->cur_size = 0; - - _DBG("jpeg header, "); - memcpy(ptr, header1, sizeof(header1)); - ptr += sizeof(header1); - header3 = 0; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, psrc, 64); - ptr += 64; - header3 = 1; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, psrc + 64, 64); - ptr += 64; - memcpy(ptr, header2, sizeof(header2)); - ptr += sizeof(header2); - memcpy(ptr, psrc + 128, - purb->actual_length - 128); - ptr += purb->actual_length - 128; - _DBG("header : %d %d %d %d %d %d %d %d %d\n", - psrc[0], psrc[1], psrc[2], - psrc[3], psrc[4], psrc[5], - psrc[6], psrc[7], psrc[8]); - frm->cur_size = ptr - pdest; - } else { - if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { - dev_info(&cam->udev->dev, - "%s: buffer (%d bytes) too small to hold " - "frame data. Discarding frame data.\n", - __func__, MAX_FRAME_SIZE); - } else { - pdest += frm->cur_size; - memcpy(pdest, psrc, purb->actual_length); - frm->cur_size += purb->actual_length; - } - } - /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, - purb->actual_length);*/ - - if (purb->actual_length < pipe_info->transfer_size) { - _DBG("****************Buffer[%d]full*************\n", idx); - cam->last_frame = cam->cur_frame; - cam->cur_frame++; - /* end of system frame ring buffer, start at zero */ - if (cam->cur_frame == cam->buffer.dwFrames) - cam->cur_frame = 0; - - /* frame ready */ - /* go back to find the JPEG EOI marker */ - ptr = pdest = frm->lpvbits; - ptr += frm->cur_size - 2; - while (ptr > pdest) { - if (*ptr == 0xFF && *(ptr + 1) == 0xD9 - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr == pdest) - DBG("No EOI marker\n"); - - /* Sometimes there is junk data in the middle of the picture, - * we want to skip this bogus frames */ - while (ptr > pdest) { - if (*ptr == 0xFF && *(ptr + 1) == 0xFF - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr != pdest) { - DBG("Bogus frame ? %d\n", ++(cam->nb)); - } else if (cam->b_acquire) { - /* we skip the 2 first frames which are usually buggy */ - if (cam->skip) - cam->skip--; - else { - _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", - frm->cur_size, - pdest[0], pdest[1], pdest[2], pdest[3], - pdest[4], pdest[5], pdest[6], pdest[7]); - - zr364xx_got_frame(cam, frm->cur_size); - } - } - cam->frame_count++; - frm->ulState = ZR364XX_READ_IDLE; - frm->cur_size = 0; - } - /* done successfully */ - return 0; -} - -static int zr364xx_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); - strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&cam->udev->dev), - sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int zr364xx_vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, DRIVER_DESC " Camera"); - i->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - -static int zr364xx_vidioc_g_input(struct file *file, void *priv, - unsigned int *i) -{ - *i = 0; - return 0; -} - -static int zr364xx_vidioc_s_input(struct file *file, void *priv, - unsigned int i) -{ - if (i != 0) - return -EINVAL; - return 0; -} - -static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct zr364xx_camera *cam = - container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); - int temp; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - /* hardware brightness */ - send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); - temp = (0x60 << 8) + 127 - ctrl->val; - send_control_msg(cam->udev, 1, temp, 0, NULL, 0); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, - void *priv, struct v4l2_fmtdesc *f) -{ - if (f->index > 0) - return -EINVAL; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - strcpy(f->description, formats[0].name); - f->pixelformat = formats[0].fourcc; - return 0; -} - -static char *decode_fourcc(__u32 pixelformat, char *buf) -{ - buf[0] = pixelformat & 0xff; - buf[1] = (pixelformat >> 8) & 0xff; - buf[2] = (pixelformat >> 16) & 0xff; - buf[3] = (pixelformat >> 24) & 0xff; - buf[4] = '\0'; - return buf; -} - -static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam = video_drvdata(file); - char pixelformat_name[5]; - - if (cam == NULL) - return -ENODEV; - - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { - DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); - return -EINVAL; - } - - if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && - !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - } - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), - f->fmt.pix.field); - return 0; -} - -static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam; - - if (file == NULL) - return -ENODEV; - cam = video_drvdata(file); - - f->fmt.pix.pixelformat = formats[0].fourcc; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - return 0; -} - -static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam = video_drvdata(file); - struct videobuf_queue *q = &cam->vb_vidq; - char pixelformat_name[5]; - int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); - int i; - - if (ret < 0) - return ret; - - mutex_lock(&q->vb_lock); - - if (videobuf_queue_is_busy(&cam->vb_vidq)) { - DBG("%s queue busy\n", __func__); - ret = -EBUSY; - goto out; - } - - if (cam->owner) { - DBG("%s can't change format after started\n", __func__); - ret = -EBUSY; - goto out; - } - - cam->width = f->fmt.pix.width; - cam->height = f->fmt.pix.height; - DBG("%s: %dx%d mode selected\n", __func__, - cam->width, cam->height); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - cam->vb_vidq.field = f->fmt.pix.field; - - if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) - mode = 1; - else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) - mode = 2; - else - mode = 0; - - m0d1[0] = mode; - m1[2].value = 0xf000 + mode; - m2[1].value = 0xf000 + mode; - - /* special case for METHOD3, the modes are different */ - if (cam->method == METHOD3) { - switch (mode) { - case 1: - m2[1].value = 0xf000 + 4; - break; - case 2: - m2[1].value = 0xf000 + 0; - break; - default: - m2[1].value = 0xf000 + 1; - break; - } - } - - header2[437] = cam->height / 256; - header2[438] = cam->height % 256; - header2[439] = cam->width / 256; - header2[440] = cam->width % 256; - - for (i = 0; init[cam->method][i].size != -1; i++) { - ret = - send_control_msg(cam->udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (ret < 0) { - dev_err(&cam->udev->dev, - "error during resolution change sequence: %d\n", i); - goto out; - } - } - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - cam->skip = 2; - ret = 0; - -out: - mutex_unlock(&q->vb_lock); - - DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), - f->fmt.pix.field); - return ret; -} - -static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - if (cam->owner && cam->owner != priv) - return -EBUSY; - return videobuf_reqbufs(&cam->vb_vidq, p); -} - -static int zr364xx_vidioc_querybuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - rc = videobuf_querybuf(&cam->vb_vidq, p); - return rc; -} - -static int zr364xx_vidioc_qbuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - _DBG("%s\n", __func__); - if (cam->owner && cam->owner != priv) - return -EBUSY; - rc = videobuf_qbuf(&cam->vb_vidq, p); - return rc; -} - -static int zr364xx_vidioc_dqbuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - _DBG("%s\n", __func__); - if (cam->owner && cam->owner != priv) - return -EBUSY; - rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); - return rc; -} - -static void read_pipe_completion(struct urb *purb) -{ - struct zr364xx_pipeinfo *pipe_info; - struct zr364xx_camera *cam; - int pipe; - - pipe_info = purb->context; - _DBG("%s %p, status %d\n", __func__, purb, purb->status); - if (pipe_info == NULL) { - printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); - return; - } - - cam = pipe_info->cam; - if (cam == NULL) { - printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); - return; - } - - /* if shutting down, do not resubmit, exit immediately */ - if (purb->status == -ESHUTDOWN) { - DBG("%s, err shutdown\n", __func__); - pipe_info->err_count++; - return; - } - - if (pipe_info->state == 0) { - DBG("exiting USB pipe\n"); - return; - } - - if (purb->actual_length < 0 || - purb->actual_length > pipe_info->transfer_size) { - dev_err(&cam->udev->dev, "wrong number of bytes\n"); - return; - } - - if (purb->status == 0) - zr364xx_read_video_callback(cam, pipe_info, purb); - else { - pipe_info->err_count++; - DBG("%s: failed URB %d\n", __func__, purb->status); - } - - pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); - - /* reuse urb */ - usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->transfer_size, - read_pipe_completion, pipe_info); - - if (pipe_info->state != 0) { - purb->status = usb_submit_urb(pipe_info->stream_urb, - GFP_ATOMIC); - - if (purb->status) - dev_err(&cam->udev->dev, - "error submitting urb (error=%i)\n", - purb->status); - } else - DBG("read pipe complete state 0\n"); -} - -static int zr364xx_start_readpipe(struct zr364xx_camera *cam) -{ - int pipe; - int retval; - struct zr364xx_pipeinfo *pipe_info = cam->pipe; - pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); - DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); - - pipe_info->state = 1; - pipe_info->err_count = 0; - pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pipe_info->stream_urb) { - dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n"); - return -ENOMEM; - } - /* transfer buffer allocated in board_init */ - usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->transfer_size, - read_pipe_completion, pipe_info); - - DBG("submitting URB %p\n", pipe_info->stream_urb); - retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); - if (retval) { - printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); - return retval; - } - - return 0; -} - -static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) -{ - struct zr364xx_pipeinfo *pipe_info; - - if (cam == NULL) { - printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); - return; - } - DBG("stop read pipe\n"); - pipe_info = cam->pipe; - if (pipe_info) { - if (pipe_info->state != 0) - pipe_info->state = 0; - - if (pipe_info->stream_urb) { - /* cancel urb */ - usb_kill_urb(pipe_info->stream_urb); - usb_free_urb(pipe_info->stream_urb); - pipe_info->stream_urb = NULL; - } - } - return; -} - -/* starts acquisition process */ -static int zr364xx_start_acquire(struct zr364xx_camera *cam) -{ - int j; - - DBG("start acquire\n"); - - cam->last_frame = -1; - cam->cur_frame = 0; - for (j = 0; j < FRAMES; j++) { - cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[j].cur_size = 0; - } - cam->b_acquire = 1; - return 0; -} - -static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) -{ - cam->b_acquire = 0; - return 0; -} - -static int zr364xx_prepare(struct zr364xx_camera *cam) -{ - int res; - int i, j; - - for (i = 0; init[cam->method][i].size != -1; i++) { - res = send_control_msg(cam->udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (res < 0) { - dev_err(&cam->udev->dev, - "error during open sequence: %d\n", i); - return res; - } - } - - cam->skip = 2; - cam->last_frame = -1; - cam->cur_frame = 0; - cam->frame_count = 0; - for (j = 0; j < FRAMES; j++) { - cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[j].cur_size = 0; - } - v4l2_ctrl_handler_setup(&cam->ctrl_handler); - return 0; -} - -static int zr364xx_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int res; - - DBG("%s\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->owner && cam->owner != priv) - return -EBUSY; - - res = zr364xx_prepare(cam); - if (res) - return res; - res = videobuf_streamon(&cam->vb_vidq); - if (res == 0) { - zr364xx_start_acquire(cam); - cam->owner = file->private_data; - } - return res; -} - -static int zr364xx_vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - DBG("%s\n", __func__); - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cam->owner && cam->owner != priv) - return -EBUSY; - zr364xx_stop_acquire(cam); - return videobuf_streamoff(&cam->vb_vidq); -} - - -/* open the camera */ -static int zr364xx_open(struct file *file) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int err; - - DBG("%s\n", __func__); - - if (mutex_lock_interruptible(&cam->lock)) - return -ERESTARTSYS; - - err = v4l2_fh_open(file); - if (err) - goto out; - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - err = 0; - -out: - mutex_unlock(&cam->lock); - DBG("%s: %d\n", __func__, err); - return err; -} - -static void zr364xx_release(struct v4l2_device *v4l2_dev) -{ - struct zr364xx_camera *cam = - container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); - unsigned long i; - - v4l2_device_unregister(&cam->v4l2_dev); - - videobuf_mmap_free(&cam->vb_vidq); - - /* release sys buffers */ - for (i = 0; i < FRAMES; i++) { - if (cam->buffer.frame[i].lpvbits) { - DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); - vfree(cam->buffer.frame[i].lpvbits); - } - cam->buffer.frame[i].lpvbits = NULL; - } - - v4l2_ctrl_handler_free(&cam->ctrl_handler); - /* release transfer buffer */ - kfree(cam->pipe->transfer_buffer); - kfree(cam); -} - -/* release the camera */ -static int zr364xx_close(struct file *file) -{ - struct zr364xx_camera *cam; - struct usb_device *udev; - int i; - - DBG("%s\n", __func__); - cam = video_drvdata(file); - - mutex_lock(&cam->lock); - udev = cam->udev; - - if (file->private_data == cam->owner) { - /* turn off stream */ - if (cam->b_acquire) - zr364xx_stop_acquire(cam); - videobuf_streamoff(&cam->vb_vidq); - - for (i = 0; i < 2; i++) { - send_control_msg(udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - } - cam->owner = NULL; - } - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - mutex_unlock(&cam->lock); - return v4l2_fh_release(file); -} - - -static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int ret; - - if (cam == NULL) { - DBG("%s: cam == NULL\n", __func__); - return -ENODEV; - } - DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); - - ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); - - DBG("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; -} - -static unsigned int zr364xx_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct zr364xx_camera *cam = video_drvdata(file); - struct videobuf_queue *q = &cam->vb_vidq; - unsigned res = v4l2_ctrl_poll(file, wait); - - _DBG("%s\n", __func__); - - return res | videobuf_poll_stream(file, q, wait); -} - -static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { - .s_ctrl = zr364xx_s_ctrl, -}; - -static const struct v4l2_file_operations zr364xx_fops = { - .owner = THIS_MODULE, - .open = zr364xx_open, - .release = zr364xx_close, - .read = zr364xx_read, - .mmap = zr364xx_mmap, - .unlocked_ioctl = video_ioctl2, - .poll = zr364xx_poll, -}; - -static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { - .vidioc_querycap = zr364xx_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, - .vidioc_enum_input = zr364xx_vidioc_enum_input, - .vidioc_g_input = zr364xx_vidioc_g_input, - .vidioc_s_input = zr364xx_vidioc_s_input, - .vidioc_streamon = zr364xx_vidioc_streamon, - .vidioc_streamoff = zr364xx_vidioc_streamoff, - .vidioc_reqbufs = zr364xx_vidioc_reqbufs, - .vidioc_querybuf = zr364xx_vidioc_querybuf, - .vidioc_qbuf = zr364xx_vidioc_qbuf, - .vidioc_dqbuf = zr364xx_vidioc_dqbuf, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device zr364xx_template = { - .name = DRIVER_DESC, - .fops = &zr364xx_fops, - .ioctl_ops = &zr364xx_ioctl_ops, - .release = video_device_release_empty, -}; - - - -/*******************/ -/* USB integration */ -/*******************/ -static int zr364xx_board_init(struct zr364xx_camera *cam) -{ - struct zr364xx_pipeinfo *pipe = cam->pipe; - unsigned long i; - - DBG("board init: %p\n", cam); - memset(pipe, 0, sizeof(*pipe)); - pipe->cam = cam; - pipe->transfer_size = BUFFER_SIZE; - - pipe->transfer_buffer = kzalloc(pipe->transfer_size, - GFP_KERNEL); - if (pipe->transfer_buffer == NULL) { - DBG("out of memory!\n"); - return -ENOMEM; - } - - cam->b_acquire = 0; - cam->frame_count = 0; - - /*** start create system buffers ***/ - for (i = 0; i < FRAMES; i++) { - /* always allocate maximum size for system buffers */ - cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); - - DBG("valloc %p, idx %lu, pdata %p\n", - &cam->buffer.frame[i], i, - cam->buffer.frame[i].lpvbits); - if (cam->buffer.frame[i].lpvbits == NULL) { - printk(KERN_INFO KBUILD_MODNAME ": out of memory. " - "Using less frames\n"); - break; - } - } - - if (i == 0) { - printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); - kfree(cam->pipe->transfer_buffer); - cam->pipe->transfer_buffer = NULL; - return -ENOMEM; - } else - cam->buffer.dwFrames = i; - - /* make sure internal states are set */ - for (i = 0; i < FRAMES; i++) { - cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[i].cur_size = 0; - } - - cam->cur_frame = 0; - cam->last_frame = -1; - /*** end create system buffers ***/ - - /* start read pipe */ - zr364xx_start_readpipe(cam); - DBG(": board initialized\n"); - return 0; -} - -static int zr364xx_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct zr364xx_camera *cam = NULL; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct v4l2_ctrl_handler *hdl; - int err; - int i; - - DBG("probing...\n"); - - dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); - dev_info(&intf->dev, "model %04x:%04x detected\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); - - cam = kzalloc(sizeof(struct zr364xx_camera), GFP_KERNEL); - if (cam == NULL) { - dev_err(&udev->dev, "cam: out of memory !\n"); - return -ENOMEM; - } - - cam->v4l2_dev.release = zr364xx_release; - err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); - if (err < 0) { - dev_err(&udev->dev, "couldn't register v4l2_device\n"); - kfree(cam); - return err; - } - hdl = &cam->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 1); - v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); - if (hdl->error) { - err = hdl->error; - dev_err(&udev->dev, "couldn't register control\n"); - goto fail; - } - /* save the init method used by this camera */ - cam->method = id->driver_info; - mutex_init(&cam->lock); - cam->vdev = zr364xx_template; - cam->vdev.lock = &cam->lock; - cam->vdev.v4l2_dev = &cam->v4l2_dev; - cam->vdev.ctrl_handler = &cam->ctrl_handler; - set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); - video_set_drvdata(&cam->vdev, cam); - if (debug) - cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; - - cam->udev = udev; - - switch (mode) { - case 1: - dev_info(&udev->dev, "160x120 mode selected\n"); - cam->width = 160; - cam->height = 120; - break; - case 2: - dev_info(&udev->dev, "640x480 mode selected\n"); - cam->width = 640; - cam->height = 480; - break; - default: - dev_info(&udev->dev, "320x240 mode selected\n"); - cam->width = 320; - cam->height = 240; - break; - } - - m0d1[0] = mode; - m1[2].value = 0xf000 + mode; - m2[1].value = 0xf000 + mode; - - /* special case for METHOD3, the modes are different */ - if (cam->method == METHOD3) { - switch (mode) { - case 1: - m2[1].value = 0xf000 + 4; - break; - case 2: - m2[1].value = 0xf000 + 0; - break; - default: - m2[1].value = 0xf000 + 1; - break; - } - } - - header2[437] = cam->height / 256; - header2[438] = cam->height % 256; - header2[439] = cam->width / 256; - header2[440] = cam->width % 256; - - cam->nb = 0; - - DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); - - /* set up the endpoint information */ - iface_desc = intf->cur_altsetting; - DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - cam->read_endpoint = endpoint->bEndpointAddress; - } - } - - if (!cam->read_endpoint) { - err = -ENOMEM; - dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); - goto fail; - } - - /* v4l */ - INIT_LIST_HEAD(&cam->vidq.active); - cam->vidq.cam = cam; - - usb_set_intfdata(intf, cam); - - /* load zr364xx board specific */ - err = zr364xx_board_init(cam); - if (!err) - err = v4l2_ctrl_handler_setup(hdl); - if (err) - goto fail; - - spin_lock_init(&cam->slock); - - cam->fmt = formats; - - videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, - NULL, &cam->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct zr364xx_buffer), cam, &cam->lock); - - err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); - if (err) { - dev_err(&udev->dev, "video_register_device failed\n"); - goto fail; - } - - dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", - video_device_node_name(&cam->vdev)); - return 0; - -fail: - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); - return err; -} - - -static void zr364xx_disconnect(struct usb_interface *intf) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - - mutex_lock(&cam->lock); - usb_set_intfdata(intf, NULL); - dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); - video_unregister_device(&cam->vdev); - v4l2_device_disconnect(&cam->v4l2_dev); - - /* stops the read pipe if it is running */ - if (cam->b_acquire) - zr364xx_stop_acquire(cam); - - zr364xx_stop_readpipe(cam); - mutex_unlock(&cam->lock); - v4l2_device_put(&cam->v4l2_dev); -} - - -#ifdef CONFIG_PM -static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - - cam->was_streaming = cam->b_acquire; - if (!cam->was_streaming) - return 0; - zr364xx_stop_acquire(cam); - zr364xx_stop_readpipe(cam); - return 0; -} - -static int zr364xx_resume(struct usb_interface *intf) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - int res; - - if (!cam->was_streaming) - return 0; - - zr364xx_start_readpipe(cam); - res = zr364xx_prepare(cam); - if (!res) - zr364xx_start_acquire(cam); - return res; -} -#endif - -/**********************/ -/* Module integration */ -/**********************/ - -static struct usb_driver zr364xx_driver = { - .name = "zr364xx", - .probe = zr364xx_probe, - .disconnect = zr364xx_disconnect, -#ifdef CONFIG_PM - .suspend = zr364xx_suspend, - .resume = zr364xx_resume, - .reset_resume = zr364xx_resume, -#endif - .id_table = device_table -}; - -module_usb_driver(zr364xx_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); -- cgit v1.2.3 From cb7a01ac324bf2ee2c666f37ac867e4135f9785a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Aug 2012 16:23:43 -0300 Subject: [media] move i2c files into drivers/media/i2c Move ancillary I2C drivers into drivers/media/i2c, in order to better organize them. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 9 +- drivers/media/Makefile | 2 +- drivers/media/i2c/Kconfig | 566 +++ drivers/media/i2c/Makefile | 63 + drivers/media/i2c/adp1653.c | 487 +++ drivers/media/i2c/adv7170.c | 410 ++ drivers/media/i2c/adv7175.c | 460 ++ drivers/media/i2c/adv7180.c | 667 +++ drivers/media/i2c/adv7183.c | 699 ++++ drivers/media/i2c/adv7183_regs.h | 107 + drivers/media/i2c/adv7343.c | 476 +++ drivers/media/i2c/adv7343_regs.h | 181 + drivers/media/i2c/adv7393.c | 487 +++ drivers/media/i2c/adv7393_regs.h | 188 + drivers/media/i2c/ak881x.c | 359 ++ drivers/media/i2c/aptina-pll.c | 173 + drivers/media/i2c/aptina-pll.h | 56 + drivers/media/i2c/as3645a.c | 888 ++++ drivers/media/i2c/bt819.c | 517 +++ drivers/media/i2c/bt856.c | 273 ++ drivers/media/i2c/bt866.c | 243 ++ drivers/media/i2c/btcx-risc.c | 260 ++ drivers/media/i2c/btcx-risc.h | 34 + drivers/media/i2c/cs5345.c | 252 ++ drivers/media/i2c/cs53l32a.c | 251 ++ drivers/media/i2c/cx2341x.c | 1726 ++++++++ drivers/media/i2c/cx25840/Kconfig | 8 + drivers/media/i2c/cx25840/Makefile | 6 + drivers/media/i2c/cx25840/cx25840-audio.c | 571 +++ drivers/media/i2c/cx25840/cx25840-core.c | 5340 ++++++++++++++++++++++++ drivers/media/i2c/cx25840/cx25840-core.h | 137 + drivers/media/i2c/cx25840/cx25840-firmware.c | 175 + drivers/media/i2c/cx25840/cx25840-ir.c | 1281 ++++++ drivers/media/i2c/cx25840/cx25840-vbi.c | 256 ++ drivers/media/i2c/ir-kbd-i2c.c | 489 +++ drivers/media/i2c/ks0127.c | 724 ++++ drivers/media/i2c/ks0127.h | 51 + drivers/media/i2c/m52790.c | 216 + drivers/media/i2c/m5mols/Kconfig | 6 + drivers/media/i2c/m5mols/Makefile | 3 + drivers/media/i2c/m5mols/m5mols.h | 334 ++ drivers/media/i2c/m5mols/m5mols_capture.c | 155 + drivers/media/i2c/m5mols/m5mols_controls.c | 628 +++ drivers/media/i2c/m5mols/m5mols_core.c | 990 +++++ drivers/media/i2c/m5mols/m5mols_reg.h | 362 ++ drivers/media/i2c/msp3400-driver.c | 899 ++++ drivers/media/i2c/msp3400-driver.h | 137 + drivers/media/i2c/msp3400-kthreads.c | 1165 ++++++ drivers/media/i2c/mt9m032.c | 878 ++++ drivers/media/i2c/mt9p031.c | 1071 +++++ drivers/media/i2c/mt9t001.c | 833 ++++ drivers/media/i2c/mt9v011.c | 712 ++++ drivers/media/i2c/mt9v032.c | 763 ++++ drivers/media/i2c/noon010pc30.c | 851 ++++ drivers/media/i2c/ov7670.c | 1586 +++++++ drivers/media/i2c/s5k6aa.c | 1667 ++++++++ drivers/media/i2c/saa6588.c | 542 +++ drivers/media/i2c/saa7110.c | 494 +++ drivers/media/i2c/saa7115.c | 1727 ++++++++ drivers/media/i2c/saa711x_regs.h | 549 +++ drivers/media/i2c/saa7127.c | 852 ++++ drivers/media/i2c/saa717x.c | 1378 ++++++ drivers/media/i2c/saa7185.c | 377 ++ drivers/media/i2c/saa7191.c | 659 +++ drivers/media/i2c/saa7191.h | 245 ++ drivers/media/i2c/smiapp-pll.c | 418 ++ drivers/media/i2c/smiapp-pll.h | 103 + drivers/media/i2c/smiapp/Kconfig | 7 + drivers/media/i2c/smiapp/Makefile | 5 + drivers/media/i2c/smiapp/smiapp-core.c | 2895 +++++++++++++ drivers/media/i2c/smiapp/smiapp-limits.c | 132 + drivers/media/i2c/smiapp/smiapp-limits.h | 128 + drivers/media/i2c/smiapp/smiapp-quirk.c | 306 ++ drivers/media/i2c/smiapp/smiapp-quirk.h | 83 + drivers/media/i2c/smiapp/smiapp-reg-defs.h | 503 +++ drivers/media/i2c/smiapp/smiapp-reg.h | 122 + drivers/media/i2c/smiapp/smiapp-regs.c | 273 ++ drivers/media/i2c/smiapp/smiapp-regs.h | 49 + drivers/media/i2c/smiapp/smiapp.h | 252 ++ drivers/media/i2c/sr030pc30.c | 871 ++++ drivers/media/i2c/tcm825x.c | 937 +++++ drivers/media/i2c/tcm825x.h | 200 + drivers/media/i2c/tda7432.c | 485 +++ drivers/media/i2c/tda9840.c | 224 + drivers/media/i2c/tea6415c.c | 187 + drivers/media/i2c/tea6415c.h | 27 + drivers/media/i2c/tea6420.c | 169 + drivers/media/i2c/tea6420.h | 24 + drivers/media/i2c/ths7303.c | 140 + drivers/media/i2c/tlv320aic23b.c | 230 + drivers/media/i2c/tvaudio.c | 2118 ++++++++++ drivers/media/i2c/tveeprom.c | 792 ++++ drivers/media/i2c/tvp514x.c | 1166 ++++++ drivers/media/i2c/tvp514x_regs.h | 287 ++ drivers/media/i2c/tvp5150.c | 1274 ++++++ drivers/media/i2c/tvp5150_reg.h | 139 + drivers/media/i2c/tvp7002.c | 1145 +++++ drivers/media/i2c/tvp7002_reg.h | 150 + drivers/media/i2c/upd64031a.c | 274 ++ drivers/media/i2c/upd64083.c | 246 ++ drivers/media/i2c/vp27smpx.c | 211 + drivers/media/i2c/vpx3220.c | 591 +++ drivers/media/i2c/vs6624.c | 928 ++++ drivers/media/i2c/vs6624_regs.h | 337 ++ drivers/media/i2c/wm8739.c | 294 ++ drivers/media/i2c/wm8775.c | 342 ++ drivers/media/pci/bt8xx/Makefile | 2 +- drivers/media/pci/cx23885/Makefile | 2 +- drivers/media/pci/cx25821/Makefile | 2 +- drivers/media/pci/cx88/Makefile | 2 +- drivers/media/pci/ivtv/Makefile | 2 +- drivers/media/pci/saa7134/Makefile | 2 +- drivers/media/pci/saa7146/Makefile | 2 +- drivers/media/pci/saa7164/Makefile | 2 +- drivers/media/usb/cx231xx/Makefile | 2 +- drivers/media/usb/em28xx/Makefile | 2 +- drivers/media/usb/hdpvr/Makefile | 2 +- drivers/media/usb/pvrusb2/Makefile | 2 +- drivers/media/usb/stk1160/Makefile | 2 +- drivers/media/usb/tlg2300/Makefile | 2 +- drivers/media/usb/tm6000/Makefile | 2 +- drivers/media/usb/usbvision/Makefile | 2 +- drivers/media/video/Kconfig | 579 +-- drivers/media/video/Makefile | 71 - drivers/media/video/adp1653.c | 487 --- drivers/media/video/adv7170.c | 410 -- drivers/media/video/adv7175.c | 460 -- drivers/media/video/adv7180.c | 667 --- drivers/media/video/adv7183.c | 699 ---- drivers/media/video/adv7183_regs.h | 107 - drivers/media/video/adv7343.c | 476 --- drivers/media/video/adv7343_regs.h | 181 - drivers/media/video/adv7393.c | 487 --- drivers/media/video/adv7393_regs.h | 188 - drivers/media/video/ak881x.c | 359 -- drivers/media/video/aptina-pll.c | 173 - drivers/media/video/aptina-pll.h | 56 - drivers/media/video/as3645a.c | 888 ---- drivers/media/video/bt819.c | 517 --- drivers/media/video/bt856.c | 273 -- drivers/media/video/bt866.c | 243 -- drivers/media/video/btcx-risc.c | 260 -- drivers/media/video/btcx-risc.h | 34 - drivers/media/video/cs5345.c | 252 -- drivers/media/video/cs53l32a.c | 251 -- drivers/media/video/cx2341x.c | 1726 -------- drivers/media/video/cx25840/Kconfig | 8 - drivers/media/video/cx25840/Makefile | 6 - drivers/media/video/cx25840/cx25840-audio.c | 571 --- drivers/media/video/cx25840/cx25840-core.c | 5340 ------------------------ drivers/media/video/cx25840/cx25840-core.h | 137 - drivers/media/video/cx25840/cx25840-firmware.c | 175 - drivers/media/video/cx25840/cx25840-ir.c | 1281 ------ drivers/media/video/cx25840/cx25840-vbi.c | 256 -- drivers/media/video/ir-kbd-i2c.c | 489 --- drivers/media/video/ks0127.c | 724 ---- drivers/media/video/ks0127.h | 51 - drivers/media/video/m52790.c | 216 - drivers/media/video/m5mols/Kconfig | 6 - drivers/media/video/m5mols/Makefile | 3 - drivers/media/video/m5mols/m5mols.h | 334 -- drivers/media/video/m5mols/m5mols_capture.c | 155 - drivers/media/video/m5mols/m5mols_controls.c | 628 --- drivers/media/video/m5mols/m5mols_core.c | 990 ----- drivers/media/video/m5mols/m5mols_reg.h | 362 -- drivers/media/video/msp3400-driver.c | 899 ---- drivers/media/video/msp3400-driver.h | 137 - drivers/media/video/msp3400-kthreads.c | 1165 ------ drivers/media/video/mt9m032.c | 878 ---- drivers/media/video/mt9p031.c | 1071 ----- drivers/media/video/mt9t001.c | 833 ---- drivers/media/video/mt9v011.c | 712 ---- drivers/media/video/mt9v032.c | 763 ---- drivers/media/video/noon010pc30.c | 851 ---- drivers/media/video/ov7670.c | 1586 ------- drivers/media/video/s5k6aa.c | 1667 -------- drivers/media/video/saa6588.c | 542 --- drivers/media/video/saa7110.c | 494 --- drivers/media/video/saa7115.c | 1727 -------- drivers/media/video/saa711x_regs.h | 549 --- drivers/media/video/saa7127.c | 852 ---- drivers/media/video/saa717x.c | 1378 ------ drivers/media/video/saa7185.c | 377 -- drivers/media/video/saa7191.c | 659 --- drivers/media/video/saa7191.h | 245 -- drivers/media/video/smiapp-pll.c | 418 -- drivers/media/video/smiapp-pll.h | 103 - drivers/media/video/smiapp/Kconfig | 7 - drivers/media/video/smiapp/Makefile | 5 - drivers/media/video/smiapp/smiapp-core.c | 2895 ------------- drivers/media/video/smiapp/smiapp-limits.c | 132 - drivers/media/video/smiapp/smiapp-limits.h | 128 - drivers/media/video/smiapp/smiapp-quirk.c | 306 -- drivers/media/video/smiapp/smiapp-quirk.h | 83 - drivers/media/video/smiapp/smiapp-reg-defs.h | 503 --- drivers/media/video/smiapp/smiapp-reg.h | 122 - drivers/media/video/smiapp/smiapp-regs.c | 273 -- drivers/media/video/smiapp/smiapp-regs.h | 49 - drivers/media/video/smiapp/smiapp.h | 252 -- drivers/media/video/sr030pc30.c | 871 ---- drivers/media/video/tcm825x.c | 937 ----- drivers/media/video/tcm825x.h | 200 - drivers/media/video/tda7432.c | 485 --- drivers/media/video/tda9840.c | 224 - drivers/media/video/tea6415c.c | 187 - drivers/media/video/tea6415c.h | 27 - drivers/media/video/tea6420.c | 169 - drivers/media/video/tea6420.h | 24 - drivers/media/video/ths7303.c | 140 - drivers/media/video/tlv320aic23b.c | 230 - drivers/media/video/tvaudio.c | 2118 ---------- drivers/media/video/tveeprom.c | 792 ---- drivers/media/video/tvp514x.c | 1166 ------ drivers/media/video/tvp514x_regs.h | 287 -- drivers/media/video/tvp5150.c | 1274 ------ drivers/media/video/tvp5150_reg.h | 139 - drivers/media/video/tvp7002.c | 1145 ----- drivers/media/video/tvp7002_reg.h | 150 - drivers/media/video/upd64031a.c | 274 -- drivers/media/video/upd64083.c | 246 -- drivers/media/video/vp27smpx.c | 211 - drivers/media/video/vpx3220.c | 591 --- drivers/media/video/vs6624.c | 928 ---- drivers/media/video/vs6624_regs.h | 337 -- drivers/media/video/wm8739.c | 294 -- drivers/media/video/wm8775.c | 342 -- 226 files changed, 58027 insertions(+), 58045 deletions(-) create mode 100644 drivers/media/i2c/Kconfig create mode 100644 drivers/media/i2c/Makefile create mode 100644 drivers/media/i2c/adp1653.c create mode 100644 drivers/media/i2c/adv7170.c create mode 100644 drivers/media/i2c/adv7175.c create mode 100644 drivers/media/i2c/adv7180.c create mode 100644 drivers/media/i2c/adv7183.c create mode 100644 drivers/media/i2c/adv7183_regs.h create mode 100644 drivers/media/i2c/adv7343.c create mode 100644 drivers/media/i2c/adv7343_regs.h create mode 100644 drivers/media/i2c/adv7393.c create mode 100644 drivers/media/i2c/adv7393_regs.h create mode 100644 drivers/media/i2c/ak881x.c create mode 100644 drivers/media/i2c/aptina-pll.c create mode 100644 drivers/media/i2c/aptina-pll.h create mode 100644 drivers/media/i2c/as3645a.c create mode 100644 drivers/media/i2c/bt819.c create mode 100644 drivers/media/i2c/bt856.c create mode 100644 drivers/media/i2c/bt866.c create mode 100644 drivers/media/i2c/btcx-risc.c create mode 100644 drivers/media/i2c/btcx-risc.h create mode 100644 drivers/media/i2c/cs5345.c create mode 100644 drivers/media/i2c/cs53l32a.c create mode 100644 drivers/media/i2c/cx2341x.c create mode 100644 drivers/media/i2c/cx25840/Kconfig create mode 100644 drivers/media/i2c/cx25840/Makefile create mode 100644 drivers/media/i2c/cx25840/cx25840-audio.c create mode 100644 drivers/media/i2c/cx25840/cx25840-core.c create mode 100644 drivers/media/i2c/cx25840/cx25840-core.h create mode 100644 drivers/media/i2c/cx25840/cx25840-firmware.c create mode 100644 drivers/media/i2c/cx25840/cx25840-ir.c create mode 100644 drivers/media/i2c/cx25840/cx25840-vbi.c create mode 100644 drivers/media/i2c/ir-kbd-i2c.c create mode 100644 drivers/media/i2c/ks0127.c create mode 100644 drivers/media/i2c/ks0127.h create mode 100644 drivers/media/i2c/m52790.c create mode 100644 drivers/media/i2c/m5mols/Kconfig create mode 100644 drivers/media/i2c/m5mols/Makefile create mode 100644 drivers/media/i2c/m5mols/m5mols.h create mode 100644 drivers/media/i2c/m5mols/m5mols_capture.c create mode 100644 drivers/media/i2c/m5mols/m5mols_controls.c create mode 100644 drivers/media/i2c/m5mols/m5mols_core.c create mode 100644 drivers/media/i2c/m5mols/m5mols_reg.h create mode 100644 drivers/media/i2c/msp3400-driver.c create mode 100644 drivers/media/i2c/msp3400-driver.h create mode 100644 drivers/media/i2c/msp3400-kthreads.c create mode 100644 drivers/media/i2c/mt9m032.c create mode 100644 drivers/media/i2c/mt9p031.c create mode 100644 drivers/media/i2c/mt9t001.c create mode 100644 drivers/media/i2c/mt9v011.c create mode 100644 drivers/media/i2c/mt9v032.c create mode 100644 drivers/media/i2c/noon010pc30.c create mode 100644 drivers/media/i2c/ov7670.c create mode 100644 drivers/media/i2c/s5k6aa.c create mode 100644 drivers/media/i2c/saa6588.c create mode 100644 drivers/media/i2c/saa7110.c create mode 100644 drivers/media/i2c/saa7115.c create mode 100644 drivers/media/i2c/saa711x_regs.h create mode 100644 drivers/media/i2c/saa7127.c create mode 100644 drivers/media/i2c/saa717x.c create mode 100644 drivers/media/i2c/saa7185.c create mode 100644 drivers/media/i2c/saa7191.c create mode 100644 drivers/media/i2c/saa7191.h create mode 100644 drivers/media/i2c/smiapp-pll.c create mode 100644 drivers/media/i2c/smiapp-pll.h create mode 100644 drivers/media/i2c/smiapp/Kconfig create mode 100644 drivers/media/i2c/smiapp/Makefile create mode 100644 drivers/media/i2c/smiapp/smiapp-core.c create mode 100644 drivers/media/i2c/smiapp/smiapp-limits.c create mode 100644 drivers/media/i2c/smiapp/smiapp-limits.h create mode 100644 drivers/media/i2c/smiapp/smiapp-quirk.c create mode 100644 drivers/media/i2c/smiapp/smiapp-quirk.h create mode 100644 drivers/media/i2c/smiapp/smiapp-reg-defs.h create mode 100644 drivers/media/i2c/smiapp/smiapp-reg.h create mode 100644 drivers/media/i2c/smiapp/smiapp-regs.c create mode 100644 drivers/media/i2c/smiapp/smiapp-regs.h create mode 100644 drivers/media/i2c/smiapp/smiapp.h create mode 100644 drivers/media/i2c/sr030pc30.c create mode 100644 drivers/media/i2c/tcm825x.c create mode 100644 drivers/media/i2c/tcm825x.h create mode 100644 drivers/media/i2c/tda7432.c create mode 100644 drivers/media/i2c/tda9840.c create mode 100644 drivers/media/i2c/tea6415c.c create mode 100644 drivers/media/i2c/tea6415c.h create mode 100644 drivers/media/i2c/tea6420.c create mode 100644 drivers/media/i2c/tea6420.h create mode 100644 drivers/media/i2c/ths7303.c create mode 100644 drivers/media/i2c/tlv320aic23b.c create mode 100644 drivers/media/i2c/tvaudio.c create mode 100644 drivers/media/i2c/tveeprom.c create mode 100644 drivers/media/i2c/tvp514x.c create mode 100644 drivers/media/i2c/tvp514x_regs.h create mode 100644 drivers/media/i2c/tvp5150.c create mode 100644 drivers/media/i2c/tvp5150_reg.h create mode 100644 drivers/media/i2c/tvp7002.c create mode 100644 drivers/media/i2c/tvp7002_reg.h create mode 100644 drivers/media/i2c/upd64031a.c create mode 100644 drivers/media/i2c/upd64083.c create mode 100644 drivers/media/i2c/vp27smpx.c create mode 100644 drivers/media/i2c/vpx3220.c create mode 100644 drivers/media/i2c/vs6624.c create mode 100644 drivers/media/i2c/vs6624_regs.h create mode 100644 drivers/media/i2c/wm8739.c create mode 100644 drivers/media/i2c/wm8775.c delete mode 100644 drivers/media/video/adp1653.c delete mode 100644 drivers/media/video/adv7170.c delete mode 100644 drivers/media/video/adv7175.c delete mode 100644 drivers/media/video/adv7180.c delete mode 100644 drivers/media/video/adv7183.c delete mode 100644 drivers/media/video/adv7183_regs.h delete mode 100644 drivers/media/video/adv7343.c delete mode 100644 drivers/media/video/adv7343_regs.h delete mode 100644 drivers/media/video/adv7393.c delete mode 100644 drivers/media/video/adv7393_regs.h delete mode 100644 drivers/media/video/ak881x.c delete mode 100644 drivers/media/video/aptina-pll.c delete mode 100644 drivers/media/video/aptina-pll.h delete mode 100644 drivers/media/video/as3645a.c delete mode 100644 drivers/media/video/bt819.c delete mode 100644 drivers/media/video/bt856.c delete mode 100644 drivers/media/video/bt866.c delete mode 100644 drivers/media/video/btcx-risc.c delete mode 100644 drivers/media/video/btcx-risc.h delete mode 100644 drivers/media/video/cs5345.c delete mode 100644 drivers/media/video/cs53l32a.c delete mode 100644 drivers/media/video/cx2341x.c delete mode 100644 drivers/media/video/cx25840/Kconfig delete mode 100644 drivers/media/video/cx25840/Makefile delete mode 100644 drivers/media/video/cx25840/cx25840-audio.c delete mode 100644 drivers/media/video/cx25840/cx25840-core.c delete mode 100644 drivers/media/video/cx25840/cx25840-core.h delete mode 100644 drivers/media/video/cx25840/cx25840-firmware.c delete mode 100644 drivers/media/video/cx25840/cx25840-ir.c delete mode 100644 drivers/media/video/cx25840/cx25840-vbi.c delete mode 100644 drivers/media/video/ir-kbd-i2c.c delete mode 100644 drivers/media/video/ks0127.c delete mode 100644 drivers/media/video/ks0127.h delete mode 100644 drivers/media/video/m52790.c delete mode 100644 drivers/media/video/m5mols/Kconfig delete mode 100644 drivers/media/video/m5mols/Makefile delete mode 100644 drivers/media/video/m5mols/m5mols.h delete mode 100644 drivers/media/video/m5mols/m5mols_capture.c delete mode 100644 drivers/media/video/m5mols/m5mols_controls.c delete mode 100644 drivers/media/video/m5mols/m5mols_core.c delete mode 100644 drivers/media/video/m5mols/m5mols_reg.h delete mode 100644 drivers/media/video/msp3400-driver.c delete mode 100644 drivers/media/video/msp3400-driver.h delete mode 100644 drivers/media/video/msp3400-kthreads.c delete mode 100644 drivers/media/video/mt9m032.c delete mode 100644 drivers/media/video/mt9p031.c delete mode 100644 drivers/media/video/mt9t001.c delete mode 100644 drivers/media/video/mt9v011.c delete mode 100644 drivers/media/video/mt9v032.c delete mode 100644 drivers/media/video/noon010pc30.c delete mode 100644 drivers/media/video/ov7670.c delete mode 100644 drivers/media/video/s5k6aa.c delete mode 100644 drivers/media/video/saa6588.c delete mode 100644 drivers/media/video/saa7110.c delete mode 100644 drivers/media/video/saa7115.c delete mode 100644 drivers/media/video/saa711x_regs.h delete mode 100644 drivers/media/video/saa7127.c delete mode 100644 drivers/media/video/saa717x.c delete mode 100644 drivers/media/video/saa7185.c delete mode 100644 drivers/media/video/saa7191.c delete mode 100644 drivers/media/video/saa7191.h delete mode 100644 drivers/media/video/smiapp-pll.c delete mode 100644 drivers/media/video/smiapp-pll.h delete mode 100644 drivers/media/video/smiapp/Kconfig delete mode 100644 drivers/media/video/smiapp/Makefile delete mode 100644 drivers/media/video/smiapp/smiapp-core.c delete mode 100644 drivers/media/video/smiapp/smiapp-limits.c delete mode 100644 drivers/media/video/smiapp/smiapp-limits.h delete mode 100644 drivers/media/video/smiapp/smiapp-quirk.c delete mode 100644 drivers/media/video/smiapp/smiapp-quirk.h delete mode 100644 drivers/media/video/smiapp/smiapp-reg-defs.h delete mode 100644 drivers/media/video/smiapp/smiapp-reg.h delete mode 100644 drivers/media/video/smiapp/smiapp-regs.c delete mode 100644 drivers/media/video/smiapp/smiapp-regs.h delete mode 100644 drivers/media/video/smiapp/smiapp.h delete mode 100644 drivers/media/video/sr030pc30.c delete mode 100644 drivers/media/video/tcm825x.c delete mode 100644 drivers/media/video/tcm825x.h delete mode 100644 drivers/media/video/tda7432.c delete mode 100644 drivers/media/video/tda9840.c delete mode 100644 drivers/media/video/tea6415c.c delete mode 100644 drivers/media/video/tea6415c.h delete mode 100644 drivers/media/video/tea6420.c delete mode 100644 drivers/media/video/tea6420.h delete mode 100644 drivers/media/video/ths7303.c delete mode 100644 drivers/media/video/tlv320aic23b.c delete mode 100644 drivers/media/video/tvaudio.c delete mode 100644 drivers/media/video/tveeprom.c delete mode 100644 drivers/media/video/tvp514x.c delete mode 100644 drivers/media/video/tvp514x_regs.h delete mode 100644 drivers/media/video/tvp5150.c delete mode 100644 drivers/media/video/tvp5150_reg.h delete mode 100644 drivers/media/video/tvp7002.c delete mode 100644 drivers/media/video/tvp7002_reg.h delete mode 100644 drivers/media/video/upd64031a.c delete mode 100644 drivers/media/video/upd64083.c delete mode 100644 drivers/media/video/vp27smpx.c delete mode 100644 drivers/media/video/vpx3220.c delete mode 100644 drivers/media/video/vs6624.c delete mode 100644 drivers/media/video/vs6624_regs.h delete mode 100644 drivers/media/video/wm8739.c delete mode 100644 drivers/media/video/wm8775.c (limited to 'drivers/media/usb') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index c9cdc61e8b51..26f3de57a971 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -151,18 +151,15 @@ source "drivers/media/rc/Kconfig" source "drivers/media/tuners/Kconfig" +source "drivers/media/i2c/Kconfig" + # -# Video/Radio/Hybrid adapters +# V4L platform/mem2mem drivers # - source "drivers/media/video/Kconfig" source "drivers/media/radio/Kconfig" -# -# DVB adapters -# - source "drivers/media/pci/Kconfig" source "drivers/media/usb/Kconfig" source "drivers/media/mmc/Kconfig" diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 360c44dec3e4..e1be19615861 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -9,7 +9,7 @@ ifeq ($(CONFIG_MEDIA_CONTROLLER),y) endif obj-y += tuners/ common/ rc/ video/ -obj-y += pci/ usb/ mmc/ firewire/ parport/ +obj-y += i2c/ pci/ usb/ mmc/ firewire/ parport/ obj-$(CONFIG_VIDEO_DEV) += radio/ v4l2-core/ obj-$(CONFIG_DVB_CORE) += dvb-core/ dvb-frontends/ diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig new file mode 100644 index 000000000000..1c677f5e3a1a --- /dev/null +++ b/drivers/media/i2c/Kconfig @@ -0,0 +1,566 @@ +# +# Generic video config states +# + +config VIDEO_BTCX + depends on PCI + tristate + +config VIDEO_TVEEPROM + tristate + depends on I2C + +# +# Multimedia Video device configuration +# + +if VIDEO_V4L2 + +config VIDEO_HELPER_CHIPS_AUTO + bool "Autoselect pertinent encoders/decoders and other helper chips" + default y if !EXPERT + ---help--- + Most video cards may require additional modules to encode or + decode audio/video standards. This option will autoselect + all pertinent modules to each selected video module. + + Unselect this only if you know exactly what you are doing, since + it may break support on some boards. + + In doubt, say Y. + +config VIDEO_IR_I2C + tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO + depends on I2C && RC_CORE + default y + ---help--- + Most boards have an IR chip directly connected via GPIO. However, + some video boards have the IR connected via I2C bus. + + If your board doesn't have an I2C IR chip, you may disable this + option. + + In doubt, say Y. + +# +# Encoder / Decoder module configuration +# + +menu "Encoders, decoders, sensors and other helper chips" + visible if !VIDEO_HELPER_CHIPS_AUTO + +comment "Audio decoders, processors and mixers" + +config VIDEO_TVAUDIO + tristate "Simple audio decoder chips" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for several audio decoder chips found on some bt8xx boards: + Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, + tea6320, tea6420, tda8425, ta8874z. + Microchip: pic16c54 based design on ProVideo PV951 board. + + To compile this driver as a module, choose M here: the + module will be called tvaudio. + +config VIDEO_TDA7432 + tristate "Philips TDA7432 audio processor" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for tda7432 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tda7432. + +config VIDEO_TDA9840 + tristate "Philips TDA9840 audio processor" + depends on I2C + ---help--- + Support for tda9840 audio decoder chip found on some Zoran boards. + + To compile this driver as a module, choose M here: the + module will be called tda9840. + +config VIDEO_TEA6415C + tristate "Philips TEA6415C audio processor" + depends on I2C + ---help--- + Support for tea6415c audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6415c. + +config VIDEO_TEA6420 + tristate "Philips TEA6420 audio processor" + depends on I2C + ---help--- + Support for tea6420 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6420. + +config VIDEO_MSP3400 + tristate "Micronas MSP34xx audio decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Micronas MSP34xx series of audio decoders. + + To compile this driver as a module, choose M here: the + module will be called msp3400. + +config VIDEO_CS5345 + tristate "Cirrus Logic CS5345 audio ADC" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Cirrus Logic CS5345 24-bit, 192 kHz + stereo A/D converter. + + To compile this driver as a module, choose M here: the + module will be called cs5345. + +config VIDEO_CS53L32A + tristate "Cirrus Logic CS53L32A audio ADC" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Cirrus Logic CS53L32A low voltage + stereo A/D converter. + + To compile this driver as a module, choose M here: the + module will be called cs53l32a. + +config VIDEO_TLV320AIC23B + tristate "Texas Instruments TLV320AIC23B audio codec" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Texas Instruments TLV320AIC23B audio codec. + + To compile this driver as a module, choose M here: the + module will be called tlv320aic23b. + +config VIDEO_WM8775 + tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Wolfson Microelectronics WM8775 high + performance stereo A/D Converter with a 4 channel input mixer. + + To compile this driver as a module, choose M here: the + module will be called wm8775. + +config VIDEO_WM8739 + tristate "Wolfson Microelectronics WM8739 stereo audio ADC" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Wolfson Microelectronics WM8739 + stereo A/D Converter. + + To compile this driver as a module, choose M here: the + module will be called wm8739. + +config VIDEO_VP27SMPX + tristate "Panasonic VP27s internal MPX" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the internal MPX of the Panasonic VP27s tuner. + + To compile this driver as a module, choose M here: the + module will be called vp27smpx. + +comment "RDS decoders" + +config VIDEO_SAA6588 + tristate "SAA6588 Radio Chip RDS decoder support" + depends on VIDEO_V4L2 && I2C + + help + Support for this Radio Data System (RDS) decoder. This allows + seeing radio station identification transmitted using this + standard. + + To compile this driver as a module, choose M here: the + module will be called saa6588. + +comment "Video decoders" + +config VIDEO_ADV7180 + tristate "Analog Devices ADV7180 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Analog Devices ADV7180 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7180. + +config VIDEO_ADV7183 + tristate "Analog Devices ADV7183 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + V4l2 subdevice driver for the Analog Devices + ADV7183 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7183. + +config VIDEO_BT819 + tristate "BT819A VideoStream decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for BT819A video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt819. + +config VIDEO_BT856 + tristate "BT856 VideoStream decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for BT856 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt856. + +config VIDEO_BT866 + tristate "BT866 VideoStream decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for BT866 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt866. + +config VIDEO_KS0127 + tristate "KS0127 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for KS0127 video decoder. + + This chip is used on AverMedia AVS6EYES Zoran-based MJPEG + cards. + + To compile this driver as a module, choose M here: the + module will be called ks0127. + +config VIDEO_SAA7110 + tristate "Philips SAA7110 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7110 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7110. + +config VIDEO_SAA711X + tristate "Philips SAA7111/3/4/5 video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7111/3/4/5 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7115. + +config VIDEO_SAA7191 + tristate "Philips SAA7191 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7191 video decoder. + + To compile this driver as a module, choose M here: the + module will be called saa7191. + +config VIDEO_TVP514X + tristate "Texas Instruments TVP514x video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 + decoder. It is currently working with the TI OMAP3 camera + controller. + + To compile this driver as a module, choose M here: the + module will be called tvp514x. + +config VIDEO_TVP5150 + tristate "Texas Instruments TVP5150 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Texas Instruments TVP5150 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp5150. + +config VIDEO_TVP7002 + tristate "Texas Instruments TVP7002 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Texas Instruments TVP7002 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp7002. + +config VIDEO_VPX3220 + tristate "vpx3220a, vpx3216b & vpx3214c video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for VPX322x video decoders. + + To compile this driver as a module, choose M here: the + module will be called vpx3220. + +comment "Video and audio decoders" + +config VIDEO_SAA717X + tristate "Philips SAA7171/3/4 audio/video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7171/3/4 audio/video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa717x. + +source "drivers/media/i2c/cx25840/Kconfig" + +comment "MPEG video encoders" + +config VIDEO_CX2341X + tristate "Conexant CX2341x MPEG encoders" + depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON + ---help--- + Support for the Conexant CX23416 MPEG encoders + and CX23415 MPEG encoder/decoders. + + This module currently supports the encoding functions only. + + To compile this driver as a module, choose M here: the + module will be called cx2341x. + +comment "Video encoders" + +config VIDEO_SAA7127 + tristate "Philips SAA7127/9 digital video encoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7127/9 digital video encoders. + + To compile this driver as a module, choose M here: the + module will be called saa7127. + +config VIDEO_SAA7185 + tristate "Philips SAA7185 video encoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7185 video encoder. + + To compile this driver as a module, choose M here: the + module will be called saa7185. + +config VIDEO_ADV7170 + tristate "Analog Devices ADV7170 video encoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Analog Devices ADV7170 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7170. + +config VIDEO_ADV7175 + tristate "Analog Devices ADV7175 video encoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Analog Devices ADV7175 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7175. + +config VIDEO_ADV7343 + tristate "ADV7343 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7343 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7343. + +config VIDEO_ADV7393 + tristate "ADV7393 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7393 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7393. + +config VIDEO_AK881X + tristate "AK8813/AK8814 video encoders" + depends on I2C + help + Video output driver for AKM AK8813 and AK8814 TV encoders + +comment "Camera sensor devices" + +config VIDEO_APTINA_PLL + tristate + +config VIDEO_SMIAPP_PLL + tristate + +config VIDEO_OV7670 + tristate "OmniVision OV7670 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7670 VGA camera. It currently only works with the M88ALP01 + controller. + +config VIDEO_VS6624 + tristate "ST VS6624 sensor support" + depends on VIDEO_V4L2 && I2C + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the ST VS6624 + camera. + + To compile this driver as a module, choose M here: the + module will be called vs6624. + +config VIDEO_MT9M032 + tristate "MT9M032 camera sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select VIDEO_APTINA_PLL + ---help--- + This driver supports MT9M032 camera sensors from Aptina, monochrome + models only. + +config VIDEO_MT9P031 + tristate "Aptina MT9P031 support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select VIDEO_APTINA_PLL + ---help--- + This is a Video4Linux2 sensor-level driver for the Aptina + (Micron) mt9p031 5 Mpixel camera. + +config VIDEO_MT9T001 + tristate "Aptina MT9T001 support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Aptina + (Micron) mt0t001 3 Mpixel camera. + +config VIDEO_MT9V011 + tristate "Micron mt9v011 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Micron + mt0v011 1.3 Mpixel camera. It currently only works with the + em28xx driver. + +config VIDEO_MT9V032 + tristate "Micron MT9V032 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Micron + MT9V032 752x480 CMOS sensor. + +config VIDEO_TCM825X + tristate "TCM825x camera sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a driver for the Toshiba TCM825x VGA camera sensor. + It is used for example in Nokia N800. + +config VIDEO_SR030PC30 + tristate "Siliconfile SR030PC30 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This driver supports SR030PC30 VGA camera from Siliconfile + +config VIDEO_NOON010PC30 + tristate "Siliconfile NOON010PC30 sensor support" + depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This driver supports NOON010PC30 CIF camera from Siliconfile + +source "drivers/media/i2c/m5mols/Kconfig" + +config VIDEO_S5K6AA + tristate "Samsung S5K6AAFX sensor support" + depends on MEDIA_CAMERA_SUPPORT + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M + camera sensor with an embedded SoC image signal processor. + +source "drivers/media/i2c/smiapp/Kconfig" + +comment "Flash devices" + +config VIDEO_ADP1653 + tristate "ADP1653 flash support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a driver for the ADP1653 flash controller. It is used for + example in Nokia N900. + +config VIDEO_AS3645A + tristate "AS3645A flash driver support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a driver for the AS3645A and LM3555 flash controllers. It has + build in control for flash, torch and indicator LEDs. + +comment "Video improvement chips" + +config VIDEO_UPD64031A + tristate "NEC Electronics uPD64031A Ghost Reduction" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the NEC Electronics uPD64031A Ghost Reduction + video chip. It is most often found in NTSC TV cards made for + Japan and is used to reduce the 'ghosting' effect that can + be present in analog TV broadcasts. + + To compile this driver as a module, choose M here: the + module will be called upd64031a. + +config VIDEO_UPD64083 + tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the NEC Electronics uPD64083 3-Dimensional Y/C + separation video chip. It is used to improve the quality of + the colors of a composite signal. + + To compile this driver as a module, choose M here: the + module will be called upd64083. + +comment "Miscelaneous helper chips" + +config VIDEO_THS7303 + tristate "THS7303 Video Amplifier" + depends on I2C + help + Support for TI THS7303 video amplifier + + To compile this driver as a module, choose M here: the + module will be called ths7303. + +config VIDEO_M52790 + tristate "Mitsubishi M52790 A/V switch" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Mitsubishi M52790 A/V switch. + + To compile this driver as a module, choose M here: the + module will be called m52790. + +endmenu +endif diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile new file mode 100644 index 000000000000..93e8c1439596 --- /dev/null +++ b/drivers/media/i2c/Makefile @@ -0,0 +1,63 @@ +msp3400-objs := msp3400-driver.o msp3400-kthreads.o +obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o + +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ +obj-$(CONFIG_VIDEO_CX25840) += cx25840/ +obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ + +obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o +obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o +obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o +obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o +obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o +obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o +obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o +obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o +obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o +obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o +obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o +obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o +obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o +obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o +obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o +obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o +obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o +obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o +obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o +obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o +obj-$(CONFIG_VIDEO_VS6624) += vs6624.o +obj-$(CONFIG_VIDEO_BT819) += bt819.o +obj-$(CONFIG_VIDEO_BT856) += bt856.o +obj-$(CONFIG_VIDEO_BT866) += bt866.o +obj-$(CONFIG_VIDEO_KS0127) += ks0127.o +obj-$(CONFIG_VIDEO_THS7303) += ths7303.o +obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o +obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o +obj-$(CONFIG_VIDEO_CS5345) += cs5345.o +obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o +obj-$(CONFIG_VIDEO_M52790) += m52790.o +obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o +obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_WM8739) += wm8739.o +obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o +obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o +obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o +obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o +obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o +obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o +obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o +obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o +obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o +obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o +obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o +obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o +obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o +obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o +obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o +obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o +obj-$(CONFIG_VIDEO_AK881X) += ak881x.o +obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c new file mode 100644 index 000000000000..18a38b38fcb8 --- /dev/null +++ b/drivers/media/i2c/adp1653.c @@ -0,0 +1,487 @@ +/* + * drivers/media/i2c/adp1653.c + * + * Copyright (C) 2008--2011 Nokia Corporation + * + * Contact: Sakari Ailus + * + * Contributors: + * Sakari Ailus + * Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * TODO: + * - fault interrupt handling + * - hardware strobe + * - power doesn't need to be ON if all lights are off + * + */ + +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_MAX 820000 +#define TIMEOUT_STEP 54600 +#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ + * TIMEOUT_STEP) +#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ + / TIMEOUT_STEP) +#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) + +/* Write values into ADP1653 registers. */ +static int adp1653_update_hw(struct adp1653_flash *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + u8 out_sel; + u8 config = 0; + int rval; + + out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( + flash->indicator_intensity->val) + << ADP1653_REG_OUT_SEL_ILED_SHIFT; + + switch (flash->led_mode->val) { + case V4L2_FLASH_LED_MODE_NONE: + break; + case V4L2_FLASH_LED_MODE_FLASH: + /* Flash mode, light on with strobe, duration from timer */ + config = ADP1653_REG_CONFIG_TMR_CFG; + config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) + << ADP1653_REG_CONFIG_TMR_SET_SHIFT; + break; + case V4L2_FLASH_LED_MODE_TORCH: + /* Torch mode, light immediately on, duration indefinite */ + out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( + flash->torch_intensity->val) + << ADP1653_REG_OUT_SEL_HPLED_SHIFT; + break; + } + + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); + if (rval < 0) + return rval; + + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); + if (rval < 0) + return rval; + + return 0; +} + +static int adp1653_get_fault(struct adp1653_flash *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int fault; + int rval; + + fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); + if (IS_ERR_VALUE(fault)) + return fault; + + flash->fault |= fault; + + if (!flash->fault) + return 0; + + /* Clear faults. */ + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); + if (IS_ERR_VALUE(rval)) + return rval; + + flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; + + rval = adp1653_update_hw(flash); + if (IS_ERR_VALUE(rval)) + return rval; + + return flash->fault; +} + +static int adp1653_strobe(struct adp1653_flash *flash, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( + flash->indicator_intensity->val) + << ADP1653_REG_OUT_SEL_ILED_SHIFT; + int rval; + + if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) + return -EBUSY; + + if (!enable) + return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, + out_sel); + + out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( + flash->flash_intensity->val) + << ADP1653_REG_OUT_SEL_HPLED_SHIFT; + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); + if (rval) + return rval; + + /* Software strobe using i2c */ + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, + ADP1653_REG_SW_STROBE_SW_STROBE); + if (rval) + return rval; + return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); +} + +/* -------------------------------------------------------------------------- + * V4L2 controls + */ + +static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct adp1653_flash *flash = + container_of(ctrl->handler, struct adp1653_flash, ctrls); + int rval; + + rval = adp1653_get_fault(flash); + if (IS_ERR_VALUE(rval)) + return rval; + + ctrl->cur.val = 0; + + if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) + ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; + if (flash->fault & ADP1653_REG_FAULT_FLT_OT) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; + if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) + ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; + if (flash->fault & ADP1653_REG_FAULT_FLT_OV) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; + + flash->fault = 0; + + return 0; +} + +static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct adp1653_flash *flash = + container_of(ctrl->handler, struct adp1653_flash, ctrls); + int rval; + + rval = adp1653_get_fault(flash); + if (IS_ERR_VALUE(rval)) + return rval; + if ((rval & (ADP1653_REG_FAULT_FLT_SCP | + ADP1653_REG_FAULT_FLT_OT | + ADP1653_REG_FAULT_FLT_OV)) && + (ctrl->id == V4L2_CID_FLASH_STROBE || + ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || + ctrl->id == V4L2_CID_FLASH_LED_MODE)) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_FLASH_STROBE: + return adp1653_strobe(flash, 1); + case V4L2_CID_FLASH_STROBE_STOP: + return adp1653_strobe(flash, 0); + } + + return adp1653_update_hw(flash); +} + +static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { + .g_volatile_ctrl = adp1653_get_ctrl, + .s_ctrl = adp1653_set_ctrl, +}; + +static int adp1653_init_controls(struct adp1653_flash *flash) +{ + struct v4l2_ctrl *fault; + + v4l2_ctrl_handler_init(&flash->ctrls, 9); + + flash->led_mode = + v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_LED_MODE, + V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); + v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_STROBE_SOURCE, + V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); + flash->flash_timeout = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, + flash->platform_data->max_flash_timeout, + TIMEOUT_STEP, + flash->platform_data->max_flash_timeout); + flash->flash_intensity = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_INTENSITY, + ADP1653_FLASH_INTENSITY_MIN, + flash->platform_data->max_flash_intensity, + 1, flash->platform_data->max_flash_intensity); + flash->torch_intensity = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_TORCH_INTENSITY, + ADP1653_TORCH_INTENSITY_MIN, + flash->platform_data->max_torch_intensity, + ADP1653_FLASH_INTENSITY_STEP, + flash->platform_data->max_torch_intensity); + flash->indicator_intensity = + v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_INDICATOR_INTENSITY, + ADP1653_INDICATOR_INTENSITY_MIN, + flash->platform_data->max_indicator_intensity, + ADP1653_INDICATOR_INTENSITY_STEP, + ADP1653_INDICATOR_INTENSITY_MIN); + fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, + V4L2_CID_FLASH_FAULT, 0, + V4L2_FLASH_FAULT_OVER_VOLTAGE + | V4L2_FLASH_FAULT_OVER_TEMPERATURE + | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); + + if (flash->ctrls.error) + return flash->ctrls.error; + + fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + + flash->subdev.ctrl_handler = &flash->ctrls; + return 0; +} + +/* -------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static int +adp1653_init_device(struct adp1653_flash *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + /* Clear FAULT register by writing zero to OUT_SEL */ + rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); + if (rval < 0) { + dev_err(&client->dev, "failed writing fault register\n"); + return -EIO; + } + + mutex_lock(flash->ctrls.lock); + /* Reset faults before reading new ones. */ + flash->fault = 0; + rval = adp1653_get_fault(flash); + mutex_unlock(flash->ctrls.lock); + if (rval > 0) { + dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); + return -EIO; + } + + mutex_lock(flash->ctrls.lock); + rval = adp1653_update_hw(flash); + mutex_unlock(flash->ctrls.lock); + if (rval) { + dev_err(&client->dev, + "adp1653_update_hw failed at %s\n", __func__); + return -EIO; + } + + return 0; +} + +static int +__adp1653_set_power(struct adp1653_flash *flash, int on) +{ + int ret; + + ret = flash->platform_data->power(&flash->subdev, on); + if (ret < 0) + return ret; + + if (!on) + return 0; + + ret = adp1653_init_device(flash); + if (ret < 0) + flash->platform_data->power(&flash->subdev, 0); + + return ret; +} + +static int +adp1653_set_power(struct v4l2_subdev *subdev, int on) +{ + struct adp1653_flash *flash = to_adp1653_flash(subdev); + int ret = 0; + + mutex_lock(&flash->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (flash->power_count == !on) { + ret = __adp1653_set_power(flash, !!on); + if (ret < 0) + goto done; + } + + /* Update the power count. */ + flash->power_count += on ? 1 : -1; + WARN_ON(flash->power_count < 0); + +done: + mutex_unlock(&flash->power_lock); + return ret; +} + +static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return adp1653_set_power(sd, 1); +} + +static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return adp1653_set_power(sd, 0); +} + +static const struct v4l2_subdev_core_ops adp1653_core_ops = { + .s_power = adp1653_set_power, +}; + +static const struct v4l2_subdev_ops adp1653_ops = { + .core = &adp1653_core_ops, +}; + +static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { + .open = adp1653_open, + .close = adp1653_close, +}; + +/* -------------------------------------------------------------------------- + * I2C driver + */ +#ifdef CONFIG_PM + +static int adp1653_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct adp1653_flash *flash = to_adp1653_flash(subdev); + + if (!flash->power_count) + return 0; + + return __adp1653_set_power(flash, 0); +} + +static int adp1653_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct adp1653_flash *flash = to_adp1653_flash(subdev); + + if (!flash->power_count) + return 0; + + return __adp1653_set_power(flash, 1); +} + +#else + +#define adp1653_suspend NULL +#define adp1653_resume NULL + +#endif /* CONFIG_PM */ + +static int adp1653_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct adp1653_flash *flash; + int ret; + + /* we couldn't work without platform data */ + if (client->dev.platform_data == NULL) + return -ENODEV; + + flash = kzalloc(sizeof(*flash), GFP_KERNEL); + if (flash == NULL) + return -ENOMEM; + + flash->platform_data = client->dev.platform_data; + + mutex_init(&flash->power_lock); + + v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); + flash->subdev.internal_ops = &adp1653_internal_ops; + flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = adp1653_init_controls(flash); + if (ret) + goto free_and_quit; + + ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + if (ret < 0) + goto free_and_quit; + + flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + + return 0; + +free_and_quit: + v4l2_ctrl_handler_free(&flash->ctrls); + kfree(flash); + return ret; +} + +static int __exit adp1653_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct adp1653_flash *flash = to_adp1653_flash(subdev); + + v4l2_device_unregister_subdev(&flash->subdev); + v4l2_ctrl_handler_free(&flash->ctrls); + media_entity_cleanup(&flash->subdev.entity); + kfree(flash); + return 0; +} + +static const struct i2c_device_id adp1653_id_table[] = { + { ADP1653_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp1653_id_table); + +static struct dev_pm_ops adp1653_pm_ops = { + .suspend = adp1653_suspend, + .resume = adp1653_resume, +}; + +static struct i2c_driver adp1653_i2c_driver = { + .driver = { + .name = ADP1653_NAME, + .pm = &adp1653_pm_ops, + }, + .probe = adp1653_probe, + .remove = __exit_p(adp1653_remove), + .id_table = adp1653_id_table, +}; + +module_i2c_driver(adp1653_i2c_driver); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c new file mode 100644 index 000000000000..6bc01fb98ff8 --- /dev/null +++ b/drivers/media/i2c/adv7170.c @@ -0,0 +1,410 @@ +/* + * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1 + * + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Based on adv7176 driver by: + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * This program is free software; 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 + +MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); +MODULE_AUTHOR("Maxim Yevtyushkin"); +MODULE_LICENSE("GPL"); + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* ----------------------------------------------------------------------- */ + +struct adv7170 { + struct v4l2_subdev sd; + unsigned char reg[128]; + + v4l2_std_id norm; + int input; +}; + +static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7170, sd); +} + +static char *inputs[] = { "pass_through", "play_back" }; + +static enum v4l2_mbus_pixelcode adv7170_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_UYVY8_1X16, +}; + +/* ----------------------------------------------------------------------- */ + +static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7170 *encoder = to_adv7170(sd); + + encoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int adv7170_write_block(struct v4l2_subdev *sd, + const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7170 *encoder = to_adv7170(sd); + int ret = -1; + u8 reg; + + /* the adv7170 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + encoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = adv7170_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + return ret; +} + +/* ----------------------------------------------------------------------- */ + +#define TR0MODE 0x4c +#define TR0RST 0x80 + +#define TR1CAPT 0x00 +#define TR1PLAY 0x00 + +static const unsigned char init_NTSC[] = { + 0x00, 0x10, /* MR0 */ + 0x01, 0x20, /* MR1 */ + 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ + 0x03, 0x80, /* MR3 */ + 0x04, 0x30, /* MR4 */ + 0x05, 0x00, /* Reserved */ + 0x06, 0x00, /* Reserved */ + 0x07, TR0MODE, /* TM0 */ + 0x08, TR1CAPT, /* TM1 */ + 0x09, 0x16, /* Fsc0 */ + 0x0a, 0x7c, /* Fsc1 */ + 0x0b, 0xf0, /* Fsc2 */ + 0x0c, 0x21, /* Fsc3 */ + 0x0d, 0x00, /* Subcarrier Phase */ + 0x0e, 0x00, /* Closed Capt. Ext 0 */ + 0x0f, 0x00, /* Closed Capt. Ext 1 */ + 0x10, 0x00, /* Closed Capt. 0 */ + 0x11, 0x00, /* Closed Capt. 1 */ + 0x12, 0x00, /* Pedestal Ctl 0 */ + 0x13, 0x00, /* Pedestal Ctl 1 */ + 0x14, 0x00, /* Pedestal Ctl 2 */ + 0x15, 0x00, /* Pedestal Ctl 3 */ + 0x16, 0x00, /* CGMS_WSS_0 */ + 0x17, 0x00, /* CGMS_WSS_1 */ + 0x18, 0x00, /* CGMS_WSS_2 */ + 0x19, 0x00, /* Teletext Ctl */ +}; + +static const unsigned char init_PAL[] = { + 0x00, 0x71, /* MR0 */ + 0x01, 0x20, /* MR1 */ + 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ + 0x03, 0x80, /* MR3 */ + 0x04, 0x30, /* MR4 */ + 0x05, 0x00, /* Reserved */ + 0x06, 0x00, /* Reserved */ + 0x07, TR0MODE, /* TM0 */ + 0x08, TR1CAPT, /* TM1 */ + 0x09, 0xcb, /* Fsc0 */ + 0x0a, 0x8a, /* Fsc1 */ + 0x0b, 0x09, /* Fsc2 */ + 0x0c, 0x2a, /* Fsc3 */ + 0x0d, 0x00, /* Subcarrier Phase */ + 0x0e, 0x00, /* Closed Capt. Ext 0 */ + 0x0f, 0x00, /* Closed Capt. Ext 1 */ + 0x10, 0x00, /* Closed Capt. 0 */ + 0x11, 0x00, /* Closed Capt. 1 */ + 0x12, 0x00, /* Pedestal Ctl 0 */ + 0x13, 0x00, /* Pedestal Ctl 1 */ + 0x14, 0x00, /* Pedestal Ctl 2 */ + 0x15, 0x00, /* Pedestal Ctl 3 */ + 0x16, 0x00, /* CGMS_WSS_0 */ + 0x17, 0x00, /* CGMS_WSS_1 */ + 0x18, 0x00, /* CGMS_WSS_2 */ + 0x19, 0x00, /* Teletext Ctl */ +}; + + +static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7170 *encoder = to_adv7170(sd); + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (std & V4L2_STD_NTSC) { + adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); + if (encoder->input == 0) + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_PAL) { + adv7170_write_block(sd, init_PAL, sizeof(init_PAL)); + if (encoder->input == 0) + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + } else { + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); + encoder->norm = std; + return 0; +} + +static int adv7170_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7170 *encoder = to_adv7170(sd); + + /* RJ: input = 0: input is from decoder + input = 1: input is from ZR36060 + input = 2: color bar */ + + v4l2_dbg(1, debug, sd, "set input from %s\n", + input == 0 ? "decoder" : "ZR36060"); + + switch (input) { + case 0: + adv7170_write(sd, 0x01, 0x20); + adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + case 1: + adv7170_write(sd, 0x01, 0x00); + adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */ + adv7170_write(sd, 0x02, 0x08); + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + default: + v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); + encoder->input = input; + return 0; +} + +static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(adv7170_codes)) + return -EINVAL; + + *code = adv7170_codes[index]; + return 0; +} + +static int adv7170_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7170_read(sd, 0x7); + + if ((val & 0x40) == (1 << 6)) + mf->code = V4L2_MBUS_FMT_UYVY8_1X16; + else + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->width = 0; + mf->height = 0; + mf->field = V4L2_FIELD_ANY; + + return 0; +} + +static int adv7170_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7170_read(sd, 0x7); + int ret; + + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + val &= ~0x40; + break; + + case V4L2_MBUS_FMT_UYVY8_1X16: + val |= 0x40; + break; + + default: + v4l2_dbg(1, debug, sd, + "illegal v4l2_mbus_framefmt code: %d\n", mf->code); + return -EINVAL; + } + + ret = adv7170_write(sd, 0x7, val); + + return ret; +} + +static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops adv7170_core_ops = { + .g_chip_ident = adv7170_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops adv7170_video_ops = { + .s_std_output = adv7170_s_std_output, + .s_routing = adv7170_s_routing, + .s_mbus_fmt = adv7170_s_fmt, + .g_mbus_fmt = adv7170_g_fmt, + .enum_mbus_fmt = adv7170_enum_fmt, +}; + +static const struct v4l2_subdev_ops adv7170_ops = { + .core = &adv7170_core_ops, + .video = &adv7170_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int adv7170_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7170 *encoder; + struct v4l2_subdev *sd; + int i; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7170_ops); + encoder->norm = V4L2_STD_NTSC; + encoder->input = 0; + + i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); + if (i >= 0) { + i = adv7170_write(sd, 0x07, TR0MODE | TR0RST); + i = adv7170_write(sd, 0x07, TR0MODE); + i = adv7170_read(sd, 0x12); + v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); + } + if (i < 0) + v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); + return 0; +} + +static int adv7170_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_adv7170(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id adv7170_id[] = { + { "adv7170", 0 }, + { "adv7171", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7170_id); + +static struct i2c_driver adv7170_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7170", + }, + .probe = adv7170_probe, + .remove = adv7170_remove, + .id_table = adv7170_id, +}; + +module_i2c_driver(adv7170_driver); diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c new file mode 100644 index 000000000000..c7640fab5730 --- /dev/null +++ b/drivers/media/i2c/adv7175.c @@ -0,0 +1,460 @@ +/* + * adv7175 - adv7175a video encoder driver version 0.0.3 + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * This program is free software; 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 + +MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); +MODULE_AUTHOR("Dave Perks"); +MODULE_LICENSE("GPL"); + +#define I2C_ADV7175 0xd4 +#define I2C_ADV7176 0x54 + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* ----------------------------------------------------------------------- */ + +struct adv7175 { + struct v4l2_subdev sd; + v4l2_std_id norm; + int input; +}; + +static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7175, sd); +} + +static char *inputs[] = { "pass_through", "play_back", "color_bar" }; + +static enum v4l2_mbus_pixelcode adv7175_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_UYVY8_1X16, +}; + +/* ----------------------------------------------------------------------- */ + +static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int adv7175_write_block(struct v4l2_subdev *sd, + const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = -1; + u8 reg; + + /* the adv7175 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = data[1]; + reg++; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = adv7175_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + + return ret; +} + +static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through) +{ + /* for some reason pass_through NTSC needs + * a different sub-carrier freq to remain stable. */ + if (pass_through) + adv7175_write(sd, 0x02, 0x00); + else + adv7175_write(sd, 0x02, 0x55); + + adv7175_write(sd, 0x03, 0x55); + adv7175_write(sd, 0x04, 0x55); + adv7175_write(sd, 0x05, 0x25); +} + +/* ----------------------------------------------------------------------- */ +/* Output filter: S-Video Composite */ + +#define MR050 0x11 /* 0x09 */ +#define MR060 0x14 /* 0x0c */ + +/* ----------------------------------------------------------------------- */ + +#define TR0MODE 0x46 +#define TR0RST 0x80 + +#define TR1CAPT 0x80 +#define TR1PLAY 0x00 + +static const unsigned char init_common[] = { + + 0x00, MR050, /* MR0, PAL enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x0c, /* subc. freq. */ + 0x03, 0x8c, /* subc. freq. */ + 0x04, 0x79, /* subc. freq. */ + 0x05, 0x26, /* subc. freq. */ + 0x06, 0x40, /* subc. phase */ + + 0x07, TR0MODE, /* TR0, 16bit */ + 0x08, 0x21, /* */ + 0x09, 0x00, /* */ + 0x0a, 0x00, /* */ + 0x0b, 0x00, /* */ + 0x0c, TR1CAPT, /* TR1 */ + 0x0d, 0x4f, /* MR2 */ + 0x0e, 0x00, /* */ + 0x0f, 0x00, /* */ + 0x10, 0x00, /* */ + 0x11, 0x00, /* */ +}; + +static const unsigned char init_pal[] = { + 0x00, MR050, /* MR0, PAL enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x0c, /* subc. freq. */ + 0x03, 0x8c, /* subc. freq. */ + 0x04, 0x79, /* subc. freq. */ + 0x05, 0x26, /* subc. freq. */ + 0x06, 0x40, /* subc. phase */ +}; + +static const unsigned char init_ntsc[] = { + 0x00, MR060, /* MR0, NTSC enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x55, /* subc. freq. */ + 0x03, 0x55, /* subc. freq. */ + 0x04, 0x55, /* subc. freq. */ + 0x05, 0x25, /* subc. freq. */ + 0x06, 0x1a, /* subc. phase */ +}; + +static int adv7175_init(struct v4l2_subdev *sd, u32 val) +{ + /* This is just for testing!!! */ + adv7175_write_block(sd, init_common, sizeof(init_common)); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + return 0; +} + +static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7175 *encoder = to_adv7175(sd); + + if (std & V4L2_STD_NTSC) { + adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_PAL) { + adv7175_write_block(sd, init_pal, sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_SECAM) { + /* This is an attempt to convert + * SECAM->PAL (typically it does not work + * due to genlock: when decoder is in SECAM + * and encoder in in PAL the subcarrier can + * not be syncronized with horizontal + * quency) */ + adv7175_write_block(sd, init_pal, sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else { + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); + encoder->norm = std; + return 0; +} + +static int adv7175_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7175 *encoder = to_adv7175(sd); + + /* RJ: input = 0: input is from decoder + input = 1: input is from ZR36060 + input = 2: color bar */ + + switch (input) { + case 0: + adv7175_write(sd, 0x01, 0x00); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 1); + + adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */ + if (encoder->norm & V4L2_STD_SECAM) + adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ + else + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /*udelay(10);*/ + break; + + case 1: + adv7175_write(sd, 0x01, 0x00); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 0); + + adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */ + adv7175_write(sd, 0x0d, 0x49); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + case 2: + adv7175_write(sd, 0x01, 0x80); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 0); + + adv7175_write(sd, 0x0d, 0x49); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /* udelay(10); */ + break; + + default: + v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); + encoder->input = input; + return 0; +} + +static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(adv7175_codes)) + return -EINVAL; + + *code = adv7175_codes[index]; + return 0; +} + +static int adv7175_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7175_read(sd, 0x7); + + if ((val & 0x40) == (1 << 6)) + mf->code = V4L2_MBUS_FMT_UYVY8_1X16; + else + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->width = 0; + mf->height = 0; + mf->field = V4L2_FIELD_ANY; + + return 0; +} + +static int adv7175_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7175_read(sd, 0x7); + int ret; + + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + val &= ~0x40; + break; + + case V4L2_MBUS_FMT_UYVY8_1X16: + val |= 0x40; + break; + + default: + v4l2_dbg(1, debug, sd, + "illegal v4l2_mbus_framefmt code: %d\n", mf->code); + return -EINVAL; + } + + ret = adv7175_write(sd, 0x7, val); + + return ret; +} + +static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); +} + +static int adv7175_s_power(struct v4l2_subdev *sd, int on) +{ + if (on) + adv7175_write(sd, 0x01, 0x00); + else + adv7175_write(sd, 0x01, 0x78); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops adv7175_core_ops = { + .g_chip_ident = adv7175_g_chip_ident, + .init = adv7175_init, + .s_power = adv7175_s_power, +}; + +static const struct v4l2_subdev_video_ops adv7175_video_ops = { + .s_std_output = adv7175_s_std_output, + .s_routing = adv7175_s_routing, + .s_mbus_fmt = adv7175_s_fmt, + .g_mbus_fmt = adv7175_g_fmt, + .enum_mbus_fmt = adv7175_enum_fmt, +}; + +static const struct v4l2_subdev_ops adv7175_ops = { + .core = &adv7175_core_ops, + .video = &adv7175_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int adv7175_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i; + struct adv7175 *encoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7175_ops); + encoder->norm = V4L2_STD_NTSC; + encoder->input = 0; + + i = adv7175_write_block(sd, init_common, sizeof(init_common)); + if (i >= 0) { + i = adv7175_write(sd, 0x07, TR0MODE | TR0RST); + i = adv7175_write(sd, 0x07, TR0MODE); + i = adv7175_read(sd, 0x12); + v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); + } + if (i < 0) + v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); + return 0; +} + +static int adv7175_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_adv7175(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id adv7175_id[] = { + { "adv7175", 0 }, + { "adv7176", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7175_id); + +static struct i2c_driver adv7175_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7175", + }, + .probe = adv7175_probe, + .remove = adv7175_remove, + .id_table = adv7175_id, +}; + +module_i2c_driver(adv7175_driver); diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c new file mode 100644 index 000000000000..45ecf8db1eae --- /dev/null +++ b/drivers/media/i2c/adv7180.c @@ -0,0 +1,667 @@ +/* + * adv7180.c Analog Devices ADV7180 video decoder driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADV7180_INPUT_CONTROL_REG 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 +#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 +#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 +#define ADV7180_INPUT_CONTROL_PAL60 0x60 +#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 +#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 +#define ADV7180_INPUT_CONTROL_PAL_N 0x90 +#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 +#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 +#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f + +#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 +#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 + +#define ADV7180_AUTODETECT_ENABLE_REG 0x07 +#define ADV7180_AUTODETECT_DEFAULT 0x7f +/* Contrast */ +#define ADV7180_CON_REG 0x08 /*Unsigned */ +#define ADV7180_CON_MIN 0 +#define ADV7180_CON_DEF 128 +#define ADV7180_CON_MAX 255 +/* Brightness*/ +#define ADV7180_BRI_REG 0x0a /*Signed */ +#define ADV7180_BRI_MIN -128 +#define ADV7180_BRI_DEF 0 +#define ADV7180_BRI_MAX 127 +/* Hue */ +#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ +#define ADV7180_HUE_MIN -127 +#define ADV7180_HUE_DEF 0 +#define ADV7180_HUE_MAX 128 + +#define ADV7180_ADI_CTRL_REG 0x0e +#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 + +#define ADV7180_PWR_MAN_REG 0x0f +#define ADV7180_PWR_MAN_ON 0x04 +#define ADV7180_PWR_MAN_OFF 0x24 +#define ADV7180_PWR_MAN_RES 0x80 + +#define ADV7180_STATUS1_REG 0x10 +#define ADV7180_STATUS1_IN_LOCK 0x01 +#define ADV7180_STATUS1_AUTOD_MASK 0x70 +#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 +#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 +#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 +#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 +#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 +#define ADV7180_STATUS1_AUTOD_SECAM 0x50 +#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 +#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 + +#define ADV7180_IDENT_REG 0x11 +#define ADV7180_ID_7180 0x18 + +#define ADV7180_ICONF1_ADI 0x40 +#define ADV7180_ICONF1_ACTIVE_LOW 0x01 +#define ADV7180_ICONF1_PSYNC_ONLY 0x10 +#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 +/* Saturation */ +#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ +#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ +#define ADV7180_SAT_MIN 0 +#define ADV7180_SAT_DEF 128 +#define ADV7180_SAT_MAX 255 + +#define ADV7180_IRQ1_LOCK 0x01 +#define ADV7180_IRQ1_UNLOCK 0x02 +#define ADV7180_ISR1_ADI 0x42 +#define ADV7180_ICR1_ADI 0x43 +#define ADV7180_IMR1_ADI 0x44 +#define ADV7180_IMR2_ADI 0x48 +#define ADV7180_IRQ3_AD_CHANGE 0x08 +#define ADV7180_ISR3_ADI 0x4A +#define ADV7180_ICR3_ADI 0x4B +#define ADV7180_IMR3_ADI 0x4C +#define ADV7180_IMR4_ADI 0x50 + +#define ADV7180_NTSC_V_BIT_END_REG 0xE6 +#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F + +struct adv7180_state { + struct v4l2_ctrl_handler ctrl_hdl; + struct v4l2_subdev sd; + struct work_struct work; + struct mutex mutex; /* mutual excl. when accessing chip */ + int irq; + v4l2_std_id curr_norm; + bool autodetect; + u8 input; +}; +#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ + struct adv7180_state, \ + ctrl_hdl)->sd) + +static v4l2_std_id adv7180_std_to_v4l2(u8 status1) +{ + switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { + case ADV7180_STATUS1_AUTOD_NTSM_M_J: + return V4L2_STD_NTSC; + case ADV7180_STATUS1_AUTOD_NTSC_4_43: + return V4L2_STD_NTSC_443; + case ADV7180_STATUS1_AUTOD_PAL_M: + return V4L2_STD_PAL_M; + case ADV7180_STATUS1_AUTOD_PAL_60: + return V4L2_STD_PAL_60; + case ADV7180_STATUS1_AUTOD_PAL_B_G: + return V4L2_STD_PAL; + case ADV7180_STATUS1_AUTOD_SECAM: + return V4L2_STD_SECAM; + case ADV7180_STATUS1_AUTOD_PAL_COMB: + return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; + case ADV7180_STATUS1_AUTOD_SECAM_525: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static int v4l2_std_to_adv7180(v4l2_std_id std) +{ + if (std == V4L2_STD_PAL_60) + return ADV7180_INPUT_CONTROL_PAL60; + if (std == V4L2_STD_NTSC_443) + return ADV7180_INPUT_CONTROL_NTSC_443; + if (std == V4L2_STD_PAL_N) + return ADV7180_INPUT_CONTROL_PAL_N; + if (std == V4L2_STD_PAL_M) + return ADV7180_INPUT_CONTROL_PAL_M; + if (std == V4L2_STD_PAL_Nc) + return ADV7180_INPUT_CONTROL_PAL_COMB_N; + + if (std & V4L2_STD_PAL) + return ADV7180_INPUT_CONTROL_PAL_BG; + if (std & V4L2_STD_NTSC) + return ADV7180_INPUT_CONTROL_NTSC_M; + if (std & V4L2_STD_SECAM) + return ADV7180_INPUT_CONTROL_PAL_SECAM; + + return -EINVAL; +} + +static u32 adv7180_status_to_v4l2(u8 status1) +{ + if (!(status1 & ADV7180_STATUS1_IN_LOCK)) + return V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int __adv7180_status(struct i2c_client *client, u32 *status, + v4l2_std_id *std) +{ + int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); + + if (status1 < 0) + return status1; + + if (status) + *status = adv7180_status_to_v4l2(status1); + if (std) + *std = adv7180_std_to_v4l2(status1); + + return 0; +} + +static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7180_state, sd); +} + +static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7180_state *state = to_state(sd); + int err = mutex_lock_interruptible(&state->mutex); + if (err) + return err; + + /* when we are interrupt driven we know the state */ + if (!state->autodetect || state->irq > 0) + *std = state->curr_norm; + else + err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); + + mutex_unlock(&state->mutex); + return err; +} + +static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (ret) + return ret; + + /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept + * all inputs and let the card driver take care of validation + */ + if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) + goto out; + + ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); + + if (ret < 0) + goto out; + + ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret | input); + state->input = input; +out: + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); +} + +static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + /* all standards -> autodetect */ + if (std == V4L2_STD_ALL) { + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); + if (ret < 0) + goto out; + + __adv7180_status(client, NULL, &state->curr_norm); + state->autodetect = true; + } else { + ret = v4l2_std_to_adv7180(std); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, + ret | state->input); + if (ret < 0) + goto out; + + state->curr_norm = std; + state->autodetect = false; + } + ret = 0; +out: + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_adv7180_sd(ctrl); + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + int val; + + if (ret) + return ret; + val = ctrl->val; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); + break; + case V4L2_CID_HUE: + /*Hue is inverted according to HSL chart */ + ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val); + break; + case V4L2_CID_CONTRAST: + ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); + break; + case V4L2_CID_SATURATION: + /* + *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE + *Let's not confuse the user, everybody understands saturation + */ + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, + val); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, + val); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&state->mutex); + return ret; +} + +static const struct v4l2_ctrl_ops adv7180_ctrl_ops = { + .s_ctrl = adv7180_s_ctrl, +}; + +static int adv7180_init_controls(struct adv7180_state *state) +{ + v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); + + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, + ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_CONTRAST, ADV7180_CON_MIN, + ADV7180_CON_MAX, 1, ADV7180_CON_DEF); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_SATURATION, ADV7180_SAT_MIN, + ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, + V4L2_CID_HUE, ADV7180_HUE_MIN, + ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); + state->sd.ctrl_handler = &state->ctrl_hdl; + if (state->ctrl_hdl.error) { + int err = state->ctrl_hdl.error; + + v4l2_ctrl_handler_free(&state->ctrl_hdl); + return err; + } + v4l2_ctrl_handler_setup(&state->ctrl_hdl); + + return 0; +} +static void adv7180_exit_controls(struct adv7180_state *state) +{ + v4l2_ctrl_handler_free(&state->ctrl_hdl); +} + +static const struct v4l2_subdev_video_ops adv7180_video_ops = { + .querystd = adv7180_querystd, + .g_input_status = adv7180_g_input_status, + .s_routing = adv7180_s_routing, +}; + +static const struct v4l2_subdev_core_ops adv7180_core_ops = { + .g_chip_ident = adv7180_g_chip_ident, + .s_std = adv7180_s_std, + .queryctrl = v4l2_subdev_queryctrl, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, +}; + +static const struct v4l2_subdev_ops adv7180_ops = { + .core = &adv7180_core_ops, + .video = &adv7180_video_ops, +}; + +static void adv7180_work(struct work_struct *work) +{ + struct adv7180_state *state = container_of(work, struct adv7180_state, + work); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + u8 isr3; + + mutex_lock(&state->mutex); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); + /* clear */ + i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); + + if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) + __adv7180_status(client, NULL, &state->curr_norm); + mutex_unlock(&state->mutex); + + enable_irq(state->irq); +} + +static irqreturn_t adv7180_irq(int irq, void *devid) +{ + struct adv7180_state *state = devid; + + schedule_work(&state->work); + + disable_irq_nosync(state->irq); + + return IRQ_HANDLED; +} + +static int init_device(struct i2c_client *client, struct adv7180_state *state) +{ + int ret; + + /* Initialize adv7180 */ + /* Enable autodetection */ + if (state->autodetect) { + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); + if (ret < 0) + return ret; + + ret = + i2c_smbus_write_byte_data(client, + ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + return ret; + } else { + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ret | state->input); + if (ret < 0) + return ret; + + } + /* ITU-R BT.656-4 compatible */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + if (ret < 0) + return ret; + + /* Manually set V bit end position in NTSC mode */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_NTSC_V_BIT_END_REG, + ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); + if (ret < 0) + return ret; + + /* read current norm */ + __adv7180_status(client, NULL, &state->curr_norm); + + /* register for interrupts */ + if (state->irq > 0) { + ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME, + state); + if (ret) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + if (ret < 0) + return ret; + + /* config the Interrupt pin to be active low */ + ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, + ADV7180_ICONF1_ACTIVE_LOW | + ADV7180_ICONF1_PSYNC_ONLY); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); + if (ret < 0) + return ret; + + /* enable AD change interrupts interrupts */ + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, + ADV7180_IRQ3_AD_CHANGE); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static __devinit int adv7180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7180_state *state; + struct v4l2_subdev *sd; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr, client->adapter->name); + + state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + state->irq = client->irq; + INIT_WORK(&state->work, adv7180_work); + mutex_init(&state->mutex); + state->autodetect = true; + state->input = 0; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + + ret = adv7180_init_controls(state); + if (ret) + goto err_unreg_subdev; + ret = init_device(client, state); + if (ret) + goto err_free_ctrl; + return 0; + +err_free_ctrl: + adv7180_exit_controls(state); +err_unreg_subdev: + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(state); +err: + printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret); + return ret; +} + +static __devexit int adv7180_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + + if (state->irq > 0) { + free_irq(client->irq, state); + if (cancel_work_sync(&state->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(state->irq); + } + } + + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id adv7180_id[] = { + {KBUILD_MODNAME, 0}, + {}, +}; + +#ifdef CONFIG_PM +static int adv7180_suspend(struct i2c_client *client, pm_message_t state) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_OFF); + if (ret < 0) + return ret; + return 0; +} + +static int adv7180_resume(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_ON); + if (ret < 0) + return ret; + ret = init_device(client, state); + if (ret < 0) + return ret; + return 0; +} +#endif + +MODULE_DEVICE_TABLE(i2c, adv7180_id); + +static struct i2c_driver adv7180_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + }, + .probe = adv7180_probe, + .remove = __devexit_p(adv7180_remove), +#ifdef CONFIG_PM + .suspend = adv7180_suspend, + .resume = adv7180_resume, +#endif + .id_table = adv7180_id, +}; + +module_i2c_driver(adv7180_driver); + +MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c new file mode 100644 index 000000000000..e1d4c89d7140 --- /dev/null +++ b/drivers/media/i2c/adv7183.c @@ -0,0 +1,699 @@ +/* + * adv7183.c Analog Devices ADV7183 video decoder driver + * + * Copyright (c) 2011 Analog Devices 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. + * + * 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 + +#include +#include +#include +#include + +#include "adv7183_regs.h" + +struct adv7183 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + v4l2_std_id std; /* Current set standard */ + u32 input; + u32 output; + unsigned reset_pin; + unsigned oe_pin; + struct v4l2_mbus_framefmt fmt; +}; + +/* EXAMPLES USING 27 MHz CLOCK + * Mode 1 CVBS Input (Composite Video on AIN5) + * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8. + */ +static const unsigned char adv7183_init_regs[] = { + ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */ + ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */ + ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */ + ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */ + ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */ + /* ADI recommended programming sequence */ + ADV7183_ADI_CTRL, 0x80, + ADV7183_CTI_DNR_CTRL_4, 0x20, + 0x52, 0x18, + 0x58, 0xED, + 0x77, 0xC5, + 0x7C, 0x93, + 0x7D, 0x00, + 0xD0, 0x48, + 0xD5, 0xA0, + 0xD7, 0xEA, + ADV7183_SD_SATURATION_CR, 0x3E, + ADV7183_PAL_V_END, 0x3E, + ADV7183_PAL_F_TOGGLE, 0x0F, + ADV7183_ADI_CTRL, 0x00, +}; + +static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7183, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7183, hdl)->sd; +} + +static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int adv7183_writeregs(struct v4l2_subdev *sd, + const unsigned char *regs, unsigned int num) +{ + unsigned char reg, data; + unsigned int cnt = 0; + + if (num & 0x1) { + v4l2_err(sd, "invalid regs array\n"); + return -1; + } + + while (cnt < num) { + reg = *regs++; + data = *regs++; + cnt += 2; + + adv7183_write(sd, reg, data); + } + return 0; +} + +static int adv7183_log_status(struct v4l2_subdev *sd) +{ + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_info(sd, "adv7183: Input control = 0x%02x\n", + adv7183_read(sd, ADV7183_IN_CTRL)); + v4l2_info(sd, "adv7183: Video selection = 0x%02x\n", + adv7183_read(sd, ADV7183_VD_SEL)); + v4l2_info(sd, "adv7183: Output control = 0x%02x\n", + adv7183_read(sd, ADV7183_OUT_CTRL)); + v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n", + adv7183_read(sd, ADV7183_EXT_OUT_CTRL)); + v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n", + adv7183_read(sd, ADV7183_AUTO_DET_EN)); + v4l2_info(sd, "adv7183: Contrast = 0x%02x\n", + adv7183_read(sd, ADV7183_CONTRAST)); + v4l2_info(sd, "adv7183: Brightness = 0x%02x\n", + adv7183_read(sd, ADV7183_BRIGHTNESS)); + v4l2_info(sd, "adv7183: Hue = 0x%02x\n", + adv7183_read(sd, ADV7183_HUE)); + v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_Y)); + v4l2_info(sd, "adv7183: Default value C = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_C)); + v4l2_info(sd, "adv7183: ADI control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL)); + v4l2_info(sd, "adv7183: Power Management = 0x%02x\n", + adv7183_read(sd, ADV7183_POW_MANAGE)); + v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_STATUS_1), + adv7183_read(sd, ADV7183_STATUS_2), + adv7183_read(sd, ADV7183_STATUS_3)); + v4l2_info(sd, "adv7183: Ident = 0x%02x\n", + adv7183_read(sd, ADV7183_IDENT)); + v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n", + adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL)); + v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n", + adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1)); + v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL), + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2)); + v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n", + adv7183_read(sd, ADV7183_COMB_FILT_CTRL)); + v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL_2)); + v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n", + adv7183_read(sd, ADV7183_PIX_DELAY_CTRL)); + v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n", + adv7183_read(sd, ADV7183_MISC_GAIN_CTRL)); + v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n", + adv7183_read(sd, ADV7183_AGC_MODE_CTRL)); + v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); + v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_HS_POS_CTRL_1), + adv7183_read(sd, ADV7183_HS_POS_CTRL_2), + adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); + v4l2_info(sd, "adv7183: Polarity = 0x%02x\n", + adv7183_read(sd, ADV7183_POLARITY)); + v4l2_info(sd, "adv7183: ADC control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADC_CTRL)); + v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_OFFSET_CB), + adv7183_read(sd, ADV7183_SD_OFFSET_CR)); + v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_SATURATION_CB), + adv7183_read(sd, ADV7183_SD_SATURATION_CR)); + v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n", + adv7183_read(sd, ADV7183_DRIVE_STR)); + v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name); + return 0; +} + +static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *std = decoder->std; + return 0; +} + +static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + if (std == V4L2_STD_PAL_60) + reg |= 0x60; + else if (std == V4L2_STD_NTSC_443) + reg |= 0x70; + else if (std == V4L2_STD_PAL_N) + reg |= 0x90; + else if (std == V4L2_STD_PAL_M) + reg |= 0xA0; + else if (std == V4L2_STD_PAL_Nc) + reg |= 0xC0; + else if (std & V4L2_STD_PAL) + reg |= 0x80; + else if (std & V4L2_STD_NTSC) + reg |= 0x50; + else if (std & V4L2_STD_SECAM) + reg |= 0xE0; + else + return -EINVAL; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + decoder->std = std; + + return 0; +} + +static int adv7183_reset(struct v4l2_subdev *sd, u32 val) +{ + int reg; + + reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80; + adv7183_write(sd, ADV7183_POW_MANAGE, reg); + /* wait 5ms before any further i2c writes are performed */ + usleep_range(5000, 10000); + return 0; +} + +static int adv7183_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT)) + return -EINVAL; + + if (input != decoder->input) { + decoder->input = input; + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0; + switch (input) { + case ADV7183_COMPOSITE1: + reg |= 0x1; + break; + case ADV7183_COMPOSITE2: + reg |= 0x2; + break; + case ADV7183_COMPOSITE3: + reg |= 0x3; + break; + case ADV7183_COMPOSITE4: + reg |= 0x4; + break; + case ADV7183_COMPOSITE5: + reg |= 0x5; + break; + case ADV7183_COMPOSITE6: + reg |= 0xB; + break; + case ADV7183_COMPOSITE7: + reg |= 0xC; + break; + case ADV7183_COMPOSITE8: + reg |= 0xD; + break; + case ADV7183_COMPOSITE9: + reg |= 0xE; + break; + case ADV7183_COMPOSITE10: + reg |= 0xF; + break; + case ADV7183_SVIDEO0: + reg |= 0x6; + break; + case ADV7183_SVIDEO1: + reg |= 0x7; + break; + case ADV7183_SVIDEO2: + reg |= 0x8; + break; + case ADV7183_COMPONENT0: + reg |= 0x9; + break; + case ADV7183_COMPONENT1: + reg |= 0xA; + break; + default: + break; + } + adv7183_write(sd, ADV7183_IN_CTRL, reg); + } + + if (output != decoder->output) { + decoder->output = output; + reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0; + switch (output) { + case ADV7183_16BIT_OUT: + reg |= 0x9; + break; + default: + reg |= 0xC; + break; + } + adv7183_write(sd, ADV7183_OUT_CTRL, reg); + } + + return 0; +} + +static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + int val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (val < 0) + val = 127 - val; + adv7183_write(sd, ADV7183_BRIGHTNESS, val); + break; + case V4L2_CID_CONTRAST: + adv7183_write(sd, ADV7183_CONTRAST, val); + break; + case V4L2_CID_SATURATION: + adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF)); + break; + case V4L2_CID_HUE: + adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + /* enable autodetection block */ + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + /* wait autodetection switch */ + mdelay(10); + + /* get autodetection result */ + reg = adv7183_read(sd, ADV7183_STATUS_1); + switch ((reg >> 0x4) & 0x7) { + case 0: + *std = V4L2_STD_NTSC; + break; + case 1: + *std = V4L2_STD_NTSC_443; + break; + case 2: + *std = V4L2_STD_PAL_M; + break; + case 3: + *std = V4L2_STD_PAL_60; + break; + case 4: + *std = V4L2_STD_PAL; + break; + case 5: + *std = V4L2_STD_SECAM; + break; + case 6: + *std = V4L2_STD_PAL_Nc; + break; + case 7: + *std = V4L2_STD_SECAM; + break; + default: + *std = V4L2_STD_UNKNOWN; + break; + } + + /* after std detection, write back user set std */ + adv7183_s_std(sd, decoder->std); + return 0; +} + +static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + int reg; + + *status = V4L2_IN_ST_NO_SIGNAL; + reg = adv7183_read(sd, ADV7183_STATUS_1); + if (reg < 0) + return reg; + if (reg & 0x1) + *status = 0; + return 0; +} + +static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index > 0) + return -EINVAL; + + *code = V4L2_MBUS_FMT_UYVY8_2X8; + return 0; +} + +static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + if (decoder->std & V4L2_STD_525_60) { + fmt->field = V4L2_FIELD_SEQ_TB; + fmt->width = 720; + fmt->height = 480; + } else { + fmt->field = V4L2_FIELD_SEQ_BT; + fmt->width = 720; + fmt->height = 576; + } + return 0; +} + +static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + adv7183_try_mbus_fmt(sd, fmt); + decoder->fmt = *fmt; + return 0; +} + +static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *fmt = decoder->fmt; + return 0; +} + +static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7183 *decoder = to_adv7183(sd); + + if (enable) + gpio_direction_output(decoder->oe_pin, 0); + else + gpio_direction_output(decoder->oe_pin, 1); + udelay(1); + return 0; +} + +static int adv7183_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* 0x11 for adv7183, 0x13 for adv7183b */ + rev = adv7183_read(sd, ADV7183_IDENT); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = adv7183_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { + .s_ctrl = adv7183_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7183_core_ops = { + .log_status = adv7183_log_status, + .g_std = adv7183_g_std, + .s_std = adv7183_s_std, + .reset = adv7183_reset, + .g_chip_ident = adv7183_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7183_g_register, + .s_register = adv7183_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops adv7183_video_ops = { + .s_routing = adv7183_s_routing, + .querystd = adv7183_querystd, + .g_input_status = adv7183_g_input_status, + .enum_mbus_fmt = adv7183_enum_mbus_fmt, + .try_mbus_fmt = adv7183_try_mbus_fmt, + .s_mbus_fmt = adv7183_s_mbus_fmt, + .g_mbus_fmt = adv7183_g_mbus_fmt, + .s_stream = adv7183_s_stream, +}; + +static const struct v4l2_subdev_ops adv7183_ops = { + .core = &adv7183_core_ops, + .video = &adv7183_video_ops, +}; + +static int adv7183_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7183 *decoder; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int ret; + struct v4l2_mbus_framefmt fmt; + const unsigned *pin_array; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + pin_array = client->dev.platform_data; + if (pin_array == NULL) + return -EINVAL; + + decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + decoder->reset_pin = pin_array[0]; + decoder->oe_pin = pin_array[1]; + + if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); + ret = -EBUSY; + goto err_free_decoder; + } + + if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); + ret = -EBUSY; + goto err_free_reset; + } + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7183_ops); + + hdl = &decoder->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + ret = hdl->error; + + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + /* v4l2 doesn't support an autodetect standard, pick PAL as default */ + decoder->std = V4L2_STD_PAL; + decoder->input = ADV7183_COMPOSITE4; + decoder->output = ADV7183_8BIT_OUT; + + gpio_direction_output(decoder->oe_pin, 1); + /* reset chip */ + gpio_direction_output(decoder->reset_pin, 0); + /* reset pulse width at least 5ms */ + mdelay(10); + gpio_direction_output(decoder->reset_pin, 1); + /* wait 5ms before any further i2c writes are performed */ + mdelay(5); + + adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); + adv7183_s_std(sd, decoder->std); + fmt.width = 720; + fmt.height = 576; + adv7183_s_mbus_fmt(sd, &fmt); + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + return 0; +err_free_oe: + gpio_free(decoder->oe_pin); +err_free_reset: + gpio_free(decoder->reset_pin); +err_free_decoder: + kfree(decoder); + return ret; +} + +static int adv7183_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(decoder->oe_pin); + gpio_free(decoder->reset_pin); + kfree(decoder); + return 0; +} + +static const struct i2c_device_id adv7183_id[] = { + {"adv7183", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7183_id); + +static struct i2c_driver adv7183_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7183", + }, + .probe = adv7183_probe, + .remove = __devexit_p(adv7183_remove), + .id_table = adv7183_id, +}; + +static __init int adv7183_init(void) +{ + return i2c_add_driver(&adv7183_driver); +} + +static __exit void adv7183_exit(void) +{ + i2c_del_driver(&adv7183_driver); +} + +module_init(adv7183_init); +module_exit(adv7183_exit); + +MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/adv7183_regs.h b/drivers/media/i2c/adv7183_regs.h new file mode 100644 index 000000000000..4a5b7d211d2f --- /dev/null +++ b/drivers/media/i2c/adv7183_regs.h @@ -0,0 +1,107 @@ +/* + * adv7183 - Analog Devices ADV7183 video decoder registers + * + * Copyright (c) 2011 Analog Devices 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. + * + * 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 _ADV7183_REGS_H_ +#define _ADV7183_REGS_H_ + +#define ADV7183_IN_CTRL 0x00 /* Input control */ +#define ADV7183_VD_SEL 0x01 /* Video selection */ +#define ADV7183_OUT_CTRL 0x03 /* Output control */ +#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */ +#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */ +#define ADV7183_CONTRAST 0x08 /* Contrast */ +#define ADV7183_BRIGHTNESS 0x0A /* Brightness */ +#define ADV7183_HUE 0x0B /* Hue */ +#define ADV7183_DEF_Y 0x0C /* Default value Y */ +#define ADV7183_DEF_C 0x0D /* Default value C */ +#define ADV7183_ADI_CTRL 0x0E /* ADI control */ +#define ADV7183_POW_MANAGE 0x0F /* Power Management */ +#define ADV7183_STATUS_1 0x10 /* Status 1 */ +#define ADV7183_IDENT 0x11 /* Ident */ +#define ADV7183_STATUS_2 0x12 /* Status 2 */ +#define ADV7183_STATUS_3 0x13 /* Status 3 */ +#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */ +#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */ +#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */ +#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */ +#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */ +#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */ +#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */ +#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */ +#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */ +#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */ +#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */ +#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */ +#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */ +#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ +#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ +#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ +#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ +#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ +#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ +#define ADV7183_POLARITY 0x37 /* Polarity */ +#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ +#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ +#define ADV7183_ADC_CTRL 0x3A /* ADC control */ +#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */ +#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */ +#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */ +#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */ +#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */ +#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */ +#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */ +#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */ +#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */ +#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */ +#define ADV7183_LOCK_CNT 0x51 /* Lock count */ +#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */ +#define ADV7183_VBI_INFO 0x90 /* VBI info */ +#define ADV7183_WSS_1 0x91 /* WSS 1 */ +#define ADV7183_WSS_2 0x92 /* WSS 2 */ +#define ADV7183_EDTV_1 0x93 /* EDTV 1 */ +#define ADV7183_EDTV_2 0x94 /* EDTV 2 */ +#define ADV7183_EDTV_3 0x95 /* EDTV 3 */ +#define ADV7183_CGMS_1 0x96 /* CGMS 1 */ +#define ADV7183_CGMS_2 0x97 /* CGMS 2 */ +#define ADV7183_CGMS_3 0x98 /* CGMS 3 */ +#define ADV7183_CCAP_1 0x99 /* CCAP 1 */ +#define ADV7183_CCAP_2 0x9A /* CCAP 2 */ +#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */ +#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */ +#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ +#define ADV7183_CRC_EN 0xB2 /* CRC enable */ +#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ +#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ +#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ +#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ +#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ +#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */ +#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */ +#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */ +#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */ +#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */ +#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */ +#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */ +#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */ +#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */ +#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */ +#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */ +#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */ + +#endif diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c new file mode 100644 index 000000000000..2b5aa676a84e --- /dev/null +++ b/drivers/media/i2c/adv7343.c @@ -0,0 +1,476 @@ +/* + * adv7343 - ADV7343 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7343_regs.h" + +MODULE_DESCRIPTION("ADV7343 video encoder driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7343_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7343_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7343_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd; +} + +static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7343_init_reg_val[] = { + ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT, + ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT, + + ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT, + ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT, + ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT, + ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT, + ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT, + ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT, + ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT, + + ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT, + ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT, + ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT, + ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT, + ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT, + ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT, + ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT, + ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT, + + ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT, + ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT, + ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7343_std_info stdinfo[] = { + { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, +}; + +static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7343_state *state = to_state(sd); + struct adv7343_std_info *std_info; + int num_std; + char *fsc_ptr; + u8 reg, val; + int err = 0; + int i = 0; + + std_info = (struct adv7343_std_info *)stdinfo; + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (std_info[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + /* Set the standard */ + val = state->reg80 & (~(SD_STD_MASK)); + val |= std_info[i].standard_val3; + err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & (~((u8) INPUT_MODE_MASK)); + val |= SD_INPUT_MODE; + err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + fsc_ptr = (unsigned char *)&std_info[i].fsc_val; + reg = ADV7343_FSC_REG0; + for (i = 0; i < 4; i++, reg++, fsc_ptr++) { + err = adv7343_write(sd, reg, *fsc_ptr); + if (err < 0) + goto setstd_exit; + } + + val = state->reg80; + + /* Filter settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val &= 0x03; + else if (std & ~V4L2_STD_SECAM) + val |= 0x04; + + err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7343_state *state = to_state(sd); + unsigned char val; + int err = 0; + + if (output_type > ADV7343_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7343_COMPOSITE_ID) + val |= ADV7343_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7343_COMPONENT_ID) + val |= ADV7343_COMPONENT_POWER_VALUE; + else + val |= ADV7343_SVIDEO_POWER_VALUE; + + err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7343_write(sd, ADV7343_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ + val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); + err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to + * zero */ + val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI); + err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7343_log_status(struct v4l2_subdev *sd) +{ + struct adv7343_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, + ctrl->val); + + case V4L2_CID_HUE: + return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val); + + case V4L2_CID_GAIN: + return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val); + } + return -EINVAL; +} + +static int adv7343_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0); +} + +static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { + .s_ctrl = adv7343_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7343_core_ops = { + .log_status = adv7343_log_status, + .g_chip_ident = adv7343_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7343_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7343_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7343_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7343_video_ops = { + .s_std_output = adv7343_s_std_output, + .s_routing = adv7343_s_routing, +}; + +static const struct v4l2_subdev_ops adv7343_ops = { + .core = &adv7343_core_ops, + .video = &adv7343_video_ops, +}; + +static int adv7343_initialize(struct v4l2_subdev *sd) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) { + + err = adv7343_write(sd, adv7343_init_reg_val[i], + adv7343_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7343_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7343_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7343_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7343_state *state; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = 0x80; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = 0x00; + state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT; + + state->output = ADV7343_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops); + + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN, + ADV7343_BRIGHTNESS_MAX, 1, + ADV7343_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_HUE, ADV7343_HUE_MIN, + ADV7343_HUE_MAX, 1, + ADV7343_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_GAIN, ADV7343_GAIN_MIN, + ADV7343_GAIN_MAX, 1, + ADV7343_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7343_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; +} + +static int adv7343_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7343_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7343_id[] = { + {"adv7343", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7343_id); + +static struct i2c_driver adv7343_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7343", + }, + .probe = adv7343_probe, + .remove = adv7343_remove, + .id_table = adv7343_id, +}; + +module_i2c_driver(adv7343_driver); diff --git a/drivers/media/i2c/adv7343_regs.h b/drivers/media/i2c/adv7343_regs.h new file mode 100644 index 000000000000..446606764346 --- /dev/null +++ b/drivers/media/i2c/adv7343_regs.h @@ -0,0 +1,181 @@ +/* + * ADV7343 encoder related structure and register definitions + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7343_REG_H +#define ADV7343_REGS_H + +struct adv7343_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7343_POWER_MODE_REG (0x00) +#define ADV7343_MODE_SELECT_REG (0x01) +#define ADV7343_MODE_REG0 (0x02) + +#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b) + +#define ADV7343_SOFT_RESET (0x17) + +#define ADV7343_HD_MODE_REG1 (0x30) +#define ADV7343_HD_MODE_REG2 (0x31) +#define ADV7343_HD_MODE_REG3 (0x32) +#define ADV7343_HD_MODE_REG4 (0x33) +#define ADV7343_HD_MODE_REG5 (0x34) +#define ADV7343_HD_MODE_REG6 (0x35) + +#define ADV7343_HD_MODE_REG7 (0x39) + +#define ADV7343_SD_MODE_REG1 (0x80) +#define ADV7343_SD_MODE_REG2 (0x82) +#define ADV7343_SD_MODE_REG3 (0x83) +#define ADV7343_SD_MODE_REG4 (0x84) +#define ADV7343_SD_MODE_REG5 (0x86) +#define ADV7343_SD_MODE_REG6 (0x87) +#define ADV7343_SD_MODE_REG7 (0x88) +#define ADV7343_SD_MODE_REG8 (0x89) + +#define ADV7343_FSC_REG0 (0x8C) +#define ADV7343_FSC_REG1 (0x8D) +#define ADV7343_FSC_REG2 (0x8E) +#define ADV7343_FSC_REG3 (0x8F) + +#define ADV7343_SD_CGMS_WSS0 (0x99) + +#define ADV7343_SD_HUE_REG (0xA0) +#define ADV7343_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7343_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAVSAV code*/ +#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */ +#define ADV7343_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7343_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7343_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) +#define ADV7343_SOFT_RESET_DEFAULT (0x02) +#define ADV7343_COMPOSITE_POWER_VALUE (0x80) +#define ADV7343_COMPONENT_POWER_VALUE (0x1C) +#define ADV7343_SVIDEO_POWER_VALUE (0x60) +#define ADV7343_SD_HUE_REG_DEFAULT (127) +#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03) + +#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7343_SD_MODE_REG1_DEFAULT (0x00) +#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7343_SD_MODE_REG3_DEFAULT (0x10) +#define ADV7343_SD_MODE_REG4_DEFAULT (0x01) +#define ADV7343_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C) +#define ADV7343_SD_MODE_REG7_DEFAULT (0x04) +#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for DAC output levels */ +#define DAC_OUTPUT_LEVEL_MASK (0xFF) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_SHIFT (3) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25fps (0x0E) +#define STD_MODE_1080P_24 (0x12) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x7) +#define SD_LUMA_FLTR_SHIFT (0x2) +#define SD_CHROMA_FLTR_MASK (0x7) +#define SD_CHROMA_FLTR_SHIFT (0x5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PBPR_SSAF_EN (0x01) +#define SD_PBPR_SSAF_DI (0xFE) +#define SD_DAC_1_DI (0xFD) +#define SD_DAC_2_DI (0xFB) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_RGB_INPUT_EN (0x02) +#define HD_RGB_INPUT_DI (0xFD) +#define HD_PBPR_SYNC_EN (0x04) +#define HD_PBPR_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7343_BRIGHTNESS_MAX (127) +#define ADV7343_BRIGHTNESS_MIN (0) +#define ADV7343_BRIGHTNESS_DEF (3) +#define ADV7343_HUE_MAX (255) +#define ADV7343_HUE_MIN (0) +#define ADV7343_HUE_DEF (127) +#define ADV7343_GAIN_MAX (64) +#define ADV7343_GAIN_MIN (-64) +#define ADV7343_GAIN_DEF (0) + +#endif diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c new file mode 100644 index 000000000000..3dc6098c7267 --- /dev/null +++ b/drivers/media/i2c/adv7393.c @@ -0,0 +1,487 @@ +/* + * adv7393 - ADV7393 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7393_regs.h" + +MODULE_DESCRIPTION("ADV7393 video encoder driver"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7393_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7393_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; +} + +static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7393_init_reg_val[] = { + ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, + ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, + + ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, + ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, + ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, + ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, + ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, + ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, + ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, + + ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, + ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, + ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, + ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, + ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, + ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, + ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, + ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, + + ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, + + ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, + ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, + ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7393_std_info stdinfo[] = { + { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 Hz */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, +}; + +static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + const struct adv7393_std_info *std_info; + int num_std; + u8 reg; + u32 val; + int err = 0; + int i; + + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (stdinfo[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + std_info = &stdinfo[i]; + + /* Set the standard */ + val = state->reg80 & ~SD_STD_MASK; + val |= std_info->standard_val3; + err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & ~INPUT_MODE_MASK; + val |= SD_INPUT_MODE; + err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + val = std_info->fsc_val; + for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { + err = adv7393_write(sd, reg, val); + if (err < 0) + goto setstd_exit; + val >>= 8; + } + + val = state->reg82; + + /* Pedestal settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val |= SD_PEDESTAL_EN; + else + val &= SD_PEDESTAL_DI; + + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setstd_exit; + + state->reg82 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7393_state *state = to_state(sd); + u8 val; + int err = 0; + + if (output_type > ADV7393_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7393_COMPOSITE_ID) + val |= ADV7393_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7393_COMPONENT_ID) + val |= ADV7393_COMPONENT_POWER_VALUE; + else + val |= ADV7393_SVIDEO_POWER_VALUE; + + err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7393_write(sd, ADV7393_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 1 bit */ + val = state->reg82; + if (output_type == ADV7393_COMPONENT_ID) + val &= SD_DAC_OUT1_DI; + else + val |= SD_DAC_OUT1_EN; + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap bit to zero */ + val = state->reg35 & HD_DAC_SWAP_DI; + err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7393_log_status(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, + ctrl->val & SD_BRIGHTNESS_VALUE_MASK); + + case V4L2_CID_HUE: + return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, + ctrl->val - ADV7393_HUE_MIN); + + case V4L2_CID_GAIN: + return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, + ctrl->val); + } + return -EINVAL; +} + +static int adv7393_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); +} + +static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { + .s_ctrl = adv7393_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7393_core_ops = { + .log_status = adv7393_log_status, + .g_chip_ident = adv7393_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7393_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7393_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7393_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7393_video_ops = { + .s_std_output = adv7393_s_std_output, + .s_routing = adv7393_s_routing, +}; + +static const struct v4l2_subdev_ops adv7393_ops = { + .core = &adv7393_core_ops, + .video = &adv7393_video_ops, +}; + +static int adv7393_initialize(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { + + err = adv7393_write(sd, adv7393_init_reg_val[i], + adv7393_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7393_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7393_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7393_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7393_state *state; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; + state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; + + state->output = ADV7393_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); + + v4l2_ctrl_handler_init(&state->hdl, 3); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, + ADV7393_BRIGHTNESS_MAX, 1, + ADV7393_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_HUE, ADV7393_HUE_MIN, + ADV7393_HUE_MAX, 1, + ADV7393_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_GAIN, ADV7393_GAIN_MIN, + ADV7393_GAIN_MAX, 1, + ADV7393_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7393_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; +} + +static int adv7393_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7393_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7393_id[] = { + {"adv7393", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adv7393_id); + +static struct i2c_driver adv7393_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7393", + }, + .probe = adv7393_probe, + .remove = adv7393_remove, + .id_table = adv7393_id, +}; +module_i2c_driver(adv7393_driver); diff --git a/drivers/media/i2c/adv7393_regs.h b/drivers/media/i2c/adv7393_regs.h new file mode 100644 index 000000000000..78968330f0be --- /dev/null +++ b/drivers/media/i2c/adv7393_regs.h @@ -0,0 +1,188 @@ +/* + * ADV7393 encoder related structure and register definitions + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7393_REGS_H +#define ADV7393_REGS_H + +struct adv7393_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7393_POWER_MODE_REG (0x00) +#define ADV7393_MODE_SELECT_REG (0x01) +#define ADV7393_MODE_REG0 (0x02) + +#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) + +#define ADV7393_SOFT_RESET (0x17) + +#define ADV7393_HD_MODE_REG1 (0x30) +#define ADV7393_HD_MODE_REG2 (0x31) +#define ADV7393_HD_MODE_REG3 (0x32) +#define ADV7393_HD_MODE_REG4 (0x33) +#define ADV7393_HD_MODE_REG5 (0x34) +#define ADV7393_HD_MODE_REG6 (0x35) + +#define ADV7393_HD_MODE_REG7 (0x39) + +#define ADV7393_SD_MODE_REG1 (0x80) +#define ADV7393_SD_MODE_REG2 (0x82) +#define ADV7393_SD_MODE_REG3 (0x83) +#define ADV7393_SD_MODE_REG4 (0x84) +#define ADV7393_SD_MODE_REG5 (0x86) +#define ADV7393_SD_MODE_REG6 (0x87) +#define ADV7393_SD_MODE_REG7 (0x88) +#define ADV7393_SD_MODE_REG8 (0x89) + +#define ADV7393_SD_TIMING_REG0 (0x8A) + +#define ADV7393_FSC_REG0 (0x8C) +#define ADV7393_FSC_REG1 (0x8D) +#define ADV7393_FSC_REG2 (0x8E) +#define ADV7393_FSC_REG3 (0x8F) + +#define ADV7393_SD_CGMS_WSS0 (0x99) + +#define ADV7393_SD_HUE_ADJUST (0xA0) +#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAV/SAV code*/ +#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ +#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7393_SOFT_RESET_DEFAULT (0x02) +#define ADV7393_COMPOSITE_POWER_VALUE (0x10) +#define ADV7393_COMPONENT_POWER_VALUE (0x1C) +#define ADV7393_SVIDEO_POWER_VALUE (0x0C) +#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) +#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) + +#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) +#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) +#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) +#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) + +#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for SD brightness/WSS */ +#define SD_BRIGHTNESS_VALUE_MASK (0x7F) +#define SD_BLANK_WSS_DATA_MASK (0x80) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_SHIFT (3) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25 (0x0E) +#define STD_MODE_1080P_24 (0x11) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x07) +#define SD_LUMA_FLTR_SHIFT (2) +#define SD_CHROMA_FLTR_MASK (0x07) +#define SD_CHROMA_FLTR_SHIFT (5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PRPB_SSAF_EN (0x01) +#define SD_PRPB_SSAF_DI (0xFE) +#define SD_DAC_OUT1_EN (0x02) +#define SD_DAC_OUT1_DI (0xFD) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_PRPB_SYNC_EN (0x04) +#define HD_PRPB_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7393_BRIGHTNESS_MAX (63) +#define ADV7393_BRIGHTNESS_MIN (-64) +#define ADV7393_BRIGHTNESS_DEF (0) +#define ADV7393_HUE_MAX (127) +#define ADV7393_HUE_MIN (-128) +#define ADV7393_HUE_DEF (0) +#define ADV7393_GAIN_MAX (64) +#define ADV7393_GAIN_MIN (-64) +#define ADV7393_GAIN_DEF (0) + +#endif diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c new file mode 100644 index 000000000000..ba674656b10d --- /dev/null +++ b/drivers/media/i2c/ak881x.c @@ -0,0 +1,359 @@ +/* + * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM) + * + * Copyright (C) 2010, Guennadi Liakhovetski + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AK881X_INTERFACE_MODE 0 +#define AK881X_VIDEO_PROCESS1 1 +#define AK881X_VIDEO_PROCESS2 2 +#define AK881X_VIDEO_PROCESS3 3 +#define AK881X_DAC_MODE 5 +#define AK881X_STATUS 0x24 +#define AK881X_DEVICE_ID 0x25 +#define AK881X_DEVICE_REVISION 0x26 + +struct ak881x { + struct v4l2_subdev subdev; + struct ak881x_pdata *pdata; + unsigned int lines; + int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */ + char revision; /* DEVICE_REVISION content */ +}; + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u8 data) +{ + return i2c_smbus_write_byte_data(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u8 data, u8 mask) +{ + int ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static struct ak881x *to_ak881x(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ak881x, subdev); +} + +static int ak881x_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = ak881x->id; + id->revision = ak881x->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ak881x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int ak881x_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + v4l_bound_align_image(&mf->width, 0, 720, 2, + &mf->height, 0, ak881x->lines, 1, 0); + mf->field = V4L2_FIELD_INTERLACED; + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + if (mf->field != V4L2_FIELD_INTERLACED || + mf->code != V4L2_MBUS_FMT_YUYV8_2X8) + return -EINVAL; + + return ak881x_try_g_mbus_fmt(sd, mf); +} + +static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_YUYV8_2X8; + return 0; +} + +static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = 720; + a->bounds.height = ak881x->lines; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + u8 vp1; + + if (std == V4L2_STD_NTSC_443) { + vp1 = 3; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_M) { + vp1 = 5; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_60) { + vp1 = 7; + ak881x->lines = 480; + } else if (std && !(std & ~V4L2_STD_PAL)) { + vp1 = 0xf; + ak881x->lines = 576; + } else if (std && !(std & ~V4L2_STD_NTSC)) { + vp1 = 0; + ak881x->lines = 480; + } else { + /* No SECAM or PAL_N/Nc supported */ + return -EINVAL; + } + + reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf); + + return 0; +} + +static int ak881x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (enable) { + u8 dac; + /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */ + /* Default: composite output */ + if (ak881x->pdata->flags & AK881X_COMPONENT) + dac = 3; + else + dac = 4; + /* Turn on the DAC(s) */ + reg_write(client, AK881X_DAC_MODE, dac); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } else { + /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */ + reg_write(client, AK881X_DAC_MODE, 0); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } + + return 0; +} + +static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { + .g_chip_ident = ak881x_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ak881x_g_register, + .s_register = ak881x_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { + .s_mbus_fmt = ak881x_s_mbus_fmt, + .g_mbus_fmt = ak881x_try_g_mbus_fmt, + .try_mbus_fmt = ak881x_try_g_mbus_fmt, + .cropcap = ak881x_cropcap, + .enum_mbus_fmt = ak881x_enum_mbus_fmt, + .s_std_output = ak881x_s_std_output, + .s_stream = ak881x_s_stream, +}; + +static struct v4l2_subdev_ops ak881x_subdev_ops = { + .core = &ak881x_subdev_core_ops, + .video = &ak881x_subdev_video_ops, +}; + +static int ak881x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct ak881x *ak881x; + u8 ifmode, data; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL); + if (!ak881x) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops); + + data = reg_read(client, AK881X_DEVICE_ID); + + switch (data) { + case 0x13: + ak881x->id = V4L2_IDENT_AK8813; + break; + case 0x14: + ak881x->id = V4L2_IDENT_AK8814; + break; + default: + dev_err(&client->dev, + "No ak881x chip detected, register read %x\n", data); + kfree(ak881x); + return -ENODEV; + } + + ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION); + ak881x->pdata = client->dev.platform_data; + + if (ak881x->pdata) { + if (ak881x->pdata->flags & AK881X_FIELD) + ifmode = 4; + else + ifmode = 0; + + switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) { + case AK881X_IF_MODE_BT656: + ifmode |= 1; + break; + case AK881X_IF_MODE_MASTER: + ifmode |= 2; + break; + case AK881X_IF_MODE_SLAVE: + default: + break; + } + + dev_dbg(&client->dev, "IF mode %x\n", ifmode); + + /* + * "Line Blanking No." seems to be the same as the number of + * "black" lines on, e.g., SuperH VOU, whose default value of 20 + * "incidentally" matches ak881x' default + */ + reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3)); + } + + /* Hardware default: NTSC-M */ + ak881x->lines = 480; + + dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n", + data, ak881x->revision); + + return 0; +} + +static int ak881x_remove(struct i2c_client *client) +{ + struct ak881x *ak881x = to_ak881x(client); + + v4l2_device_unregister_subdev(&ak881x->subdev); + kfree(ak881x); + + return 0; +} + +static const struct i2c_device_id ak881x_id[] = { + { "ak8813", 0 }, + { "ak8814", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak881x_id); + +static struct i2c_driver ak881x_i2c_driver = { + .driver = { + .name = "ak881x", + }, + .probe = ak881x_probe, + .remove = ak881x_remove, + .id_table = ak881x_id, +}; + +module_i2c_driver(ak881x_i2c_driver); + +MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/aptina-pll.c b/drivers/media/i2c/aptina-pll.c new file mode 100644 index 000000000000..8153a449846b --- /dev/null +++ b/drivers/media/i2c/aptina-pll.c @@ -0,0 +1,173 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "aptina-pll.h" + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll) +{ + unsigned int mf_min; + unsigned int mf_max; + unsigned int p1_min; + unsigned int p1_max; + unsigned int p1; + unsigned int div; + + dev_dbg(dev, "PLL: ext clock %u pix clock %u\n", + pll->ext_clock, pll->pix_clock); + + if (pll->ext_clock < limits->ext_clock_min || + pll->ext_clock > limits->ext_clock_max) { + dev_err(dev, "pll: invalid external clock frequency.\n"); + return -EINVAL; + } + + if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) { + dev_err(dev, "pll: invalid pixel clock frequency.\n"); + return -EINVAL; + } + + /* Compute the multiplier M and combined N*P1 divisor. */ + div = gcd(pll->pix_clock, pll->ext_clock); + pll->m = pll->pix_clock / div; + div = pll->ext_clock / div; + + /* We now have the smallest M and N*P1 values that will result in the + * desired pixel clock frequency, but they might be out of the valid + * range. Compute the factor by which we should multiply them given the + * following constraints: + * + * - minimum/maximum multiplier + * - minimum/maximum multiplier output clock frequency assuming the + * minimum/maximum N value + * - minimum/maximum combined N*P1 divisor + */ + mf_min = DIV_ROUND_UP(limits->m_min, pll->m); + mf_min = max(mf_min, limits->out_clock_min / + (pll->ext_clock / limits->n_min * pll->m)); + mf_min = max(mf_min, limits->n_min * limits->p1_min / div); + mf_max = limits->m_max / pll->m; + mf_max = min(mf_max, limits->out_clock_max / + (pll->ext_clock / limits->n_max * pll->m)); + mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div)); + + dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max); + if (mf_min > mf_max) { + dev_err(dev, "pll: no valid combined N*P1 divisor.\n"); + return -EINVAL; + } + + /* + * We're looking for the highest acceptable P1 value for which a + * multiplier factor MF exists that fulfills the following conditions: + * + * 1. p1 is in the [p1_min, p1_max] range given by the limits and is + * even + * 2. mf is in the [mf_min, mf_max] range computed above + * 3. div * mf is a multiple of p1, in order to compute + * n = div * mf / p1 + * m = pll->m * mf + * 4. the internal clock frequency, given by ext_clock / n, is in the + * [int_clock_min, int_clock_max] range given by the limits + * 5. the output clock frequency, given by ext_clock / n * m, is in the + * [out_clock_min, out_clock_max] range given by the limits + * + * The first naive approach is to iterate over all p1 values acceptable + * according to (1) and all mf values acceptable according to (2), and + * stop at the first combination that fulfills (3), (4) and (5). This + * has a O(n^2) complexity. + * + * Instead of iterating over all mf values in the [mf_min, mf_max] range + * we can compute the mf increment between two acceptable values + * according to (3) with + * + * mf_inc = p1 / gcd(div, p1) (6) + * + * and round the minimum up to the nearest multiple of mf_inc. This will + * restrict the number of mf values to be checked. + * + * Furthermore, conditions (4) and (5) only restrict the range of + * acceptable p1 and mf values by modifying the minimum and maximum + * limits. (5) can be expressed as + * + * ext_clock / (div * mf / p1) * m * mf >= out_clock_min + * ext_clock / (div * mf / p1) * m * mf <= out_clock_max + * + * or + * + * p1 >= out_clock_min * div / (ext_clock * m) (7) + * p1 <= out_clock_max * div / (ext_clock * m) + * + * Similarly, (4) can be expressed as + * + * mf >= ext_clock * p1 / (int_clock_max * div) (8) + * mf <= ext_clock * p1 / (int_clock_min * div) + * + * We can thus iterate over the restricted p1 range defined by the + * combination of (1) and (7), and then compute the restricted mf range + * defined by the combination of (2), (6) and (8). If the resulting mf + * range is not empty, any value in the mf range is acceptable. We thus + * select the mf lwoer bound and the corresponding p1 value. + */ + if (limits->p1_min == 0) { + dev_err(dev, "pll: P1 minimum value must be >0.\n"); + return -EINVAL; + } + + p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div, + pll->ext_clock * pll->m)); + p1_max = min(limits->p1_max, limits->out_clock_max * div / + (pll->ext_clock * pll->m)); + + for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { + unsigned int mf_inc = p1 / gcd(div, p1); + unsigned int mf_high; + unsigned int mf_low; + + mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, + limits->int_clock_max * div)), mf_inc); + mf_high = min(mf_max, pll->ext_clock * p1 / + (limits->int_clock_min * div)); + + if (mf_low > mf_high) + continue; + + pll->n = div * mf_low / p1; + pll->m *= mf_low; + pll->p1 = p1; + dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1); + return 0; + } + + dev_err(dev, "pll: no valid N and P1 divisors found.\n"); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(aptina_pll_calculate); + +MODULE_DESCRIPTION("Aptina PLL Helpers"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/aptina-pll.h b/drivers/media/i2c/aptina-pll.h new file mode 100644 index 000000000000..b370e341e75d --- /dev/null +++ b/drivers/media/i2c/aptina-pll.h @@ -0,0 +1,56 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __APTINA_PLL_H +#define __APTINA_PLL_H + +struct aptina_pll { + unsigned int ext_clock; + unsigned int pix_clock; + + unsigned int n; + unsigned int m; + unsigned int p1; +}; + +struct aptina_pll_limits { + unsigned int ext_clock_min; + unsigned int ext_clock_max; + unsigned int int_clock_min; + unsigned int int_clock_max; + unsigned int out_clock_min; + unsigned int out_clock_max; + unsigned int pix_clock_max; + + unsigned int n_min; + unsigned int n_max; + unsigned int m_min; + unsigned int m_max; + unsigned int p1_min; + unsigned int p1_max; +}; + +struct device; + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll); + +#endif /* __APTINA_PLL_H */ diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c new file mode 100644 index 000000000000..3bfdbf9d9bf1 --- /dev/null +++ b/drivers/media/i2c/as3645a.c @@ -0,0 +1,888 @@ +/* + * drivers/media/i2c/as3645a.c - AS3645A and LM3555 flash controllers driver + * + * Copyright (C) 2008-2011 Nokia Corporation + * Copyright (c) 2011, Intel Corporation. + * + * Contact: Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * TODO: + * - Check hardware FSTROBE control when sensor driver add support for this + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50) +#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100) + +/* Register definitions */ + +/* Read-only Design info register: Reset state: xxxx 0001 */ +#define AS_DESIGN_INFO_REG 0x00 +#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4)) +#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f) + +/* Read-only Version control register: Reset state: 0000 0000 + * for first engineering samples + */ +#define AS_VERSION_CONTROL_REG 0x01 +#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4)) +#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f) + +/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */ +#define AS_INDICATOR_AND_TIMER_REG 0x02 +#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0 +#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4 +#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6 + +/* Read / Write (Current set register): Reset state: 0110 1001 */ +#define AS_CURRENT_SET_REG 0x03 +#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0 +#define AS_CURRENT_LED_DET_ON (1 << 3) +#define AS_CURRENT_FLASH_CURRENT_SHIFT 4 + +/* Read / Write (Control register): Reset state: 1011 0100 */ +#define AS_CONTROL_REG 0x04 +#define AS_CONTROL_MODE_SETTING_SHIFT 0 +#define AS_CONTROL_STROBE_ON (1 << 2) +#define AS_CONTROL_OUT_ON (1 << 3) +#define AS_CONTROL_EXT_TORCH_ON (1 << 4) +#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5) +#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5) +#define AS_CONTROL_COIL_PEAK_SHIFT 6 + +/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */ +#define AS_FAULT_INFO_REG 0x05 +#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1) +#define AS_FAULT_INFO_INDICATOR_LED (1 << 2) +#define AS_FAULT_INFO_LED_AMOUNT (1 << 3) +#define AS_FAULT_INFO_TIMEOUT (1 << 4) +#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5) +#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6) +#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7) + +/* Boost register */ +#define AS_BOOST_REG 0x0d +#define AS_BOOST_CURRENT_DISABLE (0 << 0) +#define AS_BOOST_CURRENT_ENABLE (1 << 0) + +/* Password register is used to unlock boost register writing */ +#define AS_PASSWORD_REG 0x0f +#define AS_PASSWORD_UNLOCK_VALUE 0x55 + +enum as_mode { + AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT, +}; + +/* + * struct as3645a + * + * @subdev: V4L2 subdev + * @pdata: Flash platform data + * @power_lock: Protects power_count + * @power_count: Power reference count + * @led_mode: V4L2 flash LED mode + * @timeout: Flash timeout in microseconds + * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum + * values are 400mA for two LEDs and 500mA for one LED. + * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA) + * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA) + * @strobe_source: Flash strobe source (software or external) + */ +struct as3645a { + struct v4l2_subdev subdev; + const struct as3645a_platform_data *pdata; + + struct mutex power_lock; + int power_count; + + /* Controls */ + struct v4l2_ctrl_handler ctrls; + + enum v4l2_flash_led_mode led_mode; + unsigned int timeout; + u8 flash_current; + u8 assist_current; + u8 indicator_current; + enum v4l2_flash_strobe_source strobe_source; +}; + +#define to_as3645a(sd) container_of(sd, struct as3645a, subdev) + +/* Return negative errno else zero on success */ +static int as3645a_write(struct as3645a *flash, u8 addr, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + rval = i2c_smbus_write_byte_data(client, addr, val); + + dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val, + rval < 0 ? "fail" : "ok"); + + return rval; +} + +/* Return negative errno else a data byte received from the device. */ +static int as3645a_read(struct as3645a *flash, u8 addr) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + rval = i2c_smbus_read_byte_data(client, addr); + + dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval, + rval < 0 ? "fail" : "ok"); + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Hardware configuration and trigger + */ + +/* + * as3645a_set_config - Set flash configuration registers + * @flash: The flash + * + * Configure the hardware with flash, assist and indicator currents, as well as + * flash timeout. + * + * Return 0 on success, or a negative error code if an I2C communication error + * occurred. + */ +static int as3645a_set_config(struct as3645a *flash) +{ + int ret; + u8 val; + + val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT) + | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT) + | AS_CURRENT_LED_DET_ON; + + ret = as3645a_write(flash, AS_CURRENT_SET_REG, val); + if (ret < 0) + return ret; + + val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000) + << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT; + + val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT) + | ((flash->indicator_current ? flash->indicator_current - 1 : 0) + << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT); + + return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val); +} + +/* + * as3645a_set_control - Set flash control register + * @flash: The flash + * @mode: Desired output mode + * @on: Desired output state + * + * Configure the hardware with output mode and state. + * + * Return 0 on success, or a negative error code if an I2C communication error + * occurred. + */ +static int +as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on) +{ + u8 reg; + + /* Configure output parameters and operation mode. */ + reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT) + | (on ? AS_CONTROL_OUT_ON : 0) + | mode; + + if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH && + flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) { + reg |= AS_CONTROL_STROBE_TYPE_LEVEL + | AS_CONTROL_STROBE_ON; + } + + return as3645a_write(flash, AS_CONTROL_REG, reg); +} + +/* + * as3645a_set_output - Configure output and operation mode + * @flash: Flash controller + * @strobe: Strobe the flash (only valid in flash mode) + * + * Turn the LEDs output on/off and set the operation mode based on the current + * parameters. + * + * The AS3645A can't control the indicator LED independently of the flash/torch + * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the + * chip to indicator mode. Otherwise set it to assist light (torch) or flash + * mode. + * + * In indicator and assist modes, turn the output on/off based on the indicator + * and torch currents. In software strobe flash mode, turn the output on/off + * based on the strobe parameter. + */ +static int as3645a_set_output(struct as3645a *flash, bool strobe) +{ + enum as_mode mode; + bool on; + + switch (flash->led_mode) { + case V4L2_FLASH_LED_MODE_NONE: + on = flash->indicator_current != 0; + mode = AS_MODE_INDICATOR; + break; + case V4L2_FLASH_LED_MODE_TORCH: + on = true; + mode = AS_MODE_ASSIST; + break; + case V4L2_FLASH_LED_MODE_FLASH: + on = strobe; + mode = AS_MODE_FLASH; + break; + default: + BUG(); + } + + /* Configure output parameters and operation mode. */ + return as3645a_set_control(flash, mode, on); +} + +/* ----------------------------------------------------------------------------- + * V4L2 controls + */ + +static int as3645a_is_active(struct as3645a *flash) +{ + int ret; + + ret = as3645a_read(flash, AS_CONTROL_REG); + return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON); +} + +static int as3645a_read_fault(struct as3645a *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int rval; + + /* NOTE: reading register clear fault status */ + rval = as3645a_read(flash, AS_FAULT_INFO_REG); + if (rval < 0) + return rval; + + if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) + dev_dbg(&client->dev, "Inductor Peak limit fault\n"); + + if (rval & AS_FAULT_INFO_INDICATOR_LED) + dev_dbg(&client->dev, "Indicator LED fault: " + "Short circuit or open loop\n"); + + dev_dbg(&client->dev, "%u connected LEDs\n", + rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1); + + if (rval & AS_FAULT_INFO_TIMEOUT) + dev_dbg(&client->dev, "Timeout fault\n"); + + if (rval & AS_FAULT_INFO_OVER_TEMPERATURE) + dev_dbg(&client->dev, "Over temperature fault\n"); + + if (rval & AS_FAULT_INFO_SHORT_CIRCUIT) + dev_dbg(&client->dev, "Short circuit fault\n"); + + if (rval & AS_FAULT_INFO_OVER_VOLTAGE) + dev_dbg(&client->dev, "Over voltage fault: " + "Indicates missing capacitor or open connection\n"); + + return rval; +} + +static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct as3645a *flash = + container_of(ctrl->handler, struct as3645a, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int value; + + switch (ctrl->id) { + case V4L2_CID_FLASH_FAULT: + value = as3645a_read_fault(flash); + if (value < 0) + return value; + + ctrl->cur.val = 0; + if (value & AS_FAULT_INFO_SHORT_CIRCUIT) + ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; + if (value & AS_FAULT_INFO_OVER_TEMPERATURE) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; + if (value & AS_FAULT_INFO_TIMEOUT) + ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; + if (value & AS_FAULT_INFO_OVER_VOLTAGE) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; + if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) + ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT; + if (value & AS_FAULT_INFO_INDICATOR_LED) + ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR; + break; + + case V4L2_CID_FLASH_STROBE_STATUS: + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { + ctrl->cur.val = 0; + break; + } + + value = as3645a_is_active(flash); + if (value < 0) + return value; + + ctrl->cur.val = value; + break; + } + + dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val); + + return 0; +} + +static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct as3645a *flash = + container_of(ctrl->handler, struct as3645a, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int ret; + + dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val); + + /* If a control that doesn't apply to the current mode is modified, + * we store the value and return immediately. The setting will be + * applied when the LED mode is changed. Otherwise we apply the setting + * immediately. + */ + + switch (ctrl->id) { + case V4L2_CID_FLASH_LED_MODE: + if (flash->indicator_current) + return -EBUSY; + + ret = as3645a_set_config(flash); + if (ret < 0) + return ret; + + flash->led_mode = ctrl->val; + return as3645a_set_output(flash, false); + + case V4L2_CID_FLASH_STROBE_SOURCE: + flash->strobe_source = ctrl->val; + + /* Applies to flash mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + break; + + return as3645a_set_output(flash, false); + + case V4L2_CID_FLASH_STROBE: + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + return -EBUSY; + + return as3645a_set_output(flash, true); + + case V4L2_CID_FLASH_STROBE_STOP: + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + return -EBUSY; + + return as3645a_set_output(flash, false); + + case V4L2_CID_FLASH_TIMEOUT: + flash->timeout = ctrl->val; + + /* Applies to flash mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + break; + + return as3645a_set_config(flash); + + case V4L2_CID_FLASH_INTENSITY: + flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN) + / AS3645A_FLASH_INTENSITY_STEP; + + /* Applies to flash mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) + break; + + return as3645a_set_config(flash); + + case V4L2_CID_FLASH_TORCH_INTENSITY: + flash->assist_current = + (ctrl->val - AS3645A_TORCH_INTENSITY_MIN) + / AS3645A_TORCH_INTENSITY_STEP; + + /* Applies to torch mode only. */ + if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH) + break; + + return as3645a_set_config(flash); + + case V4L2_CID_FLASH_INDICATOR_INTENSITY: + if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE) + return -EBUSY; + + flash->indicator_current = + (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN) + / AS3645A_INDICATOR_INTENSITY_STEP; + + ret = as3645a_set_config(flash); + if (ret < 0) + return ret; + + if ((ctrl->val == 0) == (ctrl->cur.val == 0)) + break; + + return as3645a_set_output(flash, false); + } + + return 0; +} + +static const struct v4l2_ctrl_ops as3645a_ctrl_ops = { + .g_volatile_ctrl = as3645a_get_ctrl, + .s_ctrl = as3645a_set_ctrl, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +/* Put device into know state. */ +static int as3645a_setup(struct as3645a *flash) +{ + struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); + int ret; + + /* clear errors */ + ret = as3645a_read(flash, AS_FAULT_INFO_REG); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Fault info: %02x\n", ret); + + ret = as3645a_set_config(flash); + if (ret < 0) + return ret; + + ret = as3645a_set_output(flash, false); + if (ret < 0) + return ret; + + /* read status */ + ret = as3645a_read_fault(flash); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n", + as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG)); + dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n", + as3645a_read(flash, AS_CURRENT_SET_REG)); + dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n", + as3645a_read(flash, AS_CONTROL_REG)); + + return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0; +} + +static int __as3645a_set_power(struct as3645a *flash, int on) +{ + int ret; + + if (!on) + as3645a_set_control(flash, AS_MODE_EXT_TORCH, false); + + if (flash->pdata->set_power) { + ret = flash->pdata->set_power(&flash->subdev, on); + if (ret < 0) + return ret; + } + + if (!on) + return 0; + + ret = as3645a_setup(flash); + if (ret < 0) { + if (flash->pdata->set_power) + flash->pdata->set_power(&flash->subdev, 0); + } + + return ret; +} + +static int as3645a_set_power(struct v4l2_subdev *sd, int on) +{ + struct as3645a *flash = to_as3645a(sd); + int ret = 0; + + mutex_lock(&flash->power_lock); + + if (flash->power_count == !on) { + ret = __as3645a_set_power(flash, !!on); + if (ret < 0) + goto done; + } + + flash->power_count += on ? 1 : -1; + WARN_ON(flash->power_count < 0); + +done: + mutex_unlock(&flash->power_lock); + return ret; +} + +static int as3645a_registered(struct v4l2_subdev *sd) +{ + struct as3645a *flash = to_as3645a(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int rval, man, model, rfu, version; + const char *vendor; + + /* Power up the flash driver and read manufacturer ID, model ID, RFU + * and version. + */ + rval = as3645a_set_power(&flash->subdev, 1); + if (rval < 0) + return rval; + + rval = as3645a_read(flash, AS_DESIGN_INFO_REG); + if (rval < 0) + goto power_off; + + man = AS_DESIGN_INFO_FACTORY(rval); + model = AS_DESIGN_INFO_MODEL(rval); + + rval = as3645a_read(flash, AS_VERSION_CONTROL_REG); + if (rval < 0) + goto power_off; + + rfu = AS_VERSION_CONTROL_RFU(rval); + version = AS_VERSION_CONTROL_VERSION(rval); + + /* Verify the chip model and version. */ + if (model != 0x01 || rfu != 0x00) { + dev_err(&client->dev, "AS3645A not detected " + "(model %d rfu %d)\n", model, rfu); + rval = -ENODEV; + goto power_off; + } + + switch (man) { + case 1: + vendor = "AMS, Austria Micro Systems"; + break; + case 2: + vendor = "ADI, Analog Devices Inc."; + break; + case 3: + vendor = "NSC, National Semiconductor"; + break; + case 4: + vendor = "NXP"; + break; + case 5: + vendor = "TI, Texas Instrument"; + break; + default: + vendor = "Unknown"; + } + + dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor, + man, version); + + rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE); + if (rval < 0) + goto power_off; + + rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); + if (rval < 0) + goto power_off; + + /* Setup default values. This makes sure that the chip is in a known + * state, in case the power rail can't be controlled. + */ + rval = as3645a_setup(flash); + +power_off: + as3645a_set_power(&flash->subdev, 0); + + return rval; +} + +static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return as3645a_set_power(sd, 1); +} + +static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return as3645a_set_power(sd, 0); +} + +static const struct v4l2_subdev_core_ops as3645a_core_ops = { + .s_power = as3645a_set_power, +}; + +static const struct v4l2_subdev_ops as3645a_ops = { + .core = &as3645a_core_ops, +}; + +static const struct v4l2_subdev_internal_ops as3645a_internal_ops = { + .registered = as3645a_registered, + .open = as3645a_open, + .close = as3645a_close, +}; + +/* ----------------------------------------------------------------------------- + * I2C driver + */ +#ifdef CONFIG_PM + +static int as3645a_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct as3645a *flash = to_as3645a(subdev); + int rval; + + if (flash->power_count == 0) + return 0; + + rval = __as3645a_set_power(flash, 0); + + dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok"); + + return rval; +} + +static int as3645a_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct as3645a *flash = to_as3645a(subdev); + int rval; + + if (flash->power_count == 0) + return 0; + + rval = __as3645a_set_power(flash, 1); + + dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok"); + + return rval; +} + +#else + +#define as3645a_suspend NULL +#define as3645a_resume NULL + +#endif /* CONFIG_PM */ + +/* + * as3645a_init_controls - Create controls + * @flash: The flash + * + * The number of LEDs reported in platform data is used to compute default + * limits. Parameters passed through platform data can override those limits. + */ +static int __devinit as3645a_init_controls(struct as3645a *flash) +{ + const struct as3645a_platform_data *pdata = flash->pdata; + struct v4l2_ctrl *ctrl; + int maximum; + + v4l2_ctrl_handler_init(&flash->ctrls, 10); + + /* V4L2_CID_FLASH_LED_MODE */ + v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_LED_MODE, 2, ~7, + V4L2_FLASH_LED_MODE_NONE); + + /* V4L2_CID_FLASH_STROBE_SOURCE */ + v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE_SOURCE, + pdata->ext_strobe ? 1 : 0, + pdata->ext_strobe ? ~3 : ~1, + V4L2_FLASH_STROBE_SOURCE_SOFTWARE); + + flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; + + /* V4L2_CID_FLASH_STROBE */ + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); + + /* V4L2_CID_FLASH_STROBE_STOP */ + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); + + /* V4L2_CID_FLASH_STROBE_STATUS */ + ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1); + if (ctrl != NULL) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + /* V4L2_CID_FLASH_TIMEOUT */ + maximum = pdata->timeout_max; + + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN, + maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum); + + flash->timeout = maximum; + + /* V4L2_CID_FLASH_INTENSITY */ + maximum = pdata->flash_max_current; + + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN, + maximum, AS3645A_FLASH_INTENSITY_STEP, maximum); + + flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN) + / AS3645A_FLASH_INTENSITY_STEP; + + /* V4L2_CID_FLASH_TORCH_INTENSITY */ + maximum = pdata->torch_max_current; + + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_TORCH_INTENSITY, + AS3645A_TORCH_INTENSITY_MIN, maximum, + AS3645A_TORCH_INTENSITY_STEP, + AS3645A_TORCH_INTENSITY_MIN); + + flash->assist_current = 0; + + /* V4L2_CID_FLASH_INDICATOR_INTENSITY */ + v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_INDICATOR_INTENSITY, + AS3645A_INDICATOR_INTENSITY_MIN, + AS3645A_INDICATOR_INTENSITY_MAX, + AS3645A_INDICATOR_INTENSITY_STEP, + AS3645A_INDICATOR_INTENSITY_MIN); + + flash->indicator_current = 0; + + /* V4L2_CID_FLASH_FAULT */ + ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, + V4L2_CID_FLASH_FAULT, 0, + V4L2_FLASH_FAULT_OVER_VOLTAGE | + V4L2_FLASH_FAULT_TIMEOUT | + V4L2_FLASH_FAULT_OVER_TEMPERATURE | + V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); + if (ctrl != NULL) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + flash->subdev.ctrl_handler = &flash->ctrls; + + return flash->ctrls.error; +} + +static int __devinit as3645a_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct as3645a *flash; + int ret; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + flash = kzalloc(sizeof(*flash), GFP_KERNEL); + if (flash == NULL) + return -ENOMEM; + + flash->pdata = client->dev.platform_data; + + v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops); + flash->subdev.internal_ops = &as3645a_internal_ops; + flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = as3645a_init_controls(flash); + if (ret < 0) + goto done; + + ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + if (ret < 0) + goto done; + + flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + + mutex_init(&flash->power_lock); + + flash->led_mode = V4L2_FLASH_LED_MODE_NONE; + +done: + if (ret < 0) { + v4l2_ctrl_handler_free(&flash->ctrls); + kfree(flash); + } + + return ret; +} + +static int __devexit as3645a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct as3645a *flash = to_as3645a(subdev); + + v4l2_device_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&flash->ctrls); + media_entity_cleanup(&flash->subdev.entity); + mutex_destroy(&flash->power_lock); + kfree(flash); + + return 0; +} + +static const struct i2c_device_id as3645a_id_table[] = { + { AS3645A_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, as3645a_id_table); + +static const struct dev_pm_ops as3645a_pm_ops = { + .suspend = as3645a_suspend, + .resume = as3645a_resume, +}; + +static struct i2c_driver as3645a_i2c_driver = { + .driver = { + .name = AS3645A_NAME, + .pm = &as3645a_pm_ops, + }, + .probe = as3645a_probe, + .remove = __devexit_p(as3645a_remove), + .id_table = as3645a_id_table, +}; + +module_i2c_driver(as3645a_i2c_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c new file mode 100644 index 000000000000..377bf05b1efd --- /dev/null +++ b/drivers/media/i2c/bt819.c @@ -0,0 +1,517 @@ +/* + * bt819 - BT819A VideoStream Decoder (Rockwell Part) + * + * Copyright (C) 1999 Mike Bernson + * Copyright (C) 1998 Dave Perks + * + * Modifications for LML33/DC10plus unified driver + * Copyright (C) 2000 Serguei Miridonov + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * This code was modify/ported from the saa7111 driver written + * by Dave Perks. + * + * This program is free software; 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 +#include +#include + +MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +struct bt819 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + unsigned char reg[32]; + + v4l2_std_id norm; + int ident; + int input; + int enable; +}; + +static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt819, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct bt819, hdl)->sd; +} + +struct timing { + int hactive; + int hdelay; + int vactive; + int vdelay; + int hscale; + int vscale; +}; + +/* for values, see the bt819 datasheet */ +static struct timing timing_data[] = { + {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000}, + {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000}, +}; + +/* ----------------------------------------------------------------------- */ + +static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value) +{ + return bt819_write(decoder, reg, + (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0)); +} + +static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + int ret = -1; + u8 reg; + + /* the bt819 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + decoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = bt819_write(decoder, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + + return ret; +} + +static inline int bt819_read(struct bt819 *decoder, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int bt819_init(struct v4l2_subdev *sd) +{ + static unsigned char init[] = { + /*0x1f, 0x00,*/ /* Reset */ + 0x01, 0x59, /* 0x01 input format */ + 0x02, 0x00, /* 0x02 temporal decimation */ + 0x03, 0x12, /* 0x03 Cropping msb */ + 0x04, 0x16, /* 0x04 Vertical Delay, lsb */ + 0x05, 0xe0, /* 0x05 Vertical Active lsb */ + 0x06, 0x80, /* 0x06 Horizontal Delay lsb */ + 0x07, 0xd0, /* 0x07 Horizontal Active lsb */ + 0x08, 0x00, /* 0x08 Horizontal Scaling msb */ + 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */ + 0x0a, 0x00, /* 0x0a Brightness control */ + 0x0b, 0x30, /* 0x0b Miscellaneous control */ + 0x0c, 0xd8, /* 0x0c Luma Gain lsb */ + 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */ + 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */ + 0x0f, 0x00, /* 0x0f Hue control */ + 0x12, 0x04, /* 0x12 Output Format */ + 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00 + chroma comb OFF, line drop scaling, interlace scaling + BUG? Why does turning the chroma comb on fuck up color? + Bug in the bt819 stepping on my board? + */ + 0x14, 0x00, /* 0x14 Vertial Scaling lsb */ + 0x16, 0x07, /* 0x16 Video Timing Polarity + ACTIVE=active low + FIELD: high=odd, + vreset=active high, + hreset=active high */ + 0x18, 0x68, /* 0x18 AGC Delay */ + 0x19, 0x5d, /* 0x19 Burst Gate Delay */ + 0x1a, 0x80, /* 0x1a ADC Interface */ + }; + + struct bt819 *decoder = to_bt819(sd); + struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0]; + + init[0x03 * 2 - 1] = + (((timing->vdelay >> 8) & 0x03) << 6) | + (((timing->vactive >> 8) & 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | + ((timing->hactive >> 8) & 0x03); + init[0x04 * 2 - 1] = timing->vdelay & 0xff; + init[0x05 * 2 - 1] = timing->vactive & 0xff; + init[0x06 * 2 - 1] = timing->hdelay & 0xff; + init[0x07 * 2 - 1] = timing->hactive & 0xff; + init[0x08 * 2 - 1] = timing->hscale >> 8; + init[0x09 * 2 - 1] = timing->hscale & 0xff; + /* 0x15 in array is address 0x19 */ + init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */ + /* reset */ + bt819_write(decoder, 0x1f, 0x00); + mdelay(1); + + /* init */ + return bt819_write_block(decoder, init, sizeof(init)); +} + +/* ----------------------------------------------------------------------- */ + +static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) +{ + struct bt819 *decoder = to_bt819(sd); + int status = bt819_read(decoder, 0x00); + int res = V4L2_IN_ST_NO_SIGNAL; + v4l2_std_id std; + + if ((status & 0x80)) + res = 0; + + if ((status & 0x10)) + std = V4L2_STD_PAL; + else + std = V4L2_STD_NTSC; + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = res; + + v4l2_dbg(1, debug, sd, "get status %x\n", status); + return 0; +} + +static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + return bt819_status(sd, NULL, std); +} + +static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + return bt819_status(sd, status, NULL); +} + +static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct bt819 *decoder = to_bt819(sd); + struct timing *timing = NULL; + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + + if (std & V4L2_STD_NTSC) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 0); + bt819_setbit(decoder, 0x01, 5, 0); + bt819_write(decoder, 0x18, 0x68); + bt819_write(decoder, 0x19, 0x5d); + /* bt819_setbit(decoder, 0x1a, 5, 1); */ + timing = &timing_data[1]; + } else if (std & V4L2_STD_PAL) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 1); + bt819_setbit(decoder, 0x01, 5, 1); + bt819_write(decoder, 0x18, 0x7f); + bt819_write(decoder, 0x19, 0x72); + /* bt819_setbit(decoder, 0x1a, 5, 0); */ + timing = &timing_data[0]; + } else { + v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", + (unsigned long long)std); + return -EINVAL; + } + bt819_write(decoder, 0x03, + (((timing->vdelay >> 8) & 0x03) << 6) | + (((timing->vactive >> 8) & 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | + ((timing->hactive >> 8) & 0x03)); + bt819_write(decoder, 0x04, timing->vdelay & 0xff); + bt819_write(decoder, 0x05, timing->vactive & 0xff); + bt819_write(decoder, 0x06, timing->hdelay & 0xff); + bt819_write(decoder, 0x07, timing->hactive & 0xff); + bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); + bt819_write(decoder, 0x09, timing->hscale & 0xff); + decoder->norm = std; + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); + return 0; +} + +static int bt819_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct bt819 *decoder = to_bt819(sd); + + v4l2_dbg(1, debug, sd, "set input %x\n", input); + + if (input > 7) + return -EINVAL; + + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + + if (decoder->input != input) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); + decoder->input = input; + /* select mode */ + if (decoder->input == 0) { + bt819_setbit(decoder, 0x0b, 6, 0); + bt819_setbit(decoder, 0x1a, 1, 1); + } else { + bt819_setbit(decoder, 0x0b, 6, 1); + bt819_setbit(decoder, 0x1a, 1, 0); + } + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); + } + return 0; +} + +static int bt819_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct bt819 *decoder = to_bt819(sd); + + v4l2_dbg(1, debug, sd, "enable output %x\n", enable); + + if (decoder->enable != enable) { + decoder->enable = enable; + bt819_setbit(decoder, 0x16, 7, !enable); + } + return 0; +} + +static int bt819_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct bt819 *decoder = to_bt819(sd); + int temp; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + bt819_write(decoder, 0x0a, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + bt819_write(decoder, 0x0c, ctrl->val & 0xff); + bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01)); + break; + + case V4L2_CID_SATURATION: + bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01)); + + /* Ratio between U gain and V gain must stay the same as + the ratio between the default U and V gain values. */ + temp = (ctrl->val * 180) / 254; + bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); + break; + + case V4L2_CID_HUE: + bt819_write(decoder, 0x0f, ctrl->val); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct bt819 *decoder = to_bt819(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops bt819_ctrl_ops = { + .s_ctrl = bt819_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops bt819_core_ops = { + .g_chip_ident = bt819_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = bt819_s_std, +}; + +static const struct v4l2_subdev_video_ops bt819_video_ops = { + .s_routing = bt819_s_routing, + .s_stream = bt819_s_stream, + .querystd = bt819_querystd, + .g_input_status = bt819_g_input_status, +}; + +static const struct v4l2_subdev_ops bt819_ops = { + .core = &bt819_core_ops, + .video = &bt819_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int bt819_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i, ver; + struct bt819 *decoder; + struct v4l2_subdev *sd; + const char *name; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt819_ops); + + ver = bt819_read(decoder, 0x17); + switch (ver & 0xf0) { + case 0x70: + name = "bt819a"; + decoder->ident = V4L2_IDENT_BT819A; + break; + case 0x60: + name = "bt817a"; + decoder->ident = V4L2_IDENT_BT817A; + break; + case 0x20: + name = "bt815a"; + decoder->ident = V4L2_IDENT_BT815A; + break; + default: + v4l2_dbg(1, debug, sd, + "unknown chip version 0x%02x\n", ver); + return -ENODEV; + } + + v4l_info(client, "%s found @ 0x%x (%s)\n", name, + client->addr << 1, client->adapter->name); + + decoder->norm = V4L2_STD_NTSC; + decoder->input = 0; + decoder->enable = 1; + + i = bt819_init(sd); + if (i < 0) + v4l2_dbg(1, debug, sd, "init status %d\n", i); + + v4l2_ctrl_handler_init(&decoder->hdl, 4); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_CONTRAST, 0, 511, 1, 0xd8); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_SATURATION, 0, 511, 1, 0xfe); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + return 0; +} + +static int bt819_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct bt819 *decoder = to_bt819(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id bt819_id[] = { + { "bt819a", 0 }, + { "bt817a", 0 }, + { "bt815a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bt819_id); + +static struct i2c_driver bt819_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bt819", + }, + .probe = bt819_probe, + .remove = bt819_remove, + .id_table = bt819_id, +}; + +module_i2c_driver(bt819_driver); diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c new file mode 100644 index 000000000000..7e5bd365c239 --- /dev/null +++ b/drivers/media/i2c/bt856.c @@ -0,0 +1,273 @@ +/* + * bt856 - BT856A Digital Video Encoder (Rockwell Part) + * + * Copyright (C) 1999 Mike Bernson + * Copyright (C) 1998 Dave Perks + * + * Modifications for LML33/DC10plus unified driver + * Copyright (C) 2000 Serguei Miridonov + * + * This code was modify/ported from the saa7111 driver written + * by Dave Perks. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * This program is free software; 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 + +MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +#define BT856_REG_OFFSET 0xDA +#define BT856_NR_REG 6 + +struct bt856 { + struct v4l2_subdev sd; + unsigned char reg[BT856_NR_REG]; + + v4l2_std_id norm; +}; + +static inline struct bt856 *to_bt856(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt856, sd); +} + +/* ----------------------------------------------------------------------- */ + +static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); + + encoder->reg[reg - BT856_REG_OFFSET] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value) +{ + return bt856_write(encoder, reg, + (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | + (value ? (1 << bit) : 0)); +} + +static void bt856_dump(struct bt856 *encoder) +{ + int i; + + v4l2_info(&encoder->sd, "register dump:\n"); + for (i = 0; i < BT856_NR_REG; i += 2) + printk(KERN_CONT " %02x", encoder->reg[i]); + printk(KERN_CONT "\n"); +} + +/* ----------------------------------------------------------------------- */ + +static int bt856_init(struct v4l2_subdev *sd, u32 arg) +{ + struct bt856 *encoder = to_bt856(sd); + + /* This is just for testing!!! */ + v4l2_dbg(1, debug, sd, "init\n"); + bt856_write(encoder, 0xdc, 0x18); + bt856_write(encoder, 0xda, 0); + bt856_write(encoder, 0xde, 0); + + bt856_setbit(encoder, 0xdc, 3, 1); + /*bt856_setbit(encoder, 0xdc, 6, 0);*/ + bt856_setbit(encoder, 0xdc, 4, 1); + + if (encoder->norm & V4L2_STD_NTSC) + bt856_setbit(encoder, 0xdc, 2, 0); + else + bt856_setbit(encoder, 0xdc, 2, 1); + + bt856_setbit(encoder, 0xdc, 1, 1); + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct bt856 *encoder = to_bt856(sd); + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (std & V4L2_STD_NTSC) { + bt856_setbit(encoder, 0xdc, 2, 0); + } else if (std & V4L2_STD_PAL) { + bt856_setbit(encoder, 0xdc, 2, 1); + bt856_setbit(encoder, 0xda, 0, 0); + /*bt856_setbit(encoder, 0xda, 0, 1);*/ + } else { + return -EINVAL; + } + encoder->norm = std; + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct bt856 *encoder = to_bt856(sd); + + v4l2_dbg(1, debug, sd, "set input %d\n", input); + + /* We only have video bus. + * input= 0: input is from bt819 + * input= 1: input is from ZR36060 */ + switch (input) { + case 0: + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + bt856_setbit(encoder, 0xdc, 3, 1); + bt856_setbit(encoder, 0xdc, 6, 0); + break; + case 1: + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + bt856_setbit(encoder, 0xdc, 3, 1); + bt856_setbit(encoder, 0xdc, 6, 1); + break; + case 2: /* Color bar */ + bt856_setbit(encoder, 0xdc, 3, 0); + bt856_setbit(encoder, 0xde, 4, 1); + break; + default: + return -EINVAL; + } + + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops bt856_core_ops = { + .g_chip_ident = bt856_g_chip_ident, + .init = bt856_init, +}; + +static const struct v4l2_subdev_video_ops bt856_video_ops = { + .s_std_output = bt856_s_std_output, + .s_routing = bt856_s_routing, +}; + +static const struct v4l2_subdev_ops bt856_ops = { + .core = &bt856_core_ops, + .video = &bt856_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int bt856_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bt856 *encoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt856_ops); + encoder->norm = V4L2_STD_NTSC; + + bt856_write(encoder, 0xdc, 0x18); + bt856_write(encoder, 0xda, 0); + bt856_write(encoder, 0xde, 0); + + bt856_setbit(encoder, 0xdc, 3, 1); + /*bt856_setbit(encoder, 0xdc, 6, 0);*/ + bt856_setbit(encoder, 0xdc, 4, 1); + + if (encoder->norm & V4L2_STD_NTSC) + bt856_setbit(encoder, 0xdc, 2, 0); + else + bt856_setbit(encoder, 0xdc, 2, 1); + + bt856_setbit(encoder, 0xdc, 1, 1); + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + + if (debug != 0) + bt856_dump(encoder); + return 0; +} + +static int bt856_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_bt856(sd)); + return 0; +} + +static const struct i2c_device_id bt856_id[] = { + { "bt856", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bt856_id); + +static struct i2c_driver bt856_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bt856", + }, + .probe = bt856_probe, + .remove = bt856_remove, + .id_table = bt856_id, +}; + +module_i2c_driver(bt856_driver); diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c new file mode 100644 index 000000000000..905320b67a1c --- /dev/null +++ b/drivers/media/i2c/bt866.c @@ -0,0 +1,243 @@ +/* + bt866 - BT866 Digital Video Encoder (Rockwell Part) + + Copyright (C) 1999 Mike Bernson + Copyright (C) 1998 Dave Perks + + Modifications for LML33/DC10plus unified driver + Copyright (C) 2000 Serguei Miridonov + + This code was modify/ported from the saa7111 driver written + by Dave Perks. + + This code was adapted for the bt866 by Christer Weinigel and ported + to 2.6 by Martin Samuelsson. + + This program is free software; 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 + +MODULE_DESCRIPTION("Brooktree-866 video encoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +struct bt866 { + struct v4l2_subdev sd; + u8 reg[256]; +}; + +static inline struct bt866 *to_bt866(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt866, sd); +} + +static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) +{ + struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); + u8 buffer[2]; + int err; + + buffer[0] = subaddr; + buffer[1] = data; + + encoder->reg[subaddr] = data; + + v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data); + + for (err = 0; err < 3;) { + if (i2c_master_send(client, buffer, 2) == 2) + break; + err++; + v4l_warn(client, "error #%d writing to 0x%02x\n", + err, subaddr); + schedule_timeout_interruptible(msecs_to_jiffies(100)); + } + if (err == 3) { + v4l_warn(client, "giving up\n"); + return -1; + } + + return 0; +} + +static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + /* Only PAL supported by this driver at the moment! */ + if (!(std & V4L2_STD_NTSC)) + return -EINVAL; + return 0; +} + +static int bt866_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + static const __u8 init[] = { + 0xc8, 0xcc, /* CRSCALE */ + 0xca, 0x91, /* CBSCALE */ + 0xcc, 0x24, /* YC16 | OSDNUM */ + 0xda, 0x00, /* */ + 0xdc, 0x24, /* SETMODE | PAL */ + 0xde, 0x02, /* EACTIVE */ + + /* overlay colors */ + 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ + 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ + 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ + 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ + 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ + 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ + 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ + 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ + + 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ + 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ + 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ + 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ + 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ + 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ + 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ + 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ + }; + struct bt866 *encoder = to_bt866(sd); + u8 val; + int i; + + for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) + bt866_write(encoder, init[i], init[i+1]); + + val = encoder->reg[0xdc]; + + if (input == 0) + val |= 0x40; /* CBSWAP */ + else + val &= ~0x40; /* !CBSWAP */ + + bt866_write(encoder, 0xdc, val); + + val = encoder->reg[0xcc]; + if (input == 2) + val |= 0x01; /* OSDBAR */ + else + val &= ~0x01; /* !OSDBAR */ + bt866_write(encoder, 0xcc, val); + + v4l2_dbg(1, debug, sd, "set input %d\n", input); + + switch (input) { + case 0: + case 1: + case 2: + break; + default: + return -EINVAL; + } + return 0; +} + +#if 0 +/* Code to setup square pixels, might be of some use in the future, + but is currently unused. */ + val = encoder->reg[0xdc]; + if (*iarg) + val |= 1; /* SQUARE */ + else + val &= ~1; /* !SQUARE */ + bt866_write(client, 0xdc, val); +#endif + +static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops bt866_core_ops = { + .g_chip_ident = bt866_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops bt866_video_ops = { + .s_std_output = bt866_s_std_output, + .s_routing = bt866_s_routing, +}; + +static const struct v4l2_subdev_ops bt866_ops = { + .core = &bt866_core_ops, + .video = &bt866_video_ops, +}; + +static int bt866_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bt866 *encoder; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt866_ops); + return 0; +} + +static int bt866_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_bt866(sd)); + return 0; +} + +static const struct i2c_device_id bt866_id[] = { + { "bt866", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bt866_id); + +static struct i2c_driver bt866_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bt866", + }, + .probe = bt866_probe, + .remove = bt866_remove, + .id_table = bt866_id, +}; + +module_i2c_driver(bt866_driver); diff --git a/drivers/media/i2c/btcx-risc.c b/drivers/media/i2c/btcx-risc.c new file mode 100644 index 000000000000..ac1b2687a20d --- /dev/null +++ b/drivers/media/i2c/btcx-risc.c @@ -0,0 +1,260 @@ +/* + + btcx-risc.c + + bt848/bt878/cx2388x risc code generator. + + (c) 2000-03 Gerd Knorr [SuSE Labs] + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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 "btcx-risc.h" + +MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); + +/* ---------------------------------------------------------- */ +/* allocate/free risc memory */ + +static int memcnt; + +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc) +{ + if (NULL == risc->cpu) + return; + if (debug) { + memcnt--; + printk("btcx: riscmem free [%d] dma=%lx\n", + memcnt, (unsigned long)risc->dma); + } + pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + memset(risc,0,sizeof(*risc)); +} + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size) +{ + __le32 *cpu; + dma_addr_t dma = 0; + + if (NULL != risc->cpu && risc->size < size) + btcx_riscmem_free(pci,risc); + if (NULL == risc->cpu) { + cpu = pci_alloc_consistent(pci, size, &dma); + if (NULL == cpu) + return -ENOMEM; + risc->cpu = cpu; + risc->dma = dma; + risc->size = size; + if (debug) { + memcnt++; + printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", + memcnt, (unsigned long)dma, cpu, size); + } + } + memset(risc->cpu,0,risc->size); + return 0; +} + +/* ---------------------------------------------------------- */ +/* screen overlay helpers */ + +int +btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n) +{ + if (win->left < 0) { + /* left */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = -win->left; + clips[n].c.height = win->height; + n++; + } + if (win->left + win->width > swidth) { + /* right */ + clips[n].c.left = swidth - win->left; + clips[n].c.top = 0; + clips[n].c.width = win->width - clips[n].c.left; + clips[n].c.height = win->height; + n++; + } + if (win->top < 0) { + /* top */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = win->width; + clips[n].c.height = -win->top; + n++; + } + if (win->top + win->height > sheight) { + /* bottom */ + clips[n].c.left = 0; + clips[n].c.top = sheight - win->top; + clips[n].c.width = win->width; + clips[n].c.height = win->height - clips[n].c.top; + n++; + } + return n; +} + +int +btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) +{ + s32 nx,nw,dx; + unsigned int i; + + /* fixup window */ + nx = (win->left + mask) & ~mask; + nw = (win->width) & ~mask; + if (nx + nw > win->left + win->width) + nw -= mask+1; + dx = nx - win->left; + win->left = nx; + win->width = nw; + if (debug) + printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", + win->width, win->height, win->left, win->top, dx); + + /* fixup clips */ + for (i = 0; i < n; i++) { + nx = (clips[i].c.left-dx) & ~mask; + nw = (clips[i].c.width) & ~mask; + if (nx + nw < clips[i].c.left-dx + clips[i].c.width) + nw += mask+1; + clips[i].c.left = nx; + clips[i].c.width = nw; + if (debug) + printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", + clips[i].c.width, clips[i].c.height, + clips[i].c.left, clips[i].c.top); + } + return 0; +} + +void +btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) +{ + struct v4l2_clip swap; + int i,j,n; + + if (nclips < 2) + return; + for (i = nclips-2; i >= 0; i--) { + for (n = 0, j = 0; j <= i; j++) { + if (clips[j].c.left > clips[j+1].c.left) { + swap = clips[j]; + clips[j] = clips[j+1]; + clips[j+1] = swap; + n++; + } + } + if (0 == n) + break; + } +} + +void +btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips) +{ + unsigned int clip,skip; + int end, maxline; + + skip=0; + maxline = 9999; + for (clip = 0; clip < nclips; clip++) { + + /* sanity checks */ + if (clips[clip].c.left + clips[clip].c.width <= 0) + continue; + if (clips[clip].c.left > (signed)width) + break; + + /* vertical range */ + if (line > clips[clip].c.top+clips[clip].c.height-1) + continue; + if (line < clips[clip].c.top) { + if (maxline > clips[clip].c.top-1) + maxline = clips[clip].c.top-1; + continue; + } + if (maxline > clips[clip].c.top+clips[clip].c.height-1) + maxline = clips[clip].c.top+clips[clip].c.height-1; + + /* horizontal range */ + if (0 == skip || clips[clip].c.left > skips[skip-1].end) { + /* new one */ + skips[skip].start = clips[clip].c.left; + if (skips[skip].start < 0) + skips[skip].start = 0; + skips[skip].end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip].end > width) + skips[skip].end = width; + skip++; + } else { + /* overlaps -- expand last one */ + end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip-1].end < end) + skips[skip-1].end = end; + if (skips[skip-1].end > width) + skips[skip-1].end = width; + } + } + *nskips = skip; + *maxy = maxline; + + if (debug) { + printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); + for (skip = 0; skip < *nskips; skip++) { + printk(" %d-%d",skips[skip].start,skips[skip].end); + } + printk("\n"); + } +} + +/* ---------------------------------------------------------- */ + +EXPORT_SYMBOL(btcx_riscmem_alloc); +EXPORT_SYMBOL(btcx_riscmem_free); + +EXPORT_SYMBOL(btcx_screen_clips); +EXPORT_SYMBOL(btcx_align); +EXPORT_SYMBOL(btcx_sort_clips); +EXPORT_SYMBOL(btcx_calc_skips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/btcx-risc.h b/drivers/media/i2c/btcx-risc.h new file mode 100644 index 000000000000..f8bc6e8e7b51 --- /dev/null +++ b/drivers/media/i2c/btcx-risc.h @@ -0,0 +1,34 @@ +/* + */ +struct btcx_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + +struct btcx_skiplist { + int start; + int end; +}; + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size); +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc); + +int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n); +int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, + unsigned int n, int mask); +void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); +void btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c new file mode 100644 index 000000000000..c8581e26fa9c --- /dev/null +++ b/drivers/media/i2c/cs5345.c @@ -0,0 +1,252 @@ +/* + * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC + * Copyright (C) 2007 Hans Verkuil + * + * This program is free software; 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 + +MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static bool debug; + +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); + +struct cs5345_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct cs5345_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cs5345_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int cs5345_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + if ((input & 0xf) > 6) { + v4l2_err(sd, "Invalid input %d.\n", input); + return -EINVAL; + } + cs5345_write(sd, 0x09, input & 0xf); + cs5345_write(sd, 0x05, input & 0xf0); + return 0; +} + +static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0); + return 0; + case V4L2_CID_AUDIO_VOLUME: + cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f); + cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f); + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 1; + reg->val = cs5345_read(sd, reg->reg & 0x1f); + return 0; +} + +static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff); + return 0; +} +#endif + +static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0); +} + +static int cs5345_log_status(struct v4l2_subdev *sd) +{ + u8 v = cs5345_read(sd, 0x09) & 7; + u8 m = cs5345_read(sd, 0x04); + int vol = cs5345_read(sd, 0x08) & 0x3f; + + v4l2_info(sd, "Input: %d%s\n", v, + (m & 0x80) ? " (muted)" : ""); + if (vol >= 32) + vol = vol - 64; + v4l2_info(sd, "Volume: %d dB\n", vol); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { + .s_ctrl = cs5345_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops cs5345_core_ops = { + .log_status = cs5345_log_status, + .g_chip_ident = cs5345_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cs5345_g_register, + .s_register = cs5345_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops cs5345_audio_ops = { + .s_routing = cs5345_s_routing, +}; + +static const struct v4l2_subdev_ops cs5345_ops = { + .core = &cs5345_core_ops, + .audio = &cs5345_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int cs5345_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs5345_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cs5345_ops); + + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + /* set volume/mute */ + v4l2_ctrl_handler_setup(&state->hdl); + + cs5345_write(sd, 0x02, 0x00); + cs5345_write(sd, 0x04, 0x01); + cs5345_write(sd, 0x09, 0x01); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cs5345_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cs5345_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id cs5345_id[] = { + { "cs5345", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs5345_id); + +static struct i2c_driver cs5345_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cs5345", + }, + .probe = cs5345_probe, + .remove = cs5345_remove, + .id_table = cs5345_id, +}; + +module_i2c_driver(cs5345_driver); diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c new file mode 100644 index 000000000000..b293912206eb --- /dev/null +++ b/drivers/media/i2c/cs53l32a.c @@ -0,0 +1,251 @@ +/* + * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver. + * Copyright (C) 2005 Martin Vaughan + * + * Audio source switching for Adaptec AVC-2410 added by Trev Jackson + * + * This program is free software; 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 +#include + +MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); +MODULE_AUTHOR("Martin Vaughan"); +MODULE_LICENSE("GPL"); + +static bool debug; + +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); + + +struct cs53l32a_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cs53l32a_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int cs53l32a_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + /* There are 2 physical inputs, but the second input can be + placed in two modes, the first mode bypasses the PGA (gain), + the second goes through the PGA. Hence there are three + possible inputs to choose from. */ + if (input > 2) { + v4l2_err(sd, "Invalid input %d.\n", input); + return -EINVAL; + } + cs53l32a_write(sd, 0x01, 0x01 + (input << 4)); + return 0; +} + +static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30); + return 0; + case V4L2_CID_AUDIO_VOLUME: + cs53l32a_write(sd, 0x04, (u8)ctrl->val); + cs53l32a_write(sd, 0x05, (u8)ctrl->val); + return 0; + } + return -EINVAL; +} + +static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, + chip, V4L2_IDENT_CS53l32A, 0); +} + +static int cs53l32a_log_status(struct v4l2_subdev *sd) +{ + struct cs53l32a_state *state = to_state(sd); + u8 v = cs53l32a_read(sd, 0x01); + + v4l2_info(sd, "Input: %d\n", (v >> 4) & 3); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { + .s_ctrl = cs53l32a_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { + .log_status = cs53l32a_log_status, + .g_chip_ident = cs53l32a_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { + .s_routing = cs53l32a_s_routing, +}; + +static const struct v4l2_subdev_ops cs53l32a_ops = { + .core = &cs53l32a_core_ops, + .audio = &cs53l32a_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int cs53l32a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs53l32a_state *state; + struct v4l2_subdev *sd; + int i; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + if (!id) + strlcpy(client->name, "cs53l32a", sizeof(client->name)); + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); + + for (i = 1; i <= 7; i++) { + u8 v = cs53l32a_read(sd, i); + + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); + } + + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + + /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ + + cs53l32a_write(sd, 0x01, 0x21); + cs53l32a_write(sd, 0x02, 0x29); + cs53l32a_write(sd, 0x03, 0x30); + cs53l32a_write(sd, 0x04, 0x00); + cs53l32a_write(sd, 0x05, 0x00); + cs53l32a_write(sd, 0x06, 0x00); + cs53l32a_write(sd, 0x07, 0x00); + + /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ + + for (i = 1; i <= 7; i++) { + u8 v = cs53l32a_read(sd, i); + + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); + } + return 0; +} + +static int cs53l32a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cs53l32a_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id cs53l32a_id[] = { + { "cs53l32a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs53l32a_id); + +static struct i2c_driver cs53l32a_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cs53l32a", + }, + .probe = cs53l32a_probe, + .remove = cs53l32a_remove, + .id_table = cs53l32a_id, +}; + +module_i2c_driver(cs53l32a_driver); diff --git a/drivers/media/i2c/cx2341x.c b/drivers/media/i2c/cx2341x.c new file mode 100644 index 000000000000..103ef6bad2e2 --- /dev/null +++ b/drivers/media/i2c/cx2341x.c @@ -0,0 +1,1726 @@ +/* + * cx2341x - generic code for cx23415/6/8 based devices + * + * Copyright (C) 2006 Hans Verkuil + * + * This program is free software; 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 + +MODULE_DESCRIPTION("cx23415/6/8 driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/********************** COMMON CODE *********************/ + +/* definitions for audio properties bits 29-28 */ +#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 +#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 +#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 + +static const char *cx2341x_get_name(u32 id) +{ + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return "Spatial Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + return "Spatial Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return "Spatial Luma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return "Spatial Chroma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return "Temporal Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + return "Temporal Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return "Median Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + return "Median Luma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + return "Median Luma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + return "Median Chroma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + return "Median Chroma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return "Insert Navigation Packets"; + } + return NULL; +} + +static const char **cx2341x_get_menu(u32 id) +{ + static const char *cx2341x_video_spatial_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + "1D Vertical", + "2D H/V Separable", + "2D Symmetric non-separable", + NULL + }; + + static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + NULL + }; + + static const char *cx2341x_video_temporal_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_median_filter_type_menu[] = { + "Off", + "Horizontal", + "Vertical", + "Horizontal/Vertical", + "Diagonal", + NULL + }; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return cx2341x_video_spatial_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_luma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_chroma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return cx2341x_video_temporal_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_video_median_filter_type_menu; + } + return NULL; +} + +static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) +{ + *name = cx2341x_get_name(id); + *flags = 0; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *type = V4L2_CTRL_TYPE_MENU; + *min = 0; + *step = 0; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + *type = V4L2_CTRL_TYPE_BOOLEAN; + *min = 0; + *max = *step = 1; + break; + default: + *type = V4L2_CTRL_TYPE_INTEGER; + break; + } + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *flags |= V4L2_CTRL_FLAG_UPDATE; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + *flags |= V4L2_CTRL_FLAG_SLIDER; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; + } +} + + +/********************** OLD CODE *********************/ + +/* Must be sorted from low to high control ID! */ +const u32 cx2341x_mpeg_ctrls[] = { + V4L2_CID_MPEG_CLASS, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_STREAM_VBI_FMT, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_CID_MPEG_AUDIO_MODE, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + V4L2_CID_MPEG_AUDIO_EMPHASIS, + V4L2_CID_MPEG_AUDIO_CRC, + V4L2_CID_MPEG_AUDIO_MUTE, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_CID_MPEG_VIDEO_B_FRAMES, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, + V4L2_CID_MPEG_VIDEO_MUTE, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, + 0 +}; +EXPORT_SYMBOL(cx2341x_mpeg_ctrls); + +static const struct cx2341x_mpeg_params default_params = { + /* misc */ + .capabilities = 0, + .port = CX2341X_PORT_MEMORY, + .width = 720, + .height = 480, + .is_50hz = 0, + + /* stream */ + .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, + .stream_insert_nav_packets = 0, + + /* audio */ + .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, + .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K, + .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, + .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, + .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, + .audio_mute = 0, + + /* video */ + .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, + .video_b_frames = 2, + .video_gop_size = 12, + .video_gop_closure = 1, + .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .video_bitrate = 6000000, + .video_bitrate_peak = 8000000, + .video_temporal_decimation = 0, + .video_mute = 0, + .video_mute_yuv = 0x008080, /* YCbCr value for black */ + + /* encoding filters */ + .video_spatial_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + .video_spatial_filter = 0, + .video_luma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_chroma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_temporal_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + .video_temporal_filter = 8, + .video_median_filter_type = + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + .video_luma_median_filter_top = 255, + .video_luma_median_filter_bottom = 0, + .video_chroma_median_filter_top = 255, + .video_chroma_median_filter_bottom = 0, +}; +/* Map the control ID to the correct field in the cx2341x_mpeg_params + struct. Return -EINVAL if the ID is unknown, else return 0. */ +static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + ctrl->value = params->audio_sampling_freq; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = params->audio_encoding; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + ctrl->value = params->audio_l2_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + ctrl->value = params->audio_ac3_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_MODE: + ctrl->value = params->audio_mode; + break; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + ctrl->value = params->audio_mode_extension; + break; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + ctrl->value = params->audio_emphasis; + break; + case V4L2_CID_MPEG_AUDIO_CRC: + ctrl->value = params->audio_crc; + break; + case V4L2_CID_MPEG_AUDIO_MUTE: + ctrl->value = params->audio_mute; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = params->video_encoding; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + ctrl->value = params->video_aspect; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + ctrl->value = params->video_b_frames; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrl->value = params->video_gop_size; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + ctrl->value = params->video_gop_closure; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = params->video_bitrate_mode; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = params->video_bitrate; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = params->video_bitrate_peak; + break; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + ctrl->value = params->video_temporal_decimation; + break; + case V4L2_CID_MPEG_VIDEO_MUTE: + ctrl->value = params->video_mute; + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + ctrl->value = params->video_mute_yuv; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = params->stream_type; + break; + case V4L2_CID_MPEG_STREAM_VBI_FMT: + ctrl->value = params->stream_vbi_fmt; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + ctrl->value = params->video_spatial_filter_mode; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + ctrl->value = params->video_spatial_filter; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + ctrl->value = params->video_luma_spatial_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + ctrl->value = params->video_chroma_spatial_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + ctrl->value = params->video_temporal_filter_mode; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + ctrl->value = params->video_temporal_filter; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + ctrl->value = params->video_median_filter_type; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + ctrl->value = params->video_luma_median_filter_top; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + ctrl->value = params->video_luma_median_filter_bottom; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + ctrl->value = params->video_chroma_median_filter_top; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + ctrl->value = params->video_chroma_median_filter_bottom; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + ctrl->value = params->stream_insert_nav_packets; + break; + default: + return -EINVAL; + } + return 0; +} + +/* Map the control ID to the correct field in the cx2341x_mpeg_params + struct. Return -EINVAL if the ID is unknown, else return 0. */ +static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + if (busy) + return -EBUSY; + params->audio_sampling_freq = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (busy) + return -EBUSY; + if (params->capabilities & CX2341X_CAP_HAS_AC3) + if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3) + return -ERANGE; + params->audio_encoding = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + if (busy) + return -EBUSY; + params->audio_l2_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (busy) + return -EBUSY; + if (!(params->capabilities & CX2341X_CAP_HAS_AC3)) + return -EINVAL; + params->audio_ac3_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MODE: + params->audio_mode = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + params->audio_mode_extension = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + params->audio_emphasis = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_CRC: + params->audio_crc = ctrl->value; + break; + case V4L2_CID_MPEG_AUDIO_MUTE: + params->audio_mute = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + params->video_aspect = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: { + int b = ctrl->value + 1; + int gop = params->video_gop_size; + params->video_b_frames = ctrl->value; + params->video_gop_size = b * ((gop + b - 1) / b); + /* Max GOP size = 34 */ + while (params->video_gop_size > 34) + params->video_gop_size -= b; + break; + } + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: { + int b = params->video_b_frames + 1; + int gop = ctrl->value; + params->video_gop_size = b * ((gop + b - 1) / b); + /* Max GOP size = 34 */ + while (params->video_gop_size > 34) + params->video_gop_size -= b; + ctrl->value = params->video_gop_size; + break; + } + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + params->video_gop_closure = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (busy) + return -EBUSY; + /* MPEG-1 only allows CBR */ + if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && + ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + return -EINVAL; + params->video_bitrate_mode = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + if (busy) + return -EBUSY; + params->video_bitrate = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + if (busy) + return -EBUSY; + params->video_bitrate_peak = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + params->video_temporal_decimation = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MUTE: + params->video_mute = (ctrl->value != 0); + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + params->video_mute_yuv = ctrl->value; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + if (busy) + return -EBUSY; + params->stream_type = ctrl->value; + params->video_encoding = + (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : + V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + /* MPEG-1 implies CBR */ + params->video_bitrate_mode = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + break; + case V4L2_CID_MPEG_STREAM_VBI_FMT: + params->stream_vbi_fmt = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + params->video_spatial_filter_mode = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + params->video_spatial_filter = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + params->video_luma_spatial_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + params->video_chroma_spatial_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + params->video_temporal_filter_mode = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + params->video_temporal_filter = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + params->video_median_filter_type = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + params->video_luma_median_filter_top = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + params->video_luma_median_filter_bottom = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + params->video_chroma_median_filter_top = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + params->video_chroma_median_filter_bottom = ctrl->value; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + params->stream_insert_nav_packets = ctrl->value; + break; + default: + return -EINVAL; + } + return 0; +} + +static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, + s32 min, s32 max, s32 step, s32 def) +{ + const char *name; + + switch (qctrl->id) { + /* MPEG controls */ + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type, + &min, &max, &step, &def, &qctrl->flags); + qctrl->minimum = min; + qctrl->maximum = max; + qctrl->step = step; + qctrl->default_value = def; + qctrl->reserved[0] = qctrl->reserved[1] = 0; + strlcpy(qctrl->name, name, sizeof(qctrl->name)); + return 0; + + default: + return v4l2_ctrl_query_fill(qctrl, min, max, step, def); + } +} + +int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, + struct v4l2_queryctrl *qctrl) +{ + int err; + + switch (qctrl->id) { + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, + default_params.stream_vbi_fmt); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + /* + * The state of L2 & AC3 bitrate controls can change + * when this control changes, but v4l2_ctrl_query_fill() + * already sets V4L2_CTRL_FLAG_UPDATE for + * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here. + */ + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_AC3, 1, + default_params.audio_encoding); + } + + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, + default_params.audio_encoding); + + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_L2_BITRATE_192K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, + default_params.audio_l2_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3 && + params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_AUDIO_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_STEREO, + V4L2_MPEG_AUDIO_MODE_MONO, 1, + V4L2_MPEG_AUDIO_MODE_STEREO); + + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + if (err == 0 && + params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_EMPHASIS_NONE, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + + case V4L2_CID_MPEG_AUDIO_CRC: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_CRC_NONE, + V4L2_MPEG_AUDIO_CRC_CRC16, 1, + V4L2_MPEG_AUDIO_CRC_NONE); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1, + default_params.audio_ac3_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + if (params->audio_encoding != + V4L2_MPEG_AUDIO_ENCODING_AC3) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } else + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + case V4L2_CID_MPEG_VIDEO_ENCODING: + /* this setting is read-only for the cx2341x since the + V4L2_CID_MPEG_STREAM_TYPE really determines the + MPEG-1/2 setting */ + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + if (err == 0) + qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + return err; + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_221x100, 1, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); + + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, + params->is_50hz ? 12 : 15); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); + + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + if (err == 0 && + params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); + + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); + if (err == 0 && + params->video_bitrate_mode == + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); + + /* CX23415/6 specific */ + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1, + default_params.video_spatial_filter_mode); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, + default_params.video_spatial_filter); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, + 1, + default_params.video_luma_spatial_filter_type); + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + 1, + default_params.video_chroma_spatial_filter_type); + if (params->video_spatial_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1, + default_params.video_temporal_filter_mode); + + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, + default_params.video_temporal_filter); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_temporal_filter_mode == + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1, + default_params.video_median_filter_type); + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_top); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_bottom); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_top); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_bottom); + qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; + if (params->video_median_filter_type == + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; + + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, + default_params.stream_insert_nav_packets); + + default: + return -EINVAL; + + } +} +EXPORT_SYMBOL(cx2341x_ctrl_query); + +const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) +{ + static const char * const mpeg_stream_type_without_ts[] = { + "MPEG-2 Program Stream", + "", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + + static const char *mpeg_stream_type_with_ts[] = { + "MPEG-2 Program Stream", + "MPEG-2 Transport Stream", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + + static const char *mpeg_audio_encoding_l2_ac3[] = { + "", + "MPEG-1/2 Layer II", + "", + "", + "AC-3", + NULL + }; + + switch (id) { + case V4L2_CID_MPEG_STREAM_TYPE: + return (p->capabilities & CX2341X_CAP_HAS_TS) ? + mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; + case V4L2_CID_MPEG_AUDIO_ENCODING: + return (p->capabilities & CX2341X_CAP_HAS_AC3) ? + mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id); + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + return NULL; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_get_menu(id); + default: + return v4l2_ctrl_get_menu(id); + } +} +EXPORT_SYMBOL(cx2341x_ctrl_get_menu); + +static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) +{ + params->audio_properties = + (params->audio_sampling_freq << 0) | + (params->audio_mode << 8) | + (params->audio_mode_extension << 10) | + (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) + ? 3 : params->audio_emphasis) << 12) | + (params->audio_crc << 14); + + if ((params->capabilities & CX2341X_CAP_HAS_AC3) && + params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + params->audio_properties |= + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | + (params->audio_ac3_bitrate << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + params->audio_properties |= + ((3 - params->audio_encoding) << 2) | + ((1 + params->audio_l2_bitrate) << 4); + } +} + +int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, + struct v4l2_ext_controls *ctrls, unsigned int cmd) +{ + int err = 0; + int i; + + if (cmd == VIDIOC_G_EXT_CTRLS) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = cx2341x_get_ctrl(params, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + } + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + struct v4l2_queryctrl qctrl; + const char * const *menu_items = NULL; + + qctrl.id = ctrl->id; + err = cx2341x_ctrl_query(params, &qctrl); + if (err) + break; + if (qctrl.type == V4L2_CTRL_TYPE_MENU) + menu_items = cx2341x_ctrl_get_menu(params, qctrl.id); + err = v4l2_ctrl_check(ctrl, &qctrl, menu_items); + if (err) + break; + err = cx2341x_set_ctrl(params, busy, ctrl); + if (err) + break; + } + if (err == 0 && + params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + params->video_bitrate_peak < params->video_bitrate) { + err = -ERANGE; + ctrls->error_idx = ctrls->count; + } + if (err) + ctrls->error_idx = i; + else + cx2341x_calc_audio_properties(params); + return err; +} +EXPORT_SYMBOL(cx2341x_ext_ctrls); + +void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) +{ + *p = default_params; + cx2341x_calc_audio_properties(p); +} +EXPORT_SYMBOL(cx2341x_fill_defaults); + +static int cx2341x_api(void *priv, cx2341x_mbox_func func, + u32 cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i; + + va_start(vargs, args); + + for (i = 0; i < args; i++) + data[i] = va_arg(vargs, int); + va_end(vargs); + return func(priv, cmd, args, 0, data); +} + +#define NEQ(field) (old->field != new->field) + +int cx2341x_update(void *priv, cx2341x_mbox_func func, + const struct cx2341x_mpeg_params *old, + const struct cx2341x_mpeg_params *new) +{ + static int mpeg_stream_type[] = { + 0, /* MPEG-2 PS */ + 1, /* MPEG-2 TS */ + 2, /* MPEG-1 SS */ + 14, /* DVD */ + 11, /* VCD */ + 12, /* SVCD */ + }; + + int err = 0; + int force = (old == NULL); + u16 temporal = new->video_temporal_filter; + + cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0); + + if (force || NEQ(is_50hz)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, + new->is_50hz); + if (err) return err; + } + + if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) { + u16 w = new->width; + u16 h = new->height; + + if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { + w /= 2; + h /= 2; + } + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, + h, w); + if (err) return err; + } + if (force || NEQ(stream_type)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, + mpeg_stream_type[new->stream_type]); + if (err) return err; + } + if (force || NEQ(video_aspect)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, + 1 + new->video_aspect); + if (err) return err; + } + if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2, + new->video_gop_size, new->video_b_frames + 1); + if (err) return err; + } + if (force || NEQ(video_gop_closure)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, + new->video_gop_closure); + if (err) return err; + } + if (force || NEQ(audio_properties)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, + 1, new->audio_properties); + if (err) return err; + } + if (force || NEQ(audio_mute)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, + new->audio_mute); + if (err) return err; + } + if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) || + NEQ(video_bitrate_peak)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5, + new->video_bitrate_mode, new->video_bitrate, + new->video_bitrate_peak / 400, 0, 0); + if (err) return err; + } + if (force || NEQ(video_spatial_filter_mode) || + NEQ(video_temporal_filter_mode) || + NEQ(video_median_filter_type)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, + 2, new->video_spatial_filter_mode | + (new->video_temporal_filter_mode << 1), + new->video_median_filter_type); + if (err) return err; + } + if (force || NEQ(video_luma_median_filter_bottom) || + NEQ(video_luma_median_filter_top) || + NEQ(video_chroma_median_filter_bottom) || + NEQ(video_chroma_median_filter_top)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4, + new->video_luma_median_filter_bottom, + new->video_luma_median_filter_top, + new->video_chroma_median_filter_bottom, + new->video_chroma_median_filter_top); + if (err) return err; + } + if (force || NEQ(video_luma_spatial_filter_type) || + NEQ(video_chroma_spatial_filter_type)) { + err = cx2341x_api(priv, func, + CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, + 2, new->video_luma_spatial_filter_type, + new->video_chroma_spatial_filter_type); + if (err) return err; + } + if (force || NEQ(video_spatial_filter) || + old->video_temporal_filter != temporal) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, + 2, new->video_spatial_filter, temporal); + if (err) return err; + } + if (force || NEQ(video_temporal_decimation)) { + err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, + 1, new->video_temporal_decimation); + if (err) return err; + } + if (force || NEQ(video_mute) || + (new->video_mute && NEQ(video_mute_yuv))) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, + new->video_mute | (new->video_mute_yuv << 8)); + if (err) return err; + } + if (force || NEQ(stream_insert_nav_packets)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, + 7, new->stream_insert_nav_packets); + if (err) return err; + } + return 0; +} +EXPORT_SYMBOL(cx2341x_update); + +static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id) +{ + const char * const *menu = cx2341x_ctrl_get_menu(p, id); + struct v4l2_ext_control ctrl; + + if (menu == NULL) + goto invalid; + ctrl.id = id; + if (cx2341x_get_ctrl(p, &ctrl)) + goto invalid; + while (ctrl.value-- && *menu) menu++; + if (*menu == NULL) + goto invalid; + return *menu; + +invalid: + return ""; +} + +void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) +{ + int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + + /* Stream */ + printk(KERN_INFO "%s: Stream: %s", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); + if (p->stream_insert_nav_packets) + printk(" (with navigation packets)"); + printk("\n"); + printk(KERN_INFO "%s: VBI Format: %s\n", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); + + /* Video */ + printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", + prefix, + p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), + p->is_50hz ? 25 : 30, + (p->video_mute) ? " (muted)" : ""); + printk(KERN_INFO "%s: Video: %s, %s, %s, %d", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), + cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), + p->video_bitrate); + if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + printk(", Peak %d", p->video_bitrate_peak); + printk("\n"); + printk(KERN_INFO + "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", + prefix, + p->video_gop_size, p->video_b_frames, + p->video_gop_closure ? "" : "No "); + if (p->video_temporal_decimation) + printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", + prefix, p->video_temporal_decimation); + + /* Audio */ + printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), + cx2341x_menu_item(p, + p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3 + ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE + : V4L2_CID_MPEG_AUDIO_L2_BITRATE), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), + p->audio_mute ? " (muted)" : ""); + if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) + printk(", %s", cx2341x_menu_item(p, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); + printk(", %s, %s\n", + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS), + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); + + /* Encoding filters */ + printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), + p->video_spatial_filter); + + printk(KERN_INFO "%s: Temporal Filter: %s, %d\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), + p->video_temporal_filter); + printk(KERN_INFO + "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", + prefix, + cx2341x_menu_item(p, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), + p->video_luma_median_filter_bottom, + p->video_luma_median_filter_top, + p->video_chroma_median_filter_bottom, + p->video_chroma_median_filter_top); +} +EXPORT_SYMBOL(cx2341x_log_status); + + + +/********************** NEW CODE *********************/ + +static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct cx2341x_handler, hdl); +} + +static int cx2341x_hdl_api(struct cx2341x_handler *hdl, + u32 cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i; + + va_start(vargs, args); + + for (i = 0; i < args; i++) + data[i] = va_arg(vargs, int); + va_end(vargs); + return hdl->func(hdl->priv, cmd, args, 0, data); +} + +/* ctrl->handler->lock is held, so it is safe to access cur.val */ +static inline int cx2341x_neq(struct v4l2_ctrl *ctrl) +{ + return ctrl && ctrl->val != ctrl->cur.val; +} + +static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_B_FRAMES: { + /* video gop cluster */ + int b = val + 1; + int gop = hdl->video_gop_size->val; + + gop = b * ((gop + b - 1) / b); + + /* Max GOP size = 34 */ + while (gop > 34) + gop -= b; + hdl->video_gop_size->val = gop; + break; + } + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + hdl->video_encoding->val = + (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : + V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + /* MPEG-1 implies CBR */ + hdl->video_bitrate_mode->val = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + /* peak bitrate shall be >= normal bitrate */ + if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + hdl->video_bitrate_peak->val < hdl->video_bitrate->val) + hdl->video_bitrate_peak->val = hdl->video_bitrate->val; + break; + } + return 0; +} + +static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const int mpeg_stream_type[] = { + 0, /* MPEG-2 PS */ + 1, /* MPEG-2 TS */ + 2, /* MPEG-1 SS */ + 14, /* DVD */ + 11, /* VCD */ + 12, /* SVCD */ + }; + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + u32 props; + int err; + + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (hdl->ops && hdl->ops->s_stream_vbi_fmt) + return hdl->ops->s_stream_vbi_fmt(hdl, val); + return 0; + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val); + + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val); + + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + /* audio properties cluster */ + props = (hdl->audio_sampling_freq->val << 0) | + (hdl->audio_mode->val << 8) | + (hdl->audio_mode_extension->val << 10) | + (hdl->audio_crc->val << 14); + if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) + props |= 3 << 12; + else + props |= hdl->audio_emphasis->val << 12; + + if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) { + props |= +#if 1 + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | +#endif + (hdl->audio_ac3_bitrate->val << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + props |= + ((3 - hdl->audio_encoding->val) << 2) | + ((1 + hdl->audio_l2_bitrate->val) << 4); + } + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props); + if (err) + return err; + + hdl->audio_properties = props; + if (hdl->audio_ac3_bitrate) { + int is_ac3 = hdl->audio_encoding->val == + V4L2_MPEG_AUDIO_ENCODING_AC3; + + v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3); + v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3); + } + v4l2_ctrl_activate(hdl->audio_mode_extension, + hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO); + if (cx2341x_neq(hdl->audio_sampling_freq) && + hdl->ops && hdl->ops->s_audio_sampling_freq) + return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val); + if (cx2341x_neq(hdl->audio_mode) && + hdl->ops && hdl->ops->s_audio_mode) + return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + /* video gop cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2, + hdl->video_gop_size->val, + hdl->video_b_frames->val + 1); + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]); + if (err) + return err; + + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5, + hdl->video_bitrate_mode->val, + hdl->video_bitrate->val, + hdl->video_bitrate_peak->val / 400, 0, 0); + if (err) + return err; + + v4l2_ctrl_activate(hdl->video_bitrate_mode, + hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1); + v4l2_ctrl_activate(hdl->video_bitrate_peak, + hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + if (cx2341x_neq(hdl->video_encoding) && + hdl->ops && hdl->ops->s_video_encoding) + return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_MUTE: + /* video mute cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1, + hdl->video_mute->val | + (hdl->video_mute_yuv->val << 8)); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: { + int active_filter; + + /* video filter mode */ + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, + hdl->video_spatial_filter_mode->val | + (hdl->video_temporal_filter_mode->val << 1), + hdl->video_median_filter_type->val); + if (err) + return err; + + active_filter = hdl->video_spatial_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter); + v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter); + active_filter = hdl->video_temporal_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter); + active_filter = hdl->video_median_filter_type->val != + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF; + v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter); + return 0; + } + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + /* video filter type cluster */ + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, + hdl->video_luma_spatial_filter_type->val, + hdl->video_chroma_spatial_filter_type->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + /* video filter cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, + hdl->video_spatial_filter->val, + hdl->video_temporal_filter->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + /* video median cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4, + hdl->video_luma_median_filter_bottom->val, + hdl->video_luma_median_filter_top->val, + hdl->video_chroma_median_filter_bottom->val, + hdl->video_chroma_median_filter_top->val); + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops cx2341x_ops = { + .try_ctrl = cx2341x_try_ctrl, + .s_ctrl = cx2341x_s_ctrl, +}; + +static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + struct v4l2_ctrl_config cfg; + + cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags); + cfg.ops = &cx2341x_ops; + cfg.id = id; + cfg.min = min; + cfg.max = max; + cfg.def = def; + if (cfg.type == V4L2_CTRL_TYPE_MENU) { + cfg.step = 0; + cfg.menu_skip_mask = step; + cfg.qmenu = cx2341x_get_menu(id); + } else { + cfg.step = step; + cfg.menu_skip_mask = 0; + } + return v4l2_ctrl_new_custom(hdl, &cfg, NULL); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl, + u32 id, s32 max, s32 mask, s32 def) +{ + return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def); +} + +int cx2341x_handler_init(struct cx2341x_handler *cxhdl, + unsigned nr_of_controls_hint) +{ + struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; + u32 caps = cxhdl->capabilities; + int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI; + int has_ac3 = caps & CX2341X_CAP_HAS_AC3; + int has_ts = caps & CX2341X_CAP_HAS_TS; + + cxhdl->width = 720; + cxhdl->height = 480; + + v4l2_ctrl_handler_init(hdl, nr_of_controls_hint); + + /* Add controls in ascending control ID order for fastest + insertion time. */ + cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_VBI_FMT, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff, + V4L2_MPEG_AUDIO_L2_BITRATE_224K); + cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE, + V4L2_MPEG_AUDIO_MODE_MONO, 0, + V4L2_MPEG_AUDIO_MODE_STEREO); + cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_EMPHASIS, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_CRC, + V4L2_MPEG_AUDIO_CRC_CRC16, 0, + V4L2_MPEG_AUDIO_CRC_NONE); + + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0); + if (has_ac3) + cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K); + cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_221x100, 0, + V4L2_MPEG_VIDEO_ASPECT_4x3); + cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2); + cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 34, 1, cxhdl->is_50hz ? 12 : 15); + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); + cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, 27000000, 1, 6000000); + cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, 27000000, 1, 8000000); + cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0); + cxhdl->video_mute = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0); + cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080); + + /* CX23415/6 specific */ + cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); + cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + 0, 15, 1, 0); + cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, + 0, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + 0, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, + 0, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); + cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + 0, 31, 1, 8); + cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, + 0, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); + cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, + 0, 1, 1, 0); + + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + return err; + } + + v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq); + v4l2_ctrl_cluster(2, &cxhdl->video_b_frames); + v4l2_ctrl_cluster(5, &cxhdl->stream_type); + v4l2_ctrl_cluster(2, &cxhdl->video_mute); + v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode); + v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type); + v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter); + v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top); + + return 0; +} +EXPORT_SYMBOL(cx2341x_handler_init); + +void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz) +{ + cxhdl->is_50hz = is_50hz; + cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15; +} +EXPORT_SYMBOL(cx2341x_handler_set_50hz); + +int cx2341x_handler_setup(struct cx2341x_handler *cxhdl) +{ + int h = cxhdl->height; + int w = cxhdl->width; + int err; + + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0); + if (err) + return err; + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz); + if (err) + return err; + + if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { + w /= 2; + h /= 2; + } + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); + if (err) + return err; + return v4l2_ctrl_handler_setup(&cxhdl->hdl); +} +EXPORT_SYMBOL(cx2341x_handler_setup); + +void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy) +{ + v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy); + v4l2_ctrl_grab(cxhdl->audio_encoding, busy); + v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy); + v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy); + v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy); + v4l2_ctrl_grab(cxhdl->stream_type, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy); +} +EXPORT_SYMBOL(cx2341x_handler_set_busy); diff --git a/drivers/media/i2c/cx25840/Kconfig b/drivers/media/i2c/cx25840/Kconfig new file mode 100644 index 000000000000..451133ad41ff --- /dev/null +++ b/drivers/media/i2c/cx25840/Kconfig @@ -0,0 +1,8 @@ +config VIDEO_CX25840 + tristate "Conexant CX2584x audio/video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Conexant CX2584x audio/video decoders. + + To compile this driver as a module, choose M here: the + module will be called cx25840 diff --git a/drivers/media/i2c/cx25840/Makefile b/drivers/media/i2c/cx25840/Makefile new file mode 100644 index 000000000000..898eb13340ae --- /dev/null +++ b/drivers/media/i2c/cx25840/Makefile @@ -0,0 +1,6 @@ +cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ + cx25840-vbi.o cx25840-ir.o + +obj-$(CONFIG_VIDEO_CX25840) += cx25840.o + +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/i2c/cx25840/cx25840-audio.c b/drivers/media/i2c/cx25840/cx25840-audio.c new file mode 100644 index 000000000000..34b96c7cfd62 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-audio.c @@ -0,0 +1,571 @@ +/* cx25840 audio functions + * + * This program is free software; 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 +#include +#include +#include + +#include "cx25840-core.h" + +/* + * Note: The PLL and SRC parameters are based on a reference frequency that + * would ideally be: + * + * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz + * + * However, it's not the exact reference frequency that matters, only that the + * firmware and modules that comprise the driver for a particular board all + * use the same value (close to the ideal value). + * + * Comments below will note which reference frequency is assumed for various + * parameters. They will usually be one of + * + * ref_freq = 28.636360 MHz + * or + * ref_freq = 28.636363 MHz + */ + +static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1006040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x1bb39ee + * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 + * 196.6 MHz pre-postdivide + * FIXME < 200 MHz is out of specified valid range + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x01bb39ee); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ + cx25840_write4(client, 0x900, 0x0801f77f); + cx25840_write4(client, 0x904, 0x0801f77f); + cx25840_write4(client, 0x90c, 0x0801f77f); + break; + + case 44100: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1009040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ + cx25840_write4(client, 0x900, 0x08016d59); + cx25840_write4(client, 0x904, 0x08016d59); + cx25840_write4(client, 0x90c, 0x08016d59); + break; + + case 48000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x100a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + case 32000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e + */ + cx25840_write4(client, 0x108, 0x1e08040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x12a0869 + * 28636363 * 0x8.9504348/0x1e = 32000 * 256 + * 246 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x012a0869); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x54); + + if (is_cx2583x(state)) + break; + + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ + cx25840_write4(client, 0x8f8, 0x08010000); + + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ + cx25840_write4(client, 0x900, 0x08020000); + cx25840_write4(client, 0x904, 0x08020000); + cx25840_write4(client, 0x90c, 0x08020000); + break; + + case 44100: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x1809040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ + cx25840_write4(client, 0x8f8, 0x080160cd); + + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ + cx25840_write4(client, 0x900, 0x08017385); + cx25840_write4(client, 0x904, 0x08017385); + cx25840_write4(client, 0x90c, 0x08017385); + break; + + case 48000: + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x180a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; + + /* src1_ctl */ + /* 0x1.8000 = 48000/32000 */ + cx25840_write4(client, 0x8f8, 0x08018000); + + /* src3/4/6_ctl */ + /* 0x1.5555 = 2 * (32000/48000) */ + cx25840_write4(client, 0x900, 0x08015555); + cx25840_write4(client, 0x904, 0x08015555); + cx25840_write4(client, 0x90c, 0x08015555); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + return cx25840_set_audclk_freq(client, freq); +} + +static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + case 44100: + case 48000: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + } + } else { + switch (freq) { + case 32000: + case 44100: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ + cx25840_write4(client, 0x900, 0x0801f77f); + cx25840_write4(client, 0x904, 0x0801f77f); + cx25840_write4(client, 0x90c, 0x0801f77f); + break; + + case 44100: + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ + cx25840_write4(client, 0x900, 0x08016d59); + cx25840_write4(client, 0x904, 0x08016d59); + cx25840_write4(client, 0x90c, 0x08016d59); + break; + + case 48000: + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + /* FIXME These cases make different assumptions about audclk */ + case 32000: + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ + cx25840_write4(client, 0x8f8, 0x08010000); + + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ + cx25840_write4(client, 0x900, 0x08020000); + cx25840_write4(client, 0x904, 0x08020000); + cx25840_write4(client, 0x90c, 0x08020000); + break; + + case 44100: + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ + cx25840_write4(client, 0x8f8, 0x080160cd); + + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ + cx25840_write4(client, 0x900, 0x08017385); + cx25840_write4(client, 0x904, 0x08017385); + cx25840_write4(client, 0x90c, 0x08017385); + break; + + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static int set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (freq != 32000 && freq != 44100 && freq != 48000) + return -EINVAL; + + if (is_cx231xx(state)) + return cx231xx_set_audclk_freq(client, freq); + + if (is_cx2388x(state)) + return cx23885_set_audclk_freq(client, freq); + + if (is_cx2583x(state)) + return cx25836_set_audclk_freq(client, freq); + + return cx25840_set_audclk_freq(client, freq); +} + +void cx25840_audio_set_path(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (!is_cx2583x(state)) { + /* assert soft reset */ + cx25840_and_or(client, 0x810, ~0x1, 0x01); + + /* stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0); + + /* Mute everything to prevent the PFFT! */ + cx25840_write(client, 0x8d3, 0x1f); + + if (state->aud_input == CX25840_AUDIO_SERIAL) { + /* Set Path1 to Serial Audio Input */ + cx25840_write4(client, 0x8d0, 0x01011012); + + /* The microcontroller should not be started for the + * non-tuner inputs: autodetection is specific for + * TV audio. */ + } else { + /* Set Path1 to Analog Demod Main Channel */ + cx25840_write4(client, 0x8d0, 0x1f063870); + } + } + + set_audclk_freq(client, state->audclk_freq); + + if (!is_cx2583x(state)) { + if (state->aud_input != CX25840_AUDIO_SERIAL) { + /* When the microcontroller detects the + * audio format, it will unmute the lines */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } + + /* deassert soft reset */ + cx25840_and_or(client, 0x810, ~0x1, 0x00); + + /* Ensure the controller is running when we exit */ + if (is_cx2388x(state) || is_cx231xx(state)) + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } +} + +static void set_volume(struct i2c_client *client, int volume) +{ + int vol; + + /* Convert the volume to msp3400 values (0-127) */ + vol = volume >> 9; + + /* now scale it up to cx25840 values + * -114dB to -96dB maps to 0 + * this should be 19, but in my testing that was 4dB too loud */ + if (vol <= 23) { + vol = 0; + } else { + vol -= 23; + } + + /* PATH1_VOLUME */ + cx25840_write(client, 0x8d4, 228 - (vol * 2)); +} + +static void set_balance(struct i2c_client *client, int balance) +{ + int bal = balance >> 8; + if (bal > 0x80) { + /* PATH1_BAL_LEFT */ + cx25840_and_or(client, 0x8d5, 0x7f, 0x80); + /* PATH1_BAL_LEVEL */ + cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f); + } else { + /* PATH1_BAL_LEFT */ + cx25840_and_or(client, 0x8d5, 0x7f, 0x00); + /* PATH1_BAL_LEVEL */ + cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal); + } +} + +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int retval; + + if (!is_cx2583x(state)) + cx25840_and_or(client, 0x810, ~0x1, 1); + if (state->aud_input != CX25840_AUDIO_SERIAL) { + cx25840_and_or(client, 0x803, ~0x10, 0); + cx25840_write(client, 0x8d3, 0x1f); + } + retval = set_audclk_freq(client, freq); + if (state->aud_input != CX25840_AUDIO_SERIAL) + cx25840_and_or(client, 0x803, ~0x10, 0x10); + if (!is_cx2583x(state)) + cx25840_and_or(client, 0x810, ~0x1, 0); + return retval; +} + +static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + if (state->mute->val) + set_volume(client, 0); + else + set_volume(client, state->volume->val); + break; + case V4L2_CID_AUDIO_BASS: + /* PATH1_EQ_BASS_VOL */ + cx25840_and_or(client, 0x8d9, ~0x3f, + 48 - (ctrl->val * 48 / 0xffff)); + break; + case V4L2_CID_AUDIO_TREBLE: + /* PATH1_EQ_TREBLE_VOL */ + cx25840_and_or(client, 0x8db, ~0x3f, + 48 - (ctrl->val * 48 / 0xffff)); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(client, ctrl->val); + break; + default: + return -EINVAL; + } + return 0; +} + +const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { + .s_ctrl = cx25840_audio_s_ctrl, +}; diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c new file mode 100644 index 000000000000..d8eac3e30a7e --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -0,0 +1,5340 @@ +/* cx25840 - Conexant CX25840 audio/video decoder driver + * + * Copyright (C) 2004 Ulf Eklund + * + * Based on the saa7115 driver and on the first version of Chris Kennedy's + * cx25840 driver. + * + * Changes by Tyler Trafford + * - cleanup/rewrite for V4L2 API (2005) + * + * VBI support by Hans Verkuil . + * + * NTSC sliced VBI support by Christopher Neufeld + * with additional fixes by Hans Verkuil . + * + * CX23885 support by Steven Toth . + * + * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are + * Copyright (C) 2010 Andy Walls + * + * CX23888 DIF support for the HVR1850 + * Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cx25840-core.h" + +MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); +MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); +MODULE_LICENSE("GPL"); + +#define CX25840_VID_INT_STAT_REG 0x410 +#define CX25840_VID_INT_STAT_BITS 0x0000ffff +#define CX25840_VID_INT_MASK_BITS 0xffff0000 +#define CX25840_VID_INT_MASK_SHFT 16 +#define CX25840_VID_INT_MASK_REG 0x412 + +#define CX23885_AUD_MC_INT_MASK_REG 0x80c +#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000 +#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff +#define CX23885_AUD_MC_INT_STAT_SHFT 16 + +#define CX25840_AUD_INT_CTRL_REG 0x812 +#define CX25840_AUD_INT_STAT_REG 0x813 + +#define CX23885_PIN_CTRL_IRQ_REG 0x123 +#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40 +#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20 +#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10 + +#define CX25840_IR_STATS_REG 0x210 +#define CX25840_IR_IRQEN_REG 0x214 + +static int cx25840_debug; + +module_param_named(debug,cx25840_debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); + + +/* ----------------------------------------------------------------------- */ +static void cx23888_std_setup(struct i2c_client *client); + +int cx25840_write(struct i2c_client *client, u16 addr, u8 value) +{ + u8 buffer[3]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + buffer[2] = value; + return i2c_master_send(client, buffer, 3); +} + +int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) +{ + u8 buffer[6]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + buffer[2] = value & 0xff; + buffer[3] = (value >> 8) & 0xff; + buffer[4] = (value >> 16) & 0xff; + buffer[5] = value >> 24; + return i2c_master_send(client, buffer, 6); +} + +u8 cx25840_read(struct i2c_client * client, u16 addr) +{ + struct i2c_msg msgs[2]; + u8 tx_buf[2], rx_buf[1]; + + /* Write register address */ + tx_buf[0] = addr >> 8; + tx_buf[1] = addr & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (char *) tx_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = (char *) rx_buf; + + if (i2c_transfer(client->adapter, msgs, 2) < 2) + return 0; + + return rx_buf[0]; +} + +u32 cx25840_read4(struct i2c_client * client, u16 addr) +{ + struct i2c_msg msgs[2]; + u8 tx_buf[2], rx_buf[4]; + + /* Write register address */ + tx_buf[0] = addr >> 8; + tx_buf[1] = addr & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (char *) tx_buf; + + /* Read data from registers */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 4; + msgs[1].buf = (char *) rx_buf; + + if (i2c_transfer(client->adapter, msgs, 2) < 2) + return 0; + + return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) | + rx_buf[0]; +} + +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, + u8 or_value) +{ + return cx25840_write(client, addr, + (cx25840_read(client, addr) & and_mask) | + or_value); +} + +int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, + u32 or_value) +{ + return cx25840_write4(client, addr, + (cx25840_read4(client, addr) & and_mask) | + or_value); +} + +/* ----------------------------------------------------------------------- */ + +static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input); + +/* ----------------------------------------------------------------------- */ + +static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, + struct v4l2_subdev_io_pin_config *p) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + u32 pin_ctrl; + u8 gpio_oe, gpio_data, strength; + + pin_ctrl = cx25840_read4(client, 0x120); + gpio_oe = cx25840_read(client, 0x160); + gpio_data = cx25840_read(client, 0x164); + + for (i = 0; i < n; i++) { + strength = p[i].strength; + if (strength > CX25840_PIN_DRIVE_FAST) + strength = CX25840_PIN_DRIVE_FAST; + + switch (p[i].pin) { + case CX23885_PIN_IRQ_N_GPIO16: + if (p[i].function != CX23885_PAD_IRQ_N) { + /* GPIO16 */ + pin_ctrl &= ~(0x1 << 25); + } else { + /* IRQ_N */ + if (p[i].flags & + (V4L2_SUBDEV_IO_PIN_DISABLE | + V4L2_SUBDEV_IO_PIN_INPUT)) { + pin_ctrl &= ~(0x1 << 25); + } else { + pin_ctrl |= (0x1 << 25); + } + if (p[i].flags & + V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) { + pin_ctrl &= ~(0x1 << 24); + } else { + pin_ctrl |= (0x1 << 24); + } + } + break; + case CX23885_PIN_IR_RX_GPIO19: + if (p[i].function != CX23885_PAD_GPIO19) { + /* IR_RX */ + gpio_oe |= (0x1 << 0); + pin_ctrl &= ~(0x3 << 18); + pin_ctrl |= (strength << 18); + } else { + /* GPIO19 */ + gpio_oe &= ~(0x1 << 0); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 0); + gpio_data |= ((p[i].value & 0x1) << 0); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_IR_TX_GPIO20: + if (p[i].function != CX23885_PAD_GPIO20) { + /* IR_TX */ + gpio_oe |= (0x1 << 1); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE) + pin_ctrl &= ~(0x1 << 10); + else + pin_ctrl |= (0x1 << 10); + pin_ctrl &= ~(0x3 << 18); + pin_ctrl |= (strength << 18); + } else { + /* GPIO20 */ + gpio_oe &= ~(0x1 << 1); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 1); + gpio_data |= ((p[i].value & 0x1) << 1); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_SDAT_GPIO21: + if (p[i].function != CX23885_PAD_GPIO21) { + /* I2S_SDAT */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 2); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO21 */ + gpio_oe &= ~(0x1 << 2); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 2); + gpio_data |= ((p[i].value & 0x1) << 2); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_WCLK_GPIO22: + if (p[i].function != CX23885_PAD_GPIO22) { + /* I2S_WCLK */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 3); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO22 */ + gpio_oe &= ~(0x1 << 3); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 3); + gpio_data |= ((p[i].value & 0x1) << 3); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_BCLK_GPIO23: + if (p[i].function != CX23885_PAD_GPIO23) { + /* I2S_BCLK */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 4); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO23 */ + gpio_oe &= ~(0x1 << 4); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 4); + gpio_data |= ((p[i].value & 0x1) << 4); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + } + } + + cx25840_write(client, 0x164, gpio_data); + cx25840_write(client, 0x160, gpio_oe); + cx25840_write4(client, 0x120, pin_ctrl); + return 0; +} + +static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, + struct v4l2_subdev_io_pin_config *pincfg) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx2388x(state)) + return cx23885_s_io_pin_config(sd, n, pincfg); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void init_dll1(struct i2c_client *client) +{ + /* This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 1 (ADC DLL). */ + cx25840_write(client, 0x159, 0x23); + cx25840_write(client, 0x15a, 0x87); + cx25840_write(client, 0x15b, 0x06); + udelay(10); + cx25840_write(client, 0x159, 0xe1); + udelay(10); + cx25840_write(client, 0x15a, 0x86); + cx25840_write(client, 0x159, 0xe0); + cx25840_write(client, 0x159, 0xe1); + cx25840_write(client, 0x15b, 0x10); +} + +static void init_dll2(struct i2c_client *client) +{ + /* This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 2 (ADC DLL). */ + cx25840_write(client, 0x15d, 0xe3); + cx25840_write(client, 0x15e, 0x86); + cx25840_write(client, 0x15f, 0x06); + udelay(10); + cx25840_write(client, 0x15d, 0xe1); + cx25840_write(client, 0x15d, 0xe0); + cx25840_write(client, 0x15d, 0xe1); +} + +static void cx25836_initialize(struct i2c_client *client) +{ + /* reset configuration is described on page 3-77 of the CX25836 datasheet */ + /* 2. */ + cx25840_and_or(client, 0x000, ~0x01, 0x01); + cx25840_and_or(client, 0x000, ~0x01, 0x00); + /* 3a. */ + cx25840_and_or(client, 0x15a, ~0x70, 0x00); + /* 3b. */ + cx25840_and_or(client, 0x15b, ~0x1e, 0x06); + /* 3c. */ + cx25840_and_or(client, 0x159, ~0x02, 0x02); + /* 3d. */ + udelay(10); + /* 3e. */ + cx25840_and_or(client, 0x159, ~0x02, 0x00); + /* 3f. */ + cx25840_and_or(client, 0x159, ~0xc0, 0xc0); + /* 3g. */ + cx25840_and_or(client, 0x159, ~0x01, 0x00); + cx25840_and_or(client, 0x159, ~0x01, 0x01); + /* 3h. */ + cx25840_and_or(client, 0x15b, ~0x1e, 0x10); +} + +static void cx25840_work_handler(struct work_struct *work) +{ + struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work); + cx25840_loadfw(state->c); + wake_up(&state->fw_wait); +} + +static void cx25840_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* datasheet startup in numbered steps, refer to page 3-77 */ + /* 2. */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + /* The default of this register should be 4, but I get 0 instead. + * Set this register to 4 manually. */ + cx25840_write(client, 0x000, 0x04); + /* 3. */ + init_dll1(client); + init_dll2(client); + cx25840_write(client, 0x136, 0x0a); + /* 4. */ + cx25840_write(client, 0x13c, 0x01); + cx25840_write(client, 0x13c, 0x00); + /* 5. */ + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ + INIT_WORK(&state->fw_work, cx25840_work_handler); + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + /* 6. */ + cx25840_write(client, 0x115, 0x8c); + cx25840_write(client, 0x116, 0x07); + cx25840_write(client, 0x118, 0x02); + /* 7. */ + cx25840_write(client, 0x4a5, 0x80); + cx25840_write(client, 0x4a5, 0x00); + cx25840_write(client, 0x402, 0x00); + /* 8. */ + cx25840_and_or(client, 0x401, ~0x18, 0); + cx25840_and_or(client, 0x4a2, ~0x10, 0x10); + /* steps 8c and 8d are done in change_input() */ + /* 10. */ + cx25840_write(client, 0x8d3, 0x1f); + cx25840_write(client, 0x8e3, 0x03); + + cx25840_std_setup(client); + + /* trial and error says these are needed to get audio */ + cx25840_write(client, 0x914, 0xa0); + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); + + /* stereo preferred */ + cx25840_write(client, 0x809, 0x04); + /* AC97 shift */ + cx25840_write(client, 0x8cf, 0x0f); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); +} + +static void cx23885_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* + * Come out of digital power down + * The CX23888, at least, needs this, otherwise registers aside from + * 0x0-0x2 can't be read or written. + */ + cx25840_write(client, 0x000, 0); + + /* Internal Reset */ + cx25840_and_or(client, 0x102, ~0x01, 0x01); + cx25840_and_or(client, 0x102, ~0x01, 0x00); + + /* Stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + + /* DIF in reset? */ + cx25840_write(client, 0x398, 0); + + /* + * Trust the default xtal, no division + * '885: 28.636363... MHz + * '887: 25.000000 MHz + * '888: 50.000000 MHz + */ + cx25840_write(client, 0x2, 0x76); + + /* Power up all the PLL's and DLL */ + cx25840_write(client, 0x1, 0x40); + + /* Sys PLL */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + /* HVR1850 or 50MHz xtal */ + cx25840_write(client, 0x2, 0x71); + cx25840_write4(client, 0x11c, 0x01d1744c); + cx25840_write4(client, 0x118, 0x00000416); + cx25840_write4(client, 0x404, 0x0010253e); + cx25840_write4(client, 0x42c, 0x42600000); + cx25840_write4(client, 0x44c, 0x161f1000); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x01d1744c); + cx25840_write4(client, 0x118, 0x00000416); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00000000); + cx25840_write4(client, 0x118, 0x00000414); + break; + } + + /* Disable DIF bypass */ + cx25840_write4(client, 0x33c, 0x00000001); + + /* DIF Src phase inc */ + cx25840_write4(client, 0x340, 0x0df7df83); + + /* + * Vid PLL + * Setup for a BT.656 pixel clock of 13.5 Mpixels/second + * + * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz + * 432.0 MHz before post divide + */ + + /* HVR1850 */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* 888/HVR1250 specific */ + cx25840_write4(client, 0x10c, 0x13333333); + cx25840_write4(client, 0x108, 0x00000515); + break; + default: + cx25840_write4(client, 0x10c, 0x002be2c9); + cx25840_write4(client, 0x108, 0x0000040f); + } + + /* Luma */ + cx25840_write4(client, 0x414, 0x00107d12); + + /* Chroma */ + cx25840_write4(client, 0x420, 0x3d008282); + + /* + * Aux PLL + * Initial setup for audio sample clock: + * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz + * Initial I2S output/master clock(?): + * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz + */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + /* HVR1850 or 50MHz xtal */ + cx25840_write4(client, 0x114, 0x017dbf48); + cx25840_write4(client, 0x110, 0x000a030e); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x017dbf48); + cx25840_write4(client, 0x110, 0x000a030e); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x01bf0c9e); + cx25840_write4(client, 0x110, 0x000a030c); + break; + }; + + /* ADC2 input select */ + cx25840_write(client, 0x102, 0x10); + + /* VIN1 & VIN5 */ + cx25840_write(client, 0x103, 0x11); + + /* Enable format auto detect */ + cx25840_write(client, 0x400, 0); + /* Fast subchroma lock */ + /* White crush, Chroma AGC & Chroma Killer enabled */ + cx25840_write(client, 0x401, 0xe8); + + /* Select AFE clock pad output source */ + cx25840_write(client, 0x144, 0x05); + + /* Drive GPIO2 direction and values for HVR1700 + * where an onboard mux selects the output of demodulator + * vs the 417. Failure to set this results in no DTV. + * It's safe to set this across all Hauppauge boards + * currently, regardless of the board type. + */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ + INIT_WORK(&state->fw_work, cx25840_work_handler); + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + /* Call the cx23888 specific std setup func, we no longer rely on + * the generic cx24840 func. + */ + if (is_cx23888(state)) + cx23888_std_setup(client); + else + cx25840_std_setup(client); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + + /* Disable and clear video interrupts - we don't use them */ + cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff); + + /* Disable and clear audio interrupts - we don't use them */ + cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff); + cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff); + + /* CC raw enable */ + /* - VIP 1.1 control codes - 10bit, blue field enable. + * - enable raw data during vertical blanking. + * - enable ancillary Data insertion for 656 or VIP. + */ + cx25840_write4(client, 0x404, 0x0010253e); + + /* CC on - Undocumented Register */ + cx25840_write(client, 0x42f, 0x66); + + /* HVR-1250 / HVR1850 DIF related */ + /* Power everything up */ + cx25840_write4(client, 0x130, 0x0); + + /* Undocumented */ + cx25840_write4(client, 0x478, 0x6628021F); + + /* AFE_CLK_OUT_CTRL - Select the clock output source as output */ + cx25840_write4(client, 0x144, 0x5); + + /* I2C_OUT_CTL - I2S output configuration as + * Master, Sony, Left justified, left sample on WS=1 + */ + cx25840_write4(client, 0x918, 0x1a0); + + /* AFE_DIAG_CTRL1 */ + cx25840_write4(client, 0x134, 0x000a1800); + + /* AFE_DIAG_CTRL3 - Inverted Polarity for Audio and Video */ + cx25840_write4(client, 0x13c, 0x00310000); +} + +/* ----------------------------------------------------------------------- */ + +static void cx231xx_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* Internal Reset */ + cx25840_and_or(client, 0x102, ~0x01, 0x01); + cx25840_and_or(client, 0x102, ~0x01, 0x00); + + /* Stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + + /* DIF in reset? */ + cx25840_write(client, 0x398, 0); + + /* Trust the default xtal, no division */ + /* This changes for the cx23888 products */ + cx25840_write(client, 0x2, 0x76); + + /* Bring down the regulator for AUX clk */ + cx25840_write(client, 0x1, 0x40); + + /* Disable DIF bypass */ + cx25840_write4(client, 0x33c, 0x00000001); + + /* DIF Src phase inc */ + cx25840_write4(client, 0x340, 0x0df7df83); + + /* Luma */ + cx25840_write4(client, 0x414, 0x00107d12); + + /* Chroma */ + cx25840_write4(client, 0x420, 0x3d008282); + + /* ADC2 input select */ + cx25840_write(client, 0x102, 0x10); + + /* VIN1 & VIN5 */ + cx25840_write(client, 0x103, 0x11); + + /* Enable format auto detect */ + cx25840_write(client, 0x400, 0); + /* Fast subchroma lock */ + /* White crush, Chroma AGC & Chroma Killer enabled */ + cx25840_write(client, 0x401, 0xe8); + + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ + INIT_WORK(&state->fw_work, cx25840_work_handler); + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + cx25840_std_setup(client); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + + /* CC raw enable */ + cx25840_write(client, 0x404, 0x0b); + + /* CC on */ + cx25840_write(client, 0x42f, 0x66); + cx25840_write4(client, 0x474, 0x1e1e601a); +} + +/* ----------------------------------------------------------------------- */ + +void cx25840_std_setup(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + v4l2_std_id std = state->std; + int hblank, hactive, burst, vblank, vactive, sc; + int vblank656, src_decimation; + int luma_lpf, uv_lpf, comb; + u32 pll_int, pll_frac, pll_post; + + /* datasheet startup, step 8d */ + if (std & ~V4L2_STD_NTSC) + cx25840_write(client, 0x49f, 0x11); + else + cx25840_write(client, 0x49f, 0x14); + + if (std & V4L2_STD_625_50) { + hblank = 132; + hactive = 720; + burst = 93; + vblank = 36; + vactive = 580; + vblank656 = 40; + src_decimation = 0x21f; + luma_lpf = 2; + + if (std & V4L2_STD_SECAM) { + uv_lpf = 0; + comb = 0; + sc = 0x0a425f; + } else if (std == V4L2_STD_PAL_Nc) { + uv_lpf = 1; + comb = 0x20; + sc = 556453; + } else { + uv_lpf = 1; + comb = 0x20; + sc = 688739; + } + } else { + hactive = 720; + hblank = 122; + vactive = 487; + luma_lpf = 1; + uv_lpf = 1; + + src_decimation = 0x21f; + if (std == V4L2_STD_PAL_60) { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + luma_lpf = 2; + comb = 0x20; + sc = 688739; + } else if (std == V4L2_STD_PAL_M) { + vblank = 20; + vblank656 = 24; + burst = 0x61; + comb = 0x20; + sc = 555452; + } else { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + comb = 0x66; + sc = 556063; + } + } + + /* DEBUG: Displays configured PLL frequency */ + if (!is_cx231xx(state)) { + pll_int = cx25840_read(client, 0x108); + pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; + pll_post = cx25840_read(client, 0x109); + v4l_dbg(1, cx25840_debug, client, + "PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); + + if (pll_post) { + int fin, fsc; + int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; + + pll /= pll_post; + v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); + + fin = ((u64)src_decimation * pll) >> 12; + v4l_dbg(1, cx25840_debug, client, + "ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); + + fsc = (((u64)sc) * pll) >> 24L; + v4l_dbg(1, cx25840_debug, client, + "Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " + "vblank %i, vactive %i, vblank656 %i, src_dec %i, " + "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " + "sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + } + } + + /* Sets horizontal blanking delay and active lines */ + cx25840_write(client, 0x470, hblank); + cx25840_write(client, 0x471, + 0xff & (((hblank >> 8) & 0x3) | (hactive << 4))); + cx25840_write(client, 0x472, hactive >> 4); + + /* Sets burst gate delay */ + cx25840_write(client, 0x473, burst); + + /* Sets vertical blanking delay and active duration */ + cx25840_write(client, 0x474, vblank); + cx25840_write(client, 0x475, + 0xff & (((vblank >> 8) & 0x3) | (vactive << 4))); + cx25840_write(client, 0x476, vactive >> 4); + cx25840_write(client, 0x477, vblank656); + + /* Sets src decimation rate */ + cx25840_write(client, 0x478, 0xff & src_decimation); + cx25840_write(client, 0x479, 0xff & (src_decimation >> 8)); + + /* Sets Luma and UV Low pass filters */ + cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); + + /* Enables comb filters */ + cx25840_write(client, 0x47b, comb); + + /* Sets SC Step*/ + cx25840_write(client, 0x47c, sc); + cx25840_write(client, 0x47d, 0xff & sc >> 8); + cx25840_write(client, 0x47e, 0xff & sc >> 16); + + /* Sets VBI parameters */ + if (std & V4L2_STD_625_50) { + cx25840_write(client, 0x47f, 0x01); + state->vbi_line_offset = 5; + } else { + cx25840_write(client, 0x47f, 0x00); + state->vbi_line_offset = 8; + } +} + +/* ----------------------------------------------------------------------- */ + +static void input_change(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + v4l2_std_id std = state->std; + + /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ + if (std & V4L2_STD_SECAM) { + cx25840_write(client, 0x402, 0); + } + else { + cx25840_write(client, 0x402, 0x04); + cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); + } + cx25840_and_or(client, 0x401, ~0x60, 0); + cx25840_and_or(client, 0x401, ~0x60, 0x60); + + /* Don't write into audio registers on cx2583x chips */ + if (is_cx2583x(state)) + return; + + cx25840_and_or(client, 0x810, ~0x01, 1); + + if (state->radio) { + cx25840_write(client, 0x808, 0xf9); + cx25840_write(client, 0x80b, 0x00); + } + else if (std & V4L2_STD_525_60) { + /* Certain Hauppauge PVR150 models have a hardware bug + that causes audio to drop out. For these models the + audio standard must be set explicitly. + To be precise: it affects cards with tuner models + 85, 99 and 112 (model numbers from tveeprom). */ + int hw_fix = state->pvr150_workaround; + + if (std == V4L2_STD_NTSC_M_JP) { + /* Japan uses EIAJ audio standard */ + cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7); + } else if (std == V4L2_STD_NTSC_M_KR) { + /* South Korea uses A2 audio standard */ + cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8); + } else { + /* Others use the BTSC audio standard */ + cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); + } + cx25840_write(client, 0x80b, 0x00); + } else if (std & V4L2_STD_PAL) { + /* Autodetect audio standard and audio system */ + cx25840_write(client, 0x808, 0xff); + /* Since system PAL-L is pretty much non-existent and + not used by any public broadcast network, force + 6.5 MHz carrier to be interpreted as System DK, + this avoids DK audio detection instability */ + cx25840_write(client, 0x80b, 0x00); + } else if (std & V4L2_STD_SECAM) { + /* Autodetect audio standard and audio system */ + cx25840_write(client, 0x808, 0xff); + /* If only one of SECAM-DK / SECAM-L is required, then force + 6.5MHz carrier, else autodetect it */ + if ((std & V4L2_STD_SECAM_DK) && + !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System DK */ + cx25840_write(client, 0x80b, 0x00); + } else if (!(std & V4L2_STD_SECAM_DK) && + (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System L */ + cx25840_write(client, 0x80b, 0x08); + } else { + /* 6.5 MHz carrier to be autodetected */ + cx25840_write(client, 0x80b, 0x10); + } + } + + cx25840_and_or(client, 0x810, ~0x01, 0); +} + +static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && + vid_input <= CX25840_COMPOSITE8); + u8 is_component = (vid_input & CX25840_COMPONENT_ON) == + CX25840_COMPONENT_ON; + u8 is_dif = (vid_input & CX25840_DIF_ON) == + CX25840_DIF_ON; + u8 is_svideo = (vid_input & CX25840_SVIDEO_ON) == + CX25840_SVIDEO_ON; + int luma = vid_input & 0xf0; + int chroma = vid_input & 0xf00; + u8 reg; + u32 val; + + v4l_dbg(1, cx25840_debug, client, + "decoder set video input %d, audio input %d\n", + vid_input, aud_input); + + if (vid_input >= CX25840_VIN1_CH1) { + v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", + vid_input); + reg = vid_input & 0xff; + is_composite = !is_component && + ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); + + v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", + reg, is_composite); + } else if (is_composite) { + reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); + } else { + if ((vid_input & ~0xff0) || + luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || + chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { + v4l_err(client, "0x%04x is not a valid video input!\n", + vid_input); + return -EINVAL; + } + reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); + if (chroma >= CX25840_SVIDEO_CHROMA7) { + reg &= 0x3f; + reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2; + } else { + reg &= 0xcf; + reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4; + } + } + + /* The caller has previously prepared the correct routing + * configuration in reg (for the cx23885) so we have no + * need to attempt to flip bits for earlier av decoders. + */ + if (!is_cx2388x(state) && !is_cx231xx(state)) { + switch (aud_input) { + case CX25840_AUDIO_SERIAL: + /* do nothing, use serial audio input */ + break; + case CX25840_AUDIO4: reg &= ~0x30; break; + case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; + case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; + case CX25840_AUDIO7: reg &= ~0xc0; break; + case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; + + default: + v4l_err(client, "0x%04x is not a valid audio input!\n", + aud_input); + return -EINVAL; + } + } + + cx25840_write(client, 0x103, reg); + + /* Set INPUT_MODE to Composite, S-Video or Component */ + if (is_component) + cx25840_and_or(client, 0x401, ~0x6, 0x6); + else + cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); + + if (is_cx2388x(state)) { + + /* Enable or disable the DIF for tuner use */ + if (is_dif) { + cx25840_and_or(client, 0x102, ~0x80, 0x80); + + /* Set of defaults for NTSC and PAL */ + cx25840_write4(client, 0x31c, 0xc2262600); + cx25840_write4(client, 0x320, 0xc2262600); + + /* 18271 IF - Nobody else yet uses a different + * tuner with the DIF, so these are reasonable + * assumptions (HVR1250 and HVR1850 specific). + */ + cx25840_write4(client, 0x318, 0xda262600); + cx25840_write4(client, 0x33c, 0x2a24c800); + cx25840_write4(client, 0x104, 0x0704dd00); + } else { + cx25840_write4(client, 0x300, 0x015c28f5); + + cx25840_and_or(client, 0x102, ~0x80, 0); + cx25840_write4(client, 0x340, 0xdf7df83); + cx25840_write4(client, 0x104, 0x0704dd80); + cx25840_write4(client, 0x314, 0x22400600); + cx25840_write4(client, 0x318, 0x40002600); + cx25840_write4(client, 0x324, 0x40002600); + cx25840_write4(client, 0x32c, 0x0250e620); + cx25840_write4(client, 0x39c, 0x01FF0B00); + + cx25840_write4(client, 0x410, 0xffff0dbf); + cx25840_write4(client, 0x414, 0x00137d03); + + /* on the 887, 0x418 is HSCALE_CTRL, on the 888 it is + CHROMA_CTRL */ + if (is_cx23888(state)) + cx25840_write4(client, 0x418, 0x01008080); + else + cx25840_write4(client, 0x418, 0x01000000); + + cx25840_write4(client, 0x41c, 0x00000000); + + /* on the 887, 0x420 is CHROMA_CTRL, on the 888 it is + CRUSH_CTRL */ + if (is_cx23888(state)) + cx25840_write4(client, 0x420, 0x001c3e0f); + else + cx25840_write4(client, 0x420, 0x001c8282); + + cx25840_write4(client, 0x42c, 0x42600000); + cx25840_write4(client, 0x430, 0x0000039b); + cx25840_write4(client, 0x438, 0x00000000); + + cx25840_write4(client, 0x440, 0xF8E3E824); + cx25840_write4(client, 0x444, 0x401040dc); + cx25840_write4(client, 0x448, 0xcd3f02a0); + cx25840_write4(client, 0x44c, 0x161f1000); + cx25840_write4(client, 0x450, 0x00000802); + + cx25840_write4(client, 0x91c, 0x01000000); + cx25840_write4(client, 0x8e0, 0x03063870); + cx25840_write4(client, 0x8d4, 0x7FFF0024); + cx25840_write4(client, 0x8d0, 0x00063073); + + cx25840_write4(client, 0x8c8, 0x00010000); + cx25840_write4(client, 0x8cc, 0x00080023); + + /* DIF BYPASS */ + cx25840_write4(client, 0x33c, 0x2a04c800); + } + + /* Reset the DIF */ + cx25840_write4(client, 0x398, 0); + } + + if (!is_cx2388x(state) && !is_cx231xx(state)) { + /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ + cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); + /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ + if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) + cx25840_and_or(client, 0x102, ~0x4, 4); + else + cx25840_and_or(client, 0x102, ~0x4, 0); + } else { + /* Set DUAL_MODE_ADC2 to 1 if component*/ + cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); + if (is_composite) { + /* ADC2 input select channel 2 */ + cx25840_and_or(client, 0x102, ~0x2, 0); + } else if (!is_component) { + /* S-Video */ + if (chroma >= CX25840_SVIDEO_CHROMA7) { + /* ADC2 input select channel 3 */ + cx25840_and_or(client, 0x102, ~0x2, 2); + } else { + /* ADC2 input select channel 2 */ + cx25840_and_or(client, 0x102, ~0x2, 0); + } + } + + /* cx23885 / SVIDEO */ + if (is_cx2388x(state) && is_svideo) { +#define AFE_CTRL (0x104) +#define MODE_CTRL (0x400) + cx25840_and_or(client, 0x102, ~0x2, 0x2); + + val = cx25840_read4(client, MODE_CTRL); + val &= 0xFFFFF9FF; + + /* YC */ + val |= 0x00000200; + val &= ~0x2000; + cx25840_write4(client, MODE_CTRL, val); + + val = cx25840_read4(client, AFE_CTRL); + + /* Chroma in select */ + val |= 0x00001000; + val &= 0xfffffe7f; + /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8). + * This sets them to use video rather than audio. + * Only one of the two will be in use. + */ + cx25840_write4(client, AFE_CTRL, val); + } else + cx25840_and_or(client, 0x102, ~0x2, 0); + } + + state->vid_input = vid_input; + state->aud_input = aud_input; + cx25840_audio_set_path(client); + input_change(client); + + if (is_cx2388x(state)) { + /* Audio channel 1 src : Parallel 1 */ + cx25840_write(client, 0x124, 0x03); + + /* Select AFE clock pad output source */ + cx25840_write(client, 0x144, 0x05); + + /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ + cx25840_write(client, 0x914, 0xa0); + + /* I2S_OUT_CTL: + * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 + * I2S_OUT_MASTER_MODE = Master + */ + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); + } else if (is_cx231xx(state)) { + /* Audio channel 1 src : Parallel 1 */ + cx25840_write(client, 0x124, 0x03); + + /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ + cx25840_write(client, 0x914, 0xa0); + + /* I2S_OUT_CTL: + * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 + * I2S_OUT_MASTER_MODE = Master + */ + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); + } + + if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) || + (aud_input == CX25840_AUDIO6))) { + /* Configure audio from LR1 or LR2 input */ + cx25840_write4(client, 0x910, 0); + cx25840_write4(client, 0x8d0, 0x63073); + } else + if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { + /* Configure audio from tuner/sif input */ + cx25840_write4(client, 0x910, 0x12b000c9); + cx25840_write4(client, 0x8d0, 0x1f063870); + } + + if (is_cx23888(state)) { + /* HVR1850 */ + /* AUD_IO_CTRL - I2S Input, Parallel1*/ + /* - Channel 1 src - Parallel1 (Merlin out) */ + /* - Channel 2 src - Parallel2 (Merlin out) */ + /* - Channel 3 src - Parallel3 (Merlin AC97 out) */ + /* - I2S source and dir - Merlin, output */ + cx25840_write4(client, 0x124, 0x100); + + if (!is_dif) { + /* Stop microcontroller if we don't need it + * to avoid audio popping on svideo/composite use. + */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + } + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int set_v4lstd(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 fmt = 0; /* zero is autodetect */ + u8 pal_m = 0; + + /* First tests should be against specific std */ + if (state->std == V4L2_STD_NTSC_M_JP) { + fmt = 0x2; + } else if (state->std == V4L2_STD_NTSC_443) { + fmt = 0x3; + } else if (state->std == V4L2_STD_PAL_M) { + pal_m = 1; + fmt = 0x5; + } else if (state->std == V4L2_STD_PAL_N) { + fmt = 0x6; + } else if (state->std == V4L2_STD_PAL_Nc) { + fmt = 0x7; + } else if (state->std == V4L2_STD_PAL_60) { + fmt = 0x8; + } else { + /* Then, test against generic ones */ + if (state->std & V4L2_STD_NTSC) + fmt = 0x1; + else if (state->std & V4L2_STD_PAL) + fmt = 0x4; + else if (state->std & V4L2_STD_SECAM) + fmt = 0xc; + } + + v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); + + /* Follow step 9 of section 3.16 in the cx25840 datasheet. + Without this PAL may display a vertical ghosting effect. + This happens for example with the Yuan MPC622. */ + if (fmt >= 4 && fmt < 8) { + /* Set format to NTSC-M */ + cx25840_and_or(client, 0x400, ~0xf, 1); + /* Turn off LCOMB */ + cx25840_and_or(client, 0x47b, ~6, 0); + } + cx25840_and_or(client, 0x400, ~0xf, fmt); + cx25840_and_or(client, 0x403, ~0x3, pal_m); + if (is_cx23888(state)) + cx23888_std_setup(client); + else + cx25840_std_setup(client); + if (!is_cx2583x(state)) + input_change(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cx25840_write(client, 0x414, ctrl->val - 128); + break; + + case V4L2_CID_CONTRAST: + cx25840_write(client, 0x415, ctrl->val << 1); + break; + + case V4L2_CID_SATURATION: + if (is_cx23888(state)) { + cx25840_write(client, 0x418, ctrl->val << 1); + cx25840_write(client, 0x419, ctrl->val << 1); + } else { + cx25840_write(client, 0x420, ctrl->val << 1); + cx25840_write(client, 0x421, ctrl->val << 1); + } + break; + + case V4L2_CID_HUE: + if (is_cx23888(state)) + cx25840_write(client, 0x41a, ctrl->val); + else + cx25840_write(client, 0x422, ctrl->val); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int HSC, VSC, Vsrc, Hsrc, filter, Vlines; + int is_50Hz = !(state->std & V4L2_STD_525_60); + + if (fmt->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + + fmt->field = V4L2_FIELD_INTERLACED; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + + if (is_cx23888(state)) { + Vsrc = (cx25840_read(client, 0x42a) & 0x3f) << 4; + Vsrc |= (cx25840_read(client, 0x429) & 0xf0) >> 4; + } else { + Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; + Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; + } + + if (is_cx23888(state)) { + Hsrc = (cx25840_read(client, 0x426) & 0x3f) << 4; + Hsrc |= (cx25840_read(client, 0x425) & 0xf0) >> 4; + } else { + Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; + Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; + } + + Vlines = fmt->height + (is_50Hz ? 4 : 7); + + if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || + (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { + v4l_err(client, "%dx%d is not a valid size!\n", + fmt->width, fmt->height); + return -ERANGE; + } + + HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); + VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); + VSC &= 0x1fff; + + if (fmt->width >= 385) + filter = 0; + else if (fmt->width > 192) + filter = 1; + else if (fmt->width > 96) + filter = 2; + else + filter = 3; + + v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", + fmt->width, fmt->height, HSC, VSC); + + /* HSCALE=HSC */ + cx25840_write(client, 0x418, HSC & 0xff); + cx25840_write(client, 0x419, (HSC >> 8) & 0xff); + cx25840_write(client, 0x41a, HSC >> 16); + /* VSCALE=VSC */ + cx25840_write(client, 0x41c, VSC & 0xff); + cx25840_write(client, 0x41d, VSC >> 8); + /* VS_INTRLACE=1 VFILT=filter */ + cx25840_write(client, 0x41e, 0x8 | filter); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void log_video_status(struct i2c_client *client) +{ + static const char *const fmt_strs[] = { + "0x0", + "NTSC-M", "NTSC-J", "NTSC-4.43", + "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", + "0x9", "0xA", "0xB", + "SECAM", + "0xD", "0xE", "0xF" + }; + + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; + u8 gen_stat1 = cx25840_read(client, 0x40d); + u8 gen_stat2 = cx25840_read(client, 0x40e); + int vid_input = state->vid_input; + + v4l_info(client, "Video signal: %spresent\n", + (gen_stat2 & 0x20) ? "" : "not "); + v4l_info(client, "Detected format: %s\n", + fmt_strs[gen_stat1 & 0xf]); + + v4l_info(client, "Specified standard: %s\n", + vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); + + if (vid_input >= CX25840_COMPOSITE1 && + vid_input <= CX25840_COMPOSITE8) { + v4l_info(client, "Specified video input: Composite %d\n", + vid_input - CX25840_COMPOSITE1 + 1); + } else { + v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", + (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); + } + + v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); +} + +/* ----------------------------------------------------------------------- */ + +static void log_audio_status(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 download_ctl = cx25840_read(client, 0x803); + u8 mod_det_stat0 = cx25840_read(client, 0x804); + u8 mod_det_stat1 = cx25840_read(client, 0x805); + u8 audio_config = cx25840_read(client, 0x808); + u8 pref_mode = cx25840_read(client, 0x809); + u8 afc0 = cx25840_read(client, 0x80b); + u8 mute_ctl = cx25840_read(client, 0x8d3); + int aud_input = state->aud_input; + char *p; + + switch (mod_det_stat0) { + case 0x00: p = "mono"; break; + case 0x01: p = "stereo"; break; + case 0x02: p = "dual"; break; + case 0x04: p = "tri"; break; + case 0x10: p = "mono with SAP"; break; + case 0x11: p = "stereo with SAP"; break; + case 0x12: p = "dual with SAP"; break; + case 0x14: p = "tri with SAP"; break; + case 0xfe: p = "forced mode"; break; + default: p = "not defined"; + } + v4l_info(client, "Detected audio mode: %s\n", p); + + switch (mod_det_stat1) { + case 0x00: p = "not defined"; break; + case 0x01: p = "EIAJ"; break; + case 0x02: p = "A2-M"; break; + case 0x03: p = "A2-BG"; break; + case 0x04: p = "A2-DK1"; break; + case 0x05: p = "A2-DK2"; break; + case 0x06: p = "A2-DK3"; break; + case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x08: p = "AM-L"; break; + case 0x09: p = "NICAM-BG"; break; + case 0x0a: p = "NICAM-DK"; break; + case 0x0b: p = "NICAM-I"; break; + case 0x0c: p = "NICAM-L"; break; + case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; + case 0x0e: p = "IF FM Radio"; break; + case 0x0f: p = "BTSC"; break; + case 0x10: p = "high-deviation FM"; break; + case 0x11: p = "very high-deviation FM"; break; + case 0xfd: p = "unknown audio standard"; break; + case 0xfe: p = "forced audio standard"; break; + case 0xff: p = "no detected audio standard"; break; + default: p = "not defined"; + } + v4l_info(client, "Detected audio standard: %s\n", p); + v4l_info(client, "Audio microcontroller: %s\n", + (download_ctl & 0x10) ? + ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); + + switch (audio_config >> 4) { + case 0x00: p = "undefined"; break; + case 0x01: p = "BTSC"; break; + case 0x02: p = "EIAJ"; break; + case 0x03: p = "A2-M"; break; + case 0x04: p = "A2-BG"; break; + case 0x05: p = "A2-DK1"; break; + case 0x06: p = "A2-DK2"; break; + case 0x07: p = "A2-DK3"; break; + case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x09: p = "AM-L"; break; + case 0x0a: p = "NICAM-BG"; break; + case 0x0b: p = "NICAM-DK"; break; + case 0x0c: p = "NICAM-I"; break; + case 0x0d: p = "NICAM-L"; break; + case 0x0e: p = "FM radio"; break; + case 0x0f: p = "automatic detection"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio standard: %s\n", p); + + if ((audio_config >> 4) < 0xF) { + switch (audio_config & 0xF) { + case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; + case 0x01: p = "MONO2 (LANGUAGE B)"; break; + case 0x02: p = "MONO3 (STEREO forced MONO)"; break; + case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; + case 0x04: p = "STEREO"; break; + case 0x05: p = "DUAL1 (AB)"; break; + case 0x06: p = "DUAL2 (AC) (FM)"; break; + case 0x07: p = "DUAL3 (BC) (FM)"; break; + case 0x08: p = "DUAL4 (AC) (AM)"; break; + case 0x09: p = "DUAL5 (BC) (AM)"; break; + case 0x0a: p = "SAP"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio mode: %s\n", p); + } else { + switch (audio_config & 0xF) { + case 0x00: p = "BG"; break; + case 0x01: p = "DK1"; break; + case 0x02: p = "DK2"; break; + case 0x03: p = "DK3"; break; + case 0x04: p = "I"; break; + case 0x05: p = "L"; break; + case 0x06: p = "BTSC"; break; + case 0x07: p = "EIAJ"; break; + case 0x08: p = "A2-M"; break; + case 0x09: p = "FM Radio"; break; + case 0x0f: p = "automatic standard and mode detection"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio system: %s\n", p); + } + + if (aud_input) { + v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); + } else { + v4l_info(client, "Specified audio input: External\n"); + } + + switch (pref_mode & 0xf) { + case 0: p = "mono/language A"; break; + case 1: p = "language B"; break; + case 2: p = "language C"; break; + case 3: p = "analog fallback"; break; + case 4: p = "stereo"; break; + case 5: p = "language AC"; break; + case 6: p = "language BC"; break; + case 7: p = "language AB"; break; + default: p = "undefined"; + } + v4l_info(client, "Preferred audio mode: %s\n", p); + + if ((audio_config & 0xf) == 0xf) { + switch ((afc0 >> 3) & 0x3) { + case 0: p = "system DK"; break; + case 1: p = "system L"; break; + case 2: p = "autodetect"; break; + default: p = "undefined"; + } + v4l_info(client, "Selected 65 MHz format: %s\n", p); + + switch (afc0 & 0x7) { + case 0: p = "chroma"; break; + case 1: p = "BTSC"; break; + case 2: p = "EIAJ"; break; + case 3: p = "A2-M"; break; + case 4: p = "autodetect"; break; + default: p = "undefined"; + } + v4l_info(client, "Selected 45 MHz format: %s\n", p); + } +} + +/* ----------------------------------------------------------------------- */ + +/* This load_fw operation must be called to load the driver's firmware. + Without this the audio standard detection will fail and you will + only get mono. + + Since loading the firmware is often problematic when the driver is + compiled into the kernel I recommend postponing calling this function + until the first open of the video device. Another reason for + postponing it is that loading this firmware takes a long time (seconds) + due to the slow i2c bus speed. So it will speed up the boot process if + you can avoid loading the fw as long as the video device isn't used. */ +static int cx25840_load_fw(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!state->is_initialized) { + /* initialize and load firmware */ + state->is_initialized = 1; + if (is_cx2583x(state)) + cx25836_initialize(client); + else if (is_cx2388x(state)) + cx23885_initialize(client); + else if (is_cx231xx(state)) + cx231xx_initialize(client); + else + cx25840_initialize(client); + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 1; + reg->val = cx25840_read(client, reg->reg & 0x0fff); + return 0; +} + +static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); + return 0; +} +#endif + +static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 v; + + if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state)) + return 0; + + v4l_dbg(1, cx25840_debug, client, "%s audio output\n", + enable ? "enable" : "disable"); + + if (enable) { + v = cx25840_read(client, 0x115) | 0x80; + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) | 0x03; + cx25840_write(client, 0x116, v); + } else { + v = cx25840_read(client, 0x115) & ~(0x80); + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) & ~(0x03); + cx25840_write(client, 0x116, v); + } + return 0; +} + +static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 v; + + v4l_dbg(1, cx25840_debug, client, "%s video output\n", + enable ? "enable" : "disable"); + if (enable) { + if (is_cx2388x(state) || is_cx231xx(state)) { + v = cx25840_read(client, 0x421) | 0x0b; + cx25840_write(client, 0x421, v); + } else { + v = cx25840_read(client, 0x115) | 0x0c; + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) | 0x04; + cx25840_write(client, 0x116, v); + } + } else { + if (is_cx2388x(state) || is_cx231xx(state)) { + v = cx25840_read(client, 0x421) & ~(0x0b); + cx25840_write(client, 0x421, v); + } else { + v = cx25840_read(client, 0x115) & ~(0x0c); + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) & ~(0x04); + cx25840_write(client, 0x116, v); + } + } + return 0; +} + +/* Query the current detected video format */ +static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + v4l2_std_id stds[] = { + /* 0000 */ V4L2_STD_UNKNOWN, + + /* 0001 */ V4L2_STD_NTSC_M, + /* 0010 */ V4L2_STD_NTSC_M_JP, + /* 0011 */ V4L2_STD_NTSC_443, + /* 0100 */ V4L2_STD_PAL, + /* 0101 */ V4L2_STD_PAL_M, + /* 0110 */ V4L2_STD_PAL_N, + /* 0111 */ V4L2_STD_PAL_Nc, + /* 1000 */ V4L2_STD_PAL_60, + + /* 1001 */ V4L2_STD_UNKNOWN, + /* 1010 */ V4L2_STD_UNKNOWN, + /* 1001 */ V4L2_STD_UNKNOWN, + /* 1010 */ V4L2_STD_UNKNOWN, + /* 1011 */ V4L2_STD_UNKNOWN, + /* 1110 */ V4L2_STD_UNKNOWN, + /* 1111 */ V4L2_STD_UNKNOWN + }; + + u32 fmt = (cx25840_read4(client, 0x40c) >> 8) & 0xf; + *std = stds[ fmt ]; + + v4l_dbg(1, cx25840_debug, client, "g_std fmt = %x, v4l2_std_id = 0x%x\n", + fmt, (unsigned int)stds[ fmt ]); + + return 0; +} + +static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* A limited function that checks for signal status and returns + * the state. + */ + + /* Check for status of Horizontal lock (SRC lock isn't reliable) */ + if ((cx25840_read4(client, 0x40c) & 0x00010000) == 0) + *status |= V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio == 0 && state->std == std) + return 0; + state->radio = 0; + state->std = std; + return set_v4lstd(client); +} + +static int cx25840_s_radio(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + + state->radio = 1; + return 0; +} + +static int cx25840_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (is_cx23888(state)) + cx23888_std_setup(client); + + return set_input(client, input, state->aud_input); +} + +static int cx25840_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (is_cx23888(state)) + cx23888_std_setup(client); + return set_input(client, state->vid_input, input); +} + +static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + input_change(client); + return 0; +} + +static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 vpres = cx25840_read(client, 0x40e) & 0x20; + u8 mode; + int val = 0; + + if (state->radio) + return 0; + + vt->signal = vpres ? 0xffff : 0x0; + if (is_cx2583x(state)) + return 0; + + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + + mode = cx25840_read(client, 0x804); + + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; + + if (mode == 2 || mode == 4) + val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; + + vt->rxsubchans = val; + vt->audmode = state->audmode; + return 0; +} + +static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio || is_cx2583x(state)) + return 0; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + /* mono -> mono + stereo -> mono + bilingual -> lang1 */ + cx25840_and_or(client, 0x809, ~0xf, 0x00); + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + /* mono -> mono + stereo -> stereo + bilingual -> lang1 */ + cx25840_and_or(client, 0x809, ~0xf, 0x04); + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang1/lang2 */ + cx25840_and_or(client, 0x809, ~0xf, 0x07); + break; + case V4L2_TUNER_MODE_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang2 */ + cx25840_and_or(client, 0x809, ~0xf, 0x01); + break; + default: + return -EINVAL; + } + state->audmode = vt->audmode; + return 0; +} + +static int cx25840_reset(struct v4l2_subdev *sd, u32 val) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (is_cx2583x(state)) + cx25836_initialize(client); + else if (is_cx2388x(state)) + cx23885_initialize(client); + else if (is_cx231xx(state)) + cx231xx_initialize(client); + else + cx25840_initialize(client); + return 0; +} + +static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); +} + +static int cx25840_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + log_video_status(client); + if (!is_cx2583x(state)) + log_audio_status(client); + cx25840_ir_log_status(sd); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *c = v4l2_get_subdevdata(sd); + u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en; + u32 vid_stat, aud_mc_stat; + bool block_handled; + int ret = 0; + + irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); + v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n", + irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); + + if ((is_cx23885(state) || is_cx23887(state))) { + ir_stat = cx25840_read(c, CX25840_IR_STATS_REG); + ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core ir IRQ status: %#04x disables: %#04x\n", + ir_stat, ir_en); + if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) { + block_handled = false; + ret = cx25840_ir_irq_handler(sd, + status, &block_handled); + if (block_handled) + *handled = true; + } + } + + aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG); + aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core audio IRQ status: %#04x disables: %#04x\n", + aud_stat, aud_en); + aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core audio MC IRQ status: %#06x enables: %#06x\n", + aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT, + aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS); + if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) { + if (aud_stat) { + cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat); + *handled = true; + } + } + + vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core video IRQ status: %#06x disables: %#06x\n", + vid_stat & CX25840_VID_INT_STAT_BITS, + vid_stat >> CX25840_VID_INT_MASK_SHFT); + if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) { + if (vid_stat & CX25840_VID_INT_STAT_BITS) { + cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat); + *handled = true; + } + } + + irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); + v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n", + irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); + + return ret; +} + +static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx25840_state *state = to_state(sd); + + *handled = false; + + /* Only support the CX2388[578] AV Core for now */ + if (is_cx2388x(state)) + return cx23885_irq_handler(sd, status, handled); + + return -ENODEV; +} + +/* ----------------------------------------------------------------------- */ + +#define DIF_PLL_FREQ_WORD (0x300) +#define DIF_BPF_COEFF01 (0x348) +#define DIF_BPF_COEFF23 (0x34c) +#define DIF_BPF_COEFF45 (0x350) +#define DIF_BPF_COEFF67 (0x354) +#define DIF_BPF_COEFF89 (0x358) +#define DIF_BPF_COEFF1011 (0x35c) +#define DIF_BPF_COEFF1213 (0x360) +#define DIF_BPF_COEFF1415 (0x364) +#define DIF_BPF_COEFF1617 (0x368) +#define DIF_BPF_COEFF1819 (0x36c) +#define DIF_BPF_COEFF2021 (0x370) +#define DIF_BPF_COEFF2223 (0x374) +#define DIF_BPF_COEFF2425 (0x378) +#define DIF_BPF_COEFF2627 (0x37c) +#define DIF_BPF_COEFF2829 (0x380) +#define DIF_BPF_COEFF3031 (0x384) +#define DIF_BPF_COEFF3233 (0x388) +#define DIF_BPF_COEFF3435 (0x38c) +#define DIF_BPF_COEFF36 (0x390) + +void cx23885_dif_setup(struct i2c_client *client, u32 ifHz) +{ + u64 pll_freq; + u32 pll_freq_word; + + v4l_dbg(1, cx25840_debug, client, "%s(%d)\n", __func__, ifHz); + + /* Assuming TV */ + /* Calculate the PLL frequency word based on the adjusted ifHz */ + pll_freq = div_u64((u64)ifHz * 268435456, 50000000); + pll_freq_word = (u32)pll_freq; + + cx25840_write4(client, DIF_PLL_FREQ_WORD, pll_freq_word); + + /* Round down to the nearest 100KHz */ + ifHz = (ifHz / 100000) * 100000; + + if (ifHz < 3000000) + ifHz = 3000000; + + if (ifHz > 16000000) + ifHz = 16000000; + + v4l_dbg(1, cx25840_debug, client, "%s(%d) again\n", __func__, ifHz); + + switch (ifHz) { + case 3000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0024); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001bfff8); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff50); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed8fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe34); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfebaffc7); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d031f); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04f0065d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07010688); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04c901d6); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f9d3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f342); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f337); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf64efb22); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105070f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0c460fce); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00220032); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00370026); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff91); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0efe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fdcc); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe0afedb); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440224); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0434060c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738074e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06090361); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99fb39); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef3b6); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21af2a5); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf573fa33); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034067d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bfb0fb9); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00200038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c004f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002fffdf); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff5cfeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dfd92); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7ffe03); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36010a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03410575); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x072607d2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x071804d5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134fcb7); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff451); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223f22e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4a7f94b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xff6405e8); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bae0fa4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00000008); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0036); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0056006d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00670030); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffbdff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46fd8d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd25fd4f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35ffe0); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0224049f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9080e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x07ef0627); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9fe45); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f513); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250f1d2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3ecf869); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930552); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b5f0f8f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0001); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000f002c); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0054007d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0093007c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0024ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6fdbb); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd03fcca); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51feb9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00eb0392); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270802); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08880750); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x044dffdb); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf5f8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0f193); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf342f78f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc404b9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b0e0f78); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff9); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0002001b); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0046007d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad00ba); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00870000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe1a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1bfc7e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fda4); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xffa5025c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x054507ad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08dd0847); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80172); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef6ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313f170); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2abf6bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6041f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0abc0f61); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff50006); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002f006c); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b200e3); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00dc007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9fea0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd6bfc71); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fcb1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe65010b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x042d0713); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ec0906); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020302); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff823); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7f16a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf228f5f5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2a0384); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a670f4a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9fff1); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010004d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a100f2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011a00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053ff44); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdedfca2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fbef); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd39ffae); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x02ea0638); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08b50987); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230483); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f960); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45bf180); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1b8f537); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb6102e7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a110f32); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffdd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00024); + cx25840_write4(client, DIF_BPF_COEFF89, 0x007c00e5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013a014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e6fff8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe98fd0f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fb67); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc32fe54); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x01880525); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x083909c7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x091505ee); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7fab3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52df1b4); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf15df484); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9b0249); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x09ba0f19); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 3900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffcf); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1fff6); + cx25840_write4(client, DIF_BPF_COEFF89, 0x004800be); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01390184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x016300ac); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff5efdb1); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb23); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb5cfd0d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x001703e4); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x077b09c4); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d2073c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251fc18); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61cf203); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf118f3dc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d801aa); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x09600eff); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffefff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffca); + cx25840_write4(client, DIF_BPF_COEFF89, 0x000b0082); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01170198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10152); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0030fe7b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb24); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfac3fbe9); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0683097f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a560867); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2fd89); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723f26f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0e8f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919010a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x09060ee5); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8ffca); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffa4); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffcd0036); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d70184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f601dc); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ffff60); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb6d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6efaf5); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd410103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x055708f9); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e0969); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543ff02); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842f2f5); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cef2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85e006b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x08aa0ecb); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050003); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff3ffd3); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaff8b); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ffe5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0080014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe023f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ba0050); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fbf8); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa62fa3b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9ff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04010836); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa90a3d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f007f); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf975f395); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cbf231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ffcb); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x084c0eaf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000a); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0000ffe4); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff81); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff96); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x001c00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70271); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0254013b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fcbd); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa9ff9c5); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbfdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x028c073b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a750adf); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e101fa); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab8f44e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0ddf1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ff2b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x07ed0e94); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000efff8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff87); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff54); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffb5007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01860270); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c00210); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fdb2); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb22f997); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2fc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0102060f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a050b4c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902036e); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0af51e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf106f15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64efe8b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x078d0e77); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0019000e); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff9e); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff25); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff560000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112023b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f702c0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfec8); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbe5f9b3); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xff7004b9); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a0b81); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a0004d8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd65f603); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf144f104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aafdec); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x072b0e5a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00200022); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ffc1); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ff10); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff09ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x008601d7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f50340); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fff0); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcddfa19); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfde30343); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08790b7f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50631); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec7f6fc); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf198f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50dfd4e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x06c90e3d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00220030); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffed); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff87ff15); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed6ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffed014c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b90386); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110119); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfdfefac4); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6f92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc6701b7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07670b44); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0776); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x002df807); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf200f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477fcb1); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x06650e1e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0009); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f001b); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffbcff36); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec2feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff5600a5); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0248038d); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00232); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff39fbab); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4f87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb060020); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x062a0ad2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf908a3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0192f922); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf27df05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8fc14); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x06000e00); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 4900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0002); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00160037); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00510046); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff9ff6d); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed0fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefff0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01aa0356); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413032b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x007ffcc5); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cf812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9cefe87); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c90a2c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4309b4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f3fa4a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf30ef046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361fb7a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x059b0de0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000a002d); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00570067); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0037ffb5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfefffe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62ff3d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ec02e3); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x043503f6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x01befe05); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa27f7ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8c6fcf8); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x034c0954); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0aa4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x044cfb7e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3b1f03f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2fae1); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x05340dc0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffd001e); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0051007b); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006e0006); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff48fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfe9a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x001d023e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130488); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02e6ff5b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1ef812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7f7fb7f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bc084e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430b72); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x059afcba); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf467f046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cfa4a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x04cd0da0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff00009); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00980056); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffa5feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe15); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff4b0170); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b004d7); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03e800b9); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc48f87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf768fa23); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022071f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90c1b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x06dafdfd); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf52df05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef9b5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x04640d7f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6fff3); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00250072); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00af009c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x000cff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13fdb8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe870089); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104e1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04b8020f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd98f92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf71df8f0); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe8805ce); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0c9c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0808ff44); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf603f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af922); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x03fb0d5e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe0); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00050056); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b000d1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0071ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53fd8c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfddfff99); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104a3); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x054a034d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff01fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf717f7ed); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf50461); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50cf4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0921008d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf6e7f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff891); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x03920d3b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffffff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffd1); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5002f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009c00ed); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00cb0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfebafd94); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd61feb0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d0422); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05970464); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0074fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf759f721); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb7502de); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000d21); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a2201d4); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7d9f104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf804); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x03280d19); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fffa); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3ffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc90002); + cx25840_write4(client, DIF_BPF_COEFF89, 0x007500ef); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x010e007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3dfdcf); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd16fddd); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440365); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x059b0548); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3fc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7dff691); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0f014d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020d23); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0318); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8d7f15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f779); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x02bd0cf6); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060001); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffecffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffd4); + cx25840_write4(client, DIF_BPF_COEFF89, 0x004000d5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013600f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd3fe39); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd04fd31); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff360277); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x055605ef); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x033efdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8a5f642); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbffb6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10cfb); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50456); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9dff1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f6f2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x02520cd2); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080009); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff8ffd2); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffac); + cx25840_write4(client, DIF_BPF_COEFF89, 0x000200a3); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013c014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x006dfec9); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2bfcb7); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe350165); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04cb0651); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477ff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9a5f635); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1fe20); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0ca8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c81058b); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfaf0f231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f66d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x01e60cae); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 5900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0005ffe1); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacff90); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5005f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01210184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fcff72); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd8afc77); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51003f); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04020669); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfad7f66b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8fc93); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430c2b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d06b5); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfc08f2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af5ec); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x017b0c89); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0012fff5); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff8e000f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e80198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750028); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe18fc75); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99ff15); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03050636); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0656027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc32f6e2); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614fb17); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20b87); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d7707d2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfd26f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf56f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x010f0c64); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001c000b); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff84); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ffbe); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00960184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd00da); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeccfcb2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fdf9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x01e005bc); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e703e4); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfdabf798); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f9b3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510abd); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf08df); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfe48f3dc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f4f6); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x00a20c3e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021001f); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff97); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff74); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0034014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa0179); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff97fd2a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fcfa); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00a304fe); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310525); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xff37f886); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf86e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c709d0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de209db); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xff6df484); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf481); + cx25840_write4(client, DIF_BPF_COEFF3435, 0x00360c18); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe000a); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021002f); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ffb8); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff3b); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffcc00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa01fa); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0069fdd4); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fc26); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff5d0407); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310638); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x00c9f9a8); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf74e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff3908c3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de20ac3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0093f537); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf410); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xffca0bf2); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb0003); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001c0037); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002fffe2); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ff17); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff6a007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd0251); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0134fea5); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb8b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe2002e0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e70713); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0255faf5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f658); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0799); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf0b96); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x01b8f5f5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f3a3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xff5e0bca); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00120037); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00460010); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff8eff0f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff180000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750276); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01e8ff8d); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb31); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcfb0198); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x065607ad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x03cefc64); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614f592); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0656); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d770c52); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x02daf6bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf33b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfef10ba3); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff5); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0005002f); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0054003c); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5ff22); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedfff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fc0267); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0276007e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb1c); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbfe003e); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830802); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0529fdec); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8f4fe); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd04ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d0cf6); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x03f8f78f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af2d7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe850b7b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff80020); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00560060); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0002ff4e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec4ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x006d0225); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02d50166); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb4e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb35fee1); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477080e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x065bff82); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1f4a0); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610397); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c810d80); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0510f869); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f278); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe1a0b52); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffaffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffec000c); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0078); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0040ff8e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecafeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd301b6); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fc0235); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fbc5); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaaafd90); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x033e07d2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x075b011b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbf47a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f0224); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50def); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0621f94b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f21e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfdae0b29); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 6900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3fff6); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0037007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0075ffdc); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef2fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3d0122); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02ea02dd); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fc79); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa65fc5d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3074e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x082102ad); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0ff48c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe00a9); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0e43); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0729fa33); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f1c9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfd430b00); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0001fff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe2); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001b0076); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009c002d); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff35fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfeba0076); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x029f0352); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfd60); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa69fb53); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x00740688); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08a7042d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb75f4d6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600ff2d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a220e7a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0827fb22); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf17a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfcd80ad6); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff9); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffd2); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb005e); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b0007a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8ffe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53ffc1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0221038c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fe6e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfab6fa80); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff010587); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e90590); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf5f556); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfdb3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x09210e95); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0919fc15); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff12f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc6e0aab); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070000); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6ffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0039); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00af00b8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfff4feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13ff10); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01790388); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311ff92); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb48f9ed); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd980453); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e306cd); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe88f60a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fc40); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x08080e93); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x09fdfd0c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af0ea); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc050a81); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080008); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff0ffc9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1000d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009800e2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x005bff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe74); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00b50345); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000bc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc18f9a1); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc4802f9); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x089807dc); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022f6f0); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fada); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x06da0e74); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ad3fe06); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef0ab); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb9c0a55); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffdffd0); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffdf); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006e00f2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00b8ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfdf8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffe302c8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x041301dc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd1af99e); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1e0183); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x080908b5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bcf801); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf985); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x059a0e38); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b99ff03); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cf071); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb330a2a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00070011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000affdf); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffa9ffb5); + cx25840_write4(client, DIF_BPF_COEFF89, 0x003700e6); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01010000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62fda8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff140219); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x043502e1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe42f9e6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa270000); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x073a0953); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x034cf939); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a4f845); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x044c0de1); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c4f0000); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2f03c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfacc09fe); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00040012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0016fff3); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffafff95); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff900c0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0130007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefd89); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe560146); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x041303bc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff81fa76); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cfe7d); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x063209b1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c9fa93); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf71e); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f30d6e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cf200fd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361f00e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa6509d1); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00010010); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0008); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ff84); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffbc0084); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013e00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff56fd9f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdb8005c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00460); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00c7fb45); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4fd07); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04fa09ce); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x062afc07); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407f614); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x01920ce0); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d8301fa); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8efe5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa0009a4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd000b); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001d); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff870039); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x012a014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffedfde7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd47ff6b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104c6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0202fc4c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6fbad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x039909a7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0767fd8e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482f52b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x002d0c39); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e0002f4); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477efc2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf99b0977); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 7900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa0004); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0020002d); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff91); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ffe8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f70184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0086fe5c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0bfe85); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104e5); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0323fd7d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa79); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x021d093f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0879ff22); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bf465); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec70b79); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e6803eb); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50defa5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf937094a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffd); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00190036); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001bffaf); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff99); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00aa0198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112fef3); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd09fdb9); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04be); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x041bfecc); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947f978); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x00900897); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a00b9); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f3c5); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd650aa3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ebc04de); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aaef8e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf8d5091c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff6); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000e0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ffd7); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff56); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x004b0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0186ffa1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd40fd16); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440452); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04de0029); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2f8b2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfefe07b5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a05024d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef34d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0a09b8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0efa05cd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64eef7d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf87308ed); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00000031); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0005); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff27); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffe4014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70057); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdacfca6); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3603a7); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05610184); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbf82e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd74069f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a7503d6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff2ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab808b9); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f2306b5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ef72); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf81308bf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff30022); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00560032); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ff10); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8000f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe0106); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe46fc71); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3502c7); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x059e02ce); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9f7f2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfbff055b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa9054c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f2db); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf97507aa); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f350797); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ef6d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7b40890); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8000f); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00540058); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffcdff14); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff29007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f6019e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff01fc7c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5101bf); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x059203f6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd41f7fe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfaa903f3); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e06a9); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf2e2); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842068b); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f320871); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85eef6e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7560860); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fff2); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1fff9); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00460073); + cx25840_write4(client, DIF_BPF_COEFF89, 0x000bff34); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee90000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10215); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffd0fcc5); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99009d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x053d04f1); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5f853); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf97d0270); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a5607e4); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef314); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723055f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f180943); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919ef75); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6fa0830); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff8); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe4); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002f007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0048ff6b); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec7ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0163025f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00a2fd47); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff73); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04a405b2); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0017f8ed); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf88500dc); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d208f9); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff370); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61c0429); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ee80a0b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d8ef82); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6a00800); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007ffff); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffd4); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010007a); + cx25840_write4(client, DIF_BPF_COEFF89, 0x007cffb2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec6ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e60277); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0168fdf9); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fe50); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x03ce0631); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0188f9c8); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7c7ff43); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x091509e3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f3f6); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52d02ea); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ea30ac9); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9bef95); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf64607d0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00090007); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9ffca); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00065); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a10003); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee6feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053025b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0213fed0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fd46); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02c70668); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x02eafadb); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf74bfdae); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230a9c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f4a3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45b01a6); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e480b7c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb61efae); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf5ef079f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 8900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000d); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff5ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10043); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b20053); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff24fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9020c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0295ffbb); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fc64); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x019b0654); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x042dfc1c); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf714fc2a); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020b21); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f575); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7005e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0dd80c24); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2aefcd); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf599076e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0002ffcf); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffba0018); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad009a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff79fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff260192); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e500ab); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fbb6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x005b05f7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545fd81); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf723fabf); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80b70); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f669); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313ff15); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d550cbf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6eff2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544073d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00030012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000fffdd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffea); + cx25840_write4(client, DIF_BPF_COEFF89, 0x009300cf); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdcfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea600f7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fd0190); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb46); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff150554); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0627fefd); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf778f978); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x044d0b87); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f77d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0fdcf); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbe0d4e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc4f01d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2070b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00000010); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001afff0); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffbf); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006700ed); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe460047); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02db0258); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb1b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddc0473); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c90082); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf811f85e); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c90b66); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff8ad); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250fc8d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c140dcf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe93f04d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a106d9); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc000c); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00200006); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff9c); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002f00ef); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a4ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dff92); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x028102f7); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb37); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbf035e); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07260202); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e8f778); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x01340b0d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f9f4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223fb51); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b590e42); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xff64f083); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf45206a7); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90005); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001a); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff86); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff000d7); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fee5); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f60362); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb99); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbcc0222); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07380370); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f7f6cc); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff990a7e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902fb50); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21afa1f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8d0ea6); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034f0bf); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4050675); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffe); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001e002b); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff81); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffb400a5); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe50); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01460390); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfc3a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb1000ce); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x070104bf); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb37f65f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe0009bc); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00fcbb); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f8f8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b20efc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105f101); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3ba0642); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff7); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00150036); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff8c); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff810061); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013d007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe71fddf); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x007c0380); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fd13); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa94ff70); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x068005e2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9bf633); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc7308ca); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5fe30); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf274f7e0); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c90f43); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d4f147); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371060f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fff1); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00090038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffa7); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5e0012); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013200f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee3fd9b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaa0331); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fe15); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa60fe18); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bd06d1); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1bf64a); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafa07ae); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7effab); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d5f6d7); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d30f7a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a3f194); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf32905dc); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffb0032); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003fffcd); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effc1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6efd8a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfedd02aa); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0ff34); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa74fcd7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bf0781); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaaf6a3); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99e066b); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90128); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf359f5e1); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d20fa2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0370f1e5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e405a8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 9900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffef0024); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0051fffa); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff54ff77); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00be0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0006fdad); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe2701f3); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413005e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad1fbba); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x039007ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x013bf73d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868050a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4302a1); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fdf4fe); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c70fba); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x043bf23c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a10575); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fff1); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe50011); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00570027); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff70ff3c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00620198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x009efe01); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95011a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350183); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb71fad0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x023c0812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c3f811); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75e0390); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0411); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c1f432); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b30fc1); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0503f297); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2610541); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0006fff7); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdffffc); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00510050); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff9dff18); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffc0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0128fe80); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32002e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130292); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4dfa21); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d107ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0435f91c); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6850205); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430573); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a1f37d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x03990fba); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c7f2f8); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222050d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffe); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdfffe7); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f006e); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffd6ff0f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0197ff1f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd05ff3e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0037c); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd59f9b7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5d0781); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0585fa56); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4006f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf906c4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69df2e0); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x02790fa2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0688f35d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e604d8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00090005); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4ffd6); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025007e); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0014ff20); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3c00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e1ffd0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd12fe5c); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110433); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88f996); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf106d1); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aafbb7); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57efed8); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e07ff); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b0f25e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x01560f7a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0745f3c7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1ac04a4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000c); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffedffcb); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005007d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0050ff4c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ff0086); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd58fd97); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104ad); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xffcaf9c0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc9905e2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x079afd35); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf555fd46); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50920); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d9f1f6); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x00310f43); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fdf435); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174046f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050011); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffaffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5006b); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0082ff8c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f00130); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd2fcfc); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04e3); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x010efa32); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb6404bf); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x084efec5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569fbc2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000a23); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa15f1ab); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0b0efc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b0f4a7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13f043a); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00020012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0007ffcd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9004c); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a4ffd9); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b401c1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe76fc97); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404d2); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0245fae8); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5f0370); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1005f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bcfa52); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020b04); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb60f17b); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde70ea6); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x095df51e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10c0405); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0014ffdb); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40023); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2002a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedbff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150022d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff38fc6f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36047b); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x035efbda); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9940202); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ee01f5); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf649f8fe); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10bc2); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb6f169); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc60e42); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a04f599); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0db03d0); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb000d); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001dffed); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaafff5); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00aa0077); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00ce026b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x000afc85); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3503e3); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x044cfcfb); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90c0082); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5037f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf710f7cc); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0c59); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe16f173); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaa0dcf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa5f617); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0ad039b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 10900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90006); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00210003); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffc8); + cx25840_write4(client, DIF_BPF_COEFF89, 0x008e00b6); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff63fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x003a0275); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00dafcda); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd510313); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0501fe40); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cbfefd); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x087604f0); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80af6c2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430cc8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7af19a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa940d4e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3ff699); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0810365); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffff); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00210018); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffa3); + cx25840_write4(client, DIF_BPF_COEFF89, 0x006000e1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc4fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0024b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x019afd66); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc990216); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0575ff99); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4fd81); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d40640); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf932f5e6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20d0d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x00dff1de); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9860cbf); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd1f71e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058032f); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff8); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001b0029); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff8a); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002600f2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x002cfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0f01f0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x023bfe20); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc1700fa); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a200f7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf927fc1c); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f40765); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa82f53b); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510d27); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0243f23d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8810c24); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5cf7a7); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf03102fa); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00110035); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff81); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffe700e7); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x008ffeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94016d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b0fefb); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3ffd1); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05850249); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c1fadb); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x05de0858); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf2f4c4); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c70d17); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a0f2b8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7870b7c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdff833); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00d02c4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00040038); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ff88); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffac00c2); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e2ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe3900cb); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f1ffe9); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3feaa); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05210381); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9cf9c8); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x04990912); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7af484); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff390cdb); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f4f34d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69a0ac9); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5af8c1); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefec028e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0000ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff60033); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002fff9f); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7b0087); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011eff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe080018); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f900d8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fd96); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x04790490); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbadf8ed); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x032f098e); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff10f47d); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0c75); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x063cf3fc); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5ba0a0b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dccf952); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcd0258); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff1); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffea0026); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0046ffc3); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5a003c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04ff63); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c801b8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fca6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397056a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcecf853); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x01ad09c9); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x00acf4ad); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0be7); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0773f4c2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e90943); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35f9e6); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb10221); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff6); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe20014); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0054ffee); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effeb); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2efebb); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260027a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fbe6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02870605); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4af7fe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x001d09c1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0243f515); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd0b32); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0897f59e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4280871); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e95fa7c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef9701eb); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffd); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffff); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0056001d); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff57ff9c); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011300f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe82fe2e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ca0310); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb62); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155065a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xffbaf7f2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8c0977); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x03cef5b2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610a58); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a5f68f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3790797); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eebfb14); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef8001b5); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080004); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe9); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0047); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff75ff58); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef9fdc8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111036f); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb21); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x00120665); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x012df82e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd0708ec); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542f682); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f095c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9af792); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2db06b5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f38fbad); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6c017e); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 11900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000b); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe7ffd8); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00370068); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffa4ff28); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00790184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87fd91); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00430392); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb26); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfece0626); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294f8b2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb990825); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0698f77f); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe0842); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b73f8a7); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf25105cd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7bfc48); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5a0148); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00050010); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff2ffcc); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001b007b); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffdfff10); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00140198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0020fd8e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff710375); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb73); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9a059f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e0f978); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4e0726); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c8f8a7); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600070c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2ff9c9); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1db04de); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4fce5); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4b0111); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00010012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffffffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb007e); + cx25840_write4(client, DIF_BPF_COEFF89, 0x001dff14); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffad0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b7fdbe); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9031b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fc01); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc8504d6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0504fa79); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf93005f6); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08caf9f2); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52b05c0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccbfaf9); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf17903eb); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3fd83); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3f00db); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe0011); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000cffcc); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0071); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0058ff32); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4f014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x013cfe1f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfb028a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fcc9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9d03d6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f4fbad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848049d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0999fb5b); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf4820461); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d46fc32); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12d02f4); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x1007fe21); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3600a4); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0017ffd9); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc10055); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0088ff68); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0400f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a6fea7); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7501cc); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fdc0); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaef02a8); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a7fd07); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79d0326); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a31fcda); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf40702f3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9ffd72); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f601fa); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x1021fec0); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2f006d); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80007); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001fffeb); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaf002d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a8ffb0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e9ff4c); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2000ee); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fed8); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82015c); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0715fe7d); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7340198); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8dfe69); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bd017c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd5feb8); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d500fd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x1031ff60); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2b0037); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff70000); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00220000); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffa90000); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b30000); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec20000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x02000000); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd030000); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350000); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa5e0000); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x073b0000); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7110000); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aac0000); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a40000); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de70000); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0c90000); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x10360000); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef290000); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff9); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001f0015); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffd3); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a80050); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e900b4); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd20ff12); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130128); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82fea4); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x07150183); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf734fe68); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8d0197); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdfe84); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd50148); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d5ff03); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x103100a0); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2bffc9); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00170027); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ffab); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00880098); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff04ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a60159); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd75fe34); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00240); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaeffd58); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a702f9); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79dfcda); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a310326); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fd0d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9f028e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f6fe06); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x10210140); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2fff93); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000c0034); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff8f); + cx25840_write4(client, DIF_BPF_COEFF89, 0x005800ce); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4ffeb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x013c01e1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfbfd76); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110337); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9dfc2a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f40453); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848fb63); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x099904a5); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fb9f); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d4603ce); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12dfd0c); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x100701df); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef36ff5c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 12900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0001ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffff0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0x001d00ec); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffadfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b70242); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9fce5); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024103ff); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc85fb2a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05040587); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf930fa0a); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x08ca060e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfa40); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccb0507); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf179fc15); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3027d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3fff25); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff20034); + cx25840_write4(client, DIF_BPF_COEFF67, 0x001bff85); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffdf00f0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0014fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00200272); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff71fc8b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d048d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9afa61); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e00688); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4ef8da); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c80759); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f8f4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2f0637); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1dbfb22); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4031b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4bfeef); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff5); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe70028); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ff98); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffa400d8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0079fe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87026f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0043fc6e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404da); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfecef9da); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294074e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb99f7db); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x06980881); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef7be); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b730759); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf251fa33); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7b03b8); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5afeb8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffc); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe00017); + cx25840_write4(client, DIF_BPF_COEFF67, 0x004cffb9); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7500a8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef90238); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111fc91); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604df); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0012f99b); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x012d07d2); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd07f714); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542097e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff6a4); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9a086e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2dbf94b); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f380453); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6cfe82); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080003); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffde0001); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0056ffe3); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff570064); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0113ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe8201d2); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01cafcf0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35049e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155f9a6); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xffba080e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8cf689); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x03ce0a4e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f5a8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a50971); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf379f869); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eeb04ec); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef80fe4b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000a); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe2ffec); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00540012); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e0015); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2e0145); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260fd86); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51041a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0287f9fb); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4a0802); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x001df63f); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02430aeb); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf4ce); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x08970a62); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf428f78f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e950584); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xef97fe15); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffeaffda); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0046003d); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5affc4); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04009d); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c8fe48); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99035a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397fa96); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcec07ad); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x01adf637); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x00ac0b53); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef419); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x07730b3e); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e9f6bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35061a); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb1fddf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00000012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfff6ffcd); + cx25840_write4(client, DIF_BPF_COEFF67, 0x002f0061); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7bff79); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x011e007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe08ffe8); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f9ff28); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17026a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0479fb70); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbad0713); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x032ff672); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff100b83); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff38b); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x063c0c04); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5baf5f5); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dcc06ae); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcdfda8); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0012); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0004ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00100078); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffacff3e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e200f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe39ff35); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f10017); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd30156); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0521fc7f); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9c0638); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0499f6ee); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7a0b7c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f325); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f40cb3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69af537); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5a073f); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xefecfd72); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0011ffcb); + cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffe7ff19); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x008f014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94fe93); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b00105); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3002f); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x0585fdb7); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c10525); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x05def7a8); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf20b3c); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f2e9); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a00d48); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf787f484); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdf07cd); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00dfd3c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 13900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80008); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001bffd7); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10076); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0026ff0e); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x002c0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0ffe10); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x023b01e0); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff06); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a2ff09); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf92703e4); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f4f89b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa820ac5); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f2d9); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x02430dc3); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf881f3dc); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5c0859); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf031fd06); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80001); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021ffe8); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffba005d); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0060ff1f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc40198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0fdb5); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x019a029a); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fdea); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x05750067); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d4f9c0); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf9320a1a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f2f3); + cx25840_write4(client, DIF_BPF_COEFF2829, 0x00df0e22); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xf986f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd108e2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058fcd1); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0021fffd); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0038); + cx25840_write4(client, DIF_BPF_COEFF89, 0x008eff4a); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff630184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x003afd8b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x00da0326); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fced); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x050101c0); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cb0103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x0876fb10); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80a093e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f338); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7a0e66); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa94f2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3f0967); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf081fc9b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff3); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001d0013); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa000b); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00aaff89); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00cefd95); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x000a037b); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fc1d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x044c0305); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90cff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5fc81); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7100834); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff3a7); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe160e8d); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaaf231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa509e9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0adfc65); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00140025); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffdd); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2ffd6); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedb00f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150fdd3); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xff380391); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb85); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x035e0426); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xf994fdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08eefe0b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6490702); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f43e); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb60e97); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc6f1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a040a67); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0dbfc30); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0002ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00070033); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ffb4); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00a40027); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b4fe3f); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe760369); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb2e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x02450518); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5ffc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1ffa1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bc05ae); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902f4fc); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb600e85); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde7f15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x095d0ae2); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10cfbfb); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0005ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffa0038); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff95); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00820074); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f0fed0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd20304); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb1d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x010e05ce); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb64fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x084e013b); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569043e); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00f5dd); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa150e55); + cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0bf104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b00b59); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13ffbc6); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffed0035); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff83); + cx25840_write4(client, DIF_BPF_COEFF89, 0x005000b4); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6ff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ffff7a); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd580269); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fb53); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xffca0640); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc99fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x079a02cb); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55502ba); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5f6e0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d90e0a); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0031f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fd0bcb); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174fb91); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0009fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4002a); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ff82); + cx25840_write4(client, DIF_BPF_COEFF89, 0x001400e0); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3cff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e10030); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1201a4); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fbcd); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88066a); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf1f92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aa0449); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57e0128); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7ef801); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b00da2); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0156f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x07450c39); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1acfb5c); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00080002); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0019); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003fff92); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffd600f1); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x019700e1); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0500c2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fc84); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd590649); + cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5df87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x058505aa); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4ff91); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9f93c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69d0d20); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0279f05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x06880ca3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e6fb28); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 14900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x00060009); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0004); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0051ffb0); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff9d00e8); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffcfe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x01280180); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32ffd2); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fd6e); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4d05df); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d1f812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x043506e4); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf685fdfb); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fa8d); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a10c83); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0399f046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c70d08); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222faf3); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffe5ffef); + cx25840_write4(client, DIF_BPF_COEFF67, 0x0057ffd9); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff7000c4); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0062fe68); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x009e01ff); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95fee6); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0435fe7d); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb710530); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x023cf7ee); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c307ef); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75efc70); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5cfbef); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c10bce); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b3f03f); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x05030d69); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf261fabf); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15100000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); + cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xffefffdc); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00510006); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff540089); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00befe7c); + cx25840_write4(client, DIF_BPF_COEFF1213, 0x00060253); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe27fe0d); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413ffa2); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad10446); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0390f812); + cx25840_write4(client, DIF_BPF_COEFF2223, 0x013b08c3); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868faf6); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fd5f); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fd0b02); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c7f046); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x043b0dc4); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a1fa8b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15200000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0012); + cx25840_write4(client, DIF_BPF_COEFF45, 0xfffbffce); + cx25840_write4(client, DIF_BPF_COEFF67, 0x003f0033); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e003f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106feb6); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6e0276); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeddfd56); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000cc); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa740329); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bff87f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaa095d); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99ef995); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9fed8); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3590a1f); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d2f05e); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x03700e1b); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e4fa58); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15300000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9000f); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0009ffc8); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00250059); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff5effee); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0132ff10); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee30265); + cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaafccf); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x031101eb); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6001e8); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bdf92f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1b09b6); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafaf852); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0055); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d50929); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d3f086); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a30e6c); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf329fa24); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15400000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80009); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0015ffca); + cx25840_write4(client, DIF_BPF_COEFF67, 0x00050074); + cx25840_write4(client, DIF_BPF_COEFF89, 0xff81ff9f); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x013dff82); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe710221); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x007cfc80); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x024102ed); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa940090); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0680fa1e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9b09cd); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc73f736); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad501d0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2740820); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c9f0bd); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d40eb9); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371f9f1); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15500000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80002); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001effd5); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5007f); + cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff5b); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2401b0); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0146fc70); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d03c6); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb10ff32); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0701fb41); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb3709a1); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f644); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000345); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2350708); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b2f104); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x01050eff); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3baf9be); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15600000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0022ffe6); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9007a); + cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff29); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2007e); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01011b); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f6fc9e); + cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440467); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbccfdde); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738fc90); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f70934); + cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99f582); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x090204b0); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21a05e1); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8df15a); + cx25840_write4(client, DIF_BPF_COEFF3233, 0x00340f41); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf405f98b); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15700000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcfff4); + cx25840_write4(client, DIF_BPF_COEFF45, 0x0020fffa); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40064); + cx25840_write4(client, DIF_BPF_COEFF89, 0x002fff11); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a400f0); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0d006e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x0281fd09); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604c9); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbffca2); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0726fdfe); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e80888); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134f4f3); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1060c); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf22304af); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b59f1be); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xff640f7d); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf452f959); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15800000: + cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0000fff0); + cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0010); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa0041); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0067ff13); + cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043014a); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46ffb9); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02dbfda8); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3504e5); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddcfb8d); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9ff7e); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf81107a2); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9f49a); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0753); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2500373); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c14f231); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930fb3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a1f927); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 15900000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0003ffee); + cx25840_write4(client, DIF_BPF_COEFF45, 0x000f0023); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0016); + cx25840_write4(client, DIF_BPF_COEFF89, 0x0093ff31); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdc0184); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6ff09); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fdfe70); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5104ba); + cx25840_write4(client, DIF_BPF_COEFF1819, 0xff15faac); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270103); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7780688); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x044df479); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430883); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a00231); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbef2b2); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc40fe3); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2f8f5); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + + case 16000000: + cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); + cx25840_write4(client, DIF_BPF_COEFF23, 0x0006ffef); + cx25840_write4(client, DIF_BPF_COEFF45, 0x00020031); + cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffe8); + cx25840_write4(client, DIF_BPF_COEFF89, 0x00adff66); + cx25840_write4(client, DIF_BPF_COEFF1011, 0xff790198); + cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe6e); + cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e5ff55); + cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99044a); + cx25840_write4(client, DIF_BPF_COEFF1819, 0x005bfa09); + cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545027f); + cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7230541); + cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b8f490); + cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20997); + cx25840_write4(client, DIF_BPF_COEFF2829, 0xf31300eb); + cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d55f341); + cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6100e); + cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544f8c3); + cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); + break; + } +} + +static void cx23888_std_setup(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + v4l2_std_id std = state->std; + u32 ifHz; + + cx25840_write4(client, 0x478, 0x6628021F); + cx25840_write4(client, 0x400, 0x0); + cx25840_write4(client, 0x4b4, 0x20524030); + cx25840_write4(client, 0x47c, 0x010a8263); + + if (std & V4L2_STD_NTSC) { + v4l_dbg(1, cx25840_debug, client, "%s() Selecting NTSC", + __func__); + + /* Horiz / vert timing */ + cx25840_write4(client, 0x428, 0x1e1e601a); + cx25840_write4(client, 0x424, 0x5b2d007a); + + /* DIF NTSC */ + cx25840_write4(client, 0x304, 0x6503bc0c); + cx25840_write4(client, 0x308, 0xbd038c85); + cx25840_write4(client, 0x30c, 0x1db4640a); + cx25840_write4(client, 0x310, 0x00008800); + cx25840_write4(client, 0x314, 0x44400400); + cx25840_write4(client, 0x32c, 0x0c800800); + cx25840_write4(client, 0x330, 0x27000100); + cx25840_write4(client, 0x334, 0x1f296e1f); + cx25840_write4(client, 0x338, 0x009f50c1); + cx25840_write4(client, 0x340, 0x1befbf06); + cx25840_write4(client, 0x344, 0x000035e8); + + /* DIF I/F */ + ifHz = 5400000; + + } else { + v4l_dbg(1, cx25840_debug, client, "%s() Selecting PAL-BG", + __func__); + + /* Horiz / vert timing */ + cx25840_write4(client, 0x428, 0x28244024); + cx25840_write4(client, 0x424, 0x5d2d0084); + + /* DIF */ + cx25840_write4(client, 0x304, 0x6503bc0c); + cx25840_write4(client, 0x308, 0xbd038c85); + cx25840_write4(client, 0x30c, 0x1db4640a); + cx25840_write4(client, 0x310, 0x00008800); + cx25840_write4(client, 0x314, 0x44400600); + cx25840_write4(client, 0x32c, 0x0c800800); + cx25840_write4(client, 0x330, 0x27000100); + cx25840_write4(client, 0x334, 0x213530ec); + cx25840_write4(client, 0x338, 0x00a65ba8); + cx25840_write4(client, 0x340, 0x1befbf06); + cx25840_write4(client, 0x344, 0x000035e8); + + /* DIF I/F */ + ifHz = 6000000; + } + + cx23885_dif_setup(client, ifHz); + + /* Explicitly ensure the inputs are reconfigured after + * a standard change. + */ + set_input(client, state->vid_input, state->aud_input); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { + .s_ctrl = cx25840_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops cx25840_core_ops = { + .log_status = cx25840_log_status, + .g_chip_ident = cx25840_g_chip_ident, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = cx25840_s_std, + .g_std = cx25840_g_std, + .reset = cx25840_reset, + .load_fw = cx25840_load_fw, + .s_io_pin_config = common_s_io_pin_config, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx25840_g_register, + .s_register = cx25840_s_register, +#endif + .interrupt_service_routine = cx25840_irq_handler, +}; + +static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { + .s_frequency = cx25840_s_frequency, + .s_radio = cx25840_s_radio, + .g_tuner = cx25840_g_tuner, + .s_tuner = cx25840_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { + .s_clock_freq = cx25840_s_clock_freq, + .s_routing = cx25840_s_audio_routing, + .s_stream = cx25840_s_audio_stream, +}; + +static const struct v4l2_subdev_video_ops cx25840_video_ops = { + .s_routing = cx25840_s_video_routing, + .s_mbus_fmt = cx25840_s_mbus_fmt, + .s_stream = cx25840_s_stream, + .g_input_status = cx25840_g_input_status, +}; + +static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { + .decode_vbi_line = cx25840_decode_vbi_line, + .s_raw_fmt = cx25840_s_raw_fmt, + .s_sliced_fmt = cx25840_s_sliced_fmt, + .g_sliced_fmt = cx25840_g_sliced_fmt, +}; + +static const struct v4l2_subdev_ops cx25840_ops = { + .core = &cx25840_core_ops, + .tuner = &cx25840_tuner_ops, + .audio = &cx25840_audio_ops, + .video = &cx25840_video_ops, + .vbi = &cx25840_vbi_ops, + .ir = &cx25840_ir_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static u32 get_cx2388x_ident(struct i2c_client *client) +{ + u32 ret; + + /* Come out of digital power down */ + cx25840_write(client, 0x000, 0); + + /* Detecting whether the part is cx23885/7/8 is more + * difficult than it needs to be. No ID register. Instead we + * probe certain registers indicated in the datasheets to look + * for specific defaults that differ between the silicon designs. */ + + /* It's either 885/7 if the IR Tx Clk Divider register exists */ + if (cx25840_read4(client, 0x204) & 0xffff) { + /* CX23885 returns bogus repetitive byte values for the DIF, + * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ + ret = cx25840_read4(client, 0x300); + if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { + /* No DIF */ + ret = V4L2_IDENT_CX23885_AV; + } else { + /* CX23887 has a broken DIF, but the registers + * appear valid (but unused), good enough to detect. */ + ret = V4L2_IDENT_CX23887_AV; + } + } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { + /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ + ret = V4L2_IDENT_CX23888_AV; + } else { + v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); + ret = V4L2_IDENT_CX23887_AV; + } + + /* Back into digital power down */ + cx25840_write(client, 0x000, 2); + return ret; +} + +static int cx25840_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct cx25840_state *state; + struct v4l2_subdev *sd; + int default_volume; + u32 id = V4L2_IDENT_NONE; + u16 device_id; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1); + + device_id = cx25840_read(client, 0x101) << 8; + device_id |= cx25840_read(client, 0x100); + v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id); + + /* The high byte of the device ID should be + * 0x83 for the cx2583x and 0x84 for the cx2584x */ + if ((device_id & 0xff00) == 0x8300) { + id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + } else if ((device_id & 0xff00) == 0x8400) { + id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); + } else if (device_id == 0x0000) { + id = get_cx2388x_ident(client); + } else if ((device_id & 0xfff0) == 0x5A30) { + /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ + id = V4L2_IDENT_CX2310X_AV; + } else if ((device_id & 0xff) == (device_id >> 8)) { + v4l_err(client, + "likely a confused/unresponsive cx2388[578] A/V decoder" + " found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_err(client, "A method to reset it from the cx25840 driver" + " software is not known at this time\n"); + return -ENODEV; + } else { + v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); + return -ENODEV; + } + + state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cx25840_ops); + + switch (id) { + case V4L2_IDENT_CX23885_AV: + v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23887_AV: + v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23888_AV: + v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX2310X_AV: + v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", + device_id, client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25840: + case V4L2_IDENT_CX25841: + case V4L2_IDENT_CX25842: + case V4L2_IDENT_CX25843: + /* Note: revision '(device_id & 0x0f) == 2' was never built. The + marking skips from 0x1 == 22 to 0x3 == 23. */ + v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, + (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 + : (device_id & 0x0f), + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25836: + case V4L2_IDENT_CX25837: + default: + v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, device_id & 0x0f, + client->addr << 1, client->adapter->name); + break; + } + + state->c = client; + state->vid_input = CX25840_COMPOSITE7; + state->aud_input = CX25840_AUDIO8; + state->audclk_freq = 48000; + state->audmode = V4L2_TUNER_MODE_LANG1; + state->vbi_line_offset = 8; + state->id = id; + state->rev = device_id; + v4l2_ctrl_handler_init(&state->hdl, 9); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + if (!is_cx2583x(state)) { + default_volume = cx25840_read(client, 0x8d4); + /* + * Enforce the legacy PVR-350/MSP3400 to PVR-150/CX25843 volume + * scale mapping limits to avoid -ERANGE errors when + * initializing the volume control + */ + if (default_volume > 228) { + /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ + default_volume = 228; + cx25840_write(client, 0x8d4, 228); + } + else if (default_volume < 20) { + /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ + default_volume = 20; + cx25840_write(client, 0x8d4, 20); + } + default_volume = (((228 - default_volume) >> 1) + 23) << 9; + + state->volume = v4l2_ctrl_new_std(&state->hdl, + &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, + 0, 65535, 65535 / 100, default_volume); + state->mute = v4l2_ctrl_new_std(&state->hdl, + &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_BASS, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, + 0, 65535, 65535 / 100, 32768); + } + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + if (!is_cx2583x(state)) + v4l2_ctrl_cluster(2, &state->volume); + v4l2_ctrl_handler_setup(&state->hdl); + + if (client->dev.platform_data) { + struct cx25840_platform_data *pdata = client->dev.platform_data; + + state->pvr150_workaround = pdata->pvr150_workaround; + } + + cx25840_ir_probe(sd); + return 0; +} + +static int cx25840_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + + cx25840_ir_remove(sd); + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id cx25840_id[] = { + { "cx25840", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cx25840_id); + +static struct i2c_driver cx25840_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cx25840", + }, + .probe = cx25840_probe, + .remove = cx25840_remove, + .id_table = cx25840_id, +}; + +module_i2c_driver(cx25840_driver); diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h new file mode 100644 index 000000000000..bd4ada28b490 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-core.h @@ -0,0 +1,137 @@ +/* cx25840 internal API header + * + * Copyright (C) 2003-2004 Chris Kennedy + * + * This program is free software; 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 _CX25840_CORE_H_ +#define _CX25840_CORE_H_ + + +#include +#include +#include +#include +#include + +struct cx25840_ir_state; + +struct cx25840_state { + struct i2c_client *c; + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* volume cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + }; + int pvr150_workaround; + int radio; + v4l2_std_id std; + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; + u32 audclk_freq; + int audmode; + int vbi_line_offset; + u32 id; + u32 rev; + int is_initialized; + wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ + struct work_struct fw_work; /* work entry for fw load */ + struct cx25840_ir_state *ir_state; +}; + +static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cx25840_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd; +} + +static inline bool is_cx2583x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX25836 || + state->id == V4L2_IDENT_CX25837; +} + +static inline bool is_cx231xx(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX2310X_AV; +} + +static inline bool is_cx2388x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV || + state->id == V4L2_IDENT_CX23887_AV || + state->id == V4L2_IDENT_CX23888_AV; +} + +static inline bool is_cx23885(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV; +} + +static inline bool is_cx23887(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23887_AV; +} + +static inline bool is_cx23888(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23888_AV; +} + +/* ----------------------------------------------------------------------- */ +/* cx25850-core.c */ +int cx25840_write(struct i2c_client *client, u16 addr, u8 value); +int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); +u8 cx25840_read(struct i2c_client *client, u16 addr); +u32 cx25840_read4(struct i2c_client *client, u16 addr); +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); +int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, + u32 or_value); +void cx25840_std_setup(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ +/* cx25850-firmware.c */ +int cx25840_loadfw(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ +/* cx25850-audio.c */ +void cx25840_audio_set_path(struct i2c_client *client); +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); + +extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops; + +/* ----------------------------------------------------------------------- */ +/* cx25850-vbi.c */ +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); + +/* ----------------------------------------------------------------------- */ +/* cx25850-ir.c */ +extern const struct v4l2_subdev_ir_ops cx25840_ir_ops; +int cx25840_ir_log_status(struct v4l2_subdev *sd); +int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled); +int cx25840_ir_probe(struct v4l2_subdev *sd); +int cx25840_ir_remove(struct v4l2_subdev *sd); + +#endif diff --git a/drivers/media/i2c/cx25840/cx25840-firmware.c b/drivers/media/i2c/cx25840/cx25840-firmware.c new file mode 100644 index 000000000000..b3169f94ece8 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-firmware.c @@ -0,0 +1,175 @@ +/* cx25840 firmware functions + * + * This program is free software; 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 +#include +#include +#include +#include + +#include "cx25840-core.h" + +/* + * Mike Isely - The FWSEND parameter controls the + * size of the firmware chunks sent down the I2C bus to the chip. + * Previously this had been set to 1024 but unfortunately some I2C + * implementations can't transfer data in such big gulps. + * Specifically, the pvrusb2 driver has a hard limit of around 60 + * bytes, due to the encapsulation there of I2C traffic into USB + * messages. So we have to significantly reduce this parameter. + */ +#define FWSEND 48 + +#define FWDEV(x) &((x)->dev) + +static char *firmware = ""; + +module_param(firmware, charp, 0444); + +MODULE_PARM_DESC(firmware, "Firmware image to load"); + +static void start_fw_load(struct i2c_client *client) +{ + /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ + cx25840_write(client, 0x800, 0x00); + cx25840_write(client, 0x801, 0x00); + // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 + cx25840_write(client, 0x803, 0x0b); + /* AUTO_INC_DIS=1 */ + cx25840_write(client, 0x000, 0x20); +} + +static void end_fw_load(struct i2c_client *client) +{ + /* AUTO_INC_DIS=0 */ + cx25840_write(client, 0x000, 0x00); + /* DL_ENABLE=0 */ + cx25840_write(client, 0x803, 0x03); +} + +#define CX2388x_FIRMWARE "v4l-cx23885-avcore-01.fw" +#define CX231xx_FIRMWARE "v4l-cx231xx-avcore-01.fw" +#define CX25840_FIRMWARE "v4l-cx25840.fw" + +static const char *get_fw_name(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (firmware[0]) + return firmware; + if (is_cx2388x(state)) + return CX2388x_FIRMWARE; + if (is_cx231xx(state)) + return CX231xx_FIRMWARE; + return CX25840_FIRMWARE; +} + +static int check_fw_load(struct i2c_client *client, int size) +{ + /* DL_ADDR_HB DL_ADDR_LB */ + int s = cx25840_read(client, 0x801) << 8; + s |= cx25840_read(client, 0x800); + + if (size != s) { + v4l_err(client, "firmware %s load failed\n", + get_fw_name(client)); + return -EINVAL; + } + + v4l_info(client, "loaded %s firmware (%d bytes)\n", + get_fw_name(client), size); + return 0; +} + +static int fw_write(struct i2c_client *client, const u8 *data, int size) +{ + if (i2c_master_send(client, data, size) < size) { + v4l_err(client, "firmware load i2c failure\n"); + return -ENOSYS; + } + + return 0; +} + +int cx25840_loadfw(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + const struct firmware *fw = NULL; + u8 buffer[FWSEND]; + const u8 *ptr; + const char *fwname = get_fw_name(client); + int size, retval; + int MAX_BUF_SIZE = FWSEND; + u32 gpio_oe = 0, gpio_da = 0; + + if (is_cx2388x(state)) { + /* Preserve the GPIO OE and output bits */ + gpio_oe = cx25840_read(client, 0x160); + gpio_da = cx25840_read(client, 0x164); + } + + if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { + v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); + MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ + } + + if (request_firmware(&fw, fwname, FWDEV(client)) != 0) { + v4l_err(client, "unable to open firmware %s\n", fwname); + return -EINVAL; + } + + start_fw_load(client); + + buffer[0] = 0x08; + buffer[1] = 0x02; + + size = fw->size; + ptr = fw->data; + while (size > 0) { + int len = min(MAX_BUF_SIZE - 2, size); + + memcpy(buffer + 2, ptr, len); + + retval = fw_write(client, buffer, len + 2); + + if (retval < 0) { + release_firmware(fw); + return retval; + } + + size -= len; + ptr += len; + } + + end_fw_load(client); + + size = fw->size; + release_firmware(fw); + + if (is_cx2388x(state)) { + /* Restore GPIO configuration after f/w load */ + cx25840_write(client, 0x160, gpio_oe); + cx25840_write(client, 0x164, gpio_da); + } + + return check_fw_load(client, size); +} + +MODULE_FIRMWARE(CX2388x_FIRMWARE); +MODULE_FIRMWARE(CX231xx_FIRMWARE); +MODULE_FIRMWARE(CX25840_FIRMWARE); + diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c new file mode 100644 index 000000000000..38ce76ed1924 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -0,0 +1,1281 @@ +/* + * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores + * + * Integrated Consumer Infrared Controller + * + * Copyright (C) 2010 Andy Walls + * + * This program is free software; 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 +#include +#include +#include +#include + +#include "cx25840-core.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages"); + +#define CX25840_IR_REG_BASE 0x200 + +#define CX25840_IR_CNTRL_REG 0x200 +#define CNTRL_WIN_3_3 0x00000000 +#define CNTRL_WIN_4_3 0x00000001 +#define CNTRL_WIN_3_4 0x00000002 +#define CNTRL_WIN_4_4 0x00000003 +#define CNTRL_WIN 0x00000003 +#define CNTRL_EDG_NONE 0x00000000 +#define CNTRL_EDG_FALL 0x00000004 +#define CNTRL_EDG_RISE 0x00000008 +#define CNTRL_EDG_BOTH 0x0000000C +#define CNTRL_EDG 0x0000000C +#define CNTRL_DMD 0x00000010 +#define CNTRL_MOD 0x00000020 +#define CNTRL_RFE 0x00000040 +#define CNTRL_TFE 0x00000080 +#define CNTRL_RXE 0x00000100 +#define CNTRL_TXE 0x00000200 +#define CNTRL_RIC 0x00000400 +#define CNTRL_TIC 0x00000800 +#define CNTRL_CPL 0x00001000 +#define CNTRL_LBM 0x00002000 +#define CNTRL_R 0x00004000 + +#define CX25840_IR_TXCLK_REG 0x204 +#define TXCLK_TCD 0x0000FFFF + +#define CX25840_IR_RXCLK_REG 0x208 +#define RXCLK_RCD 0x0000FFFF + +#define CX25840_IR_CDUTY_REG 0x20C +#define CDUTY_CDC 0x0000000F + +#define CX25840_IR_STATS_REG 0x210 +#define STATS_RTO 0x00000001 +#define STATS_ROR 0x00000002 +#define STATS_RBY 0x00000004 +#define STATS_TBY 0x00000008 +#define STATS_RSR 0x00000010 +#define STATS_TSR 0x00000020 + +#define CX25840_IR_IRQEN_REG 0x214 +#define IRQEN_RTE 0x00000001 +#define IRQEN_ROE 0x00000002 +#define IRQEN_RSE 0x00000010 +#define IRQEN_TSE 0x00000020 +#define IRQEN_MSK 0x00000033 + +#define CX25840_IR_FILTR_REG 0x218 +#define FILTR_LPF 0x0000FFFF + +#define CX25840_IR_FIFO_REG 0x23C +#define FIFO_RXTX 0x0000FFFF +#define FIFO_RXTX_LVL 0x00010000 +#define FIFO_RXTX_RTO 0x0001FFFF +#define FIFO_RX_NDV 0x00020000 +#define FIFO_RX_DEPTH 8 +#define FIFO_TX_DEPTH 8 + +#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ +#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2) + +/* + * We use this union internally for convenience, but callers to tx_write + * and rx_read will be expecting records of type struct ir_raw_event. + * Always ensure the size of this union is dictated by struct ir_raw_event. + */ +union cx25840_ir_fifo_rec { + u32 hw_fifo_data; + struct ir_raw_event ir_core_data; +}; + +#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) +#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) + +struct cx25840_ir_state { + struct i2c_client *c; + + struct v4l2_subdev_ir_parameters rx_params; + struct mutex rx_params_lock; /* protects Rx parameter settings cache */ + atomic_t rxclk_divider; + atomic_t rx_invert; + + struct kfifo rx_kfifo; + spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */ + + struct v4l2_subdev_ir_parameters tx_params; + struct mutex tx_params_lock; /* protects Tx parameter settings cache */ + atomic_t txclk_divider; +}; + +static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + return state ? state->ir_state : NULL; +} + + +/* + * Rx and Tx Clock Divider register computations + * + * Note the largest clock divider value of 0xffff corresponds to: + * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_clock_divider(unsigned int d) +{ + if (d > RXCLK_RCD + 1) + d = RXCLK_RCD; + else if (d < 2) + d = 1; + else + d--; + return (u16) d; +} + +static inline u16 ns_to_clock_divider(unsigned int ns) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int clock_divider_to_ns(unsigned int divider) +{ + /* Period of the Rx or Tx clock in ns */ + return DIV_ROUND_CLOSEST((divider + 1) * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static inline u16 carrier_freq_to_clock_divider(unsigned int freq) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16)); +} + +static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) +{ + return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16); +} + +static inline u16 freq_to_clock_divider(unsigned int freq, + unsigned int rollovers) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers)); +} + +static inline unsigned int clock_divider_to_freq(unsigned int divider, + unsigned int rollovers) +{ + return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, + (divider + 1) * rollovers); +} + +/* + * Low Pass Filter register calculations + * + * Note the largest count value of 0xffff corresponds to: + * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_lpf_count(unsigned int d) +{ + if (d > FILTR_LPF) + d = FILTR_LPF; + else if (d < 4) + d = 0; + return (u16) d; +} + +static inline u16 ns_to_lpf_count(unsigned int ns) +{ + return count_to_lpf_count( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int lpf_count_to_ns(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in ns */ + return DIV_ROUND_CLOSEST(count * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static inline unsigned int lpf_count_to_us(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in us */ + return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000); +} + +/* + * FIFO register pulse width count compuations + */ +static u32 clock_divider_to_resolution(u16 divider) +{ + /* + * Resolution is the duration of 1 tick of the readable portion of + * of the pulse width counter as read from the FIFO. The two lsb's are + * not readable, hence the << 2. This function returns ns. + */ + return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static u64 pulse_width_count_to_ns(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ + rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ + if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return n; +} + +#if 0 +/* Keep as we will need this for Transmit functionality */ +static u16 ns_to_pulse_width_count(u32 ns, u16 divider) +{ + u64 n; + u32 d; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not accessible, hence + * the (1 << 2) + */ + n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */ + d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */ + rem = do_div(n, d); + if (rem >= d / 2) + n++; + + if (n > FIFO_RXTX) + n = FIFO_RXTX; + else if (n == 0) + n = 1; + return (u16) n; +} + +#endif +static unsigned int pulse_width_count_to_us(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ + rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ + if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return (unsigned int) n; +} + +/* + * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts + * + * The total pulse clock count is an 18 bit pulse width timer count as the most + * significant part and (up to) 16 bit clock divider count as a modulus. + * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse + * width timer count's least significant bit. + */ +static u64 ns_to_pulse_clocks(u32 ns) +{ + u64 clocks; + u32 rem; + clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ + rem = do_div(clocks, 1000); /* /1000 = cycles */ + if (rem >= 1000 / 2) + clocks++; + return clocks; +} + +static u16 pulse_clocks_to_clock_divider(u64 count) +{ + do_div(count, (FIFO_RXTX << 2) | 0x3); + + /* net result needs to be rounded down and decremented by 1 */ + if (count > RXCLK_RCD + 1) + count = RXCLK_RCD; + else if (count < 2) + count = 1; + else + count--; + return (u16) count; +} + +/* + * IR Control Register helpers + */ +enum tx_fifo_watermark { + TX_FIFO_HALF_EMPTY = 0, + TX_FIFO_EMPTY = CNTRL_TIC, +}; + +enum rx_fifo_watermark { + RX_FIFO_HALF_FULL = 0, + RX_FIFO_NOT_EMPTY = CNTRL_RIC, +}; + +static inline void control_tx_irq_watermark(struct i2c_client *c, + enum tx_fifo_watermark level) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level); +} + +static inline void control_rx_irq_watermark(struct i2c_client *c, + enum rx_fifo_watermark level) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level); +} + +static inline void control_tx_enable(struct i2c_client *c, bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), + enable ? (CNTRL_TXE | CNTRL_TFE) : 0); +} + +static inline void control_rx_enable(struct i2c_client *c, bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), + enable ? (CNTRL_RXE | CNTRL_RFE) : 0); +} + +static inline void control_tx_modulation_enable(struct i2c_client *c, + bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD, + enable ? CNTRL_MOD : 0); +} + +static inline void control_rx_demodulation_enable(struct i2c_client *c, + bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD, + enable ? CNTRL_DMD : 0); +} + +static inline void control_rx_s_edge_detection(struct i2c_client *c, + u32 edge_types) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, + edge_types & CNTRL_EDG_BOTH); +} + +static void control_rx_s_carrier_window(struct i2c_client *c, + unsigned int carrier, + unsigned int *carrier_range_low, + unsigned int *carrier_range_high) +{ + u32 v; + unsigned int c16 = carrier * 16; + + if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { + v = CNTRL_WIN_3_4; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); + } else { + v = CNTRL_WIN_3_3; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); + } + + if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { + v |= CNTRL_WIN_4_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); + } else { + v |= CNTRL_WIN_3_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); + } + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v); +} + +static inline void control_tx_polarity_invert(struct i2c_client *c, + bool invert) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL, + invert ? CNTRL_CPL : 0); +} + +/* + * IR Rx & Tx Clock Register helpers + */ +static unsigned int txclk_tx_s_carrier(struct i2c_client *c, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static unsigned int rxclk_rx_s_carrier(struct i2c_client *c, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +/* + * IR Tx Carrier Duty Cycle register helpers + */ +static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c, + unsigned int duty_cycle) +{ + u32 n; + n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ + if (n != 0) + n--; + if (n > 15) + n = 15; + cx25840_write4(c, CX25840_IR_CDUTY_REG, n); + return DIV_ROUND_CLOSEST((n + 1) * 100, 16); +} + +/* + * IR Filter Register helpers + */ +static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns) +{ + u32 count = ns_to_lpf_count(min_width_ns); + cx25840_write4(c, CX25840_IR_FILTR_REG, count); + return lpf_count_to_ns(count); +} + +/* + * IR IRQ Enable Register helpers + */ +static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx23885(state) || is_cx23887(state)) + mask ^= IRQEN_MSK; + mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); + cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, + ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); +} + +static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx23885(state) || is_cx23887(state)) + mask ^= IRQEN_MSK; + mask &= IRQEN_TSE; + cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask); +} + +/* + * V4L2 Subdevice IR Ops + */ +int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c = NULL; + unsigned long flags; + + union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; + unsigned int i, j, k; + u32 events, v; + int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; + u32 cntrl, irqen, stats; + + *handled = false; + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + + /* Only support the IR controller for the CX2388[57] AV Core for now */ + if (!(is_cx23885(state) || is_cx23887(state))) + return -ENODEV; + + cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); + irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); + if (is_cx23885(state) || is_cx23887(state)) + irqen ^= IRQEN_MSK; + stats = cx25840_read4(c, CX25840_IR_STATS_REG); + + tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ + rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ + rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ + ror = stats & STATS_ROR; /* Rx FIFO Over Run */ + + tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ + rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ + rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ + roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ + + v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n", + tsr ? "tsr" : " ", rsr ? "rsr" : " ", + rto ? "rto" : " ", ror ? "ror" : " ", + stats & STATS_TBY ? "tby" : " ", + stats & STATS_RBY ? "rby" : " "); + + v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n", + tse ? "tse" : " ", rse ? "rse" : " ", + rte ? "rte" : " ", roe ? "roe" : " "); + + /* + * Transmitter interrupt service + */ + if (tse && tsr) { + /* + * TODO: + * Check the watermark threshold setting + * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo + * Push the data to the hardware FIFO. + * If there was nothing more to send in the tx_kfifo, disable + * the TSR IRQ and notify the v4l2_device. + * If there was something in the tx_kfifo, check the tx_kfifo + * level and notify the v4l2_device, if it is low. + */ + /* For now, inhibit TSR interrupt until Tx is implemented */ + irqenable_tx(sd, 0); + events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); + *handled = true; + } + + /* + * Receiver interrupt service + */ + kror = 0; + if ((rse && rsr) || (rte && rto)) { + /* + * Receive data on RSR to clear the STATS_RSR. + * Receive data on RTO, since we may not have yet hit the RSR + * watermark when we receive the RTO. + */ + for (i = 0, v = FIFO_RX_NDV; + (v & FIFO_RX_NDV) && !kror; i = 0) { + for (j = 0; + (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { + v = cx25840_read4(c, CX25840_IR_FIFO_REG); + rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; + i++; + } + if (i == 0) + break; + j = i * sizeof(union cx25840_ir_fifo_rec); + k = kfifo_in_locked(&ir_state->rx_kfifo, + (unsigned char *) rx_data, j, + &ir_state->rx_kfifo_lock); + if (k != j) + kror++; /* rx_kfifo over run */ + } + *handled = true; + } + + events = 0; + v = 0; + if (kror) { + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver software FIFO overrun\n"); + } + if (roe && ror) { + /* + * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear + * the Rx FIFO Over Run status (STATS_ROR) + */ + v |= CNTRL_RFE; + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); + } + if (rte && rto) { + /* + * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear + * the Rx Pulse Width Timer Time Out (STATS_RTO) + */ + v |= CNTRL_RXE; + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + } + if (v) { + /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ + cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v); + cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl); + *handled = true; + } + spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); + if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); + + if (events) + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); + return 0; +} + +/* Receiver */ +static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + bool invert; + u16 divider; + unsigned int i, n; + union cx25840_ir_fifo_rec *p; + unsigned u, v, w; + + if (ir_state == NULL) + return -ENODEV; + + invert = (bool) atomic_read(&ir_state->rx_invert); + divider = (u16) atomic_read(&ir_state->rxclk_divider); + + n = count / sizeof(union cx25840_ir_fifo_rec) + * sizeof(union cx25840_ir_fifo_rec); + if (n == 0) { + *num = 0; + return 0; + } + + n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n, + &ir_state->rx_kfifo_lock); + + n /= sizeof(union cx25840_ir_fifo_rec); + *num = n * sizeof(union cx25840_ir_fifo_rec); + + for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { + + if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { + /* Assume RTO was because of no IR light input */ + u = 0; + w = 1; + } else { + u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; + if (invert) + u = u ? 0 : 1; + w = 0; + } + + v = (unsigned) pulse_width_count_to_ns( + (u16) (p->hw_fifo_data & FIFO_RXTX), divider); + if (v > IR_MAX_DURATION) + v = IR_MAX_DURATION; + + init_ir_raw_event(&p->ir_core_data); + p->ir_core_data.pulse = u; + p->ir_core_data.duration = v; + p->ir_core_data.timeout = w; + + v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n", + v, u ? "mark" : "space", w ? "(timed out)" : ""); + if (w) + v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); + } + return 0; +} + +static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + mutex_lock(&ir_state->rx_params_lock); + memcpy(p, &ir_state->rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + mutex_lock(&ir_state->rx_params_lock); + + /* Disable or slow down all IR Rx circuits and counters */ + irqenable_rx(sd, 0); + control_rx_enable(c, false); + control_rx_demodulation_enable(c, false); + control_rx_s_edge_detection(c, CNTRL_EDG_NONE); + filter_rx_s_min_width(c, 0); + cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD); + + ir_state->rx_params.shutdown = true; + + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + struct v4l2_subdev_ir_parameters *o; + u16 rxclk_divider; + + if (ir_state == NULL) + return -ENODEV; + + if (p->shutdown) + return cx25840_ir_rx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + c = ir_state->c; + o = &ir_state->rx_params; + + mutex_lock(&ir_state->rx_params_lock); + + o->shutdown = p->shutdown; + + p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + o->mode = p->mode; + + p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); + o->bytes_per_data_element = p->bytes_per_data_element; + + /* Before we tweak the hardware, we have to disable the receiver */ + irqenable_rx(sd, 0); + control_rx_enable(c, false); + + control_rx_demodulation_enable(c, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq, + &rxclk_divider); + + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = 50; + o->duty_cycle = p->duty_cycle; + + control_rx_s_carrier_window(c, p->carrier_freq, + &p->carrier_range_lower, + &p->carrier_range_upper); + o->carrier_range_lower = p->carrier_range_lower; + o->carrier_range_upper = p->carrier_range_upper; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); + } else { + p->max_pulse_width = + rxclk_rx_s_max_pulse_width(c, p->max_pulse_width, + &rxclk_divider); + } + o->max_pulse_width = p->max_pulse_width; + atomic_set(&ir_state->rxclk_divider, rxclk_divider); + + p->noise_filter_min_width = + filter_rx_s_min_width(c, p->noise_filter_min_width); + o->noise_filter_min_width = p->noise_filter_min_width; + + p->resolution = clock_divider_to_resolution(rxclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_rx_irq_watermark(c, RX_FIFO_HALF_FULL); + + control_rx_s_edge_detection(c, CNTRL_EDG_BOTH); + + o->invert_level = p->invert_level; + atomic_set(&ir_state->rx_invert, p->invert_level); + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + unsigned long flags; + + spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); + kfifo_reset(&ir_state->rx_kfifo); + spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); + if (p->interrupt_enable) + irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); + control_rx_enable(c, p->enable); + } + + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +/* Transmitter */ +static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + +#if 0 + /* + * FIXME - the code below is an incomplete and untested sketch of what + * may need to be done. The critical part is to get 4 (or 8) pulses + * from the tx_kfifo, or converted from ns to the proper units from the + * input, and push them off to the hardware Tx FIFO right away, if the + * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in + * a less critical timeframe. Also watch out for overruning the + * tx_kfifo - don't let it happen and let the caller know not all his + * pulses were written. + */ + u32 *ns_pulse = (u32 *) buf; + unsigned int n; + u32 fifo_pulse[FIFO_TX_DEPTH]; + u32 mark; + + /* Compute how much we can fit in the tx kfifo */ + n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo); + n = min(n, (unsigned int) count); + n /= sizeof(u32); + + /* FIXME - turn on Tx Fifo service interrupt + * check hardware fifo level, and other stuff + */ + for (i = 0; i < n; ) { + for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) { + mark = ns_pulse[i] & LEVEL_MASK; + fifo_pulse[j] = ns_to_pulse_width_count( + ns_pulse[i] & + ~LEVEL_MASK, + ir_state->txclk_divider); + if (mark) + fifo_pulse[j] &= FIFO_RXTX_LVL; + i++; + } + kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse, + j * sizeof(u32)); + } + *num = n * sizeof(u32); +#else + /* For now enable the Tx FIFO Service interrupt & pretend we did work */ + irqenable_tx(sd, IRQEN_TSE); + *num = count; +#endif + return 0; +} + +static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + mutex_lock(&ir_state->tx_params_lock); + memcpy(p, &ir_state->tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + +static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + mutex_lock(&ir_state->tx_params_lock); + + /* Disable or slow down all IR Tx circuits and counters */ + irqenable_tx(sd, 0); + control_tx_enable(c, false); + control_tx_modulation_enable(c, false); + cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD); + + ir_state->tx_params.shutdown = true; + + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + +static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + struct v4l2_subdev_ir_parameters *o; + u16 txclk_divider; + + if (ir_state == NULL) + return -ENODEV; + + if (p->shutdown) + return cx25840_ir_tx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + c = ir_state->c; + o = &ir_state->tx_params; + mutex_lock(&ir_state->tx_params_lock); + + o->shutdown = p->shutdown; + + p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + o->mode = p->mode; + + p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); + o->bytes_per_data_element = p->bytes_per_data_element; + + /* Before we tweak the hardware, we have to disable the transmitter */ + irqenable_tx(sd, 0); + control_tx_enable(c, false); + + control_tx_modulation_enable(c, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq, + &txclk_divider); + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle); + o->duty_cycle = p->duty_cycle; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); + } else { + p->max_pulse_width = + txclk_tx_s_max_pulse_width(c, p->max_pulse_width, + &txclk_divider); + } + o->max_pulse_width = p->max_pulse_width; + atomic_set(&ir_state->txclk_divider, txclk_divider); + + p->resolution = clock_divider_to_resolution(txclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY); + + control_tx_polarity_invert(c, p->invert_carrier_sense); + o->invert_carrier_sense = p->invert_carrier_sense; + + /* + * FIXME: we don't have hardware help for IO pin level inversion + * here like we have on the CX23888. + * Act on this with some mix of logical inversion of data levels, + * carrier polarity, and carrier duty cycle. + */ + o->invert_level = p->invert_level; + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + /* reset tx_fifo here */ + if (p->interrupt_enable) + irqenable_tx(sd, IRQEN_TSE); + control_tx_enable(c, p->enable); + } + + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + + +/* + * V4L2 Subdevice Core Ops support + */ +int cx25840_ir_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *c = state->c; + char *s; + int i, j; + u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr; + + /* The CX23888 chip doesn't have an IR controller on the A/V core */ + if (is_cx23888(state)) + return 0; + + cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); + txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD; + rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD; + cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC; + stats = cx25840_read4(c, CX25840_IR_STATS_REG); + irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); + if (is_cx23885(state) || is_cx23887(state)) + irqen ^= IRQEN_MSK; + filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF; + + v4l2_info(sd, "IR Receiver:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_RXE ? "yes" : "no"); + v4l2_info(sd, "\tDemodulation from a carrier: %s\n", + cntrl & CNTRL_DMD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_RFE ? "enabled" : "disabled"); + switch (cntrl & CNTRL_EDG) { + case CNTRL_EDG_NONE: + s = "disabled"; + break; + case CNTRL_EDG_FALL: + s = "falling edge"; + break; + case CNTRL_EDG_RISE: + s = "rising edge"; + break; + case CNTRL_EDG_BOTH: + s = "rising & falling edges"; + break; + default: + s = "??? edge"; + break; + } + v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); + v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", + cntrl & CNTRL_R ? "not loaded" : "overflow marker"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); + v4l2_info(sd, "\tLoopback mode: %s\n", + cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); + if (cntrl & CNTRL_DMD) { + v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(rxclk)); + switch (cntrl & CNTRL_WIN) { + case CNTRL_WIN_3_3: + i = 3; + j = 3; + break; + case CNTRL_WIN_4_3: + i = 4; + j = 3; + break; + case CNTRL_WIN_3_4: + i = 3; + j = 4; + break; + case CNTRL_WIN_4_4: + i = 4; + j = 4; + break; + default: + i = 0; + j = 0; + break; + } + v4l2_info(sd, "\tNext carrier edge window: 16 clocks " + "-%1d/+%1d, %u to %u Hz\n", i, j, + clock_divider_to_freq(rxclk, 16 + j), + clock_divider_to_freq(rxclk, 16 - i)); + } + v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, rxclk), + pulse_width_count_to_ns(FIFO_RXTX, rxclk)); + v4l2_info(sd, "\tLow pass filter: %s\n", + filtr ? "enabled" : "disabled"); + if (filtr) + v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " + "%u ns\n", + lpf_count_to_us(filtr), + lpf_count_to_ns(filtr)); + v4l2_info(sd, "\tPulse width timer timed-out: %s\n", + stats & STATS_RTO ? "yes" : "no"); + v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", + irqen & IRQEN_RTE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO overrun: %s\n", + stats & STATS_ROR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", + irqen & IRQEN_ROE ? "enabled" : "disabled"); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_RBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_RSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_RSE ? "enabled" : "disabled"); + + v4l2_info(sd, "IR Transmitter:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_TXE ? "yes" : "no"); + v4l2_info(sd, "\tModulation onto a carrier: %s\n", + cntrl & CNTRL_MOD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_TFE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_TIC ? "not empty" : "half full or less"); + v4l2_info(sd, "\tCarrier polarity: %s\n", + cntrl & CNTRL_CPL ? "space:burst mark:noburst" + : "space:noburst mark:burst"); + if (cntrl & CNTRL_MOD) { + v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(txclk)); + v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", + cduty + 1); + } + v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, txclk), + pulse_width_count_to_ns(FIFO_RXTX, txclk)); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_TBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_TSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_TSE ? "enabled" : "disabled"); + + return 0; +} + + +const struct v4l2_subdev_ir_ops cx25840_ir_ops = { + .rx_read = cx25840_ir_rx_read, + .rx_g_parameters = cx25840_ir_rx_g_parameters, + .rx_s_parameters = cx25840_ir_rx_s_parameters, + + .tx_write = cx25840_ir_tx_write, + .tx_g_parameters = cx25840_ir_tx_g_parameters, + .tx_s_parameters = cx25840_ir_tx_s_parameters, +}; + + +static const struct v4l2_subdev_ir_parameters default_rx_params = { + .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */ + + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + .noise_filter_min_width = 333333, /* ns */ + .carrier_range_lower = 35000, + .carrier_range_upper = 37000, + .invert_level = false, +}; + +static const struct v4l2_subdev_ir_parameters default_tx_params = { + .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ + .duty_cycle = 25, /* 25 % - RC-5 carrier */ + .invert_level = false, + .invert_carrier_sense = false, +}; + +int cx25840_ir_probe(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state; + struct v4l2_subdev_ir_parameters default_params; + + /* Only init the IR controller for the CX2388[57] AV Core for now */ + if (!(is_cx23885(state) || is_cx23887(state))) + return 0; + + ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL); + if (ir_state == NULL) + return -ENOMEM; + + spin_lock_init(&ir_state->rx_kfifo_lock); + if (kfifo_alloc(&ir_state->rx_kfifo, + CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) { + kfree(ir_state); + return -ENOMEM; + } + + ir_state->c = state->c; + state->ir_state = ir_state; + + /* Ensure no interrupts arrive yet */ + if (is_cx23885(state) || is_cx23887(state)) + cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK); + else + cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); + + mutex_init(&ir_state->rx_params_lock); + memcpy(&default_params, &default_rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); + + mutex_init(&ir_state->tx_params_lock); + memcpy(&default_params, &default_tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); + + return 0; +} + +int cx25840_ir_remove(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + cx25840_ir_rx_shutdown(sd); + cx25840_ir_tx_shutdown(sd); + + kfifo_free(&ir_state->rx_kfifo); + kfree(ir_state); + state->ir_state = NULL; + return 0; +} diff --git a/drivers/media/i2c/cx25840/cx25840-vbi.c b/drivers/media/i2c/cx25840/cx25840-vbi.c new file mode 100644 index 000000000000..64a4004f8a97 --- /dev/null +++ b/drivers/media/i2c/cx25840/cx25840-vbi.c @@ -0,0 +1,256 @@ +/* cx25840 VBI functions + * + * This program is free software; 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 +#include +#include +#include + +#include "cx25840-core.h" + +static int odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static int decode_vps(u8 * dst, u8 * p) +{ + static const u8 biphase_tbl[] = { + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, + 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, + 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, + 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, + 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, + 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, + 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + }; + + u8 c, err = 0; + int i; + + for (i = 0; i < 2 * 13; i += 2) { + err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; + c = (biphase_tbl[p[i + 1]] & 0xf) | + ((biphase_tbl[p[i]] & 0xf) << 4); + dst[i / 2] = c; + } + + return err & 0xf0; +} + +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + static const u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int is_pal = !(state->std & V4L2_STD_525_60); + int i; + + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx25840_read(client, 0x404) & 0x10) == 0) + return 0; + + if (is_pal) { + for (i = 7; i <= 23; i++) { + u8 v = cx25840_read(client, 0x424 + i - 7); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } else { + for (i = 10; i <= 21; i++) { + u8 v = cx25840_read(client, 0x424 + i - 10); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } + return 0; +} + +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + + /* Setup standard */ + cx25840_std_setup(client); + + /* VBI Offset */ + cx25840_write(client, 0x47f, vbi_offset); + cx25840_write(client, 0x404, 0x2e); + return 0; +} + +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; + + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup standard */ + cx25840_std_setup(client); + + /* Sliced VBI */ + cx25840_write(client, 0x404, 0x32); /* Ancillary data */ + cx25840_write(client, 0x406, 0x13); + cx25840_write(client, 0x47f, vbi_offset); + + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } + + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; + } + } + } + + if (is_pal) { + for (x = 1, i = 0x424; i <= 0x434; i++, x++) + cx25840_write(client, i, lcr[6 + x]); + } else { + for (x = 1, i = 0x424; i <= 0x430; i++, x++) + cx25840_write(client, i, lcr[9 + x]); + for (i = 0x431; i <= 0x434; i++) + cx25840_write(client, i, 0); + } + + cx25840_write(client, 0x43c, 0x16); + cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22); + return 0; +} + +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct cx25840_state *state = to_state(sd); + u8 *p = vbi->p; + int id1, id2, l, err = 0; + + if (p[0] || p[1] != 0xff || p[2] != 0xff || + (p[3] != 0x55 && p[3] != 0x91)) { + vbi->line = vbi->type = 0; + return 0; + } + + p += 4; + id1 = p[-1]; + id2 = p[0] & 0xf; + l = p[2] & 0x3f; + l += state->vbi_line_offset; + p += 4; + + switch (id2) { + case 1: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case 4: + id2 = V4L2_SLICED_WSS_625; + break; + case 6: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + id2 = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) + err = 1; + break; + default: + id2 = 0; + err = 1; + break; + } + + vbi->type = err ? 0 : id2; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (id1 == 0x55); + vbi->p = p; + return 0; +} diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c new file mode 100644 index 000000000000..04f192a0398a --- /dev/null +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -0,0 +1,489 @@ +/* + * + * keyboard input driver for i2c IR remote controls + * + * Copyright (c) 2000-2003 Gerd Knorr + * modified for PixelView (BT878P+W/FM) by + * Michal Kochanowicz + * Christoph Bartelmus + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by + * Ulrich Mueller + * modified for em2820 based USB TV tuners by + * Markus Rechberger + * modified for DViCO Fusion HDTV 5 RT GOLD by + * Chaogui Zhang + * modified for MSI TV@nywhere Plus by + * Henry Wong + * Mark Schultz + * Brian Rogers + * modified for AVerMedia Cardbus by + * Oldrich Jedlicka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* ----------------------------------------------------------------------- */ +/* insmod parameters */ + +static int debug; +module_param(debug, int, 0644); /* debug level (0,1,2) */ + + +#define MODULE_NAME "ir-kbd-i2c" +#define dprintk(level, fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg) + +/* ----------------------------------------------------------------------- */ + +static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, + int size, int offset) +{ + unsigned char buf[6]; + int start, range, toggle, dev, code, ircode; + + /* poll IR chip */ + if (size != i2c_master_recv(ir->c, buf, size)) + return -EIO; + + /* split rc5 data block ... */ + start = (buf[offset] >> 7) & 1; + range = (buf[offset] >> 6) & 1; + toggle = (buf[offset] >> 5) & 1; + dev = buf[offset] & 0x1f; + code = (buf[offset+1] >> 2) & 0x3f; + + /* rc5 has two start bits + * the first bit must be one + * the second bit defines the command range (1 = 0-63, 0 = 64 - 127) + */ + if (!start) + /* no key pressed */ + return 0; + /* + * Hauppauge remotes (black/silver) always use + * specific device ids. If we do not filter the + * device ids then messages destined for devices + * such as TVs (id=0) will get through causing + * mis-fired events. + * + * We also filter out invalid key presses which + * produce annoying debug log entries. + */ + ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; + if ((ircode & 0x1fff)==0x1fff) + /* invalid key press */ + return 0; + + if (!range) + code += 64; + + dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", + start, range, toggle, dev, code); + + /* return key */ + *ir_key = (dev << 8) | code; + *ir_raw = ircode; + return 1; +} + +static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); +} + +static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + int ret; + unsigned char buf[1] = { 0 }; + + /* + * This is the same apparent "are you ready?" poll command observed + * watching Windows driver traffic and implemented in lirc_zilog. With + * this added, we get far saner remote behavior with z8 chips on usb + * connected devices, even with the default polling interval of 100ms. + */ + ret = i2c_master_send(ir->c, buf, 1); + if (ret != 1) + return (ret < 0) ? ret : -EINVAL; + + return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); +} + +static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char b; + + /* poll IR chip */ + if (1 != i2c_master_recv(ir->c, &b, 1)) { + dprintk(1,"read error\n"); + return -EIO; + } + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char buf[4]; + + /* poll IR chip */ + if (4 != i2c_master_recv(ir->c, buf, 4)) { + dprintk(1,"read error\n"); + return -EIO; + } + + if(buf[0] !=0 || buf[1] !=0 || buf[2] !=0 || buf[3] != 0) + dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __func__, + buf[0], buf[1], buf[2], buf[3]); + + /* no key pressed or signal from other ir remote */ + if(buf[0] != 0x1 || buf[1] != 0xfe) + return 0; + + *ir_key = buf[2]; + *ir_raw = (buf[2] << 8) | buf[3]; + + return 1; +} + +static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char b; + + /* poll IR chip */ + if (1 != i2c_master_recv(ir->c, &b, 1)) { + dprintk(1,"read error\n"); + return -EIO; + } + + /* it seems that 0xFE indicates that a button is still hold + down, while 0xff indicates that no button is hold + down. 0xfe sequences are sometimes interrupted by 0xFF */ + + dprintk(2,"key %02x\n", b); + + if (b == 0xff) + return 0; + + if (b == 0xfe) + /* keep old data */ + return 1; + + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int get_key_avermedia_cardbus(struct IR_i2c *ir, + u32 *ir_key, u32 *ir_raw) +{ + unsigned char subaddr, key, keygroup; + struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, + .buf = &subaddr, .len = 1}, + { .addr = ir->c->addr, .flags = I2C_M_RD, + .buf = &key, .len = 1} }; + subaddr = 0x0d; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + dprintk(1, "read error\n"); + return -EIO; + } + + if (key == 0xff) + return 0; + + subaddr = 0x0b; + msg[1].buf = &keygroup; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + dprintk(1, "read error\n"); + return -EIO; + } + + if (keygroup == 0xff) + return 0; + + dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); + if (keygroup < 2 || keygroup > 3) { + /* Only a warning */ + dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n", + keygroup, key); + } + key |= (keygroup & 1) << 6; + + *ir_key = key; + *ir_raw = key; + return 1; +} + +/* ----------------------------------------------------------------------- */ + +static int ir_key_poll(struct IR_i2c *ir) +{ + static u32 ir_key, ir_raw; + int rc; + + dprintk(3, "%s\n", __func__); + rc = ir->get_key(ir, &ir_key, &ir_raw); + if (rc < 0) { + dprintk(2,"error\n"); + return rc; + } + + if (rc) { + dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); + rc_keydown(ir->rc, ir_key, 0); + } + return 0; +} + +static void ir_work(struct work_struct *work) +{ + int rc; + struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); + + rc = ir_key_poll(ir); + if (rc == -ENODEV) { + rc_unregister_device(ir->rc); + ir->rc = NULL; + return; + } + + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval)); +} + +/* ----------------------------------------------------------------------- */ + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + char *ir_codes = NULL; + const char *name = NULL; + u64 rc_type = RC_TYPE_UNKNOWN; + struct IR_i2c *ir; + struct rc_dev *rc = NULL; + struct i2c_adapter *adap = client->adapter; + unsigned short addr = client->addr; + int err; + + ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL); + if (!ir) + return -ENOMEM; + + ir->c = client; + ir->polling_interval = DEFAULT_POLLING_INTERVAL; + i2c_set_clientdata(client, ir); + + switch(addr) { + case 0x64: + name = "Pixelview"; + ir->get_key = get_key_pixelview; + rc_type = RC_TYPE_OTHER; + ir_codes = RC_MAP_EMPTY; + break; + case 0x18: + case 0x1f: + case 0x1a: + name = "Hauppauge"; + ir->get_key = get_key_haup; + rc_type = RC_TYPE_RC5; + ir_codes = RC_MAP_HAUPPAUGE; + break; + case 0x30: + name = "KNC One"; + ir->get_key = get_key_knc1; + rc_type = RC_TYPE_OTHER; + ir_codes = RC_MAP_EMPTY; + break; + case 0x6b: + name = "FusionHDTV"; + ir->get_key = get_key_fusionhdtv; + rc_type = RC_TYPE_RC5; + ir_codes = RC_MAP_FUSIONHDTV_MCE; + break; + case 0x40: + name = "AVerMedia Cardbus remote"; + ir->get_key = get_key_avermedia_cardbus; + rc_type = RC_TYPE_OTHER; + ir_codes = RC_MAP_AVERMEDIA_CARDBUS; + break; + case 0x71: + name = "Hauppauge/Zilog Z8"; + ir->get_key = get_key_haup_xvr; + rc_type = RC_TYPE_RC5; + ir_codes = RC_MAP_HAUPPAUGE; + break; + } + + /* Let the caller override settings */ + if (client->dev.platform_data) { + const struct IR_i2c_init_data *init_data = + client->dev.platform_data; + + ir_codes = init_data->ir_codes; + rc = init_data->rc_dev; + + name = init_data->name; + if (init_data->type) + rc_type = init_data->type; + + if (init_data->polling_interval) + ir->polling_interval = init_data->polling_interval; + + switch (init_data->internal_get_key_func) { + case IR_KBD_GET_KEY_CUSTOM: + /* The bridge driver provided us its own function */ + ir->get_key = init_data->get_key; + break; + case IR_KBD_GET_KEY_PIXELVIEW: + ir->get_key = get_key_pixelview; + break; + case IR_KBD_GET_KEY_HAUP: + ir->get_key = get_key_haup; + break; + case IR_KBD_GET_KEY_KNC1: + ir->get_key = get_key_knc1; + break; + case IR_KBD_GET_KEY_FUSIONHDTV: + ir->get_key = get_key_fusionhdtv; + break; + case IR_KBD_GET_KEY_HAUP_XVR: + ir->get_key = get_key_haup_xvr; + break; + case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: + ir->get_key = get_key_avermedia_cardbus; + break; + } + } + + if (!rc) { + /* + * If platform_data doesn't specify rc_dev, initilize it + * internally + */ + rc = rc_allocate_device(); + if (!rc) { + err = -ENOMEM; + goto err_out_free; + } + } + ir->rc = rc; + + /* Make sure we are all setup before going on */ + if (!name || !ir->get_key || !rc_type || !ir_codes) { + dprintk(1, ": Unsupported device at address 0x%02x\n", + addr); + err = -ENODEV; + goto err_out_free; + } + + /* Sets name */ + snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); + ir->ir_codes = ir_codes; + + snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", + dev_name(&adap->dev), + dev_name(&client->dev)); + + /* + * Initialize input_dev fields + * It doesn't make sense to allow overriding them via platform_data + */ + rc->input_id.bustype = BUS_I2C; + rc->input_phys = ir->phys; + rc->input_name = ir->name; + + /* + * Initialize the other fields of rc_dev + */ + rc->map_name = ir->ir_codes; + rc->allowed_protos = rc_type; + if (!rc->driver_name) + rc->driver_name = MODULE_NAME; + + err = rc_register_device(rc); + if (err) + goto err_out_free; + + printk(MODULE_NAME ": %s detected at %s [%s]\n", + ir->name, ir->phys, adap->name); + + /* start polling via eventd */ + INIT_DELAYED_WORK(&ir->work, ir_work); + schedule_delayed_work(&ir->work, 0); + + return 0; + + err_out_free: + /* Only frees rc if it were allocated internally */ + rc_free_device(rc); + kfree(ir); + return err; +} + +static int ir_remove(struct i2c_client *client) +{ + struct IR_i2c *ir = i2c_get_clientdata(client); + + /* kill outstanding polls */ + cancel_delayed_work_sync(&ir->work); + + /* unregister device */ + if (ir->rc) + rc_unregister_device(ir->rc); + + /* free memory */ + kfree(ir); + return 0; +} + +static const struct i2c_device_id ir_kbd_id[] = { + /* Generic entry for any IR receiver */ + { "ir_video", 0 }, + /* IR device specific entries should be added here */ + { "ir_rx_z8f0811_haup", 0 }, + { "ir_rx_z8f0811_hdpvr", 0 }, + { } +}; + +static struct i2c_driver ir_kbd_driver = { + .driver = { + .name = "ir-kbd-i2c", + }, + .probe = ir_probe, + .remove = ir_remove, + .id_table = ir_kbd_id, +}; + +module_i2c_driver(ir_kbd_driver); + +/* ----------------------------------------------------------------------- */ + +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); +MODULE_DESCRIPTION("input driver for i2c IR remote controls"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c new file mode 100644 index 000000000000..ee7ca2dcca2f --- /dev/null +++ b/drivers/media/i2c/ks0127.c @@ -0,0 +1,724 @@ +/* + * Video Capture Driver (Video for Linux 1/2) + * for the Matrox Marvel G200,G400 and Rainbow Runner-G series + * + * This module is an interface to the KS0127 video decoder chip. + * + * Copyright (C) 1999 Ryan Drake + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ***************************************************************************** + * + * Modified and extended by + * Mike Bernson + * Gerard v.d. Horst + * Leon van Stuivenberg + * Gernot Ziegler + * + * Version History: + * V1.0 Ryan Drake Initial version by Ryan Drake + * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ks0127.h" + +MODULE_DESCRIPTION("KS0127 video decoder driver"); +MODULE_AUTHOR("Ryan Drake"); +MODULE_LICENSE("GPL"); + +/* Addresses */ +#define I2C_KS0127_ADDON 0xD8 +#define I2C_KS0127_ONBOARD 0xDA + + +/* ks0127 control registers */ +#define KS_STAT 0x00 +#define KS_CMDA 0x01 +#define KS_CMDB 0x02 +#define KS_CMDC 0x03 +#define KS_CMDD 0x04 +#define KS_HAVB 0x05 +#define KS_HAVE 0x06 +#define KS_HS1B 0x07 +#define KS_HS1E 0x08 +#define KS_HS2B 0x09 +#define KS_HS2E 0x0a +#define KS_AGC 0x0b +#define KS_HXTRA 0x0c +#define KS_CDEM 0x0d +#define KS_PORTAB 0x0e +#define KS_LUMA 0x0f +#define KS_CON 0x10 +#define KS_BRT 0x11 +#define KS_CHROMA 0x12 +#define KS_CHROMB 0x13 +#define KS_DEMOD 0x14 +#define KS_SAT 0x15 +#define KS_HUE 0x16 +#define KS_VERTIA 0x17 +#define KS_VERTIB 0x18 +#define KS_VERTIC 0x19 +#define KS_HSCLL 0x1a +#define KS_HSCLH 0x1b +#define KS_VSCLL 0x1c +#define KS_VSCLH 0x1d +#define KS_OFMTA 0x1e +#define KS_OFMTB 0x1f +#define KS_VBICTL 0x20 +#define KS_CCDAT2 0x21 +#define KS_CCDAT1 0x22 +#define KS_VBIL30 0x23 +#define KS_VBIL74 0x24 +#define KS_VBIL118 0x25 +#define KS_VBIL1512 0x26 +#define KS_TTFRAM 0x27 +#define KS_TESTA 0x28 +#define KS_UVOFFH 0x29 +#define KS_UVOFFL 0x2a +#define KS_UGAIN 0x2b +#define KS_VGAIN 0x2c +#define KS_VAVB 0x2d +#define KS_VAVE 0x2e +#define KS_CTRACK 0x2f +#define KS_POLCTL 0x30 +#define KS_REFCOD 0x31 +#define KS_INVALY 0x32 +#define KS_INVALU 0x33 +#define KS_INVALV 0x34 +#define KS_UNUSEY 0x35 +#define KS_UNUSEU 0x36 +#define KS_UNUSEV 0x37 +#define KS_USRSAV 0x38 +#define KS_USREAV 0x39 +#define KS_SHS1A 0x3a +#define KS_SHS1B 0x3b +#define KS_SHS1C 0x3c +#define KS_CMDE 0x3d +#define KS_VSDEL 0x3e +#define KS_CMDF 0x3f +#define KS_GAMMA0 0x40 +#define KS_GAMMA1 0x41 +#define KS_GAMMA2 0x42 +#define KS_GAMMA3 0x43 +#define KS_GAMMA4 0x44 +#define KS_GAMMA5 0x45 +#define KS_GAMMA6 0x46 +#define KS_GAMMA7 0x47 +#define KS_GAMMA8 0x48 +#define KS_GAMMA9 0x49 +#define KS_GAMMA10 0x4a +#define KS_GAMMA11 0x4b +#define KS_GAMMA12 0x4c +#define KS_GAMMA13 0x4d +#define KS_GAMMA14 0x4e +#define KS_GAMMA15 0x4f +#define KS_GAMMA16 0x50 +#define KS_GAMMA17 0x51 +#define KS_GAMMA18 0x52 +#define KS_GAMMA19 0x53 +#define KS_GAMMA20 0x54 +#define KS_GAMMA21 0x55 +#define KS_GAMMA22 0x56 +#define KS_GAMMA23 0x57 +#define KS_GAMMA24 0x58 +#define KS_GAMMA25 0x59 +#define KS_GAMMA26 0x5a +#define KS_GAMMA27 0x5b +#define KS_GAMMA28 0x5c +#define KS_GAMMA29 0x5d +#define KS_GAMMA30 0x5e +#define KS_GAMMA31 0x5f +#define KS_GAMMAD0 0x60 +#define KS_GAMMAD1 0x61 +#define KS_GAMMAD2 0x62 +#define KS_GAMMAD3 0x63 +#define KS_GAMMAD4 0x64 +#define KS_GAMMAD5 0x65 +#define KS_GAMMAD6 0x66 +#define KS_GAMMAD7 0x67 +#define KS_GAMMAD8 0x68 +#define KS_GAMMAD9 0x69 +#define KS_GAMMAD10 0x6a +#define KS_GAMMAD11 0x6b +#define KS_GAMMAD12 0x6c +#define KS_GAMMAD13 0x6d +#define KS_GAMMAD14 0x6e +#define KS_GAMMAD15 0x6f +#define KS_GAMMAD16 0x70 +#define KS_GAMMAD17 0x71 +#define KS_GAMMAD18 0x72 +#define KS_GAMMAD19 0x73 +#define KS_GAMMAD20 0x74 +#define KS_GAMMAD21 0x75 +#define KS_GAMMAD22 0x76 +#define KS_GAMMAD23 0x77 +#define KS_GAMMAD24 0x78 +#define KS_GAMMAD25 0x79 +#define KS_GAMMAD26 0x7a +#define KS_GAMMAD27 0x7b +#define KS_GAMMAD28 0x7c +#define KS_GAMMAD29 0x7d +#define KS_GAMMAD30 0x7e +#define KS_GAMMAD31 0x7f + + +/**************************************************************************** +* mga_dev : represents one ks0127 chip. +****************************************************************************/ + +struct adjust { + int contrast; + int bright; + int hue; + int ugain; + int vgain; +}; + +struct ks0127 { + struct v4l2_subdev sd; + v4l2_std_id norm; + int ident; + u8 regs[256]; +}; + +static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ks0127, sd); +} + + +static int debug; /* insmod parameter */ + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug output"); + +static u8 reg_defaults[64]; + +static void init_reg_defaults(void) +{ + static int initialized; + u8 *table = reg_defaults; + + if (initialized) + return; + initialized = 1; + + table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */ + table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */ + table[KS_CMDC] = 0x00; /* Test options */ + /* clock & input select, write 1 to PORTA */ + table[KS_CMDD] = 0x01; + table[KS_HAVB] = 0x00; /* HAV Start Control */ + table[KS_HAVE] = 0x00; /* HAV End Control */ + table[KS_HS1B] = 0x10; /* HS1 Start Control */ + table[KS_HS1E] = 0x00; /* HS1 End Control */ + table[KS_HS2B] = 0x00; /* HS2 Start Control */ + table[KS_HS2E] = 0x00; /* HS2 End Control */ + table[KS_AGC] = 0x53; /* Manual setting for AGC */ + table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */ + table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */ + table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */ + table[KS_LUMA] = 0x01; /* Luma control */ + table[KS_CON] = 0x00; /* Contrast Control */ + table[KS_BRT] = 0x00; /* Brightness Control */ + table[KS_CHROMA] = 0x2a; /* Chroma control A */ + table[KS_CHROMB] = 0x90; /* Chroma control B */ + table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */ + table[KS_SAT] = 0x00; /* Color Saturation Control*/ + table[KS_HUE] = 0x00; /* Hue Control */ + table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */ + /* Vertical Processing Control B, luma 1 line delayed */ + table[KS_VERTIB] = 0x12; + table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */ + table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */ + table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */ + table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */ + table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */ + /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */ + table[KS_OFMTA] = 0x30; + table[KS_OFMTB] = 0x00; /* Output Control B */ + /* VBI Decoder Control; 4bit fmt: avoid Y overflow */ + table[KS_VBICTL] = 0x5d; + table[KS_CCDAT2] = 0x00; /* Read Only register */ + table[KS_CCDAT1] = 0x00; /* Read Only register */ + table[KS_VBIL30] = 0xa8; /* VBI data decoding options */ + table[KS_VBIL74] = 0xaa; /* VBI data decoding options */ + table[KS_VBIL118] = 0x2a; /* VBI data decoding options */ + table[KS_VBIL1512] = 0x00; /* VBI data decoding options */ + table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */ + table[KS_TESTA] = 0x00; /* test register, shouldn't be written */ + table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */ + table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */ + table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */ + table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */ + table[KS_VAVB] = 0x07; /* VAV Begin */ + table[KS_VAVE] = 0x00; /* VAV End */ + table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */ + table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */ + table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */ + table[KS_INVALY] = 0x10; /* Invalid Y Code */ + table[KS_INVALU] = 0x80; /* Invalid U Code */ + table[KS_INVALV] = 0x80; /* Invalid V Code */ + table[KS_UNUSEY] = 0x10; /* Unused Y Code */ + table[KS_UNUSEU] = 0x80; /* Unused U Code */ + table[KS_UNUSEV] = 0x80; /* Unused V Code */ + table[KS_USRSAV] = 0x00; /* reserved */ + table[KS_USREAV] = 0x00; /* reserved */ + table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */ + /* User Defined SHS1 B, ALT656=1 on 0127B */ + table[KS_SHS1B] = 0x80; + table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */ + table[KS_CMDE] = 0x00; /* Command Register E */ + table[KS_VSDEL] = 0x00; /* VS Delay Control */ + /* Command Register F, update -immediately- */ + /* (there might come no vsync)*/ + table[KS_CMDF] = 0x02; +} + + +/* We need to manually read because of a bug in the KS0127 chip. + * + * An explanation from kayork@mail.utexas.edu: + * + * During I2C reads, the KS0127 only samples for a stop condition + * during the place where the acknowledge bit should be. Any standard + * I2C implementation (correctly) throws in another clock transition + * at the 9th bit, and the KS0127 will not recognize the stop condition + * and will continue to clock out data. + * + * So we have to do the read ourself. Big deal. + * workaround in i2c-algo-bit + */ + + +static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + char val = 0; + struct i2c_msg msgs[] = { + { client->addr, 0, sizeof(reg), ® }, + { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val } + }; + int ret; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + v4l2_dbg(1, debug, sd, "read error\n"); + + return val; +} + + +static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ks0127 *ks = to_ks0127(sd); + char msg[] = { reg, val }; + + if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg)) + v4l2_dbg(1, debug, sd, "write error\n"); + + ks->regs[reg] = val; +} + + +/* generic bit-twiddling */ +static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v) +{ + struct ks0127 *ks = to_ks0127(sd); + + u8 val = ks->regs[reg]; + val = (val & and_v) | or_v; + ks0127_write(sd, reg, val); +} + + + +/**************************************************************************** +* ks0127 private api +****************************************************************************/ +static void ks0127_init(struct v4l2_subdev *sd) +{ + struct ks0127 *ks = to_ks0127(sd); + u8 *table = reg_defaults; + int i; + + ks->ident = V4L2_IDENT_KS0127; + + v4l2_dbg(1, debug, sd, "reset\n"); + msleep(1); + + /* initialize all registers to known values */ + /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */ + + for (i = 1; i < 33; i++) + ks0127_write(sd, i, table[i]); + + for (i = 35; i < 40; i++) + ks0127_write(sd, i, table[i]); + + for (i = 41; i < 56; i++) + ks0127_write(sd, i, table[i]); + + for (i = 58; i < 64; i++) + ks0127_write(sd, i, table[i]); + + + if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) { + ks->ident = V4L2_IDENT_KS0122S; + v4l2_dbg(1, debug, sd, "ks0122s found\n"); + return; + } + + switch (ks0127_read(sd, KS_CMDE) & 0x0f) { + case 0: + v4l2_dbg(1, debug, sd, "ks0127 found\n"); + break; + + case 9: + ks->ident = V4L2_IDENT_KS0127B; + v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n"); + break; + + default: + v4l2_dbg(1, debug, sd, "unknown revision\n"); + break; + } +} + +static int ks0127_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct ks0127 *ks = to_ks0127(sd); + + switch (input) { + case KS_INPUT_COMPOSITE_1: + case KS_INPUT_COMPOSITE_2: + case KS_INPUT_COMPOSITE_3: + case KS_INPUT_COMPOSITE_4: + case KS_INPUT_COMPOSITE_5: + case KS_INPUT_COMPOSITE_6: + v4l2_dbg(1, debug, sd, + "s_routing %d: Composite\n", input); + /* autodetect 50/60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(sd, KS_CMDB, 0xb0, input); + /* non-freerunning mode */ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); + /* chroma trap, HYBWR=1 */ + ks0127_and_or(sd, KS_LUMA, 0x00, + (reg_defaults[KS_LUMA])|0x0c); + /* scaler fullbw, luma comb off */ + ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); + /* manual chroma comb .25 .5 .25 */ + ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90); + + /* chroma path delay */ + ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90); + + ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); + break; + + case KS_INPUT_SVIDEO_1: + case KS_INPUT_SVIDEO_2: + case KS_INPUT_SVIDEO_3: + v4l2_dbg(1, debug, sd, + "s_routing %d: S-Video\n", input); + /* autodetect 50/60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(sd, KS_CMDB, 0xb0, input); + /* non-freerunning mode */ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); + ks0127_and_or(sd, KS_LUMA, 0x00, + reg_defaults[KS_LUMA]); + /* disable luma comb */ + ks0127_and_or(sd, KS_VERTIA, 0x08, + (reg_defaults[KS_VERTIA]&0xf0)|0x01); + ks0127_and_or(sd, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + ks0127_and_or(sd, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); + break; + + case KS_INPUT_YUV656: + v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n"); + if (ks->norm & V4L2_STD_525_60) + /* force 60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03); + else + /* force 50 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02); + + ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */ + /* set input line and VALIGN */ + ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40)); + /* freerunning mode, */ + /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x87); + /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x08); + /* disable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30); + /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ + ks0127_and_or(sd, KS_LUMA, 0x00, 0x71); + ks0127_and_or(sd, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + /* scaler fullbw, luma comb off */ + ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); + + ks0127_and_or(sd, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_and_or(sd, KS_CON, 0x00, 0x00); + ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */ + /* spec: 229 (e5) */ + ks0127_and_or(sd, KS_SAT, 0x00, 0xe8); + ks0127_and_or(sd, KS_HUE, 0x00, 0); + + ks0127_and_or(sd, KS_UGAIN, 0x00, 238); + ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00); + + /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ + ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f); + ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00); + break; + + default: + v4l2_dbg(1, debug, sd, + "s_routing: Unknown input %d\n", input); + break; + } + + /* hack: CDMLPF sometimes spontaneously switches on; */ + /* force back off */ + ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]); + return 0; +} + +static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct ks0127 *ks = to_ks0127(sd); + + /* Set to automatic SECAM/Fsc mode */ + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); + + ks->norm = std; + if (std & V4L2_STD_NTSC) { + v4l2_dbg(1, debug, sd, + "s_std: NTSC_M\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); + } else if (std & V4L2_STD_PAL_N) { + v4l2_dbg(1, debug, sd, + "s_std: NTSC_N (fixme)\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); + } else if (std & V4L2_STD_PAL) { + v4l2_dbg(1, debug, sd, + "s_std: PAL_N\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); + } else if (std & V4L2_STD_PAL_M) { + v4l2_dbg(1, debug, sd, + "s_std: PAL_M (fixme)\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); + } else if (std & V4L2_STD_SECAM) { + v4l2_dbg(1, debug, sd, + "s_std: SECAM\n"); + + /* set to secam autodetection */ + ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20); + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); + schedule_timeout_interruptible(HZ/10+1); + + /* did it autodetect? */ + if (!(ks0127_read(sd, KS_DEMOD) & 0x40)) + /* force to secam mode */ + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); + } else { + v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n", + (unsigned long long)std); + } + return 0; +} + +static int ks0127_s_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable); + if (enable) { + /* All output pins on */ + ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30); + /* Obey the OEN pin */ + ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00); + } else { + /* Video output pins off */ + ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00); + /* Ignore the OEN pin */ + ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80); + } + return 0; +} + +static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) +{ + int stat = V4L2_IN_ST_NO_SIGNAL; + u8 status; + v4l2_std_id std = V4L2_STD_ALL; + + status = ks0127_read(sd, KS_STAT); + if (!(status & 0x20)) /* NOVID not set */ + stat = 0; + if (!(status & 0x01)) /* CLOCK set */ + stat |= V4L2_IN_ST_NO_COLOR; + if ((status & 0x08)) /* PALDET set */ + std = V4L2_STD_PAL; + else + std = V4L2_STD_NTSC; + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = stat; + return 0; +} + +static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + v4l2_dbg(1, debug, sd, "querystd\n"); + return ks0127_status(sd, NULL, std); +} + +static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + v4l2_dbg(1, debug, sd, "g_input_status\n"); + return ks0127_status(sd, status, NULL); +} + +static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ks0127 *ks = to_ks0127(sd); + + return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ks0127_core_ops = { + .g_chip_ident = ks0127_g_chip_ident, + .s_std = ks0127_s_std, +}; + +static const struct v4l2_subdev_video_ops ks0127_video_ops = { + .s_routing = ks0127_s_routing, + .s_stream = ks0127_s_stream, + .querystd = ks0127_querystd, + .g_input_status = ks0127_g_input_status, +}; + +static const struct v4l2_subdev_ops ks0127_ops = { + .core = &ks0127_core_ops, + .video = &ks0127_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct ks0127 *ks; + struct v4l2_subdev *sd; + + v4l_info(client, "%s chip found @ 0x%x (%s)\n", + client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board", + client->addr << 1, client->adapter->name); + + ks = kzalloc(sizeof(*ks), GFP_KERNEL); + if (ks == NULL) + return -ENOMEM; + sd = &ks->sd; + v4l2_i2c_subdev_init(sd, client, &ks0127_ops); + + /* power up */ + init_reg_defaults(); + ks0127_write(sd, KS_CMDA, 0x2c); + mdelay(10); + + /* reset the device */ + ks0127_init(sd); + return 0; +} + +static int ks0127_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */ + ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */ + kfree(to_ks0127(sd)); + return 0; +} + +static const struct i2c_device_id ks0127_id[] = { + { "ks0127", 0 }, + { "ks0127b", 0 }, + { "ks0122s", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ks0127_id); + +static struct i2c_driver ks0127_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ks0127", + }, + .probe = ks0127_probe, + .remove = ks0127_remove, + .id_table = ks0127_id, +}; + +module_i2c_driver(ks0127_driver); diff --git a/drivers/media/i2c/ks0127.h b/drivers/media/i2c/ks0127.h new file mode 100644 index 000000000000..cb8abd5403b3 --- /dev/null +++ b/drivers/media/i2c/ks0127.h @@ -0,0 +1,51 @@ +/* + * Video Capture Driver ( Video for Linux 1/2 ) + * for the Matrox Marvel G200,G400 and Rainbow Runner-G series + * + * This module is an interface to the KS0127 video decoder chip. + * + * Copyright (C) 1999 Ryan Drake + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KS0127_H +#define KS0127_H + +/* input channels */ +#define KS_INPUT_COMPOSITE_1 0 +#define KS_INPUT_COMPOSITE_2 1 +#define KS_INPUT_COMPOSITE_3 2 +#define KS_INPUT_COMPOSITE_4 4 +#define KS_INPUT_COMPOSITE_5 5 +#define KS_INPUT_COMPOSITE_6 6 + +#define KS_INPUT_SVIDEO_1 8 +#define KS_INPUT_SVIDEO_2 9 +#define KS_INPUT_SVIDEO_3 10 + +#define KS_INPUT_YUV656 15 +#define KS_INPUT_COUNT 10 + +/* output channels */ +#define KS_OUTPUT_YUV656E 0 +#define KS_OUTPUT_EXV 1 + +/* video standards */ +#define KS_STD_NTSC_N 112 /* 50 Hz NTSC */ +#define KS_STD_PAL_M 113 /* 60 Hz PAL */ + +#endif /* KS0127_H */ + diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c new file mode 100644 index 000000000000..0991576f4c82 --- /dev/null +++ b/drivers/media/i2c/m52790.c @@ -0,0 +1,216 @@ +/* + * m52790 i2c ivtv driver. + * Copyright (C) 2007 Hans Verkuil + * + * A/V source switching Mitsubishi M52790SP/FP + * + * This program is free software; 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 +#include + +MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + + +struct m52790_state { + struct v4l2_subdev sd; + u16 input; + u16 output; +}; + +static inline struct m52790_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct m52790_state, sd); +} + +/* ----------------------------------------------------------------------- */ + +static int m52790_write(struct v4l2_subdev *sd) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + u8 sw1 = (state->input | state->output) & 0xff; + u8 sw2 = (state->input | state->output) >> 8; + + return i2c_smbus_write_byte_data(client, sw1, sw2); +} + +/* Note: audio and video are linked and cannot be switched separately. + So audio and video routing commands are identical for this chip. + In theory the video amplifier and audio modes could be handled + separately for the output, but that seems to be overkill right now. + The same holds for implementing an audio mute control, this is now + part of the audio output routing. The normal case is that another + chip takes care of the actual muting so making it part of the + output routing seems to be the right thing to do for now. */ +static int m52790_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct m52790_state *state = to_state(sd); + + state->input = input; + state->output = output; + m52790_write(sd); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + reg->size = 1; + reg->val = state->input | state->output; + return 0; +} + +static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + state->input = reg->val & 0x0303; + state->output = reg->val & ~0x0303; + m52790_write(sd); + return 0; +} +#endif + +static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); +} + +static int m52790_log_status(struct v4l2_subdev *sd) +{ + struct m52790_state *state = to_state(sd); + + v4l2_info(sd, "Switch 1: %02x\n", + (state->input | state->output) & 0xff); + v4l2_info(sd, "Switch 2: %02x\n", + (state->input | state->output) >> 8); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops m52790_core_ops = { + .log_status = m52790_log_status, + .g_chip_ident = m52790_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = m52790_g_register, + .s_register = m52790_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops m52790_audio_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_video_ops m52790_video_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_ops m52790_ops = { + .core = &m52790_core_ops, + .audio = &m52790_audio_ops, + .video = &m52790_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +static int m52790_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct m52790_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &m52790_ops); + state->input = M52790_IN_TUNER; + state->output = M52790_OUT_STEREO; + m52790_write(sd); + return 0; +} + +static int m52790_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id m52790_id[] = { + { "m52790", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, m52790_id); + +static struct i2c_driver m52790_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "m52790", + }, + .probe = m52790_probe, + .remove = m52790_remove, + .id_table = m52790_id, +}; + +module_i2c_driver(m52790_driver); diff --git a/drivers/media/i2c/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig new file mode 100644 index 000000000000..dc8c2505907e --- /dev/null +++ b/drivers/media/i2c/m5mols/Kconfig @@ -0,0 +1,6 @@ +config VIDEO_M5MOLS + tristate "Fujitsu M-5MOLS 8MP sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/i2c/m5mols/Makefile b/drivers/media/i2c/m5mols/Makefile new file mode 100644 index 000000000000..0a44e028edc7 --- /dev/null +++ b/drivers/media/i2c/m5mols/Makefile @@ -0,0 +1,3 @@ +m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o + +obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o diff --git a/drivers/media/i2c/m5mols/m5mols.h b/drivers/media/i2c/m5mols/m5mols.h new file mode 100644 index 000000000000..bb589917b65b --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols.h @@ -0,0 +1,334 @@ +/* + * Header for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * This program is free software; 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. + */ + +#ifndef M5MOLS_H +#define M5MOLS_H + +#include +#include "m5mols_reg.h" + +extern int m5mols_debug; + +enum m5mols_restype { + M5MOLS_RESTYPE_MONITOR, + M5MOLS_RESTYPE_CAPTURE, + M5MOLS_RESTYPE_MAX, +}; + +/** + * struct m5mols_resolution - structure for the resolution + * @type: resolution type according to the pixel code + * @width: width of the resolution + * @height: height of the resolution + * @reg: resolution preset register value + */ +struct m5mols_resolution { + u8 reg; + enum m5mols_restype type; + u16 width; + u16 height; +}; + +/** + * struct m5mols_exif - structure for the EXIF information of M-5MOLS + * @exposure_time: exposure time register value + * @shutter_speed: speed of the shutter register value + * @aperture: aperture register value + * @exposure_bias: it calls also EV bias + * @iso_speed: ISO register value + * @flash: status register value of the flash + * @sdr: status register value of the Subject Distance Range + * @qval: not written exact meaning in document + */ +struct m5mols_exif { + u32 exposure_time; + u32 shutter_speed; + u32 aperture; + u32 brightness; + u32 exposure_bias; + u16 iso_speed; + u16 flash; + u16 sdr; + u16 qval; +}; + +/** + * struct m5mols_capture - Structure for the capture capability + * @exif: EXIF information + * @main: size in bytes of the main image + * @thumb: size in bytes of the thumb image, if it was accompanied + * @total: total size in bytes of the produced image + */ +struct m5mols_capture { + struct m5mols_exif exif; + u32 main; + u32 thumb; + u32 total; +}; + +/** + * struct m5mols_scenemode - structure for the scenemode capability + * @metering: metering light register value + * @ev_bias: EV bias register value + * @wb_mode: mode which means the WhiteBalance is Auto or Manual + * @wb_preset: whitebalance preset register value in the Manual mode + * @chroma_en: register value whether the Chroma capability is enabled or not + * @chroma_lvl: chroma's level register value + * @edge_en: register value Whether the Edge capability is enabled or not + * @edge_lvl: edge's level register value + * @af_range: Auto Focus's range + * @fd_mode: Face Detection mode + * @mcc: Multi-axis Color Conversion which means emotion color + * @light: status of the Light + * @flash: status of the Flash + * @tone: Tone color which means Contrast + * @iso: ISO register value + * @capt_mode: Mode of the Image Stabilization while the camera capturing + * @wdr: Wide Dynamic Range register value + * + * The each value according to each scenemode is recommended in the documents. + */ +struct m5mols_scenemode { + u8 metering; + u8 ev_bias; + u8 wb_mode; + u8 wb_preset; + u8 chroma_en; + u8 chroma_lvl; + u8 edge_en; + u8 edge_lvl; + u8 af_range; + u8 fd_mode; + u8 mcc; + u8 light; + u8 flash; + u8 tone; + u8 iso; + u8 capt_mode; + u8 wdr; +}; + +/** + * struct m5mols_version - firmware version information + * @customer: customer information + * @project: version of project information according to customer + * @fw: firmware revision + * @hw: hardware revision + * @param: version of the parameter + * @awb: Auto WhiteBalance algorithm version + * @str: information about manufacturer and packaging vendor + * @af: Auto Focus version + * + * The register offset starts the customer version at 0x0, and it ends + * the awb version at 0x09. The customer, project information occupies 1 bytes + * each. And also the fw, hw, param, awb each requires 2 bytes. The str is + * unique string associated with firmware's version. It includes information + * about manufacturer and the vendor of the sensor's packaging. The least + * significant 2 bytes of the string indicate packaging manufacturer. + */ +#define VERSION_STRING_SIZE 22 +struct m5mols_version { + u8 customer; + u8 project; + u16 fw; + u16 hw; + u16 param; + u16 awb; + u8 str[VERSION_STRING_SIZE]; + u8 af; +}; + +/** + * struct m5mols_info - M-5MOLS driver data structure + * @pdata: platform data + * @sd: v4l-subdev instance + * @pad: media pad + * @ffmt: current fmt according to resolution type + * @res_type: current resolution type + * @irq_waitq: waitqueue for the capture + * @irq_done: set to 1 in the interrupt handler + * @handle: control handler + * @auto_exposure: auto/manual exposure control + * @exposure_bias: exposure compensation control + * @exposure: manual exposure control + * @metering: exposure metering control + * @auto_iso: auto/manual ISO sensitivity control + * @iso: manual ISO sensitivity control + * @auto_wb: auto white balance control + * @lock_3a: 3A lock control + * @colorfx: color effect control + * @saturation: saturation control + * @zoom: zoom control + * @wdr: wide dynamic range control + * @stabilization: image stabilization control + * @jpeg_quality: JPEG compression quality control + * @ver: information of the version + * @cap: the capture mode attributes + * @isp_ready: 1 when the ISP controller has completed booting + * @power: current sensor's power status + * @ctrl_sync: 1 when the control handler state is restored in H/W + * @resolution: register value for current resolution + * @mode: register value for current operation mode + * @set_power: optional power callback to the board code + */ +struct m5mols_info { + const struct m5mols_platform_data *pdata; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; + int res_type; + + wait_queue_head_t irq_waitq; + atomic_t irq_done; + + struct v4l2_ctrl_handler handle; + struct { + /* exposure/exposure bias/auto exposure cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure_bias; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *metering; + }; + struct { + /* iso/auto iso cluster */ + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + struct v4l2_ctrl *auto_wb; + + struct v4l2_ctrl *lock_3a; + struct v4l2_ctrl *colorfx; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *zoom; + struct v4l2_ctrl *wdr; + struct v4l2_ctrl *stabilization; + struct v4l2_ctrl *jpeg_quality; + + struct m5mols_version ver; + struct m5mols_capture cap; + + unsigned int isp_ready:1; + unsigned int power:1; + unsigned int ctrl_sync:1; + + u8 resolution; + u8 mode; + + int (*set_power)(struct device *dev, int on); +}; + +#define is_available_af(__info) (__info->ver.af) +#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) +#define is_manufacturer(__info, __manufacturer) \ + (__info->ver.str[0] == __manufacturer[0] && \ + __info->ver.str[1] == __manufacturer[1]) +/* + * I2C operation of the M-5MOLS + * + * The I2C read operation of the M-5MOLS requires 2 messages. The first + * message sends the information about the command, command category, and total + * message size. The second message is used to retrieve the data specifed in + * the first message + * + * 1st message 2nd message + * +-------+---+----------+-----+-------+ +------+------+------+------+ + * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | + * +-------+---+----------+-----+-------+ +------+------+------+------+ + * - size1: message data size(5 in this case) + * - size2: desired buffer size of the 2nd message + * - d[0..3]: according to size2 + * + * The I2C write operation needs just one message. The message includes + * category, command, total size, and desired data. + * + * 1st message + * +-------+---+----------+-----+------+------+------+------+ + * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | + * +-------+---+----------+-----+------+------+------+------+ + * - d[0..3]: according to size1 + */ +int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val); +int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val); +int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); +int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); + +int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, + int timeout); + +/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */ +#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16) +/* ISP state transition timeout, in ms */ +#define M5MOLS_MODE_CHANGE_TIMEOUT 200 +#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250 + +/* + * Mode operation of the M-5MOLS + * + * Changing the mode of the M-5MOLS is needed right executing order. + * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed + * by user. There are various categories associated with each mode. + * + * +============================================================+ + * | mode | category | + * +============================================================+ + * | FLASH | FLASH(only after Stand-by or Power-on) | + * | SYSTEM | SYSTEM(only after sensor arm-booting) | + * | PARAMETER | PARAMETER | + * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | + * | CAPTURE | Single CAPTURE, Preview(recording) | + * +============================================================+ + * + * The available executing order between each modes are as follows: + * PARAMETER <---> MONITOR <---> CAPTURE + */ +int m5mols_set_mode(struct m5mols_info *info, u8 mode); + +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); +int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); +int m5mols_restore_controls(struct m5mols_info *info); +int m5mols_start_capture(struct m5mols_info *info); +int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); +int m5mols_lock_3a(struct m5mols_info *info, bool lock); +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); +int m5mols_init_controls(struct v4l2_subdev *sd); + +/* The firmware function */ +int m5mols_update_fw(struct v4l2_subdev *sd, + int (*set_power)(struct m5mols_info *, bool)); + +static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct m5mols_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + struct m5mols_info *info = container_of(ctrl->handler, + struct m5mols_info, handle); + return &info->sd; +} + +static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, + unsigned int mode) +{ + ctrl->priv = (void *)mode; +} + +static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) +{ + return (unsigned int)ctrl->priv; +} + +#endif /* M5MOLS_H */ diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c new file mode 100644 index 000000000000..cb243bd278ce --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_capture.c @@ -0,0 +1,155 @@ + +/* + * The Capture code for Fujitsu M-5MOLS ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * This program is free software; 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m5mols.h" +#include "m5mols_reg.h" + +/** + * m5mols_read_rational - I2C read of a rational number + * + * Read numerator and denominator from registers @addr_num and @addr_den + * respectively and return the division result in @val. + */ +static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, + u32 addr_den, u32 *val) +{ + u32 num, den; + + int ret = m5mols_read_u32(sd, addr_num, &num); + if (!ret) + ret = m5mols_read_u32(sd, addr_den, &den); + if (ret) + return ret; + *val = den == 0 ? 0 : num / den; + return ret; +} + +/** + * m5mols_capture_info - Gather captured image information + * + * For now it gathers only EXIF information and file size. + */ +static int m5mols_capture_info(struct m5mols_info *info) +{ + struct m5mols_exif *exif = &info->cap.exif; + struct v4l2_subdev *sd = &info->sd; + int ret; + + ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, + EXIF_INFO_EXPTIME_DE, &exif->exposure_time); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, + &exif->shutter_speed); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, + &exif->aperture); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, + &exif->brightness); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, + &exif->exposure_bias); + if (ret) + return ret; + + ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed); + if (!ret) + ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash); + if (!ret) + ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr); + if (!ret) + ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval); + if (ret) + return ret; + + if (!ret) + ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main); + if (!ret) + ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb); + if (!ret) + info->cap.total = info->cap.main + info->cap.thumb; + + return ret; +} + +int m5mols_start_capture(struct m5mols_info *info) +{ + struct v4l2_subdev *sd = &info->sd; + int ret; + + /* + * Synchronize the controls, set the capture frame resolution and color + * format. The frame capture is initiated during switching from Monitor + * to Capture mode. + */ + ret = m5mols_set_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_restore_controls(info); + if (!ret) + ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); + if (!ret) + ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); + if (!ret) + ret = m5mols_set_mode(info, REG_CAPTURE); + if (!ret) + /* Wait until a frame is captured to ISP internal memory */ + ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); + if (ret) + return ret; + + /* + * Initiate the captured data transfer to a MIPI-CSI receiver. + */ + ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); + if (!ret) + ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); + if (!ret) { + bool captured = false; + unsigned int size; + + /* Wait for the capture completion interrupt */ + ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); + if (!ret) { + captured = true; + ret = m5mols_capture_info(info); + } + size = captured ? info->cap.main : 0; + v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", + __func__, size, info->cap.thumb); + + v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); + } + + return ret; +} diff --git a/drivers/media/i2c/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c new file mode 100644 index 000000000000..fdbc205a2969 --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_controls.c @@ -0,0 +1,628 @@ +/* + * Controls for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * This program is free software; 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. + */ + +#include +#include +#include +#include + +#include "m5mols.h" +#include "m5mols_reg.h" + +static struct m5mols_scenemode m5mols_default_scenemode[] = { + [REG_SCENE_NORMAL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, + 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PORTRAIT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 4, + REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_LANDSCAPE] = { + REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 6, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SPORTS] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PARTY_INDOOR] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_BEACH_SNOW] = { + REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SUNSET] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_DAYLIGHT, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_DAWN_DUSK] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_FLUORESCENT_1, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FALL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 5, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_NIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_AGAINST_LIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FIRE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_TEXT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 7, + REG_AF_MACRO, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, + }, + [REG_SCENE_CANDLE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, +}; + +/** + * m5mols_do_scenemode() - Change current scenemode + * @mode: Desired mode of the scenemode + * + * WARNING: The execution order is important. Do not change the order. + */ +int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) +{ + struct v4l2_subdev *sd = &info->sd; + struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; + int ret; + + if (mode > REG_SCENE_CANDLE) + return -EINVAL; + + ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); + if (!ret) + ret = m5mols_write(sd, AE_MODE, scenemode.metering); + if (!ret) + ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); + if (!ret) + ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); + if (!ret) + ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, AF_MODE, scenemode.af_range); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); + if (!ret) + ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); + if (!ret) + ret = m5mols_write(sd, AE_ISO, scenemode.iso); + if (!ret) + ret = m5mols_set_mode(info, REG_CAPTURE); + if (!ret) + ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); + if (!ret) + ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); + if (!ret) + ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); + if (!ret) + ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); + if (!ret) + ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); + if (!ret) + ret = m5mols_set_mode(info, REG_MONITOR); + + return ret; +} + +static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) +{ + bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; + int ret = 0; + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; + + ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? + REG_AE_LOCK : REG_AE_UNLOCK); + if (ret) + return ret; + } + + if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) + && info->auto_wb->val) { + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; + + ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? + REG_AWB_LOCK : REG_AWB_UNLOCK); + if (ret) + return ret; + } + + if (!info->ver.af || !af_lock) + return ret; + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) + ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); + + return ret; +} + +static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) +{ + unsigned int metering; + + switch (mode) { + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + metering = REG_AE_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + metering = REG_AE_SPOT; + break; + default: + metering = REG_AE_ALL; + break; + } + + return m5mols_write(&info->sd, AE_MODE, metering); +} + +static int m5mols_set_exposure(struct m5mols_info *info, int exposure) +{ + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + if (exposure == V4L2_EXPOSURE_AUTO) { + /* Unlock auto exposure */ + info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; + m5mols_3a_lock(info, info->lock_3a); + + ret = m5mols_set_metering_mode(info, info->metering->val); + if (ret < 0) + return ret; + + v4l2_dbg(1, m5mols_debug, sd, + "%s: exposure bias: %#x, metering: %#x\n", + __func__, info->exposure_bias->val, + info->metering->val); + + return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); + } + + if (exposure == V4L2_EXPOSURE_MANUAL) { + ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_MON, + info->exposure->val); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_CAP, + info->exposure->val); + + v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", + __func__, info->exposure->val); + } + + return ret; +} + +static int m5mols_set_white_balance(struct m5mols_info *info, int val) +{ + static const unsigned short wb[][2] = { + { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, + { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, + { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, + { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, + { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, + { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, + { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, + { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, + { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, + }; + int i; + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(wb); i++) { + int awb; + if (wb[i][0] != val) + continue; + + v4l2_dbg(1, m5mols_debug, sd, + "Setting white balance to: %#x\n", wb[i][0]); + + awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; + ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : + REG_AWB_PRESET); + if (ret < 0) + return ret; + + if (!awb) + ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); + } + + return ret; +} + +static int m5mols_set_saturation(struct m5mols_info *info, int val) +{ + int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); +} + +static int m5mols_set_color_effect(struct m5mols_info *info, int val) +{ + unsigned int m_effect = REG_COLOR_EFFECT_OFF; + unsigned int p_effect = REG_EFFECT_OFF; + unsigned int cfix_r = 0, cfix_b = 0; + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + switch (val) { + case V4L2_COLORFX_BW: + m_effect = REG_COLOR_EFFECT_ON; + break; + case V4L2_COLORFX_NEGATIVE: + p_effect = REG_EFFECT_NEGA; + break; + case V4L2_COLORFX_EMBOSS: + p_effect = REG_EFFECT_EMBOSS; + break; + case V4L2_COLORFX_SEPIA: + m_effect = REG_COLOR_EFFECT_ON; + cfix_r = REG_CFIXR_SEPIA; + cfix_b = REG_CFIXB_SEPIA; + break; + } + + ret = m5mols_write(sd, PARM_EFFECT, p_effect); + if (!ret) + ret = m5mols_write(sd, MON_EFFECT, m_effect); + + if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { + ret = m5mols_write(sd, MON_CFIXR, cfix_r); + if (!ret) + ret = m5mols_write(sd, MON_CFIXB, cfix_b); + } + + v4l2_dbg(1, m5mols_debug, sd, + "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", + p_effect, m_effect, cfix_r, cfix_b, ret); + + return ret; +} + +static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) +{ + u32 iso = auto_iso ? 0 : info->iso->val + 1; + + return m5mols_write(&info->sd, AE_ISO, iso); +} + +static int m5mols_set_wdr(struct m5mols_info *info, int wdr) +{ + int ret; + + ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); + if (ret < 0) + return ret; + + ret = m5mols_set_mode(info, REG_CAPTURE); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); +} + +static int m5mols_set_stabilization(struct m5mols_info *info, int val) +{ + struct v4l2_subdev *sd = &info->sd; + unsigned int evp = val ? 0xe : 0x0; + int ret; + + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); + if (ret < 0) + return ret; + + return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); +} + +static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int ret = 0; + u8 status; + + v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", + __func__, ctrl->name, info->isp_ready); + + if (!info->isp_ready) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_ISO_SENSITIVITY_AUTO: + ret = m5mols_read_u8(sd, AE_ISO, &status); + if (ret == 0) + ctrl->val = !status; + if (status != REG_ISO_AUTO) + info->iso->val = status - 1; + break; + + case V4L2_CID_3A_LOCK: + ctrl->val &= ~0x7; + + ret = m5mols_read_u8(sd, AE_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AWB_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AF_EXECUTE, &status); + if (!status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + break; + } + + return ret; +} + +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) +{ + unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int last_mode = info->mode; + int ret = 0; + + /* + * If needed, defer restoring the controls until + * the device is fully initialized. + */ + if (!info->isp_ready) { + info->ctrl_sync = 0; + return 0; + } + + v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n", + __func__, ctrl->name, ctrl->val, (int)ctrl->priv); + + if (ctrl_mode && ctrl_mode != info->mode) { + ret = m5mols_set_mode(info, ctrl_mode); + if (ret < 0) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_3A_LOCK: + ret = m5mols_3a_lock(info, ctrl); + break; + + case V4L2_CID_ZOOM_ABSOLUTE: + ret = m5mols_write(sd, MON_ZOOM, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + ret = m5mols_set_exposure(info, ctrl->val); + break; + + case V4L2_CID_ISO_SENSITIVITY: + ret = m5mols_set_iso(info, ctrl->val); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = m5mols_set_white_balance(info, ctrl->val); + break; + + case V4L2_CID_SATURATION: + ret = m5mols_set_saturation(info, ctrl->val); + break; + + case V4L2_CID_COLORFX: + ret = m5mols_set_color_effect(info, ctrl->val); + break; + + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = m5mols_set_wdr(info, ctrl->val); + break; + + case V4L2_CID_IMAGE_STABILIZATION: + ret = m5mols_set_stabilization(info, ctrl->val); + break; + + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); + break; + } + + if (ret == 0 && info->mode != last_mode) + ret = m5mols_set_mode(info, last_mode); + + return ret; +} + +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { + .g_volatile_ctrl = m5mols_g_volatile_ctrl, + .s_ctrl = m5mols_s_ctrl, +}; + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ + 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 +}; + +/* Supported Exposure Bias values, -2.0EV...+2.0EV */ +static const s64 ev_bias_qmenu[] = { + /* AE_INDEX: 0x00...0x08 */ + -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 +}; + +int m5mols_init_controls(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + u16 exposure_max; + u16 zoom_step; + int ret; + + /* Determine the firmware dependant control range and step values */ + ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); + if (ret < 0) + return ret; + + zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; + v4l2_ctrl_handler_init(&info->handle, 20); + + info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); + + /* Exposure control cluster */ + info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, + 1, ~0x03, V4L2_EXPOSURE_AUTO); + + info->exposure = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, + 0, exposure_max, 1, exposure_max / 2); + + info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(ev_bias_qmenu) - 1, + ARRAY_SIZE(ev_bias_qmenu)/2 - 1, + ev_bias_qmenu); + + info->metering = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, + 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); + + /* ISO control cluster */ + info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); + + info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_SATURATION, 1, 5, 1, 3); + + info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); + + info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); + + info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + + info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + + info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + + info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); + + if (info->handle.error) { + int ret = info->handle.error; + v4l2_err(sd, "Failed to initialize controls: %d\n", ret); + v4l2_ctrl_handler_free(&info->handle); + return ret; + } + + v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); + info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); + + info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; + + m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); + m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); + m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); + + sd->ctrl_handler = &info->handle; + + return 0; +} diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c new file mode 100644 index 000000000000..ac7d28b6ddf2 --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -0,0 +1,990 @@ +/* + * Driver for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * This program is free software; 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m5mols.h" +#include "m5mols_reg.h" + +int m5mols_debug; +module_param(m5mols_debug, int, 0644); + +#define MODULE_NAME "M5MOLS" +#define M5MOLS_I2C_CHECK_RETRY 500 + +/* The regulator consumer names for external voltage regulators */ +static struct regulator_bulk_data supplies[] = { + { + .supply = "core", /* ARM core power, 1.2V */ + }, { + .supply = "dig_18", /* digital power 1, 1.8V */ + }, { + .supply = "d_sensor", /* sensor power 1, 1.8V */ + }, { + .supply = "dig_28", /* digital power 2, 2.8V */ + }, { + .supply = "a_sensor", /* analog power */ + }, { + .supply = "dig_12", /* digital power 3, 1.2V */ + }, +}; + +static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { + [M5MOLS_RESTYPE_MONITOR] = { + .width = 1920, + .height = 1080, + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + [M5MOLS_RESTYPE_CAPTURE] = { + .width = 1920, + .height = 1080, + .code = V4L2_MBUS_FMT_JPEG_1X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; +#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) + +static const struct m5mols_resolution m5mols_reg_res[] = { + { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ + { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ + { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ + { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, + { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ + { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ + { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ + { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ + { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ + { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, + { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ + { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ + { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, + { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ + { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ + { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ + { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ + { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ + { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ + + { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ + { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ + { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, + { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ + { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ + { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ + { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ + { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ + { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ + { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ + { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ + { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, + { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ + { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, + { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ + { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ + { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, + { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ +}; + +/** + * m5mols_swap_byte - an byte array to integer conversion function + * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet + * + * Convert I2C data byte array with performing any required byte + * reordering to assure proper values for each data type, regardless + * of the architecture endianness. + */ +static u32 m5mols_swap_byte(u8 *data, u8 length) +{ + if (length == 1) + return *data; + else if (length == 2) + return be16_to_cpu(*((u16 *)data)); + else + return be32_to_cpu(*((u32 *)data)); +} + +/** + * m5mols_read - I2C read function + * @reg: combination of size, category and command for the I2C packet + * @size: desired size of I2C packet + * @val: read value + * + * Returns 0 on success, or else negative errno. + */ +static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct m5mols_info *info = to_m5mols(sd); + u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; + u8 category = I2C_CATEGORY(reg); + u8 cmd = I2C_COMMAND(reg); + struct i2c_msg msg[2]; + u8 wbuf[5]; + int ret; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 5; + msg[0].buf = wbuf; + wbuf[0] = 5; + wbuf[1] = M5MOLS_BYTE_READ; + wbuf[2] = category; + wbuf[3] = cmd; + wbuf[4] = size; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = size + 1; + msg[1].buf = rbuf; + + /* minimum stabilization time */ + usleep_range(200, 200); + + ret = i2c_transfer(client->adapter, msg, 2); + + if (ret == 2) { + *val = m5mols_swap_byte(&rbuf[1], size); + return 0; + } + + if (info->isp_ready) + v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", + size, category, cmd, ret); + + return ret < 0 ? ret : -EIO; +} + +int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) +{ + u32 val_32; + int ret; + + if (I2C_SIZE(reg) != 1) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); + if (ret) + return ret; + + *val = (u8)val_32; + return ret; +} + +int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) +{ + u32 val_32; + int ret; + + if (I2C_SIZE(reg) != 2) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); + if (ret) + return ret; + + *val = (u16)val_32; + return ret; +} + +int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) +{ + if (I2C_SIZE(reg) != 4) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + return m5mols_read(sd, I2C_SIZE(reg), reg, val); +} + +/** + * m5mols_write - I2C command write function + * @reg: combination of size, category and command for the I2C packet + * @val: value to write + * + * Returns 0 on success, or else negative errno. + */ +int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct m5mols_info *info = to_m5mols(sd); + u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; + u8 category = I2C_CATEGORY(reg); + u8 cmd = I2C_COMMAND(reg); + u8 size = I2C_SIZE(reg); + u32 *buf = (u32 *)&wbuf[4]; + struct i2c_msg msg[1]; + int ret; + + if (!client->adapter) + return -ENODEV; + + if (size != 1 && size != 2 && size != 4) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + msg->addr = client->addr; + msg->flags = 0; + msg->len = (u16)size + 4; + msg->buf = wbuf; + wbuf[0] = size + 4; + wbuf[1] = M5MOLS_BYTE_WRITE; + wbuf[2] = category; + wbuf[3] = cmd; + + *buf = m5mols_swap_byte((u8 *)&val, size); + + usleep_range(200, 200); + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret == 1) + return 0; + + if (info->isp_ready) + v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", + category, cmd, ret); + + return ret < 0 ? ret : -EIO; +} + +/** + * m5mols_busy_wait - Busy waiting with I2C register polling + * @reg: the I2C_REG() address of an 8-bit status register to check + * @value: expected status register value + * @mask: bit mask for the read status register value + * @timeout: timeout in miliseconds, or -1 for default timeout + * + * The @reg register value is ORed with @mask before comparing with @value. + * + * Return: 0 if the requested condition became true within less than + * @timeout ms, or else negative errno. + */ +int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, + int timeout) +{ + int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; + unsigned long end = jiffies + msecs_to_jiffies(ms); + u8 status; + + do { + int ret = m5mols_read_u8(sd, reg, &status); + + if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) + return ret; + if (!ret && (status & mask & 0xff) == (value & 0xff)) + return 0; + usleep_range(100, 250); + } while (ms > 0 && time_is_after_jiffies(end)); + + return -EBUSY; +} + +/** + * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts + * + * Before writing desired interrupt value the INT_FACTOR register should + * be read to clear pending interrupts. + */ +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) +{ + struct m5mols_info *info = to_m5mols(sd); + u8 mask = is_available_af(info) ? REG_INT_AF : 0; + u8 dummy; + int ret; + + ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); + if (!ret) + ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); + return ret; +} + +int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) +{ + struct m5mols_info *info = to_m5mols(sd); + + int ret = wait_event_interruptible_timeout(info->irq_waitq, + atomic_add_unless(&info->irq_done, -1, 0), + msecs_to_jiffies(timeout)); + if (ret <= 0) + return ret ? ret : -ETIMEDOUT; + + return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, + M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); +} + +/** + * m5mols_reg_mode - Write the mode and check busy status + * + * It always accompanies a little delay changing the M-5MOLS mode, so it is + * needed checking current busy status to guarantee right mode. + */ +static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) +{ + int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); + if (ret < 0) + return ret; + return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, + M5MOLS_MODE_CHANGE_TIMEOUT); +} + +/** + * m5mols_set_mode - set the M-5MOLS controller mode + * @mode: the required operation mode + * + * The commands of M-5MOLS are grouped into specific modes. Each functionality + * can be guaranteed only when the sensor is operating in mode which a command + * belongs to. + */ +int m5mols_set_mode(struct m5mols_info *info, u8 mode) +{ + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; + u8 reg; + + if (mode < REG_PARAMETER || mode > REG_CAPTURE) + return ret; + + ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); + if (ret || reg == mode) + return ret; + + switch (reg) { + case REG_PARAMETER: + ret = m5mols_reg_mode(sd, REG_MONITOR); + if (mode == REG_MONITOR) + break; + if (!ret) + ret = m5mols_reg_mode(sd, REG_CAPTURE); + break; + + case REG_MONITOR: + if (mode == REG_PARAMETER) { + ret = m5mols_reg_mode(sd, REG_PARAMETER); + break; + } + + ret = m5mols_reg_mode(sd, REG_CAPTURE); + break; + + case REG_CAPTURE: + ret = m5mols_reg_mode(sd, REG_MONITOR); + if (mode == REG_MONITOR) + break; + if (!ret) + ret = m5mols_reg_mode(sd, REG_PARAMETER); + break; + + default: + v4l2_warn(sd, "Wrong mode: %d\n", mode); + } + + if (!ret) + info->mode = mode; + + return ret; +} + +/** + * m5mols_get_version - retrieve full revisions information of M-5MOLS + * + * The version information includes revisions of hardware and firmware, + * AutoFocus alghorithm version and the version string. + */ +static int m5mols_get_version(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + struct m5mols_version *ver = &info->ver; + u8 *str = ver->str; + int i; + int ret; + + ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); + if (!ret) + ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); + if (!ret) + ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); + if (!ret) + ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); + if (ret) + return ret; + + for (i = 0; i < VERSION_STRING_SIZE; i++) { + ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); + if (ret) + return ret; + } + + ver->fw = be16_to_cpu(ver->fw); + ver->hw = be16_to_cpu(ver->hw); + ver->param = be16_to_cpu(ver->param); + ver->awb = be16_to_cpu(ver->awb); + + v4l2_info(sd, "Manufacturer\t[%s]\n", + is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? + "Samsung Electro-Machanics" : + is_manufacturer(info, REG_SAMSUNG_OPTICS) ? + "Samsung Fiber-Optics" : + is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? + "Samsung Techwin" : "None"); + v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", + info->ver.customer, info->ver.project); + + if (!is_available_af(info)) + v4l2_info(sd, "No support Auto Focus on this firmware\n"); + + return ret; +} + +/** + * __find_restype - Lookup M-5MOLS resolution type according to pixel code + * @code: pixel code + */ +static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) +{ + enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; + + do { + if (code == m5mols_default_ffmt[type].code) + return type; + } while (type++ != SIZE_DEFAULT_FFMT); + + return 0; +} + +/** + * __find_resolution - Lookup preset and type of M-5MOLS's resolution + * @mf: pixel format to find/negotiate the resolution preset for + * @type: M-5MOLS resolution type + * @resolution: M-5MOLS resolution preset register value + * + * Find nearest resolution matching resolution preset and adjust mf + * to supported values. + */ +static int __find_resolution(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf, + enum m5mols_restype *type, + u32 *resolution) +{ + const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; + const struct m5mols_resolution *match = NULL; + enum m5mols_restype stype = __find_restype(mf->code); + int i = ARRAY_SIZE(m5mols_reg_res); + unsigned int min_err = ~0; + + while (i--) { + int err; + if (stype == fsize->type) { + err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + + if (err < min_err) { + min_err = err; + match = fsize; + } + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + *resolution = match->reg; + *type = stype; + return 0; + } + + return -EINVAL; +} + +static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, + struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which, + enum m5mols_restype type) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; + + return &info->ffmt[type]; +} + +static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct m5mols_info *info = to_m5mols(sd); + struct v4l2_mbus_framefmt *format; + + format = __find_format(info, fh, fmt->which, info->res_type); + if (!format) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct m5mols_info *info = to_m5mols(sd); + struct v4l2_mbus_framefmt *format = &fmt->format; + struct v4l2_mbus_framefmt *sfmt; + enum m5mols_restype type; + u32 resolution = 0; + int ret; + + ret = __find_resolution(sd, format, &type, &resolution); + if (ret < 0) + return ret; + + sfmt = __find_format(info, fh, fmt->which, type); + if (!sfmt) + return 0; + + + format->code = m5mols_default_ffmt[type].code; + format->colorspace = V4L2_COLORSPACE_JPEG; + format->field = V4L2_FIELD_NONE; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + *sfmt = *format; + info->resolution = resolution; + info->res_type = type; + } + + return 0; +} + +static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (!code || code->index >= SIZE_DEFAULT_FFMT) + return -EINVAL; + + code->code = m5mols_default_ffmt[code->index].code; + + return 0; +} + +static struct v4l2_subdev_pad_ops m5mols_pad_ops = { + .enum_mbus_code = m5mols_enum_mbus_code, + .get_fmt = m5mols_get_fmt, + .set_fmt = m5mols_set_fmt, +}; + +/** + * m5mols_restore_controls - Apply current control values to the registers + * + * m5mols_do_scenemode() handles all parameters for which there is yet no + * individual control. It should be replaced at some point by setting each + * control individually, in required register set up order. + */ +int m5mols_restore_controls(struct m5mols_info *info) +{ + int ret; + + if (info->ctrl_sync) + return 0; + + ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); + if (ret) + return ret; + + ret = v4l2_ctrl_handler_setup(&info->handle); + info->ctrl_sync = !ret; + + return ret; +} + +/** + * m5mols_start_monitor - Start the monitor mode + * + * Before applying the controls setup the resolution and frame rate + * in PARAMETER mode, and then switch over to MONITOR mode. + */ +static int m5mols_start_monitor(struct m5mols_info *info) +{ + struct v4l2_subdev *sd = &info->sd; + int ret; + + ret = m5mols_set_mode(info, REG_PARAMETER); + if (!ret) + ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); + if (!ret) + ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); + if (!ret) + ret = m5mols_set_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_restore_controls(info); + + return ret; +} + +static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct m5mols_info *info = to_m5mols(sd); + u32 code = info->ffmt[info->res_type].code; + + if (enable) { + int ret = -EINVAL; + + if (is_code(code, M5MOLS_RESTYPE_MONITOR)) + ret = m5mols_start_monitor(info); + if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) + ret = m5mols_start_capture(info); + + return ret; + } + + return m5mols_set_mode(info, REG_PARAMETER); +} + +static const struct v4l2_subdev_video_ops m5mols_video_ops = { + .s_stream = m5mols_s_stream, +}; + +static int m5mols_sensor_power(struct m5mols_info *info, bool enable) +{ + struct v4l2_subdev *sd = &info->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + const struct m5mols_platform_data *pdata = info->pdata; + int ret; + + if (info->power == enable) + return 0; + + if (enable) { + if (info->set_power) { + ret = info->set_power(&client->dev, 1); + if (ret) + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); + if (ret) { + info->set_power(&client->dev, 0); + return ret; + } + + gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); + info->power = 1; + + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); + if (ret) + return ret; + + if (info->set_power) + info->set_power(&client->dev, 0); + + gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); + + info->isp_ready = 0; + info->power = 0; + + return ret; +} + +/* m5mols_update_fw - optional firmware update routine */ +int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, + int (*set_power)(struct m5mols_info *, bool)) +{ + return 0; +} + +/** + * m5mols_fw_start - M-5MOLS internal ARM controller initialization + * + * Execute the M-5MOLS internal ARM controller initialization sequence. + * This function should be called after the supply voltage has been + * applied and before any requests to the device are made. + */ +static int m5mols_fw_start(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + int ret; + + atomic_set(&info->irq_done, 0); + /* Wait until I2C slave is initialized in Flash Writer mode */ + ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, + M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); + if (!ret) + ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); + if (!ret) + ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); + if (ret < 0) + return ret; + + info->isp_ready = 1; + + ret = m5mols_get_version(sd); + if (!ret) + ret = m5mols_update_fw(sd, m5mols_sensor_power); + if (ret) + return ret; + + v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); + + ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); + if (!ret) + ret = m5mols_enable_interrupt(sd, + REG_INT_AF | REG_INT_CAPTURE); + + return ret; +} + +/** + * m5mols_s_power - Main sensor power control function + * + * To prevent breaking the lens when the sensor is powered off the Soft-Landing + * algorithm is called where available. The Soft-Landing algorithm availability + * dependends on the firmware provider. + */ +static int m5mols_s_power(struct v4l2_subdev *sd, int on) +{ + struct m5mols_info *info = to_m5mols(sd); + int ret; + + if (on) { + ret = m5mols_sensor_power(info, true); + if (!ret) + ret = m5mols_fw_start(sd); + return ret; + } + + if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { + ret = m5mols_set_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); + if (!ret) + ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); + if (!ret) + ret = m5mols_busy_wait(sd, SYSTEM_STATUS, REG_AF_IDLE, + 0xff, -1); + if (ret < 0) + v4l2_warn(sd, "Soft landing lens failed\n"); + } + + ret = m5mols_sensor_power(info, false); + info->ctrl_sync = 0; + + return ret; +} + +static int m5mols_log_status(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + + v4l2_ctrl_handler_log_status(&info->handle, sd->name); + + return 0; +} + +static const struct v4l2_subdev_core_ops m5mols_core_ops = { + .s_power = m5mols_s_power, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .log_status = m5mols_log_status, +}; + +/* + * V4L2 subdev internal operations + */ +static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + + *format = m5mols_default_ffmt[0]; + return 0; +} + +static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = { + .open = m5mols_open, +}; + +static const struct v4l2_subdev_ops m5mols_ops = { + .core = &m5mols_core_ops, + .pad = &m5mols_pad_ops, + .video = &m5mols_video_ops, +}; + +static irqreturn_t m5mols_irq_handler(int irq, void *data) +{ + struct m5mols_info *info = to_m5mols(data); + + atomic_set(&info->irq_done, 1); + wake_up_interruptible(&info->irq_waitq); + + return IRQ_HANDLED; +} + +static int __devinit m5mols_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct m5mols_platform_data *pdata = client->dev.platform_data; + struct m5mols_info *info; + struct v4l2_subdev *sd; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!gpio_is_valid(pdata->gpio_reset)) { + dev_err(&client->dev, "No valid RESET GPIO specified\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, "Interrupt not assigned\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdata = pdata; + info->set_power = pdata->set_power; + + ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); + if (ret) { + dev_err(&client->dev, "Failed to request gpio: %d\n", ret); + goto out_free; + } + gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); + + ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); + if (ret) { + dev_err(&client->dev, "Failed to get regulators: %d\n", ret); + goto out_gpio; + } + + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &m5mols_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + sd->internal_ops = &m5mols_subdev_internal_ops; + info->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + if (ret < 0) + goto out_reg; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + init_waitqueue_head(&info->irq_waitq); + ret = request_irq(client->irq, m5mols_irq_handler, + IRQF_TRIGGER_RISING, MODULE_NAME, sd); + if (ret) { + dev_err(&client->dev, "Interrupt request failed: %d\n", ret); + goto out_me; + } + info->res_type = M5MOLS_RESTYPE_MONITOR; + info->ffmt[0] = m5mols_default_ffmt[0]; + info->ffmt[1] = m5mols_default_ffmt[1]; + + ret = m5mols_sensor_power(info, true); + if (ret) + goto out_me; + + ret = m5mols_fw_start(sd); + if (!ret) + ret = m5mols_init_controls(sd); + + m5mols_sensor_power(info, false); + if (!ret) + return 0; +out_me: + media_entity_cleanup(&sd->entity); +out_reg: + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); +out_gpio: + gpio_free(pdata->gpio_reset); +out_free: + kfree(info); + return ret; +} + +static int __devexit m5mols_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct m5mols_info *info = to_m5mols(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + free_irq(client->irq, sd); + + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + gpio_free(info->pdata->gpio_reset); + media_entity_cleanup(&sd->entity); + kfree(info); + return 0; +} + +static const struct i2c_device_id m5mols_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, m5mols_id); + +static struct i2c_driver m5mols_i2c_driver = { + .driver = { + .name = MODULE_NAME, + }, + .probe = m5mols_probe, + .remove = __devexit_p(m5mols_remove), + .id_table = m5mols_id, +}; + +module_i2c_driver(m5mols_i2c_driver); + +MODULE_AUTHOR("HeungJun Kim "); +MODULE_AUTHOR("Dongsoo Kim "); +MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/m5mols/m5mols_reg.h b/drivers/media/i2c/m5mols/m5mols_reg.h new file mode 100644 index 000000000000..14d4be72aeff --- /dev/null +++ b/drivers/media/i2c/m5mols/m5mols_reg.h @@ -0,0 +1,362 @@ +/* + * Register map for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim + * + * This program is free software; 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. + */ + +#ifndef M5MOLS_REG_H +#define M5MOLS_REG_H + +#define M5MOLS_I2C_MAX_SIZE 4 +#define M5MOLS_BYTE_READ 0x01 +#define M5MOLS_BYTE_WRITE 0x02 + +#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) +#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) +#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) +#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) + +/* + * Category section register + * + * The category means set including relevant command of M-5MOLS. + */ +#define CAT_SYSTEM 0x00 +#define CAT_PARAM 0x01 +#define CAT_MONITOR 0x02 +#define CAT_AE 0x03 +#define CAT_WB 0x06 +#define CAT_EXIF 0x07 +#define CAT_FD 0x09 +#define CAT_LENS 0x0a +#define CAT_CAPT_PARM 0x0b +#define CAT_CAPT_CTRL 0x0c +#define CAT_FLASH 0x0f /* related to FW, revisions, booting */ + +/* + * Category 0 - SYSTEM mode + * + * The SYSTEM mode in the M-5MOLS means area available to handle with the whole + * & all-round system of sensor. It deals with version/interrupt/setting mode & + * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by + * packaging & manufacturer, even the customer and project code. And the + * function details may vary among them. The version information helps to + * determine what methods shall be used in the driver. + * + * There is many registers between customer version address and awb one. For + * more specific contents, see definition if file m5mols.h. + */ +#define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1) +#define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1) +#define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2) +#define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2) +#define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2) +#define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2) + +#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1) +#define REG_SYSINIT 0x00 /* SYSTEM mode */ +#define REG_PARAMETER 0x01 /* PARAMETER mode */ +#define REG_MONITOR 0x02 /* MONITOR mode */ +#define REG_CAPTURE 0x03 /* CAPTURE mode */ + +#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) +#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1) +#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ +#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ +#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ +/* SYSTEM mode status */ +#define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1) + +/* Interrupt pending register */ +#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1) +/* interrupt enable register */ +#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1) +#define REG_INT_MODE (1 << 0) +#define REG_INT_AF (1 << 1) +#define REG_INT_ZOOM (1 << 2) +#define REG_INT_CAPTURE (1 << 3) +#define REG_INT_FRAMESYNC (1 << 4) +#define REG_INT_FD (1 << 5) +#define REG_INT_LENS_INIT (1 << 6) +#define REG_INT_SOUND (1 << 7) +#define REG_INT_MASK 0x0f + +/* + * category 1 - PARAMETER mode + * + * This category supports function of camera features of M-5MOLS. It means we + * can handle with preview(MONITOR) resolution size/frame per second/interface + * between the sensor and the Application Processor/even the image effect. + */ + +/* Resolution in the MONITOR mode */ +#define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1) + +/* Frame rate */ +#define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1) +#define REG_FPS_30 0x02 + +/* Video bus between the sensor and a host processor */ +#define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1) +#define REG_INTERFACE_MIPI 0x02 + +/* Image effects */ +#define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1) +#define REG_EFFECT_OFF 0x00 +#define REG_EFFECT_NEGA 0x01 +#define REG_EFFECT_EMBOSS 0x06 +#define REG_EFFECT_OUTLINE 0x07 +#define REG_EFFECT_WATERCOLOR 0x08 + +/* + * Category 2 - MONITOR mode + * + * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another + * mode named "Preview", but this preview mode is used at the case specific + * vider-recording mode. This mmode supports only YUYV format. On the other + * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are + * another options like zoom/color effect(different with effect in PARAMETER + * mode)/anti hand shaking algorithm. + */ + +/* Target digital zoom position */ +#define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1) + +/* CR value for color effect */ +#define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1) +/* CB value for color effect */ +#define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1) +#define REG_CFIXB_SEPIA 0xd8 +#define REG_CFIXR_SEPIA 0x18 + +#define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1) +#define REG_COLOR_EFFECT_OFF 0x00 +#define REG_COLOR_EFFECT_ON 0x01 + +/* Chroma enable */ +#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1) +/* Chroma level */ +#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1) +#define REG_CHROMA_OFF 0x00 +#define REG_CHROMA_ON 0x01 + +/* Sharpness on/off */ +#define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1) +/* Sharpness level */ +#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1) +#define REG_EDGE_OFF 0x00 +#define REG_EDGE_ON 0x01 + +/* Set color tone (contrast) */ +#define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1) + +/* + * Category 3 - Auto Exposure + * + * The M-5MOLS exposure capbility is detailed as which is similar to digital + * camera. This category supports AE locking/various AE mode(range of exposure) + * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the + * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be + * different. So, this category also provide getting the max/min values. And, + * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. + */ + +/* Auto Exposure locking */ +#define AE_LOCK I2C_REG(CAT_AE, 0x00, 1) +#define REG_AE_UNLOCK 0x00 +#define REG_AE_LOCK 0x01 + +/* Auto Exposure algorithm mode */ +#define AE_MODE I2C_REG(CAT_AE, 0x01, 1) +#define REG_AE_OFF 0x00 /* AE off */ +#define REG_AE_ALL 0x01 /* calc AE in all block integral */ +#define REG_AE_CENTER 0x03 /* calc AE in center weighted */ +#define REG_AE_SPOT 0x06 /* calc AE in specific spot */ + +#define AE_ISO I2C_REG(CAT_AE, 0x05, 1) +#define REG_ISO_AUTO 0x00 +#define REG_ISO_50 0x01 +#define REG_ISO_100 0x02 +#define REG_ISO_200 0x03 +#define REG_ISO_400 0x04 +#define REG_ISO_800 0x05 + +/* EV (scenemode) preset for MONITOR */ +#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1) +/* EV (scenemode) preset for CAPTURE */ +#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1) +#define REG_SCENE_NORMAL 0x00 +#define REG_SCENE_PORTRAIT 0x01 +#define REG_SCENE_LANDSCAPE 0x02 +#define REG_SCENE_SPORTS 0x03 +#define REG_SCENE_PARTY_INDOOR 0x04 +#define REG_SCENE_BEACH_SNOW 0x05 +#define REG_SCENE_SUNSET 0x06 +#define REG_SCENE_DAWN_DUSK 0x07 +#define REG_SCENE_FALL 0x08 +#define REG_SCENE_NIGHT 0x09 +#define REG_SCENE_AGAINST_LIGHT 0x0a +#define REG_SCENE_FIRE 0x0b +#define REG_SCENE_TEXT 0x0c +#define REG_SCENE_CANDLE 0x0d + +/* Manual gain in MONITOR mode */ +#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2) +/* Maximum gain in MONITOR mode */ +#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2) +/* Manual gain in CAPTURE mode */ +#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2) + +#define AE_INDEX I2C_REG(CAT_AE, 0x38, 1) +#define REG_AE_INDEX_20_NEG 0x00 +#define REG_AE_INDEX_15_NEG 0x01 +#define REG_AE_INDEX_10_NEG 0x02 +#define REG_AE_INDEX_05_NEG 0x03 +#define REG_AE_INDEX_00 0x04 +#define REG_AE_INDEX_05_POS 0x05 +#define REG_AE_INDEX_10_POS 0x06 +#define REG_AE_INDEX_15_POS 0x07 +#define REG_AE_INDEX_20_POS 0x08 + +/* + * Category 6 - White Balance + */ + +/* Auto Whitebalance locking */ +#define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1) +#define REG_AWB_UNLOCK 0x00 +#define REG_AWB_LOCK 0x01 + +#define AWB_MODE I2C_REG(CAT_WB, 0x02, 1) +#define REG_AWB_AUTO 0x01 /* AWB off */ +#define REG_AWB_PRESET 0x02 /* AWB preset */ + +/* Manual WB (preset) */ +#define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1) +#define REG_AWB_INCANDESCENT 0x01 +#define REG_AWB_FLUORESCENT_1 0x02 +#define REG_AWB_FLUORESCENT_2 0x03 +#define REG_AWB_DAYLIGHT 0x04 +#define REG_AWB_CLOUDY 0x05 +#define REG_AWB_SHADE 0x06 +#define REG_AWB_HORIZON 0x07 +#define REG_AWB_LEDLIGHT 0x09 + +/* + * Category 7 - EXIF information + */ +#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4) +#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4) +#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4) +#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4) +#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4) +#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4) +#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4) +#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4) +#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4) +#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4) +#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2) +#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2) +#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2) +#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2) + +/* + * Category 9 - Face Detection + */ +#define FD_CTL I2C_REG(CAT_FD, 0x00, 1) +#define BIT_FD_EN 0 +#define BIT_FD_DRAW_FACE_FRAME 4 +#define BIT_FD_DRAW_SMILE_LVL 6 +#define REG_FD(shift) (1 << shift) +#define REG_FD_OFF 0x0 + +/* + * Category A - Lens Parameter + */ +#define AF_MODE I2C_REG(CAT_LENS, 0x01, 1) +#define REG_AF_NORMAL 0x00 /* Normal AF, one time */ +#define REG_AF_MACRO 0x01 /* Macro AF, one time */ +#define REG_AF_POWEROFF 0x07 + +#define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1) +#define REG_AF_STOP 0x00 +#define REG_AF_EXE_AUTO 0x01 +#define REG_AF_EXE_CAF 0x02 + +#define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1) +#define REG_AF_FAIL 0x00 +#define REG_AF_SUCCESS 0x02 +#define REG_AF_IDLE 0x04 +#define REG_AF_BUSY 0x05 + +#define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1) + +/* + * Category B - CAPTURE Parameter + */ +#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1) +#define REG_YUV422 0x00 +#define REG_BAYER10 0x05 +#define REG_BAYER8 0x06 +#define REG_JPEG 0x10 + +#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) +#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) + +#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) +#define REG_MCC_OFF 0x00 +#define REG_MCC_NORMAL 0x01 + +#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1) +#define REG_WDR_OFF 0x00 +#define REG_WDR_ON 0x01 +#define REG_WDR_AUTO 0x02 + +#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1) +#define REG_LIGHT_OFF 0x00 +#define REG_LIGHT_ON 0x01 +#define REG_LIGHT_AUTO 0x02 + +#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1) +#define REG_FLASH_OFF 0x00 +#define REG_FLASH_ON 0x01 +#define REG_FLASH_AUTO 0x02 + +/* + * Category C - CAPTURE Control + */ +#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1) +#define REG_CAP_NONE 0x00 +#define REG_CAP_ANTI_SHAKE 0x02 + +/* Select single- or multi-shot capture */ +#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1) + +#define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1) +#define REG_CAP_START_MAIN 0x01 +#define REG_CAP_START_THUMB 0x03 + +#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4) +#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4) + +/* + * Category F - Flash + * + * This mode provides functions about internal flash stuff and system startup. + */ + +/* Starts internal ARM core booting after power-up */ +#define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1) +#define REG_START_ARM_BOOT 0x01 /* write value */ +#define REG_IN_FLASH_MODE 0x00 /* read value */ + +#endif /* M5MOLS_REG_H */ diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c new file mode 100644 index 000000000000..aeb22be7dcbd --- /dev/null +++ b/drivers/media/i2c/msp3400-driver.c @@ -0,0 +1,899 @@ +/* + * Programming the mspx4xx sound processor family + * + * (c) 1997-2001 Gerd Knorr + * + * what works and what doesn't: + * + * AM-Mono + * Support for Hauppauge cards added (decoding handled by tuner) added by + * Frederic Crozat + * + * FM-Mono + * should work. The stereo modes are backward compatible to FM-mono, + * therefore FM-Mono should be allways available. + * + * FM-Stereo (B/G, used in germany) + * should work, with autodetect + * + * FM-Stereo (satellite) + * should work, no autodetect (i.e. default is mono, but you can + * switch to stereo -- untested) + * + * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) + * should work, with autodetect. Support for NICAM was added by + * Pekka Pietikainen + * + * TODO: + * - better SAT support + * + * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) + * using soundcore instead of OSS + * + * This program is free software; 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msp3400-driver.h" + +/* ---------------------------------------------------------------------- */ + +MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +/* module parameters */ +static int opmode = OPMODE_AUTO; +int msp_debug; /* msp_debug output */ +bool msp_once; /* no continuous stereo monitoring */ +bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france), + the autoscan seems work well only with FM... */ +int msp_standard = 1; /* Override auto detect of audio msp_standard, + if needed. */ +bool msp_dolby; + +int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual + (msp34xxg only) 0x00a0-0x03c0 */ + +/* read-only */ +module_param(opmode, int, 0444); + +/* read-write */ +module_param_named(once, msp_once, bool, 0644); +module_param_named(debug, msp_debug, int, 0644); +module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644); +module_param_named(standard, msp_standard, int, 0644); +module_param_named(amsound, msp_amsound, bool, 0644); +module_param_named(dolby, msp_dolby, bool, 0644); + +MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect"); +MODULE_PARM_DESC(once, "No continuous stereo monitoring"); +MODULE_PARM_DESC(debug, "Enable debug messages [0-3]"); +MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo"); +MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); +MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); +MODULE_PARM_DESC(dolby, "Activates Dolby processing"); + +/* ---------------------------------------------------------------------- */ + +/* control subaddress */ +#define I2C_MSP_CONTROL 0x00 +/* demodulator unit subaddress */ +#define I2C_MSP_DEM 0x10 +/* DSP unit subaddress */ +#define I2C_MSP_DSP 0x12 + + +/* ----------------------------------------------------------------------- */ +/* functions for talking to the MSP3400C Sound processor */ + +int msp_reset(struct i2c_client *client) +{ + /* reset and read revision code */ + static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 }; + static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 }; + static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e }; + u8 read[2]; + struct i2c_msg reset[2] = { + { client->addr, I2C_M_IGNORE_NAK, 3, reset_off }, + { client->addr, I2C_M_IGNORE_NAK, 3, reset_on }, + }; + struct i2c_msg test[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read }, + }; + + v4l_dbg(3, msp_debug, client, "msp_reset\n"); + if (i2c_transfer(client->adapter, &reset[0], 1) != 1 || + i2c_transfer(client->adapter, &reset[1], 1) != 1 || + i2c_transfer(client->adapter, test, 2) != 2) { + v4l_err(client, "chip reset failed\n"); + return -1; + } + return 0; +} + +static int msp_read(struct i2c_client *client, int dev, int addr) +{ + int err, retval; + u8 write[3]; + u8 read[2]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read } + }; + + write[0] = dev + 1; + write[1] = addr >> 8; + write[2] = addr & 0xff; + + for (err = 0; err < 3; err++) { + if (i2c_transfer(client->adapter, msgs, 2) == 2) + break; + v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err, + dev, addr); + schedule_timeout_interruptible(msecs_to_jiffies(10)); + } + if (err == 3) { + v4l_warn(client, "resetting chip, sound will go off.\n"); + msp_reset(client); + return -1; + } + retval = read[0] << 8 | read[1]; + v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", + dev, addr, retval); + return retval; +} + +int msp_read_dem(struct i2c_client *client, int addr) +{ + return msp_read(client, I2C_MSP_DEM, addr); +} + +int msp_read_dsp(struct i2c_client *client, int addr) +{ + return msp_read(client, I2C_MSP_DSP, addr); +} + +static int msp_write(struct i2c_client *client, int dev, int addr, int val) +{ + int err; + u8 buffer[5]; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + + v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", + dev, addr, val); + for (err = 0; err < 3; err++) { + if (i2c_master_send(client, buffer, 5) == 5) + break; + v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err, + dev, addr); + schedule_timeout_interruptible(msecs_to_jiffies(10)); + } + if (err == 3) { + v4l_warn(client, "resetting chip, sound will go off.\n"); + msp_reset(client); + return -1; + } + return 0; +} + +int msp_write_dem(struct i2c_client *client, int addr, int val) +{ + return msp_write(client, I2C_MSP_DEM, addr, val); +} + +int msp_write_dsp(struct i2c_client *client, int addr, int val) +{ + return msp_write(client, I2C_MSP_DSP, addr, val); +} + +/* ----------------------------------------------------------------------- * + * bits 9 8 5 - SCART DSP input Select: + * 0 0 0 - SCART 1 to DSP input (reset position) + * 0 1 0 - MONO to DSP input + * 1 0 0 - SCART 2 to DSP input + * 1 1 1 - Mute DSP input + * + * bits 11 10 6 - SCART 1 Output Select: + * 0 0 0 - undefined (reset position) + * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) + * 1 0 0 - MONO input to SCART 1 Output + * 1 1 0 - SCART 1 DA to SCART 1 Output + * 0 0 1 - SCART 2 DA to SCART 1 Output + * 0 1 1 - SCART 1 Input to SCART 1 Output + * 1 1 1 - Mute SCART 1 Output + * + * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART): + * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position) + * 0 1 0 - SCART 1 Input to SCART 2 Output + * 1 0 0 - MONO input to SCART 2 Output + * 0 0 1 - SCART 2 DA to SCART 2 Output + * 0 1 1 - SCART 2 Input to SCART 2 Output + * 1 1 0 - Mute SCART 2 Output + * + * Bits 4 to 0 should be zero. + * ----------------------------------------------------------------------- */ + +static int scarts[3][9] = { + /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */ + /* SCART DSP Input select */ + { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 }, + /* SCART1 Output select */ + { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 }, + /* SCART2 Output select */ + { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 }, +}; + +static char *scart_names[] = { + "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute" +}; + +void msp_set_scart(struct i2c_client *client, int in, int out) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + state->in_scart = in; + + if (in >= 0 && in <= 7 && out >= 0 && out <= 2) { + if (-1 == scarts[out][in + 1]) + return; + + state->acb &= ~scarts[out][0]; + state->acb |= scarts[out][in + 1]; + } else + state->acb = 0xf60; /* Mute Input and SCART 1 Output */ + + v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n", + scart_names[in], out, state->acb); + msp_write_dsp(client, 0x13, state->acb); + + /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); +} + +/* ------------------------------------------------------------------------ */ + +static void msp_wake_thread(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (NULL == state->kthread) + return; + state->watch_stereo = 0; + state->restart = 1; + wake_up_interruptible(&state->wq); +} + +int msp_sleep(struct msp_state *state, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&state->wq, &wait); + if (!kthread_should_stop()) { + if (timeout < 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } else { + schedule_timeout_interruptible + (msecs_to_jiffies(timeout)); + } + } + + remove_wait_queue(&state->wq, &wait); + try_to_freeze(); + return state->restart; +} + +/* ------------------------------------------------------------------------ */ + +static int msp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct msp_state *state = ctrl_to_state(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + int val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: { + /* audio volume cluster */ + int reallymuted = state->muted->val | state->scan_in_progress; + + if (!reallymuted) + val = (val * 0x7f / 65535) << 8; + + v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", + state->muted->val ? "on" : "off", + state->scan_in_progress ? "yes" : "no", + state->volume->val); + + msp_write_dsp(client, 0x0000, val); + msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_scart2_out_volume) + msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_headphones) + msp_write_dsp(client, 0x0006, val); + break; + } + + case V4L2_CID_AUDIO_BASS: + val = ((val - 32768) * 0x60 / 65535) << 8; + msp_write_dsp(client, 0x0002, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0031, val); + break; + + case V4L2_CID_AUDIO_TREBLE: + val = ((val - 32768) * 0x60 / 65535) << 8; + msp_write_dsp(client, 0x0003, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0032, val); + break; + + case V4L2_CID_AUDIO_LOUDNESS: + val = val ? ((5 * 4) << 8) : 0; + msp_write_dsp(client, 0x0004, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0033, val); + break; + + case V4L2_CID_AUDIO_BALANCE: + val = (u8)((val / 256) - 128); + msp_write_dsp(client, 0x0001, val << 8); + if (state->has_headphones) + msp_write_dsp(client, 0x0030, val << 8); + break; + + default: + return -EINVAL; + } + return 0; +} + +void msp_update_volume(struct msp_state *state) +{ + /* Force an update of the volume/mute cluster */ + v4l2_ctrl_lock(state->volume); + state->volume->val = state->volume->cur.val; + state->muted->val = state->muted->cur.val; + msp_s_ctrl(state->volume); + v4l2_ctrl_unlock(state->volume); +} + +/* --- v4l2 ioctls --- */ +static int msp_s_radio(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio) + return 0; + state->radio = 1; + v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); + state->watch_stereo = 0; + switch (state->opmode) { + case OPMODE_MANUAL: + /* set msp3400 to FM radio mode */ + msp3400c_set_mode(client, MSP_MODE_FM_RADIO); + msp3400c_set_carrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + msp_update_volume(state); + break; + case OPMODE_AUTODETECT: + case OPMODE_AUTOSELECT: + /* the thread will do for us */ + msp_wake_thread(client); + break; + } + return 0; +} + +static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* new channel -- kick audio carrier scan */ + msp_wake_thread(client); + return 0; +} + +static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + *id &= state->detected_std; + + v4l_dbg(2, msp_debug, client, + "detected standard: %s(0x%08Lx)\n", + msp_standard_std_name(state->std), state->detected_std); + + return 0; +} + +static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int update = state->radio || state->v4l2_std != id; + + state->v4l2_std = id; + state->radio = 0; + if (update) + msp_wake_thread(client); + return 0; +} + +static int msp_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int tuner = (input >> 3) & 1; + int sc_in = input & 0x7; + int sc1_out = output & 0xf; + int sc2_out = (output >> 4) & 0xf; + u16 val, reg; + int i; + int extern_input = 1; + + if (state->route_in == input && state->route_out == output) + return 0; + state->route_in = input; + state->route_out = output; + /* check if the tuner input is used */ + for (i = 0; i < 5; i++) { + if (((input >> (4 + i * 4)) & 0xf) == 0) + extern_input = 0; + } + state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp_set_scart(client, sc_in, 0); + msp_set_scart(client, sc1_out, 1); + msp_set_scart(client, sc2_out, 2); + msp_set_audmode(client); + reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; + val = msp_read_dem(client, reg); + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new input is chosen */ + msp_wake_thread(client); + return 0; +} + +static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (vt->type != V4L2_TUNER_ANALOG_TV) + return 0; + if (!state->radio) { + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + vt->rxsubchans = state->rxsubchans; + } + vt->audmode = state->audmode; + vt->capability |= V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + return 0; +} + +static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (state->radio) /* TODO: add mono/stereo support for radio */ + return 0; + if (state->audmode == vt->audmode) + return 0; + state->audmode = vt->audmode; + /* only set audmode */ + msp_set_audmode(client); + return 0; +} + +static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); + + switch (freq) { + case 1024000: + state->i2s_mode = 0; + break; + case 2048000: + state->i2s_mode = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, + (state->rev1 << 16) | state->rev2); +} + +static int msp_log_status(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + const char *p; + char prefix[V4L2_SUBDEV_NAME_SIZE + 20]; + + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", + client->name, state->rev1, state->rev2); + snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name); + v4l2_ctrl_handler_log_status(&state->hdl, prefix); + switch (state->mode) { + case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; + case MSP_MODE_FM_RADIO: p = "FM Radio"; break; + case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break; + case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break; + case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break; + case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break; + case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break; + case MSP_MODE_BTSC: p = "BTSC"; break; + case MSP_MODE_EXTERN: p = "External input"; break; + default: p = "unknown"; break; + } + if (state->mode == MSP_MODE_EXTERN) { + v4l_info(client, "Mode: %s\n", p); + } else if (state->opmode == OPMODE_MANUAL) { + v4l_info(client, "Mode: %s (%s%s)\n", p, + (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", + (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); + } else { + if (state->opmode == OPMODE_AUTODETECT) + v4l_info(client, "Mode: %s\n", p); + v4l_info(client, "Standard: %s (%s%s)\n", + msp_standard_std_name(state->std), + (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", + (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); + } + v4l_info(client, "Audmode: 0x%04x\n", state->audmode); + v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", + state->route_in, state->route_out); + v4l_info(client, "ACB: 0x%04x\n", state->acb); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int msp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + v4l_dbg(1, msp_debug, client, "suspend\n"); + msp_reset(client); + return 0; +} + +static int msp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + v4l_dbg(1, msp_debug, client, "resume\n"); + msp_wake_thread(client); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops msp_ctrl_ops = { + .s_ctrl = msp_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops msp_core_ops = { + .log_status = msp_log_status, + .g_chip_ident = msp_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = msp_s_std, +}; + +static const struct v4l2_subdev_video_ops msp_video_ops = { + .querystd = msp_querystd, +}; + +static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { + .s_frequency = msp_s_frequency, + .g_tuner = msp_g_tuner, + .s_tuner = msp_s_tuner, + .s_radio = msp_s_radio, +}; + +static const struct v4l2_subdev_audio_ops msp_audio_ops = { + .s_routing = msp_s_routing, + .s_i2s_clock_freq = msp_s_i2s_clock_freq, +}; + +static const struct v4l2_subdev_ops msp_ops = { + .core = &msp_core_ops, + .video = &msp_video_ops, + .tuner = &msp_tuner_ops, + .audio = &msp_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct msp_state *state; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int (*thread_func)(void *data) = NULL; + int msp_hard; + int msp_family; + int msp_revision; + int msp_product, msp_prod_hi, msp_prod_lo; + int msp_rom; + + if (!id) + strlcpy(client->name, "msp3400", sizeof(client->name)); + + if (msp_reset(client) == -1) { + v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); + return -ENODEV; + } + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &msp_ops); + + state->v4l2_std = V4L2_STD_NTSC; + state->detected_std = V4L2_STD_ALL; + state->audmode = V4L2_TUNER_MODE_STEREO; + state->input = -1; + state->i2s_mode = 0; + init_waitqueue_head(&state->wq); + /* These are the reset input/output positions */ + state->route_in = MSP_INPUT_DEFAULT; + state->route_out = MSP_OUTPUT_DEFAULT; + + state->rev1 = msp_read_dsp(client, 0x1e); + if (state->rev1 != -1) + state->rev2 = msp_read_dsp(client, 0x1f); + v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", + state->rev1, state->rev2); + if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) { + v4l_dbg(1, msp_debug, client, + "not an msp3400 (cannot read chip version)\n"); + kfree(state); + return -ENODEV; + } + + msp_family = ((state->rev1 >> 4) & 0x0f) + 3; + msp_product = (state->rev2 >> 8) & 0xff; + msp_prod_hi = msp_product / 10; + msp_prod_lo = msp_product % 10; + msp_revision = (state->rev1 & 0x0f) + '@'; + msp_hard = ((state->rev1 >> 8) & 0xff) + '@'; + msp_rom = state->rev2 & 0x1f; + /* Rev B=2, C=3, D=4, G=7 */ + state->ident = msp_family * 10000 + 4000 + msp_product * 10 + + msp_revision - '@'; + + /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ + state->has_nicam = + msp_prod_hi == 1 || msp_prod_hi == 5; + /* Has radio support: was added with revision G */ + state->has_radio = + msp_revision >= 'G'; + /* Has headphones output: not for stripped down products */ + state->has_headphones = + msp_prod_lo < 5; + /* Has scart2 input: not in stripped down products of the '3' family */ + state->has_scart2 = + msp_family >= 4 || msp_prod_lo < 7; + /* Has scart3 input: not in stripped down products of the '3' family */ + state->has_scart3 = + msp_family >= 4 || msp_prod_lo < 5; + /* Has scart4 input: not in pre D revisions, not in stripped D revs */ + state->has_scart4 = + msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); + /* Has scart2 output: not in stripped down products of + * the '3' family */ + state->has_scart2_out = + msp_family >= 4 || msp_prod_lo < 5; + /* Has scart2 a volume control? Not in pre-D revisions. */ + state->has_scart2_out_volume = + msp_revision > 'C' && state->has_scart2_out; + /* Has a configurable i2s out? */ + state->has_i2s_conf = + msp_revision >= 'G' && msp_prod_lo < 7; + /* Has subwoofer output: not in pre-D revs and not in stripped down + * products */ + state->has_subwoofer = + msp_revision >= 'D' && msp_prod_lo < 5; + /* Has soundprocessing (bass/treble/balance/loudness/equalizer): + * not in stripped down products */ + state->has_sound_processing = + msp_prod_lo < 7; + /* Has Virtual Dolby Surround: only in msp34x1 */ + state->has_virtual_dolby_surround = + msp_revision == 'G' && msp_prod_lo == 1; + /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */ + state->has_dolby_pro_logic = + msp_revision == 'G' && msp_prod_lo == 2; + /* The msp343xG supports BTSC only and cannot do Automatic Standard + * Detection. */ + state->force_btsc = + msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3; + + state->opmode = opmode; + if (state->opmode == OPMODE_AUTO) { + /* MSP revision G and up have both autodetect and autoselect */ + if (msp_revision >= 'G') + state->opmode = OPMODE_AUTOSELECT; + /* MSP revision D and up have autodetect */ + else if (msp_revision >= 'D') + state->opmode = OPMODE_AUTODETECT; + else + state->opmode = OPMODE_MANUAL; + } + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + if (state->has_sound_processing) { + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0); + } + state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(state); + return err; + } + + v4l2_ctrl_cluster(2, &state->volume); + v4l2_ctrl_handler_setup(hdl); + + /* hello world :-) */ + v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", + msp_family, msp_product, + msp_revision, msp_hard, msp_rom, + client->addr << 1, client->adapter->name); + v4l_info(client, "%s ", client->name); + if (state->has_nicam && state->has_radio) + printk(KERN_CONT "supports nicam and radio, "); + else if (state->has_nicam) + printk(KERN_CONT "supports nicam, "); + else if (state->has_radio) + printk(KERN_CONT "supports radio, "); + printk(KERN_CONT "mode is "); + + /* version-specific initialization */ + switch (state->opmode) { + case OPMODE_MANUAL: + printk(KERN_CONT "manual"); + thread_func = msp3400c_thread; + break; + case OPMODE_AUTODETECT: + printk(KERN_CONT "autodetect"); + thread_func = msp3410d_thread; + break; + case OPMODE_AUTOSELECT: + printk(KERN_CONT "autodetect and autoselect"); + thread_func = msp34xxg_thread; + break; + } + printk(KERN_CONT "\n"); + + /* startup control thread if needed */ + if (thread_func) { + state->kthread = kthread_run(thread_func, client, "msp34xx"); + + if (IS_ERR(state->kthread)) + v4l_warn(client, "kernel_thread() failed\n"); + msp_wake_thread(client); + } + return 0; +} + +static int msp_remove(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + v4l2_device_unregister_subdev(&state->sd); + /* shutdown control thread */ + if (state->kthread) { + state->restart = 1; + kthread_stop(state->kthread); + } + msp_reset(client); + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct dev_pm_ops msp3400_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) +}; + +static const struct i2c_device_id msp_id[] = { + { "msp3400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, msp_id); + +static struct i2c_driver msp_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "msp3400", + .pm = &msp3400_pm_ops, + }, + .probe = msp_probe, + .remove = msp_remove, + .id_table = msp_id, +}; + +module_i2c_driver(msp_driver); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/msp3400-driver.h b/drivers/media/i2c/msp3400-driver.h new file mode 100644 index 000000000000..fbe5e0715f93 --- /dev/null +++ b/drivers/media/i2c/msp3400-driver.h @@ -0,0 +1,137 @@ +/* + */ + +#ifndef MSP3400_DRIVER_H +#define MSP3400_DRIVER_H + +#include +#include +#include + +/* ---------------------------------------------------------------------- */ + +/* This macro is allowed for *constants* only, gcc must calculate it + at compile time. Remember -- no floats in kernel mode */ +#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) + +#define MSP_MODE_AM_DETECT 0 +#define MSP_MODE_FM_RADIO 2 +#define MSP_MODE_FM_TERRA 3 +#define MSP_MODE_FM_SAT 4 +#define MSP_MODE_FM_NICAM1 5 +#define MSP_MODE_FM_NICAM2 6 +#define MSP_MODE_AM_NICAM 7 +#define MSP_MODE_BTSC 8 +#define MSP_MODE_EXTERN 9 + +#define SCART_IN1 0 +#define SCART_IN2 1 +#define SCART_IN3 2 +#define SCART_IN4 3 +#define SCART_IN1_DA 4 +#define SCART_IN2_DA 5 +#define SCART_MONO 6 +#define SCART_MUTE 7 + +#define SCART_DSP_IN 0 +#define SCART1_OUT 1 +#define SCART2_OUT 2 + +#define OPMODE_AUTO -1 +#define OPMODE_MANUAL 0 +#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ +#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ + +/* module parameters */ +extern int msp_debug; +extern bool msp_once; +extern bool msp_amsound; +extern int msp_standard; +extern bool msp_dolby; +extern int msp_stereo_thresh; + +struct msp_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + int rev1, rev2; + int ident; + u8 has_nicam; + u8 has_radio; + u8 has_headphones; + u8 has_ntsc_jp_d_k3; + u8 has_scart2; + u8 has_scart3; + u8 has_scart4; + u8 has_scart2_out; + u8 has_scart2_out_volume; + u8 has_i2s_conf; + u8 has_subwoofer; + u8 has_sound_processing; + u8 has_virtual_dolby_surround; + u8 has_dolby_pro_logic; + u8 force_btsc; + + int radio; + int opmode; + int std; + int mode; + v4l2_std_id v4l2_std, detected_std; + int nicam_on; + int acb; + int in_scart; + int i2s_mode; + int main, second; /* sound carrier */ + int input; + u32 route_in; + u32 route_out; + + /* v4l2 */ + int audmode; + int rxsubchans; + + struct { + /* volume cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *muted; + }; + + int scan_in_progress; + + /* thread */ + struct task_struct *kthread; + wait_queue_head_t wq; + unsigned int restart:1; + unsigned int watch_stereo:1; +}; + +static inline struct msp_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct msp_state, sd); +} + +static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct msp_state, hdl); +} + +/* msp3400-driver.c */ +int msp_write_dem(struct i2c_client *client, int addr, int val); +int msp_write_dsp(struct i2c_client *client, int addr, int val); +int msp_read_dem(struct i2c_client *client, int addr); +int msp_read_dsp(struct i2c_client *client, int addr); +int msp_reset(struct i2c_client *client); +void msp_set_scart(struct i2c_client *client, int in, int out); +void msp_update_volume(struct msp_state *state); +int msp_sleep(struct msp_state *state, int timeout); + +/* msp3400-kthreads.c */ +const char *msp_standard_std_name(int std); +void msp_set_audmode(struct i2c_client *client); +int msp_detect_stereo(struct i2c_client *client); +int msp3400c_thread(void *data); +int msp3410d_thread(void *data); +int msp34xxg_thread(void *data); +void msp3400c_set_mode(struct i2c_client *client, int mode); +void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2); + +#endif /* MSP3400_DRIVER_H */ diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c new file mode 100644 index 000000000000..f8b51714f2f9 --- /dev/null +++ b/drivers/media/i2c/msp3400-kthreads.c @@ -0,0 +1,1165 @@ +/* + * Programming the mspx4xx sound processor family + * + * (c) 1997-2001 Gerd Knorr + * + * This program is free software; 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "msp3400-driver.h" + +/* this one uses the automatic sound standard detection of newer msp34xx + chip versions */ +static struct { + int retval; + int main, second; + char *name; + v4l2_std_id std; +} msp_stdlist[] = { + { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL }, + { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL }, + { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), + "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN }, + { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), + "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG }, + { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), + "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), + "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), + "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK }, + { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), + "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), + "5.5/5.85 B/G NICAM FM", V4L2_STD_BG }, + { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 L NICAM AM", V4L2_STD_L }, + { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), + "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I }, + { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM", V4L2_STD_DK }, + { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK }, + { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK }, + { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M BTSC-Stereo", V4L2_STD_MTS }, + { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS }, + { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP }, + { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), + "10.7 FM-Stereo Radio", V4L2_STD_ALL }, + { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), + "6.5 SAT-Mono", V4L2_STD_ALL }, + { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), + "7.02/7.20 SAT-Stereo", V4L2_STD_ALL }, + { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), + "7.2 SAT ADR", V4L2_STD_ALL }, + { -1, 0, 0, NULL, 0 }, /* EOF */ +}; + +static struct msp3400c_init_data_dem { + int fir1[6]; + int fir2[6]; + int cdo1; + int cdo2; + int ad_cv; + int mode_reg; + int dsp_src; + int dsp_matrix; +} msp3400c_init_data[] = { + { /* AM (for carrier detect / msp3400) */ + {75, 19, 36, 35, 39, 40}, + {75, 19, 36, 35, 39, 40}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0500, 0x0020, 0x3000 + }, { /* AM (for carrier detect / msp3410) */ + {-1, -1, -8, 2, 59, 126}, + {-1, -1, -8, 2, 59, 126}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0100, 0x0020, 0x3000 + }, { /* FM Radio */ + {-8, -8, 4, 6, 78, 107}, + {-8, -8, 4, 6, 78, 107}, + MSP_CARRIER(10.7), MSP_CARRIER(10.7), + 0x00d0, 0x0480, 0x0020, 0x3000 + }, { /* Terrestrial FM-mono + FM-stereo */ + {3, 18, 27, 48, 66, 72}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0480, 0x0030, 0x3000 + }, { /* Sat FM-mono */ + { 1, 9, 14, 24, 33, 37}, + { 3, 18, 27, 48, 66, 72}, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0480, 0x0000, 0x3000 + }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ + {-2, -8, -10, 10, 50, 86}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0040, 0x0120, 0x3000 + }, { /* NICAM/FM -- I (6.0/6.552) */ + {2, 4, -6, -4, 40, 94}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(6.0), MSP_CARRIER(6.0), + 0x00d0, 0x0040, 0x0120, 0x3000 + }, { /* NICAM/AM -- L (6.5/5.85) */ + {-2, -8, -10, 10, 50, 86}, + {-4, -12, -9, 23, 79, 126}, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0140, 0x0120, 0x7c00 + }, +}; + +struct msp3400c_carrier_detect { + int cdo; + char *name; +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { + /* main carrier */ + { MSP_CARRIER(4.5), "4.5 NTSC" }, + { MSP_CARRIER(5.5), "5.5 PAL B/G" }, + { MSP_CARRIER(6.0), "6.0 PAL I" }, + { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { + /* PAL B/G */ + { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, + { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { + /* PAL SAT / SECAM */ + { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, + { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, + { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, + { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, + { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, + { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, +}; + +/* ------------------------------------------------------------------------ */ + +const char *msp_standard_std_name(int std) +{ + int i; + + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == std) + return msp_stdlist[i].name; + return "unknown"; +} + +static v4l2_std_id msp_standard_std(int std) +{ + int i; + + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == std) + return msp_stdlist[i].std; + return V4L2_STD_ALL; +} + +static void msp_set_source(struct i2c_client *client, u16 src) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (msp_dolby) { + msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ + msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ + } else { + msp_write_dsp(client, 0x0008, src); + msp_write_dsp(client, 0x0009, src); + } + msp_write_dsp(client, 0x000a, src); + msp_write_dsp(client, 0x000b, src); + msp_write_dsp(client, 0x000c, src); + if (state->has_scart2_out) + msp_write_dsp(client, 0x0041, src); +} + +void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) +{ + msp_write_dem(client, 0x0093, cdo1 & 0xfff); + msp_write_dem(client, 0x009b, cdo1 >> 12); + msp_write_dem(client, 0x00a3, cdo2 & 0xfff); + msp_write_dem(client, 0x00ab, cdo2 >> 12); + msp_write_dem(client, 0x0056, 0); /* LOAD_REG_1/2 */ +} + +void msp3400c_set_mode(struct i2c_client *client, int mode) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; + int tuner = (state->route_in >> 3) & 1; + int i; + + v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode); + state->mode = mode; + state->rxsubchans = V4L2_TUNER_SUB_MONO; + + msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0)); + + for (i = 5; i >= 0; i--) /* fir 1 */ + msp_write_dem(client, 0x0001, data->fir1[i]); + + msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ + msp_write_dem(client, 0x0005, 0x0040); + msp_write_dem(client, 0x0005, 0x0000); + for (i = 5; i >= 0; i--) + msp_write_dem(client, 0x0005, data->fir2[i]); + + msp_write_dem(client, 0x0083, data->mode_reg); + + msp3400c_set_carrier(client, data->cdo1, data->cdo2); + + msp_set_source(client, data->dsp_src); + /* set prescales */ + + /* volume prescale for SCART (AM mono input) */ + msp_write_dsp(client, 0x000d, 0x1900); + msp_write_dsp(client, 0x000e, data->dsp_matrix); + if (state->has_nicam) /* nicam prescale */ + msp_write_dsp(client, 0x0010, 0x5a00); +} + +/* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP, + nor do they support stereo BTSC. */ +static void msp3400c_set_audmode(struct i2c_client *client) +{ + static char *strmode[] = { + "mono", "stereo", "lang2", "lang1", "lang1+lang2" + }; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + char *modestr = (state->audmode >= 0 && state->audmode < 5) ? + strmode[state->audmode] : "unknown"; + int src = 0; /* channel source: FM/AM, nicam or SCART */ + int audmode = state->audmode; + + if (state->opmode == OPMODE_AUTOSELECT) { + /* this method would break everything, let's make sure + * it's never called + */ + v4l_dbg(1, msp_debug, client, + "set_audmode called with mode=%d instead of set_source (ignored)\n", + state->audmode); + return; + } + + /* Note: for the C and D revs no NTSC stereo + SAP is possible as + the hardware does not support SAP. So the rxsubchans combination + of STEREO | LANG2 does not occur. */ + + if (state->mode != MSP_MODE_EXTERN) { + /* switch to mono if only mono is available */ + if (state->rxsubchans == V4L2_TUNER_SUB_MONO) + audmode = V4L2_TUNER_MODE_MONO; + /* if bilingual */ + else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { + /* and mono or stereo, then fallback to lang1 */ + if (audmode == V4L2_TUNER_MODE_MONO || + audmode == V4L2_TUNER_MODE_STEREO) + audmode = V4L2_TUNER_MODE_LANG1; + } + /* if stereo, and audmode is not mono, then switch to stereo */ + else if (audmode != V4L2_TUNER_MODE_MONO) + audmode = V4L2_TUNER_MODE_STEREO; + } + + /* switch demodulator */ + switch (state->mode) { + case MSP_MODE_FM_TERRA: + v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr); + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: + msp_write_dsp(client, 0x000e, 0x3001); + break; + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + case V4L2_TUNER_MODE_LANG1_LANG2: + msp_write_dsp(client, 0x000e, 0x3000); + break; + } + break; + case MSP_MODE_FM_SAT: + v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr); + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG1: + msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG2: + msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + } + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + v4l_dbg(1, msp_debug, client, + "NICAM set_audmode: %s\n", modestr); + if (state->nicam_on) + src = 0x0100; /* NICAM */ + break; + case MSP_MODE_BTSC: + v4l_dbg(1, msp_debug, client, + "BTSC set_audmode: %s\n", modestr); + break; + case MSP_MODE_EXTERN: + v4l_dbg(1, msp_debug, client, + "extern set_audmode: %s\n", modestr); + src = 0x0200; /* SCART */ + break; + case MSP_MODE_FM_RADIO: + v4l_dbg(1, msp_debug, client, + "FM-Radio set_audmode: %s\n", modestr); + break; + default: + v4l_dbg(1, msp_debug, client, "mono set_audmode\n"); + return; + } + + /* switch audio */ + v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode); + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + src |= 0x0020; + break; + case V4L2_TUNER_MODE_MONO: + if (state->mode == MSP_MODE_AM_NICAM) { + v4l_dbg(1, msp_debug, client, "switching to AM mono\n"); + /* AM mono decoding is handled by tuner, not MSP chip */ + /* SCART switching control register */ + msp_set_scart(client, SCART_MONO, 0); + src = 0x0200; + break; + } + if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) + src = 0x0030; + break; + case V4L2_TUNER_MODE_LANG1: + break; + case V4L2_TUNER_MODE_LANG2: + src |= 0x0010; + break; + } + v4l_dbg(1, msp_debug, client, + "set_audmode final source/matrix = 0x%x\n", src); + + msp_set_source(client, src); +} + +static void msp3400c_print_mode(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (state->main == state->second) + v4l_dbg(1, msp_debug, client, + "mono sound carrier: %d.%03d MHz\n", + state->main / 910000, (state->main / 910) % 1000); + else + v4l_dbg(1, msp_debug, client, + "main sound carrier: %d.%03d MHz\n", + state->main / 910000, (state->main / 910) % 1000); + if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) + v4l_dbg(1, msp_debug, client, + "NICAM/FM carrier : %d.%03d MHz\n", + state->second / 910000, (state->second/910) % 1000); + if (state->mode == MSP_MODE_AM_NICAM) + v4l_dbg(1, msp_debug, client, + "NICAM/AM carrier : %d.%03d MHz\n", + state->second / 910000, (state->second / 910) % 1000); + if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { + v4l_dbg(1, msp_debug, client, + "FM-stereo carrier : %d.%03d MHz\n", + state->second / 910000, (state->second / 910) % 1000); + } +} + +/* ----------------------------------------------------------------------- */ + +static int msp3400c_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int val; + int rxsubchans = state->rxsubchans; + int newnicam = state->nicam_on; + int update = 0; + + switch (state->mode) { + case MSP_MODE_FM_TERRA: + val = msp_read_dsp(client, 0x18); + if (val > 32767) + val -= 65536; + v4l_dbg(2, msp_debug, client, + "stereo detect register: %d\n", val); + if (val > 8192) { + rxsubchans = V4L2_TUNER_SUB_STEREO; + } else if (val < -4096) { + rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } else { + rxsubchans = V4L2_TUNER_SUB_MONO; + } + newnicam = 0; + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + val = msp_read_dem(client, 0x23); + v4l_dbg(2, msp_debug, client, "nicam sync=%d, mode=%d\n", + val & 1, (val & 0x1e) >> 1); + + if (val & 1) { + /* nicam synced */ + switch ((val & 0x1e) >> 1) { + case 0: + case 8: + rxsubchans = V4L2_TUNER_SUB_STEREO; + break; + case 1: + case 9: + rxsubchans = V4L2_TUNER_SUB_MONO; + break; + case 2: + case 10: + rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + default: + rxsubchans = V4L2_TUNER_SUB_MONO; + break; + } + newnicam = 1; + } else { + newnicam = 0; + rxsubchans = V4L2_TUNER_SUB_MONO; + } + break; + } + if (rxsubchans != state->rxsubchans) { + update = 1; + v4l_dbg(1, msp_debug, client, + "watch: rxsubchans %02x => %02x\n", + state->rxsubchans, rxsubchans); + state->rxsubchans = rxsubchans; + } + if (newnicam != state->nicam_on) { + update = 1; + v4l_dbg(1, msp_debug, client, "watch: nicam %d => %d\n", + state->nicam_on, newnicam); + state->nicam_on = newnicam; + } + return update; +} + +/* + * A kernel thread for msp3400 control -- we don't want to block the + * in the ioctl while doing the sound carrier & stereo detect + */ +/* stereo/multilang monitoring */ +static void watch_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (msp_detect_stereo(client)) + msp_set_audmode(client); + + if (msp_once) + state->watch_stereo = 0; +} + +int msp3400c_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + struct msp3400c_carrier_detect *cd; + int count, max1, max2, val1, val2, val, i; + + v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); + state->detected_std = V4L2_STD_ALL; + set_freezable(); + for (;;) { + v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n"); + +restart: + v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->radio || MSP_MODE_EXTERN == state->mode) { + /* no carrier scan, just unmute */ + v4l_dbg(1, msp_debug, client, + "thread: no carrier scan\n"); + state->scan_in_progress = 0; + msp_update_volume(state); + continue; + } + + /* mute audio */ + state->scan_in_progress = 1; + msp_update_volume(state); + + msp3400c_set_mode(client, MSP_MODE_AM_DETECT); + val1 = val2 = 0; + max1 = max2 = -1; + state->watch_stereo = 0; + state->nicam_on = 0; + + /* wait for tuner to settle down after a channel change */ + if (msp_sleep(state, 200)) + goto restart; + + /* carrier detect pass #1 -- main carrier */ + cd = msp3400c_carrier_detect_main; + count = ARRAY_SIZE(msp3400c_carrier_detect_main); + + if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { + /* autodetect doesn't work well with AM ... */ + max1 = 3; + count = 0; + v4l_dbg(1, msp_debug, client, "AM sound override\n"); + } + + for (i = 0; i < count; i++) { + msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); + if (msp_sleep(state, 100)) + goto restart; + val = msp_read_dsp(client, 0x1b); + if (val > 32767) + val -= 65536; + if (val1 < val) + val1 = val, max1 = i; + v4l_dbg(1, msp_debug, client, + "carrier1 val: %5d / %s\n", val, cd[i].name); + } + + /* carrier detect pass #2 -- second (stereo) carrier */ + switch (max1) { + case 1: /* 5.5 */ + cd = msp3400c_carrier_detect_55; + count = ARRAY_SIZE(msp3400c_carrier_detect_55); + break; + case 3: /* 6.5 */ + cd = msp3400c_carrier_detect_65; + count = ARRAY_SIZE(msp3400c_carrier_detect_65); + break; + case 0: /* 4.5 */ + case 2: /* 6.0 */ + default: + cd = NULL; + count = 0; + break; + } + + if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { + /* autodetect doesn't work well with AM ... */ + cd = NULL; + count = 0; + max2 = 0; + } + for (i = 0; i < count; i++) { + msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); + if (msp_sleep(state, 100)) + goto restart; + val = msp_read_dsp(client, 0x1b); + if (val > 32767) + val -= 65536; + if (val2 < val) + val2 = val, max2 = i; + v4l_dbg(1, msp_debug, client, + "carrier2 val: %5d / %s\n", val, cd[i].name); + } + + /* program the msp3400 according to the results */ + state->main = msp3400c_carrier_detect_main[max1].cdo; + switch (max1) { + case 1: /* 5.5 */ + state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H; + if (max2 == 0) { + /* B/G FM-stereo */ + state->second = msp3400c_carrier_detect_55[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_TERRA); + state->watch_stereo = 1; + } else if (max2 == 1 && state->has_nicam) { + /* B/G NICAM */ + state->second = msp3400c_carrier_detect_55[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); + state->nicam_on = 1; + state->watch_stereo = 1; + } else { + goto no_second; + } + break; + case 2: /* 6.0 */ + /* PAL I NICAM */ + state->detected_std = V4L2_STD_PAL_I; + state->second = MSP_CARRIER(6.552); + msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); + state->nicam_on = 1; + state->watch_stereo = 1; + break; + case 3: /* 6.5 */ + if (max2 == 1 || max2 == 2) { + /* D/K FM-stereo */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_TERRA); + state->watch_stereo = 1; + state->detected_std = V4L2_STD_DK; + } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { + /* L NICAM or AM-mono */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_AM_NICAM); + state->watch_stereo = 1; + state->detected_std = V4L2_STD_L; + } else if (max2 == 0 && state->has_nicam) { + /* D/K NICAM */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); + state->nicam_on = 1; + state->watch_stereo = 1; + state->detected_std = V4L2_STD_DK; + } else { + goto no_second; + } + break; + case 0: /* 4.5 */ + state->detected_std = V4L2_STD_MN; + default: +no_second: + state->second = msp3400c_carrier_detect_main[max1].cdo; + msp3400c_set_mode(client, MSP_MODE_FM_TERRA); + break; + } + msp3400c_set_carrier(client, state->second, state->main); + + /* unmute */ + state->scan_in_progress = 0; + msp3400c_set_audmode(client); + msp_update_volume(state); + + if (msp_debug) + msp3400c_print_mode(client); + + /* monitor tv audio mode, the first time don't wait + so long to get a quick stereo/bilingual result */ + count = 3; + while (state->watch_stereo) { + if (msp_sleep(state, count ? 1000 : 5000)) + goto restart; + if (count) + count--; + watch_stereo(client); + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + + +int msp3410d_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int val, i, std, count; + + v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); + state->detected_std = V4L2_STD_ALL; + set_freezable(); + for (;;) { + v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n"); + +restart: + v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + v4l_dbg(1, msp_debug, client, + "thread: no carrier scan\n"); + state->scan_in_progress = 0; + msp_update_volume(state); + continue; + } + + /* mute audio */ + state->scan_in_progress = 1; + msp_update_volume(state); + + /* start autodetect. Note: autodetect is not supported for + NTSC-M and radio, hence we force the standard in those + cases. */ + if (state->radio) + std = 0x40; + else + std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; + state->watch_stereo = 0; + state->nicam_on = 0; + + /* wait for tuner to settle down after a channel change */ + if (msp_sleep(state, 200)) + goto restart; + + if (msp_debug) + v4l_dbg(2, msp_debug, client, + "setting standard: %s (0x%04x)\n", + msp_standard_std_name(std), std); + + if (std != 1) { + /* programmed some specific mode */ + val = std; + } else { + /* triggered autodetect */ + msp_write_dem(client, 0x20, std); + for (;;) { + if (msp_sleep(state, 100)) + goto restart; + + /* check results */ + val = msp_read_dem(client, 0x7e); + if (val < 0x07ff) + break; + v4l_dbg(2, msp_debug, client, + "detection still in progress\n"); + } + } + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == val) + break; + v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n", + msp_standard_std_name(val), val); + state->main = msp_stdlist[i].main; + state->second = msp_stdlist[i].second; + state->std = val; + state->rxsubchans = V4L2_TUNER_SUB_MONO; + + if (msp_amsound && !state->radio && + (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { + /* autodetection has failed, let backup */ + v4l_dbg(1, msp_debug, client, "autodetection failed," + " switching to backup standard: %s (0x%04x)\n", + msp_stdlist[8].name ? + msp_stdlist[8].name : "unknown", val); + state->std = val = 0x0009; + msp_write_dem(client, 0x20, val); + } else { + state->detected_std = msp_standard_std(state->std); + } + + /* set stereo */ + switch (val) { + case 0x0008: /* B/G NICAM */ + case 0x000a: /* I NICAM */ + case 0x000b: /* D/K NICAM */ + if (val == 0x000a) + state->mode = MSP_MODE_FM_NICAM2; + else + state->mode = MSP_MODE_FM_NICAM1; + /* just turn on stereo */ + state->nicam_on = 1; + state->watch_stereo = 1; + break; + case 0x0009: + state->mode = MSP_MODE_AM_NICAM; + state->nicam_on = 1; + state->watch_stereo = 1; + break; + case 0x0020: /* BTSC */ + /* The pre-'G' models only have BTSC-mono */ + state->mode = MSP_MODE_BTSC; + break; + case 0x0040: /* FM radio */ + state->mode = MSP_MODE_FM_RADIO; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + /* not needed in theory if we have radio, but + short programming enables carrier mute */ + msp3400c_set_mode(client, MSP_MODE_FM_RADIO); + msp3400c_set_carrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + break; + case 0x0002: + case 0x0003: + case 0x0004: + case 0x0005: + state->mode = MSP_MODE_FM_TERRA; + state->watch_stereo = 1; + break; + } + + /* set various prescales */ + msp_write_dsp(client, 0x0d, 0x1900); /* scart */ + msp_write_dsp(client, 0x0e, 0x3000); /* FM */ + if (state->has_nicam) + msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ + + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); + + /* unmute */ + msp3400c_set_audmode(client); + state->scan_in_progress = 0; + msp_update_volume(state); + + /* monitor tv audio mode, the first time don't wait + so long to get a quick stereo/bilingual result */ + count = 3; + while (state->watch_stereo) { + if (msp_sleep(state, count ? 1000 : 5000)) + goto restart; + if (count) + count--; + watch_stereo(client); + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* msp34xxG + (autoselect no-thread) + * this one uses both automatic standard detection and automatic sound + * select which are available in the newer G versions + * struct msp: only norm, acb and source are really used in this mode + */ + +static int msp34xxg_modus(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (state->radio) { + v4l_dbg(1, msp_debug, client, "selected radio modus\n"); + return 0x0001; + } + if (state->v4l2_std == V4L2_STD_NTSC_M_JP) { + v4l_dbg(1, msp_debug, client, "selected M (EIA-J) modus\n"); + return 0x4001; + } + if (state->v4l2_std == V4L2_STD_NTSC_M_KR) { + v4l_dbg(1, msp_debug, client, "selected M (A2) modus\n"); + return 0x0001; + } + if (state->v4l2_std == V4L2_STD_SECAM_L) { + v4l_dbg(1, msp_debug, client, "selected SECAM-L modus\n"); + return 0x6001; + } + if (state->v4l2_std & V4L2_STD_MN) { + v4l_dbg(1, msp_debug, client, "selected M (BTSC) modus\n"); + return 0x2001; + } + return 0x7001; +} + +static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) + { + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int source, matrix; + + switch (state->audmode) { + case V4L2_TUNER_MODE_MONO: + source = 0; /* mono only */ + matrix = 0x30; + break; + case V4L2_TUNER_MODE_LANG2: + source = 4; /* stereo or B */ + matrix = 0x10; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + source = 1; /* stereo or A|B */ + matrix = 0x20; + break; + case V4L2_TUNER_MODE_LANG1: + source = 3; /* stereo or A */ + matrix = 0x00; + break; + case V4L2_TUNER_MODE_STEREO: + default: + source = 3; /* stereo or A */ + matrix = 0x20; + break; + } + + if (in == MSP_DSP_IN_TUNER) + source = (source << 8) | 0x20; + /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 + instead of 11, 12, 13. So we add one for that msp version. */ + else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) + source = ((in + 1) << 8) | matrix; + else + source = (in << 8) | matrix; + + v4l_dbg(1, msp_debug, client, + "set source to %d (0x%x) for output %02x\n", in, source, reg); + msp_write_dsp(client, reg, source); +} + +static void msp34xxg_set_sources(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + u32 in = state->route_in; + + msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); + /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ + msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); + msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); + msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); + if (state->has_scart2_out) + msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); + msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); +} + +/* (re-)initialize the msp34xxg */ +static void msp34xxg_reset(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int tuner = (state->route_in >> 3) & 1; + int modus; + + /* initialize std to 1 (autodetect) to signal that no standard is + selected yet. */ + state->std = 1; + + msp_reset(client); + + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); + + /* step-by-step initialisation, as described in the manual */ + modus = msp34xxg_modus(client); + modus |= tuner ? 0x100 : 0; + msp_write_dem(client, 0x30, modus); + + /* write the dsps that may have an influence on + standard/audio autodetection right now */ + msp34xxg_set_sources(client); + + msp_write_dsp(client, 0x0d, 0x1900); /* scart */ + msp_write_dsp(client, 0x0e, 0x3000); /* FM */ + if (state->has_nicam) + msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ + + /* set identification threshold. Personally, I + * I set it to a higher value than the default + * of 0x190 to ignore noisy stereo signals. + * this needs tuning. (recommended range 0x00a0-0x03c0) + * 0x7f0 = forced mono mode + * + * a2 threshold for stereo/bilingual. + * Note: this register is part of the Manual/Compatibility mode. + * It is supported by all 'G'-family chips. + */ + msp_write_dem(client, 0x22, msp_stereo_thresh); +} + +int msp34xxg_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int val, i; + + v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); + state->detected_std = V4L2_STD_ALL; + set_freezable(); + for (;;) { + v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n"); + +restart: + v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + v4l_dbg(1, msp_debug, client, + "thread: no carrier scan\n"); + state->scan_in_progress = 0; + msp_update_volume(state); + continue; + } + + /* setup the chip*/ + msp34xxg_reset(client); + state->std = state->radio ? 0x40 : + (state->force_btsc && msp_standard == 1) ? 32 : msp_standard; + msp_write_dem(client, 0x20, state->std); + /* start autodetect */ + if (state->std != 1) + goto unmute; + + /* watch autodetect */ + v4l_dbg(1, msp_debug, client, + "started autodetect, waiting for result\n"); + for (i = 0; i < 10; i++) { + if (msp_sleep(state, 100)) + goto restart; + + /* check results */ + val = msp_read_dem(client, 0x7e); + if (val < 0x07ff) { + state->std = val; + break; + } + v4l_dbg(2, msp_debug, client, + "detection still in progress\n"); + } + if (state->std == 1) { + v4l_dbg(1, msp_debug, client, + "detection still in progress after 10 tries. giving up.\n"); + continue; + } + +unmute: + v4l_dbg(1, msp_debug, client, + "detected standard: %s (0x%04x)\n", + msp_standard_std_name(state->std), state->std); + state->detected_std = msp_standard_std(state->std); + + if (state->std == 9) { + /* AM NICAM mode */ + msp_write_dsp(client, 0x0e, 0x7c00); + } + + /* unmute: dispatch sound to scart output, set scart volume */ + msp_update_volume(state); + + /* restore ACB */ + if (msp_write_dsp(client, 0x13, state->acb)) + return -1; + + /* the periodic stereo/SAP check is only relevant for + the 0x20 standard (BTSC) */ + if (state->std != 0x20) + continue; + + state->watch_stereo = 1; + + /* monitor tv audio mode, the first time don't wait + in order to get a quick stereo/SAP update */ + watch_stereo(client); + while (state->watch_stereo) { + watch_stereo(client); + if (msp_sleep(state, 5000)) + goto restart; + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + +static int msp34xxg_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + int status = msp_read_dem(client, 0x0200); + int is_bilingual = status & 0x100; + int is_stereo = status & 0x40; + int oldrx = state->rxsubchans; + + if (state->mode == MSP_MODE_EXTERN) + return 0; + + state->rxsubchans = 0; + if (is_stereo) + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + else + state->rxsubchans = V4L2_TUNER_SUB_MONO; + if (is_bilingual) { + if (state->std == 0x20) + state->rxsubchans |= V4L2_TUNER_SUB_SAP; + else + state->rxsubchans = + V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } + v4l_dbg(1, msp_debug, client, + "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", + status, is_stereo, is_bilingual, state->rxsubchans); + return (oldrx != state->rxsubchans); +} + +static void msp34xxg_set_audmode(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + if (state->std == 0x20) { + if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && + (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 || + state->audmode == V4L2_TUNER_MODE_LANG2)) { + msp_write_dem(client, 0x20, 0x21); + } else { + msp_write_dem(client, 0x20, 0x20); + } + } + + msp34xxg_set_sources(client); +} + +void msp_set_audmode(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + msp3400c_set_audmode(client); + break; + case OPMODE_AUTOSELECT: + msp34xxg_set_audmode(client); + break; + } +} + +int msp_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = to_state(i2c_get_clientdata(client)); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + return msp3400c_detect_stereo(client); + case OPMODE_AUTOSELECT: + return msp34xxg_detect_stereo(client); + } + return 0; +} + diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c new file mode 100644 index 000000000000..445359c96113 --- /dev/null +++ b/drivers/media/i2c/mt9m032.c @@ -0,0 +1,878 @@ +/* + * Driver for MT9M032 CMOS Image Sensor from Micron + * + * Copyright (C) 2010-2011 Lund Engineering + * Contact: Gil Lund + * Author: Martin Hostettler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "aptina-pll.h" + +/* + * width and height include active boundary and black parts + * + * column 0- 15 active boundary + * column 16-1455 image + * column 1456-1471 active boundary + * column 1472-1599 black + * + * row 0- 51 black + * row 53- 59 active boundary + * row 60-1139 image + * row 1140-1147 active boundary + * row 1148-1151 black + */ + +#define MT9M032_PIXEL_ARRAY_WIDTH 1600 +#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 + +#define MT9M032_CHIP_VERSION 0x00 +#define MT9M032_CHIP_VERSION_VALUE 0x1402 +#define MT9M032_ROW_START 0x01 +#define MT9M032_ROW_START_MIN 0 +#define MT9M032_ROW_START_MAX 1152 +#define MT9M032_ROW_START_DEF 60 +#define MT9M032_COLUMN_START 0x02 +#define MT9M032_COLUMN_START_MIN 0 +#define MT9M032_COLUMN_START_MAX 1600 +#define MT9M032_COLUMN_START_DEF 16 +#define MT9M032_ROW_SIZE 0x03 +#define MT9M032_ROW_SIZE_MIN 32 +#define MT9M032_ROW_SIZE_MAX 1152 +#define MT9M032_ROW_SIZE_DEF 1080 +#define MT9M032_COLUMN_SIZE 0x04 +#define MT9M032_COLUMN_SIZE_MIN 32 +#define MT9M032_COLUMN_SIZE_MAX 1600 +#define MT9M032_COLUMN_SIZE_DEF 1440 +#define MT9M032_HBLANK 0x05 +#define MT9M032_VBLANK 0x06 +#define MT9M032_VBLANK_MAX 0x7ff +#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 +#define MT9M032_SHUTTER_WIDTH_LOW 0x09 +#define MT9M032_SHUTTER_WIDTH_MIN 1 +#define MT9M032_SHUTTER_WIDTH_MAX 1048575 +#define MT9M032_SHUTTER_WIDTH_DEF 1943 +#define MT9M032_PIX_CLK_CTRL 0x0a +#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 +#define MT9M032_RESTART 0x0b +#define MT9M032_RESET 0x0d +#define MT9M032_PLL_CONFIG1 0x11 +#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f +#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 +#define MT9M032_READ_MODE1 0x1e +#define MT9M032_READ_MODE2 0x20 +#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 +#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 +#define MT9M032_READ_MODE2_ROW_BLC 0x40 +#define MT9M032_GAIN_GREEN1 0x2b +#define MT9M032_GAIN_BLUE 0x2c +#define MT9M032_GAIN_RED 0x2d +#define MT9M032_GAIN_GREEN2 0x2e + +/* write only */ +#define MT9M032_GAIN_ALL 0x35 +#define MT9M032_GAIN_DIGITAL_MASK 0x7f +#define MT9M032_GAIN_DIGITAL_SHIFT 8 +#define MT9M032_GAIN_AMUL_SHIFT 6 +#define MT9M032_GAIN_ANALOG_MASK 0x3f +#define MT9M032_FORMATTER1 0x9e +#define MT9M032_FORMATTER2 0x9f +#define MT9M032_FORMATTER2_DOUT_EN 0x1000 +#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 + +/* + * The available MT9M032 datasheet is missing documentation for register 0x10 + * MT9P031 seems to be close enough, so use constants from that datasheet for + * now. + * But keep the name MT9P031 to remind us, that this isn't really confirmed + * for this sensor. + */ +#define MT9P031_PLL_CONTROL 0x10 +#define MT9P031_PLL_CONTROL_PWROFF 0x0050 +#define MT9P031_PLL_CONTROL_PWRON 0x0051 +#define MT9P031_PLL_CONTROL_USEPLL 0x0052 +#define MT9P031_PLL_CONFIG2 0x11 +#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f + +struct mt9m032 { + struct v4l2_subdev subdev; + struct media_pad pad; + struct mt9m032_platform_data *pdata; + + unsigned int pix_clock; + + struct v4l2_ctrl_handler ctrls; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + struct mutex lock; /* Protects streaming, format, interval and crop */ + + bool streaming; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + struct v4l2_fract frame_interval; +}; + +#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) +#define to_dev(sensor) \ + (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) + +static int mt9m032_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) +{ + unsigned int effective_width; + u32 ns; + + effective_width = width + 716; /* empirical value */ + ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); + dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); + return ns; +} + +static int mt9m032_update_timing(struct mt9m032 *sensor, + struct v4l2_fract *interval) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct v4l2_rect *crop = &sensor->crop; + unsigned int min_vblank; + unsigned int vblank; + u32 row_time; + + if (!interval) + interval = &sensor->frame_interval; + + row_time = mt9m032_row_time(sensor, crop->width); + + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + + if (vblank > MT9M032_VBLANK_MAX) { + /* hardware limits to 11 bit values */ + interval->denominator = 1000; + interval->numerator = + div_u64((crop->height + MT9M032_VBLANK_MAX) * + (u64)row_time * interval->denominator, + 1000000000ULL); + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + } + /* enforce minimal 1.6ms blanking time. */ + min_vblank = 1600000 / row_time; + vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); + + return mt9m032_write(client, MT9M032_VBLANK, vblank); +} + +static int mt9m032_update_geom_timing(struct mt9m032 *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, + sensor->crop.width - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_SIZE, + sensor->crop.height - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_COLUMN_START, + sensor->crop.left); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_START, + sensor->crop.top); + if (!ret) + ret = mt9m032_update_timing(sensor, NULL); + return ret; +} + +static int update_formatter2(struct mt9m032 *sensor, bool streaming) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + u16 reg_val = MT9M032_FORMATTER2_DOUT_EN + | 0x0070; /* parts reserved! */ + /* possibly for changing to 14-bit mode */ + + if (streaming) + reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ + + return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); +} + +static int mt9m032_setup_pll(struct mt9m032 *sensor) +{ + static const struct aptina_pll_limits limits = { + .ext_clock_min = 8000000, + .ext_clock_max = 16500000, + .int_clock_min = 2000000, + .int_clock_max = 24000000, + .out_clock_min = 322000000, + .out_clock_max = 693000000, + .pix_clock_max = 99000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct mt9m032_platform_data *pdata = sensor->pdata; + struct aptina_pll pll; + int ret; + + pll.ext_clock = pdata->ext_clock; + pll.pix_clock = pdata->pix_clock; + + ret = aptina_pll_calculate(&client->dev, &limits, &pll); + if (ret < 0) + return ret; + + sensor->pix_clock = pdata->pix_clock; + + ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, + (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) + | (pll.p1 - 1)); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON | + MT9P031_PLL_CONTROL_USEPLL); + if (!ret) /* more reserved, Continuous, Master Mode */ + ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); + if (!ret) /* Set 14-bit mode, select 7 divider */ + ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Subdev pad operations + */ + +static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_Y8_1X8; + return 0; +} + +static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8) + return -EINVAL; + + fse->min_width = MT9M032_COLUMN_SIZE_DEF; + fse->max_width = MT9M032_COLUMN_SIZE_DEF; + fse->min_height = MT9M032_ROW_SIZE_DEF; + fse->max_height = MT9M032_ROW_SIZE_DEF; + + return 0; +} + +/** + * __mt9m032_get_pad_crop() - get crop rect + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try crop rect from + * @which: select try or active crop rect + * + * Returns a pointer the current active or fh relative try crop rect + */ +static struct v4l2_rect * +__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->crop; + default: + return NULL; + } +} + +/** + * __mt9m032_get_pad_format() - get format + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try format from + * @which: select try or active format + * + * Returns a pointer the current active or fh relative try format + */ +static struct v4l2_mbus_framefmt * +__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->format; + default: + return NULL; + } +} + +static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Scaling is not supported, the format is thus fixed. */ + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + ret = 0; + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + int ret = 0; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, + MT9M032_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, + MT9M032_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, + MT9M032_COLUMN_SIZE_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, + MT9M032_ROW_SIZE_MAX); + + rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + format = __mt9m032_get_pad_format(sensor, fh, crop->which); + format->width = rect.width; + format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ret = mt9m032_update_geom_timing(sensor); + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + memset(fi, 0, sizeof(*fi)); + fi->interval = sensor->frame_interval; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming) { + ret = -EBUSY; + goto done; + } + + /* Avoid divisions by 0. */ + if (fi->interval.denominator == 0) + fi->interval.denominator = 1; + + ret = mt9m032_update_timing(sensor, &fi->interval); + if (!ret) + sensor->frame_interval = fi->interval; + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + ret = update_formatter2(sensor, streaming); + if (!ret) + sensor->streaming = streaming; + mutex_unlock(&sensor->lock); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m032_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int val; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + if (reg->match.addr != client->addr) + return -ENODEV; + + val = mt9m032_read(client, reg->reg); + if (val < 0) + return -EIO; + + reg->size = 2; + reg->val = val; + + return 0; +} + +static int mt9m032_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + return mt9m032_write(client, reg->reg, reg->val); +} +#endif + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) + | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) + | MT9M032_READ_MODE2_ROW_BLC + | 0x0007; + + return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); +} + +static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int digital_gain_val; /* in 1/8th (0..127) */ + int analog_mul; /* 0 or 1 */ + int analog_gain_val; /* in 1/16th. (0..63) */ + u16 reg_val; + + digital_gain_val = 51; /* from setup example */ + + if (val < 63) { + analog_mul = 0; + analog_gain_val = val; + } else { + analog_mul = 1; + analog_gain_val = val / 2; + } + + /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ + /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ + + reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) + << MT9M032_GAIN_DIGITAL_SHIFT) + | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) + | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); + + return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); +} + +static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { + /* round because of multiplier used for values >= 63 */ + ctrl->val &= ~1; + } + + return 0; +} + +static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m032 *sensor = + container_of(ctrl->handler, struct mt9m032, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + return mt9m032_set_gain(sensor, ctrl->val); + + case V4L2_CID_HFLIP: + /* case V4L2_CID_VFLIP: -- In the same cluster */ + return update_read_mode2(sensor, sensor->vflip->val, + sensor->hflip->val); + + case V4L2_CID_EXPOSURE: + ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, + (ctrl->val >> 16) & 0xffff); + if (ret < 0) + return ret; + + return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, + ctrl->val & 0xffff); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9m032_ctrl_ops = { + .s_ctrl = mt9m032_set_ctrl, + .try_ctrl = mt9m032_try_ctrl, +}; + +/* -------------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops mt9m032_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m032_g_register, + .s_register = mt9m032_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops mt9m032_video_ops = { + .s_stream = mt9m032_s_stream, + .g_frame_interval = mt9m032_get_frame_interval, + .s_frame_interval = mt9m032_set_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { + .enum_mbus_code = mt9m032_enum_mbus_code, + .enum_frame_size = mt9m032_enum_frame_size, + .get_fmt = mt9m032_get_pad_format, + .set_fmt = mt9m032_set_pad_format, + .set_crop = mt9m032_set_pad_crop, + .get_crop = mt9m032_get_pad_crop, +}; + +static const struct v4l2_subdev_ops mt9m032_ops = { + .core = &mt9m032_core_ops, + .video = &mt9m032_video_ops, + .pad = &mt9m032_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9m032_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct mt9m032_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *adapter = client->adapter; + struct mt9m032 *sensor; + int chip_version; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + if (!client->dev.platform_data) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + mutex_init(&sensor->lock); + + sensor->pdata = pdata; + + v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); + sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); + if (chip_version != MT9M032_CHIP_VERSION_VALUE) { + dev_err(&client->dev, "MT9M032 not detected, wrong version " + "0x%04x\n", chip_version); + ret = -ENODEV; + goto error_sensor; + } + + dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", + client->addr); + + sensor->frame_interval.numerator = 1; + sensor->frame_interval.denominator = 30; + + sensor->crop.left = MT9M032_COLUMN_START_DEF; + sensor->crop.top = MT9M032_ROW_START_DEF; + sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; + sensor->crop.height = MT9M032_ROW_SIZE_DEF; + + sensor->format.width = sensor->crop.width; + sensor->format.height = sensor->crop.height; + sensor->format.code = V4L2_MBUS_FMT_Y8_1X8; + sensor->format.field = V4L2_FIELD_NONE; + sensor->format.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_ctrl_handler_init(&sensor->ctrls, 5); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, + MT9M032_SHUTTER_WIDTH_MAX, 1, + MT9M032_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->pix_clock, + pdata->pix_clock, 1, pdata->pix_clock); + + if (sensor->ctrls.error) { + ret = sensor->ctrls.error; + dev_err(&client->dev, "control initialization error %d\n", ret); + goto error_ctrl; + } + + v4l2_ctrl_cluster(2, &sensor->hflip); + + sensor->subdev.ctrl_handler = &sensor->ctrls; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); + if (ret < 0) + goto error_ctrl; + + ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ + if (ret < 0) + goto error_entity; + mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ + if (ret < 0) + goto error_entity; + + ret = mt9m032_setup_pll(sensor); + if (ret < 0) + goto error_entity; + usleep_range(10000, 11000); + + ret = v4l2_ctrl_handler_setup(&sensor->ctrls); + if (ret < 0) + goto error_entity; + + /* SIZE */ + ret = mt9m032_update_geom_timing(sensor); + if (ret < 0) + goto error_entity; + + ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + if (sensor->pdata->invert_pixclock) { + ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, + MT9M032_PIX_CLK_CTRL_INV_PIXCLK); + if (ret < 0) + goto error_entity; + } + + ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = update_formatter2(sensor, false); + if (ret < 0) + goto error_entity; + + return ret; + +error_entity: + media_entity_cleanup(&sensor->subdev.entity); +error_ctrl: + v4l2_ctrl_handler_free(&sensor->ctrls); +error_sensor: + mutex_destroy(&sensor->lock); + kfree(sensor); + return ret; +} + +static int mt9m032_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9m032 *sensor = to_mt9m032(subdev); + + v4l2_device_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&sensor->ctrls); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&sensor->lock); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id mt9m032_id_table[] = { + { MT9M032_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); + +static struct i2c_driver mt9m032_i2c_driver = { + .driver = { + .name = MT9M032_NAME, + }, + .probe = mt9m032_probe, + .remove = mt9m032_remove, + .id_table = mt9m032_id_table, +}; + +module_i2c_driver(mt9m032_i2c_driver); + +MODULE_AUTHOR("Martin Hostettler "); +MODULE_DESCRIPTION("MT9M032 camera sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c new file mode 100644 index 000000000000..3be537ef22d2 --- /dev/null +++ b/drivers/media/i2c/mt9p031.c @@ -0,0 +1,1071 @@ +/* + * Driver for MT9P031 CMOS Image Sensor from Aptina + * + * Copyright (C) 2011, Laurent Pinchart + * Copyright (C) 2011, Javier Martin + * Copyright (C) 2011, Guennadi Liakhovetski + * + * Based on the MT9V032 driver and Bastian Hecht's code. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "aptina-pll.h" + +#define MT9P031_PIXEL_ARRAY_WIDTH 2752 +#define MT9P031_PIXEL_ARRAY_HEIGHT 2004 + +#define MT9P031_CHIP_VERSION 0x00 +#define MT9P031_CHIP_VERSION_VALUE 0x1801 +#define MT9P031_ROW_START 0x01 +#define MT9P031_ROW_START_MIN 0 +#define MT9P031_ROW_START_MAX 2004 +#define MT9P031_ROW_START_DEF 54 +#define MT9P031_COLUMN_START 0x02 +#define MT9P031_COLUMN_START_MIN 0 +#define MT9P031_COLUMN_START_MAX 2750 +#define MT9P031_COLUMN_START_DEF 16 +#define MT9P031_WINDOW_HEIGHT 0x03 +#define MT9P031_WINDOW_HEIGHT_MIN 2 +#define MT9P031_WINDOW_HEIGHT_MAX 2006 +#define MT9P031_WINDOW_HEIGHT_DEF 1944 +#define MT9P031_WINDOW_WIDTH 0x04 +#define MT9P031_WINDOW_WIDTH_MIN 2 +#define MT9P031_WINDOW_WIDTH_MAX 2752 +#define MT9P031_WINDOW_WIDTH_DEF 2592 +#define MT9P031_HORIZONTAL_BLANK 0x05 +#define MT9P031_HORIZONTAL_BLANK_MIN 0 +#define MT9P031_HORIZONTAL_BLANK_MAX 4095 +#define MT9P031_VERTICAL_BLANK 0x06 +#define MT9P031_VERTICAL_BLANK_MIN 0 +#define MT9P031_VERTICAL_BLANK_MAX 4095 +#define MT9P031_VERTICAL_BLANK_DEF 25 +#define MT9P031_OUTPUT_CONTROL 0x07 +#define MT9P031_OUTPUT_CONTROL_CEN 2 +#define MT9P031_OUTPUT_CONTROL_SYN 1 +#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 +#define MT9P031_SHUTTER_WIDTH_UPPER 0x08 +#define MT9P031_SHUTTER_WIDTH_LOWER 0x09 +#define MT9P031_SHUTTER_WIDTH_MIN 1 +#define MT9P031_SHUTTER_WIDTH_MAX 1048575 +#define MT9P031_SHUTTER_WIDTH_DEF 1943 +#define MT9P031_PLL_CONTROL 0x10 +#define MT9P031_PLL_CONTROL_PWROFF 0x0050 +#define MT9P031_PLL_CONTROL_PWRON 0x0051 +#define MT9P031_PLL_CONTROL_USEPLL 0x0052 +#define MT9P031_PLL_CONFIG_1 0x11 +#define MT9P031_PLL_CONFIG_2 0x12 +#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a +#define MT9P031_FRAME_RESTART 0x0b +#define MT9P031_SHUTTER_DELAY 0x0c +#define MT9P031_RST 0x0d +#define MT9P031_RST_ENABLE 1 +#define MT9P031_RST_DISABLE 0 +#define MT9P031_READ_MODE_1 0x1e +#define MT9P031_READ_MODE_2 0x20 +#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) +#define MT9P031_READ_MODE_2_COL_MIR (1 << 14) +#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) +#define MT9P031_ROW_ADDRESS_MODE 0x22 +#define MT9P031_COLUMN_ADDRESS_MODE 0x23 +#define MT9P031_GLOBAL_GAIN 0x35 +#define MT9P031_GLOBAL_GAIN_MIN 8 +#define MT9P031_GLOBAL_GAIN_MAX 1024 +#define MT9P031_GLOBAL_GAIN_DEF 8 +#define MT9P031_GLOBAL_GAIN_MULT (1 << 6) +#define MT9P031_ROW_BLACK_TARGET 0x49 +#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b +#define MT9P031_GREEN1_OFFSET 0x60 +#define MT9P031_GREEN2_OFFSET 0x61 +#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9P031_BLC_MANUAL_BLC (1 << 0) +#define MT9P031_RED_OFFSET 0x63 +#define MT9P031_BLUE_OFFSET 0x64 +#define MT9P031_TEST_PATTERN 0xa0 +#define MT9P031_TEST_PATTERN_SHIFT 3 +#define MT9P031_TEST_PATTERN_ENABLE (1 << 0) +#define MT9P031_TEST_PATTERN_DISABLE (0 << 0) +#define MT9P031_TEST_PATTERN_GREEN 0xa1 +#define MT9P031_TEST_PATTERN_RED 0xa2 +#define MT9P031_TEST_PATTERN_BLUE 0xa3 + +enum mt9p031_model { + MT9P031_MODEL_COLOR, + MT9P031_MODEL_MONOCHROME, +}; + +struct mt9p031 { + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_rect crop; /* Sensor window */ + struct v4l2_mbus_framefmt format; + struct mt9p031_platform_data *pdata; + struct mutex power_lock; /* lock to protect power_count */ + int power_count; + + enum mt9p031_model model; + struct aptina_pll pll; + int reset; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *blc_auto; + struct v4l2_ctrl *blc_offset; + + /* Registers cache */ + u16 output_control; + u16 mode2; +}; + +static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9p031, subdev); +} + +static int mt9p031_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, + u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 value = (mt9p031->output_control & ~clear) | set; + int ret; + + ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); + if (ret < 0) + return ret; + + mt9p031->output_control = value; + return 0; +} + +static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 value = (mt9p031->mode2 & ~clear) | set; + int ret; + + ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); + if (ret < 0) + return ret; + + mt9p031->mode2 = value; + return 0; +} + +static int mt9p031_reset(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + /* Disable chip output, synchronous option update */ + ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); + if (ret < 0) + return ret; + + return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, + 0); +} + +static int mt9p031_pll_setup(struct mt9p031 *mt9p031) +{ + static const struct aptina_pll_limits limits = { + .ext_clock_min = 6000000, + .ext_clock_max = 27000000, + .int_clock_min = 2000000, + .int_clock_max = 13500000, + .out_clock_min = 180000000, + .out_clock_max = 360000000, + .pix_clock_max = 96000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + struct mt9p031_platform_data *pdata = mt9p031->pdata; + + mt9p031->pll.ext_clock = pdata->ext_freq; + mt9p031->pll.pix_clock = pdata->target_freq; + + return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); +} + +static int mt9p031_pll_enable(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, + (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + ret = mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON | + MT9P031_PLL_CONTROL_USEPLL); + return ret; +} + +static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + + return mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWROFF); +} + +static int mt9p031_power_on(struct mt9p031 *mt9p031) +{ + /* Ensure RESET_BAR is low */ + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); + usleep_range(1000, 2000); + } + + /* Emable clock */ + if (mt9p031->pdata->set_xclk) + mt9p031->pdata->set_xclk(&mt9p031->subdev, + mt9p031->pdata->ext_freq); + + /* Now RESET_BAR must be high */ + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 1); + usleep_range(1000, 2000); + } + + return 0; +} + +static void mt9p031_power_off(struct mt9p031 *mt9p031) +{ + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); + usleep_range(1000, 2000); + } + + if (mt9p031->pdata->set_xclk) + mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); +} + +static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + if (!on) { + mt9p031_power_off(mt9p031); + return 0; + } + + ret = mt9p031_power_on(mt9p031); + if (ret < 0) + return ret; + + ret = mt9p031_reset(mt9p031); + if (ret < 0) { + dev_err(&client->dev, "Failed to reset the camera\n"); + return ret; + } + + return v4l2_ctrl_handler_setup(&mt9p031->ctrls); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int mt9p031_set_params(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + struct v4l2_mbus_framefmt *format = &mt9p031->format; + const struct v4l2_rect *crop = &mt9p031->crop; + unsigned int hblank; + unsigned int vblank; + unsigned int xskip; + unsigned int yskip; + unsigned int xbin; + unsigned int ybin; + int ret; + + /* Windows position and size. + * + * TODO: Make sure the start coordinates and window size match the + * skipping, binning and mirroring (see description of registers 2 and 4 + * in table 13, and Binning section on page 41). + */ + ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); + if (ret < 0) + return ret; + + /* Row and column binning and skipping. Use the maximum binning value + * compatible with the skipping settings. + */ + xskip = DIV_ROUND_CLOSEST(crop->width, format->width); + yskip = DIV_ROUND_CLOSEST(crop->height, format->height); + xbin = 1 << (ffs(xskip) - 1); + ybin = 1 << (ffs(yskip) - 1); + + ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (xskip - 1)); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (yskip - 1)); + if (ret < 0) + return ret; + + /* Blanking - use minimum value for horizontal blanking and default + * value for vertical blanking. + */ + hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3)); + vblank = MT9P031_VERTICAL_BLANK_DEF; + + ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank); + if (ret < 0) + return ret; + + return ret; +} + +static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + int ret; + + if (!enable) { + /* Stop sensor readout */ + ret = mt9p031_set_output_control(mt9p031, + MT9P031_OUTPUT_CONTROL_CEN, 0); + if (ret < 0) + return ret; + + return mt9p031_pll_disable(mt9p031); + } + + ret = mt9p031_set_params(mt9p031); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + ret = mt9p031_set_output_control(mt9p031, 0, + MT9P031_OUTPUT_CONTROL_CEN); + if (ret < 0) + return ret; + + return mt9p031_pll_enable(mt9p031); +} + +static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + if (code->pad || code->index) + return -EINVAL; + + code->code = mt9p031->format.code; + return 0; +} + +static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + if (fse->index >= 8 || fse->code != mt9p031->format.code) + return -EINVAL; + + fse->min_width = MT9P031_WINDOW_WIDTH_DEF + / min_t(unsigned int, 7, fse->index + 1); + fse->max_width = fse->min_width; + fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); + fse->max_height = fse->min_height; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9p031->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9p031->crop; + default: + return NULL; + } +} + +static int mt9p031_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, + fmt->which); + return 0; +} + +static int mt9p031_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9p031_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9p031_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, + MT9P031_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, + MT9P031_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + MT9P031_WINDOW_WIDTH_MIN, + MT9P031_WINDOW_WIDTH_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), + MT9P031_WINDOW_HEIGHT_MIN, + MT9P031_WINDOW_HEIGHT_MAX); + + rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) +#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) + +static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9p031 *mt9p031 = + container_of(ctrl->handler, struct mt9p031, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 data; + int ret; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, + (ctrl->val >> 16) & 0xffff); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, + ctrl->val & 0xffff); + + case V4L2_CID_GAIN: + /* Gain is controlled by 2 analog stages and a digital stage. + * Valid values for the 3 stages are + * + * Stage Min Max Step + * ------------------------------------------ + * First analog stage x1 x2 1 + * Second analog stage x1 x4 0.125 + * Digital stage x1 x16 0.125 + * + * To minimize noise, the gain stages should be used in the + * second analog stage, first analog stage, digital stage order. + * Gain from a previous stage should be pushed to its maximum + * value before the next stage is used. + */ + if (ctrl->val <= 32) { + data = ctrl->val; + } else if (ctrl->val <= 64) { + ctrl->val &= ~1; + data = (1 << 6) | (ctrl->val >> 1); + } else { + ctrl->val &= ~7; + data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; + } + + return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); + + case V4L2_CID_HFLIP: + if (ctrl->val) + return mt9p031_set_mode2(mt9p031, + 0, MT9P031_READ_MODE_2_COL_MIR); + else + return mt9p031_set_mode2(mt9p031, + MT9P031_READ_MODE_2_COL_MIR, 0); + + case V4L2_CID_VFLIP: + if (ctrl->val) + return mt9p031_set_mode2(mt9p031, + 0, MT9P031_READ_MODE_2_ROW_MIR); + else + return mt9p031_set_mode2(mt9p031, + MT9P031_READ_MODE_2_ROW_MIR, 0); + + case V4L2_CID_TEST_PATTERN: + if (!ctrl->val) { + /* Restore the black level compensation settings. */ + if (mt9p031->blc_auto->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_auto); + if (ret < 0) + return ret; + } + if (mt9p031->blc_offset->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_offset); + if (ret < 0) + return ret; + } + return mt9p031_write(client, MT9P031_TEST_PATTERN, + MT9P031_TEST_PATTERN_DISABLE); + } + + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); + if (ret < 0) + return ret; + + /* Disable digital black level compensation when using a test + * pattern. + */ + ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, + 0); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_TEST_PATTERN, + ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) + | MT9P031_TEST_PATTERN_ENABLE); + + case V4L2_CID_BLC_AUTO: + ret = mt9p031_set_mode2(mt9p031, + ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, + ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, + ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); + + case V4L2_CID_BLC_TARGET_LEVEL: + return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, + ctrl->val); + + case V4L2_CID_BLC_ANALOG_OFFSET: + data = ctrl->val & ((1 << 9) - 1); + + ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); + if (ret < 0) + return ret; + return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); + + case V4L2_CID_BLC_DIGITAL_OFFSET: + return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, + ctrl->val & ((1 << 12) - 1)); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9p031_ctrl_ops = { + .s_ctrl = mt9p031_s_ctrl, +}; + +static const char * const mt9p031_test_pattern_menu[] = { + "Disabled", + "Color Field", + "Horizontal Gradient", + "Vertical Gradient", + "Diagonal Gradient", + "Classic Test Pattern", + "Walking 1s", + "Monochrome Horizontal Bars", + "Monochrome Vertical Bars", + "Vertical Color Bars", +}; + +static const struct v4l2_ctrl_config mt9p031_ctrls[] = { + { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Test Pattern", + .min = 0, + .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, + .step = 0, + .def = 0, + .flags = 0, + .menu_skip_mask = 0, + .qmenu = mt9p031_test_pattern_menu, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "BLC, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_TARGET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Target Level", + .min = 0, + .max = 4095, + .step = 1, + .def = 168, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_ANALOG_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Analog Offset", + .min = -255, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_DIGITAL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Digital Offset", + .min = -2048, + .max = 2047, + .step = 1, + .def = 40, + .flags = 0, + } +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + int ret = 0; + + mutex_lock(&mt9p031->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (mt9p031->power_count == !on) { + ret = __mt9p031_set_power(mt9p031, !!on); + if (ret < 0) + goto out; + } + + /* Update the power count. */ + mt9p031->power_count += on ? 1 : -1; + WARN_ON(mt9p031->power_count < 0); + +out: + mutex_unlock(&mt9p031->power_lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9p031_registered(struct v4l2_subdev *subdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + s32 data; + int ret; + + ret = mt9p031_power_on(mt9p031); + if (ret < 0) { + dev_err(&client->dev, "MT9P031 power up failed\n"); + return ret; + } + + /* Read out the chip version register */ + data = mt9p031_read(client, MT9P031_CHIP_VERSION); + if (data != MT9P031_CHIP_VERSION_VALUE) { + dev_err(&client->dev, "MT9P031 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + mt9p031_power_off(mt9p031); + + dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9P031_COLUMN_START_DEF; + crop->top = MT9P031_ROW_START_DEF; + crop->width = MT9P031_WINDOW_WIDTH_DEF; + crop->height = MT9P031_WINDOW_HEIGHT_DEF; + + format = v4l2_subdev_get_try_format(fh, 0); + + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) + format->code = V4L2_MBUS_FMT_Y12_1X12; + else + format->code = V4L2_MBUS_FMT_SGRBG12_1X12; + + format->width = MT9P031_WINDOW_WIDTH_DEF; + format->height = MT9P031_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return mt9p031_set_power(subdev, 1); +} + +static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return mt9p031_set_power(subdev, 0); +} + +static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { + .s_power = mt9p031_set_power, +}; + +static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { + .s_stream = mt9p031_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { + .enum_mbus_code = mt9p031_enum_mbus_code, + .enum_frame_size = mt9p031_enum_frame_size, + .get_fmt = mt9p031_get_format, + .set_fmt = mt9p031_set_format, + .get_crop = mt9p031_get_crop, + .set_crop = mt9p031_set_crop, +}; + +static struct v4l2_subdev_ops mt9p031_subdev_ops = { + .core = &mt9p031_subdev_core_ops, + .video = &mt9p031_subdev_video_ops, + .pad = &mt9p031_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { + .registered = mt9p031_registered, + .open = mt9p031_open, + .close = mt9p031_close, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9p031_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9p031_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct mt9p031 *mt9p031; + unsigned int i; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); + if (mt9p031 == NULL) + return -ENOMEM; + + mt9p031->pdata = pdata; + mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; + mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; + mt9p031->model = did->driver_data; + mt9p031->reset = -1; + + v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5); + + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, + MT9P031_SHUTTER_WIDTH_MAX, 1, + MT9P031_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, + MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->target_freq, + pdata->target_freq, 1, pdata->target_freq); + + for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); + + mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; + + if (mt9p031->ctrls.error) { + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9p031->ctrls.error); + ret = mt9p031->ctrls.error; + goto done; + } + + mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); + mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, + V4L2_CID_BLC_DIGITAL_OFFSET); + + mutex_init(&mt9p031->power_lock); + v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); + mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; + + mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); + if (ret < 0) + goto done; + + mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; + mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; + mt9p031->crop.left = MT9P031_COLUMN_START_DEF; + mt9p031->crop.top = MT9P031_ROW_START_DEF; + + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) + mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; + else + mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + + mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; + mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; + mt9p031->format.field = V4L2_FIELD_NONE; + mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; + + if (pdata->reset != -1) { + ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, + "mt9p031_rst"); + if (ret < 0) + goto done; + + mt9p031->reset = pdata->reset; + } + + ret = mt9p031_pll_setup(mt9p031); + +done: + if (ret < 0) { + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); + + v4l2_ctrl_handler_free(&mt9p031->ctrls); + media_entity_cleanup(&mt9p031->subdev.entity); + kfree(mt9p031); + } + + return ret; +} + +static int mt9p031_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + v4l2_ctrl_handler_free(&mt9p031->ctrls); + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); + kfree(mt9p031); + + return 0; +} + +static const struct i2c_device_id mt9p031_id[] = { + { "mt9p031", MT9P031_MODEL_COLOR }, + { "mt9p031m", MT9P031_MODEL_MONOCHROME }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9p031_id); + +static struct i2c_driver mt9p031_i2c_driver = { + .driver = { + .name = "mt9p031", + }, + .probe = mt9p031_probe, + .remove = mt9p031_remove, + .id_table = mt9p031_id, +}; + +module_i2c_driver(mt9p031_i2c_driver); + +MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); +MODULE_AUTHOR("Bastian Hecht "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c new file mode 100644 index 000000000000..6d343adf891d --- /dev/null +++ b/drivers/media/i2c/mt9t001.c @@ -0,0 +1,833 @@ +/* + * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) + * + * Copyright (C) 2010-2011, Laurent Pinchart + * + * Based on the MT9M001 driver, + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MT9T001_PIXEL_ARRAY_HEIGHT 1568 +#define MT9T001_PIXEL_ARRAY_WIDTH 2112 + +#define MT9T001_CHIP_VERSION 0x00 +#define MT9T001_CHIP_ID 0x1621 +#define MT9T001_ROW_START 0x01 +#define MT9T001_ROW_START_MIN 0 +#define MT9T001_ROW_START_DEF 20 +#define MT9T001_ROW_START_MAX 1534 +#define MT9T001_COLUMN_START 0x02 +#define MT9T001_COLUMN_START_MIN 0 +#define MT9T001_COLUMN_START_DEF 32 +#define MT9T001_COLUMN_START_MAX 2046 +#define MT9T001_WINDOW_HEIGHT 0x03 +#define MT9T001_WINDOW_HEIGHT_MIN 1 +#define MT9T001_WINDOW_HEIGHT_DEF 1535 +#define MT9T001_WINDOW_HEIGHT_MAX 1567 +#define MT9T001_WINDOW_WIDTH 0x04 +#define MT9T001_WINDOW_WIDTH_MIN 1 +#define MT9T001_WINDOW_WIDTH_DEF 2047 +#define MT9T001_WINDOW_WIDTH_MAX 2111 +#define MT9T001_HORIZONTAL_BLANKING 0x05 +#define MT9T001_HORIZONTAL_BLANKING_MIN 21 +#define MT9T001_HORIZONTAL_BLANKING_MAX 1023 +#define MT9T001_VERTICAL_BLANKING 0x06 +#define MT9T001_VERTICAL_BLANKING_MIN 3 +#define MT9T001_VERTICAL_BLANKING_MAX 1023 +#define MT9T001_OUTPUT_CONTROL 0x07 +#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) +#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) +#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) +#define MT9T001_SHUTTER_WIDTH_HIGH 0x08 +#define MT9T001_SHUTTER_WIDTH_LOW 0x09 +#define MT9T001_SHUTTER_WIDTH_MIN 1 +#define MT9T001_SHUTTER_WIDTH_DEF 1561 +#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) +#define MT9T001_PIXEL_CLOCK 0x0a +#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) +#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) +#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 +#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) +#define MT9T001_FRAME_RESTART 0x0b +#define MT9T001_SHUTTER_DELAY 0x0c +#define MT9T001_SHUTTER_DELAY_MAX 2047 +#define MT9T001_RESET 0x0d +#define MT9T001_READ_MODE1 0x1e +#define MT9T001_READ_MODE_SNAPSHOT (1 << 8) +#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) +#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) +#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) +#define MT9T001_READ_MODE2 0x20 +#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) +#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) +#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) +#define MT9T001_READ_MODE3 0x21 +#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) +#define MT9T001_READ_MODE_GHST_CTL (1 << 1) +#define MT9T001_ROW_ADDRESS_MODE 0x22 +#define MT9T001_ROW_SKIP_MASK (7 << 0) +#define MT9T001_ROW_BIN_MASK (3 << 3) +#define MT9T001_ROW_BIN_SHIFT 3 +#define MT9T001_COLUMN_ADDRESS_MODE 0x23 +#define MT9T001_COLUMN_SKIP_MASK (7 << 0) +#define MT9T001_COLUMN_BIN_MASK (3 << 3) +#define MT9T001_COLUMN_BIN_SHIFT 3 +#define MT9T001_GREEN1_GAIN 0x2b +#define MT9T001_BLUE_GAIN 0x2c +#define MT9T001_RED_GAIN 0x2d +#define MT9T001_GREEN2_GAIN 0x2e +#define MT9T001_TEST_DATA 0x32 +#define MT9T001_GLOBAL_GAIN 0x35 +#define MT9T001_GLOBAL_GAIN_MIN 8 +#define MT9T001_GLOBAL_GAIN_MAX 1024 +#define MT9T001_BLACK_LEVEL 0x49 +#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b +#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d +#define MT9T001_CAL_THRESHOLDS 0x5f +#define MT9T001_GREEN1_OFFSET 0x60 +#define MT9T001_GREEN2_OFFSET 0x61 +#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) +#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) +#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) +#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) +#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) +#define MT9T001_RED_OFFSET 0x63 +#define MT9T001_BLUE_OFFSET 0x64 + +struct mt9t001 { + struct v4l2_subdev subdev; + struct media_pad pad; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *gains[4]; + + u16 output_control; + u16 black_level; +}; + +static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9t001, subdev); +} + +static int mt9t001_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, + u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); + u16 value = (mt9t001->output_control & ~clear) | set; + int ret; + + if (value == mt9t001->output_control) + return 0; + + ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); + if (ret < 0) + return ret; + + mt9t001->output_control = value; + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static struct v4l2_mbus_framefmt * +__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9t001->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9t001->crop; + default: + return NULL; + } +} + +static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *format = &mt9t001->format; + struct v4l2_rect *crop = &mt9t001->crop; + unsigned int hratio; + unsigned int vratio; + int ret; + + if (!enable) + return mt9t001_set_output_control(mt9t001, mode, 0); + + /* Configure the window size and row/column bin */ + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); + + ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + return mt9t001_set_output_control(mt9t001, 0, mode); +} + +static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; + return 0; +} + +static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; + fse->max_width = fse->min_width; + fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; + fse->max_height = fse->min_height; + + return 0; +} + +static int mt9t001_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad, + format->which); + return 0; +} + +static int mt9t001_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9t001_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9t001_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), + MT9T001_COLUMN_START_MIN, + MT9T001_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), + MT9T001_ROW_START_MIN, + MT9T001_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + MT9T001_WINDOW_WIDTH_MIN + 1, + MT9T001_WINDOW_WIDTH_MAX + 1); + rect.height = clamp(ALIGN(crop->rect.height, 2), + MT9T001_WINDOW_HEIGHT_MIN + 1, + MT9T001_WINDOW_HEIGHT_MAX + 1); + + rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) + +#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) +#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) +#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) +#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) + +static u16 mt9t001_gain_value(s32 *gain) +{ + /* Gain is controlled by 2 analog stages and a digital stage. Valid + * values for the 3 stages are + * + * Stage Min Max Step + * ------------------------------------------ + * First analog stage x1 x2 1 + * Second analog stage x1 x4 0.125 + * Digital stage x1 x16 0.125 + * + * To minimize noise, the gain stages should be used in the second + * analog stage, first analog stage, digital stage order. Gain from a + * previous stage should be pushed to its maximum value before the next + * stage is used. + */ + if (*gain <= 32) + return *gain; + + if (*gain <= 64) { + *gain &= ~1; + return (1 << 6) | (*gain >> 1); + } + + *gain &= ~7; + return ((*gain - 64) << 5) | (1 << 6) | 32; +} + +static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) +{ + return mt9t001_set_output_control(mt9t001, + freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, + freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); +} + +static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const u8 gains[4] = { + MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, + MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN + }; + + struct mt9t001 *mt9t001 = + container_of(ctrl->handler, struct mt9t001, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); + unsigned int count; + unsigned int i; + u16 value; + int ret; + + switch (ctrl->id) { + case V4L2_CID_GAIN_RED: + case V4L2_CID_GAIN_GREEN_RED: + case V4L2_CID_GAIN_GREEN_BLUE: + case V4L2_CID_GAIN_BLUE: + + /* Disable control updates if more than one control has changed + * in the cluster. + */ + for (i = 0, count = 0; i < 4; ++i) { + struct v4l2_ctrl *gain = mt9t001->gains[i]; + + if (gain->val != gain->cur.val) + count++; + } + + if (count > 1) { + ret = mt9t001_ctrl_freeze(mt9t001, true); + if (ret < 0) + return ret; + } + + /* Update the gain controls. */ + for (i = 0; i < 4; ++i) { + struct v4l2_ctrl *gain = mt9t001->gains[i]; + + if (gain->val == gain->cur.val) + continue; + + value = mt9t001_gain_value(&gain->val); + ret = mt9t001_write(client, gains[i], value); + if (ret < 0) { + mt9t001_ctrl_freeze(mt9t001, false); + return ret; + } + } + + /* Enable control updates. */ + if (count > 1) { + ret = mt9t001_ctrl_freeze(mt9t001, false); + if (ret < 0) + return ret; + } + + break; + + case V4L2_CID_EXPOSURE: + ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, + ctrl->val & 0xffff); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, + ctrl->val >> 16); + + case V4L2_CID_TEST_PATTERN: + ret = mt9t001_set_output_control(mt9t001, + ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, + ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); + + case V4L2_CID_BLACK_LEVEL_AUTO: + value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; + ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, + value); + if (ret < 0) + return ret; + + mt9t001->black_level = value; + break; + + case V4L2_CID_BLACK_LEVEL_OFFSET: + ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); + + case V4L2_CID_BLACK_LEVEL_CALIBRATE: + return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, + MT9T001_BLACK_LEVEL_RECALCULATE | + mt9t001->black_level); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9t001_ctrl_ops = { + .s_ctrl = mt9t001_s_ctrl, +}; + +static const struct v4l2_ctrl_config mt9t001_ctrls[] = { + { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test pattern", + .min = 0, + .max = 1023, + .step = 1, + .def = 0, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Black Level, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Black Level, Offset", + .min = -256, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Black Level, Calibrate", + .min = 0, + .max = 0, + .step = 0, + .def = 0, + .flags = V4L2_CTRL_FLAG_WRITE_ONLY, + }, +}; + +static const struct v4l2_ctrl_config mt9t001_gains[] = { + { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_RED, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_GREEN_RED, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green (R)", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_GREEN_BLUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green (B)", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_BLUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9T001_COLUMN_START_DEF; + crop->top = MT9T001_ROW_START_DEF; + crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; + crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; + + format = v4l2_subdev_get_try_format(fh, 0); + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + format->width = MT9T001_WINDOW_WIDTH_DEF + 1; + format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { + .s_stream = mt9t001_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { + .enum_mbus_code = mt9t001_enum_mbus_code, + .enum_frame_size = mt9t001_enum_frame_size, + .get_fmt = mt9t001_get_format, + .set_fmt = mt9t001_set_format, + .get_crop = mt9t001_get_crop, + .set_crop = mt9t001_set_crop, +}; + +static struct v4l2_subdev_ops mt9t001_subdev_ops = { + .video = &mt9t001_subdev_video_ops, + .pad = &mt9t001_subdev_pad_ops, +}; + +static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { + .open = mt9t001_open, +}; + +static int mt9t001_video_probe(struct i2c_client *client) +{ + struct mt9t001_platform_data *pdata = client->dev.platform_data; + s32 data; + int ret; + + dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", + client->addr); + + /* Reset the chip and stop data read out */ + ret = mt9t001_write(client, MT9T001_RESET, 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_RESET, 0); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); + if (ret < 0) + return ret; + + /* Configure the pixel clock polarity */ + if (pdata->clk_pol) { + ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, + MT9T001_PIXEL_CLOCK_INVERT); + if (ret < 0) + return ret; + } + + /* Read and check the sensor version */ + data = mt9t001_read(client, MT9T001_CHIP_VERSION); + if (data != MT9T001_CHIP_ID) { + dev_err(&client->dev, "MT9T001 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9t001_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9t001_platform_data *pdata = client->dev.platform_data; + struct mt9t001 *mt9t001; + unsigned int i; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + ret = mt9t001_video_probe(client); + if (ret < 0) + return ret; + + mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL); + if (!mt9t001) + return -ENOMEM; + + v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + + ARRAY_SIZE(mt9t001_gains) + 3); + + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, + MT9T001_SHUTTER_WIDTH_MAX, 1, + MT9T001_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, + 1, pdata->ext_clk); + + for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); + + for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) + mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, + &mt9t001_gains[i], NULL); + + v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); + + mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; + + if (mt9t001->ctrls.error) { + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9t001->ctrls.error); + ret = -EINVAL; + goto done; + } + + mt9t001->crop.left = MT9T001_COLUMN_START_DEF; + mt9t001->crop.top = MT9T001_ROW_START_DEF; + mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; + mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; + + mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; + mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; + mt9t001->format.field = V4L2_FIELD_NONE; + mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); + mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; + mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); + +done: + if (ret < 0) { + v4l2_ctrl_handler_free(&mt9t001->ctrls); + media_entity_cleanup(&mt9t001->subdev.entity); + kfree(mt9t001); + } + + return ret; +} + +static int mt9t001_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + v4l2_ctrl_handler_free(&mt9t001->ctrls); + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + kfree(mt9t001); + return 0; +} + +static const struct i2c_device_id mt9t001_id[] = { + { "mt9t001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9t001_id); + +static struct i2c_driver mt9t001_driver = { + .driver = { + .name = "mt9t001", + }, + .probe = mt9t001_probe, + .remove = mt9t001_remove, + .id_table = mt9t001_id, +}; + +module_i2c_driver(mt9t001_driver); + +MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c new file mode 100644 index 000000000000..6bf01ad62765 --- /dev/null +++ b/drivers/media/i2c/mt9v011.c @@ -0,0 +1,712 @@ +/* + * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor + * + * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) + * This code is placed under the terms of the GNU General Public License v2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +#define R00_MT9V011_CHIP_VERSION 0x00 +#define R01_MT9V011_ROWSTART 0x01 +#define R02_MT9V011_COLSTART 0x02 +#define R03_MT9V011_HEIGHT 0x03 +#define R04_MT9V011_WIDTH 0x04 +#define R05_MT9V011_HBLANK 0x05 +#define R06_MT9V011_VBLANK 0x06 +#define R07_MT9V011_OUT_CTRL 0x07 +#define R09_MT9V011_SHUTTER_WIDTH 0x09 +#define R0A_MT9V011_CLK_SPEED 0x0a +#define R0B_MT9V011_RESTART 0x0b +#define R0C_MT9V011_SHUTTER_DELAY 0x0c +#define R0D_MT9V011_RESET 0x0d +#define R1E_MT9V011_DIGITAL_ZOOM 0x1e +#define R20_MT9V011_READ_MODE 0x20 +#define R2B_MT9V011_GREEN_1_GAIN 0x2b +#define R2C_MT9V011_BLUE_GAIN 0x2c +#define R2D_MT9V011_RED_GAIN 0x2d +#define R2E_MT9V011_GREEN_2_GAIN 0x2e +#define R35_MT9V011_GLOBAL_GAIN 0x35 +#define RF1_MT9V011_CHIP_ENABLE 0xf1 + +#define MT9V011_VERSION 0x8232 +#define MT9V011_REV_B_VERSION 0x8243 + +/* supported controls */ +static struct v4l2_queryctrl mt9v011_qctrl[] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = (1 << 12) - 1 - 0x0020, + .step = 1, + .default_value = 0x0020, + .flags = 0, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 2047, + .step = 1, + .default_value = 0x01fc, + .flags = 0, + }, { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = -1 << 9, + .maximum = (1 << 9) - 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = -1 << 9, + .maximum = (1 << 9) - 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + } +}; + +struct mt9v011 { + struct v4l2_subdev sd; + unsigned width, height; + unsigned xtal; + unsigned hflip:1; + unsigned vflip:1; + + u16 global_gain, exposure; + s16 red_bal, blue_bal; +}; + +static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9v011, sd); +} + +static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + __be16 buffer; + int rc, val; + + rc = i2c_master_send(c, &addr, 1); + if (rc != 1) + v4l2_dbg(0, debug, sd, + "i2c i/o error: rc == %d (should be 1)\n", rc); + + msleep(10); + + rc = i2c_master_recv(c, (char *)&buffer, 2); + if (rc != 2) + v4l2_dbg(0, debug, sd, + "i2c i/o error: rc == %d (should be 2)\n", rc); + + val = be16_to_cpu(buffer); + + v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val); + + return val; +} + +static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr, + u16 value) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[3]; + int rc; + + buffer[0] = addr; + buffer[1] = value >> 8; + buffer[2] = value & 0xff; + + v4l2_dbg(2, debug, sd, + "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value); + rc = i2c_master_send(c, buffer, 3); + if (rc != 3) + v4l2_dbg(0, debug, sd, + "i2c i/o error: rc == %d (should be 3)\n", rc); +} + + +struct i2c_reg_value { + unsigned char reg; + u16 value; +}; + +/* + * Values used at the original driver + * Some values are marked as Reserved at the datasheet + */ +static const struct i2c_reg_value mt9v011_init_default[] = { + { R0D_MT9V011_RESET, 0x0001 }, + { R0D_MT9V011_RESET, 0x0000 }, + + { R0C_MT9V011_SHUTTER_DELAY, 0x0000 }, + { R09_MT9V011_SHUTTER_WIDTH, 0x1fc }, + + { R0A_MT9V011_CLK_SPEED, 0x0000 }, + { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, + + { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ +}; + + +static u16 calc_mt9v011_gain(s16 lineargain) +{ + + u16 digitalgain = 0; + u16 analogmult = 0; + u16 analoginit = 0; + + if (lineargain < 0) + lineargain = 0; + + /* recommended minimum */ + lineargain += 0x0020; + + if (lineargain > 2047) + lineargain = 2047; + + if (lineargain > 1023) { + digitalgain = 3; + analogmult = 3; + analoginit = lineargain / 16; + } else if (lineargain > 511) { + digitalgain = 1; + analogmult = 3; + analoginit = lineargain / 8; + } else if (lineargain > 255) { + analogmult = 3; + analoginit = lineargain / 4; + } else if (lineargain > 127) { + analogmult = 1; + analoginit = lineargain / 2; + } else + analoginit = lineargain; + + return analoginit + (analogmult << 7) + (digitalgain << 9); + +} + +static void set_balance(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + u16 green_gain, blue_gain, red_gain; + u16 exposure; + s16 bal; + + exposure = core->exposure; + + green_gain = calc_mt9v011_gain(core->global_gain); + + bal = core->global_gain; + bal += (core->blue_bal * core->global_gain / (1 << 7)); + blue_gain = calc_mt9v011_gain(bal); + + bal = core->global_gain; + bal += (core->red_bal * core->global_gain / (1 << 7)); + red_gain = calc_mt9v011_gain(bal); + + mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); + mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); + mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); + mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); + mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); +} + +static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank, speed; + unsigned row_time, t_time; + u64 frames_per_ms; + unsigned tmp; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); + + row_time = (width + 113 + hblank) * (speed + 2); + t_time = row_time * (height + vblank + 1); + + frames_per_ms = core->xtal * 1000l; + do_div(frames_per_ms, t_time); + tmp = frames_per_ms; + + v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", + tmp / 1000, tmp % 1000, t_time); + + if (numerator && denominator) { + *numerator = 1000; + *denominator = (u32)frames_per_ms; + } +} + +static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank; + unsigned row_time, line_time; + u64 t_time, speed; + + /* Avoid bogus calculus */ + if (!numerator || !denominator) + return 0; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + + row_time = width + 113 + hblank; + line_time = height + vblank + 1; + + t_time = core->xtal * ((u64)numerator); + /* round to the closest value */ + t_time += denominator / 2; + do_div(t_time, denominator); + + speed = t_time; + do_div(speed, row_time * line_time); + + /* Avoid having a negative value for speed */ + if (speed < 2) + speed = 0; + else + speed -= 2; + + /* Avoid speed overflow */ + if (speed > 15) + return 15; + + return (u16)speed; +} + +static void set_res(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned vstart, hstart; + + /* + * The mt9v011 doesn't have scaling. So, in order to select the desired + * resolution, we're cropping at the middle of the sensor. + * hblank and vblank should be adjusted, in order to warrant that + * we'll preserve the line timings for 30 fps, no matter what resolution + * is selected. + * NOTE: datasheet says that width (and height) should be filled with + * width-1. However, this doesn't work, since one pixel per line will + * be missing. + */ + + hstart = 20 + (640 - core->width) / 2; + mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); + mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); + mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); + + vstart = 8 + (480 - core->height) / 2; + mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); + mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); + mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); + + calc_fps(sd, NULL, NULL); +}; + +static void set_read_mode(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned mode = 0x1000; + + if (core->hflip) + mode |= 0x4000; + + if (core->vflip) + mode |= 0x8000; + + mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); +} + +static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++) + mt9v011_write(sd, mt9v011_init_default[i].reg, + mt9v011_init_default[i].value); + + set_balance(sd); + set_res(sd); + set_read_mode(sd); + + return 0; +}; + +static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct mt9v011 *core = to_mt9v011(sd); + + v4l2_dbg(1, debug, sd, "g_ctrl called\n"); + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = core->global_gain; + return 0; + case V4L2_CID_EXPOSURE: + ctrl->value = core->exposure; + return 0; + case V4L2_CID_RED_BALANCE: + ctrl->value = core->red_bal; + return 0; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = core->blue_bal; + return 0; + case V4L2_CID_HFLIP: + ctrl->value = core->hflip ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + ctrl->value = core->vflip ? 1 : 0; + return 0; + } + return -EINVAL; +} + +static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; + + v4l2_dbg(1, debug, sd, "queryctrl called\n"); + + for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) + if (qc->id && qc->id == mt9v011_qctrl[i].id) { + memcpy(qc, &(mt9v011_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + + +static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct mt9v011 *core = to_mt9v011(sd); + u8 i, n; + n = ARRAY_SIZE(mt9v011_qctrl); + + for (i = 0; i < n; i++) { + if (ctrl->id != mt9v011_qctrl[i].id) + continue; + if (ctrl->value < mt9v011_qctrl[i].minimum || + ctrl->value > mt9v011_qctrl[i].maximum) + return -ERANGE; + v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", + ctrl->id, ctrl->value); + break; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + core->global_gain = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + core->exposure = ctrl->value; + break; + case V4L2_CID_RED_BALANCE: + core->red_bal = ctrl->value; + break; + case V4L2_CID_BLUE_BALANCE: + core->blue_bal = ctrl->value; + break; + case V4L2_CID_HFLIP: + core->hflip = ctrl->value; + set_read_mode(sd); + return 0; + case V4L2_CID_VFLIP: + core->vflip = ctrl->value; + set_read_mode(sd); + return 0; + default: + return -EINVAL; + } + + set_balance(sd); + + return 0; +} + +static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index > 0) + return -EINVAL; + + *code = V4L2_MBUS_FMT_SGRBG8_1X8; + return 0; +} + +static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8) + return -EINVAL; + + v4l_bound_align_image(&fmt->width, 48, 639, 1, + &fmt->height, 32, 480, 1, 0); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + calc_fps(sd, + &cp->timeperframe.numerator, + &cp->timeperframe.denominator); + + return 0; +} + +static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + u16 speed; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + speed = calc_speed(sd, tpf->numerator, tpf->denominator); + + mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); + v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); + + /* Recalculate and update fps info */ + calc_fps(sd, &tpf->numerator, &tpf->denominator); + + return 0; +} + +static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + struct mt9v011 *core = to_mt9v011(sd); + int rc; + + rc = mt9v011_try_mbus_fmt(sd, fmt); + if (rc < 0) + return -EINVAL; + + core->width = fmt->width; + core->height = fmt->height; + + set_res(sd); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9v011_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + reg->val = mt9v011_read(sd, reg->reg & 0xff); + reg->size = 2; + + return 0; +} + +static int mt9v011_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff); + + return 0; +} +#endif + +static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + u16 version; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, + version); +} + +static const struct v4l2_subdev_core_ops mt9v011_core_ops = { + .queryctrl = mt9v011_queryctrl, + .g_ctrl = mt9v011_g_ctrl, + .s_ctrl = mt9v011_s_ctrl, + .reset = mt9v011_reset, + .g_chip_ident = mt9v011_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9v011_g_register, + .s_register = mt9v011_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops mt9v011_video_ops = { + .enum_mbus_fmt = mt9v011_enum_mbus_fmt, + .try_mbus_fmt = mt9v011_try_mbus_fmt, + .s_mbus_fmt = mt9v011_s_mbus_fmt, + .g_parm = mt9v011_g_parm, + .s_parm = mt9v011_s_parm, +}; + +static const struct v4l2_subdev_ops mt9v011_ops = { + .core = &mt9v011_core_ops, + .video = &mt9v011_video_ops, +}; + + +/**************************************************************************** + I2C Client & Driver + ****************************************************************************/ + +static int mt9v011_probe(struct i2c_client *c, + const struct i2c_device_id *id) +{ + u16 version; + struct mt9v011 *core; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(c->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL); + if (!core) + return -ENOMEM; + + sd = &core->sd; + v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); + + /* Check if the sensor is really a MT9V011 */ + version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); + if ((version != MT9V011_VERSION) && + (version != MT9V011_REV_B_VERSION)) { + v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", + version); + kfree(core); + return -EINVAL; + } + + core->global_gain = 0x0024; + core->exposure = 0x01fc; + core->width = 640; + core->height = 480; + core->xtal = 27000000; /* Hz */ + + if (c->dev.platform_data) { + struct mt9v011_platform_data *pdata = c->dev.platform_data; + + core->xtal = pdata->xtal; + v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", + core->xtal / 1000000, (core->xtal / 1000) % 1000); + } + + v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", + c->addr << 1, c->adapter->name, version); + + return 0; +} + +static int mt9v011_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + + v4l2_dbg(1, debug, sd, + "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", + c->addr << 1); + + v4l2_device_unregister_subdev(sd); + kfree(to_mt9v011(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id mt9v011_id[] = { + { "mt9v011", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v011_id); + +static struct i2c_driver mt9v011_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "mt9v011", + }, + .probe = mt9v011_probe, + .remove = mt9v011_remove, + .id_table = mt9v011_id, +}; + +module_i2c_driver(mt9v011_driver); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c new file mode 100644 index 000000000000..4ba4884c016e --- /dev/null +++ b/drivers/media/i2c/mt9v032.c @@ -0,0 +1,763 @@ +/* + * Driver for MT9V032 CMOS Image Sensor from Micron + * + * Copyright (C) 2010, Laurent Pinchart + * + * Based on the MT9M001 driver, + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MT9V032_PIXEL_ARRAY_HEIGHT 492 +#define MT9V032_PIXEL_ARRAY_WIDTH 782 + +#define MT9V032_CHIP_VERSION 0x00 +#define MT9V032_CHIP_ID_REV1 0x1311 +#define MT9V032_CHIP_ID_REV3 0x1313 +#define MT9V032_COLUMN_START 0x01 +#define MT9V032_COLUMN_START_MIN 1 +#define MT9V032_COLUMN_START_DEF 1 +#define MT9V032_COLUMN_START_MAX 752 +#define MT9V032_ROW_START 0x02 +#define MT9V032_ROW_START_MIN 4 +#define MT9V032_ROW_START_DEF 5 +#define MT9V032_ROW_START_MAX 482 +#define MT9V032_WINDOW_HEIGHT 0x03 +#define MT9V032_WINDOW_HEIGHT_MIN 1 +#define MT9V032_WINDOW_HEIGHT_DEF 480 +#define MT9V032_WINDOW_HEIGHT_MAX 480 +#define MT9V032_WINDOW_WIDTH 0x04 +#define MT9V032_WINDOW_WIDTH_MIN 1 +#define MT9V032_WINDOW_WIDTH_DEF 752 +#define MT9V032_WINDOW_WIDTH_MAX 752 +#define MT9V032_HORIZONTAL_BLANKING 0x05 +#define MT9V032_HORIZONTAL_BLANKING_MIN 43 +#define MT9V032_HORIZONTAL_BLANKING_MAX 1023 +#define MT9V032_VERTICAL_BLANKING 0x06 +#define MT9V032_VERTICAL_BLANKING_MIN 4 +#define MT9V032_VERTICAL_BLANKING_MAX 3000 +#define MT9V032_CHIP_CONTROL 0x07 +#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) +#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) +#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) +#define MT9V032_SHUTTER_WIDTH1 0x08 +#define MT9V032_SHUTTER_WIDTH2 0x09 +#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a +#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b +#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 +#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 +#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 +#define MT9V032_RESET 0x0c +#define MT9V032_READ_MODE 0x0d +#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) +#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 +#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) +#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 +#define MT9V032_READ_MODE_ROW_FLIP (1 << 4) +#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) +#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) +#define MT9V032_READ_MODE_DARK_ROWS (1 << 7) +#define MT9V032_PIXEL_OPERATION_MODE 0x0f +#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) +#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) +#define MT9V032_ANALOG_GAIN 0x35 +#define MT9V032_ANALOG_GAIN_MIN 16 +#define MT9V032_ANALOG_GAIN_DEF 16 +#define MT9V032_ANALOG_GAIN_MAX 64 +#define MT9V032_MAX_ANALOG_GAIN 0x36 +#define MT9V032_MAX_ANALOG_GAIN_MAX 127 +#define MT9V032_FRAME_DARK_AVERAGE 0x42 +#define MT9V032_DARK_AVG_THRESH 0x46 +#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) +#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 +#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) +#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 +#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 +#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) +#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) +#define MT9V032_PIXEL_CLOCK 0x74 +#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) +#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) +#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) +#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) +#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) +#define MT9V032_TEST_PATTERN 0x7f +#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) +#define MT9V032_TEST_PATTERN_DATA_SHIFT 0 +#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) +#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) +#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) +#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) +#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) +#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) +#define MT9V032_TEST_PATTERN_ENABLE (1 << 13) +#define MT9V032_TEST_PATTERN_FLIP (1 << 14) +#define MT9V032_AEC_AGC_ENABLE 0xaf +#define MT9V032_AEC_ENABLE (1 << 0) +#define MT9V032_AGC_ENABLE (1 << 1) +#define MT9V032_THERMAL_INFO 0xc1 + +struct mt9v032 { + struct v4l2_subdev subdev; + struct media_pad pad; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + + struct v4l2_ctrl_handler ctrls; + + struct mutex power_lock; + int power_count; + + struct mt9v032_platform_data *pdata; + u16 chip_control; + u16 aec_agc; +}; + +static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9v032, subdev); +} + +static int mt9v032_read(struct i2c_client *client, const u8 reg) +{ + s32 data = i2c_smbus_read_word_swapped(client, reg); + dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, + data, reg); + return data; +} + +static int mt9v032_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, + data, reg); + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 value = (mt9v032->chip_control & ~clear) | set; + int ret; + + ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); + if (ret < 0) + return ret; + + mt9v032->chip_control = value; + return 0; +} + +static int +mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 value = mt9v032->aec_agc; + int ret; + + if (enable) + value |= which; + else + value &= ~which; + + ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); + if (ret < 0) + return ret; + + mt9v032->aec_agc = value; + return 0; +} + +static int mt9v032_power_on(struct mt9v032 *mt9v032) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + int ret; + + if (mt9v032->pdata->set_clock) { + mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000); + udelay(1); + } + + /* Reset the chip and stop data read out */ + ret = mt9v032_write(client, MT9V032_RESET, 1); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_RESET, 0); + if (ret < 0) + return ret; + + return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); +} + +static void mt9v032_power_off(struct mt9v032 *mt9v032) +{ + if (mt9v032->pdata->set_clock) + mt9v032->pdata->set_clock(&mt9v032->subdev, 0); +} + +static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + int ret; + + if (!on) { + mt9v032_power_off(mt9v032); + return 0; + } + + ret = mt9v032_power_on(mt9v032); + if (ret < 0) + return ret; + + /* Configure the pixel clock polarity */ + if (mt9v032->pdata && mt9v032->pdata->clk_pol) { + ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, + MT9V032_PIXEL_CLOCK_INV_PXL_CLK); + if (ret < 0) + return ret; + } + + /* Disable the noise correction algorithm and restore the controls. */ + ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); + if (ret < 0) + return ret; + + return v4l2_ctrl_handler_setup(&mt9v032->ctrls); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static struct v4l2_mbus_framefmt * +__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v032->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v032->crop; + default: + return NULL; + } +} + +static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE + | MT9V032_CHIP_CONTROL_DOUT_ENABLE + | MT9V032_CHIP_CONTROL_SEQUENTIAL; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *format = &mt9v032->format; + struct v4l2_rect *crop = &mt9v032->crop; + unsigned int hratio; + unsigned int vratio; + int ret; + + if (!enable) + return mt9v032_set_chip_control(mt9v032, mode, 0); + + /* Configure the window size and row/column bin */ + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); + + ret = mt9v032_write(client, MT9V032_READ_MODE, + (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | + (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); + if (ret < 0) + return ret; + + ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, + max(43, 660 - crop->width)); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + return mt9v032_set_chip_control(mt9v032, 0, mode); +} + +static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; + return 0; +} + +static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; + fse->max_width = fse->min_width; + fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; + fse->max_height = fse->min_height; + + return 0; +} + +static int mt9v032_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->which); + return 0; +} + +static int mt9v032_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, + format->which); + + /* Clamp the width and height to avoid dividing by zero. */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9v032_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9v032_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a non multiple + * of 2 pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, + MT9V032_COLUMN_START_MIN, + MT9V032_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, + MT9V032_ROW_START_MIN, + MT9V032_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), + MT9V032_WINDOW_WIDTH_MIN, + MT9V032_WINDOW_WIDTH_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), + MT9V032_WINDOW_HEIGHT_MIN, + MT9V032_WINDOW_HEIGHT_MAX); + + rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) + +static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v032 *mt9v032 = + container_of(ctrl->handler, struct mt9v032, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + u16 data; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, + ctrl->val); + + case V4L2_CID_GAIN: + return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); + + case V4L2_CID_EXPOSURE_AUTO: + return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, + !ctrl->val); + + case V4L2_CID_EXPOSURE: + return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, + ctrl->val); + + case V4L2_CID_TEST_PATTERN: + switch (ctrl->val) { + case 0: + data = 0; + break; + case 1: + data = MT9V032_TEST_PATTERN_GRAY_VERTICAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + case 2: + data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + case 3: + data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL + | MT9V032_TEST_PATTERN_ENABLE; + break; + default: + data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT) + | MT9V032_TEST_PATTERN_USE_DATA + | MT9V032_TEST_PATTERN_ENABLE + | MT9V032_TEST_PATTERN_FLIP; + break; + } + + return mt9v032_write(client, MT9V032_TEST_PATTERN, data); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { + .s_ctrl = mt9v032_s_ctrl, +}; + +static const struct v4l2_ctrl_config mt9v032_ctrls[] = { + { + .ops = &mt9v032_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test pattern", + .min = 0, + .max = 1023, + .step = 1, + .def = 0, + .flags = 0, + } +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) +{ + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + int ret = 0; + + mutex_lock(&mt9v032->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (mt9v032->power_count == !on) { + ret = __mt9v032_set_power(mt9v032, !!on); + if (ret < 0) + goto done; + } + + /* Update the power count. */ + mt9v032->power_count += on ? 1 : -1; + WARN_ON(mt9v032->power_count < 0); + +done: + mutex_unlock(&mt9v032->power_lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9v032_registered(struct v4l2_subdev *subdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + s32 data; + int ret; + + dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", + client->addr); + + ret = mt9v032_power_on(mt9v032); + if (ret < 0) { + dev_err(&client->dev, "MT9V032 power up failed\n"); + return ret; + } + + /* Read and check the sensor version */ + data = mt9v032_read(client, MT9V032_CHIP_VERSION); + if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { + dev_err(&client->dev, "MT9V032 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + mt9v032_power_off(mt9v032); + + dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9V032_COLUMN_START_DEF; + crop->top = MT9V032_ROW_START_DEF; + crop->width = MT9V032_WINDOW_WIDTH_DEF; + crop->height = MT9V032_WINDOW_HEIGHT_DEF; + + format = v4l2_subdev_get_try_format(fh, 0); + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + format->width = MT9V032_WINDOW_WIDTH_DEF; + format->height = MT9V032_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return mt9v032_set_power(subdev, 1); +} + +static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return mt9v032_set_power(subdev, 0); +} + +static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { + .s_power = mt9v032_set_power, +}; + +static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { + .s_stream = mt9v032_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { + .enum_mbus_code = mt9v032_enum_mbus_code, + .enum_frame_size = mt9v032_enum_frame_size, + .get_fmt = mt9v032_get_format, + .set_fmt = mt9v032_set_format, + .get_crop = mt9v032_get_crop, + .set_crop = mt9v032_set_crop, +}; + +static struct v4l2_subdev_ops mt9v032_subdev_ops = { + .core = &mt9v032_subdev_core_ops, + .video = &mt9v032_subdev_video_ops, + .pad = &mt9v032_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { + .registered = mt9v032_registered, + .open = mt9v032_open, + .close = mt9v032_close, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9v032_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9v032 *mt9v032; + unsigned int i; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); + if (!mt9v032) + return -ENOMEM; + + mutex_init(&mt9v032->power_lock); + mt9v032->pdata = client->dev.platform_data; + + v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4); + + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, + MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); + v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, + V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, + MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, + MT9V032_TOTAL_SHUTTER_WIDTH_DEF); + + for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL); + + mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; + + if (mt9v032->ctrls.error) + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9v032->ctrls.error); + + mt9v032->crop.left = MT9V032_COLUMN_START_DEF; + mt9v032->crop.top = MT9V032_ROW_START_DEF; + mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; + mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; + + mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; + mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; + mt9v032->format.field = V4L2_FIELD_NONE; + mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; + + mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; + + v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); + mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; + mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); + if (ret < 0) + kfree(mt9v032); + + return ret; +} + +static int mt9v032_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + kfree(mt9v032); + return 0; +} + +static const struct i2c_device_id mt9v032_id[] = { + { "mt9v032", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v032_id); + +static struct i2c_driver mt9v032_driver = { + .driver = { + .name = "mt9v032", + }, + .probe = mt9v032_probe, + .remove = mt9v032_remove, + .id_table = mt9v032_id, +}; + +module_i2c_driver(mt9v032_driver); + +MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c new file mode 100644 index 000000000000..440c12962bae --- /dev/null +++ b/drivers/media/i2c/noon010pc30.c @@ -0,0 +1,851 @@ +/* + * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP + * + * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. + * Contact: Sylwester Nawrocki, + * + * Initial register configuration based on a driver authored by + * HeungJun Kim . + * + * This program is free software; 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); + +#define MODULE_NAME "NOON010PC30" + +/* + * Register offsets within a page + * b15..b8 - page id, b7..b0 - register address + */ +#define POWER_CTRL_REG 0x0001 +#define PAGEMODE_REG 0x03 +#define DEVICE_ID_REG 0x0004 +#define NOON010PC30_ID 0x86 +#define VDO_CTL_REG(n) (0x0010 + (n)) +#define SYNC_CTL_REG 0x0012 +/* Window size and position */ +#define WIN_ROWH_REG 0x0013 +#define WIN_ROWL_REG 0x0014 +#define WIN_COLH_REG 0x0015 +#define WIN_COLL_REG 0x0016 +#define WIN_HEIGHTH_REG 0x0017 +#define WIN_HEIGHTL_REG 0x0018 +#define WIN_WIDTHH_REG 0x0019 +#define WIN_WIDTHL_REG 0x001A +#define HBLANKH_REG 0x001B +#define HBLANKL_REG 0x001C +#define VSYNCH_REG 0x001D +#define VSYNCL_REG 0x001E +/* VSYNC control */ +#define VS_CTL_REG(n) (0x00A1 + (n)) +/* page 1 */ +#define ISP_CTL_REG(n) (0x0110 + (n)) +#define YOFS_REG 0x0119 +#define DARK_YOFS_REG 0x011A +#define SAT_CTL_REG 0x0120 +#define BSAT_REG 0x0121 +#define RSAT_REG 0x0122 +/* Color correction */ +#define CMC_CTL_REG 0x0130 +#define CMC_OFSGH_REG 0x0133 +#define CMC_OFSGL_REG 0x0135 +#define CMC_SIGN_REG 0x0136 +#define CMC_GOFS_REG 0x0137 +#define CMC_COEF_REG(n) (0x0138 + (n)) +#define CMC_OFS_REG(n) (0x0141 + (n)) +/* Gamma correction */ +#define GMA_CTL_REG 0x0160 +#define GMA_COEF_REG(n) (0x0161 + (n)) +/* Lens Shading */ +#define LENS_CTRL_REG 0x01D0 +#define LENS_XCEN_REG 0x01D1 +#define LENS_YCEN_REG 0x01D2 +#define LENS_RC_REG 0x01D3 +#define LENS_GC_REG 0x01D4 +#define LENS_BC_REG 0x01D5 +#define L_AGON_REG 0x01D6 +#define L_AGOFF_REG 0x01D7 +/* Page 3 - Auto Exposure */ +#define AE_CTL_REG(n) (0x0310 + (n)) +#define AE_CTL9_REG 0x032C +#define AE_CTL10_REG 0x032D +#define AE_YLVL_REG 0x031C +#define AE_YTH_REG(n) (0x031D + (n)) +#define AE_WGT_REG 0x0326 +#define EXP_TIMEH_REG 0x0333 +#define EXP_TIMEM_REG 0x0334 +#define EXP_TIMEL_REG 0x0335 +#define EXP_MMINH_REG 0x0336 +#define EXP_MMINL_REG 0x0337 +#define EXP_MMAXH_REG 0x0338 +#define EXP_MMAXM_REG 0x0339 +#define EXP_MMAXL_REG 0x033A +/* Page 4 - Auto White Balance */ +#define AWB_CTL_REG(n) (0x0410 + (n)) +#define AWB_ENABE 0x80 +#define AWB_WGHT_REG 0x0419 +#define BGAIN_PAR_REG(n) (0x044F + (n)) +/* Manual white balance, when AWB_CTL2[0]=1 */ +#define MWB_RGAIN_REG 0x0466 +#define MWB_BGAIN_REG 0x0467 + +/* The token to mark an array end */ +#define REG_TERM 0xFFFF + +struct noon010_format { + enum v4l2_mbus_pixelcode code; + enum v4l2_colorspace colorspace; + u16 ispctl1_reg; +}; + +struct noon010_frmsize { + u16 width; + u16 height; + int vid_ctl1; +}; + +static const char * const noon010_supply_name[] = { + "vdd_core", "vddio", "vdda" +}; + +#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) + +struct noon010_info { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; + u32 gpio_nreset; + u32 gpio_nstby; + + /* Protects the struct members below */ + struct mutex lock; + + const struct noon010_format *curr_fmt; + const struct noon010_frmsize *curr_win; + unsigned int apply_new_cfg:1; + unsigned int streaming:1; + unsigned int hflip:1; + unsigned int vflip:1; + unsigned int power:1; + u8 i2c_reg_page; +}; + +struct i2c_regval { + u16 addr; + u16 val; +}; + +/* Supported resolutions. */ +static const struct noon010_frmsize noon010_sizes[] = { + { + .width = 352, + .height = 288, + .vid_ctl1 = 0, + }, { + .width = 176, + .height = 144, + .vid_ctl1 = 0x10, + }, { + .width = 88, + .height = 72, + .vid_ctl1 = 0x20, + }, +}; + +/* Supported pixel formats. */ +static const struct noon010_format noon010_formats[] = { + { + .code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x03, + }, { + .code = V4L2_MBUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x02, + }, { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0, + }, { + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x01, + }, { + .code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x40, + }, +}; + +static const struct i2c_regval noon010_base_regs[] = { + { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, + /* Color corection and saturation */ + { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, + { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, + { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, + { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, + { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, + { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, + { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, + { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, + { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, + { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, + { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, + { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, + { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, + { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, + /* Automatic white balance */ + { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, + { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, + /* Auto exposure */ + { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, + { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, + { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, + { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, + { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, + { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, + { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, + /* Lens shading compensation */ + { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, + { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, + { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, + { REG_TERM, 0 }, +}; + +static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) +{ + return container_of(sd, struct noon010_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; +} + +static inline int set_i2c_page(struct noon010_info *info, + struct i2c_client *client, unsigned int reg) +{ + u32 page = reg >> 8 & 0xFF; + int ret = 0; + + if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { + ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); + if (!ret) + info->i2c_reg_page = page; + } + return ret; +} + +static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct noon010_info *info = to_noon010(sd); + int ret = set_i2c_page(info, client, reg_addr); + + if (ret) + return ret; + return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); +} + +static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct noon010_info *info = to_noon010(sd); + int ret = set_i2c_page(info, client, reg_addr); + + if (ret) + return ret; + return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); +} + +static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, + const struct i2c_regval *msg) +{ + while (msg->addr != REG_TERM) { + int ret = cam_i2c_write(sd, msg->addr, msg->val); + + if (ret) + return ret; + msg++; + } + return 0; +} + +/* Device reset and sleep mode control */ +static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) +{ + struct noon010_info *info = to_noon010(sd); + u8 reg = sleep ? 0xF1 : 0xF0; + int ret = 0; + + if (reset) { + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); + udelay(20); + } + if (!ret) { + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); + if (reset && !ret) + info->i2c_reg_page = -1; + } + return ret; +} + +/* Automatic white balance control */ +static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) +{ + int ret; + + ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); + if (!ret) + ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); + return ret; +} + +/* Called with struct noon010_info.lock mutex held */ +static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) +{ + struct noon010_info *info = to_noon010(sd); + int reg, ret; + + reg = cam_i2c_read(sd, VDO_CTL_REG(1)); + if (reg < 0) + return reg; + + reg &= 0x7C; + if (hflip) + reg |= 0x01; + if (vflip) + reg |= 0x02; + + ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); + if (!ret) { + info->hflip = hflip; + info->vflip = vflip; + } + return ret; +} + +/* Configure resolution and color format */ +static int noon010_set_params(struct v4l2_subdev *sd) +{ + struct noon010_info *info = to_noon010(sd); + + int ret = cam_i2c_write(sd, VDO_CTL_REG(0), + info->curr_win->vid_ctl1); + if (ret) + return ret; + return cam_i2c_write(sd, ISP_CTL_REG(0), + info->curr_fmt->ispctl1_reg); +} + +/* Find nearest matching image pixel size. */ +static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, + const struct noon010_frmsize **size) +{ + unsigned int min_err = ~0; + int i = ARRAY_SIZE(noon010_sizes); + const struct noon010_frmsize *fsize = &noon010_sizes[0], + *match = NULL; + + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + if (size) + *size = match; + return 0; + } + return -EINVAL; +} + +/* Called with info.lock mutex held */ +static int power_enable(struct noon010_info *info) +{ + int ret; + + if (info->power) { + v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); + return 0; + } + + if (gpio_is_valid(info->gpio_nstby)) + gpio_set_value(info->gpio_nstby, 0); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_set_value(info->gpio_nreset, 0); + + ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); + if (ret) + return ret; + + if (gpio_is_valid(info->gpio_nreset)) { + msleep(50); + gpio_set_value(info->gpio_nreset, 1); + } + if (gpio_is_valid(info->gpio_nstby)) { + udelay(1000); + gpio_set_value(info->gpio_nstby, 1); + } + if (gpio_is_valid(info->gpio_nreset)) { + udelay(1000); + gpio_set_value(info->gpio_nreset, 0); + msleep(100); + gpio_set_value(info->gpio_nreset, 1); + msleep(20); + } + info->power = 1; + + v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); + return 0; +} + +/* Called with info.lock mutex held */ +static int power_disable(struct noon010_info *info) +{ + int ret; + + if (!info->power) { + v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); + return 0; + } + + ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); + if (ret) + return ret; + + if (gpio_is_valid(info->gpio_nstby)) + gpio_set_value(info->gpio_nstby, 0); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_set_value(info->gpio_nreset, 0); + + info->power = 0; + + v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); + + return 0; +} + +static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct noon010_info *info = to_noon010(sd); + int ret = 0; + + v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", + __func__, ctrl->id, ctrl->val); + + mutex_lock(&info->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (!info->power) + goto unlock; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = noon010_enable_autowhitebalance(sd, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); + break; + default: + ret = -EINVAL; + } +unlock: + mutex_unlock(&info->lock); + return ret; +} + +static int noon010_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(noon010_formats)) + return -EINVAL; + + code->code = noon010_formats[code->index].code; + return 0; +} + +static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct noon010_info *info = to_noon010(sd); + struct v4l2_mbus_framefmt *mf; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + } + return 0; + } + mf = &fmt->format; + + mutex_lock(&info->lock); + mf->width = info->curr_win->width; + mf->height = info->curr_win->height; + mf->code = info->curr_fmt->code; + mf->colorspace = info->curr_fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + mutex_unlock(&info->lock); + return 0; +} + +/* Return nearest media bus frame format. */ +static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + int i = ARRAY_SIZE(noon010_formats); + + while (--i) + if (mf->code == noon010_formats[i].code) + break; + mf->code = noon010_formats[i].code; + + return &noon010_formats[i]; +} + +static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct noon010_info *info = to_noon010(sd); + const struct noon010_frmsize *size = NULL; + const struct noon010_format *nf; + struct v4l2_mbus_framefmt *mf; + int ret = 0; + + nf = noon010_try_fmt(sd, &fmt->format); + noon010_try_frame_size(&fmt->format, &size); + fmt->format.colorspace = V4L2_COLORSPACE_JPEG; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh) { + mf = v4l2_subdev_get_try_format(fh, 0); + *mf = fmt->format; + } + return 0; + } + mutex_lock(&info->lock); + if (!info->streaming) { + info->apply_new_cfg = 1; + info->curr_fmt = nf; + info->curr_win = size; + } else { + ret = -EBUSY; + } + mutex_unlock(&info->lock); + return ret; +} + +/* Called with struct noon010_info.lock mutex held */ +static int noon010_base_config(struct v4l2_subdev *sd) +{ + int ret = noon010_bulk_write_reg(sd, noon010_base_regs); + if (!ret) + ret = noon010_set_params(sd); + if (!ret) + ret = noon010_set_flip(sd, 1, 0); + + return ret; +} + +static int noon010_s_power(struct v4l2_subdev *sd, int on) +{ + struct noon010_info *info = to_noon010(sd); + int ret; + + mutex_lock(&info->lock); + if (on) { + ret = power_enable(info); + if (!ret) + ret = noon010_base_config(sd); + } else { + noon010_power_ctrl(sd, false, true); + ret = power_disable(info); + } + mutex_unlock(&info->lock); + + /* Restore the controls state */ + if (!ret && on) + ret = v4l2_ctrl_handler_setup(&info->hdl); + + return ret; +} + +static int noon010_s_stream(struct v4l2_subdev *sd, int on) +{ + struct noon010_info *info = to_noon010(sd); + int ret = 0; + + mutex_lock(&info->lock); + if (!info->streaming != !on) { + ret = noon010_power_ctrl(sd, false, !on); + if (!ret) + info->streaming = on; + } + if (!ret && on && info->apply_new_cfg) { + ret = noon010_set_params(sd); + if (!ret) + info->apply_new_cfg = 0; + } + mutex_unlock(&info->lock); + return ret; +} + +static int noon010_log_status(struct v4l2_subdev *sd) +{ + struct noon010_info *info = to_noon010(sd); + + v4l2_ctrl_handler_log_status(&info->hdl, sd->name); + return 0; +} + +static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + + mf->width = noon010_sizes[0].width; + mf->height = noon010_sizes[0].height; + mf->code = noon010_formats[0].code; + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->field = V4L2_FIELD_NONE; + return 0; +} + +static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { + .open = noon010_open, +}; + +static const struct v4l2_ctrl_ops noon010_ctrl_ops = { + .s_ctrl = noon010_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops noon010_core_ops = { + .s_power = noon010_s_power, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .log_status = noon010_log_status, +}; + +static struct v4l2_subdev_pad_ops noon010_pad_ops = { + .enum_mbus_code = noon010_enum_mbus_code, + .get_fmt = noon010_get_fmt, + .set_fmt = noon010_set_fmt, +}; + +static struct v4l2_subdev_video_ops noon010_video_ops = { + .s_stream = noon010_s_stream, +}; + +static const struct v4l2_subdev_ops noon010_ops = { + .core = &noon010_core_ops, + .pad = &noon010_pad_ops, + .video = &noon010_video_ops, +}; + +/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ +static int noon010_detect(struct i2c_client *client, struct noon010_info *info) +{ + int ret; + + ret = power_enable(info); + if (ret) + return ret; + + ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); + if (ret < 0) + dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); + + power_disable(info); + + return ret == NOON010PC30_ID ? 0 : -ENODEV; +} + +static int noon010_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct noon010_info *info; + struct v4l2_subdev *sd; + const struct noon010pc30_platform_data *pdata + = client->dev.platform_data; + int ret; + int i; + + if (!pdata) { + dev_err(&client->dev, "No platform data!\n"); + return -EIO; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &noon010_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + + sd->internal_ops = &noon010_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&info->hdl, 3); + + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 64); + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); + + sd->ctrl_handler = &info->hdl; + + ret = info->hdl.error; + if (ret) + goto np_err; + + info->i2c_reg_page = -1; + info->gpio_nreset = -EINVAL; + info->gpio_nstby = -EINVAL; + info->curr_fmt = &noon010_formats[0]; + info->curr_win = &noon010_sizes[0]; + + if (gpio_is_valid(pdata->gpio_nreset)) { + ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST"); + if (ret) { + dev_err(&client->dev, "GPIO request error: %d\n", ret); + goto np_err; + } + info->gpio_nreset = pdata->gpio_nreset; + gpio_direction_output(info->gpio_nreset, 0); + gpio_export(info->gpio_nreset, 0); + } + + if (gpio_is_valid(pdata->gpio_nstby)) { + ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY"); + if (ret) { + dev_err(&client->dev, "GPIO request error: %d\n", ret); + goto np_gpio_err; + } + info->gpio_nstby = pdata->gpio_nstby; + gpio_direction_output(info->gpio_nstby, 0); + gpio_export(info->gpio_nstby, 0); + } + + for (i = 0; i < NOON010_NUM_SUPPLIES; i++) + info->supply[i].supply = noon010_supply_name[i]; + + ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, + info->supply); + if (ret) + goto np_reg_err; + + info->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + if (ret < 0) + goto np_me_err; + + ret = noon010_detect(client, info); + if (!ret) + return 0; + +np_me_err: + regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); +np_reg_err: + if (gpio_is_valid(info->gpio_nstby)) + gpio_free(info->gpio_nstby); +np_gpio_err: + if (gpio_is_valid(info->gpio_nreset)) + gpio_free(info->gpio_nreset); +np_err: + v4l2_ctrl_handler_free(&info->hdl); + v4l2_device_unregister_subdev(sd); + kfree(info); + return ret; +} + +static int noon010_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct noon010_info *info = to_noon010(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&info->hdl); + + regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_free(info->gpio_nreset); + + if (gpio_is_valid(info->gpio_nstby)) + gpio_free(info->gpio_nstby); + + media_entity_cleanup(&sd->entity); + kfree(info); + return 0; +} + +static const struct i2c_device_id noon010_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, noon010_id); + + +static struct i2c_driver noon010_i2c_driver = { + .driver = { + .name = MODULE_NAME + }, + .probe = noon010_probe, + .remove = noon010_remove, + .id_table = noon010_id, +}; + +module_i2c_driver(noon010_i2c_driver); + +MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c new file mode 100644 index 000000000000..e7c82b297514 --- /dev/null +++ b/drivers/media/i2c/ov7670.c @@ -0,0 +1,1586 @@ +/* + * A V4L2 driver for OmniVision OV7670 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +/* + * The 7670 sits on i2c with ID 0x42 + */ +#define OV7670_I2C_ADDR 0x42 + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clocl control */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x00 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_REG76 0x76 /* OV's name */ +#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ +#define R76_WHTPCOR 0x40 /* White pixel correction enable */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + +/* + * Information we maintain about a known sensor. + */ +struct ov7670_format_struct; /* coming later */ +struct ov7670_info { + struct v4l2_subdev sd; + struct ov7670_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ + int min_width; /* Filter out smaller sizes */ + int min_height; /* Filter out smaller sizes */ + int clock_speed; /* External clock speed (MHz) */ + u8 clkrc; /* Clock divider value */ + bool use_smbus; /* Use smbus I/O instead of I2C */ +}; + +static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov7670_info, sd); +} + + + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ov7670_default_regs[] = { + { REG_COM7, COM7_RESET }, +/* + * Clock scale: 3 = 15fps + * 2 = 20fps + * 1 = 30fps + */ + { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ + { REG_TSLB, 0x04 }, /* OV */ + { REG_COM7, 0 }, /* VGA */ + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, + { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, + { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, + + { REG_COM3, 0 }, { REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, { REG_COM10, 0x0 }, + + /* Gamma curve values */ + { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, + { REG_GAIN, 0 }, { REG_AECH, 0 }, + { REG_COM4, 0x40 }, /* magic reserved bit */ + { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, + { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, + { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, + { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ + { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, + { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, + { REG_HAECC7, 0x94 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, + { 0x16, 0x02 }, { REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, + { REG_RED, 0x60 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + + +/* + * Here we'll try to encapsulate the changes for just the output + * video format. + * + * RGB656 and YUV422 come from OV; RGB444 is homebrewed. + * + * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. + */ + + +static struct regval_list ov7670_fmt_yuv422[] = { + { REG_COM7, 0x0 }, /* Selects YUV mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0 }, /* CCIR601 */ + { REG_COM15, COM15_R00FF }, + { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_rgb565[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0x0 }, /* CCIR601 */ + { REG_COM15, COM15_RGB565 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_rgb444[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ + { REG_COM1, 0x0 }, /* CCIR601 */ + { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_raw[] = { + { REG_COM7, COM7_BAYER }, + { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ + { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ + { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ + { 0xff, 0xff }, +}; + + + +/* + * Low-level register I/O. + * + * Note that there are two versions of these. On the XO 1, the + * i2c controller only does SMBUS, so that's what we use. The + * ov7670 is not really an SMBUS device, though, so the communication + * is not always entirely reliable. + */ +static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + *value = (unsigned char)ret; + ret = 0; + } + return ret; +} + + +static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = i2c_smbus_write_byte_data(client, reg, value); + + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(5); /* Wait for reset to run */ + return ret; +} + +/* + * On most platforms, we'd rather do straight i2c I/O. + */ +static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 data = reg; + struct i2c_msg msg; + int ret; + + /* + * Send out the register address... + */ + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + msg.buf = &data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + printk(KERN_ERR "Error %d on register write\n", ret); + return ret; + } + /* + * ...then read back the result. + */ + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) { + *value = data; + ret = 0; + } + return ret; +} + + +static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_msg msg; + unsigned char data[2] = { reg, value }; + int ret; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret > 0) + ret = 0; + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(5); /* Wait for reset to run */ + return ret; +} + +static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct ov7670_info *info = to_state(sd); + if (info->use_smbus) + return ov7670_read_smbus(sd, reg, value); + else + return ov7670_read_i2c(sd, reg, value); +} + +static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct ov7670_info *info = to_state(sd); + if (info->use_smbus) + return ov7670_write_smbus(sd, reg, value); + else + return ov7670_write_i2c(sd, reg, value); +} + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) +{ + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7670_write(sd, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static int ov7670_reset(struct v4l2_subdev *sd, u32 val) +{ + ov7670_write(sd, REG_COM7, COM7_RESET); + msleep(1); + return 0; +} + + +static int ov7670_init(struct v4l2_subdev *sd, u32 val) +{ + return ov7670_write_array(sd, ov7670_default_regs); +} + + + +static int ov7670_detect(struct v4l2_subdev *sd) +{ + unsigned char v; + int ret; + + ret = ov7670_init(sd, 0); + if (ret < 0) + return ret; + ret = ov7670_read(sd, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7670_read(sd, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + /* + * OK, we know we have an OmniVision chip...but which one? + */ + ret = ov7670_read(sd, REG_PID, &v); + if (ret < 0) + return ret; + if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + ret = ov7670_read(sd, REG_VER, &v); + if (ret < 0) + return ret; + if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + return 0; +} + + +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix numbers come from OmniVision. + */ +static struct ov7670_format_struct { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; + struct regval_list *regs; + int cmatrix[CMATRIX_LEN]; +} ov7670_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .regs = ov7670_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .regs = ov7670_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .regs = ov7670_fmt_rgb565, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + { + .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .regs = ov7670_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + }, +}; +#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) + + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list ov7670_qcif_regs[] = { + { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, + { REG_COM3, COM3_DCWEN }, + { REG_COM14, COM14_DCWEN | 0x01}, + { 0x73, 0xf1 }, + { 0xa2, 0x52 }, + { 0x7b, 0x1c }, + { 0x7c, 0x28 }, + { 0x7d, 0x3c }, + { 0x7f, 0x69 }, + { REG_COM9, 0x38 }, + { 0xa1, 0x0b }, + { 0x74, 0x19 }, + { 0x9a, 0x80 }, + { 0x43, 0x14 }, + { REG_COM13, 0xc0 }, + { 0xff, 0xff }, +}; + +static struct ov7670_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ov7670_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 168, /* Empirically determined */ + .hstop = 24, + .vstart = 12, + .vstop = 492, + .regs = NULL, + }, + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = ov7670_qcif_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) + + +/* + * Store a set of start/stop values into the camera. + */ +static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; +/* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7670_read(sd, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov7670_write(sd, REG_HREF, v); +/* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7670_read(sd, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += ov7670_write(sd, REG_VREF, v); + return ret; +} + + +static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= N_OV7670_FMTS) + return -EINVAL; + + *code = ov7670_formats[index].mbus_code; + return 0; +} + +static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + struct ov7670_format_struct **ret_fmt, + struct ov7670_win_size **ret_wsize) +{ + int index; + struct ov7670_win_size *wsize; + + for (index = 0; index < N_OV7670_FMTS; index++) + if (ov7670_formats[index].mbus_code == fmt->code) + break; + if (index >= N_OV7670_FMTS) { + /* default to first format */ + index = 0; + fmt->code = ov7670_formats[0].mbus_code; + } + if (ret_fmt != NULL) + *ret_fmt = ov7670_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + fmt->field = V4L2_FIELD_NONE; + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; + wsize++) + if (fmt->width >= wsize->width && fmt->height >= wsize->height) + break; + if (wsize >= ov7670_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + fmt->width = wsize->width; + fmt->height = wsize->height; + fmt->colorspace = ov7670_formats[index].colorspace; + return 0; +} + +static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); +} + +/* + * Set a format. + */ +static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov7670_format_struct *ovfmt; + struct ov7670_win_size *wsize; + struct ov7670_info *info = to_state(sd); + unsigned char com7; + int ret; + + ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); + + if (ret) + return ret; + /* + * COM7 is a pain in the ass, it doesn't like to be read then + * quickly written afterward. But we have everything we need + * to set it absolutely here, as long as the format-specific + * register sets list it first. + */ + com7 = ovfmt->regs[0].value; + com7 |= wsize->com7_bit; + ov7670_write(sd, REG_COM7, com7); + /* + * Now write the rest of the array. Also store start/stops + */ + ov7670_write_array(sd, ovfmt->regs + 1); + ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, + wsize->vstop); + ret = 0; + if (wsize->regs) + ret = ov7670_write_array(sd, wsize->regs); + info->fmt = ovfmt; + + /* + * If we're running RGB565, we must rewrite clkrc after setting + * the other parameters or the image looks poor. If we're *not* + * doing RGB565, we must not rewrite clkrc or the image looks + * *really* poor. + * + * (Update) Now that we retain clkrc state, we should be able + * to write it unconditionally, and that will make the frame + * rate persistent too. + */ + if (ret == 0) + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + return 0; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct ov7670_info *info = to_state(sd); + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = info->clock_speed; + if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); + return 0; +} + +static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct ov7670_info *info = to_state(sd); + int div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator * info->clock_speed) / tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + info->clkrc = (info->clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = info->clock_speed / div; + return ov7670_write(sd, REG_CLKRC, info->clkrc); +} + + +/* + * Frame intervals. Since frame rates are controlled with the clock + * divider, we can only do 30/n for integer n values. So no continuous + * or stepwise options. Here we just pick a handful of logical values. + */ + +static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; + +static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_frmivalenum *interval) +{ + if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) + return -EINVAL; + interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; + interval->discrete.numerator = 1; + interval->discrete.denominator = ov7670_frame_rates[interval->index]; + return 0; +} + +/* + * Frame size enumeration + */ +static int ov7670_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize) +{ + struct ov7670_info *info = to_state(sd); + int i; + int num_valid = -1; + __u32 index = fsize->index; + + /* + * If a minimum width/height was requested, filter out the capture + * windows that fall outside that. + */ + for (i = 0; i < N_WIN_SIZES; i++) { + struct ov7670_win_size *win = &ov7670_win_sizes[index]; + if (info->min_width && win->width < info->min_width) + continue; + if (info->min_height && win->height < info->min_height) + continue; + if (index == ++num_valid) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = win->width; + fsize->discrete.height = win->height; + return 0; + } + } + + return -EINVAL; +} + +/* + * Code for dealing with controls. + */ + +static int ov7670_store_cmatrix(struct v4l2_subdev *sd, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits = 0; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); + } + ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov7670_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov7670_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov7670_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov7670_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov7670_sine(theta); +} + + + + +static void ov7670_calc_cmatrix(struct ov7670_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov7670_sine(info->hue); + costh = ov7670_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int ov7670_s_sat(struct v4l2_subdev *sd, int value) +{ + struct ov7670_info *info = to_state(sd); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(sd, matrix); + return ret; +} + +static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) +{ + struct ov7670_info *info = to_state(sd); + + *value = info->sat; + return 0; +} + +static int ov7670_s_hue(struct v4l2_subdev *sd, int value) +{ + struct ov7670_info *info = to_state(sd); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(sd, matrix); + return ret; +} + + +static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) +{ + struct ov7670_info *info = to_state(sd); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char ov7670_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + return 128 - (v & 0x7f); +} + + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + return (128 - v) | 0x80; +} + +static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) +{ + unsigned char com8 = 0, v; + int ret; + + ov7670_read(sd, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov7670_write(sd, REG_COM8, com8); + v = ov7670_abs_to_sm(value); + ret = ov7670_write(sd, REG_BRIGHT, v); + return ret; +} + +static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) +{ + unsigned char v = 0; + int ret = ov7670_read(sd, REG_BRIGHT, &v); + + *value = ov7670_sm_to_abs(v); + return ret; +} + +static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) +{ + return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); +} + +static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) +{ + unsigned char v = 0; + int ret = ov7670_read(sd, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char v = 0; + + ret = ov7670_read(sd, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) +{ + unsigned char v = 0; + int ret; + + ret = ov7670_read(sd, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += ov7670_write(sd, REG_MVFP, v); + return ret; +} + + + +static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char v = 0; + + ret = ov7670_read(sd, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) +{ + unsigned char v = 0; + int ret; + + ret = ov7670_read(sd, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += ov7670_write(sd, REG_MVFP, v); + return ret; +} + +/* + * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes + * the data sheet, the VREF parts should be the most significant, but + * experience shows otherwise. There seems to be little value in + * messing with the VREF bits, so we leave them alone. + */ +static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char gain; + + ret = ov7670_read(sd, REG_GAIN, &gain); + *value = gain; + return ret; +} + +static int ov7670_s_gain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_write(sd, REG_GAIN, value & 0xff); + /* Have to turn off AGC as well */ + if (ret == 0) { + ret = ov7670_read(sd, REG_COM8, &com8); + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); + } + return ret; +} + +/* + * Tweak autogain. + */ +static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + *value = (com8 & COM8_AGC) != 0; + return ret; +} + +static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value) + com8 |= COM8_AGC; + else + com8 &= ~COM8_AGC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + +/* + * Exposure is spread all over the place: top 6 bits in AECHH, middle + * 8 in AECH, and two stashed in COM1 just for the hell of it. + */ +static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com1, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_AECH, &aech) + + ov7670_read(sd, REG_AECHH, &aechh); + *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); + return ret; +} + +static int ov7670_s_exp(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com1, com8, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_COM8, &com8); + ov7670_read(sd, REG_AECHH, &aechh); + if (ret) + return ret; + + com1 = (com1 & 0xfc) | (value & 0x03); + aech = (value >> 2) & 0xff; + aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); + ret = ov7670_write(sd, REG_COM1, com1) + + ov7670_write(sd, REG_AECH, aech) + + ov7670_write(sd, REG_AECHH, aechh); + /* Have to turn off AEC as well */ + if (ret == 0) + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); + return ret; +} + +/* + * Tweak autoexposure. + */ +static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (com8 & COM8_AEC) + *atype = V4L2_EXPOSURE_AUTO; + else + *atype = V4L2_EXPOSURE_MANUAL; + return ret; +} + +static int ov7670_s_autoexp(struct v4l2_subdev *sd, + enum v4l2_exposure_auto_type value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value == V4L2_EXPOSURE_AUTO) + com8 |= COM8_AEC; + else + com8 &= ~COM8_AEC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + + + +static int ov7670_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); + case V4L2_CID_GAIN: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_AUTOGAIN: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + case V4L2_CID_EXPOSURE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); + case V4L2_CID_EXPOSURE_AUTO: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + } + return -EINVAL; +} + +static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_g_brightness(sd, &ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_g_contrast(sd, &ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_g_sat(sd, &ctrl->value); + case V4L2_CID_HUE: + return ov7670_g_hue(sd, &ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_g_vflip(sd, &ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_g_hflip(sd, &ctrl->value); + case V4L2_CID_GAIN: + return ov7670_g_gain(sd, &ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_g_autogain(sd, &ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_g_exp(sd, &ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_g_autoexp(sd, &ctrl->value); + } + return -EINVAL; +} + +static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_s_brightness(sd, ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_s_contrast(sd, ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_s_sat(sd, ctrl->value); + case V4L2_CID_HUE: + return ov7670_s_hue(sd, ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_s_vflip(sd, ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_s_hflip(sd, ctrl->value); + case V4L2_CID_GAIN: + return ov7670_s_gain(sd, ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_s_autogain(sd, ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_s_exp(sd, ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_s_autoexp(sd, + (enum v4l2_exposure_auto_type) ctrl->value); + } + return -EINVAL; +} + +static int ov7670_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char val = 0; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ret = ov7670_read(sd, reg->reg & 0xff, &val); + reg->val = val; + reg->size = 1; + return ret; +} + +static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ov7670_core_ops = { + .g_chip_ident = ov7670_g_chip_ident, + .g_ctrl = ov7670_g_ctrl, + .s_ctrl = ov7670_s_ctrl, + .queryctrl = ov7670_queryctrl, + .reset = ov7670_reset, + .init = ov7670_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov7670_g_register, + .s_register = ov7670_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops ov7670_video_ops = { + .enum_mbus_fmt = ov7670_enum_mbus_fmt, + .try_mbus_fmt = ov7670_try_mbus_fmt, + .s_mbus_fmt = ov7670_s_mbus_fmt, + .s_parm = ov7670_s_parm, + .g_parm = ov7670_g_parm, + .enum_frameintervals = ov7670_enum_frameintervals, + .enum_framesizes = ov7670_enum_framesizes, +}; + +static const struct v4l2_subdev_ops ov7670_ops = { + .core = &ov7670_core_ops, + .video = &ov7670_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int ov7670_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct ov7670_info *info; + int ret; + + info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ov7670_ops); + + info->clock_speed = 30; /* default: a guess */ + if (client->dev.platform_data) { + struct ov7670_config *config = client->dev.platform_data; + + /* + * Must apply configuration before initializing device, because it + * selects I/O method. + */ + info->min_width = config->min_width; + info->min_height = config->min_height; + info->use_smbus = config->use_smbus; + + if (config->clock_speed) + info->clock_speed = config->clock_speed; + } + + /* Make sure it's an ov7670 */ + ret = ov7670_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ov7670 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + info->fmt = &ov7670_formats[0]; + info->sat = 128; /* Review this */ + info->clkrc = info->clock_speed / 30; + return 0; +} + + +static int ov7670_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id ov7670_id[] = { + { "ov7670", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov7670_id); + +static struct i2c_driver ov7670_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ov7670", + }, + .probe = ov7670_probe, + .remove = ov7670_remove, + .id_table = ov7670_id, +}; + +module_i2c_driver(ov7670_driver); diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c new file mode 100644 index 000000000000..045ca7f4f6ca --- /dev/null +++ b/drivers/media/i2c/s5k6aa.c @@ -0,0 +1,1667 @@ +/* + * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor + * with embedded SoC ISP. + * + * Copyright (C) 2011, Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * Based on a driver authored by Dongsoo Nathaniel Kim. + * Copyright (C) 2009, Dongsoo Nathaniel Kim + * + * This program is free software; 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); + +#define DRIVER_NAME "S5K6AA" + +/* The token to indicate array termination */ +#define S5K6AA_TERM 0xffff +#define S5K6AA_OUT_WIDTH_DEF 640 +#define S5K6AA_OUT_HEIGHT_DEF 480 +#define S5K6AA_WIN_WIDTH_MAX 1280 +#define S5K6AA_WIN_HEIGHT_MAX 1024 +#define S5K6AA_WIN_WIDTH_MIN 8 +#define S5K6AA_WIN_HEIGHT_MIN 8 + +/* + * H/W register Interface (0xD0000000 - 0xD0000FFF) + */ +#define AHB_MSB_ADDR_PTR 0xfcfc +#define GEN_REG_OFFSH 0xd000 +#define REG_CMDWR_ADDRH 0x0028 +#define REG_CMDWR_ADDRL 0x002a +#define REG_CMDRD_ADDRH 0x002c +#define REG_CMDRD_ADDRL 0x002e +#define REG_CMDBUF0_ADDR 0x0f12 +#define REG_CMDBUF1_ADDR 0x0f10 + +/* + * Host S/W Register interface (0x70000000 - 0x70002000) + * The value of the two most significant address bytes is 0x7000, + * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. + */ +#define HOST_SWIF_OFFSH 0x7000 + +/* Initialization parameters */ +/* Master clock frequency in KHz */ +#define REG_I_INCLK_FREQ_L 0x01b8 +#define REG_I_INCLK_FREQ_H 0x01ba +#define MIN_MCLK_FREQ_KHZ 6000U +#define MAX_MCLK_FREQ_KHZ 27000U +#define REG_I_USE_NPVI_CLOCKS 0x01c6 +#define REG_I_USE_NMIPI_CLOCKS 0x01c8 + +/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ +#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) +#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) +#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) +#define SYS_PLL_OUT_FREQ (48000000 / 4000) +#define PCLK_FREQ_MIN (24000000 / 4000) +#define PCLK_FREQ_MAX (48000000 / 4000) +#define REG_I_INIT_PARAMS_UPDATED 0x01e0 +#define REG_I_ERROR_INFO 0x01e2 + +/* General purpose parameters */ +#define REG_USER_BRIGHTNESS 0x01e4 +#define REG_USER_CONTRAST 0x01e6 +#define REG_USER_SATURATION 0x01e8 +#define REG_USER_SHARPBLUR 0x01ea + +#define REG_G_SPEC_EFFECTS 0x01ee +#define REG_G_ENABLE_PREV 0x01f0 +#define REG_G_ENABLE_PREV_CHG 0x01f2 +#define REG_G_NEW_CFG_SYNC 0x01f8 +#define REG_G_PREVZOOM_IN_WIDTH 0x020a +#define REG_G_PREVZOOM_IN_HEIGHT 0x020c +#define REG_G_PREVZOOM_IN_XOFFS 0x020e +#define REG_G_PREVZOOM_IN_YOFFS 0x0210 +#define REG_G_INPUTS_CHANGE_REQ 0x021a +#define REG_G_ACTIVE_PREV_CFG 0x021c +#define REG_G_PREV_CFG_CHG 0x021e +#define REG_G_PREV_OPEN_AFTER_CH 0x0220 +#define REG_G_PREV_CFG_ERROR 0x0222 + +/* Preview control section. n = 0...4. */ +#define PREG(n, x) ((n) * 0x26 + x) +#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) +#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) +#define REG_P_FMT(n) PREG(n, 0x0246) +#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) +#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) +#define REG_P_PVI_MASK(n) PREG(n, 0x024c) +#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) +#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) +#define FR_RATE_DYNAMIC 0 +#define FR_RATE_FIXED 1 +#define FR_RATE_FIXED_ACCURATE 2 +#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) +#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ +#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ +/* Frame period in 0.1 ms units */ +#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) +#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) +/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ +#define US_TO_FR_TIME(__t) ((__t) / 100) +#define S5K6AA_MIN_FR_TIME 33300 /* us */ +#define S5K6AA_MAX_FR_TIME 650000 /* us */ +#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ +/* The below 5 registers are for "device correction" values */ +#define REG_P_COLORTEMP(n) PREG(n, 0x025e) +#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) + +/* Extended image property controls */ +/* Exposure time in 10 us units */ +#define REG_SF_USR_EXPOSURE_L 0x03c6 +#define REG_SF_USR_EXPOSURE_H 0x03c8 +#define REG_SF_USR_EXPOSURE_CHG 0x03ca +#define REG_SF_USR_TOT_GAIN 0x03cc +#define REG_SF_USR_TOT_GAIN_CHG 0x03ce +#define REG_SF_RGAIN 0x03d0 +#define REG_SF_RGAIN_CHG 0x03d2 +#define REG_SF_GGAIN 0x03d4 +#define REG_SF_GGAIN_CHG 0x03d6 +#define REG_SF_BGAIN 0x03d8 +#define REG_SF_BGAIN_CHG 0x03da +#define REG_SF_FLICKER_QUANT 0x03dc +#define REG_SF_FLICKER_QUANT_CHG 0x03de + +/* Output interface (parallel/MIPI) setup */ +#define REG_OIF_EN_MIPI_LANES 0x03fa +#define REG_OIF_EN_PACKETS 0x03fc +#define REG_OIF_CFG_CHG 0x03fe + +/* Auto-algorithms enable mask */ +#define REG_DBG_AUTOALG_EN 0x0400 +#define AALG_ALL_EN_MASK (1 << 0) +#define AALG_AE_EN_MASK (1 << 1) +#define AALG_DIVLEI_EN_MASK (1 << 2) +#define AALG_WB_EN_MASK (1 << 3) +#define AALG_FLICKER_EN_MASK (1 << 5) +#define AALG_FIT_EN_MASK (1 << 6) +#define AALG_WRHW_EN_MASK (1 << 7) + +/* Firmware revision information */ +#define REG_FW_APIVER 0x012e +#define S5K6AAFX_FW_APIVER 0x0001 +#define REG_FW_REVISION 0x0130 + +/* For now we use only one user configuration register set */ +#define S5K6AA_MAX_PRESETS 1 + +static const char * const s5k6aa_supply_names[] = { + "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ + "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ + "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) + or 2.8V (2.6V to 3.0) */ + "vddio", /* I/O supply 1.8V (1.65V to 1.95V) + or 2.8V (2.5V to 3.1V) */ +}; +#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) + +enum s5k6aa_gpio_id { + STBY, + RST, + GPIO_NUM, +}; + +struct s5k6aa_regval { + u16 addr; + u16 val; +}; + +struct s5k6aa_pixfmt { + enum v4l2_mbus_pixelcode code; + u32 colorspace; + /* REG_P_FMT(x) register value */ + u16 reg_p_fmt; +}; + +struct s5k6aa_preset { + /* output pixel format and resolution */ + struct v4l2_mbus_framefmt mbus_fmt; + u8 clk_id; + u8 index; +}; + +struct s5k6aa_ctrls { + struct v4l2_ctrl_handler handler; + /* Auto / manual white balance cluster */ + struct v4l2_ctrl *awb; + struct v4l2_ctrl *gain_red; + struct v4l2_ctrl *gain_blue; + struct v4l2_ctrl *gain_green; + /* Mirror cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + /* Auto exposure / manual exposure and gain cluster */ + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; +}; + +struct s5k6aa_interval { + u16 reg_fr_time; + struct v4l2_fract interval; + /* Maximum rectangle for the interval */ + struct v4l2_frmsize_discrete size; +}; + +struct s5k6aa { + struct v4l2_subdev sd; + struct media_pad pad; + + enum v4l2_mbus_type bus_type; + u8 mipi_lanes; + + int (*s_power)(int enable); + struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; + struct s5k6aa_gpio gpio[GPIO_NUM]; + + /* external master clock frequency */ + unsigned long mclk_frequency; + /* ISP internal master clock frequency */ + u16 clk_fop; + /* output pixel clock frequency range */ + u16 pclk_fmin; + u16 pclk_fmax; + + unsigned int inv_hflip:1; + unsigned int inv_vflip:1; + + /* protects the struct members below */ + struct mutex lock; + + /* sensor matrix scan window */ + struct v4l2_rect ccd_rect; + + struct s5k6aa_ctrls ctrls; + struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; + struct s5k6aa_preset *preset; + const struct s5k6aa_interval *fiv; + + unsigned int streaming:1; + unsigned int apply_cfg:1; + unsigned int apply_crop:1; + unsigned int power; +}; + +static struct s5k6aa_regval s5k6aa_analog_config[] = { + /* Analog settings */ + { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, + { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, + { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, + { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, + { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, + { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, + { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, + { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, + { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, + { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, + { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, + { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, + { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, +}; + +/* TODO: Add RGB888 and Bayer format */ +static const struct s5k6aa_pixfmt s5k6aa_formats[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, + /* range 16-240 */ + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, + { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, +}; + +static const struct s5k6aa_interval s5k6aa_intervals[] = { + { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ + { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ + { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ + { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ + { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ +}; + +#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; +} + +static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5k6aa, sd); +} + +/* Set initial values for all preview presets */ +static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) +{ + struct s5k6aa_preset *preset = &s5k6aa->presets[0]; + int i; + + for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { + preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; + preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; + preset->mbus_fmt.code = s5k6aa_formats[0].code; + preset->index = i; + preset->clk_id = 0; + preset++; + } + + s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; + s5k6aa->preset = &s5k6aa->presets[0]; +} + +static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) +{ + u8 wbuf[2] = {addr >> 8, addr & 0xFF}; + struct i2c_msg msg[2]; + u8 rbuf[2]; + int ret; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = wbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rbuf; + + ret = i2c_transfer(client->adapter, msg, 2); + *val = be16_to_cpu(*((u16 *)rbuf)); + + v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); + + return ret == 2 ? 0 : ret; +} + +static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) +{ + u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; + + int ret = i2c_master_send(client, buf, 4); + v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); + + return ret == 4 ? 0 : ret; +} + +/* The command register write, assumes Command_Wr_addH = 0x7000. */ +static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) +{ + int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); +} + +/* The command register read, assumes Command_Rd_addH = 0x7000. */ +static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) +{ + int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); +} + +static int s5k6aa_write_array(struct v4l2_subdev *sd, + const struct s5k6aa_regval *msg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr_incr = 0; + int ret = 0; + + while (msg->addr != S5K6AA_TERM) { + if (addr_incr != 2) + ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, + msg->addr); + if (ret) + break; + ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); + if (ret) + break; + /* Assume that msg->addr is always less than 0xfffc */ + addr_incr = (msg + 1)->addr - msg->addr; + msg++; + } + + return ret; +} + +/* Configure the AHB high address bytes for GTG registers access */ +static int s5k6aa_set_ahb_address(struct i2c_client *client) +{ + int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); + if (ret) + return ret; + ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); + if (ret) + return ret; + return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); +} + +/** + * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration + * + * Configure the internal ISP PLL for the required output frequency. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned long fmclk = s5k6aa->mclk_frequency / 1000; + u16 status; + int ret; + + if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, + "Invalid clock frequency: %ld\n", fmclk)) + return -EINVAL; + + s5k6aa->pclk_fmin = PCLK_FREQ_MIN; + s5k6aa->pclk_fmax = PCLK_FREQ_MAX; + s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; + + /* External input clock frequency in kHz */ + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); + if (!ret) + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); + if (!ret) + ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); + /* Internal PLL frequency */ + if (!ret) + ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); + if (!ret) + ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); + if (!ret) + ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); + + return ret ? ret : (status ? -EINVAL : 0); +} + +/* Set horizontal and vertical image flipping */ +static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int index = s5k6aa->preset->index; + + unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; + unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); + + return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); +} + +/* Configure auto/manual white balance and R/G/B gains */ +static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + u16 reg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); + + if (!ret && !awb) { + ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); + } + if (!ret) { + reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; + ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); + } + + return ret; +} + +/* Program FW with exposure time, 'exposure' in us units */ +static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) +{ + unsigned int time = exposure / 10; + + int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); + if (!ret) + ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); +} + +static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) +{ + int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); +} + +/* Set auto/manual exposure and total gain */ +static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned int exp_time = s5k6aa->ctrls.exposure->val; + u16 auto_alg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", + exp_time, value, auto_alg); + + if (value == V4L2_EXPOSURE_AUTO) { + auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; + } else { + ret = s5k6aa_set_user_exposure(c, exp_time); + if (ret) + return ret; + ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); + if (ret) + return ret; + auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); + } + + return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 auto_alg; + int ret; + + ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { + auto_alg |= AALG_FLICKER_EN_MASK; + } else { + auto_alg &= ~AALG_FLICKER_EN_MASK; + /* The V4L2_CID_LINE_FREQUENCY control values match + * the register values */ + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); + if (ret) + return ret; + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); + if (ret) + return ret; + } + + return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + static const struct v4l2_control colorfx[] = { + { V4L2_COLORFX_NONE, 0 }, + { V4L2_COLORFX_BW, 1 }, + { V4L2_COLORFX_NEGATIVE, 2 }, + { V4L2_COLORFX_SEPIA, 3 }, + { V4L2_COLORFX_SKY_BLUE, 4 }, + { V4L2_COLORFX_SKETCH, 5 }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) { + if (colorfx[i].id == val) + return s5k6aa_write(client, REG_G_SPEC_EFFECTS, + colorfx[i].value); + } + return -EINVAL; +} + +static int s5k6aa_preview_config_status(struct i2c_client *client) +{ + u16 error = 0; + int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); + + v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); + return ret ? ret : (error ? -EINVAL : 0); +} + +static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) + if (mf->colorspace == s5k6aa_formats[i].colorspace && + mf->code == s5k6aa_formats[i].code) + return i; + return 0; +} + +static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); + int ret; + + ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), + preset->mbus_fmt.width); + if (!ret) + ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), + preset->mbus_fmt.height); + if (!ret) + ret = s5k6aa_write(client, REG_P_FMT(preset->index), + s5k6aa_formats[fmt_index].reg_p_fmt); + return ret; +} + +static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct v4l2_rect *r = &s5k6aa->ccd_rect; + int ret; + + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); + if (!ret) + ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); + if (!ret) + s5k6aa->apply_crop = 0; + + return ret; +} + +/** + * s5k6aa_configure_video_bus - configure the video output interface + * @bus_type: video bus type: parallel or MIPI-CSI + * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) + * + * Note: Only parallel bus operation has been tested. + */ +static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, + enum v4l2_mbus_type bus_type, int nlanes) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 cfg = 0; + int ret; + + /* + * TODO: The sensor is supposed to support BT.601 and BT.656 + * but there is nothing indicating how to switch between both + * in the datasheet. For now default BT.601 interface is assumed. + */ + if (bus_type == V4L2_MBUS_CSI2) + cfg = nlanes; + else if (bus_type != V4L2_MBUS_PARALLEL) + return -EINVAL; + + ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); + if (ret) + return ret; + return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); +} + +/* This function should be called when switching to new user configuration set*/ +static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, + int cid) +{ + unsigned long end = jiffies + msecs_to_jiffies(timeout); + u16 reg = 1; + int ret; + + ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); + if (!ret) + ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + if (!ret) + ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); + if (timeout == 0) + return ret; + + while (ret >= 0 && time_is_after_jiffies(end)) { + ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); + if (!reg) + return 0; + usleep_range(1000, 5000); + } + return ret ? ret : -ETIMEDOUT; +} + +/** + * s5k6aa_set_prev_config - write user preview register set + * + * Configure output resolution and color fromat, pixel clock + * frequency range, device frame rate type and frame period range. + */ +static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int idx = preset->index; + u16 frame_rate_q; + int ret; + + if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) + frame_rate_q = FR_RATE_Q_BEST_FRRATE; + else + frame_rate_q = FR_RATE_Q_BEST_QUALITY; + + ret = s5k6aa_set_output_framefmt(s5k6aa, preset); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), + preset->clk_id); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), + FR_RATE_DYNAMIC); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), + frame_rate_q); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time + 33); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time - 33); + if (!ret) + ret = s5k6aa_new_config_sync(client, 250, idx); + if (!ret) + ret = s5k6aa_preview_config_status(client); + if (!ret) + s5k6aa->apply_cfg = 0; + + v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", + s5k6aa->fiv->reg_fr_time, ret); + return ret; +} + +/** + * s5k6aa_initialize_isp - basic ISP MCU initialization + * + * Configure AHB addresses for registers read/write; configure PLLs for + * required output pixel clock. The ISP power supply needs to be already + * enabled, with an optional H/W reset. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + s5k6aa->apply_crop = 1; + s5k6aa->apply_cfg = 1; + msleep(100); + + ret = s5k6aa_set_ahb_address(client); + if (ret) + return ret; + ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, + s5k6aa->mipi_lanes); + if (ret) + return ret; + ret = s5k6aa_write_array(sd, s5k6aa_analog_config); + if (ret) + return ret; + msleep(20); + + return s5k6aa_configure_pixel_clocks(s5k6aa); +} + +static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) +{ + if (!gpio_is_valid(priv->gpio[id].gpio)) + return 0; + gpio_set_value(priv->gpio[id].gpio, !!val); + return 1; +} + +static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); +} + +static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); +} + +static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) +{ + int ret; + + ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + if (ret) + return ret; + if (s5k6aa_gpio_deassert(s5k6aa, STBY)) + usleep_range(150, 200); + + if (s5k6aa->s_power) + ret = s5k6aa->s_power(1); + usleep_range(4000, 4000); + + if (s5k6aa_gpio_deassert(s5k6aa, RST)) + msleep(20); + + return ret; +} + +static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) +{ + int ret; + + if (s5k6aa_gpio_assert(s5k6aa, RST)) + usleep_range(100, 150); + + if (s5k6aa->s_power) { + ret = s5k6aa->s_power(0); + if (ret) + return ret; + } + if (s5k6aa_gpio_assert(s5k6aa, STBY)) + usleep_range(50, 100); + s5k6aa->streaming = 0; + + return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +} + +/* + * V4L2 subdev core and video operations + */ +static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (!on == s5k6aa->power) { + if (on) { + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) + ret = s5k6aa_initialize_isp(sd); + } else { + ret = __s5k6aa_power_off(s5k6aa); + } + + if (!ret) + s5k6aa->power += on ? 1 : -1; + } + + mutex_unlock(&s5k6aa->lock); + + if (!on || ret || s5k6aa->power != 1) + return ret; + + return v4l2_ctrl_handler_setup(sd->ctrl_handler); +} + +static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int ret = 0; + + ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); + if (!ret) + ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); + if (!ret) + s5k6aa->streaming = enable; + + return ret; +} + +static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (s5k6aa->streaming == !on) { + if (!ret && s5k6aa->apply_cfg) + ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); + if (s5k6aa->apply_crop) + ret = s5k6aa_set_input_params(s5k6aa); + if (!ret) + ret = __s5k6aa_stream(s5k6aa, !!on); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + mutex_lock(&s5k6aa->lock); + fi->interval = s5k6aa->fiv->interval; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; + const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; + unsigned int err, min_err = UINT_MAX; + unsigned int i, fr_time; + + if (fi->interval.denominator == 0) + return -EINVAL; + + fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { + const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; + + if (mbus_fmt->width > iv->size.width || + mbus_fmt->height > iv->size.height) + continue; + + err = abs(iv->reg_fr_time - fr_time); + if (err < min_err) { + fiv = iv; + min_err = err; + } + } + s5k6aa->fiv = fiv; + + v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", + fiv->reg_fr_time * 100); + return 0; +} + +static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_set_frame_interval(s5k6aa, fi); + s5k6aa->apply_cfg = 1; + + mutex_unlock(&s5k6aa->lock); + return ret; +} + +/* + * V4L2 subdev pad level and video operations + */ +static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + const struct s5k6aa_interval *fi; + int ret = 0; + + if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) + return -EINVAL; + + v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &fie->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + mutex_lock(&s5k6aa->lock); + fi = &s5k6aa_intervals[fie->index]; + if (fie->width > fi->size.width || fie->height > fi->size.height) + ret = -EINVAL; + else + fie->interval = fi->interval; + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(s5k6aa_formats)) + return -EINVAL; + + code->code = s5k6aa_formats[code->index].code; + return 0; +} + +static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i = ARRAY_SIZE(s5k6aa_formats); + + if (fse->index > 0) + return -EINVAL; + + while (--i) + if (fse->code == s5k6aa_formats[i].code) + break; + + fse->code = s5k6aa_formats[i].code; + fse->min_width = S5K6AA_WIN_WIDTH_MIN; + fse->max_width = S5K6AA_WIN_WIDTH_MAX; + fse->max_height = S5K6AA_WIN_HEIGHT_MIN; + fse->min_height = S5K6AA_WIN_HEIGHT_MAX; + + return 0; +} + +static struct v4l2_rect * +__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &s5k6aa->ccd_rect; + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, 0); + + return NULL; +} + +static void s5k6aa_try_format(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int index; + + v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &mf->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + if (mf->colorspace != V4L2_COLORSPACE_JPEG && + mf->colorspace != V4L2_COLORSPACE_REC709) + mf->colorspace = V4L2_COLORSPACE_JPEG; + + index = s5k6aa_get_pixfmt_index(s5k6aa, mf); + + mf->colorspace = s5k6aa_formats[index].colorspace; + mf->code = s5k6aa_formats[index].code; + mf->field = V4L2_FIELD_NONE; +} + +static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + + memset(fmt->reserved, 0, sizeof(fmt->reserved)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + return 0; + } + + mutex_lock(&s5k6aa->lock); + fmt->format = s5k6aa->preset->mbus_fmt; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct s5k6aa_preset *preset = s5k6aa->preset; + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop; + int ret = 0; + + mutex_lock(&s5k6aa->lock); + s5k6aa_try_format(s5k6aa, &fmt->format); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + crop = v4l2_subdev_get_try_crop(fh, 0); + } else { + if (s5k6aa->streaming) { + ret = -EBUSY; + } else { + mf = &preset->mbus_fmt; + crop = &s5k6aa->ccd_rect; + s5k6aa->apply_cfg = 1; + } + } + + if (ret == 0) { + struct v4l2_subdev_frame_interval fiv = { + .interval = {0, 1} + }; + + *mf = fmt->format; + /* + * Make sure the crop window is valid, i.e. its size is + * greater than the output window, as the ISP supports + * only down-scaling. + */ + crop->width = clamp_t(unsigned int, crop->width, mf->width, + S5K6AA_WIN_WIDTH_MAX); + crop->height = clamp_t(unsigned int, crop->height, mf->height, + S5K6AA_WIN_HEIGHT_MAX); + crop->left = clamp_t(unsigned int, crop->left, 0, + S5K6AA_WIN_WIDTH_MAX - crop->width); + crop->top = clamp_t(unsigned int, crop->top, 0, + S5K6AA_WIN_HEIGHT_MAX - crop->height); + + /* Reset to minimum possible frame interval */ + ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_rect *rect; + + memset(crop->reserved, 0, sizeof(crop->reserved)); + mutex_lock(&s5k6aa->lock); + + rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + if (rect) + crop->rect = *rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", + rect->left, rect->top, rect->width, rect->height); + + return 0; +} + +static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + unsigned int max_x, max_y; + struct v4l2_rect *crop_r; + + mutex_lock(&s5k6aa->lock); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + mf = &s5k6aa->preset->mbus_fmt; + s5k6aa->apply_crop = 1; + } else { + mf = v4l2_subdev_get_try_format(fh, 0); + } + v4l_bound_align_image(&crop->rect.width, mf->width, + S5K6AA_WIN_WIDTH_MAX, 1, + &crop->rect.height, mf->height, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; + max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; + + crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); + crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); + + *crop_r = crop->rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", + crop_r->left, crop_r->top, crop_r->width, crop_r->height); + + return 0; +} + +static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { + .enum_mbus_code = s5k6aa_enum_mbus_code, + .enum_frame_size = s5k6aa_enum_frame_size, + .enum_frame_interval = s5k6aa_enum_frame_interval, + .get_fmt = s5k6aa_get_fmt, + .set_fmt = s5k6aa_set_fmt, + .get_crop = s5k6aa_get_crop, + .set_crop = s5k6aa_set_crop, +}; + +static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { + .g_frame_interval = s5k6aa_g_frame_interval, + .s_frame_interval = s5k6aa_s_frame_interval, + .s_stream = s5k6aa_s_stream, +}; + +/* + * V4L2 subdev controls + */ + +static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int idx, err = 0; + + v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); + + mutex_lock(&s5k6aa->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (s5k6aa->power == 0) + goto unlock; + idx = s5k6aa->preset->index; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = s5k6aa_set_awb(s5k6aa, ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); + break; + + case V4L2_CID_COLORFX: + err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); + break; + + case V4L2_CID_HFLIP: + err = s5k6aa_set_mirror(s5k6aa, ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); + break; + + case V4L2_CID_SATURATION: + err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); + break; + + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + } +unlock: + mutex_unlock(&s5k6aa->lock); + return err; +} + +static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { + .s_ctrl = s5k6aa_s_ctrl, +}; + +static int s5k6aa_log_status(struct v4l2_subdev *sd) +{ + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + return 0; +} + +#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) +#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) +#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) + +static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { + { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_RED_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_GREEN_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_BLUE_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, +}; + +static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) +{ + const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + int ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret) + return ret; + /* Auto white balance cluster */ + ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); + ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); + ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); + v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); + + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &ctrls->hflip); + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + /* Exposure time: x 1 us */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 6000000U, 1, 100000U); + /* Total gain: 256 <=> 1x */ + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, + 0, 256, 1, 256); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, 256, 1, 0); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + s5k6aa->sd.ctrl_handler = hdl; + return 0; +} + +/* + * V4L2 subdev internal operations + */ +static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); + + format->colorspace = s5k6aa_formats[0].colorspace; + format->code = s5k6aa_formats[0].code; + format->width = S5K6AA_OUT_WIDTH_DEF; + format->height = S5K6AA_OUT_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + + crop->width = S5K6AA_WIN_WIDTH_MAX; + crop->height = S5K6AA_WIN_HEIGHT_MAX; + crop->left = 0; + crop->top = 0; + + return 0; +} + +static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 api_ver = 0, fw_rev = 0; + + int ret = s5k6aa_set_ahb_address(client); + + if (!ret) + ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); + if (!ret) + ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); + if (ret) { + v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); + return ret; + } + + v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", + api_ver, fw_rev); + + return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; +} + +static int s5k6aa_registered(struct v4l2_subdev *sd) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) { + msleep(100); + ret = s5k6aa_check_fw_revision(s5k6aa); + __s5k6aa_power_off(s5k6aa); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { + .registered = s5k6aa_registered, + .open = s5k6aa_open, +}; + +static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { + .s_power = s5k6aa_set_power, + .log_status = s5k6aa_log_status, +}; + +static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { + .core = &s5k6aa_core_ops, + .pad = &s5k6aa_pad_ops, + .video = &s5k6aa_video_ops, +}; + +/* + * GPIO setup + */ +static int s5k6aa_configure_gpio(int nr, int val, const char *name) +{ + unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + int ret; + + if (!gpio_is_valid(nr)) + return 0; + ret = gpio_request_one(nr, flags, name); + if (!ret) + gpio_export(nr, 0); + return ret; +} + +static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) { + if (!gpio_is_valid(s5k6aa->gpio[i].gpio)) + continue; + gpio_free(s5k6aa->gpio[i].gpio); + s5k6aa->gpio[i].gpio = -EINVAL; + } +} + +static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, + const struct s5k6aa_platform_data *pdata) +{ + const struct s5k6aa_gpio *gpio = &pdata->gpio_stby; + int ret; + + s5k6aa->gpio[STBY].gpio = -EINVAL; + s5k6aa->gpio[RST].gpio = -EINVAL; + + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[STBY] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + gpio = &pdata->gpio_reset; + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[RST] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + return 0; +} + +static int s5k6aa_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct s5k6aa_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct s5k6aa *s5k6aa; + int i, ret; + + if (pdata == NULL) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + + s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL); + if (!s5k6aa) + return -ENOMEM; + + mutex_init(&s5k6aa->lock); + + s5k6aa->mclk_frequency = pdata->mclk_frequency; + s5k6aa->bus_type = pdata->bus_type; + s5k6aa->mipi_lanes = pdata->nlanes; + s5k6aa->s_power = pdata->set_power; + s5k6aa->inv_hflip = pdata->horiz_flip; + s5k6aa->inv_vflip = pdata->vert_flip; + + sd = &s5k6aa->sd; + v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + + sd->internal_ops = &s5k6aa_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); + if (ret) + return ret; + + ret = s5k6aa_configure_gpios(s5k6aa, pdata); + if (ret) + goto out_err2; + + for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) + s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; + + ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, + s5k6aa->supplies); + if (ret) { + dev_err(&client->dev, "Failed to get regulators\n"); + goto out_err3; + } + + ret = s5k6aa_initialize_ctrls(s5k6aa); + if (ret) + goto out_err4; + + s5k6aa_presets_data_init(s5k6aa); + + s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; + s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; + s5k6aa->ccd_rect.left = 0; + s5k6aa->ccd_rect.top = 0; + + return 0; + +out_err4: + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +out_err3: + s5k6aa_free_gpios(s5k6aa); +out_err2: + media_entity_cleanup(&s5k6aa->sd.entity); + return ret; +} + +static int s5k6aa_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + s5k6aa_free_gpios(s5k6aa); + + return 0; +} + +static const struct i2c_device_id s5k6aa_id[] = { + { DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, s5k6aa_id); + + +static struct i2c_driver s5k6aa_i2c_driver = { + .driver = { + .name = DRIVER_NAME + }, + .probe = s5k6aa_probe, + .remove = s5k6aa_remove, + .id_table = s5k6aa_id, +}; + +module_i2c_driver(s5k6aa_i2c_driver); + +MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c new file mode 100644 index 000000000000..0caac50d7cf4 --- /dev/null +++ b/drivers/media/i2c/saa6588.c @@ -0,0 +1,542 @@ +/* + Driver for SAA6588 RDS decoder + + (c) 2005 Hans J. Koch + + This program is free software; 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 +#include +#include + +#include +#include +#include + + +/* insmod options */ +static unsigned int debug; +static unsigned int xtal; +static unsigned int mmbs; +static unsigned int plvl; +static unsigned int bufblocks = 100; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); +module_param(xtal, int, 0); +MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); +module_param(mmbs, int, 0); +MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on"); +module_param(plvl, int, 0); +MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); +module_param(bufblocks, int, 0); +MODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100"); + +MODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder"); +MODULE_AUTHOR("Hans J. Koch "); + +MODULE_LICENSE("GPL"); + +/* ---------------------------------------------------------------------- */ + +#define UNSET (-1U) +#define PREFIX "saa6588: " +#define dprintk if (debug) printk + +struct saa6588 { + struct v4l2_subdev sd; + struct delayed_work work; + spinlock_t lock; + unsigned char *buffer; + unsigned int buf_size; + unsigned int rd_index; + unsigned int wr_index; + unsigned int block_count; + unsigned char last_blocknum; + wait_queue_head_t read_queue; + int data_available_for_read; + u8 sync; +}; + +static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa6588, sd); +} + +/* ---------------------------------------------------------------------- */ + +/* + * SAA6588 defines + */ + +/* Initialization and mode control byte (0w) */ + +/* bit 0+1 (DAC0/DAC1) */ +#define cModeStandard 0x00 +#define cModeFastPI 0x01 +#define cModeReducedRequest 0x02 +#define cModeInvalid 0x03 + +/* bit 2 (RBDS) */ +#define cProcessingModeRDS 0x00 +#define cProcessingModeRBDS 0x04 + +/* bit 3+4 (SYM0/SYM1) */ +#define cErrCorrectionNone 0x00 +#define cErrCorrection2Bits 0x08 +#define cErrCorrection5Bits 0x10 +#define cErrCorrectionNoneRBDS 0x18 + +/* bit 5 (NWSY) */ +#define cSyncNormal 0x00 +#define cSyncRestart 0x20 + +/* bit 6 (TSQD) */ +#define cSigQualityDetectOFF 0x00 +#define cSigQualityDetectON 0x40 + +/* bit 7 (SQCM) */ +#define cSigQualityTriggered 0x00 +#define cSigQualityContinous 0x80 + +/* Pause level and flywheel control byte (1w) */ + +/* bits 0..5 (FEB0..FEB5) */ +#define cFlywheelMaxBlocksMask 0x3F +#define cFlywheelDefault 0x20 + +/* bits 6+7 (PL0/PL1) */ +#define cPauseLevel_11mV 0x00 +#define cPauseLevel_17mV 0x40 +#define cPauseLevel_27mV 0x80 +#define cPauseLevel_43mV 0xC0 + +/* Pause time/oscillator frequency/quality detector control byte (1w) */ + +/* bits 0..4 (SQS0..SQS4) */ +#define cQualityDetectSensMask 0x1F +#define cQualityDetectDefault 0x0F + +/* bit 5 (SOSC) */ +#define cSelectOscFreqOFF 0x00 +#define cSelectOscFreqON 0x20 + +/* bit 6+7 (PTF0/PTF1) */ +#define cOscFreq_4332kHz 0x00 +#define cOscFreq_8664kHz 0x40 +#define cOscFreq_12996kHz 0x80 +#define cOscFreq_17328kHz 0xC0 + +/* ---------------------------------------------------------------------- */ + +static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) +{ + int i; + + if (s->rd_index == s->wr_index) { + if (debug > 2) + dprintk(PREFIX "Read: buffer empty.\n"); + return 0; + } + + if (debug > 2) { + dprintk(PREFIX "Read: "); + for (i = s->rd_index; i < s->rd_index + 3; i++) + dprintk("0x%02x ", s->buffer[i]); + } + + if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3)) + return -EFAULT; + + s->rd_index += 3; + if (s->rd_index >= s->buf_size) + s->rd_index = 0; + s->block_count--; + + if (debug > 2) + dprintk("%d blocks total.\n", s->block_count); + + return 1; +} + +static void read_from_buf(struct saa6588 *s, struct saa6588_command *a) +{ + unsigned long flags; + + unsigned char __user *buf_ptr = a->buffer; + unsigned int i; + unsigned int rd_blocks; + + a->result = 0; + if (!a->buffer) + return; + + while (!s->data_available_for_read) { + int ret = wait_event_interruptible(s->read_queue, + s->data_available_for_read); + if (ret == -ERESTARTSYS) { + a->result = -EINTR; + return; + } + } + + spin_lock_irqsave(&s->lock, flags); + rd_blocks = a->block_count; + if (rd_blocks > s->block_count) + rd_blocks = s->block_count; + + if (!rd_blocks) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + for (i = 0; i < rd_blocks; i++) { + if (block_to_user_buf(s, buf_ptr)) { + buf_ptr += 3; + a->result++; + } else + break; + } + a->result *= 3; + s->data_available_for_read = (s->block_count > 0); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) +{ + unsigned int i; + + if (debug > 3) + dprintk(PREFIX "New block: "); + + for (i = 0; i < 3; ++i) { + if (debug > 3) + dprintk("0x%02x ", blockbuf[i]); + s->buffer[s->wr_index] = blockbuf[i]; + s->wr_index++; + } + + if (s->wr_index >= s->buf_size) + s->wr_index = 0; + + if (s->wr_index == s->rd_index) { + s->rd_index += 3; + if (s->rd_index >= s->buf_size) + s->rd_index = 0; + } else + s->block_count++; + + if (debug > 3) + dprintk("%d blocks total.\n", s->block_count); +} + +static void saa6588_i2c_poll(struct saa6588 *s) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s->sd); + unsigned long flags; + unsigned char tmpbuf[6]; + unsigned char blocknum; + unsigned char tmp; + + /* Although we only need 3 bytes, we have to read at least 6. + SAA6588 returns garbage otherwise. */ + if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { + if (debug > 1) + dprintk(PREFIX "read error!\n"); + return; + } + + s->sync = tmpbuf[0] & 0x10; + if (!s->sync) + return; + blocknum = tmpbuf[0] >> 5; + if (blocknum == s->last_blocknum) { + if (debug > 3) + dprintk("Saw block %d again.\n", blocknum); + return; + } + + s->last_blocknum = blocknum; + + /* + Byte order according to v4l2 specification: + + Byte 0: Least Significant Byte of RDS Block + Byte 1: Most Significant Byte of RDS Block + Byte 2 Bit 7: Error bit. Indicates that an uncorrectable error + occurred during reception of this block. + Bit 6: Corrected bit. Indicates that an error was + corrected for this data block. + Bits 5-3: Same as bits 0-2. + Bits 2-0: Block number. + + SAA6588 byte order is Status-MSB-LSB, so we have to swap the + first and the last of the 3 bytes block. + */ + + tmp = tmpbuf[2]; + tmpbuf[2] = tmpbuf[0]; + tmpbuf[0] = tmp; + + /* Map 'Invalid block E' to 'Invalid Block' */ + if (blocknum == 6) + blocknum = V4L2_RDS_BLOCK_INVALID; + /* And if are not in mmbs mode, then 'Block E' is also mapped + to 'Invalid Block'. As far as I can tell MMBS is discontinued, + and if there is ever a need to support E blocks, then please + contact the linux-media mailinglist. */ + else if (!mmbs && blocknum == 5) + blocknum = V4L2_RDS_BLOCK_INVALID; + tmp = blocknum; + tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */ + if ((tmpbuf[2] & 0x03) == 0x03) + tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */ + else if ((tmpbuf[2] & 0x03) != 0x00) + tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */ + tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */ + + spin_lock_irqsave(&s->lock, flags); + block_to_buf(s, tmpbuf); + spin_unlock_irqrestore(&s->lock, flags); + s->data_available_for_read = 1; + wake_up_interruptible(&s->read_queue); +} + +static void saa6588_work(struct work_struct *work) +{ + struct saa6588 *s = container_of(work, struct saa6588, work.work); + + saa6588_i2c_poll(s); + schedule_delayed_work(&s->work, msecs_to_jiffies(20)); +} + +static void saa6588_configure(struct saa6588 *s) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s->sd); + unsigned char buf[3]; + int rc; + + buf[0] = cSyncRestart; + if (mmbs) + buf[0] |= cProcessingModeRBDS; + + buf[1] = cFlywheelDefault; + switch (plvl) { + case 0: + buf[1] |= cPauseLevel_11mV; + break; + case 1: + buf[1] |= cPauseLevel_17mV; + break; + case 2: + buf[1] |= cPauseLevel_27mV; + break; + case 3: + buf[1] |= cPauseLevel_43mV; + break; + default: /* nothing */ + break; + } + + buf[2] = cQualityDetectDefault | cSelectOscFreqON; + + switch (xtal) { + case 0: + buf[2] |= cOscFreq_4332kHz; + break; + case 1: + buf[2] |= cOscFreq_8664kHz; + break; + case 2: + buf[2] |= cOscFreq_12996kHz; + break; + case 3: + buf[2] |= cOscFreq_17328kHz; + break; + default: /* nothing */ + break; + } + + dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", + buf[0], buf[1], buf[2]); + + rc = i2c_master_send(client, buf, 3); + if (rc != 3) + printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); +} + +/* ---------------------------------------------------------------------- */ + +static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct saa6588 *s = to_saa6588(sd); + struct saa6588_command *a = arg; + + switch (cmd) { + /* --- open() for /dev/radio --- */ + case SAA6588_CMD_OPEN: + a->result = 0; /* return error if chip doesn't work ??? */ + break; + /* --- close() for /dev/radio --- */ + case SAA6588_CMD_CLOSE: + s->data_available_for_read = 1; + wake_up_interruptible(&s->read_queue); + a->result = 0; + break; + /* --- read() for /dev/radio --- */ + case SAA6588_CMD_READ: + read_from_buf(s, a); + break; + /* --- poll() for /dev/radio --- */ + case SAA6588_CMD_POLL: + a->result = 0; + if (s->data_available_for_read) { + a->result |= POLLIN | POLLRDNORM; + } + poll_wait(a->instance, &s->read_queue, a->event_list); + break; + + default: + /* nothing */ + return -ENOIOCTLCMD; + } + return 0; +} + +static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; + if (s->sync) + vt->rxsubchans |= V4L2_TUNER_SUB_RDS; + return 0; +} + +static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + saa6588_configure(s); + return 0; +} + +static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa6588_core_ops = { + .g_chip_ident = saa6588_g_chip_ident, + .ioctl = saa6588_ioctl, +}; + +static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = { + .g_tuner = saa6588_g_tuner, + .s_tuner = saa6588_s_tuner, +}; + +static const struct v4l2_subdev_ops saa6588_ops = { + .core = &saa6588_core_ops, + .tuner = &saa6588_tuner_ops, +}; + +/* ---------------------------------------------------------------------- */ + +static int saa6588_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa6588 *s; + struct v4l2_subdev *sd; + + v4l_info(client, "saa6588 found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return -ENOMEM; + + s->buf_size = bufblocks * 3; + + s->buffer = kmalloc(s->buf_size, GFP_KERNEL); + if (s->buffer == NULL) { + kfree(s); + return -ENOMEM; + } + sd = &s->sd; + v4l2_i2c_subdev_init(sd, client, &saa6588_ops); + spin_lock_init(&s->lock); + s->block_count = 0; + s->wr_index = 0; + s->rd_index = 0; + s->last_blocknum = 0xff; + init_waitqueue_head(&s->read_queue); + s->data_available_for_read = 0; + + saa6588_configure(s); + + /* start polling via eventd */ + INIT_DELAYED_WORK(&s->work, saa6588_work); + schedule_delayed_work(&s->work, 0); + return 0; +} + +static int saa6588_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa6588 *s = to_saa6588(sd); + + v4l2_device_unregister_subdev(sd); + + cancel_delayed_work_sync(&s->work); + + kfree(s->buffer); + kfree(s); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa6588_id[] = { + { "saa6588", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa6588_id); + +static struct i2c_driver saa6588_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa6588", + }, + .probe = saa6588_probe, + .remove = saa6588_remove, + .id_table = saa6588_id, +}; + +module_i2c_driver(saa6588_driver); diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c new file mode 100644 index 000000000000..51cd4c8f0520 --- /dev/null +++ b/drivers/media/i2c/saa7110.c @@ -0,0 +1,494 @@ +/* + * saa7110 - Philips SAA7110(A) video decoder driver + * + * Copyright (C) 1998 Pauline Middelink + * + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * This program is free software; 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 +#include +#include +#include + +MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); +MODULE_AUTHOR("Pauline Middelink"); +MODULE_LICENSE("GPL"); + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ +#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */ + +#define SAA7110_NR_REG 0x35 + +struct saa7110 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg[SAA7110_NR_REG]; + + v4l2_std_id norm; + int input; + int enable; + + wait_queue_head_t wq; +}; + +static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7110, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa7110, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ +/* I2C support functions */ +/* ----------------------------------------------------------------------- */ + +static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7110 *decoder = to_saa7110(sd); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7110 *decoder = to_saa7110(sd); + int ret = -1; + u8 reg = *data; /* first register to write to */ + + /* Sanity check */ + if (reg + (len - 1) > SAA7110_NR_REG) + return ret; + + /* the saa7110 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + ret = i2c_master_send(client, data, len); + + /* Cache the written data */ + memcpy(decoder->reg + reg, data + 1, len - 1); + } else { + for (++data, --len; len; len--) { + ret = saa7110_write(sd, reg++, *data++); + if (ret < 0) + break; + } + } + + return ret; +} + +static inline int saa7110_read(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte(client); +} + +/* ----------------------------------------------------------------------- */ +/* SAA7110 functions */ +/* ----------------------------------------------------------------------- */ + +#define FRESP_06H_COMPST 0x03 /*0x13*/ +#define FRESP_06H_SVIDEO 0x83 /*0xC0*/ + + +static int saa7110_selmux(struct v4l2_subdev *sd, int chan) +{ + static const unsigned char modes[9][8] = { + /* mode 0 */ + {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, + 0x44, 0x75, 0x16}, + /* mode 1 */ + {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, + 0x44, 0x75, 0x16}, + /* mode 2 */ + {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, + 0x60, 0xB5, 0x05}, + /* mode 3 */ + {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, + 0x60, 0xB5, 0x05}, + /* mode 4 */ + {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, + 0x60, 0xB5, 0x03}, + /* mode 5 */ + {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, + 0x60, 0xB5, 0x03}, + /* mode 6 */ + {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, + 0x44, 0x75, 0x12}, + /* mode 7 */ + {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, + 0x60, 0xB5, 0x14}, + /* mode 8 */ + {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, + 0x44, 0x75, 0x21} + }; + struct saa7110 *decoder = to_saa7110(sd); + const unsigned char *ptr = modes[chan]; + + saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */ + saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */ + saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */ + saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */ + saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */ + saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */ + saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */ + saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */ + decoder->input = chan; + + return 0; +} + +static const unsigned char initseq[1 + SAA7110_NR_REG] = { + 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, + /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, + /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, + /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, + /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, + /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 +}; + +static v4l2_std_id determine_norm(struct v4l2_subdev *sd) +{ + DEFINE_WAIT(wait); + struct saa7110 *decoder = to_saa7110(sd); + int status; + + /* mode changed, start automatic detection */ + saa7110_write_block(sd, initseq, sizeof(initseq)); + saa7110_selmux(sd, decoder->input); + prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(250)); + finish_wait(&decoder->wq, &wait); + status = saa7110_read(sd); + if (status & 0x40) { + v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status); + return decoder->norm; /* no change*/ + } + if ((status & 3) == 0) { + saa7110_write(sd, 0x06, 0x83); + if (status & 0x20) { + v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status); + /*saa7110_write(sd,0x2E,0x81);*/ + return V4L2_STD_NTSC; + } + v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status); + /*saa7110_write(sd,0x2E,0x9A);*/ + return V4L2_STD_PAL; + } + /*saa7110_write(sd,0x06,0x03);*/ + if (status & 0x20) { /* 60Hz */ + v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status); + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x50); + saa7110_write(sd, 0x11, 0x2C); + /*saa7110_write(sd,0x2E,0x81);*/ + return V4L2_STD_NTSC; + } + + /* 50Hz -> PAL/SECAM */ + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd,0x2E,0x9A);*/ + + prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(250)); + finish_wait(&decoder->wq, &wait); + + status = saa7110_read(sd); + if ((status & 0x03) == 0x01) { + v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status); + saa7110_write(sd, 0x0D, 0x87); + return V4L2_STD_SECAM; + } + v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status); + return V4L2_STD_PAL; +} + +static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) +{ + struct saa7110 *decoder = to_saa7110(sd); + int res = V4L2_IN_ST_NO_SIGNAL; + int status = saa7110_read(sd); + + v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", + status, (unsigned long long)decoder->norm); + if (!(status & 0x40)) + res = 0; + if (!(status & 0x03)) + res |= V4L2_IN_ST_NO_COLOR; + + *pstatus = res; + return 0; +} + +static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + *(v4l2_std_id *)std = determine_norm(sd); + return 0; +} + +static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (decoder->norm != std) { + decoder->norm = std; + /*saa7110_write(sd, 0x06, 0x03);*/ + if (std & V4L2_STD_NTSC) { + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x50); + saa7110_write(sd, 0x11, 0x2C); + /*saa7110_write(sd, 0x2E, 0x81);*/ + v4l2_dbg(1, debug, sd, "switched to NTSC\n"); + } else if (std & V4L2_STD_PAL) { + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + v4l2_dbg(1, debug, sd, "switched to PAL\n"); + } else if (std & V4L2_STD_SECAM) { + saa7110_write(sd, 0x0D, 0x87); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + v4l2_dbg(1, debug, sd, "switched to SECAM\n"); + } else { + return -EINVAL; + } + } + return 0; +} + +static int saa7110_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (input >= SAA7110_MAX_INPUT) { + v4l2_dbg(1, debug, sd, "input=%d not available\n", input); + return -EINVAL; + } + if (decoder->input != input) { + saa7110_selmux(sd, input); + v4l2_dbg(1, debug, sd, "switched to input=%d\n", input); + } + return 0; +} + +static int saa7110_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (decoder->enable != enable) { + decoder->enable = enable; + saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80); + v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off"); + } + return 0; +} + +static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + saa7110_write(sd, 0x19, ctrl->val); + break; + case V4L2_CID_CONTRAST: + saa7110_write(sd, 0x13, ctrl->val); + break; + case V4L2_CID_SATURATION: + saa7110_write(sd, 0x12, ctrl->val); + break; + case V4L2_CID_HUE: + saa7110_write(sd, 0x07, ctrl->val); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { + .s_ctrl = saa7110_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa7110_core_ops = { + .g_chip_ident = saa7110_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = saa7110_s_std, +}; + +static const struct v4l2_subdev_video_ops saa7110_video_ops = { + .s_routing = saa7110_s_routing, + .s_stream = saa7110_s_stream, + .querystd = saa7110_querystd, + .g_input_status = saa7110_g_input_status, +}; + +static const struct v4l2_subdev_ops saa7110_ops = { + .core = &saa7110_core_ops, + .video = &saa7110_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa7110 *decoder; + struct v4l2_subdev *sd; + int rv; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7110_ops); + decoder->norm = V4L2_STD_PAL; + decoder->input = 0; + decoder->enable = 1; + v4l2_ctrl_handler_init(&decoder->hdl, 2); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + + init_waitqueue_head(&decoder->wq); + + rv = saa7110_write_block(sd, initseq, sizeof(initseq)); + if (rv < 0) { + v4l2_dbg(1, debug, sd, "init status %d\n", rv); + } else { + int ver, status; + saa7110_write(sd, 0x21, 0x10); + saa7110_write(sd, 0x0e, 0x18); + saa7110_write(sd, 0x0D, 0x04); + ver = saa7110_read(sd); + saa7110_write(sd, 0x0D, 0x06); + /*mdelay(150);*/ + status = saa7110_read(sd); + v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n", + ver, status); + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + } + + /*saa7110_selmux(sd,0);*/ + /*determine_norm(sd);*/ + /* setup and implicit mode 0 select has been performed */ + + return 0; +} + +static int saa7110_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa7110 *decoder = to_saa7110(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa7110_id[] = { + { "saa7110", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7110_id); + +static struct i2c_driver saa7110_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7110", + }, + .probe = saa7110_probe, + .remove = saa7110_remove, + .id_table = saa7110_id, +}; + +module_i2c_driver(saa7110_driver); diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c new file mode 100644 index 000000000000..2107336cd836 --- /dev/null +++ b/drivers/media/i2c/saa7115.c @@ -0,0 +1,1727 @@ +/* saa711x - Philips SAA711x video decoder driver + * This driver can work with saa7111, saa7111a, saa7113, saa7114, + * saa7115 and saa7118. + * + * Based on saa7114 driver by Maxim Yevtyushkin, which is based on + * the saa7111 driver by Dave Perks. + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003) + * by Ronald Bultje + * + * Added saa7115 support by Kevin Thayer + * (2/17/2003) + * + * VBI support (2004) and cleanups (2005) by Hans Verkuil + * + * Copyright (c) 2005-2006 Mauro Carvalho Chehab + * SAA7111, SAA7113 and SAA7118 support + * + * This program is free software; 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 "saa711x_regs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VRES_60HZ (480+16) + +MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver"); +MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " + "Hans Verkuil, Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +struct saa711x_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + struct { + /* chroma gain control cluster */ + struct v4l2_ctrl *agc; + struct v4l2_ctrl *gain; + }; + + v4l2_std_id std; + int input; + int output; + int enable; + int radio; + int width; + int height; + u32 ident; + u32 audclk_freq; + u32 crystal_freq; + u8 ucgc; + u8 cgcdiv; + u8 apll; +}; + +static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa711x_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Sanity routine to check if a register is present */ +static int saa711x_has_reg(const int id, const u8 reg) +{ + if (id == V4L2_IDENT_SAA7111) + return reg < 0x20 && reg != 0x01 && reg != 0x0f && + (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e; + if (id == V4L2_IDENT_SAA7111A) + return reg < 0x20 && reg != 0x01 && reg != 0x0f && + reg != 0x14 && reg != 0x18 && reg != 0x19 && + reg != 0x1d && reg != 0x1e; + + /* common for saa7113/4/5/8 */ + if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f || + reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) || + reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) || + reg == 0x82 || (reg >= 0x89 && reg <= 0x8e))) + return 0; + + switch (id) { + case V4L2_IDENT_SAA7113: + return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) && + reg != 0x5d && reg < 0x63; + case V4L2_IDENT_SAA7114: + return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) && + (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 && + reg != 0x81 && reg < 0xf0; + case V4L2_IDENT_SAA7115: + return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe); + case V4L2_IDENT_SAA7118: + return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) && + (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 && + (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0; + } + return 1; +} + +static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) +{ + struct saa711x_state *state = to_state(sd); + unsigned char reg, data; + + while (*regs != 0x00) { + reg = *(regs++); + data = *(regs++); + + /* According with datasheets, reserved regs should be + filled with 0 - seems better not to touch on they */ + if (saa711x_has_reg(state->ident, reg)) { + if (saa711x_write(sd, reg, data) < 0) + return -1; + } else { + v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); + } + } + return 0; +} + +static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +/* SAA7111 initialization table */ +static const unsigned char saa7111_init[] = { + R_01_INC_DELAY, 0x00, /* reserved */ + + /*front end */ + R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */ + R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, + * GAFIX=0, GAI1=256, GAI2=256 */ + R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */ + R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */ + + /* decoder */ + R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz) + * pixels after end of last line */ + R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to + * work with NTSC, too */ + R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0, + * VTRC=1, HPLL=0, VNOI=0 */ + R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0, + * VBLB=0, UPTCV=0, APER=1 */ + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */ + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, + * FCTC=0, CHBW=1 */ + R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */ + R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ + R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, + * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ + R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */ + R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */ + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + +/* SAA7113 init codes */ +static const unsigned char saa7113_init[] = { + R_01_INC_DELAY, 0x08, + R_02_INPUT_CNTL_1, 0xc2, + R_03_INPUT_CNTL_2, 0x30, + R_04_INPUT_CNTL_3, 0x00, + R_05_INPUT_CNTL_4, 0x00, + R_06_H_SYNC_START, 0x89, + R_07_H_SYNC_STOP, 0x0d, + R_08_SYNC_CNTL, 0x88, + R_09_LUMA_CNTL, 0x01, + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, + R_0F_CHROMA_GAIN_CNTL, 0x2a, + R_10_CHROMA_CNTL_2, 0x08, + R_11_MODE_DELAY_CNTL, 0x0c, + R_12_RT_SIGNAL_CNTL, 0x07, + R_13_RT_X_PORT_OUT_CNTL, 0x00, + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + +/* If a value differs from the Hauppauge driver values, then the comment starts with + 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the + Hauppauge driver sets. */ + +/* SAA7114 and SAA7115 initialization table */ +static const unsigned char saa7115_init_auto_input[] = { + /* Front-End Part */ + R_01_INC_DELAY, 0x48, /* white peak control disabled */ + R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */ + R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */ + R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */ + /* Decoder Part */ + R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */ + R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */ + R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ + R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ + R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ + R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */ + R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */ + R_11_MODE_DELAY_CNTL, 0x00, + R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */ + R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */ + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */ + R_19_RAW_DATA_OFF_CNTL, 0x80, + R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */ + R_1B_MISC_TVVCRDET, 0x42, /* recommended value */ + R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */ + R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */ + + + R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */ + + /* Power Device Control */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */ + 0x00, 0x00 +}; + +/* Used to reset saa7113, saa7114 and saa7115 */ +static const unsigned char saa7115_cfg_reset_scaler[] = { + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */ + 0x00, 0x00 +}; + +/* ============== SAA7715 VIDEO templates ============= */ + +static const unsigned char saa7115_cfg_60hz_video[] = { + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + + R_15_VGATE_START_FID_CHG, 0x03, + R_16_VGATE_STOP, 0x11, + R_17_MISC_VGATE_CONF_AND_MSB, 0x9c, + + R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ + R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */ + + R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */ + + /* Task A */ + R_90_A_TASK_HANDLING_CNTL, 0x80, + R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, + R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, + R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, + + /* hoffset low (input), 0x0002 is minimum */ + R_94_A_HORIZ_INPUT_WINDOW_START, 0x01, + R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize low (input), 0x02d0 = 720 */ + R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + R_98_A_VERT_INPUT_WINDOW_START, 0x05, + R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, + + R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c, + R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, + + R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, + R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, + + R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c, + R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, + + /* Task B */ + R_C0_B_TASK_HANDLING_CNTL, 0x00, + R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, + R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, + R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, + + /* 0x0002 is minimum */ + R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02, + R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* 0x02d0 = 720 */ + R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + /* vwindow start 0x12 = 18 */ + R_C8_B_VERT_INPUT_WINDOW_START, 0x12, + R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vwindow length 0xf8 = 248 */ + R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1, + R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9, + + /* hwindow 0x02d0 = 720 */ + R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, + R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, + + R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ + R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */ + R_F5_PULSGEN_LINE_LENGTH, 0xad, + R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, + + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_50hz_video[] = { + R_80_GLOBAL_CNTL_1, 0x00, + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + + R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */ + R_16_VGATE_STOP, 0x16, + R_17_MISC_VGATE_CONF_AND_MSB, 0x99, + + R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */ + R_0E_CHROMA_CNTL_1, 0x07, + + R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */ + + /* Task A */ + R_90_A_TASK_HANDLING_CNTL, 0x81, + R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, + R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, + R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, + + /* This is weird: the datasheet says that you should use 2 as the minimum value, */ + /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ + /* hoffset low (input), 0x0002 is minimum */ + R_94_A_HORIZ_INPUT_WINDOW_START, 0x00, + R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize low (input), 0x02d0 = 720 */ + R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + R_98_A_VERT_INPUT_WINDOW_START, 0x03, + R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vsize 0x12 = 18 */ + R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12, + R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, + + /* hsize 0x05a0 = 1440 */ + R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, + R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */ + R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */ + R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */ + + /* Task B */ + R_C0_B_TASK_HANDLING_CNTL, 0x00, + R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, + R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, + R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, + + /* This is weird: the datasheet says that you should use 2 as the minimum value, */ + /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ + /* hoffset low (input), 0x0002 is minimum. See comment above. */ + R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00, + R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize 0x02d0 = 720 */ + R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + /* voffset 0x16 = 22 */ + R_C8_B_VERT_INPUT_WINDOW_START, 0x16, + R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vsize 0x0120 = 288 */ + R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20, + R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01, + + /* hsize 0x02d0 = 720 */ + R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, + R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, + + R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ + R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */ + R_F5_PULSGEN_LINE_LENGTH, 0xb0, + R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, + + 0x00, 0x00 +}; + +/* ============== SAA7715 VIDEO templates (end) ======= */ + +static const unsigned char saa7115_cfg_vbi_on[] = { + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ + + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_vbi_off[] = { + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ + + 0x00, 0x00 +}; + + +static const unsigned char saa7115_init_misc[] = { + R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01, + R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01, + R_84_I_PORT_SIGNAL_DEF, 0x20, + R_85_I_PORT_SIGNAL_POLAR, 0x21, + R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5, + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, + + /* Task A */ + R_A0_A_HORIZ_PRESCALING, 0x01, + R_A1_A_ACCUMULATION_LENGTH, 0x00, + R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, + + /* Configure controls at nominal value*/ + R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80, + R_A5_A_LUMA_CONTRAST_CNTL, 0x40, + R_A6_A_CHROMA_SATURATION_CNTL, 0x40, + + /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ + R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00, + R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02, + + R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00, + + /* must be horiz lum scaling / 2 */ + R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00, + R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01, + + /* must be offset luma / 2 */ + R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00, + + R_B0_A_VERT_LUMA_SCALING_INC, 0x00, + R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04, + + R_B2_A_VERT_CHROMA_SCALING_INC, 0x00, + R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04, + + R_B4_A_VERT_SCALING_MODE_CNTL, 0x01, + + R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00, + R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00, + R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00, + R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00, + + R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00, + R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00, + R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00, + R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00, + + /* Task B */ + R_D0_B_HORIZ_PRESCALING, 0x01, + R_D1_B_ACCUMULATION_LENGTH, 0x00, + R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, + + /* Configure controls at nominal value*/ + R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80, + R_D5_B_LUMA_CONTRAST_CNTL, 0x40, + R_D6_B_CHROMA_SATURATION_CNTL, 0x40, + + /* hor lum scaling 0x0400 = 1 */ + R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00, + R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04, + + R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00, + + /* must be hor lum scaling / 2 */ + R_DC_B_HORIZ_CHROMA_SCALING, 0x00, + R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02, + + /* must be offset luma / 2 */ + R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00, + + R_E0_B_VERT_LUMA_SCALING_INC, 0x00, + R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04, + + R_E2_B_VERT_CHROMA_SCALING_INC, 0x00, + R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04, + + R_E4_B_VERT_SCALING_MODE_CNTL, 0x01, + + R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00, + R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00, + R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00, + R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00, + + R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00, + R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00, + R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00, + R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00, + + R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ + R_F3_PLL_INCREMENT, 0x46, + R_F4_PLL2_STATUS, 0x00, + R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */ + R_F8_PULSE_B_POS, 0x00, + R_F9_PULSE_B_POS_MSB, 0x4b, + R_FA_PULSE_C_POS, 0x00, + R_FB_PULSE_C_POS_MSB, 0x4b, + + /* PLL2 lock detection settings: 71 lines 50% phase error */ + R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88, + + /* Turn off VBI */ + R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */ + R_41_LCR_BASE, 0xff, + R_41_LCR_BASE+1, 0xff, + R_41_LCR_BASE+2, 0xff, + R_41_LCR_BASE+3, 0xff, + R_41_LCR_BASE+4, 0xff, + R_41_LCR_BASE+5, 0xff, + R_41_LCR_BASE+6, 0xff, + R_41_LCR_BASE+7, 0xff, + R_41_LCR_BASE+8, 0xff, + R_41_LCR_BASE+9, 0xff, + R_41_LCR_BASE+10, 0xff, + R_41_LCR_BASE+11, 0xff, + R_41_LCR_BASE+12, 0xff, + R_41_LCR_BASE+13, 0xff, + R_41_LCR_BASE+14, 0xff, + R_41_LCR_BASE+15, 0xff, + R_41_LCR_BASE+16, 0xff, + R_41_LCR_BASE+17, 0xff, + R_41_LCR_BASE+18, 0xff, + R_41_LCR_BASE+19, 0xff, + R_41_LCR_BASE+20, 0xff, + R_41_LCR_BASE+21, 0xff, + R_41_LCR_BASE+22, 0xff, + R_58_PROGRAM_FRAMING_CODE, 0x40, + R_59_H_OFF_FOR_SLICER, 0x47, + R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83, + R_5D_DID, 0xbd, + R_5E_SDID, 0x35, + + R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */ + + R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, + 0x00, 0x00 +}; + +static int saa711x_odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static int saa711x_decode_vps(u8 *dst, u8 *p) +{ + static const u8 biphase_tbl[] = { + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, + 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, + 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, + 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, + 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, + 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, + 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + }; + int i; + u8 c, err = 0; + + for (i = 0; i < 2 * 13; i += 2) { + err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; + c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4); + dst[i / 2] = c; + } + return err & 0xf0; +} + +static int saa711x_decode_wss(u8 *p) +{ + static const int wss_bits[8] = { + 0, 0, 0, 1, 0, 1, 1, 1 + }; + unsigned char parity; + int wss = 0; + int i; + + for (i = 0; i < 16; i++) { + int b1 = wss_bits[p[i] & 7]; + int b2 = wss_bits[(p[i] >> 3) & 7]; + + if (b1 == b2) + return -1; + wss |= b2 << i; + } + parity = wss & 15; + parity ^= parity >> 2; + parity ^= parity >> 1; + + if (!(parity & 1)) + return -1; + + return wss; +} + +static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct saa711x_state *state = to_state(sd); + u32 acpf; + u32 acni; + u32 hz; + u64 f; + u8 acc = 0; /* reg 0x3a, audio clock control */ + + /* Checks for chips that don't have audio clock (saa7111, saa7113) */ + if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) + return 0; + + v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); + + /* sanity check */ + if (freq < 32000 || freq > 48000) + return -EINVAL; + + /* hz is the refresh rate times 100 */ + hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; + /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ + acpf = (25600 * freq) / hz; + /* acni = (256 * freq * 2^23) / crystal_frequency = + (freq * 2^(8+23)) / crystal_frequency = + (freq << 31) / crystal_frequency */ + f = freq; + f = f << 31; + do_div(f, state->crystal_freq); + acni = f; + if (state->ucgc) { + acpf = acpf * state->cgcdiv / 16; + acni = acni * state->cgcdiv / 16; + acc = 0x80; + if (state->cgcdiv == 3) + acc |= 0x40; + } + if (state->apll) + acc |= 0x08; + + saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); + saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); + saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); + + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, + (acpf >> 8) & 0xff); + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, + (acpf >> 16) & 0x03); + + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); + state->audclk_freq = freq; + return 0; +} + +static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct saa711x_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_CHROMA_AGC: + /* chroma gain cluster */ + if (state->agc->val) + state->gain->val = + saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; + break; + } + return 0; +} + +static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct saa711x_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val); + break; + + case V4L2_CID_SATURATION: + saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val); + break; + + case V4L2_CID_HUE: + saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val); + break; + + case V4L2_CID_CHROMA_AGC: + /* chroma gain cluster */ + if (state->agc->val) + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val); + else + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) +{ + struct saa711x_state *state = to_state(sd); + int HPSC, HFSC; + int VSCY; + int res; + int is_50hz = state->std & V4L2_STD_625_50; + int Vsrc = is_50hz ? 576 : 480; + + v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); + + /* FIXME need better bounds checking here */ + if ((width < 1) || (width > 1440)) + return -EINVAL; + if ((height < 1) || (height > Vsrc)) + return -EINVAL; + + if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { + /* Decoder only supports 720 columns and 480 or 576 lines */ + if (width != 720) + return -EINVAL; + if (height != Vsrc) + return -EINVAL; + } + + state->width = width; + state->height = height; + + if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH)) + return 0; + + /* probably have a valid size, let's set it */ + /* Set output width/height */ + /* width */ + + saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, + (u8) (width & 0xff)); + saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, + (u8) ((width >> 8) & 0xff)); + + /* Vertical Scaling uses height/2 */ + res = height / 2; + + /* On 60Hz, it is using a higher Vertical Output Size */ + if (!is_50hz) + res += (VRES_60HZ - 480) >> 1; + + /* height */ + saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, + (u8) (res & 0xff)); + saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, + (u8) ((res >> 8) & 0xff)); + + /* Scaling settings */ + /* Hprescaler is floor(inres/outres) */ + HPSC = (int)(720 / width); + /* 0 is not allowed (div. by zero) */ + HPSC = HPSC ? HPSC : 1; + HFSC = (int)((1024 * 720) / (HPSC * width)); + /* FIXME hardcodes to "Task B" + * write H prescaler integer */ + saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, + (u8) (HPSC & 0x3f)); + + v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + /* write H fine-scaling (luminance) */ + saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, + (u8) (HFSC & 0xff)); + saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, + (u8) ((HFSC >> 8) & 0xff)); + /* write H fine-scaling (chrominance) + * must be lum/2, so i'll just bitshift :) */ + saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, + (u8) ((HFSC >> 1) & 0xff)); + saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, + (u8) ((HFSC >> 9) & 0xff)); + + VSCY = (int)((1024 * Vsrc) / height); + v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + + /* Correct Contrast and Luminance */ + saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, + (u8) (64 * 1024 / VSCY)); + saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, + (u8) (64 * 1024 / VSCY)); + + /* write V fine-scaling (luminance) */ + saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, + (u8) (VSCY & 0xff)); + saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, + (u8) ((VSCY >> 8) & 0xff)); + /* write V fine-scaling (chrominance) */ + saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, + (u8) (VSCY & 0xff)); + saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, + (u8) ((VSCY >> 8) & 0xff)); + + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); + + /* Activates task "B" */ + saa711x_write(sd, R_80_GLOBAL_CNTL_1, + saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); + + return 0; +} + +static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); + + /* Prevent unnecessary standard changes. During a standard + change the I-Port is temporarily disabled. Any devices + reading from that port can get confused. + Note that s_std is also used to switch from + radio to TV mode, so if a s_std is broadcast to + all I2C devices then you do not want to have an unwanted + side-effect here. */ + if (std == state->std) + return; + + state->std = std; + + // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. + if (std & V4L2_STD_525_60) { + v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_60hz_video); + saa711x_set_size(sd, 720, 480); + } else { + v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_50hz_video); + saa711x_set_size(sd, 720, 576); + } + + /* Register 0E - Bits D6-D4 on NO-AUTO mode + (SAA7111 and SAA7113 doesn't have auto mode) + 50 Hz / 625 lines 60 Hz / 525 lines + 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz) + 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz) + 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz) + 011 NTSC N (3.58MHz) PAL M (3.58MHz) + 100 reserved NTSC-Japan (3.58MHz) + */ + if (state->ident <= V4L2_IDENT_SAA7113) { + u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; + + if (std == V4L2_STD_PAL_M) { + reg |= 0x30; + } else if (std == V4L2_STD_PAL_Nc) { + reg |= 0x20; + } else if (std == V4L2_STD_PAL_60) { + reg |= 0x10; + } else if (std == V4L2_STD_NTSC_M_JP) { + reg |= 0x40; + } else if (std & V4L2_STD_SECAM) { + reg |= 0x50; + } + saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); + } else { + /* restart task B if needed */ + int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; + + if (taskb && state->ident == V4L2_IDENT_SAA7114) { + saa711x_writeregs(sd, saa7115_cfg_vbi_on); + } + + /* switch audio mode too! */ + saa711x_s_clock_freq(sd, state->audclk_freq); + } +} + +/* setup the sliced VBI lcr registers according to the sliced VBI format */ +static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + struct saa711x_state *state = to_state(sd); + int is_50hz = (state->std & V4L2_STD_625_50); + u8 lcr[24]; + int i, x; + +#if 1 + /* saa7113/7114/7118 VBI support are experimental */ + if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) + return; + +#else + /* SAA7113 and SAA7118 also should support VBI - Need testing */ + if (state->ident != V4L2_IDENT_SAA7115) + return; +#endif + + for (i = 0; i <= 23; i++) + lcr[i] = 0xff; + + if (fmt == NULL) { + /* raw VBI */ + if (is_50hz) + for (i = 6; i <= 23; i++) + lcr[i] = 0xdd; + else + for (i = 10; i <= 21; i++) + lcr[i] = 0xdd; + } else { + /* sliced VBI */ + /* first clear lines that cannot be captured */ + if (is_50hz) { + for (i = 0; i <= 5; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + } + else { + for (i = 0; i <= 9; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + for (i = 22; i <= 23; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + } + + /* Now set the lcr values according to the specified service */ + for (i = 6; i <= 23; i++) { + lcr[i] = 0; + for (x = 0; x <= 1; x++) { + switch (fmt->service_lines[1-x][i]) { + case 0: + lcr[i] |= 0xf << (4 * x); + break; + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 5 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 7 << (4 * x); + break; + } + } + } + } + + /* write the lcr registers */ + for (i = 2; i <= 23; i++) { + saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); + } + + /* enable/disable raw VBI capturing */ + saa711x_writeregs(sd, fmt == NULL ? + saa7115_cfg_vbi_on : + saa7115_cfg_vbi_off); +} + +static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) +{ + static u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_CAPTION_525, /* 4 */ + V4L2_SLICED_WSS_625, 0, /* 5 */ + V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ + 0, 0, 0, 0 + }; + int i; + + memset(sliced, 0, sizeof(*sliced)); + /* done if using raw VBI */ + if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) + return 0; + for (i = 2; i <= 23; i++) { + u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); + + sliced->service_lines[0][i] = lcr2vbi[v >> 4]; + sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; + sliced->service_set |= + sliced->service_lines[0][i] | sliced->service_lines[1][i]; + } + return 0; +} + +static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + saa711x_set_lcr(sd, NULL); + return 0; +} + +static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + saa711x_set_lcr(sd, fmt); + return 0; +} + +static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + if (fmt->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + fmt->field = V4L2_FIELD_INTERLACED; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + return saa711x_set_size(sd, fmt->width, fmt->height); +} + +/* Decode the sliced VBI data stream as created by the saa7115. + The format is described in the saa7115 datasheet in Tables 25 and 26 + and in Figure 33. + The current implementation uses SAV/EAV codes and not the ancillary data + headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV + code. */ +static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct saa711x_state *state = to_state(sd); + static const char vbi_no_data_pattern[] = { + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 + }; + u8 *p = vbi->p; + u32 wss; + int id1, id2; /* the ID1 and ID2 bytes from the internal header */ + + vbi->type = 0; /* mark result as a failure */ + id1 = p[2]; + id2 = p[3]; + /* Note: the field bit is inverted for 60 Hz video */ + if (state->std & V4L2_STD_525_60) + id1 ^= 0x40; + + /* Skip internal header, p now points to the start of the payload */ + p += 4; + vbi->p = p; + + /* calculate field and line number of the VBI packet (1-23) */ + vbi->is_second_field = ((id1 & 0x40) != 0); + vbi->line = (id1 & 0x3f) << 3; + vbi->line |= (id2 & 0x70) >> 4; + + /* Obtain data type */ + id2 &= 0xf; + + /* If the VBI slicer does not detect any signal it will fill up + the payload buffer with 0xa0 bytes. */ + if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) + return 0; + + /* decode payloads */ + switch (id2) { + case 1: + vbi->type = V4L2_SLICED_TELETEXT_B; + break; + case 4: + if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) + return 0; + vbi->type = V4L2_SLICED_CAPTION_525; + break; + case 5: + wss = saa711x_decode_wss(p); + if (wss == -1) + return 0; + p[0] = wss & 0xff; + p[1] = wss >> 8; + vbi->type = V4L2_SLICED_WSS_625; + break; + case 7: + if (saa711x_decode_vps(p, p) != 0) + return 0; + vbi->type = V4L2_SLICED_VPS; + break; + default: + break; + } + return 0; +} + +/* ============ SAA7115 AUDIO settings (end) ============= */ + +static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa711x_state *state = to_state(sd); + int status; + + if (state->radio) + return 0; + status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + + v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); + vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; + return 0; +} + +static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); + + state->radio = 0; + saa711x_set_v4lstd(sd, std); + return 0; +} + +static int saa711x_s_radio(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + + state->radio = 1; + return 0; +} + +static int saa711x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa711x_state *state = to_state(sd); + u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0; + + v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", + input, output); + + /* saa7111/3 does not have these inputs */ + if (state->ident <= V4L2_IDENT_SAA7113 && + (input == SAA7115_COMPOSITE4 || + input == SAA7115_COMPOSITE5)) { + return -EINVAL; + } + if (input > SAA7115_SVIDEO3) + return -EINVAL; + if (state->input == input && state->output == output) + return 0; + v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", + (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", + (output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); + state->input = input; + + /* saa7111 has slightly different input numbering */ + if (state->ident <= V4L2_IDENT_SAA7111A) { + if (input >= SAA7115_COMPOSITE4) + input -= 2; + /* saa7111 specific */ + saa711x_write(sd, R_10_CHROMA_CNTL_2, + (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | + ((output & 0xc0) ^ 0x40)); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((output & 2) ? 0x0a : 0)); + } + + /* select mode */ + saa711x_write(sd, R_02_INPUT_CNTL_1, + (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | + input); + + /* bypass chrominance trap for S-Video modes */ + saa711x_write(sd, R_09_LUMA_CNTL, + (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | + (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); + + state->output = output; + if (state->ident == V4L2_IDENT_SAA7114 || + state->ident == V4L2_IDENT_SAA7115) { + saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, + (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | + (state->output & 0x01)); + } + return 0; +} + +static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) +{ + struct saa711x_state *state = to_state(sd); + + if (state->ident > V4L2_IDENT_SAA7111A) + return -EINVAL; + saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | + (val ? 0x80 : 0)); + return 0; +} + +static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa711x_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s output\n", + enable ? "enable" : "disable"); + + if (state->enable == enable) + return 0; + state->enable = enable; + if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED)) + return 0; + saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable); + return 0; +} + +static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) +{ + struct saa711x_state *state = to_state(sd); + + if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ) + return -EINVAL; + state->crystal_freq = freq; + state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + saa711x_s_clock_freq(sd, state->audclk_freq); + return 0; +} + +static int saa711x_reset(struct v4l2_subdev *sd, u32 val) +{ + v4l2_dbg(1, debug, sd, "decoder RESET\n"); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); + return 0; +} + +static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) +{ + /* Note: the internal field ID is inverted for NTSC, + so data->field 0 maps to the saa7115 even field, + whereas for PAL it maps to the saa7115 odd field. */ + switch (data->id) { + case V4L2_SLICED_WSS_625: + if (saa711x_read(sd, 0x6b) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x6c); + data->data[1] = saa711x_read(sd, 0x6d); + return 0; + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) { + /* CC */ + if (saa711x_read(sd, 0x66) & 0x30) + return -EIO; + data->data[0] = saa711x_read(sd, 0x69); + data->data[1] = saa711x_read(sd, 0x6a); + return 0; + } + /* XDS */ + if (saa711x_read(sd, 0x66) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x67); + data->data[1] = saa711x_read(sd, 0x68); + return 0; + default: + return -EINVAL; + } +} + +static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct saa711x_state *state = to_state(sd); + int reg1f, reg1e; + + /* + * The V4L2 core already initializes std with all supported + * Standards. All driver needs to do is to mask it, to remove + * standards that don't apply from the mask + */ + + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f); + + /* horizontal/vertical not locked */ + if (reg1f & 0x40) + goto ret; + + if (reg1f & 0x20) + *std &= V4L2_STD_525_60; + else + *std &= V4L2_STD_625_50; + + if (state->ident != V4L2_IDENT_SAA7115) + goto ret; + + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + + switch (reg1e & 0x03) { + case 1: + *std &= V4L2_STD_NTSC; + break; + case 2: + /* + * V4L2_STD_PAL just cover the european PAL standards. + * This is wrong, as the device could also be using an + * other PAL standard. + */ + *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | + V4L2_STD_PAL_M | V4L2_STD_PAL_60; + break; + case 3: + *std &= V4L2_STD_SECAM; + break; + default: + /* Can't detect anything */ + break; + } + + v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); + +ret: + v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); + + return 0; +} + +static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct saa711x_state *state = to_state(sd); + int reg1e = 0x80; + int reg1f; + + *status = V4L2_IN_ST_NO_SIGNAL; + if (state->ident == V4L2_IDENT_SAA7115) + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80) + *status = 0; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa711x_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct saa711x_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} + +static int saa711x_log_status(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + int reg1e, reg1f; + int signalOk; + int vcr; + + v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); + if (state->ident != V4L2_IDENT_SAA7115) { + /* status for the saa7114 */ + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + signalOk = (reg1f & 0xc1) == 0x81; + v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + return 0; + } + + /* status for the saa7115 */ + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + + signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; + vcr = !(reg1f & 0x10); + + if (state->input >= 6) + v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); + else + v4l2_info(sd, "Input: Composite %d\n", state->input); + v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + + switch (reg1e & 0x03) { + case 1: + v4l2_info(sd, "Detected format: NTSC\n"); + break; + case 2: + v4l2_info(sd, "Detected format: PAL\n"); + break; + case 3: + v4l2_info(sd, "Detected format: SECAM\n"); + break; + default: + v4l2_info(sd, "Detected format: BW/No color\n"); + break; + } + v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { + .s_ctrl = saa711x_s_ctrl, + .g_volatile_ctrl = saa711x_g_volatile_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa711x_core_ops = { + .log_status = saa711x_log_status, + .g_chip_ident = saa711x_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = saa711x_s_std, + .reset = saa711x_reset, + .s_gpio = saa711x_s_gpio, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa711x_g_register, + .s_register = saa711x_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { + .s_radio = saa711x_s_radio, + .g_tuner = saa711x_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { + .s_clock_freq = saa711x_s_clock_freq, +}; + +static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_routing = saa711x_s_routing, + .s_crystal_freq = saa711x_s_crystal_freq, + .s_mbus_fmt = saa711x_s_mbus_fmt, + .s_stream = saa711x_s_stream, + .querystd = saa711x_querystd, + .g_input_status = saa711x_g_input_status, +}; + +static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .g_sliced_fmt = saa711x_g_sliced_fmt, + .s_sliced_fmt = saa711x_s_sliced_fmt, + .s_raw_fmt = saa711x_s_raw_fmt, +}; + +static const struct v4l2_subdev_ops saa711x_ops = { + .core = &saa711x_core_ops, + .tuner = &saa711x_tuner_ops, + .audio = &saa711x_audio_ops, + .video = &saa711x_video_ops, + .vbi = &saa711x_vbi_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa711x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa711x_state *state; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int i; + char name[17]; + char chip_id; + int autodetect = !id || id->driver_data == 1; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + for (i = 0; i < 0x0f; i++) { + i2c_smbus_write_byte_data(client, 0, i); + name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; + if (name[i] > '9') + name[i] += 'a' - '9' - 1; + } + name[i] = '\0'; + + chip_id = name[5]; + + /* Check whether this chip is part of the saa711x series */ + if (memcmp(name + 1, "f711", 4)) { + v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", + client->addr << 1, name); + return -ENODEV; + } + + /* Safety check */ + if (!autodetect && id->name[6] != chip_id) { + v4l_warn(client, "found saa711%c while %s was expected\n", + chip_id, id->name); + } + snprintf(client->name, sizeof(client->name), "saa711%c", chip_id); + v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name, + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa711x_ops); + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); + state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(state); + return err; + } + v4l2_ctrl_auto_cluster(2, &state->agc, 0, true); + + state->input = -1; + state->output = SAA7115_IPORT_ON; + state->enable = 1; + state->radio = 0; + switch (chip_id) { + case '1': + state->ident = V4L2_IDENT_SAA7111; + if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) { + v4l_info(client, "saa7111a variant found\n"); + state->ident = V4L2_IDENT_SAA7111A; + } + break; + case '3': + state->ident = V4L2_IDENT_SAA7113; + break; + case '4': + state->ident = V4L2_IDENT_SAA7114; + break; + case '5': + state->ident = V4L2_IDENT_SAA7115; + break; + case '8': + state->ident = V4L2_IDENT_SAA7118; + break; + default: + state->ident = V4L2_IDENT_SAA7111; + v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); + break; + } + + state->audclk_freq = 48000; + + v4l2_dbg(1, debug, sd, "writing init values\n"); + + /* init to 60hz/48khz */ + state->crystal_freq = SAA7115_FREQ_24_576_MHZ; + switch (state->ident) { + case V4L2_IDENT_SAA7111: + case V4L2_IDENT_SAA7111A: + saa711x_writeregs(sd, saa7111_init); + break; + case V4L2_IDENT_SAA7113: + saa711x_writeregs(sd, saa7113_init); + break; + default: + state->crystal_freq = SAA7115_FREQ_32_11_MHZ; + saa711x_writeregs(sd, saa7115_init_auto_input); + } + if (state->ident > V4L2_IDENT_SAA7111A) + saa711x_writeregs(sd, saa7115_init_misc); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); + v4l2_ctrl_handler_setup(hdl); + + v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", + saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), + saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa711x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id saa711x_id[] = { + { "saa7115_auto", 1 }, /* autodetect */ + { "saa7111", 0 }, + { "saa7113", 0 }, + { "saa7114", 0 }, + { "saa7115", 0 }, + { "saa7118", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa711x_id); + +static struct i2c_driver saa711x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7115", + }, + .probe = saa711x_probe, + .remove = saa711x_remove, + .id_table = saa711x_id, +}; + +module_i2c_driver(saa711x_driver); diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h new file mode 100644 index 000000000000..4e5f2eb0a2c1 --- /dev/null +++ b/drivers/media/i2c/saa711x_regs.h @@ -0,0 +1,549 @@ +/* saa711x - Philips SAA711x video decoder register specifications + * + * Copyright (c) 2006 Mauro Carvalho Chehab + * + * This program is free software; 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. + */ + +#define R_00_CHIP_VERSION 0x00 +/* Video Decoder */ + /* Video Decoder - Frontend part */ +#define R_01_INC_DELAY 0x01 +#define R_02_INPUT_CNTL_1 0x02 +#define R_03_INPUT_CNTL_2 0x03 +#define R_04_INPUT_CNTL_3 0x04 +#define R_05_INPUT_CNTL_4 0x05 + /* Video Decoder - Decoder part */ +#define R_06_H_SYNC_START 0x06 +#define R_07_H_SYNC_STOP 0x07 +#define R_08_SYNC_CNTL 0x08 +#define R_09_LUMA_CNTL 0x09 +#define R_0A_LUMA_BRIGHT_CNTL 0x0a +#define R_0B_LUMA_CONTRAST_CNTL 0x0b +#define R_0C_CHROMA_SAT_CNTL 0x0c +#define R_0D_CHROMA_HUE_CNTL 0x0d +#define R_0E_CHROMA_CNTL_1 0x0e +#define R_0F_CHROMA_GAIN_CNTL 0x0f +#define R_10_CHROMA_CNTL_2 0x10 +#define R_11_MODE_DELAY_CNTL 0x11 +#define R_12_RT_SIGNAL_CNTL 0x12 +#define R_13_RT_X_PORT_OUT_CNTL 0x13 +#define R_14_ANAL_ADC_COMPAT_CNTL 0x14 +#define R_15_VGATE_START_FID_CHG 0x15 +#define R_16_VGATE_STOP 0x16 +#define R_17_MISC_VGATE_CONF_AND_MSB 0x17 +#define R_18_RAW_DATA_GAIN_CNTL 0x18 +#define R_19_RAW_DATA_OFF_CNTL 0x19 +#define R_1A_COLOR_KILL_LVL_CNTL 0x1a +#define R_1B_MISC_TVVCRDET 0x1b +#define R_1C_ENHAN_COMB_CTRL1 0x1c +#define R_1D_ENHAN_COMB_CTRL2 0x1d +#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e +#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f + +/* Component processing and interrupt masking part */ +#define R_23_INPUT_CNTL_5 0x23 +#define R_24_INPUT_CNTL_6 0x24 +#define R_25_INPUT_CNTL_7 0x25 +#define R_29_COMP_DELAY 0x29 +#define R_2A_COMP_BRIGHT_CNTL 0x2a +#define R_2B_COMP_CONTRAST_CNTL 0x2b +#define R_2C_COMP_SAT_CNTL 0x2c +#define R_2D_INTERRUPT_MASK_1 0x2d +#define R_2E_INTERRUPT_MASK_2 0x2e +#define R_2F_INTERRUPT_MASK_3 0x2f + +/* Audio clock generator part */ +#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30 +#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34 +#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38 +#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39 +#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a + +/* General purpose VBI data slicer part */ +#define R_40_SLICER_CNTL_1 0x40 +#define R_41_LCR_BASE 0x41 +#define R_58_PROGRAM_FRAMING_CODE 0x58 +#define R_59_H_OFF_FOR_SLICER 0x59 +#define R_5A_V_OFF_FOR_SLICER 0x5a +#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b +#define R_5D_DID 0x5d +#define R_5E_SDID 0x5e +#define R_60_SLICER_STATUS_BYTE_0 0x60 +#define R_61_SLICER_STATUS_BYTE_1 0x61 +#define R_62_SLICER_STATUS_BYTE_2 0x62 + +/* X port, I port and the scaler part */ + /* Task independent global settings */ +#define R_80_GLOBAL_CNTL_1 0x80 +#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81 +#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83 +#define R_84_I_PORT_SIGNAL_DEF 0x84 +#define R_85_I_PORT_SIGNAL_POLAR 0x85 +#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86 +#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87 +#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88 +#define R_8F_STATUS_INFO_SCALER 0x8f + /* Task A definition */ + /* Basic settings and acquisition window definition */ +#define R_90_A_TASK_HANDLING_CNTL 0x90 +#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91 +#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92 +#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93 +#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94 +#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95 +#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96 +#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97 +#define R_98_A_VERT_INPUT_WINDOW_START 0x98 +#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99 +#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a +#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b +#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c +#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d +#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e +#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f + /* FIR filtering and prescaling */ +#define R_A0_A_HORIZ_PRESCALING 0xa0 +#define R_A1_A_ACCUMULATION_LENGTH 0xa1 +#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2 +#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4 +#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5 +#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6 + /* Horizontal phase scaling */ +#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8 +#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9 +#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa +#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac +#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad +#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae +#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf + /* Vertical scaling */ +#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0 +#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1 +#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2 +#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3 +#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4 +#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8 +#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9 +#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba +#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb +#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc +#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd +#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe +#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf + /* Task B definition */ + /* Basic settings and acquisition window definition */ +#define R_C0_B_TASK_HANDLING_CNTL 0xc0 +#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1 +#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2 +#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3 +#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4 +#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5 +#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6 +#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7 +#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8 +#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9 +#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca +#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb +#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc +#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd +#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce +#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf + /* FIR filtering and prescaling */ +#define R_D0_B_HORIZ_PRESCALING 0xd0 +#define R_D1_B_ACCUMULATION_LENGTH 0xd1 +#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2 +#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4 +#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5 +#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6 + /* Horizontal phase scaling */ +#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8 +#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9 +#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda +#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc +#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd +#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde + /* Vertical scaling */ +#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0 +#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1 +#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2 +#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3 +#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4 +#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8 +#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9 +#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea +#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb +#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec +#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed +#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee +#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef + +/* second PLL (PLL2) and Pulsegenerator Programming */ +#define R_F0_LFCO_PER_LINE 0xf0 +#define R_F1_P_I_PARAM_SELECT 0xf1 +#define R_F2_NOMINAL_PLL2_DTO 0xf2 +#define R_F3_PLL_INCREMENT 0xf3 +#define R_F4_PLL2_STATUS 0xf4 +#define R_F5_PULSGEN_LINE_LENGTH 0xf5 +#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6 +#define R_F7_PULSE_A_POS_MSB 0xf7 +#define R_F8_PULSE_B_POS 0xf8 +#define R_F9_PULSE_B_POS_MSB 0xf9 +#define R_FA_PULSE_C_POS 0xfa +#define R_FB_PULSE_C_POS_MSB 0xfb +#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff + +#if 0 +/* Those structs will be used in the future for debug purposes */ +struct saa711x_reg_descr { + u8 reg; + int count; + char *name; +}; + +struct saa711x_reg_descr saa711x_regs[] = { + /* REG COUNT NAME */ + {R_00_CHIP_VERSION,1, + "Chip version"}, + + /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */ + + /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */ + {R_01_INC_DELAY,1, + "Increment delay"}, + {R_02_INPUT_CNTL_1,1, + "Analog input control 1"}, + {R_03_INPUT_CNTL_2,1, + "Analog input control 2"}, + {R_04_INPUT_CNTL_3,1, + "Analog input control 3"}, + {R_05_INPUT_CNTL_4,1, + "Analog input control 4"}, + + /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */ + {R_06_H_SYNC_START,1, + "Horizontal sync start"}, + {R_07_H_SYNC_STOP,1, + "Horizontal sync stop"}, + {R_08_SYNC_CNTL,1, + "Sync control"}, + {R_09_LUMA_CNTL,1, + "Luminance control"}, + {R_0A_LUMA_BRIGHT_CNTL,1, + "Luminance brightness control"}, + {R_0B_LUMA_CONTRAST_CNTL,1, + "Luminance contrast control"}, + {R_0C_CHROMA_SAT_CNTL,1, + "Chrominance saturation control"}, + {R_0D_CHROMA_HUE_CNTL,1, + "Chrominance hue control"}, + {R_0E_CHROMA_CNTL_1,1, + "Chrominance control 1"}, + {R_0F_CHROMA_GAIN_CNTL,1, + "Chrominance gain control"}, + {R_10_CHROMA_CNTL_2,1, + "Chrominance control 2"}, + {R_11_MODE_DELAY_CNTL,1, + "Mode/delay control"}, + {R_12_RT_SIGNAL_CNTL,1, + "RT signal control"}, + {R_13_RT_X_PORT_OUT_CNTL,1, + "RT/X port output control"}, + {R_14_ANAL_ADC_COMPAT_CNTL,1, + "Analog/ADC/compatibility control"}, + {R_15_VGATE_START_FID_CHG, 1, + "VGATE start FID change"}, + {R_16_VGATE_STOP,1, + "VGATE stop"}, + {R_17_MISC_VGATE_CONF_AND_MSB, 1, + "Miscellaneous VGATE configuration and MSBs"}, + {R_18_RAW_DATA_GAIN_CNTL,1, + "Raw data gain control",}, + {R_19_RAW_DATA_OFF_CNTL,1, + "Raw data offset control",}, + {R_1A_COLOR_KILL_LVL_CNTL,1, + "Color Killer Level Control"}, + { R_1B_MISC_TVVCRDET, 1, + "MISC /TVVCRDET"}, + { R_1C_ENHAN_COMB_CTRL1, 1, + "Enhanced comb ctrl1"}, + { R_1D_ENHAN_COMB_CTRL2, 1, + "Enhanced comb ctrl1"}, + {R_1E_STATUS_BYTE_1_VD_DEC,1, + "Status byte 1 video decoder"}, + {R_1F_STATUS_BYTE_2_VD_DEC,1, + "Status byte 2 video decoder"}, + + /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */ + /* 0x20 to 0x22 - Reserved */ + {R_23_INPUT_CNTL_5,1, + "Analog input control 5"}, + {R_24_INPUT_CNTL_6,1, + "Analog input control 6"}, + {R_25_INPUT_CNTL_7,1, + "Analog input control 7"}, + /* 0x26 to 0x28 - Reserved */ + {R_29_COMP_DELAY,1, + "Component delay"}, + {R_2A_COMP_BRIGHT_CNTL,1, + "Component brightness control"}, + {R_2B_COMP_CONTRAST_CNTL,1, + "Component contrast control"}, + {R_2C_COMP_SAT_CNTL,1, + "Component saturation control"}, + {R_2D_INTERRUPT_MASK_1,1, + "Interrupt mask 1"}, + {R_2E_INTERRUPT_MASK_2,1, + "Interrupt mask 2"}, + {R_2F_INTERRUPT_MASK_3,1, + "Interrupt mask 3"}, + + /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */ + {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3, + "Audio master clock cycles per field"}, + /* 0x33 - Reserved */ + {R_34_AUD_MAST_CLK_NOMINAL_INC,3, + "Audio master clock nominal increment"}, + /* 0x37 - Reserved */ + {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1, + "Clock ratio AMXCLK to ASCLK"}, + {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1, + "Clock ratio ASCLK to ALRCLK"}, + {R_3A_AUD_CLK_GEN_BASIC_SETUP,1, + "Audio clock generator basic setup"}, + /* 0x3b-0x3f - Reserved */ + + /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */ + {R_40_SLICER_CNTL_1,1, + "Slicer control 1"}, + {R_41_LCR,23, + "R_41_LCR"}, + {R_58_PROGRAM_FRAMING_CODE,1, + "Programmable framing code"}, + {R_59_H_OFF_FOR_SLICER,1, + "Horizontal offset for slicer"}, + {R_5A_V_OFF_FOR_SLICER,1, + "Vertical offset for slicer"}, + {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1, + "Field offset and MSBs for horizontal and vertical offset"}, + {R_5D_DID,1, + "Header and data identification (R_5D_DID)"}, + {R_5E_SDID,1, + "Sliced data identification (R_5E_SDID) code"}, + {R_60_SLICER_STATUS_BYTE_0,1, + "Slicer status byte 0"}, + {R_61_SLICER_STATUS_BYTE_1,1, + "Slicer status byte 1"}, + {R_62_SLICER_STATUS_BYTE_2,1, + "Slicer status byte 2"}, + /* 0x63-0x7f - Reserved */ + + /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ + /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */ + {R_80_GLOBAL_CNTL_1,1, + "Global control 1"}, + {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1, + "Vertical sync and Field ID source selection, retimed V and F signals"}, + /* 0x82 - Reserved */ + {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1, + "X port I/O enable and output clock"}, + {R_84_I_PORT_SIGNAL_DEF,1, + "I port signal definitions"}, + {R_85_I_PORT_SIGNAL_POLAR,1, + "I port signal polarities"}, + {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1, + "I port FIFO flag control and arbitration"}, + {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1, + "I port I/O enable output clock and gated"}, + {R_88_POWER_SAVE_ADC_PORT_CNTL,1, + "Power save/ADC port control"}, + /* 089-0x8e - Reserved */ + {R_8F_STATUS_INFO_SCALER,1, + "Status information scaler part"}, + + /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */ + /* Task A: Basic settings and acquisition window definition */ + {R_90_A_TASK_HANDLING_CNTL,1, + "Task A: Task handling control"}, + {R_91_A_X_PORT_FORMATS_AND_CONF,1, + "Task A: X port formats and configuration"}, + {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1, + "Task A: X port input reference signal definition"}, + {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1, + "Task A: I port output formats and configuration"}, + {R_94_A_HORIZ_INPUT_WINDOW_START,2, + "Task A: Horizontal input window start"}, + {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2, + "Task A: Horizontal input window length"}, + {R_98_A_VERT_INPUT_WINDOW_START,2, + "Task A: Vertical input window start"}, + {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2, + "Task A: Vertical input window length"}, + {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2, + "Task A: Horizontal output window length"}, + {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2, + "Task A: Vertical output window length"}, + + /* Task A: FIR filtering and prescaling */ + {R_A0_A_HORIZ_PRESCALING,1, + "Task A: Horizontal prescaling"}, + {R_A1_A_ACCUMULATION_LENGTH,1, + "Task A: Accumulation length"}, + {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, + "Task A: Prescaler DC gain and FIR prefilter"}, + /* 0xa3 - Reserved */ + {R_A4_A_LUMA_BRIGHTNESS_CNTL,1, + "Task A: Luminance brightness control"}, + {R_A5_A_LUMA_CONTRAST_CNTL,1, + "Task A: Luminance contrast control"}, + {R_A6_A_CHROMA_SATURATION_CNTL,1, + "Task A: Chrominance saturation control"}, + /* 0xa7 - Reserved */ + + /* Task A: Horizontal phase scaling */ + {R_A8_A_HORIZ_LUMA_SCALING_INC,2, + "Task A: Horizontal luminance scaling increment"}, + {R_AA_A_HORIZ_LUMA_PHASE_OFF,1, + "Task A: Horizontal luminance phase offset"}, + /* 0xab - Reserved */ + {R_AC_A_HORIZ_CHROMA_SCALING_INC,2, + "Task A: Horizontal chrominance scaling increment"}, + {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1, + "Task A: Horizontal chrominance phase offset"}, + /* 0xaf - Reserved */ + + /* Task A: Vertical scaling */ + {R_B0_A_VERT_LUMA_SCALING_INC,2, + "Task A: Vertical luminance scaling increment"}, + {R_B2_A_VERT_CHROMA_SCALING_INC,2, + "Task A: Vertical chrominance scaling increment"}, + {R_B4_A_VERT_SCALING_MODE_CNTL,1, + "Task A: Vertical scaling mode control"}, + /* 0xb5-0xb7 - Reserved */ + {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1, + "Task A: Vertical chrominance phase offset '00'"}, + {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1, + "Task A: Vertical chrominance phase offset '01'"}, + {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1, + "Task A: Vertical chrominance phase offset '10'"}, + {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1, + "Task A: Vertical chrominance phase offset '11'"}, + {R_BC_A_VERT_LUMA_PHASE_OFF_00,1, + "Task A: Vertical luminance phase offset '00'"}, + {R_BD_A_VERT_LUMA_PHASE_OFF_01,1, + "Task A: Vertical luminance phase offset '01'"}, + {R_BE_A_VERT_LUMA_PHASE_OFF_10,1, + "Task A: Vertical luminance phase offset '10'"}, + {R_BF_A_VERT_LUMA_PHASE_OFF_11,1, + "Task A: Vertical luminance phase offset '11'"}, + + /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ + /* Task B: Basic settings and acquisition window definition */ + {R_C0_B_TASK_HANDLING_CNTL,1, + "Task B: Task handling control"}, + {R_C1_B_X_PORT_FORMATS_AND_CONF,1, + "Task B: X port formats and configuration"}, + {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1, + "Task B: Input reference signal definition"}, + {R_C3_B_I_PORT_FORMATS_AND_CONF,1, + "Task B: I port formats and configuration"}, + {R_C4_B_HORIZ_INPUT_WINDOW_START,2, + "Task B: Horizontal input window start"}, + {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2, + "Task B: Horizontal input window length"}, + {R_C8_B_VERT_INPUT_WINDOW_START,2, + "Task B: Vertical input window start"}, + {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2, + "Task B: Vertical input window length"}, + {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2, + "Task B: Horizontal output window length"}, + {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2, + "Task B: Vertical output window length"}, + + /* Task B: FIR filtering and prescaling */ + {R_D0_B_HORIZ_PRESCALING,1, + "Task B: Horizontal prescaling"}, + {R_D1_B_ACCUMULATION_LENGTH,1, + "Task B: Accumulation length"}, + {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, + "Task B: Prescaler DC gain and FIR prefilter"}, + /* 0xd3 - Reserved */ + {R_D4_B_LUMA_BRIGHTNESS_CNTL,1, + "Task B: Luminance brightness control"}, + {R_D5_B_LUMA_CONTRAST_CNTL,1, + "Task B: Luminance contrast control"}, + {R_D6_B_CHROMA_SATURATION_CNTL,1, + "Task B: Chrominance saturation control"}, + /* 0xd7 - Reserved */ + + /* Task B: Horizontal phase scaling */ + {R_D8_B_HORIZ_LUMA_SCALING_INC,2, + "Task B: Horizontal luminance scaling increment"}, + {R_DA_B_HORIZ_LUMA_PHASE_OFF,1, + "Task B: Horizontal luminance phase offset"}, + /* 0xdb - Reserved */ + {R_DC_B_HORIZ_CHROMA_SCALING,2, + "Task B: Horizontal chrominance scaling"}, + {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1, + "Task B: Horizontal Phase Offset Chroma"}, + /* 0xdf - Reserved */ + + /* Task B: Vertical scaling */ + {R_E0_B_VERT_LUMA_SCALING_INC,2, + "Task B: Vertical luminance scaling increment"}, + {R_E2_B_VERT_CHROMA_SCALING_INC,2, + "Task B: Vertical chrominance scaling increment"}, + {R_E4_B_VERT_SCALING_MODE_CNTL,1, + "Task B: Vertical scaling mode control"}, + /* 0xe5-0xe7 - Reserved */ + {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1, + "Task B: Vertical chrominance phase offset '00'"}, + {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1, + "Task B: Vertical chrominance phase offset '01'"}, + {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1, + "Task B: Vertical chrominance phase offset '10'"}, + {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1, + "Task B: Vertical chrominance phase offset '11'"}, + {R_EC_B_VERT_LUMA_PHASE_OFF_00,1, + "Task B: Vertical luminance phase offset '00'"}, + {R_ED_B_VERT_LUMA_PHASE_OFF_01,1, + "Task B: Vertical luminance phase offset '01'"}, + {R_EE_B_VERT_LUMA_PHASE_OFF_10,1, + "Task B: Vertical luminance phase offset '10'"}, + {R_EF_B_VERT_LUMA_PHASE_OFF_11,1, + "Task B: Vertical luminance phase offset '11'"}, + + /* second PLL (PLL2) and Pulsegenerator Programming */ + { R_F0_LFCO_PER_LINE, 1, + "LFCO's per line"}, + { R_F1_P_I_PARAM_SELECT,1, + "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"}, + { R_F2_NOMINAL_PLL2_DTO,1, + "Nominal PLL2 DTO"}, + {R_F3_PLL_INCREMENT,1, + "PLL2 Increment"}, + {R_F4_PLL2_STATUS,1, + "PLL2 Status"}, + {R_F5_PULSGEN_LINE_LENGTH,1, + "Pulsgen. line length"}, + {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1, + "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"}, + {R_F7_PULSE_A_POS_MSB,1, + "Pulse A Position"}, + {R_F8_PULSE_B_POS,2, + "Pulse B Position"}, + {R_FA_PULSE_C_POS,2, + "Pulse C Position"}, + /* 0xfc to 0xfe - Reserved */ + {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1, + "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"}, +}; +#endif diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c new file mode 100644 index 000000000000..8ecb6564a315 --- /dev/null +++ b/drivers/media/i2c/saa7127.c @@ -0,0 +1,852 @@ +/* + * saa7127 - Philips SAA7127/SAA7129 video encoder driver + * + * Copyright (C) 2003 Roy Bulter + * + * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter + * + * Copyright (C) 2000-2001 Gillem + * Copyright (C) 2002 Andreas Oberritter + * + * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo + * + * Copyright (C) 1999 Nathan Laredo + * + * This driver is designed for the Hauppauge 250/350 Linux driver + * from the ivtv Project + * + * Copyright (C) 2003 Kevin Thayer + * + * Dual output support: + * Copyright (C) 2004 Eric Varsanyi + * + * NTSC Tuning and 7.5 IRE Setup + * Copyright (C) 2004 Chris Kennedy + * + * VBI additions & cleanup: + * Copyright (C) 2004, 2005 Hans Verkuil + * + * Note: the saa7126 is identical to the saa7127, and the saa7128 is + * identical to the saa7129, except that the saa7126 and saa7128 have + * macrovision anti-taping support. This driver will almost certainly + * work fine for those chips, except of course for the missing anti-taping + * support. + * + * This program is free software; 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 + +static int debug; +static int test_image; + +MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver"); +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); +MODULE_LICENSE("GPL"); +module_param(debug, int, 0644); +module_param(test_image, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); +MODULE_PARM_DESC(test_image, "test_image (0-1)"); + + +/* + * SAA7127 registers + */ + +#define SAA7127_REG_STATUS 0x00 +#define SAA7127_REG_WIDESCREEN_CONFIG 0x26 +#define SAA7127_REG_WIDESCREEN_ENABLE 0x27 +#define SAA7127_REG_BURST_START 0x28 +#define SAA7127_REG_BURST_END 0x29 +#define SAA7127_REG_COPYGEN_0 0x2a +#define SAA7127_REG_COPYGEN_1 0x2b +#define SAA7127_REG_COPYGEN_2 0x2c +#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d +#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 +#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 +#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a +#define SAA7129_REG_FADE_KEY_COL2 0x4f +#define SAA7127_REG_CHROMA_PHASE 0x5a +#define SAA7127_REG_GAINU 0x5b +#define SAA7127_REG_GAINV 0x5c +#define SAA7127_REG_BLACK_LEVEL 0x5d +#define SAA7127_REG_BLANKING_LEVEL 0x5e +#define SAA7127_REG_VBI_BLANKING 0x5f +#define SAA7127_REG_DAC_CONTROL 0x61 +#define SAA7127_REG_BURST_AMP 0x62 +#define SAA7127_REG_SUBC3 0x63 +#define SAA7127_REG_SUBC2 0x64 +#define SAA7127_REG_SUBC1 0x65 +#define SAA7127_REG_SUBC0 0x66 +#define SAA7127_REG_LINE_21_ODD_0 0x67 +#define SAA7127_REG_LINE_21_ODD_1 0x68 +#define SAA7127_REG_LINE_21_EVEN_0 0x69 +#define SAA7127_REG_LINE_21_EVEN_1 0x6a +#define SAA7127_REG_RCV_PORT_CONTROL 0x6b +#define SAA7127_REG_VTRIG 0x6c +#define SAA7127_REG_HTRIG_HI 0x6d +#define SAA7127_REG_MULTI 0x6e +#define SAA7127_REG_CLOSED_CAPTION 0x6f +#define SAA7127_REG_RCV2_OUTPUT_START 0x70 +#define SAA7127_REG_RCV2_OUTPUT_END 0x71 +#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 +#define SAA7127_REG_TTX_REQUEST_H_START 0x73 +#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 +#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 +#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 +#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 +#define SAA7127_REG_FIRST_ACTIVE 0x7a +#define SAA7127_REG_LAST_ACTIVE 0x7b +#define SAA7127_REG_MSB_VERTICAL 0x7c +#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e +#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f + +/* + ********************************************************************** + * + * Arrays with configuration parameters for the SAA7127 + * + ********************************************************************** + */ + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +static const struct i2c_reg_value saa7129_init_config_extra[] = { + { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, + { SAA7127_REG_VTRIG, 0xfa }, + { 0, 0 } +}; + +static const struct i2c_reg_value saa7127_init_config_common[] = { + { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d }, + { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 }, + { SAA7127_REG_COPYGEN_0, 0x77 }, + { SAA7127_REG_COPYGEN_1, 0x41 }, + { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */ + { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf }, + { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 }, + { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 }, + { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */ + { SAA7127_REG_LINE_21_ODD_0, 0x77 }, + { SAA7127_REG_LINE_21_ODD_1, 0x41 }, + { SAA7127_REG_LINE_21_EVEN_0, 0x88 }, + { SAA7127_REG_LINE_21_EVEN_1, 0x41 }, + { SAA7127_REG_RCV_PORT_CONTROL, 0x12 }, + { SAA7127_REG_VTRIG, 0xf9 }, + { SAA7127_REG_HTRIG_HI, 0x00 }, + { SAA7127_REG_RCV2_OUTPUT_START, 0x41 }, + { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 }, + { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 }, + { SAA7127_REG_TTX_REQUEST_H_START, 0x3e }, + { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 }, + { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 }, + { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 }, + { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 }, + { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 }, + { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 }, + { SAA7127_REG_FIRST_ACTIVE, 0x1a }, + { SAA7127_REG_LAST_ACTIVE, 0x01 }, + { SAA7127_REG_MSB_VERTICAL, 0xc0 }, + { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 }, + { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 }, + { 0, 0 } +}; + +#define SAA7127_60HZ_DAC_CONTROL 0x15 +static const struct i2c_reg_value saa7127_init_config_60hz[] = { + { SAA7127_REG_BURST_START, 0x19 }, + /* BURST_END is also used as a chip ID in saa7127_probe */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0xa3 }, + { SAA7127_REG_GAINU, 0x98 }, + { SAA7127_REG_GAINV, 0xd3 }, + { SAA7127_REG_BLACK_LEVEL, 0x39 }, + { SAA7127_REG_BLANKING_LEVEL, 0x2e }, + { SAA7127_REG_VBI_BLANKING, 0x2e }, + { SAA7127_REG_DAC_CONTROL, 0x15 }, + { SAA7127_REG_BURST_AMP, 0x4d }, + { SAA7127_REG_SUBC3, 0x1f }, + { SAA7127_REG_SUBC2, 0x7c }, + { SAA7127_REG_SUBC1, 0xf0 }, + { SAA7127_REG_SUBC0, 0x21 }, + { SAA7127_REG_MULTI, 0x90 }, + { SAA7127_REG_CLOSED_CAPTION, 0x11 }, + { 0, 0 } +}; + +#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02 +static struct i2c_reg_value saa7127_init_config_50hz_pal[] = { + { SAA7127_REG_BURST_START, 0x21 }, + /* BURST_END is also used as a chip ID in saa7127_probe */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0x3f }, + { SAA7127_REG_GAINU, 0x7d }, + { SAA7127_REG_GAINV, 0xaf }, + { SAA7127_REG_BLACK_LEVEL, 0x33 }, + { SAA7127_REG_BLANKING_LEVEL, 0x35 }, + { SAA7127_REG_VBI_BLANKING, 0x35 }, + { SAA7127_REG_DAC_CONTROL, 0x02 }, + { SAA7127_REG_BURST_AMP, 0x2f }, + { SAA7127_REG_SUBC3, 0xcb }, + { SAA7127_REG_SUBC2, 0x8a }, + { SAA7127_REG_SUBC1, 0x09 }, + { SAA7127_REG_SUBC0, 0x2a }, + { SAA7127_REG_MULTI, 0xa0 }, + { SAA7127_REG_CLOSED_CAPTION, 0x00 }, + { 0, 0 } +}; + +#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08 +static struct i2c_reg_value saa7127_init_config_50hz_secam[] = { + { SAA7127_REG_BURST_START, 0x21 }, + /* BURST_END is also used as a chip ID in saa7127_probe */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0x3f }, + { SAA7127_REG_GAINU, 0x6a }, + { SAA7127_REG_GAINV, 0x81 }, + { SAA7127_REG_BLACK_LEVEL, 0x33 }, + { SAA7127_REG_BLANKING_LEVEL, 0x35 }, + { SAA7127_REG_VBI_BLANKING, 0x35 }, + { SAA7127_REG_DAC_CONTROL, 0x08 }, + { SAA7127_REG_BURST_AMP, 0x2f }, + { SAA7127_REG_SUBC3, 0xb2 }, + { SAA7127_REG_SUBC2, 0x3b }, + { SAA7127_REG_SUBC1, 0xa3 }, + { SAA7127_REG_SUBC0, 0x28 }, + { SAA7127_REG_MULTI, 0x90 }, + { SAA7127_REG_CLOSED_CAPTION, 0x00 }, + { 0, 0 } +}; + +/* + ********************************************************************** + * + * Encoder Struct, holds the configuration state of the encoder + * + ********************************************************************** + */ + +struct saa7127_state { + struct v4l2_subdev sd; + v4l2_std_id std; + u32 ident; + enum saa7127_input_type input_type; + enum saa7127_output_type output_type; + int video_enable; + int wss_enable; + u16 wss_mode; + int cc_enable; + u16 cc_data; + int xds_enable; + u16 xds_data; + int vps_enable; + u8 vps_data[5]; + u8 reg_2d; + u8 reg_3a; + u8 reg_3a_cb; /* colorbar bit */ + u8 reg_61; +}; + +static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7127_state, sd); +} + +static const char * const output_strs[] = +{ + "S-Video + Composite", + "Composite", + "S-Video", + "RGB", + "YUV C", + "YUV V" +}; + +static const char * const wss_strs[] = { + "invalid", + "letterbox 14:9 center", + "letterbox 14:9 top", + "invalid", + "letterbox 16:9 top", + "invalid", + "invalid", + "16:9 full format anamorphic", + "4:3 full format", + "invalid", + "invalid", + "letterbox 16:9 center", + "invalid", + "letterbox >16:9 center", + "14:9 full format center", + "invalid", +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + for (i = 0; i < 3; i++) { + if (i2c_smbus_write_byte_data(client, reg, val) == 0) + return 0; + } + v4l2_err(sd, "I2C Write Problem\n"); + return -1; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_write_inittab(struct v4l2_subdev *sd, + const struct i2c_reg_value *regs) +{ + while (regs->reg != 0) { + saa7127_write(sd, regs->reg, regs->value); + regs++; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 16)) + return -EINVAL; + if (state->vps_enable != enable) { + v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x54, enable << 7); + state->vps_enable = enable; + } + if (!enable) + return 0; + + state->vps_data[0] = data->data[2]; + state->vps_data[1] = data->data[8]; + state->vps_data[2] = data->data[9]; + state->vps_data[3] = data->data[10]; + state->vps_data[4] = data->data[11]; + v4l2_dbg(1, debug, sd, "Set VPS data %*ph\n", 5, state->vps_data); + saa7127_write(sd, 0x55, state->vps_data[0]); + saa7127_write(sd, 0x56, state->vps_data[1]); + saa7127_write(sd, 0x57, state->vps_data[2]); + saa7127_write(sd, 0x58, state->vps_data[3]); + saa7127_write(sd, 0x59, state->vps_data[4]); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + u16 cc = data->data[1] << 8 | data->data[0]; + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 21)) + return -EINVAL; + if (state->cc_enable != enable) { + v4l2_dbg(1, debug, sd, + "Turn CC %s\n", enable ? "on" : "off"); + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, + (state->xds_enable << 7) | (enable << 6) | 0x11); + state->cc_enable = enable; + } + if (!enable) + return 0; + + v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); + state->cc_data = cc; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + u16 xds = data->data[1] << 8 | data->data[0]; + int enable = (data->line != 0); + + if (enable && (data->field != 1 || data->line != 21)) + return -EINVAL; + if (state->xds_enable != enable) { + v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, + (enable << 7) | (state->cc_enable << 6) | 0x11); + state->xds_enable = enable; + } + if (!enable) + return 0; + + v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); + state->xds_data = xds; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = to_state(sd); + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 23)) + return -EINVAL; + if (state->wss_enable != enable) { + v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x27, enable << 7); + state->wss_enable = enable; + } + if (!enable) + return 0; + + saa7127_write(sd, 0x26, data->data[0]); + saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); + v4l2_dbg(1, debug, sd, + "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); + state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) +{ + struct saa7127_state *state = to_state(sd); + + if (enable) { + v4l2_dbg(1, debug, sd, "Enable Video Output\n"); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x61, state->reg_61); + } else { + v4l2_dbg(1, debug, sd, "Disable Video Output\n"); + saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); + saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); + } + state->video_enable = enable; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7127_state *state = to_state(sd); + const struct i2c_reg_value *inittab; + + if (std & V4L2_STD_525_60) { + v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); + inittab = saa7127_init_config_60hz; + state->reg_61 = SAA7127_60HZ_DAC_CONTROL; + + } else if (state->ident == V4L2_IDENT_SAA7129 && + (std & V4L2_STD_SECAM) && + !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) { + + /* If and only if SECAM, with a SAA712[89] */ + v4l2_dbg(1, debug, sd, + "Selecting 50 Hz SECAM video Standard\n"); + inittab = saa7127_init_config_50hz_secam; + state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL; + + } else { + v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n"); + inittab = saa7127_init_config_50hz_pal; + state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL; + } + + /* Write Table */ + saa7127_write_inittab(sd, inittab); + state->std = std; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) +{ + struct saa7127_state *state = to_state(sd); + + switch (output) { + case SAA7127_OUTPUT_TYPE_RGB: + state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_COMPOSITE: + if (state->ident == V4L2_IDENT_SAA7129) + state->reg_2d = 0x20; /* CVBS only */ + else + state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_SVIDEO: + if (state->ident == V4L2_IDENT_SAA7129) + state->reg_2d = 0x18; /* Y + C */ + else + state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_YUV_V: + state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ + state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ + break; + + case SAA7127_OUTPUT_TYPE_YUV_C: + state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ + state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ + break; + + case SAA7127_OUTPUT_TYPE_BOTH: + if (state->ident == V4L2_IDENT_SAA7129) + state->reg_2d = 0x38; + else + state->reg_2d = 0xbf; + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + default: + return -EINVAL; + } + v4l2_dbg(1, debug, sd, + "Selecting %s output type\n", output_strs[output]); + + /* Configure Encoder */ + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); + state->output_type = output; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) +{ + struct saa7127_state *state = to_state(sd); + + switch (input) { + case SAA7127_INPUT_TYPE_NORMAL: /* avia */ + v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); + state->reg_3a_cb = 0; + break; + + case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ + v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); + state->reg_3a_cb = 0x80; + break; + + default: + return -EINVAL; + } + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); + state->input_type = input; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7127_state *state = to_state(sd); + + if (state->std == std) + return 0; + return saa7127_set_std(sd, std); +} + +static int saa7127_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7127_state *state = to_state(sd); + int rc = 0; + + if (state->input_type != input) + rc = saa7127_set_input_type(sd, input); + if (rc == 0 && state->output_type != output) + rc = saa7127_set_output_type(sd, output); + return rc; +} + +static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7127_state *state = to_state(sd); + + if (state->video_enable == enable) + return 0; + return saa7127_set_video_enable(sd, enable); +} + +static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + struct saa7127_state *state = to_state(sd); + + memset(fmt, 0, sizeof(*fmt)); + if (state->vps_enable) + fmt->service_lines[0][16] = V4L2_SLICED_VPS; + if (state->wss_enable) + fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; + if (state->cc_enable) { + fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } + fmt->service_set = + (state->vps_enable ? V4L2_SLICED_VPS : 0) | + (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | + (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); + return 0; +} + +static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + switch (data->id) { + case V4L2_SLICED_WSS_625: + return saa7127_set_wss(sd, data); + case V4L2_SLICED_VPS: + return saa7127_set_vps(sd, data); + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) + return saa7127_set_cc(sd, data); + return saa7127_set_xds(sd, data); + default: + return -EINVAL; + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa7127_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct saa7127_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} + +static int saa7127_log_status(struct v4l2_subdev *sd) +{ + struct saa7127_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); + v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); + v4l2_info(sd, "Output: %s\n", state->video_enable ? + output_strs[state->output_type] : "disabled"); + v4l2_info(sd, "WSS: %s\n", state->wss_enable ? + wss_strs[state->wss_mode] : "disabled"); + v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); + v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7127_core_ops = { + .log_status = saa7127_log_status, + .g_chip_ident = saa7127_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa7127_g_register, + .s_register = saa7127_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops saa7127_video_ops = { + .s_std_output = saa7127_s_std_output, + .s_routing = saa7127_s_routing, + .s_stream = saa7127_s_stream, +}; + +static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { + .s_vbi_data = saa7127_s_vbi_data, + .g_sliced_fmt = saa7127_g_sliced_fmt, +}; + +static const struct v4l2_subdev_ops saa7127_ops = { + .core = &saa7127_core_ops, + .video = &saa7127_video_ops, + .vbi = &saa7127_vbi_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa7127_state *state; + struct v4l2_subdev *sd; + struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", + client->addr << 1); + + state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa7127_ops); + + /* First test register 0: Bits 5-7 are a version ID (should be 0), + and bit 2 should also be 0. + This is rather general, so the second test is more specific and + looks at the 'ending point of burst in clock cycles' which is + 0x1d after a reset and not expected to ever change. */ + if ((saa7127_read(sd, 0) & 0xe4) != 0 || + (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { + v4l2_dbg(1, debug, sd, "saa7127 not found\n"); + kfree(state); + return -ENODEV; + } + + if (id->driver_data) { /* Chip type is already known */ + state->ident = id->driver_data; + } else { /* Needs detection */ + int read_result; + + /* Detect if it's an saa7129 */ + read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); + if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, + read_result); + state->ident = V4L2_IDENT_SAA7129; + strlcpy(client->name, "saa7129", I2C_NAME_SIZE); + } else { + state->ident = V4L2_IDENT_SAA7127; + strlcpy(client->name, "saa7127", I2C_NAME_SIZE); + } + } + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + + v4l2_dbg(1, debug, sd, "Configuring encoder\n"); + saa7127_write_inittab(sd, saa7127_init_config_common); + saa7127_set_std(sd, V4L2_STD_NTSC); + saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); + saa7127_set_vps(sd, &vbi); + saa7127_set_wss(sd, &vbi); + saa7127_set_cc(sd, &vbi); + saa7127_set_xds(sd, &vbi); + if (test_image == 1) + /* The Encoder has an internal Colorbar generator */ + /* This can be used for debugging */ + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); + else + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); + saa7127_set_video_enable(sd, 1); + + if (state->ident == V4L2_IDENT_SAA7129) + saa7127_write_inittab(sd, saa7129_init_config_extra); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + /* Turn off TV output */ + saa7127_set_video_enable(sd, 0); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_device_id saa7127_id[] = { + { "saa7127_auto", 0 }, /* auto-detection */ + { "saa7126", V4L2_IDENT_SAA7127 }, + { "saa7127", V4L2_IDENT_SAA7127 }, + { "saa7128", V4L2_IDENT_SAA7129 }, + { "saa7129", V4L2_IDENT_SAA7129 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7127_id); + +static struct i2c_driver saa7127_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7127", + }, + .probe = saa7127_probe, + .remove = saa7127_remove, + .id_table = saa7127_id, +}; + +module_i2c_driver(saa7127_driver); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c new file mode 100644 index 000000000000..1e84466515aa --- /dev/null +++ b/drivers/media/i2c/saa717x.c @@ -0,0 +1,1378 @@ +/* + * saa717x - Philips SAA717xHL video decoder driver + * + * Based on the saa7115 driver + * + * Changes by Ohta Kyuma + * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004) + * + * Changes by T.Adachi (tadachi@tadachi-net.com) + * - support audio, video scaler etc, and checked the initialize sequence. + * + * Cleaned up by Hans Verkuil + * + * Note: this is a reversed engineered driver based on captures from + * the I2C bus under Windows. This chip is very similar to the saa7134, + * though. Unfortunately, this driver is currently only working for NTSC. + * + * This program is free software; 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 + +MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); +MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +struct saa717x_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + v4l2_std_id std; + int input; + int enable; + int radio; + int playback; + int audio; + int tuner_audio_mode; + int audio_main_mute; + int audio_main_vol_r; + int audio_main_vol_l; + u16 audio_main_bass; + u16 audio_main_treble; + u16 audio_main_volume; + u16 audio_main_balance; + int audio_input; +}; + +static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa717x_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd; +} + +/* ----------------------------------------------------------------------- */ + +/* for audio mode */ +#define TUNER_AUDIO_MONO 0 /* LL */ +#define TUNER_AUDIO_STEREO 1 /* LR */ +#define TUNER_AUDIO_LANG1 2 /* LL */ +#define TUNER_AUDIO_LANG2 3 /* RR */ + +#define SAA717X_NTSC_WIDTH (704) +#define SAA717X_NTSC_HEIGHT (480) + +/* ----------------------------------------------------------------------- */ + +static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_adapter *adap = client->adapter; + int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; + unsigned char mm1[6]; + struct i2c_msg msg; + + msg.flags = 0; + msg.addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + + if (fw_addr) { + mm1[4] = (value >> 16) & 0xff; + mm1[3] = (value >> 8) & 0xff; + mm1[2] = value & 0xff; + } else { + mm1[2] = value & 0xff; + } + msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ + msg.buf = mm1; + v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); + return i2c_transfer(adap, &msg, 1) == 1; +} + +static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) +{ + while (data[0] || data[1]) { + saa717x_write(sd, data[0], data[1]); + data += 2; + } +} + +static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_adapter *adap = client->adapter; + int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; + unsigned char mm1[2]; + unsigned char mm2[4] = { 0, 0, 0, 0 }; + struct i2c_msg msgs[2]; + u32 value; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + msgs[0].len = 2; + msgs[0].buf = mm1; + msgs[1].len = fw_addr ? 3 : 1; /* Multibyte Registers contains *only* 3 bytes */ + msgs[1].buf = mm2; + i2c_transfer(adap, msgs, 2); + + if (fw_addr) + value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16); + else + value = mm2[0] & 0xff; + + v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); + return value; +} + +/* ----------------------------------------------------------------------- */ + +static u32 reg_init_initialize[] = +{ + /* from linux driver */ + 0x101, 0x008, /* Increment delay */ + + 0x103, 0x000, /* Analog input control 2 */ + 0x104, 0x090, /* Analog input control 3 */ + 0x105, 0x090, /* Analog input control 4 */ + 0x106, 0x0eb, /* Horizontal sync start */ + 0x107, 0x0e0, /* Horizontal sync stop */ + 0x109, 0x055, /* Luminance control */ + + 0x10f, 0x02a, /* Chroma gain control */ + 0x110, 0x000, /* Chroma control 2 */ + + 0x114, 0x045, /* analog/ADC */ + + 0x118, 0x040, /* RAW data gain */ + 0x119, 0x080, /* RAW data offset */ + + 0x044, 0x000, /* VBI horizontal input window start (L) TASK A */ + 0x045, 0x000, /* VBI horizontal input window start (H) TASK A */ + 0x046, 0x0cf, /* VBI horizontal input window stop (L) TASK A */ + 0x047, 0x002, /* VBI horizontal input window stop (H) TASK A */ + + 0x049, 0x000, /* VBI vertical input window start (H) TASK A */ + + 0x04c, 0x0d0, /* VBI horizontal output length (L) TASK A */ + 0x04d, 0x002, /* VBI horizontal output length (H) TASK A */ + + 0x064, 0x080, /* Lumina brightness TASK A */ + 0x065, 0x040, /* Luminance contrast TASK A */ + 0x066, 0x040, /* Chroma saturation TASK A */ + /* 067H: Reserved */ + 0x068, 0x000, /* VBI horizontal scaling increment (L) TASK A */ + 0x069, 0x004, /* VBI horizontal scaling increment (H) TASK A */ + 0x06a, 0x000, /* VBI phase offset TASK A */ + + 0x06e, 0x000, /* Horizontal phase offset Luma TASK A */ + 0x06f, 0x000, /* Horizontal phase offset Chroma TASK A */ + + 0x072, 0x000, /* Vertical filter mode TASK A */ + + 0x084, 0x000, /* VBI horizontal input window start (L) TAKS B */ + 0x085, 0x000, /* VBI horizontal input window start (H) TAKS B */ + 0x086, 0x0cf, /* VBI horizontal input window stop (L) TAKS B */ + 0x087, 0x002, /* VBI horizontal input window stop (H) TAKS B */ + + 0x089, 0x000, /* VBI vertical input window start (H) TAKS B */ + + 0x08c, 0x0d0, /* VBI horizontal output length (L) TASK B */ + 0x08d, 0x002, /* VBI horizontal output length (H) TASK B */ + + 0x0a4, 0x080, /* Lumina brightness TASK B */ + 0x0a5, 0x040, /* Luminance contrast TASK B */ + 0x0a6, 0x040, /* Chroma saturation TASK B */ + /* 0A7H reserved */ + 0x0a8, 0x000, /* VBI horizontal scaling increment (L) TASK B */ + 0x0a9, 0x004, /* VBI horizontal scaling increment (H) TASK B */ + 0x0aa, 0x000, /* VBI phase offset TASK B */ + + 0x0ae, 0x000, /* Horizontal phase offset Luma TASK B */ + 0x0af, 0x000, /*Horizontal phase offset Chroma TASK B */ + + 0x0b2, 0x000, /* Vertical filter mode TASK B */ + + 0x00c, 0x000, /* Start point GREEN path */ + 0x00d, 0x000, /* Start point BLUE path */ + 0x00e, 0x000, /* Start point RED path */ + + 0x010, 0x010, /* GREEN path gamma curve --- */ + 0x011, 0x020, + 0x012, 0x030, + 0x013, 0x040, + 0x014, 0x050, + 0x015, 0x060, + 0x016, 0x070, + 0x017, 0x080, + 0x018, 0x090, + 0x019, 0x0a0, + 0x01a, 0x0b0, + 0x01b, 0x0c0, + 0x01c, 0x0d0, + 0x01d, 0x0e0, + 0x01e, 0x0f0, + 0x01f, 0x0ff, /* --- GREEN path gamma curve */ + + 0x020, 0x010, /* BLUE path gamma curve --- */ + 0x021, 0x020, + 0x022, 0x030, + 0x023, 0x040, + 0x024, 0x050, + 0x025, 0x060, + 0x026, 0x070, + 0x027, 0x080, + 0x028, 0x090, + 0x029, 0x0a0, + 0x02a, 0x0b0, + 0x02b, 0x0c0, + 0x02c, 0x0d0, + 0x02d, 0x0e0, + 0x02e, 0x0f0, + 0x02f, 0x0ff, /* --- BLUE path gamma curve */ + + 0x030, 0x010, /* RED path gamma curve --- */ + 0x031, 0x020, + 0x032, 0x030, + 0x033, 0x040, + 0x034, 0x050, + 0x035, 0x060, + 0x036, 0x070, + 0x037, 0x080, + 0x038, 0x090, + 0x039, 0x0a0, + 0x03a, 0x0b0, + 0x03b, 0x0c0, + 0x03c, 0x0d0, + 0x03d, 0x0e0, + 0x03e, 0x0f0, + 0x03f, 0x0ff, /* --- RED path gamma curve */ + + 0x109, 0x085, /* Luminance control */ + + /**** from app start ****/ + 0x584, 0x000, /* AGC gain control */ + 0x585, 0x000, /* Program count */ + 0x586, 0x003, /* Status reset */ + 0x588, 0x0ff, /* Number of audio samples (L) */ + 0x589, 0x00f, /* Number of audio samples (M) */ + 0x58a, 0x000, /* Number of audio samples (H) */ + 0x58b, 0x000, /* Audio select */ + 0x58c, 0x010, /* Audio channel assign1 */ + 0x58d, 0x032, /* Audio channel assign2 */ + 0x58e, 0x054, /* Audio channel assign3 */ + 0x58f, 0x023, /* Audio format */ + 0x590, 0x000, /* SIF control */ + + 0x595, 0x000, /* ?? */ + 0x596, 0x000, /* ?? */ + 0x597, 0x000, /* ?? */ + + 0x464, 0x00, /* Digital input crossbar1 */ + + 0x46c, 0xbbbb10, /* Digital output selection1-3 */ + 0x470, 0x101010, /* Digital output selection4-6 */ + + 0x478, 0x00, /* Sound feature control */ + + 0x474, 0x18, /* Softmute control */ + + 0x454, 0x0425b9, /* Sound Easy programming(reset) */ + 0x454, 0x042539, /* Sound Easy programming(reset) */ + + + /**** common setting( of DVD play, including scaler commands) ****/ + 0x042, 0x003, /* Data path configuration for VBI (TASK A) */ + + 0x082, 0x003, /* Data path configuration for VBI (TASK B) */ + + 0x108, 0x0f8, /* Sync control */ + 0x2a9, 0x0fd, /* ??? */ + 0x102, 0x089, /* select video input "mode 9" */ + 0x111, 0x000, /* Mode/delay control */ + + 0x10e, 0x00a, /* Chroma control 1 */ + + 0x594, 0x002, /* SIF, analog I/O select */ + + 0x454, 0x0425b9, /* Sound */ + 0x454, 0x042539, + + 0x111, 0x000, + 0x10e, 0x00a, + 0x464, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x0ff, 0x0ff, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x0ff, 0x0ff, + 0x108, 0x0f8, + 0x111, 0x000, + 0x10e, 0x00a, + 0x2a9, 0x0fd, + 0x464, 0x001, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x0a6, + 0x108, 0x0f8, + 0x042, 0x003, + 0x082, 0x003, + 0x454, 0x0425b9, + 0x454, 0x042539, + 0x193, 0x000, + 0x193, 0x0a6, + 0x464, 0x000, + + 0, 0 +}; + +/* Tuner */ +static u32 reg_init_tuner_input[] = { + 0x108, 0x0f8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x00a, /* Chroma control 1 */ + 0, 0 +}; + +/* Composite */ +static u32 reg_init_composite_input[] = { + 0x108, 0x0e8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x04a, /* Chroma control 1 */ + 0, 0 +}; + +/* S-Video */ +static u32 reg_init_svideo_input[] = { + 0x108, 0x0e8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x04a, /* Chroma control 1 */ + 0, 0 +}; + +static u32 reg_set_audio_template[4][2] = +{ + { /* for MONO + tadachi 6/29 DMA audio output select? + Register 0x46c + 7-4: DMA2, 3-0: DMA1 ch. DMA4, DMA3 DMA2, DMA1 + 0: MAIN left, 1: MAIN right + 2: AUX1 left, 3: AUX1 right + 4: AUX2 left, 5: AUX2 right + 6: DPL left, 7: DPL right + 8: DPL center, 9: DPL surround + A: monitor output, B: digital sense */ + 0xbbbb00, + + /* tadachi 6/29 DAC and I2S output select? + Register 0x470 + 7-4:DAC right ch. 3-0:DAC left ch. + I2S1 right,left I2S2 right,left */ + 0x00, + }, + { /* for STEREO */ + 0xbbbb10, 0x101010, + }, + { /* for LANG1 */ + 0xbbbb00, 0x00, + }, + { /* for LANG2/SAP */ + 0xbbbb11, 0x111111, + } +}; + + +/* Get detected audio flags (from saa7134 driver) */ +static void get_inf_dev_status(struct v4l2_subdev *sd, + int *dual_flag, int *stereo_flag) +{ + u32 reg_data3; + + static char *stdres[0x20] = { + [0x00] = "no standard detected", + [0x01] = "B/G (in progress)", + [0x02] = "D/K (in progress)", + [0x03] = "M (in progress)", + + [0x04] = "B/G A2", + [0x05] = "B/G NICAM", + [0x06] = "D/K A2 (1)", + [0x07] = "D/K A2 (2)", + [0x08] = "D/K A2 (3)", + [0x09] = "D/K NICAM", + [0x0a] = "L NICAM", + [0x0b] = "I NICAM", + + [0x0c] = "M Korea", + [0x0d] = "M BTSC ", + [0x0e] = "M EIAJ", + + [0x0f] = "FM radio / IF 10.7 / 50 deemp", + [0x10] = "FM radio / IF 10.7 / 75 deemp", + [0x11] = "FM radio / IF sel / 50 deemp", + [0x12] = "FM radio / IF sel / 75 deemp", + + [0x13 ... 0x1e] = "unknown", + [0x1f] = "??? [in progress]", + }; + + + *dual_flag = *stereo_flag = 0; + + /* (demdec status: 0x528) */ + + /* read current status */ + reg_data3 = saa717x_read(sd, 0x0528); + + v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", + reg_data3, stdres[reg_data3 & 0x1f], + (reg_data3 & 0x000020) ? ",stereo" : "", + (reg_data3 & 0x000040) ? ",dual" : ""); + v4l2_dbg(1, debug, sd, "detailed status: " + "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", + (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", + (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", + (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "", + (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "", + + (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "", + (reg_data3 & 0x001000) ? " SAP carrier " : "", + (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "", + (reg_data3 & 0x004000) ? " SAP noise mute " : "", + (reg_data3 & 0x008000) ? " VDSP " : "", + + (reg_data3 & 0x010000) ? " NICST " : "", + (reg_data3 & 0x020000) ? " NICDU " : "", + (reg_data3 & 0x040000) ? " NICAM muted " : "", + (reg_data3 & 0x080000) ? " NICAM reserve sound " : "", + + (reg_data3 & 0x100000) ? " init done " : ""); + + if (reg_data3 & 0x000220) { + v4l2_dbg(1, debug, sd, "ST!!!\n"); + *stereo_flag = 1; + } + + if (reg_data3 & 0x000140) { + v4l2_dbg(1, debug, sd, "DUAL!!!\n"); + *dual_flag = 1; + } +} + +/* regs write to set audio mode */ +static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) +{ + v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", + audio_mode); + + saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); + saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); +} + +/* write regs to set audio volume, bass and treble */ +static int set_audio_regs(struct v4l2_subdev *sd, + struct saa717x_state *decoder) +{ + u8 mute = 0xac; /* -84 dB */ + u32 val; + unsigned int work_l, work_r; + + /* set SIF analog I/O select */ + saa717x_write(sd, 0x0594, decoder->audio_input); + v4l2_dbg(1, debug, sd, "set audio input %d\n", + decoder->audio_input); + + /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ + work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768; + work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768; + decoder->audio_main_vol_l = (long)work_l * (24 - (-40)) / 65535 - 40; + decoder->audio_main_vol_r = (long)work_r * (24 - (-40)) / 65535 - 40; + + /* set main volume */ + /* main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) */ + /* def:0dB->6dB(MPG600GR) */ + /* if mute is on, set mute */ + if (decoder->audio_main_mute) { + val = mute | (mute << 8); + } else { + val = (u8)decoder->audio_main_vol_l | + ((u8)decoder->audio_main_vol_r << 8); + } + + saa717x_write(sd, 0x480, val); + + /* set bass and treble */ + val = decoder->audio_main_bass & 0x1f; + val |= (decoder->audio_main_treble & 0x1f) << 5; + saa717x_write(sd, 0x488, val); + return 0; +} + +/********** scaling staff ***********/ +static void set_h_prescale(struct v4l2_subdev *sd, + int task, int prescale) +{ + static const struct { + int xpsc; + int xacl; + int xc2_1; + int xdcg; + int vpfy; + } vals[] = { + /* XPSC XACL XC2_1 XDCG VPFY */ + { 1, 0, 0, 0, 0 }, + { 2, 2, 1, 2, 2 }, + { 3, 4, 1, 3, 2 }, + { 4, 8, 1, 4, 2 }, + { 5, 8, 1, 4, 2 }, + { 6, 8, 1, 4, 3 }, + { 7, 8, 1, 4, 3 }, + { 8, 15, 0, 4, 3 }, + { 9, 15, 0, 4, 3 }, + { 10, 16, 1, 5, 3 }, + }; + static const int count = ARRAY_SIZE(vals); + int i, task_shift; + + task_shift = task * 0x40; + for (i = 0; i < count; i++) + if (vals[i].xpsc == prescale) + break; + if (i == count) + return; + + /* horizonal prescaling */ + saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); + /* accumulation length */ + saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); + /* level control */ + saa717x_write(sd, 0x62 + task_shift, + (vals[i].xc2_1 << 3) | vals[i].xdcg); + /*FIR prefilter control */ + saa717x_write(sd, 0x63 + task_shift, + (vals[i].vpfy << 2) | vals[i].vpfy); +} + +/********** scaling staff ***********/ +static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) +{ + int task_shift; + + task_shift = task * 0x40; + /* Vertical scaling ratio (LOW) */ + saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); + /* Vertical scaling ratio (HI) */ + saa717x_write(sd, 0x71 + task_shift, yscale >> 8); +} + +static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct saa717x_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + saa717x_write(sd, 0x10a, ctrl->val); + return 0; + + case V4L2_CID_CONTRAST: + saa717x_write(sd, 0x10b, ctrl->val); + return 0; + + case V4L2_CID_SATURATION: + saa717x_write(sd, 0x10c, ctrl->val); + return 0; + + case V4L2_CID_HUE: + saa717x_write(sd, 0x10d, ctrl->val); + return 0; + + case V4L2_CID_AUDIO_MUTE: + state->audio_main_mute = ctrl->val; + break; + + case V4L2_CID_AUDIO_VOLUME: + state->audio_main_volume = ctrl->val; + break; + + case V4L2_CID_AUDIO_BALANCE: + state->audio_main_balance = ctrl->val; + break; + + case V4L2_CID_AUDIO_TREBLE: + state->audio_main_treble = ctrl->val; + break; + + case V4L2_CID_AUDIO_BASS: + state->audio_main_bass = ctrl->val; + break; + + default: + return 0; + } + set_audio_regs(sd, state); + return 0; +} + +static int saa717x_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa717x_state *decoder = to_state(sd); + int is_tuner = input & 0x80; /* tuner input flag */ + + input &= 0x7f; + + v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); + /* inputs from 0-9 are available*/ + /* saa717x have mode0-mode9 but mode5 is reserved. */ + if (input > 9 || input == 5) + return -EINVAL; + + if (decoder->input != input) { + int input_line = input; + + decoder->input = input_line; + v4l2_dbg(1, debug, sd, "now setting %s input %d\n", + input_line >= 6 ? "S-Video" : "Composite", + input_line); + + /* select mode */ + saa717x_write(sd, 0x102, + (saa717x_read(sd, 0x102) & 0xf0) | + input_line); + + /* bypass chrominance trap for modes 6..9 */ + saa717x_write(sd, 0x109, + (saa717x_read(sd, 0x109) & 0x7f) | + (input_line < 6 ? 0x0 : 0x80)); + + /* change audio_mode */ + if (is_tuner) { + /* tuner */ + set_audio_mode(sd, decoder->tuner_audio_mode); + } else { + /* Force to STEREO mode if Composite or + * S-Video were chosen */ + set_audio_mode(sd, TUNER_AUDIO_STEREO); + } + /* change initialize procedure (Composite/S-Video) */ + if (is_tuner) + saa717x_write_regs(sd, reg_init_tuner_input); + else if (input_line >= 6) + saa717x_write_regs(sd, reg_init_svideo_input); + else + saa717x_write_regs(sd, reg_init_composite_input); + } + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa717x_read(sd, reg->reg); + reg->size = 1; + return 0; +} + +static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr = reg->reg & 0xffff; + u8 val = reg->val & 0xff; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa717x_write(sd, addr, val); + return 0; +} +#endif + +static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + int prescale, h_scale, v_scale; + + v4l2_dbg(1, debug, sd, "decoder set size\n"); + + if (fmt->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + + /* FIXME need better bounds checking here */ + if (fmt->width < 1 || fmt->width > 1440) + return -EINVAL; + if (fmt->height < 1 || fmt->height > 960) + return -EINVAL; + + fmt->field = V4L2_FIELD_INTERLACED; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + + /* scaling setting */ + /* NTSC and interlace only */ + prescale = SAA717X_NTSC_WIDTH / fmt->width; + if (prescale == 0) + prescale = 1; + h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / fmt->width; + /* interlace */ + v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / fmt->height; + + /* Horizontal prescaling etc */ + set_h_prescale(sd, 0, prescale); + set_h_prescale(sd, 1, prescale); + + /* Horizontal scaling increment */ + /* TASK A */ + saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); + + /* Vertical prescaling etc */ + set_v_scale(sd, 0, v_scale); + set_v_scale(sd, 1, v_scale); + + /* set video output size */ + /* video number of pixels at output */ + /* TASK A */ + saa717x_write(sd, 0x5C, (u8)(fmt->width & 0xFF)); + saa717x_write(sd, 0x5D, (u8)((fmt->width >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9C, (u8)(fmt->width & 0xFF)); + saa717x_write(sd, 0x9D, (u8)((fmt->width >> 8) & 0xFF)); + + /* video number of lines at output */ + /* TASK A */ + saa717x_write(sd, 0x5E, (u8)(fmt->height & 0xFF)); + saa717x_write(sd, 0x5F, (u8)((fmt->height >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9E, (u8)(fmt->height & 0xFF)); + saa717x_write(sd, 0x9F, (u8)((fmt->height >> 8) & 0xFF)); + return 0; +} + +static int saa717x_s_radio(struct v4l2_subdev *sd) +{ + struct saa717x_state *decoder = to_state(sd); + + decoder->radio = 1; + return 0; +} + +static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa717x_state *decoder = to_state(sd); + + v4l2_dbg(1, debug, sd, "decoder set norm "); + v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); + + decoder->radio = 0; + decoder->std = std; + return 0; +} + +static int saa717x_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa717x_state *decoder = to_state(sd); + + if (input < 3) { /* FIXME! --tadachi */ + decoder->audio_input = input; + v4l2_dbg(1, debug, sd, + "set decoder audio input to %d\n", + decoder->audio_input); + set_audio_regs(sd, decoder); + return 0; + } + return -ERANGE; +} + +static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa717x_state *decoder = to_state(sd); + + v4l2_dbg(1, debug, sd, "decoder %s output\n", + enable ? "enable" : "disable"); + decoder->enable = enable; + saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); + return 0; +} + +/* change audio mode */ +static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int audio_mode; + char *mes[4] = { + "MONO", "STEREO", "LANG1", "LANG2/SAP" + }; + + audio_mode = TUNER_AUDIO_STEREO; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + audio_mode = TUNER_AUDIO_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + audio_mode = TUNER_AUDIO_STEREO; + break; + case V4L2_TUNER_MODE_LANG2: + audio_mode = TUNER_AUDIO_LANG2; + break; + case V4L2_TUNER_MODE_LANG1: + audio_mode = TUNER_AUDIO_LANG1; + break; + } + + v4l2_dbg(1, debug, sd, "change audio mode to %s\n", + mes[audio_mode]); + decoder->tuner_audio_mode = audio_mode; + /* The registers are not changed here. */ + /* See DECODER_ENABLE_OUTPUT section. */ + set_audio_mode(sd, decoder->tuner_audio_mode); + return 0; +} + +static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int dual_f, stereo_f; + + if (decoder->radio) + return 0; + get_inf_dev_status(sd, &dual_f, &stereo_f); + + v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", + stereo_f, dual_f); + + /* mono */ + if ((dual_f == 0) && (stereo_f == 0)) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); + } + + /* stereo */ + if (stereo_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_STEREO || + vt->audmode == V4L2_TUNER_MODE_LANG1) { + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); + } + } + + /* dual */ + if (dual_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_LANG2) { + vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); + } + } + return 0; +} + +static int saa717x_log_status(struct v4l2_subdev *sd) +{ + struct saa717x_state *state = to_state(sd); + + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa717x_ctrl_ops = { + .s_ctrl = saa717x_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa717x_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa717x_g_register, + .s_register = saa717x_s_register, +#endif + .s_std = saa717x_s_std, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .log_status = saa717x_log_status, +}; + +static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { + .g_tuner = saa717x_g_tuner, + .s_tuner = saa717x_s_tuner, + .s_radio = saa717x_s_radio, +}; + +static const struct v4l2_subdev_video_ops saa717x_video_ops = { + .s_routing = saa717x_s_video_routing, + .s_mbus_fmt = saa717x_s_mbus_fmt, + .s_stream = saa717x_s_stream, +}; + +static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { + .s_routing = saa717x_s_audio_routing, +}; + +static const struct v4l2_subdev_ops saa717x_ops = { + .core = &saa717x_core_ops, + .tuner = &saa717x_tuner_ops, + .audio = &saa717x_audio_ops, + .video = &saa717x_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +/* i2c implementation */ + +/* ----------------------------------------------------------------------- */ +static int saa717x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct saa717x_state *decoder; + struct v4l2_ctrl_handler *hdl; + struct v4l2_subdev *sd; + u8 id = 0; + char *p = ""; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa717x_ops); + + if (saa717x_write(sd, 0x5a4, 0xfe) && + saa717x_write(sd, 0x5a5, 0x0f) && + saa717x_write(sd, 0x5a6, 0x00) && + saa717x_write(sd, 0x5a7, 0x01)) + id = saa717x_read(sd, 0x5a0); + if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { + v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); + kfree(decoder); + return -ENODEV; + } + if (id == 0xc2) + p = "saa7173"; + else if (id == 0x32) + p = "saa7174A"; + else if (id == 0x6c) + p = "saa7174HL"; + else + p = "saa7171"; + v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, + client->addr << 1, client->adapter->name); + + hdl = &decoder->hdl; + v4l2_ctrl_handler_init(hdl, 9); + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 68); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 64); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_BASS, -16, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(decoder); + return err; + } + + decoder->std = V4L2_STD_NTSC; + decoder->input = -1; + decoder->enable = 1; + + /* FIXME!! */ + decoder->playback = 0; /* initially capture mode used */ + decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */ + + decoder->audio_input = 2; /* FIXME!! */ + + decoder->tuner_audio_mode = TUNER_AUDIO_STEREO; + /* set volume, bass and treble */ + decoder->audio_main_vol_l = 6; + decoder->audio_main_vol_r = 6; + + v4l2_dbg(1, debug, sd, "writing init values\n"); + + /* FIXME!! */ + saa717x_write_regs(sd, reg_init_initialize); + + v4l2_ctrl_handler_setup(hdl); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2*HZ); + return 0; +} + +static int saa717x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa717x_id[] = { + { "saa717x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa717x_id); + +static struct i2c_driver saa717x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa717x", + }, + .probe = saa717x_probe, + .remove = saa717x_remove, + .id_table = saa717x_id, +}; + +module_i2c_driver(saa717x_driver); diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c new file mode 100644 index 000000000000..2c6b65c76e2b --- /dev/null +++ b/drivers/media/i2c/saa7185.c @@ -0,0 +1,377 @@ +/* + * saa7185 - Philips SAA7185B video encoder driver version 0.0.3 + * + * Copyright (C) 1998 Dave Perks + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * This program is free software; 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 + +MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); +MODULE_AUTHOR("Dave Perks"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ----------------------------------------------------------------------- */ + +struct saa7185 { + struct v4l2_subdev sd; + unsigned char reg[128]; + + v4l2_std_id norm; +}; + +static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7185, sd); +} + +/* ----------------------------------------------------------------------- */ + +static inline int saa7185_read(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte(client); +} + +static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7185 *encoder = to_saa7185(sd); + + v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value); + encoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int saa7185_write_block(struct v4l2_subdev *sd, + const u8 *data, unsigned int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7185 *encoder = to_saa7185(sd); + int ret = -1; + u8 reg; + + /* the adv7175 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + encoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && block_len < 32); + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = saa7185_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + } + + return ret; +} + +/* ----------------------------------------------------------------------- */ + +static const unsigned char init_common[] = { + 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, + * YUV2C=1, MY2C=1, MUV2C=1 */ + + 0x42, 0x6b, /* OVLY0=107 */ + 0x43, 0x00, /* OVLU0=0 white */ + 0x44, 0x00, /* OVLV0=0 */ + 0x45, 0x22, /* OVLY1=34 */ + 0x46, 0xac, /* OVLU1=172 yellow */ + 0x47, 0x0e, /* OVLV1=14 */ + 0x48, 0x03, /* OVLY2=3 */ + 0x49, 0x1d, /* OVLU2=29 cyan */ + 0x4a, 0xac, /* OVLV2=172 */ + 0x4b, 0xf0, /* OVLY3=240 */ + 0x4c, 0xc8, /* OVLU3=200 green */ + 0x4d, 0xb9, /* OVLV3=185 */ + 0x4e, 0xd4, /* OVLY4=212 */ + 0x4f, 0x38, /* OVLU4=56 magenta */ + 0x50, 0x47, /* OVLV4=71 */ + 0x51, 0xc1, /* OVLY5=193 */ + 0x52, 0xe3, /* OVLU5=227 red */ + 0x53, 0x54, /* OVLV5=84 */ + 0x54, 0xa3, /* OVLY6=163 */ + 0x55, 0x54, /* OVLU6=84 blue */ + 0x56, 0xf2, /* OVLV6=242 */ + 0x57, 0x90, /* OVLY7=144 */ + 0x58, 0x00, /* OVLU7=0 black */ + 0x59, 0x00, /* OVLV7=0 */ + + 0x5a, 0x00, /* CHPS=0 */ + 0x5b, 0x76, /* GAINU=118 */ + 0x5c, 0xa5, /* GAINV=165 */ + 0x5d, 0x3c, /* BLCKL=60 */ + 0x5e, 0x3a, /* BLNNL=58 */ + 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ + 0x60, 0x00, /* NULL */ + + /* 0x61 - 0x66 set according to norm */ + + 0x67, 0x00, /* 0 : caption 1st byte odd field */ + 0x68, 0x00, /* 0 : caption 2nd byte odd field */ + 0x69, 0x00, /* 0 : caption 1st byte even field */ + 0x6a, 0x00, /* 0 : caption 2nd byte even field */ + + 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ + 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, + * CBLF=0, ORCV2=0, PRCV2=0 */ + 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ + + 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at + * least for PAL */ + 0x6f, 0x00, /* HTRIG upper bits */ + 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ + + /* The following should not be needed */ + + 0x71, 0x15, /* BMRQ=0x115 */ + 0x72, 0x90, /* EMRQ=0x690 */ + 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ + 0x74, 0x00, /* NULL */ + 0x75, 0x00, /* NULL */ + 0x76, 0x00, /* NULL */ + 0x77, 0x15, /* BRCV=0x115 */ + 0x78, 0x90, /* ERCV=0x690 */ + 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ + + /* Field length controls */ + + 0x7a, 0x70, /* FLC=0 */ + + /* The following should not be needed if SBLN = 1 */ + + 0x7b, 0x16, /* FAL=22 */ + 0x7c, 0x35, /* LAL=244 */ + 0x7d, 0x20, /* LAL=244, FAL=22 */ +}; + +static const unsigned char init_pal[] = { + 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, + * YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ + 0x63, 0xcb, /* FSC0 */ + 0x64, 0x8a, /* FSC1 */ + 0x65, 0x09, /* FSC2 */ + 0x66, 0x2a, /* FSC3 */ +}; + +static const unsigned char init_ntsc[] = { + 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, + * YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ + 0x63, 0x1f, /* FSC0 */ + 0x64, 0x7c, /* FSC1 */ + 0x65, 0xf0, /* FSC2 */ + 0x66, 0x21, /* FSC3 */ +}; + + +static int saa7185_init(struct v4l2_subdev *sd, u32 val) +{ + struct saa7185 *encoder = to_saa7185(sd); + + saa7185_write_block(sd, init_common, sizeof(init_common)); + if (encoder->norm & V4L2_STD_NTSC) + saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + else + saa7185_write_block(sd, init_pal, sizeof(init_pal)); + return 0; +} + +static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7185 *encoder = to_saa7185(sd); + + if (std & V4L2_STD_NTSC) + saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + else if (std & V4L2_STD_PAL) + saa7185_write_block(sd, init_pal, sizeof(init_pal)); + else + return -EINVAL; + encoder->norm = std; + return 0; +} + +static int saa7185_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7185 *encoder = to_saa7185(sd); + + /* RJ: input = 0: input is from SA7111 + input = 1: input is from ZR36060 */ + + switch (input) { + case 0: + /* turn off colorbar */ + saa7185_write(sd, 0x3a, 0x0f); + /* Switch RTCE to 1 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + saa7185_write(sd, 0x6e, 0x01); + break; + + case 1: + /* turn off colorbar */ + saa7185_write(sd, 0x3a, 0x0f); + /* Switch RTCE to 0 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); + /* SW: a slight sync problem... */ + saa7185_write(sd, 0x6e, 0x00); + break; + + case 2: + /* turn on colorbar */ + saa7185_write(sd, 0x3a, 0x8f); + /* Switch RTCE to 0 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + /* SW: a slight sync problem... */ + saa7185_write(sd, 0x6e, 0x01); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7185_core_ops = { + .g_chip_ident = saa7185_g_chip_ident, + .init = saa7185_init, +}; + +static const struct v4l2_subdev_video_ops saa7185_video_ops = { + .s_std_output = saa7185_s_std_output, + .s_routing = saa7185_s_routing, +}; + +static const struct v4l2_subdev_ops saa7185_ops = { + .core = &saa7185_core_ops, + .video = &saa7185_video_ops, +}; + + +/* ----------------------------------------------------------------------- */ + +static int saa7185_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i; + struct saa7185 *encoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); + if (encoder == NULL) + return -ENOMEM; + encoder->norm = V4L2_STD_NTSC; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7185_ops); + + i = saa7185_write_block(sd, init_common, sizeof(init_common)); + if (i >= 0) + i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + if (i < 0) + v4l2_dbg(1, debug, sd, "init error %d\n", i); + else + v4l2_dbg(1, debug, sd, "revision 0x%x\n", + saa7185_read(sd) >> 5); + return 0; +} + +static int saa7185_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa7185 *encoder = to_saa7185(sd); + + v4l2_device_unregister_subdev(sd); + /* SW: output off is active */ + saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40); + kfree(encoder); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id saa7185_id[] = { + { "saa7185", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7185_id); + +static struct i2c_driver saa7185_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7185", + }, + .probe = saa7185_probe, + .remove = saa7185_remove, + .id_table = saa7185_id, +}; + +module_i2c_driver(saa7185_driver); diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c new file mode 100644 index 000000000000..d7d1670e0ca3 --- /dev/null +++ b/drivers/media/i2c/saa7191.c @@ -0,0 +1,659 @@ +/* + * saa7191.c - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "saa7191.h" + +#define SAA7191_MODULE_VERSION "0.0.5" + +MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); +MODULE_VERSION(SAA7191_MODULE_VERSION); +MODULE_AUTHOR("Mikael Nousiainen "); +MODULE_LICENSE("GPL"); + + +// #define SAA7191_DEBUG + +#ifdef SAA7191_DEBUG +#define dprintk(x...) printk("SAA7191: " x); +#else +#define dprintk(x...) +#endif + +#define SAA7191_SYNC_COUNT 30 +#define SAA7191_SYNC_DELAY 100 /* milliseconds */ + +struct saa7191 { + struct v4l2_subdev sd; + + /* the register values are stored here as the actual + * I2C-registers are write-only */ + u8 reg[25]; + + int input; + v4l2_std_id norm; +}; + +static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7191, sd); +} + +static const u8 initseq[] = { + 0, /* Subaddress */ + + 0x50, /* (0x50) SAA7191_REG_IDEL */ + + /* 50 Hz signal timing */ + 0x30, /* (0x30) SAA7191_REG_HSYB */ + 0x00, /* (0x00) SAA7191_REG_HSYS */ + 0xe8, /* (0xe8) SAA7191_REG_HCLB */ + 0xb6, /* (0xb6) SAA7191_REG_HCLS */ + 0xf4, /* (0xf4) SAA7191_REG_HPHI */ + + /* control */ + SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */ + 0x00, /* (0x00) SAA7191_REG_HUEC */ + 0xf8, /* (0xf8) SAA7191_REG_CKTQ */ + 0xf8, /* (0xf8) SAA7191_REG_CKTS */ + 0x90, /* (0x90) SAA7191_REG_PLSE */ + 0x90, /* (0x90) SAA7191_REG_SESE */ + 0x00, /* (0x00) SAA7191_REG_GAIN */ + SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC + * - not SECAM, + * slow time constant */ + SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS + | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK + * - chroma from CVBS, GPSW1 & 2 off */ + SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS + | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3 + * - automatic field detection */ + 0x00, /* (0x00) SAA7191_REG_CTL4 */ + 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */ + 0x00, /* unused */ + 0x00, /* unused */ + + /* 60 Hz signal timing */ + 0x34, /* (0x34) SAA7191_REG_HS6B */ + 0x0a, /* (0x0a) SAA7191_REG_HS6S */ + 0xf4, /* (0xf4) SAA7191_REG_HC6B */ + 0xce, /* (0xce) SAA7191_REG_HC6S */ + 0xf4, /* (0xf4) SAA7191_REG_HP6I */ +}; + +/* SAA7191 register handling */ + +static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) +{ + return to_saa7191(sd)->reg[reg]; +} + +static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ret = i2c_master_recv(client, value, 1); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n"); + return ret; + } + + return 0; +} + + +static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + to_saa7191(sd)->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* the first byte of data must be the first subaddress number (register) */ +static int saa7191_write_block(struct v4l2_subdev *sd, + u8 length, const u8 *data) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7191 *decoder = to_saa7191(sd); + int i; + int ret; + + for (i = 0; i < (length - 1); i++) { + decoder->reg[data[0] + i] = data[i + 1]; + } + + ret = i2c_master_send(client, data, length); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_write_block(): " + "write failed\n"); + return ret; + } + + return 0; +} + +/* Helper functions */ + +static int saa7191_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct saa7191 *decoder = to_saa7191(sd); + u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); + u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); + int err; + + switch (input) { + case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ + iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 + | SAA7191_IOCK_GPSW2); + /* Chrominance trap active */ + luma &= ~SAA7191_LUMA_BYPS; + break; + case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ + iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; + /* Chrominance trap bypassed */ + luma |= SAA7191_LUMA_BYPS; + break; + default: + return -EINVAL; + } + + err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); + if (err) + return -EIO; + err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); + if (err) + return -EIO; + + decoder->input = input; + + return 0; +} + +static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); + u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); + int err; + + if (norm & V4L2_STD_PAL) { + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + } else if (norm & V4L2_STD_NTSC) { + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~SAA7191_CTL3_AUFD; + ctl3 |= SAA7191_CTL3_FSEL; + chcv = SAA7191_CHCV_NTSC; + } else if (norm & V4L2_STD_SECAM) { + stdc |= SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + } else { + return -EINVAL; + } + + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); + if (err) + return -EIO; + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); + if (err) + return -EIO; + err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); + if (err) + return -EIO; + + decoder->norm = norm; + + dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, + stdc, chcv); + dprintk("norm: %llx\n", norm); + + return 0; +} + +static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) +{ + int i = 0; + + dprintk("Checking for signal...\n"); + + for (i = 0; i < SAA7191_SYNC_COUNT; i++) { + if (saa7191_read_status(sd, status)) + return -EIO; + + if (((*status) & SAA7191_STATUS_HLCK) == 0) { + dprintk("Signal found\n"); + return 0; + } + + msleep(SAA7191_SYNC_DELAY); + } + + dprintk("No signal\n"); + + return -EBUSY; +} + +static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); + u8 status; + v4l2_std_id old_norm = decoder->norm; + int err = 0; + + dprintk("SAA7191 extended signal auto-detection...\n"); + + *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_FSEL); + + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); + if (err) { + err = -EIO; + goto out; + } + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); + if (err) { + err = -EIO; + goto out; + } + + ctl3 |= SAA7191_CTL3_AUFD; + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); + if (err) { + err = -EIO; + goto out; + } + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(sd, &status); + if (err) + goto out; + + if (status & SAA7191_STATUS_FIDT) { + /* 60Hz signal -> NTSC */ + dprintk("60Hz signal: NTSC\n"); + *norm = V4L2_STD_NTSC; + return 0; + } + + /* 50Hz signal */ + dprintk("50Hz signal: Trying PAL...\n"); + + /* try PAL first */ + err = saa7191_s_std(sd, V4L2_STD_PAL); + if (err) + goto out; + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(sd, &status); + if (err) + goto out; + + /* not 50Hz ? */ + if (status & SAA7191_STATUS_FIDT) { + dprintk("No 50Hz signal\n"); + saa7191_s_std(sd, old_norm); + return -EAGAIN; + } + + if (status & SAA7191_STATUS_CODE) { + dprintk("PAL\n"); + *norm = V4L2_STD_PAL; + return saa7191_s_std(sd, old_norm); + } + + dprintk("No color detected with PAL - Trying SECAM...\n"); + + /* no color detected ? -> try SECAM */ + err = saa7191_s_std(sd, V4L2_STD_SECAM); + if (err) + goto out; + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(sd, &status); + if (err) + goto out; + + /* not 50Hz ? */ + if (status & SAA7191_STATUS_FIDT) { + dprintk("No 50Hz signal\n"); + err = -EAGAIN; + goto out; + } + + if (status & SAA7191_STATUS_CODE) { + /* Color detected -> SECAM */ + dprintk("SECAM\n"); + *norm = V4L2_STD_SECAM; + return saa7191_s_std(sd, old_norm); + } + + dprintk("No color detected with SECAM - Going back to PAL.\n"); + +out: + return saa7191_s_std(sd, old_norm); +} + +static int saa7191_autodetect_norm(struct v4l2_subdev *sd) +{ + u8 status; + + dprintk("SAA7191 signal auto-detection...\n"); + + dprintk("Reading status...\n"); + + if (saa7191_read_status(sd, &status)) + return -EIO; + + dprintk("Checking for signal...\n"); + + /* no signal ? */ + if (status & SAA7191_STATUS_HLCK) { + dprintk("No signal\n"); + return -EBUSY; + } + + dprintk("Signal found\n"); + + if (status & SAA7191_STATUS_FIDT) { + /* 60hz signal -> NTSC */ + dprintk("NTSC\n"); + return saa7191_s_std(sd, V4L2_STD_NTSC); + } else { + /* 50hz signal -> PAL */ + dprintk("PAL\n"); + return saa7191_s_std(sd, V4L2_STD_PAL); + } +} + +static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + u8 reg; + int ret = 0; + + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + case SAA7191_CONTROL_BANDPASS_WEIGHT: + case SAA7191_CONTROL_CORING: + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) + >> SAA7191_LUMA_BPSS_SHIFT; + break; + case SAA7191_CONTROL_BANDPASS_WEIGHT: + ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK) + >> SAA7191_LUMA_APER_SHIFT; + break; + case SAA7191_CONTROL_CORING: + ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK) + >> SAA7191_LUMA_CORI_SHIFT; + break; + } + break; + case SAA7191_CONTROL_FORCE_COLOUR: + case SAA7191_CONTROL_CHROMA_GAIN: + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) + ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; + else + ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) + >> SAA7191_GAIN_LFIS_SHIFT; + break; + case V4L2_CID_HUE: + reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); + if (reg < 0x80) + reg += 0x80; + else + reg -= 0x80; + ctrl->value = (s32)reg; + break; + case SAA7191_CONTROL_VTRC: + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); + ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; + break; + case SAA7191_CONTROL_LUMA_DELAY: + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); + ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) + >> SAA7191_CTL3_YDEL_SHIFT; + if (ctrl->value >= 4) + ctrl->value -= 8; + break; + case SAA7191_CONTROL_VNR: + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); + ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) + >> SAA7191_CTL4_VNOI_SHIFT; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + u8 reg; + int ret = 0; + + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + case SAA7191_CONTROL_BANDPASS_WEIGHT: + case SAA7191_CONTROL_CORING: + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { + case SAA7191_CONTROL_BANDPASS: + reg &= ~SAA7191_LUMA_BPSS_MASK; + reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) + & SAA7191_LUMA_BPSS_MASK; + break; + case SAA7191_CONTROL_BANDPASS_WEIGHT: + reg &= ~SAA7191_LUMA_APER_MASK; + reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT) + & SAA7191_LUMA_APER_MASK; + break; + case SAA7191_CONTROL_CORING: + reg &= ~SAA7191_LUMA_CORI_MASK; + reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT) + & SAA7191_LUMA_CORI_MASK; + break; + } + ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); + break; + case SAA7191_CONTROL_FORCE_COLOUR: + case SAA7191_CONTROL_CHROMA_GAIN: + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { + if (ctrl->value) + reg |= SAA7191_GAIN_COLO; + else + reg &= ~SAA7191_GAIN_COLO; + } else { + reg &= ~SAA7191_GAIN_LFIS_MASK; + reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) + & SAA7191_GAIN_LFIS_MASK; + } + ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); + break; + case V4L2_CID_HUE: + reg = ctrl->value & 0xff; + if (reg < 0x80) + reg += 0x80; + else + reg -= 0x80; + ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); + break; + case SAA7191_CONTROL_VTRC: + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); + if (ctrl->value) + reg |= SAA7191_STDC_VTRC; + else + reg &= ~SAA7191_STDC_VTRC; + ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); + break; + case SAA7191_CONTROL_LUMA_DELAY: { + s32 value = ctrl->value; + if (value < 0) + value += 8; + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); + reg &= ~SAA7191_CTL3_YDEL_MASK; + reg |= (value << SAA7191_CTL3_YDEL_SHIFT) + & SAA7191_CTL3_YDEL_MASK; + ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); + break; + } + case SAA7191_CONTROL_VNR: + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); + reg &= ~SAA7191_CTL4_VNOI_MASK; + reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) + & SAA7191_CTL4_VNOI_MASK; + ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* I2C-interface */ + +static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + u8 status_reg; + int res = V4L2_IN_ST_NO_SIGNAL; + + if (saa7191_read_status(sd, &status_reg)) + return -EIO; + if ((status_reg & SAA7191_STATUS_HLCK) == 0) + res = 0; + if (!(status_reg & SAA7191_STATUS_CODE)) + res |= V4L2_IN_ST_NO_COLOR; + *status = res; + return 0; +} + + +static int saa7191_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7191_core_ops = { + .g_chip_ident = saa7191_g_chip_ident, + .g_ctrl = saa7191_g_ctrl, + .s_ctrl = saa7191_s_ctrl, + .s_std = saa7191_s_std, +}; + +static const struct v4l2_subdev_video_ops saa7191_video_ops = { + .s_routing = saa7191_s_routing, + .querystd = saa7191_querystd, + .g_input_status = saa7191_g_input_status, +}; + +static const struct v4l2_subdev_ops saa7191_ops = { + .core = &saa7191_core_ops, + .video = &saa7191_video_ops, +}; + +static int saa7191_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct saa7191 *decoder; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7191_ops); + + err = saa7191_write_block(sd, sizeof(initseq), initseq); + if (err) { + printk(KERN_ERR "SAA7191 initialization failed\n"); + kfree(decoder); + return err; + } + + printk(KERN_INFO "SAA7191 initialized\n"); + + decoder->input = SAA7191_INPUT_COMPOSITE; + decoder->norm = V4L2_STD_PAL; + + err = saa7191_autodetect_norm(sd); + if (err && (err != -EBUSY)) + printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); + + return 0; +} + +static int saa7191_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_saa7191(sd)); + return 0; +} + +static const struct i2c_device_id saa7191_id[] = { + { "saa7191", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7191_id); + +static struct i2c_driver saa7191_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa7191", + }, + .probe = saa7191_probe, + .remove = saa7191_remove, + .id_table = saa7191_id, +}; + +module_i2c_driver(saa7191_driver); diff --git a/drivers/media/i2c/saa7191.h b/drivers/media/i2c/saa7191.h new file mode 100644 index 000000000000..803c74d6066f --- /dev/null +++ b/drivers/media/i2c/saa7191.h @@ -0,0 +1,245 @@ +/* + * saa7191.h - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * 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. + */ + +#ifndef _SAA7191_H_ +#define _SAA7191_H_ + +/* Philips SAA7191 DMSD I2C bus address */ +#define SAA7191_ADDR 0x8a + +/* Register subaddresses. */ +#define SAA7191_REG_IDEL 0x00 +#define SAA7191_REG_HSYB 0x01 +#define SAA7191_REG_HSYS 0x02 +#define SAA7191_REG_HCLB 0x03 +#define SAA7191_REG_HCLS 0x04 +#define SAA7191_REG_HPHI 0x05 +#define SAA7191_REG_LUMA 0x06 +#define SAA7191_REG_HUEC 0x07 +#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */ +#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */ +#define SAA7191_REG_PLSE 0x0a +#define SAA7191_REG_SESE 0x0b +#define SAA7191_REG_GAIN 0x0c +#define SAA7191_REG_STDC 0x0d +#define SAA7191_REG_IOCK 0x0e +#define SAA7191_REG_CTL3 0x0f +#define SAA7191_REG_CTL4 0x10 +#define SAA7191_REG_CHCV 0x11 +#define SAA7191_REG_HS6B 0x14 +#define SAA7191_REG_HS6S 0x15 +#define SAA7191_REG_HC6B 0x16 +#define SAA7191_REG_HC6S 0x17 +#define SAA7191_REG_HP6I 0x18 +#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ + +/* Status Register definitions */ +#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ +#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */ +#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */ +#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ + +/* Luminance Control Register definitions */ +/* input mode select bit: + * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */ +#define SAA7191_LUMA_BYPS 0x80 +/* pre-filter (only when chrominance trap is active) */ +#define SAA7191_LUMA_PREF 0x40 +/* aperture bandpass to select different characteristics with maximums + * (bits 4-5) */ +#define SAA7191_LUMA_BPSS_MASK 0x30 +#define SAA7191_LUMA_BPSS_SHIFT 4 +#define SAA7191_LUMA_BPSS_3 0x30 +#define SAA7191_LUMA_BPSS_2 0x20 +#define SAA7191_LUMA_BPSS_1 0x10 +#define SAA7191_LUMA_BPSS_0 0x00 +/* coring range for high frequency components according to 8-bit luminance + * (bits 2-3) + * 0=coring off, n= (+-)n LSB */ +#define SAA7191_LUMA_CORI_MASK 0x0c +#define SAA7191_LUMA_CORI_SHIFT 2 +#define SAA7191_LUMA_CORI_3 0x0c +#define SAA7191_LUMA_CORI_2 0x08 +#define SAA7191_LUMA_CORI_1 0x04 +#define SAA7191_LUMA_CORI_0 0x00 +/* aperture bandpass filter weights high frequency components of luminance + * signal (bits 0-1) + * 0=factor 0, 1=0.25, 2=0.5, 3=1 */ +#define SAA7191_LUMA_APER_MASK 0x03 +#define SAA7191_LUMA_APER_SHIFT 0 +#define SAA7191_LUMA_APER_3 0x03 +#define SAA7191_LUMA_APER_2 0x02 +#define SAA7191_LUMA_APER_1 0x01 +#define SAA7191_LUMA_APER_0 0x00 + +/* Chrominance Gain Control Settings Register definitions */ +/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */ +#define SAA7191_GAIN_COLO 0x80 +/* chrominance gain control (AGC filter) + * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */ +#define SAA7191_GAIN_LFIS_MASK 0x60 +#define SAA7191_GAIN_LFIS_SHIFT 5 +#define SAA7191_GAIN_LFIS_3 0x60 +#define SAA7191_GAIN_LFIS_2 0x40 +#define SAA7191_GAIN_LFIS_1 0x20 +#define SAA7191_GAIN_LFIS_0 0x00 + +/* Standard/Mode Control Register definitions */ +/* tv/vtr mode bit: 0=TV mode (slow time constant), + * 1=VTR mode (fast time constant) */ +#define SAA7191_STDC_VTRC 0x80 +/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs) + * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */ +#define SAA7191_STDC_NFEN 0x08 +/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */ +#define SAA7191_STDC_HRMV 0x04 +/* general purpose switch 0 + * (not used with VINO afaik) */ +#define SAA7191_STDC_GPSW0 0x02 +/* SECAM mode bit: 0=other standards, 1=SECAM */ +#define SAA7191_STDC_SECS 0x01 + +/* I/O and Clock Control Register definitions */ +/* horizontal clock PLL: 0=PLL closed, + * 1=PLL circuit open and horizontal freq fixed */ +#define SAA7191_IOCK_HPLL 0x80 +/* colour-difference output enable (outputs UV0-UV7) */ +#define SAA7191_IOCK_OEDC 0x40 +/* H-sync output enable */ +#define SAA7191_IOCK_OEHS 0x20 +/* V-sync output enable */ +#define SAA7191_IOCK_OEVS 0x10 +/* luminance output enable (outputs Y0-Y7) */ +#define SAA7191_IOCK_OEDY 0x08 +/* S-VHS bit (chrominance from CVBS or from chrominance input): + * 0=controlled by BYPS-bit, 1=from chrominance input */ +#define SAA7191_IOCK_CHRS 0x04 +/* general purpose switch 2 + * VINO-specific: 0=used with CVBS, 1=used with S-Video */ +#define SAA7191_IOCK_GPSW2 0x02 +/* general purpose switch 1 */ +/* VINO-specific: 0=always, 1=not used!*/ +#define SAA7191_IOCK_GPSW1 0x01 + +/* Miscellaneous Control #1 Register definitions */ +/* automatic field detection (50/60Hz standard) */ +#define SAA7191_CTL3_AUFD 0x80 +/* field select: (if AUFD=0) + * 0=50Hz (625 lines), 1=60Hz (525 lines) */ +#define SAA7191_CTL3_FSEL 0x40 +/* SECAM cross-colour reduction enable */ +#define SAA7191_CTL3_SXCR 0x20 +/* sync and clamping pulse enable (HCL and HSY outputs) */ +#define SAA7191_CTL3_SCEN 0x10 +/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */ +#define SAA7191_CTL3_OFTS 0x08 +/* luminance delay compensation + * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC, + * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC + * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */ +#define SAA7191_CTL3_YDEL_MASK 0x07 +#define SAA7191_CTL3_YDEL_SHIFT 0 +#define SAA7191_CTL3_YDEL2 0x04 +#define SAA7191_CTL3_YDEL1 0x02 +#define SAA7191_CTL3_YDEL0 0x01 + +/* Miscellaneous Control #2 Register definitions */ +/* select HREF position + * 0=normal, HREF is matched to YUV output port, + * 1=HREF is matched to CVBS input port */ +#define SAA7191_CTL4_HRFS 0x04 +/* vertical noise reduction + * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */ +#define SAA7191_CTL4_VNOI_MASK 0x03 +#define SAA7191_CTL4_VNOI_SHIFT 0 +#define SAA7191_CTL4_VNOI_3 0x03 +#define SAA7191_CTL4_VNOI_2 0x02 +#define SAA7191_CTL4_VNOI_1 0x01 +#define SAA7191_CTL4_VNOI_0 0x00 + +/* Chrominance Gain Control Register definitions + * - for QAM-modulated input signals, effects output amplitude + * (SECAM gain fixed) + * (nominal values for UV CCIR level) */ +#define SAA7191_CHCV_NTSC 0x2c +#define SAA7191_CHCV_PAL 0x59 + +/* Driver interface definitions */ +#define SAA7191_INPUT_COMPOSITE 0 +#define SAA7191_INPUT_SVIDEO 1 + +#define SAA7191_NORM_PAL 1 +#define SAA7191_NORM_NTSC 2 +#define SAA7191_NORM_SECAM 3 + +struct saa7191_status { + /* 0=no signal, 1=signal detected */ + int signal; + /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */ + int signal_60hz; + /* 0=no color detected, 1=color detected */ + int color; + + /* current SAA7191_INPUT_ */ + int input; + /* current SAA7191_NORM_ */ + int norm; +}; + +#define SAA7191_BANDPASS_MIN 0x00 +#define SAA7191_BANDPASS_MAX 0x03 +#define SAA7191_BANDPASS_DEFAULT 0x00 + +#define SAA7191_BANDPASS_WEIGHT_MIN 0x00 +#define SAA7191_BANDPASS_WEIGHT_MAX 0x03 +#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01 + +#define SAA7191_CORING_MIN 0x00 +#define SAA7191_CORING_MAX 0x03 +#define SAA7191_CORING_DEFAULT 0x00 + +#define SAA7191_HUE_MIN 0x00 +#define SAA7191_HUE_MAX 0xff +#define SAA7191_HUE_DEFAULT 0x80 + +#define SAA7191_VTRC_MIN 0x00 +#define SAA7191_VTRC_MAX 0x01 +#define SAA7191_VTRC_DEFAULT 0x00 + +#define SAA7191_FORCE_COLOUR_MIN 0x00 +#define SAA7191_FORCE_COLOUR_MAX 0x01 +#define SAA7191_FORCE_COLOUR_DEFAULT 0x00 + +#define SAA7191_CHROMA_GAIN_MIN 0x00 +#define SAA7191_CHROMA_GAIN_MAX 0x03 +#define SAA7191_CHROMA_GAIN_DEFAULT 0x00 + +#define SAA7191_LUMA_DELAY_MIN -0x04 +#define SAA7191_LUMA_DELAY_MAX 0x03 +#define SAA7191_LUMA_DELAY_DEFAULT 0x01 + +#define SAA7191_VNR_MIN 0x00 +#define SAA7191_VNR_MAX 0x03 +#define SAA7191_VNR_DEFAULT 0x00 + +#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) +#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) +#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) +#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) +#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) +#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) +#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) +#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) + +#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) +#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) + +#endif diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c new file mode 100644 index 000000000000..a577614bd84f --- /dev/null +++ b/drivers/media/i2c/smiapp-pll.c @@ -0,0 +1,418 @@ +/* + * drivers/media/i2c/smiapp-pll.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include + +#include "smiapp-pll.h" + +/* Return an even number or one. */ +static inline uint32_t clk_div_even(uint32_t a) +{ + return max_t(uint32_t, 1, a & ~1); +} + +/* Return an even number or one. */ +static inline uint32_t clk_div_even_up(uint32_t a) +{ + if (a == 1) + return 1; + return (a + 1) & ~1; +} + +static inline uint32_t is_one_or_even(uint32_t a) +{ + if (a == 1) + return 1; + if (a & 1) + return 0; + + return 1; +} + +static int bounds_check(struct device *dev, uint32_t val, + uint32_t min, uint32_t max, char *str) +{ + if (val >= min && val <= max) + return 0; + + dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); + + return -EINVAL; +} + +static void print_pll(struct device *dev, struct smiapp_pll *pll) +{ + dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); + if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); + dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); + } + dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); + dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); + + dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); + dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); + dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", + pll->op_sys_clk_freq_hz); + dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", + pll->op_pix_clk_freq_hz); + } + dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); + dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); +} + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll) +{ + uint32_t sys_div; + uint32_t best_pix_div = INT_MAX >> 1; + uint32_t vt_op_binning_div; + uint32_t lane_op_clock_ratio; + uint32_t mul, div; + uint32_t more_mul_min, more_mul_max; + uint32_t more_mul_factor; + uint32_t min_vt_div, max_vt_div, vt_div; + uint32_t min_sys_div, max_sys_div; + unsigned int i; + int rval; + + if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) + lane_op_clock_ratio = pll->lanes; + else + lane_op_clock_ratio = 1; + dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); + + dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, + pll->binning_vertical); + + /* CSI transfers 2 bits per clock per lane; thus times 2 */ + pll->pll_op_clk_freq_hz = pll->link_freq * 2 + * (pll->lanes / lane_op_clock_ratio); + + /* Figure out limits for pre-pll divider based on extclk */ + dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + limits->max_pre_pll_clk_div = + min_t(uint16_t, limits->max_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->min_pll_ip_freq_hz)); + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(pll->ext_clk_freq_hz, + limits->max_pll_ip_freq_hz))); + dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); + mul = div_u64(pll->pll_op_clk_freq_hz, i); + div = pll->ext_clk_freq_hz / i; + dev_dbg(dev, "mul %d / div %d\n", mul, div); + + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, + limits->max_pll_op_freq_hz))); + dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { + dev_err(dev, "unable to compute pre_pll divisor\n"); + return -EINVAL; + } + + pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; + + /* + * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be + * too high. + */ + dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); + + /* Don't go above max pll multiplier. */ + more_mul_max = limits->max_pll_multiplier / mul; + dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", + more_mul_max); + /* Don't go above max pll op frequency. */ + more_mul_max = + min_t(int, + more_mul_max, + limits->max_pll_op_freq_hz + / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); + dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", + more_mul_max); + /* Don't go above the division capability of op sys clock divider. */ + more_mul_max = min(more_mul_max, + limits->max_op_sys_clk_div * pll->pre_pll_clk_div + / div); + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", + more_mul_max); + /* Ensure we won't go above min_pll_multiplier. */ + more_mul_max = min(more_mul_max, + DIV_ROUND_UP(limits->max_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", + more_mul_max); + + /* Ensure we won't go below min_pll_op_freq_hz. */ + more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, + pll->ext_clk_freq_hz / pll->pre_pll_clk_div + * mul); + dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", + more_mul_min); + /* Ensure we won't go below min_pll_multiplier. */ + more_mul_min = max(more_mul_min, + DIV_ROUND_UP(limits->min_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", + more_mul_min); + + if (more_mul_min > more_mul_max) { + dev_warn(dev, + "unable to compute more_mul_min and more_mul_max"); + return -EINVAL; + } + + more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; + dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); + dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", + more_mul_factor); + i = roundup(more_mul_min, more_mul_factor); + if (!is_one_or_even(i)) + i <<= 1; + + dev_dbg(dev, "final more_mul: %d\n", i); + if (i > more_mul_max) { + dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); + return -EINVAL; + } + + pll->pll_multiplier = mul * i; + pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); + + pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz + / pll->pre_pll_clk_div; + + pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz + * pll->pll_multiplier; + + /* Derive pll_op_clk_freq_hz. */ + pll->op_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; + + pll->op_pix_clk_div = pll->bits_per_pixel; + dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); + + pll->op_pix_clk_freq_hz = + pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; + + /* + * Some sensors perform analogue binning and some do this + * digitally. The ones doing this digitally can be roughly be + * found out using this formula. The ones doing this digitally + * should run at higher clock rate, so smaller divisor is used + * on video timing side. + */ + if (limits->min_line_length_pck_bin > limits->min_line_length_pck + / pll->binning_horizontal) + vt_op_binning_div = pll->binning_horizontal; + else + vt_op_binning_div = 1; + dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); + + /* + * Profile 2 supports vt_pix_clk_div E [4, 10] + * + * Horizontal binning can be used as a base for difference in + * divisors. One must make sure that horizontal blanking is + * enough to accommodate the CSI-2 sync codes. + * + * Take scaling factor into account as well. + * + * Find absolute limits for the factor of vt divider. + */ + dev_dbg(dev, "scale_m: %d\n", pll->scale_m); + min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div + * pll->scale_n, + lane_op_clock_ratio * vt_op_binning_div + * pll->scale_m); + + /* Find smallest and biggest allowed vt divisor. */ + dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); + min_vt_div = max(min_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz)); + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", + min_vt_div); + min_vt_div = max_t(uint32_t, min_vt_div, + limits->min_vt_pix_clk_div + * limits->min_vt_sys_clk_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); + + max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; + dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); + max_vt_div = min(max_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", + max_vt_div); + + /* + * Find limitsits for sys_clk_div. Not all values are possible + * with all values of pix_clk_div. + */ + min_sys_div = limits->min_vt_sys_clk_div; + dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + DIV_ROUND_UP(min_vt_div, + limits->max_vt_pix_clk_div)); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + pll->pll_op_clk_freq_hz + / limits->max_vt_sys_clk_freq_hz); + dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); + min_sys_div = clk_div_even_up(min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); + + max_sys_div = limits->max_vt_sys_clk_div; + dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(max_vt_div, + limits->min_vt_pix_clk_div)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); + + /* + * Find pix_div such that a legal pix_div * sys_div results + * into a value which is not smaller than div, the desired + * divisor. + */ + for (vt_div = min_vt_div; vt_div <= max_vt_div; + vt_div += 2 - (vt_div & 1)) { + for (sys_div = min_sys_div; + sys_div <= max_sys_div; + sys_div += 2 - (sys_div & 1)) { + int pix_div = DIV_ROUND_UP(vt_div, sys_div); + + if (pix_div < limits->min_vt_pix_clk_div + || pix_div > limits->max_vt_pix_clk_div) { + dev_dbg(dev, + "pix_div %d too small or too big (%d--%d)\n", + pix_div, + limits->min_vt_pix_clk_div, + limits->max_vt_pix_clk_div); + continue; + } + + /* Check if this one is better. */ + if (pix_div * sys_div + <= roundup(min_vt_div, best_pix_div)) + best_pix_div = pix_div; + } + if (best_pix_div < INT_MAX >> 1) + break; + } + + pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); + pll->vt_pix_clk_div = best_pix_div; + + pll->vt_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; + pll->vt_pix_clk_freq_hz = + pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; + + pll->pixel_rate_csi = + pll->op_pix_clk_freq_hz * lane_op_clock_ratio; + + print_pll(dev, pll); + + rval = bounds_check(dev, pll->pre_pll_clk_div, + limits->min_pre_pll_clk_div, + limits->max_pre_pll_clk_div, "pre_pll_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->pll_ip_clk_freq_hz, + limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, + "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->pll_multiplier, + limits->min_pll_multiplier, limits->max_pll_multiplier, + "pll_multiplier"); + if (!rval) + rval = bounds_check( + dev, pll->pll_op_clk_freq_hz, + limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, + "pll_op_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_div, + limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, + "op_sys_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_div, + limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, + "op_pix_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_freq_hz, + limits->min_op_sys_clk_freq_hz, + limits->max_op_sys_clk_freq_hz, + "op_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_freq_hz, + limits->min_op_pix_clk_freq_hz, + limits->max_op_pix_clk_freq_hz, + "op_pix_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_sys_clk_freq_hz, + limits->min_vt_sys_clk_freq_hz, + limits->max_vt_sys_clk_freq_hz, + "vt_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_pix_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz, + "vt_pix_clk_freq_hz"); + + return rval; +} +EXPORT_SYMBOL_GPL(smiapp_pll_calculate); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h new file mode 100644 index 000000000000..cb2d2db5d02d --- /dev/null +++ b/drivers/media/i2c/smiapp-pll.h @@ -0,0 +1,103 @@ +/* + * drivers/media/i2c/smiapp-pll.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_PLL_H +#define SMIAPP_PLL_H + +#include + +struct smiapp_pll { + uint8_t lanes; + uint8_t binning_horizontal; + uint8_t binning_vertical; + uint8_t scale_m; + uint8_t scale_n; + uint8_t bits_per_pixel; + uint16_t flags; + uint32_t link_freq; + + uint16_t pre_pll_clk_div; + uint16_t pll_multiplier; + uint16_t op_sys_clk_div; + uint16_t op_pix_clk_div; + uint16_t vt_sys_clk_div; + uint16_t vt_pix_clk_div; + + uint32_t ext_clk_freq_hz; + uint32_t pll_ip_clk_freq_hz; + uint32_t pll_op_clk_freq_hz; + uint32_t op_sys_clk_freq_hz; + uint32_t op_pix_clk_freq_hz; + uint32_t vt_sys_clk_freq_hz; + uint32_t vt_pix_clk_freq_hz; + + uint32_t pixel_rate_csi; +}; + +struct smiapp_pll_limits { + /* Strict PLL limits */ + uint32_t min_ext_clk_freq_hz; + uint32_t max_ext_clk_freq_hz; + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t min_pll_ip_freq_hz; + uint32_t max_pll_ip_freq_hz; + uint16_t min_pll_multiplier; + uint16_t max_pll_multiplier; + uint32_t min_pll_op_freq_hz; + uint32_t max_pll_op_freq_hz; + + uint16_t min_vt_sys_clk_div; + uint16_t max_vt_sys_clk_div; + uint32_t min_vt_sys_clk_freq_hz; + uint32_t max_vt_sys_clk_freq_hz; + uint16_t min_vt_pix_clk_div; + uint16_t max_vt_pix_clk_div; + uint32_t min_vt_pix_clk_freq_hz; + uint32_t max_vt_pix_clk_freq_hz; + + uint16_t min_op_sys_clk_div; + uint16_t max_op_sys_clk_div; + uint32_t min_op_sys_clk_freq_hz; + uint32_t max_op_sys_clk_freq_hz; + uint16_t min_op_pix_clk_div; + uint16_t max_op_pix_clk_div; + uint32_t min_op_pix_clk_freq_hz; + uint32_t max_op_pix_clk_freq_hz; + + /* Other relevant limits */ + uint32_t min_line_length_pck_bin; + uint32_t min_line_length_pck; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) + +struct device; + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll); + +#endif /* SMIAPP_PLL_H */ diff --git a/drivers/media/i2c/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig new file mode 100644 index 000000000000..3149cda1d0db --- /dev/null +++ b/drivers/media/i2c/smiapp/Kconfig @@ -0,0 +1,7 @@ +config VIDEO_SMIAPP + tristate "SMIA++/SMIA sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK + depends on MEDIA_CAMERA_SUPPORT + select VIDEO_SMIAPP_PLL + ---help--- + This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/i2c/smiapp/Makefile b/drivers/media/i2c/smiapp/Makefile new file mode 100644 index 000000000000..f45a003cbe7e --- /dev/null +++ b/drivers/media/i2c/smiapp/Makefile @@ -0,0 +1,5 @@ +smiapp-objs += smiapp-core.o smiapp-regs.o \ + smiapp-quirk.o smiapp-limits.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o + +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c new file mode 100644 index 000000000000..1cf914d11345 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -0,0 +1,2895 @@ +/* + * drivers/media/i2c/smiapp/smiapp-core.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * Based on smiapp driver by Vimarsh Zutshi + * Based on jt8ev1.c by Vimarsh Zutshi + * Based on smia-sensor.c by Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smiapp.h" + +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? ALIGN((dim), 2) \ + : (dim) & ~1) + +/* + * smiapp_module_idents - supported camera modules + */ +static const struct smiapp_module_ident smiapp_module_idents[] = { + SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), + SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), + SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), + SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), + SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), + SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), + SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), + SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), + SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), +}; + +/* + * + * Dynamic Capability Identification + * + */ + +static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; + unsigned int i; + int rval; + int line_count = 0; + int embedded_start = -1, embedded_end = -1; + int image_start = 0; + + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, + &fmt_model_type); + if (rval) + return rval; + + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, + &fmt_model_subtype); + if (rval) + return rval; + + ncol_desc = (fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) + >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; + nrow_desc = fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; + + dev_dbg(&client->dev, "format_model_type %s\n", + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE + ? "2 byte" : + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE + ? "4 byte" : "is simply bad"); + + for (i = 0; i < ncol_desc + nrow_desc; i++) { + u32 desc; + u32 pixelcode; + u32 pixels; + char *which; + char *what; + + if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; + } else if (fmt_model_type + == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; + } else { + dev_dbg(&client->dev, + "invalid frame format model type %d\n", + fmt_model_type); + return -EINVAL; + } + + if (i < ncol_desc) + which = "columns"; + else + which = "rows"; + + switch (pixelcode) { + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: + what = "embedded"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: + what = "dummy"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: + what = "black"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: + what = "dark"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: + what = "visible"; + break; + default: + what = "invalid"; + dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); + break; + } + + dev_dbg(&client->dev, "%s pixels: %d %s\n", + what, pixels, which); + + if (i < ncol_desc) + continue; + + /* Handle row descriptors */ + if (pixelcode + == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { + embedded_start = line_count; + } else { + if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE + || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) + image_start = line_count; + if (embedded_start != -1 && embedded_end == -1) + embedded_end = line_count; + } + line_count += pixels; + } + + if (embedded_start == -1 || embedded_end == -1) { + embedded_start = 0; + embedded_end = 0; + } + + dev_dbg(&client->dev, "embedded data from lines %d to %d\n", + embedded_start, embedded_end); + dev_dbg(&client->dev, "image data starts at line %d\n", image_start); + + return 0; +} + +static int smiapp_pll_configure(struct smiapp_sensor *sensor) +{ + struct smiapp_pll *pll = &sensor->pll; + int rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); + if (rval < 0) + return rval; + + /* Lane op clock ratio does not apply here. */ + rval = smiapp_write( + sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, + DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); + if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); + if (rval < 0) + return rval; + + return smiapp_write( + sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); +} + +static int smiapp_pll_update(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll_limits lim = { + .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], + .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], + .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], + .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], + .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], + .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], + .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], + .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], + + .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], + .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], + .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], + .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], + .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], + .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], + .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], + .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], + + .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], + .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], + .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], + .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], + .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], + .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], + .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], + .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], + + .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], + .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], + }; + struct smiapp_pll *pll = &sensor->pll; + int rval; + + memset(&sensor->pll, 0, sizeof(sensor->pll)); + + pll->lanes = sensor->platform_data->lanes; + pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; + + if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { + /* + * Fill in operational clock divisors limits from the + * video timing ones. On profile 0 sensors the + * requirements regarding them are essentially the + * same as on VT ones. + */ + lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div; + lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div; + lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div; + lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div; + lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz; + lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz; + lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz; + lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz; + /* Profile 0 sensors have no separate OP clock branch. */ + pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; + } + + if (smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) + pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + + pll->binning_horizontal = sensor->binning_horizontal; + pll->binning_vertical = sensor->binning_vertical; + pll->link_freq = + sensor->link_freq->qmenu_int[sensor->link_freq->val]; + pll->scale_m = sensor->scale_m; + pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + pll->bits_per_pixel = sensor->csi_format->compressed; + + rval = smiapp_pll_calculate(&client->dev, &lim, pll); + if (rval < 0) + return rval; + + sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; + sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; + + return 0; +} + + +/* + * + * V4L2 Controls handling + * + */ + +static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *ctrl = sensor->exposure; + int max; + + max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val + - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; + + ctrl->maximum = max; + if (ctrl->default_value > max) + ctrl->default_value = max; + if (ctrl->val > max) + ctrl->val = max; + if (ctrl->cur.val > max) + ctrl->cur.val = max; +} + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { + { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, +}; + +const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; + +#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ + - (unsigned long)smiapp_csi_data_formats) \ + / sizeof(*smiapp_csi_data_formats)) + +static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int flip = 0; + + if (sensor->hflip) { + if (sensor->hflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + } + + flip ^= sensor->hvflip_inv_mask; + + dev_dbg(&client->dev, "flip %d\n", flip); + return sensor->default_pixel_order ^ flip; +} + +static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int csi_format_idx = + to_csi_format_idx(sensor->csi_format) & ~3; + unsigned int internal_csi_format_idx = + to_csi_format_idx(sensor->internal_csi_format) & ~3; + unsigned int pixel_order = smiapp_pixel_order(sensor); + + sensor->mbus_frame_fmts = + sensor->default_mbus_frame_fmts << pixel_order; + sensor->csi_format = + &smiapp_csi_data_formats[csi_format_idx + pixel_order]; + sensor->internal_csi_format = + &smiapp_csi_data_formats[internal_csi_format_idx + + pixel_order]; + + BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order + >= ARRAY_SIZE(smiapp_csi_data_formats)); + BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); + + dev_dbg(&client->dev, "new pixel order %s\n", + pixel_order_str[pixel_order]); +} + +static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct smiapp_sensor *sensor = + container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) + ->sensor; + u32 orient = 0; + int exposure; + int rval; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + return smiapp_write( + sensor, + SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); + + case V4L2_CID_EXPOSURE: + return smiapp_write( + sensor, + SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + if (sensor->streaming) + return -EBUSY; + + if (sensor->hflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + + orient ^= sensor->hvflip_inv_mask; + rval = smiapp_write(sensor, + SMIAPP_REG_U8_IMAGE_ORIENTATION, + orient); + if (rval < 0) + return rval; + + smiapp_update_mbus_formats(sensor); + + return 0; + + case V4L2_CID_VBLANK: + exposure = sensor->exposure->val; + + __smiapp_update_exposure_limits(sensor); + + if (exposure > sensor->exposure->maximum) { + sensor->exposure->val = + sensor->exposure->maximum; + rval = smiapp_set_ctrl( + sensor->exposure); + if (rval < 0) + return rval; + } + + return smiapp_write( + sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + ctrl->val); + + case V4L2_CID_HBLANK: + return smiapp_write( + sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + ctrl->val); + + case V4L2_CID_LINK_FREQ: + if (sensor->streaming) + return -EBUSY; + + return smiapp_pll_update(sensor); + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { + .s_ctrl = smiapp_set_ctrl, +}; + +static int smiapp_init_controls(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int max; + int rval; + + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); + if (rval) + return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + + sensor->analog_gain = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], + max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); + + /* Exposure limits will be updated soon, use just something here. */ + sensor->exposure = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0, 1, 0); + + sensor->hflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + sensor->vblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VBLANK, 0, 1, 1, 0); + + if (sensor->vblank) + sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->hblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HBLANK, 0, 1, 1, 0); + + if (sensor->hblank) + sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->pixel_rate_parray = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->pixel_array->ctrl_handler.error) { + dev_err(&client->dev, + "pixel array controls initialization failed (%d)\n", + sensor->pixel_array->ctrl_handler.error); + rval = sensor->pixel_array->ctrl_handler.error; + goto error; + } + + sensor->pixel_array->sd.ctrl_handler = + &sensor->pixel_array->ctrl_handler; + + v4l2_ctrl_cluster(2, &sensor->hflip); + + rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); + if (rval) + goto error; + sensor->src->ctrl_handler.lock = &sensor->mutex; + + for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); + + sensor->link_freq = v4l2_ctrl_new_int_menu( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_LINK_FREQ, max, 0, + sensor->platform_data->op_sys_clock); + + sensor->pixel_rate_csi = v4l2_ctrl_new_std( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->src->ctrl_handler.error) { + dev_err(&client->dev, + "src controls initialization failed (%d)\n", + sensor->src->ctrl_handler.error); + rval = sensor->src->ctrl_handler.error; + goto error; + } + + sensor->src->sd.ctrl_handler = + &sensor->src->ctrl_handler; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); + v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); + + return rval; +} + +static void smiapp_free_controls(struct smiapp_sensor *sensor) +{ + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); +} + +static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, + unsigned int n) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + u32 val; + int rval; + + for (i = 0; i < n; i++) { + rval = smiapp_read( + sensor, smiapp_reg_limits[limit[i]].addr, &val); + if (rval) + return rval; + sensor->limits[limit[i]] = val; + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit[i]].addr, + smiapp_reg_limits[limit[i]].what, val, val); + } + + return 0; +} + +static int smiapp_get_all_limits(struct smiapp_sensor *sensor) +{ + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { + rval = smiapp_get_limits(sensor, &i, 1); + if (rval < 0) + return rval; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) + smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); + + return 0; +} + +static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + static u32 const limits[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, + }; + static u32 const limits_replace[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, + }; + unsigned int i; + int rval; + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == + SMIAPP_BINNING_CAPABILITY_NO) { + for (i = 0; i < ARRAY_SIZE(limits); i++) + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + + return 0; + } + + rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); + if (rval < 0) + return rval; + + /* + * Sanity check whether the binning limits are valid. If not, + * use the non-binning ones. + */ + if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) + return 0; + + for (i = 0; i < ARRAY_SIZE(limits); i++) { + dev_dbg(&client->dev, + "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limits[i]].addr, + smiapp_reg_limits[limits[i]].what, + sensor->limits[limits_replace[i]], + sensor->limits[limits_replace[i]]); + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + } + + return 0; +} + +static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int type, n; + unsigned int i, pixel_order; + int rval; + + rval = smiapp_read( + sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); + if (rval) + return rval; + + dev_dbg(&client->dev, "data_format_model_type %d\n", type); + + rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, + &pixel_order); + if (rval) + return rval; + + if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { + dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); + return -EINVAL; + } + + dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, + pixel_order_str[pixel_order]); + + switch (type) { + case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; + break; + case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; + break; + default: + return -EINVAL; + } + + sensor->default_pixel_order = pixel_order; + sensor->mbus_frame_fmts = 0; + + for (i = 0; i < n; i++) { + unsigned int fmt, j; + + rval = smiapp_read( + sensor, + SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); + if (rval) + return rval; + + dev_dbg(&client->dev, "bpp %d, compressed %d\n", + fmt >> 8, (u8)fmt); + + for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { + const struct smiapp_csi_data_format *f = + &smiapp_csi_data_formats[j]; + + if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) + continue; + + if (f->width != fmt >> 8 || f->compressed != (u8)fmt) + continue; + + dev_dbg(&client->dev, "jolly good! %d\n", j); + + sensor->default_mbus_frame_fmts |= 1 << j; + if (!sensor->csi_format) { + sensor->csi_format = f; + sensor->internal_csi_format = f; + } + } + } + + if (!sensor->csi_format) { + dev_err(&client->dev, "no supported mbus code found\n"); + return -EINVAL; + } + + smiapp_update_mbus_formats(sensor); + + return 0; +} + +static void smiapp_update_blanking(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *vblank = sensor->vblank; + struct v4l2_ctrl *hblank = sensor->hblank; + + vblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); + vblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; + + vblank->val = clamp_t(int, vblank->val, + vblank->minimum, vblank->maximum); + vblank->default_value = vblank->minimum; + vblank->val = vblank->val; + vblank->cur.val = vblank->val; + + hblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); + hblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; + + hblank->val = clamp_t(int, hblank->val, + hblank->minimum, hblank->maximum); + hblank->default_value = hblank->minimum; + hblank->val = hblank->val; + hblank->cur.val = hblank->val; + + __smiapp_update_exposure_limits(sensor); +} + +static int smiapp_update_mode(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; + int rval; + + dev_dbg(&client->dev, "frame size: %dx%d\n", + sensor->src->crop[SMIAPP_PAD_SRC].width, + sensor->src->crop[SMIAPP_PAD_SRC].height); + dev_dbg(&client->dev, "csi format width: %d\n", + sensor->csi_format->width); + + /* Binning has to be set up here; it affects limits */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = smiapp_write( + sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + if (rval < 0) + return rval; + + binning_mode = 1; + } + rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + if (rval < 0) + return rval; + + /* Get updated limits due to binning */ + rval = smiapp_get_limits_binning(sensor); + if (rval < 0) + return rval; + + rval = smiapp_pll_update(sensor); + if (rval < 0) + return rval; + + /* Output from pixel array, including blanking */ + smiapp_update_blanking(sensor); + + dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); + dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); + + dev_dbg(&client->dev, "real timeperframe\t100/%d\n", + sensor->pll.vt_pix_clk_freq_hz / + ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + sensor->hblank->val) * + (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val) / 100)); + + return 0; +} + +/* + * + * SMIA++ NVM handling + * + */ +static int smiapp_read_nvm(struct smiapp_sensor *sensor, + unsigned char *nvm) +{ + u32 i, s, p, np, v; + int rval = 0, rval2; + + np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; + for (p = 0; p < np; p++) { + rval = smiapp_write( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + goto out; + + rval = smiapp_write(sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | + SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); + if (rval) + goto out; + + for (i = 0; i < 1000; i++) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + + if (rval) + goto out; + + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; + + if (--i == 0) { + rval = -ETIMEDOUT; + goto out; + } + + } + + for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, + &v); + if (rval) + goto out; + + *nvm++ = v; + } + } + +out: + rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); + if (rval < 0) + return rval; + else + return rval2; +} + +/* + * + * SMIA++ CCI address control + * + */ +static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + u32 val; + + client->addr = sensor->platform_data->i2c_addr_dfl; + + rval = smiapp_write(sensor, + SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, + sensor->platform_data->i2c_addr_alt << 1); + if (rval) + return rval; + + client->addr = sensor->platform_data->i2c_addr_alt; + + /* verify addr change went ok */ + rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); + if (rval) + return rval; + + if (val != sensor->platform_data->i2c_addr_alt << 1) + return -ENODEV; + + return 0; +} + +/* + * + * SMIA++ Mode Control + * + */ +static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) +{ + struct smiapp_flash_strobe_parms *strobe_setup; + unsigned int ext_freq = sensor->platform_data->ext_clk; + u32 tmp; + u32 strobe_adjustment; + u32 strobe_width_high_rs; + int rval; + + strobe_setup = sensor->platform_data->strobe_setup; + + /* + * How to calculate registers related to strobe length. Please + * do not change, or if you do at least know what you're + * doing. :-) + * + * Sakari Ailus 2010-10-25 + * + * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl + * / EXTCLK freq [Hz]) * flash_strobe_adjustment + * + * tFlash_strobe_width_ctrl E N, [1 - 0xffff] + * flash_strobe_adjustment E N, [1 - 0xff] + * + * The formula above is written as below to keep it on one + * line: + * + * l / 10^6 = w / e * a + * + * Let's mark w * a by x: + * + * x = w * a + * + * Thus, we get: + * + * x = l * e / 10^6 + * + * The strobe width must be at least as long as requested, + * thus rounding upwards is needed. + * + * x = (l * e + 10^6 - 1) / 10^6 + * ----------------------------- + * + * Maximum possible accuracy is wanted at all times. Thus keep + * a as small as possible. + * + * Calculate a, assuming maximum w, with rounding upwards: + * + * a = (x + (2^16 - 1) - 1) / (2^16 - 1) + * ------------------------------------- + * + * Thus, we also get w, with that a, with rounding upwards: + * + * w = (x + a - 1) / a + * ------------------- + * + * To get limits: + * + * x E [1, (2^16 - 1) * (2^8 - 1)] + * + * Substituting maximum x to the original formula (with rounding), + * the maximum l is thus + * + * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 + * + * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e + * -------------------------------------------------- + * + * flash_strobe_length must be clamped between 1 and + * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. + * + * Then, + * + * flash_strobe_adjustment = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) + * + * tFlash_strobe_width_ctrl = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + + * flash_strobe_adjustment - 1) / flash_strobe_adjustment + */ + tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - + 1000000 + 1, ext_freq); + strobe_setup->strobe_width_high_us = + clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); + + tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + + 1000000 - 1), 1000000ULL); + strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); + strobe_width_high_rs = (tmp + strobe_adjustment - 1) / + strobe_adjustment; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, + strobe_setup->mode); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, + strobe_adjustment); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, + strobe_width_high_rs); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, + strobe_setup->strobe_delay); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, + strobe_setup->stobe_start_point); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, + strobe_setup->trigger); + +out: + sensor->platform_data->strobe_setup->trigger = 0; + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int smiapp_power_on(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int sleep; + int rval; + + rval = regulator_enable(sensor->vana); + if (rval) { + dev_err(&client->dev, "failed to enable vana regulator\n"); + return rval; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->set_xclk) + rval = sensor->platform_data->set_xclk( + &sensor->src->sd, sensor->platform_data->ext_clk); + else + rval = clk_enable(sensor->ext_clk); + if (rval < 0) { + dev_dbg(&client->dev, "failed to set xclk\n"); + goto out_xclk_fail; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 1); + + sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); + usleep_range(sleep, sleep); + + /* + * Failures to respond to the address change command have been noticed. + * Those failures seem to be caused by the sensor requiring a longer + * boot time than advertised. An additional 10ms delay seems to work + * around the issue, but the SMIA++ I2C write retry hack makes the delay + * unnecessary. The failures need to be investigated to find a proper + * fix, and a delay will likely need to be added here if the I2C write + * retry hack is reverted before the root cause of the boot time issue + * is found. + */ + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + if (rval < 0) { + dev_err(&client->dev, "software reset failed\n"); + goto out_cci_addr_fail; + } + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, + SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); + if (rval) { + dev_err(&client->dev, "compression mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, + sensor->platform_data->ext_clk / (1000000 / (1 << 8))); + if (rval) { + dev_err(&client->dev, "extclk frequency set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, + sensor->platform_data->lanes - 1); + if (rval) { + dev_err(&client->dev, "csi lane mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, + SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); + if (rval) { + dev_err(&client->dev, "fast standby set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, + sensor->platform_data->csi_signalling_mode); + if (rval) { + dev_err(&client->dev, "csi signalling mode set failed\n"); + goto out_cci_addr_fail; + } + + /* DPHY control done by sensor based on requested link rate */ + rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, + SMIAPP_DPHY_CTRL_UI); + if (rval < 0) + return rval; + + rval = smiapp_call_quirk(sensor, post_poweron); + if (rval) { + dev_err(&client->dev, "post_poweron quirks failed\n"); + goto out_cci_addr_fail; + } + + /* Are we still initialising...? If yes, return here. */ + if (!sensor->pixel_array) + return 0; + + rval = v4l2_ctrl_handler_setup( + &sensor->pixel_array->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + mutex_lock(&sensor->mutex); + rval = smiapp_update_mode(sensor); + mutex_unlock(&sensor->mutex); + if (rval < 0) + goto out_cci_addr_fail; + + return 0; + +out_cci_addr_fail: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + +out_xclk_fail: + regulator_disable(sensor->vana); + return rval; +} + +static void smiapp_power_off(struct smiapp_sensor *sensor) +{ + /* + * Currently power/clock to lens are enable/disabled separately + * but they are essentially the same signals. So if the sensor is + * powered off while the lens is powered on the sensor does not + * really see a power off and next time the cci address change + * will fail. So do a soft reset explicitly here. + */ + if (sensor->platform_data->i2c_addr_alt) + smiapp_write(sensor, + SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + usleep_range(5000, 5000); + regulator_disable(sensor->vana); + sensor->streaming = 0; +} + +static int smiapp_set_power(struct v4l2_subdev *subdev, int on) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret = 0; + + mutex_lock(&sensor->power_mutex); + + /* + * If the power count is modified from 0 to != 0 or from != 0 + * to 0, update the power state. + */ + if (!sensor->power_count == !on) + goto out; + + if (on) { + /* Power on and perform initialisation. */ + ret = smiapp_power_on(sensor); + if (ret < 0) + goto out; + } else { + smiapp_power_off(sensor); + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); + +out: + mutex_unlock(&sensor->power_mutex); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Video stream management + */ + +static int smiapp_start_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + + rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, + (sensor->csi_format->width << 8) | + sensor->csi_format->compressed); + if (rval) + goto out; + + rval = smiapp_pll_configure(sensor); + if (rval) + goto out; + + /* Analog crop start coordinates */ + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); + if (rval < 0) + goto out; + + /* Analog crop end coordinates */ + rval = smiapp_write( + sensor, SMIAPP_REG_U16_X_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_Y_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); + if (rval < 0) + goto out; + + /* + * Output from pixel array, including blanking, is set using + * controls below. No need to set here. + */ + + /* Digital crop */ + if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].left); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].top); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler->crop[SMIAPP_PAD_SINK].width); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler->crop[SMIAPP_PAD_SINK].height); + if (rval < 0) + goto out; + } + + /* Scaling */ + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, + sensor->scaling_mode); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, + sensor->scale_m); + if (rval < 0) + goto out; + } + + /* Output size from sensor */ + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].width); + if (rval < 0) + goto out; + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].height); + if (rval < 0) + goto out; + + if ((sensor->flash_capability & + (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | + SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && + sensor->platform_data->strobe_setup != NULL && + sensor->platform_data->strobe_setup->trigger != 0) { + rval = smiapp_setup_flash_strobe(sensor); + if (rval) + goto out; + } + + rval = smiapp_call_quirk(sensor, pre_streamon); + if (rval) { + dev_err(&client->dev, "pre_streamon quirks failed\n"); + goto out; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_STREAMING); + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int smiapp_stop_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); + if (rval) + goto out; + + rval = smiapp_call_quirk(sensor, post_streamoff); + if (rval) + dev_err(&client->dev, "post_streamoff quirks failed\n"); + +out: + mutex_unlock(&sensor->mutex); + return rval; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->streaming == enable) + return 0; + + if (enable) { + sensor->streaming = 1; + rval = smiapp_start_streaming(sensor); + if (rval < 0) + sensor->streaming = 0; + } else { + rval = smiapp_stop_streaming(sensor); + sensor->streaming = 0; + } + + return rval; +} + +static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + int idx = -1; + int rval = -EINVAL; + + mutex_lock(&sensor->mutex); + + dev_err(&client->dev, "subdev %s, pad %d, index %d\n", + subdev->name, code->pad, code->index); + + if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { + if (code->index) + goto out; + + code->code = sensor->internal_csi_format->code; + rval = 0; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i)) + idx++; + + if (idx == code->index) { + code->code = smiapp_csi_data_formats[i].code; + dev_err(&client->dev, "found index %d, i %d, code %x\n", + code->index, i, code->code); + rval = 0; + break; + } + } + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, + unsigned int pad) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) + return sensor->csi_format->code; + else + return sensor->internal_csi_format->code; +} + +static int __smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + } else { + struct v4l2_rect *r; + + if (fmt->pad == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width = r->width; + fmt->format.height = r->height; + } + + return 0; +} + +static int smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_format(subdev, fh, fmt); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_rect **crops, + struct v4l2_rect **comps, int which) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + unsigned int i; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; + } else { + if (crops) { + for (i = 0; i < subdev->entity.num_pads; i++) { + crops[i] = v4l2_subdev_get_try_crop(fh, i); + BUG_ON(!crops[i]); + } + } + if (comps) { + *comps = v4l2_subdev_get_try_compose(fh, + SMIAPP_PAD_SINK); + BUG_ON(!*comps); + } + } +} + +/* Changes require propagation only on sink pad. */ +static void smiapp_propagate(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, int which, + int target) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, which); + + switch (target) { + case V4L2_SEL_TGT_CROP: + comp->width = crops[SMIAPP_PAD_SINK]->width; + comp->height = crops[SMIAPP_PAD_SINK]->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ssd == sensor->scaler) { + sensor->scale_m = + sensor->limits[ + SMIAPP_LIMIT_SCALER_N_MIN]; + sensor->scaling_mode = + SMIAPP_SCALING_MODE_NONE; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + } + /* Fall through */ + case V4L2_SEL_TGT_COMPOSE: + *crops[SMIAPP_PAD_SRC] = *comp; + break; + default: + BUG(); + } +} + +static const struct smiapp_csi_data_format +*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) +{ + const struct smiapp_csi_data_format *csi_format = sensor->csi_format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i) + && smiapp_csi_data_formats[i].code == code) + return &smiapp_csi_data_formats[i]; + } + + return csi_format; +} + +static int smiapp_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *crops[SMIAPP_PADS]; + + mutex_lock(&sensor->mutex); + + /* + * Media bus code is changeable on src subdev's source pad. On + * other source pads we just get format here. + */ + if (fmt->pad == ssd->source_pad) { + u32 code = fmt->format.code; + int rval = __smiapp_get_format(subdev, fh, fmt); + + if (!rval && subdev == &sensor->src->sd) { + const struct smiapp_csi_data_format *csi_format = + smiapp_validate_csi_data_format(sensor, code); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + sensor->csi_format = csi_format; + fmt->format.code = csi_format->code; + } + + mutex_unlock(&sensor->mutex); + return rval; + } + + /* Sink pad. Width and height are changeable here. */ + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width &= ~1; + fmt->format.height &= ~1; + + fmt->format.width = + clamp(fmt->format.width, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); + fmt->format.height = + clamp(fmt->format.height, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); + + smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = fmt->format.width; + crops[ssd->sink_pad]->height = fmt->format.height; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ssd->sink_fmt = *crops[ssd->sink_pad]; + smiapp_propagate(subdev, fh, fmt->which, + V4L2_SEL_TGT_CROP); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Calculate goodness of scaled image size compared to expected image + * size and flags provided. + */ +#define SCALING_GOODNESS 100000 +#define SCALING_GOODNESS_EXTREME 100000000 +static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int val = 0; + + w &= ~1; + ask_w &= ~1; + h &= ~1; + ask_h &= ~1; + + if (flags & V4L2_SEL_FLAG_GE) { + if (w < ask_w) + val -= SCALING_GOODNESS; + if (h < ask_h) + val -= SCALING_GOODNESS; + } + + if (flags & V4L2_SEL_FLAG_LE) { + if (w > ask_w) + val -= SCALING_GOODNESS; + if (h > ask_h) + val -= SCALING_GOODNESS; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) + val -= SCALING_GOODNESS_EXTREME; + + dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", + w, ask_h, h, ask_h, val); + + return val; +} + +static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + unsigned int binh = 1, binv = 1; + unsigned int best = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width, sel->r.width, + crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / sensor->binning_subtypes[i].horizontal, + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / sensor->binning_subtypes[i].vertical, + sel->r.height, sel->flags); + + if (this > best) { + binh = sensor->binning_subtypes[i].horizontal; + binv = sensor->binning_subtypes[i].vertical; + best = this; + } + } + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->binning_vertical = binv; + sensor->binning_horizontal = binh; + } + + sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; + sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; +} + +/* + * Calculate best scaling ratio and mode for given output resolution. + * + * Try all of these: horizontal ratio, vertical ratio and smallest + * size possible (horizontally). + * + * Also try whether horizontal scaler or full scaler gives a better + * result. + */ +static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + u32 min, max, a, b, max_m; + u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + int mode = SMIAPP_SCALING_MODE_HORIZONTAL; + u32 try[4]; + u32 ntry = 0; + unsigned int i; + int best = INT_MIN; + + sel->r.width = min_t(unsigned int, sel->r.width, + crops[SMIAPP_PAD_SINK]->width); + sel->r.height = min_t(unsigned int, sel->r.height, + crops[SMIAPP_PAD_SINK]->height); + + a = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; + b = crops[SMIAPP_PAD_SINK]->height + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; + max_m = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] + / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; + + a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + + dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); + + min = min(max_m, min(a, b)); + max = min(max_m, max(a, b)); + + try[ntry] = min; + ntry++; + if (min != max) { + try[ntry] = max; + ntry++; + } + if (max != max_m) { + try[ntry] = min + 1; + ntry++; + if (min != max) { + try[ntry] = max + 1; + ntry++; + } + } + + for (i = 0; i < ntry; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height, + sel->r.height, + sel->flags); + + dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_HORIZONTAL; + best = this; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + continue; + + this = scaling_goodness( + subdev, crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.height, + sel->flags); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_BOTH; + best = this; + } + } + + sel->r.width = + (crops[SMIAPP_PAD_SINK]->width + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; + if (mode == SMIAPP_SCALING_MODE_BOTH) + sel->r.height = + (crops[SMIAPP_PAD_SINK]->height + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) + & ~1; + else + sel->r.height = crops[SMIAPP_PAD_SINK]->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->scale_m = scale_m; + sensor->scaling_mode = mode; + } +} +/* We're only called on source pads. This function sets scaling. */ +static int smiapp_set_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + sel->r.top = 0; + sel->r.left = 0; + + if (ssd == sensor->binner) + smiapp_set_compose_binner(subdev, fh, sel, crops, comp); + else + smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); + + *comp = sel->r; + smiapp_propagate(subdev, fh, sel->which, + V4L2_SEL_TGT_COMPOSE); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return smiapp_update_mode(sensor); + + return 0; +} + +static int __smiapp_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + /* We only implement crop in three places. */ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array + && sel->pad == SMIAPP_PA_PAD_SRC) + return 0; + if (ssd == sensor->src + && sel->pad == SMIAPP_PAD_SRC) + return 0; + if (ssd == sensor->scaler + && sel->pad == SMIAPP_PAD_SINK + && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) + return 0; + return -EINVAL; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == ssd->source_pad) + return -EINVAL; + if (ssd == sensor->binner) + return 0; + if (ssd == sensor->scaler + && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) + return 0; + /* Fall through */ + default: + return -EINVAL; + } +} + +static int smiapp_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; + struct v4l2_rect _r; + + smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + } else { + if (sel->pad == ssd->sink_pad) { + _r.left = 0; + _r.top = 0; + _r.width = v4l2_subdev_get_try_format(fh, sel->pad) + ->width; + _r.height = v4l2_subdev_get_try_format(fh, sel->pad) + ->height; + src_size = &_r; + } else { + src_size = + v4l2_subdev_get_try_compose( + fh, ssd->sink_pad); + } + } + + if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { + sel->r.left = 0; + sel->r.top = 0; + } + + sel->r.width = min(sel->r.width, src_size->width); + sel->r.height = min(sel->r.height, src_size->height); + + sel->r.left = min(sel->r.left, src_size->width - sel->r.width); + sel->r.top = min(sel->r.top, src_size->height - sel->r.height); + + *crops[sel->pad] = sel->r; + + if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) + smiapp_propagate(subdev, fh, sel->which, + V4L2_SEL_TGT_CROP); + + return 0; +} + +static int __smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + struct v4l2_rect sink_fmt; + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = ssd->sink_fmt; + } else { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(fh, ssd->sink_pad); + + sink_fmt.left = 0; + sink_fmt.top = 0; + sink_fmt.width = fmt->width; + sink_fmt.height = fmt->height; + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array) { + sel->r.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + sel->r.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + } else if (sel->pad == ssd->sink_pad) { + sel->r = sink_fmt; + } else { + sel->r = *comp; + } + break; + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r = *crops[sel->pad]; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r = *comp; + break; + } + + return 0; +} + +static int smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_selection(subdev, fh, sel); + mutex_unlock(&sensor->mutex); + + return rval; +} +static int smiapp_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + mutex_lock(&sensor->mutex); + + sel->r.left = max(0, sel->r.left & ~1); + sel->r.top = max(0, sel->r.top & ~1); + sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); + sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); + + sel->r.width = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sel->r.width); + sel->r.height = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sel->r.height); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + ret = smiapp_set_crop(subdev, fh, sel); + break; + case V4L2_SEL_TGT_COMPOSE: + ret = smiapp_set_compose(subdev, fh, sel); + break; + default: + BUG(); + } + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + *frames = sensor->frame_skip; + return 0; +} + +/* ----------------------------------------------------------------------------- + * sysfs attributes + */ + +static ssize_t +smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int nbytes; + + if (!sensor->dev_init_done) + return -EBUSY; + + if (!sensor->nvm_size) { + /* NVM not read yet - read it now */ + sensor->nvm_size = sensor->platform_data->nvm_size; + if (smiapp_set_power(subdev, 1) < 0) + return -ENODEV; + if (smiapp_read_nvm(sensor, sensor->nvm)) { + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } + smiapp_set_power(subdev, 0); + } + /* + * NVM is still way below a PAGE_SIZE, so we can safely + * assume this for now. + */ + nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); + memcpy(buf, sensor->nvm, nbytes); + + return nbytes; +} +static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int smiapp_identify_module(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_module_info *minfo = &sensor->minfo; + unsigned int i; + int rval = 0; + + minfo->name = SMIAPP_NAME; + + /* Module info */ + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->manufacturer_id); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, + &minfo->model_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, + &minfo->revision_number_major); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MINOR, + &minfo->revision_number_minor); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_YEAR, + &minfo->module_year); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_MONTH, + &minfo->module_month); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, + &minfo->module_day); + + /* Sensor info */ + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, + &minfo->sensor_manufacturer_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U16_SENSOR_MODEL_ID, + &minfo->sensor_model_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); + + /* SMIA */ + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); + + if (rval) { + dev_err(&client->dev, "sensor detection failed\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", + minfo->manufacturer_id, minfo->model_id); + + dev_dbg(&client->dev, + "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", + minfo->revision_number_major, minfo->revision_number_minor, + minfo->module_year, minfo->module_month, minfo->module_day); + + dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", + minfo->sensor_manufacturer_id, minfo->sensor_model_id); + + dev_dbg(&client->dev, + "sensor revision 0x%2.2x firmware version 0x%2.2x\n", + minfo->sensor_revision_number, minfo->sensor_firmware_version); + + dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", + minfo->smia_version, minfo->smiapp_version); + + /* + * Some modules have bad data in the lvalues below. Hope the + * rvalues have better stuff. The lvalues are module + * parameters whereas the rvalues are sensor parameters. + */ + if (!minfo->manufacturer_id && !minfo->model_id) { + minfo->manufacturer_id = minfo->sensor_manufacturer_id; + minfo->model_id = minfo->sensor_model_id; + minfo->revision_number_major = minfo->sensor_revision_number; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { + if (smiapp_module_idents[i].manufacturer_id + != minfo->manufacturer_id) + continue; + if (smiapp_module_idents[i].model_id != minfo->model_id) + continue; + if (smiapp_module_idents[i].flags + & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { + if (smiapp_module_idents[i].revision_number_major + < minfo->revision_number_major) + continue; + } else { + if (smiapp_module_idents[i].revision_number_major + != minfo->revision_number_major) + continue; + } + + minfo->name = smiapp_module_idents[i].name; + minfo->quirk = smiapp_module_idents[i].quirk; + break; + } + + if (i >= ARRAY_SIZE(smiapp_module_idents)) + dev_warn(&client->dev, + "no quirks for this module; let's hope it's fully compliant\n"); + + dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", + minfo->name, minfo->manufacturer_id, minfo->model_id, + minfo->revision_number_major); + + strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); + + return 0; +} + +static const struct v4l2_subdev_ops smiapp_ops; +static const struct v4l2_subdev_internal_ops smiapp_internal_ops; +static const struct media_entity_operations smiapp_entity_ops; + +static int smiapp_registered(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_subdev *last = NULL; + u32 tmp; + unsigned int i; + int rval; + + sensor->vana = regulator_get(&client->dev, "VANA"); + if (IS_ERR(sensor->vana)) { + dev_err(&client->dev, "could not get regulator for vana\n"); + return -ENODEV; + } + + if (!sensor->platform_data->set_xclk) { + sensor->ext_clk = clk_get(&client->dev, + sensor->platform_data->ext_clk_name); + if (IS_ERR(sensor->ext_clk)) { + dev_err(&client->dev, "could not get clock %s\n", + sensor->platform_data->ext_clk_name); + rval = -ENODEV; + goto out_clk_get; + } + + rval = clk_set_rate(sensor->ext_clk, + sensor->platform_data->ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock %s freq to %u\n", + sensor->platform_data->ext_clk_name, + sensor->platform_data->ext_clk); + rval = -ENODEV; + goto out_clk_set_rate; + } + } + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { + if (gpio_request_one(sensor->platform_data->xshutdown, 0, + "SMIA++ xshutdown") != 0) { + dev_err(&client->dev, + "unable to acquire reset gpio %d\n", + sensor->platform_data->xshutdown); + rval = -ENODEV; + goto out_clk_set_rate; + } + } + + rval = smiapp_power_on(sensor); + if (rval) { + rval = -ENODEV; + goto out_smiapp_power_on; + } + + rval = smiapp_identify_module(subdev); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + rval = smiapp_get_all_limits(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + /* + * Handle Sensor Module orientation on the board. + * + * The application of H-FLIP and V-FLIP on the sensor is modified by + * the sensor orientation on the board. + * + * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set + * both H-FLIP and V-FLIP for normal operation which also implies + * that a set/unset operation for user space HFLIP and VFLIP v4l2 + * controls will need to be internally inverted. + * + * Rotation also changes the bayer pattern. + */ + if (sensor->platform_data->module_board_orient == + SMIAPP_MODULE_BOARD_ORIENT_180) + sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | + SMIAPP_IMAGE_ORIENTATION_VFLIP; + + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { + u32 val; + + rval = smiapp_read(sensor, + SMIAPP_REG_U8_BINNING_SUBTYPES, &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->nbinning_subtypes = min_t(u8, val, + SMIAPP_BINNING_SUBTYPES); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + rval = smiapp_read( + sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->binning_subtypes[i] = + *(struct smiapp_binning_subtype *)&val; + + dev_dbg(&client->dev, "binning %xx%x\n", + sensor->binning_subtypes[i].horizontal, + sensor->binning_subtypes[i].vertical); + } + } + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + + /* SMIA++ NVM initialization - it will be read from the sensor + * when it is first requested by userspace. + */ + if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { + sensor->nvm = kzalloc(sensor->platform_data->nvm_size, + GFP_KERNEL); + if (sensor->nvm == NULL) { + dev_err(&client->dev, "nvm buf allocation failed\n"); + rval = -ENOMEM; + goto out_power_off; + } + + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto out_power_off; + } + } + + rval = smiapp_call_quirk(sensor, limits); + if (rval) { + dev_err(&client->dev, "limits quirks failed\n"); + goto out_nvm_release; + } + + /* We consider this as profile 0 sensor if any of these are zero. */ + if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; + } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; + else + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + + sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + + for (i = 0; i < SMIAPP_SUBDEVS; i++) { + struct { + struct smiapp_subdev *ssd; + char *name; + } const __this[] = { + { sensor->scaler, "scaler", }, + { sensor->binner, "binner", }, + { sensor->pixel_array, "pixel array", }, + }, *_this = &__this[i]; + struct smiapp_subdev *this = _this->ssd; + + if (!this) + continue; + + if (this != sensor->src) + v4l2_subdev_init(&this->sd, &smiapp_ops); + + this->sensor = sensor; + + if (this == sensor->pixel_array) { + this->npads = 1; + } else { + this->npads = 2; + this->source_pad = 1; + } + + snprintf(this->sd.name, + sizeof(this->sd.name), "%s %s", + sensor->minfo.name, _this->name); + + this->sink_fmt.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + this->sink_fmt.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + this->compose.width = this->sink_fmt.width; + this->compose.height = this->sink_fmt.height; + this->crop[this->source_pad] = this->compose; + this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; + if (this != sensor->pixel_array) { + this->crop[this->sink_pad] = this->compose; + this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; + } + + this->sd.entity.ops = &smiapp_entity_ops; + + if (last == NULL) { + last = this; + continue; + } + + this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + this->sd.internal_ops = &smiapp_internal_ops; + this->sd.owner = NULL; + v4l2_set_subdevdata(&this->sd, client); + + rval = media_entity_init(&this->sd.entity, + this->npads, this->pads, 0); + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + goto out_nvm_release; + } + + rval = media_entity_create_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + goto out_nvm_release; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + goto out_nvm_release; + } + + last = this; + } + + dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); + + sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + /* final steps */ + smiapp_read_frame_fmt(sensor); + rval = smiapp_init_controls(sensor); + if (rval < 0) + goto out_nvm_release; + + rval = smiapp_update_mode(sensor); + if (rval) { + dev_err(&client->dev, "update mode failed\n"); + goto out_nvm_release; + } + + sensor->streaming = false; + sensor->dev_init_done = true; + + /* check flash capability */ + rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); + sensor->flash_capability = tmp; + if (rval) + goto out_nvm_release; + + smiapp_power_off(sensor); + + return 0; + +out_nvm_release: + device_remove_file(&client->dev, &dev_attr_nvm); + +out_power_off: + kfree(sensor->nvm); + sensor->nvm = NULL; + smiapp_power_off(sensor); + +out_smiapp_power_on: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + +out_clk_set_rate: + clk_put(sensor->ext_clk); + sensor->ext_clk = NULL; + +out_clk_get: + regulator_put(sensor->vana); + sensor->vana = NULL; + return rval; +} + +static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(sd); + struct smiapp_sensor *sensor = ssd->sensor; + u32 mbus_code = + smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; + unsigned int i; + + mutex_lock(&sensor->mutex); + + for (i = 0; i < ssd->npads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(fh, i); + struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); + struct v4l2_rect *try_comp; + + try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + try_fmt->code = mbus_code; + + try_crop->top = 0; + try_crop->left = 0; + try_crop->width = try_fmt->width; + try_crop->height = try_fmt->height; + + if (ssd != sensor->pixel_array) + continue; + + try_comp = v4l2_subdev_get_try_compose(fh, i); + *try_comp = *try_crop; + } + + mutex_unlock(&sensor->mutex); + + return smiapp_set_power(sd, 1); +} + +static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return smiapp_set_power(sd, 0); +} + +static const struct v4l2_subdev_video_ops smiapp_video_ops = { + .s_stream = smiapp_set_stream, +}; + +static const struct v4l2_subdev_core_ops smiapp_core_ops = { + .s_power = smiapp_set_power, +}; + +static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { + .enum_mbus_code = smiapp_enum_mbus_code, + .get_fmt = smiapp_get_format, + .set_fmt = smiapp_set_format, + .get_selection = smiapp_get_selection, + .set_selection = smiapp_set_selection, +}; + +static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { + .g_skip_frames = smiapp_get_skip_frames, +}; + +static const struct v4l2_subdev_ops smiapp_ops = { + .core = &smiapp_core_ops, + .video = &smiapp_video_ops, + .pad = &smiapp_pad_ops, + .sensor = &smiapp_sensor_ops, +}; + +static const struct media_entity_operations smiapp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { + .registered = smiapp_registered, + .open = smiapp_open, + .close = smiapp_close, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { + .open = smiapp_open, + .close = smiapp_close, +}; + +/* ----------------------------------------------------------------------------- + * I2C Driver + */ + +#ifdef CONFIG_PM + +static int smiapp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + bool streaming; + + BUG_ON(mutex_is_locked(&sensor->mutex)); + + if (sensor->power_count == 0) + return 0; + + if (sensor->streaming) + smiapp_stop_streaming(sensor); + + streaming = sensor->streaming; + + smiapp_power_off(sensor); + + /* save state for resume */ + sensor->streaming = streaming; + + return 0; +} + +static int smiapp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->power_count == 0) + return 0; + + rval = smiapp_power_on(sensor); + if (rval) + return rval; + + if (sensor->streaming) + rval = smiapp_start_streaming(sensor); + + return rval; +} + +#else + +#define smiapp_suspend NULL +#define smiapp_resume NULL + +#endif /* CONFIG_PM */ + +static int smiapp_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct smiapp_sensor *sensor; + int rval; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + sensor->platform_data = client->dev.platform_data; + mutex_init(&sensor->mutex); + mutex_init(&sensor->power_mutex); + sensor->src = &sensor->ssds[sensor->ssds_used]; + + v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); + sensor->src->sd.internal_ops = &smiapp_internal_src_ops; + sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->src->sensor = sensor; + + sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; + rval = media_entity_init(&sensor->src->sd.entity, 2, + sensor->src->pads, 0); + if (rval < 0) + kfree(sensor); + + return rval; +} + +static int __exit smiapp_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + + if (sensor->power_count) { + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + sensor->power_count = 0; + } + + if (sensor->nvm) { + device_remove_file(&client->dev, &dev_attr_nvm); + kfree(sensor->nvm); + } + + for (i = 0; i < sensor->ssds_used; i++) { + media_entity_cleanup(&sensor->ssds[i].sd.entity); + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); + } + smiapp_free_controls(sensor); + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + if (sensor->ext_clk) + clk_put(sensor->ext_clk); + if (sensor->vana) + regulator_put(sensor->vana); + + kfree(sensor); + + return 0; +} + +static const struct i2c_device_id smiapp_id_table[] = { + { SMIAPP_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, smiapp_id_table); + +static const struct dev_pm_ops smiapp_pm_ops = { + .suspend = smiapp_suspend, + .resume = smiapp_resume, +}; + +static struct i2c_driver smiapp_i2c_driver = { + .driver = { + .name = SMIAPP_NAME, + .pm = &smiapp_pm_ops, + }, + .probe = smiapp_probe, + .remove = __exit_p(smiapp_remove), + .id_table = smiapp_id_table, +}; + +module_i2c_driver(smiapp_i2c_driver); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c new file mode 100644 index 000000000000..fb2f81ad8c3b --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-limits.c @@ -0,0 +1,132 @@ +/* + * drivers/media/i2c/smiapp/smiapp-limits.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "smiapp.h" + +struct smiapp_reg_limits smiapp_reg_limits[] = { + { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, + { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, + { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, + { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ + { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, + { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ + { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, + { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, + { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, + { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ + { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ + { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, + { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ + { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, + { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, + { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ + { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ + { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, + { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, + { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, + { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, + { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ + { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, + { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, + { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, + { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, + { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ + { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, + { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, + { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, + { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, + { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ + { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, + { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, + { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, + { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, + { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ + { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, + { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, + { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, + { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, + { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ + { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ + { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, + { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, + { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ + { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, + { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, + { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, + { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, + { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, + { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, + { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, + { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ + { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, + { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, + { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, + { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, + { 0, NULL }, +}; diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h new file mode 100644 index 000000000000..9ae765e23ea5 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-limits.h @@ -0,0 +1,128 @@ +/* + * drivers/media/i2c/smiapp/smiapp-limits.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 +#define SMIAPP_LIMIT_THS_ZERO_MIN 3 +#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 +#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 +#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 +#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 +#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 +#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 +#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 +#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 +#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 +#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 +#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 +#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 +#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 +#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 +#define SMIAPP_LIMIT_X_ADDR_MIN 46 +#define SMIAPP_LIMIT_Y_ADDR_MIN 47 +#define SMIAPP_LIMIT_X_ADDR_MAX 48 +#define SMIAPP_LIMIT_Y_ADDR_MAX 49 +#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 +#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 +#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 +#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 +#define SMIAPP_LIMIT_MIN_EVEN_INC 54 +#define SMIAPP_LIMIT_MAX_EVEN_INC 55 +#define SMIAPP_LIMIT_MIN_ODD_INC 56 +#define SMIAPP_LIMIT_MAX_ODD_INC 57 +#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 +#define SMIAPP_LIMIT_SCALER_M_MIN 59 +#define SMIAPP_LIMIT_SCALER_M_MAX 60 +#define SMIAPP_LIMIT_SCALER_N_MIN 61 +#define SMIAPP_LIMIT_SCALER_N_MAX 62 +#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 +#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 +#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 +#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 +#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 +#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 +#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 +#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 +#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 +#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 +#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 +#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 +#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 +#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 +#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 +#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 +#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 +#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 +#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 +#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 +#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 +#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 +#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 +#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 +#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c new file mode 100644 index 000000000000..cf048128367c --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -0,0 +1,306 @@ +/* + * drivers/media/i2c/smiapp/smiapp-quirk.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include + +#include "smiapp.h" + +static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) +{ + return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); +} + +static int smiapp_write_8s(struct smiapp_sensor *sensor, + struct smiapp_reg_8 *regs, int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + for (; len > 0; len--, regs++) { + rval = smiapp_write_8(sensor, regs->reg, regs->val); + if (rval < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + rval, regs->reg, regs->val); + return rval; + } + } + + return 0; +} + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit].addr, + smiapp_reg_limits[limit].what, val, val); + sensor->limits[limit] = val; +} + +int smiapp_replace_limit_at(struct smiapp_sensor *sensor, + u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + + for (i = 0; smiapp_reg_limits[i].addr; i++) { + if ((smiapp_reg_limits[i].addr & 0xffff) != reg) + continue; + + smiapp_replace_limit(sensor, i, val); + + return 0; + } + + dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg); + + return -EINVAL; +} + +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct smia_reg *sreg; + + if (!sensor->minfo.quirk) + return false; + + sreg = sensor->minfo.quirk->regs; + + if (!sreg) + return false; + + while (sreg->type) { + u16 type = reg >> 16; + u16 reg16 = reg; + + if (sreg->type != type || sreg->reg != reg16) { + sreg++; + continue; + } + + switch ((u8)type) { + case SMIA_REG_8BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", + reg, sreg->val); + break; + case SMIA_REG_16BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", + reg, sreg->val); + break; + case SMIA_REG_32BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", + reg, sreg->val); + break; + } + + *val = sreg->val; + + return true; + } + + return false; +} + +static int jt8ew9_limits(struct smiapp_sensor *sensor) +{ + if (sensor->minfo.revision_number_major < 0x03) + sensor->frame_skip = 1; + + /* Below 24 gain doesn't have effect at all, */ + /* but ~59 is needed for full dynamic range */ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); + smiapp_replace_limit( + sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); + + return 0; +} + +static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) +{ + struct smiapp_reg_8 regs[] = { + { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ + { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ + { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + /* Taken from v03. No idea what the rest are. */ + { 0x32e0, 0x05 }, + { 0x32e1, 0x05 }, + { 0x32e2, 0x04 }, + { 0x32e5, 0x04 }, + { 0x32e6, 0x04 }, + + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_jt8ew9_quirk = { + .limits = jt8ew9_limits, + .post_poweron = jt8ew9_post_poweron, +}; + +static int imx125es_post_poweron(struct smiapp_sensor *sensor) +{ + /* Taken from v02. No idea what the other two are. */ + struct smiapp_reg_8 regs[] = { + /* + * 0x3302: clk during frame blanking: + * 0x00 - HS mode, 0x01 - LP11 + */ + { 0x3302, 0x01 }, + { 0x302d, 0x00 }, + { 0x3b08, 0x8c }, + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_imx125es_quirk = { + .post_poweron = imx125es_post_poweron, +}; + +static int jt8ev1_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); + smiapp_replace_limit(sensor, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); + + return 0; +} + +static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + struct smiapp_reg_8 regs[] = { + { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ + { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ + { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ + { 0x3238, 0x43 }, + { 0x3301, 0x06 }, /* For analog bias for sensor */ + { 0x3302, 0x06 }, + { 0x3304, 0x00 }, + { 0x3305, 0x88 }, + { 0x332a, 0x14 }, + { 0x332c, 0x6b }, + { 0x3336, 0x01 }, + { 0x333f, 0x1f }, + { 0x3355, 0x00 }, + { 0x3356, 0x20 }, + { 0x33bf, 0x20 }, /* Adjust the FBC speed */ + { 0x33c9, 0x20 }, + { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ + { 0x33cf, 0xec }, /* For Black sun */ + { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ + }; + + struct smiapp_reg_8 regs_96[] = { + { 0x30ae, 0x00 }, /* For control of ADC clock */ + { 0x30af, 0xd0 }, + { 0x30b0, 0x01 }, + }; + + rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + if (rval < 0) + return rval; + + switch (sensor->platform_data->ext_clk) { + case 9600000: + return smiapp_write_8s(sensor, regs_96, + ARRAY_SIZE(regs_96)); + default: + dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", + sensor->platform_data->ext_clk); + return 0; + } +} + +static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) +{ + return smiapp_write_8(sensor, 0x3328, 0x00); +} + +static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) +{ + int rval; + + /* Workaround: allows fast standby to work properly */ + rval = smiapp_write_8(sensor, 0x3205, 0x04); + if (rval < 0) + return rval; + + /* Wait for 1 ms + one line => 2 ms is likely enough */ + usleep_range(2000, 2000); + + /* Restore it */ + rval = smiapp_write_8(sensor, 0x3205, 0x00); + if (rval < 0) + return rval; + + return smiapp_write_8(sensor, 0x3328, 0x80); +} + +const struct smiapp_quirk smiapp_jt8ev1_quirk = { + .limits = jt8ev1_limits, + .post_poweron = jt8ev1_post_poweron, + .pre_streamon = jt8ev1_pre_streamon, + .post_streamoff = jt8ev1_post_streamoff, + .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, +}; + +static int tcm8500md_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); + + return 0; +} + +const struct smiapp_quirk smiapp_tcm8500md_quirk = { + .limits = tcm8500md_limits, +}; diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h new file mode 100644 index 000000000000..86fd3e8bfb0f --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -0,0 +1,83 @@ +/* + * drivers/media/i2c/smiapp/smiapp-quirk.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_QUIRK__ +#define __SMIAPP_QUIRK__ + +struct smiapp_sensor; + +/** + * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard + * + * @limits: Replace sensor->limits with values which can't be read from + * sensor registers. Called the first time the sensor is powered up. + * @post_poweron: Called always after the sensor has been fully powered on. + * @pre_streamon: Called just before streaming is enabled. + * @post_streamon: Called right after stopping streaming. + */ +struct smiapp_quirk { + int (*limits)(struct smiapp_sensor *sensor); + int (*post_poweron)(struct smiapp_sensor *sensor); + int (*pre_streamon)(struct smiapp_sensor *sensor); + int (*post_streamoff)(struct smiapp_sensor *sensor); + const struct smia_reg *regs; + unsigned long flags; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) + +struct smiapp_reg_8 { + u16 reg; + u8 val; +}; + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val); +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val); + +#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ + { \ + .type = (_reg >> 16), \ + .reg = (u16)_reg, \ + .val = _val, \ + } + +#define smiapp_call_quirk(_sensor, _quirk, ...) \ + (_sensor->minfo.quirk && \ + _sensor->minfo.quirk->_quirk ? \ + _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) + +#define smiapp_needs_quirk(_sensor, _quirk) \ + (_sensor->minfo.quirk ? \ + _sensor->minfo.quirk->flags & _quirk : 0) + +extern const struct smiapp_quirk smiapp_jt8ev1_quirk; +extern const struct smiapp_quirk smiapp_imx125es_quirk; +extern const struct smiapp_quirk smiapp_jt8ew9_quirk; +extern const struct smiapp_quirk smiapp_tcm8500md_quirk; + +#endif /* __SMIAPP_QUIRK__ */ diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h new file mode 100644 index 000000000000..defa7c5adebf --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h @@ -0,0 +1,503 @@ +/* + * drivers/media/i2c/smiapp/smiapp-reg-defs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) +#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) +#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) +#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) +#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) +#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) +#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) +#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) +#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) +#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) +#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) +#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) +#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) +#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) +#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) +#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) +#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) +#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) +#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) +#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) +#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) +#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ +#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) +#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) +#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) +#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) +#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) +#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) +#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) +#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) +#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) +#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) +#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) +#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) +#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) +#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) +#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) +#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) +#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) +#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) +#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) +#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) +#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) +#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) +#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) +#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) +#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) +#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) +#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) +#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) +#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) +#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) +#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) +#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) +#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) +#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) +#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) +#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) +#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) +#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) +#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) +#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) +#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) +#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) +#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) +#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) +#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) +#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) +#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) +#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) +#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) +#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) +#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) +#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) +#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) +#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) +#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) +#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) +#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) +#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) +#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) +#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) +#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) +#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) +#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) +#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) +#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) +#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) +#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) +#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) +#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) +#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) +#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) +#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) +#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) +#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) +#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) +#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) +#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) +#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) +#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) +#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) +#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) +#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) +#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) +#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) +#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) +#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) +#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) +#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) +#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) +#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) +#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) +#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) +#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) +#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) +#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) +#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) +#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) +#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) +#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) +#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) +#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) +#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) +#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) +#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) +#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) +#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) +#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) +#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) +#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) +#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) +#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) +#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) +#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) +#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) +#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) +#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) +#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) +#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) +#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) +#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) +#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) +#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) +#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) +#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) +#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) +#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) +#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) +#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) +#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) +#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) +#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) +#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) +#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) +#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) +#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) +#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) +#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) +#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) +#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) +#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) +#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) +#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) +#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) +#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) +#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) +#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) +#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) +#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) +#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) +#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) +#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) +#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) +#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) +#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) +#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) +#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) +#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) +#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) +#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) +#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) +#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) +#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) +#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) +#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) +#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) +#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) +#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) +#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) +#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) +#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) +#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) +#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) +#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) +#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) +#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) +#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) +#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) +#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) +#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) +#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) +#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) +#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) +#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) +#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) +#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) +#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) +#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) +#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) +#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) +#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) +#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) +#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) +#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) +#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) +#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) +#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) +#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) +#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) +#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) +#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) +#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) +#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) +#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) +#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) +#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) +#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h new file mode 100644 index 000000000000..54568ca2fe6d --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -0,0 +1,122 @@ +/* + * drivers/media/i2c/smiapp/smiapp-reg.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_REG_H_ +#define __SMIAPP_REG_H_ + +#include "smiapp-reg-defs.h" + +/* Bits for above register */ +#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) + +#define SMIAPP_SOFTWARE_RESET (1 << 0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) + +#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 +/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ +#define SMIAPP_DPHY_CTRL_UI 1 +#define SMIAPP_DPHY_CTRL_REGISTER 2 + +#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 +#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 + +#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 +#define SMIAPP_MODE_SELECT_STREAMING 1 + +#define SMIAPP_SCALING_MODE_NONE 0 +#define SMIAPP_SCALING_MODE_HORIZONTAL 1 +#define SMIAPP_SCALING_MODE_BOTH 2 + +#define SMIAPP_SCALING_CAPABILITY_NONE 0 +#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 +#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ + +/* digital crop right before scaler */ +#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 +#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 + +#define SMIAPP_BINNING_CAPABILITY_NO 0 +#define SMIAPP_BINNING_CAPABILITY_YES 1 + +/* Maximum number of binning subtypes */ +#define SMIAPP_BINNING_SUBTYPES 253 + +#define SMIAPP_PIXEL_ORDER_GRBG 0 +#define SMIAPP_PIXEL_ORDER_RGGB 1 +#define SMIAPP_PIXEL_ORDER_BGGR 2 +#define SMIAPP_PIXEL_ORDER_GBRG 3 + +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 + +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 + +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff + +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff + +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 + +#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 +#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 + +/* Scaling N factor */ +#define SMIAPP_SCALE_N 16 + +/* Image statistics registers */ +/* Registers 0x2000 to 0x2fff are reserved for future + * use for statistics features. + */ + +/* Manufacturer Specific Registers: 0x3000 to 0x3fff + * The manufacturer specifies these as a black box. + */ + +#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c new file mode 100644 index 000000000000..70e0d8db0130 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -0,0 +1,273 @@ +/* + * drivers/media/i2c/smiapp/smiapp-regs.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include + +#include "smiapp.h" +#include "smiapp-regs.h" + +static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, + uint32_t phloat) +{ + int32_t exp; + uint64_t man; + + if (phloat >= 0x80000000) { + dev_err(&client->dev, "this is a negative number\n"); + return 0; + } + + if (phloat == 0x7f800000) + return ~0; /* Inf. */ + + if ((phloat & 0x7f800000) == 0x7f800000) { + dev_err(&client->dev, "NaN or other special number\n"); + return 0; + } + + /* Valid cases begin here */ + if (phloat == 0) + return 0; /* Valid zero */ + + if (phloat > 0x4f800000) + return ~0; /* larger than 4294967295 */ + + /* + * Unbias exponent (note how phloat is now guaranteed to + * have 0 in the high bit) + */ + exp = ((int32_t)phloat >> 23) - 127; + + /* Extract mantissa, add missing '1' bit and it's in MHz */ + man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; + + if (exp < 0) + man >>= -exp; + else + man <<= exp; + + man >>= 23; /* Remove mantissa bias */ + + return man & 0xffffffff; +} + + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[4]; + u16 offset = reg; + int r; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (offset >> 8); + data[1] = (u8) offset; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + msg.len = len; + msg.flags = I2C_M_RD; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + *val = 0; + /* high byte comes first */ + switch (len) { + case SMIA_REG_32BIT: + *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + + data[3]; + break; + case SMIA_REG_16BIT: + *val = (data[0] << 8) + data[1]; + break; + case SMIA_REG_8BIT: + *val = data[0]; + break; + default: + BUG(); + } + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + + return r; +} + +/* Read a register using 8-bit access only. */ +static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + unsigned int i; + int rval; + + *val = 0; + + for (i = 0; i < len; i++) { + u32 val8; + + rval = ____smiapp_read(sensor, reg + i, 1, &val8); + if (rval < 0) + return rval; + *val |= val8 << ((len - i - 1) << 3); + } + + return 0; +} + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, + bool only8) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int len = (u8)(reg >> 16); + int rval; + + if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT + && len != SMIA_REG_32BIT) + return -EINVAL; + + if (smiapp_quirk_reg(sensor, reg, val)) + goto found_quirk; + + if (len == SMIA_REG_8BIT && !only8) + rval = ____smiapp_read(sensor, (u16)reg, len, val); + else + rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); + if (rval < 0) + return rval; + +found_quirk: + if (reg & SMIA_REG_FLAG_FLOAT) + *val = float_to_u32_mul_1000000(client, *val); + + return 0; +} + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read( + sensor, reg, val, + smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); +} + +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read(sensor, reg, val, true); +} + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[6]; + unsigned int retries; + unsigned int flags = reg >> 24; + unsigned int len = (u8)(reg >> 16); + u16 offset = reg; + int r; + + if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && + len != SMIA_REG_32BIT) || flags) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; /* Write */ + msg.len = 2 + len; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + + switch (len) { + case SMIA_REG_8BIT: + data[2] = val; + break; + case SMIA_REG_16BIT: + data[2] = val >> 8; + data[3] = val; + break; + case SMIA_REG_32BIT: + data[2] = val >> 24; + data[3] = val >> 16; + data[4] = val >> 8; + data[5] = val; + break; + default: + BUG(); + } + + for (retries = 0; retries < 5; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, &msg, 1); + if (r == 1) { + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. " + "retries: %d\n", retries); + return 0; + } + + usleep_range(2000, 2000); + } + + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); + + return r; +} diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h new file mode 100644 index 000000000000..7f9013b47971 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp-regs.h @@ -0,0 +1,49 @@ +/* + * include/media/smiapp/smiapp-regs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_REGS_H +#define SMIAPP_REGS_H + +#include +#include + +/* Use upper 8 bits of the type field for flags */ +#define SMIA_REG_FLAG_FLOAT (1 << 24) + +#define SMIA_REG_8BIT 1 +#define SMIA_REG_16BIT 2 +#define SMIA_REG_32BIT 4 +struct smia_reg { + u16 type; + u16 reg; /* 16-bit offset */ + u32 val; /* 8/16/32-bit value */ +}; + +struct smiapp_sensor; + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); + +#endif diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h new file mode 100644 index 000000000000..4182a695ab53 --- /dev/null +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -0,0 +1,252 @@ +/* + * drivers/media/i2c/smiapp/smiapp.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_PRIV_H_ +#define __SMIAPP_PRIV_H_ + +#include +#include +#include +#include + +#include "smiapp-pll.h" +#include "smiapp-reg.h" +#include "smiapp-regs.h" +#include "smiapp-quirk.h" + +/* + * Standard SMIA++ constants + */ +#define SMIA_VERSION_1 10 +#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ +#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ +#define SMIAPP_VERSION_1 10 + +#define SMIAPP_PROFILE_0 0 +#define SMIAPP_PROFILE_1 1 +#define SMIAPP_PROFILE_2 2 + +#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ + +#define SMIAPP_RESET_DELAY_CLOCKS 2400 +#define SMIAPP_RESET_DELAY(clk) \ + (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + + (clk) / 1000 - 1) / ((clk) / 1000)) + +#include "smiapp-limits.h" + +struct smiapp_quirk; + +#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) + +struct smiapp_module_ident { + u8 manufacturer_id; + u16 model_id; + u8 revision_number_major; + + u8 flags; + + char *name; + const struct smiapp_quirk *quirk; +}; + +struct smiapp_module_info { + u32 manufacturer_id; + u32 model_id; + u32 revision_number_major; + u32 revision_number_minor; + + u32 module_year; + u32 module_month; + u32 module_day; + + u32 sensor_manufacturer_id; + u32 sensor_model_id; + u32 sensor_revision_number; + u32 sensor_firmware_version; + + u32 smia_version; + u32 smiapp_version; + + u32 smiapp_profile; + + char *name; + const struct smiapp_quirk *quirk; +}; + +#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = fl, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, } + +#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, } + +struct smiapp_reg_limits { + u32 addr; + char *what; +}; + +extern struct smiapp_reg_limits smiapp_reg_limits[]; + +struct smiapp_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; +}; + +#define SMIAPP_SUBDEVS 3 + +#define SMIAPP_PA_PAD_SRC 0 +#define SMIAPP_PAD_SINK 0 +#define SMIAPP_PAD_SRC 1 +#define SMIAPP_PADS 2 + +struct smiapp_binning_subtype { + u8 horizontal:4; + u8 vertical:4; +} __packed; + +struct smiapp_subdev { + struct v4l2_subdev sd; + struct media_pad pads[2]; + struct v4l2_rect sink_fmt; + struct v4l2_rect crop[2]; + struct v4l2_rect compose; /* compose on sink */ + unsigned short sink_pad; + unsigned short source_pad; + int npads; + struct smiapp_sensor *sensor; + struct v4l2_ctrl_handler ctrl_handler; +}; + +/* + * struct smiapp_sensor - Main device structure + */ +struct smiapp_sensor { + /* + * "mutex" is used to serialise access to all fields here + * except v4l2_ctrls at the end of the struct. "mutex" is also + * used to serialise access to file handle specific + * information. The exception to this rule is the power_mutex + * below. + */ + struct mutex mutex; + /* + * power_mutex is used to serialise power management related + * activities. Acquiring "mutex" at that time isn't necessary + * since there are no other users anyway. + */ + struct mutex power_mutex; + struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; + u32 ssds_used; + struct smiapp_subdev *src; + struct smiapp_subdev *binner; + struct smiapp_subdev *scaler; + struct smiapp_subdev *pixel_array; + struct smiapp_platform_data *platform_data; + struct regulator *vana; + struct clk *ext_clk; + u32 limits[SMIAPP_LIMIT_LAST]; + u8 nbinning_subtypes; + struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; + u32 mbus_frame_fmts; + const struct smiapp_csi_data_format *csi_format; + const struct smiapp_csi_data_format *internal_csi_format; + u32 default_mbus_frame_fmts; + int default_pixel_order; + + u8 binning_horizontal; + u8 binning_vertical; + + u8 scale_m; + u8 scaling_mode; + + u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ + u8 flash_capability; + u8 frame_skip; + + int power_count; + + bool streaming; + bool dev_init_done; + + u8 *nvm; /* nvm memory buffer */ + unsigned int nvm_size; /* bytes */ + + struct smiapp_module_info minfo; + + struct smiapp_pll pll; + + /* Pixel array controls */ + struct v4l2_ctrl *analog_gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *pixel_rate_parray; + /* src controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate_csi; +}; + +#define to_smiapp_subdev(_sd) \ + container_of(_sd, struct smiapp_subdev, sd) + +#define to_smiapp_sensor(_sd) \ + (to_smiapp_subdev(_sd)->sensor) + +#endif /* __SMIAPP_PRIV_H_ */ diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c new file mode 100644 index 000000000000..e9d95bda2ab1 --- /dev/null +++ b/drivers/media/i2c/sr030pc30.c @@ -0,0 +1,871 @@ +/* + * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP + * + * Copyright (C) 2010 Samsung Electronics Co., Ltd + * Author: Sylwester Nawrocki, s.nawrocki@samsung.com + * + * Based on original driver authored by Dongsoo Nathaniel Kim + * and HeungJun Kim . + * + * Based on mt9v011 Micron Digital Image Sensor driver + * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); + +#define MODULE_NAME "SR030PC30" + +/* + * Register offsets within a page + * b15..b8 - page id, b7..b0 - register address + */ +#define POWER_CTRL_REG 0x0001 +#define PAGEMODE_REG 0x03 +#define DEVICE_ID_REG 0x0004 +#define NOON010PC30_ID 0x86 +#define SR030PC30_ID 0x8C +#define VDO_CTL1_REG 0x0010 +#define SUBSAMPL_NONE_VGA 0 +#define SUBSAMPL_QVGA 0x10 +#define SUBSAMPL_QQVGA 0x20 +#define VDO_CTL2_REG 0x0011 +#define SYNC_CTL_REG 0x0012 +#define WIN_ROWH_REG 0x0020 +#define WIN_ROWL_REG 0x0021 +#define WIN_COLH_REG 0x0022 +#define WIN_COLL_REG 0x0023 +#define WIN_HEIGHTH_REG 0x0024 +#define WIN_HEIGHTL_REG 0x0025 +#define WIN_WIDTHH_REG 0x0026 +#define WIN_WIDTHL_REG 0x0027 +#define HBLANKH_REG 0x0040 +#define HBLANKL_REG 0x0041 +#define VSYNCH_REG 0x0042 +#define VSYNCL_REG 0x0043 +/* page 10 */ +#define ISP_CTL_REG(n) (0x1010 + (n)) +#define YOFS_REG 0x1040 +#define DARK_YOFS_REG 0x1041 +#define AG_ABRTH_REG 0x1050 +#define SAT_CTL_REG 0x1060 +#define BSAT_REG 0x1061 +#define RSAT_REG 0x1062 +#define AG_SAT_TH_REG 0x1063 +/* page 11 */ +#define ZLPF_CTRL_REG 0x1110 +#define ZLPF_CTRL2_REG 0x1112 +#define ZLPF_AGH_THR_REG 0x1121 +#define ZLPF_THR_REG 0x1160 +#define ZLPF_DYN_THR_REG 0x1160 +/* page 12 */ +#define YCLPF_CTL1_REG 0x1240 +#define YCLPF_CTL2_REG 0x1241 +#define YCLPF_THR_REG 0x1250 +#define BLPF_CTL_REG 0x1270 +#define BLPF_THR1_REG 0x1274 +#define BLPF_THR2_REG 0x1275 +/* page 14 - Lens Shading Compensation */ +#define LENS_CTRL_REG 0x1410 +#define LENS_XCEN_REG 0x1420 +#define LENS_YCEN_REG 0x1421 +#define LENS_R_COMP_REG 0x1422 +#define LENS_G_COMP_REG 0x1423 +#define LENS_B_COMP_REG 0x1424 +/* page 15 - Color correction */ +#define CMC_CTL_REG 0x1510 +#define CMC_OFSGH_REG 0x1514 +#define CMC_OFSGL_REG 0x1516 +#define CMC_SIGN_REG 0x1517 +/* Color correction coefficients */ +#define CMC_COEF_REG(n) (0x1530 + (n)) +/* Color correction offset coefficients */ +#define CMC_OFS_REG(n) (0x1540 + (n)) +/* page 16 - Gamma correction */ +#define GMA_CTL_REG 0x1610 +/* Gamma correction coefficients 0.14 */ +#define GMA_COEF_REG(n) (0x1630 + (n)) +/* page 20 - Auto Exposure */ +#define AE_CTL1_REG 0x2010 +#define AE_CTL2_REG 0x2011 +#define AE_FRM_CTL_REG 0x2020 +#define AE_FINE_CTL_REG(n) (0x2028 + (n)) +#define EXP_TIMEH_REG 0x2083 +#define EXP_TIMEM_REG 0x2084 +#define EXP_TIMEL_REG 0x2085 +#define EXP_MMINH_REG 0x2086 +#define EXP_MMINL_REG 0x2087 +#define EXP_MMAXH_REG 0x2088 +#define EXP_MMAXM_REG 0x2089 +#define EXP_MMAXL_REG 0x208A +/* page 22 - Auto White Balance */ +#define AWB_CTL1_REG 0x2210 +#define AWB_ENABLE 0x80 +#define AWB_CTL2_REG 0x2211 +#define MWB_ENABLE 0x01 +/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */ +#define AWB_RGAIN_REG 0x2280 +#define AWB_GGAIN_REG 0x2281 +#define AWB_BGAIN_REG 0x2282 +#define AWB_RMAX_REG 0x2283 +#define AWB_RMIN_REG 0x2284 +#define AWB_BMAX_REG 0x2285 +#define AWB_BMIN_REG 0x2286 +/* R, B gain range in bright light conditions */ +#define AWB_RMAXB_REG 0x2287 +#define AWB_RMINB_REG 0x2288 +#define AWB_BMAXB_REG 0x2289 +#define AWB_BMINB_REG 0x228A +/* manual white balance, when AWB_CTL2[0]=1 */ +#define MWB_RGAIN_REG 0x22B2 +#define MWB_BGAIN_REG 0x22B3 +/* the token to mark an array end */ +#define REG_TERM 0xFFFF + +/* Minimum and maximum exposure time in ms */ +#define EXPOS_MIN_MS 1 +#define EXPOS_MAX_MS 125 + +struct sr030pc30_info { + struct v4l2_subdev sd; + const struct sr030pc30_platform_data *pdata; + const struct sr030pc30_format *curr_fmt; + const struct sr030pc30_frmsize *curr_win; + unsigned int auto_wb:1; + unsigned int auto_exp:1; + unsigned int hflip:1; + unsigned int vflip:1; + unsigned int sleep:1; + unsigned int exposure; + u8 blue_balance; + u8 red_balance; + u8 i2c_reg_page; +}; + +struct sr030pc30_format { + enum v4l2_mbus_pixelcode code; + enum v4l2_colorspace colorspace; + u16 ispctl1_reg; +}; + +struct sr030pc30_frmsize { + u16 width; + u16 height; + int vid_ctl1; +}; + +struct i2c_regval { + u16 addr; + u16 val; +}; + +static const struct v4l2_queryctrl sr030pc30_ctrl[] = { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = EXPOS_MIN_MS, + .maximum = EXPOS_MAX_MS, + .step = 1, + .default_value = 1, + }, { + } +}; + +/* supported resolutions */ +static const struct sr030pc30_frmsize sr030pc30_sizes[] = { + { + .width = 640, + .height = 480, + .vid_ctl1 = SUBSAMPL_NONE_VGA, + }, { + .width = 320, + .height = 240, + .vid_ctl1 = SUBSAMPL_QVGA, + }, { + .width = 160, + .height = 120, + .vid_ctl1 = SUBSAMPL_QQVGA, + }, +}; + +/* supported pixel formats */ +static const struct sr030pc30_format sr030pc30_formats[] = { + { + .code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x03, + }, { + .code = V4L2_MBUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x02, + }, { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0, + }, { + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x01, + }, { + .code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x40, + }, +}; + +static const struct i2c_regval sr030pc30_base_regs[] = { + /* Window size and position within pixel matrix */ + { WIN_ROWH_REG, 0x00 }, { WIN_ROWL_REG, 0x06 }, + { WIN_COLH_REG, 0x00 }, { WIN_COLL_REG, 0x06 }, + { WIN_HEIGHTH_REG, 0x01 }, { WIN_HEIGHTL_REG, 0xE0 }, + { WIN_WIDTHH_REG, 0x02 }, { WIN_WIDTHL_REG, 0x80 }, + { HBLANKH_REG, 0x01 }, { HBLANKL_REG, 0x50 }, + { VSYNCH_REG, 0x00 }, { VSYNCL_REG, 0x14 }, + { SYNC_CTL_REG, 0 }, + /* Color corection and saturation */ + { ISP_CTL_REG(0), 0x30 }, { YOFS_REG, 0x80 }, + { DARK_YOFS_REG, 0x04 }, { AG_ABRTH_REG, 0x78 }, + { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, + { AG_SAT_TH_REG, 0xF0 }, { 0x1064, 0x80 }, + { CMC_CTL_REG, 0x03 }, { CMC_OFSGH_REG, 0x3C }, + { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x2F }, + { CMC_COEF_REG(0), 0xCB }, { CMC_OFS_REG(0), 0x87 }, + { CMC_COEF_REG(1), 0x61 }, { CMC_OFS_REG(1), 0x18 }, + { CMC_COEF_REG(2), 0x16 }, { CMC_OFS_REG(2), 0x91 }, + { CMC_COEF_REG(3), 0x23 }, { CMC_OFS_REG(3), 0x94 }, + { CMC_COEF_REG(4), 0xCE }, { CMC_OFS_REG(4), 0x9f }, + { CMC_COEF_REG(5), 0x2B }, { CMC_OFS_REG(5), 0x33 }, + { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x00 }, + { CMC_COEF_REG(7), 0x34 }, { CMC_OFS_REG(7), 0x94 }, + { CMC_COEF_REG(8), 0x75 }, { CMC_OFS_REG(8), 0x14 }, + /* Color corection coefficients */ + { GMA_CTL_REG, 0x03 }, { GMA_COEF_REG(0), 0x00 }, + { GMA_COEF_REG(1), 0x19 }, { GMA_COEF_REG(2), 0x26 }, + { GMA_COEF_REG(3), 0x3B }, { GMA_COEF_REG(4), 0x5D }, + { GMA_COEF_REG(5), 0x79 }, { GMA_COEF_REG(6), 0x8E }, + { GMA_COEF_REG(7), 0x9F }, { GMA_COEF_REG(8), 0xAF }, + { GMA_COEF_REG(9), 0xBD }, { GMA_COEF_REG(10), 0xCA }, + { GMA_COEF_REG(11), 0xDD }, { GMA_COEF_REG(12), 0xEC }, + { GMA_COEF_REG(13), 0xF7 }, { GMA_COEF_REG(14), 0xFF }, + /* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */ + { ZLPF_CTRL_REG, 0x99 }, { ZLPF_CTRL2_REG, 0x0E }, + { ZLPF_AGH_THR_REG, 0x29 }, { ZLPF_THR_REG, 0x0F }, + { ZLPF_DYN_THR_REG, 0x63 }, { YCLPF_CTL1_REG, 0x23 }, + { YCLPF_CTL2_REG, 0x3B }, { YCLPF_THR_REG, 0x05 }, + { BLPF_CTL_REG, 0x1D }, { BLPF_THR1_REG, 0x05 }, + { BLPF_THR2_REG, 0x04 }, + /* Automatic white balance */ + { AWB_CTL1_REG, 0xFB }, { AWB_CTL2_REG, 0x26 }, + { AWB_RMAX_REG, 0x54 }, { AWB_RMIN_REG, 0x2B }, + { AWB_BMAX_REG, 0x57 }, { AWB_BMIN_REG, 0x29 }, + { AWB_RMAXB_REG, 0x50 }, { AWB_RMINB_REG, 0x43 }, + { AWB_BMAXB_REG, 0x30 }, { AWB_BMINB_REG, 0x22 }, + /* Auto exposure */ + { AE_CTL1_REG, 0x8C }, { AE_CTL2_REG, 0x04 }, + { AE_FRM_CTL_REG, 0x01 }, { AE_FINE_CTL_REG(0), 0x3F }, + { AE_FINE_CTL_REG(1), 0xA3 }, { AE_FINE_CTL_REG(3), 0x34 }, + /* Lens shading compensation */ + { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, + { LENS_YCEN_REG, 0x70 }, { LENS_R_COMP_REG, 0x53 }, + { LENS_G_COMP_REG, 0x40 }, { LENS_B_COMP_REG, 0x3e }, + { REG_TERM, 0 }, +}; + +static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd) +{ + return container_of(sd, struct sr030pc30_info, sd); +} + +static inline int set_i2c_page(struct sr030pc30_info *info, + struct i2c_client *client, unsigned int reg) +{ + int ret = 0; + u32 page = reg >> 8 & 0xFF; + + if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { + ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); + if (!ret) + info->i2c_reg_page = page; + } + return ret; +} + +static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct sr030pc30_info *info = to_sr030pc30(sd); + + int ret = set_i2c_page(info, client, reg_addr); + if (!ret) + ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF); + return ret; +} + +static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct sr030pc30_info *info = to_sr030pc30(sd); + + int ret = set_i2c_page(info, client, reg_addr); + if (!ret) + ret = i2c_smbus_write_byte_data( + client, reg_addr & 0xFF, val); + return ret; +} + +static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd, + const struct i2c_regval *msg) +{ + while (msg->addr != REG_TERM) { + int ret = cam_i2c_write(sd, msg->addr, msg->val); + if (ret) + return ret; + msg++; + } + return 0; +} + +/* Device reset and sleep mode control */ +static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd, + bool reset, bool sleep) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + u8 reg = sleep ? 0xF1 : 0xF0; + int ret = 0; + + if (reset) + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); + if (!ret) { + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); + if (!ret) { + info->sleep = sleep; + if (reset) + info->i2c_reg_page = -1; + } + } + return ret; +} + +static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + /* auto anti-flicker is also enabled here */ + int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C); + if (!ret) + info->auto_exp = on; + return ret; +} + +static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + unsigned long expos = value * info->pdata->clk_rate / (8 * 1000); + + int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF); + if (!ret) { /* Turn off AE */ + info->exposure = value; + ret = sr030pc30_enable_autoexposure(sd, 0); + } + return ret; +} + +/* Automatic white balance control */ +static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F); + if (!ret) + ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B); + if (!ret) + info->auto_wb = on; + + return ret; +} + +static int sr030pc30_set_flip(struct v4l2_subdev *sd) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + s32 reg = cam_i2c_read(sd, VDO_CTL2_REG); + if (reg < 0) + return reg; + + reg &= 0x7C; + if (info->hflip) + reg |= 0x01; + if (info->vflip) + reg |= 0x02; + return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80); +} + +/* Configure resolution, color format and image flip */ +static int sr030pc30_set_params(struct v4l2_subdev *sd) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + int ret; + + if (!info->curr_win) + return -EINVAL; + + /* Configure the resolution through subsampling */ + ret = cam_i2c_write(sd, VDO_CTL1_REG, + info->curr_win->vid_ctl1); + + if (!ret && info->curr_fmt) + ret = cam_i2c_write(sd, ISP_CTL_REG(0), + info->curr_fmt->ispctl1_reg); + if (!ret) + ret = sr030pc30_set_flip(sd); + + return ret; +} + +/* Find nearest matching image pixel size. */ +static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf) +{ + unsigned int min_err = ~0; + int i = ARRAY_SIZE(sr030pc30_sizes); + const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0], + *match = NULL; + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + return 0; + } + return -EINVAL; +} + +static int sr030pc30_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) + if (qc->id == sr030pc30_ctrl[i].id) { + *qc = sr030pc30_ctrl[i]; + v4l2_dbg(1, debug, sd, "%s id: %d\n", + __func__, qc->id); + return 0; + } + + return -EINVAL; +} + +static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value) +{ + int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value); + if (!ret) + to_sr030pc30(sd)->blue_balance = value; + return ret; +} + +static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value) +{ + int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value); + if (!ret) + to_sr030pc30(sd)->red_balance = value; + return ret; +} + +static int sr030pc30_s_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) + if (ctrl->id == sr030pc30_ctrl[i].id) + break; + + if (i == ARRAY_SIZE(sr030pc30_ctrl)) + return -EINVAL; + + if (ctrl->value < sr030pc30_ctrl[i].minimum || + ctrl->value > sr030pc30_ctrl[i].maximum) + return -ERANGE; + + v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", + __func__, ctrl->id, ctrl->value); + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + sr030pc30_enable_autowhitebalance(sd, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + ret = sr030pc30_set_bluebalance(sd, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + ret = sr030pc30_set_redbalance(sd, ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + sr030pc30_enable_autoexposure(sd, + ctrl->value == V4L2_EXPOSURE_AUTO); + break; + case V4L2_CID_EXPOSURE: + ret = sr030pc30_set_exposure(sd, ctrl->value); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int sr030pc30_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id); + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = info->auto_wb; + break; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = info->blue_balance; + break; + case V4L2_CID_RED_BALANCE: + ctrl->value = info->red_balance; + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = info->auto_exp; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = info->exposure; + break; + default: + return -EINVAL; + } + return 0; +} + +static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (!code || index >= ARRAY_SIZE(sr030pc30_formats)) + return -EINVAL; + + *code = sr030pc30_formats[index].code; + return 0; +} + +static int sr030pc30_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + int ret; + + if (!mf) + return -EINVAL; + + if (!info->curr_win || !info->curr_fmt) { + ret = sr030pc30_set_params(sd); + if (ret) + return ret; + } + + mf->width = info->curr_win->width; + mf->height = info->curr_win->height; + mf->code = info->curr_fmt->code; + mf->colorspace = info->curr_fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +/* Return nearest media bus frame format. */ +static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + int i = ARRAY_SIZE(sr030pc30_formats); + + sr030pc30_try_frame_size(mf); + + while (i--) + if (mf->code == sr030pc30_formats[i].code) + break; + + mf->code = sr030pc30_formats[i].code; + + return &sr030pc30_formats[i]; +} + +/* Return nearest media bus frame format. */ +static int sr030pc30_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + if (!sd || !mf) + return -EINVAL; + + try_fmt(sd, mf); + return 0; +} + +static int sr030pc30_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + + if (!sd || !mf) + return -EINVAL; + + info->curr_fmt = try_fmt(sd, mf); + + return sr030pc30_set_params(sd); +} + +static int sr030pc30_base_config(struct v4l2_subdev *sd) +{ + struct sr030pc30_info *info = to_sr030pc30(sd); + int ret; + unsigned long expmin, expmax; + + ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs); + if (!ret) { + info->curr_fmt = &sr030pc30_formats[0]; + info->curr_win = &sr030pc30_sizes[0]; + ret = sr030pc30_set_params(sd); + } + if (!ret) + ret = sr030pc30_pwr_ctrl(sd, false, false); + + if (!ret && !info->pdata) + return ret; + + expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000); + expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000); + + v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__, + expmin, expmax); + + /* Setting up manual exposure time range */ + ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF); + if (!ret) + ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF); + + return ret; +} + +static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct sr030pc30_info *info = to_sr030pc30(sd); + const struct sr030pc30_platform_data *pdata = info->pdata; + int ret; + + if (pdata == NULL) { + WARN(1, "No platform data!\n"); + return -EINVAL; + } + + /* + * Put sensor into power sleep mode before switching off + * power and disabling MCLK. + */ + if (!on) + sr030pc30_pwr_ctrl(sd, false, true); + + /* set_power controls sensor's power and clock */ + if (pdata->set_power) { + ret = pdata->set_power(&client->dev, on); + if (ret) + return ret; + } + + if (on) { + ret = sr030pc30_base_config(sd); + } else { + ret = 0; + info->curr_win = NULL; + info->curr_fmt = NULL; + } + + return ret; +} + +static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { + .s_power = sr030pc30_s_power, + .queryctrl = sr030pc30_queryctrl, + .s_ctrl = sr030pc30_s_ctrl, + .g_ctrl = sr030pc30_g_ctrl, +}; + +static const struct v4l2_subdev_video_ops sr030pc30_video_ops = { + .g_mbus_fmt = sr030pc30_g_fmt, + .s_mbus_fmt = sr030pc30_s_fmt, + .try_mbus_fmt = sr030pc30_try_fmt, + .enum_mbus_fmt = sr030pc30_enum_fmt, +}; + +static const struct v4l2_subdev_ops sr030pc30_ops = { + .core = &sr030pc30_core_ops, + .video = &sr030pc30_video_ops, +}; + +/* + * Detect sensor type. Return 0 if SR030PC30 was detected + * or -ENODEV otherwise. + */ +static int sr030pc30_detect(struct i2c_client *client) +{ + const struct sr030pc30_platform_data *pdata + = client->dev.platform_data; + int ret; + + /* Enable sensor's power and clock */ + if (pdata->set_power) { + ret = pdata->set_power(&client->dev, 1); + if (ret) + return ret; + } + + ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); + + if (pdata->set_power) + pdata->set_power(&client->dev, 0); + + if (ret < 0) { + dev_err(&client->dev, "%s: I2C read failed\n", __func__); + return ret; + } + + return ret == SR030PC30_ID ? 0 : -ENODEV; +} + + +static int sr030pc30_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sr030pc30_info *info; + struct v4l2_subdev *sd; + const struct sr030pc30_platform_data *pdata + = client->dev.platform_data; + int ret; + + if (!pdata) { + dev_err(&client->dev, "No platform data!"); + return -EIO; + } + + ret = sr030pc30_detect(client); + if (ret) + return ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + sd = &info->sd; + strcpy(sd->name, MODULE_NAME); + info->pdata = client->dev.platform_data; + + v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops); + + info->i2c_reg_page = -1; + info->hflip = 1; + info->auto_exp = 1; + info->exposure = 30; + + return 0; +} + +static int sr030pc30_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sr030pc30_info *info = to_sr030pc30(sd); + + v4l2_device_unregister_subdev(sd); + kfree(info); + return 0; +} + +static const struct i2c_device_id sr030pc30_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sr030pc30_id); + + +static struct i2c_driver sr030pc30_i2c_driver = { + .driver = { + .name = MODULE_NAME + }, + .probe = sr030pc30_probe, + .remove = sr030pc30_remove, + .id_table = sr030pc30_id, +}; + +module_i2c_driver(sr030pc30_i2c_driver); + +MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tcm825x.c b/drivers/media/i2c/tcm825x.c new file mode 100644 index 000000000000..9252529fc5dd --- /dev/null +++ b/drivers/media/i2c/tcm825x.c @@ -0,0 +1,937 @@ +/* + * drivers/media/i2c/tcm825x.c + * + * TCM825X camera sensor driver. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from David Cohen + * + * This driver was based on ov9640 sensor driver from MontaVista + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include + +#include "tcm825x.h" + +/* + * The sensor has two fps modes: the lower one just gives half the fps + * at the same xclk than the high one. + */ +#define MAX_FPS 30 +#define MIN_FPS 8 +#define MAX_HALF_FPS (MAX_FPS / 2) +#define HIGH_FPS_MODE_LOWER_LIMIT 14 +#define DEFAULT_FPS MAX_HALF_FPS + +struct tcm825x_sensor { + const struct tcm825x_platform_data *platform_data; + struct v4l2_int_device *v4l2_int_device; + struct i2c_client *i2c_client; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; +}; + +/* list of image formats supported by TCM825X sensor */ +static const struct v4l2_fmtdesc tcm825x_formats[] = { + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * We interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, +}; + +#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats) + +/* + * TCM825X register configuration for all combinations of pixel format and + * image size + */ +static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; +static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; +static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; +static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; +static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; +static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; + +static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; +static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; + +/* Our own specific controls */ +#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE +#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1 +#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2 +#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3 +#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4 +#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME + +/* Video controls */ +static struct vcontrol { + struct v4l2_queryctrl qc; + u16 reg; + u16 start_bit; +} video_control[] = { + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 63, + .step = 1, + }, + .reg = TCM825X_AG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 255, + .step = 1, + }, + .reg = TCM825X_MRG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 255, + .step = 1, + }, + .reg = TCM825X_MBG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_AWBSW, + .start_bit = 7, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure Time", + .minimum = 0, + .maximum = 0x1fff, + .step = 1, + }, + .reg = TCM825X_ESRSPD_U, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror Image", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_H_INV, + .start_bit = 6, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_V_INV, + .start_bit = 7, + }, + /* Private controls */ + { + { + .id = V4L2_CID_ALC, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Luminance Control", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_ALCSW, + .start_bit = 7, + }, + { + { + .id = V4L2_CID_H_EDGE_EN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Horizontal Edge Enhancement", + .minimum = 0, + .maximum = 0xff, + .step = 1, + }, + .reg = TCM825X_HDTG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_V_EDGE_EN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Vertical Edge Enhancement", + .minimum = 0, + .maximum = 0xff, + .step = 1, + }, + .reg = TCM825X_VDTG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_LENS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Lens Shading Compensation", + .minimum = 0, + .maximum = 0x3f, + .step = 1, + }, + .reg = TCM825X_LENS, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_MAX_EXPOSURE_TIME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Maximum Exposure Time", + .minimum = 0, + .maximum = 0x3, + .step = 1, + }, + .reg = TCM825X_ESRLIM, + .start_bit = 5, + }, +}; + + +static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = +{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; + +static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = +{ &yuv422, &rgb565 }; + +/* + * Read a value from a register in an TCM825X sensor device. The value is + * returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_read_reg(struct i2c_client *client, int reg) +{ + int err; + struct i2c_msg msg[2]; + u8 reg_buf, data_buf = 0; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ®_buf; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &data_buf; + + reg_buf = reg; + + err = i2c_transfer(client->adapter, msg, 2); + if (err < 0) + return err; + return data_buf; +} + +/* + * Write a value to a register in an TCM825X sensor device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + data[0] = reg; + data[1] = val; + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) + return 0; + return err; +} + +static int __tcm825x_write_reg_mask(struct i2c_client *client, + u8 reg, u8 val, u8 mask) +{ + int rc; + + /* need to do read - modify - write */ + rc = tcm825x_read_reg(client, reg); + if (rc < 0) + return rc; + + rc &= (~mask); /* Clear the masked bits */ + val &= mask; /* Enforce mask on value */ + val |= rc; + + /* write the new value to the register */ + rc = tcm825x_write_reg(client, reg, val); + if (rc) + return rc; + + return 0; +} + +#define tcm825x_write_reg_mask(client, regmask, val) \ + __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \ + TCM825X_MASK((regmask))) + + +/* + * Initialize a list of TCM825X registers. + * The list of registers is terminated by the pair of values + * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_write_default_regs(struct i2c_client *client, + const struct tcm825x_reg *reglist) +{ + int err; + const struct tcm825x_reg *next = reglist; + + while (!((next->reg == TCM825X_REG_TERM) + && (next->val == TCM825X_VAL_TERM))) { + err = tcm825x_write_reg(client, next->reg, next->val); + if (err) { + dev_err(&client->dev, "register writing failed\n"); + return err; + } + next++; + } + + return 0; +} + +static struct vcontrol *find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return NULL; + + for (i = 0; i < ARRAY_SIZE(video_control); i++) + if (video_control[i].qc.id == id) + return &video_control[i]; + + return NULL; +} + +/* + * Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + */ +static enum image_size tcm825x_find_size(struct v4l2_int_device *s, + unsigned int width, + unsigned int height) +{ + enum image_size isize; + unsigned long pixels = width * height; + struct tcm825x_sensor *sensor = s->priv; + + for (isize = subQCIF; isize < VGA; isize++) { + if (tcm825x_sizes[isize + 1].height + * tcm825x_sizes[isize + 1].width > pixels) { + dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); + + return isize; + } + } + + dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); + + return VGA; +} + +/* + * Configure the TCM825X for current image size, pixel format, and + * frame period. fper is the frame period (in seconds) expressed as a + * fraction. Returns zero if successful, or non-zero otherwise. The + * actual frame period is returned in fper. + */ +static int tcm825x_configure(struct v4l2_int_device *s) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &sensor->pix; + enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); + struct v4l2_fract *fper = &sensor->timeperframe; + enum pixel_format pfmt; + int err; + u32 tgt_fps; + u8 val; + + /* common register initialization */ + err = tcm825x_write_default_regs( + sensor->i2c_client, sensor->platform_data->default_regs()); + if (err) + return err; + + /* configure image size */ + val = tcm825x_siz_reg[isize]->val; + dev_dbg(&sensor->i2c_client->dev, + "configuring image size %d\n", isize); + err = tcm825x_write_reg_mask(sensor->i2c_client, + tcm825x_siz_reg[isize]->reg, val); + if (err) + return err; + + /* configure pixel format */ + switch (pix->pixelformat) { + default: + case V4L2_PIX_FMT_RGB565: + pfmt = RGB565; + break; + case V4L2_PIX_FMT_UYVY: + pfmt = YUV422; + break; + } + + dev_dbg(&sensor->i2c_client->dev, + "configuring pixel format %d\n", pfmt); + val = tcm825x_fmt_reg[pfmt]->val; + + err = tcm825x_write_reg_mask(sensor->i2c_client, + tcm825x_fmt_reg[pfmt]->reg, val); + if (err) + return err; + + /* + * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be + * set. Frame rate will be halved from the normal. + */ + tgt_fps = fper->denominator / fper->numerator; + if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { + val = tcm825x_read_reg(sensor->i2c_client, 0x02); + val |= 0x80; + tcm825x_write_reg(sensor->i2c_client, 0x02, val); + } + + return 0; +} + +static int ioctl_queryctrl(struct v4l2_int_device *s, + struct v4l2_queryctrl *qc) +{ + struct vcontrol *control; + + control = find_vctrl(qc->id); + + if (control == NULL) + return -EINVAL; + + *qc = control->qc; + + return 0; +} + +static int ioctl_g_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tcm825x_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + int val, r; + struct vcontrol *lvc; + + /* exposure time is special, spread across 2 registers */ + if (vc->id == V4L2_CID_EXPOSURE) { + int val_lower, val_upper; + + val_upper = tcm825x_read_reg(client, + TCM825X_ADDR(TCM825X_ESRSPD_U)); + if (val_upper < 0) + return val_upper; + val_lower = tcm825x_read_reg(client, + TCM825X_ADDR(TCM825X_ESRSPD_L)); + if (val_lower < 0) + return val_lower; + + vc->value = ((val_upper & 0x1f) << 8) | (val_lower); + return 0; + } + + lvc = find_vctrl(vc->id); + if (lvc == NULL) + return -EINVAL; + + r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); + if (r < 0) + return r; + val = r & TCM825X_MASK(lvc->reg); + val >>= lvc->start_bit; + + if (val < 0) + return val; + + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + + vc->value = val; + return 0; +} + +static int ioctl_s_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tcm825x_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + struct vcontrol *lvc; + int val = vc->value; + + /* exposure time is special, spread across 2 registers */ + if (vc->id == V4L2_CID_EXPOSURE) { + int val_lower, val_upper; + val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); + val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); + + if (tcm825x_write_reg_mask(client, + TCM825X_ESRSPD_U, val_upper)) + return -EIO; + + if (tcm825x_write_reg_mask(client, + TCM825X_ESRSPD_L, val_lower)) + return -EIO; + + return 0; + } + + lvc = find_vctrl(vc->id); + if (lvc == NULL) + return -EINVAL; + + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + + val = val << lvc->start_bit; + if (tcm825x_write_reg_mask(client, lvc->reg, val)) + return -EIO; + + return 0; +} + +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= TCM825X_NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + fmt->flags = tcm825x_formats[index].flags; + strlcpy(fmt->description, tcm825x_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = tcm825x_formats[index].pixelformat; + + return 0; +} + +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + enum image_size isize; + int ifmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + + isize = tcm825x_find_size(s, pix->width, pix->height); + dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", + isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); + + pix->width = tcm825x_sizes[isize].width; + pix->height = tcm825x_sizes[isize].height; + + for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) + if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) + break; + + if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) + ifmt = 0; /* Default = YUV 4:2:2 */ + + pix->pixelformat = tcm825x_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", + pix->pixelformat); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + break; + case V4L2_PIX_FMT_RGB565: + pix->colorspace = V4L2_COLORSPACE_SRGB; + break; + } + + return 0; +} + +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + int rval; + + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + rval = tcm825x_configure(s); + + sensor->pix = *pix; + + return rval; +} + +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + + f->fmt.pix = sensor->pix; + + return 0; +} + +static int ioctl_g_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = sensor->timeperframe; + + return 0; +} + +static int ioctl_s_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps; /* target frames per secound */ + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if ((timeperframe->numerator == 0) + || (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + sensor->timeperframe = *timeperframe; + + rval = tcm825x_configure(s); + + return rval; +} + +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct tcm825x_sensor *sensor = s->priv; + + return sensor->platform_data->power_set(on); +} + +/* + * Given the image capture format in pix, the nominal frame period in + * timeperframe, calculate the required xclk frequency. + * + * TCM825X input frequency characteristics are: + * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz + */ + +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &sensor->timeperframe; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + int rval; + + rval = sensor->platform_data->ifparm(p); + if (rval) + return rval; + + tgt_fps = timeperframe->denominator / timeperframe->numerator; + + tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? + (2457 * tgt_fps) / MAX_HALF_FPS : + (2457 * tgt_fps) / MAX_FPS; + tgt_xclk *= 10000; + + tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); + + p->u.bt656.clock_curr = tgt_xclk; + + return 0; +} + +static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) +{ + struct tcm825x_sensor *sensor = s->priv; + + return sensor->platform_data->needs_reset(s, buf, &sensor->pix); +} + +static int ioctl_reset(struct v4l2_int_device *s) +{ + return -EBUSY; +} + +static int ioctl_init(struct v4l2_int_device *s) +{ + return tcm825x_configure(s); +} + +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct tcm825x_sensor *sensor = s->priv; + int r; + + r = tcm825x_read_reg(sensor->i2c_client, 0x01); + if (r < 0) + return r; + if (r == 0) { + dev_err(&sensor->i2c_client->dev, "device not detected\n"); + return -EIO; + } + return 0; +} + +static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { + { vidioc_int_dev_init_num, + (v4l2_int_ioctl_func *)ioctl_dev_init }, + { vidioc_int_dev_exit_num, + (v4l2_int_ioctl_func *)ioctl_dev_exit }, + { vidioc_int_s_power_num, + (v4l2_int_ioctl_func *)ioctl_s_power }, + { vidioc_int_g_ifparm_num, + (v4l2_int_ioctl_func *)ioctl_g_ifparm }, + { vidioc_int_g_needs_reset_num, + (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, + { vidioc_int_reset_num, + (v4l2_int_ioctl_func *)ioctl_reset }, + { vidioc_int_init_num, + (v4l2_int_ioctl_func *)ioctl_init }, + { vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, + { vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, + { vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, + { vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, + { vidioc_int_g_parm_num, + (v4l2_int_ioctl_func *)ioctl_g_parm }, + { vidioc_int_s_parm_num, + (v4l2_int_ioctl_func *)ioctl_s_parm }, + { vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *)ioctl_queryctrl }, + { vidioc_int_g_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { vidioc_int_s_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_s_ctrl }, +}; + +static struct v4l2_int_slave tcm825x_slave = { + .ioctls = tcm825x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), +}; + +static struct tcm825x_sensor tcm825x; + +static struct v4l2_int_device tcm825x_int_device = { + .module = THIS_MODULE, + .name = TCM825X_NAME, + .priv = &tcm825x, + .type = v4l2_int_type_slave, + .u = { + .slave = &tcm825x_slave, + }, +}; + +static int tcm825x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct tcm825x_sensor *sensor = &tcm825x; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + sensor->platform_data = client->dev.platform_data; + + if (sensor->platform_data == NULL + || !sensor->platform_data->is_okay()) + return -ENODEV; + + sensor->v4l2_int_device = &tcm825x_int_device; + + sensor->i2c_client = client; + i2c_set_clientdata(client, sensor); + + /* Make the default capture format QVGA RGB565 */ + sensor->pix.width = tcm825x_sizes[QVGA].width; + sensor->pix.height = tcm825x_sizes[QVGA].height; + sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; + + return v4l2_int_device_register(sensor->v4l2_int_device); +} + +static int tcm825x_remove(struct i2c_client *client) +{ + struct tcm825x_sensor *sensor = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(sensor->v4l2_int_device); + + return 0; +} + +static const struct i2c_device_id tcm825x_id[] = { + { "tcm825x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tcm825x_id); + +static struct i2c_driver tcm825x_i2c_driver = { + .driver = { + .name = TCM825X_NAME, + }, + .probe = tcm825x_probe, + .remove = tcm825x_remove, + .id_table = tcm825x_id, +}; + +static struct tcm825x_sensor tcm825x = { + .timeperframe = { + .numerator = 1, + .denominator = DEFAULT_FPS, + }, +}; + +static int __init tcm825x_init(void) +{ + int rval; + + rval = i2c_add_driver(&tcm825x_i2c_driver); + if (rval) + printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", + __func__); + + return rval; +} + +static void __exit tcm825x_exit(void) +{ + i2c_del_driver(&tcm825x_i2c_driver); +} + +/* + * FIXME: Menelaus isn't ready (?) at module_init stage, so use + * late_initcall for now. + */ +late_initcall(tcm825x_init); +module_exit(tcm825x_exit); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("TCM825x camera sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tcm825x.h b/drivers/media/i2c/tcm825x.h new file mode 100644 index 000000000000..8ebab953963f --- /dev/null +++ b/drivers/media/i2c/tcm825x.h @@ -0,0 +1,200 @@ +/* + * drivers/media/i2c/tcm825x.h + * + * Register definitions for the TCM825X CameraChip. + * + * Author: David Cohen (david.cohen@indt.org.br) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * This file was based on ov9640.h from MontaVista + */ + +#ifndef TCM825X_H +#define TCM825X_H + +#include + +#include + +#define TCM825X_NAME "tcm825x" + +#define TCM825X_MASK(x) x & 0x00ff +#define TCM825X_ADDR(x) (x & 0xff00) >> 8 + +/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ +#define TCM825X_I2C_ADDR 0x3d + +/* + * define register offsets for the TCM825X sensor chip + * OFFSET(8 bits) + MASK(8 bits) + * MASK bit 4 and 3 are used when the register uses more than one address + */ +#define TCM825X_FPS 0x0280 +#define TCM825X_ACF 0x0240 +#define TCM825X_DOUTBUF 0x020C +#define TCM825X_DCLKP 0x0202 +#define TCM825X_ACFDET 0x0201 +#define TCM825X_DOUTSW 0x0380 +#define TCM825X_DATAHZ 0x0340 +#define TCM825X_PICSIZ 0x033c +#define TCM825X_PICFMT 0x0302 +#define TCM825X_V_INV 0x0480 +#define TCM825X_H_INV 0x0440 +#define TCM825X_ESRLSW 0x0430 +#define TCM825X_V_LENGTH 0x040F +#define TCM825X_ALCSW 0x0580 +#define TCM825X_ESRLIM 0x0560 +#define TCM825X_ESRSPD_U 0x051F +#define TCM825X_ESRSPD_L 0x06FF +#define TCM825X_AG 0x07FF +#define TCM825X_ESRSPD2 0x06FF +#define TCM825X_ALCMODE 0x0830 +#define TCM825X_ALCH 0x080F +#define TCM825X_ALCL 0x09FF +#define TCM825X_AWBSW 0x0A80 +#define TCM825X_MRG 0x0BFF +#define TCM825X_MBG 0x0CFF +#define TCM825X_GAMSW 0x0D80 +#define TCM825X_HDTG 0x0EFF +#define TCM825X_VDTG 0x0FFF +#define TCM825X_HDTCORE 0x10F0 +#define TCM825X_VDTCORE 0x100F +#define TCM825X_CONT 0x11FF +#define TCM825X_BRIGHT 0x12FF +#define TCM825X_VHUE 0x137F +#define TCM825X_UHUE 0x147F +#define TCM825X_VGAIN 0x153F +#define TCM825X_UGAIN 0x163F +#define TCM825X_UVCORE 0x170F +#define TCM825X_SATU 0x187F +#define TCM825X_MHMODE 0x1980 +#define TCM825X_MHLPFSEL 0x1940 +#define TCM825X_YMODE 0x1930 +#define TCM825X_MIXHG 0x1907 +#define TCM825X_LENS 0x1A3F +#define TCM825X_AGLIM 0x1BE0 +#define TCM825X_LENSRPOL 0x1B10 +#define TCM825X_LENSRGAIN 0x1B0F +#define TCM825X_ES100S 0x1CFF +#define TCM825X_ES120S 0x1DFF +#define TCM825X_DMASK 0x1EC0 +#define TCM825X_CODESW 0x1E20 +#define TCM825X_CODESEL 0x1E10 +#define TCM825X_TESPIC 0x1E04 +#define TCM825X_PICSEL 0x1E03 +#define TCM825X_HNUM 0x20FF +#define TCM825X_VOUTPH 0x287F +#define TCM825X_ESROUT 0x327F +#define TCM825X_ESROUT2 0x33FF +#define TCM825X_AGOUT 0x34FF +#define TCM825X_DGOUT 0x353F +#define TCM825X_AGSLOW1 0x39C0 +#define TCM825X_FLLSMODE 0x3930 +#define TCM825X_FLLSLIM 0x390F +#define TCM825X_DETSEL 0x3AF0 +#define TCM825X_ACDETNC 0x3A0F +#define TCM825X_AGSLOW2 0x3BC0 +#define TCM825X_DG 0x3B3F +#define TCM825X_REJHLEV 0x3CFF +#define TCM825X_ALCLOCK 0x3D80 +#define TCM825X_FPSLNKSW 0x3D40 +#define TCM825X_ALCSPD 0x3D30 +#define TCM825X_REJH 0x3D03 +#define TCM825X_SHESRSW 0x3E80 +#define TCM825X_ESLIMSEL 0x3E40 +#define TCM825X_SHESRSPD 0x3E30 +#define TCM825X_ELSTEP 0x3E0C +#define TCM825X_ELSTART 0x3E03 +#define TCM825X_AGMIN 0x3FFF +#define TCM825X_PREGRG 0x423F +#define TCM825X_PREGBG 0x433F +#define TCM825X_PRERG 0x443F +#define TCM825X_PREBG 0x453F +#define TCM825X_MSKBR 0x477F +#define TCM825X_MSKGR 0x487F +#define TCM825X_MSKRB 0x497F +#define TCM825X_MSKGB 0x4A7F +#define TCM825X_MSKRG 0x4B7F +#define TCM825X_MSKBG 0x4C7F +#define TCM825X_HDTCSW 0x4D80 +#define TCM825X_VDTCSW 0x4D40 +#define TCM825X_DTCYL 0x4D3F +#define TCM825X_HDTPSW 0x4E80 +#define TCM825X_VDTPSW 0x4E40 +#define TCM825X_DTCGAIN 0x4E3F +#define TCM825X_DTLLIMSW 0x4F10 +#define TCM825X_DTLYLIM 0x4F0F +#define TCM825X_YLCUTLMSK 0x5080 +#define TCM825X_YLCUTL 0x503F +#define TCM825X_YLCUTHMSK 0x5180 +#define TCM825X_YLCUTH 0x513F +#define TCM825X_UVSKNC 0x527F +#define TCM825X_UVLJ 0x537F +#define TCM825X_WBGMIN 0x54FF +#define TCM825X_WBGMAX 0x55FF +#define TCM825X_WBSPDUP 0x5603 +#define TCM825X_ALLAREA 0x5820 +#define TCM825X_WBLOCK 0x5810 +#define TCM825X_WB2SP 0x580F +#define TCM825X_KIZUSW 0x5920 +#define TCM825X_PBRSW 0x5910 +#define TCM825X_ABCSW 0x5903 +#define TCM825X_PBDLV 0x5AFF +#define TCM825X_PBC1LV 0x5BFF + +#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1) + +#define TCM825X_BYTES_PER_PIXEL 2 + +#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */ +#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */ + +/* define a structure for tcm825x register initialization values */ +struct tcm825x_reg { + u8 val; + u16 reg; +}; + +enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; +enum pixel_format { YUV422 = 0, RGB565 }; +#define NUM_IMAGE_SIZES 6 +#define NUM_PIXEL_FORMATS 2 + +#define TCM825X_XCLK_MIN 11900000 +#define TCM825X_XCLK_MAX 25000000 + +struct capture_size { + unsigned long width; + unsigned long height; +}; + +struct tcm825x_platform_data { + /* Is the sensor usable? Doesn't yet mean it's there, but you + * can try! */ + int (*is_okay)(void); + /* Set power state, zero is off, non-zero is on. */ + int (*power_set)(int power); + /* Default registers written after power-on or reset. */ + const struct tcm825x_reg *(*default_regs)(void); + int (*needs_reset)(struct v4l2_int_device *s, void *buf, + struct v4l2_pix_format *fmt); + int (*ifparm)(struct v4l2_ifparm *p); + int (*is_upside_down)(void); +}; + +/* Array of image sizes supported by TCM825X. These must be ordered from + * smallest image size to largest. + */ +static const struct capture_size tcm825x_sizes[] = { + { 128, 96 }, /* subQCIF */ + { 160, 120 }, /* QQVGA */ + { 176, 144 }, /* QCIF */ + { 320, 240 }, /* QVGA */ + { 352, 288 }, /* CIF */ + { 640, 480 }, /* VGA */ +}; + +#endif /* ifndef TCM825X_H */ diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c new file mode 100644 index 000000000000..f7707e65761e --- /dev/null +++ b/drivers/media/i2c/tda7432.c @@ -0,0 +1,485 @@ +/* + * For the STS-Thompson TDA7432 audio processor chip + * + * Handles audio functions: volume, balance, tone, loudness + * This driver will not complain if used with any + * other i2c device with the same address. + * + * Muting and tone control by Jonathan Isom + * + * Copyright (c) 2000 Eric Sandeen + * Copyright (c) 2006 Mauro Carvalho Chehab + * This code is placed under the terms of the GNU General Public License + * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Which was based on tda8425.c by Greg Alexander (c) 1998 + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * set to 2 if you'd like to be inundated with debug messages + * + * loudness - set between 0 and 15 for varying degrees of loudness effect + * + * maxvol - set maximium volume to +20db (1), default is 0db(0) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef VIDEO_AUDIO_BALANCE +# define VIDEO_AUDIO_BALANCE 32 +#endif + +MODULE_AUTHOR("Eric Sandeen "); +MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip"); +MODULE_LICENSE("GPL"); + +static int maxvol; +static int loudness; /* disable loudness by default */ +static int debug; /* insmod parameter */ +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0)."); +module_param(loudness, int, S_IRUGO); +MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0)."); +module_param(maxvol, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0)."); + + +/* Structure of address and subaddresses for the tda7432 */ + +struct tda7432 { + struct v4l2_subdev sd; + int addr; + int input; + int volume; + int muted; + int bass, treble; + int lf, lr, rf, rr; + int loud; +}; + +static inline struct tda7432 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tda7432, sd); +} + +/* The TDA7432 is made by STS-Thompson + * http://www.st.com + * http://us.st.com/stonline/books/pdf/docs/4056.pdf + * + * TDA7432: I2C-bus controlled basic audio processor + * + * The TDA7432 controls basic audio functions like volume, balance, + * and tone control (including loudness). It also has four channel + * output (for front and rear). Since most vidcap cards probably + * don't have 4 channel output, this driver will set front & rear + * together (no independent control). + */ + + /* Subaddresses for TDA7432 */ + +#define TDA7432_IN 0x00 /* Input select */ +#define TDA7432_VL 0x01 /* Volume */ +#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */ +#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */ +#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */ +#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */ +#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */ +#define TDA7432_LD 0x07 /* Loudness */ + + + /* Masks for bits in TDA7432 subaddresses */ + +/* Many of these not used - just for documentation */ + +/* Subaddress 0x00 - Input selection and bass control */ + +/* Bits 0,1,2 control input: + * 0x00 - Stereo input + * 0x02 - Mono input + * 0x03 - Mute (Using Attenuators Plays better with modules) + * Mono probably isn't used - I'm guessing only the stereo + * input is connected on most cards, so we'll set it to stereo. + * + * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut + * Bit 4 controls bass range: 0/1 is extended/standard bass range + * + * Highest 3 bits not used + */ + +#define TDA7432_STEREO_IN 0 +#define TDA7432_MONO_IN 2 /* Probably won't be used */ +#define TDA7432_BASS_SYM 1 << 3 +#define TDA7432_BASS_NORM 1 << 4 + +/* Subaddress 0x01 - Volume */ + +/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps + * Recommended maximum is +20 dB + * + * +32dB: 0x00 + * +20dB: 0x0c + * 0dB: 0x20 + * -79dB: 0x6f + * + * MSB (bit 7) controls loudness: 1/0 is loudness on/off + */ + +#define TDA7432_VOL_0DB 0x20 +#define TDA7432_LD_ON 1 << 7 + + +/* Subaddress 0x02 - Tone control */ + +/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB + * 0x0 is 14dB, 0x7 is 0dB + * + * Bit 3 controls treble attenuation/gain (sign) + * 1 = gain (+) + * 0 = attenuation (-) + * + * Bits 4,5,6 control absolute bass gain from 0dB to 14dB + * (This is only true for normal base range, set in 0x00) + * 0x0 << 4 is 14dB, 0x7 is 0dB + * + * Bit 7 controls bass attenuation/gain (sign) + * 1 << 7 = gain (+) + * 0 << 7 = attenuation (-) + * + * Example: + * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble + */ + +#define TDA7432_TREBLE_0DB 0xf +#define TDA7432_TREBLE 7 +#define TDA7432_TREBLE_GAIN 1 << 3 +#define TDA7432_BASS_0DB 0xf +#define TDA7432_BASS 7 << 4 +#define TDA7432_BASS_GAIN 1 << 7 + + +/* Subaddress 0x03 - Left Front attenuation */ +/* Subaddress 0x04 - Left Rear attenuation */ +/* Subaddress 0x05 - Right Front attenuation */ +/* Subaddress 0x06 - Right Rear attenuation */ + +/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB + * in 1.5dB steps. + * + * 0x00 is 0dB + * 0x1f is -37.5dB + * + * Bit 5 mutes that channel when set (1 = mute, 0 = unmute) + * We'll use the mute on the input, though (above) + * Bits 6,7 unused + */ + +#define TDA7432_ATTEN_0DB 0x00 +#define TDA7432_MUTE 0x1 << 5 + + +/* Subaddress 0x07 - Loudness Control */ + +/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps + * when bit 4 is NOT set + * + * 0x0 is 0dB + * 0xf is -15dB + * + * If bit 4 is set, then there is a flat attenuation according to + * the lower 4 bits, as above. + * + * Bits 5,6,7 unused + */ + + + +/* Begin code */ + +static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char buffer[2]; + + v4l2_dbg(2, debug, sd, "In tda7432_write\n"); + v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client, buffer, 2)) { + v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +static int tda7432_set(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tda7432 *t = to_state(sd); + unsigned char buf[16]; + + v4l2_dbg(1, debug, sd, + "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + t->input, t->volume, t->bass, t->treble, t->lf, t->lr, + t->rf, t->rr, t->loud); + buf[0] = TDA7432_IN; + buf[1] = t->input; + buf[2] = t->volume; + buf[3] = t->bass; + buf[4] = t->treble; + buf[5] = t->lf; + buf[6] = t->lr; + buf[7] = t->rf; + buf[8] = t->rr; + buf[9] = t->loud; + if (10 != i2c_master_send(client, buf, 10)) { + v4l2_err(sd, "I/O error, trying tda7432_set\n"); + return -1; + } + + return 0; +} + +static void do_tda7432_init(struct v4l2_subdev *sd) +{ + struct tda7432 *t = to_state(sd); + + v4l2_dbg(2, debug, sd, "In tda7432_init\n"); + + t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ + TDA7432_BASS_SYM | /* Symmetric bass cut */ + TDA7432_BASS_NORM; /* Normal bass range */ + t->volume = 0x3b ; /* -27dB Volume */ + if (loudness) /* Turn loudness on? */ + t->volume |= TDA7432_LD_ON; + t->muted = 1; + t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */ + t->bass = TDA7432_BASS_0DB; /* 0dB Bass */ + t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->loud = loudness; /* insmod parameter */ + + tda7432_set(sd); +} + +static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tda7432 *t = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=t->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (!maxvol){ /* max +20db */ + ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630; + } else { /* max 0db */ + ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829; + } + return 0; + case V4L2_CID_AUDIO_BALANCE: + { + if ( (t->lf) < (t->rf) ) + /* right is attenuated, balance shifted left */ + ctrl->value = (32768 - 1057*(t->rf)); + else + /* left is attenuated, balance shifted right */ + ctrl->value = (32768 + 1057*(t->lf)); + return 0; + } + case V4L2_CID_AUDIO_BASS: + { + /* Bass/treble 4 bits each */ + int bass=t->bass; + if(bass >= 0x8) + bass = ~(bass - 0x8) & 0xf; + ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass); + return 0; + } + case V4L2_CID_AUDIO_TREBLE: + { + int treble=t->treble; + if(treble >= 0x8) + treble = ~(treble - 0x8) & 0xf; + ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble); + return 0; + } + } + return -EINVAL; +} + +static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tda7432 *t = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + t->muted=ctrl->value; + break; + case V4L2_CID_AUDIO_VOLUME: + if(!maxvol){ /* max +20db */ + t->volume = 0x6f - ((ctrl->value)/630); + } else { /* max 0db */ + t->volume = 0x6f - ((ctrl->value)/829); + } + if (loudness) /* Turn on the loudness bit */ + t->volume |= TDA7432_LD_ON; + + tda7432_write(sd, TDA7432_VL, t->volume); + return 0; + case V4L2_CID_AUDIO_BALANCE: + if (ctrl->value < 32768) { + /* shifted to left, attenuate right */ + t->rr = (32768 - ctrl->value)/1057; + t->rf = t->rr; + t->lr = TDA7432_ATTEN_0DB; + t->lf = TDA7432_ATTEN_0DB; + } else if(ctrl->value > 32769) { + /* shifted to right, attenuate left */ + t->lf = (ctrl->value - 32768)/1057; + t->lr = t->lf; + t->rr = TDA7432_ATTEN_0DB; + t->rf = TDA7432_ATTEN_0DB; + } else { + /* centered */ + t->rr = TDA7432_ATTEN_0DB; + t->rf = TDA7432_ATTEN_0DB; + t->lf = TDA7432_ATTEN_0DB; + t->lr = TDA7432_ATTEN_0DB; + } + break; + case V4L2_CID_AUDIO_BASS: + t->bass = ctrl->value >> 12; + if(t->bass>= 0x8) + t->bass = (~t->bass & 0xf) + 0x8 ; + + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); + return 0; + case V4L2_CID_AUDIO_TREBLE: + t->treble= ctrl->value >> 12; + if(t->treble>= 0x8) + t->treble = (~t->treble & 0xf) + 0x8 ; + + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); + return 0; + default: + return -EINVAL; + } + + /* Used for both mute and balance changes */ + if (t->muted) + { + /* Mute & update balance*/ + tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); + } else { + tda7432_write(sd, TDA7432_LF, t->lf); + tda7432_write(sd, TDA7432_LR, t->lr); + tda7432_write(sd, TDA7432_RF, t->rf); + tda7432_write(sd, TDA7432_RR, t->rr); + } + return 0; +} + +static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + } + return -EINVAL; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda7432_core_ops = { + .queryctrl = tda7432_queryctrl, + .g_ctrl = tda7432_g_ctrl, + .s_ctrl = tda7432_s_ctrl, +}; + +static const struct v4l2_subdev_ops tda7432_ops = { + .core = &tda7432_core_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda7432_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tda7432 *t; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &tda7432_ops); + if (loudness < 0 || loudness > 15) { + v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); + if (loudness < 0) + loudness = 0; + if (loudness > 15) + loudness = 15; + } + + do_tda7432_init(sd); + return 0; +} + +static int tda7432_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + do_tda7432_init(sd); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id tda7432_id[] = { + { "tda7432", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda7432_id); + +static struct i2c_driver tda7432_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tda7432", + }, + .probe = tda7432_probe, + .remove = tda7432_remove, + .id_table = tda7432_id, +}; + +module_i2c_driver(tda7432_driver); diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c new file mode 100644 index 000000000000..3d7ddd93282d --- /dev/null +++ b/drivers/media/i2c/tda9840.c @@ -0,0 +1,224 @@ + /* + tda9840 - i2c-driver for the tda9840 by SGS Thomson + + Copyright (C) 1998-2003 Michael Hunold + Copyright (C) 2008 Hans Verkuil + + The tda9840 is a stereo/dual sound processor with digital + identification. It can be found at address 0x84 on the i2c-bus. + + For detailed informations download the specifications directly + from SGS Thomson at http://www.st.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("tda9840 driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define SWITCH 0x00 +#define LEVEL_ADJUST 0x02 +#define STEREO_ADJUST 0x03 +#define TEST 0x04 + +#define TDA9840_SET_MUTE 0x00 +#define TDA9840_SET_MONO 0x10 +#define TDA9840_SET_STEREO 0x2a +#define TDA9840_SET_LANG1 0x12 +#define TDA9840_SET_LANG2 0x1e +#define TDA9840_SET_BOTH 0x1a +#define TDA9840_SET_BOTH_R 0x16 +#define TDA9840_SET_EXTERNAL 0x7a + + +static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (i2c_smbus_write_byte_data(client, reg, val)) + v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n", + val, reg); +} + +static int tda9840_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte; + + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l2_dbg(1, debug, sd, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l2_dbg(1, debug, sd, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; + } + + v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + return byte & 0x60; +} + +static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + int stat = tda9840_status(sd); + int byte; + + if (t->index) + return -EINVAL; + + stat = stat < 0 ? 0 : stat; + if (stat == 0 || stat == 0x60) /* mono input */ + byte = TDA9840_SET_MONO; + else if (stat == 0x40) /* stereo input */ + byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? + TDA9840_SET_MONO : TDA9840_SET_STEREO; + else { /* bilingual */ + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_LANG1; + break; + } + } + v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); + tda9840_write(sd, SWITCH, byte); + return 0; +} + +static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + int stat = tda9840_status(sd); + + if (stat < 0) + return stat; + + t->rxsubchans = V4L2_TUNER_SUB_MONO; + + switch (stat & 0x60) { + case 0x00: + t->rxsubchans = V4L2_TUNER_SUB_MONO; + break; + case 0x20: + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x40: + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + break; + default: /* Incorrect detect */ + t->rxsubchans = V4L2_TUNER_MODE_MONO; + break; + } + return 0; +} + +static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda9840_core_ops = { + .g_chip_ident = tda9840_g_chip_ident, +}; + +static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { + .s_tuner = tda9840_s_tuner, + .g_tuner = tda9840_g_tuner, +}; + +static const struct v4l2_subdev_ops tda9840_ops = { + .core = &tda9840_core_ops, + .tuner = &tda9840_tuner_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int tda9840_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tda9840_ops); + + /* set initial values for level & stereo - adjustment, mode */ + tda9840_write(sd, LEVEL_ADJUST, 0); + tda9840_write(sd, STEREO_ADJUST, 0); + tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); + return 0; +} + +static int tda9840_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id tda9840_id[] = { + { "tda9840", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda9840_id); + +static struct i2c_driver tda9840_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tda9840", + }, + .probe = tda9840_probe, + .remove = tda9840_remove, + .id_table = tda9840_id, +}; + +module_i2c_driver(tda9840_driver); diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c new file mode 100644 index 000000000000..d1d6ea1dd273 --- /dev/null +++ b/drivers/media/i2c/tea6415c.c @@ -0,0 +1,187 @@ + /* + tea6415c - i2c-driver for the tea6415c by SGS Thomson + + Copyright (C) 1998-2003 Michael Hunold + Copyright (C) 2008 Hans Verkuil + + The tea6415c is a bus controlled video-matrix-switch + with 8 inputs and 6 outputs. + It is cascadable, i.e. it can be found at the addresses + 0x86 and 0x06 on the i2c-bus. + + For detailed informations download the specifications directly + from SGS Thomson at http://www.st.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include "tea6415c.h" + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("tea6415c driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* makes a connection between the input-pin 'i' and the output-pin 'o' */ +static int tea6415c_s_routing(struct v4l2_subdev *sd, + u32 i, u32 o, u32 config) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte = 0; + int ret; + + v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o); + + /* check if the pins are valid */ + if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) + && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o))) + return -EINVAL; + + /* to understand this, have a look at the tea6415c-specs (p.5) */ + switch (o) { + case 18: + byte = 0x00; + break; + case 14: + byte = 0x20; + break; + case 16: + byte = 0x10; + break; + case 17: + byte = 0x08; + break; + case 15: + byte = 0x18; + break; + case 13: + byte = 0x28; + break; + }; + + switch (i) { + case 5: + byte |= 0x00; + break; + case 8: + byte |= 0x04; + break; + case 3: + byte |= 0x02; + break; + case 20: + byte |= 0x06; + break; + case 6: + byte |= 0x01; + break; + case 10: + byte |= 0x05; + break; + case 1: + byte |= 0x03; + break; + case 11: + byte |= 0x07; + break; + }; + + ret = i2c_smbus_write_byte(client, byte); + if (ret) { + v4l2_dbg(1, debug, sd, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); + return -EIO; + } + return ret; +} + +static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6415c_core_ops = { + .g_chip_ident = tea6415c_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops tea6415c_video_ops = { + .s_routing = tea6415c_s_routing, +}; + +static const struct v4l2_subdev_ops tea6415c_ops = { + .core = &tea6415c_core_ops, + .video = &tea6415c_video_ops, +}; + +static int tea6415c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); + return 0; +} + +static int tea6415c_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id tea6415c_id[] = { + { "tea6415c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tea6415c_id); + +static struct i2c_driver tea6415c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tea6415c", + }, + .probe = tea6415c_probe, + .remove = tea6415c_remove, + .id_table = tea6415c_id, +}; + +module_i2c_driver(tea6415c_driver); diff --git a/drivers/media/i2c/tea6415c.h b/drivers/media/i2c/tea6415c.h new file mode 100644 index 000000000000..3a47d697536e --- /dev/null +++ b/drivers/media/i2c/tea6415c.h @@ -0,0 +1,27 @@ +#ifndef __INCLUDED_TEA6415C__ +#define __INCLUDED_TEA6415C__ + +/* the tea6415c's design is quite brain-dead. although there are + 8 inputs and 6 outputs, these aren't enumerated in any way. because + I don't want to say "connect input pin 20 to output pin 17", I define + a "virtual" pin-order. */ + +/* input pins */ +#define TEA6415C_OUTPUT1 18 +#define TEA6415C_OUTPUT2 14 +#define TEA6415C_OUTPUT3 16 +#define TEA6415C_OUTPUT4 17 +#define TEA6415C_OUTPUT5 13 +#define TEA6415C_OUTPUT6 15 + +/* output pins */ +#define TEA6415C_INPUT1 5 +#define TEA6415C_INPUT2 8 +#define TEA6415C_INPUT3 3 +#define TEA6415C_INPUT4 20 +#define TEA6415C_INPUT5 6 +#define TEA6415C_INPUT6 10 +#define TEA6415C_INPUT7 1 +#define TEA6415C_INPUT8 11 + +#endif diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c new file mode 100644 index 000000000000..38757217a074 --- /dev/null +++ b/drivers/media/i2c/tea6420.c @@ -0,0 +1,169 @@ + /* + tea6420 - i2c-driver for the tea6420 by SGS Thomson + + Copyright (C) 1998-2003 Michael Hunold + Copyright (C) 2008 Hans Verkuil + + The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, + 4 stereo outputs and gain control for each output. + It is cascadable, i.e. it can be found at the addresses 0x98 + and 0x9a on the i2c-bus. + + For detailed informations download the specifications directly + from SGS Thomson at http://www.st.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include "tea6420.h" + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("tea6420 driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* make a connection between the input 'i' and the output 'o' + with gain 'g' (note: i = 6 means 'mute') */ +static int tea6420_s_routing(struct v4l2_subdev *sd, + u32 i, u32 o, u32 config) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int g = (o >> 4) & 0xf; + u8 byte; + int ret; + + o &= 0xf; + v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g); + + /* check if the parameters are valid */ + if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) + return -EINVAL; + + byte = ((o - 1) << 5); + byte |= (i - 1); + + /* to understand this, have a look at the tea6420-specs (p.5) */ + switch (g) { + case 0: + byte |= (3 << 3); + break; + case 2: + byte |= (2 << 3); + break; + case 4: + byte |= (1 << 3); + break; + case 6: + break; + } + + ret = i2c_smbus_write_byte(client, byte); + if (ret) { + v4l2_dbg(1, debug, sd, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); + return -EIO; + } + return 0; +} + +static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6420_core_ops = { + .g_chip_ident = tea6420_g_chip_ident, +}; + +static const struct v4l2_subdev_audio_ops tea6420_audio_ops = { + .s_routing = tea6420_s_routing, +}; + +static const struct v4l2_subdev_ops tea6420_ops = { + .core = &tea6420_core_ops, + .audio = &tea6420_audio_ops, +}; + +static int tea6420_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + int err, i; + + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6420_ops); + + /* set initial values: set "mute"-input to all outputs at gain 0 */ + err = 0; + for (i = 1; i < 5; i++) + err += tea6420_s_routing(sd, 6, i, 0); + if (err) { + v4l_dbg(1, debug, client, "could not initialize tea6420\n"); + return -ENODEV; + } + return 0; +} + +static int tea6420_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + +static const struct i2c_device_id tea6420_id[] = { + { "tea6420", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tea6420_id); + +static struct i2c_driver tea6420_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tea6420", + }, + .probe = tea6420_probe, + .remove = tea6420_remove, + .id_table = tea6420_id, +}; + +module_i2c_driver(tea6420_driver); diff --git a/drivers/media/i2c/tea6420.h b/drivers/media/i2c/tea6420.h new file mode 100644 index 000000000000..4aa3edb3e193 --- /dev/null +++ b/drivers/media/i2c/tea6420.h @@ -0,0 +1,24 @@ +#ifndef __INCLUDED_TEA6420__ +#define __INCLUDED_TEA6420__ + +/* input pins */ +#define TEA6420_OUTPUT1 1 +#define TEA6420_OUTPUT2 2 +#define TEA6420_OUTPUT3 3 +#define TEA6420_OUTPUT4 4 + +/* output pins */ +#define TEA6420_INPUT1 1 +#define TEA6420_INPUT2 2 +#define TEA6420_INPUT3 3 +#define TEA6420_INPUT4 4 +#define TEA6420_INPUT5 5 +#define TEA6420_INPUT6 6 + +/* gain on the output pins, ORed with the output pin */ +#define TEA6420_GAIN0 0x00 +#define TEA6420_GAIN2 0x20 +#define TEA6420_GAIN4 0x40 +#define TEA6420_GAIN6 0x60 + +#endif diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c new file mode 100644 index 000000000000..e5c0eedebc58 --- /dev/null +++ b/drivers/media/i2c/ths7303.c @@ -0,0 +1,140 @@ +/* + * ths7303- THS7303 Video Amplifier driver + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 +#include +#include +#include +#include + +#include +#include +#include + +MODULE_DESCRIPTION("TI THS7303 video amplifier driver"); +MODULE_AUTHOR("Chaithrika U S"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +/* following function is used to set ths7303 */ +static int ths7303_setvalue(struct v4l2_subdev *sd, v4l2_std_id std) +{ + int err = 0; + u8 val; + struct i2c_client *client; + + client = v4l2_get_subdevdata(sd); + + if (std & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { + val = 0x02; + v4l2_dbg(1, debug, sd, "setting value for SDTV format\n"); + } else { + val = 0x00; + v4l2_dbg(1, debug, sd, "disabling all channels\n"); + } + + err |= i2c_smbus_write_byte_data(client, 0x01, val); + err |= i2c_smbus_write_byte_data(client, 0x02, val); + err |= i2c_smbus_write_byte_data(client, 0x03, val); + + if (err) + v4l2_err(sd, "write failed\n"); + + return err; +} + +static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + return ths7303_setvalue(sd, norm); +} + +static int ths7303_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); +} + +static const struct v4l2_subdev_video_ops ths7303_video_ops = { + .s_std_output = ths7303_s_std_output, +}; + +static const struct v4l2_subdev_core_ops ths7303_core_ops = { + .g_chip_ident = ths7303_g_chip_ident, +}; + +static const struct v4l2_subdev_ops ths7303_ops = { + .core = &ths7303_core_ops, + .video = &ths7303_video_ops, +}; + +static int ths7303_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + v4l2_std_id std_id = V4L2_STD_NTSC; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + + v4l2_i2c_subdev_init(sd, client, &ths7303_ops); + + return ths7303_setvalue(sd, std_id); +} + +static int ths7303_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + + return 0; +} + +static const struct i2c_device_id ths7303_id[] = { + {"ths7303", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ths7303_id); + +static struct i2c_driver ths7303_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ths7303", + }, + .probe = ths7303_probe, + .remove = ths7303_remove, + .id_table = ths7303_id, +}; + +module_i2c_driver(ths7303_driver); diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c new file mode 100644 index 000000000000..809a75a558ee --- /dev/null +++ b/drivers/media/i2c/tlv320aic23b.c @@ -0,0 +1,230 @@ +/* + * tlv320aic23b - driver version 0.0.1 + * + * Copyright (C) 2006 Scott Alfter + * + * Based on wm8775 driver + * + * Copyright (C) 2004 Ulf Eklund + * Copyright (C) 2005 Hans Verkuil + * + * This program is free software; 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 + +MODULE_DESCRIPTION("tlv320aic23b driver"); +MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); +MODULE_LICENSE("GPL"); + + +/* ----------------------------------------------------------------------- */ + +struct tlv320aic23b_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tlv320aic23b_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd; +} + +static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + if ((reg < 0 || reg > 9) && (reg != 15)) { + v4l2_err(sd, "Invalid register R%d\n", reg); + return -1; + } + + for (i = 0; i < 3; i++) + if (i2c_smbus_write_byte_data(client, + (reg << 1) | (val >> 8), val & 0xff) == 0) + return 0; + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + switch (freq) { + case 32000: /* set sample rate to 32 kHz */ + tlv320aic23b_write(sd, 8, 0x018); + break; + case 44100: /* set sample rate to 44.1 kHz */ + tlv320aic23b_write(sd, 8, 0x022); + break; + case 48000: /* set sample rate to 48 kHz */ + tlv320aic23b_write(sd, 8, 0x000); + break; + default: + return -EINVAL; + } + return 0; +} + +static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ + /* set gain on both channels to +3.0 dB */ + if (!ctrl->val) + tlv320aic23b_write(sd, 0, 0x119); + return 0; + } + return -EINVAL; +} + +static int tlv320aic23b_log_status(struct v4l2_subdev *sd) +{ + struct tlv320aic23b_state *state = to_state(sd); + + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { + .s_ctrl = tlv320aic23b_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { + .log_status = tlv320aic23b_log_status, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { + .s_clock_freq = tlv320aic23b_s_clock_freq, +}; + +static const struct v4l2_subdev_ops tlv320aic23b_ops = { + .core = &tlv320aic23b_core_ops, + .audio = &tlv320aic23b_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int tlv320aic23b_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tlv320aic23b_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); + + /* Initialize tlv320aic23b */ + + /* RESET */ + tlv320aic23b_write(sd, 15, 0x000); + /* turn off DAC & mic input */ + tlv320aic23b_write(sd, 6, 0x00A); + /* left-justified, 24-bit, master mode */ + tlv320aic23b_write(sd, 7, 0x049); + /* set gain on both channels to +3.0 dB */ + tlv320aic23b_write(sd, 0, 0x119); + /* set sample rate to 48 kHz */ + tlv320aic23b_write(sd, 8, 0x000); + /* activate digital interface */ + tlv320aic23b_write(sd, 9, 0x001); + + v4l2_ctrl_handler_init(&state->hdl, 1); + v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + return 0; +} + +static int tlv320aic23b_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tlv320aic23b_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id tlv320aic23b_id[] = { + { "tlv320aic23b", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); + +static struct i2c_driver tlv320aic23b_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tlv320aic23b", + }, + .probe = tlv320aic23b_probe, + .remove = tlv320aic23b_remove, + .id_table = tlv320aic23b_id, +}; + +module_i2c_driver(tlv320aic23b_driver); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c new file mode 100644 index 000000000000..321b3153df87 --- /dev/null +++ b/drivers/media/i2c/tvaudio.c @@ -0,0 +1,2118 @@ +/* + * Driver for simple i2c audio chips. + * + * Copyright (c) 2000 Gerd Knorr + * based on code by: + * Eric Sandeen (eric_sandeen@bigfoot.com) + * Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Greg Alexander (galexand@acm.org) + * + * Copyright(c) 2005-2008 Mauro Carvalho Chehab + * - Some cleanups, code fixes, etc + * - Convert it to V4L2 API + * + * This code is placed under the terms of the GNU General Public License + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* ---------------------------------------------------------------------- */ +/* insmod args */ + +static int debug; /* insmod parameter */ +module_param(debug, int, 0644); + +MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); +MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr"); +MODULE_LICENSE("GPL"); + +#define UNSET (-1U) + +/* ---------------------------------------------------------------------- */ +/* our structs */ + +#define MAXREGS 256 + +struct CHIPSTATE; +typedef int (*getvalue)(int); +typedef int (*checkit)(struct CHIPSTATE*); +typedef int (*initialize)(struct CHIPSTATE*); +typedef int (*getrxsubchans)(struct CHIPSTATE *); +typedef void (*setaudmode)(struct CHIPSTATE*, int mode); + +/* i2c command */ +typedef struct AUDIOCMD { + int count; /* # of bytes to send */ + unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */ +} audiocmd; + +/* chip description */ +struct CHIPDESC { + char *name; /* chip name */ + int addr_lo, addr_hi; /* i2c address range */ + int registers; /* # of registers */ + + int *insmodopt; + checkit checkit; + initialize initialize; + int flags; +#define CHIP_HAS_VOLUME 1 +#define CHIP_HAS_BASSTREBLE 2 +#define CHIP_HAS_INPUTSEL 4 +#define CHIP_NEED_CHECKMODE 8 + + /* various i2c command sequences */ + audiocmd init; + + /* which register has which value */ + int leftreg,rightreg,treblereg,bassreg; + + /* initialize with (defaults to 65535/65535/32768/32768 */ + int leftinit,rightinit,trebleinit,bassinit; + + /* functions to convert the values (v4l -> chip) */ + getvalue volfunc,treblefunc,bassfunc; + + /* get/set mode */ + getrxsubchans getrxsubchans; + setaudmode setaudmode; + + /* input switch register + values for v4l inputs */ + int inputreg; + int inputmap[4]; + int inputmute; + int inputmask; +}; + +/* current state of the chip */ +struct CHIPSTATE { + struct v4l2_subdev sd; + + /* chip-specific description - should point to + an entry at CHIPDESC table */ + struct CHIPDESC *desc; + + /* shadow register set */ + audiocmd shadow; + + /* current settings */ + __u16 left, right, treble, bass, muted; + int prevmode; + int radio; + int input; + + /* thread */ + struct task_struct *thread; + struct timer_list wt; + int audmode; +}; + +static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct CHIPSTATE, sd); +} + + +/* ---------------------------------------------------------------------- */ +/* i2c I/O functions */ + +static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[2]; + + if (subaddr < 0) { + v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); + chip->shadow.bytes[1] = val; + buffer[0] = val; + if (1 != i2c_master_send(c, buffer, 1)) { + v4l2_warn(sd, "I/O error (write 0x%x)\n", val); + return -1; + } + } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l2_info(sd, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + + v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", + subaddr, val); + chip->shadow.bytes[subaddr+1] = val; + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(c, buffer, 2)) { + v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", + subaddr, val); + return -1; + } + } + return 0; +} + +static int chip_write_masked(struct CHIPSTATE *chip, + int subaddr, int val, int mask) +{ + struct v4l2_subdev *sd = &chip->sd; + + if (mask != 0) { + if (subaddr < 0) { + val = (chip->shadow.bytes[1] & ~mask) | (val & mask); + } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l2_info(sd, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + + val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); + } + } + return chip_write(chip, subaddr, val); +} + +static int chip_read(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer; + + if (1 != i2c_master_recv(c, &buffer, 1)) { + v4l2_warn(sd, "I/O error (read)\n"); + return -1; + } + v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); + return buffer; +} + +static int chip_read2(struct CHIPSTATE *chip, int subaddr) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char write[1]; + unsigned char read[1]; + struct i2c_msg msgs[2] = { + { c->addr, 0, 1, write }, + { c->addr, I2C_M_RD, 1, read } + }; + + write[0] = subaddr; + + if (2 != i2c_transfer(c->adapter, msgs, 2)) { + v4l2_warn(sd, "I/O error (read2)\n"); + return -1; + } + v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", + subaddr, read[0]); + return read[0]; +} + +static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) +{ + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + int i; + + if (0 == cmd->count) + return 0; + + if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l2_info(sd, + "Tried to access a non-existent register range: %d to %d\n", + cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); + return -EINVAL; + } + + /* FIXME: it seems that the shadow bytes are wrong bellow !*/ + + /* update our shadow register set; print bytes if (debug > 0) */ + v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", + name, cmd->bytes[0]); + for (i = 1; i < cmd->count; i++) { + if (debug) + printk(KERN_CONT " 0x%x", cmd->bytes[i]); + chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; + } + if (debug) + printk(KERN_CONT "\n"); + + /* send data to the chip */ + if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { + v4l2_warn(sd, "I/O error (%s)\n", name); + return -1; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* kernel thread for doing i2c stuff asyncronly + * right now it is used only to check the audio mode (mono/stereo/whatever) + * some time after switching to another TV channel, then turn on stereo + * if available, ... + */ + +static void chip_thread_wake(unsigned long data) +{ + struct CHIPSTATE *chip = (struct CHIPSTATE*)data; + wake_up_process(chip->thread); +} + +static int chip_thread(void *data) +{ + struct CHIPSTATE *chip = data; + struct CHIPDESC *desc = chip->desc; + struct v4l2_subdev *sd = &chip->sd; + int mode, selected; + + v4l2_dbg(1, debug, sd, "thread started\n"); + set_freezable(); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!kthread_should_stop()) + schedule(); + set_current_state(TASK_RUNNING); + try_to_freeze(); + if (kthread_should_stop()) + break; + v4l2_dbg(1, debug, sd, "thread wakeup\n"); + + /* don't do anything for radio */ + if (chip->radio) + continue; + + /* have a look what's going on */ + mode = desc->getrxsubchans(chip); + if (mode == chip->prevmode) + continue; + + /* chip detected a new audio mode - set it */ + v4l2_dbg(1, debug, sd, "thread checkmode\n"); + + chip->prevmode = mode; + + selected = V4L2_TUNER_MODE_MONO; + switch (chip->audmode) { + case V4L2_TUNER_MODE_MONO: + if (mode & V4L2_TUNER_SUB_LANG1) + selected = V4L2_TUNER_MODE_LANG1; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + if (mode & V4L2_TUNER_SUB_LANG1) + selected = V4L2_TUNER_MODE_LANG1; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + break; + case V4L2_TUNER_MODE_LANG2: + if (mode & V4L2_TUNER_SUB_LANG2) + selected = V4L2_TUNER_MODE_LANG2; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + if (mode & V4L2_TUNER_SUB_LANG2) + selected = V4L2_TUNER_MODE_LANG1_LANG2; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + } + desc->setaudmode(chip, selected); + + /* schedule next check */ + mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); + } + + v4l2_dbg(1, debug, sd, "thread exiting\n"); + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda9840 */ + +#define TDA9840_SW 0x00 +#define TDA9840_LVADJ 0x02 +#define TDA9840_STADJ 0x03 +#define TDA9840_TEST 0x04 + +#define TDA9840_MONO 0x10 +#define TDA9840_STEREO 0x2a +#define TDA9840_DUALA 0x12 +#define TDA9840_DUALB 0x1e +#define TDA9840_DUALAB 0x1a +#define TDA9840_DUALBA 0x16 +#define TDA9840_EXTERNAL 0x7a + +#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */ +#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */ +#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */ + +#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ +#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ + +static int tda9840_getrxsubchans(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int val, mode; + + val = chip_read(chip); + mode = V4L2_TUNER_SUB_MONO; + if (val & TDA9840_DS_DUAL) + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + if (val & TDA9840_ST_STEREO) + mode = V4L2_TUNER_SUB_STEREO; + + v4l2_dbg(1, debug, sd, + "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n", + val, mode); + return mode; +} + +static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) +{ + int update = 1; + int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; + + switch (mode) { + case V4L2_TUNER_MODE_MONO: + t |= TDA9840_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + t |= TDA9840_STEREO; + break; + case V4L2_TUNER_MODE_LANG1: + t |= TDA9840_DUALA; + break; + case V4L2_TUNER_MODE_LANG2: + t |= TDA9840_DUALB; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + t |= TDA9840_DUALAB; + break; + default: + update = 0; + } + + if (update) + chip_write(chip, TDA9840_SW, t); +} + +static int tda9840_checkit(struct CHIPSTATE *chip) +{ + int rc; + rc = chip_read(chip); + /* lower 5 bits should be 0 */ + return ((rc & 0x1f) == 0) ? 1 : 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda985x */ + +/* subaddresses for TDA9855 */ +#define TDA9855_VR 0x00 /* Volume, right */ +#define TDA9855_VL 0x01 /* Volume, left */ +#define TDA9855_BA 0x02 /* Bass */ +#define TDA9855_TR 0x03 /* Treble */ +#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ + +/* subaddresses for TDA9850 */ +#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */ + +/* subaddesses for both chips */ +#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */ +#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */ +#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */ +#define TDA985x_A1 0x08 /* Alignment 1 for both chips */ +#define TDA985x_A2 0x09 /* Alignment 2 for both chips */ +#define TDA985x_A3 0x0a /* Alignment 3 for both chips */ + +/* Masks for bits in TDA9855 subaddresses */ +/* 0x00 - VR in TDA9855 */ +/* 0x01 - VL in TDA9855 */ +/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) + * in 1dB steps - mute is 0x27 */ + + +/* 0x02 - BA in TDA9855 */ +/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) + * in .5dB steps - 0 is 0x0E */ + + +/* 0x03 - TR in TDA9855 */ +/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) + * in 3dB steps - 0 is 0x7 */ + +/* Masks for bits in both chips' subaddresses */ +/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */ +/* Unique to TDA9855: */ +/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf) + * in 3dB steps - mute is 0x0 */ + +/* Unique to TDA9850: */ +/* lower 4 bits control stereo noise threshold, over which stereo turns off + * set to values of 0x00 through 0x0f for Ster1 through Ster16 */ + + +/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/ +/* Unique to TDA9855: */ +#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ +#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ +#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ +#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ + /* Bits 0 to 3 select various combinations + * of line in and line out, only the + * interesting ones are defined */ +#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ +#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ + +/* Unique to TDA9850: */ +/* lower 4 bits contol SAP noise threshold, over which SAP turns off + * set to values of 0x00 through 0x0f for SAP1 through SAP16 */ + + +/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ +/* Common to TDA9855 and TDA9850: */ +#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ +#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */ +#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ +#define TDA985x_MONO 0 /* Forces Mono output */ +#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ + +/* Unique to TDA9855: */ +#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ +#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ +#define TDA9855_LINEAR 0 /* Linear Stereo */ +#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ +#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ +#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ +#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ + +/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) + * in .5dB steps - 0dB is 0x7 */ + +/* 0x08, 0x09 - A1 and A2 (read/write) */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 5 bites are wideband and spectral expander alignment + * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ +#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */ +#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ +#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ + +/* 0x0a - A3 */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), + * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ +#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ + +static int tda9855_volume(int val) { return val/0x2e8+0x27; } +static int tda9855_bass(int val) { return val/0xccc+0x06; } +static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } + +static int tda985x_getrxsubchans(struct CHIPSTATE *chip) +{ + int mode, val; + + /* Add mono mode regardless of SAP and stereo */ + /* Allows forced mono */ + mode = V4L2_TUNER_SUB_MONO; + val = chip_read(chip); + if (val & TDA985x_STP) + mode = V4L2_TUNER_SUB_STEREO; + if (val & TDA985x_SAPP) + mode |= V4L2_TUNER_SUB_SAP; + return mode; +} + +static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode) +{ + int update = 1; + int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; + + switch (mode) { + case V4L2_TUNER_MODE_MONO: + c6 |= TDA985x_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + c6 |= TDA985x_STEREO; + break; + case V4L2_TUNER_MODE_SAP: + c6 |= TDA985x_SAP; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + c6 |= TDA985x_MONOSAP; + break; + default: + update = 0; + } + if (update) + chip_write(chip,TDA985x_C6,c6); +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda9873h */ + +/* Subaddresses for TDA9873H */ + +#define TDA9873_SW 0x00 /* Switching */ +#define TDA9873_AD 0x01 /* Adjust */ +#define TDA9873_PT 0x02 /* Port */ + +/* Subaddress 0x00: Switching Data + * B7..B0: + * + * B1, B0: Input source selection + * 0, 0 internal + * 1, 0 external stereo + * 0, 1 external mono + */ +#define TDA9873_INP_MASK 3 +#define TDA9873_INTERNAL 0 +#define TDA9873_EXT_STEREO 2 +#define TDA9873_EXT_MONO 1 + +/* B3, B2: output signal select + * B4 : transmission mode + * 0, 0, 1 Mono + * 1, 0, 0 Stereo + * 1, 1, 1 Stereo (reversed channel) + * 0, 0, 0 Dual AB + * 0, 0, 1 Dual AA + * 0, 1, 0 Dual BB + * 0, 1, 1 Dual BA + */ + +#define TDA9873_TR_MASK (7 << 2) +#define TDA9873_TR_MONO 4 +#define TDA9873_TR_STEREO 1 << 4 +#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2)) +#define TDA9873_TR_DUALA 1 << 2 +#define TDA9873_TR_DUALB 1 << 3 +#define TDA9873_TR_DUALAB 0 + +/* output level controls + * B5: output level switch (0 = reduced gain, 1 = normal gain) + * B6: mute (1 = muted) + * B7: auto-mute (1 = auto-mute enabled) + */ + +#define TDA9873_GAIN_NORMAL 1 << 5 +#define TDA9873_MUTE 1 << 6 +#define TDA9873_AUTOMUTE 1 << 7 + +/* Subaddress 0x01: Adjust/standard */ + +/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB) + * Recommended value is +0 dB + */ + +#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */ + +/* Bits C6..C4 control FM stantard + * C6, C5, C4 + * 0, 0, 0 B/G (PAL FM) + * 0, 0, 1 M + * 0, 1, 0 D/K(1) + * 0, 1, 1 D/K(2) + * 1, 0, 0 D/K(3) + * 1, 0, 1 I + */ +#define TDA9873_BG 0 +#define TDA9873_M 1 +#define TDA9873_DK1 2 +#define TDA9873_DK2 3 +#define TDA9873_DK3 4 +#define TDA9873_I 5 + +/* C7 controls identification response time (1=fast/0=normal) + */ +#define TDA9873_IDR_NORM 0 +#define TDA9873_IDR_FAST 1 << 7 + + +/* Subaddress 0x02: Port data */ + +/* E1, E0 free programmable ports P1/P2 + 0, 0 both ports low + 0, 1 P1 high + 1, 0 P2 high + 1, 1 both ports high +*/ + +#define TDA9873_PORTS 3 + +/* E2: test port */ +#define TDA9873_TST_PORT 1 << 2 + +/* E5..E3 control mono output channel (together with transmission mode bit B4) + * + * E5 E4 E3 B4 OUTM + * 0 0 0 0 mono + * 0 0 1 0 DUAL B + * 0 1 0 1 mono (from stereo decoder) + */ +#define TDA9873_MOUT_MONO 0 +#define TDA9873_MOUT_FMONO 0 +#define TDA9873_MOUT_DUALA 0 +#define TDA9873_MOUT_DUALB 1 << 3 +#define TDA9873_MOUT_ST 1 << 4 +#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3)) +#define TDA9873_MOUT_EXTL 1 << 5 +#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3)) +#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4)) +#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3)) + +/* Status bits: (chip read) */ +#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ +#define TDA9873_STEREO 2 /* Stereo sound is identified */ +#define TDA9873_DUAL 4 /* Dual sound is identified */ + +static int tda9873_getrxsubchans(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int val,mode; + + val = chip_read(chip); + mode = V4L2_TUNER_SUB_MONO; + if (val & TDA9873_STEREO) + mode = V4L2_TUNER_SUB_STEREO; + if (val & TDA9873_DUAL) + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + v4l2_dbg(1, debug, sd, + "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n", + val, mode); + return mode; +} + +static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode) +{ + struct v4l2_subdev *sd = &chip->sd; + int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; + /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ + + if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): external input\n"); + return; + } + + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n", + TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); + v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n", + sw_data); + + switch (mode) { + case V4L2_TUNER_MODE_MONO: + sw_data |= TDA9873_TR_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + sw_data |= TDA9873_TR_STEREO; + break; + case V4L2_TUNER_MODE_LANG1: + sw_data |= TDA9873_TR_DUALA; + break; + case V4L2_TUNER_MODE_LANG2: + sw_data |= TDA9873_TR_DUALB; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + sw_data |= TDA9873_TR_DUALAB; + break; + default: + return; + } + + chip_write(chip, TDA9873_SW, sw_data); + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): req. mode %d; chip_write: %d\n", + mode, sw_data); +} + +static int tda9873_checkit(struct CHIPSTATE *chip) +{ + int rc; + + if (-1 == (rc = chip_read2(chip,254))) + return 0; + return (rc & ~0x1f) == 0x80; +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip description - defines+functions for tda9874h and tda9874a */ +/* Dariusz Kowalewski */ + +/* Subaddresses for TDA9874H and TDA9874A (slave rx) */ +#define TDA9874A_AGCGR 0x00 /* AGC gain */ +#define TDA9874A_GCONR 0x01 /* general config */ +#define TDA9874A_MSR 0x02 /* monitor select */ +#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */ +#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */ +#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */ +#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */ +#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */ +#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */ +#define TDA9874A_DCR 0x09 /* demodulator config */ +#define TDA9874A_FMER 0x0a /* FM de-emphasis */ +#define TDA9874A_FMMR 0x0b /* FM dematrix */ +#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */ +#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */ +#define TDA9874A_NCONR 0x0e /* NICAM config */ +#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */ +#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */ +#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */ +#define TDA9874A_AMCONR 0x12 /* audio mute control */ +#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */ +#define TDA9874A_AOSR 0x14 /* analog output select */ +#define TDA9874A_DAICONR 0x15 /* digital audio interface config */ +#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */ +#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */ +#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */ +#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */ + +/* Subaddresses for TDA9874H and TDA9874A (slave tx) */ +#define TDA9874A_DSR 0x00 /* device status */ +#define TDA9874A_NSR 0x01 /* NICAM status */ +#define TDA9874A_NECR 0x02 /* NICAM error count */ +#define TDA9874A_DR1 0x03 /* add. data LSB */ +#define TDA9874A_DR2 0x04 /* add. data MSB */ +#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */ +#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */ +#define TDA9874A_SIFLR 0x07 /* SIF level */ +#define TDA9874A_TR2 252 /* test reg. 2 */ +#define TDA9874A_TR1 253 /* test reg. 1 */ +#define TDA9874A_DIC 254 /* device id. code */ +#define TDA9874A_SIC 255 /* software id. code */ + + +static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */ +static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */ +static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */ +static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */ +static int tda9874a_dic = -1; /* device id. code */ + +/* insmod options for tda9874a */ +static unsigned int tda9874a_SIF = UNSET; +static unsigned int tda9874a_AMSEL = UNSET; +static unsigned int tda9874a_STD = UNSET; +module_param(tda9874a_SIF, int, 0444); +module_param(tda9874a_AMSEL, int, 0444); +module_param(tda9874a_STD, int, 0444); + +/* + * initialization table for tda9874 decoder: + * - carrier 1 freq. registers (3 bytes) + * - carrier 2 freq. registers (3 bytes) + * - demudulator config register + * - FM de-emphasis register (slow identification mode) + * Note: frequency registers must be written in single i2c transfer. + */ +static struct tda9874a_MODES { + char *name; + audiocmd cmd; +} tda9874a_modelist[9] = { + { "A2, B/G", /* default */ + { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, + { "A2, M (Korea)", + { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, + { "A2, D/K (1)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} }, + { "A2, D/K (2)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} }, + { "A2, D/K (3)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} }, + { "NICAM, I", + { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, + { "NICAM, B/G", + { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, + { "NICAM, D/K", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, + { "NICAM, L", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } +}; + +static int tda9874a_setup(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + + chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ + chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); + chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); + if(tda9874a_dic == 0x11) { + chip_write(chip, TDA9874A_FMMR, 0x80); + } else { /* dic == 0x07 */ + chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd); + chip_write(chip, TDA9874A_FMMR, 0x00); + } + chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */ + chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */ + chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); + chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */ + /* Note: If signal quality is poor you may want to change NICAM */ + /* error limit registers (NLELR and NUELR) to some greater values. */ + /* Then the sound would remain stereo, but won't be so clear. */ + chip_write(chip, TDA9874A_NLELR, 0x14); /* default */ + chip_write(chip, TDA9874A_NUELR, 0x50); /* default */ + + if(tda9874a_dic == 0x11) { + chip_write(chip, TDA9874A_AMCONR, 0xf9); + chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); + chip_write(chip, TDA9874A_AOSR, 0x80); + chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); + chip_write(chip, TDA9874A_ESP, tda9874a_ESP); + } else { /* dic == 0x07 */ + chip_write(chip, TDA9874A_AMCONR, 0xfb); + chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); + chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ + } + v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", + tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); + return 1; +} + +static int tda9874a_getrxsubchans(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dsr,nsr,mode; + int necr; /* just for debugging */ + + mode = V4L2_TUNER_SUB_MONO; + + if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR))) + return mode; + if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR))) + return mode; + if(-1 == (necr = chip_read2(chip,TDA9874A_NECR))) + return mode; + + /* need to store dsr/nsr somewhere */ + chip->shadow.bytes[MAXREGS-2] = dsr; + chip->shadow.bytes[MAXREGS-1] = nsr; + + if(tda9874a_mode) { + /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked. + * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates + * that sound has (temporarily) switched from NICAM to + * mono FM (or AM) on 1st sound carrier due to high NICAM bit + * error count. So in fact there is no stereo in this case :-( + * But changing the mode to V4L2_TUNER_MODE_MONO would switch + * external 4052 multiplexer in audio_hook(). + */ + if(nsr & 0x02) /* NSR.S/MB=1 */ + mode = V4L2_TUNER_SUB_STEREO; + if(nsr & 0x01) /* NSR.D/SB=1 */ + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } else { + if(dsr & 0x02) /* DSR.IDSTE=1 */ + mode = V4L2_TUNER_SUB_STEREO; + if(dsr & 0x04) /* DSR.IDDUA=1 */ + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } + + v4l2_dbg(1, debug, sd, + "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", + dsr, nsr, necr, mode); + return mode; +} + +static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode) +{ + struct v4l2_subdev *sd = &chip->sd; + + /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ + /* If auto-muting is disabled, we can hear a signal of degrading quality. */ + if (tda9874a_mode) { + if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ + tda9874a_NCONR &= 0xfe; /* enable */ + else + tda9874a_NCONR |= 0x01; /* disable */ + chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); + } + + /* Note: TDA9874A supports automatic FM dematrixing (FMMR register) + * and has auto-select function for audio output (AOSR register). + * Old TDA9874H doesn't support these features. + * TDA9874A also has additional mono output pin (OUTM), which + * on same (all?) tv-cards is not used, anyway (as well as MONOIN). + */ + if(tda9874a_dic == 0x11) { + int aosr = 0x80; + int mdacosr = (tda9874a_mode) ? 0x82:0x80; + + switch(mode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_STEREO: + break; + case V4L2_TUNER_MODE_LANG1: + aosr = 0x80; /* auto-select, dual A/A */ + mdacosr = (tda9874a_mode) ? 0x82:0x80; + break; + case V4L2_TUNER_MODE_LANG2: + aosr = 0xa0; /* auto-select, dual B/B */ + mdacosr = (tda9874a_mode) ? 0x83:0x81; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + aosr = 0x00; /* always route L to L and R to R */ + mdacosr = (tda9874a_mode) ? 0x82:0x80; + break; + default: + return; + } + chip_write(chip, TDA9874A_AOSR, aosr); + chip_write(chip, TDA9874A_MDACOSR, mdacosr); + + v4l2_dbg(1, debug, sd, + "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", + mode, aosr, mdacosr); + + } else { /* dic == 0x07 */ + int fmmr,aosr; + + switch(mode) { + case V4L2_TUNER_MODE_MONO: + fmmr = 0x00; /* mono */ + aosr = 0x10; /* A/A */ + break; + case V4L2_TUNER_MODE_STEREO: + if(tda9874a_mode) { + fmmr = 0x00; + aosr = 0x00; /* handled by NICAM auto-mute */ + } else { + fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */ + aosr = 0x00; + } + break; + case V4L2_TUNER_MODE_LANG1: + fmmr = 0x02; /* dual */ + aosr = 0x10; /* dual A/A */ + break; + case V4L2_TUNER_MODE_LANG2: + fmmr = 0x02; /* dual */ + aosr = 0x20; /* dual B/B */ + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + fmmr = 0x02; /* dual */ + aosr = 0x00; /* dual A/B */ + break; + default: + return; + } + chip_write(chip, TDA9874A_FMMR, fmmr); + chip_write(chip, TDA9874A_AOSR, aosr); + + v4l2_dbg(1, debug, sd, + "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", + mode, fmmr, aosr); + } +} + +static int tda9874a_checkit(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dic,sic; /* device id. and software id. codes */ + + if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) + return 0; + if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) + return 0; + + v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); + + if((dic == 0x11)||(dic == 0x07)) { + v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); + tda9874a_dic = dic; /* remember device id. */ + return 1; + } + return 0; /* not found */ +} + +static int tda9874a_initialize(struct CHIPSTATE *chip) +{ + if (tda9874a_SIF > 2) + tda9874a_SIF = 1; + if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) + tda9874a_STD = 0; + if(tda9874a_AMSEL > 1) + tda9874a_AMSEL = 0; + + if(tda9874a_SIF == 1) + tda9874a_GCONR = 0xc0; /* sound IF input 1 */ + else + tda9874a_GCONR = 0xc1; /* sound IF input 2 */ + + tda9874a_ESP = tda9874a_STD; + tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; + + if(tda9874a_AMSEL == 0) + tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ + else + tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ + + tda9874a_setup(chip); + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip description - defines+functions for tda9875 */ +/* The TDA9875 is made by Philips Semiconductor + * http://www.semiconductors.philips.com + * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator + * + */ + +/* subaddresses for TDA9875 */ +#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ +#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ +#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ +#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ + +#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ +#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ +#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ +#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ + +#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ +#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ +#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ +#define TDA9875_MVL 0x1a /* Main volume gauche */ +#define TDA9875_MVR 0x1b /* Main volume droite */ +#define TDA9875_MBA 0x1d /* Main Basse */ +#define TDA9875_MTR 0x1e /* Main treble */ +#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/ +#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */ +#define TDA9875_AVR 0x21 /* Auxiliary volume droite */ +#define TDA9875_ABA 0x22 /* Auxiliary Basse */ +#define TDA9875_ATR 0x23 /* Auxiliary treble */ + +#define TDA9875_MSR 0x02 /* Monitor select register */ +#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ +#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ +#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ +#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ +#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ +#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ +#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ +#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ +#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ + +/* values */ +#define TDA9875_MUTE_ON 0xff /* general mute */ +#define TDA9875_MUTE_OFF 0xcc /* general no mute */ + +static int tda9875_initialize(struct CHIPSTATE *chip) +{ + chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ + chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ + chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ + chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ + chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ + chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ + chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ + chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ + chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ + chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ + chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ + chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ + chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ + + chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ + chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ + chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ + chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ + chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ + chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ + chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ + chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ + chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ + chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ + chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ + chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ + chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ + chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ + chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ + chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ + + chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ + return 0; +} + +static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } +static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } +static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } + +/* ----------------------------------------------------------------------- */ + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda9875_checkit(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dic, rev; + + dic = chip_read2(chip, 254); + rev = chip_read2(chip, 255); + + if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ + v4l2_info(sd, "found tda9875%s rev. %d.\n", + dic == 0 ? "" : "A", rev); + return 1; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tea6420 */ + +#define TEA6300_VL 0x00 /* volume left */ +#define TEA6300_VR 0x01 /* volume right */ +#define TEA6300_BA 0x02 /* bass */ +#define TEA6300_TR 0x03 /* treble */ +#define TEA6300_FA 0x04 /* fader control */ +#define TEA6300_S 0x05 /* switch register */ + /* values for those registers: */ +#define TEA6300_S_SA 0x01 /* stereo A input */ +#define TEA6300_S_SB 0x02 /* stereo B */ +#define TEA6300_S_SC 0x04 /* stereo C */ +#define TEA6300_S_GMU 0x80 /* general mute */ + +#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */ +#define TEA6320_FFR 0x01 /* fader front right (0-5) */ +#define TEA6320_FFL 0x02 /* fader front left (0-5) */ +#define TEA6320_FRR 0x03 /* fader rear right (0-5) */ +#define TEA6320_FRL 0x04 /* fader rear left (0-5) */ +#define TEA6320_BA 0x05 /* bass (0-4) */ +#define TEA6320_TR 0x06 /* treble (0-4) */ +#define TEA6320_S 0x07 /* switch register */ + /* values for those registers: */ +#define TEA6320_S_SA 0x07 /* stereo A input */ +#define TEA6320_S_SB 0x06 /* stereo B */ +#define TEA6320_S_SC 0x05 /* stereo C */ +#define TEA6320_S_SD 0x04 /* stereo D */ +#define TEA6320_S_GMU 0x80 /* general mute */ + +#define TEA6420_S_SA 0x00 /* stereo A input */ +#define TEA6420_S_SB 0x01 /* stereo B */ +#define TEA6420_S_SC 0x02 /* stereo C */ +#define TEA6420_S_SD 0x03 /* stereo D */ +#define TEA6420_S_SE 0x04 /* stereo E */ +#define TEA6420_S_GMU 0x05 /* general mute */ + +static int tea6300_shift10(int val) { return val >> 10; } +static int tea6300_shift12(int val) { return val >> 12; } + +/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */ +/* 0x0c mirror those immediately higher) */ +static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; } +static int tea6320_shift11(int val) { return val >> 11; } +static int tea6320_initialize(struct CHIPSTATE * chip) +{ + chip_write(chip, TEA6320_FFR, 0x3f); + chip_write(chip, TEA6320_FFL, 0x3f); + chip_write(chip, TEA6320_FRR, 0x3f); + chip_write(chip, TEA6320_FRL, 0x3f); + + return 0; +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda8425 */ + +#define TDA8425_VL 0x00 /* volume left */ +#define TDA8425_VR 0x01 /* volume right */ +#define TDA8425_BA 0x02 /* bass */ +#define TDA8425_TR 0x03 /* treble */ +#define TDA8425_S1 0x08 /* switch functions */ + /* values for those registers: */ +#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ +#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */ +#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */ +#define TDA8425_S1_MU 0x20 /* mute bit */ +#define TDA8425_S1_STEREO 0x18 /* stereo bits */ +#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */ +#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */ +#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */ +#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */ +#define TDA8425_S1_ML 0x06 /* language selector */ +#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */ +#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */ +#define TDA8425_S1_ML_STEREO 0x06 /* stereo */ +#define TDA8425_S1_IS 0x01 /* channel selector */ + + +static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; } +static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } + +static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode) +{ + int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1; + + switch (mode) { + case V4L2_TUNER_MODE_LANG1: + s1 |= TDA8425_S1_ML_SOUND_A; + s1 |= TDA8425_S1_STEREO_PSEUDO; + break; + case V4L2_TUNER_MODE_LANG2: + s1 |= TDA8425_S1_ML_SOUND_B; + s1 |= TDA8425_S1_STEREO_PSEUDO; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_LINEAR; + break; + case V4L2_TUNER_MODE_MONO: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_SPATIAL; + break; + default: + return; + } + chip_write(chip,TDA8425_S1,s1); +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for pic16c54 (PV951) */ + +/* the registers of 16C54, I2C sub address. */ +#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */ +#define PIC16C54_REG_MISC 0x02 + +/* bit definition of the RESET register, I2C data. */ +#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ + /* code of remote controller */ +#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */ +#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */ +#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */ +#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */ +#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */ +#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ +#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for TA8874Z */ + +/* write 1st byte */ +#define TA8874Z_LED_STE 0x80 +#define TA8874Z_LED_BIL 0x40 +#define TA8874Z_LED_EXT 0x20 +#define TA8874Z_MONO_SET 0x10 +#define TA8874Z_MUTE 0x08 +#define TA8874Z_F_MONO 0x04 +#define TA8874Z_MODE_SUB 0x02 +#define TA8874Z_MODE_MAIN 0x01 + +/* write 2nd byte */ +/*#define TA8874Z_TI 0x80 */ /* test mode */ +#define TA8874Z_SEPARATION 0x3f +#define TA8874Z_SEPARATION_DEFAULT 0x10 + +/* read */ +#define TA8874Z_B1 0x80 +#define TA8874Z_B0 0x40 +#define TA8874Z_CHAG_FLAG 0x20 + +/* + * B1 B0 + * mono L H + * stereo L L + * BIL H L + */ +static int ta8874z_getrxsubchans(struct CHIPSTATE *chip) +{ + int val, mode; + + val = chip_read(chip); + mode = V4L2_TUNER_SUB_MONO; + if (val & TA8874Z_B1){ + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + }else if (!(val & TA8874Z_B0)){ + mode = V4L2_TUNER_SUB_STEREO; + } + /* v4l2_dbg(1, debug, &chip->sd, + "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n", + val, mode); */ + return mode; +} + +static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; + +static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) +{ + struct v4l2_subdev *sd = &chip->sd; + int update = 1; + audiocmd *t = NULL; + + v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode); + + switch(mode){ + case V4L2_TUNER_MODE_MONO: + t = &ta8874z_mono; + break; + case V4L2_TUNER_MODE_STEREO: + t = &ta8874z_stereo; + break; + case V4L2_TUNER_MODE_LANG1: + t = &ta8874z_main; + break; + case V4L2_TUNER_MODE_LANG2: + t = &ta8874z_sub; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + t = &ta8874z_both; + break; + default: + update = 0; + } + + if(update) + chip_cmd(chip, "TA8874Z", t); +} + +static int ta8874z_checkit(struct CHIPSTATE *chip) +{ + int rc; + rc = chip_read(chip); + return ((rc & 0x1f) == 0x1f) ? 1 : 0; +} + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - struct CHIPDESC */ + +/* insmod options to enable/disable individual audio chips */ +static int tda8425 = 1; +static int tda9840 = 1; +static int tda9850 = 1; +static int tda9855 = 1; +static int tda9873 = 1; +static int tda9874a = 1; +static int tda9875 = 1; +static int tea6300; /* default 0 - address clash with msp34xx */ +static int tea6320; /* default 0 - address clash with msp34xx */ +static int tea6420 = 1; +static int pic16c54 = 1; +static int ta8874z; /* default 0 - address clash with tda9840 */ + +module_param(tda8425, int, 0444); +module_param(tda9840, int, 0444); +module_param(tda9850, int, 0444); +module_param(tda9855, int, 0444); +module_param(tda9873, int, 0444); +module_param(tda9874a, int, 0444); +module_param(tda9875, int, 0444); +module_param(tea6300, int, 0444); +module_param(tea6320, int, 0444); +module_param(tea6420, int, 0444); +module_param(pic16c54, int, 0444); +module_param(ta8874z, int, 0444); + +static struct CHIPDESC chiplist[] = { + { + .name = "tda9840", + .insmodopt = &tda9840, + .addr_lo = I2C_ADDR_TDA9840 >> 1, + .addr_hi = I2C_ADDR_TDA9840 >> 1, + .registers = 5, + .flags = CHIP_NEED_CHECKMODE, + + /* callbacks */ + .checkit = tda9840_checkit, + .getrxsubchans = tda9840_getrxsubchans, + .setaudmode = tda9840_setaudmode, + + .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN + /* ,TDA9840_SW, TDA9840_MONO */} } + }, + { + .name = "tda9873h", + .insmodopt = &tda9873, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, + .registers = 3, + .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, + + /* callbacks */ + .checkit = tda9873_checkit, + .getrxsubchans = tda9873_getrxsubchans, + .setaudmode = tda9873_setaudmode, + + .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, + .inputreg = TDA9873_SW, + .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE, + .inputmap = {0xa0, 0xa2, 0xa0, 0xa0}, + .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE, + + }, + { + .name = "tda9874h/a", + .insmodopt = &tda9874a, + .addr_lo = I2C_ADDR_TDA9874 >> 1, + .addr_hi = I2C_ADDR_TDA9874 >> 1, + .flags = CHIP_NEED_CHECKMODE, + + /* callbacks */ + .initialize = tda9874a_initialize, + .checkit = tda9874a_checkit, + .getrxsubchans = tda9874a_getrxsubchans, + .setaudmode = tda9874a_setaudmode, + }, + { + .name = "tda9875", + .insmodopt = &tda9875, + .addr_lo = I2C_ADDR_TDA9875 >> 1, + .addr_hi = I2C_ADDR_TDA9875 >> 1, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, + + /* callbacks */ + .initialize = tda9875_initialize, + .checkit = tda9875_checkit, + .volfunc = tda9875_volume, + .bassfunc = tda9875_bass, + .treblefunc = tda9875_treble, + .leftreg = TDA9875_MVL, + .rightreg = TDA9875_MVR, + .bassreg = TDA9875_MBA, + .treblereg = TDA9875_MTR, + .leftinit = 58880, + .rightinit = 58880, + }, + { + .name = "tda9850", + .insmodopt = &tda9850, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, + .registers = 11, + + .getrxsubchans = tda985x_getrxsubchans, + .setaudmode = tda985x_setaudmode, + + .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } + }, + { + .name = "tda9855", + .insmodopt = &tda9855, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, + .registers = 11, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, + + .leftreg = TDA9855_VL, + .rightreg = TDA9855_VR, + .bassreg = TDA9855_BA, + .treblereg = TDA9855_TR, + + /* callbacks */ + .volfunc = tda9855_volume, + .bassfunc = tda9855_bass, + .treblefunc = tda9855_treble, + .getrxsubchans = tda985x_getrxsubchans, + .setaudmode = tda985x_setaudmode, + + .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, + TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, + TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, + 0x07, 0x10, 0x10, 0x03 }} + }, + { + .name = "tea6300", + .insmodopt = &tea6300, + .addr_lo = I2C_ADDR_TEA6300 >> 1, + .addr_hi = I2C_ADDR_TEA6300 >> 1, + .registers = 6, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + .leftreg = TEA6300_VR, + .rightreg = TEA6300_VL, + .bassreg = TEA6300_BA, + .treblereg = TEA6300_TR, + + /* callbacks */ + .volfunc = tea6300_shift10, + .bassfunc = tea6300_shift12, + .treblefunc = tea6300_shift12, + + .inputreg = TEA6300_S, + .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, + .inputmute = TEA6300_S_GMU, + }, + { + .name = "tea6320", + .insmodopt = &tea6320, + .addr_lo = I2C_ADDR_TEA6300 >> 1, + .addr_hi = I2C_ADDR_TEA6300 >> 1, + .registers = 8, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + .leftreg = TEA6320_V, + .rightreg = TEA6320_V, + .bassreg = TEA6320_BA, + .treblereg = TEA6320_TR, + + /* callbacks */ + .initialize = tea6320_initialize, + .volfunc = tea6320_volume, + .bassfunc = tea6320_shift11, + .treblefunc = tea6320_shift11, + + .inputreg = TEA6320_S, + .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD }, + .inputmute = TEA6300_S_GMU, + }, + { + .name = "tea6420", + .insmodopt = &tea6420, + .addr_lo = I2C_ADDR_TEA6420 >> 1, + .addr_hi = I2C_ADDR_TEA6420 >> 1, + .registers = 1, + .flags = CHIP_HAS_INPUTSEL, + + .inputreg = -1, + .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, + .inputmute = TEA6300_S_GMU, + }, + { + .name = "tda8425", + .insmodopt = &tda8425, + .addr_lo = I2C_ADDR_TDA8425 >> 1, + .addr_hi = I2C_ADDR_TDA8425 >> 1, + .registers = 9, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + .leftreg = TDA8425_VL, + .rightreg = TDA8425_VR, + .bassreg = TDA8425_BA, + .treblereg = TDA8425_TR, + + /* callbacks */ + .volfunc = tda8425_shift10, + .bassfunc = tda8425_shift12, + .treblefunc = tda8425_shift12, + .setaudmode = tda8425_setaudmode, + + .inputreg = TDA8425_S1, + .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, + .inputmute = TDA8425_S1_OFF, + + }, + { + .name = "pic16c54 (PV951)", + .insmodopt = &pic16c54, + .addr_lo = I2C_ADDR_PIC16C54 >> 1, + .addr_hi = I2C_ADDR_PIC16C54>> 1, + .registers = 2, + .flags = CHIP_HAS_INPUTSEL, + + .inputreg = PIC16C54_REG_MISC, + .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, + PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, + PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, + PIC16C54_MISC_SND_MUTE}, + .inputmute = PIC16C54_MISC_SND_MUTE, + }, + { + .name = "ta8874z", + .checkit = ta8874z_checkit, + .insmodopt = &ta8874z, + .addr_lo = I2C_ADDR_TDA9840 >> 1, + .addr_hi = I2C_ADDR_TDA9840 >> 1, + .registers = 2, + + /* callbacks */ + .getrxsubchans = ta8874z_getrxsubchans, + .setaudmode = ta8874z_setaudmode, + + .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, + }, + { .name = NULL } /* EOF */ +}; + + +/* ---------------------------------------------------------------------- */ + +static int tvaudio_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; + ctrl->value=chip->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + ctrl->value = max(chip->left,chip->right); + return 0; + case V4L2_CID_AUDIO_BALANCE: + { + int volume; + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + volume = max(chip->left,chip->right); + if (volume) + ctrl->value=(32768*min(chip->left,chip->right))/volume; + else + ctrl->value=32768; + return 0; + } + case V4L2_CID_AUDIO_BASS: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + ctrl->value = chip->bass; + return 0; + case V4L2_CID_AUDIO_TREBLE: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + ctrl->value = chip->treble; + return 0; + } + return -EINVAL; +} + +static int tvaudio_s_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; + + if (ctrl->value < 0 || ctrl->value >= 2) + return -ERANGE; + chip->muted = ctrl->value; + if (chip->muted) + chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); + else + chip_write_masked(chip,desc->inputreg, + desc->inputmap[chip->input],desc->inputmask); + return 0; + case V4L2_CID_AUDIO_VOLUME: + { + int volume,balance; + + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + + volume = max(chip->left,chip->right); + if (volume) + balance=(32768*min(chip->left,chip->right))/volume; + else + balance=32768; + + volume=ctrl->value; + chip->left = (min(65536 - balance,32768) * volume) / 32768; + chip->right = (min(balance,volume *(__u16)32768)) / 32768; + + chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); + chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + + return 0; + } + case V4L2_CID_AUDIO_BALANCE: + { + int volume, balance; + + if (!(desc->flags & CHIP_HAS_VOLUME)) + break; + + volume = max(chip->left, chip->right); + balance = ctrl->value; + chip->left = (min(65536 - balance, 32768) * volume) / 32768; + chip->right = (min(balance, volume * (__u16)32768)) / 32768; + + chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); + + return 0; + } + case V4L2_CID_AUDIO_BASS: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + chip->bass = ctrl->value; + chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); + + return 0; + case V4L2_CID_AUDIO_TREBLE: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; + chip->treble = ctrl->value; + chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); + + return 0; + } + return -EINVAL; +} + + +/* ---------------------------------------------------------------------- */ +/* video4linux interface */ + +static int tvaudio_s_radio(struct v4l2_subdev *sd) +{ + struct CHIPSTATE *chip = to_state(sd); + + chip->radio = 1; + /* del_timer(&chip->wt); */ + return 0; +} + +static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + if (desc->flags & CHIP_HAS_INPUTSEL) + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + break; + case V4L2_CID_AUDIO_VOLUME: + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + break; + case V4L2_CID_AUDIO_BALANCE: + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + break; + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + if (desc->flags & CHIP_HAS_BASSTREBLE) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + break; + default: + break; + } + return -EINVAL; +} + +static int tvaudio_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + return 0; + if (input >= 4) + return -EINVAL; + /* There are four inputs: tuner, radio, extern and intern. */ + chip->input = input; + if (chip->muted) + return 0; + chip_write_masked(chip, desc->inputreg, + desc->inputmap[chip->input], desc->inputmask); + return 0; +} + +static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + if (!desc->setaudmode) + return 0; + if (chip->radio) + return 0; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + case V4L2_TUNER_MODE_LANG1_LANG2: + break; + default: + return -EINVAL; + } + chip->audmode = vt->audmode; + + if (chip->thread) + wake_up_process(chip->thread); + else + desc->setaudmode(chip, vt->audmode); + + return 0; +} + +static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + if (!desc->getrxsubchans) + return 0; + if (chip->radio) + return 0; + + vt->audmode = chip->audmode; + vt->rxsubchans = desc->getrxsubchans(chip); + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + + return 0; +} + +static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct CHIPSTATE *chip = to_state(sd); + + chip->radio = 0; + return 0; +} + +static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + /* For chips that provide getrxsubchans and setaudmode, and doesn't + automatically follows the stereo carrier, a kthread is + created to set the audio standard. In this case, when then + the video channel is changed, tvaudio starts on MONO mode. + After waiting for 2 seconds, the kernel thread is called, + to follow whatever audio standard is pointed by the + audio carrier. + */ + if (chip->thread) { + desc->setaudmode(chip, V4L2_TUNER_MODE_MONO); + chip->prevmode = -1; /* reset previous mode */ + mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); + } + return 0; +} + +static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tvaudio_core_ops = { + .g_chip_ident = tvaudio_g_chip_ident, + .queryctrl = tvaudio_queryctrl, + .g_ctrl = tvaudio_g_ctrl, + .s_ctrl = tvaudio_s_ctrl, + .s_std = tvaudio_s_std, +}; + +static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { + .s_radio = tvaudio_s_radio, + .s_frequency = tvaudio_s_frequency, + .s_tuner = tvaudio_s_tuner, + .g_tuner = tvaudio_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { + .s_routing = tvaudio_s_routing, +}; + +static const struct v4l2_subdev_ops tvaudio_ops = { + .core = &tvaudio_core_ops, + .tuner = &tvaudio_tuner_ops, + .audio = &tvaudio_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +/* i2c registration */ + +static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct CHIPSTATE *chip; + struct CHIPDESC *desc; + struct v4l2_subdev *sd; + + if (debug) { + printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); + printk(KERN_INFO "tvaudio: known chips: "); + for (desc = chiplist; desc->name != NULL; desc++) + printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); + printk("\n"); + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + sd = &chip->sd; + v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); + + /* find description for the chip */ + v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); + for (desc = chiplist; desc->name != NULL; desc++) { + if (0 == *(desc->insmodopt)) + continue; + if (client->addr < desc->addr_lo || + client->addr > desc->addr_hi) + continue; + if (desc->checkit && !desc->checkit(chip)) + continue; + break; + } + if (desc->name == NULL) { + v4l2_dbg(1, debug, sd, "no matching chip description found\n"); + kfree(chip); + return -EIO; + } + v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); + if (desc->flags) { + v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", + (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", + (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", + (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); + } + + /* fill required data structures */ + if (!id) + strlcpy(client->name, desc->name, I2C_NAME_SIZE); + chip->desc = desc; + chip->shadow.count = desc->registers+1; + chip->prevmode = -1; + chip->audmode = V4L2_TUNER_MODE_LANG1; + + /* initialization */ + if (desc->initialize != NULL) + desc->initialize(chip); + else + chip_cmd(chip, "init", &desc->init); + + if (desc->flags & CHIP_HAS_VOLUME) { + if (!desc->volfunc) { + /* This shouldn't be happen. Warn user, but keep working + without volume controls + */ + v4l2_info(sd, "volume callback undefined!\n"); + desc->flags &= ~CHIP_HAS_VOLUME; + } else { + chip->left = desc->leftinit ? desc->leftinit : 65535; + chip->right = desc->rightinit ? desc->rightinit : 65535; + chip_write(chip, desc->leftreg, + desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, + desc->volfunc(chip->right)); + } + } + if (desc->flags & CHIP_HAS_BASSTREBLE) { + if (!desc->bassfunc || !desc->treblefunc) { + /* This shouldn't be happen. Warn user, but keep working + without bass/treble controls + */ + v4l2_info(sd, "bass/treble callbacks undefined!\n"); + desc->flags &= ~CHIP_HAS_BASSTREBLE; + } else { + chip->treble = desc->trebleinit ? + desc->trebleinit : 32768; + chip->bass = desc->bassinit ? + desc->bassinit : 32768; + chip_write(chip, desc->bassreg, + desc->bassfunc(chip->bass)); + chip_write(chip, desc->treblereg, + desc->treblefunc(chip->treble)); + } + } + + chip->thread = NULL; + init_timer(&chip->wt); + if (desc->flags & CHIP_NEED_CHECKMODE) { + if (!desc->getrxsubchans || !desc->setaudmode) { + /* This shouldn't be happen. Warn user, but keep working + without kthread + */ + v4l2_info(sd, "set/get mode callbacks undefined!\n"); + return 0; + } + /* start async thread */ + chip->wt.function = chip_thread_wake; + chip->wt.data = (unsigned long)chip; + chip->thread = kthread_run(chip_thread, chip, client->name); + if (IS_ERR(chip->thread)) { + v4l2_warn(sd, "failed to create kthread\n"); + chip->thread = NULL; + } + } + return 0; +} + +static int tvaudio_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct CHIPSTATE *chip = to_state(sd); + + del_timer_sync(&chip->wt); + if (chip->thread) { + /* shutdown async thread */ + kthread_stop(chip->thread); + chip->thread = NULL; + } + + v4l2_device_unregister_subdev(sd); + kfree(chip); + return 0; +} + +/* This driver supports many devices and the idea is to let the driver + detect which device is present. So rather than listing all supported + devices here, we pretend to support a single, fake device type. */ +static const struct i2c_device_id tvaudio_id[] = { + { "tvaudio", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvaudio_id); + +static struct i2c_driver tvaudio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tvaudio", + }, + .probe = tvaudio_probe, + .remove = tvaudio_remove, + .id_table = tvaudio_id, +}; + +module_i2c_driver(tvaudio_driver); diff --git a/drivers/media/i2c/tveeprom.c b/drivers/media/i2c/tveeprom.c new file mode 100644 index 000000000000..3b6cf034976a --- /dev/null +++ b/drivers/media/i2c/tveeprom.c @@ -0,0 +1,792 @@ +/* + * tveeprom - eeprom decoder for tvcard configuration eeproms + * + * Data and decoding routines shamelessly borrowed from bttv-cards.c + * eeprom access routine shamelessly borrowed from bttv-if.c + * which are: + + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.de) + (c) 1999-2001 Gerd Knorr + + * Adjustments to fit a more general model and all bugs: + + Copyright (C) 2003 John Klar + + * This program is free software; 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 +#include +#include + +MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); +MODULE_AUTHOR("John Klar"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define STRM(array, i) \ + (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown") + +#define tveeprom_info(fmt, arg...) \ + v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_warn(fmt, arg...) \ + v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_dbg(fmt, arg...) do { \ + if (debug) \ + v4l_printk(KERN_DEBUG, "tveeprom", \ + c->adapter, c->addr, fmt , ## arg); \ + } while (0) + +/* + * The Hauppauge eeprom uses an 8bit field to determine which + * tuner formats the tuner supports. + */ +static struct HAUPPAUGE_TUNER_FMT +{ + int id; + char *name; +} +hauppauge_tuner_fmt[] = +{ + { V4L2_STD_UNKNOWN, " UNKNOWN" }, + { V4L2_STD_UNKNOWN, " FM" }, + { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" }, + { V4L2_STD_MN, " NTSC(M)" }, + { V4L2_STD_PAL_I, " PAL(I)" }, + { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" }, + { V4L2_STD_DK, " PAL(D/D1/K)" }, + { V4L2_STD_ATSC, " ATSC/DVB Digital" }, +}; + +/* This is the full list of possible tuners. Many thanks to Hauppauge for + supplying this information. Note that many tuners where only used for + testing and never made it to the outside world. So you will only see + a subset in actual produced cards. */ +static struct HAUPPAUGE_TUNER +{ + int id; + char *name; +} +hauppauge_tuner[] = +{ + /* 0-9 */ + { TUNER_ABSENT, "None" }, + { TUNER_ABSENT, "External" }, + { TUNER_ABSENT, "Unspecified" }, + { TUNER_PHILIPS_PAL, "Philips FI1216" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, + { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, + { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, + /* 10-19 */ + { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, + { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, + { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, + { TUNER_TEMIC_PAL, "Temic 4002FH5" }, + { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, + { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, + /* 20-29 */ + { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, + { TUNER_PHILIPS_PAL, "Philips FM1216" }, + { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FM1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, + { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, + { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, + { TUNER_ABSENT, "Samsung TCPN9082D" }, + { TUNER_ABSENT, "Samsung TCPM9092P" }, + { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, + /* 30-39 */ + { TUNER_ABSENT, "Samsung TCPN9085D" }, + { TUNER_ABSENT, "Samsung TCPB9085P" }, + { TUNER_ABSENT, "Samsung TCPL9091P" }, + { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, + { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, + { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, + { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ + { TUNER_ABSENT, "Philips FI1256MP" }, + /* 40-49 */ + { TUNER_ABSENT, "Samsung TCPQ9091P" }, + { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, + { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, + { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, + { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, + { TUNER_ABSENT, "Philips TD1536D FH 44"}, + { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, + { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, + { TUNER_LG_PAL, "LG TP18PSB11D"}, + { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, + /* 50-59 */ + { TUNER_LG_PAL_I, "LG TAPC-I701D"}, + { TUNER_ABSENT, "Temic 4042FI5"}, + { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, + { TUNER_ABSENT, "LG TPI8NSR11F"}, + { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, + { TUNER_ABSENT, "Philips FI1236 MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, + { TUNER_ABSENT, "Philips FM1216MP MK3"}, + /* 60-69 */ + { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, + { TUNER_ABSENT, "LG M001D MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, + { TUNER_ABSENT, "LG M701D MK3"}, + { TUNER_ABSENT, "Temic 4146FM5"}, + { TUNER_ABSENT, "Temic 4136FY5"}, + { TUNER_ABSENT, "Temic 4106FH5"}, + { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, + /* 70-79 */ + { TUNER_ABSENT, "LG TALN H200T"}, + { TUNER_ABSENT, "LG TALN H250T"}, + { TUNER_ABSENT, "LG TALN M200T"}, + { TUNER_ABSENT, "LG TALN Z200T"}, + { TUNER_ABSENT, "LG TALN S200T"}, + { TUNER_ABSENT, "Thompson DTT7595"}, + { TUNER_ABSENT, "Thompson DTT7592"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, + { TUNER_ABSENT, "Thompson DTT757"}, + /* 80-89 */ + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, + { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, + { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, + { TUNER_TCL_2002N, "TCL 2002N 6A"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, + { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, + { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, + /* 90-99 */ + { TUNER_ABSENT, "LG TALN H202T"}, + { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, + { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, + { TUNER_ABSENT, "Philips FQ1286A MK4"}, + { TUNER_ABSENT, "Philips FQ1216ME MK5"}, + { TUNER_ABSENT, "Philips FQ1236 MK5"}, + { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, + { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, + { TUNER_ABSENT, "TCL 2002MI_3H"}, + { TUNER_TCL_2002N, "TCL 2002N 5H"}, + /* 100-109 */ + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, + { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, + { TUNER_ABSENT, "Panasonic ENV57H12D5"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, + { TUNER_ABSENT, "TCL MQNM05-4"}, + { TUNER_ABSENT, "LG TAPC-W701D"}, + { TUNER_ABSENT, "TCL 9886P-WM"}, + { TUNER_ABSENT, "TCL 1676NM-WM"}, + /* 110-119 */ + { TUNER_ABSENT, "Thompson DTT75105"}, + { TUNER_ABSENT, "Conexant_CX24109"}, + { TUNER_TCL_2002N, "TCL M2523_5N_E"}, + { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, + { TUNER_ABSENT, "Philips 8275A"}, + { TUNER_ABSENT, "Microtune MT2060"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, + { TUNER_ABSENT, "TCL M2523_3DI_E"}, + { TUNER_ABSENT, "Samsung THPD5222FG30A"}, + /* 120-129 */ + { TUNER_XC2028, "Xceive XC3028"}, + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, + { TUNER_ABSENT, "Philips FQD1216LME"}, + { TUNER_ABSENT, "Conexant CX24118A"}, + { TUNER_ABSENT, "TCL DMF11WIP"}, + { TUNER_ABSENT, "TCL MFNM05_4H_E"}, + { TUNER_ABSENT, "TCL MNM05_4H_E"}, + { TUNER_ABSENT, "TCL MPE05_2H_E"}, + { TUNER_ABSENT, "TCL MQNM05_4_U"}, + { TUNER_ABSENT, "TCL M2523_5NH_E"}, + /* 130-139 */ + { TUNER_ABSENT, "TCL M2523_3DBH_E"}, + { TUNER_ABSENT, "TCL M2523_3DIH_E"}, + { TUNER_ABSENT, "TCL MFPE05_2_U"}, + { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, + { TUNER_ABSENT, "Philips FRH2036B"}, + { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, + { TUNER_ABSENT, "MaxLinear MXL5005"}, + { TUNER_ABSENT, "MaxLinear MXL5003"}, + { TUNER_ABSENT, "Xceive XC2028"}, + { TUNER_ABSENT, "Microtune MT2131"}, + /* 140-149 */ + { TUNER_ABSENT, "Philips 8275A_8295"}, + { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, + { TUNER_ABSENT, "Microtune MT2266"}, + { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, + { TUNER_ABSENT, "LG TAPQ_H702F"}, + { TUNER_ABSENT, "TCL M09WPP_4N_E"}, + { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, + { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, + /* 150-159 */ + { TUNER_XC5000, "Xceive XC5000"}, + { TUNER_ABSENT, "Xceive XC3028L"}, + { TUNER_ABSENT, "NXP 18271C2_716x"}, + { TUNER_ABSENT, "Xceive XC4000"}, + { TUNER_ABSENT, "Dibcom 7070"}, + { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, + { TUNER_ABSENT, "Siano SMS1010"}, + { TUNER_ABSENT, "Siano SMS1150"}, + { TUNER_ABSENT, "MaxLinear 5007"}, + { TUNER_ABSENT, "TCL M09WPP_2P_E"}, + /* 160-169 */ + { TUNER_ABSENT, "Siano SMS1180"}, + { TUNER_ABSENT, "Maxim_MAX2165"}, + { TUNER_ABSENT, "Siano SMS1140"}, + { TUNER_ABSENT, "Siano SMS1150 B1"}, + { TUNER_ABSENT, "MaxLinear 111"}, + { TUNER_ABSENT, "Dibcom 7770"}, + { TUNER_ABSENT, "Siano SMS1180VNS"}, + { TUNER_ABSENT, "Siano SMS1184"}, + { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"}, + { TUNER_ABSENT, "TCL_M11WPP_2PN_E"}, + /* 170-179 */ + { TUNER_ABSENT, "MaxLinear 301"}, + { TUNER_ABSENT, "Mirics MSi001"}, + { TUNER_ABSENT, "MaxLinear MxL241SF"}, + { TUNER_XC5000C, "Xceive XC5000C"}, + { TUNER_ABSENT, "Montage M68TS2020"}, + { TUNER_ABSENT, "Siano SMS1530"}, + { TUNER_ABSENT, "Dibcom 7090"}, + { TUNER_ABSENT, "Xceive XC5200C"}, + { TUNER_ABSENT, "NXP 18273"}, + { TUNER_ABSENT, "Montage M88TS2022"}, + /* 180-189 */ + { TUNER_ABSENT, "NXP 18272M"}, + { TUNER_ABSENT, "NXP 18272S"}, +}; + +/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are + * internal to a video chip, i.e. not a separate audio chip. */ +static struct HAUPPAUGE_AUDIOIC +{ + u32 id; + char *name; +} +audioIC[] = +{ + /* 0-4 */ + { V4L2_IDENT_NONE, "None" }, + { V4L2_IDENT_UNKNOWN, "TEA6300" }, + { V4L2_IDENT_UNKNOWN, "TEA6320" }, + { V4L2_IDENT_UNKNOWN, "TDA9850" }, + { V4L2_IDENT_MSPX4XX, "MSP3400C" }, + /* 5-9 */ + { V4L2_IDENT_MSPX4XX, "MSP3410D" }, + { V4L2_IDENT_MSPX4XX, "MSP3415" }, + { V4L2_IDENT_MSPX4XX, "MSP3430" }, + { V4L2_IDENT_MSPX4XX, "MSP3438" }, + { V4L2_IDENT_UNKNOWN, "CS5331" }, + /* 10-14 */ + { V4L2_IDENT_MSPX4XX, "MSP3435" }, + { V4L2_IDENT_MSPX4XX, "MSP3440" }, + { V4L2_IDENT_MSPX4XX, "MSP3445" }, + { V4L2_IDENT_MSPX4XX, "MSP3411" }, + { V4L2_IDENT_MSPX4XX, "MSP3416" }, + /* 15-19 */ + { V4L2_IDENT_MSPX4XX, "MSP3425" }, + { V4L2_IDENT_MSPX4XX, "MSP3451" }, + { V4L2_IDENT_MSPX4XX, "MSP3418" }, + { V4L2_IDENT_UNKNOWN, "Type 0x12" }, + { V4L2_IDENT_UNKNOWN, "OKI7716" }, + /* 20-24 */ + { V4L2_IDENT_MSPX4XX, "MSP4410" }, + { V4L2_IDENT_MSPX4XX, "MSP4420" }, + { V4L2_IDENT_MSPX4XX, "MSP4440" }, + { V4L2_IDENT_MSPX4XX, "MSP4450" }, + { V4L2_IDENT_MSPX4XX, "MSP4408" }, + /* 25-29 */ + { V4L2_IDENT_MSPX4XX, "MSP4418" }, + { V4L2_IDENT_MSPX4XX, "MSP4428" }, + { V4L2_IDENT_MSPX4XX, "MSP4448" }, + { V4L2_IDENT_MSPX4XX, "MSP4458" }, + { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, + /* 30-34 */ + { V4L2_IDENT_AMBIGUOUS, "CX880" }, + { V4L2_IDENT_AMBIGUOUS, "CX881" }, + { V4L2_IDENT_AMBIGUOUS, "CX883" }, + { V4L2_IDENT_AMBIGUOUS, "CX882" }, + { V4L2_IDENT_AMBIGUOUS, "CX25840" }, + /* 35-39 */ + { V4L2_IDENT_AMBIGUOUS, "CX25841" }, + { V4L2_IDENT_AMBIGUOUS, "CX25842" }, + { V4L2_IDENT_AMBIGUOUS, "CX25843" }, + { V4L2_IDENT_AMBIGUOUS, "CX23418" }, + { V4L2_IDENT_AMBIGUOUS, "CX23885" }, + /* 40-44 */ + { V4L2_IDENT_AMBIGUOUS, "CX23888" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, + { V4L2_IDENT_AMBIGUOUS, "CX23887" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, + { V4L2_IDENT_AMBIGUOUS, "AU8522" }, +}; + +/* This list is supplied by Hauppauge. Thanks! */ +static const char *decoderIC[] = { + /* 0-4 */ + "None", "BT815", "BT817", "BT819", "BT815A", + /* 5-9 */ + "BT817A", "BT819A", "BT827", "BT829", "BT848", + /* 10-14 */ + "BT848A", "BT849A", "BT829A", "BT827A", "BT878", + /* 15-19 */ + "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115", + /* 20-24 */ + "CX880", "CX881", "CX883", "SAA7111", "SAA7113", + /* 25-29 */ + "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", + /* 30-34 */ + "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", + /* 35-39 */ + "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A", + /* 40-42 */ + "SAA7164", "CX23885B", "AU8522" +}; + +static int hasRadioTuner(int tunerType) +{ + switch (tunerType) { + case 18: /* PNPEnv_TUNER_FR1236_MK2 */ + case 23: /* PNPEnv_TUNER_FM1236 */ + case 38: /* PNPEnv_TUNER_FMR1236 */ + case 16: /* PNPEnv_TUNER_FR1216_MK2 */ + case 19: /* PNPEnv_TUNER_FR1246_MK2 */ + case 21: /* PNPEnv_TUNER_FM1216 */ + case 24: /* PNPEnv_TUNER_FM1246 */ + case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */ + case 22: /* PNPEnv_TUNER_FM1216MF */ + case 20: /* PNPEnv_TUNER_FR1256_MK2 */ + case 25: /* PNPEnv_TUNER_FM1256 */ + case 33: /* PNPEnv_TUNER_4039FR5 */ + case 42: /* PNPEnv_TUNER_4009FR5 */ + case 52: /* PNPEnv_TUNER_4049FM5 */ + case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */ + case 44: /* PNPEnv_TUNER_4009FN5 */ + case 31: /* PNPEnv_TUNER_TCPB9085P */ + case 30: /* PNPEnv_TUNER_TCPN9085D */ + case 46: /* PNPEnv_TUNER_TP18NSR01F */ + case 47: /* PNPEnv_TUNER_TP18PSB01D */ + case 49: /* PNPEnv_TUNER_TAPC_I001D */ + case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */ + case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */ + case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */ + case 58: /* PNPEnv_TUNER_FM1236_MK3 */ + case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */ + case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */ + case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */ + case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */ + case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */ + case 105: + return 1; + } + return 0; +} + +void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, + unsigned char *eeprom_data) +{ + /* ---------------------------------------------- + ** The hauppauge eeprom format is tagged + ** + ** if packet[0] == 0x84, then packet[0..1] == length + ** else length = packet[0] & 3f; + ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum + ** + ** In our (ivtv) case we're interested in the following: + ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner) + ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into + ** hauppauge_tuner_fmt) + ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM) + ** audio proc: tag [02].01 or [05].00 (mask with 0x7f) + ** decoder proc: tag [09].01) + + ** Fun info: + ** model: tag [00].07-08 or [06].00-01 + ** revision: tag [00].09-0b or [06].04-06 + ** serial#: tag [01].05-07 or [04].04-06 + + ** # of inputs/outputs ??? + */ + + int i, j, len, done, beenhere, tag, start; + + int tuner1 = 0, t_format1 = 0, audioic = -1; + char *t_name1 = NULL; + const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" }; + + int tuner2 = 0, t_format2 = 0; + char *t_name2 = NULL; + const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" }; + + memset(tvee, 0, sizeof(*tvee)); + tvee->tuner_type = TUNER_ABSENT; + tvee->tuner2_type = TUNER_ABSENT; + + done = len = beenhere = 0; + + /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */ + if (eeprom_data[0] == 0x1a && + eeprom_data[1] == 0xeb && + eeprom_data[2] == 0x67 && + eeprom_data[3] == 0x95) + start = 0xa0; /* Generic em28xx offset */ + else if ((eeprom_data[0] & 0xe1) == 0x01 && + eeprom_data[1] == 0x00 && + eeprom_data[2] == 0x00 && + eeprom_data[8] == 0x84) + start = 8; /* Generic cx2388x offset */ + else if (eeprom_data[1] == 0x70 && + eeprom_data[2] == 0x00 && + eeprom_data[4] == 0x74 && + eeprom_data[8] == 0x84) + start = 8; /* Generic cx23418 offset (models 74xxx) */ + else + start = 0; + + for (i = start; !done && i < 256; i += len) { + if (eeprom_data[i] == 0x84) { + len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); + i += 3; + } else if ((eeprom_data[i] & 0xf0) == 0x70) { + if (eeprom_data[i] & 0x08) { + /* verify checksum! */ + done = 1; + break; + } + len = eeprom_data[i] & 0x07; + ++i; + } else { + tveeprom_warn("Encountered bad packet header [%02x]. " + "Corrupt or not a Hauppauge eeprom.\n", + eeprom_data[i]); + return; + } + + if (debug) { + tveeprom_info("Tag [%02x] + %d bytes:", + eeprom_data[i], len - 1); + for (j = 1; j < len; j++) + printk(KERN_CONT " %02x", eeprom_data[i + j]); + printk(KERN_CONT "\n"); + } + + /* process by tag */ + tag = eeprom_data[i]; + switch (tag) { + case 0x00: + /* tag: 'Comprehensive' */ + tuner1 = eeprom_data[i+6]; + t_format1 = eeprom_data[i+5]; + tvee->has_radio = eeprom_data[i+len-1]; + /* old style tag, don't know how to detect + IR presence, mark as unknown. */ + tvee->has_ir = 0; + tvee->model = + eeprom_data[i+8] + + (eeprom_data[i+9] << 8); + tvee->revision = eeprom_data[i+10] + + (eeprom_data[i+11] << 8) + + (eeprom_data[i+12] << 16); + break; + + case 0x01: + /* tag: 'SerialID' */ + tvee->serial_number = + eeprom_data[i+6] + + (eeprom_data[i+7] << 8) + + (eeprom_data[i+8] << 16); + break; + + case 0x02: + /* tag 'AudioInfo' + Note mask with 0x7F, high bit used on some older models + to indicate 4052 mux was removed in favor of using MSP + inputs directly. */ + audioic = eeprom_data[i+2] & 0x7f; + if (audioic < ARRAY_SIZE(audioIC)) + tvee->audio_processor = audioIC[audioic].id; + else + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + break; + + /* case 0x03: tag 'EEInfo' */ + + case 0x04: + /* tag 'SerialID2' */ + tvee->serial_number = + eeprom_data[i+5] + + (eeprom_data[i+6] << 8) + + (eeprom_data[i+7] << 16); + + if ((eeprom_data[i + 8] & 0xf0) && + (tvee->serial_number < 0xffffff)) { + tvee->MAC_address[0] = 0x00; + tvee->MAC_address[1] = 0x0D; + tvee->MAC_address[2] = 0xFE; + tvee->MAC_address[3] = eeprom_data[i + 7]; + tvee->MAC_address[4] = eeprom_data[i + 6]; + tvee->MAC_address[5] = eeprom_data[i + 5]; + tvee->has_MAC_address = 1; + } + break; + + case 0x05: + /* tag 'Audio2' + Note mask with 0x7F, high bit used on some older models + to indicate 4052 mux was removed in favor of using MSP + inputs directly. */ + audioic = eeprom_data[i+1] & 0x7f; + if (audioic < ARRAY_SIZE(audioIC)) + tvee->audio_processor = audioIC[audioic].id; + else + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + + break; + + case 0x06: + /* tag 'ModelRev' */ + tvee->model = + eeprom_data[i + 1] + + (eeprom_data[i + 2] << 8) + + (eeprom_data[i + 3] << 16) + + (eeprom_data[i + 4] << 24); + tvee->revision = + eeprom_data[i + 5] + + (eeprom_data[i + 6] << 8) + + (eeprom_data[i + 7] << 16); + break; + + case 0x07: + /* tag 'Details': according to Hauppauge not interesting + on any PCI-era or later boards. */ + break; + + /* there is no tag 0x08 defined */ + + case 0x09: + /* tag 'Video' */ + tvee->decoder_processor = eeprom_data[i + 1]; + break; + + case 0x0a: + /* tag 'Tuner' */ + if (beenhere == 0) { + tuner1 = eeprom_data[i + 2]; + t_format1 = eeprom_data[i + 1]; + beenhere = 1; + } else { + /* a second (radio) tuner may be present */ + tuner2 = eeprom_data[i + 2]; + t_format2 = eeprom_data[i + 1]; + /* not a TV tuner? */ + if (t_format2 == 0) + tvee->has_radio = 1; /* must be radio */ + } + break; + + case 0x0b: + /* tag 'Inputs': according to Hauppauge this is specific + to each driver family, so no good assumptions can be + made. */ + break; + + /* case 0x0c: tag 'Balun' */ + /* case 0x0d: tag 'Teletext' */ + + case 0x0e: + /* tag: 'Radio' */ + tvee->has_radio = eeprom_data[i+1]; + break; + + case 0x0f: + /* tag 'IRInfo' */ + tvee->has_ir = 1 | (eeprom_data[i+1] << 1); + break; + + /* case 0x10: tag 'VBIInfo' */ + /* case 0x11: tag 'QCInfo' */ + /* case 0x12: tag 'InfoBits' */ + + default: + tveeprom_dbg("Not sure what to do with tag [%02x]\n", + tag); + /* dump the rest of the packet? */ + } + } + + if (!done) { + tveeprom_warn("Ran out of data!\n"); + return; + } + + if (tvee->revision != 0) { + tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); + tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); + tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); + tvee->rev_str[3] = 32 + (tvee->revision & 0x3f); + tvee->rev_str[4] = 0; + } + + if (hasRadioTuner(tuner1) && !tvee->has_radio) { + tveeprom_info("The eeprom says no radio is present, but the tuner type\n"); + tveeprom_info("indicates otherwise. I will assume that radio is present.\n"); + tvee->has_radio = 1; + } + + if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { + tvee->tuner_type = hauppauge_tuner[tuner1].id; + t_name1 = hauppauge_tuner[tuner1].name; + } else { + t_name1 = "unknown"; + } + + if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { + tvee->tuner2_type = hauppauge_tuner[tuner2].id; + t_name2 = hauppauge_tuner[tuner2].name; + } else { + t_name2 = "unknown"; + } + + tvee->tuner_hauppauge_model = tuner1; + tvee->tuner2_hauppauge_model = tuner2; + tvee->tuner_formats = 0; + tvee->tuner2_formats = 0; + for (i = j = 0; i < 8; i++) { + if (t_format1 & (1 << i)) { + tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; + t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; + } + } + for (i = j = 0; i < 8; i++) { + if (t_format2 & (1 << i)) { + tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; + t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; + } + } + + tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", + tvee->model, tvee->rev_str, tvee->serial_number); + if (tvee->has_MAC_address == 1) + tveeprom_info("MAC address is %pM\n", tvee->MAC_address); + tveeprom_info("tuner model is %s (idx %d, type %d)\n", + t_name1, tuner1, tvee->tuner_type); + tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", + t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], + t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5], + t_fmt_name1[6], t_fmt_name1[7], t_format1); + if (tuner2) + tveeprom_info("second tuner model is %s (idx %d, type %d)\n", + t_name2, tuner2, tvee->tuner2_type); + if (t_format2) + tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", + t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], + t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5], + t_fmt_name2[6], t_fmt_name2[7], t_format2); + if (audioic < 0) { + tveeprom_info("audio processor is unknown (no idx)\n"); + tvee->audio_processor = V4L2_IDENT_UNKNOWN; + } else { + if (audioic < ARRAY_SIZE(audioIC)) + tveeprom_info("audio processor is %s (idx %d)\n", + audioIC[audioic].name, audioic); + else + tveeprom_info("audio processor is unknown (idx %d)\n", + audioic); + } + if (tvee->decoder_processor) + tveeprom_info("decoder processor is %s (idx %d)\n", + STRM(decoderIC, tvee->decoder_processor), + tvee->decoder_processor); + if (tvee->has_ir) + tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", + tvee->has_radio ? "" : "no ", + (tvee->has_ir & 2) ? "" : "no ", + (tvee->has_ir & 4) ? "" : "no "); + else + tveeprom_info("has %sradio\n", + tvee->has_radio ? "" : "no "); +} +EXPORT_SYMBOL(tveeprom_hauppauge_analog); + +/* ----------------------------------------------------------------------- */ +/* generic helper functions */ + +int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) +{ + unsigned char buf; + int err; + + buf = 0; + err = i2c_master_send(c, &buf, 1); + if (err != 1) { + tveeprom_info("Huh, no eeprom present (err=%d)?\n", err); + return -1; + } + err = i2c_master_recv(c, eedata, len); + if (err != len) { + tveeprom_warn("i2c eeprom read error (err=%d)\n", err); + return -1; + } + if (debug) { + int i; + + tveeprom_info("full 256-byte eeprom dump:\n"); + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + tveeprom_info("%02x:", i); + printk(KERN_CONT " %02x", eedata[i]); + if (15 == (i % 16)) + printk(KERN_CONT "\n"); + } + } + return 0; +} +EXPORT_SYMBOL(tveeprom_read); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c new file mode 100644 index 000000000000..1f3943bb87d5 --- /dev/null +++ b/drivers/media/i2c/tvp514x.c @@ -0,0 +1,1166 @@ +/* + * drivers/media/i2c/tvp514x.c + * + * TI TVP5146/47 decoder driver + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tvp514x_regs.h" + +/* Module Name */ +#define TVP514X_MODULE_NAME "tvp514x" + +/* Private macros for TVP */ +#define I2C_RETRY_COUNT (5) +#define LOCK_RETRY_COUNT (5) +#define LOCK_RETRY_DELAY (200) + +/* Debug functions */ +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); + +/* enum tvp514x_std - enum for supported standards */ +enum tvp514x_std { + STD_NTSC_MJ = 0, + STD_PAL_BDGHIN, + STD_INVALID +}; + +/** + * struct tvp514x_std_info - Structure to store standard informations + * @width: Line width in pixels + * @height:Number of active lines + * @video_std: Value to write in REG_VIDEO_STD register + * @standard: v4l2 standard structure information + */ +struct tvp514x_std_info { + unsigned long width; + unsigned long height; + u8 video_std; + struct v4l2_standard standard; +}; + +static struct tvp514x_reg tvp514x_reg_list_default[0x40]; + +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); +/** + * struct tvp514x_decoder - TVP5146/47 decoder object + * @sd: Subdevice Slave handle + * @tvp514x_regs: copy of hw's regs with preset values. + * @pdata: Board specific + * @ver: Chip version + * @streaming: TVP5146/47 decoder streaming - enabled or disabled. + * @current_std: Current standard + * @num_stds: Number of standards + * @std_list: Standards list + * @input: Input routing at chip level + * @output: Output routing at chip level + */ +struct tvp514x_decoder { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; + const struct tvp514x_platform_data *pdata; + + int ver; + int streaming; + + enum tvp514x_std current_std; + int num_stds; + const struct tvp514x_std_info *std_list; + /* Input and Output Routing parameters */ + u32 input; + u32 output; +}; + +/* TVP514x default register values */ +static struct tvp514x_reg tvp514x_reg_list_default[] = { + /* Composite selected */ + {TOK_WRITE, REG_INPUT_SEL, 0x05}, + {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, + /* Auto mode */ + {TOK_WRITE, REG_VIDEO_STD, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, + {TOK_WRITE, REG_COLOR_KILLER, 0x10}, + {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, + {TOK_WRITE, REG_BRIGHTNESS, 0x80}, + {TOK_WRITE, REG_CONTRAST, 0x80}, + {TOK_WRITE, REG_SATURATION, 0x80}, + {TOK_WRITE, REG_HUE, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, + /* Reserved */ + {TOK_SKIP, 0x0F, 0x00}, + {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, + {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, + {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, + /* Reserved */ + {TOK_SKIP, 0x13, 0x00}, + {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, + /* Reserved */ + {TOK_SKIP, 0x15, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, + {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, + /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, + {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, + {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, + {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, + {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x26, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, + {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, + /* Reserved */ + {TOK_SKIP, 0x29, 0x00}, + {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x2B, 0x00}, + {TOK_SKIP, REG_SCART_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_CONTROL, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x2F, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, + /* HS, VS active high */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, + /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, + /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, + /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, + /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, + /* Clear status */ + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, + {TOK_TERM, 0, 0}, +}; + +/** + * Supported standards - + * + * Currently supports two standards only, need to add support for rest of the + * modes, like SECAM, etc... + */ +static const struct tvp514x_std_info tvp514x_std_list[] = { + /* Standard: STD_NTSC_MJ */ + [STD_NTSC_MJ] = { + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_NTSC_MJ_BIT, + .standard = { + .index = 0, + .id = V4L2_STD_NTSC, + .name = "NTSC", + .frameperiod = {1001, 30000}, + .framelines = 525 + }, + /* Standard: STD_PAL_BDGHIN */ + }, + [STD_PAL_BDGHIN] = { + .width = PAL_NUM_ACTIVE_PIXELS, + .height = PAL_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_PAL_BDGHIN_BIT, + .standard = { + .index = 1, + .id = V4L2_STD_PAL, + .name = "PAL", + .frameperiod = {1, 25}, + .framelines = 625 + }, + }, + /* Standard: need to add for additional standard */ +}; + + +static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp514x_decoder, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd; +} + + +/** + * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) +{ + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + +read_again: + + err = i2c_smbus_read_byte_data(client, reg); + if (err < 0) { + if (retry <= I2C_RETRY_COUNT) { + v4l2_warn(sd, "Read: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto read_again; + } + } + + return err; +} + +/** + * dump_reg() - dump the register content of TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + */ +static void dump_reg(struct v4l2_subdev *sd, u8 reg) +{ + u32 val; + + val = tvp514x_read_reg(sd, reg); + v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); +} + +/** + * tvp514x_write_reg() - Write a value to a register in TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * @val: value to be written to the register + * + * Write a value to a register in an TVP5146/47 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + +write_again: + + err = i2c_smbus_write_byte_data(client, reg, val); + if (err) { + if (retry <= I2C_RETRY_COUNT) { + v4l2_warn(sd, "Write: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto write_again; + } + } + + return err; +} + +/** + * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @reglist: list of TVP5146/47 registers and values + * + * Initializes a list of TVP5146/47 registers:- + * if token is TOK_TERM, then entire write operation terminates + * if token is TOK_DELAY, then a delay of 'val' msec is introduced + * if token is TOK_SKIP, then the register write is skipped + * if token is TOK_WRITE, then the register write is performed + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_regs(struct v4l2_subdev *sd, + const struct tvp514x_reg reglist[]) +{ + int err; + const struct tvp514x_reg *next = reglist; + + for (; next->token != TOK_TERM; next++) { + if (next->token == TOK_DELAY) { + msleep(next->val); + continue; + } + + if (next->token == TOK_SKIP) + continue; + + err = tvp514x_write_reg(sd, next->reg, (u8) next->val); + if (err) { + v4l2_err(sd, "Write failed. Err[%d]\n", err); + return err; + } + } + return 0; +} + +/** + * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * + * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no + * standard detected. + */ +static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd) +{ + u8 std, std_status; + + std = tvp514x_read_reg(sd, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) + /* use the standard status register */ + std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); + else + /* use the standard register itself */ + std_status = std; + + switch (std_status & VIDEO_STD_MASK) { + case VIDEO_STD_NTSC_MJ_BIT: + return STD_NTSC_MJ; + + case VIDEO_STD_PAL_BDGHIN_BIT: + return STD_PAL_BDGHIN; + + default: + return STD_INVALID; + } + + return STD_INVALID; +} + +/* TVP5146/47 register dump function */ +static void tvp514x_reg_dump(struct v4l2_subdev *sd) +{ + dump_reg(sd, REG_INPUT_SEL); + dump_reg(sd, REG_AFE_GAIN_CTRL); + dump_reg(sd, REG_VIDEO_STD); + dump_reg(sd, REG_OPERATION_MODE); + dump_reg(sd, REG_COLOR_KILLER); + dump_reg(sd, REG_LUMA_CONTROL1); + dump_reg(sd, REG_LUMA_CONTROL2); + dump_reg(sd, REG_LUMA_CONTROL3); + dump_reg(sd, REG_BRIGHTNESS); + dump_reg(sd, REG_CONTRAST); + dump_reg(sd, REG_SATURATION); + dump_reg(sd, REG_HUE); + dump_reg(sd, REG_CHROMA_CONTROL1); + dump_reg(sd, REG_CHROMA_CONTROL2); + dump_reg(sd, REG_COMP_PR_SATURATION); + dump_reg(sd, REG_COMP_Y_CONTRAST); + dump_reg(sd, REG_COMP_PB_SATURATION); + dump_reg(sd, REG_COMP_Y_BRIGHTNESS); + dump_reg(sd, REG_AVID_START_PIXEL_LSB); + dump_reg(sd, REG_AVID_START_PIXEL_MSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); + dump_reg(sd, REG_VSYNC_START_LINE_LSB); + dump_reg(sd, REG_VSYNC_START_LINE_MSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); + dump_reg(sd, REG_VBLK_START_LINE_LSB); + dump_reg(sd, REG_VBLK_START_LINE_MSB); + dump_reg(sd, REG_VBLK_STOP_LINE_LSB); + dump_reg(sd, REG_VBLK_STOP_LINE_MSB); + dump_reg(sd, REG_SYNC_CONTROL); + dump_reg(sd, REG_OUTPUT_FORMATTER1); + dump_reg(sd, REG_OUTPUT_FORMATTER2); + dump_reg(sd, REG_OUTPUT_FORMATTER3); + dump_reg(sd, REG_OUTPUT_FORMATTER4); + dump_reg(sd, REG_OUTPUT_FORMATTER5); + dump_reg(sd, REG_OUTPUT_FORMATTER6); + dump_reg(sd, REG_CLEAR_LOST_LOCK); +} + +/** + * tvp514x_configure() - Configure the TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @decoder: ptr to tvp514x_decoder structure + * + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_configure(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) +{ + int err; + + /* common register initialization */ + err = + tvp514x_write_regs(sd, decoder->tvp514x_regs); + if (err) + return err; + + if (debug) + tvp514x_reg_dump(sd); + + return 0; +} + +/** + * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. + * @sd: pointer to standard V4L2 sub-device structure + * @decoder: pointer to tvp514x_decoder structure + * + * A device is considered to be detected if the chip ID (LSB and MSB) + * registers match the expected values. + * Any value of the rom version register is accepted. + * Returns ENODEV error number if no device is detected, or zero + * if a device is detected. + */ +static int tvp514x_detect(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) +{ + u8 chip_id_msb, chip_id_lsb, rom_ver; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); + + v4l2_dbg(1, debug, sd, + "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", + chip_id_msb, chip_id_lsb, rom_ver); + if ((chip_id_msb != TVP514X_CHIP_ID_MSB) + || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) + && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { + /* We didn't read the values we expected, so this must not be + * an TVP5146/47. + */ + v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); + return -ENODEV; + } + + decoder->ver = rom_ver; + + v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", + client->name, decoder->ver, + client->addr << 1, client->adapter->name); + return 0; +} + +/** + * tvp514x_querystd() - V4L2 decoder interface handler for querystd + * @sd: pointer to standard V4L2 sub-device structure + * @std_id: standard V4L2 std_id ioctl enum + * + * Returns the current standard detected by TVP5146/47. If no active input is + * detected then *std_id is set to 0 and the function returns 0. + */ +static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + enum tvp514x_std current_std; + enum tvp514x_input input_sel; + u8 sync_lock_status, lock_mask; + + if (std_id == NULL) + return -EINVAL; + + *std_id = V4L2_STD_UNKNOWN; + + /* query the current standard */ + current_std = tvp514x_query_current_std(sd); + if (current_std == STD_INVALID) + return 0; + + input_sel = decoder->input; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + /* check whether signal is locked */ + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); + if (lock_mask != (sync_lock_status & lock_mask)) + return 0; /* No input detected */ + + *std_id = decoder->std_list[current_std].standard.id; + + v4l2_dbg(1, debug, sd, "Current STD: %s\n", + decoder->std_list[current_std].standard.name); + return 0; +} + +/** + * tvp514x_s_std() - V4L2 decoder interface handler for s_std + * @sd: pointer to standard V4L2 sub-device structure + * @std_id: standard V4L2 v4l2_std_id ioctl enum + * + * If std_id is supported, sets the requested standard. Otherwise, returns + * -EINVAL + */ +static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + int err, i; + + for (i = 0; i < decoder->num_stds; i++) + if (std_id & decoder->std_list[i].standard.id) + break; + + if ((i == decoder->num_stds) || (i == STD_INVALID)) + return -EINVAL; + + err = tvp514x_write_reg(sd, REG_VIDEO_STD, + decoder->std_list[i].video_std); + if (err) + return err; + + decoder->current_std = i; + decoder->tvp514x_regs[REG_VIDEO_STD].val = + decoder->std_list[i].video_std; + + v4l2_dbg(1, debug, sd, "Standard set to: %s\n", + decoder->std_list[i].standard.name); + return 0; +} + +/** + * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing + * @sd: pointer to standard V4L2 sub-device structure + * @input: input selector for routing the signal + * @output: output selector for routing the signal + * @config: config value. Not used + * + * If index is valid, selects the requested input. Otherwise, returns -EINVAL if + * the input is not supported or there is no active signal present in the + * selected input. + */ +static int tvp514x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + int err; + enum tvp514x_input input_sel; + enum tvp514x_output output_sel; + u8 sync_lock_status, lock_mask; + int try_count = LOCK_RETRY_COUNT; + + if ((input >= INPUT_INVALID) || + (output >= OUTPUT_INVALID)) + /* Index out of bound */ + return -EINVAL; + + /* + * For the sequence streamon -> streamoff and again s_input + * it fails to lock the signal, since streamoff puts TVP514x + * into power off state which leads to failure in sub-sequent s_input. + * + * So power up the TVP514x device here, since it is important to lock + * the signal at this stage. + */ + if (!decoder->streaming) + tvp514x_s_stream(sd, 1); + + input_sel = input; + output_sel = output; + + err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); + if (err) + return err; + + output_sel |= tvp514x_read_reg(sd, + REG_OUTPUT_FORMATTER1) & 0x7; + err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, + output_sel); + if (err) + return err; + + decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; + + /* Clear status */ + msleep(LOCK_RETRY_DELAY); + err = + tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01); + if (err) + return err; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /* Need to add other interfaces*/ + default: + return -EINVAL; + } + + while (try_count-- > 0) { + /* Allow decoder to sync up with new input */ + msleep(LOCK_RETRY_DELAY); + + sync_lock_status = tvp514x_read_reg(sd, + REG_STATUS1); + if (lock_mask == (sync_lock_status & lock_mask)) + /* Input detected */ + break; + } + + if (try_count < 0) + return -EINVAL; + + decoder->input = input; + decoder->output = output; + + v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel); + + return 0; +} + +/** + * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl + * @ctrl: pointer to v4l2_ctrl structure + * + * If the requested control is supported, sets the control's current + * value in HW. Otherwise, returns -EINVAL if the control is not supported. + */ +static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct tvp514x_decoder *decoder = to_decoder(sd); + int err = -EINVAL, value; + + value = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); + if (!err) + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; + break; + case V4L2_CID_CONTRAST: + err = tvp514x_write_reg(sd, REG_CONTRAST, value); + if (!err) + decoder->tvp514x_regs[REG_CONTRAST].val = value; + break; + case V4L2_CID_SATURATION: + err = tvp514x_write_reg(sd, REG_SATURATION, value); + if (!err) + decoder->tvp514x_regs[REG_SATURATION].val = value; + break; + case V4L2_CID_HUE: + if (value == 180) + value = 0x7F; + else if (value == -180) + value = 0x80; + err = tvp514x_write_reg(sd, REG_HUE, value); + if (!err) + decoder->tvp514x_regs[REG_HUE].val = value; + break; + case V4L2_CID_AUTOGAIN: + err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c); + if (!err) + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; + break; + } + + v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n", + ctrl->id, ctrl->val); + return err; +} + +/** + * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @index: index of pixelcode to retrieve + * @code: receives the pixelcode + * + * Enumerates supported mediabus formats + */ +static int +tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_YUYV10_2X10; + return 0; +} + +/** + * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @f: pointer to the mediabus format structure + * + * Negotiates the image capture size and mediabus format. + */ +static int +tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + enum tvp514x_std current_std; + + if (f == NULL) + return -EINVAL; + + /* Calculate height and width based on current standard */ + current_std = decoder->current_std; + + f->code = V4L2_MBUS_FMT_YUYV10_2X10; + f->width = decoder->std_list[current_std].width; + f->height = decoder->std_list[current_std].height; + f->field = V4L2_FIELD_INTERLACED; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + + v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n", + f->width, f->height); + return 0; +} + +/** + * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm + * @sd: pointer to standard V4L2 sub-device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the decoder's video CAPTURE parameters. + */ +static int +tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + struct v4l2_captureparm *cparm; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + /* only capture is supported */ + return -EINVAL; + + /* get the current standard */ + current_std = decoder->current_std; + + cparm = &a->parm.capture; + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm + * @sd: pointer to standard V4L2 sub-device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the decoder to use the input parameters, if possible. If + * not possible, returns the appropriate error code. + */ +static int +tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = to_decoder(sd); + struct v4l2_fract *timeperframe; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + /* only capture is supported */ + return -EINVAL; + + timeperframe = &a->parm.capture.timeperframe; + + /* get the current standard */ + current_std = decoder->current_std; + + *timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable + * + * Sets streaming to enable or disable, if possible. + */ +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) +{ + int err = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tvp514x_decoder *decoder = to_decoder(sd); + + if (decoder->streaming == enable) + return 0; + + switch (enable) { + case 0: + { + /* Power Down Sequence */ + err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); + if (err) { + v4l2_err(sd, "Unable to turn off decoder\n"); + return err; + } + decoder->streaming = enable; + break; + } + case 1: + { + struct tvp514x_reg *int_seq = (struct tvp514x_reg *) + client->driver->id_table->driver_data; + + /* Power Up Sequence */ + err = tvp514x_write_regs(sd, int_seq); + if (err) { + v4l2_err(sd, "Unable to turn on decoder\n"); + return err; + } + /* Detect if not already detected */ + err = tvp514x_detect(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to detect decoder\n"); + return err; + } + err = tvp514x_configure(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to configure decoder\n"); + return err; + } + decoder->streaming = enable; + break; + } + default: + err = -ENODEV; + break; + } + + return err; +} + +static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { + .s_ctrl = tvp514x_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tvp514x_core_ops = { + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = tvp514x_s_std, +}; + +static const struct v4l2_subdev_video_ops tvp514x_video_ops = { + .s_routing = tvp514x_s_routing, + .querystd = tvp514x_querystd, + .enum_mbus_fmt = tvp514x_enum_mbus_fmt, + .g_mbus_fmt = tvp514x_mbus_fmt, + .try_mbus_fmt = tvp514x_mbus_fmt, + .s_mbus_fmt = tvp514x_mbus_fmt, + .g_parm = tvp514x_g_parm, + .s_parm = tvp514x_s_parm, + .s_stream = tvp514x_s_stream, +}; + +static const struct v4l2_subdev_ops tvp514x_ops = { + .core = &tvp514x_core_ops, + .video = &tvp514x_video_ops, +}; + +static struct tvp514x_decoder tvp514x_dev = { + .streaming = 0, + .current_std = STD_NTSC_MJ, + .std_list = tvp514x_std_list, + .num_stds = ARRAY_SIZE(tvp514x_std_list), + +}; + +/** + * tvp514x_probe() - decoder driver i2c probe handler + * @client: i2c driver client device structure + * @id: i2c driver id table + * + * Register decoder as an i2c client device and V4L2 + * device. + */ +static int +tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct tvp514x_decoder *decoder; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + if (!client->dev.platform_data) { + v4l2_err(client, "No platform data!!\n"); + return -ENODEV; + } + + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + /* Initialize the tvp514x_decoder with default configuration */ + *decoder = tvp514x_dev; + /* Copy default register configuration */ + memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, + sizeof(tvp514x_reg_list_default)); + + /* Copy board specific information here */ + decoder->pdata = client->dev.platform_data; + + /** + * Fetch platform specific data, and configure the + * tvp514x_reg_list[] accordingly. Since this is one + * time configuration, no need to preserve. + */ + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= + (decoder->pdata->clk_polarity << 1); + decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* Set default standard to auto */ + decoder->tvp514x_regs[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; + + /* Register with V4L2 layer as slave device */ + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); + + v4l2_ctrl_handler_init(&decoder->hdl, 5); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_HUE, -180, 180, 180, 0); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + + v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); + + return 0; + +} + +/** + * tvp514x_remove() - decoder driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister decoder as an i2c client device and V4L2 + * device. Complement of tvp514x_probe(). + */ +static int tvp514x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tvp514x_decoder *decoder = to_decoder(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} +/* TVP5146 Init/Power on Sequence */ +static const struct tvp514x_reg tvp5146_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, +}; + +/* TVP5147 Init/Power on Sequence */ +static const struct tvp514x_reg tvp5147_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, +}; + +/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ +static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, +}; + +/** + * I2C Device Table - + * + * name - Name of the actual device/chip. + * driver_data - Driver data + */ +static const struct i2c_device_id tvp514x_id[] = { + {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, + {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, + {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, + {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tvp514x_id); + +static struct i2c_driver tvp514x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + }, + .probe = tvp514x_probe, + .remove = tvp514x_remove, + .id_table = tvp514x_id, +}; + +module_i2c_driver(tvp514x_driver); diff --git a/drivers/media/i2c/tvp514x_regs.h b/drivers/media/i2c/tvp514x_regs.h new file mode 100644 index 000000000000..d23aa2fbb9b1 --- /dev/null +++ b/drivers/media/i2c/tvp514x_regs.h @@ -0,0 +1,287 @@ +/* + * drivers/media/i2c/tvp514x_regs.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _TVP514X_REGS_H +#define _TVP514X_REGS_H + +/* + * TVP5146/47 registers + */ +#define REG_INPUT_SEL (0x00) +#define REG_AFE_GAIN_CTRL (0x01) +#define REG_VIDEO_STD (0x02) +#define REG_OPERATION_MODE (0x03) +#define REG_AUTOSWITCH_MASK (0x04) + +#define REG_COLOR_KILLER (0x05) +#define REG_LUMA_CONTROL1 (0x06) +#define REG_LUMA_CONTROL2 (0x07) +#define REG_LUMA_CONTROL3 (0x08) + +#define REG_BRIGHTNESS (0x09) +#define REG_CONTRAST (0x0A) +#define REG_SATURATION (0x0B) +#define REG_HUE (0x0C) + +#define REG_CHROMA_CONTROL1 (0x0D) +#define REG_CHROMA_CONTROL2 (0x0E) + +/* 0x0F Reserved */ + +#define REG_COMP_PR_SATURATION (0x10) +#define REG_COMP_Y_CONTRAST (0x11) +#define REG_COMP_PB_SATURATION (0x12) + +/* 0x13 Reserved */ + +#define REG_COMP_Y_BRIGHTNESS (0x14) + +/* 0x15 Reserved */ + +#define REG_AVID_START_PIXEL_LSB (0x16) +#define REG_AVID_START_PIXEL_MSB (0x17) +#define REG_AVID_STOP_PIXEL_LSB (0x18) +#define REG_AVID_STOP_PIXEL_MSB (0x19) + +#define REG_HSYNC_START_PIXEL_LSB (0x1A) +#define REG_HSYNC_START_PIXEL_MSB (0x1B) +#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) +#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) + +#define REG_VSYNC_START_LINE_LSB (0x1E) +#define REG_VSYNC_START_LINE_MSB (0x1F) +#define REG_VSYNC_STOP_LINE_LSB (0x20) +#define REG_VSYNC_STOP_LINE_MSB (0x21) + +#define REG_VBLK_START_LINE_LSB (0x22) +#define REG_VBLK_START_LINE_MSB (0x23) +#define REG_VBLK_STOP_LINE_LSB (0x24) +#define REG_VBLK_STOP_LINE_MSB (0x25) + +/* 0x26 - 0x27 Reserved */ + +#define REG_FAST_SWTICH_CONTROL (0x28) + +/* 0x29 Reserved */ + +#define REG_FAST_SWTICH_SCART_DELAY (0x2A) + +/* 0x2B Reserved */ + +#define REG_SCART_DELAY (0x2C) +#define REG_CTI_DELAY (0x2D) +#define REG_CTI_CONTROL (0x2E) + +/* 0x2F - 0x31 Reserved */ + +#define REG_SYNC_CONTROL (0x32) +#define REG_OUTPUT_FORMATTER1 (0x33) +#define REG_OUTPUT_FORMATTER2 (0x34) +#define REG_OUTPUT_FORMATTER3 (0x35) +#define REG_OUTPUT_FORMATTER4 (0x36) +#define REG_OUTPUT_FORMATTER5 (0x37) +#define REG_OUTPUT_FORMATTER6 (0x38) +#define REG_CLEAR_LOST_LOCK (0x39) + +#define REG_STATUS1 (0x3A) +#define REG_STATUS2 (0x3B) + +#define REG_AGC_GAIN_STATUS_LSB (0x3C) +#define REG_AGC_GAIN_STATUS_MSB (0x3D) + +/* 0x3E Reserved */ + +#define REG_VIDEO_STD_STATUS (0x3F) +#define REG_GPIO_INPUT1 (0x40) +#define REG_GPIO_INPUT2 (0x41) + +/* 0x42 - 0x45 Reserved */ + +#define REG_AFE_COARSE_GAIN_CH1 (0x46) +#define REG_AFE_COARSE_GAIN_CH2 (0x47) +#define REG_AFE_COARSE_GAIN_CH3 (0x48) +#define REG_AFE_COARSE_GAIN_CH4 (0x49) + +#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) +#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) +#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) +#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) + +/* 0x52 - 0x68 Reserved */ + +#define REG_FBIT_VBIT_CONTROL1 (0x69) + +/* 0x6A - 0x6B Reserved */ + +#define REG_BACKEND_AGC_CONTROL (0x6C) + +/* 0x6D - 0x6E Reserved */ + +#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) +#define REG_ROM_VERSION (0x70) + +/* 0x71 - 0x73 Reserved */ + +#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) +#define REG_FBIT_VBIT_CONTROL2 (0x75) +#define REG_VCR_TRICK_MODE_CONTROL (0x76) +#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) +#define REG_AGC_INCREMENT_SPEED (0x78) +#define REG_AGC_INCREMENT_DELAY (0x79) + +/* 0x7A - 0x7F Reserved */ + +#define REG_CHIP_ID_MSB (0x80) +#define REG_CHIP_ID_LSB (0x81) + +/* 0x82 Reserved */ + +#define REG_CPLL_SPEED_CONTROL (0x83) + +/* 0x84 - 0x96 Reserved */ + +#define REG_STATUS_REQUEST (0x97) + +/* 0x98 - 0x99 Reserved */ + +#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) +#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) + +/* 0x9C - 0x9D Reserved */ + +#define REG_AGC_DECREMENT_DELAY (0x9E) + +/* 0x9F - 0xB0 Reserved */ + +#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) +#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) +#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) +#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) +#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) +#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) +#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) +#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) +#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) +#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) +#define REG_VDP_TTX_FILTER_CONTROL (0xBB) +#define REG_VDP_FIFO_WORD_COUNT (0xBC) +#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) + +/* 0xBE Reserved */ + +#define REG_VDP_FIFO_RESET (0xBF) +#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) +#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) +#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) +#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) + +/* 0xC4 - 0xD5 Reserved */ + +#define REG_VDP_LINE_START (0xD6) +#define REG_VDP_LINE_STOP (0xD7) +#define REG_VDP_GLOBAL_LINE_MODE (0xD8) +#define REG_VDP_FULL_FIELD_ENABLE (0xD9) +#define REG_VDP_FULL_FIELD_MODE (0xDA) + +/* 0xDB - 0xDF Reserved */ + +#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) +#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) +#define REG_FIFO_READ_DATA (0xE2) + +/* 0xE3 - 0xE7 Reserved */ + +#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) +#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) +#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) + +/* 0xEB - 0xEF Reserved */ + +#define REG_INTERRUPT_RAW_STATUS0 (0xF0) +#define REG_INTERRUPT_RAW_STATUS1 (0xF1) +#define REG_INTERRUPT_STATUS0 (0xF2) +#define REG_INTERRUPT_STATUS1 (0xF3) +#define REG_INTERRUPT_MASK0 (0xF4) +#define REG_INTERRUPT_MASK1 (0xF5) +#define REG_INTERRUPT_CLEAR0 (0xF6) +#define REG_INTERRUPT_CLEAR1 (0xF7) + +/* 0xF8 - 0xFF Reserved */ + +/* + * Mask and bit definitions of TVP5146/47 registers + */ +/* The ID values we are looking for */ +#define TVP514X_CHIP_ID_MSB (0x51) +#define TVP5146_CHIP_ID_LSB (0x46) +#define TVP5147_CHIP_ID_LSB (0x47) + +#define VIDEO_STD_MASK (0x07) +#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) +#define VIDEO_STD_NTSC_MJ_BIT (0x01) +#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) +#define VIDEO_STD_PAL_M_BIT (0x03) +#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) +#define VIDEO_STD_NTSC_4_43_BIT (0x05) +#define VIDEO_STD_SECAM_BIT (0x06) +#define VIDEO_STD_PAL_60_BIT (0x07) + +/* + * Status bit + */ +#define STATUS_TV_VCR_BIT (1<<0) +#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) +#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) +#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) +#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) +#define STATUS_FEILD_RATE_BIT (1<<5) +#define STATUS_LINE_ALTERNATING_BIT (1<<6) +#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) + +/* Tokens for register write */ +#define TOK_WRITE (0) /* token for write operation */ +#define TOK_TERM (1) /* terminating token */ +#define TOK_DELAY (2) /* delay token for reg list */ +#define TOK_SKIP (3) /* token to skip a register */ +/** + * struct tvp514x_reg - Structure for TVP5146/47 register initialization values + * @token - Token: TOK_WRITE, TOK_TERM etc.. + * @reg - Register offset + * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY + */ +struct tvp514x_reg { + u8 token; + u8 reg; + u32 val; +}; + +#endif /* ifndef _TVP514X_REGS_H */ diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c new file mode 100644 index 000000000000..a751b6c146fd --- /dev/null +++ b/drivers/media/i2c/tvp5150.c @@ -0,0 +1,1274 @@ +/* + * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver + * + * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNU General Public License v2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tvp5150_reg.h" + +#define TVP5150_H_MAX 720 +#define TVP5150_V_MAX_525_60 480 +#define TVP5150_V_MAX_OTHERS 576 +#define TVP5150_MAX_CROP_LEFT 511 +#define TVP5150_MAX_CROP_TOP 127 +#define TVP5150_CROP_SHIFT 2 + +MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); + + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +struct tvp5150 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_rect rect; + + v4l2_std_id norm; /* Current set standard */ + u32 input; + u32 output; + int enable; +}; + +static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp5150, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp5150, hdl)->sd; +} + +static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[1]; + int rc; + + buffer[0] = addr; + + rc = i2c_master_send(c, buffer, 1); + if (rc < 0) { + v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + return rc; + } + + msleep(10); + + rc = i2c_master_recv(c, buffer, 1); + if (rc < 0) { + v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + return rc; + } + + v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); + + return (buffer[0]); +} + +static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, + unsigned char value) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + unsigned char buffer[2]; + int rc; + + buffer[0] = addr; + buffer[1] = value; + v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); + if (2 != (rc = i2c_master_send(c, buffer, 2))) + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); +} + +static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, + const u8 end, int max_line) +{ + int i = 0; + + while (init != (u8)(end + 1)) { + if ((i % max_line) == 0) { + if (i > 0) + printk("\n"); + printk("tvp5150: %s reg 0x%02x = ", s, init); + } + printk("%02x ", tvp5150_read(sd, init)); + + init++; + i++; + } + printk("\n"); +} + +static int tvp5150_log_status(struct v4l2_subdev *sd) +{ + printk("tvp5150: Video input source selection #1 = 0x%02x\n", + tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); + printk("tvp5150: Analog channel controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); + printk("tvp5150: Operation mode controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_OP_MODE_CTL)); + printk("tvp5150: Miscellaneous controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_MISC_CTL)); + printk("tvp5150: Autoswitch mask= 0x%02x\n", + tvp5150_read(sd, TVP5150_AUTOSW_MSK)); + printk("tvp5150: Color killer threshold control = 0x%02x\n", + tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); + printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); + printk("tvp5150: Brightness control = 0x%02x\n", + tvp5150_read(sd, TVP5150_BRIGHT_CTL)); + printk("tvp5150: Color saturation control = 0x%02x\n", + tvp5150_read(sd, TVP5150_SATURATION_CTL)); + printk("tvp5150: Hue control = 0x%02x\n", + tvp5150_read(sd, TVP5150_HUE_CTL)); + printk("tvp5150: Contrast control = 0x%02x\n", + tvp5150_read(sd, TVP5150_CONTRAST_CTL)); + printk("tvp5150: Outputs and data rates select = 0x%02x\n", + tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); + printk("tvp5150: Configuration shared pins = 0x%02x\n", + tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); + printk("tvp5150: Active video cropping start = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); + printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); + printk("tvp5150: Genlock/RTC = 0x%02x\n", + tvp5150_read(sd, TVP5150_GENLOCK)); + printk("tvp5150: Horizontal sync start = 0x%02x\n", + tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); + printk("tvp5150: Vertical blanking start = 0x%02x\n", + tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); + printk("tvp5150: Vertical blanking stop = 0x%02x\n", + tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); + printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); + printk("tvp5150: Interrupt reset register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); + printk("tvp5150: Interrupt enable register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); + printk("tvp5150: Interrupt configuration register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); + printk("tvp5150: Video standard = 0x%02x\n", + tvp5150_read(sd, TVP5150_VIDEO_STD)); + printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", + tvp5150_read(sd, TVP5150_CB_GAIN_FACT), + tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); + printk("tvp5150: Macrovision on counter = 0x%02x\n", + tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); + printk("tvp5150: Macrovision off counter = 0x%02x\n", + tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); + printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", + (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); + printk("tvp5150: Device ID = %02x%02x\n", + tvp5150_read(sd, TVP5150_MSB_DEV_ID), + tvp5150_read(sd, TVP5150_LSB_DEV_ID)); + printk("tvp5150: ROM version = (hex) %02x.%02x\n", + tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), + tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); + printk("tvp5150: Vertical line count = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); + printk("tvp5150: Interrupt status register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); + printk("tvp5150: Interrupt active register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); + printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", + tvp5150_read(sd, TVP5150_STATUS_REG_1), + tvp5150_read(sd, TVP5150_STATUS_REG_2), + tvp5150_read(sd, TVP5150_STATUS_REG_3), + tvp5150_read(sd, TVP5150_STATUS_REG_4), + tvp5150_read(sd, TVP5150_STATUS_REG_5)); + + dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, + TVP5150_TELETEXT_FIL1_END, 8); + dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, + TVP5150_TELETEXT_FIL2_END, 8); + + printk("tvp5150: Teletext filter enable = 0x%02x\n", + tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); + printk("tvp5150: Interrupt status register A = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); + printk("tvp5150: Interrupt enable register A = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); + printk("tvp5150: Interrupt configuration = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_CONF)); + printk("tvp5150: VDP status register = 0x%02x\n", + tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); + printk("tvp5150: FIFO word count = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); + printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); + printk("tvp5150: FIFO reset = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_RESET)); + printk("tvp5150: Line number interrupt = 0x%02x\n", + tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); + printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); + printk("tvp5150: FIFO output control = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); + printk("tvp5150: Full field enable = 0x%02x\n", + tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); + printk("tvp5150: Full field mode register = 0x%02x\n", + tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); + + dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, + TVP5150_CC_DATA_END, 8); + + dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI, + TVP5150_WSS_DATA_END, 8); + + dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI, + TVP5150_VPS_DATA_END, 8); + + dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI, + TVP5150_VITC_DATA_END, 10); + + dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI, + TVP5150_LINE_MODE_END, 8); + return 0; +} + +/**************************************************************************** + Basic functions + ****************************************************************************/ + +static inline void tvp5150_selmux(struct v4l2_subdev *sd) +{ + int opmode = 0; + struct tvp5150 *decoder = to_tvp5150(sd); + int input = 0; + int val; + + if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) + input = 8; + + switch (decoder->input) { + case TVP5150_COMPOSITE1: + input |= 2; + /* fall through */ + case TVP5150_COMPOSITE0: + break; + case TVP5150_SVIDEO: + default: + input |= 1; + break; + } + + v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " + "=> tvp5150 input=%i, opmode=%i\n", + decoder->input, decoder->output, + input, opmode); + + tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); + tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); + + /* Svideo should enable YCrCb output and disable GPCL output + * For Composite and TV, it should be the reverse + */ + val = tvp5150_read(sd, TVP5150_MISC_CTL); + if (val < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", __func__, val); + return; + } + + if (decoder->input == TVP5150_SVIDEO) + val = (val & ~0x40) | 0x10; + else + val = (val & ~0x10) | 0x40; + tvp5150_write(sd, TVP5150_MISC_CTL, val); +}; + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +/* Default values as sugested at TVP5150AM1 datasheet */ +static const struct i2c_reg_value tvp5150_init_default[] = { + { /* 0x00 */ + TVP5150_VD_IN_SRC_SEL_1,0x00 + }, + { /* 0x01 */ + TVP5150_ANAL_CHL_CTL,0x15 + }, + { /* 0x02 */ + TVP5150_OP_MODE_CTL,0x00 + }, + { /* 0x03 */ + TVP5150_MISC_CTL,0x01 + }, + { /* 0x06 */ + TVP5150_COLOR_KIL_THSH_CTL,0x10 + }, + { /* 0x07 */ + TVP5150_LUMA_PROC_CTL_1,0x60 + }, + { /* 0x08 */ + TVP5150_LUMA_PROC_CTL_2,0x00 + }, + { /* 0x09 */ + TVP5150_BRIGHT_CTL,0x80 + }, + { /* 0x0a */ + TVP5150_SATURATION_CTL,0x80 + }, + { /* 0x0b */ + TVP5150_HUE_CTL,0x00 + }, + { /* 0x0c */ + TVP5150_CONTRAST_CTL,0x80 + }, + { /* 0x0d */ + TVP5150_DATA_RATE_SEL,0x47 + }, + { /* 0x0e */ + TVP5150_LUMA_PROC_CTL_3,0x00 + }, + { /* 0x0f */ + TVP5150_CONF_SHARED_PIN,0x08 + }, + { /* 0x11 */ + TVP5150_ACT_VD_CROP_ST_MSB,0x00 + }, + { /* 0x12 */ + TVP5150_ACT_VD_CROP_ST_LSB,0x00 + }, + { /* 0x13 */ + TVP5150_ACT_VD_CROP_STP_MSB,0x00 + }, + { /* 0x14 */ + TVP5150_ACT_VD_CROP_STP_LSB,0x00 + }, + { /* 0x15 */ + TVP5150_GENLOCK,0x01 + }, + { /* 0x16 */ + TVP5150_HORIZ_SYNC_START,0x80 + }, + { /* 0x18 */ + TVP5150_VERT_BLANKING_START,0x00 + }, + { /* 0x19 */ + TVP5150_VERT_BLANKING_STOP,0x00 + }, + { /* 0x1a */ + TVP5150_CHROMA_PROC_CTL_1,0x0c + }, + { /* 0x1b */ + TVP5150_CHROMA_PROC_CTL_2,0x14 + }, + { /* 0x1c */ + TVP5150_INT_RESET_REG_B,0x00 + }, + { /* 0x1d */ + TVP5150_INT_ENABLE_REG_B,0x00 + }, + { /* 0x1e */ + TVP5150_INTT_CONFIG_REG_B,0x00 + }, + { /* 0x28 */ + TVP5150_VIDEO_STD,0x00 + }, + { /* 0x2e */ + TVP5150_MACROVISION_ON_CTR,0x0f + }, + { /* 0x2f */ + TVP5150_MACROVISION_OFF_CTR,0x01 + }, + { /* 0xbb */ + TVP5150_TELETEXT_FIL_ENA,0x00 + }, + { /* 0xc0 */ + TVP5150_INT_STATUS_REG_A,0x00 + }, + { /* 0xc1 */ + TVP5150_INT_ENABLE_REG_A,0x00 + }, + { /* 0xc2 */ + TVP5150_INT_CONF,0x04 + }, + { /* 0xc8 */ + TVP5150_FIFO_INT_THRESHOLD,0x80 + }, + { /* 0xc9 */ + TVP5150_FIFO_RESET,0x00 + }, + { /* 0xca */ + TVP5150_LINE_NUMBER_INT,0x00 + }, + { /* 0xcb */ + TVP5150_PIX_ALIGN_REG_LOW,0x4e + }, + { /* 0xcc */ + TVP5150_PIX_ALIGN_REG_HIGH,0x00 + }, + { /* 0xcd */ + TVP5150_FIFO_OUT_CTRL,0x01 + }, + { /* 0xcf */ + TVP5150_FULL_FIELD_ENA,0x00 + }, + { /* 0xd0 */ + TVP5150_LINE_MODE_INI,0x00 + }, + { /* 0xfc */ + TVP5150_FULL_FIELD_MODE_REG,0x7f + }, + { /* end of data */ + 0xff,0xff + } +}; + +/* Default values as sugested at TVP5150AM1 datasheet */ +static const struct i2c_reg_value tvp5150_init_enable[] = { + { + TVP5150_CONF_SHARED_PIN, 2 + },{ /* Automatic offset and AGC enabled */ + TVP5150_ANAL_CHL_CTL, 0x15 + },{ /* Activate YCrCb output 0x9 or 0xd ? */ + TVP5150_MISC_CTL, 0x6f + },{ /* Activates video std autodetection for all standards */ + TVP5150_AUTOSW_MSK, 0x0 + },{ /* Default format: 0x47. For 4:2:2: 0x40 */ + TVP5150_DATA_RATE_SEL, 0x47 + },{ + TVP5150_CHROMA_PROC_CTL_1, 0x0c + },{ + TVP5150_CHROMA_PROC_CTL_2, 0x54 + },{ /* Non documented, but initialized on WinTV USB2 */ + 0x27, 0x20 + },{ + 0xff,0xff + } +}; + +struct tvp5150_vbi_type { + unsigned int vbi_type; + unsigned int ini_line; + unsigned int end_line; + unsigned int by_field :1; +}; + +struct i2c_vbi_ram_value { + u16 reg; + struct tvp5150_vbi_type type; + unsigned char values[16]; +}; + +/* This struct have the values for each supported VBI Standard + * by + tvp5150_vbi_types should follow the same order as vbi_ram_default + * value 0 means rom position 0x10, value 1 means rom position 0x30 + * and so on. There are 16 possible locations from 0 to 15. + */ + +static struct i2c_vbi_ram_value vbi_ram_default[] = +{ + /* FIXME: Current api doesn't handle all VBI types, those not + yet supported are placed under #if 0 */ +#if 0 + {0x010, /* Teletext, SECAM, WST System A */ + {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, + 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, +#endif + {0x030, /* Teletext, PAL, WST System B */ + {V4L2_SLICED_TELETEXT_B,6,22,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, + 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, +#if 0 + {0x050, /* Teletext, PAL, WST System C */ + {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, + 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, + {0x070, /* Teletext, NTSC, WST System B */ + {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, + 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, + {0x090, /* Tetetext, NTSC NABTS System C */ + {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, + 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } + }, + {0x0b0, /* Teletext, NTSC-J, NABTS System D */ + {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, + { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, + 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } + }, + {0x0d0, /* Closed Caption, PAL/SECAM */ + {V4L2_SLICED_CAPTION_625,22,22,1}, + { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, + 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } + }, +#endif + {0x0f0, /* Closed Caption, NTSC */ + {V4L2_SLICED_CAPTION_525,21,21,1}, + { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, + 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } + }, + {0x110, /* Wide Screen Signal, PAL/SECAM */ + {V4L2_SLICED_WSS_625,23,23,1}, + { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, + 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } + }, +#if 0 + {0x130, /* Wide Screen Signal, NTSC C */ + {V4L2_SLICED_WSS_525,20,20,1}, + { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, + 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } + }, + {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ + {V4l2_SLICED_VITC_625,6,22,0}, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, + 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } + }, + {0x170, /* Vertical Interval Timecode (VITC), NTSC */ + {V4l2_SLICED_VITC_525,10,20,0}, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, + 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } + }, +#endif + {0x190, /* Video Program System (VPS), PAL */ + {V4L2_SLICED_VPS,16,16,0}, + { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, + 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } + }, + /* 0x1d0 User programmable */ + + /* End of struct */ + { (u16)-1 } +}; + +static int tvp5150_write_inittab(struct v4l2_subdev *sd, + const struct i2c_reg_value *regs) +{ + while (regs->reg != 0xff) { + tvp5150_write(sd, regs->reg, regs->value); + regs++; + } + return 0; +} + +static int tvp5150_vdp_init(struct v4l2_subdev *sd, + const struct i2c_vbi_ram_value *regs) +{ + unsigned int i; + + /* Disable Full Field */ + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + + /* Before programming, Line mode should be at 0xff */ + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); + + /* Load Ram Table */ + while (regs->reg != (u16)-1) { + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); + + for (i = 0; i < 16; i++) + tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); + + regs++; + } + return 0; +} + +/* Fills VBI capabilities based on i2c_vbi_ram_value struct */ +static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, + struct v4l2_sliced_vbi_cap *cap) +{ + const struct i2c_vbi_ram_value *regs = vbi_ram_default; + int line; + + v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); + memset(cap, 0, sizeof *cap); + + while (regs->reg != (u16)-1 ) { + for (line=regs->type.ini_line;line<=regs->type.end_line;line++) { + cap->service_lines[0][line] |= regs->type.vbi_type; + } + cap->service_set |= regs->type.vbi_type; + + regs++; + } + return 0; +} + +/* Set vbi processing + * type - one of tvp5150_vbi_types + * line - line to gather data + * fields: bit 0 field1, bit 1, field2 + * flags (default=0xf0) is a bitmask, were set means: + * bit 7: enable filtering null bytes on CC + * bit 6: send data also to FIFO + * bit 5: don't allow data with errors on FIFO + * bit 4: enable ECC when possible + * pix_align = pix alignment: + * LSB = field1 + * MSB = field2 + */ +static int tvp5150_set_vbi(struct v4l2_subdev *sd, + const struct i2c_vbi_ram_value *regs, + unsigned int type,u8 flags, int line, + const int fields) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; + u8 reg; + int pos=0; + + if (std == V4L2_STD_ALL) { + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); + return 0; + } else if (std & V4L2_STD_625_50) { + /* Don't follow NTSC Line number convension */ + line += 3; + } + + if (line<6||line>27) + return 0; + + while (regs->reg != (u16)-1 ) { + if ((type & regs->type.vbi_type) && + (line>=regs->type.ini_line) && + (line<=regs->type.end_line)) { + type=regs->type.vbi_type; + break; + } + + regs++; + pos++; + } + if (regs->reg == (u16)-1) + return 0; + + type=pos | (flags & 0xf0); + reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; + + if (fields&1) { + tvp5150_write(sd, reg, type); + } + + if (fields&2) { + tvp5150_write(sd, reg+1, type); + } + + return type; +} + +static int tvp5150_get_vbi(struct v4l2_subdev *sd, + const struct i2c_vbi_ram_value *regs, int line) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; + u8 reg; + int pos, type = 0; + int i, ret = 0; + + if (std == V4L2_STD_ALL) { + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); + return 0; + } else if (std & V4L2_STD_625_50) { + /* Don't follow NTSC Line number convension */ + line += 3; + } + + if (line < 6 || line > 27) + return 0; + + reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; + + for (i = 0; i <= 1; i++) { + ret = tvp5150_read(sd, reg + i); + if (ret < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", + __func__, ret); + return 0; + } + pos = ret & 0x0f; + if (pos < 0x0f) + type |= regs[pos].type.vbi_type; + } + + return type; +} + +static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + int fmt = 0; + + decoder->norm = std; + + /* First tests should be against specific std */ + + if (std == V4L2_STD_ALL) { + fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */ + } else if (std & V4L2_STD_NTSC_443) { + fmt = VIDEO_STD_NTSC_4_43_BIT; + } else if (std & V4L2_STD_PAL_M) { + fmt = VIDEO_STD_PAL_M_BIT; + } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { + fmt = VIDEO_STD_PAL_COMBINATION_N_BIT; + } else { + /* Then, test against generic ones */ + if (std & V4L2_STD_NTSC) + fmt = VIDEO_STD_NTSC_MJ_BIT; + else if (std & V4L2_STD_PAL) + fmt = VIDEO_STD_PAL_BDGHIN_BIT; + else if (std & V4L2_STD_SECAM) + fmt = VIDEO_STD_SECAM_BIT; + } + + v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); + tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); + return 0; +} + +static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->norm == std) + return 0; + + /* Change cropping height limits */ + if (std & V4L2_STD_525_60) + decoder->rect.height = TVP5150_V_MAX_525_60; + else + decoder->rect.height = TVP5150_V_MAX_OTHERS; + + + return tvp5150_set_std(sd, std); +} + +static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + /* Initializes TVP5150 to its default values */ + tvp5150_write_inittab(sd, tvp5150_init_default); + + /* Initializes VDP registers */ + tvp5150_vdp_init(sd, vbi_ram_default); + + /* Selects decoder input */ + tvp5150_selmux(sd); + + /* Initializes TVP5150 to stream enabled values */ + tvp5150_write_inittab(sd, tvp5150_init_enable); + + /* Initialize image preferences */ + v4l2_ctrl_handler_setup(&decoder->hdl); + + tvp5150_set_std(sd, decoder->norm); + return 0; +}; + +static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); + return 0; + case V4L2_CID_CONTRAST: + tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); + return 0; + case V4L2_CID_SATURATION: + tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); + return 0; + case V4L2_CID_HUE: + tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); + return 0; + } + return -EINVAL; +} + +static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) +{ + int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); + + switch (val & 0x0F) { + case 0x01: + return V4L2_STD_NTSC; + case 0x03: + return V4L2_STD_PAL; + case 0x05: + return V4L2_STD_PAL_M; + case 0x07: + return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; + case 0x09: + return V4L2_STD_NTSC_443; + case 0xb: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_UYVY8_2X8; + return 0; +} + +static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *f) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + if (f == NULL) + return -EINVAL; + + tvp5150_reset(sd, 0); + + f->width = decoder->rect.width; + f->height = decoder->rect.height; + + f->code = V4L2_MBUS_FMT_UYVY8_2X8; + f->field = V4L2_FIELD_SEQ_TB; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + + v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width, + f->height); + return 0; +} + +static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct v4l2_rect rect = a->c; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + int hmax; + + v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect.left, rect.top, rect.width, rect.height); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* tvp5150 has some special limits */ + rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); + rect.width = clamp(rect.width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, + TVP5150_H_MAX - rect.left); + rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + hmax = TVP5150_V_MAX_525_60; + else + hmax = TVP5150_V_MAX_OTHERS; + + rect.height = clamp(rect.height, + hmax - TVP5150_MAX_CROP_TOP - rect.top, + hmax - rect.top); + + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, + rect.top + rect.height - hmax); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, + rect.left >> TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, + rect.left | (1 << TVP5150_CROP_SHIFT)); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, + (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> + TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, + rect.left + rect.width - TVP5150_MAX_CROP_LEFT); + + decoder->rect = rect; + + return 0; +} + +static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + + a->c = decoder->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + v4l2_std_id std; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = TVP5150_H_MAX; + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + a->bounds.height = TVP5150_V_MAX_525_60; + else + a->bounds.height = TVP5150_V_MAX_OTHERS; + + a->defrect = a->bounds; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +/**************************************************************************** + I2C Command + ****************************************************************************/ + +static int tvp5150_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + decoder->input = input; + decoder->output = output; + tvp5150_selmux(sd); + return 0; +} + +static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + /* this is for capturing 36 raw vbi lines + if there's a way to cut off the beginning 2 vbi lines + with the tvp5150 then the vbi line count could be lowered + to 17 lines/field again, although I couldn't find a register + which could do that cropping */ + if (fmt->sample_format == V4L2_PIX_FMT_GREY) + tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + if (fmt->count[0] == 18 && fmt->count[1] == 18) { + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + } + return 0; +} + +static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + int i; + + if (svbi->service_set != 0) { + for (i = 0; i <= 23; i++) { + svbi->service_lines[1][i] = 0; + svbi->service_lines[0][i] = + tvp5150_set_vbi(sd, vbi_ram_default, + svbi->service_lines[0][i], 0xf0, i, 3); + } + /* Enables FIFO */ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); + } else { + /* Disables FIFO*/ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); + + /* Disable Full Field */ + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + + /* Disable Line modes */ + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); + } + return 0; +} + +static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + int i, mask = 0; + + memset(svbi, 0, sizeof(*svbi)); + + for (i = 0; i <= 23; i++) { + svbi->service_lines[0][i] = + tvp5150_get_vbi(sd, vbi_ram_default, i); + mask |= svbi->service_lines[0][i]; + } + svbi->service_set = mask; + return 0; +} + +static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | + tvp5150_read(sd, TVP5150_ROM_MINOR_VER); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, + rev); +} + + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + int res; + + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + res = tvp5150_read(sd, reg->reg & 0xff); + if (res < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", __func__, res); + return res; + } + + reg->val = res; + reg->size = 1; + return 0; +} + +static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + int status = tvp5150_read(sd, 0x88); + + vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { + .s_ctrl = tvp5150_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tvp5150_core_ops = { + .log_status = tvp5150_log_status, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = tvp5150_s_std, + .reset = tvp5150_reset, + .g_chip_ident = tvp5150_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tvp5150_g_register, + .s_register = tvp5150_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { + .g_tuner = tvp5150_g_tuner, +}; + +static const struct v4l2_subdev_video_ops tvp5150_video_ops = { + .s_routing = tvp5150_s_routing, + .enum_mbus_fmt = tvp5150_enum_mbus_fmt, + .s_mbus_fmt = tvp5150_mbus_fmt, + .try_mbus_fmt = tvp5150_mbus_fmt, + .g_mbus_fmt = tvp5150_mbus_fmt, + .s_crop = tvp5150_s_crop, + .g_crop = tvp5150_g_crop, + .cropcap = tvp5150_cropcap, +}; + +static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { + .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, + .g_sliced_fmt = tvp5150_g_sliced_fmt, + .s_sliced_fmt = tvp5150_s_sliced_fmt, + .s_raw_fmt = tvp5150_s_raw_fmt, +}; + +static const struct v4l2_subdev_ops tvp5150_ops = { + .core = &tvp5150_core_ops, + .tuner = &tvp5150_tuner_ops, + .video = &tvp5150_video_ops, + .vbi = &tvp5150_vbi_ops, +}; + + +/**************************************************************************** + I2C Client & Driver + ****************************************************************************/ + +static int tvp5150_probe(struct i2c_client *c, + const struct i2c_device_id *id) +{ + struct tvp5150 *core; + struct v4l2_subdev *sd; + int tvp5150_id[4]; + int i, res; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(c->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); + if (!core) { + return -ENOMEM; + } + sd = &core->sd; + v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + + /* + * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, + * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER + */ + for (i = 0; i < 4; i++) { + res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); + if (res < 0) + goto free_core; + tvp5150_id[i] = res; + } + + v4l_info(c, "chip found @ 0x%02x (%s)\n", + c->addr << 1, c->adapter->name); + + if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */ + v4l2_info(sd, "tvp%02x%02xam1 detected.\n", + tvp5150_id[0], tvp5150_id[1]); + + /* ITU-T BT.656.4 timing */ + tvp5150_write(sd, TVP5150_REV_SELECT, 0); + } else { + /* Is TVP5150A */ + if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { + v4l2_info(sd, "tvp%02x%02xa detected.\n", + tvp5150_id[2], tvp5150_id[3]); + } else { + v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", + tvp5150_id[2], tvp5150_id[3]); + v4l2_info(sd, "*** Rom ver is %d.%d\n", + tvp5150_id[2], tvp5150_id[3]); + } + } + + core->norm = V4L2_STD_ALL; /* Default is autodetect */ + core->input = TVP5150_COMPOSITE1; + core->enable = 1; + + v4l2_ctrl_handler_init(&core->hdl, 4); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &core->hdl; + if (core->hdl.error) { + res = core->hdl.error; + v4l2_ctrl_handler_free(&core->hdl); + goto free_core; + } + v4l2_ctrl_handler_setup(&core->hdl); + + /* Default is no cropping */ + core->rect.top = 0; + if (tvp5150_read_std(sd) & V4L2_STD_525_60) + core->rect.height = TVP5150_V_MAX_525_60; + else + core->rect.height = TVP5150_V_MAX_OTHERS; + core->rect.left = 0; + core->rect.width = TVP5150_H_MAX; + + if (debug > 1) + tvp5150_log_status(sd); + return 0; + +free_core: + kfree(core); + return res; +} + +static int tvp5150_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct tvp5150 *decoder = to_tvp5150(sd); + + v4l2_dbg(1, debug, sd, + "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", + c->addr << 1); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(to_tvp5150(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id tvp5150_id[] = { + { "tvp5150", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvp5150_id); + +static struct i2c_driver tvp5150_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tvp5150", + }, + .probe = tvp5150_probe, + .remove = tvp5150_remove, + .id_table = tvp5150_id, +}; + +module_i2c_driver(tvp5150_driver); diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h new file mode 100644 index 000000000000..25a994944918 --- /dev/null +++ b/drivers/media/i2c/tvp5150_reg.h @@ -0,0 +1,139 @@ +/* + * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers + * + * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNU General Public License v2 + */ + +#define TVP5150_VD_IN_SRC_SEL_1 0x00 /* Video input source selection #1 */ +#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */ +#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */ +#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */ +#define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */ + +/* Reserved 05h */ + +#define TVP5150_COLOR_KIL_THSH_CTL 0x06 /* Color killer threshold control */ +#define TVP5150_LUMA_PROC_CTL_1 0x07 /* Luminance processing control #1 */ +#define TVP5150_LUMA_PROC_CTL_2 0x08 /* Luminance processing control #2 */ +#define TVP5150_BRIGHT_CTL 0x09 /* Brightness control */ +#define TVP5150_SATURATION_CTL 0x0a /* Color saturation control */ +#define TVP5150_HUE_CTL 0x0b /* Hue control */ +#define TVP5150_CONTRAST_CTL 0x0c /* Contrast control */ +#define TVP5150_DATA_RATE_SEL 0x0d /* Outputs and data rates select */ +#define TVP5150_LUMA_PROC_CTL_3 0x0e /* Luminance processing control #3 */ +#define TVP5150_CONF_SHARED_PIN 0x0f /* Configuration shared pins */ + +/* Reserved 10h */ + +#define TVP5150_ACT_VD_CROP_ST_MSB 0x11 /* Active video cropping start MSB */ +#define TVP5150_ACT_VD_CROP_ST_LSB 0x12 /* Active video cropping start LSB */ +#define TVP5150_ACT_VD_CROP_STP_MSB 0x13 /* Active video cropping stop MSB */ +#define TVP5150_ACT_VD_CROP_STP_LSB 0x14 /* Active video cropping stop LSB */ +#define TVP5150_GENLOCK 0x15 /* Genlock/RTC */ +#define TVP5150_HORIZ_SYNC_START 0x16 /* Horizontal sync start */ + +/* Reserved 17h */ + +#define TVP5150_VERT_BLANKING_START 0x18 /* Vertical blanking start */ +#define TVP5150_VERT_BLANKING_STOP 0x19 /* Vertical blanking stop */ +#define TVP5150_CHROMA_PROC_CTL_1 0x1a /* Chrominance processing control #1 */ +#define TVP5150_CHROMA_PROC_CTL_2 0x1b /* Chrominance processing control #2 */ +#define TVP5150_INT_RESET_REG_B 0x1c /* Interrupt reset register B */ +#define TVP5150_INT_ENABLE_REG_B 0x1d /* Interrupt enable register B */ +#define TVP5150_INTT_CONFIG_REG_B 0x1e /* Interrupt configuration register B */ + +/* Reserved 1Fh-27h */ + +#define VIDEO_STD_MASK (0x07 >> 1) +#define TVP5150_VIDEO_STD 0x28 /* Video standard */ +#define VIDEO_STD_AUTO_SWITCH_BIT 0x00 +#define VIDEO_STD_NTSC_MJ_BIT 0x02 +#define VIDEO_STD_PAL_BDGHIN_BIT 0x04 +#define VIDEO_STD_PAL_M_BIT 0x06 +#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08 +#define VIDEO_STD_NTSC_4_43_BIT 0x0a +#define VIDEO_STD_SECAM_BIT 0x0c + +#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01 +#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03 +#define VIDEO_STD_PAL_M_BIT_AS 0x05 +#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07 +#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09 +#define VIDEO_STD_SECAM_BIT_AS 0x0b + +/* Reserved 29h-2bh */ + +#define TVP5150_CB_GAIN_FACT 0x2c /* Cb gain factor */ +#define TVP5150_CR_GAIN_FACTOR 0x2d /* Cr gain factor */ +#define TVP5150_MACROVISION_ON_CTR 0x2e /* Macrovision on counter */ +#define TVP5150_MACROVISION_OFF_CTR 0x2f /* Macrovision off counter */ +#define TVP5150_REV_SELECT 0x30 /* revision select (TVP5150AM1 only) */ + +/* Reserved 31h-7Fh */ + +#define TVP5150_MSB_DEV_ID 0x80 /* MSB of device ID */ +#define TVP5150_LSB_DEV_ID 0x81 /* LSB of device ID */ +#define TVP5150_ROM_MAJOR_VER 0x82 /* ROM major version */ +#define TVP5150_ROM_MINOR_VER 0x83 /* ROM minor version */ +#define TVP5150_VERT_LN_COUNT_MSB 0x84 /* Vertical line count MSB */ +#define TVP5150_VERT_LN_COUNT_LSB 0x85 /* Vertical line count LSB */ +#define TVP5150_INT_STATUS_REG_B 0x86 /* Interrupt status register B */ +#define TVP5150_INT_ACTIVE_REG_B 0x87 /* Interrupt active register B */ +#define TVP5150_STATUS_REG_1 0x88 /* Status register #1 */ +#define TVP5150_STATUS_REG_2 0x89 /* Status register #2 */ +#define TVP5150_STATUS_REG_3 0x8a /* Status register #3 */ +#define TVP5150_STATUS_REG_4 0x8b /* Status register #4 */ +#define TVP5150_STATUS_REG_5 0x8c /* Status register #5 */ +/* Reserved 8Dh-8Fh */ + /* Closed caption data registers */ +#define TVP5150_CC_DATA_INI 0x90 +#define TVP5150_CC_DATA_END 0x93 + + /* WSS data registers */ +#define TVP5150_WSS_DATA_INI 0x94 +#define TVP5150_WSS_DATA_END 0x99 + +/* VPS data registers */ +#define TVP5150_VPS_DATA_INI 0x9a +#define TVP5150_VPS_DATA_END 0xa6 + +/* VITC data registers */ +#define TVP5150_VITC_DATA_INI 0xa7 +#define TVP5150_VITC_DATA_END 0xaf + +#define TVP5150_VBI_FIFO_READ_DATA 0xb0 /* VBI FIFO read data */ + +/* Teletext filter 1 */ +#define TVP5150_TELETEXT_FIL1_INI 0xb1 +#define TVP5150_TELETEXT_FIL1_END 0xb5 + +/* Teletext filter 2 */ +#define TVP5150_TELETEXT_FIL2_INI 0xb6 +#define TVP5150_TELETEXT_FIL2_END 0xba + +#define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */ +/* Reserved BCh-BFh */ +#define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */ +#define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */ +#define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */ +#define TVP5150_VDP_CONF_RAM_DATA 0xc3 /* VDP configuration RAM data */ +#define TVP5150_CONF_RAM_ADDR_LOW 0xc4 /* Configuration RAM address low byte */ +#define TVP5150_CONF_RAM_ADDR_HIGH 0xc5 /* Configuration RAM address high byte */ +#define TVP5150_VDP_STATUS_REG 0xc6 /* VDP status register */ +#define TVP5150_FIFO_WORD_COUNT 0xc7 /* FIFO word count */ +#define TVP5150_FIFO_INT_THRESHOLD 0xc8 /* FIFO interrupt threshold */ +#define TVP5150_FIFO_RESET 0xc9 /* FIFO reset */ +#define TVP5150_LINE_NUMBER_INT 0xca /* Line number interrupt */ +#define TVP5150_PIX_ALIGN_REG_LOW 0xcb /* Pixel alignment register low byte */ +#define TVP5150_PIX_ALIGN_REG_HIGH 0xcc /* Pixel alignment register high byte */ +#define TVP5150_FIFO_OUT_CTRL 0xcd /* FIFO output control */ +/* Reserved CEh */ +#define TVP5150_FULL_FIELD_ENA 0xcf /* Full field enable 1 */ + +/* Line mode registers */ +#define TVP5150_LINE_MODE_INI 0xd0 +#define TVP5150_LINE_MODE_END 0xfb + +#define TVP5150_FULL_FIELD_MODE_REG 0xfc /* Full field mode register */ +/* Reserved FDh-FFh */ diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c new file mode 100644 index 000000000000..fb6a5b57eb83 --- /dev/null +++ b/drivers/media/i2c/tvp7002.c @@ -0,0 +1,1145 @@ +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics + * Digitizer with Horizontal PLL registers + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Santiago Nunez-Corrales + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath + * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by + * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). + * + * This program is free software; 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 +#include +#include +#include "tvp7002_reg.h" + +MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); +MODULE_AUTHOR("Santiago Nunez-Corrales "); +MODULE_LICENSE("GPL"); + +/* Module Name */ +#define TVP7002_MODULE_NAME "tvp7002" + +/* I2C retry attempts */ +#define I2C_RETRY_COUNT (5) + +/* End of registers */ +#define TVP7002_EOR 0x5c + +/* Read write definition for registers */ +#define TVP7002_READ 0 +#define TVP7002_WRITE 1 +#define TVP7002_RESERVED 2 + +/* Interlaced vs progressive mask and shift */ +#define TVP7002_IP_SHIFT 5 +#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) + +/* Shift for CPL and LPF registers */ +#define TVP7002_CL_SHIFT 8 +#define TVP7002_CL_MASK 0x0f + +/* Debug functions */ +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +/* Structure for register values */ +struct i2c_reg_value { + u8 reg; + u8 value; + u8 type; +}; + +/* + * Register default values (according to tvp7002 datasheet) + * In the case of read-only registers, the value (0xff) is + * never written. R/W functionality is controlled by the + * writable bit in the register struct definition. + */ +static const struct i2c_reg_value tvp7002_init_default[] = { + { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, + { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, + { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, + { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, + { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, + { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, + { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, + { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, + { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, + { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, + { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, + { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, + { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, + { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, + { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, + { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, + { 0x29, 0x08, TVP7002_RESERVED }, + { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, + /* PWR_CTL is controlled only by the probe and reset functions */ + { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, + { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, + { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, + { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, + { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, + { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { 0x32, 0x18, TVP7002_RESERVED }, + { 0x33, 0x60, TVP7002_RESERVED }, + { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, + { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, + { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, + { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, + { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, + { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, + { 0x3e, 0x60, TVP7002_RESERVED }, + { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, + { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, + { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, + { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, + { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, + { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, + { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, + { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, + { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, + { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, + { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, + { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, + { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, + { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, + { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, + /* This signals end of register values */ + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 480P */ +static const struct i2c_reg_value tvp7002_parms_480P[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 576P */ +static const struct i2c_reg_value tvp7002_parms_576P[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080I60 */ +static const struct i2c_reg_value tvp7002_parms_1080I60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080P60 */ +static const struct i2c_reg_value tvp7002_parms_1080P60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080I50 */ +static const struct i2c_reg_value tvp7002_parms_1080I50[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 720P60 */ +static const struct i2c_reg_value tvp7002_parms_720P60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 720P50 */ +static const struct i2c_reg_value tvp7002_parms_720P50[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Preset definition for handling device operation */ +struct tvp7002_preset_definition { + u32 preset; + struct v4l2_dv_timings timings; + const struct i2c_reg_value *p_settings; + enum v4l2_colorspace color_space; + enum v4l2_field scanmode; + u16 progressive; + u16 lines_per_frame; + u16 cpl_min; + u16 cpl_max; +}; + +/* Struct list for digital video presets */ +static const struct tvp7002_preset_definition tvp7002_presets[] = { + { + V4L2_DV_720P60, + V4L2_DV_BT_CEA_1280X720P60, + tvp7002_parms_720P60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x2EE, + 135, + 153 + }, + { + V4L2_DV_1080I60, + V4L2_DV_BT_CEA_1920X1080I60, + tvp7002_parms_1080I60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_INTERLACED, + 0, + 0x465, + 181, + 205 + }, + { + V4L2_DV_1080I50, + V4L2_DV_BT_CEA_1920X1080I50, + tvp7002_parms_1080I50, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_INTERLACED, + 0, + 0x465, + 217, + 245 + }, + { + V4L2_DV_720P50, + V4L2_DV_BT_CEA_1280X720P50, + tvp7002_parms_720P50, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x2EE, + 163, + 183 + }, + { + V4L2_DV_1080P60, + V4L2_DV_BT_CEA_1920X1080P60, + tvp7002_parms_1080P60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x465, + 90, + 102 + }, + { + V4L2_DV_480P59_94, + V4L2_DV_BT_CEA_720X480P59_94, + tvp7002_parms_480P, + V4L2_COLORSPACE_SMPTE170M, + V4L2_FIELD_NONE, + 1, + 0x20D, + 0xffff, + 0xffff + }, + { + V4L2_DV_576P50, + V4L2_DV_BT_CEA_720X576P50, + tvp7002_parms_576P, + V4L2_COLORSPACE_SMPTE170M, + V4L2_FIELD_NONE, + 1, + 0x271, + 0xffff, + 0xffff + } +}; + +#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets) + +/* Device definition */ +struct tvp7002 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + const struct tvp7002_config *pdata; + + int ver; + int streaming; + + const struct tvp7002_preset_definition *current_preset; +}; + +/* + * to_tvp7002 - Obtain device handler TVP7002 + * @sd: ptr to v4l2_subdev struct + * + * Returns device handler tvp7002. + */ +static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp7002, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; +} + +/* + * tvp7002_read - Read a value from a register in an TVP7002 + * @sd: ptr to v4l2_subdev struct + * @addr: TVP7002 register address + * @dst: pointer to 8-bit destination + * + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + int retry; + int error; + + for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { + error = i2c_smbus_read_byte_data(c, addr); + + if (error >= 0) { + *dst = (u8)error; + return 0; + } + + msleep_interruptible(10); + } + v4l2_err(sd, "TVP7002 read error %d\n", error); + return error; +} + +/* + * tvp7002_read_err() - Read a register value with error code + * @sd: pointer to standard V4L2 sub-device structure + * @reg: destination register + * @val: value to be read + * @err: pointer to error value + * + * Read a value in a register and save error value in pointer. + * Also update the register table if successful + */ +static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, + u8 *dst, int *err) +{ + if (!*err) + *err = tvp7002_read(sd, reg, dst); +} + +/* + * tvp7002_write() - Write a value to a register in TVP7002 + * @sd: ptr to v4l2_subdev struct + * @addr: TVP7002 register address + * @value: value to be written to the register + * + * Write a value to a register in an TVP7002 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) +{ + struct i2c_client *c; + int retry; + int error; + + c = v4l2_get_subdevdata(sd); + + for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { + error = i2c_smbus_write_byte_data(c, addr, value); + + if (error >= 0) + return 0; + + v4l2_warn(sd, "Write: retry ... %d\n", retry); + msleep_interruptible(10); + } + v4l2_err(sd, "TVP7002 write error %d\n", error); + return error; +} + +/* + * tvp7002_write_err() - Write a register value with error code + * @sd: pointer to standard V4L2 sub-device structure + * @reg: destination register + * @val: value to be written + * @err: pointer to error value + * + * Write a value in a register and save error value in pointer. + * Also update the register table if successful + */ +static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, + u8 val, int *err) +{ + if (!*err) + *err = tvp7002_write(sd, reg, val); +} + +/* + * tvp7002_g_chip_ident() - Get chip identification number + * @sd: ptr to v4l2_subdev struct + * @chip: ptr to v4l2_dbg_chip_ident struct + * + * Obtains the chip's identification number. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + u8 rev; + int error; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev); + + if (error < 0) + return error; + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev); +} + +/* + * tvp7002_write_inittab() - Write initialization values + * @sd: ptr to v4l2_subdev struct + * @regs: ptr to i2c_reg_value struct + * + * Write initialization values. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_write_inittab(struct v4l2_subdev *sd, + const struct i2c_reg_value *regs) +{ + int error = 0; + + /* Initialize the first (defined) registers */ + while (TVP7002_EOR != regs->reg) { + if (TVP7002_WRITE == regs->type) + tvp7002_write_err(sd, regs->reg, regs->value, &error); + regs++; + } + + return error; +} + +/* + * tvp7002_s_dv_preset() - Set digital video preset + * @sd: ptr to v4l2_subdev struct + * @dv_preset: ptr to v4l2_dv_preset struct + * + * Set the digital video preset for a TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register access fails. + */ +static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + struct tvp7002 *device = to_tvp7002(sd); + u32 preset; + int i; + + for (i = 0; i < NUM_PRESETS; i++) { + preset = tvp7002_presets[i].preset; + if (preset == dv_preset->preset) { + device->current_preset = &tvp7002_presets[i]; + return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + } + } + + return -EINVAL; +} + +static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + const struct v4l2_bt_timings *bt = &dv_timings->bt; + int i; + + if (dv_timings->type != V4L2_DV_BT_656_1120) + return -EINVAL; + for (i = 0; i < NUM_PRESETS; i++) { + const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; + + if (!memcmp(bt, t, &bt->standards - &bt->width)) { + device->current_preset = &tvp7002_presets[i]; + return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + } + } + return -EINVAL; +} + +static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + + *dv_timings = device->current_preset->timings; + return 0; +} + +/* + * tvp7002_s_ctrl() - Set a control + * @ctrl: ptr to v4l2_ctrl struct + * + * Set a control in TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register access fails. + */ +static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + int error = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); + tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); + tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); + return error; + } + return -EINVAL; +} + +/* + * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @f: pointer to mediabus format structure + * + * Negotiate the image capture size and mediabus format. + * There is only one possible format, so this single function works for + * get, set and try. + */ +static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct tvp7002 *device = to_tvp7002(sd); + struct v4l2_dv_enum_preset e_preset; + int error; + + /* Calculate height and width based on current standard */ + error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); + if (error) + return error; + + f->width = e_preset.width; + f->height = e_preset.height; + f->code = V4L2_MBUS_FMT_YUYV10_1X20; + f->field = device->current_preset->scanmode; + f->colorspace = device->current_preset->color_space; + + v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d", + f->width, f->height); + return 0; +} + +/* + * tvp7002_query_dv_preset() - query DV preset + * @sd: pointer to standard V4L2 sub-device structure + * @qpreset: standard V4L2 v4l2_dv_preset structure + * + * Returns the current DV preset by TVP7002. If no active input is + * detected, returns -EINVAL + */ +static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) +{ + const struct tvp7002_preset_definition *presets = tvp7002_presets; + u8 progressive; + u32 lpfr; + u32 cpln; + int error = 0; + u8 lpf_lsb; + u8 lpf_msb; + u8 cpl_lsb; + u8 cpl_msb; + + /* Return invalid index if no active input is detected */ + *index = NUM_PRESETS; + + /* Read standards from device registers */ + tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); + tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); + + if (error < 0) + return error; + + tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); + tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); + + if (error < 0) + return error; + + /* Get lines per frame, clocks per line and interlaced/progresive */ + lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); + cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); + progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; + + /* Do checking of video modes */ + for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) + if (lpfr == presets->lines_per_frame && + progressive == presets->progressive) { + if (presets->cpl_min == 0xffff) + break; + if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) + break; + } + + if (*index == NUM_PRESETS) { + v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", + lpfr, cpln); + return -ENOLINK; + } + + /* Update lines per frame and clocks per line info */ + v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); + return 0; +} + +static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *qpreset) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err || index == NUM_PRESETS) { + qpreset->preset = V4L2_DV_INVALID; + if (err == -ENOLINK) + err = 0; + return err; + } + qpreset->preset = tvp7002_presets[index].preset; + return 0; +} + +static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err) + return err; + *timings = tvp7002_presets[index].timings; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * tvp7002_g_register() - Get the value of a register + * @sd: ptr to v4l2_subdev struct + * @reg: ptr to v4l2_dbg_register struct + * + * Get the value of a TVP7002 decoder device register. + * Returns zero when successful, -EINVAL if register read fails or + * access to I2C client fails, -EPERM if the call is not allowed + * by disabled CAP_SYS_ADMIN. + */ +static int tvp7002_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 val; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + ret = tvp7002_read(sd, reg->reg & 0xff, &val); + reg->val = val; + return ret; +} + +/* + * tvp7002_s_register() - set a control + * @sd: ptr to v4l2_subdev struct + * @reg: ptr to v4l2_dbg_register struct + * + * Get the value of a TVP7002 decoder device register. + * Returns zero when successful, -EINVAL if register read fails or + * -EPERM if call not allowed. + */ +static int tvp7002_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); +} +#endif + +/* + * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats + * @sd: pointer to standard V4L2 sub-device structure + * @index: format index + * @code: pointer to mediabus format + * + * Enumerate supported mediabus formats. + */ + +static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + /* Check requested format index is within range */ + if (index) + return -EINVAL; + *code = V4L2_MBUS_FMT_YUYV10_1X20; + return 0; +} + +/* + * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable + * + * Sets streaming to enable or disable, if possible. + */ +static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct tvp7002 *device = to_tvp7002(sd); + int error = 0; + + if (device->streaming == enable) + return 0; + + if (enable) { + /* Set output state on (low impedance means stream on) */ + error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00); + device->streaming = enable; + } else { + /* Set output state off (high impedance means stream off) */ + error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03); + if (error) + v4l2_dbg(1, debug, sd, "Unable to stop streaming\n"); + + device->streaming = enable; + } + + return error; +} + +/* + * tvp7002_log_status() - Print information about register settings + * @sd: ptr to v4l2_subdev struct + * + * Log register values of a TVP7002 decoder device. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_log_status(struct v4l2_subdev *sd) +{ + const struct tvp7002_preset_definition *presets = tvp7002_presets; + struct tvp7002 *device = to_tvp7002(sd); + struct v4l2_dv_enum_preset e_preset; + struct v4l2_dv_preset detected; + int i; + + detected.preset = V4L2_DV_INVALID; + /* Find my current standard*/ + tvp7002_query_dv_preset(sd, &detected); + + /* Print standard related code values */ + for (i = 0; i < NUM_PRESETS; i++, presets++) + if (presets->preset == detected.preset) + break; + + if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) + return -EINVAL; + + v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); + v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); + v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); + if (i == NUM_PRESETS) { + v4l2_info(sd, "Detected DV Preset: None\n"); + } else { + if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) + return -EINVAL; + v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); + v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); + v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); + } + v4l2_info(sd, "Streaming enabled: %s\n", + device->streaming ? "yes" : "no"); + + /* Print the current value of the gain control */ + v4l2_ctrl_handler_log_status(&device->hdl, sd->name); + + return 0; +} + +/* + * tvp7002_enum_dv_presets() - Enum supported digital video formats + * @sd: pointer to standard V4L2 sub-device structure + * @preset: pointer to format struct + * + * Enumerate supported digital video formats. + */ +static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, + struct v4l2_dv_enum_preset *preset) +{ + /* Check requested format index is within range */ + if (preset->index >= NUM_PRESETS) + return -EINVAL; + + return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); +} + +static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + /* Check requested format index is within range */ + if (timings->index >= NUM_PRESETS) + return -EINVAL; + + timings->timings = tvp7002_presets[timings->index].timings; + return 0; +} + +static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { + .s_ctrl = tvp7002_s_ctrl, +}; + +/* V4L2 core operation handlers */ +static const struct v4l2_subdev_core_ops tvp7002_core_ops = { + .g_chip_ident = tvp7002_g_chip_ident, + .log_status = tvp7002_log_status, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tvp7002_g_register, + .s_register = tvp7002_s_register, +#endif +}; + +/* Specific video subsystem operation handlers */ +static const struct v4l2_subdev_video_ops tvp7002_video_ops = { + .enum_dv_presets = tvp7002_enum_dv_presets, + .s_dv_preset = tvp7002_s_dv_preset, + .query_dv_preset = tvp7002_query_dv_preset, + .g_dv_timings = tvp7002_g_dv_timings, + .s_dv_timings = tvp7002_s_dv_timings, + .enum_dv_timings = tvp7002_enum_dv_timings, + .query_dv_timings = tvp7002_query_dv_timings, + .s_stream = tvp7002_s_stream, + .g_mbus_fmt = tvp7002_mbus_fmt, + .try_mbus_fmt = tvp7002_mbus_fmt, + .s_mbus_fmt = tvp7002_mbus_fmt, + .enum_mbus_fmt = tvp7002_enum_mbus_fmt, +}; + +/* V4L2 top level operation handlers */ +static const struct v4l2_subdev_ops tvp7002_ops = { + .core = &tvp7002_core_ops, + .video = &tvp7002_video_ops, +}; + +/* + * tvp7002_probe - Probe a TVP7002 device + * @c: ptr to i2c_client struct + * @id: ptr to i2c_device_id struct + * + * Initialize the TVP7002 device + * Returns zero when successful, -EINVAL if register read fails or + * -EIO if i2c access is not available. + */ +static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct tvp7002 *device; + struct v4l2_dv_preset preset; + int polarity_a; + int polarity_b; + u8 revision; + + int error; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(c->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + if (!c->dev.platform_data) { + v4l_err(c, "No platform data!!\n"); + return -ENODEV; + } + + device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL); + + if (!device) + return -ENOMEM; + + sd = &device->sd; + device->pdata = c->dev.platform_data; + device->current_preset = tvp7002_presets; + + /* Tell v4l2 the device is ready */ + v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); + v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", + c->addr, c->adapter->name); + + error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); + if (error < 0) + goto found_error; + + /* Get revision number */ + v4l2_info(sd, "Rev. %02x detected.\n", revision); + if (revision != 0x02) + v4l2_info(sd, "Unknown revision detected.\n"); + + /* Initializes TVP7002 to its default values */ + error = tvp7002_write_inittab(sd, tvp7002_init_default); + + if (error < 0) + goto found_error; + + /* Set polarity information after registers have been set */ + polarity_a = 0x20 | device->pdata->hs_polarity << 5 + | device->pdata->vs_polarity << 2; + error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); + if (error < 0) + goto found_error; + + polarity_b = 0x01 | device->pdata->fid_polarity << 2 + | device->pdata->sog_polarity << 1 + | device->pdata->clk_polarity; + error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); + if (error < 0) + goto found_error; + + /* Set registers according to default video mode */ + preset.preset = device->current_preset->preset; + error = tvp7002_s_dv_preset(sd, &preset); + + v4l2_ctrl_handler_init(&device->hdl, 1); + v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 0); + sd->ctrl_handler = &device->hdl; + if (device->hdl.error) { + int err = device->hdl.error; + + v4l2_ctrl_handler_free(&device->hdl); + kfree(device); + return err; + } + v4l2_ctrl_handler_setup(&device->hdl); + +found_error: + if (error < 0) + kfree(device); + + return error; +} + +/* + * tvp7002_remove - Remove TVP7002 device support + * @c: ptr to i2c_client struct + * + * Reset the TVP7002 device + * Returns zero. + */ +static int tvp7002_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct tvp7002 *device = to_tvp7002(sd); + + v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" + "on address 0x%x\n", c->addr); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&device->hdl); + kfree(device); + return 0; +} + +/* I2C Device ID table */ +static const struct i2c_device_id tvp7002_id[] = { + { "tvp7002", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvp7002_id); + +/* I2C driver data */ +static struct i2c_driver tvp7002_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TVP7002_MODULE_NAME, + }, + .probe = tvp7002_probe, + .remove = tvp7002_remove, + .id_table = tvp7002_id, +}; + +module_i2c_driver(tvp7002_driver); diff --git a/drivers/media/i2c/tvp7002_reg.h b/drivers/media/i2c/tvp7002_reg.h new file mode 100644 index 000000000000..0e34ca9bccf3 --- /dev/null +++ b/drivers/media/i2c/tvp7002_reg.h @@ -0,0 +1,150 @@ +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics + * Digitizer with Horizontal PLL registers + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Santiago Nunez-Corrales + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath + * and the TVP7002 driver in the TI LSP 2.10.00.14 + * + * This program is free software; 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. + */ + +/* Naming conventions + * ------------------ + * + * FDBK: Feedback + * DIV: Divider + * CTL: Control + * SEL: Select + * IN: Input + * OUT: Output + * R: Red + * G: Green + * B: Blue + * OFF: Offset + * THRS: Threshold + * DGTL: Digital + * LVL: Level + * PWR: Power + * MVIS: Macrovision + * W: Width + * H: Height + * ALGN: Alignment + * CLK: Clocks + * TOL: Tolerance + * BWTH: Bandwidth + * COEF: Coefficient + * STAT: Status + * AUTO: Automatic + * FLD: Field + * L: Line + */ + +#define TVP7002_CHIP_REV 0x00 +#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01 +#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02 +#define TVP7002_HPLL_CRTL 0x03 +#define TVP7002_HPLL_PHASE_SEL 0x04 +#define TVP7002_CLAMP_START 0x05 +#define TVP7002_CLAMP_W 0x06 +#define TVP7002_HSYNC_OUT_W 0x07 +#define TVP7002_B_FINE_GAIN 0x08 +#define TVP7002_G_FINE_GAIN 0x09 +#define TVP7002_R_FINE_GAIN 0x0a +#define TVP7002_B_FINE_OFF_MSBS 0x0b +#define TVP7002_G_FINE_OFF_MSBS 0x0c +#define TVP7002_R_FINE_OFF_MSBS 0x0d +#define TVP7002_SYNC_CTL_1 0x0e +#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f +#define TVP7002_SYNC_ON_G_THRS 0x10 +#define TVP7002_SYNC_SEPARATOR_THRS 0x11 +#define TVP7002_HPLL_PRE_COAST 0x12 +#define TVP7002_HPLL_POST_COAST 0x13 +#define TVP7002_SYNC_DETECT_STAT 0x14 +#define TVP7002_OUT_FORMATTER 0x15 +#define TVP7002_MISC_CTL_1 0x16 +#define TVP7002_MISC_CTL_2 0x17 +#define TVP7002_MISC_CTL_3 0x18 +#define TVP7002_IN_MUX_SEL_1 0x19 +#define TVP7002_IN_MUX_SEL_2 0x1a +#define TVP7002_B_AND_G_COARSE_GAIN 0x1b +#define TVP7002_R_COARSE_GAIN 0x1c +#define TVP7002_FINE_OFF_LSBS 0x1d +#define TVP7002_B_COARSE_OFF 0x1e +#define TVP7002_G_COARSE_OFF 0x1f +#define TVP7002_R_COARSE_OFF 0x20 +#define TVP7002_HSOUT_OUT_START 0x21 +#define TVP7002_MISC_CTL_4 0x22 +#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23 +#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24 +#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25 +#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26 +#define TVP7002_DGTL_ALC_OUT_MSBS 0x27 +#define TVP7002_AUTO_LVL_CTL_FILTER 0x28 +/* Reserved 0x29*/ +#define TVP7002_FINE_CLAMP_CTL 0x2a +#define TVP7002_PWR_CTL 0x2b +#define TVP7002_ADC_SETUP 0x2c +#define TVP7002_COARSE_CLAMP_CTL 0x2d +#define TVP7002_SOG_CLAMP 0x2e +#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f +#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30 +#define TVP7002_ALC_PLACEMENT 0x31 +/* Reserved 0x32 */ +/* Reserved 0x33 */ +#define TVP7002_MVIS_STRIPPER_W 0x34 +#define TVP7002_VSYNC_ALGN 0x35 +#define TVP7002_SYNC_BYPASS 0x36 +#define TVP7002_L_FRAME_STAT_LSBS 0x37 +#define TVP7002_L_FRAME_STAT_MSBS 0x38 +#define TVP7002_CLK_L_STAT_LSBS 0x39 +#define TVP7002_CLK_L_STAT_MSBS 0x3a +#define TVP7002_HSYNC_W 0x3b +#define TVP7002_VSYNC_W 0x3c +#define TVP7002_L_LENGTH_TOL 0x3d +/* Reserved 0x3e */ +#define TVP7002_VIDEO_BWTH_CTL 0x3f +#define TVP7002_AVID_START_PIXEL_LSBS 0x40 +#define TVP7002_AVID_START_PIXEL_MSBS 0x41 +#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42 +#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43 +#define TVP7002_VBLK_F_0_START_L_OFF 0x44 +#define TVP7002_VBLK_F_1_START_L_OFF 0x45 +#define TVP7002_VBLK_F_0_DURATION 0x46 +#define TVP7002_VBLK_F_1_DURATION 0x47 +#define TVP7002_FBIT_F_0_START_L_OFF 0x48 +#define TVP7002_FBIT_F_1_START_L_OFF 0x49 +#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a +#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b +#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c +#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d +#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e +#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f +#define TVP7002_YUV_U_G_COEF_LSBS 0x50 +#define TVP7002_YUV_U_G_COEF_MSBS 0x51 +#define TVP7002_YUV_U_B_COEF_LSBS 0x52 +#define TVP7002_YUV_U_B_COEF_MSBS 0x53 +#define TVP7002_YUV_U_R_COEF_LSBS 0x54 +#define TVP7002_YUV_U_R_COEF_MSBS 0x55 +#define TVP7002_YUV_V_G_COEF_LSBS 0x56 +#define TVP7002_YUV_V_G_COEF_MSBS 0x57 +#define TVP7002_YUV_V_B_COEF_LSBS 0x58 +#define TVP7002_YUV_V_B_COEF_MSBS 0x59 +#define TVP7002_YUV_V_R_COEF_LSBS 0x5a +#define TVP7002_YUV_V_R_COEF_MSBS 0x5b + diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c new file mode 100644 index 000000000000..1e7446542091 --- /dev/null +++ b/drivers/media/i2c/upd64031a.c @@ -0,0 +1,274 @@ +/* + * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan + * + * 2003 by T.Adachi + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * This program is free software; 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 + +/* --------------------- read registers functions define -------------------- */ + +/* bit masks */ +#define GR_MODE_MASK 0xc0 +#define DIRECT_3DYCS_CONNECT_MASK 0xc0 +#define SYNC_CIRCUIT_MASK 0xa0 + +/* -------------------------------------------------------------------------- */ + +MODULE_DESCRIPTION("uPD64031A driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + /* unused registers + R10, R11, R12, R13, R14, + R15, R16, R17, + */ + TOT_REGS +}; + +struct upd64031a_state { + struct v4l2_subdev sd; + u8 regs[TOT_REGS]; + u8 gr_mode; + u8 direct_3dycs_connect; + u8 ext_comp_sync; + u8 ext_vert_sync; +}; + +static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64031a_state, sd); +} + +static u8 upd64031a_init[] = { + 0x00, 0xb8, 0x48, 0xd2, 0xe6, + 0x03, 0x10, 0x0b, 0xaf, 0x7f, + 0x00, 0x00, 0x1d, 0x5e, 0x00, + 0xd0 +}; + +/* ------------------------------------------------------------------------ */ + +static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, 2); + return buf[reg]; +} + +/* ------------------------------------------------------------------------ */ + +static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +/* The input changed due to new input or channel changed */ +static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct upd64031a_state *state = to_state(sd); + u8 reg = state->regs[R00]; + + v4l2_dbg(1, debug, sd, "changed input or channel\n"); + upd64031a_write(sd, R00, reg | 0x10); + upd64031a_write(sd, R00, reg & ~0x10); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int upd64031a_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct upd64031a_state *state = to_state(sd); + u8 r00, r05, r08; + + state->gr_mode = (input & 3) << 6; + state->direct_3dycs_connect = (input & 0xc) << 4; + state->ext_comp_sync = + (input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + state->ext_vert_sync = + (input & UPD64031A_VERTICAL_EXTERNAL) << 2; + r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; + r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | + state->ext_comp_sync | state->ext_vert_sync; + r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | + state->direct_3dycs_connect; + upd64031a_write(sd, R00, r00); + upd64031a_write(sd, R05, r05); + upd64031a_write(sd, R08, r08); + return upd64031a_s_frequency(sd, NULL); +} + +static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); +} + +static int upd64031a_log_status(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", + upd64031a_read(sd, 0), upd64031a_read(sd, 1)); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64031a_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops upd64031a_core_ops = { + .log_status = upd64031a_log_status, + .g_chip_ident = upd64031a_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = upd64031a_g_register, + .s_register = upd64031a_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { + .s_frequency = upd64031a_s_frequency, +}; + +static const struct v4l2_subdev_video_ops upd64031a_video_ops = { + .s_routing = upd64031a_s_routing, +}; + +static const struct v4l2_subdev_ops upd64031a_ops = { + .core = &upd64031a_core_ops, + .tuner = &upd64031a_tuner_ops, + .video = &upd64031a_video_ops, +}; + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static int upd64031a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct upd64031a_state *state; + struct v4l2_subdev *sd; + int i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); + memcpy(state->regs, upd64031a_init, sizeof(state->regs)); + state->gr_mode = UPD64031A_GR_ON << 6; + state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; + state->ext_comp_sync = state->ext_vert_sync = 0; + for (i = 0; i < TOT_REGS; i++) + upd64031a_write(sd, i, state->regs[i]); + return 0; +} + +static int upd64031a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id upd64031a_id[] = { + { "upd64031a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, upd64031a_id); + +static struct i2c_driver upd64031a_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "upd64031a", + }, + .probe = upd64031a_probe, + .remove = upd64031a_remove, + .id_table = upd64031a_id, +}; + +module_i2c_driver(upd64031a_driver); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c new file mode 100644 index 000000000000..75d6acc62018 --- /dev/null +++ b/drivers/media/i2c/upd64083.c @@ -0,0 +1,246 @@ +/* + * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver + * + * 2003 by T.Adachi (tadachi@tadachi-net.com) + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * This program is free software; 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 +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("uPD64083 driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + R10, R11, R12, R13, R14, + R15, R16, + TOT_REGS +}; + +struct upd64083_state { + struct v4l2_subdev sd; + u8 mode; + u8 ext_y_adc; + u8 regs[TOT_REGS]; +}; + +static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64083_state, sd); +} + +/* Initial values when used in combination with the + NEC upd64031a ghost reduction chip. */ +static u8 upd64083_init[] = { + 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ + 0x36, 0xdd, 0x05, 0x56, 0x48, + 0x00, 0x3a, 0xa0, 0x05, 0x08, + 0x44, 0x60, 0x08, 0x52, 0xf8, + 0x53, 0x60, 0x10 +}; + +/* ------------------------------------------------------------------------ */ + +static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[7]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, sizeof(buf)); + return buf[reg]; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int upd64083_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct upd64083_state *state = to_state(sd); + u8 r00, r02; + + if (input > 7 || (input & 6) == 6) + return -EINVAL; + state->mode = (input & 3) << 6; + state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3; + r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; + r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; + upd64083_write(sd, R00, r00); + upd64083_write(sd, R02, r02); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64083_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); +} + +static int upd64083_log_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[7]; + + i2c_master_recv(client, buf, 7); + v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " + "SA04=%02x SA05=%02x SA06=%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops upd64083_core_ops = { + .log_status = upd64083_log_status, + .g_chip_ident = upd64083_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = upd64083_g_register, + .s_register = upd64083_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops upd64083_video_ops = { + .s_routing = upd64083_s_routing, +}; + +static const struct v4l2_subdev_ops upd64083_ops = { + .core = &upd64083_core_ops, + .video = &upd64083_video_ops, +}; + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static int upd64083_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct upd64083_state *state; + struct v4l2_subdev *sd; + int i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64083_ops); + /* Initially assume that a ghost reduction chip is present */ + state->mode = 0; /* YCS mode */ + state->ext_y_adc = (1 << 5); + memcpy(state->regs, upd64083_init, TOT_REGS); + for (i = 0; i < TOT_REGS; i++) + upd64083_write(sd, i, state->regs[i]); + return 0; +} + +static int upd64083_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id upd64083_id[] = { + { "upd64083", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, upd64083_id); + +static struct i2c_driver upd64083_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "upd64083", + }, + .probe = upd64083_probe, + .remove = upd64083_remove, + .id_table = upd64083_id, +}; + +module_i2c_driver(upd64083_driver); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c new file mode 100644 index 000000000000..7cfbc9d94a48 --- /dev/null +++ b/drivers/media/i2c/vp27smpx.c @@ -0,0 +1,211 @@ +/* + * vp27smpx - driver version 0.0.1 + * + * Copyright (C) 2007 Hans Verkuil + * + * Based on a tvaudio patch from Takahiro Adachi + * and Kazuhiko Kawakami + * + * This program is free software; 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 + +MODULE_DESCRIPTION("vp27smpx driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + + +/* ----------------------------------------------------------------------- */ + +struct vp27smpx_state { + struct v4l2_subdev sd; + int radio; + u32 audmode; +}; + +static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vp27smpx_state, sd); +} + +static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) +{ + struct vp27smpx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 data[3] = { 0x00, 0x00, 0x04 }; + + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + data[1] = 0x01; + break; + case V4L2_TUNER_MODE_LANG2: + data[1] = 0x02; + break; + } + + if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) + v4l2_err(sd, "I/O error setting audmode\n"); + else + state->audmode = audmode; +} + +static int vp27smpx_s_radio(struct v4l2_subdev *sd) +{ + struct vp27smpx_state *state = to_state(sd); + + state->radio = 1; + return 0; +} + +static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct vp27smpx_state *state = to_state(sd); + + state->radio = 0; + return 0; +} + +static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); + + if (!state->radio) + vp27smpx_set_audmode(sd, vt->audmode); + return 0; +} + +static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); + + if (state->radio) + return 0; + vt->audmode = state->audmode; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} + +static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); +} + +static int vp27smpx_log_status(struct v4l2_subdev *sd) +{ + struct vp27smpx_state *state = to_state(sd); + + v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, + state->radio ? " (Radio)" : ""); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { + .log_status = vp27smpx_log_status, + .g_chip_ident = vp27smpx_g_chip_ident, + .s_std = vp27smpx_s_std, +}; + +static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { + .s_radio = vp27smpx_s_radio, + .s_tuner = vp27smpx_s_tuner, + .g_tuner = vp27smpx_g_tuner, +}; + +static const struct v4l2_subdev_ops vp27smpx_ops = { + .core = &vp27smpx_core_ops, + .tuner = &vp27smpx_tuner_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int vp27smpx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vp27smpx_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); + state->audmode = V4L2_TUNER_MODE_STEREO; + + /* initialize vp27smpx */ + vp27smpx_set_audmode(sd, state->audmode); + return 0; +} + +static int vp27smpx_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id vp27smpx_id[] = { + { "vp27smpx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vp27smpx_id); + +static struct i2c_driver vp27smpx_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vp27smpx", + }, + .probe = vp27smpx_probe, + .remove = vp27smpx_remove, + .id_table = vp27smpx_id, +}; + +module_i2c_driver(vp27smpx_driver); diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c new file mode 100644 index 000000000000..2f67b4c5c823 --- /dev/null +++ b/drivers/media/i2c/vpx3220.c @@ -0,0 +1,591 @@ +/* + * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 + * + * Copyright (C) 2001 Laurent Pinchart + * + * This program is free software; 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 +#include +#include + +MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); +MODULE_AUTHOR("Laurent Pinchart"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +#define VPX_TIMEOUT_COUNT 10 + +/* ----------------------------------------------------------------------- */ + +struct vpx3220 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + unsigned char reg[255]; + + v4l2_std_id norm; + int ident; + int input; + int enable; +}; + +static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vpx3220, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vpx3220, hdl)->sd; +} + +static char *inputs[] = { "internal", "composite", "svideo" }; + +/* ----------------------------------------------------------------------- */ + +static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct vpx3220 *decoder = i2c_get_clientdata(client); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int vpx3220_fp_status(struct v4l2_subdev *sd) +{ + unsigned char status; + unsigned int i; + + for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { + status = vpx3220_read(sd, 0x29); + + if (!(status & 4)) + return 0; + + udelay(10); + + if (need_resched()) + cond_resched(); + } + + return -1; +} + +static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* Write the 16-bit address to the FPWR register */ + if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + if (vpx3220_fp_status(sd) < 0) + return -1; + + /* Write the 16-bit data to the FPDAT register */ + if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + return 0; +} + +static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + s16 data; + + /* Write the 16-bit address to the FPRD register */ + if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + if (vpx3220_fp_status(sd) < 0) + return -1; + + /* Read the 16-bit data from the FPDAT register */ + data = i2c_smbus_read_word_data(client, 0x28); + if (data == -1) { + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); + return -1; + } + + return swab16(data); +} + +static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) +{ + u8 reg; + int ret = -1; + + while (len >= 2) { + reg = *data++; + ret = vpx3220_write(sd, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + + return ret; +} + +static int vpx3220_write_fp_block(struct v4l2_subdev *sd, + const u16 *data, unsigned int len) +{ + u8 reg; + int ret = 0; + + while (len > 1) { + reg = *data++; + ret |= vpx3220_fp_write(sd, reg, *data++); + len -= 2; + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ + +static const unsigned short init_ntsc[] = { + 0x1c, 0x00, /* NTSC tint angle */ + 0x88, 17, /* Window 1 vertical */ + 0x89, 240, /* Vertical lines in */ + 0x8a, 240, /* Vertical lines out */ + 0x8b, 000, /* Horizontal begin */ + 0x8c, 640, /* Horizontal length */ + 0x8d, 640, /* Number of pixels */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x73, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x13, /* NTSC M, composite input */ + 0xe7, 0x1e1, /* Enable vertical standard + * locking @ 240 lines */ +}; + +static const unsigned short init_pal[] = { + 0x88, 23, /* Window 1 vertical begin */ + 0x89, 288, /* Vertical lines in (16 lines + * skipped by the VFE) */ + 0x8a, 288, /* Vertical lines out (16 lines + * skipped by the VFE) */ + 0x8b, 16, /* Horizontal begin */ + 0x8c, 768, /* Horizontal length */ + 0x8d, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x77, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */ + 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ +}; + +static const unsigned short init_secam[] = { + 0x88, 23, /* Window 1 vertical begin */ + 0x89, 288, /* Vertical lines in (16 lines + * skipped by the VFE) */ + 0x8a, 288, /* Vertical lines out (16 lines + * skipped by the VFE) */ + 0x8b, 16, /* Horizontal begin */ + 0x8c, 768, /* Horizontal length */ + 0x8d, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x77, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x3d5, /* SECAM, composite input */ + 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ +}; + +static const unsigned char init_common[] = { + 0xf2, 0x00, /* Disable all outputs */ + 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN + * (clamp off) */ + 0xd8, 0xa8, /* HREF/VREF active high, VREF + * pulse = 2, Odd/Even flag */ + 0x20, 0x03, /* IF compensation 0dB/oct */ + 0xe0, 0xff, /* Open up all comparators */ + 0xe1, 0x00, + 0xe2, 0x7f, + 0xe3, 0x80, + 0xe4, 0x7f, + 0xe5, 0x80, + 0xe6, 0x00, /* Brightness set to 0 */ + 0xe7, 0xe0, /* Contrast to 1.0, noise shaping + * 10 to 8 2-bit error diffusion */ + 0xe8, 0xf8, /* YUV422, CbCr binary offset, + * ... (p.32) */ + 0xea, 0x18, /* LLC2 connected, output FIFO + * reset with VACTintern */ + 0xf0, 0x8a, /* Half full level to 10, bus + * shuffler [7:0, 23:16, 15:8] */ + 0xf1, 0x18, /* Single clock, sync mode, no + * FE delay, no HLEN counter */ + 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE# + * strength to 2 */ + 0xf9, 0x24, /* Port B, HREF, VREF, PREF & + * ALPHA strength to 4 */ +}; + +static const unsigned short init_fp[] = { + 0x59, 0, + 0xa0, 2070, /* ACC reference */ + 0xa3, 0, + 0xa4, 0, + 0xa8, 30, + 0xb2, 768, + 0xbe, 27, + 0x58, 0, + 0x26, 0, + 0x4b, 0x298, /* PLL gain */ +}; + + +static int vpx3220_init(struct v4l2_subdev *sd, u32 val) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + + vpx3220_write_block(sd, init_common, sizeof(init_common)); + vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); + if (decoder->norm & V4L2_STD_NTSC) + vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); + else if (decoder->norm & V4L2_STD_PAL) + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + else if (decoder->norm & V4L2_STD_SECAM) + vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); + else + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + return 0; +} + +static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) +{ + int res = V4L2_IN_ST_NO_SIGNAL, status; + v4l2_std_id std = 0; + + status = vpx3220_fp_read(sd, 0x0f3); + + v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); + + if (status < 0) + return status; + + if ((status & 0x20) == 0) { + res = 0; + + switch (status & 0x18) { + case 0x00: + case 0x10: + case 0x14: + case 0x18: + std = V4L2_STD_PAL; + break; + + case 0x08: + std = V4L2_STD_SECAM; + break; + + case 0x04: + case 0x0c: + case 0x1c: + std = V4L2_STD_NTSC; + break; + } + } + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = res; + return 0; +} + +static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + v4l2_dbg(1, debug, sd, "querystd\n"); + return vpx3220_status(sd, NULL, std); +} + +static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + v4l2_dbg(1, debug, sd, "g_input_status\n"); + return vpx3220_status(sd, status, NULL); +} + +static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + int temp_input; + + /* Here we back up the input selection because it gets + overwritten when we fill the registers with the + chosen video norm */ + temp_input = vpx3220_fp_read(sd, 0xf2); + + v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); + if (std & V4L2_STD_NTSC) { + vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); + } else if (std & V4L2_STD_PAL) { + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); + } else if (std & V4L2_STD_SECAM) { + vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); + } else { + return -EINVAL; + } + + decoder->norm = std; + + /* And here we set the backed up video input again */ + vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); + udelay(10); + return 0; +} + +static int vpx3220_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + int data; + + /* RJ: input = 0: ST8 (PCTV) input + input = 1: COMPOSITE input + input = 2: SVHS input */ + + const int input_vals[3][2] = { + {0x0c, 0}, + {0x0d, 0}, + {0x0e, 1} + }; + + if (input > 2) + return -EINVAL; + + v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); + + vpx3220_write(sd, 0x33, input_vals[input][0]); + + data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); + if (data < 0) + return data; + /* 0x0010 is required to latch the setting */ + vpx3220_fp_write(sd, 0xf2, + data | (input_vals[input][1] << 5) | 0x0010); + + udelay(10); + return 0; +} + +static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); + + vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); + return 0; +} + +static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + vpx3220_write(sd, 0xe6, ctrl->val); + return 0; + case V4L2_CID_CONTRAST: + /* Bit 7 and 8 is for noise shaping */ + vpx3220_write(sd, 0xe7, ctrl->val + 192); + return 0; + case V4L2_CID_SATURATION: + vpx3220_fp_write(sd, 0xa0, ctrl->val); + return 0; + case V4L2_CID_HUE: + vpx3220_fp_write(sd, 0x1c, ctrl->val); + return 0; + } + return -EINVAL; +} + +static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { + .s_ctrl = vpx3220_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops vpx3220_core_ops = { + .g_chip_ident = vpx3220_g_chip_ident, + .init = vpx3220_init, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .s_std = vpx3220_s_std, +}; + +static const struct v4l2_subdev_video_ops vpx3220_video_ops = { + .s_routing = vpx3220_s_routing, + .s_stream = vpx3220_s_stream, + .querystd = vpx3220_querystd, + .g_input_status = vpx3220_g_input_status, +}; + +static const struct v4l2_subdev_ops vpx3220_ops = { + .core = &vpx3220_core_ops, + .video = &vpx3220_video_ops, +}; + +/* ----------------------------------------------------------------------- + * Client management code + */ + +static int vpx3220_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vpx3220 *decoder; + struct v4l2_subdev *sd; + const char *name = NULL; + u8 ver; + u16 pn; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); + decoder->norm = V4L2_STD_PAL; + decoder->input = 0; + decoder->enable = 1; + v4l2_ctrl_handler_init(&decoder->hdl, 4); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 32); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_SATURATION, 0, 4095, 1, 2048); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_HUE, -512, 511, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + + ver = i2c_smbus_read_byte_data(client, 0x00); + pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + + i2c_smbus_read_byte_data(client, 0x01); + decoder->ident = V4L2_IDENT_VPX3220A; + if (ver == 0xec) { + switch (pn) { + case 0x4680: + name = "vpx3220a"; + break; + case 0x4260: + name = "vpx3216b"; + decoder->ident = V4L2_IDENT_VPX3216B; + break; + case 0x4280: + name = "vpx3214c"; + decoder->ident = V4L2_IDENT_VPX3214C; + break; + } + } + if (name) + v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, + client->addr << 1, client->adapter->name); + else + v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", + ver, pn, client->addr << 1, client->adapter->name); + + vpx3220_write_block(sd, init_common, sizeof(init_common)); + vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); + /* Default to PAL */ + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + return 0; +} + +static int vpx3220_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vpx3220 *decoder = to_vpx3220(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return 0; +} + +static const struct i2c_device_id vpx3220_id[] = { + { "vpx3220a", 0 }, + { "vpx3216b", 0 }, + { "vpx3214c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vpx3220_id); + +static struct i2c_driver vpx3220_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vpx3220", + }, + .probe = vpx3220_probe, + .remove = vpx3220_remove, + .id_table = vpx3220_id, +}; + +module_i2c_driver(vpx3220_driver); diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c new file mode 100644 index 000000000000..42ae9dc9c574 --- /dev/null +++ b/drivers/media/i2c/vs6624.c @@ -0,0 +1,928 @@ +/* + * vs6624.c ST VS6624 CMOS image sensor driver + * + * Copyright (c) 2011 Analog Devices 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. + * + * 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 + +#include +#include +#include +#include + +#include "vs6624_regs.h" + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 +#define QQCIF_WIDTH 88 +#define QQCIF_HEIGHT 72 + +#define MAX_FRAME_RATE 30 + +struct vs6624 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_fract frame_rate; + struct v4l2_mbus_framefmt fmt; + unsigned ce_pin; +}; + +static const struct vs6624_format { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; +} vs6624_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + }, +}; + +static struct v4l2_mbus_framefmt vs6624_default_fmt = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, +}; + +static const u16 vs6624_p1[] = { + 0x8104, 0x03, + 0x8105, 0x01, + 0xc900, 0x03, + 0xc904, 0x47, + 0xc905, 0x10, + 0xc906, 0x80, + 0xc907, 0x3a, + 0x903a, 0x02, + 0x903b, 0x47, + 0x903c, 0x15, + 0xc908, 0x31, + 0xc909, 0xdc, + 0xc90a, 0x80, + 0xc90b, 0x44, + 0x9044, 0x02, + 0x9045, 0x31, + 0x9046, 0xe2, + 0xc90c, 0x07, + 0xc90d, 0xe0, + 0xc90e, 0x80, + 0xc90f, 0x47, + 0x9047, 0x90, + 0x9048, 0x83, + 0x9049, 0x81, + 0x904a, 0xe0, + 0x904b, 0x60, + 0x904c, 0x08, + 0x904d, 0x90, + 0x904e, 0xc0, + 0x904f, 0x43, + 0x9050, 0x74, + 0x9051, 0x01, + 0x9052, 0xf0, + 0x9053, 0x80, + 0x9054, 0x05, + 0x9055, 0xE4, + 0x9056, 0x90, + 0x9057, 0xc0, + 0x9058, 0x43, + 0x9059, 0xf0, + 0x905a, 0x02, + 0x905b, 0x07, + 0x905c, 0xec, + 0xc910, 0x5d, + 0xc911, 0xca, + 0xc912, 0x80, + 0xc913, 0x5d, + 0x905d, 0xa3, + 0x905e, 0x04, + 0x905f, 0xf0, + 0x9060, 0xa3, + 0x9061, 0x04, + 0x9062, 0xf0, + 0x9063, 0x22, + 0xc914, 0x72, + 0xc915, 0x92, + 0xc916, 0x80, + 0xc917, 0x64, + 0x9064, 0x74, + 0x9065, 0x01, + 0x9066, 0x02, + 0x9067, 0x72, + 0x9068, 0x95, + 0xc918, 0x47, + 0xc919, 0xf2, + 0xc91a, 0x81, + 0xc91b, 0x69, + 0x9169, 0x74, + 0x916a, 0x02, + 0x916b, 0xf0, + 0x916c, 0xec, + 0x916d, 0xb4, + 0x916e, 0x10, + 0x916f, 0x0a, + 0x9170, 0x90, + 0x9171, 0x80, + 0x9172, 0x16, + 0x9173, 0xe0, + 0x9174, 0x70, + 0x9175, 0x04, + 0x9176, 0x90, + 0x9177, 0xd3, + 0x9178, 0xc4, + 0x9179, 0xf0, + 0x917a, 0x22, + 0xc91c, 0x0a, + 0xc91d, 0xbe, + 0xc91e, 0x80, + 0xc91f, 0x73, + 0x9073, 0xfc, + 0x9074, 0xa3, + 0x9075, 0xe0, + 0x9076, 0xf5, + 0x9077, 0x82, + 0x9078, 0x8c, + 0x9079, 0x83, + 0x907a, 0xa3, + 0x907b, 0xa3, + 0x907c, 0xe0, + 0x907d, 0xfc, + 0x907e, 0xa3, + 0x907f, 0xe0, + 0x9080, 0xc3, + 0x9081, 0x9f, + 0x9082, 0xff, + 0x9083, 0xec, + 0x9084, 0x9e, + 0x9085, 0xfe, + 0x9086, 0x02, + 0x9087, 0x0a, + 0x9088, 0xea, + 0xc920, 0x47, + 0xc921, 0x38, + 0xc922, 0x80, + 0xc923, 0x89, + 0x9089, 0xec, + 0x908a, 0xd3, + 0x908b, 0x94, + 0x908c, 0x20, + 0x908d, 0x40, + 0x908e, 0x01, + 0x908f, 0x1c, + 0x9090, 0x90, + 0x9091, 0xd3, + 0x9092, 0xd4, + 0x9093, 0xec, + 0x9094, 0xf0, + 0x9095, 0x02, + 0x9096, 0x47, + 0x9097, 0x3d, + 0xc924, 0x45, + 0xc925, 0xca, + 0xc926, 0x80, + 0xc927, 0x98, + 0x9098, 0x12, + 0x9099, 0x77, + 0x909a, 0xd6, + 0x909b, 0x02, + 0x909c, 0x45, + 0x909d, 0xcd, + 0xc928, 0x20, + 0xc929, 0xd5, + 0xc92a, 0x80, + 0xc92b, 0x9e, + 0x909e, 0x90, + 0x909f, 0x82, + 0x90a0, 0x18, + 0x90a1, 0xe0, + 0x90a2, 0xb4, + 0x90a3, 0x03, + 0x90a4, 0x0e, + 0x90a5, 0x90, + 0x90a6, 0x83, + 0x90a7, 0xbf, + 0x90a8, 0xe0, + 0x90a9, 0x60, + 0x90aa, 0x08, + 0x90ab, 0x90, + 0x90ac, 0x81, + 0x90ad, 0xfc, + 0x90ae, 0xe0, + 0x90af, 0xff, + 0x90b0, 0xc3, + 0x90b1, 0x13, + 0x90b2, 0xf0, + 0x90b3, 0x90, + 0x90b4, 0x81, + 0x90b5, 0xfc, + 0x90b6, 0xe0, + 0x90b7, 0xff, + 0x90b8, 0x02, + 0x90b9, 0x20, + 0x90ba, 0xda, + 0xc92c, 0x70, + 0xc92d, 0xbc, + 0xc92e, 0x80, + 0xc92f, 0xbb, + 0x90bb, 0x90, + 0x90bc, 0x82, + 0x90bd, 0x18, + 0x90be, 0xe0, + 0x90bf, 0xb4, + 0x90c0, 0x03, + 0x90c1, 0x06, + 0x90c2, 0x90, + 0x90c3, 0xc1, + 0x90c4, 0x06, + 0x90c5, 0x74, + 0x90c6, 0x05, + 0x90c7, 0xf0, + 0x90c8, 0x90, + 0x90c9, 0xd3, + 0x90ca, 0xa0, + 0x90cb, 0x02, + 0x90cc, 0x70, + 0x90cd, 0xbf, + 0xc930, 0x72, + 0xc931, 0x21, + 0xc932, 0x81, + 0xc933, 0x3b, + 0x913b, 0x7d, + 0x913c, 0x02, + 0x913d, 0x7f, + 0x913e, 0x7b, + 0x913f, 0x02, + 0x9140, 0x72, + 0x9141, 0x25, + 0xc934, 0x28, + 0xc935, 0xae, + 0xc936, 0x80, + 0xc937, 0xd2, + 0x90d2, 0xf0, + 0x90d3, 0x90, + 0x90d4, 0xd2, + 0x90d5, 0x0a, + 0x90d6, 0x02, + 0x90d7, 0x28, + 0x90d8, 0xb4, + 0xc938, 0x28, + 0xc939, 0xb1, + 0xc93a, 0x80, + 0xc93b, 0xd9, + 0x90d9, 0x90, + 0x90da, 0x83, + 0x90db, 0xba, + 0x90dc, 0xe0, + 0x90dd, 0xff, + 0x90de, 0x90, + 0x90df, 0xd2, + 0x90e0, 0x08, + 0x90e1, 0xe0, + 0x90e2, 0xe4, + 0x90e3, 0xef, + 0x90e4, 0xf0, + 0x90e5, 0xa3, + 0x90e6, 0xe0, + 0x90e7, 0x74, + 0x90e8, 0xff, + 0x90e9, 0xf0, + 0x90ea, 0x90, + 0x90eb, 0xd2, + 0x90ec, 0x0a, + 0x90ed, 0x02, + 0x90ee, 0x28, + 0x90ef, 0xb4, + 0xc93c, 0x29, + 0xc93d, 0x79, + 0xc93e, 0x80, + 0xc93f, 0xf0, + 0x90f0, 0xf0, + 0x90f1, 0x90, + 0x90f2, 0xd2, + 0x90f3, 0x0e, + 0x90f4, 0x02, + 0x90f5, 0x29, + 0x90f6, 0x7f, + 0xc940, 0x29, + 0xc941, 0x7c, + 0xc942, 0x80, + 0xc943, 0xf7, + 0x90f7, 0x90, + 0x90f8, 0x83, + 0x90f9, 0xba, + 0x90fa, 0xe0, + 0x90fb, 0xff, + 0x90fc, 0x90, + 0x90fd, 0xd2, + 0x90fe, 0x0c, + 0x90ff, 0xe0, + 0x9100, 0xe4, + 0x9101, 0xef, + 0x9102, 0xf0, + 0x9103, 0xa3, + 0x9104, 0xe0, + 0x9105, 0x74, + 0x9106, 0xff, + 0x9107, 0xf0, + 0x9108, 0x90, + 0x9109, 0xd2, + 0x910a, 0x0e, + 0x910b, 0x02, + 0x910c, 0x29, + 0x910d, 0x7f, + 0xc944, 0x2a, + 0xc945, 0x42, + 0xc946, 0x81, + 0xc947, 0x0e, + 0x910e, 0xf0, + 0x910f, 0x90, + 0x9110, 0xd2, + 0x9111, 0x12, + 0x9112, 0x02, + 0x9113, 0x2a, + 0x9114, 0x48, + 0xc948, 0x2a, + 0xc949, 0x45, + 0xc94a, 0x81, + 0xc94b, 0x15, + 0x9115, 0x90, + 0x9116, 0x83, + 0x9117, 0xba, + 0x9118, 0xe0, + 0x9119, 0xff, + 0x911a, 0x90, + 0x911b, 0xd2, + 0x911c, 0x10, + 0x911d, 0xe0, + 0x911e, 0xe4, + 0x911f, 0xef, + 0x9120, 0xf0, + 0x9121, 0xa3, + 0x9122, 0xe0, + 0x9123, 0x74, + 0x9124, 0xff, + 0x9125, 0xf0, + 0x9126, 0x90, + 0x9127, 0xd2, + 0x9128, 0x12, + 0x9129, 0x02, + 0x912a, 0x2a, + 0x912b, 0x48, + 0xc900, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_p2[] = { + 0x806f, 0x01, + 0x058c, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_run_setup[] = { + 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ + VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ + VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ + VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ + VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ + VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ + VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ + VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ + VS6624_NORA_USAGE, 0x04, /* Nora usage */ + VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ + VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ + VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ + VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ + VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_F2B_DISABLE, 0x00, /* Disable */ + 0x1d8a, 0x30, /* MAXWeightHigh */ + 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ + 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ + 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ + 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ + 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ + 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ + 0x1e08, 0x06, /* MAXWeightLow */ + 0x1e0a, 0x0a, /* MAXWeightHigh */ + 0x1601, 0x3a, /* Red A MSB */ + 0x1602, 0x14, /* Red A LSB */ + 0x1605, 0x3b, /* Blue A MSB */ + 0x1606, 0x85, /* BLue A LSB */ + 0x1609, 0x3b, /* RED B MSB */ + 0x160a, 0x85, /* RED B LSB */ + 0x160d, 0x3a, /* Blue B MSB */ + 0x160e, 0x14, /* Blue B LSB */ + 0x1611, 0x30, /* Max Distance from Locus MSB */ + 0x1612, 0x8f, /* Max Distance from Locus MSB */ + 0x1614, 0x01, /* Enable constrainer */ + 0x0000, 0x00, +}; + +static const u16 vs6624_default[] = { + VS6624_CONTRAST0, 0x84, + VS6624_SATURATION0, 0x75, + VS6624_GAMMA0, 0x11, + VS6624_CONTRAST1, 0x84, + VS6624_SATURATION1, 0x75, + VS6624_GAMMA1, 0x11, + VS6624_MAN_RG, 0x80, + VS6624_MAN_GG, 0x80, + VS6624_MAN_BG, 0x80, + VS6624_WB_MODE, 0x1, + VS6624_EXPO_COMPENSATION, 0xfe, + VS6624_EXPO_METER, 0x0, + VS6624_LIGHT_FREQ, 0x64, + VS6624_PEAK_GAIN, 0xe, + VS6624_PEAK_LOW_THR, 0x28, + VS6624_HMIRROR0, 0x0, + VS6624_VFLIP0, 0x0, + VS6624_ZOOM_HSTEP0_MSB, 0x0, + VS6624_ZOOM_HSTEP0_LSB, 0x1, + VS6624_ZOOM_VSTEP0_MSB, 0x0, + VS6624_ZOOM_VSTEP0_LSB, 0x1, + VS6624_PAN_HSTEP0_MSB, 0x0, + VS6624_PAN_HSTEP0_LSB, 0xf, + VS6624_PAN_VSTEP0_MSB, 0x0, + VS6624_PAN_VSTEP0_LSB, 0xf, + VS6624_SENSOR_MODE, 0x1, + VS6624_SYNC_CODE_SETUP, 0x21, + VS6624_DISABLE_FR_DAMPER, 0x0, + VS6624_FR_DEN, 0x1, + VS6624_FR_NUM_LSB, 0xf, + VS6624_INIT_PIPE_SETUP, 0x0, + VS6624_IMG_FMT0, 0x0, + VS6624_YUV_SETUP, 0x1, + VS6624_IMAGE_SIZE0, 0x2, + 0x0000, 0x00, +}; + +static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vs6624, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vs6624, hdl)->sd; +} + +static int vs6624_read(struct v4l2_subdev *sd, u16 index) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = index >> 8; + buf[1] = index; + i2c_master_send(client, buf, 2); + i2c_master_recv(client, buf, 1); + + return buf[0]; +} + +static int vs6624_write(struct v4l2_subdev *sd, u16 index, + u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[3]; + + buf[0] = index >> 8; + buf[1] = index; + buf[2] = value; + + return i2c_master_send(client, buf, 3); +} + +static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) +{ + u16 reg; + u8 data; + + while (*regs != 0x00) { + reg = *regs++; + data = *regs++; + + vs6624_write(sd, reg, data); + } + return 0; +} + +static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); + break; + case V4L2_CID_SATURATION: + vs6624_write(sd, VS6624_SATURATION0, ctrl->val); + break; + case V4L2_CID_HFLIP: + vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); + break; + case V4L2_CID_VFLIP: + vs6624_write(sd, VS6624_VFLIP0, ctrl->val); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(vs6624_formats)) + return -EINVAL; + + *code = vs6624_formats[index].mbus_code; + return 0; +} + +static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int index; + + for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) + if (vs6624_formats[index].mbus_code == fmt->code) + break; + if (index >= ARRAY_SIZE(vs6624_formats)) { + /* default to first format */ + index = 0; + fmt->code = vs6624_formats[0].mbus_code; + } + + /* sensor mode is VGA */ + if (fmt->width > VGA_WIDTH) + fmt->width = VGA_WIDTH; + if (fmt->height > VGA_HEIGHT) + fmt->height = VGA_HEIGHT; + fmt->width = fmt->width & (~3); + fmt->height = fmt->height & (~3); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = vs6624_formats[index].colorspace; + return 0; +} + +static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + int ret; + + ret = vs6624_try_mbus_fmt(sd, fmt); + if (ret) + return ret; + + /* set image format */ + switch (fmt->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x1); + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x3); + break; + case V4L2_MBUS_FMT_RGB565_2X8_LE: + vs6624_write(sd, VS6624_IMG_FMT0, 0x4); + vs6624_write(sd, VS6624_RGB_SETUP, 0x0); + break; + default: + return -EINVAL; + } + + /* set image size */ + if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); + else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); + else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); + else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); + else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); + else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); + else { + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); + vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); + vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); + vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); + vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); + vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); + } + + sensor->fmt = *fmt; + + return 0; +} + +static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + + *fmt = sensor->fmt; + return 0; +} + +static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(*cp)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = sensor->frame_rate.denominator; + cp->timeperframe.denominator = sensor->frame_rate.numerator; + return 0; +} + +static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + if (tpf->numerator == 0 || tpf->denominator == 0 + || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { + /* reset to max frame rate */ + tpf->numerator = 1; + tpf->denominator = MAX_FRAME_RATE; + } + sensor->frame_rate.numerator = tpf->denominator; + sensor->frame_rate.denominator = tpf->numerator; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + return 0; +} + +static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) +{ + if (enable) + vs6624_write(sd, VS6624_USER_CMD, 0x2); + else + vs6624_write(sd, VS6624_USER_CMD, 0x4); + udelay(100); + return 0; +} + +static int vs6624_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8) + | vs6624_read(sd, VS6624_FW_VSN_MINOR); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = vs6624_read(sd, reg->reg & 0xffff); + reg->size = 1; + return 0; +} + +static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { + .s_ctrl = vs6624_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops vs6624_core_ops = { + .g_chip_ident = vs6624_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = vs6624_g_register, + .s_register = vs6624_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops vs6624_video_ops = { + .enum_mbus_fmt = vs6624_enum_mbus_fmt, + .try_mbus_fmt = vs6624_try_mbus_fmt, + .s_mbus_fmt = vs6624_s_mbus_fmt, + .g_mbus_fmt = vs6624_g_mbus_fmt, + .s_parm = vs6624_s_parm, + .g_parm = vs6624_g_parm, + .s_stream = vs6624_s_stream, +}; + +static const struct v4l2_subdev_ops vs6624_ops = { + .core = &vs6624_core_ops, + .video = &vs6624_video_ops, +}; + +static int __devinit vs6624_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vs6624 *sensor; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + const unsigned *ce; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EIO; + + ce = client->dev.platform_data; + if (ce == NULL) + return -EINVAL; + + ret = gpio_request(*ce, "VS6624 Chip Enable"); + if (ret) { + v4l_err(client, "failed to request GPIO %d\n", *ce); + return ret; + } + gpio_direction_output(*ce, 1); + /* wait 100ms before any further i2c writes are performed */ + mdelay(100); + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) { + gpio_free(*ce); + return -ENOMEM; + } + + sd = &sensor->sd; + v4l2_i2c_subdev_init(sd, client, &vs6624_ops); + + vs6624_writeregs(sd, vs6624_p1); + vs6624_write(sd, VS6624_MICRO_EN, 0x2); + vs6624_write(sd, VS6624_DIO_EN, 0x1); + mdelay(10); + vs6624_writeregs(sd, vs6624_p2); + + vs6624_writeregs(sd, vs6624_default); + vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); + vs6624_writeregs(sd, vs6624_run_setup); + + /* set frame rate */ + sensor->frame_rate.numerator = MAX_FRAME_RATE; + sensor->frame_rate.denominator = 1; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + + sensor->fmt = vs6624_default_fmt; + sensor->ce_pin = *ce; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + hdl = &sensor->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + return err; + } + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + } + return ret; +} + +static int __devexit vs6624_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vs6624 *sensor = to_vs6624(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(sensor->ce_pin); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id vs6624_id[] = { + {"vs6624", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, vs6624_id); + +static struct i2c_driver vs6624_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vs6624", + }, + .probe = vs6624_probe, + .remove = __devexit_p(vs6624_remove), + .id_table = vs6624_id, +}; + +static __init int vs6624_init(void) +{ + return i2c_add_driver(&vs6624_driver); +} + +static __exit void vs6624_exit(void) +{ + i2c_del_driver(&vs6624_driver); +} + +module_init(vs6624_init); +module_exit(vs6624_exit); + +MODULE_DESCRIPTION("VS6624 sensor driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/vs6624_regs.h b/drivers/media/i2c/vs6624_regs.h new file mode 100644 index 000000000000..6ba2ee25827e --- /dev/null +++ b/drivers/media/i2c/vs6624_regs.h @@ -0,0 +1,337 @@ +/* + * vs6624 - ST VS6624 CMOS image sensor registers + * + * Copyright (c) 2011 Analog Devices 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. + * + * 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 _VS6624_REGS_H_ +#define _VS6624_REGS_H_ + +/* low level control registers */ +#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ +#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ +/* device parameters */ +#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ +#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ +#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ +#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ +#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ +#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ +/* host interface manager control */ +#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ +/* host interface manager status */ +#define VS6624_STATE 0x0202 /* current state of the mode manager */ +/* run mode control */ +#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ +/* mode setup */ +#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ +#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ +/* pipe setup bank0 */ +#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ +#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ +#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ +#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ +#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ +#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ +#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ +#define VS6624_GAMMA0 0x03B8 /* gamma settings */ +#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ +#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ +/* pipe setup bank1 */ +#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ +#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ +#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ +#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ +#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ +#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ +#define VS6624_SATURATION1 0x0436 /* saturation control for output */ +#define VS6624_GAMMA1 0x0438 /* gamma settings */ +#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ +#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ +/* view live control */ +#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ +#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ +/* view live status */ +#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ +/* power management */ +#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ +/* video timing parameter host inputs */ +#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ +#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ +#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ +/* video timing control */ +#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ +/* frame dimension parameter host inputs */ +#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ +#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ +/* static frame rate control */ +#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ +#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ +#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ +/* automatic frame rate control */ +#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ +#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ +#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ +/* exposure controls */ +#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ +#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ +#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ +#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ +#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ +#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ +#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ +#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ +#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ +#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ +#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ +#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ +#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ +#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ +#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ +#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ +#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ +#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ +#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ +#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ +#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ +#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ +#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ +#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ +#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ +#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ +#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ +/* white balance control */ +#define VS6624_WB_MODE 0x1480 /* set white balance mode */ +#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ +#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ +#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ +#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ +#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ +#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ +#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ +#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ +#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ +/* sensor setup */ +#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ +/* image stability */ +#define VS6624_STABLE_WB 0x1900 /* white balance stable */ +#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ +#define VS6624_STABLE 0x1906 /* system stable */ +/* flash control */ +#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ +#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ +#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ +/* flash status */ +#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ +#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ +/* scythe filter controls */ +#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ +/* jack filter controls */ +#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ +/* demosaic control */ +#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ +/* color matrix dampers */ +#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ +#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ +#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ +#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ +#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ +#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ +#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ +/* peaking control */ +#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ +#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ +#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ +#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ +#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ +#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ +#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ +#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ +#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ +#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ +#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ +#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ +#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ +#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ +#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ +#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ +#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ +/* pipe 0 RGB to YUV matrix manual control */ +#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ +#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ +#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ +#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ +#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ +#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ +/* pipe 1 RGB to YUV matrix manual control */ +#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ +#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ +#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ +#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ +#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ +#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ +/* pipe 0 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ +/* pipe 1 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ +/* fade to black */ +#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ +#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ +#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ +#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ +#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ +#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ +#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ +#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ +#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ +/* output formatter control */ +#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ +#define VS6624_BLANK_FMT 0x2582 /* blank format */ +#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ +#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ +#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ +#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ +#define VS6624_PCLK_EN 0x258C /* PCLK enable */ +#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ +#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ +#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ +#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ +#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ +#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ +#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ +#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ +#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ +#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ +#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ +#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ +#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ +#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ +#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ +#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ +#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ +#define VS6624_OUT_IF 0x25B0 /* output interface */ +#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ +/* NoRA controls */ +#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ +#define VS6624_NORA_USAGE 0x2602 /* usage */ +#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ +#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ +#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ +#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ +#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ +#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ +#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ +#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ +#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ +#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ + +#endif diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c new file mode 100644 index 000000000000..3bb99e93febe --- /dev/null +++ b/drivers/media/i2c/wm8739.c @@ -0,0 +1,294 @@ +/* + * wm8739 + * + * Copyright (C) 2005 T. Adachi + * + * Copyright (C) 2005 Hans Verkuil + * - Cleanup + * + * This program is free software; 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 +#include + +MODULE_DESCRIPTION("wm8739 driver"); +MODULE_AUTHOR("T. Adachi, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; + +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +/* ------------------------------------------------------------------------ */ + +enum { + R0 = 0, R1, + R5 = 5, R6, R7, R8, R9, R15 = 15, + TOT_REGS +}; + +struct wm8739_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* audio cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + struct v4l2_ctrl *balance; + }; + u32 clock_freq; +}; + +static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wm8739_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; +} + +/* ------------------------------------------------------------------------ */ + +static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + if (reg < 0 || reg >= TOT_REGS) { + v4l2_err(sd, "Invalid register R%d\n", reg); + return -1; + } + + v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); + + for (i = 0; i < 3; i++) + if (i2c_smbus_write_byte_data(client, + (reg << 1) | (val >> 8), val & 0xff) == 0) + return 0; + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct wm8739_state *state = to_state(sd); + unsigned int work_l, work_r; + u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u16 mute; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + break; + + default: + return -EINVAL; + } + + /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ + work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768; + work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768; + + vol_l = (long)work_l * 31 / 65535; + vol_r = (long)work_r * 31 / 65535; + + /* set audio volume etc. */ + mute = state->mute->val ? 0x80 : 0; + + /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB + * Default setting: 0x17 = 0 dB + */ + wm8739_write(sd, R0, (vol_l & 0x1f) | mute); + wm8739_write(sd, R1, (vol_r & 0x1f) | mute); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) +{ + struct wm8739_state *state = to_state(sd); + + state->clock_freq = audiofreq; + /* de-activate */ + wm8739_write(sd, R9, 0x000); + switch (audiofreq) { + case 44100: + /* 256fps, fs=44.1k */ + wm8739_write(sd, R8, 0x020); + break; + case 48000: + /* 256fps, fs=48k */ + wm8739_write(sd, R8, 0x000); + break; + case 32000: + /* 256fps, fs=32k */ + wm8739_write(sd, R8, 0x018); + break; + default: + break; + } + /* activate */ + wm8739_write(sd, R9, 0x001); + return 0; +} + +static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); +} + +static int wm8739_log_status(struct v4l2_subdev *sd) +{ + struct wm8739_state *state = to_state(sd); + + v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { + .s_ctrl = wm8739_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops wm8739_core_ops = { + .log_status = wm8739_log_status, + .g_chip_ident = wm8739_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { + .s_clock_freq = wm8739_s_clock_freq, +}; + +static const struct v4l2_subdev_ops wm8739_ops = { + .core = &wm8739_core_ops, + .audio = &wm8739_audio_ops, +}; + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static int wm8739_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wm8739_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8739_ops); + v4l2_ctrl_handler_init(&state->hdl, 2); + state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736); + state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_cluster(3, &state->volume); + + state->clock_freq = 48000; + + /* Initialize wm8739 */ + + /* reset */ + wm8739_write(sd, R15, 0x00); + /* filter setting, high path, offet clear */ + wm8739_write(sd, R5, 0x000); + /* ADC, OSC, Power Off mode Disable */ + wm8739_write(sd, R6, 0x000); + /* Digital Audio interface format: + Enable Master mode, 24 bit, MSB first/left justified */ + wm8739_write(sd, R7, 0x049); + /* sampling control: normal, 256fs, 48KHz sampling rate */ + wm8739_write(sd, R8, 0x000); + /* activate */ + wm8739_write(sd, R9, 0x001); + /* set volume/mute */ + v4l2_ctrl_handler_setup(&state->hdl); + return 0; +} + +static int wm8739_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id wm8739_id[] = { + { "wm8739", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8739_id); + +static struct i2c_driver wm8739_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "wm8739", + }, + .probe = wm8739_probe, + .remove = wm8739_remove, + .id_table = wm8739_id, +}; + +module_i2c_driver(wm8739_driver); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c new file mode 100644 index 000000000000..bee77ea9f49e --- /dev/null +++ b/drivers/media/i2c/wm8775.c @@ -0,0 +1,342 @@ +/* + * wm8775 - driver version 0.0.1 + * + * Copyright (C) 2004 Ulf Eklund + * + * Based on saa7115 driver + * + * Copyright (C) 2005 Hans Verkuil + * - Cleanup + * - V4L2 API update + * - sound fixes + * + * This program is free software; 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 +#include +#include + +MODULE_DESCRIPTION("wm8775 driver"); +MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); +MODULE_LICENSE("GPL"); + + + +/* ----------------------------------------------------------------------- */ + +enum { + R7 = 7, R11 = 11, + R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23, + TOT_REGS +}; + +#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */ +#define ALC_EN 0x100 /* R17: ALC enable */ + +struct wm8775_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *mute; + struct v4l2_ctrl *vol; + struct v4l2_ctrl *bal; + struct v4l2_ctrl *loud; + u8 input; /* Last selected input (0-0xf) */ +}; + +static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wm8775_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd; +} + +static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + + if (reg < 0 || reg >= TOT_REGS) { + v4l2_err(sd, "Invalid register R%d\n", reg); + return -1; + } + + for (i = 0; i < 3; i++) + if (i2c_smbus_write_byte_data(client, + (reg << 1) | (val >> 8), val & 0xff) == 0) + return 0; + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly) +{ + struct wm8775_state *state = to_state(sd); + u8 vol_l, vol_r; + int muted = 0 != state->mute->val; + u16 volume = (u16)state->vol->val; + u16 balance = (u16)state->bal->val; + + /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ + vol_l = (min(65536 - balance, 32768) * volume) >> 23; + vol_r = (min(balance, (u16)32768) * volume) >> 23; + + /* Mute */ + if (muted || quietly) + wm8775_write(sd, R21, 0x0c0 | state->input); + + wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */ + wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */ + + /* Un-mute */ + if (!muted) + wm8775_write(sd, R21, state->input); +} + +static int wm8775_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct wm8775_state *state = to_state(sd); + + /* There are 4 inputs and one output. Zero or more inputs + are multiplexed together to the output. Hence there are + 16 combinations. + If only one input is active (the normal case) then the + input values 1, 2, 4 or 8 should be used. */ + if (input > 15) { + v4l2_err(sd, "Invalid input %d.\n", input); + return -EINVAL; + } + state->input = input; + if (!v4l2_ctrl_g_ctrl(state->mute)) + return 0; + if (!v4l2_ctrl_g_ctrl(state->vol)) + return 0; + if (!v4l2_ctrl_g_ctrl(state->bal)) + return 0; + wm8775_set_audio(sd, 1); + return 0; +} + +static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + wm8775_set_audio(sd, 0); + return 0; + case V4L2_CID_AUDIO_LOUDNESS: + wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD); + return 0; + } + return -EINVAL; +} + +static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); +} + +static int wm8775_log_status(struct v4l2_subdev *sd) +{ + struct wm8775_state *state = to_state(sd); + + v4l2_info(sd, "Input: %d\n", state->input); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + wm8775_set_audio(sd, 0); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { + .s_ctrl = wm8775_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops wm8775_core_ops = { + .log_status = wm8775_log_status, + .g_chip_ident = wm8775_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { + .s_frequency = wm8775_s_frequency, +}; + +static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { + .s_routing = wm8775_s_routing, +}; + +static const struct v4l2_subdev_ops wm8775_ops = { + .core = &wm8775_core_ops, + .tuner = &wm8775_tuner_ops, + .audio = &wm8775_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int wm8775_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wm8775_state *state; + struct v4l2_subdev *sd; + int err; + bool is_nova_s = false; + + if (client->dev.platform_data) { + struct wm8775_platform_data *data = client->dev.platform_data; + is_nova_s = data->is_nova_s; + } + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8775_ops); + state->input = 2; + + v4l2_ctrl_handler_init(&state->hdl, 4); + state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/ + state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768); + state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1); + sd->ctrl_handler = &state->hdl; + err = state->hdl.error; + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + + /* Initialize wm8775 */ + + /* RESET */ + wm8775_write(sd, R23, 0x000); + /* Disable zero cross detect timeout */ + wm8775_write(sd, R7, 0x000); + /* HPF enable, left justified, 24-bit (Philips) mode */ + wm8775_write(sd, R11, 0x021); + /* Master mode, clock ratio 256fs */ + wm8775_write(sd, R12, 0x102); + /* Powered up */ + wm8775_write(sd, R13, 0x000); + + if (!is_nova_s) { + /* ADC gain +2.5dB, enable zero cross */ + wm8775_write(sd, R14, 0x1d4); + /* ADC gain +2.5dB, enable zero cross */ + wm8775_write(sd, R15, 0x1d4); + /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ + wm8775_write(sd, R16, 0x1bf); + /* Enable gain control, use zero cross detection, + ALC hold time 42.6 ms */ + wm8775_write(sd, R17, 0x185); + } else { + /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ + wm8775_write(sd, R16, 0x1bb); + /* Set ALC mode and hold time */ + wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD); + } + /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ + wm8775_write(sd, R18, 0x0a2); + /* Enable noise gate, threshold -72dBfs */ + wm8775_write(sd, R19, 0x005); + if (!is_nova_s) { + /* Transient window 4ms, lower PGA gain limit -1dB */ + wm8775_write(sd, R20, 0x07a); + /* LRBOTH = 1, use input 2. */ + wm8775_write(sd, R21, 0x102); + } else { + /* Transient window 4ms, ALC min gain -5dB */ + wm8775_write(sd, R20, 0x0fb); + + wm8775_set_audio(sd, 1); /* set volume/mute/mux */ + } + return 0; +} + +static int wm8775_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wm8775_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id wm8775_id[] = { + { "wm8775", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8775_id); + +static struct i2c_driver wm8775_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "wm8775", + }, + .probe = wm8775_probe, + .remove = wm8775_remove, + .id_table = wm8775_id, +}; + +module_i2c_driver(wm8775_driver); diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile index ae347b78fccf..5f06597c6a6e 100644 --- a/drivers/media/pci/bt8xx/Makefile +++ b/drivers/media/pci/bt8xx/Makefile @@ -7,5 +7,5 @@ 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 +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile index f92cc4c14f0c..a2cbdcf15a8c 100644 --- a/drivers/media/pci/cx23885/Makefile +++ b/drivers/media/pci/cx23885/Makefile @@ -7,7 +7,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index 1434e8094803..c038941d6054 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -7,7 +7,7 @@ cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o -ccflags-y := -Idrivers/media/video +ccflags-y := -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile index 884b4cdd8ff0..d3679c3ee248 100644 --- a/drivers/media/pci/cx88/Makefile +++ b/drivers/media/pci/cx88/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o 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/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/ivtv/Makefile b/drivers/media/pci/ivtv/Makefile index 80b4ec18475d..1408c9f1de93 100644 --- a/drivers/media/pci/ivtv/Makefile +++ b/drivers/media/pci/ivtv/Makefile @@ -7,7 +7,7 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ 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/i2c 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/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile index aba50088dcdc..9e510c1459f3 100644 --- a/drivers/media/pci/saa7134/Makefile +++ b/drivers/media/pci/saa7134/Makefile @@ -10,7 +10,7 @@ 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/i2c 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/pci/saa7146/Makefile b/drivers/media/pci/saa7146/Makefile index 362a38b96308..f3566a95e4aa 100644 --- a/drivers/media/pci/saa7146/Makefile +++ b/drivers/media/pci/saa7146/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o -ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/pci/saa7164/Makefile b/drivers/media/pci/saa7164/Makefile index 847110c2e14c..ba0e33a1ee24 100644 --- a/drivers/media/pci/saa7164/Makefile +++ b/drivers/media/pci/saa7164/Makefile @@ -4,7 +4,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/i2c 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/usb/cx231xx/Makefile b/drivers/media/usb/cx231xx/Makefile index 1d40fce77601..52cf76935e69 100644 --- a/drivers/media/usb/cx231xx/Makefile +++ b/drivers/media/usb/cx231xx/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o 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/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile index 65c7c29e4161..6c5f3381da7d 100644 --- a/drivers/media/usb/em28xx/Makefile +++ b/drivers/media/usb/em28xx/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o 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/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/hdpvr/Makefile b/drivers/media/usb/hdpvr/Makefile index 52f057f24e39..9b8d1463c526 100644 --- a/drivers/media/usb/hdpvr/Makefile +++ b/drivers/media/usb/hdpvr/Makefile @@ -2,6 +2,6 @@ hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/usb/pvrusb2/Makefile b/drivers/media/usb/pvrusb2/Makefile index bc716db797e3..ad705547bdce 100644 --- a/drivers/media/usb/pvrusb2/Makefile +++ b/drivers/media/usb/pvrusb2/Makefile @@ -16,7 +16,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o \ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/stk1160/Makefile b/drivers/media/usb/stk1160/Makefile index 8a3c78482e73..dfe3e90ff392 100644 --- a/drivers/media/usb/stk1160/Makefile +++ b/drivers/media/usb/stk1160/Makefile @@ -8,4 +8,4 @@ stk1160-y := stk1160-core.o \ obj-$(CONFIG_VIDEO_STK1160) += stk1160.o -ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/usb/tlg2300/Makefile b/drivers/media/usb/tlg2300/Makefile index 4d660879999f..137f8e38cdec 100644 --- a/drivers/media/usb/tlg2300/Makefile +++ b/drivers/media/usb/tlg2300/Makefile @@ -2,7 +2,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/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/tm6000/Makefile b/drivers/media/usb/tm6000/Makefile index 1feb8c9c816c..6fa1f1044512 100644 --- a/drivers/media/usb/tm6000/Makefile +++ b/drivers/media/usb/tm6000/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000.o 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/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/usb/usbvision/Makefile b/drivers/media/usb/usbvision/Makefile index d55c6bd97a35..9b3a5581df42 100644 --- a/drivers/media/usb/usbvision/Makefile +++ b/drivers/media/usb/usbvision/Makefile @@ -2,5 +2,5 @@ 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/i2c ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index a7bd9576ccd0..f2171e777dd3 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -1,578 +1,4 @@ -# -# Generic video config states -# - -config VIDEO_BTCX - depends on PCI - tristate - -config VIDEO_TVEEPROM - tristate - depends on I2C - -# -# Multimedia Video device configuration -# - -menuconfig VIDEO_CAPTURE_DRIVERS - bool "Video capture adapters" - depends on VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT - default y - ---help--- - Say Y here to enable selecting the video adapters for - webcams, analog TV, and hybrid analog/digital TV. - Some of those devices also supports FM radio. - -if VIDEO_CAPTURE_DRIVERS && VIDEO_V4L2 - -config VIDEO_HELPER_CHIPS_AUTO - bool "Autoselect pertinent encoders/decoders and other helper chips" - default y if !EXPERT - ---help--- - Most video cards may require additional modules to encode or - decode audio/video standards. This option will autoselect - all pertinent modules to each selected video module. - - Unselect this only if you know exactly what you are doing, since - it may break support on some boards. - - In doubt, say Y. - -config VIDEO_IR_I2C - tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO - depends on I2C && RC_CORE - default y - ---help--- - Most boards have an IR chip directly connected via GPIO. However, - some video boards have the IR connected via I2C bus. - - If your board doesn't have an I2C IR chip, you may disable this - option. - - In doubt, say Y. - -# -# Encoder / Decoder module configuration -# - -menu "Encoders, decoders, sensors and other helper chips" - visible if !VIDEO_HELPER_CHIPS_AUTO - -comment "Audio decoders, processors and mixers" - -config VIDEO_TVAUDIO - tristate "Simple audio decoder chips" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for several audio decoder chips found on some bt8xx boards: - Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, - tea6320, tea6420, tda8425, ta8874z. - Microchip: pic16c54 based design on ProVideo PV951 board. - - To compile this driver as a module, choose M here: the - module will be called tvaudio. - -config VIDEO_TDA7432 - tristate "Philips TDA7432 audio processor" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for tda7432 audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tda7432. - -config VIDEO_TDA9840 - tristate "Philips TDA9840 audio processor" - depends on I2C - ---help--- - Support for tda9840 audio decoder chip found on some Zoran boards. - - To compile this driver as a module, choose M here: the - module will be called tda9840. - -config VIDEO_TEA6415C - tristate "Philips TEA6415C audio processor" - depends on I2C - ---help--- - Support for tea6415c audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tea6415c. - -config VIDEO_TEA6420 - tristate "Philips TEA6420 audio processor" - depends on I2C - ---help--- - Support for tea6420 audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tea6420. - -config VIDEO_MSP3400 - tristate "Micronas MSP34xx audio decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Micronas MSP34xx series of audio decoders. - - To compile this driver as a module, choose M here: the - module will be called msp3400. - -config VIDEO_CS5345 - tristate "Cirrus Logic CS5345 audio ADC" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Cirrus Logic CS5345 24-bit, 192 kHz - stereo A/D converter. - - To compile this driver as a module, choose M here: the - module will be called cs5345. - -config VIDEO_CS53L32A - tristate "Cirrus Logic CS53L32A audio ADC" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Cirrus Logic CS53L32A low voltage - stereo A/D converter. - - To compile this driver as a module, choose M here: the - module will be called cs53l32a. - -config VIDEO_TLV320AIC23B - tristate "Texas Instruments TLV320AIC23B audio codec" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL - ---help--- - Support for the Texas Instruments TLV320AIC23B audio codec. - - To compile this driver as a module, choose M here: the - module will be called tlv320aic23b. - -config VIDEO_WM8775 - tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Wolfson Microelectronics WM8775 high - performance stereo A/D Converter with a 4 channel input mixer. - - To compile this driver as a module, choose M here: the - module will be called wm8775. - -config VIDEO_WM8739 - tristate "Wolfson Microelectronics WM8739 stereo audio ADC" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Wolfson Microelectronics WM8739 - stereo A/D Converter. - - To compile this driver as a module, choose M here: the - module will be called wm8739. - -config VIDEO_VP27SMPX - tristate "Panasonic VP27s internal MPX" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the internal MPX of the Panasonic VP27s tuner. - - To compile this driver as a module, choose M here: the - module will be called vp27smpx. - -comment "RDS decoders" - -config VIDEO_SAA6588 - tristate "SAA6588 Radio Chip RDS decoder support" - depends on VIDEO_V4L2 && I2C - - help - Support for this Radio Data System (RDS) decoder. This allows - seeing radio station identification transmitted using this - standard. - - To compile this driver as a module, choose M here: the - module will be called saa6588. - -comment "Video decoders" - -config VIDEO_ADV7180 - tristate "Analog Devices ADV7180 decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Analog Devices ADV7180 video decoder. - - To compile this driver as a module, choose M here: the - module will be called adv7180. - -config VIDEO_ADV7183 - tristate "Analog Devices ADV7183 decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - V4l2 subdevice driver for the Analog Devices - ADV7183 video decoder. - - To compile this driver as a module, choose M here: the - module will be called adv7183. - -config VIDEO_BT819 - tristate "BT819A VideoStream decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for BT819A video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt819. - -config VIDEO_BT856 - tristate "BT856 VideoStream decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for BT856 video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt856. - -config VIDEO_BT866 - tristate "BT866 VideoStream decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for BT866 video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt866. - -config VIDEO_KS0127 - tristate "KS0127 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for KS0127 video decoder. - - This chip is used on AverMedia AVS6EYES Zoran-based MJPEG - cards. - - To compile this driver as a module, choose M here: the - module will be called ks0127. - -config VIDEO_SAA7110 - tristate "Philips SAA7110 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7110 video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa7110. - -config VIDEO_SAA711X - tristate "Philips SAA7111/3/4/5 video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7111/3/4/5 video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa7115. - -config VIDEO_SAA7191 - tristate "Philips SAA7191 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7191 video decoder. - - To compile this driver as a module, choose M here: the - module will be called saa7191. - -config VIDEO_TVP514X - tristate "Texas Instruments TVP514x video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 - decoder. It is currently working with the TI OMAP3 camera - controller. - - To compile this driver as a module, choose M here: the - module will be called tvp514x. - -config VIDEO_TVP5150 - tristate "Texas Instruments TVP5150 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Texas Instruments TVP5150 video decoder. - - To compile this driver as a module, choose M here: the - module will be called tvp5150. - -config VIDEO_TVP7002 - tristate "Texas Instruments TVP7002 video decoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Texas Instruments TVP7002 video decoder. - - To compile this driver as a module, choose M here: the - module will be called tvp7002. - -config VIDEO_VPX3220 - tristate "vpx3220a, vpx3216b & vpx3214c video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for VPX322x video decoders. - - To compile this driver as a module, choose M here: the - module will be called vpx3220. - -comment "Video and audio decoders" - -config VIDEO_SAA717X - tristate "Philips SAA7171/3/4 audio/video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7171/3/4 audio/video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa717x. - -source "drivers/media/video/cx25840/Kconfig" - -comment "MPEG video encoders" - -config VIDEO_CX2341X - tristate "Conexant CX2341x MPEG encoders" - depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON - ---help--- - Support for the Conexant CX23416 MPEG encoders - and CX23415 MPEG encoder/decoders. - - This module currently supports the encoding functions only. - - To compile this driver as a module, choose M here: the - module will be called cx2341x. - -comment "Video encoders" - -config VIDEO_SAA7127 - tristate "Philips SAA7127/9 digital video encoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7127/9 digital video encoders. - - To compile this driver as a module, choose M here: the - module will be called saa7127. - -config VIDEO_SAA7185 - tristate "Philips SAA7185 video encoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7185 video encoder. - - To compile this driver as a module, choose M here: the - module will be called saa7185. - -config VIDEO_ADV7170 - tristate "Analog Devices ADV7170 video encoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Analog Devices ADV7170 video encoder driver - - To compile this driver as a module, choose M here: the - module will be called adv7170. - -config VIDEO_ADV7175 - tristate "Analog Devices ADV7175 video encoder" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Analog Devices ADV7175 video encoder driver - - To compile this driver as a module, choose M here: the - module will be called adv7175. - -config VIDEO_ADV7343 - tristate "ADV7343 video encoder" - depends on I2C - help - Support for Analog Devices I2C bus based ADV7343 encoder. - - To compile this driver as a module, choose M here: the - module will be called adv7343. - -config VIDEO_ADV7393 - tristate "ADV7393 video encoder" - depends on I2C - help - Support for Analog Devices I2C bus based ADV7393 encoder. - - To compile this driver as a module, choose M here: the - module will be called adv7393. - -config VIDEO_AK881X - tristate "AK8813/AK8814 video encoders" - depends on I2C - help - Video output driver for AKM AK8813 and AK8814 TV encoders - -comment "Camera sensor devices" - -config VIDEO_APTINA_PLL - tristate - -config VIDEO_SMIAPP_PLL - tristate - -config VIDEO_OV7670 - tristate "OmniVision OV7670 sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision - OV7670 VGA camera. It currently only works with the M88ALP01 - controller. - -config VIDEO_VS6624 - tristate "ST VS6624 sensor support" - depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the ST VS6624 - camera. - - To compile this driver as a module, choose M here: the - module will be called vs6624. - -config VIDEO_MT9M032 - tristate "MT9M032 camera sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - select VIDEO_APTINA_PLL - ---help--- - This driver supports MT9M032 camera sensors from Aptina, monochrome - models only. - -config VIDEO_MT9P031 - tristate "Aptina MT9P031 support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - select VIDEO_APTINA_PLL - ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina - (Micron) mt9p031 5 Mpixel camera. - -config VIDEO_MT9T001 - tristate "Aptina MT9T001 support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina - (Micron) mt0t001 3 Mpixel camera. - -config VIDEO_MT9V011 - tristate "Micron mt9v011 sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the Micron - mt0v011 1.3 Mpixel camera. It currently only works with the - em28xx driver. - -config VIDEO_MT9V032 - tristate "Micron MT9V032 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a Video4Linux2 sensor-level driver for the Micron - MT9V032 752x480 CMOS sensor. - -config VIDEO_TCM825X - tristate "TCM825x camera sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a driver for the Toshiba TCM825x VGA camera sensor. - It is used for example in Nokia N800. - -config VIDEO_SR030PC30 - tristate "Siliconfile SR030PC30 sensor support" - depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This driver supports SR030PC30 VGA camera from Siliconfile - -config VIDEO_NOON010PC30 - tristate "Siliconfile NOON010PC30 sensor support" - depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This driver supports NOON010PC30 CIF camera from Siliconfile - -source "drivers/media/video/m5mols/Kconfig" - -config VIDEO_S5K6AA - tristate "Samsung S5K6AAFX sensor support" - depends on MEDIA_CAMERA_SUPPORT - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - ---help--- - This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M - camera sensor with an embedded SoC image signal processor. - -source "drivers/media/video/smiapp/Kconfig" - -comment "Flash devices" - -config VIDEO_ADP1653 - tristate "ADP1653 flash support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a driver for the ADP1653 flash controller. It is used for - example in Nokia N900. - -config VIDEO_AS3645A - tristate "AS3645A flash driver support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This is a driver for the AS3645A and LM3555 flash controllers. It has - build in control for flash, torch and indicator LEDs. - -comment "Video improvement chips" - -config VIDEO_UPD64031A - tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the NEC Electronics uPD64031A Ghost Reduction - video chip. It is most often found in NTSC TV cards made for - Japan and is used to reduce the 'ghosting' effect that can - be present in analog TV broadcasts. - - To compile this driver as a module, choose M here: the - module will be called upd64031a. - -config VIDEO_UPD64083 - tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the NEC Electronics uPD64083 3-Dimensional Y/C - separation video chip. It is used to improve the quality of - the colors of a composite signal. - - To compile this driver as a module, choose M here: the - module will be called upd64083. - -comment "Miscelaneous helper chips" - -config VIDEO_THS7303 - tristate "THS7303 Video Amplifier" - depends on I2C - help - Support for TI THS7303 video amplifier - - To compile this driver as a module, choose M here: the - module will be called ths7303. - -config VIDEO_M52790 - tristate "Mitsubishi M52790 A/V switch" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Mitsubishi M52790 A/V switch. - - To compile this driver as a module, choose M here: the - module will be called m52790. - -endmenu # encoder / decoder chips +if MEDIA_CAMERA_SUPPORT config VIDEO_VIVI tristate "Virtual Video Driver" @@ -877,7 +303,6 @@ source "drivers/media/video/s5p-fimc/Kconfig" source "drivers/media/video/s5p-tv/Kconfig" endif # V4L_PLATFORM_DRIVERS -endif # VIDEO_CAPTURE_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS bool "Memory-to-memory multimedia devices" @@ -955,3 +380,5 @@ config VIDEO_MX2_EMMAPRP conversion. endif # V4L_MEM2MEM_DRIVERS + +endif # MEDIA_CAMERA_SUPPORT diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a0c66923fcde..52a04faa60e8 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -2,73 +2,9 @@ # Makefile for the video capture/playback device drivers. # -msp3400-objs := msp3400-driver.o msp3400-kthreads.o - omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -# Helper modules - -obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o - -# All i2c modules must come first: - -obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o -obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o -obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o -obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o -obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o -obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o -obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o -obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o -obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o -obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o -obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o -obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o -obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o -obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o -obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o -obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o -obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o -obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o -obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o -obj-$(CONFIG_VIDEO_VS6624) += vs6624.o -obj-$(CONFIG_VIDEO_BT819) += bt819.o -obj-$(CONFIG_VIDEO_BT856) += bt856.o -obj-$(CONFIG_VIDEO_BT866) += bt866.o -obj-$(CONFIG_VIDEO_KS0127) += ks0127.o -obj-$(CONFIG_VIDEO_THS7303) += ths7303.o obj-$(CONFIG_VIDEO_VINO) += indycam.o -obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o -obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o -obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o -obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o -obj-$(CONFIG_VIDEO_CS5345) += cs5345.o -obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o -obj-$(CONFIG_VIDEO_M52790) += m52790.o -obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o -obj-$(CONFIG_VIDEO_WM8775) += wm8775.o -obj-$(CONFIG_VIDEO_WM8739) += wm8739.o -obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o -obj-$(CONFIG_VIDEO_CX25840) += cx25840/ -obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o -obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o -obj-$(CONFIG_VIDEO_OV7670) += ov7670.o -obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o -obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o -obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o -obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o -obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o -obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o -obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o -obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o -obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o -obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ -obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o -obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ -obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o -obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o - -obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o @@ -85,16 +21,12 @@ obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o -# And now the v4l2 drivers: - obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o -obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o -obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ @@ -107,7 +39,6 @@ obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o -obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o @@ -140,8 +71,6 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o -obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o - obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c deleted file mode 100644 index 57e87090388d..000000000000 --- a/drivers/media/video/adp1653.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * drivers/media/video/adp1653.c - * - * Copyright (C) 2008--2011 Nokia Corporation - * - * Contact: Sakari Ailus - * - * Contributors: - * Sakari Ailus - * Tuukka Toivonen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * TODO: - * - fault interrupt handling - * - hardware strobe - * - power doesn't need to be ON if all lights are off - * - */ - -#include -#include -#include -#include -#include -#include - -#define TIMEOUT_MAX 820000 -#define TIMEOUT_STEP 54600 -#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ - * TIMEOUT_STEP) -#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ - / TIMEOUT_STEP) -#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) - -/* Write values into ADP1653 registers. */ -static int adp1653_update_hw(struct adp1653_flash *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - u8 out_sel; - u8 config = 0; - int rval; - - out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( - flash->indicator_intensity->val) - << ADP1653_REG_OUT_SEL_ILED_SHIFT; - - switch (flash->led_mode->val) { - case V4L2_FLASH_LED_MODE_NONE: - break; - case V4L2_FLASH_LED_MODE_FLASH: - /* Flash mode, light on with strobe, duration from timer */ - config = ADP1653_REG_CONFIG_TMR_CFG; - config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) - << ADP1653_REG_CONFIG_TMR_SET_SHIFT; - break; - case V4L2_FLASH_LED_MODE_TORCH: - /* Torch mode, light immediately on, duration indefinite */ - out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( - flash->torch_intensity->val) - << ADP1653_REG_OUT_SEL_HPLED_SHIFT; - break; - } - - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); - if (rval < 0) - return rval; - - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); - if (rval < 0) - return rval; - - return 0; -} - -static int adp1653_get_fault(struct adp1653_flash *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int fault; - int rval; - - fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); - if (IS_ERR_VALUE(fault)) - return fault; - - flash->fault |= fault; - - if (!flash->fault) - return 0; - - /* Clear faults. */ - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); - if (IS_ERR_VALUE(rval)) - return rval; - - flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; - - rval = adp1653_update_hw(flash); - if (IS_ERR_VALUE(rval)) - return rval; - - return flash->fault; -} - -static int adp1653_strobe(struct adp1653_flash *flash, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( - flash->indicator_intensity->val) - << ADP1653_REG_OUT_SEL_ILED_SHIFT; - int rval; - - if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; - - if (!enable) - return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, - out_sel); - - out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( - flash->flash_intensity->val) - << ADP1653_REG_OUT_SEL_HPLED_SHIFT; - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); - if (rval) - return rval; - - /* Software strobe using i2c */ - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, - ADP1653_REG_SW_STROBE_SW_STROBE); - if (rval) - return rval; - return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); -} - -/* -------------------------------------------------------------------------- - * V4L2 controls - */ - -static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) -{ - struct adp1653_flash *flash = - container_of(ctrl->handler, struct adp1653_flash, ctrls); - int rval; - - rval = adp1653_get_fault(flash); - if (IS_ERR_VALUE(rval)) - return rval; - - ctrl->cur.val = 0; - - if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) - ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; - if (flash->fault & ADP1653_REG_FAULT_FLT_OT) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; - if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) - ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; - if (flash->fault & ADP1653_REG_FAULT_FLT_OV) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; - - flash->fault = 0; - - return 0; -} - -static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct adp1653_flash *flash = - container_of(ctrl->handler, struct adp1653_flash, ctrls); - int rval; - - rval = adp1653_get_fault(flash); - if (IS_ERR_VALUE(rval)) - return rval; - if ((rval & (ADP1653_REG_FAULT_FLT_SCP | - ADP1653_REG_FAULT_FLT_OT | - ADP1653_REG_FAULT_FLT_OV)) && - (ctrl->id == V4L2_CID_FLASH_STROBE || - ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || - ctrl->id == V4L2_CID_FLASH_LED_MODE)) - return -EBUSY; - - switch (ctrl->id) { - case V4L2_CID_FLASH_STROBE: - return adp1653_strobe(flash, 1); - case V4L2_CID_FLASH_STROBE_STOP: - return adp1653_strobe(flash, 0); - } - - return adp1653_update_hw(flash); -} - -static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { - .g_volatile_ctrl = adp1653_get_ctrl, - .s_ctrl = adp1653_set_ctrl, -}; - -static int adp1653_init_controls(struct adp1653_flash *flash) -{ - struct v4l2_ctrl *fault; - - v4l2_ctrl_handler_init(&flash->ctrls, 9); - - flash->led_mode = - v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_LED_MODE, - V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); - v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_STROBE_SOURCE, - V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); - flash->flash_timeout = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, - flash->platform_data->max_flash_timeout, - TIMEOUT_STEP, - flash->platform_data->max_flash_timeout); - flash->flash_intensity = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_INTENSITY, - ADP1653_FLASH_INTENSITY_MIN, - flash->platform_data->max_flash_intensity, - 1, flash->platform_data->max_flash_intensity); - flash->torch_intensity = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_TORCH_INTENSITY, - ADP1653_TORCH_INTENSITY_MIN, - flash->platform_data->max_torch_intensity, - ADP1653_FLASH_INTENSITY_STEP, - flash->platform_data->max_torch_intensity); - flash->indicator_intensity = - v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_INDICATOR_INTENSITY, - ADP1653_INDICATOR_INTENSITY_MIN, - flash->platform_data->max_indicator_intensity, - ADP1653_INDICATOR_INTENSITY_STEP, - ADP1653_INDICATOR_INTENSITY_MIN); - fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, - V4L2_CID_FLASH_FAULT, 0, - V4L2_FLASH_FAULT_OVER_VOLTAGE - | V4L2_FLASH_FAULT_OVER_TEMPERATURE - | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); - - if (flash->ctrls.error) - return flash->ctrls.error; - - fault->flags |= V4L2_CTRL_FLAG_VOLATILE; - - flash->subdev.ctrl_handler = &flash->ctrls; - return 0; -} - -/* -------------------------------------------------------------------------- - * V4L2 subdev operations - */ - -static int -adp1653_init_device(struct adp1653_flash *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - /* Clear FAULT register by writing zero to OUT_SEL */ - rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); - if (rval < 0) { - dev_err(&client->dev, "failed writing fault register\n"); - return -EIO; - } - - mutex_lock(flash->ctrls.lock); - /* Reset faults before reading new ones. */ - flash->fault = 0; - rval = adp1653_get_fault(flash); - mutex_unlock(flash->ctrls.lock); - if (rval > 0) { - dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); - return -EIO; - } - - mutex_lock(flash->ctrls.lock); - rval = adp1653_update_hw(flash); - mutex_unlock(flash->ctrls.lock); - if (rval) { - dev_err(&client->dev, - "adp1653_update_hw failed at %s\n", __func__); - return -EIO; - } - - return 0; -} - -static int -__adp1653_set_power(struct adp1653_flash *flash, int on) -{ - int ret; - - ret = flash->platform_data->power(&flash->subdev, on); - if (ret < 0) - return ret; - - if (!on) - return 0; - - ret = adp1653_init_device(flash); - if (ret < 0) - flash->platform_data->power(&flash->subdev, 0); - - return ret; -} - -static int -adp1653_set_power(struct v4l2_subdev *subdev, int on) -{ - struct adp1653_flash *flash = to_adp1653_flash(subdev); - int ret = 0; - - mutex_lock(&flash->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (flash->power_count == !on) { - ret = __adp1653_set_power(flash, !!on); - if (ret < 0) - goto done; - } - - /* Update the power count. */ - flash->power_count += on ? 1 : -1; - WARN_ON(flash->power_count < 0); - -done: - mutex_unlock(&flash->power_lock); - return ret; -} - -static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return adp1653_set_power(sd, 1); -} - -static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return adp1653_set_power(sd, 0); -} - -static const struct v4l2_subdev_core_ops adp1653_core_ops = { - .s_power = adp1653_set_power, -}; - -static const struct v4l2_subdev_ops adp1653_ops = { - .core = &adp1653_core_ops, -}; - -static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { - .open = adp1653_open, - .close = adp1653_close, -}; - -/* -------------------------------------------------------------------------- - * I2C driver - */ -#ifdef CONFIG_PM - -static int adp1653_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct adp1653_flash *flash = to_adp1653_flash(subdev); - - if (!flash->power_count) - return 0; - - return __adp1653_set_power(flash, 0); -} - -static int adp1653_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct adp1653_flash *flash = to_adp1653_flash(subdev); - - if (!flash->power_count) - return 0; - - return __adp1653_set_power(flash, 1); -} - -#else - -#define adp1653_suspend NULL -#define adp1653_resume NULL - -#endif /* CONFIG_PM */ - -static int adp1653_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct adp1653_flash *flash; - int ret; - - /* we couldn't work without platform data */ - if (client->dev.platform_data == NULL) - return -ENODEV; - - flash = kzalloc(sizeof(*flash), GFP_KERNEL); - if (flash == NULL) - return -ENOMEM; - - flash->platform_data = client->dev.platform_data; - - mutex_init(&flash->power_lock); - - v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); - flash->subdev.internal_ops = &adp1653_internal_ops; - flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - ret = adp1653_init_controls(flash); - if (ret) - goto free_and_quit; - - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); - if (ret < 0) - goto free_and_quit; - - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; - - return 0; - -free_and_quit: - v4l2_ctrl_handler_free(&flash->ctrls); - kfree(flash); - return ret; -} - -static int __exit adp1653_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct adp1653_flash *flash = to_adp1653_flash(subdev); - - v4l2_device_unregister_subdev(&flash->subdev); - v4l2_ctrl_handler_free(&flash->ctrls); - media_entity_cleanup(&flash->subdev.entity); - kfree(flash); - return 0; -} - -static const struct i2c_device_id adp1653_id_table[] = { - { ADP1653_NAME, 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adp1653_id_table); - -static struct dev_pm_ops adp1653_pm_ops = { - .suspend = adp1653_suspend, - .resume = adp1653_resume, -}; - -static struct i2c_driver adp1653_i2c_driver = { - .driver = { - .name = ADP1653_NAME, - .pm = &adp1653_pm_ops, - }, - .probe = adp1653_probe, - .remove = __exit_p(adp1653_remove), - .id_table = adp1653_id_table, -}; - -module_i2c_driver(adp1653_i2c_driver); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c deleted file mode 100644 index 6bc01fb98ff8..000000000000 --- a/drivers/media/video/adv7170.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1 - * - * Copyright (C) 2002 Maxim Yevtyushkin - * - * Based on adv7176 driver by: - * - * Copyright (C) 1998 Dave Perks - * Copyright (C) 1999 Wolfgang Scherr - * Copyright (C) 2000 Serguei Miridonov - * - some corrections for Pinnacle Systems Inc. DC10plus card. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * This program is free software; 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 - -MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); -MODULE_AUTHOR("Maxim Yevtyushkin"); -MODULE_LICENSE("GPL"); - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* ----------------------------------------------------------------------- */ - -struct adv7170 { - struct v4l2_subdev sd; - unsigned char reg[128]; - - v4l2_std_id norm; - int input; -}; - -static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7170, sd); -} - -static char *inputs[] = { "pass_through", "play_back" }; - -static enum v4l2_mbus_pixelcode adv7170_codes[] = { - V4L2_MBUS_FMT_UYVY8_2X8, - V4L2_MBUS_FMT_UYVY8_1X16, -}; - -/* ----------------------------------------------------------------------- */ - -static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct adv7170 *encoder = to_adv7170(sd); - - encoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int adv7170_write_block(struct v4l2_subdev *sd, - const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct adv7170 *encoder = to_adv7170(sd); - int ret = -1; - u8 reg; - - /* the adv7170 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = - encoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = adv7170_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - return ret; -} - -/* ----------------------------------------------------------------------- */ - -#define TR0MODE 0x4c -#define TR0RST 0x80 - -#define TR1CAPT 0x00 -#define TR1PLAY 0x00 - -static const unsigned char init_NTSC[] = { - 0x00, 0x10, /* MR0 */ - 0x01, 0x20, /* MR1 */ - 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ - 0x03, 0x80, /* MR3 */ - 0x04, 0x30, /* MR4 */ - 0x05, 0x00, /* Reserved */ - 0x06, 0x00, /* Reserved */ - 0x07, TR0MODE, /* TM0 */ - 0x08, TR1CAPT, /* TM1 */ - 0x09, 0x16, /* Fsc0 */ - 0x0a, 0x7c, /* Fsc1 */ - 0x0b, 0xf0, /* Fsc2 */ - 0x0c, 0x21, /* Fsc3 */ - 0x0d, 0x00, /* Subcarrier Phase */ - 0x0e, 0x00, /* Closed Capt. Ext 0 */ - 0x0f, 0x00, /* Closed Capt. Ext 1 */ - 0x10, 0x00, /* Closed Capt. 0 */ - 0x11, 0x00, /* Closed Capt. 1 */ - 0x12, 0x00, /* Pedestal Ctl 0 */ - 0x13, 0x00, /* Pedestal Ctl 1 */ - 0x14, 0x00, /* Pedestal Ctl 2 */ - 0x15, 0x00, /* Pedestal Ctl 3 */ - 0x16, 0x00, /* CGMS_WSS_0 */ - 0x17, 0x00, /* CGMS_WSS_1 */ - 0x18, 0x00, /* CGMS_WSS_2 */ - 0x19, 0x00, /* Teletext Ctl */ -}; - -static const unsigned char init_PAL[] = { - 0x00, 0x71, /* MR0 */ - 0x01, 0x20, /* MR1 */ - 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ - 0x03, 0x80, /* MR3 */ - 0x04, 0x30, /* MR4 */ - 0x05, 0x00, /* Reserved */ - 0x06, 0x00, /* Reserved */ - 0x07, TR0MODE, /* TM0 */ - 0x08, TR1CAPT, /* TM1 */ - 0x09, 0xcb, /* Fsc0 */ - 0x0a, 0x8a, /* Fsc1 */ - 0x0b, 0x09, /* Fsc2 */ - 0x0c, 0x2a, /* Fsc3 */ - 0x0d, 0x00, /* Subcarrier Phase */ - 0x0e, 0x00, /* Closed Capt. Ext 0 */ - 0x0f, 0x00, /* Closed Capt. Ext 1 */ - 0x10, 0x00, /* Closed Capt. 0 */ - 0x11, 0x00, /* Closed Capt. 1 */ - 0x12, 0x00, /* Pedestal Ctl 0 */ - 0x13, 0x00, /* Pedestal Ctl 1 */ - 0x14, 0x00, /* Pedestal Ctl 2 */ - 0x15, 0x00, /* Pedestal Ctl 3 */ - 0x16, 0x00, /* CGMS_WSS_0 */ - 0x17, 0x00, /* CGMS_WSS_1 */ - 0x18, 0x00, /* CGMS_WSS_2 */ - 0x19, 0x00, /* Teletext Ctl */ -}; - - -static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7170 *encoder = to_adv7170(sd); - - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - if (std & V4L2_STD_NTSC) { - adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); - if (encoder->input == 0) - adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - } else if (std & V4L2_STD_PAL) { - adv7170_write_block(sd, init_PAL, sizeof(init_PAL)); - if (encoder->input == 0) - adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - } else { - v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); - encoder->norm = std; - return 0; -} - -static int adv7170_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7170 *encoder = to_adv7170(sd); - - /* RJ: input = 0: input is from decoder - input = 1: input is from ZR36060 - input = 2: color bar */ - - v4l2_dbg(1, debug, sd, "set input from %s\n", - input == 0 ? "decoder" : "ZR36060"); - - switch (input) { - case 0: - adv7170_write(sd, 0x01, 0x20); - adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ - adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - case 1: - adv7170_write(sd, 0x01, 0x00); - adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */ - adv7170_write(sd, 0x02, 0x08); - adv7170_write(sd, 0x07, TR0MODE | TR0RST); - adv7170_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - default: - v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); - encoder->input = input; - return 0; -} - -static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= ARRAY_SIZE(adv7170_codes)) - return -EINVAL; - - *code = adv7170_codes[index]; - return 0; -} - -static int adv7170_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7170_read(sd, 0x7); - - if ((val & 0x40) == (1 << 6)) - mf->code = V4L2_MBUS_FMT_UYVY8_1X16; - else - mf->code = V4L2_MBUS_FMT_UYVY8_2X8; - - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - mf->width = 0; - mf->height = 0; - mf->field = V4L2_FIELD_ANY; - - return 0; -} - -static int adv7170_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7170_read(sd, 0x7); - int ret; - - switch (mf->code) { - case V4L2_MBUS_FMT_UYVY8_2X8: - val &= ~0x40; - break; - - case V4L2_MBUS_FMT_UYVY8_1X16: - val |= 0x40; - break; - - default: - v4l2_dbg(1, debug, sd, - "illegal v4l2_mbus_framefmt code: %d\n", mf->code); - return -EINVAL; - } - - ret = adv7170_write(sd, 0x7, val); - - return ret; -} - -static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops adv7170_core_ops = { - .g_chip_ident = adv7170_g_chip_ident, -}; - -static const struct v4l2_subdev_video_ops adv7170_video_ops = { - .s_std_output = adv7170_s_std_output, - .s_routing = adv7170_s_routing, - .s_mbus_fmt = adv7170_s_fmt, - .g_mbus_fmt = adv7170_g_fmt, - .enum_mbus_fmt = adv7170_enum_fmt, -}; - -static const struct v4l2_subdev_ops adv7170_ops = { - .core = &adv7170_core_ops, - .video = &adv7170_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int adv7170_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7170 *encoder; - struct v4l2_subdev *sd; - int i; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &adv7170_ops); - encoder->norm = V4L2_STD_NTSC; - encoder->input = 0; - - i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); - if (i >= 0) { - i = adv7170_write(sd, 0x07, TR0MODE | TR0RST); - i = adv7170_write(sd, 0x07, TR0MODE); - i = adv7170_read(sd, 0x12); - v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); - } - if (i < 0) - v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); - return 0; -} - -static int adv7170_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_adv7170(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id adv7170_id[] = { - { "adv7170", 0 }, - { "adv7171", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adv7170_id); - -static struct i2c_driver adv7170_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7170", - }, - .probe = adv7170_probe, - .remove = adv7170_remove, - .id_table = adv7170_id, -}; - -module_i2c_driver(adv7170_driver); diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c deleted file mode 100644 index c7640fab5730..000000000000 --- a/drivers/media/video/adv7175.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * adv7175 - adv7175a video encoder driver version 0.0.3 - * - * Copyright (C) 1998 Dave Perks - * Copyright (C) 1999 Wolfgang Scherr - * Copyright (C) 2000 Serguei Miridonov - * - some corrections for Pinnacle Systems Inc. DC10plus card. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (9/9/2002) - * - * This program is free software; 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 - -MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); -MODULE_AUTHOR("Dave Perks"); -MODULE_LICENSE("GPL"); - -#define I2C_ADV7175 0xd4 -#define I2C_ADV7176 0x54 - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* ----------------------------------------------------------------------- */ - -struct adv7175 { - struct v4l2_subdev sd; - v4l2_std_id norm; - int input; -}; - -static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7175, sd); -} - -static char *inputs[] = { "pass_through", "play_back", "color_bar" }; - -static enum v4l2_mbus_pixelcode adv7175_codes[] = { - V4L2_MBUS_FMT_UYVY8_2X8, - V4L2_MBUS_FMT_UYVY8_1X16, -}; - -/* ----------------------------------------------------------------------- */ - -static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int adv7175_write_block(struct v4l2_subdev *sd, - const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = -1; - u8 reg; - - /* the adv7175 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = data[1]; - reg++; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = adv7175_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through) -{ - /* for some reason pass_through NTSC needs - * a different sub-carrier freq to remain stable. */ - if (pass_through) - adv7175_write(sd, 0x02, 0x00); - else - adv7175_write(sd, 0x02, 0x55); - - adv7175_write(sd, 0x03, 0x55); - adv7175_write(sd, 0x04, 0x55); - adv7175_write(sd, 0x05, 0x25); -} - -/* ----------------------------------------------------------------------- */ -/* Output filter: S-Video Composite */ - -#define MR050 0x11 /* 0x09 */ -#define MR060 0x14 /* 0x0c */ - -/* ----------------------------------------------------------------------- */ - -#define TR0MODE 0x46 -#define TR0RST 0x80 - -#define TR1CAPT 0x80 -#define TR1PLAY 0x00 - -static const unsigned char init_common[] = { - - 0x00, MR050, /* MR0, PAL enabled */ - 0x01, 0x00, /* MR1 */ - 0x02, 0x0c, /* subc. freq. */ - 0x03, 0x8c, /* subc. freq. */ - 0x04, 0x79, /* subc. freq. */ - 0x05, 0x26, /* subc. freq. */ - 0x06, 0x40, /* subc. phase */ - - 0x07, TR0MODE, /* TR0, 16bit */ - 0x08, 0x21, /* */ - 0x09, 0x00, /* */ - 0x0a, 0x00, /* */ - 0x0b, 0x00, /* */ - 0x0c, TR1CAPT, /* TR1 */ - 0x0d, 0x4f, /* MR2 */ - 0x0e, 0x00, /* */ - 0x0f, 0x00, /* */ - 0x10, 0x00, /* */ - 0x11, 0x00, /* */ -}; - -static const unsigned char init_pal[] = { - 0x00, MR050, /* MR0, PAL enabled */ - 0x01, 0x00, /* MR1 */ - 0x02, 0x0c, /* subc. freq. */ - 0x03, 0x8c, /* subc. freq. */ - 0x04, 0x79, /* subc. freq. */ - 0x05, 0x26, /* subc. freq. */ - 0x06, 0x40, /* subc. phase */ -}; - -static const unsigned char init_ntsc[] = { - 0x00, MR060, /* MR0, NTSC enabled */ - 0x01, 0x00, /* MR1 */ - 0x02, 0x55, /* subc. freq. */ - 0x03, 0x55, /* subc. freq. */ - 0x04, 0x55, /* subc. freq. */ - 0x05, 0x25, /* subc. freq. */ - 0x06, 0x1a, /* subc. phase */ -}; - -static int adv7175_init(struct v4l2_subdev *sd, u32 val) -{ - /* This is just for testing!!! */ - adv7175_write_block(sd, init_common, sizeof(init_common)); - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - return 0; -} - -static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7175 *encoder = to_adv7175(sd); - - if (std & V4L2_STD_NTSC) { - adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc)); - if (encoder->input == 0) - adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - } else if (std & V4L2_STD_PAL) { - adv7175_write_block(sd, init_pal, sizeof(init_pal)); - if (encoder->input == 0) - adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - } else if (std & V4L2_STD_SECAM) { - /* This is an attempt to convert - * SECAM->PAL (typically it does not work - * due to genlock: when decoder is in SECAM - * and encoder in in PAL the subcarrier can - * not be syncronized with horizontal - * quency) */ - adv7175_write_block(sd, init_pal, sizeof(init_pal)); - if (encoder->input == 0) - adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - } else { - v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); - encoder->norm = std; - return 0; -} - -static int adv7175_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7175 *encoder = to_adv7175(sd); - - /* RJ: input = 0: input is from decoder - input = 1: input is from ZR36060 - input = 2: color bar */ - - switch (input) { - case 0: - adv7175_write(sd, 0x01, 0x00); - - if (encoder->norm & V4L2_STD_NTSC) - set_subcarrier_freq(sd, 1); - - adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */ - if (encoder->norm & V4L2_STD_SECAM) - adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ - else - adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - /*udelay(10);*/ - break; - - case 1: - adv7175_write(sd, 0x01, 0x00); - - if (encoder->norm & V4L2_STD_NTSC) - set_subcarrier_freq(sd, 0); - - adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */ - adv7175_write(sd, 0x0d, 0x49); - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - case 2: - adv7175_write(sd, 0x01, 0x80); - - if (encoder->norm & V4L2_STD_NTSC) - set_subcarrier_freq(sd, 0); - - adv7175_write(sd, 0x0d, 0x49); - adv7175_write(sd, 0x07, TR0MODE | TR0RST); - adv7175_write(sd, 0x07, TR0MODE); - /* udelay(10); */ - break; - - default: - v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); - return -EINVAL; - } - v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); - encoder->input = input; - return 0; -} - -static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= ARRAY_SIZE(adv7175_codes)) - return -EINVAL; - - *code = adv7175_codes[index]; - return 0; -} - -static int adv7175_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7175_read(sd, 0x7); - - if ((val & 0x40) == (1 << 6)) - mf->code = V4L2_MBUS_FMT_UYVY8_1X16; - else - mf->code = V4L2_MBUS_FMT_UYVY8_2X8; - - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - mf->width = 0; - mf->height = 0; - mf->field = V4L2_FIELD_ANY; - - return 0; -} - -static int adv7175_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u8 val = adv7175_read(sd, 0x7); - int ret; - - switch (mf->code) { - case V4L2_MBUS_FMT_UYVY8_2X8: - val &= ~0x40; - break; - - case V4L2_MBUS_FMT_UYVY8_1X16: - val |= 0x40; - break; - - default: - v4l2_dbg(1, debug, sd, - "illegal v4l2_mbus_framefmt code: %d\n", mf->code); - return -EINVAL; - } - - ret = adv7175_write(sd, 0x7, val); - - return ret; -} - -static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); -} - -static int adv7175_s_power(struct v4l2_subdev *sd, int on) -{ - if (on) - adv7175_write(sd, 0x01, 0x00); - else - adv7175_write(sd, 0x01, 0x78); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops adv7175_core_ops = { - .g_chip_ident = adv7175_g_chip_ident, - .init = adv7175_init, - .s_power = adv7175_s_power, -}; - -static const struct v4l2_subdev_video_ops adv7175_video_ops = { - .s_std_output = adv7175_s_std_output, - .s_routing = adv7175_s_routing, - .s_mbus_fmt = adv7175_s_fmt, - .g_mbus_fmt = adv7175_g_fmt, - .enum_mbus_fmt = adv7175_enum_fmt, -}; - -static const struct v4l2_subdev_ops adv7175_ops = { - .core = &adv7175_core_ops, - .video = &adv7175_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int adv7175_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i; - struct adv7175 *encoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &adv7175_ops); - encoder->norm = V4L2_STD_NTSC; - encoder->input = 0; - - i = adv7175_write_block(sd, init_common, sizeof(init_common)); - if (i >= 0) { - i = adv7175_write(sd, 0x07, TR0MODE | TR0RST); - i = adv7175_write(sd, 0x07, TR0MODE); - i = adv7175_read(sd, 0x12); - v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); - } - if (i < 0) - v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); - return 0; -} - -static int adv7175_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_adv7175(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id adv7175_id[] = { - { "adv7175", 0 }, - { "adv7176", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adv7175_id); - -static struct i2c_driver adv7175_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7175", - }, - .probe = adv7175_probe, - .remove = adv7175_remove, - .id_table = adv7175_id, -}; - -module_i2c_driver(adv7175_driver); diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c deleted file mode 100644 index 45ecf8db1eae..000000000000 --- a/drivers/media/video/adv7180.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * adv7180.c Analog Devices ADV7180 video decoder driver - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ADV7180_INPUT_CONTROL_REG 0x00 -#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 -#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 -#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 -#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 -#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 -#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 -#define ADV7180_INPUT_CONTROL_PAL60 0x60 -#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 -#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 -#define ADV7180_INPUT_CONTROL_PAL_N 0x90 -#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 -#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 -#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 -#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 -#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 -#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 -#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f - -#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 -#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 - -#define ADV7180_AUTODETECT_ENABLE_REG 0x07 -#define ADV7180_AUTODETECT_DEFAULT 0x7f -/* Contrast */ -#define ADV7180_CON_REG 0x08 /*Unsigned */ -#define ADV7180_CON_MIN 0 -#define ADV7180_CON_DEF 128 -#define ADV7180_CON_MAX 255 -/* Brightness*/ -#define ADV7180_BRI_REG 0x0a /*Signed */ -#define ADV7180_BRI_MIN -128 -#define ADV7180_BRI_DEF 0 -#define ADV7180_BRI_MAX 127 -/* Hue */ -#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ -#define ADV7180_HUE_MIN -127 -#define ADV7180_HUE_DEF 0 -#define ADV7180_HUE_MAX 128 - -#define ADV7180_ADI_CTRL_REG 0x0e -#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 - -#define ADV7180_PWR_MAN_REG 0x0f -#define ADV7180_PWR_MAN_ON 0x04 -#define ADV7180_PWR_MAN_OFF 0x24 -#define ADV7180_PWR_MAN_RES 0x80 - -#define ADV7180_STATUS1_REG 0x10 -#define ADV7180_STATUS1_IN_LOCK 0x01 -#define ADV7180_STATUS1_AUTOD_MASK 0x70 -#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 -#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 -#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 -#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 -#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 -#define ADV7180_STATUS1_AUTOD_SECAM 0x50 -#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 -#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 - -#define ADV7180_IDENT_REG 0x11 -#define ADV7180_ID_7180 0x18 - -#define ADV7180_ICONF1_ADI 0x40 -#define ADV7180_ICONF1_ACTIVE_LOW 0x01 -#define ADV7180_ICONF1_PSYNC_ONLY 0x10 -#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 -/* Saturation */ -#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ -#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ -#define ADV7180_SAT_MIN 0 -#define ADV7180_SAT_DEF 128 -#define ADV7180_SAT_MAX 255 - -#define ADV7180_IRQ1_LOCK 0x01 -#define ADV7180_IRQ1_UNLOCK 0x02 -#define ADV7180_ISR1_ADI 0x42 -#define ADV7180_ICR1_ADI 0x43 -#define ADV7180_IMR1_ADI 0x44 -#define ADV7180_IMR2_ADI 0x48 -#define ADV7180_IRQ3_AD_CHANGE 0x08 -#define ADV7180_ISR3_ADI 0x4A -#define ADV7180_ICR3_ADI 0x4B -#define ADV7180_IMR3_ADI 0x4C -#define ADV7180_IMR4_ADI 0x50 - -#define ADV7180_NTSC_V_BIT_END_REG 0xE6 -#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F - -struct adv7180_state { - struct v4l2_ctrl_handler ctrl_hdl; - struct v4l2_subdev sd; - struct work_struct work; - struct mutex mutex; /* mutual excl. when accessing chip */ - int irq; - v4l2_std_id curr_norm; - bool autodetect; - u8 input; -}; -#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ - struct adv7180_state, \ - ctrl_hdl)->sd) - -static v4l2_std_id adv7180_std_to_v4l2(u8 status1) -{ - switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { - case ADV7180_STATUS1_AUTOD_NTSM_M_J: - return V4L2_STD_NTSC; - case ADV7180_STATUS1_AUTOD_NTSC_4_43: - return V4L2_STD_NTSC_443; - case ADV7180_STATUS1_AUTOD_PAL_M: - return V4L2_STD_PAL_M; - case ADV7180_STATUS1_AUTOD_PAL_60: - return V4L2_STD_PAL_60; - case ADV7180_STATUS1_AUTOD_PAL_B_G: - return V4L2_STD_PAL; - case ADV7180_STATUS1_AUTOD_SECAM: - return V4L2_STD_SECAM; - case ADV7180_STATUS1_AUTOD_PAL_COMB: - return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; - case ADV7180_STATUS1_AUTOD_SECAM_525: - return V4L2_STD_SECAM; - default: - return V4L2_STD_UNKNOWN; - } -} - -static int v4l2_std_to_adv7180(v4l2_std_id std) -{ - if (std == V4L2_STD_PAL_60) - return ADV7180_INPUT_CONTROL_PAL60; - if (std == V4L2_STD_NTSC_443) - return ADV7180_INPUT_CONTROL_NTSC_443; - if (std == V4L2_STD_PAL_N) - return ADV7180_INPUT_CONTROL_PAL_N; - if (std == V4L2_STD_PAL_M) - return ADV7180_INPUT_CONTROL_PAL_M; - if (std == V4L2_STD_PAL_Nc) - return ADV7180_INPUT_CONTROL_PAL_COMB_N; - - if (std & V4L2_STD_PAL) - return ADV7180_INPUT_CONTROL_PAL_BG; - if (std & V4L2_STD_NTSC) - return ADV7180_INPUT_CONTROL_NTSC_M; - if (std & V4L2_STD_SECAM) - return ADV7180_INPUT_CONTROL_PAL_SECAM; - - return -EINVAL; -} - -static u32 adv7180_status_to_v4l2(u8 status1) -{ - if (!(status1 & ADV7180_STATUS1_IN_LOCK)) - return V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int __adv7180_status(struct i2c_client *client, u32 *status, - v4l2_std_id *std) -{ - int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); - - if (status1 < 0) - return status1; - - if (status) - *status = adv7180_status_to_v4l2(status1); - if (std) - *std = adv7180_std_to_v4l2(status1); - - return 0; -} - -static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7180_state, sd); -} - -static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct adv7180_state *state = to_state(sd); - int err = mutex_lock_interruptible(&state->mutex); - if (err) - return err; - - /* when we are interrupt driven we know the state */ - if (!state->autodetect || state->irq > 0) - *std = state->curr_norm; - else - err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); - - mutex_unlock(&state->mutex); - return err; -} - -static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, - u32 output, u32 config) -{ - struct adv7180_state *state = to_state(sd); - int ret = mutex_lock_interruptible(&state->mutex); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (ret) - return ret; - - /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept - * all inputs and let the card driver take care of validation - */ - if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) - goto out; - - ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); - - if (ret < 0) - goto out; - - ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, ret | input); - state->input = input; -out: - mutex_unlock(&state->mutex); - return ret; -} - -static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - struct adv7180_state *state = to_state(sd); - int ret = mutex_lock_interruptible(&state->mutex); - if (ret) - return ret; - - ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); - mutex_unlock(&state->mutex); - return ret; -} - -static int adv7180_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); -} - -static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7180_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = mutex_lock_interruptible(&state->mutex); - if (ret) - return ret; - - /* all standards -> autodetect */ - if (std == V4L2_STD_ALL) { - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM - | state->input); - if (ret < 0) - goto out; - - __adv7180_status(client, NULL, &state->curr_norm); - state->autodetect = true; - } else { - ret = v4l2_std_to_adv7180(std); - if (ret < 0) - goto out; - - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, - ret | state->input); - if (ret < 0) - goto out; - - state->curr_norm = std; - state->autodetect = false; - } - ret = 0; -out: - mutex_unlock(&state->mutex); - return ret; -} - -static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_adv7180_sd(ctrl); - struct adv7180_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = mutex_lock_interruptible(&state->mutex); - int val; - - if (ret) - return ret; - val = ctrl->val; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); - break; - case V4L2_CID_HUE: - /*Hue is inverted according to HSL chart */ - ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val); - break; - case V4L2_CID_CONTRAST: - ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); - break; - case V4L2_CID_SATURATION: - /* - *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE - *Let's not confuse the user, everybody understands saturation - */ - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, - val); - if (ret < 0) - break; - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, - val); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&state->mutex); - return ret; -} - -static const struct v4l2_ctrl_ops adv7180_ctrl_ops = { - .s_ctrl = adv7180_s_ctrl, -}; - -static int adv7180_init_controls(struct adv7180_state *state) -{ - v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); - - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, - ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF); - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_CONTRAST, ADV7180_CON_MIN, - ADV7180_CON_MAX, 1, ADV7180_CON_DEF); - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_SATURATION, ADV7180_SAT_MIN, - ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF); - v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_HUE, ADV7180_HUE_MIN, - ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); - state->sd.ctrl_handler = &state->ctrl_hdl; - if (state->ctrl_hdl.error) { - int err = state->ctrl_hdl.error; - - v4l2_ctrl_handler_free(&state->ctrl_hdl); - return err; - } - v4l2_ctrl_handler_setup(&state->ctrl_hdl); - - return 0; -} -static void adv7180_exit_controls(struct adv7180_state *state) -{ - v4l2_ctrl_handler_free(&state->ctrl_hdl); -} - -static const struct v4l2_subdev_video_ops adv7180_video_ops = { - .querystd = adv7180_querystd, - .g_input_status = adv7180_g_input_status, - .s_routing = adv7180_s_routing, -}; - -static const struct v4l2_subdev_core_ops adv7180_core_ops = { - .g_chip_ident = adv7180_g_chip_ident, - .s_std = adv7180_s_std, - .queryctrl = v4l2_subdev_queryctrl, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, -}; - -static const struct v4l2_subdev_ops adv7180_ops = { - .core = &adv7180_core_ops, - .video = &adv7180_video_ops, -}; - -static void adv7180_work(struct work_struct *work) -{ - struct adv7180_state *state = container_of(work, struct adv7180_state, - work); - struct i2c_client *client = v4l2_get_subdevdata(&state->sd); - u8 isr3; - - mutex_lock(&state->mutex); - i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); - isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); - /* clear */ - i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); - i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); - - if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) - __adv7180_status(client, NULL, &state->curr_norm); - mutex_unlock(&state->mutex); - - enable_irq(state->irq); -} - -static irqreturn_t adv7180_irq(int irq, void *devid) -{ - struct adv7180_state *state = devid; - - schedule_work(&state->work); - - disable_irq_nosync(state->irq); - - return IRQ_HANDLED; -} - -static int init_device(struct i2c_client *client, struct adv7180_state *state) -{ - int ret; - - /* Initialize adv7180 */ - /* Enable autodetection */ - if (state->autodetect) { - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM - | state->input); - if (ret < 0) - return ret; - - ret = - i2c_smbus_write_byte_data(client, - ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) - return ret; - } else { - ret = v4l2_std_to_adv7180(state->curr_norm); - if (ret < 0) - return ret; - - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ret | state->input); - if (ret < 0) - return ret; - - } - /* ITU-R BT.656-4 compatible */ - ret = i2c_smbus_write_byte_data(client, - ADV7180_EXTENDED_OUTPUT_CONTROL_REG, - ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); - if (ret < 0) - return ret; - - /* Manually set V bit end position in NTSC mode */ - ret = i2c_smbus_write_byte_data(client, - ADV7180_NTSC_V_BIT_END_REG, - ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); - if (ret < 0) - return ret; - - /* read current norm */ - __adv7180_status(client, NULL, &state->curr_norm); - - /* register for interrupts */ - if (state->irq > 0) { - ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME, - state); - if (ret) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); - if (ret < 0) - return ret; - - /* config the Interrupt pin to be active low */ - ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, - ADV7180_ICONF1_ACTIVE_LOW | - ADV7180_ICONF1_PSYNC_ONLY); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); - if (ret < 0) - return ret; - - /* enable AD change interrupts interrupts */ - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, - ADV7180_IRQ3_AD_CHANGE); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - 0); - if (ret < 0) - return ret; - } - - return 0; -} - -static __devinit int adv7180_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7180_state *state; - struct v4l2_subdev *sd; - int ret; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); - - state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); - if (state == NULL) { - ret = -ENOMEM; - goto err; - } - - state->irq = client->irq; - INIT_WORK(&state->work, adv7180_work); - mutex_init(&state->mutex); - state->autodetect = true; - state->input = 0; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7180_ops); - - ret = adv7180_init_controls(state); - if (ret) - goto err_unreg_subdev; - ret = init_device(client, state); - if (ret) - goto err_free_ctrl; - return 0; - -err_free_ctrl: - adv7180_exit_controls(state); -err_unreg_subdev: - mutex_destroy(&state->mutex); - v4l2_device_unregister_subdev(sd); - kfree(state); -err: - printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret); - return ret; -} - -static __devexit int adv7180_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7180_state *state = to_state(sd); - - if (state->irq > 0) { - free_irq(client->irq, state); - if (cancel_work_sync(&state->work)) { - /* - * Work was pending, therefore we need to enable - * IRQ here to balance the disable_irq() done in the - * interrupt handler. - */ - enable_irq(state->irq); - } - } - - mutex_destroy(&state->mutex); - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id adv7180_id[] = { - {KBUILD_MODNAME, 0}, - {}, -}; - -#ifdef CONFIG_PM -static int adv7180_suspend(struct i2c_client *client, pm_message_t state) -{ - int ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, - ADV7180_PWR_MAN_OFF); - if (ret < 0) - return ret; - return 0; -} - -static int adv7180_resume(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7180_state *state = to_state(sd); - int ret; - - ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, - ADV7180_PWR_MAN_ON); - if (ret < 0) - return ret; - ret = init_device(client, state); - if (ret < 0) - return ret; - return 0; -} -#endif - -MODULE_DEVICE_TABLE(i2c, adv7180_id); - -static struct i2c_driver adv7180_driver = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - }, - .probe = adv7180_probe, - .remove = __devexit_p(adv7180_remove), -#ifdef CONFIG_PM - .suspend = adv7180_suspend, - .resume = adv7180_resume, -#endif - .id_table = adv7180_id, -}; - -module_i2c_driver(adv7180_driver); - -MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); -MODULE_AUTHOR("Mocean Laboratories"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c deleted file mode 100644 index e1d4c89d7140..000000000000 --- a/drivers/media/video/adv7183.c +++ /dev/null @@ -1,699 +0,0 @@ -/* - * adv7183.c Analog Devices ADV7183 video decoder driver - * - * Copyright (c) 2011 Analog Devices 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. - * - * 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 - -#include -#include -#include -#include - -#include "adv7183_regs.h" - -struct adv7183 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - - v4l2_std_id std; /* Current set standard */ - u32 input; - u32 output; - unsigned reset_pin; - unsigned oe_pin; - struct v4l2_mbus_framefmt fmt; -}; - -/* EXAMPLES USING 27 MHz CLOCK - * Mode 1 CVBS Input (Composite Video on AIN5) - * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8. - */ -static const unsigned char adv7183_init_regs[] = { - ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */ - ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */ - ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */ - ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */ - ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */ - /* ADI recommended programming sequence */ - ADV7183_ADI_CTRL, 0x80, - ADV7183_CTI_DNR_CTRL_4, 0x20, - 0x52, 0x18, - 0x58, 0xED, - 0x77, 0xC5, - 0x7C, 0x93, - 0x7D, 0x00, - 0xD0, 0x48, - 0xD5, 0xA0, - 0xD7, 0xEA, - ADV7183_SD_SATURATION_CR, 0x3E, - ADV7183_PAL_V_END, 0x3E, - ADV7183_PAL_F_TOGGLE, 0x0F, - ADV7183_ADI_CTRL, 0x00, -}; - -static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7183, sd); -} -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct adv7183, hdl)->sd; -} - -static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int adv7183_writeregs(struct v4l2_subdev *sd, - const unsigned char *regs, unsigned int num) -{ - unsigned char reg, data; - unsigned int cnt = 0; - - if (num & 0x1) { - v4l2_err(sd, "invalid regs array\n"); - return -1; - } - - while (cnt < num) { - reg = *regs++; - data = *regs++; - cnt += 2; - - adv7183_write(sd, reg, data); - } - return 0; -} - -static int adv7183_log_status(struct v4l2_subdev *sd) -{ - struct adv7183 *decoder = to_adv7183(sd); - - v4l2_info(sd, "adv7183: Input control = 0x%02x\n", - adv7183_read(sd, ADV7183_IN_CTRL)); - v4l2_info(sd, "adv7183: Video selection = 0x%02x\n", - adv7183_read(sd, ADV7183_VD_SEL)); - v4l2_info(sd, "adv7183: Output control = 0x%02x\n", - adv7183_read(sd, ADV7183_OUT_CTRL)); - v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n", - adv7183_read(sd, ADV7183_EXT_OUT_CTRL)); - v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n", - adv7183_read(sd, ADV7183_AUTO_DET_EN)); - v4l2_info(sd, "adv7183: Contrast = 0x%02x\n", - adv7183_read(sd, ADV7183_CONTRAST)); - v4l2_info(sd, "adv7183: Brightness = 0x%02x\n", - adv7183_read(sd, ADV7183_BRIGHTNESS)); - v4l2_info(sd, "adv7183: Hue = 0x%02x\n", - adv7183_read(sd, ADV7183_HUE)); - v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n", - adv7183_read(sd, ADV7183_DEF_Y)); - v4l2_info(sd, "adv7183: Default value C = 0x%02x\n", - adv7183_read(sd, ADV7183_DEF_C)); - v4l2_info(sd, "adv7183: ADI control = 0x%02x\n", - adv7183_read(sd, ADV7183_ADI_CTRL)); - v4l2_info(sd, "adv7183: Power Management = 0x%02x\n", - adv7183_read(sd, ADV7183_POW_MANAGE)); - v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_STATUS_1), - adv7183_read(sd, ADV7183_STATUS_2), - adv7183_read(sd, ADV7183_STATUS_3)); - v4l2_info(sd, "adv7183: Ident = 0x%02x\n", - adv7183_read(sd, ADV7183_IDENT)); - v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n", - adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL)); - v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n", - adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1)); - v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_SHAP_FILT_CTRL), - adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2)); - v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n", - adv7183_read(sd, ADV7183_COMB_FILT_CTRL)); - v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n", - adv7183_read(sd, ADV7183_ADI_CTRL_2)); - v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n", - adv7183_read(sd, ADV7183_PIX_DELAY_CTRL)); - v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n", - adv7183_read(sd, ADV7183_MISC_GAIN_CTRL)); - v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n", - adv7183_read(sd, ADV7183_AGC_MODE_CTRL)); - v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1), - adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2)); - v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1), - adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2)); - v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1), - adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2), - adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); - v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_HS_POS_CTRL_1), - adv7183_read(sd, ADV7183_HS_POS_CTRL_2), - adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); - v4l2_info(sd, "adv7183: Polarity = 0x%02x\n", - adv7183_read(sd, ADV7183_POLARITY)); - v4l2_info(sd, "adv7183: ADC control = 0x%02x\n", - adv7183_read(sd, ADV7183_ADC_CTRL)); - v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_SD_OFFSET_CB), - adv7183_read(sd, ADV7183_SD_OFFSET_CR)); - v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n", - adv7183_read(sd, ADV7183_SD_SATURATION_CB), - adv7183_read(sd, ADV7183_SD_SATURATION_CR)); - v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n", - adv7183_read(sd, ADV7183_DRIVE_STR)); - v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name); - return 0; -} - -static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct adv7183 *decoder = to_adv7183(sd); - - *std = decoder->std; - return 0; -} - -static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7183 *decoder = to_adv7183(sd); - int reg; - - reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; - if (std == V4L2_STD_PAL_60) - reg |= 0x60; - else if (std == V4L2_STD_NTSC_443) - reg |= 0x70; - else if (std == V4L2_STD_PAL_N) - reg |= 0x90; - else if (std == V4L2_STD_PAL_M) - reg |= 0xA0; - else if (std == V4L2_STD_PAL_Nc) - reg |= 0xC0; - else if (std & V4L2_STD_PAL) - reg |= 0x80; - else if (std & V4L2_STD_NTSC) - reg |= 0x50; - else if (std & V4L2_STD_SECAM) - reg |= 0xE0; - else - return -EINVAL; - adv7183_write(sd, ADV7183_IN_CTRL, reg); - - decoder->std = std; - - return 0; -} - -static int adv7183_reset(struct v4l2_subdev *sd, u32 val) -{ - int reg; - - reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80; - adv7183_write(sd, ADV7183_POW_MANAGE, reg); - /* wait 5ms before any further i2c writes are performed */ - usleep_range(5000, 10000); - return 0; -} - -static int adv7183_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7183 *decoder = to_adv7183(sd); - int reg; - - if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT)) - return -EINVAL; - - if (input != decoder->input) { - decoder->input = input; - reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0; - switch (input) { - case ADV7183_COMPOSITE1: - reg |= 0x1; - break; - case ADV7183_COMPOSITE2: - reg |= 0x2; - break; - case ADV7183_COMPOSITE3: - reg |= 0x3; - break; - case ADV7183_COMPOSITE4: - reg |= 0x4; - break; - case ADV7183_COMPOSITE5: - reg |= 0x5; - break; - case ADV7183_COMPOSITE6: - reg |= 0xB; - break; - case ADV7183_COMPOSITE7: - reg |= 0xC; - break; - case ADV7183_COMPOSITE8: - reg |= 0xD; - break; - case ADV7183_COMPOSITE9: - reg |= 0xE; - break; - case ADV7183_COMPOSITE10: - reg |= 0xF; - break; - case ADV7183_SVIDEO0: - reg |= 0x6; - break; - case ADV7183_SVIDEO1: - reg |= 0x7; - break; - case ADV7183_SVIDEO2: - reg |= 0x8; - break; - case ADV7183_COMPONENT0: - reg |= 0x9; - break; - case ADV7183_COMPONENT1: - reg |= 0xA; - break; - default: - break; - } - adv7183_write(sd, ADV7183_IN_CTRL, reg); - } - - if (output != decoder->output) { - decoder->output = output; - reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0; - switch (output) { - case ADV7183_16BIT_OUT: - reg |= 0x9; - break; - default: - reg |= 0xC; - break; - } - adv7183_write(sd, ADV7183_OUT_CTRL, reg); - } - - return 0; -} - -static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - int val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (val < 0) - val = 127 - val; - adv7183_write(sd, ADV7183_BRIGHTNESS, val); - break; - case V4L2_CID_CONTRAST: - adv7183_write(sd, ADV7183_CONTRAST, val); - break; - case V4L2_CID_SATURATION: - adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8); - adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF)); - break; - case V4L2_CID_HUE: - adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8); - adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF)); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct adv7183 *decoder = to_adv7183(sd); - int reg; - - /* enable autodetection block */ - reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; - adv7183_write(sd, ADV7183_IN_CTRL, reg); - - /* wait autodetection switch */ - mdelay(10); - - /* get autodetection result */ - reg = adv7183_read(sd, ADV7183_STATUS_1); - switch ((reg >> 0x4) & 0x7) { - case 0: - *std = V4L2_STD_NTSC; - break; - case 1: - *std = V4L2_STD_NTSC_443; - break; - case 2: - *std = V4L2_STD_PAL_M; - break; - case 3: - *std = V4L2_STD_PAL_60; - break; - case 4: - *std = V4L2_STD_PAL; - break; - case 5: - *std = V4L2_STD_SECAM; - break; - case 6: - *std = V4L2_STD_PAL_Nc; - break; - case 7: - *std = V4L2_STD_SECAM; - break; - default: - *std = V4L2_STD_UNKNOWN; - break; - } - - /* after std detection, write back user set std */ - adv7183_s_std(sd, decoder->std); - return 0; -} - -static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - int reg; - - *status = V4L2_IN_ST_NO_SIGNAL; - reg = adv7183_read(sd, ADV7183_STATUS_1); - if (reg < 0) - return reg; - if (reg & 0x1) - *status = 0; - return 0; -} - -static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index > 0) - return -EINVAL; - - *code = V4L2_MBUS_FMT_UYVY8_2X8; - return 0; -} - -static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct adv7183 *decoder = to_adv7183(sd); - - fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - if (decoder->std & V4L2_STD_525_60) { - fmt->field = V4L2_FIELD_SEQ_TB; - fmt->width = 720; - fmt->height = 480; - } else { - fmt->field = V4L2_FIELD_SEQ_BT; - fmt->width = 720; - fmt->height = 576; - } - return 0; -} - -static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct adv7183 *decoder = to_adv7183(sd); - - adv7183_try_mbus_fmt(sd, fmt); - decoder->fmt = *fmt; - return 0; -} - -static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct adv7183 *decoder = to_adv7183(sd); - - *fmt = decoder->fmt; - return 0; -} - -static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct adv7183 *decoder = to_adv7183(sd); - - if (enable) - gpio_direction_output(decoder->oe_pin, 0); - else - gpio_direction_output(decoder->oe_pin, 1); - udelay(1); - return 0; -} - -static int adv7183_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - int rev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* 0x11 for adv7183, 0x13 for adv7183b */ - rev = adv7183_read(sd, ADV7183_IDENT); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = adv7183_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { - .s_ctrl = adv7183_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops adv7183_core_ops = { - .log_status = adv7183_log_status, - .g_std = adv7183_g_std, - .s_std = adv7183_s_std, - .reset = adv7183_reset, - .g_chip_ident = adv7183_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = adv7183_g_register, - .s_register = adv7183_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops adv7183_video_ops = { - .s_routing = adv7183_s_routing, - .querystd = adv7183_querystd, - .g_input_status = adv7183_g_input_status, - .enum_mbus_fmt = adv7183_enum_mbus_fmt, - .try_mbus_fmt = adv7183_try_mbus_fmt, - .s_mbus_fmt = adv7183_s_mbus_fmt, - .g_mbus_fmt = adv7183_g_mbus_fmt, - .s_stream = adv7183_s_stream, -}; - -static const struct v4l2_subdev_ops adv7183_ops = { - .core = &adv7183_core_ops, - .video = &adv7183_video_ops, -}; - -static int adv7183_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7183 *decoder; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - int ret; - struct v4l2_mbus_framefmt fmt; - const unsigned *pin_array; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - pin_array = client->dev.platform_data; - if (pin_array == NULL) - return -EINVAL; - - decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - - decoder->reset_pin = pin_array[0]; - decoder->oe_pin = pin_array[1]; - - if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) { - v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); - ret = -EBUSY; - goto err_free_decoder; - } - - if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) { - v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); - ret = -EBUSY; - goto err_free_reset; - } - - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &adv7183_ops); - - hdl = &decoder->hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080); - v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, - V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080); - /* hook the control handler into the driver */ - sd->ctrl_handler = hdl; - if (hdl->error) { - ret = hdl->error; - - v4l2_ctrl_handler_free(hdl); - goto err_free_oe; - } - - /* v4l2 doesn't support an autodetect standard, pick PAL as default */ - decoder->std = V4L2_STD_PAL; - decoder->input = ADV7183_COMPOSITE4; - decoder->output = ADV7183_8BIT_OUT; - - gpio_direction_output(decoder->oe_pin, 1); - /* reset chip */ - gpio_direction_output(decoder->reset_pin, 0); - /* reset pulse width at least 5ms */ - mdelay(10); - gpio_direction_output(decoder->reset_pin, 1); - /* wait 5ms before any further i2c writes are performed */ - mdelay(5); - - adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); - adv7183_s_std(sd, decoder->std); - fmt.width = 720; - fmt.height = 576; - adv7183_s_mbus_fmt(sd, &fmt); - - /* initialize the hardware to the default control values */ - ret = v4l2_ctrl_handler_setup(hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - goto err_free_oe; - } - - return 0; -err_free_oe: - gpio_free(decoder->oe_pin); -err_free_reset: - gpio_free(decoder->reset_pin); -err_free_decoder: - kfree(decoder); - return ret; -} - -static int adv7183_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7183 *decoder = to_adv7183(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - gpio_free(decoder->oe_pin); - gpio_free(decoder->reset_pin); - kfree(decoder); - return 0; -} - -static const struct i2c_device_id adv7183_id[] = { - {"adv7183", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, adv7183_id); - -static struct i2c_driver adv7183_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7183", - }, - .probe = adv7183_probe, - .remove = __devexit_p(adv7183_remove), - .id_table = adv7183_id, -}; - -static __init int adv7183_init(void) -{ - return i2c_add_driver(&adv7183_driver); -} - -static __exit void adv7183_exit(void) -{ - i2c_del_driver(&adv7183_driver); -} - -module_init(adv7183_init); -module_exit(adv7183_exit); - -MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver"); -MODULE_AUTHOR("Scott Jiang "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h deleted file mode 100644 index 4a5b7d211d2f..000000000000 --- a/drivers/media/video/adv7183_regs.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * adv7183 - Analog Devices ADV7183 video decoder registers - * - * Copyright (c) 2011 Analog Devices 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. - * - * 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 _ADV7183_REGS_H_ -#define _ADV7183_REGS_H_ - -#define ADV7183_IN_CTRL 0x00 /* Input control */ -#define ADV7183_VD_SEL 0x01 /* Video selection */ -#define ADV7183_OUT_CTRL 0x03 /* Output control */ -#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */ -#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */ -#define ADV7183_CONTRAST 0x08 /* Contrast */ -#define ADV7183_BRIGHTNESS 0x0A /* Brightness */ -#define ADV7183_HUE 0x0B /* Hue */ -#define ADV7183_DEF_Y 0x0C /* Default value Y */ -#define ADV7183_DEF_C 0x0D /* Default value C */ -#define ADV7183_ADI_CTRL 0x0E /* ADI control */ -#define ADV7183_POW_MANAGE 0x0F /* Power Management */ -#define ADV7183_STATUS_1 0x10 /* Status 1 */ -#define ADV7183_IDENT 0x11 /* Ident */ -#define ADV7183_STATUS_2 0x12 /* Status 2 */ -#define ADV7183_STATUS_3 0x13 /* Status 3 */ -#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */ -#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */ -#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */ -#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */ -#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */ -#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */ -#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */ -#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */ -#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */ -#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */ -#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */ -#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */ -#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */ -#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ -#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ -#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ -#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ -#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ -#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ -#define ADV7183_POLARITY 0x37 /* Polarity */ -#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ -#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ -#define ADV7183_ADC_CTRL 0x3A /* ADC control */ -#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */ -#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */ -#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */ -#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */ -#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */ -#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */ -#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */ -#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */ -#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */ -#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */ -#define ADV7183_LOCK_CNT 0x51 /* Lock count */ -#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */ -#define ADV7183_VBI_INFO 0x90 /* VBI info */ -#define ADV7183_WSS_1 0x91 /* WSS 1 */ -#define ADV7183_WSS_2 0x92 /* WSS 2 */ -#define ADV7183_EDTV_1 0x93 /* EDTV 1 */ -#define ADV7183_EDTV_2 0x94 /* EDTV 2 */ -#define ADV7183_EDTV_3 0x95 /* EDTV 3 */ -#define ADV7183_CGMS_1 0x96 /* CGMS 1 */ -#define ADV7183_CGMS_2 0x97 /* CGMS 2 */ -#define ADV7183_CGMS_3 0x98 /* CGMS 3 */ -#define ADV7183_CCAP_1 0x99 /* CCAP 1 */ -#define ADV7183_CCAP_2 0x9A /* CCAP 2 */ -#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */ -#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */ -#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ -#define ADV7183_CRC_EN 0xB2 /* CRC enable */ -#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ -#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ -#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ -#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ -#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ -#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */ -#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */ -#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */ -#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */ -#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */ -#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */ -#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */ -#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */ -#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */ -#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */ -#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */ -#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */ - -#endif diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c deleted file mode 100644 index 2b5aa676a84e..000000000000 --- a/drivers/media/video/adv7343.c +++ /dev/null @@ -1,476 +0,0 @@ -/* - * adv7343 - ADV7343 Video Encoder Driver - * - * The encoder hardware does not support SECAM. - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; 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 -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "adv7343_regs.h" - -MODULE_DESCRIPTION("ADV7343 video encoder driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-1"); - -struct adv7343_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - u8 reg00; - u8 reg01; - u8 reg02; - u8 reg35; - u8 reg80; - u8 reg82; - u32 output; - v4l2_std_id std; -}; - -static inline struct adv7343_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7343_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd; -} - -static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static const u8 adv7343_init_reg_val[] = { - ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT, - ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT, - - ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT, - ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT, - ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT, - ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT, - ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT, - ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT, - ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT, - - ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT, - ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT, - ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT, - ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT, - ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT, - ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT, - ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT, - ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT, - - ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT, - ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT, - ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT, -}; - -/* - * 2^32 - * FSC(reg) = FSC (HZ) * -------- - * 27000000 - */ -static const struct adv7343_std_info stdinfo[] = { - { - /* FSC(Hz) = 3,579,545.45 Hz */ - SD_STD_NTSC, 569408542, V4L2_STD_NTSC, - }, { - /* FSC(Hz) = 3,575,611.00 Hz */ - SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, - }, { - /* FSC(Hz) = 3,582,056.00 */ - SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, - }, -}; - -static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7343_state *state = to_state(sd); - struct adv7343_std_info *std_info; - int num_std; - char *fsc_ptr; - u8 reg, val; - int err = 0; - int i = 0; - - std_info = (struct adv7343_std_info *)stdinfo; - num_std = ARRAY_SIZE(stdinfo); - - for (i = 0; i < num_std; i++) { - if (std_info[i].stdid & std) - break; - } - - if (i == num_std) { - v4l2_dbg(1, debug, sd, - "Invalid std or std is not supported: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - - /* Set the standard */ - val = state->reg80 & (~(SD_STD_MASK)); - val |= std_info[i].standard_val3; - err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); - if (err < 0) - goto setstd_exit; - - state->reg80 = val; - - /* Configure the input mode register */ - val = state->reg01 & (~((u8) INPUT_MODE_MASK)); - val |= SD_INPUT_MODE; - err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val); - if (err < 0) - goto setstd_exit; - - state->reg01 = val; - - /* Program the sub carrier frequency registers */ - fsc_ptr = (unsigned char *)&std_info[i].fsc_val; - reg = ADV7343_FSC_REG0; - for (i = 0; i < 4; i++, reg++, fsc_ptr++) { - err = adv7343_write(sd, reg, *fsc_ptr); - if (err < 0) - goto setstd_exit; - } - - val = state->reg80; - - /* Filter settings */ - if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) - val &= 0x03; - else if (std & ~V4L2_STD_SECAM) - val |= 0x04; - - err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); - if (err < 0) - goto setstd_exit; - - state->reg80 = val; - -setstd_exit: - if (err != 0) - v4l2_err(sd, "Error setting std, write failed\n"); - - return err; -} - -static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) -{ - struct adv7343_state *state = to_state(sd); - unsigned char val; - int err = 0; - - if (output_type > ADV7343_SVIDEO_ID) { - v4l2_dbg(1, debug, sd, - "Invalid output type or output type not supported:%d\n", - output_type); - return -EINVAL; - } - - /* Enable Appropriate DAC */ - val = state->reg00 & 0x03; - - if (output_type == ADV7343_COMPOSITE_ID) - val |= ADV7343_COMPOSITE_POWER_VALUE; - else if (output_type == ADV7343_COMPONENT_ID) - val |= ADV7343_COMPONENT_POWER_VALUE; - else - val |= ADV7343_SVIDEO_POWER_VALUE; - - err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); - if (err < 0) - goto setoutput_exit; - - state->reg00 = val; - - /* Enable YUV output */ - val = state->reg02 | YUV_OUTPUT_SELECT; - err = adv7343_write(sd, ADV7343_MODE_REG0, val); - if (err < 0) - goto setoutput_exit; - - state->reg02 = val; - - /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ - val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); - err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); - if (err < 0) - goto setoutput_exit; - - state->reg82 = val; - - /* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to - * zero */ - val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI); - err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val); - if (err < 0) - goto setoutput_exit; - - state->reg35 = val; - -setoutput_exit: - if (err != 0) - v4l2_err(sd, "Error setting output, write failed\n"); - - return err; -} - -static int adv7343_log_status(struct v4l2_subdev *sd) -{ - struct adv7343_state *state = to_state(sd); - - v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); - v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : - ((state->output == 1) ? "Component" : "S-Video")); - return 0; -} - -static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, - ctrl->val); - - case V4L2_CID_HUE: - return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val); - - case V4L2_CID_GAIN: - return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val); - } - return -EINVAL; -} - -static int adv7343_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0); -} - -static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { - .s_ctrl = adv7343_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops adv7343_core_ops = { - .log_status = adv7343_log_status, - .g_chip_ident = adv7343_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7343_state *state = to_state(sd); - int err = 0; - - if (state->std == std) - return 0; - - err = adv7343_setstd(sd, std); - if (!err) - state->std = std; - - return err; -} - -static int adv7343_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7343_state *state = to_state(sd); - int err = 0; - - if (state->output == output) - return 0; - - err = adv7343_setoutput(sd, output); - if (!err) - state->output = output; - - return err; -} - -static const struct v4l2_subdev_video_ops adv7343_video_ops = { - .s_std_output = adv7343_s_std_output, - .s_routing = adv7343_s_routing, -}; - -static const struct v4l2_subdev_ops adv7343_ops = { - .core = &adv7343_core_ops, - .video = &adv7343_video_ops, -}; - -static int adv7343_initialize(struct v4l2_subdev *sd) -{ - struct adv7343_state *state = to_state(sd); - int err = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) { - - err = adv7343_write(sd, adv7343_init_reg_val[i], - adv7343_init_reg_val[i+1]); - if (err) { - v4l2_err(sd, "Error initializing\n"); - return err; - } - } - - /* Configure for default video standard */ - err = adv7343_setoutput(sd, state->output); - if (err < 0) { - v4l2_err(sd, "Error setting output during init\n"); - return -EINVAL; - } - - err = adv7343_setstd(sd, state->std); - if (err < 0) { - v4l2_err(sd, "Error setting std during init\n"); - return -EINVAL; - } - - return err; -} - -static int adv7343_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7343_state *state; - int err; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - state->reg00 = 0x80; - state->reg01 = 0x00; - state->reg02 = 0x20; - state->reg35 = 0x00; - state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT; - state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT; - - state->output = ADV7343_COMPOSITE_ID; - state->std = V4L2_STD_NTSC; - - v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops); - - v4l2_ctrl_handler_init(&state->hdl, 2); - v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, - V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN, - ADV7343_BRIGHTNESS_MAX, 1, - ADV7343_BRIGHTNESS_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, - V4L2_CID_HUE, ADV7343_HUE_MIN, - ADV7343_HUE_MAX, 1, - ADV7343_HUE_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, - V4L2_CID_GAIN, ADV7343_GAIN_MIN, - ADV7343_GAIN_MAX, 1, - ADV7343_GAIN_DEF); - state->sd.ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_handler_setup(&state->hdl); - - err = adv7343_initialize(&state->sd); - if (err) { - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - } - return err; -} - -static int adv7343_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7343_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - - return 0; -} - -static const struct i2c_device_id adv7343_id[] = { - {"adv7343", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, adv7343_id); - -static struct i2c_driver adv7343_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7343", - }, - .probe = adv7343_probe, - .remove = adv7343_remove, - .id_table = adv7343_id, -}; - -module_i2c_driver(adv7343_driver); diff --git a/drivers/media/video/adv7343_regs.h b/drivers/media/video/adv7343_regs.h deleted file mode 100644 index 446606764346..000000000000 --- a/drivers/media/video/adv7343_regs.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * ADV7343 encoder related structure and register definitions - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ADV7343_REG_H -#define ADV7343_REGS_H - -struct adv7343_std_info { - u32 standard_val3; - u32 fsc_val; - v4l2_std_id stdid; -}; - -/* Register offset macros */ -#define ADV7343_POWER_MODE_REG (0x00) -#define ADV7343_MODE_SELECT_REG (0x01) -#define ADV7343_MODE_REG0 (0x02) - -#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b) - -#define ADV7343_SOFT_RESET (0x17) - -#define ADV7343_HD_MODE_REG1 (0x30) -#define ADV7343_HD_MODE_REG2 (0x31) -#define ADV7343_HD_MODE_REG3 (0x32) -#define ADV7343_HD_MODE_REG4 (0x33) -#define ADV7343_HD_MODE_REG5 (0x34) -#define ADV7343_HD_MODE_REG6 (0x35) - -#define ADV7343_HD_MODE_REG7 (0x39) - -#define ADV7343_SD_MODE_REG1 (0x80) -#define ADV7343_SD_MODE_REG2 (0x82) -#define ADV7343_SD_MODE_REG3 (0x83) -#define ADV7343_SD_MODE_REG4 (0x84) -#define ADV7343_SD_MODE_REG5 (0x86) -#define ADV7343_SD_MODE_REG6 (0x87) -#define ADV7343_SD_MODE_REG7 (0x88) -#define ADV7343_SD_MODE_REG8 (0x89) - -#define ADV7343_FSC_REG0 (0x8C) -#define ADV7343_FSC_REG1 (0x8D) -#define ADV7343_FSC_REG2 (0x8E) -#define ADV7343_FSC_REG3 (0x8F) - -#define ADV7343_SD_CGMS_WSS0 (0x99) - -#define ADV7343_SD_HUE_REG (0xA0) -#define ADV7343_SD_BRIGHTNESS_WSS (0xA1) - -/* Default values for the registers */ -#define ADV7343_POWER_MODE_REG_DEFAULT (0x10) -#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default - 720p EAVSAV code*/ -#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data - valid */ -#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ -#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */ -#define ADV7343_HD_MODE_REG5_DEFAULT (0x08) -#define ADV7343_HD_MODE_REG6_DEFAULT (0x00) -#define ADV7343_HD_MODE_REG7_DEFAULT (0x00) -#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) -#define ADV7343_SOFT_RESET_DEFAULT (0x02) -#define ADV7343_COMPOSITE_POWER_VALUE (0x80) -#define ADV7343_COMPONENT_POWER_VALUE (0x1C) -#define ADV7343_SVIDEO_POWER_VALUE (0x60) -#define ADV7343_SD_HUE_REG_DEFAULT (127) -#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03) - -#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10) - -#define ADV7343_SD_MODE_REG1_DEFAULT (0x00) -#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9) -#define ADV7343_SD_MODE_REG3_DEFAULT (0x10) -#define ADV7343_SD_MODE_REG4_DEFAULT (0x01) -#define ADV7343_SD_MODE_REG5_DEFAULT (0x02) -#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C) -#define ADV7343_SD_MODE_REG7_DEFAULT (0x04) -#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) - -/* Bit masks for Mode Select Register */ -#define INPUT_MODE_MASK (0x70) -#define SD_INPUT_MODE (0x00) -#define HD_720P_INPUT_MODE (0x10) -#define HD_1080I_INPUT_MODE (0x10) - -/* Bit masks for Mode Register 0 */ -#define TEST_PATTERN_BLACK_BAR_EN (0x04) -#define YUV_OUTPUT_SELECT (0x20) -#define RGB_OUTPUT_SELECT (0xDF) - -/* Bit masks for DAC output levels */ -#define DAC_OUTPUT_LEVEL_MASK (0xFF) - -/* Bit masks for soft reset register */ -#define SOFT_RESET (0x02) - -/* Bit masks for HD Mode Register 1 */ -#define OUTPUT_STD_MASK (0x03) -#define OUTPUT_STD_SHIFT (0) -#define OUTPUT_STD_EIA0_2 (0x00) -#define OUTPUT_STD_EIA0_1 (0x01) -#define OUTPUT_STD_FULL (0x02) -#define EMBEDDED_SYNC (0x04) -#define EXTERNAL_SYNC (0xFB) -#define STD_MODE_SHIFT (3) -#define STD_MODE_MASK (0x1F) -#define STD_MODE_720P (0x05) -#define STD_MODE_720P_25 (0x08) -#define STD_MODE_720P_30 (0x07) -#define STD_MODE_720P_50 (0x06) -#define STD_MODE_1080I (0x0D) -#define STD_MODE_1080I_25fps (0x0E) -#define STD_MODE_1080P_24 (0x12) -#define STD_MODE_1080P_25 (0x10) -#define STD_MODE_1080P_30 (0x0F) -#define STD_MODE_525P (0x00) -#define STD_MODE_625P (0x03) - -/* Bit masks for SD Mode Register 1 */ -#define SD_STD_MASK (0x03) -#define SD_STD_NTSC (0x00) -#define SD_STD_PAL_BDGHI (0x01) -#define SD_STD_PAL_M (0x02) -#define SD_STD_PAL_N (0x03) -#define SD_LUMA_FLTR_MASK (0x7) -#define SD_LUMA_FLTR_SHIFT (0x2) -#define SD_CHROMA_FLTR_MASK (0x7) -#define SD_CHROMA_FLTR_SHIFT (0x5) - -/* Bit masks for SD Mode Register 2 */ -#define SD_PBPR_SSAF_EN (0x01) -#define SD_PBPR_SSAF_DI (0xFE) -#define SD_DAC_1_DI (0xFD) -#define SD_DAC_2_DI (0xFB) -#define SD_PEDESTAL_EN (0x08) -#define SD_PEDESTAL_DI (0xF7) -#define SD_SQUARE_PIXEL_EN (0x10) -#define SD_SQUARE_PIXEL_DI (0xEF) -#define SD_PIXEL_DATA_VALID (0x40) -#define SD_ACTIVE_EDGE_EN (0x80) -#define SD_ACTIVE_EDGE_DI (0x7F) - -/* Bit masks for HD Mode Register 6 */ -#define HD_RGB_INPUT_EN (0x02) -#define HD_RGB_INPUT_DI (0xFD) -#define HD_PBPR_SYNC_EN (0x04) -#define HD_PBPR_SYNC_DI (0xFB) -#define HD_DAC_SWAP_EN (0x08) -#define HD_DAC_SWAP_DI (0xF7) -#define HD_GAMMA_CURVE_A (0xEF) -#define HD_GAMMA_CURVE_B (0x10) -#define HD_GAMMA_EN (0x20) -#define HD_GAMMA_DI (0xDF) -#define HD_ADPT_FLTR_MODEB (0x40) -#define HD_ADPT_FLTR_MODEA (0xBF) -#define HD_ADPT_FLTR_EN (0x80) -#define HD_ADPT_FLTR_DI (0x7F) - -#define ADV7343_BRIGHTNESS_MAX (127) -#define ADV7343_BRIGHTNESS_MIN (0) -#define ADV7343_BRIGHTNESS_DEF (3) -#define ADV7343_HUE_MAX (255) -#define ADV7343_HUE_MIN (0) -#define ADV7343_HUE_DEF (127) -#define ADV7343_GAIN_MAX (64) -#define ADV7343_GAIN_MIN (-64) -#define ADV7343_GAIN_DEF (0) - -#endif diff --git a/drivers/media/video/adv7393.c b/drivers/media/video/adv7393.c deleted file mode 100644 index 3dc6098c7267..000000000000 --- a/drivers/media/video/adv7393.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * adv7393 - ADV7393 Video Encoder Driver - * - * The encoder hardware does not support SECAM. - * - * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ - * Benoît Thébaudeau - * - * Based on ADV7343 driver, - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; 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 -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "adv7393_regs.h" - -MODULE_DESCRIPTION("ADV7393 video encoder driver"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-1"); - -struct adv7393_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - u8 reg00; - u8 reg01; - u8 reg02; - u8 reg35; - u8 reg80; - u8 reg82; - u32 output; - v4l2_std_id std; -}; - -static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct adv7393_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; -} - -static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static const u8 adv7393_init_reg_val[] = { - ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, - ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, - - ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, - ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, - ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, - ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, - ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, - ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, - ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, - - ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, - ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, - ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, - ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, - ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, - ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, - ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, - ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, - - ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, - - ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, - ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, - ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, -}; - -/* - * 2^32 - * FSC(reg) = FSC (HZ) * -------- - * 27000000 - */ -static const struct adv7393_std_info stdinfo[] = { - { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, - }, { - /* FSC(Hz) = 3,579,545.45 Hz */ - SD_STD_NTSC, 569408542, V4L2_STD_NTSC, - }, { - /* FSC(Hz) = 3,575,611.00 Hz */ - SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, - }, { - /* FSC(Hz) = 3,582,056.00 Hz */ - SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, - }, { - /* FSC(Hz) = 4,433,618.75 Hz */ - SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, - }, -}; - -static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7393_state *state = to_state(sd); - const struct adv7393_std_info *std_info; - int num_std; - u8 reg; - u32 val; - int err = 0; - int i; - - num_std = ARRAY_SIZE(stdinfo); - - for (i = 0; i < num_std; i++) { - if (stdinfo[i].stdid & std) - break; - } - - if (i == num_std) { - v4l2_dbg(1, debug, sd, - "Invalid std or std is not supported: %llx\n", - (unsigned long long)std); - return -EINVAL; - } - - std_info = &stdinfo[i]; - - /* Set the standard */ - val = state->reg80 & ~SD_STD_MASK; - val |= std_info->standard_val3; - err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); - if (err < 0) - goto setstd_exit; - - state->reg80 = val; - - /* Configure the input mode register */ - val = state->reg01 & ~INPUT_MODE_MASK; - val |= SD_INPUT_MODE; - err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); - if (err < 0) - goto setstd_exit; - - state->reg01 = val; - - /* Program the sub carrier frequency registers */ - val = std_info->fsc_val; - for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { - err = adv7393_write(sd, reg, val); - if (err < 0) - goto setstd_exit; - val >>= 8; - } - - val = state->reg82; - - /* Pedestal settings */ - if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) - val |= SD_PEDESTAL_EN; - else - val &= SD_PEDESTAL_DI; - - err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); - if (err < 0) - goto setstd_exit; - - state->reg82 = val; - -setstd_exit: - if (err != 0) - v4l2_err(sd, "Error setting std, write failed\n"); - - return err; -} - -static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) -{ - struct adv7393_state *state = to_state(sd); - u8 val; - int err = 0; - - if (output_type > ADV7393_SVIDEO_ID) { - v4l2_dbg(1, debug, sd, - "Invalid output type or output type not supported:%d\n", - output_type); - return -EINVAL; - } - - /* Enable Appropriate DAC */ - val = state->reg00 & 0x03; - - if (output_type == ADV7393_COMPOSITE_ID) - val |= ADV7393_COMPOSITE_POWER_VALUE; - else if (output_type == ADV7393_COMPONENT_ID) - val |= ADV7393_COMPONENT_POWER_VALUE; - else - val |= ADV7393_SVIDEO_POWER_VALUE; - - err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); - if (err < 0) - goto setoutput_exit; - - state->reg00 = val; - - /* Enable YUV output */ - val = state->reg02 | YUV_OUTPUT_SELECT; - err = adv7393_write(sd, ADV7393_MODE_REG0, val); - if (err < 0) - goto setoutput_exit; - - state->reg02 = val; - - /* configure SD DAC Output 1 bit */ - val = state->reg82; - if (output_type == ADV7393_COMPONENT_ID) - val &= SD_DAC_OUT1_DI; - else - val |= SD_DAC_OUT1_EN; - err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); - if (err < 0) - goto setoutput_exit; - - state->reg82 = val; - - /* configure ED/HD Color DAC Swap bit to zero */ - val = state->reg35 & HD_DAC_SWAP_DI; - err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); - if (err < 0) - goto setoutput_exit; - - state->reg35 = val; - -setoutput_exit: - if (err != 0) - v4l2_err(sd, "Error setting output, write failed\n"); - - return err; -} - -static int adv7393_log_status(struct v4l2_subdev *sd) -{ - struct adv7393_state *state = to_state(sd); - - v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); - v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : - ((state->output == 1) ? "Component" : "S-Video")); - return 0; -} - -static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, - ctrl->val & SD_BRIGHTNESS_VALUE_MASK); - - case V4L2_CID_HUE: - return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, - ctrl->val - ADV7393_HUE_MIN); - - case V4L2_CID_GAIN: - return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, - ctrl->val); - } - return -EINVAL; -} - -static int adv7393_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); -} - -static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { - .s_ctrl = adv7393_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops adv7393_core_ops = { - .log_status = adv7393_log_status, - .g_chip_ident = adv7393_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct adv7393_state *state = to_state(sd); - int err = 0; - - if (state->std == std) - return 0; - - err = adv7393_setstd(sd, std); - if (!err) - state->std = std; - - return err; -} - -static int adv7393_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct adv7393_state *state = to_state(sd); - int err = 0; - - if (state->output == output) - return 0; - - err = adv7393_setoutput(sd, output); - if (!err) - state->output = output; - - return err; -} - -static const struct v4l2_subdev_video_ops adv7393_video_ops = { - .s_std_output = adv7393_s_std_output, - .s_routing = adv7393_s_routing, -}; - -static const struct v4l2_subdev_ops adv7393_ops = { - .core = &adv7393_core_ops, - .video = &adv7393_video_ops, -}; - -static int adv7393_initialize(struct v4l2_subdev *sd) -{ - struct adv7393_state *state = to_state(sd); - int err = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { - - err = adv7393_write(sd, adv7393_init_reg_val[i], - adv7393_init_reg_val[i+1]); - if (err) { - v4l2_err(sd, "Error initializing\n"); - return err; - } - } - - /* Configure for default video standard */ - err = adv7393_setoutput(sd, state->output); - if (err < 0) { - v4l2_err(sd, "Error setting output during init\n"); - return -EINVAL; - } - - err = adv7393_setstd(sd, state->std); - if (err < 0) { - v4l2_err(sd, "Error setting std during init\n"); - return -EINVAL; - } - - return err; -} - -static int adv7393_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adv7393_state *state; - int err; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; - state->reg01 = 0x00; - state->reg02 = 0x20; - state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; - state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; - state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; - - state->output = ADV7393_COMPOSITE_ID; - state->std = V4L2_STD_NTSC; - - v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); - - v4l2_ctrl_handler_init(&state->hdl, 3); - v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, - V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, - ADV7393_BRIGHTNESS_MAX, 1, - ADV7393_BRIGHTNESS_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, - V4L2_CID_HUE, ADV7393_HUE_MIN, - ADV7393_HUE_MAX, 1, - ADV7393_HUE_DEF); - v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, - V4L2_CID_GAIN, ADV7393_GAIN_MIN, - ADV7393_GAIN_MAX, 1, - ADV7393_GAIN_DEF); - state->sd.ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_handler_setup(&state->hdl); - - err = adv7393_initialize(&state->sd); - if (err) { - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - } - return err; -} - -static int adv7393_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7393_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - - return 0; -} - -static const struct i2c_device_id adv7393_id[] = { - {"adv7393", 0}, - {}, -}; -MODULE_DEVICE_TABLE(i2c, adv7393_id); - -static struct i2c_driver adv7393_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "adv7393", - }, - .probe = adv7393_probe, - .remove = adv7393_remove, - .id_table = adv7393_id, -}; -module_i2c_driver(adv7393_driver); diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/video/adv7393_regs.h deleted file mode 100644 index 78968330f0be..000000000000 --- a/drivers/media/video/adv7393_regs.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * ADV7393 encoder related structure and register definitions - * - * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ - * Benoît Thébaudeau - * - * Based on ADV7343 driver, - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ADV7393_REGS_H -#define ADV7393_REGS_H - -struct adv7393_std_info { - u32 standard_val3; - u32 fsc_val; - v4l2_std_id stdid; -}; - -/* Register offset macros */ -#define ADV7393_POWER_MODE_REG (0x00) -#define ADV7393_MODE_SELECT_REG (0x01) -#define ADV7393_MODE_REG0 (0x02) - -#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) - -#define ADV7393_SOFT_RESET (0x17) - -#define ADV7393_HD_MODE_REG1 (0x30) -#define ADV7393_HD_MODE_REG2 (0x31) -#define ADV7393_HD_MODE_REG3 (0x32) -#define ADV7393_HD_MODE_REG4 (0x33) -#define ADV7393_HD_MODE_REG5 (0x34) -#define ADV7393_HD_MODE_REG6 (0x35) - -#define ADV7393_HD_MODE_REG7 (0x39) - -#define ADV7393_SD_MODE_REG1 (0x80) -#define ADV7393_SD_MODE_REG2 (0x82) -#define ADV7393_SD_MODE_REG3 (0x83) -#define ADV7393_SD_MODE_REG4 (0x84) -#define ADV7393_SD_MODE_REG5 (0x86) -#define ADV7393_SD_MODE_REG6 (0x87) -#define ADV7393_SD_MODE_REG7 (0x88) -#define ADV7393_SD_MODE_REG8 (0x89) - -#define ADV7393_SD_TIMING_REG0 (0x8A) - -#define ADV7393_FSC_REG0 (0x8C) -#define ADV7393_FSC_REG1 (0x8D) -#define ADV7393_FSC_REG2 (0x8E) -#define ADV7393_FSC_REG3 (0x8F) - -#define ADV7393_SD_CGMS_WSS0 (0x99) - -#define ADV7393_SD_HUE_ADJUST (0xA0) -#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) - -/* Default values for the registers */ -#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) -#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default - 720p EAV/SAV code*/ -#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data - valid */ -#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ -#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ -#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) -#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) -#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) -#define ADV7393_SOFT_RESET_DEFAULT (0x02) -#define ADV7393_COMPOSITE_POWER_VALUE (0x10) -#define ADV7393_COMPONENT_POWER_VALUE (0x1C) -#define ADV7393_SVIDEO_POWER_VALUE (0x0C) -#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) -#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) - -#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) - -#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) -#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) -#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) -#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) -#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) -#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) -#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) -#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) - -#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) - -/* Bit masks for Mode Select Register */ -#define INPUT_MODE_MASK (0x70) -#define SD_INPUT_MODE (0x00) -#define HD_720P_INPUT_MODE (0x10) -#define HD_1080I_INPUT_MODE (0x10) - -/* Bit masks for Mode Register 0 */ -#define TEST_PATTERN_BLACK_BAR_EN (0x04) -#define YUV_OUTPUT_SELECT (0x20) -#define RGB_OUTPUT_SELECT (0xDF) - -/* Bit masks for SD brightness/WSS */ -#define SD_BRIGHTNESS_VALUE_MASK (0x7F) -#define SD_BLANK_WSS_DATA_MASK (0x80) - -/* Bit masks for soft reset register */ -#define SOFT_RESET (0x02) - -/* Bit masks for HD Mode Register 1 */ -#define OUTPUT_STD_MASK (0x03) -#define OUTPUT_STD_SHIFT (0) -#define OUTPUT_STD_EIA0_2 (0x00) -#define OUTPUT_STD_EIA0_1 (0x01) -#define OUTPUT_STD_FULL (0x02) -#define EMBEDDED_SYNC (0x04) -#define EXTERNAL_SYNC (0xFB) -#define STD_MODE_MASK (0x1F) -#define STD_MODE_SHIFT (3) -#define STD_MODE_720P (0x05) -#define STD_MODE_720P_25 (0x08) -#define STD_MODE_720P_30 (0x07) -#define STD_MODE_720P_50 (0x06) -#define STD_MODE_1080I (0x0D) -#define STD_MODE_1080I_25 (0x0E) -#define STD_MODE_1080P_24 (0x11) -#define STD_MODE_1080P_25 (0x10) -#define STD_MODE_1080P_30 (0x0F) -#define STD_MODE_525P (0x00) -#define STD_MODE_625P (0x03) - -/* Bit masks for SD Mode Register 1 */ -#define SD_STD_MASK (0x03) -#define SD_STD_NTSC (0x00) -#define SD_STD_PAL_BDGHI (0x01) -#define SD_STD_PAL_M (0x02) -#define SD_STD_PAL_N (0x03) -#define SD_LUMA_FLTR_MASK (0x07) -#define SD_LUMA_FLTR_SHIFT (2) -#define SD_CHROMA_FLTR_MASK (0x07) -#define SD_CHROMA_FLTR_SHIFT (5) - -/* Bit masks for SD Mode Register 2 */ -#define SD_PRPB_SSAF_EN (0x01) -#define SD_PRPB_SSAF_DI (0xFE) -#define SD_DAC_OUT1_EN (0x02) -#define SD_DAC_OUT1_DI (0xFD) -#define SD_PEDESTAL_EN (0x08) -#define SD_PEDESTAL_DI (0xF7) -#define SD_SQUARE_PIXEL_EN (0x10) -#define SD_SQUARE_PIXEL_DI (0xEF) -#define SD_PIXEL_DATA_VALID (0x40) -#define SD_ACTIVE_EDGE_EN (0x80) -#define SD_ACTIVE_EDGE_DI (0x7F) - -/* Bit masks for HD Mode Register 6 */ -#define HD_PRPB_SYNC_EN (0x04) -#define HD_PRPB_SYNC_DI (0xFB) -#define HD_DAC_SWAP_EN (0x08) -#define HD_DAC_SWAP_DI (0xF7) -#define HD_GAMMA_CURVE_A (0xEF) -#define HD_GAMMA_CURVE_B (0x10) -#define HD_GAMMA_EN (0x20) -#define HD_GAMMA_DI (0xDF) -#define HD_ADPT_FLTR_MODEA (0xBF) -#define HD_ADPT_FLTR_MODEB (0x40) -#define HD_ADPT_FLTR_EN (0x80) -#define HD_ADPT_FLTR_DI (0x7F) - -#define ADV7393_BRIGHTNESS_MAX (63) -#define ADV7393_BRIGHTNESS_MIN (-64) -#define ADV7393_BRIGHTNESS_DEF (0) -#define ADV7393_HUE_MAX (127) -#define ADV7393_HUE_MIN (-128) -#define ADV7393_HUE_DEF (0) -#define ADV7393_GAIN_MAX (64) -#define ADV7393_GAIN_MIN (-64) -#define ADV7393_GAIN_DEF (0) - -#endif diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c deleted file mode 100644 index ba674656b10d..000000000000 --- a/drivers/media/video/ak881x.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM) - * - * Copyright (C) 2010, Guennadi Liakhovetski - * - * 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. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define AK881X_INTERFACE_MODE 0 -#define AK881X_VIDEO_PROCESS1 1 -#define AK881X_VIDEO_PROCESS2 2 -#define AK881X_VIDEO_PROCESS3 3 -#define AK881X_DAC_MODE 5 -#define AK881X_STATUS 0x24 -#define AK881X_DEVICE_ID 0x25 -#define AK881X_DEVICE_REVISION 0x26 - -struct ak881x { - struct v4l2_subdev subdev; - struct ak881x_pdata *pdata; - unsigned int lines; - int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */ - char revision; /* DEVICE_REVISION content */ -}; - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u8 data) -{ - return i2c_smbus_write_byte_data(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u8 data, u8 mask) -{ - int ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, (ret & ~mask) | (data & mask)); -} - -static struct ak881x *to_ak881x(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct ak881x, subdev); -} - -static int ak881x_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *id) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) - return -EINVAL; - - if (id->match.addr != client->addr) - return -ENODEV; - - id->ident = ak881x->id; - id->revision = ak881x->revision; - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ak881x_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) - return -EINVAL; - - if (reg->match.addr != client->addr) - return -ENODEV; - - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int ak881x_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) - return -EINVAL; - - if (reg->match.addr != client->addr) - return -ENODEV; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - v4l_bound_align_image(&mf->width, 0, 720, 2, - &mf->height, 0, ak881x->lines, 1, 0); - mf->field = V4L2_FIELD_INTERLACED; - mf->code = V4L2_MBUS_FMT_YUYV8_2X8; - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - if (mf->field != V4L2_FIELD_INTERLACED || - mf->code != V4L2_MBUS_FMT_YUYV8_2X8) - return -EINVAL; - - return ak881x_try_g_mbus_fmt(sd, mf); -} - -static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (index) - return -EINVAL; - - *code = V4L2_MBUS_FMT_YUYV8_2X8; - return 0; -} - -static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = 720; - a->bounds.height = ak881x->lines; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - -static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - u8 vp1; - - if (std == V4L2_STD_NTSC_443) { - vp1 = 3; - ak881x->lines = 480; - } else if (std == V4L2_STD_PAL_M) { - vp1 = 5; - ak881x->lines = 480; - } else if (std == V4L2_STD_PAL_60) { - vp1 = 7; - ak881x->lines = 480; - } else if (std && !(std & ~V4L2_STD_PAL)) { - vp1 = 0xf; - ak881x->lines = 576; - } else if (std && !(std & ~V4L2_STD_NTSC)) { - vp1 = 0; - ak881x->lines = 480; - } else { - /* No SECAM or PAL_N/Nc supported */ - return -EINVAL; - } - - reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf); - - return 0; -} - -static int ak881x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ak881x *ak881x = to_ak881x(client); - - if (enable) { - u8 dac; - /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */ - /* Default: composite output */ - if (ak881x->pdata->flags & AK881X_COMPONENT) - dac = 3; - else - dac = 4; - /* Turn on the DAC(s) */ - reg_write(client, AK881X_DAC_MODE, dac); - dev_dbg(&client->dev, "chip status 0x%x\n", - reg_read(client, AK881X_STATUS)); - } else { - /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */ - reg_write(client, AK881X_DAC_MODE, 0); - dev_dbg(&client->dev, "chip status 0x%x\n", - reg_read(client, AK881X_STATUS)); - } - - return 0; -} - -static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { - .g_chip_ident = ak881x_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ak881x_g_register, - .s_register = ak881x_s_register, -#endif -}; - -static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { - .s_mbus_fmt = ak881x_s_mbus_fmt, - .g_mbus_fmt = ak881x_try_g_mbus_fmt, - .try_mbus_fmt = ak881x_try_g_mbus_fmt, - .cropcap = ak881x_cropcap, - .enum_mbus_fmt = ak881x_enum_mbus_fmt, - .s_std_output = ak881x_s_std_output, - .s_stream = ak881x_s_stream, -}; - -static struct v4l2_subdev_ops ak881x_subdev_ops = { - .core = &ak881x_subdev_core_ops, - .video = &ak881x_subdev_video_ops, -}; - -static int ak881x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct ak881x *ak881x; - u8 ifmode, data; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL); - if (!ak881x) - return -ENOMEM; - - v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops); - - data = reg_read(client, AK881X_DEVICE_ID); - - switch (data) { - case 0x13: - ak881x->id = V4L2_IDENT_AK8813; - break; - case 0x14: - ak881x->id = V4L2_IDENT_AK8814; - break; - default: - dev_err(&client->dev, - "No ak881x chip detected, register read %x\n", data); - kfree(ak881x); - return -ENODEV; - } - - ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION); - ak881x->pdata = client->dev.platform_data; - - if (ak881x->pdata) { - if (ak881x->pdata->flags & AK881X_FIELD) - ifmode = 4; - else - ifmode = 0; - - switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) { - case AK881X_IF_MODE_BT656: - ifmode |= 1; - break; - case AK881X_IF_MODE_MASTER: - ifmode |= 2; - break; - case AK881X_IF_MODE_SLAVE: - default: - break; - } - - dev_dbg(&client->dev, "IF mode %x\n", ifmode); - - /* - * "Line Blanking No." seems to be the same as the number of - * "black" lines on, e.g., SuperH VOU, whose default value of 20 - * "incidentally" matches ak881x' default - */ - reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3)); - } - - /* Hardware default: NTSC-M */ - ak881x->lines = 480; - - dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n", - data, ak881x->revision); - - return 0; -} - -static int ak881x_remove(struct i2c_client *client) -{ - struct ak881x *ak881x = to_ak881x(client); - - v4l2_device_unregister_subdev(&ak881x->subdev); - kfree(ak881x); - - return 0; -} - -static const struct i2c_device_id ak881x_id[] = { - { "ak8813", 0 }, - { "ak8814", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ak881x_id); - -static struct i2c_driver ak881x_i2c_driver = { - .driver = { - .name = "ak881x", - }, - .probe = ak881x_probe, - .remove = ak881x_remove, - .id_table = ak881x_id, -}; - -module_i2c_driver(ak881x_i2c_driver); - -MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c deleted file mode 100644 index 8153a449846b..000000000000 --- a/drivers/media/video/aptina-pll.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Aptina Sensor PLL Configuration - * - * Copyright (C) 2012 Laurent Pinchart - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include -#include -#include - -#include "aptina-pll.h" - -int aptina_pll_calculate(struct device *dev, - const struct aptina_pll_limits *limits, - struct aptina_pll *pll) -{ - unsigned int mf_min; - unsigned int mf_max; - unsigned int p1_min; - unsigned int p1_max; - unsigned int p1; - unsigned int div; - - dev_dbg(dev, "PLL: ext clock %u pix clock %u\n", - pll->ext_clock, pll->pix_clock); - - if (pll->ext_clock < limits->ext_clock_min || - pll->ext_clock > limits->ext_clock_max) { - dev_err(dev, "pll: invalid external clock frequency.\n"); - return -EINVAL; - } - - if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) { - dev_err(dev, "pll: invalid pixel clock frequency.\n"); - return -EINVAL; - } - - /* Compute the multiplier M and combined N*P1 divisor. */ - div = gcd(pll->pix_clock, pll->ext_clock); - pll->m = pll->pix_clock / div; - div = pll->ext_clock / div; - - /* We now have the smallest M and N*P1 values that will result in the - * desired pixel clock frequency, but they might be out of the valid - * range. Compute the factor by which we should multiply them given the - * following constraints: - * - * - minimum/maximum multiplier - * - minimum/maximum multiplier output clock frequency assuming the - * minimum/maximum N value - * - minimum/maximum combined N*P1 divisor - */ - mf_min = DIV_ROUND_UP(limits->m_min, pll->m); - mf_min = max(mf_min, limits->out_clock_min / - (pll->ext_clock / limits->n_min * pll->m)); - mf_min = max(mf_min, limits->n_min * limits->p1_min / div); - mf_max = limits->m_max / pll->m; - mf_max = min(mf_max, limits->out_clock_max / - (pll->ext_clock / limits->n_max * pll->m)); - mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div)); - - dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max); - if (mf_min > mf_max) { - dev_err(dev, "pll: no valid combined N*P1 divisor.\n"); - return -EINVAL; - } - - /* - * We're looking for the highest acceptable P1 value for which a - * multiplier factor MF exists that fulfills the following conditions: - * - * 1. p1 is in the [p1_min, p1_max] range given by the limits and is - * even - * 2. mf is in the [mf_min, mf_max] range computed above - * 3. div * mf is a multiple of p1, in order to compute - * n = div * mf / p1 - * m = pll->m * mf - * 4. the internal clock frequency, given by ext_clock / n, is in the - * [int_clock_min, int_clock_max] range given by the limits - * 5. the output clock frequency, given by ext_clock / n * m, is in the - * [out_clock_min, out_clock_max] range given by the limits - * - * The first naive approach is to iterate over all p1 values acceptable - * according to (1) and all mf values acceptable according to (2), and - * stop at the first combination that fulfills (3), (4) and (5). This - * has a O(n^2) complexity. - * - * Instead of iterating over all mf values in the [mf_min, mf_max] range - * we can compute the mf increment between two acceptable values - * according to (3) with - * - * mf_inc = p1 / gcd(div, p1) (6) - * - * and round the minimum up to the nearest multiple of mf_inc. This will - * restrict the number of mf values to be checked. - * - * Furthermore, conditions (4) and (5) only restrict the range of - * acceptable p1 and mf values by modifying the minimum and maximum - * limits. (5) can be expressed as - * - * ext_clock / (div * mf / p1) * m * mf >= out_clock_min - * ext_clock / (div * mf / p1) * m * mf <= out_clock_max - * - * or - * - * p1 >= out_clock_min * div / (ext_clock * m) (7) - * p1 <= out_clock_max * div / (ext_clock * m) - * - * Similarly, (4) can be expressed as - * - * mf >= ext_clock * p1 / (int_clock_max * div) (8) - * mf <= ext_clock * p1 / (int_clock_min * div) - * - * We can thus iterate over the restricted p1 range defined by the - * combination of (1) and (7), and then compute the restricted mf range - * defined by the combination of (2), (6) and (8). If the resulting mf - * range is not empty, any value in the mf range is acceptable. We thus - * select the mf lwoer bound and the corresponding p1 value. - */ - if (limits->p1_min == 0) { - dev_err(dev, "pll: P1 minimum value must be >0.\n"); - return -EINVAL; - } - - p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div, - pll->ext_clock * pll->m)); - p1_max = min(limits->p1_max, limits->out_clock_max * div / - (pll->ext_clock * pll->m)); - - for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { - unsigned int mf_inc = p1 / gcd(div, p1); - unsigned int mf_high; - unsigned int mf_low; - - mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, - limits->int_clock_max * div)), mf_inc); - mf_high = min(mf_max, pll->ext_clock * p1 / - (limits->int_clock_min * div)); - - if (mf_low > mf_high) - continue; - - pll->n = div * mf_low / p1; - pll->m *= mf_low; - pll->p1 = p1; - dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1); - return 0; - } - - dev_err(dev, "pll: no valid N and P1 divisors found.\n"); - return -EINVAL; -} -EXPORT_SYMBOL_GPL(aptina_pll_calculate); - -MODULE_DESCRIPTION("Aptina PLL Helpers"); -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/video/aptina-pll.h deleted file mode 100644 index b370e341e75d..000000000000 --- a/drivers/media/video/aptina-pll.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Aptina Sensor PLL Configuration - * - * Copyright (C) 2012 Laurent Pinchart - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __APTINA_PLL_H -#define __APTINA_PLL_H - -struct aptina_pll { - unsigned int ext_clock; - unsigned int pix_clock; - - unsigned int n; - unsigned int m; - unsigned int p1; -}; - -struct aptina_pll_limits { - unsigned int ext_clock_min; - unsigned int ext_clock_max; - unsigned int int_clock_min; - unsigned int int_clock_max; - unsigned int out_clock_min; - unsigned int out_clock_max; - unsigned int pix_clock_max; - - unsigned int n_min; - unsigned int n_max; - unsigned int m_min; - unsigned int m_max; - unsigned int p1_min; - unsigned int p1_max; -}; - -struct device; - -int aptina_pll_calculate(struct device *dev, - const struct aptina_pll_limits *limits, - struct aptina_pll *pll); - -#endif /* __APTINA_PLL_H */ diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c deleted file mode 100644 index c4b03572dce8..000000000000 --- a/drivers/media/video/as3645a.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * drivers/media/video/as3645a.c - AS3645A and LM3555 flash controllers driver - * - * Copyright (C) 2008-2011 Nokia Corporation - * Copyright (c) 2011, Intel Corporation. - * - * Contact: Laurent Pinchart - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * TODO: - * - Check hardware FSTROBE control when sensor driver add support for this - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50) -#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100) - -/* Register definitions */ - -/* Read-only Design info register: Reset state: xxxx 0001 */ -#define AS_DESIGN_INFO_REG 0x00 -#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4)) -#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f) - -/* Read-only Version control register: Reset state: 0000 0000 - * for first engineering samples - */ -#define AS_VERSION_CONTROL_REG 0x01 -#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4)) -#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f) - -/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */ -#define AS_INDICATOR_AND_TIMER_REG 0x02 -#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0 -#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4 -#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6 - -/* Read / Write (Current set register): Reset state: 0110 1001 */ -#define AS_CURRENT_SET_REG 0x03 -#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0 -#define AS_CURRENT_LED_DET_ON (1 << 3) -#define AS_CURRENT_FLASH_CURRENT_SHIFT 4 - -/* Read / Write (Control register): Reset state: 1011 0100 */ -#define AS_CONTROL_REG 0x04 -#define AS_CONTROL_MODE_SETTING_SHIFT 0 -#define AS_CONTROL_STROBE_ON (1 << 2) -#define AS_CONTROL_OUT_ON (1 << 3) -#define AS_CONTROL_EXT_TORCH_ON (1 << 4) -#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5) -#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5) -#define AS_CONTROL_COIL_PEAK_SHIFT 6 - -/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */ -#define AS_FAULT_INFO_REG 0x05 -#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1) -#define AS_FAULT_INFO_INDICATOR_LED (1 << 2) -#define AS_FAULT_INFO_LED_AMOUNT (1 << 3) -#define AS_FAULT_INFO_TIMEOUT (1 << 4) -#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5) -#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6) -#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7) - -/* Boost register */ -#define AS_BOOST_REG 0x0d -#define AS_BOOST_CURRENT_DISABLE (0 << 0) -#define AS_BOOST_CURRENT_ENABLE (1 << 0) - -/* Password register is used to unlock boost register writing */ -#define AS_PASSWORD_REG 0x0f -#define AS_PASSWORD_UNLOCK_VALUE 0x55 - -enum as_mode { - AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT, -}; - -/* - * struct as3645a - * - * @subdev: V4L2 subdev - * @pdata: Flash platform data - * @power_lock: Protects power_count - * @power_count: Power reference count - * @led_mode: V4L2 flash LED mode - * @timeout: Flash timeout in microseconds - * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum - * values are 400mA for two LEDs and 500mA for one LED. - * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA) - * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA) - * @strobe_source: Flash strobe source (software or external) - */ -struct as3645a { - struct v4l2_subdev subdev; - const struct as3645a_platform_data *pdata; - - struct mutex power_lock; - int power_count; - - /* Controls */ - struct v4l2_ctrl_handler ctrls; - - enum v4l2_flash_led_mode led_mode; - unsigned int timeout; - u8 flash_current; - u8 assist_current; - u8 indicator_current; - enum v4l2_flash_strobe_source strobe_source; -}; - -#define to_as3645a(sd) container_of(sd, struct as3645a, subdev) - -/* Return negative errno else zero on success */ -static int as3645a_write(struct as3645a *flash, u8 addr, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - rval = i2c_smbus_write_byte_data(client, addr, val); - - dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val, - rval < 0 ? "fail" : "ok"); - - return rval; -} - -/* Return negative errno else a data byte received from the device. */ -static int as3645a_read(struct as3645a *flash, u8 addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - rval = i2c_smbus_read_byte_data(client, addr); - - dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval, - rval < 0 ? "fail" : "ok"); - - return rval; -} - -/* ----------------------------------------------------------------------------- - * Hardware configuration and trigger - */ - -/* - * as3645a_set_config - Set flash configuration registers - * @flash: The flash - * - * Configure the hardware with flash, assist and indicator currents, as well as - * flash timeout. - * - * Return 0 on success, or a negative error code if an I2C communication error - * occurred. - */ -static int as3645a_set_config(struct as3645a *flash) -{ - int ret; - u8 val; - - val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT) - | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT) - | AS_CURRENT_LED_DET_ON; - - ret = as3645a_write(flash, AS_CURRENT_SET_REG, val); - if (ret < 0) - return ret; - - val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000) - << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT; - - val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT) - | ((flash->indicator_current ? flash->indicator_current - 1 : 0) - << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT); - - return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val); -} - -/* - * as3645a_set_control - Set flash control register - * @flash: The flash - * @mode: Desired output mode - * @on: Desired output state - * - * Configure the hardware with output mode and state. - * - * Return 0 on success, or a negative error code if an I2C communication error - * occurred. - */ -static int -as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on) -{ - u8 reg; - - /* Configure output parameters and operation mode. */ - reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT) - | (on ? AS_CONTROL_OUT_ON : 0) - | mode; - - if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH && - flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) { - reg |= AS_CONTROL_STROBE_TYPE_LEVEL - | AS_CONTROL_STROBE_ON; - } - - return as3645a_write(flash, AS_CONTROL_REG, reg); -} - -/* - * as3645a_set_output - Configure output and operation mode - * @flash: Flash controller - * @strobe: Strobe the flash (only valid in flash mode) - * - * Turn the LEDs output on/off and set the operation mode based on the current - * parameters. - * - * The AS3645A can't control the indicator LED independently of the flash/torch - * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the - * chip to indicator mode. Otherwise set it to assist light (torch) or flash - * mode. - * - * In indicator and assist modes, turn the output on/off based on the indicator - * and torch currents. In software strobe flash mode, turn the output on/off - * based on the strobe parameter. - */ -static int as3645a_set_output(struct as3645a *flash, bool strobe) -{ - enum as_mode mode; - bool on; - - switch (flash->led_mode) { - case V4L2_FLASH_LED_MODE_NONE: - on = flash->indicator_current != 0; - mode = AS_MODE_INDICATOR; - break; - case V4L2_FLASH_LED_MODE_TORCH: - on = true; - mode = AS_MODE_ASSIST; - break; - case V4L2_FLASH_LED_MODE_FLASH: - on = strobe; - mode = AS_MODE_FLASH; - break; - default: - BUG(); - } - - /* Configure output parameters and operation mode. */ - return as3645a_set_control(flash, mode, on); -} - -/* ----------------------------------------------------------------------------- - * V4L2 controls - */ - -static int as3645a_is_active(struct as3645a *flash) -{ - int ret; - - ret = as3645a_read(flash, AS_CONTROL_REG); - return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON); -} - -static int as3645a_read_fault(struct as3645a *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int rval; - - /* NOTE: reading register clear fault status */ - rval = as3645a_read(flash, AS_FAULT_INFO_REG); - if (rval < 0) - return rval; - - if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) - dev_dbg(&client->dev, "Inductor Peak limit fault\n"); - - if (rval & AS_FAULT_INFO_INDICATOR_LED) - dev_dbg(&client->dev, "Indicator LED fault: " - "Short circuit or open loop\n"); - - dev_dbg(&client->dev, "%u connected LEDs\n", - rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1); - - if (rval & AS_FAULT_INFO_TIMEOUT) - dev_dbg(&client->dev, "Timeout fault\n"); - - if (rval & AS_FAULT_INFO_OVER_TEMPERATURE) - dev_dbg(&client->dev, "Over temperature fault\n"); - - if (rval & AS_FAULT_INFO_SHORT_CIRCUIT) - dev_dbg(&client->dev, "Short circuit fault\n"); - - if (rval & AS_FAULT_INFO_OVER_VOLTAGE) - dev_dbg(&client->dev, "Over voltage fault: " - "Indicates missing capacitor or open connection\n"); - - return rval; -} - -static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl) -{ - struct as3645a *flash = - container_of(ctrl->handler, struct as3645a, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int value; - - switch (ctrl->id) { - case V4L2_CID_FLASH_FAULT: - value = as3645a_read_fault(flash); - if (value < 0) - return value; - - ctrl->cur.val = 0; - if (value & AS_FAULT_INFO_SHORT_CIRCUIT) - ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; - if (value & AS_FAULT_INFO_OVER_TEMPERATURE) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; - if (value & AS_FAULT_INFO_TIMEOUT) - ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; - if (value & AS_FAULT_INFO_OVER_VOLTAGE) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; - if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) - ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT; - if (value & AS_FAULT_INFO_INDICATOR_LED) - ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR; - break; - - case V4L2_CID_FLASH_STROBE_STATUS: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { - ctrl->cur.val = 0; - break; - } - - value = as3645a_is_active(flash); - if (value < 0) - return value; - - ctrl->cur.val = value; - break; - } - - dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val); - - return 0; -} - -static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct as3645a *flash = - container_of(ctrl->handler, struct as3645a, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int ret; - - dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val); - - /* If a control that doesn't apply to the current mode is modified, - * we store the value and return immediately. The setting will be - * applied when the LED mode is changed. Otherwise we apply the setting - * immediately. - */ - - switch (ctrl->id) { - case V4L2_CID_FLASH_LED_MODE: - if (flash->indicator_current) - return -EBUSY; - - ret = as3645a_set_config(flash); - if (ret < 0) - return ret; - - flash->led_mode = ctrl->val; - return as3645a_set_output(flash, false); - - case V4L2_CID_FLASH_STROBE_SOURCE: - flash->strobe_source = ctrl->val; - - /* Applies to flash mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - break; - - return as3645a_set_output(flash, false); - - case V4L2_CID_FLASH_STROBE: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; - - return as3645a_set_output(flash, true); - - case V4L2_CID_FLASH_STROBE_STOP: - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - return -EBUSY; - - return as3645a_set_output(flash, false); - - case V4L2_CID_FLASH_TIMEOUT: - flash->timeout = ctrl->val; - - /* Applies to flash mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - break; - - return as3645a_set_config(flash); - - case V4L2_CID_FLASH_INTENSITY: - flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN) - / AS3645A_FLASH_INTENSITY_STEP; - - /* Applies to flash mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) - break; - - return as3645a_set_config(flash); - - case V4L2_CID_FLASH_TORCH_INTENSITY: - flash->assist_current = - (ctrl->val - AS3645A_TORCH_INTENSITY_MIN) - / AS3645A_TORCH_INTENSITY_STEP; - - /* Applies to torch mode only. */ - if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH) - break; - - return as3645a_set_config(flash); - - case V4L2_CID_FLASH_INDICATOR_INTENSITY: - if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE) - return -EBUSY; - - flash->indicator_current = - (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN) - / AS3645A_INDICATOR_INTENSITY_STEP; - - ret = as3645a_set_config(flash); - if (ret < 0) - return ret; - - if ((ctrl->val == 0) == (ctrl->cur.val == 0)) - break; - - return as3645a_set_output(flash, false); - } - - return 0; -} - -static const struct v4l2_ctrl_ops as3645a_ctrl_ops = { - .g_volatile_ctrl = as3645a_get_ctrl, - .s_ctrl = as3645a_set_ctrl, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -/* Put device into know state. */ -static int as3645a_setup(struct as3645a *flash) -{ - struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); - int ret; - - /* clear errors */ - ret = as3645a_read(flash, AS_FAULT_INFO_REG); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Fault info: %02x\n", ret); - - ret = as3645a_set_config(flash); - if (ret < 0) - return ret; - - ret = as3645a_set_output(flash, false); - if (ret < 0) - return ret; - - /* read status */ - ret = as3645a_read_fault(flash); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n", - as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG)); - dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n", - as3645a_read(flash, AS_CURRENT_SET_REG)); - dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n", - as3645a_read(flash, AS_CONTROL_REG)); - - return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0; -} - -static int __as3645a_set_power(struct as3645a *flash, int on) -{ - int ret; - - if (!on) - as3645a_set_control(flash, AS_MODE_EXT_TORCH, false); - - if (flash->pdata->set_power) { - ret = flash->pdata->set_power(&flash->subdev, on); - if (ret < 0) - return ret; - } - - if (!on) - return 0; - - ret = as3645a_setup(flash); - if (ret < 0) { - if (flash->pdata->set_power) - flash->pdata->set_power(&flash->subdev, 0); - } - - return ret; -} - -static int as3645a_set_power(struct v4l2_subdev *sd, int on) -{ - struct as3645a *flash = to_as3645a(sd); - int ret = 0; - - mutex_lock(&flash->power_lock); - - if (flash->power_count == !on) { - ret = __as3645a_set_power(flash, !!on); - if (ret < 0) - goto done; - } - - flash->power_count += on ? 1 : -1; - WARN_ON(flash->power_count < 0); - -done: - mutex_unlock(&flash->power_lock); - return ret; -} - -static int as3645a_registered(struct v4l2_subdev *sd) -{ - struct as3645a *flash = to_as3645a(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int rval, man, model, rfu, version; - const char *vendor; - - /* Power up the flash driver and read manufacturer ID, model ID, RFU - * and version. - */ - rval = as3645a_set_power(&flash->subdev, 1); - if (rval < 0) - return rval; - - rval = as3645a_read(flash, AS_DESIGN_INFO_REG); - if (rval < 0) - goto power_off; - - man = AS_DESIGN_INFO_FACTORY(rval); - model = AS_DESIGN_INFO_MODEL(rval); - - rval = as3645a_read(flash, AS_VERSION_CONTROL_REG); - if (rval < 0) - goto power_off; - - rfu = AS_VERSION_CONTROL_RFU(rval); - version = AS_VERSION_CONTROL_VERSION(rval); - - /* Verify the chip model and version. */ - if (model != 0x01 || rfu != 0x00) { - dev_err(&client->dev, "AS3645A not detected " - "(model %d rfu %d)\n", model, rfu); - rval = -ENODEV; - goto power_off; - } - - switch (man) { - case 1: - vendor = "AMS, Austria Micro Systems"; - break; - case 2: - vendor = "ADI, Analog Devices Inc."; - break; - case 3: - vendor = "NSC, National Semiconductor"; - break; - case 4: - vendor = "NXP"; - break; - case 5: - vendor = "TI, Texas Instrument"; - break; - default: - vendor = "Unknown"; - } - - dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor, - man, version); - - rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE); - if (rval < 0) - goto power_off; - - rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); - if (rval < 0) - goto power_off; - - /* Setup default values. This makes sure that the chip is in a known - * state, in case the power rail can't be controlled. - */ - rval = as3645a_setup(flash); - -power_off: - as3645a_set_power(&flash->subdev, 0); - - return rval; -} - -static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return as3645a_set_power(sd, 1); -} - -static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return as3645a_set_power(sd, 0); -} - -static const struct v4l2_subdev_core_ops as3645a_core_ops = { - .s_power = as3645a_set_power, -}; - -static const struct v4l2_subdev_ops as3645a_ops = { - .core = &as3645a_core_ops, -}; - -static const struct v4l2_subdev_internal_ops as3645a_internal_ops = { - .registered = as3645a_registered, - .open = as3645a_open, - .close = as3645a_close, -}; - -/* ----------------------------------------------------------------------------- - * I2C driver - */ -#ifdef CONFIG_PM - -static int as3645a_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct as3645a *flash = to_as3645a(subdev); - int rval; - - if (flash->power_count == 0) - return 0; - - rval = __as3645a_set_power(flash, 0); - - dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok"); - - return rval; -} - -static int as3645a_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct as3645a *flash = to_as3645a(subdev); - int rval; - - if (flash->power_count == 0) - return 0; - - rval = __as3645a_set_power(flash, 1); - - dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok"); - - return rval; -} - -#else - -#define as3645a_suspend NULL -#define as3645a_resume NULL - -#endif /* CONFIG_PM */ - -/* - * as3645a_init_controls - Create controls - * @flash: The flash - * - * The number of LEDs reported in platform data is used to compute default - * limits. Parameters passed through platform data can override those limits. - */ -static int __devinit as3645a_init_controls(struct as3645a *flash) -{ - const struct as3645a_platform_data *pdata = flash->pdata; - struct v4l2_ctrl *ctrl; - int maximum; - - v4l2_ctrl_handler_init(&flash->ctrls, 10); - - /* V4L2_CID_FLASH_LED_MODE */ - v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_LED_MODE, 2, ~7, - V4L2_FLASH_LED_MODE_NONE); - - /* V4L2_CID_FLASH_STROBE_SOURCE */ - v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE_SOURCE, - pdata->ext_strobe ? 1 : 0, - pdata->ext_strobe ? ~3 : ~1, - V4L2_FLASH_STROBE_SOURCE_SOFTWARE); - - flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; - - /* V4L2_CID_FLASH_STROBE */ - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); - - /* V4L2_CID_FLASH_STROBE_STOP */ - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); - - /* V4L2_CID_FLASH_STROBE_STATUS */ - ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1); - if (ctrl != NULL) - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - - /* V4L2_CID_FLASH_TIMEOUT */ - maximum = pdata->timeout_max; - - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN, - maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum); - - flash->timeout = maximum; - - /* V4L2_CID_FLASH_INTENSITY */ - maximum = pdata->flash_max_current; - - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN, - maximum, AS3645A_FLASH_INTENSITY_STEP, maximum); - - flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN) - / AS3645A_FLASH_INTENSITY_STEP; - - /* V4L2_CID_FLASH_TORCH_INTENSITY */ - maximum = pdata->torch_max_current; - - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_TORCH_INTENSITY, - AS3645A_TORCH_INTENSITY_MIN, maximum, - AS3645A_TORCH_INTENSITY_STEP, - AS3645A_TORCH_INTENSITY_MIN); - - flash->assist_current = 0; - - /* V4L2_CID_FLASH_INDICATOR_INTENSITY */ - v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_INDICATOR_INTENSITY, - AS3645A_INDICATOR_INTENSITY_MIN, - AS3645A_INDICATOR_INTENSITY_MAX, - AS3645A_INDICATOR_INTENSITY_STEP, - AS3645A_INDICATOR_INTENSITY_MIN); - - flash->indicator_current = 0; - - /* V4L2_CID_FLASH_FAULT */ - ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, - V4L2_CID_FLASH_FAULT, 0, - V4L2_FLASH_FAULT_OVER_VOLTAGE | - V4L2_FLASH_FAULT_TIMEOUT | - V4L2_FLASH_FAULT_OVER_TEMPERATURE | - V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); - if (ctrl != NULL) - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - - flash->subdev.ctrl_handler = &flash->ctrls; - - return flash->ctrls.error; -} - -static int __devinit as3645a_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct as3645a *flash; - int ret; - - if (client->dev.platform_data == NULL) - return -ENODEV; - - flash = kzalloc(sizeof(*flash), GFP_KERNEL); - if (flash == NULL) - return -ENOMEM; - - flash->pdata = client->dev.platform_data; - - v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops); - flash->subdev.internal_ops = &as3645a_internal_ops; - flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - ret = as3645a_init_controls(flash); - if (ret < 0) - goto done; - - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); - if (ret < 0) - goto done; - - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; - - mutex_init(&flash->power_lock); - - flash->led_mode = V4L2_FLASH_LED_MODE_NONE; - -done: - if (ret < 0) { - v4l2_ctrl_handler_free(&flash->ctrls); - kfree(flash); - } - - return ret; -} - -static int __devexit as3645a_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct as3645a *flash = to_as3645a(subdev); - - v4l2_device_unregister_subdev(subdev); - v4l2_ctrl_handler_free(&flash->ctrls); - media_entity_cleanup(&flash->subdev.entity); - mutex_destroy(&flash->power_lock); - kfree(flash); - - return 0; -} - -static const struct i2c_device_id as3645a_id_table[] = { - { AS3645A_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, as3645a_id_table); - -static const struct dev_pm_ops as3645a_pm_ops = { - .suspend = as3645a_suspend, - .resume = as3645a_resume, -}; - -static struct i2c_driver as3645a_i2c_driver = { - .driver = { - .name = AS3645A_NAME, - .pm = &as3645a_pm_ops, - }, - .probe = as3645a_probe, - .remove = __devexit_p(as3645a_remove), - .id_table = as3645a_id_table, -}; - -module_i2c_driver(as3645a_i2c_driver); - -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c deleted file mode 100644 index 377bf05b1efd..000000000000 --- a/drivers/media/video/bt819.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * bt819 - BT819A VideoStream Decoder (Rockwell Part) - * - * Copyright (C) 1999 Mike Bernson - * Copyright (C) 1998 Dave Perks - * - * Modifications for LML33/DC10plus unified driver - * Copyright (C) 2000 Serguei Miridonov - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (9/9/2002) - * - * This code was modify/ported from the saa7111 driver written - * by Dave Perks. - * - * This program is free software; 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 -#include -#include - -MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); -MODULE_AUTHOR("Mike Bernson & Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -struct bt819 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - unsigned char reg[32]; - - v4l2_std_id norm; - int ident; - int input; - int enable; -}; - -static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) -{ - return container_of(sd, struct bt819, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct bt819, hdl)->sd; -} - -struct timing { - int hactive; - int hdelay; - int vactive; - int vdelay; - int hscale; - int vscale; -}; - -/* for values, see the bt819 datasheet */ -static struct timing timing_data[] = { - {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000}, - {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000}, -}; - -/* ----------------------------------------------------------------------- */ - -static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value) -{ - return bt819_write(decoder, reg, - (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0)); -} - -static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); - int ret = -1; - u8 reg; - - /* the bt819 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = - decoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = bt819_write(decoder, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -static inline int bt819_read(struct bt819 *decoder, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int bt819_init(struct v4l2_subdev *sd) -{ - static unsigned char init[] = { - /*0x1f, 0x00,*/ /* Reset */ - 0x01, 0x59, /* 0x01 input format */ - 0x02, 0x00, /* 0x02 temporal decimation */ - 0x03, 0x12, /* 0x03 Cropping msb */ - 0x04, 0x16, /* 0x04 Vertical Delay, lsb */ - 0x05, 0xe0, /* 0x05 Vertical Active lsb */ - 0x06, 0x80, /* 0x06 Horizontal Delay lsb */ - 0x07, 0xd0, /* 0x07 Horizontal Active lsb */ - 0x08, 0x00, /* 0x08 Horizontal Scaling msb */ - 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */ - 0x0a, 0x00, /* 0x0a Brightness control */ - 0x0b, 0x30, /* 0x0b Miscellaneous control */ - 0x0c, 0xd8, /* 0x0c Luma Gain lsb */ - 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */ - 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */ - 0x0f, 0x00, /* 0x0f Hue control */ - 0x12, 0x04, /* 0x12 Output Format */ - 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00 - chroma comb OFF, line drop scaling, interlace scaling - BUG? Why does turning the chroma comb on fuck up color? - Bug in the bt819 stepping on my board? - */ - 0x14, 0x00, /* 0x14 Vertial Scaling lsb */ - 0x16, 0x07, /* 0x16 Video Timing Polarity - ACTIVE=active low - FIELD: high=odd, - vreset=active high, - hreset=active high */ - 0x18, 0x68, /* 0x18 AGC Delay */ - 0x19, 0x5d, /* 0x19 Burst Gate Delay */ - 0x1a, 0x80, /* 0x1a ADC Interface */ - }; - - struct bt819 *decoder = to_bt819(sd); - struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0]; - - init[0x03 * 2 - 1] = - (((timing->vdelay >> 8) & 0x03) << 6) | - (((timing->vactive >> 8) & 0x03) << 4) | - (((timing->hdelay >> 8) & 0x03) << 2) | - ((timing->hactive >> 8) & 0x03); - init[0x04 * 2 - 1] = timing->vdelay & 0xff; - init[0x05 * 2 - 1] = timing->vactive & 0xff; - init[0x06 * 2 - 1] = timing->hdelay & 0xff; - init[0x07 * 2 - 1] = timing->hactive & 0xff; - init[0x08 * 2 - 1] = timing->hscale >> 8; - init[0x09 * 2 - 1] = timing->hscale & 0xff; - /* 0x15 in array is address 0x19 */ - init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */ - /* reset */ - bt819_write(decoder, 0x1f, 0x00); - mdelay(1); - - /* init */ - return bt819_write_block(decoder, init, sizeof(init)); -} - -/* ----------------------------------------------------------------------- */ - -static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) -{ - struct bt819 *decoder = to_bt819(sd); - int status = bt819_read(decoder, 0x00); - int res = V4L2_IN_ST_NO_SIGNAL; - v4l2_std_id std; - - if ((status & 0x80)) - res = 0; - - if ((status & 0x10)) - std = V4L2_STD_PAL; - else - std = V4L2_STD_NTSC; - if (pstd) - *pstd = std; - if (pstatus) - *pstatus = res; - - v4l2_dbg(1, debug, sd, "get status %x\n", status); - return 0; -} - -static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - return bt819_status(sd, NULL, std); -} - -static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - return bt819_status(sd, status, NULL); -} - -static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct bt819 *decoder = to_bt819(sd); - struct timing *timing = NULL; - - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) - v4l2_err(sd, "no notify found!\n"); - - if (std & V4L2_STD_NTSC) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); - bt819_setbit(decoder, 0x01, 0, 1); - bt819_setbit(decoder, 0x01, 1, 0); - bt819_setbit(decoder, 0x01, 5, 0); - bt819_write(decoder, 0x18, 0x68); - bt819_write(decoder, 0x19, 0x5d); - /* bt819_setbit(decoder, 0x1a, 5, 1); */ - timing = &timing_data[1]; - } else if (std & V4L2_STD_PAL) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); - bt819_setbit(decoder, 0x01, 0, 1); - bt819_setbit(decoder, 0x01, 1, 1); - bt819_setbit(decoder, 0x01, 5, 1); - bt819_write(decoder, 0x18, 0x7f); - bt819_write(decoder, 0x19, 0x72); - /* bt819_setbit(decoder, 0x1a, 5, 0); */ - timing = &timing_data[0]; - } else { - v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", - (unsigned long long)std); - return -EINVAL; - } - bt819_write(decoder, 0x03, - (((timing->vdelay >> 8) & 0x03) << 6) | - (((timing->vactive >> 8) & 0x03) << 4) | - (((timing->hdelay >> 8) & 0x03) << 2) | - ((timing->hactive >> 8) & 0x03)); - bt819_write(decoder, 0x04, timing->vdelay & 0xff); - bt819_write(decoder, 0x05, timing->vactive & 0xff); - bt819_write(decoder, 0x06, timing->hdelay & 0xff); - bt819_write(decoder, 0x07, timing->hactive & 0xff); - bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); - bt819_write(decoder, 0x09, timing->hscale & 0xff); - decoder->norm = std; - v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); - return 0; -} - -static int bt819_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct bt819 *decoder = to_bt819(sd); - - v4l2_dbg(1, debug, sd, "set input %x\n", input); - - if (input > 7) - return -EINVAL; - - if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) - v4l2_err(sd, "no notify found!\n"); - - if (decoder->input != input) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); - decoder->input = input; - /* select mode */ - if (decoder->input == 0) { - bt819_setbit(decoder, 0x0b, 6, 0); - bt819_setbit(decoder, 0x1a, 1, 1); - } else { - bt819_setbit(decoder, 0x0b, 6, 1); - bt819_setbit(decoder, 0x1a, 1, 0); - } - v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); - } - return 0; -} - -static int bt819_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct bt819 *decoder = to_bt819(sd); - - v4l2_dbg(1, debug, sd, "enable output %x\n", enable); - - if (decoder->enable != enable) { - decoder->enable = enable; - bt819_setbit(decoder, 0x16, 7, !enable); - } - return 0; -} - -static int bt819_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct bt819 *decoder = to_bt819(sd); - int temp; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - bt819_write(decoder, 0x0a, ctrl->val); - break; - - case V4L2_CID_CONTRAST: - bt819_write(decoder, 0x0c, ctrl->val & 0xff); - bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01)); - break; - - case V4L2_CID_SATURATION: - bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff); - bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01)); - - /* Ratio between U gain and V gain must stay the same as - the ratio between the default U and V gain values. */ - temp = (ctrl->val * 180) / 254; - bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); - bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); - break; - - case V4L2_CID_HUE: - bt819_write(decoder, 0x0f, ctrl->val); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct bt819 *decoder = to_bt819(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops bt819_ctrl_ops = { - .s_ctrl = bt819_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops bt819_core_ops = { - .g_chip_ident = bt819_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = bt819_s_std, -}; - -static const struct v4l2_subdev_video_ops bt819_video_ops = { - .s_routing = bt819_s_routing, - .s_stream = bt819_s_stream, - .querystd = bt819_querystd, - .g_input_status = bt819_g_input_status, -}; - -static const struct v4l2_subdev_ops bt819_ops = { - .core = &bt819_core_ops, - .video = &bt819_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int bt819_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i, ver; - struct bt819 *decoder; - struct v4l2_subdev *sd; - const char *name; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &bt819_ops); - - ver = bt819_read(decoder, 0x17); - switch (ver & 0xf0) { - case 0x70: - name = "bt819a"; - decoder->ident = V4L2_IDENT_BT819A; - break; - case 0x60: - name = "bt817a"; - decoder->ident = V4L2_IDENT_BT817A; - break; - case 0x20: - name = "bt815a"; - decoder->ident = V4L2_IDENT_BT815A; - break; - default: - v4l2_dbg(1, debug, sd, - "unknown chip version 0x%02x\n", ver); - return -ENODEV; - } - - v4l_info(client, "%s found @ 0x%x (%s)\n", name, - client->addr << 1, client->adapter->name); - - decoder->norm = V4L2_STD_NTSC; - decoder->input = 0; - decoder->enable = 1; - - i = bt819_init(sd); - if (i < 0) - v4l2_dbg(1, debug, sd, "init status %d\n", i); - - v4l2_ctrl_handler_init(&decoder->hdl, 4); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_CONTRAST, 0, 511, 1, 0xd8); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_SATURATION, 0, 511, 1, 0xfe); - v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - return 0; -} - -static int bt819_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct bt819 *decoder = to_bt819(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id bt819_id[] = { - { "bt819a", 0 }, - { "bt817a", 0 }, - { "bt815a", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bt819_id); - -static struct i2c_driver bt819_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "bt819", - }, - .probe = bt819_probe, - .remove = bt819_remove, - .id_table = bt819_id, -}; - -module_i2c_driver(bt819_driver); diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c deleted file mode 100644 index 7e5bd365c239..000000000000 --- a/drivers/media/video/bt856.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * bt856 - BT856A Digital Video Encoder (Rockwell Part) - * - * Copyright (C) 1999 Mike Bernson - * Copyright (C) 1998 Dave Perks - * - * Modifications for LML33/DC10plus unified driver - * Copyright (C) 2000 Serguei Miridonov - * - * This code was modify/ported from the saa7111 driver written - * by Dave Perks. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (9/9/2002) - * - * This program is free software; 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 - -MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); -MODULE_AUTHOR("Mike Bernson & Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -#define BT856_REG_OFFSET 0xDA -#define BT856_NR_REG 6 - -struct bt856 { - struct v4l2_subdev sd; - unsigned char reg[BT856_NR_REG]; - - v4l2_std_id norm; -}; - -static inline struct bt856 *to_bt856(struct v4l2_subdev *sd) -{ - return container_of(sd, struct bt856, sd); -} - -/* ----------------------------------------------------------------------- */ - -static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); - - encoder->reg[reg - BT856_REG_OFFSET] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value) -{ - return bt856_write(encoder, reg, - (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | - (value ? (1 << bit) : 0)); -} - -static void bt856_dump(struct bt856 *encoder) -{ - int i; - - v4l2_info(&encoder->sd, "register dump:\n"); - for (i = 0; i < BT856_NR_REG; i += 2) - printk(KERN_CONT " %02x", encoder->reg[i]); - printk(KERN_CONT "\n"); -} - -/* ----------------------------------------------------------------------- */ - -static int bt856_init(struct v4l2_subdev *sd, u32 arg) -{ - struct bt856 *encoder = to_bt856(sd); - - /* This is just for testing!!! */ - v4l2_dbg(1, debug, sd, "init\n"); - bt856_write(encoder, 0xdc, 0x18); - bt856_write(encoder, 0xda, 0); - bt856_write(encoder, 0xde, 0); - - bt856_setbit(encoder, 0xdc, 3, 1); - /*bt856_setbit(encoder, 0xdc, 6, 0);*/ - bt856_setbit(encoder, 0xdc, 4, 1); - - if (encoder->norm & V4L2_STD_NTSC) - bt856_setbit(encoder, 0xdc, 2, 0); - else - bt856_setbit(encoder, 0xdc, 2, 1); - - bt856_setbit(encoder, 0xdc, 1, 1); - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct bt856 *encoder = to_bt856(sd); - - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - if (std & V4L2_STD_NTSC) { - bt856_setbit(encoder, 0xdc, 2, 0); - } else if (std & V4L2_STD_PAL) { - bt856_setbit(encoder, 0xdc, 2, 1); - bt856_setbit(encoder, 0xda, 0, 0); - /*bt856_setbit(encoder, 0xda, 0, 1);*/ - } else { - return -EINVAL; - } - encoder->norm = std; - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct bt856 *encoder = to_bt856(sd); - - v4l2_dbg(1, debug, sd, "set input %d\n", input); - - /* We only have video bus. - * input= 0: input is from bt819 - * input= 1: input is from ZR36060 */ - switch (input) { - case 0: - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - bt856_setbit(encoder, 0xdc, 3, 1); - bt856_setbit(encoder, 0xdc, 6, 0); - break; - case 1: - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - bt856_setbit(encoder, 0xdc, 3, 1); - bt856_setbit(encoder, 0xdc, 6, 1); - break; - case 2: /* Color bar */ - bt856_setbit(encoder, 0xdc, 3, 0); - bt856_setbit(encoder, 0xde, 4, 1); - break; - default: - return -EINVAL; - } - - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops bt856_core_ops = { - .g_chip_ident = bt856_g_chip_ident, - .init = bt856_init, -}; - -static const struct v4l2_subdev_video_ops bt856_video_ops = { - .s_std_output = bt856_s_std_output, - .s_routing = bt856_s_routing, -}; - -static const struct v4l2_subdev_ops bt856_ops = { - .core = &bt856_core_ops, - .video = &bt856_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int bt856_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct bt856 *encoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &bt856_ops); - encoder->norm = V4L2_STD_NTSC; - - bt856_write(encoder, 0xdc, 0x18); - bt856_write(encoder, 0xda, 0); - bt856_write(encoder, 0xde, 0); - - bt856_setbit(encoder, 0xdc, 3, 1); - /*bt856_setbit(encoder, 0xdc, 6, 0);*/ - bt856_setbit(encoder, 0xdc, 4, 1); - - if (encoder->norm & V4L2_STD_NTSC) - bt856_setbit(encoder, 0xdc, 2, 0); - else - bt856_setbit(encoder, 0xdc, 2, 1); - - bt856_setbit(encoder, 0xdc, 1, 1); - bt856_setbit(encoder, 0xde, 4, 0); - bt856_setbit(encoder, 0xde, 3, 1); - - if (debug != 0) - bt856_dump(encoder); - return 0; -} - -static int bt856_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_bt856(sd)); - return 0; -} - -static const struct i2c_device_id bt856_id[] = { - { "bt856", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bt856_id); - -static struct i2c_driver bt856_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "bt856", - }, - .probe = bt856_probe, - .remove = bt856_remove, - .id_table = bt856_id, -}; - -module_i2c_driver(bt856_driver); diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c deleted file mode 100644 index 905320b67a1c..000000000000 --- a/drivers/media/video/bt866.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - bt866 - BT866 Digital Video Encoder (Rockwell Part) - - Copyright (C) 1999 Mike Bernson - Copyright (C) 1998 Dave Perks - - Modifications for LML33/DC10plus unified driver - Copyright (C) 2000 Serguei Miridonov - - This code was modify/ported from the saa7111 driver written - by Dave Perks. - - This code was adapted for the bt866 by Christer Weinigel and ported - to 2.6 by Martin Samuelsson. - - This program is free software; 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 - -MODULE_DESCRIPTION("Brooktree-866 video encoder driver"); -MODULE_AUTHOR("Mike Bernson & Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -struct bt866 { - struct v4l2_subdev sd; - u8 reg[256]; -}; - -static inline struct bt866 *to_bt866(struct v4l2_subdev *sd) -{ - return container_of(sd, struct bt866, sd); -} - -static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) -{ - struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); - u8 buffer[2]; - int err; - - buffer[0] = subaddr; - buffer[1] = data; - - encoder->reg[subaddr] = data; - - v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data); - - for (err = 0; err < 3;) { - if (i2c_master_send(client, buffer, 2) == 2) - break; - err++; - v4l_warn(client, "error #%d writing to 0x%02x\n", - err, subaddr); - schedule_timeout_interruptible(msecs_to_jiffies(100)); - } - if (err == 3) { - v4l_warn(client, "giving up\n"); - return -1; - } - - return 0; -} - -static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - - /* Only PAL supported by this driver at the moment! */ - if (!(std & V4L2_STD_NTSC)) - return -EINVAL; - return 0; -} - -static int bt866_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - static const __u8 init[] = { - 0xc8, 0xcc, /* CRSCALE */ - 0xca, 0x91, /* CBSCALE */ - 0xcc, 0x24, /* YC16 | OSDNUM */ - 0xda, 0x00, /* */ - 0xdc, 0x24, /* SETMODE | PAL */ - 0xde, 0x02, /* EACTIVE */ - - /* overlay colors */ - 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ - 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ - 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ - 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ - 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ - 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ - 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ - 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ - - 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ - 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ - 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ - 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ - 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ - 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ - 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ - 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ - }; - struct bt866 *encoder = to_bt866(sd); - u8 val; - int i; - - for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) - bt866_write(encoder, init[i], init[i+1]); - - val = encoder->reg[0xdc]; - - if (input == 0) - val |= 0x40; /* CBSWAP */ - else - val &= ~0x40; /* !CBSWAP */ - - bt866_write(encoder, 0xdc, val); - - val = encoder->reg[0xcc]; - if (input == 2) - val |= 0x01; /* OSDBAR */ - else - val &= ~0x01; /* !OSDBAR */ - bt866_write(encoder, 0xcc, val); - - v4l2_dbg(1, debug, sd, "set input %d\n", input); - - switch (input) { - case 0: - case 1: - case 2: - break; - default: - return -EINVAL; - } - return 0; -} - -#if 0 -/* Code to setup square pixels, might be of some use in the future, - but is currently unused. */ - val = encoder->reg[0xdc]; - if (*iarg) - val |= 1; /* SQUARE */ - else - val &= ~1; /* !SQUARE */ - bt866_write(client, 0xdc, val); -#endif - -static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops bt866_core_ops = { - .g_chip_ident = bt866_g_chip_ident, -}; - -static const struct v4l2_subdev_video_ops bt866_video_ops = { - .s_std_output = bt866_s_std_output, - .s_routing = bt866_s_routing, -}; - -static const struct v4l2_subdev_ops bt866_ops = { - .core = &bt866_core_ops, - .video = &bt866_video_ops, -}; - -static int bt866_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct bt866 *encoder; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &bt866_ops); - return 0; -} - -static int bt866_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_bt866(sd)); - return 0; -} - -static const struct i2c_device_id bt866_id[] = { - { "bt866", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bt866_id); - -static struct i2c_driver bt866_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "bt866", - }, - .probe = bt866_probe, - .remove = bt866_remove, - .id_table = bt866_id, -}; - -module_i2c_driver(bt866_driver); diff --git a/drivers/media/video/btcx-risc.c b/drivers/media/video/btcx-risc.c deleted file mode 100644 index ac1b2687a20d..000000000000 --- a/drivers/media/video/btcx-risc.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - - btcx-risc.c - - bt848/bt878/cx2388x risc code generator. - - (c) 2000-03 Gerd Knorr [SuSE Labs] - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; 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 "btcx-risc.h" - -MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); - -/* ---------------------------------------------------------- */ -/* allocate/free risc memory */ - -static int memcnt; - -void btcx_riscmem_free(struct pci_dev *pci, - struct btcx_riscmem *risc) -{ - if (NULL == risc->cpu) - return; - if (debug) { - memcnt--; - printk("btcx: riscmem free [%d] dma=%lx\n", - memcnt, (unsigned long)risc->dma); - } - pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); - memset(risc,0,sizeof(*risc)); -} - -int btcx_riscmem_alloc(struct pci_dev *pci, - struct btcx_riscmem *risc, - unsigned int size) -{ - __le32 *cpu; - dma_addr_t dma = 0; - - if (NULL != risc->cpu && risc->size < size) - btcx_riscmem_free(pci,risc); - if (NULL == risc->cpu) { - cpu = pci_alloc_consistent(pci, size, &dma); - if (NULL == cpu) - return -ENOMEM; - risc->cpu = cpu; - risc->dma = dma; - risc->size = size; - if (debug) { - memcnt++; - printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", - memcnt, (unsigned long)dma, cpu, size); - } - } - memset(risc->cpu,0,risc->size); - return 0; -} - -/* ---------------------------------------------------------- */ -/* screen overlay helpers */ - -int -btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, - struct v4l2_clip *clips, unsigned int n) -{ - if (win->left < 0) { - /* left */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = -win->left; - clips[n].c.height = win->height; - n++; - } - if (win->left + win->width > swidth) { - /* right */ - clips[n].c.left = swidth - win->left; - clips[n].c.top = 0; - clips[n].c.width = win->width - clips[n].c.left; - clips[n].c.height = win->height; - n++; - } - if (win->top < 0) { - /* top */ - clips[n].c.left = 0; - clips[n].c.top = 0; - clips[n].c.width = win->width; - clips[n].c.height = -win->top; - n++; - } - if (win->top + win->height > sheight) { - /* bottom */ - clips[n].c.left = 0; - clips[n].c.top = sheight - win->top; - clips[n].c.width = win->width; - clips[n].c.height = win->height - clips[n].c.top; - n++; - } - return n; -} - -int -btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) -{ - s32 nx,nw,dx; - unsigned int i; - - /* fixup window */ - nx = (win->left + mask) & ~mask; - nw = (win->width) & ~mask; - if (nx + nw > win->left + win->width) - nw -= mask+1; - dx = nx - win->left; - win->left = nx; - win->width = nw; - if (debug) - printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", - win->width, win->height, win->left, win->top, dx); - - /* fixup clips */ - for (i = 0; i < n; i++) { - nx = (clips[i].c.left-dx) & ~mask; - nw = (clips[i].c.width) & ~mask; - if (nx + nw < clips[i].c.left-dx + clips[i].c.width) - nw += mask+1; - clips[i].c.left = nx; - clips[i].c.width = nw; - if (debug) - printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", - clips[i].c.width, clips[i].c.height, - clips[i].c.left, clips[i].c.top); - } - return 0; -} - -void -btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) -{ - struct v4l2_clip swap; - int i,j,n; - - if (nclips < 2) - return; - for (i = nclips-2; i >= 0; i--) { - for (n = 0, j = 0; j <= i; j++) { - if (clips[j].c.left > clips[j+1].c.left) { - swap = clips[j]; - clips[j] = clips[j+1]; - clips[j+1] = swap; - n++; - } - } - if (0 == n) - break; - } -} - -void -btcx_calc_skips(int line, int width, int *maxy, - struct btcx_skiplist *skips, unsigned int *nskips, - const struct v4l2_clip *clips, unsigned int nclips) -{ - unsigned int clip,skip; - int end, maxline; - - skip=0; - maxline = 9999; - for (clip = 0; clip < nclips; clip++) { - - /* sanity checks */ - if (clips[clip].c.left + clips[clip].c.width <= 0) - continue; - if (clips[clip].c.left > (signed)width) - break; - - /* vertical range */ - if (line > clips[clip].c.top+clips[clip].c.height-1) - continue; - if (line < clips[clip].c.top) { - if (maxline > clips[clip].c.top-1) - maxline = clips[clip].c.top-1; - continue; - } - if (maxline > clips[clip].c.top+clips[clip].c.height-1) - maxline = clips[clip].c.top+clips[clip].c.height-1; - - /* horizontal range */ - if (0 == skip || clips[clip].c.left > skips[skip-1].end) { - /* new one */ - skips[skip].start = clips[clip].c.left; - if (skips[skip].start < 0) - skips[skip].start = 0; - skips[skip].end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip].end > width) - skips[skip].end = width; - skip++; - } else { - /* overlaps -- expand last one */ - end = clips[clip].c.left + clips[clip].c.width; - if (skips[skip-1].end < end) - skips[skip-1].end = end; - if (skips[skip-1].end > width) - skips[skip-1].end = width; - } - } - *nskips = skip; - *maxy = maxline; - - if (debug) { - printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); - for (skip = 0; skip < *nskips; skip++) { - printk(" %d-%d",skips[skip].start,skips[skip].end); - } - printk("\n"); - } -} - -/* ---------------------------------------------------------- */ - -EXPORT_SYMBOL(btcx_riscmem_alloc); -EXPORT_SYMBOL(btcx_riscmem_free); - -EXPORT_SYMBOL(btcx_screen_clips); -EXPORT_SYMBOL(btcx_align); -EXPORT_SYMBOL(btcx_sort_clips); -EXPORT_SYMBOL(btcx_calc_skips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/btcx-risc.h b/drivers/media/video/btcx-risc.h deleted file mode 100644 index f8bc6e8e7b51..000000000000 --- a/drivers/media/video/btcx-risc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - */ -struct btcx_riscmem { - unsigned int size; - __le32 *cpu; - __le32 *jmp; - dma_addr_t dma; -}; - -struct btcx_skiplist { - int start; - int end; -}; - -int btcx_riscmem_alloc(struct pci_dev *pci, - struct btcx_riscmem *risc, - unsigned int size); -void btcx_riscmem_free(struct pci_dev *pci, - struct btcx_riscmem *risc); - -int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, - struct v4l2_clip *clips, unsigned int n); -int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, - unsigned int n, int mask); -void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); -void btcx_calc_skips(int line, int width, int *maxy, - struct btcx_skiplist *skips, unsigned int *nskips, - const struct v4l2_clip *clips, unsigned int nclips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c deleted file mode 100644 index c8581e26fa9c..000000000000 --- a/drivers/media/video/cs5345.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC - * Copyright (C) 2007 Hans Verkuil - * - * This program is free software; 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 - -MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static bool debug; - -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); - -struct cs5345_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; -}; - -static inline struct cs5345_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct cs5345_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int cs5345_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - if ((input & 0xf) > 6) { - v4l2_err(sd, "Invalid input %d.\n", input); - return -EINVAL; - } - cs5345_write(sd, 0x09, input & 0xf); - cs5345_write(sd, 0x05, input & 0xf0); - return 0; -} - -static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0); - return 0; - case V4L2_CID_AUDIO_VOLUME: - cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f); - cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f); - return 0; - } - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->size = 1; - reg->val = cs5345_read(sd, reg->reg & 0x1f); - return 0; -} - -static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff); - return 0; -} -#endif - -static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0); -} - -static int cs5345_log_status(struct v4l2_subdev *sd) -{ - u8 v = cs5345_read(sd, 0x09) & 7; - u8 m = cs5345_read(sd, 0x04); - int vol = cs5345_read(sd, 0x08) & 0x3f; - - v4l2_info(sd, "Input: %d%s\n", v, - (m & 0x80) ? " (muted)" : ""); - if (vol >= 32) - vol = vol - 64; - v4l2_info(sd, "Volume: %d dB\n", vol); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { - .s_ctrl = cs5345_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops cs5345_core_ops = { - .log_status = cs5345_log_status, - .g_chip_ident = cs5345_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = cs5345_g_register, - .s_register = cs5345_s_register, -#endif -}; - -static const struct v4l2_subdev_audio_ops cs5345_audio_ops = { - .s_routing = cs5345_s_routing, -}; - -static const struct v4l2_subdev_ops cs5345_ops = { - .core = &cs5345_core_ops, - .audio = &cs5345_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int cs5345_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct cs5345_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &cs5345_ops); - - v4l2_ctrl_handler_init(&state->hdl, 2); - v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - /* set volume/mute */ - v4l2_ctrl_handler_setup(&state->hdl); - - cs5345_write(sd, 0x02, 0x00); - cs5345_write(sd, 0x04, 0x01); - cs5345_write(sd, 0x09, 0x01); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int cs5345_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct cs5345_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id cs5345_id[] = { - { "cs5345", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, cs5345_id); - -static struct i2c_driver cs5345_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "cs5345", - }, - .probe = cs5345_probe, - .remove = cs5345_remove, - .id_table = cs5345_id, -}; - -module_i2c_driver(cs5345_driver); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c deleted file mode 100644 index b293912206eb..000000000000 --- a/drivers/media/video/cs53l32a.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver. - * Copyright (C) 2005 Martin Vaughan - * - * Audio source switching for Adaptec AVC-2410 added by Trev Jackson - * - * This program is free software; 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 -#include - -MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); -MODULE_AUTHOR("Martin Vaughan"); -MODULE_LICENSE("GPL"); - -static bool debug; - -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); - - -struct cs53l32a_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; -}; - -static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct cs53l32a_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int cs53l32a_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - /* There are 2 physical inputs, but the second input can be - placed in two modes, the first mode bypasses the PGA (gain), - the second goes through the PGA. Hence there are three - possible inputs to choose from. */ - if (input > 2) { - v4l2_err(sd, "Invalid input %d.\n", input); - return -EINVAL; - } - cs53l32a_write(sd, 0x01, 0x01 + (input << 4)); - return 0; -} - -static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30); - return 0; - case V4L2_CID_AUDIO_VOLUME: - cs53l32a_write(sd, 0x04, (u8)ctrl->val); - cs53l32a_write(sd, 0x05, (u8)ctrl->val); - return 0; - } - return -EINVAL; -} - -static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, - chip, V4L2_IDENT_CS53l32A, 0); -} - -static int cs53l32a_log_status(struct v4l2_subdev *sd) -{ - struct cs53l32a_state *state = to_state(sd); - u8 v = cs53l32a_read(sd, 0x01); - - v4l2_info(sd, "Input: %d\n", (v >> 4) & 3); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { - .s_ctrl = cs53l32a_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { - .log_status = cs53l32a_log_status, - .g_chip_ident = cs53l32a_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { - .s_routing = cs53l32a_s_routing, -}; - -static const struct v4l2_subdev_ops cs53l32a_ops = { - .core = &cs53l32a_core_ops, - .audio = &cs53l32a_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int cs53l32a_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct cs53l32a_state *state; - struct v4l2_subdev *sd; - int i; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - if (!id) - strlcpy(client->name, "cs53l32a", sizeof(client->name)); - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); - - for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(sd, i); - - v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); - } - - v4l2_ctrl_handler_init(&state->hdl, 2); - v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0); - v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - - /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ - - cs53l32a_write(sd, 0x01, 0x21); - cs53l32a_write(sd, 0x02, 0x29); - cs53l32a_write(sd, 0x03, 0x30); - cs53l32a_write(sd, 0x04, 0x00); - cs53l32a_write(sd, 0x05, 0x00); - cs53l32a_write(sd, 0x06, 0x00); - cs53l32a_write(sd, 0x07, 0x00); - - /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ - - for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(sd, i); - - v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); - } - return 0; -} - -static int cs53l32a_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct cs53l32a_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -static const struct i2c_device_id cs53l32a_id[] = { - { "cs53l32a", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, cs53l32a_id); - -static struct i2c_driver cs53l32a_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "cs53l32a", - }, - .probe = cs53l32a_probe, - .remove = cs53l32a_remove, - .id_table = cs53l32a_id, -}; - -module_i2c_driver(cs53l32a_driver); diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c deleted file mode 100644 index 103ef6bad2e2..000000000000 --- a/drivers/media/video/cx2341x.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* - * cx2341x - generic code for cx23415/6/8 based devices - * - * Copyright (C) 2006 Hans Verkuil - * - * This program is free software; 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 - -MODULE_DESCRIPTION("cx23415/6/8 driver"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/********************** COMMON CODE *********************/ - -/* definitions for audio properties bits 29-28 */ -#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 -#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 -#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 - -static const char *cx2341x_get_name(u32 id) -{ - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return "Spatial Filter Mode"; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - return "Spatial Filter"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - return "Spatial Luma Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - return "Spatial Chroma Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return "Temporal Filter Mode"; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - return "Temporal Filter"; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return "Median Filter Type"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - return "Median Luma Filter Maximum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - return "Median Luma Filter Minimum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - return "Median Chroma Filter Maximum"; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - return "Median Chroma Filter Minimum"; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return "Insert Navigation Packets"; - } - return NULL; -} - -static const char **cx2341x_get_menu(u32 id) -{ - static const char *cx2341x_video_spatial_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - "1D Vertical", - "2D H/V Separable", - "2D Symmetric non-separable", - NULL - }; - - static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - NULL - }; - - static const char *cx2341x_video_temporal_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_median_filter_type_menu[] = { - "Off", - "Horizontal", - "Vertical", - "Horizontal/Vertical", - "Diagonal", - NULL - }; - - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return cx2341x_video_spatial_filter_mode_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_luma_spatial_filter_type_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_chroma_spatial_filter_type_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return cx2341x_video_temporal_filter_mode_menu; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_video_median_filter_type_menu; - } - return NULL; -} - -static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) -{ - *name = cx2341x_get_name(id); - *flags = 0; - - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - *type = V4L2_CTRL_TYPE_MENU; - *min = 0; - *step = 0; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - *type = V4L2_CTRL_TYPE_BOOLEAN; - *min = 0; - *max = *step = 1; - break; - default: - *type = V4L2_CTRL_TYPE_INTEGER; - break; - } - switch (id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - *flags |= V4L2_CTRL_FLAG_UPDATE; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - *flags |= V4L2_CTRL_FLAG_SLIDER; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - *flags |= V4L2_CTRL_FLAG_READ_ONLY; - break; - } -} - - -/********************** OLD CODE *********************/ - -/* Must be sorted from low to high control ID! */ -const u32 cx2341x_mpeg_ctrls[] = { - V4L2_CID_MPEG_CLASS, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_CID_MPEG_STREAM_VBI_FMT, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_CID_MPEG_AUDIO_MODE, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, - V4L2_CID_MPEG_AUDIO_EMPHASIS, - V4L2_CID_MPEG_AUDIO_CRC, - V4L2_CID_MPEG_AUDIO_MUTE, - V4L2_CID_MPEG_AUDIO_AC3_BITRATE, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_CID_MPEG_VIDEO_B_FRAMES, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_BITRATE, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, - V4L2_CID_MPEG_VIDEO_MUTE, - V4L2_CID_MPEG_VIDEO_MUTE_YUV, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, - V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, - 0 -}; -EXPORT_SYMBOL(cx2341x_mpeg_ctrls); - -static const struct cx2341x_mpeg_params default_params = { - /* misc */ - .capabilities = 0, - .port = CX2341X_PORT_MEMORY, - .width = 720, - .height = 480, - .is_50hz = 0, - - /* stream */ - .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, - .stream_insert_nav_packets = 0, - - /* audio */ - .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, - .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, - .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K, - .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, - .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, - .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, - .audio_mute = 0, - - /* video */ - .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, - .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, - .video_b_frames = 2, - .video_gop_size = 12, - .video_gop_closure = 1, - .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - .video_bitrate = 6000000, - .video_bitrate_peak = 8000000, - .video_temporal_decimation = 0, - .video_mute = 0, - .video_mute_yuv = 0x008080, /* YCbCr value for black */ - - /* encoding filters */ - .video_spatial_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - .video_spatial_filter = 0, - .video_luma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_chroma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_temporal_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - .video_temporal_filter = 8, - .video_median_filter_type = - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - .video_luma_median_filter_top = 255, - .video_luma_median_filter_bottom = 0, - .video_chroma_median_filter_top = 255, - .video_chroma_median_filter_bottom = 0, -}; -/* Map the control ID to the correct field in the cx2341x_mpeg_params - struct. Return -EINVAL if the ID is unknown, else return 0. */ -static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - ctrl->value = params->audio_sampling_freq; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - ctrl->value = params->audio_encoding; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - ctrl->value = params->audio_l2_bitrate; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - ctrl->value = params->audio_ac3_bitrate; - break; - case V4L2_CID_MPEG_AUDIO_MODE: - ctrl->value = params->audio_mode; - break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - ctrl->value = params->audio_mode_extension; - break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - ctrl->value = params->audio_emphasis; - break; - case V4L2_CID_MPEG_AUDIO_CRC: - ctrl->value = params->audio_crc; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - ctrl->value = params->audio_mute; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = params->video_encoding; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - ctrl->value = params->video_aspect; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - ctrl->value = params->video_b_frames; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = params->video_gop_size; - break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - ctrl->value = params->video_gop_closure; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - ctrl->value = params->video_bitrate_mode; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = params->video_bitrate; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - ctrl->value = params->video_bitrate_peak; - break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - ctrl->value = params->video_temporal_decimation; - break; - case V4L2_CID_MPEG_VIDEO_MUTE: - ctrl->value = params->video_mute; - break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: - ctrl->value = params->video_mute_yuv; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - ctrl->value = params->stream_type; - break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - ctrl->value = params->stream_vbi_fmt; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - ctrl->value = params->video_spatial_filter_mode; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - ctrl->value = params->video_spatial_filter; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - ctrl->value = params->video_luma_spatial_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - ctrl->value = params->video_chroma_spatial_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - ctrl->value = params->video_temporal_filter_mode; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - ctrl->value = params->video_temporal_filter; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - ctrl->value = params->video_median_filter_type; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - ctrl->value = params->video_luma_median_filter_top; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - ctrl->value = params->video_luma_median_filter_bottom; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - ctrl->value = params->video_chroma_median_filter_top; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - ctrl->value = params->video_chroma_median_filter_bottom; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - ctrl->value = params->stream_insert_nav_packets; - break; - default: - return -EINVAL; - } - return 0; -} - -/* Map the control ID to the correct field in the cx2341x_mpeg_params - struct. Return -EINVAL if the ID is unknown, else return 0. */ -static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, - struct v4l2_ext_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - if (busy) - return -EBUSY; - params->audio_sampling_freq = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (busy) - return -EBUSY; - if (params->capabilities & CX2341X_CAP_HAS_AC3) - if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && - ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3) - return -ERANGE; - params->audio_encoding = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - if (busy) - return -EBUSY; - params->audio_l2_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - if (busy) - return -EBUSY; - if (!(params->capabilities & CX2341X_CAP_HAS_AC3)) - return -EINVAL; - params->audio_ac3_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MODE: - params->audio_mode = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - params->audio_mode_extension = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - params->audio_emphasis = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_CRC: - params->audio_crc = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - params->audio_mute = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - params->video_aspect = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: { - int b = ctrl->value + 1; - int gop = params->video_gop_size; - params->video_b_frames = ctrl->value; - params->video_gop_size = b * ((gop + b - 1) / b); - /* Max GOP size = 34 */ - while (params->video_gop_size > 34) - params->video_gop_size -= b; - break; - } - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: { - int b = params->video_b_frames + 1; - int gop = ctrl->value; - params->video_gop_size = b * ((gop + b - 1) / b); - /* Max GOP size = 34 */ - while (params->video_gop_size > 34) - params->video_gop_size -= b; - ctrl->value = params->video_gop_size; - break; - } - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - params->video_gop_closure = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if (busy) - return -EBUSY; - /* MPEG-1 only allows CBR */ - if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && - ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) - return -EINVAL; - params->video_bitrate_mode = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - if (busy) - return -EBUSY; - params->video_bitrate = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - if (busy) - return -EBUSY; - params->video_bitrate_peak = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - params->video_temporal_decimation = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_MUTE: - params->video_mute = (ctrl->value != 0); - break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: - params->video_mute_yuv = ctrl->value; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - if (busy) - return -EBUSY; - params->stream_type = ctrl->value; - params->video_encoding = - (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || - params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : - V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - /* MPEG-1 implies CBR */ - params->video_bitrate_mode = - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - params->stream_vbi_fmt = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - params->video_spatial_filter_mode = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - params->video_spatial_filter = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - params->video_luma_spatial_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - params->video_chroma_spatial_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - params->video_temporal_filter_mode = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - params->video_temporal_filter = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - params->video_median_filter_type = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - params->video_luma_median_filter_top = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - params->video_luma_median_filter_bottom = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - params->video_chroma_median_filter_top = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - params->video_chroma_median_filter_bottom = ctrl->value; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - params->stream_insert_nav_packets = ctrl->value; - break; - default: - return -EINVAL; - } - return 0; -} - -static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, - s32 min, s32 max, s32 step, s32 def) -{ - const char *name; - - switch (qctrl->id) { - /* MPEG controls */ - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type, - &min, &max, &step, &def, &qctrl->flags); - qctrl->minimum = min; - qctrl->maximum = max; - qctrl->step = step; - qctrl->default_value = def; - qctrl->reserved[0] = qctrl->reserved[1] = 0; - strlcpy(qctrl->name, name, sizeof(qctrl->name)); - return 0; - - default: - return v4l2_ctrl_query_fill(qctrl, min, max, step, def); - } -} - -int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, - struct v4l2_queryctrl *qctrl) -{ - int err; - - switch (qctrl->id) { - case V4L2_CID_MPEG_CLASS: - return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, - default_params.stream_vbi_fmt); - - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - - case V4L2_CID_MPEG_AUDIO_ENCODING: - if (params->capabilities & CX2341X_CAP_HAS_AC3) { - /* - * The state of L2 & AC3 bitrate controls can change - * when this control changes, but v4l2_ctrl_query_fill() - * already sets V4L2_CTRL_FLAG_UPDATE for - * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here. - */ - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_AC3, 1, - default_params.audio_encoding); - } - - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, - default_params.audio_encoding); - - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L2_BITRATE_192K, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, - default_params.audio_l2_bitrate); - if (err) - return err; - if (params->capabilities & CX2341X_CAP_HAS_AC3 && - params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_AUDIO_MODE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_STEREO, - V4L2_MPEG_AUDIO_MODE_MONO, 1, - V4L2_MPEG_AUDIO_MODE_STEREO); - - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - if (err == 0 && - params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_EMPHASIS_NONE, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - - case V4L2_CID_MPEG_AUDIO_CRC: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_CRC_NONE, - V4L2_MPEG_AUDIO_CRC_CRC16, 1, - V4L2_MPEG_AUDIO_CRC_NONE); - - case V4L2_CID_MPEG_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_AC3_BITRATE_48K, - V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1, - default_params.audio_ac3_bitrate); - if (err) - return err; - if (params->capabilities & CX2341X_CAP_HAS_AC3) { - if (params->audio_encoding != - V4L2_MPEG_AUDIO_ENCODING_AC3) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - } else - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - - case V4L2_CID_MPEG_VIDEO_ENCODING: - /* this setting is read-only for the cx2341x since the - V4L2_CID_MPEG_STREAM_TYPE really determines the - MPEG-1/2 setting */ - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - if (err == 0) - qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - return err; - - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_221x100, 1, - V4L2_MPEG_VIDEO_ASPECT_4x3); - - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); - - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, - params->is_50hz ? 12 : 15); - - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); - - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - err = v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - if (err == 0 && - params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); - - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); - if (err == 0 && - params->video_bitrate_mode == - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - - case V4L2_CID_MPEG_VIDEO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ - return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); - - /* CX23415/6 specific */ - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1, - default_params.video_spatial_filter_mode); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, - default_params.video_spatial_filter); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, - 1, - default_params.video_luma_spatial_filter_type); - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - 1, - default_params.video_chroma_spatial_filter_type); - if (params->video_spatial_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1, - default_params.video_temporal_filter_mode); - - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, - default_params.video_temporal_filter); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_temporal_filter_mode == - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1, - default_params.video_median_filter_type); - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_luma_median_filter_top); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_luma_median_filter_bottom); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_chroma_median_filter_top); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, - default_params.video_chroma_median_filter_bottom); - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - if (params->video_median_filter_type == - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return 0; - - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, - default_params.stream_insert_nav_packets); - - default: - return -EINVAL; - - } -} -EXPORT_SYMBOL(cx2341x_ctrl_query); - -const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) -{ - static const char * const mpeg_stream_type_without_ts[] = { - "MPEG-2 Program Stream", - "", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - - static const char *mpeg_stream_type_with_ts[] = { - "MPEG-2 Program Stream", - "MPEG-2 Transport Stream", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - - static const char *mpeg_audio_encoding_l2_ac3[] = { - "", - "MPEG-1/2 Layer II", - "", - "", - "AC-3", - NULL - }; - - switch (id) { - case V4L2_CID_MPEG_STREAM_TYPE: - return (p->capabilities & CX2341X_CAP_HAS_TS) ? - mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; - case V4L2_CID_MPEG_AUDIO_ENCODING: - return (p->capabilities & CX2341X_CAP_HAS_AC3) ? - mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id); - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return NULL; - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_get_menu(id); - default: - return v4l2_ctrl_get_menu(id); - } -} -EXPORT_SYMBOL(cx2341x_ctrl_get_menu); - -static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) -{ - params->audio_properties = - (params->audio_sampling_freq << 0) | - (params->audio_mode << 8) | - (params->audio_mode_extension << 10) | - (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) - ? 3 : params->audio_emphasis) << 12) | - (params->audio_crc << 14); - - if ((params->capabilities & CX2341X_CAP_HAS_AC3) && - params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { - params->audio_properties |= - /* Not sure if this MPEG Layer II setting is required */ - ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | - (params->audio_ac3_bitrate << 4) | - (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); - } else { - /* Assuming MPEG Layer II */ - params->audio_properties |= - ((3 - params->audio_encoding) << 2) | - ((1 + params->audio_l2_bitrate) << 4); - } -} - -int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, - struct v4l2_ext_controls *ctrls, unsigned int cmd) -{ - int err = 0; - int i; - - if (cmd == VIDIOC_G_EXT_CTRLS) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = cx2341x_get_ctrl(params, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - } - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - struct v4l2_queryctrl qctrl; - const char * const *menu_items = NULL; - - qctrl.id = ctrl->id; - err = cx2341x_ctrl_query(params, &qctrl); - if (err) - break; - if (qctrl.type == V4L2_CTRL_TYPE_MENU) - menu_items = cx2341x_ctrl_get_menu(params, qctrl.id); - err = v4l2_ctrl_check(ctrl, &qctrl, menu_items); - if (err) - break; - err = cx2341x_set_ctrl(params, busy, ctrl); - if (err) - break; - } - if (err == 0 && - params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - params->video_bitrate_peak < params->video_bitrate) { - err = -ERANGE; - ctrls->error_idx = ctrls->count; - } - if (err) - ctrls->error_idx = i; - else - cx2341x_calc_audio_properties(params); - return err; -} -EXPORT_SYMBOL(cx2341x_ext_ctrls); - -void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) -{ - *p = default_params; - cx2341x_calc_audio_properties(p); -} -EXPORT_SYMBOL(cx2341x_fill_defaults); - -static int cx2341x_api(void *priv, cx2341x_mbox_func func, - u32 cmd, int args, ...) -{ - u32 data[CX2341X_MBOX_MAX_DATA]; - va_list vargs; - int i; - - va_start(vargs, args); - - for (i = 0; i < args; i++) - data[i] = va_arg(vargs, int); - va_end(vargs); - return func(priv, cmd, args, 0, data); -} - -#define NEQ(field) (old->field != new->field) - -int cx2341x_update(void *priv, cx2341x_mbox_func func, - const struct cx2341x_mpeg_params *old, - const struct cx2341x_mpeg_params *new) -{ - static int mpeg_stream_type[] = { - 0, /* MPEG-2 PS */ - 1, /* MPEG-2 TS */ - 2, /* MPEG-1 SS */ - 14, /* DVD */ - 11, /* VCD */ - 12, /* SVCD */ - }; - - int err = 0; - int force = (old == NULL); - u16 temporal = new->video_temporal_filter; - - cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0); - - if (force || NEQ(is_50hz)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, - new->is_50hz); - if (err) return err; - } - - if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) { - u16 w = new->width; - u16 h = new->height; - - if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { - w /= 2; - h /= 2; - } - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, - h, w); - if (err) return err; - } - if (force || NEQ(stream_type)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, - mpeg_stream_type[new->stream_type]); - if (err) return err; - } - if (force || NEQ(video_aspect)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, - 1 + new->video_aspect); - if (err) return err; - } - if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2, - new->video_gop_size, new->video_b_frames + 1); - if (err) return err; - } - if (force || NEQ(video_gop_closure)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, - new->video_gop_closure); - if (err) return err; - } - if (force || NEQ(audio_properties)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, - 1, new->audio_properties); - if (err) return err; - } - if (force || NEQ(audio_mute)) { - err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, - new->audio_mute); - if (err) return err; - } - if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) || - NEQ(video_bitrate_peak)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5, - new->video_bitrate_mode, new->video_bitrate, - new->video_bitrate_peak / 400, 0, 0); - if (err) return err; - } - if (force || NEQ(video_spatial_filter_mode) || - NEQ(video_temporal_filter_mode) || - NEQ(video_median_filter_type)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, - 2, new->video_spatial_filter_mode | - (new->video_temporal_filter_mode << 1), - new->video_median_filter_type); - if (err) return err; - } - if (force || NEQ(video_luma_median_filter_bottom) || - NEQ(video_luma_median_filter_top) || - NEQ(video_chroma_median_filter_bottom) || - NEQ(video_chroma_median_filter_top)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4, - new->video_luma_median_filter_bottom, - new->video_luma_median_filter_top, - new->video_chroma_median_filter_bottom, - new->video_chroma_median_filter_top); - if (err) return err; - } - if (force || NEQ(video_luma_spatial_filter_type) || - NEQ(video_chroma_spatial_filter_type)) { - err = cx2341x_api(priv, func, - CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, - 2, new->video_luma_spatial_filter_type, - new->video_chroma_spatial_filter_type); - if (err) return err; - } - if (force || NEQ(video_spatial_filter) || - old->video_temporal_filter != temporal) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, - 2, new->video_spatial_filter, temporal); - if (err) return err; - } - if (force || NEQ(video_temporal_decimation)) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, - 1, new->video_temporal_decimation); - if (err) return err; - } - if (force || NEQ(video_mute) || - (new->video_mute && NEQ(video_mute_yuv))) { - err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, - new->video_mute | (new->video_mute_yuv << 8)); - if (err) return err; - } - if (force || NEQ(stream_insert_nav_packets)) { - err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, - 7, new->stream_insert_nav_packets); - if (err) return err; - } - return 0; -} -EXPORT_SYMBOL(cx2341x_update); - -static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id) -{ - const char * const *menu = cx2341x_ctrl_get_menu(p, id); - struct v4l2_ext_control ctrl; - - if (menu == NULL) - goto invalid; - ctrl.id = id; - if (cx2341x_get_ctrl(p, &ctrl)) - goto invalid; - while (ctrl.value-- && *menu) menu++; - if (*menu == NULL) - goto invalid; - return *menu; - -invalid: - return ""; -} - -void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) -{ - int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - - /* Stream */ - printk(KERN_INFO "%s: Stream: %s", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); - if (p->stream_insert_nav_packets) - printk(" (with navigation packets)"); - printk("\n"); - printk(KERN_INFO "%s: VBI Format: %s\n", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); - - /* Video */ - printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", - prefix, - p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), - p->is_50hz ? 25 : 30, - (p->video_mute) ? " (muted)" : ""); - printk(KERN_INFO "%s: Video: %s, %s, %s, %d", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), - cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), - p->video_bitrate); - if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) - printk(", Peak %d", p->video_bitrate_peak); - printk("\n"); - printk(KERN_INFO - "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", - prefix, - p->video_gop_size, p->video_b_frames, - p->video_gop_closure ? "" : "No "); - if (p->video_temporal_decimation) - printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", - prefix, p->video_temporal_decimation); - - /* Audio */ - printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", - prefix, - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), - cx2341x_menu_item(p, - p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3 - ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE - : V4L2_CID_MPEG_AUDIO_L2_BITRATE), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), - p->audio_mute ? " (muted)" : ""); - if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) - printk(", %s", cx2341x_menu_item(p, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); - printk(", %s, %s\n", - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); - - /* Encoding filters */ - printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), - p->video_spatial_filter); - - printk(KERN_INFO "%s: Temporal Filter: %s, %d\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), - p->video_temporal_filter); - printk(KERN_INFO - "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", - prefix, - cx2341x_menu_item(p, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), - p->video_luma_median_filter_bottom, - p->video_luma_median_filter_top, - p->video_chroma_median_filter_bottom, - p->video_chroma_median_filter_top); -} -EXPORT_SYMBOL(cx2341x_log_status); - - - -/********************** NEW CODE *********************/ - -static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct cx2341x_handler, hdl); -} - -static int cx2341x_hdl_api(struct cx2341x_handler *hdl, - u32 cmd, int args, ...) -{ - u32 data[CX2341X_MBOX_MAX_DATA]; - va_list vargs; - int i; - - va_start(vargs, args); - - for (i = 0; i < args; i++) - data[i] = va_arg(vargs, int); - va_end(vargs); - return hdl->func(hdl->priv, cmd, args, 0, data); -} - -/* ctrl->handler->lock is held, so it is safe to access cur.val */ -static inline int cx2341x_neq(struct v4l2_ctrl *ctrl) -{ - return ctrl && ctrl->val != ctrl->cur.val; -} - -static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct cx2341x_handler *hdl = to_cxhdl(ctrl); - s32 val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_B_FRAMES: { - /* video gop cluster */ - int b = val + 1; - int gop = hdl->video_gop_size->val; - - gop = b * ((gop + b - 1) / b); - - /* Max GOP size = 34 */ - while (gop > 34) - gop -= b; - hdl->video_gop_size->val = gop; - break; - } - - case V4L2_CID_MPEG_STREAM_TYPE: - /* stream type cluster */ - hdl->video_encoding->val = - (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || - hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? - V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : - V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - /* MPEG-1 implies CBR */ - hdl->video_bitrate_mode->val = - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - /* peak bitrate shall be >= normal bitrate */ - if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - hdl->video_bitrate_peak->val < hdl->video_bitrate->val) - hdl->video_bitrate_peak->val = hdl->video_bitrate->val; - break; - } - return 0; -} - -static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - static const int mpeg_stream_type[] = { - 0, /* MPEG-2 PS */ - 1, /* MPEG-2 TS */ - 2, /* MPEG-1 SS */ - 14, /* DVD */ - 11, /* VCD */ - 12, /* SVCD */ - }; - struct cx2341x_handler *hdl = to_cxhdl(ctrl); - s32 val = ctrl->val; - u32 props; - int err; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (hdl->ops && hdl->ops->s_stream_vbi_fmt) - return hdl->ops->s_stream_vbi_fmt(hdl, val); - return 0; - - case V4L2_CID_MPEG_VIDEO_ASPECT: - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1); - - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val); - - case V4L2_CID_MPEG_AUDIO_MUTE: - return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val); - - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val); - - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val); - - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - /* audio properties cluster */ - props = (hdl->audio_sampling_freq->val << 0) | - (hdl->audio_mode->val << 8) | - (hdl->audio_mode_extension->val << 10) | - (hdl->audio_crc->val << 14); - if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) - props |= 3 << 12; - else - props |= hdl->audio_emphasis->val << 12; - - if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) { - props |= -#if 1 - /* Not sure if this MPEG Layer II setting is required */ - ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | -#endif - (hdl->audio_ac3_bitrate->val << 4) | - (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); - } else { - /* Assuming MPEG Layer II */ - props |= - ((3 - hdl->audio_encoding->val) << 2) | - ((1 + hdl->audio_l2_bitrate->val) << 4); - } - err = cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props); - if (err) - return err; - - hdl->audio_properties = props; - if (hdl->audio_ac3_bitrate) { - int is_ac3 = hdl->audio_encoding->val == - V4L2_MPEG_AUDIO_ENCODING_AC3; - - v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3); - v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3); - } - v4l2_ctrl_activate(hdl->audio_mode_extension, - hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO); - if (cx2341x_neq(hdl->audio_sampling_freq) && - hdl->ops && hdl->ops->s_audio_sampling_freq) - return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val); - if (cx2341x_neq(hdl->audio_mode) && - hdl->ops && hdl->ops->s_audio_mode) - return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val); - return 0; - - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - /* video gop cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2, - hdl->video_gop_size->val, - hdl->video_b_frames->val + 1); - - case V4L2_CID_MPEG_STREAM_TYPE: - /* stream type cluster */ - err = cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]); - if (err) - return err; - - err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5, - hdl->video_bitrate_mode->val, - hdl->video_bitrate->val, - hdl->video_bitrate_peak->val / 400, 0, 0); - if (err) - return err; - - v4l2_ctrl_activate(hdl->video_bitrate_mode, - hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1); - v4l2_ctrl_activate(hdl->video_bitrate_peak, - hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - if (cx2341x_neq(hdl->video_encoding) && - hdl->ops && hdl->ops->s_video_encoding) - return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val); - return 0; - - case V4L2_CID_MPEG_VIDEO_MUTE: - /* video mute cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1, - hdl->video_mute->val | - (hdl->video_mute_yuv->val << 8)); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: { - int active_filter; - - /* video filter mode */ - err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, - hdl->video_spatial_filter_mode->val | - (hdl->video_temporal_filter_mode->val << 1), - hdl->video_median_filter_type->val); - if (err) - return err; - - active_filter = hdl->video_spatial_filter_mode->val != - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO; - v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter); - v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter); - active_filter = hdl->video_temporal_filter_mode->val != - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO; - v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter); - active_filter = hdl->video_median_filter_type->val != - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF; - v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter); - v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter); - v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter); - return 0; - } - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - /* video filter type cluster */ - return cx2341x_hdl_api(hdl, - CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, - hdl->video_luma_spatial_filter_type->val, - hdl->video_chroma_spatial_filter_type->val); - - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - /* video filter cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, - hdl->video_spatial_filter->val, - hdl->video_temporal_filter->val); - - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - /* video median cluster */ - return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4, - hdl->video_luma_median_filter_bottom->val, - hdl->video_luma_median_filter_top->val, - hdl->video_chroma_median_filter_bottom->val, - hdl->video_chroma_median_filter_top->val); - } - return -EINVAL; -} - -static const struct v4l2_ctrl_ops cx2341x_ops = { - .try_ctrl = cx2341x_try_ctrl, - .s_ctrl = cx2341x_s_ctrl, -}; - -static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, - u32 id, s32 min, s32 max, s32 step, s32 def) -{ - struct v4l2_ctrl_config cfg; - - cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags); - cfg.ops = &cx2341x_ops; - cfg.id = id; - cfg.min = min; - cfg.max = max; - cfg.def = def; - if (cfg.type == V4L2_CTRL_TYPE_MENU) { - cfg.step = 0; - cfg.menu_skip_mask = step; - cfg.qmenu = cx2341x_get_menu(id); - } else { - cfg.step = step; - cfg.menu_skip_mask = 0; - } - return v4l2_ctrl_new_custom(hdl, &cfg, NULL); -} - -static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl, - u32 id, s32 min, s32 max, s32 step, s32 def) -{ - return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def); -} - -static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl, - u32 id, s32 max, s32 mask, s32 def) -{ - return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def); -} - -int cx2341x_handler_init(struct cx2341x_handler *cxhdl, - unsigned nr_of_controls_hint) -{ - struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; - u32 caps = cxhdl->capabilities; - int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI; - int has_ac3 = caps & CX2341X_CAP_HAS_AC3; - int has_ts = caps & CX2341X_CAP_HAS_TS; - - cxhdl->width = 720; - cxhdl->height = 480; - - v4l2_ctrl_handler_init(hdl, nr_of_controls_hint); - - /* Add controls in ascending control ID order for fastest - insertion time. */ - cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_STREAM_VBI_FMT, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_ENCODING, - V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); - cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_L2_BITRATE, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff, - V4L2_MPEG_AUDIO_L2_BITRATE_224K); - cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_MODE, - V4L2_MPEG_AUDIO_MODE_MONO, 0, - V4L2_MPEG_AUDIO_MODE_STEREO); - cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_EMPHASIS, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_CRC, - V4L2_MPEG_AUDIO_CRC_CRC16, 0, - V4L2_MPEG_AUDIO_CRC_NONE); - - cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0); - if (has_ac3) - cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_AUDIO_AC3_BITRATE, - V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03, - V4L2_MPEG_AUDIO_AC3_BITRATE_224K); - cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_MPEG_VIDEO_ASPECT_221x100, 0, - V4L2_MPEG_VIDEO_ASPECT_4x3); - cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2); - cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 1, 34, 1, cxhdl->is_50hz ? 12 : 15); - cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); - cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0, 27000000, 1, 6000000); - cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0, 27000000, 1, 8000000); - cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0); - cxhdl->video_mute = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0); - cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl, - V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080); - - /* CX23415/6 specific */ - cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); - cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, - 0, 15, 1, 0); - cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, - 0, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR); - cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - 0, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR); - cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, - 0, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); - cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, - 0, 31, 1, 8); - cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, - 0, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); - cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, - 0, 255, 1, 0); - cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, - 0, 255, 1, 255); - cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, - 0, 255, 1, 0); - cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl, - V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, - 0, 255, 1, 255); - cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, - 0, 1, 1, 0); - - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - return err; - } - - v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq); - v4l2_ctrl_cluster(2, &cxhdl->video_b_frames); - v4l2_ctrl_cluster(5, &cxhdl->stream_type); - v4l2_ctrl_cluster(2, &cxhdl->video_mute); - v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode); - v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type); - v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter); - v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top); - - return 0; -} -EXPORT_SYMBOL(cx2341x_handler_init); - -void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz) -{ - cxhdl->is_50hz = is_50hz; - cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15; -} -EXPORT_SYMBOL(cx2341x_handler_set_50hz); - -int cx2341x_handler_setup(struct cx2341x_handler *cxhdl) -{ - int h = cxhdl->height; - int w = cxhdl->width; - int err; - - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0); - if (err) - return err; - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz); - if (err) - return err; - - if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { - w /= 2; - h /= 2; - } - err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); - if (err) - return err; - return v4l2_ctrl_handler_setup(&cxhdl->hdl); -} -EXPORT_SYMBOL(cx2341x_handler_setup); - -void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy) -{ - v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy); - v4l2_ctrl_grab(cxhdl->audio_encoding, busy); - v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy); - v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy); - v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy); - v4l2_ctrl_grab(cxhdl->stream_type, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate, busy); - v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy); -} -EXPORT_SYMBOL(cx2341x_handler_set_busy); diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig deleted file mode 100644 index 451133ad41ff..000000000000 --- a/drivers/media/video/cx25840/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config VIDEO_CX25840 - tristate "Conexant CX2584x audio/video decoders" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Conexant CX2584x audio/video decoders. - - To compile this driver as a module, choose M here: the - module will be called cx25840 diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile deleted file mode 100644 index dc40dde2e0c8..000000000000 --- a/drivers/media/video/cx25840/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ - cx25840-vbi.o cx25840-ir.o - -obj-$(CONFIG_VIDEO_CX25840) += cx25840.o - -ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c deleted file mode 100644 index 34b96c7cfd62..000000000000 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ /dev/null @@ -1,571 +0,0 @@ -/* cx25840 audio functions - * - * This program is free software; 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 -#include -#include -#include - -#include "cx25840-core.h" - -/* - * Note: The PLL and SRC parameters are based on a reference frequency that - * would ideally be: - * - * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz - * - * However, it's not the exact reference frequency that matters, only that the - * firmware and modules that comprise the driver for a particular board all - * use the same value (close to the ideal value). - * - * Comments below will note which reference frequency is assumed for various - * parameters. They will usually be one of - * - * ref_freq = 28.636360 MHz - * or - * ref_freq = 28.636363 MHz - */ - -static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (state->aud_input != CX25840_AUDIO_SERIAL) { - switch (freq) { - case 32000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 - */ - cx25840_write4(client, 0x108, 0x1006040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x1bb39ee - * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 - * 196.6 MHz pre-postdivide - * FIXME < 200 MHz is out of specified valid range - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x01bb39ee); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src3/4/6_ctl */ - /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ - cx25840_write4(client, 0x900, 0x0801f77f); - cx25840_write4(client, 0x904, 0x0801f77f); - cx25840_write4(client, 0x90c, 0x0801f77f); - break; - - case 44100: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 - */ - cx25840_write4(client, 0x108, 0x1009040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x0ec6bd6 - * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 - * 271 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src3/4/6_ctl */ - /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ - cx25840_write4(client, 0x900, 0x08016d59); - cx25840_write4(client, 0x904, 0x08016d59); - cx25840_write4(client, 0x90c, 0x08016d59); - break; - - case 48000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 - */ - cx25840_write4(client, 0x108, 0x100a040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x098d6e5 - * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 - * 295 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x0098d6e5); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } else { - switch (freq) { - case 32000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e - */ - cx25840_write4(client, 0x108, 0x1e08040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x12a0869 - * 28636363 * 0x8.9504348/0x1e = 32000 * 256 - * 246 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x012a0869); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x54); - - if (is_cx2583x(state)) - break; - - /* src1_ctl */ - /* 0x1.0000 = 32000/32000 */ - cx25840_write4(client, 0x8f8, 0x08010000); - - /* src3/4/6_ctl */ - /* 0x2.0000 = 2 * (32000/32000) */ - cx25840_write4(client, 0x900, 0x08020000); - cx25840_write4(client, 0x904, 0x08020000); - cx25840_write4(client, 0x90c, 0x08020000); - break; - - case 44100: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 - */ - cx25840_write4(client, 0x108, 0x1809040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x0ec6bd6 - * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 - * 271 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src1_ctl */ - /* 0x1.60cd = 44100/32000 */ - cx25840_write4(client, 0x8f8, 0x080160cd); - - /* src3/4/6_ctl */ - /* 0x1.7385 = 2 * (32000/44100) */ - cx25840_write4(client, 0x900, 0x08017385); - cx25840_write4(client, 0x904, 0x08017385); - cx25840_write4(client, 0x90c, 0x08017385); - break; - - case 48000: - /* - * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 - * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 - */ - cx25840_write4(client, 0x108, 0x180a040f); - - /* - * VID_PLL Fraction (register 0x10c) = 0x2be2fe - * 28636360 * 0xf.15f17f0/4 = 108 MHz - * 432 MHz pre-postdivide - */ - - /* - * AUX_PLL Fraction = 0x098d6e5 - * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 - * 295 MHz pre-postdivide - * FIXME 28636363 ref_freq doesn't match VID PLL ref - */ - cx25840_write4(client, 0x110, 0x0098d6e5); - - /* - * SA_MCLK_SEL = 1 - * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider - */ - cx25840_write(client, 0x127, 0x50); - - if (is_cx2583x(state)) - break; - - /* src1_ctl */ - /* 0x1.8000 = 48000/32000 */ - cx25840_write4(client, 0x8f8, 0x08018000); - - /* src3/4/6_ctl */ - /* 0x1.5555 = 2 * (32000/48000) */ - cx25840_write4(client, 0x900, 0x08015555); - cx25840_write4(client, 0x904, 0x08015555); - cx25840_write4(client, 0x90c, 0x08015555); - break; - } - } - - state->audclk_freq = freq; - - return 0; -} - -static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - return cx25840_set_audclk_freq(client, freq); -} - -static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (state->aud_input != CX25840_AUDIO_SERIAL) { - switch (freq) { - case 32000: - case 44100: - case 48000: - /* We don't have register values - * so avoid destroying registers. */ - /* FIXME return -EINVAL; */ - break; - } - } else { - switch (freq) { - case 32000: - case 44100: - /* We don't have register values - * so avoid destroying registers. */ - /* FIXME return -EINVAL; */ - break; - - case 48000: - /* src1_ctl */ - /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ - cx25840_write4(client, 0x8f8, 0x0801867c); - - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } - - state->audclk_freq = freq; - - return 0; -} - -static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (state->aud_input != CX25840_AUDIO_SERIAL) { - switch (freq) { - case 32000: - /* src3/4/6_ctl */ - /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ - cx25840_write4(client, 0x900, 0x0801f77f); - cx25840_write4(client, 0x904, 0x0801f77f); - cx25840_write4(client, 0x90c, 0x0801f77f); - break; - - case 44100: - /* src3/4/6_ctl */ - /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ - cx25840_write4(client, 0x900, 0x08016d59); - cx25840_write4(client, 0x904, 0x08016d59); - cx25840_write4(client, 0x90c, 0x08016d59); - break; - - case 48000: - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } else { - switch (freq) { - /* FIXME These cases make different assumptions about audclk */ - case 32000: - /* src1_ctl */ - /* 0x1.0000 = 32000/32000 */ - cx25840_write4(client, 0x8f8, 0x08010000); - - /* src3/4/6_ctl */ - /* 0x2.0000 = 2 * (32000/32000) */ - cx25840_write4(client, 0x900, 0x08020000); - cx25840_write4(client, 0x904, 0x08020000); - cx25840_write4(client, 0x90c, 0x08020000); - break; - - case 44100: - /* src1_ctl */ - /* 0x1.60cd = 44100/32000 */ - cx25840_write4(client, 0x8f8, 0x080160cd); - - /* src3/4/6_ctl */ - /* 0x1.7385 = 2 * (32000/44100) */ - cx25840_write4(client, 0x900, 0x08017385); - cx25840_write4(client, 0x904, 0x08017385); - cx25840_write4(client, 0x90c, 0x08017385); - break; - - case 48000: - /* src1_ctl */ - /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ - cx25840_write4(client, 0x8f8, 0x0801867c); - - /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - break; - } - } - - state->audclk_freq = freq; - - return 0; -} - -static int set_audclk_freq(struct i2c_client *client, u32 freq) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (freq != 32000 && freq != 44100 && freq != 48000) - return -EINVAL; - - if (is_cx231xx(state)) - return cx231xx_set_audclk_freq(client, freq); - - if (is_cx2388x(state)) - return cx23885_set_audclk_freq(client, freq); - - if (is_cx2583x(state)) - return cx25836_set_audclk_freq(client, freq); - - return cx25840_set_audclk_freq(client, freq); -} - -void cx25840_audio_set_path(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (!is_cx2583x(state)) { - /* assert soft reset */ - cx25840_and_or(client, 0x810, ~0x1, 0x01); - - /* stop microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0); - - /* Mute everything to prevent the PFFT! */ - cx25840_write(client, 0x8d3, 0x1f); - - if (state->aud_input == CX25840_AUDIO_SERIAL) { - /* Set Path1 to Serial Audio Input */ - cx25840_write4(client, 0x8d0, 0x01011012); - - /* The microcontroller should not be started for the - * non-tuner inputs: autodetection is specific for - * TV audio. */ - } else { - /* Set Path1 to Analog Demod Main Channel */ - cx25840_write4(client, 0x8d0, 0x1f063870); - } - } - - set_audclk_freq(client, state->audclk_freq); - - if (!is_cx2583x(state)) { - if (state->aud_input != CX25840_AUDIO_SERIAL) { - /* When the microcontroller detects the - * audio format, it will unmute the lines */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); - } - - /* deassert soft reset */ - cx25840_and_or(client, 0x810, ~0x1, 0x00); - - /* Ensure the controller is running when we exit */ - if (is_cx2388x(state) || is_cx231xx(state)) - cx25840_and_or(client, 0x803, ~0x10, 0x10); - } -} - -static void set_volume(struct i2c_client *client, int volume) -{ - int vol; - - /* Convert the volume to msp3400 values (0-127) */ - vol = volume >> 9; - - /* now scale it up to cx25840 values - * -114dB to -96dB maps to 0 - * this should be 19, but in my testing that was 4dB too loud */ - if (vol <= 23) { - vol = 0; - } else { - vol -= 23; - } - - /* PATH1_VOLUME */ - cx25840_write(client, 0x8d4, 228 - (vol * 2)); -} - -static void set_balance(struct i2c_client *client, int balance) -{ - int bal = balance >> 8; - if (bal > 0x80) { - /* PATH1_BAL_LEFT */ - cx25840_and_or(client, 0x8d5, 0x7f, 0x80); - /* PATH1_BAL_LEVEL */ - cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f); - } else { - /* PATH1_BAL_LEFT */ - cx25840_and_or(client, 0x8d5, 0x7f, 0x00); - /* PATH1_BAL_LEVEL */ - cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal); - } -} - -int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - int retval; - - if (!is_cx2583x(state)) - cx25840_and_or(client, 0x810, ~0x1, 1); - if (state->aud_input != CX25840_AUDIO_SERIAL) { - cx25840_and_or(client, 0x803, ~0x10, 0); - cx25840_write(client, 0x8d3, 0x1f); - } - retval = set_audclk_freq(client, freq); - if (state->aud_input != CX25840_AUDIO_SERIAL) - cx25840_and_or(client, 0x803, ~0x10, 0x10); - if (!is_cx2583x(state)) - cx25840_and_or(client, 0x810, ~0x1, 0); - return retval; -} - -static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - if (state->mute->val) - set_volume(client, 0); - else - set_volume(client, state->volume->val); - break; - case V4L2_CID_AUDIO_BASS: - /* PATH1_EQ_BASS_VOL */ - cx25840_and_or(client, 0x8d9, ~0x3f, - 48 - (ctrl->val * 48 / 0xffff)); - break; - case V4L2_CID_AUDIO_TREBLE: - /* PATH1_EQ_TREBLE_VOL */ - cx25840_and_or(client, 0x8db, ~0x3f, - 48 - (ctrl->val * 48 / 0xffff)); - break; - case V4L2_CID_AUDIO_BALANCE: - set_balance(client, ctrl->val); - break; - default: - return -EINVAL; - } - return 0; -} - -const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { - .s_ctrl = cx25840_audio_s_ctrl, -}; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c deleted file mode 100644 index d8eac3e30a7e..000000000000 --- a/drivers/media/video/cx25840/cx25840-core.c +++ /dev/null @@ -1,5340 +0,0 @@ -/* cx25840 - Conexant CX25840 audio/video decoder driver - * - * Copyright (C) 2004 Ulf Eklund - * - * Based on the saa7115 driver and on the first version of Chris Kennedy's - * cx25840 driver. - * - * Changes by Tyler Trafford - * - cleanup/rewrite for V4L2 API (2005) - * - * VBI support by Hans Verkuil . - * - * NTSC sliced VBI support by Christopher Neufeld - * with additional fixes by Hans Verkuil . - * - * CX23885 support by Steven Toth . - * - * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are - * Copyright (C) 2010 Andy Walls - * - * CX23888 DIF support for the HVR1850 - * Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cx25840-core.h" - -MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); -MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); -MODULE_LICENSE("GPL"); - -#define CX25840_VID_INT_STAT_REG 0x410 -#define CX25840_VID_INT_STAT_BITS 0x0000ffff -#define CX25840_VID_INT_MASK_BITS 0xffff0000 -#define CX25840_VID_INT_MASK_SHFT 16 -#define CX25840_VID_INT_MASK_REG 0x412 - -#define CX23885_AUD_MC_INT_MASK_REG 0x80c -#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000 -#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff -#define CX23885_AUD_MC_INT_STAT_SHFT 16 - -#define CX25840_AUD_INT_CTRL_REG 0x812 -#define CX25840_AUD_INT_STAT_REG 0x813 - -#define CX23885_PIN_CTRL_IRQ_REG 0x123 -#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40 -#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20 -#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10 - -#define CX25840_IR_STATS_REG 0x210 -#define CX25840_IR_IRQEN_REG 0x214 - -static int cx25840_debug; - -module_param_named(debug,cx25840_debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); - - -/* ----------------------------------------------------------------------- */ -static void cx23888_std_setup(struct i2c_client *client); - -int cx25840_write(struct i2c_client *client, u16 addr, u8 value) -{ - u8 buffer[3]; - buffer[0] = addr >> 8; - buffer[1] = addr & 0xff; - buffer[2] = value; - return i2c_master_send(client, buffer, 3); -} - -int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) -{ - u8 buffer[6]; - buffer[0] = addr >> 8; - buffer[1] = addr & 0xff; - buffer[2] = value & 0xff; - buffer[3] = (value >> 8) & 0xff; - buffer[4] = (value >> 16) & 0xff; - buffer[5] = value >> 24; - return i2c_master_send(client, buffer, 6); -} - -u8 cx25840_read(struct i2c_client * client, u16 addr) -{ - struct i2c_msg msgs[2]; - u8 tx_buf[2], rx_buf[1]; - - /* Write register address */ - tx_buf[0] = addr >> 8; - tx_buf[1] = addr & 0xff; - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = 2; - msgs[0].buf = (char *) tx_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = 1; - msgs[1].buf = (char *) rx_buf; - - if (i2c_transfer(client->adapter, msgs, 2) < 2) - return 0; - - return rx_buf[0]; -} - -u32 cx25840_read4(struct i2c_client * client, u16 addr) -{ - struct i2c_msg msgs[2]; - u8 tx_buf[2], rx_buf[4]; - - /* Write register address */ - tx_buf[0] = addr >> 8; - tx_buf[1] = addr & 0xff; - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = 2; - msgs[0].buf = (char *) tx_buf; - - /* Read data from registers */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = 4; - msgs[1].buf = (char *) rx_buf; - - if (i2c_transfer(client->adapter, msgs, 2) < 2) - return 0; - - return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) | - rx_buf[0]; -} - -int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, - u8 or_value) -{ - return cx25840_write(client, addr, - (cx25840_read(client, addr) & and_mask) | - or_value); -} - -int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, - u32 or_value) -{ - return cx25840_write4(client, addr, - (cx25840_read4(client, addr) & and_mask) | - or_value); -} - -/* ----------------------------------------------------------------------- */ - -static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, - enum cx25840_audio_input aud_input); - -/* ----------------------------------------------------------------------- */ - -static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, - struct v4l2_subdev_io_pin_config *p) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - u32 pin_ctrl; - u8 gpio_oe, gpio_data, strength; - - pin_ctrl = cx25840_read4(client, 0x120); - gpio_oe = cx25840_read(client, 0x160); - gpio_data = cx25840_read(client, 0x164); - - for (i = 0; i < n; i++) { - strength = p[i].strength; - if (strength > CX25840_PIN_DRIVE_FAST) - strength = CX25840_PIN_DRIVE_FAST; - - switch (p[i].pin) { - case CX23885_PIN_IRQ_N_GPIO16: - if (p[i].function != CX23885_PAD_IRQ_N) { - /* GPIO16 */ - pin_ctrl &= ~(0x1 << 25); - } else { - /* IRQ_N */ - if (p[i].flags & - (V4L2_SUBDEV_IO_PIN_DISABLE | - V4L2_SUBDEV_IO_PIN_INPUT)) { - pin_ctrl &= ~(0x1 << 25); - } else { - pin_ctrl |= (0x1 << 25); - } - if (p[i].flags & - V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) { - pin_ctrl &= ~(0x1 << 24); - } else { - pin_ctrl |= (0x1 << 24); - } - } - break; - case CX23885_PIN_IR_RX_GPIO19: - if (p[i].function != CX23885_PAD_GPIO19) { - /* IR_RX */ - gpio_oe |= (0x1 << 0); - pin_ctrl &= ~(0x3 << 18); - pin_ctrl |= (strength << 18); - } else { - /* GPIO19 */ - gpio_oe &= ~(0x1 << 0); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 0); - gpio_data |= ((p[i].value & 0x1) << 0); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_IR_TX_GPIO20: - if (p[i].function != CX23885_PAD_GPIO20) { - /* IR_TX */ - gpio_oe |= (0x1 << 1); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE) - pin_ctrl &= ~(0x1 << 10); - else - pin_ctrl |= (0x1 << 10); - pin_ctrl &= ~(0x3 << 18); - pin_ctrl |= (strength << 18); - } else { - /* GPIO20 */ - gpio_oe &= ~(0x1 << 1); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 1); - gpio_data |= ((p[i].value & 0x1) << 1); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_I2S_SDAT_GPIO21: - if (p[i].function != CX23885_PAD_GPIO21) { - /* I2S_SDAT */ - /* TODO: Input or Output config */ - gpio_oe |= (0x1 << 2); - pin_ctrl &= ~(0x3 << 22); - pin_ctrl |= (strength << 22); - } else { - /* GPIO21 */ - gpio_oe &= ~(0x1 << 2); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 2); - gpio_data |= ((p[i].value & 0x1) << 2); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_I2S_WCLK_GPIO22: - if (p[i].function != CX23885_PAD_GPIO22) { - /* I2S_WCLK */ - /* TODO: Input or Output config */ - gpio_oe |= (0x1 << 3); - pin_ctrl &= ~(0x3 << 22); - pin_ctrl |= (strength << 22); - } else { - /* GPIO22 */ - gpio_oe &= ~(0x1 << 3); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 3); - gpio_data |= ((p[i].value & 0x1) << 3); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - case CX23885_PIN_I2S_BCLK_GPIO23: - if (p[i].function != CX23885_PAD_GPIO23) { - /* I2S_BCLK */ - /* TODO: Input or Output config */ - gpio_oe |= (0x1 << 4); - pin_ctrl &= ~(0x3 << 22); - pin_ctrl |= (strength << 22); - } else { - /* GPIO23 */ - gpio_oe &= ~(0x1 << 4); - if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { - gpio_data &= ~(0x1 << 4); - gpio_data |= ((p[i].value & 0x1) << 4); - } - pin_ctrl &= ~(0x3 << 12); - pin_ctrl |= (strength << 12); - } - break; - } - } - - cx25840_write(client, 0x164, gpio_data); - cx25840_write(client, 0x160, gpio_oe); - cx25840_write4(client, 0x120, pin_ctrl); - return 0; -} - -static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, - struct v4l2_subdev_io_pin_config *pincfg) -{ - struct cx25840_state *state = to_state(sd); - - if (is_cx2388x(state)) - return cx23885_s_io_pin_config(sd, n, pincfg); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static void init_dll1(struct i2c_client *client) -{ - /* This is the Hauppauge sequence used to - * initialize the Delay Lock Loop 1 (ADC DLL). */ - cx25840_write(client, 0x159, 0x23); - cx25840_write(client, 0x15a, 0x87); - cx25840_write(client, 0x15b, 0x06); - udelay(10); - cx25840_write(client, 0x159, 0xe1); - udelay(10); - cx25840_write(client, 0x15a, 0x86); - cx25840_write(client, 0x159, 0xe0); - cx25840_write(client, 0x159, 0xe1); - cx25840_write(client, 0x15b, 0x10); -} - -static void init_dll2(struct i2c_client *client) -{ - /* This is the Hauppauge sequence used to - * initialize the Delay Lock Loop 2 (ADC DLL). */ - cx25840_write(client, 0x15d, 0xe3); - cx25840_write(client, 0x15e, 0x86); - cx25840_write(client, 0x15f, 0x06); - udelay(10); - cx25840_write(client, 0x15d, 0xe1); - cx25840_write(client, 0x15d, 0xe0); - cx25840_write(client, 0x15d, 0xe1); -} - -static void cx25836_initialize(struct i2c_client *client) -{ - /* reset configuration is described on page 3-77 of the CX25836 datasheet */ - /* 2. */ - cx25840_and_or(client, 0x000, ~0x01, 0x01); - cx25840_and_or(client, 0x000, ~0x01, 0x00); - /* 3a. */ - cx25840_and_or(client, 0x15a, ~0x70, 0x00); - /* 3b. */ - cx25840_and_or(client, 0x15b, ~0x1e, 0x06); - /* 3c. */ - cx25840_and_or(client, 0x159, ~0x02, 0x02); - /* 3d. */ - udelay(10); - /* 3e. */ - cx25840_and_or(client, 0x159, ~0x02, 0x00); - /* 3f. */ - cx25840_and_or(client, 0x159, ~0xc0, 0xc0); - /* 3g. */ - cx25840_and_or(client, 0x159, ~0x01, 0x00); - cx25840_and_or(client, 0x159, ~0x01, 0x01); - /* 3h. */ - cx25840_and_or(client, 0x15b, ~0x1e, 0x10); -} - -static void cx25840_work_handler(struct work_struct *work) -{ - struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work); - cx25840_loadfw(state->c); - wake_up(&state->fw_wait); -} - -static void cx25840_initialize(struct i2c_client *client) -{ - DEFINE_WAIT(wait); - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct workqueue_struct *q; - - /* datasheet startup in numbered steps, refer to page 3-77 */ - /* 2. */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - /* The default of this register should be 4, but I get 0 instead. - * Set this register to 4 manually. */ - cx25840_write(client, 0x000, 0x04); - /* 3. */ - init_dll1(client); - init_dll2(client); - cx25840_write(client, 0x136, 0x0a); - /* 4. */ - cx25840_write(client, 0x13c, 0x01); - cx25840_write(client, 0x13c, 0x00); - /* 5. */ - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ - INIT_WORK(&state->fw_work, cx25840_work_handler); - init_waitqueue_head(&state->fw_wait); - q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); - - /* 6. */ - cx25840_write(client, 0x115, 0x8c); - cx25840_write(client, 0x116, 0x07); - cx25840_write(client, 0x118, 0x02); - /* 7. */ - cx25840_write(client, 0x4a5, 0x80); - cx25840_write(client, 0x4a5, 0x00); - cx25840_write(client, 0x402, 0x00); - /* 8. */ - cx25840_and_or(client, 0x401, ~0x18, 0); - cx25840_and_or(client, 0x4a2, ~0x10, 0x10); - /* steps 8c and 8d are done in change_input() */ - /* 10. */ - cx25840_write(client, 0x8d3, 0x1f); - cx25840_write(client, 0x8e3, 0x03); - - cx25840_std_setup(client); - - /* trial and error says these are needed to get audio */ - cx25840_write(client, 0x914, 0xa0); - cx25840_write(client, 0x918, 0xa0); - cx25840_write(client, 0x919, 0x01); - - /* stereo preferred */ - cx25840_write(client, 0x809, 0x04); - /* AC97 shift */ - cx25840_write(client, 0x8cf, 0x0f); - - /* (re)set input */ - set_input(client, state->vid_input, state->aud_input); - - /* start microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); -} - -static void cx23885_initialize(struct i2c_client *client) -{ - DEFINE_WAIT(wait); - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct workqueue_struct *q; - - /* - * Come out of digital power down - * The CX23888, at least, needs this, otherwise registers aside from - * 0x0-0x2 can't be read or written. - */ - cx25840_write(client, 0x000, 0); - - /* Internal Reset */ - cx25840_and_or(client, 0x102, ~0x01, 0x01); - cx25840_and_or(client, 0x102, ~0x01, 0x00); - - /* Stop microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - - /* DIF in reset? */ - cx25840_write(client, 0x398, 0); - - /* - * Trust the default xtal, no division - * '885: 28.636363... MHz - * '887: 25.000000 MHz - * '888: 50.000000 MHz - */ - cx25840_write(client, 0x2, 0x76); - - /* Power up all the PLL's and DLL */ - cx25840_write(client, 0x1, 0x40); - - /* Sys PLL */ - switch (state->id) { - case V4L2_IDENT_CX23888_AV: - /* - * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz - * 572.73 MHz before post divide - */ - /* HVR1850 or 50MHz xtal */ - cx25840_write(client, 0x2, 0x71); - cx25840_write4(client, 0x11c, 0x01d1744c); - cx25840_write4(client, 0x118, 0x00000416); - cx25840_write4(client, 0x404, 0x0010253e); - cx25840_write4(client, 0x42c, 0x42600000); - cx25840_write4(client, 0x44c, 0x161f1000); - break; - case V4L2_IDENT_CX23887_AV: - /* - * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz - * 572.73 MHz before post divide - */ - cx25840_write4(client, 0x11c, 0x01d1744c); - cx25840_write4(client, 0x118, 0x00000416); - break; - case V4L2_IDENT_CX23885_AV: - default: - /* - * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz - * 572.73 MHz before post divide - */ - cx25840_write4(client, 0x11c, 0x00000000); - cx25840_write4(client, 0x118, 0x00000414); - break; - } - - /* Disable DIF bypass */ - cx25840_write4(client, 0x33c, 0x00000001); - - /* DIF Src phase inc */ - cx25840_write4(client, 0x340, 0x0df7df83); - - /* - * Vid PLL - * Setup for a BT.656 pixel clock of 13.5 Mpixels/second - * - * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz - * 432.0 MHz before post divide - */ - - /* HVR1850 */ - switch (state->id) { - case V4L2_IDENT_CX23888_AV: - /* 888/HVR1250 specific */ - cx25840_write4(client, 0x10c, 0x13333333); - cx25840_write4(client, 0x108, 0x00000515); - break; - default: - cx25840_write4(client, 0x10c, 0x002be2c9); - cx25840_write4(client, 0x108, 0x0000040f); - } - - /* Luma */ - cx25840_write4(client, 0x414, 0x00107d12); - - /* Chroma */ - cx25840_write4(client, 0x420, 0x3d008282); - - /* - * Aux PLL - * Initial setup for audio sample clock: - * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz - * Initial I2S output/master clock(?): - * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz - */ - switch (state->id) { - case V4L2_IDENT_CX23888_AV: - /* - * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz - * 368.64 MHz before post divide - * 122.88 MHz / 0xa = 12.288 MHz - */ - /* HVR1850 or 50MHz xtal */ - cx25840_write4(client, 0x114, 0x017dbf48); - cx25840_write4(client, 0x110, 0x000a030e); - break; - case V4L2_IDENT_CX23887_AV: - /* - * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz - * 368.64 MHz before post divide - * 122.88 MHz / 0xa = 12.288 MHz - */ - cx25840_write4(client, 0x114, 0x017dbf48); - cx25840_write4(client, 0x110, 0x000a030e); - break; - case V4L2_IDENT_CX23885_AV: - default: - /* - * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz - * 368.64 MHz before post divide - * 122.88 MHz / 0xa = 12.288 MHz - */ - cx25840_write4(client, 0x114, 0x01bf0c9e); - cx25840_write4(client, 0x110, 0x000a030c); - break; - }; - - /* ADC2 input select */ - cx25840_write(client, 0x102, 0x10); - - /* VIN1 & VIN5 */ - cx25840_write(client, 0x103, 0x11); - - /* Enable format auto detect */ - cx25840_write(client, 0x400, 0); - /* Fast subchroma lock */ - /* White crush, Chroma AGC & Chroma Killer enabled */ - cx25840_write(client, 0x401, 0xe8); - - /* Select AFE clock pad output source */ - cx25840_write(client, 0x144, 0x05); - - /* Drive GPIO2 direction and values for HVR1700 - * where an onboard mux selects the output of demodulator - * vs the 417. Failure to set this results in no DTV. - * It's safe to set this across all Hauppauge boards - * currently, regardless of the board type. - */ - cx25840_write(client, 0x160, 0x1d); - cx25840_write(client, 0x164, 0x00); - - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ - INIT_WORK(&state->fw_work, cx25840_work_handler); - init_waitqueue_head(&state->fw_wait); - q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); - - /* Call the cx23888 specific std setup func, we no longer rely on - * the generic cx24840 func. - */ - if (is_cx23888(state)) - cx23888_std_setup(client); - else - cx25840_std_setup(client); - - /* (re)set input */ - set_input(client, state->vid_input, state->aud_input); - - /* start microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); - - /* Disable and clear video interrupts - we don't use them */ - cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff); - - /* Disable and clear audio interrupts - we don't use them */ - cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff); - cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff); - - /* CC raw enable */ - /* - VIP 1.1 control codes - 10bit, blue field enable. - * - enable raw data during vertical blanking. - * - enable ancillary Data insertion for 656 or VIP. - */ - cx25840_write4(client, 0x404, 0x0010253e); - - /* CC on - Undocumented Register */ - cx25840_write(client, 0x42f, 0x66); - - /* HVR-1250 / HVR1850 DIF related */ - /* Power everything up */ - cx25840_write4(client, 0x130, 0x0); - - /* Undocumented */ - cx25840_write4(client, 0x478, 0x6628021F); - - /* AFE_CLK_OUT_CTRL - Select the clock output source as output */ - cx25840_write4(client, 0x144, 0x5); - - /* I2C_OUT_CTL - I2S output configuration as - * Master, Sony, Left justified, left sample on WS=1 - */ - cx25840_write4(client, 0x918, 0x1a0); - - /* AFE_DIAG_CTRL1 */ - cx25840_write4(client, 0x134, 0x000a1800); - - /* AFE_DIAG_CTRL3 - Inverted Polarity for Audio and Video */ - cx25840_write4(client, 0x13c, 0x00310000); -} - -/* ----------------------------------------------------------------------- */ - -static void cx231xx_initialize(struct i2c_client *client) -{ - DEFINE_WAIT(wait); - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct workqueue_struct *q; - - /* Internal Reset */ - cx25840_and_or(client, 0x102, ~0x01, 0x01); - cx25840_and_or(client, 0x102, ~0x01, 0x00); - - /* Stop microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - - /* DIF in reset? */ - cx25840_write(client, 0x398, 0); - - /* Trust the default xtal, no division */ - /* This changes for the cx23888 products */ - cx25840_write(client, 0x2, 0x76); - - /* Bring down the regulator for AUX clk */ - cx25840_write(client, 0x1, 0x40); - - /* Disable DIF bypass */ - cx25840_write4(client, 0x33c, 0x00000001); - - /* DIF Src phase inc */ - cx25840_write4(client, 0x340, 0x0df7df83); - - /* Luma */ - cx25840_write4(client, 0x414, 0x00107d12); - - /* Chroma */ - cx25840_write4(client, 0x420, 0x3d008282); - - /* ADC2 input select */ - cx25840_write(client, 0x102, 0x10); - - /* VIN1 & VIN5 */ - cx25840_write(client, 0x103, 0x11); - - /* Enable format auto detect */ - cx25840_write(client, 0x400, 0); - /* Fast subchroma lock */ - /* White crush, Chroma AGC & Chroma Killer enabled */ - cx25840_write(client, 0x401, 0xe8); - - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ - INIT_WORK(&state->fw_work, cx25840_work_handler); - init_waitqueue_head(&state->fw_wait); - q = create_singlethread_workqueue("cx25840_fw"); - prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); - queue_work(q, &state->fw_work); - schedule(); - finish_wait(&state->fw_wait, &wait); - destroy_workqueue(q); - - cx25840_std_setup(client); - - /* (re)set input */ - set_input(client, state->vid_input, state->aud_input); - - /* start microcontroller */ - cx25840_and_or(client, 0x803, ~0x10, 0x10); - - /* CC raw enable */ - cx25840_write(client, 0x404, 0x0b); - - /* CC on */ - cx25840_write(client, 0x42f, 0x66); - cx25840_write4(client, 0x474, 0x1e1e601a); -} - -/* ----------------------------------------------------------------------- */ - -void cx25840_std_setup(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - v4l2_std_id std = state->std; - int hblank, hactive, burst, vblank, vactive, sc; - int vblank656, src_decimation; - int luma_lpf, uv_lpf, comb; - u32 pll_int, pll_frac, pll_post; - - /* datasheet startup, step 8d */ - if (std & ~V4L2_STD_NTSC) - cx25840_write(client, 0x49f, 0x11); - else - cx25840_write(client, 0x49f, 0x14); - - if (std & V4L2_STD_625_50) { - hblank = 132; - hactive = 720; - burst = 93; - vblank = 36; - vactive = 580; - vblank656 = 40; - src_decimation = 0x21f; - luma_lpf = 2; - - if (std & V4L2_STD_SECAM) { - uv_lpf = 0; - comb = 0; - sc = 0x0a425f; - } else if (std == V4L2_STD_PAL_Nc) { - uv_lpf = 1; - comb = 0x20; - sc = 556453; - } else { - uv_lpf = 1; - comb = 0x20; - sc = 688739; - } - } else { - hactive = 720; - hblank = 122; - vactive = 487; - luma_lpf = 1; - uv_lpf = 1; - - src_decimation = 0x21f; - if (std == V4L2_STD_PAL_60) { - vblank = 26; - vblank656 = 26; - burst = 0x5b; - luma_lpf = 2; - comb = 0x20; - sc = 688739; - } else if (std == V4L2_STD_PAL_M) { - vblank = 20; - vblank656 = 24; - burst = 0x61; - comb = 0x20; - sc = 555452; - } else { - vblank = 26; - vblank656 = 26; - burst = 0x5b; - comb = 0x66; - sc = 556063; - } - } - - /* DEBUG: Displays configured PLL frequency */ - if (!is_cx231xx(state)) { - pll_int = cx25840_read(client, 0x108); - pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; - pll_post = cx25840_read(client, 0x109); - v4l_dbg(1, cx25840_debug, client, - "PLL regs = int: %u, frac: %u, post: %u\n", - pll_int, pll_frac, pll_post); - - if (pll_post) { - int fin, fsc; - int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; - - pll /= pll_post; - v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", - pll / 1000000, pll % 1000000); - v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", - pll / 8000000, (pll / 8) % 1000000); - - fin = ((u64)src_decimation * pll) >> 12; - v4l_dbg(1, cx25840_debug, client, - "ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); - - fsc = (((u64)sc) * pll) >> 24L; - v4l_dbg(1, cx25840_debug, client, - "Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); - - v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " - "vblank %i, vactive %i, vblank656 %i, src_dec %i, " - "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " - "sc 0x%06x\n", - hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); - } - } - - /* Sets horizontal blanking delay and active lines */ - cx25840_write(client, 0x470, hblank); - cx25840_write(client, 0x471, - 0xff & (((hblank >> 8) & 0x3) | (hactive << 4))); - cx25840_write(client, 0x472, hactive >> 4); - - /* Sets burst gate delay */ - cx25840_write(client, 0x473, burst); - - /* Sets vertical blanking delay and active duration */ - cx25840_write(client, 0x474, vblank); - cx25840_write(client, 0x475, - 0xff & (((vblank >> 8) & 0x3) | (vactive << 4))); - cx25840_write(client, 0x476, vactive >> 4); - cx25840_write(client, 0x477, vblank656); - - /* Sets src decimation rate */ - cx25840_write(client, 0x478, 0xff & src_decimation); - cx25840_write(client, 0x479, 0xff & (src_decimation >> 8)); - - /* Sets Luma and UV Low pass filters */ - cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); - - /* Enables comb filters */ - cx25840_write(client, 0x47b, comb); - - /* Sets SC Step*/ - cx25840_write(client, 0x47c, sc); - cx25840_write(client, 0x47d, 0xff & sc >> 8); - cx25840_write(client, 0x47e, 0xff & sc >> 16); - - /* Sets VBI parameters */ - if (std & V4L2_STD_625_50) { - cx25840_write(client, 0x47f, 0x01); - state->vbi_line_offset = 5; - } else { - cx25840_write(client, 0x47f, 0x00); - state->vbi_line_offset = 8; - } -} - -/* ----------------------------------------------------------------------- */ - -static void input_change(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - v4l2_std_id std = state->std; - - /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ - if (std & V4L2_STD_SECAM) { - cx25840_write(client, 0x402, 0); - } - else { - cx25840_write(client, 0x402, 0x04); - cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); - } - cx25840_and_or(client, 0x401, ~0x60, 0); - cx25840_and_or(client, 0x401, ~0x60, 0x60); - - /* Don't write into audio registers on cx2583x chips */ - if (is_cx2583x(state)) - return; - - cx25840_and_or(client, 0x810, ~0x01, 1); - - if (state->radio) { - cx25840_write(client, 0x808, 0xf9); - cx25840_write(client, 0x80b, 0x00); - } - else if (std & V4L2_STD_525_60) { - /* Certain Hauppauge PVR150 models have a hardware bug - that causes audio to drop out. For these models the - audio standard must be set explicitly. - To be precise: it affects cards with tuner models - 85, 99 and 112 (model numbers from tveeprom). */ - int hw_fix = state->pvr150_workaround; - - if (std == V4L2_STD_NTSC_M_JP) { - /* Japan uses EIAJ audio standard */ - cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7); - } else if (std == V4L2_STD_NTSC_M_KR) { - /* South Korea uses A2 audio standard */ - cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8); - } else { - /* Others use the BTSC audio standard */ - cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); - } - cx25840_write(client, 0x80b, 0x00); - } else if (std & V4L2_STD_PAL) { - /* Autodetect audio standard and audio system */ - cx25840_write(client, 0x808, 0xff); - /* Since system PAL-L is pretty much non-existent and - not used by any public broadcast network, force - 6.5 MHz carrier to be interpreted as System DK, - this avoids DK audio detection instability */ - cx25840_write(client, 0x80b, 0x00); - } else if (std & V4L2_STD_SECAM) { - /* Autodetect audio standard and audio system */ - cx25840_write(client, 0x808, 0xff); - /* If only one of SECAM-DK / SECAM-L is required, then force - 6.5MHz carrier, else autodetect it */ - if ((std & V4L2_STD_SECAM_DK) && - !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { - /* 6.5 MHz carrier to be interpreted as System DK */ - cx25840_write(client, 0x80b, 0x00); - } else if (!(std & V4L2_STD_SECAM_DK) && - (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { - /* 6.5 MHz carrier to be interpreted as System L */ - cx25840_write(client, 0x80b, 0x08); - } else { - /* 6.5 MHz carrier to be autodetected */ - cx25840_write(client, 0x80b, 0x10); - } - } - - cx25840_and_or(client, 0x810, ~0x01, 0); -} - -static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, - enum cx25840_audio_input aud_input) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && - vid_input <= CX25840_COMPOSITE8); - u8 is_component = (vid_input & CX25840_COMPONENT_ON) == - CX25840_COMPONENT_ON; - u8 is_dif = (vid_input & CX25840_DIF_ON) == - CX25840_DIF_ON; - u8 is_svideo = (vid_input & CX25840_SVIDEO_ON) == - CX25840_SVIDEO_ON; - int luma = vid_input & 0xf0; - int chroma = vid_input & 0xf00; - u8 reg; - u32 val; - - v4l_dbg(1, cx25840_debug, client, - "decoder set video input %d, audio input %d\n", - vid_input, aud_input); - - if (vid_input >= CX25840_VIN1_CH1) { - v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", - vid_input); - reg = vid_input & 0xff; - is_composite = !is_component && - ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); - - v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", - reg, is_composite); - } else if (is_composite) { - reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); - } else { - if ((vid_input & ~0xff0) || - luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || - chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { - v4l_err(client, "0x%04x is not a valid video input!\n", - vid_input); - return -EINVAL; - } - reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); - if (chroma >= CX25840_SVIDEO_CHROMA7) { - reg &= 0x3f; - reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2; - } else { - reg &= 0xcf; - reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4; - } - } - - /* The caller has previously prepared the correct routing - * configuration in reg (for the cx23885) so we have no - * need to attempt to flip bits for earlier av decoders. - */ - if (!is_cx2388x(state) && !is_cx231xx(state)) { - switch (aud_input) { - case CX25840_AUDIO_SERIAL: - /* do nothing, use serial audio input */ - break; - case CX25840_AUDIO4: reg &= ~0x30; break; - case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; - case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; - case CX25840_AUDIO7: reg &= ~0xc0; break; - case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; - - default: - v4l_err(client, "0x%04x is not a valid audio input!\n", - aud_input); - return -EINVAL; - } - } - - cx25840_write(client, 0x103, reg); - - /* Set INPUT_MODE to Composite, S-Video or Component */ - if (is_component) - cx25840_and_or(client, 0x401, ~0x6, 0x6); - else - cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); - - if (is_cx2388x(state)) { - - /* Enable or disable the DIF for tuner use */ - if (is_dif) { - cx25840_and_or(client, 0x102, ~0x80, 0x80); - - /* Set of defaults for NTSC and PAL */ - cx25840_write4(client, 0x31c, 0xc2262600); - cx25840_write4(client, 0x320, 0xc2262600); - - /* 18271 IF - Nobody else yet uses a different - * tuner with the DIF, so these are reasonable - * assumptions (HVR1250 and HVR1850 specific). - */ - cx25840_write4(client, 0x318, 0xda262600); - cx25840_write4(client, 0x33c, 0x2a24c800); - cx25840_write4(client, 0x104, 0x0704dd00); - } else { - cx25840_write4(client, 0x300, 0x015c28f5); - - cx25840_and_or(client, 0x102, ~0x80, 0); - cx25840_write4(client, 0x340, 0xdf7df83); - cx25840_write4(client, 0x104, 0x0704dd80); - cx25840_write4(client, 0x314, 0x22400600); - cx25840_write4(client, 0x318, 0x40002600); - cx25840_write4(client, 0x324, 0x40002600); - cx25840_write4(client, 0x32c, 0x0250e620); - cx25840_write4(client, 0x39c, 0x01FF0B00); - - cx25840_write4(client, 0x410, 0xffff0dbf); - cx25840_write4(client, 0x414, 0x00137d03); - - /* on the 887, 0x418 is HSCALE_CTRL, on the 888 it is - CHROMA_CTRL */ - if (is_cx23888(state)) - cx25840_write4(client, 0x418, 0x01008080); - else - cx25840_write4(client, 0x418, 0x01000000); - - cx25840_write4(client, 0x41c, 0x00000000); - - /* on the 887, 0x420 is CHROMA_CTRL, on the 888 it is - CRUSH_CTRL */ - if (is_cx23888(state)) - cx25840_write4(client, 0x420, 0x001c3e0f); - else - cx25840_write4(client, 0x420, 0x001c8282); - - cx25840_write4(client, 0x42c, 0x42600000); - cx25840_write4(client, 0x430, 0x0000039b); - cx25840_write4(client, 0x438, 0x00000000); - - cx25840_write4(client, 0x440, 0xF8E3E824); - cx25840_write4(client, 0x444, 0x401040dc); - cx25840_write4(client, 0x448, 0xcd3f02a0); - cx25840_write4(client, 0x44c, 0x161f1000); - cx25840_write4(client, 0x450, 0x00000802); - - cx25840_write4(client, 0x91c, 0x01000000); - cx25840_write4(client, 0x8e0, 0x03063870); - cx25840_write4(client, 0x8d4, 0x7FFF0024); - cx25840_write4(client, 0x8d0, 0x00063073); - - cx25840_write4(client, 0x8c8, 0x00010000); - cx25840_write4(client, 0x8cc, 0x00080023); - - /* DIF BYPASS */ - cx25840_write4(client, 0x33c, 0x2a04c800); - } - - /* Reset the DIF */ - cx25840_write4(client, 0x398, 0); - } - - if (!is_cx2388x(state) && !is_cx231xx(state)) { - /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ - cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); - /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ - if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) - cx25840_and_or(client, 0x102, ~0x4, 4); - else - cx25840_and_or(client, 0x102, ~0x4, 0); - } else { - /* Set DUAL_MODE_ADC2 to 1 if component*/ - cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); - if (is_composite) { - /* ADC2 input select channel 2 */ - cx25840_and_or(client, 0x102, ~0x2, 0); - } else if (!is_component) { - /* S-Video */ - if (chroma >= CX25840_SVIDEO_CHROMA7) { - /* ADC2 input select channel 3 */ - cx25840_and_or(client, 0x102, ~0x2, 2); - } else { - /* ADC2 input select channel 2 */ - cx25840_and_or(client, 0x102, ~0x2, 0); - } - } - - /* cx23885 / SVIDEO */ - if (is_cx2388x(state) && is_svideo) { -#define AFE_CTRL (0x104) -#define MODE_CTRL (0x400) - cx25840_and_or(client, 0x102, ~0x2, 0x2); - - val = cx25840_read4(client, MODE_CTRL); - val &= 0xFFFFF9FF; - - /* YC */ - val |= 0x00000200; - val &= ~0x2000; - cx25840_write4(client, MODE_CTRL, val); - - val = cx25840_read4(client, AFE_CTRL); - - /* Chroma in select */ - val |= 0x00001000; - val &= 0xfffffe7f; - /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8). - * This sets them to use video rather than audio. - * Only one of the two will be in use. - */ - cx25840_write4(client, AFE_CTRL, val); - } else - cx25840_and_or(client, 0x102, ~0x2, 0); - } - - state->vid_input = vid_input; - state->aud_input = aud_input; - cx25840_audio_set_path(client); - input_change(client); - - if (is_cx2388x(state)) { - /* Audio channel 1 src : Parallel 1 */ - cx25840_write(client, 0x124, 0x03); - - /* Select AFE clock pad output source */ - cx25840_write(client, 0x144, 0x05); - - /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ - cx25840_write(client, 0x914, 0xa0); - - /* I2S_OUT_CTL: - * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 - * I2S_OUT_MASTER_MODE = Master - */ - cx25840_write(client, 0x918, 0xa0); - cx25840_write(client, 0x919, 0x01); - } else if (is_cx231xx(state)) { - /* Audio channel 1 src : Parallel 1 */ - cx25840_write(client, 0x124, 0x03); - - /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ - cx25840_write(client, 0x914, 0xa0); - - /* I2S_OUT_CTL: - * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 - * I2S_OUT_MASTER_MODE = Master - */ - cx25840_write(client, 0x918, 0xa0); - cx25840_write(client, 0x919, 0x01); - } - - if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) || - (aud_input == CX25840_AUDIO6))) { - /* Configure audio from LR1 or LR2 input */ - cx25840_write4(client, 0x910, 0); - cx25840_write4(client, 0x8d0, 0x63073); - } else - if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { - /* Configure audio from tuner/sif input */ - cx25840_write4(client, 0x910, 0x12b000c9); - cx25840_write4(client, 0x8d0, 0x1f063870); - } - - if (is_cx23888(state)) { - /* HVR1850 */ - /* AUD_IO_CTRL - I2S Input, Parallel1*/ - /* - Channel 1 src - Parallel1 (Merlin out) */ - /* - Channel 2 src - Parallel2 (Merlin out) */ - /* - Channel 3 src - Parallel3 (Merlin AC97 out) */ - /* - I2S source and dir - Merlin, output */ - cx25840_write4(client, 0x124, 0x100); - - if (!is_dif) { - /* Stop microcontroller if we don't need it - * to avoid audio popping on svideo/composite use. - */ - cx25840_and_or(client, 0x803, ~0x10, 0x00); - } - } - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int set_v4lstd(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 fmt = 0; /* zero is autodetect */ - u8 pal_m = 0; - - /* First tests should be against specific std */ - if (state->std == V4L2_STD_NTSC_M_JP) { - fmt = 0x2; - } else if (state->std == V4L2_STD_NTSC_443) { - fmt = 0x3; - } else if (state->std == V4L2_STD_PAL_M) { - pal_m = 1; - fmt = 0x5; - } else if (state->std == V4L2_STD_PAL_N) { - fmt = 0x6; - } else if (state->std == V4L2_STD_PAL_Nc) { - fmt = 0x7; - } else if (state->std == V4L2_STD_PAL_60) { - fmt = 0x8; - } else { - /* Then, test against generic ones */ - if (state->std & V4L2_STD_NTSC) - fmt = 0x1; - else if (state->std & V4L2_STD_PAL) - fmt = 0x4; - else if (state->std & V4L2_STD_SECAM) - fmt = 0xc; - } - - v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); - - /* Follow step 9 of section 3.16 in the cx25840 datasheet. - Without this PAL may display a vertical ghosting effect. - This happens for example with the Yuan MPC622. */ - if (fmt >= 4 && fmt < 8) { - /* Set format to NTSC-M */ - cx25840_and_or(client, 0x400, ~0xf, 1); - /* Turn off LCOMB */ - cx25840_and_or(client, 0x47b, ~6, 0); - } - cx25840_and_or(client, 0x400, ~0xf, fmt); - cx25840_and_or(client, 0x403, ~0x3, pal_m); - if (is_cx23888(state)) - cx23888_std_setup(client); - else - cx25840_std_setup(client); - if (!is_cx2583x(state)) - input_change(client); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - cx25840_write(client, 0x414, ctrl->val - 128); - break; - - case V4L2_CID_CONTRAST: - cx25840_write(client, 0x415, ctrl->val << 1); - break; - - case V4L2_CID_SATURATION: - if (is_cx23888(state)) { - cx25840_write(client, 0x418, ctrl->val << 1); - cx25840_write(client, 0x419, ctrl->val << 1); - } else { - cx25840_write(client, 0x420, ctrl->val << 1); - cx25840_write(client, 0x421, ctrl->val << 1); - } - break; - - case V4L2_CID_HUE: - if (is_cx23888(state)) - cx25840_write(client, 0x41a, ctrl->val); - else - cx25840_write(client, 0x422, ctrl->val); - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int HSC, VSC, Vsrc, Hsrc, filter, Vlines; - int is_50Hz = !(state->std & V4L2_STD_525_60); - - if (fmt->code != V4L2_MBUS_FMT_FIXED) - return -EINVAL; - - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - - if (is_cx23888(state)) { - Vsrc = (cx25840_read(client, 0x42a) & 0x3f) << 4; - Vsrc |= (cx25840_read(client, 0x429) & 0xf0) >> 4; - } else { - Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; - Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; - } - - if (is_cx23888(state)) { - Hsrc = (cx25840_read(client, 0x426) & 0x3f) << 4; - Hsrc |= (cx25840_read(client, 0x425) & 0xf0) >> 4; - } else { - Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; - Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; - } - - Vlines = fmt->height + (is_50Hz ? 4 : 7); - - if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || - (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { - v4l_err(client, "%dx%d is not a valid size!\n", - fmt->width, fmt->height); - return -ERANGE; - } - - HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); - VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); - VSC &= 0x1fff; - - if (fmt->width >= 385) - filter = 0; - else if (fmt->width > 192) - filter = 1; - else if (fmt->width > 96) - filter = 2; - else - filter = 3; - - v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", - fmt->width, fmt->height, HSC, VSC); - - /* HSCALE=HSC */ - cx25840_write(client, 0x418, HSC & 0xff); - cx25840_write(client, 0x419, (HSC >> 8) & 0xff); - cx25840_write(client, 0x41a, HSC >> 16); - /* VSCALE=VSC */ - cx25840_write(client, 0x41c, VSC & 0xff); - cx25840_write(client, 0x41d, VSC >> 8); - /* VS_INTRLACE=1 VFILT=filter */ - cx25840_write(client, 0x41e, 0x8 | filter); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static void log_video_status(struct i2c_client *client) -{ - static const char *const fmt_strs[] = { - "0x0", - "NTSC-M", "NTSC-J", "NTSC-4.43", - "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", - "0x9", "0xA", "0xB", - "SECAM", - "0xD", "0xE", "0xF" - }; - - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; - u8 gen_stat1 = cx25840_read(client, 0x40d); - u8 gen_stat2 = cx25840_read(client, 0x40e); - int vid_input = state->vid_input; - - v4l_info(client, "Video signal: %spresent\n", - (gen_stat2 & 0x20) ? "" : "not "); - v4l_info(client, "Detected format: %s\n", - fmt_strs[gen_stat1 & 0xf]); - - v4l_info(client, "Specified standard: %s\n", - vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); - - if (vid_input >= CX25840_COMPOSITE1 && - vid_input <= CX25840_COMPOSITE8) { - v4l_info(client, "Specified video input: Composite %d\n", - vid_input - CX25840_COMPOSITE1 + 1); - } else { - v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", - (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); - } - - v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); -} - -/* ----------------------------------------------------------------------- */ - -static void log_audio_status(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - u8 download_ctl = cx25840_read(client, 0x803); - u8 mod_det_stat0 = cx25840_read(client, 0x804); - u8 mod_det_stat1 = cx25840_read(client, 0x805); - u8 audio_config = cx25840_read(client, 0x808); - u8 pref_mode = cx25840_read(client, 0x809); - u8 afc0 = cx25840_read(client, 0x80b); - u8 mute_ctl = cx25840_read(client, 0x8d3); - int aud_input = state->aud_input; - char *p; - - switch (mod_det_stat0) { - case 0x00: p = "mono"; break; - case 0x01: p = "stereo"; break; - case 0x02: p = "dual"; break; - case 0x04: p = "tri"; break; - case 0x10: p = "mono with SAP"; break; - case 0x11: p = "stereo with SAP"; break; - case 0x12: p = "dual with SAP"; break; - case 0x14: p = "tri with SAP"; break; - case 0xfe: p = "forced mode"; break; - default: p = "not defined"; - } - v4l_info(client, "Detected audio mode: %s\n", p); - - switch (mod_det_stat1) { - case 0x00: p = "not defined"; break; - case 0x01: p = "EIAJ"; break; - case 0x02: p = "A2-M"; break; - case 0x03: p = "A2-BG"; break; - case 0x04: p = "A2-DK1"; break; - case 0x05: p = "A2-DK2"; break; - case 0x06: p = "A2-DK3"; break; - case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; - case 0x08: p = "AM-L"; break; - case 0x09: p = "NICAM-BG"; break; - case 0x0a: p = "NICAM-DK"; break; - case 0x0b: p = "NICAM-I"; break; - case 0x0c: p = "NICAM-L"; break; - case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; - case 0x0e: p = "IF FM Radio"; break; - case 0x0f: p = "BTSC"; break; - case 0x10: p = "high-deviation FM"; break; - case 0x11: p = "very high-deviation FM"; break; - case 0xfd: p = "unknown audio standard"; break; - case 0xfe: p = "forced audio standard"; break; - case 0xff: p = "no detected audio standard"; break; - default: p = "not defined"; - } - v4l_info(client, "Detected audio standard: %s\n", p); - v4l_info(client, "Audio microcontroller: %s\n", - (download_ctl & 0x10) ? - ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); - - switch (audio_config >> 4) { - case 0x00: p = "undefined"; break; - case 0x01: p = "BTSC"; break; - case 0x02: p = "EIAJ"; break; - case 0x03: p = "A2-M"; break; - case 0x04: p = "A2-BG"; break; - case 0x05: p = "A2-DK1"; break; - case 0x06: p = "A2-DK2"; break; - case 0x07: p = "A2-DK3"; break; - case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; - case 0x09: p = "AM-L"; break; - case 0x0a: p = "NICAM-BG"; break; - case 0x0b: p = "NICAM-DK"; break; - case 0x0c: p = "NICAM-I"; break; - case 0x0d: p = "NICAM-L"; break; - case 0x0e: p = "FM radio"; break; - case 0x0f: p = "automatic detection"; break; - default: p = "undefined"; - } - v4l_info(client, "Configured audio standard: %s\n", p); - - if ((audio_config >> 4) < 0xF) { - switch (audio_config & 0xF) { - case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; - case 0x01: p = "MONO2 (LANGUAGE B)"; break; - case 0x02: p = "MONO3 (STEREO forced MONO)"; break; - case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; - case 0x04: p = "STEREO"; break; - case 0x05: p = "DUAL1 (AB)"; break; - case 0x06: p = "DUAL2 (AC) (FM)"; break; - case 0x07: p = "DUAL3 (BC) (FM)"; break; - case 0x08: p = "DUAL4 (AC) (AM)"; break; - case 0x09: p = "DUAL5 (BC) (AM)"; break; - case 0x0a: p = "SAP"; break; - default: p = "undefined"; - } - v4l_info(client, "Configured audio mode: %s\n", p); - } else { - switch (audio_config & 0xF) { - case 0x00: p = "BG"; break; - case 0x01: p = "DK1"; break; - case 0x02: p = "DK2"; break; - case 0x03: p = "DK3"; break; - case 0x04: p = "I"; break; - case 0x05: p = "L"; break; - case 0x06: p = "BTSC"; break; - case 0x07: p = "EIAJ"; break; - case 0x08: p = "A2-M"; break; - case 0x09: p = "FM Radio"; break; - case 0x0f: p = "automatic standard and mode detection"; break; - default: p = "undefined"; - } - v4l_info(client, "Configured audio system: %s\n", p); - } - - if (aud_input) { - v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); - } else { - v4l_info(client, "Specified audio input: External\n"); - } - - switch (pref_mode & 0xf) { - case 0: p = "mono/language A"; break; - case 1: p = "language B"; break; - case 2: p = "language C"; break; - case 3: p = "analog fallback"; break; - case 4: p = "stereo"; break; - case 5: p = "language AC"; break; - case 6: p = "language BC"; break; - case 7: p = "language AB"; break; - default: p = "undefined"; - } - v4l_info(client, "Preferred audio mode: %s\n", p); - - if ((audio_config & 0xf) == 0xf) { - switch ((afc0 >> 3) & 0x3) { - case 0: p = "system DK"; break; - case 1: p = "system L"; break; - case 2: p = "autodetect"; break; - default: p = "undefined"; - } - v4l_info(client, "Selected 65 MHz format: %s\n", p); - - switch (afc0 & 0x7) { - case 0: p = "chroma"; break; - case 1: p = "BTSC"; break; - case 2: p = "EIAJ"; break; - case 3: p = "A2-M"; break; - case 4: p = "autodetect"; break; - default: p = "undefined"; - } - v4l_info(client, "Selected 45 MHz format: %s\n", p); - } -} - -/* ----------------------------------------------------------------------- */ - -/* This load_fw operation must be called to load the driver's firmware. - Without this the audio standard detection will fail and you will - only get mono. - - Since loading the firmware is often problematic when the driver is - compiled into the kernel I recommend postponing calling this function - until the first open of the video device. Another reason for - postponing it is that loading this firmware takes a long time (seconds) - due to the slow i2c bus speed. So it will speed up the boot process if - you can avoid loading the fw as long as the video device isn't used. */ -static int cx25840_load_fw(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!state->is_initialized) { - /* initialize and load firmware */ - state->is_initialized = 1; - if (is_cx2583x(state)) - cx25836_initialize(client); - else if (is_cx2388x(state)) - cx23885_initialize(client); - else if (is_cx231xx(state)) - cx231xx_initialize(client); - else - cx25840_initialize(client); - } - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->size = 1; - reg->val = cx25840_read(client, reg->reg & 0x0fff); - return 0; -} - -static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); - return 0; -} -#endif - -static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 v; - - if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state)) - return 0; - - v4l_dbg(1, cx25840_debug, client, "%s audio output\n", - enable ? "enable" : "disable"); - - if (enable) { - v = cx25840_read(client, 0x115) | 0x80; - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) | 0x03; - cx25840_write(client, 0x116, v); - } else { - v = cx25840_read(client, 0x115) & ~(0x80); - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) & ~(0x03); - cx25840_write(client, 0x116, v); - } - return 0; -} - -static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 v; - - v4l_dbg(1, cx25840_debug, client, "%s video output\n", - enable ? "enable" : "disable"); - if (enable) { - if (is_cx2388x(state) || is_cx231xx(state)) { - v = cx25840_read(client, 0x421) | 0x0b; - cx25840_write(client, 0x421, v); - } else { - v = cx25840_read(client, 0x115) | 0x0c; - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) | 0x04; - cx25840_write(client, 0x116, v); - } - } else { - if (is_cx2388x(state) || is_cx231xx(state)) { - v = cx25840_read(client, 0x421) & ~(0x0b); - cx25840_write(client, 0x421, v); - } else { - v = cx25840_read(client, 0x115) & ~(0x0c); - cx25840_write(client, 0x115, v); - v = cx25840_read(client, 0x116) & ~(0x04); - cx25840_write(client, 0x116, v); - } - } - return 0; -} - -/* Query the current detected video format */ -static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - v4l2_std_id stds[] = { - /* 0000 */ V4L2_STD_UNKNOWN, - - /* 0001 */ V4L2_STD_NTSC_M, - /* 0010 */ V4L2_STD_NTSC_M_JP, - /* 0011 */ V4L2_STD_NTSC_443, - /* 0100 */ V4L2_STD_PAL, - /* 0101 */ V4L2_STD_PAL_M, - /* 0110 */ V4L2_STD_PAL_N, - /* 0111 */ V4L2_STD_PAL_Nc, - /* 1000 */ V4L2_STD_PAL_60, - - /* 1001 */ V4L2_STD_UNKNOWN, - /* 1010 */ V4L2_STD_UNKNOWN, - /* 1001 */ V4L2_STD_UNKNOWN, - /* 1010 */ V4L2_STD_UNKNOWN, - /* 1011 */ V4L2_STD_UNKNOWN, - /* 1110 */ V4L2_STD_UNKNOWN, - /* 1111 */ V4L2_STD_UNKNOWN - }; - - u32 fmt = (cx25840_read4(client, 0x40c) >> 8) & 0xf; - *std = stds[ fmt ]; - - v4l_dbg(1, cx25840_debug, client, "g_std fmt = %x, v4l2_std_id = 0x%x\n", - fmt, (unsigned int)stds[ fmt ]); - - return 0; -} - -static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* A limited function that checks for signal status and returns - * the state. - */ - - /* Check for status of Horizontal lock (SRC lock isn't reliable) */ - if ((cx25840_read4(client, 0x40c) & 0x00010000) == 0) - *status |= V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio == 0 && state->std == std) - return 0; - state->radio = 0; - state->std = std; - return set_v4lstd(client); -} - -static int cx25840_s_radio(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - - state->radio = 1; - return 0; -} - -static int cx25840_s_video_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (is_cx23888(state)) - cx23888_std_setup(client); - - return set_input(client, input, state->aud_input); -} - -static int cx25840_s_audio_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (is_cx23888(state)) - cx23888_std_setup(client); - return set_input(client, state->vid_input, input); -} - -static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - input_change(client); - return 0; -} - -static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 vpres = cx25840_read(client, 0x40e) & 0x20; - u8 mode; - int val = 0; - - if (state->radio) - return 0; - - vt->signal = vpres ? 0xffff : 0x0; - if (is_cx2583x(state)) - return 0; - - vt->capability |= - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - - mode = cx25840_read(client, 0x804); - - /* get rxsubchans and audmode */ - if ((mode & 0xf) == 1) - val |= V4L2_TUNER_SUB_STEREO; - else - val |= V4L2_TUNER_SUB_MONO; - - if (mode == 2 || mode == 4) - val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - - if (mode & 0x10) - val |= V4L2_TUNER_SUB_SAP; - - vt->rxsubchans = val; - vt->audmode = state->audmode; - return 0; -} - -static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio || is_cx2583x(state)) - return 0; - - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - /* mono -> mono - stereo -> mono - bilingual -> lang1 */ - cx25840_and_or(client, 0x809, ~0xf, 0x00); - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - /* mono -> mono - stereo -> stereo - bilingual -> lang1 */ - cx25840_and_or(client, 0x809, ~0xf, 0x04); - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang1/lang2 */ - cx25840_and_or(client, 0x809, ~0xf, 0x07); - break; - case V4L2_TUNER_MODE_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang2 */ - cx25840_and_or(client, 0x809, ~0xf, 0x01); - break; - default: - return -EINVAL; - } - state->audmode = vt->audmode; - return 0; -} - -static int cx25840_reset(struct v4l2_subdev *sd, u32 val) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (is_cx2583x(state)) - cx25836_initialize(client); - else if (is_cx2388x(state)) - cx23885_initialize(client); - else if (is_cx231xx(state)) - cx231xx_initialize(client); - else - cx25840_initialize(client); - return 0; -} - -static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); -} - -static int cx25840_log_status(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - log_video_status(client); - if (!is_cx2583x(state)) - log_audio_status(client); - cx25840_ir_log_status(sd); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status, - bool *handled) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *c = v4l2_get_subdevdata(sd); - u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en; - u32 vid_stat, aud_mc_stat; - bool block_handled; - int ret = 0; - - irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); - v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n", - irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); - - if ((is_cx23885(state) || is_cx23887(state))) { - ir_stat = cx25840_read(c, CX25840_IR_STATS_REG); - ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core ir IRQ status: %#04x disables: %#04x\n", - ir_stat, ir_en); - if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) { - block_handled = false; - ret = cx25840_ir_irq_handler(sd, - status, &block_handled); - if (block_handled) - *handled = true; - } - } - - aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG); - aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core audio IRQ status: %#04x disables: %#04x\n", - aud_stat, aud_en); - aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core audio MC IRQ status: %#06x enables: %#06x\n", - aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT, - aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS); - if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) { - if (aud_stat) { - cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat); - *handled = true; - } - } - - vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG); - v4l_dbg(2, cx25840_debug, c, - "AV Core video IRQ status: %#06x disables: %#06x\n", - vid_stat & CX25840_VID_INT_STAT_BITS, - vid_stat >> CX25840_VID_INT_MASK_SHFT); - if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) { - if (vid_stat & CX25840_VID_INT_STAT_BITS) { - cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat); - *handled = true; - } - } - - irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); - v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n", - irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", - irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); - - return ret; -} - -static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status, - bool *handled) -{ - struct cx25840_state *state = to_state(sd); - - *handled = false; - - /* Only support the CX2388[578] AV Core for now */ - if (is_cx2388x(state)) - return cx23885_irq_handler(sd, status, handled); - - return -ENODEV; -} - -/* ----------------------------------------------------------------------- */ - -#define DIF_PLL_FREQ_WORD (0x300) -#define DIF_BPF_COEFF01 (0x348) -#define DIF_BPF_COEFF23 (0x34c) -#define DIF_BPF_COEFF45 (0x350) -#define DIF_BPF_COEFF67 (0x354) -#define DIF_BPF_COEFF89 (0x358) -#define DIF_BPF_COEFF1011 (0x35c) -#define DIF_BPF_COEFF1213 (0x360) -#define DIF_BPF_COEFF1415 (0x364) -#define DIF_BPF_COEFF1617 (0x368) -#define DIF_BPF_COEFF1819 (0x36c) -#define DIF_BPF_COEFF2021 (0x370) -#define DIF_BPF_COEFF2223 (0x374) -#define DIF_BPF_COEFF2425 (0x378) -#define DIF_BPF_COEFF2627 (0x37c) -#define DIF_BPF_COEFF2829 (0x380) -#define DIF_BPF_COEFF3031 (0x384) -#define DIF_BPF_COEFF3233 (0x388) -#define DIF_BPF_COEFF3435 (0x38c) -#define DIF_BPF_COEFF36 (0x390) - -void cx23885_dif_setup(struct i2c_client *client, u32 ifHz) -{ - u64 pll_freq; - u32 pll_freq_word; - - v4l_dbg(1, cx25840_debug, client, "%s(%d)\n", __func__, ifHz); - - /* Assuming TV */ - /* Calculate the PLL frequency word based on the adjusted ifHz */ - pll_freq = div_u64((u64)ifHz * 268435456, 50000000); - pll_freq_word = (u32)pll_freq; - - cx25840_write4(client, DIF_PLL_FREQ_WORD, pll_freq_word); - - /* Round down to the nearest 100KHz */ - ifHz = (ifHz / 100000) * 100000; - - if (ifHz < 3000000) - ifHz = 3000000; - - if (ifHz > 16000000) - ifHz = 16000000; - - v4l_dbg(1, cx25840_debug, client, "%s(%d) again\n", __func__, ifHz); - - switch (ifHz) { - case 3000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0024); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001bfff8); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff50); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed8fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe34); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfebaffc7); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d031f); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04f0065d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07010688); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04c901d6); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f9d3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f342); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f337); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf64efb22); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105070f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0c460fce); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00220032); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00370026); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff91); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0efe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fdcc); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe0afedb); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440224); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0434060c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738074e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06090361); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99fb39); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef3b6); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21af2a5); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf573fa33); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034067d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bfb0fb9); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00200038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c004f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002fffdf); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff5cfeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dfd92); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7ffe03); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36010a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03410575); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x072607d2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x071804d5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134fcb7); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff451); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223f22e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4a7f94b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xff6405e8); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bae0fa4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00000008); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0036); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0056006d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00670030); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffbdff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46fd8d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd25fd4f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35ffe0); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0224049f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9080e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x07ef0627); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9fe45); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f513); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250f1d2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3ecf869); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930552); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b5f0f8f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0001); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000f002c); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0054007d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0093007c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0024ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6fdbb); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd03fcca); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51feb9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00eb0392); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270802); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08880750); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x044dffdb); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf5f8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0f193); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf342f78f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc404b9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b0e0f78); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff9); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0002001b); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0046007d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad00ba); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00870000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe1a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1bfc7e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fda4); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xffa5025c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x054507ad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08dd0847); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80172); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef6ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313f170); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2abf6bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6041f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0abc0f61); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff50006); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002f006c); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b200e3); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00dc007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9fea0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd6bfc71); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fcb1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe65010b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x042d0713); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ec0906); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020302); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff823); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7f16a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf228f5f5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2a0384); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a670f4a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9fff1); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010004d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a100f2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011a00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053ff44); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdedfca2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fbef); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd39ffae); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x02ea0638); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08b50987); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230483); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f960); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45bf180); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1b8f537); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb6102e7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a110f32); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffdd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00024); - cx25840_write4(client, DIF_BPF_COEFF89, 0x007c00e5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013a014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e6fff8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe98fd0f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fb67); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc32fe54); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x01880525); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x083909c7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x091505ee); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7fab3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52df1b4); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf15df484); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9b0249); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x09ba0f19); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 3900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffcf); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1fff6); - cx25840_write4(client, DIF_BPF_COEFF89, 0x004800be); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01390184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x016300ac); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff5efdb1); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb23); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb5cfd0d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x001703e4); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x077b09c4); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d2073c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251fc18); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61cf203); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf118f3dc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d801aa); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x09600eff); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffefff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffca); - cx25840_write4(client, DIF_BPF_COEFF89, 0x000b0082); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01170198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10152); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0030fe7b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb24); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfac3fbe9); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0683097f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a560867); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2fd89); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723f26f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0e8f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919010a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x09060ee5); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8ffca); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffa4); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffcd0036); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d70184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f601dc); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ffff60); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb6d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6efaf5); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd410103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x055708f9); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e0969); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543ff02); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842f2f5); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cef2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85e006b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x08aa0ecb); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050003); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff3ffd3); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaff8b); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ffe5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0080014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe023f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ba0050); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fbf8); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa62fa3b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9ff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04010836); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa90a3d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f007f); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf975f395); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cbf231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ffcb); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x084c0eaf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000a); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0000ffe4); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff81); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff96); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x001c00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70271); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0254013b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fcbd); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa9ff9c5); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbfdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x028c073b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a750adf); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e101fa); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab8f44e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0ddf1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ff2b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x07ed0e94); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000efff8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff87); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff54); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffb5007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01860270); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c00210); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fdb2); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb22f997); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2fc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0102060f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a050b4c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902036e); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0af51e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf106f15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64efe8b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x078d0e77); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0019000e); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff9e); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff25); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff560000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112023b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f702c0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfec8); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbe5f9b3); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xff7004b9); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a0b81); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a0004d8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd65f603); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf144f104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aafdec); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x072b0e5a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00200022); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ffc1); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ff10); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff09ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x008601d7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f50340); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fff0); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcddfa19); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfde30343); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08790b7f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50631); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec7f6fc); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf198f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50dfd4e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x06c90e3d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00220030); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffed); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff87ff15); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed6ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffed014c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b90386); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110119); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfdfefac4); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6f92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc6701b7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07670b44); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0776); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x002df807); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf200f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477fcb1); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x06650e1e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0009); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f001b); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffbcff36); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec2feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff5600a5); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0248038d); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00232); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff39fbab); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4f87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb060020); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x062a0ad2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf908a3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0192f922); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf27df05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8fc14); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x06000e00); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 4900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0002); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00160037); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00510046); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff9ff6d); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed0fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefff0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01aa0356); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413032b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x007ffcc5); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cf812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9cefe87); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c90a2c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4309b4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f3fa4a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf30ef046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361fb7a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x059b0de0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000a002d); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00570067); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0037ffb5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfefffe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62ff3d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ec02e3); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x043503f6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x01befe05); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa27f7ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8c6fcf8); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x034c0954); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0aa4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x044cfb7e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3b1f03f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2fae1); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x05340dc0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffd001e); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0051007b); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006e0006); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff48fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfe9a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x001d023e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130488); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02e6ff5b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1ef812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7f7fb7f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bc084e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430b72); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x059afcba); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf467f046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cfa4a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x04cd0da0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff00009); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00980056); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffa5feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe15); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff4b0170); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b004d7); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03e800b9); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc48f87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf768fa23); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022071f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90c1b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x06dafdfd); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf52df05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef9b5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x04640d7f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6fff3); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00250072); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00af009c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x000cff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13fdb8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe870089); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104e1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04b8020f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd98f92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf71df8f0); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe8805ce); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0c9c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0808ff44); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf603f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af922); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x03fb0d5e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe0); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00050056); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b000d1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0071ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53fd8c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfddfff99); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104a3); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x054a034d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff01fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf717f7ed); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf50461); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50cf4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0921008d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf6e7f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff891); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x03920d3b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffffff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffd1); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5002f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009c00ed); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00cb0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfebafd94); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd61feb0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d0422); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05970464); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0074fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf759f721); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb7502de); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000d21); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a2201d4); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7d9f104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf804); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x03280d19); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fffa); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3ffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc90002); - cx25840_write4(client, DIF_BPF_COEFF89, 0x007500ef); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x010e007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3dfdcf); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd16fddd); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440365); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x059b0548); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3fc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7dff691); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0f014d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020d23); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0318); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8d7f15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f779); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x02bd0cf6); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060001); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffecffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffd4); - cx25840_write4(client, DIF_BPF_COEFF89, 0x004000d5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013600f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd3fe39); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd04fd31); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff360277); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x055605ef); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x033efdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8a5f642); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbffb6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10cfb); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50456); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9dff1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f6f2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x02520cd2); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080009); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff8ffd2); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffac); - cx25840_write4(client, DIF_BPF_COEFF89, 0x000200a3); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013c014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x006dfec9); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2bfcb7); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe350165); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04cb0651); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477ff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9a5f635); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1fe20); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0ca8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c81058b); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfaf0f231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f66d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x01e60cae); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 5900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0005ffe1); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacff90); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5005f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01210184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fcff72); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd8afc77); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51003f); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04020669); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfad7f66b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8fc93); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430c2b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d06b5); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfc08f2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af5ec); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x017b0c89); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0012fff5); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff8e000f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e80198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750028); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe18fc75); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99ff15); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03050636); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0656027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc32f6e2); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614fb17); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20b87); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d7707d2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfd26f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf56f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x010f0c64); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001c000b); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff84); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ffbe); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00960184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd00da); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeccfcb2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fdf9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x01e005bc); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e703e4); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfdabf798); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f9b3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510abd); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf08df); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfe48f3dc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f4f6); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x00a20c3e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021001f); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff97); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff74); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0034014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa0179); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff97fd2a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fcfa); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00a304fe); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310525); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xff37f886); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf86e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c709d0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de209db); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xff6df484); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf481); - cx25840_write4(client, DIF_BPF_COEFF3435, 0x00360c18); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe000a); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021002f); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ffb8); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff3b); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffcc00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa01fa); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0069fdd4); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fc26); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff5d0407); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310638); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x00c9f9a8); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf74e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff3908c3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de20ac3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0093f537); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf410); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xffca0bf2); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb0003); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001c0037); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002fffe2); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ff17); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff6a007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd0251); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0134fea5); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb8b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe2002e0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e70713); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0255faf5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f658); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0799); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf0b96); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x01b8f5f5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f3a3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xff5e0bca); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00120037); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00460010); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff8eff0f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff180000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750276); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01e8ff8d); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb31); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcfb0198); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x065607ad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x03cefc64); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614f592); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0656); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d770c52); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x02daf6bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf33b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfef10ba3); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff5); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0005002f); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0054003c); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5ff22); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedfff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fc0267); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0276007e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb1c); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbfe003e); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830802); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0529fdec); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8f4fe); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd04ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d0cf6); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x03f8f78f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af2d7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe850b7b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff80020); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00560060); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0002ff4e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec4ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x006d0225); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02d50166); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb4e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb35fee1); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477080e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x065bff82); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1f4a0); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610397); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c810d80); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0510f869); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f278); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe1a0b52); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffaffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffec000c); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0078); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0040ff8e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecafeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd301b6); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fc0235); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fbc5); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaaafd90); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x033e07d2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x075b011b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbf47a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f0224); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50def); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0621f94b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f21e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfdae0b29); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 6900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3fff6); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0037007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0075ffdc); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef2fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3d0122); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02ea02dd); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fc79); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa65fc5d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3074e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x082102ad); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0ff48c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe00a9); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0e43); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0729fa33); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f1c9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfd430b00); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0001fff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe2); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001b0076); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009c002d); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff35fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfeba0076); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x029f0352); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfd60); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa69fb53); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x00740688); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08a7042d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb75f4d6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600ff2d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a220e7a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0827fb22); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf17a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfcd80ad6); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff9); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffd2); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb005e); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b0007a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8ffe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53ffc1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0221038c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fe6e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfab6fa80); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff010587); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e90590); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf5f556); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfdb3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x09210e95); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0919fc15); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff12f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc6e0aab); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070000); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6ffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0039); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00af00b8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfff4feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13ff10); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01790388); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311ff92); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb48f9ed); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd980453); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e306cd); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe88f60a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fc40); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x08080e93); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x09fdfd0c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af0ea); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc050a81); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080008); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff0ffc9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1000d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009800e2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x005bff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe74); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00b50345); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000bc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc18f9a1); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc4802f9); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x089807dc); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022f6f0); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fada); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x06da0e74); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ad3fe06); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef0ab); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb9c0a55); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffdffd0); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffdf); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006e00f2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00b8ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfdf8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffe302c8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x041301dc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd1af99e); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1e0183); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x080908b5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bcf801); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf985); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x059a0e38); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b99ff03); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cf071); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb330a2a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00070011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000affdf); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffa9ffb5); - cx25840_write4(client, DIF_BPF_COEFF89, 0x003700e6); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01010000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62fda8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff140219); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x043502e1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe42f9e6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa270000); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x073a0953); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x034cf939); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a4f845); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x044c0de1); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c4f0000); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2f03c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfacc09fe); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00040012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0016fff3); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffafff95); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff900c0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0130007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefd89); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe560146); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x041303bc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff81fa76); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cfe7d); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x063209b1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c9fa93); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf71e); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f30d6e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cf200fd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361f00e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa6509d1); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00010010); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0008); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ff84); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffbc0084); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013e00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff56fd9f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdb8005c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00460); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00c7fb45); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4fd07); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04fa09ce); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x062afc07); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407f614); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x01920ce0); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d8301fa); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8efe5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa0009a4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd000b); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001d); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff870039); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x012a014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffedfde7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd47ff6b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104c6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0202fc4c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6fbad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x039909a7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0767fd8e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482f52b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x002d0c39); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e0002f4); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477efc2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf99b0977); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 7900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa0004); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0020002d); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff91); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ffe8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f70184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0086fe5c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0bfe85); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104e5); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0323fd7d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa79); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x021d093f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0879ff22); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bf465); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec70b79); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e6803eb); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50defa5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf937094a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffd); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00190036); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001bffaf); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff99); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00aa0198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112fef3); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd09fdb9); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04be); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x041bfecc); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947f978); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x00900897); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a00b9); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f3c5); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd650aa3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ebc04de); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aaef8e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf8d5091c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff6); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000e0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ffd7); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff56); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x004b0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0186ffa1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd40fd16); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440452); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04de0029); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2f8b2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfefe07b5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a05024d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef34d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0a09b8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0efa05cd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64eef7d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf87308ed); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00000031); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0005); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff27); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffe4014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70057); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdacfca6); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3603a7); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05610184); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbf82e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd74069f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a7503d6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff2ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab808b9); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f2306b5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ef72); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf81308bf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff30022); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00560032); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ff10); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8000f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe0106); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe46fc71); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3502c7); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x059e02ce); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9f7f2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfbff055b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa9054c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f2db); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf97507aa); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f350797); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ef6d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7b40890); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8000f); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00540058); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffcdff14); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff29007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f6019e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff01fc7c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5101bf); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x059203f6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd41f7fe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfaa903f3); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e06a9); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf2e2); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842068b); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f320871); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85eef6e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7560860); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fff2); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1fff9); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00460073); - cx25840_write4(client, DIF_BPF_COEFF89, 0x000bff34); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee90000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10215); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffd0fcc5); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99009d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x053d04f1); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5f853); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf97d0270); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a5607e4); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef314); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723055f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f180943); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919ef75); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6fa0830); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff8); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe4); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002f007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0048ff6b); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec7ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0163025f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00a2fd47); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff73); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04a405b2); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0017f8ed); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf88500dc); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d208f9); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff370); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61c0429); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ee80a0b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d8ef82); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6a00800); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007ffff); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffd4); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010007a); - cx25840_write4(client, DIF_BPF_COEFF89, 0x007cffb2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec6ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e60277); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0168fdf9); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fe50); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x03ce0631); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0188f9c8); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7c7ff43); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x091509e3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f3f6); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52d02ea); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ea30ac9); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9bef95); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf64607d0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00090007); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9ffca); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00065); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a10003); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee6feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053025b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0213fed0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fd46); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02c70668); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x02eafadb); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf74bfdae); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230a9c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f4a3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45b01a6); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e480b7c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb61efae); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf5ef079f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 8900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000d); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff5ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10043); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b20053); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff24fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9020c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0295ffbb); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fc64); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x019b0654); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x042dfc1c); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf714fc2a); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020b21); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f575); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7005e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0dd80c24); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2aefcd); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf599076e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0002ffcf); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffba0018); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad009a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff79fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff260192); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e500ab); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fbb6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x005b05f7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545fd81); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf723fabf); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80b70); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f669); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313ff15); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d550cbf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6eff2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544073d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00030012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000fffdd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffea); - cx25840_write4(client, DIF_BPF_COEFF89, 0x009300cf); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdcfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea600f7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fd0190); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb46); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff150554); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0627fefd); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf778f978); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x044d0b87); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f77d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0fdcf); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbe0d4e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc4f01d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2070b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00000010); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001afff0); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffbf); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006700ed); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe460047); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02db0258); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb1b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddc0473); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c90082); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf811f85e); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c90b66); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff8ad); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250fc8d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c140dcf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe93f04d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a106d9); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc000c); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00200006); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff9c); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002f00ef); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a4ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dff92); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x028102f7); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb37); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbf035e); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07260202); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e8f778); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x01340b0d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f9f4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223fb51); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b590e42); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xff64f083); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf45206a7); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90005); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001a); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff86); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff000d7); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fee5); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f60362); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb99); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbcc0222); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07380370); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f7f6cc); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff990a7e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902fb50); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21afa1f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8d0ea6); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034f0bf); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4050675); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffe); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001e002b); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff81); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffb400a5); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe50); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01460390); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfc3a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb1000ce); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x070104bf); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb37f65f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe0009bc); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00fcbb); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f8f8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b20efc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105f101); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3ba0642); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff7); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00150036); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff8c); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff810061); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013d007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe71fddf); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x007c0380); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fd13); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa94ff70); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x068005e2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9bf633); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc7308ca); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5fe30); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf274f7e0); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c90f43); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d4f147); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371060f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fff1); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00090038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffa7); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5e0012); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013200f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee3fd9b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaa0331); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fe15); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa60fe18); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bd06d1); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1bf64a); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafa07ae); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7effab); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d5f6d7); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d30f7a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a3f194); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf32905dc); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffb0032); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003fffcd); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effc1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6efd8a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfedd02aa); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0ff34); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa74fcd7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bf0781); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaaf6a3); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99e066b); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90128); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf359f5e1); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d20fa2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0370f1e5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e405a8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 9900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffef0024); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0051fffa); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff54ff77); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00be0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0006fdad); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe2701f3); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413005e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad1fbba); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x039007ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x013bf73d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868050a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4302a1); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fdf4fe); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c70fba); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x043bf23c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a10575); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fff1); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe50011); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00570027); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff70ff3c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00620198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x009efe01); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95011a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350183); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb71fad0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x023c0812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c3f811); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75e0390); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0411); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c1f432); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b30fc1); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0503f297); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2610541); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0006fff7); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdffffc); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00510050); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff9dff18); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffc0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0128fe80); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32002e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130292); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4dfa21); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d107ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0435f91c); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6850205); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430573); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a1f37d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x03990fba); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c7f2f8); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222050d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffe); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdfffe7); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f006e); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffd6ff0f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0197ff1f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd05ff3e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0037c); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd59f9b7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5d0781); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0585fa56); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4006f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf906c4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69df2e0); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x02790fa2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0688f35d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e604d8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00090005); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4ffd6); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025007e); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0014ff20); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3c00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e1ffd0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd12fe5c); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110433); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88f996); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf106d1); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aafbb7); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57efed8); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e07ff); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b0f25e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x01560f7a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0745f3c7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1ac04a4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000c); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffedffcb); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005007d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0050ff4c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ff0086); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd58fd97); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104ad); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xffcaf9c0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc9905e2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x079afd35); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf555fd46); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50920); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d9f1f6); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x00310f43); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fdf435); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174046f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050011); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffaffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5006b); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0082ff8c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f00130); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd2fcfc); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04e3); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x010efa32); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb6404bf); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x084efec5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569fbc2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000a23); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa15f1ab); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0b0efc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b0f4a7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13f043a); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00020012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0007ffcd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9004c); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a4ffd9); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b401c1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe76fc97); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404d2); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0245fae8); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5f0370); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1005f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bcfa52); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020b04); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb60f17b); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde70ea6); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x095df51e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10c0405); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0014ffdb); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40023); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2002a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedbff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150022d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff38fc6f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36047b); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x035efbda); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9940202); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ee01f5); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf649f8fe); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10bc2); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb6f169); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc60e42); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a04f599); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0db03d0); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb000d); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001dffed); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaafff5); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00aa0077); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00ce026b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x000afc85); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3503e3); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x044cfcfb); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90c0082); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5037f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf710f7cc); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0c59); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe16f173); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaa0dcf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa5f617); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0ad039b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 10900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90006); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00210003); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffc8); - cx25840_write4(client, DIF_BPF_COEFF89, 0x008e00b6); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff63fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x003a0275); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00dafcda); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd510313); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0501fe40); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cbfefd); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x087604f0); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80af6c2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430cc8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7af19a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa940d4e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3ff699); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0810365); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffff); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00210018); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffa3); - cx25840_write4(client, DIF_BPF_COEFF89, 0x006000e1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc4fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0024b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x019afd66); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc990216); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0575ff99); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4fd81); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d40640); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf932f5e6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20d0d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x00dff1de); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9860cbf); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd1f71e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058032f); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff8); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001b0029); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff8a); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002600f2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x002cfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0f01f0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x023bfe20); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc1700fa); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a200f7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf927fc1c); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f40765); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa82f53b); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510d27); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0243f23d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8810c24); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5cf7a7); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf03102fa); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00110035); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff81); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffe700e7); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x008ffeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94016d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b0fefb); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3ffd1); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05850249); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c1fadb); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x05de0858); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf2f4c4); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c70d17); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a0f2b8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7870b7c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdff833); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00d02c4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00040038); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ff88); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffac00c2); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e2ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe3900cb); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f1ffe9); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3feaa); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05210381); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9cf9c8); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x04990912); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7af484); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff390cdb); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f4f34d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69a0ac9); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5af8c1); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefec028e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0000ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff60033); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002fff9f); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7b0087); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011eff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe080018); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f900d8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fd96); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x04790490); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbadf8ed); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x032f098e); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff10f47d); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0c75); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x063cf3fc); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5ba0a0b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dccf952); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcd0258); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff1); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffea0026); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0046ffc3); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5a003c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04ff63); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c801b8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fca6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397056a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcecf853); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x01ad09c9); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x00acf4ad); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0be7); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0773f4c2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e90943); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35f9e6); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb10221); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff6); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe20014); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0054ffee); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effeb); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2efebb); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260027a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fbe6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02870605); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4af7fe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x001d09c1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0243f515); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd0b32); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0897f59e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4280871); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e95fa7c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef9701eb); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffd); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffff); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0056001d); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff57ff9c); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011300f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe82fe2e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ca0310); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb62); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155065a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xffbaf7f2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8c0977); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x03cef5b2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610a58); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a5f68f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3790797); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eebfb14); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef8001b5); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080004); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe9); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0047); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff75ff58); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef9fdc8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111036f); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb21); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x00120665); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x012df82e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd0708ec); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542f682); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f095c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9af792); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2db06b5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f38fbad); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6c017e); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 11900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000b); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe7ffd8); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00370068); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffa4ff28); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00790184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87fd91); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00430392); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb26); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfece0626); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294f8b2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb990825); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0698f77f); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe0842); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b73f8a7); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf25105cd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7bfc48); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5a0148); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00050010); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff2ffcc); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001b007b); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffdfff10); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00140198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0020fd8e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff710375); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb73); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9a059f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e0f978); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4e0726); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c8f8a7); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600070c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2ff9c9); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1db04de); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4fce5); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4b0111); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00010012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffffffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb007e); - cx25840_write4(client, DIF_BPF_COEFF89, 0x001dff14); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffad0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b7fdbe); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9031b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fc01); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc8504d6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0504fa79); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf93005f6); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08caf9f2); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52b05c0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccbfaf9); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf17903eb); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3fd83); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3f00db); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe0011); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000cffcc); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0071); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0058ff32); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4f014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x013cfe1f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfb028a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fcc9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9d03d6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f4fbad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848049d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0999fb5b); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf4820461); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d46fc32); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12d02f4); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x1007fe21); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3600a4); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0017ffd9); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc10055); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0088ff68); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0400f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a6fea7); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7501cc); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fdc0); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaef02a8); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a7fd07); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79d0326); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a31fcda); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf40702f3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9ffd72); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f601fa); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x1021fec0); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2f006d); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80007); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001fffeb); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaf002d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a8ffb0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e9ff4c); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2000ee); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fed8); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82015c); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0715fe7d); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7340198); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8dfe69); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bd017c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd5feb8); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d500fd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x1031ff60); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2b0037); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff70000); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00220000); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffa90000); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b30000); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec20000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x02000000); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd030000); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350000); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa5e0000); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x073b0000); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7110000); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aac0000); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a40000); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de70000); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0c90000); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x10360000); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef290000); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff9); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001f0015); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffd3); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a80050); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e900b4); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd20ff12); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130128); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82fea4); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x07150183); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf734fe68); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8d0197); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdfe84); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd50148); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d5ff03); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x103100a0); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2bffc9); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00170027); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ffab); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00880098); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff04ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a60159); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd75fe34); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00240); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaeffd58); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a702f9); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79dfcda); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a310326); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fd0d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9f028e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f6fe06); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x10210140); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2fff93); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000c0034); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff8f); - cx25840_write4(client, DIF_BPF_COEFF89, 0x005800ce); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4ffeb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x013c01e1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfbfd76); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110337); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9dfc2a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f40453); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848fb63); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x099904a5); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fb9f); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d4603ce); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12dfd0c); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x100701df); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef36ff5c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 12900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0001ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffff0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0x001d00ec); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffadfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b70242); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9fce5); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024103ff); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc85fb2a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05040587); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf930fa0a); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x08ca060e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfa40); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccb0507); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf179fc15); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3027d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3fff25); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff20034); - cx25840_write4(client, DIF_BPF_COEFF67, 0x001bff85); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffdf00f0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0014fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00200272); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff71fc8b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d048d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9afa61); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e00688); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4ef8da); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c80759); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f8f4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2f0637); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1dbfb22); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4031b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4bfeef); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff5); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe70028); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ff98); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffa400d8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0079fe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87026f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0043fc6e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404da); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfecef9da); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294074e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb99f7db); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x06980881); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef7be); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b730759); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf251fa33); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7b03b8); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5afeb8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffc); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe00017); - cx25840_write4(client, DIF_BPF_COEFF67, 0x004cffb9); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7500a8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef90238); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111fc91); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604df); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0012f99b); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x012d07d2); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd07f714); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542097e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff6a4); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9a086e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2dbf94b); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f380453); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6cfe82); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080003); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffde0001); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0056ffe3); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff570064); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0113ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe8201d2); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01cafcf0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35049e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155f9a6); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xffba080e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8cf689); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x03ce0a4e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f5a8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a50971); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf379f869); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eeb04ec); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef80fe4b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000a); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe2ffec); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00540012); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e0015); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2e0145); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260fd86); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51041a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0287f9fb); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4a0802); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x001df63f); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02430aeb); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf4ce); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x08970a62); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf428f78f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e950584); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xef97fe15); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffeaffda); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0046003d); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5affc4); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04009d); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c8fe48); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99035a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397fa96); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcec07ad); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x01adf637); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x00ac0b53); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef419); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x07730b3e); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e9f6bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35061a); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb1fddf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00000012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfff6ffcd); - cx25840_write4(client, DIF_BPF_COEFF67, 0x002f0061); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7bff79); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x011e007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe08ffe8); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f9ff28); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17026a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0479fb70); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbad0713); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x032ff672); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff100b83); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff38b); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x063c0c04); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5baf5f5); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dcc06ae); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcdfda8); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0012); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0004ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00100078); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffacff3e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e200f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe39ff35); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f10017); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd30156); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0521fc7f); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9c0638); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0499f6ee); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7a0b7c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f325); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f40cb3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69af537); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5a073f); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xefecfd72); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0011ffcb); - cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffe7ff19); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x008f014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94fe93); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b00105); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3002f); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x0585fdb7); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c10525); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x05def7a8); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf20b3c); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f2e9); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a00d48); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf787f484); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdf07cd); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00dfd3c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 13900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80008); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001bffd7); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10076); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0026ff0e); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x002c0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0ffe10); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x023b01e0); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff06); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a2ff09); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf92703e4); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f4f89b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa820ac5); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f2d9); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x02430dc3); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf881f3dc); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5c0859); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf031fd06); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80001); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021ffe8); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffba005d); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0060ff1f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc40198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0fdb5); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x019a029a); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fdea); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x05750067); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d4f9c0); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf9320a1a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f2f3); - cx25840_write4(client, DIF_BPF_COEFF2829, 0x00df0e22); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xf986f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd108e2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058fcd1); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0021fffd); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0038); - cx25840_write4(client, DIF_BPF_COEFF89, 0x008eff4a); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff630184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x003afd8b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x00da0326); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fced); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x050101c0); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cb0103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x0876fb10); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80a093e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f338); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7a0e66); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa94f2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3f0967); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf081fc9b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff3); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001d0013); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa000b); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00aaff89); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00cefd95); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x000a037b); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fc1d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x044c0305); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90cff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5fc81); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7100834); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff3a7); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe160e8d); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaaf231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa509e9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0adfc65); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00140025); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffdd); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2ffd6); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedb00f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150fdd3); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xff380391); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb85); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x035e0426); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xf994fdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08eefe0b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6490702); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f43e); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb60e97); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc6f1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a040a67); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0dbfc30); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0002ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00070033); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ffb4); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00a40027); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b4fe3f); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe760369); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb2e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x02450518); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5ffc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1ffa1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bc05ae); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902f4fc); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb600e85); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde7f15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x095d0ae2); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10cfbfb); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0005ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffa0038); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff95); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00820074); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f0fed0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd20304); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb1d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x010e05ce); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb64fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x084e013b); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569043e); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00f5dd); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa150e55); - cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0bf104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b00b59); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13ffbc6); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffed0035); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff83); - cx25840_write4(client, DIF_BPF_COEFF89, 0x005000b4); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6ff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ffff7a); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd580269); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fb53); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xffca0640); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc99fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x079a02cb); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55502ba); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5f6e0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d90e0a); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0031f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fd0bcb); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174fb91); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0009fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4002a); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ff82); - cx25840_write4(client, DIF_BPF_COEFF89, 0x001400e0); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3cff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e10030); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1201a4); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fbcd); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88066a); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf1f92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aa0449); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57e0128); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7ef801); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b00da2); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0156f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x07450c39); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1acfb5c); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00080002); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0019); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003fff92); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffd600f1); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x019700e1); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0500c2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fc84); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd590649); - cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5df87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x058505aa); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4ff91); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9f93c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69d0d20); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0279f05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x06880ca3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e6fb28); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 14900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x00060009); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0004); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0051ffb0); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff9d00e8); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffcfe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x01280180); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32ffd2); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fd6e); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4d05df); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d1f812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x043506e4); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf685fdfb); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fa8d); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a10c83); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0399f046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c70d08); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222faf3); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffe5ffef); - cx25840_write4(client, DIF_BPF_COEFF67, 0x0057ffd9); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff7000c4); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0062fe68); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x009e01ff); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95fee6); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0435fe7d); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb710530); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x023cf7ee); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c307ef); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75efc70); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5cfbef); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c10bce); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b3f03f); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x05030d69); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf261fabf); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15100000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); - cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xffefffdc); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00510006); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff540089); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00befe7c); - cx25840_write4(client, DIF_BPF_COEFF1213, 0x00060253); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe27fe0d); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413ffa2); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad10446); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0390f812); - cx25840_write4(client, DIF_BPF_COEFF2223, 0x013b08c3); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868faf6); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fd5f); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fd0b02); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c7f046); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x043b0dc4); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a1fa8b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15200000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0012); - cx25840_write4(client, DIF_BPF_COEFF45, 0xfffbffce); - cx25840_write4(client, DIF_BPF_COEFF67, 0x003f0033); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e003f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106feb6); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6e0276); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeddfd56); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000cc); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa740329); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bff87f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaa095d); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99ef995); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9fed8); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3590a1f); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d2f05e); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x03700e1b); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e4fa58); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15300000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9000f); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0009ffc8); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00250059); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff5effee); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0132ff10); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee30265); - cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaafccf); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x031101eb); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6001e8); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bdf92f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1b09b6); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafaf852); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0055); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d50929); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d3f086); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a30e6c); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf329fa24); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15400000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80009); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0015ffca); - cx25840_write4(client, DIF_BPF_COEFF67, 0x00050074); - cx25840_write4(client, DIF_BPF_COEFF89, 0xff81ff9f); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x013dff82); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe710221); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x007cfc80); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x024102ed); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa940090); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0680fa1e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9b09cd); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc73f736); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad501d0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2740820); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c9f0bd); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d40eb9); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371f9f1); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15500000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80002); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001effd5); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5007f); - cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff5b); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2401b0); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0146fc70); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d03c6); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb10ff32); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0701fb41); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb3709a1); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f644); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000345); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2350708); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b2f104); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x01050eff); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3baf9be); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15600000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0022ffe6); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9007a); - cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff29); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2007e); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01011b); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f6fc9e); - cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440467); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbccfdde); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738fc90); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f70934); - cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99f582); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x090204b0); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21a05e1); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8df15a); - cx25840_write4(client, DIF_BPF_COEFF3233, 0x00340f41); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf405f98b); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15700000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcfff4); - cx25840_write4(client, DIF_BPF_COEFF45, 0x0020fffa); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40064); - cx25840_write4(client, DIF_BPF_COEFF89, 0x002fff11); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a400f0); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0d006e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x0281fd09); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604c9); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbffca2); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0726fdfe); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e80888); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134f4f3); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1060c); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf22304af); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b59f1be); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xff640f7d); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf452f959); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15800000: - cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0000fff0); - cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0010); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa0041); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0067ff13); - cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043014a); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46ffb9); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02dbfda8); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3504e5); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddcfb8d); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9ff7e); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf81107a2); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9f49a); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0753); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2500373); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c14f231); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930fb3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a1f927); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 15900000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0003ffee); - cx25840_write4(client, DIF_BPF_COEFF45, 0x000f0023); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0016); - cx25840_write4(client, DIF_BPF_COEFF89, 0x0093ff31); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdc0184); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6ff09); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fdfe70); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5104ba); - cx25840_write4(client, DIF_BPF_COEFF1819, 0xff15faac); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270103); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7780688); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x044df479); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430883); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a00231); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbef2b2); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc40fe3); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2f8f5); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - - case 16000000: - cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); - cx25840_write4(client, DIF_BPF_COEFF23, 0x0006ffef); - cx25840_write4(client, DIF_BPF_COEFF45, 0x00020031); - cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffe8); - cx25840_write4(client, DIF_BPF_COEFF89, 0x00adff66); - cx25840_write4(client, DIF_BPF_COEFF1011, 0xff790198); - cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe6e); - cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e5ff55); - cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99044a); - cx25840_write4(client, DIF_BPF_COEFF1819, 0x005bfa09); - cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545027f); - cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7230541); - cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b8f490); - cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20997); - cx25840_write4(client, DIF_BPF_COEFF2829, 0xf31300eb); - cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d55f341); - cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6100e); - cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544f8c3); - cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); - break; - } -} - -static void cx23888_std_setup(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - v4l2_std_id std = state->std; - u32 ifHz; - - cx25840_write4(client, 0x478, 0x6628021F); - cx25840_write4(client, 0x400, 0x0); - cx25840_write4(client, 0x4b4, 0x20524030); - cx25840_write4(client, 0x47c, 0x010a8263); - - if (std & V4L2_STD_NTSC) { - v4l_dbg(1, cx25840_debug, client, "%s() Selecting NTSC", - __func__); - - /* Horiz / vert timing */ - cx25840_write4(client, 0x428, 0x1e1e601a); - cx25840_write4(client, 0x424, 0x5b2d007a); - - /* DIF NTSC */ - cx25840_write4(client, 0x304, 0x6503bc0c); - cx25840_write4(client, 0x308, 0xbd038c85); - cx25840_write4(client, 0x30c, 0x1db4640a); - cx25840_write4(client, 0x310, 0x00008800); - cx25840_write4(client, 0x314, 0x44400400); - cx25840_write4(client, 0x32c, 0x0c800800); - cx25840_write4(client, 0x330, 0x27000100); - cx25840_write4(client, 0x334, 0x1f296e1f); - cx25840_write4(client, 0x338, 0x009f50c1); - cx25840_write4(client, 0x340, 0x1befbf06); - cx25840_write4(client, 0x344, 0x000035e8); - - /* DIF I/F */ - ifHz = 5400000; - - } else { - v4l_dbg(1, cx25840_debug, client, "%s() Selecting PAL-BG", - __func__); - - /* Horiz / vert timing */ - cx25840_write4(client, 0x428, 0x28244024); - cx25840_write4(client, 0x424, 0x5d2d0084); - - /* DIF */ - cx25840_write4(client, 0x304, 0x6503bc0c); - cx25840_write4(client, 0x308, 0xbd038c85); - cx25840_write4(client, 0x30c, 0x1db4640a); - cx25840_write4(client, 0x310, 0x00008800); - cx25840_write4(client, 0x314, 0x44400600); - cx25840_write4(client, 0x32c, 0x0c800800); - cx25840_write4(client, 0x330, 0x27000100); - cx25840_write4(client, 0x334, 0x213530ec); - cx25840_write4(client, 0x338, 0x00a65ba8); - cx25840_write4(client, 0x340, 0x1befbf06); - cx25840_write4(client, 0x344, 0x000035e8); - - /* DIF I/F */ - ifHz = 6000000; - } - - cx23885_dif_setup(client, ifHz); - - /* Explicitly ensure the inputs are reconfigured after - * a standard change. - */ - set_input(client, state->vid_input, state->aud_input); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { - .s_ctrl = cx25840_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops cx25840_core_ops = { - .log_status = cx25840_log_status, - .g_chip_ident = cx25840_g_chip_ident, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = cx25840_s_std, - .g_std = cx25840_g_std, - .reset = cx25840_reset, - .load_fw = cx25840_load_fw, - .s_io_pin_config = common_s_io_pin_config, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = cx25840_g_register, - .s_register = cx25840_s_register, -#endif - .interrupt_service_routine = cx25840_irq_handler, -}; - -static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { - .s_frequency = cx25840_s_frequency, - .s_radio = cx25840_s_radio, - .g_tuner = cx25840_g_tuner, - .s_tuner = cx25840_s_tuner, -}; - -static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { - .s_clock_freq = cx25840_s_clock_freq, - .s_routing = cx25840_s_audio_routing, - .s_stream = cx25840_s_audio_stream, -}; - -static const struct v4l2_subdev_video_ops cx25840_video_ops = { - .s_routing = cx25840_s_video_routing, - .s_mbus_fmt = cx25840_s_mbus_fmt, - .s_stream = cx25840_s_stream, - .g_input_status = cx25840_g_input_status, -}; - -static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { - .decode_vbi_line = cx25840_decode_vbi_line, - .s_raw_fmt = cx25840_s_raw_fmt, - .s_sliced_fmt = cx25840_s_sliced_fmt, - .g_sliced_fmt = cx25840_g_sliced_fmt, -}; - -static const struct v4l2_subdev_ops cx25840_ops = { - .core = &cx25840_core_ops, - .tuner = &cx25840_tuner_ops, - .audio = &cx25840_audio_ops, - .video = &cx25840_video_ops, - .vbi = &cx25840_vbi_ops, - .ir = &cx25840_ir_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static u32 get_cx2388x_ident(struct i2c_client *client) -{ - u32 ret; - - /* Come out of digital power down */ - cx25840_write(client, 0x000, 0); - - /* Detecting whether the part is cx23885/7/8 is more - * difficult than it needs to be. No ID register. Instead we - * probe certain registers indicated in the datasheets to look - * for specific defaults that differ between the silicon designs. */ - - /* It's either 885/7 if the IR Tx Clk Divider register exists */ - if (cx25840_read4(client, 0x204) & 0xffff) { - /* CX23885 returns bogus repetitive byte values for the DIF, - * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ - ret = cx25840_read4(client, 0x300); - if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { - /* No DIF */ - ret = V4L2_IDENT_CX23885_AV; - } else { - /* CX23887 has a broken DIF, but the registers - * appear valid (but unused), good enough to detect. */ - ret = V4L2_IDENT_CX23887_AV; - } - } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { - /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ - ret = V4L2_IDENT_CX23888_AV; - } else { - v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); - ret = V4L2_IDENT_CX23887_AV; - } - - /* Back into digital power down */ - cx25840_write(client, 0x000, 2); - return ret; -} - -static int cx25840_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct cx25840_state *state; - struct v4l2_subdev *sd; - int default_volume; - u32 id = V4L2_IDENT_NONE; - u16 device_id; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1); - - device_id = cx25840_read(client, 0x101) << 8; - device_id |= cx25840_read(client, 0x100); - v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id); - - /* The high byte of the device ID should be - * 0x83 for the cx2583x and 0x84 for the cx2584x */ - if ((device_id & 0xff00) == 0x8300) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } else if ((device_id & 0xff00) == 0x8400) { - id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); - } else if (device_id == 0x0000) { - id = get_cx2388x_ident(client); - } else if ((device_id & 0xfff0) == 0x5A30) { - /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ - id = V4L2_IDENT_CX2310X_AV; - } else if ((device_id & 0xff) == (device_id >> 8)) { - v4l_err(client, - "likely a confused/unresponsive cx2388[578] A/V decoder" - " found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - v4l_err(client, "A method to reset it from the cx25840 driver" - " software is not known at this time\n"); - return -ENODEV; - } else { - v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); - return -ENODEV; - } - - state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &cx25840_ops); - - switch (id) { - case V4L2_IDENT_CX23885_AV: - v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX23887_AV: - v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX23888_AV: - v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX2310X_AV: - v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", - device_id, client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX25840: - case V4L2_IDENT_CX25841: - case V4L2_IDENT_CX25842: - case V4L2_IDENT_CX25843: - /* Note: revision '(device_id & 0x0f) == 2' was never built. The - marking skips from 0x1 == 22 to 0x3 == 23. */ - v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", - (device_id & 0xfff0) >> 4, - (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 - : (device_id & 0x0f), - client->addr << 1, client->adapter->name); - break; - case V4L2_IDENT_CX25836: - case V4L2_IDENT_CX25837: - default: - v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", - (device_id & 0xfff0) >> 4, device_id & 0x0f, - client->addr << 1, client->adapter->name); - break; - } - - state->c = client; - state->vid_input = CX25840_COMPOSITE7; - state->aud_input = CX25840_AUDIO8; - state->audclk_freq = 48000; - state->audmode = V4L2_TUNER_MODE_LANG1; - state->vbi_line_offset = 8; - state->id = id; - state->rev = device_id; - v4l2_ctrl_handler_init(&state->hdl, 9); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); - v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - if (!is_cx2583x(state)) { - default_volume = cx25840_read(client, 0x8d4); - /* - * Enforce the legacy PVR-350/MSP3400 to PVR-150/CX25843 volume - * scale mapping limits to avoid -ERANGE errors when - * initializing the volume control - */ - if (default_volume > 228) { - /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ - default_volume = 228; - cx25840_write(client, 0x8d4, 228); - } - else if (default_volume < 20) { - /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ - default_volume = 20; - cx25840_write(client, 0x8d4, 20); - } - default_volume = (((228 - default_volume) >> 1) + 23) << 9; - - state->volume = v4l2_ctrl_new_std(&state->hdl, - &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, - 0, 65535, 65535 / 100, default_volume); - state->mute = v4l2_ctrl_new_std(&state->hdl, - &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, - 0, 1, 1, 0); - v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, - 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_BASS, - 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_TREBLE, - 0, 65535, 65535 / 100, 32768); - } - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - if (!is_cx2583x(state)) - v4l2_ctrl_cluster(2, &state->volume); - v4l2_ctrl_handler_setup(&state->hdl); - - if (client->dev.platform_data) { - struct cx25840_platform_data *pdata = client->dev.platform_data; - - state->pvr150_workaround = pdata->pvr150_workaround; - } - - cx25840_ir_probe(sd); - return 0; -} - -static int cx25840_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct cx25840_state *state = to_state(sd); - - cx25840_ir_remove(sd); - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -static const struct i2c_device_id cx25840_id[] = { - { "cx25840", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, cx25840_id); - -static struct i2c_driver cx25840_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "cx25840", - }, - .probe = cx25840_probe, - .remove = cx25840_remove, - .id_table = cx25840_id, -}; - -module_i2c_driver(cx25840_driver); diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h deleted file mode 100644 index bd4ada28b490..000000000000 --- a/drivers/media/video/cx25840/cx25840-core.h +++ /dev/null @@ -1,137 +0,0 @@ -/* cx25840 internal API header - * - * Copyright (C) 2003-2004 Chris Kennedy - * - * This program is free software; 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 _CX25840_CORE_H_ -#define _CX25840_CORE_H_ - - -#include -#include -#include -#include -#include - -struct cx25840_ir_state; - -struct cx25840_state { - struct i2c_client *c; - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct { - /* volume cluster */ - struct v4l2_ctrl *volume; - struct v4l2_ctrl *mute; - }; - int pvr150_workaround; - int radio; - v4l2_std_id std; - enum cx25840_video_input vid_input; - enum cx25840_audio_input aud_input; - u32 audclk_freq; - int audmode; - int vbi_line_offset; - u32 id; - u32 rev; - int is_initialized; - wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ - struct work_struct fw_work; /* work entry for fw load */ - struct cx25840_ir_state *ir_state; -}; - -static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct cx25840_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd; -} - -static inline bool is_cx2583x(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX25836 || - state->id == V4L2_IDENT_CX25837; -} - -static inline bool is_cx231xx(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX2310X_AV; -} - -static inline bool is_cx2388x(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23885_AV || - state->id == V4L2_IDENT_CX23887_AV || - state->id == V4L2_IDENT_CX23888_AV; -} - -static inline bool is_cx23885(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23885_AV; -} - -static inline bool is_cx23887(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23887_AV; -} - -static inline bool is_cx23888(struct cx25840_state *state) -{ - return state->id == V4L2_IDENT_CX23888_AV; -} - -/* ----------------------------------------------------------------------- */ -/* cx25850-core.c */ -int cx25840_write(struct i2c_client *client, u16 addr, u8 value); -int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); -u8 cx25840_read(struct i2c_client *client, u16 addr); -u32 cx25840_read4(struct i2c_client *client, u16 addr); -int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); -int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, - u32 or_value); -void cx25840_std_setup(struct i2c_client *client); - -/* ----------------------------------------------------------------------- */ -/* cx25850-firmware.c */ -int cx25840_loadfw(struct i2c_client *client); - -/* ----------------------------------------------------------------------- */ -/* cx25850-audio.c */ -void cx25840_audio_set_path(struct i2c_client *client); -int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); - -extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops; - -/* ----------------------------------------------------------------------- */ -/* cx25850-vbi.c */ -int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); -int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); -int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); -int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); - -/* ----------------------------------------------------------------------- */ -/* cx25850-ir.c */ -extern const struct v4l2_subdev_ir_ops cx25840_ir_ops; -int cx25840_ir_log_status(struct v4l2_subdev *sd); -int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled); -int cx25840_ir_probe(struct v4l2_subdev *sd); -int cx25840_ir_remove(struct v4l2_subdev *sd); - -#endif diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c deleted file mode 100644 index b3169f94ece8..000000000000 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ /dev/null @@ -1,175 +0,0 @@ -/* cx25840 firmware functions - * - * This program is free software; 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 -#include -#include -#include -#include - -#include "cx25840-core.h" - -/* - * Mike Isely - The FWSEND parameter controls the - * size of the firmware chunks sent down the I2C bus to the chip. - * Previously this had been set to 1024 but unfortunately some I2C - * implementations can't transfer data in such big gulps. - * Specifically, the pvrusb2 driver has a hard limit of around 60 - * bytes, due to the encapsulation there of I2C traffic into USB - * messages. So we have to significantly reduce this parameter. - */ -#define FWSEND 48 - -#define FWDEV(x) &((x)->dev) - -static char *firmware = ""; - -module_param(firmware, charp, 0444); - -MODULE_PARM_DESC(firmware, "Firmware image to load"); - -static void start_fw_load(struct i2c_client *client) -{ - /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ - cx25840_write(client, 0x800, 0x00); - cx25840_write(client, 0x801, 0x00); - // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 - cx25840_write(client, 0x803, 0x0b); - /* AUTO_INC_DIS=1 */ - cx25840_write(client, 0x000, 0x20); -} - -static void end_fw_load(struct i2c_client *client) -{ - /* AUTO_INC_DIS=0 */ - cx25840_write(client, 0x000, 0x00); - /* DL_ENABLE=0 */ - cx25840_write(client, 0x803, 0x03); -} - -#define CX2388x_FIRMWARE "v4l-cx23885-avcore-01.fw" -#define CX231xx_FIRMWARE "v4l-cx231xx-avcore-01.fw" -#define CX25840_FIRMWARE "v4l-cx25840.fw" - -static const char *get_fw_name(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (firmware[0]) - return firmware; - if (is_cx2388x(state)) - return CX2388x_FIRMWARE; - if (is_cx231xx(state)) - return CX231xx_FIRMWARE; - return CX25840_FIRMWARE; -} - -static int check_fw_load(struct i2c_client *client, int size) -{ - /* DL_ADDR_HB DL_ADDR_LB */ - int s = cx25840_read(client, 0x801) << 8; - s |= cx25840_read(client, 0x800); - - if (size != s) { - v4l_err(client, "firmware %s load failed\n", - get_fw_name(client)); - return -EINVAL; - } - - v4l_info(client, "loaded %s firmware (%d bytes)\n", - get_fw_name(client), size); - return 0; -} - -static int fw_write(struct i2c_client *client, const u8 *data, int size) -{ - if (i2c_master_send(client, data, size) < size) { - v4l_err(client, "firmware load i2c failure\n"); - return -ENOSYS; - } - - return 0; -} - -int cx25840_loadfw(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - const struct firmware *fw = NULL; - u8 buffer[FWSEND]; - const u8 *ptr; - const char *fwname = get_fw_name(client); - int size, retval; - int MAX_BUF_SIZE = FWSEND; - u32 gpio_oe = 0, gpio_da = 0; - - if (is_cx2388x(state)) { - /* Preserve the GPIO OE and output bits */ - gpio_oe = cx25840_read(client, 0x160); - gpio_da = cx25840_read(client, 0x164); - } - - if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { - v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); - MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ - } - - if (request_firmware(&fw, fwname, FWDEV(client)) != 0) { - v4l_err(client, "unable to open firmware %s\n", fwname); - return -EINVAL; - } - - start_fw_load(client); - - buffer[0] = 0x08; - buffer[1] = 0x02; - - size = fw->size; - ptr = fw->data; - while (size > 0) { - int len = min(MAX_BUF_SIZE - 2, size); - - memcpy(buffer + 2, ptr, len); - - retval = fw_write(client, buffer, len + 2); - - if (retval < 0) { - release_firmware(fw); - return retval; - } - - size -= len; - ptr += len; - } - - end_fw_load(client); - - size = fw->size; - release_firmware(fw); - - if (is_cx2388x(state)) { - /* Restore GPIO configuration after f/w load */ - cx25840_write(client, 0x160, gpio_oe); - cx25840_write(client, 0x164, gpio_da); - } - - return check_fw_load(client, size); -} - -MODULE_FIRMWARE(CX2388x_FIRMWARE); -MODULE_FIRMWARE(CX231xx_FIRMWARE); -MODULE_FIRMWARE(CX25840_FIRMWARE); - diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c deleted file mode 100644 index 38ce76ed1924..000000000000 --- a/drivers/media/video/cx25840/cx25840-ir.c +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores - * - * Integrated Consumer Infrared Controller - * - * Copyright (C) 2010 Andy Walls - * - * This program is free software; 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 -#include -#include -#include -#include - -#include "cx25840-core.h" - -static unsigned int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages"); - -#define CX25840_IR_REG_BASE 0x200 - -#define CX25840_IR_CNTRL_REG 0x200 -#define CNTRL_WIN_3_3 0x00000000 -#define CNTRL_WIN_4_3 0x00000001 -#define CNTRL_WIN_3_4 0x00000002 -#define CNTRL_WIN_4_4 0x00000003 -#define CNTRL_WIN 0x00000003 -#define CNTRL_EDG_NONE 0x00000000 -#define CNTRL_EDG_FALL 0x00000004 -#define CNTRL_EDG_RISE 0x00000008 -#define CNTRL_EDG_BOTH 0x0000000C -#define CNTRL_EDG 0x0000000C -#define CNTRL_DMD 0x00000010 -#define CNTRL_MOD 0x00000020 -#define CNTRL_RFE 0x00000040 -#define CNTRL_TFE 0x00000080 -#define CNTRL_RXE 0x00000100 -#define CNTRL_TXE 0x00000200 -#define CNTRL_RIC 0x00000400 -#define CNTRL_TIC 0x00000800 -#define CNTRL_CPL 0x00001000 -#define CNTRL_LBM 0x00002000 -#define CNTRL_R 0x00004000 - -#define CX25840_IR_TXCLK_REG 0x204 -#define TXCLK_TCD 0x0000FFFF - -#define CX25840_IR_RXCLK_REG 0x208 -#define RXCLK_RCD 0x0000FFFF - -#define CX25840_IR_CDUTY_REG 0x20C -#define CDUTY_CDC 0x0000000F - -#define CX25840_IR_STATS_REG 0x210 -#define STATS_RTO 0x00000001 -#define STATS_ROR 0x00000002 -#define STATS_RBY 0x00000004 -#define STATS_TBY 0x00000008 -#define STATS_RSR 0x00000010 -#define STATS_TSR 0x00000020 - -#define CX25840_IR_IRQEN_REG 0x214 -#define IRQEN_RTE 0x00000001 -#define IRQEN_ROE 0x00000002 -#define IRQEN_RSE 0x00000010 -#define IRQEN_TSE 0x00000020 -#define IRQEN_MSK 0x00000033 - -#define CX25840_IR_FILTR_REG 0x218 -#define FILTR_LPF 0x0000FFFF - -#define CX25840_IR_FIFO_REG 0x23C -#define FIFO_RXTX 0x0000FFFF -#define FIFO_RXTX_LVL 0x00010000 -#define FIFO_RXTX_RTO 0x0001FFFF -#define FIFO_RX_NDV 0x00020000 -#define FIFO_RX_DEPTH 8 -#define FIFO_TX_DEPTH 8 - -#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ -#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2) - -/* - * We use this union internally for convenience, but callers to tx_write - * and rx_read will be expecting records of type struct ir_raw_event. - * Always ensure the size of this union is dictated by struct ir_raw_event. - */ -union cx25840_ir_fifo_rec { - u32 hw_fifo_data; - struct ir_raw_event ir_core_data; -}; - -#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) -#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) - -struct cx25840_ir_state { - struct i2c_client *c; - - struct v4l2_subdev_ir_parameters rx_params; - struct mutex rx_params_lock; /* protects Rx parameter settings cache */ - atomic_t rxclk_divider; - atomic_t rx_invert; - - struct kfifo rx_kfifo; - spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */ - - struct v4l2_subdev_ir_parameters tx_params; - struct mutex tx_params_lock; /* protects Tx parameter settings cache */ - atomic_t txclk_divider; -}; - -static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - return state ? state->ir_state : NULL; -} - - -/* - * Rx and Tx Clock Divider register computations - * - * Note the largest clock divider value of 0xffff corresponds to: - * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns - * which fits in 21 bits, so we'll use unsigned int for time arguments. - */ -static inline u16 count_to_clock_divider(unsigned int d) -{ - if (d > RXCLK_RCD + 1) - d = RXCLK_RCD; - else if (d < 2) - d = 1; - else - d--; - return (u16) d; -} - -static inline u16 ns_to_clock_divider(unsigned int ns) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); -} - -static inline unsigned int clock_divider_to_ns(unsigned int divider) -{ - /* Period of the Rx or Tx clock in ns */ - return DIV_ROUND_CLOSEST((divider + 1) * 1000, - CX25840_IR_REFCLK_FREQ / 1000000); -} - -static inline u16 carrier_freq_to_clock_divider(unsigned int freq) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16)); -} - -static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) -{ - return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16); -} - -static inline u16 freq_to_clock_divider(unsigned int freq, - unsigned int rollovers) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers)); -} - -static inline unsigned int clock_divider_to_freq(unsigned int divider, - unsigned int rollovers) -{ - return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, - (divider + 1) * rollovers); -} - -/* - * Low Pass Filter register calculations - * - * Note the largest count value of 0xffff corresponds to: - * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns - * which fits in 21 bits, so we'll use unsigned int for time arguments. - */ -static inline u16 count_to_lpf_count(unsigned int d) -{ - if (d > FILTR_LPF) - d = FILTR_LPF; - else if (d < 4) - d = 0; - return (u16) d; -} - -static inline u16 ns_to_lpf_count(unsigned int ns) -{ - return count_to_lpf_count( - DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); -} - -static inline unsigned int lpf_count_to_ns(unsigned int count) -{ - /* Duration of the Low Pass Filter rejection window in ns */ - return DIV_ROUND_CLOSEST(count * 1000, - CX25840_IR_REFCLK_FREQ / 1000000); -} - -static inline unsigned int lpf_count_to_us(unsigned int count) -{ - /* Duration of the Low Pass Filter rejection window in us */ - return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000); -} - -/* - * FIFO register pulse width count compuations - */ -static u32 clock_divider_to_resolution(u16 divider) -{ - /* - * Resolution is the duration of 1 tick of the readable portion of - * of the pulse width counter as read from the FIFO. The two lsb's are - * not readable, hence the << 2. This function returns ns. - */ - return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, - CX25840_IR_REFCLK_FREQ / 1000000); -} - -static u64 pulse_width_count_to_ns(u16 count, u16 divider) -{ - u64 n; - u32 rem; - - /* - * The 2 lsb's of the pulse width timer count are not readable, hence - * the (count << 2) | 0x3 - */ - n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ - rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ - if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) - n++; - return n; -} - -#if 0 -/* Keep as we will need this for Transmit functionality */ -static u16 ns_to_pulse_width_count(u32 ns, u16 divider) -{ - u64 n; - u32 d; - u32 rem; - - /* - * The 2 lsb's of the pulse width timer count are not accessible, hence - * the (1 << 2) - */ - n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */ - d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */ - rem = do_div(n, d); - if (rem >= d / 2) - n++; - - if (n > FIFO_RXTX) - n = FIFO_RXTX; - else if (n == 0) - n = 1; - return (u16) n; -} - -#endif -static unsigned int pulse_width_count_to_us(u16 count, u16 divider) -{ - u64 n; - u32 rem; - - /* - * The 2 lsb's of the pulse width timer count are not readable, hence - * the (count << 2) | 0x3 - */ - n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ - rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ - if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) - n++; - return (unsigned int) n; -} - -/* - * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts - * - * The total pulse clock count is an 18 bit pulse width timer count as the most - * significant part and (up to) 16 bit clock divider count as a modulus. - * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse - * width timer count's least significant bit. - */ -static u64 ns_to_pulse_clocks(u32 ns) -{ - u64 clocks; - u32 rem; - clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ - rem = do_div(clocks, 1000); /* /1000 = cycles */ - if (rem >= 1000 / 2) - clocks++; - return clocks; -} - -static u16 pulse_clocks_to_clock_divider(u64 count) -{ - do_div(count, (FIFO_RXTX << 2) | 0x3); - - /* net result needs to be rounded down and decremented by 1 */ - if (count > RXCLK_RCD + 1) - count = RXCLK_RCD; - else if (count < 2) - count = 1; - else - count--; - return (u16) count; -} - -/* - * IR Control Register helpers - */ -enum tx_fifo_watermark { - TX_FIFO_HALF_EMPTY = 0, - TX_FIFO_EMPTY = CNTRL_TIC, -}; - -enum rx_fifo_watermark { - RX_FIFO_HALF_FULL = 0, - RX_FIFO_NOT_EMPTY = CNTRL_RIC, -}; - -static inline void control_tx_irq_watermark(struct i2c_client *c, - enum tx_fifo_watermark level) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level); -} - -static inline void control_rx_irq_watermark(struct i2c_client *c, - enum rx_fifo_watermark level) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level); -} - -static inline void control_tx_enable(struct i2c_client *c, bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), - enable ? (CNTRL_TXE | CNTRL_TFE) : 0); -} - -static inline void control_rx_enable(struct i2c_client *c, bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), - enable ? (CNTRL_RXE | CNTRL_RFE) : 0); -} - -static inline void control_tx_modulation_enable(struct i2c_client *c, - bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD, - enable ? CNTRL_MOD : 0); -} - -static inline void control_rx_demodulation_enable(struct i2c_client *c, - bool enable) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD, - enable ? CNTRL_DMD : 0); -} - -static inline void control_rx_s_edge_detection(struct i2c_client *c, - u32 edge_types) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, - edge_types & CNTRL_EDG_BOTH); -} - -static void control_rx_s_carrier_window(struct i2c_client *c, - unsigned int carrier, - unsigned int *carrier_range_low, - unsigned int *carrier_range_high) -{ - u32 v; - unsigned int c16 = carrier * 16; - - if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { - v = CNTRL_WIN_3_4; - *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); - } else { - v = CNTRL_WIN_3_3; - *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); - } - - if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { - v |= CNTRL_WIN_4_3; - *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); - } else { - v |= CNTRL_WIN_3_3; - *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); - } - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v); -} - -static inline void control_tx_polarity_invert(struct i2c_client *c, - bool invert) -{ - cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL, - invert ? CNTRL_CPL : 0); -} - -/* - * IR Rx & Tx Clock Register helpers - */ -static unsigned int txclk_tx_s_carrier(struct i2c_client *c, - unsigned int freq, - u16 *divider) -{ - *divider = carrier_freq_to_clock_divider(freq); - cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); - return clock_divider_to_carrier_freq(*divider); -} - -static unsigned int rxclk_rx_s_carrier(struct i2c_client *c, - unsigned int freq, - u16 *divider) -{ - *divider = carrier_freq_to_clock_divider(freq); - cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); - return clock_divider_to_carrier_freq(*divider); -} - -static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns, - u16 *divider) -{ - u64 pulse_clocks; - - if (ns > IR_MAX_DURATION) - ns = IR_MAX_DURATION; - pulse_clocks = ns_to_pulse_clocks(ns); - *divider = pulse_clocks_to_clock_divider(pulse_clocks); - cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); - return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); -} - -static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns, - u16 *divider) -{ - u64 pulse_clocks; - - if (ns > IR_MAX_DURATION) - ns = IR_MAX_DURATION; - pulse_clocks = ns_to_pulse_clocks(ns); - *divider = pulse_clocks_to_clock_divider(pulse_clocks); - cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); - return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); -} - -/* - * IR Tx Carrier Duty Cycle register helpers - */ -static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c, - unsigned int duty_cycle) -{ - u32 n; - n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ - if (n != 0) - n--; - if (n > 15) - n = 15; - cx25840_write4(c, CX25840_IR_CDUTY_REG, n); - return DIV_ROUND_CLOSEST((n + 1) * 100, 16); -} - -/* - * IR Filter Register helpers - */ -static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns) -{ - u32 count = ns_to_lpf_count(min_width_ns); - cx25840_write4(c, CX25840_IR_FILTR_REG, count); - return lpf_count_to_ns(count); -} - -/* - * IR IRQ Enable Register helpers - */ -static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask) -{ - struct cx25840_state *state = to_state(sd); - - if (is_cx23885(state) || is_cx23887(state)) - mask ^= IRQEN_MSK; - mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); - cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, - ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); -} - -static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask) -{ - struct cx25840_state *state = to_state(sd); - - if (is_cx23885(state) || is_cx23887(state)) - mask ^= IRQEN_MSK; - mask &= IRQEN_TSE; - cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask); -} - -/* - * V4L2 Subdevice IR Ops - */ -int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) -{ - struct cx25840_state *state = to_state(sd); - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c = NULL; - unsigned long flags; - - union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; - unsigned int i, j, k; - u32 events, v; - int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; - u32 cntrl, irqen, stats; - - *handled = false; - if (ir_state == NULL) - return -ENODEV; - - c = ir_state->c; - - /* Only support the IR controller for the CX2388[57] AV Core for now */ - if (!(is_cx23885(state) || is_cx23887(state))) - return -ENODEV; - - cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); - irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); - if (is_cx23885(state) || is_cx23887(state)) - irqen ^= IRQEN_MSK; - stats = cx25840_read4(c, CX25840_IR_STATS_REG); - - tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ - rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ - rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ - ror = stats & STATS_ROR; /* Rx FIFO Over Run */ - - tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ - rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ - rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ - roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ - - v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n", - tsr ? "tsr" : " ", rsr ? "rsr" : " ", - rto ? "rto" : " ", ror ? "ror" : " ", - stats & STATS_TBY ? "tby" : " ", - stats & STATS_RBY ? "rby" : " "); - - v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n", - tse ? "tse" : " ", rse ? "rse" : " ", - rte ? "rte" : " ", roe ? "roe" : " "); - - /* - * Transmitter interrupt service - */ - if (tse && tsr) { - /* - * TODO: - * Check the watermark threshold setting - * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo - * Push the data to the hardware FIFO. - * If there was nothing more to send in the tx_kfifo, disable - * the TSR IRQ and notify the v4l2_device. - * If there was something in the tx_kfifo, check the tx_kfifo - * level and notify the v4l2_device, if it is low. - */ - /* For now, inhibit TSR interrupt until Tx is implemented */ - irqenable_tx(sd, 0); - events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; - v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); - *handled = true; - } - - /* - * Receiver interrupt service - */ - kror = 0; - if ((rse && rsr) || (rte && rto)) { - /* - * Receive data on RSR to clear the STATS_RSR. - * Receive data on RTO, since we may not have yet hit the RSR - * watermark when we receive the RTO. - */ - for (i = 0, v = FIFO_RX_NDV; - (v & FIFO_RX_NDV) && !kror; i = 0) { - for (j = 0; - (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { - v = cx25840_read4(c, CX25840_IR_FIFO_REG); - rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; - i++; - } - if (i == 0) - break; - j = i * sizeof(union cx25840_ir_fifo_rec); - k = kfifo_in_locked(&ir_state->rx_kfifo, - (unsigned char *) rx_data, j, - &ir_state->rx_kfifo_lock); - if (k != j) - kror++; /* rx_kfifo over run */ - } - *handled = true; - } - - events = 0; - v = 0; - if (kror) { - events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; - v4l2_err(sd, "IR receiver software FIFO overrun\n"); - } - if (roe && ror) { - /* - * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear - * the Rx FIFO Over Run status (STATS_ROR) - */ - v |= CNTRL_RFE; - events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; - v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); - } - if (rte && rto) { - /* - * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear - * the Rx Pulse Width Timer Time Out (STATS_RTO) - */ - v |= CNTRL_RXE; - events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; - } - if (v) { - /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ - cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v); - cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl); - *handled = true; - } - spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); - if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2) - events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; - spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); - - if (events) - v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); - return 0; -} - -/* Receiver */ -static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, - ssize_t *num) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - bool invert; - u16 divider; - unsigned int i, n; - union cx25840_ir_fifo_rec *p; - unsigned u, v, w; - - if (ir_state == NULL) - return -ENODEV; - - invert = (bool) atomic_read(&ir_state->rx_invert); - divider = (u16) atomic_read(&ir_state->rxclk_divider); - - n = count / sizeof(union cx25840_ir_fifo_rec) - * sizeof(union cx25840_ir_fifo_rec); - if (n == 0) { - *num = 0; - return 0; - } - - n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n, - &ir_state->rx_kfifo_lock); - - n /= sizeof(union cx25840_ir_fifo_rec); - *num = n * sizeof(union cx25840_ir_fifo_rec); - - for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { - - if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { - /* Assume RTO was because of no IR light input */ - u = 0; - w = 1; - } else { - u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; - if (invert) - u = u ? 0 : 1; - w = 0; - } - - v = (unsigned) pulse_width_count_to_ns( - (u16) (p->hw_fifo_data & FIFO_RXTX), divider); - if (v > IR_MAX_DURATION) - v = IR_MAX_DURATION; - - init_ir_raw_event(&p->ir_core_data); - p->ir_core_data.pulse = u; - p->ir_core_data.duration = v; - p->ir_core_data.timeout = w; - - v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n", - v, u ? "mark" : "space", w ? "(timed out)" : ""); - if (w) - v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); - } - return 0; -} - -static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - - mutex_lock(&ir_state->rx_params_lock); - memcpy(p, &ir_state->rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - mutex_unlock(&ir_state->rx_params_lock); - return 0; -} - -static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - - if (ir_state == NULL) - return -ENODEV; - - c = ir_state->c; - mutex_lock(&ir_state->rx_params_lock); - - /* Disable or slow down all IR Rx circuits and counters */ - irqenable_rx(sd, 0); - control_rx_enable(c, false); - control_rx_demodulation_enable(c, false); - control_rx_s_edge_detection(c, CNTRL_EDG_NONE); - filter_rx_s_min_width(c, 0); - cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD); - - ir_state->rx_params.shutdown = true; - - mutex_unlock(&ir_state->rx_params_lock); - return 0; -} - -static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - struct v4l2_subdev_ir_parameters *o; - u16 rxclk_divider; - - if (ir_state == NULL) - return -ENODEV; - - if (p->shutdown) - return cx25840_ir_rx_shutdown(sd); - - if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) - return -ENOSYS; - - c = ir_state->c; - o = &ir_state->rx_params; - - mutex_lock(&ir_state->rx_params_lock); - - o->shutdown = p->shutdown; - - p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; - o->mode = p->mode; - - p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); - o->bytes_per_data_element = p->bytes_per_data_element; - - /* Before we tweak the hardware, we have to disable the receiver */ - irqenable_rx(sd, 0); - control_rx_enable(c, false); - - control_rx_demodulation_enable(c, p->modulation); - o->modulation = p->modulation; - - if (p->modulation) { - p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq, - &rxclk_divider); - - o->carrier_freq = p->carrier_freq; - - p->duty_cycle = 50; - o->duty_cycle = p->duty_cycle; - - control_rx_s_carrier_window(c, p->carrier_freq, - &p->carrier_range_lower, - &p->carrier_range_upper); - o->carrier_range_lower = p->carrier_range_lower; - o->carrier_range_upper = p->carrier_range_upper; - - p->max_pulse_width = - (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); - } else { - p->max_pulse_width = - rxclk_rx_s_max_pulse_width(c, p->max_pulse_width, - &rxclk_divider); - } - o->max_pulse_width = p->max_pulse_width; - atomic_set(&ir_state->rxclk_divider, rxclk_divider); - - p->noise_filter_min_width = - filter_rx_s_min_width(c, p->noise_filter_min_width); - o->noise_filter_min_width = p->noise_filter_min_width; - - p->resolution = clock_divider_to_resolution(rxclk_divider); - o->resolution = p->resolution; - - /* FIXME - make this dependent on resolution for better performance */ - control_rx_irq_watermark(c, RX_FIFO_HALF_FULL); - - control_rx_s_edge_detection(c, CNTRL_EDG_BOTH); - - o->invert_level = p->invert_level; - atomic_set(&ir_state->rx_invert, p->invert_level); - - o->interrupt_enable = p->interrupt_enable; - o->enable = p->enable; - if (p->enable) { - unsigned long flags; - - spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); - kfifo_reset(&ir_state->rx_kfifo); - spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); - if (p->interrupt_enable) - irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); - control_rx_enable(c, p->enable); - } - - mutex_unlock(&ir_state->rx_params_lock); - return 0; -} - -/* Transmitter */ -static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, - ssize_t *num) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - -#if 0 - /* - * FIXME - the code below is an incomplete and untested sketch of what - * may need to be done. The critical part is to get 4 (or 8) pulses - * from the tx_kfifo, or converted from ns to the proper units from the - * input, and push them off to the hardware Tx FIFO right away, if the - * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in - * a less critical timeframe. Also watch out for overruning the - * tx_kfifo - don't let it happen and let the caller know not all his - * pulses were written. - */ - u32 *ns_pulse = (u32 *) buf; - unsigned int n; - u32 fifo_pulse[FIFO_TX_DEPTH]; - u32 mark; - - /* Compute how much we can fit in the tx kfifo */ - n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo); - n = min(n, (unsigned int) count); - n /= sizeof(u32); - - /* FIXME - turn on Tx Fifo service interrupt - * check hardware fifo level, and other stuff - */ - for (i = 0; i < n; ) { - for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) { - mark = ns_pulse[i] & LEVEL_MASK; - fifo_pulse[j] = ns_to_pulse_width_count( - ns_pulse[i] & - ~LEVEL_MASK, - ir_state->txclk_divider); - if (mark) - fifo_pulse[j] &= FIFO_RXTX_LVL; - i++; - } - kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse, - j * sizeof(u32)); - } - *num = n * sizeof(u32); -#else - /* For now enable the Tx FIFO Service interrupt & pretend we did work */ - irqenable_tx(sd, IRQEN_TSE); - *num = count; -#endif - return 0; -} - -static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - - mutex_lock(&ir_state->tx_params_lock); - memcpy(p, &ir_state->tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - mutex_unlock(&ir_state->tx_params_lock); - return 0; -} - -static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - - if (ir_state == NULL) - return -ENODEV; - - c = ir_state->c; - mutex_lock(&ir_state->tx_params_lock); - - /* Disable or slow down all IR Tx circuits and counters */ - irqenable_tx(sd, 0); - control_tx_enable(c, false); - control_tx_modulation_enable(c, false); - cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD); - - ir_state->tx_params.shutdown = true; - - mutex_unlock(&ir_state->tx_params_lock); - return 0; -} - -static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd, - struct v4l2_subdev_ir_parameters *p) -{ - struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; - struct v4l2_subdev_ir_parameters *o; - u16 txclk_divider; - - if (ir_state == NULL) - return -ENODEV; - - if (p->shutdown) - return cx25840_ir_tx_shutdown(sd); - - if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) - return -ENOSYS; - - c = ir_state->c; - o = &ir_state->tx_params; - mutex_lock(&ir_state->tx_params_lock); - - o->shutdown = p->shutdown; - - p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; - o->mode = p->mode; - - p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); - o->bytes_per_data_element = p->bytes_per_data_element; - - /* Before we tweak the hardware, we have to disable the transmitter */ - irqenable_tx(sd, 0); - control_tx_enable(c, false); - - control_tx_modulation_enable(c, p->modulation); - o->modulation = p->modulation; - - if (p->modulation) { - p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq, - &txclk_divider); - o->carrier_freq = p->carrier_freq; - - p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle); - o->duty_cycle = p->duty_cycle; - - p->max_pulse_width = - (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); - } else { - p->max_pulse_width = - txclk_tx_s_max_pulse_width(c, p->max_pulse_width, - &txclk_divider); - } - o->max_pulse_width = p->max_pulse_width; - atomic_set(&ir_state->txclk_divider, txclk_divider); - - p->resolution = clock_divider_to_resolution(txclk_divider); - o->resolution = p->resolution; - - /* FIXME - make this dependent on resolution for better performance */ - control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY); - - control_tx_polarity_invert(c, p->invert_carrier_sense); - o->invert_carrier_sense = p->invert_carrier_sense; - - /* - * FIXME: we don't have hardware help for IO pin level inversion - * here like we have on the CX23888. - * Act on this with some mix of logical inversion of data levels, - * carrier polarity, and carrier duty cycle. - */ - o->invert_level = p->invert_level; - - o->interrupt_enable = p->interrupt_enable; - o->enable = p->enable; - if (p->enable) { - /* reset tx_fifo here */ - if (p->interrupt_enable) - irqenable_tx(sd, IRQEN_TSE); - control_tx_enable(c, p->enable); - } - - mutex_unlock(&ir_state->tx_params_lock); - return 0; -} - - -/* - * V4L2 Subdevice Core Ops support - */ -int cx25840_ir_log_status(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *c = state->c; - char *s; - int i, j; - u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr; - - /* The CX23888 chip doesn't have an IR controller on the A/V core */ - if (is_cx23888(state)) - return 0; - - cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); - txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD; - rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD; - cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC; - stats = cx25840_read4(c, CX25840_IR_STATS_REG); - irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); - if (is_cx23885(state) || is_cx23887(state)) - irqen ^= IRQEN_MSK; - filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF; - - v4l2_info(sd, "IR Receiver:\n"); - v4l2_info(sd, "\tEnabled: %s\n", - cntrl & CNTRL_RXE ? "yes" : "no"); - v4l2_info(sd, "\tDemodulation from a carrier: %s\n", - cntrl & CNTRL_DMD ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO: %s\n", - cntrl & CNTRL_RFE ? "enabled" : "disabled"); - switch (cntrl & CNTRL_EDG) { - case CNTRL_EDG_NONE: - s = "disabled"; - break; - case CNTRL_EDG_FALL: - s = "falling edge"; - break; - case CNTRL_EDG_RISE: - s = "rising edge"; - break; - case CNTRL_EDG_BOTH: - s = "rising & falling edges"; - break; - default: - s = "??? edge"; - break; - } - v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); - v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", - cntrl & CNTRL_R ? "not loaded" : "overflow marker"); - v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", - cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); - v4l2_info(sd, "\tLoopback mode: %s\n", - cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); - if (cntrl & CNTRL_DMD) { - v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", - clock_divider_to_carrier_freq(rxclk)); - switch (cntrl & CNTRL_WIN) { - case CNTRL_WIN_3_3: - i = 3; - j = 3; - break; - case CNTRL_WIN_4_3: - i = 4; - j = 3; - break; - case CNTRL_WIN_3_4: - i = 3; - j = 4; - break; - case CNTRL_WIN_4_4: - i = 4; - j = 4; - break; - default: - i = 0; - j = 0; - break; - } - v4l2_info(sd, "\tNext carrier edge window: 16 clocks " - "-%1d/+%1d, %u to %u Hz\n", i, j, - clock_divider_to_freq(rxclk, 16 + j), - clock_divider_to_freq(rxclk, 16 - i)); - } - v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", - pulse_width_count_to_us(FIFO_RXTX, rxclk), - pulse_width_count_to_ns(FIFO_RXTX, rxclk)); - v4l2_info(sd, "\tLow pass filter: %s\n", - filtr ? "enabled" : "disabled"); - if (filtr) - v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " - "%u ns\n", - lpf_count_to_us(filtr), - lpf_count_to_ns(filtr)); - v4l2_info(sd, "\tPulse width timer timed-out: %s\n", - stats & STATS_RTO ? "yes" : "no"); - v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", - irqen & IRQEN_RTE ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO overrun: %s\n", - stats & STATS_ROR ? "yes" : "no"); - v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", - irqen & IRQEN_ROE ? "enabled" : "disabled"); - v4l2_info(sd, "\tBusy: %s\n", - stats & STATS_RBY ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service requested: %s\n", - stats & STATS_RSR ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service request interrupt: %s\n", - irqen & IRQEN_RSE ? "enabled" : "disabled"); - - v4l2_info(sd, "IR Transmitter:\n"); - v4l2_info(sd, "\tEnabled: %s\n", - cntrl & CNTRL_TXE ? "yes" : "no"); - v4l2_info(sd, "\tModulation onto a carrier: %s\n", - cntrl & CNTRL_MOD ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO: %s\n", - cntrl & CNTRL_TFE ? "enabled" : "disabled"); - v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", - cntrl & CNTRL_TIC ? "not empty" : "half full or less"); - v4l2_info(sd, "\tCarrier polarity: %s\n", - cntrl & CNTRL_CPL ? "space:burst mark:noburst" - : "space:noburst mark:burst"); - if (cntrl & CNTRL_MOD) { - v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", - clock_divider_to_carrier_freq(txclk)); - v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", - cduty + 1); - } - v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", - pulse_width_count_to_us(FIFO_RXTX, txclk), - pulse_width_count_to_ns(FIFO_RXTX, txclk)); - v4l2_info(sd, "\tBusy: %s\n", - stats & STATS_TBY ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service requested: %s\n", - stats & STATS_TSR ? "yes" : "no"); - v4l2_info(sd, "\tFIFO service request interrupt: %s\n", - irqen & IRQEN_TSE ? "enabled" : "disabled"); - - return 0; -} - - -const struct v4l2_subdev_ir_ops cx25840_ir_ops = { - .rx_read = cx25840_ir_rx_read, - .rx_g_parameters = cx25840_ir_rx_g_parameters, - .rx_s_parameters = cx25840_ir_rx_s_parameters, - - .tx_write = cx25840_ir_tx_write, - .tx_g_parameters = cx25840_ir_tx_g_parameters, - .tx_s_parameters = cx25840_ir_tx_s_parameters, -}; - - -static const struct v4l2_subdev_ir_parameters default_rx_params = { - .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), - .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, - - .enable = false, - .interrupt_enable = false, - .shutdown = true, - - .modulation = true, - .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */ - - /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ - /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ - .noise_filter_min_width = 333333, /* ns */ - .carrier_range_lower = 35000, - .carrier_range_upper = 37000, - .invert_level = false, -}; - -static const struct v4l2_subdev_ir_parameters default_tx_params = { - .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), - .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, - - .enable = false, - .interrupt_enable = false, - .shutdown = true, - - .modulation = true, - .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ - .duty_cycle = 25, /* 25 % - RC-5 carrier */ - .invert_level = false, - .invert_carrier_sense = false, -}; - -int cx25840_ir_probe(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct cx25840_ir_state *ir_state; - struct v4l2_subdev_ir_parameters default_params; - - /* Only init the IR controller for the CX2388[57] AV Core for now */ - if (!(is_cx23885(state) || is_cx23887(state))) - return 0; - - ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL); - if (ir_state == NULL) - return -ENOMEM; - - spin_lock_init(&ir_state->rx_kfifo_lock); - if (kfifo_alloc(&ir_state->rx_kfifo, - CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) { - kfree(ir_state); - return -ENOMEM; - } - - ir_state->c = state->c; - state->ir_state = ir_state; - - /* Ensure no interrupts arrive yet */ - if (is_cx23885(state) || is_cx23887(state)) - cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK); - else - cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); - - mutex_init(&ir_state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); - - mutex_init(&ir_state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); - v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); - - return 0; -} - -int cx25840_ir_remove(struct v4l2_subdev *sd) -{ - struct cx25840_state *state = to_state(sd); - struct cx25840_ir_state *ir_state = to_ir_state(sd); - - if (ir_state == NULL) - return -ENODEV; - - cx25840_ir_rx_shutdown(sd); - cx25840_ir_tx_shutdown(sd); - - kfifo_free(&ir_state->rx_kfifo); - kfree(ir_state); - state->ir_state = NULL; - return 0; -} diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c deleted file mode 100644 index 64a4004f8a97..000000000000 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ /dev/null @@ -1,256 +0,0 @@ -/* cx25840 VBI functions - * - * This program is free software; 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 -#include -#include -#include - -#include "cx25840-core.h" - -static int odd_parity(u8 c) -{ - c ^= (c >> 4); - c ^= (c >> 2); - c ^= (c >> 1); - - return c & 1; -} - -static int decode_vps(u8 * dst, u8 * p) -{ - static const u8 biphase_tbl[] = { - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, - 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, - 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, - 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, - 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, - 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, - 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - }; - - u8 c, err = 0; - int i; - - for (i = 0; i < 2 * 13; i += 2) { - err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; - c = (biphase_tbl[p[i + 1]] & 0xf) | - ((biphase_tbl[p[i]] & 0xf) << 4); - dst[i / 2] = c; - } - - return err & 0xf0; -} - -int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - static const u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_WSS_625, 0, /* 4 */ - V4L2_SLICED_CAPTION_525, /* 6 */ - 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ - 0, 0, 0, 0 - }; - int is_pal = !(state->std & V4L2_STD_525_60); - int i; - - memset(svbi, 0, sizeof(*svbi)); - /* we're done if raw VBI is active */ - if ((cx25840_read(client, 0x404) & 0x10) == 0) - return 0; - - if (is_pal) { - for (i = 7; i <= 23; i++) { - u8 v = cx25840_read(client, 0x424 + i - 7); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } else { - for (i = 10; i <= 21; i++) { - u8 v = cx25840_read(client, 0x424 + i - 10); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } - return 0; -} - -int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; - - /* Setup standard */ - cx25840_std_setup(client); - - /* VBI Offset */ - cx25840_write(client, 0x47f, vbi_offset); - cx25840_write(client, 0x404, 0x2e); - return 0; -} - -int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct cx25840_state *state = to_state(sd); - int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; - int i, x; - u8 lcr[24]; - - for (x = 0; x <= 23; x++) - lcr[x] = 0x00; - - /* Setup standard */ - cx25840_std_setup(client); - - /* Sliced VBI */ - cx25840_write(client, 0x404, 0x32); /* Ancillary data */ - cx25840_write(client, 0x406, 0x13); - cx25840_write(client, 0x47f, vbi_offset); - - if (is_pal) { - for (i = 0; i <= 6; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } else { - for (i = 0; i <= 9; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - - for (i = 22; i <= 23; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } - - for (i = 7; i <= 23; i++) { - for (x = 0; x <= 1; x++) { - switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 6 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 9 << (4 * x); - break; - } - } - } - - if (is_pal) { - for (x = 1, i = 0x424; i <= 0x434; i++, x++) - cx25840_write(client, i, lcr[6 + x]); - } else { - for (x = 1, i = 0x424; i <= 0x430; i++, x++) - cx25840_write(client, i, lcr[9 + x]); - for (i = 0x431; i <= 0x434; i++) - cx25840_write(client, i, 0); - } - - cx25840_write(client, 0x43c, 0x16); - cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22); - return 0; -} - -int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) -{ - struct cx25840_state *state = to_state(sd); - u8 *p = vbi->p; - int id1, id2, l, err = 0; - - if (p[0] || p[1] != 0xff || p[2] != 0xff || - (p[3] != 0x55 && p[3] != 0x91)) { - vbi->line = vbi->type = 0; - return 0; - } - - p += 4; - id1 = p[-1]; - id2 = p[0] & 0xf; - l = p[2] & 0x3f; - l += state->vbi_line_offset; - p += 4; - - switch (id2) { - case 1: - id2 = V4L2_SLICED_TELETEXT_B; - break; - case 4: - id2 = V4L2_SLICED_WSS_625; - break; - case 6: - id2 = V4L2_SLICED_CAPTION_525; - err = !odd_parity(p[0]) || !odd_parity(p[1]); - break; - case 9: - id2 = V4L2_SLICED_VPS; - if (decode_vps(p, p) != 0) - err = 1; - break; - default: - id2 = 0; - err = 1; - break; - } - - vbi->type = err ? 0 : id2; - vbi->line = err ? 0 : l; - vbi->is_second_field = err ? 0 : (id1 == 0x55); - vbi->p = p; - return 0; -} diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c deleted file mode 100644 index 04f192a0398a..000000000000 --- a/drivers/media/video/ir-kbd-i2c.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - * - * keyboard input driver for i2c IR remote controls - * - * Copyright (c) 2000-2003 Gerd Knorr - * modified for PixelView (BT878P+W/FM) by - * Michal Kochanowicz - * Christoph Bartelmus - * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by - * Ulrich Mueller - * modified for em2820 based USB TV tuners by - * Markus Rechberger - * modified for DViCO Fusion HDTV 5 RT GOLD by - * Chaogui Zhang - * modified for MSI TV@nywhere Plus by - * Henry Wong - * Mark Schultz - * Brian Rogers - * modified for AVerMedia Cardbus by - * Oldrich Jedlicka - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* ----------------------------------------------------------------------- */ -/* insmod parameters */ - -static int debug; -module_param(debug, int, 0644); /* debug level (0,1,2) */ - - -#define MODULE_NAME "ir-kbd-i2c" -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg) - -/* ----------------------------------------------------------------------- */ - -static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, - int size, int offset) -{ - unsigned char buf[6]; - int start, range, toggle, dev, code, ircode; - - /* poll IR chip */ - if (size != i2c_master_recv(ir->c, buf, size)) - return -EIO; - - /* split rc5 data block ... */ - start = (buf[offset] >> 7) & 1; - range = (buf[offset] >> 6) & 1; - toggle = (buf[offset] >> 5) & 1; - dev = buf[offset] & 0x1f; - code = (buf[offset+1] >> 2) & 0x3f; - - /* rc5 has two start bits - * the first bit must be one - * the second bit defines the command range (1 = 0-63, 0 = 64 - 127) - */ - if (!start) - /* no key pressed */ - return 0; - /* - * Hauppauge remotes (black/silver) always use - * specific device ids. If we do not filter the - * device ids then messages destined for devices - * such as TVs (id=0) will get through causing - * mis-fired events. - * - * We also filter out invalid key presses which - * produce annoying debug log entries. - */ - ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; - if ((ircode & 0x1fff)==0x1fff) - /* invalid key press */ - return 0; - - if (!range) - code += 64; - - dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", - start, range, toggle, dev, code); - - /* return key */ - *ir_key = (dev << 8) | code; - *ir_raw = ircode; - return 1; -} - -static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); -} - -static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - int ret; - unsigned char buf[1] = { 0 }; - - /* - * This is the same apparent "are you ready?" poll command observed - * watching Windows driver traffic and implemented in lirc_zilog. With - * this added, we get far saner remote behavior with z8 chips on usb - * connected devices, even with the default polling interval of 100ms. - */ - ret = i2c_master_send(ir->c, buf, 1); - if (ret != 1) - return (ret < 0) ? ret : -EINVAL; - - return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); -} - -static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - unsigned char b; - - /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { - dprintk(1,"read error\n"); - return -EIO; - } - *ir_key = b; - *ir_raw = b; - return 1; -} - -static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - unsigned char buf[4]; - - /* poll IR chip */ - if (4 != i2c_master_recv(ir->c, buf, 4)) { - dprintk(1,"read error\n"); - return -EIO; - } - - if(buf[0] !=0 || buf[1] !=0 || buf[2] !=0 || buf[3] != 0) - dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __func__, - buf[0], buf[1], buf[2], buf[3]); - - /* no key pressed or signal from other ir remote */ - if(buf[0] != 0x1 || buf[1] != 0xfe) - return 0; - - *ir_key = buf[2]; - *ir_raw = (buf[2] << 8) | buf[3]; - - return 1; -} - -static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - unsigned char b; - - /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { - dprintk(1,"read error\n"); - return -EIO; - } - - /* it seems that 0xFE indicates that a button is still hold - down, while 0xff indicates that no button is hold - down. 0xfe sequences are sometimes interrupted by 0xFF */ - - dprintk(2,"key %02x\n", b); - - if (b == 0xff) - return 0; - - if (b == 0xfe) - /* keep old data */ - return 1; - - *ir_key = b; - *ir_raw = b; - return 1; -} - -static int get_key_avermedia_cardbus(struct IR_i2c *ir, - u32 *ir_key, u32 *ir_raw) -{ - unsigned char subaddr, key, keygroup; - struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, - .buf = &subaddr, .len = 1}, - { .addr = ir->c->addr, .flags = I2C_M_RD, - .buf = &key, .len = 1} }; - subaddr = 0x0d; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - dprintk(1, "read error\n"); - return -EIO; - } - - if (key == 0xff) - return 0; - - subaddr = 0x0b; - msg[1].buf = &keygroup; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - dprintk(1, "read error\n"); - return -EIO; - } - - if (keygroup == 0xff) - return 0; - - dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); - if (keygroup < 2 || keygroup > 3) { - /* Only a warning */ - dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n", - keygroup, key); - } - key |= (keygroup & 1) << 6; - - *ir_key = key; - *ir_raw = key; - return 1; -} - -/* ----------------------------------------------------------------------- */ - -static int ir_key_poll(struct IR_i2c *ir) -{ - static u32 ir_key, ir_raw; - int rc; - - dprintk(3, "%s\n", __func__); - rc = ir->get_key(ir, &ir_key, &ir_raw); - if (rc < 0) { - dprintk(2,"error\n"); - return rc; - } - - if (rc) { - dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); - rc_keydown(ir->rc, ir_key, 0); - } - return 0; -} - -static void ir_work(struct work_struct *work) -{ - int rc; - struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); - - rc = ir_key_poll(ir); - if (rc == -ENODEV) { - rc_unregister_device(ir->rc); - ir->rc = NULL; - return; - } - - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval)); -} - -/* ----------------------------------------------------------------------- */ - -static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - char *ir_codes = NULL; - const char *name = NULL; - u64 rc_type = RC_TYPE_UNKNOWN; - struct IR_i2c *ir; - struct rc_dev *rc = NULL; - struct i2c_adapter *adap = client->adapter; - unsigned short addr = client->addr; - int err; - - ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL); - if (!ir) - return -ENOMEM; - - ir->c = client; - ir->polling_interval = DEFAULT_POLLING_INTERVAL; - i2c_set_clientdata(client, ir); - - switch(addr) { - case 0x64: - name = "Pixelview"; - ir->get_key = get_key_pixelview; - rc_type = RC_TYPE_OTHER; - ir_codes = RC_MAP_EMPTY; - break; - case 0x18: - case 0x1f: - case 0x1a: - name = "Hauppauge"; - ir->get_key = get_key_haup; - rc_type = RC_TYPE_RC5; - ir_codes = RC_MAP_HAUPPAUGE; - break; - case 0x30: - name = "KNC One"; - ir->get_key = get_key_knc1; - rc_type = RC_TYPE_OTHER; - ir_codes = RC_MAP_EMPTY; - break; - case 0x6b: - name = "FusionHDTV"; - ir->get_key = get_key_fusionhdtv; - rc_type = RC_TYPE_RC5; - ir_codes = RC_MAP_FUSIONHDTV_MCE; - break; - case 0x40: - name = "AVerMedia Cardbus remote"; - ir->get_key = get_key_avermedia_cardbus; - rc_type = RC_TYPE_OTHER; - ir_codes = RC_MAP_AVERMEDIA_CARDBUS; - break; - case 0x71: - name = "Hauppauge/Zilog Z8"; - ir->get_key = get_key_haup_xvr; - rc_type = RC_TYPE_RC5; - ir_codes = RC_MAP_HAUPPAUGE; - break; - } - - /* Let the caller override settings */ - if (client->dev.platform_data) { - const struct IR_i2c_init_data *init_data = - client->dev.platform_data; - - ir_codes = init_data->ir_codes; - rc = init_data->rc_dev; - - name = init_data->name; - if (init_data->type) - rc_type = init_data->type; - - if (init_data->polling_interval) - ir->polling_interval = init_data->polling_interval; - - switch (init_data->internal_get_key_func) { - case IR_KBD_GET_KEY_CUSTOM: - /* The bridge driver provided us its own function */ - ir->get_key = init_data->get_key; - break; - case IR_KBD_GET_KEY_PIXELVIEW: - ir->get_key = get_key_pixelview; - break; - case IR_KBD_GET_KEY_HAUP: - ir->get_key = get_key_haup; - break; - case IR_KBD_GET_KEY_KNC1: - ir->get_key = get_key_knc1; - break; - case IR_KBD_GET_KEY_FUSIONHDTV: - ir->get_key = get_key_fusionhdtv; - break; - case IR_KBD_GET_KEY_HAUP_XVR: - ir->get_key = get_key_haup_xvr; - break; - case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: - ir->get_key = get_key_avermedia_cardbus; - break; - } - } - - if (!rc) { - /* - * If platform_data doesn't specify rc_dev, initilize it - * internally - */ - rc = rc_allocate_device(); - if (!rc) { - err = -ENOMEM; - goto err_out_free; - } - } - ir->rc = rc; - - /* Make sure we are all setup before going on */ - if (!name || !ir->get_key || !rc_type || !ir_codes) { - dprintk(1, ": Unsupported device at address 0x%02x\n", - addr); - err = -ENODEV; - goto err_out_free; - } - - /* Sets name */ - snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); - ir->ir_codes = ir_codes; - - snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", - dev_name(&adap->dev), - dev_name(&client->dev)); - - /* - * Initialize input_dev fields - * It doesn't make sense to allow overriding them via platform_data - */ - rc->input_id.bustype = BUS_I2C; - rc->input_phys = ir->phys; - rc->input_name = ir->name; - - /* - * Initialize the other fields of rc_dev - */ - rc->map_name = ir->ir_codes; - rc->allowed_protos = rc_type; - if (!rc->driver_name) - rc->driver_name = MODULE_NAME; - - err = rc_register_device(rc); - if (err) - goto err_out_free; - - printk(MODULE_NAME ": %s detected at %s [%s]\n", - ir->name, ir->phys, adap->name); - - /* start polling via eventd */ - INIT_DELAYED_WORK(&ir->work, ir_work); - schedule_delayed_work(&ir->work, 0); - - return 0; - - err_out_free: - /* Only frees rc if it were allocated internally */ - rc_free_device(rc); - kfree(ir); - return err; -} - -static int ir_remove(struct i2c_client *client) -{ - struct IR_i2c *ir = i2c_get_clientdata(client); - - /* kill outstanding polls */ - cancel_delayed_work_sync(&ir->work); - - /* unregister device */ - if (ir->rc) - rc_unregister_device(ir->rc); - - /* free memory */ - kfree(ir); - return 0; -} - -static const struct i2c_device_id ir_kbd_id[] = { - /* Generic entry for any IR receiver */ - { "ir_video", 0 }, - /* IR device specific entries should be added here */ - { "ir_rx_z8f0811_haup", 0 }, - { "ir_rx_z8f0811_hdpvr", 0 }, - { } -}; - -static struct i2c_driver ir_kbd_driver = { - .driver = { - .name = "ir-kbd-i2c", - }, - .probe = ir_probe, - .remove = ir_remove, - .id_table = ir_kbd_id, -}; - -module_i2c_driver(ir_kbd_driver); - -/* ----------------------------------------------------------------------- */ - -MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); -MODULE_DESCRIPTION("input driver for i2c IR remote controls"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c deleted file mode 100644 index ee7ca2dcca2f..000000000000 --- a/drivers/media/video/ks0127.c +++ /dev/null @@ -1,724 +0,0 @@ -/* - * Video Capture Driver (Video for Linux 1/2) - * for the Matrox Marvel G200,G400 and Rainbow Runner-G series - * - * This module is an interface to the KS0127 video decoder chip. - * - * Copyright (C) 1999 Ryan Drake - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - ***************************************************************************** - * - * Modified and extended by - * Mike Bernson - * Gerard v.d. Horst - * Leon van Stuivenberg - * Gernot Ziegler - * - * Version History: - * V1.0 Ryan Drake Initial version by Ryan Drake - * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ks0127.h" - -MODULE_DESCRIPTION("KS0127 video decoder driver"); -MODULE_AUTHOR("Ryan Drake"); -MODULE_LICENSE("GPL"); - -/* Addresses */ -#define I2C_KS0127_ADDON 0xD8 -#define I2C_KS0127_ONBOARD 0xDA - - -/* ks0127 control registers */ -#define KS_STAT 0x00 -#define KS_CMDA 0x01 -#define KS_CMDB 0x02 -#define KS_CMDC 0x03 -#define KS_CMDD 0x04 -#define KS_HAVB 0x05 -#define KS_HAVE 0x06 -#define KS_HS1B 0x07 -#define KS_HS1E 0x08 -#define KS_HS2B 0x09 -#define KS_HS2E 0x0a -#define KS_AGC 0x0b -#define KS_HXTRA 0x0c -#define KS_CDEM 0x0d -#define KS_PORTAB 0x0e -#define KS_LUMA 0x0f -#define KS_CON 0x10 -#define KS_BRT 0x11 -#define KS_CHROMA 0x12 -#define KS_CHROMB 0x13 -#define KS_DEMOD 0x14 -#define KS_SAT 0x15 -#define KS_HUE 0x16 -#define KS_VERTIA 0x17 -#define KS_VERTIB 0x18 -#define KS_VERTIC 0x19 -#define KS_HSCLL 0x1a -#define KS_HSCLH 0x1b -#define KS_VSCLL 0x1c -#define KS_VSCLH 0x1d -#define KS_OFMTA 0x1e -#define KS_OFMTB 0x1f -#define KS_VBICTL 0x20 -#define KS_CCDAT2 0x21 -#define KS_CCDAT1 0x22 -#define KS_VBIL30 0x23 -#define KS_VBIL74 0x24 -#define KS_VBIL118 0x25 -#define KS_VBIL1512 0x26 -#define KS_TTFRAM 0x27 -#define KS_TESTA 0x28 -#define KS_UVOFFH 0x29 -#define KS_UVOFFL 0x2a -#define KS_UGAIN 0x2b -#define KS_VGAIN 0x2c -#define KS_VAVB 0x2d -#define KS_VAVE 0x2e -#define KS_CTRACK 0x2f -#define KS_POLCTL 0x30 -#define KS_REFCOD 0x31 -#define KS_INVALY 0x32 -#define KS_INVALU 0x33 -#define KS_INVALV 0x34 -#define KS_UNUSEY 0x35 -#define KS_UNUSEU 0x36 -#define KS_UNUSEV 0x37 -#define KS_USRSAV 0x38 -#define KS_USREAV 0x39 -#define KS_SHS1A 0x3a -#define KS_SHS1B 0x3b -#define KS_SHS1C 0x3c -#define KS_CMDE 0x3d -#define KS_VSDEL 0x3e -#define KS_CMDF 0x3f -#define KS_GAMMA0 0x40 -#define KS_GAMMA1 0x41 -#define KS_GAMMA2 0x42 -#define KS_GAMMA3 0x43 -#define KS_GAMMA4 0x44 -#define KS_GAMMA5 0x45 -#define KS_GAMMA6 0x46 -#define KS_GAMMA7 0x47 -#define KS_GAMMA8 0x48 -#define KS_GAMMA9 0x49 -#define KS_GAMMA10 0x4a -#define KS_GAMMA11 0x4b -#define KS_GAMMA12 0x4c -#define KS_GAMMA13 0x4d -#define KS_GAMMA14 0x4e -#define KS_GAMMA15 0x4f -#define KS_GAMMA16 0x50 -#define KS_GAMMA17 0x51 -#define KS_GAMMA18 0x52 -#define KS_GAMMA19 0x53 -#define KS_GAMMA20 0x54 -#define KS_GAMMA21 0x55 -#define KS_GAMMA22 0x56 -#define KS_GAMMA23 0x57 -#define KS_GAMMA24 0x58 -#define KS_GAMMA25 0x59 -#define KS_GAMMA26 0x5a -#define KS_GAMMA27 0x5b -#define KS_GAMMA28 0x5c -#define KS_GAMMA29 0x5d -#define KS_GAMMA30 0x5e -#define KS_GAMMA31 0x5f -#define KS_GAMMAD0 0x60 -#define KS_GAMMAD1 0x61 -#define KS_GAMMAD2 0x62 -#define KS_GAMMAD3 0x63 -#define KS_GAMMAD4 0x64 -#define KS_GAMMAD5 0x65 -#define KS_GAMMAD6 0x66 -#define KS_GAMMAD7 0x67 -#define KS_GAMMAD8 0x68 -#define KS_GAMMAD9 0x69 -#define KS_GAMMAD10 0x6a -#define KS_GAMMAD11 0x6b -#define KS_GAMMAD12 0x6c -#define KS_GAMMAD13 0x6d -#define KS_GAMMAD14 0x6e -#define KS_GAMMAD15 0x6f -#define KS_GAMMAD16 0x70 -#define KS_GAMMAD17 0x71 -#define KS_GAMMAD18 0x72 -#define KS_GAMMAD19 0x73 -#define KS_GAMMAD20 0x74 -#define KS_GAMMAD21 0x75 -#define KS_GAMMAD22 0x76 -#define KS_GAMMAD23 0x77 -#define KS_GAMMAD24 0x78 -#define KS_GAMMAD25 0x79 -#define KS_GAMMAD26 0x7a -#define KS_GAMMAD27 0x7b -#define KS_GAMMAD28 0x7c -#define KS_GAMMAD29 0x7d -#define KS_GAMMAD30 0x7e -#define KS_GAMMAD31 0x7f - - -/**************************************************************************** -* mga_dev : represents one ks0127 chip. -****************************************************************************/ - -struct adjust { - int contrast; - int bright; - int hue; - int ugain; - int vgain; -}; - -struct ks0127 { - struct v4l2_subdev sd; - v4l2_std_id norm; - int ident; - u8 regs[256]; -}; - -static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ks0127, sd); -} - - -static int debug; /* insmod parameter */ - -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug output"); - -static u8 reg_defaults[64]; - -static void init_reg_defaults(void) -{ - static int initialized; - u8 *table = reg_defaults; - - if (initialized) - return; - initialized = 1; - - table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */ - table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */ - table[KS_CMDC] = 0x00; /* Test options */ - /* clock & input select, write 1 to PORTA */ - table[KS_CMDD] = 0x01; - table[KS_HAVB] = 0x00; /* HAV Start Control */ - table[KS_HAVE] = 0x00; /* HAV End Control */ - table[KS_HS1B] = 0x10; /* HS1 Start Control */ - table[KS_HS1E] = 0x00; /* HS1 End Control */ - table[KS_HS2B] = 0x00; /* HS2 Start Control */ - table[KS_HS2E] = 0x00; /* HS2 End Control */ - table[KS_AGC] = 0x53; /* Manual setting for AGC */ - table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */ - table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */ - table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */ - table[KS_LUMA] = 0x01; /* Luma control */ - table[KS_CON] = 0x00; /* Contrast Control */ - table[KS_BRT] = 0x00; /* Brightness Control */ - table[KS_CHROMA] = 0x2a; /* Chroma control A */ - table[KS_CHROMB] = 0x90; /* Chroma control B */ - table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */ - table[KS_SAT] = 0x00; /* Color Saturation Control*/ - table[KS_HUE] = 0x00; /* Hue Control */ - table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */ - /* Vertical Processing Control B, luma 1 line delayed */ - table[KS_VERTIB] = 0x12; - table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */ - table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */ - table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */ - table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */ - table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */ - /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */ - table[KS_OFMTA] = 0x30; - table[KS_OFMTB] = 0x00; /* Output Control B */ - /* VBI Decoder Control; 4bit fmt: avoid Y overflow */ - table[KS_VBICTL] = 0x5d; - table[KS_CCDAT2] = 0x00; /* Read Only register */ - table[KS_CCDAT1] = 0x00; /* Read Only register */ - table[KS_VBIL30] = 0xa8; /* VBI data decoding options */ - table[KS_VBIL74] = 0xaa; /* VBI data decoding options */ - table[KS_VBIL118] = 0x2a; /* VBI data decoding options */ - table[KS_VBIL1512] = 0x00; /* VBI data decoding options */ - table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */ - table[KS_TESTA] = 0x00; /* test register, shouldn't be written */ - table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */ - table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */ - table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */ - table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */ - table[KS_VAVB] = 0x07; /* VAV Begin */ - table[KS_VAVE] = 0x00; /* VAV End */ - table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */ - table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */ - table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */ - table[KS_INVALY] = 0x10; /* Invalid Y Code */ - table[KS_INVALU] = 0x80; /* Invalid U Code */ - table[KS_INVALV] = 0x80; /* Invalid V Code */ - table[KS_UNUSEY] = 0x10; /* Unused Y Code */ - table[KS_UNUSEU] = 0x80; /* Unused U Code */ - table[KS_UNUSEV] = 0x80; /* Unused V Code */ - table[KS_USRSAV] = 0x00; /* reserved */ - table[KS_USREAV] = 0x00; /* reserved */ - table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */ - /* User Defined SHS1 B, ALT656=1 on 0127B */ - table[KS_SHS1B] = 0x80; - table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */ - table[KS_CMDE] = 0x00; /* Command Register E */ - table[KS_VSDEL] = 0x00; /* VS Delay Control */ - /* Command Register F, update -immediately- */ - /* (there might come no vsync)*/ - table[KS_CMDF] = 0x02; -} - - -/* We need to manually read because of a bug in the KS0127 chip. - * - * An explanation from kayork@mail.utexas.edu: - * - * During I2C reads, the KS0127 only samples for a stop condition - * during the place where the acknowledge bit should be. Any standard - * I2C implementation (correctly) throws in another clock transition - * at the 9th bit, and the KS0127 will not recognize the stop condition - * and will continue to clock out data. - * - * So we have to do the read ourself. Big deal. - * workaround in i2c-algo-bit - */ - - -static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - char val = 0; - struct i2c_msg msgs[] = { - { client->addr, 0, sizeof(reg), ® }, - { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val } - }; - int ret; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - v4l2_dbg(1, debug, sd, "read error\n"); - - return val; -} - - -static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ks0127 *ks = to_ks0127(sd); - char msg[] = { reg, val }; - - if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg)) - v4l2_dbg(1, debug, sd, "write error\n"); - - ks->regs[reg] = val; -} - - -/* generic bit-twiddling */ -static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v) -{ - struct ks0127 *ks = to_ks0127(sd); - - u8 val = ks->regs[reg]; - val = (val & and_v) | or_v; - ks0127_write(sd, reg, val); -} - - - -/**************************************************************************** -* ks0127 private api -****************************************************************************/ -static void ks0127_init(struct v4l2_subdev *sd) -{ - struct ks0127 *ks = to_ks0127(sd); - u8 *table = reg_defaults; - int i; - - ks->ident = V4L2_IDENT_KS0127; - - v4l2_dbg(1, debug, sd, "reset\n"); - msleep(1); - - /* initialize all registers to known values */ - /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */ - - for (i = 1; i < 33; i++) - ks0127_write(sd, i, table[i]); - - for (i = 35; i < 40; i++) - ks0127_write(sd, i, table[i]); - - for (i = 41; i < 56; i++) - ks0127_write(sd, i, table[i]); - - for (i = 58; i < 64; i++) - ks0127_write(sd, i, table[i]); - - - if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) { - ks->ident = V4L2_IDENT_KS0122S; - v4l2_dbg(1, debug, sd, "ks0122s found\n"); - return; - } - - switch (ks0127_read(sd, KS_CMDE) & 0x0f) { - case 0: - v4l2_dbg(1, debug, sd, "ks0127 found\n"); - break; - - case 9: - ks->ident = V4L2_IDENT_KS0127B; - v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n"); - break; - - default: - v4l2_dbg(1, debug, sd, "unknown revision\n"); - break; - } -} - -static int ks0127_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct ks0127 *ks = to_ks0127(sd); - - switch (input) { - case KS_INPUT_COMPOSITE_1: - case KS_INPUT_COMPOSITE_2: - case KS_INPUT_COMPOSITE_3: - case KS_INPUT_COMPOSITE_4: - case KS_INPUT_COMPOSITE_5: - case KS_INPUT_COMPOSITE_6: - v4l2_dbg(1, debug, sd, - "s_routing %d: Composite\n", input); - /* autodetect 50/60 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); - /* VSE=0 */ - ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); - /* set input line */ - ks0127_and_or(sd, KS_CMDB, 0xb0, input); - /* non-freerunning mode */ - ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); - /* analog input */ - ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); - /* enable chroma demodulation */ - ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); - /* chroma trap, HYBWR=1 */ - ks0127_and_or(sd, KS_LUMA, 0x00, - (reg_defaults[KS_LUMA])|0x0c); - /* scaler fullbw, luma comb off */ - ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); - /* manual chroma comb .25 .5 .25 */ - ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90); - - /* chroma path delay */ - ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90); - - ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); - ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); - ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); - ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); - break; - - case KS_INPUT_SVIDEO_1: - case KS_INPUT_SVIDEO_2: - case KS_INPUT_SVIDEO_3: - v4l2_dbg(1, debug, sd, - "s_routing %d: S-Video\n", input); - /* autodetect 50/60 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); - /* VSE=0 */ - ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); - /* set input line */ - ks0127_and_or(sd, KS_CMDB, 0xb0, input); - /* non-freerunning mode */ - ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); - /* analog input */ - ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); - /* enable chroma demodulation */ - ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); - ks0127_and_or(sd, KS_LUMA, 0x00, - reg_defaults[KS_LUMA]); - /* disable luma comb */ - ks0127_and_or(sd, KS_VERTIA, 0x08, - (reg_defaults[KS_VERTIA]&0xf0)|0x01); - ks0127_and_or(sd, KS_VERTIC, 0x0f, - reg_defaults[KS_VERTIC]&0xf0); - - ks0127_and_or(sd, KS_CHROMB, 0x0f, - reg_defaults[KS_CHROMB]&0xf0); - - ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); - ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); - ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); - ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); - break; - - case KS_INPUT_YUV656: - v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n"); - if (ks->norm & V4L2_STD_525_60) - /* force 60 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03); - else - /* force 50 Hz */ - ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02); - - ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */ - /* set input line and VALIGN */ - ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40)); - /* freerunning mode, */ - /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ - ks0127_and_or(sd, KS_CMDC, 0x70, 0x87); - /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ - ks0127_and_or(sd, KS_CMDD, 0x03, 0x08); - /* disable chroma demodulation */ - ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30); - /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ - ks0127_and_or(sd, KS_LUMA, 0x00, 0x71); - ks0127_and_or(sd, KS_VERTIC, 0x0f, - reg_defaults[KS_VERTIC]&0xf0); - - /* scaler fullbw, luma comb off */ - ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); - - ks0127_and_or(sd, KS_CHROMB, 0x0f, - reg_defaults[KS_CHROMB]&0xf0); - - ks0127_and_or(sd, KS_CON, 0x00, 0x00); - ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */ - /* spec: 229 (e5) */ - ks0127_and_or(sd, KS_SAT, 0x00, 0xe8); - ks0127_and_or(sd, KS_HUE, 0x00, 0); - - ks0127_and_or(sd, KS_UGAIN, 0x00, 238); - ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00); - - /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ - ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f); - ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00); - break; - - default: - v4l2_dbg(1, debug, sd, - "s_routing: Unknown input %d\n", input); - break; - } - - /* hack: CDMLPF sometimes spontaneously switches on; */ - /* force back off */ - ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]); - return 0; -} - -static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct ks0127 *ks = to_ks0127(sd); - - /* Set to automatic SECAM/Fsc mode */ - ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); - - ks->norm = std; - if (std & V4L2_STD_NTSC) { - v4l2_dbg(1, debug, sd, - "s_std: NTSC_M\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); - } else if (std & V4L2_STD_PAL_N) { - v4l2_dbg(1, debug, sd, - "s_std: NTSC_N (fixme)\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); - } else if (std & V4L2_STD_PAL) { - v4l2_dbg(1, debug, sd, - "s_std: PAL_N\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); - } else if (std & V4L2_STD_PAL_M) { - v4l2_dbg(1, debug, sd, - "s_std: PAL_M (fixme)\n"); - ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); - } else if (std & V4L2_STD_SECAM) { - v4l2_dbg(1, debug, sd, - "s_std: SECAM\n"); - - /* set to secam autodetection */ - ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20); - ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); - schedule_timeout_interruptible(HZ/10+1); - - /* did it autodetect? */ - if (!(ks0127_read(sd, KS_DEMOD) & 0x40)) - /* force to secam mode */ - ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); - } else { - v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n", - (unsigned long long)std); - } - return 0; -} - -static int ks0127_s_stream(struct v4l2_subdev *sd, int enable) -{ - v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable); - if (enable) { - /* All output pins on */ - ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30); - /* Obey the OEN pin */ - ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00); - } else { - /* Video output pins off */ - ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00); - /* Ignore the OEN pin */ - ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80); - } - return 0; -} - -static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) -{ - int stat = V4L2_IN_ST_NO_SIGNAL; - u8 status; - v4l2_std_id std = V4L2_STD_ALL; - - status = ks0127_read(sd, KS_STAT); - if (!(status & 0x20)) /* NOVID not set */ - stat = 0; - if (!(status & 0x01)) /* CLOCK set */ - stat |= V4L2_IN_ST_NO_COLOR; - if ((status & 0x08)) /* PALDET set */ - std = V4L2_STD_PAL; - else - std = V4L2_STD_NTSC; - if (pstd) - *pstd = std; - if (pstatus) - *pstatus = stat; - return 0; -} - -static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - v4l2_dbg(1, debug, sd, "querystd\n"); - return ks0127_status(sd, NULL, std); -} - -static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - v4l2_dbg(1, debug, sd, "g_input_status\n"); - return ks0127_status(sd, status, NULL); -} - -static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ks0127 *ks = to_ks0127(sd); - - return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops ks0127_core_ops = { - .g_chip_ident = ks0127_g_chip_ident, - .s_std = ks0127_s_std, -}; - -static const struct v4l2_subdev_video_ops ks0127_video_ops = { - .s_routing = ks0127_s_routing, - .s_stream = ks0127_s_stream, - .querystd = ks0127_querystd, - .g_input_status = ks0127_g_input_status, -}; - -static const struct v4l2_subdev_ops ks0127_ops = { - .core = &ks0127_core_ops, - .video = &ks0127_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - - -static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct ks0127 *ks; - struct v4l2_subdev *sd; - - v4l_info(client, "%s chip found @ 0x%x (%s)\n", - client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board", - client->addr << 1, client->adapter->name); - - ks = kzalloc(sizeof(*ks), GFP_KERNEL); - if (ks == NULL) - return -ENOMEM; - sd = &ks->sd; - v4l2_i2c_subdev_init(sd, client, &ks0127_ops); - - /* power up */ - init_reg_defaults(); - ks0127_write(sd, KS_CMDA, 0x2c); - mdelay(10); - - /* reset the device */ - ks0127_init(sd); - return 0; -} - -static int ks0127_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */ - ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */ - kfree(to_ks0127(sd)); - return 0; -} - -static const struct i2c_device_id ks0127_id[] = { - { "ks0127", 0 }, - { "ks0127b", 0 }, - { "ks0122s", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ks0127_id); - -static struct i2c_driver ks0127_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "ks0127", - }, - .probe = ks0127_probe, - .remove = ks0127_remove, - .id_table = ks0127_id, -}; - -module_i2c_driver(ks0127_driver); diff --git a/drivers/media/video/ks0127.h b/drivers/media/video/ks0127.h deleted file mode 100644 index cb8abd5403b3..000000000000 --- a/drivers/media/video/ks0127.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Video Capture Driver ( Video for Linux 1/2 ) - * for the Matrox Marvel G200,G400 and Rainbow Runner-G series - * - * This module is an interface to the KS0127 video decoder chip. - * - * Copyright (C) 1999 Ryan Drake - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef KS0127_H -#define KS0127_H - -/* input channels */ -#define KS_INPUT_COMPOSITE_1 0 -#define KS_INPUT_COMPOSITE_2 1 -#define KS_INPUT_COMPOSITE_3 2 -#define KS_INPUT_COMPOSITE_4 4 -#define KS_INPUT_COMPOSITE_5 5 -#define KS_INPUT_COMPOSITE_6 6 - -#define KS_INPUT_SVIDEO_1 8 -#define KS_INPUT_SVIDEO_2 9 -#define KS_INPUT_SVIDEO_3 10 - -#define KS_INPUT_YUV656 15 -#define KS_INPUT_COUNT 10 - -/* output channels */ -#define KS_OUTPUT_YUV656E 0 -#define KS_OUTPUT_EXV 1 - -/* video standards */ -#define KS_STD_NTSC_N 112 /* 50 Hz NTSC */ -#define KS_STD_PAL_M 113 /* 60 Hz PAL */ - -#endif /* KS0127_H */ - diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c deleted file mode 100644 index 0991576f4c82..000000000000 --- a/drivers/media/video/m52790.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * m52790 i2c ivtv driver. - * Copyright (C) 2007 Hans Verkuil - * - * A/V source switching Mitsubishi M52790SP/FP - * - * This program is free software; 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 -#include - -MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - - -struct m52790_state { - struct v4l2_subdev sd; - u16 input; - u16 output; -}; - -static inline struct m52790_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct m52790_state, sd); -} - -/* ----------------------------------------------------------------------- */ - -static int m52790_write(struct v4l2_subdev *sd) -{ - struct m52790_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - u8 sw1 = (state->input | state->output) & 0xff; - u8 sw2 = (state->input | state->output) >> 8; - - return i2c_smbus_write_byte_data(client, sw1, sw2); -} - -/* Note: audio and video are linked and cannot be switched separately. - So audio and video routing commands are identical for this chip. - In theory the video amplifier and audio modes could be handled - separately for the output, but that seems to be overkill right now. - The same holds for implementing an audio mute control, this is now - part of the audio output routing. The normal case is that another - chip takes care of the actual muting so making it part of the - output routing seems to be the right thing to do for now. */ -static int m52790_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct m52790_state *state = to_state(sd); - - state->input = input; - state->output = output; - m52790_write(sd); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct m52790_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (reg->reg != 0) - return -EINVAL; - reg->size = 1; - reg->val = state->input | state->output; - return 0; -} - -static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct m52790_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (reg->reg != 0) - return -EINVAL; - state->input = reg->val & 0x0303; - state->output = reg->val & ~0x0303; - m52790_write(sd); - return 0; -} -#endif - -static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); -} - -static int m52790_log_status(struct v4l2_subdev *sd) -{ - struct m52790_state *state = to_state(sd); - - v4l2_info(sd, "Switch 1: %02x\n", - (state->input | state->output) & 0xff); - v4l2_info(sd, "Switch 2: %02x\n", - (state->input | state->output) >> 8); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops m52790_core_ops = { - .log_status = m52790_log_status, - .g_chip_ident = m52790_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = m52790_g_register, - .s_register = m52790_s_register, -#endif -}; - -static const struct v4l2_subdev_audio_ops m52790_audio_ops = { - .s_routing = m52790_s_routing, -}; - -static const struct v4l2_subdev_video_ops m52790_video_ops = { - .s_routing = m52790_s_routing, -}; - -static const struct v4l2_subdev_ops m52790_ops = { - .core = &m52790_core_ops, - .audio = &m52790_audio_ops, - .video = &m52790_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -static int m52790_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct m52790_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &m52790_ops); - state->input = M52790_IN_TUNER; - state->output = M52790_OUT_STEREO; - m52790_write(sd); - return 0; -} - -static int m52790_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id m52790_id[] = { - { "m52790", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, m52790_id); - -static struct i2c_driver m52790_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "m52790", - }, - .probe = m52790_probe, - .remove = m52790_remove, - .id_table = m52790_id, -}; - -module_i2c_driver(m52790_driver); diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig deleted file mode 100644 index dc8c2505907e..000000000000 --- a/drivers/media/video/m5mols/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config VIDEO_M5MOLS - tristate "Fujitsu M-5MOLS 8MP sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - ---help--- - This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile deleted file mode 100644 index 0a44e028edc7..000000000000 --- a/drivers/media/video/m5mols/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o - -obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h deleted file mode 100644 index bb589917b65b..000000000000 --- a/drivers/media/video/m5mols/m5mols.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Header for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * This program is free software; 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. - */ - -#ifndef M5MOLS_H -#define M5MOLS_H - -#include -#include "m5mols_reg.h" - -extern int m5mols_debug; - -enum m5mols_restype { - M5MOLS_RESTYPE_MONITOR, - M5MOLS_RESTYPE_CAPTURE, - M5MOLS_RESTYPE_MAX, -}; - -/** - * struct m5mols_resolution - structure for the resolution - * @type: resolution type according to the pixel code - * @width: width of the resolution - * @height: height of the resolution - * @reg: resolution preset register value - */ -struct m5mols_resolution { - u8 reg; - enum m5mols_restype type; - u16 width; - u16 height; -}; - -/** - * struct m5mols_exif - structure for the EXIF information of M-5MOLS - * @exposure_time: exposure time register value - * @shutter_speed: speed of the shutter register value - * @aperture: aperture register value - * @exposure_bias: it calls also EV bias - * @iso_speed: ISO register value - * @flash: status register value of the flash - * @sdr: status register value of the Subject Distance Range - * @qval: not written exact meaning in document - */ -struct m5mols_exif { - u32 exposure_time; - u32 shutter_speed; - u32 aperture; - u32 brightness; - u32 exposure_bias; - u16 iso_speed; - u16 flash; - u16 sdr; - u16 qval; -}; - -/** - * struct m5mols_capture - Structure for the capture capability - * @exif: EXIF information - * @main: size in bytes of the main image - * @thumb: size in bytes of the thumb image, if it was accompanied - * @total: total size in bytes of the produced image - */ -struct m5mols_capture { - struct m5mols_exif exif; - u32 main; - u32 thumb; - u32 total; -}; - -/** - * struct m5mols_scenemode - structure for the scenemode capability - * @metering: metering light register value - * @ev_bias: EV bias register value - * @wb_mode: mode which means the WhiteBalance is Auto or Manual - * @wb_preset: whitebalance preset register value in the Manual mode - * @chroma_en: register value whether the Chroma capability is enabled or not - * @chroma_lvl: chroma's level register value - * @edge_en: register value Whether the Edge capability is enabled or not - * @edge_lvl: edge's level register value - * @af_range: Auto Focus's range - * @fd_mode: Face Detection mode - * @mcc: Multi-axis Color Conversion which means emotion color - * @light: status of the Light - * @flash: status of the Flash - * @tone: Tone color which means Contrast - * @iso: ISO register value - * @capt_mode: Mode of the Image Stabilization while the camera capturing - * @wdr: Wide Dynamic Range register value - * - * The each value according to each scenemode is recommended in the documents. - */ -struct m5mols_scenemode { - u8 metering; - u8 ev_bias; - u8 wb_mode; - u8 wb_preset; - u8 chroma_en; - u8 chroma_lvl; - u8 edge_en; - u8 edge_lvl; - u8 af_range; - u8 fd_mode; - u8 mcc; - u8 light; - u8 flash; - u8 tone; - u8 iso; - u8 capt_mode; - u8 wdr; -}; - -/** - * struct m5mols_version - firmware version information - * @customer: customer information - * @project: version of project information according to customer - * @fw: firmware revision - * @hw: hardware revision - * @param: version of the parameter - * @awb: Auto WhiteBalance algorithm version - * @str: information about manufacturer and packaging vendor - * @af: Auto Focus version - * - * The register offset starts the customer version at 0x0, and it ends - * the awb version at 0x09. The customer, project information occupies 1 bytes - * each. And also the fw, hw, param, awb each requires 2 bytes. The str is - * unique string associated with firmware's version. It includes information - * about manufacturer and the vendor of the sensor's packaging. The least - * significant 2 bytes of the string indicate packaging manufacturer. - */ -#define VERSION_STRING_SIZE 22 -struct m5mols_version { - u8 customer; - u8 project; - u16 fw; - u16 hw; - u16 param; - u16 awb; - u8 str[VERSION_STRING_SIZE]; - u8 af; -}; - -/** - * struct m5mols_info - M-5MOLS driver data structure - * @pdata: platform data - * @sd: v4l-subdev instance - * @pad: media pad - * @ffmt: current fmt according to resolution type - * @res_type: current resolution type - * @irq_waitq: waitqueue for the capture - * @irq_done: set to 1 in the interrupt handler - * @handle: control handler - * @auto_exposure: auto/manual exposure control - * @exposure_bias: exposure compensation control - * @exposure: manual exposure control - * @metering: exposure metering control - * @auto_iso: auto/manual ISO sensitivity control - * @iso: manual ISO sensitivity control - * @auto_wb: auto white balance control - * @lock_3a: 3A lock control - * @colorfx: color effect control - * @saturation: saturation control - * @zoom: zoom control - * @wdr: wide dynamic range control - * @stabilization: image stabilization control - * @jpeg_quality: JPEG compression quality control - * @ver: information of the version - * @cap: the capture mode attributes - * @isp_ready: 1 when the ISP controller has completed booting - * @power: current sensor's power status - * @ctrl_sync: 1 when the control handler state is restored in H/W - * @resolution: register value for current resolution - * @mode: register value for current operation mode - * @set_power: optional power callback to the board code - */ -struct m5mols_info { - const struct m5mols_platform_data *pdata; - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; - int res_type; - - wait_queue_head_t irq_waitq; - atomic_t irq_done; - - struct v4l2_ctrl_handler handle; - struct { - /* exposure/exposure bias/auto exposure cluster */ - struct v4l2_ctrl *auto_exposure; - struct v4l2_ctrl *exposure_bias; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *metering; - }; - struct { - /* iso/auto iso cluster */ - struct v4l2_ctrl *auto_iso; - struct v4l2_ctrl *iso; - }; - struct v4l2_ctrl *auto_wb; - - struct v4l2_ctrl *lock_3a; - struct v4l2_ctrl *colorfx; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *zoom; - struct v4l2_ctrl *wdr; - struct v4l2_ctrl *stabilization; - struct v4l2_ctrl *jpeg_quality; - - struct m5mols_version ver; - struct m5mols_capture cap; - - unsigned int isp_ready:1; - unsigned int power:1; - unsigned int ctrl_sync:1; - - u8 resolution; - u8 mode; - - int (*set_power)(struct device *dev, int on); -}; - -#define is_available_af(__info) (__info->ver.af) -#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) -#define is_manufacturer(__info, __manufacturer) \ - (__info->ver.str[0] == __manufacturer[0] && \ - __info->ver.str[1] == __manufacturer[1]) -/* - * I2C operation of the M-5MOLS - * - * The I2C read operation of the M-5MOLS requires 2 messages. The first - * message sends the information about the command, command category, and total - * message size. The second message is used to retrieve the data specifed in - * the first message - * - * 1st message 2nd message - * +-------+---+----------+-----+-------+ +------+------+------+------+ - * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | - * +-------+---+----------+-----+-------+ +------+------+------+------+ - * - size1: message data size(5 in this case) - * - size2: desired buffer size of the 2nd message - * - d[0..3]: according to size2 - * - * The I2C write operation needs just one message. The message includes - * category, command, total size, and desired data. - * - * 1st message - * +-------+---+----------+-----+------+------+------+------+ - * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | - * +-------+---+----------+-----+------+------+------+------+ - * - d[0..3]: according to size1 - */ -int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val); -int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val); -int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); -int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); - -int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, - int timeout); - -/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */ -#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16) -/* ISP state transition timeout, in ms */ -#define M5MOLS_MODE_CHANGE_TIMEOUT 200 -#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250 - -/* - * Mode operation of the M-5MOLS - * - * Changing the mode of the M-5MOLS is needed right executing order. - * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed - * by user. There are various categories associated with each mode. - * - * +============================================================+ - * | mode | category | - * +============================================================+ - * | FLASH | FLASH(only after Stand-by or Power-on) | - * | SYSTEM | SYSTEM(only after sensor arm-booting) | - * | PARAMETER | PARAMETER | - * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | - * | CAPTURE | Single CAPTURE, Preview(recording) | - * +============================================================+ - * - * The available executing order between each modes are as follows: - * PARAMETER <---> MONITOR <---> CAPTURE - */ -int m5mols_set_mode(struct m5mols_info *info, u8 mode); - -int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); -int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); -int m5mols_restore_controls(struct m5mols_info *info); -int m5mols_start_capture(struct m5mols_info *info); -int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); -int m5mols_lock_3a(struct m5mols_info *info, bool lock); -int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); -int m5mols_init_controls(struct v4l2_subdev *sd); - -/* The firmware function */ -int m5mols_update_fw(struct v4l2_subdev *sd, - int (*set_power)(struct m5mols_info *, bool)); - -static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) -{ - return container_of(subdev, struct m5mols_info, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - struct m5mols_info *info = container_of(ctrl->handler, - struct m5mols_info, handle); - return &info->sd; -} - -static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, - unsigned int mode) -{ - ctrl->priv = (void *)mode; -} - -static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) -{ - return (unsigned int)ctrl->priv; -} - -#endif /* M5MOLS_H */ diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c deleted file mode 100644 index cb243bd278ce..000000000000 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ /dev/null @@ -1,155 +0,0 @@ - -/* - * The Capture code for Fujitsu M-5MOLS ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * This program is free software; 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "m5mols.h" -#include "m5mols_reg.h" - -/** - * m5mols_read_rational - I2C read of a rational number - * - * Read numerator and denominator from registers @addr_num and @addr_den - * respectively and return the division result in @val. - */ -static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, - u32 addr_den, u32 *val) -{ - u32 num, den; - - int ret = m5mols_read_u32(sd, addr_num, &num); - if (!ret) - ret = m5mols_read_u32(sd, addr_den, &den); - if (ret) - return ret; - *val = den == 0 ? 0 : num / den; - return ret; -} - -/** - * m5mols_capture_info - Gather captured image information - * - * For now it gathers only EXIF information and file size. - */ -static int m5mols_capture_info(struct m5mols_info *info) -{ - struct m5mols_exif *exif = &info->cap.exif; - struct v4l2_subdev *sd = &info->sd; - int ret; - - ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, - EXIF_INFO_EXPTIME_DE, &exif->exposure_time); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, - &exif->shutter_speed); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, - &exif->aperture); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, - &exif->brightness); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, - &exif->exposure_bias); - if (ret) - return ret; - - ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval); - if (ret) - return ret; - - if (!ret) - ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main); - if (!ret) - ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb); - if (!ret) - info->cap.total = info->cap.main + info->cap.thumb; - - return ret; -} - -int m5mols_start_capture(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - int ret; - - /* - * Synchronize the controls, set the capture frame resolution and color - * format. The frame capture is initiated during switching from Monitor - * to Capture mode. - */ - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_restore_controls(info); - if (!ret) - ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); - if (!ret) - ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); - if (!ret) - ret = m5mols_set_mode(info, REG_CAPTURE); - if (!ret) - /* Wait until a frame is captured to ISP internal memory */ - ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (ret) - return ret; - - /* - * Initiate the captured data transfer to a MIPI-CSI receiver. - */ - ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); - if (!ret) - ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); - if (!ret) { - bool captured = false; - unsigned int size; - - /* Wait for the capture completion interrupt */ - ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (!ret) { - captured = true; - ret = m5mols_capture_info(info); - } - size = captured ? info->cap.main : 0; - v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", - __func__, size, info->cap.thumb); - - v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); - } - - return ret; -} diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c deleted file mode 100644 index fdbc205a2969..000000000000 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Controls for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * This program is free software; 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. - */ - -#include -#include -#include -#include - -#include "m5mols.h" -#include "m5mols_reg.h" - -static struct m5mols_scenemode m5mols_default_scenemode[] = { - [REG_SCENE_NORMAL] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, - 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_PORTRAIT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 4, - REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_LANDSCAPE] = { - REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 6, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_SPORTS] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_PARTY_INDOOR] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_BEACH_SNOW] = { - REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_SUNSET] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, - REG_AWB_DAYLIGHT, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_DAWN_DUSK] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, - REG_AWB_FLUORESCENT_1, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_FALL] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 5, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_NIGHT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_AGAINST_LIGHT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_FIRE] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_TEXT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 7, - REG_AF_MACRO, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, - }, - [REG_SCENE_CANDLE] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, -}; - -/** - * m5mols_do_scenemode() - Change current scenemode - * @mode: Desired mode of the scenemode - * - * WARNING: The execution order is important. Do not change the order. - */ -int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) -{ - struct v4l2_subdev *sd = &info->sd; - struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; - int ret; - - if (mode > REG_SCENE_CANDLE) - return -EINVAL; - - ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); - if (!ret) - ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); - if (!ret) - ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); - if (!ret) - ret = m5mols_write(sd, AE_MODE, scenemode.metering); - if (!ret) - ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); - if (!ret) - ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); - if (!ret) - ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); - if (!ret) - ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); - if (!ret) - ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); - if (!ret && is_available_af(info)) - ret = m5mols_write(sd, AF_MODE, scenemode.af_range); - if (!ret && is_available_af(info)) - ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); - if (!ret) - ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); - if (!ret) - ret = m5mols_write(sd, AE_ISO, scenemode.iso); - if (!ret) - ret = m5mols_set_mode(info, REG_CAPTURE); - if (!ret) - ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); - if (!ret) - ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); - if (!ret) - ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); - if (!ret) - ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); - if (!ret) - ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); - if (!ret) - ret = m5mols_set_mode(info, REG_MONITOR); - - return ret; -} - -static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) -{ - bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; - int ret = 0; - - if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { - bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; - - ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? - REG_AE_LOCK : REG_AE_UNLOCK); - if (ret) - return ret; - } - - if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) - && info->auto_wb->val) { - bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; - - ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? - REG_AWB_LOCK : REG_AWB_UNLOCK); - if (ret) - return ret; - } - - if (!info->ver.af || !af_lock) - return ret; - - if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) - ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); - - return ret; -} - -static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) -{ - unsigned int metering; - - switch (mode) { - case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: - metering = REG_AE_CENTER; - break; - case V4L2_EXPOSURE_METERING_SPOT: - metering = REG_AE_SPOT; - break; - default: - metering = REG_AE_ALL; - break; - } - - return m5mols_write(&info->sd, AE_MODE, metering); -} - -static int m5mols_set_exposure(struct m5mols_info *info, int exposure) -{ - struct v4l2_subdev *sd = &info->sd; - int ret = 0; - - if (exposure == V4L2_EXPOSURE_AUTO) { - /* Unlock auto exposure */ - info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; - m5mols_3a_lock(info, info->lock_3a); - - ret = m5mols_set_metering_mode(info, info->metering->val); - if (ret < 0) - return ret; - - v4l2_dbg(1, m5mols_debug, sd, - "%s: exposure bias: %#x, metering: %#x\n", - __func__, info->exposure_bias->val, - info->metering->val); - - return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); - } - - if (exposure == V4L2_EXPOSURE_MANUAL) { - ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); - if (ret == 0) - ret = m5mols_write(sd, AE_MAN_GAIN_MON, - info->exposure->val); - if (ret == 0) - ret = m5mols_write(sd, AE_MAN_GAIN_CAP, - info->exposure->val); - - v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", - __func__, info->exposure->val); - } - - return ret; -} - -static int m5mols_set_white_balance(struct m5mols_info *info, int val) -{ - static const unsigned short wb[][2] = { - { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, - { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, - { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, - { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, - { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, - { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, - { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, - { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, - { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, - }; - int i; - struct v4l2_subdev *sd = &info->sd; - int ret = -EINVAL; - - for (i = 0; i < ARRAY_SIZE(wb); i++) { - int awb; - if (wb[i][0] != val) - continue; - - v4l2_dbg(1, m5mols_debug, sd, - "Setting white balance to: %#x\n", wb[i][0]); - - awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; - ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : - REG_AWB_PRESET); - if (ret < 0) - return ret; - - if (!awb) - ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); - } - - return ret; -} - -static int m5mols_set_saturation(struct m5mols_info *info, int val) -{ - int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); - if (ret < 0) - return ret; - - return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); -} - -static int m5mols_set_color_effect(struct m5mols_info *info, int val) -{ - unsigned int m_effect = REG_COLOR_EFFECT_OFF; - unsigned int p_effect = REG_EFFECT_OFF; - unsigned int cfix_r = 0, cfix_b = 0; - struct v4l2_subdev *sd = &info->sd; - int ret = 0; - - switch (val) { - case V4L2_COLORFX_BW: - m_effect = REG_COLOR_EFFECT_ON; - break; - case V4L2_COLORFX_NEGATIVE: - p_effect = REG_EFFECT_NEGA; - break; - case V4L2_COLORFX_EMBOSS: - p_effect = REG_EFFECT_EMBOSS; - break; - case V4L2_COLORFX_SEPIA: - m_effect = REG_COLOR_EFFECT_ON; - cfix_r = REG_CFIXR_SEPIA; - cfix_b = REG_CFIXB_SEPIA; - break; - } - - ret = m5mols_write(sd, PARM_EFFECT, p_effect); - if (!ret) - ret = m5mols_write(sd, MON_EFFECT, m_effect); - - if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { - ret = m5mols_write(sd, MON_CFIXR, cfix_r); - if (!ret) - ret = m5mols_write(sd, MON_CFIXB, cfix_b); - } - - v4l2_dbg(1, m5mols_debug, sd, - "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", - p_effect, m_effect, cfix_r, cfix_b, ret); - - return ret; -} - -static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) -{ - u32 iso = auto_iso ? 0 : info->iso->val + 1; - - return m5mols_write(&info->sd, AE_ISO, iso); -} - -static int m5mols_set_wdr(struct m5mols_info *info, int wdr) -{ - int ret; - - ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); - if (ret < 0) - return ret; - - ret = m5mols_set_mode(info, REG_CAPTURE); - if (ret < 0) - return ret; - - return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); -} - -static int m5mols_set_stabilization(struct m5mols_info *info, int val) -{ - struct v4l2_subdev *sd = &info->sd; - unsigned int evp = val ? 0xe : 0x0; - int ret; - - ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); - if (ret < 0) - return ret; - - return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); -} - -static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int ret = 0; - u8 status; - - v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", - __func__, ctrl->name, info->isp_ready); - - if (!info->isp_ready) - return -EBUSY; - - switch (ctrl->id) { - case V4L2_CID_ISO_SENSITIVITY_AUTO: - ret = m5mols_read_u8(sd, AE_ISO, &status); - if (ret == 0) - ctrl->val = !status; - if (status != REG_ISO_AUTO) - info->iso->val = status - 1; - break; - - case V4L2_CID_3A_LOCK: - ctrl->val &= ~0x7; - - ret = m5mols_read_u8(sd, AE_LOCK, &status); - if (ret) - return ret; - if (status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - - ret = m5mols_read_u8(sd, AWB_LOCK, &status); - if (ret) - return ret; - if (status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - - ret = m5mols_read_u8(sd, AF_EXECUTE, &status); - if (!status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - break; - } - - return ret; -} - -static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) -{ - unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int last_mode = info->mode; - int ret = 0; - - /* - * If needed, defer restoring the controls until - * the device is fully initialized. - */ - if (!info->isp_ready) { - info->ctrl_sync = 0; - return 0; - } - - v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n", - __func__, ctrl->name, ctrl->val, (int)ctrl->priv); - - if (ctrl_mode && ctrl_mode != info->mode) { - ret = m5mols_set_mode(info, ctrl_mode); - if (ret < 0) - return ret; - } - - switch (ctrl->id) { - case V4L2_CID_3A_LOCK: - ret = m5mols_3a_lock(info, ctrl); - break; - - case V4L2_CID_ZOOM_ABSOLUTE: - ret = m5mols_write(sd, MON_ZOOM, ctrl->val); - break; - - case V4L2_CID_EXPOSURE_AUTO: - ret = m5mols_set_exposure(info, ctrl->val); - break; - - case V4L2_CID_ISO_SENSITIVITY: - ret = m5mols_set_iso(info, ctrl->val); - break; - - case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: - ret = m5mols_set_white_balance(info, ctrl->val); - break; - - case V4L2_CID_SATURATION: - ret = m5mols_set_saturation(info, ctrl->val); - break; - - case V4L2_CID_COLORFX: - ret = m5mols_set_color_effect(info, ctrl->val); - break; - - case V4L2_CID_WIDE_DYNAMIC_RANGE: - ret = m5mols_set_wdr(info, ctrl->val); - break; - - case V4L2_CID_IMAGE_STABILIZATION: - ret = m5mols_set_stabilization(info, ctrl->val); - break; - - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); - break; - } - - if (ret == 0 && info->mode != last_mode) - ret = m5mols_set_mode(info, last_mode); - - return ret; -} - -static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { - .g_volatile_ctrl = m5mols_g_volatile_ctrl, - .s_ctrl = m5mols_s_ctrl, -}; - -/* Supported manual ISO values */ -static const s64 iso_qmenu[] = { - /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ - 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 -}; - -/* Supported Exposure Bias values, -2.0EV...+2.0EV */ -static const s64 ev_bias_qmenu[] = { - /* AE_INDEX: 0x00...0x08 */ - -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 -}; - -int m5mols_init_controls(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - u16 exposure_max; - u16 zoom_step; - int ret; - - /* Determine the firmware dependant control range and step values */ - ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); - if (ret < 0) - return ret; - - zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; - v4l2_ctrl_handler_init(&info->handle, 20); - - info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, - 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); - - /* Exposure control cluster */ - info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, - 1, ~0x03, V4L2_EXPOSURE_AUTO); - - info->exposure = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, - 0, exposure_max, 1, exposure_max / 2); - - info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, - ARRAY_SIZE(ev_bias_qmenu) - 1, - ARRAY_SIZE(ev_bias_qmenu)/2 - 1, - ev_bias_qmenu); - - info->metering = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, - 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); - - /* ISO control cluster */ - info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); - - info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, - ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); - - info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_SATURATION, 1, 5, 1, 3); - - info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); - - info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); - - info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); - - info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); - - info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); - - info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); - - if (info->handle.error) { - int ret = info->handle.error; - v4l2_err(sd, "Failed to initialize controls: %d\n", ret); - v4l2_ctrl_handler_free(&info->handle); - return ret; - } - - v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); - info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | - V4L2_CTRL_FLAG_UPDATE; - v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); - - info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; - - m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); - m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); - m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); - - sd->ctrl_handler = &info->handle; - - return 0; -} diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c deleted file mode 100644 index ac7d28b6ddf2..000000000000 --- a/drivers/media/video/m5mols/m5mols_core.c +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Driver for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * This program is free software; 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "m5mols.h" -#include "m5mols_reg.h" - -int m5mols_debug; -module_param(m5mols_debug, int, 0644); - -#define MODULE_NAME "M5MOLS" -#define M5MOLS_I2C_CHECK_RETRY 500 - -/* The regulator consumer names for external voltage regulators */ -static struct regulator_bulk_data supplies[] = { - { - .supply = "core", /* ARM core power, 1.2V */ - }, { - .supply = "dig_18", /* digital power 1, 1.8V */ - }, { - .supply = "d_sensor", /* sensor power 1, 1.8V */ - }, { - .supply = "dig_28", /* digital power 2, 2.8V */ - }, { - .supply = "a_sensor", /* analog power */ - }, { - .supply = "dig_12", /* digital power 3, 1.2V */ - }, -}; - -static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { - [M5MOLS_RESTYPE_MONITOR] = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_VYUY8_2X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - [M5MOLS_RESTYPE_CAPTURE] = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_JPEG_1X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; -#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) - -static const struct m5mols_resolution m5mols_reg_res[] = { - { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ - { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ - { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ - { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, - { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ - { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ - { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ - { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ - { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ - { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, - { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ - { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ - { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, - { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ - { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ - { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ - { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ - { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ - { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ - - { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ - { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ - { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, - { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ - { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ - { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ - { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ - { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ - { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ - { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ - { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ - { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, - { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ - { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, - { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ - { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ - { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, - { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ -}; - -/** - * m5mols_swap_byte - an byte array to integer conversion function - * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet - * - * Convert I2C data byte array with performing any required byte - * reordering to assure proper values for each data type, regardless - * of the architecture endianness. - */ -static u32 m5mols_swap_byte(u8 *data, u8 length) -{ - if (length == 1) - return *data; - else if (length == 2) - return be16_to_cpu(*((u16 *)data)); - else - return be32_to_cpu(*((u32 *)data)); -} - -/** - * m5mols_read - I2C read function - * @reg: combination of size, category and command for the I2C packet - * @size: desired size of I2C packet - * @val: read value - * - * Returns 0 on success, or else negative errno. - */ -static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct m5mols_info *info = to_m5mols(sd); - u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; - u8 category = I2C_CATEGORY(reg); - u8 cmd = I2C_COMMAND(reg); - struct i2c_msg msg[2]; - u8 wbuf[5]; - int ret; - - if (!client->adapter) - return -ENODEV; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 5; - msg[0].buf = wbuf; - wbuf[0] = 5; - wbuf[1] = M5MOLS_BYTE_READ; - wbuf[2] = category; - wbuf[3] = cmd; - wbuf[4] = size; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = size + 1; - msg[1].buf = rbuf; - - /* minimum stabilization time */ - usleep_range(200, 200); - - ret = i2c_transfer(client->adapter, msg, 2); - - if (ret == 2) { - *val = m5mols_swap_byte(&rbuf[1], size); - return 0; - } - - if (info->isp_ready) - v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", - size, category, cmd, ret); - - return ret < 0 ? ret : -EIO; -} - -int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) -{ - u32 val_32; - int ret; - - if (I2C_SIZE(reg) != 1) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); - if (ret) - return ret; - - *val = (u8)val_32; - return ret; -} - -int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) -{ - u32 val_32; - int ret; - - if (I2C_SIZE(reg) != 2) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); - if (ret) - return ret; - - *val = (u16)val_32; - return ret; -} - -int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) -{ - if (I2C_SIZE(reg) != 4) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - return m5mols_read(sd, I2C_SIZE(reg), reg, val); -} - -/** - * m5mols_write - I2C command write function - * @reg: combination of size, category and command for the I2C packet - * @val: value to write - * - * Returns 0 on success, or else negative errno. - */ -int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct m5mols_info *info = to_m5mols(sd); - u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; - u8 category = I2C_CATEGORY(reg); - u8 cmd = I2C_COMMAND(reg); - u8 size = I2C_SIZE(reg); - u32 *buf = (u32 *)&wbuf[4]; - struct i2c_msg msg[1]; - int ret; - - if (!client->adapter) - return -ENODEV; - - if (size != 1 && size != 2 && size != 4) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - msg->addr = client->addr; - msg->flags = 0; - msg->len = (u16)size + 4; - msg->buf = wbuf; - wbuf[0] = size + 4; - wbuf[1] = M5MOLS_BYTE_WRITE; - wbuf[2] = category; - wbuf[3] = cmd; - - *buf = m5mols_swap_byte((u8 *)&val, size); - - usleep_range(200, 200); - - ret = i2c_transfer(client->adapter, msg, 1); - if (ret == 1) - return 0; - - if (info->isp_ready) - v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", - category, cmd, ret); - - return ret < 0 ? ret : -EIO; -} - -/** - * m5mols_busy_wait - Busy waiting with I2C register polling - * @reg: the I2C_REG() address of an 8-bit status register to check - * @value: expected status register value - * @mask: bit mask for the read status register value - * @timeout: timeout in miliseconds, or -1 for default timeout - * - * The @reg register value is ORed with @mask before comparing with @value. - * - * Return: 0 if the requested condition became true within less than - * @timeout ms, or else negative errno. - */ -int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, - int timeout) -{ - int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; - unsigned long end = jiffies + msecs_to_jiffies(ms); - u8 status; - - do { - int ret = m5mols_read_u8(sd, reg, &status); - - if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) - return ret; - if (!ret && (status & mask & 0xff) == (value & 0xff)) - return 0; - usleep_range(100, 250); - } while (ms > 0 && time_is_after_jiffies(end)); - - return -EBUSY; -} - -/** - * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts - * - * Before writing desired interrupt value the INT_FACTOR register should - * be read to clear pending interrupts. - */ -int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) -{ - struct m5mols_info *info = to_m5mols(sd); - u8 mask = is_available_af(info) ? REG_INT_AF : 0; - u8 dummy; - int ret; - - ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); - if (!ret) - ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); - return ret; -} - -int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) -{ - struct m5mols_info *info = to_m5mols(sd); - - int ret = wait_event_interruptible_timeout(info->irq_waitq, - atomic_add_unless(&info->irq_done, -1, 0), - msecs_to_jiffies(timeout)); - if (ret <= 0) - return ret ? ret : -ETIMEDOUT; - - return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, - M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); -} - -/** - * m5mols_reg_mode - Write the mode and check busy status - * - * It always accompanies a little delay changing the M-5MOLS mode, so it is - * needed checking current busy status to guarantee right mode. - */ -static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) -{ - int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); - if (ret < 0) - return ret; - return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, - M5MOLS_MODE_CHANGE_TIMEOUT); -} - -/** - * m5mols_set_mode - set the M-5MOLS controller mode - * @mode: the required operation mode - * - * The commands of M-5MOLS are grouped into specific modes. Each functionality - * can be guaranteed only when the sensor is operating in mode which a command - * belongs to. - */ -int m5mols_set_mode(struct m5mols_info *info, u8 mode) -{ - struct v4l2_subdev *sd = &info->sd; - int ret = -EINVAL; - u8 reg; - - if (mode < REG_PARAMETER || mode > REG_CAPTURE) - return ret; - - ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); - if (ret || reg == mode) - return ret; - - switch (reg) { - case REG_PARAMETER: - ret = m5mols_reg_mode(sd, REG_MONITOR); - if (mode == REG_MONITOR) - break; - if (!ret) - ret = m5mols_reg_mode(sd, REG_CAPTURE); - break; - - case REG_MONITOR: - if (mode == REG_PARAMETER) { - ret = m5mols_reg_mode(sd, REG_PARAMETER); - break; - } - - ret = m5mols_reg_mode(sd, REG_CAPTURE); - break; - - case REG_CAPTURE: - ret = m5mols_reg_mode(sd, REG_MONITOR); - if (mode == REG_MONITOR) - break; - if (!ret) - ret = m5mols_reg_mode(sd, REG_PARAMETER); - break; - - default: - v4l2_warn(sd, "Wrong mode: %d\n", mode); - } - - if (!ret) - info->mode = mode; - - return ret; -} - -/** - * m5mols_get_version - retrieve full revisions information of M-5MOLS - * - * The version information includes revisions of hardware and firmware, - * AutoFocus alghorithm version and the version string. - */ -static int m5mols_get_version(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - struct m5mols_version *ver = &info->ver; - u8 *str = ver->str; - int i; - int ret; - - ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); - if (!ret) - ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); - if (!ret) - ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); - if (ret) - return ret; - - for (i = 0; i < VERSION_STRING_SIZE; i++) { - ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); - if (ret) - return ret; - } - - ver->fw = be16_to_cpu(ver->fw); - ver->hw = be16_to_cpu(ver->hw); - ver->param = be16_to_cpu(ver->param); - ver->awb = be16_to_cpu(ver->awb); - - v4l2_info(sd, "Manufacturer\t[%s]\n", - is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? - "Samsung Electro-Machanics" : - is_manufacturer(info, REG_SAMSUNG_OPTICS) ? - "Samsung Fiber-Optics" : - is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? - "Samsung Techwin" : "None"); - v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", - info->ver.customer, info->ver.project); - - if (!is_available_af(info)) - v4l2_info(sd, "No support Auto Focus on this firmware\n"); - - return ret; -} - -/** - * __find_restype - Lookup M-5MOLS resolution type according to pixel code - * @code: pixel code - */ -static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) -{ - enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; - - do { - if (code == m5mols_default_ffmt[type].code) - return type; - } while (type++ != SIZE_DEFAULT_FFMT); - - return 0; -} - -/** - * __find_resolution - Lookup preset and type of M-5MOLS's resolution - * @mf: pixel format to find/negotiate the resolution preset for - * @type: M-5MOLS resolution type - * @resolution: M-5MOLS resolution preset register value - * - * Find nearest resolution matching resolution preset and adjust mf - * to supported values. - */ -static int __find_resolution(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf, - enum m5mols_restype *type, - u32 *resolution) -{ - const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; - const struct m5mols_resolution *match = NULL; - enum m5mols_restype stype = __find_restype(mf->code); - int i = ARRAY_SIZE(m5mols_reg_res); - unsigned int min_err = ~0; - - while (i--) { - int err; - if (stype == fsize->type) { - err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - - if (err < min_err) { - min_err = err; - match = fsize; - } - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - *resolution = match->reg; - *type = stype; - return 0; - } - - return -EINVAL; -} - -static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, - struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which, - enum m5mols_restype type) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; - - return &info->ffmt[type]; -} - -static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct m5mols_info *info = to_m5mols(sd); - struct v4l2_mbus_framefmt *format; - - format = __find_format(info, fh, fmt->which, info->res_type); - if (!format) - return -EINVAL; - - fmt->format = *format; - return 0; -} - -static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct m5mols_info *info = to_m5mols(sd); - struct v4l2_mbus_framefmt *format = &fmt->format; - struct v4l2_mbus_framefmt *sfmt; - enum m5mols_restype type; - u32 resolution = 0; - int ret; - - ret = __find_resolution(sd, format, &type, &resolution); - if (ret < 0) - return ret; - - sfmt = __find_format(info, fh, fmt->which, type); - if (!sfmt) - return 0; - - - format->code = m5mols_default_ffmt[type].code; - format->colorspace = V4L2_COLORSPACE_JPEG; - format->field = V4L2_FIELD_NONE; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - *sfmt = *format; - info->resolution = resolution; - info->res_type = type; - } - - return 0; -} - -static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (!code || code->index >= SIZE_DEFAULT_FFMT) - return -EINVAL; - - code->code = m5mols_default_ffmt[code->index].code; - - return 0; -} - -static struct v4l2_subdev_pad_ops m5mols_pad_ops = { - .enum_mbus_code = m5mols_enum_mbus_code, - .get_fmt = m5mols_get_fmt, - .set_fmt = m5mols_set_fmt, -}; - -/** - * m5mols_restore_controls - Apply current control values to the registers - * - * m5mols_do_scenemode() handles all parameters for which there is yet no - * individual control. It should be replaced at some point by setting each - * control individually, in required register set up order. - */ -int m5mols_restore_controls(struct m5mols_info *info) -{ - int ret; - - if (info->ctrl_sync) - return 0; - - ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); - if (ret) - return ret; - - ret = v4l2_ctrl_handler_setup(&info->handle); - info->ctrl_sync = !ret; - - return ret; -} - -/** - * m5mols_start_monitor - Start the monitor mode - * - * Before applying the controls setup the resolution and frame rate - * in PARAMETER mode, and then switch over to MONITOR mode. - */ -static int m5mols_start_monitor(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - int ret; - - ret = m5mols_set_mode(info, REG_PARAMETER); - if (!ret) - ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); - if (!ret) - ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); - if (!ret) - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_restore_controls(info); - - return ret; -} - -static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct m5mols_info *info = to_m5mols(sd); - u32 code = info->ffmt[info->res_type].code; - - if (enable) { - int ret = -EINVAL; - - if (is_code(code, M5MOLS_RESTYPE_MONITOR)) - ret = m5mols_start_monitor(info); - if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) - ret = m5mols_start_capture(info); - - return ret; - } - - return m5mols_set_mode(info, REG_PARAMETER); -} - -static const struct v4l2_subdev_video_ops m5mols_video_ops = { - .s_stream = m5mols_s_stream, -}; - -static int m5mols_sensor_power(struct m5mols_info *info, bool enable) -{ - struct v4l2_subdev *sd = &info->sd; - struct i2c_client *client = v4l2_get_subdevdata(sd); - const struct m5mols_platform_data *pdata = info->pdata; - int ret; - - if (info->power == enable) - return 0; - - if (enable) { - if (info->set_power) { - ret = info->set_power(&client->dev, 1); - if (ret) - return ret; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); - if (ret) { - info->set_power(&client->dev, 0); - return ret; - } - - gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); - info->power = 1; - - return ret; - } - - ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); - if (ret) - return ret; - - if (info->set_power) - info->set_power(&client->dev, 0); - - gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); - - info->isp_ready = 0; - info->power = 0; - - return ret; -} - -/* m5mols_update_fw - optional firmware update routine */ -int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, - int (*set_power)(struct m5mols_info *, bool)) -{ - return 0; -} - -/** - * m5mols_fw_start - M-5MOLS internal ARM controller initialization - * - * Execute the M-5MOLS internal ARM controller initialization sequence. - * This function should be called after the supply voltage has been - * applied and before any requests to the device are made. - */ -static int m5mols_fw_start(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - int ret; - - atomic_set(&info->irq_done, 0); - /* Wait until I2C slave is initialized in Flash Writer mode */ - ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, - M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); - if (!ret) - ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); - if (!ret) - ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); - if (ret < 0) - return ret; - - info->isp_ready = 1; - - ret = m5mols_get_version(sd); - if (!ret) - ret = m5mols_update_fw(sd, m5mols_sensor_power); - if (ret) - return ret; - - v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); - - ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); - if (!ret) - ret = m5mols_enable_interrupt(sd, - REG_INT_AF | REG_INT_CAPTURE); - - return ret; -} - -/** - * m5mols_s_power - Main sensor power control function - * - * To prevent breaking the lens when the sensor is powered off the Soft-Landing - * algorithm is called where available. The Soft-Landing algorithm availability - * dependends on the firmware provider. - */ -static int m5mols_s_power(struct v4l2_subdev *sd, int on) -{ - struct m5mols_info *info = to_m5mols(sd); - int ret; - - if (on) { - ret = m5mols_sensor_power(info, true); - if (!ret) - ret = m5mols_fw_start(sd); - return ret; - } - - if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); - if (!ret) - ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); - if (!ret) - ret = m5mols_busy_wait(sd, SYSTEM_STATUS, REG_AF_IDLE, - 0xff, -1); - if (ret < 0) - v4l2_warn(sd, "Soft landing lens failed\n"); - } - - ret = m5mols_sensor_power(info, false); - info->ctrl_sync = 0; - - return ret; -} - -static int m5mols_log_status(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - - v4l2_ctrl_handler_log_status(&info->handle, sd->name); - - return 0; -} - -static const struct v4l2_subdev_core_ops m5mols_core_ops = { - .s_power = m5mols_s_power, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .log_status = m5mols_log_status, -}; - -/* - * V4L2 subdev internal operations - */ -static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); - - *format = m5mols_default_ffmt[0]; - return 0; -} - -static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = { - .open = m5mols_open, -}; - -static const struct v4l2_subdev_ops m5mols_ops = { - .core = &m5mols_core_ops, - .pad = &m5mols_pad_ops, - .video = &m5mols_video_ops, -}; - -static irqreturn_t m5mols_irq_handler(int irq, void *data) -{ - struct m5mols_info *info = to_m5mols(data); - - atomic_set(&info->irq_done, 1); - wake_up_interruptible(&info->irq_waitq); - - return IRQ_HANDLED; -} - -static int __devinit m5mols_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - const struct m5mols_platform_data *pdata = client->dev.platform_data; - struct m5mols_info *info; - struct v4l2_subdev *sd; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!gpio_is_valid(pdata->gpio_reset)) { - dev_err(&client->dev, "No valid RESET GPIO specified\n"); - return -EINVAL; - } - - if (!client->irq) { - dev_err(&client->dev, "Interrupt not assigned\n"); - return -EINVAL; - } - - info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->pdata = pdata; - info->set_power = pdata->set_power; - - ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); - if (ret) { - dev_err(&client->dev, "Failed to request gpio: %d\n", ret); - goto out_free; - } - gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); - - ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); - if (ret) { - dev_err(&client->dev, "Failed to get regulators: %d\n", ret); - goto out_gpio; - } - - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &m5mols_ops); - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - sd->internal_ops = &m5mols_subdev_internal_ops; - info->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); - if (ret < 0) - goto out_reg; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - - init_waitqueue_head(&info->irq_waitq); - ret = request_irq(client->irq, m5mols_irq_handler, - IRQF_TRIGGER_RISING, MODULE_NAME, sd); - if (ret) { - dev_err(&client->dev, "Interrupt request failed: %d\n", ret); - goto out_me; - } - info->res_type = M5MOLS_RESTYPE_MONITOR; - info->ffmt[0] = m5mols_default_ffmt[0]; - info->ffmt[1] = m5mols_default_ffmt[1]; - - ret = m5mols_sensor_power(info, true); - if (ret) - goto out_me; - - ret = m5mols_fw_start(sd); - if (!ret) - ret = m5mols_init_controls(sd); - - m5mols_sensor_power(info, false); - if (!ret) - return 0; -out_me: - media_entity_cleanup(&sd->entity); -out_reg: - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); -out_gpio: - gpio_free(pdata->gpio_reset); -out_free: - kfree(info); - return ret; -} - -static int __devexit m5mols_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct m5mols_info *info = to_m5mols(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - free_irq(client->irq, sd); - - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); - gpio_free(info->pdata->gpio_reset); - media_entity_cleanup(&sd->entity); - kfree(info); - return 0; -} - -static const struct i2c_device_id m5mols_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, m5mols_id); - -static struct i2c_driver m5mols_i2c_driver = { - .driver = { - .name = MODULE_NAME, - }, - .probe = m5mols_probe, - .remove = __devexit_p(m5mols_remove), - .id_table = m5mols_id, -}; - -module_i2c_driver(m5mols_i2c_driver); - -MODULE_AUTHOR("HeungJun Kim "); -MODULE_AUTHOR("Dongsoo Kim "); -MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h deleted file mode 100644 index 14d4be72aeff..000000000000 --- a/drivers/media/video/m5mols/m5mols_reg.h +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Register map for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim - * - * This program is free software; 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. - */ - -#ifndef M5MOLS_REG_H -#define M5MOLS_REG_H - -#define M5MOLS_I2C_MAX_SIZE 4 -#define M5MOLS_BYTE_READ 0x01 -#define M5MOLS_BYTE_WRITE 0x02 - -#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) -#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) -#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) -#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) - -/* - * Category section register - * - * The category means set including relevant command of M-5MOLS. - */ -#define CAT_SYSTEM 0x00 -#define CAT_PARAM 0x01 -#define CAT_MONITOR 0x02 -#define CAT_AE 0x03 -#define CAT_WB 0x06 -#define CAT_EXIF 0x07 -#define CAT_FD 0x09 -#define CAT_LENS 0x0a -#define CAT_CAPT_PARM 0x0b -#define CAT_CAPT_CTRL 0x0c -#define CAT_FLASH 0x0f /* related to FW, revisions, booting */ - -/* - * Category 0 - SYSTEM mode - * - * The SYSTEM mode in the M-5MOLS means area available to handle with the whole - * & all-round system of sensor. It deals with version/interrupt/setting mode & - * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by - * packaging & manufacturer, even the customer and project code. And the - * function details may vary among them. The version information helps to - * determine what methods shall be used in the driver. - * - * There is many registers between customer version address and awb one. For - * more specific contents, see definition if file m5mols.h. - */ -#define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1) -#define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1) -#define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2) -#define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2) -#define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2) -#define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2) - -#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1) -#define REG_SYSINIT 0x00 /* SYSTEM mode */ -#define REG_PARAMETER 0x01 /* PARAMETER mode */ -#define REG_MONITOR 0x02 /* MONITOR mode */ -#define REG_CAPTURE 0x03 /* CAPTURE mode */ - -#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) -#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1) -#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ -#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ -#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ -/* SYSTEM mode status */ -#define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1) - -/* Interrupt pending register */ -#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1) -/* interrupt enable register */ -#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1) -#define REG_INT_MODE (1 << 0) -#define REG_INT_AF (1 << 1) -#define REG_INT_ZOOM (1 << 2) -#define REG_INT_CAPTURE (1 << 3) -#define REG_INT_FRAMESYNC (1 << 4) -#define REG_INT_FD (1 << 5) -#define REG_INT_LENS_INIT (1 << 6) -#define REG_INT_SOUND (1 << 7) -#define REG_INT_MASK 0x0f - -/* - * category 1 - PARAMETER mode - * - * This category supports function of camera features of M-5MOLS. It means we - * can handle with preview(MONITOR) resolution size/frame per second/interface - * between the sensor and the Application Processor/even the image effect. - */ - -/* Resolution in the MONITOR mode */ -#define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1) - -/* Frame rate */ -#define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1) -#define REG_FPS_30 0x02 - -/* Video bus between the sensor and a host processor */ -#define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1) -#define REG_INTERFACE_MIPI 0x02 - -/* Image effects */ -#define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1) -#define REG_EFFECT_OFF 0x00 -#define REG_EFFECT_NEGA 0x01 -#define REG_EFFECT_EMBOSS 0x06 -#define REG_EFFECT_OUTLINE 0x07 -#define REG_EFFECT_WATERCOLOR 0x08 - -/* - * Category 2 - MONITOR mode - * - * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another - * mode named "Preview", but this preview mode is used at the case specific - * vider-recording mode. This mmode supports only YUYV format. On the other - * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are - * another options like zoom/color effect(different with effect in PARAMETER - * mode)/anti hand shaking algorithm. - */ - -/* Target digital zoom position */ -#define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1) - -/* CR value for color effect */ -#define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1) -/* CB value for color effect */ -#define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1) -#define REG_CFIXB_SEPIA 0xd8 -#define REG_CFIXR_SEPIA 0x18 - -#define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1) -#define REG_COLOR_EFFECT_OFF 0x00 -#define REG_COLOR_EFFECT_ON 0x01 - -/* Chroma enable */ -#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1) -/* Chroma level */ -#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1) -#define REG_CHROMA_OFF 0x00 -#define REG_CHROMA_ON 0x01 - -/* Sharpness on/off */ -#define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1) -/* Sharpness level */ -#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1) -#define REG_EDGE_OFF 0x00 -#define REG_EDGE_ON 0x01 - -/* Set color tone (contrast) */ -#define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1) - -/* - * Category 3 - Auto Exposure - * - * The M-5MOLS exposure capbility is detailed as which is similar to digital - * camera. This category supports AE locking/various AE mode(range of exposure) - * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the - * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be - * different. So, this category also provide getting the max/min values. And, - * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. - */ - -/* Auto Exposure locking */ -#define AE_LOCK I2C_REG(CAT_AE, 0x00, 1) -#define REG_AE_UNLOCK 0x00 -#define REG_AE_LOCK 0x01 - -/* Auto Exposure algorithm mode */ -#define AE_MODE I2C_REG(CAT_AE, 0x01, 1) -#define REG_AE_OFF 0x00 /* AE off */ -#define REG_AE_ALL 0x01 /* calc AE in all block integral */ -#define REG_AE_CENTER 0x03 /* calc AE in center weighted */ -#define REG_AE_SPOT 0x06 /* calc AE in specific spot */ - -#define AE_ISO I2C_REG(CAT_AE, 0x05, 1) -#define REG_ISO_AUTO 0x00 -#define REG_ISO_50 0x01 -#define REG_ISO_100 0x02 -#define REG_ISO_200 0x03 -#define REG_ISO_400 0x04 -#define REG_ISO_800 0x05 - -/* EV (scenemode) preset for MONITOR */ -#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1) -/* EV (scenemode) preset for CAPTURE */ -#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1) -#define REG_SCENE_NORMAL 0x00 -#define REG_SCENE_PORTRAIT 0x01 -#define REG_SCENE_LANDSCAPE 0x02 -#define REG_SCENE_SPORTS 0x03 -#define REG_SCENE_PARTY_INDOOR 0x04 -#define REG_SCENE_BEACH_SNOW 0x05 -#define REG_SCENE_SUNSET 0x06 -#define REG_SCENE_DAWN_DUSK 0x07 -#define REG_SCENE_FALL 0x08 -#define REG_SCENE_NIGHT 0x09 -#define REG_SCENE_AGAINST_LIGHT 0x0a -#define REG_SCENE_FIRE 0x0b -#define REG_SCENE_TEXT 0x0c -#define REG_SCENE_CANDLE 0x0d - -/* Manual gain in MONITOR mode */ -#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2) -/* Maximum gain in MONITOR mode */ -#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2) -/* Manual gain in CAPTURE mode */ -#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2) - -#define AE_INDEX I2C_REG(CAT_AE, 0x38, 1) -#define REG_AE_INDEX_20_NEG 0x00 -#define REG_AE_INDEX_15_NEG 0x01 -#define REG_AE_INDEX_10_NEG 0x02 -#define REG_AE_INDEX_05_NEG 0x03 -#define REG_AE_INDEX_00 0x04 -#define REG_AE_INDEX_05_POS 0x05 -#define REG_AE_INDEX_10_POS 0x06 -#define REG_AE_INDEX_15_POS 0x07 -#define REG_AE_INDEX_20_POS 0x08 - -/* - * Category 6 - White Balance - */ - -/* Auto Whitebalance locking */ -#define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1) -#define REG_AWB_UNLOCK 0x00 -#define REG_AWB_LOCK 0x01 - -#define AWB_MODE I2C_REG(CAT_WB, 0x02, 1) -#define REG_AWB_AUTO 0x01 /* AWB off */ -#define REG_AWB_PRESET 0x02 /* AWB preset */ - -/* Manual WB (preset) */ -#define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1) -#define REG_AWB_INCANDESCENT 0x01 -#define REG_AWB_FLUORESCENT_1 0x02 -#define REG_AWB_FLUORESCENT_2 0x03 -#define REG_AWB_DAYLIGHT 0x04 -#define REG_AWB_CLOUDY 0x05 -#define REG_AWB_SHADE 0x06 -#define REG_AWB_HORIZON 0x07 -#define REG_AWB_LEDLIGHT 0x09 - -/* - * Category 7 - EXIF information - */ -#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4) -#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4) -#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4) -#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4) -#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4) -#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4) -#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4) -#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4) -#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4) -#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4) -#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2) -#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2) -#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2) -#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2) - -/* - * Category 9 - Face Detection - */ -#define FD_CTL I2C_REG(CAT_FD, 0x00, 1) -#define BIT_FD_EN 0 -#define BIT_FD_DRAW_FACE_FRAME 4 -#define BIT_FD_DRAW_SMILE_LVL 6 -#define REG_FD(shift) (1 << shift) -#define REG_FD_OFF 0x0 - -/* - * Category A - Lens Parameter - */ -#define AF_MODE I2C_REG(CAT_LENS, 0x01, 1) -#define REG_AF_NORMAL 0x00 /* Normal AF, one time */ -#define REG_AF_MACRO 0x01 /* Macro AF, one time */ -#define REG_AF_POWEROFF 0x07 - -#define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1) -#define REG_AF_STOP 0x00 -#define REG_AF_EXE_AUTO 0x01 -#define REG_AF_EXE_CAF 0x02 - -#define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1) -#define REG_AF_FAIL 0x00 -#define REG_AF_SUCCESS 0x02 -#define REG_AF_IDLE 0x04 -#define REG_AF_BUSY 0x05 - -#define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1) - -/* - * Category B - CAPTURE Parameter - */ -#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1) -#define REG_YUV422 0x00 -#define REG_BAYER10 0x05 -#define REG_BAYER8 0x06 -#define REG_JPEG 0x10 - -#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) -#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) - -#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) -#define REG_MCC_OFF 0x00 -#define REG_MCC_NORMAL 0x01 - -#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1) -#define REG_WDR_OFF 0x00 -#define REG_WDR_ON 0x01 -#define REG_WDR_AUTO 0x02 - -#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1) -#define REG_LIGHT_OFF 0x00 -#define REG_LIGHT_ON 0x01 -#define REG_LIGHT_AUTO 0x02 - -#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1) -#define REG_FLASH_OFF 0x00 -#define REG_FLASH_ON 0x01 -#define REG_FLASH_AUTO 0x02 - -/* - * Category C - CAPTURE Control - */ -#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1) -#define REG_CAP_NONE 0x00 -#define REG_CAP_ANTI_SHAKE 0x02 - -/* Select single- or multi-shot capture */ -#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1) - -#define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1) -#define REG_CAP_START_MAIN 0x01 -#define REG_CAP_START_THUMB 0x03 - -#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4) -#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4) - -/* - * Category F - Flash - * - * This mode provides functions about internal flash stuff and system startup. - */ - -/* Starts internal ARM core booting after power-up */ -#define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1) -#define REG_START_ARM_BOOT 0x01 /* write value */ -#define REG_IN_FLASH_MODE 0x00 /* read value */ - -#endif /* M5MOLS_REG_H */ diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c deleted file mode 100644 index aeb22be7dcbd..000000000000 --- a/drivers/media/video/msp3400-driver.c +++ /dev/null @@ -1,899 +0,0 @@ -/* - * Programming the mspx4xx sound processor family - * - * (c) 1997-2001 Gerd Knorr - * - * what works and what doesn't: - * - * AM-Mono - * Support for Hauppauge cards added (decoding handled by tuner) added by - * Frederic Crozat - * - * FM-Mono - * should work. The stereo modes are backward compatible to FM-mono, - * therefore FM-Mono should be allways available. - * - * FM-Stereo (B/G, used in germany) - * should work, with autodetect - * - * FM-Stereo (satellite) - * should work, no autodetect (i.e. default is mono, but you can - * switch to stereo -- untested) - * - * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) - * should work, with autodetect. Support for NICAM was added by - * Pekka Pietikainen - * - * TODO: - * - better SAT support - * - * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) - * using soundcore instead of OSS - * - * This program is free software; 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "msp3400-driver.h" - -/* ---------------------------------------------------------------------- */ - -MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -/* module parameters */ -static int opmode = OPMODE_AUTO; -int msp_debug; /* msp_debug output */ -bool msp_once; /* no continuous stereo monitoring */ -bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france), - the autoscan seems work well only with FM... */ -int msp_standard = 1; /* Override auto detect of audio msp_standard, - if needed. */ -bool msp_dolby; - -int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual - (msp34xxg only) 0x00a0-0x03c0 */ - -/* read-only */ -module_param(opmode, int, 0444); - -/* read-write */ -module_param_named(once, msp_once, bool, 0644); -module_param_named(debug, msp_debug, int, 0644); -module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644); -module_param_named(standard, msp_standard, int, 0644); -module_param_named(amsound, msp_amsound, bool, 0644); -module_param_named(dolby, msp_dolby, bool, 0644); - -MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect"); -MODULE_PARM_DESC(once, "No continuous stereo monitoring"); -MODULE_PARM_DESC(debug, "Enable debug messages [0-3]"); -MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo"); -MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); -MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); -MODULE_PARM_DESC(dolby, "Activates Dolby processing"); - -/* ---------------------------------------------------------------------- */ - -/* control subaddress */ -#define I2C_MSP_CONTROL 0x00 -/* demodulator unit subaddress */ -#define I2C_MSP_DEM 0x10 -/* DSP unit subaddress */ -#define I2C_MSP_DSP 0x12 - - -/* ----------------------------------------------------------------------- */ -/* functions for talking to the MSP3400C Sound processor */ - -int msp_reset(struct i2c_client *client) -{ - /* reset and read revision code */ - static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 }; - static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 }; - static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e }; - u8 read[2]; - struct i2c_msg reset[2] = { - { client->addr, I2C_M_IGNORE_NAK, 3, reset_off }, - { client->addr, I2C_M_IGNORE_NAK, 3, reset_on }, - }; - struct i2c_msg test[2] = { - { client->addr, 0, 3, write }, - { client->addr, I2C_M_RD, 2, read }, - }; - - v4l_dbg(3, msp_debug, client, "msp_reset\n"); - if (i2c_transfer(client->adapter, &reset[0], 1) != 1 || - i2c_transfer(client->adapter, &reset[1], 1) != 1 || - i2c_transfer(client->adapter, test, 2) != 2) { - v4l_err(client, "chip reset failed\n"); - return -1; - } - return 0; -} - -static int msp_read(struct i2c_client *client, int dev, int addr) -{ - int err, retval; - u8 write[3]; - u8 read[2]; - struct i2c_msg msgs[2] = { - { client->addr, 0, 3, write }, - { client->addr, I2C_M_RD, 2, read } - }; - - write[0] = dev + 1; - write[1] = addr >> 8; - write[2] = addr & 0xff; - - for (err = 0; err < 3; err++) { - if (i2c_transfer(client->adapter, msgs, 2) == 2) - break; - v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err, - dev, addr); - schedule_timeout_interruptible(msecs_to_jiffies(10)); - } - if (err == 3) { - v4l_warn(client, "resetting chip, sound will go off.\n"); - msp_reset(client); - return -1; - } - retval = read[0] << 8 | read[1]; - v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", - dev, addr, retval); - return retval; -} - -int msp_read_dem(struct i2c_client *client, int addr) -{ - return msp_read(client, I2C_MSP_DEM, addr); -} - -int msp_read_dsp(struct i2c_client *client, int addr) -{ - return msp_read(client, I2C_MSP_DSP, addr); -} - -static int msp_write(struct i2c_client *client, int dev, int addr, int val) -{ - int err; - u8 buffer[5]; - - buffer[0] = dev; - buffer[1] = addr >> 8; - buffer[2] = addr & 0xff; - buffer[3] = val >> 8; - buffer[4] = val & 0xff; - - v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", - dev, addr, val); - for (err = 0; err < 3; err++) { - if (i2c_master_send(client, buffer, 5) == 5) - break; - v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err, - dev, addr); - schedule_timeout_interruptible(msecs_to_jiffies(10)); - } - if (err == 3) { - v4l_warn(client, "resetting chip, sound will go off.\n"); - msp_reset(client); - return -1; - } - return 0; -} - -int msp_write_dem(struct i2c_client *client, int addr, int val) -{ - return msp_write(client, I2C_MSP_DEM, addr, val); -} - -int msp_write_dsp(struct i2c_client *client, int addr, int val) -{ - return msp_write(client, I2C_MSP_DSP, addr, val); -} - -/* ----------------------------------------------------------------------- * - * bits 9 8 5 - SCART DSP input Select: - * 0 0 0 - SCART 1 to DSP input (reset position) - * 0 1 0 - MONO to DSP input - * 1 0 0 - SCART 2 to DSP input - * 1 1 1 - Mute DSP input - * - * bits 11 10 6 - SCART 1 Output Select: - * 0 0 0 - undefined (reset position) - * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) - * 1 0 0 - MONO input to SCART 1 Output - * 1 1 0 - SCART 1 DA to SCART 1 Output - * 0 0 1 - SCART 2 DA to SCART 1 Output - * 0 1 1 - SCART 1 Input to SCART 1 Output - * 1 1 1 - Mute SCART 1 Output - * - * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART): - * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position) - * 0 1 0 - SCART 1 Input to SCART 2 Output - * 1 0 0 - MONO input to SCART 2 Output - * 0 0 1 - SCART 2 DA to SCART 2 Output - * 0 1 1 - SCART 2 Input to SCART 2 Output - * 1 1 0 - Mute SCART 2 Output - * - * Bits 4 to 0 should be zero. - * ----------------------------------------------------------------------- */ - -static int scarts[3][9] = { - /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */ - /* SCART DSP Input select */ - { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 }, - /* SCART1 Output select */ - { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 }, - /* SCART2 Output select */ - { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 }, -}; - -static char *scart_names[] = { - "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute" -}; - -void msp_set_scart(struct i2c_client *client, int in, int out) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - state->in_scart = in; - - if (in >= 0 && in <= 7 && out >= 0 && out <= 2) { - if (-1 == scarts[out][in + 1]) - return; - - state->acb &= ~scarts[out][0]; - state->acb |= scarts[out][in + 1]; - } else - state->acb = 0xf60; /* Mute Input and SCART 1 Output */ - - v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n", - scart_names[in], out, state->acb); - msp_write_dsp(client, 0x13, state->acb); - - /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ - if (state->has_i2s_conf) - msp_write_dem(client, 0x40, state->i2s_mode); -} - -/* ------------------------------------------------------------------------ */ - -static void msp_wake_thread(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (NULL == state->kthread) - return; - state->watch_stereo = 0; - state->restart = 1; - wake_up_interruptible(&state->wq); -} - -int msp_sleep(struct msp_state *state, int timeout) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&state->wq, &wait); - if (!kthread_should_stop()) { - if (timeout < 0) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } else { - schedule_timeout_interruptible - (msecs_to_jiffies(timeout)); - } - } - - remove_wait_queue(&state->wq, &wait); - try_to_freeze(); - return state->restart; -} - -/* ------------------------------------------------------------------------ */ - -static int msp_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct msp_state *state = ctrl_to_state(ctrl); - struct i2c_client *client = v4l2_get_subdevdata(&state->sd); - int val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: { - /* audio volume cluster */ - int reallymuted = state->muted->val | state->scan_in_progress; - - if (!reallymuted) - val = (val * 0x7f / 65535) << 8; - - v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", - state->muted->val ? "on" : "off", - state->scan_in_progress ? "yes" : "no", - state->volume->val); - - msp_write_dsp(client, 0x0000, val); - msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_headphones) - msp_write_dsp(client, 0x0006, val); - break; - } - - case V4L2_CID_AUDIO_BASS: - val = ((val - 32768) * 0x60 / 65535) << 8; - msp_write_dsp(client, 0x0002, val); - if (state->has_headphones) - msp_write_dsp(client, 0x0031, val); - break; - - case V4L2_CID_AUDIO_TREBLE: - val = ((val - 32768) * 0x60 / 65535) << 8; - msp_write_dsp(client, 0x0003, val); - if (state->has_headphones) - msp_write_dsp(client, 0x0032, val); - break; - - case V4L2_CID_AUDIO_LOUDNESS: - val = val ? ((5 * 4) << 8) : 0; - msp_write_dsp(client, 0x0004, val); - if (state->has_headphones) - msp_write_dsp(client, 0x0033, val); - break; - - case V4L2_CID_AUDIO_BALANCE: - val = (u8)((val / 256) - 128); - msp_write_dsp(client, 0x0001, val << 8); - if (state->has_headphones) - msp_write_dsp(client, 0x0030, val << 8); - break; - - default: - return -EINVAL; - } - return 0; -} - -void msp_update_volume(struct msp_state *state) -{ - /* Force an update of the volume/mute cluster */ - v4l2_ctrl_lock(state->volume); - state->volume->val = state->volume->cur.val; - state->muted->val = state->muted->cur.val; - msp_s_ctrl(state->volume); - v4l2_ctrl_unlock(state->volume); -} - -/* --- v4l2 ioctls --- */ -static int msp_s_radio(struct v4l2_subdev *sd) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio) - return 0; - state->radio = 1; - v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); - state->watch_stereo = 0; - switch (state->opmode) { - case OPMODE_MANUAL: - /* set msp3400 to FM radio mode */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - msp_update_volume(state); - break; - case OPMODE_AUTODETECT: - case OPMODE_AUTOSELECT: - /* the thread will do for us */ - msp_wake_thread(client); - break; - } - return 0; -} - -static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* new channel -- kick audio carrier scan */ - msp_wake_thread(client); - return 0; -} - -static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - *id &= state->detected_std; - - v4l_dbg(2, msp_debug, client, - "detected standard: %s(0x%08Lx)\n", - msp_standard_std_name(state->std), state->detected_std); - - return 0; -} - -static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int update = state->radio || state->v4l2_std != id; - - state->v4l2_std = id; - state->radio = 0; - if (update) - msp_wake_thread(client); - return 0; -} - -static int msp_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int tuner = (input >> 3) & 1; - int sc_in = input & 0x7; - int sc1_out = output & 0xf; - int sc2_out = (output >> 4) & 0xf; - u16 val, reg; - int i; - int extern_input = 1; - - if (state->route_in == input && state->route_out == output) - return 0; - state->route_in = input; - state->route_out = output; - /* check if the tuner input is used */ - for (i = 0; i < 5; i++) { - if (((input >> (4 + i * 4)) & 0xf) == 0) - extern_input = 0; - } - state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp_set_scart(client, sc_in, 0); - msp_set_scart(client, sc1_out, 1); - msp_set_scart(client, sc2_out, 2); - msp_set_audmode(client); - reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; - val = msp_read_dem(client, reg); - msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); - /* wake thread when a new input is chosen */ - msp_wake_thread(client); - return 0; -} - -static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (vt->type != V4L2_TUNER_ANALOG_TV) - return 0; - if (!state->radio) { - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - vt->rxsubchans = state->rxsubchans; - } - vt->audmode = state->audmode; - vt->capability |= V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - return 0; -} - -static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (state->radio) /* TODO: add mono/stereo support for radio */ - return 0; - if (state->audmode == vt->audmode) - return 0; - state->audmode = vt->audmode; - /* only set audmode */ - msp_set_audmode(client); - return 0; -} - -static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); - - switch (freq) { - case 1024000: - state->i2s_mode = 0; - break; - case 2048000: - state->i2s_mode = 1; - break; - default: - return -EINVAL; - } - return 0; -} - -static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->ident, - (state->rev1 << 16) | state->rev2); -} - -static int msp_log_status(struct v4l2_subdev *sd) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - const char *p; - char prefix[V4L2_SUBDEV_NAME_SIZE + 20]; - - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", - client->name, state->rev1, state->rev2); - snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name); - v4l2_ctrl_handler_log_status(&state->hdl, prefix); - switch (state->mode) { - case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; - case MSP_MODE_FM_RADIO: p = "FM Radio"; break; - case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break; - case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break; - case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break; - case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break; - case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break; - case MSP_MODE_BTSC: p = "BTSC"; break; - case MSP_MODE_EXTERN: p = "External input"; break; - default: p = "unknown"; break; - } - if (state->mode == MSP_MODE_EXTERN) { - v4l_info(client, "Mode: %s\n", p); - } else if (state->opmode == OPMODE_MANUAL) { - v4l_info(client, "Mode: %s (%s%s)\n", p, - (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", - (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } else { - if (state->opmode == OPMODE_AUTODETECT) - v4l_info(client, "Mode: %s\n", p); - v4l_info(client, "Standard: %s (%s%s)\n", - msp_standard_std_name(state->std), - (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", - (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } - v4l_info(client, "Audmode: 0x%04x\n", state->audmode); - v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", - state->route_in, state->route_out); - v4l_info(client, "ACB: 0x%04x\n", state->acb); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int msp_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - v4l_dbg(1, msp_debug, client, "suspend\n"); - msp_reset(client); - return 0; -} - -static int msp_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - v4l_dbg(1, msp_debug, client, "resume\n"); - msp_wake_thread(client); - return 0; -} -#endif - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops msp_ctrl_ops = { - .s_ctrl = msp_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops msp_core_ops = { - .log_status = msp_log_status, - .g_chip_ident = msp_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = msp_s_std, -}; - -static const struct v4l2_subdev_video_ops msp_video_ops = { - .querystd = msp_querystd, -}; - -static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { - .s_frequency = msp_s_frequency, - .g_tuner = msp_g_tuner, - .s_tuner = msp_s_tuner, - .s_radio = msp_s_radio, -}; - -static const struct v4l2_subdev_audio_ops msp_audio_ops = { - .s_routing = msp_s_routing, - .s_i2s_clock_freq = msp_s_i2s_clock_freq, -}; - -static const struct v4l2_subdev_ops msp_ops = { - .core = &msp_core_ops, - .video = &msp_video_ops, - .tuner = &msp_tuner_ops, - .audio = &msp_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct msp_state *state; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - int (*thread_func)(void *data) = NULL; - int msp_hard; - int msp_family; - int msp_revision; - int msp_product, msp_prod_hi, msp_prod_lo; - int msp_rom; - - if (!id) - strlcpy(client->name, "msp3400", sizeof(client->name)); - - if (msp_reset(client) == -1) { - v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); - return -ENODEV; - } - - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &msp_ops); - - state->v4l2_std = V4L2_STD_NTSC; - state->detected_std = V4L2_STD_ALL; - state->audmode = V4L2_TUNER_MODE_STEREO; - state->input = -1; - state->i2s_mode = 0; - init_waitqueue_head(&state->wq); - /* These are the reset input/output positions */ - state->route_in = MSP_INPUT_DEFAULT; - state->route_out = MSP_OUTPUT_DEFAULT; - - state->rev1 = msp_read_dsp(client, 0x1e); - if (state->rev1 != -1) - state->rev2 = msp_read_dsp(client, 0x1f); - v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", - state->rev1, state->rev2); - if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) { - v4l_dbg(1, msp_debug, client, - "not an msp3400 (cannot read chip version)\n"); - kfree(state); - return -ENODEV; - } - - msp_family = ((state->rev1 >> 4) & 0x0f) + 3; - msp_product = (state->rev2 >> 8) & 0xff; - msp_prod_hi = msp_product / 10; - msp_prod_lo = msp_product % 10; - msp_revision = (state->rev1 & 0x0f) + '@'; - msp_hard = ((state->rev1 >> 8) & 0xff) + '@'; - msp_rom = state->rev2 & 0x1f; - /* Rev B=2, C=3, D=4, G=7 */ - state->ident = msp_family * 10000 + 4000 + msp_product * 10 + - msp_revision - '@'; - - /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ - state->has_nicam = - msp_prod_hi == 1 || msp_prod_hi == 5; - /* Has radio support: was added with revision G */ - state->has_radio = - msp_revision >= 'G'; - /* Has headphones output: not for stripped down products */ - state->has_headphones = - msp_prod_lo < 5; - /* Has scart2 input: not in stripped down products of the '3' family */ - state->has_scart2 = - msp_family >= 4 || msp_prod_lo < 7; - /* Has scart3 input: not in stripped down products of the '3' family */ - state->has_scart3 = - msp_family >= 4 || msp_prod_lo < 5; - /* Has scart4 input: not in pre D revisions, not in stripped D revs */ - state->has_scart4 = - msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); - /* Has scart2 output: not in stripped down products of - * the '3' family */ - state->has_scart2_out = - msp_family >= 4 || msp_prod_lo < 5; - /* Has scart2 a volume control? Not in pre-D revisions. */ - state->has_scart2_out_volume = - msp_revision > 'C' && state->has_scart2_out; - /* Has a configurable i2s out? */ - state->has_i2s_conf = - msp_revision >= 'G' && msp_prod_lo < 7; - /* Has subwoofer output: not in pre-D revs and not in stripped down - * products */ - state->has_subwoofer = - msp_revision >= 'D' && msp_prod_lo < 5; - /* Has soundprocessing (bass/treble/balance/loudness/equalizer): - * not in stripped down products */ - state->has_sound_processing = - msp_prod_lo < 7; - /* Has Virtual Dolby Surround: only in msp34x1 */ - state->has_virtual_dolby_surround = - msp_revision == 'G' && msp_prod_lo == 1; - /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */ - state->has_dolby_pro_logic = - msp_revision == 'G' && msp_prod_lo == 2; - /* The msp343xG supports BTSC only and cannot do Automatic Standard - * Detection. */ - state->force_btsc = - msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3; - - state->opmode = opmode; - if (state->opmode == OPMODE_AUTO) { - /* MSP revision G and up have both autodetect and autoselect */ - if (msp_revision >= 'G') - state->opmode = OPMODE_AUTOSELECT; - /* MSP revision D and up have autodetect */ - else if (msp_revision >= 'D') - state->opmode = OPMODE_AUTODETECT; - else - state->opmode = OPMODE_MANUAL; - } - - hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, 6); - if (state->has_sound_processing) { - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0); - } - state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880); - v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); - state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(state); - return err; - } - - v4l2_ctrl_cluster(2, &state->volume); - v4l2_ctrl_handler_setup(hdl); - - /* hello world :-) */ - v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", - msp_family, msp_product, - msp_revision, msp_hard, msp_rom, - client->addr << 1, client->adapter->name); - v4l_info(client, "%s ", client->name); - if (state->has_nicam && state->has_radio) - printk(KERN_CONT "supports nicam and radio, "); - else if (state->has_nicam) - printk(KERN_CONT "supports nicam, "); - else if (state->has_radio) - printk(KERN_CONT "supports radio, "); - printk(KERN_CONT "mode is "); - - /* version-specific initialization */ - switch (state->opmode) { - case OPMODE_MANUAL: - printk(KERN_CONT "manual"); - thread_func = msp3400c_thread; - break; - case OPMODE_AUTODETECT: - printk(KERN_CONT "autodetect"); - thread_func = msp3410d_thread; - break; - case OPMODE_AUTOSELECT: - printk(KERN_CONT "autodetect and autoselect"); - thread_func = msp34xxg_thread; - break; - } - printk(KERN_CONT "\n"); - - /* startup control thread if needed */ - if (thread_func) { - state->kthread = kthread_run(thread_func, client, "msp34xx"); - - if (IS_ERR(state->kthread)) - v4l_warn(client, "kernel_thread() failed\n"); - msp_wake_thread(client); - } - return 0; -} - -static int msp_remove(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - v4l2_device_unregister_subdev(&state->sd); - /* shutdown control thread */ - if (state->kthread) { - state->restart = 1; - kthread_stop(state->kthread); - } - msp_reset(client); - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct dev_pm_ops msp3400_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) -}; - -static const struct i2c_device_id msp_id[] = { - { "msp3400", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, msp_id); - -static struct i2c_driver msp_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "msp3400", - .pm = &msp3400_pm_ops, - }, - .probe = msp_probe, - .remove = msp_remove, - .id_table = msp_id, -}; - -module_i2c_driver(msp_driver); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h deleted file mode 100644 index fbe5e0715f93..000000000000 --- a/drivers/media/video/msp3400-driver.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - */ - -#ifndef MSP3400_DRIVER_H -#define MSP3400_DRIVER_H - -#include -#include -#include - -/* ---------------------------------------------------------------------- */ - -/* This macro is allowed for *constants* only, gcc must calculate it - at compile time. Remember -- no floats in kernel mode */ -#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) - -#define MSP_MODE_AM_DETECT 0 -#define MSP_MODE_FM_RADIO 2 -#define MSP_MODE_FM_TERRA 3 -#define MSP_MODE_FM_SAT 4 -#define MSP_MODE_FM_NICAM1 5 -#define MSP_MODE_FM_NICAM2 6 -#define MSP_MODE_AM_NICAM 7 -#define MSP_MODE_BTSC 8 -#define MSP_MODE_EXTERN 9 - -#define SCART_IN1 0 -#define SCART_IN2 1 -#define SCART_IN3 2 -#define SCART_IN4 3 -#define SCART_IN1_DA 4 -#define SCART_IN2_DA 5 -#define SCART_MONO 6 -#define SCART_MUTE 7 - -#define SCART_DSP_IN 0 -#define SCART1_OUT 1 -#define SCART2_OUT 2 - -#define OPMODE_AUTO -1 -#define OPMODE_MANUAL 0 -#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ -#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ - -/* module parameters */ -extern int msp_debug; -extern bool msp_once; -extern bool msp_amsound; -extern int msp_standard; -extern bool msp_dolby; -extern int msp_stereo_thresh; - -struct msp_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - int rev1, rev2; - int ident; - u8 has_nicam; - u8 has_radio; - u8 has_headphones; - u8 has_ntsc_jp_d_k3; - u8 has_scart2; - u8 has_scart3; - u8 has_scart4; - u8 has_scart2_out; - u8 has_scart2_out_volume; - u8 has_i2s_conf; - u8 has_subwoofer; - u8 has_sound_processing; - u8 has_virtual_dolby_surround; - u8 has_dolby_pro_logic; - u8 force_btsc; - - int radio; - int opmode; - int std; - int mode; - v4l2_std_id v4l2_std, detected_std; - int nicam_on; - int acb; - int in_scart; - int i2s_mode; - int main, second; /* sound carrier */ - int input; - u32 route_in; - u32 route_out; - - /* v4l2 */ - int audmode; - int rxsubchans; - - struct { - /* volume cluster */ - struct v4l2_ctrl *volume; - struct v4l2_ctrl *muted; - }; - - int scan_in_progress; - - /* thread */ - struct task_struct *kthread; - wait_queue_head_t wq; - unsigned int restart:1; - unsigned int watch_stereo:1; -}; - -static inline struct msp_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct msp_state, sd); -} - -static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct msp_state, hdl); -} - -/* msp3400-driver.c */ -int msp_write_dem(struct i2c_client *client, int addr, int val); -int msp_write_dsp(struct i2c_client *client, int addr, int val); -int msp_read_dem(struct i2c_client *client, int addr); -int msp_read_dsp(struct i2c_client *client, int addr); -int msp_reset(struct i2c_client *client); -void msp_set_scart(struct i2c_client *client, int in, int out); -void msp_update_volume(struct msp_state *state); -int msp_sleep(struct msp_state *state, int timeout); - -/* msp3400-kthreads.c */ -const char *msp_standard_std_name(int std); -void msp_set_audmode(struct i2c_client *client); -int msp_detect_stereo(struct i2c_client *client); -int msp3400c_thread(void *data); -int msp3410d_thread(void *data); -int msp34xxg_thread(void *data); -void msp3400c_set_mode(struct i2c_client *client, int mode); -void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2); - -#endif /* MSP3400_DRIVER_H */ diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c deleted file mode 100644 index f8b51714f2f9..000000000000 --- a/drivers/media/video/msp3400-kthreads.c +++ /dev/null @@ -1,1165 +0,0 @@ -/* - * Programming the mspx4xx sound processor family - * - * (c) 1997-2001 Gerd Knorr - * - * This program is free software; 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include "msp3400-driver.h" - -/* this one uses the automatic sound standard detection of newer msp34xx - chip versions */ -static struct { - int retval; - int main, second; - char *name; - v4l2_std_id std; -} msp_stdlist[] = { - { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL }, - { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL }, - { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), - "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN }, - { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), - "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG }, - { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), - "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK }, - { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), - "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK }, - { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), - "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK }, - { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), - "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK }, - { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), - "5.5/5.85 B/G NICAM FM", V4L2_STD_BG }, - { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 L NICAM AM", V4L2_STD_L }, - { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), - "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I }, - { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 D/K NICAM FM", V4L2_STD_DK }, - { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK }, - { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), - "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK }, - { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), - "4.5 M BTSC-Stereo", V4L2_STD_MTS }, - { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), - "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS }, - { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), - "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP }, - { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), - "10.7 FM-Stereo Radio", V4L2_STD_ALL }, - { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), - "6.5 SAT-Mono", V4L2_STD_ALL }, - { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), - "7.02/7.20 SAT-Stereo", V4L2_STD_ALL }, - { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), - "7.2 SAT ADR", V4L2_STD_ALL }, - { -1, 0, 0, NULL, 0 }, /* EOF */ -}; - -static struct msp3400c_init_data_dem { - int fir1[6]; - int fir2[6]; - int cdo1; - int cdo2; - int ad_cv; - int mode_reg; - int dsp_src; - int dsp_matrix; -} msp3400c_init_data[] = { - { /* AM (for carrier detect / msp3400) */ - {75, 19, 36, 35, 39, 40}, - {75, 19, 36, 35, 39, 40}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0500, 0x0020, 0x3000 - }, { /* AM (for carrier detect / msp3410) */ - {-1, -1, -8, 2, 59, 126}, - {-1, -1, -8, 2, 59, 126}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0100, 0x0020, 0x3000 - }, { /* FM Radio */ - {-8, -8, 4, 6, 78, 107}, - {-8, -8, 4, 6, 78, 107}, - MSP_CARRIER(10.7), MSP_CARRIER(10.7), - 0x00d0, 0x0480, 0x0020, 0x3000 - }, { /* Terrestrial FM-mono + FM-stereo */ - {3, 18, 27, 48, 66, 72}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0480, 0x0030, 0x3000 - }, { /* Sat FM-mono */ - { 1, 9, 14, 24, 33, 37}, - { 3, 18, 27, 48, 66, 72}, - MSP_CARRIER(6.5), MSP_CARRIER(6.5), - 0x00c6, 0x0480, 0x0000, 0x3000 - }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ - {-2, -8, -10, 10, 50, 86}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0040, 0x0120, 0x3000 - }, { /* NICAM/FM -- I (6.0/6.552) */ - {2, 4, -6, -4, 40, 94}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(6.0), MSP_CARRIER(6.0), - 0x00d0, 0x0040, 0x0120, 0x3000 - }, { /* NICAM/AM -- L (6.5/5.85) */ - {-2, -8, -10, 10, 50, 86}, - {-4, -12, -9, 23, 79, 126}, - MSP_CARRIER(6.5), MSP_CARRIER(6.5), - 0x00c6, 0x0140, 0x0120, 0x7c00 - }, -}; - -struct msp3400c_carrier_detect { - int cdo; - char *name; -}; - -static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { - /* main carrier */ - { MSP_CARRIER(4.5), "4.5 NTSC" }, - { MSP_CARRIER(5.5), "5.5 PAL B/G" }, - { MSP_CARRIER(6.0), "6.0 PAL I" }, - { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } -}; - -static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { - /* PAL B/G */ - { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, - { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } -}; - -static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { - /* PAL SAT / SECAM */ - { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, - { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, - { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, - { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, - { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, - { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, -}; - -/* ------------------------------------------------------------------------ */ - -const char *msp_standard_std_name(int std) -{ - int i; - - for (i = 0; msp_stdlist[i].name != NULL; i++) - if (msp_stdlist[i].retval == std) - return msp_stdlist[i].name; - return "unknown"; -} - -static v4l2_std_id msp_standard_std(int std) -{ - int i; - - for (i = 0; msp_stdlist[i].name != NULL; i++) - if (msp_stdlist[i].retval == std) - return msp_stdlist[i].std; - return V4L2_STD_ALL; -} - -static void msp_set_source(struct i2c_client *client, u16 src) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (msp_dolby) { - msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ - msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ - } else { - msp_write_dsp(client, 0x0008, src); - msp_write_dsp(client, 0x0009, src); - } - msp_write_dsp(client, 0x000a, src); - msp_write_dsp(client, 0x000b, src); - msp_write_dsp(client, 0x000c, src); - if (state->has_scart2_out) - msp_write_dsp(client, 0x0041, src); -} - -void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) -{ - msp_write_dem(client, 0x0093, cdo1 & 0xfff); - msp_write_dem(client, 0x009b, cdo1 >> 12); - msp_write_dem(client, 0x00a3, cdo2 & 0xfff); - msp_write_dem(client, 0x00ab, cdo2 >> 12); - msp_write_dem(client, 0x0056, 0); /* LOAD_REG_1/2 */ -} - -void msp3400c_set_mode(struct i2c_client *client, int mode) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; - int tuner = (state->route_in >> 3) & 1; - int i; - - v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode); - state->mode = mode; - state->rxsubchans = V4L2_TUNER_SUB_MONO; - - msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0)); - - for (i = 5; i >= 0; i--) /* fir 1 */ - msp_write_dem(client, 0x0001, data->fir1[i]); - - msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ - msp_write_dem(client, 0x0005, 0x0040); - msp_write_dem(client, 0x0005, 0x0000); - for (i = 5; i >= 0; i--) - msp_write_dem(client, 0x0005, data->fir2[i]); - - msp_write_dem(client, 0x0083, data->mode_reg); - - msp3400c_set_carrier(client, data->cdo1, data->cdo2); - - msp_set_source(client, data->dsp_src); - /* set prescales */ - - /* volume prescale for SCART (AM mono input) */ - msp_write_dsp(client, 0x000d, 0x1900); - msp_write_dsp(client, 0x000e, data->dsp_matrix); - if (state->has_nicam) /* nicam prescale */ - msp_write_dsp(client, 0x0010, 0x5a00); -} - -/* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP, - nor do they support stereo BTSC. */ -static void msp3400c_set_audmode(struct i2c_client *client) -{ - static char *strmode[] = { - "mono", "stereo", "lang2", "lang1", "lang1+lang2" - }; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - char *modestr = (state->audmode >= 0 && state->audmode < 5) ? - strmode[state->audmode] : "unknown"; - int src = 0; /* channel source: FM/AM, nicam or SCART */ - int audmode = state->audmode; - - if (state->opmode == OPMODE_AUTOSELECT) { - /* this method would break everything, let's make sure - * it's never called - */ - v4l_dbg(1, msp_debug, client, - "set_audmode called with mode=%d instead of set_source (ignored)\n", - state->audmode); - return; - } - - /* Note: for the C and D revs no NTSC stereo + SAP is possible as - the hardware does not support SAP. So the rxsubchans combination - of STEREO | LANG2 does not occur. */ - - if (state->mode != MSP_MODE_EXTERN) { - /* switch to mono if only mono is available */ - if (state->rxsubchans == V4L2_TUNER_SUB_MONO) - audmode = V4L2_TUNER_MODE_MONO; - /* if bilingual */ - else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { - /* and mono or stereo, then fallback to lang1 */ - if (audmode == V4L2_TUNER_MODE_MONO || - audmode == V4L2_TUNER_MODE_STEREO) - audmode = V4L2_TUNER_MODE_LANG1; - } - /* if stereo, and audmode is not mono, then switch to stereo */ - else if (audmode != V4L2_TUNER_MODE_MONO) - audmode = V4L2_TUNER_MODE_STEREO; - } - - /* switch demodulator */ - switch (state->mode) { - case MSP_MODE_FM_TERRA: - v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr); - switch (audmode) { - case V4L2_TUNER_MODE_STEREO: - msp_write_dsp(client, 0x000e, 0x3001); - break; - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_LANG1: - case V4L2_TUNER_MODE_LANG2: - case V4L2_TUNER_MODE_LANG1_LANG2: - msp_write_dsp(client, 0x000e, 0x3000); - break; - } - break; - case MSP_MODE_FM_SAT: - v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr); - switch (audmode) { - case V4L2_TUNER_MODE_MONO: - msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); - break; - case V4L2_TUNER_MODE_LANG1: - msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - case V4L2_TUNER_MODE_LANG2: - msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - } - break; - case MSP_MODE_FM_NICAM1: - case MSP_MODE_FM_NICAM2: - case MSP_MODE_AM_NICAM: - v4l_dbg(1, msp_debug, client, - "NICAM set_audmode: %s\n", modestr); - if (state->nicam_on) - src = 0x0100; /* NICAM */ - break; - case MSP_MODE_BTSC: - v4l_dbg(1, msp_debug, client, - "BTSC set_audmode: %s\n", modestr); - break; - case MSP_MODE_EXTERN: - v4l_dbg(1, msp_debug, client, - "extern set_audmode: %s\n", modestr); - src = 0x0200; /* SCART */ - break; - case MSP_MODE_FM_RADIO: - v4l_dbg(1, msp_debug, client, - "FM-Radio set_audmode: %s\n", modestr); - break; - default: - v4l_dbg(1, msp_debug, client, "mono set_audmode\n"); - return; - } - - /* switch audio */ - v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode); - switch (audmode) { - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - src |= 0x0020; - break; - case V4L2_TUNER_MODE_MONO: - if (state->mode == MSP_MODE_AM_NICAM) { - v4l_dbg(1, msp_debug, client, "switching to AM mono\n"); - /* AM mono decoding is handled by tuner, not MSP chip */ - /* SCART switching control register */ - msp_set_scart(client, SCART_MONO, 0); - src = 0x0200; - break; - } - if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) - src = 0x0030; - break; - case V4L2_TUNER_MODE_LANG1: - break; - case V4L2_TUNER_MODE_LANG2: - src |= 0x0010; - break; - } - v4l_dbg(1, msp_debug, client, - "set_audmode final source/matrix = 0x%x\n", src); - - msp_set_source(client, src); -} - -static void msp3400c_print_mode(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (state->main == state->second) - v4l_dbg(1, msp_debug, client, - "mono sound carrier: %d.%03d MHz\n", - state->main / 910000, (state->main / 910) % 1000); - else - v4l_dbg(1, msp_debug, client, - "main sound carrier: %d.%03d MHz\n", - state->main / 910000, (state->main / 910) % 1000); - if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) - v4l_dbg(1, msp_debug, client, - "NICAM/FM carrier : %d.%03d MHz\n", - state->second / 910000, (state->second/910) % 1000); - if (state->mode == MSP_MODE_AM_NICAM) - v4l_dbg(1, msp_debug, client, - "NICAM/AM carrier : %d.%03d MHz\n", - state->second / 910000, (state->second / 910) % 1000); - if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { - v4l_dbg(1, msp_debug, client, - "FM-stereo carrier : %d.%03d MHz\n", - state->second / 910000, (state->second / 910) % 1000); - } -} - -/* ----------------------------------------------------------------------- */ - -static int msp3400c_detect_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int val; - int rxsubchans = state->rxsubchans; - int newnicam = state->nicam_on; - int update = 0; - - switch (state->mode) { - case MSP_MODE_FM_TERRA: - val = msp_read_dsp(client, 0x18); - if (val > 32767) - val -= 65536; - v4l_dbg(2, msp_debug, client, - "stereo detect register: %d\n", val); - if (val > 8192) { - rxsubchans = V4L2_TUNER_SUB_STEREO; - } else if (val < -4096) { - rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } else { - rxsubchans = V4L2_TUNER_SUB_MONO; - } - newnicam = 0; - break; - case MSP_MODE_FM_NICAM1: - case MSP_MODE_FM_NICAM2: - case MSP_MODE_AM_NICAM: - val = msp_read_dem(client, 0x23); - v4l_dbg(2, msp_debug, client, "nicam sync=%d, mode=%d\n", - val & 1, (val & 0x1e) >> 1); - - if (val & 1) { - /* nicam synced */ - switch ((val & 0x1e) >> 1) { - case 0: - case 8: - rxsubchans = V4L2_TUNER_SUB_STEREO; - break; - case 1: - case 9: - rxsubchans = V4L2_TUNER_SUB_MONO; - break; - case 2: - case 10: - rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - break; - default: - rxsubchans = V4L2_TUNER_SUB_MONO; - break; - } - newnicam = 1; - } else { - newnicam = 0; - rxsubchans = V4L2_TUNER_SUB_MONO; - } - break; - } - if (rxsubchans != state->rxsubchans) { - update = 1; - v4l_dbg(1, msp_debug, client, - "watch: rxsubchans %02x => %02x\n", - state->rxsubchans, rxsubchans); - state->rxsubchans = rxsubchans; - } - if (newnicam != state->nicam_on) { - update = 1; - v4l_dbg(1, msp_debug, client, "watch: nicam %d => %d\n", - state->nicam_on, newnicam); - state->nicam_on = newnicam; - } - return update; -} - -/* - * A kernel thread for msp3400 control -- we don't want to block the - * in the ioctl while doing the sound carrier & stereo detect - */ -/* stereo/multilang monitoring */ -static void watch_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (msp_detect_stereo(client)) - msp_set_audmode(client); - - if (msp_once) - state->watch_stereo = 0; -} - -int msp3400c_thread(void *data) -{ - struct i2c_client *client = data; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - struct msp3400c_carrier_detect *cd; - int count, max1, max2, val1, val2, val, i; - - v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); - state->detected_std = V4L2_STD_ALL; - set_freezable(); - for (;;) { - v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n"); - msp_sleep(state, -1); - v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n"); - -restart: - v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); - state->restart = 0; - if (kthread_should_stop()) - break; - - if (state->radio || MSP_MODE_EXTERN == state->mode) { - /* no carrier scan, just unmute */ - v4l_dbg(1, msp_debug, client, - "thread: no carrier scan\n"); - state->scan_in_progress = 0; - msp_update_volume(state); - continue; - } - - /* mute audio */ - state->scan_in_progress = 1; - msp_update_volume(state); - - msp3400c_set_mode(client, MSP_MODE_AM_DETECT); - val1 = val2 = 0; - max1 = max2 = -1; - state->watch_stereo = 0; - state->nicam_on = 0; - - /* wait for tuner to settle down after a channel change */ - if (msp_sleep(state, 200)) - goto restart; - - /* carrier detect pass #1 -- main carrier */ - cd = msp3400c_carrier_detect_main; - count = ARRAY_SIZE(msp3400c_carrier_detect_main); - - if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { - /* autodetect doesn't work well with AM ... */ - max1 = 3; - count = 0; - v4l_dbg(1, msp_debug, client, "AM sound override\n"); - } - - for (i = 0; i < count; i++) { - msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); - if (msp_sleep(state, 100)) - goto restart; - val = msp_read_dsp(client, 0x1b); - if (val > 32767) - val -= 65536; - if (val1 < val) - val1 = val, max1 = i; - v4l_dbg(1, msp_debug, client, - "carrier1 val: %5d / %s\n", val, cd[i].name); - } - - /* carrier detect pass #2 -- second (stereo) carrier */ - switch (max1) { - case 1: /* 5.5 */ - cd = msp3400c_carrier_detect_55; - count = ARRAY_SIZE(msp3400c_carrier_detect_55); - break; - case 3: /* 6.5 */ - cd = msp3400c_carrier_detect_65; - count = ARRAY_SIZE(msp3400c_carrier_detect_65); - break; - case 0: /* 4.5 */ - case 2: /* 6.0 */ - default: - cd = NULL; - count = 0; - break; - } - - if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { - /* autodetect doesn't work well with AM ... */ - cd = NULL; - count = 0; - max2 = 0; - } - for (i = 0; i < count; i++) { - msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); - if (msp_sleep(state, 100)) - goto restart; - val = msp_read_dsp(client, 0x1b); - if (val > 32767) - val -= 65536; - if (val2 < val) - val2 = val, max2 = i; - v4l_dbg(1, msp_debug, client, - "carrier2 val: %5d / %s\n", val, cd[i].name); - } - - /* program the msp3400 according to the results */ - state->main = msp3400c_carrier_detect_main[max1].cdo; - switch (max1) { - case 1: /* 5.5 */ - state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H; - if (max2 == 0) { - /* B/G FM-stereo */ - state->second = msp3400c_carrier_detect_55[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - state->watch_stereo = 1; - } else if (max2 == 1 && state->has_nicam) { - /* B/G NICAM */ - state->second = msp3400c_carrier_detect_55[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - state->nicam_on = 1; - state->watch_stereo = 1; - } else { - goto no_second; - } - break; - case 2: /* 6.0 */ - /* PAL I NICAM */ - state->detected_std = V4L2_STD_PAL_I; - state->second = MSP_CARRIER(6.552); - msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); - state->nicam_on = 1; - state->watch_stereo = 1; - break; - case 3: /* 6.5 */ - if (max2 == 1 || max2 == 2) { - /* D/K FM-stereo */ - state->second = msp3400c_carrier_detect_65[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - state->watch_stereo = 1; - state->detected_std = V4L2_STD_DK; - } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { - /* L NICAM or AM-mono */ - state->second = msp3400c_carrier_detect_65[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_AM_NICAM); - state->watch_stereo = 1; - state->detected_std = V4L2_STD_L; - } else if (max2 == 0 && state->has_nicam) { - /* D/K NICAM */ - state->second = msp3400c_carrier_detect_65[max2].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - state->nicam_on = 1; - state->watch_stereo = 1; - state->detected_std = V4L2_STD_DK; - } else { - goto no_second; - } - break; - case 0: /* 4.5 */ - state->detected_std = V4L2_STD_MN; - default: -no_second: - state->second = msp3400c_carrier_detect_main[max1].cdo; - msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - break; - } - msp3400c_set_carrier(client, state->second, state->main); - - /* unmute */ - state->scan_in_progress = 0; - msp3400c_set_audmode(client); - msp_update_volume(state); - - if (msp_debug) - msp3400c_print_mode(client); - - /* monitor tv audio mode, the first time don't wait - so long to get a quick stereo/bilingual result */ - count = 3; - while (state->watch_stereo) { - if (msp_sleep(state, count ? 1000 : 5000)) - goto restart; - if (count) - count--; - watch_stereo(client); - } - } - v4l_dbg(1, msp_debug, client, "thread: exit\n"); - return 0; -} - - -int msp3410d_thread(void *data) -{ - struct i2c_client *client = data; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int val, i, std, count; - - v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); - state->detected_std = V4L2_STD_ALL; - set_freezable(); - for (;;) { - v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n"); - msp_sleep(state, -1); - v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n"); - -restart: - v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); - state->restart = 0; - if (kthread_should_stop()) - break; - - if (state->mode == MSP_MODE_EXTERN) { - /* no carrier scan needed, just unmute */ - v4l_dbg(1, msp_debug, client, - "thread: no carrier scan\n"); - state->scan_in_progress = 0; - msp_update_volume(state); - continue; - } - - /* mute audio */ - state->scan_in_progress = 1; - msp_update_volume(state); - - /* start autodetect. Note: autodetect is not supported for - NTSC-M and radio, hence we force the standard in those - cases. */ - if (state->radio) - std = 0x40; - else - std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; - state->watch_stereo = 0; - state->nicam_on = 0; - - /* wait for tuner to settle down after a channel change */ - if (msp_sleep(state, 200)) - goto restart; - - if (msp_debug) - v4l_dbg(2, msp_debug, client, - "setting standard: %s (0x%04x)\n", - msp_standard_std_name(std), std); - - if (std != 1) { - /* programmed some specific mode */ - val = std; - } else { - /* triggered autodetect */ - msp_write_dem(client, 0x20, std); - for (;;) { - if (msp_sleep(state, 100)) - goto restart; - - /* check results */ - val = msp_read_dem(client, 0x7e); - if (val < 0x07ff) - break; - v4l_dbg(2, msp_debug, client, - "detection still in progress\n"); - } - } - for (i = 0; msp_stdlist[i].name != NULL; i++) - if (msp_stdlist[i].retval == val) - break; - v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n", - msp_standard_std_name(val), val); - state->main = msp_stdlist[i].main; - state->second = msp_stdlist[i].second; - state->std = val; - state->rxsubchans = V4L2_TUNER_SUB_MONO; - - if (msp_amsound && !state->radio && - (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { - /* autodetection has failed, let backup */ - v4l_dbg(1, msp_debug, client, "autodetection failed," - " switching to backup standard: %s (0x%04x)\n", - msp_stdlist[8].name ? - msp_stdlist[8].name : "unknown", val); - state->std = val = 0x0009; - msp_write_dem(client, 0x20, val); - } else { - state->detected_std = msp_standard_std(state->std); - } - - /* set stereo */ - switch (val) { - case 0x0008: /* B/G NICAM */ - case 0x000a: /* I NICAM */ - case 0x000b: /* D/K NICAM */ - if (val == 0x000a) - state->mode = MSP_MODE_FM_NICAM2; - else - state->mode = MSP_MODE_FM_NICAM1; - /* just turn on stereo */ - state->nicam_on = 1; - state->watch_stereo = 1; - break; - case 0x0009: - state->mode = MSP_MODE_AM_NICAM; - state->nicam_on = 1; - state->watch_stereo = 1; - break; - case 0x0020: /* BTSC */ - /* The pre-'G' models only have BTSC-mono */ - state->mode = MSP_MODE_BTSC; - break; - case 0x0040: /* FM radio */ - state->mode = MSP_MODE_FM_RADIO; - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - /* not needed in theory if we have radio, but - short programming enables carrier mute */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - break; - case 0x0002: - case 0x0003: - case 0x0004: - case 0x0005: - state->mode = MSP_MODE_FM_TERRA; - state->watch_stereo = 1; - break; - } - - /* set various prescales */ - msp_write_dsp(client, 0x0d, 0x1900); /* scart */ - msp_write_dsp(client, 0x0e, 0x3000); /* FM */ - if (state->has_nicam) - msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ - - if (state->has_i2s_conf) - msp_write_dem(client, 0x40, state->i2s_mode); - - /* unmute */ - msp3400c_set_audmode(client); - state->scan_in_progress = 0; - msp_update_volume(state); - - /* monitor tv audio mode, the first time don't wait - so long to get a quick stereo/bilingual result */ - count = 3; - while (state->watch_stereo) { - if (msp_sleep(state, count ? 1000 : 5000)) - goto restart; - if (count) - count--; - watch_stereo(client); - } - } - v4l_dbg(1, msp_debug, client, "thread: exit\n"); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -/* msp34xxG + (autoselect no-thread) - * this one uses both automatic standard detection and automatic sound - * select which are available in the newer G versions - * struct msp: only norm, acb and source are really used in this mode - */ - -static int msp34xxg_modus(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (state->radio) { - v4l_dbg(1, msp_debug, client, "selected radio modus\n"); - return 0x0001; - } - if (state->v4l2_std == V4L2_STD_NTSC_M_JP) { - v4l_dbg(1, msp_debug, client, "selected M (EIA-J) modus\n"); - return 0x4001; - } - if (state->v4l2_std == V4L2_STD_NTSC_M_KR) { - v4l_dbg(1, msp_debug, client, "selected M (A2) modus\n"); - return 0x0001; - } - if (state->v4l2_std == V4L2_STD_SECAM_L) { - v4l_dbg(1, msp_debug, client, "selected SECAM-L modus\n"); - return 0x6001; - } - if (state->v4l2_std & V4L2_STD_MN) { - v4l_dbg(1, msp_debug, client, "selected M (BTSC) modus\n"); - return 0x2001; - } - return 0x7001; -} - -static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) - { - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int source, matrix; - - switch (state->audmode) { - case V4L2_TUNER_MODE_MONO: - source = 0; /* mono only */ - matrix = 0x30; - break; - case V4L2_TUNER_MODE_LANG2: - source = 4; /* stereo or B */ - matrix = 0x10; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - source = 1; /* stereo or A|B */ - matrix = 0x20; - break; - case V4L2_TUNER_MODE_LANG1: - source = 3; /* stereo or A */ - matrix = 0x00; - break; - case V4L2_TUNER_MODE_STEREO: - default: - source = 3; /* stereo or A */ - matrix = 0x20; - break; - } - - if (in == MSP_DSP_IN_TUNER) - source = (source << 8) | 0x20; - /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 - instead of 11, 12, 13. So we add one for that msp version. */ - else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) - source = ((in + 1) << 8) | matrix; - else - source = (in << 8) | matrix; - - v4l_dbg(1, msp_debug, client, - "set source to %d (0x%x) for output %02x\n", in, source, reg); - msp_write_dsp(client, reg, source); -} - -static void msp34xxg_set_sources(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - u32 in = state->route_in; - - msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); - /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ - msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); - msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); - msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); - if (state->has_scart2_out) - msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); - msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); -} - -/* (re-)initialize the msp34xxg */ -static void msp34xxg_reset(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int tuner = (state->route_in >> 3) & 1; - int modus; - - /* initialize std to 1 (autodetect) to signal that no standard is - selected yet. */ - state->std = 1; - - msp_reset(client); - - if (state->has_i2s_conf) - msp_write_dem(client, 0x40, state->i2s_mode); - - /* step-by-step initialisation, as described in the manual */ - modus = msp34xxg_modus(client); - modus |= tuner ? 0x100 : 0; - msp_write_dem(client, 0x30, modus); - - /* write the dsps that may have an influence on - standard/audio autodetection right now */ - msp34xxg_set_sources(client); - - msp_write_dsp(client, 0x0d, 0x1900); /* scart */ - msp_write_dsp(client, 0x0e, 0x3000); /* FM */ - if (state->has_nicam) - msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ - - /* set identification threshold. Personally, I - * I set it to a higher value than the default - * of 0x190 to ignore noisy stereo signals. - * this needs tuning. (recommended range 0x00a0-0x03c0) - * 0x7f0 = forced mono mode - * - * a2 threshold for stereo/bilingual. - * Note: this register is part of the Manual/Compatibility mode. - * It is supported by all 'G'-family chips. - */ - msp_write_dem(client, 0x22, msp_stereo_thresh); -} - -int msp34xxg_thread(void *data) -{ - struct i2c_client *client = data; - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int val, i; - - v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); - state->detected_std = V4L2_STD_ALL; - set_freezable(); - for (;;) { - v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); - msp_sleep(state, -1); - v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n"); - -restart: - v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); - state->restart = 0; - if (kthread_should_stop()) - break; - - if (state->mode == MSP_MODE_EXTERN) { - /* no carrier scan needed, just unmute */ - v4l_dbg(1, msp_debug, client, - "thread: no carrier scan\n"); - state->scan_in_progress = 0; - msp_update_volume(state); - continue; - } - - /* setup the chip*/ - msp34xxg_reset(client); - state->std = state->radio ? 0x40 : - (state->force_btsc && msp_standard == 1) ? 32 : msp_standard; - msp_write_dem(client, 0x20, state->std); - /* start autodetect */ - if (state->std != 1) - goto unmute; - - /* watch autodetect */ - v4l_dbg(1, msp_debug, client, - "started autodetect, waiting for result\n"); - for (i = 0; i < 10; i++) { - if (msp_sleep(state, 100)) - goto restart; - - /* check results */ - val = msp_read_dem(client, 0x7e); - if (val < 0x07ff) { - state->std = val; - break; - } - v4l_dbg(2, msp_debug, client, - "detection still in progress\n"); - } - if (state->std == 1) { - v4l_dbg(1, msp_debug, client, - "detection still in progress after 10 tries. giving up.\n"); - continue; - } - -unmute: - v4l_dbg(1, msp_debug, client, - "detected standard: %s (0x%04x)\n", - msp_standard_std_name(state->std), state->std); - state->detected_std = msp_standard_std(state->std); - - if (state->std == 9) { - /* AM NICAM mode */ - msp_write_dsp(client, 0x0e, 0x7c00); - } - - /* unmute: dispatch sound to scart output, set scart volume */ - msp_update_volume(state); - - /* restore ACB */ - if (msp_write_dsp(client, 0x13, state->acb)) - return -1; - - /* the periodic stereo/SAP check is only relevant for - the 0x20 standard (BTSC) */ - if (state->std != 0x20) - continue; - - state->watch_stereo = 1; - - /* monitor tv audio mode, the first time don't wait - in order to get a quick stereo/SAP update */ - watch_stereo(client); - while (state->watch_stereo) { - watch_stereo(client); - if (msp_sleep(state, 5000)) - goto restart; - } - } - v4l_dbg(1, msp_debug, client, "thread: exit\n"); - return 0; -} - -static int msp34xxg_detect_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int status = msp_read_dem(client, 0x0200); - int is_bilingual = status & 0x100; - int is_stereo = status & 0x40; - int oldrx = state->rxsubchans; - - if (state->mode == MSP_MODE_EXTERN) - return 0; - - state->rxsubchans = 0; - if (is_stereo) - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - else - state->rxsubchans = V4L2_TUNER_SUB_MONO; - if (is_bilingual) { - if (state->std == 0x20) - state->rxsubchans |= V4L2_TUNER_SUB_SAP; - else - state->rxsubchans = - V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } - v4l_dbg(1, msp_debug, client, - "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", - status, is_stereo, is_bilingual, state->rxsubchans); - return (oldrx != state->rxsubchans); -} - -static void msp34xxg_set_audmode(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - if (state->std == 0x20) { - if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && - (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 || - state->audmode == V4L2_TUNER_MODE_LANG2)) { - msp_write_dem(client, 0x20, 0x21); - } else { - msp_write_dem(client, 0x20, 0x20); - } - } - - msp34xxg_set_sources(client); -} - -void msp_set_audmode(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - switch (state->opmode) { - case OPMODE_MANUAL: - case OPMODE_AUTODETECT: - msp3400c_set_audmode(client); - break; - case OPMODE_AUTOSELECT: - msp34xxg_set_audmode(client); - break; - } -} - -int msp_detect_stereo(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - - switch (state->opmode) { - case OPMODE_MANUAL: - case OPMODE_AUTODETECT: - return msp3400c_detect_stereo(client); - case OPMODE_AUTOSELECT: - return msp34xxg_detect_stereo(client); - } - return 0; -} - diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c deleted file mode 100644 index 445359c96113..000000000000 --- a/drivers/media/video/mt9m032.c +++ /dev/null @@ -1,878 +0,0 @@ -/* - * Driver for MT9M032 CMOS Image Sensor from Micron - * - * Copyright (C) 2010-2011 Lund Engineering - * Contact: Gil Lund - * Author: Martin Hostettler - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "aptina-pll.h" - -/* - * width and height include active boundary and black parts - * - * column 0- 15 active boundary - * column 16-1455 image - * column 1456-1471 active boundary - * column 1472-1599 black - * - * row 0- 51 black - * row 53- 59 active boundary - * row 60-1139 image - * row 1140-1147 active boundary - * row 1148-1151 black - */ - -#define MT9M032_PIXEL_ARRAY_WIDTH 1600 -#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 - -#define MT9M032_CHIP_VERSION 0x00 -#define MT9M032_CHIP_VERSION_VALUE 0x1402 -#define MT9M032_ROW_START 0x01 -#define MT9M032_ROW_START_MIN 0 -#define MT9M032_ROW_START_MAX 1152 -#define MT9M032_ROW_START_DEF 60 -#define MT9M032_COLUMN_START 0x02 -#define MT9M032_COLUMN_START_MIN 0 -#define MT9M032_COLUMN_START_MAX 1600 -#define MT9M032_COLUMN_START_DEF 16 -#define MT9M032_ROW_SIZE 0x03 -#define MT9M032_ROW_SIZE_MIN 32 -#define MT9M032_ROW_SIZE_MAX 1152 -#define MT9M032_ROW_SIZE_DEF 1080 -#define MT9M032_COLUMN_SIZE 0x04 -#define MT9M032_COLUMN_SIZE_MIN 32 -#define MT9M032_COLUMN_SIZE_MAX 1600 -#define MT9M032_COLUMN_SIZE_DEF 1440 -#define MT9M032_HBLANK 0x05 -#define MT9M032_VBLANK 0x06 -#define MT9M032_VBLANK_MAX 0x7ff -#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 -#define MT9M032_SHUTTER_WIDTH_LOW 0x09 -#define MT9M032_SHUTTER_WIDTH_MIN 1 -#define MT9M032_SHUTTER_WIDTH_MAX 1048575 -#define MT9M032_SHUTTER_WIDTH_DEF 1943 -#define MT9M032_PIX_CLK_CTRL 0x0a -#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 -#define MT9M032_RESTART 0x0b -#define MT9M032_RESET 0x0d -#define MT9M032_PLL_CONFIG1 0x11 -#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f -#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 -#define MT9M032_READ_MODE1 0x1e -#define MT9M032_READ_MODE2 0x20 -#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 -#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 -#define MT9M032_READ_MODE2_ROW_BLC 0x40 -#define MT9M032_GAIN_GREEN1 0x2b -#define MT9M032_GAIN_BLUE 0x2c -#define MT9M032_GAIN_RED 0x2d -#define MT9M032_GAIN_GREEN2 0x2e - -/* write only */ -#define MT9M032_GAIN_ALL 0x35 -#define MT9M032_GAIN_DIGITAL_MASK 0x7f -#define MT9M032_GAIN_DIGITAL_SHIFT 8 -#define MT9M032_GAIN_AMUL_SHIFT 6 -#define MT9M032_GAIN_ANALOG_MASK 0x3f -#define MT9M032_FORMATTER1 0x9e -#define MT9M032_FORMATTER2 0x9f -#define MT9M032_FORMATTER2_DOUT_EN 0x1000 -#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 - -/* - * The available MT9M032 datasheet is missing documentation for register 0x10 - * MT9P031 seems to be close enough, so use constants from that datasheet for - * now. - * But keep the name MT9P031 to remind us, that this isn't really confirmed - * for this sensor. - */ -#define MT9P031_PLL_CONTROL 0x10 -#define MT9P031_PLL_CONTROL_PWROFF 0x0050 -#define MT9P031_PLL_CONTROL_PWRON 0x0051 -#define MT9P031_PLL_CONTROL_USEPLL 0x0052 -#define MT9P031_PLL_CONFIG2 0x11 -#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f - -struct mt9m032 { - struct v4l2_subdev subdev; - struct media_pad pad; - struct mt9m032_platform_data *pdata; - - unsigned int pix_clock; - - struct v4l2_ctrl_handler ctrls; - struct { - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; - - struct mutex lock; /* Protects streaming, format, interval and crop */ - - bool streaming; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - struct v4l2_fract frame_interval; -}; - -#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) -#define to_dev(sensor) \ - (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) - -static int mt9m032_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) -{ - unsigned int effective_width; - u32 ns; - - effective_width = width + 716; /* empirical value */ - ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); - dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); - return ns; -} - -static int mt9m032_update_timing(struct mt9m032 *sensor, - struct v4l2_fract *interval) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - struct v4l2_rect *crop = &sensor->crop; - unsigned int min_vblank; - unsigned int vblank; - u32 row_time; - - if (!interval) - interval = &sensor->frame_interval; - - row_time = mt9m032_row_time(sensor, crop->width); - - vblank = div_u64(1000000000ULL * interval->numerator, - (u64)row_time * interval->denominator) - - crop->height; - - if (vblank > MT9M032_VBLANK_MAX) { - /* hardware limits to 11 bit values */ - interval->denominator = 1000; - interval->numerator = - div_u64((crop->height + MT9M032_VBLANK_MAX) * - (u64)row_time * interval->denominator, - 1000000000ULL); - vblank = div_u64(1000000000ULL * interval->numerator, - (u64)row_time * interval->denominator) - - crop->height; - } - /* enforce minimal 1.6ms blanking time. */ - min_vblank = 1600000 / row_time; - vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); - - return mt9m032_write(client, MT9M032_VBLANK, vblank); -} - -static int mt9m032_update_geom_timing(struct mt9m032 *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int ret; - - ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, - sensor->crop.width - 1); - if (!ret) - ret = mt9m032_write(client, MT9M032_ROW_SIZE, - sensor->crop.height - 1); - if (!ret) - ret = mt9m032_write(client, MT9M032_COLUMN_START, - sensor->crop.left); - if (!ret) - ret = mt9m032_write(client, MT9M032_ROW_START, - sensor->crop.top); - if (!ret) - ret = mt9m032_update_timing(sensor, NULL); - return ret; -} - -static int update_formatter2(struct mt9m032 *sensor, bool streaming) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - u16 reg_val = MT9M032_FORMATTER2_DOUT_EN - | 0x0070; /* parts reserved! */ - /* possibly for changing to 14-bit mode */ - - if (streaming) - reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ - - return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); -} - -static int mt9m032_setup_pll(struct mt9m032 *sensor) -{ - static const struct aptina_pll_limits limits = { - .ext_clock_min = 8000000, - .ext_clock_max = 16500000, - .int_clock_min = 2000000, - .int_clock_max = 24000000, - .out_clock_min = 322000000, - .out_clock_max = 693000000, - .pix_clock_max = 99000000, - .n_min = 1, - .n_max = 64, - .m_min = 16, - .m_max = 255, - .p1_min = 1, - .p1_max = 128, - }; - - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - struct mt9m032_platform_data *pdata = sensor->pdata; - struct aptina_pll pll; - int ret; - - pll.ext_clock = pdata->ext_clock; - pll.pix_clock = pdata->pix_clock; - - ret = aptina_pll_calculate(&client->dev, &limits, &pll); - if (ret < 0) - return ret; - - sensor->pix_clock = pdata->pix_clock; - - ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, - (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) - | (pll.p1 - 1)); - if (!ret) - ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); - if (!ret) - ret = mt9m032_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWRON | - MT9P031_PLL_CONTROL_USEPLL); - if (!ret) /* more reserved, Continuous, Master Mode */ - ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); - if (!ret) /* Set 14-bit mode, select 7 divider */ - ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * Subdev pad operations - */ - -static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index != 0) - return -EINVAL; - - code->code = V4L2_MBUS_FMT_Y8_1X8; - return 0; -} - -static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8) - return -EINVAL; - - fse->min_width = MT9M032_COLUMN_SIZE_DEF; - fse->max_width = MT9M032_COLUMN_SIZE_DEF; - fse->min_height = MT9M032_ROW_SIZE_DEF; - fse->max_height = MT9M032_ROW_SIZE_DEF; - - return 0; -} - -/** - * __mt9m032_get_pad_crop() - get crop rect - * @sensor: pointer to the sensor struct - * @fh: file handle for getting the try crop rect from - * @which: select try or active crop rect - * - * Returns a pointer the current active or fh relative try crop rect - */ -static struct v4l2_rect * -__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, 0); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &sensor->crop; - default: - return NULL; - } -} - -/** - * __mt9m032_get_pad_format() - get format - * @sensor: pointer to the sensor struct - * @fh: file handle for getting the try format from - * @which: select try or active format - * - * Returns a pointer the current active or fh relative try format - */ -static struct v4l2_mbus_framefmt * -__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, 0); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &sensor->format; - default: - return NULL; - } -} - -static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - - if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = -EBUSY; - goto done; - } - - /* Scaling is not supported, the format is thus fixed. */ - fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); - ret = 0; - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - int ret = 0; - - mutex_lock(&sensor->lock); - - if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = -EBUSY; - goto done; - } - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels to ensure a GRBG Bayer pattern. - */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, - MT9M032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, - MT9M032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, - MT9M032_COLUMN_SIZE_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, - MT9M032_ROW_SIZE_MAX); - - rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - format = __mt9m032_get_pad_format(sensor, fh, crop->which); - format->width = rect.width; - format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ret = mt9m032_update_geom_timing(sensor); - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - memset(fi, 0, sizeof(*fi)); - fi->interval = sensor->frame_interval; - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - - if (sensor->streaming) { - ret = -EBUSY; - goto done; - } - - /* Avoid divisions by 0. */ - if (fi->interval.denominator == 0) - fi->interval.denominator = 1; - - ret = mt9m032_update_timing(sensor, &fi->interval); - if (!ret) - sensor->frame_interval = fi->interval; - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - ret = update_formatter2(sensor, streaming); - if (!ret) - sensor->streaming = streaming; - mutex_unlock(&sensor->lock); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m032_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct mt9m032 *sensor = to_mt9m032(sd); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int val; - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) - return -EINVAL; - if (reg->match.addr != client->addr) - return -ENODEV; - - val = mt9m032_read(client, reg->reg); - if (val < 0) - return -EIO; - - reg->size = 2; - reg->val = val; - - return 0; -} - -static int mt9m032_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct mt9m032 *sensor = to_mt9m032(sd); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - - if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) - return -EINVAL; - - if (reg->match.addr != client->addr) - return -ENODEV; - - return mt9m032_write(client, reg->reg, reg->val); -} -#endif - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) - | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) - | MT9M032_READ_MODE2_ROW_BLC - | 0x0007; - - return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); -} - -static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int digital_gain_val; /* in 1/8th (0..127) */ - int analog_mul; /* 0 or 1 */ - int analog_gain_val; /* in 1/16th. (0..63) */ - u16 reg_val; - - digital_gain_val = 51; /* from setup example */ - - if (val < 63) { - analog_mul = 0; - analog_gain_val = val; - } else { - analog_mul = 1; - analog_gain_val = val / 2; - } - - /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ - /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ - - reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) - << MT9M032_GAIN_DIGITAL_SHIFT) - | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) - | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); - - return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); -} - -static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) -{ - if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { - /* round because of multiplier used for values >= 63 */ - ctrl->val &= ~1; - } - - return 0; -} - -static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m032 *sensor = - container_of(ctrl->handler, struct mt9m032, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int ret; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - return mt9m032_set_gain(sensor, ctrl->val); - - case V4L2_CID_HFLIP: - /* case V4L2_CID_VFLIP: -- In the same cluster */ - return update_read_mode2(sensor, sensor->vflip->val, - sensor->hflip->val); - - case V4L2_CID_EXPOSURE: - ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, - (ctrl->val >> 16) & 0xffff); - if (ret < 0) - return ret; - - return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, - ctrl->val & 0xffff); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9m032_ctrl_ops = { - .s_ctrl = mt9m032_set_ctrl, - .try_ctrl = mt9m032_try_ctrl, -}; - -/* -------------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops mt9m032_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9m032_g_register, - .s_register = mt9m032_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops mt9m032_video_ops = { - .s_stream = mt9m032_s_stream, - .g_frame_interval = mt9m032_get_frame_interval, - .s_frame_interval = mt9m032_set_frame_interval, -}; - -static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { - .enum_mbus_code = mt9m032_enum_mbus_code, - .enum_frame_size = mt9m032_enum_frame_size, - .get_fmt = mt9m032_get_pad_format, - .set_fmt = mt9m032_set_pad_format, - .set_crop = mt9m032_set_pad_crop, - .get_crop = mt9m032_get_pad_crop, -}; - -static const struct v4l2_subdev_ops mt9m032_ops = { - .core = &mt9m032_core_ops, - .video = &mt9m032_video_ops, - .pad = &mt9m032_pad_ops, -}; - -/* ----------------------------------------------------------------------------- - * Driver initialization and probing - */ - -static int mt9m032_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct mt9m032_platform_data *pdata = client->dev.platform_data; - struct i2c_adapter *adapter = client->adapter; - struct mt9m032 *sensor; - int chip_version; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - if (!client->dev.platform_data) - return -ENODEV; - - sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) - return -ENOMEM; - - mutex_init(&sensor->lock); - - sensor->pdata = pdata; - - v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); - sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); - if (chip_version != MT9M032_CHIP_VERSION_VALUE) { - dev_err(&client->dev, "MT9M032 not detected, wrong version " - "0x%04x\n", chip_version); - ret = -ENODEV; - goto error_sensor; - } - - dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", - client->addr); - - sensor->frame_interval.numerator = 1; - sensor->frame_interval.denominator = 30; - - sensor->crop.left = MT9M032_COLUMN_START_DEF; - sensor->crop.top = MT9M032_ROW_START_DEF; - sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; - sensor->crop.height = MT9M032_ROW_SIZE_DEF; - - sensor->format.width = sensor->crop.width; - sensor->format.height = sensor->crop.height; - sensor->format.code = V4L2_MBUS_FMT_Y8_1X8; - sensor->format.field = V4L2_FIELD_NONE; - sensor->format.colorspace = V4L2_COLORSPACE_SRGB; - - v4l2_ctrl_handler_init(&sensor->ctrls, 5); - - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, - &mt9m032_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, - &mt9m032_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, - MT9M032_SHUTTER_WIDTH_MAX, 1, - MT9M032_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->pix_clock, - pdata->pix_clock, 1, pdata->pix_clock); - - if (sensor->ctrls.error) { - ret = sensor->ctrls.error; - dev_err(&client->dev, "control initialization error %d\n", ret); - goto error_ctrl; - } - - v4l2_ctrl_cluster(2, &sensor->hflip); - - sensor->subdev.ctrl_handler = &sensor->ctrls; - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); - if (ret < 0) - goto error_ctrl; - - ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ - if (ret < 0) - goto error_entity; - mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ - if (ret < 0) - goto error_entity; - - ret = mt9m032_setup_pll(sensor); - if (ret < 0) - goto error_entity; - usleep_range(10000, 11000); - - ret = v4l2_ctrl_handler_setup(&sensor->ctrls); - if (ret < 0) - goto error_entity; - - /* SIZE */ - ret = mt9m032_update_geom_timing(sensor); - if (ret < 0) - goto error_entity; - - ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ - if (ret < 0) - goto error_entity; - if (sensor->pdata->invert_pixclock) { - ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, - MT9M032_PIX_CLK_CTRL_INV_PIXCLK); - if (ret < 0) - goto error_entity; - } - - ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ - if (ret < 0) - goto error_entity; - msleep(100); - ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ - if (ret < 0) - goto error_entity; - msleep(100); - ret = update_formatter2(sensor, false); - if (ret < 0) - goto error_entity; - - return ret; - -error_entity: - media_entity_cleanup(&sensor->subdev.entity); -error_ctrl: - v4l2_ctrl_handler_free(&sensor->ctrls); -error_sensor: - mutex_destroy(&sensor->lock); - kfree(sensor); - return ret; -} - -static int mt9m032_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9m032 *sensor = to_mt9m032(subdev); - - v4l2_device_unregister_subdev(subdev); - v4l2_ctrl_handler_free(&sensor->ctrls); - media_entity_cleanup(&subdev->entity); - mutex_destroy(&sensor->lock); - kfree(sensor); - return 0; -} - -static const struct i2c_device_id mt9m032_id_table[] = { - { MT9M032_NAME, 0 }, - { } -}; - -MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); - -static struct i2c_driver mt9m032_i2c_driver = { - .driver = { - .name = MT9M032_NAME, - }, - .probe = mt9m032_probe, - .remove = mt9m032_remove, - .id_table = mt9m032_id_table, -}; - -module_i2c_driver(mt9m032_i2c_driver); - -MODULE_AUTHOR("Martin Hostettler "); -MODULE_DESCRIPTION("MT9M032 camera sensor driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c deleted file mode 100644 index 3be537ef22d2..000000000000 --- a/drivers/media/video/mt9p031.c +++ /dev/null @@ -1,1071 +0,0 @@ -/* - * Driver for MT9P031 CMOS Image Sensor from Aptina - * - * Copyright (C) 2011, Laurent Pinchart - * Copyright (C) 2011, Javier Martin - * Copyright (C) 2011, Guennadi Liakhovetski - * - * Based on the MT9V032 driver and Bastian Hecht's code. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "aptina-pll.h" - -#define MT9P031_PIXEL_ARRAY_WIDTH 2752 -#define MT9P031_PIXEL_ARRAY_HEIGHT 2004 - -#define MT9P031_CHIP_VERSION 0x00 -#define MT9P031_CHIP_VERSION_VALUE 0x1801 -#define MT9P031_ROW_START 0x01 -#define MT9P031_ROW_START_MIN 0 -#define MT9P031_ROW_START_MAX 2004 -#define MT9P031_ROW_START_DEF 54 -#define MT9P031_COLUMN_START 0x02 -#define MT9P031_COLUMN_START_MIN 0 -#define MT9P031_COLUMN_START_MAX 2750 -#define MT9P031_COLUMN_START_DEF 16 -#define MT9P031_WINDOW_HEIGHT 0x03 -#define MT9P031_WINDOW_HEIGHT_MIN 2 -#define MT9P031_WINDOW_HEIGHT_MAX 2006 -#define MT9P031_WINDOW_HEIGHT_DEF 1944 -#define MT9P031_WINDOW_WIDTH 0x04 -#define MT9P031_WINDOW_WIDTH_MIN 2 -#define MT9P031_WINDOW_WIDTH_MAX 2752 -#define MT9P031_WINDOW_WIDTH_DEF 2592 -#define MT9P031_HORIZONTAL_BLANK 0x05 -#define MT9P031_HORIZONTAL_BLANK_MIN 0 -#define MT9P031_HORIZONTAL_BLANK_MAX 4095 -#define MT9P031_VERTICAL_BLANK 0x06 -#define MT9P031_VERTICAL_BLANK_MIN 0 -#define MT9P031_VERTICAL_BLANK_MAX 4095 -#define MT9P031_VERTICAL_BLANK_DEF 25 -#define MT9P031_OUTPUT_CONTROL 0x07 -#define MT9P031_OUTPUT_CONTROL_CEN 2 -#define MT9P031_OUTPUT_CONTROL_SYN 1 -#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 -#define MT9P031_SHUTTER_WIDTH_UPPER 0x08 -#define MT9P031_SHUTTER_WIDTH_LOWER 0x09 -#define MT9P031_SHUTTER_WIDTH_MIN 1 -#define MT9P031_SHUTTER_WIDTH_MAX 1048575 -#define MT9P031_SHUTTER_WIDTH_DEF 1943 -#define MT9P031_PLL_CONTROL 0x10 -#define MT9P031_PLL_CONTROL_PWROFF 0x0050 -#define MT9P031_PLL_CONTROL_PWRON 0x0051 -#define MT9P031_PLL_CONTROL_USEPLL 0x0052 -#define MT9P031_PLL_CONFIG_1 0x11 -#define MT9P031_PLL_CONFIG_2 0x12 -#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a -#define MT9P031_FRAME_RESTART 0x0b -#define MT9P031_SHUTTER_DELAY 0x0c -#define MT9P031_RST 0x0d -#define MT9P031_RST_ENABLE 1 -#define MT9P031_RST_DISABLE 0 -#define MT9P031_READ_MODE_1 0x1e -#define MT9P031_READ_MODE_2 0x20 -#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) -#define MT9P031_READ_MODE_2_COL_MIR (1 << 14) -#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) -#define MT9P031_ROW_ADDRESS_MODE 0x22 -#define MT9P031_COLUMN_ADDRESS_MODE 0x23 -#define MT9P031_GLOBAL_GAIN 0x35 -#define MT9P031_GLOBAL_GAIN_MIN 8 -#define MT9P031_GLOBAL_GAIN_MAX 1024 -#define MT9P031_GLOBAL_GAIN_DEF 8 -#define MT9P031_GLOBAL_GAIN_MULT (1 << 6) -#define MT9P031_ROW_BLACK_TARGET 0x49 -#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b -#define MT9P031_GREEN1_OFFSET 0x60 -#define MT9P031_GREEN2_OFFSET 0x61 -#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 -#define MT9P031_BLC_MANUAL_BLC (1 << 0) -#define MT9P031_RED_OFFSET 0x63 -#define MT9P031_BLUE_OFFSET 0x64 -#define MT9P031_TEST_PATTERN 0xa0 -#define MT9P031_TEST_PATTERN_SHIFT 3 -#define MT9P031_TEST_PATTERN_ENABLE (1 << 0) -#define MT9P031_TEST_PATTERN_DISABLE (0 << 0) -#define MT9P031_TEST_PATTERN_GREEN 0xa1 -#define MT9P031_TEST_PATTERN_RED 0xa2 -#define MT9P031_TEST_PATTERN_BLUE 0xa3 - -enum mt9p031_model { - MT9P031_MODEL_COLOR, - MT9P031_MODEL_MONOCHROME, -}; - -struct mt9p031 { - struct v4l2_subdev subdev; - struct media_pad pad; - struct v4l2_rect crop; /* Sensor window */ - struct v4l2_mbus_framefmt format; - struct mt9p031_platform_data *pdata; - struct mutex power_lock; /* lock to protect power_count */ - int power_count; - - enum mt9p031_model model; - struct aptina_pll pll; - int reset; - - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *blc_auto; - struct v4l2_ctrl *blc_offset; - - /* Registers cache */ - u16 output_control; - u16 mode2; -}; - -static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9p031, subdev); -} - -static int mt9p031_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, - u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - u16 value = (mt9p031->output_control & ~clear) | set; - int ret; - - ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); - if (ret < 0) - return ret; - - mt9p031->output_control = value; - return 0; -} - -static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - u16 value = (mt9p031->mode2 & ~clear) | set; - int ret; - - ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); - if (ret < 0) - return ret; - - mt9p031->mode2 = value; - return 0; -} - -static int mt9p031_reset(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int ret; - - /* Disable chip output, synchronous option update */ - ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); - if (ret < 0) - return ret; - - return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, - 0); -} - -static int mt9p031_pll_setup(struct mt9p031 *mt9p031) -{ - static const struct aptina_pll_limits limits = { - .ext_clock_min = 6000000, - .ext_clock_max = 27000000, - .int_clock_min = 2000000, - .int_clock_max = 13500000, - .out_clock_min = 180000000, - .out_clock_max = 360000000, - .pix_clock_max = 96000000, - .n_min = 1, - .n_max = 64, - .m_min = 16, - .m_max = 255, - .p1_min = 1, - .p1_max = 128, - }; - - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - struct mt9p031_platform_data *pdata = mt9p031->pdata; - - mt9p031->pll.ext_clock = pdata->ext_freq; - mt9p031->pll.pix_clock = pdata->target_freq; - - return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); -} - -static int mt9p031_pll_enable(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int ret; - - ret = mt9p031_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWRON); - if (ret < 0) - return ret; - - ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, - (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); - if (ret < 0) - return ret; - - ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); - if (ret < 0) - return ret; - - usleep_range(1000, 2000); - ret = mt9p031_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWRON | - MT9P031_PLL_CONTROL_USEPLL); - return ret; -} - -static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - - return mt9p031_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWROFF); -} - -static int mt9p031_power_on(struct mt9p031 *mt9p031) -{ - /* Ensure RESET_BAR is low */ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 0); - usleep_range(1000, 2000); - } - - /* Emable clock */ - if (mt9p031->pdata->set_xclk) - mt9p031->pdata->set_xclk(&mt9p031->subdev, - mt9p031->pdata->ext_freq); - - /* Now RESET_BAR must be high */ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 1); - usleep_range(1000, 2000); - } - - return 0; -} - -static void mt9p031_power_off(struct mt9p031 *mt9p031) -{ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 0); - usleep_range(1000, 2000); - } - - if (mt9p031->pdata->set_xclk) - mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); -} - -static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int ret; - - if (!on) { - mt9p031_power_off(mt9p031); - return 0; - } - - ret = mt9p031_power_on(mt9p031); - if (ret < 0) - return ret; - - ret = mt9p031_reset(mt9p031); - if (ret < 0) { - dev_err(&client->dev, "Failed to reset the camera\n"); - return ret; - } - - return v4l2_ctrl_handler_setup(&mt9p031->ctrls); -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static int mt9p031_set_params(struct mt9p031 *mt9p031) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - struct v4l2_mbus_framefmt *format = &mt9p031->format; - const struct v4l2_rect *crop = &mt9p031->crop; - unsigned int hblank; - unsigned int vblank; - unsigned int xskip; - unsigned int yskip; - unsigned int xbin; - unsigned int ybin; - int ret; - - /* Windows position and size. - * - * TODO: Make sure the start coordinates and window size match the - * skipping, binning and mirroring (see description of registers 2 and 4 - * in table 13, and Binning section on page 41). - */ - ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); - if (ret < 0) - return ret; - - /* Row and column binning and skipping. Use the maximum binning value - * compatible with the skipping settings. - */ - xskip = DIV_ROUND_CLOSEST(crop->width, format->width); - yskip = DIV_ROUND_CLOSEST(crop->height, format->height); - xbin = 1 << (ffs(xskip) - 1); - ybin = 1 << (ffs(yskip) - 1); - - ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (xskip - 1)); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (yskip - 1)); - if (ret < 0) - return ret; - - /* Blanking - use minimum value for horizontal blanking and default - * value for vertical blanking. - */ - hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3)); - vblank = MT9P031_VERTICAL_BLANK_DEF; - - ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank); - if (ret < 0) - return ret; - - return ret; -} - -static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - int ret; - - if (!enable) { - /* Stop sensor readout */ - ret = mt9p031_set_output_control(mt9p031, - MT9P031_OUTPUT_CONTROL_CEN, 0); - if (ret < 0) - return ret; - - return mt9p031_pll_disable(mt9p031); - } - - ret = mt9p031_set_params(mt9p031); - if (ret < 0) - return ret; - - /* Switch to master "normal" mode */ - ret = mt9p031_set_output_control(mt9p031, 0, - MT9P031_OUTPUT_CONTROL_CEN); - if (ret < 0) - return ret; - - return mt9p031_pll_enable(mt9p031); -} - -static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - if (code->pad || code->index) - return -EINVAL; - - code->code = mt9p031->format.code; - return 0; -} - -static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - if (fse->index >= 8 || fse->code != mt9p031->format.code) - return -EINVAL; - - fse->min_width = MT9P031_WINDOW_WIDTH_DEF - / min_t(unsigned int, 7, fse->index + 1); - fse->max_width = fse->min_width; - fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); - fse->max_height = fse->min_height; - - return 0; -} - -static struct v4l2_mbus_framefmt * -__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, - unsigned int pad, u32 which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9p031->format; - default: - return NULL; - } -} - -static struct v4l2_rect * -__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, - unsigned int pad, u32 which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9p031->crop; - default: - return NULL; - } -} - -static int mt9p031_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, - fmt->which); - return 0; -} - -static int mt9p031_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - unsigned int width; - unsigned int height; - unsigned int hratio; - unsigned int vratio; - - __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, - format->which); - - /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); - - __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, - format->which); - __format->width = __crop->width / hratio; - __format->height = __crop->height / vratio; - - format->format = *__format; - - return 0; -} - -static int mt9p031_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, - crop->which); - return 0; -} - -static int mt9p031_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels to ensure a GRBG Bayer pattern. - */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, - MT9P031_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, - MT9P031_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9P031_WINDOW_WIDTH_MIN, - MT9P031_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9P031_WINDOW_HEIGHT_MIN, - MT9P031_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, - crop->which); - __format->width = rect.width; - __format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) -#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) -#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) -#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) -#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) - -static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9p031 *mt9p031 = - container_of(ctrl->handler, struct mt9p031, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - u16 data; - int ret; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, - (ctrl->val >> 16) & 0xffff); - if (ret < 0) - return ret; - - return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, - ctrl->val & 0xffff); - - case V4L2_CID_GAIN: - /* Gain is controlled by 2 analog stages and a digital stage. - * Valid values for the 3 stages are - * - * Stage Min Max Step - * ------------------------------------------ - * First analog stage x1 x2 1 - * Second analog stage x1 x4 0.125 - * Digital stage x1 x16 0.125 - * - * To minimize noise, the gain stages should be used in the - * second analog stage, first analog stage, digital stage order. - * Gain from a previous stage should be pushed to its maximum - * value before the next stage is used. - */ - if (ctrl->val <= 32) { - data = ctrl->val; - } else if (ctrl->val <= 64) { - ctrl->val &= ~1; - data = (1 << 6) | (ctrl->val >> 1); - } else { - ctrl->val &= ~7; - data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; - } - - return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); - - case V4L2_CID_HFLIP: - if (ctrl->val) - return mt9p031_set_mode2(mt9p031, - 0, MT9P031_READ_MODE_2_COL_MIR); - else - return mt9p031_set_mode2(mt9p031, - MT9P031_READ_MODE_2_COL_MIR, 0); - - case V4L2_CID_VFLIP: - if (ctrl->val) - return mt9p031_set_mode2(mt9p031, - 0, MT9P031_READ_MODE_2_ROW_MIR); - else - return mt9p031_set_mode2(mt9p031, - MT9P031_READ_MODE_2_ROW_MIR, 0); - - case V4L2_CID_TEST_PATTERN: - if (!ctrl->val) { - /* Restore the black level compensation settings. */ - if (mt9p031->blc_auto->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_auto); - if (ret < 0) - return ret; - } - if (mt9p031->blc_offset->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_offset); - if (ret < 0) - return ret; - } - return mt9p031_write(client, MT9P031_TEST_PATTERN, - MT9P031_TEST_PATTERN_DISABLE); - } - - ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); - if (ret < 0) - return ret; - - /* Disable digital black level compensation when using a test - * pattern. - */ - ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, - 0); - if (ret < 0) - return ret; - - ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); - if (ret < 0) - return ret; - - return mt9p031_write(client, MT9P031_TEST_PATTERN, - ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) - | MT9P031_TEST_PATTERN_ENABLE); - - case V4L2_CID_BLC_AUTO: - ret = mt9p031_set_mode2(mt9p031, - ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, - ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); - if (ret < 0) - return ret; - - return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, - ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); - - case V4L2_CID_BLC_TARGET_LEVEL: - return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, - ctrl->val); - - case V4L2_CID_BLC_ANALOG_OFFSET: - data = ctrl->val & ((1 << 9) - 1); - - ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); - if (ret < 0) - return ret; - ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); - if (ret < 0) - return ret; - return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); - - case V4L2_CID_BLC_DIGITAL_OFFSET: - return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, - ctrl->val & ((1 << 12) - 1)); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9p031_ctrl_ops = { - .s_ctrl = mt9p031_s_ctrl, -}; - -static const char * const mt9p031_test_pattern_menu[] = { - "Disabled", - "Color Field", - "Horizontal Gradient", - "Vertical Gradient", - "Diagonal Gradient", - "Classic Test Pattern", - "Walking 1s", - "Monochrome Horizontal Bars", - "Monochrome Vertical Bars", - "Vertical Color Bars", -}; - -static const struct v4l2_ctrl_config mt9p031_ctrls[] = { - { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_TEST_PATTERN, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Test Pattern", - .min = 0, - .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, - .step = 0, - .def = 0, - .flags = 0, - .menu_skip_mask = 0, - .qmenu = mt9p031_test_pattern_menu, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "BLC, Auto", - .min = 0, - .max = 1, - .step = 1, - .def = 1, - .flags = 0, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_TARGET_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "BLC Target Level", - .min = 0, - .max = 4095, - .step = 1, - .def = 168, - .flags = 0, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_ANALOG_OFFSET, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "BLC Analog Offset", - .min = -255, - .max = 255, - .step = 1, - .def = 32, - .flags = 0, - }, { - .ops = &mt9p031_ctrl_ops, - .id = V4L2_CID_BLC_DIGITAL_OFFSET, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "BLC Digital Offset", - .min = -2048, - .max = 2047, - .step = 1, - .def = 40, - .flags = 0, - } -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - int ret = 0; - - mutex_lock(&mt9p031->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (mt9p031->power_count == !on) { - ret = __mt9p031_set_power(mt9p031, !!on); - if (ret < 0) - goto out; - } - - /* Update the power count. */ - mt9p031->power_count += on ? 1 : -1; - WARN_ON(mt9p031->power_count < 0); - -out: - mutex_unlock(&mt9p031->power_lock); - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev internal operations - */ - -static int mt9p031_registered(struct v4l2_subdev *subdev) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - s32 data; - int ret; - - ret = mt9p031_power_on(mt9p031); - if (ret < 0) { - dev_err(&client->dev, "MT9P031 power up failed\n"); - return ret; - } - - /* Read out the chip version register */ - data = mt9p031_read(client, MT9P031_CHIP_VERSION); - if (data != MT9P031_CHIP_VERSION_VALUE) { - dev_err(&client->dev, "MT9P031 not detected, wrong version " - "0x%04x\n", data); - return -ENODEV; - } - - mt9p031_power_off(mt9p031); - - dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", - client->addr); - - return ret; -} - -static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(fh, 0); - crop->left = MT9P031_COLUMN_START_DEF; - crop->top = MT9P031_ROW_START_DEF; - crop->width = MT9P031_WINDOW_WIDTH_DEF; - crop->height = MT9P031_WINDOW_HEIGHT_DEF; - - format = v4l2_subdev_get_try_format(fh, 0); - - if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - format->code = V4L2_MBUS_FMT_Y12_1X12; - else - format->code = V4L2_MBUS_FMT_SGRBG12_1X12; - - format->width = MT9P031_WINDOW_WIDTH_DEF; - format->height = MT9P031_WINDOW_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - return mt9p031_set_power(subdev, 1); -} - -static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - return mt9p031_set_power(subdev, 0); -} - -static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { - .s_power = mt9p031_set_power, -}; - -static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { - .s_stream = mt9p031_s_stream, -}; - -static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { - .enum_mbus_code = mt9p031_enum_mbus_code, - .enum_frame_size = mt9p031_enum_frame_size, - .get_fmt = mt9p031_get_format, - .set_fmt = mt9p031_set_format, - .get_crop = mt9p031_get_crop, - .set_crop = mt9p031_set_crop, -}; - -static struct v4l2_subdev_ops mt9p031_subdev_ops = { - .core = &mt9p031_subdev_core_ops, - .video = &mt9p031_subdev_video_ops, - .pad = &mt9p031_subdev_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { - .registered = mt9p031_registered, - .open = mt9p031_open, - .close = mt9p031_close, -}; - -/* ----------------------------------------------------------------------------- - * Driver initialization and probing - */ - -static int mt9p031_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9p031_platform_data *pdata = client->dev.platform_data; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct mt9p031 *mt9p031; - unsigned int i; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); - if (mt9p031 == NULL) - return -ENOMEM; - - mt9p031->pdata = pdata; - mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; - mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; - mt9p031->model = did->driver_data; - mt9p031->reset = -1; - - v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5); - - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, - MT9P031_SHUTTER_WIDTH_MAX, 1, - MT9P031_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, - MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->target_freq, - pdata->target_freq, 1, pdata->target_freq); - - for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) - v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); - - mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; - - if (mt9p031->ctrls.error) { - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9p031->ctrls.error); - ret = mt9p031->ctrls.error; - goto done; - } - - mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); - mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, - V4L2_CID_BLC_DIGITAL_OFFSET); - - mutex_init(&mt9p031->power_lock); - v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); - mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; - - mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); - if (ret < 0) - goto done; - - mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; - mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; - mt9p031->crop.left = MT9P031_COLUMN_START_DEF; - mt9p031->crop.top = MT9P031_ROW_START_DEF; - - if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; - else - mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; - - mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; - mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; - mt9p031->format.field = V4L2_FIELD_NONE; - mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; - - if (pdata->reset != -1) { - ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, - "mt9p031_rst"); - if (ret < 0) - goto done; - - mt9p031->reset = pdata->reset; - } - - ret = mt9p031_pll_setup(mt9p031); - -done: - if (ret < 0) { - if (mt9p031->reset != -1) - gpio_free(mt9p031->reset); - - v4l2_ctrl_handler_free(&mt9p031->ctrls); - media_entity_cleanup(&mt9p031->subdev.entity); - kfree(mt9p031); - } - - return ret; -} - -static int mt9p031_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - - v4l2_ctrl_handler_free(&mt9p031->ctrls); - v4l2_device_unregister_subdev(subdev); - media_entity_cleanup(&subdev->entity); - if (mt9p031->reset != -1) - gpio_free(mt9p031->reset); - kfree(mt9p031); - - return 0; -} - -static const struct i2c_device_id mt9p031_id[] = { - { "mt9p031", MT9P031_MODEL_COLOR }, - { "mt9p031m", MT9P031_MODEL_MONOCHROME }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9p031_id); - -static struct i2c_driver mt9p031_i2c_driver = { - .driver = { - .name = "mt9p031", - }, - .probe = mt9p031_probe, - .remove = mt9p031_remove, - .id_table = mt9p031_id, -}; - -module_i2c_driver(mt9p031_i2c_driver); - -MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); -MODULE_AUTHOR("Bastian Hecht "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c deleted file mode 100644 index 6d343adf891d..000000000000 --- a/drivers/media/video/mt9t001.c +++ /dev/null @@ -1,833 +0,0 @@ -/* - * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) - * - * Copyright (C) 2010-2011, Laurent Pinchart - * - * Based on the MT9M001 driver, - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * 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. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MT9T001_PIXEL_ARRAY_HEIGHT 1568 -#define MT9T001_PIXEL_ARRAY_WIDTH 2112 - -#define MT9T001_CHIP_VERSION 0x00 -#define MT9T001_CHIP_ID 0x1621 -#define MT9T001_ROW_START 0x01 -#define MT9T001_ROW_START_MIN 0 -#define MT9T001_ROW_START_DEF 20 -#define MT9T001_ROW_START_MAX 1534 -#define MT9T001_COLUMN_START 0x02 -#define MT9T001_COLUMN_START_MIN 0 -#define MT9T001_COLUMN_START_DEF 32 -#define MT9T001_COLUMN_START_MAX 2046 -#define MT9T001_WINDOW_HEIGHT 0x03 -#define MT9T001_WINDOW_HEIGHT_MIN 1 -#define MT9T001_WINDOW_HEIGHT_DEF 1535 -#define MT9T001_WINDOW_HEIGHT_MAX 1567 -#define MT9T001_WINDOW_WIDTH 0x04 -#define MT9T001_WINDOW_WIDTH_MIN 1 -#define MT9T001_WINDOW_WIDTH_DEF 2047 -#define MT9T001_WINDOW_WIDTH_MAX 2111 -#define MT9T001_HORIZONTAL_BLANKING 0x05 -#define MT9T001_HORIZONTAL_BLANKING_MIN 21 -#define MT9T001_HORIZONTAL_BLANKING_MAX 1023 -#define MT9T001_VERTICAL_BLANKING 0x06 -#define MT9T001_VERTICAL_BLANKING_MIN 3 -#define MT9T001_VERTICAL_BLANKING_MAX 1023 -#define MT9T001_OUTPUT_CONTROL 0x07 -#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) -#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) -#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) -#define MT9T001_SHUTTER_WIDTH_HIGH 0x08 -#define MT9T001_SHUTTER_WIDTH_LOW 0x09 -#define MT9T001_SHUTTER_WIDTH_MIN 1 -#define MT9T001_SHUTTER_WIDTH_DEF 1561 -#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) -#define MT9T001_PIXEL_CLOCK 0x0a -#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) -#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) -#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 -#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) -#define MT9T001_FRAME_RESTART 0x0b -#define MT9T001_SHUTTER_DELAY 0x0c -#define MT9T001_SHUTTER_DELAY_MAX 2047 -#define MT9T001_RESET 0x0d -#define MT9T001_READ_MODE1 0x1e -#define MT9T001_READ_MODE_SNAPSHOT (1 << 8) -#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) -#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) -#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) -#define MT9T001_READ_MODE2 0x20 -#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) -#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) -#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) -#define MT9T001_READ_MODE3 0x21 -#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) -#define MT9T001_READ_MODE_GHST_CTL (1 << 1) -#define MT9T001_ROW_ADDRESS_MODE 0x22 -#define MT9T001_ROW_SKIP_MASK (7 << 0) -#define MT9T001_ROW_BIN_MASK (3 << 3) -#define MT9T001_ROW_BIN_SHIFT 3 -#define MT9T001_COLUMN_ADDRESS_MODE 0x23 -#define MT9T001_COLUMN_SKIP_MASK (7 << 0) -#define MT9T001_COLUMN_BIN_MASK (3 << 3) -#define MT9T001_COLUMN_BIN_SHIFT 3 -#define MT9T001_GREEN1_GAIN 0x2b -#define MT9T001_BLUE_GAIN 0x2c -#define MT9T001_RED_GAIN 0x2d -#define MT9T001_GREEN2_GAIN 0x2e -#define MT9T001_TEST_DATA 0x32 -#define MT9T001_GLOBAL_GAIN 0x35 -#define MT9T001_GLOBAL_GAIN_MIN 8 -#define MT9T001_GLOBAL_GAIN_MAX 1024 -#define MT9T001_BLACK_LEVEL 0x49 -#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b -#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d -#define MT9T001_CAL_THRESHOLDS 0x5f -#define MT9T001_GREEN1_OFFSET 0x60 -#define MT9T001_GREEN2_OFFSET 0x61 -#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 -#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) -#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) -#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) -#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) -#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) -#define MT9T001_RED_OFFSET 0x63 -#define MT9T001_BLUE_OFFSET 0x64 - -struct mt9t001 { - struct v4l2_subdev subdev; - struct media_pad pad; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *gains[4]; - - u16 output_control; - u16 black_level; -}; - -static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9t001, subdev); -} - -static int mt9t001_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, - u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - u16 value = (mt9t001->output_control & ~clear) | set; - int ret; - - if (value == mt9t001->output_control) - return 0; - - ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); - if (ret < 0) - return ret; - - mt9t001->output_control = value; - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static struct v4l2_mbus_framefmt * -__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9t001->format; - default: - return NULL; - } -} - -static struct v4l2_rect * -__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9t001->crop; - default: - return NULL; - } -} - -static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) -{ - const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *format = &mt9t001->format; - struct v4l2_rect *crop = &mt9t001->crop; - unsigned int hratio; - unsigned int vratio; - int ret; - - if (!enable) - return mt9t001_set_output_control(mt9t001, mode, 0); - - /* Configure the window size and row/column bin */ - hratio = DIV_ROUND_CLOSEST(crop->width, format->width); - vratio = DIV_ROUND_CLOSEST(crop->height, format->height); - - ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); - if (ret < 0) - return ret; - - /* Switch to master "normal" mode */ - return mt9t001_set_output_control(mt9t001, 0, mode); -} - -static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index > 0) - return -EINVAL; - - code->code = V4L2_MBUS_FMT_SGRBG10_1X10; - return 0; -} - -static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; - fse->max_width = fse->min_width; - fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; - fse->max_height = fse->min_height; - - return 0; -} - -static int mt9t001_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad, - format->which); - return 0; -} - -static int mt9t001_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - unsigned int width; - unsigned int height; - unsigned int hratio; - unsigned int vratio; - - __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad, - format->which); - - /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); - - __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad, - format->which); - __format->width = __crop->width / hratio; - __format->height = __crop->height / vratio; - - format->format = *__format; - - return 0; -} - -static int mt9t001_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, - crop->which); - return 0; -} - -static int mt9t001_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels. - */ - rect.left = clamp(ALIGN(crop->rect.left, 2), - MT9T001_COLUMN_START_MIN, - MT9T001_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), - MT9T001_ROW_START_MIN, - MT9T001_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9T001_WINDOW_WIDTH_MIN + 1, - MT9T001_WINDOW_WIDTH_MAX + 1); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9T001_WINDOW_HEIGHT_MIN + 1, - MT9T001_WINDOW_HEIGHT_MAX + 1); - - rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, - crop->which); - __format->width = rect.width; - __format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) -#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) -#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) -#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) - -#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) -#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) -#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) -#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) - -static u16 mt9t001_gain_value(s32 *gain) -{ - /* Gain is controlled by 2 analog stages and a digital stage. Valid - * values for the 3 stages are - * - * Stage Min Max Step - * ------------------------------------------ - * First analog stage x1 x2 1 - * Second analog stage x1 x4 0.125 - * Digital stage x1 x16 0.125 - * - * To minimize noise, the gain stages should be used in the second - * analog stage, first analog stage, digital stage order. Gain from a - * previous stage should be pushed to its maximum value before the next - * stage is used. - */ - if (*gain <= 32) - return *gain; - - if (*gain <= 64) { - *gain &= ~1; - return (1 << 6) | (*gain >> 1); - } - - *gain &= ~7; - return ((*gain - 64) << 5) | (1 << 6) | 32; -} - -static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) -{ - return mt9t001_set_output_control(mt9t001, - freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, - freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); -} - -static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) -{ - static const u8 gains[4] = { - MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, - MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN - }; - - struct mt9t001 *mt9t001 = - container_of(ctrl->handler, struct mt9t001, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - unsigned int count; - unsigned int i; - u16 value; - int ret; - - switch (ctrl->id) { - case V4L2_CID_GAIN_RED: - case V4L2_CID_GAIN_GREEN_RED: - case V4L2_CID_GAIN_GREEN_BLUE: - case V4L2_CID_GAIN_BLUE: - - /* Disable control updates if more than one control has changed - * in the cluster. - */ - for (i = 0, count = 0; i < 4; ++i) { - struct v4l2_ctrl *gain = mt9t001->gains[i]; - - if (gain->val != gain->cur.val) - count++; - } - - if (count > 1) { - ret = mt9t001_ctrl_freeze(mt9t001, true); - if (ret < 0) - return ret; - } - - /* Update the gain controls. */ - for (i = 0; i < 4; ++i) { - struct v4l2_ctrl *gain = mt9t001->gains[i]; - - if (gain->val == gain->cur.val) - continue; - - value = mt9t001_gain_value(&gain->val); - ret = mt9t001_write(client, gains[i], value); - if (ret < 0) { - mt9t001_ctrl_freeze(mt9t001, false); - return ret; - } - } - - /* Enable control updates. */ - if (count > 1) { - ret = mt9t001_ctrl_freeze(mt9t001, false); - if (ret < 0) - return ret; - } - - break; - - case V4L2_CID_EXPOSURE: - ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, - ctrl->val & 0xffff); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, - ctrl->val >> 16); - - case V4L2_CID_TEST_PATTERN: - ret = mt9t001_set_output_control(mt9t001, - ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, - ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); - - case V4L2_CID_BLACK_LEVEL_AUTO: - value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; - ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, - value); - if (ret < 0) - return ret; - - mt9t001->black_level = value; - break; - - case V4L2_CID_BLACK_LEVEL_OFFSET: - ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); - - case V4L2_CID_BLACK_LEVEL_CALIBRATE: - return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, - MT9T001_BLACK_LEVEL_RECALCULATE | - mt9t001->black_level); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9t001_ctrl_ops = { - .s_ctrl = mt9t001_s_ctrl, -}; - -static const struct v4l2_ctrl_config mt9t001_ctrls[] = { - { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_TEST_PATTERN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Test pattern", - .min = 0, - .max = 1023, - .step = 1, - .def = 0, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Black Level, Auto", - .min = 0, - .max = 1, - .step = 1, - .def = 1, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_OFFSET, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Black Level, Offset", - .min = -256, - .max = 255, - .step = 1, - .def = 32, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Black Level, Calibrate", - .min = 0, - .max = 0, - .step = 0, - .def = 0, - .flags = V4L2_CTRL_FLAG_WRITE_ONLY, - }, -}; - -static const struct v4l2_ctrl_config mt9t001_gains[] = { - { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_RED, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Red", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_GREEN_RED, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green (R)", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_GREEN_BLUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green (B)", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_BLUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Blue", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev internal operations - */ - -static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(fh, 0); - crop->left = MT9T001_COLUMN_START_DEF; - crop->top = MT9T001_ROW_START_DEF; - crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; - crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - - format = v4l2_subdev_get_try_format(fh, 0); - format->code = V4L2_MBUS_FMT_SGRBG10_1X10; - format->width = MT9T001_WINDOW_WIDTH_DEF + 1; - format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - return 0; -} - -static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { - .s_stream = mt9t001_s_stream, -}; - -static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { - .enum_mbus_code = mt9t001_enum_mbus_code, - .enum_frame_size = mt9t001_enum_frame_size, - .get_fmt = mt9t001_get_format, - .set_fmt = mt9t001_set_format, - .get_crop = mt9t001_get_crop, - .set_crop = mt9t001_set_crop, -}; - -static struct v4l2_subdev_ops mt9t001_subdev_ops = { - .video = &mt9t001_subdev_video_ops, - .pad = &mt9t001_subdev_pad_ops, -}; - -static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { - .open = mt9t001_open, -}; - -static int mt9t001_video_probe(struct i2c_client *client) -{ - struct mt9t001_platform_data *pdata = client->dev.platform_data; - s32 data; - int ret; - - dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", - client->addr); - - /* Reset the chip and stop data read out */ - ret = mt9t001_write(client, MT9T001_RESET, 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_RESET, 0); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); - if (ret < 0) - return ret; - - /* Configure the pixel clock polarity */ - if (pdata->clk_pol) { - ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, - MT9T001_PIXEL_CLOCK_INVERT); - if (ret < 0) - return ret; - } - - /* Read and check the sensor version */ - data = mt9t001_read(client, MT9T001_CHIP_VERSION); - if (data != MT9T001_CHIP_ID) { - dev_err(&client->dev, "MT9T001 not detected, wrong version " - "0x%04x\n", data); - return -ENODEV; - } - - dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", - client->addr); - - return ret; -} - -static int mt9t001_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t001_platform_data *pdata = client->dev.platform_data; - struct mt9t001 *mt9t001; - unsigned int i; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - ret = mt9t001_video_probe(client); - if (ret < 0) - return ret; - - mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL); - if (!mt9t001) - return -ENOMEM; - - v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + - ARRAY_SIZE(mt9t001_gains) + 3); - - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, - MT9T001_SHUTTER_WIDTH_MAX, 1, - MT9T001_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, - 1, pdata->ext_clk); - - for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) - v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); - - for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) - mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, - &mt9t001_gains[i], NULL); - - v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); - - mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; - - if (mt9t001->ctrls.error) { - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9t001->ctrls.error); - ret = -EINVAL; - goto done; - } - - mt9t001->crop.left = MT9T001_COLUMN_START_DEF; - mt9t001->crop.top = MT9T001_ROW_START_DEF; - mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; - mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; - - mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; - mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; - mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; - mt9t001->format.field = V4L2_FIELD_NONE; - mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; - - v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); - mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; - mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); - -done: - if (ret < 0) { - v4l2_ctrl_handler_free(&mt9t001->ctrls); - media_entity_cleanup(&mt9t001->subdev.entity); - kfree(mt9t001); - } - - return ret; -} - -static int mt9t001_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - v4l2_ctrl_handler_free(&mt9t001->ctrls); - v4l2_device_unregister_subdev(subdev); - media_entity_cleanup(&subdev->entity); - kfree(mt9t001); - return 0; -} - -static const struct i2c_device_id mt9t001_id[] = { - { "mt9t001", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t001_id); - -static struct i2c_driver mt9t001_driver = { - .driver = { - .name = "mt9t001", - }, - .probe = mt9t001_probe, - .remove = mt9t001_remove, - .id_table = mt9t001_id, -}; - -module_i2c_driver(mt9t001_driver); - -MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c deleted file mode 100644 index 6bf01ad62765..000000000000 --- a/drivers/media/video/mt9v011.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor - * - * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) - * This code is placed under the terms of the GNU General Public License v2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -#define R00_MT9V011_CHIP_VERSION 0x00 -#define R01_MT9V011_ROWSTART 0x01 -#define R02_MT9V011_COLSTART 0x02 -#define R03_MT9V011_HEIGHT 0x03 -#define R04_MT9V011_WIDTH 0x04 -#define R05_MT9V011_HBLANK 0x05 -#define R06_MT9V011_VBLANK 0x06 -#define R07_MT9V011_OUT_CTRL 0x07 -#define R09_MT9V011_SHUTTER_WIDTH 0x09 -#define R0A_MT9V011_CLK_SPEED 0x0a -#define R0B_MT9V011_RESTART 0x0b -#define R0C_MT9V011_SHUTTER_DELAY 0x0c -#define R0D_MT9V011_RESET 0x0d -#define R1E_MT9V011_DIGITAL_ZOOM 0x1e -#define R20_MT9V011_READ_MODE 0x20 -#define R2B_MT9V011_GREEN_1_GAIN 0x2b -#define R2C_MT9V011_BLUE_GAIN 0x2c -#define R2D_MT9V011_RED_GAIN 0x2d -#define R2E_MT9V011_GREEN_2_GAIN 0x2e -#define R35_MT9V011_GLOBAL_GAIN 0x35 -#define RF1_MT9V011_CHIP_ENABLE 0xf1 - -#define MT9V011_VERSION 0x8232 -#define MT9V011_REV_B_VERSION 0x8243 - -/* supported controls */ -static struct v4l2_queryctrl mt9v011_qctrl[] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = (1 << 12) - 1 - 0x0020, - .step = 1, - .default_value = 0x0020, - .flags = 0, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 2047, - .step = 1, - .default_value = 0x01fc, - .flags = 0, - }, { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - } -}; - -struct mt9v011 { - struct v4l2_subdev sd; - unsigned width, height; - unsigned xtal; - unsigned hflip:1; - unsigned vflip:1; - - u16 global_gain, exposure; - s16 red_bal, blue_bal; -}; - -static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9v011, sd); -} - -static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - __be16 buffer; - int rc, val; - - rc = i2c_master_send(c, &addr, 1); - if (rc != 1) - v4l2_dbg(0, debug, sd, - "i2c i/o error: rc == %d (should be 1)\n", rc); - - msleep(10); - - rc = i2c_master_recv(c, (char *)&buffer, 2); - if (rc != 2) - v4l2_dbg(0, debug, sd, - "i2c i/o error: rc == %d (should be 2)\n", rc); - - val = be16_to_cpu(buffer); - - v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val); - - return val; -} - -static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr, - u16 value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[3]; - int rc; - - buffer[0] = addr; - buffer[1] = value >> 8; - buffer[2] = value & 0xff; - - v4l2_dbg(2, debug, sd, - "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value); - rc = i2c_master_send(c, buffer, 3); - if (rc != 3) - v4l2_dbg(0, debug, sd, - "i2c i/o error: rc == %d (should be 3)\n", rc); -} - - -struct i2c_reg_value { - unsigned char reg; - u16 value; -}; - -/* - * Values used at the original driver - * Some values are marked as Reserved at the datasheet - */ -static const struct i2c_reg_value mt9v011_init_default[] = { - { R0D_MT9V011_RESET, 0x0001 }, - { R0D_MT9V011_RESET, 0x0000 }, - - { R0C_MT9V011_SHUTTER_DELAY, 0x0000 }, - { R09_MT9V011_SHUTTER_WIDTH, 0x1fc }, - - { R0A_MT9V011_CLK_SPEED, 0x0000 }, - { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, - - { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ -}; - - -static u16 calc_mt9v011_gain(s16 lineargain) -{ - - u16 digitalgain = 0; - u16 analogmult = 0; - u16 analoginit = 0; - - if (lineargain < 0) - lineargain = 0; - - /* recommended minimum */ - lineargain += 0x0020; - - if (lineargain > 2047) - lineargain = 2047; - - if (lineargain > 1023) { - digitalgain = 3; - analogmult = 3; - analoginit = lineargain / 16; - } else if (lineargain > 511) { - digitalgain = 1; - analogmult = 3; - analoginit = lineargain / 8; - } else if (lineargain > 255) { - analogmult = 3; - analoginit = lineargain / 4; - } else if (lineargain > 127) { - analogmult = 1; - analoginit = lineargain / 2; - } else - analoginit = lineargain; - - return analoginit + (analogmult << 7) + (digitalgain << 9); - -} - -static void set_balance(struct v4l2_subdev *sd) -{ - struct mt9v011 *core = to_mt9v011(sd); - u16 green_gain, blue_gain, red_gain; - u16 exposure; - s16 bal; - - exposure = core->exposure; - - green_gain = calc_mt9v011_gain(core->global_gain); - - bal = core->global_gain; - bal += (core->blue_bal * core->global_gain / (1 << 7)); - blue_gain = calc_mt9v011_gain(bal); - - bal = core->global_gain; - bal += (core->red_bal * core->global_gain / (1 << 7)); - red_gain = calc_mt9v011_gain(bal); - - mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); - mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); - mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); - mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); - mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); -} - -static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned height, width, hblank, vblank, speed; - unsigned row_time, t_time; - u64 frames_per_ms; - unsigned tmp; - - height = mt9v011_read(sd, R03_MT9V011_HEIGHT); - width = mt9v011_read(sd, R04_MT9V011_WIDTH); - hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); - vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); - speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); - - row_time = (width + 113 + hblank) * (speed + 2); - t_time = row_time * (height + vblank + 1); - - frames_per_ms = core->xtal * 1000l; - do_div(frames_per_ms, t_time); - tmp = frames_per_ms; - - v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", - tmp / 1000, tmp % 1000, t_time); - - if (numerator && denominator) { - *numerator = 1000; - *denominator = (u32)frames_per_ms; - } -} - -static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned height, width, hblank, vblank; - unsigned row_time, line_time; - u64 t_time, speed; - - /* Avoid bogus calculus */ - if (!numerator || !denominator) - return 0; - - height = mt9v011_read(sd, R03_MT9V011_HEIGHT); - width = mt9v011_read(sd, R04_MT9V011_WIDTH); - hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); - vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); - - row_time = width + 113 + hblank; - line_time = height + vblank + 1; - - t_time = core->xtal * ((u64)numerator); - /* round to the closest value */ - t_time += denominator / 2; - do_div(t_time, denominator); - - speed = t_time; - do_div(speed, row_time * line_time); - - /* Avoid having a negative value for speed */ - if (speed < 2) - speed = 0; - else - speed -= 2; - - /* Avoid speed overflow */ - if (speed > 15) - return 15; - - return (u16)speed; -} - -static void set_res(struct v4l2_subdev *sd) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned vstart, hstart; - - /* - * The mt9v011 doesn't have scaling. So, in order to select the desired - * resolution, we're cropping at the middle of the sensor. - * hblank and vblank should be adjusted, in order to warrant that - * we'll preserve the line timings for 30 fps, no matter what resolution - * is selected. - * NOTE: datasheet says that width (and height) should be filled with - * width-1. However, this doesn't work, since one pixel per line will - * be missing. - */ - - hstart = 20 + (640 - core->width) / 2; - mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); - mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); - mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); - - vstart = 8 + (480 - core->height) / 2; - mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); - mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); - mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); - - calc_fps(sd, NULL, NULL); -}; - -static void set_read_mode(struct v4l2_subdev *sd) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned mode = 0x1000; - - if (core->hflip) - mode |= 0x4000; - - if (core->vflip) - mode |= 0x8000; - - mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); -} - -static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++) - mt9v011_write(sd, mt9v011_init_default[i].reg, - mt9v011_init_default[i].value); - - set_balance(sd); - set_res(sd); - set_read_mode(sd); - - return 0; -}; - -static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - - v4l2_dbg(1, debug, sd, "g_ctrl called\n"); - - switch (ctrl->id) { - case V4L2_CID_GAIN: - ctrl->value = core->global_gain; - return 0; - case V4L2_CID_EXPOSURE: - ctrl->value = core->exposure; - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = core->red_bal; - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = core->blue_bal; - return 0; - case V4L2_CID_HFLIP: - ctrl->value = core->hflip ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - ctrl->value = core->vflip ? 1 : 0; - return 0; - } - return -EINVAL; -} - -static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int i; - - v4l2_dbg(1, debug, sd, "queryctrl called\n"); - - for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) - if (qc->id && qc->id == mt9v011_qctrl[i].id) { - memcpy(qc, &(mt9v011_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - - -static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - u8 i, n; - n = ARRAY_SIZE(mt9v011_qctrl); - - for (i = 0; i < n; i++) { - if (ctrl->id != mt9v011_qctrl[i].id) - continue; - if (ctrl->value < mt9v011_qctrl[i].minimum || - ctrl->value > mt9v011_qctrl[i].maximum) - return -ERANGE; - v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", - ctrl->id, ctrl->value); - break; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - core->global_gain = ctrl->value; - break; - case V4L2_CID_EXPOSURE: - core->exposure = ctrl->value; - break; - case V4L2_CID_RED_BALANCE: - core->red_bal = ctrl->value; - break; - case V4L2_CID_BLUE_BALANCE: - core->blue_bal = ctrl->value; - break; - case V4L2_CID_HFLIP: - core->hflip = ctrl->value; - set_read_mode(sd); - return 0; - case V4L2_CID_VFLIP: - core->vflip = ctrl->value; - set_read_mode(sd); - return 0; - default: - return -EINVAL; - } - - set_balance(sd); - - return 0; -} - -static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index > 0) - return -EINVAL; - - *code = V4L2_MBUS_FMT_SGRBG8_1X8; - return 0; -} - -static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8) - return -EINVAL; - - v4l_bound_align_image(&fmt->width, 48, 639, 1, - &fmt->height, 32, 480, 1, 0); - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - - return 0; -} - -static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(struct v4l2_captureparm)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - calc_fps(sd, - &cp->timeperframe.numerator, - &cp->timeperframe.denominator); - - return 0; -} - -static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - u16 speed; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; - - speed = calc_speed(sd, tpf->numerator, tpf->denominator); - - mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); - v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); - - /* Recalculate and update fps info */ - calc_fps(sd, &tpf->numerator, &tpf->denominator); - - return 0; -} - -static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - struct mt9v011 *core = to_mt9v011(sd); - int rc; - - rc = mt9v011_try_mbus_fmt(sd, fmt); - if (rc < 0) - return -EINVAL; - - core->width = fmt->width; - core->height = fmt->height; - - set_res(sd); - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v011_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - reg->val = mt9v011_read(sd, reg->reg & 0xff); - reg->size = 2; - - return 0; -} - -static int mt9v011_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff); - - return 0; -} -#endif - -static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - u16 version; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, - version); -} - -static const struct v4l2_subdev_core_ops mt9v011_core_ops = { - .queryctrl = mt9v011_queryctrl, - .g_ctrl = mt9v011_g_ctrl, - .s_ctrl = mt9v011_s_ctrl, - .reset = mt9v011_reset, - .g_chip_ident = mt9v011_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9v011_g_register, - .s_register = mt9v011_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops mt9v011_video_ops = { - .enum_mbus_fmt = mt9v011_enum_mbus_fmt, - .try_mbus_fmt = mt9v011_try_mbus_fmt, - .s_mbus_fmt = mt9v011_s_mbus_fmt, - .g_parm = mt9v011_g_parm, - .s_parm = mt9v011_s_parm, -}; - -static const struct v4l2_subdev_ops mt9v011_ops = { - .core = &mt9v011_core_ops, - .video = &mt9v011_video_ops, -}; - - -/**************************************************************************** - I2C Client & Driver - ****************************************************************************/ - -static int mt9v011_probe(struct i2c_client *c, - const struct i2c_device_id *id) -{ - u16 version; - struct mt9v011 *core; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(c->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL); - if (!core) - return -ENOMEM; - - sd = &core->sd; - v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); - - /* Check if the sensor is really a MT9V011 */ - version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); - if ((version != MT9V011_VERSION) && - (version != MT9V011_REV_B_VERSION)) { - v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", - version); - kfree(core); - return -EINVAL; - } - - core->global_gain = 0x0024; - core->exposure = 0x01fc; - core->width = 640; - core->height = 480; - core->xtal = 27000000; /* Hz */ - - if (c->dev.platform_data) { - struct mt9v011_platform_data *pdata = c->dev.platform_data; - - core->xtal = pdata->xtal; - v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", - core->xtal / 1000000, (core->xtal / 1000) % 1000); - } - - v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", - c->addr << 1, c->adapter->name, version); - - return 0; -} - -static int mt9v011_remove(struct i2c_client *c) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(c); - - v4l2_dbg(1, debug, sd, - "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", - c->addr << 1); - - v4l2_device_unregister_subdev(sd); - kfree(to_mt9v011(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id mt9v011_id[] = { - { "mt9v011", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v011_id); - -static struct i2c_driver mt9v011_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "mt9v011", - }, - .probe = mt9v011_probe, - .remove = mt9v011_remove, - .id_table = mt9v011_id, -}; - -module_i2c_driver(mt9v011_driver); diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c deleted file mode 100644 index 4ba4884c016e..000000000000 --- a/drivers/media/video/mt9v032.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * Driver for MT9V032 CMOS Image Sensor from Micron - * - * Copyright (C) 2010, Laurent Pinchart - * - * Based on the MT9M001 driver, - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MT9V032_PIXEL_ARRAY_HEIGHT 492 -#define MT9V032_PIXEL_ARRAY_WIDTH 782 - -#define MT9V032_CHIP_VERSION 0x00 -#define MT9V032_CHIP_ID_REV1 0x1311 -#define MT9V032_CHIP_ID_REV3 0x1313 -#define MT9V032_COLUMN_START 0x01 -#define MT9V032_COLUMN_START_MIN 1 -#define MT9V032_COLUMN_START_DEF 1 -#define MT9V032_COLUMN_START_MAX 752 -#define MT9V032_ROW_START 0x02 -#define MT9V032_ROW_START_MIN 4 -#define MT9V032_ROW_START_DEF 5 -#define MT9V032_ROW_START_MAX 482 -#define MT9V032_WINDOW_HEIGHT 0x03 -#define MT9V032_WINDOW_HEIGHT_MIN 1 -#define MT9V032_WINDOW_HEIGHT_DEF 480 -#define MT9V032_WINDOW_HEIGHT_MAX 480 -#define MT9V032_WINDOW_WIDTH 0x04 -#define MT9V032_WINDOW_WIDTH_MIN 1 -#define MT9V032_WINDOW_WIDTH_DEF 752 -#define MT9V032_WINDOW_WIDTH_MAX 752 -#define MT9V032_HORIZONTAL_BLANKING 0x05 -#define MT9V032_HORIZONTAL_BLANKING_MIN 43 -#define MT9V032_HORIZONTAL_BLANKING_MAX 1023 -#define MT9V032_VERTICAL_BLANKING 0x06 -#define MT9V032_VERTICAL_BLANKING_MIN 4 -#define MT9V032_VERTICAL_BLANKING_MAX 3000 -#define MT9V032_CHIP_CONTROL 0x07 -#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) -#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) -#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) -#define MT9V032_SHUTTER_WIDTH1 0x08 -#define MT9V032_SHUTTER_WIDTH2 0x09 -#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a -#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b -#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 -#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 -#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 -#define MT9V032_RESET 0x0c -#define MT9V032_READ_MODE 0x0d -#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) -#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 -#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) -#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 -#define MT9V032_READ_MODE_ROW_FLIP (1 << 4) -#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) -#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) -#define MT9V032_READ_MODE_DARK_ROWS (1 << 7) -#define MT9V032_PIXEL_OPERATION_MODE 0x0f -#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) -#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) -#define MT9V032_ANALOG_GAIN 0x35 -#define MT9V032_ANALOG_GAIN_MIN 16 -#define MT9V032_ANALOG_GAIN_DEF 16 -#define MT9V032_ANALOG_GAIN_MAX 64 -#define MT9V032_MAX_ANALOG_GAIN 0x36 -#define MT9V032_MAX_ANALOG_GAIN_MAX 127 -#define MT9V032_FRAME_DARK_AVERAGE 0x42 -#define MT9V032_DARK_AVG_THRESH 0x46 -#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) -#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 -#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) -#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 -#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 -#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) -#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) -#define MT9V032_PIXEL_CLOCK 0x74 -#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) -#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) -#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) -#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) -#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) -#define MT9V032_TEST_PATTERN 0x7f -#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) -#define MT9V032_TEST_PATTERN_DATA_SHIFT 0 -#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) -#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) -#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) -#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) -#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) -#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) -#define MT9V032_TEST_PATTERN_ENABLE (1 << 13) -#define MT9V032_TEST_PATTERN_FLIP (1 << 14) -#define MT9V032_AEC_AGC_ENABLE 0xaf -#define MT9V032_AEC_ENABLE (1 << 0) -#define MT9V032_AGC_ENABLE (1 << 1) -#define MT9V032_THERMAL_INFO 0xc1 - -struct mt9v032 { - struct v4l2_subdev subdev; - struct media_pad pad; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - - struct v4l2_ctrl_handler ctrls; - - struct mutex power_lock; - int power_count; - - struct mt9v032_platform_data *pdata; - u16 chip_control; - u16 aec_agc; -}; - -static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9v032, subdev); -} - -static int mt9v032_read(struct i2c_client *client, const u8 reg) -{ - s32 data = i2c_smbus_read_word_swapped(client, reg); - dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, - data, reg); - return data; -} - -static int mt9v032_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, - data, reg); - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - u16 value = (mt9v032->chip_control & ~clear) | set; - int ret; - - ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); - if (ret < 0) - return ret; - - mt9v032->chip_control = value; - return 0; -} - -static int -mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - u16 value = mt9v032->aec_agc; - int ret; - - if (enable) - value |= which; - else - value &= ~which; - - ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); - if (ret < 0) - return ret; - - mt9v032->aec_agc = value; - return 0; -} - -static int mt9v032_power_on(struct mt9v032 *mt9v032) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - int ret; - - if (mt9v032->pdata->set_clock) { - mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000); - udelay(1); - } - - /* Reset the chip and stop data read out */ - ret = mt9v032_write(client, MT9V032_RESET, 1); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_RESET, 0); - if (ret < 0) - return ret; - - return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); -} - -static void mt9v032_power_off(struct mt9v032 *mt9v032) -{ - if (mt9v032->pdata->set_clock) - mt9v032->pdata->set_clock(&mt9v032->subdev, 0); -} - -static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - int ret; - - if (!on) { - mt9v032_power_off(mt9v032); - return 0; - } - - ret = mt9v032_power_on(mt9v032); - if (ret < 0) - return ret; - - /* Configure the pixel clock polarity */ - if (mt9v032->pdata && mt9v032->pdata->clk_pol) { - ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, - MT9V032_PIXEL_CLOCK_INV_PXL_CLK); - if (ret < 0) - return ret; - } - - /* Disable the noise correction algorithm and restore the controls. */ - ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); - if (ret < 0) - return ret; - - return v4l2_ctrl_handler_setup(&mt9v032->ctrls); -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static struct v4l2_mbus_framefmt * -__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9v032->format; - default: - return NULL; - } -} - -static struct v4l2_rect * -__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9v032->crop; - default: - return NULL; - } -} - -static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) -{ - const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE - | MT9V032_CHIP_CONTROL_DOUT_ENABLE - | MT9V032_CHIP_CONTROL_SEQUENTIAL; - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - struct v4l2_mbus_framefmt *format = &mt9v032->format; - struct v4l2_rect *crop = &mt9v032->crop; - unsigned int hratio; - unsigned int vratio; - int ret; - - if (!enable) - return mt9v032_set_chip_control(mt9v032, mode, 0); - - /* Configure the window size and row/column bin */ - hratio = DIV_ROUND_CLOSEST(crop->width, format->width); - vratio = DIV_ROUND_CLOSEST(crop->height, format->height); - - ret = mt9v032_write(client, MT9V032_READ_MODE, - (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | - (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); - if (ret < 0) - return ret; - - ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, - max(43, 660 - crop->width)); - if (ret < 0) - return ret; - - /* Switch to master "normal" mode */ - return mt9v032_set_chip_control(mt9v032, 0, mode); -} - -static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index > 0) - return -EINVAL; - - code->code = V4L2_MBUS_FMT_SGRBG10_1X10; - return 0; -} - -static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; - fse->max_width = fse->min_width; - fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; - fse->max_height = fse->min_height; - - return 0; -} - -static int mt9v032_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - - format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, - format->which); - return 0; -} - -static int mt9v032_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *format) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - unsigned int width; - unsigned int height; - unsigned int hratio; - unsigned int vratio; - - __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, - format->which); - - /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); - - __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, - format->which); - __format->width = __crop->width / hratio; - __format->height = __crop->height / vratio; - - format->format = *__format; - - return 0; -} - -static int mt9v032_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - - crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, - crop->which); - return 0; -} - -static int mt9v032_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - /* Clamp the crop rectangle boundaries and align them to a non multiple - * of 2 pixels to ensure a GRBG Bayer pattern. - */ - rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, - MT9V032_COLUMN_START_MIN, - MT9V032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, - MT9V032_ROW_START_MIN, - MT9V032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9V032_WINDOW_WIDTH_MIN, - MT9V032_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9V032_WINDOW_HEIGHT_MIN, - MT9V032_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, - crop->which); - __format->width = rect.width; - __format->height = rect.height; - } - - *__crop = rect; - crop->rect = rect; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) - -static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v032 *mt9v032 = - container_of(ctrl->handler, struct mt9v032, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - u16 data; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, - ctrl->val); - - case V4L2_CID_GAIN: - return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); - - case V4L2_CID_EXPOSURE_AUTO: - return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, - !ctrl->val); - - case V4L2_CID_EXPOSURE: - return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, - ctrl->val); - - case V4L2_CID_TEST_PATTERN: - switch (ctrl->val) { - case 0: - data = 0; - break; - case 1: - data = MT9V032_TEST_PATTERN_GRAY_VERTICAL - | MT9V032_TEST_PATTERN_ENABLE; - break; - case 2: - data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL - | MT9V032_TEST_PATTERN_ENABLE; - break; - case 3: - data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL - | MT9V032_TEST_PATTERN_ENABLE; - break; - default: - data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT) - | MT9V032_TEST_PATTERN_USE_DATA - | MT9V032_TEST_PATTERN_ENABLE - | MT9V032_TEST_PATTERN_FLIP; - break; - } - - return mt9v032_write(client, MT9V032_TEST_PATTERN, data); - } - - return 0; -} - -static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { - .s_ctrl = mt9v032_s_ctrl, -}; - -static const struct v4l2_ctrl_config mt9v032_ctrls[] = { - { - .ops = &mt9v032_ctrl_ops, - .id = V4L2_CID_TEST_PATTERN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Test pattern", - .min = 0, - .max = 1023, - .step = 1, - .def = 0, - .flags = 0, - } -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) -{ - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - int ret = 0; - - mutex_lock(&mt9v032->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (mt9v032->power_count == !on) { - ret = __mt9v032_set_power(mt9v032, !!on); - if (ret < 0) - goto done; - } - - /* Update the power count. */ - mt9v032->power_count += on ? 1 : -1; - WARN_ON(mt9v032->power_count < 0); - -done: - mutex_unlock(&mt9v032->power_lock); - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev internal operations - */ - -static int mt9v032_registered(struct v4l2_subdev *subdev) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - s32 data; - int ret; - - dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", - client->addr); - - ret = mt9v032_power_on(mt9v032); - if (ret < 0) { - dev_err(&client->dev, "MT9V032 power up failed\n"); - return ret; - } - - /* Read and check the sensor version */ - data = mt9v032_read(client, MT9V032_CHIP_VERSION); - if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { - dev_err(&client->dev, "MT9V032 not detected, wrong version " - "0x%04x\n", data); - return -ENODEV; - } - - mt9v032_power_off(mt9v032); - - dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", - client->addr); - - return ret; -} - -static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(fh, 0); - crop->left = MT9V032_COLUMN_START_DEF; - crop->top = MT9V032_ROW_START_DEF; - crop->width = MT9V032_WINDOW_WIDTH_DEF; - crop->height = MT9V032_WINDOW_HEIGHT_DEF; - - format = v4l2_subdev_get_try_format(fh, 0); - format->code = V4L2_MBUS_FMT_SGRBG10_1X10; - format->width = MT9V032_WINDOW_WIDTH_DEF; - format->height = MT9V032_WINDOW_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - return mt9v032_set_power(subdev, 1); -} - -static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - return mt9v032_set_power(subdev, 0); -} - -static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { - .s_power = mt9v032_set_power, -}; - -static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { - .s_stream = mt9v032_s_stream, -}; - -static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { - .enum_mbus_code = mt9v032_enum_mbus_code, - .enum_frame_size = mt9v032_enum_frame_size, - .get_fmt = mt9v032_get_format, - .set_fmt = mt9v032_set_format, - .get_crop = mt9v032_get_crop, - .set_crop = mt9v032_set_crop, -}; - -static struct v4l2_subdev_ops mt9v032_subdev_ops = { - .core = &mt9v032_subdev_core_ops, - .video = &mt9v032_subdev_video_ops, - .pad = &mt9v032_subdev_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { - .registered = mt9v032_registered, - .open = mt9v032_open, - .close = mt9v032_close, -}; - -/* ----------------------------------------------------------------------------- - * Driver initialization and probing - */ - -static int mt9v032_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9v032 *mt9v032; - unsigned int i; - int ret; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); - if (!mt9v032) - return -ENOMEM; - - mutex_init(&mt9v032->power_lock); - mt9v032->pdata = client->dev.platform_data; - - v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4); - - v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, - MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); - v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, - V4L2_EXPOSURE_AUTO); - v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, - MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, - MT9V032_TOTAL_SHUTTER_WIDTH_DEF); - - for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i) - v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL); - - mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; - - if (mt9v032->ctrls.error) - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9v032->ctrls.error); - - mt9v032->crop.left = MT9V032_COLUMN_START_DEF; - mt9v032->crop.top = MT9V032_ROW_START_DEF; - mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; - mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; - - mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; - mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; - mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; - mt9v032->format.field = V4L2_FIELD_NONE; - mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; - - mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; - - v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); - mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; - mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); - if (ret < 0) - kfree(mt9v032); - - return ret; -} - -static int mt9v032_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9v032 *mt9v032 = to_mt9v032(subdev); - - v4l2_device_unregister_subdev(subdev); - media_entity_cleanup(&subdev->entity); - kfree(mt9v032); - return 0; -} - -static const struct i2c_device_id mt9v032_id[] = { - { "mt9v032", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v032_id); - -static struct i2c_driver mt9v032_driver = { - .driver = { - .name = "mt9v032", - }, - .probe = mt9v032_probe, - .remove = mt9v032_remove, - .id_table = mt9v032_id, -}; - -module_i2c_driver(mt9v032_driver); - -MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c deleted file mode 100644 index 440c12962bae..000000000000 --- a/drivers/media/video/noon010pc30.c +++ /dev/null @@ -1,851 +0,0 @@ -/* - * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP - * - * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, - * - * Initial register configuration based on a driver authored by - * HeungJun Kim . - * - * This program is free software; 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); - -#define MODULE_NAME "NOON010PC30" - -/* - * Register offsets within a page - * b15..b8 - page id, b7..b0 - register address - */ -#define POWER_CTRL_REG 0x0001 -#define PAGEMODE_REG 0x03 -#define DEVICE_ID_REG 0x0004 -#define NOON010PC30_ID 0x86 -#define VDO_CTL_REG(n) (0x0010 + (n)) -#define SYNC_CTL_REG 0x0012 -/* Window size and position */ -#define WIN_ROWH_REG 0x0013 -#define WIN_ROWL_REG 0x0014 -#define WIN_COLH_REG 0x0015 -#define WIN_COLL_REG 0x0016 -#define WIN_HEIGHTH_REG 0x0017 -#define WIN_HEIGHTL_REG 0x0018 -#define WIN_WIDTHH_REG 0x0019 -#define WIN_WIDTHL_REG 0x001A -#define HBLANKH_REG 0x001B -#define HBLANKL_REG 0x001C -#define VSYNCH_REG 0x001D -#define VSYNCL_REG 0x001E -/* VSYNC control */ -#define VS_CTL_REG(n) (0x00A1 + (n)) -/* page 1 */ -#define ISP_CTL_REG(n) (0x0110 + (n)) -#define YOFS_REG 0x0119 -#define DARK_YOFS_REG 0x011A -#define SAT_CTL_REG 0x0120 -#define BSAT_REG 0x0121 -#define RSAT_REG 0x0122 -/* Color correction */ -#define CMC_CTL_REG 0x0130 -#define CMC_OFSGH_REG 0x0133 -#define CMC_OFSGL_REG 0x0135 -#define CMC_SIGN_REG 0x0136 -#define CMC_GOFS_REG 0x0137 -#define CMC_COEF_REG(n) (0x0138 + (n)) -#define CMC_OFS_REG(n) (0x0141 + (n)) -/* Gamma correction */ -#define GMA_CTL_REG 0x0160 -#define GMA_COEF_REG(n) (0x0161 + (n)) -/* Lens Shading */ -#define LENS_CTRL_REG 0x01D0 -#define LENS_XCEN_REG 0x01D1 -#define LENS_YCEN_REG 0x01D2 -#define LENS_RC_REG 0x01D3 -#define LENS_GC_REG 0x01D4 -#define LENS_BC_REG 0x01D5 -#define L_AGON_REG 0x01D6 -#define L_AGOFF_REG 0x01D7 -/* Page 3 - Auto Exposure */ -#define AE_CTL_REG(n) (0x0310 + (n)) -#define AE_CTL9_REG 0x032C -#define AE_CTL10_REG 0x032D -#define AE_YLVL_REG 0x031C -#define AE_YTH_REG(n) (0x031D + (n)) -#define AE_WGT_REG 0x0326 -#define EXP_TIMEH_REG 0x0333 -#define EXP_TIMEM_REG 0x0334 -#define EXP_TIMEL_REG 0x0335 -#define EXP_MMINH_REG 0x0336 -#define EXP_MMINL_REG 0x0337 -#define EXP_MMAXH_REG 0x0338 -#define EXP_MMAXM_REG 0x0339 -#define EXP_MMAXL_REG 0x033A -/* Page 4 - Auto White Balance */ -#define AWB_CTL_REG(n) (0x0410 + (n)) -#define AWB_ENABE 0x80 -#define AWB_WGHT_REG 0x0419 -#define BGAIN_PAR_REG(n) (0x044F + (n)) -/* Manual white balance, when AWB_CTL2[0]=1 */ -#define MWB_RGAIN_REG 0x0466 -#define MWB_BGAIN_REG 0x0467 - -/* The token to mark an array end */ -#define REG_TERM 0xFFFF - -struct noon010_format { - enum v4l2_mbus_pixelcode code; - enum v4l2_colorspace colorspace; - u16 ispctl1_reg; -}; - -struct noon010_frmsize { - u16 width; - u16 height; - int vid_ctl1; -}; - -static const char * const noon010_supply_name[] = { - "vdd_core", "vddio", "vdda" -}; - -#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) - -struct noon010_info { - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_ctrl_handler hdl; - struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; - u32 gpio_nreset; - u32 gpio_nstby; - - /* Protects the struct members below */ - struct mutex lock; - - const struct noon010_format *curr_fmt; - const struct noon010_frmsize *curr_win; - unsigned int apply_new_cfg:1; - unsigned int streaming:1; - unsigned int hflip:1; - unsigned int vflip:1; - unsigned int power:1; - u8 i2c_reg_page; -}; - -struct i2c_regval { - u16 addr; - u16 val; -}; - -/* Supported resolutions. */ -static const struct noon010_frmsize noon010_sizes[] = { - { - .width = 352, - .height = 288, - .vid_ctl1 = 0, - }, { - .width = 176, - .height = 144, - .vid_ctl1 = 0x10, - }, { - .width = 88, - .height = 72, - .vid_ctl1 = 0x20, - }, -}; - -/* Supported pixel formats. */ -static const struct noon010_format noon010_formats[] = { - { - .code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x03, - }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x02, - }, { - .code = V4L2_MBUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0, - }, { - .code = V4L2_MBUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x01, - }, { - .code = V4L2_MBUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x40, - }, -}; - -static const struct i2c_regval noon010_base_regs[] = { - { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, - /* Color corection and saturation */ - { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, - { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, - { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, - { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, - { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, - { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, - { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, - { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, - { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, - { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, - { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, - { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, - { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, - { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, - /* Automatic white balance */ - { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, - { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, - /* Auto exposure */ - { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, - { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, - { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, - { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, - { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, - { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, - { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, - /* Lens shading compensation */ - { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, - { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, - { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, - { REG_TERM, 0 }, -}; - -static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) -{ - return container_of(sd, struct noon010_info, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; -} - -static inline int set_i2c_page(struct noon010_info *info, - struct i2c_client *client, unsigned int reg) -{ - u32 page = reg >> 8 & 0xFF; - int ret = 0; - - if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { - ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); - if (!ret) - info->i2c_reg_page = page; - } - return ret; -} - -static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct noon010_info *info = to_noon010(sd); - int ret = set_i2c_page(info, client, reg_addr); - - if (ret) - return ret; - return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); -} - -static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct noon010_info *info = to_noon010(sd); - int ret = set_i2c_page(info, client, reg_addr); - - if (ret) - return ret; - return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); -} - -static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, - const struct i2c_regval *msg) -{ - while (msg->addr != REG_TERM) { - int ret = cam_i2c_write(sd, msg->addr, msg->val); - - if (ret) - return ret; - msg++; - } - return 0; -} - -/* Device reset and sleep mode control */ -static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) -{ - struct noon010_info *info = to_noon010(sd); - u8 reg = sleep ? 0xF1 : 0xF0; - int ret = 0; - - if (reset) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); - udelay(20); - } - if (!ret) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); - if (reset && !ret) - info->i2c_reg_page = -1; - } - return ret; -} - -/* Automatic white balance control */ -static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) -{ - int ret; - - ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); - if (!ret) - ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); - return ret; -} - -/* Called with struct noon010_info.lock mutex held */ -static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) -{ - struct noon010_info *info = to_noon010(sd); - int reg, ret; - - reg = cam_i2c_read(sd, VDO_CTL_REG(1)); - if (reg < 0) - return reg; - - reg &= 0x7C; - if (hflip) - reg |= 0x01; - if (vflip) - reg |= 0x02; - - ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); - if (!ret) { - info->hflip = hflip; - info->vflip = vflip; - } - return ret; -} - -/* Configure resolution and color format */ -static int noon010_set_params(struct v4l2_subdev *sd) -{ - struct noon010_info *info = to_noon010(sd); - - int ret = cam_i2c_write(sd, VDO_CTL_REG(0), - info->curr_win->vid_ctl1); - if (ret) - return ret; - return cam_i2c_write(sd, ISP_CTL_REG(0), - info->curr_fmt->ispctl1_reg); -} - -/* Find nearest matching image pixel size. */ -static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, - const struct noon010_frmsize **size) -{ - unsigned int min_err = ~0; - int i = ARRAY_SIZE(noon010_sizes); - const struct noon010_frmsize *fsize = &noon010_sizes[0], - *match = NULL; - - while (i--) { - int err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - - if (err < min_err) { - min_err = err; - match = fsize; - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - if (size) - *size = match; - return 0; - } - return -EINVAL; -} - -/* Called with info.lock mutex held */ -static int power_enable(struct noon010_info *info) -{ - int ret; - - if (info->power) { - v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); - return 0; - } - - if (gpio_is_valid(info->gpio_nstby)) - gpio_set_value(info->gpio_nstby, 0); - - if (gpio_is_valid(info->gpio_nreset)) - gpio_set_value(info->gpio_nreset, 0); - - ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); - if (ret) - return ret; - - if (gpio_is_valid(info->gpio_nreset)) { - msleep(50); - gpio_set_value(info->gpio_nreset, 1); - } - if (gpio_is_valid(info->gpio_nstby)) { - udelay(1000); - gpio_set_value(info->gpio_nstby, 1); - } - if (gpio_is_valid(info->gpio_nreset)) { - udelay(1000); - gpio_set_value(info->gpio_nreset, 0); - msleep(100); - gpio_set_value(info->gpio_nreset, 1); - msleep(20); - } - info->power = 1; - - v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); - return 0; -} - -/* Called with info.lock mutex held */ -static int power_disable(struct noon010_info *info) -{ - int ret; - - if (!info->power) { - v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); - return 0; - } - - ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); - if (ret) - return ret; - - if (gpio_is_valid(info->gpio_nstby)) - gpio_set_value(info->gpio_nstby, 0); - - if (gpio_is_valid(info->gpio_nreset)) - gpio_set_value(info->gpio_nreset, 0); - - info->power = 0; - - v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); - - return 0; -} - -static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct noon010_info *info = to_noon010(sd); - int ret = 0; - - v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", - __func__, ctrl->id, ctrl->val); - - mutex_lock(&info->lock); - /* - * If the device is not powered up by the host driver do - * not apply any controls to H/W at this time. Instead - * the controls will be restored right after power-up. - */ - if (!info->power) - goto unlock; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = noon010_enable_autowhitebalance(sd, ctrl->val); - break; - case V4L2_CID_BLUE_BALANCE: - ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); - break; - case V4L2_CID_RED_BALANCE: - ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); - break; - default: - ret = -EINVAL; - } -unlock: - mutex_unlock(&info->lock); - return ret; -} - -static int noon010_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(noon010_formats)) - return -EINVAL; - - code->code = noon010_formats[code->index].code; - return 0; -} - -static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct noon010_info *info = to_noon010(sd); - struct v4l2_mbus_framefmt *mf; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); - fmt->format = *mf; - } - return 0; - } - mf = &fmt->format; - - mutex_lock(&info->lock); - mf->width = info->curr_win->width; - mf->height = info->curr_win->height; - mf->code = info->curr_fmt->code; - mf->colorspace = info->curr_fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - mutex_unlock(&info->lock); - return 0; -} - -/* Return nearest media bus frame format. */ -static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - int i = ARRAY_SIZE(noon010_formats); - - while (--i) - if (mf->code == noon010_formats[i].code) - break; - mf->code = noon010_formats[i].code; - - return &noon010_formats[i]; -} - -static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct noon010_info *info = to_noon010(sd); - const struct noon010_frmsize *size = NULL; - const struct noon010_format *nf; - struct v4l2_mbus_framefmt *mf; - int ret = 0; - - nf = noon010_try_fmt(sd, &fmt->format); - noon010_try_frame_size(&fmt->format, &size); - fmt->format.colorspace = V4L2_COLORSPACE_JPEG; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (fh) { - mf = v4l2_subdev_get_try_format(fh, 0); - *mf = fmt->format; - } - return 0; - } - mutex_lock(&info->lock); - if (!info->streaming) { - info->apply_new_cfg = 1; - info->curr_fmt = nf; - info->curr_win = size; - } else { - ret = -EBUSY; - } - mutex_unlock(&info->lock); - return ret; -} - -/* Called with struct noon010_info.lock mutex held */ -static int noon010_base_config(struct v4l2_subdev *sd) -{ - int ret = noon010_bulk_write_reg(sd, noon010_base_regs); - if (!ret) - ret = noon010_set_params(sd); - if (!ret) - ret = noon010_set_flip(sd, 1, 0); - - return ret; -} - -static int noon010_s_power(struct v4l2_subdev *sd, int on) -{ - struct noon010_info *info = to_noon010(sd); - int ret; - - mutex_lock(&info->lock); - if (on) { - ret = power_enable(info); - if (!ret) - ret = noon010_base_config(sd); - } else { - noon010_power_ctrl(sd, false, true); - ret = power_disable(info); - } - mutex_unlock(&info->lock); - - /* Restore the controls state */ - if (!ret && on) - ret = v4l2_ctrl_handler_setup(&info->hdl); - - return ret; -} - -static int noon010_s_stream(struct v4l2_subdev *sd, int on) -{ - struct noon010_info *info = to_noon010(sd); - int ret = 0; - - mutex_lock(&info->lock); - if (!info->streaming != !on) { - ret = noon010_power_ctrl(sd, false, !on); - if (!ret) - info->streaming = on; - } - if (!ret && on && info->apply_new_cfg) { - ret = noon010_set_params(sd); - if (!ret) - info->apply_new_cfg = 0; - } - mutex_unlock(&info->lock); - return ret; -} - -static int noon010_log_status(struct v4l2_subdev *sd) -{ - struct noon010_info *info = to_noon010(sd); - - v4l2_ctrl_handler_log_status(&info->hdl, sd->name); - return 0; -} - -static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); - - mf->width = noon010_sizes[0].width; - mf->height = noon010_sizes[0].height; - mf->code = noon010_formats[0].code; - mf->colorspace = V4L2_COLORSPACE_JPEG; - mf->field = V4L2_FIELD_NONE; - return 0; -} - -static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { - .open = noon010_open, -}; - -static const struct v4l2_ctrl_ops noon010_ctrl_ops = { - .s_ctrl = noon010_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops noon010_core_ops = { - .s_power = noon010_s_power, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .log_status = noon010_log_status, -}; - -static struct v4l2_subdev_pad_ops noon010_pad_ops = { - .enum_mbus_code = noon010_enum_mbus_code, - .get_fmt = noon010_get_fmt, - .set_fmt = noon010_set_fmt, -}; - -static struct v4l2_subdev_video_ops noon010_video_ops = { - .s_stream = noon010_s_stream, -}; - -static const struct v4l2_subdev_ops noon010_ops = { - .core = &noon010_core_ops, - .pad = &noon010_pad_ops, - .video = &noon010_video_ops, -}; - -/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ -static int noon010_detect(struct i2c_client *client, struct noon010_info *info) -{ - int ret; - - ret = power_enable(info); - if (ret) - return ret; - - ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); - if (ret < 0) - dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); - - power_disable(info); - - return ret == NOON010PC30_ID ? 0 : -ENODEV; -} - -static int noon010_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct noon010_info *info; - struct v4l2_subdev *sd; - const struct noon010pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - int i; - - if (!pdata) { - dev_err(&client->dev, "No platform data!\n"); - return -EIO; - } - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - mutex_init(&info->lock); - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &noon010_ops); - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); - - sd->internal_ops = &noon010_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - v4l2_ctrl_handler_init(&info->hdl, 3); - - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 127, 1, 64); - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); - - sd->ctrl_handler = &info->hdl; - - ret = info->hdl.error; - if (ret) - goto np_err; - - info->i2c_reg_page = -1; - info->gpio_nreset = -EINVAL; - info->gpio_nstby = -EINVAL; - info->curr_fmt = &noon010_formats[0]; - info->curr_win = &noon010_sizes[0]; - - if (gpio_is_valid(pdata->gpio_nreset)) { - ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST"); - if (ret) { - dev_err(&client->dev, "GPIO request error: %d\n", ret); - goto np_err; - } - info->gpio_nreset = pdata->gpio_nreset; - gpio_direction_output(info->gpio_nreset, 0); - gpio_export(info->gpio_nreset, 0); - } - - if (gpio_is_valid(pdata->gpio_nstby)) { - ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY"); - if (ret) { - dev_err(&client->dev, "GPIO request error: %d\n", ret); - goto np_gpio_err; - } - info->gpio_nstby = pdata->gpio_nstby; - gpio_direction_output(info->gpio_nstby, 0); - gpio_export(info->gpio_nstby, 0); - } - - for (i = 0; i < NOON010_NUM_SUPPLIES; i++) - info->supply[i].supply = noon010_supply_name[i]; - - ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, - info->supply); - if (ret) - goto np_reg_err; - - info->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); - if (ret < 0) - goto np_me_err; - - ret = noon010_detect(client, info); - if (!ret) - return 0; - -np_me_err: - regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); -np_reg_err: - if (gpio_is_valid(info->gpio_nstby)) - gpio_free(info->gpio_nstby); -np_gpio_err: - if (gpio_is_valid(info->gpio_nreset)) - gpio_free(info->gpio_nreset); -np_err: - v4l2_ctrl_handler_free(&info->hdl); - v4l2_device_unregister_subdev(sd); - kfree(info); - return ret; -} - -static int noon010_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct noon010_info *info = to_noon010(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&info->hdl); - - regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); - - if (gpio_is_valid(info->gpio_nreset)) - gpio_free(info->gpio_nreset); - - if (gpio_is_valid(info->gpio_nstby)) - gpio_free(info->gpio_nstby); - - media_entity_cleanup(&sd->entity); - kfree(info); - return 0; -} - -static const struct i2c_device_id noon010_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, noon010_id); - - -static struct i2c_driver noon010_i2c_driver = { - .driver = { - .name = MODULE_NAME - }, - .probe = noon010_probe, - .remove = noon010_remove, - .id_table = noon010_id, -}; - -module_i2c_driver(noon010_i2c_driver); - -MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c deleted file mode 100644 index e7c82b297514..000000000000 --- a/drivers/media/video/ov7670.c +++ /dev/null @@ -1,1586 +0,0 @@ -/* - * A V4L2 driver for OmniVision OV7670 cameras. - * - * Copyright 2006 One Laptop Per Child Association, Inc. Written - * by Jonathan Corbet with substantial inspiration from Mark - * McClelland's ovcamchip code. - * - * Copyright 2006-7 Jonathan Corbet - * - * This file may be distributed under the terms of the GNU General - * Public License, version 2. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Jonathan Corbet "); -MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* - * Basic window sizes. These probably belong somewhere more globally - * useful. - */ -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QVGA_WIDTH 320 -#define QVGA_HEIGHT 240 -#define CIF_WIDTH 352 -#define CIF_HEIGHT 288 -#define QCIF_WIDTH 176 -#define QCIF_HEIGHT 144 - -/* - * The 7670 sits on i2c with ID 0x42 - */ -#define OV7670_I2C_ADDR 0x42 - -/* Registers */ -#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ -#define REG_COM1 0x04 /* Control 1 */ -#define COM1_CCIR656 0x40 /* CCIR656 enable */ -#define REG_BAVE 0x05 /* U/B Average level */ -#define REG_GbAVE 0x06 /* Y/Gb Average level */ -#define REG_AECHH 0x07 /* AEC MS 5 bits */ -#define REG_RAVE 0x08 /* V/R Average level */ -#define REG_COM2 0x09 /* Control 2 */ -#define COM2_SSLEEP 0x10 /* Soft sleep mode */ -#define REG_PID 0x0a /* Product ID MSB */ -#define REG_VER 0x0b /* Product ID LSB */ -#define REG_COM3 0x0c /* Control 3 */ -#define COM3_SWAP 0x40 /* Byte swap */ -#define COM3_SCALEEN 0x08 /* Enable scaling */ -#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ -#define REG_COM4 0x0d /* Control 4 */ -#define REG_COM5 0x0e /* All "reserved" */ -#define REG_COM6 0x0f /* Control 6 */ -#define REG_AECH 0x10 /* More bits of AEC value */ -#define REG_CLKRC 0x11 /* Clocl control */ -#define CLK_EXT 0x40 /* Use external clock directly */ -#define CLK_SCALE 0x3f /* Mask for internal clock scale */ -#define REG_COM7 0x12 /* Control 7 */ -#define COM7_RESET 0x80 /* Register reset */ -#define COM7_FMT_MASK 0x38 -#define COM7_FMT_VGA 0x00 -#define COM7_FMT_CIF 0x20 /* CIF format */ -#define COM7_FMT_QVGA 0x10 /* QVGA format */ -#define COM7_FMT_QCIF 0x08 /* QCIF format */ -#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ -#define COM7_YUV 0x00 /* YUV */ -#define COM7_BAYER 0x01 /* Bayer format */ -#define COM7_PBAYER 0x05 /* "Processed bayer" */ -#define REG_COM8 0x13 /* Control 8 */ -#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ -#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ -#define COM8_BFILT 0x20 /* Band filter enable */ -#define COM8_AGC 0x04 /* Auto gain enable */ -#define COM8_AWB 0x02 /* White balance enable */ -#define COM8_AEC 0x01 /* Auto exposure enable */ -#define REG_COM9 0x14 /* Control 9 - gain ceiling */ -#define REG_COM10 0x15 /* Control 10 */ -#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ -#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ -#define COM10_HREF_REV 0x08 /* Reverse HREF */ -#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ -#define COM10_VS_NEG 0x02 /* VSYNC negative */ -#define COM10_HS_NEG 0x01 /* HSYNC negative */ -#define REG_HSTART 0x17 /* Horiz start high bits */ -#define REG_HSTOP 0x18 /* Horiz stop high bits */ -#define REG_VSTART 0x19 /* Vert start high bits */ -#define REG_VSTOP 0x1a /* Vert stop high bits */ -#define REG_PSHFT 0x1b /* Pixel delay after HREF */ -#define REG_MIDH 0x1c /* Manuf. ID high */ -#define REG_MIDL 0x1d /* Manuf. ID low */ -#define REG_MVFP 0x1e /* Mirror / vflip */ -#define MVFP_MIRROR 0x20 /* Mirror image */ -#define MVFP_FLIP 0x10 /* Vertical flip */ - -#define REG_AEW 0x24 /* AGC upper limit */ -#define REG_AEB 0x25 /* AGC lower limit */ -#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ -#define REG_HSYST 0x30 /* HSYNC rising edge delay */ -#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ -#define REG_HREF 0x32 /* HREF pieces */ -#define REG_TSLB 0x3a /* lots of stuff */ -#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ -#define REG_COM11 0x3b /* Control 11 */ -#define COM11_NIGHT 0x80 /* NIght mode enable */ -#define COM11_NMFR 0x60 /* Two bit NM frame rate */ -#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ -#define COM11_50HZ 0x08 /* Manual 50Hz select */ -#define COM11_EXP 0x02 -#define REG_COM12 0x3c /* Control 12 */ -#define COM12_HREF 0x80 /* HREF always */ -#define REG_COM13 0x3d /* Control 13 */ -#define COM13_GAMMA 0x80 /* Gamma enable */ -#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ -#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ -#define REG_COM14 0x3e /* Control 14 */ -#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ -#define REG_EDGE 0x3f /* Edge enhancement factor */ -#define REG_COM15 0x40 /* Control 15 */ -#define COM15_R10F0 0x00 /* Data range 10 to F0 */ -#define COM15_R01FE 0x80 /* 01 to FE */ -#define COM15_R00FF 0xc0 /* 00 to FF */ -#define COM15_RGB565 0x10 /* RGB565 output */ -#define COM15_RGB555 0x30 /* RGB555 output */ -#define REG_COM16 0x41 /* Control 16 */ -#define COM16_AWBGAIN 0x08 /* AWB gain enable */ -#define REG_COM17 0x42 /* Control 17 */ -#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ -#define COM17_CBAR 0x08 /* DSP Color bar */ - -/* - * This matrix defines how the colors are generated, must be - * tweaked to adjust hue and saturation. - * - * Order: v-red, v-green, v-blue, u-red, u-green, u-blue - * - * They are nine-bit signed quantities, with the sign bit - * stored in 0x58. Sign for v-red is bit 0, and up from there. - */ -#define REG_CMATRIX_BASE 0x4f -#define CMATRIX_LEN 6 -#define REG_CMATRIX_SIGN 0x58 - - -#define REG_BRIGHT 0x55 /* Brightness */ -#define REG_CONTRAS 0x56 /* Contrast control */ - -#define REG_GFIX 0x69 /* Fix gain control */ - -#define REG_REG76 0x76 /* OV's name */ -#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ -#define R76_WHTPCOR 0x40 /* White pixel correction enable */ - -#define REG_RGB444 0x8c /* RGB 444 control */ -#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ -#define R444_RGBX 0x01 /* Empty nibble at end */ - -#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ -#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ - -#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ -#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ -#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ -#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ -#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ -#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ -#define REG_BD60MAX 0xab /* 60hz banding step limit */ - - -/* - * Information we maintain about a known sensor. - */ -struct ov7670_format_struct; /* coming later */ -struct ov7670_info { - struct v4l2_subdev sd; - struct ov7670_format_struct *fmt; /* Current format */ - unsigned char sat; /* Saturation value */ - int hue; /* Hue value */ - int min_width; /* Filter out smaller sizes */ - int min_height; /* Filter out smaller sizes */ - int clock_speed; /* External clock speed (MHz) */ - u8 clkrc; /* Clock divider value */ - bool use_smbus; /* Use smbus I/O instead of I2C */ -}; - -static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ov7670_info, sd); -} - - - -/* - * The default register settings, as obtained from OmniVision. There - * is really no making sense of most of these - lots of "reserved" values - * and such. - * - * These settings give VGA YUYV. - */ - -struct regval_list { - unsigned char reg_num; - unsigned char value; -}; - -static struct regval_list ov7670_default_regs[] = { - { REG_COM7, COM7_RESET }, -/* - * Clock scale: 3 = 15fps - * 2 = 20fps - * 1 = 30fps - */ - { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ - { REG_TSLB, 0x04 }, /* OV */ - { REG_COM7, 0 }, /* VGA */ - /* - * Set the hardware window. These values from OV don't entirely - * make sense - hstop is less than hstart. But they work... - */ - { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, - { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, - { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, - - { REG_COM3, 0 }, { REG_COM14, 0 }, - /* Mystery scaling numbers */ - { 0x70, 0x3a }, { 0x71, 0x35 }, - { 0x72, 0x11 }, { 0x73, 0xf0 }, - { 0xa2, 0x02 }, { REG_COM10, 0x0 }, - - /* Gamma curve values */ - { 0x7a, 0x20 }, { 0x7b, 0x10 }, - { 0x7c, 0x1e }, { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, - - /* AGC and AEC parameters. Note we start by disabling those features, - then turn them only after tweaking the values. */ - { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, - { REG_GAIN, 0 }, { REG_AECH, 0 }, - { REG_COM4, 0x40 }, /* magic reserved bit */ - { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ - { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, - { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, - { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, - { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ - { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, - { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, - { REG_HAECC7, 0x94 }, - { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, - - /* Almost all of these are magic "reserved" values. */ - { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, - { 0x16, 0x02 }, { REG_MVFP, 0x07 }, - { 0x21, 0x02 }, { 0x22, 0x91 }, - { 0x29, 0x07 }, { 0x33, 0x0b }, - { 0x35, 0x0b }, { 0x37, 0x1d }, - { 0x38, 0x71 }, { 0x39, 0x2a }, - { REG_COM12, 0x78 }, { 0x4d, 0x40 }, - { 0x4e, 0x20 }, { REG_GFIX, 0 }, - { 0x6b, 0x4a }, { 0x74, 0x10 }, - { 0x8d, 0x4f }, { 0x8e, 0 }, - { 0x8f, 0 }, { 0x90, 0 }, - { 0x91, 0 }, { 0x96, 0 }, - { 0x9a, 0 }, { 0xb0, 0x84 }, - { 0xb1, 0x0c }, { 0xb2, 0x0e }, - { 0xb3, 0x82 }, { 0xb8, 0x0a }, - - /* More reserved magic, some of which tweaks white balance */ - { 0x43, 0x0a }, { 0x44, 0xf0 }, - { 0x45, 0x34 }, { 0x46, 0x58 }, - { 0x47, 0x28 }, { 0x48, 0x3a }, - { 0x59, 0x88 }, { 0x5a, 0x88 }, - { 0x5b, 0x44 }, { 0x5c, 0x67 }, - { 0x5d, 0x49 }, { 0x5e, 0x0e }, - { 0x6c, 0x0a }, { 0x6d, 0x55 }, - { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ - { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, - { REG_RED, 0x60 }, - { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, - - /* Matrix coefficients */ - { 0x4f, 0x80 }, { 0x50, 0x80 }, - { 0x51, 0 }, { 0x52, 0x22 }, - { 0x53, 0x5e }, { 0x54, 0x80 }, - { 0x58, 0x9e }, - - { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, - { 0x75, 0x05 }, { 0x76, 0xe1 }, - { 0x4c, 0 }, { 0x77, 0x01 }, - { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, - { 0xc9, 0x60 }, { REG_COM16, 0x38 }, - { 0x56, 0x40 }, - - { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, - { 0xa4, 0x88 }, { 0x96, 0 }, - { 0x97, 0x30 }, { 0x98, 0x20 }, - { 0x99, 0x30 }, { 0x9a, 0x84 }, - { 0x9b, 0x29 }, { 0x9c, 0x03 }, - { 0x9d, 0x4c }, { 0x9e, 0x3f }, - { 0x78, 0x04 }, - - /* Extra-weird stuff. Some sort of multiplexor register */ - { 0x79, 0x01 }, { 0xc8, 0xf0 }, - { 0x79, 0x0f }, { 0xc8, 0x00 }, - { 0x79, 0x10 }, { 0xc8, 0x7e }, - { 0x79, 0x0a }, { 0xc8, 0x80 }, - { 0x79, 0x0b }, { 0xc8, 0x01 }, - { 0x79, 0x0c }, { 0xc8, 0x0f }, - { 0x79, 0x0d }, { 0xc8, 0x20 }, - { 0x79, 0x09 }, { 0xc8, 0x80 }, - { 0x79, 0x02 }, { 0xc8, 0xc0 }, - { 0x79, 0x03 }, { 0xc8, 0x40 }, - { 0x79, 0x05 }, { 0xc8, 0x30 }, - { 0x79, 0x26 }, - - { 0xff, 0xff }, /* END MARKER */ -}; - - -/* - * Here we'll try to encapsulate the changes for just the output - * video format. - * - * RGB656 and YUV422 come from OV; RGB444 is homebrewed. - * - * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. - */ - - -static struct regval_list ov7670_fmt_yuv422[] = { - { REG_COM7, 0x0 }, /* Selects YUV mode */ - { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0 }, /* CCIR601 */ - { REG_COM15, COM15_R00FF }, - { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA|COM13_UVSAT }, - { 0xff, 0xff }, -}; - -static struct regval_list ov7670_fmt_rgb565[] = { - { REG_COM7, COM7_RGB }, /* Selects RGB mode */ - { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0x0 }, /* CCIR601 */ - { REG_COM15, COM15_RGB565 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA|COM13_UVSAT }, - { 0xff, 0xff }, -}; - -static struct regval_list ov7670_fmt_rgb444[] = { - { REG_COM7, COM7_RGB }, /* Selects RGB mode */ - { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ - { REG_COM1, 0x0 }, /* CCIR601 */ - { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ - { 0xff, 0xff }, -}; - -static struct regval_list ov7670_fmt_raw[] = { - { REG_COM7, COM7_BAYER }, - { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ - { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ - { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ - { 0xff, 0xff }, -}; - - - -/* - * Low-level register I/O. - * - * Note that there are two versions of these. On the XO 1, the - * i2c controller only does SMBUS, so that's what we use. The - * ov7670 is not really an SMBUS device, though, so the communication - * is not always entirely reliable. - */ -static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, - unsigned char *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = i2c_smbus_read_byte_data(client, reg); - if (ret >= 0) { - *value = (unsigned char)ret; - ret = 0; - } - return ret; -} - - -static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = i2c_smbus_write_byte_data(client, reg, value); - - if (reg == REG_COM7 && (value & COM7_RESET)) - msleep(5); /* Wait for reset to run */ - return ret; -} - -/* - * On most platforms, we'd rather do straight i2c I/O. - */ -static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, - unsigned char *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 data = reg; - struct i2c_msg msg; - int ret; - - /* - * Send out the register address... - */ - msg.addr = client->addr; - msg.flags = 0; - msg.len = 1; - msg.buf = &data; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - printk(KERN_ERR "Error %d on register write\n", ret); - return ret; - } - /* - * ...then read back the result. - */ - msg.flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret >= 0) { - *value = data; - ret = 0; - } - return ret; -} - - -static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_msg msg; - unsigned char data[2] = { reg, value }; - int ret; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 2; - msg.buf = data; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret > 0) - ret = 0; - if (reg == REG_COM7 && (value & COM7_RESET)) - msleep(5); /* Wait for reset to run */ - return ret; -} - -static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, - unsigned char *value) -{ - struct ov7670_info *info = to_state(sd); - if (info->use_smbus) - return ov7670_read_smbus(sd, reg, value); - else - return ov7670_read_i2c(sd, reg, value); -} - -static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, - unsigned char value) -{ - struct ov7670_info *info = to_state(sd); - if (info->use_smbus) - return ov7670_write_smbus(sd, reg, value); - else - return ov7670_write_i2c(sd, reg, value); -} - -/* - * Write a list of register settings; ff/ff stops the process. - */ -static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) -{ - while (vals->reg_num != 0xff || vals->value != 0xff) { - int ret = ov7670_write(sd, vals->reg_num, vals->value); - if (ret < 0) - return ret; - vals++; - } - return 0; -} - - -/* - * Stuff that knows about the sensor. - */ -static int ov7670_reset(struct v4l2_subdev *sd, u32 val) -{ - ov7670_write(sd, REG_COM7, COM7_RESET); - msleep(1); - return 0; -} - - -static int ov7670_init(struct v4l2_subdev *sd, u32 val) -{ - return ov7670_write_array(sd, ov7670_default_regs); -} - - - -static int ov7670_detect(struct v4l2_subdev *sd) -{ - unsigned char v; - int ret; - - ret = ov7670_init(sd, 0); - if (ret < 0) - return ret; - ret = ov7670_read(sd, REG_MIDH, &v); - if (ret < 0) - return ret; - if (v != 0x7f) /* OV manuf. id. */ - return -ENODEV; - ret = ov7670_read(sd, REG_MIDL, &v); - if (ret < 0) - return ret; - if (v != 0xa2) - return -ENODEV; - /* - * OK, we know we have an OmniVision chip...but which one? - */ - ret = ov7670_read(sd, REG_PID, &v); - if (ret < 0) - return ret; - if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ - return -ENODEV; - ret = ov7670_read(sd, REG_VER, &v); - if (ret < 0) - return ret; - if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ - return -ENODEV; - return 0; -} - - -/* - * Store information about the video data format. The color matrix - * is deeply tied into the format, so keep the relevant values here. - * The magic matrix numbers come from OmniVision. - */ -static struct ov7670_format_struct { - enum v4l2_mbus_pixelcode mbus_code; - enum v4l2_colorspace colorspace; - struct regval_list *regs; - int cmatrix[CMATRIX_LEN]; -} ov7670_formats[] = { - { - .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .regs = ov7670_fmt_yuv422, - .cmatrix = { 128, -128, 0, -34, -94, 128 }, - }, - { - .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .regs = ov7670_fmt_rgb444, - .cmatrix = { 179, -179, 0, -61, -176, 228 }, - }, - { - .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .regs = ov7670_fmt_rgb565, - .cmatrix = { 179, -179, 0, -61, -176, 228 }, - }, - { - .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .regs = ov7670_fmt_raw, - .cmatrix = { 0, 0, 0, 0, 0, 0 }, - }, -}; -#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) - - -/* - * Then there is the issue of window sizes. Try to capture the info here. - */ - -/* - * QCIF mode is done (by OV) in a very strange way - it actually looks like - * VGA with weird scaling options - they do *not* use the canned QCIF mode - * which is allegedly provided by the sensor. So here's the weird register - * settings. - */ -static struct regval_list ov7670_qcif_regs[] = { - { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, - { REG_COM3, COM3_DCWEN }, - { REG_COM14, COM14_DCWEN | 0x01}, - { 0x73, 0xf1 }, - { 0xa2, 0x52 }, - { 0x7b, 0x1c }, - { 0x7c, 0x28 }, - { 0x7d, 0x3c }, - { 0x7f, 0x69 }, - { REG_COM9, 0x38 }, - { 0xa1, 0x0b }, - { 0x74, 0x19 }, - { 0x9a, 0x80 }, - { 0x43, 0x14 }, - { REG_COM13, 0xc0 }, - { 0xff, 0xff }, -}; - -static struct ov7670_win_size { - int width; - int height; - unsigned char com7_bit; - int hstart; /* Start/stop values for the camera. Note */ - int hstop; /* that they do not always make complete */ - int vstart; /* sense to humans, but evidently the sensor */ - int vstop; /* will do the right thing... */ - struct regval_list *regs; /* Regs to tweak */ -/* h/vref stuff */ -} ov7670_win_sizes[] = { - /* VGA */ - { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .com7_bit = COM7_FMT_VGA, - .hstart = 158, /* These values from */ - .hstop = 14, /* Omnivision */ - .vstart = 10, - .vstop = 490, - .regs = NULL, - }, - /* CIF */ - { - .width = CIF_WIDTH, - .height = CIF_HEIGHT, - .com7_bit = COM7_FMT_CIF, - .hstart = 170, /* Empirically determined */ - .hstop = 90, - .vstart = 14, - .vstop = 494, - .regs = NULL, - }, - /* QVGA */ - { - .width = QVGA_WIDTH, - .height = QVGA_HEIGHT, - .com7_bit = COM7_FMT_QVGA, - .hstart = 168, /* Empirically determined */ - .hstop = 24, - .vstart = 12, - .vstop = 492, - .regs = NULL, - }, - /* QCIF */ - { - .width = QCIF_WIDTH, - .height = QCIF_HEIGHT, - .com7_bit = COM7_FMT_VGA, /* see comment above */ - .hstart = 456, /* Empirically determined */ - .hstop = 24, - .vstart = 14, - .vstop = 494, - .regs = ov7670_qcif_regs, - }, -}; - -#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) - - -/* - * Store a set of start/stop values into the camera. - */ -static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, - int vstart, int vstop) -{ - int ret; - unsigned char v; -/* - * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of - * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is - * a mystery "edge offset" value in the top two bits of href. - */ - ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); - ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); - ret += ov7670_read(sd, REG_HREF, &v); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); - msleep(10); - ret += ov7670_write(sd, REG_HREF, v); -/* - * Vertical: similar arrangement, but only 10 bits. - */ - ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); - ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); - ret += ov7670_read(sd, REG_VREF, &v); - v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); - msleep(10); - ret += ov7670_write(sd, REG_VREF, v); - return ret; -} - - -static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= N_OV7670_FMTS) - return -EINVAL; - - *code = ov7670_formats[index].mbus_code; - return 0; -} - -static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt, - struct ov7670_format_struct **ret_fmt, - struct ov7670_win_size **ret_wsize) -{ - int index; - struct ov7670_win_size *wsize; - - for (index = 0; index < N_OV7670_FMTS; index++) - if (ov7670_formats[index].mbus_code == fmt->code) - break; - if (index >= N_OV7670_FMTS) { - /* default to first format */ - index = 0; - fmt->code = ov7670_formats[0].mbus_code; - } - if (ret_fmt != NULL) - *ret_fmt = ov7670_formats + index; - /* - * Fields: the OV devices claim to be progressive. - */ - fmt->field = V4L2_FIELD_NONE; - /* - * Round requested image size down to the nearest - * we support, but not below the smallest. - */ - for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; - wsize++) - if (fmt->width >= wsize->width && fmt->height >= wsize->height) - break; - if (wsize >= ov7670_win_sizes + N_WIN_SIZES) - wsize--; /* Take the smallest one */ - if (ret_wsize != NULL) - *ret_wsize = wsize; - /* - * Note the size we'll actually handle. - */ - fmt->width = wsize->width; - fmt->height = wsize->height; - fmt->colorspace = ov7670_formats[index].colorspace; - return 0; -} - -static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); -} - -/* - * Set a format. - */ -static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct ov7670_format_struct *ovfmt; - struct ov7670_win_size *wsize; - struct ov7670_info *info = to_state(sd); - unsigned char com7; - int ret; - - ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); - - if (ret) - return ret; - /* - * COM7 is a pain in the ass, it doesn't like to be read then - * quickly written afterward. But we have everything we need - * to set it absolutely here, as long as the format-specific - * register sets list it first. - */ - com7 = ovfmt->regs[0].value; - com7 |= wsize->com7_bit; - ov7670_write(sd, REG_COM7, com7); - /* - * Now write the rest of the array. Also store start/stops - */ - ov7670_write_array(sd, ovfmt->regs + 1); - ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, - wsize->vstop); - ret = 0; - if (wsize->regs) - ret = ov7670_write_array(sd, wsize->regs); - info->fmt = ovfmt; - - /* - * If we're running RGB565, we must rewrite clkrc after setting - * the other parameters or the image looks poor. If we're *not* - * doing RGB565, we must not rewrite clkrc or the image looks - * *really* poor. - * - * (Update) Now that we retain clkrc state, we should be able - * to write it unconditionally, and that will make the frame - * rate persistent too. - */ - if (ret == 0) - ret = ov7670_write(sd, REG_CLKRC, info->clkrc); - return 0; -} - -/* - * Implement G/S_PARM. There is a "high quality" mode we could try - * to do someday; for now, we just do the frame rate tweak. - */ -static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - struct ov7670_info *info = to_state(sd); - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(struct v4l2_captureparm)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = info->clock_speed; - if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) - cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); - return 0; -} - -static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - struct ov7670_info *info = to_state(sd); - int div; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; - - if (tpf->numerator == 0 || tpf->denominator == 0) - div = 1; /* Reset to full rate */ - else - div = (tpf->numerator * info->clock_speed) / tpf->denominator; - if (div == 0) - div = 1; - else if (div > CLK_SCALE) - div = CLK_SCALE; - info->clkrc = (info->clkrc & 0x80) | div; - tpf->numerator = 1; - tpf->denominator = info->clock_speed / div; - return ov7670_write(sd, REG_CLKRC, info->clkrc); -} - - -/* - * Frame intervals. Since frame rates are controlled with the clock - * divider, we can only do 30/n for integer n values. So no continuous - * or stepwise options. Here we just pick a handful of logical values. - */ - -static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; - -static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, - struct v4l2_frmivalenum *interval) -{ - if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) - return -EINVAL; - interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; - interval->discrete.numerator = 1; - interval->discrete.denominator = ov7670_frame_rates[interval->index]; - return 0; -} - -/* - * Frame size enumeration - */ -static int ov7670_enum_framesizes(struct v4l2_subdev *sd, - struct v4l2_frmsizeenum *fsize) -{ - struct ov7670_info *info = to_state(sd); - int i; - int num_valid = -1; - __u32 index = fsize->index; - - /* - * If a minimum width/height was requested, filter out the capture - * windows that fall outside that. - */ - for (i = 0; i < N_WIN_SIZES; i++) { - struct ov7670_win_size *win = &ov7670_win_sizes[index]; - if (info->min_width && win->width < info->min_width) - continue; - if (info->min_height && win->height < info->min_height) - continue; - if (index == ++num_valid) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = win->width; - fsize->discrete.height = win->height; - return 0; - } - } - - return -EINVAL; -} - -/* - * Code for dealing with controls. - */ - -static int ov7670_store_cmatrix(struct v4l2_subdev *sd, - int matrix[CMATRIX_LEN]) -{ - int i, ret; - unsigned char signbits = 0; - - /* - * Weird crap seems to exist in the upper part of - * the sign bits register, so let's preserve it. - */ - ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); - signbits &= 0xc0; - - for (i = 0; i < CMATRIX_LEN; i++) { - unsigned char raw; - - if (matrix[i] < 0) { - signbits |= (1 << i); - if (matrix[i] < -255) - raw = 0xff; - else - raw = (-1 * matrix[i]) & 0xff; - } - else { - if (matrix[i] > 255) - raw = 0xff; - else - raw = matrix[i] & 0xff; - } - ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); - } - ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); - return ret; -} - - -/* - * Hue also requires messing with the color matrix. It also requires - * trig functions, which tend not to be well supported in the kernel. - * So here is a simple table of sine values, 0-90 degrees, in steps - * of five degrees. Values are multiplied by 1000. - * - * The following naive approximate trig functions require an argument - * carefully limited to -180 <= theta <= 180. - */ -#define SIN_STEP 5 -static const int ov7670_sin_table[] = { - 0, 87, 173, 258, 342, 422, - 499, 573, 642, 707, 766, 819, - 866, 906, 939, 965, 984, 996, - 1000 -}; - -static int ov7670_sine(int theta) -{ - int chs = 1; - int sine; - - if (theta < 0) { - theta = -theta; - chs = -1; - } - if (theta <= 90) - sine = ov7670_sin_table[theta/SIN_STEP]; - else { - theta -= 90; - sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; - } - return sine*chs; -} - -static int ov7670_cosine(int theta) -{ - theta = 90 - theta; - if (theta > 180) - theta -= 360; - else if (theta < -180) - theta += 360; - return ov7670_sine(theta); -} - - - - -static void ov7670_calc_cmatrix(struct ov7670_info *info, - int matrix[CMATRIX_LEN]) -{ - int i; - /* - * Apply the current saturation setting first. - */ - for (i = 0; i < CMATRIX_LEN; i++) - matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; - /* - * Then, if need be, rotate the hue value. - */ - if (info->hue != 0) { - int sinth, costh, tmpmatrix[CMATRIX_LEN]; - - memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); - sinth = ov7670_sine(info->hue); - costh = ov7670_cosine(info->hue); - - matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; - matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; - matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; - matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; - matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; - matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; - } -} - - - -static int ov7670_s_sat(struct v4l2_subdev *sd, int value) -{ - struct ov7670_info *info = to_state(sd); - int matrix[CMATRIX_LEN]; - int ret; - - info->sat = value; - ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(sd, matrix); - return ret; -} - -static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->sat; - return 0; -} - -static int ov7670_s_hue(struct v4l2_subdev *sd, int value) -{ - struct ov7670_info *info = to_state(sd); - int matrix[CMATRIX_LEN]; - int ret; - - if (value < -180 || value > 180) - return -EINVAL; - info->hue = value; - ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(sd, matrix); - return ret; -} - - -static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->hue; - return 0; -} - - -/* - * Some weird registers seem to store values in a sign/magnitude format! - */ -static unsigned char ov7670_sm_to_abs(unsigned char v) -{ - if ((v & 0x80) == 0) - return v + 128; - return 128 - (v & 0x7f); -} - - -static unsigned char ov7670_abs_to_sm(unsigned char v) -{ - if (v > 127) - return v & 0x7f; - return (128 - v) | 0x80; -} - -static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) -{ - unsigned char com8 = 0, v; - int ret; - - ov7670_read(sd, REG_COM8, &com8); - com8 &= ~COM8_AEC; - ov7670_write(sd, REG_COM8, com8); - v = ov7670_abs_to_sm(value); - ret = ov7670_write(sd, REG_BRIGHT, v); - return ret; -} - -static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_BRIGHT, &v); - - *value = ov7670_sm_to_abs(v); - return ret; -} - -static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) -{ - return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); -} - -static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_CONTRAS, &v); - - *value = v; - return ret; -} - -static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_MIRROR) == MVFP_MIRROR; - return ret; -} - - -static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) -{ - unsigned char v = 0; - int ret; - - ret = ov7670_read(sd, REG_MVFP, &v); - if (value) - v |= MVFP_MIRROR; - else - v &= ~MVFP_MIRROR; - msleep(10); /* FIXME */ - ret += ov7670_write(sd, REG_MVFP, v); - return ret; -} - - - -static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_FLIP) == MVFP_FLIP; - return ret; -} - - -static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) -{ - unsigned char v = 0; - int ret; - - ret = ov7670_read(sd, REG_MVFP, &v); - if (value) - v |= MVFP_FLIP; - else - v &= ~MVFP_FLIP; - msleep(10); /* FIXME */ - ret += ov7670_write(sd, REG_MVFP, v); - return ret; -} - -/* - * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes - * the data sheet, the VREF parts should be the most significant, but - * experience shows otherwise. There seems to be little value in - * messing with the VREF bits, so we leave them alone. - */ -static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char gain; - - ret = ov7670_read(sd, REG_GAIN, &gain); - *value = gain; - return ret; -} - -static int ov7670_s_gain(struct v4l2_subdev *sd, int value) -{ - int ret; - unsigned char com8; - - ret = ov7670_write(sd, REG_GAIN, value & 0xff); - /* Have to turn off AGC as well */ - if (ret == 0) { - ret = ov7670_read(sd, REG_COM8, &com8); - ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); - } - return ret; -} - -/* - * Tweak autogain. - */ -static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - *value = (com8 & COM8_AGC) != 0; - return ret; -} - -static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (ret == 0) { - if (value) - com8 |= COM8_AGC; - else - com8 &= ~COM8_AGC; - ret = ov7670_write(sd, REG_COM8, com8); - } - return ret; -} - -/* - * Exposure is spread all over the place: top 6 bits in AECHH, middle - * 8 in AECH, and two stashed in COM1 just for the hell of it. - */ -static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com1, aech, aechh; - - ret = ov7670_read(sd, REG_COM1, &com1) + - ov7670_read(sd, REG_AECH, &aech) + - ov7670_read(sd, REG_AECHH, &aechh); - *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); - return ret; -} - -static int ov7670_s_exp(struct v4l2_subdev *sd, int value) -{ - int ret; - unsigned char com1, com8, aech, aechh; - - ret = ov7670_read(sd, REG_COM1, &com1) + - ov7670_read(sd, REG_COM8, &com8); - ov7670_read(sd, REG_AECHH, &aechh); - if (ret) - return ret; - - com1 = (com1 & 0xfc) | (value & 0x03); - aech = (value >> 2) & 0xff; - aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); - ret = ov7670_write(sd, REG_COM1, com1) + - ov7670_write(sd, REG_AECH, aech) + - ov7670_write(sd, REG_AECHH, aechh); - /* Have to turn off AEC as well */ - if (ret == 0) - ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); - return ret; -} - -/* - * Tweak autoexposure. - */ -static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (com8 & COM8_AEC) - *atype = V4L2_EXPOSURE_AUTO; - else - *atype = V4L2_EXPOSURE_MANUAL; - return ret; -} - -static int ov7670_s_autoexp(struct v4l2_subdev *sd, - enum v4l2_exposure_auto_type value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (ret == 0) { - if (value == V4L2_EXPOSURE_AUTO) - com8 |= COM8_AEC; - else - com8 &= ~COM8_AEC; - ret = ov7670_write(sd, REG_COM8, com8); - } - return ret; -} - - - -static int ov7670_queryctrl(struct v4l2_subdev *sd, - struct v4l2_queryctrl *qc) -{ - /* Fill in min, max, step and default value for these controls. */ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_VFLIP: - case V4L2_CID_HFLIP: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); - case V4L2_CID_GAIN: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_AUTOGAIN: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_EXPOSURE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); - case V4L2_CID_EXPOSURE_AUTO: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - } - return -EINVAL; -} - -static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return ov7670_g_brightness(sd, &ctrl->value); - case V4L2_CID_CONTRAST: - return ov7670_g_contrast(sd, &ctrl->value); - case V4L2_CID_SATURATION: - return ov7670_g_sat(sd, &ctrl->value); - case V4L2_CID_HUE: - return ov7670_g_hue(sd, &ctrl->value); - case V4L2_CID_VFLIP: - return ov7670_g_vflip(sd, &ctrl->value); - case V4L2_CID_HFLIP: - return ov7670_g_hflip(sd, &ctrl->value); - case V4L2_CID_GAIN: - return ov7670_g_gain(sd, &ctrl->value); - case V4L2_CID_AUTOGAIN: - return ov7670_g_autogain(sd, &ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_g_exp(sd, &ctrl->value); - case V4L2_CID_EXPOSURE_AUTO: - return ov7670_g_autoexp(sd, &ctrl->value); - } - return -EINVAL; -} - -static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return ov7670_s_brightness(sd, ctrl->value); - case V4L2_CID_CONTRAST: - return ov7670_s_contrast(sd, ctrl->value); - case V4L2_CID_SATURATION: - return ov7670_s_sat(sd, ctrl->value); - case V4L2_CID_HUE: - return ov7670_s_hue(sd, ctrl->value); - case V4L2_CID_VFLIP: - return ov7670_s_vflip(sd, ctrl->value); - case V4L2_CID_HFLIP: - return ov7670_s_hflip(sd, ctrl->value); - case V4L2_CID_GAIN: - return ov7670_s_gain(sd, ctrl->value); - case V4L2_CID_AUTOGAIN: - return ov7670_s_autogain(sd, ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_s_exp(sd, ctrl->value); - case V4L2_CID_EXPOSURE_AUTO: - return ov7670_s_autoexp(sd, - (enum v4l2_exposure_auto_type) ctrl->value); - } - return -EINVAL; -} - -static int ov7670_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - unsigned char val = 0; - int ret; - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - ret = ov7670_read(sd, reg->reg & 0xff, &val); - reg->val = val; - reg->size = 1; - return ret; -} - -static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops ov7670_core_ops = { - .g_chip_ident = ov7670_g_chip_ident, - .g_ctrl = ov7670_g_ctrl, - .s_ctrl = ov7670_s_ctrl, - .queryctrl = ov7670_queryctrl, - .reset = ov7670_reset, - .init = ov7670_init, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov7670_g_register, - .s_register = ov7670_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops ov7670_video_ops = { - .enum_mbus_fmt = ov7670_enum_mbus_fmt, - .try_mbus_fmt = ov7670_try_mbus_fmt, - .s_mbus_fmt = ov7670_s_mbus_fmt, - .s_parm = ov7670_s_parm, - .g_parm = ov7670_g_parm, - .enum_frameintervals = ov7670_enum_frameintervals, - .enum_framesizes = ov7670_enum_framesizes, -}; - -static const struct v4l2_subdev_ops ov7670_ops = { - .core = &ov7670_core_ops, - .video = &ov7670_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int ov7670_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - struct ov7670_info *info; - int ret; - - info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); - if (info == NULL) - return -ENOMEM; - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &ov7670_ops); - - info->clock_speed = 30; /* default: a guess */ - if (client->dev.platform_data) { - struct ov7670_config *config = client->dev.platform_data; - - /* - * Must apply configuration before initializing device, because it - * selects I/O method. - */ - info->min_width = config->min_width; - info->min_height = config->min_height; - info->use_smbus = config->use_smbus; - - if (config->clock_speed) - info->clock_speed = config->clock_speed; - } - - /* Make sure it's an ov7670 */ - ret = ov7670_detect(sd); - if (ret) { - v4l_dbg(1, debug, client, - "chip found @ 0x%x (%s) is not an ov7670 chip.\n", - client->addr << 1, client->adapter->name); - kfree(info); - return ret; - } - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - info->fmt = &ov7670_formats[0]; - info->sat = 128; /* Review this */ - info->clkrc = info->clock_speed / 30; - return 0; -} - - -static int ov7670_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id ov7670_id[] = { - { "ov7670", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov7670_id); - -static struct i2c_driver ov7670_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "ov7670", - }, - .probe = ov7670_probe, - .remove = ov7670_remove, - .id_table = ov7670_id, -}; - -module_i2c_driver(ov7670_driver); diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c deleted file mode 100644 index 045ca7f4f6ca..000000000000 --- a/drivers/media/video/s5k6aa.c +++ /dev/null @@ -1,1667 +0,0 @@ -/* - * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor - * with embedded SoC ISP. - * - * Copyright (C) 2011, Samsung Electronics Co., Ltd. - * Sylwester Nawrocki - * - * Based on a driver authored by Dongsoo Nathaniel Kim. - * Copyright (C) 2009, Dongsoo Nathaniel Kim - * - * This program is free software; 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static int debug; -module_param(debug, int, 0644); - -#define DRIVER_NAME "S5K6AA" - -/* The token to indicate array termination */ -#define S5K6AA_TERM 0xffff -#define S5K6AA_OUT_WIDTH_DEF 640 -#define S5K6AA_OUT_HEIGHT_DEF 480 -#define S5K6AA_WIN_WIDTH_MAX 1280 -#define S5K6AA_WIN_HEIGHT_MAX 1024 -#define S5K6AA_WIN_WIDTH_MIN 8 -#define S5K6AA_WIN_HEIGHT_MIN 8 - -/* - * H/W register Interface (0xD0000000 - 0xD0000FFF) - */ -#define AHB_MSB_ADDR_PTR 0xfcfc -#define GEN_REG_OFFSH 0xd000 -#define REG_CMDWR_ADDRH 0x0028 -#define REG_CMDWR_ADDRL 0x002a -#define REG_CMDRD_ADDRH 0x002c -#define REG_CMDRD_ADDRL 0x002e -#define REG_CMDBUF0_ADDR 0x0f12 -#define REG_CMDBUF1_ADDR 0x0f10 - -/* - * Host S/W Register interface (0x70000000 - 0x70002000) - * The value of the two most significant address bytes is 0x7000, - * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. - */ -#define HOST_SWIF_OFFSH 0x7000 - -/* Initialization parameters */ -/* Master clock frequency in KHz */ -#define REG_I_INCLK_FREQ_L 0x01b8 -#define REG_I_INCLK_FREQ_H 0x01ba -#define MIN_MCLK_FREQ_KHZ 6000U -#define MAX_MCLK_FREQ_KHZ 27000U -#define REG_I_USE_NPVI_CLOCKS 0x01c6 -#define REG_I_USE_NMIPI_CLOCKS 0x01c8 - -/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ -#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) -#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) -#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) -#define SYS_PLL_OUT_FREQ (48000000 / 4000) -#define PCLK_FREQ_MIN (24000000 / 4000) -#define PCLK_FREQ_MAX (48000000 / 4000) -#define REG_I_INIT_PARAMS_UPDATED 0x01e0 -#define REG_I_ERROR_INFO 0x01e2 - -/* General purpose parameters */ -#define REG_USER_BRIGHTNESS 0x01e4 -#define REG_USER_CONTRAST 0x01e6 -#define REG_USER_SATURATION 0x01e8 -#define REG_USER_SHARPBLUR 0x01ea - -#define REG_G_SPEC_EFFECTS 0x01ee -#define REG_G_ENABLE_PREV 0x01f0 -#define REG_G_ENABLE_PREV_CHG 0x01f2 -#define REG_G_NEW_CFG_SYNC 0x01f8 -#define REG_G_PREVZOOM_IN_WIDTH 0x020a -#define REG_G_PREVZOOM_IN_HEIGHT 0x020c -#define REG_G_PREVZOOM_IN_XOFFS 0x020e -#define REG_G_PREVZOOM_IN_YOFFS 0x0210 -#define REG_G_INPUTS_CHANGE_REQ 0x021a -#define REG_G_ACTIVE_PREV_CFG 0x021c -#define REG_G_PREV_CFG_CHG 0x021e -#define REG_G_PREV_OPEN_AFTER_CH 0x0220 -#define REG_G_PREV_CFG_ERROR 0x0222 - -/* Preview control section. n = 0...4. */ -#define PREG(n, x) ((n) * 0x26 + x) -#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) -#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) -#define REG_P_FMT(n) PREG(n, 0x0246) -#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) -#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) -#define REG_P_PVI_MASK(n) PREG(n, 0x024c) -#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) -#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) -#define FR_RATE_DYNAMIC 0 -#define FR_RATE_FIXED 1 -#define FR_RATE_FIXED_ACCURATE 2 -#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) -#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ -#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ -/* Frame period in 0.1 ms units */ -#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) -#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) -/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ -#define US_TO_FR_TIME(__t) ((__t) / 100) -#define S5K6AA_MIN_FR_TIME 33300 /* us */ -#define S5K6AA_MAX_FR_TIME 650000 /* us */ -#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ -/* The below 5 registers are for "device correction" values */ -#define REG_P_COLORTEMP(n) PREG(n, 0x025e) -#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) - -/* Extended image property controls */ -/* Exposure time in 10 us units */ -#define REG_SF_USR_EXPOSURE_L 0x03c6 -#define REG_SF_USR_EXPOSURE_H 0x03c8 -#define REG_SF_USR_EXPOSURE_CHG 0x03ca -#define REG_SF_USR_TOT_GAIN 0x03cc -#define REG_SF_USR_TOT_GAIN_CHG 0x03ce -#define REG_SF_RGAIN 0x03d0 -#define REG_SF_RGAIN_CHG 0x03d2 -#define REG_SF_GGAIN 0x03d4 -#define REG_SF_GGAIN_CHG 0x03d6 -#define REG_SF_BGAIN 0x03d8 -#define REG_SF_BGAIN_CHG 0x03da -#define REG_SF_FLICKER_QUANT 0x03dc -#define REG_SF_FLICKER_QUANT_CHG 0x03de - -/* Output interface (parallel/MIPI) setup */ -#define REG_OIF_EN_MIPI_LANES 0x03fa -#define REG_OIF_EN_PACKETS 0x03fc -#define REG_OIF_CFG_CHG 0x03fe - -/* Auto-algorithms enable mask */ -#define REG_DBG_AUTOALG_EN 0x0400 -#define AALG_ALL_EN_MASK (1 << 0) -#define AALG_AE_EN_MASK (1 << 1) -#define AALG_DIVLEI_EN_MASK (1 << 2) -#define AALG_WB_EN_MASK (1 << 3) -#define AALG_FLICKER_EN_MASK (1 << 5) -#define AALG_FIT_EN_MASK (1 << 6) -#define AALG_WRHW_EN_MASK (1 << 7) - -/* Firmware revision information */ -#define REG_FW_APIVER 0x012e -#define S5K6AAFX_FW_APIVER 0x0001 -#define REG_FW_REVISION 0x0130 - -/* For now we use only one user configuration register set */ -#define S5K6AA_MAX_PRESETS 1 - -static const char * const s5k6aa_supply_names[] = { - "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ - "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ - "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) - or 2.8V (2.6V to 3.0) */ - "vddio", /* I/O supply 1.8V (1.65V to 1.95V) - or 2.8V (2.5V to 3.1V) */ -}; -#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) - -enum s5k6aa_gpio_id { - STBY, - RST, - GPIO_NUM, -}; - -struct s5k6aa_regval { - u16 addr; - u16 val; -}; - -struct s5k6aa_pixfmt { - enum v4l2_mbus_pixelcode code; - u32 colorspace; - /* REG_P_FMT(x) register value */ - u16 reg_p_fmt; -}; - -struct s5k6aa_preset { - /* output pixel format and resolution */ - struct v4l2_mbus_framefmt mbus_fmt; - u8 clk_id; - u8 index; -}; - -struct s5k6aa_ctrls { - struct v4l2_ctrl_handler handler; - /* Auto / manual white balance cluster */ - struct v4l2_ctrl *awb; - struct v4l2_ctrl *gain_red; - struct v4l2_ctrl *gain_blue; - struct v4l2_ctrl *gain_green; - /* Mirror cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - /* Auto exposure / manual exposure and gain cluster */ - struct v4l2_ctrl *auto_exp; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; -}; - -struct s5k6aa_interval { - u16 reg_fr_time; - struct v4l2_fract interval; - /* Maximum rectangle for the interval */ - struct v4l2_frmsize_discrete size; -}; - -struct s5k6aa { - struct v4l2_subdev sd; - struct media_pad pad; - - enum v4l2_mbus_type bus_type; - u8 mipi_lanes; - - int (*s_power)(int enable); - struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; - struct s5k6aa_gpio gpio[GPIO_NUM]; - - /* external master clock frequency */ - unsigned long mclk_frequency; - /* ISP internal master clock frequency */ - u16 clk_fop; - /* output pixel clock frequency range */ - u16 pclk_fmin; - u16 pclk_fmax; - - unsigned int inv_hflip:1; - unsigned int inv_vflip:1; - - /* protects the struct members below */ - struct mutex lock; - - /* sensor matrix scan window */ - struct v4l2_rect ccd_rect; - - struct s5k6aa_ctrls ctrls; - struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; - struct s5k6aa_preset *preset; - const struct s5k6aa_interval *fiv; - - unsigned int streaming:1; - unsigned int apply_cfg:1; - unsigned int apply_crop:1; - unsigned int power; -}; - -static struct s5k6aa_regval s5k6aa_analog_config[] = { - /* Analog settings */ - { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, - { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, - { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, - { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, - { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, - { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, - { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, - { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, - { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, - { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, - { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, - { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, - { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, -}; - -/* TODO: Add RGB888 and Bayer format */ -static const struct s5k6aa_pixfmt s5k6aa_formats[] = { - { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, - /* range 16-240 */ - { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, - { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, -}; - -static const struct s5k6aa_interval s5k6aa_intervals[] = { - { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ - { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ - { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ - { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ - { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ -}; - -#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ - -static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; -} - -static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) -{ - return container_of(sd, struct s5k6aa, sd); -} - -/* Set initial values for all preview presets */ -static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) -{ - struct s5k6aa_preset *preset = &s5k6aa->presets[0]; - int i; - - for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { - preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; - preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; - preset->mbus_fmt.code = s5k6aa_formats[0].code; - preset->index = i; - preset->clk_id = 0; - preset++; - } - - s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; - s5k6aa->preset = &s5k6aa->presets[0]; -} - -static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) -{ - u8 wbuf[2] = {addr >> 8, addr & 0xFF}; - struct i2c_msg msg[2]; - u8 rbuf[2]; - int ret; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 2; - msg[0].buf = wbuf; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 2; - msg[1].buf = rbuf; - - ret = i2c_transfer(client->adapter, msg, 2); - *val = be16_to_cpu(*((u16 *)rbuf)); - - v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); - - return ret == 2 ? 0 : ret; -} - -static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) -{ - u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; - - int ret = i2c_master_send(client, buf, 4); - v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); - - return ret == 4 ? 0 : ret; -} - -/* The command register write, assumes Command_Wr_addH = 0x7000. */ -static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) -{ - int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); - if (ret) - return ret; - return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); -} - -/* The command register read, assumes Command_Rd_addH = 0x7000. */ -static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) -{ - int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); - if (ret) - return ret; - return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); -} - -static int s5k6aa_write_array(struct v4l2_subdev *sd, - const struct s5k6aa_regval *msg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u16 addr_incr = 0; - int ret = 0; - - while (msg->addr != S5K6AA_TERM) { - if (addr_incr != 2) - ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, - msg->addr); - if (ret) - break; - ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); - if (ret) - break; - /* Assume that msg->addr is always less than 0xfffc */ - addr_incr = (msg + 1)->addr - msg->addr; - msg++; - } - - return ret; -} - -/* Configure the AHB high address bytes for GTG registers access */ -static int s5k6aa_set_ahb_address(struct i2c_client *client) -{ - int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); - if (ret) - return ret; - ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); - if (ret) - return ret; - return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); -} - -/** - * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration - * - * Configure the internal ISP PLL for the required output frequency. - * Locking: called with s5k6aa.lock mutex held. - */ -static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - unsigned long fmclk = s5k6aa->mclk_frequency / 1000; - u16 status; - int ret; - - if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, - "Invalid clock frequency: %ld\n", fmclk)) - return -EINVAL; - - s5k6aa->pclk_fmin = PCLK_FREQ_MIN; - s5k6aa->pclk_fmax = PCLK_FREQ_MAX; - s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; - - /* External input clock frequency in kHz */ - ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); - if (!ret) - ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); - if (!ret) - ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); - /* Internal PLL frequency */ - if (!ret) - ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); - if (!ret) - ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), - s5k6aa->pclk_fmin); - if (!ret) - ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), - s5k6aa->pclk_fmax); - if (!ret) - ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); - if (!ret) - ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); - - return ret ? ret : (status ? -EINVAL : 0); -} - -/* Set horizontal and vertical image flipping */ -static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int index = s5k6aa->preset->index; - - unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; - unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); - - return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); -} - -/* Configure auto/manual white balance and R/G/B gains */ -static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; - u16 reg; - - int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); - - if (!ret && !awb) { - ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); - if (ret) - return ret; - - ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); - if (ret) - return ret; - - ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); - } - if (!ret) { - reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; - ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); - } - - return ret; -} - -/* Program FW with exposure time, 'exposure' in us units */ -static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) -{ - unsigned int time = exposure / 10; - - int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); - if (!ret) - ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); - if (ret) - return ret; - return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); -} - -static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) -{ - int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); - if (ret) - return ret; - return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); -} - -/* Set auto/manual exposure and total gain */ -static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - unsigned int exp_time = s5k6aa->ctrls.exposure->val; - u16 auto_alg; - - int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); - if (ret) - return ret; - - v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", - exp_time, value, auto_alg); - - if (value == V4L2_EXPOSURE_AUTO) { - auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; - } else { - ret = s5k6aa_set_user_exposure(c, exp_time); - if (ret) - return ret; - ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); - if (ret) - return ret; - auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); - } - - return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); -} - -static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 auto_alg; - int ret; - - ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); - if (ret) - return ret; - - if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { - auto_alg |= AALG_FLICKER_EN_MASK; - } else { - auto_alg &= ~AALG_FLICKER_EN_MASK; - /* The V4L2_CID_LINE_FREQUENCY control values match - * the register values */ - ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); - if (ret) - return ret; - ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); - if (ret) - return ret; - } - - return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); -} - -static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - static const struct v4l2_control colorfx[] = { - { V4L2_COLORFX_NONE, 0 }, - { V4L2_COLORFX_BW, 1 }, - { V4L2_COLORFX_NEGATIVE, 2 }, - { V4L2_COLORFX_SEPIA, 3 }, - { V4L2_COLORFX_SKY_BLUE, 4 }, - { V4L2_COLORFX_SKETCH, 5 }, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(colorfx); i++) { - if (colorfx[i].id == val) - return s5k6aa_write(client, REG_G_SPEC_EFFECTS, - colorfx[i].value); - } - return -EINVAL; -} - -static int s5k6aa_preview_config_status(struct i2c_client *client) -{ - u16 error = 0; - int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); - - v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); - return ret ? ret : (error ? -EINVAL : 0); -} - -static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, - struct v4l2_mbus_framefmt *mf) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) - if (mf->colorspace == s5k6aa_formats[i].colorspace && - mf->code == s5k6aa_formats[i].code) - return i; - return 0; -} - -static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, - struct s5k6aa_preset *preset) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); - int ret; - - ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), - preset->mbus_fmt.width); - if (!ret) - ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), - preset->mbus_fmt.height); - if (!ret) - ret = s5k6aa_write(client, REG_P_FMT(preset->index), - s5k6aa_formats[fmt_index].reg_p_fmt); - return ret; -} - -static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - struct v4l2_rect *r = &s5k6aa->ccd_rect; - int ret; - - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); - if (!ret) - ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); - if (!ret) - s5k6aa->apply_crop = 0; - - return ret; -} - -/** - * s5k6aa_configure_video_bus - configure the video output interface - * @bus_type: video bus type: parallel or MIPI-CSI - * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) - * - * Note: Only parallel bus operation has been tested. - */ -static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, - enum v4l2_mbus_type bus_type, int nlanes) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 cfg = 0; - int ret; - - /* - * TODO: The sensor is supposed to support BT.601 and BT.656 - * but there is nothing indicating how to switch between both - * in the datasheet. For now default BT.601 interface is assumed. - */ - if (bus_type == V4L2_MBUS_CSI2) - cfg = nlanes; - else if (bus_type != V4L2_MBUS_PARALLEL) - return -EINVAL; - - ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); - if (ret) - return ret; - return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); -} - -/* This function should be called when switching to new user configuration set*/ -static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, - int cid) -{ - unsigned long end = jiffies + msecs_to_jiffies(timeout); - u16 reg = 1; - int ret; - - ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); - if (!ret) - ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - if (!ret) - ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); - if (timeout == 0) - return ret; - - while (ret >= 0 && time_is_after_jiffies(end)) { - ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); - if (!reg) - return 0; - usleep_range(1000, 5000); - } - return ret ? ret : -ETIMEDOUT; -} - -/** - * s5k6aa_set_prev_config - write user preview register set - * - * Configure output resolution and color fromat, pixel clock - * frequency range, device frame rate type and frame period range. - */ -static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, - struct s5k6aa_preset *preset) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int idx = preset->index; - u16 frame_rate_q; - int ret; - - if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) - frame_rate_q = FR_RATE_Q_BEST_FRRATE; - else - frame_rate_q = FR_RATE_Q_BEST_QUALITY; - - ret = s5k6aa_set_output_framefmt(s5k6aa, preset); - if (!ret) - ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), - s5k6aa->pclk_fmax); - if (!ret) - ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), - s5k6aa->pclk_fmin); - if (!ret) - ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), - preset->clk_id); - if (!ret) - ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), - FR_RATE_DYNAMIC); - if (!ret) - ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), - frame_rate_q); - if (!ret) - ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), - s5k6aa->fiv->reg_fr_time + 33); - if (!ret) - ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), - s5k6aa->fiv->reg_fr_time - 33); - if (!ret) - ret = s5k6aa_new_config_sync(client, 250, idx); - if (!ret) - ret = s5k6aa_preview_config_status(client); - if (!ret) - s5k6aa->apply_cfg = 0; - - v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", - s5k6aa->fiv->reg_fr_time, ret); - return ret; -} - -/** - * s5k6aa_initialize_isp - basic ISP MCU initialization - * - * Configure AHB addresses for registers read/write; configure PLLs for - * required output pixel clock. The ISP power supply needs to be already - * enabled, with an optional H/W reset. - * Locking: called with s5k6aa.lock mutex held. - */ -static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - s5k6aa->apply_crop = 1; - s5k6aa->apply_cfg = 1; - msleep(100); - - ret = s5k6aa_set_ahb_address(client); - if (ret) - return ret; - ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, - s5k6aa->mipi_lanes); - if (ret) - return ret; - ret = s5k6aa_write_array(sd, s5k6aa_analog_config); - if (ret) - return ret; - msleep(20); - - return s5k6aa_configure_pixel_clocks(s5k6aa); -} - -static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) -{ - if (!gpio_is_valid(priv->gpio[id].gpio)) - return 0; - gpio_set_value(priv->gpio[id].gpio, !!val); - return 1; -} - -static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) -{ - return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); -} - -static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) -{ - return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); -} - -static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) -{ - int ret; - - ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); - if (ret) - return ret; - if (s5k6aa_gpio_deassert(s5k6aa, STBY)) - usleep_range(150, 200); - - if (s5k6aa->s_power) - ret = s5k6aa->s_power(1); - usleep_range(4000, 4000); - - if (s5k6aa_gpio_deassert(s5k6aa, RST)) - msleep(20); - - return ret; -} - -static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) -{ - int ret; - - if (s5k6aa_gpio_assert(s5k6aa, RST)) - usleep_range(100, 150); - - if (s5k6aa->s_power) { - ret = s5k6aa->s_power(0); - if (ret) - return ret; - } - if (s5k6aa_gpio_assert(s5k6aa, STBY)) - usleep_range(50, 100); - s5k6aa->streaming = 0; - - return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); -} - -/* - * V4L2 subdev core and video operations - */ -static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret = 0; - - mutex_lock(&s5k6aa->lock); - - if (!on == s5k6aa->power) { - if (on) { - ret = __s5k6aa_power_on(s5k6aa); - if (!ret) - ret = s5k6aa_initialize_isp(sd); - } else { - ret = __s5k6aa_power_off(s5k6aa); - } - - if (!ret) - s5k6aa->power += on ? 1 : -1; - } - - mutex_unlock(&s5k6aa->lock); - - if (!on || ret || s5k6aa->power != 1) - return ret; - - return v4l2_ctrl_handler_setup(sd->ctrl_handler); -} - -static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int ret = 0; - - ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); - if (!ret) - ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); - if (!ret) - s5k6aa->streaming = enable; - - return ret; -} - -static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret = 0; - - mutex_lock(&s5k6aa->lock); - - if (s5k6aa->streaming == !on) { - if (!ret && s5k6aa->apply_cfg) - ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); - if (s5k6aa->apply_crop) - ret = s5k6aa_set_input_params(s5k6aa); - if (!ret) - ret = __s5k6aa_stream(s5k6aa, !!on); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - - mutex_lock(&s5k6aa->lock); - fi->interval = s5k6aa->fiv->interval; - mutex_unlock(&s5k6aa->lock); - - return 0; -} - -static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, - struct v4l2_subdev_frame_interval *fi) -{ - struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; - const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; - unsigned int err, min_err = UINT_MAX; - unsigned int i, fr_time; - - if (fi->interval.denominator == 0) - return -EINVAL; - - fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; - - for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { - const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; - - if (mbus_fmt->width > iv->size.width || - mbus_fmt->height > iv->size.height) - continue; - - err = abs(iv->reg_fr_time - fr_time); - if (err < min_err) { - fiv = iv; - min_err = err; - } - } - s5k6aa->fiv = fiv; - - v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", - fiv->reg_fr_time * 100); - return 0; -} - -static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", - fi->interval.numerator, fi->interval.denominator); - - mutex_lock(&s5k6aa->lock); - ret = __s5k6aa_set_frame_interval(s5k6aa, fi); - s5k6aa->apply_cfg = 1; - - mutex_unlock(&s5k6aa->lock); - return ret; -} - -/* - * V4L2 subdev pad level and video operations - */ -static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_interval_enum *fie) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - const struct s5k6aa_interval *fi; - int ret = 0; - - if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) - return -EINVAL; - - v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, - S5K6AA_WIN_WIDTH_MAX, 1, - &fie->height, S5K6AA_WIN_HEIGHT_MIN, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - mutex_lock(&s5k6aa->lock); - fi = &s5k6aa_intervals[fie->index]; - if (fie->width > fi->size.width || fie->height > fi->size.height) - ret = -EINVAL; - else - fie->interval = fi->interval; - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(s5k6aa_formats)) - return -EINVAL; - - code->code = s5k6aa_formats[code->index].code; - return 0; -} - -static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_frame_size_enum *fse) -{ - int i = ARRAY_SIZE(s5k6aa_formats); - - if (fse->index > 0) - return -EINVAL; - - while (--i) - if (fse->code == s5k6aa_formats[i].code) - break; - - fse->code = s5k6aa_formats[i].code; - fse->min_width = S5K6AA_WIN_WIDTH_MIN; - fse->max_width = S5K6AA_WIN_WIDTH_MAX; - fse->max_height = S5K6AA_WIN_HEIGHT_MIN; - fse->min_height = S5K6AA_WIN_HEIGHT_MAX; - - return 0; -} - -static struct v4l2_rect * -__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - return &s5k6aa->ccd_rect; - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, 0); - - return NULL; -} - -static void s5k6aa_try_format(struct s5k6aa *s5k6aa, - struct v4l2_mbus_framefmt *mf) -{ - unsigned int index; - - v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, - S5K6AA_WIN_WIDTH_MAX, 1, - &mf->height, S5K6AA_WIN_HEIGHT_MIN, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - if (mf->colorspace != V4L2_COLORSPACE_JPEG && - mf->colorspace != V4L2_COLORSPACE_REC709) - mf->colorspace = V4L2_COLORSPACE_JPEG; - - index = s5k6aa_get_pixfmt_index(s5k6aa, mf); - - mf->colorspace = s5k6aa_formats[index].colorspace; - mf->code = s5k6aa_formats[index].code; - mf->field = V4L2_FIELD_NONE; -} - -static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_mbus_framefmt *mf; - - memset(fmt->reserved, 0, sizeof(fmt->reserved)); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, 0); - fmt->format = *mf; - return 0; - } - - mutex_lock(&s5k6aa->lock); - fmt->format = s5k6aa->preset->mbus_fmt; - mutex_unlock(&s5k6aa->lock); - - return 0; -} - -static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct s5k6aa_preset *preset = s5k6aa->preset; - struct v4l2_mbus_framefmt *mf; - struct v4l2_rect *crop; - int ret = 0; - - mutex_lock(&s5k6aa->lock); - s5k6aa_try_format(s5k6aa, &fmt->format); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); - crop = v4l2_subdev_get_try_crop(fh, 0); - } else { - if (s5k6aa->streaming) { - ret = -EBUSY; - } else { - mf = &preset->mbus_fmt; - crop = &s5k6aa->ccd_rect; - s5k6aa->apply_cfg = 1; - } - } - - if (ret == 0) { - struct v4l2_subdev_frame_interval fiv = { - .interval = {0, 1} - }; - - *mf = fmt->format; - /* - * Make sure the crop window is valid, i.e. its size is - * greater than the output window, as the ISP supports - * only down-scaling. - */ - crop->width = clamp_t(unsigned int, crop->width, mf->width, - S5K6AA_WIN_WIDTH_MAX); - crop->height = clamp_t(unsigned int, crop->height, mf->height, - S5K6AA_WIN_HEIGHT_MAX); - crop->left = clamp_t(unsigned int, crop->left, 0, - S5K6AA_WIN_WIDTH_MAX - crop->width); - crop->top = clamp_t(unsigned int, crop->top, 0, - S5K6AA_WIN_HEIGHT_MAX - crop->height); - - /* Reset to minimum possible frame interval */ - ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_rect *rect; - - memset(crop->reserved, 0, sizeof(crop->reserved)); - mutex_lock(&s5k6aa->lock); - - rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); - if (rect) - crop->rect = *rect; - - mutex_unlock(&s5k6aa->lock); - - v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", - rect->left, rect->top, rect->width, rect->height); - - return 0; -} - -static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_mbus_framefmt *mf; - unsigned int max_x, max_y; - struct v4l2_rect *crop_r; - - mutex_lock(&s5k6aa->lock); - crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); - - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - mf = &s5k6aa->preset->mbus_fmt; - s5k6aa->apply_crop = 1; - } else { - mf = v4l2_subdev_get_try_format(fh, 0); - } - v4l_bound_align_image(&crop->rect.width, mf->width, - S5K6AA_WIN_WIDTH_MAX, 1, - &crop->rect.height, mf->height, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; - max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; - - crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); - crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); - - *crop_r = crop->rect; - - mutex_unlock(&s5k6aa->lock); - - v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", - crop_r->left, crop_r->top, crop_r->width, crop_r->height); - - return 0; -} - -static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { - .enum_mbus_code = s5k6aa_enum_mbus_code, - .enum_frame_size = s5k6aa_enum_frame_size, - .enum_frame_interval = s5k6aa_enum_frame_interval, - .get_fmt = s5k6aa_get_fmt, - .set_fmt = s5k6aa_set_fmt, - .get_crop = s5k6aa_get_crop, - .set_crop = s5k6aa_set_crop, -}; - -static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { - .g_frame_interval = s5k6aa_g_frame_interval, - .s_frame_interval = s5k6aa_s_frame_interval, - .s_stream = s5k6aa_s_stream, -}; - -/* - * V4L2 subdev controls - */ - -static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int idx, err = 0; - - v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); - - mutex_lock(&s5k6aa->lock); - /* - * If the device is not powered up by the host driver do - * not apply any controls to H/W at this time. Instead - * the controls will be restored right after power-up. - */ - if (s5k6aa->power == 0) - goto unlock; - idx = s5k6aa->preset->index; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - err = s5k6aa_set_awb(s5k6aa, ctrl->val); - break; - - case V4L2_CID_BRIGHTNESS: - err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); - break; - - case V4L2_CID_COLORFX: - err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); - break; - - case V4L2_CID_CONTRAST: - err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); - break; - - case V4L2_CID_EXPOSURE_AUTO: - err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); - break; - - case V4L2_CID_HFLIP: - err = s5k6aa_set_mirror(s5k6aa, ctrl->val); - if (err) - break; - err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - break; - - case V4L2_CID_POWER_LINE_FREQUENCY: - err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); - break; - - case V4L2_CID_SATURATION: - err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); - break; - - case V4L2_CID_SHARPNESS: - err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); - break; - - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: - err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); - if (err) - break; - err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - break; - } -unlock: - mutex_unlock(&s5k6aa->lock); - return err; -} - -static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { - .s_ctrl = s5k6aa_s_ctrl, -}; - -static int s5k6aa_log_status(struct v4l2_subdev *sd) -{ - v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); - return 0; -} - -#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) -#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) -#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) - -static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { - { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_RED_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Red", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_GREEN_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_BLUE_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Blue", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, -}; - -static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) -{ - const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; - struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - int ret = v4l2_ctrl_handler_init(hdl, 16); - if (ret) - return ret; - /* Auto white balance cluster */ - ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 1); - ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); - ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); - ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); - v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); - - ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_cluster(2, &ctrls->hflip); - - ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - /* Exposure time: x 1 us */ - ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - 0, 6000000U, 1, 100000U); - /* Total gain: 256 <=> 1x */ - ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, - 0, 256, 1, 256); - v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); - - v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO); - - v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, - V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, - 0, 256, 1, 0); - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); - - if (hdl->error) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - s5k6aa->sd.ctrl_handler = hdl; - return 0; -} - -/* - * V4L2 subdev internal operations - */ -static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); - - format->colorspace = s5k6aa_formats[0].colorspace; - format->code = s5k6aa_formats[0].code; - format->width = S5K6AA_OUT_WIDTH_DEF; - format->height = S5K6AA_OUT_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - - crop->width = S5K6AA_WIN_WIDTH_MAX; - crop->height = S5K6AA_WIN_HEIGHT_MAX; - crop->left = 0; - crop->top = 0; - - return 0; -} - -static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 api_ver = 0, fw_rev = 0; - - int ret = s5k6aa_set_ahb_address(client); - - if (!ret) - ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); - if (!ret) - ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); - if (ret) { - v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); - return ret; - } - - v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", - api_ver, fw_rev); - - return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; -} - -static int s5k6aa_registered(struct v4l2_subdev *sd) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - mutex_lock(&s5k6aa->lock); - ret = __s5k6aa_power_on(s5k6aa); - if (!ret) { - msleep(100); - ret = s5k6aa_check_fw_revision(s5k6aa); - __s5k6aa_power_off(s5k6aa); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { - .registered = s5k6aa_registered, - .open = s5k6aa_open, -}; - -static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { - .s_power = s5k6aa_set_power, - .log_status = s5k6aa_log_status, -}; - -static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { - .core = &s5k6aa_core_ops, - .pad = &s5k6aa_pad_ops, - .video = &s5k6aa_video_ops, -}; - -/* - * GPIO setup - */ -static int s5k6aa_configure_gpio(int nr, int val, const char *name) -{ - unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; - int ret; - - if (!gpio_is_valid(nr)) - return 0; - ret = gpio_request_one(nr, flags, name); - if (!ret) - gpio_export(nr, 0); - return ret; -} - -static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) { - if (!gpio_is_valid(s5k6aa->gpio[i].gpio)) - continue; - gpio_free(s5k6aa->gpio[i].gpio); - s5k6aa->gpio[i].gpio = -EINVAL; - } -} - -static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, - const struct s5k6aa_platform_data *pdata) -{ - const struct s5k6aa_gpio *gpio = &pdata->gpio_stby; - int ret; - - s5k6aa->gpio[STBY].gpio = -EINVAL; - s5k6aa->gpio[RST].gpio = -EINVAL; - - ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY"); - if (ret) { - s5k6aa_free_gpios(s5k6aa); - return ret; - } - s5k6aa->gpio[STBY] = *gpio; - if (gpio_is_valid(gpio->gpio)) - gpio_set_value(gpio->gpio, 0); - - gpio = &pdata->gpio_reset; - ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST"); - if (ret) { - s5k6aa_free_gpios(s5k6aa); - return ret; - } - s5k6aa->gpio[RST] = *gpio; - if (gpio_is_valid(gpio->gpio)) - gpio_set_value(gpio->gpio, 0); - - return 0; -} - -static int s5k6aa_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - const struct s5k6aa_platform_data *pdata = client->dev.platform_data; - struct v4l2_subdev *sd; - struct s5k6aa *s5k6aa; - int i, ret; - - if (pdata == NULL) { - dev_err(&client->dev, "Platform data not specified\n"); - return -EINVAL; - } - - if (pdata->mclk_frequency == 0) { - dev_err(&client->dev, "MCLK frequency not specified\n"); - return -EINVAL; - } - - s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL); - if (!s5k6aa) - return -ENOMEM; - - mutex_init(&s5k6aa->lock); - - s5k6aa->mclk_frequency = pdata->mclk_frequency; - s5k6aa->bus_type = pdata->bus_type; - s5k6aa->mipi_lanes = pdata->nlanes; - s5k6aa->s_power = pdata->set_power; - s5k6aa->inv_hflip = pdata->horiz_flip; - s5k6aa->inv_vflip = pdata->vert_flip; - - sd = &s5k6aa->sd; - v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); - strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); - - sd->internal_ops = &s5k6aa_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); - if (ret) - return ret; - - ret = s5k6aa_configure_gpios(s5k6aa, pdata); - if (ret) - goto out_err2; - - for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) - s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; - - ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, - s5k6aa->supplies); - if (ret) { - dev_err(&client->dev, "Failed to get regulators\n"); - goto out_err3; - } - - ret = s5k6aa_initialize_ctrls(s5k6aa); - if (ret) - goto out_err4; - - s5k6aa_presets_data_init(s5k6aa); - - s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; - s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; - s5k6aa->ccd_rect.left = 0; - s5k6aa->ccd_rect.top = 0; - - return 0; - -out_err4: - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); -out_err3: - s5k6aa_free_gpios(s5k6aa); -out_err2: - media_entity_cleanup(&s5k6aa->sd.entity); - return ret; -} - -static int s5k6aa_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - media_entity_cleanup(&sd->entity); - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); - s5k6aa_free_gpios(s5k6aa); - - return 0; -} - -static const struct i2c_device_id s5k6aa_id[] = { - { DRIVER_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, s5k6aa_id); - - -static struct i2c_driver s5k6aa_i2c_driver = { - .driver = { - .name = DRIVER_NAME - }, - .probe = s5k6aa_probe, - .remove = s5k6aa_remove, - .id_table = s5k6aa_id, -}; - -module_i2c_driver(s5k6aa_i2c_driver); - -MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c deleted file mode 100644 index 0caac50d7cf4..000000000000 --- a/drivers/media/video/saa6588.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - Driver for SAA6588 RDS decoder - - (c) 2005 Hans J. Koch - - This program is free software; 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 -#include -#include - -#include -#include -#include - - -/* insmod options */ -static unsigned int debug; -static unsigned int xtal; -static unsigned int mmbs; -static unsigned int plvl; -static unsigned int bufblocks = 100; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages"); -module_param(xtal, int, 0); -MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); -module_param(mmbs, int, 0); -MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on"); -module_param(plvl, int, 0); -MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); -module_param(bufblocks, int, 0); -MODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100"); - -MODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder"); -MODULE_AUTHOR("Hans J. Koch "); - -MODULE_LICENSE("GPL"); - -/* ---------------------------------------------------------------------- */ - -#define UNSET (-1U) -#define PREFIX "saa6588: " -#define dprintk if (debug) printk - -struct saa6588 { - struct v4l2_subdev sd; - struct delayed_work work; - spinlock_t lock; - unsigned char *buffer; - unsigned int buf_size; - unsigned int rd_index; - unsigned int wr_index; - unsigned int block_count; - unsigned char last_blocknum; - wait_queue_head_t read_queue; - int data_available_for_read; - u8 sync; -}; - -static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa6588, sd); -} - -/* ---------------------------------------------------------------------- */ - -/* - * SAA6588 defines - */ - -/* Initialization and mode control byte (0w) */ - -/* bit 0+1 (DAC0/DAC1) */ -#define cModeStandard 0x00 -#define cModeFastPI 0x01 -#define cModeReducedRequest 0x02 -#define cModeInvalid 0x03 - -/* bit 2 (RBDS) */ -#define cProcessingModeRDS 0x00 -#define cProcessingModeRBDS 0x04 - -/* bit 3+4 (SYM0/SYM1) */ -#define cErrCorrectionNone 0x00 -#define cErrCorrection2Bits 0x08 -#define cErrCorrection5Bits 0x10 -#define cErrCorrectionNoneRBDS 0x18 - -/* bit 5 (NWSY) */ -#define cSyncNormal 0x00 -#define cSyncRestart 0x20 - -/* bit 6 (TSQD) */ -#define cSigQualityDetectOFF 0x00 -#define cSigQualityDetectON 0x40 - -/* bit 7 (SQCM) */ -#define cSigQualityTriggered 0x00 -#define cSigQualityContinous 0x80 - -/* Pause level and flywheel control byte (1w) */ - -/* bits 0..5 (FEB0..FEB5) */ -#define cFlywheelMaxBlocksMask 0x3F -#define cFlywheelDefault 0x20 - -/* bits 6+7 (PL0/PL1) */ -#define cPauseLevel_11mV 0x00 -#define cPauseLevel_17mV 0x40 -#define cPauseLevel_27mV 0x80 -#define cPauseLevel_43mV 0xC0 - -/* Pause time/oscillator frequency/quality detector control byte (1w) */ - -/* bits 0..4 (SQS0..SQS4) */ -#define cQualityDetectSensMask 0x1F -#define cQualityDetectDefault 0x0F - -/* bit 5 (SOSC) */ -#define cSelectOscFreqOFF 0x00 -#define cSelectOscFreqON 0x20 - -/* bit 6+7 (PTF0/PTF1) */ -#define cOscFreq_4332kHz 0x00 -#define cOscFreq_8664kHz 0x40 -#define cOscFreq_12996kHz 0x80 -#define cOscFreq_17328kHz 0xC0 - -/* ---------------------------------------------------------------------- */ - -static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) -{ - int i; - - if (s->rd_index == s->wr_index) { - if (debug > 2) - dprintk(PREFIX "Read: buffer empty.\n"); - return 0; - } - - if (debug > 2) { - dprintk(PREFIX "Read: "); - for (i = s->rd_index; i < s->rd_index + 3; i++) - dprintk("0x%02x ", s->buffer[i]); - } - - if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3)) - return -EFAULT; - - s->rd_index += 3; - if (s->rd_index >= s->buf_size) - s->rd_index = 0; - s->block_count--; - - if (debug > 2) - dprintk("%d blocks total.\n", s->block_count); - - return 1; -} - -static void read_from_buf(struct saa6588 *s, struct saa6588_command *a) -{ - unsigned long flags; - - unsigned char __user *buf_ptr = a->buffer; - unsigned int i; - unsigned int rd_blocks; - - a->result = 0; - if (!a->buffer) - return; - - while (!s->data_available_for_read) { - int ret = wait_event_interruptible(s->read_queue, - s->data_available_for_read); - if (ret == -ERESTARTSYS) { - a->result = -EINTR; - return; - } - } - - spin_lock_irqsave(&s->lock, flags); - rd_blocks = a->block_count; - if (rd_blocks > s->block_count) - rd_blocks = s->block_count; - - if (!rd_blocks) { - spin_unlock_irqrestore(&s->lock, flags); - return; - } - - for (i = 0; i < rd_blocks; i++) { - if (block_to_user_buf(s, buf_ptr)) { - buf_ptr += 3; - a->result++; - } else - break; - } - a->result *= 3; - s->data_available_for_read = (s->block_count > 0); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) -{ - unsigned int i; - - if (debug > 3) - dprintk(PREFIX "New block: "); - - for (i = 0; i < 3; ++i) { - if (debug > 3) - dprintk("0x%02x ", blockbuf[i]); - s->buffer[s->wr_index] = blockbuf[i]; - s->wr_index++; - } - - if (s->wr_index >= s->buf_size) - s->wr_index = 0; - - if (s->wr_index == s->rd_index) { - s->rd_index += 3; - if (s->rd_index >= s->buf_size) - s->rd_index = 0; - } else - s->block_count++; - - if (debug > 3) - dprintk("%d blocks total.\n", s->block_count); -} - -static void saa6588_i2c_poll(struct saa6588 *s) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s->sd); - unsigned long flags; - unsigned char tmpbuf[6]; - unsigned char blocknum; - unsigned char tmp; - - /* Although we only need 3 bytes, we have to read at least 6. - SAA6588 returns garbage otherwise. */ - if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { - if (debug > 1) - dprintk(PREFIX "read error!\n"); - return; - } - - s->sync = tmpbuf[0] & 0x10; - if (!s->sync) - return; - blocknum = tmpbuf[0] >> 5; - if (blocknum == s->last_blocknum) { - if (debug > 3) - dprintk("Saw block %d again.\n", blocknum); - return; - } - - s->last_blocknum = blocknum; - - /* - Byte order according to v4l2 specification: - - Byte 0: Least Significant Byte of RDS Block - Byte 1: Most Significant Byte of RDS Block - Byte 2 Bit 7: Error bit. Indicates that an uncorrectable error - occurred during reception of this block. - Bit 6: Corrected bit. Indicates that an error was - corrected for this data block. - Bits 5-3: Same as bits 0-2. - Bits 2-0: Block number. - - SAA6588 byte order is Status-MSB-LSB, so we have to swap the - first and the last of the 3 bytes block. - */ - - tmp = tmpbuf[2]; - tmpbuf[2] = tmpbuf[0]; - tmpbuf[0] = tmp; - - /* Map 'Invalid block E' to 'Invalid Block' */ - if (blocknum == 6) - blocknum = V4L2_RDS_BLOCK_INVALID; - /* And if are not in mmbs mode, then 'Block E' is also mapped - to 'Invalid Block'. As far as I can tell MMBS is discontinued, - and if there is ever a need to support E blocks, then please - contact the linux-media mailinglist. */ - else if (!mmbs && blocknum == 5) - blocknum = V4L2_RDS_BLOCK_INVALID; - tmp = blocknum; - tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */ - if ((tmpbuf[2] & 0x03) == 0x03) - tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */ - else if ((tmpbuf[2] & 0x03) != 0x00) - tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */ - tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */ - - spin_lock_irqsave(&s->lock, flags); - block_to_buf(s, tmpbuf); - spin_unlock_irqrestore(&s->lock, flags); - s->data_available_for_read = 1; - wake_up_interruptible(&s->read_queue); -} - -static void saa6588_work(struct work_struct *work) -{ - struct saa6588 *s = container_of(work, struct saa6588, work.work); - - saa6588_i2c_poll(s); - schedule_delayed_work(&s->work, msecs_to_jiffies(20)); -} - -static void saa6588_configure(struct saa6588 *s) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s->sd); - unsigned char buf[3]; - int rc; - - buf[0] = cSyncRestart; - if (mmbs) - buf[0] |= cProcessingModeRBDS; - - buf[1] = cFlywheelDefault; - switch (plvl) { - case 0: - buf[1] |= cPauseLevel_11mV; - break; - case 1: - buf[1] |= cPauseLevel_17mV; - break; - case 2: - buf[1] |= cPauseLevel_27mV; - break; - case 3: - buf[1] |= cPauseLevel_43mV; - break; - default: /* nothing */ - break; - } - - buf[2] = cQualityDetectDefault | cSelectOscFreqON; - - switch (xtal) { - case 0: - buf[2] |= cOscFreq_4332kHz; - break; - case 1: - buf[2] |= cOscFreq_8664kHz; - break; - case 2: - buf[2] |= cOscFreq_12996kHz; - break; - case 3: - buf[2] |= cOscFreq_17328kHz; - break; - default: /* nothing */ - break; - } - - dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", - buf[0], buf[1], buf[2]); - - rc = i2c_master_send(client, buf, 3); - if (rc != 3) - printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); -} - -/* ---------------------------------------------------------------------- */ - -static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct saa6588 *s = to_saa6588(sd); - struct saa6588_command *a = arg; - - switch (cmd) { - /* --- open() for /dev/radio --- */ - case SAA6588_CMD_OPEN: - a->result = 0; /* return error if chip doesn't work ??? */ - break; - /* --- close() for /dev/radio --- */ - case SAA6588_CMD_CLOSE: - s->data_available_for_read = 1; - wake_up_interruptible(&s->read_queue); - a->result = 0; - break; - /* --- read() for /dev/radio --- */ - case SAA6588_CMD_READ: - read_from_buf(s, a); - break; - /* --- poll() for /dev/radio --- */ - case SAA6588_CMD_POLL: - a->result = 0; - if (s->data_available_for_read) { - a->result |= POLLIN | POLLRDNORM; - } - poll_wait(a->instance, &s->read_queue, a->event_list); - break; - - default: - /* nothing */ - return -ENOIOCTLCMD; - } - return 0; -} - -static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa6588 *s = to_saa6588(sd); - - vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; - if (s->sync) - vt->rxsubchans |= V4L2_TUNER_SUB_RDS; - return 0; -} - -static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa6588 *s = to_saa6588(sd); - - saa6588_configure(s); - return 0; -} - -static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa6588_core_ops = { - .g_chip_ident = saa6588_g_chip_ident, - .ioctl = saa6588_ioctl, -}; - -static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = { - .g_tuner = saa6588_g_tuner, - .s_tuner = saa6588_s_tuner, -}; - -static const struct v4l2_subdev_ops saa6588_ops = { - .core = &saa6588_core_ops, - .tuner = &saa6588_tuner_ops, -}; - -/* ---------------------------------------------------------------------- */ - -static int saa6588_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa6588 *s; - struct v4l2_subdev *sd; - - v4l_info(client, "saa6588 found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (s == NULL) - return -ENOMEM; - - s->buf_size = bufblocks * 3; - - s->buffer = kmalloc(s->buf_size, GFP_KERNEL); - if (s->buffer == NULL) { - kfree(s); - return -ENOMEM; - } - sd = &s->sd; - v4l2_i2c_subdev_init(sd, client, &saa6588_ops); - spin_lock_init(&s->lock); - s->block_count = 0; - s->wr_index = 0; - s->rd_index = 0; - s->last_blocknum = 0xff; - init_waitqueue_head(&s->read_queue); - s->data_available_for_read = 0; - - saa6588_configure(s); - - /* start polling via eventd */ - INIT_DELAYED_WORK(&s->work, saa6588_work); - schedule_delayed_work(&s->work, 0); - return 0; -} - -static int saa6588_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct saa6588 *s = to_saa6588(sd); - - v4l2_device_unregister_subdev(sd); - - cancel_delayed_work_sync(&s->work); - - kfree(s->buffer); - kfree(s); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa6588_id[] = { - { "saa6588", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa6588_id); - -static struct i2c_driver saa6588_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa6588", - }, - .probe = saa6588_probe, - .remove = saa6588_remove, - .id_table = saa6588_id, -}; - -module_i2c_driver(saa6588_driver); diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c deleted file mode 100644 index 51cd4c8f0520..000000000000 --- a/drivers/media/video/saa7110.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * saa7110 - Philips SAA7110(A) video decoder driver - * - * Copyright (C) 1998 Pauline Middelink - * - * Copyright (C) 1999 Wolfgang Scherr - * Copyright (C) 2000 Serguei Miridonov - * - some corrections for Pinnacle Systems Inc. DC10plus card. - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * This program is free software; 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 -#include -#include -#include - -MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); -MODULE_AUTHOR("Pauline Middelink"); -MODULE_LICENSE("GPL"); - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ -#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */ - -#define SAA7110_NR_REG 0x35 - -struct saa7110 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - u8 reg[SAA7110_NR_REG]; - - v4l2_std_id norm; - int input; - int enable; - - wait_queue_head_t wq; -}; - -static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7110, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct saa7110, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ -/* I2C support functions */ -/* ----------------------------------------------------------------------- */ - -static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7110 *decoder = to_saa7110(sd); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7110 *decoder = to_saa7110(sd); - int ret = -1; - u8 reg = *data; /* first register to write to */ - - /* Sanity check */ - if (reg + (len - 1) > SAA7110_NR_REG) - return ret; - - /* the saa7110 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - ret = i2c_master_send(client, data, len); - - /* Cache the written data */ - memcpy(decoder->reg + reg, data + 1, len - 1); - } else { - for (++data, --len; len; len--) { - ret = saa7110_write(sd, reg++, *data++); - if (ret < 0) - break; - } - } - - return ret; -} - -static inline int saa7110_read(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte(client); -} - -/* ----------------------------------------------------------------------- */ -/* SAA7110 functions */ -/* ----------------------------------------------------------------------- */ - -#define FRESP_06H_COMPST 0x03 /*0x13*/ -#define FRESP_06H_SVIDEO 0x83 /*0xC0*/ - - -static int saa7110_selmux(struct v4l2_subdev *sd, int chan) -{ - static const unsigned char modes[9][8] = { - /* mode 0 */ - {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, - 0x44, 0x75, 0x16}, - /* mode 1 */ - {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, - 0x44, 0x75, 0x16}, - /* mode 2 */ - {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, - 0x60, 0xB5, 0x05}, - /* mode 3 */ - {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, - 0x60, 0xB5, 0x05}, - /* mode 4 */ - {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, - 0x60, 0xB5, 0x03}, - /* mode 5 */ - {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, - 0x60, 0xB5, 0x03}, - /* mode 6 */ - {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, - 0x44, 0x75, 0x12}, - /* mode 7 */ - {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, - 0x60, 0xB5, 0x14}, - /* mode 8 */ - {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, - 0x44, 0x75, 0x21} - }; - struct saa7110 *decoder = to_saa7110(sd); - const unsigned char *ptr = modes[chan]; - - saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */ - saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */ - saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */ - saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */ - saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */ - saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */ - saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */ - saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */ - decoder->input = chan; - - return 0; -} - -static const unsigned char initseq[1 + SAA7110_NR_REG] = { - 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, - /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, - /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, - /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, - /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, - /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 -}; - -static v4l2_std_id determine_norm(struct v4l2_subdev *sd) -{ - DEFINE_WAIT(wait); - struct saa7110 *decoder = to_saa7110(sd); - int status; - - /* mode changed, start automatic detection */ - saa7110_write_block(sd, initseq, sizeof(initseq)); - saa7110_selmux(sd, decoder->input); - prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(250)); - finish_wait(&decoder->wq, &wait); - status = saa7110_read(sd); - if (status & 0x40) { - v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status); - return decoder->norm; /* no change*/ - } - if ((status & 3) == 0) { - saa7110_write(sd, 0x06, 0x83); - if (status & 0x20) { - v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status); - /*saa7110_write(sd,0x2E,0x81);*/ - return V4L2_STD_NTSC; - } - v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status); - /*saa7110_write(sd,0x2E,0x9A);*/ - return V4L2_STD_PAL; - } - /*saa7110_write(sd,0x06,0x03);*/ - if (status & 0x20) { /* 60Hz */ - v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status); - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x50); - saa7110_write(sd, 0x11, 0x2C); - /*saa7110_write(sd,0x2E,0x81);*/ - return V4L2_STD_NTSC; - } - - /* 50Hz -> PAL/SECAM */ - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd,0x2E,0x9A);*/ - - prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(250)); - finish_wait(&decoder->wq, &wait); - - status = saa7110_read(sd); - if ((status & 0x03) == 0x01) { - v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status); - saa7110_write(sd, 0x0D, 0x87); - return V4L2_STD_SECAM; - } - v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status); - return V4L2_STD_PAL; -} - -static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) -{ - struct saa7110 *decoder = to_saa7110(sd); - int res = V4L2_IN_ST_NO_SIGNAL; - int status = saa7110_read(sd); - - v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", - status, (unsigned long long)decoder->norm); - if (!(status & 0x40)) - res = 0; - if (!(status & 0x03)) - res |= V4L2_IN_ST_NO_COLOR; - - *pstatus = res; - return 0; -} - -static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - *(v4l2_std_id *)std = determine_norm(sd); - return 0; -} - -static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7110 *decoder = to_saa7110(sd); - - if (decoder->norm != std) { - decoder->norm = std; - /*saa7110_write(sd, 0x06, 0x03);*/ - if (std & V4L2_STD_NTSC) { - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x50); - saa7110_write(sd, 0x11, 0x2C); - /*saa7110_write(sd, 0x2E, 0x81);*/ - v4l2_dbg(1, debug, sd, "switched to NTSC\n"); - } else if (std & V4L2_STD_PAL) { - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd, 0x2E, 0x9A);*/ - v4l2_dbg(1, debug, sd, "switched to PAL\n"); - } else if (std & V4L2_STD_SECAM) { - saa7110_write(sd, 0x0D, 0x87); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd, 0x2E, 0x9A);*/ - v4l2_dbg(1, debug, sd, "switched to SECAM\n"); - } else { - return -EINVAL; - } - } - return 0; -} - -static int saa7110_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7110 *decoder = to_saa7110(sd); - - if (input >= SAA7110_MAX_INPUT) { - v4l2_dbg(1, debug, sd, "input=%d not available\n", input); - return -EINVAL; - } - if (decoder->input != input) { - saa7110_selmux(sd, input); - v4l2_dbg(1, debug, sd, "switched to input=%d\n", input); - } - return 0; -} - -static int saa7110_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa7110 *decoder = to_saa7110(sd); - - if (decoder->enable != enable) { - decoder->enable = enable; - saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80); - v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off"); - } - return 0; -} - -static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - saa7110_write(sd, 0x19, ctrl->val); - break; - case V4L2_CID_CONTRAST: - saa7110_write(sd, 0x13, ctrl->val); - break; - case V4L2_CID_SATURATION: - saa7110_write(sd, 0x12, ctrl->val); - break; - case V4L2_CID_HUE: - saa7110_write(sd, 0x07, ctrl->val); - break; - default: - return -EINVAL; - } - return 0; -} - -static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { - .s_ctrl = saa7110_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops saa7110_core_ops = { - .g_chip_ident = saa7110_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = saa7110_s_std, -}; - -static const struct v4l2_subdev_video_ops saa7110_video_ops = { - .s_routing = saa7110_s_routing, - .s_stream = saa7110_s_stream, - .querystd = saa7110_querystd, - .g_input_status = saa7110_g_input_status, -}; - -static const struct v4l2_subdev_ops saa7110_ops = { - .core = &saa7110_core_ops, - .video = &saa7110_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int saa7110_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa7110 *decoder; - struct v4l2_subdev *sd; - int rv; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); - if (!decoder) - return -ENOMEM; - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa7110_ops); - decoder->norm = V4L2_STD_PAL; - decoder->input = 0; - decoder->enable = 1; - v4l2_ctrl_handler_init(&decoder->hdl, 2); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); - v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - - init_waitqueue_head(&decoder->wq); - - rv = saa7110_write_block(sd, initseq, sizeof(initseq)); - if (rv < 0) { - v4l2_dbg(1, debug, sd, "init status %d\n", rv); - } else { - int ver, status; - saa7110_write(sd, 0x21, 0x10); - saa7110_write(sd, 0x0e, 0x18); - saa7110_write(sd, 0x0D, 0x04); - ver = saa7110_read(sd); - saa7110_write(sd, 0x0D, 0x06); - /*mdelay(150);*/ - status = saa7110_read(sd); - v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n", - ver, status); - saa7110_write(sd, 0x0D, 0x86); - saa7110_write(sd, 0x0F, 0x10); - saa7110_write(sd, 0x11, 0x59); - /*saa7110_write(sd, 0x2E, 0x9A);*/ - } - - /*saa7110_selmux(sd,0);*/ - /*determine_norm(sd);*/ - /* setup and implicit mode 0 select has been performed */ - - return 0; -} - -static int saa7110_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct saa7110 *decoder = to_saa7110(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa7110_id[] = { - { "saa7110", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7110_id); - -static struct i2c_driver saa7110_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7110", - }, - .probe = saa7110_probe, - .remove = saa7110_remove, - .id_table = saa7110_id, -}; - -module_i2c_driver(saa7110_driver); diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c deleted file mode 100644 index 2107336cd836..000000000000 --- a/drivers/media/video/saa7115.c +++ /dev/null @@ -1,1727 +0,0 @@ -/* saa711x - Philips SAA711x video decoder driver - * This driver can work with saa7111, saa7111a, saa7113, saa7114, - * saa7115 and saa7118. - * - * Based on saa7114 driver by Maxim Yevtyushkin, which is based on - * the saa7111 driver by Dave Perks. - * - * Copyright (C) 1998 Dave Perks - * Copyright (C) 2002 Maxim Yevtyushkin - * - * Slight changes for video timing and attachment output by - * Wolfgang Scherr - * - * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003) - * by Ronald Bultje - * - * Added saa7115 support by Kevin Thayer - * (2/17/2003) - * - * VBI support (2004) and cleanups (2005) by Hans Verkuil - * - * Copyright (c) 2005-2006 Mauro Carvalho Chehab - * SAA7111, SAA7113 and SAA7118 support - * - * This program is free software; 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 "saa711x_regs.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define VRES_60HZ (480+16) - -MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver"); -MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " - "Hans Verkuil, Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -struct saa711x_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - - struct { - /* chroma gain control cluster */ - struct v4l2_ctrl *agc; - struct v4l2_ctrl *gain; - }; - - v4l2_std_id std; - int input; - int output; - int enable; - int radio; - int width; - int height; - u32 ident; - u32 audclk_freq; - u32 crystal_freq; - u8 ucgc; - u8 cgcdiv; - u8 apll; -}; - -static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa711x_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Sanity routine to check if a register is present */ -static int saa711x_has_reg(const int id, const u8 reg) -{ - if (id == V4L2_IDENT_SAA7111) - return reg < 0x20 && reg != 0x01 && reg != 0x0f && - (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e; - if (id == V4L2_IDENT_SAA7111A) - return reg < 0x20 && reg != 0x01 && reg != 0x0f && - reg != 0x14 && reg != 0x18 && reg != 0x19 && - reg != 0x1d && reg != 0x1e; - - /* common for saa7113/4/5/8 */ - if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f || - reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) || - reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) || - reg == 0x82 || (reg >= 0x89 && reg <= 0x8e))) - return 0; - - switch (id) { - case V4L2_IDENT_SAA7113: - return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) && - reg != 0x5d && reg < 0x63; - case V4L2_IDENT_SAA7114: - return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) && - (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 && - reg != 0x81 && reg < 0xf0; - case V4L2_IDENT_SAA7115: - return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe); - case V4L2_IDENT_SAA7118: - return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) && - (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 && - (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0; - } - return 1; -} - -static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) -{ - struct saa711x_state *state = to_state(sd); - unsigned char reg, data; - - while (*regs != 0x00) { - reg = *(regs++); - data = *(regs++); - - /* According with datasheets, reserved regs should be - filled with 0 - seems better not to touch on they */ - if (saa711x_has_reg(state->ident, reg)) { - if (saa711x_write(sd, reg, data) < 0) - return -1; - } else { - v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); - } - } - return 0; -} - -static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -/* ----------------------------------------------------------------------- */ - -/* SAA7111 initialization table */ -static const unsigned char saa7111_init[] = { - R_01_INC_DELAY, 0x00, /* reserved */ - - /*front end */ - R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */ - R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, - * GAFIX=0, GAI1=256, GAI2=256 */ - R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */ - R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */ - - /* decoder */ - R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz) - * pixels after end of last line */ - R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to - * work with NTSC, too */ - R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0, - * VTRC=1, HPLL=0, VNOI=0 */ - R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0, - * VBLB=0, UPTCV=0, APER=1 */ - R_0A_LUMA_BRIGHT_CNTL, 0x80, - R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */ - R_0C_CHROMA_SAT_CNTL, 0x40, - R_0D_CHROMA_HUE_CNTL, 0x00, - R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, - * FCTC=0, CHBW=1 */ - R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */ - R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ - R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, - * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ - R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */ - R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */ - R_14_ANAL_ADC_COMPAT_CNTL, 0x00, - R_15_VGATE_START_FID_CHG, 0x00, - R_16_VGATE_STOP, 0x00, - R_17_MISC_VGATE_CONF_AND_MSB, 0x00, - - 0x00, 0x00 -}; - -/* SAA7113 init codes */ -static const unsigned char saa7113_init[] = { - R_01_INC_DELAY, 0x08, - R_02_INPUT_CNTL_1, 0xc2, - R_03_INPUT_CNTL_2, 0x30, - R_04_INPUT_CNTL_3, 0x00, - R_05_INPUT_CNTL_4, 0x00, - R_06_H_SYNC_START, 0x89, - R_07_H_SYNC_STOP, 0x0d, - R_08_SYNC_CNTL, 0x88, - R_09_LUMA_CNTL, 0x01, - R_0A_LUMA_BRIGHT_CNTL, 0x80, - R_0B_LUMA_CONTRAST_CNTL, 0x47, - R_0C_CHROMA_SAT_CNTL, 0x40, - R_0D_CHROMA_HUE_CNTL, 0x00, - R_0E_CHROMA_CNTL_1, 0x01, - R_0F_CHROMA_GAIN_CNTL, 0x2a, - R_10_CHROMA_CNTL_2, 0x08, - R_11_MODE_DELAY_CNTL, 0x0c, - R_12_RT_SIGNAL_CNTL, 0x07, - R_13_RT_X_PORT_OUT_CNTL, 0x00, - R_14_ANAL_ADC_COMPAT_CNTL, 0x00, - R_15_VGATE_START_FID_CHG, 0x00, - R_16_VGATE_STOP, 0x00, - R_17_MISC_VGATE_CONF_AND_MSB, 0x00, - - 0x00, 0x00 -}; - -/* If a value differs from the Hauppauge driver values, then the comment starts with - 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the - Hauppauge driver sets. */ - -/* SAA7114 and SAA7115 initialization table */ -static const unsigned char saa7115_init_auto_input[] = { - /* Front-End Part */ - R_01_INC_DELAY, 0x48, /* white peak control disabled */ - R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */ - R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */ - R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */ - /* Decoder Part */ - R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */ - R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */ - R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ - R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ - R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ - R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ - R_0D_CHROMA_HUE_CNTL, 0x00, - R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */ - R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */ - R_11_MODE_DELAY_CNTL, 0x00, - R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */ - R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */ - R_14_ANAL_ADC_COMPAT_CNTL, 0x00, - R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */ - R_19_RAW_DATA_OFF_CNTL, 0x80, - R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */ - R_1B_MISC_TVVCRDET, 0x42, /* recommended value */ - R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */ - R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */ - - - R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */ - - /* Power Device Control */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */ - 0x00, 0x00 -}; - -/* Used to reset saa7113, saa7114 and saa7115 */ -static const unsigned char saa7115_cfg_reset_scaler[] = { - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */ - 0x00, 0x00 -}; - -/* ============== SAA7715 VIDEO templates ============= */ - -static const unsigned char saa7115_cfg_60hz_video[] = { - R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - - R_15_VGATE_START_FID_CHG, 0x03, - R_16_VGATE_STOP, 0x11, - R_17_MISC_VGATE_CONF_AND_MSB, 0x9c, - - R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ - R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */ - - R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */ - - /* Task A */ - R_90_A_TASK_HANDLING_CNTL, 0x80, - R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, - R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, - R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, - - /* hoffset low (input), 0x0002 is minimum */ - R_94_A_HORIZ_INPUT_WINDOW_START, 0x01, - R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* hsize low (input), 0x02d0 = 720 */ - R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - R_98_A_VERT_INPUT_WINDOW_START, 0x05, - R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, - - R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c, - R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, - - R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, - R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, - - R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c, - R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, - - /* Task B */ - R_C0_B_TASK_HANDLING_CNTL, 0x00, - R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, - R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, - R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, - - /* 0x0002 is minimum */ - R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02, - R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* 0x02d0 = 720 */ - R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - /* vwindow start 0x12 = 18 */ - R_C8_B_VERT_INPUT_WINDOW_START, 0x12, - R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, - - /* vwindow length 0xf8 = 248 */ - R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1, - R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9, - - /* hwindow 0x02d0 = 720 */ - R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, - R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, - - R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ - R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */ - R_F5_PULSGEN_LINE_LENGTH, 0xad, - R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, - - 0x00, 0x00 -}; - -static const unsigned char saa7115_cfg_50hz_video[] = { - R_80_GLOBAL_CNTL_1, 0x00, - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - - R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */ - R_16_VGATE_STOP, 0x16, - R_17_MISC_VGATE_CONF_AND_MSB, 0x99, - - R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */ - R_0E_CHROMA_CNTL_1, 0x07, - - R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */ - - /* Task A */ - R_90_A_TASK_HANDLING_CNTL, 0x81, - R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, - R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, - R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, - - /* This is weird: the datasheet says that you should use 2 as the minimum value, */ - /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ - /* hoffset low (input), 0x0002 is minimum */ - R_94_A_HORIZ_INPUT_WINDOW_START, 0x00, - R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* hsize low (input), 0x02d0 = 720 */ - R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - R_98_A_VERT_INPUT_WINDOW_START, 0x03, - R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, - - /* vsize 0x12 = 18 */ - R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12, - R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, - - /* hsize 0x05a0 = 1440 */ - R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, - R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */ - R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */ - R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */ - - /* Task B */ - R_C0_B_TASK_HANDLING_CNTL, 0x00, - R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, - R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, - R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, - - /* This is weird: the datasheet says that you should use 2 as the minimum value, */ - /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ - /* hoffset low (input), 0x0002 is minimum. See comment above. */ - R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00, - R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - - /* hsize 0x02d0 = 720 */ - R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, - R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - - /* voffset 0x16 = 22 */ - R_C8_B_VERT_INPUT_WINDOW_START, 0x16, - R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, - - /* vsize 0x0120 = 288 */ - R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20, - R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01, - - /* hsize 0x02d0 = 720 */ - R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, - R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, - - R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ - R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */ - R_F5_PULSGEN_LINE_LENGTH, 0xb0, - R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, - - 0x00, 0x00 -}; - -/* ============== SAA7715 VIDEO templates (end) ======= */ - -static const unsigned char saa7115_cfg_vbi_on[] = { - R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ - - 0x00, 0x00 -}; - -static const unsigned char saa7115_cfg_vbi_off[] = { - R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ - - 0x00, 0x00 -}; - - -static const unsigned char saa7115_init_misc[] = { - R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01, - R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01, - R_84_I_PORT_SIGNAL_DEF, 0x20, - R_85_I_PORT_SIGNAL_POLAR, 0x21, - R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5, - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, - - /* Task A */ - R_A0_A_HORIZ_PRESCALING, 0x01, - R_A1_A_ACCUMULATION_LENGTH, 0x00, - R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, - - /* Configure controls at nominal value*/ - R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80, - R_A5_A_LUMA_CONTRAST_CNTL, 0x40, - R_A6_A_CHROMA_SATURATION_CNTL, 0x40, - - /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ - R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00, - R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02, - - R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00, - - /* must be horiz lum scaling / 2 */ - R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00, - R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01, - - /* must be offset luma / 2 */ - R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00, - - R_B0_A_VERT_LUMA_SCALING_INC, 0x00, - R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04, - - R_B2_A_VERT_CHROMA_SCALING_INC, 0x00, - R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04, - - R_B4_A_VERT_SCALING_MODE_CNTL, 0x01, - - R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00, - R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00, - R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00, - R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00, - - R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00, - R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00, - R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00, - R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00, - - /* Task B */ - R_D0_B_HORIZ_PRESCALING, 0x01, - R_D1_B_ACCUMULATION_LENGTH, 0x00, - R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, - - /* Configure controls at nominal value*/ - R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80, - R_D5_B_LUMA_CONTRAST_CNTL, 0x40, - R_D6_B_CHROMA_SATURATION_CNTL, 0x40, - - /* hor lum scaling 0x0400 = 1 */ - R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00, - R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04, - - R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00, - - /* must be hor lum scaling / 2 */ - R_DC_B_HORIZ_CHROMA_SCALING, 0x00, - R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02, - - /* must be offset luma / 2 */ - R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00, - - R_E0_B_VERT_LUMA_SCALING_INC, 0x00, - R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04, - - R_E2_B_VERT_CHROMA_SCALING_INC, 0x00, - R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04, - - R_E4_B_VERT_SCALING_MODE_CNTL, 0x01, - - R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00, - R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00, - R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00, - R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00, - - R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00, - R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00, - R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00, - R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00, - - R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ - R_F3_PLL_INCREMENT, 0x46, - R_F4_PLL2_STATUS, 0x00, - R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */ - R_F8_PULSE_B_POS, 0x00, - R_F9_PULSE_B_POS_MSB, 0x4b, - R_FA_PULSE_C_POS, 0x00, - R_FB_PULSE_C_POS_MSB, 0x4b, - - /* PLL2 lock detection settings: 71 lines 50% phase error */ - R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88, - - /* Turn off VBI */ - R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */ - R_41_LCR_BASE, 0xff, - R_41_LCR_BASE+1, 0xff, - R_41_LCR_BASE+2, 0xff, - R_41_LCR_BASE+3, 0xff, - R_41_LCR_BASE+4, 0xff, - R_41_LCR_BASE+5, 0xff, - R_41_LCR_BASE+6, 0xff, - R_41_LCR_BASE+7, 0xff, - R_41_LCR_BASE+8, 0xff, - R_41_LCR_BASE+9, 0xff, - R_41_LCR_BASE+10, 0xff, - R_41_LCR_BASE+11, 0xff, - R_41_LCR_BASE+12, 0xff, - R_41_LCR_BASE+13, 0xff, - R_41_LCR_BASE+14, 0xff, - R_41_LCR_BASE+15, 0xff, - R_41_LCR_BASE+16, 0xff, - R_41_LCR_BASE+17, 0xff, - R_41_LCR_BASE+18, 0xff, - R_41_LCR_BASE+19, 0xff, - R_41_LCR_BASE+20, 0xff, - R_41_LCR_BASE+21, 0xff, - R_41_LCR_BASE+22, 0xff, - R_58_PROGRAM_FRAMING_CODE, 0x40, - R_59_H_OFF_FOR_SLICER, 0x47, - R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83, - R_5D_DID, 0xbd, - R_5E_SDID, 0x35, - - R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */ - - R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, - R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, - 0x00, 0x00 -}; - -static int saa711x_odd_parity(u8 c) -{ - c ^= (c >> 4); - c ^= (c >> 2); - c ^= (c >> 1); - - return c & 1; -} - -static int saa711x_decode_vps(u8 *dst, u8 *p) -{ - static const u8 biphase_tbl[] = { - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, - 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, - 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, - 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, - 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, - 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, - 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, - 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, - 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, - 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, - 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, - 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, - 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, - 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, - 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, - 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, - }; - int i; - u8 c, err = 0; - - for (i = 0; i < 2 * 13; i += 2) { - err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; - c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4); - dst[i / 2] = c; - } - return err & 0xf0; -} - -static int saa711x_decode_wss(u8 *p) -{ - static const int wss_bits[8] = { - 0, 0, 0, 1, 0, 1, 1, 1 - }; - unsigned char parity; - int wss = 0; - int i; - - for (i = 0; i < 16; i++) { - int b1 = wss_bits[p[i] & 7]; - int b2 = wss_bits[(p[i] >> 3) & 7]; - - if (b1 == b2) - return -1; - wss |= b2 << i; - } - parity = wss & 15; - parity ^= parity >> 2; - parity ^= parity >> 1; - - if (!(parity & 1)) - return -1; - - return wss; -} - -static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct saa711x_state *state = to_state(sd); - u32 acpf; - u32 acni; - u32 hz; - u64 f; - u8 acc = 0; /* reg 0x3a, audio clock control */ - - /* Checks for chips that don't have audio clock (saa7111, saa7113) */ - if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) - return 0; - - v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); - - /* sanity check */ - if (freq < 32000 || freq > 48000) - return -EINVAL; - - /* hz is the refresh rate times 100 */ - hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; - /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ - acpf = (25600 * freq) / hz; - /* acni = (256 * freq * 2^23) / crystal_frequency = - (freq * 2^(8+23)) / crystal_frequency = - (freq << 31) / crystal_frequency */ - f = freq; - f = f << 31; - do_div(f, state->crystal_freq); - acni = f; - if (state->ucgc) { - acpf = acpf * state->cgcdiv / 16; - acni = acni * state->cgcdiv / 16; - acc = 0x80; - if (state->cgcdiv == 3) - acc |= 0x40; - } - if (state->apll) - acc |= 0x08; - - saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); - saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); - saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); - - saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); - saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, - (acpf >> 8) & 0xff); - saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, - (acpf >> 16) & 0x03); - - saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); - saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); - saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); - state->audclk_freq = freq; - return 0; -} - -static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct saa711x_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_CHROMA_AGC: - /* chroma gain cluster */ - if (state->agc->val) - state->gain->val = - saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; - break; - } - return 0; -} - -static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct saa711x_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val); - break; - - case V4L2_CID_CONTRAST: - saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val); - break; - - case V4L2_CID_SATURATION: - saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val); - break; - - case V4L2_CID_HUE: - saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val); - break; - - case V4L2_CID_CHROMA_AGC: - /* chroma gain cluster */ - if (state->agc->val) - saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val); - else - saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) -{ - struct saa711x_state *state = to_state(sd); - int HPSC, HFSC; - int VSCY; - int res; - int is_50hz = state->std & V4L2_STD_625_50; - int Vsrc = is_50hz ? 576 : 480; - - v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); - - /* FIXME need better bounds checking here */ - if ((width < 1) || (width > 1440)) - return -EINVAL; - if ((height < 1) || (height > Vsrc)) - return -EINVAL; - - if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { - /* Decoder only supports 720 columns and 480 or 576 lines */ - if (width != 720) - return -EINVAL; - if (height != Vsrc) - return -EINVAL; - } - - state->width = width; - state->height = height; - - if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH)) - return 0; - - /* probably have a valid size, let's set it */ - /* Set output width/height */ - /* width */ - - saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, - (u8) (width & 0xff)); - saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, - (u8) ((width >> 8) & 0xff)); - - /* Vertical Scaling uses height/2 */ - res = height / 2; - - /* On 60Hz, it is using a higher Vertical Output Size */ - if (!is_50hz) - res += (VRES_60HZ - 480) >> 1; - - /* height */ - saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, - (u8) (res & 0xff)); - saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, - (u8) ((res >> 8) & 0xff)); - - /* Scaling settings */ - /* Hprescaler is floor(inres/outres) */ - HPSC = (int)(720 / width); - /* 0 is not allowed (div. by zero) */ - HPSC = HPSC ? HPSC : 1; - HFSC = (int)((1024 * 720) / (HPSC * width)); - /* FIXME hardcodes to "Task B" - * write H prescaler integer */ - saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, - (u8) (HPSC & 0x3f)); - - v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); - /* write H fine-scaling (luminance) */ - saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, - (u8) (HFSC & 0xff)); - saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, - (u8) ((HFSC >> 8) & 0xff)); - /* write H fine-scaling (chrominance) - * must be lum/2, so i'll just bitshift :) */ - saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, - (u8) ((HFSC >> 1) & 0xff)); - saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, - (u8) ((HFSC >> 9) & 0xff)); - - VSCY = (int)((1024 * Vsrc) / height); - v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); - - /* Correct Contrast and Luminance */ - saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, - (u8) (64 * 1024 / VSCY)); - saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, - (u8) (64 * 1024 / VSCY)); - - /* write V fine-scaling (luminance) */ - saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, - (u8) (VSCY & 0xff)); - saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, - (u8) ((VSCY >> 8) & 0xff)); - /* write V fine-scaling (chrominance) */ - saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, - (u8) (VSCY & 0xff)); - saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, - (u8) ((VSCY >> 8) & 0xff)); - - saa711x_writeregs(sd, saa7115_cfg_reset_scaler); - - /* Activates task "B" */ - saa711x_write(sd, R_80_GLOBAL_CNTL_1, - saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); - - return 0; -} - -static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa711x_state *state = to_state(sd); - - /* Prevent unnecessary standard changes. During a standard - change the I-Port is temporarily disabled. Any devices - reading from that port can get confused. - Note that s_std is also used to switch from - radio to TV mode, so if a s_std is broadcast to - all I2C devices then you do not want to have an unwanted - side-effect here. */ - if (std == state->std) - return; - - state->std = std; - - // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. - if (std & V4L2_STD_525_60) { - v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); - saa711x_writeregs(sd, saa7115_cfg_60hz_video); - saa711x_set_size(sd, 720, 480); - } else { - v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); - saa711x_writeregs(sd, saa7115_cfg_50hz_video); - saa711x_set_size(sd, 720, 576); - } - - /* Register 0E - Bits D6-D4 on NO-AUTO mode - (SAA7111 and SAA7113 doesn't have auto mode) - 50 Hz / 625 lines 60 Hz / 525 lines - 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz) - 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz) - 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz) - 011 NTSC N (3.58MHz) PAL M (3.58MHz) - 100 reserved NTSC-Japan (3.58MHz) - */ - if (state->ident <= V4L2_IDENT_SAA7113) { - u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; - - if (std == V4L2_STD_PAL_M) { - reg |= 0x30; - } else if (std == V4L2_STD_PAL_Nc) { - reg |= 0x20; - } else if (std == V4L2_STD_PAL_60) { - reg |= 0x10; - } else if (std == V4L2_STD_NTSC_M_JP) { - reg |= 0x40; - } else if (std & V4L2_STD_SECAM) { - reg |= 0x50; - } - saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); - } else { - /* restart task B if needed */ - int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; - - if (taskb && state->ident == V4L2_IDENT_SAA7114) { - saa711x_writeregs(sd, saa7115_cfg_vbi_on); - } - - /* switch audio mode too! */ - saa711x_s_clock_freq(sd, state->audclk_freq); - } -} - -/* setup the sliced VBI lcr registers according to the sliced VBI format */ -static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) -{ - struct saa711x_state *state = to_state(sd); - int is_50hz = (state->std & V4L2_STD_625_50); - u8 lcr[24]; - int i, x; - -#if 1 - /* saa7113/7114/7118 VBI support are experimental */ - if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) - return; - -#else - /* SAA7113 and SAA7118 also should support VBI - Need testing */ - if (state->ident != V4L2_IDENT_SAA7115) - return; -#endif - - for (i = 0; i <= 23; i++) - lcr[i] = 0xff; - - if (fmt == NULL) { - /* raw VBI */ - if (is_50hz) - for (i = 6; i <= 23; i++) - lcr[i] = 0xdd; - else - for (i = 10; i <= 21; i++) - lcr[i] = 0xdd; - } else { - /* sliced VBI */ - /* first clear lines that cannot be captured */ - if (is_50hz) { - for (i = 0; i <= 5; i++) - fmt->service_lines[0][i] = - fmt->service_lines[1][i] = 0; - } - else { - for (i = 0; i <= 9; i++) - fmt->service_lines[0][i] = - fmt->service_lines[1][i] = 0; - for (i = 22; i <= 23; i++) - fmt->service_lines[0][i] = - fmt->service_lines[1][i] = 0; - } - - /* Now set the lcr values according to the specified service */ - for (i = 6; i <= 23; i++) { - lcr[i] = 0; - for (x = 0; x <= 1; x++) { - switch (fmt->service_lines[1-x][i]) { - case 0: - lcr[i] |= 0xf << (4 * x); - break; - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 5 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 7 << (4 * x); - break; - } - } - } - } - - /* write the lcr registers */ - for (i = 2; i <= 23; i++) { - saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); - } - - /* enable/disable raw VBI capturing */ - saa711x_writeregs(sd, fmt == NULL ? - saa7115_cfg_vbi_on : - saa7115_cfg_vbi_off); -} - -static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) -{ - static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_CAPTION_525, /* 4 */ - V4L2_SLICED_WSS_625, 0, /* 5 */ - V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ - 0, 0, 0, 0 - }; - int i; - - memset(sliced, 0, sizeof(*sliced)); - /* done if using raw VBI */ - if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) - return 0; - for (i = 2; i <= 23; i++) { - u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); - - sliced->service_lines[0][i] = lcr2vbi[v >> 4]; - sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; - sliced->service_set |= - sliced->service_lines[0][i] | sliced->service_lines[1][i]; - } - return 0; -} - -static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) -{ - saa711x_set_lcr(sd, NULL); - return 0; -} - -static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) -{ - saa711x_set_lcr(sd, fmt); - return 0; -} - -static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - if (fmt->code != V4L2_MBUS_FMT_FIXED) - return -EINVAL; - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - return saa711x_set_size(sd, fmt->width, fmt->height); -} - -/* Decode the sliced VBI data stream as created by the saa7115. - The format is described in the saa7115 datasheet in Tables 25 and 26 - and in Figure 33. - The current implementation uses SAV/EAV codes and not the ancillary data - headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV - code. */ -static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) -{ - struct saa711x_state *state = to_state(sd); - static const char vbi_no_data_pattern[] = { - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 - }; - u8 *p = vbi->p; - u32 wss; - int id1, id2; /* the ID1 and ID2 bytes from the internal header */ - - vbi->type = 0; /* mark result as a failure */ - id1 = p[2]; - id2 = p[3]; - /* Note: the field bit is inverted for 60 Hz video */ - if (state->std & V4L2_STD_525_60) - id1 ^= 0x40; - - /* Skip internal header, p now points to the start of the payload */ - p += 4; - vbi->p = p; - - /* calculate field and line number of the VBI packet (1-23) */ - vbi->is_second_field = ((id1 & 0x40) != 0); - vbi->line = (id1 & 0x3f) << 3; - vbi->line |= (id2 & 0x70) >> 4; - - /* Obtain data type */ - id2 &= 0xf; - - /* If the VBI slicer does not detect any signal it will fill up - the payload buffer with 0xa0 bytes. */ - if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) - return 0; - - /* decode payloads */ - switch (id2) { - case 1: - vbi->type = V4L2_SLICED_TELETEXT_B; - break; - case 4: - if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) - return 0; - vbi->type = V4L2_SLICED_CAPTION_525; - break; - case 5: - wss = saa711x_decode_wss(p); - if (wss == -1) - return 0; - p[0] = wss & 0xff; - p[1] = wss >> 8; - vbi->type = V4L2_SLICED_WSS_625; - break; - case 7: - if (saa711x_decode_vps(p, p) != 0) - return 0; - vbi->type = V4L2_SLICED_VPS; - break; - default: - break; - } - return 0; -} - -/* ============ SAA7115 AUDIO settings (end) ============= */ - -static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa711x_state *state = to_state(sd); - int status; - - if (state->radio) - return 0; - status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - - v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); - vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; - return 0; -} - -static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa711x_state *state = to_state(sd); - - state->radio = 0; - saa711x_set_v4lstd(sd, std); - return 0; -} - -static int saa711x_s_radio(struct v4l2_subdev *sd) -{ - struct saa711x_state *state = to_state(sd); - - state->radio = 1; - return 0; -} - -static int saa711x_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa711x_state *state = to_state(sd); - u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0; - - v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", - input, output); - - /* saa7111/3 does not have these inputs */ - if (state->ident <= V4L2_IDENT_SAA7113 && - (input == SAA7115_COMPOSITE4 || - input == SAA7115_COMPOSITE5)) { - return -EINVAL; - } - if (input > SAA7115_SVIDEO3) - return -EINVAL; - if (state->input == input && state->output == output) - return 0; - v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", - (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", - (output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); - state->input = input; - - /* saa7111 has slightly different input numbering */ - if (state->ident <= V4L2_IDENT_SAA7111A) { - if (input >= SAA7115_COMPOSITE4) - input -= 2; - /* saa7111 specific */ - saa711x_write(sd, R_10_CHROMA_CNTL_2, - (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | - ((output & 0xc0) ^ 0x40)); - saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, - (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | - ((output & 2) ? 0x0a : 0)); - } - - /* select mode */ - saa711x_write(sd, R_02_INPUT_CNTL_1, - (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | - input); - - /* bypass chrominance trap for S-Video modes */ - saa711x_write(sd, R_09_LUMA_CNTL, - (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | - (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); - - state->output = output; - if (state->ident == V4L2_IDENT_SAA7114 || - state->ident == V4L2_IDENT_SAA7115) { - saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, - (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | - (state->output & 0x01)); - } - return 0; -} - -static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) -{ - struct saa711x_state *state = to_state(sd); - - if (state->ident > V4L2_IDENT_SAA7111A) - return -EINVAL; - saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | - (val ? 0x80 : 0)); - return 0; -} - -static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa711x_state *state = to_state(sd); - - v4l2_dbg(1, debug, sd, "%s output\n", - enable ? "enable" : "disable"); - - if (state->enable == enable) - return 0; - state->enable = enable; - if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED)) - return 0; - saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable); - return 0; -} - -static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) -{ - struct saa711x_state *state = to_state(sd); - - if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ) - return -EINVAL; - state->crystal_freq = freq; - state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; - saa711x_s_clock_freq(sd, state->audclk_freq); - return 0; -} - -static int saa711x_reset(struct v4l2_subdev *sd, u32 val) -{ - v4l2_dbg(1, debug, sd, "decoder RESET\n"); - saa711x_writeregs(sd, saa7115_cfg_reset_scaler); - return 0; -} - -static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) -{ - /* Note: the internal field ID is inverted for NTSC, - so data->field 0 maps to the saa7115 even field, - whereas for PAL it maps to the saa7115 odd field. */ - switch (data->id) { - case V4L2_SLICED_WSS_625: - if (saa711x_read(sd, 0x6b) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(sd, 0x6c); - data->data[1] = saa711x_read(sd, 0x6d); - return 0; - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) { - /* CC */ - if (saa711x_read(sd, 0x66) & 0x30) - return -EIO; - data->data[0] = saa711x_read(sd, 0x69); - data->data[1] = saa711x_read(sd, 0x6a); - return 0; - } - /* XDS */ - if (saa711x_read(sd, 0x66) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(sd, 0x67); - data->data[1] = saa711x_read(sd, 0x68); - return 0; - default: - return -EINVAL; - } -} - -static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - struct saa711x_state *state = to_state(sd); - int reg1f, reg1e; - - /* - * The V4L2 core already initializes std with all supported - * Standards. All driver needs to do is to mask it, to remove - * standards that don't apply from the mask - */ - - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f); - - /* horizontal/vertical not locked */ - if (reg1f & 0x40) - goto ret; - - if (reg1f & 0x20) - *std &= V4L2_STD_525_60; - else - *std &= V4L2_STD_625_50; - - if (state->ident != V4L2_IDENT_SAA7115) - goto ret; - - reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - - switch (reg1e & 0x03) { - case 1: - *std &= V4L2_STD_NTSC; - break; - case 2: - /* - * V4L2_STD_PAL just cover the european PAL standards. - * This is wrong, as the device could also be using an - * other PAL standard. - */ - *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | - V4L2_STD_PAL_M | V4L2_STD_PAL_60; - break; - case 3: - *std &= V4L2_STD_SECAM; - break; - default: - /* Can't detect anything */ - break; - } - - v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); - -ret: - v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); - - return 0; -} - -static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - struct saa711x_state *state = to_state(sd); - int reg1e = 0x80; - int reg1f; - - *status = V4L2_IN_ST_NO_SIGNAL; - if (state->ident == V4L2_IDENT_SAA7115) - reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80) - *status = 0; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa711x_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct saa711x_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); -} - -static int saa711x_log_status(struct v4l2_subdev *sd) -{ - struct saa711x_state *state = to_state(sd); - int reg1e, reg1f; - int signalOk; - int vcr; - - v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); - if (state->ident != V4L2_IDENT_SAA7115) { - /* status for the saa7114 */ - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - signalOk = (reg1f & 0xc1) == 0x81; - v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); - v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - return 0; - } - - /* status for the saa7115 */ - reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - - signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; - vcr = !(reg1f & 0x10); - - if (state->input >= 6) - v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); - else - v4l2_info(sd, "Input: Composite %d\n", state->input); - v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); - v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - - switch (reg1e & 0x03) { - case 1: - v4l2_info(sd, "Detected format: NTSC\n"); - break; - case 2: - v4l2_info(sd, "Detected format: PAL\n"); - break; - case 3: - v4l2_info(sd, "Detected format: SECAM\n"); - break; - default: - v4l2_info(sd, "Detected format: BW/No color\n"); - break; - } - v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { - .s_ctrl = saa711x_s_ctrl, - .g_volatile_ctrl = saa711x_g_volatile_ctrl, -}; - -static const struct v4l2_subdev_core_ops saa711x_core_ops = { - .log_status = saa711x_log_status, - .g_chip_ident = saa711x_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = saa711x_s_std, - .reset = saa711x_reset, - .s_gpio = saa711x_s_gpio, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = saa711x_g_register, - .s_register = saa711x_s_register, -#endif -}; - -static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { - .s_radio = saa711x_s_radio, - .g_tuner = saa711x_g_tuner, -}; - -static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { - .s_clock_freq = saa711x_s_clock_freq, -}; - -static const struct v4l2_subdev_video_ops saa711x_video_ops = { - .s_routing = saa711x_s_routing, - .s_crystal_freq = saa711x_s_crystal_freq, - .s_mbus_fmt = saa711x_s_mbus_fmt, - .s_stream = saa711x_s_stream, - .querystd = saa711x_querystd, - .g_input_status = saa711x_g_input_status, -}; - -static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { - .g_vbi_data = saa711x_g_vbi_data, - .decode_vbi_line = saa711x_decode_vbi_line, - .g_sliced_fmt = saa711x_g_sliced_fmt, - .s_sliced_fmt = saa711x_s_sliced_fmt, - .s_raw_fmt = saa711x_s_raw_fmt, -}; - -static const struct v4l2_subdev_ops saa711x_ops = { - .core = &saa711x_core_ops, - .tuner = &saa711x_tuner_ops, - .audio = &saa711x_audio_ops, - .video = &saa711x_video_ops, - .vbi = &saa711x_vbi_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int saa711x_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa711x_state *state; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - int i; - char name[17]; - char chip_id; - int autodetect = !id || id->driver_data == 1; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - for (i = 0; i < 0x0f; i++) { - i2c_smbus_write_byte_data(client, 0, i); - name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; - if (name[i] > '9') - name[i] += 'a' - '9' - 1; - } - name[i] = '\0'; - - chip_id = name[5]; - - /* Check whether this chip is part of the saa711x series */ - if (memcmp(name + 1, "f711", 4)) { - v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", - client->addr << 1, name); - return -ENODEV; - } - - /* Safety check */ - if (!autodetect && id->name[6] != chip_id) { - v4l_warn(client, "found saa711%c while %s was expected\n", - chip_id, id->name); - } - snprintf(client->name, sizeof(client->name), "saa711%c", chip_id); - v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name, - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &saa711x_ops); - - hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, 6); - /* add in ascending ID order */ - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); - v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); - state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, - V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(state); - return err; - } - v4l2_ctrl_auto_cluster(2, &state->agc, 0, true); - - state->input = -1; - state->output = SAA7115_IPORT_ON; - state->enable = 1; - state->radio = 0; - switch (chip_id) { - case '1': - state->ident = V4L2_IDENT_SAA7111; - if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) { - v4l_info(client, "saa7111a variant found\n"); - state->ident = V4L2_IDENT_SAA7111A; - } - break; - case '3': - state->ident = V4L2_IDENT_SAA7113; - break; - case '4': - state->ident = V4L2_IDENT_SAA7114; - break; - case '5': - state->ident = V4L2_IDENT_SAA7115; - break; - case '8': - state->ident = V4L2_IDENT_SAA7118; - break; - default: - state->ident = V4L2_IDENT_SAA7111; - v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); - break; - } - - state->audclk_freq = 48000; - - v4l2_dbg(1, debug, sd, "writing init values\n"); - - /* init to 60hz/48khz */ - state->crystal_freq = SAA7115_FREQ_24_576_MHZ; - switch (state->ident) { - case V4L2_IDENT_SAA7111: - case V4L2_IDENT_SAA7111A: - saa711x_writeregs(sd, saa7111_init); - break; - case V4L2_IDENT_SAA7113: - saa711x_writeregs(sd, saa7113_init); - break; - default: - state->crystal_freq = SAA7115_FREQ_32_11_MHZ; - saa711x_writeregs(sd, saa7115_init_auto_input); - } - if (state->ident > V4L2_IDENT_SAA7111A) - saa711x_writeregs(sd, saa7115_init_misc); - saa711x_set_v4lstd(sd, V4L2_STD_NTSC); - v4l2_ctrl_handler_setup(hdl); - - v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", - saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), - saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa711x_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id saa711x_id[] = { - { "saa7115_auto", 1 }, /* autodetect */ - { "saa7111", 0 }, - { "saa7113", 0 }, - { "saa7114", 0 }, - { "saa7115", 0 }, - { "saa7118", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa711x_id); - -static struct i2c_driver saa711x_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7115", - }, - .probe = saa711x_probe, - .remove = saa711x_remove, - .id_table = saa711x_id, -}; - -module_i2c_driver(saa711x_driver); diff --git a/drivers/media/video/saa711x_regs.h b/drivers/media/video/saa711x_regs.h deleted file mode 100644 index 4e5f2eb0a2c1..000000000000 --- a/drivers/media/video/saa711x_regs.h +++ /dev/null @@ -1,549 +0,0 @@ -/* saa711x - Philips SAA711x video decoder register specifications - * - * Copyright (c) 2006 Mauro Carvalho Chehab - * - * This program is free software; 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. - */ - -#define R_00_CHIP_VERSION 0x00 -/* Video Decoder */ - /* Video Decoder - Frontend part */ -#define R_01_INC_DELAY 0x01 -#define R_02_INPUT_CNTL_1 0x02 -#define R_03_INPUT_CNTL_2 0x03 -#define R_04_INPUT_CNTL_3 0x04 -#define R_05_INPUT_CNTL_4 0x05 - /* Video Decoder - Decoder part */ -#define R_06_H_SYNC_START 0x06 -#define R_07_H_SYNC_STOP 0x07 -#define R_08_SYNC_CNTL 0x08 -#define R_09_LUMA_CNTL 0x09 -#define R_0A_LUMA_BRIGHT_CNTL 0x0a -#define R_0B_LUMA_CONTRAST_CNTL 0x0b -#define R_0C_CHROMA_SAT_CNTL 0x0c -#define R_0D_CHROMA_HUE_CNTL 0x0d -#define R_0E_CHROMA_CNTL_1 0x0e -#define R_0F_CHROMA_GAIN_CNTL 0x0f -#define R_10_CHROMA_CNTL_2 0x10 -#define R_11_MODE_DELAY_CNTL 0x11 -#define R_12_RT_SIGNAL_CNTL 0x12 -#define R_13_RT_X_PORT_OUT_CNTL 0x13 -#define R_14_ANAL_ADC_COMPAT_CNTL 0x14 -#define R_15_VGATE_START_FID_CHG 0x15 -#define R_16_VGATE_STOP 0x16 -#define R_17_MISC_VGATE_CONF_AND_MSB 0x17 -#define R_18_RAW_DATA_GAIN_CNTL 0x18 -#define R_19_RAW_DATA_OFF_CNTL 0x19 -#define R_1A_COLOR_KILL_LVL_CNTL 0x1a -#define R_1B_MISC_TVVCRDET 0x1b -#define R_1C_ENHAN_COMB_CTRL1 0x1c -#define R_1D_ENHAN_COMB_CTRL2 0x1d -#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e -#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f - -/* Component processing and interrupt masking part */ -#define R_23_INPUT_CNTL_5 0x23 -#define R_24_INPUT_CNTL_6 0x24 -#define R_25_INPUT_CNTL_7 0x25 -#define R_29_COMP_DELAY 0x29 -#define R_2A_COMP_BRIGHT_CNTL 0x2a -#define R_2B_COMP_CONTRAST_CNTL 0x2b -#define R_2C_COMP_SAT_CNTL 0x2c -#define R_2D_INTERRUPT_MASK_1 0x2d -#define R_2E_INTERRUPT_MASK_2 0x2e -#define R_2F_INTERRUPT_MASK_3 0x2f - -/* Audio clock generator part */ -#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30 -#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34 -#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38 -#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39 -#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a - -/* General purpose VBI data slicer part */ -#define R_40_SLICER_CNTL_1 0x40 -#define R_41_LCR_BASE 0x41 -#define R_58_PROGRAM_FRAMING_CODE 0x58 -#define R_59_H_OFF_FOR_SLICER 0x59 -#define R_5A_V_OFF_FOR_SLICER 0x5a -#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b -#define R_5D_DID 0x5d -#define R_5E_SDID 0x5e -#define R_60_SLICER_STATUS_BYTE_0 0x60 -#define R_61_SLICER_STATUS_BYTE_1 0x61 -#define R_62_SLICER_STATUS_BYTE_2 0x62 - -/* X port, I port and the scaler part */ - /* Task independent global settings */ -#define R_80_GLOBAL_CNTL_1 0x80 -#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81 -#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83 -#define R_84_I_PORT_SIGNAL_DEF 0x84 -#define R_85_I_PORT_SIGNAL_POLAR 0x85 -#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86 -#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87 -#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88 -#define R_8F_STATUS_INFO_SCALER 0x8f - /* Task A definition */ - /* Basic settings and acquisition window definition */ -#define R_90_A_TASK_HANDLING_CNTL 0x90 -#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91 -#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92 -#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93 -#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94 -#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95 -#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96 -#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97 -#define R_98_A_VERT_INPUT_WINDOW_START 0x98 -#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99 -#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a -#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b -#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c -#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d -#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e -#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f - /* FIR filtering and prescaling */ -#define R_A0_A_HORIZ_PRESCALING 0xa0 -#define R_A1_A_ACCUMULATION_LENGTH 0xa1 -#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2 -#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4 -#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5 -#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6 - /* Horizontal phase scaling */ -#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8 -#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9 -#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa -#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac -#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad -#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae -#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf - /* Vertical scaling */ -#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0 -#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1 -#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2 -#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3 -#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4 -#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8 -#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9 -#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba -#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb -#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc -#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd -#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe -#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf - /* Task B definition */ - /* Basic settings and acquisition window definition */ -#define R_C0_B_TASK_HANDLING_CNTL 0xc0 -#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1 -#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2 -#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3 -#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4 -#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5 -#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6 -#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7 -#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8 -#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9 -#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca -#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb -#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc -#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd -#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce -#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf - /* FIR filtering and prescaling */ -#define R_D0_B_HORIZ_PRESCALING 0xd0 -#define R_D1_B_ACCUMULATION_LENGTH 0xd1 -#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2 -#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4 -#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5 -#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6 - /* Horizontal phase scaling */ -#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8 -#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9 -#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda -#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc -#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd -#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde - /* Vertical scaling */ -#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0 -#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1 -#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2 -#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3 -#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4 -#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8 -#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9 -#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea -#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb -#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec -#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed -#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee -#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef - -/* second PLL (PLL2) and Pulsegenerator Programming */ -#define R_F0_LFCO_PER_LINE 0xf0 -#define R_F1_P_I_PARAM_SELECT 0xf1 -#define R_F2_NOMINAL_PLL2_DTO 0xf2 -#define R_F3_PLL_INCREMENT 0xf3 -#define R_F4_PLL2_STATUS 0xf4 -#define R_F5_PULSGEN_LINE_LENGTH 0xf5 -#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6 -#define R_F7_PULSE_A_POS_MSB 0xf7 -#define R_F8_PULSE_B_POS 0xf8 -#define R_F9_PULSE_B_POS_MSB 0xf9 -#define R_FA_PULSE_C_POS 0xfa -#define R_FB_PULSE_C_POS_MSB 0xfb -#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff - -#if 0 -/* Those structs will be used in the future for debug purposes */ -struct saa711x_reg_descr { - u8 reg; - int count; - char *name; -}; - -struct saa711x_reg_descr saa711x_regs[] = { - /* REG COUNT NAME */ - {R_00_CHIP_VERSION,1, - "Chip version"}, - - /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */ - - /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */ - {R_01_INC_DELAY,1, - "Increment delay"}, - {R_02_INPUT_CNTL_1,1, - "Analog input control 1"}, - {R_03_INPUT_CNTL_2,1, - "Analog input control 2"}, - {R_04_INPUT_CNTL_3,1, - "Analog input control 3"}, - {R_05_INPUT_CNTL_4,1, - "Analog input control 4"}, - - /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */ - {R_06_H_SYNC_START,1, - "Horizontal sync start"}, - {R_07_H_SYNC_STOP,1, - "Horizontal sync stop"}, - {R_08_SYNC_CNTL,1, - "Sync control"}, - {R_09_LUMA_CNTL,1, - "Luminance control"}, - {R_0A_LUMA_BRIGHT_CNTL,1, - "Luminance brightness control"}, - {R_0B_LUMA_CONTRAST_CNTL,1, - "Luminance contrast control"}, - {R_0C_CHROMA_SAT_CNTL,1, - "Chrominance saturation control"}, - {R_0D_CHROMA_HUE_CNTL,1, - "Chrominance hue control"}, - {R_0E_CHROMA_CNTL_1,1, - "Chrominance control 1"}, - {R_0F_CHROMA_GAIN_CNTL,1, - "Chrominance gain control"}, - {R_10_CHROMA_CNTL_2,1, - "Chrominance control 2"}, - {R_11_MODE_DELAY_CNTL,1, - "Mode/delay control"}, - {R_12_RT_SIGNAL_CNTL,1, - "RT signal control"}, - {R_13_RT_X_PORT_OUT_CNTL,1, - "RT/X port output control"}, - {R_14_ANAL_ADC_COMPAT_CNTL,1, - "Analog/ADC/compatibility control"}, - {R_15_VGATE_START_FID_CHG, 1, - "VGATE start FID change"}, - {R_16_VGATE_STOP,1, - "VGATE stop"}, - {R_17_MISC_VGATE_CONF_AND_MSB, 1, - "Miscellaneous VGATE configuration and MSBs"}, - {R_18_RAW_DATA_GAIN_CNTL,1, - "Raw data gain control",}, - {R_19_RAW_DATA_OFF_CNTL,1, - "Raw data offset control",}, - {R_1A_COLOR_KILL_LVL_CNTL,1, - "Color Killer Level Control"}, - { R_1B_MISC_TVVCRDET, 1, - "MISC /TVVCRDET"}, - { R_1C_ENHAN_COMB_CTRL1, 1, - "Enhanced comb ctrl1"}, - { R_1D_ENHAN_COMB_CTRL2, 1, - "Enhanced comb ctrl1"}, - {R_1E_STATUS_BYTE_1_VD_DEC,1, - "Status byte 1 video decoder"}, - {R_1F_STATUS_BYTE_2_VD_DEC,1, - "Status byte 2 video decoder"}, - - /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */ - /* 0x20 to 0x22 - Reserved */ - {R_23_INPUT_CNTL_5,1, - "Analog input control 5"}, - {R_24_INPUT_CNTL_6,1, - "Analog input control 6"}, - {R_25_INPUT_CNTL_7,1, - "Analog input control 7"}, - /* 0x26 to 0x28 - Reserved */ - {R_29_COMP_DELAY,1, - "Component delay"}, - {R_2A_COMP_BRIGHT_CNTL,1, - "Component brightness control"}, - {R_2B_COMP_CONTRAST_CNTL,1, - "Component contrast control"}, - {R_2C_COMP_SAT_CNTL,1, - "Component saturation control"}, - {R_2D_INTERRUPT_MASK_1,1, - "Interrupt mask 1"}, - {R_2E_INTERRUPT_MASK_2,1, - "Interrupt mask 2"}, - {R_2F_INTERRUPT_MASK_3,1, - "Interrupt mask 3"}, - - /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */ - {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3, - "Audio master clock cycles per field"}, - /* 0x33 - Reserved */ - {R_34_AUD_MAST_CLK_NOMINAL_INC,3, - "Audio master clock nominal increment"}, - /* 0x37 - Reserved */ - {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1, - "Clock ratio AMXCLK to ASCLK"}, - {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1, - "Clock ratio ASCLK to ALRCLK"}, - {R_3A_AUD_CLK_GEN_BASIC_SETUP,1, - "Audio clock generator basic setup"}, - /* 0x3b-0x3f - Reserved */ - - /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */ - {R_40_SLICER_CNTL_1,1, - "Slicer control 1"}, - {R_41_LCR,23, - "R_41_LCR"}, - {R_58_PROGRAM_FRAMING_CODE,1, - "Programmable framing code"}, - {R_59_H_OFF_FOR_SLICER,1, - "Horizontal offset for slicer"}, - {R_5A_V_OFF_FOR_SLICER,1, - "Vertical offset for slicer"}, - {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1, - "Field offset and MSBs for horizontal and vertical offset"}, - {R_5D_DID,1, - "Header and data identification (R_5D_DID)"}, - {R_5E_SDID,1, - "Sliced data identification (R_5E_SDID) code"}, - {R_60_SLICER_STATUS_BYTE_0,1, - "Slicer status byte 0"}, - {R_61_SLICER_STATUS_BYTE_1,1, - "Slicer status byte 1"}, - {R_62_SLICER_STATUS_BYTE_2,1, - "Slicer status byte 2"}, - /* 0x63-0x7f - Reserved */ - - /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ - /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */ - {R_80_GLOBAL_CNTL_1,1, - "Global control 1"}, - {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1, - "Vertical sync and Field ID source selection, retimed V and F signals"}, - /* 0x82 - Reserved */ - {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1, - "X port I/O enable and output clock"}, - {R_84_I_PORT_SIGNAL_DEF,1, - "I port signal definitions"}, - {R_85_I_PORT_SIGNAL_POLAR,1, - "I port signal polarities"}, - {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1, - "I port FIFO flag control and arbitration"}, - {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1, - "I port I/O enable output clock and gated"}, - {R_88_POWER_SAVE_ADC_PORT_CNTL,1, - "Power save/ADC port control"}, - /* 089-0x8e - Reserved */ - {R_8F_STATUS_INFO_SCALER,1, - "Status information scaler part"}, - - /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */ - /* Task A: Basic settings and acquisition window definition */ - {R_90_A_TASK_HANDLING_CNTL,1, - "Task A: Task handling control"}, - {R_91_A_X_PORT_FORMATS_AND_CONF,1, - "Task A: X port formats and configuration"}, - {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1, - "Task A: X port input reference signal definition"}, - {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1, - "Task A: I port output formats and configuration"}, - {R_94_A_HORIZ_INPUT_WINDOW_START,2, - "Task A: Horizontal input window start"}, - {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2, - "Task A: Horizontal input window length"}, - {R_98_A_VERT_INPUT_WINDOW_START,2, - "Task A: Vertical input window start"}, - {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2, - "Task A: Vertical input window length"}, - {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2, - "Task A: Horizontal output window length"}, - {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2, - "Task A: Vertical output window length"}, - - /* Task A: FIR filtering and prescaling */ - {R_A0_A_HORIZ_PRESCALING,1, - "Task A: Horizontal prescaling"}, - {R_A1_A_ACCUMULATION_LENGTH,1, - "Task A: Accumulation length"}, - {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, - "Task A: Prescaler DC gain and FIR prefilter"}, - /* 0xa3 - Reserved */ - {R_A4_A_LUMA_BRIGHTNESS_CNTL,1, - "Task A: Luminance brightness control"}, - {R_A5_A_LUMA_CONTRAST_CNTL,1, - "Task A: Luminance contrast control"}, - {R_A6_A_CHROMA_SATURATION_CNTL,1, - "Task A: Chrominance saturation control"}, - /* 0xa7 - Reserved */ - - /* Task A: Horizontal phase scaling */ - {R_A8_A_HORIZ_LUMA_SCALING_INC,2, - "Task A: Horizontal luminance scaling increment"}, - {R_AA_A_HORIZ_LUMA_PHASE_OFF,1, - "Task A: Horizontal luminance phase offset"}, - /* 0xab - Reserved */ - {R_AC_A_HORIZ_CHROMA_SCALING_INC,2, - "Task A: Horizontal chrominance scaling increment"}, - {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1, - "Task A: Horizontal chrominance phase offset"}, - /* 0xaf - Reserved */ - - /* Task A: Vertical scaling */ - {R_B0_A_VERT_LUMA_SCALING_INC,2, - "Task A: Vertical luminance scaling increment"}, - {R_B2_A_VERT_CHROMA_SCALING_INC,2, - "Task A: Vertical chrominance scaling increment"}, - {R_B4_A_VERT_SCALING_MODE_CNTL,1, - "Task A: Vertical scaling mode control"}, - /* 0xb5-0xb7 - Reserved */ - {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1, - "Task A: Vertical chrominance phase offset '00'"}, - {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1, - "Task A: Vertical chrominance phase offset '01'"}, - {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1, - "Task A: Vertical chrominance phase offset '10'"}, - {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1, - "Task A: Vertical chrominance phase offset '11'"}, - {R_BC_A_VERT_LUMA_PHASE_OFF_00,1, - "Task A: Vertical luminance phase offset '00'"}, - {R_BD_A_VERT_LUMA_PHASE_OFF_01,1, - "Task A: Vertical luminance phase offset '01'"}, - {R_BE_A_VERT_LUMA_PHASE_OFF_10,1, - "Task A: Vertical luminance phase offset '10'"}, - {R_BF_A_VERT_LUMA_PHASE_OFF_11,1, - "Task A: Vertical luminance phase offset '11'"}, - - /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ - /* Task B: Basic settings and acquisition window definition */ - {R_C0_B_TASK_HANDLING_CNTL,1, - "Task B: Task handling control"}, - {R_C1_B_X_PORT_FORMATS_AND_CONF,1, - "Task B: X port formats and configuration"}, - {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1, - "Task B: Input reference signal definition"}, - {R_C3_B_I_PORT_FORMATS_AND_CONF,1, - "Task B: I port formats and configuration"}, - {R_C4_B_HORIZ_INPUT_WINDOW_START,2, - "Task B: Horizontal input window start"}, - {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2, - "Task B: Horizontal input window length"}, - {R_C8_B_VERT_INPUT_WINDOW_START,2, - "Task B: Vertical input window start"}, - {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2, - "Task B: Vertical input window length"}, - {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2, - "Task B: Horizontal output window length"}, - {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2, - "Task B: Vertical output window length"}, - - /* Task B: FIR filtering and prescaling */ - {R_D0_B_HORIZ_PRESCALING,1, - "Task B: Horizontal prescaling"}, - {R_D1_B_ACCUMULATION_LENGTH,1, - "Task B: Accumulation length"}, - {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, - "Task B: Prescaler DC gain and FIR prefilter"}, - /* 0xd3 - Reserved */ - {R_D4_B_LUMA_BRIGHTNESS_CNTL,1, - "Task B: Luminance brightness control"}, - {R_D5_B_LUMA_CONTRAST_CNTL,1, - "Task B: Luminance contrast control"}, - {R_D6_B_CHROMA_SATURATION_CNTL,1, - "Task B: Chrominance saturation control"}, - /* 0xd7 - Reserved */ - - /* Task B: Horizontal phase scaling */ - {R_D8_B_HORIZ_LUMA_SCALING_INC,2, - "Task B: Horizontal luminance scaling increment"}, - {R_DA_B_HORIZ_LUMA_PHASE_OFF,1, - "Task B: Horizontal luminance phase offset"}, - /* 0xdb - Reserved */ - {R_DC_B_HORIZ_CHROMA_SCALING,2, - "Task B: Horizontal chrominance scaling"}, - {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1, - "Task B: Horizontal Phase Offset Chroma"}, - /* 0xdf - Reserved */ - - /* Task B: Vertical scaling */ - {R_E0_B_VERT_LUMA_SCALING_INC,2, - "Task B: Vertical luminance scaling increment"}, - {R_E2_B_VERT_CHROMA_SCALING_INC,2, - "Task B: Vertical chrominance scaling increment"}, - {R_E4_B_VERT_SCALING_MODE_CNTL,1, - "Task B: Vertical scaling mode control"}, - /* 0xe5-0xe7 - Reserved */ - {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1, - "Task B: Vertical chrominance phase offset '00'"}, - {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1, - "Task B: Vertical chrominance phase offset '01'"}, - {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1, - "Task B: Vertical chrominance phase offset '10'"}, - {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1, - "Task B: Vertical chrominance phase offset '11'"}, - {R_EC_B_VERT_LUMA_PHASE_OFF_00,1, - "Task B: Vertical luminance phase offset '00'"}, - {R_ED_B_VERT_LUMA_PHASE_OFF_01,1, - "Task B: Vertical luminance phase offset '01'"}, - {R_EE_B_VERT_LUMA_PHASE_OFF_10,1, - "Task B: Vertical luminance phase offset '10'"}, - {R_EF_B_VERT_LUMA_PHASE_OFF_11,1, - "Task B: Vertical luminance phase offset '11'"}, - - /* second PLL (PLL2) and Pulsegenerator Programming */ - { R_F0_LFCO_PER_LINE, 1, - "LFCO's per line"}, - { R_F1_P_I_PARAM_SELECT,1, - "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"}, - { R_F2_NOMINAL_PLL2_DTO,1, - "Nominal PLL2 DTO"}, - {R_F3_PLL_INCREMENT,1, - "PLL2 Increment"}, - {R_F4_PLL2_STATUS,1, - "PLL2 Status"}, - {R_F5_PULSGEN_LINE_LENGTH,1, - "Pulsgen. line length"}, - {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1, - "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"}, - {R_F7_PULSE_A_POS_MSB,1, - "Pulse A Position"}, - {R_F8_PULSE_B_POS,2, - "Pulse B Position"}, - {R_FA_PULSE_C_POS,2, - "Pulse C Position"}, - /* 0xfc to 0xfe - Reserved */ - {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1, - "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"}, -}; -#endif diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c deleted file mode 100644 index 8ecb6564a315..000000000000 --- a/drivers/media/video/saa7127.c +++ /dev/null @@ -1,852 +0,0 @@ -/* - * saa7127 - Philips SAA7127/SAA7129 video encoder driver - * - * Copyright (C) 2003 Roy Bulter - * - * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter - * - * Copyright (C) 2000-2001 Gillem - * Copyright (C) 2002 Andreas Oberritter - * - * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo - * - * Copyright (C) 1999 Nathan Laredo - * - * This driver is designed for the Hauppauge 250/350 Linux driver - * from the ivtv Project - * - * Copyright (C) 2003 Kevin Thayer - * - * Dual output support: - * Copyright (C) 2004 Eric Varsanyi - * - * NTSC Tuning and 7.5 IRE Setup - * Copyright (C) 2004 Chris Kennedy - * - * VBI additions & cleanup: - * Copyright (C) 2004, 2005 Hans Verkuil - * - * Note: the saa7126 is identical to the saa7127, and the saa7128 is - * identical to the saa7129, except that the saa7126 and saa7128 have - * macrovision anti-taping support. This driver will almost certainly - * work fine for those chips, except of course for the missing anti-taping - * support. - * - * This program is free software; 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 - -static int debug; -static int test_image; - -MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver"); -MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); -MODULE_LICENSE("GPL"); -module_param(debug, int, 0644); -module_param(test_image, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); -MODULE_PARM_DESC(test_image, "test_image (0-1)"); - - -/* - * SAA7127 registers - */ - -#define SAA7127_REG_STATUS 0x00 -#define SAA7127_REG_WIDESCREEN_CONFIG 0x26 -#define SAA7127_REG_WIDESCREEN_ENABLE 0x27 -#define SAA7127_REG_BURST_START 0x28 -#define SAA7127_REG_BURST_END 0x29 -#define SAA7127_REG_COPYGEN_0 0x2a -#define SAA7127_REG_COPYGEN_1 0x2b -#define SAA7127_REG_COPYGEN_2 0x2c -#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d -#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 -#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 -#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a -#define SAA7129_REG_FADE_KEY_COL2 0x4f -#define SAA7127_REG_CHROMA_PHASE 0x5a -#define SAA7127_REG_GAINU 0x5b -#define SAA7127_REG_GAINV 0x5c -#define SAA7127_REG_BLACK_LEVEL 0x5d -#define SAA7127_REG_BLANKING_LEVEL 0x5e -#define SAA7127_REG_VBI_BLANKING 0x5f -#define SAA7127_REG_DAC_CONTROL 0x61 -#define SAA7127_REG_BURST_AMP 0x62 -#define SAA7127_REG_SUBC3 0x63 -#define SAA7127_REG_SUBC2 0x64 -#define SAA7127_REG_SUBC1 0x65 -#define SAA7127_REG_SUBC0 0x66 -#define SAA7127_REG_LINE_21_ODD_0 0x67 -#define SAA7127_REG_LINE_21_ODD_1 0x68 -#define SAA7127_REG_LINE_21_EVEN_0 0x69 -#define SAA7127_REG_LINE_21_EVEN_1 0x6a -#define SAA7127_REG_RCV_PORT_CONTROL 0x6b -#define SAA7127_REG_VTRIG 0x6c -#define SAA7127_REG_HTRIG_HI 0x6d -#define SAA7127_REG_MULTI 0x6e -#define SAA7127_REG_CLOSED_CAPTION 0x6f -#define SAA7127_REG_RCV2_OUTPUT_START 0x70 -#define SAA7127_REG_RCV2_OUTPUT_END 0x71 -#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 -#define SAA7127_REG_TTX_REQUEST_H_START 0x73 -#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 -#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 -#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 -#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 -#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 -#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 -#define SAA7127_REG_FIRST_ACTIVE 0x7a -#define SAA7127_REG_LAST_ACTIVE 0x7b -#define SAA7127_REG_MSB_VERTICAL 0x7c -#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e -#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f - -/* - ********************************************************************** - * - * Arrays with configuration parameters for the SAA7127 - * - ********************************************************************** - */ - -struct i2c_reg_value { - unsigned char reg; - unsigned char value; -}; - -static const struct i2c_reg_value saa7129_init_config_extra[] = { - { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, - { SAA7127_REG_VTRIG, 0xfa }, - { 0, 0 } -}; - -static const struct i2c_reg_value saa7127_init_config_common[] = { - { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d }, - { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 }, - { SAA7127_REG_COPYGEN_0, 0x77 }, - { SAA7127_REG_COPYGEN_1, 0x41 }, - { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */ - { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf }, - { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 }, - { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 }, - { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */ - { SAA7127_REG_LINE_21_ODD_0, 0x77 }, - { SAA7127_REG_LINE_21_ODD_1, 0x41 }, - { SAA7127_REG_LINE_21_EVEN_0, 0x88 }, - { SAA7127_REG_LINE_21_EVEN_1, 0x41 }, - { SAA7127_REG_RCV_PORT_CONTROL, 0x12 }, - { SAA7127_REG_VTRIG, 0xf9 }, - { SAA7127_REG_HTRIG_HI, 0x00 }, - { SAA7127_REG_RCV2_OUTPUT_START, 0x41 }, - { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 }, - { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 }, - { SAA7127_REG_TTX_REQUEST_H_START, 0x3e }, - { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 }, - { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 }, - { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 }, - { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 }, - { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 }, - { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 }, - { SAA7127_REG_FIRST_ACTIVE, 0x1a }, - { SAA7127_REG_LAST_ACTIVE, 0x01 }, - { SAA7127_REG_MSB_VERTICAL, 0xc0 }, - { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 }, - { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 }, - { 0, 0 } -}; - -#define SAA7127_60HZ_DAC_CONTROL 0x15 -static const struct i2c_reg_value saa7127_init_config_60hz[] = { - { SAA7127_REG_BURST_START, 0x19 }, - /* BURST_END is also used as a chip ID in saa7127_probe */ - { SAA7127_REG_BURST_END, 0x1d }, - { SAA7127_REG_CHROMA_PHASE, 0xa3 }, - { SAA7127_REG_GAINU, 0x98 }, - { SAA7127_REG_GAINV, 0xd3 }, - { SAA7127_REG_BLACK_LEVEL, 0x39 }, - { SAA7127_REG_BLANKING_LEVEL, 0x2e }, - { SAA7127_REG_VBI_BLANKING, 0x2e }, - { SAA7127_REG_DAC_CONTROL, 0x15 }, - { SAA7127_REG_BURST_AMP, 0x4d }, - { SAA7127_REG_SUBC3, 0x1f }, - { SAA7127_REG_SUBC2, 0x7c }, - { SAA7127_REG_SUBC1, 0xf0 }, - { SAA7127_REG_SUBC0, 0x21 }, - { SAA7127_REG_MULTI, 0x90 }, - { SAA7127_REG_CLOSED_CAPTION, 0x11 }, - { 0, 0 } -}; - -#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02 -static struct i2c_reg_value saa7127_init_config_50hz_pal[] = { - { SAA7127_REG_BURST_START, 0x21 }, - /* BURST_END is also used as a chip ID in saa7127_probe */ - { SAA7127_REG_BURST_END, 0x1d }, - { SAA7127_REG_CHROMA_PHASE, 0x3f }, - { SAA7127_REG_GAINU, 0x7d }, - { SAA7127_REG_GAINV, 0xaf }, - { SAA7127_REG_BLACK_LEVEL, 0x33 }, - { SAA7127_REG_BLANKING_LEVEL, 0x35 }, - { SAA7127_REG_VBI_BLANKING, 0x35 }, - { SAA7127_REG_DAC_CONTROL, 0x02 }, - { SAA7127_REG_BURST_AMP, 0x2f }, - { SAA7127_REG_SUBC3, 0xcb }, - { SAA7127_REG_SUBC2, 0x8a }, - { SAA7127_REG_SUBC1, 0x09 }, - { SAA7127_REG_SUBC0, 0x2a }, - { SAA7127_REG_MULTI, 0xa0 }, - { SAA7127_REG_CLOSED_CAPTION, 0x00 }, - { 0, 0 } -}; - -#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08 -static struct i2c_reg_value saa7127_init_config_50hz_secam[] = { - { SAA7127_REG_BURST_START, 0x21 }, - /* BURST_END is also used as a chip ID in saa7127_probe */ - { SAA7127_REG_BURST_END, 0x1d }, - { SAA7127_REG_CHROMA_PHASE, 0x3f }, - { SAA7127_REG_GAINU, 0x6a }, - { SAA7127_REG_GAINV, 0x81 }, - { SAA7127_REG_BLACK_LEVEL, 0x33 }, - { SAA7127_REG_BLANKING_LEVEL, 0x35 }, - { SAA7127_REG_VBI_BLANKING, 0x35 }, - { SAA7127_REG_DAC_CONTROL, 0x08 }, - { SAA7127_REG_BURST_AMP, 0x2f }, - { SAA7127_REG_SUBC3, 0xb2 }, - { SAA7127_REG_SUBC2, 0x3b }, - { SAA7127_REG_SUBC1, 0xa3 }, - { SAA7127_REG_SUBC0, 0x28 }, - { SAA7127_REG_MULTI, 0x90 }, - { SAA7127_REG_CLOSED_CAPTION, 0x00 }, - { 0, 0 } -}; - -/* - ********************************************************************** - * - * Encoder Struct, holds the configuration state of the encoder - * - ********************************************************************** - */ - -struct saa7127_state { - struct v4l2_subdev sd; - v4l2_std_id std; - u32 ident; - enum saa7127_input_type input_type; - enum saa7127_output_type output_type; - int video_enable; - int wss_enable; - u16 wss_mode; - int cc_enable; - u16 cc_data; - int xds_enable; - u16 xds_data; - int vps_enable; - u8 vps_data[5]; - u8 reg_2d; - u8 reg_3a; - u8 reg_3a_cb; /* colorbar bit */ - u8 reg_61; -}; - -static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7127_state, sd); -} - -static const char * const output_strs[] = -{ - "S-Video + Composite", - "Composite", - "S-Video", - "RGB", - "YUV C", - "YUV V" -}; - -static const char * const wss_strs[] = { - "invalid", - "letterbox 14:9 center", - "letterbox 14:9 top", - "invalid", - "letterbox 16:9 top", - "invalid", - "invalid", - "16:9 full format anamorphic", - "4:3 full format", - "invalid", - "invalid", - "letterbox 16:9 center", - "invalid", - "letterbox >16:9 center", - "14:9 full format center", - "invalid", -}; - -/* ----------------------------------------------------------------------- */ - -static int saa7127_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - for (i = 0; i < 3; i++) { - if (i2c_smbus_write_byte_data(client, reg, val) == 0) - return 0; - } - v4l2_err(sd, "I2C Write Problem\n"); - return -1; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_write_inittab(struct v4l2_subdev *sd, - const struct i2c_reg_value *regs) -{ - while (regs->reg != 0) { - saa7127_write(sd, regs->reg, regs->value); - regs++; - } - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - int enable = (data->line != 0); - - if (enable && (data->field != 0 || data->line != 16)) - return -EINVAL; - if (state->vps_enable != enable) { - v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); - saa7127_write(sd, 0x54, enable << 7); - state->vps_enable = enable; - } - if (!enable) - return 0; - - state->vps_data[0] = data->data[2]; - state->vps_data[1] = data->data[8]; - state->vps_data[2] = data->data[9]; - state->vps_data[3] = data->data[10]; - state->vps_data[4] = data->data[11]; - v4l2_dbg(1, debug, sd, "Set VPS data %*ph\n", 5, state->vps_data); - saa7127_write(sd, 0x55, state->vps_data[0]); - saa7127_write(sd, 0x56, state->vps_data[1]); - saa7127_write(sd, 0x57, state->vps_data[2]); - saa7127_write(sd, 0x58, state->vps_data[3]); - saa7127_write(sd, 0x59, state->vps_data[4]); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - u16 cc = data->data[1] << 8 | data->data[0]; - int enable = (data->line != 0); - - if (enable && (data->field != 0 || data->line != 21)) - return -EINVAL; - if (state->cc_enable != enable) { - v4l2_dbg(1, debug, sd, - "Turn CC %s\n", enable ? "on" : "off"); - saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, - (state->xds_enable << 7) | (enable << 6) | 0x11); - state->cc_enable = enable; - } - if (!enable) - return 0; - - v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); - saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); - saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); - state->cc_data = cc; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - u16 xds = data->data[1] << 8 | data->data[0]; - int enable = (data->line != 0); - - if (enable && (data->field != 1 || data->line != 21)) - return -EINVAL; - if (state->xds_enable != enable) { - v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); - saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, - (enable << 7) | (state->cc_enable << 6) | 0x11); - state->xds_enable = enable; - } - if (!enable) - return 0; - - v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); - saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); - saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); - state->xds_data = xds; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - struct saa7127_state *state = to_state(sd); - int enable = (data->line != 0); - - if (enable && (data->field != 0 || data->line != 23)) - return -EINVAL; - if (state->wss_enable != enable) { - v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); - saa7127_write(sd, 0x27, enable << 7); - state->wss_enable = enable; - } - if (!enable) - return 0; - - saa7127_write(sd, 0x26, data->data[0]); - saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); - v4l2_dbg(1, debug, sd, - "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); - state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) -{ - struct saa7127_state *state = to_state(sd); - - if (enable) { - v4l2_dbg(1, debug, sd, "Enable Video Output\n"); - saa7127_write(sd, 0x2d, state->reg_2d); - saa7127_write(sd, 0x61, state->reg_61); - } else { - v4l2_dbg(1, debug, sd, "Disable Video Output\n"); - saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); - saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); - } - state->video_enable = enable; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7127_state *state = to_state(sd); - const struct i2c_reg_value *inittab; - - if (std & V4L2_STD_525_60) { - v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); - inittab = saa7127_init_config_60hz; - state->reg_61 = SAA7127_60HZ_DAC_CONTROL; - - } else if (state->ident == V4L2_IDENT_SAA7129 && - (std & V4L2_STD_SECAM) && - !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) { - - /* If and only if SECAM, with a SAA712[89] */ - v4l2_dbg(1, debug, sd, - "Selecting 50 Hz SECAM video Standard\n"); - inittab = saa7127_init_config_50hz_secam; - state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL; - - } else { - v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n"); - inittab = saa7127_init_config_50hz_pal; - state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL; - } - - /* Write Table */ - saa7127_write_inittab(sd, inittab); - state->std = std; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) -{ - struct saa7127_state *state = to_state(sd); - - switch (output) { - case SAA7127_OUTPUT_TYPE_RGB: - state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - case SAA7127_OUTPUT_TYPE_COMPOSITE: - if (state->ident == V4L2_IDENT_SAA7129) - state->reg_2d = 0x20; /* CVBS only */ - else - state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - case SAA7127_OUTPUT_TYPE_SVIDEO: - if (state->ident == V4L2_IDENT_SAA7129) - state->reg_2d = 0x18; /* Y + C */ - else - state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */ - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - case SAA7127_OUTPUT_TYPE_YUV_V: - state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ - state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ - break; - - case SAA7127_OUTPUT_TYPE_YUV_C: - state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ - state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ - break; - - case SAA7127_OUTPUT_TYPE_BOTH: - if (state->ident == V4L2_IDENT_SAA7129) - state->reg_2d = 0x38; - else - state->reg_2d = 0xbf; - state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ - break; - - default: - return -EINVAL; - } - v4l2_dbg(1, debug, sd, - "Selecting %s output type\n", output_strs[output]); - - /* Configure Encoder */ - saa7127_write(sd, 0x2d, state->reg_2d); - saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); - state->output_type = output; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) -{ - struct saa7127_state *state = to_state(sd); - - switch (input) { - case SAA7127_INPUT_TYPE_NORMAL: /* avia */ - v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); - state->reg_3a_cb = 0; - break; - - case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ - v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); - state->reg_3a_cb = 0x80; - break; - - default: - return -EINVAL; - } - saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); - state->input_type = input; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7127_state *state = to_state(sd); - - if (state->std == std) - return 0; - return saa7127_set_std(sd, std); -} - -static int saa7127_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7127_state *state = to_state(sd); - int rc = 0; - - if (state->input_type != input) - rc = saa7127_set_input_type(sd, input); - if (rc == 0 && state->output_type != output) - rc = saa7127_set_output_type(sd, output); - return rc; -} - -static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa7127_state *state = to_state(sd); - - if (state->video_enable == enable) - return 0; - return saa7127_set_video_enable(sd, enable); -} - -static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) -{ - struct saa7127_state *state = to_state(sd); - - memset(fmt, 0, sizeof(*fmt)); - if (state->vps_enable) - fmt->service_lines[0][16] = V4L2_SLICED_VPS; - if (state->wss_enable) - fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; - if (state->cc_enable) { - fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; - } - fmt->service_set = - (state->vps_enable ? V4L2_SLICED_VPS : 0) | - (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | - (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); - return 0; -} - -static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) -{ - switch (data->id) { - case V4L2_SLICED_WSS_625: - return saa7127_set_wss(sd, data); - case V4L2_SLICED_VPS: - return saa7127_set_vps(sd, data); - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) - return saa7127_set_cc(sd, data); - return saa7127_set_xds(sd, data); - default: - return -EINVAL; - } - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa7127_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct saa7127_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); -} - -static int saa7127_log_status(struct v4l2_subdev *sd) -{ - struct saa7127_state *state = to_state(sd); - - v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); - v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); - v4l2_info(sd, "Output: %s\n", state->video_enable ? - output_strs[state->output_type] : "disabled"); - v4l2_info(sd, "WSS: %s\n", state->wss_enable ? - wss_strs[state->wss_mode] : "disabled"); - v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); - v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa7127_core_ops = { - .log_status = saa7127_log_status, - .g_chip_ident = saa7127_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = saa7127_g_register, - .s_register = saa7127_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops saa7127_video_ops = { - .s_std_output = saa7127_s_std_output, - .s_routing = saa7127_s_routing, - .s_stream = saa7127_s_stream, -}; - -static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { - .s_vbi_data = saa7127_s_vbi_data, - .g_sliced_fmt = saa7127_g_sliced_fmt, -}; - -static const struct v4l2_subdev_ops saa7127_ops = { - .core = &saa7127_core_ops, - .video = &saa7127_video_ops, - .vbi = &saa7127_vbi_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int saa7127_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct saa7127_state *state; - struct v4l2_subdev *sd; - struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", - client->addr << 1); - - state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &saa7127_ops); - - /* First test register 0: Bits 5-7 are a version ID (should be 0), - and bit 2 should also be 0. - This is rather general, so the second test is more specific and - looks at the 'ending point of burst in clock cycles' which is - 0x1d after a reset and not expected to ever change. */ - if ((saa7127_read(sd, 0) & 0xe4) != 0 || - (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { - v4l2_dbg(1, debug, sd, "saa7127 not found\n"); - kfree(state); - return -ENODEV; - } - - if (id->driver_data) { /* Chip type is already known */ - state->ident = id->driver_data; - } else { /* Needs detection */ - int read_result; - - /* Detect if it's an saa7129 */ - read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); - saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); - if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { - saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, - read_result); - state->ident = V4L2_IDENT_SAA7129; - strlcpy(client->name, "saa7129", I2C_NAME_SIZE); - } else { - state->ident = V4L2_IDENT_SAA7127; - strlcpy(client->name, "saa7127", I2C_NAME_SIZE); - } - } - - v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, - client->addr << 1, client->adapter->name); - - v4l2_dbg(1, debug, sd, "Configuring encoder\n"); - saa7127_write_inittab(sd, saa7127_init_config_common); - saa7127_set_std(sd, V4L2_STD_NTSC); - saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); - saa7127_set_vps(sd, &vbi); - saa7127_set_wss(sd, &vbi); - saa7127_set_cc(sd, &vbi); - saa7127_set_xds(sd, &vbi); - if (test_image == 1) - /* The Encoder has an internal Colorbar generator */ - /* This can be used for debugging */ - saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); - else - saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); - saa7127_set_video_enable(sd, 1); - - if (state->ident == V4L2_IDENT_SAA7129) - saa7127_write_inittab(sd, saa7129_init_config_extra); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int saa7127_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - /* Turn off TV output */ - saa7127_set_video_enable(sd, 0); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_device_id saa7127_id[] = { - { "saa7127_auto", 0 }, /* auto-detection */ - { "saa7126", V4L2_IDENT_SAA7127 }, - { "saa7127", V4L2_IDENT_SAA7127 }, - { "saa7128", V4L2_IDENT_SAA7129 }, - { "saa7129", V4L2_IDENT_SAA7129 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7127_id); - -static struct i2c_driver saa7127_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7127", - }, - .probe = saa7127_probe, - .remove = saa7127_remove, - .id_table = saa7127_id, -}; - -module_i2c_driver(saa7127_driver); diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c deleted file mode 100644 index 1e84466515aa..000000000000 --- a/drivers/media/video/saa717x.c +++ /dev/null @@ -1,1378 +0,0 @@ -/* - * saa717x - Philips SAA717xHL video decoder driver - * - * Based on the saa7115 driver - * - * Changes by Ohta Kyuma - * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004) - * - * Changes by T.Adachi (tadachi@tadachi-net.com) - * - support audio, video scaler etc, and checked the initialize sequence. - * - * Cleaned up by Hans Verkuil - * - * Note: this is a reversed engineered driver based on captures from - * the I2C bus under Windows. This chip is very similar to the saa7134, - * though. Unfortunately, this driver is currently only working for NTSC. - * - * This program is free software; 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 - -MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); -MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -struct saa717x_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - v4l2_std_id std; - int input; - int enable; - int radio; - int playback; - int audio; - int tuner_audio_mode; - int audio_main_mute; - int audio_main_vol_r; - int audio_main_vol_l; - u16 audio_main_bass; - u16 audio_main_treble; - u16 audio_main_volume; - u16 audio_main_balance; - int audio_input; -}; - -static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa717x_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd; -} - -/* ----------------------------------------------------------------------- */ - -/* for audio mode */ -#define TUNER_AUDIO_MONO 0 /* LL */ -#define TUNER_AUDIO_STEREO 1 /* LR */ -#define TUNER_AUDIO_LANG1 2 /* LL */ -#define TUNER_AUDIO_LANG2 3 /* RR */ - -#define SAA717X_NTSC_WIDTH (704) -#define SAA717X_NTSC_HEIGHT (480) - -/* ----------------------------------------------------------------------- */ - -static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_adapter *adap = client->adapter; - int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; - unsigned char mm1[6]; - struct i2c_msg msg; - - msg.flags = 0; - msg.addr = client->addr; - mm1[0] = (reg >> 8) & 0xff; - mm1[1] = reg & 0xff; - - if (fw_addr) { - mm1[4] = (value >> 16) & 0xff; - mm1[3] = (value >> 8) & 0xff; - mm1[2] = value & 0xff; - } else { - mm1[2] = value & 0xff; - } - msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ - msg.buf = mm1; - v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); - return i2c_transfer(adap, &msg, 1) == 1; -} - -static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) -{ - while (data[0] || data[1]) { - saa717x_write(sd, data[0], data[1]); - data += 2; - } -} - -static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_adapter *adap = client->adapter; - int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; - unsigned char mm1[2]; - unsigned char mm2[4] = { 0, 0, 0, 0 }; - struct i2c_msg msgs[2]; - u32 value; - - msgs[0].flags = 0; - msgs[1].flags = I2C_M_RD; - msgs[0].addr = msgs[1].addr = client->addr; - mm1[0] = (reg >> 8) & 0xff; - mm1[1] = reg & 0xff; - msgs[0].len = 2; - msgs[0].buf = mm1; - msgs[1].len = fw_addr ? 3 : 1; /* Multibyte Registers contains *only* 3 bytes */ - msgs[1].buf = mm2; - i2c_transfer(adap, msgs, 2); - - if (fw_addr) - value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16); - else - value = mm2[0] & 0xff; - - v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); - return value; -} - -/* ----------------------------------------------------------------------- */ - -static u32 reg_init_initialize[] = -{ - /* from linux driver */ - 0x101, 0x008, /* Increment delay */ - - 0x103, 0x000, /* Analog input control 2 */ - 0x104, 0x090, /* Analog input control 3 */ - 0x105, 0x090, /* Analog input control 4 */ - 0x106, 0x0eb, /* Horizontal sync start */ - 0x107, 0x0e0, /* Horizontal sync stop */ - 0x109, 0x055, /* Luminance control */ - - 0x10f, 0x02a, /* Chroma gain control */ - 0x110, 0x000, /* Chroma control 2 */ - - 0x114, 0x045, /* analog/ADC */ - - 0x118, 0x040, /* RAW data gain */ - 0x119, 0x080, /* RAW data offset */ - - 0x044, 0x000, /* VBI horizontal input window start (L) TASK A */ - 0x045, 0x000, /* VBI horizontal input window start (H) TASK A */ - 0x046, 0x0cf, /* VBI horizontal input window stop (L) TASK A */ - 0x047, 0x002, /* VBI horizontal input window stop (H) TASK A */ - - 0x049, 0x000, /* VBI vertical input window start (H) TASK A */ - - 0x04c, 0x0d0, /* VBI horizontal output length (L) TASK A */ - 0x04d, 0x002, /* VBI horizontal output length (H) TASK A */ - - 0x064, 0x080, /* Lumina brightness TASK A */ - 0x065, 0x040, /* Luminance contrast TASK A */ - 0x066, 0x040, /* Chroma saturation TASK A */ - /* 067H: Reserved */ - 0x068, 0x000, /* VBI horizontal scaling increment (L) TASK A */ - 0x069, 0x004, /* VBI horizontal scaling increment (H) TASK A */ - 0x06a, 0x000, /* VBI phase offset TASK A */ - - 0x06e, 0x000, /* Horizontal phase offset Luma TASK A */ - 0x06f, 0x000, /* Horizontal phase offset Chroma TASK A */ - - 0x072, 0x000, /* Vertical filter mode TASK A */ - - 0x084, 0x000, /* VBI horizontal input window start (L) TAKS B */ - 0x085, 0x000, /* VBI horizontal input window start (H) TAKS B */ - 0x086, 0x0cf, /* VBI horizontal input window stop (L) TAKS B */ - 0x087, 0x002, /* VBI horizontal input window stop (H) TAKS B */ - - 0x089, 0x000, /* VBI vertical input window start (H) TAKS B */ - - 0x08c, 0x0d0, /* VBI horizontal output length (L) TASK B */ - 0x08d, 0x002, /* VBI horizontal output length (H) TASK B */ - - 0x0a4, 0x080, /* Lumina brightness TASK B */ - 0x0a5, 0x040, /* Luminance contrast TASK B */ - 0x0a6, 0x040, /* Chroma saturation TASK B */ - /* 0A7H reserved */ - 0x0a8, 0x000, /* VBI horizontal scaling increment (L) TASK B */ - 0x0a9, 0x004, /* VBI horizontal scaling increment (H) TASK B */ - 0x0aa, 0x000, /* VBI phase offset TASK B */ - - 0x0ae, 0x000, /* Horizontal phase offset Luma TASK B */ - 0x0af, 0x000, /*Horizontal phase offset Chroma TASK B */ - - 0x0b2, 0x000, /* Vertical filter mode TASK B */ - - 0x00c, 0x000, /* Start point GREEN path */ - 0x00d, 0x000, /* Start point BLUE path */ - 0x00e, 0x000, /* Start point RED path */ - - 0x010, 0x010, /* GREEN path gamma curve --- */ - 0x011, 0x020, - 0x012, 0x030, - 0x013, 0x040, - 0x014, 0x050, - 0x015, 0x060, - 0x016, 0x070, - 0x017, 0x080, - 0x018, 0x090, - 0x019, 0x0a0, - 0x01a, 0x0b0, - 0x01b, 0x0c0, - 0x01c, 0x0d0, - 0x01d, 0x0e0, - 0x01e, 0x0f0, - 0x01f, 0x0ff, /* --- GREEN path gamma curve */ - - 0x020, 0x010, /* BLUE path gamma curve --- */ - 0x021, 0x020, - 0x022, 0x030, - 0x023, 0x040, - 0x024, 0x050, - 0x025, 0x060, - 0x026, 0x070, - 0x027, 0x080, - 0x028, 0x090, - 0x029, 0x0a0, - 0x02a, 0x0b0, - 0x02b, 0x0c0, - 0x02c, 0x0d0, - 0x02d, 0x0e0, - 0x02e, 0x0f0, - 0x02f, 0x0ff, /* --- BLUE path gamma curve */ - - 0x030, 0x010, /* RED path gamma curve --- */ - 0x031, 0x020, - 0x032, 0x030, - 0x033, 0x040, - 0x034, 0x050, - 0x035, 0x060, - 0x036, 0x070, - 0x037, 0x080, - 0x038, 0x090, - 0x039, 0x0a0, - 0x03a, 0x0b0, - 0x03b, 0x0c0, - 0x03c, 0x0d0, - 0x03d, 0x0e0, - 0x03e, 0x0f0, - 0x03f, 0x0ff, /* --- RED path gamma curve */ - - 0x109, 0x085, /* Luminance control */ - - /**** from app start ****/ - 0x584, 0x000, /* AGC gain control */ - 0x585, 0x000, /* Program count */ - 0x586, 0x003, /* Status reset */ - 0x588, 0x0ff, /* Number of audio samples (L) */ - 0x589, 0x00f, /* Number of audio samples (M) */ - 0x58a, 0x000, /* Number of audio samples (H) */ - 0x58b, 0x000, /* Audio select */ - 0x58c, 0x010, /* Audio channel assign1 */ - 0x58d, 0x032, /* Audio channel assign2 */ - 0x58e, 0x054, /* Audio channel assign3 */ - 0x58f, 0x023, /* Audio format */ - 0x590, 0x000, /* SIF control */ - - 0x595, 0x000, /* ?? */ - 0x596, 0x000, /* ?? */ - 0x597, 0x000, /* ?? */ - - 0x464, 0x00, /* Digital input crossbar1 */ - - 0x46c, 0xbbbb10, /* Digital output selection1-3 */ - 0x470, 0x101010, /* Digital output selection4-6 */ - - 0x478, 0x00, /* Sound feature control */ - - 0x474, 0x18, /* Softmute control */ - - 0x454, 0x0425b9, /* Sound Easy programming(reset) */ - 0x454, 0x042539, /* Sound Easy programming(reset) */ - - - /**** common setting( of DVD play, including scaler commands) ****/ - 0x042, 0x003, /* Data path configuration for VBI (TASK A) */ - - 0x082, 0x003, /* Data path configuration for VBI (TASK B) */ - - 0x108, 0x0f8, /* Sync control */ - 0x2a9, 0x0fd, /* ??? */ - 0x102, 0x089, /* select video input "mode 9" */ - 0x111, 0x000, /* Mode/delay control */ - - 0x10e, 0x00a, /* Chroma control 1 */ - - 0x594, 0x002, /* SIF, analog I/O select */ - - 0x454, 0x0425b9, /* Sound */ - 0x454, 0x042539, - - 0x111, 0x000, - 0x10e, 0x00a, - 0x464, 0x000, - 0x300, 0x000, - 0x301, 0x006, - 0x302, 0x000, - 0x303, 0x006, - 0x308, 0x040, - 0x309, 0x000, - 0x30a, 0x000, - 0x30b, 0x000, - 0x000, 0x002, - 0x001, 0x000, - 0x002, 0x000, - 0x003, 0x000, - 0x004, 0x033, - 0x040, 0x01d, - 0x041, 0x001, - 0x042, 0x004, - 0x043, 0x000, - 0x080, 0x01e, - 0x081, 0x001, - 0x082, 0x004, - 0x083, 0x000, - 0x190, 0x018, - 0x115, 0x000, - 0x116, 0x012, - 0x117, 0x018, - 0x04a, 0x011, - 0x08a, 0x011, - 0x04b, 0x000, - 0x08b, 0x000, - 0x048, 0x000, - 0x088, 0x000, - 0x04e, 0x012, - 0x08e, 0x012, - 0x058, 0x012, - 0x098, 0x012, - 0x059, 0x000, - 0x099, 0x000, - 0x05a, 0x003, - 0x09a, 0x003, - 0x05b, 0x001, - 0x09b, 0x001, - 0x054, 0x008, - 0x094, 0x008, - 0x055, 0x000, - 0x095, 0x000, - 0x056, 0x0c7, - 0x096, 0x0c7, - 0x057, 0x002, - 0x097, 0x002, - 0x0ff, 0x0ff, - 0x060, 0x001, - 0x0a0, 0x001, - 0x061, 0x000, - 0x0a1, 0x000, - 0x062, 0x000, - 0x0a2, 0x000, - 0x063, 0x000, - 0x0a3, 0x000, - 0x070, 0x000, - 0x0b0, 0x000, - 0x071, 0x004, - 0x0b1, 0x004, - 0x06c, 0x0e9, - 0x0ac, 0x0e9, - 0x06d, 0x003, - 0x0ad, 0x003, - 0x05c, 0x0d0, - 0x09c, 0x0d0, - 0x05d, 0x002, - 0x09d, 0x002, - 0x05e, 0x0f2, - 0x09e, 0x0f2, - 0x05f, 0x000, - 0x09f, 0x000, - 0x074, 0x000, - 0x0b4, 0x000, - 0x075, 0x000, - 0x0b5, 0x000, - 0x076, 0x000, - 0x0b6, 0x000, - 0x077, 0x000, - 0x0b7, 0x000, - 0x195, 0x008, - 0x0ff, 0x0ff, - 0x108, 0x0f8, - 0x111, 0x000, - 0x10e, 0x00a, - 0x2a9, 0x0fd, - 0x464, 0x001, - 0x454, 0x042135, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - - - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x193, 0x000, - 0x300, 0x000, - 0x301, 0x006, - 0x302, 0x000, - 0x303, 0x006, - 0x308, 0x040, - 0x309, 0x000, - 0x30a, 0x000, - 0x30b, 0x000, - 0x000, 0x002, - 0x001, 0x000, - 0x002, 0x000, - 0x003, 0x000, - 0x004, 0x033, - 0x040, 0x01d, - 0x041, 0x001, - 0x042, 0x004, - 0x043, 0x000, - 0x080, 0x01e, - 0x081, 0x001, - 0x082, 0x004, - 0x083, 0x000, - 0x190, 0x018, - 0x115, 0x000, - 0x116, 0x012, - 0x117, 0x018, - 0x04a, 0x011, - 0x08a, 0x011, - 0x04b, 0x000, - 0x08b, 0x000, - 0x048, 0x000, - 0x088, 0x000, - 0x04e, 0x012, - 0x08e, 0x012, - 0x058, 0x012, - 0x098, 0x012, - 0x059, 0x000, - 0x099, 0x000, - 0x05a, 0x003, - 0x09a, 0x003, - 0x05b, 0x001, - 0x09b, 0x001, - 0x054, 0x008, - 0x094, 0x008, - 0x055, 0x000, - 0x095, 0x000, - 0x056, 0x0c7, - 0x096, 0x0c7, - 0x057, 0x002, - 0x097, 0x002, - 0x060, 0x001, - 0x0a0, 0x001, - 0x061, 0x000, - 0x0a1, 0x000, - 0x062, 0x000, - 0x0a2, 0x000, - 0x063, 0x000, - 0x0a3, 0x000, - 0x070, 0x000, - 0x0b0, 0x000, - 0x071, 0x004, - 0x0b1, 0x004, - 0x06c, 0x0e9, - 0x0ac, 0x0e9, - 0x06d, 0x003, - 0x0ad, 0x003, - 0x05c, 0x0d0, - 0x09c, 0x0d0, - 0x05d, 0x002, - 0x09d, 0x002, - 0x05e, 0x0f2, - 0x09e, 0x0f2, - 0x05f, 0x000, - 0x09f, 0x000, - 0x074, 0x000, - 0x0b4, 0x000, - 0x075, 0x000, - 0x0b5, 0x000, - 0x076, 0x000, - 0x0b6, 0x000, - 0x077, 0x000, - 0x0b7, 0x000, - 0x195, 0x008, - 0x598, 0x0e7, - 0x599, 0x07d, - 0x59a, 0x018, - 0x59c, 0x066, - 0x59d, 0x090, - 0x59e, 0x001, - 0x584, 0x000, - 0x585, 0x000, - 0x586, 0x003, - 0x588, 0x0ff, - 0x589, 0x00f, - 0x58a, 0x000, - 0x58b, 0x000, - 0x58c, 0x010, - 0x58d, 0x032, - 0x58e, 0x054, - 0x58f, 0x023, - 0x590, 0x000, - 0x595, 0x000, - 0x596, 0x000, - 0x597, 0x000, - 0x464, 0x000, - 0x46c, 0xbbbb10, - 0x470, 0x101010, - 0x478, 0x000, - 0x474, 0x018, - 0x454, 0x042135, - 0x193, 0x0a6, - 0x108, 0x0f8, - 0x042, 0x003, - 0x082, 0x003, - 0x454, 0x0425b9, - 0x454, 0x042539, - 0x193, 0x000, - 0x193, 0x0a6, - 0x464, 0x000, - - 0, 0 -}; - -/* Tuner */ -static u32 reg_init_tuner_input[] = { - 0x108, 0x0f8, /* Sync control */ - 0x111, 0x000, /* Mode/delay control */ - 0x10e, 0x00a, /* Chroma control 1 */ - 0, 0 -}; - -/* Composite */ -static u32 reg_init_composite_input[] = { - 0x108, 0x0e8, /* Sync control */ - 0x111, 0x000, /* Mode/delay control */ - 0x10e, 0x04a, /* Chroma control 1 */ - 0, 0 -}; - -/* S-Video */ -static u32 reg_init_svideo_input[] = { - 0x108, 0x0e8, /* Sync control */ - 0x111, 0x000, /* Mode/delay control */ - 0x10e, 0x04a, /* Chroma control 1 */ - 0, 0 -}; - -static u32 reg_set_audio_template[4][2] = -{ - { /* for MONO - tadachi 6/29 DMA audio output select? - Register 0x46c - 7-4: DMA2, 3-0: DMA1 ch. DMA4, DMA3 DMA2, DMA1 - 0: MAIN left, 1: MAIN right - 2: AUX1 left, 3: AUX1 right - 4: AUX2 left, 5: AUX2 right - 6: DPL left, 7: DPL right - 8: DPL center, 9: DPL surround - A: monitor output, B: digital sense */ - 0xbbbb00, - - /* tadachi 6/29 DAC and I2S output select? - Register 0x470 - 7-4:DAC right ch. 3-0:DAC left ch. - I2S1 right,left I2S2 right,left */ - 0x00, - }, - { /* for STEREO */ - 0xbbbb10, 0x101010, - }, - { /* for LANG1 */ - 0xbbbb00, 0x00, - }, - { /* for LANG2/SAP */ - 0xbbbb11, 0x111111, - } -}; - - -/* Get detected audio flags (from saa7134 driver) */ -static void get_inf_dev_status(struct v4l2_subdev *sd, - int *dual_flag, int *stereo_flag) -{ - u32 reg_data3; - - static char *stdres[0x20] = { - [0x00] = "no standard detected", - [0x01] = "B/G (in progress)", - [0x02] = "D/K (in progress)", - [0x03] = "M (in progress)", - - [0x04] = "B/G A2", - [0x05] = "B/G NICAM", - [0x06] = "D/K A2 (1)", - [0x07] = "D/K A2 (2)", - [0x08] = "D/K A2 (3)", - [0x09] = "D/K NICAM", - [0x0a] = "L NICAM", - [0x0b] = "I NICAM", - - [0x0c] = "M Korea", - [0x0d] = "M BTSC ", - [0x0e] = "M EIAJ", - - [0x0f] = "FM radio / IF 10.7 / 50 deemp", - [0x10] = "FM radio / IF 10.7 / 75 deemp", - [0x11] = "FM radio / IF sel / 50 deemp", - [0x12] = "FM radio / IF sel / 75 deemp", - - [0x13 ... 0x1e] = "unknown", - [0x1f] = "??? [in progress]", - }; - - - *dual_flag = *stereo_flag = 0; - - /* (demdec status: 0x528) */ - - /* read current status */ - reg_data3 = saa717x_read(sd, 0x0528); - - v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", - reg_data3, stdres[reg_data3 & 0x1f], - (reg_data3 & 0x000020) ? ",stereo" : "", - (reg_data3 & 0x000040) ? ",dual" : ""); - v4l2_dbg(1, debug, sd, "detailed status: " - "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", - (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", - (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", - (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "", - (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "", - - (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "", - (reg_data3 & 0x001000) ? " SAP carrier " : "", - (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "", - (reg_data3 & 0x004000) ? " SAP noise mute " : "", - (reg_data3 & 0x008000) ? " VDSP " : "", - - (reg_data3 & 0x010000) ? " NICST " : "", - (reg_data3 & 0x020000) ? " NICDU " : "", - (reg_data3 & 0x040000) ? " NICAM muted " : "", - (reg_data3 & 0x080000) ? " NICAM reserve sound " : "", - - (reg_data3 & 0x100000) ? " init done " : ""); - - if (reg_data3 & 0x000220) { - v4l2_dbg(1, debug, sd, "ST!!!\n"); - *stereo_flag = 1; - } - - if (reg_data3 & 0x000140) { - v4l2_dbg(1, debug, sd, "DUAL!!!\n"); - *dual_flag = 1; - } -} - -/* regs write to set audio mode */ -static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) -{ - v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", - audio_mode); - - saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); - saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); -} - -/* write regs to set audio volume, bass and treble */ -static int set_audio_regs(struct v4l2_subdev *sd, - struct saa717x_state *decoder) -{ - u8 mute = 0xac; /* -84 dB */ - u32 val; - unsigned int work_l, work_r; - - /* set SIF analog I/O select */ - saa717x_write(sd, 0x0594, decoder->audio_input); - v4l2_dbg(1, debug, sd, "set audio input %d\n", - decoder->audio_input); - - /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ - work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768; - work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768; - decoder->audio_main_vol_l = (long)work_l * (24 - (-40)) / 65535 - 40; - decoder->audio_main_vol_r = (long)work_r * (24 - (-40)) / 65535 - 40; - - /* set main volume */ - /* main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) */ - /* def:0dB->6dB(MPG600GR) */ - /* if mute is on, set mute */ - if (decoder->audio_main_mute) { - val = mute | (mute << 8); - } else { - val = (u8)decoder->audio_main_vol_l | - ((u8)decoder->audio_main_vol_r << 8); - } - - saa717x_write(sd, 0x480, val); - - /* set bass and treble */ - val = decoder->audio_main_bass & 0x1f; - val |= (decoder->audio_main_treble & 0x1f) << 5; - saa717x_write(sd, 0x488, val); - return 0; -} - -/********** scaling staff ***********/ -static void set_h_prescale(struct v4l2_subdev *sd, - int task, int prescale) -{ - static const struct { - int xpsc; - int xacl; - int xc2_1; - int xdcg; - int vpfy; - } vals[] = { - /* XPSC XACL XC2_1 XDCG VPFY */ - { 1, 0, 0, 0, 0 }, - { 2, 2, 1, 2, 2 }, - { 3, 4, 1, 3, 2 }, - { 4, 8, 1, 4, 2 }, - { 5, 8, 1, 4, 2 }, - { 6, 8, 1, 4, 3 }, - { 7, 8, 1, 4, 3 }, - { 8, 15, 0, 4, 3 }, - { 9, 15, 0, 4, 3 }, - { 10, 16, 1, 5, 3 }, - }; - static const int count = ARRAY_SIZE(vals); - int i, task_shift; - - task_shift = task * 0x40; - for (i = 0; i < count; i++) - if (vals[i].xpsc == prescale) - break; - if (i == count) - return; - - /* horizonal prescaling */ - saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); - /* accumulation length */ - saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); - /* level control */ - saa717x_write(sd, 0x62 + task_shift, - (vals[i].xc2_1 << 3) | vals[i].xdcg); - /*FIR prefilter control */ - saa717x_write(sd, 0x63 + task_shift, - (vals[i].vpfy << 2) | vals[i].vpfy); -} - -/********** scaling staff ***********/ -static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) -{ - int task_shift; - - task_shift = task * 0x40; - /* Vertical scaling ratio (LOW) */ - saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); - /* Vertical scaling ratio (HI) */ - saa717x_write(sd, 0x71 + task_shift, yscale >> 8); -} - -static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct saa717x_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - saa717x_write(sd, 0x10a, ctrl->val); - return 0; - - case V4L2_CID_CONTRAST: - saa717x_write(sd, 0x10b, ctrl->val); - return 0; - - case V4L2_CID_SATURATION: - saa717x_write(sd, 0x10c, ctrl->val); - return 0; - - case V4L2_CID_HUE: - saa717x_write(sd, 0x10d, ctrl->val); - return 0; - - case V4L2_CID_AUDIO_MUTE: - state->audio_main_mute = ctrl->val; - break; - - case V4L2_CID_AUDIO_VOLUME: - state->audio_main_volume = ctrl->val; - break; - - case V4L2_CID_AUDIO_BALANCE: - state->audio_main_balance = ctrl->val; - break; - - case V4L2_CID_AUDIO_TREBLE: - state->audio_main_treble = ctrl->val; - break; - - case V4L2_CID_AUDIO_BASS: - state->audio_main_bass = ctrl->val; - break; - - default: - return 0; - } - set_audio_regs(sd, state); - return 0; -} - -static int saa717x_s_video_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa717x_state *decoder = to_state(sd); - int is_tuner = input & 0x80; /* tuner input flag */ - - input &= 0x7f; - - v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); - /* inputs from 0-9 are available*/ - /* saa717x have mode0-mode9 but mode5 is reserved. */ - if (input > 9 || input == 5) - return -EINVAL; - - if (decoder->input != input) { - int input_line = input; - - decoder->input = input_line; - v4l2_dbg(1, debug, sd, "now setting %s input %d\n", - input_line >= 6 ? "S-Video" : "Composite", - input_line); - - /* select mode */ - saa717x_write(sd, 0x102, - (saa717x_read(sd, 0x102) & 0xf0) | - input_line); - - /* bypass chrominance trap for modes 6..9 */ - saa717x_write(sd, 0x109, - (saa717x_read(sd, 0x109) & 0x7f) | - (input_line < 6 ? 0x0 : 0x80)); - - /* change audio_mode */ - if (is_tuner) { - /* tuner */ - set_audio_mode(sd, decoder->tuner_audio_mode); - } else { - /* Force to STEREO mode if Composite or - * S-Video were chosen */ - set_audio_mode(sd, TUNER_AUDIO_STEREO); - } - /* change initialize procedure (Composite/S-Video) */ - if (is_tuner) - saa717x_write_regs(sd, reg_init_tuner_input); - else if (input_line >= 6) - saa717x_write_regs(sd, reg_init_svideo_input); - else - saa717x_write_regs(sd, reg_init_composite_input); - } - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa717x_read(sd, reg->reg); - reg->size = 1; - return 0; -} - -static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u16 addr = reg->reg & 0xffff; - u8 val = reg->val & 0xff; - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa717x_write(sd, addr, val); - return 0; -} -#endif - -static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - int prescale, h_scale, v_scale; - - v4l2_dbg(1, debug, sd, "decoder set size\n"); - - if (fmt->code != V4L2_MBUS_FMT_FIXED) - return -EINVAL; - - /* FIXME need better bounds checking here */ - if (fmt->width < 1 || fmt->width > 1440) - return -EINVAL; - if (fmt->height < 1 || fmt->height > 960) - return -EINVAL; - - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - - /* scaling setting */ - /* NTSC and interlace only */ - prescale = SAA717X_NTSC_WIDTH / fmt->width; - if (prescale == 0) - prescale = 1; - h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / fmt->width; - /* interlace */ - v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / fmt->height; - - /* Horizontal prescaling etc */ - set_h_prescale(sd, 0, prescale); - set_h_prescale(sd, 1, prescale); - - /* Horizontal scaling increment */ - /* TASK A */ - saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); - saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); - saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); - - /* Vertical prescaling etc */ - set_v_scale(sd, 0, v_scale); - set_v_scale(sd, 1, v_scale); - - /* set video output size */ - /* video number of pixels at output */ - /* TASK A */ - saa717x_write(sd, 0x5C, (u8)(fmt->width & 0xFF)); - saa717x_write(sd, 0x5D, (u8)((fmt->width >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(sd, 0x9C, (u8)(fmt->width & 0xFF)); - saa717x_write(sd, 0x9D, (u8)((fmt->width >> 8) & 0xFF)); - - /* video number of lines at output */ - /* TASK A */ - saa717x_write(sd, 0x5E, (u8)(fmt->height & 0xFF)); - saa717x_write(sd, 0x5F, (u8)((fmt->height >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(sd, 0x9E, (u8)(fmt->height & 0xFF)); - saa717x_write(sd, 0x9F, (u8)((fmt->height >> 8) & 0xFF)); - return 0; -} - -static int saa717x_s_radio(struct v4l2_subdev *sd) -{ - struct saa717x_state *decoder = to_state(sd); - - decoder->radio = 1; - return 0; -} - -static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa717x_state *decoder = to_state(sd); - - v4l2_dbg(1, debug, sd, "decoder set norm "); - v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); - - decoder->radio = 0; - decoder->std = std; - return 0; -} - -static int saa717x_s_audio_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa717x_state *decoder = to_state(sd); - - if (input < 3) { /* FIXME! --tadachi */ - decoder->audio_input = input; - v4l2_dbg(1, debug, sd, - "set decoder audio input to %d\n", - decoder->audio_input); - set_audio_regs(sd, decoder); - return 0; - } - return -ERANGE; -} - -static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct saa717x_state *decoder = to_state(sd); - - v4l2_dbg(1, debug, sd, "decoder %s output\n", - enable ? "enable" : "disable"); - decoder->enable = enable; - saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); - return 0; -} - -/* change audio mode */ -static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa717x_state *decoder = to_state(sd); - int audio_mode; - char *mes[4] = { - "MONO", "STEREO", "LANG1", "LANG2/SAP" - }; - - audio_mode = TUNER_AUDIO_STEREO; - - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - audio_mode = TUNER_AUDIO_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - audio_mode = TUNER_AUDIO_STEREO; - break; - case V4L2_TUNER_MODE_LANG2: - audio_mode = TUNER_AUDIO_LANG2; - break; - case V4L2_TUNER_MODE_LANG1: - audio_mode = TUNER_AUDIO_LANG1; - break; - } - - v4l2_dbg(1, debug, sd, "change audio mode to %s\n", - mes[audio_mode]); - decoder->tuner_audio_mode = audio_mode; - /* The registers are not changed here. */ - /* See DECODER_ENABLE_OUTPUT section. */ - set_audio_mode(sd, decoder->tuner_audio_mode); - return 0; -} - -static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct saa717x_state *decoder = to_state(sd); - int dual_f, stereo_f; - - if (decoder->radio) - return 0; - get_inf_dev_status(sd, &dual_f, &stereo_f); - - v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", - stereo_f, dual_f); - - /* mono */ - if ((dual_f == 0) && (stereo_f == 0)) { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); - } - - /* stereo */ - if (stereo_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_STEREO || - vt->audmode == V4L2_TUNER_MODE_LANG1) { - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); - } - } - - /* dual */ - if (dual_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_LANG2) { - vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; - v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); - } - } - return 0; -} - -static int saa717x_log_status(struct v4l2_subdev *sd) -{ - struct saa717x_state *state = to_state(sd); - - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops saa717x_ctrl_ops = { - .s_ctrl = saa717x_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops saa717x_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = saa717x_g_register, - .s_register = saa717x_s_register, -#endif - .s_std = saa717x_s_std, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .log_status = saa717x_log_status, -}; - -static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { - .g_tuner = saa717x_g_tuner, - .s_tuner = saa717x_s_tuner, - .s_radio = saa717x_s_radio, -}; - -static const struct v4l2_subdev_video_ops saa717x_video_ops = { - .s_routing = saa717x_s_video_routing, - .s_mbus_fmt = saa717x_s_mbus_fmt, - .s_stream = saa717x_s_stream, -}; - -static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { - .s_routing = saa717x_s_audio_routing, -}; - -static const struct v4l2_subdev_ops saa717x_ops = { - .core = &saa717x_core_ops, - .tuner = &saa717x_tuner_ops, - .audio = &saa717x_audio_ops, - .video = &saa717x_video_ops, -}; - -/* ----------------------------------------------------------------------- */ - - -/* i2c implementation */ - -/* ----------------------------------------------------------------------- */ -static int saa717x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct saa717x_state *decoder; - struct v4l2_ctrl_handler *hdl; - struct v4l2_subdev *sd; - u8 id = 0; - char *p = ""; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa717x_ops); - - if (saa717x_write(sd, 0x5a4, 0xfe) && - saa717x_write(sd, 0x5a5, 0x0f) && - saa717x_write(sd, 0x5a6, 0x00) && - saa717x_write(sd, 0x5a7, 0x01)) - id = saa717x_read(sd, 0x5a0); - if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { - v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); - kfree(decoder); - return -ENODEV; - } - if (id == 0xc2) - p = "saa7173"; - else if (id == 0x32) - p = "saa7174A"; - else if (id == 0x6c) - p = "saa7174HL"; - else - p = "saa7171"; - v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, - client->addr << 1, client->adapter->name); - - hdl = &decoder->hdl; - v4l2_ctrl_handler_init(hdl, 9); - /* add in ascending ID order */ - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 68); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 64); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_BASS, -16, 15, 1, 0); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0); - v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(decoder); - return err; - } - - decoder->std = V4L2_STD_NTSC; - decoder->input = -1; - decoder->enable = 1; - - /* FIXME!! */ - decoder->playback = 0; /* initially capture mode used */ - decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */ - - decoder->audio_input = 2; /* FIXME!! */ - - decoder->tuner_audio_mode = TUNER_AUDIO_STEREO; - /* set volume, bass and treble */ - decoder->audio_main_vol_l = 6; - decoder->audio_main_vol_r = 6; - - v4l2_dbg(1, debug, sd, "writing init values\n"); - - /* FIXME!! */ - saa717x_write_regs(sd, reg_init_initialize); - - v4l2_ctrl_handler_setup(hdl); - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(2*HZ); - return 0; -} - -static int saa717x_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa717x_id[] = { - { "saa717x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa717x_id); - -static struct i2c_driver saa717x_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa717x", - }, - .probe = saa717x_probe, - .remove = saa717x_remove, - .id_table = saa717x_id, -}; - -module_i2c_driver(saa717x_driver); diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c deleted file mode 100644 index 2c6b65c76e2b..000000000000 --- a/drivers/media/video/saa7185.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * saa7185 - Philips SAA7185B video encoder driver version 0.0.3 - * - * Copyright (C) 1998 Dave Perks - * - * Slight changes for video timing and attachment output by - * Wolfgang Scherr - * - * Changes by Ronald Bultje - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * This program is free software; 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 - -MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); -MODULE_AUTHOR("Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ----------------------------------------------------------------------- */ - -struct saa7185 { - struct v4l2_subdev sd; - unsigned char reg[128]; - - v4l2_std_id norm; -}; - -static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7185, sd); -} - -/* ----------------------------------------------------------------------- */ - -static inline int saa7185_read(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte(client); -} - -static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7185 *encoder = to_saa7185(sd); - - v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value); - encoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int saa7185_write_block(struct v4l2_subdev *sd, - const u8 *data, unsigned int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7185 *encoder = to_saa7185(sd); - int ret = -1; - u8 reg; - - /* the adv7175 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = - encoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = saa7185_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -/* ----------------------------------------------------------------------- */ - -static const unsigned char init_common[] = { - 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, - * YUV2C=1, MY2C=1, MUV2C=1 */ - - 0x42, 0x6b, /* OVLY0=107 */ - 0x43, 0x00, /* OVLU0=0 white */ - 0x44, 0x00, /* OVLV0=0 */ - 0x45, 0x22, /* OVLY1=34 */ - 0x46, 0xac, /* OVLU1=172 yellow */ - 0x47, 0x0e, /* OVLV1=14 */ - 0x48, 0x03, /* OVLY2=3 */ - 0x49, 0x1d, /* OVLU2=29 cyan */ - 0x4a, 0xac, /* OVLV2=172 */ - 0x4b, 0xf0, /* OVLY3=240 */ - 0x4c, 0xc8, /* OVLU3=200 green */ - 0x4d, 0xb9, /* OVLV3=185 */ - 0x4e, 0xd4, /* OVLY4=212 */ - 0x4f, 0x38, /* OVLU4=56 magenta */ - 0x50, 0x47, /* OVLV4=71 */ - 0x51, 0xc1, /* OVLY5=193 */ - 0x52, 0xe3, /* OVLU5=227 red */ - 0x53, 0x54, /* OVLV5=84 */ - 0x54, 0xa3, /* OVLY6=163 */ - 0x55, 0x54, /* OVLU6=84 blue */ - 0x56, 0xf2, /* OVLV6=242 */ - 0x57, 0x90, /* OVLY7=144 */ - 0x58, 0x00, /* OVLU7=0 black */ - 0x59, 0x00, /* OVLV7=0 */ - - 0x5a, 0x00, /* CHPS=0 */ - 0x5b, 0x76, /* GAINU=118 */ - 0x5c, 0xa5, /* GAINV=165 */ - 0x5d, 0x3c, /* BLCKL=60 */ - 0x5e, 0x3a, /* BLNNL=58 */ - 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ - 0x60, 0x00, /* NULL */ - - /* 0x61 - 0x66 set according to norm */ - - 0x67, 0x00, /* 0 : caption 1st byte odd field */ - 0x68, 0x00, /* 0 : caption 2nd byte odd field */ - 0x69, 0x00, /* 0 : caption 1st byte even field */ - 0x6a, 0x00, /* 0 : caption 2nd byte even field */ - - 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ - 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, - * CBLF=0, ORCV2=0, PRCV2=0 */ - 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ - - 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at - * least for PAL */ - 0x6f, 0x00, /* HTRIG upper bits */ - 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ - - /* The following should not be needed */ - - 0x71, 0x15, /* BMRQ=0x115 */ - 0x72, 0x90, /* EMRQ=0x690 */ - 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ - 0x74, 0x00, /* NULL */ - 0x75, 0x00, /* NULL */ - 0x76, 0x00, /* NULL */ - 0x77, 0x15, /* BRCV=0x115 */ - 0x78, 0x90, /* ERCV=0x690 */ - 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ - - /* Field length controls */ - - 0x7a, 0x70, /* FLC=0 */ - - /* The following should not be needed if SBLN = 1 */ - - 0x7b, 0x16, /* FAL=22 */ - 0x7c, 0x35, /* LAL=244 */ - 0x7d, 0x20, /* LAL=244, FAL=22 */ -}; - -static const unsigned char init_pal[] = { - 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, - * YGS=1, INPI=0, DOWN=0 */ - 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ - 0x63, 0xcb, /* FSC0 */ - 0x64, 0x8a, /* FSC1 */ - 0x65, 0x09, /* FSC2 */ - 0x66, 0x2a, /* FSC3 */ -}; - -static const unsigned char init_ntsc[] = { - 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, - * YGS=1, INPI=0, DOWN=0 */ - 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ - 0x63, 0x1f, /* FSC0 */ - 0x64, 0x7c, /* FSC1 */ - 0x65, 0xf0, /* FSC2 */ - 0x66, 0x21, /* FSC3 */ -}; - - -static int saa7185_init(struct v4l2_subdev *sd, u32 val) -{ - struct saa7185 *encoder = to_saa7185(sd); - - saa7185_write_block(sd, init_common, sizeof(init_common)); - if (encoder->norm & V4L2_STD_NTSC) - saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); - else - saa7185_write_block(sd, init_pal, sizeof(init_pal)); - return 0; -} - -static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct saa7185 *encoder = to_saa7185(sd); - - if (std & V4L2_STD_NTSC) - saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); - else if (std & V4L2_STD_PAL) - saa7185_write_block(sd, init_pal, sizeof(init_pal)); - else - return -EINVAL; - encoder->norm = std; - return 0; -} - -static int saa7185_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7185 *encoder = to_saa7185(sd); - - /* RJ: input = 0: input is from SA7111 - input = 1: input is from ZR36060 */ - - switch (input) { - case 0: - /* turn off colorbar */ - saa7185_write(sd, 0x3a, 0x0f); - /* Switch RTCE to 1 */ - saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); - saa7185_write(sd, 0x6e, 0x01); - break; - - case 1: - /* turn off colorbar */ - saa7185_write(sd, 0x3a, 0x0f); - /* Switch RTCE to 0 */ - saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); - /* SW: a slight sync problem... */ - saa7185_write(sd, 0x6e, 0x00); - break; - - case 2: - /* turn on colorbar */ - saa7185_write(sd, 0x3a, 0x8f); - /* Switch RTCE to 0 */ - saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); - /* SW: a slight sync problem... */ - saa7185_write(sd, 0x6e, 0x01); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa7185_core_ops = { - .g_chip_ident = saa7185_g_chip_ident, - .init = saa7185_init, -}; - -static const struct v4l2_subdev_video_ops saa7185_video_ops = { - .s_std_output = saa7185_s_std_output, - .s_routing = saa7185_s_routing, -}; - -static const struct v4l2_subdev_ops saa7185_ops = { - .core = &saa7185_core_ops, - .video = &saa7185_video_ops, -}; - - -/* ----------------------------------------------------------------------- */ - -static int saa7185_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i; - struct saa7185 *encoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); - if (encoder == NULL) - return -ENOMEM; - encoder->norm = V4L2_STD_NTSC; - sd = &encoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa7185_ops); - - i = saa7185_write_block(sd, init_common, sizeof(init_common)); - if (i >= 0) - i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); - if (i < 0) - v4l2_dbg(1, debug, sd, "init error %d\n", i); - else - v4l2_dbg(1, debug, sd, "revision 0x%x\n", - saa7185_read(sd) >> 5); - return 0; -} - -static int saa7185_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct saa7185 *encoder = to_saa7185(sd); - - v4l2_device_unregister_subdev(sd); - /* SW: output off is active */ - saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40); - kfree(encoder); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa7185_id[] = { - { "saa7185", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7185_id); - -static struct i2c_driver saa7185_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7185", - }, - .probe = saa7185_probe, - .remove = saa7185_remove, - .id_table = saa7185_id, -}; - -module_i2c_driver(saa7185_driver); diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c deleted file mode 100644 index d7d1670e0ca3..000000000000 --- a/drivers/media/video/saa7191.c +++ /dev/null @@ -1,659 +0,0 @@ -/* - * saa7191.c - Philips SAA7191 video decoder driver - * - * Copyright (C) 2003 Ladislav Michl - * Copyright (C) 2004,2005 Mikael Nousiainen - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "saa7191.h" - -#define SAA7191_MODULE_VERSION "0.0.5" - -MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); -MODULE_VERSION(SAA7191_MODULE_VERSION); -MODULE_AUTHOR("Mikael Nousiainen "); -MODULE_LICENSE("GPL"); - - -// #define SAA7191_DEBUG - -#ifdef SAA7191_DEBUG -#define dprintk(x...) printk("SAA7191: " x); -#else -#define dprintk(x...) -#endif - -#define SAA7191_SYNC_COUNT 30 -#define SAA7191_SYNC_DELAY 100 /* milliseconds */ - -struct saa7191 { - struct v4l2_subdev sd; - - /* the register values are stored here as the actual - * I2C-registers are write-only */ - u8 reg[25]; - - int input; - v4l2_std_id norm; -}; - -static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7191, sd); -} - -static const u8 initseq[] = { - 0, /* Subaddress */ - - 0x50, /* (0x50) SAA7191_REG_IDEL */ - - /* 50 Hz signal timing */ - 0x30, /* (0x30) SAA7191_REG_HSYB */ - 0x00, /* (0x00) SAA7191_REG_HSYS */ - 0xe8, /* (0xe8) SAA7191_REG_HCLB */ - 0xb6, /* (0xb6) SAA7191_REG_HCLS */ - 0xf4, /* (0xf4) SAA7191_REG_HPHI */ - - /* control */ - SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */ - 0x00, /* (0x00) SAA7191_REG_HUEC */ - 0xf8, /* (0xf8) SAA7191_REG_CKTQ */ - 0xf8, /* (0xf8) SAA7191_REG_CKTS */ - 0x90, /* (0x90) SAA7191_REG_PLSE */ - 0x90, /* (0x90) SAA7191_REG_SESE */ - 0x00, /* (0x00) SAA7191_REG_GAIN */ - SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC - * - not SECAM, - * slow time constant */ - SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS - | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK - * - chroma from CVBS, GPSW1 & 2 off */ - SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS - | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3 - * - automatic field detection */ - 0x00, /* (0x00) SAA7191_REG_CTL4 */ - 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */ - 0x00, /* unused */ - 0x00, /* unused */ - - /* 60 Hz signal timing */ - 0x34, /* (0x34) SAA7191_REG_HS6B */ - 0x0a, /* (0x0a) SAA7191_REG_HS6S */ - 0xf4, /* (0xf4) SAA7191_REG_HC6B */ - 0xce, /* (0xce) SAA7191_REG_HC6S */ - 0xf4, /* (0xf4) SAA7191_REG_HP6I */ -}; - -/* SAA7191 register handling */ - -static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) -{ - return to_saa7191(sd)->reg[reg]; -} - -static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = i2c_master_recv(client, value, 1); - if (ret < 0) { - printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n"); - return ret; - } - - return 0; -} - - -static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - to_saa7191(sd)->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* the first byte of data must be the first subaddress number (register) */ -static int saa7191_write_block(struct v4l2_subdev *sd, - u8 length, const u8 *data) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7191 *decoder = to_saa7191(sd); - int i; - int ret; - - for (i = 0; i < (length - 1); i++) { - decoder->reg[data[0] + i] = data[i + 1]; - } - - ret = i2c_master_send(client, data, length); - if (ret < 0) { - printk(KERN_ERR "SAA7191: saa7191_write_block(): " - "write failed\n"); - return ret; - } - - return 0; -} - -/* Helper functions */ - -static int saa7191_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); - u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); - int err; - - switch (input) { - case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ - iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 - | SAA7191_IOCK_GPSW2); - /* Chrominance trap active */ - luma &= ~SAA7191_LUMA_BYPS; - break; - case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ - iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; - /* Chrominance trap bypassed */ - luma |= SAA7191_LUMA_BYPS; - break; - default: - return -EINVAL; - } - - err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); - if (err) - return -EIO; - - decoder->input = input; - - return 0; -} - -static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); - u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); - int err; - - if (norm & V4L2_STD_PAL) { - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); - chcv = SAA7191_CHCV_PAL; - } else if (norm & V4L2_STD_NTSC) { - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~SAA7191_CTL3_AUFD; - ctl3 |= SAA7191_CTL3_FSEL; - chcv = SAA7191_CHCV_NTSC; - } else if (norm & V4L2_STD_SECAM) { - stdc |= SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); - chcv = SAA7191_CHCV_PAL; - } else { - return -EINVAL; - } - - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); - if (err) - return -EIO; - - decoder->norm = norm; - - dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, - stdc, chcv); - dprintk("norm: %llx\n", norm); - - return 0; -} - -static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) -{ - int i = 0; - - dprintk("Checking for signal...\n"); - - for (i = 0; i < SAA7191_SYNC_COUNT; i++) { - if (saa7191_read_status(sd, status)) - return -EIO; - - if (((*status) & SAA7191_STATUS_HLCK) == 0) { - dprintk("Signal found\n"); - return 0; - } - - msleep(SAA7191_SYNC_DELAY); - } - - dprintk("No signal\n"); - - return -EBUSY; -} - -static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); - u8 status; - v4l2_std_id old_norm = decoder->norm; - int err = 0; - - dprintk("SAA7191 extended signal auto-detection...\n"); - - *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_FSEL); - - err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); - if (err) { - err = -EIO; - goto out; - } - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - goto out; - } - - ctl3 |= SAA7191_CTL3_AUFD; - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - goto out; - } - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - if (status & SAA7191_STATUS_FIDT) { - /* 60Hz signal -> NTSC */ - dprintk("60Hz signal: NTSC\n"); - *norm = V4L2_STD_NTSC; - return 0; - } - - /* 50Hz signal */ - dprintk("50Hz signal: Trying PAL...\n"); - - /* try PAL first */ - err = saa7191_s_std(sd, V4L2_STD_PAL); - if (err) - goto out; - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - /* not 50Hz ? */ - if (status & SAA7191_STATUS_FIDT) { - dprintk("No 50Hz signal\n"); - saa7191_s_std(sd, old_norm); - return -EAGAIN; - } - - if (status & SAA7191_STATUS_CODE) { - dprintk("PAL\n"); - *norm = V4L2_STD_PAL; - return saa7191_s_std(sd, old_norm); - } - - dprintk("No color detected with PAL - Trying SECAM...\n"); - - /* no color detected ? -> try SECAM */ - err = saa7191_s_std(sd, V4L2_STD_SECAM); - if (err) - goto out; - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - /* not 50Hz ? */ - if (status & SAA7191_STATUS_FIDT) { - dprintk("No 50Hz signal\n"); - err = -EAGAIN; - goto out; - } - - if (status & SAA7191_STATUS_CODE) { - /* Color detected -> SECAM */ - dprintk("SECAM\n"); - *norm = V4L2_STD_SECAM; - return saa7191_s_std(sd, old_norm); - } - - dprintk("No color detected with SECAM - Going back to PAL.\n"); - -out: - return saa7191_s_std(sd, old_norm); -} - -static int saa7191_autodetect_norm(struct v4l2_subdev *sd) -{ - u8 status; - - dprintk("SAA7191 signal auto-detection...\n"); - - dprintk("Reading status...\n"); - - if (saa7191_read_status(sd, &status)) - return -EIO; - - dprintk("Checking for signal...\n"); - - /* no signal ? */ - if (status & SAA7191_STATUS_HLCK) { - dprintk("No signal\n"); - return -EBUSY; - } - - dprintk("Signal found\n"); - - if (status & SAA7191_STATUS_FIDT) { - /* 60hz signal -> NTSC */ - dprintk("NTSC\n"); - return saa7191_s_std(sd, V4L2_STD_NTSC); - } else { - /* 50hz signal -> PAL */ - dprintk("PAL\n"); - return saa7191_s_std(sd, V4L2_STD_PAL); - } -} - -static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - case SAA7191_CONTROL_BANDPASS_WEIGHT: - case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) - >> SAA7191_LUMA_BPSS_SHIFT; - break; - case SAA7191_CONTROL_BANDPASS_WEIGHT: - ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK) - >> SAA7191_LUMA_APER_SHIFT; - break; - case SAA7191_CONTROL_CORING: - ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK) - >> SAA7191_LUMA_CORI_SHIFT; - break; - } - break; - case SAA7191_CONTROL_FORCE_COLOUR: - case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); - if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) - ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; - else - ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) - >> SAA7191_GAIN_LFIS_SHIFT; - break; - case V4L2_CID_HUE: - reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); - if (reg < 0x80) - reg += 0x80; - else - reg -= 0x80; - ctrl->value = (s32)reg; - break; - case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(sd, SAA7191_REG_STDC); - ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; - break; - case SAA7191_CONTROL_LUMA_DELAY: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); - ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) - >> SAA7191_CTL3_YDEL_SHIFT; - if (ctrl->value >= 4) - ctrl->value -= 8; - break; - case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); - ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) - >> SAA7191_CTL4_VNOI_SHIFT; - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - case SAA7191_CONTROL_BANDPASS_WEIGHT: - case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - reg &= ~SAA7191_LUMA_BPSS_MASK; - reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) - & SAA7191_LUMA_BPSS_MASK; - break; - case SAA7191_CONTROL_BANDPASS_WEIGHT: - reg &= ~SAA7191_LUMA_APER_MASK; - reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT) - & SAA7191_LUMA_APER_MASK; - break; - case SAA7191_CONTROL_CORING: - reg &= ~SAA7191_LUMA_CORI_MASK; - reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT) - & SAA7191_LUMA_CORI_MASK; - break; - } - ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); - break; - case SAA7191_CONTROL_FORCE_COLOUR: - case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); - if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { - if (ctrl->value) - reg |= SAA7191_GAIN_COLO; - else - reg &= ~SAA7191_GAIN_COLO; - } else { - reg &= ~SAA7191_GAIN_LFIS_MASK; - reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) - & SAA7191_GAIN_LFIS_MASK; - } - ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); - break; - case V4L2_CID_HUE: - reg = ctrl->value & 0xff; - if (reg < 0x80) - reg += 0x80; - else - reg -= 0x80; - ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); - break; - case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(sd, SAA7191_REG_STDC); - if (ctrl->value) - reg |= SAA7191_STDC_VTRC; - else - reg &= ~SAA7191_STDC_VTRC; - ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); - break; - case SAA7191_CONTROL_LUMA_DELAY: { - s32 value = ctrl->value; - if (value < 0) - value += 8; - reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); - reg &= ~SAA7191_CTL3_YDEL_MASK; - reg |= (value << SAA7191_CTL3_YDEL_SHIFT) - & SAA7191_CTL3_YDEL_MASK; - ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); - break; - } - case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); - reg &= ~SAA7191_CTL4_VNOI_MASK; - reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) - & SAA7191_CTL4_VNOI_MASK; - ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -/* I2C-interface */ - -static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - u8 status_reg; - int res = V4L2_IN_ST_NO_SIGNAL; - - if (saa7191_read_status(sd, &status_reg)) - return -EIO; - if ((status_reg & SAA7191_STATUS_HLCK) == 0) - res = 0; - if (!(status_reg & SAA7191_STATUS_CODE)) - res |= V4L2_IN_ST_NO_COLOR; - *status = res; - return 0; -} - - -static int saa7191_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa7191_core_ops = { - .g_chip_ident = saa7191_g_chip_ident, - .g_ctrl = saa7191_g_ctrl, - .s_ctrl = saa7191_s_ctrl, - .s_std = saa7191_s_std, -}; - -static const struct v4l2_subdev_video_ops saa7191_video_ops = { - .s_routing = saa7191_s_routing, - .querystd = saa7191_querystd, - .g_input_status = saa7191_g_input_status, -}; - -static const struct v4l2_subdev_ops saa7191_ops = { - .core = &saa7191_core_ops, - .video = &saa7191_video_ops, -}; - -static int saa7191_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int err = 0; - struct saa7191 *decoder; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); - if (!decoder) - return -ENOMEM; - - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa7191_ops); - - err = saa7191_write_block(sd, sizeof(initseq), initseq); - if (err) { - printk(KERN_ERR "SAA7191 initialization failed\n"); - kfree(decoder); - return err; - } - - printk(KERN_INFO "SAA7191 initialized\n"); - - decoder->input = SAA7191_INPUT_COMPOSITE; - decoder->norm = V4L2_STD_PAL; - - err = saa7191_autodetect_norm(sd); - if (err && (err != -EBUSY)) - printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); - - return 0; -} - -static int saa7191_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_saa7191(sd)); - return 0; -} - -static const struct i2c_device_id saa7191_id[] = { - { "saa7191", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7191_id); - -static struct i2c_driver saa7191_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7191", - }, - .probe = saa7191_probe, - .remove = saa7191_remove, - .id_table = saa7191_id, -}; - -module_i2c_driver(saa7191_driver); diff --git a/drivers/media/video/saa7191.h b/drivers/media/video/saa7191.h deleted file mode 100644 index 803c74d6066f..000000000000 --- a/drivers/media/video/saa7191.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * saa7191.h - Philips SAA7191 video decoder driver - * - * Copyright (C) 2003 Ladislav Michl - * Copyright (C) 2004,2005 Mikael Nousiainen - * - * 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. - */ - -#ifndef _SAA7191_H_ -#define _SAA7191_H_ - -/* Philips SAA7191 DMSD I2C bus address */ -#define SAA7191_ADDR 0x8a - -/* Register subaddresses. */ -#define SAA7191_REG_IDEL 0x00 -#define SAA7191_REG_HSYB 0x01 -#define SAA7191_REG_HSYS 0x02 -#define SAA7191_REG_HCLB 0x03 -#define SAA7191_REG_HCLS 0x04 -#define SAA7191_REG_HPHI 0x05 -#define SAA7191_REG_LUMA 0x06 -#define SAA7191_REG_HUEC 0x07 -#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */ -#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */ -#define SAA7191_REG_PLSE 0x0a -#define SAA7191_REG_SESE 0x0b -#define SAA7191_REG_GAIN 0x0c -#define SAA7191_REG_STDC 0x0d -#define SAA7191_REG_IOCK 0x0e -#define SAA7191_REG_CTL3 0x0f -#define SAA7191_REG_CTL4 0x10 -#define SAA7191_REG_CHCV 0x11 -#define SAA7191_REG_HS6B 0x14 -#define SAA7191_REG_HS6S 0x15 -#define SAA7191_REG_HC6B 0x16 -#define SAA7191_REG_HC6S 0x17 -#define SAA7191_REG_HP6I 0x18 -#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ - -/* Status Register definitions */ -#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ -#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */ -#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */ -#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ - -/* Luminance Control Register definitions */ -/* input mode select bit: - * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */ -#define SAA7191_LUMA_BYPS 0x80 -/* pre-filter (only when chrominance trap is active) */ -#define SAA7191_LUMA_PREF 0x40 -/* aperture bandpass to select different characteristics with maximums - * (bits 4-5) */ -#define SAA7191_LUMA_BPSS_MASK 0x30 -#define SAA7191_LUMA_BPSS_SHIFT 4 -#define SAA7191_LUMA_BPSS_3 0x30 -#define SAA7191_LUMA_BPSS_2 0x20 -#define SAA7191_LUMA_BPSS_1 0x10 -#define SAA7191_LUMA_BPSS_0 0x00 -/* coring range for high frequency components according to 8-bit luminance - * (bits 2-3) - * 0=coring off, n= (+-)n LSB */ -#define SAA7191_LUMA_CORI_MASK 0x0c -#define SAA7191_LUMA_CORI_SHIFT 2 -#define SAA7191_LUMA_CORI_3 0x0c -#define SAA7191_LUMA_CORI_2 0x08 -#define SAA7191_LUMA_CORI_1 0x04 -#define SAA7191_LUMA_CORI_0 0x00 -/* aperture bandpass filter weights high frequency components of luminance - * signal (bits 0-1) - * 0=factor 0, 1=0.25, 2=0.5, 3=1 */ -#define SAA7191_LUMA_APER_MASK 0x03 -#define SAA7191_LUMA_APER_SHIFT 0 -#define SAA7191_LUMA_APER_3 0x03 -#define SAA7191_LUMA_APER_2 0x02 -#define SAA7191_LUMA_APER_1 0x01 -#define SAA7191_LUMA_APER_0 0x00 - -/* Chrominance Gain Control Settings Register definitions */ -/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */ -#define SAA7191_GAIN_COLO 0x80 -/* chrominance gain control (AGC filter) - * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */ -#define SAA7191_GAIN_LFIS_MASK 0x60 -#define SAA7191_GAIN_LFIS_SHIFT 5 -#define SAA7191_GAIN_LFIS_3 0x60 -#define SAA7191_GAIN_LFIS_2 0x40 -#define SAA7191_GAIN_LFIS_1 0x20 -#define SAA7191_GAIN_LFIS_0 0x00 - -/* Standard/Mode Control Register definitions */ -/* tv/vtr mode bit: 0=TV mode (slow time constant), - * 1=VTR mode (fast time constant) */ -#define SAA7191_STDC_VTRC 0x80 -/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs) - * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */ -#define SAA7191_STDC_NFEN 0x08 -/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */ -#define SAA7191_STDC_HRMV 0x04 -/* general purpose switch 0 - * (not used with VINO afaik) */ -#define SAA7191_STDC_GPSW0 0x02 -/* SECAM mode bit: 0=other standards, 1=SECAM */ -#define SAA7191_STDC_SECS 0x01 - -/* I/O and Clock Control Register definitions */ -/* horizontal clock PLL: 0=PLL closed, - * 1=PLL circuit open and horizontal freq fixed */ -#define SAA7191_IOCK_HPLL 0x80 -/* colour-difference output enable (outputs UV0-UV7) */ -#define SAA7191_IOCK_OEDC 0x40 -/* H-sync output enable */ -#define SAA7191_IOCK_OEHS 0x20 -/* V-sync output enable */ -#define SAA7191_IOCK_OEVS 0x10 -/* luminance output enable (outputs Y0-Y7) */ -#define SAA7191_IOCK_OEDY 0x08 -/* S-VHS bit (chrominance from CVBS or from chrominance input): - * 0=controlled by BYPS-bit, 1=from chrominance input */ -#define SAA7191_IOCK_CHRS 0x04 -/* general purpose switch 2 - * VINO-specific: 0=used with CVBS, 1=used with S-Video */ -#define SAA7191_IOCK_GPSW2 0x02 -/* general purpose switch 1 */ -/* VINO-specific: 0=always, 1=not used!*/ -#define SAA7191_IOCK_GPSW1 0x01 - -/* Miscellaneous Control #1 Register definitions */ -/* automatic field detection (50/60Hz standard) */ -#define SAA7191_CTL3_AUFD 0x80 -/* field select: (if AUFD=0) - * 0=50Hz (625 lines), 1=60Hz (525 lines) */ -#define SAA7191_CTL3_FSEL 0x40 -/* SECAM cross-colour reduction enable */ -#define SAA7191_CTL3_SXCR 0x20 -/* sync and clamping pulse enable (HCL and HSY outputs) */ -#define SAA7191_CTL3_SCEN 0x10 -/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */ -#define SAA7191_CTL3_OFTS 0x08 -/* luminance delay compensation - * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC, - * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC - * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */ -#define SAA7191_CTL3_YDEL_MASK 0x07 -#define SAA7191_CTL3_YDEL_SHIFT 0 -#define SAA7191_CTL3_YDEL2 0x04 -#define SAA7191_CTL3_YDEL1 0x02 -#define SAA7191_CTL3_YDEL0 0x01 - -/* Miscellaneous Control #2 Register definitions */ -/* select HREF position - * 0=normal, HREF is matched to YUV output port, - * 1=HREF is matched to CVBS input port */ -#define SAA7191_CTL4_HRFS 0x04 -/* vertical noise reduction - * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */ -#define SAA7191_CTL4_VNOI_MASK 0x03 -#define SAA7191_CTL4_VNOI_SHIFT 0 -#define SAA7191_CTL4_VNOI_3 0x03 -#define SAA7191_CTL4_VNOI_2 0x02 -#define SAA7191_CTL4_VNOI_1 0x01 -#define SAA7191_CTL4_VNOI_0 0x00 - -/* Chrominance Gain Control Register definitions - * - for QAM-modulated input signals, effects output amplitude - * (SECAM gain fixed) - * (nominal values for UV CCIR level) */ -#define SAA7191_CHCV_NTSC 0x2c -#define SAA7191_CHCV_PAL 0x59 - -/* Driver interface definitions */ -#define SAA7191_INPUT_COMPOSITE 0 -#define SAA7191_INPUT_SVIDEO 1 - -#define SAA7191_NORM_PAL 1 -#define SAA7191_NORM_NTSC 2 -#define SAA7191_NORM_SECAM 3 - -struct saa7191_status { - /* 0=no signal, 1=signal detected */ - int signal; - /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */ - int signal_60hz; - /* 0=no color detected, 1=color detected */ - int color; - - /* current SAA7191_INPUT_ */ - int input; - /* current SAA7191_NORM_ */ - int norm; -}; - -#define SAA7191_BANDPASS_MIN 0x00 -#define SAA7191_BANDPASS_MAX 0x03 -#define SAA7191_BANDPASS_DEFAULT 0x00 - -#define SAA7191_BANDPASS_WEIGHT_MIN 0x00 -#define SAA7191_BANDPASS_WEIGHT_MAX 0x03 -#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01 - -#define SAA7191_CORING_MIN 0x00 -#define SAA7191_CORING_MAX 0x03 -#define SAA7191_CORING_DEFAULT 0x00 - -#define SAA7191_HUE_MIN 0x00 -#define SAA7191_HUE_MAX 0xff -#define SAA7191_HUE_DEFAULT 0x80 - -#define SAA7191_VTRC_MIN 0x00 -#define SAA7191_VTRC_MAX 0x01 -#define SAA7191_VTRC_DEFAULT 0x00 - -#define SAA7191_FORCE_COLOUR_MIN 0x00 -#define SAA7191_FORCE_COLOUR_MAX 0x01 -#define SAA7191_FORCE_COLOUR_DEFAULT 0x00 - -#define SAA7191_CHROMA_GAIN_MIN 0x00 -#define SAA7191_CHROMA_GAIN_MAX 0x03 -#define SAA7191_CHROMA_GAIN_DEFAULT 0x00 - -#define SAA7191_LUMA_DELAY_MIN -0x04 -#define SAA7191_LUMA_DELAY_MAX 0x03 -#define SAA7191_LUMA_DELAY_DEFAULT 0x01 - -#define SAA7191_VNR_MIN 0x00 -#define SAA7191_VNR_MAX 0x03 -#define SAA7191_VNR_DEFAULT 0x00 - -#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) -#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) -#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) -#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) -#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) -#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) -#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) -#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) - -#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) -#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) - -#endif diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c deleted file mode 100644 index a2e41a21dc65..000000000000 --- a/drivers/media/video/smiapp-pll.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * drivers/media/video/smiapp-pll.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include - -#include "smiapp-pll.h" - -/* Return an even number or one. */ -static inline uint32_t clk_div_even(uint32_t a) -{ - return max_t(uint32_t, 1, a & ~1); -} - -/* Return an even number or one. */ -static inline uint32_t clk_div_even_up(uint32_t a) -{ - if (a == 1) - return 1; - return (a + 1) & ~1; -} - -static inline uint32_t is_one_or_even(uint32_t a) -{ - if (a == 1) - return 1; - if (a & 1) - return 0; - - return 1; -} - -static int bounds_check(struct device *dev, uint32_t val, - uint32_t min, uint32_t max, char *str) -{ - if (val >= min && val <= max) - return 0; - - dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); - - return -EINVAL; -} - -static void print_pll(struct device *dev, struct smiapp_pll *pll) -{ - dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); - dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); - if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); - dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); - } - dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); - dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); - - dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); - dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); - dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", - pll->op_sys_clk_freq_hz); - dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", - pll->op_pix_clk_freq_hz); - } - dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); - dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); -} - -int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, - struct smiapp_pll *pll) -{ - uint32_t sys_div; - uint32_t best_pix_div = INT_MAX >> 1; - uint32_t vt_op_binning_div; - uint32_t lane_op_clock_ratio; - uint32_t mul, div; - uint32_t more_mul_min, more_mul_max; - uint32_t more_mul_factor; - uint32_t min_vt_div, max_vt_div, vt_div; - uint32_t min_sys_div, max_sys_div; - unsigned int i; - int rval; - - if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) - lane_op_clock_ratio = pll->lanes; - else - lane_op_clock_ratio = 1; - dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); - - dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, - pll->binning_vertical); - - /* CSI transfers 2 bits per clock per lane; thus times 2 */ - pll->pll_op_clk_freq_hz = pll->link_freq * 2 - * (pll->lanes / lane_op_clock_ratio); - - /* Figure out limits for pre-pll divider based on extclk */ - dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - limits->max_pre_pll_clk_div = - min_t(uint16_t, limits->max_pre_pll_clk_div, - clk_div_even(pll->ext_clk_freq_hz / - limits->min_pll_ip_freq_hz)); - limits->min_pre_pll_clk_div = - max_t(uint16_t, limits->min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(pll->ext_clk_freq_hz, - limits->max_pll_ip_freq_hz))); - dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - - i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); - mul = div_u64(pll->pll_op_clk_freq_hz, i); - div = pll->ext_clk_freq_hz / i; - dev_dbg(dev, "mul %d / div %d\n", mul, div); - - limits->min_pre_pll_clk_div = - max_t(uint16_t, limits->min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, - limits->max_pll_op_freq_hz))); - dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - - if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { - dev_err(dev, "unable to compute pre_pll divisor\n"); - return -EINVAL; - } - - pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; - - /* - * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be - * too high. - */ - dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); - - /* Don't go above max pll multiplier. */ - more_mul_max = limits->max_pll_multiplier / mul; - dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", - more_mul_max); - /* Don't go above max pll op frequency. */ - more_mul_max = - min_t(int, - more_mul_max, - limits->max_pll_op_freq_hz - / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); - dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", - more_mul_max); - /* Don't go above the division capability of op sys clock divider. */ - more_mul_max = min(more_mul_max, - limits->max_op_sys_clk_div * pll->pre_pll_clk_div - / div); - dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", - more_mul_max); - /* Ensure we won't go above min_pll_multiplier. */ - more_mul_max = min(more_mul_max, - DIV_ROUND_UP(limits->max_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", - more_mul_max); - - /* Ensure we won't go below min_pll_op_freq_hz. */ - more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, - pll->ext_clk_freq_hz / pll->pre_pll_clk_div - * mul); - dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", - more_mul_min); - /* Ensure we won't go below min_pll_multiplier. */ - more_mul_min = max(more_mul_min, - DIV_ROUND_UP(limits->min_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", - more_mul_min); - - if (more_mul_min > more_mul_max) { - dev_warn(dev, - "unable to compute more_mul_min and more_mul_max"); - return -EINVAL; - } - - more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; - dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); - more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); - dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", - more_mul_factor); - i = roundup(more_mul_min, more_mul_factor); - if (!is_one_or_even(i)) - i <<= 1; - - dev_dbg(dev, "final more_mul: %d\n", i); - if (i > more_mul_max) { - dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); - return -EINVAL; - } - - pll->pll_multiplier = mul * i; - pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; - dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); - - pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz - / pll->pre_pll_clk_div; - - pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz - * pll->pll_multiplier; - - /* Derive pll_op_clk_freq_hz. */ - pll->op_sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; - - pll->op_pix_clk_div = pll->bits_per_pixel; - dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); - - pll->op_pix_clk_freq_hz = - pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; - - /* - * Some sensors perform analogue binning and some do this - * digitally. The ones doing this digitally can be roughly be - * found out using this formula. The ones doing this digitally - * should run at higher clock rate, so smaller divisor is used - * on video timing side. - */ - if (limits->min_line_length_pck_bin > limits->min_line_length_pck - / pll->binning_horizontal) - vt_op_binning_div = pll->binning_horizontal; - else - vt_op_binning_div = 1; - dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); - - /* - * Profile 2 supports vt_pix_clk_div E [4, 10] - * - * Horizontal binning can be used as a base for difference in - * divisors. One must make sure that horizontal blanking is - * enough to accommodate the CSI-2 sync codes. - * - * Take scaling factor into account as well. - * - * Find absolute limits for the factor of vt divider. - */ - dev_dbg(dev, "scale_m: %d\n", pll->scale_m); - min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div - * pll->scale_n, - lane_op_clock_ratio * vt_op_binning_div - * pll->scale_m); - - /* Find smallest and biggest allowed vt divisor. */ - dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); - min_vt_div = max(min_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->max_vt_pix_clk_freq_hz)); - dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", - min_vt_div); - min_vt_div = max_t(uint32_t, min_vt_div, - limits->min_vt_pix_clk_div - * limits->min_vt_sys_clk_div); - dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); - - max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; - dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); - max_vt_div = min(max_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->min_vt_pix_clk_freq_hz)); - dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", - max_vt_div); - - /* - * Find limitsits for sys_clk_div. Not all values are possible - * with all values of pix_clk_div. - */ - min_sys_div = limits->min_vt_sys_clk_div; - dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); - min_sys_div = max(min_sys_div, - DIV_ROUND_UP(min_vt_div, - limits->max_vt_pix_clk_div)); - dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); - min_sys_div = max(min_sys_div, - pll->pll_op_clk_freq_hz - / limits->max_vt_sys_clk_freq_hz); - dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); - min_sys_div = clk_div_even_up(min_sys_div); - dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); - - max_sys_div = limits->max_vt_sys_clk_div; - dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(max_vt_div, - limits->min_vt_pix_clk_div)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->min_vt_pix_clk_freq_hz)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); - - /* - * Find pix_div such that a legal pix_div * sys_div results - * into a value which is not smaller than div, the desired - * divisor. - */ - for (vt_div = min_vt_div; vt_div <= max_vt_div; - vt_div += 2 - (vt_div & 1)) { - for (sys_div = min_sys_div; - sys_div <= max_sys_div; - sys_div += 2 - (sys_div & 1)) { - int pix_div = DIV_ROUND_UP(vt_div, sys_div); - - if (pix_div < limits->min_vt_pix_clk_div - || pix_div > limits->max_vt_pix_clk_div) { - dev_dbg(dev, - "pix_div %d too small or too big (%d--%d)\n", - pix_div, - limits->min_vt_pix_clk_div, - limits->max_vt_pix_clk_div); - continue; - } - - /* Check if this one is better. */ - if (pix_div * sys_div - <= roundup(min_vt_div, best_pix_div)) - best_pix_div = pix_div; - } - if (best_pix_div < INT_MAX >> 1) - break; - } - - pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); - pll->vt_pix_clk_div = best_pix_div; - - pll->vt_sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; - pll->vt_pix_clk_freq_hz = - pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; - - pll->pixel_rate_csi = - pll->op_pix_clk_freq_hz * lane_op_clock_ratio; - - print_pll(dev, pll); - - rval = bounds_check(dev, pll->pre_pll_clk_div, - limits->min_pre_pll_clk_div, - limits->max_pre_pll_clk_div, "pre_pll_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->pll_ip_clk_freq_hz, - limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, - "pll_ip_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->pll_multiplier, - limits->min_pll_multiplier, limits->max_pll_multiplier, - "pll_multiplier"); - if (!rval) - rval = bounds_check( - dev, pll->pll_op_clk_freq_hz, - limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, - "pll_op_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->op_sys_clk_div, - limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, - "op_sys_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->op_pix_clk_div, - limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, - "op_pix_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->op_sys_clk_freq_hz, - limits->min_op_sys_clk_freq_hz, - limits->max_op_sys_clk_freq_hz, - "op_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->op_pix_clk_freq_hz, - limits->min_op_pix_clk_freq_hz, - limits->max_op_pix_clk_freq_hz, - "op_pix_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt_sys_clk_freq_hz, - limits->min_vt_sys_clk_freq_hz, - limits->max_vt_sys_clk_freq_hz, - "vt_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt_pix_clk_freq_hz, - limits->min_vt_pix_clk_freq_hz, - limits->max_vt_pix_clk_freq_hz, - "vt_pix_clk_freq_hz"); - - return rval; -} -EXPORT_SYMBOL_GPL(smiapp_pll_calculate); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h deleted file mode 100644 index 9eab63f23afb..000000000000 --- a/drivers/media/video/smiapp-pll.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * drivers/media/video/smiapp-pll.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef SMIAPP_PLL_H -#define SMIAPP_PLL_H - -#include - -struct smiapp_pll { - uint8_t lanes; - uint8_t binning_horizontal; - uint8_t binning_vertical; - uint8_t scale_m; - uint8_t scale_n; - uint8_t bits_per_pixel; - uint16_t flags; - uint32_t link_freq; - - uint16_t pre_pll_clk_div; - uint16_t pll_multiplier; - uint16_t op_sys_clk_div; - uint16_t op_pix_clk_div; - uint16_t vt_sys_clk_div; - uint16_t vt_pix_clk_div; - - uint32_t ext_clk_freq_hz; - uint32_t pll_ip_clk_freq_hz; - uint32_t pll_op_clk_freq_hz; - uint32_t op_sys_clk_freq_hz; - uint32_t op_pix_clk_freq_hz; - uint32_t vt_sys_clk_freq_hz; - uint32_t vt_pix_clk_freq_hz; - - uint32_t pixel_rate_csi; -}; - -struct smiapp_pll_limits { - /* Strict PLL limits */ - uint32_t min_ext_clk_freq_hz; - uint32_t max_ext_clk_freq_hz; - uint16_t min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div; - uint32_t min_pll_ip_freq_hz; - uint32_t max_pll_ip_freq_hz; - uint16_t min_pll_multiplier; - uint16_t max_pll_multiplier; - uint32_t min_pll_op_freq_hz; - uint32_t max_pll_op_freq_hz; - - uint16_t min_vt_sys_clk_div; - uint16_t max_vt_sys_clk_div; - uint32_t min_vt_sys_clk_freq_hz; - uint32_t max_vt_sys_clk_freq_hz; - uint16_t min_vt_pix_clk_div; - uint16_t max_vt_pix_clk_div; - uint32_t min_vt_pix_clk_freq_hz; - uint32_t max_vt_pix_clk_freq_hz; - - uint16_t min_op_sys_clk_div; - uint16_t max_op_sys_clk_div; - uint32_t min_op_sys_clk_freq_hz; - uint32_t max_op_sys_clk_freq_hz; - uint16_t min_op_pix_clk_div; - uint16_t max_op_pix_clk_div; - uint32_t min_op_pix_clk_freq_hz; - uint32_t max_op_pix_clk_freq_hz; - - /* Other relevant limits */ - uint32_t min_line_length_pck_bin; - uint32_t min_line_length_pck; -}; - -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) -#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) - -struct device; - -int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, - struct smiapp_pll *pll); - -#endif /* SMIAPP_PLL_H */ diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig deleted file mode 100644 index 3149cda1d0db..000000000000 --- a/drivers/media/video/smiapp/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config VIDEO_SMIAPP - tristate "SMIA++/SMIA sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK - depends on MEDIA_CAMERA_SUPPORT - select VIDEO_SMIAPP_PLL - ---help--- - This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile deleted file mode 100644 index 36b0cfa2c541..000000000000 --- a/drivers/media/video/smiapp/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -smiapp-objs += smiapp-core.o smiapp-regs.o \ - smiapp-quirk.o smiapp-limits.o -obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o - -ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c deleted file mode 100644 index bfd47c106134..000000000000 --- a/drivers/media/video/smiapp/smiapp-core.c +++ /dev/null @@ -1,2895 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-core.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2010--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * Based on smiapp driver by Vimarsh Zutshi - * Based on jt8ev1.c by Vimarsh Zutshi - * Based on smia-sensor.c by Tuukka Toivonen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "smiapp.h" - -#define SMIAPP_ALIGN_DIM(dim, flags) \ - ((flags) & V4L2_SEL_FLAG_GE \ - ? ALIGN((dim), 2) \ - : (dim) & ~1) - -/* - * smiapp_module_idents - supported camera modules - */ -static const struct smiapp_module_ident smiapp_module_idents[] = { - SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), - SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), - SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), - SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), - SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), - SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), - SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), - SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), - SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), - SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), - SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), -}; - -/* - * - * Dynamic Capability Identification - * - */ - -static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; - unsigned int i; - int rval; - int line_count = 0; - int embedded_start = -1, embedded_end = -1; - int image_start = 0; - - rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, - &fmt_model_type); - if (rval) - return rval; - - rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, - &fmt_model_subtype); - if (rval) - return rval; - - ncol_desc = (fmt_model_subtype - & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) - >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; - nrow_desc = fmt_model_subtype - & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; - - dev_dbg(&client->dev, "format_model_type %s\n", - fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE - ? "2 byte" : - fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE - ? "4 byte" : "is simply bad"); - - for (i = 0; i < ncol_desc + nrow_desc; i++) { - u32 desc; - u32 pixelcode; - u32 pixels; - char *which; - char *what; - - if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), - &desc); - if (rval) - return rval; - - pixelcode = - (desc - & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) - >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; - pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; - } else if (fmt_model_type - == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), - &desc); - if (rval) - return rval; - - pixelcode = - (desc - & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) - >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; - pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; - } else { - dev_dbg(&client->dev, - "invalid frame format model type %d\n", - fmt_model_type); - return -EINVAL; - } - - if (i < ncol_desc) - which = "columns"; - else - which = "rows"; - - switch (pixelcode) { - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: - what = "embedded"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: - what = "dummy"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: - what = "black"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: - what = "dark"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: - what = "visible"; - break; - default: - what = "invalid"; - dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); - break; - } - - dev_dbg(&client->dev, "%s pixels: %d %s\n", - what, pixels, which); - - if (i < ncol_desc) - continue; - - /* Handle row descriptors */ - if (pixelcode - == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { - embedded_start = line_count; - } else { - if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE - || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) - image_start = line_count; - if (embedded_start != -1 && embedded_end == -1) - embedded_end = line_count; - } - line_count += pixels; - } - - if (embedded_start == -1 || embedded_end == -1) { - embedded_start = 0; - embedded_end = 0; - } - - dev_dbg(&client->dev, "embedded data from lines %d to %d\n", - embedded_start, embedded_end); - dev_dbg(&client->dev, "image data starts at line %d\n", image_start); - - return 0; -} - -static int smiapp_pll_configure(struct smiapp_sensor *sensor) -{ - struct smiapp_pll *pll = &sensor->pll; - int rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); - if (rval < 0) - return rval; - - /* Lane op clock ratio does not apply here. */ - rval = smiapp_write( - sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, - DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); - if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); - if (rval < 0) - return rval; - - return smiapp_write( - sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); -} - -static int smiapp_pll_update(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct smiapp_pll_limits lim = { - .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], - .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], - .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], - .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], - .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], - .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], - .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], - .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], - - .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], - .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], - .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], - .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], - .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], - .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], - .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], - .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], - - .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], - .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], - .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], - .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], - .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], - .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], - .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], - .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], - - .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], - .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], - }; - struct smiapp_pll *pll = &sensor->pll; - int rval; - - memset(&sensor->pll, 0, sizeof(sensor->pll)); - - pll->lanes = sensor->platform_data->lanes; - pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; - - if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { - /* - * Fill in operational clock divisors limits from the - * video timing ones. On profile 0 sensors the - * requirements regarding them are essentially the - * same as on VT ones. - */ - lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div; - lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div; - lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div; - lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div; - lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz; - lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz; - lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz; - lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz; - /* Profile 0 sensors have no separate OP clock branch. */ - pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; - } - - if (smiapp_needs_quirk(sensor, - SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) - pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; - - pll->binning_horizontal = sensor->binning_horizontal; - pll->binning_vertical = sensor->binning_vertical; - pll->link_freq = - sensor->link_freq->qmenu_int[sensor->link_freq->val]; - pll->scale_m = sensor->scale_m; - pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; - pll->bits_per_pixel = sensor->csi_format->compressed; - - rval = smiapp_pll_calculate(&client->dev, &lim, pll); - if (rval < 0) - return rval; - - sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; - sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; - - return 0; -} - - -/* - * - * V4L2 Controls handling - * - */ - -static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) -{ - struct v4l2_ctrl *ctrl = sensor->exposure; - int max; - - max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + sensor->vblank->val - - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; - - ctrl->maximum = max; - if (ctrl->default_value > max) - ctrl->default_value = max; - if (ctrl->val > max) - ctrl->val = max; - if (ctrl->cur.val > max) - ctrl->cur.val = max; -} - -/* - * Order matters. - * - * 1. Bits-per-pixel, descending. - * 2. Bits-per-pixel compressed, descending. - * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel - * orders must be defined. - */ -static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { - { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, -}; - -const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; - -#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ - - (unsigned long)smiapp_csi_data_formats) \ - / sizeof(*smiapp_csi_data_formats)) - -static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int flip = 0; - - if (sensor->hflip) { - if (sensor->hflip->val) - flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; - - if (sensor->vflip->val) - flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; - } - - flip ^= sensor->hvflip_inv_mask; - - dev_dbg(&client->dev, "flip %d\n", flip); - return sensor->default_pixel_order ^ flip; -} - -static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int csi_format_idx = - to_csi_format_idx(sensor->csi_format) & ~3; - unsigned int internal_csi_format_idx = - to_csi_format_idx(sensor->internal_csi_format) & ~3; - unsigned int pixel_order = smiapp_pixel_order(sensor); - - sensor->mbus_frame_fmts = - sensor->default_mbus_frame_fmts << pixel_order; - sensor->csi_format = - &smiapp_csi_data_formats[csi_format_idx + pixel_order]; - sensor->internal_csi_format = - &smiapp_csi_data_formats[internal_csi_format_idx - + pixel_order]; - - BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order - >= ARRAY_SIZE(smiapp_csi_data_formats)); - BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); - - dev_dbg(&client->dev, "new pixel order %s\n", - pixel_order_str[pixel_order]); -} - -static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct smiapp_sensor *sensor = - container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) - ->sensor; - u32 orient = 0; - int exposure; - int rval; - - switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - return smiapp_write( - sensor, - SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); - - case V4L2_CID_EXPOSURE: - return smiapp_write( - sensor, - SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); - - case V4L2_CID_HFLIP: - case V4L2_CID_VFLIP: - if (sensor->streaming) - return -EBUSY; - - if (sensor->hflip->val) - orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; - - if (sensor->vflip->val) - orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; - - orient ^= sensor->hvflip_inv_mask; - rval = smiapp_write(sensor, - SMIAPP_REG_U8_IMAGE_ORIENTATION, - orient); - if (rval < 0) - return rval; - - smiapp_update_mbus_formats(sensor); - - return 0; - - case V4L2_CID_VBLANK: - exposure = sensor->exposure->val; - - __smiapp_update_exposure_limits(sensor); - - if (exposure > sensor->exposure->maximum) { - sensor->exposure->val = - sensor->exposure->maximum; - rval = smiapp_set_ctrl( - sensor->exposure); - if (rval < 0) - return rval; - } - - return smiapp_write( - sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + ctrl->val); - - case V4L2_CID_HBLANK: - return smiapp_write( - sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - + ctrl->val); - - case V4L2_CID_LINK_FREQ: - if (sensor->streaming) - return -EBUSY; - - return smiapp_pll_update(sensor); - - default: - return -EINVAL; - } -} - -static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { - .s_ctrl = smiapp_set_ctrl, -}; - -static int smiapp_init_controls(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int max; - int rval; - - rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); - if (rval) - return rval; - sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; - - sensor->analog_gain = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], - max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); - - /* Exposure limits will be updated soon, use just something here. */ - sensor->exposure = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0, 1, 0); - - sensor->hflip = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sensor->vflip = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - sensor->vblank = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_VBLANK, 0, 1, 1, 0); - - if (sensor->vblank) - sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; - - sensor->hblank = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_HBLANK, 0, 1, 1, 0); - - if (sensor->hblank) - sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; - - sensor->pixel_rate_parray = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); - - if (sensor->pixel_array->ctrl_handler.error) { - dev_err(&client->dev, - "pixel array controls initialization failed (%d)\n", - sensor->pixel_array->ctrl_handler.error); - rval = sensor->pixel_array->ctrl_handler.error; - goto error; - } - - sensor->pixel_array->sd.ctrl_handler = - &sensor->pixel_array->ctrl_handler; - - v4l2_ctrl_cluster(2, &sensor->hflip); - - rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); - if (rval) - goto error; - sensor->src->ctrl_handler.lock = &sensor->mutex; - - for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); - - sensor->link_freq = v4l2_ctrl_new_int_menu( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_LINK_FREQ, max, 0, - sensor->platform_data->op_sys_clock); - - sensor->pixel_rate_csi = v4l2_ctrl_new_std( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); - - if (sensor->src->ctrl_handler.error) { - dev_err(&client->dev, - "src controls initialization failed (%d)\n", - sensor->src->ctrl_handler.error); - rval = sensor->src->ctrl_handler.error; - goto error; - } - - sensor->src->sd.ctrl_handler = - &sensor->src->ctrl_handler; - - return 0; - -error: - v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); - v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); - - return rval; -} - -static void smiapp_free_controls(struct smiapp_sensor *sensor) -{ - unsigned int i; - - for (i = 0; i < sensor->ssds_used; i++) - v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); -} - -static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, - unsigned int n) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int i; - u32 val; - int rval; - - for (i = 0; i < n; i++) { - rval = smiapp_read( - sensor, smiapp_reg_limits[limit[i]].addr, &val); - if (rval) - return rval; - sensor->limits[limit[i]] = val; - dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limit[i]].addr, - smiapp_reg_limits[limit[i]].what, val, val); - } - - return 0; -} - -static int smiapp_get_all_limits(struct smiapp_sensor *sensor) -{ - unsigned int i; - int rval; - - for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { - rval = smiapp_get_limits(sensor, &i, 1); - if (rval < 0) - return rval; - } - - if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) - smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); - - return 0; -} - -static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - static u32 const limits[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, - }; - static u32 const limits_replace[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, - }; - unsigned int i; - int rval; - - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == - SMIAPP_BINNING_CAPABILITY_NO) { - for (i = 0; i < ARRAY_SIZE(limits); i++) - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - - return 0; - } - - rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); - if (rval < 0) - return rval; - - /* - * Sanity check whether the binning limits are valid. If not, - * use the non-binning ones. - */ - if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) - return 0; - - for (i = 0; i < ARRAY_SIZE(limits); i++) { - dev_dbg(&client->dev, - "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limits[i]].addr, - smiapp_reg_limits[limits[i]].what, - sensor->limits[limits_replace[i]], - sensor->limits[limits_replace[i]]); - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - } - - return 0; -} - -static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int type, n; - unsigned int i, pixel_order; - int rval; - - rval = smiapp_read( - sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); - if (rval) - return rval; - - dev_dbg(&client->dev, "data_format_model_type %d\n", type); - - rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, - &pixel_order); - if (rval) - return rval; - - if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { - dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); - return -EINVAL; - } - - dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, - pixel_order_str[pixel_order]); - - switch (type) { - case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: - n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; - break; - case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: - n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; - break; - default: - return -EINVAL; - } - - sensor->default_pixel_order = pixel_order; - sensor->mbus_frame_fmts = 0; - - for (i = 0; i < n; i++) { - unsigned int fmt, j; - - rval = smiapp_read( - sensor, - SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); - if (rval) - return rval; - - dev_dbg(&client->dev, "bpp %d, compressed %d\n", - fmt >> 8, (u8)fmt); - - for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { - const struct smiapp_csi_data_format *f = - &smiapp_csi_data_formats[j]; - - if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) - continue; - - if (f->width != fmt >> 8 || f->compressed != (u8)fmt) - continue; - - dev_dbg(&client->dev, "jolly good! %d\n", j); - - sensor->default_mbus_frame_fmts |= 1 << j; - if (!sensor->csi_format) { - sensor->csi_format = f; - sensor->internal_csi_format = f; - } - } - } - - if (!sensor->csi_format) { - dev_err(&client->dev, "no supported mbus code found\n"); - return -EINVAL; - } - - smiapp_update_mbus_formats(sensor); - - return 0; -} - -static void smiapp_update_blanking(struct smiapp_sensor *sensor) -{ - struct v4l2_ctrl *vblank = sensor->vblank; - struct v4l2_ctrl *hblank = sensor->hblank; - - vblank->minimum = - max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); - vblank->maximum = - sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; - - vblank->val = clamp_t(int, vblank->val, - vblank->minimum, vblank->maximum); - vblank->default_value = vblank->minimum; - vblank->val = vblank->val; - vblank->cur.val = vblank->val; - - hblank->minimum = - max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); - hblank->maximum = - sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; - - hblank->val = clamp_t(int, hblank->val, - hblank->minimum, hblank->maximum); - hblank->default_value = hblank->minimum; - hblank->val = hblank->val; - hblank->cur.val = hblank->val; - - __smiapp_update_exposure_limits(sensor); -} - -static int smiapp_update_mode(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int binning_mode; - int rval; - - dev_dbg(&client->dev, "frame size: %dx%d\n", - sensor->src->crop[SMIAPP_PAD_SRC].width, - sensor->src->crop[SMIAPP_PAD_SRC].height); - dev_dbg(&client->dev, "csi format width: %d\n", - sensor->csi_format->width); - - /* Binning has to be set up here; it affects limits */ - if (sensor->binning_horizontal == 1 && - sensor->binning_vertical == 1) { - binning_mode = 0; - } else { - u8 binning_type = - (sensor->binning_horizontal << 4) - | sensor->binning_vertical; - - rval = smiapp_write( - sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); - if (rval < 0) - return rval; - - binning_mode = 1; - } - rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); - if (rval < 0) - return rval; - - /* Get updated limits due to binning */ - rval = smiapp_get_limits_binning(sensor); - if (rval < 0) - return rval; - - rval = smiapp_pll_update(sensor); - if (rval < 0) - return rval; - - /* Output from pixel array, including blanking */ - smiapp_update_blanking(sensor); - - dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); - dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); - - dev_dbg(&client->dev, "real timeperframe\t100/%d\n", - sensor->pll.vt_pix_clk_freq_hz / - ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - + sensor->hblank->val) * - (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + sensor->vblank->val) / 100)); - - return 0; -} - -/* - * - * SMIA++ NVM handling - * - */ -static int smiapp_read_nvm(struct smiapp_sensor *sensor, - unsigned char *nvm) -{ - u32 i, s, p, np, v; - int rval = 0, rval2; - - np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; - for (p = 0; p < np; p++) { - rval = smiapp_write( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); - if (rval) - goto out; - - rval = smiapp_write(sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, - SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | - SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); - if (rval) - goto out; - - for (i = 0; i < 1000; i++) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); - - if (rval) - goto out; - - if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) - break; - - if (--i == 0) { - rval = -ETIMEDOUT; - goto out; - } - - } - - for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, - &v); - if (rval) - goto out; - - *nvm++ = v; - } - } - -out: - rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); - if (rval < 0) - return rval; - else - return rval2; -} - -/* - * - * SMIA++ CCI address control - * - */ -static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - u32 val; - - client->addr = sensor->platform_data->i2c_addr_dfl; - - rval = smiapp_write(sensor, - SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, - sensor->platform_data->i2c_addr_alt << 1); - if (rval) - return rval; - - client->addr = sensor->platform_data->i2c_addr_alt; - - /* verify addr change went ok */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); - if (rval) - return rval; - - if (val != sensor->platform_data->i2c_addr_alt << 1) - return -ENODEV; - - return 0; -} - -/* - * - * SMIA++ Mode Control - * - */ -static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) -{ - struct smiapp_flash_strobe_parms *strobe_setup; - unsigned int ext_freq = sensor->platform_data->ext_clk; - u32 tmp; - u32 strobe_adjustment; - u32 strobe_width_high_rs; - int rval; - - strobe_setup = sensor->platform_data->strobe_setup; - - /* - * How to calculate registers related to strobe length. Please - * do not change, or if you do at least know what you're - * doing. :-) - * - * Sakari Ailus 2010-10-25 - * - * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl - * / EXTCLK freq [Hz]) * flash_strobe_adjustment - * - * tFlash_strobe_width_ctrl E N, [1 - 0xffff] - * flash_strobe_adjustment E N, [1 - 0xff] - * - * The formula above is written as below to keep it on one - * line: - * - * l / 10^6 = w / e * a - * - * Let's mark w * a by x: - * - * x = w * a - * - * Thus, we get: - * - * x = l * e / 10^6 - * - * The strobe width must be at least as long as requested, - * thus rounding upwards is needed. - * - * x = (l * e + 10^6 - 1) / 10^6 - * ----------------------------- - * - * Maximum possible accuracy is wanted at all times. Thus keep - * a as small as possible. - * - * Calculate a, assuming maximum w, with rounding upwards: - * - * a = (x + (2^16 - 1) - 1) / (2^16 - 1) - * ------------------------------------- - * - * Thus, we also get w, with that a, with rounding upwards: - * - * w = (x + a - 1) / a - * ------------------- - * - * To get limits: - * - * x E [1, (2^16 - 1) * (2^8 - 1)] - * - * Substituting maximum x to the original formula (with rounding), - * the maximum l is thus - * - * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 - * - * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e - * -------------------------------------------------- - * - * flash_strobe_length must be clamped between 1 and - * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. - * - * Then, - * - * flash_strobe_adjustment = ((flash_strobe_length * - * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) - * - * tFlash_strobe_width_ctrl = ((flash_strobe_length * - * EXTCLK freq + 10^6 - 1) / 10^6 + - * flash_strobe_adjustment - 1) / flash_strobe_adjustment - */ - tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - - 1000000 + 1, ext_freq); - strobe_setup->strobe_width_high_us = - clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); - - tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + - 1000000 - 1), 1000000ULL); - strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); - strobe_width_high_rs = (tmp + strobe_adjustment - 1) / - strobe_adjustment; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, - strobe_setup->mode); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, - strobe_adjustment); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, - strobe_width_high_rs); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, - strobe_setup->strobe_delay); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, - strobe_setup->stobe_start_point); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, - strobe_setup->trigger); - -out: - sensor->platform_data->strobe_setup->trigger = 0; - - return rval; -} - -/* ----------------------------------------------------------------------------- - * Power management - */ - -static int smiapp_power_on(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int sleep; - int rval; - - rval = regulator_enable(sensor->vana); - if (rval) { - dev_err(&client->dev, "failed to enable vana regulator\n"); - return rval; - } - usleep_range(1000, 1000); - - if (sensor->platform_data->set_xclk) - rval = sensor->platform_data->set_xclk( - &sensor->src->sd, sensor->platform_data->ext_clk); - else - rval = clk_enable(sensor->ext_clk); - if (rval < 0) { - dev_dbg(&client->dev, "failed to set xclk\n"); - goto out_xclk_fail; - } - usleep_range(1000, 1000); - - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 1); - - sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); - usleep_range(sleep, sleep); - - /* - * Failures to respond to the address change command have been noticed. - * Those failures seem to be caused by the sensor requiring a longer - * boot time than advertised. An additional 10ms delay seems to work - * around the issue, but the SMIA++ I2C write retry hack makes the delay - * unnecessary. The failures need to be investigated to find a proper - * fix, and a delay will likely need to be added here if the I2C write - * retry hack is reverted before the root cause of the boot time issue - * is found. - */ - - if (sensor->platform_data->i2c_addr_alt) { - rval = smiapp_change_cci_addr(sensor); - if (rval) { - dev_err(&client->dev, "cci address change error\n"); - goto out_cci_addr_fail; - } - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, - SMIAPP_SOFTWARE_RESET); - if (rval < 0) { - dev_err(&client->dev, "software reset failed\n"); - goto out_cci_addr_fail; - } - - if (sensor->platform_data->i2c_addr_alt) { - rval = smiapp_change_cci_addr(sensor); - if (rval) { - dev_err(&client->dev, "cci address change error\n"); - goto out_cci_addr_fail; - } - } - - rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, - SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); - if (rval) { - dev_err(&client->dev, "compression mode set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, - sensor->platform_data->ext_clk / (1000000 / (1 << 8))); - if (rval) { - dev_err(&client->dev, "extclk frequency set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, - sensor->platform_data->lanes - 1); - if (rval) { - dev_err(&client->dev, "csi lane mode set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, - SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); - if (rval) { - dev_err(&client->dev, "fast standby set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, - sensor->platform_data->csi_signalling_mode); - if (rval) { - dev_err(&client->dev, "csi signalling mode set failed\n"); - goto out_cci_addr_fail; - } - - /* DPHY control done by sensor based on requested link rate */ - rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, - SMIAPP_DPHY_CTRL_UI); - if (rval < 0) - return rval; - - rval = smiapp_call_quirk(sensor, post_poweron); - if (rval) { - dev_err(&client->dev, "post_poweron quirks failed\n"); - goto out_cci_addr_fail; - } - - /* Are we still initialising...? If yes, return here. */ - if (!sensor->pixel_array) - return 0; - - rval = v4l2_ctrl_handler_setup( - &sensor->pixel_array->ctrl_handler); - if (rval) - goto out_cci_addr_fail; - - rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); - if (rval) - goto out_cci_addr_fail; - - mutex_lock(&sensor->mutex); - rval = smiapp_update_mode(sensor); - mutex_unlock(&sensor->mutex); - if (rval < 0) - goto out_cci_addr_fail; - - return 0; - -out_cci_addr_fail: - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable(sensor->ext_clk); - -out_xclk_fail: - regulator_disable(sensor->vana); - return rval; -} - -static void smiapp_power_off(struct smiapp_sensor *sensor) -{ - /* - * Currently power/clock to lens are enable/disabled separately - * but they are essentially the same signals. So if the sensor is - * powered off while the lens is powered on the sensor does not - * really see a power off and next time the cci address change - * will fail. So do a soft reset explicitly here. - */ - if (sensor->platform_data->i2c_addr_alt) - smiapp_write(sensor, - SMIAPP_REG_U8_SOFTWARE_RESET, - SMIAPP_SOFTWARE_RESET); - - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable(sensor->ext_clk); - usleep_range(5000, 5000); - regulator_disable(sensor->vana); - sensor->streaming = 0; -} - -static int smiapp_set_power(struct v4l2_subdev *subdev, int on) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int ret = 0; - - mutex_lock(&sensor->power_mutex); - - /* - * If the power count is modified from 0 to != 0 or from != 0 - * to 0, update the power state. - */ - if (!sensor->power_count == !on) - goto out; - - if (on) { - /* Power on and perform initialisation. */ - ret = smiapp_power_on(sensor); - if (ret < 0) - goto out; - } else { - smiapp_power_off(sensor); - } - - /* Update the power count. */ - sensor->power_count += on ? 1 : -1; - WARN_ON(sensor->power_count < 0); - -out: - mutex_unlock(&sensor->power_mutex); - return ret; -} - -/* ----------------------------------------------------------------------------- - * Video stream management - */ - -static int smiapp_start_streaming(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - mutex_lock(&sensor->mutex); - - rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, - (sensor->csi_format->width << 8) | - sensor->csi_format->compressed); - if (rval) - goto out; - - rval = smiapp_pll_configure(sensor); - if (rval) - goto out; - - /* Analog crop start coordinates */ - rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); - if (rval < 0) - goto out; - - /* Analog crop end coordinates */ - rval = smiapp_write( - sensor, SMIAPP_REG_U16_X_ADDR_END, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_Y_ADDR_END, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); - if (rval < 0) - goto out; - - /* - * Output from pixel array, including blanking, is set using - * controls below. No need to set here. - */ - - /* Digital crop */ - if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, - sensor->scaler->crop[SMIAPP_PAD_SINK].left); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, - sensor->scaler->crop[SMIAPP_PAD_SINK].top); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, - sensor->scaler->crop[SMIAPP_PAD_SINK].width); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, - sensor->scaler->crop[SMIAPP_PAD_SINK].height); - if (rval < 0) - goto out; - } - - /* Scaling */ - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - != SMIAPP_SCALING_CAPABILITY_NONE) { - rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, - sensor->scaling_mode); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, - sensor->scale_m); - if (rval < 0) - goto out; - } - - /* Output size from sensor */ - rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, - sensor->src->crop[SMIAPP_PAD_SRC].width); - if (rval < 0) - goto out; - rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, - sensor->src->crop[SMIAPP_PAD_SRC].height); - if (rval < 0) - goto out; - - if ((sensor->flash_capability & - (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | - SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && - sensor->platform_data->strobe_setup != NULL && - sensor->platform_data->strobe_setup->trigger != 0) { - rval = smiapp_setup_flash_strobe(sensor); - if (rval) - goto out; - } - - rval = smiapp_call_quirk(sensor, pre_streamon); - if (rval) { - dev_err(&client->dev, "pre_streamon quirks failed\n"); - goto out; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, - SMIAPP_MODE_SELECT_STREAMING); - -out: - mutex_unlock(&sensor->mutex); - - return rval; -} - -static int smiapp_stop_streaming(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - mutex_lock(&sensor->mutex); - rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, - SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); - if (rval) - goto out; - - rval = smiapp_call_quirk(sensor, post_streamoff); - if (rval) - dev_err(&client->dev, "post_streamoff quirks failed\n"); - -out: - mutex_unlock(&sensor->mutex); - return rval; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - if (sensor->streaming == enable) - return 0; - - if (enable) { - sensor->streaming = 1; - rval = smiapp_start_streaming(sensor); - if (rval < 0) - sensor->streaming = 0; - } else { - rval = smiapp_stop_streaming(sensor); - sensor->streaming = 0; - } - - return rval; -} - -static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - int idx = -1; - int rval = -EINVAL; - - mutex_lock(&sensor->mutex); - - dev_err(&client->dev, "subdev %s, pad %d, index %d\n", - subdev->name, code->pad, code->index); - - if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { - if (code->index) - goto out; - - code->code = sensor->internal_csi_format->code; - rval = 0; - goto out; - } - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - if (sensor->mbus_frame_fmts & (1 << i)) - idx++; - - if (idx == code->index) { - code->code = smiapp_csi_data_formats[i].code; - dev_err(&client->dev, "found index %d, i %d, code %x\n", - code->index, i, code->code); - rval = 0; - break; - } - } - -out: - mutex_unlock(&sensor->mutex); - - return rval; -} - -static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, - unsigned int pad) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - - if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) - return sensor->csi_format->code; - else - return sensor->internal_csi_format->code; -} - -static int __smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); - } else { - struct v4l2_rect *r; - - if (fmt->pad == ssd->source_pad) - r = &ssd->crop[ssd->source_pad]; - else - r = &ssd->sink_fmt; - - fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); - fmt->format.width = r->width; - fmt->format.height = r->height; - } - - return 0; -} - -static int smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __smiapp_get_format(subdev, fh, fmt); - mutex_unlock(&sensor->mutex); - - return rval; -} - -static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_rect **crops, - struct v4l2_rect **comps, int which) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - unsigned int i; - - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (crops) - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = &ssd->crop[i]; - if (comps) - *comps = &ssd->compose; - } else { - if (crops) { - for (i = 0; i < subdev->entity.num_pads; i++) { - crops[i] = v4l2_subdev_get_try_crop(fh, i); - BUG_ON(!crops[i]); - } - } - if (comps) { - *comps = v4l2_subdev_get_try_compose(fh, - SMIAPP_PAD_SINK); - BUG_ON(!*comps); - } - } -} - -/* Changes require propagation only on sink pad. */ -static void smiapp_propagate(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, int which, - int target) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - - smiapp_get_crop_compose(subdev, fh, crops, &comp, which); - - switch (target) { - case V4L2_SEL_TGT_CROP: - comp->width = crops[SMIAPP_PAD_SINK]->width; - comp->height = crops[SMIAPP_PAD_SINK]->height; - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (ssd == sensor->scaler) { - sensor->scale_m = - sensor->limits[ - SMIAPP_LIMIT_SCALER_N_MIN]; - sensor->scaling_mode = - SMIAPP_SCALING_MODE_NONE; - } else if (ssd == sensor->binner) { - sensor->binning_horizontal = 1; - sensor->binning_vertical = 1; - } - } - /* Fall through */ - case V4L2_SEL_TGT_COMPOSE: - *crops[SMIAPP_PAD_SRC] = *comp; - break; - default: - BUG(); - } -} - -static const struct smiapp_csi_data_format -*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) -{ - const struct smiapp_csi_data_format *csi_format = sensor->csi_format; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - if (sensor->mbus_frame_fmts & (1 << i) - && smiapp_csi_data_formats[i].code == code) - return &smiapp_csi_data_formats[i]; - } - - return csi_format; -} - -static int smiapp_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *crops[SMIAPP_PADS]; - - mutex_lock(&sensor->mutex); - - /* - * Media bus code is changeable on src subdev's source pad. On - * other source pads we just get format here. - */ - if (fmt->pad == ssd->source_pad) { - u32 code = fmt->format.code; - int rval = __smiapp_get_format(subdev, fh, fmt); - - if (!rval && subdev == &sensor->src->sd) { - const struct smiapp_csi_data_format *csi_format = - smiapp_validate_csi_data_format(sensor, code); - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - sensor->csi_format = csi_format; - fmt->format.code = csi_format->code; - } - - mutex_unlock(&sensor->mutex); - return rval; - } - - /* Sink pad. Width and height are changeable here. */ - fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); - fmt->format.width &= ~1; - fmt->format.height &= ~1; - - fmt->format.width = - clamp(fmt->format.width, - sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], - sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); - fmt->format.height = - clamp(fmt->format.height, - sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], - sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); - - smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); - - crops[ssd->sink_pad]->left = 0; - crops[ssd->sink_pad]->top = 0; - crops[ssd->sink_pad]->width = fmt->format.width; - crops[ssd->sink_pad]->height = fmt->format.height; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ssd->sink_fmt = *crops[ssd->sink_pad]; - smiapp_propagate(subdev, fh, fmt->which, - V4L2_SEL_TGT_CROP); - - mutex_unlock(&sensor->mutex); - - return 0; -} - -/* - * Calculate goodness of scaled image size compared to expected image - * size and flags provided. - */ -#define SCALING_GOODNESS 100000 -#define SCALING_GOODNESS_EXTREME 100000000 -static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, - int h, int ask_h, u32 flags) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - int val = 0; - - w &= ~1; - ask_w &= ~1; - h &= ~1; - ask_h &= ~1; - - if (flags & V4L2_SEL_FLAG_GE) { - if (w < ask_w) - val -= SCALING_GOODNESS; - if (h < ask_h) - val -= SCALING_GOODNESS; - } - - if (flags & V4L2_SEL_FLAG_LE) { - if (w > ask_w) - val -= SCALING_GOODNESS; - if (h > ask_h) - val -= SCALING_GOODNESS; - } - - val -= abs(w - ask_w); - val -= abs(h - ask_h); - - if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) - val -= SCALING_GOODNESS_EXTREME; - - dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", - w, ask_h, h, ask_h, val); - - return val; -} - -static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - unsigned int binh = 1, binv = 1; - unsigned int best = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width, sel->r.width, - crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); - - for (i = 0; i < sensor->nbinning_subtypes; i++) { - int this = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width - / sensor->binning_subtypes[i].horizontal, - sel->r.width, - crops[SMIAPP_PAD_SINK]->height - / sensor->binning_subtypes[i].vertical, - sel->r.height, sel->flags); - - if (this > best) { - binh = sensor->binning_subtypes[i].horizontal; - binv = sensor->binning_subtypes[i].vertical; - best = this; - } - } - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sensor->binning_vertical = binv; - sensor->binning_horizontal = binh; - } - - sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; - sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; -} - -/* - * Calculate best scaling ratio and mode for given output resolution. - * - * Try all of these: horizontal ratio, vertical ratio and smallest - * size possible (horizontally). - * - * Also try whether horizontal scaler or full scaler gives a better - * result. - */ -static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - u32 min, max, a, b, max_m; - u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; - int mode = SMIAPP_SCALING_MODE_HORIZONTAL; - u32 try[4]; - u32 ntry = 0; - unsigned int i; - int best = INT_MIN; - - sel->r.width = min_t(unsigned int, sel->r.width, - crops[SMIAPP_PAD_SINK]->width); - sel->r.height = min_t(unsigned int, sel->r.height, - crops[SMIAPP_PAD_SINK]->height); - - a = crops[SMIAPP_PAD_SINK]->width - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; - b = crops[SMIAPP_PAD_SINK]->height - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; - max_m = crops[SMIAPP_PAD_SINK]->width - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] - / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; - - a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - - dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); - - min = min(max_m, min(a, b)); - max = min(max_m, max(a, b)); - - try[ntry] = min; - ntry++; - if (min != max) { - try[ntry] = max; - ntry++; - } - if (max != max_m) { - try[ntry] = min + 1; - ntry++; - if (min != max) { - try[ntry] = max + 1; - ntry++; - } - } - - for (i = 0; i < ntry; i++) { - int this = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width - / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], - sel->r.width, - crops[SMIAPP_PAD_SINK]->height, - sel->r.height, - sel->flags); - - dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); - - if (this > best) { - scale_m = try[i]; - mode = SMIAPP_SCALING_MODE_HORIZONTAL; - best = this; - } - - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) - continue; - - this = scaling_goodness( - subdev, crops[SMIAPP_PAD_SINK]->width - / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], - sel->r.width, - crops[SMIAPP_PAD_SINK]->height - / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], - sel->r.height, - sel->flags); - - if (this > best) { - scale_m = try[i]; - mode = SMIAPP_SCALING_MODE_BOTH; - best = this; - } - } - - sel->r.width = - (crops[SMIAPP_PAD_SINK]->width - / scale_m - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; - if (mode == SMIAPP_SCALING_MODE_BOTH) - sel->r.height = - (crops[SMIAPP_PAD_SINK]->height - / scale_m - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) - & ~1; - else - sel->r.height = crops[SMIAPP_PAD_SINK]->height; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sensor->scale_m = scale_m; - sensor->scaling_mode = mode; - } -} -/* We're only called on source pads. This function sets scaling. */ -static int smiapp_set_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - - smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); - - sel->r.top = 0; - sel->r.left = 0; - - if (ssd == sensor->binner) - smiapp_set_compose_binner(subdev, fh, sel, crops, comp); - else - smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); - - *comp = sel->r; - smiapp_propagate(subdev, fh, sel->which, - V4L2_SEL_TGT_COMPOSE); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return smiapp_update_mode(sensor); - - return 0; -} - -static int __smiapp_sel_supported(struct v4l2_subdev *subdev, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - - /* We only implement crop in three places. */ - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_CROP_BOUNDS: - if (ssd == sensor->pixel_array - && sel->pad == SMIAPP_PA_PAD_SRC) - return 0; - if (ssd == sensor->src - && sel->pad == SMIAPP_PAD_SRC) - return 0; - if (ssd == sensor->scaler - && sel->pad == SMIAPP_PAD_SINK - && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) - return 0; - return -EINVAL; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - if (sel->pad == ssd->source_pad) - return -EINVAL; - if (ssd == sensor->binner) - return 0; - if (ssd == sensor->scaler - && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - != SMIAPP_SCALING_CAPABILITY_NONE) - return 0; - /* Fall through */ - default: - return -EINVAL; - } -} - -static int smiapp_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; - struct v4l2_rect _r; - - smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (sel->pad == ssd->sink_pad) - src_size = &ssd->sink_fmt; - else - src_size = &ssd->compose; - } else { - if (sel->pad == ssd->sink_pad) { - _r.left = 0; - _r.top = 0; - _r.width = v4l2_subdev_get_try_format(fh, sel->pad) - ->width; - _r.height = v4l2_subdev_get_try_format(fh, sel->pad) - ->height; - src_size = &_r; - } else { - src_size = - v4l2_subdev_get_try_compose( - fh, ssd->sink_pad); - } - } - - if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { - sel->r.left = 0; - sel->r.top = 0; - } - - sel->r.width = min(sel->r.width, src_size->width); - sel->r.height = min(sel->r.height, src_size->height); - - sel->r.left = min(sel->r.left, src_size->width - sel->r.width); - sel->r.top = min(sel->r.top, src_size->height - sel->r.height); - - *crops[sel->pad] = sel->r; - - if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) - smiapp_propagate(subdev, fh, sel->which, - V4L2_SEL_TGT_CROP); - - return 0; -} - -static int __smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - struct v4l2_rect sink_fmt; - int ret; - - ret = __smiapp_sel_supported(subdev, sel); - if (ret) - return ret; - - smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sink_fmt = ssd->sink_fmt; - } else { - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(fh, ssd->sink_pad); - - sink_fmt.left = 0; - sink_fmt.top = 0; - sink_fmt.width = fmt->width; - sink_fmt.height = fmt->height; - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - if (ssd == sensor->pixel_array) { - sel->r.width = - sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; - sel->r.height = - sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; - } else if (sel->pad == ssd->sink_pad) { - sel->r = sink_fmt; - } else { - sel->r = *comp; - } - break; - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - sel->r = *crops[sel->pad]; - break; - case V4L2_SEL_TGT_COMPOSE: - sel->r = *comp; - break; - } - - return 0; -} - -static int smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __smiapp_get_selection(subdev, fh, sel); - mutex_unlock(&sensor->mutex); - - return rval; -} -static int smiapp_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int ret; - - ret = __smiapp_sel_supported(subdev, sel); - if (ret) - return ret; - - mutex_lock(&sensor->mutex); - - sel->r.left = max(0, sel->r.left & ~1); - sel->r.top = max(0, sel->r.top & ~1); - sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); - sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); - - sel->r.width = max_t(unsigned int, - sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], - sel->r.width); - sel->r.height = max_t(unsigned int, - sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], - sel->r.height); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - ret = smiapp_set_crop(subdev, fh, sel); - break; - case V4L2_SEL_TGT_COMPOSE: - ret = smiapp_set_compose(subdev, fh, sel); - break; - default: - BUG(); - } - - mutex_unlock(&sensor->mutex); - return ret; -} - -static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - - *frames = sensor->frame_skip; - return 0; -} - -/* ----------------------------------------------------------------------------- - * sysfs attributes - */ - -static ssize_t -smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int nbytes; - - if (!sensor->dev_init_done) - return -EBUSY; - - if (!sensor->nvm_size) { - /* NVM not read yet - read it now */ - sensor->nvm_size = sensor->platform_data->nvm_size; - if (smiapp_set_power(subdev, 1) < 0) - return -ENODEV; - if (smiapp_read_nvm(sensor, sensor->nvm)) { - dev_err(&client->dev, "nvm read failed\n"); - return -ENODEV; - } - smiapp_set_power(subdev, 0); - } - /* - * NVM is still way below a PAGE_SIZE, so we can safely - * assume this for now. - */ - nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); - memcpy(buf, sensor->nvm, nbytes); - - return nbytes; -} -static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int smiapp_identify_module(struct v4l2_subdev *subdev) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_module_info *minfo = &sensor->minfo; - unsigned int i; - int rval = 0; - - minfo->name = SMIAPP_NAME; - - /* Module info */ - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, - &minfo->manufacturer_id); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, - &minfo->model_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, - &minfo->revision_number_major); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_REVISION_NUMBER_MINOR, - &minfo->revision_number_minor); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_MODULE_DATE_YEAR, - &minfo->module_year); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_MODULE_DATE_MONTH, - &minfo->module_month); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, - &minfo->module_day); - - /* Sensor info */ - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, - &minfo->sensor_manufacturer_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U16_SENSOR_MODEL_ID, - &minfo->sensor_model_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, - &minfo->sensor_revision_number); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, - &minfo->sensor_firmware_version); - - /* SMIA */ - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, - &minfo->smia_version); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, - &minfo->smiapp_version); - - if (rval) { - dev_err(&client->dev, "sensor detection failed\n"); - return -ENODEV; - } - - dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", - minfo->manufacturer_id, minfo->model_id); - - dev_dbg(&client->dev, - "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", - minfo->revision_number_major, minfo->revision_number_minor, - minfo->module_year, minfo->module_month, minfo->module_day); - - dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", - minfo->sensor_manufacturer_id, minfo->sensor_model_id); - - dev_dbg(&client->dev, - "sensor revision 0x%2.2x firmware version 0x%2.2x\n", - minfo->sensor_revision_number, minfo->sensor_firmware_version); - - dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", - minfo->smia_version, minfo->smiapp_version); - - /* - * Some modules have bad data in the lvalues below. Hope the - * rvalues have better stuff. The lvalues are module - * parameters whereas the rvalues are sensor parameters. - */ - if (!minfo->manufacturer_id && !minfo->model_id) { - minfo->manufacturer_id = minfo->sensor_manufacturer_id; - minfo->model_id = minfo->sensor_model_id; - minfo->revision_number_major = minfo->sensor_revision_number; - } - - for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { - if (smiapp_module_idents[i].manufacturer_id - != minfo->manufacturer_id) - continue; - if (smiapp_module_idents[i].model_id != minfo->model_id) - continue; - if (smiapp_module_idents[i].flags - & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { - if (smiapp_module_idents[i].revision_number_major - < minfo->revision_number_major) - continue; - } else { - if (smiapp_module_idents[i].revision_number_major - != minfo->revision_number_major) - continue; - } - - minfo->name = smiapp_module_idents[i].name; - minfo->quirk = smiapp_module_idents[i].quirk; - break; - } - - if (i >= ARRAY_SIZE(smiapp_module_idents)) - dev_warn(&client->dev, - "no quirks for this module; let's hope it's fully compliant\n"); - - dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", - minfo->name, minfo->manufacturer_id, minfo->model_id, - minfo->revision_number_major); - - strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); - - return 0; -} - -static const struct v4l2_subdev_ops smiapp_ops; -static const struct v4l2_subdev_internal_ops smiapp_internal_ops; -static const struct media_entity_operations smiapp_entity_ops; - -static int smiapp_registered(struct v4l2_subdev *subdev) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_subdev *last = NULL; - u32 tmp; - unsigned int i; - int rval; - - sensor->vana = regulator_get(&client->dev, "VANA"); - if (IS_ERR(sensor->vana)) { - dev_err(&client->dev, "could not get regulator for vana\n"); - return -ENODEV; - } - - if (!sensor->platform_data->set_xclk) { - sensor->ext_clk = clk_get(&client->dev, - sensor->platform_data->ext_clk_name); - if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock %s\n", - sensor->platform_data->ext_clk_name); - rval = -ENODEV; - goto out_clk_get; - } - - rval = clk_set_rate(sensor->ext_clk, - sensor->platform_data->ext_clk); - if (rval < 0) { - dev_err(&client->dev, - "unable to set clock %s freq to %u\n", - sensor->platform_data->ext_clk_name, - sensor->platform_data->ext_clk); - rval = -ENODEV; - goto out_clk_set_rate; - } - } - - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { - if (gpio_request_one(sensor->platform_data->xshutdown, 0, - "SMIA++ xshutdown") != 0) { - dev_err(&client->dev, - "unable to acquire reset gpio %d\n", - sensor->platform_data->xshutdown); - rval = -ENODEV; - goto out_clk_set_rate; - } - } - - rval = smiapp_power_on(sensor); - if (rval) { - rval = -ENODEV; - goto out_smiapp_power_on; - } - - rval = smiapp_identify_module(subdev); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - rval = smiapp_get_all_limits(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - /* - * Handle Sensor Module orientation on the board. - * - * The application of H-FLIP and V-FLIP on the sensor is modified by - * the sensor orientation on the board. - * - * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set - * both H-FLIP and V-FLIP for normal operation which also implies - * that a set/unset operation for user space HFLIP and VFLIP v4l2 - * controls will need to be internally inverted. - * - * Rotation also changes the bayer pattern. - */ - if (sensor->platform_data->module_board_orient == - SMIAPP_MODULE_BOARD_ORIENT_180) - sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | - SMIAPP_IMAGE_ORIENTATION_VFLIP; - - rval = smiapp_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { - u32 val; - - rval = smiapp_read(sensor, - SMIAPP_REG_U8_BINNING_SUBTYPES, &val); - if (rval < 0) { - rval = -ENODEV; - goto out_power_off; - } - sensor->nbinning_subtypes = min_t(u8, val, - SMIAPP_BINNING_SUBTYPES); - - for (i = 0; i < sensor->nbinning_subtypes; i++) { - rval = smiapp_read( - sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); - if (rval < 0) { - rval = -ENODEV; - goto out_power_off; - } - sensor->binning_subtypes[i] = - *(struct smiapp_binning_subtype *)&val; - - dev_dbg(&client->dev, "binning %xx%x\n", - sensor->binning_subtypes[i].horizontal, - sensor->binning_subtypes[i].vertical); - } - } - sensor->binning_horizontal = 1; - sensor->binning_vertical = 1; - - /* SMIA++ NVM initialization - it will be read from the sensor - * when it is first requested by userspace. - */ - if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { - sensor->nvm = kzalloc(sensor->platform_data->nvm_size, - GFP_KERNEL); - if (sensor->nvm == NULL) { - dev_err(&client->dev, "nvm buf allocation failed\n"); - rval = -ENOMEM; - goto out_power_off; - } - - if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { - dev_err(&client->dev, "sysfs nvm entry failed\n"); - rval = -EBUSY; - goto out_power_off; - } - } - - rval = smiapp_call_quirk(sensor, limits); - if (rval) { - dev_err(&client->dev, "limits quirks failed\n"); - goto out_nvm_release; - } - - /* We consider this as profile 0 sensor if any of these are zero. */ - if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; - } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - != SMIAPP_SCALING_CAPABILITY_NONE) { - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] - == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; - else - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; - sensor->scaler = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - sensor->scaler = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - } - sensor->binner = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - - sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; - - for (i = 0; i < SMIAPP_SUBDEVS; i++) { - struct { - struct smiapp_subdev *ssd; - char *name; - } const __this[] = { - { sensor->scaler, "scaler", }, - { sensor->binner, "binner", }, - { sensor->pixel_array, "pixel array", }, - }, *_this = &__this[i]; - struct smiapp_subdev *this = _this->ssd; - - if (!this) - continue; - - if (this != sensor->src) - v4l2_subdev_init(&this->sd, &smiapp_ops); - - this->sensor = sensor; - - if (this == sensor->pixel_array) { - this->npads = 1; - } else { - this->npads = 2; - this->source_pad = 1; - } - - snprintf(this->sd.name, - sizeof(this->sd.name), "%s %s", - sensor->minfo.name, _this->name); - - this->sink_fmt.width = - sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; - this->sink_fmt.height = - sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; - this->compose.width = this->sink_fmt.width; - this->compose.height = this->sink_fmt.height; - this->crop[this->source_pad] = this->compose; - this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; - if (this != sensor->pixel_array) { - this->crop[this->sink_pad] = this->compose; - this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; - } - - this->sd.entity.ops = &smiapp_entity_ops; - - if (last == NULL) { - last = this; - continue; - } - - this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - this->sd.internal_ops = &smiapp_internal_ops; - this->sd.owner = NULL; - v4l2_set_subdevdata(&this->sd, client); - - rval = media_entity_init(&this->sd.entity, - this->npads, this->pads, 0); - if (rval) { - dev_err(&client->dev, - "media_entity_init failed\n"); - goto out_nvm_release; - } - - rval = media_entity_create_link(&this->sd.entity, - this->source_pad, - &last->sd.entity, - last->sink_pad, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (rval) { - dev_err(&client->dev, - "media_entity_create_link failed\n"); - goto out_nvm_release; - } - - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, - &this->sd); - if (rval) { - dev_err(&client->dev, - "v4l2_device_register_subdev failed\n"); - goto out_nvm_release; - } - - last = this; - } - - dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); - - sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - - /* final steps */ - smiapp_read_frame_fmt(sensor); - rval = smiapp_init_controls(sensor); - if (rval < 0) - goto out_nvm_release; - - rval = smiapp_update_mode(sensor); - if (rval) { - dev_err(&client->dev, "update mode failed\n"); - goto out_nvm_release; - } - - sensor->streaming = false; - sensor->dev_init_done = true; - - /* check flash capability */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); - sensor->flash_capability = tmp; - if (rval) - goto out_nvm_release; - - smiapp_power_off(sensor); - - return 0; - -out_nvm_release: - device_remove_file(&client->dev, &dev_attr_nvm); - -out_power_off: - kfree(sensor->nvm); - sensor->nvm = NULL; - smiapp_power_off(sensor); - -out_smiapp_power_on: - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_free(sensor->platform_data->xshutdown); - -out_clk_set_rate: - clk_put(sensor->ext_clk); - sensor->ext_clk = NULL; - -out_clk_get: - regulator_put(sensor->vana); - sensor->vana = NULL; - return rval; -} - -static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(sd); - struct smiapp_sensor *sensor = ssd->sensor; - u32 mbus_code = - smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; - unsigned int i; - - mutex_lock(&sensor->mutex); - - for (i = 0; i < ssd->npads; i++) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(fh, i); - struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); - struct v4l2_rect *try_comp; - - try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; - try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; - try_fmt->code = mbus_code; - - try_crop->top = 0; - try_crop->left = 0; - try_crop->width = try_fmt->width; - try_crop->height = try_fmt->height; - - if (ssd != sensor->pixel_array) - continue; - - try_comp = v4l2_subdev_get_try_compose(fh, i); - *try_comp = *try_crop; - } - - mutex_unlock(&sensor->mutex); - - return smiapp_set_power(sd, 1); -} - -static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - return smiapp_set_power(sd, 0); -} - -static const struct v4l2_subdev_video_ops smiapp_video_ops = { - .s_stream = smiapp_set_stream, -}; - -static const struct v4l2_subdev_core_ops smiapp_core_ops = { - .s_power = smiapp_set_power, -}; - -static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { - .enum_mbus_code = smiapp_enum_mbus_code, - .get_fmt = smiapp_get_format, - .set_fmt = smiapp_set_format, - .get_selection = smiapp_get_selection, - .set_selection = smiapp_set_selection, -}; - -static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { - .g_skip_frames = smiapp_get_skip_frames, -}; - -static const struct v4l2_subdev_ops smiapp_ops = { - .core = &smiapp_core_ops, - .video = &smiapp_video_ops, - .pad = &smiapp_pad_ops, - .sensor = &smiapp_sensor_ops, -}; - -static const struct media_entity_operations smiapp_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { - .registered = smiapp_registered, - .open = smiapp_open, - .close = smiapp_close, -}; - -static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { - .open = smiapp_open, - .close = smiapp_close, -}; - -/* ----------------------------------------------------------------------------- - * I2C Driver - */ - -#ifdef CONFIG_PM - -static int smiapp_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - bool streaming; - - BUG_ON(mutex_is_locked(&sensor->mutex)); - - if (sensor->power_count == 0) - return 0; - - if (sensor->streaming) - smiapp_stop_streaming(sensor); - - streaming = sensor->streaming; - - smiapp_power_off(sensor); - - /* save state for resume */ - sensor->streaming = streaming; - - return 0; -} - -static int smiapp_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - if (sensor->power_count == 0) - return 0; - - rval = smiapp_power_on(sensor); - if (rval) - return rval; - - if (sensor->streaming) - rval = smiapp_start_streaming(sensor); - - return rval; -} - -#else - -#define smiapp_suspend NULL -#define smiapp_resume NULL - -#endif /* CONFIG_PM */ - -static int smiapp_probe(struct i2c_client *client, - const struct i2c_device_id *devid) -{ - struct smiapp_sensor *sensor; - int rval; - - if (client->dev.platform_data == NULL) - return -ENODEV; - - sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) - return -ENOMEM; - - sensor->platform_data = client->dev.platform_data; - mutex_init(&sensor->mutex); - mutex_init(&sensor->power_mutex); - sensor->src = &sensor->ssds[sensor->ssds_used]; - - v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); - sensor->src->sd.internal_ops = &smiapp_internal_src_ops; - sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - sensor->src->sensor = sensor; - - sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; - rval = media_entity_init(&sensor->src->sd.entity, 2, - sensor->src->pads, 0); - if (rval < 0) - kfree(sensor); - - return rval; -} - -static int __exit smiapp_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - - if (sensor->power_count) { - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable(sensor->ext_clk); - sensor->power_count = 0; - } - - if (sensor->nvm) { - device_remove_file(&client->dev, &dev_attr_nvm); - kfree(sensor->nvm); - } - - for (i = 0; i < sensor->ssds_used; i++) { - media_entity_cleanup(&sensor->ssds[i].sd.entity); - v4l2_device_unregister_subdev(&sensor->ssds[i].sd); - } - smiapp_free_controls(sensor); - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) - gpio_free(sensor->platform_data->xshutdown); - if (sensor->ext_clk) - clk_put(sensor->ext_clk); - if (sensor->vana) - regulator_put(sensor->vana); - - kfree(sensor); - - return 0; -} - -static const struct i2c_device_id smiapp_id_table[] = { - { SMIAPP_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, smiapp_id_table); - -static const struct dev_pm_ops smiapp_pm_ops = { - .suspend = smiapp_suspend, - .resume = smiapp_resume, -}; - -static struct i2c_driver smiapp_i2c_driver = { - .driver = { - .name = SMIAPP_NAME, - .pm = &smiapp_pm_ops, - }, - .probe = smiapp_probe, - .remove = __exit_p(smiapp_remove), - .id_table = smiapp_id_table, -}; - -module_i2c_driver(smiapp_i2c_driver); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp/smiapp-limits.c b/drivers/media/video/smiapp/smiapp-limits.c deleted file mode 100644 index 0800e095724e..000000000000 --- a/drivers/media/video/smiapp/smiapp-limits.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-limits.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include "smiapp.h" - -struct smiapp_reg_limits smiapp_reg_limits[] = { - { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, - { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, - { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, - { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ - { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, - { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, - { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ - { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, - { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, - { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ - { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, - { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, - { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, - { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, - { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ - { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, - { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, - { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, - { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, - { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ - { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, - { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, - { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ - { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, - { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, - { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, - { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, - { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ - { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, - { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, - { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, - { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, - { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ - { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, - { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, - { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ - { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, - { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, - { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, - { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, - { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ - { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, - { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, - { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, - { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, - { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ - { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, - { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, - { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, - { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, - { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ - { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, - { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, - { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, - { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, - { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ - { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, - { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, - { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, - { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, - { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ - { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ - { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, - { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, - { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, - { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, - { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ - { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, - { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, - { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ - { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, - { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, - { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, - { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, - { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ - { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, - { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, - { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, - { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, - { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ - { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, - { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, - { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, - { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, - { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ - { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, - { 0, NULL }, -}; diff --git a/drivers/media/video/smiapp/smiapp-limits.h b/drivers/media/video/smiapp/smiapp-limits.h deleted file mode 100644 index 7f4836bb78db..000000000000 --- a/drivers/media/video/smiapp/smiapp-limits.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-limits.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 -#define SMIAPP_LIMIT_THS_ZERO_MIN 3 -#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 -#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 -#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 -#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 -#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 -#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 -#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 -#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 -#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 -#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 -#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 -#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 -#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 -#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 -#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 -#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 -#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 -#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 -#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 -#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 -#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 -#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 -#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 -#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 -#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 -#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 -#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 -#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 -#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 -#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 -#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 -#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 -#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 -#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 -#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 -#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 -#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 -#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 -#define SMIAPP_LIMIT_X_ADDR_MIN 46 -#define SMIAPP_LIMIT_Y_ADDR_MIN 47 -#define SMIAPP_LIMIT_X_ADDR_MAX 48 -#define SMIAPP_LIMIT_Y_ADDR_MAX 49 -#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 -#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 -#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 -#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 -#define SMIAPP_LIMIT_MIN_EVEN_INC 54 -#define SMIAPP_LIMIT_MAX_EVEN_INC 55 -#define SMIAPP_LIMIT_MIN_ODD_INC 56 -#define SMIAPP_LIMIT_MAX_ODD_INC 57 -#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 -#define SMIAPP_LIMIT_SCALER_M_MIN 59 -#define SMIAPP_LIMIT_SCALER_M_MAX 60 -#define SMIAPP_LIMIT_SCALER_N_MIN 61 -#define SMIAPP_LIMIT_SCALER_N_MAX 62 -#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 -#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 -#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 -#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 -#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 -#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 -#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 -#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 -#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 -#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 -#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 -#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 -#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 -#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 -#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 -#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 -#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 -#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 -#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 -#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 -#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 -#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 -#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 -#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 -#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 -#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 -#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 -#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 -#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 -#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 -#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 -#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 -#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c deleted file mode 100644 index 55e87950dcea..000000000000 --- a/drivers/media/video/smiapp/smiapp-quirk.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-quirk.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include - -#include "smiapp.h" - -static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) -{ - return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); -} - -static int smiapp_write_8s(struct smiapp_sensor *sensor, - struct smiapp_reg_8 *regs, int len) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - for (; len > 0; len--, regs++) { - rval = smiapp_write_8(sensor, regs->reg, regs->val); - if (rval < 0) { - dev_err(&client->dev, - "error %d writing reg 0x%4.4x, val 0x%2.2x", - rval, regs->reg, regs->val); - return rval; - } - } - - return 0; -} - -void smiapp_replace_limit(struct smiapp_sensor *sensor, - u32 limit, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - - dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limit].addr, - smiapp_reg_limits[limit].what, val, val); - sensor->limits[limit] = val; -} - -int smiapp_replace_limit_at(struct smiapp_sensor *sensor, - u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int i; - - for (i = 0; smiapp_reg_limits[i].addr; i++) { - if ((smiapp_reg_limits[i].addr & 0xffff) != reg) - continue; - - smiapp_replace_limit(sensor, i, val); - - return 0; - } - - dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg); - - return -EINVAL; -} - -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, - u32 reg, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - const struct smia_reg *sreg; - - if (!sensor->minfo.quirk) - return false; - - sreg = sensor->minfo.quirk->regs; - - if (!sreg) - return false; - - while (sreg->type) { - u16 type = reg >> 16; - u16 reg16 = reg; - - if (sreg->type != type || sreg->reg != reg16) { - sreg++; - continue; - } - - switch ((u8)type) { - case SMIA_REG_8BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", - reg, sreg->val); - break; - case SMIA_REG_16BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", - reg, sreg->val); - break; - case SMIA_REG_32BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", - reg, sreg->val); - break; - } - - *val = sreg->val; - - return true; - } - - return false; -} - -static int jt8ew9_limits(struct smiapp_sensor *sensor) -{ - if (sensor->minfo.revision_number_major < 0x03) - sensor->frame_skip = 1; - - /* Below 24 gain doesn't have effect at all, */ - /* but ~59 is needed for full dynamic range */ - smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); - smiapp_replace_limit( - sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); - - return 0; -} - -static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) -{ - struct smiapp_reg_8 regs[] = { - { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ - { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ - { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ - { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ - { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ - { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ - { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ - { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ - { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ - { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ - { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ - { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ - { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ - { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ - { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ - /* Taken from v03. No idea what the rest are. */ - { 0x32e0, 0x05 }, - { 0x32e1, 0x05 }, - { 0x32e2, 0x04 }, - { 0x32e5, 0x04 }, - { 0x32e6, 0x04 }, - - }; - - return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); -} - -const struct smiapp_quirk smiapp_jt8ew9_quirk = { - .limits = jt8ew9_limits, - .post_poweron = jt8ew9_post_poweron, -}; - -static int imx125es_post_poweron(struct smiapp_sensor *sensor) -{ - /* Taken from v02. No idea what the other two are. */ - struct smiapp_reg_8 regs[] = { - /* - * 0x3302: clk during frame blanking: - * 0x00 - HS mode, 0x01 - LP11 - */ - { 0x3302, 0x01 }, - { 0x302d, 0x00 }, - { 0x3b08, 0x8c }, - }; - - return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); -} - -const struct smiapp_quirk smiapp_imx125es_quirk = { - .post_poweron = imx125es_post_poweron, -}; - -static int jt8ev1_limits(struct smiapp_sensor *sensor) -{ - smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); - smiapp_replace_limit(sensor, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); - - return 0; -} - -static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - struct smiapp_reg_8 regs[] = { - { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ - { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ - { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ - { 0x3238, 0x43 }, - { 0x3301, 0x06 }, /* For analog bias for sensor */ - { 0x3302, 0x06 }, - { 0x3304, 0x00 }, - { 0x3305, 0x88 }, - { 0x332a, 0x14 }, - { 0x332c, 0x6b }, - { 0x3336, 0x01 }, - { 0x333f, 0x1f }, - { 0x3355, 0x00 }, - { 0x3356, 0x20 }, - { 0x33bf, 0x20 }, /* Adjust the FBC speed */ - { 0x33c9, 0x20 }, - { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ - { 0x33cf, 0xec }, /* For Black sun */ - { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ - }; - - struct smiapp_reg_8 regs_96[] = { - { 0x30ae, 0x00 }, /* For control of ADC clock */ - { 0x30af, 0xd0 }, - { 0x30b0, 0x01 }, - }; - - rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); - if (rval < 0) - return rval; - - switch (sensor->platform_data->ext_clk) { - case 9600000: - return smiapp_write_8s(sensor, regs_96, - ARRAY_SIZE(regs_96)); - default: - dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", - sensor->platform_data->ext_clk); - return 0; - } -} - -static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) -{ - return smiapp_write_8(sensor, 0x3328, 0x00); -} - -static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) -{ - int rval; - - /* Workaround: allows fast standby to work properly */ - rval = smiapp_write_8(sensor, 0x3205, 0x04); - if (rval < 0) - return rval; - - /* Wait for 1 ms + one line => 2 ms is likely enough */ - usleep_range(2000, 2000); - - /* Restore it */ - rval = smiapp_write_8(sensor, 0x3205, 0x00); - if (rval < 0) - return rval; - - return smiapp_write_8(sensor, 0x3328, 0x80); -} - -const struct smiapp_quirk smiapp_jt8ev1_quirk = { - .limits = jt8ev1_limits, - .post_poweron = jt8ev1_post_poweron, - .pre_streamon = jt8ev1_pre_streamon, - .post_streamoff = jt8ev1_post_streamoff, - .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, -}; - -static int tcm8500md_limits(struct smiapp_sensor *sensor) -{ - smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); - - return 0; -} - -const struct smiapp_quirk smiapp_tcm8500md_quirk = { - .limits = tcm8500md_limits, -}; diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h deleted file mode 100644 index f4dcaabaefe7..000000000000 --- a/drivers/media/video/smiapp/smiapp-quirk.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-quirk.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __SMIAPP_QUIRK__ -#define __SMIAPP_QUIRK__ - -struct smiapp_sensor; - -/** - * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard - * - * @limits: Replace sensor->limits with values which can't be read from - * sensor registers. Called the first time the sensor is powered up. - * @post_poweron: Called always after the sensor has been fully powered on. - * @pre_streamon: Called just before streaming is enabled. - * @post_streamon: Called right after stopping streaming. - */ -struct smiapp_quirk { - int (*limits)(struct smiapp_sensor *sensor); - int (*post_poweron)(struct smiapp_sensor *sensor); - int (*pre_streamon)(struct smiapp_sensor *sensor); - int (*post_streamoff)(struct smiapp_sensor *sensor); - const struct smia_reg *regs; - unsigned long flags; -}; - -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) -#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) - -struct smiapp_reg_8 { - u16 reg; - u8 val; -}; - -void smiapp_replace_limit(struct smiapp_sensor *sensor, - u32 limit, u32 val); -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, - u32 reg, u32 *val); - -#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ - { \ - .type = (_reg >> 16), \ - .reg = (u16)_reg, \ - .val = _val, \ - } - -#define smiapp_call_quirk(_sensor, _quirk, ...) \ - (_sensor->minfo.quirk && \ - _sensor->minfo.quirk->_quirk ? \ - _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) - -#define smiapp_needs_quirk(_sensor, _quirk) \ - (_sensor->minfo.quirk ? \ - _sensor->minfo.quirk->flags & _quirk : 0) - -extern const struct smiapp_quirk smiapp_jt8ev1_quirk; -extern const struct smiapp_quirk smiapp_imx125es_quirk; -extern const struct smiapp_quirk smiapp_jt8ew9_quirk; -extern const struct smiapp_quirk smiapp_tcm8500md_quirk; - -#endif /* __SMIAPP_QUIRK__ */ diff --git a/drivers/media/video/smiapp/smiapp-reg-defs.h b/drivers/media/video/smiapp/smiapp-reg-defs.h deleted file mode 100644 index a089eb8161e1..000000000000 --- a/drivers/media/video/smiapp/smiapp-reg-defs.h +++ /dev/null @@ -1,503 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-reg-defs.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) -#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) -#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) - -#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) - -#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) -#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) -#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) -#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) -#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) -#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) -#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) -#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) -#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) -#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) -#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) -#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) -#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) -#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) -#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) -#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) -#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) -#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) -#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) -#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ -#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) -#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) -#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) -#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) -#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) -#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) -#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) -#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) -#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) -#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) -#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) -#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) -#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) -#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) -#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) -#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) -#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) -#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) -#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) -#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) -#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) -#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) -#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) -#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) -#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) -#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) -#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) -#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) -#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) -#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) -#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) -#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) -#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) -#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) -#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) -#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) -#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) -#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) -#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) -#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) -#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) -#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) -#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) -#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) -#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) -#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) -#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) -#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) -#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) -#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) -#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) -#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) -#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) -#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) -#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) -#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) -#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) -#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) -#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) -#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) -#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) -#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) -#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) -#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) -#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) -#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) -#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) -#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) -#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) -#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) -#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) -#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) -#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) -#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) -#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) -#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) -#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) -#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) -#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) -#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) -#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) -#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) -#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) -#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) -#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) -#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) -#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) -#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) -#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) -#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) -#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) -#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) -#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) -#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) -#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) -#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) -#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) -#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) -#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) -#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) -#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) -#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) -#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) -#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) -#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) -#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) -#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) -#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) -#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) -#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) -#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) -#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) -#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) -#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) -#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) -#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) -#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) -#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) -#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) -#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) -#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) -#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) -#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) -#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) -#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) -#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) -#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) -#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) -#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) -#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) -#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) -#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) -#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) -#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) -#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) -#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) -#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) -#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) -#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) -#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) -#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) -#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) -#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) -#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) -#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) -#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) -#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) -#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) -#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) -#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) -#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) -#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) -#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) -#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) -#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) -#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) -#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) -#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) -#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) -#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) -#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) -#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) -#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) -#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) -#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) -#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) -#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) -#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) -#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) -#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) -#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) -#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) -#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) -#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) -#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) -#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) -#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) -#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) -#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) -#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) -#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) -#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) -#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) -#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) -#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) -#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) -#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) -#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) -#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) -#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) -#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) -#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) -#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) -#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) -#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) -#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) -#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) -#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) -#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) -#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) -#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) -#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) -#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) -#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) -#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) -#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) -#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) -#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) -#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) -#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/video/smiapp/smiapp-reg.h b/drivers/media/video/smiapp/smiapp-reg.h deleted file mode 100644 index d0167aa17534..000000000000 --- a/drivers/media/video/smiapp/smiapp-reg.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-reg.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __SMIAPP_REG_H_ -#define __SMIAPP_REG_H_ - -#include "smiapp-reg-defs.h" - -/* Bits for above register */ -#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) -#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) - -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) - -#define SMIAPP_SOFTWARE_RESET (1 << 0) - -#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) -#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) - -#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 -/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ -#define SMIAPP_DPHY_CTRL_UI 1 -#define SMIAPP_DPHY_CTRL_REGISTER 2 - -#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 -#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 - -#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 -#define SMIAPP_MODE_SELECT_STREAMING 1 - -#define SMIAPP_SCALING_MODE_NONE 0 -#define SMIAPP_SCALING_MODE_HORIZONTAL 1 -#define SMIAPP_SCALING_MODE_BOTH 2 - -#define SMIAPP_SCALING_CAPABILITY_NONE 0 -#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 -#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ - -/* digital crop right before scaler */ -#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 -#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 - -#define SMIAPP_BINNING_CAPABILITY_NO 0 -#define SMIAPP_BINNING_CAPABILITY_YES 1 - -/* Maximum number of binning subtypes */ -#define SMIAPP_BINNING_SUBTYPES 253 - -#define SMIAPP_PIXEL_ORDER_GRBG 0 -#define SMIAPP_PIXEL_ORDER_RGGB 1 -#define SMIAPP_PIXEL_ORDER_BGGR 2 -#define SMIAPP_PIXEL_ORDER_GBRG 3 - -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 - -#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 -#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 - -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff - -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff - -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 - -#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 -#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 - -/* Scaling N factor */ -#define SMIAPP_SCALE_N 16 - -/* Image statistics registers */ -/* Registers 0x2000 to 0x2fff are reserved for future - * use for statistics features. - */ - -/* Manufacturer Specific Registers: 0x3000 to 0x3fff - * The manufacturer specifies these as a black box. - */ - -#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c deleted file mode 100644 index b1812b17a407..000000000000 --- a/drivers/media/video/smiapp/smiapp-regs.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-regs.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include - -#include "smiapp.h" -#include "smiapp-regs.h" - -static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, - uint32_t phloat) -{ - int32_t exp; - uint64_t man; - - if (phloat >= 0x80000000) { - dev_err(&client->dev, "this is a negative number\n"); - return 0; - } - - if (phloat == 0x7f800000) - return ~0; /* Inf. */ - - if ((phloat & 0x7f800000) == 0x7f800000) { - dev_err(&client->dev, "NaN or other special number\n"); - return 0; - } - - /* Valid cases begin here */ - if (phloat == 0) - return 0; /* Valid zero */ - - if (phloat > 0x4f800000) - return ~0; /* larger than 4294967295 */ - - /* - * Unbias exponent (note how phloat is now guaranteed to - * have 0 in the high bit) - */ - exp = ((int32_t)phloat >> 23) - 127; - - /* Extract mantissa, add missing '1' bit and it's in MHz */ - man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; - - if (exp < 0) - man >>= -exp; - else - man <<= exp; - - man >>= 23; /* Remove mantissa bias */ - - return man & 0xffffffff; -} - - -/* - * Read a 8/16/32-bit i2c register. The value is returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, - u16 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data[4]; - u16 offset = reg; - int r; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 2; - msg.buf = data; - - /* high byte goes out first */ - data[0] = (u8) (offset >> 8); - data[1] = (u8) offset; - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - msg.len = len; - msg.flags = I2C_M_RD; - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - *val = 0; - /* high byte comes first */ - switch (len) { - case SMIA_REG_32BIT: - *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + - data[3]; - break; - case SMIA_REG_16BIT: - *val = (data[0] << 8) + data[1]; - break; - case SMIA_REG_8BIT: - *val = data[0]; - break; - default: - BUG(); - } - - return 0; - -err: - dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); - - return r; -} - -/* Read a register using 8-bit access only. */ -static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, - u16 len, u32 *val) -{ - unsigned int i; - int rval; - - *val = 0; - - for (i = 0; i < len; i++) { - u32 val8; - - rval = ____smiapp_read(sensor, reg + i, 1, &val8); - if (rval < 0) - return rval; - *val |= val8 << ((len - i - 1) << 3); - } - - return 0; -} - -/* - * Read a 8/16/32-bit i2c register. The value is returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, - bool only8) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int len = (u8)(reg >> 16); - int rval; - - if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT - && len != SMIA_REG_32BIT) - return -EINVAL; - - if (smiapp_quirk_reg(sensor, reg, val)) - goto found_quirk; - - if (len == SMIA_REG_8BIT && !only8) - rval = ____smiapp_read(sensor, (u16)reg, len, val); - else - rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); - if (rval < 0) - return rval; - -found_quirk: - if (reg & SMIA_REG_FLAG_FLOAT) - *val = float_to_u32_mul_1000000(client, *val); - - return 0; -} - -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) -{ - return __smiapp_read( - sensor, reg, val, - smiapp_needs_quirk(sensor, - SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); -} - -int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) -{ - return __smiapp_read(sensor, reg, val, true); -} - -/* - * Write to a 8/16-bit register. - * Returns zero if successful, or non-zero otherwise. - */ -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data[6]; - unsigned int retries; - unsigned int flags = reg >> 24; - unsigned int len = (u8)(reg >> 16); - u16 offset = reg; - int r; - - if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && - len != SMIA_REG_32BIT) || flags) - return -EINVAL; - - msg.addr = client->addr; - msg.flags = 0; /* Write */ - msg.len = 2 + len; - msg.buf = data; - - /* high byte goes out first */ - data[0] = (u8) (reg >> 8); - data[1] = (u8) (reg & 0xff); - - switch (len) { - case SMIA_REG_8BIT: - data[2] = val; - break; - case SMIA_REG_16BIT: - data[2] = val >> 8; - data[3] = val; - break; - case SMIA_REG_32BIT: - data[2] = val >> 24; - data[3] = val >> 16; - data[4] = val >> 8; - data[5] = val; - break; - default: - BUG(); - } - - for (retries = 0; retries < 5; retries++) { - /* - * Due to unknown reason sensor stops responding. This - * loop is a temporaty solution until the root cause - * is found. - */ - r = i2c_transfer(client->adapter, &msg, 1); - if (r == 1) { - if (retries) - dev_err(&client->dev, - "sensor i2c stall encountered. " - "retries: %d\n", retries); - return 0; - } - - usleep_range(2000, 2000); - } - - dev_err(&client->dev, - "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); - - return r; -} diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h deleted file mode 100644 index 7f9013b47971..000000000000 --- a/drivers/media/video/smiapp/smiapp-regs.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * include/media/smiapp/smiapp-regs.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef SMIAPP_REGS_H -#define SMIAPP_REGS_H - -#include -#include - -/* Use upper 8 bits of the type field for flags */ -#define SMIA_REG_FLAG_FLOAT (1 << 24) - -#define SMIA_REG_8BIT 1 -#define SMIA_REG_16BIT 2 -#define SMIA_REG_32BIT 4 -struct smia_reg { - u16 type; - u16 reg; /* 16-bit offset */ - u32 val; /* 8/16/32-bit value */ -}; - -struct smiapp_sensor; - -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); -int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); - -#endif diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h deleted file mode 100644 index 587f7f11238d..000000000000 --- a/drivers/media/video/smiapp/smiapp.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2010--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __SMIAPP_PRIV_H_ -#define __SMIAPP_PRIV_H_ - -#include -#include -#include -#include - -#include "smiapp-pll.h" -#include "smiapp-reg.h" -#include "smiapp-regs.h" -#include "smiapp-quirk.h" - -/* - * Standard SMIA++ constants - */ -#define SMIA_VERSION_1 10 -#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ -#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ -#define SMIAPP_VERSION_1 10 - -#define SMIAPP_PROFILE_0 0 -#define SMIAPP_PROFILE_1 1 -#define SMIAPP_PROFILE_2 2 - -#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ - -#define SMIAPP_RESET_DELAY_CLOCKS 2400 -#define SMIAPP_RESET_DELAY(clk) \ - (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ - + (clk) / 1000 - 1) / ((clk) / 1000)) - -#include "smiapp-limits.h" - -struct smiapp_quirk; - -#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) - -struct smiapp_module_ident { - u8 manufacturer_id; - u16 model_id; - u8 revision_number_major; - - u8 flags; - - char *name; - const struct smiapp_quirk *quirk; -}; - -struct smiapp_module_info { - u32 manufacturer_id; - u32 model_id; - u32 revision_number_major; - u32 revision_number_minor; - - u32 module_year; - u32 module_month; - u32 module_day; - - u32 sensor_manufacturer_id; - u32 sensor_model_id; - u32 sensor_revision_number; - u32 sensor_firmware_version; - - u32 smia_version; - u32 smiapp_version; - - u32 smiapp_profile; - - char *name; - const struct smiapp_quirk *quirk; -}; - -#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = fl, \ - .name = _name, \ - .quirk = _quirk, } - -#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ - .name = _name, \ - .quirk = _quirk, } - -#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ - .name = _name, } - -#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = 0, \ - .name = _name, \ - .quirk = _quirk, } - -#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ - { .manufacturer_id = manufacturer, \ - .model_id = model, \ - .revision_number_major = rev, \ - .flags = 0, \ - .name = _name, } - -struct smiapp_reg_limits { - u32 addr; - char *what; -}; - -extern struct smiapp_reg_limits smiapp_reg_limits[]; - -struct smiapp_csi_data_format { - u32 code; - u8 width; - u8 compressed; - u8 pixel_order; -}; - -#define SMIAPP_SUBDEVS 3 - -#define SMIAPP_PA_PAD_SRC 0 -#define SMIAPP_PAD_SINK 0 -#define SMIAPP_PAD_SRC 1 -#define SMIAPP_PADS 2 - -struct smiapp_binning_subtype { - u8 horizontal:4; - u8 vertical:4; -} __packed; - -struct smiapp_subdev { - struct v4l2_subdev sd; - struct media_pad pads[2]; - struct v4l2_rect sink_fmt; - struct v4l2_rect crop[2]; - struct v4l2_rect compose; /* compose on sink */ - unsigned short sink_pad; - unsigned short source_pad; - int npads; - struct smiapp_sensor *sensor; - struct v4l2_ctrl_handler ctrl_handler; -}; - -/* - * struct smiapp_sensor - Main device structure - */ -struct smiapp_sensor { - /* - * "mutex" is used to serialise access to all fields here - * except v4l2_ctrls at the end of the struct. "mutex" is also - * used to serialise access to file handle specific - * information. The exception to this rule is the power_mutex - * below. - */ - struct mutex mutex; - /* - * power_mutex is used to serialise power management related - * activities. Acquiring "mutex" at that time isn't necessary - * since there are no other users anyway. - */ - struct mutex power_mutex; - struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; - u32 ssds_used; - struct smiapp_subdev *src; - struct smiapp_subdev *binner; - struct smiapp_subdev *scaler; - struct smiapp_subdev *pixel_array; - struct smiapp_platform_data *platform_data; - struct regulator *vana; - struct clk *ext_clk; - u32 limits[SMIAPP_LIMIT_LAST]; - u8 nbinning_subtypes; - struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; - u32 mbus_frame_fmts; - const struct smiapp_csi_data_format *csi_format; - const struct smiapp_csi_data_format *internal_csi_format; - u32 default_mbus_frame_fmts; - int default_pixel_order; - - u8 binning_horizontal; - u8 binning_vertical; - - u8 scale_m; - u8 scaling_mode; - - u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ - u8 flash_capability; - u8 frame_skip; - - int power_count; - - bool streaming; - bool dev_init_done; - - u8 *nvm; /* nvm memory buffer */ - unsigned int nvm_size; /* bytes */ - - struct smiapp_module_info minfo; - - struct smiapp_pll pll; - - /* Pixel array controls */ - struct v4l2_ctrl *analog_gain; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *pixel_rate_parray; - /* src controls */ - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate_csi; -}; - -#define to_smiapp_subdev(_sd) \ - container_of(_sd, struct smiapp_subdev, sd) - -#define to_smiapp_sensor(_sd) \ - (to_smiapp_subdev(_sd)->sensor) - -#endif /* __SMIAPP_PRIV_H_ */ diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c deleted file mode 100644 index e9d95bda2ab1..000000000000 --- a/drivers/media/video/sr030pc30.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP - * - * Copyright (C) 2010 Samsung Electronics Co., Ltd - * Author: Sylwester Nawrocki, s.nawrocki@samsung.com - * - * Based on original driver authored by Dongsoo Nathaniel Kim - * and HeungJun Kim . - * - * Based on mt9v011 Micron Digital Image Sensor driver - * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static int debug; -module_param(debug, int, 0644); - -#define MODULE_NAME "SR030PC30" - -/* - * Register offsets within a page - * b15..b8 - page id, b7..b0 - register address - */ -#define POWER_CTRL_REG 0x0001 -#define PAGEMODE_REG 0x03 -#define DEVICE_ID_REG 0x0004 -#define NOON010PC30_ID 0x86 -#define SR030PC30_ID 0x8C -#define VDO_CTL1_REG 0x0010 -#define SUBSAMPL_NONE_VGA 0 -#define SUBSAMPL_QVGA 0x10 -#define SUBSAMPL_QQVGA 0x20 -#define VDO_CTL2_REG 0x0011 -#define SYNC_CTL_REG 0x0012 -#define WIN_ROWH_REG 0x0020 -#define WIN_ROWL_REG 0x0021 -#define WIN_COLH_REG 0x0022 -#define WIN_COLL_REG 0x0023 -#define WIN_HEIGHTH_REG 0x0024 -#define WIN_HEIGHTL_REG 0x0025 -#define WIN_WIDTHH_REG 0x0026 -#define WIN_WIDTHL_REG 0x0027 -#define HBLANKH_REG 0x0040 -#define HBLANKL_REG 0x0041 -#define VSYNCH_REG 0x0042 -#define VSYNCL_REG 0x0043 -/* page 10 */ -#define ISP_CTL_REG(n) (0x1010 + (n)) -#define YOFS_REG 0x1040 -#define DARK_YOFS_REG 0x1041 -#define AG_ABRTH_REG 0x1050 -#define SAT_CTL_REG 0x1060 -#define BSAT_REG 0x1061 -#define RSAT_REG 0x1062 -#define AG_SAT_TH_REG 0x1063 -/* page 11 */ -#define ZLPF_CTRL_REG 0x1110 -#define ZLPF_CTRL2_REG 0x1112 -#define ZLPF_AGH_THR_REG 0x1121 -#define ZLPF_THR_REG 0x1160 -#define ZLPF_DYN_THR_REG 0x1160 -/* page 12 */ -#define YCLPF_CTL1_REG 0x1240 -#define YCLPF_CTL2_REG 0x1241 -#define YCLPF_THR_REG 0x1250 -#define BLPF_CTL_REG 0x1270 -#define BLPF_THR1_REG 0x1274 -#define BLPF_THR2_REG 0x1275 -/* page 14 - Lens Shading Compensation */ -#define LENS_CTRL_REG 0x1410 -#define LENS_XCEN_REG 0x1420 -#define LENS_YCEN_REG 0x1421 -#define LENS_R_COMP_REG 0x1422 -#define LENS_G_COMP_REG 0x1423 -#define LENS_B_COMP_REG 0x1424 -/* page 15 - Color correction */ -#define CMC_CTL_REG 0x1510 -#define CMC_OFSGH_REG 0x1514 -#define CMC_OFSGL_REG 0x1516 -#define CMC_SIGN_REG 0x1517 -/* Color correction coefficients */ -#define CMC_COEF_REG(n) (0x1530 + (n)) -/* Color correction offset coefficients */ -#define CMC_OFS_REG(n) (0x1540 + (n)) -/* page 16 - Gamma correction */ -#define GMA_CTL_REG 0x1610 -/* Gamma correction coefficients 0.14 */ -#define GMA_COEF_REG(n) (0x1630 + (n)) -/* page 20 - Auto Exposure */ -#define AE_CTL1_REG 0x2010 -#define AE_CTL2_REG 0x2011 -#define AE_FRM_CTL_REG 0x2020 -#define AE_FINE_CTL_REG(n) (0x2028 + (n)) -#define EXP_TIMEH_REG 0x2083 -#define EXP_TIMEM_REG 0x2084 -#define EXP_TIMEL_REG 0x2085 -#define EXP_MMINH_REG 0x2086 -#define EXP_MMINL_REG 0x2087 -#define EXP_MMAXH_REG 0x2088 -#define EXP_MMAXM_REG 0x2089 -#define EXP_MMAXL_REG 0x208A -/* page 22 - Auto White Balance */ -#define AWB_CTL1_REG 0x2210 -#define AWB_ENABLE 0x80 -#define AWB_CTL2_REG 0x2211 -#define MWB_ENABLE 0x01 -/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */ -#define AWB_RGAIN_REG 0x2280 -#define AWB_GGAIN_REG 0x2281 -#define AWB_BGAIN_REG 0x2282 -#define AWB_RMAX_REG 0x2283 -#define AWB_RMIN_REG 0x2284 -#define AWB_BMAX_REG 0x2285 -#define AWB_BMIN_REG 0x2286 -/* R, B gain range in bright light conditions */ -#define AWB_RMAXB_REG 0x2287 -#define AWB_RMINB_REG 0x2288 -#define AWB_BMAXB_REG 0x2289 -#define AWB_BMINB_REG 0x228A -/* manual white balance, when AWB_CTL2[0]=1 */ -#define MWB_RGAIN_REG 0x22B2 -#define MWB_BGAIN_REG 0x22B3 -/* the token to mark an array end */ -#define REG_TERM 0xFFFF - -/* Minimum and maximum exposure time in ms */ -#define EXPOS_MIN_MS 1 -#define EXPOS_MAX_MS 125 - -struct sr030pc30_info { - struct v4l2_subdev sd; - const struct sr030pc30_platform_data *pdata; - const struct sr030pc30_format *curr_fmt; - const struct sr030pc30_frmsize *curr_win; - unsigned int auto_wb:1; - unsigned int auto_exp:1; - unsigned int hflip:1; - unsigned int vflip:1; - unsigned int sleep:1; - unsigned int exposure; - u8 blue_balance; - u8 red_balance; - u8 i2c_reg_page; -}; - -struct sr030pc30_format { - enum v4l2_mbus_pixelcode code; - enum v4l2_colorspace colorspace; - u16 ispctl1_reg; -}; - -struct sr030pc30_frmsize { - u16 width; - u16 height; - int vid_ctl1; -}; - -struct i2c_regval { - u16 addr; - u16 val; -}; - -static const struct v4l2_queryctrl sr030pc30_ctrl[] = { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .flags = 0, - }, { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - }, { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Auto Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = EXPOS_MIN_MS, - .maximum = EXPOS_MAX_MS, - .step = 1, - .default_value = 1, - }, { - } -}; - -/* supported resolutions */ -static const struct sr030pc30_frmsize sr030pc30_sizes[] = { - { - .width = 640, - .height = 480, - .vid_ctl1 = SUBSAMPL_NONE_VGA, - }, { - .width = 320, - .height = 240, - .vid_ctl1 = SUBSAMPL_QVGA, - }, { - .width = 160, - .height = 120, - .vid_ctl1 = SUBSAMPL_QQVGA, - }, -}; - -/* supported pixel formats */ -static const struct sr030pc30_format sr030pc30_formats[] = { - { - .code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x03, - }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x02, - }, { - .code = V4L2_MBUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0, - }, { - .code = V4L2_MBUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x01, - }, { - .code = V4L2_MBUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x40, - }, -}; - -static const struct i2c_regval sr030pc30_base_regs[] = { - /* Window size and position within pixel matrix */ - { WIN_ROWH_REG, 0x00 }, { WIN_ROWL_REG, 0x06 }, - { WIN_COLH_REG, 0x00 }, { WIN_COLL_REG, 0x06 }, - { WIN_HEIGHTH_REG, 0x01 }, { WIN_HEIGHTL_REG, 0xE0 }, - { WIN_WIDTHH_REG, 0x02 }, { WIN_WIDTHL_REG, 0x80 }, - { HBLANKH_REG, 0x01 }, { HBLANKL_REG, 0x50 }, - { VSYNCH_REG, 0x00 }, { VSYNCL_REG, 0x14 }, - { SYNC_CTL_REG, 0 }, - /* Color corection and saturation */ - { ISP_CTL_REG(0), 0x30 }, { YOFS_REG, 0x80 }, - { DARK_YOFS_REG, 0x04 }, { AG_ABRTH_REG, 0x78 }, - { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, - { AG_SAT_TH_REG, 0xF0 }, { 0x1064, 0x80 }, - { CMC_CTL_REG, 0x03 }, { CMC_OFSGH_REG, 0x3C }, - { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x2F }, - { CMC_COEF_REG(0), 0xCB }, { CMC_OFS_REG(0), 0x87 }, - { CMC_COEF_REG(1), 0x61 }, { CMC_OFS_REG(1), 0x18 }, - { CMC_COEF_REG(2), 0x16 }, { CMC_OFS_REG(2), 0x91 }, - { CMC_COEF_REG(3), 0x23 }, { CMC_OFS_REG(3), 0x94 }, - { CMC_COEF_REG(4), 0xCE }, { CMC_OFS_REG(4), 0x9f }, - { CMC_COEF_REG(5), 0x2B }, { CMC_OFS_REG(5), 0x33 }, - { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x00 }, - { CMC_COEF_REG(7), 0x34 }, { CMC_OFS_REG(7), 0x94 }, - { CMC_COEF_REG(8), 0x75 }, { CMC_OFS_REG(8), 0x14 }, - /* Color corection coefficients */ - { GMA_CTL_REG, 0x03 }, { GMA_COEF_REG(0), 0x00 }, - { GMA_COEF_REG(1), 0x19 }, { GMA_COEF_REG(2), 0x26 }, - { GMA_COEF_REG(3), 0x3B }, { GMA_COEF_REG(4), 0x5D }, - { GMA_COEF_REG(5), 0x79 }, { GMA_COEF_REG(6), 0x8E }, - { GMA_COEF_REG(7), 0x9F }, { GMA_COEF_REG(8), 0xAF }, - { GMA_COEF_REG(9), 0xBD }, { GMA_COEF_REG(10), 0xCA }, - { GMA_COEF_REG(11), 0xDD }, { GMA_COEF_REG(12), 0xEC }, - { GMA_COEF_REG(13), 0xF7 }, { GMA_COEF_REG(14), 0xFF }, - /* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */ - { ZLPF_CTRL_REG, 0x99 }, { ZLPF_CTRL2_REG, 0x0E }, - { ZLPF_AGH_THR_REG, 0x29 }, { ZLPF_THR_REG, 0x0F }, - { ZLPF_DYN_THR_REG, 0x63 }, { YCLPF_CTL1_REG, 0x23 }, - { YCLPF_CTL2_REG, 0x3B }, { YCLPF_THR_REG, 0x05 }, - { BLPF_CTL_REG, 0x1D }, { BLPF_THR1_REG, 0x05 }, - { BLPF_THR2_REG, 0x04 }, - /* Automatic white balance */ - { AWB_CTL1_REG, 0xFB }, { AWB_CTL2_REG, 0x26 }, - { AWB_RMAX_REG, 0x54 }, { AWB_RMIN_REG, 0x2B }, - { AWB_BMAX_REG, 0x57 }, { AWB_BMIN_REG, 0x29 }, - { AWB_RMAXB_REG, 0x50 }, { AWB_RMINB_REG, 0x43 }, - { AWB_BMAXB_REG, 0x30 }, { AWB_BMINB_REG, 0x22 }, - /* Auto exposure */ - { AE_CTL1_REG, 0x8C }, { AE_CTL2_REG, 0x04 }, - { AE_FRM_CTL_REG, 0x01 }, { AE_FINE_CTL_REG(0), 0x3F }, - { AE_FINE_CTL_REG(1), 0xA3 }, { AE_FINE_CTL_REG(3), 0x34 }, - /* Lens shading compensation */ - { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, - { LENS_YCEN_REG, 0x70 }, { LENS_R_COMP_REG, 0x53 }, - { LENS_G_COMP_REG, 0x40 }, { LENS_B_COMP_REG, 0x3e }, - { REG_TERM, 0 }, -}; - -static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd) -{ - return container_of(sd, struct sr030pc30_info, sd); -} - -static inline int set_i2c_page(struct sr030pc30_info *info, - struct i2c_client *client, unsigned int reg) -{ - int ret = 0; - u32 page = reg >> 8 & 0xFF; - - if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { - ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); - if (!ret) - info->i2c_reg_page = page; - } - return ret; -} - -static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = set_i2c_page(info, client, reg_addr); - if (!ret) - ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF); - return ret; -} - -static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = set_i2c_page(info, client, reg_addr); - if (!ret) - ret = i2c_smbus_write_byte_data( - client, reg_addr & 0xFF, val); - return ret; -} - -static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd, - const struct i2c_regval *msg) -{ - while (msg->addr != REG_TERM) { - int ret = cam_i2c_write(sd, msg->addr, msg->val); - if (ret) - return ret; - msg++; - } - return 0; -} - -/* Device reset and sleep mode control */ -static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd, - bool reset, bool sleep) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - u8 reg = sleep ? 0xF1 : 0xF0; - int ret = 0; - - if (reset) - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); - if (!ret) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); - if (!ret) { - info->sleep = sleep; - if (reset) - info->i2c_reg_page = -1; - } - } - return ret; -} - -static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - /* auto anti-flicker is also enabled here */ - int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C); - if (!ret) - info->auto_exp = on; - return ret; -} - -static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - unsigned long expos = value * info->pdata->clk_rate / (8 * 1000); - - int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF); - if (!ret) { /* Turn off AE */ - info->exposure = value; - ret = sr030pc30_enable_autoexposure(sd, 0); - } - return ret; -} - -/* Automatic white balance control */ -static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F); - if (!ret) - ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B); - if (!ret) - info->auto_wb = on; - - return ret; -} - -static int sr030pc30_set_flip(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - s32 reg = cam_i2c_read(sd, VDO_CTL2_REG); - if (reg < 0) - return reg; - - reg &= 0x7C; - if (info->hflip) - reg |= 0x01; - if (info->vflip) - reg |= 0x02; - return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80); -} - -/* Configure resolution, color format and image flip */ -static int sr030pc30_set_params(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - - if (!info->curr_win) - return -EINVAL; - - /* Configure the resolution through subsampling */ - ret = cam_i2c_write(sd, VDO_CTL1_REG, - info->curr_win->vid_ctl1); - - if (!ret && info->curr_fmt) - ret = cam_i2c_write(sd, ISP_CTL_REG(0), - info->curr_fmt->ispctl1_reg); - if (!ret) - ret = sr030pc30_set_flip(sd); - - return ret; -} - -/* Find nearest matching image pixel size. */ -static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf) -{ - unsigned int min_err = ~0; - int i = ARRAY_SIZE(sr030pc30_sizes); - const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0], - *match = NULL; - while (i--) { - int err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - if (err < min_err) { - min_err = err; - match = fsize; - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - return 0; - } - return -EINVAL; -} - -static int sr030pc30_queryctrl(struct v4l2_subdev *sd, - struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) - if (qc->id == sr030pc30_ctrl[i].id) { - *qc = sr030pc30_ctrl[i]; - v4l2_dbg(1, debug, sd, "%s id: %d\n", - __func__, qc->id); - return 0; - } - - return -EINVAL; -} - -static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value) -{ - int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value); - if (!ret) - to_sr030pc30(sd)->blue_balance = value; - return ret; -} - -static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value) -{ - int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value); - if (!ret) - to_sr030pc30(sd)->red_balance = value; - return ret; -} - -static int sr030pc30_s_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - int i, ret = 0; - - for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) - if (ctrl->id == sr030pc30_ctrl[i].id) - break; - - if (i == ARRAY_SIZE(sr030pc30_ctrl)) - return -EINVAL; - - if (ctrl->value < sr030pc30_ctrl[i].minimum || - ctrl->value > sr030pc30_ctrl[i].maximum) - return -ERANGE; - - v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", - __func__, ctrl->id, ctrl->value); - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - sr030pc30_enable_autowhitebalance(sd, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - ret = sr030pc30_set_bluebalance(sd, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - ret = sr030pc30_set_redbalance(sd, ctrl->value); - break; - case V4L2_CID_EXPOSURE_AUTO: - sr030pc30_enable_autoexposure(sd, - ctrl->value == V4L2_EXPOSURE_AUTO); - break; - case V4L2_CID_EXPOSURE: - ret = sr030pc30_set_exposure(sd, ctrl->value); - break; - default: - return -EINVAL; - } - - return ret; -} - -static int sr030pc30_g_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id); - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->value = info->auto_wb; - break; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = info->blue_balance; - break; - case V4L2_CID_RED_BALANCE: - ctrl->value = info->red_balance; - break; - case V4L2_CID_EXPOSURE_AUTO: - ctrl->value = info->auto_exp; - break; - case V4L2_CID_EXPOSURE: - ctrl->value = info->exposure; - break; - default: - return -EINVAL; - } - return 0; -} - -static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) -{ - if (!code || index >= ARRAY_SIZE(sr030pc30_formats)) - return -EINVAL; - - *code = sr030pc30_formats[index].code; - return 0; -} - -static int sr030pc30_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - - if (!mf) - return -EINVAL; - - if (!info->curr_win || !info->curr_fmt) { - ret = sr030pc30_set_params(sd); - if (ret) - return ret; - } - - mf->width = info->curr_win->width; - mf->height = info->curr_win->height; - mf->code = info->curr_fmt->code; - mf->colorspace = info->curr_fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* Return nearest media bus frame format. */ -static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - int i = ARRAY_SIZE(sr030pc30_formats); - - sr030pc30_try_frame_size(mf); - - while (i--) - if (mf->code == sr030pc30_formats[i].code) - break; - - mf->code = sr030pc30_formats[i].code; - - return &sr030pc30_formats[i]; -} - -/* Return nearest media bus frame format. */ -static int sr030pc30_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - if (!sd || !mf) - return -EINVAL; - - try_fmt(sd, mf); - return 0; -} - -static int sr030pc30_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - if (!sd || !mf) - return -EINVAL; - - info->curr_fmt = try_fmt(sd, mf); - - return sr030pc30_set_params(sd); -} - -static int sr030pc30_base_config(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - unsigned long expmin, expmax; - - ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs); - if (!ret) { - info->curr_fmt = &sr030pc30_formats[0]; - info->curr_win = &sr030pc30_sizes[0]; - ret = sr030pc30_set_params(sd); - } - if (!ret) - ret = sr030pc30_pwr_ctrl(sd, false, false); - - if (!ret && !info->pdata) - return ret; - - expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000); - expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000); - - v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__, - expmin, expmax); - - /* Setting up manual exposure time range */ - ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF); - - return ret; -} - -static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - const struct sr030pc30_platform_data *pdata = info->pdata; - int ret; - - if (pdata == NULL) { - WARN(1, "No platform data!\n"); - return -EINVAL; - } - - /* - * Put sensor into power sleep mode before switching off - * power and disabling MCLK. - */ - if (!on) - sr030pc30_pwr_ctrl(sd, false, true); - - /* set_power controls sensor's power and clock */ - if (pdata->set_power) { - ret = pdata->set_power(&client->dev, on); - if (ret) - return ret; - } - - if (on) { - ret = sr030pc30_base_config(sd); - } else { - ret = 0; - info->curr_win = NULL; - info->curr_fmt = NULL; - } - - return ret; -} - -static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { - .s_power = sr030pc30_s_power, - .queryctrl = sr030pc30_queryctrl, - .s_ctrl = sr030pc30_s_ctrl, - .g_ctrl = sr030pc30_g_ctrl, -}; - -static const struct v4l2_subdev_video_ops sr030pc30_video_ops = { - .g_mbus_fmt = sr030pc30_g_fmt, - .s_mbus_fmt = sr030pc30_s_fmt, - .try_mbus_fmt = sr030pc30_try_fmt, - .enum_mbus_fmt = sr030pc30_enum_fmt, -}; - -static const struct v4l2_subdev_ops sr030pc30_ops = { - .core = &sr030pc30_core_ops, - .video = &sr030pc30_video_ops, -}; - -/* - * Detect sensor type. Return 0 if SR030PC30 was detected - * or -ENODEV otherwise. - */ -static int sr030pc30_detect(struct i2c_client *client) -{ - const struct sr030pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - - /* Enable sensor's power and clock */ - if (pdata->set_power) { - ret = pdata->set_power(&client->dev, 1); - if (ret) - return ret; - } - - ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); - - if (pdata->set_power) - pdata->set_power(&client->dev, 0); - - if (ret < 0) { - dev_err(&client->dev, "%s: I2C read failed\n", __func__); - return ret; - } - - return ret == SR030PC30_ID ? 0 : -ENODEV; -} - - -static int sr030pc30_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct sr030pc30_info *info; - struct v4l2_subdev *sd; - const struct sr030pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - - if (!pdata) { - dev_err(&client->dev, "No platform data!"); - return -EIO; - } - - ret = sr030pc30_detect(client); - if (ret) - return ret; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - sd = &info->sd; - strcpy(sd->name, MODULE_NAME); - info->pdata = client->dev.platform_data; - - v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops); - - info->i2c_reg_page = -1; - info->hflip = 1; - info->auto_exp = 1; - info->exposure = 30; - - return 0; -} - -static int sr030pc30_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct sr030pc30_info *info = to_sr030pc30(sd); - - v4l2_device_unregister_subdev(sd); - kfree(info); - return 0; -} - -static const struct i2c_device_id sr030pc30_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, sr030pc30_id); - - -static struct i2c_driver sr030pc30_i2c_driver = { - .driver = { - .name = MODULE_NAME - }, - .probe = sr030pc30_probe, - .remove = sr030pc30_remove, - .id_table = sr030pc30_id, -}; - -module_i2c_driver(sr030pc30_i2c_driver); - -MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c deleted file mode 100644 index 462caa44ae00..000000000000 --- a/drivers/media/video/tcm825x.c +++ /dev/null @@ -1,937 +0,0 @@ -/* - * drivers/media/video/tcm825x.c - * - * TCM825X camera sensor driver. - * - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus - * - * Based on code from David Cohen - * - * This driver was based on ov9640 sensor driver from MontaVista - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include - -#include "tcm825x.h" - -/* - * The sensor has two fps modes: the lower one just gives half the fps - * at the same xclk than the high one. - */ -#define MAX_FPS 30 -#define MIN_FPS 8 -#define MAX_HALF_FPS (MAX_FPS / 2) -#define HIGH_FPS_MODE_LOWER_LIMIT 14 -#define DEFAULT_FPS MAX_HALF_FPS - -struct tcm825x_sensor { - const struct tcm825x_platform_data *platform_data; - struct v4l2_int_device *v4l2_int_device; - struct i2c_client *i2c_client; - struct v4l2_pix_format pix; - struct v4l2_fract timeperframe; -}; - -/* list of image formats supported by TCM825X sensor */ -static const struct v4l2_fmtdesc tcm825x_formats[] = { - { - .description = "YUYV (YUV 4:2:2), packed", - .pixelformat = V4L2_PIX_FMT_UYVY, - }, { - /* Note: V4L2 defines RGB565 as: - * - * Byte 0 Byte 1 - * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 - * - * We interpret RGB565 as: - * - * Byte 0 Byte 1 - * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 - */ - .description = "RGB565, le", - .pixelformat = V4L2_PIX_FMT_RGB565, - }, -}; - -#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats) - -/* - * TCM825X register configuration for all combinations of pixel format and - * image size - */ -static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; -static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; -static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; -static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; -static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; -static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; - -static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; -static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; - -/* Our own specific controls */ -#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE -#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1 -#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2 -#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3 -#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4 -#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME - -/* Video controls */ -static struct vcontrol { - struct v4l2_queryctrl qc; - u16 reg; - u16 start_bit; -} video_control[] = { - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 63, - .step = 1, - }, - .reg = TCM825X_AG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 255, - .step = 1, - }, - .reg = TCM825X_MRG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 255, - .step = 1, - }, - .reg = TCM825X_MBG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_AWBSW, - .start_bit = 7, - }, - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure Time", - .minimum = 0, - .maximum = 0x1fff, - .step = 1, - }, - .reg = TCM825X_ESRSPD_U, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror Image", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_H_INV, - .start_bit = 6, - }, - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_V_INV, - .start_bit = 7, - }, - /* Private controls */ - { - { - .id = V4L2_CID_ALC, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Luminance Control", - .minimum = 0, - .maximum = 1, - .step = 0, - }, - .reg = TCM825X_ALCSW, - .start_bit = 7, - }, - { - { - .id = V4L2_CID_H_EDGE_EN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Horizontal Edge Enhancement", - .minimum = 0, - .maximum = 0xff, - .step = 1, - }, - .reg = TCM825X_HDTG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_V_EDGE_EN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Vertical Edge Enhancement", - .minimum = 0, - .maximum = 0xff, - .step = 1, - }, - .reg = TCM825X_VDTG, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_LENS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Lens Shading Compensation", - .minimum = 0, - .maximum = 0x3f, - .step = 1, - }, - .reg = TCM825X_LENS, - .start_bit = 0, - }, - { - { - .id = V4L2_CID_MAX_EXPOSURE_TIME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Maximum Exposure Time", - .minimum = 0, - .maximum = 0x3, - .step = 1, - }, - .reg = TCM825X_ESRLIM, - .start_bit = 5, - }, -}; - - -static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = -{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; - -static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = -{ &yuv422, &rgb565 }; - -/* - * Read a value from a register in an TCM825X sensor device. The value is - * returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_read_reg(struct i2c_client *client, int reg) -{ - int err; - struct i2c_msg msg[2]; - u8 reg_buf, data_buf = 0; - - if (!client->adapter) - return -ENODEV; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = ®_buf; - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 1; - msg[1].buf = &data_buf; - - reg_buf = reg; - - err = i2c_transfer(client->adapter, msg, 2); - if (err < 0) - return err; - return data_buf; -} - -/* - * Write a value to a register in an TCM825X sensor device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) -{ - int err; - struct i2c_msg msg[1]; - unsigned char data[2]; - - if (!client->adapter) - return -ENODEV; - - msg->addr = client->addr; - msg->flags = 0; - msg->len = 2; - msg->buf = data; - data[0] = reg; - data[1] = val; - err = i2c_transfer(client->adapter, msg, 1); - if (err >= 0) - return 0; - return err; -} - -static int __tcm825x_write_reg_mask(struct i2c_client *client, - u8 reg, u8 val, u8 mask) -{ - int rc; - - /* need to do read - modify - write */ - rc = tcm825x_read_reg(client, reg); - if (rc < 0) - return rc; - - rc &= (~mask); /* Clear the masked bits */ - val &= mask; /* Enforce mask on value */ - val |= rc; - - /* write the new value to the register */ - rc = tcm825x_write_reg(client, reg, val); - if (rc) - return rc; - - return 0; -} - -#define tcm825x_write_reg_mask(client, regmask, val) \ - __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \ - TCM825X_MASK((regmask))) - - -/* - * Initialize a list of TCM825X registers. - * The list of registers is terminated by the pair of values - * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_default_regs(struct i2c_client *client, - const struct tcm825x_reg *reglist) -{ - int err; - const struct tcm825x_reg *next = reglist; - - while (!((next->reg == TCM825X_REG_TERM) - && (next->val == TCM825X_VAL_TERM))) { - err = tcm825x_write_reg(client, next->reg, next->val); - if (err) { - dev_err(&client->dev, "register writing failed\n"); - return err; - } - next++; - } - - return 0; -} - -static struct vcontrol *find_vctrl(int id) -{ - int i; - - if (id < V4L2_CID_BASE) - return NULL; - - for (i = 0; i < ARRAY_SIZE(video_control); i++) - if (video_control[i].qc.id == id) - return &video_control[i]; - - return NULL; -} - -/* - * Find the best match for a requested image capture size. The best match - * is chosen as the nearest match that has the same number or fewer pixels - * as the requested size, or the smallest image size if the requested size - * has fewer pixels than the smallest image. - */ -static enum image_size tcm825x_find_size(struct v4l2_int_device *s, - unsigned int width, - unsigned int height) -{ - enum image_size isize; - unsigned long pixels = width * height; - struct tcm825x_sensor *sensor = s->priv; - - for (isize = subQCIF; isize < VGA; isize++) { - if (tcm825x_sizes[isize + 1].height - * tcm825x_sizes[isize + 1].width > pixels) { - dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); - - return isize; - } - } - - dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); - - return VGA; -} - -/* - * Configure the TCM825X for current image size, pixel format, and - * frame period. fper is the frame period (in seconds) expressed as a - * fraction. Returns zero if successful, or non-zero otherwise. The - * actual frame period is returned in fper. - */ -static int tcm825x_configure(struct v4l2_int_device *s) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_pix_format *pix = &sensor->pix; - enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); - struct v4l2_fract *fper = &sensor->timeperframe; - enum pixel_format pfmt; - int err; - u32 tgt_fps; - u8 val; - - /* common register initialization */ - err = tcm825x_write_default_regs( - sensor->i2c_client, sensor->platform_data->default_regs()); - if (err) - return err; - - /* configure image size */ - val = tcm825x_siz_reg[isize]->val; - dev_dbg(&sensor->i2c_client->dev, - "configuring image size %d\n", isize); - err = tcm825x_write_reg_mask(sensor->i2c_client, - tcm825x_siz_reg[isize]->reg, val); - if (err) - return err; - - /* configure pixel format */ - switch (pix->pixelformat) { - default: - case V4L2_PIX_FMT_RGB565: - pfmt = RGB565; - break; - case V4L2_PIX_FMT_UYVY: - pfmt = YUV422; - break; - } - - dev_dbg(&sensor->i2c_client->dev, - "configuring pixel format %d\n", pfmt); - val = tcm825x_fmt_reg[pfmt]->val; - - err = tcm825x_write_reg_mask(sensor->i2c_client, - tcm825x_fmt_reg[pfmt]->reg, val); - if (err) - return err; - - /* - * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be - * set. Frame rate will be halved from the normal. - */ - tgt_fps = fper->denominator / fper->numerator; - if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { - val = tcm825x_read_reg(sensor->i2c_client, 0x02); - val |= 0x80; - tcm825x_write_reg(sensor->i2c_client, 0x02, val); - } - - return 0; -} - -static int ioctl_queryctrl(struct v4l2_int_device *s, - struct v4l2_queryctrl *qc) -{ - struct vcontrol *control; - - control = find_vctrl(qc->id); - - if (control == NULL) - return -EINVAL; - - *qc = control->qc; - - return 0; -} - -static int ioctl_g_ctrl(struct v4l2_int_device *s, - struct v4l2_control *vc) -{ - struct tcm825x_sensor *sensor = s->priv; - struct i2c_client *client = sensor->i2c_client; - int val, r; - struct vcontrol *lvc; - - /* exposure time is special, spread across 2 registers */ - if (vc->id == V4L2_CID_EXPOSURE) { - int val_lower, val_upper; - - val_upper = tcm825x_read_reg(client, - TCM825X_ADDR(TCM825X_ESRSPD_U)); - if (val_upper < 0) - return val_upper; - val_lower = tcm825x_read_reg(client, - TCM825X_ADDR(TCM825X_ESRSPD_L)); - if (val_lower < 0) - return val_lower; - - vc->value = ((val_upper & 0x1f) << 8) | (val_lower); - return 0; - } - - lvc = find_vctrl(vc->id); - if (lvc == NULL) - return -EINVAL; - - r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); - if (r < 0) - return r; - val = r & TCM825X_MASK(lvc->reg); - val >>= lvc->start_bit; - - if (val < 0) - return val; - - if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) - val ^= sensor->platform_data->is_upside_down(); - - vc->value = val; - return 0; -} - -static int ioctl_s_ctrl(struct v4l2_int_device *s, - struct v4l2_control *vc) -{ - struct tcm825x_sensor *sensor = s->priv; - struct i2c_client *client = sensor->i2c_client; - struct vcontrol *lvc; - int val = vc->value; - - /* exposure time is special, spread across 2 registers */ - if (vc->id == V4L2_CID_EXPOSURE) { - int val_lower, val_upper; - val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); - val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); - - if (tcm825x_write_reg_mask(client, - TCM825X_ESRSPD_U, val_upper)) - return -EIO; - - if (tcm825x_write_reg_mask(client, - TCM825X_ESRSPD_L, val_lower)) - return -EIO; - - return 0; - } - - lvc = find_vctrl(vc->id); - if (lvc == NULL) - return -EINVAL; - - if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) - val ^= sensor->platform_data->is_upside_down(); - - val = val << lvc->start_bit; - if (tcm825x_write_reg_mask(client, lvc->reg, val)) - return -EIO; - - return 0; -} - -static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, - struct v4l2_fmtdesc *fmt) -{ - int index = fmt->index; - - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (index >= TCM825X_NUM_CAPTURE_FORMATS) - return -EINVAL; - break; - - default: - return -EINVAL; - } - - fmt->flags = tcm825x_formats[index].flags; - strlcpy(fmt->description, tcm825x_formats[index].description, - sizeof(fmt->description)); - fmt->pixelformat = tcm825x_formats[index].pixelformat; - - return 0; -} - -static int ioctl_try_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - enum image_size isize; - int ifmt; - struct v4l2_pix_format *pix = &f->fmt.pix; - - isize = tcm825x_find_size(s, pix->width, pix->height); - dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", - isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); - - pix->width = tcm825x_sizes[isize].width; - pix->height = tcm825x_sizes[isize].height; - - for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) - if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) - break; - - if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) - ifmt = 0; /* Default = YUV 4:2:2 */ - - pix->pixelformat = tcm825x_formats[ifmt].pixelformat; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; - pix->sizeimage = pix->bytesperline * pix->height; - pix->priv = 0; - dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", - pix->pixelformat); - - switch (pix->pixelformat) { - case V4L2_PIX_FMT_UYVY: - default: - pix->colorspace = V4L2_COLORSPACE_JPEG; - break; - case V4L2_PIX_FMT_RGB565: - pix->colorspace = V4L2_COLORSPACE_SRGB; - break; - } - - return 0; -} - -static int ioctl_s_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - int rval; - - rval = ioctl_try_fmt_cap(s, f); - if (rval) - return rval; - - rval = tcm825x_configure(s); - - sensor->pix = *pix; - - return rval; -} - -static int ioctl_g_fmt_cap(struct v4l2_int_device *s, - struct v4l2_format *f) -{ - struct tcm825x_sensor *sensor = s->priv; - - f->fmt.pix = sensor->pix; - - return 0; -} - -static int ioctl_g_parm(struct v4l2_int_device *s, - struct v4l2_streamparm *a) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_captureparm *cparm = &a->parm.capture; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(a, 0, sizeof(*a)); - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - cparm->capability = V4L2_CAP_TIMEPERFRAME; - cparm->timeperframe = sensor->timeperframe; - - return 0; -} - -static int ioctl_s_parm(struct v4l2_int_device *s, - struct v4l2_streamparm *a) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; - u32 tgt_fps; /* target frames per secound */ - int rval; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if ((timeperframe->numerator == 0) - || (timeperframe->denominator == 0)) { - timeperframe->denominator = DEFAULT_FPS; - timeperframe->numerator = 1; - } - - tgt_fps = timeperframe->denominator / timeperframe->numerator; - - if (tgt_fps > MAX_FPS) { - timeperframe->denominator = MAX_FPS; - timeperframe->numerator = 1; - } else if (tgt_fps < MIN_FPS) { - timeperframe->denominator = MIN_FPS; - timeperframe->numerator = 1; - } - - sensor->timeperframe = *timeperframe; - - rval = tcm825x_configure(s); - - return rval; -} - -static int ioctl_s_power(struct v4l2_int_device *s, int on) -{ - struct tcm825x_sensor *sensor = s->priv; - - return sensor->platform_data->power_set(on); -} - -/* - * Given the image capture format in pix, the nominal frame period in - * timeperframe, calculate the required xclk frequency. - * - * TCM825X input frequency characteristics are: - * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz - */ - -static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) -{ - struct tcm825x_sensor *sensor = s->priv; - struct v4l2_fract *timeperframe = &sensor->timeperframe; - u32 tgt_xclk; /* target xclk */ - u32 tgt_fps; /* target frames per secound */ - int rval; - - rval = sensor->platform_data->ifparm(p); - if (rval) - return rval; - - tgt_fps = timeperframe->denominator / timeperframe->numerator; - - tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? - (2457 * tgt_fps) / MAX_HALF_FPS : - (2457 * tgt_fps) / MAX_FPS; - tgt_xclk *= 10000; - - tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); - tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); - - p->u.bt656.clock_curr = tgt_xclk; - - return 0; -} - -static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) -{ - struct tcm825x_sensor *sensor = s->priv; - - return sensor->platform_data->needs_reset(s, buf, &sensor->pix); -} - -static int ioctl_reset(struct v4l2_int_device *s) -{ - return -EBUSY; -} - -static int ioctl_init(struct v4l2_int_device *s) -{ - return tcm825x_configure(s); -} - -static int ioctl_dev_exit(struct v4l2_int_device *s) -{ - return 0; -} - -static int ioctl_dev_init(struct v4l2_int_device *s) -{ - struct tcm825x_sensor *sensor = s->priv; - int r; - - r = tcm825x_read_reg(sensor->i2c_client, 0x01); - if (r < 0) - return r; - if (r == 0) { - dev_err(&sensor->i2c_client->dev, "device not detected\n"); - return -EIO; - } - return 0; -} - -static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { - { vidioc_int_dev_init_num, - (v4l2_int_ioctl_func *)ioctl_dev_init }, - { vidioc_int_dev_exit_num, - (v4l2_int_ioctl_func *)ioctl_dev_exit }, - { vidioc_int_s_power_num, - (v4l2_int_ioctl_func *)ioctl_s_power }, - { vidioc_int_g_ifparm_num, - (v4l2_int_ioctl_func *)ioctl_g_ifparm }, - { vidioc_int_g_needs_reset_num, - (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, - { vidioc_int_reset_num, - (v4l2_int_ioctl_func *)ioctl_reset }, - { vidioc_int_init_num, - (v4l2_int_ioctl_func *)ioctl_init }, - { vidioc_int_enum_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, - { vidioc_int_try_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, - { vidioc_int_g_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, - { vidioc_int_s_fmt_cap_num, - (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, - { vidioc_int_g_parm_num, - (v4l2_int_ioctl_func *)ioctl_g_parm }, - { vidioc_int_s_parm_num, - (v4l2_int_ioctl_func *)ioctl_s_parm }, - { vidioc_int_queryctrl_num, - (v4l2_int_ioctl_func *)ioctl_queryctrl }, - { vidioc_int_g_ctrl_num, - (v4l2_int_ioctl_func *)ioctl_g_ctrl }, - { vidioc_int_s_ctrl_num, - (v4l2_int_ioctl_func *)ioctl_s_ctrl }, -}; - -static struct v4l2_int_slave tcm825x_slave = { - .ioctls = tcm825x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), -}; - -static struct tcm825x_sensor tcm825x; - -static struct v4l2_int_device tcm825x_int_device = { - .module = THIS_MODULE, - .name = TCM825X_NAME, - .priv = &tcm825x, - .type = v4l2_int_type_slave, - .u = { - .slave = &tcm825x_slave, - }, -}; - -static int tcm825x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct tcm825x_sensor *sensor = &tcm825x; - - if (i2c_get_clientdata(client)) - return -EBUSY; - - sensor->platform_data = client->dev.platform_data; - - if (sensor->platform_data == NULL - || !sensor->platform_data->is_okay()) - return -ENODEV; - - sensor->v4l2_int_device = &tcm825x_int_device; - - sensor->i2c_client = client; - i2c_set_clientdata(client, sensor); - - /* Make the default capture format QVGA RGB565 */ - sensor->pix.width = tcm825x_sizes[QVGA].width; - sensor->pix.height = tcm825x_sizes[QVGA].height; - sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; - - return v4l2_int_device_register(sensor->v4l2_int_device); -} - -static int tcm825x_remove(struct i2c_client *client) -{ - struct tcm825x_sensor *sensor = i2c_get_clientdata(client); - - if (!client->adapter) - return -ENODEV; /* our client isn't attached */ - - v4l2_int_device_unregister(sensor->v4l2_int_device); - - return 0; -} - -static const struct i2c_device_id tcm825x_id[] = { - { "tcm825x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tcm825x_id); - -static struct i2c_driver tcm825x_i2c_driver = { - .driver = { - .name = TCM825X_NAME, - }, - .probe = tcm825x_probe, - .remove = tcm825x_remove, - .id_table = tcm825x_id, -}; - -static struct tcm825x_sensor tcm825x = { - .timeperframe = { - .numerator = 1, - .denominator = DEFAULT_FPS, - }, -}; - -static int __init tcm825x_init(void) -{ - int rval; - - rval = i2c_add_driver(&tcm825x_i2c_driver); - if (rval) - printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", - __func__); - - return rval; -} - -static void __exit tcm825x_exit(void) -{ - i2c_del_driver(&tcm825x_i2c_driver); -} - -/* - * FIXME: Menelaus isn't ready (?) at module_init stage, so use - * late_initcall for now. - */ -late_initcall(tcm825x_init); -module_exit(tcm825x_exit); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("TCM825x camera sensor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h deleted file mode 100644 index 5b7e69682368..000000000000 --- a/drivers/media/video/tcm825x.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * drivers/media/video/tcm825x.h - * - * Register definitions for the TCM825X CameraChip. - * - * Author: David Cohen (david.cohen@indt.org.br) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - * This file was based on ov9640.h from MontaVista - */ - -#ifndef TCM825X_H -#define TCM825X_H - -#include - -#include - -#define TCM825X_NAME "tcm825x" - -#define TCM825X_MASK(x) x & 0x00ff -#define TCM825X_ADDR(x) (x & 0xff00) >> 8 - -/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ -#define TCM825X_I2C_ADDR 0x3d - -/* - * define register offsets for the TCM825X sensor chip - * OFFSET(8 bits) + MASK(8 bits) - * MASK bit 4 and 3 are used when the register uses more than one address - */ -#define TCM825X_FPS 0x0280 -#define TCM825X_ACF 0x0240 -#define TCM825X_DOUTBUF 0x020C -#define TCM825X_DCLKP 0x0202 -#define TCM825X_ACFDET 0x0201 -#define TCM825X_DOUTSW 0x0380 -#define TCM825X_DATAHZ 0x0340 -#define TCM825X_PICSIZ 0x033c -#define TCM825X_PICFMT 0x0302 -#define TCM825X_V_INV 0x0480 -#define TCM825X_H_INV 0x0440 -#define TCM825X_ESRLSW 0x0430 -#define TCM825X_V_LENGTH 0x040F -#define TCM825X_ALCSW 0x0580 -#define TCM825X_ESRLIM 0x0560 -#define TCM825X_ESRSPD_U 0x051F -#define TCM825X_ESRSPD_L 0x06FF -#define TCM825X_AG 0x07FF -#define TCM825X_ESRSPD2 0x06FF -#define TCM825X_ALCMODE 0x0830 -#define TCM825X_ALCH 0x080F -#define TCM825X_ALCL 0x09FF -#define TCM825X_AWBSW 0x0A80 -#define TCM825X_MRG 0x0BFF -#define TCM825X_MBG 0x0CFF -#define TCM825X_GAMSW 0x0D80 -#define TCM825X_HDTG 0x0EFF -#define TCM825X_VDTG 0x0FFF -#define TCM825X_HDTCORE 0x10F0 -#define TCM825X_VDTCORE 0x100F -#define TCM825X_CONT 0x11FF -#define TCM825X_BRIGHT 0x12FF -#define TCM825X_VHUE 0x137F -#define TCM825X_UHUE 0x147F -#define TCM825X_VGAIN 0x153F -#define TCM825X_UGAIN 0x163F -#define TCM825X_UVCORE 0x170F -#define TCM825X_SATU 0x187F -#define TCM825X_MHMODE 0x1980 -#define TCM825X_MHLPFSEL 0x1940 -#define TCM825X_YMODE 0x1930 -#define TCM825X_MIXHG 0x1907 -#define TCM825X_LENS 0x1A3F -#define TCM825X_AGLIM 0x1BE0 -#define TCM825X_LENSRPOL 0x1B10 -#define TCM825X_LENSRGAIN 0x1B0F -#define TCM825X_ES100S 0x1CFF -#define TCM825X_ES120S 0x1DFF -#define TCM825X_DMASK 0x1EC0 -#define TCM825X_CODESW 0x1E20 -#define TCM825X_CODESEL 0x1E10 -#define TCM825X_TESPIC 0x1E04 -#define TCM825X_PICSEL 0x1E03 -#define TCM825X_HNUM 0x20FF -#define TCM825X_VOUTPH 0x287F -#define TCM825X_ESROUT 0x327F -#define TCM825X_ESROUT2 0x33FF -#define TCM825X_AGOUT 0x34FF -#define TCM825X_DGOUT 0x353F -#define TCM825X_AGSLOW1 0x39C0 -#define TCM825X_FLLSMODE 0x3930 -#define TCM825X_FLLSLIM 0x390F -#define TCM825X_DETSEL 0x3AF0 -#define TCM825X_ACDETNC 0x3A0F -#define TCM825X_AGSLOW2 0x3BC0 -#define TCM825X_DG 0x3B3F -#define TCM825X_REJHLEV 0x3CFF -#define TCM825X_ALCLOCK 0x3D80 -#define TCM825X_FPSLNKSW 0x3D40 -#define TCM825X_ALCSPD 0x3D30 -#define TCM825X_REJH 0x3D03 -#define TCM825X_SHESRSW 0x3E80 -#define TCM825X_ESLIMSEL 0x3E40 -#define TCM825X_SHESRSPD 0x3E30 -#define TCM825X_ELSTEP 0x3E0C -#define TCM825X_ELSTART 0x3E03 -#define TCM825X_AGMIN 0x3FFF -#define TCM825X_PREGRG 0x423F -#define TCM825X_PREGBG 0x433F -#define TCM825X_PRERG 0x443F -#define TCM825X_PREBG 0x453F -#define TCM825X_MSKBR 0x477F -#define TCM825X_MSKGR 0x487F -#define TCM825X_MSKRB 0x497F -#define TCM825X_MSKGB 0x4A7F -#define TCM825X_MSKRG 0x4B7F -#define TCM825X_MSKBG 0x4C7F -#define TCM825X_HDTCSW 0x4D80 -#define TCM825X_VDTCSW 0x4D40 -#define TCM825X_DTCYL 0x4D3F -#define TCM825X_HDTPSW 0x4E80 -#define TCM825X_VDTPSW 0x4E40 -#define TCM825X_DTCGAIN 0x4E3F -#define TCM825X_DTLLIMSW 0x4F10 -#define TCM825X_DTLYLIM 0x4F0F -#define TCM825X_YLCUTLMSK 0x5080 -#define TCM825X_YLCUTL 0x503F -#define TCM825X_YLCUTHMSK 0x5180 -#define TCM825X_YLCUTH 0x513F -#define TCM825X_UVSKNC 0x527F -#define TCM825X_UVLJ 0x537F -#define TCM825X_WBGMIN 0x54FF -#define TCM825X_WBGMAX 0x55FF -#define TCM825X_WBSPDUP 0x5603 -#define TCM825X_ALLAREA 0x5820 -#define TCM825X_WBLOCK 0x5810 -#define TCM825X_WB2SP 0x580F -#define TCM825X_KIZUSW 0x5920 -#define TCM825X_PBRSW 0x5910 -#define TCM825X_ABCSW 0x5903 -#define TCM825X_PBDLV 0x5AFF -#define TCM825X_PBC1LV 0x5BFF - -#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1) - -#define TCM825X_BYTES_PER_PIXEL 2 - -#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */ -#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */ - -/* define a structure for tcm825x register initialization values */ -struct tcm825x_reg { - u8 val; - u16 reg; -}; - -enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; -enum pixel_format { YUV422 = 0, RGB565 }; -#define NUM_IMAGE_SIZES 6 -#define NUM_PIXEL_FORMATS 2 - -#define TCM825X_XCLK_MIN 11900000 -#define TCM825X_XCLK_MAX 25000000 - -struct capture_size { - unsigned long width; - unsigned long height; -}; - -struct tcm825x_platform_data { - /* Is the sensor usable? Doesn't yet mean it's there, but you - * can try! */ - int (*is_okay)(void); - /* Set power state, zero is off, non-zero is on. */ - int (*power_set)(int power); - /* Default registers written after power-on or reset. */ - const struct tcm825x_reg *(*default_regs)(void); - int (*needs_reset)(struct v4l2_int_device *s, void *buf, - struct v4l2_pix_format *fmt); - int (*ifparm)(struct v4l2_ifparm *p); - int (*is_upside_down)(void); -}; - -/* Array of image sizes supported by TCM825X. These must be ordered from - * smallest image size to largest. - */ -static const struct capture_size tcm825x_sizes[] = { - { 128, 96 }, /* subQCIF */ - { 160, 120 }, /* QQVGA */ - { 176, 144 }, /* QCIF */ - { 320, 240 }, /* QVGA */ - { 352, 288 }, /* CIF */ - { 640, 480 }, /* VGA */ -}; - -#endif /* ifndef TCM825X_H */ diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c deleted file mode 100644 index f7707e65761e..000000000000 --- a/drivers/media/video/tda7432.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * For the STS-Thompson TDA7432 audio processor chip - * - * Handles audio functions: volume, balance, tone, loudness - * This driver will not complain if used with any - * other i2c device with the same address. - * - * Muting and tone control by Jonathan Isom - * - * Copyright (c) 2000 Eric Sandeen - * Copyright (c) 2006 Mauro Carvalho Chehab - * This code is placed under the terms of the GNU General Public License - * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) - * Which was based on tda8425.c by Greg Alexander (c) 1998 - * - * OPTIONS: - * debug - set to 1 if you'd like to see debug messages - * set to 2 if you'd like to be inundated with debug messages - * - * loudness - set between 0 and 15 for varying degrees of loudness effect - * - * maxvol - set maximium volume to +20db (1), default is 0db(0) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifndef VIDEO_AUDIO_BALANCE -# define VIDEO_AUDIO_BALANCE 32 -#endif - -MODULE_AUTHOR("Eric Sandeen "); -MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip"); -MODULE_LICENSE("GPL"); - -static int maxvol; -static int loudness; /* disable loudness by default */ -static int debug; /* insmod parameter */ -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0)."); -module_param(loudness, int, S_IRUGO); -MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0)."); -module_param(maxvol, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0)."); - - -/* Structure of address and subaddresses for the tda7432 */ - -struct tda7432 { - struct v4l2_subdev sd; - int addr; - int input; - int volume; - int muted; - int bass, treble; - int lf, lr, rf, rr; - int loud; -}; - -static inline struct tda7432 *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tda7432, sd); -} - -/* The TDA7432 is made by STS-Thompson - * http://www.st.com - * http://us.st.com/stonline/books/pdf/docs/4056.pdf - * - * TDA7432: I2C-bus controlled basic audio processor - * - * The TDA7432 controls basic audio functions like volume, balance, - * and tone control (including loudness). It also has four channel - * output (for front and rear). Since most vidcap cards probably - * don't have 4 channel output, this driver will set front & rear - * together (no independent control). - */ - - /* Subaddresses for TDA7432 */ - -#define TDA7432_IN 0x00 /* Input select */ -#define TDA7432_VL 0x01 /* Volume */ -#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */ -#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */ -#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */ -#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */ -#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */ -#define TDA7432_LD 0x07 /* Loudness */ - - - /* Masks for bits in TDA7432 subaddresses */ - -/* Many of these not used - just for documentation */ - -/* Subaddress 0x00 - Input selection and bass control */ - -/* Bits 0,1,2 control input: - * 0x00 - Stereo input - * 0x02 - Mono input - * 0x03 - Mute (Using Attenuators Plays better with modules) - * Mono probably isn't used - I'm guessing only the stereo - * input is connected on most cards, so we'll set it to stereo. - * - * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut - * Bit 4 controls bass range: 0/1 is extended/standard bass range - * - * Highest 3 bits not used - */ - -#define TDA7432_STEREO_IN 0 -#define TDA7432_MONO_IN 2 /* Probably won't be used */ -#define TDA7432_BASS_SYM 1 << 3 -#define TDA7432_BASS_NORM 1 << 4 - -/* Subaddress 0x01 - Volume */ - -/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps - * Recommended maximum is +20 dB - * - * +32dB: 0x00 - * +20dB: 0x0c - * 0dB: 0x20 - * -79dB: 0x6f - * - * MSB (bit 7) controls loudness: 1/0 is loudness on/off - */ - -#define TDA7432_VOL_0DB 0x20 -#define TDA7432_LD_ON 1 << 7 - - -/* Subaddress 0x02 - Tone control */ - -/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB - * 0x0 is 14dB, 0x7 is 0dB - * - * Bit 3 controls treble attenuation/gain (sign) - * 1 = gain (+) - * 0 = attenuation (-) - * - * Bits 4,5,6 control absolute bass gain from 0dB to 14dB - * (This is only true for normal base range, set in 0x00) - * 0x0 << 4 is 14dB, 0x7 is 0dB - * - * Bit 7 controls bass attenuation/gain (sign) - * 1 << 7 = gain (+) - * 0 << 7 = attenuation (-) - * - * Example: - * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble - */ - -#define TDA7432_TREBLE_0DB 0xf -#define TDA7432_TREBLE 7 -#define TDA7432_TREBLE_GAIN 1 << 3 -#define TDA7432_BASS_0DB 0xf -#define TDA7432_BASS 7 << 4 -#define TDA7432_BASS_GAIN 1 << 7 - - -/* Subaddress 0x03 - Left Front attenuation */ -/* Subaddress 0x04 - Left Rear attenuation */ -/* Subaddress 0x05 - Right Front attenuation */ -/* Subaddress 0x06 - Right Rear attenuation */ - -/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB - * in 1.5dB steps. - * - * 0x00 is 0dB - * 0x1f is -37.5dB - * - * Bit 5 mutes that channel when set (1 = mute, 0 = unmute) - * We'll use the mute on the input, though (above) - * Bits 6,7 unused - */ - -#define TDA7432_ATTEN_0DB 0x00 -#define TDA7432_MUTE 0x1 << 5 - - -/* Subaddress 0x07 - Loudness Control */ - -/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps - * when bit 4 is NOT set - * - * 0x0 is 0dB - * 0xf is -15dB - * - * If bit 4 is set, then there is a flat attenuation according to - * the lower 4 bits, as above. - * - * Bits 5,6,7 unused - */ - - - -/* Begin code */ - -static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - - v4l2_dbg(2, debug, sd, "In tda7432_write\n"); - v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); - buffer[0] = subaddr; - buffer[1] = val; - if (2 != i2c_master_send(client, buffer, 2)) { - v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n", - subaddr, val); - return -1; - } - return 0; -} - -static int tda7432_set(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tda7432 *t = to_state(sd); - unsigned char buf[16]; - - v4l2_dbg(1, debug, sd, - "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", - t->input, t->volume, t->bass, t->treble, t->lf, t->lr, - t->rf, t->rr, t->loud); - buf[0] = TDA7432_IN; - buf[1] = t->input; - buf[2] = t->volume; - buf[3] = t->bass; - buf[4] = t->treble; - buf[5] = t->lf; - buf[6] = t->lr; - buf[7] = t->rf; - buf[8] = t->rr; - buf[9] = t->loud; - if (10 != i2c_master_send(client, buf, 10)) { - v4l2_err(sd, "I/O error, trying tda7432_set\n"); - return -1; - } - - return 0; -} - -static void do_tda7432_init(struct v4l2_subdev *sd) -{ - struct tda7432 *t = to_state(sd); - - v4l2_dbg(2, debug, sd, "In tda7432_init\n"); - - t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ - TDA7432_BASS_SYM | /* Symmetric bass cut */ - TDA7432_BASS_NORM; /* Normal bass range */ - t->volume = 0x3b ; /* -27dB Volume */ - if (loudness) /* Turn loudness on? */ - t->volume |= TDA7432_LD_ON; - t->muted = 1; - t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */ - t->bass = TDA7432_BASS_0DB; /* 0dB Bass */ - t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ - t->loud = loudness; /* insmod parameter */ - - tda7432_set(sd); -} - -static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tda7432 *t = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=t->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (!maxvol){ /* max +20db */ - ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630; - } else { /* max 0db */ - ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829; - } - return 0; - case V4L2_CID_AUDIO_BALANCE: - { - if ( (t->lf) < (t->rf) ) - /* right is attenuated, balance shifted left */ - ctrl->value = (32768 - 1057*(t->rf)); - else - /* left is attenuated, balance shifted right */ - ctrl->value = (32768 + 1057*(t->lf)); - return 0; - } - case V4L2_CID_AUDIO_BASS: - { - /* Bass/treble 4 bits each */ - int bass=t->bass; - if(bass >= 0x8) - bass = ~(bass - 0x8) & 0xf; - ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass); - return 0; - } - case V4L2_CID_AUDIO_TREBLE: - { - int treble=t->treble; - if(treble >= 0x8) - treble = ~(treble - 0x8) & 0xf; - ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble); - return 0; - } - } - return -EINVAL; -} - -static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tda7432 *t = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - t->muted=ctrl->value; - break; - case V4L2_CID_AUDIO_VOLUME: - if(!maxvol){ /* max +20db */ - t->volume = 0x6f - ((ctrl->value)/630); - } else { /* max 0db */ - t->volume = 0x6f - ((ctrl->value)/829); - } - if (loudness) /* Turn on the loudness bit */ - t->volume |= TDA7432_LD_ON; - - tda7432_write(sd, TDA7432_VL, t->volume); - return 0; - case V4L2_CID_AUDIO_BALANCE: - if (ctrl->value < 32768) { - /* shifted to left, attenuate right */ - t->rr = (32768 - ctrl->value)/1057; - t->rf = t->rr; - t->lr = TDA7432_ATTEN_0DB; - t->lf = TDA7432_ATTEN_0DB; - } else if(ctrl->value > 32769) { - /* shifted to right, attenuate left */ - t->lf = (ctrl->value - 32768)/1057; - t->lr = t->lf; - t->rr = TDA7432_ATTEN_0DB; - t->rf = TDA7432_ATTEN_0DB; - } else { - /* centered */ - t->rr = TDA7432_ATTEN_0DB; - t->rf = TDA7432_ATTEN_0DB; - t->lf = TDA7432_ATTEN_0DB; - t->lr = TDA7432_ATTEN_0DB; - } - break; - case V4L2_CID_AUDIO_BASS: - t->bass = ctrl->value >> 12; - if(t->bass>= 0x8) - t->bass = (~t->bass & 0xf) + 0x8 ; - - tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); - return 0; - case V4L2_CID_AUDIO_TREBLE: - t->treble= ctrl->value >> 12; - if(t->treble>= 0x8) - t->treble = (~t->treble & 0xf) + 0x8 ; - - tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); - return 0; - default: - return -EINVAL; - } - - /* Used for both mute and balance changes */ - if (t->muted) - { - /* Mute & update balance*/ - tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); - tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); - tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); - tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); - } else { - tda7432_write(sd, TDA7432_LF, t->lf); - tda7432_write(sd, TDA7432_LR, t->lr); - tda7432_write(sd, TDA7432_RF, t->rf); - tda7432_write(sd, TDA7432_RR, t->rr); - } - return 0; -} - -static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - } - return -EINVAL; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tda7432_core_ops = { - .queryctrl = tda7432_queryctrl, - .g_ctrl = tda7432_g_ctrl, - .s_ctrl = tda7432_s_ctrl, -}; - -static const struct v4l2_subdev_ops tda7432_ops = { - .core = &tda7432_core_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda7432_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tda7432 *t; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return -ENOMEM; - sd = &t->sd; - v4l2_i2c_subdev_init(sd, client, &tda7432_ops); - if (loudness < 0 || loudness > 15) { - v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); - if (loudness < 0) - loudness = 0; - if (loudness > 15) - loudness = 15; - } - - do_tda7432_init(sd); - return 0; -} - -static int tda7432_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - do_tda7432_init(sd); - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id tda7432_id[] = { - { "tda7432", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tda7432_id); - -static struct i2c_driver tda7432_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tda7432", - }, - .probe = tda7432_probe, - .remove = tda7432_remove, - .id_table = tda7432_id, -}; - -module_i2c_driver(tda7432_driver); diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c deleted file mode 100644 index 3d7ddd93282d..000000000000 --- a/drivers/media/video/tda9840.c +++ /dev/null @@ -1,224 +0,0 @@ - /* - tda9840 - i2c-driver for the tda9840 by SGS Thomson - - Copyright (C) 1998-2003 Michael Hunold - Copyright (C) 2008 Hans Verkuil - - The tda9840 is a stereo/dual sound processor with digital - identification. It can be found at address 0x84 on the i2c-bus. - - For detailed informations download the specifications directly - from SGS Thomson at http://www.st.com - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("tda9840 driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define SWITCH 0x00 -#define LEVEL_ADJUST 0x02 -#define STEREO_ADJUST 0x03 -#define TEST 0x04 - -#define TDA9840_SET_MUTE 0x00 -#define TDA9840_SET_MONO 0x10 -#define TDA9840_SET_STEREO 0x2a -#define TDA9840_SET_LANG1 0x12 -#define TDA9840_SET_LANG2 0x1e -#define TDA9840_SET_BOTH 0x1a -#define TDA9840_SET_BOTH_R 0x16 -#define TDA9840_SET_EXTERNAL 0x7a - - -static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (i2c_smbus_write_byte_data(client, reg, val)) - v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n", - val, reg); -} - -static int tda9840_status(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 byte; - - if (1 != i2c_master_recv(client, &byte, 1)) { - v4l2_dbg(1, debug, sd, - "i2c_master_recv() failed\n"); - return -EIO; - } - - if (byte & 0x80) { - v4l2_dbg(1, debug, sd, - "TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } - - v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); - return byte & 0x60; -} - -static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) -{ - int stat = tda9840_status(sd); - int byte; - - if (t->index) - return -EINVAL; - - stat = stat < 0 ? 0 : stat; - if (stat == 0 || stat == 0x60) /* mono input */ - byte = TDA9840_SET_MONO; - else if (stat == 0x40) /* stereo input */ - byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? - TDA9840_SET_MONO : TDA9840_SET_STEREO; - else { /* bilingual */ - switch (t->audmode) { - case V4L2_TUNER_MODE_LANG1_LANG2: - byte = TDA9840_SET_BOTH; - break; - case V4L2_TUNER_MODE_LANG2: - byte = TDA9840_SET_LANG2; - break; - default: - byte = TDA9840_SET_LANG1; - break; - } - } - v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); - tda9840_write(sd, SWITCH, byte); - return 0; -} - -static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) -{ - int stat = tda9840_status(sd); - - if (stat < 0) - return stat; - - t->rxsubchans = V4L2_TUNER_SUB_MONO; - - switch (stat & 0x60) { - case 0x00: - t->rxsubchans = V4L2_TUNER_SUB_MONO; - break; - case 0x20: - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - break; - case 0x40: - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - break; - default: /* Incorrect detect */ - t->rxsubchans = V4L2_TUNER_MODE_MONO; - break; - } - return 0; -} - -static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tda9840_core_ops = { - .g_chip_ident = tda9840_g_chip_ident, -}; - -static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { - .s_tuner = tda9840_s_tuner, - .g_tuner = tda9840_g_tuner, -}; - -static const struct v4l2_subdev_ops tda9840_ops = { - .core = &tda9840_core_ops, - .tuner = &tda9840_tuner_ops, -}; - -/* ----------------------------------------------------------------------- */ - -static int tda9840_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - - /* let's see whether this adapter can support what we need */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - v4l2_i2c_subdev_init(sd, client, &tda9840_ops); - - /* set initial values for level & stereo - adjustment, mode */ - tda9840_write(sd, LEVEL_ADJUST, 0); - tda9840_write(sd, STEREO_ADJUST, 0); - tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); - return 0; -} - -static int tda9840_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - return 0; -} - -static const struct i2c_device_id tda9840_id[] = { - { "tda9840", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tda9840_id); - -static struct i2c_driver tda9840_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tda9840", - }, - .probe = tda9840_probe, - .remove = tda9840_remove, - .id_table = tda9840_id, -}; - -module_i2c_driver(tda9840_driver); diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c deleted file mode 100644 index d1d6ea1dd273..000000000000 --- a/drivers/media/video/tea6415c.c +++ /dev/null @@ -1,187 +0,0 @@ - /* - tea6415c - i2c-driver for the tea6415c by SGS Thomson - - Copyright (C) 1998-2003 Michael Hunold - Copyright (C) 2008 Hans Verkuil - - The tea6415c is a bus controlled video-matrix-switch - with 8 inputs and 6 outputs. - It is cascadable, i.e. it can be found at the addresses - 0x86 and 0x06 on the i2c-bus. - - For detailed informations download the specifications directly - from SGS Thomson at http://www.st.com - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include -#include -#include -#include -#include "tea6415c.h" - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("tea6415c driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* makes a connection between the input-pin 'i' and the output-pin 'o' */ -static int tea6415c_s_routing(struct v4l2_subdev *sd, - u32 i, u32 o, u32 config) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 byte = 0; - int ret; - - v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o); - - /* check if the pins are valid */ - if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) - && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o))) - return -EINVAL; - - /* to understand this, have a look at the tea6415c-specs (p.5) */ - switch (o) { - case 18: - byte = 0x00; - break; - case 14: - byte = 0x20; - break; - case 16: - byte = 0x10; - break; - case 17: - byte = 0x08; - break; - case 15: - byte = 0x18; - break; - case 13: - byte = 0x28; - break; - }; - - switch (i) { - case 5: - byte |= 0x00; - break; - case 8: - byte |= 0x04; - break; - case 3: - byte |= 0x02; - break; - case 20: - byte |= 0x06; - break; - case 6: - byte |= 0x01; - break; - case 10: - byte |= 0x05; - break; - case 1: - byte |= 0x03; - break; - case 11: - byte |= 0x07; - break; - }; - - ret = i2c_smbus_write_byte(client, byte); - if (ret) { - v4l2_dbg(1, debug, sd, - "i2c_smbus_write_byte() failed, ret:%d\n", ret); - return -EIO; - } - return ret; -} - -static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tea6415c_core_ops = { - .g_chip_ident = tea6415c_g_chip_ident, -}; - -static const struct v4l2_subdev_video_ops tea6415c_video_ops = { - .s_routing = tea6415c_s_routing, -}; - -static const struct v4l2_subdev_ops tea6415c_ops = { - .core = &tea6415c_core_ops, - .video = &tea6415c_video_ops, -}; - -static int tea6415c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - - /* let's see whether this adapter can support what we need */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); - return 0; -} - -static int tea6415c_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - return 0; -} - -static const struct i2c_device_id tea6415c_id[] = { - { "tea6415c", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tea6415c_id); - -static struct i2c_driver tea6415c_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tea6415c", - }, - .probe = tea6415c_probe, - .remove = tea6415c_remove, - .id_table = tea6415c_id, -}; - -module_i2c_driver(tea6415c_driver); diff --git a/drivers/media/video/tea6415c.h b/drivers/media/video/tea6415c.h deleted file mode 100644 index 3a47d697536e..000000000000 --- a/drivers/media/video/tea6415c.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __INCLUDED_TEA6415C__ -#define __INCLUDED_TEA6415C__ - -/* the tea6415c's design is quite brain-dead. although there are - 8 inputs and 6 outputs, these aren't enumerated in any way. because - I don't want to say "connect input pin 20 to output pin 17", I define - a "virtual" pin-order. */ - -/* input pins */ -#define TEA6415C_OUTPUT1 18 -#define TEA6415C_OUTPUT2 14 -#define TEA6415C_OUTPUT3 16 -#define TEA6415C_OUTPUT4 17 -#define TEA6415C_OUTPUT5 13 -#define TEA6415C_OUTPUT6 15 - -/* output pins */ -#define TEA6415C_INPUT1 5 -#define TEA6415C_INPUT2 8 -#define TEA6415C_INPUT3 3 -#define TEA6415C_INPUT4 20 -#define TEA6415C_INPUT5 6 -#define TEA6415C_INPUT6 10 -#define TEA6415C_INPUT7 1 -#define TEA6415C_INPUT8 11 - -#endif diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c deleted file mode 100644 index 38757217a074..000000000000 --- a/drivers/media/video/tea6420.c +++ /dev/null @@ -1,169 +0,0 @@ - /* - tea6420 - i2c-driver for the tea6420 by SGS Thomson - - Copyright (C) 1998-2003 Michael Hunold - Copyright (C) 2008 Hans Verkuil - - The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, - 4 stereo outputs and gain control for each output. - It is cascadable, i.e. it can be found at the addresses 0x98 - and 0x9a on the i2c-bus. - - For detailed informations download the specifications directly - from SGS Thomson at http://www.st.com - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include -#include -#include -#include -#include "tea6420.h" - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("tea6420 driver"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* make a connection between the input 'i' and the output 'o' - with gain 'g' (note: i = 6 means 'mute') */ -static int tea6420_s_routing(struct v4l2_subdev *sd, - u32 i, u32 o, u32 config) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int g = (o >> 4) & 0xf; - u8 byte; - int ret; - - o &= 0xf; - v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g); - - /* check if the parameters are valid */ - if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) - return -EINVAL; - - byte = ((o - 1) << 5); - byte |= (i - 1); - - /* to understand this, have a look at the tea6420-specs (p.5) */ - switch (g) { - case 0: - byte |= (3 << 3); - break; - case 2: - byte |= (2 << 3); - break; - case 4: - byte |= (1 << 3); - break; - case 6: - break; - } - - ret = i2c_smbus_write_byte(client, byte); - if (ret) { - v4l2_dbg(1, debug, sd, - "i2c_smbus_write_byte() failed, ret:%d\n", ret); - return -EIO; - } - return 0; -} - -static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tea6420_core_ops = { - .g_chip_ident = tea6420_g_chip_ident, -}; - -static const struct v4l2_subdev_audio_ops tea6420_audio_ops = { - .s_routing = tea6420_s_routing, -}; - -static const struct v4l2_subdev_ops tea6420_ops = { - .core = &tea6420_core_ops, - .audio = &tea6420_audio_ops, -}; - -static int tea6420_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - int err, i; - - /* let's see whether this adapter can support what we need */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - v4l2_i2c_subdev_init(sd, client, &tea6420_ops); - - /* set initial values: set "mute"-input to all outputs at gain 0 */ - err = 0; - for (i = 1; i < 5; i++) - err += tea6420_s_routing(sd, 6, i, 0); - if (err) { - v4l_dbg(1, debug, client, "could not initialize tea6420\n"); - return -ENODEV; - } - return 0; -} - -static int tea6420_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - return 0; -} - -static const struct i2c_device_id tea6420_id[] = { - { "tea6420", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tea6420_id); - -static struct i2c_driver tea6420_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tea6420", - }, - .probe = tea6420_probe, - .remove = tea6420_remove, - .id_table = tea6420_id, -}; - -module_i2c_driver(tea6420_driver); diff --git a/drivers/media/video/tea6420.h b/drivers/media/video/tea6420.h deleted file mode 100644 index 4aa3edb3e193..000000000000 --- a/drivers/media/video/tea6420.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __INCLUDED_TEA6420__ -#define __INCLUDED_TEA6420__ - -/* input pins */ -#define TEA6420_OUTPUT1 1 -#define TEA6420_OUTPUT2 2 -#define TEA6420_OUTPUT3 3 -#define TEA6420_OUTPUT4 4 - -/* output pins */ -#define TEA6420_INPUT1 1 -#define TEA6420_INPUT2 2 -#define TEA6420_INPUT3 3 -#define TEA6420_INPUT4 4 -#define TEA6420_INPUT5 5 -#define TEA6420_INPUT6 6 - -/* gain on the output pins, ORed with the output pin */ -#define TEA6420_GAIN0 0x00 -#define TEA6420_GAIN2 0x20 -#define TEA6420_GAIN4 0x40 -#define TEA6420_GAIN6 0x60 - -#endif diff --git a/drivers/media/video/ths7303.c b/drivers/media/video/ths7303.c deleted file mode 100644 index e5c0eedebc58..000000000000 --- a/drivers/media/video/ths7303.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ths7303- THS7303 Video Amplifier driver - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; 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 -#include -#include -#include -#include - -#include -#include -#include - -MODULE_DESCRIPTION("TI THS7303 video amplifier driver"); -MODULE_AUTHOR("Chaithrika U S"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level 0-1"); - -/* following function is used to set ths7303 */ -static int ths7303_setvalue(struct v4l2_subdev *sd, v4l2_std_id std) -{ - int err = 0; - u8 val; - struct i2c_client *client; - - client = v4l2_get_subdevdata(sd); - - if (std & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { - val = 0x02; - v4l2_dbg(1, debug, sd, "setting value for SDTV format\n"); - } else { - val = 0x00; - v4l2_dbg(1, debug, sd, "disabling all channels\n"); - } - - err |= i2c_smbus_write_byte_data(client, 0x01, val); - err |= i2c_smbus_write_byte_data(client, 0x02, val); - err |= i2c_smbus_write_byte_data(client, 0x03, val); - - if (err) - v4l2_err(sd, "write failed\n"); - - return err; -} - -static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - return ths7303_setvalue(sd, norm); -} - -static int ths7303_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); -} - -static const struct v4l2_subdev_video_ops ths7303_video_ops = { - .s_std_output = ths7303_s_std_output, -}; - -static const struct v4l2_subdev_core_ops ths7303_core_ops = { - .g_chip_ident = ths7303_g_chip_ident, -}; - -static const struct v4l2_subdev_ops ths7303_ops = { - .core = &ths7303_core_ops, - .video = &ths7303_video_ops, -}; - -static int ths7303_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - v4l2_std_id std_id = V4L2_STD_NTSC; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - - v4l2_i2c_subdev_init(sd, client, &ths7303_ops); - - return ths7303_setvalue(sd, std_id); -} - -static int ths7303_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(sd); - - return 0; -} - -static const struct i2c_device_id ths7303_id[] = { - {"ths7303", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, ths7303_id); - -static struct i2c_driver ths7303_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "ths7303", - }, - .probe = ths7303_probe, - .remove = ths7303_remove, - .id_table = ths7303_id, -}; - -module_i2c_driver(ths7303_driver); diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c deleted file mode 100644 index 809a75a558ee..000000000000 --- a/drivers/media/video/tlv320aic23b.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * tlv320aic23b - driver version 0.0.1 - * - * Copyright (C) 2006 Scott Alfter - * - * Based on wm8775 driver - * - * Copyright (C) 2004 Ulf Eklund - * Copyright (C) 2005 Hans Verkuil - * - * This program is free software; 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 - -MODULE_DESCRIPTION("tlv320aic23b driver"); -MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); -MODULE_LICENSE("GPL"); - - -/* ----------------------------------------------------------------------- */ - -struct tlv320aic23b_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; -}; - -static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tlv320aic23b_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd; -} - -static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - if ((reg < 0 || reg > 9) && (reg != 15)) { - v4l2_err(sd, "Invalid register R%d\n", reg); - return -1; - } - - for (i = 0; i < 3; i++) - if (i2c_smbus_write_byte_data(client, - (reg << 1) | (val >> 8), val & 0xff) == 0) - return 0; - v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); - return -1; -} - -static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - switch (freq) { - case 32000: /* set sample rate to 32 kHz */ - tlv320aic23b_write(sd, 8, 0x018); - break; - case 44100: /* set sample rate to 44.1 kHz */ - tlv320aic23b_write(sd, 8, 0x022); - break; - case 48000: /* set sample rate to 48 kHz */ - tlv320aic23b_write(sd, 8, 0x000); - break; - default: - return -EINVAL; - } - return 0; -} - -static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ - /* set gain on both channels to +3.0 dB */ - if (!ctrl->val) - tlv320aic23b_write(sd, 0, 0x119); - return 0; - } - return -EINVAL; -} - -static int tlv320aic23b_log_status(struct v4l2_subdev *sd) -{ - struct tlv320aic23b_state *state = to_state(sd); - - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { - .s_ctrl = tlv320aic23b_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { - .log_status = tlv320aic23b_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { - .s_clock_freq = tlv320aic23b_s_clock_freq, -}; - -static const struct v4l2_subdev_ops tlv320aic23b_ops = { - .core = &tlv320aic23b_core_ops, - .audio = &tlv320aic23b_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int tlv320aic23b_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tlv320aic23b_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); - - /* Initialize tlv320aic23b */ - - /* RESET */ - tlv320aic23b_write(sd, 15, 0x000); - /* turn off DAC & mic input */ - tlv320aic23b_write(sd, 6, 0x00A); - /* left-justified, 24-bit, master mode */ - tlv320aic23b_write(sd, 7, 0x049); - /* set gain on both channels to +3.0 dB */ - tlv320aic23b_write(sd, 0, 0x119); - /* set sample rate to 48 kHz */ - tlv320aic23b_write(sd, 8, 0x000); - /* activate digital interface */ - tlv320aic23b_write(sd, 9, 0x001); - - v4l2_ctrl_handler_init(&state->hdl, 1); - v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_handler_setup(&state->hdl); - return 0; -} - -static int tlv320aic23b_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct tlv320aic23b_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id tlv320aic23b_id[] = { - { "tlv320aic23b", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); - -static struct i2c_driver tlv320aic23b_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tlv320aic23b", - }, - .probe = tlv320aic23b_probe, - .remove = tlv320aic23b_remove, - .id_table = tlv320aic23b_id, -}; - -module_i2c_driver(tlv320aic23b_driver); diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c deleted file mode 100644 index 321b3153df87..000000000000 --- a/drivers/media/video/tvaudio.c +++ /dev/null @@ -1,2118 +0,0 @@ -/* - * Driver for simple i2c audio chips. - * - * Copyright (c) 2000 Gerd Knorr - * based on code by: - * Eric Sandeen (eric_sandeen@bigfoot.com) - * Steve VanDeBogart (vandebo@uclink.berkeley.edu) - * Greg Alexander (galexand@acm.org) - * - * Copyright(c) 2005-2008 Mauro Carvalho Chehab - * - Some cleanups, code fixes, etc - * - Convert it to V4L2 API - * - * This code is placed under the terms of the GNU General Public License - * - * OPTIONS: - * debug - set to 1 if you'd like to see debug messages - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -/* ---------------------------------------------------------------------- */ -/* insmod args */ - -static int debug; /* insmod parameter */ -module_param(debug, int, 0644); - -MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); -MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr"); -MODULE_LICENSE("GPL"); - -#define UNSET (-1U) - -/* ---------------------------------------------------------------------- */ -/* our structs */ - -#define MAXREGS 256 - -struct CHIPSTATE; -typedef int (*getvalue)(int); -typedef int (*checkit)(struct CHIPSTATE*); -typedef int (*initialize)(struct CHIPSTATE*); -typedef int (*getrxsubchans)(struct CHIPSTATE *); -typedef void (*setaudmode)(struct CHIPSTATE*, int mode); - -/* i2c command */ -typedef struct AUDIOCMD { - int count; /* # of bytes to send */ - unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */ -} audiocmd; - -/* chip description */ -struct CHIPDESC { - char *name; /* chip name */ - int addr_lo, addr_hi; /* i2c address range */ - int registers; /* # of registers */ - - int *insmodopt; - checkit checkit; - initialize initialize; - int flags; -#define CHIP_HAS_VOLUME 1 -#define CHIP_HAS_BASSTREBLE 2 -#define CHIP_HAS_INPUTSEL 4 -#define CHIP_NEED_CHECKMODE 8 - - /* various i2c command sequences */ - audiocmd init; - - /* which register has which value */ - int leftreg,rightreg,treblereg,bassreg; - - /* initialize with (defaults to 65535/65535/32768/32768 */ - int leftinit,rightinit,trebleinit,bassinit; - - /* functions to convert the values (v4l -> chip) */ - getvalue volfunc,treblefunc,bassfunc; - - /* get/set mode */ - getrxsubchans getrxsubchans; - setaudmode setaudmode; - - /* input switch register + values for v4l inputs */ - int inputreg; - int inputmap[4]; - int inputmute; - int inputmask; -}; - -/* current state of the chip */ -struct CHIPSTATE { - struct v4l2_subdev sd; - - /* chip-specific description - should point to - an entry at CHIPDESC table */ - struct CHIPDESC *desc; - - /* shadow register set */ - audiocmd shadow; - - /* current settings */ - __u16 left, right, treble, bass, muted; - int prevmode; - int radio; - int input; - - /* thread */ - struct task_struct *thread; - struct timer_list wt; - int audmode; -}; - -static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct CHIPSTATE, sd); -} - - -/* ---------------------------------------------------------------------- */ -/* i2c I/O functions */ - -static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - - if (subaddr < 0) { - v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); - chip->shadow.bytes[1] = val; - buffer[0] = val; - if (1 != i2c_master_send(c, buffer, 1)) { - v4l2_warn(sd, "I/O error (write 0x%x)\n", val); - return -1; - } - } else { - if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l2_info(sd, - "Tried to access a non-existent register: %d\n", - subaddr); - return -EINVAL; - } - - v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", - subaddr, val); - chip->shadow.bytes[subaddr+1] = val; - buffer[0] = subaddr; - buffer[1] = val; - if (2 != i2c_master_send(c, buffer, 2)) { - v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", - subaddr, val); - return -1; - } - } - return 0; -} - -static int chip_write_masked(struct CHIPSTATE *chip, - int subaddr, int val, int mask) -{ - struct v4l2_subdev *sd = &chip->sd; - - if (mask != 0) { - if (subaddr < 0) { - val = (chip->shadow.bytes[1] & ~mask) | (val & mask); - } else { - if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l2_info(sd, - "Tried to access a non-existent register: %d\n", - subaddr); - return -EINVAL; - } - - val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); - } - } - return chip_write(chip, subaddr, val); -} - -static int chip_read(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer; - - if (1 != i2c_master_recv(c, &buffer, 1)) { - v4l2_warn(sd, "I/O error (read)\n"); - return -1; - } - v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); - return buffer; -} - -static int chip_read2(struct CHIPSTATE *chip, int subaddr) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char write[1]; - unsigned char read[1]; - struct i2c_msg msgs[2] = { - { c->addr, 0, 1, write }, - { c->addr, I2C_M_RD, 1, read } - }; - - write[0] = subaddr; - - if (2 != i2c_transfer(c->adapter, msgs, 2)) { - v4l2_warn(sd, "I/O error (read2)\n"); - return -1; - } - v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", - subaddr, read[0]); - return read[0]; -} - -static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) -{ - struct v4l2_subdev *sd = &chip->sd; - struct i2c_client *c = v4l2_get_subdevdata(sd); - int i; - - if (0 == cmd->count) - return 0; - - if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l2_info(sd, - "Tried to access a non-existent register range: %d to %d\n", - cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); - return -EINVAL; - } - - /* FIXME: it seems that the shadow bytes are wrong bellow !*/ - - /* update our shadow register set; print bytes if (debug > 0) */ - v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", - name, cmd->bytes[0]); - for (i = 1; i < cmd->count; i++) { - if (debug) - printk(KERN_CONT " 0x%x", cmd->bytes[i]); - chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; - } - if (debug) - printk(KERN_CONT "\n"); - - /* send data to the chip */ - if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { - v4l2_warn(sd, "I/O error (%s)\n", name); - return -1; - } - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* kernel thread for doing i2c stuff asyncronly - * right now it is used only to check the audio mode (mono/stereo/whatever) - * some time after switching to another TV channel, then turn on stereo - * if available, ... - */ - -static void chip_thread_wake(unsigned long data) -{ - struct CHIPSTATE *chip = (struct CHIPSTATE*)data; - wake_up_process(chip->thread); -} - -static int chip_thread(void *data) -{ - struct CHIPSTATE *chip = data; - struct CHIPDESC *desc = chip->desc; - struct v4l2_subdev *sd = &chip->sd; - int mode, selected; - - v4l2_dbg(1, debug, sd, "thread started\n"); - set_freezable(); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (!kthread_should_stop()) - schedule(); - set_current_state(TASK_RUNNING); - try_to_freeze(); - if (kthread_should_stop()) - break; - v4l2_dbg(1, debug, sd, "thread wakeup\n"); - - /* don't do anything for radio */ - if (chip->radio) - continue; - - /* have a look what's going on */ - mode = desc->getrxsubchans(chip); - if (mode == chip->prevmode) - continue; - - /* chip detected a new audio mode - set it */ - v4l2_dbg(1, debug, sd, "thread checkmode\n"); - - chip->prevmode = mode; - - selected = V4L2_TUNER_MODE_MONO; - switch (chip->audmode) { - case V4L2_TUNER_MODE_MONO: - if (mode & V4L2_TUNER_SUB_LANG1) - selected = V4L2_TUNER_MODE_LANG1; - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - if (mode & V4L2_TUNER_SUB_LANG1) - selected = V4L2_TUNER_MODE_LANG1; - else if (mode & V4L2_TUNER_SUB_STEREO) - selected = V4L2_TUNER_MODE_STEREO; - break; - case V4L2_TUNER_MODE_LANG2: - if (mode & V4L2_TUNER_SUB_LANG2) - selected = V4L2_TUNER_MODE_LANG2; - else if (mode & V4L2_TUNER_SUB_STEREO) - selected = V4L2_TUNER_MODE_STEREO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - if (mode & V4L2_TUNER_SUB_LANG2) - selected = V4L2_TUNER_MODE_LANG1_LANG2; - else if (mode & V4L2_TUNER_SUB_STEREO) - selected = V4L2_TUNER_MODE_STEREO; - } - desc->setaudmode(chip, selected); - - /* schedule next check */ - mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); - } - - v4l2_dbg(1, debug, sd, "thread exiting\n"); - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda9840 */ - -#define TDA9840_SW 0x00 -#define TDA9840_LVADJ 0x02 -#define TDA9840_STADJ 0x03 -#define TDA9840_TEST 0x04 - -#define TDA9840_MONO 0x10 -#define TDA9840_STEREO 0x2a -#define TDA9840_DUALA 0x12 -#define TDA9840_DUALB 0x1e -#define TDA9840_DUALAB 0x1a -#define TDA9840_DUALBA 0x16 -#define TDA9840_EXTERNAL 0x7a - -#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */ -#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */ -#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */ - -#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ -#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ - -static int tda9840_getrxsubchans(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int val, mode; - - val = chip_read(chip); - mode = V4L2_TUNER_SUB_MONO; - if (val & TDA9840_DS_DUAL) - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - if (val & TDA9840_ST_STEREO) - mode = V4L2_TUNER_SUB_STEREO; - - v4l2_dbg(1, debug, sd, - "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n", - val, mode); - return mode; -} - -static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) -{ - int update = 1; - int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; - - switch (mode) { - case V4L2_TUNER_MODE_MONO: - t |= TDA9840_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - t |= TDA9840_STEREO; - break; - case V4L2_TUNER_MODE_LANG1: - t |= TDA9840_DUALA; - break; - case V4L2_TUNER_MODE_LANG2: - t |= TDA9840_DUALB; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - t |= TDA9840_DUALAB; - break; - default: - update = 0; - } - - if (update) - chip_write(chip, TDA9840_SW, t); -} - -static int tda9840_checkit(struct CHIPSTATE *chip) -{ - int rc; - rc = chip_read(chip); - /* lower 5 bits should be 0 */ - return ((rc & 0x1f) == 0) ? 1 : 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda985x */ - -/* subaddresses for TDA9855 */ -#define TDA9855_VR 0x00 /* Volume, right */ -#define TDA9855_VL 0x01 /* Volume, left */ -#define TDA9855_BA 0x02 /* Bass */ -#define TDA9855_TR 0x03 /* Treble */ -#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ - -/* subaddresses for TDA9850 */ -#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */ - -/* subaddesses for both chips */ -#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */ -#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */ -#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */ -#define TDA985x_A1 0x08 /* Alignment 1 for both chips */ -#define TDA985x_A2 0x09 /* Alignment 2 for both chips */ -#define TDA985x_A3 0x0a /* Alignment 3 for both chips */ - -/* Masks for bits in TDA9855 subaddresses */ -/* 0x00 - VR in TDA9855 */ -/* 0x01 - VL in TDA9855 */ -/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) - * in 1dB steps - mute is 0x27 */ - - -/* 0x02 - BA in TDA9855 */ -/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) - * in .5dB steps - 0 is 0x0E */ - - -/* 0x03 - TR in TDA9855 */ -/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) - * in 3dB steps - 0 is 0x7 */ - -/* Masks for bits in both chips' subaddresses */ -/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */ -/* Unique to TDA9855: */ -/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf) - * in 3dB steps - mute is 0x0 */ - -/* Unique to TDA9850: */ -/* lower 4 bits control stereo noise threshold, over which stereo turns off - * set to values of 0x00 through 0x0f for Ster1 through Ster16 */ - - -/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/ -/* Unique to TDA9855: */ -#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ -#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ -#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ -#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ - /* Bits 0 to 3 select various combinations - * of line in and line out, only the - * interesting ones are defined */ -#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ -#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ - -/* Unique to TDA9850: */ -/* lower 4 bits contol SAP noise threshold, over which SAP turns off - * set to values of 0x00 through 0x0f for SAP1 through SAP16 */ - - -/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ -/* Common to TDA9855 and TDA9850: */ -#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ -#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */ -#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ -#define TDA985x_MONO 0 /* Forces Mono output */ -#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ - -/* Unique to TDA9855: */ -#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ -#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ -#define TDA9855_LINEAR 0 /* Linear Stereo */ -#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ -#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ -#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ -#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ - -/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */ -/* Common to both TDA9855 and TDA9850: */ -/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) - * in .5dB steps - 0dB is 0x7 */ - -/* 0x08, 0x09 - A1 and A2 (read/write) */ -/* Common to both TDA9855 and TDA9850: */ -/* lower 5 bites are wideband and spectral expander alignment - * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ -#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */ -#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ -#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ - -/* 0x0a - A3 */ -/* Common to both TDA9855 and TDA9850: */ -/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), - * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ -#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ - -static int tda9855_volume(int val) { return val/0x2e8+0x27; } -static int tda9855_bass(int val) { return val/0xccc+0x06; } -static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } - -static int tda985x_getrxsubchans(struct CHIPSTATE *chip) -{ - int mode, val; - - /* Add mono mode regardless of SAP and stereo */ - /* Allows forced mono */ - mode = V4L2_TUNER_SUB_MONO; - val = chip_read(chip); - if (val & TDA985x_STP) - mode = V4L2_TUNER_SUB_STEREO; - if (val & TDA985x_SAPP) - mode |= V4L2_TUNER_SUB_SAP; - return mode; -} - -static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode) -{ - int update = 1; - int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; - - switch (mode) { - case V4L2_TUNER_MODE_MONO: - c6 |= TDA985x_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - c6 |= TDA985x_STEREO; - break; - case V4L2_TUNER_MODE_SAP: - c6 |= TDA985x_SAP; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - c6 |= TDA985x_MONOSAP; - break; - default: - update = 0; - } - if (update) - chip_write(chip,TDA985x_C6,c6); -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda9873h */ - -/* Subaddresses for TDA9873H */ - -#define TDA9873_SW 0x00 /* Switching */ -#define TDA9873_AD 0x01 /* Adjust */ -#define TDA9873_PT 0x02 /* Port */ - -/* Subaddress 0x00: Switching Data - * B7..B0: - * - * B1, B0: Input source selection - * 0, 0 internal - * 1, 0 external stereo - * 0, 1 external mono - */ -#define TDA9873_INP_MASK 3 -#define TDA9873_INTERNAL 0 -#define TDA9873_EXT_STEREO 2 -#define TDA9873_EXT_MONO 1 - -/* B3, B2: output signal select - * B4 : transmission mode - * 0, 0, 1 Mono - * 1, 0, 0 Stereo - * 1, 1, 1 Stereo (reversed channel) - * 0, 0, 0 Dual AB - * 0, 0, 1 Dual AA - * 0, 1, 0 Dual BB - * 0, 1, 1 Dual BA - */ - -#define TDA9873_TR_MASK (7 << 2) -#define TDA9873_TR_MONO 4 -#define TDA9873_TR_STEREO 1 << 4 -#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2)) -#define TDA9873_TR_DUALA 1 << 2 -#define TDA9873_TR_DUALB 1 << 3 -#define TDA9873_TR_DUALAB 0 - -/* output level controls - * B5: output level switch (0 = reduced gain, 1 = normal gain) - * B6: mute (1 = muted) - * B7: auto-mute (1 = auto-mute enabled) - */ - -#define TDA9873_GAIN_NORMAL 1 << 5 -#define TDA9873_MUTE 1 << 6 -#define TDA9873_AUTOMUTE 1 << 7 - -/* Subaddress 0x01: Adjust/standard */ - -/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB) - * Recommended value is +0 dB - */ - -#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */ - -/* Bits C6..C4 control FM stantard - * C6, C5, C4 - * 0, 0, 0 B/G (PAL FM) - * 0, 0, 1 M - * 0, 1, 0 D/K(1) - * 0, 1, 1 D/K(2) - * 1, 0, 0 D/K(3) - * 1, 0, 1 I - */ -#define TDA9873_BG 0 -#define TDA9873_M 1 -#define TDA9873_DK1 2 -#define TDA9873_DK2 3 -#define TDA9873_DK3 4 -#define TDA9873_I 5 - -/* C7 controls identification response time (1=fast/0=normal) - */ -#define TDA9873_IDR_NORM 0 -#define TDA9873_IDR_FAST 1 << 7 - - -/* Subaddress 0x02: Port data */ - -/* E1, E0 free programmable ports P1/P2 - 0, 0 both ports low - 0, 1 P1 high - 1, 0 P2 high - 1, 1 both ports high -*/ - -#define TDA9873_PORTS 3 - -/* E2: test port */ -#define TDA9873_TST_PORT 1 << 2 - -/* E5..E3 control mono output channel (together with transmission mode bit B4) - * - * E5 E4 E3 B4 OUTM - * 0 0 0 0 mono - * 0 0 1 0 DUAL B - * 0 1 0 1 mono (from stereo decoder) - */ -#define TDA9873_MOUT_MONO 0 -#define TDA9873_MOUT_FMONO 0 -#define TDA9873_MOUT_DUALA 0 -#define TDA9873_MOUT_DUALB 1 << 3 -#define TDA9873_MOUT_ST 1 << 4 -#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3)) -#define TDA9873_MOUT_EXTL 1 << 5 -#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3)) -#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4)) -#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3)) - -/* Status bits: (chip read) */ -#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ -#define TDA9873_STEREO 2 /* Stereo sound is identified */ -#define TDA9873_DUAL 4 /* Dual sound is identified */ - -static int tda9873_getrxsubchans(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int val,mode; - - val = chip_read(chip); - mode = V4L2_TUNER_SUB_MONO; - if (val & TDA9873_STEREO) - mode = V4L2_TUNER_SUB_STEREO; - if (val & TDA9873_DUAL) - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - v4l2_dbg(1, debug, sd, - "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n", - val, mode); - return mode; -} - -static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode) -{ - struct v4l2_subdev *sd = &chip->sd; - int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; - /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ - - if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { - v4l2_dbg(1, debug, sd, - "tda9873_setaudmode(): external input\n"); - return; - } - - v4l2_dbg(1, debug, sd, - "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n", - TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); - v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n", - sw_data); - - switch (mode) { - case V4L2_TUNER_MODE_MONO: - sw_data |= TDA9873_TR_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - sw_data |= TDA9873_TR_STEREO; - break; - case V4L2_TUNER_MODE_LANG1: - sw_data |= TDA9873_TR_DUALA; - break; - case V4L2_TUNER_MODE_LANG2: - sw_data |= TDA9873_TR_DUALB; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - sw_data |= TDA9873_TR_DUALAB; - break; - default: - return; - } - - chip_write(chip, TDA9873_SW, sw_data); - v4l2_dbg(1, debug, sd, - "tda9873_setaudmode(): req. mode %d; chip_write: %d\n", - mode, sw_data); -} - -static int tda9873_checkit(struct CHIPSTATE *chip) -{ - int rc; - - if (-1 == (rc = chip_read2(chip,254))) - return 0; - return (rc & ~0x1f) == 0x80; -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip description - defines+functions for tda9874h and tda9874a */ -/* Dariusz Kowalewski */ - -/* Subaddresses for TDA9874H and TDA9874A (slave rx) */ -#define TDA9874A_AGCGR 0x00 /* AGC gain */ -#define TDA9874A_GCONR 0x01 /* general config */ -#define TDA9874A_MSR 0x02 /* monitor select */ -#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */ -#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */ -#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */ -#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */ -#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */ -#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */ -#define TDA9874A_DCR 0x09 /* demodulator config */ -#define TDA9874A_FMER 0x0a /* FM de-emphasis */ -#define TDA9874A_FMMR 0x0b /* FM dematrix */ -#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */ -#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */ -#define TDA9874A_NCONR 0x0e /* NICAM config */ -#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */ -#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */ -#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */ -#define TDA9874A_AMCONR 0x12 /* audio mute control */ -#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */ -#define TDA9874A_AOSR 0x14 /* analog output select */ -#define TDA9874A_DAICONR 0x15 /* digital audio interface config */ -#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */ -#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */ -#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */ -#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */ - -/* Subaddresses for TDA9874H and TDA9874A (slave tx) */ -#define TDA9874A_DSR 0x00 /* device status */ -#define TDA9874A_NSR 0x01 /* NICAM status */ -#define TDA9874A_NECR 0x02 /* NICAM error count */ -#define TDA9874A_DR1 0x03 /* add. data LSB */ -#define TDA9874A_DR2 0x04 /* add. data MSB */ -#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */ -#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */ -#define TDA9874A_SIFLR 0x07 /* SIF level */ -#define TDA9874A_TR2 252 /* test reg. 2 */ -#define TDA9874A_TR1 253 /* test reg. 1 */ -#define TDA9874A_DIC 254 /* device id. code */ -#define TDA9874A_SIC 255 /* software id. code */ - - -static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */ -static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */ -static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */ -static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */ -static int tda9874a_dic = -1; /* device id. code */ - -/* insmod options for tda9874a */ -static unsigned int tda9874a_SIF = UNSET; -static unsigned int tda9874a_AMSEL = UNSET; -static unsigned int tda9874a_STD = UNSET; -module_param(tda9874a_SIF, int, 0444); -module_param(tda9874a_AMSEL, int, 0444); -module_param(tda9874a_STD, int, 0444); - -/* - * initialization table for tda9874 decoder: - * - carrier 1 freq. registers (3 bytes) - * - carrier 2 freq. registers (3 bytes) - * - demudulator config register - * - FM de-emphasis register (slow identification mode) - * Note: frequency registers must be written in single i2c transfer. - */ -static struct tda9874a_MODES { - char *name; - audiocmd cmd; -} tda9874a_modelist[9] = { - { "A2, B/G", /* default */ - { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, - { "A2, M (Korea)", - { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, - { "A2, D/K (1)", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} }, - { "A2, D/K (2)", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} }, - { "A2, D/K (3)", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} }, - { "NICAM, I", - { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, - { "NICAM, B/G", - { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, - { "NICAM, D/K", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, - { "NICAM, L", - { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } -}; - -static int tda9874a_setup(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - - chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); - chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); - if(tda9874a_dic == 0x11) { - chip_write(chip, TDA9874A_FMMR, 0x80); - } else { /* dic == 0x07 */ - chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd); - chip_write(chip, TDA9874A_FMMR, 0x00); - } - chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); - chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */ - /* Note: If signal quality is poor you may want to change NICAM */ - /* error limit registers (NLELR and NUELR) to some greater values. */ - /* Then the sound would remain stereo, but won't be so clear. */ - chip_write(chip, TDA9874A_NLELR, 0x14); /* default */ - chip_write(chip, TDA9874A_NUELR, 0x50); /* default */ - - if(tda9874a_dic == 0x11) { - chip_write(chip, TDA9874A_AMCONR, 0xf9); - chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); - chip_write(chip, TDA9874A_AOSR, 0x80); - chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); - chip_write(chip, TDA9874A_ESP, tda9874a_ESP); - } else { /* dic == 0x07 */ - chip_write(chip, TDA9874A_AMCONR, 0xfb); - chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); - chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ - } - v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", - tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); - return 1; -} - -static int tda9874a_getrxsubchans(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int dsr,nsr,mode; - int necr; /* just for debugging */ - - mode = V4L2_TUNER_SUB_MONO; - - if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR))) - return mode; - if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR))) - return mode; - if(-1 == (necr = chip_read2(chip,TDA9874A_NECR))) - return mode; - - /* need to store dsr/nsr somewhere */ - chip->shadow.bytes[MAXREGS-2] = dsr; - chip->shadow.bytes[MAXREGS-1] = nsr; - - if(tda9874a_mode) { - /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked. - * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates - * that sound has (temporarily) switched from NICAM to - * mono FM (or AM) on 1st sound carrier due to high NICAM bit - * error count. So in fact there is no stereo in this case :-( - * But changing the mode to V4L2_TUNER_MODE_MONO would switch - * external 4052 multiplexer in audio_hook(). - */ - if(nsr & 0x02) /* NSR.S/MB=1 */ - mode = V4L2_TUNER_SUB_STEREO; - if(nsr & 0x01) /* NSR.D/SB=1 */ - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } else { - if(dsr & 0x02) /* DSR.IDSTE=1 */ - mode = V4L2_TUNER_SUB_STEREO; - if(dsr & 0x04) /* DSR.IDDUA=1 */ - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } - - v4l2_dbg(1, debug, sd, - "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", - dsr, nsr, necr, mode); - return mode; -} - -static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode) -{ - struct v4l2_subdev *sd = &chip->sd; - - /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ - /* If auto-muting is disabled, we can hear a signal of degrading quality. */ - if (tda9874a_mode) { - if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ - tda9874a_NCONR &= 0xfe; /* enable */ - else - tda9874a_NCONR |= 0x01; /* disable */ - chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); - } - - /* Note: TDA9874A supports automatic FM dematrixing (FMMR register) - * and has auto-select function for audio output (AOSR register). - * Old TDA9874H doesn't support these features. - * TDA9874A also has additional mono output pin (OUTM), which - * on same (all?) tv-cards is not used, anyway (as well as MONOIN). - */ - if(tda9874a_dic == 0x11) { - int aosr = 0x80; - int mdacosr = (tda9874a_mode) ? 0x82:0x80; - - switch(mode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_STEREO: - break; - case V4L2_TUNER_MODE_LANG1: - aosr = 0x80; /* auto-select, dual A/A */ - mdacosr = (tda9874a_mode) ? 0x82:0x80; - break; - case V4L2_TUNER_MODE_LANG2: - aosr = 0xa0; /* auto-select, dual B/B */ - mdacosr = (tda9874a_mode) ? 0x83:0x81; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - aosr = 0x00; /* always route L to L and R to R */ - mdacosr = (tda9874a_mode) ? 0x82:0x80; - break; - default: - return; - } - chip_write(chip, TDA9874A_AOSR, aosr); - chip_write(chip, TDA9874A_MDACOSR, mdacosr); - - v4l2_dbg(1, debug, sd, - "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", - mode, aosr, mdacosr); - - } else { /* dic == 0x07 */ - int fmmr,aosr; - - switch(mode) { - case V4L2_TUNER_MODE_MONO: - fmmr = 0x00; /* mono */ - aosr = 0x10; /* A/A */ - break; - case V4L2_TUNER_MODE_STEREO: - if(tda9874a_mode) { - fmmr = 0x00; - aosr = 0x00; /* handled by NICAM auto-mute */ - } else { - fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */ - aosr = 0x00; - } - break; - case V4L2_TUNER_MODE_LANG1: - fmmr = 0x02; /* dual */ - aosr = 0x10; /* dual A/A */ - break; - case V4L2_TUNER_MODE_LANG2: - fmmr = 0x02; /* dual */ - aosr = 0x20; /* dual B/B */ - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - fmmr = 0x02; /* dual */ - aosr = 0x00; /* dual A/B */ - break; - default: - return; - } - chip_write(chip, TDA9874A_FMMR, fmmr); - chip_write(chip, TDA9874A_AOSR, aosr); - - v4l2_dbg(1, debug, sd, - "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", - mode, fmmr, aosr); - } -} - -static int tda9874a_checkit(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int dic,sic; /* device id. and software id. codes */ - - if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) - return 0; - if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) - return 0; - - v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); - - if((dic == 0x11)||(dic == 0x07)) { - v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); - tda9874a_dic = dic; /* remember device id. */ - return 1; - } - return 0; /* not found */ -} - -static int tda9874a_initialize(struct CHIPSTATE *chip) -{ - if (tda9874a_SIF > 2) - tda9874a_SIF = 1; - if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) - tda9874a_STD = 0; - if(tda9874a_AMSEL > 1) - tda9874a_AMSEL = 0; - - if(tda9874a_SIF == 1) - tda9874a_GCONR = 0xc0; /* sound IF input 1 */ - else - tda9874a_GCONR = 0xc1; /* sound IF input 2 */ - - tda9874a_ESP = tda9874a_STD; - tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; - - if(tda9874a_AMSEL == 0) - tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ - else - tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ - - tda9874a_setup(chip); - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip description - defines+functions for tda9875 */ -/* The TDA9875 is made by Philips Semiconductor - * http://www.semiconductors.philips.com - * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator - * - */ - -/* subaddresses for TDA9875 */ -#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ -#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ -#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ -#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ - -#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ -#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ -#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ -#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ - -#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ -#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ -#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ -#define TDA9875_MVL 0x1a /* Main volume gauche */ -#define TDA9875_MVR 0x1b /* Main volume droite */ -#define TDA9875_MBA 0x1d /* Main Basse */ -#define TDA9875_MTR 0x1e /* Main treble */ -#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/ -#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */ -#define TDA9875_AVR 0x21 /* Auxiliary volume droite */ -#define TDA9875_ABA 0x22 /* Auxiliary Basse */ -#define TDA9875_ATR 0x23 /* Auxiliary treble */ - -#define TDA9875_MSR 0x02 /* Monitor select register */ -#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ -#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ -#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ -#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ -#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ -#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ -#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ -#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ -#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ - -/* values */ -#define TDA9875_MUTE_ON 0xff /* general mute */ -#define TDA9875_MUTE_OFF 0xcc /* general no mute */ - -static int tda9875_initialize(struct CHIPSTATE *chip) -{ - chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ - chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ - chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ - chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ - chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ - chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ - chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ - chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ - chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ - chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ - chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ - chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ - chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ - - chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ - chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ - chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ - chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ - chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ - chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ - chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ - chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ - chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ - chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ - chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ - chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ - chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ - chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ - chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ - chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ - - chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ - return 0; -} - -static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } -static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } -static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } - -/* ----------------------------------------------------------------------- */ - - -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda9875_checkit(struct CHIPSTATE *chip) -{ - struct v4l2_subdev *sd = &chip->sd; - int dic, rev; - - dic = chip_read2(chip, 254); - rev = chip_read2(chip, 255); - - if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ - v4l2_info(sd, "found tda9875%s rev. %d.\n", - dic == 0 ? "" : "A", rev); - return 1; - } - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tea6420 */ - -#define TEA6300_VL 0x00 /* volume left */ -#define TEA6300_VR 0x01 /* volume right */ -#define TEA6300_BA 0x02 /* bass */ -#define TEA6300_TR 0x03 /* treble */ -#define TEA6300_FA 0x04 /* fader control */ -#define TEA6300_S 0x05 /* switch register */ - /* values for those registers: */ -#define TEA6300_S_SA 0x01 /* stereo A input */ -#define TEA6300_S_SB 0x02 /* stereo B */ -#define TEA6300_S_SC 0x04 /* stereo C */ -#define TEA6300_S_GMU 0x80 /* general mute */ - -#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */ -#define TEA6320_FFR 0x01 /* fader front right (0-5) */ -#define TEA6320_FFL 0x02 /* fader front left (0-5) */ -#define TEA6320_FRR 0x03 /* fader rear right (0-5) */ -#define TEA6320_FRL 0x04 /* fader rear left (0-5) */ -#define TEA6320_BA 0x05 /* bass (0-4) */ -#define TEA6320_TR 0x06 /* treble (0-4) */ -#define TEA6320_S 0x07 /* switch register */ - /* values for those registers: */ -#define TEA6320_S_SA 0x07 /* stereo A input */ -#define TEA6320_S_SB 0x06 /* stereo B */ -#define TEA6320_S_SC 0x05 /* stereo C */ -#define TEA6320_S_SD 0x04 /* stereo D */ -#define TEA6320_S_GMU 0x80 /* general mute */ - -#define TEA6420_S_SA 0x00 /* stereo A input */ -#define TEA6420_S_SB 0x01 /* stereo B */ -#define TEA6420_S_SC 0x02 /* stereo C */ -#define TEA6420_S_SD 0x03 /* stereo D */ -#define TEA6420_S_SE 0x04 /* stereo E */ -#define TEA6420_S_GMU 0x05 /* general mute */ - -static int tea6300_shift10(int val) { return val >> 10; } -static int tea6300_shift12(int val) { return val >> 12; } - -/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */ -/* 0x0c mirror those immediately higher) */ -static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; } -static int tea6320_shift11(int val) { return val >> 11; } -static int tea6320_initialize(struct CHIPSTATE * chip) -{ - chip_write(chip, TEA6320_FFR, 0x3f); - chip_write(chip, TEA6320_FFL, 0x3f); - chip_write(chip, TEA6320_FRR, 0x3f); - chip_write(chip, TEA6320_FRL, 0x3f); - - return 0; -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for tda8425 */ - -#define TDA8425_VL 0x00 /* volume left */ -#define TDA8425_VR 0x01 /* volume right */ -#define TDA8425_BA 0x02 /* bass */ -#define TDA8425_TR 0x03 /* treble */ -#define TDA8425_S1 0x08 /* switch functions */ - /* values for those registers: */ -#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ -#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */ -#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */ -#define TDA8425_S1_MU 0x20 /* mute bit */ -#define TDA8425_S1_STEREO 0x18 /* stereo bits */ -#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */ -#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */ -#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */ -#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */ -#define TDA8425_S1_ML 0x06 /* language selector */ -#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */ -#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */ -#define TDA8425_S1_ML_STEREO 0x06 /* stereo */ -#define TDA8425_S1_IS 0x01 /* channel selector */ - - -static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; } -static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } - -static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode) -{ - int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1; - - switch (mode) { - case V4L2_TUNER_MODE_LANG1: - s1 |= TDA8425_S1_ML_SOUND_A; - s1 |= TDA8425_S1_STEREO_PSEUDO; - break; - case V4L2_TUNER_MODE_LANG2: - s1 |= TDA8425_S1_ML_SOUND_B; - s1 |= TDA8425_S1_STEREO_PSEUDO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - s1 |= TDA8425_S1_ML_STEREO; - s1 |= TDA8425_S1_STEREO_LINEAR; - break; - case V4L2_TUNER_MODE_MONO: - s1 |= TDA8425_S1_ML_STEREO; - s1 |= TDA8425_S1_STEREO_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - s1 |= TDA8425_S1_ML_STEREO; - s1 |= TDA8425_S1_STEREO_SPATIAL; - break; - default: - return; - } - chip_write(chip,TDA8425_S1,s1); -} - - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for pic16c54 (PV951) */ - -/* the registers of 16C54, I2C sub address. */ -#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */ -#define PIC16C54_REG_MISC 0x02 - -/* bit definition of the RESET register, I2C data. */ -#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ - /* code of remote controller */ -#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */ -#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */ -#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */ -#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */ -#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */ -#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ -#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - defines+functions for TA8874Z */ - -/* write 1st byte */ -#define TA8874Z_LED_STE 0x80 -#define TA8874Z_LED_BIL 0x40 -#define TA8874Z_LED_EXT 0x20 -#define TA8874Z_MONO_SET 0x10 -#define TA8874Z_MUTE 0x08 -#define TA8874Z_F_MONO 0x04 -#define TA8874Z_MODE_SUB 0x02 -#define TA8874Z_MODE_MAIN 0x01 - -/* write 2nd byte */ -/*#define TA8874Z_TI 0x80 */ /* test mode */ -#define TA8874Z_SEPARATION 0x3f -#define TA8874Z_SEPARATION_DEFAULT 0x10 - -/* read */ -#define TA8874Z_B1 0x80 -#define TA8874Z_B0 0x40 -#define TA8874Z_CHAG_FLAG 0x20 - -/* - * B1 B0 - * mono L H - * stereo L L - * BIL H L - */ -static int ta8874z_getrxsubchans(struct CHIPSTATE *chip) -{ - int val, mode; - - val = chip_read(chip); - mode = V4L2_TUNER_SUB_MONO; - if (val & TA8874Z_B1){ - mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - }else if (!(val & TA8874Z_B0)){ - mode = V4L2_TUNER_SUB_STEREO; - } - /* v4l2_dbg(1, debug, &chip->sd, - "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n", - val, mode); */ - return mode; -} - -static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; -static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; - -static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) -{ - struct v4l2_subdev *sd = &chip->sd; - int update = 1; - audiocmd *t = NULL; - - v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode); - - switch(mode){ - case V4L2_TUNER_MODE_MONO: - t = &ta8874z_mono; - break; - case V4L2_TUNER_MODE_STEREO: - t = &ta8874z_stereo; - break; - case V4L2_TUNER_MODE_LANG1: - t = &ta8874z_main; - break; - case V4L2_TUNER_MODE_LANG2: - t = &ta8874z_sub; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - t = &ta8874z_both; - break; - default: - update = 0; - } - - if(update) - chip_cmd(chip, "TA8874Z", t); -} - -static int ta8874z_checkit(struct CHIPSTATE *chip) -{ - int rc; - rc = chip_read(chip); - return ((rc & 0x1f) == 0x1f) ? 1 : 0; -} - -/* ---------------------------------------------------------------------- */ -/* audio chip descriptions - struct CHIPDESC */ - -/* insmod options to enable/disable individual audio chips */ -static int tda8425 = 1; -static int tda9840 = 1; -static int tda9850 = 1; -static int tda9855 = 1; -static int tda9873 = 1; -static int tda9874a = 1; -static int tda9875 = 1; -static int tea6300; /* default 0 - address clash with msp34xx */ -static int tea6320; /* default 0 - address clash with msp34xx */ -static int tea6420 = 1; -static int pic16c54 = 1; -static int ta8874z; /* default 0 - address clash with tda9840 */ - -module_param(tda8425, int, 0444); -module_param(tda9840, int, 0444); -module_param(tda9850, int, 0444); -module_param(tda9855, int, 0444); -module_param(tda9873, int, 0444); -module_param(tda9874a, int, 0444); -module_param(tda9875, int, 0444); -module_param(tea6300, int, 0444); -module_param(tea6320, int, 0444); -module_param(tea6420, int, 0444); -module_param(pic16c54, int, 0444); -module_param(ta8874z, int, 0444); - -static struct CHIPDESC chiplist[] = { - { - .name = "tda9840", - .insmodopt = &tda9840, - .addr_lo = I2C_ADDR_TDA9840 >> 1, - .addr_hi = I2C_ADDR_TDA9840 >> 1, - .registers = 5, - .flags = CHIP_NEED_CHECKMODE, - - /* callbacks */ - .checkit = tda9840_checkit, - .getrxsubchans = tda9840_getrxsubchans, - .setaudmode = tda9840_setaudmode, - - .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN - /* ,TDA9840_SW, TDA9840_MONO */} } - }, - { - .name = "tda9873h", - .insmodopt = &tda9873, - .addr_lo = I2C_ADDR_TDA985x_L >> 1, - .addr_hi = I2C_ADDR_TDA985x_H >> 1, - .registers = 3, - .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, - - /* callbacks */ - .checkit = tda9873_checkit, - .getrxsubchans = tda9873_getrxsubchans, - .setaudmode = tda9873_setaudmode, - - .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, - .inputreg = TDA9873_SW, - .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE, - .inputmap = {0xa0, 0xa2, 0xa0, 0xa0}, - .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE, - - }, - { - .name = "tda9874h/a", - .insmodopt = &tda9874a, - .addr_lo = I2C_ADDR_TDA9874 >> 1, - .addr_hi = I2C_ADDR_TDA9874 >> 1, - .flags = CHIP_NEED_CHECKMODE, - - /* callbacks */ - .initialize = tda9874a_initialize, - .checkit = tda9874a_checkit, - .getrxsubchans = tda9874a_getrxsubchans, - .setaudmode = tda9874a_setaudmode, - }, - { - .name = "tda9875", - .insmodopt = &tda9875, - .addr_lo = I2C_ADDR_TDA9875 >> 1, - .addr_hi = I2C_ADDR_TDA9875 >> 1, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, - - /* callbacks */ - .initialize = tda9875_initialize, - .checkit = tda9875_checkit, - .volfunc = tda9875_volume, - .bassfunc = tda9875_bass, - .treblefunc = tda9875_treble, - .leftreg = TDA9875_MVL, - .rightreg = TDA9875_MVR, - .bassreg = TDA9875_MBA, - .treblereg = TDA9875_MTR, - .leftinit = 58880, - .rightinit = 58880, - }, - { - .name = "tda9850", - .insmodopt = &tda9850, - .addr_lo = I2C_ADDR_TDA985x_L >> 1, - .addr_hi = I2C_ADDR_TDA985x_H >> 1, - .registers = 11, - - .getrxsubchans = tda985x_getrxsubchans, - .setaudmode = tda985x_setaudmode, - - .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } - }, - { - .name = "tda9855", - .insmodopt = &tda9855, - .addr_lo = I2C_ADDR_TDA985x_L >> 1, - .addr_hi = I2C_ADDR_TDA985x_H >> 1, - .registers = 11, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, - - .leftreg = TDA9855_VL, - .rightreg = TDA9855_VR, - .bassreg = TDA9855_BA, - .treblereg = TDA9855_TR, - - /* callbacks */ - .volfunc = tda9855_volume, - .bassfunc = tda9855_bass, - .treblefunc = tda9855_treble, - .getrxsubchans = tda985x_getrxsubchans, - .setaudmode = tda985x_setaudmode, - - .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, - TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, - TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, - 0x07, 0x10, 0x10, 0x03 }} - }, - { - .name = "tea6300", - .insmodopt = &tea6300, - .addr_lo = I2C_ADDR_TEA6300 >> 1, - .addr_hi = I2C_ADDR_TEA6300 >> 1, - .registers = 6, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, - - .leftreg = TEA6300_VR, - .rightreg = TEA6300_VL, - .bassreg = TEA6300_BA, - .treblereg = TEA6300_TR, - - /* callbacks */ - .volfunc = tea6300_shift10, - .bassfunc = tea6300_shift12, - .treblefunc = tea6300_shift12, - - .inputreg = TEA6300_S, - .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, - .inputmute = TEA6300_S_GMU, - }, - { - .name = "tea6320", - .insmodopt = &tea6320, - .addr_lo = I2C_ADDR_TEA6300 >> 1, - .addr_hi = I2C_ADDR_TEA6300 >> 1, - .registers = 8, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, - - .leftreg = TEA6320_V, - .rightreg = TEA6320_V, - .bassreg = TEA6320_BA, - .treblereg = TEA6320_TR, - - /* callbacks */ - .initialize = tea6320_initialize, - .volfunc = tea6320_volume, - .bassfunc = tea6320_shift11, - .treblefunc = tea6320_shift11, - - .inputreg = TEA6320_S, - .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD }, - .inputmute = TEA6300_S_GMU, - }, - { - .name = "tea6420", - .insmodopt = &tea6420, - .addr_lo = I2C_ADDR_TEA6420 >> 1, - .addr_hi = I2C_ADDR_TEA6420 >> 1, - .registers = 1, - .flags = CHIP_HAS_INPUTSEL, - - .inputreg = -1, - .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, - .inputmute = TEA6300_S_GMU, - }, - { - .name = "tda8425", - .insmodopt = &tda8425, - .addr_lo = I2C_ADDR_TDA8425 >> 1, - .addr_hi = I2C_ADDR_TDA8425 >> 1, - .registers = 9, - .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, - - .leftreg = TDA8425_VL, - .rightreg = TDA8425_VR, - .bassreg = TDA8425_BA, - .treblereg = TDA8425_TR, - - /* callbacks */ - .volfunc = tda8425_shift10, - .bassfunc = tda8425_shift12, - .treblefunc = tda8425_shift12, - .setaudmode = tda8425_setaudmode, - - .inputreg = TDA8425_S1, - .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, - .inputmute = TDA8425_S1_OFF, - - }, - { - .name = "pic16c54 (PV951)", - .insmodopt = &pic16c54, - .addr_lo = I2C_ADDR_PIC16C54 >> 1, - .addr_hi = I2C_ADDR_PIC16C54>> 1, - .registers = 2, - .flags = CHIP_HAS_INPUTSEL, - - .inputreg = PIC16C54_REG_MISC, - .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, - PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, - PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, - PIC16C54_MISC_SND_MUTE}, - .inputmute = PIC16C54_MISC_SND_MUTE, - }, - { - .name = "ta8874z", - .checkit = ta8874z_checkit, - .insmodopt = &ta8874z, - .addr_lo = I2C_ADDR_TDA9840 >> 1, - .addr_hi = I2C_ADDR_TDA9840 >> 1, - .registers = 2, - - /* callbacks */ - .getrxsubchans = ta8874z_getrxsubchans, - .setaudmode = ta8874z_setaudmode, - - .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, - }, - { .name = NULL } /* EOF */ -}; - - -/* ---------------------------------------------------------------------- */ - -static int tvaudio_g_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - ctrl->value=chip->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - ctrl->value = max(chip->left,chip->right); - return 0; - case V4L2_CID_AUDIO_BALANCE: - { - int volume; - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - volume = max(chip->left,chip->right); - if (volume) - ctrl->value=(32768*min(chip->left,chip->right))/volume; - else - ctrl->value=32768; - return 0; - } - case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->bass; - return 0; - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->treble; - return 0; - } - return -EINVAL; -} - -static int tvaudio_s_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - - if (ctrl->value < 0 || ctrl->value >= 2) - return -ERANGE; - chip->muted = ctrl->value; - if (chip->muted) - chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); - else - chip_write_masked(chip,desc->inputreg, - desc->inputmap[chip->input],desc->inputmask); - return 0; - case V4L2_CID_AUDIO_VOLUME: - { - int volume,balance; - - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - volume = max(chip->left,chip->right); - if (volume) - balance=(32768*min(chip->left,chip->right))/volume; - else - balance=32768; - - volume=ctrl->value; - chip->left = (min(65536 - balance,32768) * volume) / 32768; - chip->right = (min(balance,volume *(__u16)32768)) / 32768; - - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); - - return 0; - } - case V4L2_CID_AUDIO_BALANCE: - { - int volume, balance; - - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - volume = max(chip->left, chip->right); - balance = ctrl->value; - chip->left = (min(65536 - balance, 32768) * volume) / 32768; - chip->right = (min(balance, volume * (__u16)32768)) / 32768; - - chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); - - return 0; - } - case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->bass = ctrl->value; - chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); - - return 0; - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->treble = ctrl->value; - chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); - - return 0; - } - return -EINVAL; -} - - -/* ---------------------------------------------------------------------- */ -/* video4linux interface */ - -static int tvaudio_s_radio(struct v4l2_subdev *sd) -{ - struct CHIPSTATE *chip = to_state(sd); - - chip->radio = 1; - /* del_timer(&chip->wt); */ - return 0; -} - -static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - if (desc->flags & CHIP_HAS_INPUTSEL) - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - break; - case V4L2_CID_AUDIO_VOLUME: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - break; - case V4L2_CID_AUDIO_BALANCE: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - default: - break; - } - return -EINVAL; -} - -static int tvaudio_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - return 0; - if (input >= 4) - return -EINVAL; - /* There are four inputs: tuner, radio, extern and intern. */ - chip->input = input; - if (chip->muted) - return 0; - chip_write_masked(chip, desc->inputreg, - desc->inputmap[chip->input], desc->inputmask); - return 0; -} - -static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - if (!desc->setaudmode) - return 0; - if (chip->radio) - return 0; - - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - case V4L2_TUNER_MODE_LANG2: - case V4L2_TUNER_MODE_LANG1_LANG2: - break; - default: - return -EINVAL; - } - chip->audmode = vt->audmode; - - if (chip->thread) - wake_up_process(chip->thread); - else - desc->setaudmode(chip, vt->audmode); - - return 0; -} - -static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - if (!desc->getrxsubchans) - return 0; - if (chip->radio) - return 0; - - vt->audmode = chip->audmode; - vt->rxsubchans = desc->getrxsubchans(chip); - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - - return 0; -} - -static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct CHIPSTATE *chip = to_state(sd); - - chip->radio = 0; - return 0; -} - -static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - /* For chips that provide getrxsubchans and setaudmode, and doesn't - automatically follows the stereo carrier, a kthread is - created to set the audio standard. In this case, when then - the video channel is changed, tvaudio starts on MONO mode. - After waiting for 2 seconds, the kernel thread is called, - to follow whatever audio standard is pointed by the - audio carrier. - */ - if (chip->thread) { - desc->setaudmode(chip, V4L2_TUNER_MODE_MONO); - chip->prevmode = -1; /* reset previous mode */ - mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); - } - return 0; -} - -static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tvaudio_core_ops = { - .g_chip_ident = tvaudio_g_chip_ident, - .queryctrl = tvaudio_queryctrl, - .g_ctrl = tvaudio_g_ctrl, - .s_ctrl = tvaudio_s_ctrl, - .s_std = tvaudio_s_std, -}; - -static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { - .s_radio = tvaudio_s_radio, - .s_frequency = tvaudio_s_frequency, - .s_tuner = tvaudio_s_tuner, - .g_tuner = tvaudio_g_tuner, -}; - -static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { - .s_routing = tvaudio_s_routing, -}; - -static const struct v4l2_subdev_ops tvaudio_ops = { - .core = &tvaudio_core_ops, - .tuner = &tvaudio_tuner_ops, - .audio = &tvaudio_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - - -/* i2c registration */ - -static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct CHIPSTATE *chip; - struct CHIPDESC *desc; - struct v4l2_subdev *sd; - - if (debug) { - printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); - printk(KERN_INFO "tvaudio: known chips: "); - for (desc = chiplist; desc->name != NULL; desc++) - printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); - printk("\n"); - } - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - sd = &chip->sd; - v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); - - /* find description for the chip */ - v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); - for (desc = chiplist; desc->name != NULL; desc++) { - if (0 == *(desc->insmodopt)) - continue; - if (client->addr < desc->addr_lo || - client->addr > desc->addr_hi) - continue; - if (desc->checkit && !desc->checkit(chip)) - continue; - break; - } - if (desc->name == NULL) { - v4l2_dbg(1, debug, sd, "no matching chip description found\n"); - kfree(chip); - return -EIO; - } - v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); - if (desc->flags) { - v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", - (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", - (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", - (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); - } - - /* fill required data structures */ - if (!id) - strlcpy(client->name, desc->name, I2C_NAME_SIZE); - chip->desc = desc; - chip->shadow.count = desc->registers+1; - chip->prevmode = -1; - chip->audmode = V4L2_TUNER_MODE_LANG1; - - /* initialization */ - if (desc->initialize != NULL) - desc->initialize(chip); - else - chip_cmd(chip, "init", &desc->init); - - if (desc->flags & CHIP_HAS_VOLUME) { - if (!desc->volfunc) { - /* This shouldn't be happen. Warn user, but keep working - without volume controls - */ - v4l2_info(sd, "volume callback undefined!\n"); - desc->flags &= ~CHIP_HAS_VOLUME; - } else { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; - chip_write(chip, desc->leftreg, - desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, - desc->volfunc(chip->right)); - } - } - if (desc->flags & CHIP_HAS_BASSTREBLE) { - if (!desc->bassfunc || !desc->treblefunc) { - /* This shouldn't be happen. Warn user, but keep working - without bass/treble controls - */ - v4l2_info(sd, "bass/treble callbacks undefined!\n"); - desc->flags &= ~CHIP_HAS_BASSTREBLE; - } else { - chip->treble = desc->trebleinit ? - desc->trebleinit : 32768; - chip->bass = desc->bassinit ? - desc->bassinit : 32768; - chip_write(chip, desc->bassreg, - desc->bassfunc(chip->bass)); - chip_write(chip, desc->treblereg, - desc->treblefunc(chip->treble)); - } - } - - chip->thread = NULL; - init_timer(&chip->wt); - if (desc->flags & CHIP_NEED_CHECKMODE) { - if (!desc->getrxsubchans || !desc->setaudmode) { - /* This shouldn't be happen. Warn user, but keep working - without kthread - */ - v4l2_info(sd, "set/get mode callbacks undefined!\n"); - return 0; - } - /* start async thread */ - chip->wt.function = chip_thread_wake; - chip->wt.data = (unsigned long)chip; - chip->thread = kthread_run(chip_thread, chip, client->name); - if (IS_ERR(chip->thread)) { - v4l2_warn(sd, "failed to create kthread\n"); - chip->thread = NULL; - } - } - return 0; -} - -static int tvaudio_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct CHIPSTATE *chip = to_state(sd); - - del_timer_sync(&chip->wt); - if (chip->thread) { - /* shutdown async thread */ - kthread_stop(chip->thread); - chip->thread = NULL; - } - - v4l2_device_unregister_subdev(sd); - kfree(chip); - return 0; -} - -/* This driver supports many devices and the idea is to let the driver - detect which device is present. So rather than listing all supported - devices here, we pretend to support a single, fake device type. */ -static const struct i2c_device_id tvaudio_id[] = { - { "tvaudio", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tvaudio_id); - -static struct i2c_driver tvaudio_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tvaudio", - }, - .probe = tvaudio_probe, - .remove = tvaudio_remove, - .id_table = tvaudio_id, -}; - -module_i2c_driver(tvaudio_driver); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c deleted file mode 100644 index 3b6cf034976a..000000000000 --- a/drivers/media/video/tveeprom.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * tveeprom - eeprom decoder for tvcard configuration eeproms - * - * Data and decoding routines shamelessly borrowed from bttv-cards.c - * eeprom access routine shamelessly borrowed from bttv-if.c - * which are: - - Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - & Marcus Metzler (mocm@thp.uni-koeln.de) - (c) 1999-2001 Gerd Knorr - - * Adjustments to fit a more general model and all bugs: - - Copyright (C) 2003 John Klar - - * This program is free software; 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 -#include -#include - -MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); -MODULE_AUTHOR("John Klar"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define STRM(array, i) \ - (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown") - -#define tveeprom_info(fmt, arg...) \ - v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg) -#define tveeprom_warn(fmt, arg...) \ - v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg) -#define tveeprom_dbg(fmt, arg...) do { \ - if (debug) \ - v4l_printk(KERN_DEBUG, "tveeprom", \ - c->adapter, c->addr, fmt , ## arg); \ - } while (0) - -/* - * The Hauppauge eeprom uses an 8bit field to determine which - * tuner formats the tuner supports. - */ -static struct HAUPPAUGE_TUNER_FMT -{ - int id; - char *name; -} -hauppauge_tuner_fmt[] = -{ - { V4L2_STD_UNKNOWN, " UNKNOWN" }, - { V4L2_STD_UNKNOWN, " FM" }, - { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" }, - { V4L2_STD_MN, " NTSC(M)" }, - { V4L2_STD_PAL_I, " PAL(I)" }, - { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" }, - { V4L2_STD_DK, " PAL(D/D1/K)" }, - { V4L2_STD_ATSC, " ATSC/DVB Digital" }, -}; - -/* This is the full list of possible tuners. Many thanks to Hauppauge for - supplying this information. Note that many tuners where only used for - testing and never made it to the outside world. So you will only see - a subset in actual produced cards. */ -static struct HAUPPAUGE_TUNER -{ - int id; - char *name; -} -hauppauge_tuner[] = -{ - /* 0-9 */ - { TUNER_ABSENT, "None" }, - { TUNER_ABSENT, "External" }, - { TUNER_ABSENT, "Unspecified" }, - { TUNER_PHILIPS_PAL, "Philips FI1216" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FI1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, - { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, - { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, - /* 10-19 */ - { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, - { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, - { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, - { TUNER_TEMIC_PAL, "Temic 4002FH5" }, - { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, - { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, - { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, - /* 20-29 */ - { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, - { TUNER_PHILIPS_PAL, "Philips FM1216" }, - { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FM1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, - { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, - { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, - { TUNER_ABSENT, "Samsung TCPN9082D" }, - { TUNER_ABSENT, "Samsung TCPM9092P" }, - { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, - /* 30-39 */ - { TUNER_ABSENT, "Samsung TCPN9085D" }, - { TUNER_ABSENT, "Samsung TCPB9085P" }, - { TUNER_ABSENT, "Samsung TCPL9091P" }, - { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, - { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, - { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, - { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ - { TUNER_ABSENT, "Philips FI1256MP" }, - /* 40-49 */ - { TUNER_ABSENT, "Samsung TCPQ9091P" }, - { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, - { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, - { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, - { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, - { TUNER_ABSENT, "Philips TD1536D FH 44"}, - { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, - { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, - { TUNER_LG_PAL, "LG TP18PSB11D"}, - { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, - /* 50-59 */ - { TUNER_LG_PAL_I, "LG TAPC-I701D"}, - { TUNER_ABSENT, "Temic 4042FI5"}, - { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, - { TUNER_ABSENT, "LG TPI8NSR11F"}, - { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, - { TUNER_ABSENT, "Philips FI1236 MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, - { TUNER_ABSENT, "Philips FM1216MP MK3"}, - /* 60-69 */ - { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, - { TUNER_ABSENT, "LG M001D MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, - { TUNER_ABSENT, "LG M701D MK3"}, - { TUNER_ABSENT, "Temic 4146FM5"}, - { TUNER_ABSENT, "Temic 4136FY5"}, - { TUNER_ABSENT, "Temic 4106FH5"}, - { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, - /* 70-79 */ - { TUNER_ABSENT, "LG TALN H200T"}, - { TUNER_ABSENT, "LG TALN H250T"}, - { TUNER_ABSENT, "LG TALN M200T"}, - { TUNER_ABSENT, "LG TALN Z200T"}, - { TUNER_ABSENT, "LG TALN S200T"}, - { TUNER_ABSENT, "Thompson DTT7595"}, - { TUNER_ABSENT, "Thompson DTT7592"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, - { TUNER_ABSENT, "Thompson DTT757"}, - /* 80-89 */ - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, - { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, - { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, - { TUNER_TCL_2002N, "TCL 2002N 6A"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, - { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, - { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, - /* 90-99 */ - { TUNER_ABSENT, "LG TALN H202T"}, - { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, - { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, - { TUNER_ABSENT, "Philips FQ1286A MK4"}, - { TUNER_ABSENT, "Philips FQ1216ME MK5"}, - { TUNER_ABSENT, "Philips FQ1236 MK5"}, - { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, - { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, - { TUNER_ABSENT, "TCL 2002MI_3H"}, - { TUNER_TCL_2002N, "TCL 2002N 5H"}, - /* 100-109 */ - { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, - { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, - { TUNER_ABSENT, "Panasonic ENV57H12D5"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, - { TUNER_ABSENT, "TCL MQNM05-4"}, - { TUNER_ABSENT, "LG TAPC-W701D"}, - { TUNER_ABSENT, "TCL 9886P-WM"}, - { TUNER_ABSENT, "TCL 1676NM-WM"}, - /* 110-119 */ - { TUNER_ABSENT, "Thompson DTT75105"}, - { TUNER_ABSENT, "Conexant_CX24109"}, - { TUNER_TCL_2002N, "TCL M2523_5N_E"}, - { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, - { TUNER_ABSENT, "Philips 8275A"}, - { TUNER_ABSENT, "Microtune MT2060"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, - { TUNER_ABSENT, "TCL M2523_3DI_E"}, - { TUNER_ABSENT, "Samsung THPD5222FG30A"}, - /* 120-129 */ - { TUNER_XC2028, "Xceive XC3028"}, - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, - { TUNER_ABSENT, "Philips FQD1216LME"}, - { TUNER_ABSENT, "Conexant CX24118A"}, - { TUNER_ABSENT, "TCL DMF11WIP"}, - { TUNER_ABSENT, "TCL MFNM05_4H_E"}, - { TUNER_ABSENT, "TCL MNM05_4H_E"}, - { TUNER_ABSENT, "TCL MPE05_2H_E"}, - { TUNER_ABSENT, "TCL MQNM05_4_U"}, - { TUNER_ABSENT, "TCL M2523_5NH_E"}, - /* 130-139 */ - { TUNER_ABSENT, "TCL M2523_3DBH_E"}, - { TUNER_ABSENT, "TCL M2523_3DIH_E"}, - { TUNER_ABSENT, "TCL MFPE05_2_U"}, - { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, - { TUNER_ABSENT, "Philips FRH2036B"}, - { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, - { TUNER_ABSENT, "MaxLinear MXL5005"}, - { TUNER_ABSENT, "MaxLinear MXL5003"}, - { TUNER_ABSENT, "Xceive XC2028"}, - { TUNER_ABSENT, "Microtune MT2131"}, - /* 140-149 */ - { TUNER_ABSENT, "Philips 8275A_8295"}, - { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, - { TUNER_ABSENT, "Microtune MT2266"}, - { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, - { TUNER_ABSENT, "LG TAPQ_H702F"}, - { TUNER_ABSENT, "TCL M09WPP_4N_E"}, - { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, - { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, - /* 150-159 */ - { TUNER_XC5000, "Xceive XC5000"}, - { TUNER_ABSENT, "Xceive XC3028L"}, - { TUNER_ABSENT, "NXP 18271C2_716x"}, - { TUNER_ABSENT, "Xceive XC4000"}, - { TUNER_ABSENT, "Dibcom 7070"}, - { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, - { TUNER_ABSENT, "Siano SMS1010"}, - { TUNER_ABSENT, "Siano SMS1150"}, - { TUNER_ABSENT, "MaxLinear 5007"}, - { TUNER_ABSENT, "TCL M09WPP_2P_E"}, - /* 160-169 */ - { TUNER_ABSENT, "Siano SMS1180"}, - { TUNER_ABSENT, "Maxim_MAX2165"}, - { TUNER_ABSENT, "Siano SMS1140"}, - { TUNER_ABSENT, "Siano SMS1150 B1"}, - { TUNER_ABSENT, "MaxLinear 111"}, - { TUNER_ABSENT, "Dibcom 7770"}, - { TUNER_ABSENT, "Siano SMS1180VNS"}, - { TUNER_ABSENT, "Siano SMS1184"}, - { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"}, - { TUNER_ABSENT, "TCL_M11WPP_2PN_E"}, - /* 170-179 */ - { TUNER_ABSENT, "MaxLinear 301"}, - { TUNER_ABSENT, "Mirics MSi001"}, - { TUNER_ABSENT, "MaxLinear MxL241SF"}, - { TUNER_XC5000C, "Xceive XC5000C"}, - { TUNER_ABSENT, "Montage M68TS2020"}, - { TUNER_ABSENT, "Siano SMS1530"}, - { TUNER_ABSENT, "Dibcom 7090"}, - { TUNER_ABSENT, "Xceive XC5200C"}, - { TUNER_ABSENT, "NXP 18273"}, - { TUNER_ABSENT, "Montage M88TS2022"}, - /* 180-189 */ - { TUNER_ABSENT, "NXP 18272M"}, - { TUNER_ABSENT, "NXP 18272S"}, -}; - -/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are - * internal to a video chip, i.e. not a separate audio chip. */ -static struct HAUPPAUGE_AUDIOIC -{ - u32 id; - char *name; -} -audioIC[] = -{ - /* 0-4 */ - { V4L2_IDENT_NONE, "None" }, - { V4L2_IDENT_UNKNOWN, "TEA6300" }, - { V4L2_IDENT_UNKNOWN, "TEA6320" }, - { V4L2_IDENT_UNKNOWN, "TDA9850" }, - { V4L2_IDENT_MSPX4XX, "MSP3400C" }, - /* 5-9 */ - { V4L2_IDENT_MSPX4XX, "MSP3410D" }, - { V4L2_IDENT_MSPX4XX, "MSP3415" }, - { V4L2_IDENT_MSPX4XX, "MSP3430" }, - { V4L2_IDENT_MSPX4XX, "MSP3438" }, - { V4L2_IDENT_UNKNOWN, "CS5331" }, - /* 10-14 */ - { V4L2_IDENT_MSPX4XX, "MSP3435" }, - { V4L2_IDENT_MSPX4XX, "MSP3440" }, - { V4L2_IDENT_MSPX4XX, "MSP3445" }, - { V4L2_IDENT_MSPX4XX, "MSP3411" }, - { V4L2_IDENT_MSPX4XX, "MSP3416" }, - /* 15-19 */ - { V4L2_IDENT_MSPX4XX, "MSP3425" }, - { V4L2_IDENT_MSPX4XX, "MSP3451" }, - { V4L2_IDENT_MSPX4XX, "MSP3418" }, - { V4L2_IDENT_UNKNOWN, "Type 0x12" }, - { V4L2_IDENT_UNKNOWN, "OKI7716" }, - /* 20-24 */ - { V4L2_IDENT_MSPX4XX, "MSP4410" }, - { V4L2_IDENT_MSPX4XX, "MSP4420" }, - { V4L2_IDENT_MSPX4XX, "MSP4440" }, - { V4L2_IDENT_MSPX4XX, "MSP4450" }, - { V4L2_IDENT_MSPX4XX, "MSP4408" }, - /* 25-29 */ - { V4L2_IDENT_MSPX4XX, "MSP4418" }, - { V4L2_IDENT_MSPX4XX, "MSP4428" }, - { V4L2_IDENT_MSPX4XX, "MSP4448" }, - { V4L2_IDENT_MSPX4XX, "MSP4458" }, - { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, - /* 30-34 */ - { V4L2_IDENT_AMBIGUOUS, "CX880" }, - { V4L2_IDENT_AMBIGUOUS, "CX881" }, - { V4L2_IDENT_AMBIGUOUS, "CX883" }, - { V4L2_IDENT_AMBIGUOUS, "CX882" }, - { V4L2_IDENT_AMBIGUOUS, "CX25840" }, - /* 35-39 */ - { V4L2_IDENT_AMBIGUOUS, "CX25841" }, - { V4L2_IDENT_AMBIGUOUS, "CX25842" }, - { V4L2_IDENT_AMBIGUOUS, "CX25843" }, - { V4L2_IDENT_AMBIGUOUS, "CX23418" }, - { V4L2_IDENT_AMBIGUOUS, "CX23885" }, - /* 40-44 */ - { V4L2_IDENT_AMBIGUOUS, "CX23888" }, - { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, - { V4L2_IDENT_AMBIGUOUS, "CX23887" }, - { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, - { V4L2_IDENT_AMBIGUOUS, "AU8522" }, -}; - -/* This list is supplied by Hauppauge. Thanks! */ -static const char *decoderIC[] = { - /* 0-4 */ - "None", "BT815", "BT817", "BT819", "BT815A", - /* 5-9 */ - "BT817A", "BT819A", "BT827", "BT829", "BT848", - /* 10-14 */ - "BT848A", "BT849A", "BT829A", "BT827A", "BT878", - /* 15-19 */ - "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115", - /* 20-24 */ - "CX880", "CX881", "CX883", "SAA7111", "SAA7113", - /* 25-29 */ - "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", - /* 30-34 */ - "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", - /* 35-39 */ - "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A", - /* 40-42 */ - "SAA7164", "CX23885B", "AU8522" -}; - -static int hasRadioTuner(int tunerType) -{ - switch (tunerType) { - case 18: /* PNPEnv_TUNER_FR1236_MK2 */ - case 23: /* PNPEnv_TUNER_FM1236 */ - case 38: /* PNPEnv_TUNER_FMR1236 */ - case 16: /* PNPEnv_TUNER_FR1216_MK2 */ - case 19: /* PNPEnv_TUNER_FR1246_MK2 */ - case 21: /* PNPEnv_TUNER_FM1216 */ - case 24: /* PNPEnv_TUNER_FM1246 */ - case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */ - case 22: /* PNPEnv_TUNER_FM1216MF */ - case 20: /* PNPEnv_TUNER_FR1256_MK2 */ - case 25: /* PNPEnv_TUNER_FM1256 */ - case 33: /* PNPEnv_TUNER_4039FR5 */ - case 42: /* PNPEnv_TUNER_4009FR5 */ - case 52: /* PNPEnv_TUNER_4049FM5 */ - case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */ - case 44: /* PNPEnv_TUNER_4009FN5 */ - case 31: /* PNPEnv_TUNER_TCPB9085P */ - case 30: /* PNPEnv_TUNER_TCPN9085D */ - case 46: /* PNPEnv_TUNER_TP18NSR01F */ - case 47: /* PNPEnv_TUNER_TP18PSB01D */ - case 49: /* PNPEnv_TUNER_TAPC_I001D */ - case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */ - case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */ - case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */ - case 58: /* PNPEnv_TUNER_FM1236_MK3 */ - case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */ - case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */ - case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */ - case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */ - case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */ - case 105: - return 1; - } - return 0; -} - -void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, - unsigned char *eeprom_data) -{ - /* ---------------------------------------------- - ** The hauppauge eeprom format is tagged - ** - ** if packet[0] == 0x84, then packet[0..1] == length - ** else length = packet[0] & 3f; - ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum - ** - ** In our (ivtv) case we're interested in the following: - ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner) - ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into - ** hauppauge_tuner_fmt) - ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM) - ** audio proc: tag [02].01 or [05].00 (mask with 0x7f) - ** decoder proc: tag [09].01) - - ** Fun info: - ** model: tag [00].07-08 or [06].00-01 - ** revision: tag [00].09-0b or [06].04-06 - ** serial#: tag [01].05-07 or [04].04-06 - - ** # of inputs/outputs ??? - */ - - int i, j, len, done, beenhere, tag, start; - - int tuner1 = 0, t_format1 = 0, audioic = -1; - char *t_name1 = NULL; - const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" }; - - int tuner2 = 0, t_format2 = 0; - char *t_name2 = NULL; - const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" }; - - memset(tvee, 0, sizeof(*tvee)); - tvee->tuner_type = TUNER_ABSENT; - tvee->tuner2_type = TUNER_ABSENT; - - done = len = beenhere = 0; - - /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */ - if (eeprom_data[0] == 0x1a && - eeprom_data[1] == 0xeb && - eeprom_data[2] == 0x67 && - eeprom_data[3] == 0x95) - start = 0xa0; /* Generic em28xx offset */ - else if ((eeprom_data[0] & 0xe1) == 0x01 && - eeprom_data[1] == 0x00 && - eeprom_data[2] == 0x00 && - eeprom_data[8] == 0x84) - start = 8; /* Generic cx2388x offset */ - else if (eeprom_data[1] == 0x70 && - eeprom_data[2] == 0x00 && - eeprom_data[4] == 0x74 && - eeprom_data[8] == 0x84) - start = 8; /* Generic cx23418 offset (models 74xxx) */ - else - start = 0; - - for (i = start; !done && i < 256; i += len) { - if (eeprom_data[i] == 0x84) { - len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); - i += 3; - } else if ((eeprom_data[i] & 0xf0) == 0x70) { - if (eeprom_data[i] & 0x08) { - /* verify checksum! */ - done = 1; - break; - } - len = eeprom_data[i] & 0x07; - ++i; - } else { - tveeprom_warn("Encountered bad packet header [%02x]. " - "Corrupt or not a Hauppauge eeprom.\n", - eeprom_data[i]); - return; - } - - if (debug) { - tveeprom_info("Tag [%02x] + %d bytes:", - eeprom_data[i], len - 1); - for (j = 1; j < len; j++) - printk(KERN_CONT " %02x", eeprom_data[i + j]); - printk(KERN_CONT "\n"); - } - - /* process by tag */ - tag = eeprom_data[i]; - switch (tag) { - case 0x00: - /* tag: 'Comprehensive' */ - tuner1 = eeprom_data[i+6]; - t_format1 = eeprom_data[i+5]; - tvee->has_radio = eeprom_data[i+len-1]; - /* old style tag, don't know how to detect - IR presence, mark as unknown. */ - tvee->has_ir = 0; - tvee->model = - eeprom_data[i+8] + - (eeprom_data[i+9] << 8); - tvee->revision = eeprom_data[i+10] + - (eeprom_data[i+11] << 8) + - (eeprom_data[i+12] << 16); - break; - - case 0x01: - /* tag: 'SerialID' */ - tvee->serial_number = - eeprom_data[i+6] + - (eeprom_data[i+7] << 8) + - (eeprom_data[i+8] << 16); - break; - - case 0x02: - /* tag 'AudioInfo' - Note mask with 0x7F, high bit used on some older models - to indicate 4052 mux was removed in favor of using MSP - inputs directly. */ - audioic = eeprom_data[i+2] & 0x7f; - if (audioic < ARRAY_SIZE(audioIC)) - tvee->audio_processor = audioIC[audioic].id; - else - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - break; - - /* case 0x03: tag 'EEInfo' */ - - case 0x04: - /* tag 'SerialID2' */ - tvee->serial_number = - eeprom_data[i+5] + - (eeprom_data[i+6] << 8) + - (eeprom_data[i+7] << 16); - - if ((eeprom_data[i + 8] & 0xf0) && - (tvee->serial_number < 0xffffff)) { - tvee->MAC_address[0] = 0x00; - tvee->MAC_address[1] = 0x0D; - tvee->MAC_address[2] = 0xFE; - tvee->MAC_address[3] = eeprom_data[i + 7]; - tvee->MAC_address[4] = eeprom_data[i + 6]; - tvee->MAC_address[5] = eeprom_data[i + 5]; - tvee->has_MAC_address = 1; - } - break; - - case 0x05: - /* tag 'Audio2' - Note mask with 0x7F, high bit used on some older models - to indicate 4052 mux was removed in favor of using MSP - inputs directly. */ - audioic = eeprom_data[i+1] & 0x7f; - if (audioic < ARRAY_SIZE(audioIC)) - tvee->audio_processor = audioIC[audioic].id; - else - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - - break; - - case 0x06: - /* tag 'ModelRev' */ - tvee->model = - eeprom_data[i + 1] + - (eeprom_data[i + 2] << 8) + - (eeprom_data[i + 3] << 16) + - (eeprom_data[i + 4] << 24); - tvee->revision = - eeprom_data[i + 5] + - (eeprom_data[i + 6] << 8) + - (eeprom_data[i + 7] << 16); - break; - - case 0x07: - /* tag 'Details': according to Hauppauge not interesting - on any PCI-era or later boards. */ - break; - - /* there is no tag 0x08 defined */ - - case 0x09: - /* tag 'Video' */ - tvee->decoder_processor = eeprom_data[i + 1]; - break; - - case 0x0a: - /* tag 'Tuner' */ - if (beenhere == 0) { - tuner1 = eeprom_data[i + 2]; - t_format1 = eeprom_data[i + 1]; - beenhere = 1; - } else { - /* a second (radio) tuner may be present */ - tuner2 = eeprom_data[i + 2]; - t_format2 = eeprom_data[i + 1]; - /* not a TV tuner? */ - if (t_format2 == 0) - tvee->has_radio = 1; /* must be radio */ - } - break; - - case 0x0b: - /* tag 'Inputs': according to Hauppauge this is specific - to each driver family, so no good assumptions can be - made. */ - break; - - /* case 0x0c: tag 'Balun' */ - /* case 0x0d: tag 'Teletext' */ - - case 0x0e: - /* tag: 'Radio' */ - tvee->has_radio = eeprom_data[i+1]; - break; - - case 0x0f: - /* tag 'IRInfo' */ - tvee->has_ir = 1 | (eeprom_data[i+1] << 1); - break; - - /* case 0x10: tag 'VBIInfo' */ - /* case 0x11: tag 'QCInfo' */ - /* case 0x12: tag 'InfoBits' */ - - default: - tveeprom_dbg("Not sure what to do with tag [%02x]\n", - tag); - /* dump the rest of the packet? */ - } - } - - if (!done) { - tveeprom_warn("Ran out of data!\n"); - return; - } - - if (tvee->revision != 0) { - tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); - tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); - tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); - tvee->rev_str[3] = 32 + (tvee->revision & 0x3f); - tvee->rev_str[4] = 0; - } - - if (hasRadioTuner(tuner1) && !tvee->has_radio) { - tveeprom_info("The eeprom says no radio is present, but the tuner type\n"); - tveeprom_info("indicates otherwise. I will assume that radio is present.\n"); - tvee->has_radio = 1; - } - - if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { - tvee->tuner_type = hauppauge_tuner[tuner1].id; - t_name1 = hauppauge_tuner[tuner1].name; - } else { - t_name1 = "unknown"; - } - - if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { - tvee->tuner2_type = hauppauge_tuner[tuner2].id; - t_name2 = hauppauge_tuner[tuner2].name; - } else { - t_name2 = "unknown"; - } - - tvee->tuner_hauppauge_model = tuner1; - tvee->tuner2_hauppauge_model = tuner2; - tvee->tuner_formats = 0; - tvee->tuner2_formats = 0; - for (i = j = 0; i < 8; i++) { - if (t_format1 & (1 << i)) { - tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; - t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; - } - } - for (i = j = 0; i < 8; i++) { - if (t_format2 & (1 << i)) { - tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; - t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; - } - } - - tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", - tvee->model, tvee->rev_str, tvee->serial_number); - if (tvee->has_MAC_address == 1) - tveeprom_info("MAC address is %pM\n", tvee->MAC_address); - tveeprom_info("tuner model is %s (idx %d, type %d)\n", - t_name1, tuner1, tvee->tuner_type); - tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", - t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], - t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5], - t_fmt_name1[6], t_fmt_name1[7], t_format1); - if (tuner2) - tveeprom_info("second tuner model is %s (idx %d, type %d)\n", - t_name2, tuner2, tvee->tuner2_type); - if (t_format2) - tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", - t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], - t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5], - t_fmt_name2[6], t_fmt_name2[7], t_format2); - if (audioic < 0) { - tveeprom_info("audio processor is unknown (no idx)\n"); - tvee->audio_processor = V4L2_IDENT_UNKNOWN; - } else { - if (audioic < ARRAY_SIZE(audioIC)) - tveeprom_info("audio processor is %s (idx %d)\n", - audioIC[audioic].name, audioic); - else - tveeprom_info("audio processor is unknown (idx %d)\n", - audioic); - } - if (tvee->decoder_processor) - tveeprom_info("decoder processor is %s (idx %d)\n", - STRM(decoderIC, tvee->decoder_processor), - tvee->decoder_processor); - if (tvee->has_ir) - tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", - tvee->has_radio ? "" : "no ", - (tvee->has_ir & 2) ? "" : "no ", - (tvee->has_ir & 4) ? "" : "no "); - else - tveeprom_info("has %sradio\n", - tvee->has_radio ? "" : "no "); -} -EXPORT_SYMBOL(tveeprom_hauppauge_analog); - -/* ----------------------------------------------------------------------- */ -/* generic helper functions */ - -int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) -{ - unsigned char buf; - int err; - - buf = 0; - err = i2c_master_send(c, &buf, 1); - if (err != 1) { - tveeprom_info("Huh, no eeprom present (err=%d)?\n", err); - return -1; - } - err = i2c_master_recv(c, eedata, len); - if (err != len) { - tveeprom_warn("i2c eeprom read error (err=%d)\n", err); - return -1; - } - if (debug) { - int i; - - tveeprom_info("full 256-byte eeprom dump:\n"); - for (i = 0; i < len; i++) { - if (0 == (i % 16)) - tveeprom_info("%02x:", i); - printk(KERN_CONT " %02x", eedata[i]); - if (15 == (i % 16)) - printk(KERN_CONT "\n"); - } - } - return 0; -} -EXPORT_SYMBOL(tveeprom_read); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c deleted file mode 100644 index cd615c1d6011..000000000000 --- a/drivers/media/video/tvp514x.c +++ /dev/null @@ -1,1166 +0,0 @@ -/* - * drivers/media/video/tvp514x.c - * - * TI TVP5146/47 decoder driver - * - * Copyright (C) 2008 Texas Instruments Inc - * Author: Vaibhav Hiremath - * - * Contributors: - * Sivaraj R - * Brijesh R Jadav - * Hardik Shah - * Manjunath Hadli - * Karicheri Muralidharan - * - * This package is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "tvp514x_regs.h" - -/* Module Name */ -#define TVP514X_MODULE_NAME "tvp514x" - -/* Private macros for TVP */ -#define I2C_RETRY_COUNT (5) -#define LOCK_RETRY_COUNT (5) -#define LOCK_RETRY_DELAY (200) - -/* Debug functions */ -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("TVP514X linux decoder driver"); -MODULE_LICENSE("GPL"); - -/* enum tvp514x_std - enum for supported standards */ -enum tvp514x_std { - STD_NTSC_MJ = 0, - STD_PAL_BDGHIN, - STD_INVALID -}; - -/** - * struct tvp514x_std_info - Structure to store standard informations - * @width: Line width in pixels - * @height:Number of active lines - * @video_std: Value to write in REG_VIDEO_STD register - * @standard: v4l2 standard structure information - */ -struct tvp514x_std_info { - unsigned long width; - unsigned long height; - u8 video_std; - struct v4l2_standard standard; -}; - -static struct tvp514x_reg tvp514x_reg_list_default[0x40]; - -static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); -/** - * struct tvp514x_decoder - TVP5146/47 decoder object - * @sd: Subdevice Slave handle - * @tvp514x_regs: copy of hw's regs with preset values. - * @pdata: Board specific - * @ver: Chip version - * @streaming: TVP5146/47 decoder streaming - enabled or disabled. - * @current_std: Current standard - * @num_stds: Number of standards - * @std_list: Standards list - * @input: Input routing at chip level - * @output: Output routing at chip level - */ -struct tvp514x_decoder { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; - const struct tvp514x_platform_data *pdata; - - int ver; - int streaming; - - enum tvp514x_std current_std; - int num_stds; - const struct tvp514x_std_info *std_list; - /* Input and Output Routing parameters */ - u32 input; - u32 output; -}; - -/* TVP514x default register values */ -static struct tvp514x_reg tvp514x_reg_list_default[] = { - /* Composite selected */ - {TOK_WRITE, REG_INPUT_SEL, 0x05}, - {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, - /* Auto mode */ - {TOK_WRITE, REG_VIDEO_STD, 0x00}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, - {TOK_WRITE, REG_COLOR_KILLER, 0x10}, - {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, - {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, - {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, - {TOK_WRITE, REG_BRIGHTNESS, 0x80}, - {TOK_WRITE, REG_CONTRAST, 0x80}, - {TOK_WRITE, REG_SATURATION, 0x80}, - {TOK_WRITE, REG_HUE, 0x00}, - {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, - {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, - /* Reserved */ - {TOK_SKIP, 0x0F, 0x00}, - {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, - {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, - {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, - /* Reserved */ - {TOK_SKIP, 0x13, 0x00}, - {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, - /* Reserved */ - {TOK_SKIP, 0x15, 0x00}, - /* NTSC timing */ - {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, - {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, - {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, - {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, - /* NTSC timing */ - {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, - {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, - {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, - {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, - /* NTSC timing */ - {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, - {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, - {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, - {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, - /* NTSC timing */ - {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, - {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, - {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, - {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x26, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x27, 0x00}, - {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, - /* Reserved */ - {TOK_SKIP, 0x29, 0x00}, - {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x2B, 0x00}, - {TOK_SKIP, REG_SCART_DELAY, 0x00}, - {TOK_SKIP, REG_CTI_DELAY, 0x00}, - {TOK_SKIP, REG_CTI_CONTROL, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x2F, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x30, 0x00}, - /* Reserved */ - {TOK_SKIP, 0x31, 0x00}, - /* HS, VS active high */ - {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, - /* 10-bit BT.656 */ - {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, - /* Enable clk & data */ - {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, - /* Enable AVID & FLD */ - {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, - /* Enable VS & HS */ - {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, - {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, - {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, - /* Clear status */ - {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, - {TOK_TERM, 0, 0}, -}; - -/** - * Supported standards - - * - * Currently supports two standards only, need to add support for rest of the - * modes, like SECAM, etc... - */ -static const struct tvp514x_std_info tvp514x_std_list[] = { - /* Standard: STD_NTSC_MJ */ - [STD_NTSC_MJ] = { - .width = NTSC_NUM_ACTIVE_PIXELS, - .height = NTSC_NUM_ACTIVE_LINES, - .video_std = VIDEO_STD_NTSC_MJ_BIT, - .standard = { - .index = 0, - .id = V4L2_STD_NTSC, - .name = "NTSC", - .frameperiod = {1001, 30000}, - .framelines = 525 - }, - /* Standard: STD_PAL_BDGHIN */ - }, - [STD_PAL_BDGHIN] = { - .width = PAL_NUM_ACTIVE_PIXELS, - .height = PAL_NUM_ACTIVE_LINES, - .video_std = VIDEO_STD_PAL_BDGHIN_BIT, - .standard = { - .index = 1, - .id = V4L2_STD_PAL, - .name = "PAL", - .frameperiod = {1, 25}, - .framelines = 625 - }, - }, - /* Standard: need to add for additional standard */ -}; - - -static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tvp514x_decoder, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd; -} - - -/** - * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. - * @sd: ptr to v4l2_subdev struct - * @reg: TVP5146/47 register address - * - * Returns value read if successful, or non-zero (-1) otherwise. - */ -static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) -{ - int err, retry = 0; - struct i2c_client *client = v4l2_get_subdevdata(sd); - -read_again: - - err = i2c_smbus_read_byte_data(client, reg); - if (err < 0) { - if (retry <= I2C_RETRY_COUNT) { - v4l2_warn(sd, "Read: retry ... %d\n", retry); - retry++; - msleep_interruptible(10); - goto read_again; - } - } - - return err; -} - -/** - * dump_reg() - dump the register content of TVP5146/47. - * @sd: ptr to v4l2_subdev struct - * @reg: TVP5146/47 register address - */ -static void dump_reg(struct v4l2_subdev *sd, u8 reg) -{ - u32 val; - - val = tvp514x_read_reg(sd, reg); - v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); -} - -/** - * tvp514x_write_reg() - Write a value to a register in TVP5146/47 - * @sd: ptr to v4l2_subdev struct - * @reg: TVP5146/47 register address - * @val: value to be written to the register - * - * Write a value to a register in an TVP5146/47 decoder device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - int err, retry = 0; - struct i2c_client *client = v4l2_get_subdevdata(sd); - -write_again: - - err = i2c_smbus_write_byte_data(client, reg, val); - if (err) { - if (retry <= I2C_RETRY_COUNT) { - v4l2_warn(sd, "Write: retry ... %d\n", retry); - retry++; - msleep_interruptible(10); - goto write_again; - } - } - - return err; -} - -/** - * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers - * @sd: ptr to v4l2_subdev struct - * @reglist: list of TVP5146/47 registers and values - * - * Initializes a list of TVP5146/47 registers:- - * if token is TOK_TERM, then entire write operation terminates - * if token is TOK_DELAY, then a delay of 'val' msec is introduced - * if token is TOK_SKIP, then the register write is skipped - * if token is TOK_WRITE, then the register write is performed - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp514x_write_regs(struct v4l2_subdev *sd, - const struct tvp514x_reg reglist[]) -{ - int err; - const struct tvp514x_reg *next = reglist; - - for (; next->token != TOK_TERM; next++) { - if (next->token == TOK_DELAY) { - msleep(next->val); - continue; - } - - if (next->token == TOK_SKIP) - continue; - - err = tvp514x_write_reg(sd, next->reg, (u8) next->val); - if (err) { - v4l2_err(sd, "Write failed. Err[%d]\n", err); - return err; - } - } - return 0; -} - -/** - * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47 - * @sd: ptr to v4l2_subdev struct - * - * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no - * standard detected. - */ -static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd) -{ - u8 std, std_status; - - std = tvp514x_read_reg(sd, REG_VIDEO_STD); - if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) - /* use the standard status register */ - std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); - else - /* use the standard register itself */ - std_status = std; - - switch (std_status & VIDEO_STD_MASK) { - case VIDEO_STD_NTSC_MJ_BIT: - return STD_NTSC_MJ; - - case VIDEO_STD_PAL_BDGHIN_BIT: - return STD_PAL_BDGHIN; - - default: - return STD_INVALID; - } - - return STD_INVALID; -} - -/* TVP5146/47 register dump function */ -static void tvp514x_reg_dump(struct v4l2_subdev *sd) -{ - dump_reg(sd, REG_INPUT_SEL); - dump_reg(sd, REG_AFE_GAIN_CTRL); - dump_reg(sd, REG_VIDEO_STD); - dump_reg(sd, REG_OPERATION_MODE); - dump_reg(sd, REG_COLOR_KILLER); - dump_reg(sd, REG_LUMA_CONTROL1); - dump_reg(sd, REG_LUMA_CONTROL2); - dump_reg(sd, REG_LUMA_CONTROL3); - dump_reg(sd, REG_BRIGHTNESS); - dump_reg(sd, REG_CONTRAST); - dump_reg(sd, REG_SATURATION); - dump_reg(sd, REG_HUE); - dump_reg(sd, REG_CHROMA_CONTROL1); - dump_reg(sd, REG_CHROMA_CONTROL2); - dump_reg(sd, REG_COMP_PR_SATURATION); - dump_reg(sd, REG_COMP_Y_CONTRAST); - dump_reg(sd, REG_COMP_PB_SATURATION); - dump_reg(sd, REG_COMP_Y_BRIGHTNESS); - dump_reg(sd, REG_AVID_START_PIXEL_LSB); - dump_reg(sd, REG_AVID_START_PIXEL_MSB); - dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); - dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); - dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); - dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); - dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); - dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); - dump_reg(sd, REG_VSYNC_START_LINE_LSB); - dump_reg(sd, REG_VSYNC_START_LINE_MSB); - dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); - dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); - dump_reg(sd, REG_VBLK_START_LINE_LSB); - dump_reg(sd, REG_VBLK_START_LINE_MSB); - dump_reg(sd, REG_VBLK_STOP_LINE_LSB); - dump_reg(sd, REG_VBLK_STOP_LINE_MSB); - dump_reg(sd, REG_SYNC_CONTROL); - dump_reg(sd, REG_OUTPUT_FORMATTER1); - dump_reg(sd, REG_OUTPUT_FORMATTER2); - dump_reg(sd, REG_OUTPUT_FORMATTER3); - dump_reg(sd, REG_OUTPUT_FORMATTER4); - dump_reg(sd, REG_OUTPUT_FORMATTER5); - dump_reg(sd, REG_OUTPUT_FORMATTER6); - dump_reg(sd, REG_CLEAR_LOST_LOCK); -} - -/** - * tvp514x_configure() - Configure the TVP5146/47 registers - * @sd: ptr to v4l2_subdev struct - * @decoder: ptr to tvp514x_decoder structure - * - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp514x_configure(struct v4l2_subdev *sd, - struct tvp514x_decoder *decoder) -{ - int err; - - /* common register initialization */ - err = - tvp514x_write_regs(sd, decoder->tvp514x_regs); - if (err) - return err; - - if (debug) - tvp514x_reg_dump(sd); - - return 0; -} - -/** - * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. - * @sd: pointer to standard V4L2 sub-device structure - * @decoder: pointer to tvp514x_decoder structure - * - * A device is considered to be detected if the chip ID (LSB and MSB) - * registers match the expected values. - * Any value of the rom version register is accepted. - * Returns ENODEV error number if no device is detected, or zero - * if a device is detected. - */ -static int tvp514x_detect(struct v4l2_subdev *sd, - struct tvp514x_decoder *decoder) -{ - u8 chip_id_msb, chip_id_lsb, rom_ver; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); - chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); - rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); - - v4l2_dbg(1, debug, sd, - "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", - chip_id_msb, chip_id_lsb, rom_ver); - if ((chip_id_msb != TVP514X_CHIP_ID_MSB) - || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) - && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { - /* We didn't read the values we expected, so this must not be - * an TVP5146/47. - */ - v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", - chip_id_msb, chip_id_lsb); - return -ENODEV; - } - - decoder->ver = rom_ver; - - v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", - client->name, decoder->ver, - client->addr << 1, client->adapter->name); - return 0; -} - -/** - * tvp514x_querystd() - V4L2 decoder interface handler for querystd - * @sd: pointer to standard V4L2 sub-device structure - * @std_id: standard V4L2 std_id ioctl enum - * - * Returns the current standard detected by TVP5146/47. If no active input is - * detected then *std_id is set to 0 and the function returns 0. - */ -static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - enum tvp514x_std current_std; - enum tvp514x_input input_sel; - u8 sync_lock_status, lock_mask; - - if (std_id == NULL) - return -EINVAL; - - *std_id = V4L2_STD_UNKNOWN; - - /* query the current standard */ - current_std = tvp514x_query_current_std(sd); - if (current_std == STD_INVALID) - return 0; - - input_sel = decoder->input; - - switch (input_sel) { - case INPUT_CVBS_VI1A: - case INPUT_CVBS_VI1B: - case INPUT_CVBS_VI1C: - case INPUT_CVBS_VI2A: - case INPUT_CVBS_VI2B: - case INPUT_CVBS_VI2C: - case INPUT_CVBS_VI3A: - case INPUT_CVBS_VI3B: - case INPUT_CVBS_VI3C: - case INPUT_CVBS_VI4A: - lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | - STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - - case INPUT_SVIDEO_VI2A_VI1A: - case INPUT_SVIDEO_VI2B_VI1B: - case INPUT_SVIDEO_VI2C_VI1C: - case INPUT_SVIDEO_VI2A_VI3A: - case INPUT_SVIDEO_VI2B_VI3B: - case INPUT_SVIDEO_VI2C_VI3C: - case INPUT_SVIDEO_VI4A_VI1A: - case INPUT_SVIDEO_VI4A_VI1B: - case INPUT_SVIDEO_VI4A_VI1C: - case INPUT_SVIDEO_VI4A_VI3A: - case INPUT_SVIDEO_VI4A_VI3B: - case INPUT_SVIDEO_VI4A_VI3C: - lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - /*Need to add other interfaces*/ - default: - return -EINVAL; - } - /* check whether signal is locked */ - sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); - if (lock_mask != (sync_lock_status & lock_mask)) - return 0; /* No input detected */ - - *std_id = decoder->std_list[current_std].standard.id; - - v4l2_dbg(1, debug, sd, "Current STD: %s\n", - decoder->std_list[current_std].standard.name); - return 0; -} - -/** - * tvp514x_s_std() - V4L2 decoder interface handler for s_std - * @sd: pointer to standard V4L2 sub-device structure - * @std_id: standard V4L2 v4l2_std_id ioctl enum - * - * If std_id is supported, sets the requested standard. Otherwise, returns - * -EINVAL - */ -static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - int err, i; - - for (i = 0; i < decoder->num_stds; i++) - if (std_id & decoder->std_list[i].standard.id) - break; - - if ((i == decoder->num_stds) || (i == STD_INVALID)) - return -EINVAL; - - err = tvp514x_write_reg(sd, REG_VIDEO_STD, - decoder->std_list[i].video_std); - if (err) - return err; - - decoder->current_std = i; - decoder->tvp514x_regs[REG_VIDEO_STD].val = - decoder->std_list[i].video_std; - - v4l2_dbg(1, debug, sd, "Standard set to: %s\n", - decoder->std_list[i].standard.name); - return 0; -} - -/** - * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing - * @sd: pointer to standard V4L2 sub-device structure - * @input: input selector for routing the signal - * @output: output selector for routing the signal - * @config: config value. Not used - * - * If index is valid, selects the requested input. Otherwise, returns -EINVAL if - * the input is not supported or there is no active signal present in the - * selected input. - */ -static int tvp514x_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - int err; - enum tvp514x_input input_sel; - enum tvp514x_output output_sel; - u8 sync_lock_status, lock_mask; - int try_count = LOCK_RETRY_COUNT; - - if ((input >= INPUT_INVALID) || - (output >= OUTPUT_INVALID)) - /* Index out of bound */ - return -EINVAL; - - /* - * For the sequence streamon -> streamoff and again s_input - * it fails to lock the signal, since streamoff puts TVP514x - * into power off state which leads to failure in sub-sequent s_input. - * - * So power up the TVP514x device here, since it is important to lock - * the signal at this stage. - */ - if (!decoder->streaming) - tvp514x_s_stream(sd, 1); - - input_sel = input; - output_sel = output; - - err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); - if (err) - return err; - - output_sel |= tvp514x_read_reg(sd, - REG_OUTPUT_FORMATTER1) & 0x7; - err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, - output_sel); - if (err) - return err; - - decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; - decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; - - /* Clear status */ - msleep(LOCK_RETRY_DELAY); - err = - tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01); - if (err) - return err; - - switch (input_sel) { - case INPUT_CVBS_VI1A: - case INPUT_CVBS_VI1B: - case INPUT_CVBS_VI1C: - case INPUT_CVBS_VI2A: - case INPUT_CVBS_VI2B: - case INPUT_CVBS_VI2C: - case INPUT_CVBS_VI3A: - case INPUT_CVBS_VI3B: - case INPUT_CVBS_VI3C: - case INPUT_CVBS_VI4A: - lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | - STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - - case INPUT_SVIDEO_VI2A_VI1A: - case INPUT_SVIDEO_VI2B_VI1B: - case INPUT_SVIDEO_VI2C_VI1C: - case INPUT_SVIDEO_VI2A_VI3A: - case INPUT_SVIDEO_VI2B_VI3B: - case INPUT_SVIDEO_VI2C_VI3C: - case INPUT_SVIDEO_VI4A_VI1A: - case INPUT_SVIDEO_VI4A_VI1B: - case INPUT_SVIDEO_VI4A_VI1C: - case INPUT_SVIDEO_VI4A_VI3A: - case INPUT_SVIDEO_VI4A_VI3B: - case INPUT_SVIDEO_VI4A_VI3C: - lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | - STATUS_VIRT_SYNC_LOCK_BIT; - break; - /* Need to add other interfaces*/ - default: - return -EINVAL; - } - - while (try_count-- > 0) { - /* Allow decoder to sync up with new input */ - msleep(LOCK_RETRY_DELAY); - - sync_lock_status = tvp514x_read_reg(sd, - REG_STATUS1); - if (lock_mask == (sync_lock_status & lock_mask)) - /* Input detected */ - break; - } - - if (try_count < 0) - return -EINVAL; - - decoder->input = input; - decoder->output = output; - - v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel); - - return 0; -} - -/** - * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl - * @ctrl: pointer to v4l2_ctrl structure - * - * If the requested control is supported, sets the control's current - * value in HW. Otherwise, returns -EINVAL if the control is not supported. - */ -static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct tvp514x_decoder *decoder = to_decoder(sd); - int err = -EINVAL, value; - - value = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); - if (!err) - decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; - break; - case V4L2_CID_CONTRAST: - err = tvp514x_write_reg(sd, REG_CONTRAST, value); - if (!err) - decoder->tvp514x_regs[REG_CONTRAST].val = value; - break; - case V4L2_CID_SATURATION: - err = tvp514x_write_reg(sd, REG_SATURATION, value); - if (!err) - decoder->tvp514x_regs[REG_SATURATION].val = value; - break; - case V4L2_CID_HUE: - if (value == 180) - value = 0x7F; - else if (value == -180) - value = 0x80; - err = tvp514x_write_reg(sd, REG_HUE, value); - if (!err) - decoder->tvp514x_regs[REG_HUE].val = value; - break; - case V4L2_CID_AUTOGAIN: - err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c); - if (!err) - decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; - break; - } - - v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n", - ctrl->id, ctrl->val); - return err; -} - -/** - * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @index: index of pixelcode to retrieve - * @code: receives the pixelcode - * - * Enumerates supported mediabus formats - */ -static int -tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index) - return -EINVAL; - - *code = V4L2_MBUS_FMT_YUYV10_2X10; - return 0; -} - -/** - * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @f: pointer to the mediabus format structure - * - * Negotiates the image capture size and mediabus format. - */ -static int -tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - enum tvp514x_std current_std; - - if (f == NULL) - return -EINVAL; - - /* Calculate height and width based on current standard */ - current_std = decoder->current_std; - - f->code = V4L2_MBUS_FMT_YUYV10_2X10; - f->width = decoder->std_list[current_std].width; - f->height = decoder->std_list[current_std].height; - f->field = V4L2_FIELD_INTERLACED; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; - - v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n", - f->width, f->height); - return 0; -} - -/** - * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm - * @sd: pointer to standard V4L2 sub-device structure - * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure - * - * Returns the decoder's video CAPTURE parameters. - */ -static int -tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - struct v4l2_captureparm *cparm; - enum tvp514x_std current_std; - - if (a == NULL) - return -EINVAL; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - /* only capture is supported */ - return -EINVAL; - - /* get the current standard */ - current_std = decoder->current_std; - - cparm = &a->parm.capture; - cparm->capability = V4L2_CAP_TIMEPERFRAME; - cparm->timeperframe = - decoder->std_list[current_std].standard.frameperiod; - - return 0; -} - -/** - * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm - * @sd: pointer to standard V4L2 sub-device structure - * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure - * - * Configures the decoder to use the input parameters, if possible. If - * not possible, returns the appropriate error code. - */ -static int -tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - struct v4l2_fract *timeperframe; - enum tvp514x_std current_std; - - if (a == NULL) - return -EINVAL; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - /* only capture is supported */ - return -EINVAL; - - timeperframe = &a->parm.capture.timeperframe; - - /* get the current standard */ - current_std = decoder->current_std; - - *timeperframe = - decoder->std_list[current_std].standard.frameperiod; - - return 0; -} - -/** - * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream - * @sd: pointer to standard V4L2 sub-device structure - * @enable: streaming enable or disable - * - * Sets streaming to enable or disable, if possible. - */ -static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) -{ - int err = 0; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tvp514x_decoder *decoder = to_decoder(sd); - - if (decoder->streaming == enable) - return 0; - - switch (enable) { - case 0: - { - /* Power Down Sequence */ - err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); - if (err) { - v4l2_err(sd, "Unable to turn off decoder\n"); - return err; - } - decoder->streaming = enable; - break; - } - case 1: - { - struct tvp514x_reg *int_seq = (struct tvp514x_reg *) - client->driver->id_table->driver_data; - - /* Power Up Sequence */ - err = tvp514x_write_regs(sd, int_seq); - if (err) { - v4l2_err(sd, "Unable to turn on decoder\n"); - return err; - } - /* Detect if not already detected */ - err = tvp514x_detect(sd, decoder); - if (err) { - v4l2_err(sd, "Unable to detect decoder\n"); - return err; - } - err = tvp514x_configure(sd, decoder); - if (err) { - v4l2_err(sd, "Unable to configure decoder\n"); - return err; - } - decoder->streaming = enable; - break; - } - default: - err = -ENODEV; - break; - } - - return err; -} - -static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { - .s_ctrl = tvp514x_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops tvp514x_core_ops = { - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = tvp514x_s_std, -}; - -static const struct v4l2_subdev_video_ops tvp514x_video_ops = { - .s_routing = tvp514x_s_routing, - .querystd = tvp514x_querystd, - .enum_mbus_fmt = tvp514x_enum_mbus_fmt, - .g_mbus_fmt = tvp514x_mbus_fmt, - .try_mbus_fmt = tvp514x_mbus_fmt, - .s_mbus_fmt = tvp514x_mbus_fmt, - .g_parm = tvp514x_g_parm, - .s_parm = tvp514x_s_parm, - .s_stream = tvp514x_s_stream, -}; - -static const struct v4l2_subdev_ops tvp514x_ops = { - .core = &tvp514x_core_ops, - .video = &tvp514x_video_ops, -}; - -static struct tvp514x_decoder tvp514x_dev = { - .streaming = 0, - .current_std = STD_NTSC_MJ, - .std_list = tvp514x_std_list, - .num_stds = ARRAY_SIZE(tvp514x_std_list), - -}; - -/** - * tvp514x_probe() - decoder driver i2c probe handler - * @client: i2c driver client device structure - * @id: i2c driver id table - * - * Register decoder as an i2c client device and V4L2 - * device. - */ -static int -tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct tvp514x_decoder *decoder; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - if (!client->dev.platform_data) { - v4l2_err(client, "No platform data!!\n"); - return -ENODEV; - } - - decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); - if (!decoder) - return -ENOMEM; - - /* Initialize the tvp514x_decoder with default configuration */ - *decoder = tvp514x_dev; - /* Copy default register configuration */ - memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, - sizeof(tvp514x_reg_list_default)); - - /* Copy board specific information here */ - decoder->pdata = client->dev.platform_data; - - /** - * Fetch platform specific data, and configure the - * tvp514x_reg_list[] accordingly. Since this is one - * time configuration, no need to preserve. - */ - decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= - (decoder->pdata->clk_polarity << 1); - decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= - ((decoder->pdata->hs_polarity << 2) | - (decoder->pdata->vs_polarity << 3)); - /* Set default standard to auto */ - decoder->tvp514x_regs[REG_VIDEO_STD].val = - VIDEO_STD_AUTO_SWITCH_BIT; - - /* Register with V4L2 layer as slave device */ - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); - - v4l2_ctrl_handler_init(&decoder->hdl, 5); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_HUE, -180, 180, 180, 0); - v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - - v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); - - return 0; - -} - -/** - * tvp514x_remove() - decoder driver i2c remove handler - * @client: i2c driver client device structure - * - * Unregister decoder as an i2c client device and V4L2 - * device. Complement of tvp514x_probe(). - */ -static int tvp514x_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct tvp514x_decoder *decoder = to_decoder(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} -/* TVP5146 Init/Power on Sequence */ -static const struct tvp514x_reg tvp5146_init_reg_seq[] = { - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, - {TOK_WRITE, REG_OPERATION_MODE, 0x01}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_TERM, 0, 0}, -}; - -/* TVP5147 Init/Power on Sequence */ -static const struct tvp514x_reg tvp5147_init_reg_seq[] = { - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, - {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, - {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, - {TOK_WRITE, REG_OPERATION_MODE, 0x01}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_TERM, 0, 0}, -}; - -/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ -static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { - {TOK_WRITE, REG_OPERATION_MODE, 0x01}, - {TOK_WRITE, REG_OPERATION_MODE, 0x00}, - {TOK_TERM, 0, 0}, -}; - -/** - * I2C Device Table - - * - * name - Name of the actual device/chip. - * driver_data - Driver data - */ -static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, - {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, - {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, - {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, tvp514x_id); - -static struct i2c_driver tvp514x_driver = { - .driver = { - .owner = THIS_MODULE, - .name = TVP514X_MODULE_NAME, - }, - .probe = tvp514x_probe, - .remove = tvp514x_remove, - .id_table = tvp514x_id, -}; - -module_i2c_driver(tvp514x_driver); diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h deleted file mode 100644 index 18f29ad0dfe2..000000000000 --- a/drivers/media/video/tvp514x_regs.h +++ /dev/null @@ -1,287 +0,0 @@ -/* - * drivers/media/video/tvp514x_regs.h - * - * Copyright (C) 2008 Texas Instruments Inc - * Author: Vaibhav Hiremath - * - * Contributors: - * Sivaraj R - * Brijesh R Jadav - * Hardik Shah - * Manjunath Hadli - * Karicheri Muralidharan - * - * This package is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#ifndef _TVP514X_REGS_H -#define _TVP514X_REGS_H - -/* - * TVP5146/47 registers - */ -#define REG_INPUT_SEL (0x00) -#define REG_AFE_GAIN_CTRL (0x01) -#define REG_VIDEO_STD (0x02) -#define REG_OPERATION_MODE (0x03) -#define REG_AUTOSWITCH_MASK (0x04) - -#define REG_COLOR_KILLER (0x05) -#define REG_LUMA_CONTROL1 (0x06) -#define REG_LUMA_CONTROL2 (0x07) -#define REG_LUMA_CONTROL3 (0x08) - -#define REG_BRIGHTNESS (0x09) -#define REG_CONTRAST (0x0A) -#define REG_SATURATION (0x0B) -#define REG_HUE (0x0C) - -#define REG_CHROMA_CONTROL1 (0x0D) -#define REG_CHROMA_CONTROL2 (0x0E) - -/* 0x0F Reserved */ - -#define REG_COMP_PR_SATURATION (0x10) -#define REG_COMP_Y_CONTRAST (0x11) -#define REG_COMP_PB_SATURATION (0x12) - -/* 0x13 Reserved */ - -#define REG_COMP_Y_BRIGHTNESS (0x14) - -/* 0x15 Reserved */ - -#define REG_AVID_START_PIXEL_LSB (0x16) -#define REG_AVID_START_PIXEL_MSB (0x17) -#define REG_AVID_STOP_PIXEL_LSB (0x18) -#define REG_AVID_STOP_PIXEL_MSB (0x19) - -#define REG_HSYNC_START_PIXEL_LSB (0x1A) -#define REG_HSYNC_START_PIXEL_MSB (0x1B) -#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) -#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) - -#define REG_VSYNC_START_LINE_LSB (0x1E) -#define REG_VSYNC_START_LINE_MSB (0x1F) -#define REG_VSYNC_STOP_LINE_LSB (0x20) -#define REG_VSYNC_STOP_LINE_MSB (0x21) - -#define REG_VBLK_START_LINE_LSB (0x22) -#define REG_VBLK_START_LINE_MSB (0x23) -#define REG_VBLK_STOP_LINE_LSB (0x24) -#define REG_VBLK_STOP_LINE_MSB (0x25) - -/* 0x26 - 0x27 Reserved */ - -#define REG_FAST_SWTICH_CONTROL (0x28) - -/* 0x29 Reserved */ - -#define REG_FAST_SWTICH_SCART_DELAY (0x2A) - -/* 0x2B Reserved */ - -#define REG_SCART_DELAY (0x2C) -#define REG_CTI_DELAY (0x2D) -#define REG_CTI_CONTROL (0x2E) - -/* 0x2F - 0x31 Reserved */ - -#define REG_SYNC_CONTROL (0x32) -#define REG_OUTPUT_FORMATTER1 (0x33) -#define REG_OUTPUT_FORMATTER2 (0x34) -#define REG_OUTPUT_FORMATTER3 (0x35) -#define REG_OUTPUT_FORMATTER4 (0x36) -#define REG_OUTPUT_FORMATTER5 (0x37) -#define REG_OUTPUT_FORMATTER6 (0x38) -#define REG_CLEAR_LOST_LOCK (0x39) - -#define REG_STATUS1 (0x3A) -#define REG_STATUS2 (0x3B) - -#define REG_AGC_GAIN_STATUS_LSB (0x3C) -#define REG_AGC_GAIN_STATUS_MSB (0x3D) - -/* 0x3E Reserved */ - -#define REG_VIDEO_STD_STATUS (0x3F) -#define REG_GPIO_INPUT1 (0x40) -#define REG_GPIO_INPUT2 (0x41) - -/* 0x42 - 0x45 Reserved */ - -#define REG_AFE_COARSE_GAIN_CH1 (0x46) -#define REG_AFE_COARSE_GAIN_CH2 (0x47) -#define REG_AFE_COARSE_GAIN_CH3 (0x48) -#define REG_AFE_COARSE_GAIN_CH4 (0x49) - -#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) -#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) -#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) -#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) -#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) -#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) -#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) -#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) - -/* 0x52 - 0x68 Reserved */ - -#define REG_FBIT_VBIT_CONTROL1 (0x69) - -/* 0x6A - 0x6B Reserved */ - -#define REG_BACKEND_AGC_CONTROL (0x6C) - -/* 0x6D - 0x6E Reserved */ - -#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) -#define REG_ROM_VERSION (0x70) - -/* 0x71 - 0x73 Reserved */ - -#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) -#define REG_FBIT_VBIT_CONTROL2 (0x75) -#define REG_VCR_TRICK_MODE_CONTROL (0x76) -#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) -#define REG_AGC_INCREMENT_SPEED (0x78) -#define REG_AGC_INCREMENT_DELAY (0x79) - -/* 0x7A - 0x7F Reserved */ - -#define REG_CHIP_ID_MSB (0x80) -#define REG_CHIP_ID_LSB (0x81) - -/* 0x82 Reserved */ - -#define REG_CPLL_SPEED_CONTROL (0x83) - -/* 0x84 - 0x96 Reserved */ - -#define REG_STATUS_REQUEST (0x97) - -/* 0x98 - 0x99 Reserved */ - -#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) -#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) - -/* 0x9C - 0x9D Reserved */ - -#define REG_AGC_DECREMENT_DELAY (0x9E) - -/* 0x9F - 0xB0 Reserved */ - -#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) -#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) -#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) -#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) -#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) -#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) -#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) -#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) -#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) -#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) -#define REG_VDP_TTX_FILTER_CONTROL (0xBB) -#define REG_VDP_FIFO_WORD_COUNT (0xBC) -#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) - -/* 0xBE Reserved */ - -#define REG_VDP_FIFO_RESET (0xBF) -#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) -#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) -#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) -#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) - -/* 0xC4 - 0xD5 Reserved */ - -#define REG_VDP_LINE_START (0xD6) -#define REG_VDP_LINE_STOP (0xD7) -#define REG_VDP_GLOBAL_LINE_MODE (0xD8) -#define REG_VDP_FULL_FIELD_ENABLE (0xD9) -#define REG_VDP_FULL_FIELD_MODE (0xDA) - -/* 0xDB - 0xDF Reserved */ - -#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) -#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) -#define REG_FIFO_READ_DATA (0xE2) - -/* 0xE3 - 0xE7 Reserved */ - -#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) -#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) -#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) - -/* 0xEB - 0xEF Reserved */ - -#define REG_INTERRUPT_RAW_STATUS0 (0xF0) -#define REG_INTERRUPT_RAW_STATUS1 (0xF1) -#define REG_INTERRUPT_STATUS0 (0xF2) -#define REG_INTERRUPT_STATUS1 (0xF3) -#define REG_INTERRUPT_MASK0 (0xF4) -#define REG_INTERRUPT_MASK1 (0xF5) -#define REG_INTERRUPT_CLEAR0 (0xF6) -#define REG_INTERRUPT_CLEAR1 (0xF7) - -/* 0xF8 - 0xFF Reserved */ - -/* - * Mask and bit definitions of TVP5146/47 registers - */ -/* The ID values we are looking for */ -#define TVP514X_CHIP_ID_MSB (0x51) -#define TVP5146_CHIP_ID_LSB (0x46) -#define TVP5147_CHIP_ID_LSB (0x47) - -#define VIDEO_STD_MASK (0x07) -#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) -#define VIDEO_STD_NTSC_MJ_BIT (0x01) -#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) -#define VIDEO_STD_PAL_M_BIT (0x03) -#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) -#define VIDEO_STD_NTSC_4_43_BIT (0x05) -#define VIDEO_STD_SECAM_BIT (0x06) -#define VIDEO_STD_PAL_60_BIT (0x07) - -/* - * Status bit - */ -#define STATUS_TV_VCR_BIT (1<<0) -#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) -#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) -#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) -#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) -#define STATUS_FEILD_RATE_BIT (1<<5) -#define STATUS_LINE_ALTERNATING_BIT (1<<6) -#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) - -/* Tokens for register write */ -#define TOK_WRITE (0) /* token for write operation */ -#define TOK_TERM (1) /* terminating token */ -#define TOK_DELAY (2) /* delay token for reg list */ -#define TOK_SKIP (3) /* token to skip a register */ -/** - * struct tvp514x_reg - Structure for TVP5146/47 register initialization values - * @token - Token: TOK_WRITE, TOK_TERM etc.. - * @reg - Register offset - * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY - */ -struct tvp514x_reg { - u8 token; - u8 reg; - u32 val; -}; - -#endif /* ifndef _TVP514X_REGS_H */ diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c deleted file mode 100644 index a751b6c146fd..000000000000 --- a/drivers/media/video/tvp5150.c +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver - * - * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) - * This code is placed under the terms of the GNU General Public License v2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tvp5150_reg.h" - -#define TVP5150_H_MAX 720 -#define TVP5150_V_MAX_525_60 480 -#define TVP5150_V_MAX_OTHERS 576 -#define TVP5150_MAX_CROP_LEFT 511 -#define TVP5150_MAX_CROP_TOP 127 -#define TVP5150_CROP_SHIFT 2 - -MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL"); - - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -struct tvp5150 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct v4l2_rect rect; - - v4l2_std_id norm; /* Current set standard */ - u32 input; - u32 output; - int enable; -}; - -static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tvp5150, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tvp5150, hdl)->sd; -} - -static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[1]; - int rc; - - buffer[0] = addr; - - rc = i2c_master_send(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; - } - - msleep(10); - - rc = i2c_master_recv(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; - } - - v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); - - return (buffer[0]); -} - -static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, - unsigned char value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - int rc; - - buffer[0] = addr; - buffer[1] = value; - v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); - if (2 != (rc = i2c_master_send(c, buffer, 2))) - v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); -} - -static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, - const u8 end, int max_line) -{ - int i = 0; - - while (init != (u8)(end + 1)) { - if ((i % max_line) == 0) { - if (i > 0) - printk("\n"); - printk("tvp5150: %s reg 0x%02x = ", s, init); - } - printk("%02x ", tvp5150_read(sd, init)); - - init++; - i++; - } - printk("\n"); -} - -static int tvp5150_log_status(struct v4l2_subdev *sd) -{ - printk("tvp5150: Video input source selection #1 = 0x%02x\n", - tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); - printk("tvp5150: Analog channel controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); - printk("tvp5150: Operation mode controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_OP_MODE_CTL)); - printk("tvp5150: Miscellaneous controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_MISC_CTL)); - printk("tvp5150: Autoswitch mask= 0x%02x\n", - tvp5150_read(sd, TVP5150_AUTOSW_MSK)); - printk("tvp5150: Color killer threshold control = 0x%02x\n", - tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); - printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); - printk("tvp5150: Brightness control = 0x%02x\n", - tvp5150_read(sd, TVP5150_BRIGHT_CTL)); - printk("tvp5150: Color saturation control = 0x%02x\n", - tvp5150_read(sd, TVP5150_SATURATION_CTL)); - printk("tvp5150: Hue control = 0x%02x\n", - tvp5150_read(sd, TVP5150_HUE_CTL)); - printk("tvp5150: Contrast control = 0x%02x\n", - tvp5150_read(sd, TVP5150_CONTRAST_CTL)); - printk("tvp5150: Outputs and data rates select = 0x%02x\n", - tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); - printk("tvp5150: Configuration shared pins = 0x%02x\n", - tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); - printk("tvp5150: Active video cropping start = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), - tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); - printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), - tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); - printk("tvp5150: Genlock/RTC = 0x%02x\n", - tvp5150_read(sd, TVP5150_GENLOCK)); - printk("tvp5150: Horizontal sync start = 0x%02x\n", - tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); - printk("tvp5150: Vertical blanking start = 0x%02x\n", - tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); - printk("tvp5150: Vertical blanking stop = 0x%02x\n", - tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); - printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", - tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), - tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); - printk("tvp5150: Interrupt reset register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); - printk("tvp5150: Interrupt enable register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); - printk("tvp5150: Interrupt configuration register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); - printk("tvp5150: Video standard = 0x%02x\n", - tvp5150_read(sd, TVP5150_VIDEO_STD)); - printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", - tvp5150_read(sd, TVP5150_CB_GAIN_FACT), - tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); - printk("tvp5150: Macrovision on counter = 0x%02x\n", - tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); - printk("tvp5150: Macrovision off counter = 0x%02x\n", - tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); - printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", - (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); - printk("tvp5150: Device ID = %02x%02x\n", - tvp5150_read(sd, TVP5150_MSB_DEV_ID), - tvp5150_read(sd, TVP5150_LSB_DEV_ID)); - printk("tvp5150: ROM version = (hex) %02x.%02x\n", - tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), - tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); - printk("tvp5150: Vertical line count = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), - tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); - printk("tvp5150: Interrupt status register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); - printk("tvp5150: Interrupt active register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); - printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", - tvp5150_read(sd, TVP5150_STATUS_REG_1), - tvp5150_read(sd, TVP5150_STATUS_REG_2), - tvp5150_read(sd, TVP5150_STATUS_REG_3), - tvp5150_read(sd, TVP5150_STATUS_REG_4), - tvp5150_read(sd, TVP5150_STATUS_REG_5)); - - dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, - TVP5150_TELETEXT_FIL1_END, 8); - dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, - TVP5150_TELETEXT_FIL2_END, 8); - - printk("tvp5150: Teletext filter enable = 0x%02x\n", - tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); - printk("tvp5150: Interrupt status register A = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); - printk("tvp5150: Interrupt enable register A = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); - printk("tvp5150: Interrupt configuration = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_CONF)); - printk("tvp5150: VDP status register = 0x%02x\n", - tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); - printk("tvp5150: FIFO word count = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); - printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); - printk("tvp5150: FIFO reset = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_RESET)); - printk("tvp5150: Line number interrupt = 0x%02x\n", - tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); - printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), - tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); - printk("tvp5150: FIFO output control = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); - printk("tvp5150: Full field enable = 0x%02x\n", - tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); - printk("tvp5150: Full field mode register = 0x%02x\n", - tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); - - dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, - TVP5150_CC_DATA_END, 8); - - dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI, - TVP5150_WSS_DATA_END, 8); - - dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI, - TVP5150_VPS_DATA_END, 8); - - dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI, - TVP5150_VITC_DATA_END, 10); - - dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI, - TVP5150_LINE_MODE_END, 8); - return 0; -} - -/**************************************************************************** - Basic functions - ****************************************************************************/ - -static inline void tvp5150_selmux(struct v4l2_subdev *sd) -{ - int opmode = 0; - struct tvp5150 *decoder = to_tvp5150(sd); - int input = 0; - int val; - - if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) - input = 8; - - switch (decoder->input) { - case TVP5150_COMPOSITE1: - input |= 2; - /* fall through */ - case TVP5150_COMPOSITE0: - break; - case TVP5150_SVIDEO: - default: - input |= 1; - break; - } - - v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " - "=> tvp5150 input=%i, opmode=%i\n", - decoder->input, decoder->output, - input, opmode); - - tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); - tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); - - /* Svideo should enable YCrCb output and disable GPCL output - * For Composite and TV, it should be the reverse - */ - val = tvp5150_read(sd, TVP5150_MISC_CTL); - if (val < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", __func__, val); - return; - } - - if (decoder->input == TVP5150_SVIDEO) - val = (val & ~0x40) | 0x10; - else - val = (val & ~0x10) | 0x40; - tvp5150_write(sd, TVP5150_MISC_CTL, val); -}; - -struct i2c_reg_value { - unsigned char reg; - unsigned char value; -}; - -/* Default values as sugested at TVP5150AM1 datasheet */ -static const struct i2c_reg_value tvp5150_init_default[] = { - { /* 0x00 */ - TVP5150_VD_IN_SRC_SEL_1,0x00 - }, - { /* 0x01 */ - TVP5150_ANAL_CHL_CTL,0x15 - }, - { /* 0x02 */ - TVP5150_OP_MODE_CTL,0x00 - }, - { /* 0x03 */ - TVP5150_MISC_CTL,0x01 - }, - { /* 0x06 */ - TVP5150_COLOR_KIL_THSH_CTL,0x10 - }, - { /* 0x07 */ - TVP5150_LUMA_PROC_CTL_1,0x60 - }, - { /* 0x08 */ - TVP5150_LUMA_PROC_CTL_2,0x00 - }, - { /* 0x09 */ - TVP5150_BRIGHT_CTL,0x80 - }, - { /* 0x0a */ - TVP5150_SATURATION_CTL,0x80 - }, - { /* 0x0b */ - TVP5150_HUE_CTL,0x00 - }, - { /* 0x0c */ - TVP5150_CONTRAST_CTL,0x80 - }, - { /* 0x0d */ - TVP5150_DATA_RATE_SEL,0x47 - }, - { /* 0x0e */ - TVP5150_LUMA_PROC_CTL_3,0x00 - }, - { /* 0x0f */ - TVP5150_CONF_SHARED_PIN,0x08 - }, - { /* 0x11 */ - TVP5150_ACT_VD_CROP_ST_MSB,0x00 - }, - { /* 0x12 */ - TVP5150_ACT_VD_CROP_ST_LSB,0x00 - }, - { /* 0x13 */ - TVP5150_ACT_VD_CROP_STP_MSB,0x00 - }, - { /* 0x14 */ - TVP5150_ACT_VD_CROP_STP_LSB,0x00 - }, - { /* 0x15 */ - TVP5150_GENLOCK,0x01 - }, - { /* 0x16 */ - TVP5150_HORIZ_SYNC_START,0x80 - }, - { /* 0x18 */ - TVP5150_VERT_BLANKING_START,0x00 - }, - { /* 0x19 */ - TVP5150_VERT_BLANKING_STOP,0x00 - }, - { /* 0x1a */ - TVP5150_CHROMA_PROC_CTL_1,0x0c - }, - { /* 0x1b */ - TVP5150_CHROMA_PROC_CTL_2,0x14 - }, - { /* 0x1c */ - TVP5150_INT_RESET_REG_B,0x00 - }, - { /* 0x1d */ - TVP5150_INT_ENABLE_REG_B,0x00 - }, - { /* 0x1e */ - TVP5150_INTT_CONFIG_REG_B,0x00 - }, - { /* 0x28 */ - TVP5150_VIDEO_STD,0x00 - }, - { /* 0x2e */ - TVP5150_MACROVISION_ON_CTR,0x0f - }, - { /* 0x2f */ - TVP5150_MACROVISION_OFF_CTR,0x01 - }, - { /* 0xbb */ - TVP5150_TELETEXT_FIL_ENA,0x00 - }, - { /* 0xc0 */ - TVP5150_INT_STATUS_REG_A,0x00 - }, - { /* 0xc1 */ - TVP5150_INT_ENABLE_REG_A,0x00 - }, - { /* 0xc2 */ - TVP5150_INT_CONF,0x04 - }, - { /* 0xc8 */ - TVP5150_FIFO_INT_THRESHOLD,0x80 - }, - { /* 0xc9 */ - TVP5150_FIFO_RESET,0x00 - }, - { /* 0xca */ - TVP5150_LINE_NUMBER_INT,0x00 - }, - { /* 0xcb */ - TVP5150_PIX_ALIGN_REG_LOW,0x4e - }, - { /* 0xcc */ - TVP5150_PIX_ALIGN_REG_HIGH,0x00 - }, - { /* 0xcd */ - TVP5150_FIFO_OUT_CTRL,0x01 - }, - { /* 0xcf */ - TVP5150_FULL_FIELD_ENA,0x00 - }, - { /* 0xd0 */ - TVP5150_LINE_MODE_INI,0x00 - }, - { /* 0xfc */ - TVP5150_FULL_FIELD_MODE_REG,0x7f - }, - { /* end of data */ - 0xff,0xff - } -}; - -/* Default values as sugested at TVP5150AM1 datasheet */ -static const struct i2c_reg_value tvp5150_init_enable[] = { - { - TVP5150_CONF_SHARED_PIN, 2 - },{ /* Automatic offset and AGC enabled */ - TVP5150_ANAL_CHL_CTL, 0x15 - },{ /* Activate YCrCb output 0x9 or 0xd ? */ - TVP5150_MISC_CTL, 0x6f - },{ /* Activates video std autodetection for all standards */ - TVP5150_AUTOSW_MSK, 0x0 - },{ /* Default format: 0x47. For 4:2:2: 0x40 */ - TVP5150_DATA_RATE_SEL, 0x47 - },{ - TVP5150_CHROMA_PROC_CTL_1, 0x0c - },{ - TVP5150_CHROMA_PROC_CTL_2, 0x54 - },{ /* Non documented, but initialized on WinTV USB2 */ - 0x27, 0x20 - },{ - 0xff,0xff - } -}; - -struct tvp5150_vbi_type { - unsigned int vbi_type; - unsigned int ini_line; - unsigned int end_line; - unsigned int by_field :1; -}; - -struct i2c_vbi_ram_value { - u16 reg; - struct tvp5150_vbi_type type; - unsigned char values[16]; -}; - -/* This struct have the values for each supported VBI Standard - * by - tvp5150_vbi_types should follow the same order as vbi_ram_default - * value 0 means rom position 0x10, value 1 means rom position 0x30 - * and so on. There are 16 possible locations from 0 to 15. - */ - -static struct i2c_vbi_ram_value vbi_ram_default[] = -{ - /* FIXME: Current api doesn't handle all VBI types, those not - yet supported are placed under #if 0 */ -#if 0 - {0x010, /* Teletext, SECAM, WST System A */ - {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, - 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, -#endif - {0x030, /* Teletext, PAL, WST System B */ - {V4L2_SLICED_TELETEXT_B,6,22,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, - 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, -#if 0 - {0x050, /* Teletext, PAL, WST System C */ - {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, - 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, - {0x070, /* Teletext, NTSC, WST System B */ - {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, - 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, - {0x090, /* Tetetext, NTSC NABTS System C */ - {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, - 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } - }, - {0x0b0, /* Teletext, NTSC-J, NABTS System D */ - {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, - { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, - 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } - }, - {0x0d0, /* Closed Caption, PAL/SECAM */ - {V4L2_SLICED_CAPTION_625,22,22,1}, - { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, - 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } - }, -#endif - {0x0f0, /* Closed Caption, NTSC */ - {V4L2_SLICED_CAPTION_525,21,21,1}, - { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, - 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } - }, - {0x110, /* Wide Screen Signal, PAL/SECAM */ - {V4L2_SLICED_WSS_625,23,23,1}, - { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, - 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } - }, -#if 0 - {0x130, /* Wide Screen Signal, NTSC C */ - {V4L2_SLICED_WSS_525,20,20,1}, - { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, - 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } - }, - {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ - {V4l2_SLICED_VITC_625,6,22,0}, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, - 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } - }, - {0x170, /* Vertical Interval Timecode (VITC), NTSC */ - {V4l2_SLICED_VITC_525,10,20,0}, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, - 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } - }, -#endif - {0x190, /* Video Program System (VPS), PAL */ - {V4L2_SLICED_VPS,16,16,0}, - { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, - 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } - }, - /* 0x1d0 User programmable */ - - /* End of struct */ - { (u16)-1 } -}; - -static int tvp5150_write_inittab(struct v4l2_subdev *sd, - const struct i2c_reg_value *regs) -{ - while (regs->reg != 0xff) { - tvp5150_write(sd, regs->reg, regs->value); - regs++; - } - return 0; -} - -static int tvp5150_vdp_init(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs) -{ - unsigned int i; - - /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); - - /* Before programming, Line mode should be at 0xff */ - for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); - - /* Load Ram Table */ - while (regs->reg != (u16)-1) { - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); - - for (i = 0; i < 16; i++) - tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); - - regs++; - } - return 0; -} - -/* Fills VBI capabilities based on i2c_vbi_ram_value struct */ -static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, - struct v4l2_sliced_vbi_cap *cap) -{ - const struct i2c_vbi_ram_value *regs = vbi_ram_default; - int line; - - v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); - memset(cap, 0, sizeof *cap); - - while (regs->reg != (u16)-1 ) { - for (line=regs->type.ini_line;line<=regs->type.end_line;line++) { - cap->service_lines[0][line] |= regs->type.vbi_type; - } - cap->service_set |= regs->type.vbi_type; - - regs++; - } - return 0; -} - -/* Set vbi processing - * type - one of tvp5150_vbi_types - * line - line to gather data - * fields: bit 0 field1, bit 1, field2 - * flags (default=0xf0) is a bitmask, were set means: - * bit 7: enable filtering null bytes on CC - * bit 6: send data also to FIFO - * bit 5: don't allow data with errors on FIFO - * bit 4: enable ECC when possible - * pix_align = pix alignment: - * LSB = field1 - * MSB = field2 - */ -static int tvp5150_set_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, - unsigned int type,u8 flags, int line, - const int fields) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std = decoder->norm; - u8 reg; - int pos=0; - - if (std == V4L2_STD_ALL) { - v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); - return 0; - } else if (std & V4L2_STD_625_50) { - /* Don't follow NTSC Line number convension */ - line += 3; - } - - if (line<6||line>27) - return 0; - - while (regs->reg != (u16)-1 ) { - if ((type & regs->type.vbi_type) && - (line>=regs->type.ini_line) && - (line<=regs->type.end_line)) { - type=regs->type.vbi_type; - break; - } - - regs++; - pos++; - } - if (regs->reg == (u16)-1) - return 0; - - type=pos | (flags & 0xf0); - reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; - - if (fields&1) { - tvp5150_write(sd, reg, type); - } - - if (fields&2) { - tvp5150_write(sd, reg+1, type); - } - - return type; -} - -static int tvp5150_get_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, int line) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std = decoder->norm; - u8 reg; - int pos, type = 0; - int i, ret = 0; - - if (std == V4L2_STD_ALL) { - v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); - return 0; - } else if (std & V4L2_STD_625_50) { - /* Don't follow NTSC Line number convension */ - line += 3; - } - - if (line < 6 || line > 27) - return 0; - - reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; - - for (i = 0; i <= 1; i++) { - ret = tvp5150_read(sd, reg + i); - if (ret < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", - __func__, ret); - return 0; - } - pos = ret & 0x0f; - if (pos < 0x0f) - type |= regs[pos].type.vbi_type; - } - - return type; -} - -static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - int fmt = 0; - - decoder->norm = std; - - /* First tests should be against specific std */ - - if (std == V4L2_STD_ALL) { - fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */ - } else if (std & V4L2_STD_NTSC_443) { - fmt = VIDEO_STD_NTSC_4_43_BIT; - } else if (std & V4L2_STD_PAL_M) { - fmt = VIDEO_STD_PAL_M_BIT; - } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { - fmt = VIDEO_STD_PAL_COMBINATION_N_BIT; - } else { - /* Then, test against generic ones */ - if (std & V4L2_STD_NTSC) - fmt = VIDEO_STD_NTSC_MJ_BIT; - else if (std & V4L2_STD_PAL) - fmt = VIDEO_STD_PAL_BDGHIN_BIT; - else if (std & V4L2_STD_SECAM) - fmt = VIDEO_STD_SECAM_BIT; - } - - v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); - tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); - return 0; -} - -static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - if (decoder->norm == std) - return 0; - - /* Change cropping height limits */ - if (std & V4L2_STD_525_60) - decoder->rect.height = TVP5150_V_MAX_525_60; - else - decoder->rect.height = TVP5150_V_MAX_OTHERS; - - - return tvp5150_set_std(sd, std); -} - -static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - /* Initializes TVP5150 to its default values */ - tvp5150_write_inittab(sd, tvp5150_init_default); - - /* Initializes VDP registers */ - tvp5150_vdp_init(sd, vbi_ram_default); - - /* Selects decoder input */ - tvp5150_selmux(sd); - - /* Initializes TVP5150 to stream enabled values */ - tvp5150_write_inittab(sd, tvp5150_init_enable); - - /* Initialize image preferences */ - v4l2_ctrl_handler_setup(&decoder->hdl); - - tvp5150_set_std(sd, decoder->norm); - return 0; -}; - -static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); - return 0; - case V4L2_CID_CONTRAST: - tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); - return 0; - case V4L2_CID_SATURATION: - tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); - return 0; - case V4L2_CID_HUE: - tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); - return 0; - } - return -EINVAL; -} - -static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) -{ - int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); - - switch (val & 0x0F) { - case 0x01: - return V4L2_STD_NTSC; - case 0x03: - return V4L2_STD_PAL; - case 0x05: - return V4L2_STD_PAL_M; - case 0x07: - return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; - case 0x09: - return V4L2_STD_NTSC_443; - case 0xb: - return V4L2_STD_SECAM; - default: - return V4L2_STD_UNKNOWN; - } -} - -static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index) - return -EINVAL; - - *code = V4L2_MBUS_FMT_UYVY8_2X8; - return 0; -} - -static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *f) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - if (f == NULL) - return -EINVAL; - - tvp5150_reset(sd, 0); - - f->width = decoder->rect.width; - f->height = decoder->rect.height; - - f->code = V4L2_MBUS_FMT_UYVY8_2X8; - f->field = V4L2_FIELD_SEQ_TB; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; - - v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width, - f->height); - return 0; -} - -static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - struct v4l2_rect rect = a->c; - struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std; - int hmax; - - v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", - __func__, rect.left, rect.top, rect.width, rect.height); - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* tvp5150 has some special limits */ - rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); - rect.width = clamp(rect.width, - TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, - TVP5150_H_MAX - rect.left); - rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); - - /* Calculate height based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - - if (std & V4L2_STD_525_60) - hmax = TVP5150_V_MAX_525_60; - else - hmax = TVP5150_V_MAX_OTHERS; - - rect.height = clamp(rect.height, - hmax - TVP5150_MAX_CROP_TOP - rect.top, - hmax - rect.top); - - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, - rect.top + rect.height - hmax); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, - rect.left >> TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, - rect.left | (1 << TVP5150_CROP_SHIFT)); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, - (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> - TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, - rect.left + rect.width - TVP5150_MAX_CROP_LEFT); - - decoder->rect = rect; - - return 0; -} - -static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); - - a->c = decoder->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); - v4l2_std_id std; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = TVP5150_H_MAX; - - /* Calculate height based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - - if (std & V4L2_STD_525_60) - a->bounds.height = TVP5150_V_MAX_525_60; - else - a->bounds.height = TVP5150_V_MAX_OTHERS; - - a->defrect = a->bounds; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - -/**************************************************************************** - I2C Command - ****************************************************************************/ - -static int tvp5150_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - decoder->input = input; - decoder->output = output; - tvp5150_selmux(sd); - return 0; -} - -static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) -{ - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ - if (fmt->sample_format == V4L2_PIX_FMT_GREY) - tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); - if (fmt->count[0] == 18 && fmt->count[1] == 18) { - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); - } - return 0; -} - -static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - int i; - - if (svbi->service_set != 0) { - for (i = 0; i <= 23; i++) { - svbi->service_lines[1][i] = 0; - svbi->service_lines[0][i] = - tvp5150_set_vbi(sd, vbi_ram_default, - svbi->service_lines[0][i], 0xf0, i, 3); - } - /* Enables FIFO */ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); - } else { - /* Disables FIFO*/ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); - - /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); - - /* Disable Line modes */ - for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); - } - return 0; -} - -static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) -{ - int i, mask = 0; - - memset(svbi, 0, sizeof(*svbi)); - - for (i = 0; i <= 23; i++) { - svbi->service_lines[0][i] = - tvp5150_get_vbi(sd, vbi_ram_default, i); - mask |= svbi->service_lines[0][i]; - } - svbi->service_set = mask; - return 0; -} - -static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - int rev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | - tvp5150_read(sd, TVP5150_ROM_MINOR_VER); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, - rev); -} - - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - int res; - - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - res = tvp5150_read(sd, reg->reg & 0xff); - if (res < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", __func__, res); - return res; - } - - reg->val = res; - reg->size = 1; - return 0; -} - -static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - int status = tvp5150_read(sd, 0x88); - - vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { - .s_ctrl = tvp5150_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops tvp5150_core_ops = { - .log_status = tvp5150_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = tvp5150_s_std, - .reset = tvp5150_reset, - .g_chip_ident = tvp5150_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = tvp5150_g_register, - .s_register = tvp5150_s_register, -#endif -}; - -static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { - .g_tuner = tvp5150_g_tuner, -}; - -static const struct v4l2_subdev_video_ops tvp5150_video_ops = { - .s_routing = tvp5150_s_routing, - .enum_mbus_fmt = tvp5150_enum_mbus_fmt, - .s_mbus_fmt = tvp5150_mbus_fmt, - .try_mbus_fmt = tvp5150_mbus_fmt, - .g_mbus_fmt = tvp5150_mbus_fmt, - .s_crop = tvp5150_s_crop, - .g_crop = tvp5150_g_crop, - .cropcap = tvp5150_cropcap, -}; - -static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { - .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, - .g_sliced_fmt = tvp5150_g_sliced_fmt, - .s_sliced_fmt = tvp5150_s_sliced_fmt, - .s_raw_fmt = tvp5150_s_raw_fmt, -}; - -static const struct v4l2_subdev_ops tvp5150_ops = { - .core = &tvp5150_core_ops, - .tuner = &tvp5150_tuner_ops, - .video = &tvp5150_video_ops, - .vbi = &tvp5150_vbi_ops, -}; - - -/**************************************************************************** - I2C Client & Driver - ****************************************************************************/ - -static int tvp5150_probe(struct i2c_client *c, - const struct i2c_device_id *id) -{ - struct tvp5150 *core; - struct v4l2_subdev *sd; - int tvp5150_id[4]; - int i, res; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(c->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); - if (!core) { - return -ENOMEM; - } - sd = &core->sd; - v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); - - /* - * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, - * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER - */ - for (i = 0; i < 4; i++) { - res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); - if (res < 0) - goto free_core; - tvp5150_id[i] = res; - } - - v4l_info(c, "chip found @ 0x%02x (%s)\n", - c->addr << 1, c->adapter->name); - - if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */ - v4l2_info(sd, "tvp%02x%02xam1 detected.\n", - tvp5150_id[0], tvp5150_id[1]); - - /* ITU-T BT.656.4 timing */ - tvp5150_write(sd, TVP5150_REV_SELECT, 0); - } else { - /* Is TVP5150A */ - if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { - v4l2_info(sd, "tvp%02x%02xa detected.\n", - tvp5150_id[2], tvp5150_id[3]); - } else { - v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", - tvp5150_id[2], tvp5150_id[3]); - v4l2_info(sd, "*** Rom ver is %d.%d\n", - tvp5150_id[2], tvp5150_id[3]); - } - } - - core->norm = V4L2_STD_ALL; /* Default is autodetect */ - core->input = TVP5150_COMPOSITE1; - core->enable = 1; - - v4l2_ctrl_handler_init(&core->hdl, 4); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - sd->ctrl_handler = &core->hdl; - if (core->hdl.error) { - res = core->hdl.error; - v4l2_ctrl_handler_free(&core->hdl); - goto free_core; - } - v4l2_ctrl_handler_setup(&core->hdl); - - /* Default is no cropping */ - core->rect.top = 0; - if (tvp5150_read_std(sd) & V4L2_STD_525_60) - core->rect.height = TVP5150_V_MAX_525_60; - else - core->rect.height = TVP5150_V_MAX_OTHERS; - core->rect.left = 0; - core->rect.width = TVP5150_H_MAX; - - if (debug > 1) - tvp5150_log_status(sd); - return 0; - -free_core: - kfree(core); - return res; -} - -static int tvp5150_remove(struct i2c_client *c) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(c); - struct tvp5150 *decoder = to_tvp5150(sd); - - v4l2_dbg(1, debug, sd, - "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", - c->addr << 1); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(to_tvp5150(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id tvp5150_id[] = { - { "tvp5150", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tvp5150_id); - -static struct i2c_driver tvp5150_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tvp5150", - }, - .probe = tvp5150_probe, - .remove = tvp5150_remove, - .id_table = tvp5150_id, -}; - -module_i2c_driver(tvp5150_driver); diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/video/tvp5150_reg.h deleted file mode 100644 index 25a994944918..000000000000 --- a/drivers/media/video/tvp5150_reg.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers - * - * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) - * This code is placed under the terms of the GNU General Public License v2 - */ - -#define TVP5150_VD_IN_SRC_SEL_1 0x00 /* Video input source selection #1 */ -#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */ -#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */ -#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */ -#define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */ - -/* Reserved 05h */ - -#define TVP5150_COLOR_KIL_THSH_CTL 0x06 /* Color killer threshold control */ -#define TVP5150_LUMA_PROC_CTL_1 0x07 /* Luminance processing control #1 */ -#define TVP5150_LUMA_PROC_CTL_2 0x08 /* Luminance processing control #2 */ -#define TVP5150_BRIGHT_CTL 0x09 /* Brightness control */ -#define TVP5150_SATURATION_CTL 0x0a /* Color saturation control */ -#define TVP5150_HUE_CTL 0x0b /* Hue control */ -#define TVP5150_CONTRAST_CTL 0x0c /* Contrast control */ -#define TVP5150_DATA_RATE_SEL 0x0d /* Outputs and data rates select */ -#define TVP5150_LUMA_PROC_CTL_3 0x0e /* Luminance processing control #3 */ -#define TVP5150_CONF_SHARED_PIN 0x0f /* Configuration shared pins */ - -/* Reserved 10h */ - -#define TVP5150_ACT_VD_CROP_ST_MSB 0x11 /* Active video cropping start MSB */ -#define TVP5150_ACT_VD_CROP_ST_LSB 0x12 /* Active video cropping start LSB */ -#define TVP5150_ACT_VD_CROP_STP_MSB 0x13 /* Active video cropping stop MSB */ -#define TVP5150_ACT_VD_CROP_STP_LSB 0x14 /* Active video cropping stop LSB */ -#define TVP5150_GENLOCK 0x15 /* Genlock/RTC */ -#define TVP5150_HORIZ_SYNC_START 0x16 /* Horizontal sync start */ - -/* Reserved 17h */ - -#define TVP5150_VERT_BLANKING_START 0x18 /* Vertical blanking start */ -#define TVP5150_VERT_BLANKING_STOP 0x19 /* Vertical blanking stop */ -#define TVP5150_CHROMA_PROC_CTL_1 0x1a /* Chrominance processing control #1 */ -#define TVP5150_CHROMA_PROC_CTL_2 0x1b /* Chrominance processing control #2 */ -#define TVP5150_INT_RESET_REG_B 0x1c /* Interrupt reset register B */ -#define TVP5150_INT_ENABLE_REG_B 0x1d /* Interrupt enable register B */ -#define TVP5150_INTT_CONFIG_REG_B 0x1e /* Interrupt configuration register B */ - -/* Reserved 1Fh-27h */ - -#define VIDEO_STD_MASK (0x07 >> 1) -#define TVP5150_VIDEO_STD 0x28 /* Video standard */ -#define VIDEO_STD_AUTO_SWITCH_BIT 0x00 -#define VIDEO_STD_NTSC_MJ_BIT 0x02 -#define VIDEO_STD_PAL_BDGHIN_BIT 0x04 -#define VIDEO_STD_PAL_M_BIT 0x06 -#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08 -#define VIDEO_STD_NTSC_4_43_BIT 0x0a -#define VIDEO_STD_SECAM_BIT 0x0c - -#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01 -#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03 -#define VIDEO_STD_PAL_M_BIT_AS 0x05 -#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07 -#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09 -#define VIDEO_STD_SECAM_BIT_AS 0x0b - -/* Reserved 29h-2bh */ - -#define TVP5150_CB_GAIN_FACT 0x2c /* Cb gain factor */ -#define TVP5150_CR_GAIN_FACTOR 0x2d /* Cr gain factor */ -#define TVP5150_MACROVISION_ON_CTR 0x2e /* Macrovision on counter */ -#define TVP5150_MACROVISION_OFF_CTR 0x2f /* Macrovision off counter */ -#define TVP5150_REV_SELECT 0x30 /* revision select (TVP5150AM1 only) */ - -/* Reserved 31h-7Fh */ - -#define TVP5150_MSB_DEV_ID 0x80 /* MSB of device ID */ -#define TVP5150_LSB_DEV_ID 0x81 /* LSB of device ID */ -#define TVP5150_ROM_MAJOR_VER 0x82 /* ROM major version */ -#define TVP5150_ROM_MINOR_VER 0x83 /* ROM minor version */ -#define TVP5150_VERT_LN_COUNT_MSB 0x84 /* Vertical line count MSB */ -#define TVP5150_VERT_LN_COUNT_LSB 0x85 /* Vertical line count LSB */ -#define TVP5150_INT_STATUS_REG_B 0x86 /* Interrupt status register B */ -#define TVP5150_INT_ACTIVE_REG_B 0x87 /* Interrupt active register B */ -#define TVP5150_STATUS_REG_1 0x88 /* Status register #1 */ -#define TVP5150_STATUS_REG_2 0x89 /* Status register #2 */ -#define TVP5150_STATUS_REG_3 0x8a /* Status register #3 */ -#define TVP5150_STATUS_REG_4 0x8b /* Status register #4 */ -#define TVP5150_STATUS_REG_5 0x8c /* Status register #5 */ -/* Reserved 8Dh-8Fh */ - /* Closed caption data registers */ -#define TVP5150_CC_DATA_INI 0x90 -#define TVP5150_CC_DATA_END 0x93 - - /* WSS data registers */ -#define TVP5150_WSS_DATA_INI 0x94 -#define TVP5150_WSS_DATA_END 0x99 - -/* VPS data registers */ -#define TVP5150_VPS_DATA_INI 0x9a -#define TVP5150_VPS_DATA_END 0xa6 - -/* VITC data registers */ -#define TVP5150_VITC_DATA_INI 0xa7 -#define TVP5150_VITC_DATA_END 0xaf - -#define TVP5150_VBI_FIFO_READ_DATA 0xb0 /* VBI FIFO read data */ - -/* Teletext filter 1 */ -#define TVP5150_TELETEXT_FIL1_INI 0xb1 -#define TVP5150_TELETEXT_FIL1_END 0xb5 - -/* Teletext filter 2 */ -#define TVP5150_TELETEXT_FIL2_INI 0xb6 -#define TVP5150_TELETEXT_FIL2_END 0xba - -#define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */ -/* Reserved BCh-BFh */ -#define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */ -#define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */ -#define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */ -#define TVP5150_VDP_CONF_RAM_DATA 0xc3 /* VDP configuration RAM data */ -#define TVP5150_CONF_RAM_ADDR_LOW 0xc4 /* Configuration RAM address low byte */ -#define TVP5150_CONF_RAM_ADDR_HIGH 0xc5 /* Configuration RAM address high byte */ -#define TVP5150_VDP_STATUS_REG 0xc6 /* VDP status register */ -#define TVP5150_FIFO_WORD_COUNT 0xc7 /* FIFO word count */ -#define TVP5150_FIFO_INT_THRESHOLD 0xc8 /* FIFO interrupt threshold */ -#define TVP5150_FIFO_RESET 0xc9 /* FIFO reset */ -#define TVP5150_LINE_NUMBER_INT 0xca /* Line number interrupt */ -#define TVP5150_PIX_ALIGN_REG_LOW 0xcb /* Pixel alignment register low byte */ -#define TVP5150_PIX_ALIGN_REG_HIGH 0xcc /* Pixel alignment register high byte */ -#define TVP5150_FIFO_OUT_CTRL 0xcd /* FIFO output control */ -/* Reserved CEh */ -#define TVP5150_FULL_FIELD_ENA 0xcf /* Full field enable 1 */ - -/* Line mode registers */ -#define TVP5150_LINE_MODE_INI 0xd0 -#define TVP5150_LINE_MODE_END 0xfb - -#define TVP5150_FULL_FIELD_MODE_REG 0xfc /* Full field mode register */ -/* Reserved FDh-FFh */ diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c deleted file mode 100644 index fb6a5b57eb83..000000000000 --- a/drivers/media/video/tvp7002.c +++ /dev/null @@ -1,1145 +0,0 @@ -/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics - * Digitizer with Horizontal PLL registers - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Santiago Nunez-Corrales - * - * This code is partially based upon the TVP5150 driver - * written by Mauro Carvalho Chehab (mchehab@infradead.org), - * the TVP514x driver written by Vaibhav Hiremath - * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by - * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). - * - * This program is free software; 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 -#include -#include -#include "tvp7002_reg.h" - -MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); -MODULE_AUTHOR("Santiago Nunez-Corrales "); -MODULE_LICENSE("GPL"); - -/* Module Name */ -#define TVP7002_MODULE_NAME "tvp7002" - -/* I2C retry attempts */ -#define I2C_RETRY_COUNT (5) - -/* End of registers */ -#define TVP7002_EOR 0x5c - -/* Read write definition for registers */ -#define TVP7002_READ 0 -#define TVP7002_WRITE 1 -#define TVP7002_RESERVED 2 - -/* Interlaced vs progressive mask and shift */ -#define TVP7002_IP_SHIFT 5 -#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) - -/* Shift for CPL and LPF registers */ -#define TVP7002_CL_SHIFT 8 -#define TVP7002_CL_MASK 0x0f - -/* Debug functions */ -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -/* Structure for register values */ -struct i2c_reg_value { - u8 reg; - u8 value; - u8 type; -}; - -/* - * Register default values (according to tvp7002 datasheet) - * In the case of read-only registers, the value (0xff) is - * never written. R/W functionality is controlled by the - * writable bit in the register struct definition. - */ -static const struct i2c_reg_value tvp7002_init_default[] = { - { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, - { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, - { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, - { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, - { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, - { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, - { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, - { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, - { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, - { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, - { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, - { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, - { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, - { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, - { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, - { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, - { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, - { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, - { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, - { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, - { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, - { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, - { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, - { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, - { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, - { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, - { 0x29, 0x08, TVP7002_RESERVED }, - { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, - /* PWR_CTL is controlled only by the probe and reset functions */ - { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, - { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, - { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, - { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, - { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, - { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { 0x32, 0x18, TVP7002_RESERVED }, - { 0x33, 0x60, TVP7002_RESERVED }, - { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, - { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, - { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, - { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, - { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, - { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, - { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, - { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, - { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, - { 0x3e, 0x60, TVP7002_RESERVED }, - { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, - { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, - { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, - { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, - { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, - { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, - { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, - { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, - { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, - { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, - { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, - { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, - { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, - { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, - { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, - { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, - /* This signals end of register values */ - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 480P */ -static const struct i2c_reg_value tvp7002_parms_480P[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 576P */ -static const struct i2c_reg_value tvp7002_parms_576P[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 1080I60 */ -static const struct i2c_reg_value tvp7002_parms_1080I60[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 1080P60 */ -static const struct i2c_reg_value tvp7002_parms_1080P60[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 1080I50 */ -static const struct i2c_reg_value tvp7002_parms_1080I50[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 720P60 */ -static const struct i2c_reg_value tvp7002_parms_720P60[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Register parameters for 720P50 */ -static const struct i2c_reg_value tvp7002_parms_720P50[] = { - { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, - { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, - { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, - { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, - { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, - { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, - { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, - { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, - { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, - { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, - { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, - { TVP7002_EOR, 0xff, TVP7002_RESERVED } -}; - -/* Preset definition for handling device operation */ -struct tvp7002_preset_definition { - u32 preset; - struct v4l2_dv_timings timings; - const struct i2c_reg_value *p_settings; - enum v4l2_colorspace color_space; - enum v4l2_field scanmode; - u16 progressive; - u16 lines_per_frame; - u16 cpl_min; - u16 cpl_max; -}; - -/* Struct list for digital video presets */ -static const struct tvp7002_preset_definition tvp7002_presets[] = { - { - V4L2_DV_720P60, - V4L2_DV_BT_CEA_1280X720P60, - tvp7002_parms_720P60, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_NONE, - 1, - 0x2EE, - 135, - 153 - }, - { - V4L2_DV_1080I60, - V4L2_DV_BT_CEA_1920X1080I60, - tvp7002_parms_1080I60, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_INTERLACED, - 0, - 0x465, - 181, - 205 - }, - { - V4L2_DV_1080I50, - V4L2_DV_BT_CEA_1920X1080I50, - tvp7002_parms_1080I50, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_INTERLACED, - 0, - 0x465, - 217, - 245 - }, - { - V4L2_DV_720P50, - V4L2_DV_BT_CEA_1280X720P50, - tvp7002_parms_720P50, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_NONE, - 1, - 0x2EE, - 163, - 183 - }, - { - V4L2_DV_1080P60, - V4L2_DV_BT_CEA_1920X1080P60, - tvp7002_parms_1080P60, - V4L2_COLORSPACE_REC709, - V4L2_FIELD_NONE, - 1, - 0x465, - 90, - 102 - }, - { - V4L2_DV_480P59_94, - V4L2_DV_BT_CEA_720X480P59_94, - tvp7002_parms_480P, - V4L2_COLORSPACE_SMPTE170M, - V4L2_FIELD_NONE, - 1, - 0x20D, - 0xffff, - 0xffff - }, - { - V4L2_DV_576P50, - V4L2_DV_BT_CEA_720X576P50, - tvp7002_parms_576P, - V4L2_COLORSPACE_SMPTE170M, - V4L2_FIELD_NONE, - 1, - 0x271, - 0xffff, - 0xffff - } -}; - -#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets) - -/* Device definition */ -struct tvp7002 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - const struct tvp7002_config *pdata; - - int ver; - int streaming; - - const struct tvp7002_preset_definition *current_preset; -}; - -/* - * to_tvp7002 - Obtain device handler TVP7002 - * @sd: ptr to v4l2_subdev struct - * - * Returns device handler tvp7002. - */ -static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tvp7002, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; -} - -/* - * tvp7002_read - Read a value from a register in an TVP7002 - * @sd: ptr to v4l2_subdev struct - * @addr: TVP7002 register address - * @dst: pointer to 8-bit destination - * - * Returns value read if successful, or non-zero (-1) otherwise. - */ -static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - int retry; - int error; - - for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { - error = i2c_smbus_read_byte_data(c, addr); - - if (error >= 0) { - *dst = (u8)error; - return 0; - } - - msleep_interruptible(10); - } - v4l2_err(sd, "TVP7002 read error %d\n", error); - return error; -} - -/* - * tvp7002_read_err() - Read a register value with error code - * @sd: pointer to standard V4L2 sub-device structure - * @reg: destination register - * @val: value to be read - * @err: pointer to error value - * - * Read a value in a register and save error value in pointer. - * Also update the register table if successful - */ -static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, - u8 *dst, int *err) -{ - if (!*err) - *err = tvp7002_read(sd, reg, dst); -} - -/* - * tvp7002_write() - Write a value to a register in TVP7002 - * @sd: ptr to v4l2_subdev struct - * @addr: TVP7002 register address - * @value: value to be written to the register - * - * Write a value to a register in an TVP7002 decoder device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) -{ - struct i2c_client *c; - int retry; - int error; - - c = v4l2_get_subdevdata(sd); - - for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { - error = i2c_smbus_write_byte_data(c, addr, value); - - if (error >= 0) - return 0; - - v4l2_warn(sd, "Write: retry ... %d\n", retry); - msleep_interruptible(10); - } - v4l2_err(sd, "TVP7002 write error %d\n", error); - return error; -} - -/* - * tvp7002_write_err() - Write a register value with error code - * @sd: pointer to standard V4L2 sub-device structure - * @reg: destination register - * @val: value to be written - * @err: pointer to error value - * - * Write a value in a register and save error value in pointer. - * Also update the register table if successful - */ -static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, - u8 val, int *err) -{ - if (!*err) - *err = tvp7002_write(sd, reg, val); -} - -/* - * tvp7002_g_chip_ident() - Get chip identification number - * @sd: ptr to v4l2_subdev struct - * @chip: ptr to v4l2_dbg_chip_ident struct - * - * Obtains the chip's identification number. - * Returns zero or -EINVAL if read operation fails. - */ -static int tvp7002_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - u8 rev; - int error; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev); - - if (error < 0) - return error; - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev); -} - -/* - * tvp7002_write_inittab() - Write initialization values - * @sd: ptr to v4l2_subdev struct - * @regs: ptr to i2c_reg_value struct - * - * Write initialization values. - * Returns zero or -EINVAL if read operation fails. - */ -static int tvp7002_write_inittab(struct v4l2_subdev *sd, - const struct i2c_reg_value *regs) -{ - int error = 0; - - /* Initialize the first (defined) registers */ - while (TVP7002_EOR != regs->reg) { - if (TVP7002_WRITE == regs->type) - tvp7002_write_err(sd, regs->reg, regs->value, &error); - regs++; - } - - return error; -} - -/* - * tvp7002_s_dv_preset() - Set digital video preset - * @sd: ptr to v4l2_subdev struct - * @dv_preset: ptr to v4l2_dv_preset struct - * - * Set the digital video preset for a TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register access fails. - */ -static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *dv_preset) -{ - struct tvp7002 *device = to_tvp7002(sd); - u32 preset; - int i; - - for (i = 0; i < NUM_PRESETS; i++) { - preset = tvp7002_presets[i].preset; - if (preset == dv_preset->preset) { - device->current_preset = &tvp7002_presets[i]; - return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); - } - } - - return -EINVAL; -} - -static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *dv_timings) -{ - struct tvp7002 *device = to_tvp7002(sd); - const struct v4l2_bt_timings *bt = &dv_timings->bt; - int i; - - if (dv_timings->type != V4L2_DV_BT_656_1120) - return -EINVAL; - for (i = 0; i < NUM_PRESETS; i++) { - const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; - - if (!memcmp(bt, t, &bt->standards - &bt->width)) { - device->current_preset = &tvp7002_presets[i]; - return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); - } - } - return -EINVAL; -} - -static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *dv_timings) -{ - struct tvp7002 *device = to_tvp7002(sd); - - *dv_timings = device->current_preset->timings; - return 0; -} - -/* - * tvp7002_s_ctrl() - Set a control - * @ctrl: ptr to v4l2_ctrl struct - * - * Set a control in TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register access fails. - */ -static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - int error = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); - tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); - tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); - return error; - } - return -EINVAL; -} - -/* - * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @f: pointer to mediabus format structure - * - * Negotiate the image capture size and mediabus format. - * There is only one possible format, so this single function works for - * get, set and try. - */ -static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct tvp7002 *device = to_tvp7002(sd); - struct v4l2_dv_enum_preset e_preset; - int error; - - /* Calculate height and width based on current standard */ - error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); - if (error) - return error; - - f->width = e_preset.width; - f->height = e_preset.height; - f->code = V4L2_MBUS_FMT_YUYV10_1X20; - f->field = device->current_preset->scanmode; - f->colorspace = device->current_preset->color_space; - - v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d", - f->width, f->height); - return 0; -} - -/* - * tvp7002_query_dv_preset() - query DV preset - * @sd: pointer to standard V4L2 sub-device structure - * @qpreset: standard V4L2 v4l2_dv_preset structure - * - * Returns the current DV preset by TVP7002. If no active input is - * detected, returns -EINVAL - */ -static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) -{ - const struct tvp7002_preset_definition *presets = tvp7002_presets; - u8 progressive; - u32 lpfr; - u32 cpln; - int error = 0; - u8 lpf_lsb; - u8 lpf_msb; - u8 cpl_lsb; - u8 cpl_msb; - - /* Return invalid index if no active input is detected */ - *index = NUM_PRESETS; - - /* Read standards from device registers */ - tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); - tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); - - if (error < 0) - return error; - - tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); - tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); - - if (error < 0) - return error; - - /* Get lines per frame, clocks per line and interlaced/progresive */ - lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); - cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); - progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; - - /* Do checking of video modes */ - for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) - if (lpfr == presets->lines_per_frame && - progressive == presets->progressive) { - if (presets->cpl_min == 0xffff) - break; - if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) - break; - } - - if (*index == NUM_PRESETS) { - v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", - lpfr, cpln); - return -ENOLINK; - } - - /* Update lines per frame and clocks per line info */ - v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); - return 0; -} - -static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *qpreset) -{ - int index; - int err = tvp7002_query_dv(sd, &index); - - if (err || index == NUM_PRESETS) { - qpreset->preset = V4L2_DV_INVALID; - if (err == -ENOLINK) - err = 0; - return err; - } - qpreset->preset = tvp7002_presets[index].preset; - return 0; -} - -static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - int index; - int err = tvp7002_query_dv(sd, &index); - - if (err) - return err; - *timings = tvp7002_presets[index].timings; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -/* - * tvp7002_g_register() - Get the value of a register - * @sd: ptr to v4l2_subdev struct - * @reg: ptr to v4l2_dbg_register struct - * - * Get the value of a TVP7002 decoder device register. - * Returns zero when successful, -EINVAL if register read fails or - * access to I2C client fails, -EPERM if the call is not allowed - * by disabled CAP_SYS_ADMIN. - */ -static int tvp7002_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 val; - int ret; - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - ret = tvp7002_read(sd, reg->reg & 0xff, &val); - reg->val = val; - return ret; -} - -/* - * tvp7002_s_register() - set a control - * @sd: ptr to v4l2_subdev struct - * @reg: ptr to v4l2_dbg_register struct - * - * Get the value of a TVP7002 decoder device register. - * Returns zero when successful, -EINVAL if register read fails or - * -EPERM if call not allowed. - */ -static int tvp7002_s_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); -} -#endif - -/* - * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats - * @sd: pointer to standard V4L2 sub-device structure - * @index: format index - * @code: pointer to mediabus format - * - * Enumerate supported mediabus formats. - */ - -static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - /* Check requested format index is within range */ - if (index) - return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV10_1X20; - return 0; -} - -/* - * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream - * @sd: pointer to standard V4L2 sub-device structure - * @enable: streaming enable or disable - * - * Sets streaming to enable or disable, if possible. - */ -static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct tvp7002 *device = to_tvp7002(sd); - int error = 0; - - if (device->streaming == enable) - return 0; - - if (enable) { - /* Set output state on (low impedance means stream on) */ - error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00); - device->streaming = enable; - } else { - /* Set output state off (high impedance means stream off) */ - error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03); - if (error) - v4l2_dbg(1, debug, sd, "Unable to stop streaming\n"); - - device->streaming = enable; - } - - return error; -} - -/* - * tvp7002_log_status() - Print information about register settings - * @sd: ptr to v4l2_subdev struct - * - * Log register values of a TVP7002 decoder device. - * Returns zero or -EINVAL if read operation fails. - */ -static int tvp7002_log_status(struct v4l2_subdev *sd) -{ - const struct tvp7002_preset_definition *presets = tvp7002_presets; - struct tvp7002 *device = to_tvp7002(sd); - struct v4l2_dv_enum_preset e_preset; - struct v4l2_dv_preset detected; - int i; - - detected.preset = V4L2_DV_INVALID; - /* Find my current standard*/ - tvp7002_query_dv_preset(sd, &detected); - - /* Print standard related code values */ - for (i = 0; i < NUM_PRESETS; i++, presets++) - if (presets->preset == detected.preset) - break; - - if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) - return -EINVAL; - - v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); - v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); - v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); - if (i == NUM_PRESETS) { - v4l2_info(sd, "Detected DV Preset: None\n"); - } else { - if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) - return -EINVAL; - v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); - v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); - v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); - } - v4l2_info(sd, "Streaming enabled: %s\n", - device->streaming ? "yes" : "no"); - - /* Print the current value of the gain control */ - v4l2_ctrl_handler_log_status(&device->hdl, sd->name); - - return 0; -} - -/* - * tvp7002_enum_dv_presets() - Enum supported digital video formats - * @sd: pointer to standard V4L2 sub-device structure - * @preset: pointer to format struct - * - * Enumerate supported digital video formats. - */ -static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, - struct v4l2_dv_enum_preset *preset) -{ - /* Check requested format index is within range */ - if (preset->index >= NUM_PRESETS) - return -EINVAL; - - return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); -} - -static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, - struct v4l2_enum_dv_timings *timings) -{ - /* Check requested format index is within range */ - if (timings->index >= NUM_PRESETS) - return -EINVAL; - - timings->timings = tvp7002_presets[timings->index].timings; - return 0; -} - -static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { - .s_ctrl = tvp7002_s_ctrl, -}; - -/* V4L2 core operation handlers */ -static const struct v4l2_subdev_core_ops tvp7002_core_ops = { - .g_chip_ident = tvp7002_g_chip_ident, - .log_status = tvp7002_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = tvp7002_g_register, - .s_register = tvp7002_s_register, -#endif -}; - -/* Specific video subsystem operation handlers */ -static const struct v4l2_subdev_video_ops tvp7002_video_ops = { - .enum_dv_presets = tvp7002_enum_dv_presets, - .s_dv_preset = tvp7002_s_dv_preset, - .query_dv_preset = tvp7002_query_dv_preset, - .g_dv_timings = tvp7002_g_dv_timings, - .s_dv_timings = tvp7002_s_dv_timings, - .enum_dv_timings = tvp7002_enum_dv_timings, - .query_dv_timings = tvp7002_query_dv_timings, - .s_stream = tvp7002_s_stream, - .g_mbus_fmt = tvp7002_mbus_fmt, - .try_mbus_fmt = tvp7002_mbus_fmt, - .s_mbus_fmt = tvp7002_mbus_fmt, - .enum_mbus_fmt = tvp7002_enum_mbus_fmt, -}; - -/* V4L2 top level operation handlers */ -static const struct v4l2_subdev_ops tvp7002_ops = { - .core = &tvp7002_core_ops, - .video = &tvp7002_video_ops, -}; - -/* - * tvp7002_probe - Probe a TVP7002 device - * @c: ptr to i2c_client struct - * @id: ptr to i2c_device_id struct - * - * Initialize the TVP7002 device - * Returns zero when successful, -EINVAL if register read fails or - * -EIO if i2c access is not available. - */ -static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) -{ - struct v4l2_subdev *sd; - struct tvp7002 *device; - struct v4l2_dv_preset preset; - int polarity_a; - int polarity_b; - u8 revision; - - int error; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(c->adapter, - I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -EIO; - - if (!c->dev.platform_data) { - v4l_err(c, "No platform data!!\n"); - return -ENODEV; - } - - device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL); - - if (!device) - return -ENOMEM; - - sd = &device->sd; - device->pdata = c->dev.platform_data; - device->current_preset = tvp7002_presets; - - /* Tell v4l2 the device is ready */ - v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); - v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", - c->addr, c->adapter->name); - - error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); - if (error < 0) - goto found_error; - - /* Get revision number */ - v4l2_info(sd, "Rev. %02x detected.\n", revision); - if (revision != 0x02) - v4l2_info(sd, "Unknown revision detected.\n"); - - /* Initializes TVP7002 to its default values */ - error = tvp7002_write_inittab(sd, tvp7002_init_default); - - if (error < 0) - goto found_error; - - /* Set polarity information after registers have been set */ - polarity_a = 0x20 | device->pdata->hs_polarity << 5 - | device->pdata->vs_polarity << 2; - error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); - if (error < 0) - goto found_error; - - polarity_b = 0x01 | device->pdata->fid_polarity << 2 - | device->pdata->sog_polarity << 1 - | device->pdata->clk_polarity; - error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); - if (error < 0) - goto found_error; - - /* Set registers according to default video mode */ - preset.preset = device->current_preset->preset; - error = tvp7002_s_dv_preset(sd, &preset); - - v4l2_ctrl_handler_init(&device->hdl, 1); - v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, - V4L2_CID_GAIN, 0, 255, 1, 0); - sd->ctrl_handler = &device->hdl; - if (device->hdl.error) { - int err = device->hdl.error; - - v4l2_ctrl_handler_free(&device->hdl); - kfree(device); - return err; - } - v4l2_ctrl_handler_setup(&device->hdl); - -found_error: - if (error < 0) - kfree(device); - - return error; -} - -/* - * tvp7002_remove - Remove TVP7002 device support - * @c: ptr to i2c_client struct - * - * Reset the TVP7002 device - * Returns zero. - */ -static int tvp7002_remove(struct i2c_client *c) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(c); - struct tvp7002 *device = to_tvp7002(sd); - - v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" - "on address 0x%x\n", c->addr); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&device->hdl); - kfree(device); - return 0; -} - -/* I2C Device ID table */ -static const struct i2c_device_id tvp7002_id[] = { - { "tvp7002", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tvp7002_id); - -/* I2C driver data */ -static struct i2c_driver tvp7002_driver = { - .driver = { - .owner = THIS_MODULE, - .name = TVP7002_MODULE_NAME, - }, - .probe = tvp7002_probe, - .remove = tvp7002_remove, - .id_table = tvp7002_id, -}; - -module_i2c_driver(tvp7002_driver); diff --git a/drivers/media/video/tvp7002_reg.h b/drivers/media/video/tvp7002_reg.h deleted file mode 100644 index 0e34ca9bccf3..000000000000 --- a/drivers/media/video/tvp7002_reg.h +++ /dev/null @@ -1,150 +0,0 @@ -/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics - * Digitizer with Horizontal PLL registers - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Santiago Nunez-Corrales - * - * This code is partially based upon the TVP5150 driver - * written by Mauro Carvalho Chehab (mchehab@infradead.org), - * the TVP514x driver written by Vaibhav Hiremath - * and the TVP7002 driver in the TI LSP 2.10.00.14 - * - * This program is free software; 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. - */ - -/* Naming conventions - * ------------------ - * - * FDBK: Feedback - * DIV: Divider - * CTL: Control - * SEL: Select - * IN: Input - * OUT: Output - * R: Red - * G: Green - * B: Blue - * OFF: Offset - * THRS: Threshold - * DGTL: Digital - * LVL: Level - * PWR: Power - * MVIS: Macrovision - * W: Width - * H: Height - * ALGN: Alignment - * CLK: Clocks - * TOL: Tolerance - * BWTH: Bandwidth - * COEF: Coefficient - * STAT: Status - * AUTO: Automatic - * FLD: Field - * L: Line - */ - -#define TVP7002_CHIP_REV 0x00 -#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01 -#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02 -#define TVP7002_HPLL_CRTL 0x03 -#define TVP7002_HPLL_PHASE_SEL 0x04 -#define TVP7002_CLAMP_START 0x05 -#define TVP7002_CLAMP_W 0x06 -#define TVP7002_HSYNC_OUT_W 0x07 -#define TVP7002_B_FINE_GAIN 0x08 -#define TVP7002_G_FINE_GAIN 0x09 -#define TVP7002_R_FINE_GAIN 0x0a -#define TVP7002_B_FINE_OFF_MSBS 0x0b -#define TVP7002_G_FINE_OFF_MSBS 0x0c -#define TVP7002_R_FINE_OFF_MSBS 0x0d -#define TVP7002_SYNC_CTL_1 0x0e -#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f -#define TVP7002_SYNC_ON_G_THRS 0x10 -#define TVP7002_SYNC_SEPARATOR_THRS 0x11 -#define TVP7002_HPLL_PRE_COAST 0x12 -#define TVP7002_HPLL_POST_COAST 0x13 -#define TVP7002_SYNC_DETECT_STAT 0x14 -#define TVP7002_OUT_FORMATTER 0x15 -#define TVP7002_MISC_CTL_1 0x16 -#define TVP7002_MISC_CTL_2 0x17 -#define TVP7002_MISC_CTL_3 0x18 -#define TVP7002_IN_MUX_SEL_1 0x19 -#define TVP7002_IN_MUX_SEL_2 0x1a -#define TVP7002_B_AND_G_COARSE_GAIN 0x1b -#define TVP7002_R_COARSE_GAIN 0x1c -#define TVP7002_FINE_OFF_LSBS 0x1d -#define TVP7002_B_COARSE_OFF 0x1e -#define TVP7002_G_COARSE_OFF 0x1f -#define TVP7002_R_COARSE_OFF 0x20 -#define TVP7002_HSOUT_OUT_START 0x21 -#define TVP7002_MISC_CTL_4 0x22 -#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23 -#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24 -#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25 -#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26 -#define TVP7002_DGTL_ALC_OUT_MSBS 0x27 -#define TVP7002_AUTO_LVL_CTL_FILTER 0x28 -/* Reserved 0x29*/ -#define TVP7002_FINE_CLAMP_CTL 0x2a -#define TVP7002_PWR_CTL 0x2b -#define TVP7002_ADC_SETUP 0x2c -#define TVP7002_COARSE_CLAMP_CTL 0x2d -#define TVP7002_SOG_CLAMP 0x2e -#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f -#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30 -#define TVP7002_ALC_PLACEMENT 0x31 -/* Reserved 0x32 */ -/* Reserved 0x33 */ -#define TVP7002_MVIS_STRIPPER_W 0x34 -#define TVP7002_VSYNC_ALGN 0x35 -#define TVP7002_SYNC_BYPASS 0x36 -#define TVP7002_L_FRAME_STAT_LSBS 0x37 -#define TVP7002_L_FRAME_STAT_MSBS 0x38 -#define TVP7002_CLK_L_STAT_LSBS 0x39 -#define TVP7002_CLK_L_STAT_MSBS 0x3a -#define TVP7002_HSYNC_W 0x3b -#define TVP7002_VSYNC_W 0x3c -#define TVP7002_L_LENGTH_TOL 0x3d -/* Reserved 0x3e */ -#define TVP7002_VIDEO_BWTH_CTL 0x3f -#define TVP7002_AVID_START_PIXEL_LSBS 0x40 -#define TVP7002_AVID_START_PIXEL_MSBS 0x41 -#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42 -#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43 -#define TVP7002_VBLK_F_0_START_L_OFF 0x44 -#define TVP7002_VBLK_F_1_START_L_OFF 0x45 -#define TVP7002_VBLK_F_0_DURATION 0x46 -#define TVP7002_VBLK_F_1_DURATION 0x47 -#define TVP7002_FBIT_F_0_START_L_OFF 0x48 -#define TVP7002_FBIT_F_1_START_L_OFF 0x49 -#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a -#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b -#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c -#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d -#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e -#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f -#define TVP7002_YUV_U_G_COEF_LSBS 0x50 -#define TVP7002_YUV_U_G_COEF_MSBS 0x51 -#define TVP7002_YUV_U_B_COEF_LSBS 0x52 -#define TVP7002_YUV_U_B_COEF_MSBS 0x53 -#define TVP7002_YUV_U_R_COEF_LSBS 0x54 -#define TVP7002_YUV_U_R_COEF_MSBS 0x55 -#define TVP7002_YUV_V_G_COEF_LSBS 0x56 -#define TVP7002_YUV_V_G_COEF_MSBS 0x57 -#define TVP7002_YUV_V_B_COEF_LSBS 0x58 -#define TVP7002_YUV_V_B_COEF_MSBS 0x59 -#define TVP7002_YUV_V_R_COEF_LSBS 0x5a -#define TVP7002_YUV_V_R_COEF_MSBS 0x5b - diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c deleted file mode 100644 index 1e7446542091..000000000000 --- a/drivers/media/video/upd64031a.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan - * - * 2003 by T.Adachi - * 2003 by Takeru KOMORIYA - * 2006 by Hans Verkuil - * - * This program is free software; 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 - -/* --------------------- read registers functions define -------------------- */ - -/* bit masks */ -#define GR_MODE_MASK 0xc0 -#define DIRECT_3DYCS_CONNECT_MASK 0xc0 -#define SYNC_CIRCUIT_MASK 0xa0 - -/* -------------------------------------------------------------------------- */ - -MODULE_DESCRIPTION("uPD64031A driver"); -MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -enum { - R00 = 0, R01, R02, R03, R04, - R05, R06, R07, R08, R09, - R0A, R0B, R0C, R0D, R0E, R0F, - /* unused registers - R10, R11, R12, R13, R14, - R15, R16, R17, - */ - TOT_REGS -}; - -struct upd64031a_state { - struct v4l2_subdev sd; - u8 regs[TOT_REGS]; - u8 gr_mode; - u8 direct_3dycs_connect; - u8 ext_comp_sync; - u8 ext_vert_sync; -}; - -static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct upd64031a_state, sd); -} - -static u8 upd64031a_init[] = { - 0x00, 0xb8, 0x48, 0xd2, 0xe6, - 0x03, 0x10, 0x0b, 0xaf, 0x7f, - 0x00, 0x00, 0x1d, 0x5e, 0x00, - 0xd0 -}; - -/* ------------------------------------------------------------------------ */ - -static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - if (reg >= sizeof(buf)) - return 0xff; - i2c_master_recv(client, buf, 2); - return buf[reg]; -} - -/* ------------------------------------------------------------------------ */ - -static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - buf[0] = reg; - buf[1] = val; - v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); - if (i2c_master_send(client, buf, 2) != 2) - v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); -} - -/* ------------------------------------------------------------------------ */ - -/* The input changed due to new input or channel changed */ -static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - struct upd64031a_state *state = to_state(sd); - u8 reg = state->regs[R00]; - - v4l2_dbg(1, debug, sd, "changed input or channel\n"); - upd64031a_write(sd, R00, reg | 0x10); - upd64031a_write(sd, R00, reg & ~0x10); - return 0; -} - -/* ------------------------------------------------------------------------ */ - -static int upd64031a_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct upd64031a_state *state = to_state(sd); - u8 r00, r05, r08; - - state->gr_mode = (input & 3) << 6; - state->direct_3dycs_connect = (input & 0xc) << 4; - state->ext_comp_sync = - (input & UPD64031A_COMPOSITE_EXTERNAL) << 1; - state->ext_vert_sync = - (input & UPD64031A_VERTICAL_EXTERNAL) << 2; - r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; - r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | - state->ext_comp_sync | state->ext_vert_sync; - r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | - state->direct_3dycs_connect; - upd64031a_write(sd, R00, r00); - upd64031a_write(sd, R05, r05); - upd64031a_write(sd, R08, r08); - return upd64031a_s_frequency(sd, NULL); -} - -static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); -} - -static int upd64031a_log_status(struct v4l2_subdev *sd) -{ - v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", - upd64031a_read(sd, 0), upd64031a_read(sd, 1)); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = upd64031a_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops upd64031a_core_ops = { - .log_status = upd64031a_log_status, - .g_chip_ident = upd64031a_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = upd64031a_g_register, - .s_register = upd64031a_s_register, -#endif -}; - -static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { - .s_frequency = upd64031a_s_frequency, -}; - -static const struct v4l2_subdev_video_ops upd64031a_video_ops = { - .s_routing = upd64031a_s_routing, -}; - -static const struct v4l2_subdev_ops upd64031a_ops = { - .core = &upd64031a_core_ops, - .tuner = &upd64031a_tuner_ops, - .video = &upd64031a_video_ops, -}; - -/* ------------------------------------------------------------------------ */ - -/* i2c implementation */ - -static int upd64031a_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct upd64031a_state *state; - struct v4l2_subdev *sd; - int i; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); - memcpy(state->regs, upd64031a_init, sizeof(state->regs)); - state->gr_mode = UPD64031A_GR_ON << 6; - state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; - state->ext_comp_sync = state->ext_vert_sync = 0; - for (i = 0; i < TOT_REGS; i++) - upd64031a_write(sd, i, state->regs[i]); - return 0; -} - -static int upd64031a_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id upd64031a_id[] = { - { "upd64031a", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, upd64031a_id); - -static struct i2c_driver upd64031a_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "upd64031a", - }, - .probe = upd64031a_probe, - .remove = upd64031a_remove, - .id_table = upd64031a_id, -}; - -module_i2c_driver(upd64031a_driver); diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c deleted file mode 100644 index 75d6acc62018..000000000000 --- a/drivers/media/video/upd64083.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver - * - * 2003 by T.Adachi (tadachi@tadachi-net.com) - * 2003 by Takeru KOMORIYA - * 2006 by Hans Verkuil - * - * This program is free software; 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 -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("uPD64083 driver"); -MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -enum { - R00 = 0, R01, R02, R03, R04, - R05, R06, R07, R08, R09, - R0A, R0B, R0C, R0D, R0E, R0F, - R10, R11, R12, R13, R14, - R15, R16, - TOT_REGS -}; - -struct upd64083_state { - struct v4l2_subdev sd; - u8 mode; - u8 ext_y_adc; - u8 regs[TOT_REGS]; -}; - -static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct upd64083_state, sd); -} - -/* Initial values when used in combination with the - NEC upd64031a ghost reduction chip. */ -static u8 upd64083_init[] = { - 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ - 0x36, 0xdd, 0x05, 0x56, 0x48, - 0x00, 0x3a, 0xa0, 0x05, 0x08, - 0x44, 0x60, 0x08, 0x52, 0xf8, - 0x53, 0x60, 0x10 -}; - -/* ------------------------------------------------------------------------ */ - -static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - buf[0] = reg; - buf[1] = val; - v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); - if (i2c_master_send(client, buf, 2) != 2) - v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); -} - -/* ------------------------------------------------------------------------ */ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[7]; - - if (reg >= sizeof(buf)) - return 0xff; - i2c_master_recv(client, buf, sizeof(buf)); - return buf[reg]; -} -#endif - -/* ------------------------------------------------------------------------ */ - -static int upd64083_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct upd64083_state *state = to_state(sd); - u8 r00, r02; - - if (input > 7 || (input & 6) == 6) - return -EINVAL; - state->mode = (input & 3) << 6; - state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3; - r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; - r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; - upd64083_write(sd, R00, r00); - upd64083_write(sd, R02, r02); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = upd64083_read(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); -} - -static int upd64083_log_status(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[7]; - - i2c_master_recv(client, buf, 7); - v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " - "SA04=%02x SA05=%02x SA06=%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops upd64083_core_ops = { - .log_status = upd64083_log_status, - .g_chip_ident = upd64083_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = upd64083_g_register, - .s_register = upd64083_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops upd64083_video_ops = { - .s_routing = upd64083_s_routing, -}; - -static const struct v4l2_subdev_ops upd64083_ops = { - .core = &upd64083_core_ops, - .video = &upd64083_video_ops, -}; - -/* ------------------------------------------------------------------------ */ - -/* i2c implementation */ - -static int upd64083_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct upd64083_state *state; - struct v4l2_subdev *sd; - int i; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &upd64083_ops); - /* Initially assume that a ghost reduction chip is present */ - state->mode = 0; /* YCS mode */ - state->ext_y_adc = (1 << 5); - memcpy(state->regs, upd64083_init, TOT_REGS); - for (i = 0; i < TOT_REGS; i++) - upd64083_write(sd, i, state->regs[i]); - return 0; -} - -static int upd64083_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id upd64083_id[] = { - { "upd64083", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, upd64083_id); - -static struct i2c_driver upd64083_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "upd64083", - }, - .probe = upd64083_probe, - .remove = upd64083_remove, - .id_table = upd64083_id, -}; - -module_i2c_driver(upd64083_driver); diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c deleted file mode 100644 index 7cfbc9d94a48..000000000000 --- a/drivers/media/video/vp27smpx.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * vp27smpx - driver version 0.0.1 - * - * Copyright (C) 2007 Hans Verkuil - * - * Based on a tvaudio patch from Takahiro Adachi - * and Kazuhiko Kawakami - * - * This program is free software; 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 - -MODULE_DESCRIPTION("vp27smpx driver"); -MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); - - -/* ----------------------------------------------------------------------- */ - -struct vp27smpx_state { - struct v4l2_subdev sd; - int radio; - u32 audmode; -}; - -static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vp27smpx_state, sd); -} - -static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) -{ - struct vp27smpx_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 data[3] = { 0x00, 0x00, 0x04 }; - - switch (audmode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_LANG1: - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - data[1] = 0x01; - break; - case V4L2_TUNER_MODE_LANG2: - data[1] = 0x02; - break; - } - - if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) - v4l2_err(sd, "I/O error setting audmode\n"); - else - state->audmode = audmode; -} - -static int vp27smpx_s_radio(struct v4l2_subdev *sd) -{ - struct vp27smpx_state *state = to_state(sd); - - state->radio = 1; - return 0; -} - -static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct vp27smpx_state *state = to_state(sd); - - state->radio = 0; - return 0; -} - -static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct vp27smpx_state *state = to_state(sd); - - if (!state->radio) - vp27smpx_set_audmode(sd, vt->audmode); - return 0; -} - -static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct vp27smpx_state *state = to_state(sd); - - if (state->radio) - return 0; - vt->audmode = state->audmode; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - return 0; -} - -static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); -} - -static int vp27smpx_log_status(struct v4l2_subdev *sd) -{ - struct vp27smpx_state *state = to_state(sd); - - v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, - state->radio ? " (Radio)" : ""); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { - .log_status = vp27smpx_log_status, - .g_chip_ident = vp27smpx_g_chip_ident, - .s_std = vp27smpx_s_std, -}; - -static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { - .s_radio = vp27smpx_s_radio, - .s_tuner = vp27smpx_s_tuner, - .g_tuner = vp27smpx_g_tuner, -}; - -static const struct v4l2_subdev_ops vp27smpx_ops = { - .core = &vp27smpx_core_ops, - .tuner = &vp27smpx_tuner_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int vp27smpx_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct vp27smpx_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); - state->audmode = V4L2_TUNER_MODE_STEREO; - - /* initialize vp27smpx */ - vp27smpx_set_audmode(sd, state->audmode); - return 0; -} - -static int vp27smpx_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id vp27smpx_id[] = { - { "vp27smpx", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, vp27smpx_id); - -static struct i2c_driver vp27smpx_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "vp27smpx", - }, - .probe = vp27smpx_probe, - .remove = vp27smpx_remove, - .id_table = vp27smpx_id, -}; - -module_i2c_driver(vp27smpx_driver); diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c deleted file mode 100644 index 2f67b4c5c823..000000000000 --- a/drivers/media/video/vpx3220.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 - * - * Copyright (C) 2001 Laurent Pinchart - * - * This program is free software; 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 -#include -#include - -MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); -MODULE_AUTHOR("Laurent Pinchart"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -#define VPX_TIMEOUT_COUNT 10 - -/* ----------------------------------------------------------------------- */ - -struct vpx3220 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - unsigned char reg[255]; - - v4l2_std_id norm; - int ident; - int input; - int enable; -}; - -static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vpx3220, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct vpx3220, hdl)->sd; -} - -static char *inputs[] = { "internal", "composite", "svideo" }; - -/* ----------------------------------------------------------------------- */ - -static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct vpx3220 *decoder = i2c_get_clientdata(client); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int vpx3220_fp_status(struct v4l2_subdev *sd) -{ - unsigned char status; - unsigned int i; - - for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { - status = vpx3220_read(sd, 0x29); - - if (!(status & 4)) - return 0; - - udelay(10); - - if (need_resched()) - cond_resched(); - } - - return -1; -} - -static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* Write the 16-bit address to the FPWR register */ - if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - if (vpx3220_fp_status(sd) < 0) - return -1; - - /* Write the 16-bit data to the FPDAT register */ - if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - return 0; -} - -static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - s16 data; - - /* Write the 16-bit address to the FPRD register */ - if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - if (vpx3220_fp_status(sd) < 0) - return -1; - - /* Read the 16-bit data from the FPDAT register */ - data = i2c_smbus_read_word_data(client, 0x28); - if (data == -1) { - v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); - return -1; - } - - return swab16(data); -} - -static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) -{ - u8 reg; - int ret = -1; - - while (len >= 2) { - reg = *data++; - ret = vpx3220_write(sd, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - - return ret; -} - -static int vpx3220_write_fp_block(struct v4l2_subdev *sd, - const u16 *data, unsigned int len) -{ - u8 reg; - int ret = 0; - - while (len > 1) { - reg = *data++; - ret |= vpx3220_fp_write(sd, reg, *data++); - len -= 2; - } - - return ret; -} - -/* ---------------------------------------------------------------------- */ - -static const unsigned short init_ntsc[] = { - 0x1c, 0x00, /* NTSC tint angle */ - 0x88, 17, /* Window 1 vertical */ - 0x89, 240, /* Vertical lines in */ - 0x8a, 240, /* Vertical lines out */ - 0x8b, 000, /* Horizontal begin */ - 0x8c, 640, /* Horizontal length */ - 0x8d, 640, /* Number of pixels */ - 0x8f, 0xc00, /* Disable window 2 */ - 0xf0, 0x73, /* 13.5 MHz transport, Forced - * mode, latch windows */ - 0xf2, 0x13, /* NTSC M, composite input */ - 0xe7, 0x1e1, /* Enable vertical standard - * locking @ 240 lines */ -}; - -static const unsigned short init_pal[] = { - 0x88, 23, /* Window 1 vertical begin */ - 0x89, 288, /* Vertical lines in (16 lines - * skipped by the VFE) */ - 0x8a, 288, /* Vertical lines out (16 lines - * skipped by the VFE) */ - 0x8b, 16, /* Horizontal begin */ - 0x8c, 768, /* Horizontal length */ - 0x8d, 784, /* Number of pixels - * Must be >= Horizontal begin + Horizontal length */ - 0x8f, 0xc00, /* Disable window 2 */ - 0xf0, 0x77, /* 13.5 MHz transport, Forced - * mode, latch windows */ - 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */ - 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ -}; - -static const unsigned short init_secam[] = { - 0x88, 23, /* Window 1 vertical begin */ - 0x89, 288, /* Vertical lines in (16 lines - * skipped by the VFE) */ - 0x8a, 288, /* Vertical lines out (16 lines - * skipped by the VFE) */ - 0x8b, 16, /* Horizontal begin */ - 0x8c, 768, /* Horizontal length */ - 0x8d, 784, /* Number of pixels - * Must be >= Horizontal begin + Horizontal length */ - 0x8f, 0xc00, /* Disable window 2 */ - 0xf0, 0x77, /* 13.5 MHz transport, Forced - * mode, latch windows */ - 0xf2, 0x3d5, /* SECAM, composite input */ - 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ -}; - -static const unsigned char init_common[] = { - 0xf2, 0x00, /* Disable all outputs */ - 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN - * (clamp off) */ - 0xd8, 0xa8, /* HREF/VREF active high, VREF - * pulse = 2, Odd/Even flag */ - 0x20, 0x03, /* IF compensation 0dB/oct */ - 0xe0, 0xff, /* Open up all comparators */ - 0xe1, 0x00, - 0xe2, 0x7f, - 0xe3, 0x80, - 0xe4, 0x7f, - 0xe5, 0x80, - 0xe6, 0x00, /* Brightness set to 0 */ - 0xe7, 0xe0, /* Contrast to 1.0, noise shaping - * 10 to 8 2-bit error diffusion */ - 0xe8, 0xf8, /* YUV422, CbCr binary offset, - * ... (p.32) */ - 0xea, 0x18, /* LLC2 connected, output FIFO - * reset with VACTintern */ - 0xf0, 0x8a, /* Half full level to 10, bus - * shuffler [7:0, 23:16, 15:8] */ - 0xf1, 0x18, /* Single clock, sync mode, no - * FE delay, no HLEN counter */ - 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE# - * strength to 2 */ - 0xf9, 0x24, /* Port B, HREF, VREF, PREF & - * ALPHA strength to 4 */ -}; - -static const unsigned short init_fp[] = { - 0x59, 0, - 0xa0, 2070, /* ACC reference */ - 0xa3, 0, - 0xa4, 0, - 0xa8, 30, - 0xb2, 768, - 0xbe, 27, - 0x58, 0, - 0x26, 0, - 0x4b, 0x298, /* PLL gain */ -}; - - -static int vpx3220_init(struct v4l2_subdev *sd, u32 val) -{ - struct vpx3220 *decoder = to_vpx3220(sd); - - vpx3220_write_block(sd, init_common, sizeof(init_common)); - vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); - if (decoder->norm & V4L2_STD_NTSC) - vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); - else if (decoder->norm & V4L2_STD_PAL) - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - else if (decoder->norm & V4L2_STD_SECAM) - vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); - else - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - return 0; -} - -static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) -{ - int res = V4L2_IN_ST_NO_SIGNAL, status; - v4l2_std_id std = 0; - - status = vpx3220_fp_read(sd, 0x0f3); - - v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); - - if (status < 0) - return status; - - if ((status & 0x20) == 0) { - res = 0; - - switch (status & 0x18) { - case 0x00: - case 0x10: - case 0x14: - case 0x18: - std = V4L2_STD_PAL; - break; - - case 0x08: - std = V4L2_STD_SECAM; - break; - - case 0x04: - case 0x0c: - case 0x1c: - std = V4L2_STD_NTSC; - break; - } - } - if (pstd) - *pstd = std; - if (pstatus) - *pstatus = res; - return 0; -} - -static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - v4l2_dbg(1, debug, sd, "querystd\n"); - return vpx3220_status(sd, NULL, std); -} - -static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - v4l2_dbg(1, debug, sd, "g_input_status\n"); - return vpx3220_status(sd, status, NULL); -} - -static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct vpx3220 *decoder = to_vpx3220(sd); - int temp_input; - - /* Here we back up the input selection because it gets - overwritten when we fill the registers with the - chosen video norm */ - temp_input = vpx3220_fp_read(sd, 0xf2); - - v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); - if (std & V4L2_STD_NTSC) { - vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); - v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); - } else if (std & V4L2_STD_PAL) { - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); - } else if (std & V4L2_STD_SECAM) { - vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); - v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); - } else { - return -EINVAL; - } - - decoder->norm = std; - - /* And here we set the backed up video input again */ - vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); - udelay(10); - return 0; -} - -static int vpx3220_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - int data; - - /* RJ: input = 0: ST8 (PCTV) input - input = 1: COMPOSITE input - input = 2: SVHS input */ - - const int input_vals[3][2] = { - {0x0c, 0}, - {0x0d, 0}, - {0x0e, 1} - }; - - if (input > 2) - return -EINVAL; - - v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); - - vpx3220_write(sd, 0x33, input_vals[input][0]); - - data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); - if (data < 0) - return data; - /* 0x0010 is required to latch the setting */ - vpx3220_fp_write(sd, 0xf2, - data | (input_vals[input][1] << 5) | 0x0010); - - udelay(10); - return 0; -} - -static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) -{ - v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); - - vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); - return 0; -} - -static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - vpx3220_write(sd, 0xe6, ctrl->val); - return 0; - case V4L2_CID_CONTRAST: - /* Bit 7 and 8 is for noise shaping */ - vpx3220_write(sd, 0xe7, ctrl->val + 192); - return 0; - case V4L2_CID_SATURATION: - vpx3220_fp_write(sd, 0xa0, ctrl->val); - return 0; - case V4L2_CID_HUE: - vpx3220_fp_write(sd, 0x1c, ctrl->val); - return 0; - } - return -EINVAL; -} - -static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct vpx3220 *decoder = to_vpx3220(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { - .s_ctrl = vpx3220_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops vpx3220_core_ops = { - .g_chip_ident = vpx3220_g_chip_ident, - .init = vpx3220_init, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .s_std = vpx3220_s_std, -}; - -static const struct v4l2_subdev_video_ops vpx3220_video_ops = { - .s_routing = vpx3220_s_routing, - .s_stream = vpx3220_s_stream, - .querystd = vpx3220_querystd, - .g_input_status = vpx3220_g_input_status, -}; - -static const struct v4l2_subdev_ops vpx3220_ops = { - .core = &vpx3220_core_ops, - .video = &vpx3220_video_ops, -}; - -/* ----------------------------------------------------------------------- - * Client management code - */ - -static int vpx3220_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct vpx3220 *decoder; - struct v4l2_subdev *sd; - const char *name = NULL; - u8 ver; - u16 pn; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; - - decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); - decoder->norm = V4L2_STD_PAL; - decoder->input = 0; - decoder->enable = 1; - v4l2_ctrl_handler_init(&decoder->hdl, 4); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_CONTRAST, 0, 63, 1, 32); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_SATURATION, 0, 4095, 1, 2048); - v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, - V4L2_CID_HUE, -512, 511, 1, 0); - sd->ctrl_handler = &decoder->hdl; - if (decoder->hdl.error) { - int err = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return err; - } - v4l2_ctrl_handler_setup(&decoder->hdl); - - ver = i2c_smbus_read_byte_data(client, 0x00); - pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + - i2c_smbus_read_byte_data(client, 0x01); - decoder->ident = V4L2_IDENT_VPX3220A; - if (ver == 0xec) { - switch (pn) { - case 0x4680: - name = "vpx3220a"; - break; - case 0x4260: - name = "vpx3216b"; - decoder->ident = V4L2_IDENT_VPX3216B; - break; - case 0x4280: - name = "vpx3214c"; - decoder->ident = V4L2_IDENT_VPX3214C; - break; - } - } - if (name) - v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, - client->addr << 1, client->adapter->name); - else - v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", - ver, pn, client->addr << 1, client->adapter->name); - - vpx3220_write_block(sd, init_common, sizeof(init_common)); - vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); - /* Default to PAL */ - vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); - return 0; -} - -static int vpx3220_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct vpx3220 *decoder = to_vpx3220(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&decoder->hdl); - kfree(decoder); - return 0; -} - -static const struct i2c_device_id vpx3220_id[] = { - { "vpx3220a", 0 }, - { "vpx3216b", 0 }, - { "vpx3214c", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, vpx3220_id); - -static struct i2c_driver vpx3220_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "vpx3220", - }, - .probe = vpx3220_probe, - .remove = vpx3220_remove, - .id_table = vpx3220_id, -}; - -module_i2c_driver(vpx3220_driver); diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c deleted file mode 100644 index 42ae9dc9c574..000000000000 --- a/drivers/media/video/vs6624.c +++ /dev/null @@ -1,928 +0,0 @@ -/* - * vs6624.c ST VS6624 CMOS image sensor driver - * - * Copyright (c) 2011 Analog Devices 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. - * - * 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 - -#include -#include -#include -#include - -#include "vs6624_regs.h" - -#define VGA_WIDTH 640 -#define VGA_HEIGHT 480 -#define QVGA_WIDTH 320 -#define QVGA_HEIGHT 240 -#define QQVGA_WIDTH 160 -#define QQVGA_HEIGHT 120 -#define CIF_WIDTH 352 -#define CIF_HEIGHT 288 -#define QCIF_WIDTH 176 -#define QCIF_HEIGHT 144 -#define QQCIF_WIDTH 88 -#define QQCIF_HEIGHT 72 - -#define MAX_FRAME_RATE 30 - -struct vs6624 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct v4l2_fract frame_rate; - struct v4l2_mbus_framefmt fmt; - unsigned ce_pin; -}; - -static const struct vs6624_format { - enum v4l2_mbus_pixelcode mbus_code; - enum v4l2_colorspace colorspace; -} vs6624_formats[] = { - { - .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - { - .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - { - .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, -}; - -static struct v4l2_mbus_framefmt vs6624_default_fmt = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .code = V4L2_MBUS_FMT_UYVY8_2X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, -}; - -static const u16 vs6624_p1[] = { - 0x8104, 0x03, - 0x8105, 0x01, - 0xc900, 0x03, - 0xc904, 0x47, - 0xc905, 0x10, - 0xc906, 0x80, - 0xc907, 0x3a, - 0x903a, 0x02, - 0x903b, 0x47, - 0x903c, 0x15, - 0xc908, 0x31, - 0xc909, 0xdc, - 0xc90a, 0x80, - 0xc90b, 0x44, - 0x9044, 0x02, - 0x9045, 0x31, - 0x9046, 0xe2, - 0xc90c, 0x07, - 0xc90d, 0xe0, - 0xc90e, 0x80, - 0xc90f, 0x47, - 0x9047, 0x90, - 0x9048, 0x83, - 0x9049, 0x81, - 0x904a, 0xe0, - 0x904b, 0x60, - 0x904c, 0x08, - 0x904d, 0x90, - 0x904e, 0xc0, - 0x904f, 0x43, - 0x9050, 0x74, - 0x9051, 0x01, - 0x9052, 0xf0, - 0x9053, 0x80, - 0x9054, 0x05, - 0x9055, 0xE4, - 0x9056, 0x90, - 0x9057, 0xc0, - 0x9058, 0x43, - 0x9059, 0xf0, - 0x905a, 0x02, - 0x905b, 0x07, - 0x905c, 0xec, - 0xc910, 0x5d, - 0xc911, 0xca, - 0xc912, 0x80, - 0xc913, 0x5d, - 0x905d, 0xa3, - 0x905e, 0x04, - 0x905f, 0xf0, - 0x9060, 0xa3, - 0x9061, 0x04, - 0x9062, 0xf0, - 0x9063, 0x22, - 0xc914, 0x72, - 0xc915, 0x92, - 0xc916, 0x80, - 0xc917, 0x64, - 0x9064, 0x74, - 0x9065, 0x01, - 0x9066, 0x02, - 0x9067, 0x72, - 0x9068, 0x95, - 0xc918, 0x47, - 0xc919, 0xf2, - 0xc91a, 0x81, - 0xc91b, 0x69, - 0x9169, 0x74, - 0x916a, 0x02, - 0x916b, 0xf0, - 0x916c, 0xec, - 0x916d, 0xb4, - 0x916e, 0x10, - 0x916f, 0x0a, - 0x9170, 0x90, - 0x9171, 0x80, - 0x9172, 0x16, - 0x9173, 0xe0, - 0x9174, 0x70, - 0x9175, 0x04, - 0x9176, 0x90, - 0x9177, 0xd3, - 0x9178, 0xc4, - 0x9179, 0xf0, - 0x917a, 0x22, - 0xc91c, 0x0a, - 0xc91d, 0xbe, - 0xc91e, 0x80, - 0xc91f, 0x73, - 0x9073, 0xfc, - 0x9074, 0xa3, - 0x9075, 0xe0, - 0x9076, 0xf5, - 0x9077, 0x82, - 0x9078, 0x8c, - 0x9079, 0x83, - 0x907a, 0xa3, - 0x907b, 0xa3, - 0x907c, 0xe0, - 0x907d, 0xfc, - 0x907e, 0xa3, - 0x907f, 0xe0, - 0x9080, 0xc3, - 0x9081, 0x9f, - 0x9082, 0xff, - 0x9083, 0xec, - 0x9084, 0x9e, - 0x9085, 0xfe, - 0x9086, 0x02, - 0x9087, 0x0a, - 0x9088, 0xea, - 0xc920, 0x47, - 0xc921, 0x38, - 0xc922, 0x80, - 0xc923, 0x89, - 0x9089, 0xec, - 0x908a, 0xd3, - 0x908b, 0x94, - 0x908c, 0x20, - 0x908d, 0x40, - 0x908e, 0x01, - 0x908f, 0x1c, - 0x9090, 0x90, - 0x9091, 0xd3, - 0x9092, 0xd4, - 0x9093, 0xec, - 0x9094, 0xf0, - 0x9095, 0x02, - 0x9096, 0x47, - 0x9097, 0x3d, - 0xc924, 0x45, - 0xc925, 0xca, - 0xc926, 0x80, - 0xc927, 0x98, - 0x9098, 0x12, - 0x9099, 0x77, - 0x909a, 0xd6, - 0x909b, 0x02, - 0x909c, 0x45, - 0x909d, 0xcd, - 0xc928, 0x20, - 0xc929, 0xd5, - 0xc92a, 0x80, - 0xc92b, 0x9e, - 0x909e, 0x90, - 0x909f, 0x82, - 0x90a0, 0x18, - 0x90a1, 0xe0, - 0x90a2, 0xb4, - 0x90a3, 0x03, - 0x90a4, 0x0e, - 0x90a5, 0x90, - 0x90a6, 0x83, - 0x90a7, 0xbf, - 0x90a8, 0xe0, - 0x90a9, 0x60, - 0x90aa, 0x08, - 0x90ab, 0x90, - 0x90ac, 0x81, - 0x90ad, 0xfc, - 0x90ae, 0xe0, - 0x90af, 0xff, - 0x90b0, 0xc3, - 0x90b1, 0x13, - 0x90b2, 0xf0, - 0x90b3, 0x90, - 0x90b4, 0x81, - 0x90b5, 0xfc, - 0x90b6, 0xe0, - 0x90b7, 0xff, - 0x90b8, 0x02, - 0x90b9, 0x20, - 0x90ba, 0xda, - 0xc92c, 0x70, - 0xc92d, 0xbc, - 0xc92e, 0x80, - 0xc92f, 0xbb, - 0x90bb, 0x90, - 0x90bc, 0x82, - 0x90bd, 0x18, - 0x90be, 0xe0, - 0x90bf, 0xb4, - 0x90c0, 0x03, - 0x90c1, 0x06, - 0x90c2, 0x90, - 0x90c3, 0xc1, - 0x90c4, 0x06, - 0x90c5, 0x74, - 0x90c6, 0x05, - 0x90c7, 0xf0, - 0x90c8, 0x90, - 0x90c9, 0xd3, - 0x90ca, 0xa0, - 0x90cb, 0x02, - 0x90cc, 0x70, - 0x90cd, 0xbf, - 0xc930, 0x72, - 0xc931, 0x21, - 0xc932, 0x81, - 0xc933, 0x3b, - 0x913b, 0x7d, - 0x913c, 0x02, - 0x913d, 0x7f, - 0x913e, 0x7b, - 0x913f, 0x02, - 0x9140, 0x72, - 0x9141, 0x25, - 0xc934, 0x28, - 0xc935, 0xae, - 0xc936, 0x80, - 0xc937, 0xd2, - 0x90d2, 0xf0, - 0x90d3, 0x90, - 0x90d4, 0xd2, - 0x90d5, 0x0a, - 0x90d6, 0x02, - 0x90d7, 0x28, - 0x90d8, 0xb4, - 0xc938, 0x28, - 0xc939, 0xb1, - 0xc93a, 0x80, - 0xc93b, 0xd9, - 0x90d9, 0x90, - 0x90da, 0x83, - 0x90db, 0xba, - 0x90dc, 0xe0, - 0x90dd, 0xff, - 0x90de, 0x90, - 0x90df, 0xd2, - 0x90e0, 0x08, - 0x90e1, 0xe0, - 0x90e2, 0xe4, - 0x90e3, 0xef, - 0x90e4, 0xf0, - 0x90e5, 0xa3, - 0x90e6, 0xe0, - 0x90e7, 0x74, - 0x90e8, 0xff, - 0x90e9, 0xf0, - 0x90ea, 0x90, - 0x90eb, 0xd2, - 0x90ec, 0x0a, - 0x90ed, 0x02, - 0x90ee, 0x28, - 0x90ef, 0xb4, - 0xc93c, 0x29, - 0xc93d, 0x79, - 0xc93e, 0x80, - 0xc93f, 0xf0, - 0x90f0, 0xf0, - 0x90f1, 0x90, - 0x90f2, 0xd2, - 0x90f3, 0x0e, - 0x90f4, 0x02, - 0x90f5, 0x29, - 0x90f6, 0x7f, - 0xc940, 0x29, - 0xc941, 0x7c, - 0xc942, 0x80, - 0xc943, 0xf7, - 0x90f7, 0x90, - 0x90f8, 0x83, - 0x90f9, 0xba, - 0x90fa, 0xe0, - 0x90fb, 0xff, - 0x90fc, 0x90, - 0x90fd, 0xd2, - 0x90fe, 0x0c, - 0x90ff, 0xe0, - 0x9100, 0xe4, - 0x9101, 0xef, - 0x9102, 0xf0, - 0x9103, 0xa3, - 0x9104, 0xe0, - 0x9105, 0x74, - 0x9106, 0xff, - 0x9107, 0xf0, - 0x9108, 0x90, - 0x9109, 0xd2, - 0x910a, 0x0e, - 0x910b, 0x02, - 0x910c, 0x29, - 0x910d, 0x7f, - 0xc944, 0x2a, - 0xc945, 0x42, - 0xc946, 0x81, - 0xc947, 0x0e, - 0x910e, 0xf0, - 0x910f, 0x90, - 0x9110, 0xd2, - 0x9111, 0x12, - 0x9112, 0x02, - 0x9113, 0x2a, - 0x9114, 0x48, - 0xc948, 0x2a, - 0xc949, 0x45, - 0xc94a, 0x81, - 0xc94b, 0x15, - 0x9115, 0x90, - 0x9116, 0x83, - 0x9117, 0xba, - 0x9118, 0xe0, - 0x9119, 0xff, - 0x911a, 0x90, - 0x911b, 0xd2, - 0x911c, 0x10, - 0x911d, 0xe0, - 0x911e, 0xe4, - 0x911f, 0xef, - 0x9120, 0xf0, - 0x9121, 0xa3, - 0x9122, 0xe0, - 0x9123, 0x74, - 0x9124, 0xff, - 0x9125, 0xf0, - 0x9126, 0x90, - 0x9127, 0xd2, - 0x9128, 0x12, - 0x9129, 0x02, - 0x912a, 0x2a, - 0x912b, 0x48, - 0xc900, 0x01, - 0x0000, 0x00, -}; - -static const u16 vs6624_p2[] = { - 0x806f, 0x01, - 0x058c, 0x01, - 0x0000, 0x00, -}; - -static const u16 vs6624_run_setup[] = { - 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ - VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ - VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ - VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ - VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ - VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ - VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ - VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ - VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ - VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ - VS6624_NORA_USAGE, 0x04, /* Nora usage */ - VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ - VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ - VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ - VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ - VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ - VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ - VS6624_F2B_DISABLE, 0x00, /* Disable */ - 0x1d8a, 0x30, /* MAXWeightHigh */ - 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ - 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ - 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ - 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ - 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ - 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ - 0x1e08, 0x06, /* MAXWeightLow */ - 0x1e0a, 0x0a, /* MAXWeightHigh */ - 0x1601, 0x3a, /* Red A MSB */ - 0x1602, 0x14, /* Red A LSB */ - 0x1605, 0x3b, /* Blue A MSB */ - 0x1606, 0x85, /* BLue A LSB */ - 0x1609, 0x3b, /* RED B MSB */ - 0x160a, 0x85, /* RED B LSB */ - 0x160d, 0x3a, /* Blue B MSB */ - 0x160e, 0x14, /* Blue B LSB */ - 0x1611, 0x30, /* Max Distance from Locus MSB */ - 0x1612, 0x8f, /* Max Distance from Locus MSB */ - 0x1614, 0x01, /* Enable constrainer */ - 0x0000, 0x00, -}; - -static const u16 vs6624_default[] = { - VS6624_CONTRAST0, 0x84, - VS6624_SATURATION0, 0x75, - VS6624_GAMMA0, 0x11, - VS6624_CONTRAST1, 0x84, - VS6624_SATURATION1, 0x75, - VS6624_GAMMA1, 0x11, - VS6624_MAN_RG, 0x80, - VS6624_MAN_GG, 0x80, - VS6624_MAN_BG, 0x80, - VS6624_WB_MODE, 0x1, - VS6624_EXPO_COMPENSATION, 0xfe, - VS6624_EXPO_METER, 0x0, - VS6624_LIGHT_FREQ, 0x64, - VS6624_PEAK_GAIN, 0xe, - VS6624_PEAK_LOW_THR, 0x28, - VS6624_HMIRROR0, 0x0, - VS6624_VFLIP0, 0x0, - VS6624_ZOOM_HSTEP0_MSB, 0x0, - VS6624_ZOOM_HSTEP0_LSB, 0x1, - VS6624_ZOOM_VSTEP0_MSB, 0x0, - VS6624_ZOOM_VSTEP0_LSB, 0x1, - VS6624_PAN_HSTEP0_MSB, 0x0, - VS6624_PAN_HSTEP0_LSB, 0xf, - VS6624_PAN_VSTEP0_MSB, 0x0, - VS6624_PAN_VSTEP0_LSB, 0xf, - VS6624_SENSOR_MODE, 0x1, - VS6624_SYNC_CODE_SETUP, 0x21, - VS6624_DISABLE_FR_DAMPER, 0x0, - VS6624_FR_DEN, 0x1, - VS6624_FR_NUM_LSB, 0xf, - VS6624_INIT_PIPE_SETUP, 0x0, - VS6624_IMG_FMT0, 0x0, - VS6624_YUV_SETUP, 0x1, - VS6624_IMAGE_SIZE0, 0x2, - 0x0000, 0x00, -}; - -static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vs6624, sd); -} -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct vs6624, hdl)->sd; -} - -static int vs6624_read(struct v4l2_subdev *sd, u16 index) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - buf[0] = index >> 8; - buf[1] = index; - i2c_master_send(client, buf, 2); - i2c_master_recv(client, buf, 1); - - return buf[0]; -} - -static int vs6624_write(struct v4l2_subdev *sd, u16 index, - u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[3]; - - buf[0] = index >> 8; - buf[1] = index; - buf[2] = value; - - return i2c_master_send(client, buf, 3); -} - -static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) -{ - u16 reg; - u8 data; - - while (*regs != 0x00) { - reg = *regs++; - data = *regs++; - - vs6624_write(sd, reg, data); - } - return 0; -} - -static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); - break; - case V4L2_CID_SATURATION: - vs6624_write(sd, VS6624_SATURATION0, ctrl->val); - break; - case V4L2_CID_HFLIP: - vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); - break; - case V4L2_CID_VFLIP: - vs6624_write(sd, VS6624_VFLIP0, ctrl->val); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) -{ - if (index >= ARRAY_SIZE(vs6624_formats)) - return -EINVAL; - - *code = vs6624_formats[index].mbus_code; - return 0; -} - -static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - int index; - - for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) - if (vs6624_formats[index].mbus_code == fmt->code) - break; - if (index >= ARRAY_SIZE(vs6624_formats)) { - /* default to first format */ - index = 0; - fmt->code = vs6624_formats[0].mbus_code; - } - - /* sensor mode is VGA */ - if (fmt->width > VGA_WIDTH) - fmt->width = VGA_WIDTH; - if (fmt->height > VGA_HEIGHT) - fmt->height = VGA_HEIGHT; - fmt->width = fmt->width & (~3); - fmt->height = fmt->height & (~3); - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = vs6624_formats[index].colorspace; - return 0; -} - -static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct vs6624 *sensor = to_vs6624(sd); - int ret; - - ret = vs6624_try_mbus_fmt(sd, fmt); - if (ret) - return ret; - - /* set image format */ - switch (fmt->code) { - case V4L2_MBUS_FMT_UYVY8_2X8: - vs6624_write(sd, VS6624_IMG_FMT0, 0x0); - vs6624_write(sd, VS6624_YUV_SETUP, 0x1); - break; - case V4L2_MBUS_FMT_YUYV8_2X8: - vs6624_write(sd, VS6624_IMG_FMT0, 0x0); - vs6624_write(sd, VS6624_YUV_SETUP, 0x3); - break; - case V4L2_MBUS_FMT_RGB565_2X8_LE: - vs6624_write(sd, VS6624_IMG_FMT0, 0x4); - vs6624_write(sd, VS6624_RGB_SETUP, 0x0); - break; - default: - return -EINVAL; - } - - /* set image size */ - if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); - else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); - else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); - else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); - else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); - else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); - else { - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); - vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); - vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); - vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); - vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); - vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); - } - - sensor->fmt = *fmt; - - return 0; -} - -static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct vs6624 *sensor = to_vs6624(sd); - - *fmt = sensor->fmt; - return 0; -} - -static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct vs6624 *sensor = to_vs6624(sd); - struct v4l2_captureparm *cp = &parms->parm.capture; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(cp, 0, sizeof(*cp)); - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = sensor->frame_rate.denominator; - cp->timeperframe.denominator = sensor->frame_rate.numerator; - return 0; -} - -static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -{ - struct vs6624 *sensor = to_vs6624(sd); - struct v4l2_captureparm *cp = &parms->parm.capture; - struct v4l2_fract *tpf = &cp->timeperframe; - - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; - - if (tpf->numerator == 0 || tpf->denominator == 0 - || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { - /* reset to max frame rate */ - tpf->numerator = 1; - tpf->denominator = MAX_FRAME_RATE; - } - sensor->frame_rate.numerator = tpf->denominator; - sensor->frame_rate.denominator = tpf->numerator; - vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); - vs6624_write(sd, VS6624_FR_NUM_MSB, - sensor->frame_rate.numerator >> 8); - vs6624_write(sd, VS6624_FR_NUM_LSB, - sensor->frame_rate.numerator & 0xFF); - vs6624_write(sd, VS6624_FR_DEN, - sensor->frame_rate.denominator & 0xFF); - return 0; -} - -static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) -{ - if (enable) - vs6624_write(sd, VS6624_USER_CMD, 0x2); - else - vs6624_write(sd, VS6624_USER_CMD, 0x4); - udelay(100); - return 0; -} - -static int vs6624_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - int rev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8) - | vs6624_read(sd, VS6624_FW_VSN_MINOR); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = vs6624_read(sd, reg->reg & 0xffff); - reg->size = 1; - return 0; -} - -static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (!v4l2_chip_match_i2c_client(client, ®->match)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); - return 0; -} -#endif - -static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { - .s_ctrl = vs6624_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops vs6624_core_ops = { - .g_chip_ident = vs6624_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = vs6624_g_register, - .s_register = vs6624_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops vs6624_video_ops = { - .enum_mbus_fmt = vs6624_enum_mbus_fmt, - .try_mbus_fmt = vs6624_try_mbus_fmt, - .s_mbus_fmt = vs6624_s_mbus_fmt, - .g_mbus_fmt = vs6624_g_mbus_fmt, - .s_parm = vs6624_s_parm, - .g_parm = vs6624_g_parm, - .s_stream = vs6624_s_stream, -}; - -static const struct v4l2_subdev_ops vs6624_ops = { - .core = &vs6624_core_ops, - .video = &vs6624_video_ops, -}; - -static int __devinit vs6624_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct vs6624 *sensor; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - const unsigned *ce; - int ret; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -EIO; - - ce = client->dev.platform_data; - if (ce == NULL) - return -EINVAL; - - ret = gpio_request(*ce, "VS6624 Chip Enable"); - if (ret) { - v4l_err(client, "failed to request GPIO %d\n", *ce); - return ret; - } - gpio_direction_output(*ce, 1); - /* wait 100ms before any further i2c writes are performed */ - mdelay(100); - - sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) { - gpio_free(*ce); - return -ENOMEM; - } - - sd = &sensor->sd; - v4l2_i2c_subdev_init(sd, client, &vs6624_ops); - - vs6624_writeregs(sd, vs6624_p1); - vs6624_write(sd, VS6624_MICRO_EN, 0x2); - vs6624_write(sd, VS6624_DIO_EN, 0x1); - mdelay(10); - vs6624_writeregs(sd, vs6624_p2); - - vs6624_writeregs(sd, vs6624_default); - vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); - vs6624_writeregs(sd, vs6624_run_setup); - - /* set frame rate */ - sensor->frame_rate.numerator = MAX_FRAME_RATE; - sensor->frame_rate.denominator = 1; - vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); - vs6624_write(sd, VS6624_FR_NUM_MSB, - sensor->frame_rate.numerator >> 8); - vs6624_write(sd, VS6624_FR_NUM_LSB, - sensor->frame_rate.numerator & 0xFF); - vs6624_write(sd, VS6624_FR_DEN, - sensor->frame_rate.denominator & 0xFF); - - sensor->fmt = vs6624_default_fmt; - sensor->ce_pin = *ce; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - hdl = &sensor->hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - /* hook the control handler into the driver */ - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - kfree(sensor); - gpio_free(*ce); - return err; - } - - /* initialize the hardware to the default control values */ - ret = v4l2_ctrl_handler_setup(hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - kfree(sensor); - gpio_free(*ce); - } - return ret; -} - -static int __devexit vs6624_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct vs6624 *sensor = to_vs6624(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - gpio_free(sensor->ce_pin); - kfree(sensor); - return 0; -} - -static const struct i2c_device_id vs6624_id[] = { - {"vs6624", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, vs6624_id); - -static struct i2c_driver vs6624_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "vs6624", - }, - .probe = vs6624_probe, - .remove = __devexit_p(vs6624_remove), - .id_table = vs6624_id, -}; - -static __init int vs6624_init(void) -{ - return i2c_add_driver(&vs6624_driver); -} - -static __exit void vs6624_exit(void) -{ - i2c_del_driver(&vs6624_driver); -} - -module_init(vs6624_init); -module_exit(vs6624_exit); - -MODULE_DESCRIPTION("VS6624 sensor driver"); -MODULE_AUTHOR("Scott Jiang "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h deleted file mode 100644 index 6ba2ee25827e..000000000000 --- a/drivers/media/video/vs6624_regs.h +++ /dev/null @@ -1,337 +0,0 @@ -/* - * vs6624 - ST VS6624 CMOS image sensor registers - * - * Copyright (c) 2011 Analog Devices 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. - * - * 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 _VS6624_REGS_H_ -#define _VS6624_REGS_H_ - -/* low level control registers */ -#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ -#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ -/* device parameters */ -#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ -#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ -#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ -#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ -#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ -#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ -/* host interface manager control */ -#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ -/* host interface manager status */ -#define VS6624_STATE 0x0202 /* current state of the mode manager */ -/* run mode control */ -#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ -/* mode setup */ -#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ -#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ -/* pipe setup bank0 */ -#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ -#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ -#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ -#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ -#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ -#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ -#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ -#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ -#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ -#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ -#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ -#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ -#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ -#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ -#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ -#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ -#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ -#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ -#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ -#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ -#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ -#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ -#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ -#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ -#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ -#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ -#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ -#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ -#define VS6624_GAMMA0 0x03B8 /* gamma settings */ -#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ -#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ -#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ -/* pipe setup bank1 */ -#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ -#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ -#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ -#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ -#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ -#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ -#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ -#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ -#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ -#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ -#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ -#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ -#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ -#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ -#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ -#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ -#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ -#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ -#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ -#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ -#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ -#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ -#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ -#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ -#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ -#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ -#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ -#define VS6624_SATURATION1 0x0436 /* saturation control for output */ -#define VS6624_GAMMA1 0x0438 /* gamma settings */ -#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ -#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ -#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ -/* view live control */ -#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ -#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ -/* view live status */ -#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ -/* power management */ -#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ -/* video timing parameter host inputs */ -#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ -#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ -#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ -/* video timing control */ -#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ -/* frame dimension parameter host inputs */ -#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ -#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ -/* static frame rate control */ -#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ -#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ -#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ -/* automatic frame rate control */ -#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ -#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ -#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ -/* exposure controls */ -#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ -#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ -#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ -#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ -#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ -#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ -#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ -#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ -#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ -#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ -#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ -#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ -#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ -#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ -#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ -#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ -#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ -#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ -#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ -#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ -#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ -#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ -#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ -#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ -#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ -#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ -#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ -#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ -#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ -/* white balance control */ -#define VS6624_WB_MODE 0x1480 /* set white balance mode */ -#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ -#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ -#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ -#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ -#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ -#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ -#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ -#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ -#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ -/* sensor setup */ -#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ -/* image stability */ -#define VS6624_STABLE_WB 0x1900 /* white balance stable */ -#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ -#define VS6624_STABLE 0x1906 /* system stable */ -/* flash control */ -#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ -#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ -#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ -/* flash status */ -#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ -#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ -/* scythe filter controls */ -#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ -/* jack filter controls */ -#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ -/* demosaic control */ -#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ -/* color matrix dampers */ -#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ -#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ -#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ -#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ -#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ -#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ -#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ -/* peaking control */ -#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ -#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ -#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ -#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ -#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ -#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ -#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ -#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ -#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ -#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ -#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ -#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ -#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ -#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ -#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ -#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ -#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ -/* pipe 0 RGB to YUV matrix manual control */ -#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ -#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ -#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ -#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ -#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ -#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ -#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ -/* pipe 1 RGB to YUV matrix manual control */ -#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ -#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ -#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ -#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ -#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ -#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ -#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ -/* pipe 0 gamma manual control */ -#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ -#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ -#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ -#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ -#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ -#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ -#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ -/* pipe 1 gamma manual control */ -#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ -#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ -#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ -#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ -#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ -#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ -#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ -/* fade to black */ -#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ -#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ -#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ -#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ -#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ -#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ -#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ -#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ -#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ -/* output formatter control */ -#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ -#define VS6624_BLANK_FMT 0x2582 /* blank format */ -#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ -#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ -#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ -#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ -#define VS6624_PCLK_EN 0x258C /* PCLK enable */ -#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ -#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ -#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ -#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ -#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ -#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ -#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ -#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ -#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ -#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ -#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ -#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ -#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ -#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ -#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ -#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ -#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ -#define VS6624_OUT_IF 0x25B0 /* output interface */ -#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ -/* NoRA controls */ -#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ -#define VS6624_NORA_USAGE 0x2602 /* usage */ -#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ -#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ -#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ -#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ -#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ -#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ -#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ -#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ -#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ -#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ - -#endif diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c deleted file mode 100644 index 3bb99e93febe..000000000000 --- a/drivers/media/video/wm8739.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * wm8739 - * - * Copyright (C) 2005 T. Adachi - * - * Copyright (C) 2005 Hans Verkuil - * - Cleanup - * - * This program is free software; 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 -#include - -MODULE_DESCRIPTION("wm8739 driver"); -MODULE_AUTHOR("T. Adachi, Hans Verkuil"); -MODULE_LICENSE("GPL"); - -static int debug; - -module_param(debug, int, 0644); - -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - - -/* ------------------------------------------------------------------------ */ - -enum { - R0 = 0, R1, - R5 = 5, R6, R7, R8, R9, R15 = 15, - TOT_REGS -}; - -struct wm8739_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct { - /* audio cluster */ - struct v4l2_ctrl *volume; - struct v4l2_ctrl *mute; - struct v4l2_ctrl *balance; - }; - u32 clock_freq; -}; - -static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct wm8739_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; -} - -/* ------------------------------------------------------------------------ */ - -static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - if (reg < 0 || reg >= TOT_REGS) { - v4l2_err(sd, "Invalid register R%d\n", reg); - return -1; - } - - v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); - - for (i = 0; i < 3; i++) - if (i2c_smbus_write_byte_data(client, - (reg << 1) | (val >> 8), val & 0xff) == 0) - return 0; - v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); - return -1; -} - -static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct wm8739_state *state = to_state(sd); - unsigned int work_l, work_r; - u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ - u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ - u16 mute; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - break; - - default: - return -EINVAL; - } - - /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ - work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768; - work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768; - - vol_l = (long)work_l * 31 / 65535; - vol_r = (long)work_r * 31 / 65535; - - /* set audio volume etc. */ - mute = state->mute->val ? 0x80 : 0; - - /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB - * Default setting: 0x17 = 0 dB - */ - wm8739_write(sd, R0, (vol_l & 0x1f) | mute); - wm8739_write(sd, R1, (vol_r & 0x1f) | mute); - return 0; -} - -/* ------------------------------------------------------------------------ */ - -static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) -{ - struct wm8739_state *state = to_state(sd); - - state->clock_freq = audiofreq; - /* de-activate */ - wm8739_write(sd, R9, 0x000); - switch (audiofreq) { - case 44100: - /* 256fps, fs=44.1k */ - wm8739_write(sd, R8, 0x020); - break; - case 48000: - /* 256fps, fs=48k */ - wm8739_write(sd, R8, 0x000); - break; - case 32000: - /* 256fps, fs=32k */ - wm8739_write(sd, R8, 0x018); - break; - default: - break; - } - /* activate */ - wm8739_write(sd, R9, 0x001); - return 0; -} - -static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); -} - -static int wm8739_log_status(struct v4l2_subdev *sd) -{ - struct wm8739_state *state = to_state(sd); - - v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { - .s_ctrl = wm8739_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops wm8739_core_ops = { - .log_status = wm8739_log_status, - .g_chip_ident = wm8739_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { - .s_clock_freq = wm8739_s_clock_freq, -}; - -static const struct v4l2_subdev_ops wm8739_ops = { - .core = &wm8739_core_ops, - .audio = &wm8739_audio_ops, -}; - -/* ------------------------------------------------------------------------ */ - -/* i2c implementation */ - -static int wm8739_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct wm8739_state *state; - struct v4l2_subdev *sd; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &wm8739_ops); - v4l2_ctrl_handler_init(&state->hdl, 2); - state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736); - state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - v4l2_ctrl_cluster(3, &state->volume); - - state->clock_freq = 48000; - - /* Initialize wm8739 */ - - /* reset */ - wm8739_write(sd, R15, 0x00); - /* filter setting, high path, offet clear */ - wm8739_write(sd, R5, 0x000); - /* ADC, OSC, Power Off mode Disable */ - wm8739_write(sd, R6, 0x000); - /* Digital Audio interface format: - Enable Master mode, 24 bit, MSB first/left justified */ - wm8739_write(sd, R7, 0x049); - /* sampling control: normal, 256fs, 48KHz sampling rate */ - wm8739_write(sd, R8, 0x000); - /* activate */ - wm8739_write(sd, R9, 0x001); - /* set volume/mute */ - v4l2_ctrl_handler_setup(&state->hdl); - return 0; -} - -static int wm8739_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct wm8739_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id wm8739_id[] = { - { "wm8739", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm8739_id); - -static struct i2c_driver wm8739_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "wm8739", - }, - .probe = wm8739_probe, - .remove = wm8739_remove, - .id_table = wm8739_id, -}; - -module_i2c_driver(wm8739_driver); diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c deleted file mode 100644 index bee77ea9f49e..000000000000 --- a/drivers/media/video/wm8775.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * wm8775 - driver version 0.0.1 - * - * Copyright (C) 2004 Ulf Eklund - * - * Based on saa7115 driver - * - * Copyright (C) 2005 Hans Verkuil - * - Cleanup - * - V4L2 API update - * - sound fixes - * - * This program is free software; 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 -#include -#include - -MODULE_DESCRIPTION("wm8775 driver"); -MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); -MODULE_LICENSE("GPL"); - - - -/* ----------------------------------------------------------------------- */ - -enum { - R7 = 7, R11 = 11, - R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23, - TOT_REGS -}; - -#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */ -#define ALC_EN 0x100 /* R17: ALC enable */ - -struct wm8775_state { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *mute; - struct v4l2_ctrl *vol; - struct v4l2_ctrl *bal; - struct v4l2_ctrl *loud; - u8 input; /* Last selected input (0-0xf) */ -}; - -static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct wm8775_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd; -} - -static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int i; - - if (reg < 0 || reg >= TOT_REGS) { - v4l2_err(sd, "Invalid register R%d\n", reg); - return -1; - } - - for (i = 0; i < 3; i++) - if (i2c_smbus_write_byte_data(client, - (reg << 1) | (val >> 8), val & 0xff) == 0) - return 0; - v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); - return -1; -} - -static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly) -{ - struct wm8775_state *state = to_state(sd); - u8 vol_l, vol_r; - int muted = 0 != state->mute->val; - u16 volume = (u16)state->vol->val; - u16 balance = (u16)state->bal->val; - - /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ - vol_l = (min(65536 - balance, 32768) * volume) >> 23; - vol_r = (min(balance, (u16)32768) * volume) >> 23; - - /* Mute */ - if (muted || quietly) - wm8775_write(sd, R21, 0x0c0 | state->input); - - wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */ - wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */ - - /* Un-mute */ - if (!muted) - wm8775_write(sd, R21, state->input); -} - -static int wm8775_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct wm8775_state *state = to_state(sd); - - /* There are 4 inputs and one output. Zero or more inputs - are multiplexed together to the output. Hence there are - 16 combinations. - If only one input is active (the normal case) then the - input values 1, 2, 4 or 8 should be used. */ - if (input > 15) { - v4l2_err(sd, "Invalid input %d.\n", input); - return -EINVAL; - } - state->input = input; - if (!v4l2_ctrl_g_ctrl(state->mute)) - return 0; - if (!v4l2_ctrl_g_ctrl(state->vol)) - return 0; - if (!v4l2_ctrl_g_ctrl(state->bal)) - return 0; - wm8775_set_audio(sd, 1); - return 0; -} - -static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - wm8775_set_audio(sd, 0); - return 0; - case V4L2_CID_AUDIO_LOUDNESS: - wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD); - return 0; - } - return -EINVAL; -} - -static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); -} - -static int wm8775_log_status(struct v4l2_subdev *sd) -{ - struct wm8775_state *state = to_state(sd); - - v4l2_info(sd, "Input: %d\n", state->input); - v4l2_ctrl_handler_log_status(&state->hdl, sd->name); - return 0; -} - -static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) -{ - wm8775_set_audio(sd, 0); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { - .s_ctrl = wm8775_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops wm8775_core_ops = { - .log_status = wm8775_log_status, - .g_chip_ident = wm8775_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { - .s_frequency = wm8775_s_frequency, -}; - -static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { - .s_routing = wm8775_s_routing, -}; - -static const struct v4l2_subdev_ops wm8775_ops = { - .core = &wm8775_core_ops, - .tuner = &wm8775_tuner_ops, - .audio = &wm8775_audio_ops, -}; - -/* ----------------------------------------------------------------------- */ - -/* i2c implementation */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static int wm8775_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct wm8775_state *state; - struct v4l2_subdev *sd; - int err; - bool is_nova_s = false; - - if (client->dev.platform_data) { - struct wm8775_platform_data *data = client->dev.platform_data; - is_nova_s = data->is_nova_s; - } - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &wm8775_ops); - state->input = 2; - - v4l2_ctrl_handler_init(&state->hdl, 4); - state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/ - state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768); - state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, - V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1); - sd->ctrl_handler = &state->hdl; - err = state->hdl.error; - if (err) { - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - - /* Initialize wm8775 */ - - /* RESET */ - wm8775_write(sd, R23, 0x000); - /* Disable zero cross detect timeout */ - wm8775_write(sd, R7, 0x000); - /* HPF enable, left justified, 24-bit (Philips) mode */ - wm8775_write(sd, R11, 0x021); - /* Master mode, clock ratio 256fs */ - wm8775_write(sd, R12, 0x102); - /* Powered up */ - wm8775_write(sd, R13, 0x000); - - if (!is_nova_s) { - /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(sd, R14, 0x1d4); - /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(sd, R15, 0x1d4); - /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ - wm8775_write(sd, R16, 0x1bf); - /* Enable gain control, use zero cross detection, - ALC hold time 42.6 ms */ - wm8775_write(sd, R17, 0x185); - } else { - /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ - wm8775_write(sd, R16, 0x1bb); - /* Set ALC mode and hold time */ - wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD); - } - /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ - wm8775_write(sd, R18, 0x0a2); - /* Enable noise gate, threshold -72dBfs */ - wm8775_write(sd, R19, 0x005); - if (!is_nova_s) { - /* Transient window 4ms, lower PGA gain limit -1dB */ - wm8775_write(sd, R20, 0x07a); - /* LRBOTH = 1, use input 2. */ - wm8775_write(sd, R21, 0x102); - } else { - /* Transient window 4ms, ALC min gain -5dB */ - wm8775_write(sd, R20, 0x0fb); - - wm8775_set_audio(sd, 1); /* set volume/mute/mux */ - } - return 0; -} - -static int wm8775_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct wm8775_state *state = to_state(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -static const struct i2c_device_id wm8775_id[] = { - { "wm8775", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm8775_id); - -static struct i2c_driver wm8775_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "wm8775", - }, - .probe = wm8775_probe, - .remove = wm8775_remove, - .id_table = wm8775_id, -}; - -module_i2c_driver(wm8775_driver); -- cgit v1.2.3 From e36c92fd63bb4773e6f4bc38ecee11a609ded1cf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Aug 2012 02:58:15 -0300 Subject: [media] em28xx: use after free in em28xx_v4l2_close() We need to move the unlock before the kfree(dev); Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index ecb23df7f16e..78d6ebd712b9 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2264,9 +2264,9 @@ static int em28xx_v4l2_close(struct file *filp) if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); kfree(dev->alt_max_pkt_size); + mutex_unlock(&dev->lock); kfree(dev); kfree(fh); - mutex_unlock(&dev->lock); return 0; } -- cgit v1.2.3 From f367cc1efebdfacfca083e55fdeafa0163054995 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Aug 2012 02:59:48 -0300 Subject: [media] stk1160: unlock on error path stk1160_set_alternate() There are some unlocks missing on error. Signed-off-by: Dan Carpenter Acked-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-v4l.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 360bdbee4270..1ad4ac187727 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -159,8 +159,9 @@ static bool stk1160_set_alternate(struct stk1160 *dev) static int stk1160_start_streaming(struct stk1160 *dev) { - int i, rc; bool new_pkt_size; + int rc = 0; + int i; /* Check device presence */ if (!dev->udev) @@ -183,7 +184,7 @@ static int stk1160_start_streaming(struct stk1160 *dev) if (!dev->isoc_ctl.num_bufs || new_pkt_size) { rc = stk1160_alloc_isoc(dev); if (rc < 0) - return rc; + goto out_unlock; } /* submit urbs and enables IRQ */ @@ -192,7 +193,7 @@ static int stk1160_start_streaming(struct stk1160 *dev) if (rc) { stk1160_err("cannot submit urb[%d] (%d)\n", i, rc); stk1160_uninit_isoc(dev); - return rc; + goto out_unlock; } } @@ -205,9 +206,10 @@ static int stk1160_start_streaming(struct stk1160 *dev) stk1160_dbg("streaming started\n"); +out_unlock: mutex_unlock(&dev->v4l_lock); - return 0; + return rc; } /* Must be called with v4l_lock hold */ -- cgit v1.2.3 From 0590c7130dfb88ddc7bbecd343efae1d3623f209 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Aug 2012 03:03:35 -0300 Subject: [media] stk1160: remove unneeded check "card" is a valid pointer here because we checked snd_card_create() for error returns. Checking after a dereference makes the static checkers complain. Signed-off-by: Dan Carpenter Acked-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-ac97.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-ac97.c b/drivers/media/usb/stk1160/stk1160-ac97.c index 8d325f50c87b..c8583c262c3d 100644 --- a/drivers/media/usb/stk1160/stk1160-ac97.c +++ b/drivers/media/usb/stk1160/stk1160-ac97.c @@ -133,8 +133,7 @@ int stk1160_ac97_register(struct stk1160 *dev) err: dev->snd_card = NULL; - if (card) - snd_card_free(card); + snd_card_free(card); return rc; } -- cgit v1.2.3 From e839776f9dfe1eda232755d5cab6eacc59208b4b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 14 Aug 2012 11:49:46 -0300 Subject: [media] drivers/media/usb/{s2255drv.c, tm6000/tm6000-alsa.c, tm6000/tm6000-input.c}: Remove potential NULL dereferences If the NULL test is necessary, the initialization involving a dereference of the tested value should be moved after the NULL test. The sematic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ type T; expression E; identifier i,fld; statement S; @@ - T i = E->fld; + T i; ... when != E when != i if (E == NULL) S + i = E->fld; // Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/s2255/s2255drv.c | 3 ++- drivers/media/usb/tm6000/tm6000-alsa.c | 3 ++- drivers/media/usb/tm6000/tm6000-input.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 6c7960cc7506..a25513d484f7 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1861,11 +1861,12 @@ static int s2255_release(struct file *file) static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) { struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; + struct s2255_dev *dev; int ret; if (!fh) return -ENODEV; + dev = fh->dev; dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma); if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c index bd07ec707956..813c1ec53608 100644 --- a/drivers/media/usb/tm6000/tm6000-alsa.c +++ b/drivers/media/usb/tm6000/tm6000-alsa.c @@ -487,10 +487,11 @@ error: static int tm6000_audio_fini(struct tm6000_core *dev) { - struct snd_tm6000_card *chip = dev->adev; + struct snd_tm6000_card *chip; if (!dev) return 0; + chip = dev->adev; if (!chip) return 0; diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index e80b7e190471..dffbd4bd47b1 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -319,12 +319,13 @@ static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type) static int __tm6000_ir_int_start(struct rc_dev *rc) { struct tm6000_IR *ir = rc->priv; - struct tm6000_core *dev = ir->dev; + struct tm6000_core *dev; int pipe, size; int err = -ENOMEM; if (!ir) return -ENODEV; + dev = ir->dev; dprintk(2, "%s\n",__func__); -- cgit v1.2.3 From f44e6b4223fdbb2556faa895861098c32b0ba83f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 14 Aug 2012 15:56:19 -0300 Subject: [media] anysee: fix compiler warning debug_dump macro was defined twice when CONFIG_DVB_USB_DEBUG was not set. Move debug_dump macro to correct place. Reported-by: Randy Dunlap Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/anysee.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h index dc40dcf7c328..834dc120f90f 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.h +++ b/drivers/media/usb/dvb-usb-v2/anysee.h @@ -41,19 +41,18 @@ #ifdef CONFIG_DVB_USB_DEBUG #define dprintk(var, level, args...) \ do { if ((var & level)) printk(args); } while (0) -#define DVB_USB_DEBUG_STATUS -#else -#define dprintk(args...) -#define debug_dump(b, l, func) -#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" -#endif - #define debug_dump(b, l, func) {\ int loop_; \ for (loop_ = 0; loop_ < l; loop_++) \ func("%02x ", b[loop_]); \ func("\n");\ } +#define DVB_USB_DEBUG_STATUS +#else +#define dprintk(args...) +#define debug_dump(b, l, func) +#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" +#endif #define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args) #define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args) -- cgit v1.2.3 From 82026f9673cfa35c74a66e9d7bf1b0e44bb0de3f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 14 Aug 2012 15:56:20 -0300 Subject: [media] anysee: convert Kernel dev_* logging Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/anysee.c | 58 ++++++++++++++++++----------------- drivers/media/usb/dvb-usb-v2/anysee.h | 30 ------------------ 2 files changed, 30 insertions(+), 58 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index fb3829a73d2d..5aa3ac625124 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -44,12 +44,7 @@ #include "isl6423.h" #include "cxd2820r.h" -/* debug */ -static int dvb_usb_anysee_debug; -module_param_named(debug, dvb_usb_anysee_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - static DEFINE_MUTEX(anysee_usb_mutex); static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, @@ -64,8 +59,7 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, mutex_lock(&anysee_usb_mutex); - deb_xfer(">>> "); - debug_dump(buf, slen, deb_xfer); + dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, slen, buf); /* We need receive one message more after dvb_usb_generic_rw due to weird transaction flow, which is 1 x send + 2 x receive. */ @@ -92,14 +86,15 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, &act_len, 2000); if (ret) { - deb_info("%s: recv bulk message failed: %d", - __func__, ret); + dev_dbg(&d->udev->dev, "%s: recv bulk message " \ + "failed=%d\n", __func__, ret); } else { - deb_xfer("<<< "); - debug_dump(buf, rlen, deb_xfer); + dev_dbg(&d->udev->dev, "%s: <<< %*ph\n", __func__, + rlen, buf); if (buf[63] != 0x4f) - deb_info("%s: cmd failed\n", __func__); + dev_dbg(&d->udev->dev, "%s: cmd failed\n", + __func__); break; } @@ -107,7 +102,8 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, if (ret) { /* all retries failed, it is fatal */ - err("%s: recv bulk message failed: %d", __func__, ret); + dev_err(&d->udev->dev, "%s: recv bulk message failed=%d\n", + KBUILD_MODNAME, ret); goto error_unlock; } @@ -126,14 +122,14 @@ static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val) u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01}; int ret; ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1); - deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val); + dev_dbg(&d->udev->dev, "%s: reg=%04x val=%02x\n", __func__, reg, *val); return ret; } static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val) { u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val}; - deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val); + dev_dbg(&d->udev->dev, "%s: reg=%04x val=%02x\n", __func__, reg, val); return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); } @@ -190,21 +186,22 @@ static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id) static int anysee_streaming_ctrl(struct dvb_frontend *fe, int onoff) { u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00}; - deb_info("%s: onoff:%02x\n", __func__, onoff); + dev_dbg(&fe_to_d(fe)->udev->dev, "%s: onoff=%d\n", __func__, onoff); return anysee_ctrl_msg(fe_to_d(fe), buf, sizeof(buf), NULL, 0); } static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval) { u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval}; - deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval); + dev_dbg(&d->udev->dev, "%s: state=%d interval=%d\n", __func__, + mode, interval); return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); } static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff) { u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff}; - deb_info("%s: onoff:%02x\n", __func__, onoff); + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); } @@ -524,9 +521,11 @@ static int anysee_read_config(struct dvb_usb_device *d) if (ret) goto error; - /* Meaning of these info bytes are guessed. */ - info("firmware version:%d.%d hardware id:%d", - hw_info[1], hw_info[2], hw_info[0]); + /* + * Meaning of these info bytes are guessed. + */ + dev_info(&d->udev->dev, "%s: firmware version %d.%d hardware id %d\n", + KBUILD_MODNAME, hw_info[1], hw_info[2], hw_info[0]); state->hw = hw_info[0]; error: @@ -545,8 +544,7 @@ static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff) struct anysee_state *state = fe_to_priv(fe); struct dvb_usb_device *d = fe_to_d(fe); int ret; - - deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); + dev_dbg(&d->udev->dev, "%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); /* no frontend sleep control */ if (onoff == 0) @@ -728,7 +726,8 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) tmp = 0; ret = i2c_transfer(&d->i2c_adap, msg, 2); if (ret == 2 && tmp == 0xc7) - deb_info("%s: TDA18212 found\n", __func__); + dev_dbg(&d->udev->dev, "%s: TDA18212 found\n", + __func__); else tmp = 0; @@ -885,8 +884,10 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) if (!adap->fe[0]) { /* we have no frontend :-( */ ret = -ENODEV; - err("Unsupported Anysee version. " \ - "Please report the ."); + dev_err(&d->udev->dev, "%s: Unsupported Anysee version. " \ + "Please report the " \ + ".\n", + KBUILD_MODNAME); } error: return ret; @@ -898,7 +899,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); struct dvb_frontend *fe; int ret; - deb_info("%s: adap=%d\n", __func__, adap->id); + dev_dbg(&d->udev->dev, "%s:\n", __func__); switch (state->hw) { case ANYSEE_HW_507T: /* 2 */ @@ -1037,7 +1038,8 @@ static int anysee_rc_query(struct dvb_usb_device *d) return ret; if (ircode[0]) { - deb_rc("%s: key pressed %02x\n", __func__, ircode[1]); + dev_dbg(&d->udev->dev, "%s: key pressed %02x\n", __func__, + ircode[1]); rc_keydown(d->rc_dev, 0x08 << 8 | ircode[1], 0); } diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h index 834dc120f90f..4ab467679a43 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.h +++ b/drivers/media/usb/dvb-usb-v2/anysee.h @@ -38,36 +38,6 @@ #include "dvb_usb.h" #include "dvb_ca_en50221.h" -#ifdef CONFIG_DVB_USB_DEBUG -#define dprintk(var, level, args...) \ - do { if ((var & level)) printk(args); } while (0) -#define debug_dump(b, l, func) {\ - int loop_; \ - for (loop_ = 0; loop_ < l; loop_++) \ - func("%02x ", b[loop_]); \ - func("\n");\ -} -#define DVB_USB_DEBUG_STATUS -#else -#define dprintk(args...) -#define debug_dump(b, l, func) -#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" -#endif - -#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args) -#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args) -#define deb_rc(args...) dprintk(dvb_usb_anysee_debug, 0x04, args) -#define deb_reg(args...) dprintk(dvb_usb_anysee_debug, 0x08, args) -#define deb_i2c(args...) dprintk(dvb_usb_anysee_debug, 0x10, args) -#define deb_fw(args...) dprintk(dvb_usb_anysee_debug, 0x20, args) - -#undef err -#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) -#undef info -#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) -#undef warn -#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) - enum cmd { CMD_I2C_READ = 0x33, CMD_I2C_WRITE = 0x31, -- cgit v1.2.3 From 0898b95409489abd7219f5f7ca675c84656fb94f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 14 Aug 2012 22:21:05 -0300 Subject: [media] dvb_usb_v2: implement power-management for suspend Put device full sleep on suspend, wake-up it on resume and acquire retune in order to return same television channel. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 83 ++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 20 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index a72f9c7de682..7ce8ffef8aca 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -486,7 +486,6 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) int ret; struct dvb_usb_adapter *adap = fe->dvb->priv; struct dvb_usb_device *d = adap_to_d(adap); - mutex_lock(&adap->sync_mutex); dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); @@ -506,22 +505,30 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) goto err; } - adap->active_fe = fe->id; - mutex_unlock(&adap->sync_mutex); - return 0; err: - mutex_unlock(&adap->sync_mutex); dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } +static int dvb_usb_fe_init_lock(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + mutex_lock(&adap->sync_mutex); + + ret = dvb_usb_fe_init(fe); + adap->active_fe = fe->id; + + mutex_unlock(&adap->sync_mutex); + return ret; +} + static int dvb_usb_fe_sleep(struct dvb_frontend *fe) { int ret; struct dvb_usb_adapter *adap = fe->dvb->priv; struct dvb_usb_device *d = adap_to_d(adap); - mutex_lock(&adap->sync_mutex); dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); @@ -541,16 +548,25 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) if (ret < 0) goto err; - adap->active_fe = -1; - mutex_unlock(&adap->sync_mutex); - return 0; err: - mutex_unlock(&adap->sync_mutex); dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } +static int dvb_usb_fe_sleep_lock(struct dvb_frontend *fe) +{ + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + mutex_lock(&adap->sync_mutex); + + ret = dvb_usb_fe_sleep(fe); + adap->active_fe = -1; + + mutex_unlock(&adap->sync_mutex); + return ret; +} + int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) { int ret, i, count_registered = 0; @@ -578,9 +594,9 @@ int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) adap->fe[i]->id = i; /* re-assign sleep and wakeup functions */ adap->fe_init[i] = adap->fe[i]->ops.init; - adap->fe[i]->ops.init = dvb_usb_fe_init; + adap->fe[i]->ops.init = dvb_usb_fe_init_lock; adap->fe_sleep[i] = adap->fe[i]->ops.sleep; - adap->fe[i]->ops.sleep = dvb_usb_fe_sleep; + adap->fe[i]->ops.sleep = dvb_usb_fe_sleep_lock; ret = dvb_register_frontend(&adap->dvb_adap, adap->fe[i]); if (ret < 0) { @@ -950,18 +966,30 @@ EXPORT_SYMBOL(dvb_usbv2_disconnect); int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) { struct dvb_usb_device *d = usb_get_intfdata(intf); - int i; + int i, active_fe; + struct dvb_frontend *fe; dev_dbg(&d->udev->dev, "%s:\n", __func__); /* stop remote controller poll */ if (d->rc.query && !d->rc.bulk_mode) cancel_delayed_work_sync(&d->rc_query_work); - /* stop streaming */ for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) { - if (d->adapter[i].dvb_adap.priv && - d->adapter[i].active_fe != -1) + active_fe = d->adapter[i].active_fe; + if (d->adapter[i].dvb_adap.priv && active_fe != -1) { + fe = d->adapter[i].fe[active_fe]; + + if (d->props->streaming_ctrl) + d->props->streaming_ctrl(fe, 0); + + /* stop usb streaming */ usb_urb_killv2(&d->adapter[i].stream); + + if (fe->ops.tuner_ops.sleep) + fe->ops.tuner_ops.sleep(fe); + + dvb_usb_fe_sleep(fe); + } } return 0; @@ -971,14 +999,29 @@ EXPORT_SYMBOL(dvb_usbv2_suspend); int dvb_usbv2_resume(struct usb_interface *intf) { struct dvb_usb_device *d = usb_get_intfdata(intf); - int i; + int i, active_fe; + struct dvb_frontend *fe; dev_dbg(&d->udev->dev, "%s:\n", __func__); - /* start streaming */ for (i = 0; i < MAX_NO_OF_ADAPTER_PER_DEVICE; i++) { - if (d->adapter[i].dvb_adap.priv && - d->adapter[i].active_fe != -1) + active_fe = d->adapter[i].active_fe; + if (d->adapter[i].dvb_adap.priv && active_fe != -1) { + fe = d->adapter[i].fe[active_fe]; + + dvb_usb_fe_init(fe); + + if (fe->ops.tuner_ops.init) + fe->ops.tuner_ops.init(fe); + + /* acquire dvb-core perform retune */ + dvb_frontend_retune(fe); + + /* resume usb streaming */ usb_urb_submitv2(&d->adapter[i].stream, NULL); + + if (d->props->streaming_ctrl) + d->props->streaming_ctrl(fe, 1); + } } /* start remote controller poll */ -- cgit v1.2.3 From 06bae1227aadf51d047f7a75834ed446e56ebae2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 14 Aug 2012 22:21:06 -0300 Subject: [media] dvb_frontend: implement suspend / resume Move initial suspend / resume support from dvb_usb_v2 to dvb_frontend as it is dvb general feature that could be used all dvb devices. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 47 ++++++++++++++++---- drivers/media/dvb-core/dvb_frontend.h | 3 +- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 3 ++ drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 66 ++++++++++------------------- 4 files changed, 66 insertions(+), 53 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 5fb19eae5a82..aa4d4d85dc20 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -307,15 +307,6 @@ void dvb_frontend_reinitialise(struct dvb_frontend *fe) } EXPORT_SYMBOL(dvb_frontend_reinitialise); -void dvb_frontend_retune(struct dvb_frontend *fe) -{ - struct dvb_frontend_private *fepriv = fe->frontend_priv; - - fepriv->state = FESTATE_RETUNE; - dvb_frontend_wakeup(fe); -} -EXPORT_SYMBOL(dvb_frontend_retune); - static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked) { int q2; @@ -2448,6 +2439,44 @@ static const struct file_operations dvb_frontend_fops = { .llseek = noop_llseek, }; +int dvb_frontend_suspend(struct dvb_frontend *fe) +{ + int ret = 0; + + dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, + fe->id); + + if (fe->ops.tuner_ops.sleep) + ret = fe->ops.tuner_ops.sleep(fe); + + if (fe->ops.sleep) + ret = fe->ops.sleep(fe); + + return ret; +} +EXPORT_SYMBOL(dvb_frontend_suspend); + +int dvb_frontend_resume(struct dvb_frontend *fe) +{ + struct dvb_frontend_private *fepriv = fe->frontend_priv; + int ret = 0; + + dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, + fe->id); + + if (fe->ops.init) + ret = fe->ops.init(fe); + + if (fe->ops.tuner_ops.init) + ret = fe->ops.tuner_ops.init(fe); + + fepriv->state = FESTATE_RETUNE; + dvb_frontend_wakeup(fe); + + return ret; +} +EXPORT_SYMBOL(dvb_frontend_resume); + int dvb_register_frontend(struct dvb_adapter* dvb, struct dvb_frontend* fe) { diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 58f6b4c16b40..db309db79bd6 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -418,7 +418,8 @@ extern int dvb_unregister_frontend(struct dvb_frontend *fe); extern void dvb_frontend_detach(struct dvb_frontend *fe); extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); -extern void dvb_frontend_retune(struct dvb_frontend *fe); +extern int dvb_frontend_suspend(struct dvb_frontend *fe); +extern int dvb_frontend_resume(struct dvb_frontend *fe); extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 79b3b8b6750d..63fc275c6497 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -295,6 +295,7 @@ struct usb_data_stream { * @stream: adapter the usb data stream * @id: index of this adapter (starting with 0) * @ts_type: transport stream, input stream, type + * @suspend_resume_active: set when there is ongoing suspend / resume * @pid_filtering: is hardware pid_filtering used or not * @feed_count: current feed count * @max_feed_count: maimum feed count device can handle @@ -312,6 +313,7 @@ struct dvb_usb_adapter { struct usb_data_stream stream; u8 id; u8 ts_type; + bool suspend_resume_active; bool pid_filtering; u8 feed_count; u8 max_feed_count; @@ -381,6 +383,7 @@ extern int dvb_usbv2_probe(struct usb_interface *, extern void dvb_usbv2_disconnect(struct usb_interface *); extern int dvb_usbv2_suspend(struct usb_interface *, pm_message_t); extern int dvb_usbv2_resume(struct usb_interface *); +#define dvb_usbv2_reset_resume dvb_usbv2_resume /* the generic read/write method for device control */ extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 7ce8ffef8aca..a0e70e91834a 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -489,6 +489,11 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); + if (!adap->suspend_resume_active) { + adap->active_fe = fe->id; + mutex_lock(&adap->sync_mutex); + } + ret = dvb_usbv2_device_power_ctrl(d, 1); if (ret < 0) goto err; @@ -504,23 +509,11 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) if (ret < 0) goto err; } - - return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} - -static int dvb_usb_fe_init_lock(struct dvb_frontend *fe) -{ - int ret; - struct dvb_usb_adapter *adap = fe->dvb->priv; - mutex_lock(&adap->sync_mutex); - - ret = dvb_usb_fe_init(fe); - adap->active_fe = fe->id; + if (!adap->suspend_resume_active) + mutex_unlock(&adap->sync_mutex); - mutex_unlock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: ret=%d\n", __func__, ret); return ret; } @@ -532,6 +525,9 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); + if (!adap->suspend_resume_active) + mutex_lock(&adap->sync_mutex); + if (adap->fe_sleep[fe->id]) { ret = adap->fe_sleep[fe->id](fe); if (ret < 0) @@ -547,23 +543,13 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) ret = dvb_usbv2_device_power_ctrl(d, 0); if (ret < 0) goto err; - - return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} - -static int dvb_usb_fe_sleep_lock(struct dvb_frontend *fe) -{ - int ret; - struct dvb_usb_adapter *adap = fe->dvb->priv; - mutex_lock(&adap->sync_mutex); - - ret = dvb_usb_fe_sleep(fe); - adap->active_fe = -1; + if (!adap->suspend_resume_active) { + adap->active_fe = -1; + mutex_unlock(&adap->sync_mutex); + } - mutex_unlock(&adap->sync_mutex); + dev_dbg(&d->udev->dev, "%s: ret=%d\n", __func__, ret); return ret; } @@ -594,9 +580,9 @@ int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) adap->fe[i]->id = i; /* re-assign sleep and wakeup functions */ adap->fe_init[i] = adap->fe[i]->ops.init; - adap->fe[i]->ops.init = dvb_usb_fe_init_lock; + adap->fe[i]->ops.init = dvb_usb_fe_init; adap->fe_sleep[i] = adap->fe[i]->ops.sleep; - adap->fe[i]->ops.sleep = dvb_usb_fe_sleep_lock; + adap->fe[i]->ops.sleep = dvb_usb_fe_sleep; ret = dvb_register_frontend(&adap->dvb_adap, adap->fe[i]); if (ret < 0) { @@ -978,6 +964,7 @@ int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) active_fe = d->adapter[i].active_fe; if (d->adapter[i].dvb_adap.priv && active_fe != -1) { fe = d->adapter[i].fe[active_fe]; + d->adapter[i].suspend_resume_active = true; if (d->props->streaming_ctrl) d->props->streaming_ctrl(fe, 0); @@ -985,10 +972,7 @@ int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) /* stop usb streaming */ usb_urb_killv2(&d->adapter[i].stream); - if (fe->ops.tuner_ops.sleep) - fe->ops.tuner_ops.sleep(fe); - - dvb_usb_fe_sleep(fe); + dvb_frontend_suspend(fe); } } @@ -1008,19 +992,15 @@ int dvb_usbv2_resume(struct usb_interface *intf) if (d->adapter[i].dvb_adap.priv && active_fe != -1) { fe = d->adapter[i].fe[active_fe]; - dvb_usb_fe_init(fe); - - if (fe->ops.tuner_ops.init) - fe->ops.tuner_ops.init(fe); - - /* acquire dvb-core perform retune */ - dvb_frontend_retune(fe); + dvb_frontend_resume(fe); /* resume usb streaming */ usb_urb_submitv2(&d->adapter[i].stream, NULL); if (d->props->streaming_ctrl) d->props->streaming_ctrl(fe, 1); + + d->adapter[i].suspend_resume_active = false; } } -- cgit v1.2.3 From 15d0883663edccea29eeb9790052a11dd9977157 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 14 Aug 2012 22:21:07 -0300 Subject: [media] dvb_usb_v2: .reset_resume() support Add .reset_resume() support. Also some other small changes for suspend / resume. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 2 +- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 42 +++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 63fc275c6497..5a53c6231fc2 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -383,7 +383,7 @@ extern int dvb_usbv2_probe(struct usb_interface *, extern void dvb_usbv2_disconnect(struct usb_interface *); extern int dvb_usbv2_suspend(struct usb_interface *, pm_message_t); extern int dvb_usbv2_resume(struct usb_interface *); -#define dvb_usbv2_reset_resume dvb_usbv2_resume +extern int dvb_usbv2_reset_resume(struct usb_interface *); /* the generic read/write method for device control */ extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index a0e70e91834a..e2d73e1a7ae0 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -952,7 +952,7 @@ EXPORT_SYMBOL(dvb_usbv2_disconnect); int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) { struct dvb_usb_device *d = usb_get_intfdata(intf); - int i, active_fe; + int ret = 0, i, active_fe; struct dvb_frontend *fe; dev_dbg(&d->udev->dev, "%s:\n", __func__); @@ -972,18 +972,17 @@ int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) /* stop usb streaming */ usb_urb_killv2(&d->adapter[i].stream); - dvb_frontend_suspend(fe); + ret = dvb_frontend_suspend(fe); } } - return 0; + return ret; } EXPORT_SYMBOL(dvb_usbv2_suspend); -int dvb_usbv2_resume(struct usb_interface *intf) +static int dvb_usbv2_resume_common(struct dvb_usb_device *d) { - struct dvb_usb_device *d = usb_get_intfdata(intf); - int i, active_fe; + int ret = 0, i, active_fe; struct dvb_frontend *fe; dev_dbg(&d->udev->dev, "%s:\n", __func__); @@ -992,7 +991,7 @@ int dvb_usbv2_resume(struct usb_interface *intf) if (d->adapter[i].dvb_adap.priv && active_fe != -1) { fe = d->adapter[i].fe[active_fe]; - dvb_frontend_resume(fe); + ret = dvb_frontend_resume(fe); /* resume usb streaming */ usb_urb_submitv2(&d->adapter[i].stream, NULL); @@ -1009,10 +1008,37 @@ int dvb_usbv2_resume(struct usb_interface *intf) schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); - return 0; + return ret; +} + +int dvb_usbv2_resume(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + return dvb_usbv2_resume_common(d); } EXPORT_SYMBOL(dvb_usbv2_resume); +int dvb_usbv2_reset_resume(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + int ret; + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + dvb_usbv2_device_power_ctrl(d, 1); + + if (d->props->init) + d->props->init(d); + + ret = dvb_usbv2_resume_common(d); + + dvb_usbv2_device_power_ctrl(d, 0); + + return ret; +} +EXPORT_SYMBOL(dvb_usbv2_reset_resume); + MODULE_VERSION("2.0"); MODULE_AUTHOR("Patrick Boettcher "); MODULE_AUTHOR("Antti Palosaari "); -- cgit v1.2.3 From 04966aa8dc7d6201755c81cd07841ca30aa7e379 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 14 Aug 2012 22:21:08 -0300 Subject: [media] dvb_usb_v2: af9015, af9035, anysee use .reset_resume All these seems to survive .reset_resume. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 1 + drivers/media/usb/dvb-usb-v2/af9035.c | 1 + drivers/media/usb/dvb-usb-v2/anysee.c | 1 + 3 files changed, 3 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index e77429b37a7d..9afceedc9d1f 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1423,6 +1423,7 @@ static struct usb_driver af9015_usb_driver = { .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, .no_dynamic_id = 1, .soft_unbind = 1, }; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index bb90b877d07b..b7004441ac2a 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1075,6 +1075,7 @@ static struct usb_driver af9035_usb_driver = { .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, .no_dynamic_id = 1, .soft_unbind = 1, }; diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 5aa3ac625124..b430bcace0b8 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1315,6 +1315,7 @@ static struct usb_driver anysee_usb_driver = { .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, .no_dynamic_id = 1, .soft_unbind = 1, }; -- cgit v1.2.3 From 78fa59038eda0ad2501c67b6d79423addab4b871 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 14 Aug 2012 22:21:09 -0300 Subject: [media] dvb_usb_v2: ce6230, rtl28xxu use .reset_resume All these seems to survive .reset_resume. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/ce6230.c | 1 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.c b/drivers/media/usb/dvb-usb-v2/ce6230.c index 84ff4a96ca4e..819db9c01d5d 100644 --- a/drivers/media/usb/dvb-usb-v2/ce6230.c +++ b/drivers/media/usb/dvb-usb-v2/ce6230.c @@ -276,6 +276,7 @@ static struct usb_driver ce6230_usb_driver = { .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, .no_dynamic_id = 1, .soft_unbind = 1, }; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index a2d1e5b9d9d4..d2b1505b36f9 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1247,6 +1247,7 @@ static struct usb_driver rtl28xxu_usb_driver = { .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, .no_dynamic_id = 1, .soft_unbind = 1, }; -- cgit v1.2.3 From 88f8472c9fc6c08f5113887471f1f4aabf7b2929 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 16 Aug 2012 16:57:21 -0300 Subject: [media] Fix some Makefile rules On a few places, := were using instead of +=, causing drivers to not compile. While here, standardize the usage of += on all cases where multiple lines are needed, and for obj-y/obj-m targets, and := when just one line is needed, on -obj rules. Reported-by: Hans Verkuil Identified-by: Antti Polosaari Tested-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/b2c2/Makefile | 5 ++-- drivers/media/dvb-frontends/Makefile | 8 +++--- drivers/media/firewire/Makefile | 2 +- drivers/media/mmc/Makefile | 2 +- drivers/media/pci/Makefile | 2 +- drivers/media/pci/cx25821/Makefile | 2 +- drivers/media/pci/saa7134/Makefile | 2 +- drivers/media/platform/omap/Makefile | 2 +- drivers/media/usb/Makefile | 4 +-- drivers/media/usb/b2c2/Makefile | 2 +- drivers/media/usb/dvb-usb-v2/Makefile | 29 ++++++++++--------- drivers/media/usb/dvb-usb/Makefile | 53 ++++++++++++++++++----------------- drivers/media/usb/em28xx/Makefile | 2 +- drivers/media/usb/pwc/Makefile | 2 +- drivers/media/usb/tm6000/Makefile | 2 +- 15 files changed, 61 insertions(+), 58 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/common/b2c2/Makefile b/drivers/media/common/b2c2/Makefile index 48a4c906a173..24993a5b38ba 100644 --- a/drivers/media/common/b2c2/Makefile +++ b/drivers/media/common/b2c2/Makefile @@ -1,5 +1,6 @@ -b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ - flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o +b2c2-flexcop-objs += flexcop.o flexcop-fe-tuner.o flexcop-i2c.o +b2c2-flexcop-objs += flexcop-sram.o flexcop-eeprom.o flexcop-misc.o +b2c2-flexcop-objs += flexcop-hw-filter.o obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o ccflags-y += -Idrivers/media/dvb-core/ diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 208bc496b90a..7eb73bbd2e26 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -5,10 +5,10 @@ ccflags-y += -I$(srctree)/drivers/media/dvb-core/ ccflags-y += -I$(srctree)/drivers/media/tuners/ -stb0899-objs = stb0899_drv.o stb0899_algo.o -stv0900-objs = stv0900_core.o stv0900_sw.o -drxd-objs = drxd_firm.o drxd_hard.o -cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o +stb0899-objs := stb0899_drv.o stb0899_algo.o +stv0900-objs := stv0900_core.o stv0900_sw.o +drxd-objs := drxd_firm.o drxd_hard.o +cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o drxk-objs := drxk_hard.o obj-$(CONFIG_DVB_PLL) += dvb-pll.o diff --git a/drivers/media/firewire/Makefile b/drivers/media/firewire/Makefile index f3148138c963..239481344d7c 100644 --- a/drivers/media/firewire/Makefile +++ b/drivers/media/firewire/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_DVB_FIREDTV) += firedtv.o -firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o +firedtv-y += firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o ccflags-y += -Idrivers/media/dvb-core diff --git a/drivers/media/mmc/Makefile b/drivers/media/mmc/Makefile index dacd3cbb80d6..31e297a202fb 100644 --- a/drivers/media/mmc/Makefile +++ b/drivers/media/mmc/Makefile @@ -1 +1 @@ -obj-y := siano/ +obj-y += siano/ diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index c8dc6c775303..35cc57862c01 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := ttpci/ \ +obj-y += ttpci/ \ b2c2/ \ pluto2/ \ dm1105/ \ diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index c038941d6054..5bf3ea4c1556 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -7,7 +7,7 @@ cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o -ccflags-y := -Idrivers/media/i2c +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile index 9e510c1459f3..35375480ed4d 100644 --- a/drivers/media/pci/saa7134/Makefile +++ b/drivers/media/pci/saa7134/Makefile @@ -1,5 +1,5 @@ -saa7134-y := saa7134-cards.o saa7134-core.o saa7134-i2c.o +saa7134-y += saa7134-cards.o saa7134-core.o saa7134-i2c.o saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o saa7134-y += saa7134-video.o saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o diff --git a/drivers/media/platform/omap/Makefile b/drivers/media/platform/omap/Makefile index fc410b438f7d..d80df41fde28 100644 --- a/drivers/media/platform/omap/Makefile +++ b/drivers/media/platform/omap/Makefile @@ -3,6 +3,6 @@ # # OMAP2/3 Display driver -omap-vout-y := omap_vout.o omap_voutlib.o +omap-vout-y += omap_vout.o omap_voutlib.o omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 63e37bb2ed74..7f51d7e5f739 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -3,8 +3,8 @@ # # DVB USB-only drivers -obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ -obj-y := zr364xx/ stkwebcam/ s2255/ +obj-y += ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ +obj-y += zr364xx/ stkwebcam/ s2255/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ diff --git a/drivers/media/usb/b2c2/Makefile b/drivers/media/usb/b2c2/Makefile index ace9d76f0b38..2778c19a45eb 100644 --- a/drivers/media/usb/b2c2/Makefile +++ b/drivers/media/usb/b2c2/Makefile @@ -1,4 +1,4 @@ -b2c2-flexcop-usb-objs = flexcop-usb.o +b2c2-flexcop-usb-objs := flexcop-usb.o obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o ccflags-y += -Idrivers/media/dvb-core/ diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile index 62568430418b..58027fd43a08 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -1,45 +1,46 @@ -dvb_usbv2-objs = dvb_usb_core.o dvb_usb_urb.o usb_urb.o +dvb_usbv2-objs := dvb_usb_core.o dvb_usb_urb.o usb_urb.o obj-$(CONFIG_DVB_USB_V2) += dvb_usbv2.o -dvb_usb_cypress_firmware-objs = cypress_firmware.o +dvb_usb_cypress_firmware-objs := cypress_firmware.o obj-$(CONFIG_DVB_USB_CYPRESS_FIRMWARE) += dvb_usb_cypress_firmware.o -dvb-usb-af9015-objs = af9015.o +dvb-usb-af9015-objs := af9015.o obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o -dvb-usb-af9035-objs = af9035.o +dvb-usb-af9035-objs := af9035.o obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o -dvb-usb-anysee-objs = anysee.o +dvb-usb-anysee-objs := anysee.o obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o -dvb-usb-au6610-objs = au6610.o +dvb-usb-au6610-objs := au6610.o obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o -dvb-usb-az6007-objs = az6007.o +dvb-usb-az6007-objs := az6007.o obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o -dvb-usb-ce6230-objs = ce6230.o +dvb-usb-ce6230-objs := ce6230.o obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o -dvb-usb-ec168-objs = ec168.o +dvb-usb-ec168-objs := ec168.o obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o -dvb-usb-it913x-objs = it913x.o +dvb-usb-it913x-objs := it913x.o obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o -dvb-usb-lmedm04-objs = lmedm04.o +dvb-usb-lmedm04-objs := lmedm04.o obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o -dvb-usb-gl861-objs = gl861.o +dvb-usb-gl861-objs := gl861.o obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o -dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o +dvb-usb-mxl111sf-objs += mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o +dvb-usb-mxl111sf-objs += mxl111sf-gpio.o obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o -dvb-usb-rtl28xxu-objs = rtl28xxu.o +dvb-usb-rtl28xxu-objs := rtl28xxu.o obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o ccflags-y += -I$(srctree)/drivers/media/dvb-core diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index d3ab1e74647d..acdd1efd4e74 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -1,78 +1,79 @@ -dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o +dvb-usb-objs += dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o +dvb-usb-objs += dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o obj-$(CONFIG_DVB_USB) += dvb-usb.o -dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o +dvb-usb-vp7045-objs := vp7045.o vp7045-fe.o obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o -dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o +dvb-usb-vp702x-objs := vp702x.o vp702x-fe.o obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o -dvb-usb-gp8psk-objs = gp8psk.o gp8psk-fe.o +dvb-usb-gp8psk-objs := gp8psk.o gp8psk-fe.o obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o -dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o +dvb-usb-dtt200u-objs := dtt200u.o dtt200u-fe.o obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o -dvb-usb-dibusb-common-objs = dibusb-common.o +dvb-usb-dibusb-common-objs := dibusb-common.o -dvb-usb-a800-objs = a800.o +dvb-usb-a800-objs := a800.o obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o -dvb-usb-dibusb-mb-objs = dibusb-mb.o +dvb-usb-dibusb-mb-objs := dibusb-mb.o obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o -dvb-usb-dibusb-mc-objs = dibusb-mc.o +dvb-usb-dibusb-mc-objs := dibusb-mc.o obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o -dvb-usb-nova-t-usb2-objs = nova-t-usb2.o +dvb-usb-nova-t-usb2-objs := nova-t-usb2.o obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o -dvb-usb-umt-010-objs = umt-010.o +dvb-usb-umt-010-objs := umt-010.o obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o -dvb-usb-m920x-objs = m920x.o +dvb-usb-m920x-objs := m920x.o obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o -dvb-usb-digitv-objs = digitv.o +dvb-usb-digitv-objs := digitv.o obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o -dvb-usb-cxusb-objs = cxusb.o +dvb-usb-cxusb-objs := cxusb.o obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o -dvb-usb-ttusb2-objs = ttusb2.o +dvb-usb-ttusb2-objs := ttusb2.o obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o -dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o +dvb-usb-dib0700-objs := dib0700_core.o dib0700_devices.o obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o -dvb-usb-opera-objs = opera1.o +dvb-usb-opera-objs := opera1.o obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o -dvb-usb-af9005-objs = af9005.o af9005-fe.o +dvb-usb-af9005-objs := af9005.o af9005-fe.o obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o -dvb-usb-af9005-remote-objs = af9005-remote.o +dvb-usb-af9005-remote-objs := af9005-remote.o obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o -dvb-usb-pctv452e-objs = pctv452e.o +dvb-usb-pctv452e-objs := pctv452e.o obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o -dvb-usb-dw2102-objs = dw2102.o +dvb-usb-dw2102-objs := dw2102.o obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o -dvb-usb-dtv5100-objs = dtv5100.o +dvb-usb-dtv5100-objs := dtv5100.o obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o -dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o +dvb-usb-cinergyT2-objs := cinergyT2-core.o cinergyT2-fe.o obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o -dvb-usb-friio-objs = friio.o friio-fe.o +dvb-usb-friio-objs := friio.o friio-fe.o obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o -dvb-usb-az6027-objs = az6027.o +dvb-usb-az6027-objs := az6027.o obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o -dvb-usb-technisat-usb2-objs = technisat-usb2.o +dvb-usb-technisat-usb2-objs := technisat-usb2.o obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o ccflags-y += -I$(srctree)/drivers/media/dvb-core diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile index 6c5f3381da7d..634fb920dd39 100644 --- a/drivers/media/usb/em28xx/Makefile +++ b/drivers/media/usb/em28xx/Makefile @@ -1,4 +1,4 @@ -em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o +em28xx-y += em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-y += em28xx-core.o em28xx-vbi.o em28xx-alsa-objs := em28xx-audio.o diff --git a/drivers/media/usb/pwc/Makefile b/drivers/media/usb/pwc/Makefile index f5c8ec261e87..d7fdbcb9edd3 100644 --- a/drivers/media/usb/pwc/Makefile +++ b/drivers/media/usb/pwc/Makefile @@ -1,4 +1,4 @@ -pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o +pwc-objs += pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o obj-$(CONFIG_USB_PWC) += pwc.o diff --git a/drivers/media/usb/tm6000/Makefile b/drivers/media/usb/tm6000/Makefile index 6fa1f1044512..f2644933b8d1 100644 --- a/drivers/media/usb/tm6000/Makefile +++ b/drivers/media/usb/tm6000/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000.o obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o -ccflags-y := -Idrivers/media/i2c +ccflags-y += -Idrivers/media/i2c ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends -- cgit v1.2.3 From fc2bbfb2c3d77c0b6da76224ef1575f0e90327e8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Aug 2012 10:47:00 -0300 Subject: [media] Kconfig: use menuconfig instead of menu This allows disabling all drivers of a certain type as a hole Signed-off-by: Mauro Carvalho Chehab --- drivers/media/parport/Kconfig | 8 +++++--- drivers/media/pci/Kconfig | 11 +++++------ drivers/media/usb/Kconfig | 12 +++++------- 3 files changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig index 48138fe46e0e..a1c7853f3a96 100644 --- a/drivers/media/parport/Kconfig +++ b/drivers/media/parport/Kconfig @@ -1,6 +1,8 @@ -menu "V4L ISA and parallel port devices" - visible if (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT +menuconfig MEDIA_PARPORT_SUPPORT + bool "V4L ISA and parallel port devices" + depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT +if MEDIA_PARPORT_SUPPORT config VIDEO_BWQCAM tristate "Quickcam BW Video For Linux" depends on PARPORT && VIDEO_V4L2 @@ -44,4 +46,4 @@ config VIDEO_W9966 Check out for more information. -endmenu +endif diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 4243d5d38c0a..083b62f8b970 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -1,9 +1,8 @@ -# -# DVB device configuration -# +menuconfig MEDIA_PCI_SUPPORT + bool "Media PCI Adapters" + depends on PCI && MEDIA_SUPPORT -menu "Media PCI Adapters" - visible if PCI && MEDIA_SUPPORT +if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" @@ -42,4 +41,4 @@ source "drivers/media/pci/ngene/Kconfig" source "drivers/media/pci/ddbridge/Kconfig" endif -endmenu +endif #MEDIA_PCI_SUPPORT diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 069a3c1d03f5..f960e7ca4738 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -1,9 +1,8 @@ -# -# USB media device configuration -# +menuconfig MEDIA_USB_SUPPORT + bool "Media USB Adapters" + depends on USB && MEDIA_SUPPORT -menu "Media USB Adapters" - visible if USB && MEDIA_SUPPORT +if MEDIA_USB_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Webcam devices" @@ -25,7 +24,6 @@ source "drivers/media/usb/hdpvr/Kconfig" source "drivers/media/usb/tlg2300/Kconfig" source "drivers/media/usb/usbvision/Kconfig" source "drivers/media/usb/stk1160/Kconfig" - endif if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) @@ -50,4 +48,4 @@ if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) source "drivers/media/usb/em28xx/Kconfig" endif -endmenu +endif #MEDIA_USB_SUPPORT -- cgit v1.2.3 From fccea74ff8b5159935acc7b4b4857ee81ee44661 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Aug 2012 14:48:02 -0300 Subject: [media] Kconfig: merge all customise options into just one Instead of having 3 options to allow customizing the media sub-drivers (tuners, I2C drivers, frontends), merge all of them into just one. That simplifies the life for users, as they can just keep this untouched. Life for developers is also simpler, as there's now just one Kconfig item to remember, for the ancillary sub-drivers providing supports for chips that could change from one board design to another. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 19 +++- drivers/media/common/b2c2/Kconfig | 28 ++--- drivers/media/dvb-frontends/Kconfig | 195 +++++++++++++++------------------ drivers/media/i2c/Kconfig | 21 +--- drivers/media/pci/bt8xx/Kconfig | 24 ++-- drivers/media/pci/cx18/Kconfig | 10 +- drivers/media/pci/cx23885/Kconfig | 34 +++--- drivers/media/pci/cx88/Kconfig | 36 +++--- drivers/media/pci/ddbridge/Kconfig | 10 +- drivers/media/pci/dm1105/Kconfig | 14 +-- drivers/media/pci/mantis/Kconfig | 20 ++-- drivers/media/pci/ngene/Kconfig | 14 +-- drivers/media/pci/saa7134/Kconfig | 40 +++---- drivers/media/pci/saa7146/Kconfig | 8 +- drivers/media/pci/saa7164/Kconfig | 6 +- drivers/media/pci/sta2x11/Kconfig | 2 +- drivers/media/pci/ttpci/Kconfig | 84 +++++++------- drivers/media/pci/zoran/Kconfig | 26 ++--- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/davinci/Kconfig | 4 +- drivers/media/tuners/Kconfig | 88 +++++++-------- drivers/media/usb/au0828/Kconfig | 10 +- drivers/media/usb/cx231xx/Kconfig | 6 +- drivers/media/usb/dvb-usb-v2/Kconfig | 88 +++++++-------- drivers/media/usb/dvb-usb/Kconfig | 148 ++++++++++++------------- drivers/media/usb/em28xx/Kconfig | 28 ++--- drivers/media/usb/pvrusb2/Kconfig | 14 +-- drivers/media/usb/ttusb-budget/Kconfig | 14 +-- drivers/media/usb/usbvision/Kconfig | 2 +- 29 files changed, 488 insertions(+), 507 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9c3698ab6132..dd13e3a4c272 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -162,10 +162,27 @@ source "drivers/media/common/Kconfig" # Ancillary drivers (tuners, i2c, frontends) # +config MEDIA_SUBDRV_AUTOSELECT + bool "Autoselect analog and hybrid tuner modules to build" + depends on MEDIA_TUNER + default y + help + By default, a TV driver auto-selects all possible tuners + thar could be used by the driver. + + This is generally the right thing to do, except when there + are strict constraints with regards to the kernel size. + + Use this option with care, as deselecting tuner drivers which + are in fact necessary will result in TV devices which cannot + be tuned due to lack of the tuning driver. + + If unsure say Y. + comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" -source "drivers/media/tuners/Kconfig" source "drivers/media/i2c/Kconfig" +source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" endif # MEDIA_SUPPORT diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig index e270dd847342..29149de66982 100644 --- a/drivers/media/common/b2c2/Kconfig +++ b/drivers/media/common/b2c2/Kconfig @@ -3,20 +3,20 @@ config DVB_B2C2_FLEXCOP depends on DVB_CORE && I2C depends on DVB_B2C2_FLEXCOP_PCI || DVB_B2C2_FLEXCOP_USB default y - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_BCM3510 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_S5H1420 if !DVB_FE_CUSTOMISE - select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_CX24123 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_BCM3510 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_ITD1000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_CX24113 if MEDIA_SUBDRV_AUTOSELECT help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index a08c2152d0ee..5efec73a32d2 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -1,20 +1,5 @@ -config DVB_FE_CUSTOMISE - bool "Customise the frontend modules to build" - depends on DVB_CORE - depends on EXPERT - default y if EXPERT - help - This allows the user to select/deselect frontend drivers for their - hardware from the build. - - Use this option with care as deselecting frontends which are in fact - necessary will result in DVB devices which cannot be tuned due to lack - of driver support. - - If unsure say N. - menu "Customise DVB Frontends" - visible if DVB_FE_CUSTOMISE + visible if !MEDIA_SUBDRV_AUTOSELECT comment "Multistandard (satellite) frontends" depends on DVB_CORE @@ -22,7 +7,7 @@ comment "Multistandard (satellite) frontends" config DVB_STB0899 tristate "STB0899 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want to support this demodulator based frontends @@ -30,7 +15,7 @@ config DVB_STB0899 config DVB_STB6100 tristate "STB6100 based tuners" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A Silicon tuner from ST used in conjunction with the STB0899 demodulator. Say Y when you want to support this tuner. @@ -38,7 +23,7 @@ config DVB_STB6100 config DVB_STV090x tristate "STV0900/STV0903(A/B) based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators. Say Y when you want to support these frontends. @@ -46,7 +31,7 @@ config DVB_STV090x config DVB_STV6110x tristate "STV6110/(A) based tuners" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A Silicon tuner that supports DVB-S and DVB-S2 modes @@ -56,7 +41,7 @@ comment "Multistandard (cable + terrestrial) frontends" config DVB_DRXK tristate "Micronas DRXK based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Micronas DRX-K DVB-C/T demodulator. @@ -65,7 +50,7 @@ config DVB_DRXK config DVB_TDA18271C2DD tristate "NXP TDA18271C2 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help NXP TDA18271 silicon tuner. @@ -77,119 +62,119 @@ comment "DVB-S (satellite) frontends" config DVB_CX24110 tristate "Conexant CX24110 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_CX24123 tristate "Conexant CX24123 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_MT312 tristate "Zarlink VP310/MT312/ZL10313 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_ZL10036 tristate "Zarlink ZL10036 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_ZL10039 tristate "Zarlink ZL10039 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_S5H1420 tristate "Samsung S5H1420 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STV0288 tristate "ST STV0288 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STB6000 tristate "ST STB6000 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S silicon tuner module. Say Y when you want to support this tuner. config DVB_STV0299 tristate "ST STV0299 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STV6110 tristate "ST STV6110 silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S silicon tuner module. Say Y when you want to support this tuner. config DVB_STV0900 tristate "ST STV0900 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 demodulator. Say Y when you want to support this frontend. config DVB_TDA8083 tristate "Philips TDA8083 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TDA10086 tristate "Philips TDA10086 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TDA8261 tristate "Philips TDA8261 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_VES1X93 tristate "VLSI VES1893 or VES1993 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TUNER_ITD1000 tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TUNER_CX24113 tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. @@ -197,42 +182,42 @@ config DVB_TUNER_CX24113 config DVB_TDA826X tristate "Philips TDA826X silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S silicon tuner module. Say Y when you want to support this tuner. config DVB_TUA6100 tristate "Infineon TUA6100 PLL" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S PLL chip. config DVB_CX24116 tristate "Conexant CX24116 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 tuner module. Say Y when you want to support this frontend. config DVB_SI21XX tristate "Silicon Labs SI21XX based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_DS3000 tristate "Montage Tehnology DS3000 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 tuner module. Say Y when you want to support this frontend. config DVB_MB86A16 tristate "Fujitsu MB86A16 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/DSS Direct Conversion reveiver. Say Y when you want to support this frontend. @@ -240,7 +225,7 @@ config DVB_MB86A16 config DVB_TDA10071 tristate "NXP TDA10071" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. @@ -250,7 +235,7 @@ comment "DVB-T (terrestrial) frontends" config DVB_SP8870 tristate "Spase sp8870 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -262,7 +247,7 @@ config DVB_SP8870 config DVB_SP887X tristate "Spase sp887x based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -274,28 +259,28 @@ config DVB_SP887X config DVB_CX22700 tristate "Conexant CX22700 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_CX22702 tristate "Conexant cx22702 demodulator (OFDM)" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_S5H1432 tristate "Samsung s5h1432 demodulator (OFDM)" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_DRXD tristate "Micronas DRXD driver" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -306,14 +291,14 @@ config DVB_DRXD config DVB_L64781 tristate "LSI L64781" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_TDA1004X tristate "Philips TDA10045H/TDA10046H based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -326,28 +311,28 @@ config DVB_TDA1004X config DVB_NXT6000 tristate "NxtWave Communications NXT6000 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_MT352 tristate "Zarlink MT352 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_ZL10353 tristate "Zarlink ZL10353 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_DIB3000MB tristate "DiBcom 3000M-B" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -355,7 +340,7 @@ config DVB_DIB3000MB config DVB_DIB3000MC tristate "DiBcom 3000P/M-C" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -363,7 +348,7 @@ config DVB_DIB3000MC config DVB_DIB7000M tristate "DiBcom 7000MA/MB/PA/PB/MC" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -371,7 +356,7 @@ config DVB_DIB7000M config DVB_DIB7000P tristate "DiBcom 7000PC" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -379,7 +364,7 @@ config DVB_DIB7000P config DVB_DIB9000 tristate "DiBcom 9000" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -387,56 +372,56 @@ config DVB_DIB9000 config DVB_TDA10048 tristate "Philips TDA10048HN based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_AF9013 tristate "Afatech AF9013 demodulator" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_EC100 tristate "E3C EC100" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_HD29L2 tristate "HDIC HD29L2" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_STV0367 tristate "ST STV0367 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T/C tuner module. Say Y when you want to support this frontend. config DVB_CXD2820R tristate "Sony CXD2820R" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_RTL2830 tristate "Realtek RTL2830 DVB-T" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. config DVB_RTL2832 tristate "Realtek RTL2832 DVB-T" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. @@ -446,28 +431,28 @@ comment "DVB-C (cable) frontends" config DVB_VES1820 tristate "VLSI VES1820 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_TDA10021 tristate "Philips TDA10021 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_TDA10023 tristate "Philips TDA10023 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_STV0297 tristate "ST STV0297 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C tuner module. Say Y when you want to support this frontend. @@ -477,7 +462,7 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" config DVB_NXT200X tristate "NxtWave Communications NXT2002/NXT2004 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -491,7 +476,7 @@ config DVB_NXT200X config DVB_OR51211 tristate "Oren OR51211 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB tuner module. Say Y when you want to support this frontend. @@ -503,7 +488,7 @@ config DVB_OR51211 config DVB_OR51132 tristate "Oren OR51132 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -518,7 +503,7 @@ config DVB_OR51132 config DVB_BCM3510 tristate "Broadcom BCM3510" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -526,7 +511,7 @@ config DVB_BCM3510 config DVB_LGDT330X tristate "LG Electronics LGDT3302/LGDT3303 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -534,7 +519,7 @@ config DVB_LGDT330X config DVB_LGDT3305 tristate "LG Electronics LGDT3304 and LGDT3305 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -542,7 +527,7 @@ config DVB_LGDT3305 config DVB_LG2160 tristate "LG Electronics LG216x based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC/MH demodulator module. Say Y when you want to support this frontend. @@ -550,7 +535,7 @@ config DVB_LG2160 config DVB_S5H1409 tristate "Samsung S5H1409 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -563,7 +548,7 @@ config DVB_AU8522_DTV tristate "Auvitek AU8522 based DTV demod" depends on DVB_CORE && I2C select DVB_AU8522 - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when you want to enable DTV demodulation support for this frontend. @@ -572,7 +557,7 @@ config DVB_AU8522_V4L tristate "Auvitek AU8522 based ATV demod" depends on VIDEO_V4L2 && I2C select DVB_AU8522 - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when you want to enable ATV demodulation support for this frontend. @@ -580,7 +565,7 @@ config DVB_AU8522_V4L config DVB_S5H1411 tristate "Samsung S5H1411 based" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -591,7 +576,7 @@ comment "ISDB-T (terrestrial) frontends" config DVB_S921 tristate "Sharp S921 frontend" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. Say Y when you want to support this frontend. @@ -599,7 +584,7 @@ config DVB_S921 config DVB_DIB8000 tristate "DiBcom 8000MB/MC" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. Say Y when you want to support this frontend. @@ -607,7 +592,7 @@ config DVB_DIB8000 config DVB_MB86A20S tristate "Fujitsu mb86a20s" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. Say Y when you want to support this frontend. @@ -618,7 +603,7 @@ comment "Digital terrestrial only tuners/PLL" config DVB_PLL tristate "Generic I2C PLL based tuners" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help This module drives a number of tuners based on PLL chips with a common I2C interface. Say Y when you want to support these tuners. @@ -626,7 +611,7 @@ config DVB_PLL config DVB_TUNER_DIB0070 tristate "DiBcom DiB0070 silicon base-band tuner" depends on I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon baseband tuner DiB0070 from DiBcom. This device is only used inside a SiP called together with a @@ -635,7 +620,7 @@ config DVB_TUNER_DIB0070 config DVB_TUNER_DIB0090 tristate "DiBcom DiB0090 silicon base-band tuner" depends on I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon baseband tuner DiB0090 from DiBcom. This device is only used inside a SiP called together with a @@ -647,14 +632,14 @@ comment "SEC control devices for DVB-S" config DVB_LNBP21 tristate "LNBP21/LNBH24 SEC controllers" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An SEC control chips. config DVB_LNBP22 tristate "LNBP22 SEC controllers" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help LNB power supply and control voltage regulator chip with step-up converter @@ -664,33 +649,33 @@ config DVB_LNBP22 config DVB_ISL6405 tristate "ISL6405 SEC controller" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An SEC control chip. config DVB_ISL6421 tristate "ISL6421 SEC controller" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An SEC control chip. config DVB_ISL6423 tristate "ISL6423 SEC controller" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A SEC controller chip from Intersil config DVB_A8293 tristate "Allegro A8293" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT config DVB_LGS8GL5 tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DMB-TH tuner module. Say Y when you want to support this frontend. @@ -698,21 +683,21 @@ config DVB_LGS8GXX tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" depends on DVB_CORE && I2C select FW_LOADER - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DMB-TH tuner module. Say Y when you want to support this frontend. config DVB_ATBM8830 tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DMB-TH tuner module. Say Y when you want to support this frontend. config DVB_TDA665x tristate "TDA665x tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help Support for tuner modules based on Philips TDA6650/TDA6651 chips. Say Y when you want to support this chip. @@ -723,14 +708,14 @@ config DVB_TDA665x config DVB_IX2505V tristate "Sharp IX2505V silicon tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_IT913X_FE tristate "it913x frontend and it9137 tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -738,7 +723,7 @@ config DVB_IT913X_FE config DVB_M88RS2000 tristate "M88RS2000 DVB-S demodulator and tuner" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. @@ -746,7 +731,7 @@ config DVB_M88RS2000 config DVB_AF9033 tristate "Afatech AF9033 DVB-T demodulator" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT comment "Tools to develop new frontends" diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 527430ac06f3..d41dc0aa005e 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -16,21 +16,8 @@ config VIDEO_TVEEPROM if VIDEO_V4L2 -config VIDEO_HELPER_CHIPS_AUTO - bool "Autoselect pertinent encoders/decoders and other helper chips" - default y if !EXPERT - ---help--- - Most video cards may require additional modules to encode or - decode audio/video standards. This option will autoselect - all pertinent modules to each selected video module. - - Unselect this only if you know exactly what you are doing, since - it may break support on some boards. - - In doubt, say Y. - config VIDEO_IR_I2C - tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO + tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT depends on I2C && RC_CORE default y ---help--- @@ -47,7 +34,7 @@ config VIDEO_IR_I2C # menu "Encoders, decoders, sensors and other helper chips" - visible if !VIDEO_HELPER_CHIPS_AUTO + visible if !MEDIA_SUBDRV_AUTOSELECT comment "Audio decoders, processors and mixers" @@ -561,10 +548,14 @@ config VIDEO_M52790 To compile this driver as a module, choose M here: the module will be called m52790. +endmenu + +menu "Sensors used on soc_camera driver" if SOC_CAMERA source "drivers/media/i2c/soc_camera/Kconfig" endif endmenu + endif diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig index f2667a5cc144..61d09e010814 100644 --- a/drivers/media/pci/bt8xx/Kconfig +++ b/drivers/media/pci/bt8xx/Kconfig @@ -7,10 +7,10 @@ config VIDEO_BT848 depends on RC_CORE select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in @@ -22,14 +22,14 @@ config VIDEO_BT848 config DVB_BT8XX tristate "DVB/ATSC Support for bt878 based TV cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_SP887X if !DVB_FE_CUSTOMISE - select DVB_NXT6000 if !DVB_FE_CUSTOMISE - select DVB_CX24110 if !DVB_FE_CUSTOMISE - select DVB_OR51211 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SP887X if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_OR51211 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, diff --git a/drivers/media/pci/cx18/Kconfig b/drivers/media/pci/cx18/Kconfig index 53b3c7702573..9a9f765dad45 100644 --- a/drivers/media/pci/cx18/Kconfig +++ b/drivers/media/pci/cx18/Kconfig @@ -8,11 +8,11 @@ config VIDEO_CX18 select VIDEO_TVEEPROM select VIDEO_CX2341X select VIDEO_CS5345 - select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index b391e9bda877..9c6afe922586 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -11,23 +11,23 @@ config VIDEO_CX23885 select VIDEOBUF_DMA_SG select VIDEO_CX25840 select VIDEO_CX2341X - select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE - select DVB_DS3000 if !DVB_FE_CUSTOMISE - select DVB_STV0367 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE + select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig index 3598dc087b08..d27fccbf03c4 100644 --- a/drivers/media/pci/cx88/Kconfig +++ b/drivers/media/pci/cx88/Kconfig @@ -6,7 +6,7 @@ config VIDEO_CX88 select VIDEOBUF_DMA_SG select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_WM8775 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 2388x based TV cards. @@ -46,23 +46,23 @@ config VIDEO_CX88_DVB tristate "DVB/ATSC Support for cx2388x based TV cards" depends on VIDEO_CX88 && DVB_CORE select VIDEOBUF_DVB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_OR51132 if !DVB_FE_CUSTOMISE - select DVB_CX22702 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_CX24123 if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_OR51132 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig index d099e1a12c85..44e5dc15e60a 100644 --- a/drivers/media/pci/ddbridge/Kconfig +++ b/drivers/media/pci/ddbridge/Kconfig @@ -1,11 +1,11 @@ config DVB_DDBRIDGE tristate "Digital Devices bridge support" depends on DVB_CORE && PCI && I2C - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for cards with the Digital Devices PCI express bridge: - Octopus PCIe Bridge diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig index f3de0a4d63f2..013df4e015cd 100644 --- a/drivers/media/pci/dm1105/Kconfig +++ b/drivers/media/pci/dm1105/Kconfig @@ -1,13 +1,13 @@ config DVB_DM1105 tristate "SDMC DM1105 based PCI cards" depends on DVB_CORE && PCI && I2C - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_SI21XX if !DVB_FE_CUSTOMISE - select DVB_DS3000 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT depends on RC_CORE help Support for cards based on the SDMC DM1105 PCI chip like diff --git a/drivers/media/pci/mantis/Kconfig b/drivers/media/pci/mantis/Kconfig index a13a50503134..d3cc21633b94 100644 --- a/drivers/media/pci/mantis/Kconfig +++ b/drivers/media/pci/mantis/Kconfig @@ -10,15 +10,15 @@ config MANTIS_CORE config DVB_MANTIS tristate "MANTIS based cards" depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_MB86A16 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_TDA665x if !DVB_FE_CUSTOMISE - select DVB_TDA10021 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_MB86A16 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA665x if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT select DVB_PLL help Support for PCI cards based on the Mantis PCI bridge. @@ -29,7 +29,7 @@ config DVB_MANTIS config DVB_HOPPER tristate "HOPPER based cards" depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT select DVB_PLL help Support for PCI cards based on the Hopper PCI bridge. diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig index 64c84702ba5c..637d506b23c5 100644 --- a/drivers/media/pci/ngene/Kconfig +++ b/drivers/media/pci/ngene/Kconfig @@ -1,13 +1,13 @@ config DVB_NGENE tristate "Micronas nGene support" depends on DVB_CORE && PCI && I2C - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for Micronas PCI express cards with nGene bridge. diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig index 39fc0187a747..15b90d6e9130 100644 --- a/drivers/media/pci/saa7134/Kconfig +++ b/drivers/media/pci/saa7134/Kconfig @@ -5,7 +5,7 @@ config VIDEO_SAA7134 select VIDEO_TUNER select VIDEO_TVEEPROM select CRC32 - select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Philips SAA713x based TV cards. @@ -37,25 +37,25 @@ config VIDEO_SAA7134_DVB tristate "DVB/ATSC Support for saa7134 based TV cards" depends on VIDEO_SAA7134 && DVB_CORE select VIDEOBUF_DVB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_NXT200X if !DVB_FE_CUSTOMISE - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select DVB_ISL6405 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select DVB_ZL10036 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE - select DVB_ZL10039 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6405 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10036 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig index 8923b762bbab..da88b77a916c 100644 --- a/drivers/media/pci/saa7146/Kconfig +++ b/drivers/media/pci/saa7146/Kconfig @@ -26,10 +26,10 @@ config VIDEO_MXB depends on PCI && VIDEO_V4L2 && I2C select VIDEO_SAA7146_VV select VIDEO_TUNER - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for the 'Multimedia eXtension Board' TV card by Siemens-Nixdorf. diff --git a/drivers/media/pci/saa7164/Kconfig b/drivers/media/pci/saa7164/Kconfig index 353263725172..84637965287e 100644 --- a/drivers/media/pci/saa7164/Kconfig +++ b/drivers/media/pci/saa7164/Kconfig @@ -6,9 +6,9 @@ config VIDEO_SAA7164 select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEOBUF_DVB - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for NXP SAA7164 based TV cards. diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 04a82cbd7c91..6749f67cab8a 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,7 +1,7 @@ config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" depends on STA2X11 - select VIDEO_ADV7180 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS help diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig index 9d83ced69dd6..314e417addae 100644 --- a/drivers/media/pci/ttpci/Kconfig +++ b/drivers/media/pci/ttpci/Kconfig @@ -9,14 +9,14 @@ config DVB_AV7110 select TTPCI_EEPROM select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_SP8870 if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_L64781 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT help Support for SAA7146 and AV7110 based DVB cards as produced by Fujitsu-Siemens, Technotrend, Hauppauge and others. @@ -63,19 +63,19 @@ config DVB_BUDGET_CORE config DVB_BUDGET tristate "Budget cards" depends on DVB_BUDGET_CORE && I2C - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_L64781 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_S5H1420 if !DVB_FE_CUSTOMISE - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_ISL6423 if !DVB_FE_CUSTOMISE - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard MPEG2 decoder, and without @@ -89,16 +89,16 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_BUDGET_CORE && I2C - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT depends on RC_CORE help Support for simple SAA7146 based DVB cards @@ -118,14 +118,14 @@ config DVB_BUDGET_AV depends on DVB_BUDGET_CORE && I2C select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_TDA10021 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_TDA8261 if !DVB_FE_CUSTOMISE - select DVB_TUA6100 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -140,9 +140,9 @@ config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" depends on DVB_BUDGET_CORE && I2C depends on DVB_AV7110 - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_VES1X93 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT help Support for Budget Patch (full TS) modification on SAA7146+AV7110 based cards (DVB-S cards). This diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig index fd4120e4c104..a9b231808413 100644 --- a/drivers/media/pci/zoran/Kconfig +++ b/drivers/media/pci/zoran/Kconfig @@ -14,8 +14,8 @@ config VIDEO_ZORAN config VIDEO_ZORAN_DC30 tristate "Pinnacle/Miro DC30(+) support" depends on VIDEO_ZORAN - select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT help Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback card. This also supports really old DC10 cards based on the @@ -32,16 +32,16 @@ config VIDEO_ZORAN_ZR36060 config VIDEO_ZORAN_BUZ tristate "Iomega Buz support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT help Support for the Iomega Buz MJPEG capture/playback card. config VIDEO_ZORAN_DC10 tristate "Pinnacle/Miro DC10(+) support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT help Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback card. @@ -49,8 +49,8 @@ config VIDEO_ZORAN_DC10 config VIDEO_ZORAN_LML33 tristate "Linux Media Labs LML33 support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT help Support for the Linux Media Labs LML33 MJPEG capture/playback card. @@ -58,8 +58,8 @@ config VIDEO_ZORAN_LML33 config VIDEO_ZORAN_LML33R10 tristate "Linux Media Labs LML33R10 support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT help support for the Linux Media Labs LML33R10 MJPEG capture/playback card. @@ -67,8 +67,8 @@ config VIDEO_ZORAN_LML33R10 config VIDEO_ZORAN_AVS6EYES tristate "AverMedia 6 Eyes support (EXPERIMENTAL)" depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL - select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_BT866 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT help Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 5955a276f468..d4c034d2bd8d 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -66,7 +66,7 @@ config VIDEO_TIMBERDALE config VIDEO_VINO tristate "SGI Vino Video For Linux" depends on I2C && SGI_IP22 && VIDEO_V4L2 - select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to build in support for the Vino video input system found on SGI Indy machines. diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 52c5ca68cb3d..78e26d24f637 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -3,8 +3,8 @@ config VIDEO_DAVINCI_VPIF_DISPLAY depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) select VIDEOBUF2_DMA_CONTIG select VIDEO_DAVINCI_VPIF - select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT help Enables Davinci VPIF module used for display devices. This module is common for following DM6467/DA850/OMAPL138 diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 94c6ff7a5da3..80238b9063b0 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -18,43 +18,31 @@ config MEDIA_ATTACH If unsure say Y. +# Analog TV tuners, auto-loaded via tuner.ko 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. + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT20XX if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TEA5761 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT && EXPERIMENTAL + select MEDIA_TUNER_TEA5767 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA9887 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT menu "Customize TV tuners" - visible if MEDIA_TUNER_CUSTOMISE + visible if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for various simple tuners. @@ -63,28 +51,28 @@ config MEDIA_TUNER_TDA8290 depends on MEDIA_SUPPORT && I2C select MEDIA_TUNER_TDA827X select MEDIA_TUNER_TDA18271 - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for Philips TDA9885/6/7 analog IF demodulator. @@ -93,70 +81,70 @@ config MEDIA_TUNER_TEA5761 tristate "TEA 5761 radio tuner (EXPERIMENTAL)" depends on MEDIA_SUPPORT && I2C depends on EXPERIMENTAL - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner XC5000 from Xceive. This device is only used inside a SiP called together with a @@ -165,7 +153,7 @@ config MEDIA_TUNER_XC5000 config MEDIA_TUNER_XC4000 tristate "Xceive XC4000 silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help A driver for the silicon tuner XC4000 from Xceive. This device is only used inside a SiP called together with a @@ -174,70 +162,70 @@ config MEDIA_TUNER_XC4000 config MEDIA_TUNER_MXL5005S tristate "MaxLinear MSL5005S silicon tuner" depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT 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 + default m if !MEDIA_SUBDRV_AUTOSELECT help Infineon TUA 9001 silicon tuner driver. endmenu diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 23f7fd22f0eb..385e557ba910 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -6,11 +6,11 @@ config VIDEO_AU0828 select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC - select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE - select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT + select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Auvitek's USB device. diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 446f692aabb7..77913dfbd238 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -42,9 +42,9 @@ config VIDEO_CX231XX_DVB tristate "DVB/ATSC Support for Cx231xx based TV cards" depends on VIDEO_CX231XX && DVB_CORE && DVB_CAPTURE_DRIVERS select VIDEOBUF_DVB - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select DVB_MB86A20S if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 276374fbaf4f..967115104047 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -21,14 +21,14 @@ config DVB_USB_AF9015 tristate "Afatech AF9015 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_AF9013 - select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver @@ -36,26 +36,26 @@ config DVB_USB_AF9035 tristate "Afatech AF9035 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_AF9033 - select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9035 based DVB USB receiver. config DVB_USB_ANYSEE tristate "Anysee DVB-T/C USB2.0 support" depends on DVB_USB_V2 - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18212 if !MEDIA_TUNER_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE - select DVB_STV6110 if !DVB_FE_CUSTOMISE - select DVB_ISL6423 if !DVB_FE_CUSTOMISE - select DVB_CXD2820R if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2820R if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Anysee E30, Anysee E30 Plus or Anysee E30 C Plus DVB USB2.0 receiver. @@ -63,8 +63,8 @@ config DVB_USB_ANYSEE config DVB_USB_AU6610 tristate "Alcor Micro AU6610 USB2.0 support" depends on DVB_USB_V2 - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. @@ -72,8 +72,8 @@ config DVB_USB_AZ6007 tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support" depends on DVB_USB_V2 select DVB_USB_CYPRESS_FIRMWARE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AZ6007 receivers like Terratec H7. @@ -81,7 +81,7 @@ config DVB_USB_CE6230 tristate "Intel CE6230 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_ZL10353 - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver @@ -89,15 +89,15 @@ config DVB_USB_EC168 tristate "E3C EC168 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_EC100 - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. config DVB_USB_GL861 tristate "Genesys Logic GL861 USB2.0 support" depends on DVB_USB_V2 - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 receiver with USB ID 0db0:5581. @@ -112,21 +112,21 @@ config DVB_USB_IT913X config DVB_USB_LME2510 tristate "LME DM04/QQBOX DVB-S USB2.0 support" depends on DVB_USB_V2 - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_IX2505V if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_M88RS2000 if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_IX2505V if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 config DVB_USB_MXL111SF tristate "MxL111SF DTV USB2.0 support" depends on DVB_USB_V2 - select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select DVB_LG2160 if !DVB_FE_CUSTOMISE + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LG2160 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVEEPROM help Say Y here to support the MxL111SF USB2.0 DTV receiver. @@ -136,11 +136,11 @@ config DVB_USB_RTL28XXU depends on DVB_USB_V2 && EXPERIMENTAL select DVB_RTL2830 select DVB_RTL2832 - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_FC0012 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_FC0013 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 00173ee15d4a..3c5fff89dbf1 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -24,17 +24,17 @@ config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" depends on DVB_USB select DVB_DIB3000MC - select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. config DVB_USB_DIBUSB_MB tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_DIB3000MB - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-B demodulator. @@ -55,7 +55,7 @@ config DVB_USB_DIBUSB_MC tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Support for USB2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-C/P demodulator. @@ -69,20 +69,20 @@ config DVB_USB_DIBUSB_MC config DVB_USB_DIB0700 tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" depends on DVB_USB - select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select DVB_DIB7000M if !DVB_FE_CUSTOMISE - select DVB_DIB8000 if !DVB_FE_CUSTOMISE - select DVB_DIB3000MC if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE - select DVB_TUNER_DIB0090 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE - 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_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB7000M if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB8000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0090 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2266 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT help Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The USB bridge is also present in devices having the DiB7700 DVB-T-USB @@ -98,29 +98,29 @@ config DVB_USB_DIB0700 config DVB_USB_UMT_010 tristate "HanfTek UMT-010 DVB-T USB2.0 support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. config DVB_USB_CXUSB tristate "Conexant USB2.0 hybrid reference design support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_CX22702 if !DVB_FE_CUSTOMISE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE - select DVB_ATBM8830 if !DVB_FE_CUSTOMISE - select DVB_LGS8GXX if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ATBM8830 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGS8GXX if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MAX2165 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -132,11 +132,11 @@ config DVB_USB_CXUSB config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB - select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. Currently, only devices with a product id of @@ -146,9 +146,9 @@ config DVB_USB_M920X config DVB_USB_DIGITV tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_NXT6000 if !DVB_FE_CUSTOMISE - select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. @@ -191,17 +191,17 @@ config DVB_USB_NOVA_T_USB2 tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB select DVB_DIB3000MC - select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. config DVB_USB_TTUSB2 tristate "Pinnacle 400e DVB-S USB2.0 support" depends on DVB_USB - select DVB_TDA10086 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE - select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The firmware protocol used by this module is similar to the one used by the @@ -220,16 +220,16 @@ config DVB_USB_DTT200U config DVB_USB_OPERA1 tristate "Opera1 DVB-S USB2.0 receiver" depends on DVB_USB - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Opera DVB-S USB2.0 receiver. config DVB_USB_AF9005 tristate "Afatech AF9005 DVB-T USB1.1 support" depends on DVB_USB && EXPERIMENTAL - select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver and the TerraTec Cinergy T USB XE (Rev.1) @@ -245,9 +245,9 @@ config DVB_USB_PCTV452E tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" depends on DVB_USB select TTPCI_EEPROM - select DVB_LNBP22 if !DVB_FE_CUSTOMISE - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_LNBP22 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT help Support for external USB adapter designed by Pinnacle, shipped under the brand name 'PCTV HDTV Pro USB'. @@ -257,19 +257,19 @@ config DVB_USB_PCTV452E config DVB_USB_DW2102 tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support" depends on DVB_USB - select DVB_PLL if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0288 if !DVB_FE_CUSTOMISE - select DVB_STB6000 if !DVB_FE_CUSTOMISE - select DVB_CX24116 if !DVB_FE_CUSTOMISE - select DVB_SI21XX if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_MT312 if !DVB_FE_CUSTOMISE - select DVB_ZL10039 if !DVB_FE_CUSTOMISE - select DVB_DS3000 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE - select DVB_STV6110 if !DVB_FE_CUSTOMISE - select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 receivers. @@ -285,8 +285,8 @@ config DVB_USB_CINERGY_T2 config DVB_USB_DTV5100 tristate "AME DTV-5100 USB2.0 DVB-T support" depends on DVB_USB - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. @@ -299,15 +299,15 @@ config DVB_USB_FRIIO config DVB_USB_AZ6027 tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" depends on DVB_USB - select DVB_STB0899 if !DVB_FE_CUSTOMISE - select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the AZ6027 device config DVB_USB_TECHNISAT_USB2 tristate "Technisat DVB-S/S2 USB2.0 support" depends on DVB_USB - select DVB_STV090x if !DVB_FE_CUSTOMISE - select DVB_STV6110x if !DVB_FE_CUSTOMISE + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Technisat USB2 DVB-S/S2 device diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 928ef0d0429f..7a5bd61bd3bb 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -4,10 +4,10 @@ config VIDEO_EM28XX select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_MT9V011 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Empia 28xx based TV cards. @@ -33,16 +33,16 @@ config VIDEO_EM28XX_ALSA config VIDEO_EM28XX_DVB tristate "DVB/ATSC Support for em28xx based TV cards" depends on VIDEO_EM28XX && DVB_CORE - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select DVB_S921 if !DVB_FE_CUSTOMISE - select DVB_DRXD if !DVB_FE_CUSTOMISE - select DVB_CXD2820R if !DVB_FE_CUSTOMISE - select DVB_DRXK if !DVB_FE_CUSTOMISE - select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE - select DVB_TDA10071 if !DVB_FE_CUSTOMISE - select DVB_A8293 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXD if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2820R if MEDIA_SUBDRV_AUTOSELECT + select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT + select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/pvrusb2/Kconfig b/drivers/media/usb/pvrusb2/Kconfig index 25e412ecad2c..32b11c15bb1a 100644 --- a/drivers/media/usb/pvrusb2/Kconfig +++ b/drivers/media/usb/pvrusb2/Kconfig @@ -36,13 +36,13 @@ config VIDEO_PVRUSB2_DVB bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)" default y depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL - select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT ---help--- This option enables a DVB interface for the pvrusb2 driver. diff --git a/drivers/media/usb/ttusb-budget/Kconfig b/drivers/media/usb/ttusb-budget/Kconfig index 2663ae39b886..97bad7da689c 100644 --- a/drivers/media/usb/ttusb-budget/Kconfig +++ b/drivers/media/usb/ttusb-budget/Kconfig @@ -1,13 +1,13 @@ config DVB_TTUSB_BUDGET tristate "Technotrend/Hauppauge Nova-USB devices" depends on DVB_CORE && USB && I2C && PCI - select DVB_CX22700 if !DVB_FE_CUSTOMISE - select DVB_TDA1004X if !DVB_FE_CUSTOMISE - select DVB_VES1820 if !DVB_FE_CUSTOMISE - select DVB_TDA8083 if !DVB_FE_CUSTOMISE - select DVB_STV0299 if !DVB_FE_CUSTOMISE - select DVB_STV0297 if !DVB_FE_CUSTOMISE - select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_CX22700 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT help Support for external USB adapters designed by Technotrend and produced by Hauppauge, shipped under the brand name 'Nova-USB'. diff --git a/drivers/media/usb/usbvision/Kconfig b/drivers/media/usb/usbvision/Kconfig index fc24ef05b3f3..6b6afc5d8f7e 100644 --- a/drivers/media/usb/usbvision/Kconfig +++ b/drivers/media/usb/usbvision/Kconfig @@ -2,7 +2,7 @@ config VIDEO_USBVISION tristate "USB video devices based on Nogatech NT1003/1004/1005" depends on I2C && VIDEO_V4L2 select VIDEO_TUNER - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT ---help--- There are more than 50 different USB video devices based on NT1003/1004/1005 USB Bridges. This driver enables using those -- cgit v1.2.3 From 8511f8eaa86bb16e4e2bd5f30d5f12764f59ae8d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Aug 2012 16:49:23 -0300 Subject: [media] flexcop: Show the item to enable debug after the driver Instead of showing the option to show debug at the end, show it after each driver. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/b2c2/Kconfig | 7 ++----- drivers/media/pci/b2c2/Kconfig | 8 ++++++++ drivers/media/usb/b2c2/Kconfig | 8 ++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig index 29149de66982..1df9e578daa5 100644 --- a/drivers/media/common/b2c2/Kconfig +++ b/drivers/media/common/b2c2/Kconfig @@ -23,9 +23,6 @@ config DVB_B2C2_FLEXCOP Say Y if you own such a device and want to use it. +# Selected via the PCI or USB flexcop drivers config DVB_B2C2_FLEXCOP_DEBUG - bool "Enable debug for the B2C2 FlexCop drivers" - depends on DVB_B2C2_FLEXCOP - help - Say Y if you want to enable the module option to control debug messages - of all B2C2 FlexCop drivers. + bool diff --git a/drivers/media/pci/b2c2/Kconfig b/drivers/media/pci/b2c2/Kconfig index aaa1f30f1ae0..78ced474aa8d 100644 --- a/drivers/media/pci/b2c2/Kconfig +++ b/drivers/media/pci/b2c2/Kconfig @@ -4,3 +4,11 @@ config DVB_B2C2_FLEXCOP_PCI Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2. Say Y if you own such a device and want to use it. + +config DVB_B2C2_FLEXCOP_PCI_DEBUG + bool "Enable debug for the B2C2 FlexCop drivers" + depends on DVB_B2C2_FLEXCOP_PCI + select DVB_B2C2_FLEXCOP_DEBUG + help + Say Y if you want to enable the module option to control debug messages + of all B2C2 FlexCop drivers. diff --git a/drivers/media/usb/b2c2/Kconfig b/drivers/media/usb/b2c2/Kconfig index 3af7c4155473..ba16583c5e13 100644 --- a/drivers/media/usb/b2c2/Kconfig +++ b/drivers/media/usb/b2c2/Kconfig @@ -4,3 +4,11 @@ config DVB_B2C2_FLEXCOP_USB Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2, Say Y if you own such a device and want to use it. + +config DVB_B2C2_FLEXCOP_USB_DEBUG + bool "Enable debug for the B2C2 FlexCop drivers" + depends on DVB_B2C2_FLEXCOP_USB + select DVB_B2C2_FLEXCOP_DEBUG + help + Say Y if you want to enable the module option to control debug messages + of all B2C2 FlexCop drivers. -- cgit v1.2.3 From e9d2f0573e918d0b2ed08ece1c4bfe850477f8cf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 21 Aug 2012 08:16:28 -0300 Subject: [media] Add missing help for some menuconfig items Help was missing during some items reorganization. Add them. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/parport/Kconfig | 5 ++++- drivers/media/pci/Kconfig | 3 +++ drivers/media/usb/Kconfig | 3 +++ 4 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d41dc0aa005e..9a5a059c1bde 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -549,7 +549,7 @@ config VIDEO_M52790 To compile this driver as a module, choose M here: the module will be called m52790. endmenu - + menu "Sensors used on soc_camera driver" if SOC_CAMERA diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig index a1c7853f3a96..ece13dcff07d 100644 --- a/drivers/media/parport/Kconfig +++ b/drivers/media/parport/Kconfig @@ -1,6 +1,9 @@ menuconfig MEDIA_PARPORT_SUPPORT - bool "V4L ISA and parallel port devices" + bool "ISA and parallel port devices" depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT + help + Enables drivers for ISA and parallel port bus. If you + need media drivers using those legacy buses, say Y. if MEDIA_PARPORT_SUPPORT config VIDEO_BWQCAM diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 083b62f8b970..d4e2ed3f27e5 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -1,6 +1,9 @@ menuconfig MEDIA_PCI_SUPPORT bool "Media PCI Adapters" depends on PCI && MEDIA_SUPPORT + help + Enable media drivers for PCI/PCIe bus. + If you have such devices, say Y. if MEDIA_PCI_SUPPORT diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index f960e7ca4738..6746994d03fe 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -1,6 +1,9 @@ menuconfig MEDIA_USB_SUPPORT bool "Media USB Adapters" depends on USB && MEDIA_SUPPORT + help + Enable media drivers for USB bus. + If you have such devices, say Y. if MEDIA_USB_SUPPORT -- cgit v1.2.3 From 01b0c11a1ba49ac96f58b7bc92772c2b469d6caa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Aug 2012 15:52:23 -0300 Subject: [media] Kconfig: Fix b2c2 common code selection As reported by Randy: > flexcop-pci.c:(.text+0x19af63): undefined reference to `flexcop_device_exit' > flexcop-pci.c:(.text+0x19af77): undefined reference to `flexcop_device_kfree' > flexcop-pci.c:(.text+0x19b10f): undefined reference to `flexcop_pass_dmx_packets' > flexcop-pci.c:(.text+0x19b182): undefined reference to `flexcop_pass_dmx_data' > flexcop-pci.c:(.text+0x19b1ae): undefined reference to `flexcop_pass_dmx_data' > flexcop-pci.c:(.text+0x19b1f8): undefined reference to `flexcop_device_kmalloc' > flexcop-pci.c:(.text+0x19b256): undefined reference to `flexcop_i2c_request' > flexcop-pci.c:(.text+0x19b261): undefined reference to `flexcop_eeprom_check_mac_addr' > flexcop-pci.c:(.text+0x19b2c6): undefined reference to `flexcop_device_initialize' > flexcop-pci.c:(.text+0x19b332): undefined reference to `flexcop_sram_set_dest' > flexcop-pci.c:(.text+0x19b348): undefined reference to `flexcop_sram_set_dest' > flexcop-pci.c:(.text+0x19b3f8): undefined reference to `flexcop_device_exit' > flexcop-pci.c:(.text+0x19b408): undefined reference to `flexcop_device_kfree' > flexcop-pci.c:(.text+0x19b4a2): undefined reference to `flexcop_pid_feed_control' > flexcop-pci.c:(.text+0x19b4d7): undefined reference to `flexcop_pid_feed_control' > > since it is possible to enable DVB_B2C2_FLEXCOP_PCI > when CONFIG_I2C is not enabled, but then DVB_B2C2_FLEXCOP > is not enabled because I2C is not enabled. Reported-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/b2c2/Kconfig | 1 + drivers/media/usb/b2c2/Kconfig | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/pci/b2c2/Kconfig b/drivers/media/pci/b2c2/Kconfig index 78ced474aa8d..58761a21caa0 100644 --- a/drivers/media/pci/b2c2/Kconfig +++ b/drivers/media/pci/b2c2/Kconfig @@ -1,5 +1,6 @@ config DVB_B2C2_FLEXCOP_PCI tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI" + depends on DVB_CORE && I2C help Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2. diff --git a/drivers/media/usb/b2c2/Kconfig b/drivers/media/usb/b2c2/Kconfig index ba16583c5e13..17d35833980c 100644 --- a/drivers/media/usb/b2c2/Kconfig +++ b/drivers/media/usb/b2c2/Kconfig @@ -1,5 +1,6 @@ config DVB_B2C2_FLEXCOP_USB tristate "Technisat/B2C2 Air/Sky/Cable2PC USB" + depends on DVB_CORE && I2C help Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2, -- cgit v1.2.3 From 64cbeb2818c9308a9fa9e25d50b676f66335cd9f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Sep 2012 08:12:08 -0300 Subject: [media] au0828, cx231xx: remove dependency for DVB_CAPTURE_DRIVERS This symbol got removed by menu reorganization; just depending on DVB_CORE is enough. Reported-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/Kconfig | 1 - drivers/media/usb/cx231xx/Kconfig | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 385e557ba910..1766c0ce93be 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -2,7 +2,6 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 - depends on DVB_CAPTURE_DRIVERS select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 77913dfbd238..86feeeaf61c2 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -40,7 +40,7 @@ config VIDEO_CX231XX_ALSA config VIDEO_CX231XX_DVB tristate "DVB/ATSC Support for Cx231xx based TV cards" - depends on VIDEO_CX231XX && DVB_CORE && DVB_CAPTURE_DRIVERS + depends on VIDEO_CX231XX && DVB_CORE select VIDEOBUF_DVB select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT -- cgit v1.2.3 From 7fa8694acf90f16b845d6ec7d1a6c6728ec8768b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 16 Aug 2012 22:07:08 -0300 Subject: [media] dvb_usb_v2: use ratelimited debugs where appropriate Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/usb_urb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c index eaf673a3978d..5989b6590377 100644 --- a/drivers/media/usb/dvb-usb-v2/usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c @@ -22,9 +22,9 @@ static void usb_urb_complete(struct urb *urb) int i; u8 *b; - dev_dbg(&stream->udev->dev, "%s: %s urb completed status=%d " \ - "length=%d/%d pack_num=%d errors=%d\n", __func__, - ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk", + dev_dbg_ratelimited(&stream->udev->dev, "%s: %s urb completed " \ + "status=%d length=%d/%d pack_num=%d errors=%d\n", + __func__, ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk", urb->status, urb->actual_length, urb->transfer_buffer_length, urb->number_of_packets, urb->error_count); @@ -38,7 +38,8 @@ static void usb_urb_complete(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_dbg(&stream->udev->dev, "%s: urb completition failed=%d\n", + dev_dbg_ratelimited(&stream->udev->dev, + "%s: urb completition failed=%d\n", __func__, urb->status); break; } -- cgit v1.2.3 From faedc36377258dc2d7e5b847db403e6e13794b1c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 16 Aug 2012 22:15:56 -0300 Subject: [media] dvb-usb: remove unused files Those files were left from dvb-usb-v2 development as I have made mistake during rebase operation. Reported-by: Hans Verkuil Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dvb_usb_dvb.c | 403 ----------------------------- drivers/media/usb/dvb-usb/dvb_usb_remote.c | 117 --------- 2 files changed, 520 deletions(-) delete mode 100644 drivers/media/usb/dvb-usb/dvb_usb_dvb.c delete mode 100644 drivers/media/usb/dvb-usb/dvb_usb_remote.c (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb/dvb_usb_dvb.c b/drivers/media/usb/dvb-usb/dvb_usb_dvb.c deleted file mode 100644 index 384fe8eec21f..000000000000 --- a/drivers/media/usb/dvb-usb/dvb_usb_dvb.c +++ /dev/null @@ -1,403 +0,0 @@ -/* dvb-usb-dvb.c is part of the DVB USB library. - * - * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) - * see dvb-usb-init.c for copyright information. - * - * This file contains functions for initializing and handling the - * linux-dvb API. - */ -#include "dvb_usb_common.h" - -static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, - size_t len) -{ - struct dvb_usb_adapter *adap = stream->user_priv; - dvb_dmx_swfilter(&adap->demux, buf, len); -} - -static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buf, - size_t len) -{ - struct dvb_usb_adapter *adap = stream->user_priv; - dvb_dmx_swfilter_204(&adap->demux, buf, len); -} - -static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, u8 *buf, - size_t len) -{ - struct dvb_usb_adapter *adap = stream->user_priv; - dvb_dmx_swfilter_raw(&adap->demux, buf, len); -} - -int dvb_usbv2_adapter_stream_init(struct dvb_usb_adapter *adap) -{ - pr_debug("%s: adap=%d\n", __func__, adap->id); - - adap->stream.udev = adap_to_d(adap)->udev; - adap->stream.user_priv = adap; - adap->stream.complete = dvb_usb_data_complete; - - return usb_urb_initv2(&adap->stream, &adap->props->stream); -} - -int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap) -{ - pr_debug("%s: adap=%d\n", __func__, adap->id); - usb_urb_exitv2(&adap->stream); - - return 0; -} - -/* does the complete input transfer handling */ -static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int count) -{ - struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; - struct dvb_usb_device *d = adap_to_d(adap); - int ret; - pr_debug("%s: adap=%d active_fe=%d feed_type=%d setting pid [%s]: " \ - "%04x (%04d) at index %d '%s'\n", __func__, adap->id, - adap->active_fe, dvbdmxfeed->type, - adap->pid_filtering ? "yes" : "no", dvbdmxfeed->pid, - dvbdmxfeed->pid, dvbdmxfeed->index, - (count == 1) ? "on" : "off"); - - if (adap->active_fe == -1) - return -EINVAL; - - adap->feed_count += count; - - /* stop feeding if it is last pid */ - if (adap->feed_count == 0) { - pr_debug("%s: stop feeding\n", __func__); - usb_urb_killv2(&adap->stream); - - if (d->props->streaming_ctrl) { - ret = d->props->streaming_ctrl(adap, 0); - if (ret < 0) { - pr_err("%s: streaming_ctrl() failed=%d\n", - KBUILD_MODNAME, ret); - goto err_mutex_unlock; - } - } - mutex_unlock(&adap->sync_mutex); - } - - /* activate the pid on the device pid filter */ - if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && - adap->pid_filtering && - adap->props->pid_filter) - ret = adap->props->pid_filter(adap, dvbdmxfeed->index, - dvbdmxfeed->pid, (count == 1) ? 1 : 0); - if (ret < 0) - pr_err("%s: pid_filter() failed=%d\n", - KBUILD_MODNAME, ret); - - /* start feeding if it is first pid */ - if (adap->feed_count == 1 && count == 1) { - struct usb_data_stream_properties stream_props; - mutex_lock(&adap->sync_mutex); - pr_debug("%s: start feeding\n", __func__); - - /* resolve input and output streaming paramters */ - if (d->props->get_stream_config) { - memcpy(&stream_props, &adap->props->stream, - sizeof(struct usb_data_stream_properties)); - ret = d->props->get_stream_config( - adap->fe[adap->active_fe], - &adap->ts_type, &stream_props); - if (ret < 0) - goto err_mutex_unlock; - } else { - stream_props = adap->props->stream; - } - - switch (adap->ts_type) { - case DVB_USB_FE_TS_TYPE_204: - adap->stream.complete = dvb_usb_data_complete_204; - break; - case DVB_USB_FE_TS_TYPE_RAW: - adap->stream.complete = dvb_usb_data_complete_raw; - break; - case DVB_USB_FE_TS_TYPE_188: - default: - adap->stream.complete = dvb_usb_data_complete; - break; - } - - usb_urb_submitv2(&adap->stream, &stream_props); - - if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && - adap->props->caps & - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF && - adap->props->pid_filter_ctrl) { - ret = adap->props->pid_filter_ctrl(adap, - adap->pid_filtering); - if (ret < 0) { - pr_err("%s: pid_filter_ctrl() failed=%d\n", - KBUILD_MODNAME, ret); - goto err_mutex_unlock; - } - } - - if (d->props->streaming_ctrl) { - ret = d->props->streaming_ctrl(adap, 1); - if (ret < 0) { - pr_err("%s: streaming_ctrl() failed=%d\n", - KBUILD_MODNAME, ret); - goto err_mutex_unlock; - } - } - } - - return 0; -err_mutex_unlock: - mutex_unlock(&adap->sync_mutex); - pr_debug("%s: failed=%d\n", __func__, ret); - return ret; -} - -static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - return dvb_usb_ctrl_feed(dvbdmxfeed, 1); -} - -static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - return dvb_usb_ctrl_feed(dvbdmxfeed, -1); -} - -int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap) -{ - int ret; - struct dvb_usb_device *d = adap_to_d(adap); - pr_debug("%s: adap=%d\n", __func__, adap->id); - - ret = dvb_register_adapter(&adap->dvb_adap, d->name, d->props->owner, - &d->udev->dev, d->props->adapter_nr); - if (ret < 0) { - pr_debug("%s: dvb_register_adapter() failed=%d\n", __func__, - ret); - goto err; - } - - adap->dvb_adap.priv = adap; - - if (d->props->read_mac_address) { - ret = d->props->read_mac_address(adap, - adap->dvb_adap.proposed_mac); - if (ret < 0) - goto err_dmx; - - pr_info("%s: MAC address: %pM\n", KBUILD_MODNAME, - adap->dvb_adap.proposed_mac); - } - - adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; - adap->demux.priv = adap; - adap->demux.filternum = 0; - if (adap->demux.filternum < adap->max_feed_count) - adap->demux.filternum = adap->max_feed_count; - adap->demux.feednum = adap->demux.filternum; - adap->demux.start_feed = dvb_usb_start_feed; - adap->demux.stop_feed = dvb_usb_stop_feed; - adap->demux.write_to_decoder = NULL; - ret = dvb_dmx_init(&adap->demux); - if (ret < 0) { - pr_err("%s: dvb_dmx_init() failed=%d\n", KBUILD_MODNAME, ret); - goto err_dmx; - } - - adap->dmxdev.filternum = adap->demux.filternum; - adap->dmxdev.demux = &adap->demux.dmx; - adap->dmxdev.capabilities = 0; - ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); - if (ret < 0) { - pr_err("%s: dvb_dmxdev_init() failed=%d\n", KBUILD_MODNAME, - ret); - goto err_dmx_dev; - } - - ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); - if (ret < 0) { - pr_err("%s: dvb_net_init() failed=%d\n", KBUILD_MODNAME, ret); - goto err_net_init; - } - - mutex_init(&adap->sync_mutex); - - return 0; -err_net_init: - dvb_dmxdev_release(&adap->dmxdev); -err_dmx_dev: - dvb_dmx_release(&adap->demux); -err_dmx: - dvb_unregister_adapter(&adap->dvb_adap); -err: - adap->dvb_adap.priv = NULL; - return ret; -} - -int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap) -{ - pr_debug("%s: adap=%d\n", __func__, adap->id); - - if (adap->dvb_adap.priv) { - dvb_net_release(&adap->dvb_net); - adap->demux.dmx.close(&adap->demux.dmx); - dvb_dmxdev_release(&adap->dmxdev); - dvb_dmx_release(&adap->demux); - dvb_unregister_adapter(&adap->dvb_adap); - } - - return 0; -} - -static int dvb_usb_fe_wakeup(struct dvb_frontend *fe) -{ - int ret; - struct dvb_usb_adapter *adap = fe->dvb->priv; - struct dvb_usb_device *d = adap_to_d(adap); - mutex_lock(&adap->sync_mutex); - pr_debug("%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); - - ret = dvb_usbv2_device_power_ctrl(d, 1); - if (ret < 0) - goto err; - - if (d->props->frontend_ctrl) { - ret = d->props->frontend_ctrl(fe, 1); - if (ret < 0) - goto err; - } - - if (adap->fe_init[fe->id]) { - ret = adap->fe_init[fe->id](fe); - if (ret < 0) - goto err; - } - - adap->active_fe = fe->id; - mutex_unlock(&adap->sync_mutex); - - return 0; -err: - mutex_unlock(&adap->sync_mutex); - pr_debug("%s: failed=%d\n", __func__, ret); - return ret; -} - -static int dvb_usb_fe_sleep(struct dvb_frontend *fe) -{ - int ret; - struct dvb_usb_adapter *adap = fe->dvb->priv; - struct dvb_usb_device *d = adap_to_d(adap); - mutex_lock(&adap->sync_mutex); - pr_debug("%s: adap=%d fe=%d\n", __func__, adap->id, fe->id); - - if (adap->fe_sleep[fe->id]) { - ret = adap->fe_sleep[fe->id](fe); - if (ret < 0) - goto err; - } - - if (d->props->frontend_ctrl) { - ret = d->props->frontend_ctrl(fe, 0); - if (ret < 0) - goto err; - } - - ret = dvb_usbv2_device_power_ctrl(d, 0); - if (ret < 0) - goto err; - - adap->active_fe = -1; - mutex_unlock(&adap->sync_mutex); - - return 0; -err: - mutex_unlock(&adap->sync_mutex); - pr_debug("%s: failed=%d\n", __func__, ret); - return ret; -} - -int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) -{ - int ret, i, count_registered = 0; - struct dvb_usb_device *d = adap_to_d(adap); - pr_debug("%s: adap=%d\n", __func__, adap->id); - - memset(adap->fe, 0, sizeof(adap->fe)); - adap->active_fe = -1; - - if (d->props->frontend_attach) { - ret = d->props->frontend_attach(adap); - if (ret < 0) { - pr_debug("%s: frontend_attach() failed=%d\n", __func__, - ret); - goto err_dvb_frontend_detach; - } - } else { - pr_debug("%s: frontend_attach() do not exists\n", __func__); - ret = 0; - goto err; - } - - for (i = 0; i < MAX_NO_OF_FE_PER_ADAP && adap->fe[i]; i++) { - adap->fe[i]->id = i; - - /* re-assign sleep and wakeup functions */ - adap->fe_init[i] = adap->fe[i]->ops.init; - adap->fe[i]->ops.init = dvb_usb_fe_wakeup; - adap->fe_sleep[i] = adap->fe[i]->ops.sleep; - adap->fe[i]->ops.sleep = dvb_usb_fe_sleep; - - ret = dvb_register_frontend(&adap->dvb_adap, adap->fe[i]); - if (ret < 0) { - pr_err("%s: frontend%d registration failed\n", - KBUILD_MODNAME, i); - goto err_dvb_unregister_frontend; - } - - count_registered++; - } - - if (d->props->tuner_attach) { - ret = d->props->tuner_attach(adap); - if (ret < 0) { - pr_debug("%s: tuner_attach() failed=%d\n", __func__, - ret); - goto err_dvb_unregister_frontend; - } - } - - return 0; - -err_dvb_unregister_frontend: - for (i = count_registered - 1; i >= 0; i--) - dvb_unregister_frontend(adap->fe[i]); - -err_dvb_frontend_detach: - for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { - if (adap->fe[i]) - dvb_frontend_detach(adap->fe[i]); - } - -err: - pr_debug("%s: failed=%d\n", __func__, ret); - return ret; -} - -int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap) -{ - int i; - pr_debug("%s: adap=%d\n", __func__, adap->id); - - for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { - if (adap->fe[i]) { - dvb_unregister_frontend(adap->fe[i]); - dvb_frontend_detach(adap->fe[i]); - } - } - - return 0; -} diff --git a/drivers/media/usb/dvb-usb/dvb_usb_remote.c b/drivers/media/usb/dvb-usb/dvb_usb_remote.c deleted file mode 100644 index f856ab6648c7..000000000000 --- a/drivers/media/usb/dvb-usb/dvb_usb_remote.c +++ /dev/null @@ -1,117 +0,0 @@ -/* dvb-usb-remote.c is part of the DVB USB library. - * - * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) - * see dvb-usb-init.c for copyright information. - * - * This file contains functions for initializing the input-device and for - * handling remote-control-queries. - */ -#include "dvb_usb_common.h" -#include - -/* Remote-control poll function - called every dib->rc_query_interval ms to see - * whether the remote control has received anything. - * - * TODO: Fix the repeat rate of the input device. - */ -static void dvb_usb_read_remote_control(struct work_struct *work) -{ - struct dvb_usb_device *d = container_of(work, - struct dvb_usb_device, rc_query_work.work); - int ret; - - /* TODO: need a lock here. We can simply skip checking for the remote - control if we're busy. */ - - /* when the parameter has been set to 1 via sysfs while the - * driver was running, or when bulk mode is enabled after IR init - */ - if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) - return; - - ret = d->rc.query(d); - if (ret < 0) - pr_err("%s: error %d while querying for an remote control " \ - "event\n", KBUILD_MODNAME, ret); - - schedule_delayed_work(&d->rc_query_work, - msecs_to_jiffies(d->rc.interval)); -} - -int dvb_usbv2_remote_init(struct dvb_usb_device *d) -{ - int ret; - struct rc_dev *dev; - - if (dvb_usbv2_disable_rc_polling || !d->props->get_rc_config) - return 0; - - ret = d->props->get_rc_config(d, &d->rc); - if (ret < 0) - goto err; - - dev = rc_allocate_device(); - if (!dev) { - ret = -ENOMEM; - goto err; - } - - dev->dev.parent = &d->udev->dev; - dev->input_name = "IR-receiver inside an USB DVB receiver"; - usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); - strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); - dev->input_phys = d->rc_phys; - usb_to_input_id(d->udev, &dev->input_id); - /* TODO: likely RC-core should took const char * */ - dev->driver_name = (char *) d->props->driver_name; - dev->driver_type = d->rc.driver_type; - dev->allowed_protos = d->rc.allowed_protos; - dev->change_protocol = d->rc.change_protocol; - dev->priv = d; - /* select used keymap */ - if (d->rc.map_name) - dev->map_name = d->rc.map_name; - else if (d->rc_map) - dev->map_name = d->rc_map; - else - dev->map_name = RC_MAP_EMPTY; /* keep rc enabled */ - - ret = rc_register_device(dev); - if (ret < 0) { - rc_free_device(dev); - goto err; - } - - d->input_dev = NULL; - d->rc_dev = dev; - - /* start polling if needed */ - if (d->rc.query && !d->rc.bulk_mode) { - /* initialize a work queue for handling polling */ - INIT_DELAYED_WORK(&d->rc_query_work, - dvb_usb_read_remote_control); - pr_info("%s: schedule remote query interval to %d msecs\n", - KBUILD_MODNAME, d->rc.interval); - schedule_delayed_work(&d->rc_query_work, - msecs_to_jiffies(d->rc.interval)); - } - - d->state |= DVB_USB_STATE_REMOTE; - - return 0; -err: - pr_debug("%s: failed=%d\n", __func__, ret); - return ret; -} - -int dvb_usbv2_remote_exit(struct dvb_usb_device *d) -{ - if (d->state & DVB_USB_STATE_REMOTE) { - cancel_delayed_work_sync(&d->rc_query_work); - rc_unregister_device(d->rc_dev); - } - - d->state &= ~DVB_USB_STATE_REMOTE; - - return 0; -} -- cgit v1.2.3 From 0ab61196e5b6d73ef5bb058a323d3ac7309cdc62 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sun, 15 Jul 2012 03:00:33 -0300 Subject: [media] pwc: Use vb2 queue mutex through a single name This lock was being taken using two different names (pointers) in the same function. Both names refer to the same lock, so this wasn't an error; but it looked very strange. Cc: Hans Verkuil Signed-off-by: Ezequiel Garcia Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index de7c7ba99ef4..b5d0729c28d3 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1127,7 +1127,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf) v4l2_device_disconnect(&pdev->v4l2_dev); video_unregister_device(&pdev->vdev); mutex_unlock(&pdev->v4l2_lock); - mutex_unlock(pdev->vb_queue.lock); + mutex_unlock(&pdev->vb_queue_lock); #ifdef CONFIG_USB_PWC_INPUT_EVDEV if (pdev->button_dev) -- cgit v1.2.3 From 4a7ec2db262628f8b13af76b09bb147943264529 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 23 Aug 2012 10:08:23 -0300 Subject: [media] pwc: Remove unneeded struct vb2_queue clearing struct vb2_queue is allocated through kzalloc as part of a larger struct, there's no need to clear it. Cc: Hans de Goede Signed-off-by: Ezequiel Garcia Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index b5d0729c28d3..42e36bac4d72 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -994,7 +994,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->power_save = my_power_save; /* Init videobuf2 queue structure */ - memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue)); pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; pdev->vb_queue.drv_priv = pdev; -- cgit v1.2.3 From 50c457a950ac06b4c461ed36fdb4a84c0d27a82b Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Sun, 5 Aug 2012 09:34:26 -0300 Subject: [media] gspca: dubious one-bit signed bitfield This patch changes some signed integers to unsigned because they are not intended for negative values and sparse is making noise about it. Sparse gives eight of these errors: drivers/media/usb/gspca/ov519.c:144:29: error: dubious one-bit signed bitfield Signed-off-by: Emil Goode Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov519.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index bfc7cefa59f8..c1a21bfb4be7 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -141,14 +141,14 @@ enum sensors { /* table of the disabled controls */ struct ctrl_valid { - int has_brightness:1; - int has_contrast:1; - int has_exposure:1; - int has_autogain:1; - int has_sat:1; - int has_hvflip:1; - int has_autobright:1; - int has_freq:1; + unsigned int has_brightness:1; + unsigned int has_contrast:1; + unsigned int has_exposure:1; + unsigned int has_autogain:1; + unsigned int has_sat:1; + unsigned int has_hvflip:1; + unsigned int has_autobright:1; + unsigned int has_freq:1; }; static const struct ctrl_valid valid_controls[] = { -- cgit v1.2.3 From 8c96f0a207bedb6f06089fde9adc7abe8136a087 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Thu, 6 Sep 2012 12:24:01 -0300 Subject: [media] drivers/media/usb/gspca/cpia1.c: fix error return code Convert a nonnegative error return code to a negative one, as returned elsewhere in the function. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // ( if@p1 (\(ret < 0\|ret != 0\)) { ... return ret; } | ret@p1 = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Peter Senna Tschudin Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/cpia1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index 2499a881d9a3..b3ba47d4d6a2 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -751,7 +751,7 @@ static int goto_high_power(struct gspca_dev *gspca_dev) if (signal_pending(current)) return -EINTR; - do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); if (ret) return ret; -- cgit v1.2.3 From 345321dc9c52b774f42c934339f9b3e2f0a39395 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 9 Sep 2012 06:30:02 -0300 Subject: [media] gspca: Don't set gspca_dev->dev to NULL before stop0 In commit a3d6e8cc0e6ddc8b3cfdeb3c979f07ed1aa528b3 gspca_dev->dev is set to NULL on disconnect, before calling stop0. The plan was to get rid of gspca_dev->present and instead simply check for gspca_dev->dev everywhere where we were checking for present. This should be race free since all users of gspca_dev->dev hold the usb_lock, or so I thought. But I was wrong, drivers which use a work-queue + synchronous bulk transfers to get the video data don't hold the usb_lock while doing so, their stop0 callbacks stop the workqueue, so they won't be using gspca_dev->dev anymore after the stop0 call, but they might be dereferincing it before, so we should not set gspca_dev->dev to NULL on disconnect before calling stop0. This also means that the workqueue functions in these drivers cannot use gspca_dev->dev to check if they need to stop because of disconnection, so we will need to keep gspca_dev->present around, and set that to 0 on disconnect, before calling stop0. Unfortunately as part of the plan to remove gspca_dev->present, these workqueues where already moved over to checking for gspca_dev->dev instead of gspca_dev->present as part of commit 254902b01d2acc6aced99ec17caa4c6cd890cdea, so this patch also reverts those parts of that commit. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/finepix.c | 6 +++--- drivers/media/usb/gspca/gspca.c | 3 +-- drivers/media/usb/gspca/jl2005bcd.c | 6 +++--- drivers/media/usb/gspca/sq905.c | 8 ++++---- drivers/media/usb/gspca/sq905c.c | 6 +++--- drivers/media/usb/gspca/vicam.c | 4 ++-- drivers/media/usb/gspca/xirlink_cit.c | 4 +--- drivers/media/usb/gspca/zc3xx.c | 4 ++-- 8 files changed, 19 insertions(+), 22 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c index c8f2201cc35a..04807eee7772 100644 --- a/drivers/media/usb/gspca/finepix.c +++ b/drivers/media/usb/gspca/finepix.c @@ -94,7 +94,7 @@ static void dostream(struct work_struct *work) /* loop reading a frame */ again: - while (gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->present && gspca_dev->streaming) { #ifdef CONFIG_PM if (gspca_dev->frozen) break; @@ -110,7 +110,7 @@ again: if (gspca_dev->frozen) break; #endif - if (!gspca_dev->dev || !gspca_dev->streaming) + if (!gspca_dev->present || !gspca_dev->streaming) break; /* the frame comes in parts */ @@ -129,7 +129,7 @@ again: if (gspca_dev->frozen) goto out; #endif - if (!gspca_dev->dev || !gspca_dev->streaming) + if (!gspca_dev->present || !gspca_dev->streaming) goto out; if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index d4e8343f5b10..7cce0f201d70 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -2358,8 +2358,6 @@ void gspca_disconnect(struct usb_interface *intf) mutex_lock(&gspca_dev->usb_lock); - usb_set_intfdata(intf, NULL); - gspca_dev->dev = NULL; gspca_dev->present = 0; destroy_urbs(gspca_dev); @@ -2375,6 +2373,7 @@ void gspca_disconnect(struct usb_interface *intf) if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) gspca_dev->sd_desc->stop0(gspca_dev); gspca_dev->streaming = 0; + gspca_dev->dev = NULL; wake_up_interruptible(&gspca_dev->wq); v4l2_device_disconnect(&gspca_dev->v4l2_dev); diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 234777116e5f..c4b4a9598db4 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -335,7 +335,7 @@ static void jl2005c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->present && gspca_dev->streaming) { #ifdef CONFIG_PM if (gspca_dev->frozen) break; @@ -371,7 +371,7 @@ static void jl2005c_dostream(struct work_struct *work) buffer, act_len); header_read = 1; } - while (bytes_left > 0 && gspca_dev->dev) { + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > JL2005C_MAX_TRANSFER ? JL2005C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -394,7 +394,7 @@ static void jl2005c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->dev) { + if (gspca_dev->present) { mutex_lock(&gspca_dev->usb_lock); jl2005c_stop(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c index a8ac97931ad6..2e05acab304c 100644 --- a/drivers/media/usb/gspca/sq905.c +++ b/drivers/media/usb/gspca/sq905.c @@ -232,7 +232,7 @@ static void sq905_dostream(struct work_struct *work) frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + FRAME_HEADER_LEN; - while (gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->present && gspca_dev->streaming) { #ifdef CONFIG_PM if (gspca_dev->frozen) break; @@ -246,7 +246,7 @@ static void sq905_dostream(struct work_struct *work) we must finish reading an entire frame, otherwise the next time we stream we start reading in the middle of a frame. */ - while (bytes_left > 0 && gspca_dev->dev) { + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905_MAX_TRANSFER ? SQ905_MAX_TRANSFER : bytes_left; ret = sq905_read_data(gspca_dev, buffer, data_len, 1); @@ -278,7 +278,7 @@ static void sq905_dostream(struct work_struct *work) gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); } - if (gspca_dev->dev) { + if (gspca_dev->present) { /* acknowledge the frame */ mutex_lock(&gspca_dev->usb_lock); ret = sq905_ack_frame(gspca_dev); @@ -288,7 +288,7 @@ static void sq905_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->dev) { + if (gspca_dev->present) { mutex_lock(&gspca_dev->usb_lock); sq905_command(gspca_dev, SQ905_CLEAR); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c index 70fae6982e96..784620c102b1 100644 --- a/drivers/media/usb/gspca/sq905c.c +++ b/drivers/media/usb/gspca/sq905c.c @@ -150,7 +150,7 @@ static void sq905c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->present && gspca_dev->streaming) { #ifdef CONFIG_PM if (gspca_dev->frozen) break; @@ -173,7 +173,7 @@ static void sq905c_dostream(struct work_struct *work) packet_type = FIRST_PACKET; gspca_frame_add(gspca_dev, packet_type, buffer, FRAME_HEADER_LEN); - while (bytes_left > 0 && gspca_dev->dev) { + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905C_MAX_TRANSFER ? SQ905C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -195,7 +195,7 @@ static void sq905c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->dev) { + if (gspca_dev->present) { mutex_lock(&gspca_dev->usb_lock); sq905c_command(gspca_dev, SQ905C_CLEAR, 0); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c index b1a64b912666..57d88f70c399 100644 --- a/drivers/media/usb/gspca/vicam.c +++ b/drivers/media/usb/gspca/vicam.c @@ -194,7 +194,7 @@ static void vicam_dostream(struct work_struct *work) goto exit; } - while (gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->present && gspca_dev->streaming) { #ifdef CONFIG_PM if (gspca_dev->frozen) break; @@ -299,7 +299,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) dev->work_thread = NULL; mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->dev) + if (gspca_dev->present) vicam_set_camera_power(gspca_dev, 0); } diff --git a/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c index 13b8d395d210..d4b23c9bf90c 100644 --- a/drivers/media/usb/gspca/xirlink_cit.c +++ b/drivers/media/usb/gspca/xirlink_cit.c @@ -2697,9 +2697,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - /* We cannot use gspca_dev->present here as that is not set when - sd_init gets called and we get called from sd_init */ - if (!gspca_dev->dev) + if (!gspca_dev->present) return; switch (sd->model) { diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index f0bacee33ef9..234d9eaa8eea 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -5950,7 +5950,7 @@ static void transfer_update(struct work_struct *work) if (gspca_dev->frozen) goto err; #endif - if (!gspca_dev->dev || !gspca_dev->streaming) + if (!gspca_dev->present || !gspca_dev->streaming) goto err; /* Bit 0 of register 11 indicates FIFO overflow */ @@ -6842,7 +6842,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) mutex_lock(&gspca_dev->usb_lock); sd->work_thread = NULL; } - if (!gspca_dev->dev) + if (!gspca_dev->present) return; send_unknown(gspca_dev, sd->sensor); } -- cgit v1.2.3 From ff8f25d326da5e7cf6216f368116744341fceb12 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 9 Sep 2012 07:00:04 -0300 Subject: [media] gspca_finepix: Remove unnecessary lock/unlock call gspca_main: init_transfer does not do anything between calling sd_start (which starts the workqueue) and releasing the usb_lock, so this synchronization is a nop, remove it. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/finepix.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c index 04807eee7772..fb68a2934255 100644 --- a/drivers/media/usb/gspca/finepix.c +++ b/drivers/media/usb/gspca/finepix.c @@ -87,9 +87,6 @@ static void dostream(struct work_struct *work) int ret = 0; int len; - /* synchronize with the main driver */ - mutex_lock(&gspca_dev->usb_lock); - mutex_unlock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "dostream started"); /* loop reading a frame */ -- cgit v1.2.3 From 844db450e6e2cf710752af1a019a877af390b541 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 9 Sep 2012 07:30:02 -0300 Subject: [media] gspca: Update / fix various comments wrt workqueue usb_lock usage Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/finepix.c | 9 ++++++++- drivers/media/usb/gspca/jl2005bcd.c | 12 +++++------- drivers/media/usb/gspca/sn9c20x.c | 2 ++ drivers/media/usb/gspca/sonixj.c | 2 ++ drivers/media/usb/gspca/sq905.c | 11 +++++------ drivers/media/usb/gspca/sq905c.c | 12 +++++------- drivers/media/usb/gspca/vicam.c | 13 ++++++------- drivers/media/usb/gspca/zc3xx.c | 4 +++- 8 files changed, 36 insertions(+), 29 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c index fb68a2934255..52bdb569760b 100644 --- a/drivers/media/usb/gspca/finepix.c +++ b/drivers/media/usb/gspca/finepix.c @@ -77,7 +77,14 @@ static int command(struct gspca_dev *gspca_dev, 12, FPIX_TIMEOUT); } -/* workqueue */ +/* + * This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use gspca_dev->usb_buf we take the usb_lock when + * performing USB operations using it. In practice we don't really need this + * as the camera doesn't provide any controls. + */ static void dostream(struct work_struct *work) { struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct); diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index c4b4a9598db4..62ba80d9b998 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -306,15 +306,13 @@ static int jl2005c_stop(struct gspca_dev *gspca_dev) return retval; } -/* This function is called as a workqueue function and runs whenever the camera +/* + * This function is called as a workqueue function and runs whenever the camera * is streaming data. Because it is a workqueue function it is allowed to sleep * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface the gspca usb_lock is - * used when performing the one USB control operation inside the workqueue, - * which tells the camera to close the stream. In practice the only thing - * which needs to be protected against is the usb_set_interface call that - * gspca makes during stream_off. Otherwise the camera doesn't provide any - * controls that the user could try to change. + * threads attempting to use gspca_dev->usb_buf we take the usb_lock when + * performing USB operations using it. In practice we don't really need this + * as the camera doesn't provide any controls. */ static void jl2005c_dostream(struct work_struct *work) { diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c index b9c6f17eabb2..41f769fe340c 100644 --- a/drivers/media/usb/gspca/sn9c20x.c +++ b/drivers/media/usb/gspca/sn9c20x.c @@ -2197,8 +2197,10 @@ static void qual_upd(struct work_struct *work) struct gspca_dev *gspca_dev = &sd->gspca_dev; s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual); + /* To protect gspca_dev->usb_buf and gspca_dev->usb_err */ mutex_lock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "qual_upd %d%%", qual); + gspca_dev->usb_err = 0; set_quality(gspca_dev, qual); mutex_unlock(&gspca_dev->usb_lock); } diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c index 150b2df40f7f..5a86047b846f 100644 --- a/drivers/media/usb/gspca/sonixj.c +++ b/drivers/media/usb/gspca/sonixj.c @@ -2380,8 +2380,10 @@ static void qual_upd(struct work_struct *work) struct sd *sd = container_of(work, struct sd, work); struct gspca_dev *gspca_dev = &sd->gspca_dev; + /* To protect gspca_dev->usb_buf and gspca_dev->usb_err */ mutex_lock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality); + gspca_dev->usb_err = 0; setjpegqual(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c index 2e05acab304c..1d99f10a3e19 100644 --- a/drivers/media/usb/gspca/sq905.c +++ b/drivers/media/usb/gspca/sq905.c @@ -201,14 +201,13 @@ sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) return 0; } -/* This function is called as a workqueue function and runs whenever the camera +/* + * This function is called as a workqueue function and runs whenever the camera * is streaming data. Because it is a workqueue function it is allowed to sleep * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface we take the gspca - * usb_lock when performing USB operations. In practice the only thing we need - * to protect against is the usb_set_interface call that gspca makes during - * stream_off as the camera doesn't provide any controls that the user could try - * to change. + * threads attempting to use gspca_dev->usb_buf we take the usb_lock when + * performing USB operations using it. In practice we don't really need this + * as the camera doesn't provide any controls. */ static void sq905_dostream(struct work_struct *work) { diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c index 784620c102b1..410cdcbb55d4 100644 --- a/drivers/media/usb/gspca/sq905c.c +++ b/drivers/media/usb/gspca/sq905c.c @@ -123,15 +123,13 @@ static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index, return 0; } -/* This function is called as a workqueue function and runs whenever the camera +/* + * This function is called as a workqueue function and runs whenever the camera * is streaming data. Because it is a workqueue function it is allowed to sleep * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface the gspca usb_lock is - * used when performing the one USB control operation inside the workqueue, - * which tells the camera to close the stream. In practice the only thing - * which needs to be protected against is the usb_set_interface call that - * gspca makes during stream_off. Otherwise the camera doesn't provide any - * controls that the user could try to change. + * threads attempting to use gspca_dev->usb_buf we take the usb_lock when + * performing USB operations using it. In practice we don't really need this + * as the camera doesn't provide any controls. */ static void sq905c_dostream(struct work_struct *work) { diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c index 57d88f70c399..d6890bc37198 100644 --- a/drivers/media/usb/gspca/vicam.c +++ b/drivers/media/usb/gspca/vicam.c @@ -110,7 +110,7 @@ static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) } /* - * request and read a block of data - see warning on vicam_command. + * request and read a block of data */ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) { @@ -170,14 +170,13 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) return 0; } -/* This function is called as a workqueue function and runs whenever the camera +/* + * This function is called as a workqueue function and runs whenever the camera * is streaming data. Because it is a workqueue function it is allowed to sleep * so we can use synchronous USB calls. To avoid possible collisions with other - * threads attempting to use the camera's USB interface we take the gspca - * usb_lock when performing USB operations. In practice the only thing we need - * to protect against is the usb_set_interface call that gspca makes during - * stream_off as the camera doesn't provide any controls that the user could try - * to change. + * threads attempting to use gspca_dev->usb_buf we take the usb_lock when + * performing USB operations using it. In practice we don't really need this + * as the cameras controls are only written from the workqueue. */ static void vicam_dostream(struct work_struct *work) { diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index 234d9eaa8eea..c47ba14c7619 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -5945,6 +5945,7 @@ static void transfer_update(struct work_struct *work) for (;;) { msleep(100); + /* To protect gspca_dev->usb_buf and gspca_dev->usb_err */ mutex_lock(&gspca_dev->usb_lock); #ifdef CONFIG_PM if (gspca_dev->frozen) @@ -6831,7 +6832,8 @@ static int sd_start(struct gspca_dev *gspca_dev) return 0; } -/* called on streamoff with alt 0 and on disconnect */ +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; -- cgit v1.2.3 From 36adfca94a354b10b4e36684a45e830f6817b067 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 9 Sep 2012 08:04:05 -0300 Subject: [media] gspca: Fix input urb creation / destruction surrounding suspend resume 1) We always re-create the input-urb on resume, so we must also always destroy it on suspend to avoid leaking it 2) If we're going to do an init_transfer, then that will destroy the urb before starting the stream (nop if there is none), and (re-)create it once the stream is started. So there is little use in creating it, if we're going to do an init_transfer immediately afterward Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/gspca.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 7cce0f201d70..2abbf52c781a 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -2391,19 +2391,22 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + gspca_input_destroy_urb(gspca_dev); + if (!gspca_dev->streaming) return 0; + mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 1; /* avoid urb error messages */ gspca_dev->usb_err = 0; if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); gspca_set_alt0(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); + return 0; } EXPORT_SYMBOL(gspca_suspend); @@ -2417,7 +2420,6 @@ int gspca_resume(struct usb_interface *intf) gspca_dev->frozen = 0; gspca_dev->usb_err = 0; gspca_dev->sd_desc->init(gspca_dev); - gspca_input_create_urb(gspca_dev); /* * Most subdrivers send all ctrl values on sd_start and thus * only write to the device registers on s_ctrl when streaming -> @@ -2427,7 +2429,10 @@ int gspca_resume(struct usb_interface *intf) gspca_dev->streaming = 0; if (streaming) ret = gspca_init_transfer(gspca_dev); + else + gspca_input_create_urb(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); + return ret; } EXPORT_SYMBOL(gspca_resume); -- cgit v1.2.3 From 97d2fbf501e3cf105ac957086c7e40e62e15cdf8 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Sun, 9 Sep 2012 15:02:19 -0300 Subject: [media] gspca_pac7302: add support for device 1ae7:2001 Speedlink Snappy Microphone SL-6825-SBK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Cc: stable@kernel.org Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac7302.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index 4877f7ab3d59..e906f564ca85 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -905,6 +905,7 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x262a)}, {USB_DEVICE(0x093a, 0x262c)}, {USB_DEVICE(0x145f, 0x013c)}, + {USB_DEVICE(0x1ae7, 0x2001)}, /* SpeedLink Snappy Mic SL-6825-SBK */ {} }; MODULE_DEVICE_TABLE(usb, device_table); -- cgit v1.2.3 From db43b9ca2f101d0945d043fa7d5ecd8f2da17fef Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Sun, 9 Sep 2012 15:02:20 -0300 Subject: [media] gspca_pac7302: make red balance and blue balance controls work again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a regression from kernel 3.4 which has been introduced with the conversion of the gspca driver to the v4l2 control framework. Signed-off-by: Frank Schäfer Cc: stable@kernel.org Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac7302.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index e906f564ca85..eb3c90e4a658 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -616,7 +616,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_RED_BALANCE, 0, 3, 1, 1); sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 3, 1, 1); + V4L2_CID_BLUE_BALANCE, 0, 3, 1, 1); gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); -- cgit v1.2.3 From b1a19c0165573b169eceb690df7a72bf1ffa47dc Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Sun, 9 Sep 2012 15:02:21 -0300 Subject: [media] gspca_pac7302: add sharpness control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows driver uses page 0 register 0xb6 for sharpness adjustment. Signed-off-by: Frank Schäfer Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac7302.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index eb3c90e4a658..8c2961326a65 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -26,6 +26,11 @@ /* * Some documentation about various registers as determined by trial and error. * + * Register page 0: + * + * Address Description + * 0xb6 Sharpness control (bits 0-4) + * * Register page 1: * * Address Description @@ -66,6 +71,7 @@ * -----+------------+--------------------------------------------------- * 0 | 0x0f..0x20 | setcolors() * 0 | 0xa2..0xab | setbrightcont() + * 0 | 0xb6 | setsharpness() * 0 | 0xc5 | setredbalance() * 0 | 0xc6 | setwhitebalance() * 0 | 0xc7 | setbluebalance() @@ -109,6 +115,7 @@ struct sd { struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; }; + struct v4l2_ctrl *sharpness; u8 flags; #define FL_HFLIP 0x01 /* mirrored by default */ #define FL_VFLIP 0x02 /* vertical flipped by default */ @@ -531,6 +538,16 @@ static void sethvflip(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x11, 0x01); } +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xb6, sd->sharpness->val); + + reg_w(gspca_dev, 0xdc, 0x01); +} + /* this function is called at probe and resume time for pac7302 */ static int sd_init(struct gspca_dev *gspca_dev) { @@ -584,6 +601,9 @@ static int sd_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_HFLIP: sethvflip(gspca_dev); break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev); + break; default: return -EINVAL; } @@ -601,7 +621,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 11); + v4l2_ctrl_handler_init(hdl, 12); sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 32, 1, 16); @@ -632,6 +652,9 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 15, 1, 8); + if (hdl->error) { pr_err("Could not initialize controls\n"); return hdl->error; -- cgit v1.2.3 From f58e5cdf12a11a0585a0e779f931290f45d48c66 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Sun, 9 Sep 2012 15:02:22 -0300 Subject: [media] gspca_pac7302: increase default value for white balance temperature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current white balance temperature default value is 4, which is much too small (possible values are 0-255). Improve the picture quality by increasing the default value to 55, which is the default value used by the Windows driver. Signed-off-by: Frank Schäfer Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac7302.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index 8c2961326a65..bed34df4ddf0 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -632,7 +632,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) V4L2_CID_SATURATION, 0, 255, 1, 127); sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, - 0, 255, 1, 4); + 0, 255, 1, 55); sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_RED_BALANCE, 0, 3, 1, 1); sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, -- cgit v1.2.3 From 0e68785db5fde372126b6d8c3ea792665249e35b Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Sun, 9 Sep 2012 15:02:23 -0300 Subject: [media] gspca_pac7302: avoid duplicate calls of the image quality adjustment functions on capturing start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to call the image quality adjustment functions in sd_start. The gspca main driver calls v4l2_ctrl_handler_setup in gspca_init_transfer, which already applies all image control values. Signed-off-by: Frank Schäfer Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac7302.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index bed34df4ddf0..71fa5a45c2e1 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -673,14 +673,6 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w_var(gspca_dev, start_7302, page3_7302, sizeof(page3_7302)); - setbrightcont(gspca_dev); - setcolors(gspca_dev); - setwhitebalance(gspca_dev); - setredbalance(gspca_dev); - setbluebalance(gspca_dev); - setexposure(gspca_dev); - setgain(gspca_dev); - sethvflip(gspca_dev); sd->sof_read = 0; sd->autogain_ignore_frames = 0; -- cgit v1.2.3 From 780d61704cf62a51c06c0ca8210d0282591e00b2 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Sun, 9 Sep 2012 15:02:24 -0300 Subject: [media] gspca_pac7302: extend register documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac7302.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index 71fa5a45c2e1..2d5c6d8343a0 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -29,6 +29,15 @@ * Register page 0: * * Address Description + * 0x02 Red balance control + * 0x03 Green balance control + * 0x04 Blue balance control + * Valus are inverted (0=max, 255=min). + * The Windows driver uses a quadratic approach to map + * the settable values (0-200) on register values: + * min=0x80, default=0x40, max=0x20 + * 0x0f-0x20 Colors, saturation and exposure control + * 0xa2-0xab Brightness, contrast and gamma control * 0xb6 Sharpness control (bits 0-4) * * Register page 1: -- cgit v1.2.3 From 3851c34a835f47b78c6ef7a3fa7dd6d9af9393f9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 21 Aug 2012 05:05:35 -0300 Subject: [media] uvcvideo: Remove outdated comment The uvcvideo driver now supports USERPTR, and isn't limited to YUYV and MJPEG anymore. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 45d7aa162d9d..287f73182a69 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -11,18 +11,6 @@ * */ -/* - * This driver aims to support video input and ouput devices compliant with the - * 'USB Video Class' specification. - * - * The driver doesn't support the deprecated v4l1 interface. It implements the - * mmap capture method only, and doesn't do any image format conversion in - * software. If your user-space application doesn't support YUYV or MJPEG, fix - * it :-). Please note that the MJPEG data have been stripped from their - * Huffman tables (DHT marker), you will need to add it back if your JPEG - * codec can't handle MJPEG data. - */ - #include #include #include -- cgit v1.2.3 From 0b8623caddde9a30c129eb8b721f8d25eebf4711 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 21 Aug 2012 09:08:51 -0300 Subject: [media] gl861: reset_resume support It survives now on reset_resume. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/gl861.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index cf29f43e3598..df78811f5234 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -163,6 +163,7 @@ static struct usb_driver gl861_usb_driver = { .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, .no_dynamic_id = 1, .soft_unbind = 1, }; -- cgit v1.2.3 From 62751a801dc7dc176725c4d4254ee8638b15bb66 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 21 Aug 2012 19:56:18 -0300 Subject: [media] rtl28xxu: stream did not start after stop on USB3.0 Stream did not start anymore after stream was stopped once. Following error can be seen, xhci_hcd WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state. usb_clear_halt for streaming endpoint helps. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index d2b1505b36f9..1ccb99b0a204 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -834,6 +834,7 @@ static int rtl28xxu_streaming_ctrl(struct dvb_frontend *fe , int onoff) if (onoff) { buf[0] = 0x00; buf[1] = 0x00; + usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x81)); } else { buf[0] = 0x10; /* stall EPA */ buf[1] = 0x02; /* reset EPA */ -- cgit v1.2.3 From e1f43269710daf3ab0d8d0da428f7f647c11676d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 21 Aug 2012 19:56:19 -0300 Subject: [media] rtl28xxu: fix rtl2832u module reload fails bug This is workaround / partial fix. rtl2832u_power_ctrl() and rtl2832u_frontend_attach() needs to be go through carefully and fix properly. There is clearly some logical errors when handling power-management ang GPIOs... Signed-off-by: Antti Palosaari Cc: Thomas Mair Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 1ccb99b0a204..c246c50be629 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -942,17 +942,6 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) /* bit 7 to 1 */ val |= 0x80; - ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); - if (ret) - goto err; - - /* demod HW reset */ - ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); - if (ret) - goto err; - /* bit 5 to 0 */ - val &= 0xdf; - ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); if (ret) goto err; -- cgit v1.2.3 From f5a8a78a1b7d52da7d2ff6b89f0d1e8d0cfd651f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 22 Aug 2012 18:52:05 -0300 Subject: [media] au6610: define reset_resume After qt1010 change that device seems to survive from reset resume. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/au6610.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/au6610.c b/drivers/media/usb/dvb-usb-v2/au6610.c index 05f2a8628142..c126b7005c19 100644 --- a/drivers/media/usb/dvb-usb-v2/au6610.c +++ b/drivers/media/usb/dvb-usb-v2/au6610.c @@ -194,6 +194,7 @@ static struct usb_driver au6610_driver = { .disconnect = dvb_usbv2_disconnect, .suspend = dvb_usbv2_suspend, .resume = dvb_usbv2_resume, + .reset_resume = dvb_usbv2_reset_resume, .no_dynamic_id = 1, .soft_unbind = 1, }; -- cgit v1.2.3 From 4ab79283aa749201aaee9e93a67285719bd2fec2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 22 Aug 2012 19:41:59 -0300 Subject: [media] dvb_usb_v2: add debug macro dvb_usb_dbg_usb_control_msg For dumping usb_control_msg(). Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 5a53c6231fc2..bae16a1189d6 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -63,6 +63,17 @@ #define fe_to_priv(fe) (fe_to_d(fe)->priv) #define d_to_priv(d) (d->priv) +#define dvb_usb_dbg_usb_control_msg(udev, r, t, v, i, b, l) { \ + char *direction; \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + direction = ">>>"; \ + else \ + direction = "<<<"; \ + dev_dbg(&udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ + "%s %*ph\n", __func__, t, r, v & 0xff, v >> 8, \ + i & 0xff, i >> 8, l & 0xff, l >> 8, direction, l, b); \ +} + #define DVB_USB_STREAM_BULK(endpoint_, count_, size_) { \ .type = USB_BULK, \ .count = count_, \ -- cgit v1.2.3 From d89b9369e03c7b56f23b02e00a10aca933c7bf67 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 22 Aug 2012 19:42:00 -0300 Subject: [media] dvb_usb_v2: use dvb_usb_dbg_usb_control_msg() Convert drivers: au6610, ce6230, ec168, rtl28xxu for dvb_usb_dbg_usb_control_msg() macro. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/au6610.c | 5 +++++ drivers/media/usb/dvb-usb-v2/ce6230.c | 4 ++-- drivers/media/usb/dvb-usb-v2/ce6230.h | 11 ----------- drivers/media/usb/dvb-usb-v2/ec168.c | 4 ++-- drivers/media/usb/dvb-usb-v2/ec168.h | 11 ----------- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 6 ++++-- drivers/media/usb/dvb-usb-v2/rtl28xxu.h | 11 ----------- 7 files changed, 13 insertions(+), 39 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/au6610.c b/drivers/media/usb/dvb-usb-v2/au6610.c index c126b7005c19..f309fd8217fd 100644 --- a/drivers/media/usb/dvb-usb-v2/au6610.c +++ b/drivers/media/usb/dvb-usb-v2/au6610.c @@ -56,6 +56,11 @@ static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf, 6, AU6610_USB_TIMEOUT); + + dvb_usb_dbg_usb_control_msg(d->udev, operation, + (USB_TYPE_VENDOR|USB_DIR_IN), addr << 1, index, + usb_buf, 6); + if (ret < 0) goto error; diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.c b/drivers/media/usb/dvb-usb-v2/ce6230.c index 819db9c01d5d..1c4357d804ff 100644 --- a/drivers/media/usb/dvb-usb-v2/ce6230.c +++ b/drivers/media/usb/dvb-usb-v2/ce6230.c @@ -74,8 +74,8 @@ static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) ret = usb_control_msg(d->udev, pipe, request, requesttype, value, index, buf, req->data_len, CE6230_USB_TIMEOUT); - ce6230_debug_dump(request, requesttype, value, index, buf, - req->data_len); + dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, value, index, + buf, req->data_len); if (ret < 0) pr_err("%s: usb_control_msg() failed=%d\n", KBUILD_MODNAME, diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.h b/drivers/media/usb/dvb-usb-v2/ce6230.h index 42d754494a3a..299e57e3390b 100644 --- a/drivers/media/usb/dvb-usb-v2/ce6230.h +++ b/drivers/media/usb/dvb-usb-v2/ce6230.h @@ -26,17 +26,6 @@ #include "zl10353.h" #include "mxl5005s.h" -#define ce6230_debug_dump(r, t, v, i, b, l) { \ - char *direction; \ - if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ - direction = ">>>"; \ - else \ - direction = "<<<"; \ - pr_debug("%s: %02x %02x %02x %02x %02x %02x %02x %02x %s [%d bytes]\n", \ - __func__, t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, \ - l & 0xff, l >> 8, direction, l); \ -} - #define CE6230_USB_TIMEOUT 1000 struct usb_req { diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c index ab77622c383d..b74c810e7dec 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.c +++ b/drivers/media/usb/dvb-usb-v2/ec168.c @@ -86,8 +86,8 @@ static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req) ret = usb_control_msg(d->udev, pipe, request, requesttype, req->value, req->index, buf, req->size, EC168_USB_TIMEOUT); - ec168_debug_dump(request, requesttype, req->value, req->index, buf, - req->size); + dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, req->value, + req->index, buf, req->size); if (ret < 0) goto err_dealloc; diff --git a/drivers/media/usb/dvb-usb-v2/ec168.h b/drivers/media/usb/dvb-usb-v2/ec168.h index 9181236f6ebc..f65180822acb 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.h +++ b/drivers/media/usb/dvb-usb-v2/ec168.h @@ -24,17 +24,6 @@ #include "dvb_usb.h" -#define ec168_debug_dump(r, t, v, i, b, l) { \ - char *direction; \ - if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ - direction = ">>>"; \ - else \ - direction = "<<<"; \ - pr_debug("%s: %02x %02x %02x %02x %02x %02x %02x %02x %s\n", \ - __func__, t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, \ - l & 0xff, l >> 8, direction); \ -} - #define EC168_USB_TIMEOUT 1000 struct ec168_req { diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c246c50be629..e29fca2ba125 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -59,11 +59,13 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value, req->index, buf, req->size, 1000); + + dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value, + req->index, buf, req->size); + if (ret > 0) ret = 0; - deb_dump(0, requesttype, req->value, req->index, buf, req->size); - /* read request, copy returned data to return buf */ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) memcpy(req->data, buf, req->size); diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index 575edbf13a92..035a9c890ce5 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -24,17 +24,6 @@ #include "dvb_usb.h" -#define deb_dump(r, t, v, i, b, l) { \ - char *direction; \ - if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ - direction = ">>>"; \ - else \ - direction = "<<<"; \ - dev_dbg(&d->udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ - "%s [%d bytes]\n", __func__, t, r, v & 0xff, v >> 8, \ - i & 0xff, i >> 8, l & 0xff, l >> 8, direction, l); \ -} - /* * USB commands * (usb_control_msg() index parameter) -- cgit v1.2.3 From c11f187bd00704cb135b33b4ee6c6a6b85d24b0c Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Thu, 23 Aug 2012 09:08:22 -0300 Subject: [media] stk1160: Remove unneeded struct vb2_queue clearing struct vb2_queue is allocated through kzalloc as part of a larger struct, there's no need to clear it. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-v4l.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 1ad4ac187727..c414bc3570c5 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -676,7 +676,6 @@ int stk1160_vb2_setup(struct stk1160 *dev) struct vb2_queue *q; q = &dev->vb_vidq; - memset(q, 0, sizeof(dev->vb_vidq)); q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; q->drv_priv = dev; -- cgit v1.2.3 From b0c47e1ec2a5d66df4ef5d34055a919140aa386a Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Thu, 23 Aug 2012 16:32:57 -0300 Subject: [media] stk1160: Remove unused 'ifnum' variable Since ifnum is not used anywhere it is safe to remove it. This was spotted by Hans's media_tree daily build. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c index 74236fd3b7ec..b62740846061 100644 --- a/drivers/media/usb/stk1160/stk1160-core.c +++ b/drivers/media/usb/stk1160/stk1160-core.c @@ -256,14 +256,12 @@ static int stk1160_scan_usb(struct usb_interface *intf, struct usb_device *udev, static int stk1160_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int ifnum; int rc = 0; unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ struct usb_device *udev; struct stk1160 *dev; - ifnum = interface->altsetting[0].desc.bInterfaceNumber; udev = interface_to_usbdev(interface); /* -- cgit v1.2.3 From 8b03663644002a6c742dd38dbdc8300da85293da Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 1 Sep 2012 09:54:43 -0300 Subject: [media] rtl28xxu: correct usb_clear_halt() usage It is not allowed to call usb_clear_halt() after urbs are submitted. That causes oops sometimes. Move whole streaming_ctrl() logic to power_ctrl() in order to avoid wrong usb_clear_halt() use. Also, configuring streaming endpoint in streaming_ctrl() sounds like a little bit wrong as it is aimed for control stream gate. Reported-by: Hin-Tak Leung Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 55 +++++++++++++++------------------ 1 file changed, 25 insertions(+), 30 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index e29fca2ba125..7d11c5dede75 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -825,37 +825,10 @@ err: return ret; } -static int rtl28xxu_streaming_ctrl(struct dvb_frontend *fe , int onoff) -{ - int ret; - u8 buf[2]; - struct dvb_usb_device *d = fe_to_d(fe); - - dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); - - if (onoff) { - buf[0] = 0x00; - buf[1] = 0x00; - usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x81)); - } else { - buf[0] = 0x10; /* stall EPA */ - buf[1] = 0x02; /* reset EPA */ - } - - ret = rtl28xx_wr_regs(d, USB_EPA_CTL, buf, 2); - if (ret) - goto err; - - return ret; -err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} - static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; - u8 gpio, sys0; + u8 gpio, sys0, epa_ctl[2]; dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); @@ -878,11 +851,15 @@ static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) gpio |= 0x04; /* GPIO2 = 1, LED on */ sys0 = sys0 & 0x0f; sys0 |= 0xe0; + epa_ctl[0] = 0x00; /* clear stall */ + epa_ctl[1] = 0x00; /* clear reset */ } else { gpio &= (~0x01); /* GPIO0 = 0 */ gpio |= 0x10; /* GPIO4 = 1 */ gpio &= (~0x04); /* GPIO2 = 1, LED off */ sys0 = sys0 & (~0xc0); + epa_ctl[0] = 0x10; /* set stall */ + epa_ctl[1] = 0x02; /* set reset */ } dev_dbg(&d->udev->dev, "%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, @@ -898,6 +875,14 @@ static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) if (ret) goto err; + /* streaming EP: stall & reset */ + ret = rtl28xx_wr_regs(d, USB_EPA_CTL, epa_ctl, 2); + if (ret) + goto err; + + if (onoff) + usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x81)); + return ret; err: dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); @@ -972,6 +957,14 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) goto err; + /* streaming EP: clear stall & reset */ + ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2); + if (ret) + goto err; + + ret = usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x81)); + if (ret) + goto err; } else { /* demod_ctl_1 */ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val); @@ -1006,6 +999,10 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) if (ret) goto err; + /* streaming EP: set stall & reset */ + ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x10\x02", 2); + if (ret) + goto err; } return ret; @@ -1182,7 +1179,6 @@ static const struct dvb_usb_device_properties rtl2831u_props = { .tuner_attach = rtl2831u_tuner_attach, .init = rtl28xxu_init, .get_rc_config = rtl2831u_get_rc_config, - .streaming_ctrl = rtl28xxu_streaming_ctrl, .num_adapters = 1, .adapter = { @@ -1204,7 +1200,6 @@ static const struct dvb_usb_device_properties rtl2832u_props = { .tuner_attach = rtl2832u_tuner_attach, .init = rtl28xxu_init, .get_rc_config = rtl2832u_get_rc_config, - .streaming_ctrl = rtl28xxu_streaming_ctrl, .num_adapters = 1, .adapter = { -- cgit v1.2.3 From 542f6a52b63359227bbc39e8436a1d7156602d86 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 1 Sep 2012 21:09:22 -0300 Subject: [media] rtl28xxu: add support for Elonics E4000 tuner Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/Kconfig | 1 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 967115104047..329d2221e915 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -141,6 +141,7 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 7d11c5dede75..88b5ea12ed28 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -30,6 +30,7 @@ #include "mxl5005s.h" #include "fc0012.h" #include "fc0013.h" +#include "e4000.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -625,10 +626,11 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) ret = rtl28xxu_ctrl_msg(d, &req_e4000); if (ret == 0 && buf[0] == 0x40) { priv->tuner = TUNER_RTL2832_E4000; - /* TODO implement tuner */ + /* FIXME: do not abuse fc0012 settings */ + rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; dev_info(&d->udev->dev, "%s: E4000 tuner found", KBUILD_MODNAME); - goto unsupported; + goto found; } /* check TDA18272 ID register; reg=00 val=c760 */ @@ -746,6 +748,11 @@ err: return ret; } +static const struct e4000_config rtl2832u_e4000_config = { + .i2c_addr = 0x64, + .clock = 28800000, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -774,6 +781,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) adap->fe[0]->ops.read_signal_strength = adap->fe[0]->ops.tuner_ops.get_rf_strength; return 0; + case TUNER_RTL2832_E4000: + fe = dvb_attach(e4000_attach, adap->fe[0], &d->i2c_adap, + &rtl2832u_e4000_config); + break; default: fe = NULL; dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, @@ -1223,6 +1234,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK, &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) }, + { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, + &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From 6d60805fd2e8103fafa02fcf6448446229ebd511 Mon Sep 17 00:00:00 2001 From: Philipp Dreimann Date: Sun, 2 Sep 2012 19:30:54 -0300 Subject: [media] Add the usb id of the Trekstor DVB-T Stick Terres 2.0 It needs the e4000 tuner driver. Signed-off-by: Philipp Dreimann Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 26c44818a5ab..fed6dcddc395 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -82,6 +82,7 @@ #define USB_PID_AFATECH_AF9035_1003 0x1003 #define USB_PID_AFATECH_AF9035_9035 0x9035 #define USB_PID_TREKSTOR_DVBT 0x901b +#define USB_PID_TREKSTOR_TERRES_2_0 0xC803 #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 #define USB_PID_ANYSEE 0x861f diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 88b5ea12ed28..d0d23f2c1fe7 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1236,6 +1236,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0, + &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From da35de640a0e9c805aba70439f524234890b96c5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 4 Sep 2012 10:43:26 -0300 Subject: [media] tlg2300: fix missing check for audio creation If we fail to set up the capture device we go through negative indexes and badness happens. Add the missing test. Resolves-bug: https://bugzilla.kernel.org/show_bug.cgi?id=44551 Signed-off-by: Alan Cox Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tlg2300/pd-alsa.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/tlg2300/pd-alsa.c b/drivers/media/usb/tlg2300/pd-alsa.c index 9f8b7da56b67..0c778695e2c3 100644 --- a/drivers/media/usb/tlg2300/pd-alsa.c +++ b/drivers/media/usb/tlg2300/pd-alsa.c @@ -305,6 +305,10 @@ int poseidon_audio_init(struct poseidon *p) return ret; ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm); + if (ret < 0) { + snd_free_card(card); + return ret; + } snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); pcm->info_flags = 0; pcm->private_data = p; -- cgit v1.2.3 From fac44ee564a54db020ad384e0de94b5d8be3c6b5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Sep 2012 16:42:44 -0300 Subject: [media] pd-alsa: fix compilation breakage by commit da35de640 commit da35de640 broke compilation, as it reverted the name of the usb card free function. Cc: Alan Cox Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tlg2300/pd-alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/tlg2300/pd-alsa.c b/drivers/media/usb/tlg2300/pd-alsa.c index 0c778695e2c3..3f3e141f70fb 100644 --- a/drivers/media/usb/tlg2300/pd-alsa.c +++ b/drivers/media/usb/tlg2300/pd-alsa.c @@ -306,7 +306,7 @@ int poseidon_audio_init(struct poseidon *p) ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm); if (ret < 0) { - snd_free_card(card); + snd_card_free(card); return ret; } snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); -- cgit v1.2.3 From aa468cc550ff3e81aef37aca8fdd9a3456ca5f3a Mon Sep 17 00:00:00 2001 From: Jose Alberto Reguero Date: Sat, 8 Sep 2012 13:08:22 -0300 Subject: [media] ttusb2: add toggle to the tt3650_rc_query function This patch add the toggle bit to the tt3650_rc_query function of the ttusb2 driver. Signed-off-by: Jose Alberto Reguero Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/ttusb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index e53a1061cb8e..6a50cdea3bce 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -440,7 +440,7 @@ static int tt3650_rc_query(struct dvb_usb_device *d) /* got a "press" event */ st->last_rc_key = (rx[3] << 8) | rx[2]; deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]); - rc_keydown(d->rc_dev, st->last_rc_key, 0); + rc_keydown(d->rc_dev, st->last_rc_key, rx[1]); } else if (st->last_rc_key) { rc_keyup(d->rc_dev); st->last_rc_key = 0; -- cgit v1.2.3 From 384df49a6a97d411af33da3237558411789b67c5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 8 Sep 2012 22:07:25 -0300 Subject: [media] rtl28xxu: add support for FCI FC2580 silicon tuner driver Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/Kconfig | 1 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 329d2221e915..e09930c6b4f9 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -142,6 +142,7 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index d0d23f2c1fe7..f195b778794a 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -31,6 +31,7 @@ #include "fc0012.h" #include "fc0013.h" #include "e4000.h" +#include "fc2580.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -576,10 +577,11 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) ret = rtl28xxu_ctrl_msg(d, &req_fc2580); if (ret == 0 && buf[0] == 0x56) { priv->tuner = TUNER_RTL2832_FC2580; - /* TODO implement tuner */ + /* FIXME: do not abuse fc0012 settings */ + rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; dev_info(&d->udev->dev, "%s: FC2580 tuner found", KBUILD_MODNAME); - goto unsupported; + goto found; } /* check MT2063 ID register; reg=00 val=9e || 9c */ @@ -753,6 +755,11 @@ static const struct e4000_config rtl2832u_e4000_config = { .clock = 28800000, }; +static const struct fc2580_config rtl2832u_fc2580_config = { + .i2c_addr = 0x56, + .clock = 16384000, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -785,6 +792,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(e4000_attach, adap->fe[0], &d->i2c_adap, &rtl2832u_e4000_config); break; + case TUNER_RTL2832_FC2580: + fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap, + &rtl2832u_fc2580_config); + break; default: fe = NULL; dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, -- cgit v1.2.3 From c2d246d1f0302fb4b390c06b73ca4f0ec6553bc6 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 8 Sep 2012 22:07:26 -0300 Subject: [media] rtl28xxu: Dexatek DK DVB-T Dongle [1d19:1101] It is RTL2832U + FC2580 reference design. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index fed6dcddc395..d572307353e9 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -24,6 +24,7 @@ #define USB_VID_COMPRO_UNK 0x145f #define USB_VID_CONEXANT 0x0572 #define USB_VID_CYPRESS 0x04b4 +#define USB_VID_DEXATEK 0x1d19 #define USB_VID_DIBCOM 0x10b8 #define USB_VID_DPOSH 0x1498 #define USB_VID_DVICO 0x0fe9 diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index f195b778794a..a62238f5aa2c 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1249,6 +1249,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0, &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, + { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101, + &rtl2832u_props, "Dexatek DK DVB-T Dongle", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From 832cc7cdfb8ba78e03cf5c8c0ad9701ed0e20fb6 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 11 Sep 2012 22:27:04 -0300 Subject: [media] rtl2832: separate tuner specific init from general It is first step closer to support multiple tuners. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/rtl2832.c | 58 ++++++++++++------------------ drivers/media/dvb-frontends/rtl2832.h | 5 ++- drivers/media/dvb-frontends/rtl2832_priv.h | 33 +++++++++++++++++ drivers/media/usb/dvb-usb-v2/rtl28xxu.h | 5 +-- 4 files changed, 63 insertions(+), 38 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 4d40b4f42a1f..d670fe76860d 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -382,10 +382,10 @@ err: static int rtl2832_init(struct dvb_frontend *fe) { struct rtl2832_priv *priv = fe->demodulator_priv; - int i, ret; - + int i, ret, len; u8 en_bbin; u64 pset_iffreq; + const struct rtl2832_reg_value *init; /* initialization values for the demodulator registers */ struct rtl2832_reg_value rtl2832_initial_regs[] = { @@ -432,39 +432,8 @@ static int rtl2832_init(struct dvb_frontend *fe) {DVBT_TRK_KC_I2, 0x5}, {DVBT_CR_THD_SET2, 0x1}, {DVBT_SPEC_INV, 0x0}, - {DVBT_DAGC_TRG_VAL, 0x5a}, - {DVBT_AGC_TARG_VAL_0, 0x0}, - {DVBT_AGC_TARG_VAL_8_1, 0x5a}, - {DVBT_AAGC_LOOP_GAIN, 0x16}, - {DVBT_LOOP_GAIN2_3_0, 0x6}, - {DVBT_LOOP_GAIN2_4, 0x1}, - {DVBT_LOOP_GAIN3, 0x16}, - {DVBT_VTOP1, 0x35}, - {DVBT_VTOP2, 0x21}, - {DVBT_VTOP3, 0x21}, - {DVBT_KRF1, 0x0}, - {DVBT_KRF2, 0x40}, - {DVBT_KRF3, 0x10}, - {DVBT_KRF4, 0x10}, - {DVBT_IF_AGC_MIN, 0x80}, - {DVBT_IF_AGC_MAX, 0x7f}, - {DVBT_RF_AGC_MIN, 0x80}, - {DVBT_RF_AGC_MAX, 0x7f}, - {DVBT_POLAR_RF_AGC, 0x0}, - {DVBT_POLAR_IF_AGC, 0x0}, - {DVBT_AD7_SETTING, 0xe9bf}, - {DVBT_EN_GI_PGA, 0x0}, - {DVBT_THD_LOCK_UP, 0x0}, - {DVBT_THD_LOCK_DW, 0x0}, - {DVBT_THD_UP1, 0x11}, - {DVBT_THD_DW1, 0xef}, - {DVBT_INTER_CNT_LEN, 0xc}, - {DVBT_GI_PGA_STATE, 0x0}, - {DVBT_EN_AGC_PGA, 0x1}, - {DVBT_IF_AGC_MAN, 0x0}, }; - dbg("%s", __func__); en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0); @@ -478,8 +447,6 @@ static int rtl2832_init(struct dvb_frontend *fe) pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); pset_iffreq = pset_iffreq & 0x3fffff; - - for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg, rtl2832_initial_regs[i].value); @@ -487,6 +454,27 @@ static int rtl2832_init(struct dvb_frontend *fe) goto err; } + /* load tuner specific settings */ + dbg("%s: load settings for tuner=%02x", __func__, priv->cfg.tuner); + switch (priv->cfg.tuner) { + case RTL2832_TUNER_FC0012: + case RTL2832_TUNER_FC0013: + len = ARRAY_SIZE(rtl2832_tuner_init_fc0012); + init = rtl2832_tuner_init_fc0012; + break; + default: + ret = -EINVAL; + goto err; + } + + for (i = 0; i < len; i++) { + ret = rtl2832_wr_demod_reg(priv, + rtl2832_tuner_init_fc0012[i].reg, + rtl2832_tuner_init_fc0012[i].value); + if (ret) + goto err; + } + /* if frequency settings */ ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); if (ret) diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h index d94dc9a3fa62..5da0cc4a0446 100644 --- a/drivers/media/dvb-frontends/rtl2832.h +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -44,11 +44,14 @@ struct rtl2832_config { u32 if_dvbt; /* + * tuner + * XXX: This must be keep sync with dvb_usb_rtl28xxu demod driver. */ +#define RTL2832_TUNER_FC0012 0x26 +#define RTL2832_TUNER_FC0013 0x29 u8 tuner; }; - #if defined(CONFIG_DVB_RTL2832) || \ (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE)) extern struct dvb_frontend *rtl2832_attach( diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index 0ce9502da8ba..65dd62a65684 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -257,4 +257,37 @@ enum DVBT_REG_BIT_NAME { DVBT_REG_BIT_NAME_ITEM_TERMINATOR, }; +static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = { + {DVBT_DAGC_TRG_VAL, 0x5a}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x5a}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x6}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x16}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9bf}, + {DVBT_EN_GI_PGA, 0x0}, + {DVBT_THD_LOCK_UP, 0x0}, + {DVBT_THD_LOCK_DW, 0x0}, + {DVBT_THD_UP1, 0x11}, + {DVBT_THD_DW1, 0xef}, + {DVBT_INTER_CNT_LEN, 0xc}, + {DVBT_GI_PGA_STATE, 0x0}, + {DVBT_EN_AGC_PGA, 0x1}, + {DVBT_IF_AGC_MAN, 0x0}, +}; + #endif /* RTL2832_PRIV_H */ diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index 035a9c890ce5..c6c8a4fe0b32 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -63,14 +63,15 @@ enum rtl28xxu_chip_id { CHIP_ID_RTL2832U, }; +/* XXX: Hack. This must be keep sync with rtl2832 demod driver. */ enum rtl28xxu_tuner { TUNER_NONE, - TUNER_RTL2830_QT1010, + TUNER_RTL2830_QT1010 = 0x10, TUNER_RTL2830_MT2060, TUNER_RTL2830_MXL5005S, - TUNER_RTL2832_MT2266, + TUNER_RTL2832_MT2266 = 0x20, TUNER_RTL2832_FC2580, TUNER_RTL2832_MT2063, TUNER_RTL2832_MAX3543, -- cgit v1.2.3 From 1835af1003655ab89fd78fc8e8fe69f4e5dcc465 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 11 Sep 2012 22:27:06 -0300 Subject: [media] af9035: relax frontend callback error handling It is not good idea to return error for missing callback handler as whole callback as optional and could be missing by intentionally. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index b7004441ac2a..fdec3b1a186b 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -652,7 +652,7 @@ static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) break; } - return -ENODEV; + return 0; } static int af9035_frontend_callback(void *adapter_priv, int component, @@ -661,6 +661,9 @@ static int af9035_frontend_callback(void *adapter_priv, int component, struct i2c_adapter *adap = adapter_priv; struct dvb_usb_device *d = i2c_get_adapdata(adap); + dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n", + __func__, component, cmd, arg); + switch (component) { case DVB_FRONTEND_COMPONENT_TUNER: return af9035_tuner_callback(d, cmd, arg); @@ -668,7 +671,7 @@ static int af9035_frontend_callback(void *adapter_priv, int component, break; } - return -EINVAL; + return 0; } static int af9035_frontend_attach(struct dvb_usb_adapter *adap) -- cgit v1.2.3 From 5be65721a7f6cdf93e34b2b7497bda4c07c469ed Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 11 Sep 2012 22:27:09 -0300 Subject: [media] rtl28xxu: add support for tua9001 tuner based devices Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 95 +++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index a62238f5aa2c..31c9f440a4e2 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -32,6 +32,7 @@ #include "fc0013.h" #include "e4000.h" #include "fc2580.h" +#include "tua9001.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -399,6 +400,12 @@ static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = { .tuner = TUNER_RTL2832_FC0013 }; +static struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .tuner = TUNER_RTL2832_TUA9001, +}; + static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -443,6 +450,54 @@ static int rtl2832u_fc0013_tuner_callback(struct dvb_usb_device *d, return 0; } +static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int ret; + u8 val; + + dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg); + + /* + * CEN always enabled by hardware wiring + * RESETN GPIO4 + * RXEN GPIO1 + */ + + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + if (ret < 0) + goto err; + + switch (cmd) { + case TUA9001_CMD_RESETN: + if (arg) + val |= (1 << 4); + else + val &= ~(1 << 4); + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret < 0) + goto err; + break; + case TUA9001_CMD_RXEN: + if (arg) + val |= (1 << 1); + else + val &= ~(1 << 1); + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret < 0) + goto err; + break; + } + + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { struct rtl28xxu_priv *priv = d->priv; @@ -453,6 +508,9 @@ static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) case TUNER_RTL2832_FC0013: return rtl2832u_fc0013_tuner_callback(d, cmd, arg); + + case TUNER_RTL2832_TUA9001: + return rtl2832u_tua9001_tuner_callback(d, cmd, arg); default: break; } @@ -608,10 +666,10 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) ret = rtl28xxu_ctrl_msg(d, &req_tua9001); if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) { priv->tuner = TUNER_RTL2832_TUA9001; - /* TODO implement tuner */ + rtl2832_config = &rtl28xxu_rtl2832_tua9001_config; dev_info(&d->udev->dev, "%s: TUA9001 tuner found", KBUILD_MODNAME); - goto unsupported; + goto found; } /* check MXL5007R ID register; reg=d9 val=14 */ @@ -760,12 +818,17 @@ static const struct fc2580_config rtl2832u_fc2580_config = { .clock = 16384000, }; +static struct tua9001_config rtl2832u_tua9001_config = { + .i2c_addr = 0x60, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; struct dvb_usb_device *d = adap_to_d(adap); struct rtl28xxu_priv *priv = d_to_priv(d); struct dvb_frontend *fe; + u8 val; dev_dbg(&d->udev->dev, "%s:\n", __func__); @@ -796,6 +859,33 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap, &rtl2832u_fc2580_config); break; + case TUNER_RTL2832_TUA9001: + /* enable GPIO1 and GPIO4 as output */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_DIR, &val); + if (ret < 0) + goto err; + + val &= ~(1 << 1); + val &= ~(1 << 4); + + ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, val); + if (ret < 0) + goto err; + + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_EN, &val); + if (ret < 0) + goto err; + + val |= (1 << 1); + val |= (1 << 4); + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, val); + if (ret < 0) + goto err; + + fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap, + &rtl2832u_tua9001_config); + break; default: fe = NULL; dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, @@ -978,7 +1068,6 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) if (ret) goto err; - /* streaming EP: clear stall & reset */ ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2); if (ret) -- cgit v1.2.3 From bab9b4fe089668ecab98547bec7e3d77e01ad206 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 11:37:25 -0300 Subject: [media] af9015: declare MODULE_FIRMWARE Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 3 ++- drivers/media/usb/dvb-usb-v2/af9015.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 9afceedc9d1f..d9d3030106d4 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1307,7 +1307,7 @@ static struct dvb_usb_device_properties af9015_props = { .generic_bulk_ctrl_endpoint_response = 0x81, .identify_state = af9015_identify_state, - .firmware = "dvb-usb-af9015.fw", + .firmware = AF9015_FIRMWARE, .download_firmware = af9015_download_firmware, .i2c_algo = &af9015_i2c_algo, @@ -1433,3 +1433,4 @@ module_usb_driver(af9015_usb_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Afatech AF9015 driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(AF9015_FIRMWARE); diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h index c6b304d962ad..35f946c478f2 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.h +++ b/drivers/media/usb/dvb-usb-v2/af9015.h @@ -57,6 +57,8 @@ #define warn(format, arg...) \ printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#define AF9015_FIRMWARE "dvb-usb-af9015.fw" + /* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. We use smaller - about 1/4 from the original, 5 and 87. */ #define TS_PACKET_SIZE 188 -- cgit v1.2.3 From 6a60e3f653a76bd0771216b3225b82861fa8dfd1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 11:37:27 -0300 Subject: [media] ec168: declare MODULE_FIRMWARE Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/ec168.c | 3 ++- drivers/media/usb/dvb-usb-v2/ec168.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c index b74c810e7dec..b6a9c5b5e8ad 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.c +++ b/drivers/media/usb/dvb-usb-v2/ec168.c @@ -322,7 +322,7 @@ static struct dvb_usb_device_properties ec168_props = { .bInterfaceNumber = 1, .identify_state = ec168_identify_state, - .firmware = "dvb-usb-ec168.fw", + .firmware = EC168_FIRMWARE, .download_firmware = ec168_download_firmware, .i2c_algo = &ec168_i2c_algo, @@ -374,3 +374,4 @@ module_usb_driver(ec168_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("E3C EC168 driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(EC168_FIRMWARE); diff --git a/drivers/media/usb/dvb-usb-v2/ec168.h b/drivers/media/usb/dvb-usb-v2/ec168.h index f65180822acb..615a6569421f 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.h +++ b/drivers/media/usb/dvb-usb-v2/ec168.h @@ -25,6 +25,7 @@ #include "dvb_usb.h" #define EC168_USB_TIMEOUT 1000 +#define EC168_FIRMWARE "dvb-usb-ec168.fw" struct ec168_req { u8 cmd; /* [1] */ -- cgit v1.2.3 From 66b3c4deb9735e18d5b71dbcbf9532bdf080d001 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:48 -0300 Subject: [media] rtl2830: use .get_if_frequency() Use .get_if_frequency() as all used tuner drivers (mt2060/qt1010/mxl5005s) supports it. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/rtl2830.c | 57 +++++++++++++++++++-------------- drivers/media/dvb-frontends/rtl2830.h | 7 ---- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 3 -- 3 files changed, 33 insertions(+), 34 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index eca1d72f0684..3954760f2bde 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -182,9 +182,6 @@ static int rtl2830_init(struct dvb_frontend *fe) { struct rtl2830_priv *priv = fe->demodulator_priv; int ret, i; - u64 num; - u8 buf[3], tmp; - u32 if_ctl; struct rtl2830_reg_val_mask tab[] = { { 0x00d, 0x01, 0x03 }, { 0x00d, 0x10, 0x10 }, @@ -240,26 +237,6 @@ static int rtl2830_init(struct dvb_frontend *fe) if (ret) goto err; - num = priv->cfg.if_dvbt % priv->cfg.xtal; - num *= 0x400000; - num = div_u64(num, priv->cfg.xtal); - num = -num; - if_ctl = num & 0x3fffff; - dev_dbg(&priv->i2c->dev, "%s: if_ctl=%08x\n", __func__, if_ctl); - - ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */ - if (ret) - goto err; - - buf[0] = tmp << 6; - buf[0] = (if_ctl >> 16) & 0x3f; - buf[1] = (if_ctl >> 8) & 0xff; - buf[2] = (if_ctl >> 0) & 0xff; - - ret = rtl2830_wr_regs(priv, 0x119, buf, 3); - if (ret) - goto err; - /* TODO: spec init */ /* soft reset */ @@ -301,6 +278,9 @@ static int rtl2830_set_frontend(struct dvb_frontend *fe) struct rtl2830_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; + u64 num; + u8 buf[3], tmp; + u32 if_ctl, if_frequency; static u8 bw_params1[3][34] = { { 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41, @@ -325,7 +305,6 @@ static int rtl2830_set_frontend(struct dvb_frontend *fe) {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */ }; - dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d inversion=%d\n", __func__, c->frequency, c->bandwidth_hz, c->inversion); @@ -353,6 +332,36 @@ static int rtl2830_set_frontend(struct dvb_frontend *fe) if (ret) goto err; + /* program if frequency */ + if (fe->ops.tuner_ops.get_if_frequency) + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + else + ret = -EINVAL; + + if (ret < 0) + goto err; + + num = if_frequency % priv->cfg.xtal; + num *= 0x400000; + num = div_u64(num, priv->cfg.xtal); + num = -num; + if_ctl = num & 0x3fffff; + dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d if_ctl=%08x\n", + __func__, if_frequency, if_ctl); + + ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */ + if (ret) + goto err; + + buf[0] = tmp << 6; + buf[0] |= (if_ctl >> 16) & 0x3f; + buf[1] = (if_ctl >> 8) & 0xff; + buf[2] = (if_ctl >> 0) & 0xff; + + ret = rtl2830_wr_regs(priv, 0x119, buf, 3); + if (ret) + goto err; + /* 1/2 split I2C write */ ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17); if (ret) diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h index e125166eed95..f4349a1fc03e 100644 --- a/drivers/media/dvb-frontends/rtl2830.h +++ b/drivers/media/dvb-frontends/rtl2830.h @@ -46,13 +46,6 @@ struct rtl2830_config { */ bool spec_inv; - /* - * IFs for all used modes. - * Hz - * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000 - */ - u32 if_dvbt; - /* */ u8 vtop; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 31c9f440a4e2..c3e2602af81e 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -259,7 +259,6 @@ static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = { .xtal = 28800000, .ts_mode = 0, .spec_inv = 1, - .if_dvbt = 36150000, .vtop = 0x20, .krf = 0x04, .agc_targ_val = 0x2d, @@ -271,7 +270,6 @@ static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = { .xtal = 28800000, .ts_mode = 0, .spec_inv = 1, - .if_dvbt = 36125000, .vtop = 0x20, .krf = 0x04, .agc_targ_val = 0x2d, @@ -282,7 +280,6 @@ static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = { .xtal = 28800000, .ts_mode = 0, .spec_inv = 0, - .if_dvbt = 4570000, .vtop = 0x3f, .krf = 0x04, .agc_targ_val = 0x3e, -- cgit v1.2.3 From f224749b68034619ff722576f46e4a4932d69d27 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:50 -0300 Subject: [media] af9015: use Kernel dev_foo() logging ... and some minor logging changes. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 155 ++++++++++++++++++++-------------- drivers/media/usb/dvb-usb-v2/af9015.h | 21 ----- 2 files changed, 91 insertions(+), 85 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index d9d3030106d4..c429da7da95d 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -23,9 +23,6 @@ #include "af9015.h" -static int dvb_usb_af9015_debug; -module_param_named(debug, dvb_usb_af9015_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); static int dvb_usb_af9015_remote; module_param_named(remote, dvb_usb_af9015_remote, int, 0644); MODULE_PARM_DESC(remote, "select remote"); @@ -72,15 +69,17 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) case BOOT: break; default: - err("unknown command:%d", req->cmd); + dev_err(&d->udev->dev, "%s: unknown command=%d\n", + KBUILD_MODNAME, req->cmd); ret = -1; goto error; } /* buffer overflow check */ if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) || - (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { - err("too much data; cmd:%d len:%d", req->cmd, req->data_len); + (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { + dev_err(&d->udev->dev, "%s: too much data; cmd=%d len=%d\n", + KBUILD_MODNAME, req->cmd, req->data_len); ret = -EINVAL; goto error; } @@ -106,7 +105,8 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) /* check status */ if (rlen && buf[1]) { - err("command failed:%d", buf[1]); + dev_err(&d->udev->dev, "%s: command failed=%d\n", + KBUILD_MODNAME, buf[1]); ret = -1; goto error; } @@ -334,7 +334,8 @@ static int af9015_identify_state(struct dvb_usb_device *d, const char **name) if (ret) return ret; - deb_info("%s: reply:%02x\n", __func__, reply); + dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply); + if (reply == 0x02) ret = WARM; else @@ -350,8 +351,7 @@ static int af9015_download_firmware(struct dvb_usb_device *d, int i, len, remaining, ret; struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL}; u16 checksum = 0; - - deb_info("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); /* calc checksum */ for (i = 0; i < fw->size; i++) @@ -373,7 +373,9 @@ static int af9015_download_firmware(struct dvb_usb_device *d, ret = af9015_ctrl_msg(d, &req); if (ret) { - err("firmware download failed:%d", ret); + dev_err(&d->udev->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); goto error; } } @@ -383,7 +385,8 @@ static int af9015_download_firmware(struct dvb_usb_device *d, req.data_len = 0; ret = af9015_ctrl_msg(d, &req); if (ret) { - err("firmware boot failed:%d", ret); + dev_err(&d->udev->dev, "%s: firmware boot failed=%d\n", + KBUILD_MODNAME, ret); goto error; } @@ -414,9 +417,9 @@ static int af9015_eeprom_hash(struct dvb_usb_device *d) eeprom[reg] = val; } - if (dvb_usb_af9015_debug & 0x01) - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, eeprom, - eeprom_size); + for (reg = 0; reg < eeprom_size; reg += 16) + dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 16, + eeprom + reg); BUG_ON(eeprom_size % 4); @@ -426,7 +429,8 @@ static int af9015_eeprom_hash(struct dvb_usb_device *d) state->eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]); } - deb_info("%s: eeprom sum=%.8x\n", __func__, state->eeprom_sum); + dev_dbg(&d->udev->dev, "%s: eeprom sum=%.8x\n", + __func__, state->eeprom_sum); ret = 0; free: @@ -441,7 +445,7 @@ static int af9015_read_config(struct dvb_usb_device *d) u8 val, i, offset = 0; struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; - deb_info("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); /* IR remote controller */ req.addr = AF9015_EEPROM_IR_MODE; @@ -458,8 +462,8 @@ static int af9015_read_config(struct dvb_usb_device *d) if (ret) goto error; - deb_info("%s: IR mode=%d\n", __func__, val); state->ir_mode = val; + dev_dbg(&d->udev->dev, "%s: IR mode=%d\n", __func__, val); /* TS mode - one or two receivers */ req.addr = AF9015_EEPROM_TS_MODE; @@ -468,7 +472,7 @@ static int af9015_read_config(struct dvb_usb_device *d) goto error; state->dual_mode = val; - deb_info("%s: TS mode=%d\n", __func__, state->dual_mode); + dev_dbg(&d->udev->dev, "%s: TS mode=%d\n", __func__, state->dual_mode); /* disable 2nd adapter because we don't have PID-filters */ if (d->udev->speed == USB_SPEED_FULL) @@ -506,8 +510,9 @@ static int af9015_read_config(struct dvb_usb_device *d) state->af9013_config[i].clock = 25000000; break; }; - deb_info("%s: [%d] xtal=%d set clock=%d\n", __func__, i, - val, state->af9013_config[i].clock); + dev_dbg(&d->udev->dev, "%s: [%d] xtal=%d set clock=%d\n", + __func__, i, val, + state->af9013_config[i].clock); /* IF frequency */ req.addr = AF9015_EEPROM_IF1H + offset; @@ -524,8 +529,8 @@ static int af9015_read_config(struct dvb_usb_device *d) state->af9013_config[i].if_frequency += val; state->af9013_config[i].if_frequency *= 1000; - deb_info("%s: [%d] IF frequency=%d\n", __func__, i, - state->af9013_config[i].if_frequency); + dev_dbg(&d->udev->dev, "%s: [%d] IF frequency=%d\n", __func__, + i, state->af9013_config[i].if_frequency); /* MT2060 IF1 */ req.addr = AF9015_EEPROM_MT2060_IF1H + offset; @@ -538,7 +543,7 @@ static int af9015_read_config(struct dvb_usb_device *d) if (ret) goto error; state->mt2060_if1[i] += val; - deb_info("%s: [%d] MT2060 IF1=%d\n", __func__, i, + dev_dbg(&d->udev->dev, "%s: [%d] MT2060 IF1=%d\n", __func__, i, state->mt2060_if1[i]); /* tuner */ @@ -568,17 +573,21 @@ static int af9015_read_config(struct dvb_usb_device *d) state->af9013_config[i].spec_inv = 1; break; default: - warn("tuner id=%d not supported, please report!", val); + dev_err(&d->udev->dev, "%s: tuner id=%d not " \ + "supported, please report!\n", + KBUILD_MODNAME, val); return -ENODEV; }; state->af9013_config[i].tuner = val; - deb_info("%s: [%d] tuner id=%d\n", __func__, i, val); + dev_dbg(&d->udev->dev, "%s: [%d] tuner id=%d\n", + __func__, i, val); } error: if (ret) - err("eeprom read failed=%d", ret); + dev_err(&d->udev->dev, "%s: eeprom read failed=%d\n", + KBUILD_MODNAME, ret); /* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM content :-( Override some wrong values here. Ditto for the @@ -588,7 +597,9 @@ error: USB_PID_AVERMEDIA_A850) || (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_AVERMEDIA_A850T))) { - deb_info("%s: AverMedia A850: overriding config\n", __func__); + dev_dbg(&d->udev->dev, + "%s: AverMedia A850: overriding config\n", + __func__); /* disable dual mode */ state->dual_mode = 0; @@ -602,9 +613,10 @@ error: static int af9015_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, struct usb_data_stream_properties *stream) { - deb_info("%s: adap=%d\n", __func__, fe_to_adap(fe)->id); + struct dvb_usb_device *d = fe_to_d(fe); + dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id); - if (fe_to_d(fe)->udev->speed == USB_SPEED_FULL) + if (d->udev->speed == USB_SPEED_FULL) stream->u.bulk.buffersize = TS_USB11_FRAME_SIZE; return 0; @@ -721,7 +733,7 @@ static int af9015_copy_firmware(struct dvb_usb_device *d) u8 val, i; struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params), fw_params }; - deb_info("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); fw_params[0] = state->firmware_size >> 8; fw_params[1] = state->firmware_size & 0xff; @@ -736,7 +748,8 @@ static int af9015_copy_firmware(struct dvb_usb_device *d) if (ret) goto error; else - deb_info("%s: firmware status:%02x\n", __func__, val); + dev_dbg(&d->udev->dev, "%s: firmware status=%02x\n", + __func__, val); if (val == 0x0c) /* fw is running, no need for download */ goto exit; @@ -751,8 +764,10 @@ static int af9015_copy_firmware(struct dvb_usb_device *d) /* copy firmware */ ret = af9015_ctrl_msg(d, &req); if (ret) - err("firmware copy cmd failed:%d", ret); - deb_info("%s: firmware copy done\n", __func__); + dev_err(&d->udev->dev, "%s: firmware copy cmd failed=%d\n", + KBUILD_MODNAME, ret); + + dev_dbg(&d->udev->dev, "%s: firmware copy done\n", __func__); /* set I2C master clock back to normal */ ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */ @@ -762,7 +777,8 @@ static int af9015_copy_firmware(struct dvb_usb_device *d) /* request boot firmware */ ret = af9015_write_reg_i2c(d, state->af9013_config[1].i2c_addr, 0xe205, 1); - deb_info("%s: firmware boot cmd status:%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: firmware boot cmd status=%d\n", + __func__, ret); if (ret) goto error; @@ -772,8 +788,8 @@ static int af9015_copy_firmware(struct dvb_usb_device *d) /* check firmware status */ ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr, 0x98be, &val); - deb_info("%s: firmware status cmd status:%d fw status:%02x\n", - __func__, ret, val); + dev_dbg(&d->udev->dev, "%s: firmware status cmd status=%d " \ + "firmware status=%02x\n", __func__, ret, val); if (ret) goto error; @@ -782,10 +798,12 @@ static int af9015_copy_firmware(struct dvb_usb_device *d) } if (val == 0x04) { - err("firmware did not run"); + dev_err(&d->udev->dev, "%s: firmware did not run\n", + KBUILD_MODNAME); ret = -1; } else if (val != 0x0c) { - err("firmware boot timeout"); + dev_err(&d->udev->dev, "%s: firmware boot timeout\n", + KBUILD_MODNAME); ret = -1; } @@ -814,8 +832,10 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) if (state->dual_mode) { ret = af9015_copy_firmware(adap_to_d(adap)); if (ret) { - err("firmware copy to 2nd frontend " \ - "failed, will disable it"); + dev_err(&adap_to_d(adap)->udev->dev, + "%s: firmware copy to 2nd " \ + "frontend failed, will " \ + "disable it\n", KBUILD_MODNAME); state->dual_mode = 0; return -ENODEV; } @@ -921,9 +941,10 @@ static struct mxl5007t_config af9015_mxl5007t_config = { static int af9015_tuner_attach(struct dvb_usb_adapter *adap) { - struct af9015_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + struct af9015_state *state = d_to_priv(d); int ret; - deb_info("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); switch (state->af9013_config[adap->id].tuner) { case AF9013_TUNER_MT2060: @@ -977,9 +998,10 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) break; case AF9013_TUNER_UNKNOWN: default: + dev_err(&d->udev->dev, "%s: unknown tuner id=%d\n", + KBUILD_MODNAME, + state->af9013_config[adap->id].tuner); ret = -ENODEV; - err("Unknown tuner id:%d", - state->af9013_config[adap->id].tuner); } if (adap->fe[0]->ops.tuner_ops.init) { @@ -999,13 +1021,14 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) { + struct dvb_usb_device *d = adap_to_d(adap); int ret; - deb_info("%s: onoff:%d\n", __func__, onoff); + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); if (onoff) - ret = af9015_set_reg_bit(adap_to_d(adap), 0xd503, 0); + ret = af9015_set_reg_bit(d, 0xd503, 0); else - ret = af9015_clear_reg_bit(adap_to_d(adap), 0xd503, 0); + ret = af9015_clear_reg_bit(d, 0xd503, 0); return ret; } @@ -1013,22 +1036,22 @@ static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) { + struct dvb_usb_device *d = adap_to_d(adap); int ret; u8 idx; + dev_dbg(&d->udev->dev, "%s: index=%d pid=%04x onoff=%d\n", + __func__, index, pid, onoff); - deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n", - __func__, index, pid, onoff); - - ret = af9015_write_reg(adap_to_d(adap), 0xd505, (pid & 0xff)); + ret = af9015_write_reg(d, 0xd505, (pid & 0xff)); if (ret) goto error; - ret = af9015_write_reg(adap_to_d(adap), 0xd506, (pid >> 8)); + ret = af9015_write_reg(d, 0xd506, (pid >> 8)); if (ret) goto error; idx = ((index & 0x1f) | (1 << 5)); - ret = af9015_write_reg(adap_to_d(adap), 0xd504, idx); + ret = af9015_write_reg(d, 0xd504, idx); error: return ret; @@ -1040,7 +1063,7 @@ static int af9015_init_endpoint(struct dvb_usb_device *d) int ret; u16 frame_size; u8 packet_size; - deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); + dev_dbg(&d->udev->dev, "%s: USB speed=%d\n", __func__, d->udev->speed); if (d->udev->speed == USB_SPEED_FULL) { frame_size = TS_USB11_FRAME_SIZE/4; @@ -1115,7 +1138,9 @@ static int af9015_init_endpoint(struct dvb_usb_device *d) error: if (ret) - err("endpoint init failed:%d", ret); + dev_err(&d->udev->dev, "%s: endpoint init failed=%d\n", + KBUILD_MODNAME, ret); + return ret; } @@ -1123,7 +1148,7 @@ static int af9015_init(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); int ret; - deb_info("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); mutex_init(&state->fe_mutex); @@ -1177,21 +1202,21 @@ static int af9015_rc_query(struct dvb_usb_device *d) int ret; u8 buf[17]; - deb_info("%s:\n", __func__); - /* read registers needed to detect remote controller code */ ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf)); if (ret) goto error; /* If any of these are non-zero, assume invalid data */ - if (buf[1] || buf[2] || buf[3]) + if (buf[1] || buf[2] || buf[3]) { + dev_dbg(&d->udev->dev, "%s: invalid data\n", __func__); return ret; + } /* Check for repeat of previous code */ if ((state->rc_repeat != buf[6] || buf[0]) && !memcmp(&buf[12], state->rc_last, 4)) { - deb_rc("%s: key repeated\n", __func__); + dev_dbg(&d->udev->dev, "%s: key repeated\n", __func__); rc_keydown(d->rc_dev, state->rc_keycode, 0); state->rc_repeat = buf[6]; return ret; @@ -1199,7 +1224,8 @@ static int af9015_rc_query(struct dvb_usb_device *d) /* Only process key if canary killed */ if (buf[16] != 0xff && buf[0] != 0x01) { - deb_rc("%s: key pressed %*ph\n", __func__, 4, buf + 12); + dev_dbg(&d->udev->dev, "%s: key pressed %*ph\n", + __func__, 4, buf + 12); /* Reset the canary */ ret = af9015_write_reg(d, 0x98e9, 0xff); @@ -1224,7 +1250,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) } rc_keydown(d->rc_dev, state->rc_keycode, 0); } else { - deb_rc("%s: no key press\n", __func__); + dev_dbg(&d->udev->dev, "%s: no key press\n", __func__); /* Invalidate last keypress */ /* Not really needed, but helps with debug */ state->rc_last[2] = state->rc_last[3]; @@ -1235,7 +1261,8 @@ static int af9015_rc_query(struct dvb_usb_device *d) error: if (ret) { - err("%s: failed:%d", __func__, ret); + dev_warn(&d->udev->dev, "%s: rc query failed=%d\n", + KBUILD_MODNAME, ret); /* allow random errors as dvb-usb will stop polling on error */ if (!state->rc_failed) diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h index 35f946c478f2..533637dedd23 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.h +++ b/drivers/media/usb/dvb-usb-v2/af9015.h @@ -36,27 +36,6 @@ #include "tda18218.h" #include "mxl5007t.h" -#define DVB_USB_LOG_PREFIX "af9015" - -#ifdef CONFIG_DVB_USB_DEBUG -#define dprintk(var, level, args...) \ - do { if ((var & level)) printk(args); } while (0) -#define DVB_USB_DEBUG_STATUS -#else -#define dprintk(args...) -#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" -#endif - -#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args) -#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args) - -#undef err -#define err(format, arg...) \ - printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) -#undef warn -#define warn(format, arg...) \ - printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) - #define AF9015_FIRMWARE "dvb-usb-af9015.fw" /* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. -- cgit v1.2.3 From 2e35c66f138c6a4f50da863a82983a541f1c2a24 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:51 -0300 Subject: [media] af9015: improve af9015_eeprom_hash() Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 49 ++++++++++++++--------------------- 1 file changed, 20 insertions(+), 29 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index c429da7da95d..a4be303edb4f 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -398,43 +398,34 @@ error: static int af9015_eeprom_hash(struct dvb_usb_device *d) { struct af9015_state *state = d_to_priv(d); - int ret; - static const unsigned int eeprom_size = 256; - unsigned int reg; - u8 val, *eeprom; - struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; - - eeprom = kmalloc(eeprom_size, GFP_KERNEL); - if (eeprom == NULL) - return -ENOMEM; - - for (reg = 0; reg < eeprom_size; reg++) { - req.addr = reg; + int ret, i; + static const unsigned int AF9015_EEPROM_SIZE = 256; + u8 buf[AF9015_EEPROM_SIZE]; + struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, NULL}; + + /* read eeprom */ + for (i = 0; i < AF9015_EEPROM_SIZE; i++) { + req.addr = i; + req.data = &buf[i]; ret = af9015_ctrl_msg(d, &req); - if (ret) - goto free; - - eeprom[reg] = val; + if (ret < 0) + goto err; } - for (reg = 0; reg < eeprom_size; reg += 16) - dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 16, - eeprom + reg); - - BUG_ON(eeprom_size % 4); - - state->eeprom_sum = 0; - for (reg = 0; reg < eeprom_size / sizeof(u32); reg++) { + /* calculate checksum */ + for (i = 0; i < AF9015_EEPROM_SIZE / sizeof(u32); i++) { state->eeprom_sum *= GOLDEN_RATIO_PRIME_32; - state->eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]); + state->eeprom_sum += le32_to_cpu(((u32 *)buf)[i]); } + for (i = 0; i < AF9015_EEPROM_SIZE; i += 16) + dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 16, buf + i); + dev_dbg(&d->udev->dev, "%s: eeprom sum=%.8x\n", __func__, state->eeprom_sum); - - ret = 0; -free: - kfree(eeprom); + return 0; +err: + dev_err(&d->udev->dev, "%s: eeprom failed=%d\n", KBUILD_MODNAME, ret); return ret; } -- cgit v1.2.3 From e1e9e510bb8639ace59dcd1f8e6b5754dfbb9b44 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:52 -0300 Subject: [media] af9015: correct few error codes Plain '-1' is not very good error code. Use more suitable error code definitions. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index a4be303edb4f..824f1911ee21 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -71,7 +71,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) default: dev_err(&d->udev->dev, "%s: unknown command=%d\n", KBUILD_MODNAME, req->cmd); - ret = -1; + ret = -EIO; goto error; } @@ -107,7 +107,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) if (rlen && buf[1]) { dev_err(&d->udev->dev, "%s: command failed=%d\n", KBUILD_MODNAME, buf[1]); - ret = -1; + ret = -EIO; goto error; } @@ -791,11 +791,11 @@ static int af9015_copy_firmware(struct dvb_usb_device *d) if (val == 0x04) { dev_err(&d->udev->dev, "%s: firmware did not run\n", KBUILD_MODNAME); - ret = -1; + ret = -ETIMEDOUT; } else if (val != 0x0c) { dev_err(&d->udev->dev, "%s: firmware boot timeout\n", KBUILD_MODNAME); - ret = -1; + ret = -ETIMEDOUT; } error: -- cgit v1.2.3 From 119f7a8c5ded2789defdccc6dfe50d9934ea156e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:53 -0300 Subject: [media] af9035: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 90 +++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 41 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index fdec3b1a186b..7d599a999619 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -53,9 +53,9 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) /* buffer overflow check */ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || - req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { - pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__, - req->wlen, req->rlen); + req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { + dev_err(&d->udev->dev, "%s: too much data wlen=%d rlen=%d\n", + __func__, req->wlen, req->rlen); return -EINVAL; } @@ -89,17 +89,17 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) checksum = af9035_checksum(buf, rlen - 2); tmp_checksum = (buf[rlen - 2] << 8) | buf[rlen - 1]; if (tmp_checksum != checksum) { - pr_err("%s: command=%02x checksum mismatch (%04x != %04x)\n", - KBUILD_MODNAME, req->cmd, tmp_checksum, - checksum); + dev_err(&d->udev->dev, "%s: command=%02x checksum mismatch " \ + "(%04x != %04x)\n", KBUILD_MODNAME, req->cmd, + tmp_checksum, checksum); ret = -EIO; goto err; } /* check status */ if (buf[2]) { - pr_debug("%s: command=%02x failed fw error=%d\n", __func__, - req->cmd, buf[2]); + dev_dbg(&d->udev->dev, "%s: command=%02x failed fw error=%d\n", + __func__, req->cmd, buf[2]); ret = -EIO; goto err; } @@ -112,7 +112,7 @@ exit: return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -290,7 +290,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) if (ret < 0) goto err; - pr_debug("%s: reply=%*ph\n", __func__, 4, rbuf); + dev_dbg(&d->udev->dev, "%s: reply=%*ph\n", __func__, 4, rbuf); if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3]) ret = WARM; else @@ -299,7 +299,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) return ret; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -339,13 +339,13 @@ static int af9035_download_firmware(struct dvb_usb_device *d, hdr_checksum = fw->data[fw->size - i + 5] << 8; hdr_checksum |= fw->data[fw->size - i + 6] << 0; - pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n", - __func__, hdr_core, hdr_addr, hdr_data_len, - hdr_checksum); + dev_dbg(&d->udev->dev, "%s: core=%d addr=%04x data_len=%d " \ + "checksum=%04x\n", __func__, hdr_core, hdr_addr, + hdr_data_len, hdr_checksum); if (((hdr_core != 1) && (hdr_core != 2)) || (hdr_data_len > i)) { - pr_debug("%s: bad firmware\n", __func__); + dev_dbg(&d->udev->dev, "%s: bad firmware\n", __func__); break; } @@ -376,7 +376,8 @@ static int af9035_download_firmware(struct dvb_usb_device *d, i -= hdr_data_len + HDR_SIZE; - pr_debug("%s: data uploaded=%zu\n", __func__, fw->size - i); + dev_dbg(&d->udev->dev, "%s: data uploaded=%zu\n", + __func__, fw->size - i); } /* firmware loaded, request boot */ @@ -392,18 +393,19 @@ static int af9035_download_firmware(struct dvb_usb_device *d, goto err; if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { - pr_err("%s: firmware did not run\n", KBUILD_MODNAME); + dev_err(&d->udev->dev, "%s: firmware did not run\n", + KBUILD_MODNAME); ret = -ENODEV; goto err; } - pr_info("%s: firmware version=%d.%d.%d.%d", KBUILD_MODNAME, - rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d", + KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]); return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -445,7 +447,8 @@ static int af9035_download_firmware_it9135(struct dvb_usb_device *d, if (ret < 0) goto err; - pr_debug("%s: data uploaded=%d\n", __func__, i); + dev_dbg(&d->udev->dev, "%s: data uploaded=%d\n", + __func__, i); } } @@ -462,18 +465,19 @@ static int af9035_download_firmware_it9135(struct dvb_usb_device *d, goto err; if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { - pr_err("%s: firmware did not run\n", KBUILD_MODNAME); + dev_err(&d->udev->dev, "%s: firmware did not run\n", + KBUILD_MODNAME); ret = -ENODEV; goto err; } - pr_info("%s: firmware version=%d.%d.%d.%d", KBUILD_MODNAME, - rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d", + KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]); return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -491,7 +495,8 @@ static int af9035_read_config(struct dvb_usb_device *d) goto err; state->dual_mode = tmp; - pr_debug("%s: dual mode=%d\n", __func__, state->dual_mode); + dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", + __func__, state->dual_mode); for (i = 0; i < state->dual_mode + 1; i++) { /* tuner */ @@ -500,7 +505,8 @@ static int af9035_read_config(struct dvb_usb_device *d) goto err; state->af9033_config[i].tuner = tmp; - pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp); + dev_dbg(&d->udev->dev, "%s: [%d]tuner=%02x\n", + __func__, i, tmp); switch (tmp) { case AF9033_TUNER_TUA9001: @@ -510,8 +516,9 @@ static int af9035_read_config(struct dvb_usb_device *d) state->af9033_config[i].spec_inv = 1; break; default: - pr_info("%s: tuner ID=%02x not supported, please " \ - "report!", KBUILD_MODNAME, tmp); + dev_warn(&d->udev->dev, "%s: tuner id=%02x not " \ + "supported, please report!", + KBUILD_MODNAME, tmp); }; /* tuner IF frequency */ @@ -527,7 +534,7 @@ static int af9035_read_config(struct dvb_usb_device *d) tmp16 |= tmp << 8; - pr_debug("%s: [%d]IF=%d\n", __func__, i, tmp16); + dev_dbg(&d->udev->dev, "%s: [%d]IF=%d\n", __func__, i, tmp16); eeprom_shift = 0x10; /* shift for the 2nd tuner params */ } @@ -545,7 +552,7 @@ static int af9035_read_config(struct dvb_usb_device *d) return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -571,7 +578,7 @@ static int af9035_read_config_it9135(struct dvb_usb_device *d) return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -636,7 +643,7 @@ static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -716,7 +723,7 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -856,7 +863,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -888,8 +895,9 @@ static int af9035_init(struct dvb_usb_device *d) { 0x80f9a4, 0x00, 0x01 }, }; - pr_debug("%s: USB speed=%d frame_size=%04x packet_size=%02x\n", - __func__, d->udev->speed, frame_size, packet_size); + dev_dbg(&d->udev->dev, "%s: USB speed=%d frame_size=%04x " \ + "packet_size=%02x\n", __func__, + d->udev->speed, frame_size, packet_size); /* init endpoints */ for (i = 0; i < ARRAY_SIZE(tab); i++) { @@ -902,7 +910,7 @@ static int af9035_init(struct dvb_usb_device *d) return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -946,7 +954,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) if (ret < 0) goto err; - pr_debug("%s: ir_mode=%02x\n", __func__, tmp); + dev_dbg(&d->udev->dev, "%s: ir_mode=%02x\n", __func__, tmp); /* don't activate rc if in HID mode or if not available */ if (tmp == 5) { @@ -954,7 +962,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) if (ret < 0) goto err; - pr_debug("%s: ir_type=%02x\n", __func__, tmp); + dev_dbg(&d->udev->dev, "%s: ir_type=%02x\n", __func__, tmp); switch (tmp) { case 0: /* NEC */ @@ -977,7 +985,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; err: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } -- cgit v1.2.3 From a835ae6777ce642e1d6de704b878aaed932d69ef Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:54 -0300 Subject: [media] au6610: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/au6610.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/au6610.c b/drivers/media/usb/dvb-usb-v2/au6610.c index f309fd8217fd..ae6a671b7fd5 100644 --- a/drivers/media/usb/dvb-usb-v2/au6610.c +++ b/drivers/media/usb/dvb-usb-v2/au6610.c @@ -48,7 +48,8 @@ static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, index += wbuf[1]; break; default: - pr_err("%s: wlen = %d, aborting\n", KBUILD_MODNAME, wlen); + dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n", + KBUILD_MODNAME, wlen); ret = -EINVAL; goto error; } -- cgit v1.2.3 From 81527688924ace05cc99907713f14eb2ec064c37 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:55 -0300 Subject: [media] gl861: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/gl861.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index df78811f5234..b1b09c547861 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -38,7 +38,8 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, value = value + wbuf[1]; break; default: - pr_err("%s: wlen=%d, aborting\n", KBUILD_MODNAME, wlen); + dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n", + KBUILD_MODNAME, wlen); return -EINVAL; } -- cgit v1.2.3 From 3cedcab88f6ef96bcbe0c70d15c5d841086f9d43 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:56 -0300 Subject: [media] ec168: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/ec168.c | 40 +++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c index b6a9c5b5e8ad..5c68f3918bc8 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.c +++ b/drivers/media/usb/dvb-usb-v2/ec168.c @@ -61,7 +61,8 @@ static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req) request = DEMOD_RW; break; default: - pr_err("%s: unknown command=%02x\n", KBUILD_MODNAME, req->cmd); + dev_err(&d->udev->dev, "%s: unknown command=%02x\n", + KBUILD_MODNAME, req->cmd); ret = -EINVAL; goto error; } @@ -104,7 +105,7 @@ static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req) err_dealloc: kfree(buf); error: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -136,7 +137,8 @@ static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ret = ec168_ctrl_msg(d, &req); i += 2; } else { - pr_err("%s: I2C read not implemented\n", + dev_err(&d->udev->dev, "%s: I2C read not " \ + "implemented\n", KBUILD_MODNAME); ret = -EOPNOTSUPP; i += 2; @@ -187,13 +189,13 @@ static int ec168_identify_state(struct dvb_usb_device *d, const char **name) int ret; u8 reply; struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply}; - pr_debug("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); ret = ec168_ctrl_msg(d, &req); if (ret) goto error; - pr_debug("%s: reply=%02x\n", __func__, reply); + dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply); if (reply == 0x01) ret = WARM; @@ -202,7 +204,7 @@ static int ec168_identify_state(struct dvb_usb_device *d, const char **name) return ret; error: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -211,7 +213,7 @@ static int ec168_download_firmware(struct dvb_usb_device *d, { int ret, len, remaining; struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL}; - pr_debug("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); #define LEN_MAX 2048 /* max packet size */ for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { @@ -225,7 +227,8 @@ static int ec168_download_firmware(struct dvb_usb_device *d, ret = ec168_ctrl_msg(d, &req); if (ret) { - pr_err("%s: firmware download failed=%d\n", + dev_err(&d->udev->dev, + "%s: firmware download failed=%d\n", KBUILD_MODNAME, ret); goto error; } @@ -259,7 +262,7 @@ static int ec168_download_firmware(struct dvb_usb_device *d, return ret; error: - pr_debug("%s: failed=%d\n", __func__, ret); + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -269,9 +272,11 @@ static struct ec100_config ec168_ec100_config = { static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap) { - pr_debug("%s:\n", __func__); + struct dvb_usb_device *d = adap_to_d(adap); + dev_dbg(&d->udev->dev, "%s:\n", __func__); + adap->fe[0] = dvb_attach(ec100_attach, &ec168_ec100_config, - &adap_to_d(adap)->i2c_adap); + &d->i2c_adap); if (adap->fe[0] == NULL) return -ENODEV; @@ -297,19 +302,22 @@ static struct mxl5005s_config ec168_mxl5003s_config = { static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) { - pr_debug("%s:\n", __func__); - return dvb_attach(mxl5005s_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, + struct dvb_usb_device *d = adap_to_d(adap); + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + return dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap, &ec168_mxl5003s_config) == NULL ? -ENODEV : 0; } static int ec168_streaming_ctrl(struct dvb_frontend *fe, int onoff) { + struct dvb_usb_device *d = fe_to_d(fe); struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL}; - pr_debug("%s: onoff=%d\n", __func__, onoff); + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); + if (onoff) req.index = 0x0102; - return ec168_ctrl_msg(fe_to_d(fe), &req); + return ec168_ctrl_msg(d, &req); } /* DVB USB Driver stuff */ -- cgit v1.2.3 From b072eee0345efd3edb582466c627364b5fa63a99 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 20:23:57 -0300 Subject: [media] ce6230: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/ce6230.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.c b/drivers/media/usb/dvb-usb-v2/ce6230.c index 1c4357d804ff..f67b14bc32e3 100644 --- a/drivers/media/usb/dvb-usb-v2/ce6230.c +++ b/drivers/media/usb/dvb-usb-v2/ce6230.c @@ -49,7 +49,8 @@ static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); break; default: - pr_debug("%s: unknown command=%02x\n", __func__, req->cmd); + dev_err(&d->udev->dev, "%s: unknown command=%02x\n", + KBUILD_MODNAME, req->cmd); ret = -EINVAL; goto error; } @@ -78,8 +79,8 @@ static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) buf, req->data_len); if (ret < 0) - pr_err("%s: usb_control_msg() failed=%d\n", KBUILD_MODNAME, - ret); + dev_err(&d->udev->dev, "%s: usb_control_msg() failed=%d\n", + KBUILD_MODNAME, ret); else ret = 0; @@ -121,7 +122,8 @@ static int ce6230_i2c_master_xfer(struct i2c_adapter *adap, req.data = &msg[i+1].buf[0]; ret = ce6230_ctrl_msg(d, &req); } else { - pr_err("%s: I2C read not implemented\n", + dev_err(&d->udev->dev, "%s: I2C read not " \ + "implemented\n", KBUILD_MODNAME); ret = -EOPNOTSUPP; } @@ -176,10 +178,12 @@ static struct zl10353_config ce6230_zl10353_config = { static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap) { - pr_debug("%s:\n", __func__); + struct dvb_usb_device *d = adap_to_d(adap); + + dev_dbg(&d->udev->dev, "%s:\n", __func__); adap->fe[0] = dvb_attach(zl10353_attach, &ce6230_zl10353_config, - &adap_to_d(adap)->i2c_adap); + &d->i2c_adap); if (adap->fe[0] == NULL) return -ENODEV; @@ -205,12 +209,12 @@ static struct mxl5005s_config ce6230_mxl5003s_config = { static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) { + struct dvb_usb_device *d = adap_to_d(adap); int ret; - pr_debug("%s:\n", __func__); + dev_dbg(&d->udev->dev, "%s:\n", __func__); - ret = dvb_attach(mxl5005s_attach, adap->fe[0], - &adap_to_d(adap)->i2c_adap, + ret = dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap, &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0; return ret; } @@ -219,14 +223,14 @@ static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; - pr_debug("%s: onoff=%d\n", __func__, onoff); + dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); /* InterfaceNumber 1 / AlternateSetting 0 idle InterfaceNumber 1 / AlternateSetting 1 streaming */ ret = usb_set_interface(d->udev, 1, onoff); if (ret) - pr_err("%s: usb_set_interface() failed=%d\n", KBUILD_MODNAME, - ret); + dev_err(&d->udev->dev, "%s: usb_set_interface() failed=%d\n", + KBUILD_MODNAME, ret); return ret; } -- cgit v1.2.3 From 18ad89659a345c017ab61f758f9dfedab3c8190f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sun, 19 Aug 2012 21:23:43 -0300 Subject: [media] stk1160: Make kill/free urb debug message more verbose This is just a cleaning patch to produce more useful debug messages. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-video.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index 378526981146..022092aab924 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -342,18 +342,18 @@ static void stk1160_isoc_irq(struct urb *urb) */ void stk1160_cancel_isoc(struct stk1160 *dev) { - int i; + int i, num_bufs = dev->isoc_ctl.num_bufs; /* * This check is not necessary, but we add it * to avoid a spurious debug message */ - if (!dev->isoc_ctl.num_bufs) + if (!num_bufs) return; - stk1160_dbg("killing urbs...\n"); + stk1160_dbg("killing %d urbs...\n", num_bufs); - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + for (i = 0; i < num_bufs; i++) { /* * To kill urbs we can't be in atomic context. @@ -373,11 +373,11 @@ void stk1160_cancel_isoc(struct stk1160 *dev) void stk1160_free_isoc(struct stk1160 *dev) { struct urb *urb; - int i; + int i, num_bufs = dev->isoc_ctl.num_bufs; - stk1160_dbg("freeing urb buffers...\n"); + stk1160_dbg("freeing %d urb buffers...\n", num_bufs); - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + for (i = 0; i < num_bufs; i++) { urb = dev->isoc_ctl.urb[i]; if (urb) { -- cgit v1.2.3 From 065741840b1a58e94d2304b283286b355cbbc616 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sun, 19 Aug 2012 21:23:44 -0300 Subject: [media] stk1160: Handle urb allocation failure condition properly When an urb buffer can't be allocated, the currently allocated buffer count must be saved so they can properly released. Moreover, it's sufficient to call stk1160_free_isoc to have all urb buffers released. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-video.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index 022092aab924..8bdfb0275313 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -462,8 +462,7 @@ int stk1160_alloc_isoc(struct stk1160 *dev) urb = usb_alloc_urb(max_packets, GFP_KERNEL); if (!urb) { stk1160_err("cannot alloc urb[%d]\n", i); - stk1160_uninit_isoc(dev); - return -ENOMEM; + goto free_i_bufs; } dev->isoc_ctl.urb[i] = urb; @@ -474,10 +473,9 @@ int stk1160_alloc_isoc(struct stk1160 *dev) dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size, GFP_KERNEL); #endif if (!dev->isoc_ctl.transfer_buffer[i]) { - stk1160_err("cannot alloc %d bytes for tx buffer\n", - sb_size); - stk1160_uninit_isoc(dev); - return -ENOMEM; + stk1160_err("cannot alloc %d bytes for tx[%d] buffer\n", + sb_size, i); + goto free_i_bufs; } memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); @@ -514,5 +512,11 @@ int stk1160_alloc_isoc(struct stk1160 *dev) dev->isoc_ctl.num_bufs = num_bufs; return 0; + +free_i_bufs: + /* Save the allocated buffers so far, so we can properly free them */ + dev->isoc_ctl.num_bufs = i+1; + stk1160_free_isoc(dev); + return -ENOMEM; } -- cgit v1.2.3 From c6b69c6c58288587c33efb43ca6e19f1c80b2757 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sun, 19 Aug 2012 21:23:45 -0300 Subject: [media] stk1160: Fix s_fmt and try_fmt implementation The driver was expecting to get a valid pixelformat on s_fmt and try_fmt. This is wrong, since the user may pass a bitmask and expect the driver to change it, returning a valid (fourcc) pixelformat. This problem was spotted by v4l2-compliance. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-v4l.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index c414bc3570c5..879800dd162f 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -318,12 +318,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct stk1160 *dev = video_drvdata(file); - if (f->fmt.pix.pixelformat != format[0].fourcc) { - stk1160_err("fourcc format 0x%08x invalid\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - /* * User can't choose size at his own will, * so we just return him the current size chosen @@ -331,6 +325,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, * TODO: Implement frame scaling? */ + f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; f->fmt.pix.field = V4L2_FIELD_INTERLACED; @@ -346,14 +341,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, { struct stk1160 *dev = video_drvdata(file); struct vb2_queue *q = &dev->vb_vidq; - int rc; if (vb2_is_busy(q)) return -EBUSY; - rc = vidioc_try_fmt_vid_cap(file, priv, f); - if (rc < 0) - return rc; + vidioc_try_fmt_vid_cap(file, priv, f); /* We don't support any format changes */ -- cgit v1.2.3 From 8ac456495a33d9466076fea94594181ceefb76d9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sun, 19 Aug 2012 21:23:46 -0300 Subject: [media] stk1160: Stop device and unqueue buffers when start_streaming() fails If start_streaming() fails (e.g. out of memory) the driver needs to rewind the start procedure. This implies possibly stopping the device and clearing the buffer queue. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-v4l.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 879800dd162f..fe6e857969ca 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -184,7 +184,7 @@ static int stk1160_start_streaming(struct stk1160 *dev) if (!dev->isoc_ctl.num_bufs || new_pkt_size) { rc = stk1160_alloc_isoc(dev); if (rc < 0) - goto out_unlock; + goto out_stop_hw; } /* submit urbs and enables IRQ */ @@ -192,8 +192,7 @@ static int stk1160_start_streaming(struct stk1160 *dev) rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL); if (rc) { stk1160_err("cannot submit urb[%d] (%d)\n", i, rc); - stk1160_uninit_isoc(dev); - goto out_unlock; + goto out_uninit; } } @@ -206,7 +205,16 @@ static int stk1160_start_streaming(struct stk1160 *dev) stk1160_dbg("streaming started\n"); -out_unlock: + mutex_unlock(&dev->v4l_lock); + + return 0; + +out_uninit: + stk1160_uninit_isoc(dev); +out_stop_hw: + usb_set_interface(dev->udev, 0, 0); + stk1160_clear_queue(dev); + mutex_unlock(&dev->v4l_lock); return rc; -- cgit v1.2.3 From 2cca7d4e4dec86c20631612163b83477db4404c5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Sep 2012 10:16:34 -0300 Subject: [media] v4l2: remove experimental tag from a number of old drivers A number of old drivers still had the experimental tag. Time to remove it. It concerns the following drivers: VIDEO_TLV320AIC23B USB_STKWEBCAM VIDEO_CX18 VIDEO_CX18_ALSA VIDEO_ZORAN_AVS6EYES DVB_USB_AF9005 MEDIA_TUNER_TEA5761 VIDEO_NOON010PC30 This decision was taken during the 2012 Media Workshop. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 4 ++-- drivers/media/pci/cx18/Kconfig | 4 ++-- drivers/media/pci/zoran/Kconfig | 4 ++-- drivers/media/tuners/Kconfig | 5 ++--- drivers/media/usb/dvb-usb/Kconfig | 2 +- drivers/media/usb/stkwebcam/Kconfig | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 0e0793ae87fe..d3be0ca27361 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -117,7 +117,7 @@ config VIDEO_CS53L32A config VIDEO_TLV320AIC23B tristate "Texas Instruments TLV320AIC23B audio codec" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C ---help--- Support for the Texas Instruments TLV320AIC23B audio codec. @@ -492,7 +492,7 @@ config VIDEO_SR030PC30 config VIDEO_NOON010PC30 tristate "Siliconfile NOON010PC30 sensor support" - depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT ---help--- This driver supports NOON010PC30 CIF camera from Siliconfile diff --git a/drivers/media/pci/cx18/Kconfig b/drivers/media/pci/cx18/Kconfig index 9a9f765dad45..c675b83c43a9 100644 --- a/drivers/media/pci/cx18/Kconfig +++ b/drivers/media/pci/cx18/Kconfig @@ -1,6 +1,6 @@ config VIDEO_CX18 tristate "Conexant cx23418 MPEG encoder support" - depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C select I2C_ALGOBIT select VIDEOBUF_VMALLOC depends on RC_CORE @@ -25,7 +25,7 @@ config VIDEO_CX18 config VIDEO_CX18_ALSA tristate "Conexant 23418 DMA audio support" - depends on VIDEO_CX18 && SND && EXPERIMENTAL + depends on VIDEO_CX18 && SND select SND_PCM ---help--- This is a video4linux driver for direct (DMA) audio on diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig index a9b231808413..26ca8702e33f 100644 --- a/drivers/media/pci/zoran/Kconfig +++ b/drivers/media/pci/zoran/Kconfig @@ -65,8 +65,8 @@ config VIDEO_ZORAN_LML33R10 card. config VIDEO_ZORAN_AVS6EYES - tristate "AverMedia 6 Eyes support (EXPERIMENTAL)" - depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL + tristate "AverMedia 6 Eyes support" + depends on VIDEO_ZORAN_ZR36060 select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 622375eeff40..e8fdf713fce8 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -28,7 +28,7 @@ config MEDIA_TUNER select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT20XX if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_TEA5761 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT && EXPERIMENTAL + select MEDIA_TUNER_TEA5761 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT select MEDIA_TUNER_TEA5767 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA9887 if MEDIA_SUBDRV_AUTOSELECT @@ -78,9 +78,8 @@ config MEDIA_TUNER_TDA9887 analog IF demodulator. config MEDIA_TUNER_TEA5761 - tristate "TEA 5761 radio tuner (EXPERIMENTAL)" + tristate "TEA 5761 radio tuner" depends on MEDIA_SUPPORT && I2C - depends on EXPERIMENTAL default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y here to include support for the Philips TEA5761 radio tuner. diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 3c5fff89dbf1..fa0b2931d305 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -227,7 +227,7 @@ config DVB_USB_OPERA1 config DVB_USB_AF9005 tristate "Afatech AF9005 DVB-T USB1.1 support" - depends on DVB_USB && EXPERIMENTAL + depends on DVB_USB select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help diff --git a/drivers/media/usb/stkwebcam/Kconfig b/drivers/media/usb/stkwebcam/Kconfig index 2fb0c2b687f4..a6a00aa4fce6 100644 --- a/drivers/media/usb/stkwebcam/Kconfig +++ b/drivers/media/usb/stkwebcam/Kconfig @@ -1,6 +1,6 @@ config USB_STKWEBCAM tristate "USB Syntek DC1125 Camera support" - depends on VIDEO_V4L2 && EXPERIMENTAL + depends on VIDEO_V4L2 ---help--- Say Y here if you want to use this type of camera. Supported devices are typically found in some Asus laptops, -- cgit v1.2.3 From d88aab53bd267c9fb0b0451e9acae68091703eab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 17 Sep 2012 05:05:25 -0300 Subject: [media] v4l2: make vidioc_s_jpegcomp const Write-only ioctls should have a const argument in the ioctl op. Do this conversion for vidioc_s_jpegcomp. Adding const for write-only ioctls was decided during the 2012 Media Workshop. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_driver.c | 4 ++-- drivers/media/usb/cpia2/cpia2_v4l.c | 5 ++--- drivers/media/usb/gspca/gspca.c | 2 +- drivers/media/usb/gspca/gspca.h | 8 +++++--- drivers/media/usb/gspca/jeilinj.c | 2 +- drivers/media/usb/gspca/ov519.c | 2 +- drivers/media/usb/gspca/topro.c | 2 +- drivers/media/usb/gspca/zc3xx.c | 9 ++------- drivers/media/usb/s2255/s2255drv.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 2 +- include/media/v4l2-ioctl.h | 2 +- 11 files changed, 18 insertions(+), 22 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index f91b551e6a52..9ecd7d711f27 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -2674,7 +2674,7 @@ static int zoran_g_jpegcomp(struct file *file, void *__fh, } static int zoran_s_jpegcomp(struct file *file, void *__fh, - struct v4l2_jpegcompression *params) + const struct v4l2_jpegcompression *params) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; @@ -2701,7 +2701,7 @@ static int zoran_s_jpegcomp(struct file *file, void *__fh, if (!fh->buffers.allocated) fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); - fh->jpg_settings.jpg_comp = *params = settings.jpg_comp; + fh->jpg_settings.jpg_comp = settings.jpg_comp; sjpegc_unlock_and_return: mutex_unlock(&zr->resource_lock); diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index 5ca6f44b4c63..aeb9d2275725 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -734,7 +734,8 @@ static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompres * *****************************************************************************/ -static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) +static int cpia2_s_jpegcomp(struct file *file, void *fh, + const struct v4l2_jpegcompression *parms) { struct camera_data *cam = video_drvdata(file); @@ -743,8 +744,6 @@ static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompres cam->params.compression.inhibit_htables = !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); - parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | - V4L2_JPEG_MARKER_DHT; if(parms->APP_len != 0) { if(parms->APP_len > 0 && diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 2abbf52c781a..a2b934146ebf 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1686,7 +1686,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, } static int vidioc_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jpegcomp) + const struct v4l2_jpegcompression *jpegcomp) { struct gspca_dev *gspca_dev = video_drvdata(file); diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h index dc688c7f5e48..e3eab82cd4e5 100644 --- a/drivers/media/usb/gspca/gspca.h +++ b/drivers/media/usb/gspca/gspca.h @@ -83,8 +83,10 @@ struct gspca_frame; typedef int (*cam_op) (struct gspca_dev *); typedef void (*cam_v_op) (struct gspca_dev *); typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); -typedef int (*cam_jpg_op) (struct gspca_dev *, +typedef int (*cam_get_jpg_op) (struct gspca_dev *, struct v4l2_jpegcompression *); +typedef int (*cam_set_jpg_op) (struct gspca_dev *, + const struct v4l2_jpegcompression *); typedef int (*cam_reg_op) (struct gspca_dev *, struct v4l2_dbg_register *); typedef int (*cam_ident_op) (struct gspca_dev *, @@ -126,8 +128,8 @@ struct sd_desc { cam_v_op stopN; /* called on stream off - main alt */ cam_v_op stop0; /* called on stream off & disconnect - alt 0 */ cam_v_op dq_callback; /* called when a frame has been dequeued */ - cam_jpg_op get_jcomp; - cam_jpg_op set_jcomp; + cam_get_jpg_op get_jcomp; + cam_set_jpg_op set_jcomp; cam_qmnu_op querymenu; cam_streamparm_op get_streamparm; cam_streamparm_op set_streamparm; diff --git a/drivers/media/usb/gspca/jeilinj.c b/drivers/media/usb/gspca/jeilinj.c index 26b99310d628..b897aa86f315 100644 --- a/drivers/media/usb/gspca/jeilinj.c +++ b/drivers/media/usb/gspca/jeilinj.c @@ -474,7 +474,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) } static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) + const struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index c1a21bfb4be7..9aa09f845ce4 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -4762,7 +4762,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, } static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) + const struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c index a6055246cb9d..4cb511ccc5f6 100644 --- a/drivers/media/usb/gspca/topro.c +++ b/drivers/media/usb/gspca/topro.c @@ -4806,7 +4806,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev, } static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) + const struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index c47ba14c7619..77c57755e7b4 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -6883,16 +6883,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } static int sd_set_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) + const struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; - int ret; - ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); - if (ret) - return ret; - jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); - return 0; + return v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); } static int sd_get_jcomp(struct gspca_dev *gspca_dev, diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index a25513d484f7..2191f6ddf9e7 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1556,7 +1556,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, } static int vidioc_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jc) + const struct v4l2_jpegcompression *jc) { struct s2255_fh *fh = priv; struct s2255_channel *channel = fh->channel; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index c184ad30fbd8..f1dff3d09957 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1392,7 +1392,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, } static int vidioc_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *params) + const struct v4l2_jpegcompression *params) { if (params->quality != 50 || params->jpeg_markers != (V4L2_JPEG_MARKER_DHT | diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 73ae24a41cb1..21f624586d56 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -195,7 +195,7 @@ struct v4l2_ioctl_ops { int (*vidioc_g_jpegcomp) (struct file *file, void *fh, struct v4l2_jpegcompression *a); int (*vidioc_s_jpegcomp) (struct file *file, void *fh, - struct v4l2_jpegcompression *a); + const struct v4l2_jpegcompression *a); int (*vidioc_g_enc_index) (struct file *file, void *fh, struct v4l2_enc_idx *a); int (*vidioc_encoder_cmd) (struct file *file, void *fh, -- cgit v1.2.3 From 0e8025b9f6011a6bd69d01080d584bc95a89d02e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Sep 2012 11:59:31 -0300 Subject: [media] v4l2: make vidioc_s_audio const Write-only ioctls should have a const argument in the ioctl op. Do this conversion for vidioc_s_audio. Adding const for write-only ioctls was decided during the 2012 Media Workshop. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-driver.c | 4 ++-- drivers/media/pci/cx18/cx18-ioctl.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 2 +- drivers/media/pci/ivtv/ivtv-ioctl.c | 2 +- drivers/media/pci/saa7134/saa7134-video.c | 4 ++-- drivers/media/pci/saa7146/mxb.c | 2 +- drivers/media/pci/ttpci/av7110_v4l.c | 2 +- drivers/media/radio/radio-miropcm20.c | 2 +- drivers/media/radio/radio-sf16fmi.c | 2 +- drivers/media/radio/radio-tea5764.c | 2 +- drivers/media/radio/radio-timb.c | 2 +- drivers/media/radio/radio-wl1273.c | 2 +- drivers/media/radio/wl128x/fmdrv_v4l2.c | 2 +- drivers/media/usb/au0828/au0828-video.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/hdpvr/hdpvr-video.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 2 +- drivers/media/usb/tlg2300/pd-radio.c | 2 +- drivers/media/usb/tlg2300/pd-video.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 2 +- drivers/media/usb/usbvision/usbvision-video.c | 2 +- include/media/v4l2-ioctl.h | 2 +- 23 files changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 26bf309c41c2..31b282667463 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3076,7 +3076,7 @@ static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { if (unlikely(a->index)) return -EINVAL; @@ -3480,7 +3480,7 @@ static int radio_s_tuner(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { if (unlikely(a->index)) return -EINVAL; diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index e9912db3b496..ff315446d4ad 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -492,7 +492,7 @@ static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) return cx18_get_audio_input(cx, vin->index, vin); } -static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) +static int cx18_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout) { struct cx18 *cx = fh2id(fh)->cx; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 22f8e7fbd665..8c4a9a5f9a50 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1426,7 +1426,7 @@ static int vidioc_g_audinput(struct file *file, void *priv, } static int vidioc_s_audinput(struct file *file, void *priv, - struct v4l2_audio *i) + const struct v4l2_audio *i) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; if (i->index >= 2) diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 966abb407304..99e35dd3d83e 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -784,7 +784,7 @@ static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) return ivtv_get_audio_input(itv, vin->index, vin); } -static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) +static int ivtv_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout) { struct ivtv *itv = fh2id(fh)->itv; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index bac4386c64b9..135bfd8c28ad 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2089,7 +2089,7 @@ static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int saa7134_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int saa7134_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { return 0; } @@ -2373,7 +2373,7 @@ static int radio_g_audio(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index b520a45cb3f3..91369daad722 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -646,7 +646,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c index 1b2d15140a1d..730e906ea912 100644 --- a/drivers/media/pci/ttpci/av7110_v4l.c +++ b/drivers/media/pci/ttpci/av7110_v4l.c @@ -526,7 +526,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 87c1ee13b058..11f76ed4c6fb 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -197,7 +197,7 @@ static int vidioc_g_audio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return a->index ? -EINVAL : 0; } diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 8185d5fbfa89..227dcdb54df3 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -239,7 +239,7 @@ static int vidioc_g_audio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return a->index ? -EINVAL : 0; } diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 6b1fae32b483..efb05aa81d08 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -448,7 +448,7 @@ static int vidioc_g_audio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { if (a->index != 0) return -EINVAL; diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 09fc56052d35..5cf07779f4bb 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -85,7 +85,7 @@ static int timbradio_vidioc_g_audio(struct file *file, void *priv, } static int timbradio_vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return a->index ? -EINVAL : 0; } diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 71968a610734..2d93354fdcec 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1479,7 +1479,7 @@ static int wl1273_fm_vidioc_g_audio(struct file *file, void *priv, } static int wl1273_fm_vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *audio) + const struct v4l2_audio *audio) { struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index f816ea68e469..09585a99f02c 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -258,7 +258,7 @@ static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv, } static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *audio) + const struct v4l2_audio *audio) { if (audio->index != 0) return -EINVAL; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index fa0fa9ae91c7..870585570571 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1465,7 +1465,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { struct au0828_fh *fh = priv; struct au0828_dev *dev = fh->dev; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 790b28d7f764..fedf7852a355 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1253,7 +1253,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; @@ -2096,7 +2096,7 @@ static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) return 0; } -static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int radio_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 78d6ebd712b9..1e553d357380 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1352,7 +1352,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -2087,7 +2087,7 @@ static int radio_s_tuner(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *fh, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 0e9e156bb2aa..da6b77912222 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -677,7 +677,7 @@ static int vidioc_enumaudio(struct file *file, void *priv, } static int vidioc_s_audio(struct file *file, void *private_data, - struct v4l2_audio *audio) + const struct v4l2_audio *audio) { struct hdpvr_fh *fh = file->private_data; struct hdpvr_device *dev = fh->dev; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index f344aed32a93..7a445b0e725e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -333,7 +333,7 @@ static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) return 0; } -static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout) +static int pvr2_s_audio(struct file *file, void *priv, const struct v4l2_audio *vout) { if (vout->index) return -EINVAL; diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c index 4fad1dfb92cf..25eeb166aa0b 100644 --- a/drivers/media/usb/tlg2300/pd-radio.c +++ b/drivers/media/usb/tlg2300/pd-radio.c @@ -348,7 +348,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) { return vt->index > 0 ? -EINVAL : 0; } -static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va) +static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *va) { return (va->index != 0) ? -EINVAL : 0; } diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index bfbf9e56b0a4..1f448ac7a496 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -1029,7 +1029,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { return (0 == a->index) ? 0 : -EINVAL; } diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 45ed59cb2d04..4342cd4f5c8a 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1401,7 +1401,7 @@ static int radio_g_audio(struct file *file, void *priv, } static int radio_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) + const struct v4l2_audio *a) { return 0; } diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 8a4317979a43..f67018ed3795 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -684,7 +684,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) } static int vidioc_s_audio(struct file *file, void *fh, - struct v4l2_audio *a) + const struct v4l2_audio *a) { if (a->index) return -EINVAL; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 3eef4de0ca33..babbe09527cd 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -167,7 +167,7 @@ struct v4l2_ioctl_ops { int (*vidioc_g_audio) (struct file *file, void *fh, struct v4l2_audio *a); int (*vidioc_s_audio) (struct file *file, void *fh, - struct v4l2_audio *a); + const struct v4l2_audio *a); /* Audio out ioctls */ int (*vidioc_enumaudout) (struct file *file, void *fh, -- cgit v1.2.3 From 4f996594ceaf6c3f9bc42b40c40b0f7f87b79c86 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Sep 2012 05:10:48 -0300 Subject: [media] v4l2: make vidioc_s_crop const Write-only ioctls should have a const argument in the ioctl op. Do this conversion for vidioc_s_crop. Adding const for write-only ioctls was decided during the 2012 Media Workshop. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/mt9m001.c | 2 +- drivers/media/i2c/soc_camera/mt9m111.c | 2 +- drivers/media/i2c/soc_camera/mt9t031.c | 2 +- drivers/media/i2c/soc_camera/mt9t112.c | 4 +-- drivers/media/i2c/soc_camera/mt9v022.c | 2 +- drivers/media/i2c/soc_camera/ov5642.c | 20 +++++++------- drivers/media/i2c/soc_camera/ov6650.c | 32 +++++++++++----------- drivers/media/i2c/soc_camera/rj54n1cb0c.c | 4 +-- drivers/media/i2c/tvp5150.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 10 +++---- drivers/media/pci/cx18/cx18-ioctl.c | 2 +- drivers/media/pci/cx25821/cx25821-video.c | 2 +- drivers/media/pci/cx25821/cx25821-video.h | 2 +- drivers/media/pci/ivtv/ivtv-ioctl.c | 2 +- drivers/media/pci/saa7134/saa7134-video.c | 32 +++++++++++----------- drivers/media/pci/zoran/zoran_driver.c | 2 +- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpfe_capture.c | 2 +- drivers/media/platform/omap/omap_vout.c | 2 +- drivers/media/platform/s5p-fimc/fimc-m2m.c | 2 +- drivers/media/platform/s5p-g2d/g2d.c | 2 +- drivers/media/platform/sh_vou.c | 2 +- .../platform/soc_camera/sh_mobile_ceu_camera.c | 4 +-- drivers/media/platform/soc_camera/soc_camera.c | 6 ++-- drivers/media/platform/vino.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 2 +- include/media/soc_camera.h | 4 +-- include/media/v4l2-ioctl.h | 2 +- include/media/v4l2-subdev.h | 2 +- 30 files changed, 79 insertions(+), 79 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index d85be41ffa1d..19f8a07764f9 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -171,7 +171,7 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 938c5c390eec..62fd94af599b 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -383,7 +383,7 @@ static int mt9m111_reset(struct mt9m111 *mt9m111) return ret; } -static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9m111_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct v4l2_rect rect = a->c; struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index d74607adc585..40800b10a080 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -294,7 +294,7 @@ static int mt9t031_set_params(struct i2c_client *client, return ret < 0 ? ret : 0; } -static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9t031_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct v4l2_rect rect = a->c; struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 9ba428ede516..de7cd836b0a2 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -907,11 +907,11 @@ static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) return 0; } -static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9t112_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); - struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &a->c; return mt9t112_set_params(priv, rect, priv->format->code); } diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 350d0d854447..13057b966ee9 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -237,7 +237,7 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index d886c0b9ce44..8577e0cfb7fe 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -865,24 +865,24 @@ static int ov5642_g_chip_ident(struct v4l2_subdev *sd, return 0; } -static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int ov5642_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5642 *priv = to_ov5642(client); - struct v4l2_rect *rect = &a->c; + struct v4l2_rect rect = a->c; int ret; - v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1, - &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0); + v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, + &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); - priv->crop_rect.width = rect->width; - priv->crop_rect.height = rect->height; - priv->total_width = rect->width + BLANKING_EXTRA_WIDTH; - priv->total_height = max_t(int, rect->height + + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; + priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; + priv->total_height = max_t(int, rect.height + BLANKING_EXTRA_HEIGHT, BLANKING_MIN_HEIGHT); - priv->crop_rect.width = rect->width; - priv->crop_rect.height = rect->height; + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; ret = ov5642_write_array(client, ov5642_default_regs_init); if (!ret) diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index 65b031f333b7..e87feb0881e3 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -451,42 +451,42 @@ static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) return 0; } -static int ov6650_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int ov6650_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - struct v4l2_rect *rect = &a->c; + struct v4l2_rect rect = a->c; int ret; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - rect->left = ALIGN(rect->left, 2); - rect->width = ALIGN(rect->width, 2); - rect->top = ALIGN(rect->top, 2); - rect->height = ALIGN(rect->height, 2); - soc_camera_limit_side(&rect->left, &rect->width, + rect.left = ALIGN(rect.left, 2); + rect.width = ALIGN(rect.width, 2); + rect.top = ALIGN(rect.top, 2); + rect.height = ALIGN(rect.height, 2); + soc_camera_limit_side(&rect.left, &rect.width, DEF_HSTRT << 1, 2, W_CIF); - soc_camera_limit_side(&rect->top, &rect->height, + soc_camera_limit_side(&rect.top, &rect.height, DEF_VSTRT << 1, 2, H_CIF); - ret = ov6650_reg_write(client, REG_HSTRT, rect->left >> 1); + ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1); if (!ret) { - priv->rect.left = rect->left; + priv->rect.left = rect.left; ret = ov6650_reg_write(client, REG_HSTOP, - (rect->left + rect->width) >> 1); + (rect.left + rect.width) >> 1); } if (!ret) { - priv->rect.width = rect->width; - ret = ov6650_reg_write(client, REG_VSTRT, rect->top >> 1); + priv->rect.width = rect.width; + ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1); } if (!ret) { - priv->rect.top = rect->top; + priv->rect.top = rect.top; ret = ov6650_reg_write(client, REG_VSTOP, - (rect->top + rect->height) >> 1); + (rect.top + rect.height) >> 1); } if (!ret) - priv->rect.height = rect->height; + priv->rect.height = rect.height; return ret; } diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 32226c9024f9..02f0400051d9 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -536,11 +536,11 @@ static int rj54n1_commit(struct i2c_client *client) static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, s32 *out_w, s32 *out_h); -static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int rj54n1_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); - struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &a->c; int dummy = 0, output_w, output_h, input_w = rect->width, input_h = rect->height; int ret; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index a751b6c146fd..b5b1792479d0 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -865,7 +865,7 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, return 0; } -static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) { struct v4l2_rect rect = a->c; struct tvp5150 *decoder = to_tvp5150(sd); diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 31b282667463..16f5ca23698c 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2986,7 +2986,7 @@ static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop) return 0; } -static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop) +static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -3028,17 +3028,17 @@ static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop) } /* Min. scaled size 48 x 32. */ - c.rect.left = clamp(crop->c.left, b_left, b_right - 48); + c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48); c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY); - c.rect.width = clamp(crop->c.width, + c.rect.width = clamp_t(s32, crop->c.width, 48, b_right - c.rect.left); - c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32); + c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32); /* Top and height must be a multiple of two. */ c.rect.top = (c.rect.top + 1) & ~1; - c.rect.height = clamp(crop->c.height, + c.rect.height = clamp_t(s32, crop->c.height, 32, b_bottom - c.rect.top); c.rect.height = (c.rect.height + 1) & ~1; diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index ff315446d4ad..bb5073f72c42 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -527,7 +527,7 @@ static int cx18_cropcap(struct file *file, void *fh, return 0; } -static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +static int cx18_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index b38d4379cc36..0a80245165d0 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -1610,7 +1610,7 @@ int cx25821_vidioc_cropcap(struct file *file, void *priv, return 0; } -int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +int cx25821_vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; struct cx25821_fh *fh = priv; diff --git a/drivers/media/pci/cx25821/cx25821-video.h b/drivers/media/pci/cx25821/cx25821-video.h index 9652a5e35ba2..c265e35b37c3 100644 --- a/drivers/media/pci/cx25821/cx25821-video.h +++ b/drivers/media/pci/cx25821/cx25821-video.h @@ -177,7 +177,7 @@ extern int cx25821_set_control(struct cx25821_dev *dev, extern int cx25821_vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap); extern int cx25821_vidioc_s_crop(struct file *file, void *priv, - struct v4l2_crop *crop); + const struct v4l2_crop *crop); extern int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop); diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index d5cbb6177754..ed6dcc7e61bc 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -874,7 +874,7 @@ static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropca return 0; } -static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +static int ivtv_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) { struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 135bfd8c28ad..22f8758d047f 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1953,11 +1953,12 @@ static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop) return 0; } -static int saa7134_s_crop(struct file *file, void *f, struct v4l2_crop *crop) +static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) { struct saa7134_fh *fh = f; struct saa7134_dev *dev = fh->dev; struct v4l2_rect *b = &dev->crop_bounds; + struct v4l2_rect *c = &dev->crop_current; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) @@ -1972,21 +1973,20 @@ static int saa7134_s_crop(struct file *file, void *f, struct v4l2_crop *crop) if (res_locked(fh->dev, RESOURCE_VIDEO)) return -EBUSY; - if (crop->c.top < b->top) - crop->c.top = b->top; - if (crop->c.top > b->top + b->height) - crop->c.top = b->top + b->height; - if (crop->c.height > b->top - crop->c.top + b->height) - crop->c.height = b->top - crop->c.top + b->height; - - if (crop->c.left < b->left) - crop->c.left = b->left; - if (crop->c.left > b->left + b->width) - crop->c.left = b->left + b->width; - if (crop->c.width > b->left - crop->c.left + b->width) - crop->c.width = b->left - crop->c.left + b->width; - - dev->crop_current = crop->c; + *c = crop->c; + if (c->top < b->top) + c->top = b->top; + if (c->top > b->top + b->height) + c->top = b->top + b->height; + if (c->height > b->top - c->top + b->height) + c->height = b->top - c->top + b->height; + + if (c->left < b->left) + c->left = b->left; + if (c->left > b->left + b->width) + c->left = b->left + b->width; + if (c->width > b->left - c->left + b->width) + c->width = b->left - c->left + b->width; return 0; } diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 9ecd7d711f27..53f12c7466b0 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -2598,7 +2598,7 @@ gcrop_unlock_and_return: return res; } -static int zoran_s_crop(struct file *file, void *__fh, struct v4l2_crop *crop) +static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *crop) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 9a05c817462c..e712d6734ac8 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -629,7 +629,7 @@ static int vpbe_display_querycap(struct file *file, void *priv, } static int vpbe_display_s_crop(struct file *file, void *priv, - struct v4l2_crop *crop) + const struct v4l2_crop *crop) { struct vpbe_fh *fh = file->private_data; struct vpbe_layer *layer = fh->layer; diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index f99198cebd35..48052cbffc2b 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1666,7 +1666,7 @@ static int vpfe_g_crop(struct file *file, void *priv, } static int vpfe_s_crop(struct file *file, void *priv, - struct v4l2_crop *crop) + const struct v4l2_crop *crop) { struct vpfe_device *vpfe_dev = video_drvdata(file); int ret = 0; diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 92845f835607..36c3be85649d 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1291,7 +1291,7 @@ static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) return 0; } -static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) { int ret = -EINVAL; struct omap_vout_device *vout = fh; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index ab4c15acdc4c..c67e53bfa43a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -551,7 +551,7 @@ static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) return 0; } -static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) +static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr) { struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 30195ef5a803..69c9f22ee52a 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -526,7 +526,7 @@ static int vidioc_try_crop(struct file *file, void *prv, struct v4l2_crop *cr) return 0; } -static int vidioc_s_crop(struct file *file, void *prv, struct v4l2_crop *cr) +static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *cr) { struct g2d_ctx *ctx = prv; struct g2d_frame *f; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 9f62fd89ab57..00cd52c61fe0 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -933,7 +933,7 @@ static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) } /* Assume a dull encoder, do all the work ourselves. */ -static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) +static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) { struct video_device *vdev = video_devdata(file); struct sh_vou_device *vou_dev = video_get_drvdata(vdev); diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 0baaf94db7e0..0a24253dcda2 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1263,7 +1263,7 @@ static void update_subrect(struct sh_mobile_ceu_cam *cam) * 3. if (2) failed, try to request the maximum image */ static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, - struct v4l2_crop *cam_crop) + const struct v4l2_crop *cam_crop) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; @@ -1517,7 +1517,7 @@ static int client_scale(struct soc_camera_device *icd, * scaling and cropping algorithms and for the meaning of referenced here steps. */ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) + const struct v4l2_crop *a) { struct v4l2_rect *rect = &a->c; struct device *dev = icd->parent; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 10b57f8e7ec8..f6b1c1f87761 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -888,11 +888,11 @@ static int soc_camera_g_crop(struct file *file, void *fh, * retrieve it. */ static int soc_camera_s_crop(struct file *file, void *fh, - struct v4l2_crop *a) + const struct v4l2_crop *a) { struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &a->c; struct v4l2_crop current_crop; int ret; @@ -1289,7 +1289,7 @@ static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a) return v4l2_subdev_call(sd, video, g_crop, a); } -static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a) +static int default_s_crop(struct soc_camera_device *icd, const struct v4l2_crop *a) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); return v4l2_subdev_call(sd, video, s_crop, a); diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index aae1720b2f2d..790d96cffeea 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3284,7 +3284,7 @@ static int vino_g_crop(struct file *file, void *__fh, } static int vino_s_crop(struct file *file, void *__fh, - struct v4l2_crop *c) + const struct v4l2_crop *c) { struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 7a445b0e725e..db249cad3cd9 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -760,7 +760,7 @@ static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) return 0; } -static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +static int pvr2_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index f1dff3d09957..980371b02749 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1372,7 +1372,7 @@ static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) /* FIXME: vidioc_s_crop is not really implemented!!! */ -static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +static int vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) { if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 435e7b8ad1c3..6442edc2a151 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -85,14 +85,14 @@ struct soc_camera_host_ops { void (*put_formats)(struct soc_camera_device *); int (*cropcap)(struct soc_camera_device *, struct v4l2_cropcap *); int (*get_crop)(struct soc_camera_device *, struct v4l2_crop *); - int (*set_crop)(struct soc_camera_device *, struct v4l2_crop *); + int (*set_crop)(struct soc_camera_device *, const struct v4l2_crop *); int (*get_selection)(struct soc_camera_device *, struct v4l2_selection *); int (*set_selection)(struct soc_camera_device *, struct v4l2_selection *); /* * The difference to .set_crop() is, that .set_livecrop is not allowed * to change the output sizes */ - int (*set_livecrop)(struct soc_camera_device *, struct v4l2_crop *); + int (*set_livecrop)(struct soc_camera_device *, const struct v4l2_crop *); int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *); int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); void (*init_videobuf)(struct videobuf_queue *, diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index fbeb00e2c109..e48b571ca37d 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -186,7 +186,7 @@ struct v4l2_ioctl_ops { int (*vidioc_g_crop) (struct file *file, void *fh, struct v4l2_crop *a); int (*vidioc_s_crop) (struct file *file, void *fh, - struct v4l2_crop *a); + const struct v4l2_crop *a); int (*vidioc_g_selection) (struct file *file, void *fh, struct v4l2_selection *s); int (*vidioc_s_selection) (struct file *file, void *fh, diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index e698f2cead4e..2ecd7377153b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -286,7 +286,7 @@ struct v4l2_subdev_video_ops { int (*s_stream)(struct v4l2_subdev *sd, int enable); int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc); int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); - int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); + int (*s_crop)(struct v4l2_subdev *sd, const struct v4l2_crop *crop); int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); int (*g_frame_interval)(struct v4l2_subdev *sd, -- cgit v1.2.3 From 954f340fc7f2fa2ae8812670da49e828d2686d8e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Sep 2012 06:05:50 -0300 Subject: [media] Set vfl_dir for all display or m2m drivers Signed-off-by: Hans Verkuil Acked-by: Sylwester Nawrocki Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-streams.c | 3 +++ drivers/media/pci/zoran/zoran_card.c | 4 ++++ drivers/media/platform/coda.c | 1 + drivers/media/platform/davinci/vpbe_display.c | 1 + drivers/media/platform/davinci/vpif_display.c | 1 + drivers/media/platform/m2m-deinterlace.c | 1 + drivers/media/platform/mem2mem_testdev.c | 1 + drivers/media/platform/mx2_emmaprp.c | 1 + drivers/media/platform/omap/omap_vout.c | 1 + drivers/media/platform/omap3isp/ispvideo.c | 1 + drivers/media/platform/s5p-fimc/fimc-m2m.c | 1 + drivers/media/platform/s5p-g2d/g2d.c | 1 + drivers/media/platform/s5p-jpeg/jpeg-core.c | 1 + drivers/media/platform/s5p-mfc/s5p_mfc.c | 1 + drivers/media/platform/s5p-tv/mixer_video.c | 1 + drivers/media/platform/sh_vou.c | 1 + drivers/media/usb/uvc/uvc_driver.c | 2 ++ 17 files changed, 23 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c index 0ff264e0e0f6..7b8648a827f5 100644 --- a/drivers/media/pci/ivtv/ivtv-streams.c +++ b/drivers/media/pci/ivtv/ivtv-streams.c @@ -222,6 +222,9 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->num = num; s->vdev->v4l2_dev = &itv->v4l2_dev; + if (ivtv_stream_info[type].v4l2_caps & + (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT)) + s->vdev->vfl_dir = VFL_DIR_TX; s->vdev->fops = ivtv_stream_info[type].fops; s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; s->vdev->release = video_device_release; diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index c3602d6cd48e..fffc54b452c8 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -1055,6 +1055,10 @@ zr36057_init (struct zoran *zr) memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template)); zr->video_dev->parent = &zr->pci_dev->dev; strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); + /* It's not a mem2mem device, but you can both capture and output from + one and the same device. This should really be split up into two + device nodes, but that's a job for another day. */ + zr->video_dev->vfl_dir = VFL_DIR_M2M; err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]); if (err < 0) goto exit_free; diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 0d4b5c0b37ee..10eaf1189d89 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1639,6 +1639,7 @@ static void coda_fw_callback(const struct firmware *fw, void *context) dev->vfd.release = video_device_release_empty, dev->vfd.lock = &dev->dev_mutex; dev->vfd.v4l2_dev = &dev->v4l2_dev; + dev->vfd.vfl_dir = VFL_DIR_M2M; snprintf(dev->vfd.name, sizeof(dev->vfd.name), "%s", CODA_NAME); video_set_drvdata(&dev->vfd, dev); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index e712d6734ac8..239f37bfa313 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1633,6 +1633,7 @@ static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vbd->minor = -1; vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; vbd->lock = &vpbe_display_layer->opslock; + vbd->vfl_dir = VFL_DIR_TX; if (disp_dev->vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 4a24848c1a66..ff6e43293ce4 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1745,6 +1745,7 @@ static __init int vpif_probe(struct platform_device *pdev) *vfd = vpif_video_template; vfd->v4l2_dev = &vpif_obj.v4l2_dev; vfd->release = video_device_release; + vfd->vfl_dir = VFL_DIR_TX; snprintf(vfd->name, sizeof(vfd->name), "VPIF_Display_DRIVER_V%s", VPIF_DISPLAY_VERSION); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 9afd93075b61..c4ad9fca5dbf 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -980,6 +980,7 @@ static struct video_device deinterlace_videodev = { .ioctl_ops = &deinterlace_ioctl_ops, .minor = -1, .release = video_device_release, + .vfl_dir = VFL_DIR_M2M, }; static struct v4l2_m2m_ops m2m_ops = { diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index c1229bef153c..d03637537118 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -999,6 +999,7 @@ static const struct v4l2_file_operations m2mtest_fops = { static struct video_device m2mtest_videodev = { .name = MEM2MEM_NAME, + .vfl_dir = VFL_DIR_M2M, .fops = &m2mtest_fops, .ioctl_ops = &m2mtest_ioctl_ops, .minor = -1, diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 2236315778c3..8f22ce543cf7 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -877,6 +877,7 @@ static struct video_device emmaprp_videodev = { .ioctl_ops = &emmaprp_ioctl_ops, .minor = -1, .release = video_device_release, + .vfl_dir = VFL_DIR_M2M, }; static struct v4l2_m2m_ops m2m_ops = { diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 36c3be85649d..196e51670050 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1951,6 +1951,7 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) vfd->fops = &omap_vout_fops; vfd->v4l2_dev = &vout->vid_dev->v4l2_dev; + vfd->vfl_dir = VFL_DIR_TX; mutex_init(&vout->lock); vfd->minor = -1; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 3a5085e90024..c78f60a0220d 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1342,6 +1342,7 @@ int omap3isp_video_init(struct isp_video *video, const char *name) case V4L2_BUF_TYPE_VIDEO_OUTPUT: direction = "input"; video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->video.vfl_dir = VFL_DIR_TX; break; default: diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index c67e53bfa43a..9237e53cf6df 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -804,6 +804,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; + vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); video_set_drvdata(vfd, fimc); diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 69c9f22ee52a..1e3b9dd014c0 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -680,6 +680,7 @@ static struct video_device g2d_videodev = { .ioctl_ops = &g2d_ioctl_ops, .minor = -1, .release = video_device_release, + .vfl_dir = VFL_DIR_M2M, }; static struct v4l2_m2m_ops g2d_m2m_ops = { diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 90459cefb2b8..bf2d94bb0f6e 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1392,6 +1392,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_encoder->release = video_device_release; jpeg->vfd_encoder->lock = &jpeg->lock; jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev; + jpeg->vfd_encoder->vfl_dir = VFL_DIR_M2M; ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e3e616d8a09d..0476be4ee567 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1048,6 +1048,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->release = video_device_release, vfd->lock = &dev->mfc_mutex; vfd->v4l2_dev = &dev->v4l2_dev; + vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index a9c6be39246d..bd42ea301650 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -1081,6 +1081,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, .minor = -1, .release = mxr_vfd_release, .fops = &mxr_fops, + .vfl_dir = VFL_DIR_TX, .ioctl_ops = &mxr_ioctl_ops, }; strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name)); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 00cd52c61fe0..ba3de3e02d47 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1320,6 +1320,7 @@ static const struct video_device sh_vou_video_template = { .ioctl_ops = &sh_vou_ioctl_ops, .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */ .current_norm = V4L2_STD_NTSC_M, + .vfl_dir = VFL_DIR_TX, }; static int __devinit sh_vou_probe(struct platform_device *pdev) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 287f73182a69..5967081747ce 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1722,6 +1722,8 @@ static int uvc_register_video(struct uvc_device *dev, vdev->v4l2_dev = &dev->vdev; vdev->fops = &uvc_fops; vdev->release = uvc_release; + if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + vdev->vfl_dir = VFL_DIR_TX; strlcpy(vdev->name, dev->name, sizeof vdev->name); /* Set the driver data before calling video_register_device, otherwise -- cgit v1.2.3 From 51639be3b39b058d9f80cfd68c52887c7b96eb41 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 16 Sep 2012 22:26:56 -0300 Subject: [media] af9035: implement TUA9001 GPIOs correctly Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 65 ++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 17 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 7d599a999619..89a3bec5cb92 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -583,6 +583,52 @@ err: return ret; } +static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int ret; + u8 val; + + dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg); + + /* + * CEN always enabled by hardware wiring + * RESETN GPIOT3 + * RXEN GPIOT2 + */ + + switch (cmd) { + case TUA9001_CMD_RESETN: + if (arg) + val = 0x00; + else + val = 0x01; + + ret = af9035_wr_reg_mask(d, 0x00d8e7, val, 0x01); + if (ret < 0) + goto err; + break; + case TUA9001_CMD_RXEN: + if (arg) + val = 0x01; + else + val = 0x00; + + ret = af9035_wr_reg_mask(d, 0x00d8eb, val, 0x01); + if (ret < 0) + goto err; + break; + } + + return 0; + +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + + static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -655,6 +701,8 @@ static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) switch (state->af9033_config[0].tuner) { case AF9033_TUNER_FC0011: return af9035_fc0011_tuner_callback(d, cmd, arg); + case AF9033_TUNER_TUA9001: + return af9035_tua9001_tuner_callback(d, cmd, arg); default: break; } @@ -779,23 +827,6 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) if (ret < 0) goto err; - /* reset tuner */ - ret = af9035_wr_reg_mask(d, 0x00d8e7, 0x00, 0x01); - if (ret < 0) - goto err; - - usleep_range(2000, 20000); - - ret = af9035_wr_reg_mask(d, 0x00d8e7, 0x01, 0x01); - if (ret < 0) - goto err; - - /* activate tuner RX */ - /* TODO: use callback for TUA9001 RXEN */ - ret = af9035_wr_reg_mask(d, 0x00d8eb, 0x01, 0x01); - if (ret < 0) - goto err; - /* attach tuner */ fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap, &af9035_tua9001_config); -- cgit v1.2.3 From 254ee2e02132c84ca252129eb893c3ef8bb3b77e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 18 Sep 2012 10:27:05 -0300 Subject: [media] rtl28xxu: add ID [0bda:2832] Realtek RTL2832U reference design Also change location of other RTL2832 reference design ID 0bda:2838. I just like to see reference design IDs at the first IDs in the list. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c3e2602af81e..226ec542a703 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1325,14 +1325,16 @@ static const struct usb_device_id rtl28xxu_id_table[] = { { DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2, &rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) }, + { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2832, + &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, + { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, + &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1, &rtl2832u_props, "Terratec Cinergy T Stick Black", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT, &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK, &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) }, - { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, - &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0, &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101, -- cgit v1.2.3 From 1e8f31f31726148c27de1ff4692c76c9bcff9860 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 19 Jul 2012 21:10:36 -0300 Subject: [media] cxd2820r: use Kernel GPIO for GPIO access Currently there is LNA behind cxd2820r demodulator GPIO. Use Kernel GPIO interface to access those GPIOs. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cxd2820r.h | 14 ++-- drivers/media/dvb-frontends/cxd2820r_c.c | 5 -- drivers/media/dvb-frontends/cxd2820r_core.c | 108 ++++++++++++++++++++++------ drivers/media/dvb-frontends/cxd2820r_priv.h | 9 ++- drivers/media/dvb-frontends/cxd2820r_t.c | 5 -- drivers/media/dvb-frontends/cxd2820r_t2.c | 5 -- drivers/media/usb/dvb-usb-v2/anysee.c | 2 +- drivers/media/usb/em28xx/em28xx-dvb.c | 21 ++++-- 8 files changed, 114 insertions(+), 55 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h index 5aa306ebb7ef..6acc21c581c5 100644 --- a/drivers/media/dvb-frontends/cxd2820r.h +++ b/drivers/media/dvb-frontends/cxd2820r.h @@ -62,14 +62,6 @@ struct cxd2820r_config { * Values: 0, 1 */ bool spec_inv; - - /* GPIOs for all used modes. - * Default: none, disabled - * Values: - */ - u8 gpio_dvbt[3]; - u8 gpio_dvbt2[3]; - u8 gpio_dvbc[3]; }; @@ -77,12 +69,14 @@ struct cxd2820r_config { (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE)) extern struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, - struct i2c_adapter *i2c + struct i2c_adapter *i2c, + int *gpio_chip_base ); #else static inline struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, - struct i2c_adapter *i2c + struct i2c_adapter *i2c, + int *gpio_chip_base ) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c index d2a0c285840d..125a44041011 100644 --- a/drivers/media/dvb-frontends/cxd2820r_c.c +++ b/drivers/media/dvb-frontends/cxd2820r_c.c @@ -50,11 +50,6 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe) dev_dbg(&priv->i2c->dev, "%s: frequency=%d symbol_rate=%d\n", __func__, c->frequency, c->symbol_rate); - /* update GPIOs */ - ret = cxd2820r_gpio(fe); - if (ret) - goto error; - /* program tuner */ if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index a3656ba67d77..4bd42f20d5b3 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -168,30 +168,15 @@ int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, return cxd2820r_wr_reg(priv, reg, val); } -int cxd2820r_gpio(struct dvb_frontend *fe) +int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio) { struct cxd2820r_priv *priv = fe->demodulator_priv; int ret, i; - u8 *gpio, tmp0, tmp1; + u8 tmp0, tmp1; dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, fe->dtv_property_cache.delivery_system); - switch (fe->dtv_property_cache.delivery_system) { - case SYS_DVBT: - gpio = priv->cfg.gpio_dvbt; - break; - case SYS_DVBT2: - gpio = priv->cfg.gpio_dvbt2; - break; - case SYS_DVBC_ANNEX_AC: - gpio = priv->cfg.gpio_dvbc; - break; - default: - ret = -EINVAL; - goto error; - } - /* update GPIOs only when needed */ if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio))) return 0; @@ -582,9 +567,19 @@ static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe) static void cxd2820r_release(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + int ret; dev_dbg(&priv->i2c->dev, "%s\n", __func__); +#ifdef CONFIG_GPIOLIB + /* remove GPIOs */ + if (priv->gpio_chip.label) { + ret = gpiochip_remove(&priv->gpio_chip); + if (ret) + dev_err(&priv->i2c->dev, "%s: gpiochip_remove() " \ + "failed=%d\n", KBUILD_MODNAME, ret); + } +#endif kfree(priv); return; } @@ -599,6 +594,49 @@ static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1); } +#ifdef CONFIG_GPIOLIB +static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr, + int val) +{ + struct cxd2820r_priv *priv = + container_of(chip, struct cxd2820r_priv, gpio_chip); + u8 gpio[GPIO_COUNT]; + + dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val); + + memcpy(gpio, priv->gpio, sizeof(gpio)); + gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); + + return cxd2820r_gpio(&priv->fe, gpio); +} + +static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val) +{ + struct cxd2820r_priv *priv = + container_of(chip, struct cxd2820r_priv, gpio_chip); + u8 gpio[GPIO_COUNT]; + + dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val); + + memcpy(gpio, priv->gpio, sizeof(gpio)); + gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); + + (void) cxd2820r_gpio(&priv->fe, gpio); + + return; +} + +static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr) +{ + struct cxd2820r_priv *priv = + container_of(chip, struct cxd2820r_priv, gpio_chip); + + dev_dbg(&priv->i2c->dev, "%s: nr=%d\n", __func__, nr); + + return (priv->gpio[nr] >> 2) & 0x01; +} +#endif + static const struct dvb_frontend_ops cxd2820r_ops = { .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, /* default: DVB-T/T2 */ @@ -645,15 +683,20 @@ static const struct dvb_frontend_ops cxd2820r_ops = { }; struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, - struct i2c_adapter *i2c) + struct i2c_adapter *i2c, int *gpio_chip_base +) { - struct cxd2820r_priv *priv = NULL; + struct cxd2820r_priv *priv; int ret; u8 tmp; priv = kzalloc(sizeof (struct cxd2820r_priv), GFP_KERNEL); - if (!priv) + if (!priv) { + ret = -ENOMEM; + dev_err(&i2c->dev, "%s: kzalloc() failed\n", + KBUILD_MODNAME); goto error; + } priv->i2c = i2c; memcpy(&priv->cfg, cfg, sizeof (struct cxd2820r_config)); @@ -664,10 +707,35 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, if (ret || tmp != 0xe1) goto error; +#ifdef CONFIG_GPIOLIB + /* add GPIOs */ + if (gpio_chip_base) { + priv->gpio_chip.label = KBUILD_MODNAME; + priv->gpio_chip.dev = &priv->i2c->dev; + priv->gpio_chip.owner = THIS_MODULE; + priv->gpio_chip.direction_output = + cxd2820r_gpio_direction_output; + priv->gpio_chip.set = cxd2820r_gpio_set; + priv->gpio_chip.get = cxd2820r_gpio_get; + priv->gpio_chip.base = -1; /* dynamic allocation */ + priv->gpio_chip.ngpio = GPIO_COUNT; + priv->gpio_chip.can_sleep = 1; + ret = gpiochip_add(&priv->gpio_chip); + if (ret) + goto error; + + dev_dbg(&priv->i2c->dev, "%s: gpio_chip.base=%d\n", __func__, + priv->gpio_chip.base); + + *gpio_chip_base = priv->gpio_chip.base; + } +#endif + memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof (struct dvb_frontend_ops)); priv->fe.demodulator_priv = priv; return &priv->fe; error: + dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); kfree(priv); return NULL; } diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h index 9396492119ca..7ff5f60c83e1 100644 --- a/drivers/media/dvb-frontends/cxd2820r_priv.h +++ b/drivers/media/dvb-frontends/cxd2820r_priv.h @@ -26,6 +26,7 @@ #include "dvb_frontend.h" #include "dvb_math.h" #include "cxd2820r.h" +#include struct reg_val_mask { u32 reg; @@ -41,7 +42,11 @@ struct cxd2820r_priv { bool ber_running; u8 bank[2]; - u8 gpio[3]; +#define GPIO_COUNT 3 + u8 gpio[GPIO_COUNT]; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif fe_delivery_system_t delivery_system; bool last_tune_failed; /* for switch between T and T2 tune */ @@ -51,7 +56,7 @@ struct cxd2820r_priv { extern int cxd2820r_debug; -int cxd2820r_gpio(struct dvb_frontend *fe); +int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio); int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, u8 mask); diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c index af5890e4f837..fa184ca2dd68 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -74,11 +74,6 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) return -EINVAL; } - /* update GPIOs */ - ret = cxd2820r_gpio(fe); - if (ret) - goto error; - /* program tuner */ if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c index 653c56eb065b..e82d82a7a2eb 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -92,11 +92,6 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) return -EINVAL; } - /* update GPIOs */ - ret = cxd2820r_gpio(fe); - if (ret) - goto error; - /* program tuner */ if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index b430bcace0b8..6705d81f0cb2 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -874,7 +874,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) /* attach demod */ adap->fe[0] = dvb_attach(cxd2820r_attach, - &anysee_cxd2820r_config, &d->i2c_adap); + &anysee_cxd2820r_config, &d->i2c_adap, NULL); state->has_ci = true; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index a16531fa937a..34c5ea996031 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -28,6 +28,7 @@ #include #include #include "tuner-simple.h" +#include #include "lgdt330x.h" #include "lgdt3305.h" @@ -610,11 +611,6 @@ static struct tda10023_config em28xx_tda10023_config = { static struct cxd2820r_config em28xx_cxd2820r_config = { .i2c_address = (0xd8 >> 1), .ts_mode = CXD2820R_TS_SERIAL, - - /* enable LNA for DVB-T, DVB-T2 and DVB-C */ - .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, - .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, - .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, }; static struct tda18271_config em28xx_cxd2820r_tda18271_config = { @@ -813,7 +809,7 @@ static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) static int em28xx_dvb_init(struct em28xx *dev) { - int result = 0, mfe_shared = 0; + int result = 0, mfe_shared = 0, gpio_chip_base; struct em28xx_dvb *dvb; if (!dev->board.has_dvb) { @@ -961,7 +957,8 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM28174_BOARD_PCTV_290E: dvb->fe[0] = dvb_attach(cxd2820r_attach, &em28xx_cxd2820r_config, - &dev->i2c_adap); + &dev->i2c_adap, + &gpio_chip_base); if (dvb->fe[0]) { /* FE 0 attach tuner */ if (!dvb_attach(tda18271_attach, @@ -975,6 +972,16 @@ static int em28xx_dvb_init(struct em28xx *dev) goto out_free; } } + + /* enable LNA for DVB-T, DVB-T2 and DVB-C */ + result = gpio_request_one(gpio_chip_base, GPIOF_INIT_LOW, + "LNA"); + if (result) + em28xx_errdev("gpio request failed %d\n", result); + else + gpio_free(gpio_chip_base); + + result = 0; /* continue even set LNA fails */ break; case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: { -- cgit v1.2.3 From 6332059e8d4e05ea0e7c813a50f1243dd17dffc0 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 11 Sep 2012 03:31:22 +0300 Subject: [media] dvb_usb_v2: rename module dvb_usbv2 => dvb_usb_v2 I think it is better name. At that phase renaming is quite painless as module is not yet merged to mainline Kernel. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile index 58027fd43a08..b76f58e6c64f 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -1,5 +1,5 @@ -dvb_usbv2-objs := dvb_usb_core.o dvb_usb_urb.o usb_urb.o -obj-$(CONFIG_DVB_USB_V2) += dvb_usbv2.o +dvb_usb_v2-objs := dvb_usb_core.o dvb_usb_urb.o usb_urb.o +obj-$(CONFIG_DVB_USB_V2) += dvb_usb_v2.o dvb_usb_cypress_firmware-objs := cypress_firmware.o obj-$(CONFIG_DVB_USB_CYPRESS_FIRMWARE) += dvb_usb_cypress_firmware.o -- cgit v1.2.3 From 0fe688f1383917ab90747fc41e11b393f33c97e8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 10 Sep 2012 22:04:43 -0300 Subject: [media] dvb_usb_v2: call streaming_ctrl() before kill urbs Logically it is better ask hardware to stop streaming before killing urbs carrying stream. Earlier it was just opposite. Now code runs: * submit urbs * start streaming ** streaming ongoing ** * stop streaming * kill urbs Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index e2d73e1a7ae0..f99015937957 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -265,7 +265,6 @@ static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, /* stop feeding if it is last pid */ if (adap->feed_count == 0) { dev_dbg(&d->udev->dev, "%s: stop feeding\n", __func__); - usb_urb_killv2(&adap->stream); if (d->props->streaming_ctrl) { ret = d->props->streaming_ctrl( @@ -274,9 +273,11 @@ static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, dev_err(&d->udev->dev, "%s: streaming_ctrl() " \ "failed=%d\n", KBUILD_MODNAME, ret); + usb_urb_killv2(&adap->stream); goto err_mutex_unlock; } } + usb_urb_killv2(&adap->stream); mutex_unlock(&adap->sync_mutex); } -- cgit v1.2.3 From 4395e4b76d550242f83eea4c461aacea66ddd385 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 12 Sep 2012 12:19:06 -0300 Subject: [media] af9035: declare MODULE_FIRMWARE Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 6 ++++-- drivers/media/usb/dvb-usb-v2/af9035.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 89a3bec5cb92..84b3b27db979 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1033,7 +1033,7 @@ static const struct dvb_usb_device_properties af9035_props = { .generic_bulk_ctrl_endpoint_response = 0x81, .identify_state = af9035_identify_state, - .firmware = "dvb-usb-af9035-02.fw", + .firmware = AF9035_FIRMWARE_AF9035, .download_firmware = af9035_download_firmware, .i2c_algo = &af9035_i2c_algo, @@ -1063,7 +1063,7 @@ static const struct dvb_usb_device_properties it9135_props = { .generic_bulk_ctrl_endpoint_response = 0x81, .identify_state = af9035_identify_state, - .firmware = "dvb-usb-it9135-01.fw", + .firmware = AF9035_FIRMWARE_IT9135, .download_firmware = af9035_download_firmware_it9135, .i2c_algo = &af9035_i2c_algo, @@ -1127,3 +1127,5 @@ module_usb_driver(af9035_usb_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Afatech AF9035 driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(AF9035_FIRMWARE_AF9035); +MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135); diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index 59ff69ede0f0..de8e761bb203 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -84,6 +84,9 @@ u32 clock_lut_it9135[] = { 22000000, /* 22.00 MHz */ }; +#define AF9035_FIRMWARE_AF9035 "dvb-usb-af9035-02.fw" +#define AF9035_FIRMWARE_IT9135 "dvb-usb-it9135-01.fw" + /* EEPROM locations */ #define EEPROM_IR_MODE 0x430d #define EEPROM_DUAL_MODE 0x4326 -- cgit v1.2.3 From ef37be1b4dd4d6fb4d4d9aae21af1ba616205322 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 17 Sep 2012 14:01:35 -0300 Subject: [media] rtl28xxu: move rtl2832u tuner probing to .read_config() Move rtl2832u tuner probing correct place. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 351 ++++++++++++++++---------------- drivers/media/usb/dvb-usb-v2/rtl28xxu.h | 1 + 2 files changed, 175 insertions(+), 177 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 226ec542a703..5eabeacccbc1 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -254,6 +254,156 @@ static struct i2c_algorithm rtl28xxu_i2c_algo = { .functionality = rtl28xxu_i2c_func, }; +static int rtl2832u_read_config(struct dvb_usb_device *d) +{ + struct rtl28xxu_priv *priv = d_to_priv(d); + int ret; + u8 buf[2], val; + /* open RTL2832U/RTL2832 I2C gate */ + struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"}; + /* close RTL2832U/RTL2832 I2C gate */ + struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"}; + /* tuner probes */ + struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf}; + struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf}; + + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + ret = rtl28xx_rd_reg(d, SYS_GPIO_DIR, &val); + if (ret) + goto err; + + val &= 0xbf; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, val); + if (ret) + goto err; + + /* enable as output GPIO3 and GPIO6 */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_EN, &val); + if (ret) + goto err; + + val |= 0x48; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, val); + if (ret) + goto err; + + /* + * Probe used tuner. We need to know used tuner before demod attach + * since there is some demod params needed to set according to tuner. + */ + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate_open); + if (ret) + goto err; + + priv->tuner_name = "NONE"; + + /* check FC0012 ID register; reg=00 val=a1 */ + ret = rtl28xxu_ctrl_msg(d, &req_fc0012); + if (ret == 0 && buf[0] == 0xa1) { + priv->tuner = TUNER_RTL2832_FC0012; + priv->tuner_name = "FC0012"; + goto found; + } + + /* check FC0013 ID register; reg=00 val=a3 */ + ret = rtl28xxu_ctrl_msg(d, &req_fc0013); + if (ret == 0 && buf[0] == 0xa3) { + priv->tuner = TUNER_RTL2832_FC0013; + priv->tuner_name = "FC0013"; + goto found; + } + + /* check MT2266 ID register; reg=00 val=85 */ + ret = rtl28xxu_ctrl_msg(d, &req_mt2266); + if (ret == 0 && buf[0] == 0x85) { + priv->tuner = TUNER_RTL2832_MT2266; + priv->tuner_name = "MT2266"; + goto found; + } + + /* check FC2580 ID register; reg=01 val=56 */ + ret = rtl28xxu_ctrl_msg(d, &req_fc2580); + if (ret == 0 && buf[0] == 0x56) { + priv->tuner = TUNER_RTL2832_FC2580; + priv->tuner_name = "FC2580"; + goto found; + } + + /* check MT2063 ID register; reg=00 val=9e || 9c */ + ret = rtl28xxu_ctrl_msg(d, &req_mt2063); + if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) { + priv->tuner = TUNER_RTL2832_MT2063; + priv->tuner_name = "MT2063"; + goto found; + } + + /* check MAX3543 ID register; reg=00 val=38 */ + ret = rtl28xxu_ctrl_msg(d, &req_max3543); + if (ret == 0 && buf[0] == 0x38) { + priv->tuner = TUNER_RTL2832_MAX3543; + priv->tuner_name = "MAX3543"; + goto found; + } + + /* check TUA9001 ID register; reg=7e val=2328 */ + ret = rtl28xxu_ctrl_msg(d, &req_tua9001); + if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) { + priv->tuner = TUNER_RTL2832_TUA9001; + priv->tuner_name = "TUA9001"; + goto found; + } + + /* check MXL5007R ID register; reg=d9 val=14 */ + ret = rtl28xxu_ctrl_msg(d, &req_mxl5007t); + if (ret == 0 && buf[0] == 0x14) { + priv->tuner = TUNER_RTL2832_MXL5007T; + priv->tuner_name = "MXL5007T"; + goto found; + } + + /* check E4000 ID register; reg=02 val=40 */ + ret = rtl28xxu_ctrl_msg(d, &req_e4000); + if (ret == 0 && buf[0] == 0x40) { + priv->tuner = TUNER_RTL2832_E4000; + priv->tuner_name = "E4000"; + goto found; + } + + /* check TDA18272 ID register; reg=00 val=c760 */ + ret = rtl28xxu_ctrl_msg(d, &req_tda18272); + if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) { + priv->tuner = TUNER_RTL2832_TDA18272; + priv->tuner_name = "TDA18272"; + goto found; + } + +found: + dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name); + + /* close demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate_close); + if (ret < 0) + goto err; + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = { .i2c_addr = 0x10, /* 0x20 */ .xtal = 28800000, @@ -537,199 +687,45 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); struct rtl28xxu_priv *priv = d_to_priv(d); struct rtl2832_config *rtl2832_config; - u8 buf[2], val; - /* open RTL2832U/RTL2832 I2C gate */ - struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"}; - /* close RTL2832U/RTL2832 I2C gate */ - struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"}; - /* for FC0012 tuner probe */ - struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf}; - /* for FC0013 tuner probe */ - struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf}; - /* for MT2266 tuner probe */ - struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf}; - /* for FC2580 tuner probe */ - struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf}; - /* for MT2063 tuner probe */ - struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf}; - /* for MAX3543 tuner probe */ - struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf}; - /* for TUA9001 tuner probe */ - struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf}; - /* for MXL5007T tuner probe */ - struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf}; - /* for E4000 tuner probe */ - struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf}; - /* for TDA18272 tuner probe */ - struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf}; dev_dbg(&d->udev->dev, "%s:\n", __func__); - ret = rtl28xx_rd_reg(d, SYS_GPIO_DIR, &val); - if (ret) - goto err; - - val &= 0xbf; - - ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, val); - if (ret) - goto err; - - /* enable as output GPIO3 and GPIO6*/ - ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_EN, &val); - if (ret) - goto err; - - val |= 0x48; - - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, val); - if (ret) - goto err; - - /* - * Probe used tuner. We need to know used tuner before demod attach - * since there is some demod params needed to set according to tuner. - */ - - /* open demod I2C gate */ - ret = rtl28xxu_ctrl_msg(d, &req_gate_open); - if (ret) - goto err; - - priv->tuner = TUNER_NONE; - - /* check FC0012 ID register; reg=00 val=a1 */ - ret = rtl28xxu_ctrl_msg(d, &req_fc0012); - if (ret == 0 && buf[0] == 0xa1) { - priv->tuner = TUNER_RTL2832_FC0012; + switch (priv->tuner) { + case TUNER_RTL2832_FC0012: rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; - dev_info(&d->udev->dev, "%s: FC0012 tuner found", - KBUILD_MODNAME); - goto found; - } - - /* check FC0013 ID register; reg=00 val=a3 */ - ret = rtl28xxu_ctrl_msg(d, &req_fc0013); - if (ret == 0 && buf[0] == 0xa3) { - priv->tuner = TUNER_RTL2832_FC0013; + break; + case TUNER_RTL2832_FC0013: rtl2832_config = &rtl28xxu_rtl2832_fc0013_config; - dev_info(&d->udev->dev, "%s: FC0013 tuner found", - KBUILD_MODNAME); - goto found; - } - - /* check MT2266 ID register; reg=00 val=85 */ - ret = rtl28xxu_ctrl_msg(d, &req_mt2266); - if (ret == 0 && buf[0] == 0x85) { - priv->tuner = TUNER_RTL2832_MT2266; - /* TODO implement tuner */ - dev_info(&d->udev->dev, "%s: MT2266 tuner found", - KBUILD_MODNAME); - goto unsupported; - } - - /* check FC2580 ID register; reg=01 val=56 */ - ret = rtl28xxu_ctrl_msg(d, &req_fc2580); - if (ret == 0 && buf[0] == 0x56) { - priv->tuner = TUNER_RTL2832_FC2580; + break; + case TUNER_RTL2832_FC2580: /* FIXME: do not abuse fc0012 settings */ rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; - dev_info(&d->udev->dev, "%s: FC2580 tuner found", - KBUILD_MODNAME); - goto found; - } - - /* check MT2063 ID register; reg=00 val=9e || 9c */ - ret = rtl28xxu_ctrl_msg(d, &req_mt2063); - if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) { - priv->tuner = TUNER_RTL2832_MT2063; - /* TODO implement tuner */ - dev_info(&d->udev->dev, "%s: MT2063 tuner found", - KBUILD_MODNAME); - goto unsupported; - } - - /* check MAX3543 ID register; reg=00 val=38 */ - ret = rtl28xxu_ctrl_msg(d, &req_max3543); - if (ret == 0 && buf[0] == 0x38) { - priv->tuner = TUNER_RTL2832_MAX3543; - /* TODO implement tuner */ - dev_info(&d->udev->dev, "%s: MAX3534 tuner found", - KBUILD_MODNAME); - goto unsupported; - } - - /* check TUA9001 ID register; reg=7e val=2328 */ - ret = rtl28xxu_ctrl_msg(d, &req_tua9001); - if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) { - priv->tuner = TUNER_RTL2832_TUA9001; + break; + case TUNER_RTL2832_TUA9001: rtl2832_config = &rtl28xxu_rtl2832_tua9001_config; - dev_info(&d->udev->dev, "%s: TUA9001 tuner found", - KBUILD_MODNAME); - goto found; - } - - /* check MXL5007R ID register; reg=d9 val=14 */ - ret = rtl28xxu_ctrl_msg(d, &req_mxl5007t); - if (ret == 0 && buf[0] == 0x14) { - priv->tuner = TUNER_RTL2832_MXL5007T; - /* TODO implement tuner */ - dev_info(&d->udev->dev, "%s: MXL5007T tuner found", - KBUILD_MODNAME); - goto unsupported; - } - - /* check E4000 ID register; reg=02 val=40 */ - ret = rtl28xxu_ctrl_msg(d, &req_e4000); - if (ret == 0 && buf[0] == 0x40) { - priv->tuner = TUNER_RTL2832_E4000; + break; + case TUNER_RTL2832_E4000: /* FIXME: do not abuse fc0012 settings */ rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; - dev_info(&d->udev->dev, "%s: E4000 tuner found", - KBUILD_MODNAME); - goto found; - } - - /* check TDA18272 ID register; reg=00 val=c760 */ - ret = rtl28xxu_ctrl_msg(d, &req_tda18272); - if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) { - priv->tuner = TUNER_RTL2832_TDA18272; - /* TODO implement tuner */ - dev_info(&d->udev->dev, "%s: TDA18272 tuner found", - KBUILD_MODNAME); - goto unsupported; - } - -unsupported: - /* close demod I2C gate */ - ret = rtl28xxu_ctrl_msg(d, &req_gate_close); - if (ret) - goto err; - - /* tuner not found */ - dev_dbg(&d->udev->dev, "%s: No compatible tuner found\n", __func__); - ret = -ENODEV; - return ret; - -found: - /* close demod I2C gate */ - ret = rtl28xxu_ctrl_msg(d, &req_gate_close); - if (ret) + break; + default: + dev_err(&d->udev->dev, "%s: unknown tuner=%s\n", + KBUILD_MODNAME, priv->tuner_name); + ret = -ENODEV; goto err; + } /* attach demodulator */ - adap->fe[0] = dvb_attach(rtl2832_attach, rtl2832_config, - &d->i2c_adap); - if (adap->fe[0] == NULL) { - ret = -ENODEV; - goto err; - } + adap->fe[0] = dvb_attach(rtl2832_attach, rtl2832_config, &d->i2c_adap); + if (!adap->fe[0]) { + ret = -ENODEV; + goto err; + } - /* set fe callbacks */ + /* set fe callback */ adap->fe[0]->callback = rtl2832u_frontend_callback; - return ret; - + return 0; err: dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; @@ -1304,6 +1300,7 @@ static const struct dvb_usb_device_properties rtl2832u_props = { .power_ctrl = rtl2832u_power_ctrl, .i2c_algo = &rtl28xxu_i2c_algo, + .read_config = rtl2832u_read_config, .frontend_attach = rtl2832u_frontend_attach, .tuner_attach = rtl2832u_tuner_attach, .init = rtl28xxu_init, diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index c6c8a4fe0b32..2f3af2d3b6ce 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -53,6 +53,7 @@ struct rtl28xxu_priv { u8 chip_id; u8 tuner; + char *tuner_name; u8 page; /* integrated demod active register page */ bool rc_active; }; -- cgit v1.2.3 From 4005c1a9f6c40b0b0de2b8ac80a6f53f23b52f6e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 17 Sep 2012 15:43:57 -0300 Subject: [media] rtl28xxu: masked reg write Implement masked register write and use it. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 83 ++++++++++++++------------------- 1 file changed, 36 insertions(+), 47 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 5eabeacccbc1..3b98ad4cd0a6 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -130,6 +130,26 @@ static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val) return rtl2831_rd_regs(d, reg, val, 1); } +static int rtl28xx_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = rtl28xx_rd_reg(d, reg, &tmp); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return rtl28xx_wr_reg(d, reg, val); +} + /* I2C */ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) @@ -258,7 +278,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) { struct rtl28xxu_priv *priv = d_to_priv(d); int ret; - u8 buf[2], val; + u8 buf[2]; /* open RTL2832U/RTL2832 I2C gate */ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"}; /* close RTL2832U/RTL2832 I2C gate */ @@ -277,24 +297,12 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) dev_dbg(&d->udev->dev, "%s:\n", __func__); - ret = rtl28xx_rd_reg(d, SYS_GPIO_DIR, &val); + /* enable GPIO3 and GPIO6 as output */ + ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x40); if (ret) goto err; - val &= 0xbf; - - ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, val); - if (ret) - goto err; - - /* enable as output GPIO3 and GPIO6 */ - ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_EN, &val); - if (ret) - goto err; - - val |= 0x48; - - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, val); + ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x48, 0x48); if (ret) goto err; @@ -611,29 +619,25 @@ static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, * RXEN GPIO1 */ - ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); - if (ret < 0) - goto err; - switch (cmd) { case TUA9001_CMD_RESETN: if (arg) - val |= (1 << 4); + val = (1 << 4); else - val &= ~(1 << 4); + val = (0 << 4); - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); - if (ret < 0) + ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x10); + if (ret) goto err; break; case TUA9001_CMD_RXEN: if (arg) - val |= (1 << 1); + val = (1 << 1); else - val &= ~(1 << 1); + val = (0 << 1); - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); - if (ret < 0) + ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x02); + if (ret) goto err; break; } @@ -821,7 +825,6 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); struct rtl28xxu_priv *priv = d_to_priv(d); struct dvb_frontend *fe; - u8 val; dev_dbg(&d->udev->dev, "%s:\n", __func__); @@ -854,26 +857,12 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) break; case TUNER_RTL2832_TUA9001: /* enable GPIO1 and GPIO4 as output */ - ret = rtl28xx_rd_reg(d, SYS_GPIO_DIR, &val); - if (ret < 0) - goto err; - - val &= ~(1 << 1); - val &= ~(1 << 4); - - ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, val); - if (ret < 0) - goto err; - - ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_EN, &val); - if (ret < 0) + ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x12); + if (ret) goto err; - val |= (1 << 1); - val |= (1 << 4); - - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, val); - if (ret < 0) + ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x12, 0x12); + if (ret) goto err; fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap, -- cgit v1.2.3 From 97efe7811be6634d6b520a9cdd9f723369af9216 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 17 Sep 2012 15:49:54 -0300 Subject: [media] rtl28xxu: do not return error for unimplemented fe callback Use of frontend callback is highly hardware design dependent and whole callback could be optional in many cases. Returning error by default when callback is not implemented is stupid. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 3b98ad4cd0a6..757abee4c2cc 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -591,7 +591,6 @@ static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, goto err; } return 0; - err: dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; @@ -643,7 +642,6 @@ static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, } return 0; - err: dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; @@ -656,17 +654,15 @@ static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) switch (priv->tuner) { case TUNER_RTL2832_FC0012: return rtl2832u_fc0012_tuner_callback(d, cmd, arg); - case TUNER_RTL2832_FC0013: return rtl2832u_fc0013_tuner_callback(d, cmd, arg); - case TUNER_RTL2832_TUA9001: return rtl2832u_tua9001_tuner_callback(d, cmd, arg); default: break; } - return -ENODEV; + return 0; } static int rtl2832u_frontend_callback(void *adapter_priv, int component, @@ -682,7 +678,7 @@ static int rtl2832u_frontend_callback(void *adapter_priv, int component, break; } - return -EINVAL; + return 0; } static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) -- cgit v1.2.3 From 81347f5a2dda0f4dffdaf5ec7f93f9d6d398ef8c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 17 Sep 2012 16:50:36 -0300 Subject: [media] rtl28xxu: move rtl2831u tuner probing to .read_config() Move rtl2831u tuner probing correct place. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 175 ++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 76 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 757abee4c2cc..4d8382a0ee8a 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -274,6 +274,87 @@ static struct i2c_algorithm rtl28xxu_i2c_algo = { .functionality = rtl28xxu_i2c_func, }; +static int rtl2831u_read_config(struct dvb_usb_device *d) +{ + struct rtl28xxu_priv *priv = d_to_priv(d); + int ret; + u8 buf[1]; + /* open RTL2831U/RTL2830 I2C gate */ + struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x08"}; + /* tuner probes */ + struct rtl28xxu_req req_mt2060 = {0x00c0, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_qt1010 = {0x0fc4, CMD_I2C_RD, 1, buf}; + + dev_dbg(&d->udev->dev, "%s:\n", __func__); + + /* + * RTL2831U GPIOs + * ========================================================= + * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?) + * GPIO2 | LED | 0 off | 1 on | + * GPIO4 | tuner#1 | 0 on | 1 off | MT2060 + */ + + /* GPIO direction */ + ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, 0x0a); + if (ret) + goto err; + + /* enable as output GPIO0, GPIO2, GPIO4 */ + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, 0x15); + if (ret) + goto err; + + /* + * Probe used tuner. We need to know used tuner before demod attach + * since there is some demod params needed to set according to tuner. + */ + + /* demod needs some time to wake up */ + msleep(20); + + priv->tuner_name = "NONE"; + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate_open); + if (ret) + goto err; + + /* check QT1010 ID(?) register; reg=0f val=2c */ + ret = rtl28xxu_ctrl_msg(d, &req_qt1010); + if (ret == 0 && buf[0] == 0x2c) { + priv->tuner = TUNER_RTL2830_QT1010; + priv->tuner_name = "QT1010"; + goto found; + } + + /* open demod I2C gate */ + ret = rtl28xxu_ctrl_msg(d, &req_gate_open); + if (ret) + goto err; + + /* check MT2060 ID register; reg=00 val=63 */ + ret = rtl28xxu_ctrl_msg(d, &req_mt2060); + if (ret == 0 && buf[0] == 0x63) { + priv->tuner = TUNER_RTL2830_MT2060; + priv->tuner_name = "MT2060"; + goto found; + } + + /* assume MXL5005S */ + priv->tuner = TUNER_RTL2830_MXL5005S; + priv->tuner_name = "MXL5005S"; + goto found; + +found: + dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name); + + return 0; +err: + dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + static int rtl2832u_read_config(struct dvb_usb_device *d) { struct rtl28xxu_priv *priv = d_to_priv(d); @@ -445,97 +526,38 @@ static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = { static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap) { - int ret; struct dvb_usb_device *d = adap_to_d(adap); struct rtl28xxu_priv *priv = d_to_priv(d); - u8 buf[1]; struct rtl2830_config *rtl2830_config; - /* open RTL2831U/RTL2830 I2C gate */ - struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" }; - /* for MT2060 tuner probe */ - struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf }; - /* for QT1010 tuner probe */ - struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf }; + int ret; dev_dbg(&d->udev->dev, "%s:\n", __func__); - /* - * RTL2831U GPIOs - * ========================================================= - * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?) - * GPIO2 | LED | 0 off | 1 on | - * GPIO4 | tuner#1 | 0 on | 1 off | MT2060 - */ - - /* GPIO direction */ - ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, 0x0a); - if (ret) - goto err; - - /* enable as output GPIO0, GPIO2, GPIO4 */ - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, 0x15); - if (ret) - goto err; - - /* - * Probe used tuner. We need to know used tuner before demod attach - * since there is some demod params needed to set according to tuner. - */ - - /* demod needs some time to wake up */ - msleep(20); - - /* open demod I2C gate */ - ret = rtl28xxu_ctrl_msg(d, &req_gate); - if (ret) - goto err; - - /* check QT1010 ID(?) register; reg=0f val=2c */ - ret = rtl28xxu_ctrl_msg(d, &req_qt1010); - if (ret == 0 && buf[0] == 0x2c) { - priv->tuner = TUNER_RTL2830_QT1010; + switch (priv->tuner) { + case TUNER_RTL2830_QT1010: rtl2830_config = &rtl28xxu_rtl2830_qt1010_config; - dev_dbg(&d->udev->dev, "%s: QT1010\n", __func__); - goto found; - } else { - dev_dbg(&d->udev->dev, "%s: QT1010 probe failed=%d - %02x\n", - __func__, ret, buf[0]); - } - - /* open demod I2C gate */ - ret = rtl28xxu_ctrl_msg(d, &req_gate); - if (ret) - goto err; - - /* check MT2060 ID register; reg=00 val=63 */ - ret = rtl28xxu_ctrl_msg(d, &req_mt2060); - if (ret == 0 && buf[0] == 0x63) { - priv->tuner = TUNER_RTL2830_MT2060; + break; + case TUNER_RTL2830_MT2060: rtl2830_config = &rtl28xxu_rtl2830_mt2060_config; - dev_dbg(&d->udev->dev, "%s: MT2060\n", __func__); - goto found; - } else { - dev_dbg(&d->udev->dev, "%s: MT2060 probe failed=%d - %02x\n", - __func__, ret, buf[0]); + break; + case TUNER_RTL2830_MXL5005S: + rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config; + break; + default: + dev_err(&d->udev->dev, "%s: unknown tuner=%s\n", + KBUILD_MODNAME, priv->tuner_name); + ret = -ENODEV; + goto err; } - /* assume MXL5005S */ - ret = 0; - priv->tuner = TUNER_RTL2830_MXL5005S; - rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config; - dev_dbg(&d->udev->dev, "%s: MXL5005S\n", __func__); - goto found; - -found: /* attach demodulator */ - adap->fe[0] = dvb_attach(rtl2830_attach, rtl2830_config, - &d->i2c_adap); - if (adap->fe[0] == NULL) { + adap->fe[0] = dvb_attach(rtl2830_attach, rtl2830_config, &d->i2c_adap); + if (!adap->fe[0]) { ret = -ENODEV; goto err; } - return ret; + return 0; err: dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return ret; @@ -1264,6 +1286,7 @@ static const struct dvb_usb_device_properties rtl2831u_props = { .power_ctrl = rtl2831u_power_ctrl, .i2c_algo = &rtl28xxu_i2c_algo, + .read_config = rtl2831u_read_config, .frontend_attach = rtl2831u_frontend_attach, .tuner_attach = rtl2831u_tuner_attach, .init = rtl28xxu_init, -- cgit v1.2.3 From 8acc91cd68e8493ce892c39d9f94afd8bcf9be67 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 17 Sep 2012 16:55:39 -0300 Subject: [media] rtl28xxu: remove fc0013 tuner fe callback It is just stub implementation, remove it. Also add debug for beginning of fe callback. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 4d8382a0ee8a..f17e73baccff 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -618,14 +618,6 @@ err: return ret; } - -static int rtl2832u_fc0013_tuner_callback(struct dvb_usb_device *d, - int cmd, int arg) -{ - /* TODO implement*/ - return 0; -} - static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -676,8 +668,6 @@ static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) switch (priv->tuner) { case TUNER_RTL2832_FC0012: return rtl2832u_fc0012_tuner_callback(d, cmd, arg); - case TUNER_RTL2832_FC0013: - return rtl2832u_fc0013_tuner_callback(d, cmd, arg); case TUNER_RTL2832_TUA9001: return rtl2832u_tua9001_tuner_callback(d, cmd, arg); default: @@ -688,11 +678,14 @@ static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) } static int rtl2832u_frontend_callback(void *adapter_priv, int component, - int cmd, int arg) + int cmd, int arg) { struct i2c_adapter *adap = adapter_priv; struct dvb_usb_device *d = i2c_get_adapdata(adap); + dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n", + __func__, component, cmd, arg); + switch (component) { case DVB_FRONTEND_COMPONENT_TUNER: return rtl2832u_tuner_callback(d, cmd, arg); -- cgit v1.2.3 From 3d8a60d5de1aefd40c8e874a9db342bc974c6031 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 17 Sep 2012 17:54:27 -0300 Subject: [media] rtl28xxu: use proper config for e4000 tuner Do not abuse anymore fc0012 tuner config. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index f17e73baccff..70c2df1269d4 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -583,6 +583,12 @@ static struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = { .tuner = TUNER_RTL2832_TUA9001, }; +static struct rtl2832_config rtl28xxu_rtl2832_e4000_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .tuner = TUNER_RTL2832_E4000, +}; + static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -720,8 +726,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) rtl2832_config = &rtl28xxu_rtl2832_tua9001_config; break; case TUNER_RTL2832_E4000: - /* FIXME: do not abuse fc0012 settings */ - rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; + rtl2832_config = &rtl28xxu_rtl2832_e4000_config; break; default: dev_err(&d->udev->dev, "%s: unknown tuner=%s\n", -- cgit v1.2.3 From d67ceb3398d7ae7fda78ec753639296f442ecf66 Mon Sep 17 00:00:00 2001 From: Oliver Schinagl Date: Thu, 20 Sep 2012 14:57:17 -0300 Subject: [media] Support for Asus MyCinema U3100Mini Plus This is initial support for the Asus MyCinema U3100Mini Plus. The driver in its current form gets detected and loads properly. Scanning using dvbscan works without problems, Locking onto a channel using tzap also works fine. Only playback using tzap -r + mplayer was tested and was fully functional. It uses the af9035 USB Bridge chip, with an af9033 demodulator. The tuner used is the FCI FC2580. Signed-off-by: Oliver Schinagl Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/dvb-frontends/af9033.c | 4 ++++ drivers/media/dvb-frontends/af9033.h | 1 + drivers/media/dvb-frontends/af9033_priv.h | 37 +++++++++++++++++++++++++++++++ drivers/media/tuners/fc2580.c | 3 ++- drivers/media/usb/dvb-usb-v2/Kconfig | 1 + drivers/media/usb/dvb-usb-v2/af9035.c | 27 ++++++++++++++++++++++ drivers/media/usb/dvb-usb-v2/af9035.h | 1 + 8 files changed, 74 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index d572307353e9..58e0220447c0 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -329,6 +329,7 @@ #define USB_PID_ASUS_U3000 0x171f #define USB_PID_ASUS_U3000H 0x1736 #define USB_PID_ASUS_U3100 0x173f +#define USB_PID_ASUS_U3100MINI_PLUS 0x1779 #define USB_PID_YUAN_EC372S 0x1edc #define USB_PID_YUAN_STK7700PH 0x1f08 #define USB_PID_YUAN_PD378S 0x2edc diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 56e9611c0abb..8162d939c4b2 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -314,6 +314,10 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_tda18218); init = tuner_init_tda18218; break; + case AF9033_TUNER_FC2580: + len = ARRAY_SIZE(tuner_init_fc2580); + init = tuner_init_fc2580; + break; default: dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h index 288622b54e8b..bfa4313fde21 100644 --- a/drivers/media/dvb-frontends/af9033.h +++ b/drivers/media/dvb-frontends/af9033.h @@ -42,6 +42,7 @@ struct af9033_config { #define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ #define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ #define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ +#define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */ u8 tuner; /* diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 0b783b9ed75e..34dddcd77538 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -466,5 +466,42 @@ static const struct reg_val tuner_init_tda18218[] = { {0x80f1e6, 0x00}, }; +/* FCI FC2580 tuner init */ +static const struct reg_val tuner_init_fc2580[] = { + { 0x800046, 0x32 }, + { 0x800057, 0x01 }, + { 0x800058, 0x00 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x05 }, + { 0x8000b3, 0x01 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f00c, 0x19 }, + { 0x80f00d, 0x1A }, + { 0x80f00e, 0x00 }, + { 0x80f00f, 0x02 }, + { 0x80f010, 0x00 }, + { 0x80f011, 0x02 }, + { 0x80f012, 0x00 }, + { 0x80f013, 0x02 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01f, 0x96 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x96 }, + { 0x80f02a, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x01 }, +}; + #endif /* AF9033_PRIV_H */ diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index afc04915f5ae..51bc39ca0090 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -498,8 +498,9 @@ struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe, dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); - if (chip_id != 0x56) + if ((chip_id != 0x56) && (chip_id != 0x5a)) { goto err; + } dev_info(&priv->i2c->dev, "%s: FCI FC2580 successfully identified\n", diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index e09930c6b4f9..834bfecbed73 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -40,6 +40,7 @@ config DVB_USB_AF9035 select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9035 based DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 84b3b27db979..aabd3fc03ea7 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -513,6 +513,7 @@ static int af9035_read_config(struct dvb_usb_device *d) case AF9033_TUNER_FC0011: case AF9033_TUNER_MXL5007T: case AF9033_TUNER_TDA18218: + case AF9033_TUNER_FC2580: state->af9033_config[i].spec_inv = 1; break; default: @@ -798,6 +799,11 @@ static struct tda18218_config af9035_tda18218_config = { .i2c_wr_max = 21, }; +static const struct fc2580_config af9035_fc2580_config = { + .i2c_addr = 0x56, + .clock = 16384000, +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); @@ -882,6 +888,25 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(tda18218_attach, adap->fe[0], &d->i2c_adap, &af9035_tda18218_config); break; + case AF9033_TUNER_FC2580: + /* Tuner enable using gpiot2_o, gpiot2_en and gpiot2_on */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + /* attach tuner */ + fe = dvb_attach(fc2580_attach, adap->fe[0], + &d->i2c_adap, &af9035_fc2580_config); + break; default: fe = NULL; } @@ -1106,6 +1131,8 @@ static const struct usb_device_id af9035_id_table[] = { &af9035_props, "AVerMedia HD Volar (A867)", NULL) }, { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR, &af9035_props, "AVerMedia Twinstar (A825)", NULL) }, + { DVB_USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100MINI_PLUS, + &af9035_props, "Asus U3100Mini Plus", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, af9035_id_table); diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index de8e761bb203..75ef1ec13fbf 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -28,6 +28,7 @@ #include "fc0011.h" #include "mxl5007t.h" #include "tda18218.h" +#include "fc2580.h" struct reg_val { u32 reg; -- cgit v1.2.3 From 3d8866dbb14923ce51f37b7e29d12075965acfa0 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 20 Sep 2012 18:34:50 -0300 Subject: [media] rtl28xxu: [0413:6680] DigitalNow Quad DVB-T Receiver It is 4 x RTL2832U + 4 x FC0012 in one PCIe board. Of course there is a PCIe USB host controller too. Big thanks for Darryl Bond reporting and testing that! Reported-by: Darryl Bond Tested-by: Darryl Bond Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 70c2df1269d4..f62cfbaa8515 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1342,6 +1342,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101, &rtl2832u_props, "Dexatek DK DVB-T Dongle", NULL) }, + { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6680, + &rtl2832u_props, "DigitalNow Quad DVB-T Receiver", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From d6f35c7128201b78fe2cb2c1eca3a5c67929566c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 20 Sep 2012 21:58:20 -0300 Subject: [media] cypress_firmware: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/cypress_firmware.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c index 9f7c970c6424..bb21eeea5495 100644 --- a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c +++ b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c @@ -30,6 +30,9 @@ static const struct usb_cypress_controller cypress[] = { static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data, u8 len) { + dvb_usb_dbg_usb_control_msg(udev, + 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len); + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000); } @@ -45,24 +48,24 @@ int usbv2_cypress_load_firmware(struct usb_device *udev, reset = 1; ret = usb_cypress_writemem(udev, cypress[type].cs_reg, &reset, 1); if (ret != 1) - pr_err("%s: could not stop the USB controller CPU", + dev_err(&udev->dev, + "%s: could not stop the USB controller CPU\n", KBUILD_MODNAME); while ((ret = dvb_usbv2_get_hexline(fw, &hx, &pos)) > 0) { - pr_debug("%s: writing to address %04x (buffer: %02x %02x)\n", - __func__, hx.addr, hx.len, hx.chk); - ret = usb_cypress_writemem(udev, hx.addr, hx.data, hx.len); if (ret != hx.len) { - pr_err("%s: error while transferring firmware " \ - "(transferred size=%d, block size=%d)", + dev_err(&udev->dev, "%s: error while transferring " \ + "firmware (transferred size=%d, " \ + "block size=%d)\n", KBUILD_MODNAME, ret, hx.len); ret = -EINVAL; break; } } if (ret < 0) { - pr_err("%s: firmware download failed at %d with %d", + dev_err(&udev->dev, + "%s: firmware download failed at %d with %d\n", KBUILD_MODNAME, pos, ret); return ret; } @@ -72,8 +75,8 @@ int usbv2_cypress_load_firmware(struct usb_device *udev, reset = 0; if (ret || usb_cypress_writemem( udev, cypress[type].cs_reg, &reset, 1) != 1) { - pr_err("%s: could not restart the USB controller CPU", - KBUILD_MODNAME); + dev_err(&udev->dev, "%s: could not restart the USB " \ + "controller CPU\n", KBUILD_MODNAME); ret = -EINVAL; } } else -- cgit v1.2.3 From 2347e6836ad2a5a2f7e62bd12b8f52fe15f04f74 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 21 Sep 2012 19:44:51 -0300 Subject: [media] cypress_firmware: refactor firmware downloading Refactor firmware download function. It also should fix one bug coming from usb_control_msg() message buffers. Taking buffers from the stack for usb_control_msg() is not allowed as it does not work on all architectures. Allocate buffers using kmalloc(). Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/cypress_firmware.c | 80 +++++++++++++------------ 1 file changed, 43 insertions(+), 37 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c index bb21eeea5495..211df549f26a 100644 --- a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c +++ b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c @@ -40,48 +40,59 @@ static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data, int usbv2_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) { - struct hexline hx; - u8 reset; + struct hexline *hx; int ret, pos = 0; + hx = kmalloc(sizeof(struct hexline), GFP_KERNEL); + if (!hx) { + dev_err(&udev->dev, "%s: kmalloc() failed\n", KBUILD_MODNAME); + return -ENOMEM; + } + /* stop the CPU */ - reset = 1; - ret = usb_cypress_writemem(udev, cypress[type].cs_reg, &reset, 1); - if (ret != 1) - dev_err(&udev->dev, - "%s: could not stop the USB controller CPU\n", - KBUILD_MODNAME); - - while ((ret = dvb_usbv2_get_hexline(fw, &hx, &pos)) > 0) { - ret = usb_cypress_writemem(udev, hx.addr, hx.data, hx.len); - if (ret != hx.len) { + hx->data[0] = 1; + ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1); + if (ret != 1) { + dev_err(&udev->dev, "%s: CPU stop failed=%d\n", + KBUILD_MODNAME, ret); + ret = -EIO; + goto err_kfree; + } + + /* write firmware to memory */ + for (;;) { + ret = dvb_usbv2_get_hexline(fw, hx, &pos); + if (ret < 0) + goto err_kfree; + else if (ret == 0) + break; + + ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len); + if (ret < 0) { + goto err_kfree; + } else if (ret != hx->len) { dev_err(&udev->dev, "%s: error while transferring " \ "firmware (transferred size=%d, " \ "block size=%d)\n", - KBUILD_MODNAME, ret, hx.len); - ret = -EINVAL; - break; + KBUILD_MODNAME, ret, hx->len); + ret = -EIO; + goto err_kfree; } } - if (ret < 0) { - dev_err(&udev->dev, - "%s: firmware download failed at %d with %d\n", - KBUILD_MODNAME, pos, ret); - return ret; - } - if (ret == 0) { - /* restart the CPU */ - reset = 0; - if (ret || usb_cypress_writemem( - udev, cypress[type].cs_reg, &reset, 1) != 1) { - dev_err(&udev->dev, "%s: could not restart the USB " \ - "controller CPU\n", KBUILD_MODNAME); - ret = -EINVAL; - } - } else + /* start the CPU */ + hx->data[0] = 0; + ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1); + if (ret != 1) { + dev_err(&udev->dev, "%s: CPU start failed=%d\n", + KBUILD_MODNAME, ret); ret = -EIO; + goto err_kfree; + } + ret = 0; +err_kfree: + kfree(hx); return ret; } EXPORT_SYMBOL(usbv2_cypress_load_firmware); @@ -96,7 +107,6 @@ int dvb_usbv2_get_hexline(const struct firmware *fw, struct hexline *hx, return 0; memset(hx, 0, sizeof(struct hexline)); - hx->len = b[0]; if ((*pos + hx->len + 4) >= fw->size) @@ -109,14 +119,10 @@ int dvb_usbv2_get_hexline(const struct firmware *fw, struct hexline *hx, /* b[4] and b[5] are the Extended linear address record data * field */ hx->addr |= (b[4] << 24) | (b[5] << 16); - /* - hx->len -= 2; - data_offs += 2; - */ } + memcpy(hx->data, &b[data_offs], hx->len); hx->chk = b[hx->len + data_offs]; - *pos += hx->len + 5; return *pos; -- cgit v1.2.3 From 2a858486e0406110541e13f07d00665ee0c08a54 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 21 Sep 2012 23:23:49 -0300 Subject: [media] dvb_usb_v2: fix error handling for .tuner_attach() fe was not set NULL after it was destroyed in tuner attach fail error case. Due to that it was destroyed again and Kernel oopsed. Reported-by: Oliver Schinagl Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index f99015937957..9859d2a2449b 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -612,8 +612,10 @@ err_dvb_unregister_frontend: err_dvb_frontend_detach: for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) { - if (adap->fe[i]) + if (adap->fe[i]) { dvb_frontend_detach(adap->fe[i]); + adap->fe[i] = NULL; + } } err: -- cgit v1.2.3 From f6068764573d2f29529c8835126dabe3f2096c1f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 22 Sep 2012 13:46:24 -0300 Subject: [media] anysee: do not remove CI when it is not attached Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/anysee.c | 8 ++++---- drivers/media/usb/dvb-usb-v2/anysee.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 6705d81f0cb2..ec540140c810 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1217,6 +1217,8 @@ static int anysee_ci_init(struct dvb_usb_device *d) if (ret) return ret; + state->ci_attached = true; + return 0; } @@ -1225,7 +1227,7 @@ static void anysee_ci_release(struct dvb_usb_device *d) struct anysee_state *state = d_to_priv(d); /* detach CI */ - if (state->has_ci) + if (state->ci_attached) dvb_ca_en50221_release(&state->ci); return; @@ -1257,10 +1259,8 @@ static int anysee_init(struct dvb_usb_device *d) /* attach CI */ if (state->has_ci) { ret = anysee_ci_init(d); - if (ret) { - state->has_ci = false; + if (ret) return ret; - } } return 0; diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h index 4ab467679a43..c1a4273f14ff 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.h +++ b/drivers/media/usb/dvb-usb-v2/anysee.h @@ -56,6 +56,7 @@ struct anysee_state { u8 seq; u8 fe_id:1; /* frondend ID */ u8 has_ci:1; + u8 ci_attached:1; struct dvb_ca_en50221 ci; unsigned long ci_cam_ready; /* jiffies */ }; -- cgit v1.2.3 From 13a5336e88b5b23047db643bb9cac2cef7548c6c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 19 Jul 2012 22:28:56 -0300 Subject: [media] em28xx: implement FE set_lna() callback Make LNA run-time switching possible for PCTV nanoStick T2 290e! Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 41 ++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 34c5ea996031..3e5c42de6359 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -81,6 +81,7 @@ struct em28xx_dvb { int (*gate_ctrl)(struct dvb_frontend *, int); struct semaphore pll_mutex; bool dont_attach_fe1; + int lna_gpio; }; @@ -568,6 +569,33 @@ static void pctv_520e_init(struct em28xx *dev) i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); }; +static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe, int val) +{ + struct em28xx *dev = fe->dvb->priv; +#ifdef CONFIG_GPIOLIB + struct em28xx_dvb *dvb = dev->dvb; + int ret; + unsigned long flags; + + if (val) + flags = GPIOF_OUT_INIT_LOW; + else + flags = GPIOF_OUT_INIT_HIGH; + + ret = gpio_request_one(dvb->lna_gpio, flags, NULL); + if (ret) + em28xx_errdev("gpio request failed %d\n", ret); + else + gpio_free(dvb->lna_gpio); + + return ret; +#else + dev_warn(&dev->udev->dev, "%s: LNA control is disabled\n", + KBUILD_MODNAME); + return 0; +#endif +} + static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) { /* Values extracted from a USB trace of the Terratec Windows driver */ @@ -809,7 +837,7 @@ static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) static int em28xx_dvb_init(struct em28xx *dev) { - int result = 0, mfe_shared = 0, gpio_chip_base; + int result = 0, mfe_shared = 0; struct em28xx_dvb *dvb; if (!dev->board.has_dvb) { @@ -958,7 +986,7 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->fe[0] = dvb_attach(cxd2820r_attach, &em28xx_cxd2820r_config, &dev->i2c_adap, - &gpio_chip_base); + &dvb->lna_gpio); if (dvb->fe[0]) { /* FE 0 attach tuner */ if (!dvb_attach(tda18271_attach, @@ -973,15 +1001,18 @@ static int em28xx_dvb_init(struct em28xx *dev) } } +#ifdef CONFIG_GPIOLIB /* enable LNA for DVB-T, DVB-T2 and DVB-C */ - result = gpio_request_one(gpio_chip_base, GPIOF_INIT_LOW, - "LNA"); + result = gpio_request_one(dvb->lna_gpio, GPIOF_OUT_INIT_LOW, + NULL); if (result) em28xx_errdev("gpio request failed %d\n", result); else - gpio_free(gpio_chip_base); + gpio_free(dvb->lna_gpio); result = 0; /* continue even set LNA fails */ +#endif + dvb->fe[0]->ops.set_lna = em28xx_pctv_290e_set_lna; break; case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: { -- cgit v1.2.3 From a36a66d7aaa6468a91941ffcfb6d324f28825910 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 16 Aug 2012 21:07:23 -0300 Subject: [media] cxd2820r: use static GPIO config when GPIOLIB is undefined It is fallback condition as GPIOLIB seems to be disabled by default. Better solution is needed, maybe GPIOLIB could be enabled by default? Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cxd2820r_core.c | 29 ++++++++++++++++++++--------- drivers/media/usb/em28xx/em28xx-dvb.c | 3 +++ 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 4bd42f20d5b3..42648643693e 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -567,7 +567,7 @@ static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe) static void cxd2820r_release(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; + int uninitialized_var(ret); /* silence compiler warning */ dev_dbg(&priv->i2c->dev, "%s\n", __func__); @@ -688,9 +688,9 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, { struct cxd2820r_priv *priv; int ret; - u8 tmp; + u8 tmp, gpio[GPIO_COUNT]; - priv = kzalloc(sizeof (struct cxd2820r_priv), GFP_KERNEL); + priv = kzalloc(sizeof(struct cxd2820r_priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; dev_err(&i2c->dev, "%s: kzalloc() failed\n", @@ -699,7 +699,9 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, } priv->i2c = i2c; - memcpy(&priv->cfg, cfg, sizeof (struct cxd2820r_config)); + memcpy(&priv->cfg, cfg, sizeof(struct cxd2820r_config)); + memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof(struct dvb_frontend_ops)); + priv->fe.demodulator_priv = priv; priv->bank[0] = priv->bank[1] = 0xff; ret = cxd2820r_rd_reg(priv, 0x000fd, &tmp); @@ -707,9 +709,9 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, if (ret || tmp != 0xe1) goto error; -#ifdef CONFIG_GPIOLIB - /* add GPIOs */ if (gpio_chip_base) { +#ifdef CONFIG_GPIOLIB + /* add GPIOs */ priv->gpio_chip.label = KBUILD_MODNAME; priv->gpio_chip.dev = &priv->i2c->dev; priv->gpio_chip.owner = THIS_MODULE; @@ -728,11 +730,20 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, priv->gpio_chip.base); *gpio_chip_base = priv->gpio_chip.base; - } +#else + /* + * Use static GPIO configuration if GPIOLIB is undefined. + * This is fallback condition. + */ + gpio[0] = (*gpio_chip_base >> 0) & 0x07; + gpio[1] = (*gpio_chip_base >> 3) & 0x07; + gpio[2] = 0; + ret = cxd2820r_gpio(&priv->fe, gpio); + if (ret) + goto error; #endif + } - memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof (struct dvb_frontend_ops)); - priv->fe.demodulator_priv = priv; return &priv->fe; error: dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 3e5c42de6359..0ca3e05992bf 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -983,6 +983,9 @@ static int em28xx_dvb_init(struct em28xx *dev) &dev->i2c_adap, &kworld_a340_config); break; case EM28174_BOARD_PCTV_290E: + /* set default GPIO0 for LNA, used if GPIOLIB is undefined */ + dvb->lna_gpio = CXD2820R_GPIO_E | CXD2820R_GPIO_O | + CXD2820R_GPIO_L; dvb->fe[0] = dvb_attach(cxd2820r_attach, &em28xx_cxd2820r_config, &dev->i2c_adap, -- cgit v1.2.3 From 0c42a55c86228ba9d0969562fcebc2adc79bfddb Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 19 Sep 2012 20:52:21 -0300 Subject: [media] em28xx: do not set PCTV 290e LNA handler if fe attach fail It was a bug that could cause oops if demodulator attach was failed. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 0ca3e05992bf..1662b70435b3 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1002,20 +1002,22 @@ static int em28xx_dvb_init(struct em28xx *dev) result = -EINVAL; goto out_free; } - } #ifdef CONFIG_GPIOLIB - /* enable LNA for DVB-T, DVB-T2 and DVB-C */ - result = gpio_request_one(dvb->lna_gpio, GPIOF_OUT_INIT_LOW, - NULL); - if (result) - em28xx_errdev("gpio request failed %d\n", result); - else - gpio_free(dvb->lna_gpio); - - result = 0; /* continue even set LNA fails */ + /* enable LNA for DVB-T, DVB-T2 and DVB-C */ + result = gpio_request_one(dvb->lna_gpio, + GPIOF_OUT_INIT_LOW, NULL); + if (result) + em28xx_errdev("gpio request failed %d\n", + result); + else + gpio_free(dvb->lna_gpio); + + result = 0; /* continue even set LNA fails */ #endif - dvb->fe[0]->ops.set_lna = em28xx_pctv_290e_set_lna; + dvb->fe[0]->ops.set_lna = em28xx_pctv_290e_set_lna; + } + break; case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: { -- cgit v1.2.3 From a4f64407b9b33122a58cb78afd73f86d4bb022c4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 29 Sep 2012 15:28:56 -0300 Subject: [media] rtl28xxu: [0ccd:00d3] TerraTec Cinergy T Stick RC (Rev. 3) It is RTL2832U + E4000. Thanks to Nikolai Spasov reporting and testing! Reported-by: Nikolai Spasov Tested-by: Nikolai Spasov Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index f62cfbaa8515..adabba8d28bc 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1344,6 +1344,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Dexatek DK DVB-T Dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6680, &rtl2832u_props, "DigitalNow Quad DVB-T Receiver", NULL) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d3, + &rtl2832u_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); -- cgit v1.2.3 From 6ae5e060840589f567c1837613e8a9d34fc9188a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 Oct 2012 15:35:27 -0300 Subject: em28xx: Make all em28xx extensions to be initialized asynchronously em28xx-dvb, em28xx-alsa and em28xx-ir are typically initialized asyncrhronously. The exception for it is when those modules are loaded before em28xx (or before an em28xx card insertion) or when they're built in. Make the extentions to always load asynchronously. That allows having all DVB firmwares loaded synchronously with udev-182. Antti tested it with the following hardware: Hauppauge WinTV HVR 930C MaxMedia UB425-TC PCTV QuatroStick nano (520e) Tested-by: Antti Palosaari Cc: stable@kernel.org # for Kernel 3.6 - please note that driver location has changed Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index ca62b9981380..ab98d0845861 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2875,12 +2875,20 @@ static void em28xx_card_setup(struct em28xx *dev) } -#if defined(CONFIG_MODULES) && defined(MODULE) static void request_module_async(struct work_struct *work) { struct em28xx *dev = container_of(work, struct em28xx, request_module_wk); + /* + * The em28xx extensions can be modules or builtin. If the + * modules are already loaded or are built in, those extensions + * can be initialised right now. Otherwise, the module init + * code will do it. + */ + em28xx_init_extension(dev); + +#if defined(CONFIG_MODULES) && defined(MODULE) if (dev->has_audio_class) request_module("snd-usb-audio"); else if (dev->has_alsa_audio) @@ -2890,6 +2898,7 @@ static void request_module_async(struct work_struct *work) request_module("em28xx-dvb"); if (dev->board.ir_codes && !disable_ir) request_module("em28xx-rc"); +#endif /* CONFIG_MODULES */ } static void request_modules(struct em28xx *dev) @@ -2902,10 +2911,6 @@ static void flush_request_modules(struct em28xx *dev) { flush_work_sync(&dev->request_module_wk); } -#else -#define request_modules(dev) -#define flush_request_modules(dev) -#endif /* CONFIG_MODULES */ /* * em28xx_release_resources() @@ -3324,13 +3329,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, */ mutex_unlock(&dev->lock); - /* - * These extensions can be modules. If the modules are already - * loaded then we can initialise the device now, otherwise we - * will initialise it when the modules load instead. - */ - em28xx_init_extension(dev); - return 0; unlock_and_free: -- cgit v1.2.3 From 2425bb3d4016ed95ce83a90b53bd92c7f31091e4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 Oct 2012 16:02:57 -0300 Subject: em28xx: regression fix: use DRX-K sync firmware requests on em28xx As em28xx-dvb will always be initialized asynchronously, there's no need anymore for a separate thread to load the DRX-K firmware. Fixes a known regression with kernel 3.6 with tda18271 driver and asynchronous DRX-K firmware load. Antti tested it with the following hardware: Hauppauge WinTV HVR 930C MaxMedia UB425-TC PCTV QuatroStick nano (520e) Tested-by: Antti Palosaari Cc: stable@kernel.org # for Kernel 3.6 - please note that driver location has changed Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media/usb') diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 1662b70435b3..913e5227897a 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -318,6 +318,7 @@ static struct drxk_config terratec_h5_drxk = { .no_i2c_bridge = 1, .microcode_name = "dvb-usb-terratec-h5-drxk.fw", .qam_demod_parameter_count = 2, + .load_firmware_sync = true, }; static struct drxk_config hauppauge_930c_drxk = { @@ -327,6 +328,7 @@ static struct drxk_config hauppauge_930c_drxk = { .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw", .chunk_size = 56, .qam_demod_parameter_count = 2, + .load_firmware_sync = true, }; struct drxk_config terratec_htc_stick_drxk = { @@ -340,12 +342,14 @@ struct drxk_config terratec_htc_stick_drxk = { .antenna_dvbt = true, /* The windows driver uses the same. This will disable LNA. */ .antenna_gpio = 0x6, + .load_firmware_sync = true, }; static struct drxk_config maxmedia_ub425_tc_drxk = { .adr = 0x29, .single_master = 1, .no_i2c_bridge = 1, + .load_firmware_sync = true, }; static struct drxk_config pctv_520e_drxk = { @@ -356,6 +360,7 @@ static struct drxk_config pctv_520e_drxk = { .chunk_size = 58, .antenna_dvbt = true, /* disable LNA */ .antenna_gpio = (1 << 2), /* disable LNA */ + .load_firmware_sync = true, }; static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) -- cgit v1.2.3